[hibernate-commits] Hibernate SVN: r11563 - in trunk/Hibernate3: cache-ehcache and 438 other directories.
hibernate-commits at lists.jboss.org
hibernate-commits at lists.jboss.org
Fri May 25 16:19:36 EDT 2007
Author: steve.ebersole at jboss.com
Date: 2007-05-25 16:19:29 -0400 (Fri, 25 May 2007)
New Revision: 11563
Added:
trunk/Hibernate3/cache-ehcache/
trunk/Hibernate3/cache-ehcache/pom.xml
trunk/Hibernate3/cache-ehcache/src/
trunk/Hibernate3/cache-ehcache/src/main/
trunk/Hibernate3/cache-ehcache/src/main/java/
trunk/Hibernate3/cache-ehcache/src/main/java/org/
trunk/Hibernate3/cache-ehcache/src/main/java/org/hibernate/
trunk/Hibernate3/cache-ehcache/src/main/java/org/hibernate/cache/
trunk/Hibernate3/cache-ehcache/src/main/java/org/hibernate/cache/EhCache.java
trunk/Hibernate3/cache-ehcache/src/main/java/org/hibernate/cache/EhCacheProvider.java
trunk/Hibernate3/cache-jbosscache/
trunk/Hibernate3/cache-jbosscache/pom.xml
trunk/Hibernate3/cache-jbosscache/src/
trunk/Hibernate3/cache-jbosscache/src/main/
trunk/Hibernate3/cache-jbosscache/src/main/java/
trunk/Hibernate3/cache-jbosscache/src/main/java/org/
trunk/Hibernate3/cache-jbosscache/src/main/java/org/hibernate/
trunk/Hibernate3/cache-jbosscache/src/main/java/org/hibernate/cache/
trunk/Hibernate3/cache-jbosscache/src/main/java/org/hibernate/cache/JndiBoundTreeCacheProvider.java
trunk/Hibernate3/cache-jbosscache/src/main/java/org/hibernate/cache/OptimisticTreeCache.java
trunk/Hibernate3/cache-jbosscache/src/main/java/org/hibernate/cache/OptimisticTreeCacheProvider.java
trunk/Hibernate3/cache-jbosscache/src/main/java/org/hibernate/cache/TreeCache.java
trunk/Hibernate3/cache-jbosscache/src/main/java/org/hibernate/cache/TreeCacheProvider.java
trunk/Hibernate3/cache-jbosscache2/
trunk/Hibernate3/cache-jbosscache2/pom.xml
trunk/Hibernate3/cache-jbosscache2/src/
trunk/Hibernate3/cache-jbosscache2/src/main/
trunk/Hibernate3/cache-jbosscache2/src/main/java/
trunk/Hibernate3/cache-jbosscache2/src/main/resources/
trunk/Hibernate3/cache-jbosscache2/src/main/resources/org/
trunk/Hibernate3/cache-jbosscache2/src/main/resources/org/hibernate/
trunk/Hibernate3/cache-jbosscache2/src/main/resources/org/hibernate/cache/
trunk/Hibernate3/cache-jbosscache2/src/main/resources/org/hibernate/cache/jbc/
trunk/Hibernate3/cache-jbosscache2/src/main/resources/org/hibernate/cache/jbc/TransactionManagerLookupAdaptor.java
trunk/Hibernate3/cache-jbosscache2/src/main/resources/org/hibernate/cache/jbc/TreeCacheRegionAdapter.java
trunk/Hibernate3/cache-jbosscache2/src/main/resources/org/hibernate/cache/jbc/TreeCacheRegionFactory.java
trunk/Hibernate3/cache-oscache/
trunk/Hibernate3/cache-oscache/pom.xml
trunk/Hibernate3/cache-oscache/src/
trunk/Hibernate3/cache-oscache/src/main/
trunk/Hibernate3/cache-oscache/src/main/java/
trunk/Hibernate3/cache-oscache/src/main/java/org/
trunk/Hibernate3/cache-oscache/src/main/java/org/hibernate/
trunk/Hibernate3/cache-oscache/src/main/java/org/hibernate/cache/
trunk/Hibernate3/cache-oscache/src/main/java/org/hibernate/cache/OSCache.java
trunk/Hibernate3/cache-oscache/src/main/java/org/hibernate/cache/OSCacheProvider.java
trunk/Hibernate3/cache-swarmcache/
trunk/Hibernate3/cache-swarmcache/pom.xml
trunk/Hibernate3/cache-swarmcache/src/
trunk/Hibernate3/cache-swarmcache/src/main/
trunk/Hibernate3/cache-swarmcache/src/main/java/
trunk/Hibernate3/cache-swarmcache/src/main/java/org/
trunk/Hibernate3/cache-swarmcache/src/main/java/org/hibernate/
trunk/Hibernate3/cache-swarmcache/src/main/java/org/hibernate/cache/
trunk/Hibernate3/cache-swarmcache/src/main/java/org/hibernate/cache/SwarmCache.java
trunk/Hibernate3/cache-swarmcache/src/main/java/org/hibernate/cache/SwarmCacheProvider.java
trunk/Hibernate3/connection-c3p0/
trunk/Hibernate3/connection-c3p0/pom.xml
trunk/Hibernate3/connection-c3p0/src/
trunk/Hibernate3/connection-c3p0/src/main/
trunk/Hibernate3/connection-c3p0/src/main/java/
trunk/Hibernate3/connection-c3p0/src/main/java/org/
trunk/Hibernate3/connection-c3p0/src/main/java/org/hibernate/
trunk/Hibernate3/connection-c3p0/src/main/java/org/hibernate/connection/
trunk/Hibernate3/connection-c3p0/src/main/java/org/hibernate/connection/C3P0ConnectionProvider.java
trunk/Hibernate3/connection-proxool/
trunk/Hibernate3/connection-proxool/pom.xml
trunk/Hibernate3/connection-proxool/src/
trunk/Hibernate3/connection-proxool/src/main/
trunk/Hibernate3/connection-proxool/src/main/java/
trunk/Hibernate3/connection-proxool/src/main/java/org/
trunk/Hibernate3/connection-proxool/src/main/java/org/hibernate/
trunk/Hibernate3/connection-proxool/src/main/java/org/hibernate/connection/
trunk/Hibernate3/connection-proxool/src/main/java/org/hibernate/connection/ProxoolConnectionProvider.java
trunk/Hibernate3/core/
trunk/Hibernate3/core/pom.xml
trunk/Hibernate3/core/src/
trunk/Hibernate3/core/src/main/
trunk/Hibernate3/core/src/main/antlr/
trunk/Hibernate3/core/src/main/antlr/hql-sql.g
trunk/Hibernate3/core/src/main/antlr/hql.g
trunk/Hibernate3/core/src/main/antlr/sql-gen.g
trunk/Hibernate3/core/src/main/java/
trunk/Hibernate3/core/src/main/java/org/
trunk/Hibernate3/core/src/main/java/org/hibernate/
trunk/Hibernate3/core/src/main/java/org/hibernate/AssertionFailure.java
trunk/Hibernate3/core/src/main/java/org/hibernate/CacheMode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/CallbackException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/ConnectionReleaseMode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/Criteria.java
trunk/Hibernate3/core/src/main/java/org/hibernate/DuplicateMappingException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/EmptyInterceptor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/EntityMode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/FetchMode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/Filter.java
trunk/Hibernate3/core/src/main/java/org/hibernate/FlushMode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/Hibernate.java
trunk/Hibernate3/core/src/main/java/org/hibernate/HibernateException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/InstantiationException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/Interceptor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/InvalidMappingException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/JDBCException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/LazyInitializationException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/LockMode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/MappingException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/MappingNotFoundException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/NonUniqueObjectException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/NonUniqueResultException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/ObjectDeletedException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/ObjectNotFoundException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/PersistentObjectException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/PropertyAccessException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/PropertyNotFoundException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/PropertyValueException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/Query.java
trunk/Hibernate3/core/src/main/java/org/hibernate/QueryException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/QueryParameterException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/ReplicationMode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/SQLQuery.java
trunk/Hibernate3/core/src/main/java/org/hibernate/ScrollMode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/ScrollableResults.java
trunk/Hibernate3/core/src/main/java/org/hibernate/Session.java
trunk/Hibernate3/core/src/main/java/org/hibernate/SessionException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/SessionFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/StaleObjectStateException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/StaleStateException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/StatelessSession.java
trunk/Hibernate3/core/src/main/java/org/hibernate/Transaction.java
trunk/Hibernate3/core/src/main/java/org/hibernate/TransactionException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/TransientObjectException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/TypeMismatchException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/UnresolvableObjectException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/WrongClassException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/action/
trunk/Hibernate3/core/src/main/java/org/hibernate/action/BulkOperationCleanupAction.java
trunk/Hibernate3/core/src/main/java/org/hibernate/action/CollectionAction.java
trunk/Hibernate3/core/src/main/java/org/hibernate/action/CollectionRecreateAction.java
trunk/Hibernate3/core/src/main/java/org/hibernate/action/CollectionRemoveAction.java
trunk/Hibernate3/core/src/main/java/org/hibernate/action/CollectionUpdateAction.java
trunk/Hibernate3/core/src/main/java/org/hibernate/action/DelayedPostInsertIdentifier.java
trunk/Hibernate3/core/src/main/java/org/hibernate/action/EntityAction.java
trunk/Hibernate3/core/src/main/java/org/hibernate/action/EntityDeleteAction.java
trunk/Hibernate3/core/src/main/java/org/hibernate/action/EntityIdentityInsertAction.java
trunk/Hibernate3/core/src/main/java/org/hibernate/action/EntityInsertAction.java
trunk/Hibernate3/core/src/main/java/org/hibernate/action/EntityUpdateAction.java
trunk/Hibernate3/core/src/main/java/org/hibernate/action/Executable.java
trunk/Hibernate3/core/src/main/java/org/hibernate/action/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/
trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/AbstractClassTransformerImpl.java
trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/BasicProxyFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/BytecodeProvider.java
trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/ClassTransformer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/InstrumentedClassLoader.java
trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/ProxyFactoryFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/ReflectionOptimizer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/cglib/
trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/cglib/AccessOptimizerAdapter.java
trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/cglib/BytecodeProviderImpl.java
trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/cglib/CglibClassTransformer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/cglib/InstantiationOptimizerAdapter.java
trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/cglib/ProxyFactoryFactoryImpl.java
trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/cglib/ReflectionOptimizerImpl.java
trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/
trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/AccessOptimizerAdapter.java
trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/BulkAccessor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/BulkAccessorException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/BulkAccessorFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/BytecodeProviderImpl.java
trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/FastClass.java
trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/FieldFilter.java
trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/FieldHandled.java
trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/FieldHandler.java
trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/FieldTransformer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/InstantiationOptimizerAdapter.java
trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/JavassistClassTransformer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/ProxyFactoryFactoryImpl.java
trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/ReflectionOptimizerImpl.java
trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/TransformingClassLoader.java
trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/util/
trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/util/BasicClassFilter.java
trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/util/ByteCodeHelper.java
trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/util/ClassDescriptor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/util/ClassFilter.java
trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/util/FieldFilter.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/AbstractJndiBoundCacheProvider.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/Cache.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/CacheConcurrencyStrategy.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/CacheDataDescription.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/CacheException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/CacheKey.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/CacheProvider.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/CollectionRegion.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/EntityRegion.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/FilterKey.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/GeneralDataRegion.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/HashtableCache.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/HashtableCacheProvider.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/NoCacheProvider.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/NoCachingEnabledException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/NonstrictReadWriteCache.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/OptimisticCache.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/OptimisticCacheSource.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/QueryCache.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/QueryCacheFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/QueryKey.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/QueryResultsRegion.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/ReadOnlyCache.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/ReadWriteCache.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/Region.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/RegionFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/StandardQueryCache.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/StandardQueryCacheFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/Timestamper.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/TimestampsRegion.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/TransactionalCache.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/TransactionalDataRegion.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/UpdateTimestampsCache.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/access/
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/access/AccessType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/access/CollectionRegionAccessStrategy.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/access/EntityRegionAccessStrategy.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/access/SoftLock.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/access/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/CacheEntry.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/CacheEntryStructure.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/CollectionCacheEntry.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/StructuredCacheEntry.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/StructuredCollectionCacheEntry.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/StructuredMapCacheEntry.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/UnstructuredCacheEntry.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/CacheDataDescriptionImpl.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/NoCachingRegionFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/BaseGeneralDataRegionAdapter.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/BaseRegionAdapter.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/BaseTransactionalDataRegionAdapter.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/CollectionAccessStrategyAdapter.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/CollectionRegionAdapter.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/EntityAccessStrategyAdapter.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/EntityRegionAdapter.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/OptimisticCacheSourceAdapter.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/QueryResultsRegionAdapter.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/RegionFactoryCacheProviderBridge.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/TimestampsRegionAdapter.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cache/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/
trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/CollectionSecondPass.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/Configuration.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/DefaultNamingStrategy.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/Environment.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/ExtendsQueueEntry.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/HbmBinder.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/ImprovedNamingStrategy.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/Mappings.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/NamedSQLQuerySecondPass.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/NamingStrategy.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/QuerySecondPass.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/ResultSetMappingBinder.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/ResultSetMappingSecondPass.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/SecondPass.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/Settings.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/SettingsFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/classic/
trunk/Hibernate3/core/src/main/java/org/hibernate/classic/Lifecycle.java
trunk/Hibernate3/core/src/main/java/org/hibernate/classic/Session.java
trunk/Hibernate3/core/src/main/java/org/hibernate/classic/Validatable.java
trunk/Hibernate3/core/src/main/java/org/hibernate/classic/ValidationFailure.java
trunk/Hibernate3/core/src/main/java/org/hibernate/classic/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/collection/
trunk/Hibernate3/core/src/main/java/org/hibernate/collection/AbstractPersistentCollection.java
trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentArrayHolder.java
trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentBag.java
trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentCollection.java
trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentElementHolder.java
trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentIdentifierBag.java
trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentIndexedElementHolder.java
trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentList.java
trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentListElementHolder.java
trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentMap.java
trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentMapElementHolder.java
trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentSet.java
trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentSortedMap.java
trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentSortedSet.java
trunk/Hibernate3/core/src/main/java/org/hibernate/collection/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/connection/
trunk/Hibernate3/core/src/main/java/org/hibernate/connection/ConnectionProvider.java
trunk/Hibernate3/core/src/main/java/org/hibernate/connection/ConnectionProviderFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/connection/DatasourceConnectionProvider.java
trunk/Hibernate3/core/src/main/java/org/hibernate/connection/DriverManagerConnectionProvider.java
trunk/Hibernate3/core/src/main/java/org/hibernate/connection/UserSuppliedConnectionProvider.java
trunk/Hibernate3/core/src/main/java/org/hibernate/connection/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/context/
trunk/Hibernate3/core/src/main/java/org/hibernate/context/CurrentSessionContext.java
trunk/Hibernate3/core/src/main/java/org/hibernate/context/JTASessionContext.java
trunk/Hibernate3/core/src/main/java/org/hibernate/context/ManagedSessionContext.java
trunk/Hibernate3/core/src/main/java/org/hibernate/context/ThreadLocalSessionContext.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/AbstractEmptinessExpression.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/AggregateProjection.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/AliasedProjection.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/AvgProjection.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/BetweenExpression.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Conjunction.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/CountProjection.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/CriteriaQuery.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/CriteriaSpecification.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Criterion.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/DetachedCriteria.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Disjunction.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Distinct.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/EmptyExpression.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Example.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/ExistsSubqueryExpression.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Expression.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/IdentifierEqExpression.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/IdentifierProjection.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/IlikeExpression.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/InExpression.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Junction.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/LikeExpression.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/LogicalExpression.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/MatchMode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/NaturalIdentifier.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/NotEmptyExpression.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/NotExpression.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/NotNullExpression.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/NullExpression.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Order.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Projection.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/ProjectionList.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Projections.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Property.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/PropertyExpression.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/PropertyProjection.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/PropertySubqueryExpression.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Restrictions.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/RowCountProjection.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/SQLCriterion.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/SQLProjection.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/SimpleExpression.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/SimpleProjection.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/SimpleSubqueryExpression.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/SizeExpression.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Subqueries.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/SubqueryExpression.java
trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/Cache71Dialect.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/DB2390Dialect.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/DB2400Dialect.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/DB2Dialect.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/DataDirectOracle9Dialect.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/DerbyDialect.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/Dialect.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/DialectFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/FirebirdDialect.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/FrontBaseDialect.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/H2Dialect.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/HSQLDialect.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/InformixDialect.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/IngresDialect.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/InterbaseDialect.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/JDataStoreDialect.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/MckoiDialect.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/MimerSQLDialect.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/MySQL5Dialect.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/MySQL5InnoDBDialect.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/MySQLDialect.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/MySQLInnoDBDialect.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/MySQLMyISAMDialect.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/Oracle9Dialect.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/OracleDialect.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/PointbaseDialect.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/ProgressDialect.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/RDMSOS2200Dialect.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/ResultColumnReferenceStrategy.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/SAPDBDialect.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/SQLServerDialect.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/Sybase11Dialect.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/SybaseAnywhereDialect.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/SybaseDialect.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/TeradataDialect.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/TimesTenDialect.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/TypeNames.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/AnsiTrimEmulationFunction.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/CastFunction.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/CharIndexFunction.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/ClassicAvgFunction.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/ClassicCountFunction.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/ClassicSumFunction.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/ConditionalParenthesisFunction.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/ConvertFunction.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/NoArgSQLFunction.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/NvlFunction.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/PositionSubstringFunction.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/SQLFunction.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/SQLFunctionRegistry.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/SQLFunctionTemplate.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/StandardJDBCEscapeFunction.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/StandardSQLFunction.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/VarArgsSQLFunction.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/lock/
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/lock/LockingStrategy.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/lock/SelectLockingStrategy.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/lock/UpdateLockingStrategy.java
trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/ActionQueue.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/AssociationKey.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/BatchFetchQueue.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/Cascade.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/CascadeStyle.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/CascadingAction.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/CollectionEntry.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/CollectionKey.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/Collections.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/EntityEntry.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/EntityKey.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/EntityUniqueKey.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/ExecuteUpdateResultCheckStyle.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/FilterDefinition.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/ForeignKeys.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/HibernateIterator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/IdentifierValue.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/JoinHelper.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/JoinSequence.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/Mapping.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/NamedQueryDefinition.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/NamedSQLQueryDefinition.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/Nullability.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/ParameterBinder.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/PersistenceContext.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/QueryParameters.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/ResultSetMappingDefinition.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/RowSelection.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/SessionFactoryImplementor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/SessionImplementor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/StatefulPersistenceContext.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/Status.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/SubselectFetch.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/TransactionHelper.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/TwoPhaseLoad.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/TypedValue.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/UnsavedValueFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/ValueInclusion.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/VersionValue.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/Versioning.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/loading/
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/loading/CollectionLoadContext.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/loading/EntityLoadContext.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/loading/LoadContexts.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/loading/LoadingCollectionEntry.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/FilterQueryPlan.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/HQLQueryPlan.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/NamedParameterDescriptor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/NativeSQLQueryPlan.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/OrdinalParameterDescriptor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/ParamLocationRecognizer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/ParameterMetadata.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/ParameterParser.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/QueryMetadata.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/QueryPlanCache.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/ReturnMetadata.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/sql/
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/sql/NativeSQLQueryCollectionReturn.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/sql/NativeSQLQueryJoinReturn.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/sql/NativeSQLQueryNonScalarReturn.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/sql/NativeSQLQueryReturn.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/sql/NativeSQLQueryRootReturn.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/sql/NativeSQLQueryScalarReturn.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/sql/NativeSQLQuerySpecification.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/transaction/
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/transaction/IsolatedWork.java
trunk/Hibernate3/core/src/main/java/org/hibernate/engine/transaction/Isolater.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/
trunk/Hibernate3/core/src/main/java/org/hibernate/event/AbstractEvent.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/AutoFlushEvent.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/AutoFlushEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/DeleteEvent.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/DeleteEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/DirtyCheckEvent.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/DirtyCheckEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/EventListeners.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/EventSource.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/EvictEvent.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/EvictEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/FlushEntityEvent.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/FlushEntityEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/FlushEvent.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/FlushEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/Initializable.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/InitializeCollectionEvent.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/InitializeCollectionEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/LoadEvent.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/LoadEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/LockEvent.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/LockEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/MergeEvent.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/MergeEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/PersistEvent.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/PersistEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/PostDeleteEvent.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/PostDeleteEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/PostInsertEvent.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/PostInsertEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/PostLoadEvent.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/PostLoadEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/PostUpdateEvent.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/PostUpdateEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreDeleteEvent.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreDeleteEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreInsertEvent.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreInsertEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreLoadEvent.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreLoadEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreUpdateEvent.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreUpdateEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/RefreshEvent.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/RefreshEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/ReplicateEvent.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/ReplicateEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/SaveOrUpdateEvent.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/SaveOrUpdateEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/
trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/AbstractFlushingEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/AbstractLockUpgradeEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/AbstractReassociateEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/AbstractSaveEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/AbstractVisitor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultAutoFlushEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultDeleteEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultDirtyCheckEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultEvictEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultFlushEntityEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultFlushEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultInitializeCollectionEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultLoadEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultLockEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultMergeEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultPersistEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultPersistOnFlushEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultPostLoadEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultPreLoadEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultRefreshEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultReplicateEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultSaveEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultSaveOrUpdateCopyEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultSaveOrUpdateEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultUpdateEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DirtyCollectionSearchVisitor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/EvictVisitor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/FlushVisitor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/OnLockVisitor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/OnReplicateVisitor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/OnUpdateVisitor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/ProxyVisitor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/ReattachVisitor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/WrapVisitor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/event/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/exception/
trunk/Hibernate3/core/src/main/java/org/hibernate/exception/CacheSQLStateConverter.java
trunk/Hibernate3/core/src/main/java/org/hibernate/exception/Configurable.java
trunk/Hibernate3/core/src/main/java/org/hibernate/exception/ConstraintViolationException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/exception/DataException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/exception/ExceptionUtils.java
trunk/Hibernate3/core/src/main/java/org/hibernate/exception/GenericJDBCException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/exception/JDBCConnectionException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/exception/JDBCExceptionHelper.java
trunk/Hibernate3/core/src/main/java/org/hibernate/exception/LockAcquisitionException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/exception/Nestable.java
trunk/Hibernate3/core/src/main/java/org/hibernate/exception/NestableDelegate.java
trunk/Hibernate3/core/src/main/java/org/hibernate/exception/NestableException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/exception/NestableRuntimeException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/exception/SQLExceptionConverter.java
trunk/Hibernate3/core/src/main/java/org/hibernate/exception/SQLExceptionConverterFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/exception/SQLGrammarException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/exception/SQLStateConverter.java
trunk/Hibernate3/core/src/main/java/org/hibernate/exception/TemplatedViolatedConstraintNameExtracter.java
trunk/Hibernate3/core/src/main/java/org/hibernate/exception/ViolatedConstraintNameExtracter.java
trunk/Hibernate3/core/src/main/java/org/hibernate/exception/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/CollectionProperties.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/CollectionSubqueryFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/FilterTranslator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/HolderInstantiator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/NameGenerator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ParameterTranslations.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/QueryExecutionRequestException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/QuerySplitter.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/QueryTranslator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/QueryTranslatorFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/antlr/
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/antlr/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/ASTQueryTranslatorFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/DetailedSemanticException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/ErrorCounter.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/ErrorReporter.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/HqlASTFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/HqlLexer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/HqlParser.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/HqlSqlWalker.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/HqlToken.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/InvalidPathException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/InvalidWithClauseException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/ParameterTranslationsImpl.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/ParseErrorHandler.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/QuerySyntaxException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/QueryTranslatorImpl.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/SqlASTFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/SqlGenerator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/exec/
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/exec/AbstractStatementExecutor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/exec/BasicExecutor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/exec/MultiTableDeleteExecutor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/exec/MultiTableUpdateExecutor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/exec/StatementExecutor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/AbstractRestrictableStatement.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/AbstractSelectExpression.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/AbstractStatement.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/AggregateNode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/AssignmentSpecification.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/BetweenOperatorNode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/BinaryArithmeticOperatorNode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/BinaryLogicOperatorNode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/BinaryOperatorNode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/BooleanLiteralNode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/Case2Node.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/CaseNode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/CollectionFunction.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/ConstructorNode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/CountNode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/DeleteStatement.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/DisplayableNode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/DotNode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/ExpectedTypeAwareNode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/FromClause.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/FromElement.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/FromElementFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/FromElementType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/FromReferenceNode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/HqlSqlWalkerNode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/IdentNode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/ImpliedFromElement.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/InLogicOperatorNode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/IndexNode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/InitializeableNode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/InsertStatement.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/IntoClause.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/JavaConstantNode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/LiteralNode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/MethodNode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/Node.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/OperatorNode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/OrderByClause.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/ParameterNode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/PathNode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/QueryNode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/ResolvableNode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/RestrictableStatement.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/SelectClause.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/SelectExpression.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/SelectExpressionImpl.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/SelectExpressionList.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/SessionFactoryAwareNode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/SqlFragment.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/SqlNode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/Statement.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/UnaryArithmeticNode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/UnaryLogicOperatorNode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/UnaryOperatorNode.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/UpdateStatement.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/ASTAppender.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/ASTIterator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/ASTParentsFirstIterator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/ASTPrinter.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/ASTUtil.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/AliasGenerator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/ColumnHelper.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/JoinProcessor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/LiteralProcessor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/NodeTraverser.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/PathHelper.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/SessionFactoryHelper.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/SyntheticAndFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/ClassicQueryTranslatorFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/ClauseParser.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/FromParser.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/FromPathExpressionParser.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/GroupByParser.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/HavingParser.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/OrderByParser.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/Parser.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/ParserHelper.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/PathExpressionParser.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/PreprocessingParser.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/QueryTranslatorImpl.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/SelectParser.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/SelectPathExpressionParser.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/WhereParser.java
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/hql/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/id/
trunk/Hibernate3/core/src/main/java/org/hibernate/id/AbstractPostInsertGenerator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/id/AbstractUUIDGenerator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/id/Assigned.java
trunk/Hibernate3/core/src/main/java/org/hibernate/id/Configurable.java
trunk/Hibernate3/core/src/main/java/org/hibernate/id/ForeignGenerator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/id/GUIDGenerator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/id/IdentifierGenerationException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/id/IdentifierGenerator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/id/IdentifierGeneratorFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/id/IdentityGenerator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/id/IncrementGenerator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/id/MultipleHiLoPerTableGenerator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/id/PersistentIdentifierGenerator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/id/PostInsertIdentifierGenerator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/id/PostInsertIdentityPersister.java
trunk/Hibernate3/core/src/main/java/org/hibernate/id/SelectGenerator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/id/SequenceGenerator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/id/SequenceHiLoGenerator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/id/SequenceIdentityGenerator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/id/TableGenerator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/id/TableHiLoGenerator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/id/UUIDHexGenerator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/id/enhanced/
trunk/Hibernate3/core/src/main/java/org/hibernate/id/enhanced/AccessCallback.java
trunk/Hibernate3/core/src/main/java/org/hibernate/id/enhanced/DatabaseStructure.java
trunk/Hibernate3/core/src/main/java/org/hibernate/id/enhanced/Optimizer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/id/enhanced/OptimizerFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/id/enhanced/SequenceStructure.java
trunk/Hibernate3/core/src/main/java/org/hibernate/id/enhanced/SequenceStyleGenerator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/id/enhanced/TableStructure.java
trunk/Hibernate3/core/src/main/java/org/hibernate/id/insert/
trunk/Hibernate3/core/src/main/java/org/hibernate/id/insert/AbstractReturningDelegate.java
trunk/Hibernate3/core/src/main/java/org/hibernate/id/insert/AbstractSelectingDelegate.java
trunk/Hibernate3/core/src/main/java/org/hibernate/id/insert/Binder.java
trunk/Hibernate3/core/src/main/java/org/hibernate/id/insert/IdentifierGeneratingInsert.java
trunk/Hibernate3/core/src/main/java/org/hibernate/id/insert/InsertGeneratedIdentifierDelegate.java
trunk/Hibernate3/core/src/main/java/org/hibernate/id/insert/InsertSelectIdentityInsert.java
trunk/Hibernate3/core/src/main/java/org/hibernate/id/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/impl/
trunk/Hibernate3/core/src/main/java/org/hibernate/impl/AbstractQueryImpl.java
trunk/Hibernate3/core/src/main/java/org/hibernate/impl/AbstractScrollableResults.java
trunk/Hibernate3/core/src/main/java/org/hibernate/impl/AbstractSessionImpl.java
trunk/Hibernate3/core/src/main/java/org/hibernate/impl/CollectionFilterImpl.java
trunk/Hibernate3/core/src/main/java/org/hibernate/impl/CriteriaImpl.java
trunk/Hibernate3/core/src/main/java/org/hibernate/impl/FetchingScrollableResultsImpl.java
trunk/Hibernate3/core/src/main/java/org/hibernate/impl/FilterImpl.java
trunk/Hibernate3/core/src/main/java/org/hibernate/impl/IteratorImpl.java
trunk/Hibernate3/core/src/main/java/org/hibernate/impl/QueryImpl.java
trunk/Hibernate3/core/src/main/java/org/hibernate/impl/SQLQueryImpl.java
trunk/Hibernate3/core/src/main/java/org/hibernate/impl/ScrollableResultsImpl.java
trunk/Hibernate3/core/src/main/java/org/hibernate/impl/SessionFactoryImpl.java
trunk/Hibernate3/core/src/main/java/org/hibernate/impl/SessionFactoryObjectFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/impl/SessionImpl.java
trunk/Hibernate3/core/src/main/java/org/hibernate/impl/StatelessSessionImpl.java
trunk/Hibernate3/core/src/main/java/org/hibernate/impl/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/
trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/AbstractFieldInterceptor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/FieldInterceptionHelper.java
trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/FieldInterceptor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/LazyPropertyInitializer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/cglib/
trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/cglib/CGLIBHelper.java
trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/cglib/FieldInterceptorImpl.java
trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/javassist/
trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/javassist/FieldInterceptorImpl.java
trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/javassist/JavassistHelper.java
trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/
trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/AbstractBatcher.java
trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/BatchFailedException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/BatchedTooManyRowsAffectedException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/Batcher.java
trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/BatcherFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/BatchingBatcher.java
trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/BatchingBatcherFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/BorrowedConnectionProxy.java
trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/ColumnNameCache.java
trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/ConnectionManager.java
trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/ConnectionWrapper.java
trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/Expectation.java
trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/Expectations.java
trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/JDBCContext.java
trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/NonBatchingBatcher.java
trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/NonBatchingBatcherFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/ResultSetWrapper.java
trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/TooManyRowsAffectedException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/AbstractEntityJoinWalker.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/BasicLoader.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/CollectionAliases.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/ColumnEntityAliases.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/DefaultEntityAliases.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/EntityAliases.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/GeneratedCollectionAliases.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/JoinWalker.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/Loader.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/OuterJoinLoader.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/OuterJoinableAssociation.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/BasicCollectionJoinWalker.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/BasicCollectionLoader.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/BatchingCollectionInitializer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/CollectionInitializer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/CollectionJoinWalker.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/CollectionLoader.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/OneToManyJoinWalker.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/OneToManyLoader.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/SubselectCollectionLoader.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/SubselectOneToManyLoader.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/criteria/
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/criteria/CriteriaJoinWalker.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/criteria/CriteriaLoader.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/criteria/CriteriaQueryTranslator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/criteria/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/CollectionFetchReturn.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/CollectionReturn.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/ColumnCollectionAliases.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/CustomLoader.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/CustomQuery.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/EntityFetchReturn.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/FetchReturn.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/NonScalarReturn.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/Return.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/RootReturn.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/ScalarReturn.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/sql/
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/sql/SQLCustomQuery.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/sql/SQLQueryParser.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/sql/SQLQueryReturnProcessor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/AbstractEntityLoader.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/BatchingEntityLoader.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/CascadeEntityJoinWalker.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/CascadeEntityLoader.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/CollectionElementLoader.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/EntityJoinWalker.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/EntityLoader.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/UniqueEntityLoader.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/hql/
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/hql/QueryLoader.java
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/hql/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/loader/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/lob/
trunk/Hibernate3/core/src/main/java/org/hibernate/lob/BlobImpl.java
trunk/Hibernate3/core/src/main/java/org/hibernate/lob/ClobImpl.java
trunk/Hibernate3/core/src/main/java/org/hibernate/lob/ReaderInputStream.java
trunk/Hibernate3/core/src/main/java/org/hibernate/lob/SerializableBlob.java
trunk/Hibernate3/core/src/main/java/org/hibernate/lob/SerializableClob.java
trunk/Hibernate3/core/src/main/java/org/hibernate/lob/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/AbstractAuxiliaryDatabaseObject.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Any.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Array.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/AuxiliaryDatabaseObject.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Backref.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Bag.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Collection.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Column.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Component.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Constraint.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/DenormalizedTable.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/DependantValue.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Fetchable.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Filterable.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/ForeignKey.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Formula.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/IdentifierBag.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/IdentifierCollection.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Index.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/IndexBackref.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/IndexedCollection.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Join.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/JoinedSubclass.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/KeyValue.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/List.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/ManyToOne.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Map.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/MetaAttributable.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/MetaAttribute.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/OneToMany.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/OneToOne.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/PersistentClass.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/PersistentClassVisitor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/PrimaryKey.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/PrimitiveArray.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Property.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/PropertyGeneration.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/RelationalModel.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/RootClass.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Selectable.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Set.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/SimpleAuxiliaryDatabaseObject.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/SimpleValue.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/SingleTableSubclass.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Subclass.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Table.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/TableOwner.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/ToOne.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/TypeDef.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/UnionSubclass.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/UniqueKey.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Value.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/ValueVisitor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/metadata/
trunk/Hibernate3/core/src/main/java/org/hibernate/metadata/ClassMetadata.java
trunk/Hibernate3/core/src/main/java/org/hibernate/metadata/CollectionMetadata.java
trunk/Hibernate3/core/src/main/java/org/hibernate/metadata/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/param/
trunk/Hibernate3/core/src/main/java/org/hibernate/param/AbstractExplicitParameterSpecification.java
trunk/Hibernate3/core/src/main/java/org/hibernate/param/CollectionFilterKeyParameterSpecification.java
trunk/Hibernate3/core/src/main/java/org/hibernate/param/DynamicFilterParameterSpecification.java
trunk/Hibernate3/core/src/main/java/org/hibernate/param/ExplicitParameterSpecification.java
trunk/Hibernate3/core/src/main/java/org/hibernate/param/NamedParameterSpecification.java
trunk/Hibernate3/core/src/main/java/org/hibernate/param/ParameterSpecification.java
trunk/Hibernate3/core/src/main/java/org/hibernate/param/PositionalParameterSpecification.java
trunk/Hibernate3/core/src/main/java/org/hibernate/param/VersionTypeSeedParameterSpecification.java
trunk/Hibernate3/core/src/main/java/org/hibernate/persister/
trunk/Hibernate3/core/src/main/java/org/hibernate/persister/PersisterFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/
trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java
trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/BasicCollectionPersister.java
trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/CollectionPersister.java
trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/CollectionPropertyMapping.java
trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/CollectionPropertyNames.java
trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/CompositeElementPropertyMapping.java
trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/ElementPropertyMapping.java
trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/NamedQueryCollectionInitializer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/OneToManyPersister.java
trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/QueryableCollection.java
trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/SQLLoadableCollection.java
trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/
trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java
trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/AbstractPropertyMapping.java
trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/BasicEntityPropertyMapping.java
trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/EntityPersister.java
trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/Joinable.java
trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java
trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/Loadable.java
trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/Lockable.java
trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/NamedQueryLoader.java
trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/OuterJoinLoadable.java
trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/PropertyMapping.java
trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/Queryable.java
trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/SQLLoadable.java
trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java
trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java
trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/UniqueKeyLoadable.java
trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/persister/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/pretty/
trunk/Hibernate3/core/src/main/java/org/hibernate/pretty/DDLFormatter.java
trunk/Hibernate3/core/src/main/java/org/hibernate/pretty/Formatter.java
trunk/Hibernate3/core/src/main/java/org/hibernate/pretty/MessageHelper.java
trunk/Hibernate3/core/src/main/java/org/hibernate/pretty/Printer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/pretty/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/property/
trunk/Hibernate3/core/src/main/java/org/hibernate/property/BackrefPropertyAccessor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/property/BasicPropertyAccessor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/property/ChainedPropertyAccessor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/property/DirectPropertyAccessor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/property/Dom4jAccessor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/property/EmbeddedPropertyAccessor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/property/Getter.java
trunk/Hibernate3/core/src/main/java/org/hibernate/property/IndexPropertyAccessor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/property/MapAccessor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/property/NoopAccessor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/property/PropertyAccessor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/property/PropertyAccessorFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/property/Setter.java
trunk/Hibernate3/core/src/main/java/org/hibernate/property/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/
trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/AbstractLazyInitializer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/EntityNotFoundDelegate.java
trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/HibernateProxy.java
trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/HibernateProxyHelper.java
trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/LazyInitializer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/ProxyFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/dom4j/
trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/dom4j/Dom4jLazyInitializer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/dom4j/Dom4jProxy.java
trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/dom4j/Dom4jProxyFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/map/
trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/map/MapLazyInitializer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/map/MapProxy.java
trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/map/MapProxyFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/pojo/
trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/pojo/BasicLazyInitializer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/pojo/cglib/
trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/pojo/cglib/CGLIBLazyInitializer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/pojo/cglib/CGLIBProxyFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/pojo/cglib/SerializableProxy.java
trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/pojo/javassist/
trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/pojo/javassist/JavassistLazyInitializer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/pojo/javassist/JavassistProxyFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/pojo/javassist/SerializableProxy.java
trunk/Hibernate3/core/src/main/java/org/hibernate/secure/
trunk/Hibernate3/core/src/main/java/org/hibernate/secure/HibernatePermission.java
trunk/Hibernate3/core/src/main/java/org/hibernate/secure/JACCConfiguration.java
trunk/Hibernate3/core/src/main/java/org/hibernate/secure/JACCPermissions.java
trunk/Hibernate3/core/src/main/java/org/hibernate/secure/JACCPreDeleteEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/secure/JACCPreInsertEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/secure/JACCPreLoadEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/secure/JACCPreUpdateEventListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/secure/JACCSecurityListener.java
trunk/Hibernate3/core/src/main/java/org/hibernate/secure/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/sql/
trunk/Hibernate3/core/src/main/java/org/hibernate/sql/ANSICaseFragment.java
trunk/Hibernate3/core/src/main/java/org/hibernate/sql/ANSIJoinFragment.java
trunk/Hibernate3/core/src/main/java/org/hibernate/sql/Alias.java
trunk/Hibernate3/core/src/main/java/org/hibernate/sql/CacheJoinFragment.java
trunk/Hibernate3/core/src/main/java/org/hibernate/sql/CaseFragment.java
trunk/Hibernate3/core/src/main/java/org/hibernate/sql/ConditionFragment.java
trunk/Hibernate3/core/src/main/java/org/hibernate/sql/DecodeCaseFragment.java
trunk/Hibernate3/core/src/main/java/org/hibernate/sql/Delete.java
trunk/Hibernate3/core/src/main/java/org/hibernate/sql/DerbyCaseFragment.java
trunk/Hibernate3/core/src/main/java/org/hibernate/sql/DisjunctionFragment.java
trunk/Hibernate3/core/src/main/java/org/hibernate/sql/ForUpdateFragment.java
trunk/Hibernate3/core/src/main/java/org/hibernate/sql/HSQLCaseFragment.java
trunk/Hibernate3/core/src/main/java/org/hibernate/sql/InFragment.java
trunk/Hibernate3/core/src/main/java/org/hibernate/sql/Insert.java
trunk/Hibernate3/core/src/main/java/org/hibernate/sql/InsertSelect.java
trunk/Hibernate3/core/src/main/java/org/hibernate/sql/JoinFragment.java
trunk/Hibernate3/core/src/main/java/org/hibernate/sql/MckoiCaseFragment.java
trunk/Hibernate3/core/src/main/java/org/hibernate/sql/OracleJoinFragment.java
trunk/Hibernate3/core/src/main/java/org/hibernate/sql/QueryJoinFragment.java
trunk/Hibernate3/core/src/main/java/org/hibernate/sql/QuerySelect.java
trunk/Hibernate3/core/src/main/java/org/hibernate/sql/Select.java
trunk/Hibernate3/core/src/main/java/org/hibernate/sql/SelectFragment.java
trunk/Hibernate3/core/src/main/java/org/hibernate/sql/SimpleSelect.java
trunk/Hibernate3/core/src/main/java/org/hibernate/sql/Sybase11JoinFragment.java
trunk/Hibernate3/core/src/main/java/org/hibernate/sql/Template.java
trunk/Hibernate3/core/src/main/java/org/hibernate/sql/Update.java
trunk/Hibernate3/core/src/main/java/org/hibernate/sql/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/stat/
trunk/Hibernate3/core/src/main/java/org/hibernate/stat/CategorizedStatistics.java
trunk/Hibernate3/core/src/main/java/org/hibernate/stat/CollectionStatistics.java
trunk/Hibernate3/core/src/main/java/org/hibernate/stat/EntityStatistics.java
trunk/Hibernate3/core/src/main/java/org/hibernate/stat/QueryStatistics.java
trunk/Hibernate3/core/src/main/java/org/hibernate/stat/SecondLevelCacheStatistics.java
trunk/Hibernate3/core/src/main/java/org/hibernate/stat/SessionStatistics.java
trunk/Hibernate3/core/src/main/java/org/hibernate/stat/SessionStatisticsImpl.java
trunk/Hibernate3/core/src/main/java/org/hibernate/stat/Statistics.java
trunk/Hibernate3/core/src/main/java/org/hibernate/stat/StatisticsImpl.java
trunk/Hibernate3/core/src/main/java/org/hibernate/stat/StatisticsImplementor.java
trunk/Hibernate3/core/src/main/java/org/hibernate/stat/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/tool/
trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/
trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/ColumnMetadata.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/ConnectionHelper.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/DatabaseMetadata.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/ForeignKeyMetadata.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/IndexMetadata.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/ManagedProviderConnectionHelper.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/SchemaExport.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/SchemaExportTask.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/SchemaUpdate.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/SchemaUpdateTask.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/SchemaValidator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/SchemaValidatorTask.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/SuppliedConnectionHelper.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/SuppliedConnectionProviderConnectionHelper.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/TableMetadata.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/tool/instrument/
trunk/Hibernate3/core/src/main/java/org/hibernate/tool/instrument/BasicInstrumentationTask.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tool/instrument/cglib/
trunk/Hibernate3/core/src/main/java/org/hibernate/tool/instrument/cglib/InstrumentTask.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tool/instrument/javassist/
trunk/Hibernate3/core/src/main/java/org/hibernate/tool/instrument/javassist/InstrumentTask.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tool/instrument/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/
trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/BESTransactionManagerLookup.java
trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/CMTTransaction.java
trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/CMTTransactionFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/CacheSynchronization.java
trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/JBossTransactionManagerLookup.java
trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/JDBCTransaction.java
trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/JDBCTransactionFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/JNDITransactionManagerLookup.java
trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/JOTMTransactionManagerLookup.java
trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/JOnASTransactionManagerLookup.java
trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/JRun4TransactionManagerLookup.java
trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/JTATransaction.java
trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/JTATransactionFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/OC4JTransactionManagerLookup.java
trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/OrionTransactionManagerLookup.java
trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/ResinTransactionManagerLookup.java
trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/SunONETransactionManagerLookup.java
trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/TransactionFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/TransactionFactoryFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/TransactionManagerLookup.java
trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/TransactionManagerLookupFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/WebSphereExtendedJTATransactionLookup.java
trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/WebSphereTransactionManagerLookup.java
trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/WeblogicTransactionManagerLookup.java
trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/transform/
trunk/Hibernate3/core/src/main/java/org/hibernate/transform/AliasToBeanConstructorResultTransformer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/transform/AliasToBeanResultTransformer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/transform/AliasToEntityMapResultTransformer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/transform/DistinctRootEntityResultTransformer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/transform/PassThroughResultTransformer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/transform/ResultTransformer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/transform/RootEntityResultTransformer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/transform/ToListResultTransformer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/transform/Transformers.java
trunk/Hibernate3/core/src/main/java/org/hibernate/transform/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/
trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/Dom4jInstantiator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/DynamicMapInstantiator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/ElementWrapper.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/EntityModeToTuplizerMapping.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/IdentifierProperty.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/Instantiator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/PojoInstantiator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/Property.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/PropertyFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/StandardProperty.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/Tuplizer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/VersionProperty.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/component/
trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/component/AbstractComponentTuplizer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/component/ComponentEntityModeToTuplizerMapping.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/component/ComponentMetamodel.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/component/ComponentTuplizer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/component/Dom4jComponentTuplizer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/component/DynamicMapComponentTuplizer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/component/PojoComponentTuplizer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/entity/
trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/entity/AbstractEntityTuplizer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/entity/Dom4jEntityTuplizer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/entity/DynamicMapEntityTuplizer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/entity/EntityEntityModeToTuplizerMapping.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/entity/EntityTuplizer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/entity/PojoEntityTuplizer.java
trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/type/
trunk/Hibernate3/core/src/main/java/org/hibernate/type/AbstractBynaryType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/AbstractCharArrayType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/AbstractComponentType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/AbstractType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/AdaptedImmutableType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/AnyType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/ArrayType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/AssociationType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/BagType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/BigDecimalType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/BigIntegerType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/BinaryType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/BlobType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/BooleanType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/ByteType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/CalendarDateType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/CalendarType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/CharArrayType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/CharBooleanType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/CharacterArrayType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/CharacterType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/ClassType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/ClobType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/CollectionType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/ComponentType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/CompositeCustomType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/CurrencyType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/CustomCollectionType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/CustomType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/DateType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/DbTimestampType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/DiscriminatorType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/DoubleType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/EmbeddedComponentType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/EntityType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/FloatType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/ForeignKeyDirection.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/IdentifierBagType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/IdentifierType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/ImmutableType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/IntegerType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/ListType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/LiteralType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/LocaleType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/LongType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/ManyToOneType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/MapType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/MetaType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/MutableType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/NullableType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/OneToOneType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/OrderedMapType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/OrderedSetType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/PrimitiveType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/SerializableType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/SerializationException.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/SetType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/ShortType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/SortedMapType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/SortedSetType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/SpecialOneToOneType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/StringType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/TextType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/TimeType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/TimeZoneType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/TimestampType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/TrueFalseType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/Type.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/TypeFactory.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/VersionType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/WrapperBinaryType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/YesNoType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/type/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/usertype/
trunk/Hibernate3/core/src/main/java/org/hibernate/usertype/CompositeUserType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/usertype/EnhancedUserType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/usertype/LoggableUserType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/usertype/ParameterizedType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/usertype/UserCollectionType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/usertype/UserType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/usertype/UserVersionType.java
trunk/Hibernate3/core/src/main/java/org/hibernate/usertype/package.html
trunk/Hibernate3/core/src/main/java/org/hibernate/util/
trunk/Hibernate3/core/src/main/java/org/hibernate/util/ArrayHelper.java
trunk/Hibernate3/core/src/main/java/org/hibernate/util/BytesHelper.java
trunk/Hibernate3/core/src/main/java/org/hibernate/util/CalendarComparator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/util/Cloneable.java
trunk/Hibernate3/core/src/main/java/org/hibernate/util/CollectionHelper.java
trunk/Hibernate3/core/src/main/java/org/hibernate/util/ComparableComparator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/util/ConfigHelper.java
trunk/Hibernate3/core/src/main/java/org/hibernate/util/DTDEntityResolver.java
trunk/Hibernate3/core/src/main/java/org/hibernate/util/EmptyIterator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/util/EqualsHelper.java
trunk/Hibernate3/core/src/main/java/org/hibernate/util/ExternalSessionFactoryConfig.java
trunk/Hibernate3/core/src/main/java/org/hibernate/util/FastHashMap.java
trunk/Hibernate3/core/src/main/java/org/hibernate/util/FilterHelper.java
trunk/Hibernate3/core/src/main/java/org/hibernate/util/GetGeneratedKeysHelper.java
trunk/Hibernate3/core/src/main/java/org/hibernate/util/IdentityMap.java
trunk/Hibernate3/core/src/main/java/org/hibernate/util/IdentitySet.java
trunk/Hibernate3/core/src/main/java/org/hibernate/util/JDBCExceptionReporter.java
trunk/Hibernate3/core/src/main/java/org/hibernate/util/JTAHelper.java
trunk/Hibernate3/core/src/main/java/org/hibernate/util/JoinedIterator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/util/LazyIterator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/util/LinkedHashCollectionHelper.java
trunk/Hibernate3/core/src/main/java/org/hibernate/util/MarkerObject.java
trunk/Hibernate3/core/src/main/java/org/hibernate/util/NamedGeneratedKeysHelper.java
trunk/Hibernate3/core/src/main/java/org/hibernate/util/NamingHelper.java
trunk/Hibernate3/core/src/main/java/org/hibernate/util/PropertiesHelper.java
trunk/Hibernate3/core/src/main/java/org/hibernate/util/Range.java
trunk/Hibernate3/core/src/main/java/org/hibernate/util/ReflectHelper.java
trunk/Hibernate3/core/src/main/java/org/hibernate/util/SerializationHelper.java
trunk/Hibernate3/core/src/main/java/org/hibernate/util/SimpleMRUCache.java
trunk/Hibernate3/core/src/main/java/org/hibernate/util/SingletonIterator.java
trunk/Hibernate3/core/src/main/java/org/hibernate/util/SoftLimitMRUCache.java
trunk/Hibernate3/core/src/main/java/org/hibernate/util/StringHelper.java
trunk/Hibernate3/core/src/main/java/org/hibernate/util/XMLHelper.java
trunk/Hibernate3/core/src/main/java/org/hibernate/util/package.html
trunk/Hibernate3/core/src/main/javadoc/
trunk/Hibernate3/core/src/main/javadoc/package.html
trunk/Hibernate3/core/src/main/resources/
trunk/Hibernate3/core/src/main/resources/org/
trunk/Hibernate3/core/src/main/resources/org/hibernate/
trunk/Hibernate3/core/src/main/resources/org/hibernate/checkstyle_checks.xml
trunk/Hibernate3/core/src/main/resources/org/hibernate/hibernate-configuration-3.0.dtd
trunk/Hibernate3/core/src/main/resources/org/hibernate/hibernate-mapping-3.0.dtd
trunk/Hibernate3/documentation/
trunk/Hibernate3/documentation/manual/
trunk/Hibernate3/documentation/manual/README
trunk/Hibernate3/documentation/manual/en-US/
trunk/Hibernate3/documentation/manual/en-US/pom.xml
trunk/Hibernate3/documentation/manual/en-US/src/
trunk/Hibernate3/documentation/manual/en-US/src/main/
trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/
trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/master.xml
trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/
trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/architecture.xml
trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/association_mapping.xml
trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/basic_mapping.xml
trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/batch.xml
trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/best_practices.xml
trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/collection_mapping.xml
trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/component_mapping.xml
trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/configuration.xml
trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/events.xml
trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/example_mappings.xml
trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/example_parentchild.xml
trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/example_weblog.xml
trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/filters.xml
trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/inheritance_mapping.xml
trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/performance.xml
trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/persistent_classes.xml
trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/query_criteria.xml
trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/query_hql.xml
trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/query_sql.xml
trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/session_api.xml
trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/toolset_guide.xml
trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/transactions.xml
trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/tutorial.xml
trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/xml.xml
trunk/Hibernate3/documentation/manual/en-US/src/main/resources/
trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/
trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/css/
trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/css/html.css
trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/
trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/AuthorWork.gif
trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/AuthorWork.zargo
trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/CustomerOrderProduct.gif
trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/CustomerOrderProduct.zargo
trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/EmployerEmployee.gif
trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/EmployerEmployee.zargo
trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/full_cream.gif
trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/full_cream.svg
trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/hibernate_logo_a.png
trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/lite.gif
trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/lite.svg
trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/overview.gif
trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/overview.svg
trunk/Hibernate3/documentation/manual/es-ES/
trunk/Hibernate3/documentation/manual/es-ES/pom.xml
trunk/Hibernate3/documentation/manual/es-ES/src/
trunk/Hibernate3/documentation/manual/es-ES/src/main/
trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/
trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/master.xml
trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/
trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/architecture.xml
trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/association_mapping.xml
trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/basic_mapping.xml
trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/batch.xml
trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/best_practices.xml
trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/collection_mapping.xml
trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/component_mapping.xml
trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/configuration.xml
trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/events.xml
trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/example_mappings.xml
trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/example_parentchild.xml
trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/example_weblog.xml
trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/filters.xml
trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/inheritance_mapping.xml
trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/performance.xml
trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/persistent_classes.xml
trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/query_criteria.xml
trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/query_hql.xml
trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/query_sql.xml
trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/quickstart.xml
trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/session_api.xml
trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/toolset_guide.xml
trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/transactions.xml
trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/tutorial.xml
trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/xml.xml
trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/
trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/
trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/css/
trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/css/html.css
trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/
trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/AuthorWork.gif
trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/AuthorWork.zargo
trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/CustomerOrderProduct.gif
trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/CustomerOrderProduct.zargo
trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/EmployerEmployee.gif
trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/EmployerEmployee.zargo
trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/full_cream.gif
trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/full_cream.svg
trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/hibernate_logo_a.png
trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/lite.gif
trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/lite.svg
trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/overview.gif
trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/overview.svg
trunk/Hibernate3/documentation/manual/fr-FR/
trunk/Hibernate3/documentation/manual/fr-FR/pom.xml
trunk/Hibernate3/documentation/manual/fr-FR/src/
trunk/Hibernate3/documentation/manual/fr-FR/src/main/
trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/
trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/master.xml
trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/
trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/architecture.xml
trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/association_mapping.xml
trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/basic_mapping.xml
trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/batch.xml
trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/best_practices.xml
trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/collection_mapping.xml
trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/component_mapping.xml
trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/configuration.xml
trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/events.xml
trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/example_mappings.xml
trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/example_parentchild.xml
trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/example_weblog.xml
trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/filters.xml
trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/inheritance_mapping.xml
trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/performance.xml
trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/persistent_classes.xml
trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/query_criteria.xml
trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/query_hql.xml
trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/query_sql.xml
trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/session_api.xml
trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/toolset_guide.xml
trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/transactions.xml
trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/tutorial.xml
trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/xml.xml
trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/
trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/
trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/css/
trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/css/html.css
trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/
trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/AuthorWork.gif
trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/AuthorWork.zargo
trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/CustomerOrderProduct.gif
trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/CustomerOrderProduct.zargo
trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/EmployerEmployee.gif
trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/EmployerEmployee.zargo
trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/full_cream.gif
trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/full_cream.svg
trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/hibernate_logo_a.png
trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/lite.gif
trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/lite.svg
trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/overview.gif
trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/overview.svg
trunk/Hibernate3/documentation/manual/ja-JP/
trunk/Hibernate3/documentation/manual/ja-JP/pom.xml
trunk/Hibernate3/documentation/manual/ja-JP/readme_ja.txt
trunk/Hibernate3/documentation/manual/ja-JP/src/
trunk/Hibernate3/documentation/manual/ja-JP/src/main/
trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/
trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/master.xml
trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/
trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/architecture.xml
trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/association_mapping.xml
trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/basic_mapping.xml
trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/batch.xml
trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/best_practices.xml
trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/collection_mapping.xml
trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/component_mapping.xml
trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/configuration.xml
trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/events.xml
trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/example_mappings.xml
trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/example_parentchild.xml
trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/example_weblog.xml
trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/filters.xml
trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/inheritance_mapping.xml
trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/performance.xml
trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/persistent_classes.xml
trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/query_criteria.xml
trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/query_hql.xml
trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/query_sql.xml
trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/session_api.xml
trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/toolset_guide.xml
trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/transactions.xml
trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/tutorial.xml
trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/xml.xml
trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/
trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/
trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/css/
trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/css/html.css
trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/
trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/AuthorWork.gif
trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/AuthorWork.zargo
trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/CustomerOrderProduct.gif
trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/CustomerOrderProduct.zargo
trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/EmployerEmployee.gif
trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/EmployerEmployee.zargo
trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/full_cream.gif
trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/full_cream.svg
trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/hibernate_logo_a.png
trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/lite.gif
trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/lite.svg
trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/overview.gif
trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/overview.svg
trunk/Hibernate3/documentation/manual/ja-JP/src/no-idea-what-to-do-with-these-yet/
trunk/Hibernate3/documentation/manual/ja-JP/src/no-idea-what-to-do-with-these-yet/fop/
trunk/Hibernate3/documentation/manual/ja-JP/src/no-idea-what-to-do-with-these-yet/fop/sazanami-gothic.ttf
trunk/Hibernate3/documentation/manual/ja-JP/src/no-idea-what-to-do-with-these-yet/fop/sazanami-gothic.xml
trunk/Hibernate3/documentation/manual/ja-JP/src/no-idea-what-to-do-with-these-yet/fop/sazanami-mincho.ttf
trunk/Hibernate3/documentation/manual/ja-JP/src/no-idea-what-to-do-with-these-yet/fop/sazanami-mincho.xml
trunk/Hibernate3/documentation/manual/ja-JP/src/no-idea-what-to-do-with-these-yet/fop/userconfig.xml
trunk/Hibernate3/documentation/manual/ja-JP/src/no-idea-what-to-do-with-these-yet/styles/
trunk/Hibernate3/documentation/manual/ja-JP/src/no-idea-what-to-do-with-these-yet/styles/fopdf.xsl
trunk/Hibernate3/documentation/manual/ja-JP/src/no-idea-what-to-do-with-these-yet/styles/html.xsl
trunk/Hibernate3/documentation/manual/ja-JP/src/no-idea-what-to-do-with-these-yet/styles/html_chunk.xsl
trunk/Hibernate3/documentation/manual/ko-KR/
trunk/Hibernate3/documentation/manual/ko-KR/README-KO.txt
trunk/Hibernate3/documentation/manual/ko-KR/pom.xml
trunk/Hibernate3/documentation/manual/ko-KR/src/
trunk/Hibernate3/documentation/manual/ko-KR/src/main/
trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/
trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/master.xml
trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/
trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/architecture.xml
trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/association_mapping.xml
trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/basic_mapping.xml
trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/batch.xml
trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/best_practices.xml
trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/collection_mapping.xml
trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/component_mapping.xml
trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/configuration.xml
trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/events.xml
trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/example_mappings.xml
trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/example_parentchild.xml
trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/example_weblog.xml
trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/filters.xml
trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/inheritance_mapping.xml
trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/performance.xml
trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/persistent_classes.xml
trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/query_criteria.xml
trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/query_hql.xml
trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/query_sql.xml
trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/quickstart.xml
trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/session_api.xml
trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/toolset_guide.xml
trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/transactions.xml
trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/tutorial.xml
trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/xml.xml
trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/
trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/
trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/css/
trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/css/html.css
trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/
trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/AuthorWork.gif
trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/AuthorWork.zargo
trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/CustomerOrderProduct.gif
trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/CustomerOrderProduct.zargo
trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/EmployerEmployee.gif
trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/EmployerEmployee.zargo
trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/full_cream.gif
trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/full_cream.svg
trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/hibernate_logo_a.png
trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/lite.gif
trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/lite.svg
trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/overview.gif
trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/overview.svg
trunk/Hibernate3/documentation/manual/ko-KR/src/no-idea-what-to-do-with-these-yet/
trunk/Hibernate3/documentation/manual/ko-KR/src/no-idea-what-to-do-with-these-yet/fop/
trunk/Hibernate3/documentation/manual/ko-KR/src/no-idea-what-to-do-with-these-yet/fop/Bangwool.ttf
trunk/Hibernate3/documentation/manual/ko-KR/src/no-idea-what-to-do-with-these-yet/fop/Bangwool.xml
trunk/Hibernate3/documentation/manual/ko-KR/src/no-idea-what-to-do-with-these-yet/fop/Hankc.ttf
trunk/Hibernate3/documentation/manual/ko-KR/src/no-idea-what-to-do-with-these-yet/fop/Hankc.xml
trunk/Hibernate3/documentation/manual/ko-KR/src/no-idea-what-to-do-with-these-yet/fop/userconfig.xml
trunk/Hibernate3/documentation/manual/ko-KR/src/no-idea-what-to-do-with-these-yet/styles/
trunk/Hibernate3/documentation/manual/ko-KR/src/no-idea-what-to-do-with-these-yet/styles/fopdf.xsl
trunk/Hibernate3/documentation/manual/ko-KR/src/no-idea-what-to-do-with-these-yet/styles/html.xsl
trunk/Hibernate3/documentation/manual/ko-KR/src/no-idea-what-to-do-with-these-yet/styles/html_chunk.xsl
trunk/Hibernate3/documentation/manual/pom.xml
trunk/Hibernate3/documentation/manual/pt-BR/
trunk/Hibernate3/documentation/manual/pt-BR/pom.xml
trunk/Hibernate3/documentation/manual/pt-BR/src/
trunk/Hibernate3/documentation/manual/pt-BR/src/main/
trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/
trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/master.xml
trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/
trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/architecture.xml
trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/association_mapping.xml
trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/basic_mapping.xml
trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/batch.xml
trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/best_practices.xml
trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/collection_mapping.xml
trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/component_mapping.xml
trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/configuration.xml
trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/events.xml
trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/example_mappings.xml
trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/example_parentchild.xml
trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/example_weblog.xml
trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/filters.xml
trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/inheritance_mapping.xml
trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/performance.xml
trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/persistent_classes.xml
trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/query_criteria.xml
trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/query_hql.xml
trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/query_sql.xml
trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/session_api.xml
trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/toolset_guide.xml
trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/transactions.xml
trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/tutorial.xml
trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/tutorial1.xml
trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/xml.xml
trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/
trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/
trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/css/
trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/css/html.css
trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/
trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/AuthorWork.gif
trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/AuthorWork.zargo
trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/CustomerOrderProduct.gif
trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/CustomerOrderProduct.zargo
trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/EmployerEmployee.gif
trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/EmployerEmployee.zargo
trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/full_cream.gif
trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/full_cream.svg
trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/hibernate_logo_a.png
trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/lite.gif
trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/lite.svg
trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/overview.gif
trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/overview.svg
trunk/Hibernate3/documentation/manual/zh-CN/
trunk/Hibernate3/documentation/manual/zh-CN/README_CHINESE_FONT.txt
trunk/Hibernate3/documentation/manual/zh-CN/pom.xml
trunk/Hibernate3/documentation/manual/zh-CN/src/
trunk/Hibernate3/documentation/manual/zh-CN/src/main/
trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/
trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/glossary.txt
trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/master.xml
trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/
trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/architecture.xml
trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/association_mapping.xml
trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/basic_mapping.xml
trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/batch.xml
trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/best_practices.xml
trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/collection_mapping.xml
trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/component_mapping.xml
trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/configuration.xml
trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/events.xml
trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/example_mappings.xml
trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/example_parentchild.xml
trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/example_weblog.xml
trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/filters.xml
trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/inheritance_mapping.xml
trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/kicken.pl
trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/performance.xml
trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/persistent_classes.xml
trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/query_criteria.xml
trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/query_hql.xml
trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/query_sql.xml
trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/quickstart.xml
trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/session_api.xml
trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/toolset_guide.xml
trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/transactions.xml
trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/tutorial.xml
trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/xml.xml
trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/
trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/
trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/css/
trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/css/html.css
trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/
trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/AuthorWork.gif
trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/AuthorWork.zargo
trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/CustomerOrderProduct.gif
trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/CustomerOrderProduct.zargo
trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/EmployerEmployee.gif
trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/EmployerEmployee.zargo
trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/full_cream.gif
trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/full_cream.svg
trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/hibernate_logo_a.png
trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/lite.gif
trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/lite.svg
trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/overview.gif
trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/overview.svg
trunk/Hibernate3/documentation/manual/zh-CN/src/no-idea-what-to-do-with-these-yet/
trunk/Hibernate3/documentation/manual/zh-CN/src/no-idea-what-to-do-with-these-yet/fop/
trunk/Hibernate3/documentation/manual/zh-CN/src/no-idea-what-to-do-with-these-yet/fop/simhei.xml
trunk/Hibernate3/documentation/manual/zh-CN/src/no-idea-what-to-do-with-these-yet/fop/simsun.xml
trunk/Hibernate3/documentation/manual/zh-CN/src/no-idea-what-to-do-with-these-yet/fop/userconfig.xml
trunk/Hibernate3/documentation/manual/zh-CN/src/no-idea-what-to-do-with-these-yet/styles/
trunk/Hibernate3/documentation/manual/zh-CN/src/no-idea-what-to-do-with-these-yet/styles/fopdf.xsl
trunk/Hibernate3/documentation/manual/zh-CN/src/no-idea-what-to-do-with-these-yet/styles/html.css
trunk/Hibernate3/documentation/manual/zh-CN/src/no-idea-what-to-do-with-these-yet/styles/html.xsl
trunk/Hibernate3/documentation/manual/zh-CN/src/no-idea-what-to-do-with-these-yet/styles/html_chunk.xsl
trunk/Hibernate3/documentation/pom.xml
trunk/Hibernate3/documentation/tutorial/
trunk/Hibernate3/documentation/tutorial/pom.xml
trunk/Hibernate3/documentation/tutorial/src/
trunk/Hibernate3/documentation/tutorial/src/main/
trunk/Hibernate3/documentation/tutorial/src/main/java/
trunk/Hibernate3/documentation/tutorial/src/main/java/events/
trunk/Hibernate3/documentation/tutorial/src/main/java/events/Event.hbm.xml
trunk/Hibernate3/documentation/tutorial/src/main/java/events/Event.java
trunk/Hibernate3/documentation/tutorial/src/main/java/events/EventManager.java
trunk/Hibernate3/documentation/tutorial/src/main/java/events/EventManagerServlet.java
trunk/Hibernate3/documentation/tutorial/src/main/java/events/Person.hbm.xml
trunk/Hibernate3/documentation/tutorial/src/main/java/events/Person.java
trunk/Hibernate3/documentation/tutorial/src/main/java/util/
trunk/Hibernate3/documentation/tutorial/src/main/java/util/HibernateUtil.java
trunk/Hibernate3/documentation/tutorial/src/main/resources/
trunk/Hibernate3/documentation/tutorial/src/main/resources/hibernate.cfg.xml
trunk/Hibernate3/documentation/tutorial/src/main/resources/log4j.properties
trunk/Hibernate3/documentation/tutorial/src/main/scripts/
trunk/Hibernate3/documentation/tutorial/src/main/scripts/runCleanDatabase.sh
trunk/Hibernate3/documentation/tutorial/src/main/webapp/
trunk/Hibernate3/documentation/tutorial/src/main/webapp/WEB-INF/
trunk/Hibernate3/documentation/tutorial/src/main/webapp/WEB-INF/web.xml
trunk/Hibernate3/eg/pom.xml
trunk/Hibernate3/eg/src/
trunk/Hibernate3/eg/src/main/
trunk/Hibernate3/eg/src/main/java/
trunk/Hibernate3/eg/src/main/java/org/
trunk/Hibernate3/eg/src/main/java/org/hibernate/
trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/
trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/AuctionInfo.java
trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/AuctionItem.hbm.xml
trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/AuctionItem.java
trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/Bid.hbm.xml
trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/Bid.java
trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/BuyNow.java
trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/Main.java
trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/Name.java
trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/Persistent.java
trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/User.hbm.xml
trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/User.java
trunk/Hibernate3/eg/src/main/resources/
trunk/Hibernate3/eg/src/main/resources/org/
trunk/Hibernate3/eg/src/main/resources/org/hibernate/
trunk/Hibernate3/eg/src/main/resources/org/hibernate/auction/
trunk/Hibernate3/eg/src/main/resources/org/hibernate/auction/AuctionItem.hbm.xml
trunk/Hibernate3/eg/src/main/resources/org/hibernate/auction/Bid.hbm.xml
trunk/Hibernate3/eg/src/main/resources/org/hibernate/auction/User.hbm.xml
trunk/Hibernate3/jmx/
trunk/Hibernate3/jmx/pom.xml
trunk/Hibernate3/jmx/src/
trunk/Hibernate3/jmx/src/main/
trunk/Hibernate3/jmx/src/main/java/
trunk/Hibernate3/jmx/src/main/java/org/
trunk/Hibernate3/jmx/src/main/java/org/hibernate/
trunk/Hibernate3/jmx/src/main/java/org/hibernate/jmx/
trunk/Hibernate3/jmx/src/main/java/org/hibernate/jmx/HibernateService.java
trunk/Hibernate3/jmx/src/main/java/org/hibernate/jmx/HibernateServiceMBean.java
trunk/Hibernate3/jmx/src/main/java/org/hibernate/jmx/SessionFactoryStub.java
trunk/Hibernate3/jmx/src/main/java/org/hibernate/jmx/StatisticsService.java
trunk/Hibernate3/jmx/src/main/java/org/hibernate/jmx/StatisticsServiceMBean.java
trunk/Hibernate3/jmx/src/main/java/org/hibernate/jmx/package.html
trunk/Hibernate3/testing/
trunk/Hibernate3/testing/pom.xml
trunk/Hibernate3/testing/src/
trunk/Hibernate3/testing/src/main/
trunk/Hibernate3/testing/src/main/java/
trunk/Hibernate3/testing/src/main/java/org/
trunk/Hibernate3/testing/src/main/java/org/hibernate/
trunk/Hibernate3/testing/src/main/java/org/hibernate/junit/
trunk/Hibernate3/testing/src/main/java/org/hibernate/junit/AbstractClassLoaderIsolatedTestCase.java
trunk/Hibernate3/testing/src/main/java/org/hibernate/junit/SkipLog.java
trunk/Hibernate3/testing/src/main/java/org/hibernate/junit/TestSuiteVisitor.java
trunk/Hibernate3/testing/src/main/java/org/hibernate/junit/UnitTestCase.java
trunk/Hibernate3/testing/src/main/java/org/hibernate/junit/functional/
trunk/Hibernate3/testing/src/main/java/org/hibernate/junit/functional/DatabaseSpecificFunctionalTestCase.java
trunk/Hibernate3/testing/src/main/java/org/hibernate/junit/functional/ExecutionEnvironment.java
trunk/Hibernate3/testing/src/main/java/org/hibernate/junit/functional/FunctionalTestCase.java
trunk/Hibernate3/testing/src/main/java/org/hibernate/junit/functional/FunctionalTestClassTestSuite.java
trunk/Hibernate3/testsuite/
trunk/Hibernate3/testsuite/pom.xml
trunk/Hibernate3/testsuite/src/
trunk/Hibernate3/testsuite/src/test/
trunk/Hibernate3/testsuite/src/test/java/
trunk/Hibernate3/testsuite/src/test/java/org/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/AllTests.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/FailureExpectedCollector.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/TestCase.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/TestSelector.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/cid/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/cid/AbstractCompositeIdTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/cid/Mappings.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/cid/MyInterface.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/cid/MyInterfaceImpl.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/propertyref/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/propertyref/AbstractComponentPropertyRefTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/propertyref/Address.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/propertyref/AddressImpl.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/propertyref/Mappings.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/propertyref/Server.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/propertyref/ServerImpl.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/Address.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/AnyTypeTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/ComplexPropertyValue.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/IntegerPropertyValue.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/Person.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/Person.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/Properties.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/PropertySet.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/PropertyValue.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/StringPropertyValue.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/array/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/array/A.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/array/A.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/array/ArrayTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/array/B.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ast/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ast/ASTIteratorTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ast/ASTUtilTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/batch/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/batch/BatchTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/batch/DataPoint.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/batch/DataPoint.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/batchfetch/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/batchfetch/BatchFetchTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/batchfetch/Model.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/batchfetch/ProductLine.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/batchfetch/ProductLine.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bidi/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bidi/Auction.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bidi/Auction.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bidi/Auction2.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bidi/AuctionTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bidi/AuctionTest2.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bidi/Bid.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/Bean.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/Bean.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/BeanReflectionHelper.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/BytecodeSuite.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/ProxyBean.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/cglib/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/cglib/CGLIBThreadLocalTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/cglib/InvocationTargetExceptionTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/cglib/ReflectionOptimizerTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/javassist/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/javassist/InvocationTargetExceptionTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/javassist/ReflectionOptimizerTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/BaseCacheProviderTestCase.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/CacheSuite.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/Item.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/Item.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/VersionedItem.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/ehcache/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/ehcache/EhCacheTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/ehcache/ehcache.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/treecache/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/treecache/optimistic/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/treecache/optimistic/OptimisticTreeCacheTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/treecache/optimistic/treecache.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/treecache/pessimistic/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/treecache/pessimistic/TreeCacheTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/treecache/pessimistic/treecache.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cascade/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cascade/Job.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cascade/Job.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cascade/JobBatch.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cascade/JobBatch.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cascade/RefreshTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cfg/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cfg/Cacheable.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cfg/CacheableFileTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/CompositeIdTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/Customer.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/Customer.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/LineItem.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/LineItem.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/Order.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/Order.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/Product.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/Product.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/CollectionSuite.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/bag/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/bag/BagOwner.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/bag/Mappings.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/bag/PersistentBagTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/idbag/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/idbag/IdbagOwner.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/idbag/Mappings.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/idbag/PersistentIdBagTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/list/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/list/ListOwner.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/list/Mappings.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/list/PersistentListTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/map/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/map/Child.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/map/Mappings.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/map/Parent.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/map/PersistentMapTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/original/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/original/CollectionTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/original/Email.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/original/Permission.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/original/User.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/original/UserPermissions.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/set/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/set/Child.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/set/Mappings.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/set/Parent.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/set/PersistentSetTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/ComponentSuite.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/basic/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/basic/ComponentTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/basic/Employee.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/basic/Person.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/basic/User.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/basic/User.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/collection/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/collection/CascadeToComponentCollectionTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/collection/Definition.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/collection/LocalizedStrings.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/collection/Mappings.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/collection/Value.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/toone/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/toone/Address.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/toone/CascadeToComponentAssociationTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/toone/Document.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/toone/Mappings.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/toone/PersonalInfo.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/toone/User.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/compositeelement/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/compositeelement/Child.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/compositeelement/CompositeElementTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/compositeelement/Parent.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/compositeelement/Parent.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/AggressiveReleaseTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/BasicConnectionProviderTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/ConnectionManagementTestCase.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/ConnectionsSuite.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/CurrentSessionConnectionTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/Other.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/Silly.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/Silly.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/SuppliedConnectionTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/ThreadLocalCurrentSessionTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/criteria/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/criteria/Course.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/criteria/CriteriaQueryTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/criteria/Enrolment.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/criteria/Enrolment.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/criteria/Student.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/criteria/StudentDTO.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cuk/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cuk/Account.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cuk/Address.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cuk/CompositePropertyRefTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cuk/Person.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cuk/Person.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cut/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cut/CompositeUserTypeTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cut/MonetoryAmount.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cut/MonetoryAmountUserType.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cut/Transaction.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cut/Transaction.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cut/types.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/deletetransient/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/deletetransient/Address.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/deletetransient/DeleteTransientEntityTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/deletetransient/Person.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/deletetransient/Person.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dialect/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dialect/functional/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dialect/functional/DialectFunctionalTestsSuite.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dialect/functional/cache/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dialect/functional/cache/SQLFunctionsInterSystemsTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dialect/functional/cache/TestInterSystemsFunctionsClass.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dialect/functional/cache/TestInterSystemsFunctionsClass.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dialect/unit/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dialect/unit/DialectUnitTestsSuite.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dialect/unit/lockhint/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dialect/unit/lockhint/AbstractLockHintTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dialect/unit/lockhint/SQLServerLockHintsTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dialect/unit/lockhint/SybaseLockHintsTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/discriminator/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/discriminator/Address.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/discriminator/Customer.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/discriminator/DiscriminatorTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/discriminator/Employee.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/discriminator/Person.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/discriminator/Person.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/Address.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/Company.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/Customer.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/DataProxyHandler.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/DynamicEntitySuite.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/Person.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/ProxyHelper.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/interceptor/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/interceptor/Customer.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/interceptor/InterceptorDynamicEntityTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/interceptor/ProxyInterceptor.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/package.html
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/tuplizer/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/tuplizer/Customer.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/tuplizer/EntityNameInterceptor.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/tuplizer/MyEntityInstantiator.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/tuplizer/MyEntityTuplizer.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/tuplizer/TuplizerDynamicEntityTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ecid/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ecid/Course.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ecid/Course.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ecid/EmbeddedCompositeIdTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ecid/UniversityCourse.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/EntityModeSuite.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/Dom4jSuite.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/accessors/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/accessors/Dom4jAccessorTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/basic/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/basic/AB.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/basic/Account.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/basic/Dom4jTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/basic/Employer.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/many2one/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/many2one/Car.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/many2one/Car.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/many2one/CarPart.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/many2one/CarType.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/many2one/Dom4jManyToOneTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/map/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/map/MapSuite.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/map/basic/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/map/basic/DynamicClassTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/map/basic/ProductLine.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/multi/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/multi/MultiRepresentationTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/multi/Stock.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/multi/Stock.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/multi/Valuation.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/multi/Valuation.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/exception/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/exception/Group.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/exception/Group.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/exception/SQLExceptionConversionTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/exception/User.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/exception/User.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/Customer.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/Customer.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/Employee.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/Employee.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/ExtendsTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/Person.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/Person.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/allinone.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/allseparateinone.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/entitynames.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/packageentitynames.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/unionsubclass.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extralazy/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extralazy/Document.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extralazy/ExtraLazyTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extralazy/Group.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extralazy/SessionAttribute.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extralazy/User.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extralazy/UserGroup.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Category.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Category.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Department.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Department.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/DynamicFilterTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/LineItem.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/LineItem.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Order.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Order.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Product.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Product.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Salesperson.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Salesperson.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/defs.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/formulajoin/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/formulajoin/Detail.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/formulajoin/FormulaJoinTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/formulajoin/Master.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/formulajoin/Master.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/AbstractGeneratedPropertyTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/ComponentOwner.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/ComponentOwner.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/GeneratedPropertyEntity.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/GeneratedPropertyEntity.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/GeneratedPropertySuite.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/MSSQLGeneratedPropertyEntity.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/PartiallyGeneratedComponentTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/TimestampGeneratedValuesWithCachingTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/TriggerGeneratedValuesWithCachingTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/TriggerGeneratedValuesWithoutCachingTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/GeneratedKeysSuite.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/identity/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/identity/IdentityGeneratedKeysTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/identity/MyChild.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/identity/MyEntity.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/identity/MyEntity.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/identity/MySibling.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/select/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/select/MyEntity.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/select/MyEntity.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/select/SelectGeneratorTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/seqidentity/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/seqidentity/MyEntity.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/seqidentity/MyEntity.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/seqidentity/SequenceIdentityTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/ASTParserLoadingTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/ASTQueryTranslatorTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Address.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Animal.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Animal.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/BooleanLiteralEntity.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/BooleanLiteralEntity.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/BulkManipulationTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Car.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Cat.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/ClassicTranslatorTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Classification.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/ClassificationType.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/ComponentContainer.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/ComponentContainer.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/CrazyCompositeKey.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/CrazyIdFieldNames.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/CriteriaClassicAggregationReturnTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/CriteriaHQLAlignmentTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Dog.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/DomesticAnimal.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/EJBQLTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/EntityWithCrazyCompositeKey.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/EntityWithCrazyCompositeKey.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/FooBarCopy.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/HQLSuite.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/HQLTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/HeresAnotherCrazyIdFieldName.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/HqlParserTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Human.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/IntegerVersioned.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Joiner.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/KeyManyToOneEntity.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/KeyManyToOneEntity.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/KeyManyToOneKeyEntity.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Lizard.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Mammal.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/MoreCrazyIdFieldNameStuffEntity.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Name.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/PettingZoo.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Pickup.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/QueryTranslatorTestCase.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Reptile.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/SUV.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/ScrollableCollectionFetchingTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/SimpleAssociatedEntity.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/SimpleEntityWithAssociation.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/SimpleEntityWithAssociation.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/StateProvince.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/TimestampVersioned.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Truck.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/User.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Vehicle.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Vehicle.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Versions.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/WithClauseTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Zoo.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/Car.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/Car.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/MultipleHiLoPerTableGeneratorTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/Plane.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/Plane.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/Product.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/Product.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/Radio.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/Radio.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/UseIdentifierRollbackTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idbag/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idbag/Group.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idbag/IdBagTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idbag/User.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idbag/UserGroup.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idclass/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idclass/Customer.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idclass/Customer.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idclass/CustomerId.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idclass/FavoriteCustomer.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idclass/IdClassTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/IdGenSuite.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/OptimizerUnitTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/SequenceStyleConfigUnitTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/Basic.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/BasicForcedTableSequenceTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/Entity.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/HiLo.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/HiLoForcedTableSequenceTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/Pooled.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/PooledForcedTableSequenceTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/sequence/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/sequence/Basic.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/sequence/BasicSequenceTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/sequence/Entity.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/sequence/HiLo.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/sequence/HiLoSequenceTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/sequence/Pooled.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/sequence/PooledSequenceTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/table/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/table/Basic.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/table/BasicTableTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/table/Entity.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/table/HiLo.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/table/HiLoTableTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/table/Pooled.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/table/PooledTableTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idprops/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idprops/IdentifierPropertyReferencesTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idprops/LineItem.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idprops/LineItemPK.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idprops/Mapping.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idprops/Order.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idprops/Person.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/immutable/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/immutable/Contract.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/immutable/ContractVariation.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/immutable/ContractVariation.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/immutable/ImmutableTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/insertordering/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/insertordering/Group.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/insertordering/InsertOrderingTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/insertordering/Mapping.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/insertordering/Membership.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/insertordering/User.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/buildtime/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/buildtime/InstrumentTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/AbstractExecutable.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/Executable.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/TestDirtyCheckExecutable.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/TestFetchAllExecutable.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/TestInjectFieldInterceptorExecutable.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/TestIsPropertyInitializedExecutable.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/TestLazyExecutable.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/TestLazyManyToOneExecutable.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/TestLazyPropertyCustomTypeExecutable.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/TestManyToOneProxyExecutable.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/domain/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/domain/CustomBlobType.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/domain/Document.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/domain/Documents.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/domain/Entity.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/domain/Folder.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/domain/Owner.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/domain/Problematic.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/domain/Problematic.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/runtime/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/runtime/AbstractTransformingClassLoaderInstrumentTestCase.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/runtime/CGLIBInstrumentationTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/runtime/JavassistInstrumentationTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/CollectionInterceptor.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/Image.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/Image.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/InterceptorTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/Log.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/PropertyInterceptor.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/StatefulInterceptor.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/User.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/User.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/Document.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/DocumentImpl.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/DocumentInterceptor.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/Folder.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/FolderImpl.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/InterfaceProxyTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/Item.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/Item.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/ItemImpl.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/SecureDocument.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/SecureDocumentImpl.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/iterate/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/iterate/Item.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/iterate/Item.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/iterate/IterateTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/join/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/join/Customer.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/join/Employee.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/join/JoinTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/join/Person.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/join/Person.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/join/User.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinedsubclass/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinedsubclass/Address.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinedsubclass/Customer.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinedsubclass/Employee.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinedsubclass/JoinedSubclassTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinedsubclass/Person.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinedsubclass/Person.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/Bid.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/Category.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/Comment.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/Group.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/Item.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/ItemBid.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/JoinFetchTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/User.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/UserGroup.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/AbstractJPATest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/Item.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/Item.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/JPAComplianceSuite.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/MyEntity.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/MyEntity.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/MySubclassEntity.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/Part.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/Part.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/CascadeTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/Child.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/ChildAssigned.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/ChildInfo.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/ChildInfoAssigned.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/Other.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/OtherAssigned.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/Parent.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/ParentAssigned.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/ParentChild.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/ParentInfo.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/ParentInfoAssigned.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/fetch/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/fetch/FetchingTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/fetch/Person.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/fetch/Person.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/fetch/Stay.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/lock/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/lock/JPALockTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/lock/RepeatableReadTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/package.html
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/proxy/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/proxy/JPAProxyTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/ql/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/ql/JPAQLComplianceTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/ql/NativeQueryTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/removed/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/removed/RemovedEntityTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/KeyManyToOneSuite.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/component/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/component/Customer.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/component/EagerKeyManyToOneTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/component/EagerMapping.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/component/LazyKeyManyToOneTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/component/LazyMapping.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/component/Order.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/embedded/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/embedded/Customer.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/embedded/KeyManyToOneTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/embedded/Mapping.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/embedded/Order.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazycache/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazycache/Document.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazycache/Documents.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazycache/InstrumentCacheTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazycache/InstrumentCacheTest2.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazyonetoone/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazyonetoone/Employee.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazyonetoone/Employment.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazyonetoone/LazyOneToOneTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazyonetoone/Person.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazyonetoone/Person.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/.cvsignore
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/A.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ABC.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ABCExtends.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ABCProxy.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ABCProxyTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ABCTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Abstract.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/AbstractProxy.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/AltSimple.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Assignable.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/B.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Bar.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/BarProxy.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/BasicNameable.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Baz.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Baz.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Blobber.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Blobber.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Broken.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Broken.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/C1.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/C2.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/CacheTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Category.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Category.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Child.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Circular.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Circular.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Commento.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Company.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Component.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ComponentCollection.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ComponentNotNull.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ComponentNotNullMaster.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ComponentNotNullMaster.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ComponentNotNullTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Componentizable.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Componentizable.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/CompositeElement.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/CompositeIdId.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/CompositeIdId.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ConfigurationPerformanceTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Contained.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Container.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Container.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Custom.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Custom.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/CustomPersister.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/CustomSQL.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/CustomSQLTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/D.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Detail.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/DoubleStringType.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Down.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Drug.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/E.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Eye.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Eye.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Fee.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Fee.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Fixed.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Fo.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Fo.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Foo.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/FooBar.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/FooBarTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/FooComponent.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/FooProxy.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Fum.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Fum.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/FumCompositeID.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/FumTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Fumm.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Fumm.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Glarch.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Glarch.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/GlarchProxy.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Holder.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Holder.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/I.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/IJ.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/IJ2.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/IJ2Test.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/IJTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Immutable.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Immutable.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Inner.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/InnerKey.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Intervention.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/J.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Jay.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/K.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/LegacyTestCase.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Location.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Location.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Lower.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MainObject.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MainObject.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Many.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Many.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Map.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MapTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Marelo.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Master.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MasterDetail.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MasterDetailTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Medication.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Middle.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Middle.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MiddleKey.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Mono.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MoreStuff.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Multi.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Multi.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MultiExtends.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MultiTableTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Multiplicity.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MultiplicityType.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Nameable.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Nameable.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Named.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/NestingComponent.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/NewPerformanceTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/NewerPerformanceTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/NonReflectiveBinderTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Object2.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Object2.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/One.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/One.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/OneToOneCacheTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Outer.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/OuterKey.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Parent.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ParentChild.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ParentChildTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Part.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Party.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/PerformanceTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Person.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Po.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/QueryByExampleTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Qux.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Qux.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Resource.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Result.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ReverseComparator.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Role.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/S.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/SQLFunctionsTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/SQLLoaderTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Several.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Simple.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Simple.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Single.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/SingleSeveral.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Sortable.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/StatisticsTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/StringComparator.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Stuff.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Stuff.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/SubComponent.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/SubDetail.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/SubMulti.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Super.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Top.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Trivial.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/TrivialClass.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Up.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/UpDown.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Vetoer.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Vetoer.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/W.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/WZ.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Wicked.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/X.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/XY.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Y.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Z.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lob/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lob/BlobTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lob/ClobTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lob/LobHolder.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lob/LobMappings.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lob/LobSuite.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lob/MaterializedBlobType.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lob/SerializableData.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lob/SerializableTypeTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/manytomany/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/manytomany/Group.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/manytomany/ManyToManyTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/manytomany/User.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/manytomany/UserGroup.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/manytomany/ordered/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/manytomany/ordered/Group.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/manytomany/ordered/OrderedManyToManyTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/manytomany/ordered/User.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/manytomany/ordered/UserGroup.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/map/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/map/Group.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/map/MapIndexFormulaTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/map/SessionAttribute.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/map/User.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/map/UserGroup.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapcompelem/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapcompelem/Item.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapcompelem/MapCompositeElementTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapcompelem/Part.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapcompelem/Product.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapcompelem/ProductPart.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapelemformula/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapelemformula/Group.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapelemformula/MapElementFormulaTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapelemformula/User.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapelemformula/UserGroup.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapping/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapping/PersistentClassVisitorTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapping/ValueVisitorTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mappingexception/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mappingexception/InvalidMapping.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mappingexception/InvalidMapping.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mappingexception/MappingExceptionTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mappingexception/User.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mappingexception/User.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mixed/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mixed/Document.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mixed/DocumentInterceptor.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mixed/Folder.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mixed/Item.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mixed/Item.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mixed/MixedTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mixed/SecureDocument.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/naturalid/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/naturalid/NaturalIdTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/naturalid/User.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/naturalid/User.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ondelete/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ondelete/Address.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ondelete/Employee.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ondelete/OnDeleteTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ondelete/Person.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ondelete/Person.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ondelete/Salesperson.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetomany/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetomany/Child.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetomany/OneToManyTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetomany/Parent.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetomany/Parent.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/OneToOneSuite.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/formula/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/formula/Address.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/formula/OneToOneFormulaTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/formula/Person.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/formula/Person.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/joined/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/joined/Address.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/joined/Entity.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/joined/JoinedSubclassOneToOneTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/joined/Org.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/joined/Person.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/joined/Person.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/link/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/link/Customer.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/link/Employee.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/link/OneToOneLinkTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/link/Person.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/link/Person.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/nopojo/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/nopojo/DynamicMapOneToOneTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/nopojo/Person.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/optional/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/optional/Address.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/optional/Entity.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/optional/OptionalOneToOneTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/optional/Org.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/optional/Person.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/optional/Person.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/singletable/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/singletable/Address.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/singletable/DiscrimSubclassOneToOneTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/singletable/Entity.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/singletable/Org.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/singletable/Person.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/singletable/Person.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/AbstractOperationTestCase.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Address.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Competition.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Competition.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Competitor.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/CreateTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/DeleteTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Employee.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Employer.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Employer.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/GetLoadTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/MergeTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Node.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Node.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/NumberedNode.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/OneToOne.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/OpsSuite.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/OptLockEntity.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Person.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/PersonalDetails.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/SaveOrUpdateTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/TimestampedEntity.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/VersionedEntity.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/optlock/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/optlock/Document.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/optlock/Document.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/optlock/OptimisticLockTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/optlock/PublicationDate.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ordered/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ordered/OrderByTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ordered/Search.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ordered/Search.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/Mail.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/Mail.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/OrphanSuite.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/OrphanTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/Part.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/Product.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/Product.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/PropertyRefTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/User.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/User.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/pagination/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/pagination/DataPoint.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/pagination/DataPoint.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/pagination/PaginationTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/pretty/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/pretty/SQLFormatterTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/PropertyRefSuite.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/basic/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/basic/Account.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/basic/Address.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/basic/Group.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/basic/Person.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/basic/Person.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/basic/PropertyRefTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/complete/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/complete/Account.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/complete/CompleteComponentPropertyRefTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/complete/Identity.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/complete/Mapping.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/complete/Person.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/partial/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/partial/Account.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/partial/Identity.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/partial/Mapping.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/partial/PartialComponentPropertyRefTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/partial/Person.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/discrim/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/discrim/Account.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/discrim/Customer.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/discrim/Person.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/discrim/Person.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/discrim/SubclassPropertyRefTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/joined/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/joined/Account.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/joined/BankAccount.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/joined/JoinedSubclassPropertyRefTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/joined/Person.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/joined/Person.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/union/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/union/Account.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/union/Customer.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/union/Person.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/union/Person.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/union/UnionSubclassPropertyRefTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/proxy/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/proxy/Container.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/proxy/DataPoint.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/proxy/DataPoint.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/proxy/Info.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/proxy/Owner.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/proxy/ProxyTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/querycache/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/querycache/Item.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/querycache/Item.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/querycache/QueryCacheTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/readonly/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/readonly/DataPoint.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/readonly/DataPoint.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/reattachment/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/reattachment/Child.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/reattachment/CollectionReattachmentTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/reattachment/Mappings.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/reattachment/Parent.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/reattachment/ProxyReattachmentTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/reattachment/ReattachmentSuite.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/rowid/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/rowid/Point.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/rowid/Point.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/rowid/RowIdTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/rowid/RowIdType.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/schemaupdate/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/schemaupdate/1_Version.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/schemaupdate/2_Version.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/schemaupdate/MigrationTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/schemaupdate/Version.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sorted/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sorted/Search.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sorted/Search.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sorted/SortTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/NativeSqlSupportSuite.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/check/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/check/CheckSuite.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/check/ExceptionCheckingEntity.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/check/OracleCheckStyleTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/check/ParamCheckingEntity.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/check/ResultCheckStyleTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/check/oracle-mappings.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/Dimension.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/Employment.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/MonetaryAmount.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/MonetaryAmountUserType.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/Order.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/Organization.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/Person.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/Product.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/SpaceShip.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/Speech.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/CustomSQLTestSupport.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/CustomStoredProcTestSupport.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/datadirect/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/datadirect/oracle/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/datadirect/oracle/DataDirectOracleCustomSQLTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/datadirect/oracle/StoredProcedures.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/db2/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/db2/DB2CustomSQLTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/db2/Mappings.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/mysql/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/mysql/Mappings.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/mysql/MySQLCustomSQLTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/oracle/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/oracle/Mappings.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/oracle/OracleCustomSQLTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/oracle/StoredProcedures.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/sybase/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/sybase/Mappings.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/sybase/SybaseCustomSQLTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/identity/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/identity/CustomInsertSQLWithIdentityColumnTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/identity/Mappings.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/query/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/query/NativeSQLQueries.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/query/NativeSQLQueriesTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stateless/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stateless/Document.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stateless/Document.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stateless/Paper.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stateless/StatelessSessionTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stats/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stats/Continent.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stats/Continent.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stats/Continent2.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stats/Country.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stats/Locality.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stats/Province.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stats/SessionStatsTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stats/State.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stats/StatsTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subclassfilter/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subclassfilter/Customer.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subclassfilter/DiscrimSubclassFilterTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subclassfilter/Employee.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subclassfilter/JoinedSubclassFilterTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subclassfilter/Person.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subclassfilter/UnionSubclassFilterTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subclassfilter/discrim-subclass.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subclassfilter/joined-subclass.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subclassfilter/union-subclass.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselect/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselect/Alien.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselect/Being.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselect/Beings.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselect/Human.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselect/SubselectTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselectfetch/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselectfetch/Child.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselectfetch/Parent.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselectfetch/ParentChild.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselectfetch/SubselectFetchTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ternary/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ternary/Employee.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ternary/Site.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ternary/Ternary.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ternary/TernaryTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/timestamp/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/timestamp/Person.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/timestamp/TimestampTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/timestamp/User.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/timestamp/User.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tm/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tm/CMTTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tm/DummyConnectionProvider.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tm/DummyJTAStyleTransationFactory.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tm/DummyTransaction.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tm/DummyTransactionManager.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tm/DummyTransactionManagerLookup.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tm/Item.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tool/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tool/Team.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tool/Team.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tool/TestSchemaTools.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedmanytoone/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedmanytoone/Address.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedmanytoone/AddressId.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedmanytoone/Customer.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedmanytoone/Customer.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedmanytoone/TypedManyToOneTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedonetoone/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedonetoone/Address.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedonetoone/AddressId.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedonetoone/Customer.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedonetoone/Customer.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedonetoone/TypedOneToOneTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typeparameters/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typeparameters/DefaultValueIntegerType.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typeparameters/TypeParameterTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typeparameters/Typedef.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typeparameters/Widget.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typeparameters/Widget.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unconstrained/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unconstrained/Employee.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unconstrained/Person.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unconstrained/Person.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unconstrained/UnconstrainedTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unidir/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unidir/BackrefTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unidir/Child.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unidir/Parent.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unidir/ParentChild.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Alien.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Being.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Beings.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Employee.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Hive.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Human.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Location.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Thing.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/UnionSubclassTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass2/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass2/Address.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass2/Customer.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass2/Employee.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass2/Person.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass2/Person.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass2/UnionSubclassTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/UserCollectionTypeSuite.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/basic/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/basic/Email.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/basic/IMyList.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/basic/MyList.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/basic/MyListType.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/basic/PersistentMyList.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/basic/User.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/basic/UserCollectionTypeTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/basic/UserPermissions.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/parameterized/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/parameterized/DefaultableList.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/parameterized/DefaultableListImpl.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/parameterized/DefaultableListType.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/parameterized/Entity.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/parameterized/Mapping.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/parameterized/ParameterizedUserCollectionTypeTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/parameterized/PersistentDefaultableList.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/util/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/util/PropertiesHelperTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/util/StringHelperTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/util/UtilSuite.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/util/dtd/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/util/dtd/Child.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/util/dtd/EntityResolverTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/util/dtd/Parent.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/util/dtd/Parent.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/util/dtd/child.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/Person.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/PersonThing.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/Task.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/Thing.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/VersionTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/db/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/db/DbVersionTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/db/Group.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/db/Permission.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/db/User.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/db/User.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/sybase/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/sybase/Group.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/sybase/Permission.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/sybase/SybaseTimestampVersioningTest.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/sybase/User.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/sybase/User.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/where/
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/where/File.hbm.xml
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/where/File.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/where/NumericTrueFalseType.java
trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/where/WhereTest.java
Removed:
trunk/Hibernate3/build.bat
trunk/Hibernate3/build.sh
trunk/Hibernate3/build.xml
trunk/Hibernate3/checkstyle_checks.xml
trunk/Hibernate3/doc/
trunk/Hibernate3/eg/org/
trunk/Hibernate3/grammar/
trunk/Hibernate3/indent.py
trunk/Hibernate3/jdbc/
trunk/Hibernate3/lib/
trunk/Hibernate3/src/
trunk/Hibernate3/test/
Log:
maven migration
Deleted: trunk/Hibernate3/build.bat
===================================================================
--- trunk/Hibernate3/build.bat 2007-05-24 19:06:06 UTC (rev 11562)
+++ trunk/Hibernate3/build.bat 2007-05-25 20:19:29 UTC (rev 11563)
@@ -1 +0,0 @@
-java -cp "lib/ant-launcher-1.6.5.jar" org.apache.tools.ant.launch.Launcher -lib lib %1 %2 %3 %4 %5
Deleted: trunk/Hibernate3/build.sh
===================================================================
--- trunk/Hibernate3/build.sh 2007-05-24 19:06:06 UTC (rev 11562)
+++ trunk/Hibernate3/build.sh 2007-05-25 20:19:29 UTC (rev 11563)
@@ -1,2 +0,0 @@
-#!/bin/sh
-java -cp "lib/ant-launcher-1.6.5.jar" org.apache.tools.ant.launch.Launcher -lib lib "$@"
Deleted: trunk/Hibernate3/build.xml
===================================================================
--- trunk/Hibernate3/build.xml 2007-05-24 19:06:06 UTC (rev 11562)
+++ trunk/Hibernate3/build.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -1,782 +0,0 @@
-<!--
-
- Hibernate ANT build script.
-
- (Originally contributed by Fabricio Goncalves)
-
--->
-
-<project name="Hibernate3" default="jar" basedir=".">
-
- <!-- ################################################################## -->
- <!-- ################## Property definitions ########################## -->
- <!-- ################################################################## -->
-
- <!-- Give user a chance to override without editing this file or typing -D -->
- <property file="build.properties"/>
- <property file="${user.home}/.ant.properties"/>
-
- <!-- Name of project and version, used to create filenames -->
- <property name="Name" value="Hibernate"/>
- <property name="name" value="hibernate"/>
- <property name="name2" value="hibernate3"/>
- <property name="version.major" value="3"/>
- <property name="version.minor" value="2"/>
- <property name="version.micro" value="0"/>
- <property name="version.qualifier" value="cr3"/>
- <property name="version.full" value="${version.major}.${version.minor}.${version.micro}.${version.qualifier}"/>
- <property name="version.major_minor" value="${version.major}.${version.minor}"/>
- <property name="fullname" value="${name}-${version.full}"/>
-
- <!-- Various SCM-controlled directories -->
- <property name="dir.src" value="src" />
- <property name="dir.test" value="test" />
- <property name="dir.grammar" value="grammar" />
- <property name="dir.etc" value="etc" />
- <property name="dir.lib" value="lib" />
- <property name="dir.jdbc" value="jdbc" />
- <property name="dir.doc" value="doc"/>
- <property name="dir.doc.api" value="${dir.doc}/api"/>
- <property name="dir.doc.ref" value="${dir.doc}/reference"/>
- <property name="dir.eg" value="eg"/>
- <property name="dir.props" value="${dir.etc}" />
-
- <!-- Define the build directory layout -->
- <property name="dir.build" value="build" />
- <property name="dir.out.classes" value="${dir.build}/classes" />
- <property name="dir.out.test-classes" value="${dir.build}/testclasses" />
- <property name="dir.out.generated-source" value="${dir.build}/gensrc" />
- <property name="dir.out.antlr-package" value="${dir.out.generated-source}/org/hibernate/hql/antlr" />
- <property name="dir.out.junit" value="${dir.build}/testout" />
- <property name="dir.out.junit-reports" value="${dir.build}/test-reports" />
- <property name="dir.out.perf-test" value="${dir.build}/testout-perf" />
- <property name="dir.out.clover" value="${dir.build}/cloverout" />
- <property name="dir.out.dist" value="${dir.build}/cloverout" />
- <property name="dir.out.db" value="${dir.build}/db" />
- <property name="dir.out.doc" value="${dir.build}/doc" />
- <property name="dir.out.eg" value="${dir.build}/eg" />
- <property name="dir.dist" value="${dir.build}/dist" />
-
- <!-- Define various operational options -->
- <property name="javadoc.url.jse" value="http://java.sun.com/j2se/1.3/docs/api"/>
- <property name="javac.debug" value="on"/>
- <property name="javac.optimize" value="off"/>
- <property name="javac.target" value="1.4"/>
- <property name="javac.source" value="1.4"/>
- <property name="jar.driver" value="${dir.jdbc}/hsqldb.jar"/>
- <property name="jar.clover" value="${ant.home}/lib/clover.jar"/>
-
- <!-- JAR and dist file names -->
- <property name="jar.name" value="${name2}"/>
-
- <property name="hibernate.test.validatefailureexpected" value="false"/>
-
- <!-- fileset definition for the lib directory -->
- <fileset id="fs.lib" dir="${dir.lib}">
- <include name="**/*.jar" />
- </fileset>
- <path id="path.lib">
- <fileset refid="fs.lib" />
- </path>
-
- <!-- path definition for the JDBC driver(s) -->
- <path id="path.jdbc">
- <!-- By default include everything in the ${dir.jdbc} directory -->
- <fileset dir="${dir.jdbc}">
- <include name="**/*.jar" />
- <include name="**/*.zip" />
- </fileset>
- <!-- And the explicitly defined jar.driver property (used by the CC scripts) -->
- <pathelement path="${jar.driver}"/>
- </path>
- <!-- allows external definition of the JDBC classpath fragment -->
- <property name="hibernate.test.jdbc.fs.importFile" value="doNotImport" />
- <import file="${hibernate.test.jdbc.fs.importFile}" optional="true" />
-
- <!-- patternset for insturmentation-eligible resources -->
- <patternset id="ps.instrument.domain">
- <include name="org/hibernate/test/instrument/domain/*" />
- <exclude name="**/*.hbm.xml" />
- </patternset>
-
-
- <!-- ################################################################## -->
- <!-- ############################# Tasks ############################## -->
- <!-- ################################################################## -->
-
- <taskdef name="splash" classname="org.apache.tools.ant.taskdefs.optional.splash.SplashTask">
- <classpath refid="path.lib"/>
- </taskdef>
-
- <taskdef name="junit" classname="org.apache.tools.ant.taskdefs.optional.junit.JUnitTask">
- <classpath refid="path.lib"/>
- </taskdef>
-
- <taskdef name="junitreport" classname="org.apache.tools.ant.taskdefs.optional.junit.XMLResultAggregator">
- <classpath refid="path.lib"/>
- </taskdef>
-
- <taskdef resource="clovertasks"/>
-
-
- <!-- ################################################################## -->
- <!-- ############################ Targets ############################# -->
- <!-- ################################################################## -->
-
- <!-- Various 'preparation targets -->
-
- <target name="splash" unless="nosplash" description="Display the logo">
- <property name="splash.dir" location="."/>
- <splash imageurl="file:${splash.dir}/hibernate_logo.gif" showduration="0"/>
- </target>
-
- <target name="init" depends="splash" description="Initialize the build">
- <tstamp>
- <format property="now" pattern="yyyy-MM-dd hh:mm:ss"/>
- </tstamp>
- <echo message="Build ${Name}-${version.full} (${now})"/>
- <echo message="JDK version: ${ant.java.version}"/>
- </target>
-
-
- <!-- Various 'clean' targets -->
-
- <target name="clean" depends="init" description="Cleans up build directories">
- <delete dir="${dir.build}"/>
- </target>
-
- <target name="cleantestdb" depends="init" description="Clean up the test file-based database directories">
- <delete dir="${dir.out.db}" />
- </target>
-
-
- <!-- The ANTLR related targets -->
-
- <target name="init.antlr" depends="init" description="Check ANTLR dependencies.">
- <uptodate property="antlr.isUpToDate" targetfile="${dir.out.antlr-package}/.antlr_run">
- <srcfiles dir="${dir.grammar}" includes="*.g"/>
- </uptodate>
- </target>
-
- <target name="antlr" depends="init.antlr" unless="antlr.isUpToDate" description="Generate ANTLR parsers.">
- <taskdef name="antlrtask" classname="org.apache.tools.ant.taskdefs.optional.ANTLR">
- <classpath>
- <fileset dir="${dir.lib}">
- <include name="ant-antlr-*.jar"/>
- <include name="antlr-*.jar"/>
- </fileset>
- </classpath>
- </taskdef>
- <mkdir dir="${dir.out.antlr-package}" />
- <antlrtask target="${dir.grammar}/hql.g" outputdirectory="${dir.out.antlr-package}" />
- <antlrtask target="${dir.grammar}/hql-sql.g" outputdirectory="${dir.out.antlr-package}" />
- <antlrtask target="${dir.grammar}/sql-gen.g" outputdirectory="${dir.out.antlr-package}" />
- <touch file="${dir.out.antlr-package}/.antlr_run"/>
- </target>
-
- <target name="cleanantlr" depends="init" description="Clean up the generated ANTLR parsers.">
- <delete dir="${dir.out.antlr-package}"/>
- </target>
-
- <target name="antlr.regen" depends="init,cleanantlr,antlr" description="Regenerate all ANTLR generated code." />
-
- <target name="antlr.bnf" depends="init" description="Generate BNF diagram for HQL">
- <mkdir dir="${dir.out.doc}/other"/>
- <property name="bnf.grammar" location="${dir.grammar}/hql.g"/>
-
- <java dir="${dir.out.doc}/other" classname="net.mark_malakanov.sdg2.Main" classpathref="path.lib" fork="true">
- <jvmarg line="-Xmx512M"/>
- <arg line="-blind"/>
- <arg line="-savehtml hql-bnf.html"/>
- <arg line="-savehtmltitle Hibernate ${version.full} HQL"/>
- <arg line="${bnf.grammar}"/>
- </java>
- </target>
-
-
- <!-- Compilation targets -->
-
- <target name="compile" depends="init,antlr" description="Compile the Java source code">
- <mkdir dir="${dir.out.classes}"/>
- <available classname="org.eclipse.core.launcher.Main" property="build.compiler"
- value="org.eclipse.jdt.core.JDTCompilerAdapter" classpath="${java.class.path}"/>
- <javac destdir="${dir.out.classes}"
- classpathref="path.lib"
- debug="${javac.debug}"
- optimize="${javac.optimize}"
- target="${javac.target}"
- source="${javac.source}"
- nowarn="on">
- <src path="${dir.src}"/>
- <src path="${dir.out.generated-source}"/>
- </javac>
- <copy todir="${dir.out.classes}">
- <fileset dir="${dir.src}">
- <include name="**/*.dtd" />
- <include name="**/*.xml" />
- <include name="**/*.xslt" />
- </fileset>
- </copy>
- </target>
-
- <target name="compiletest" depends="compile" description="Compile the tests">
- <mkdir dir="${dir.out.test-classes}"/>
- <available classname="org.eclipse.core.launcher.Main" property="build.compiler"
- value="org.eclipse.jdt.core.JDTCompilerAdapter" classpath="${java.class.path}"/>
- <javac destdir="${dir.out.test-classes}"
- debug="${javac.debug}"
- optimize="${javac.optimize}"
- target="${javac.target}"
- source="${javac.source}"
- nowarn="on">
- <classpath>
- <path refid="path.lib" />
- <pathelement path="${dir.out.classes}" />
- </classpath>
- <src path="${dir.test}" />
- </javac>
- <copy todir="${dir.out.test-classes}">
- <fileset dir="${dir.test}">
- <include name="**/*.xml" />
- <include name="**/*.properties" />
- </fileset>
- </copy>
- </target>
-
-
- <!-- testsuite targets -->
-
- <target name="junitreport" depends="splash,junit" description="Run tests and create JUnit report (requires driver.jar property)">
- <delete dir="${dir.out.junit-reports}" />
- <mkdir dir="${dir.out.junit-reports}" />
- <junitreport todir="${dir.out.junit-reports}">
- <fileset dir="${dir.out.junit}">
- <include name="TEST-*.xml"/>
- </fileset>
- <report format="frames" todir="${dir.out.junit-reports}"/>
- </junitreport>
- </target>
-
- <target name="junit" depends="junit.prepare">
- <sequential>
- <antcall target="junit.standard" />
- <antcall target="junit.instrument.cglib" />
- <antcall target="junit.instrument.javassist" />
- </sequential>
- </target>
-
- <target name="junit.prepare" depends="cleantestdb,compiletest">
- <delete dir="${dir.out.junit}"/>
- <mkdir dir="${dir.out.junit}"/>
- </target>
-
- <target name="junit.standard" depends="junit.prepare" description="Run the test suite (requires driver.jar property)">
- <path id="selector.classpath">
- <!--
- the selector needs all of this because it actually gets the suite
- from AllTests and then performs the filtering based on that
- -->
- <fileset refid="fs.lib" />
- <path refid="path.jdbc" />
- <pathelement path="${dir.out.classes}"/>
- <pathelement path="${dir.out.test-classes}"/>
- <pathelement path="${dir.props}"/>
- </path>
-
- <junit printsummary="yes" dir="${basedir}" maxmemory="256M" fork="yes" forkmode="perBatch">
- <jvmarg value="-Dhibernate.test.validatefailureexpected=${hibernate.test.validatefailureexpected}"/>
- <classpath>
- <fileset refid="fs.lib" />
- <path refid="path.jdbc" />
- <pathelement path="${dir.props}"/>
- <pathelement path="${dir.out.classes}"/>
- <pathelement path="${dir.out.test-classes}"/>
- <pathelement path="${jar.clover}"/>
- </classpath>
- <formatter type="plain"/>
- <formatter type="xml"/>
- <batchtest todir="${dir.out.junit}" haltonfailure="no">
- <fileset dir="${dir.out.test-classes}">
- <include name="org/hibernate/test/**/*Test.class" />
- <!-- exclude the build-time instrumentation tasks as we will be running them manually shortly -->
- <exclude name="org/hibernate/test/instrument/buildtime/*" />
- <!--custom selector to make sure only those tests in the AllTests suite get picked up here -->
- <custom classname="org.hibernate.test.TestSelector" classpathref="selector.classpath" />
- </fileset>
- </batchtest>
- </junit>
- </target>
-
- <target name="junit.instrument" depends="junit.prepare,junit.instrument.cglib,junit.instrument.javassist" />
-
- <target name="junit.instrument.cglib" depends="instrument.cglib">
- <mkdir dir="${dir.out.junit}"/>
- <junit printsummary="yes" maxmemory="256M" fork="yes">
- <jvmarg value="-Dhibernate.test.validatefailureexpected=${hibernate.test.validatefailureexpected}"/>
- <classpath>
- <fileset refid="fs.lib" />
- <path refid="path.jdbc" />
- <pathelement path="${dir.out.classes}"/>
- <pathelement path="${dir.out.test-classes}"/>
- <pathelement path="${dir.props}"/>
- <pathelement path="${jar.clover}"/>
- </classpath>
- <formatter type="plain"/>
- <formatter type="xml"/>
- <test name="org.hibernate.test.instrument.buildtime.InstrumentTest"
- outfile="TEST-CGLIB-org.hibernate.test.instrument.buildtime.InstrumentTest"
- todir="${dir.out.junit}"
- haltonerror="false"
- haltonfailure="false" />
- </junit>
- </target>
-
- <target name="junit.instrument.javassist" depends="instrument.javassist">
- <mkdir dir="${dir.out.junit}"/>
- <junit printsummary="yes" maxmemory="256M" fork="yes">
- <jvmarg value="-Dhibernate.test.validatefailureexpected=${hibernate.test.validatefailureexpected}"/>
- <classpath>
- <fileset refid="fs.lib" />
- <path refid="path.jdbc" />
- <pathelement path="${dir.out.classes}"/>
- <pathelement path="${dir.out.test-classes}"/>
- <pathelement path="${dir.props}"/>
- <pathelement path="${jar.clover}"/>
- </classpath>
- <formatter type="plain"/>
- <formatter type="xml"/>
- <test name="org.hibernate.test.instrument.buildtime.InstrumentTest"
- outfile="TEST-JAVASSIST-org.hibernate.test.instrument.buildtime.InstrumentTest"
- todir="${dir.out.junit}"
- haltonerror="false"
- haltonfailure="false" />
- </junit>
- </target>
-
- <!-- Run a single unit test. -->
- <target name="junitsingle" depends="cleantestdb,compiletest" description="Run a single test suite (requires testname and jar.driver properties)">
- <delete dir="${dir.out.junit}"/>
- <mkdir dir="${dir.out.junit}"/>
- <junit printsummary="yes" fork="yes" haltonfailure="yes" dir="${basedir}">
- <classpath>
- <fileset refid="fs.lib" />
- <path refid="path.jdbc" />
- <pathelement path="${dir.out.classes}"/>
- <pathelement path="${dir.out.test-classes}"/>
- <pathelement path="${dir.props}"/>
- <pathelement path="${jar.clover}"/>
- </classpath>
- <!-- support for JDWP debug
- <jvmarg value="-Xdebug"/>
- <jvmarg value="-Xnoagent"/>
- <jvmarg value="-Djava.compiler=NONE"/>
- <jvmarg value="-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=localhost:5005"/>
- -->
- <formatter type="plain"/>
- <formatter type="xml"/>
- <test fork="yes" todir="${dir.out.junit}" haltonfailure="no" name="${testname}"/>
- </junit>
- </target>
-
- <target name="perf" depends="cleantestdb,compiletest" description="Run the performance tests">
- <delete dir="${dir.out.perf-test}"/>
- <mkdir dir="${dir.out.perf-test}"/>
- <junit printsummary="yes" maxmemory="256M" fork="yes" haltonfailure="yes" dir="${basedir}">
- <jvmarg value="-Dhibernate.test.validatefailureexpected=${hibernate.test.validatefailureexpected}"/>
- <classpath>
- <fileset refid="fs.lib" />
- <path refid="path.jdbc" />
- <pathelement path="${dir.out.classes}"/>
- <pathelement path="${dir.out.test-classes}"/>
- <pathelement path="${dir.props}"/>
- <pathelement path="${jar.clover}"/>
- </classpath>
- <formatter type="plain"/>
- <test name="org.hibernate.test.NewPerformanceTest" todir="${dir.out.perf-test}" haltonfailure="no"/>
- <test name="org.hibernate.test.NewerPerformanceTest" todir="${dir.out.perf-test}" haltonfailure="no"/>
- <test name="org.hibernate.test.PerformanceTest" todir="${dir.out.perf-test}" haltonfailure="no"/>
- </junit>
- </target>
-
- <target name="eg" description="Compile and run the simple example">
- <!-- works only in distribution, not in CVS tree; see 'eg.inline' target -->
- <mkdir dir="${dir.out.eg}" />
-
- <available classname="org.eclipse.core.launcher.Main"
- property="build.compiler"
- value="org.eclipse.jdt.core.JDTCompilerAdapter"
- classpath="${java.class.path}"/>
- <javac srcdir="${dir.eg}"
- destdir="${dir.out.eg}"
- debug="${javac.debug}"
- optimize="${javac.optimize}"
- target="${javac.target}"
- source="${javac.source}"
- nowarn="on">
- <classpath>
- <pathelement path="${dir.build}/${jar.name}.jar"/>
- <fileset refid="fs.lib" />
- </classpath>
- </javac>
-
- <copy todir="${dir.out.eg}">
- <fileset dir="${dir.eg}">
- <include name="**/*.xml" />
- </fileset>
- </copy>
-
- <echo message="remember to place your JDBC driver in the lib directory"/>
- <java classname="org.hibernate.auction.Main" fork="true" failonerror="true">
- <classpath>
- <fileset refid="fs.lib" />
- <pathelement path="${dir.out.eg}" />
- <pathelement path="${dir.build}/${jar.name}.jar"/>
- <pathelement path="${dir.etc}" />
- </classpath>
- </java>
-
- <echo message="Please follow the Getting Started road map: http://www.hibernate.org/152.html"/>
- </target>
-
- <target name="eg.inline" depends="jar" description="Compile and run the simple example utilizing the build structure">
- <mkdir dir="${dir.out.eg}" />
-
- <available classname="org.eclipse.core.launcher.Main"
- property="build.compiler"
- value="org.eclipse.jdt.core.JDTCompilerAdapter"
- classpath="${java.class.path}"/>
- <javac srcdir="${dir.eg}"
- destdir="${dir.out.eg}"
- debug="${javac.debug}"
- optimize="${javac.optimize}"
- target="${javac.target}"
- source="${javac.source}"
- nowarn="on">
- <classpath>
- <pathelement path="${dir.build}/${jar.name}.jar"/>
- <fileset refid="fs.lib" />
- </classpath>
- </javac>
-
- <copy todir="${dir.out.eg}">
- <fileset dir="${dir.eg}">
- <include name="**/*.xml" />
- </fileset>
- </copy>
-
- <java classname="org.hibernate.auction.Main" fork="true" failonerror="true">
- <classpath>
- <fileset refid="fs.lib" />
- <path refid="path.jdbc" />
- <pathelement path="${dir.out.eg}" />
- <pathelement path="${dir.build}/${jar.name}.jar"/>
- <pathelement path="${dir.props}" />
- </classpath>
- </java>
- </target>
-
-
- <!-- Instrumentation tasks -->
-
- <target name="instrument.prepare">
- <delete>
- <fileset dir="${dir.out.test-classes}">
- <patternset refid="ps.instrument.domain" />
- </fileset>
- </delete>
- </target>
-
- <target name="instrument.cglib" depends="instrument.prepare,compiletest" description="Performs cglib-based instrumentation">
- <taskdef name="instrument" classname="org.hibernate.tool.instrument.cglib.InstrumentTask">
- <classpath path="${dir.out.classes}"/>
- <classpath refid="path.lib"/>
- </taskdef>
-
- <available classname="org.eclipse.core.launcher.Main" property="build.compiler"
- value="org.eclipse.jdt.core.JDTCompilerAdapter" classpath="${java.class.path}"/>
- <javac destdir="${dir.out.test-classes}"
- debug="${javac.debug}"
- optimize="${javac.optimize}"
- target="${javac.target}"
- source="${javac.source}"
- nowarn="on">
- <classpath>
- <path refid="path.lib" />
- <pathelement path="${dir.out.classes}" />
- </classpath>
- <src path="${dir.test}" />
- <patternset refid="ps.instrument.domain" />
- </javac>
-
- <instrument verbose="true">
- <fileset dir="${dir.out.test-classes}">
- <patternset refid="ps.instrument.domain" />
- </fileset>
- </instrument>
- </target>
-
- <target name="instrument.javassist" depends="instrument.prepare,compiletest" description="Performs javassist-based instrumentation">
- <taskdef name="instrument" classname="org.hibernate.tool.instrument.javassist.InstrumentTask">
- <classpath path="${dir.out.classes}"/>
- <classpath refid="path.lib"/>
- </taskdef>
-
- <available classname="org.eclipse.core.launcher.Main" property="build.compiler"
- value="org.eclipse.jdt.core.JDTCompilerAdapter" classpath="${java.class.path}"/>
- <javac destdir="${dir.out.test-classes}"
- debug="${javac.debug}"
- optimize="${javac.optimize}"
- target="${javac.target}"
- source="${javac.source}"
- nowarn="on">
- <classpath>
- <path refid="path.lib" />
- <pathelement path="${dir.out.classes}" />
- </classpath>
- <src path="${dir.test}" />
- <patternset refid="ps.instrument.domain" />
- </javac>
-
- <instrument verbose="true">
- <fileset dir="${dir.out.test-classes}">
- <patternset refid="ps.instrument.domain" />
- </fileset>
- </instrument>
- </target>
-
-
- <!-- Distribution related targets -->
-
- <target name="versioncheck" description="Check version.properties up against lib directory">
- <!-- If versioncheck fails two things can be "out-of-sync":
-
- 1) '<libraryname> not found in fileset of libraries!' means that version.properties
- defines a library, but this library is not in the lib directory. Resolution: add
- the library to the lib directory or remove the definiton from version.properties
-
- 2) '<filename> not listed in version.properties' means a file was found in the lib
- directory, but not in version.properties; Resolution: add the library to the
- version.properties or remove the library from the libraries.
- -->
- <taskdef classname="versioncheck.VersionCheckTask" classpath="${dir.lib}/versioncheck.jar" name="versioncheck"/>
- <mkdir dir="${dir.build}/versioncheck" />
- <versioncheck versionfile="${dir.lib}/version.properties" output="${dir.build}/versioncheck/_README.txt">
- <fileset dir="${dir.lib}">
- <include name="**/*.zip" />
- <include name="**/*.jar" />
- </fileset>
- </versioncheck>
- </target>
-
- <target name="jar" depends="compile" description="Build the distribution .jar file">
- <mkdir dir="${dir.build}"/>
-
- <jar jarfile="${dir.build}/${jar.name}.jar" basedir="${dir.out.classes}">
- <include name="org/hibernate/**/*.class" />
- <include name="org/hibernate/*.dtd"/>
- <manifest>
- <attribute name="Implementation-Title" value="Hibernate3"/>
- <attribute name="Implementation-Version" value="${version.full}"/>
- <attribute name="Implementation-Vendor" value="hibernate.org"/>
- <attribute name="Hibernate-Version" value="${version.full}"/>
- </manifest>
- </jar>
-
- <!-- currently for testing *only* -->
- <jar jarfile="${dir.build}/hibernate3-client.jar">
- <zipfileset src="${dir.build}/${jar.name}.jar">
- <include name="org/hibernate/AssertionFailure.class"/>
- <include name="org/hibernate/**/*Exception.class"/>
- <include name="org/hibernate/collection/**"/>
- <include name="org/hibernate/exception/Nestable*.class"/>
- <include name="org/hibernate/exception/ExceptionUtils.class"/>
- <include name="org/hibernate/engine/SessionImplementor.class"/>
- <include name="org/hibernate/loader/CollectionAliases.class"/>
- <include name="org/hibernate/persister/collection/**"/>
- <include name="org/hibernate/pretty/MessageHelper.class"/>
- <include name="org/hibernate/type/Type.class"/>
- <include name="org/hibernate/EntityMode.class"/>
- <include name="org/hibernate/util/**"/>
- <include name="org/hibernate/proxy/**"/>
- </zipfileset>
- <manifest>
- <attribute name="Implementation-Title" value="Hibernate3 Client"/>
- <attribute name="Implementation-Version" value="${version.full}"/>
- <attribute name="Implementation-Vendor" value="hibernate.org"/>
- <attribute name="Hibernate-Version" value="${version.full}"/>
- </manifest>
- </jar>
- </target>
-
- <target name="dist" depends="init,versioncheck,jar,javadoc,refdoc,antlr.bnf"
- description="Build everything and package">
- <mkdir dir="${dir.dist}" />
-
- <!-- prepare build.xml -->
- <copy file="${basedir}/build.xml" todir="${dir.dist}"/>
- <replace file="${dir.dist}/build.xml">
- <replacetoken><![CDATA[../${name}-${version}]]></replacetoken>
- <replacevalue><![CDATA[../${name}]]></replacevalue>
- </replace>
- <replace file="${dir.dist}/build.xml">
- <replacetoken><![CDATA[name="dir.jdbc" value="jdbc"]]></replacetoken>
- <replacevalue><![CDATA[name="dir.jdbc" value="lib"]]></replacevalue>
- </replace>
-
- <zip zipfile="${dir.dist}/${fullname}.zip">
- <zipfileset prefix="${name}-${version.major_minor}/doc/reference" dir="${dir.doc.ref}/build" />
- <zipfileset prefix="${name}-${version.major_minor}/doc/tutorial" dir="${dir.doc.ref}/tutorial" >
- <include name="src/**" />
- <include name="lib/**" />
- <include name="build.xml" />
- <include name="runCleanDatabase.sh" />
- <include name="web.xml" />
- </zipfileset>
- <zipfileset prefix="${name}-${version.major_minor}/doc/other" dir="${dir.doc}/other" />
- <zipfileset prefix="${name}-${version.major_minor}/doc" dir="${dir.out.doc}" />
- <zipfileset prefix="${name}-${version.major_minor}/eg" dir="${dir.eg}"/>
- <zipfileset prefix="${name}-${version.major_minor}/etc" dir="${dir.etc}"/>
- <zipfileset prefix="${name}-${version.major_minor}/grammar" dir="${dir.grammar}"/>
- <zipfileset prefix="${name}-${version.major_minor}/lib" dir="${dir.lib}"/>
- <zipfileset prefix="${name}-${version.major_minor}/lib" dir="${dir.build}/versioncheck"/>
- <zipfileset prefix="${name}-${version.major_minor}/src" dir="${dir.src}"/>
- <zipfileset prefix="${name}-${version.major_minor}/test" dir="${dir.test}"/>
- <zipfileset prefix="${name}-${version.major_minor}" file="${basedir}/readme.txt" />
- <zipfileset prefix="${name}-${version.major_minor}" file="${basedir}/lgpl.txt" />
- <zipfileset prefix="${name}-${version.major_minor}" file="${basedir}/changelog.txt" />
- <zipfileset prefix="${name}-${version.major_minor}" file="${basedir}/build.bat" />
- <zipfileset prefix="${name}-${version.major_minor}" file="${basedir}/build.sh" />
- <zipfileset prefix="${name}-${version.major_minor}" file="${basedir}/hibernate_logo.gif" />
- <zipfileset prefix="${name}-${version.major_minor}" file="${dir.dist}/build.xml" />
- <zipfileset prefix="${name}-${version.major_minor}" file="${dir.build}/${jar.name}.jar" />
- </zip>
- <checksum file="${dir.dist}/${fullname}.zip" algorithm="MD5" fileext=".md5.txt"/>
-
- <tar tarfile="${dir.dist}/${fullname}.tar">
- <tarfileset prefix="${name}-${version.major_minor}/doc/reference" dir="${dir.doc.ref}/build" />
- <tarfileset prefix="${name}-${version.major_minor}/doc/tutorial" dir="${dir.doc.ref}/tutorial" >
- <include name="src/**" />
- <include name="lib/**" />
- <include name="build.xml" />
- <include name="runCleanDatabase.sh" />
- </tarfileset>
- <tarfileset prefix="${name}-${version.major_minor}/doc/other" dir="${dir.doc}/other" />
- <tarfileset prefix="${name}-${version.major_minor}/doc" dir="${dir.out.doc}" />
- <tarfileset prefix="${name}-${version.major_minor}/eg" dir="${dir.eg}"/>
- <tarfileset prefix="${name}-${version.major_minor}/etc" dir="${dir.etc}"/>
- <tarfileset prefix="${name}-${version.major_minor}/grammar" dir="${dir.grammar}"/>
- <tarfileset prefix="${name}-${version.major_minor}/lib" dir="${dir.lib}"/>
- <tarfileset prefix="${name}-${version.major_minor}/lib" dir="${dir.build}/versioncheck"/>
- <tarfileset prefix="${name}-${version.major_minor}/src" dir="${dir.src}"/>
- <tarfileset prefix="${name}-${version.major_minor}/test" dir="${dir.test}"/>
- <tarfileset prefix="${name}-${version.major_minor}" file="${basedir}/readme.txt" />
- <tarfileset prefix="${name}-${version.major_minor}" file="${basedir}/lgpl.txt" />
- <tarfileset prefix="${name}-${version.major_minor}" file="${basedir}/changelog.txt" />
- <tarfileset prefix="${name}-${version.major_minor}" file="${basedir}/build.bat" />
- <tarfileset prefix="${name}-${version.major_minor}" file="${basedir}/build.sh" />
- <tarfileset prefix="${name}-${version.major_minor}" file="${basedir}/hibernate_logo.gif" />
- <tarfileset prefix="${name}-${version.major_minor}" file="${dir.dist}/build.xml" />
- <tarfileset prefix="${name}-${version.major_minor}" file="${dir.build}/${jar.name}.jar" />
- </tar>
- <gzip src="${dir.dist}/${fullname}.tar" zipfile="${dir.dist}/${fullname}.tar.gz"/>
- <checksum file="${dir.dist}/${fullname}.tar.gz" algorithm="MD5" fileext=".md5.txt"/>
-
- <delete file="${dir.dist}/${fullname}.tar"/>
- <delete file="${dir.dist}/build.xml"/>
- </target>
-
- <target name="dist.complete" depends="init,versioncheck,jar,javadoc,refdoc,antlr.bnf"
- description="Build and package everything, and run test suite and eg example">
-
- <!-- perform a call to the junit target -->
- <antcall target="junitreport" />
-
- <!-- perform a call to the eg.inline target -->
- <antcall target="eg.inline" />
-
- <antcall target="dist" />
- </target>
-
-
- <!-- Documentation related targets -->
-
- <target name="javadoc" description="Compile the Javadoc API documentation to dist dir" depends="splash,antlr">
- <mkdir dir="${dir.out.doc}/api"/>
- <javadoc classpathref="path.lib"
- destdir="${dir.out.doc}/api"
- use="true"
- protected="true"
- version="true"
- doctitle="${Name} API Documentation (${version.full})"
- windowtitle="${Name} API Documentation"
- Overview="${dir.doc.api}/package.html"
- stylesheetfile="${dir.doc.api}/jdstyle.css"
- link="${javadoc.url.jse}">
- <packageset dir="${dir.out.generated-source}" defaultexcludes="yes">
- <include name="org/hibernate/**" />
- </packageset>
- <packageset dir="${dir.src}" defaultexcludes="yes">
- <include name="org/hibernate/**" />
- </packageset>
- <group title="Core API"
- packages="org.hibernate:org.hibernate.classic:org.hibernate.criterion:org.hibernate.metadata:org.hibernate.cfg:org.hibernate.usertype"/>
- <group title="Extension API"
- packages="org.hibernate.id:org.hibernate.connection:org.hibernate.transaction:org.hibernate.type:org.hibernate.dialect*:org.hibernate.cache*:org.hibernate.event*:org.hibernate.action:org.hibernate.property:org.hibernate.loader*:org.hibernate.persister*:org.hibernate.proxy:org.hibernate.tuple:org.hibernate.transform:org.hibernate.collection:org.hibernate.jdbc"/>
- <group title="Miscellaneous API"
- packages="org.hibernate.stat:org.hibernate.tool.hbm2ddl:org.hibernate.jmx:org.hibernate.mapping:org.hibernate.tool.instrument"/>
- <group title="Internal Implementation"
- packages="org.hibernate.engine:org.hibernate.impl:org.hibernate.sql:org.hibernate.lob:org.hibernate.util:org.hibernate.exception:org.hibernate.hql:org.hibernate.hql.ast:org.hibernate.hql.antlr:org.hibernate.hql.classic:org.hibernate.intercept:org.hibernate.secure:org.hibernate.pretty"/>
- </javadoc>
- <copy file="${dir.doc.api}/package.html" todir="${dir.out.doc}/api"/>
- </target>
-
- <target name="refdoc" depends="splash" description="Generate and copy reference documentation">
- <ant dir="${dir.doc.ref}" inheritall="false" target="all.doc"/>
- </target>
-
-
- <!-- miscellaneous targets -->
-
- <target name="checkstyle" description="Check coding style">
- <taskdef resource="checkstyletask.properties">
- <classpath refid="path.lib"/>
- </taskdef>
- <checkstyle config="checkstyle_checks.xml">
- <fileset dir="${src.dir}">
- <include name="**/*.java"/>
- </fileset>
- <formatter type="plain"/>
- </checkstyle>
- </target>
-
- <target name="with.clover">
- <clover-setup initString="clover_coverage.db"/>
- </target>
-
- <target name="cloverreport.html" depends="with.clover" description="Generate a clover report from the current clover database.">
- <clover-report>
- <current outfile="${clover.out.dir}">
- <format type="html"/>
- </current>
- </clover-report>
- </target>
-
- <target name="cloverreport"
- depends="with.clover,junitreport,cloverreport.html"
- description="Run the tests and generate a clover report" />
-
- <target name="prepare-dir-up" depends="jar">
- <!-- for backwards compatibility with where the tools expect to find the jar and libs -->
- <property name="targetdir" value="../${name}-${version.major_minor}"/>
- <mkdir dir="${targetdir}" />
- <mkdir dir="${targetdir}/lib" />
-
- <copy todir="${targetdir}" file="${dir.build}/${jar.name}.jar" />
- <copy todir="${targetdir}/lib">
- <fileset dir="${dir.lib}" />
- </copy>
-
- <copy todir="${targetdir}/doc/api" file="${dir.doc.api}/jdstyle.css"/>
- </target>
-
-</project>
Added: trunk/Hibernate3/cache-ehcache/pom.xml
===================================================================
--- trunk/Hibernate3/cache-ehcache/pom.xml (rev 0)
+++ trunk/Hibernate3/cache-ehcache/pom.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,32 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-core-project</artifactId>
+ <version>3.3.0.beta1</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-ehcache</artifactId>
+ <packaging>jar</packaging>
+ <name>Hibernate Ehcache integration</name>
+ <description>Integration of Hibernate with Ehcache</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>net.sf.ehcache</groupId>
+ <artifactId>ehcache</artifactId>
+ <version>1.2.3</version>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
Added: trunk/Hibernate3/cache-ehcache/src/main/java/org/hibernate/cache/EhCache.java
===================================================================
--- trunk/Hibernate3/cache-ehcache/src/main/java/org/hibernate/cache/EhCache.java (rev 0)
+++ trunk/Hibernate3/cache-ehcache/src/main/java/org/hibernate/cache/EhCache.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,275 @@
+//$Id: EhCache.java 10717 2006-11-03 19:05:21Z max.andersen at jboss.com $
+/**
+ * Copyright 2003-2006 Greg Luck, Jboss Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.hibernate.cache;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import net.sf.ehcache.CacheManager;
+import net.sf.ehcache.Element;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * EHCache plugin for Hibernate
+ * <p/>
+ * EHCache uses a {@link net.sf.ehcache.store.MemoryStore} and a
+ * {@link net.sf.ehcache.store.DiskStore}.
+ * The {@link net.sf.ehcache.store.DiskStore} requires that both keys and values be {@link java.io.Serializable}.
+ * However the MemoryStore does not and in ehcache-1.2 nonSerializable Objects are permitted. They are discarded
+ * if an attempt it made to overflow them to Disk or to replicate them to remote cache peers.
+ *
+ * @author Greg Luck
+ * @author Emmanuel Bernard
+ */
+public class EhCache implements Cache {
+ private static final Log log = LogFactory.getLog( EhCache.class );
+
+ private static final int SIXTY_THOUSAND_MS = 60000;
+
+ private net.sf.ehcache.Cache cache;
+
+ /**
+ * Creates a new Hibernate pluggable cache based on a cache name.
+ * <p/>
+ *
+ * @param cache The underlying EhCache instance to use.
+ */
+ public EhCache(net.sf.ehcache.Cache cache) {
+ this.cache = cache;
+ }
+
+ /**
+ * Gets a value of an element which matches the given key.
+ *
+ * @param key the key of the element to return.
+ * @return The value placed into the cache with an earlier put, or null if not found or expired
+ * @throws CacheException
+ */
+ public Object get(Object key) throws CacheException {
+ try {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "key: " + key );
+ }
+ if ( key == null ) {
+ return null;
+ }
+ else {
+ Element element = cache.get( key );
+ if ( element == null ) {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "Element for " + key + " is null" );
+ }
+ return null;
+ }
+ else {
+ return element.getObjectValue();
+ }
+ }
+ }
+ catch (net.sf.ehcache.CacheException e) {
+ throw new CacheException( e );
+ }
+ }
+
+ public Object read(Object key) throws CacheException {
+ return get( key );
+ }
+
+
+ /**
+ * Puts an object into the cache.
+ *
+ * @param key a key
+ * @param value a value
+ * @throws CacheException if the {@link CacheManager}
+ * is shutdown or another {@link Exception} occurs.
+ */
+ public void update(Object key, Object value) throws CacheException {
+ put( key, value );
+ }
+
+ /**
+ * Puts an object into the cache.
+ *
+ * @param key a key
+ * @param value a value
+ * @throws CacheException if the {@link CacheManager}
+ * is shutdown or another {@link Exception} occurs.
+ */
+ public void put(Object key, Object value) throws CacheException {
+ try {
+ Element element = new Element( key, value );
+ cache.put( element );
+ }
+ catch (IllegalArgumentException e) {
+ throw new CacheException( e );
+ }
+ catch (IllegalStateException e) {
+ throw new CacheException( e );
+ }
+ catch (net.sf.ehcache.CacheException e) {
+ throw new CacheException( e );
+ }
+
+ }
+
+ /**
+ * Removes the element which matches the key.
+ * <p/>
+ * If no element matches, nothing is removed and no Exception is thrown.
+ *
+ * @param key the key of the element to remove
+ * @throws CacheException
+ */
+ public void remove(Object key) throws CacheException {
+ try {
+ cache.remove( key );
+ }
+ catch (ClassCastException e) {
+ throw new CacheException( e );
+ }
+ catch (IllegalStateException e) {
+ throw new CacheException( e );
+ }
+ catch (net.sf.ehcache.CacheException e) {
+ throw new CacheException( e );
+ }
+ }
+
+ /**
+ * Remove all elements in the cache, but leave the cache
+ * in a useable state.
+ *
+ * @throws CacheException
+ */
+ public void clear() throws CacheException {
+ try {
+ cache.removeAll();
+ }
+ catch (IllegalStateException e) {
+ throw new CacheException( e );
+ }
+ catch (net.sf.ehcache.CacheException e) {
+ throw new CacheException( e );
+ }
+ }
+
+ /**
+ * Remove the cache and make it unuseable.
+ *
+ * @throws CacheException
+ */
+ public void destroy() throws CacheException {
+ try {
+ cache.getCacheManager().removeCache( cache.getName() );
+ }
+ catch (IllegalStateException e) {
+ throw new CacheException( e );
+ }
+ catch (net.sf.ehcache.CacheException e) {
+ throw new CacheException( e );
+ }
+ }
+
+ /**
+ * Calls to this method should perform there own synchronization.
+ * It is provided for distributed caches. Because EHCache is not distributed
+ * this method does nothing.
+ */
+ public void lock(Object key) throws CacheException {
+ }
+
+ /**
+ * Calls to this method should perform there own synchronization.
+ * It is provided for distributed caches. Because EHCache is not distributed
+ * this method does nothing.
+ */
+ public void unlock(Object key) throws CacheException {
+ }
+
+ /**
+ * Gets the next timestamp;
+ */
+ public long nextTimestamp() {
+ return Timestamper.next();
+ }
+
+ /**
+ * Returns the lock timeout for this cache.
+ */
+ public int getTimeout() {
+ // 60 second lock timeout
+ return Timestamper.ONE_MS * SIXTY_THOUSAND_MS;
+ }
+
+ public String getRegionName() {
+ return cache.getName();
+ }
+
+ /**
+ * Warning: This method can be very expensive to run. Allow approximately 1 second
+ * per 1MB of entries. Running this method could create liveness problems
+ * because the object lock is held for a long period
+ * <p/>
+ *
+ * @return the approximate size of memory ehcache is using for the MemoryStore for this cache
+ */
+ public long getSizeInMemory() {
+ try {
+ return cache.calculateInMemorySize();
+ }
+ catch (Throwable t) {
+ return -1;
+ }
+ }
+
+ public long getElementCountInMemory() {
+ try {
+ return cache.getMemoryStoreSize();
+ }
+ catch (net.sf.ehcache.CacheException ce) {
+ throw new CacheException( ce );
+ }
+ }
+
+ public long getElementCountOnDisk() {
+ return cache.getDiskStoreSize();
+ }
+
+ public Map toMap() {
+ try {
+ Map result = new HashMap();
+ Iterator iter = cache.getKeys().iterator();
+ while ( iter.hasNext() ) {
+ Object key = iter.next();
+ result.put( key, cache.get( key ).getObjectValue() );
+ }
+ return result;
+ }
+ catch (Exception e) {
+ throw new CacheException( e );
+ }
+ }
+
+ public String toString() {
+ return "EHCache(" + getRegionName() + ')';
+ }
+
+}
\ No newline at end of file
Added: trunk/Hibernate3/cache-ehcache/src/main/java/org/hibernate/cache/EhCacheProvider.java
===================================================================
--- trunk/Hibernate3/cache-ehcache/src/main/java/org/hibernate/cache/EhCacheProvider.java (rev 0)
+++ trunk/Hibernate3/cache-ehcache/src/main/java/org/hibernate/cache/EhCacheProvider.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,167 @@
+//$Id: EhCacheProvider.java 9964 2006-05-30 15:40:54Z epbernard $
+/**
+ * Copyright 2003-2006 Greg Luck, Jboss Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.hibernate.cache;
+
+import java.util.Properties;
+import java.net.URL;
+
+import net.sf.ehcache.CacheManager;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.cfg.Environment;
+import org.hibernate.util.StringHelper;
+import org.hibernate.util.ConfigHelper;
+
+/**
+ * Cache Provider plugin for Hibernate
+ *
+ * Use <code>hibernate.cache.provider_class=org.hibernate.cache.EhCacheProvider</code>
+ * in Hibernate 3.x or later
+ *
+ * Taken from EhCache 0.9 distribution
+ * @author Greg Luck
+ * @author Emmanuel Bernard
+ */
+/**
+ * Cache Provider plugin for ehcache-1.2. New in this provider are ehcache support for multiple
+ * Hibernate session factories, each with its own ehcache configuration, and non Serializable keys and values.
+ * Ehcache-1.2 also has many other features such as cluster support and listeners, which can be used seamlessly simply
+ * by configurion in ehcache.xml.
+ * <p/>
+ * Use <code>hibernate.cache.provider_class=org.hibernate.cache.EhCacheProvider</code> in the Hibernate configuration
+ * to enable this provider for Hibernate's second level cache.
+ * <p/>
+ * When configuring multiple ehcache CacheManagers, as you would where you have multiple Hibernate Configurations and
+ * multiple SessionFactories, specify in each Hibernate configuration the ehcache configuration using
+ * the property <code>hibernate.cache.provider_configuration_file_resource_path</code> An example to set an ehcache configuration
+ * called ehcache-2.xml would be <code>hibernate.cache.provider_configuration_file_resource_path=/ehcache-2.xml</code>. If the leading
+ * slash is not there one will be added. The configuration file will be looked for in the root of the classpath.
+ * <p/>
+ * Updated for ehcache-1.2. Note this provider requires ehcache-1.2.jar. Make sure ehcache-1.1.jar or earlier
+ * is not in the classpath or it will not work.
+ * <p/>
+ * See http://ehcache.sf.net for documentation on ehcache
+ * <p/>
+ *
+ * @author Greg Luck
+ * @author Emmanuel Bernard
+ */
+public class EhCacheProvider implements CacheProvider {
+
+ private static final Log log = LogFactory.getLog(EhCacheProvider.class);
+
+ private CacheManager manager;
+
+ /**
+ * Builds a Cache.
+ * <p>
+ * Even though this method provides properties, they are not used.
+ * Properties for EHCache are specified in the ehcache.xml file.
+ * Configuration will be read from ehcache.xml for a cache declaration
+ * where the name attribute matches the name parameter in this builder.
+ *
+ * @param name the name of the cache. Must match a cache configured in ehcache.xml
+ * @param properties not used
+ * @return a newly built cache will be built and initialised
+ * @throws CacheException inter alia, if a cache of the same name already exists
+ */
+ public Cache buildCache(String name, Properties properties) throws CacheException {
+ try {
+ net.sf.ehcache.Cache cache = manager.getCache(name);
+ if (cache == null) {
+ log.warn("Could not find configuration [" + name + "]; using defaults.");
+ manager.addCache(name);
+ cache = manager.getCache(name);
+ log.debug("started EHCache region: " + name);
+ }
+ return new EhCache(cache);
+ }
+ catch (net.sf.ehcache.CacheException e) {
+ throw new CacheException(e);
+ }
+ }
+
+ /**
+ * Returns the next timestamp.
+ */
+ public long nextTimestamp() {
+ return Timestamper.next();
+ }
+
+ /**
+ * Callback to perform any necessary initialization of the underlying cache implementation
+ * during SessionFactory construction.
+ *
+ * @param properties current configuration settings.
+ */
+ public void start(Properties properties) throws CacheException {
+ if (manager != null) {
+ log.warn("Attempt to restart an already started EhCacheProvider. Use sessionFactory.close() " +
+ " between repeated calls to buildSessionFactory. Using previously created EhCacheProvider." +
+ " If this behaviour is required, consider using net.sf.ehcache.hibernate.SingletonEhCacheProvider.");
+ return;
+ }
+ try {
+ String configurationResourceName = null;
+ if (properties != null) {
+ configurationResourceName = (String) properties.get( Environment.CACHE_PROVIDER_CONFIG );
+ }
+ if ( StringHelper.isEmpty( configurationResourceName ) ) {
+ manager = new CacheManager();
+ } else {
+ URL url = loadResource(configurationResourceName);
+ manager = new CacheManager(url);
+ }
+ } catch (net.sf.ehcache.CacheException e) {
+ //yukky! Don't you have subclasses for that!
+ //TODO race conditions can happen here
+ if (e.getMessage().startsWith("Cannot parseConfiguration CacheManager. Attempt to create a new instance of " +
+ "CacheManager using the diskStorePath")) {
+ throw new CacheException("Attempt to restart an already started EhCacheProvider. Use sessionFactory.close() " +
+ " between repeated calls to buildSessionFactory. Consider using net.sf.ehcache.hibernate.SingletonEhCacheProvider."
+ , e );
+ } else {
+ throw e;
+ }
+ }
+ }
+
+ private URL loadResource(String configurationResourceName) {
+ URL url = ConfigHelper.locateConfig( configurationResourceName );
+ if (log.isDebugEnabled()) {
+ log.debug("Creating EhCacheProvider from a specified resource: "
+ + configurationResourceName + " Resolved to URL: " + url);
+ }
+ return url;
+ }
+
+ /**
+ * Callback to perform any necessary cleanup of the underlying cache implementation
+ * during SessionFactory.close().
+ */
+ public void stop() {
+ if (manager != null) {
+ manager.shutdown();
+ manager = null;
+ }
+ }
+
+ public boolean isMinimalPutsEnabledByDefault() {
+ return false;
+ }
+
+}
Added: trunk/Hibernate3/cache-jbosscache/pom.xml
===================================================================
--- trunk/Hibernate3/cache-jbosscache/pom.xml (rev 0)
+++ trunk/Hibernate3/cache-jbosscache/pom.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,63 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-core-project</artifactId>
+ <version>3.3.0.beta1</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-jbosscache</artifactId>
+ <packaging>jar</packaging>
+ <name>Hibernate JBossCache integration</name>
+ <description>Integration of Hibernate with JBossCache (based on JBossCache1.x APIs)</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>${groupId}</groupId>
+ <artifactId>hibernate-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>jboss</groupId>
+ <artifactId>jboss-cache</artifactId>
+ <version>1.4.1.GA</version>
+ </dependency>
+ <!-- jboss-cache (the one from the jboss repo, anyway) does not properly define its dependencies -->
+ <dependency>
+ <groupId>jboss</groupId>
+ <artifactId>jboss-system</artifactId>
+ <version>4.0.2</version>
+ </dependency>
+ <dependency>
+ <groupId>jboss</groupId>
+ <artifactId>jboss-common</artifactId>
+ <version>4.0.2</version>
+ </dependency>
+ <dependency>
+ <groupId>jboss</groupId>
+ <artifactId>jboss-minimal</artifactId>
+ <version>4.0.2</version>
+ </dependency>
+ <dependency>
+ <groupId>jboss</groupId>
+ <artifactId>jboss-j2se</artifactId>
+ <version>200504122039</version>
+ </dependency>
+ <dependency>
+ <groupId>concurrent</groupId>
+ <artifactId>concurrent</artifactId>
+ <version>1.3.4</version>
+ </dependency>
+ <dependency>
+ <groupId>jgroups</groupId>
+ <artifactId>jgroups-all</artifactId>
+ <version>2.2.7</version>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
Added: trunk/Hibernate3/cache-jbosscache/src/main/java/org/hibernate/cache/JndiBoundTreeCacheProvider.java
===================================================================
--- trunk/Hibernate3/cache-jbosscache/src/main/java/org/hibernate/cache/JndiBoundTreeCacheProvider.java (rev 0)
+++ trunk/Hibernate3/cache-jbosscache/src/main/java/org/hibernate/cache/JndiBoundTreeCacheProvider.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,63 @@
+// $Id: JndiBoundTreeCacheProvider.java 6079 2005-03-16 06:01:18Z oneovthafew $
+package org.hibernate.cache;
+
+import java.util.Properties;
+
+import javax.transaction.TransactionManager;
+
+import org.hibernate.transaction.TransactionManagerLookup;
+import org.hibernate.transaction.TransactionManagerLookupFactory;
+
+/**
+ * Support for JBossCache (TreeCache), where the cache instance is available
+ * via JNDI lookup.
+ *
+ * @author Steve Ebersole
+ */
+public class JndiBoundTreeCacheProvider extends AbstractJndiBoundCacheProvider {
+
+ private TransactionManager transactionManager;
+
+ /**
+ * Construct a Cache representing the "region" within in the underlying cache
+ * provider.
+ *
+ * @param regionName the name of the cache region
+ * @param properties configuration settings
+ *
+ * @throws CacheException
+ */
+ public Cache buildCache(String regionName, Properties properties) throws CacheException {
+ return new TreeCache( getTreeCacheInstance(), regionName, transactionManager );
+ }
+
+ public void prepare(Properties properties) throws CacheException {
+ TransactionManagerLookup transactionManagerLookup = TransactionManagerLookupFactory.getTransactionManagerLookup(properties);
+ if (transactionManagerLookup!=null) {
+ transactionManager = transactionManagerLookup.getTransactionManager(properties);
+ }
+ }
+ /**
+ * Generate a timestamp
+ */
+ public long nextTimestamp() {
+ return System.currentTimeMillis() / 100;
+ }
+
+ /**
+ * By default, should minimal-puts mode be enabled when using this cache.
+ * <p/>
+ * Since TreeCache is a clusterable cache and we are only getting a
+ * reference the instance from JNDI, safest to assume a clustered
+ * setup and return true here.
+ *
+ * @return True.
+ */
+ public boolean isMinimalPutsEnabledByDefault() {
+ return true;
+ }
+
+ public org.jboss.cache.TreeCache getTreeCacheInstance() {
+ return ( org.jboss.cache.TreeCache ) super.getCache();
+ }
+}
Added: trunk/Hibernate3/cache-jbosscache/src/main/java/org/hibernate/cache/OptimisticTreeCache.java
===================================================================
--- trunk/Hibernate3/cache-jbosscache/src/main/java/org/hibernate/cache/OptimisticTreeCache.java (rev 0)
+++ trunk/Hibernate3/cache-jbosscache/src/main/java/org/hibernate/cache/OptimisticTreeCache.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,329 @@
+//$Id: OptimisticTreeCache.java 10118 2006-07-13 21:38:41Z steve.ebersole at jboss.com $
+package org.hibernate.cache;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.Comparator;
+
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.optimistic.DataVersion;
+import org.jboss.cache.config.Option;
+import org.jboss.cache.lock.TimeoutException;
+
+/**
+ * Represents a particular region within the given JBossCache TreeCache
+ * utilizing TreeCache's optimistic locking capabilities.
+ *
+ * @see OptimisticTreeCacheProvider for more details
+ *
+ * @author Steve Ebersole
+ */
+public class OptimisticTreeCache implements OptimisticCache {
+
+ // todo : eventually merge this with TreeCache and just add optional opt-lock support there.
+
+ private static final Log log = LogFactory.getLog( OptimisticTreeCache.class);
+
+ private static final String ITEM = "item";
+
+ private org.jboss.cache.TreeCache cache;
+ private final String regionName;
+ private final Fqn regionFqn;
+ private OptimisticCacheSource source;
+
+ public OptimisticTreeCache(org.jboss.cache.TreeCache cache, String regionName)
+ throws CacheException {
+ this.cache = cache;
+ this.regionName = regionName;
+ this.regionFqn = Fqn.fromString( regionName.replace( '.', '/' ) );
+ }
+
+
+ // OptimisticCache impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public void setSource(OptimisticCacheSource source) {
+ this.source = source;
+ }
+
+ public void writeInsert(Object key, Object value, Object currentVersion) {
+ writeUpdate( key, value, currentVersion, null );
+ }
+
+ public void writeUpdate(Object key, Object value, Object currentVersion, Object previousVersion) {
+ try {
+ Option option = new Option();
+ DataVersion dv = ( source != null && source.isVersioned() )
+ ? new DataVersionAdapter( currentVersion, previousVersion, source.getVersionComparator(), source.toString() )
+ : NonLockingDataVersion.INSTANCE;
+ option.setDataVersion( dv );
+ cache.put( new Fqn( regionFqn, key ), ITEM, value, option );
+ }
+ catch ( Exception e ) {
+ throw new CacheException( e );
+ }
+ }
+
+ public void writeLoad(Object key, Object value, Object currentVersion) {
+ try {
+ Option option = new Option();
+ option.setFailSilently( true );
+ option.setDataVersion( NonLockingDataVersion.INSTANCE );
+ cache.remove( new Fqn( regionFqn, key ), "ITEM", option );
+
+ option = new Option();
+ option.setFailSilently( true );
+ DataVersion dv = ( source != null && source.isVersioned() )
+ ? new DataVersionAdapter( currentVersion, currentVersion, source.getVersionComparator(), source.toString() )
+ : NonLockingDataVersion.INSTANCE;
+ option.setDataVersion( dv );
+ cache.put( new Fqn( regionFqn, key ), ITEM, value, option );
+ }
+ catch (Exception e) {
+ throw new CacheException(e);
+ }
+ }
+
+
+ // Cache impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public Object get(Object key) throws CacheException {
+ try {
+ Option option = new Option();
+ option.setFailSilently( true );
+// option.setDataVersion( NonLockingDataVersion.INSTANCE );
+ return cache.get( new Fqn( regionFqn, key ), ITEM, option );
+ }
+ catch (Exception e) {
+ throw new CacheException(e);
+ }
+ }
+
+ public Object read(Object key) throws CacheException {
+ try {
+ return cache.get( new Fqn( regionFqn, key ), ITEM );
+ }
+ catch (Exception e) {
+ throw new CacheException(e);
+ }
+ }
+
+ public void update(Object key, Object value) throws CacheException {
+ try {
+ Option option = new Option();
+ option.setDataVersion( NonLockingDataVersion.INSTANCE );
+ cache.put( new Fqn( regionFqn, key ), ITEM, value, option );
+ }
+ catch (Exception e) {
+ throw new CacheException(e);
+ }
+ }
+
+ public void put(Object key, Object value) throws CacheException {
+ try {
+ log.trace( "performing put() into region [" + regionName + "]" );
+ // do the put outside the scope of the JTA txn
+ Option option = new Option();
+ option.setFailSilently( true );
+ option.setDataVersion( NonLockingDataVersion.INSTANCE );
+ cache.put( new Fqn( regionFqn, key ), ITEM, value, option );
+ }
+ catch (TimeoutException te) {
+ //ignore!
+ log.debug("ignoring write lock acquisition failure");
+ }
+ catch (Exception e) {
+ throw new CacheException(e);
+ }
+ }
+
+ public void remove(Object key) throws CacheException {
+ try {
+ // tree cache in optimistic mode seems to have as very difficult
+ // time with remove calls on non-existent nodes (NPEs)...
+ if ( cache.get( new Fqn( regionFqn, key ), ITEM ) != null ) {
+ Option option = new Option();
+ option.setDataVersion( NonLockingDataVersion.INSTANCE );
+ cache.remove( new Fqn( regionFqn, key ), option );
+ }
+ else {
+ log.trace( "skipping remove() call as the underlying node did not seem to exist" );
+ }
+ }
+ catch (Exception e) {
+ throw new CacheException(e);
+ }
+ }
+
+ public void clear() throws CacheException {
+ try {
+ Option option = new Option();
+ option.setDataVersion( NonLockingDataVersion.INSTANCE );
+ cache.remove( regionFqn, option );
+ }
+ catch (Exception e) {
+ throw new CacheException(e);
+ }
+ }
+
+ public void destroy() throws CacheException {
+ try {
+ Option option = new Option();
+ option.setCacheModeLocal( true );
+ option.setFailSilently( true );
+ option.setDataVersion( NonLockingDataVersion.INSTANCE );
+ cache.remove( regionFqn, option );
+ }
+ catch( Exception e ) {
+ throw new CacheException( e );
+ }
+ }
+
+ public void lock(Object key) throws CacheException {
+ throw new UnsupportedOperationException( "TreeCache is a fully transactional cache" + regionName );
+ }
+
+ public void unlock(Object key) throws CacheException {
+ throw new UnsupportedOperationException( "TreeCache is a fully transactional cache: " + regionName );
+ }
+
+ public long nextTimestamp() {
+ return System.currentTimeMillis() / 100;
+ }
+
+ public int getTimeout() {
+ return 600; //60 seconds
+ }
+
+ public String getRegionName() {
+ return regionName;
+ }
+
+ public long getSizeInMemory() {
+ return -1;
+ }
+
+ public long getElementCountInMemory() {
+ try {
+ Set children = cache.getChildrenNames( regionFqn );
+ return children == null ? 0 : children.size();
+ }
+ catch (Exception e) {
+ throw new CacheException(e);
+ }
+ }
+
+ public long getElementCountOnDisk() {
+ return 0;
+ }
+
+ public Map toMap() {
+ try {
+ Map result = new HashMap();
+ Set childrenNames = cache.getChildrenNames( regionFqn );
+ if (childrenNames != null) {
+ Iterator iter = childrenNames.iterator();
+ while ( iter.hasNext() ) {
+ Object key = iter.next();
+ result.put(
+ key,
+ cache.get( new Fqn( regionFqn, key ), ITEM )
+ );
+ }
+ }
+ return result;
+ }
+ catch (Exception e) {
+ throw new CacheException(e);
+ }
+ }
+
+ public String toString() {
+ return "OptimisticTreeCache(" + regionName + ')';
+ }
+
+ public static class DataVersionAdapter implements DataVersion {
+ private final Object currentVersion;
+ private final Object previousVersion;
+ private final Comparator versionComparator;
+ private final String sourceIdentifer;
+
+ public DataVersionAdapter(Object currentVersion, Object previousVersion, Comparator versionComparator, String sourceIdentifer) {
+ this.currentVersion = currentVersion;
+ this.previousVersion = previousVersion;
+ this.versionComparator = versionComparator;
+ this.sourceIdentifer = sourceIdentifer;
+ log.trace( "created " + this );
+ }
+
+ /**
+ * newerThan() call is dispatched against the DataVersion currently
+ * associated with the node; the passed dataVersion param is the
+ * DataVersion associated with the data we are trying to put into
+ * the node.
+ * <p/>
+ * we are expected to return true in the case where we (the current
+ * node DataVersion) are newer that then incoming value. Returning
+ * true here essentially means that a optimistic lock failure has
+ * occured (because conversely, the value we are trying to put into
+ * the node is "older than" the value already there...)
+ */
+ public boolean newerThan(DataVersion dataVersion) {
+ log.trace( "checking [" + this + "] against [" + dataVersion + "]" );
+ if ( dataVersion instanceof CircumventChecksDataVersion ) {
+ log.trace( "skipping lock checks..." );
+ return false;
+ }
+ else if ( dataVersion instanceof NonLockingDataVersion ) {
+ // can happen because of the multiple ways Cache.remove()
+ // can be invoked :(
+ log.trace( "skipping lock checks..." );
+ return false;
+ }
+ DataVersionAdapter other = ( DataVersionAdapter ) dataVersion;
+ if ( other.previousVersion == null ) {
+ log.warn( "Unexpected optimistic lock check on inserting data" );
+ // work around the "feature" where tree cache is validating the
+ // inserted node during the next transaction. no idea...
+ if ( this == dataVersion ) {
+ log.trace( "skipping lock checks due to same DV instance" );
+ return false;
+ }
+ }
+ return versionComparator.compare( currentVersion, other.previousVersion ) >= 1;
+ }
+
+ public String toString() {
+ return super.toString() + " [current=" + currentVersion + ", previous=" + previousVersion + ", src=" + sourceIdentifer + "]";
+ }
+ }
+
+ /**
+ * Used in regions where no locking should ever occur. This includes query-caches,
+ * update-timestamps caches, collection caches, and entity caches where the entity
+ * is not versioned.
+ */
+ public static class NonLockingDataVersion implements DataVersion {
+ public static final DataVersion INSTANCE = new NonLockingDataVersion();
+ public boolean newerThan(DataVersion dataVersion) {
+ log.trace( "non locking lock check...");
+ return false;
+ }
+ }
+
+ /**
+ * Used to signal to a DataVersionAdapter to simply not perform any checks. This
+ * is currently needed for proper handling of remove() calls for entity cache regions
+ * (we do not know the version info...).
+ */
+ public static class CircumventChecksDataVersion implements DataVersion {
+ public static final DataVersion INSTANCE = new CircumventChecksDataVersion();
+ public boolean newerThan(DataVersion dataVersion) {
+ throw new CacheException( "optimistic locking checks should never happen on CircumventChecksDataVersion" );
+ }
+ }
+}
Added: trunk/Hibernate3/cache-jbosscache/src/main/java/org/hibernate/cache/OptimisticTreeCacheProvider.java
===================================================================
--- trunk/Hibernate3/cache-jbosscache/src/main/java/org/hibernate/cache/OptimisticTreeCacheProvider.java (rev 0)
+++ trunk/Hibernate3/cache-jbosscache/src/main/java/org/hibernate/cache/OptimisticTreeCacheProvider.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,130 @@
+//$Id: OptimisticTreeCacheProvider.java 9895 2006-05-05 19:27:17Z epbernard $
+package org.hibernate.cache;
+
+import java.util.Properties;
+import javax.transaction.TransactionManager;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.cfg.Environment;
+import org.hibernate.transaction.TransactionManagerLookup;
+import org.hibernate.transaction.TransactionManagerLookupFactory;
+import org.jboss.cache.PropertyConfigurator;
+
+/**
+ * Support for a standalone JBossCache TreeCache instance utilizing TreeCache's
+ * optimistic locking capabilities. This capability was added in JBossCache
+ * version 1.3.0; as such this provider will only work with that version or
+ * higher.
+ * <p/>
+ * The TreeCache instance is configured via a local config resource. The
+ * resource to be used for configuration can be controlled by specifying a value
+ * for the {@link #CONFIG_RESOURCE} config property.
+ *
+ * @author Steve Ebersole
+ */
+public class OptimisticTreeCacheProvider implements CacheProvider {
+
+ /**
+ * @deprecated use {@link Environment.CACHE_PROVIDER_CONFIG}
+ */
+ public static final String CONFIG_RESOURCE = "hibernate.cache.opt_tree_cache.config";
+ public static final String DEFAULT_CONFIG = "treecache.xml";
+
+ private static final String NODE_LOCKING_SCHEME = "OPTIMISTIC";
+ private static final Log log = LogFactory.getLog( OptimisticTreeCacheProvider.class );
+
+ private org.jboss.cache.TreeCache cache;
+
+ /**
+ * Construct and configure the Cache representation of a named cache region.
+ *
+ * @param regionName the name of the cache region
+ * @param properties configuration settings
+ * @return The Cache representation of the named cache region.
+ * @throws CacheException
+ * Indicates an error building the cache region.
+ */
+ public Cache buildCache(String regionName, Properties properties) throws CacheException {
+ return new OptimisticTreeCache( cache, regionName );
+ }
+
+ public long nextTimestamp() {
+ return System.currentTimeMillis() / 100;
+ }
+
+ /**
+ * Prepare the underlying JBossCache TreeCache instance.
+ *
+ * @param properties All current config settings.
+ * @throws CacheException
+ * Indicates a problem preparing cache for use.
+ */
+ public void start(Properties properties) {
+ String resource = properties.getProperty( Environment.CACHE_PROVIDER_CONFIG );
+ if (resource == null) {
+ resource = properties.getProperty( CONFIG_RESOURCE );
+ }
+ if ( resource == null ) {
+ resource = DEFAULT_CONFIG;
+ }
+ log.debug( "Configuring TreeCache from resource [" + resource + "]" );
+ try {
+ cache = new org.jboss.cache.TreeCache();
+ PropertyConfigurator config = new PropertyConfigurator();
+ config.configure( cache, resource );
+ TransactionManagerLookup transactionManagerLookup =
+ TransactionManagerLookupFactory.getTransactionManagerLookup( properties );
+ if ( transactionManagerLookup == null ) {
+ throw new CacheException(
+ "JBossCache only supports optimisitc locking with a configured " +
+ "TransactionManagerLookup (" + Environment.TRANSACTION_MANAGER_STRATEGY + ")"
+ );
+ }
+ cache.setTransactionManagerLookup(
+ new TransactionManagerLookupAdaptor(
+ transactionManagerLookup,
+ properties
+ )
+ );
+ if ( ! NODE_LOCKING_SCHEME.equalsIgnoreCase( cache.getNodeLockingScheme() ) ) {
+ log.info( "Overriding node-locking-scheme to : " + NODE_LOCKING_SCHEME );
+ cache.setNodeLockingScheme( NODE_LOCKING_SCHEME );
+ }
+ cache.start();
+ }
+ catch ( Exception e ) {
+ throw new CacheException( e );
+ }
+ }
+
+ public void stop() {
+ if ( cache != null ) {
+ cache.stop();
+ cache.destroy();
+ cache = null;
+ }
+ }
+
+ public boolean isMinimalPutsEnabledByDefault() {
+ return true;
+ }
+
+ static final class TransactionManagerLookupAdaptor implements org.jboss.cache.TransactionManagerLookup {
+ private final TransactionManagerLookup tml;
+ private final Properties props;
+
+ TransactionManagerLookupAdaptor(TransactionManagerLookup tml, Properties props) {
+ this.tml = tml;
+ this.props = props;
+ }
+
+ public TransactionManager getTransactionManager() throws Exception {
+ return tml.getTransactionManager( props );
+ }
+ }
+
+ public org.jboss.cache.TreeCache getUnderlyingCache() {
+ return cache;
+ }
+}
Added: trunk/Hibernate3/cache-jbosscache/src/main/java/org/hibernate/cache/TreeCache.java
===================================================================
--- trunk/Hibernate3/cache-jbosscache/src/main/java/org/hibernate/cache/TreeCache.java (rev 0)
+++ trunk/Hibernate3/cache-jbosscache/src/main/java/org/hibernate/cache/TreeCache.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,205 @@
+//$Id: TreeCache.java 9965 2006-05-30 18:00:28Z steve.ebersole at jboss.com $
+package org.hibernate.cache;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import javax.transaction.SystemException;
+import javax.transaction.Transaction;
+import javax.transaction.TransactionManager;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.lock.TimeoutException;
+
+/**
+ * Represents a particular region within the given JBossCache TreeCache.
+ *
+ * @author Gavin King
+ */
+public class TreeCache implements Cache {
+
+ private static final Log log = LogFactory.getLog(TreeCache.class);
+
+ private static final String ITEM = "item";
+
+ private org.jboss.cache.TreeCache cache;
+ private final String regionName;
+ private final Fqn regionFqn;
+ private final TransactionManager transactionManager;
+
+ public TreeCache(org.jboss.cache.TreeCache cache, String regionName, TransactionManager transactionManager)
+ throws CacheException {
+ this.cache = cache;
+ this.regionName = regionName;
+ this.regionFqn = Fqn.fromString( regionName.replace( '.', '/' ) );
+ this.transactionManager = transactionManager;
+ }
+
+ public Object get(Object key) throws CacheException {
+ Transaction tx = suspend();
+ try {
+ return read(key);
+ }
+ finally {
+ resume( tx );
+ }
+ }
+
+ public Object read(Object key) throws CacheException {
+ try {
+ return cache.get( new Fqn( regionFqn, key ), ITEM );
+ }
+ catch (Exception e) {
+ throw new CacheException(e);
+ }
+ }
+
+ public void update(Object key, Object value) throws CacheException {
+ try {
+ cache.put( new Fqn( regionFqn, key ), ITEM, value );
+ }
+ catch (Exception e) {
+ throw new CacheException(e);
+ }
+ }
+
+ public void put(Object key, Object value) throws CacheException {
+ Transaction tx = suspend();
+ try {
+ //do the failfast put outside the scope of the JTA txn
+ cache.putFailFast( new Fqn( regionFqn, key ), ITEM, value, 0 );
+ }
+ catch (TimeoutException te) {
+ //ignore!
+ log.debug("ignoring write lock acquisition failure");
+ }
+ catch (Exception e) {
+ throw new CacheException(e);
+ }
+ finally {
+ resume( tx );
+ }
+ }
+
+ private void resume(Transaction tx) {
+ try {
+ if (tx!=null) transactionManager.resume(tx);
+ }
+ catch (Exception e) {
+ throw new CacheException("Could not resume transaction", e);
+ }
+ }
+
+ private Transaction suspend() {
+ Transaction tx = null;
+ try {
+ if ( transactionManager!=null ) {
+ tx = transactionManager.suspend();
+ }
+ }
+ catch (SystemException se) {
+ throw new CacheException("Could not suspend transaction", se);
+ }
+ return tx;
+ }
+
+ public void remove(Object key) throws CacheException {
+ try {
+ cache.remove( new Fqn( regionFqn, key ) );
+ }
+ catch (Exception e) {
+ throw new CacheException(e);
+ }
+ }
+
+ public void clear() throws CacheException {
+ try {
+ cache.remove( regionFqn );
+ }
+ catch (Exception e) {
+ throw new CacheException(e);
+ }
+ }
+
+ public void destroy() throws CacheException {
+ try {
+ // NOTE : evict() operates locally only (i.e., does not propogate
+ // to any other nodes in the potential cluster). This is
+ // exactly what is needed when we destroy() here; destroy() is used
+ // as part of the process of shutting down a SessionFactory; thus
+ // these removals should not be propogated
+ cache.evict( regionFqn );
+ }
+ catch( Exception e ) {
+ throw new CacheException( e );
+ }
+ }
+
+ public void lock(Object key) throws CacheException {
+ throw new UnsupportedOperationException( "TreeCache is a fully transactional cache" + regionName );
+ }
+
+ public void unlock(Object key) throws CacheException {
+ throw new UnsupportedOperationException( "TreeCache is a fully transactional cache: " + regionName );
+ }
+
+ public long nextTimestamp() {
+ return System.currentTimeMillis() / 100;
+ }
+
+ public int getTimeout() {
+ return 600; //60 seconds
+ }
+
+ public String getRegionName() {
+ return regionName;
+ }
+
+ public long getSizeInMemory() {
+ return -1;
+ }
+
+ public long getElementCountInMemory() {
+ try {
+ Set children = cache.getChildrenNames( regionFqn );
+ return children == null ? 0 : children.size();
+ }
+ catch (Exception e) {
+ throw new CacheException(e);
+ }
+ }
+
+ public long getElementCountOnDisk() {
+ return 0;
+ }
+
+ public Map toMap() {
+ try {
+ Map result = new HashMap();
+ Set childrenNames = cache.getChildrenNames( regionFqn );
+ if (childrenNames != null) {
+ Iterator iter = childrenNames.iterator();
+ while ( iter.hasNext() ) {
+ Object key = iter.next();
+ result.put(
+ key,
+ cache.get( new Fqn( regionFqn, key ), ITEM )
+ );
+ }
+ }
+ return result;
+ }
+ catch (Exception e) {
+ throw new CacheException(e);
+ }
+ }
+
+ public String toString() {
+ return "TreeCache(" + regionName + ')';
+ }
+
+}
Added: trunk/Hibernate3/cache-jbosscache/src/main/java/org/hibernate/cache/TreeCacheProvider.java
===================================================================
--- trunk/Hibernate3/cache-jbosscache/src/main/java/org/hibernate/cache/TreeCacheProvider.java (rev 0)
+++ trunk/Hibernate3/cache-jbosscache/src/main/java/org/hibernate/cache/TreeCacheProvider.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,109 @@
+//$Id: TreeCacheProvider.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.cache;
+
+import java.util.Properties;
+import javax.transaction.TransactionManager;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.transaction.TransactionManagerLookup;
+import org.hibernate.transaction.TransactionManagerLookupFactory;
+import org.hibernate.cfg.Environment;
+import org.jboss.cache.PropertyConfigurator;
+
+/**
+ * Support for a standalone JBossCache (TreeCache) instance. The JBossCache is configured
+ * via a local config resource.
+ *
+ * @author Gavin King
+ */
+public class TreeCacheProvider implements CacheProvider {
+
+ /**
+ * @deprecated use {@link org.hibernate.cfg.Environment#CACHE_PROVIDER_CONFIG}
+ */
+ public static final String CONFIG_RESOURCE = "hibernate.cache.tree_cache.config";
+ public static final String DEFAULT_CONFIG = "treecache.xml";
+
+ private static final Log log = LogFactory.getLog( TreeCacheProvider.class );
+
+ private org.jboss.cache.TreeCache cache;
+ private TransactionManager transactionManager;
+
+ /**
+ * Construct and configure the Cache representation of a named cache region.
+ *
+ * @param regionName the name of the cache region
+ * @param properties configuration settings
+ * @return The Cache representation of the named cache region.
+ * @throws CacheException Indicates an error building the cache region.
+ */
+ public Cache buildCache(String regionName, Properties properties) throws CacheException {
+ return new TreeCache(cache, regionName, transactionManager);
+ }
+
+ public long nextTimestamp() {
+ return System.currentTimeMillis() / 100;
+ }
+
+ /**
+ * Prepare the underlying JBossCache TreeCache instance.
+ *
+ * @param properties All current config settings.
+ *
+ * @throws CacheException Indicates a problem preparing cache for use.
+ */
+ public void start(Properties properties) {
+ String resource = properties.getProperty( Environment.CACHE_PROVIDER_CONFIG );
+
+ if ( resource == null ) {
+ resource = properties.getProperty( CONFIG_RESOURCE );
+ }
+ if ( resource == null ) {
+ resource = DEFAULT_CONFIG;
+ }
+ log.debug( "Configuring TreeCache from resource [" + resource + "]" );
+ try {
+ cache = new org.jboss.cache.TreeCache();
+ PropertyConfigurator config = new PropertyConfigurator();
+ config.configure( cache, resource );
+ TransactionManagerLookup transactionManagerLookup = TransactionManagerLookupFactory.getTransactionManagerLookup(properties);
+ if (transactionManagerLookup!=null) {
+ cache.setTransactionManagerLookup( new TransactionManagerLookupAdaptor(transactionManagerLookup, properties) );
+ transactionManager = transactionManagerLookup.getTransactionManager(properties);
+ }
+ cache.start();
+ }
+ catch (Exception e) {
+ throw new CacheException(e);
+ }
+ }
+
+ public void stop() {
+ if (cache!=null) {
+ cache.stop();
+ cache.destroy();
+ cache=null;
+ }
+ }
+
+ public boolean isMinimalPutsEnabledByDefault() {
+ return true;
+ }
+
+ static final class TransactionManagerLookupAdaptor implements org.jboss.cache.TransactionManagerLookup {
+ private final TransactionManagerLookup tml;
+ private final Properties props;
+ TransactionManagerLookupAdaptor(TransactionManagerLookup tml, Properties props) {
+ this.tml=tml;
+ this.props=props;
+ }
+ public TransactionManager getTransactionManager() throws Exception {
+ return tml.getTransactionManager(props);
+ }
+ }
+
+ public org.jboss.cache.TreeCache getUnderlyingCache() {
+ return cache;
+ }
+}
Added: trunk/Hibernate3/cache-jbosscache2/pom.xml
===================================================================
--- trunk/Hibernate3/cache-jbosscache2/pom.xml (rev 0)
+++ trunk/Hibernate3/cache-jbosscache2/pom.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,58 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-core-project</artifactId>
+ <version>3.3.0.beta1</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-jbosscache2</artifactId>
+ <packaging>jar</packaging>
+ <name>Hibernate JBossCache2.x integration</name>
+ <description>Integration of Hibernate with JBossCache (based on JBossCache2.x APIs)</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>${groupId}</groupId>
+ <artifactId>hibernate-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>jboss</groupId>
+ <artifactId>jboss-cache</artifactId>
+ <!-- does not work with idea plugin :(
+ <version>[2.0.0.BETA2,)</version>
+ -->
+ <version>2.0.0.BETA2</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-idea-plugin</artifactId>
+ <configuration>
+ <jdkName>1.5</jdkName>
+ <jdkLevel>5.0</jdkLevel>
+ <!-- why this isnt the default I have no clue -->
+ <linkModules>true</linkModules>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.5</source>
+ <target>1.5</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
\ No newline at end of file
Added: trunk/Hibernate3/cache-jbosscache2/src/main/resources/org/hibernate/cache/jbc/TransactionManagerLookupAdaptor.java
===================================================================
--- trunk/Hibernate3/cache-jbosscache2/src/main/resources/org/hibernate/cache/jbc/TransactionManagerLookupAdaptor.java (rev 0)
+++ trunk/Hibernate3/cache-jbosscache2/src/main/resources/org/hibernate/cache/jbc/TransactionManagerLookupAdaptor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,25 @@
+package org.hibernate.cache.impl.jbc;
+
+import java.util.Properties;
+import javax.transaction.TransactionManager;
+
+import org.hibernate.transaction.TransactionManagerLookup;
+
+/**
+ * An adapter between JBossCache's notion of a TM lookup and Hibernate's.
+ *
+ * @author Steve Ebersole
+ */
+public class TransactionManagerLookupAdaptor implements org.jboss.cache.transaction.TransactionManagerLookup {
+ private final TransactionManagerLookup tml;
+ private final Properties props;
+
+ TransactionManagerLookupAdaptor(TransactionManagerLookup tml, Properties props) {
+ this.tml = tml;
+ this.props = props;
+ }
+
+ public TransactionManager getTransactionManager() throws Exception {
+ return tml.getTransactionManager( props );
+ }
+}
Added: trunk/Hibernate3/cache-jbosscache2/src/main/resources/org/hibernate/cache/jbc/TreeCacheRegionAdapter.java
===================================================================
--- trunk/Hibernate3/cache-jbosscache2/src/main/resources/org/hibernate/cache/jbc/TreeCacheRegionAdapter.java (rev 0)
+++ trunk/Hibernate3/cache-jbosscache2/src/main/resources/org/hibernate/cache/jbc/TreeCacheRegionAdapter.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,105 @@
+package org.hibernate.cache.impl.jbc;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import org.jboss.cache.Fqn;
+import org.jboss.cache.Cache;
+import org.jboss.cache.Node;
+import org.jboss.cache.config.Option;
+
+import org.hibernate.cache.Region;
+import org.hibernate.cache.CacheException;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class TreeCacheRegionAdapter implements Region {
+ private static final String ITEM = "item";
+
+ protected final Node jbcNode;
+ protected final String regionName;
+
+ public TreeCacheRegionAdapter(Cache jbcCache, String regionName) {
+ this.regionName = regionName;
+ Fqn fqn = Fqn.fromString( regionName.replace( '.', '/' ) );
+ this.jbcNode = jbcCache.getRoot().addChild( fqn );
+ }
+
+ public String getName() {
+ return regionName;
+ }
+
+ public void destroy() throws CacheException {
+ try {
+ // NOTE : this is being used from the process of shutting down a
+ // SessionFactory. Specific things to consider:
+ // (1) this clearing of the region should not propogate to
+ // other nodes on the cluster (if any); this is the
+ // cache-mode-local option bit...
+ // (2) really just trying a best effort to cleanup after
+ // ourselves; lock failures, etc are not critical here;
+ // this is the fail-silently option bit...
+ Option option = new Option();
+ option.setCacheModeLocal( true );
+ option.setFailSilently( true );
+ jbcNode.
+ jbcTreeCache.remove( regionFqn, option );
+ }
+ catch( Exception e ) {
+ throw new CacheException( e );
+ }
+ }
+
+ public long getSizeInMemory() {
+ // not supported
+ return -1;
+ }
+
+ public long getElementCountInMemory() {
+ try {
+ Set children = jbcTreeCache.getChildrenNames( regionFqn );
+ return children == null ? 0 : children.size();
+ }
+ catch ( Exception e ) {
+ throw new CacheException( e );
+ }
+ }
+
+ public long getElementCountOnDisk() {
+ return -1;
+ }
+
+ public Map toMap() {
+ try {
+ Map result = new HashMap();
+ Set childrenNames = jbcTreeCache.getChildrenNames( regionFqn );
+ if (childrenNames != null) {
+ Iterator iter = childrenNames.iterator();
+ while ( iter.hasNext() ) {
+ Object key = iter.next();
+ result.put(
+ key,
+ jbcTreeCache.get( new Fqn( regionFqn, key ), ITEM )
+ );
+ }
+ }
+ return result;
+ }
+ catch (Exception e) {
+ throw new CacheException(e);
+ }
+ }
+
+ public long nextTimestamp() {
+ return System.currentTimeMillis() / 100;
+ }
+
+ public int getTimeout() {
+ return 600; //60 seconds
+ }
+}
Added: trunk/Hibernate3/cache-jbosscache2/src/main/resources/org/hibernate/cache/jbc/TreeCacheRegionFactory.java
===================================================================
--- trunk/Hibernate3/cache-jbosscache2/src/main/resources/org/hibernate/cache/jbc/TreeCacheRegionFactory.java (rev 0)
+++ trunk/Hibernate3/cache-jbosscache2/src/main/resources/org/hibernate/cache/jbc/TreeCacheRegionFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,226 @@
+package org.hibernate.cache.impl.jbc;
+
+import java.util.Properties;
+
+import javax.transaction.TransactionManager;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.jboss.cache.Cache;
+import org.jboss.cache.DefaultCacheFactory;
+
+import org.hibernate.cache.RegionFactory;
+import org.hibernate.cache.CacheException;
+import org.hibernate.cache.EntityRegion;
+import org.hibernate.cache.CacheDataDescription;
+import org.hibernate.cache.CollectionRegion;
+import org.hibernate.cache.QueryResultsRegion;
+import org.hibernate.cache.TimestampsRegion;
+import org.hibernate.cache.access.EntityRegionAccessStrategy;
+import org.hibernate.cache.access.AccessType;
+import org.hibernate.cfg.Settings;
+import org.hibernate.util.PropertiesHelper;
+
+/**
+ * A factory for building regions based on a JBossCache
+ * {@link org.jboss.cache.Node}. Here we are utilizing the
+ * same underlying {@link org.jboss.cache.Node} instance for each jbcTreeCache region.
+ *
+ * @author Steve Ebersole
+ */
+public class TreeCacheRegionFactory implements RegionFactory {
+ public static final String ENTITY_CACHE_RESOURCE_PROP = "hibernate.cache.region.jbc.cfg.entity";
+ public static final String COLL_CACHE_RESOURCE_PROP = "hibernate.cache.region.jbc.cfg.collection";
+ public static final String TS_CACHE_RESOURCE_PROP = "hibernate.cache.region.jbc.cfg.ts";
+ public static final String QUERY_CACHE_RESOURCE_PROP = "hibernate.cache.region.jbc.cfg.query";
+
+ public static final String DEF_ENTITY_RESOURCE = "entity-cache.xml";
+ public static final String DEF_COLL_RESOURCE = "collection-cache.xml";
+ public static final String DEF_TS_RESOURCE = "ts-cache.xml";
+ public static final String DEF_QUERY_RESOURCE = "query-cache.xml";
+
+ public static final String OPTIMISTIC_LOCKING_SCHEME = "OPTIMISTIC";
+
+ private static final Log log = LogFactory.getLog( TreeCacheRegionFactory.class );
+
+ private Cache jbcEntityCache;
+ private Cache jbcCollectionCache;
+ private Cache jbcTsCache;
+ private Cache jbcQueryCache;
+ private boolean useOptimisticLocking;
+
+ public void start(Settings settings, Properties properties) throws CacheException {
+ try {
+ TransactionManager tm = settings.getTransactionManagerLookup() == null
+ ? null
+ : settings.getTransactionManagerLookup().getTransactionManager( properties );
+ if ( settings.isSecondLevelCacheEnabled() ) {
+ jbcEntityCache = buildEntityRegionCacheInstance( properties );
+ jbcCollectionCache = buildCollectionRegionCacheInstance( properties );
+ if ( tm != null ) {
+ jbcEntityCache.getConfiguration().getRuntimeConfig().setTransactionManager( tm );
+ jbcCollectionCache.getConfiguration().getRuntimeConfig().setTransactionManager( tm );
+ }
+ }
+ if ( settings.isQueryCacheEnabled() ) {
+ jbcTsCache = buildTsRegionCacheInstance( properties );
+ jbcQueryCache = buildQueryRegionCacheInstance( properties );
+ }
+ }
+ catch( CacheException ce ) {
+ throw ce;
+ }
+ catch( Throwable t ) {
+ throw new CacheException( "Unable to start region factory", t );
+ }
+// String resource = PropertiesHelper.getString( Environment.CACHE_PROVIDER_CONFIG, properties, DEFAULT_CONFIG );
+// log.debug( "Configuring basic TreeCache RegionFactory from resource [" + resource + "]" );
+// try {
+// jbcTreeCache = new TreeCache();
+// PropertyConfigurator config = new PropertyConfigurator();
+// config.configure( jbcTreeCache, resource );
+// TransactionManagerLookup transactionManagerLookup = settings.getTransactionManagerLookup();
+// if ( transactionManagerLookup != null ) {
+// jbcTreeCache.setTransactionManagerLookup(
+// new TransactionManagerLookupAdaptor( transactionManagerLookup, properties )
+// );
+// }
+// jbcTreeCache.start();
+// useOptimisticLocking = OPTIMISTIC_LOCKING_SCHEME.equalsIgnoreCase( jbcTreeCache.getNodeLockingScheme() );
+// }
+// catch ( Exception e ) {
+// throw new CacheException( e );
+// }
+ }
+
+ protected Cache buildEntityRegionCacheInstance(Properties properties) {
+ try {
+ String configResource = PropertiesHelper.getString( ENTITY_CACHE_RESOURCE_PROP, properties, DEF_ENTITY_RESOURCE );
+ return DefaultCacheFactory.getInstance().createCache( configResource );
+ }
+ catch( Throwable t ) {
+ throw new CacheException( "unable to build entity region cache instance", t );
+ }
+ }
+
+ protected Cache buildCollectionRegionCacheInstance(Properties properties) {
+ try {
+ String configResource = PropertiesHelper.getString( COLL_CACHE_RESOURCE_PROP, properties, DEF_COLL_RESOURCE );
+ return DefaultCacheFactory.getInstance().createCache( configResource );
+ }
+ catch( Throwable t ) {
+ throw new CacheException( "unable to build collection region cache instance", t );
+ }
+ }
+
+ protected Cache buildTsRegionCacheInstance(Properties properties) {
+ try {
+ String configResource = PropertiesHelper.getString( TS_CACHE_RESOURCE_PROP, properties, DEF_TS_RESOURCE );
+ return DefaultCacheFactory.getInstance().createCache( configResource );
+ }
+ catch( Throwable t ) {
+ throw new CacheException( "unable to build timestamps region cache instance", t );
+ }
+ }
+
+ protected Cache buildQueryRegionCacheInstance(Properties properties) {
+ try {
+ String configResource = PropertiesHelper.getString( QUERY_CACHE_RESOURCE_PROP, properties, DEF_QUERY_RESOURCE );
+ return DefaultCacheFactory.getInstance().createCache( configResource );
+ }
+ catch( Throwable t ) {
+ throw new CacheException( "unable to build query region cache instance", t );
+ }
+ }
+
+ public void stop() {
+ if ( jbcEntityCache != null ) {
+ try {
+ jbcEntityCache.stop();
+ }
+ catch( Throwable t ) {
+ log.info( "Unable to stop entity cache instance", t );
+ }
+ }
+ if ( jbcCollectionCache != null ) {
+ try {
+ jbcCollectionCache.stop();
+ }
+ catch( Throwable t ) {
+ log.info( "Unable to stop collection cache instance", t );
+ }
+ }
+ if ( jbcTsCache != null ) {
+ try {
+ jbcTsCache.stop();
+ }
+ catch( Throwable t ) {
+ log.info( "Unable to stop timestamp cache instance", t );
+ }
+ }
+ if ( jbcQueryCache != null ) {
+ try {
+ jbcQueryCache.stop();
+ }
+ catch( Throwable t ) {
+ log.info( "Unable to stop query cache instance", t );
+ }
+ }
+ }
+
+ public boolean isMinimalPutsEnabledByDefault() {
+ return true;
+ }
+
+ public long nextTimestamp() {
+ return System.currentTimeMillis() / 100;
+ }
+
+ public EntityRegion buildEntityRegion(String regionName, Properties properties, CacheDataDescription metadata) {
+ if ( useOptimisticLocking && !metadata.isVersioned() ) {
+ log.warn( "JBossCache configured to use optimistic locking, but entity to be cached is not versioned [" + regionName + "]" );
+ }
+ else if ( !useOptimisticLocking && metadata.isVersioned() ) {
+ log.info( "Caching versioned entity without optimisitic locking; consider optimistic locking if all cached entities are versioned" );
+ }
+ return new EntityRegionAdapter( regionName, metadata );
+ }
+
+ public CollectionRegion buildCollectionRegion(String regionName, Properties properties, CacheDataDescription metadata)
+ throws CacheException {
+ return null;
+ }
+
+ public QueryResultsRegion buildQueryResultsRegion(String regionName, Properties properties) throws CacheException {
+ return null;
+ }
+
+ public TimestampsRegion buildTimestampsRegion(String regionName, Properties properties) throws CacheException {
+ return null;
+ }
+
+ private class EntityRegionAdapter extends TreeCacheRegionAdapter implements EntityRegion {
+ private final CacheDataDescription metadata;
+
+ public EntityRegionAdapter(String regionName, CacheDataDescription metadata) {
+ super( TreeCacheRegionFactory.this.jbcTreeCache, regionName );
+ this.metadata = metadata;
+ }
+
+ public boolean isTransactionAware() {
+ return jbcTreeCache.getTransactionManager() != null;
+ }
+
+ public CacheDataDescription getCacheDataDescription() {
+ return metadata;
+ }
+
+ public EntityRegionAccessStrategy buildAccessStrategy(AccessType accessType) throws CacheException {
+ if ( ! ( AccessType.READ_ONLY.equals( accessType ) || AccessType.TRANSACTIONAL.equals( accessType ) ) ) {
+ throw new CacheException( "TreeCacheRegionFactory only supports ( " + AccessType.READ_ONLY.getName() + " | " + AccessType.TRANSACTIONAL + " ) access strategies [" + accessType.getName() + "]" );
+ }
+ // todo : implement :)
+ return null;
+ }
+ }
+}
Added: trunk/Hibernate3/cache-oscache/pom.xml
===================================================================
--- trunk/Hibernate3/cache-oscache/pom.xml (rev 0)
+++ trunk/Hibernate3/cache-oscache/pom.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,32 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-core-project</artifactId>
+ <version>3.3.0.beta1</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-oscache</artifactId>
+ <packaging>jar</packaging>
+ <name>Hibernate OSCache integration</name>
+ <description>Integration of Hibernate with OSCache</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>opensymphony</groupId>
+ <artifactId>oscache</artifactId>
+ <version>2.1</version>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
Added: trunk/Hibernate3/cache-oscache/src/main/java/org/hibernate/cache/OSCache.java
===================================================================
--- trunk/Hibernate3/cache-oscache/src/main/java/org/hibernate/cache/OSCache.java (rev 0)
+++ trunk/Hibernate3/cache-oscache/src/main/java/org/hibernate/cache/OSCache.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,111 @@
+//$Id: OSCache.java 6478 2005-04-21 07:57:19Z oneovthafew $
+package org.hibernate.cache;
+
+import java.util.Map;
+
+import com.opensymphony.oscache.base.NeedsRefreshException;
+import com.opensymphony.oscache.general.GeneralCacheAdministrator;
+
+/**
+ * @author <a href="mailto:m.bogaert at intrasoft.be">Mathias Bogaert</a>
+ */
+public class OSCache implements Cache {
+
+ /**
+ * The OSCache 2.0 cache administrator.
+ */
+ private GeneralCacheAdministrator cache = new GeneralCacheAdministrator();
+
+ private final int refreshPeriod;
+ private final String cron;
+ private final String regionName;
+
+ private String toString(Object key) {
+ return String.valueOf(key) + '.' + regionName;
+ }
+
+ public OSCache(int refreshPeriod, String cron, String region) {
+ this.refreshPeriod = refreshPeriod;
+ this.cron = cron;
+ this.regionName = region;
+ }
+
+ public void setCacheCapacity(int cacheCapacity) {
+ cache.setCacheCapacity(cacheCapacity);
+ }
+
+ public Object get(Object key) throws CacheException {
+ try {
+ return cache.getFromCache( toString(key), refreshPeriod, cron );
+ }
+ catch (NeedsRefreshException e) {
+ cache.cancelUpdate( toString(key) );
+ return null;
+ }
+ }
+
+ public Object read(Object key) throws CacheException {
+ return get(key);
+ }
+
+ public void update(Object key, Object value) throws CacheException {
+ put(key, value);
+ }
+
+ public void put(Object key, Object value) throws CacheException {
+ cache.putInCache( toString(key), value );
+ }
+
+ public void remove(Object key) throws CacheException {
+ cache.flushEntry( toString(key) );
+ }
+
+ public void clear() throws CacheException {
+ cache.flushAll();
+ }
+
+ public void destroy() throws CacheException {
+ cache.destroy();
+ }
+
+ public void lock(Object key) throws CacheException {
+ // local cache, so we use synchronization
+ }
+
+ public void unlock(Object key) throws CacheException {
+ // local cache, so we use synchronization
+ }
+
+ public long nextTimestamp() {
+ return Timestamper.next();
+ }
+
+ public int getTimeout() {
+ return Timestamper.ONE_MS * 60000; //ie. 60 seconds
+ }
+
+ public String getRegionName() {
+ return regionName;
+ }
+
+ public long getSizeInMemory() {
+ return -1;
+ }
+
+ public long getElementCountInMemory() {
+ return -1;
+ }
+
+ public long getElementCountOnDisk() {
+ return -1;
+ }
+
+ public Map toMap() {
+ throw new UnsupportedOperationException();
+ }
+
+ public String toString() {
+ return "OSCache(" + regionName + ')';
+ }
+
+}
Added: trunk/Hibernate3/cache-oscache/src/main/java/org/hibernate/cache/OSCacheProvider.java
===================================================================
--- trunk/Hibernate3/cache-oscache/src/main/java/org/hibernate/cache/OSCacheProvider.java (rev 0)
+++ trunk/Hibernate3/cache-oscache/src/main/java/org/hibernate/cache/OSCacheProvider.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,87 @@
+//$Id: OSCacheProvider.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate.cache;
+
+import java.util.Properties;
+
+import org.hibernate.util.PropertiesHelper;
+import org.hibernate.util.StringHelper;
+
+import com.opensymphony.oscache.base.CacheEntry;
+import com.opensymphony.oscache.base.Config;
+
+/**
+ * Support for OpenSymphony OSCache. This implementation assumes
+ * that identifiers have well-behaved <tt>toString()</tt> methods.
+ *
+ * @author <a href="mailto:m.bogaert at intrasoft.be">Mathias Bogaert</a>
+ */
+public class OSCacheProvider implements CacheProvider {
+
+ /**
+ * The <tt>OSCache</tt> refresh period property suffix.
+ */
+ public static final String OSCACHE_REFRESH_PERIOD = "refresh.period";
+ /**
+ * The <tt>OSCache</tt> CRON expression property suffix.
+ */
+ public static final String OSCACHE_CRON = "cron";
+ /**
+ * The <tt>OSCache</tt> cache capacity property suffix.
+ */
+ public static final String OSCACHE_CAPACITY = "capacity";
+
+ private static final Properties OSCACHE_PROPERTIES = new Config().getProperties();
+
+ /**
+ * Builds a new {@link Cache} instance, and gets it's properties from the OSCache {@link Config}
+ * which reads the properties file (<code>oscache.properties</code>) from the classpath.
+ * If the file cannot be found or loaded, an the defaults are used.
+ *
+ * @param region
+ * @param properties
+ * @return
+ * @throws CacheException
+ */
+ public Cache buildCache(String region, Properties properties) throws CacheException {
+
+ int refreshPeriod = PropertiesHelper.getInt(
+ StringHelper.qualify(region, OSCACHE_REFRESH_PERIOD),
+ OSCACHE_PROPERTIES,
+ CacheEntry.INDEFINITE_EXPIRY
+ );
+ String cron = OSCACHE_PROPERTIES.getProperty( StringHelper.qualify(region, OSCACHE_CRON) );
+
+ // construct the cache
+ final OSCache cache = new OSCache(refreshPeriod, cron, region);
+
+ Integer capacity = PropertiesHelper.getInteger( StringHelper.qualify(region, OSCACHE_CAPACITY), OSCACHE_PROPERTIES );
+ if ( capacity!=null ) cache.setCacheCapacity( capacity.intValue() );
+
+ return cache;
+ }
+
+ public long nextTimestamp() {
+ return Timestamper.next();
+ }
+
+ /**
+ * Callback to perform any necessary initialization of the underlying cache implementation
+ * during SessionFactory construction.
+ *
+ * @param properties current configuration settings.
+ */
+ public void start(Properties properties) throws CacheException {
+ }
+
+ /**
+ * Callback to perform any necessary cleanup of the underlying cache implementation
+ * during SessionFactory.close().
+ */
+ public void stop() {
+ }
+
+ public boolean isMinimalPutsEnabledByDefault() {
+ return false;
+ }
+
+}
Added: trunk/Hibernate3/cache-swarmcache/pom.xml
===================================================================
--- trunk/Hibernate3/cache-swarmcache/pom.xml (rev 0)
+++ trunk/Hibernate3/cache-swarmcache/pom.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,32 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-core-project</artifactId>
+ <version>3.3.0.beta1</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-swarmcache</artifactId>
+ <packaging>jar</packaging>
+ <name>Hibernate SwarmCache integration</name>
+ <description>Integration of Hibernate with SwarmCache</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>swarmcache</groupId>
+ <artifactId>swarmcache</artifactId>
+ <version>1.0RC2</version>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
Added: trunk/Hibernate3/cache-swarmcache/src/main/java/org/hibernate/cache/SwarmCache.java
===================================================================
--- trunk/Hibernate3/cache-swarmcache/src/main/java/org/hibernate/cache/SwarmCache.java (rev 0)
+++ trunk/Hibernate3/cache-swarmcache/src/main/java/org/hibernate/cache/SwarmCache.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,144 @@
+//$Id: SwarmCache.java 6478 2005-04-21 07:57:19Z oneovthafew $
+package org.hibernate.cache;
+
+import net.sf.swarmcache.ObjectCache;
+
+import java.io.Serializable;
+import java.util.Map;
+
+/**
+ * @author Jason Carreira, Gavin King
+ */
+public class SwarmCache implements Cache {
+
+ private final ObjectCache cache;
+ private final String regionName;
+
+ public SwarmCache(ObjectCache cache, String regionName) {
+ this.cache = cache;
+ this.regionName = regionName;
+ }
+
+ /**
+ * Get an item from the cache
+ * @param key
+ * @return the cached object or <tt>null</tt>
+ * @throws CacheException
+ */
+ public Object get(Object key) throws CacheException {
+ if (key instanceof Serializable) {
+ return cache.get( (Serializable) key );
+ }
+ else {
+ throw new CacheException("Keys must implement Serializable");
+ }
+ }
+
+ public Object read(Object key) throws CacheException {
+ return get(key);
+ }
+
+ /**
+ * Add an item to the cache
+ * @param key
+ * @param value
+ * @throws CacheException
+ */
+ public void update(Object key, Object value) throws CacheException {
+ put(key, value);
+ }
+
+ /**
+ * Add an item to the cache
+ * @param key
+ * @param value
+ * @throws CacheException
+ */
+ public void put(Object key, Object value) throws CacheException {
+ if (key instanceof Serializable) {
+ cache.put( (Serializable) key, value );
+ }
+ else {
+ throw new CacheException("Keys must implement Serializable");
+ }
+ }
+
+ /**
+ * Remove an item from the cache
+ */
+ public void remove(Object key) throws CacheException {
+ if (key instanceof Serializable) {
+ cache.clear( (Serializable) key );
+ }
+ else {
+ throw new CacheException("Keys must implement Serializable");
+ }
+ }
+
+ /**
+ * Clear the cache
+ */
+ public void clear() throws CacheException {
+ cache.clearAll();
+ }
+
+ /**
+ * Clean up
+ */
+ public void destroy() throws CacheException {
+ cache.clearAll();
+ }
+
+ /**
+ * If this is a clustered cache, lock the item
+ */
+ public void lock(Object key) throws CacheException {
+ throw new UnsupportedOperationException("SwarmCache does not support locking (use nonstrict-read-write)");
+ }
+
+ /**
+ * If this is a clustered cache, unlock the item
+ */
+ public void unlock(Object key) throws CacheException {
+ throw new UnsupportedOperationException("SwarmCache does not support locking (use nonstrict-read-write)");
+ }
+
+ /**
+ * Generate a (coarse) timestamp
+ */
+ public long nextTimestamp() {
+ return System.currentTimeMillis() / 100;
+ }
+
+ /**
+ * Get a reasonable "lock timeout"
+ */
+ public int getTimeout() {
+ return 600;
+ }
+
+ public String getRegionName() {
+ return regionName;
+ }
+
+ public long getSizeInMemory() {
+ return -1;
+ }
+
+ public long getElementCountInMemory() {
+ return -1;
+ }
+
+ public long getElementCountOnDisk() {
+ return -1;
+ }
+
+ public Map toMap() {
+ throw new UnsupportedOperationException();
+ }
+
+ public String toString() {
+ return "SwarmCache(" + regionName + ')';
+ }
+
+}
Added: trunk/Hibernate3/cache-swarmcache/src/main/java/org/hibernate/cache/SwarmCacheProvider.java
===================================================================
--- trunk/Hibernate3/cache-swarmcache/src/main/java/org/hibernate/cache/SwarmCacheProvider.java (rev 0)
+++ trunk/Hibernate3/cache-swarmcache/src/main/java/org/hibernate/cache/SwarmCacheProvider.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,58 @@
+//$Id: SwarmCacheProvider.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate.cache;
+
+import net.sf.swarmcache.CacheConfiguration;
+import net.sf.swarmcache.CacheConfigurationManager;
+import net.sf.swarmcache.CacheFactory;
+import net.sf.swarmcache.ObjectCache;
+
+import java.util.Properties;
+
+/**
+ * Support for SwarmCache replicated cache. SwarmCache does not support
+ * locking, so strict "read-write" semantics are unsupported.
+ * @author Jason Carreira
+ */
+public class SwarmCacheProvider implements CacheProvider {
+
+ private CacheFactory factory;
+
+ public Cache buildCache(String regionName, Properties properties) throws CacheException {
+ ObjectCache cache = factory.createCache(regionName);
+ if (cache==null) {
+ throw new CacheException("SwarmCache did not create a cache: " + regionName);
+ }
+ return new SwarmCache(cache, regionName);
+ }
+
+ public long nextTimestamp() {
+ return System.currentTimeMillis() / 100;
+ }
+
+ /**
+ * Callback to perform any necessary initialization of the underlying cache implementation
+ * during SessionFactory construction.
+ *
+ * @param properties current configuration settings.
+ */
+ public void start(Properties properties) throws CacheException {
+ CacheConfiguration config = CacheConfigurationManager.getConfig(properties);
+ factory = new CacheFactory(config);
+ }
+
+ /**
+ * Callback to perform any necessary cleanup of the underlying cache implementation
+ * during SessionFactory.close().
+ */
+ public void stop() {
+ if (factory != null) {
+ factory.shutdown();
+ factory = null;
+ }
+ }
+
+ public boolean isMinimalPutsEnabledByDefault() {
+ return true;
+ }
+
+}
Deleted: trunk/Hibernate3/checkstyle_checks.xml
===================================================================
--- trunk/Hibernate3/checkstyle_checks.xml 2007-05-24 19:06:06 UTC (rev 11562)
+++ trunk/Hibernate3/checkstyle_checks.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -1,115 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.1//EN" "http://www.puppycrawl.com/dtds/configuration_1_1.dtd">
-<module name="Checker">
- <module name="TreeWalker">
- <module name="com.puppycrawl.tools.checkstyle.checks.blocks.AvoidNestedBlocksCheck">
- <property name="severity" value="warning"/>
- </module>
- <module name="com.puppycrawl.tools.checkstyle.checks.imports.AvoidStarImportCheck">
- <property name="severity" value="warning"/>
- </module>
- <module name="com.puppycrawl.tools.checkstyle.checks.naming.ConstantNameCheck">
- <property name="format" value="^[A-Z](_?[A-Z0-9]+)*$|log"/>
- <property name="severity" value="warning"/>
- </module>
- <module name="com.puppycrawl.tools.checkstyle.checks.coding.EmptyStatementCheck">
- <property name="severity" value="warning"/>
- </module>
- <module name="com.puppycrawl.tools.checkstyle.checks.coding.EqualsHashCodeCheck">
- <property name="severity" value="warning"/>
- </module>
- <module name="com.puppycrawl.tools.checkstyle.checks.design.FinalClassCheck">
- <property name="severity" value="warning"/>
- </module>
- <module name="com.puppycrawl.tools.checkstyle.checks.GenericIllegalRegexpCheck">
- <property name="format" value="\(\(|\)\)"/>
- <property name="ignoreCase" value="false"/>
- <property name="message" value="stacked parentheses"/>
- <property name="severity" value="warning"/>
- </module>
- <module name="com.puppycrawl.tools.checkstyle.checks.coding.HiddenFieldCheck">
- <property name="severity" value="warning"/>
- <property name="tokens" value="VARIABLE_DEF"/>
- </module>
- <module name="com.puppycrawl.tools.checkstyle.checks.design.HideUtilityClassConstructorCheck">
- <property name="severity" value="warning"/>
- </module>
- <module name="com.puppycrawl.tools.checkstyle.checks.coding.IllegalInstantiationCheck">
- <property name="classes" value="{}"/>
- <property name="severity" value="warning"/>
- </module>
- <module name="com.puppycrawl.tools.checkstyle.checks.coding.InnerAssignmentCheck">
- <property name="severity" value="warning"/>
- <property name="tokens" value="ASSIGN, BAND_ASSIGN, BOR_ASSIGN, BSR_ASSIGN, BXOR_ASSIGN, DIV_ASSIGN, MINUS_ASSIGN, MOD_ASSIGN, PLUS_ASSIGN, SL_ASSIGN, SR_ASSIGN, STAR_ASSIGN"/>
- </module>
- <module name="com.puppycrawl.tools.checkstyle.checks.design.InterfaceIsTypeCheck">
- <property name="allowMarkerInterfaces" value="true"/>
- <property name="severity" value="warning"/>
- </module>
- <module name="com.puppycrawl.tools.checkstyle.checks.blocks.LeftCurlyCheck">
- <property name="maxLineLength" value="80"/>
- <property name="option" value="eol"/>
- <property name="severity" value="warning"/>
- <property name="tokens" value="CLASS_DEF, CTOR_DEF, INTERFACE_DEF, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF, LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, METHOD_DEF"/>
- </module>
- <module name="com.puppycrawl.tools.checkstyle.checks.naming.LocalFinalVariableNameCheck">
- <property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
- <property name="severity" value="warning"/>
- </module>
- <module name="com.puppycrawl.tools.checkstyle.checks.naming.LocalVariableNameCheck">
- <property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
- <property name="severity" value="warning"/>
- </module>
- <module name="com.puppycrawl.tools.checkstyle.checks.naming.MemberNameCheck">
- <property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
- <property name="severity" value="warning"/>
- </module>
- <module name="com.puppycrawl.tools.checkstyle.checks.naming.MethodNameCheck">
- <property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
- <property name="severity" value="warning"/>
- </module>
- <module name="com.puppycrawl.tools.checkstyle.checks.ModifierOrderCheck">
- <property name="severity" value="warning"/>
- </module>
- <module name="com.puppycrawl.tools.checkstyle.checks.blocks.NeedBracesCheck">
- <property name="severity" value="warning"/>
- <property name="tokens" value="LITERAL_DO, LITERAL_ELSE"/>
- </module>
- <module name="com.puppycrawl.tools.checkstyle.checks.naming.PackageNameCheck">
- <property name="format" value="^[a-z]+(\.[a-zA-Z_][a-zA-Z0-9_]*)*$"/>
- <property name="severity" value="warning"/>
- </module>
- <module name="com.puppycrawl.tools.checkstyle.checks.imports.RedundantImportCheck">
- <property name="severity" value="warning"/>
- </module>
- <module name="com.puppycrawl.tools.checkstyle.checks.blocks.RightCurlyCheck">
- <property name="option" value="alone"/>
- <property name="severity" value="warning"/>
- <property name="tokens" value="LITERAL_CATCH, LITERAL_ELSE, LITERAL_TRY"/>
- </module>
- <module name="com.puppycrawl.tools.checkstyle.checks.coding.SimplifyBooleanExpressionCheck">
- <property name="severity" value="warning"/>
- </module>
- <module name="com.puppycrawl.tools.checkstyle.checks.coding.SimplifyBooleanReturnCheck">
- <property name="severity" value="warning"/>
- </module>
- <module name="com.puppycrawl.tools.checkstyle.checks.naming.StaticVariableNameCheck">
- <property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
- <property name="severity" value="warning"/>
- </module>
- <module name="com.puppycrawl.tools.checkstyle.checks.naming.TypeNameCheck">
- <property name="format" value="^[A-Z][a-zA-Z0-9]*$"/>
- <property name="severity" value="warning"/>
- <property name="tokens" value="CLASS_DEF, INTERFACE_DEF"/>
- </module>
- <module name="com.puppycrawl.tools.checkstyle.checks.imports.UnusedImportsCheck">
- <property name="severity" value="warning"/>
- </module>
- <module name="com.puppycrawl.tools.checkstyle.checks.design.VisibilityModifierCheck">
- <property name="packageAllowed" value="true"/>
- <property name="protectedAllowed" value="true"/>
- <property name="publicMemberPattern" value="^serialVersionUID"/>
- <property name="severity" value="warning"/>
- </module>
- </module>
-</module>
Added: trunk/Hibernate3/connection-c3p0/pom.xml
===================================================================
--- trunk/Hibernate3/connection-c3p0/pom.xml (rev 0)
+++ trunk/Hibernate3/connection-c3p0/pom.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,32 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-core-project</artifactId>
+ <version>3.3.0.beta1</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-c3p0</artifactId>
+ <packaging>jar</packaging>
+ <name>Hibernate C3P0 ConnectionProvider</name>
+ <description>C3P0-based implementation of the Hibernate ConnectionProvder contract</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>c3p0</groupId>
+ <artifactId>c3p0</artifactId>
+ <version>0.9.1</version>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
Added: trunk/Hibernate3/connection-c3p0/src/main/java/org/hibernate/connection/C3P0ConnectionProvider.java
===================================================================
--- trunk/Hibernate3/connection-c3p0/src/main/java/org/hibernate/connection/C3P0ConnectionProvider.java (rev 0)
+++ trunk/Hibernate3/connection-c3p0/src/main/java/org/hibernate/connection/C3P0ConnectionProvider.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,218 @@
+//$Id: C3P0ConnectionProvider.java 11066 2007-01-19 15:14:31Z steve.ebersole at jboss.com $
+package org.hibernate.connection;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Iterator;
+import java.util.Properties;
+
+import javax.sql.DataSource;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import com.mchange.v2.c3p0.DataSources;
+
+import org.hibernate.HibernateException;
+import org.hibernate.cfg.Environment;
+import org.hibernate.util.PropertiesHelper;
+import org.hibernate.util.ReflectHelper;
+
+/**
+ * A connection provider that uses a C3P0 connection pool. Hibernate will use this by
+ * default if the <tt>hibernate.c3p0.*</tt> properties are set.
+ *
+ * @author various people
+ * @see ConnectionProvider
+ */
+public class C3P0ConnectionProvider implements ConnectionProvider {
+
+ private static final Log log = LogFactory.getLog( C3P0ConnectionProvider.class );
+
+ //swaldman 2006-08-28: define c3p0-style configuration parameters for properties with
+ // hibernate-specific overrides to detect and warn about conflicting
+ // declarations
+ private final static String C3P0_STYLE_MIN_POOL_SIZE = "c3p0.minPoolSize";
+ private final static String C3P0_STYLE_MAX_POOL_SIZE = "c3p0.maxPoolSize";
+ private final static String C3P0_STYLE_MAX_IDLE_TIME = "c3p0.maxIdleTime";
+ private final static String C3P0_STYLE_MAX_STATEMENTS = "c3p0.maxStatements";
+ private final static String C3P0_STYLE_ACQUIRE_INCREMENT = "c3p0.acquireIncrement";
+ private final static String C3P0_STYLE_IDLE_CONNECTION_TEST_PERIOD = "c3p0.idleConnectionTestPeriod";
+ private final static String C3P0_STYLE_TEST_CONNECTION_ON_CHECKOUT = "c3p0.testConnectionOnCheckout";
+
+ //swaldman 2006-08-28: define c3p0-style configuration parameters for initialPoolSize, which
+ // hibernate sensibly lets default to minPoolSize, but we'll let users
+ // override it with the c3p0-style property if they want.
+ private final static String C3P0_STYLE_INITIAL_POOL_SIZE = "c3p0.initialPoolSize";
+
+ private DataSource ds;
+ private Integer isolation;
+ private boolean autocommit;
+
+ /**
+ * {@inheritDoc}
+ */
+ public Connection getConnection() throws SQLException {
+ final Connection c = ds.getConnection();
+ if ( isolation != null ) {
+ c.setTransactionIsolation( isolation.intValue() );
+ }
+ if ( c.getAutoCommit() != autocommit ) {
+ c.setAutoCommit( autocommit );
+ }
+ return c;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void closeConnection(Connection conn) throws SQLException {
+ conn.close();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void configure(Properties props) throws HibernateException {
+ String jdbcDriverClass = props.getProperty( Environment.DRIVER );
+ String jdbcUrl = props.getProperty( Environment.URL );
+ Properties connectionProps = ConnectionProviderFactory.getConnectionProperties( props );
+
+ log.info( "C3P0 using driver: " + jdbcDriverClass + " at URL: " + jdbcUrl );
+ log.info( "Connection properties: " + PropertiesHelper.maskOut( connectionProps, "password" ) );
+
+ autocommit = PropertiesHelper.getBoolean( Environment.AUTOCOMMIT, props );
+ log.info( "autocommit mode: " + autocommit );
+
+ if ( jdbcDriverClass == null ) {
+ log.warn( "No JDBC Driver class was specified by property " + Environment.DRIVER );
+ }
+ else {
+ try {
+ Class.forName( jdbcDriverClass );
+ }
+ catch ( ClassNotFoundException cnfe ) {
+ try {
+ ReflectHelper.classForName( jdbcDriverClass );
+ }
+ catch ( ClassNotFoundException e ) {
+ String msg = "JDBC Driver class not found: " + jdbcDriverClass;
+ log.fatal( msg, e );
+ throw new HibernateException( msg, e );
+ }
+ }
+ }
+
+ try {
+
+ //swaldman 2004-02-07: modify to allow null values to signify fall through to c3p0 PoolConfig defaults
+ Integer minPoolSize = PropertiesHelper.getInteger( Environment.C3P0_MIN_SIZE, props );
+ Integer maxPoolSize = PropertiesHelper.getInteger( Environment.C3P0_MAX_SIZE, props );
+ Integer maxIdleTime = PropertiesHelper.getInteger( Environment.C3P0_TIMEOUT, props );
+ Integer maxStatements = PropertiesHelper.getInteger( Environment.C3P0_MAX_STATEMENTS, props );
+ Integer acquireIncrement = PropertiesHelper.getInteger( Environment.C3P0_ACQUIRE_INCREMENT, props );
+ Integer idleTestPeriod = PropertiesHelper.getInteger( Environment.C3P0_IDLE_TEST_PERIOD, props );
+
+ Properties c3props = new Properties();
+
+ // turn hibernate.c3p0.* into c3p0.*, so c3p0
+ // gets a chance to see all hibernate.c3p0.*
+ for ( Iterator ii = props.keySet().iterator(); ii.hasNext(); ) {
+ String key = ( String ) ii.next();
+ if ( key.startsWith( "hibernate.c3p0." ) ) {
+ String newKey = key.substring( 10 );
+ if ( props.containsKey( newKey ) ) {
+ warnPropertyConflict( key, newKey );
+ }
+ c3props.put( newKey, props.get( key ) );
+ }
+ }
+
+ setOverwriteProperty( Environment.C3P0_MIN_SIZE, C3P0_STYLE_MIN_POOL_SIZE, props, c3props, minPoolSize );
+ setOverwriteProperty( Environment.C3P0_MAX_SIZE, C3P0_STYLE_MAX_POOL_SIZE, props, c3props, maxPoolSize );
+ setOverwriteProperty( Environment.C3P0_TIMEOUT, C3P0_STYLE_MAX_IDLE_TIME, props, c3props, maxIdleTime );
+ setOverwriteProperty(
+ Environment.C3P0_MAX_STATEMENTS, C3P0_STYLE_MAX_STATEMENTS, props, c3props, maxStatements
+ );
+ setOverwriteProperty(
+ Environment.C3P0_ACQUIRE_INCREMENT, C3P0_STYLE_ACQUIRE_INCREMENT, props, c3props, acquireIncrement
+ );
+ setOverwriteProperty(
+ Environment.C3P0_IDLE_TEST_PERIOD, C3P0_STYLE_IDLE_CONNECTION_TEST_PERIOD, props, c3props, idleTestPeriod
+ );
+
+ // revert to traditional hibernate behavior of setting initialPoolSize to minPoolSize
+ // unless otherwise specified with a c3p0.*-style parameter.
+ Integer initialPoolSize = PropertiesHelper.getInteger( C3P0_STYLE_INITIAL_POOL_SIZE, props );
+ if ( initialPoolSize == null && minPoolSize != null ) {
+ c3props.put( C3P0_STYLE_INITIAL_POOL_SIZE, String.valueOf( minPoolSize ).trim() );
+ }
+
+ /*DataSource unpooled = DataSources.unpooledDataSource(
+ jdbcUrl, props.getProperty(Environment.USER), props.getProperty(Environment.PASS)
+ );*/
+ DataSource unpooled = DataSources.unpooledDataSource( jdbcUrl, connectionProps );
+
+ Properties allProps = ( Properties ) props.clone();
+ allProps.putAll( c3props );
+
+ ds = DataSources.pooledDataSource( unpooled, allProps );
+ }
+ catch ( Exception e ) {
+ log.fatal( "could not instantiate C3P0 connection pool", e );
+ throw new HibernateException( "Could not instantiate C3P0 connection pool", e );
+ }
+
+ String i = props.getProperty( Environment.ISOLATION );
+ if ( i == null ) {
+ isolation = null;
+ }
+ else {
+ isolation = new Integer( i );
+ log.info( "JDBC isolation level: " + Environment.isolationLevelToString( isolation.intValue() ) );
+ }
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void close() {
+ try {
+ DataSources.destroy( ds );
+ }
+ catch ( SQLException sqle ) {
+ log.warn( "could not destroy C3P0 connection pool", sqle );
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean supportsAggressiveRelease() {
+ return false;
+ }
+
+ private void setOverwriteProperty(String hibernateStyleKey, String c3p0StyleKey, Properties hibp, Properties c3p, Integer value) {
+ if ( value != null ) {
+ c3p.put( c3p0StyleKey, String.valueOf( value ).trim() );
+ if ( hibp.getProperty( c3p0StyleKey ) != null ) {
+ warnPropertyConflict( hibernateStyleKey, c3p0StyleKey );
+ }
+ String longC3p0StyleKey = "hibernate." + c3p0StyleKey;
+ if ( hibp.getProperty( longC3p0StyleKey ) != null ) {
+ warnPropertyConflict( hibernateStyleKey, longC3p0StyleKey );
+ }
+ }
+ }
+
+ private void warnPropertyConflict(String hibernateStyle, String c3p0Style) {
+ log.warn(
+ "Both hibernate-style property '" + hibernateStyle +
+ "' and c3p0-style property '" + c3p0Style +
+ "' have been set in hibernate.properties. " +
+ "Hibernate-style property '" + hibernateStyle + "' will be used " +
+ "and c3p0-style property '" + c3p0Style + "' will be ignored!"
+ );
+ }
+}
Added: trunk/Hibernate3/connection-proxool/pom.xml
===================================================================
--- trunk/Hibernate3/connection-proxool/pom.xml (rev 0)
+++ trunk/Hibernate3/connection-proxool/pom.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,32 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-core-project</artifactId>
+ <version>3.3.0.beta1</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-proxool</artifactId>
+ <packaging>jar</packaging>
+ <name>Hibernate Proxool ConnectionProvider</name>
+ <description>Proxool-based implementation of the Hibernate ConnectionProvder contract</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>proxool</groupId>
+ <artifactId>proxool</artifactId>
+ <version>0.8.3</version>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
Added: trunk/Hibernate3/connection-proxool/src/main/java/org/hibernate/connection/ProxoolConnectionProvider.java
===================================================================
--- trunk/Hibernate3/connection-proxool/src/main/java/org/hibernate/connection/ProxoolConnectionProvider.java (rev 0)
+++ trunk/Hibernate3/connection-proxool/src/main/java/org/hibernate/connection/ProxoolConnectionProvider.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,199 @@
+//$Id: ProxoolConnectionProvider.java 6463 2005-04-19 15:39:07Z steveebersole $
+package org.hibernate.connection;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import org.hibernate.HibernateException;
+import org.hibernate.cfg.Environment;
+import org.hibernate.util.PropertiesHelper;
+import org.hibernate.util.StringHelper;
+import org.hibernate.util.ConfigHelper;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.logicalcobwebs.proxool.ProxoolException;
+import org.logicalcobwebs.proxool.ProxoolFacade;
+import org.logicalcobwebs.proxool.configuration.JAXPConfigurator;
+import org.logicalcobwebs.proxool.configuration.PropertyConfigurator;
+
+/**
+ * A connection provider that uses a Proxool connection pool. Hibernate will use this by
+ * default if the <tt>hibernate.proxool.*</tt> properties are set.
+ * @see ConnectionProvider
+ */
+public class ProxoolConnectionProvider implements ConnectionProvider {
+
+
+ private static final String PROXOOL_JDBC_STEM = "proxool.";
+
+ private static final Log log = LogFactory.getLog(ProxoolConnectionProvider.class);
+
+ private String proxoolAlias;
+
+ // TRUE if the pool is borrowed from the outside, FALSE if we used to create it
+ private boolean existingPool;
+
+ // Not null if the Isolation level has been specified in the configuration file.
+ // Otherwise, it is left to the Driver's default value.
+ private Integer isolation;
+
+ private boolean autocommit;
+
+ /**
+ * Grab a connection
+ * @return a JDBC connection
+ * @throws SQLException
+ */
+ public Connection getConnection() throws SQLException {
+ // get a connection from the pool (thru DriverManager, cfr. Proxool doc)
+ Connection c = DriverManager.getConnection(proxoolAlias);
+
+ // set the Transaction Isolation if defined
+ if (isolation!=null) c.setTransactionIsolation( isolation.intValue() );
+
+ // toggle autoCommit to false if set
+ if ( c.getAutoCommit()!=autocommit ) c.setAutoCommit(autocommit);
+
+ // return the connection
+ return c;
+ }
+
+ /**
+ * Dispose of a used connection.
+ * @param conn a JDBC connection
+ * @throws SQLException
+ */
+ public void closeConnection(Connection conn) throws SQLException {
+ conn.close();
+ }
+
+ /**
+ * Initialize the connection provider from given properties.
+ * @param props <tt>SessionFactory</tt> properties
+ */
+ public void configure(Properties props) throws HibernateException {
+
+ // Get the configurator files (if available)
+ String jaxpFile = props.getProperty(Environment.PROXOOL_XML);
+ String propFile = props.getProperty(Environment.PROXOOL_PROPERTIES);
+ String externalConfig = props.getProperty(Environment.PROXOOL_EXISTING_POOL);
+
+ // Default the Proxool alias setting
+ proxoolAlias = props.getProperty(Environment.PROXOOL_POOL_ALIAS);
+
+ // Configured outside of Hibernate (i.e. Servlet container, or Java Bean Container
+ // already has Proxool pools running, and this provider is to just borrow one of these
+ if ( "true".equals(externalConfig) ) {
+
+ // Validate that an alias name was provided to determine which pool to use
+ if ( !StringHelper.isNotEmpty(proxoolAlias) ) {
+ String msg = "Cannot configure Proxool Provider to use an existing in memory pool without the " + Environment.PROXOOL_POOL_ALIAS + " property set.";
+ log.fatal(msg);
+ throw new HibernateException(msg);
+ }
+ // Append the stem to the proxool pool alias
+ proxoolAlias = PROXOOL_JDBC_STEM + proxoolAlias;
+
+ // Set the existing pool flag to true
+ existingPool = true;
+
+ log.info("Configuring Proxool Provider using existing pool in memory: " + proxoolAlias);
+
+ // Configured using the JAXP Configurator
+ }
+ else if ( StringHelper.isNotEmpty(jaxpFile) ) {
+
+ log.info("Configuring Proxool Provider using JAXPConfigurator: " + jaxpFile);
+
+ // Validate that an alias name was provided to determine which pool to use
+ if ( !StringHelper.isNotEmpty(proxoolAlias) ) {
+ String msg = "Cannot configure Proxool Provider to use JAXP without the " + Environment.PROXOOL_POOL_ALIAS + " property set.";
+ log.fatal(msg);
+ throw new HibernateException(msg);
+ }
+
+ try {
+ JAXPConfigurator.configure( ConfigHelper.getConfigStreamReader(jaxpFile), false );
+ }
+ catch (ProxoolException e) {
+ String msg = "Proxool Provider unable to load JAXP configurator file: " + jaxpFile;
+ log.fatal(msg, e);
+ throw new HibernateException(msg, e);
+ }
+
+ // Append the stem to the proxool pool alias
+ proxoolAlias = PROXOOL_JDBC_STEM + proxoolAlias;
+ log.info("Configuring Proxool Provider to use pool alias: " + proxoolAlias);
+
+ // Configured using the Properties File Configurator
+ }
+ else if ( StringHelper.isNotEmpty(propFile) ) {
+
+ log.info("Configuring Proxool Provider using Properties File: " + propFile);
+
+ // Validate that an alias name was provided to determine which pool to use
+ if ( !StringHelper.isNotEmpty(proxoolAlias) ) {
+ String msg = "Cannot configure Proxool Provider to use Properties File without the " + Environment.PROXOOL_POOL_ALIAS + " property set.";
+ log.fatal(msg);
+ throw new HibernateException(msg);
+ }
+
+ try {
+ PropertyConfigurator.configure( ConfigHelper.getConfigProperties(propFile) );
+ }
+ catch (ProxoolException e) {
+ String msg = "Proxool Provider unable to load load Property configurator file: " + propFile;
+ log.fatal(msg, e);
+ throw new HibernateException(msg, e);
+ }
+
+ // Append the stem to the proxool pool alias
+ proxoolAlias = PROXOOL_JDBC_STEM + proxoolAlias;
+ log.info("Configuring Proxool Provider to use pool alias: " + proxoolAlias);
+ }
+
+ // Remember Isolation level
+ isolation = PropertiesHelper.getInteger(Environment.ISOLATION, props);
+ if (isolation!=null) {
+ log.info("JDBC isolation level: " + Environment.isolationLevelToString( isolation.intValue() ) );
+ }
+
+ autocommit = PropertiesHelper.getBoolean(Environment.AUTOCOMMIT, props);
+ log.info("autocommit mode: " + autocommit);
+ }
+
+ /**
+ * Release all resources held by this provider. JavaDoc requires a second sentence.
+ * @throws HibernateException
+ */
+ public void close() throws HibernateException {
+
+ // If the provider was leeching off an existing pool don't close it
+ if (existingPool) {
+ return;
+ }
+
+ // We have created the pool ourselves, so shut it down
+ try {
+ ProxoolFacade.shutdown(0);
+ }
+ catch (Exception e) {
+ // If you're closing down the ConnectionProvider chances are an
+ // is not a real big deal, just warn
+ log.warn("Exception occured when closing the Proxool pool", e);
+ throw new HibernateException("Exception occured when closing the Proxool pool", e);
+ }
+ }
+
+ /**
+ * @see ConnectionProvider#supportsAggressiveRelease()
+ */
+ public boolean supportsAggressiveRelease() {
+ return false;
+ }
+
+}
Added: trunk/Hibernate3/core/pom.xml
===================================================================
--- trunk/Hibernate3/core/pom.xml (rev 0)
+++ trunk/Hibernate3/core/pom.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,149 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-core-project</artifactId>
+ <version>3.3.0.beta1</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-core</artifactId>
+ <packaging>jar</packaging>
+
+ <name>Hibernate Core</name>
+ <description>The core functionality of Hibernate</description>
+
+ <url>http://hibernate.org</url>
+
+ <dependencies>
+ <dependency>
+ <groupId>antlr</groupId>
+ <artifactId>antlr</artifactId>
+ <version>2.7.6</version>
+ </dependency>
+ <dependency>
+ <groupId>commons-collections</groupId>
+ <artifactId>commons-collections</artifactId>
+ <version>3.1</version>
+ </dependency>
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ <version>1.0.4</version>
+ </dependency>
+ <dependency>
+ <groupId>dom4j</groupId>
+ <artifactId>dom4j</artifactId>
+ <version>1.6.1</version>
+ </dependency>
+ <dependency>
+ <groupId>javax.transaction</groupId>
+ <artifactId>jta</artifactId>
+ <version>1.1</version>
+ </dependency>
+ <dependency>
+ <groupId>javax.security</groupId>
+ <artifactId>jaas</artifactId>
+ <version>1.0.01</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>javax.security</groupId>
+ <artifactId>jacc</artifactId>
+ <version>1.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>ant</groupId>
+ <artifactId>ant</artifactId>
+ <version>1.6.5</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <!-- optional deps for bytecode providers until those are finally properly scoped -->
+ <dependency>
+ <groupId>javassist</groupId>
+ <artifactId>javassist</artifactId>
+ <version>3.4.GA</version>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
+ <groupId>cglib</groupId>
+ <artifactId>cglib</artifactId>
+ <version>2.1_3</version>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
+ <groupId>asm</groupId>
+ <artifactId>asm-attrs</artifactId>
+ <version>1.5.3</version>
+ <optional>true</optional>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-antlr-plugin</artifactId>
+ <configuration>
+ <grammars>hql.g,hql-sql.g,sql-gen.g</grammars>
+ </configuration>
+ <executions>
+ <execution>
+ <goals>
+ <goal>generate</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+ <reporting>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-antlr-plugin</artifactId>
+ <configuration>
+ <!-- eventually should be based on the second phase grammar -->
+ <grammars>hql.g</grammars>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <configuration>
+ <!--
+ for the time being, gonna ignore the custom stylesheet (what did it do anyway???)
+ <stylesheetfile>xyz</stylesheetfile>
+ -->
+ <groups>
+ <group>
+ <title>Core API</title>
+ <packages>org.hibernate:org.hibernate.classic:org.hibernate.criterion:org.hibernate.metadata:org.hibernate.cfg:org.hibernate.usertype</packages>
+ </group>
+ <group>
+ <title>Extension API</title>
+ <packages>org.hibernate.id:org.hibernate.connection:org.hibernate.transaction:org.hibernate.type:org.hibernate.dialect*:org.hibernate.cache*:org.hibernate.event*:org.hibernate.action:org.hibernate.property:org.hibernate.loader*:org.hibernate.persister*:org.hibernate.proxy:org.hibernate.tuple:org.hibernate.transform:org.hibernate.collection:org.hibernate.jdbc</packages>
+ </group>
+ <group>
+ <title>Miscellaneous API</title>
+ <packages>org.hibernate.stat:org.hibernate.tool.hbm2ddl:org.hibernate.jmx:org.hibernate.mapping:org.hibernate.tool.instrument</packages>
+ </group>
+ <group>
+ <title>Internal Implementation</title>
+ <packages>org.hibernate.engine:org.hibernate.impl:org.hibernate.sql:org.hibernate.lob:org.hibernate.util:org.hibernate.exception:org.hibernate.hql:org.hibernate.hql.ast:org.hibernate.hql.antlr:org.hibernate.hql.classic:org.hibernate.intercept:org.hibernate.secure:org.hibernate.pretty</[packages>
+ </group>
+ </groups>
+ </configuration>
+ </plugin>
+ </plugins>
+ </reporting>
+
+</project>
Added: trunk/Hibernate3/core/src/main/antlr/hql-sql.g
===================================================================
--- trunk/Hibernate3/core/src/main/antlr/hql-sql.g (rev 0)
+++ trunk/Hibernate3/core/src/main/antlr/hql-sql.g 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,699 @@
+header
+{
+// $Id: hql-sql.g 10001 2006-06-08 21:08:04Z steve.ebersole at jboss.com $
+package org.hibernate.hql.antlr;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+}
+
+/**
+ * Hibernate Query Language to SQL Tree Transform.<br>
+ * This is a tree grammar that transforms an HQL AST into a intermediate SQL AST
+ * with bindings to Hibernate interfaces (Queryable, etc.). The Hibernate specific methods
+ * are all implemented in the HqlSqlWalker subclass, allowing the ANTLR-generated class
+ * to have only the minimum dependencies on the Hibernate code base. This will also allow
+ * the sub-class to be easily edited using an IDE (most IDE's don't support ANTLR).
+ * <br>
+ * <i>NOTE:</i> The java class is generated from hql-sql.g by ANTLR.
+ * <i>DO NOT EDIT THE GENERATED JAVA SOURCE CODE.</i>
+ * @author Joshua Davis (joshua at hibernate.org)
+ */
+class HqlSqlBaseWalker extends TreeParser;
+
+options
+{
+ // Note: importVocab and exportVocab cause ANTLR to share the token type numbers between the
+ // two grammars. This means that the token type constants from the source tree are the same
+ // as those in the target tree. If this is not the case, tree translation can result in
+ // token types from the *source* tree being present in the target tree.
+ importVocab=Hql; // import definitions from "Hql"
+ exportVocab=HqlSql; // Call the resulting definitions "HqlSql"
+ buildAST=true;
+}
+
+tokens
+{
+ FROM_FRAGMENT; // A fragment of SQL that represents a table reference in a FROM clause.
+ IMPLIED_FROM; // An implied FROM element.
+ JOIN_FRAGMENT; // A JOIN fragment.
+ SELECT_CLAUSE;
+ LEFT_OUTER;
+ RIGHT_OUTER;
+ ALIAS_REF; // An IDENT that is a reference to an entity via it's alias.
+ PROPERTY_REF; // A DOT that is a reference to a property in an entity.
+ SQL_TOKEN; // A chunk of SQL that is 'rendered' already.
+ SELECT_COLUMNS; // A chunk of SQL representing a bunch of select columns.
+ SELECT_EXPR; // A select expression, generated from a FROM element.
+ THETA_JOINS; // Root of theta join condition subtree.
+ FILTERS; // Root of the filters condition subtree.
+ METHOD_NAME; // An IDENT that is a method name.
+ NAMED_PARAM; // A named parameter (:foo).
+ BOGUS; // Used for error state detection, etc.
+}
+
+// -- Declarations --
+{
+ private static Log log = LogFactory.getLog( HqlSqlBaseWalker.class );
+
+ private int level = 0;
+ private boolean inSelect = false;
+ private boolean inFunctionCall = false;
+ private boolean inCase = false;
+ private boolean inFrom = false;
+ private int statementType;
+ private String statementTypeName;
+ // Note: currentClauseType tracks the current clause within the current
+ // statement, regardless of level; currentTopLevelClauseType, on the other
+ // hand, tracks the current clause within the top (or primary) statement.
+ // Thus, currentTopLevelClauseType ignores the clauses from any subqueries.
+ private int currentClauseType;
+ private int currentTopLevelClauseType;
+ private int currentStatementType;
+
+ public final boolean isSubQuery() {
+ return level > 1;
+ }
+
+ public final boolean isInFrom() {
+ return inFrom;
+ }
+
+ public final boolean isInFunctionCall() {
+ return inFunctionCall;
+ }
+
+ public final boolean isInSelect() {
+ return inSelect;
+ }
+
+ public final boolean isInCase() {
+ return inCase;
+ }
+
+ public final int getStatementType() {
+ return statementType;
+ }
+
+ public final int getCurrentClauseType() {
+ return currentClauseType;
+ }
+
+ public final int getCurrentTopLevelClauseType() {
+ return currentTopLevelClauseType;
+ }
+
+ public final int getCurrentStatementType() {
+ return currentStatementType;
+ }
+
+ public final boolean isComparativeExpressionClause() {
+ // Note: once we add support for "JOIN ... ON ...",
+ // the ON clause needs to get included here
+ return getCurrentClauseType() == WHERE ||
+ getCurrentClauseType() == WITH ||
+ isInCase();
+ }
+
+ public final boolean isSelectStatement() {
+ return statementType == SELECT;
+ }
+
+ private void beforeStatement(String statementName, int statementType) {
+ inFunctionCall = false;
+ level++;
+ if ( level == 1 ) {
+ this.statementTypeName = statementName;
+ this.statementType = statementType;
+ }
+ currentStatementType = statementType;
+ if ( log.isDebugEnabled() ) {
+ log.debug( statementName + " << begin [level=" + level + ", statement=" + this.statementTypeName + "]" );
+ }
+ }
+
+ private void beforeStatementCompletion(String statementName) {
+ if ( log.isDebugEnabled() ) {
+ log.debug( statementName + " : finishing up [level=" + level + ", statement=" + statementTypeName + "]" );
+ }
+ }
+
+ private void afterStatementCompletion(String statementName) {
+ if ( log.isDebugEnabled() ) {
+ log.debug( statementName + " >> end [level=" + level + ", statement=" + statementTypeName + "]" );
+ }
+ level--;
+ }
+
+ private void handleClauseStart(int clauseType) {
+ currentClauseType = clauseType;
+ if ( level == 1 ) {
+ currentTopLevelClauseType = clauseType;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // NOTE: The real implementations for the following are in the subclass.
+
+ protected void evaluateAssignment(AST eq) throws SemanticException { }
+
+ /** Pre-process the from clause input tree. **/
+ protected void prepareFromClauseInputTree(AST fromClauseInput) {}
+
+ /** Sets the current 'FROM' context. **/
+ protected void pushFromClause(AST fromClause,AST inputFromNode) {}
+
+ protected AST createFromElement(String path,AST alias,AST propertyFetch) throws SemanticException {
+ return null;
+ }
+
+ protected void createFromJoinElement(AST path,AST alias,int joinType,AST fetch,AST propertyFetch,AST with) throws SemanticException {}
+
+ protected AST createFromFilterElement(AST filterEntity,AST alias) throws SemanticException {
+ return null;
+ }
+
+ protected void processQuery(AST select,AST query) throws SemanticException { }
+
+ protected void postProcessUpdate(AST update) throws SemanticException { }
+
+ protected void postProcessDelete(AST delete) throws SemanticException { }
+
+ protected void postProcessInsert(AST insert) throws SemanticException { }
+
+ protected void beforeSelectClause() throws SemanticException { }
+
+ protected void processIndex(AST indexOp) throws SemanticException { }
+
+ protected void processConstant(AST constant) throws SemanticException { }
+
+ protected void processBoolean(AST constant) throws SemanticException { }
+
+ protected void processNumericLiteral(AST literal) throws SemanticException { }
+
+ protected void resolve(AST node) throws SemanticException { }
+
+ protected void resolveSelectExpression(AST dotNode) throws SemanticException { }
+
+ protected void processFunction(AST functionCall,boolean inSelect) throws SemanticException { }
+
+ protected void processConstructor(AST constructor) throws SemanticException { }
+
+ protected AST generateNamedParameter(AST delimiterNode, AST nameNode) throws SemanticException {
+ return #( [NAMED_PARAM, nameNode.getText()] );
+ }
+
+ protected AST generatePositionalParameter(AST inputNode) throws SemanticException {
+ return #( [PARAM, "?"] );
+ }
+
+ protected void lookupAlias(AST ident) throws SemanticException { }
+
+ protected void setAlias(AST selectExpr, AST ident) { }
+
+ protected AST lookupProperty(AST dot,boolean root,boolean inSelect) throws SemanticException {
+ return dot;
+ }
+
+ protected boolean isNonQualifiedPropertyRef(AST ident) { return false; }
+
+ protected AST lookupNonQualifiedProperty(AST property) throws SemanticException { return property; }
+
+ protected void setImpliedJoinType(int joinType) { }
+
+ protected AST createIntoClause(String path, AST propertySpec) throws SemanticException {
+ return null;
+ };
+
+ protected void prepareVersioned(AST updateNode, AST versionedNode) throws SemanticException {}
+
+ protected void prepareLogicOperator(AST operator) throws SemanticException { }
+
+ protected void prepareArithmeticOperator(AST operator) throws SemanticException { }
+}
+
+// The main statement rule.
+statement
+ : selectStatement | updateStatement | deleteStatement | insertStatement
+ ;
+
+selectStatement
+ : query
+ ;
+
+// Cannot use just the fromElement rule here in the update and delete queries
+// because fromElement essentially relies on a FromClause already having been
+// built :(
+updateStatement!
+ : #( u:UPDATE { beforeStatement( "update", UPDATE ); } (v:VERSIONED)? f:fromClause s:setClause (w:whereClause)? ) {
+ #updateStatement = #(#u, #f, #s, #w);
+ beforeStatementCompletion( "update" );
+ prepareVersioned( #updateStatement, #v );
+ postProcessUpdate( #updateStatement );
+ afterStatementCompletion( "update" );
+ }
+ ;
+
+deleteStatement
+ : #( DELETE { beforeStatement( "delete", DELETE ); } fromClause (whereClause)? ) {
+ beforeStatementCompletion( "delete" );
+ postProcessDelete( #deleteStatement );
+ afterStatementCompletion( "delete" );
+ }
+ ;
+
+insertStatement
+ // currently only "INSERT ... SELECT ..." statements supported;
+ // do we also need support for "INSERT ... VALUES ..."?
+ //
+ : #( INSERT { beforeStatement( "insert", INSERT ); } intoClause query ) {
+ beforeStatementCompletion( "insert" );
+ postProcessInsert( #insertStatement );
+ afterStatementCompletion( "insert" );
+ }
+ ;
+
+intoClause! {
+ String p = null;
+ }
+ : #( INTO { handleClauseStart( INTO ); } (p=path) ps:insertablePropertySpec ) {
+ #intoClause = createIntoClause(p, ps);
+ }
+ ;
+
+insertablePropertySpec
+ : #( RANGE (IDENT)+ )
+ ;
+
+setClause
+ : #( SET { handleClauseStart( SET ); } (assignment)* )
+ ;
+
+assignment
+ // Note: the propertyRef here needs to be resolved
+ // *before* we evaluate the newValue rule...
+ : #( EQ (p:propertyRef) { resolve(#p); } (newValue) ) {
+ evaluateAssignment( #assignment );
+ }
+ ;
+
+// For now, just use expr. Revisit after ejb3 solidifies this.
+newValue
+ : expr | query
+ ;
+
+// The query / subquery rule. Pops the current 'from node' context
+// (list of aliases).
+query!
+ : #( QUERY { beforeStatement( "select", SELECT ); }
+ // The first phase places the FROM first to make processing the SELECT simpler.
+ #(SELECT_FROM
+ f:fromClause
+ (s:selectClause)?
+ )
+ (w:whereClause)?
+ (g:groupClause)?
+ (o:orderClause)?
+ ) {
+ // Antlr note: #x_in refers to the input AST, #x refers to the output AST
+ #query = #([SELECT,"SELECT"], #s, #f, #w, #g, #o);
+ beforeStatementCompletion( "select" );
+ processQuery( #s, #query );
+ afterStatementCompletion( "select" );
+ }
+ ;
+
+orderClause
+ : #(ORDER { handleClauseStart( ORDER ); } orderExprs)
+ ;
+
+orderExprs
+ : expr ( ASCENDING | DESCENDING )? (orderExprs)?
+ ;
+
+groupClause
+ : #(GROUP { handleClauseStart( GROUP ); } (expr)+ ( #(HAVING logicalExpr) )? )
+ ;
+
+selectClause!
+ : #(SELECT { handleClauseStart( SELECT ); beforeSelectClause(); } (d:DISTINCT)? x:selectExprList ) {
+ #selectClause = #([SELECT_CLAUSE,"{select clause}"], #d, #x);
+ }
+ ;
+
+selectExprList {
+ boolean oldInSelect = inSelect;
+ inSelect = true;
+ }
+ : ( selectExpr | aliasedSelectExpr )+ {
+ inSelect = oldInSelect;
+ }
+ ;
+
+aliasedSelectExpr!
+ : #(AS se:selectExpr i:identifier) {
+ setAlias(#se,#i);
+ #aliasedSelectExpr = #se;
+ }
+ ;
+
+selectExpr
+ : p:propertyRef { resolveSelectExpression(#p); }
+ | #(ALL ar2:aliasRef) { resolveSelectExpression(#ar2); #selectExpr = #ar2; }
+ | #(OBJECT ar3:aliasRef) { resolveSelectExpression(#ar3); #selectExpr = #ar3; }
+ | con:constructor { processConstructor(#con); }
+ | functionCall
+ | count
+ | collectionFunction // elements() or indices()
+ | literal
+ | arithmeticExpr
+ | query
+ ;
+
+count
+ : #(COUNT ( DISTINCT | ALL )? ( aggregateExpr | ROW_STAR ) )
+ ;
+
+constructor
+ { String className = null; }
+ : #(CONSTRUCTOR className=path ( selectExpr | aliasedSelectExpr )* )
+ ;
+
+aggregateExpr
+ : expr //p:propertyRef { resolve(#p); }
+ | collectionFunction
+ ;
+
+// Establishes the list of aliases being used by this query.
+fromClause {
+ // NOTE: This references the INPUT AST! (see http://www.antlr.org/doc/trees.html#Action%20Translation)
+ // the ouput AST (#fromClause) has not been built yet.
+ prepareFromClauseInputTree(#fromClause_in);
+ }
+ : #(f:FROM { pushFromClause(#fromClause,f); handleClauseStart( FROM ); } fromElementList )
+ ;
+
+fromElementList {
+ boolean oldInFrom = inFrom;
+ inFrom = true;
+ }
+ : (fromElement)+ {
+ inFrom = oldInFrom;
+ }
+ ;
+
+fromElement! {
+ String p = null;
+ }
+ // A simple class name, alias element.
+ : #(RANGE p=path (a:ALIAS)? (pf:FETCH)? ) {
+ #fromElement = createFromElement(p,a, pf);
+ }
+ | je:joinElement {
+ #fromElement = #je;
+ }
+ // A from element created due to filter compilation
+ | fe:FILTER_ENTITY a3:ALIAS {
+ #fromElement = createFromFilterElement(fe,a3);
+ }
+ ;
+
+joinElement! {
+ int j = INNER;
+ }
+ // A from element with a join. This time, the 'path' should be treated as an AST
+ // and resolved (like any path in a WHERE clause). Make sure all implied joins
+ // generated by the property ref use the join type, if it was specified.
+ : #(JOIN (j=joinType { setImpliedJoinType(j); } )? (f:FETCH)? ref:propertyRef (a:ALIAS)? (pf:FETCH)? (with:WITH)? ) {
+ //createFromJoinElement(#ref,a,j,f, pf);
+ createFromJoinElement(#ref,a,j,f, pf, with);
+ setImpliedJoinType(INNER); // Reset the implied join type.
+ }
+ ;
+
+// Returns an node type integer that represents the join type
+// tokens.
+joinType returns [int j] {
+ j = INNER;
+ }
+ : ( (left:LEFT | right:RIGHT) (outer:OUTER)? ) {
+ if (left != null) j = LEFT_OUTER;
+ else if (right != null) j = RIGHT_OUTER;
+ else if (outer != null) j = RIGHT_OUTER;
+ }
+ | FULL {
+ j = FULL;
+ }
+ | INNER {
+ j = INNER;
+ }
+ ;
+
+// Matches a path and returns the normalized string for the path (usually
+// fully qualified a class name).
+path returns [String p] {
+ p = "???";
+ String x = "?x?";
+ }
+ : a:identifier { p = a.getText(); }
+ | #(DOT x=path y:identifier) {
+ StringBuffer buf = new StringBuffer();
+ buf.append(x).append(".").append(y.getText());
+ p = buf.toString();
+ }
+ ;
+
+// Returns a path as a single identifier node.
+pathAsIdent {
+ String text = "?text?";
+ }
+ : text=path {
+ #pathAsIdent = #([IDENT,text]);
+ }
+ ;
+
+withClause
+ // Note : this is used internally from the HqlSqlWalker to
+ // parse the node recognized with the with keyword earlier.
+ // Done this way because it relies on the join it "qualifies"
+ // already having been processed, which would not be the case
+ // if withClause was simply referenced from the joinElement
+ // rule during recognition...
+ : #(w:WITH { handleClauseStart( WITH ); } b:logicalExpr ) {
+ #withClause = #(w , #b);
+ }
+ ;
+
+whereClause
+ : #(w:WHERE { handleClauseStart( WHERE ); } b:logicalExpr ) {
+ // Use the *output* AST for the boolean expression!
+ #whereClause = #(w , #b);
+ }
+ ;
+
+logicalExpr
+ : #(AND logicalExpr logicalExpr)
+ | #(OR logicalExpr logicalExpr)
+ | #(NOT logicalExpr)
+ | comparisonExpr
+ ;
+
+// TODO: Add any other comparison operators here.
+comparisonExpr
+ :
+ ( #(EQ exprOrSubquery exprOrSubquery)
+ | #(NE exprOrSubquery exprOrSubquery)
+ | #(LT exprOrSubquery exprOrSubquery)
+ | #(GT exprOrSubquery exprOrSubquery)
+ | #(LE exprOrSubquery exprOrSubquery)
+ | #(GE exprOrSubquery exprOrSubquery)
+ | #(LIKE exprOrSubquery expr ( #(ESCAPE expr) )? )
+ | #(NOT_LIKE exprOrSubquery expr ( #(ESCAPE expr) )? )
+ | #(BETWEEN exprOrSubquery exprOrSubquery exprOrSubquery)
+ | #(NOT_BETWEEN exprOrSubquery exprOrSubquery exprOrSubquery)
+ | #(IN exprOrSubquery inRhs )
+ | #(NOT_IN exprOrSubquery inRhs )
+ | #(IS_NULL exprOrSubquery)
+ | #(IS_NOT_NULL exprOrSubquery)
+// | #(IS_TRUE expr)
+// | #(IS_FALSE expr)
+ | #(EXISTS ( expr | collectionFunctionOrSubselect ) )
+ ) {
+ prepareLogicOperator( #comparisonExpr );
+ }
+ ;
+
+inRhs
+ : #(IN_LIST ( collectionFunctionOrSubselect | ( (expr)* ) ) )
+ ;
+
+exprOrSubquery
+ : expr
+ | query
+ | #(ANY collectionFunctionOrSubselect)
+ | #(ALL collectionFunctionOrSubselect)
+ | #(SOME collectionFunctionOrSubselect)
+ ;
+
+collectionFunctionOrSubselect
+ : collectionFunction
+ | query
+ ;
+
+expr
+ : ae:addrExpr [ true ] { resolve(#ae); } // Resolve the top level 'address expression'
+ | #( VECTOR_EXPR (expr)* )
+ | constant
+ | arithmeticExpr
+ | functionCall // Function call, not in the SELECT clause.
+ | parameter
+ | count // Count, not in the SELECT clause.
+ ;
+
+arithmeticExpr
+ : #(PLUS expr expr) { prepareArithmeticOperator( #arithmeticExpr ); }
+ | #(MINUS expr expr) { prepareArithmeticOperator( #arithmeticExpr ); }
+ | #(DIV expr expr) { prepareArithmeticOperator( #arithmeticExpr ); }
+ | #(STAR expr expr) { prepareArithmeticOperator( #arithmeticExpr ); }
+// | #(CONCAT expr (expr)+ ) { prepareArithmeticOperator( #arithmeticExpr ); }
+ | #(UNARY_MINUS expr) { prepareArithmeticOperator( #arithmeticExpr ); }
+ | caseExpr
+ ;
+
+caseExpr
+ : #(CASE { inCase = true; } (#(WHEN logicalExpr expr))+ (#(ELSE expr))?) { inCase = false; }
+ | #(CASE2 { inCase = true; } expr (#(WHEN expr expr))+ (#(ELSE expr))?) { inCase = false; }
+ ;
+
+//TODO: I don't think we need this anymore .. how is it different to
+// maxelements, etc, which are handled by functionCall
+collectionFunction
+ : #(e:ELEMENTS {inFunctionCall=true;} p1:propertyRef { resolve(#p1); } )
+ { processFunction(#e,inSelect); } {inFunctionCall=false;}
+ | #(i:INDICES {inFunctionCall=true;} p2:propertyRef { resolve(#p2); } )
+ { processFunction(#i,inSelect); } {inFunctionCall=false;}
+ ;
+
+functionCall
+ : #(METHOD_CALL {inFunctionCall=true;} pathAsIdent ( #(EXPR_LIST (expr)* ) )? )
+ { processFunction(#functionCall,inSelect); } {inFunctionCall=false;}
+ | #(AGGREGATE aggregateExpr )
+ ;
+
+constant
+ : literal
+ | NULL
+ | TRUE { processBoolean(#constant); }
+ | FALSE { processBoolean(#constant); }
+ | JAVA_CONSTANT
+ ;
+
+literal
+ : NUM_INT { processNumericLiteral( #literal ); }
+ | NUM_LONG { processNumericLiteral( #literal ); }
+ | NUM_FLOAT { processNumericLiteral( #literal ); }
+ | NUM_DOUBLE { processNumericLiteral( #literal ); }
+ | QUOTED_STRING
+ ;
+
+identifier
+ : (IDENT | WEIRD_IDENT)
+ ;
+
+addrExpr! [ boolean root ]
+ : #(d:DOT lhs:addrExprLhs rhs:propertyName ) {
+ // This gives lookupProperty() a chance to transform the tree
+ // to process collection properties (.elements, etc).
+ #addrExpr = #(#d, #lhs, #rhs);
+ #addrExpr = lookupProperty(#addrExpr,root,false);
+ }
+ | #(i:INDEX_OP lhs2:addrExprLhs rhs2:expr) {
+ #addrExpr = #(#i, #lhs2, #rhs2);
+ processIndex(#addrExpr);
+ }
+ | p:identifier {
+// #addrExpr = #p;
+// resolve(#addrExpr);
+ // In many cases, things other than property-refs are recognized
+ // by this addrExpr rule. Some of those I have seen:
+ // 1) select-clause from-aliases
+ // 2) sql-functions
+ if ( isNonQualifiedPropertyRef(#p) ) {
+ #addrExpr = lookupNonQualifiedProperty(#p);
+ }
+ else {
+ resolve(#p);
+ #addrExpr = #p;
+ }
+ }
+ ;
+
+addrExprLhs
+ : addrExpr [ false ]
+ ;
+
+propertyName
+ : identifier
+ | CLASS
+ | ELEMENTS
+ | INDICES
+ ;
+
+propertyRef!
+ : #(d:DOT lhs:propertyRefLhs rhs:propertyName ) {
+ // This gives lookupProperty() a chance to transform the tree to process collection properties (.elements, etc).
+ #propertyRef = #(#d, #lhs, #rhs);
+ #propertyRef = lookupProperty(#propertyRef,false,true);
+ }
+ |
+ p:identifier {
+ // In many cases, things other than property-refs are recognized
+ // by this propertyRef rule. Some of those I have seen:
+ // 1) select-clause from-aliases
+ // 2) sql-functions
+ if ( isNonQualifiedPropertyRef(#p) ) {
+ #propertyRef = lookupNonQualifiedProperty(#p);
+ }
+ else {
+ resolve(#p);
+ #propertyRef = #p;
+ }
+ }
+ ;
+
+propertyRefLhs
+ : propertyRef
+ ;
+
+aliasRef!
+ : i:identifier {
+ #aliasRef = #([ALIAS_REF,i.getText()]); // Create an ALIAS_REF node instead of an IDENT node.
+ lookupAlias(#aliasRef);
+ }
+ ;
+
+parameter!
+ : #(c:COLON a:identifier) {
+ // Create a NAMED_PARAM node instead of (COLON IDENT).
+ #parameter = generateNamedParameter( c, a );
+// #parameter = #([NAMED_PARAM,a.getText()]);
+// namedParameter(#parameter);
+ }
+ | #(p:PARAM (n:NUM_INT)?) {
+ if ( n != null ) {
+ // An ejb3-style "positional parameter", which we handle internally as a named-param
+ #parameter = generateNamedParameter( p, n );
+// #parameter = #([NAMED_PARAM,n.getText()]);
+// namedParameter(#parameter);
+ }
+ else {
+ #parameter = generatePositionalParameter( p );
+// #parameter = #([PARAM,"?"]);
+// positionalParameter(#parameter);
+ }
+ }
+ ;
+
+numericInteger
+ : NUM_INT
+ ;
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/antlr/hql.g
===================================================================
--- trunk/Hibernate3/core/src/main/antlr/hql.g (rev 0)
+++ trunk/Hibernate3/core/src/main/antlr/hql.g 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,883 @@
+header
+{
+// $Id: hql.g 10163 2006-07-26 15:07:50Z steve.ebersole at jboss.com $
+
+package org.hibernate.hql.antlr;
+
+import org.hibernate.hql.ast.*;
+import org.hibernate.hql.ast.util.*;
+
+}
+/**
+ * Hibernate Query Language Grammar
+ * <br>
+ * This grammar parses the query language for Hibernate (an Open Source, Object-Relational
+ * mapping library). A partial BNF grammar description is available for reference here:
+ * http://www.hibernate.org/Documentation/HQLBNF
+ *
+ * Text from the original reference BNF is prefixed with '//##'.
+ * @author Joshua Davis (pgmjsd at sourceforge.net)
+ */
+class HqlBaseParser extends Parser;
+
+options
+{
+ exportVocab=Hql;
+ buildAST=true;
+ k=3; // For 'not like', 'not in', etc.
+}
+
+tokens
+{
+ // -- HQL Keyword tokens --
+ ALL="all";
+ ANY="any";
+ AND="and";
+ AS="as";
+ ASCENDING="asc";
+ AVG="avg";
+ BETWEEN="between";
+ CLASS="class";
+ COUNT="count";
+ DELETE="delete";
+ DESCENDING="desc";
+ DOT;
+ DISTINCT="distinct";
+ ELEMENTS="elements";
+ ESCAPE="escape";
+ EXISTS="exists";
+ FALSE="false";
+ FETCH="fetch";
+ FROM="from";
+ FULL="full";
+ GROUP="group";
+ HAVING="having";
+ IN="in";
+ INDICES="indices";
+ INNER="inner";
+ INSERT="insert";
+ INTO="into";
+ IS="is";
+ JOIN="join";
+ LEFT="left";
+ LIKE="like";
+ MAX="max";
+ MIN="min";
+ NEW="new";
+ NOT="not";
+ NULL="null";
+ OR="or";
+ ORDER="order";
+ OUTER="outer";
+ PROPERTIES="properties";
+ RIGHT="right";
+ SELECT="select";
+ SET="set";
+ SOME="some";
+ SUM="sum";
+ TRUE="true";
+ UNION="union";
+ UPDATE="update";
+ VERSIONED="versioned";
+ WHERE="where";
+
+ // -- SQL tokens --
+ // These aren't part of HQL, but the SQL fragment parser uses the HQL lexer, so they need to be declared here.
+ CASE="case";
+ END="end";
+ ELSE="else";
+ THEN="then";
+ WHEN="when";
+ ON="on";
+ WITH="with";
+
+ // -- EJBQL tokens --
+ BOTH="both";
+ EMPTY="empty";
+ LEADING="leading";
+ MEMBER="member";
+ OBJECT="object";
+ OF="of";
+ TRAILING="trailing";
+
+ // -- Synthetic token types --
+ AGGREGATE; // One of the aggregate functions (e.g. min, max, avg)
+ ALIAS;
+ CONSTRUCTOR;
+ CASE2;
+ EXPR_LIST;
+ FILTER_ENTITY; // FROM element injected because of a filter expression (happens during compilation phase 2)
+ IN_LIST;
+ INDEX_OP;
+ IS_NOT_NULL;
+ IS_NULL; // Unary 'is null' operator.
+ METHOD_CALL;
+ NOT_BETWEEN;
+ NOT_IN;
+ NOT_LIKE;
+ ORDER_ELEMENT;
+ QUERY;
+ RANGE;
+ ROW_STAR;
+ SELECT_FROM;
+ UNARY_MINUS;
+ UNARY_PLUS;
+ VECTOR_EXPR; // ( x, y, z )
+ WEIRD_IDENT; // Identifiers that were keywords when they came in.
+
+ // Literal tokens.
+ CONSTANT;
+ NUM_DOUBLE;
+ NUM_FLOAT;
+ NUM_LONG;
+ JAVA_CONSTANT;
+}
+
+{
+ /** True if this is a filter query (allow no FROM clause). **/
+ private boolean filter = false;
+
+ /**
+ * Sets the filter flag.
+ * @param f True for a filter query, false for a normal query.
+ */
+ public void setFilter(boolean f) {
+ filter = f;
+ }
+
+ /**
+ * Returns true if this is a filter query, false if not.
+ * @return true if this is a filter query, false if not.
+ */
+ public boolean isFilter() {
+ return filter;
+ }
+
+ /**
+ * This method is overriden in the sub class in order to provide the
+ * 'keyword as identifier' hack.
+ * @param token The token to retry as an identifier.
+ * @param ex The exception to throw if it cannot be retried as an identifier.
+ */
+ public AST handleIdentifierError(Token token,RecognitionException ex) throws RecognitionException, TokenStreamException {
+ // Base implementation: Just re-throw the exception.
+ throw ex;
+ }
+
+ /**
+ * This method looks ahead and converts . <token> into . IDENT when
+ * appropriate.
+ */
+ public void handleDotIdent() throws TokenStreamException {
+ }
+
+ /**
+ * Returns the negated equivalent of the expression.
+ * @param x The expression to negate.
+ */
+ public AST negateNode(AST x) {
+ // Just create a 'not' parent for the default behavior.
+ return ASTUtil.createParent(astFactory, NOT, "not", x);
+ }
+
+ /**
+ * Returns the 'cleaned up' version of a comparison operator sub-tree.
+ * @param x The comparison operator to clean up.
+ */
+ public AST processEqualityExpression(AST x) throws RecognitionException {
+ return x;
+ }
+
+ public void weakKeywords() throws TokenStreamException { }
+
+ public void processMemberOf(Token n,AST p,ASTPair currentAST) { }
+
+}
+
+statement
+ : ( updateStatement | deleteStatement | selectStatement | insertStatement )
+ ;
+
+updateStatement
+ : UPDATE^ (VERSIONED)?
+ optionalFromTokenFromClause
+ setClause
+ (whereClause)?
+ ;
+
+setClause
+ : (SET^ assignment (COMMA! assignment)*)
+ ;
+
+assignment
+ : stateField EQ^ newValue
+ ;
+
+// "state_field" is the term used in the EJB3 sample grammar; used here for easy reference.
+// it is basically a property ref
+stateField
+ : path
+ ;
+
+// this still needs to be defined in the ejb3 spec; additiveExpression is currently just a best guess,
+// although it is highly likely I would think that the spec may limit this even more tightly.
+newValue
+ : concatenation
+ ;
+
+deleteStatement
+ : DELETE^
+ (optionalFromTokenFromClause)
+ (whereClause)?
+ ;
+
+optionalFromTokenFromClause!
+ : (FROM!)? f:path (a:asAlias)? {
+ AST #range = #([RANGE, "RANGE"], #f, #a);
+ #optionalFromTokenFromClause = #([FROM, "FROM"], #range);
+ }
+ ;
+
+selectStatement
+ : queryRule {
+ #selectStatement = #([QUERY,"query"], #selectStatement);
+ }
+ ;
+
+insertStatement
+ // Would be nice if we could abstract the FromClause/FromElement logic
+ // out such that it could be reused here; something analogous to
+ // a "table" rule in sql-grammars
+ : INSERT^ intoClause selectStatement
+ ;
+
+intoClause
+ : INTO^ path { weakKeywords(); } insertablePropertySpec
+ ;
+
+insertablePropertySpec
+ : OPEN! primaryExpression ( COMMA! primaryExpression )* CLOSE! {
+ // Just need *something* to distinguish this on the hql-sql.g side
+ #insertablePropertySpec = #([RANGE, "column-spec"], #insertablePropertySpec);
+ }
+ ;
+
+union
+ : queryRule (UNION queryRule)*
+ ;
+
+//## query:
+//## [selectClause] fromClause [whereClause] [groupByClause] [havingClause] [orderByClause];
+
+queryRule
+ : selectFrom
+ (whereClause)?
+ (groupByClause)?
+ (orderByClause)?
+ ;
+
+selectFrom!
+ : (s:selectClause)? (f:fromClause)? {
+ // If there was no FROM clause and this is a filter query, create a from clause. Otherwise, throw
+ // an exception because non-filter queries must have a FROM clause.
+ if (#f == null) {
+ if (filter) {
+ #f = #([FROM,"{filter-implied FROM}"]);
+ }
+ else
+ throw new SemanticException("FROM expected (non-filter queries must contain a FROM clause)");
+ }
+
+ // Create an artificial token so the 'FROM' can be placed
+ // before the SELECT in the tree to make tree processing
+ // simpler.
+ #selectFrom = #([SELECT_FROM,"SELECT_FROM"],f,s);
+ }
+ ;
+
+//## selectClause:
+//## SELECT DISTINCT? selectedPropertiesList | ( NEW className OPEN selectedPropertiesList CLOSE );
+
+selectClause
+ : SELECT^ // NOTE: The '^' after a token causes the corresponding AST node to be the root of the sub-tree.
+ { weakKeywords(); } // Weak keywords can appear immediately after a SELECT token.
+ (DISTINCT)? ( selectedPropertiesList | newExpression | selectObject )
+ ;
+
+newExpression
+ : (NEW! path) op:OPEN^ {#op.setType(CONSTRUCTOR);} selectedPropertiesList CLOSE!
+ ;
+
+selectObject
+ : OBJECT^ OPEN! identifier CLOSE!
+ ;
+
+//## fromClause:
+//## FROM className AS? identifier ( ( COMMA className AS? identifier ) | ( joinType path AS? identifier ) )*;
+
+// NOTE: This *must* begin with the "FROM" token, otherwise the sub-query rule will be ambiguous
+// with the expression rule.
+// Also note: after a comma weak keywords are allowed and should be treated as identifiers.
+
+fromClause
+ : FROM^ { weakKeywords(); } fromRange ( fromJoin | COMMA! { weakKeywords(); } fromRange )*
+ ;
+
+//## joinType:
+//## ( ( 'left'|'right' 'outer'? ) | 'full' | 'inner' )? JOIN FETCH?;
+
+fromJoin
+ : ( ( ( LEFT | RIGHT ) (OUTER)? ) | FULL | INNER )? JOIN^ (FETCH)?
+ path (asAlias)? (propertyFetch)? (withClause)?
+ ;
+
+withClause
+ : WITH^ logicalExpression
+ ;
+
+fromRange
+ : fromClassOrOuterQueryPath
+ | inClassDeclaration
+ | inCollectionDeclaration
+ | inCollectionElementsDeclaration
+ ;
+
+fromClassOrOuterQueryPath!
+ : c:path { weakKeywords(); } (a:asAlias)? (p:propertyFetch)? {
+ #fromClassOrOuterQueryPath = #([RANGE, "RANGE"], #c, #a, #p);
+ }
+ ;
+
+inClassDeclaration!
+ : a:alias IN! CLASS! c:path {
+ #inClassDeclaration = #([RANGE, "RANGE"], #c, #a);
+ }
+ ;
+
+inCollectionDeclaration!
+ : IN! OPEN! p:path CLOSE! a:alias {
+ #inCollectionDeclaration = #([JOIN, "join"], [INNER, "inner"], #p, #a);
+ }
+ ;
+
+inCollectionElementsDeclaration!
+ : a:alias IN! ELEMENTS! OPEN! p:path CLOSE! {
+ #inCollectionElementsDeclaration = #([JOIN, "join"], [INNER, "inner"], #p, #a);
+ }
+ ;
+
+// Alias rule - Parses the optional 'as' token and forces an AST identifier node.
+asAlias
+ : (AS!)? alias
+ ;
+
+alias
+ : a:identifier { #a.setType(ALIAS); }
+ ;
+
+propertyFetch
+ : FETCH ALL! PROPERTIES!
+ ;
+
+//## groupByClause:
+//## GROUP_BY path ( COMMA path )*;
+
+groupByClause
+ : GROUP^
+ "by"! expression ( COMMA! expression )*
+ (havingClause)?
+ ;
+
+//## orderByClause:
+//## ORDER_BY selectedPropertiesList;
+
+orderByClause
+ : ORDER^ "by"! orderElement ( COMMA! orderElement )*
+ ;
+
+orderElement
+ : expression ( ascendingOrDescending )?
+ ;
+
+ascendingOrDescending
+ : ( "asc" | "ascending" ) { #ascendingOrDescending.setType(ASCENDING); }
+ | ( "desc" | "descending") { #ascendingOrDescending.setType(DESCENDING); }
+ ;
+
+//## havingClause:
+//## HAVING logicalExpression;
+
+havingClause
+ : HAVING^ logicalExpression
+ ;
+
+//## whereClause:
+//## WHERE logicalExpression;
+
+whereClause
+ : WHERE^ logicalExpression
+ ;
+
+//## selectedPropertiesList:
+//## ( path | aggregate ) ( COMMA path | aggregate )*;
+
+selectedPropertiesList
+ : aliasedExpression ( COMMA! aliasedExpression )*
+ ;
+
+aliasedExpression
+ : expression ( AS^ identifier )?
+ ;
+
+// expressions
+// Note that most of these expressions follow the pattern
+// thisLevelExpression :
+// nextHigherPrecedenceExpression
+// (OPERATOR nextHigherPrecedenceExpression)*
+// which is a standard recursive definition for a parsing an expression.
+//
+// Operator precedence in HQL
+// lowest --> ( 7) OR
+// ( 6) AND, NOT
+// ( 5) equality: ==, <>, !=, is
+// ( 4) relational: <, <=, >, >=,
+// LIKE, NOT LIKE, BETWEEN, NOT BETWEEN, IN, NOT IN
+// ( 3) addition and subtraction: +(binary) -(binary)
+// ( 2) multiplication: * / %, concatenate: ||
+// highest --> ( 1) +(unary) -(unary)
+// [] () (method call) . (dot -- identifier qualification)
+// aggregate function
+// () (explicit parenthesis)
+//
+// Note that the above precedence levels map to the rules below...
+// Once you have a precedence chart, writing the appropriate rules as below
+// is usually very straightfoward
+
+logicalExpression
+ : expression
+ ;
+
+// Main expression rule
+expression
+ : logicalOrExpression
+ ;
+
+// level 7 - OR
+logicalOrExpression
+ : logicalAndExpression ( OR^ logicalAndExpression )*
+ ;
+
+// level 6 - AND, NOT
+logicalAndExpression
+ : negatedExpression ( AND^ negatedExpression )*
+ ;
+
+// NOT nodes aren't generated. Instead, the operator in the sub-tree will be
+// negated, if possible. Expressions without a NOT parent are passed through.
+negatedExpression!
+{ weakKeywords(); } // Weak keywords can appear in an expression, so look ahead.
+ : NOT^ x:negatedExpression { #negatedExpression = negateNode(#x); }
+ | y:equalityExpression { #negatedExpression = #y; }
+ ;
+
+//## OP: EQ | LT | GT | LE | GE | NE | SQL_NE | LIKE;
+
+// level 5 - EQ, NE
+equalityExpression
+ : x:relationalExpression (
+ ( EQ^
+ | is:IS^ { #is.setType(EQ); } (NOT! { #is.setType(NE); } )?
+ | NE^
+ | ne:SQL_NE^ { #ne.setType(NE); }
+ ) y:relationalExpression)* {
+ // Post process the equality expression to clean up 'is null', etc.
+ #equalityExpression = processEqualityExpression(#equalityExpression);
+ }
+ ;
+
+// level 4 - LT, GT, LE, GE, LIKE, NOT LIKE, BETWEEN, NOT BETWEEN
+// NOTE: The NOT prefix for LIKE and BETWEEN will be represented in the
+// token type. When traversing the AST, use the token type, and not the
+// token text to interpret the semantics of these nodes.
+relationalExpression
+ : concatenation (
+ ( ( ( LT^ | GT^ | LE^ | GE^ ) additiveExpression )* )
+ // Disable node production for the optional 'not'.
+ | (n:NOT!)? (
+ // Represent the optional NOT prefix using the token type by
+ // testing 'n' and setting the token type accordingly.
+ (i:IN^ {
+ #i.setType( (n == null) ? IN : NOT_IN);
+ #i.setText( (n == null) ? "in" : "not in");
+ }
+ inList)
+ | (b:BETWEEN^ {
+ #b.setType( (n == null) ? BETWEEN : NOT_BETWEEN);
+ #b.setText( (n == null) ? "between" : "not between");
+ }
+ betweenList )
+ | (l:LIKE^ {
+ #l.setType( (n == null) ? LIKE : NOT_LIKE);
+ #l.setText( (n == null) ? "like" : "not like");
+ }
+ concatenation likeEscape)
+ | (MEMBER! (OF!)? p:path! {
+ processMemberOf(n,#p,currentAST);
+ } ) )
+ )
+ ;
+
+likeEscape
+ : (ESCAPE^ concatenation)?
+ ;
+
+inList
+ : x:compoundExpr
+ { #inList = #([IN_LIST,"inList"], #inList); }
+ ;
+
+betweenList
+ : concatenation AND! concatenation
+ ;
+
+//level 4 - string concatenation
+concatenation
+ : additiveExpression
+ ( c:CONCAT^ { #c.setType(EXPR_LIST); #c.setText("concatList"); }
+ additiveExpression
+ ( CONCAT! additiveExpression )*
+ { #concatenation = #([METHOD_CALL, "||"], #([IDENT, "concat"]), #c ); } )?
+ ;
+
+// level 3 - binary plus and minus
+additiveExpression
+ : multiplyExpression ( ( PLUS^ | MINUS^ ) multiplyExpression )*
+ ;
+
+// level 2 - binary multiply and divide
+multiplyExpression
+ : unaryExpression ( ( STAR^ | DIV^ ) unaryExpression )*
+ ;
+
+// level 1 - unary minus, unary plus, not
+unaryExpression
+ : MINUS^ {#MINUS.setType(UNARY_MINUS);} unaryExpression
+ | PLUS^ {#PLUS.setType(UNARY_PLUS);} unaryExpression
+ | caseExpression
+ | quantifiedExpression
+ | atom
+ ;
+
+caseExpression
+ : CASE^ (whenClause)+ (elseClause)? END!
+ | CASE^ { #CASE.setType(CASE2); } unaryExpression (altWhenClause)+ (elseClause)? END!
+ ;
+
+whenClause
+ : (WHEN^ logicalExpression THEN! unaryExpression)
+ ;
+
+altWhenClause
+ : (WHEN^ unaryExpression THEN! unaryExpression)
+ ;
+
+elseClause
+ : (ELSE^ unaryExpression)
+ ;
+
+quantifiedExpression
+ : ( SOME^ | EXISTS^ | ALL^ | ANY^ )
+ ( identifier | collectionExpr | (OPEN! ( subQuery ) CLOSE!) )
+ ;
+
+// level 0 - expression atom
+// ident qualifier ('.' ident ), array index ( [ expr ] ),
+// method call ( '.' ident '(' exprList ') )
+atom
+ : primaryExpression
+ (
+ DOT^ identifier
+ ( options { greedy=true; } :
+ ( op:OPEN^ {#op.setType(METHOD_CALL);} exprList CLOSE! ) )?
+ | lb:OPEN_BRACKET^ {#lb.setType(INDEX_OP);} expression CLOSE_BRACKET!
+ )*
+ ;
+
+// level 0 - the basic element of an expression
+primaryExpression
+ : identPrimary ( options {greedy=true;} : DOT^ "class" )?
+ | constant
+ | COLON^ identifier
+ // TODO: Add parens to the tree so the user can control the operator evaluation order.
+ | OPEN! (expressionOrVector | subQuery) CLOSE!
+ | PARAM^ (NUM_INT)?
+ ;
+
+// This parses normal expression and a list of expressions separated by commas. If a comma is encountered
+// a parent VECTOR_EXPR node will be created for the list.
+expressionOrVector!
+ : e:expression ( v:vectorExpr )? {
+ // If this is a vector expression, create a parent node for it.
+ if (#v != null)
+ #expressionOrVector = #([VECTOR_EXPR,"{vector}"], #e, #v);
+ else
+ #expressionOrVector = #e;
+ }
+ ;
+
+vectorExpr
+ : COMMA! expression (COMMA! expression)*
+ ;
+
+// identifier, followed by member refs (dot ident), or method calls.
+// NOTE: handleDotIdent() is called immediately after the first IDENT is recognized because
+// the method looks a head to find keywords after DOT and turns them into identifiers.
+identPrimary
+ : identifier { handleDotIdent(); }
+ ( options { greedy=true; } : DOT^ ( identifier | ELEMENTS | o:OBJECT { #o.setType(IDENT); } ) )*
+ ( options { greedy=true; } :
+ ( op:OPEN^ { #op.setType(METHOD_CALL);} exprList CLOSE! )
+ )?
+ // Also allow special 'aggregate functions' such as count(), avg(), etc.
+ | aggregate
+ ;
+
+//## aggregate:
+//## ( aggregateFunction OPEN path CLOSE ) | ( COUNT OPEN STAR CLOSE ) | ( COUNT OPEN (DISTINCT | ALL) path CLOSE );
+
+//## aggregateFunction:
+//## COUNT | 'sum' | 'avg' | 'max' | 'min';
+
+aggregate
+ : ( SUM^ | AVG^ | MAX^ | MIN^ ) OPEN! additiveExpression CLOSE! { #aggregate.setType(AGGREGATE); }
+ // Special case for count - It's 'parameters' can be keywords.
+ | COUNT^ OPEN! ( STAR { #STAR.setType(ROW_STAR); } | ( ( DISTINCT | ALL )? ( path | collectionExpr ) ) ) CLOSE!
+ | collectionExpr
+ ;
+
+//## collection: ( OPEN query CLOSE ) | ( 'elements'|'indices' OPEN path CLOSE );
+
+collectionExpr
+ : (ELEMENTS^ | INDICES^) OPEN! path CLOSE!
+ ;
+
+// NOTE: compoundExpr can be a 'path' where the last token in the path is '.elements' or '.indicies'
+compoundExpr
+ : collectionExpr
+ | path
+ | (OPEN! ( (expression (COMMA! expression)*) | subQuery ) CLOSE!)
+ ;
+
+subQuery
+ : union
+ { #subQuery = #([QUERY,"query"], #subQuery); }
+ ;
+
+exprList
+{
+ AST trimSpec = null;
+}
+ : (t:TRAILING {#trimSpec = #t;} | l:LEADING {#trimSpec = #l;} | b:BOTH {#trimSpec = #b;})?
+ { if(#trimSpec != null) #trimSpec.setType(IDENT); }
+ (
+ expression ( (COMMA! expression)+ | FROM { #FROM.setType(IDENT); } expression | AS! identifier )?
+ | FROM { #FROM.setType(IDENT); } expression
+ )?
+ { #exprList = #([EXPR_LIST,"exprList"], #exprList); }
+ ;
+
+constant
+ : NUM_INT
+ | NUM_FLOAT
+ | NUM_LONG
+ | NUM_DOUBLE
+ | QUOTED_STRING
+ | NULL
+ | TRUE
+ | FALSE
+ | EMPTY
+ ;
+
+//## quantifiedExpression: 'exists' | ( expression 'in' ) | ( expression OP 'any' | 'some' ) collection;
+
+//## compoundPath: path ( OPEN_BRACKET expression CLOSE_BRACKET ( '.' path )? )*;
+
+//## path: identifier ( '.' identifier )*;
+
+path
+ : identifier ( DOT^ { weakKeywords(); } identifier )*
+ ;
+
+// Wraps the IDENT token from the lexer, in order to provide
+// 'keyword as identifier' trickery.
+identifier
+ : IDENT
+ exception
+ catch [RecognitionException ex]
+ {
+ identifier_AST = handleIdentifierError(LT(1),ex);
+ }
+ ;
+
+// **** LEXER ******************************************************************
+
+/**
+ * Hibernate Query Language Lexer
+ * <br>
+ * This lexer provides the HQL parser with tokens.
+ * @author Joshua Davis (pgmjsd at sourceforge.net)
+ */
+class HqlBaseLexer extends Lexer;
+
+options {
+ exportVocab=Hql; // call the vocabulary "Hql"
+ testLiterals = false;
+ k=2; // needed for newline, and to distinguish '>' from '>='.
+ // HHH-241 : Quoted strings don't allow unicode chars - This should fix it.
+ charVocabulary='\u0000'..'\uFFFE'; // Allow any char but \uFFFF (16 bit -1, ANTLR's EOF character)
+ caseSensitive = false;
+ caseSensitiveLiterals = false;
+}
+
+// -- Declarations --
+{
+ // NOTE: The real implementations are in the subclass.
+ protected void setPossibleID(boolean possibleID) {}
+}
+
+// -- Keywords --
+
+EQ: '=';
+LT: '<';
+GT: '>';
+SQL_NE: "<>";
+NE: "!=" | "^=";
+LE: "<=";
+GE: ">=";
+
+COMMA: ',';
+
+OPEN: '(';
+CLOSE: ')';
+OPEN_BRACKET: '[';
+CLOSE_BRACKET: ']';
+
+CONCAT: "||";
+PLUS: '+';
+MINUS: '-';
+STAR: '*';
+DIV: '/';
+COLON: ':';
+PARAM: '?';
+
+IDENT options { testLiterals=true; }
+ : ID_START_LETTER ( ID_LETTER )*
+ {
+ // Setting this flag allows the grammar to use keywords as identifiers, if necessary.
+ setPossibleID(true);
+ }
+ ;
+
+protected
+ID_START_LETTER
+ : '_'
+ | '$'
+ | 'a'..'z'
+ | '\u0080'..'\ufffe' // HHH-558 : Allow unicode chars in identifiers
+ ;
+
+protected
+ID_LETTER
+ : ID_START_LETTER
+ | '0'..'9'
+ ;
+
+QUOTED_STRING
+ : '\'' ( (ESCqs)=> ESCqs | ~'\'' )* '\''
+ ;
+
+protected
+ESCqs
+ :
+ '\'' '\''
+ ;
+
+WS : ( ' '
+ | '\t'
+ | '\r' '\n' { newline(); }
+ | '\n' { newline(); }
+ | '\r' { newline(); }
+ )
+ {$setType(Token.SKIP);} //ignore this token
+ ;
+
+//--- From the Java example grammar ---
+// a numeric literal
+NUM_INT
+ {boolean isDecimal=false; Token t=null;}
+ : '.' {_ttype = DOT;}
+ ( ('0'..'9')+ (EXPONENT)? (f1:FLOAT_SUFFIX {t=f1;})?
+ {
+ if (t != null && t.getText().toUpperCase().indexOf('F')>=0)
+ {
+ _ttype = NUM_FLOAT;
+ }
+ else
+ {
+ _ttype = NUM_DOUBLE; // assume double
+ }
+ }
+ )?
+ | ( '0' {isDecimal = true;} // special case for just '0'
+ ( ('x')
+ ( // hex
+ // the 'e'|'E' and float suffix stuff look
+ // like hex digits, hence the (...)+ doesn't
+ // know when to stop: ambig. ANTLR resolves
+ // it correctly by matching immediately. It
+ // is therefore ok to hush warning.
+ options { warnWhenFollowAmbig=false; }
+ : HEX_DIGIT
+ )+
+ | ('0'..'7')+ // octal
+ )?
+ | ('1'..'9') ('0'..'9')* {isDecimal=true;} // non-zero decimal
+ )
+ ( ('l') { _ttype = NUM_LONG; }
+
+ // only check to see if it's a float if looks like decimal so far
+ | {isDecimal}?
+ ( '.' ('0'..'9')* (EXPONENT)? (f2:FLOAT_SUFFIX {t=f2;})?
+ | EXPONENT (f3:FLOAT_SUFFIX {t=f3;})?
+ | f4:FLOAT_SUFFIX {t=f4;}
+ )
+ {
+ if (t != null && t.getText().toUpperCase() .indexOf('F') >= 0)
+ {
+ _ttype = NUM_FLOAT;
+ }
+ else
+ {
+ _ttype = NUM_DOUBLE; // assume double
+ }
+ }
+ )?
+ ;
+
+// hexadecimal digit (again, note it's protected!)
+protected
+HEX_DIGIT
+ : ('0'..'9'|'a'..'f')
+ ;
+
+// a couple protected methods to assist in matching floating point numbers
+protected
+EXPONENT
+ : ('e') ('+'|'-')? ('0'..'9')+
+ ;
+
+protected
+FLOAT_SUFFIX
+ : 'f'|'d'
+ ;
+
Added: trunk/Hibernate3/core/src/main/antlr/sql-gen.g
===================================================================
--- trunk/Hibernate3/core/src/main/antlr/sql-gen.g (rev 0)
+++ trunk/Hibernate3/core/src/main/antlr/sql-gen.g 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,427 @@
+header
+{
+// $Id: sql-gen.g 10001 2006-06-08 21:08:04Z steve.ebersole at jboss.com $
+package org.hibernate.hql.antlr;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+}
+/**
+ * SQL Generator Tree Parser, providing SQL rendering of SQL ASTs produced by the previous phase, HqlSqlWalker. All
+ * syntax decoration such as extra spaces, lack of spaces, extra parens, etc. should be added by this class.
+ * <br>
+ * This grammar processes the HQL/SQL AST and produces an SQL string. The intent is to move dialect-specific
+ * code into a sub-class that will override some of the methods, just like the other two grammars in this system.
+ * @author Joshua Davis (joshua at hibernate.org)
+ */
+class SqlGeneratorBase extends TreeParser;
+
+options {
+ // Note: importVocab and exportVocab cause ANTLR to share the token type numbers between the
+ // two grammars. This means that the token type constants from the source tree are the same
+ // as those in the target tree. If this is not the case, tree translation can result in
+ // token types from the *source* tree being present in the target tree.
+ importVocab=HqlSql; // import definitions from "HqlSql"
+ exportVocab=Sql; // Call the resulting definitions "Sql"
+ buildAST=false; // Don't build an AST.
+}
+
+{
+ private static Log log = LogFactory.getLog(SqlGeneratorBase.class);
+
+ /** the buffer resulting SQL statement is written to */
+ private StringBuffer buf = new StringBuffer();
+
+ protected void out(String s) {
+ buf.append(s);
+ }
+
+ /**
+ * Returns the last character written to the output, or -1 if there isn't one.
+ */
+ protected int getLastChar() {
+ int len = buf.length();
+ if ( len == 0 )
+ return -1;
+ else
+ return buf.charAt( len - 1 );
+ }
+
+ /**
+ * Add a aspace if the previous token was not a space or a parenthesis.
+ */
+ protected void optionalSpace() {
+ // Implemented in the sub-class.
+ }
+
+ protected void out(AST n) {
+ out(n.getText());
+ }
+
+ protected void separator(AST n, String sep) {
+ if (n.getNextSibling() != null)
+ out(sep);
+ }
+
+ protected boolean hasText(AST a) {
+ String t = a.getText();
+ return t != null && t.length() > 0;
+ }
+
+ protected void fromFragmentSeparator(AST a) {
+ // moved this impl into the subclass...
+ }
+
+ protected void nestedFromFragment(AST d,AST parent) {
+ // moved this impl into the subclass...
+ }
+
+ protected StringBuffer getStringBuffer() {
+ return buf;
+ }
+
+ protected void nyi(AST n) {
+ throw new UnsupportedOperationException("Unsupported node: " + n);
+ }
+
+ protected void beginFunctionTemplate(AST m,AST i) {
+ // if template is null we just write the function out as it appears in the hql statement
+ out(i);
+ out("(");
+ }
+
+ protected void endFunctionTemplate(AST m) {
+ out(")");
+ }
+
+ protected void commaBetweenParameters(String comma) {
+ out(comma);
+ }
+}
+
+statement
+ : selectStatement
+ | updateStatement
+ | deleteStatement
+ | insertStatement
+ ;
+
+selectStatement
+ : #(SELECT { out("select "); }
+ selectClause
+ from
+ ( #(WHERE { out(" where "); } whereExpr ) )?
+ ( #(GROUP { out(" group by "); } groupExprs ( #(HAVING { out(" having "); } booleanExpr[false]) )? ) )?
+ ( #(ORDER { out(" order by "); } orderExprs ) )?
+ )
+ ;
+
+// Note: eats the FROM token node, as it is not valid in an update statement.
+// It's outlived its usefulness after analysis phase :)
+// TODO : needed to use conditionList directly here and deleteStatement, as whereExprs no longer works for this stuff
+updateStatement
+ : #(UPDATE { out("update "); }
+ #( FROM fromTable )
+ setClause
+ (whereClause)?
+ )
+ ;
+
+deleteStatement
+ // Note: not space needed at end of "delete" because the from rule included one before the "from" it outputs
+ : #(DELETE { out("delete"); }
+ from
+ (whereClause)?
+ )
+ ;
+
+insertStatement
+ : #(INSERT { out( "insert " ); }
+ i:INTO { out( i ); out( " " ); }
+ selectStatement
+ )
+ ;
+
+setClause
+ // Simply re-use comparisionExpr, because it already correctly defines the EQ rule the
+ // way it is needed here; not the most aptly named, but ah
+ : #( SET { out(" set "); } comparisonExpr[false] ( { out(", "); } comparisonExpr[false] )* )
+ ;
+
+whereClause
+ : #(WHERE { out(" where "); } whereClauseExpr )
+ ;
+
+whereClauseExpr
+ : (SQL_TOKEN) => conditionList
+ | booleanExpr[ false ]
+ ;
+
+orderExprs
+ // TODO: remove goofy space before the comma when we don't have to regression test anymore.
+ : ( expr ) (dir:orderDirection { out(" "); out(dir); })? ( {out(", "); } orderExprs)?
+ ;
+
+groupExprs
+ // TODO: remove goofy space before the comma when we don't have to regression test anymore.
+ : expr ( {out(" , "); } groupExprs)?
+ ;
+
+orderDirection
+ : ASCENDING
+ | DESCENDING
+ ;
+
+whereExpr
+ // Expect the filter subtree, followed by the theta join subtree, followed by the HQL condition subtree.
+ // Might need parens around the HQL condition if there is more than one subtree.
+ // Put 'and' between each subtree.
+ : filters
+ ( { out(" and "); } thetaJoins )?
+ ( { out(" and "); } booleanExpr [ true ] )?
+ | thetaJoins
+ ( { out(" and "); } booleanExpr [ true ] )?
+ | booleanExpr[false]
+ ;
+
+filters
+ : #(FILTERS conditionList )
+ ;
+
+thetaJoins
+ : #(THETA_JOINS conditionList )
+ ;
+
+conditionList
+ : sqlToken ( { out(" and "); } conditionList )?
+ ;
+
+selectClause
+ : #(SELECT_CLAUSE (distinctOrAll)? ( selectColumn )+ )
+ ;
+
+selectColumn
+ : p:selectExpr (sc:SELECT_COLUMNS { out(sc); } )? { separator( (sc != null) ? sc : p,", "); }
+ ;
+
+selectExpr
+ : e:selectAtom { out(e); }
+ | count
+ | #(CONSTRUCTOR (DOT | IDENT) ( selectColumn )+ )
+ | methodCall
+ | aggregate
+ | c:constant { out(c); }
+ | arithmeticExpr
+ | PARAM { out("?"); }
+ | sn:SQL_NODE { out(sn); }
+ | { out("("); } selectStatement { out(")"); }
+ ;
+
+count
+ : #(COUNT { out("count("); } ( distinctOrAll ) ? countExpr { out(")"); } )
+ ;
+
+distinctOrAll
+ : DISTINCT { out("distinct "); }
+ | ALL { out("all "); }
+ ;
+
+countExpr
+ // Syntacitic predicate resolves star all by itself, avoiding a conflict with STAR in expr.
+ : ROW_STAR { out("*"); }
+ | simpleExpr
+ ;
+
+selectAtom
+ : DOT
+ | SQL_TOKEN
+ | ALIAS_REF
+ | SELECT_EXPR
+ ;
+
+// The from-clause piece is all goofed up. Currently, nodes of type FROM_FRAGMENT
+// and JOIN_FRAGMENT can occur at any level in the FromClause sub-tree. We really
+// should come back and clean this up at some point; which I think will require
+// a post-HqlSqlWalker phase to "re-align" the FromElements in a more sensible
+// manner.
+from
+ : #(f:FROM { out(" from "); }
+ (fromTable)* )
+ ;
+
+fromTable
+ // Write the table node (from fragment) and all the join fragments associated with it.
+ : #( a:FROM_FRAGMENT { out(a); } (tableJoin [ a ])* { fromFragmentSeparator(a); } )
+ | #( b:JOIN_FRAGMENT { out(b); } (tableJoin [ b ])* { fromFragmentSeparator(b); } )
+ ;
+
+tableJoin [ AST parent ]
+ : #( c:JOIN_FRAGMENT { out(" "); out(c); } (tableJoin [ c ] )* )
+ | #( d:FROM_FRAGMENT { nestedFromFragment(d,parent); } (tableJoin [ d ] )* )
+ ;
+
+booleanOp[ boolean parens ]
+ : #(AND booleanExpr[true] { out(" and "); } booleanExpr[true])
+ | #(OR { if (parens) out("("); } booleanExpr[false] { out(" or "); } booleanExpr[false] { if (parens) out(")"); })
+ | #(NOT { out(" not ("); } booleanExpr[false] { out(")"); } )
+ ;
+
+booleanExpr[ boolean parens ]
+ : booleanOp [ parens ]
+ | comparisonExpr [ parens ]
+ | st:SQL_TOKEN { out(st); } // solely for the purpose of mapping-defined where-fragments
+ ;
+
+comparisonExpr[ boolean parens ]
+ : binaryComparisonExpression
+ | { if (parens) out("("); } exoticComparisonExpression { if (parens) out(")"); }
+ ;
+
+binaryComparisonExpression
+ : #(EQ expr { out("="); } expr)
+ | #(NE expr { out("<>"); } expr)
+ | #(GT expr { out(">"); } expr)
+ | #(GE expr { out(">="); } expr)
+ | #(LT expr { out("<"); } expr)
+ | #(LE expr { out("<="); } expr)
+ ;
+
+exoticComparisonExpression
+ : #(LIKE expr { out(" like "); } expr likeEscape )
+ | #(NOT_LIKE expr { out(" not like "); } expr likeEscape)
+ | #(BETWEEN expr { out(" between "); } expr { out(" and "); } expr)
+ | #(NOT_BETWEEN expr { out(" not between "); } expr { out(" and "); } expr)
+ | #(IN expr { out(" in"); } inList )
+ | #(NOT_IN expr { out(" not in "); } inList )
+ | #(EXISTS { optionalSpace(); out("exists "); } quantified )
+ | #(IS_NULL expr) { out(" is null"); }
+ | #(IS_NOT_NULL expr) { out(" is not null"); }
+ ;
+
+likeEscape
+ : ( #(ESCAPE { out(" escape "); } expr) )?
+ ;
+
+inList
+ : #(IN_LIST { out(" "); } ( parenSelect | simpleExprList ) )
+ ;
+
+simpleExprList
+ : { out("("); } (e:simpleExpr { separator(e," , "); } )* { out(")"); }
+ ;
+
+// A simple expression, or a sub-select with parens around it.
+expr
+ : simpleExpr
+ | #( VECTOR_EXPR { out("("); } (e:expr { separator(e," , "); } )* { out(")"); } )
+ | parenSelect
+ | #(ANY { out("any "); } quantified )
+ | #(ALL { out("all "); } quantified )
+ | #(SOME { out("some "); } quantified )
+ ;
+
+quantified
+ : { out("("); } ( sqlToken | selectStatement ) { out(")"); }
+ ;
+
+parenSelect
+ : { out("("); } selectStatement { out(")"); }
+ ;
+
+simpleExpr
+ : c:constant { out(c); }
+ | NULL { out("null"); }
+ | addrExpr
+ | sqlToken
+ | aggregate
+ | methodCall
+ | count
+ | parameter
+ | arithmeticExpr
+ ;
+
+constant
+ : NUM_DOUBLE
+ | NUM_FLOAT
+ | NUM_INT
+ | NUM_LONG
+ | QUOTED_STRING
+ | CONSTANT
+ | JAVA_CONSTANT
+ | TRUE
+ | FALSE
+ | IDENT
+ ;
+
+arithmeticExpr
+ : additiveExpr
+ | multiplicativeExpr
+// | #(CONCAT { out("("); } expr ( { out("||"); } expr )+ { out(")"); } )
+ | #(UNARY_MINUS { out("-"); } expr)
+ | caseExpr
+ ;
+
+additiveExpr
+ : #(PLUS expr { out("+"); } expr)
+ | #(MINUS expr { out("-"); } nestedExprAfterMinusDiv)
+ ;
+
+multiplicativeExpr
+ : #(STAR nestedExpr { out("*"); } nestedExpr)
+ | #(DIV nestedExpr { out("/"); } nestedExprAfterMinusDiv)
+ ;
+
+nestedExpr
+ // Generate parens around nested additive expressions, use a syntactic predicate to avoid conflicts with 'expr'.
+ : (additiveExpr) => { out("("); } additiveExpr { out(")"); }
+ | expr
+ ;
+
+nestedExprAfterMinusDiv
+ // Generate parens around nested arithmetic expressions, use a syntactic predicate to avoid conflicts with 'expr'.
+ : (arithmeticExpr) => { out("("); } arithmeticExpr { out(")"); }
+ | expr
+ ;
+
+caseExpr
+ : #(CASE { out("case"); }
+ ( #(WHEN { out( " when "); } booleanExpr[false] { out(" then "); } expr) )+
+ ( #(ELSE { out(" else "); } expr) )?
+ { out(" end"); } )
+ | #(CASE2 { out("case "); } expr
+ ( #(WHEN { out( " when "); } expr { out(" then "); } expr) )+
+ ( #(ELSE { out(" else "); } expr) )?
+ { out(" end"); } )
+ ;
+
+aggregate
+ : #(a:AGGREGATE { out(a); out("("); } expr { out(")"); } )
+ ;
+
+
+methodCall
+ : #(m:METHOD_CALL i:METHOD_NAME { beginFunctionTemplate(m,i); }
+ ( #(EXPR_LIST (arguments)? ) )?
+ { endFunctionTemplate(m); } )
+ ;
+
+arguments
+ : expr ( { commaBetweenParameters(", "); } expr )*
+ ;
+
+parameter
+ : n:NAMED_PARAM { out(n); }
+ | p:PARAM { out(p); }
+ ;
+
+addrExpr
+ : #(r:DOT . .) { out(r); }
+ | i:ALIAS_REF { out(i); }
+ | j:INDEX_OP { out(j); }
+ ;
+
+sqlToken
+ : t:SQL_TOKEN { out(t); }
+ ;
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/AssertionFailure.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/AssertionFailure.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/AssertionFailure.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,36 @@
+//$Id: AssertionFailure.java 3890 2004-06-03 16:31:32Z steveebersole $
+package org.hibernate;
+
+import org.hibernate.exception.NestableRuntimeException;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Indicates failure of an assertion: a possible bug in Hibernate.
+ *
+ * @author Gavin King
+ */
+
+public class AssertionFailure extends NestableRuntimeException {
+
+ private static final Log log = LogFactory.getLog(AssertionFailure.class);
+
+ private static final String MESSAGE = "an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session)";
+
+ public AssertionFailure(String s) {
+ super(s);
+ log.error(MESSAGE, this);
+ }
+
+ public AssertionFailure(String s, Throwable t) {
+ super(s, t);
+ log.error(MESSAGE, t);
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/CacheMode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/CacheMode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/CacheMode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,78 @@
+//$Id: CacheMode.java 9194 2006-02-01 19:59:07Z steveebersole $
+package org.hibernate;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Controls how the session interacts with the second-level
+ * cache and query cache.
+ *
+ * @see Session#setCacheMode(CacheMode)
+ * @author Gavin King
+ */
+public final class CacheMode implements Serializable {
+ private final String name;
+ private final boolean isPutEnabled;
+ private final boolean isGetEnabled;
+ private static final Map INSTANCES = new HashMap();
+
+ private CacheMode(String name, boolean isPutEnabled, boolean isGetEnabled) {
+ this.name=name;
+ this.isPutEnabled = isPutEnabled;
+ this.isGetEnabled = isGetEnabled;
+ }
+ public String toString() {
+ return name;
+ }
+ public boolean isPutEnabled() {
+ return isPutEnabled;
+ }
+ public boolean isGetEnabled() {
+ return isGetEnabled;
+ }
+ /**
+ * The session may read items from the cache, and add items to the cache
+ */
+ public static final CacheMode NORMAL = new CacheMode("NORMAL", true, true);
+ /**
+ * The session will never interact with the cache, except to invalidate
+ * cache items when updates occur
+ */
+ public static final CacheMode IGNORE = new CacheMode("IGNORE", false, false);
+ /**
+ * The session may read items from the cache, but will not add items,
+ * except to invalidate items when updates occur
+ */
+ public static final CacheMode GET = new CacheMode("GET", false, true);
+ /**
+ * The session will never read items from the cache, but will add items
+ * to the cache as it reads them from the database.
+ */
+ public static final CacheMode PUT = new CacheMode("PUT", true, false);
+
+ /**
+ * The session will never read items from the cache, but will add items
+ * to the cache as it reads them from the database. In this mode, the
+ * effect of <tt>hibernate.cache.use_minimal_puts</tt> is bypassed, in
+ * order to <em>force</em> a cache refresh
+ */
+ public static final CacheMode REFRESH = new CacheMode("REFRESH", true, false);
+
+ static {
+ INSTANCES.put( NORMAL.name, NORMAL );
+ INSTANCES.put( IGNORE.name, IGNORE );
+ INSTANCES.put( GET.name, GET );
+ INSTANCES.put( PUT.name, PUT );
+ INSTANCES.put( REFRESH.name, REFRESH );
+ }
+
+ private Object readResolve() {
+ return INSTANCES.get( name );
+ }
+
+ public static CacheMode parse(String name) {
+ return ( CacheMode ) INSTANCES.get( name );
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/CacheMode.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/CallbackException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/CallbackException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/CallbackException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,34 @@
+//$Id: CallbackException.java 4242 2004-08-11 09:10:45Z oneovthafew $
+package org.hibernate;
+
+
+/**
+ * Should be thrown by persistent objects from <tt>Lifecycle</tt>
+ * or <tt>Interceptor</tt> callbacks.
+ *
+ * @see Lifecycle
+ * @see Interceptor
+ * @author Gavin King
+ */
+
+public class CallbackException extends HibernateException {
+
+ public CallbackException(Exception root) {
+ super("An exception occurred in a callback", root);
+ }
+
+ public CallbackException(String message) {
+ super(message);
+ }
+
+ public CallbackException(String message, Exception e) {
+ super(message, e);
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/ConnectionReleaseMode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/ConnectionReleaseMode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/ConnectionReleaseMode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,77 @@
+// $Id: ConnectionReleaseMode.java 8409 2005-10-14 20:28:18Z steveebersole $
+package org.hibernate;
+
+import java.io.Serializable;
+
+/**
+ * Defines the various policies by which Hibernate might release its underlying
+ * JDBC connection.
+ *
+ * @author Steve Ebersole
+ */
+public class ConnectionReleaseMode implements Serializable {
+
+ /**
+ * Indicates that JDBC connection should be aggressively released after each
+ * SQL statement is executed. In this mode, the application <em>must</em>
+ * explicitly close all iterators and scrollable results. This mode may
+ * only be used with a JTA datasource.
+ */
+ public static final ConnectionReleaseMode AFTER_STATEMENT = new ConnectionReleaseMode( "after_statement" );
+
+ /**
+ * Indicates that JDBC connections should be released after each transaction
+ * ends (works with both JTA-registered synch and HibernateTransaction API).
+ * This mode may not be used with an application server JTA datasource.
+ * <p/>
+ * This is the default mode starting in 3.1; was previously {@link #ON_CLOSE}.
+ */
+ public static final ConnectionReleaseMode AFTER_TRANSACTION = new ConnectionReleaseMode( "after_transaction" );
+
+ /**
+ * Indicates that connections should only be released when the Session is explicitly closed
+ * or disconnected; this is the legacy (Hibernate2 and pre-3.1) behavior.
+ */
+ public static final ConnectionReleaseMode ON_CLOSE = new ConnectionReleaseMode( "on_close" );
+
+
+ private String name;
+
+ private ConnectionReleaseMode(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Override of Object.toString(). Returns the release mode name.
+ *
+ * @return The release mode name.
+ */
+ public String toString() {
+ return name;
+ }
+
+ /**
+ * Determine the correct ConnectionReleaseMode instance based on the given
+ * name.
+ *
+ * @param modeName The release mode name.
+ * @return The appropriate ConnectionReleaseMode instance
+ * @throws HibernateException Indicates the modeName param did not match any known modes.
+ */
+ public static ConnectionReleaseMode parse(String modeName) throws HibernateException {
+ if ( AFTER_STATEMENT.name.equals( modeName ) ) {
+ return AFTER_STATEMENT;
+ }
+ else if ( AFTER_TRANSACTION.name.equals( modeName ) ) {
+ return AFTER_TRANSACTION;
+ }
+ else if ( ON_CLOSE.name.equals( modeName ) ) {
+ return ON_CLOSE;
+ }
+ throw new HibernateException( "could not determine appropriate connection release mode [" + modeName + "]" );
+ }
+
+ private Object readResolve() {
+ return parse( name );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/Criteria.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/Criteria.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/Criteria.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,338 @@
+//$Id: Criteria.java 9116 2006-01-23 21:21:01Z steveebersole $
+package org.hibernate;
+
+import java.util.List;
+
+import org.hibernate.criterion.CriteriaSpecification;
+import org.hibernate.criterion.Criterion;
+import org.hibernate.criterion.Order;
+import org.hibernate.criterion.Projection;
+import org.hibernate.transform.ResultTransformer;
+
+/**
+ * <tt>Criteria</tt> is a simplified API for retrieving entities
+ * by composing <tt>Criterion</tt> objects. This is a very
+ * convenient approach for functionality like "search" screens
+ * where there is a variable number of conditions to be placed
+ * upon the result set.<br>
+ * <br>
+ * The <tt>Session</tt> is a factory for <tt>Criteria</tt>.
+ * <tt>Criterion</tt> instances are usually obtained via
+ * the factory methods on <tt>Restrictions</tt>. eg.
+ * <pre>
+ * List cats = session.createCriteria(Cat.class)
+ * .add( Restrictions.like("name", "Iz%") )
+ * .add( Restrictions.gt( "weight", new Float(minWeight) ) )
+ * .addOrder( Order.asc("age") )
+ * .list();
+ * </pre>
+ * You may navigate associations using <tt>createAlias()</tt> or
+ * <tt>createCriteria()</tt>.
+ * <pre>
+ * List cats = session.createCriteria(Cat.class)
+ * .createCriteria("kittens")
+ * .add( Restrictions.like("name", "Iz%") )
+ * .list();
+ * </pre>
+ * <pre>
+ * List cats = session.createCriteria(Cat.class)
+ * .createAlias("kittens", "kit")
+ * .add( Restrictions.like("kit.name", "Iz%") )
+ * .list();
+ * </pre>
+ * You may specify projection and aggregation using <tt>Projection</tt>
+ * instances obtained via the factory methods on <tt>Projections</tt>.
+ * <pre>
+ * List cats = session.createCriteria(Cat.class)
+ * .setProjection( Projections.projectionList()
+ * .add( Projections.rowCount() )
+ * .add( Projections.avg("weight") )
+ * .add( Projections.max("weight") )
+ * .add( Projections.min("weight") )
+ * .add( Projections.groupProperty("color") )
+ * )
+ * .addOrder( Order.asc("color") )
+ * .list();
+ * </pre>
+ *
+ * @see Session#createCriteria(java.lang.Class)
+ * @see org.hibernate.criterion.Restrictions
+ * @see org.hibernate.criterion.Projections
+ * @see org.hibernate.criterion.Order
+ * @see org.hibernate.criterion.Criterion
+ * @see org.hibernate.criterion.Projection
+ * @see org.hibernate.criterion.DetachedCriteria a disconnected version of this API
+ * @author Gavin King
+ */
+public interface Criteria extends CriteriaSpecification {
+
+ /**
+ * Get the alias of the entity encapsulated by this criteria instance.
+ *
+ * @return The alias for the encapsulated entity.
+ */
+ public String getAlias();
+
+ /**
+ * Used to specify that the query results will be a projection (scalar in
+ * nature). Implicitly specifies the {@link #PROJECTION} result transformer.
+ * <p/>
+ * The individual components contained within the given
+ * {@link Projection projection} determines the overall "shape" of the
+ * query result.
+ *
+ * @param projection The projection representing the overall "shape" of the
+ * query results.
+ * @return this (for method chaining)
+ */
+ public Criteria setProjection(Projection projection);
+
+ /**
+ * Add a {@link Criterion restriction} to constrain the results to be
+ * retrieved.
+ *
+ * @param criterion The {@link Criterion criterion} object representing the
+ * restriction to be applied.
+ * @return this (for method chaining)
+ */
+ public Criteria add(Criterion criterion);
+
+ /**
+ * Add an {@link Order ordering} to the result set.
+ *
+ * @param order The {@link Order order} object representing an ordering
+ * to be applied to the results.
+ * @return this (for method chaining)
+ */
+ public Criteria addOrder(Order order);
+
+ /**
+ * Specify an association fetching strategy for an association or a
+ * collection of values.
+ *
+ * @param associationPath a dot seperated property path
+ * @param mode The fetch mode for the referenced association
+ * @return this (for method chaining)
+ */
+ public Criteria setFetchMode(String associationPath, FetchMode mode) throws HibernateException;
+
+ /**
+ * Set the lock mode of the current entity
+ *
+ * @param lockMode The lock mode to be applied
+ * @return this (for method chaining)
+ */
+ public Criteria setLockMode(LockMode lockMode);
+
+ /**
+ * Set the lock mode of the aliased entity
+ *
+ * @param alias The previously assigned alias representing the entity to
+ * which the given lock mode should apply.
+ * @param lockMode The lock mode to be applied
+ * @return this (for method chaining)
+ */
+ public Criteria setLockMode(String alias, LockMode lockMode);
+
+ /**
+ * Join an association, assigning an alias to the joined association.
+ * <p/>
+ * Functionally equivalent to {@link #createAlias(String, String, int)} using
+ * {@link #INNER_JOIN} for the joinType.
+ *
+ * @param associationPath A dot-seperated property path
+ * @param alias The alias to assign to the joined association (for later reference).
+ * @return this (for method chaining)
+ */
+ public Criteria createAlias(String associationPath, String alias) throws HibernateException;
+
+ /**
+ * Join an association using the specified join-type, assigning an alias
+ * to the joined association.
+ * <p/>
+ * The joinType is expected to be one of {@link #INNER_JOIN} (the default),
+ * {@link #FULL_JOIN}, or {@link #LEFT_JOIN}.
+ *
+ * @param associationPath A dot-seperated property path
+ * @param alias The alias to assign to the joined association (for later reference).
+ * @param joinType The type of join to use.
+ * @return this (for method chaining)
+ */
+ public Criteria createAlias(String associationPath, String alias, int joinType) throws HibernateException;
+
+ /**
+ * Create a new <tt>Criteria</tt>, "rooted" at the associated entity.
+ * <p/>
+ * Functionally equivalent to {@link #createCriteria(String, int)} using
+ * {@link #INNER_JOIN} for the joinType.
+ *
+ * @param associationPath A dot-seperated property path
+ * @return the created "sub criteria"
+ */
+ public Criteria createCriteria(String associationPath) throws HibernateException;
+
+ /**
+ * Create a new <tt>Criteria</tt>, "rooted" at the associated entity, using the
+ * specified join type.
+ *
+ * @param associationPath A dot-seperated property path
+ * @param joinType The type of join to use.
+ * @return the created "sub criteria"
+ */
+ public Criteria createCriteria(String associationPath, int joinType) throws HibernateException;
+
+ /**
+ * Create a new <tt>Criteria</tt>, "rooted" at the associated entity,
+ * assigning the given alias.
+ * <p/>
+ * Functionally equivalent to {@link #createCriteria(String, String, int)} using
+ * {@link #INNER_JOIN} for the joinType.
+ *
+ * @param associationPath A dot-seperated property path
+ * @param alias The alias to assign to the joined association (for later reference).
+ * @return the created "sub criteria"
+ */
+ public Criteria createCriteria(String associationPath, String alias) throws HibernateException;
+
+ /**
+ * Create a new <tt>Criteria</tt>, "rooted" at the associated entity,
+ * assigning the given alias and using the specified join type.
+ *
+ * @param associationPath A dot-seperated property path
+ * @param alias The alias to assign to the joined association (for later reference).
+ * @param joinType The type of join to use.
+ * @return the created "sub criteria"
+ */
+ public Criteria createCriteria(String associationPath, String alias, int joinType) throws HibernateException;
+
+ /**
+ * Set a strategy for handling the query results. This determines the
+ * "shape" of the query result.
+ *
+ * @param resultTransformer The transformer to apply
+ * @return this (for method chaining)
+ *
+ * @see #ROOT_ENTITY
+ * @see #DISTINCT_ROOT_ENTITY
+ * @see #ALIAS_TO_ENTITY_MAP
+ * @see #PROJECTION
+ */
+ public Criteria setResultTransformer(ResultTransformer resultTransformer);
+
+ /**
+ * Set a limit upon the number of objects to be retrieved.
+ *
+ * @param maxResults the maximum number of results
+ * @return this (for method chaining)
+ */
+ public Criteria setMaxResults(int maxResults);
+
+ /**
+ * Set the first result to be retrieved.
+ *
+ * @param firstResult the first result to retrieve, numbered from <tt>0</tt>
+ * @return this (for method chaining)
+ */
+ public Criteria setFirstResult(int firstResult);
+
+ /**
+ * Set a fetch size for the underlying JDBC query.
+ *
+ * @param fetchSize the fetch size
+ * @return this (for method chaining)
+ *
+ * @see java.sql.Statement#setFetchSize
+ */
+ public Criteria setFetchSize(int fetchSize);
+
+ /**
+ * Set a timeout for the underlying JDBC query.
+ *
+ * @param timeout The timeout value to apply.
+ * @return this (for method chaining)
+ *
+ * @see java.sql.Statement#setQueryTimeout
+ */
+ public Criteria setTimeout(int timeout);
+
+ /**
+ * Enable caching of this query result, provided query caching is enabled
+ * for the underlying session factory.
+ *
+ * @param cacheable Should the result be considered cacheable; default is
+ * to not cache (false).
+ * @return this (for method chaining)
+ */
+ public Criteria setCacheable(boolean cacheable);
+
+ /**
+ * Set the name of the cache region to use for query result caching.
+ *
+ * @param cacheRegion the name of a query cache region, or <tt>null</tt>
+ * for the default query cache
+ * @return this (for method chaining)
+ *
+ * @see #setCacheable
+ */
+ public Criteria setCacheRegion(String cacheRegion);
+
+ /**
+ * Add a comment to the generated SQL.
+ *
+ * @param comment a human-readable string
+ * @return this (for method chaining)
+ */
+ public Criteria setComment(String comment);
+
+ /**
+ * Override the flush mode for this particular query.
+ *
+ * @param flushMode The flush mode to use.
+ * @return this (for method chaining)
+ */
+ public Criteria setFlushMode(FlushMode flushMode);
+
+ /**
+ * Override the cache mode for this particular query.
+ *
+ * @param cacheMode The cache mode to use.
+ * @return this (for method chaining)
+ */
+ public Criteria setCacheMode(CacheMode cacheMode);
+
+ /**
+ * Get the results.
+ *
+ * @return The list of matched query results.
+ */
+ public List list() throws HibernateException;
+
+ /**
+ * Get the results as an instance of {@link ScrollableResults}
+ *
+ * @return The {@link ScrollableResults} representing the matched
+ * query results.
+ */
+ public ScrollableResults scroll() throws HibernateException;
+
+ /**
+ * Get the results as an instance of {@link ScrollableResults} based on the
+ * given scroll mode.
+ *
+ * @param scrollMode Indicates the type of underlying database cursor to
+ * request.
+ * @return The {@link ScrollableResults} representing the matched
+ * query results.
+ */
+ public ScrollableResults scroll(ScrollMode scrollMode) throws HibernateException;
+
+ /**
+ * Convenience method to return a single instance that matches
+ * the query, or null if the query returns no results.
+ *
+ * @return the single result or <tt>null</tt>
+ * @throws HibernateException if there is more than one matching result
+ */
+ public Object uniqueResult() throws HibernateException;
+
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/DuplicateMappingException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/DuplicateMappingException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/DuplicateMappingException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,32 @@
+package org.hibernate;
+
+/**
+ * Raised whenever a duplicate for a certain type occurs.
+ * Duplicate class, table, property name etc.
+ *
+ * @author Max Rydahl Andersen
+ *
+ */
+public class DuplicateMappingException extends MappingException {
+
+ private final String name;
+ private final String type;
+
+ public DuplicateMappingException(String customMessage, String type, String name) {
+ super(customMessage);
+ this.type=type;
+ this.name=name;
+ }
+
+ public DuplicateMappingException(String type, String name) {
+ this("Duplicate " + type + " mapping " + name, type, name);
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public String getName() {
+ return name;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/EmptyInterceptor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/EmptyInterceptor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/EmptyInterceptor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,98 @@
+//$Id: EmptyInterceptor.java 7859 2005-08-11 21:57:33Z oneovthafew $
+package org.hibernate;
+
+import java.io.Serializable;
+import java.util.Iterator;
+
+import org.hibernate.type.Type;
+
+/**
+ * An interceptor that does nothing. May be used as a base class
+ * for application-defined custom interceptors.
+ *
+ * @author Gavin King
+ */
+public class EmptyInterceptor implements Interceptor, Serializable {
+
+ public static final Interceptor INSTANCE = new EmptyInterceptor();
+
+ protected EmptyInterceptor() {}
+
+ public void onDelete(
+ Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {}
+
+ public boolean onFlushDirty(
+ Object entity,
+ Serializable id,
+ Object[] currentState,
+ Object[] previousState,
+ String[] propertyNames,
+ Type[] types) {
+ return false;
+ }
+
+ public boolean onLoad(
+ Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+ return false;
+ }
+
+ public boolean onSave(
+ Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+ return false;
+ }
+
+ public void postFlush(Iterator entities) {}
+ public void preFlush(Iterator entities) {}
+
+ public Boolean isTransient(Object entity) {
+ return null;
+ }
+
+ public Object instantiate(String entityName, EntityMode entityMode, Serializable id) {
+ return null;
+ }
+
+ public int[] findDirty(Object entity,
+ Serializable id,
+ Object[] currentState,
+ Object[] previousState,
+ String[] propertyNames,
+ Type[] types) {
+ return null;
+ }
+
+ public String getEntityName(Object object) {
+ return null;
+ }
+
+ public Object getEntity(String entityName, Serializable id) {
+ return null;
+ }
+
+ public void afterTransactionBegin(Transaction tx) {}
+ public void afterTransactionCompletion(Transaction tx) {}
+ public void beforeTransactionCompletion(Transaction tx) {}
+
+ public String onPrepareStatement(String sql) {
+ return sql;
+ }
+
+ public void onCollectionRemove(Object collection, Serializable key) throws CallbackException {}
+
+ public void onCollectionRecreate(Object collection, Serializable key) throws CallbackException {}
+
+ public void onCollectionUpdate(Object collection, Serializable key) throws CallbackException {}
+
+}
\ No newline at end of file
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/EmptyInterceptor.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/EntityMode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/EntityMode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/EntityMode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,49 @@
+// $Id: EntityMode.java 8697 2005-11-29 14:29:24Z steveebersole $
+package org.hibernate;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.io.Serializable;
+
+/**
+ * Defines the representation modes available for entities.
+ *
+ * @author Steve Ebersole
+ */
+public class EntityMode implements Serializable {
+
+ private static final Map INSTANCES = new HashMap();
+
+ public static final EntityMode POJO = new EntityMode( "pojo" );
+ public static final EntityMode DOM4J = new EntityMode( "dom4j" );
+ public static final EntityMode MAP = new EntityMode( "dynamic-map" );
+
+ static {
+ INSTANCES.put( POJO.name, POJO );
+ INSTANCES.put( DOM4J.name, DOM4J );
+ INSTANCES.put( MAP.name, MAP );
+ }
+
+ private final String name;
+
+ public EntityMode(String name) {
+ this.name = name;
+ }
+
+ public String toString() {
+ return name;
+ }
+
+ private Object readResolve() {
+ return INSTANCES.get( name );
+ }
+
+ public static EntityMode parse(String name) {
+ EntityMode rtn = ( EntityMode ) INSTANCES.get( name );
+ if ( rtn == null ) {
+ // default is POJO
+ rtn = POJO;
+ }
+ return rtn;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/FetchMode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/FetchMode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/FetchMode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,70 @@
+//$Id: FetchMode.java 5060 2004-12-24 03:11:05Z oneovthafew $
+package org.hibernate;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Represents an association fetching strategy. This is used
+ * together with the <tt>Criteria</tt> API to specify runtime
+ * fetching strategies.<br>
+ * <br>
+ * For HQL queries, use the <tt>FETCH</tt> keyword instead.
+ *
+ * @see Criteria#setFetchMode(java.lang.String, FetchMode)
+ * @author Gavin King
+ */
+public final class FetchMode implements Serializable {
+ private final String name;
+ private static final Map INSTANCES = new HashMap();
+
+ private FetchMode(String name) {
+ this.name=name;
+ }
+ public String toString() {
+ return name;
+ }
+ /**
+ * Default to the setting configured in the mapping file.
+ */
+ public static final FetchMode DEFAULT = new FetchMode("DEFAULT");
+
+ /**
+ * Fetch using an outer join. Equivalent to <tt>fetch="join"</tt>.
+ */
+ public static final FetchMode JOIN = new FetchMode("JOIN");
+ /**
+ * Fetch eagerly, using a separate select. Equivalent to
+ * <tt>fetch="select"</tt>.
+ */
+ public static final FetchMode SELECT = new FetchMode("SELECT");
+
+ /**
+ * Fetch lazily. Equivalent to <tt>outer-join="false"</tt>.
+ * @deprecated use <tt>FetchMode.SELECT</tt>
+ */
+ public static final FetchMode LAZY = SELECT;
+ /**
+ * Fetch eagerly, using an outer join. Equivalent to
+ * <tt>outer-join="true"</tt>.
+ * @deprecated use <tt>FetchMode.JOIN</tt>
+ */
+ public static final FetchMode EAGER = JOIN;
+
+ static {
+ INSTANCES.put( JOIN.name, JOIN );
+ INSTANCES.put( SELECT.name, SELECT );
+ INSTANCES.put( DEFAULT.name, DEFAULT );
+ }
+
+ private Object readResolve() {
+ return INSTANCES.get(name);
+ }
+
+}
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/Filter.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/Filter.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/Filter.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,68 @@
+// $Id: Filter.java 8754 2005-12-05 23:36:59Z steveebersole $
+package org.hibernate;
+
+import org.hibernate.engine.FilterDefinition;
+
+import java.util.Collection;
+
+/**
+ * Type definition of Filter. Filter defines the user's view into enabled dynamic filters,
+ * allowing them to set filter parameter values.
+ *
+ * @author Steve Ebersole
+ */
+public interface Filter {
+
+ /**
+ * Get the name of this filter.
+ *
+ * @return This filter's name.
+ */
+ public String getName();
+
+ /**
+ * Get the filter definition containing additional information about the
+ * filter (such as default-condition and expected parameter names/types).
+ *
+ * @return The filter definition
+ */
+ public FilterDefinition getFilterDefinition();
+
+
+ /**
+ * Set the named parameter's value for this filter.
+ *
+ * @param name The parameter's name.
+ * @param value The value to be applied.
+ * @return This FilterImpl instance (for method chaining).
+ */
+ public Filter setParameter(String name, Object value);
+
+ /**
+ * Set the named parameter's value list for this filter. Used
+ * in conjunction with IN-style filter criteria.
+ *
+ * @param name The parameter's name.
+ * @param values The values to be expanded into an SQL IN list.
+ * @return This FilterImpl instance (for method chaining).
+ */
+ public Filter setParameterList(String name, Collection values);
+
+ /**
+ * Set the named parameter's value list for this filter. Used
+ * in conjunction with IN-style filter criteria.
+ *
+ * @param name The parameter's name.
+ * @param values The values to be expanded into an SQL IN list.
+ * @return This FilterImpl instance (for method chaining).
+ */
+ public Filter setParameterList(String name, Object[] values);
+
+ /**
+ * Perform validation of the filter state. This is used to verify the
+ * state of the filter after its enablement and before its use.
+ *
+ * @throws HibernateException If the state is not currently valid.
+ */
+ public void validate() throws HibernateException;
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/FlushMode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/FlushMode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/FlushMode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,92 @@
+//$Id: FlushMode.java 10469 2006-09-08 12:23:18Z steve.ebersole at jboss.com $
+package org.hibernate;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Represents a flushing strategy. The flush process synchronizes
+ * database state with session state by detecting state changes
+ * and executing SQL statements.
+ *
+ * @see Session#setFlushMode(FlushMode)
+ * @see Query#setFlushMode(FlushMode)
+ * @see Criteria#setFlushMode(FlushMode)
+ *
+ * @author Gavin King
+ */
+public final class FlushMode implements Serializable {
+ private static final Map INSTANCES = new HashMap();
+
+ private final int level;
+ private final String name;
+
+ private FlushMode(int level, String name) {
+ this.level = level;
+ this.name = name;
+ }
+
+ public String toString() {
+ return name;
+ }
+
+ /**
+ * The {@link Session} is never flushed unless {@link Session#flush}
+ * is explicitly called by the application. This mode is very
+ * efficient for read only transactions.
+ *
+ * @deprecated use {@link #MANUAL} instead.
+ */
+ public static final FlushMode NEVER = new FlushMode( 0, "NEVER" );
+
+ /**
+ * The {@link Session} is only ever flushed when {@link Session#flush}
+ * is explicitly called by the application. This mode is very
+ * efficient for read only transactions.
+ */
+ public static final FlushMode MANUAL = new FlushMode( 0, "MANUAL" );
+
+ /**
+ * The {@link Session} is flushed when {@link Transaction#commit}
+ * is called.
+ */
+ public static final FlushMode COMMIT = new FlushMode(5, "COMMIT");
+
+ /**
+ * The {@link Session} is sometimes flushed before query execution
+ * in order to ensure that queries never return stale state. This
+ * is the default flush mode.
+ */
+ public static final FlushMode AUTO = new FlushMode(10, "AUTO");
+
+ /**
+ * The {@link Session} is flushed before every query. This is
+ * almost always unnecessary and inefficient.
+ */
+ public static final FlushMode ALWAYS = new FlushMode(20, "ALWAYS");
+
+ public boolean lessThan(FlushMode other) {
+ return this.level<other.level;
+ }
+
+ static {
+ INSTANCES.put( NEVER.name, NEVER );
+ INSTANCES.put( MANUAL.name, MANUAL );
+ INSTANCES.put( AUTO.name, AUTO );
+ INSTANCES.put( ALWAYS.name, ALWAYS );
+ INSTANCES.put( COMMIT.name, COMMIT );
+ }
+
+ public static boolean isManualFlushMode(FlushMode mode) {
+ return MANUAL.level == mode.level;
+ }
+
+ private Object readResolve() {
+ return INSTANCES.get( name );
+ }
+
+ public static FlushMode parse(String name) {
+ return ( FlushMode ) INSTANCES.get( name );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/Hibernate.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/Hibernate.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/Hibernate.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,454 @@
+//$Id: Hibernate.java 10009 2006-06-10 03:24:05Z epbernard $
+package org.hibernate;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.io.Serializable;
+import java.sql.Blob;
+import java.sql.Clob;
+import java.util.Iterator;
+import java.util.Properties;
+
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.engine.HibernateIterator;
+import org.hibernate.intercept.FieldInterceptionHelper;
+import org.hibernate.intercept.FieldInterceptor;
+import org.hibernate.lob.BlobImpl;
+import org.hibernate.lob.ClobImpl;
+import org.hibernate.lob.SerializableBlob;
+import org.hibernate.lob.SerializableClob;
+import org.hibernate.proxy.HibernateProxy;
+import org.hibernate.proxy.LazyInitializer;
+import org.hibernate.type.AnyType;
+import org.hibernate.type.BigDecimalType;
+import org.hibernate.type.BigIntegerType;
+import org.hibernate.type.BinaryType;
+import org.hibernate.type.BlobType;
+import org.hibernate.type.BooleanType;
+import org.hibernate.type.ByteType;
+import org.hibernate.type.CalendarDateType;
+import org.hibernate.type.CalendarType;
+import org.hibernate.type.CharacterType;
+import org.hibernate.type.ClassType;
+import org.hibernate.type.ClobType;
+import org.hibernate.type.CompositeCustomType;
+import org.hibernate.type.CurrencyType;
+import org.hibernate.type.CustomType;
+import org.hibernate.type.DateType;
+import org.hibernate.type.DoubleType;
+import org.hibernate.type.FloatType;
+import org.hibernate.type.IntegerType;
+import org.hibernate.type.LocaleType;
+import org.hibernate.type.LongType;
+import org.hibernate.type.ManyToOneType;
+import org.hibernate.type.NullableType;
+import org.hibernate.type.SerializableType;
+import org.hibernate.type.ShortType;
+import org.hibernate.type.StringType;
+import org.hibernate.type.TextType;
+import org.hibernate.type.TimeType;
+import org.hibernate.type.TimeZoneType;
+import org.hibernate.type.TimestampType;
+import org.hibernate.type.TrueFalseType;
+import org.hibernate.type.Type;
+import org.hibernate.type.YesNoType;
+import org.hibernate.type.CharArrayType;
+import org.hibernate.type.WrapperBinaryType;
+import org.hibernate.type.CharacterArrayType;
+import org.hibernate.usertype.CompositeUserType;
+
+/**
+ * <ul>
+ * <li>Provides access to the full range of Hibernate built-in types. <tt>Type</tt>
+ * instances may be used to bind values to query parameters.
+ * <li>A factory for new <tt>Blob</tt>s and <tt>Clob</tt>s.
+ * <li>Defines static methods for manipulation of proxies.
+ * </ul>
+ *
+ * @author Gavin King
+ * @see java.sql.Clob
+ * @see java.sql.Blob
+ * @see org.hibernate.type.Type
+ */
+
+public final class Hibernate {
+
+ /**
+ * Hibernate <tt>long</tt> type.
+ */
+ public static final NullableType LONG = new LongType();
+ /**
+ * Hibernate <tt>short</tt> type.
+ */
+ public static final NullableType SHORT = new ShortType();
+ /**
+ * Hibernate <tt>integer</tt> type.
+ */
+ public static final NullableType INTEGER = new IntegerType();
+ /**
+ * Hibernate <tt>byte</tt> type.
+ */
+ public static final NullableType BYTE = new ByteType();
+ /**
+ * Hibernate <tt>float</tt> type.
+ */
+ public static final NullableType FLOAT = new FloatType();
+ /**
+ * Hibernate <tt>double</tt> type.
+ */
+ public static final NullableType DOUBLE = new DoubleType();
+ /**
+ * Hibernate <tt>character</tt> type.
+ */
+ public static final NullableType CHARACTER = new CharacterType();
+ /**
+ * Hibernate <tt>string</tt> type.
+ */
+ public static final NullableType STRING = new StringType();
+ /**
+ * Hibernate <tt>time</tt> type.
+ */
+ public static final NullableType TIME = new TimeType();
+ /**
+ * Hibernate <tt>date</tt> type.
+ */
+ public static final NullableType DATE = new DateType();
+ /**
+ * Hibernate <tt>timestamp</tt> type.
+ */
+ public static final NullableType TIMESTAMP = new TimestampType();
+ /**
+ * Hibernate <tt>boolean</tt> type.
+ */
+ public static final NullableType BOOLEAN = new BooleanType();
+ /**
+ * Hibernate <tt>true_false</tt> type.
+ */
+ public static final NullableType TRUE_FALSE = new TrueFalseType();
+ /**
+ * Hibernate <tt>yes_no</tt> type.
+ */
+ public static final NullableType YES_NO = new YesNoType();
+ /**
+ * Hibernate <tt>big_decimal</tt> type.
+ */
+ public static final NullableType BIG_DECIMAL = new BigDecimalType();
+ /**
+ * Hibernate <tt>big_integer</tt> type.
+ */
+ public static final NullableType BIG_INTEGER = new BigIntegerType();
+ /**
+ * Hibernate <tt>binary</tt> type.
+ */
+ public static final NullableType BINARY = new BinaryType();
+ /**
+ * Hibernate <tt>wrapper-binary</tt> type.
+ */
+ public static final NullableType WRAPPER_BINARY = new WrapperBinaryType();
+ /**
+ * Hibernate char[] type.
+ */
+ public static final NullableType CHAR_ARRAY = new CharArrayType();
+ /**
+ * Hibernate Character[] type.
+ */
+ public static final NullableType CHARACTER_ARRAY = new CharacterArrayType();
+ /**
+ * Hibernate <tt>text</tt> type.
+ */
+ public static final NullableType TEXT = new TextType();
+ /**
+ * Hibernate <tt>blob</tt> type.
+ */
+ public static final Type BLOB = new BlobType();
+ /**
+ * Hibernate <tt>clob</tt> type.
+ */
+ public static final Type CLOB = new ClobType();
+ /**
+ * Hibernate <tt>calendar</tt> type.
+ */
+ public static final NullableType CALENDAR = new CalendarType();
+ /**
+ * Hibernate <tt>calendar_date</tt> type.
+ */
+ public static final NullableType CALENDAR_DATE = new CalendarDateType();
+ /**
+ * Hibernate <tt>locale</tt> type.
+ */
+ public static final NullableType LOCALE = new LocaleType();
+ /**
+ * Hibernate <tt>currency</tt> type.
+ */
+ public static final NullableType CURRENCY = new CurrencyType();
+ /**
+ * Hibernate <tt>timezone</tt> type.
+ */
+ public static final NullableType TIMEZONE = new TimeZoneType();
+ /**
+ * Hibernate <tt>class</tt> type.
+ */
+ public static final NullableType CLASS = new ClassType();
+ /**
+ * Hibernate <tt>serializable</tt> type.
+ */
+ public static final NullableType SERIALIZABLE = new SerializableType( Serializable.class );
+ /**
+ * Hibernate <tt>object</tt> type.
+ */
+ public static final Type OBJECT = new AnyType();
+
+
+ /**
+ * Cannot be instantiated.
+ */
+ private Hibernate() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * A Hibernate <tt>serializable</tt> type.
+ */
+ public static Type serializable(Class serializableClass) {
+ return new SerializableType( serializableClass );
+ }
+
+ /**
+ * A Hibernate <tt>any</tt> type.
+ *
+ * @param metaType a type mapping <tt>java.lang.Class</tt> to a single column
+ * @param identifierType the entity identifier type
+ * @return the Type
+ */
+ public static Type any(Type metaType, Type identifierType) {
+ return new AnyType( metaType, identifierType );
+ }
+
+ /**
+ * A Hibernate persistent object (entity) type.
+ *
+ * @param persistentClass a mapped entity class
+ */
+ public static Type entity(Class persistentClass) {
+ // not really a many-to-one association *necessarily*
+ return new ManyToOneType( persistentClass.getName() );
+ }
+
+ /**
+ * A Hibernate persistent object (entity) type.
+ *
+ * @param entityName a mapped entity class
+ */
+ public static Type entity(String entityName) {
+ // not really a many-to-one association *necessarily*
+ return new ManyToOneType( entityName );
+ }
+
+ /**
+ * A Hibernate custom type.
+ *
+ * @param userTypeClass a class that implements <tt>UserType</tt>
+ */
+ public static Type custom(Class userTypeClass) throws HibernateException {
+ return custom( userTypeClass, null );
+ }
+
+ /**
+ * A Hibernate parameterizable custom type.
+ *
+ * @param userTypeClass a class that implements <tt>UserType and ParameterizableType</tt>
+ * @param parameterNames the names of the parameters passed to the type
+ * @param parameterValues the values of the parameters passed to the type. They must match
+ * up with the order and length of the parameterNames array.
+ */
+ public static Type custom(Class userTypeClass, String[] parameterNames, String[] parameterValues)
+ throws HibernateException {
+ Properties parameters = new Properties();
+ for ( int i = 0; i < parameterNames.length; i++ ) {
+ parameters.setProperty( parameterNames[i], parameterValues[i] );
+ }
+ return custom( userTypeClass, parameters );
+ }
+
+ /**
+ * A Hibernate parameterizable custom type.
+ *
+ * @param userTypeClass a class that implements <tt>UserType and ParameterizableType</tt>
+ * @param parameters the parameters as a collection of name/value pairs
+ */
+ public static Type custom(Class userTypeClass, Properties parameters)
+ throws HibernateException {
+ if ( CompositeUserType.class.isAssignableFrom( userTypeClass ) ) {
+ CompositeCustomType type = new CompositeCustomType( userTypeClass, parameters );
+ return type;
+ }
+ else {
+ CustomType type = new CustomType( userTypeClass, parameters );
+ return type;
+ }
+ }
+
+ /**
+ * Force initialization of a proxy or persistent collection.
+ * <p/>
+ * Note: This only ensures intialization of a proxy object or collection;
+ * it is not guaranteed that the elements INSIDE the collection will be initialized/materialized.
+ *
+ * @param proxy a persistable object, proxy, persistent collection or <tt>null</tt>
+ * @throws HibernateException if we can't initialize the proxy at this time, eg. the <tt>Session</tt> was closed
+ */
+ public static void initialize(Object proxy) throws HibernateException {
+ if ( proxy == null ) {
+ return;
+ }
+ else if ( proxy instanceof HibernateProxy ) {
+ ( ( HibernateProxy ) proxy ).getHibernateLazyInitializer().initialize();
+ }
+ else if ( proxy instanceof PersistentCollection ) {
+ ( ( PersistentCollection ) proxy ).forceInitialization();
+ }
+ }
+
+ /**
+ * Check if the proxy or persistent collection is initialized.
+ *
+ * @param proxy a persistable object, proxy, persistent collection or <tt>null</tt>
+ * @return true if the argument is already initialized, or is not a proxy or collection
+ */
+ public static boolean isInitialized(Object proxy) {
+ if ( proxy instanceof HibernateProxy ) {
+ return !( ( HibernateProxy ) proxy ).getHibernateLazyInitializer().isUninitialized();
+ }
+ else if ( proxy instanceof PersistentCollection ) {
+ return ( ( PersistentCollection ) proxy ).wasInitialized();
+ }
+ else {
+ return true;
+ }
+ }
+
+ /**
+ * Get the true, underlying class of a proxied persistent class. This operation
+ * will initialize a proxy by side-effect.
+ *
+ * @param proxy a persistable object or proxy
+ * @return the true class of the instance
+ * @throws HibernateException
+ */
+ public static Class getClass(Object proxy) {
+ if ( proxy instanceof HibernateProxy ) {
+ return ( ( HibernateProxy ) proxy ).getHibernateLazyInitializer()
+ .getImplementation()
+ .getClass();
+ }
+ else {
+ return proxy.getClass();
+ }
+ }
+
+ /**
+ * Create a new <tt>Blob</tt>. The returned object will be initially immutable.
+ *
+ * @param bytes a byte array
+ * @return the Blob
+ */
+ public static Blob createBlob(byte[] bytes) {
+ return new SerializableBlob( new BlobImpl( bytes ) );
+ }
+
+ /**
+ * Create a new <tt>Blob</tt>. The returned object will be initially immutable.
+ *
+ * @param stream a binary stream
+ * @param length the number of bytes in the stream
+ * @return the Blob
+ */
+ public static Blob createBlob(InputStream stream, int length) {
+ return new SerializableBlob( new BlobImpl( stream, length ) );
+ }
+
+ /**
+ * Create a new <tt>Blob</tt>. The returned object will be initially immutable.
+ *
+ * @param stream a binary stream
+ * @return the Blob
+ * @throws IOException
+ */
+ public static Blob createBlob(InputStream stream) throws IOException {
+ return new SerializableBlob( new BlobImpl( stream, stream.available() ) );
+ }
+
+ /**
+ * Create a new <tt>Clob</tt>. The returned object will be initially immutable.
+ *
+ * @param string a <tt>String</tt>
+ */
+ public static Clob createClob(String string) {
+ return new SerializableClob( new ClobImpl( string ) );
+ }
+
+ /**
+ * Create a new <tt>Clob</tt>. The returned object will be initially immutable.
+ *
+ * @param reader a character stream
+ * @param length the number of characters in the stream
+ */
+ public static Clob createClob(Reader reader, int length) {
+ return new SerializableClob( new ClobImpl( reader, length ) );
+ }
+
+ /**
+ * Close an <tt>Iterator</tt> created by <tt>iterate()</tt> immediately,
+ * instead of waiting until the session is closed or disconnected.
+ *
+ * @param iterator an <tt>Iterator</tt> created by <tt>iterate()</tt>
+ * @throws HibernateException
+ * @see org.hibernate.Query#iterate
+ * @see Query#iterate()
+ */
+ public static void close(Iterator iterator) throws HibernateException {
+ if ( iterator instanceof HibernateIterator ) {
+ ( ( HibernateIterator ) iterator ).close();
+ }
+ else {
+ throw new IllegalArgumentException( "not a Hibernate iterator" );
+ }
+ }
+
+ /**
+ * Check if the property is initialized. If the named property does not exist
+ * or is not persistent, this method always returns <tt>true</tt>.
+ *
+ * @param proxy The potential proxy
+ * @param propertyName the name of a persistent attribute of the object
+ * @return true if the named property of the object is not listed as uninitialized
+ * @return false if the object is an uninitialized proxy, or the named property is uninitialized
+ */
+ public static boolean isPropertyInitialized(Object proxy, String propertyName) {
+
+ Object entity;
+ if ( proxy instanceof HibernateProxy ) {
+ LazyInitializer li = ( ( HibernateProxy ) proxy ).getHibernateLazyInitializer();
+ if ( li.isUninitialized() ) {
+ return false;
+ }
+ else {
+ entity = li.getImplementation();
+ }
+ }
+ else {
+ entity = proxy;
+ }
+
+ if ( FieldInterceptionHelper.isInstrumented( entity ) ) {
+ FieldInterceptor interceptor = FieldInterceptionHelper.extractFieldInterceptor( entity );
+ return interceptor == null || interceptor.isInitialized( propertyName );
+ }
+ else {
+ return true;
+ }
+
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/HibernateException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/HibernateException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/HibernateException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,34 @@
+//$Id: HibernateException.java 5683 2005-02-12 03:09:22Z oneovthafew $
+package org.hibernate;
+
+import org.hibernate.exception.NestableRuntimeException;
+
+/**
+ * Any exception that occurs inside the persistence layer
+ * or JDBC driver. <tt>SQLException</tt>s are always wrapped
+ * by instances of <tt>JDBCException</tt>.
+ *
+ * @see JDBCException
+ * @author Gavin King
+ */
+
+public class HibernateException extends NestableRuntimeException {
+
+ public HibernateException(Throwable root) {
+ super(root);
+ }
+
+ public HibernateException(String string, Throwable root) {
+ super(string, root);
+ }
+
+ public HibernateException(String s) {
+ super(s);
+ }
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/InstantiationException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/InstantiationException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/InstantiationException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,44 @@
+//$Id: InstantiationException.java 6781 2005-05-14 17:27:57Z oneovthafew $
+package org.hibernate;
+
+/**
+ * Thrown if Hibernate can't instantiate an entity or component
+ * class at runtime.
+ *
+ * @author Gavin King
+ */
+
+public class InstantiationException extends HibernateException {
+
+ private final Class clazz;
+
+ public InstantiationException(String s, Class clazz, Throwable root) {
+ super(s, root);
+ this.clazz = clazz;
+ }
+
+ public InstantiationException(String s, Class clazz) {
+ super(s);
+ this.clazz = clazz;
+ }
+
+ public InstantiationException(String s, Class clazz, Exception e) {
+ super(s, e);
+ this.clazz = clazz;
+ }
+
+ public Class getPersistentClass() {
+ return clazz;
+ }
+
+ public String getMessage() {
+ return super.getMessage() + clazz.getName();
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/Interceptor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/Interceptor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/Interceptor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,155 @@
+//$Id: Interceptor.java 7883 2005-08-12 20:03:07Z oneovthafew $
+package org.hibernate;
+
+import java.io.Serializable;
+import java.util.Iterator;
+
+import org.hibernate.type.Type;
+
+/**
+ * Allows user code to inspect and/or change property values.
+ * <br><br>
+ * Inspection occurs before property values are written and after they are read
+ * from the database.<br>
+ * <br>
+ * There might be a single instance of <tt>Interceptor</tt> for a <tt>SessionFactory</tt>, or a new instance
+ * might be specified for each <tt>Session</tt>. Whichever approach is used, the interceptor must be
+ * serializable if the <tt>Session</tt> is to be serializable. This means that <tt>SessionFactory</tt>-scoped
+ * interceptors should implement <tt>readResolve()</tt>.<br>
+ * <br>
+ * The <tt>Session</tt> may not be invoked from a callback (nor may a callback cause a collection or proxy to
+ * be lazily initialized).<br>
+ * <br>
+ * Instead of implementing this interface directly, it is usually better to extend <tt>EmptyInterceptor</tt>
+ * and override only the callback methods of interest.
+ *
+ * @see SessionFactory#openSession(Interceptor)
+ * @see org.hibernate.cfg.Configuration#setInterceptor(Interceptor)
+ * @see EmptyInterceptor
+ * @author Gavin King
+ */
+public interface Interceptor {
+ /**
+ * Called just before an object is initialized. The interceptor may change the <tt>state</tt>, which will
+ * be propagated to the persistent object. Note that when this method is called, <tt>entity</tt> will be
+ * an empty uninitialized instance of the class.
+ *
+ * @return <tt>true</tt> if the user modified the <tt>state</tt> in any way.
+ */
+ public boolean onLoad(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) throws CallbackException;
+ /**
+ * Called when an object is detected to be dirty, during a flush. The interceptor may modify the detected
+ * <tt>currentState</tt>, which will be propagated to both the database and the persistent object.
+ * Note that not all flushes end in actual synchronization with the database, in which case the
+ * new <tt>currentState</tt> will be propagated to the object, but not necessarily (immediately) to
+ * the database. It is strongly recommended that the interceptor <b>not</b> modify the <tt>previousState</tt>.
+ *
+ * @return <tt>true</tt> if the user modified the <tt>currentState</tt> in any way.
+ */
+ public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) throws CallbackException;
+ /**
+ * Called before an object is saved. The interceptor may modify the <tt>state</tt>, which will be used for
+ * the SQL <tt>INSERT</tt> and propagated to the persistent object.
+ *
+ * @return <tt>true</tt> if the user modified the <tt>state</tt> in any way.
+ */
+ public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) throws CallbackException;
+ /**
+ * Called before an object is deleted. It is not recommended that the interceptor modify the <tt>state</tt>.
+ */
+ public void onDelete(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) throws CallbackException;
+ /**
+ * Called before a collection is (re)created.
+ */
+ public void onCollectionRecreate(Object collection, Serializable key) throws CallbackException;
+ /**
+ * Called before a collection is deleted.
+ */
+ public void onCollectionRemove(Object collection, Serializable key) throws CallbackException;
+ /**
+ * Called before a collection is updated.
+ */
+ public void onCollectionUpdate(Object collection, Serializable key) throws CallbackException;
+ /**
+ * Called before a flush
+ */
+ public void preFlush(Iterator entities) throws CallbackException;
+ /**
+ * Called after a flush that actually ends in execution of the SQL statements required to synchronize
+ * in-memory state with the database.
+ */
+ public void postFlush(Iterator entities) throws CallbackException;
+ /**
+ * Called to distinguish between transient and detached entities. The return value determines the
+ * state of the entity with respect to the current session.
+ * <ul>
+ * <li><tt>Boolean.TRUE</tt> - the entity is transient
+ * <li><tt>Boolean.FALSE</tt> - the entity is detached
+ * <li><tt>null</tt> - Hibernate uses the <tt>unsaved-value</tt> mapping and other heuristics to
+ * determine if the object is unsaved
+ * </ul>
+ * @param entity a transient or detached entity
+ * @return Boolean or <tt>null</tt> to choose default behaviour
+ */
+ public Boolean isTransient(Object entity);
+ /**
+ * Called from <tt>flush()</tt>. The return value determines whether the entity is updated
+ * <ul>
+ * <li>an array of property indices - the entity is dirty
+ * <li>an empty array - the entity is not dirty
+ * <li><tt>null</tt> - use Hibernate's default dirty-checking algorithm
+ * </ul>
+ * @param entity a persistent entity
+ * @return array of dirty property indices or <tt>null</tt> to choose default behaviour
+ */
+ public int[] findDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types);
+ /**
+ * Instantiate the entity class. Return <tt>null</tt> to indicate that Hibernate should use
+ * the default constructor of the class. The identifier property of the returned instance
+ * should be initialized with the given identifier.
+ *
+ * @param entityName the name of the entity
+ * @param entityMode The type of entity instance to be returned.
+ * @param id the identifier of the new instance
+ * @return an instance of the class, or <tt>null</tt> to choose default behaviour
+ */
+ public Object instantiate(String entityName, EntityMode entityMode, Serializable id) throws CallbackException;
+
+ /**
+ * Get the entity name for a persistent or transient instance
+ * @param object an entity instance
+ * @return the name of the entity
+ */
+ public String getEntityName(Object object) throws CallbackException;
+
+ /**
+ * Get a fully loaded entity instance that is cached externally
+ * @param entityName the name of the entity
+ * @param id the instance identifier
+ * @return a fully initialized entity
+ * @throws CallbackException
+ */
+ public Object getEntity(String entityName, Serializable id) throws CallbackException;
+
+ /**
+ * Called when a Hibernate transaction is begun via the Hibernate <tt>Transaction</tt>
+ * API. Will not be called if transactions are being controlled via some other
+ * mechanism (CMT, for example).
+ */
+ public void afterTransactionBegin(Transaction tx);
+ /**
+ * Called before a transaction is committed (but not before rollback).
+ */
+ public void beforeTransactionCompletion(Transaction tx);
+ /**
+ * Called after a transaction is committed or rolled back.
+ */
+ public void afterTransactionCompletion(Transaction tx);
+
+ /**
+ * Called when sql string is being prepared.
+ * @param sql sql to be prepared
+ * @return original or modified sql
+ */
+ public String onPrepareStatement(String sql);
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/InvalidMappingException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/InvalidMappingException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/InvalidMappingException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,42 @@
+package org.hibernate;
+
+/**
+ * Thrown when a mapping is found to be invalid.
+ * Similar to MappingException, but this contains more info about the path and type of mapping (e.g. file, resource or url)
+ *
+ * @author Max Rydahl Andersen
+ *
+ */
+public class InvalidMappingException extends MappingException {
+
+ private final String path;
+ private final String type;
+
+ public InvalidMappingException(String customMessage, String type, String path, Throwable cause) {
+ super(customMessage, cause);
+ this.type=type;
+ this.path=path;
+ }
+
+ public InvalidMappingException(String customMessage, String type, String path) {
+ super(customMessage);
+ this.type=type;
+ this.path=path;
+ }
+
+ public InvalidMappingException(String type, String path) {
+ this("Could not parse mapping document from " + type + (path==null?"":" " + path), type, path);
+ }
+
+ public InvalidMappingException(String type, String path, Throwable cause) {
+ this("Could not parse mapping document from " + type + (path==null?"":" " + path), type, path, cause);
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public String getPath() {
+ return path;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/JDBCException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/JDBCException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/JDBCException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,63 @@
+//$Id: JDBCException.java 4626 2004-09-27 15:24:38Z oneovthafew $
+package org.hibernate;
+
+import java.sql.SQLException;
+
+
+/**
+ * Wraps an <tt>SQLException</tt>. Indicates that an exception
+ * occurred during a JDBC call.
+ *
+ * @see java.sql.SQLException
+ * @author Gavin King
+ */
+public class JDBCException extends HibernateException {
+
+ private SQLException sqle;
+ private String sql;
+
+ public JDBCException(String string, SQLException root) {
+ super(string, root);
+ sqle=root;
+ }
+
+ public JDBCException(String string, SQLException root, String sql) {
+ this(string, root);
+ this.sql = sql;
+ }
+
+ /**
+ * Get the SQLState of the underlying <tt>SQLException</tt>.
+ * @see java.sql.SQLException
+ * @return String
+ */
+ public String getSQLState() {
+ return sqle.getSQLState();
+ }
+
+ /**
+ * Get the <tt>errorCode</tt> of the underlying <tt>SQLException</tt>.
+ * @see java.sql.SQLException
+ * @return int the error code
+ */
+ public int getErrorCode() {
+ return sqle.getErrorCode();
+ }
+
+ /**
+ * Get the underlying <tt>SQLException</tt>.
+ * @return SQLException
+ */
+ public SQLException getSQLException() {
+ return sqle;
+ }
+
+ /**
+ * Get the actual SQL statement that caused the exception
+ * (may be null)
+ */
+ public String getSQL() {
+ return sql;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/LazyInitializationException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/LazyInitializationException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/LazyInitializationException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,28 @@
+//$Id: LazyInitializationException.java 4458 2004-08-29 09:59:17Z oneovthafew $
+package org.hibernate;
+
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Indicates access to unfetched data outside of a session context.
+ * For example, when an uninitialized proxy or collection is accessed
+ * after the session was closed.
+ *
+ * @see Hibernate#initialize(java.lang.Object)
+ * @see Hibernate#isInitialized(java.lang.Object)
+ * @author Gavin King
+ */
+public class LazyInitializationException extends HibernateException {
+
+ public LazyInitializationException(String msg) {
+ super(msg);
+ LogFactory.getLog(LazyInitializationException.class).error(msg, this);
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/LockMode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/LockMode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/LockMode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,106 @@
+//$Id: LockMode.java 9581 2006-03-09 15:50:15Z epbernard $
+package org.hibernate;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Instances represent a lock mode for a row of a relational
+ * database table. It is not intended that users spend much
+ * time worrying about locking since Hibernate usually
+ * obtains exactly the right lock level automatically.
+ * Some "advanced" users may wish to explicitly specify lock
+ * levels.
+ *
+ * @see Session#lock(Object,LockMode)
+ * @author Gavin King
+ */
+public final class LockMode implements Serializable {
+ private final int level;
+ private final String name;
+ private static final Map INSTANCES = new HashMap();
+
+ private LockMode(int level, String name) {
+ this.level=level;
+ this.name=name;
+ }
+ public String toString() {
+ return name;
+ }
+ /**
+ * Check if this lock mode is more restrictive than the given lock mode.
+ *
+ * @param mode LockMode to check
+ * @return true if this lock mode is more restrictive than given lock mode
+ */
+ public boolean greaterThan(LockMode mode) {
+ return level > mode.level;
+ }
+ /**
+ * Check if this lock mode is less restrictive than the given lock mode.
+ *
+ * @param mode LockMode to check
+ * @return true if this lock mode is less restrictive than given lock mode
+ */
+ public boolean lessThan(LockMode mode) {
+ return level < mode.level;
+ }
+ /**
+ * No lock required. If an object is requested with this lock
+ * mode, a <tt>READ</tt> lock will be obtained if it is
+ * necessary to actually read the state from the database,
+ * rather than pull it from a cache.<br>
+ * <br>
+ * This is the "default" lock mode.
+ */
+ public static final LockMode NONE = new LockMode(0, "NONE");
+ /**
+ * A shared lock. Objects in this lock mode were read from
+ * the database in the current transaction, rather than being
+ * pulled from a cache.
+ */
+ public static final LockMode READ = new LockMode(5, "READ");
+ /**
+ * An upgrade lock. Objects loaded in this lock mode are
+ * materialized using an SQL <tt>select ... for update</tt>.
+ */
+ public static final LockMode UPGRADE = new LockMode(10, "UPGRADE");
+ /**
+ * Attempt to obtain an upgrade lock, using an Oracle-style
+ * <tt>select for update nowait</tt>. The semantics of
+ * this lock mode, once obtained, are the same as
+ * <tt>UPGRADE</tt>.
+ */
+ public static final LockMode UPGRADE_NOWAIT = new LockMode(10, "UPGRADE_NOWAIT");
+ /**
+ * A <tt>WRITE</tt> lock is obtained when an object is updated
+ * or inserted. This lock mode is for internal use only and is
+ * not a valid mode for <tt>load()</tt> or <tt>lock()</tt> (both
+ * of which throw exceptions if WRITE is specified).
+ */
+ public static final LockMode WRITE = new LockMode(10, "WRITE");
+
+ /**
+ * Similiar to {@link #UPGRADE} except that, for versioned entities,
+ * it results in a forced version increment.
+ */
+ public static final LockMode FORCE = new LockMode( 15, "FORCE" );
+
+ static {
+ INSTANCES.put( NONE.name, NONE );
+ INSTANCES.put( READ.name, READ );
+ INSTANCES.put( UPGRADE.name, UPGRADE );
+ INSTANCES.put( UPGRADE_NOWAIT.name, UPGRADE_NOWAIT );
+ INSTANCES.put( WRITE.name, WRITE );
+ INSTANCES.put( FORCE.name, FORCE );
+ }
+
+ private Object readResolve() {
+ return parse( name );
+ }
+
+ public static LockMode parse(String name) {
+ return ( LockMode ) INSTANCES.get(name);
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/MappingException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/MappingException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/MappingException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,31 @@
+//$Id: MappingException.java 3890 2004-06-03 16:31:32Z steveebersole $
+package org.hibernate;
+
+/**
+ * An exception that usually occurs at configuration time, rather
+ * than runtime, as a result of something screwy in the O-R mappings.
+ *
+ * @author Gavin King
+ */
+
+public class MappingException extends HibernateException {
+
+ public MappingException(String msg, Throwable root) {
+ super( msg, root );
+ }
+
+ public MappingException(Throwable root) {
+ super(root);
+ }
+
+ public MappingException(String s) {
+ super(s);
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/MappingNotFoundException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/MappingNotFoundException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/MappingNotFoundException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,41 @@
+package org.hibernate;
+
+/**
+ * Thrown when a resource for a mapping could not be found.
+ *
+ * @author Max Rydahl Andersen
+ *
+ */
+public class MappingNotFoundException extends MappingException {
+
+ private final String path;
+ private final String type;
+
+ public MappingNotFoundException(String customMessage, String type, String path, Throwable cause) {
+ super(customMessage, cause);
+ this.type=type;
+ this.path=path;
+ }
+
+ public MappingNotFoundException(String customMessage, String type, String path) {
+ super(customMessage);
+ this.type=type;
+ this.path=path;
+ }
+
+ public MappingNotFoundException(String type, String path) {
+ this(type + ": " + path + " not found", type, path);
+ }
+
+ public MappingNotFoundException(String type, String path, Throwable cause) {
+ this(type + ": " + path + " not found", type, path, cause);
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public String getPath() {
+ return path;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/NonUniqueObjectException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/NonUniqueObjectException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/NonUniqueObjectException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,44 @@
+//$Id: NonUniqueObjectException.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate;
+
+import java.io.Serializable;
+
+import org.hibernate.pretty.MessageHelper;
+
+/**
+ * This exception is thrown when an operation would
+ * break session-scoped identity. This occurs if the
+ * user tries to associate two different instances of
+ * the same Java class with a particular identifier,
+ * in the scope of a single <tt>Session</tt>.
+ *
+ * @author Gavin King
+ */
+public class NonUniqueObjectException extends HibernateException {
+ private final Serializable identifier;
+ private final String entityName;
+
+ public NonUniqueObjectException(String message, Serializable id, String clazz) {
+ super(message);
+ this.entityName = clazz;
+ this.identifier = id;
+ }
+
+ public NonUniqueObjectException(Serializable id, String clazz) {
+ this("a different object with the same identifier value was already associated with the session", id, clazz);
+ }
+
+ public Serializable getIdentifier() {
+ return identifier;
+ }
+
+ public String getMessage() {
+ return super.getMessage() + ": " +
+ MessageHelper.infoString(entityName, identifier);
+ }
+
+ public String getEntityName() {
+ return entityName;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/NonUniqueResultException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/NonUniqueResultException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/NonUniqueResultException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,17 @@
+//$Id: NonUniqueResultException.java 3890 2004-06-03 16:31:32Z steveebersole $
+package org.hibernate;
+
+/**
+ * Thrown when the application calls <tt>Query.uniqueResult()</tt> and
+ * the query returned more than one result. Unlike all other Hibernate
+ * exceptions, this one is recoverable!
+ *
+ * @author Gavin King
+ */
+public class NonUniqueResultException extends HibernateException {
+
+ public NonUniqueResultException(int resultCount) {
+ super( "query did not return a unique result: " + resultCount );
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/ObjectDeletedException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/ObjectDeletedException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/ObjectDeletedException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,25 @@
+//$Id: ObjectDeletedException.java 3890 2004-06-03 16:31:32Z steveebersole $
+package org.hibernate;
+
+import java.io.Serializable;
+
+/**
+ * Thrown when the user tries to do something illegal with a deleted
+ * object.
+ *
+ * @author Gavin King
+ */
+public class ObjectDeletedException extends UnresolvableObjectException {
+
+ public ObjectDeletedException(String message, Serializable identifier, String clazz) {
+ super(message, identifier, clazz);
+ }
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/ObjectNotFoundException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/ObjectNotFoundException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/ObjectNotFoundException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,24 @@
+//$Id: ObjectNotFoundException.java 9855 2006-05-02 18:55:45Z steve.ebersole at jboss.com $
+package org.hibernate;
+
+import java.io.Serializable;
+
+/**
+ * Thrown when <tt>Session.load()</tt> fails to select a row with
+ * the given primary key (identifier value). This exception might not
+ * be thrown when <tt>load()</tt> is called, even if there was no
+ * row on the database, because <tt>load()</tt> returns a proxy if
+ * possible. Applications should use <tt>Session.get()</tt> to test if
+ * a row exists in the database.<br>
+ * <br>
+ * Like all Hibernate exceptions, this exception is considered
+ * unrecoverable.
+ *
+ * @author Gavin King
+ */
+public class ObjectNotFoundException extends UnresolvableObjectException {
+
+ public ObjectNotFoundException(Serializable identifier, String clazz) {
+ super(identifier, clazz);
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/PersistentObjectException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/PersistentObjectException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/PersistentObjectException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,21 @@
+//$Id: PersistentObjectException.java 6877 2005-05-23 15:00:25Z oneovthafew $
+package org.hibernate;
+
+/**
+ * Thrown when the user passes a persistent instance to a <tt>Session</tt>
+ * method that expects a transient instance.
+ *
+ * @author Gavin King
+ */
+public class PersistentObjectException extends HibernateException {
+
+ public PersistentObjectException(String s) {
+ super(s);
+ }
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/PropertyAccessException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/PropertyAccessException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/PropertyAccessException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,50 @@
+//$Id: PropertyAccessException.java 3890 2004-06-03 16:31:32Z steveebersole $
+package org.hibernate;
+
+import org.hibernate.util.StringHelper;
+
+/**
+ * A problem occurred accessing a property of an instance of a
+ * persistent class by reflection, or via CGLIB. There are a
+ * number of possible underlying causes, including
+ * <ul>
+ * <li>failure of a security check
+ * <li>an exception occurring inside the getter or setter method
+ * <li>a nullable database column was mapped to a primitive-type property
+ * <li>the Hibernate type was not castable to the property type (or vice-versa)
+ * </ul>
+ * @author Gavin King
+ */
+public class PropertyAccessException extends HibernateException {
+
+ private final Class persistentClass;
+ private final String propertyName;
+ private final boolean wasSetter;
+
+ public PropertyAccessException(Throwable root, String s, boolean wasSetter, Class persistentClass, String propertyName) {
+ super(s, root);
+ this.persistentClass = persistentClass;
+ this.wasSetter = wasSetter;
+ this.propertyName = propertyName;
+ }
+
+ public Class getPersistentClass() {
+ return persistentClass;
+ }
+
+ public String getPropertyName() {
+ return propertyName;
+ }
+
+ public String getMessage() {
+ return super.getMessage() +
+ ( wasSetter ? " setter of " : " getter of ") +
+ StringHelper.qualify( persistentClass.getName(), propertyName );
+ }
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/PropertyNotFoundException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/PropertyNotFoundException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/PropertyNotFoundException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+//$Id: PropertyNotFoundException.java 3890 2004-06-03 16:31:32Z steveebersole $
+package org.hibernate;
+
+/**
+ * Indicates that an expected getter or setter method could not be
+ * found on a class.
+ *
+ * @author Gavin King
+ */
+public class PropertyNotFoundException extends MappingException {
+
+ public PropertyNotFoundException(String s) {
+ super(s);
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/PropertyValueException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/PropertyValueException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/PropertyValueException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,56 @@
+//$Id: PropertyValueException.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate;
+
+import org.hibernate.util.StringHelper;
+
+/**
+ * Thrown when the (illegal) value of a property can not be persisted.
+ * There are two main causes:
+ * <ul>
+ * <li>a property declared <tt>not-null="true"</tt> is null
+ * <li>an association references an unsaved transient instance
+ * </ul>
+ * @author Gavin King
+ */
+public class PropertyValueException extends HibernateException {
+
+ private final String entityName;
+ private final String propertyName;
+
+ public PropertyValueException(String s, String entityName, String propertyName) {
+ super(s);
+ this.entityName = entityName;
+ this.propertyName = propertyName;
+ }
+
+ public String getEntityName() {
+ return entityName;
+ }
+
+ public String getPropertyName() {
+ return propertyName;
+ }
+
+ public String getMessage() {
+ return super.getMessage() + ": " +
+ StringHelper.qualify(entityName, propertyName);
+ }
+
+ /**
+ * Return a well formed property path.
+ * Basicaly, it will return parent.child
+ *
+ * @param parent parent in path
+ * @param child child in path
+ * @return parent-child path
+ */
+ public static String buildPropertyPath(String parent, String child) {
+ return new StringBuffer(parent).append('.').append(child).toString();
+ }
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/Query.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/Query.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/Query.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,386 @@
+//$Id: Query.java 10591 2006-10-17 08:57:26Z max.andersen at jboss.com $
+package org.hibernate;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import org.hibernate.transform.ResultTransformer;
+import org.hibernate.type.Type;
+
+/**
+ * An object-oriented representation of a Hibernate query. A <tt>Query</tt>
+ * instance is obtained by calling <tt>Session.createQuery()</tt>. This
+ * interface exposes some extra functionality beyond that provided by
+ * <tt>Session.iterate()</tt> and <tt>Session.find()</tt>:
+ * <ul>
+ * <li>a particular page of the result set may be selected by calling <tt>
+ * setMaxResults(), setFirstResult()</tt>
+ * <li>named query parameters may be used
+ * <li>the results may be returned as an instance of <tt>ScrollableResults</tt>
+ * </ul>
+ * <br>
+ * Named query parameters are tokens of the form <tt>:name</tt> in the
+ * query string. A value is bound to the <tt>integer</tt> parameter
+ * <tt>:foo</tt> by calling<br>
+ * <br>
+ * <tt>setParameter("foo", foo, Hibernate.INTEGER);</tt><br>
+ * <br>
+ * for example. A name may appear multiple times in the query string.<br>
+ * <br>
+ * JDBC-style <tt>?</tt> parameters are also supported. To bind a
+ * value to a JDBC-style parameter use a set method that accepts an
+ * <tt>int</tt> positional argument (numbered from zero, contrary
+ * to JDBC).<br>
+ * <br>
+ * You may not mix and match JDBC-style parameters and named parameters
+ * in the same query.<br>
+ * <br>
+ * Queries are executed by calling <tt>list()</tt>, <tt>scroll()</tt> or
+ * <tt>iterate()</tt>. A query may be re-executed by subsequent invocations.
+ * Its lifespan is, however, bounded by the lifespan of the <tt>Session</tt>
+ * that created it.<br>
+ * <br>
+ * Implementors are not intended to be threadsafe.
+ *
+ * @see org.hibernate.Session#createQuery(java.lang.String)
+ * @see org.hibernate.ScrollableResults
+ * @author Gavin King
+ */
+public interface Query {
+ /**
+ * Get the query string.
+ *
+ * @return the query string
+ */
+ public String getQueryString();
+ /**
+ * Return the Hibernate types of the query result set.
+ * @return an array of types
+ */
+ public Type[] getReturnTypes() throws HibernateException;
+ /**
+ * Return the HQL select clause aliases (if any)
+ * @return an array of aliases as strings
+ */
+ public String[] getReturnAliases() throws HibernateException;
+ /**
+ * Return the names of all named parameters of the query.
+ * @return the parameter names, in no particular order
+ */
+ public String[] getNamedParameters() throws HibernateException;
+ /**
+ * Return the query results as an <tt>Iterator</tt>. If the query
+ * contains multiple results pre row, the results are returned in
+ * an instance of <tt>Object[]</tt>.<br>
+ * <br>
+ * Entities returned as results are initialized on demand. The first
+ * SQL query returns identifiers only.<br>
+ *
+ * @return the result iterator
+ * @throws HibernateException
+ */
+ public Iterator iterate() throws HibernateException;
+ /**
+ * Return the query results as <tt>ScrollableResults</tt>. The
+ * scrollability of the returned results depends upon JDBC driver
+ * support for scrollable <tt>ResultSet</tt>s.<br>
+ *
+ * @see ScrollableResults
+ * @return the result iterator
+ * @throws HibernateException
+ */
+ public ScrollableResults scroll() throws HibernateException;
+ /**
+ * Return the query results as <tt>ScrollableResults</tt>. The
+ * scrollability of the returned results depends upon JDBC driver
+ * support for scrollable <tt>ResultSet</tt>s.<br>
+ *
+ * @see ScrollableResults
+ * @see ScrollMode
+ * @return the result iterator
+ * @throws HibernateException
+ */
+ public ScrollableResults scroll(ScrollMode scrollMode) throws HibernateException;
+ /**
+ * Return the query results as a <tt>List</tt>. If the query contains
+ * multiple results pre row, the results are returned in an instance
+ * of <tt>Object[]</tt>.
+ *
+ * @return the result list
+ * @throws HibernateException
+ */
+ public List list() throws HibernateException;
+ /**
+ * Convenience method to return a single instance that matches
+ * the query, or null if the query returns no results.
+ *
+ * @return the single result or <tt>null</tt>
+ * @throws NonUniqueResultException if there is more than one matching result
+ */
+ public Object uniqueResult() throws HibernateException;
+
+ /**
+ * Execute the update or delete statement.
+ * </p>
+ * The semantics are compliant with the ejb3 Query.executeUpdate()
+ * method.
+ *
+ * @return The number of entities updated or deleted.
+ * @throws HibernateException
+ */
+ public int executeUpdate() throws HibernateException;
+
+ /**
+ * Set the maximum number of rows to retrieve. If not set,
+ * there is no limit to the number of rows retrieved.
+ * @param maxResults the maximum number of rows
+ */
+ public Query setMaxResults(int maxResults);
+ /**
+ * Set the first row to retrieve. If not set, rows will be
+ * retrieved beginnning from row <tt>0</tt>.
+ * @param firstResult a row number, numbered from <tt>0</tt>
+ */
+ public Query setFirstResult(int firstResult);
+
+ /**
+ * Entities retrieved by this query will be loaded in
+ * a read-only mode where Hibernate will never dirty-check
+ * them or make changes persistent.
+ *
+ */
+ public Query setReadOnly(boolean readOnly);
+
+ /**
+ * Enable caching of this query result set.
+ * @param cacheable Should the query results be cacheable?
+ */
+ public Query setCacheable(boolean cacheable);
+
+ /**
+ * Set the name of the cache region.
+ * @param cacheRegion the name of a query cache region, or <tt>null</tt>
+ * for the default query cache
+ */
+ public Query setCacheRegion(String cacheRegion);
+
+ /**
+ * Set a timeout for the underlying JDBC query.
+ * @param timeout the timeout in seconds
+ */
+ public Query setTimeout(int timeout);
+ /**
+ * Set a fetch size for the underlying JDBC query.
+ * @param fetchSize the fetch size
+ */
+ public Query setFetchSize(int fetchSize);
+
+ /**
+ * Set the lockmode for the objects idententified by the
+ * given alias that appears in the <tt>FROM</tt> clause.
+ * @param alias a query alias, or <tt>this</tt> for a collection filter
+ */
+ public Query setLockMode(String alias, LockMode lockMode);
+
+ /**
+ * Add a comment to the generated SQL.
+ * @param comment a human-readable string
+ */
+ public Query setComment(String comment);
+
+ /**
+ * Override the current session flush mode, just for
+ * this query.
+ * @see org.hibernate.FlushMode
+ */
+ public Query setFlushMode(FlushMode flushMode);
+
+ /**
+ * Override the current session cache mode, just for
+ * this query.
+ * @see org.hibernate.CacheMode
+ */
+ public Query setCacheMode(CacheMode cacheMode);
+
+ /**
+ * Bind a value to a JDBC-style query parameter.
+ * @param position the position of the parameter in the query
+ * string, numbered from <tt>0</tt>.
+ * @param val the possibly-null parameter value
+ * @param type the Hibernate type
+ */
+ public Query setParameter(int position, Object val, Type type);
+ /**
+ * Bind a value to a named query parameter.
+ * @param name the name of the parameter
+ * @param val the possibly-null parameter value
+ * @param type the Hibernate type
+ */
+ public Query setParameter(String name, Object val, Type type);
+
+ /**
+ * Bind a value to a JDBC-style query parameter. The Hibernate type of the parameter is
+ * first detected via the usage/position in the query and if not sufficient secondly
+ * guessed from the class of the given object.
+ * @param position the position of the parameter in the query
+ * string, numbered from <tt>0</tt>.
+ * @param val the non-null parameter value
+ * @throws org.hibernate.HibernateException if no type could be determined
+ */
+ public Query setParameter(int position, Object val) throws HibernateException;
+ /**
+ * Bind a value to a named query parameter. The Hibernate type of the parameter is
+ * first detected via the usage/position in the query and if not sufficient secondly
+ * guessed from the class of the given object.
+ * @param name the name of the parameter
+ * @param val the non-null parameter value
+ * @throws org.hibernate.HibernateException if no type could be determined
+ */
+ public Query setParameter(String name, Object val) throws HibernateException;
+
+ /**
+ * Bind values and types to positional parameters.
+ */
+ public Query setParameters(Object[] values, Type[] types) throws HibernateException;
+
+ /**
+ * Bind multiple values to a named query parameter. This is useful for binding
+ * a list of values to an expression such as <tt>foo.bar in (:value_list)</tt>.
+ * @param name the name of the parameter
+ * @param vals a collection of values to list
+ * @param type the Hibernate type of the values
+ */
+ public Query setParameterList(String name, Collection vals, Type type) throws HibernateException;
+
+ /**
+ * Bind multiple values to a named query parameter. The Hibernate type of the parameter is
+ * first detected via the usage/position in the query and if not sufficient secondly
+ * guessed from the class of the first object in the collection. This is useful for binding a list of values
+ * to an expression such as <tt>foo.bar in (:value_list)</tt>.
+ * @param name the name of the parameter
+ * @param vals a collection of values to list
+ */
+ public Query setParameterList(String name, Collection vals) throws HibernateException;
+
+ /**
+ * Bind multiple values to a named query parameter. This is useful for binding
+ * a list of values to an expression such as <tt>foo.bar in (:value_list)</tt>.
+ * @param name the name of the parameter
+ * @param vals a collection of values to list
+ * @param type the Hibernate type of the values
+ */
+ public Query setParameterList(String name, Object[] vals, Type type) throws HibernateException;
+
+ /**
+ * Bind multiple values to a named query parameter. The Hibernate type of the parameter is
+ * first detected via the usage/position in the query and if not sufficient secondly
+ * guessed from the class of the first object in the array. This is useful for binding a list of values
+ * to an expression such as <tt>foo.bar in (:value_list)</tt>.
+ * @param name the name of the parameter
+ * @param vals a collection of values to list
+ */
+ public Query setParameterList(String name, Object[] vals) throws HibernateException;
+
+ /**
+ * Bind the property values of the given bean to named parameters of the query,
+ * matching property names with parameter names and mapping property types to
+ * Hibernate types using hueristics.
+ * @param bean any JavaBean or POJO
+ */
+ public Query setProperties(Object bean) throws HibernateException;
+
+ /**
+ * Bind the values of the given Map for each named parameters of the query,
+ * matching key names with parameter names and mapping value types to
+ * Hibernate types using hueristics.
+ * @param bean a java.util.Map
+ */
+ public Query setProperties(Map bean) throws HibernateException;
+
+ public Query setString(int position, String val);
+ public Query setCharacter(int position, char val);
+ public Query setBoolean(int position, boolean val);
+ public Query setByte(int position, byte val);
+ public Query setShort(int position, short val);
+ public Query setInteger(int position, int val);
+ public Query setLong(int position, long val);
+ public Query setFloat(int position, float val);
+ public Query setDouble(int position, double val);
+ public Query setBinary(int position, byte[] val);
+ public Query setText(int position, String val);
+ public Query setSerializable(int position, Serializable val);
+ public Query setLocale(int position, Locale locale);
+ public Query setBigDecimal(int position, BigDecimal number);
+ public Query setBigInteger(int position, BigInteger number);
+
+ public Query setDate(int position, Date date);
+ public Query setTime(int position, Date date);
+ public Query setTimestamp(int position, Date date);
+
+ public Query setCalendar(int position, Calendar calendar);
+ public Query setCalendarDate(int position, Calendar calendar);
+
+ public Query setString(String name, String val);
+ public Query setCharacter(String name, char val);
+ public Query setBoolean(String name, boolean val);
+ public Query setByte(String name, byte val);
+ public Query setShort(String name, short val);
+ public Query setInteger(String name, int val);
+ public Query setLong(String name, long val);
+ public Query setFloat(String name, float val);
+ public Query setDouble(String name, double val);
+ public Query setBinary(String name, byte[] val);
+ public Query setText(String name, String val);
+ public Query setSerializable(String name, Serializable val);
+ public Query setLocale(String name, Locale locale);
+ public Query setBigDecimal(String name, BigDecimal number);
+ public Query setBigInteger(String name, BigInteger number);
+
+ public Query setDate(String name, Date date);
+ public Query setTime(String name, Date date);
+ public Query setTimestamp(String name, Date date);
+
+ public Query setCalendar(String name, Calendar calendar);
+ public Query setCalendarDate(String name, Calendar calendar);
+
+ /**
+ * Bind an instance of a mapped persistent class to a JDBC-style query parameter.
+ * @param position the position of the parameter in the query
+ * string, numbered from <tt>0</tt>.
+ * @param val a non-null instance of a persistent class
+ */
+ public Query setEntity(int position, Object val); // use setParameter for null values
+
+ /**
+ * Bind an instance of a mapped persistent class to a named query parameter.
+ * @param name the name of the parameter
+ * @param val a non-null instance of a persistent class
+ */
+ public Query setEntity(String name, Object val); // use setParameter for null values
+
+
+ /**
+ * Set a strategy for handling the query results. This can be used to change
+ * "shape" of the query result.
+ *
+ * @param transformer The transformer to apply
+ * @return this (for method chaining)
+ */
+ public Query setResultTransformer(ResultTransformer transformer);
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/QueryException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/QueryException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/QueryException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,48 @@
+//$Id: QueryException.java 3890 2004-06-03 16:31:32Z steveebersole $
+package org.hibernate;
+
+/**
+ * A problem occurred translating a Hibernate query to SQL
+ * due to invalid query syntax, etc.
+ */
+public class QueryException extends HibernateException {
+
+ private String queryString;
+
+ public QueryException(String message) {
+ super(message);
+ }
+ public QueryException(String message, Throwable e) {
+ super(message, e);
+ }
+
+ public QueryException(String message, String queryString) {
+ super(message);
+ this.queryString = queryString;
+ }
+
+ public QueryException(Exception e) {
+ super(e);
+ }
+ public String getQueryString() {
+ return queryString;
+ }
+
+ public void setQueryString(String queryString) {
+ this.queryString = queryString;
+ }
+
+ public String getMessage() {
+ String msg = super.getMessage();
+ if ( queryString!=null ) msg += " [" + queryString + ']';
+ return msg;
+ }
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/QueryParameterException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/QueryParameterException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/QueryParameterException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,26 @@
+//$Id: $
+package org.hibernate;
+
+/**
+ * Parameter invalid or not found in the query
+ *
+ * @author Emmanuel Bernard
+ */
+public class QueryParameterException extends QueryException {
+
+ public QueryParameterException(Exception e) {
+ super( e );
+ }
+
+ public QueryParameterException(String message) {
+ super( message );
+ }
+
+ public QueryParameterException(String message, Throwable e) {
+ super( message, e );
+ }
+
+ public QueryParameterException(String message, String queryString) {
+ super( message, queryString );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/ReplicationMode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/ReplicationMode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/ReplicationMode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,78 @@
+//$Id: ReplicationMode.java 5060 2004-12-24 03:11:05Z oneovthafew $
+package org.hibernate;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.hibernate.type.VersionType;
+
+/**
+ * Represents a replication strategy.
+ *
+ * @see Session#replicate(Object, ReplicationMode)
+ * @author Gavin King
+ */
+public abstract class ReplicationMode implements Serializable {
+ private final String name;
+ private static final Map INSTANCES = new HashMap();
+
+ public ReplicationMode(String name) {
+ this.name=name;
+ }
+ public String toString() {
+ return name;
+ }
+ public abstract boolean shouldOverwriteCurrentVersion(Object entity, Object currentVersion, Object newVersion, VersionType versionType);
+ /**
+ * Throw an exception when a row already exists.
+ */
+ public static final ReplicationMode EXCEPTION = new ReplicationMode("EXCEPTION") {
+ public boolean shouldOverwriteCurrentVersion(Object entity, Object currentVersion, Object newVersion, VersionType versionType) {
+ throw new AssertionFailure("should not be called");
+ }
+ };
+ /**
+ * Ignore replicated entities when a row already exists.
+ */
+ public static final ReplicationMode IGNORE = new ReplicationMode("IGNORE") {
+ public boolean shouldOverwriteCurrentVersion(Object entity, Object currentVersion, Object newVersion, VersionType versionType) {
+ return false;
+ }
+ };
+ /**
+ * Overwrite existing rows when a row already exists.
+ */
+ public static final ReplicationMode OVERWRITE = new ReplicationMode("OVERWRITE") {
+ public boolean shouldOverwriteCurrentVersion(Object entity, Object currentVersion, Object newVersion, VersionType versionType) {
+ return true;
+ }
+ };
+ /**
+ * When a row already exists, choose the latest version.
+ */
+ public static final ReplicationMode LATEST_VERSION = new ReplicationMode("LATEST_VERSION") {
+ public boolean shouldOverwriteCurrentVersion(Object entity, Object currentVersion, Object newVersion, VersionType versionType) {
+ if (versionType==null) return true; //always overwrite nonversioned data
+ return versionType.getComparator().compare(currentVersion, newVersion) <= 0;
+ }
+ };
+
+ static {
+ INSTANCES.put( LATEST_VERSION.name, LATEST_VERSION );
+ INSTANCES.put( IGNORE.name, IGNORE );
+ INSTANCES.put( OVERWRITE.name, OVERWRITE );
+ INSTANCES.put( EXCEPTION.name, EXCEPTION );
+ }
+
+ private Object readResolve() {
+ return INSTANCES.get(name);
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/SQLQuery.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/SQLQuery.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/SQLQuery.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,93 @@
+//$Id: SQLQuery.java 10845 2006-11-18 04:20:30Z steve.ebersole at jboss.com $
+package org.hibernate;
+
+import org.hibernate.type.Type;
+
+/**
+ * Allows the user to declare the types and select list injection
+ * points of all entities returned by the query. Also allows
+ * declaration of the type and column alias of any scalar results
+ * of the query.
+ *
+ * @author Gavin King
+ */
+public interface SQLQuery extends Query {
+ /**
+ * Declare a "root" entity, without specifying an alias
+ */
+ public SQLQuery addEntity(String entityName);
+ /**
+ * Declare a "root" entity
+ */
+ public SQLQuery addEntity(String alias, String entityName);
+ /**
+ * Declare a "root" entity, specifying a lock mode
+ */
+ public SQLQuery addEntity(String alias, String entityName, LockMode lockMode);
+ /**
+ * Declare a "root" entity, without specifying an alias
+ */
+ public SQLQuery addEntity(Class entityClass);
+ /**
+ * Declare a "root" entity
+ */
+ public SQLQuery addEntity(String alias, Class entityClass);
+ /**
+ * Declare a "root" entity, specifying a lock mode
+ */
+ public SQLQuery addEntity(String alias, Class entityClass, LockMode lockMode);
+
+ /**
+ * Declare a "joined" entity
+ */
+ public SQLQuery addJoin(String alias, String path);
+ /**
+ * Declare a "joined" entity, specifying a lock mode
+ */
+ public SQLQuery addJoin(String alias, String path, LockMode lockMode);
+
+ /**
+ * Declare a scalar query result
+ */
+ public SQLQuery addScalar(String columnAlias, Type type);
+
+ /**
+ * Declare a scalar query. Hibernate will attempt to automatically detect the underlying type.
+ */
+ public SQLQuery addScalar(String columnAlias);
+
+ /**
+ * Use a predefined named ResultSetMapping
+ */
+ public SQLQuery setResultSetMapping(String name);
+
+ /**
+ * Adds a query space for auto-flush synchronization.
+ *
+ * @param querySpace The query space to be auto-flushed for this query.
+ * @return this, for method chaning
+ */
+ public SQLQuery addSynchronizedQuerySpace(String querySpace);
+
+ /**
+ * Adds an entity name or auto-flush synchronization.
+ *
+ * @param entityName The name of the entity upon whose defined
+ * query spaces we should additionally synchronize.
+ * @return this, for method chaning
+ * @throws MappingException Indicates the given entity name could not be
+ * resolved.
+ */
+ public SQLQuery addSynchronizedEntityName(String entityName) throws MappingException;
+
+ /**
+ * Adds an entity name or auto-flush synchronization.
+ *
+ * @param entityClass The class of the entity upon whose defined
+ * query spaces we should additionally synchronize.
+ * @return this, for method chaning
+ * @throws MappingException Indicates the given entity class could not be
+ * resolved.
+ */
+ public SQLQuery addSynchronizedEntityClass(Class entityClass) throws MappingException;
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/SQLQuery.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/ScrollMode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/ScrollMode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/ScrollMode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,74 @@
+//$Id: ScrollMode.java 4369 2004-08-18 00:28:43Z oneovthafew $
+package org.hibernate;
+
+import java.io.Serializable;
+import java.sql.ResultSet;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Specifies the type of JDBC scrollable result set to use
+ * underneath a <tt>ScrollableResults</tt>
+ *
+ * @see Query#scroll(ScrollMode)
+ * @see ScrollableResults
+ * @author Gavin King
+ */
+public final class ScrollMode implements Serializable {
+ private final int resultSetType;
+ private final String name;
+ private static final Map INSTANCES = new HashMap();
+
+ private ScrollMode(int level, String name) {
+ this.resultSetType=level;
+ this.name=name;
+ }
+
+ public String toString() {
+ return name;
+ }
+
+ /**
+ * @return the JDBC result set type code
+ */
+ public int toResultSetType() {
+ return resultSetType;
+ }
+
+ /**
+ * @see java.sql.ResultSet.TYPE_FORWARD_ONLY
+ */
+ public static final ScrollMode FORWARD_ONLY = new ScrollMode(ResultSet.TYPE_FORWARD_ONLY, "FORWARD_ONLY");
+ /**
+ * @see java.sql.ResultSet.TYPE_SCROLL_SENSITIVE
+ */
+ public static final ScrollMode SCROLL_SENSITIVE = new ScrollMode(ResultSet.TYPE_SCROLL_SENSITIVE, "SCROLL_SENSITIVE");
+ /**
+ * Note that since the Hibernate session acts as a cache, you
+ * might need to expicitly evict objects, if you need to see
+ * changes made by other transactions.
+ * @see java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE
+ */
+ public static final ScrollMode SCROLL_INSENSITIVE = new ScrollMode(ResultSet.TYPE_SCROLL_INSENSITIVE, "SCROLL_INSENSITIVE");
+
+ public boolean lessThan(ScrollMode other) {
+ return this.resultSetType<other.resultSetType;
+ }
+
+ static {
+ INSTANCES.put( FORWARD_ONLY.name, FORWARD_ONLY );
+ INSTANCES.put( SCROLL_INSENSITIVE.name, SCROLL_INSENSITIVE );
+ INSTANCES.put( SCROLL_SENSITIVE.name, SCROLL_SENSITIVE );
+ }
+
+ private Object readResolve() {
+ return INSTANCES.get(name);
+ }
+
+}
+
+
+
+
+
+
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/ScrollMode.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/ScrollableResults.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/ScrollableResults.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/ScrollableResults.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,204 @@
+//$Id: ScrollableResults.java 6411 2005-04-13 07:37:50Z oneovthafew $
+package org.hibernate;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.sql.Blob;
+import java.sql.Clob;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import org.hibernate.type.Type;
+
+/**
+ * A result iterator that allows moving around within the results
+ * by arbitrary increments. The <tt>Query</tt> / <tt>ScrollableResults</tt>
+ * pattern is very similar to the JDBC <tt>PreparedStatement</tt>/
+ * <tt>ResultSet</tt> pattern and the semantics of methods of this interface
+ * are similar to the similarly named methods on <tt>ResultSet</tt>.<br>
+ * <br>
+ * Contrary to JDBC, columns of results are numbered from zero.
+ *
+ * @see Query#scroll()
+ * @author Gavin King
+ */
+public interface ScrollableResults {
+ /**
+ * Advance to the next result
+ * @return <tt>true</tt> if there is another result
+ */
+ public boolean next() throws HibernateException;
+ /**
+ * Retreat to the previous result
+ * @return <tt>true</tt> if there is a previous result
+ */
+ public boolean previous() throws HibernateException;
+ /**
+ * Scroll an arbitrary number of locations
+ * @param i a positive (forward) or negative (backward) number of rows
+ * @return <tt>true</tt> if there is a result at the new location
+ */
+ public boolean scroll(int i) throws HibernateException;
+ /**
+ * Go to the last result
+ * @return <tt>true</tt> if there are any results
+ */
+ public boolean last() throws HibernateException;
+ /**
+ * Go to the first result
+ * @return <tt>true</tt> if there are any results
+ */
+ public boolean first() throws HibernateException;
+ /**
+ * Go to a location just before first result (this is the initial location)
+ */
+ public void beforeFirst() throws HibernateException;
+ /**
+ * Go to a location just after the last result
+ */
+ public void afterLast() throws HibernateException;
+ /**
+ * Is this the first result?
+ *
+ * @return <tt>true</tt> if this is the first row of results
+ * @throws HibernateException
+ */
+ public boolean isFirst() throws HibernateException;
+ /**
+ * Is this the last result?
+ *
+ * @return <tt>true</tt> if this is the last row of results
+ * @throws HibernateException
+ */
+ public boolean isLast() throws HibernateException;
+ /**
+ * Release resources immediately.
+ */
+ public void close() throws HibernateException;
+ /**
+ * Get the current row of results
+ * @return an object or array
+ */
+ public Object[] get() throws HibernateException;
+ /**
+ * Get the <tt>i</tt>th object in the current row of results, without
+ * initializing any other results in the row. This method may be used
+ * safely, regardless of the type of the column (ie. even for scalar
+ * results).
+ * @param i the column, numbered from zero
+ * @return an object of any Hibernate type or <tt>null</tt>
+ */
+ public Object get(int i) throws HibernateException;
+
+ /**
+ * Get the type of the <tt>i</tt>th column of results
+ * @param i the column, numbered from zero
+ * @return the Hibernate type
+ */
+ public Type getType(int i);
+
+ /**
+ * Convenience method to read an <tt>integer</tt>
+ */
+ public Integer getInteger(int col) throws HibernateException;
+ /**
+ * Convenience method to read a <tt>long</tt>
+ */
+ public Long getLong(int col) throws HibernateException;
+ /**
+ * Convenience method to read a <tt>float</tt>
+ */
+ public Float getFloat(int col) throws HibernateException;
+ /**
+ * Convenience method to read a <tt>boolean</tt>
+ */
+ public Boolean getBoolean(int col) throws HibernateException;
+ /**
+ * Convenience method to read a <tt>double</tt>
+ */
+ public Double getDouble(int col) throws HibernateException;
+ /**
+ * Convenience method to read a <tt>short</tt>
+ */
+ public Short getShort(int col) throws HibernateException;
+ /**
+ * Convenience method to read a <tt>byte</tt>
+ */
+ public Byte getByte(int col) throws HibernateException;
+ /**
+ * Convenience method to read a <tt>character</tt>
+ */
+ public Character getCharacter(int col) throws HibernateException;
+ /**
+ * Convenience method to read a <tt>binary</tt>
+ */
+ public byte[] getBinary(int col) throws HibernateException;
+ /**
+ * Convenience method to read <tt>text</tt>
+ */
+ public String getText(int col) throws HibernateException;
+ /**
+ * Convenience method to read a <tt>blob</tt>
+ */
+ public Blob getBlob(int col) throws HibernateException;
+ /**
+ * Convenience method to read a <tt>clob</tt>
+ */
+ public Clob getClob(int col) throws HibernateException;
+ /**
+ * Convenience method to read a <tt>string</tt>
+ */
+ public String getString(int col) throws HibernateException;
+ /**
+ * Convenience method to read a <tt>big_decimal</tt>
+ */
+ public BigDecimal getBigDecimal(int col) throws HibernateException;
+ /**
+ * Convenience method to read a <tt>big_integer</tt>
+ */
+ public BigInteger getBigInteger(int col) throws HibernateException;
+ /**
+ * Convenience method to read a <tt>date</tt>, <tt>time</tt> or <tt>timestamp</tt>
+ */
+ public Date getDate(int col) throws HibernateException;
+ /**
+ * Convenience method to read a <tt>locale</tt>
+ */
+ public Locale getLocale(int col) throws HibernateException;
+ /**
+ * Convenience method to read a <tt>calendar</tt> or <tt>calendar_date</tt>
+ */
+ public Calendar getCalendar(int col) throws HibernateException;
+ /**
+ * Convenience method to read a <tt>currency</tt>
+ */
+ //public Currency getCurrency(int col) throws HibernateException;
+ /**
+ * Convenience method to read a <tt>timezone</tt>
+ */
+ public TimeZone getTimeZone(int col) throws HibernateException;
+ /**
+ * Get the current location in the result set. The first
+ * row is number <tt>0</tt>, contrary to JDBC.
+ * @return the row number, numbered from <tt>0</tt>, or <tt>-1</tt> if
+ * there is no current row
+ */
+ public int getRowNumber() throws HibernateException;
+ /**
+ * Set the current location in the result set, numbered from either the
+ * first row (row number <tt>0</tt>), or the last row (row
+ * number <tt>-1</tt>).
+ * @param rowNumber the row number, numbered from the last row, in the
+ * case of a negative row number
+ * @return true if there is a row at that row number
+ */
+ public boolean setRowNumber(int rowNumber) throws HibernateException;
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/Session.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/Session.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/Session.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,783 @@
+//$Id: Session.java 11494 2007-05-09 02:00:16Z steve.ebersole at jboss.com $
+package org.hibernate;
+
+import java.io.Serializable;
+import java.sql.Connection;
+
+import org.hibernate.stat.SessionStatistics;
+
+/**
+ * The main runtime interface between a Java application and Hibernate. This is the
+ * central API class abstracting the notion of a persistence service.<br>
+ * <br>
+ * The lifecycle of a <tt>Session</tt> is bounded by the beginning and end of a logical
+ * transaction. (Long transactions might span several database transactions.)<br>
+ * <br>
+ * The main function of the <tt>Session</tt> is to offer create, read and delete operations
+ * for instances of mapped entity classes. Instances may exist in one of three states:<br>
+ * <br>
+ * <i>transient:</i> never persistent, not associated with any <tt>Session</tt><br>
+ * <i>persistent:</i> associated with a unique <tt>Session</tt><br>
+ * <i>detached:</i> previously persistent, not associated with any <tt>Session</tt><br>
+ * <br>
+ * Transient instances may be made persistent by calling <tt>save()</tt>,
+ * <tt>persist()</tt> or <tt>saveOrUpdate()</tt>. Persistent instances may be made transient
+ * by calling<tt> delete()</tt>. Any instance returned by a <tt>get()</tt> or
+ * <tt>load()</tt> method is persistent. Detached instances may be made persistent
+ * by calling <tt>update()</tt>, <tt>saveOrUpdate()</tt>, <tt>lock()</tt> or <tt>replicate()</tt>.
+ * The state of a transient or detached instance may also be made persistent as a new
+ * persistent instance by calling <tt>merge()</tt>.<br>
+ * <br>
+ * <tt>save()</tt> and <tt>persist()</tt> result in an SQL <tt>INSERT</tt>, <tt>delete()</tt>
+ * in an SQL <tt>DELETE</tt> and <tt>update()</tt> or <tt>merge()</tt> in an SQL <tt>UPDATE</tt>.
+ * Changes to <i>persistent</i> instances are detected at flush time and also result in an SQL
+ * <tt>UPDATE</tt>. <tt>saveOrUpdate()</tt> and <tt>replicate()</tt> result in either an
+ * <tt>INSERT</tt> or an <tt>UPDATE</tt>.<br>
+ * <br>
+ * It is not intended that implementors be threadsafe. Instead each thread/transaction
+ * should obtain its own instance from a <tt>SessionFactory</tt>.<br>
+ * <br>
+ * A <tt>Session</tt> instance is serializable if its persistent classes are serializable.<br>
+ * <br>
+ * A typical transaction should use the following idiom:
+ * <pre>
+ * Session sess = factory.openSession();
+ * Transaction tx;
+ * try {
+ * tx = sess.beginTransaction();
+ * //do some work
+ * ...
+ * tx.commit();
+ * }
+ * catch (Exception e) {
+ * if (tx!=null) tx.rollback();
+ * throw e;
+ * }
+ * finally {
+ * sess.close();
+ * }
+ * </pre>
+ * <br>
+ * If the <tt>Session</tt> throws an exception, the transaction must be rolled back
+ * and the session discarded. The internal state of the <tt>Session</tt> might not
+ * be consistent with the database after the exception occurs.
+ *
+ * @see SessionFactory
+ * @author Gavin King
+ */
+public interface Session extends Serializable {
+
+ /**
+ * Retrieve the entity mode in effect for this session.
+ *
+ * @return The entity mode for this session.
+ */
+ public EntityMode getEntityMode();
+
+ /**
+ * Starts a new Session with the given entity mode in effect. This secondary
+ * Session inherits the connection, transaction, and other context
+ * information from the primary Session. It doesn't need to be flushed
+ * or closed by the developer.
+ *
+ * @param entityMode The entity mode to use for the new session.
+ * @return The new session
+ */
+ public Session getSession(EntityMode entityMode);
+
+ /**
+ * Force this session to flush. Must be called at the end of a
+ * unit of work, before commiting the transaction and closing the
+ * session (depending on {@link #setFlushMode flush-mode},
+ * {@link Transaction#commit()} calls this method).
+ * <p/>
+ * <i>Flushing</i> is the process of synchronizing the underlying persistent
+ * store with persistable state held in memory.
+ *
+ * @throws HibernateException Indicates problems flushing the session or
+ * talking to the database.
+ */
+ public void flush() throws HibernateException;
+
+ /**
+ * Set the flush mode for this session.
+ * <p/>
+ * The flush mode determines the points at which the session is flushed.
+ * <i>Flushing</i> is the process of synchronizing the underlying persistent
+ * store with persistable state held in memory.
+ * <p/>
+ * For a logically "read only" session, it is reasonable to set the session's
+ * flush mode to {@link FlushMode#MANUAL} at the start of the session (in
+ * order to achieve some extra performance).
+ *
+ * @param flushMode the new flush mode
+ * @see FlushMode
+ */
+ public void setFlushMode(FlushMode flushMode);
+
+ /**
+ * Get the current flush mode for this session.
+ *
+ * @return The flush mode
+ */
+ public FlushMode getFlushMode();
+
+ /**
+ * Set the cache mode.
+ * <p/>
+ * Cache mode determines the manner in which this session can interact with
+ * the second level cache.
+ *
+ * @param cacheMode The new cache mode.
+ */
+ public void setCacheMode(CacheMode cacheMode);
+
+ /**
+ * Get the current cache mode.
+ *
+ * @return The current cache mode.
+ */
+ public CacheMode getCacheMode();
+
+ /**
+ * Get the session factory which created this session.
+ *
+ * @return The session factory.
+ * @see SessionFactory
+
+ */
+ public SessionFactory getSessionFactory();
+
+ /**
+ * Get the JDBC connection of this Session.<br>
+ * <br>
+ * If the session is using aggressive collection release (as in a
+ * CMT environment), it is the application's responsibility to
+ * close the connection returned by this call. Otherwise, the
+ * application should not close the connection.
+ *
+ * @return the JDBC connection in use by the <tt>Session</tt>
+ * @throws HibernateException if the <tt>Session</tt> is disconnected
+ * @deprecated To be replaced with a SPI for performing work against the connection; scheduled for removal in 4.x
+ */
+ public Connection connection() throws HibernateException;
+
+ /**
+ * End the session by releasing the JDBC connection and cleaning up. It is
+ * not strictly necessary to close the session but you must at least
+ * {@link #disconnect()} it.
+ *
+ * @return the connection provided by the application or null.
+ * @throws HibernateException Indicates problems cleaning up.
+ */
+ public Connection close() throws HibernateException;
+
+ /**
+ * Cancel the execution of the current query.
+ * <p/>
+ * This is the sole method on session which may be safely called from
+ * another thread.
+ *
+ * @throws HibernateException There was a problem canceling the query
+ */
+ public void cancelQuery() throws HibernateException;
+
+ /**
+ * Check if the session is still open.
+ *
+ * @return boolean
+ */
+ public boolean isOpen();
+
+ /**
+ * Check if the session is currently connected.
+ *
+ * @return boolean
+ */
+ public boolean isConnected();
+
+ /**
+ * Does this session contain any changes which must be synchronized with
+ * the database? In other words, would any DML operations be executed if
+ * we flushed this session?
+ *
+ * @return True if the session contains pending changes; false otherwise.
+ * @throws HibernateException could not perform dirtying checking
+ */
+ public boolean isDirty() throws HibernateException;
+
+ /**
+ * Return the identifier value of the given entity as associated with this
+ * session. An exception is thrown if the given entity instance is transient
+ * or detached in relation to this session.
+ *
+ * @param object a persistent instance
+ * @return the identifier
+ * @throws TransientObjectException if the instance is transient or associated with
+ * a different session
+ */
+ public Serializable getIdentifier(Object object) throws HibernateException;
+
+ /**
+ * Check if this instance is associated with this <tt>Session</tt>.
+ *
+ * @param object an instance of a persistent class
+ * @return true if the given instance is associated with this <tt>Session</tt>
+ */
+ public boolean contains(Object object);
+
+ /**
+ * Remove this instance from the session cache. Changes to the instance will
+ * not be synchronized with the database. This operation cascades to associated
+ * instances if the association is mapped with <tt>cascade="evict"</tt>.
+ *
+ * @param object a persistent instance
+ * @throws HibernateException
+ */
+ public void evict(Object object) throws HibernateException;
+
+ /**
+ * Return the persistent instance of the given entity class with the given identifier,
+ * obtaining the specified lock mode, assuming the instance exists.
+ *
+ * @param theClass a persistent class
+ * @param id a valid identifier of an existing persistent instance of the class
+ * @param lockMode the lock level
+ * @return the persistent instance or proxy
+ * @throws HibernateException
+ */
+ public Object load(Class theClass, Serializable id, LockMode lockMode) throws HibernateException;
+
+ /**
+ * Return the persistent instance of the given entity class with the given identifier,
+ * obtaining the specified lock mode, assuming the instance exists.
+ *
+ * @param entityName a persistent class
+ * @param id a valid identifier of an existing persistent instance of the class
+ * @param lockMode the lock level
+ * @return the persistent instance or proxy
+ * @throws HibernateException
+ */
+ public Object load(String entityName, Serializable id, LockMode lockMode) throws HibernateException;
+
+ /**
+ * Return the persistent instance of the given entity class with the given identifier,
+ * assuming that the instance exists. This method might return a proxied instance that
+ * is initialized on-demand, when a non-identifier method is accessed.
+ * <br><br>
+ * You should not use this method to determine if an instance exists (use <tt>get()</tt>
+ * instead). Use this only to retrieve an instance that you assume exists, where non-existence
+ * would be an actual error.
+ *
+ * @param theClass a persistent class
+ * @param id a valid identifier of an existing persistent instance of the class
+ * @return the persistent instance or proxy
+ * @throws HibernateException
+ */
+ public Object load(Class theClass, Serializable id) throws HibernateException;
+
+ /**
+ * Return the persistent instance of the given entity class with the given identifier,
+ * assuming that the instance exists. This method might return a proxied instance that
+ * is initialized on-demand, when a non-identifier method is accessed.
+ * <br><br>
+ * You should not use this method to determine if an instance exists (use <tt>get()</tt>
+ * instead). Use this only to retrieve an instance that you assume exists, where non-existence
+ * would be an actual error.
+ *
+ * @param entityName a persistent class
+ * @param id a valid identifier of an existing persistent instance of the class
+ * @return the persistent instance or proxy
+ * @throws HibernateException
+ */
+ public Object load(String entityName, Serializable id) throws HibernateException;
+
+ /**
+ * Read the persistent state associated with the given identifier into the given transient
+ * instance.
+ *
+ * @param object an "empty" instance of the persistent class
+ * @param id a valid identifier of an existing persistent instance of the class
+ * @throws HibernateException
+ */
+ public void load(Object object, Serializable id) throws HibernateException;
+
+ /**
+ * Persist the state of the given detached instance, reusing the current
+ * identifier value. This operation cascades to associated instances if
+ * the association is mapped with <tt>cascade="replicate"</tt>.
+ *
+ * @param object a detached instance of a persistent class
+ */
+ public void replicate(Object object, ReplicationMode replicationMode) throws HibernateException;
+
+ /**
+ * Persist the state of the given detached instance, reusing the current
+ * identifier value. This operation cascades to associated instances if
+ * the association is mapped with <tt>cascade="replicate"</tt>.
+ *
+ * @param object a detached instance of a persistent class
+ */
+ public void replicate(String entityName, Object object, ReplicationMode replicationMode) throws HibernateException;
+
+ /**
+ * Persist the given transient instance, first assigning a generated identifier. (Or
+ * using the current value of the identifier property if the <tt>assigned</tt>
+ * generator is used.) This operation cascades to associated instances if the
+ * association is mapped with <tt>cascade="save-update"</tt>.
+ *
+ * @param object a transient instance of a persistent class
+ * @return the generated identifier
+ * @throws HibernateException
+ */
+ public Serializable save(Object object) throws HibernateException;
+
+ /**
+ * Persist the given transient instance, first assigning a generated identifier. (Or
+ * using the current value of the identifier property if the <tt>assigned</tt>
+ * generator is used.) This operation cascades to associated instances if the
+ * association is mapped with <tt>cascade="save-update"</tt>.
+ *
+ * @param object a transient instance of a persistent class
+ * @return the generated identifier
+ * @throws HibernateException
+ */
+ public Serializable save(String entityName, Object object) throws HibernateException;
+
+ /**
+ * Either {@link #save(Object)} or {@link #update(Object)} the given
+ * instance, depending upon resolution of the unsaved-value checks (see the
+ * manual for discussion of unsaved-value checking).
+ * <p/>
+ * This operation cascades to associated instances if the association is mapped
+ * with <tt>cascade="save-update"</tt>.
+ *
+ * @see Session#save(java.lang.Object)
+ * @see Session#update(Object object)
+ * @param object a transient or detached instance containing new or updated state
+ * @throws HibernateException
+ */
+ public void saveOrUpdate(Object object) throws HibernateException;
+
+ /**
+ * Either {@link #save(String, Object)} or {@link #update(String, Object)}
+ * the given instance, depending upon resolution of the unsaved-value checks
+ * (see the manual for discussion of unsaved-value checking).
+ * <p/>
+ * This operation cascades to associated instances if the association is mapped
+ * with <tt>cascade="save-update"</tt>.
+ *
+ * @see Session#save(String,Object)
+ * @see Session#update(String,Object)
+ * @param object a transient or detached instance containing new or updated state
+ * @throws HibernateException
+ */
+ public void saveOrUpdate(String entityName, Object object) throws HibernateException;
+
+ /**
+ * Update the persistent instance with the identifier of the given detached
+ * instance. If there is a persistent instance with the same identifier,
+ * an exception is thrown. This operation cascades to associated instances
+ * if the association is mapped with <tt>cascade="save-update"</tt>.
+ *
+ * @param object a detached instance containing updated state
+ * @throws HibernateException
+ */
+ public void update(Object object) throws HibernateException;
+
+ /**
+ * Update the persistent instance with the identifier of the given detached
+ * instance. If there is a persistent instance with the same identifier,
+ * an exception is thrown. This operation cascades to associated instances
+ * if the association is mapped with <tt>cascade="save-update"</tt>.
+ *
+ * @param object a detached instance containing updated state
+ * @throws HibernateException
+ */
+ public void update(String entityName, Object object) throws HibernateException;
+
+ /**
+ * Copy the state of the given object onto the persistent object with the same
+ * identifier. If there is no persistent instance currently associated with
+ * the session, it will be loaded. Return the persistent instance. If the
+ * given instance is unsaved, save a copy of and return it as a newly persistent
+ * instance. The given instance does not become associated with the session.
+ * This operation cascades to associated instances if the association is mapped
+ * with <tt>cascade="merge"</tt>.<br>
+ * <br>
+ * The semantics of this method are defined by JSR-220.
+ *
+ * @param object a detached instance with state to be copied
+ * @return an updated persistent instance
+ */
+ public Object merge(Object object) throws HibernateException;
+
+ /**
+ * Copy the state of the given object onto the persistent object with the same
+ * identifier. If there is no persistent instance currently associated with
+ * the session, it will be loaded. Return the persistent instance. If the
+ * given instance is unsaved, save a copy of and return it as a newly persistent
+ * instance. The given instance does not become associated with the session.
+ * This operation cascades to associated instances if the association is mapped
+ * with <tt>cascade="merge"</tt>.<br>
+ * <br>
+ * The semantics of this method are defined by JSR-220.
+ *
+ * @param object a detached instance with state to be copied
+ * @return an updated persistent instance
+ */
+ public Object merge(String entityName, Object object) throws HibernateException;
+
+ /**
+ * Make a transient instance persistent. This operation cascades to associated
+ * instances if the association is mapped with <tt>cascade="persist"</tt>.<br>
+ * <br>
+ * The semantics of this method are defined by JSR-220.
+ *
+ * @param object a transient instance to be made persistent
+ */
+ public void persist(Object object) throws HibernateException;
+ /**
+ * Make a transient instance persistent. This operation cascades to associated
+ * instances if the association is mapped with <tt>cascade="persist"</tt>.<br>
+ * <br>
+ * The semantics of this method are defined by JSR-220.
+ *
+ * @param object a transient instance to be made persistent
+ */
+ public void persist(String entityName, Object object) throws HibernateException;
+
+ /**
+ * Remove a persistent instance from the datastore. The argument may be
+ * an instance associated with the receiving <tt>Session</tt> or a transient
+ * instance with an identifier associated with existing persistent state.
+ * This operation cascades to associated instances if the association is mapped
+ * with <tt>cascade="delete"</tt>.
+ *
+ * @param object the instance to be removed
+ * @throws HibernateException
+ */
+ public void delete(Object object) throws HibernateException;
+
+ /**
+ * Remove a persistent instance from the datastore. The <b>object</b> argument may be
+ * an instance associated with the receiving <tt>Session</tt> or a transient
+ * instance with an identifier associated with existing persistent state.
+ * This operation cascades to associated instances if the association is mapped
+ * with <tt>cascade="delete"</tt>.
+ *
+ * @param entityName The entity name for the instance to be removed.
+ * @param object the instance to be removed
+ * @throws HibernateException
+ */
+ public void delete(String entityName, Object object) throws HibernateException;
+
+ /**
+ * Obtain the specified lock level upon the given object. This may be used to
+ * perform a version check (<tt>LockMode.READ</tt>), to upgrade to a pessimistic
+ * lock (<tt>LockMode.UPGRADE</tt>), or to simply reassociate a transient instance
+ * with a session (<tt>LockMode.NONE</tt>). This operation cascades to associated
+ * instances if the association is mapped with <tt>cascade="lock"</tt>.
+ *
+ * @param object a persistent or transient instance
+ * @param lockMode the lock level
+ * @throws HibernateException
+ */
+ public void lock(Object object, LockMode lockMode) throws HibernateException;
+
+ /**
+ * Obtain the specified lock level upon the given object. This may be used to
+ * perform a version check (<tt>LockMode.READ</tt>), to upgrade to a pessimistic
+ * lock (<tt>LockMode.UPGRADE</tt>), or to simply reassociate a transient instance
+ * with a session (<tt>LockMode.NONE</tt>). This operation cascades to associated
+ * instances if the association is mapped with <tt>cascade="lock"</tt>.
+ *
+ * @param object a persistent or transient instance
+ * @param lockMode the lock level
+ * @throws HibernateException
+ */
+ public void lock(String entityName, Object object, LockMode lockMode) throws HibernateException;
+
+ /**
+ * Re-read the state of the given instance from the underlying database. It is
+ * inadvisable to use this to implement long-running sessions that span many
+ * business tasks. This method is, however, useful in certain special circumstances.
+ * For example
+ * <ul>
+ * <li>where a database trigger alters the object state upon insert or update
+ * <li>after executing direct SQL (eg. a mass update) in the same session
+ * <li>after inserting a <tt>Blob</tt> or <tt>Clob</tt>
+ * </ul>
+ *
+ * @param object a persistent or detached instance
+ * @throws HibernateException
+ */
+ public void refresh(Object object) throws HibernateException;
+
+ /**
+ * Re-read the state of the given instance from the underlying database, with
+ * the given <tt>LockMode</tt>. It is inadvisable to use this to implement
+ * long-running sessions that span many business tasks. This method is, however,
+ * useful in certain special circumstances.
+ *
+ * @param object a persistent or detached instance
+ * @param lockMode the lock mode to use
+ * @throws HibernateException
+ */
+ public void refresh(Object object, LockMode lockMode) throws HibernateException;
+
+ /**
+ * Determine the current lock mode of the given object.
+ *
+ * @param object a persistent instance
+ * @return the current lock mode
+ * @throws HibernateException
+ */
+ public LockMode getCurrentLockMode(Object object) throws HibernateException;
+
+ /**
+ * Begin a unit of work and return the associated <tt>Transaction</tt> object.
+ * If a new underlying transaction is required, begin the transaction. Otherwise
+ * continue the new work in the context of the existing underlying transaction.
+ * The class of the returned <tt>Transaction</tt> object is determined by the
+ * property <tt>hibernate.transaction_factory</tt>.
+ *
+ * @return a Transaction instance
+ * @throws HibernateException
+ * @see Transaction
+ */
+ public Transaction beginTransaction() throws HibernateException;
+
+ /**
+ * Get the <tt>Transaction</tt> instance associated with this session.
+ * The class of the returned <tt>Transaction</tt> object is determined by the
+ * property <tt>hibernate.transaction_factory</tt>.
+ *
+ * @return a Transaction instance
+ * @throws HibernateException
+ * @see Transaction
+ */
+ public Transaction getTransaction();
+
+ /**
+ * Create a new <tt>Criteria</tt> instance, for the given entity class,
+ * or a superclass of an entity class.
+ *
+ * @param persistentClass a class, which is persistent, or has persistent subclasses
+ * @return Criteria
+ */
+ public Criteria createCriteria(Class persistentClass);
+
+ /**
+ * Create a new <tt>Criteria</tt> instance, for the given entity class,
+ * or a superclass of an entity class, with the given alias.
+ *
+ * @param persistentClass a class, which is persistent, or has persistent subclasses
+ * @return Criteria
+ */
+ public Criteria createCriteria(Class persistentClass, String alias);
+
+ /**
+ * Create a new <tt>Criteria</tt> instance, for the given entity name.
+ *
+ * @param entityName
+ * @return Criteria
+ */
+ public Criteria createCriteria(String entityName);
+
+ /**
+ * Create a new <tt>Criteria</tt> instance, for the given entity name,
+ * with the given alias.
+ *
+ * @param entityName
+ * @return Criteria
+ */
+ public Criteria createCriteria(String entityName, String alias);
+
+ /**
+ * Create a new instance of <tt>Query</tt> for the given HQL query string.
+ *
+ * @param queryString a HQL query
+ * @return Query
+ * @throws HibernateException
+ */
+ public Query createQuery(String queryString) throws HibernateException;
+
+ /**
+ * Create a new instance of <tt>SQLQuery</tt> for the given SQL query string.
+ *
+ * @param queryString a SQL query
+ * @return SQLQuery
+ * @throws HibernateException
+ */
+ public SQLQuery createSQLQuery(String queryString) throws HibernateException;
+
+ /**
+ * Create a new instance of <tt>Query</tt> for the given collection and filter string.
+ *
+ * @param collection a persistent collection
+ * @param queryString a Hibernate query
+ * @return Query
+ * @throws HibernateException
+ */
+ public Query createFilter(Object collection, String queryString) throws HibernateException;
+
+ /**
+ * Obtain an instance of <tt>Query</tt> for a named query string defined in the
+ * mapping file.
+ *
+ * @param queryName the name of a query defined externally
+ * @return Query
+ * @throws HibernateException
+ */
+ public Query getNamedQuery(String queryName) throws HibernateException;
+
+ /**
+ * Completely clear the session. Evict all loaded instances and cancel all pending
+ * saves, updates and deletions. Do not close open iterators or instances of
+ * <tt>ScrollableResults</tt>.
+ */
+ public void clear();
+
+ /**
+ * Return the persistent instance of the given entity class with the given identifier,
+ * or null if there is no such persistent instance. (If the instance is already associated
+ * with the session, return that instance. This method never returns an uninitialized instance.)
+ * Obtain the specified lock mode if the instance exists.
+ *
+ * @param clazz a persistent class
+ * @param id an identifier
+ * @return a persistent instance or null
+ * @throws HibernateException
+ */
+ public Object get(Class clazz, Serializable id) throws HibernateException;
+
+ /**
+ * Return the persistent instance of the given entity class with the given identifier,
+ * or null if there is no such persistent instance. (If the instance is already associated
+ * with the session, return that instance. This method never returns an uninitialized instance.)
+ * Obtain the specified lock mode if the instance exists.
+ *
+ * @param clazz a persistent class
+ * @param id an identifier
+ * @param lockMode the lock mode
+ * @return a persistent instance or null
+ * @throws HibernateException
+ */
+ public Object get(Class clazz, Serializable id, LockMode lockMode) throws HibernateException;
+
+ /**
+ * Return the persistent instance of the given named entity with the given identifier,
+ * or null if there is no such persistent instance. (If the instance is already associated
+ * with the session, return that instance. This method never returns an uninitialized instance.)
+ *
+ * @param entityName the entity name
+ * @param id an identifier
+ * @return a persistent instance or null
+ * @throws HibernateException
+ */
+ public Object get(String entityName, Serializable id) throws HibernateException;
+
+ /**
+ * Return the persistent instance of the given entity class with the given identifier,
+ * or null if there is no such persistent instance. (If the instance is already associated
+ * with the session, return that instance. This method never returns an uninitialized instance.)
+ * Obtain the specified lock mode if the instance exists.
+ *
+ * @param entityName the entity name
+ * @param id an identifier
+ * @param lockMode the lock mode
+ * @return a persistent instance or null
+ * @throws HibernateException
+ */
+ public Object get(String entityName, Serializable id, LockMode lockMode) throws HibernateException;
+
+
+ /**
+ * Return the entity name for a persistent entity
+ *
+ * @param object a persistent entity
+ * @return the entity name
+ * @throws HibernateException
+ */
+ public String getEntityName(Object object) throws HibernateException;
+
+ /**
+ * Enable the named filter for this current session.
+ *
+ * @param filterName The name of the filter to be enabled.
+ * @return The Filter instance representing the enabled fiter.
+ */
+ public Filter enableFilter(String filterName);
+
+ /**
+ * Retrieve a currently enabled filter by name.
+ *
+ * @param filterName The name of the filter to be retrieved.
+ * @return The Filter instance representing the enabled fiter.
+ */
+ public Filter getEnabledFilter(String filterName);
+
+ /**
+ * Disable the named filter for the current session.
+ *
+ * @param filterName The name of the filter to be disabled.
+ */
+ public void disableFilter(String filterName);
+
+ /**
+ * Get the statistics for this session.
+ */
+ public SessionStatistics getStatistics();
+
+ /**
+ * Set an unmodified persistent object to read only mode, or a read only
+ * object to modifiable mode. In read only mode, no snapshot is maintained
+ * and the instance is never dirty checked.
+ *
+ * @see Query#setReadOnly(boolean)
+ */
+ public void setReadOnly(Object entity, boolean readOnly);
+
+
+
+ /**
+ * Disconnect the <tt>Session</tt> from the current JDBC connection. If
+ * the connection was obtained by Hibernate close it and return it to
+ * the connection pool; otherwise, return it to the application.
+ * <p/>
+ * This is used by applications which supply JDBC connections to Hibernate
+ * and which require long-sessions (or long-conversations)
+ * <p/>
+ * Note that disconnect() called on a session where the connection was
+ * retrieved by Hibernate through its configured
+ * {@link org.hibernate.connection.ConnectionProvider} has no effect,
+ * provided {@link ConnectionReleaseMode#ON_CLOSE} is not in effect.
+ *
+ * @return the application-supplied connection or <tt>null</tt>
+ * @see #reconnect(Connection)
+ * @see #reconnect()
+ */
+ Connection disconnect() throws HibernateException;
+
+ /**
+ * Obtain a new JDBC connection. This is used by applications which
+ * require long transactions and do not supply connections to the
+ * session.
+ *
+ * @see #disconnect()
+ * @deprecated Manual reconnection is only needed in the case of
+ * application-supplied connections, in which case the
+ * {@link #reconnect(java.sql.Connection)} for should be used.
+ */
+ void reconnect() throws HibernateException;
+
+ /**
+ * Reconnect to the given JDBC connection. This is used by applications
+ * which require long transactions and use application-supplied connections.
+ *
+ * @param connection a JDBC connection
+ * @see #disconnect()
+ */
+ void reconnect(Connection connection) throws HibernateException;
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/SessionException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/SessionException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/SessionException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+//$Id: SessionException.java 9024 2006-01-11 22:38:24Z steveebersole $
+package org.hibernate;
+
+/**
+ * Thrown when the user calls a method of a {@link Session} that is in an
+ * inappropropriate state for the given call (for example, the the session
+ * is closed or disconnected).
+ *
+ * @author Gavin King
+ */
+public class SessionException extends HibernateException {
+
+ /**
+ * Constructs a new SessionException with the given message.
+ *
+ * @param message The message indicating the specific problem.
+ */
+ public SessionException(String message) {
+ super( message );
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/SessionException.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/SessionFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/SessionFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/SessionFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,224 @@
+//$Id: SessionFactory.java 8754 2005-12-05 23:36:59Z steveebersole $
+package org.hibernate;
+
+import java.io.Serializable;
+import java.sql.Connection;
+import java.util.Map;
+import java.util.Set;
+
+import javax.naming.Referenceable;
+
+import org.hibernate.metadata.ClassMetadata;
+import org.hibernate.metadata.CollectionMetadata;
+import org.hibernate.stat.Statistics;
+import org.hibernate.engine.FilterDefinition;
+
+/**
+ * Creates <tt>Session</tt>s. Usually an application has a single <tt>SessionFactory</tt>.
+ * Threads servicing client requests obtain <tt>Session</tt>s from the factory.<br>
+ * <br>
+ * Implementors must be threadsafe.<br>
+ * <br>
+ * <tt>SessionFactory</tt>s are immutable. The behaviour of a <tt>SessionFactory</tt> is
+ * controlled by properties supplied at configuration time. These properties are defined
+ * on <tt>Environment</tt>.
+ *
+ * @see Session
+ * @see org.hibernate.cfg.Environment
+ * @see org.hibernate.cfg.Configuration
+ * @see org.hibernate.connection.ConnectionProvider
+ * @see org.hibernate.transaction.TransactionFactory
+ * @author Gavin King
+ */
+public interface SessionFactory extends Referenceable, Serializable {
+
+ /**
+ * Open a <tt>Session</tt> on the given connection.
+ * <p>
+ * Note that the second-level cache will be disabled if you
+ * supply a JDBC connection. Hibernate will not be able to track
+ * any statements you might have executed in the same transaction.
+ * Consider implementing your own <tt>ConnectionProvider</tt>.
+ *
+ * @param connection a connection provided by the application.
+ * @return Session
+ */
+ public org.hibernate.classic.Session openSession(Connection connection);
+
+ /**
+ * Create database connection and open a <tt>Session</tt> on it, specifying an
+ * interceptor.
+ *
+ * @param interceptor a session-scoped interceptor
+ * @return Session
+ * @throws HibernateException
+ */
+ public org.hibernate.classic.Session openSession(Interceptor interceptor) throws HibernateException;
+
+ /**
+ * Open a <tt>Session</tt> on the given connection, specifying an interceptor.
+ * <p>
+ * Note that the second-level cache will be disabled if you
+ * supply a JDBC connection. Hibernate will not be able to track
+ * any statements you might have executed in the same transaction.
+ * Consider implementing your own <tt>ConnectionProvider</tt>.
+ *
+ * @param connection a connection provided by the application.
+ * @param interceptor a session-scoped interceptor
+ * @return Session
+ */
+ public org.hibernate.classic.Session openSession(Connection connection, Interceptor interceptor);
+
+ /**
+ * Create database connection and open a <tt>Session</tt> on it.
+ *
+ * @return Session
+ * @throws HibernateException
+ */
+ public org.hibernate.classic.Session openSession() throws HibernateException;
+
+ /**
+ * Obtains the current session. The definition of what exactly "current"
+ * means controlled by the {@link org.hibernate.context.CurrentSessionContext} impl configured
+ * for use.
+ * <p/>
+ * Note that for backwards compatibility, if a {@link org.hibernate.context.CurrentSessionContext}
+ * is not configured but a JTA {@link org.hibernate.transaction.TransactionManagerLookup}
+ * is configured this will default to the {@link org.hibernate.context.JTASessionContext}
+ * impl.
+ *
+ * @return The current session.
+ * @throws HibernateException Indicates an issue locating a suitable current session.
+ */
+ public org.hibernate.classic.Session getCurrentSession() throws HibernateException;
+
+ /**
+ * Get the <tt>ClassMetadata</tt> associated with the given entity class
+ *
+ * @see org.hibernate.metadata.ClassMetadata
+ */
+ public ClassMetadata getClassMetadata(Class persistentClass) throws HibernateException;
+
+ /**
+ * Get the <tt>ClassMetadata</tt> associated with the given entity name
+ *
+ * @see org.hibernate.metadata.ClassMetadata
+ * @since 3.0
+ */
+ public ClassMetadata getClassMetadata(String entityName) throws HibernateException;
+
+ /**
+ * Get the <tt>CollectionMetadata</tt> associated with the named collection role
+ *
+ * @see org.hibernate.metadata.CollectionMetadata
+ */
+ public CollectionMetadata getCollectionMetadata(String roleName) throws HibernateException;
+
+
+ /**
+ * Get all <tt>ClassMetadata</tt> as a <tt>Map</tt> from entityname <tt>String</tt>
+ * to metadata object
+ *
+ * @see org.hibernate.metadata.ClassMetadata
+ * @return a map from <tt>String</tt> an entity name to <tt>ClassMetaData</tt>
+ * @since 3.0 changed key from <tt>Class</tt> to <tt>String</tt>
+ */
+ public Map getAllClassMetadata() throws HibernateException;
+
+ /**
+ * Get all <tt>CollectionMetadata</tt> as a <tt>Map</tt> from role name
+ * to metadata object
+ *
+ * @see org.hibernate.metadata.CollectionMetadata
+ * @return a map from <tt>String</tt> to <tt>CollectionMetadata</tt>
+ */
+ public Map getAllCollectionMetadata() throws HibernateException;
+
+ /**
+ * Get the statistics for this session factory
+ */
+ public Statistics getStatistics();
+
+ /**
+ * Destroy this <tt>SessionFactory</tt> and release all resources (caches,
+ * connection pools, etc). It is the responsibility of the application
+ * to ensure that there are no open <tt>Session</tt>s before calling
+ * <tt>close()</tt>.
+ */
+ public void close() throws HibernateException;
+
+ /**
+ * Was this <tt>SessionFactory</tt> already closed?
+ */
+ public boolean isClosed();
+
+ /**
+ * Evict all entries from the second-level cache. This method occurs outside
+ * of any transaction; it performs an immediate "hard" remove, so does not respect
+ * any transaction isolation semantics of the usage strategy. Use with care.
+ */
+ public void evict(Class persistentClass) throws HibernateException;
+ /**
+ * Evict an entry from the second-level cache. This method occurs outside
+ * of any transaction; it performs an immediate "hard" remove, so does not respect
+ * any transaction isolation semantics of the usage strategy. Use with care.
+ */
+ public void evict(Class persistentClass, Serializable id) throws HibernateException;
+ /**
+ * Evict all entries from the second-level cache. This method occurs outside
+ * of any transaction; it performs an immediate "hard" remove, so does not respect
+ * any transaction isolation semantics of the usage strategy. Use with care.
+ */
+ public void evictEntity(String entityName) throws HibernateException;
+ /**
+ * Evict an entry from the second-level cache. This method occurs outside
+ * of any transaction; it performs an immediate "hard" remove, so does not respect
+ * any transaction isolation semantics of the usage strategy. Use with care.
+ */
+ public void evictEntity(String entityName, Serializable id) throws HibernateException;
+ /**
+ * Evict all entries from the second-level cache. This method occurs outside
+ * of any transaction; it performs an immediate "hard" remove, so does not respect
+ * any transaction isolation semantics of the usage strategy. Use with care.
+ */
+ public void evictCollection(String roleName) throws HibernateException;
+ /**
+ * Evict an entry from the second-level cache. This method occurs outside
+ * of any transaction; it performs an immediate "hard" remove, so does not respect
+ * any transaction isolation semantics of the usage strategy. Use with care.
+ */
+ public void evictCollection(String roleName, Serializable id) throws HibernateException;
+
+ /**
+ * Evict any query result sets cached in the default query cache region.
+ */
+ public void evictQueries() throws HibernateException;
+ /**
+ * Evict any query result sets cached in the named query cache region.
+ */
+ public void evictQueries(String cacheRegion) throws HibernateException;
+ /**
+ * Get a new stateless session.
+ */
+ public StatelessSession openStatelessSession();
+ /**
+ * Get a new stateless session for the given JDBC connection.
+ */
+ public StatelessSession openStatelessSession(Connection connection);
+
+ /**
+ * Obtain a set of the names of all filters defined on this SessionFactory.
+ *
+ * @return The set of filter names.
+ */
+ public Set getDefinedFilterNames();
+
+ /**
+ * Obtain the definition of a filter by name.
+ *
+ * @param filterName The name of the filter for which to obtain the definition.
+ * @return The filter definition.
+ * @throws HibernateException If no filter defined with the given name.
+ */
+ public FilterDefinition getFilterDefinition(String filterName) throws HibernateException;
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/StaleObjectStateException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/StaleObjectStateException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/StaleObjectStateException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,45 @@
+//$Id: StaleObjectStateException.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate;
+
+import java.io.Serializable;
+
+import org.hibernate.pretty.MessageHelper;
+
+/**
+ * A <tt>StaleStateException</tt> that carries information
+ * about a particular entity instance that was the source
+ * of the failure.
+ *
+ * @author Gavin King
+ */
+public class StaleObjectStateException extends StaleStateException {
+ private final String entityName;
+ private final Serializable identifier;
+
+ public StaleObjectStateException(String persistentClass, Serializable identifier) {
+ super("Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)");
+ this.entityName = persistentClass;
+ this.identifier = identifier;
+ }
+
+ public String getEntityName() {
+ return entityName;
+ }
+
+ public Serializable getIdentifier() {
+ return identifier;
+ }
+
+ public String getMessage() {
+ return super.getMessage() + ": " +
+ MessageHelper.infoString(entityName, identifier);
+ }
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/StaleStateException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/StaleStateException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/StaleStateException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,21 @@
+//$Id: StaleStateException.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate;
+
+/**
+ * Thrown when a version number or timestamp check failed, indicating that the
+ * <tt>Session</tt> contained stale data (when using long transactions
+ * with versioning). Also occurs if we try delete or update a row that does
+ * not exist.<br>
+ * <br>
+ * Note that this exception often indicates that the user failed to specify the
+ * correct <tt>unsaved-value</tt> strategy for a class!
+ *
+ * @see StaleObjectStateException
+ * @author Gavin King
+ */
+public class StaleStateException extends HibernateException {
+
+ public StaleStateException(String s) {
+ super(s);
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/StaleStateException.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/StatelessSession.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/StatelessSession.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/StatelessSession.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,217 @@
+//$Id: StatelessSession.java 9705 2006-03-28 19:59:31Z steve.ebersole at jboss.com $
+package org.hibernate;
+
+import java.io.Serializable;
+import java.sql.Connection;
+
+/**
+ * A command-oriented API for performing bulk operations
+ * against a database.<br>
+ * <br>
+ * A stateless session does not implement a first-level cache nor
+ * interact with any second-level cache, nor does it implement
+ * transactional write-behind or automatic dirty checking, nor do
+ * operations cascade to associated instances. Collections are
+ * ignored by a stateless session. Operations performed via a
+ * stateless session bypass Hibernate's event model and
+ * interceptors. Stateless sessions are vulnerable to data
+ * aliasing effects, due to the lack of a first-level cache.<br>
+ * <br>
+ * For certain kinds of transactions, a stateless session may
+ * perform slightly faster than a stateful session.
+ *
+ * @author Gavin King
+ */
+public interface StatelessSession extends Serializable {
+ /**
+ * Close the stateless session and release the JDBC connection.
+ */
+ public void close();
+
+ /**
+ * Insert a row.
+ *
+ * @param entity a new transient instance
+ */
+ public Serializable insert(Object entity);
+
+ /**
+ * Insert a row.
+ *
+ * @param entityName The entityName for the entity to be inserted
+ * @param entity a new transient instance
+ * @return the identifier of the instance
+ */
+ public Serializable insert(String entityName, Object entity);
+
+ /**
+ * Update a row.
+ *
+ * @param entity a detached entity instance
+ */
+ public void update(Object entity);
+
+ /**
+ * Update a row.
+ *
+ * @param entityName The entityName for the entity to be updated
+ * @param entity a detached entity instance
+ */
+ public void update(String entityName, Object entity);
+
+ /**
+ * Delete a row.
+ *
+ * @param entity a detached entity instance
+ */
+ public void delete(Object entity);
+
+ /**
+ * Delete a row.
+ *
+ * @param entityName The entityName for the entity to be deleted
+ * @param entity a detached entity instance
+ */
+ public void delete(String entityName, Object entity);
+
+ /**
+ * Retrieve a row.
+ *
+ * @return a detached entity instance
+ */
+ public Object get(String entityName, Serializable id);
+
+ /**
+ * Retrieve a row.
+ *
+ * @return a detached entity instance
+ */
+ public Object get(Class entityClass, Serializable id);
+
+ /**
+ * Retrieve a row, obtaining the specified lock mode.
+ *
+ * @return a detached entity instance
+ */
+ public Object get(String entityName, Serializable id, LockMode lockMode);
+
+ /**
+ * Retrieve a row, obtaining the specified lock mode.
+ *
+ * @return a detached entity instance
+ */
+ public Object get(Class entityClass, Serializable id, LockMode lockMode);
+
+ /**
+ * Refresh the entity instance state from the database.
+ *
+ * @param entity The entity to be refreshed.
+ */
+ public void refresh(Object entity);
+
+ /**
+ * Refresh the entity instance state from the database.
+ *
+ * @param entityName The entityName for the entity to be refreshed.
+ * @param entity The entity to be refreshed.
+ */
+ public void refresh(String entityName, Object entity);
+
+ /**
+ * Refresh the entity instance state from the database.
+ *
+ * @param entity The entity to be refreshed.
+ * @param lockMode The LockMode to be applied.
+ */
+ public void refresh(Object entity, LockMode lockMode);
+
+ /**
+ * Refresh the entity instance state from the database.
+ *
+ * @param entityName The entityName for the entity to be refreshed.
+ * @param entity The entity to be refreshed.
+ * @param lockMode The LockMode to be applied.
+ */
+ public void refresh(String entityName, Object entity, LockMode lockMode);
+
+ /**
+ * Create a new instance of <tt>Query</tt> for the given HQL query string.
+ * Entities returned by the query are detached.
+ */
+ public Query createQuery(String queryString);
+
+ /**
+ * Obtain an instance of <tt>Query</tt> for a named query string defined in
+ * the mapping file. Entities returned by the query are detached.
+ */
+ public Query getNamedQuery(String queryName);
+
+ /**
+ * Create a new <tt>Criteria</tt> instance, for the given entity class,
+ * or a superclass of an entity class. Entities returned by the query are
+ * detached.
+ *
+ * @param persistentClass a class, which is persistent, or has persistent subclasses
+ * @return Criteria
+ */
+ public Criteria createCriteria(Class persistentClass);
+
+ /**
+ * Create a new <tt>Criteria</tt> instance, for the given entity class,
+ * or a superclass of an entity class, with the given alias.
+ * Entities returned by the query are detached.
+ *
+ * @param persistentClass a class, which is persistent, or has persistent subclasses
+ * @return Criteria
+ */
+ public Criteria createCriteria(Class persistentClass, String alias);
+
+ /**
+ * Create a new <tt>Criteria</tt> instance, for the given entity name.
+ * Entities returned by the query are detached.
+ *
+ * @param entityName
+ * @return Criteria
+ */
+ public Criteria createCriteria(String entityName);
+
+ /**
+ * Create a new <tt>Criteria</tt> instance, for the given entity name,
+ * with the given alias. Entities returned by the query are detached.
+ *
+ * @param entityName
+ * @return Criteria
+ */
+ public Criteria createCriteria(String entityName, String alias);
+
+ /**
+ * Create a new instance of <tt>SQLQuery</tt> for the given SQL query string.
+ * Entities returned by the query are detached.
+ *
+ * @param queryString a SQL query
+ * @return SQLQuery
+ * @throws HibernateException
+ */
+ public SQLQuery createSQLQuery(String queryString) throws HibernateException;
+
+ /**
+ * Begin a Hibernate transaction.
+ */
+ public Transaction beginTransaction();
+
+ /**
+ * Get the current Hibernate transaction.
+ */
+ public Transaction getTransaction();
+
+ /**
+ * Returns the current JDBC connection associated with this
+ * instance.<br>
+ * <br>
+ * If the session is using aggressive connection release (as in a
+ * CMT environment), it is the application's responsibility to
+ * close the connection returned by this call. Otherwise, the
+ * application should not close the connection.
+ */
+ public Connection connection();
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/StatelessSession.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/Transaction.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/Transaction.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/Transaction.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,105 @@
+//$Id: Transaction.java 9595 2006-03-10 18:14:21Z steve.ebersole at jboss.com $
+package org.hibernate;
+
+import javax.transaction.Synchronization;
+
+/**
+ * Allows the application to define units of work, while
+ * maintaining abstraction from the underlying transaction
+ * implementation (eg. JTA, JDBC).<br>
+ * <br>
+ * A transaction is associated with a <tt>Session</tt> and is
+ * usually instantiated by a call to <tt>Session.beginTransaction()</tt>.
+ * A single session might span multiple transactions since
+ * the notion of a session (a conversation between the application
+ * and the datastore) is of coarser granularity than the notion of
+ * a transaction. However, it is intended that there be at most one
+ * uncommitted <tt>Transaction</tt> associated with a particular
+ * <tt>Session</tt> at any time.<br>
+ * <br>
+ * Implementors are not intended to be threadsafe.
+ *
+ * @see Session#beginTransaction()
+ * @see org.hibernate.transaction.TransactionFactory
+ * @author Anton van Straaten
+ */
+public interface Transaction {
+
+ /**
+ * Begin a new transaction.
+ */
+ public void begin() throws HibernateException;
+
+ /**
+ * Flush the associated <tt>Session</tt> and end the unit of work (unless
+ * we are in {@link FlushMode#NEVER}.
+ * </p>
+ * This method will commit the underlying transaction if and only
+ * if the underlying transaction was initiated by this object.
+ *
+ * @throws HibernateException
+ */
+ public void commit() throws HibernateException;
+
+ /**
+ * Force the underlying transaction to roll back.
+ *
+ * @throws HibernateException
+ */
+ public void rollback() throws HibernateException;
+
+ /**
+ * Was this transaction rolled back or set to rollback only?
+ * <p/>
+ * This only accounts for actions initiated from this local transaction.
+ * If, for example, the underlying transaction is forced to rollback via
+ * some other means, this method still reports false because the rollback
+ * was not initiated from here.
+ *
+ * @return boolean True if the transaction was rolled back via this
+ * local transaction; false otherwise.
+ * @throws HibernateException
+ */
+ public boolean wasRolledBack() throws HibernateException;
+
+ /**
+ * Check if this transaction was successfully committed.
+ * <p/>
+ * This method could return <tt>false</tt> even after successful invocation
+ * of {@link #commit}. As an example, JTA based strategies no-op on
+ * {@link #commit} calls if they did not start the transaction; in that case,
+ * they also report {@link #wasCommitted} as false.
+ *
+ * @return boolean True if the transaction was (unequivocally) committed
+ * via this local transaction; false otherwise.
+ * @throws HibernateException
+ */
+ public boolean wasCommitted() throws HibernateException;
+
+ /**
+ * Is this transaction still active?
+ * <p/>
+ * Again, this only returns information in relation to the
+ * local transaction, not the actual underlying transaction.
+ *
+ * @return boolean Treu if this local transaction is still active.
+ */
+ public boolean isActive() throws HibernateException;
+
+ /**
+ * Register a user synchronization callback for this transaction.
+ *
+ * @param synchronization The Synchronization callback to register.
+ * @throws HibernateException
+ */
+ public void registerSynchronization(Synchronization synchronization)
+ throws HibernateException;
+
+ /**
+ * Set the transaction timeout for any transaction started by
+ * a subsequent call to <tt>begin()</tt> on this instance.
+ *
+ * @param seconds The number of seconds before a timeout.
+ */
+ public void setTimeout(int seconds);
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/TransactionException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/TransactionException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/TransactionException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+//$Id: TransactionException.java 10312 2006-08-23 12:43:54Z steve.ebersole at jboss.com $
+package org.hibernate;
+
+/**
+ * Indicates that a transaction could not be begun, committed
+ * or rolled back.
+ *
+ * @see Transaction
+ * @author Anton van Straaten
+ */
+
+public class TransactionException extends HibernateException {
+
+ public TransactionException(String message, Throwable root) {
+ super(message,root);
+ }
+
+ public TransactionException(String message) {
+ super(message);
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/TransientObjectException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/TransientObjectException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/TransientObjectException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,23 @@
+//$Id: TransientObjectException.java 6877 2005-05-23 15:00:25Z oneovthafew $
+package org.hibernate;
+
+/**
+ * Thrown when the user passes a transient instance to a <tt>Session</tt>
+ * method that expects a persistent instance.
+ *
+ * @author Gavin King
+ */
+
+public class TransientObjectException extends HibernateException {
+
+ public TransientObjectException(String s) {
+ super(s);
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/TypeMismatchException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/TypeMismatchException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/TypeMismatchException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,21 @@
+//$Id: $
+package org.hibernate;
+
+/**
+ * Used when a user provided type does not match the expected one
+ *
+ * @author Emmanuel Bernard
+ */
+public class TypeMismatchException extends HibernateException {
+ public TypeMismatchException(Throwable root) {
+ super( root );
+ }
+
+ public TypeMismatchException(String s) {
+ super( s );
+ }
+
+ public TypeMismatchException(String string, Throwable root) {
+ super( string, root );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/UnresolvableObjectException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/UnresolvableObjectException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/UnresolvableObjectException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,52 @@
+//$Id: UnresolvableObjectException.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate;
+
+import java.io.Serializable;
+
+import org.hibernate.pretty.MessageHelper;
+
+/**
+ * Thrown when Hibernate could not resolve an object by id, especially when
+ * loading an association.
+ *
+ * @author Gavin King
+ */
+public class UnresolvableObjectException extends HibernateException {
+
+ private final Serializable identifier;
+ private final String entityName;
+
+ public UnresolvableObjectException(Serializable identifier, String clazz) {
+ this("No row with the given identifier exists", identifier, clazz);
+ }
+ UnresolvableObjectException(String message, Serializable identifier, String clazz) {
+ super(message);
+ this.identifier = identifier;
+ this.entityName = clazz;
+ }
+ public Serializable getIdentifier() {
+ return identifier;
+ }
+
+ public String getMessage() {
+ return super.getMessage() + ": " +
+ MessageHelper.infoString(entityName, identifier);
+ }
+
+ public String getEntityName() {
+ return entityName;
+ }
+
+ public static void throwIfNull(Object o, Serializable id, String clazz)
+ throws UnresolvableObjectException {
+ if (o==null) throw new UnresolvableObjectException(id, clazz);
+ }
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/WrongClassException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/WrongClassException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/WrongClassException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,47 @@
+//$Id: WrongClassException.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate;
+
+import java.io.Serializable;
+
+/**
+ * Thrown when <tt>Session.load()</tt> selects a row with
+ * the given primary key (identifier value) but the row's
+ * discriminator value specifies a subclass that is not
+ * assignable to the class requested by the user.
+ *
+ * @author Gavin King
+ */
+public class WrongClassException extends HibernateException {
+
+ private final Serializable identifier;
+ private final String entityName;
+
+ public WrongClassException(String msg, Serializable identifier, String clazz) {
+ super(msg);
+ this.identifier = identifier;
+ this.entityName = clazz;
+ }
+ public Serializable getIdentifier() {
+ return identifier;
+ }
+
+ public String getMessage() {
+ return "Object with id: " +
+ identifier +
+ " was not of the specified subclass: " +
+ entityName +
+ " (" + super.getMessage() + ")" ;
+ }
+
+ public String getEntityName() {
+ return entityName;
+ }
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/action/BulkOperationCleanupAction.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/action/BulkOperationCleanupAction.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/action/BulkOperationCleanupAction.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,146 @@
+// $Id: BulkOperationCleanupAction.java 9897 2006-05-05 20:50:27Z max.andersen at jboss.com $
+package org.hibernate.action;
+
+import org.hibernate.HibernateException;
+import org.hibernate.metadata.ClassMetadata;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.persister.entity.Queryable;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Iterator;
+import java.util.HashSet;
+import java.util.ArrayList;
+
+/**
+ * Implementation of BulkOperationCleanupAction.
+ *
+ * @author Steve Ebersole
+ */
+public class BulkOperationCleanupAction implements Executable, Serializable {
+
+ private final SessionImplementor session;
+
+ private final Set affectedEntityNames = new HashSet();
+ private final Set affectedCollectionRoles = new HashSet();
+ private final Serializable[] spaces;
+
+ public BulkOperationCleanupAction(SessionImplementor session, Queryable[] affectedQueryables) {
+ this.session = session;
+ // TODO : probably better to calculate these and pass them in, as it'll be more performant
+ ArrayList tmpSpaces = new ArrayList();
+ for ( int i = 0; i < affectedQueryables.length; i++ ) {
+ if ( affectedQueryables[i].hasCache() ) {
+ affectedEntityNames.add( affectedQueryables[i].getEntityName() );
+ }
+ Set roles = session.getFactory().getCollectionRolesByEntityParticipant( affectedQueryables[i].getEntityName() );
+ if ( roles != null ) {
+ affectedCollectionRoles.addAll( roles );
+ }
+ for ( int y = 0; y < affectedQueryables[i].getQuerySpaces().length; y++ ) {
+ tmpSpaces.add( affectedQueryables[i].getQuerySpaces()[y] );
+ }
+ }
+ this.spaces = new Serializable[ tmpSpaces.size() ];
+ for ( int i = 0; i < tmpSpaces.size(); i++ ) {
+ this.spaces[i] = ( Serializable ) tmpSpaces.get( i );
+ }
+ }
+
+ /** Create an action that will evict collection and entity regions based on queryspaces (table names).
+ * TODO: cache the autodetected information and pass it in instead.
+ **/
+ public BulkOperationCleanupAction(SessionImplementor session, Set querySpaces) {
+ this.session = session;
+
+ Set tmpSpaces = new HashSet(querySpaces);
+ SessionFactoryImplementor factory = session.getFactory();
+ Iterator iterator = factory.getAllClassMetadata().entrySet().iterator();
+ while ( iterator.hasNext() ) {
+ Map.Entry entry = (Map.Entry) iterator.next();
+ String entityName = (String) entry.getKey();
+ EntityPersister persister = factory.getEntityPersister( entityName );
+ Serializable[] entitySpaces = persister.getQuerySpaces();
+
+ if (affectedEntity( querySpaces, entitySpaces )) {
+ if ( persister.hasCache() ) {
+ affectedEntityNames.add( persister.getEntityName() );
+ }
+ Set roles = session.getFactory().getCollectionRolesByEntityParticipant( persister.getEntityName() );
+ if ( roles != null ) {
+ affectedCollectionRoles.addAll( roles );
+ }
+ for ( int y = 0; y < entitySpaces.length; y++ ) {
+ tmpSpaces.add( entitySpaces[y] );
+ }
+ }
+
+ }
+ this.spaces = (Serializable[]) tmpSpaces.toArray( new Serializable[tmpSpaces.size()] );
+ }
+
+
+ /** returns true if no queryspaces or if there are a match */
+ private boolean affectedEntity(Set querySpaces, Serializable[] entitySpaces) {
+ if(querySpaces==null || querySpaces.isEmpty()) {
+ return true;
+ }
+
+ for ( int i = 0; i < entitySpaces.length; i++ ) {
+ if ( querySpaces.contains( entitySpaces[i] ) ) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void init() {
+ evictEntityRegions();
+ evictCollectionRegions();
+ }
+
+ public boolean hasAfterTransactionCompletion() {
+ return true;
+ }
+
+ public void afterTransactionCompletion(boolean success) throws HibernateException {
+ evictEntityRegions();
+ evictCollectionRegions();
+ }
+
+ public Serializable[] getPropertySpaces() {
+ return spaces;
+ }
+
+ public void beforeExecutions() throws HibernateException {
+ // nothing to do
+ }
+
+ public void execute() throws HibernateException {
+ // nothing to do
+ }
+
+ private void evictEntityRegions() {
+ if ( affectedEntityNames != null ) {
+ Iterator itr = affectedEntityNames.iterator();
+ while ( itr.hasNext() ) {
+ final String entityName = ( String ) itr.next();
+ session.getFactory().evictEntity( entityName );
+ }
+ }
+ }
+
+ private void evictCollectionRegions() {
+ if ( affectedCollectionRoles != null ) {
+ Iterator itr = affectedCollectionRoles.iterator();
+ while ( itr.hasNext() ) {
+ final String roleName = ( String ) itr.next();
+ session.getFactory().evictCollection( roleName );
+ }
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/action/CollectionAction.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/action/CollectionAction.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/action/CollectionAction.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,150 @@
+//$Id: CollectionAction.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.action;
+
+import org.hibernate.cache.access.SoftLock;
+import org.hibernate.cache.CacheException;
+import org.hibernate.cache.CacheKey;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.pretty.MessageHelper;
+import org.hibernate.util.StringHelper;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+
+/**
+ * Any action relating to insert/update/delete of a collection
+ * @author Gavin King
+ */
+public abstract class CollectionAction implements Executable, Serializable, Comparable {
+
+ private transient CollectionPersister persister;
+ private final Serializable key;
+ private Serializable finalKey;
+ private final SessionImplementor session;
+ private SoftLock lock;
+ private final String collectionRole;
+ private final PersistentCollection collection;
+
+ public CollectionAction(
+ final CollectionPersister persister,
+ final PersistentCollection collection,
+ final Serializable key,
+ final SessionImplementor session) throws CacheException {
+ this.persister = persister;
+ this.session = session;
+ this.key = key;
+ this.collectionRole = persister.getRole();
+ this.collection = collection;
+ }
+
+ protected PersistentCollection getCollection() {
+ return collection;
+ }
+
+ private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
+ ois.defaultReadObject();
+ persister = session.getFactory().getCollectionPersister( collectionRole );
+ }
+
+ public void afterTransactionCompletion(boolean success) throws CacheException {
+ if ( persister.hasCache() ) {
+ final CacheKey ck = new CacheKey(
+ key,
+ persister.getKeyType(),
+ persister.getRole(),
+ session.getEntityMode(),
+ session.getFactory()
+ );
+ persister.getCacheAccessStrategy().unlockItem( ck, lock );
+ }
+ }
+
+ public boolean hasAfterTransactionCompletion() {
+ return persister.hasCache();
+ }
+
+ public Serializable[] getPropertySpaces() {
+ return persister.getCollectionSpaces();
+ }
+
+ protected final CollectionPersister getPersister() {
+ return persister;
+ }
+
+ protected final Serializable getKey() {
+ finalKey = key;
+ if ( key instanceof DelayedPostInsertIdentifier ) {
+ // need to look it up from the persistence-context
+ finalKey = session.getPersistenceContext().getEntry( collection.getOwner() ).getId();
+ if ( finalKey == key ) {
+ // we may be screwed here since the collection action is about to execute
+ // and we do not know the final owner key value
+ }
+ }
+ return finalKey;
+ }
+
+ protected final SessionImplementor getSession() {
+ return session;
+ }
+
+ public final void beforeExecutions() throws CacheException {
+ // we need to obtain the lock before any actions are
+ // executed, since this may be an inverse="true"
+ // bidirectional association and it is one of the
+ // earlier entity actions which actually updates
+ // the database (this action is resposible for
+ // second-level cache invalidation only)
+ if ( persister.hasCache() ) {
+ final CacheKey ck = new CacheKey(
+ key,
+ persister.getKeyType(),
+ persister.getRole(),
+ session.getEntityMode(),
+ session.getFactory()
+ );
+ lock = persister.getCacheAccessStrategy().lockItem( ck, null );
+ }
+ }
+
+ protected final void evict() throws CacheException {
+ if ( persister.hasCache() ) {
+ CacheKey ck = new CacheKey(
+ key,
+ persister.getKeyType(),
+ persister.getRole(),
+ session.getEntityMode(),
+ session.getFactory()
+ );
+ persister.getCacheAccessStrategy().remove( ck );
+ }
+ }
+
+ public String toString() {
+ return StringHelper.unqualify( getClass().getName() ) +
+ MessageHelper.infoString( collectionRole, key );
+ }
+
+ public int compareTo(Object other) {
+ CollectionAction action = ( CollectionAction ) other;
+ //sort first by role name
+ int roleComparison = collectionRole.compareTo( action.collectionRole );
+ if ( roleComparison != 0 ) {
+ return roleComparison;
+ }
+ else {
+ //then by fk
+ return persister.getKeyType()
+ .compare( key, action.key, session.getEntityMode() );
+ }
+ }
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/action/CollectionRecreateAction.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/action/CollectionRecreateAction.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/action/CollectionRecreateAction.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,47 @@
+//$Id: CollectionRecreateAction.java 7147 2005-06-15 13:20:13Z oneovthafew $
+package org.hibernate.action;
+
+import org.hibernate.HibernateException;
+import org.hibernate.cache.CacheException;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.collection.CollectionPersister;
+
+import java.io.Serializable;
+
+public final class CollectionRecreateAction extends CollectionAction {
+
+ public CollectionRecreateAction(
+ final PersistentCollection collection,
+ final CollectionPersister persister,
+ final Serializable id,
+ final SessionImplementor session)
+ throws CacheException {
+ super( persister, collection, id, session );
+ }
+
+ public void execute() throws HibernateException {
+ final PersistentCollection collection = getCollection();
+
+ getPersister().recreate( collection, getKey(), getSession() );
+
+ getSession().getPersistenceContext()
+ .getCollectionEntry(collection)
+ .afterAction(collection);
+
+ evict();
+
+ if ( getSession().getFactory().getStatistics().isStatisticsEnabled() ) {
+ getSession().getFactory().getStatisticsImplementor()
+ .recreateCollection( getPersister().getRole() );
+ }
+ }
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/action/CollectionRemoveAction.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/action/CollectionRemoveAction.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/action/CollectionRemoveAction.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,53 @@
+//$Id: CollectionRemoveAction.java 7147 2005-06-15 13:20:13Z oneovthafew $
+package org.hibernate.action;
+
+import org.hibernate.HibernateException;
+import org.hibernate.cache.CacheException;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.collection.CollectionPersister;
+
+import java.io.Serializable;
+
+public final class CollectionRemoveAction extends CollectionAction {
+
+ private boolean emptySnapshot;
+
+ public CollectionRemoveAction(
+ final PersistentCollection collection,
+ final CollectionPersister persister,
+ final Serializable id,
+ final boolean emptySnapshot,
+ final SessionImplementor session)
+ throws CacheException {
+ super( persister, collection, id, session );
+ this.emptySnapshot = emptySnapshot;
+ }
+
+ public void execute() throws HibernateException {
+ if ( !emptySnapshot ) getPersister().remove( getKey(), getSession() );
+
+ final PersistentCollection collection = getCollection();
+ if (collection!=null) {
+ getSession().getPersistenceContext()
+ .getCollectionEntry(collection)
+ .afterAction(collection);
+ }
+
+ evict();
+
+ if ( getSession().getFactory().getStatistics().isStatisticsEnabled() ) {
+ getSession().getFactory().getStatisticsImplementor()
+ .removeCollection( getPersister().getRole() );
+ }
+ }
+
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/action/CollectionUpdateAction.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/action/CollectionUpdateAction.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/action/CollectionUpdateAction.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,78 @@
+//$Id: CollectionUpdateAction.java 7631 2005-07-24 21:26:21Z oneovthafew $
+package org.hibernate.action;
+
+import org.hibernate.AssertionFailure;
+import org.hibernate.HibernateException;
+import org.hibernate.cache.CacheException;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.pretty.MessageHelper;
+
+import java.io.Serializable;
+
+public final class CollectionUpdateAction extends CollectionAction {
+
+ private final boolean emptySnapshot;
+
+ public CollectionUpdateAction(
+ final PersistentCollection collection,
+ final CollectionPersister persister,
+ final Serializable id,
+ final boolean emptySnapshot,
+ final SessionImplementor session)
+ throws CacheException {
+ super( persister, collection, id, session );
+ this.emptySnapshot = emptySnapshot;
+ }
+
+ public void execute() throws HibernateException {
+ final Serializable id = getKey();
+ final SessionImplementor session = getSession();
+ final CollectionPersister persister = getPersister();
+ final PersistentCollection collection = getCollection();
+ boolean affectedByFilters = persister.isAffectedByEnabledFilters(session);
+
+ if ( !collection.wasInitialized() ) {
+ if ( !collection.hasQueuedOperations() ) throw new AssertionFailure( "no queued adds" );
+ //do nothing - we only need to notify the cache...
+ }
+ else if ( !affectedByFilters && collection.empty() ) {
+ if ( !emptySnapshot ) persister.remove( id, session );
+ }
+ else if ( collection.needsRecreate(persister) ) {
+ if (affectedByFilters) {
+ throw new HibernateException(
+ "cannot recreate collection while filter is enabled: " +
+ MessageHelper.collectionInfoString( persister, id, persister.getFactory() )
+ );
+ }
+ if ( !emptySnapshot ) persister.remove( id, session );
+ persister.recreate( collection, id, session );
+ }
+ else {
+ persister.deleteRows( collection, id, session );
+ persister.updateRows( collection, id, session );
+ persister.insertRows( collection, id, session );
+ }
+
+ getSession().getPersistenceContext()
+ .getCollectionEntry(collection)
+ .afterAction(collection);
+
+ evict();
+
+ if ( getSession().getFactory().getStatistics().isStatisticsEnabled() ) {
+ getSession().getFactory().getStatisticsImplementor().
+ updateCollection( getPersister().getRole() );
+ }
+ }
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/action/DelayedPostInsertIdentifier.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/action/DelayedPostInsertIdentifier.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/action/DelayedPostInsertIdentifier.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,49 @@
+package org.hibernate.action;
+
+import java.io.Serializable;
+
+/**
+ * Acts as a stand-in for an entity identifier which is supposed to be
+ * generated on insert (like an IDENTITY column) where the insert needed to
+ * be delayed because we were outside a transaction when the persist
+ * occurred (save currently still performs the insert).
+ * <p/>
+ * The stand-in is only used within the {@link org.hibernate.engine.PersistenceContext}
+ * in order to distinguish one instance from another; it is never injected into
+ * the entity instance or returned to the client...
+ *
+ * @author Steve Ebersole
+ */
+public class DelayedPostInsertIdentifier implements Serializable {
+ private static long SEQUENCE = 0;
+ private final long sequence;
+
+ public DelayedPostInsertIdentifier() {
+ synchronized( DelayedPostInsertIdentifier.class ) {
+ if ( SEQUENCE == Long.MAX_VALUE ) {
+ SEQUENCE = 0;
+ }
+ this.sequence = SEQUENCE++;
+ }
+ }
+
+ public boolean equals(Object o) {
+ if ( this == o ) {
+ return true;
+ }
+ if ( o == null || getClass() != o.getClass() ) {
+ return false;
+ }
+ final DelayedPostInsertIdentifier that = ( DelayedPostInsertIdentifier ) o;
+ return sequence == that.sequence;
+ }
+
+ public int hashCode() {
+ return ( int ) ( sequence ^ ( sequence >>> 32 ) );
+ }
+
+ public String toString() {
+ return "<delayed:" + sequence + ">";
+
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/action/EntityAction.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/action/EntityAction.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/action/EntityAction.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,136 @@
+//$Id: EntityAction.java 11402 2007-04-11 14:24:35Z steve.ebersole at jboss.com $
+package org.hibernate.action;
+
+import org.hibernate.AssertionFailure;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.pretty.MessageHelper;
+import org.hibernate.util.StringHelper;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+
+/**
+ * Base class for actions relating to insert/update/delete of an entity
+ * instance.
+ *
+ * @author Gavin King
+ */
+public abstract class EntityAction implements Executable, Serializable, Comparable {
+
+ private final String entityName;
+ private final Serializable id;
+ private final Object instance;
+ private final SessionImplementor session;
+
+ private transient EntityPersister persister;
+
+ /**
+ * Instantiate an action.
+ *
+ * @param session The session from which this action is coming.
+ * @param id The id of the entity
+ * @param instance The entiyt instance
+ * @param persister The entity persister
+ */
+ protected EntityAction(SessionImplementor session, Serializable id, Object instance, EntityPersister persister) {
+ this.entityName = persister.getEntityName();
+ this.id = id;
+ this.instance = instance;
+ this.session = session;
+ this.persister = persister;
+ }
+
+ protected abstract boolean hasPostCommitEventListeners();
+
+ /**
+ * entity name accessor
+ *
+ * @return The entity name
+ */
+ public String getEntityName() {
+ return entityName;
+ }
+
+ /**
+ * entity id accessor
+ *
+ * @return The entity id
+ */
+ public final Serializable getId() {
+ if ( id instanceof DelayedPostInsertIdentifier ) {
+ return session.getPersistenceContext().getEntry( instance ).getId();
+ }
+ return id;
+ }
+
+ /**
+ * entity instance accessor
+ *
+ * @return The entity instance
+ */
+ public final Object getInstance() {
+ return instance;
+ }
+
+ /**
+ * originating session accessor
+ *
+ * @return The session from which this action originated.
+ */
+ public final SessionImplementor getSession() {
+ return session;
+ }
+
+ /**
+ * entity persister accessor
+ *
+ * @return The entity persister
+ */
+ public final EntityPersister getPersister() {
+ return persister;
+ }
+
+ public final Serializable[] getPropertySpaces() {
+ return persister.getPropertySpaces();
+ }
+
+ public void beforeExecutions() {
+ throw new AssertionFailure( "beforeExecutions() called for non-collection action" );
+ }
+
+ public boolean hasAfterTransactionCompletion() {
+ return persister.hasCache() || hasPostCommitEventListeners();
+ }
+
+ public String toString() {
+ return StringHelper.unqualify( getClass().getName() ) + MessageHelper.infoString( entityName, id );
+ }
+
+ public int compareTo(Object other) {
+ EntityAction action = ( EntityAction ) other;
+ //sort first by entity name
+ int roleComparison = entityName.compareTo( action.entityName );
+ if ( roleComparison != 0 ) {
+ return roleComparison;
+ }
+ else {
+ //then by id
+ return persister.getIdentifierType().compare( id, action.id, session.getEntityMode() );
+ }
+ }
+
+ /**
+ * Serialization...
+ *
+ * @param ois Thed object stream
+ * @throws IOException Problem performing the default stream reading
+ * @throws ClassNotFoundException Problem performing the default stream reading
+ */
+ private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
+ ois.defaultReadObject();
+ persister = session.getFactory().getEntityPersister( entityName );
+ }
+}
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/action/EntityDeleteAction.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/action/EntityDeleteAction.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/action/EntityDeleteAction.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,174 @@
+//$Id: EntityDeleteAction.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.action;
+
+import java.io.Serializable;
+
+import org.hibernate.AssertionFailure;
+import org.hibernate.HibernateException;
+import org.hibernate.cache.CacheKey;
+import org.hibernate.cache.access.SoftLock;
+import org.hibernate.engine.EntityEntry;
+import org.hibernate.engine.EntityKey;
+import org.hibernate.engine.PersistenceContext;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.event.PostDeleteEvent;
+import org.hibernate.event.PostDeleteEventListener;
+import org.hibernate.event.PreDeleteEvent;
+import org.hibernate.event.PreDeleteEventListener;
+import org.hibernate.event.EventSource;
+import org.hibernate.persister.entity.EntityPersister;
+
+public final class EntityDeleteAction extends EntityAction {
+
+ private final Object version;
+ private SoftLock lock;
+ private final boolean isCascadeDeleteEnabled;
+ private final Object[] state;
+
+ public EntityDeleteAction(
+ final Serializable id,
+ final Object[] state,
+ final Object version,
+ final Object instance,
+ final EntityPersister persister,
+ final boolean isCascadeDeleteEnabled,
+ final SessionImplementor session) {
+ super( session, id, instance, persister );
+ this.version = version;
+ this.isCascadeDeleteEnabled = isCascadeDeleteEnabled;
+ this.state = state;
+ }
+
+ public void execute() throws HibernateException {
+ Serializable id = getId();
+ EntityPersister persister = getPersister();
+ SessionImplementor session = getSession();
+ Object instance = getInstance();
+
+ boolean veto = preDelete();
+
+ Object version = this.version;
+ if ( persister.isVersionPropertyGenerated() ) {
+ // we need to grab the version value from the entity, otherwise
+ // we have issues with generated-version entities that may have
+ // multiple actions queued during the same flush
+ version = persister.getVersion( instance, session.getEntityMode() );
+ }
+
+ final CacheKey ck;
+ if ( persister.hasCache() ) {
+ ck = new CacheKey(
+ id,
+ persister.getIdentifierType(),
+ persister.getRootEntityName(),
+ session.getEntityMode(),
+ session.getFactory()
+ );
+ lock = persister.getCacheAccessStrategy().lockItem( ck, version );
+ }
+ else {
+ ck = null;
+ }
+
+ if ( !isCascadeDeleteEnabled && !veto ) {
+ persister.delete( id, version, instance, session );
+ }
+
+ //postDelete:
+ // After actually deleting a row, record the fact that the instance no longer
+ // exists on the database (needed for identity-column key generation), and
+ // remove it from the session cache
+ final PersistenceContext persistenceContext = session.getPersistenceContext();
+ EntityEntry entry = persistenceContext.removeEntry( instance );
+ if ( entry == null ) {
+ throw new AssertionFailure( "possible nonthreadsafe access to session" );
+ }
+ entry.postDelete();
+
+ EntityKey key = new EntityKey( entry.getId(), entry.getPersister(), session.getEntityMode() );
+ persistenceContext.removeEntity(key);
+ persistenceContext.removeProxy(key);
+
+ if ( persister.hasCache() ) persister.getCacheAccessStrategy().remove( ck );
+
+ postDelete();
+
+ if ( getSession().getFactory().getStatistics().isStatisticsEnabled() && !veto ) {
+ getSession().getFactory().getStatisticsImplementor()
+ .deleteEntity( getPersister().getEntityName() );
+ }
+ }
+
+ private boolean preDelete() {
+ PreDeleteEventListener[] preListeners = getSession().getListeners()
+ .getPreDeleteEventListeners();
+ boolean veto = false;
+ if (preListeners.length>0) {
+ PreDeleteEvent preEvent = new PreDeleteEvent( getInstance(), getId(), state, getPersister() );
+ for ( int i = 0; i < preListeners.length; i++ ) {
+ veto = preListeners[i].onPreDelete(preEvent) || veto;
+ }
+ }
+ return veto;
+ }
+
+ private void postDelete() {
+ PostDeleteEventListener[] postListeners = getSession().getListeners()
+ .getPostDeleteEventListeners();
+ if (postListeners.length>0) {
+ PostDeleteEvent postEvent = new PostDeleteEvent(
+ getInstance(),
+ getId(),
+ state,
+ getPersister(),
+ (EventSource) getSession()
+ );
+ for ( int i = 0; i < postListeners.length; i++ ) {
+ postListeners[i].onPostDelete(postEvent);
+ }
+ }
+ }
+
+ private void postCommitDelete() {
+ PostDeleteEventListener[] postListeners = getSession().getListeners()
+ .getPostCommitDeleteEventListeners();
+ if (postListeners.length>0) {
+ PostDeleteEvent postEvent = new PostDeleteEvent(
+ getInstance(),
+ getId(),
+ state,
+ getPersister(),
+ (EventSource) getSession()
+ );
+ for ( int i = 0; i < postListeners.length; i++ ) {
+ postListeners[i].onPostDelete(postEvent);
+ }
+ }
+ }
+
+ public void afterTransactionCompletion(boolean success) throws HibernateException {
+ if ( getPersister().hasCache() ) {
+ final CacheKey ck = new CacheKey(
+ getId(),
+ getPersister().getIdentifierType(),
+ getPersister().getRootEntityName(),
+ getSession().getEntityMode(),
+ getSession().getFactory()
+ );
+ getPersister().getCacheAccessStrategy().unlockItem( ck, lock );
+ }
+ postCommitDelete();
+ }
+
+ protected boolean hasPostCommitEventListeners() {
+ return getSession().getListeners().getPostCommitDeleteEventListeners().length>0;
+ }
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/action/EntityIdentityInsertAction.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/action/EntityIdentityInsertAction.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/action/EntityIdentityInsertAction.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,159 @@
+//$Id: EntityIdentityInsertAction.java 10680 2006-11-01 22:53:30Z epbernard $
+package org.hibernate.action;
+
+import java.io.Serializable;
+
+import org.hibernate.HibernateException;
+import org.hibernate.AssertionFailure;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.EntityKey;
+import org.hibernate.event.PostInsertEvent;
+import org.hibernate.event.PostInsertEventListener;
+import org.hibernate.event.PreInsertEvent;
+import org.hibernate.event.PreInsertEventListener;
+import org.hibernate.event.EventSource;
+import org.hibernate.persister.entity.EntityPersister;
+
+public final class EntityIdentityInsertAction extends EntityAction {
+ private final Object[] state;
+ private final boolean isDelayed;
+ private final EntityKey delayedEntityKey;
+ //private CacheEntry cacheEntry;
+ private Serializable generatedId;
+
+ public EntityIdentityInsertAction(
+ Object[] state,
+ Object instance,
+ EntityPersister persister,
+ SessionImplementor session,
+ boolean isDelayed) throws HibernateException {
+ super( session, null, instance, persister );
+ this.state = state;
+ this.isDelayed = isDelayed;
+ delayedEntityKey = isDelayed ? generateDelayedEntityKey() : null;
+ }
+
+ public void execute() throws HibernateException {
+
+ final EntityPersister persister = getPersister();
+ final SessionImplementor session = getSession();
+ final Object instance = getInstance();
+
+ boolean veto = preInsert();
+
+ // Don't need to lock the cache here, since if someone
+ // else inserted the same pk first, the insert would fail
+
+ if ( !veto ) {
+ generatedId = persister.insert( state, instance, session );
+ if ( persister.hasInsertGeneratedProperties() ) {
+ persister.processInsertGeneratedProperties( generatedId, instance, state, session );
+ }
+ //need to do that here rather than in the save event listener to let
+ //the post insert events to have a id-filled entity when IDENTITY is used (EJB3)
+ persister.setIdentifier( instance, generatedId, session.getEntityMode() );
+ }
+
+
+ //TODO: this bit actually has to be called after all cascades!
+ // but since identity insert is called *synchronously*,
+ // instead of asynchronously as other actions, it isn't
+ /*if ( persister.hasCache() && !persister.isCacheInvalidationRequired() ) {
+ cacheEntry = new CacheEntry(object, persister, session);
+ persister.getCache().insert(generatedId, cacheEntry);
+ }*/
+
+ postInsert();
+
+ if ( session.getFactory().getStatistics().isStatisticsEnabled() && !veto ) {
+ session.getFactory().getStatisticsImplementor()
+ .insertEntity( getPersister().getEntityName() );
+ }
+
+ }
+
+ private void postInsert() {
+ if ( isDelayed ) {
+ getSession().getPersistenceContext().replaceDelayedEntityIdentityInsertKeys( delayedEntityKey, generatedId );
+ }
+ PostInsertEventListener[] postListeners = getSession().getListeners()
+ .getPostInsertEventListeners();
+ if (postListeners.length>0) {
+ PostInsertEvent postEvent = new PostInsertEvent(
+ getInstance(),
+ generatedId,
+ state,
+ getPersister(),
+ (EventSource) getSession()
+ );
+ for ( int i = 0; i < postListeners.length; i++ ) {
+ postListeners[i].onPostInsert(postEvent);
+ }
+ }
+ }
+
+ private void postCommitInsert() {
+ PostInsertEventListener[] postListeners = getSession().getListeners()
+ .getPostCommitInsertEventListeners();
+ if (postListeners.length>0) {
+ PostInsertEvent postEvent = new PostInsertEvent(
+ getInstance(),
+ generatedId,
+ state,
+ getPersister(),
+ (EventSource) getSession()
+ );
+ for ( int i = 0; i < postListeners.length; i++ ) {
+ postListeners[i].onPostInsert(postEvent);
+ }
+ }
+ }
+
+ private boolean preInsert() {
+ PreInsertEventListener[] preListeners = getSession().getListeners()
+ .getPreInsertEventListeners();
+ boolean veto = false;
+ if (preListeners.length>0) {
+ PreInsertEvent preEvent = new PreInsertEvent( getInstance(), null, state, getPersister(), getSession() );
+ for ( int i = 0; i < preListeners.length; i++ ) {
+ veto = preListeners[i].onPreInsert(preEvent) || veto;
+ }
+ }
+ return veto;
+ }
+
+ //Make 100% certain that this is called before any subsequent ScheduledUpdate.afterTransactionCompletion()!!
+ public void afterTransactionCompletion(boolean success) throws HibernateException {
+ //TODO: reenable if we also fix the above todo
+ /*EntityPersister persister = getEntityPersister();
+ if ( success && persister.hasCache() && !persister.isCacheInvalidationRequired() ) {
+ persister.getCache().afterInsert( getGeneratedId(), cacheEntry );
+ }*/
+ postCommitInsert();
+ }
+
+ public boolean hasAfterTransactionCompletion() {
+ //TODO: simply remove this override
+ // if we fix the above todos
+ return hasPostCommitEventListeners();
+ }
+
+ protected boolean hasPostCommitEventListeners() {
+ return getSession().getListeners().getPostCommitInsertEventListeners().length>0;
+ }
+
+ public final Serializable getGeneratedId() {
+ return generatedId;
+ }
+
+ public EntityKey getDelayedEntityKey() {
+ return delayedEntityKey;
+ }
+
+ private synchronized EntityKey generateDelayedEntityKey() {
+ if ( !isDelayed ) {
+ throw new AssertionFailure( "cannot request delayed entity-key for non-delayed post-insert-id generation" );
+ }
+ return new EntityKey( new DelayedPostInsertIdentifier(), getPersister(), getSession().getEntityMode() );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/action/EntityInsertAction.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/action/EntityInsertAction.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/action/EntityInsertAction.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,199 @@
+//$Id: EntityInsertAction.java 11402 2007-04-11 14:24:35Z steve.ebersole at jboss.com $
+package org.hibernate.action;
+
+import java.io.Serializable;
+
+import org.hibernate.AssertionFailure;
+import org.hibernate.HibernateException;
+import org.hibernate.cache.CacheKey;
+import org.hibernate.cache.entry.CacheEntry;
+import org.hibernate.engine.EntityEntry;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.Versioning;
+import org.hibernate.event.PostInsertEvent;
+import org.hibernate.event.PostInsertEventListener;
+import org.hibernate.event.PreInsertEvent;
+import org.hibernate.event.PreInsertEventListener;
+import org.hibernate.event.EventSource;
+import org.hibernate.persister.entity.EntityPersister;
+
+public final class EntityInsertAction extends EntityAction {
+
+ private Object[] state;
+ private Object version;
+ private Object cacheEntry;
+
+ public EntityInsertAction(
+ Serializable id,
+ Object[] state,
+ Object instance,
+ Object version,
+ EntityPersister persister,
+ SessionImplementor session) throws HibernateException {
+ super( session, id, instance, persister );
+ this.state = state;
+ this.version = version;
+ }
+
+ public Object[] getState() {
+ return state;
+ }
+
+ public void execute() throws HibernateException {
+ EntityPersister persister = getPersister();
+ SessionImplementor session = getSession();
+ Object instance = getInstance();
+ Serializable id = getId();
+
+ boolean veto = preInsert();
+
+ // Don't need to lock the cache here, since if someone
+ // else inserted the same pk first, the insert would fail
+
+ if ( !veto ) {
+
+ persister.insert( id, state, instance, session );
+
+ EntityEntry entry = session.getPersistenceContext().getEntry( instance );
+ if ( entry == null ) {
+ throw new AssertionFailure( "possible nonthreadsafe access to session" );
+ }
+
+ entry.postInsert();
+
+ if ( persister.hasInsertGeneratedProperties() ) {
+ persister.processInsertGeneratedProperties( id, instance, state, session );
+ if ( persister.isVersionPropertyGenerated() ) {
+ version = Versioning.getVersion(state, persister);
+ }
+ entry.postUpdate(instance, state, version);
+ }
+
+ }
+
+ final SessionFactoryImplementor factory = getSession().getFactory();
+
+ if ( isCachePutEnabled( persister, session ) ) {
+
+ CacheEntry ce = new CacheEntry(
+ state,
+ persister,
+ persister.hasUninitializedLazyProperties( instance, session.getEntityMode() ),
+ version,
+ session,
+ instance
+ );
+
+ cacheEntry = persister.getCacheEntryStructure().structure(ce);
+ final CacheKey ck = new CacheKey(
+ id,
+ persister.getIdentifierType(),
+ persister.getRootEntityName(),
+ session.getEntityMode(),
+ session.getFactory()
+ );
+// boolean put = persister.getCache().insert(ck, cacheEntry);
+ boolean put = persister.getCacheAccessStrategy().insert( ck, cacheEntry, version );
+
+ if ( put && factory.getStatistics().isStatisticsEnabled() ) {
+ factory.getStatisticsImplementor().secondLevelCachePut( getPersister().getCacheAccessStrategy().getRegion().getName() );
+ }
+
+ }
+
+ postInsert();
+
+ if ( factory.getStatistics().isStatisticsEnabled() && !veto ) {
+ factory.getStatisticsImplementor()
+ .insertEntity( getPersister().getEntityName() );
+ }
+
+ }
+
+ private void postInsert() {
+ PostInsertEventListener[] postListeners = getSession().getListeners()
+ .getPostInsertEventListeners();
+ if ( postListeners.length > 0 ) {
+ PostInsertEvent postEvent = new PostInsertEvent(
+ getInstance(),
+ getId(),
+ state,
+ getPersister(),
+ (EventSource) getSession()
+ );
+ for ( int i = 0; i < postListeners.length; i++ ) {
+ postListeners[i].onPostInsert(postEvent);
+ }
+ }
+ }
+
+ private void postCommitInsert() {
+ PostInsertEventListener[] postListeners = getSession().getListeners()
+ .getPostCommitInsertEventListeners();
+ if ( postListeners.length > 0 ) {
+ PostInsertEvent postEvent = new PostInsertEvent(
+ getInstance(),
+ getId(),
+ state,
+ getPersister(),
+ (EventSource) getSession()
+ );
+ for ( int i = 0; i < postListeners.length; i++ ) {
+ postListeners[i].onPostInsert(postEvent);
+ }
+ }
+ }
+
+ private boolean preInsert() {
+ PreInsertEventListener[] preListeners = getSession().getListeners()
+ .getPreInsertEventListeners();
+ boolean veto = false;
+ if (preListeners.length>0) {
+ PreInsertEvent preEvent = new PreInsertEvent( getInstance(), getId(), state, getPersister(), getSession() );
+ for ( int i = 0; i < preListeners.length; i++ ) {
+ veto = preListeners[i].onPreInsert(preEvent) || veto;
+ }
+ }
+ return veto;
+ }
+
+ //Make 100% certain that this is called before any subsequent ScheduledUpdate.afterTransactionCompletion()!!
+ public void afterTransactionCompletion(boolean success) throws HibernateException {
+ EntityPersister persister = getPersister();
+ if ( success && isCachePutEnabled( persister, getSession() ) ) {
+ final CacheKey ck = new CacheKey(
+ getId(),
+ persister.getIdentifierType(),
+ persister.getRootEntityName(),
+ getSession().getEntityMode(),
+ getSession().getFactory()
+ );
+ boolean put = persister.getCacheAccessStrategy().afterInsert( ck, cacheEntry, version );
+
+ if ( put && getSession().getFactory().getStatistics().isStatisticsEnabled() ) {
+ getSession().getFactory().getStatisticsImplementor()
+ .secondLevelCachePut( getPersister().getCacheAccessStrategy().getRegion().getName() );
+ }
+ }
+ postCommitInsert();
+ }
+
+ protected boolean hasPostCommitEventListeners() {
+ return getSession().getListeners().getPostCommitInsertEventListeners().length>0;
+ }
+
+ private boolean isCachePutEnabled(EntityPersister persister, SessionImplementor session) {
+ return persister.hasCache() &&
+ !persister.isCacheInvalidationRequired() &&
+ session.getCacheMode().isPutEnabled();
+ }
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/action/EntityUpdateAction.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/action/EntityUpdateAction.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/action/EntityUpdateAction.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,257 @@
+//$Id: EntityUpdateAction.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.action;
+
+import java.io.Serializable;
+
+import org.hibernate.AssertionFailure;
+import org.hibernate.HibernateException;
+import org.hibernate.cache.CacheException;
+import org.hibernate.cache.CacheKey;
+import org.hibernate.cache.access.SoftLock;
+import org.hibernate.cache.entry.CacheEntry;
+import org.hibernate.engine.EntityEntry;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.Status;
+import org.hibernate.engine.Versioning;
+import org.hibernate.event.PostUpdateEvent;
+import org.hibernate.event.PostUpdateEventListener;
+import org.hibernate.event.PreUpdateEvent;
+import org.hibernate.event.PreUpdateEventListener;
+import org.hibernate.event.EventSource;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.type.TypeFactory;
+
+public final class EntityUpdateAction extends EntityAction {
+
+ private final Object[] state;
+ private final Object[] previousState;
+ private final Object previousVersion;
+ private Object nextVersion;
+ private final int[] dirtyFields;
+ private final boolean hasDirtyCollection;
+ private final Object rowId;
+ private Object cacheEntry;
+ private SoftLock lock;
+
+ public EntityUpdateAction(
+ final Serializable id,
+ final Object[] state,
+ final int[] dirtyProperties,
+ final boolean hasDirtyCollection,
+ final Object[] previousState,
+ final Object previousVersion,
+ final Object nextVersion,
+ final Object instance,
+ final Object rowId,
+ final EntityPersister persister,
+ final SessionImplementor session) throws HibernateException {
+ super( session, id, instance, persister );
+ this.state = state;
+ this.previousState = previousState;
+ this.previousVersion = previousVersion;
+ this.nextVersion = nextVersion;
+ this.dirtyFields = dirtyProperties;
+ this.hasDirtyCollection = hasDirtyCollection;
+ this.rowId = rowId;
+ }
+
+ public void execute() throws HibernateException {
+ Serializable id = getId();
+ EntityPersister persister = getPersister();
+ SessionImplementor session = getSession();
+ Object instance = getInstance();
+
+ boolean veto = preUpdate();
+
+ final SessionFactoryImplementor factory = getSession().getFactory();
+ Object previousVersion = this.previousVersion;
+ if ( persister.isVersionPropertyGenerated() ) {
+ // we need to grab the version value from the entity, otherwise
+ // we have issues with generated-version entities that may have
+ // multiple actions queued during the same flush
+ previousVersion = persister.getVersion( instance, session.getEntityMode() );
+ }
+
+ final CacheKey ck;
+ if ( persister.hasCache() ) {
+ ck = new CacheKey(
+ id,
+ persister.getIdentifierType(),
+ persister.getRootEntityName(),
+ session.getEntityMode(),
+ session.getFactory()
+ );
+ lock = persister.getCacheAccessStrategy().lockItem( ck, previousVersion );
+ }
+ else {
+ ck = null;
+ }
+
+ if ( !veto ) {
+ persister.update(
+ id,
+ state,
+ dirtyFields,
+ hasDirtyCollection,
+ previousState,
+ previousVersion,
+ instance,
+ rowId,
+ session
+ );
+ }
+
+ EntityEntry entry = getSession().getPersistenceContext().getEntry( instance );
+ if ( entry == null ) {
+ throw new AssertionFailure( "possible nonthreadsafe access to session" );
+ }
+
+ if ( entry.getStatus()==Status.MANAGED || persister.isVersionPropertyGenerated() ) {
+ // get the updated snapshot of the entity state by cloning current state;
+ // it is safe to copy in place, since by this time no-one else (should have)
+ // has a reference to the array
+ TypeFactory.deepCopy(
+ state,
+ persister.getPropertyTypes(),
+ persister.getPropertyCheckability(),
+ state,
+ session
+ );
+ if ( persister.hasUpdateGeneratedProperties() ) {
+ // this entity defines proeprty generation, so process those generated
+ // values...
+ persister.processUpdateGeneratedProperties( id, instance, state, session );
+ if ( persister.isVersionPropertyGenerated() ) {
+ nextVersion = Versioning.getVersion( state, persister );
+ }
+ }
+ // have the entity entry perform post-update processing, passing it the
+ // update state and the new version (if one).
+ entry.postUpdate( instance, state, nextVersion );
+ }
+
+ if ( persister.hasCache() ) {
+ if ( persister.isCacheInvalidationRequired() || entry.getStatus()!=Status.MANAGED ) {
+ persister.getCacheAccessStrategy().remove( ck );
+ }
+ else {
+ //TODO: inefficient if that cache is just going to ignore the updated state!
+ CacheEntry ce = new CacheEntry(
+ state,
+ persister,
+ persister.hasUninitializedLazyProperties( instance, session.getEntityMode() ),
+ nextVersion,
+ getSession(),
+ instance
+ );
+ cacheEntry = persister.getCacheEntryStructure().structure( ce );
+ boolean put = persister.getCacheAccessStrategy().update( ck, cacheEntry, nextVersion, previousVersion );
+ if ( put && factory.getStatistics().isStatisticsEnabled() ) {
+ factory.getStatisticsImplementor().secondLevelCachePut( getPersister().getCacheAccessStrategy().getRegion().getName() );
+ }
+ }
+ }
+
+ postUpdate();
+
+ if ( factory.getStatistics().isStatisticsEnabled() && !veto ) {
+ factory.getStatisticsImplementor()
+ .updateEntity( getPersister().getEntityName() );
+ }
+ }
+
+ private void postUpdate() {
+ PostUpdateEventListener[] postListeners = getSession().getListeners()
+ .getPostUpdateEventListeners();
+ if (postListeners.length>0) {
+ PostUpdateEvent postEvent = new PostUpdateEvent(
+ getInstance(),
+ getId(),
+ state,
+ previousState,
+ getPersister(),
+ (EventSource) getSession()
+ );
+ for ( int i = 0; i < postListeners.length; i++ ) {
+ postListeners[i].onPostUpdate(postEvent);
+ }
+ }
+ }
+
+ private void postCommitUpdate() {
+ PostUpdateEventListener[] postListeners = getSession().getListeners()
+ .getPostCommitUpdateEventListeners();
+ if (postListeners.length>0) {
+ PostUpdateEvent postEvent = new PostUpdateEvent(
+ getInstance(),
+ getId(),
+ state,
+ previousState,
+ getPersister(),
+ (EventSource) getSession()
+ );
+ for ( int i = 0; i < postListeners.length; i++ ) {
+ postListeners[i].onPostUpdate(postEvent);
+ }
+ }
+ }
+
+ private boolean preUpdate() {
+ PreUpdateEventListener[] preListeners = getSession().getListeners()
+ .getPreUpdateEventListeners();
+ boolean veto = false;
+ if (preListeners.length>0) {
+ PreUpdateEvent preEvent = new PreUpdateEvent(
+ getInstance(),
+ getId(),
+ state,
+ previousState,
+ getPersister(),
+ getSession()
+ );
+ for ( int i = 0; i < preListeners.length; i++ ) {
+ veto = preListeners[i].onPreUpdate(preEvent) || veto;
+ }
+ }
+ return veto;
+ }
+
+ public void afterTransactionCompletion(boolean success) throws CacheException {
+ EntityPersister persister = getPersister();
+ if ( persister.hasCache() ) {
+
+ final CacheKey ck = new CacheKey(
+ getId(),
+ persister.getIdentifierType(),
+ persister.getRootEntityName(),
+ getSession().getEntityMode(),
+ getSession().getFactory()
+ );
+
+ if ( success && cacheEntry!=null /*!persister.isCacheInvalidationRequired()*/ ) {
+ boolean put = persister.getCacheAccessStrategy().afterUpdate( ck, cacheEntry, nextVersion, previousVersion, lock );
+
+ if ( put && getSession().getFactory().getStatistics().isStatisticsEnabled() ) {
+ getSession().getFactory().getStatisticsImplementor().secondLevelCachePut( getPersister().getCacheAccessStrategy().getRegion().getName() );
+ }
+ }
+ else {
+ persister.getCacheAccessStrategy().unlockItem( ck, lock );
+ }
+ }
+ postCommitUpdate();
+ }
+
+ protected boolean hasPostCommitEventListeners() {
+ return getSession().getListeners().getPostCommitUpdateEventListeners().length>0;
+ }
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/action/Executable.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/action/Executable.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/action/Executable.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,39 @@
+//$Id: Executable.java 6607 2005-04-29 15:26:11Z oneovthafew $
+package org.hibernate.action;
+
+import org.hibernate.HibernateException;
+
+import java.io.Serializable;
+
+/**
+ * An operation which may be scheduled for later execution.
+ * Usually, the operation is a database insert/update/delete,
+ * together with required second-level cache management.
+ *
+ * @author Gavin King
+ */
+public interface Executable {
+ /**
+ * Called before executing any actions
+ */
+ public void beforeExecutions() throws HibernateException;
+ /**
+ * Execute this action
+ */
+ public void execute() throws HibernateException;
+ /**
+ * Do we need to retain this instance until after the
+ * transaction completes?
+ * @return false if this class defines a no-op
+ * <tt>hasAfterTransactionCompletion()</tt>
+ */
+ public boolean hasAfterTransactionCompletion();
+ /**
+ * Called after the transaction completes
+ */
+ public void afterTransactionCompletion(boolean success) throws HibernateException;
+ /**
+ * What spaces (tables) are affected by this action?
+ */
+ public Serializable[] getPropertySpaces();
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/action/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/action/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/action/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,10 @@
+<html>
+<head>
+</head>
+<body>
+<p>
+ This package defines "actions" that are scheduled for
+ asycnchronous execution by the event listeners.
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/action/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/AbstractClassTransformerImpl.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/AbstractClassTransformerImpl.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/AbstractClassTransformerImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,45 @@
+//$Id: $
+package org.hibernate.bytecode;
+
+import org.hibernate.bytecode.util.ClassFilter;
+import org.hibernate.bytecode.util.FieldFilter;
+
+import java.security.ProtectionDomain;
+
+/**
+ * @author Emmanuel Bernard
+ * @author Steve Ebersole
+ */
+public abstract class AbstractClassTransformerImpl implements ClassTransformer {
+
+ protected final ClassFilter classFilter;
+ protected final FieldFilter fieldFilter;
+
+ protected AbstractClassTransformerImpl(ClassFilter classFilter, FieldFilter fieldFilter) {
+ this.classFilter = classFilter;
+ this.fieldFilter = fieldFilter;
+ }
+
+ public byte[] transform(
+ ClassLoader loader,
+ String className,
+ Class classBeingRedefined,
+ ProtectionDomain protectionDomain,
+ byte[] classfileBuffer) {
+ // to be safe...
+ className = className.replace( '/', '.' );
+ if ( classFilter.shouldInstrumentClass( className ) ) {
+ return doTransform( loader, className, classBeingRedefined, protectionDomain, classfileBuffer );
+ }
+ else {
+ return classfileBuffer;
+ }
+ }
+
+ protected abstract byte[] doTransform(
+ ClassLoader loader,
+ String className,
+ Class classBeingRedefined,
+ ProtectionDomain protectionDomain,
+ byte[] classfileBuffer);
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/BasicProxyFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/BasicProxyFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/BasicProxyFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,10 @@
+package org.hibernate.bytecode;
+
+/**
+ * A proxy factory for "basic proxy" generation
+ *
+ * @author Steve Ebersole
+ */
+public interface BasicProxyFactory {
+ public Object getProxy();
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/BytecodeProvider.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/BytecodeProvider.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/BytecodeProvider.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,49 @@
+package org.hibernate.bytecode;
+
+import org.hibernate.bytecode.util.ClassFilter;
+import org.hibernate.bytecode.util.FieldFilter;
+
+/**
+ * Contract for providers of bytecode services to Hibernate.
+ * <p/>
+ * Bytecode requirements break down into basically 3 areas<ol>
+ * <li>proxy generation (both for runtime-lazy-loading and basic proxy generation)
+ * {@link #getProxyFactoryFactory()}
+ * <li>bean relection optimization {@link #getReflectionOptimizer}
+ * <li>field-access instumentation {@link #getTransformer}
+ * </ol>
+ *
+ * @author Steve Ebersole
+ */
+public interface BytecodeProvider {
+ /**
+ * Retrieve the specific factory for this provider capable of
+ * generating run-time proxies for lazy-loading purposes.
+ *
+ * @return The provider specifc factory.
+ */
+ public ProxyFactoryFactory getProxyFactoryFactory();
+
+ /**
+ * Retrieve the ReflectionOptimizer delegate for this provider
+ * capable of generating reflection optimization components.
+ *
+ * @param clazz The class to be reflected upon.
+ * @param getterNames Names of all property getters to be accessed via reflection.
+ * @param setterNames Names of all property setters to be accessed via reflection.
+ * @param types The types of all properties to be accessed.
+ * @return The reflection optimization delegate.
+ */
+ public ReflectionOptimizer getReflectionOptimizer(Class clazz, String[] getterNames, String[] setterNames, Class[] types);
+
+ /**
+ * Generate a ClassTransformer capable of performing bytecode manipulation.
+ *
+ * @param classFilter filter used to limit which classes are to be instrumented
+ * via this ClassTransformer.
+ * @param fieldFilter filter used to limit which fields are to be instrumented
+ * via this ClassTransformer.
+ * @return The appropriate ClassTransformer.
+ */
+ public ClassTransformer getTransformer(ClassFilter classFilter, FieldFilter fieldFilter);
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/ClassTransformer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/ClassTransformer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/ClassTransformer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,34 @@
+//$Id: $
+package org.hibernate.bytecode;
+
+import java.security.ProtectionDomain;
+
+/**
+ * A persistence provider provides an instance of this interface
+ * to the PersistenceUnitInfo.addTransformer method.
+ * The supplied transformer instance will get called to transform
+ * entity class files when they are loaded and redefined. The transformation
+ * occurs before the class is defined by the JVM
+ *
+ *
+ * @author <a href="mailto:bill at jboss.org">Bill Burke</a>
+ * @author Emmanuel Bernard
+ */
+public interface ClassTransformer
+{
+ /**
+ * Invoked when a class is being loaded or redefined to add hooks for persistence bytecode manipulation
+ *
+ * @param loader the defining class loaderof the class being transformed. It may be null if using bootstrap loader
+ * @param classname The name of the class being transformed
+ * @param classBeingRedefined If an already loaded class is being redefined, then pass this as a parameter
+ * @param protectionDomain ProtectionDomain of the class being (re)-defined
+ * @param classfileBuffer The input byte buffer in class file format
+ * @return A well-formed class file that can be loaded
+ */
+ public byte[] transform(ClassLoader loader,
+ String classname,
+ Class classBeingRedefined,
+ ProtectionDomain protectionDomain,
+ byte[] classfileBuffer);
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/InstrumentedClassLoader.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/InstrumentedClassLoader.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/InstrumentedClassLoader.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,54 @@
+package org.hibernate.bytecode;
+
+import org.hibernate.bytecode.util.ByteCodeHelper;
+
+import java.io.InputStream;
+
+/**
+ * A specialized classloader which performs bytecode enhancement on class
+ * definitions as they are loaded into the classloader scope.
+ *
+ * @author Emmanuel Bernard
+ * @author Steve Ebersole
+ */
+public class InstrumentedClassLoader extends ClassLoader {
+
+ private ClassTransformer classTransformer;
+
+ public InstrumentedClassLoader(ClassLoader parent, ClassTransformer classTransformer) {
+ super( parent );
+ this.classTransformer = classTransformer;
+ }
+
+ public Class loadClass(String name) throws ClassNotFoundException {
+ if ( name.startsWith( "java." ) || classTransformer == null ) {
+ return getParent().loadClass( name );
+ }
+
+ Class c = findLoadedClass( name );
+ if ( c != null ) {
+ return c;
+ }
+
+ InputStream is = this.getResourceAsStream( name.replace( '.', '/' ) + ".class" );
+ if ( is == null ) {
+ throw new ClassNotFoundException( name + " not found" );
+ }
+
+ try {
+ byte[] originalBytecode = ByteCodeHelper.readByteCode( is );
+ byte[] transformedBytecode = classTransformer.transform( getParent(), name, null, null, originalBytecode );
+ if ( originalBytecode == transformedBytecode ) {
+ // no transformations took place, so handle it as we would a
+ // non-instrumented class
+ return getParent().loadClass( name );
+ }
+ else {
+ return defineClass( name, transformedBytecode, 0, transformedBytecode.length );
+ }
+ }
+ catch( Throwable t ) {
+ throw new ClassNotFoundException( name + " not found", t );
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/ProxyFactoryFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/ProxyFactoryFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/ProxyFactoryFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,37 @@
+package org.hibernate.bytecode;
+
+import org.hibernate.proxy.ProxyFactory;
+
+/**
+ * An interface for factories of {@link ProxyFactory proxy factory} instances.
+ * <p/>
+ * Currently used to abstract from the tupizer whether we are using CGLIB or
+ * Javassist for lazy proxy generation.
+ *
+ * @author Steve Ebersole
+ */
+public interface ProxyFactoryFactory {
+ /**
+ * Build a proxy factory specifically for handling runtime
+ * lazy loading.
+ *
+ * @return The lazy-load proxy factory.
+ */
+ public ProxyFactory buildProxyFactory();
+
+ /**
+ * Build a proxy factory for basic proxy concerns. The return
+ * should be capable of properly handling newInstance() calls.
+ * <p/>
+ * Should build basic proxies essentially equivalent to JDK proxies in
+ * terms of capabilities, but should be able to deal with abstract super
+ * classes in addition to proxy interfaces.
+ * <p/>
+ * Must pass in either superClass or interfaces (or both).
+ *
+ * @param superClass The abstract super class (or null if none).
+ * @param interfaces Interfaces to be proxied (or null if none).
+ * @return The proxy class
+ */
+ public BasicProxyFactory buildBasicProxyFactory(Class superClass, Class[] interfaces);
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/ReflectionOptimizer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/ReflectionOptimizer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/ReflectionOptimizer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,35 @@
+package org.hibernate.bytecode;
+
+/**
+ * Represents reflection optimization for a particular class.
+ *
+ * @author Steve Ebersole
+ */
+public interface ReflectionOptimizer {
+
+ public InstantiationOptimizer getInstantiationOptimizer();
+ public AccessOptimizer getAccessOptimizer();
+
+ /**
+ * Represents optimized entity instantiation.
+ */
+ public static interface InstantiationOptimizer {
+ /**
+ * Perform instantiation of an instance of the underlying class.
+ *
+ * @return The new instance.
+ */
+ public Object newInstance();
+ }
+
+ /**
+ * Represents optimized entity property access.
+ *
+ * @author Steve Ebersole
+ */
+ public interface AccessOptimizer {
+ public String[] getPropertyNames();
+ public Object[] getPropertyValues(Object object);
+ public void setPropertyValues(Object object, Object[] values);
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/cglib/AccessOptimizerAdapter.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/cglib/AccessOptimizerAdapter.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/cglib/AccessOptimizerAdapter.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,102 @@
+package org.hibernate.bytecode.cglib;
+
+import org.hibernate.bytecode.ReflectionOptimizer;
+import org.hibernate.PropertyAccessException;
+import net.sf.cglib.beans.BulkBean;
+import net.sf.cglib.beans.BulkBeanException;
+import net.sf.cglib.reflect.FastClass;
+
+import java.io.Serializable;
+import java.io.ObjectOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+
+/**
+ * The {@link ReflectionOptimizer.AccessOptimizer} implementation for CGLIB
+ * which simply acts as an adpater to the {@link BulkBean} class.
+ *
+ * @author Steve Ebersole
+ */
+public class AccessOptimizerAdapter implements ReflectionOptimizer.AccessOptimizer, Serializable {
+
+ public static final String PROPERTY_GET_EXCEPTION =
+ "exception getting property value with CGLIB (set hibernate.bytecode.use_reflection_optimizer=false for more info)";
+
+ public static final String PROPERTY_SET_EXCEPTION =
+ "exception setting property value with CGLIB (set hibernate.bytecode.use_reflection_optimizer=false for more info)";
+
+ private Class mappedClass;
+ private BulkBean bulkBean;
+
+ public AccessOptimizerAdapter(BulkBean bulkBean, Class mappedClass) {
+ this.bulkBean = bulkBean;
+ this.mappedClass = mappedClass;
+ }
+
+ public String[] getPropertyNames() {
+ return bulkBean.getGetters();
+ }
+
+ public Object[] getPropertyValues(Object object) {
+ try {
+ return bulkBean.getPropertyValues( object );
+ }
+ catch ( Throwable t ) {
+ throw new PropertyAccessException(
+ t,
+ PROPERTY_GET_EXCEPTION,
+ false,
+ mappedClass,
+ getterName( t, bulkBean )
+ );
+ }
+ }
+
+ public void setPropertyValues(Object object, Object[] values) {
+ try {
+ bulkBean.setPropertyValues( object, values );
+ }
+ catch ( Throwable t ) {
+ throw new PropertyAccessException(
+ t,
+ PROPERTY_SET_EXCEPTION,
+ true,
+ mappedClass,
+ setterName( t, bulkBean )
+ );
+ }
+ }
+
+ private static String setterName(Throwable t, BulkBean optimizer) {
+ if ( t instanceof BulkBeanException ) {
+ return optimizer.getSetters()[( ( BulkBeanException ) t ).getIndex()];
+ }
+ else {
+ return "?";
+ }
+ }
+
+ private static String getterName(Throwable t, BulkBean optimizer) {
+ if ( t instanceof BulkBeanException ) {
+ return optimizer.getGetters()[( ( BulkBeanException ) t ).getIndex()];
+ }
+ else {
+ return "?";
+ }
+ }
+
+ private void writeObject(ObjectOutputStream out) throws IOException {
+ out.writeObject( mappedClass );
+ out.writeObject( bulkBean.getGetters() );
+ out.writeObject( bulkBean.getSetters() );
+ out.writeObject( bulkBean.getPropertyTypes() );
+ }
+
+ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+ Class beanClass = ( Class ) in.readObject();
+ String[] getters = ( String[] ) in.readObject();
+ String[] setters = ( String[] ) in.readObject();
+ Class[] types = ( Class[] ) in.readObject();
+ bulkBean = BulkBean.create( beanClass, getters, setters, types );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/cglib/BytecodeProviderImpl.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/cglib/BytecodeProviderImpl.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/cglib/BytecodeProviderImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,92 @@
+package org.hibernate.bytecode.cglib;
+
+import java.lang.reflect.Modifier;
+
+import net.sf.cglib.beans.BulkBean;
+import net.sf.cglib.beans.BulkBeanException;
+import net.sf.cglib.reflect.FastClass;
+import net.sf.cglib.transform.ClassFilter;
+import net.sf.cglib.transform.ClassTransformer;
+import net.sf.cglib.transform.ClassTransformerFactory;
+import net.sf.cglib.transform.TransformingClassLoader;
+import net.sf.cglib.transform.impl.InterceptFieldFilter;
+import net.sf.cglib.transform.impl.InterceptFieldTransformer;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.bytecode.BytecodeProvider;
+import org.hibernate.bytecode.ProxyFactoryFactory;
+import org.hibernate.bytecode.ReflectionOptimizer;
+import org.hibernate.bytecode.util.FieldFilter;
+import org.hibernate.util.StringHelper;
+import org.objectweb.asm.Type;
+
+/**
+ * Bytecode provider implementation for CGLIB.
+ *
+ * @author Steve Ebersole
+ */
+public class BytecodeProviderImpl implements BytecodeProvider {
+
+ private static final Log log = LogFactory.getLog( BytecodeProviderImpl.class );
+
+ public ProxyFactoryFactory getProxyFactoryFactory() {
+ return new ProxyFactoryFactoryImpl();
+ }
+
+ public ReflectionOptimizer getReflectionOptimizer(
+ Class clazz,
+ String[] getterNames,
+ String[] setterNames,
+ Class[] types) {
+ FastClass fastClass;
+ BulkBean bulkBean;
+ try {
+ fastClass = FastClass.create( clazz );
+ bulkBean = BulkBean.create( clazz, getterNames, setterNames, types );
+ if ( !clazz.isInterface() && !Modifier.isAbstract( clazz.getModifiers() ) ) {
+ if ( fastClass == null ) {
+ bulkBean = null;
+ }
+ else {
+ //test out the optimizer:
+ Object instance = fastClass.newInstance();
+ bulkBean.setPropertyValues( instance, bulkBean.getPropertyValues( instance ) );
+ }
+ }
+ }
+ catch( Throwable t ) {
+ fastClass = null;
+ bulkBean = null;
+ String message = "reflection optimizer disabled for: " +
+ clazz.getName() +
+ " [" +
+ StringHelper.unqualify( t.getClass().getName() ) +
+ ": " +
+ t.getMessage();
+
+ if (t instanceof BulkBeanException ) {
+ int index = ( (BulkBeanException) t ).getIndex();
+ if (index >= 0) {
+ message += " (property " + setterNames[index] + ")";
+ }
+ }
+
+ log.debug( message );
+ }
+
+ if ( fastClass != null && bulkBean != null ) {
+ return new ReflectionOptimizerImpl(
+ new InstantiationOptimizerAdapter( fastClass ),
+ new AccessOptimizerAdapter( bulkBean, clazz )
+ );
+ }
+ else {
+ return null;
+ }
+ }
+
+ public org.hibernate.bytecode.ClassTransformer getTransformer(org.hibernate.bytecode.util.ClassFilter classFilter, FieldFilter fieldFilter) {
+ return new CglibClassTransformer( classFilter, fieldFilter );
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/cglib/CglibClassTransformer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/cglib/CglibClassTransformer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/cglib/CglibClassTransformer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,120 @@
+//$Id: $
+package org.hibernate.bytecode.cglib;
+
+import java.security.ProtectionDomain;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.ByteArrayOutputStream;
+
+import net.sf.cglib.transform.ClassTransformer;
+import net.sf.cglib.transform.TransformingClassGenerator;
+import net.sf.cglib.transform.ClassReaderGenerator;
+import net.sf.cglib.transform.impl.InterceptFieldEnabled;
+import net.sf.cglib.transform.impl.InterceptFieldFilter;
+import net.sf.cglib.transform.impl.InterceptFieldTransformer;
+import net.sf.cglib.core.ClassNameReader;
+import net.sf.cglib.core.DebuggingClassWriter;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.bytecode.AbstractClassTransformerImpl;
+import org.hibernate.bytecode.util.FieldFilter;
+import org.hibernate.bytecode.util.ClassFilter;
+import org.hibernate.HibernateException;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.attrs.Attributes;
+
+/**
+ * Enhance the classes allowing them to implements InterceptFieldEnabled
+ * This interface is then used by Hibernate for some optimizations.
+ *
+ * @author Emmanuel Bernard
+ */
+public class CglibClassTransformer extends AbstractClassTransformerImpl {
+
+ private static Log log = LogFactory.getLog( CglibClassTransformer.class.getName() );
+
+ public CglibClassTransformer(ClassFilter classFilter, FieldFilter fieldFilter) {
+ super( classFilter, fieldFilter );
+ }
+
+ protected byte[] doTransform(
+ ClassLoader loader,
+ String className,
+ Class classBeingRedefined,
+ ProtectionDomain protectionDomain,
+ byte[] classfileBuffer) {
+ ClassReader reader;
+ try {
+ reader = new ClassReader( new ByteArrayInputStream( classfileBuffer ) );
+ }
+ catch (IOException e) {
+ log.error( "Unable to read class", e );
+ throw new HibernateException( "Unable to read class: " + e.getMessage() );
+ }
+
+ String[] names = ClassNameReader.getClassInfo( reader );
+ ClassWriter w = new DebuggingClassWriter( true );
+ ClassTransformer t = getClassTransformer( names );
+ if ( t != null ) {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "Enhancing " + className );
+ }
+ ByteArrayOutputStream out;
+ byte[] result;
+ try {
+ reader = new ClassReader( new ByteArrayInputStream( classfileBuffer ) );
+ new TransformingClassGenerator(
+ new ClassReaderGenerator( reader, attributes(), skipDebug() ), t
+ ).generateClass( w );
+ out = new ByteArrayOutputStream();
+ out.write( w.toByteArray() );
+ result = out.toByteArray();
+ out.close();
+ }
+ catch (Exception e) {
+ log.error( "Unable to transform class", e );
+ throw new HibernateException( "Unable to transform class: " + e.getMessage() );
+ }
+ return result;
+ }
+ return classfileBuffer;
+ }
+
+
+ private Attribute[] attributes() {
+ return Attributes.getDefaultAttributes();
+ }
+
+ private boolean skipDebug() {
+ return false;
+ }
+
+ private ClassTransformer getClassTransformer(final String[] classInfo) {
+ if ( isAlreadyInstrumented( classInfo ) ) {
+ return null;
+ }
+ return new InterceptFieldTransformer(
+ new InterceptFieldFilter() {
+ public boolean acceptRead(Type owner, String name) {
+ return fieldFilter.shouldTransformFieldAccess( classInfo[0], owner.getClassName(), name );
+ }
+
+ public boolean acceptWrite(Type owner, String name) {
+ return fieldFilter.shouldTransformFieldAccess( classInfo[0], owner.getClassName(), name );
+ }
+ }
+ );
+ }
+
+ private boolean isAlreadyInstrumented(String[] classInfo) {
+ for ( int i = 1; i < classInfo.length; i++ ) {
+ if ( InterceptFieldEnabled.class.getName().equals( classInfo[i] ) ) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/cglib/InstantiationOptimizerAdapter.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/cglib/InstantiationOptimizerAdapter.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/cglib/InstantiationOptimizerAdapter.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,46 @@
+package org.hibernate.bytecode.cglib;
+
+import org.hibernate.bytecode.ReflectionOptimizer;
+import net.sf.cglib.reflect.FastClass;
+import org.hibernate.InstantiationException;
+
+import java.io.Serializable;
+import java.io.ObjectOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+
+/**
+ * The {@link ReflectionOptimizer.InstantiationOptimizer} implementation for CGLIB
+ * which simply acts as an adpater to the {@link FastClass} class.
+ *
+ * @author Steve Ebersole
+ */
+public class InstantiationOptimizerAdapter implements ReflectionOptimizer.InstantiationOptimizer, Serializable {
+ private FastClass fastClass;
+
+ public InstantiationOptimizerAdapter(FastClass fastClass) {
+ this.fastClass = fastClass;
+ }
+
+ public Object newInstance() {
+ try {
+ return fastClass.newInstance();
+ }
+ catch ( Throwable t ) {
+ throw new InstantiationException(
+ "Could not instantiate entity with CGLIB optimizer: ",
+ fastClass.getJavaClass(),
+ t
+ );
+ }
+ }
+
+ private void writeObject(ObjectOutputStream out) throws IOException {
+ out.writeObject( fastClass.getJavaClass() );
+ }
+
+ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+ Class beanClass = ( Class ) in.readObject();
+ fastClass = FastClass.create( beanClass );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/cglib/ProxyFactoryFactoryImpl.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/cglib/ProxyFactoryFactoryImpl.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/cglib/ProxyFactoryFactoryImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,141 @@
+package org.hibernate.bytecode.cglib;
+
+import org.hibernate.bytecode.ProxyFactoryFactory;
+import org.hibernate.bytecode.BasicProxyFactory;
+import org.hibernate.proxy.ProxyFactory;
+import org.hibernate.proxy.pojo.cglib.CGLIBProxyFactory;
+import org.hibernate.AssertionFailure;
+import org.hibernate.HibernateException;
+import net.sf.cglib.proxy.Enhancer;
+import net.sf.cglib.proxy.CallbackFilter;
+import net.sf.cglib.proxy.MethodInterceptor;
+import net.sf.cglib.proxy.MethodProxy;
+import net.sf.cglib.proxy.NoOp;
+import net.sf.cglib.proxy.Callback;
+import net.sf.cglib.proxy.Factory;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+
+/**
+ * A factory for CGLIB-based {@link ProxyFactory} instances.
+ *
+ * @author Steve Ebersole
+ */
+public class ProxyFactoryFactoryImpl implements ProxyFactoryFactory {
+
+ /**
+ * Builds a CGLIB-based proxy factory.
+ *
+ * @return a new CGLIB-based proxy factory.
+ */
+ public ProxyFactory buildProxyFactory() {
+ return new CGLIBProxyFactory();
+ }
+
+ public BasicProxyFactory buildBasicProxyFactory(Class superClass, Class[] interfaces) {
+ return new BasicProxyFactoryImpl( superClass, interfaces );
+ }
+
+ public static class BasicProxyFactoryImpl implements BasicProxyFactory {
+ private final Class proxyClass;
+ private final Factory factory;
+
+ public BasicProxyFactoryImpl(Class superClass, Class[] interfaces) {
+ if ( superClass == null && ( interfaces == null || interfaces.length < 1 ) ) {
+ throw new AssertionFailure( "attempting to build proxy without any superclass or interfaces" );
+ }
+
+ Enhancer en = new Enhancer();
+ en.setUseCache( false );
+ en.setInterceptDuringConstruction( false );
+ en.setUseFactory( true );
+ en.setCallbackTypes( CALLBACK_TYPES );
+ en.setCallbackFilter( FINALIZE_FILTER );
+ if ( superClass != null ) {
+ en.setSuperclass( superClass );
+ }
+ if ( interfaces != null && interfaces.length > 0 ) {
+ en.setInterfaces( interfaces );
+ }
+ proxyClass = en.createClass();
+ try {
+ factory = ( Factory ) proxyClass.newInstance();
+ }
+ catch ( Throwable t ) {
+ throw new HibernateException( "Unable to build CGLIB Factory instance" );
+ }
+ }
+
+ public Object getProxy() {
+ try {
+ return factory.newInstance(
+ new Callback[] { new PassThroughInterceptor( proxyClass.getName() ), NoOp.INSTANCE }
+ );
+ }
+ catch ( Throwable t ) {
+ throw new HibernateException( "Unable to instantiate proxy instance" );
+ }
+ }
+ }
+
+ private static final CallbackFilter FINALIZE_FILTER = new CallbackFilter() {
+ public int accept(Method method) {
+ if ( method.getParameterTypes().length == 0 && method.getName().equals("finalize") ){
+ return 1;
+ }
+ else {
+ return 0;
+ }
+ }
+ };
+
+ private static final Class[] CALLBACK_TYPES = new Class[] { MethodInterceptor.class, NoOp.class };
+
+ private static class PassThroughInterceptor implements MethodInterceptor {
+ private HashMap data = new HashMap();
+ private final String proxiedClassName;
+
+ public PassThroughInterceptor(String proxiedClassName) {
+ this.proxiedClassName = proxiedClassName;
+ }
+
+ public Object intercept(
+ Object obj,
+ Method method,
+ Object[] args,
+ MethodProxy proxy) throws Throwable {
+ String name = method.getName();
+ if ( "toString".equals( name ) ) {
+ return proxiedClassName + "@" + System.identityHashCode( obj );
+ }
+ else if ( "equals".equals( name ) ) {
+ return args[0] instanceof Factory && ( ( Factory ) args[0] ).getCallback( 0 ) == this
+ ? Boolean.TRUE
+ : Boolean.FALSE;
+ }
+ else if ( "hashCode".equals( name ) ) {
+ return new Integer( System.identityHashCode( obj ) );
+ }
+ boolean hasGetterSignature = method.getParameterTypes().length == 0 && method.getReturnType() != null;
+ boolean hasSetterSignature = method.getParameterTypes().length == 1 && ( method.getReturnType() == null || method.getReturnType() == void.class );
+ if ( name.startsWith( "get" ) && hasGetterSignature ) {
+ String propName = name.substring( 3 );
+ return data.get( propName );
+ }
+ else if ( name.startsWith( "is" ) && hasGetterSignature ) {
+ String propName = name.substring( 2 );
+ return data.get( propName );
+ }
+ else if ( name.startsWith( "set" ) && hasSetterSignature) {
+ String propName = name.substring( 3 );
+ data.put( propName, args[0] );
+ return null;
+ }
+ else {
+ // todo : what else to do here?
+ return null;
+ }
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/cglib/ReflectionOptimizerImpl.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/cglib/ReflectionOptimizerImpl.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/cglib/ReflectionOptimizerImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,34 @@
+package org.hibernate.bytecode.cglib;
+
+import org.hibernate.bytecode.ReflectionOptimizer;
+
+import java.io.Serializable;
+import java.io.ObjectOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+
+/**
+ * ReflectionOptimizer implementation for CGLIB.
+ *
+ * @author Steve Ebersole
+ */
+public class ReflectionOptimizerImpl implements ReflectionOptimizer, Serializable {
+ private transient InstantiationOptimizerAdapter instantiationOptimizer;
+ private transient AccessOptimizerAdapter accessOptimizer;
+
+ public ReflectionOptimizerImpl(
+ InstantiationOptimizerAdapter instantiationOptimizer,
+ AccessOptimizerAdapter accessOptimizer) {
+ this.instantiationOptimizer = instantiationOptimizer;
+ this.accessOptimizer = accessOptimizer;
+ }
+
+ public InstantiationOptimizer getInstantiationOptimizer() {
+ return instantiationOptimizer;
+ }
+
+ public AccessOptimizer getAccessOptimizer() {
+ return accessOptimizer;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/AccessOptimizerAdapter.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/AccessOptimizerAdapter.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/AccessOptimizerAdapter.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,81 @@
+package org.hibernate.bytecode.javassist;
+
+import org.hibernate.bytecode.ReflectionOptimizer;
+import org.hibernate.PropertyAccessException;
+
+import java.io.Serializable;
+
+/**
+ * The {@link ReflectionOptimizer.AccessOptimizer} implementation for Javassist
+ * which simply acts as an adpater to the {@link BulkAccessor} class.
+ *
+ * @author Steve Ebersole
+ */
+public class AccessOptimizerAdapter implements ReflectionOptimizer.AccessOptimizer, Serializable {
+
+ public static final String PROPERTY_GET_EXCEPTION =
+ "exception getting property value with Javassist (set hibernate.bytecode.use_reflection_optimizer=false for more info)";
+
+ public static final String PROPERTY_SET_EXCEPTION =
+ "exception setting property value with Javassist (set hibernate.bytecode.use_reflection_optimizer=false for more info)";
+
+ private final BulkAccessor bulkAccessor;
+ private final Class mappedClass;
+
+ public AccessOptimizerAdapter(BulkAccessor bulkAccessor, Class mappedClass) {
+ this.bulkAccessor = bulkAccessor;
+ this.mappedClass = mappedClass;
+ }
+
+ public String[] getPropertyNames() {
+ return bulkAccessor.getGetters();
+ }
+
+ public Object[] getPropertyValues(Object object) {
+ try {
+ return bulkAccessor.getPropertyValues( object );
+ }
+ catch ( Throwable t ) {
+ throw new PropertyAccessException(
+ t,
+ PROPERTY_GET_EXCEPTION,
+ false,
+ mappedClass,
+ getterName( t, bulkAccessor )
+ );
+ }
+ }
+
+ public void setPropertyValues(Object object, Object[] values) {
+ try {
+ bulkAccessor.setPropertyValues( object, values );
+ }
+ catch ( Throwable t ) {
+ throw new PropertyAccessException(
+ t,
+ PROPERTY_SET_EXCEPTION,
+ true,
+ mappedClass,
+ setterName( t, bulkAccessor )
+ );
+ }
+ }
+
+ private static String setterName(Throwable t, BulkAccessor accessor) {
+ if (t instanceof BulkAccessorException ) {
+ return accessor.getSetters()[ ( (BulkAccessorException) t ).getIndex() ];
+ }
+ else {
+ return "?";
+ }
+ }
+
+ private static String getterName(Throwable t, BulkAccessor accessor) {
+ if (t instanceof BulkAccessorException ) {
+ return accessor.getGetters()[ ( (BulkAccessorException) t ).getIndex() ];
+ }
+ else {
+ return "?";
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/BulkAccessor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/BulkAccessor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/BulkAccessor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,92 @@
+package org.hibernate.bytecode.javassist;
+
+import java.io.Serializable;
+
+
+/**
+ * A JavaBean accessor.
+ * <p/>
+ * <p>This object provides methods that set/get multiple properties
+ * of a JavaBean at once. This class and its support classes have been
+ * developed for the comaptibility with cglib
+ * (<tt>http://cglib.sourceforge.net/</tt>).
+ *
+ * @author Muga Nishizawa
+ * @author modified by Shigeru Chiba
+ */
+public abstract class BulkAccessor implements Serializable {
+ protected Class target;
+ protected String[] getters, setters;
+ protected Class[] types;
+
+ protected BulkAccessor() {
+ }
+
+ /**
+ * Obtains the values of properties of a given bean.
+ *
+ * @param bean JavaBean.
+ * @param values the obtained values are stored in this array.
+ */
+ public abstract void getPropertyValues(Object bean, Object[] values);
+
+ /**
+ * Sets properties of a given bean to specified values.
+ *
+ * @param bean JavaBean.
+ * @param values the values assinged to properties.
+ */
+ public abstract void setPropertyValues(Object bean, Object[] values);
+
+ /**
+ * Returns the values of properties of a given bean.
+ *
+ * @param bean JavaBean.
+ */
+ public Object[] getPropertyValues(Object bean) {
+ Object[] values = new Object[getters.length];
+ getPropertyValues( bean, values );
+ return values;
+ }
+
+ /**
+ * Returns the types of properties.
+ */
+ public Class[] getPropertyTypes() {
+ return ( Class[] ) types.clone();
+ }
+
+ /**
+ * Returns the setter names of properties.
+ */
+ public String[] getGetters() {
+ return ( String[] ) getters.clone();
+ }
+
+ /**
+ * Returns the getter names of the properties.
+ */
+ public String[] getSetters() {
+ return ( String[] ) setters.clone();
+ }
+
+ /**
+ * Creates a new instance of <code>BulkAccessor</code>.
+ * The created instance provides methods for setting/getting
+ * specified properties at once.
+ *
+ * @param beanClass the class of the JavaBeans accessed
+ * through the created object.
+ * @param getters the names of setter methods for specified properties.
+ * @param setters the names of getter methods for specified properties.
+ * @param types the types of specified properties.
+ */
+ public static BulkAccessor create(
+ Class beanClass,
+ String[] getters,
+ String[] setters,
+ Class[] types) {
+ BulkAccessorFactory factory = new BulkAccessorFactory( beanClass, getters, setters, types );
+ return factory.create();
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/BulkAccessorException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/BulkAccessorException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/BulkAccessorException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,78 @@
+package org.hibernate.bytecode.javassist;
+
+/**
+ * An exception thrown while generating a bulk accessor.
+ *
+ * @author Muga Nishizawa
+ * @author modified by Shigeru Chiba
+ */
+public class BulkAccessorException extends RuntimeException {
+ private Throwable myCause;
+
+ /**
+ * Gets the cause of this throwable.
+ * It is for JDK 1.3 compatibility.
+ */
+ public Throwable getCause() {
+ return (myCause == this ? null : myCause);
+ }
+
+ /**
+ * Initializes the cause of this throwable.
+ * It is for JDK 1.3 compatibility.
+ */
+ public synchronized Throwable initCause(Throwable cause) {
+ myCause = cause;
+ return this;
+ }
+
+ private int index;
+
+ /**
+ * Constructs an exception.
+ */
+ public BulkAccessorException(String message) {
+ super(message);
+ index = -1;
+ initCause(null);
+ }
+
+ /**
+ * Constructs an exception.
+ *
+ * @param index the index of the property that causes an exception.
+ */
+ public BulkAccessorException(String message, int index) {
+ this(message + ": " + index);
+ this.index = index;
+ }
+
+ /**
+ * Constructs an exception.
+ */
+ public BulkAccessorException(String message, Throwable cause) {
+ super(message);
+ index = -1;
+ initCause(cause);
+ }
+
+ /**
+ * Constructs an exception.
+ *
+ * @param index the index of the property that causes an exception.
+ */
+ public BulkAccessorException(Throwable cause, int index) {
+ this("Property " + index);
+ this.index = index;
+ initCause(cause);
+ }
+
+ /**
+ * Returns the index of the property that causes this exception.
+ *
+ * @return -1 if the index is not specified.
+ */
+ public int getIndex() {
+ return this.index;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/BulkAccessorFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/BulkAccessorFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/BulkAccessorFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,388 @@
+package org.hibernate.bytecode.javassist;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.security.ProtectionDomain;
+
+import javassist.CannotCompileException;
+import javassist.bytecode.AccessFlag;
+import javassist.bytecode.Bytecode;
+import javassist.bytecode.ClassFile;
+import javassist.bytecode.ConstPool;
+import javassist.bytecode.MethodInfo;
+import javassist.bytecode.Opcode;
+import javassist.util.proxy.FactoryHelper;
+import javassist.util.proxy.RuntimeSupport;
+
+/**
+ * A factory of bulk accessors.
+ *
+ * @author Muga Nishizawa
+ * @author modified by Shigeru Chiba
+ */
+class BulkAccessorFactory {
+ private static final String PACKAGE_NAME_PREFIX = "org.javassist.tmp.";
+ private static final String BULKACESSOR_CLASS_NAME = BulkAccessor.class.getName();
+ private static final String OBJECT_CLASS_NAME = Object.class.getName();
+ private static final String GENERATED_GETTER_NAME = "getPropertyValues";
+ private static final String GENERATED_SETTER_NAME = "setPropertyValues";
+ private static final String GET_SETTER_DESC = "(Ljava/lang/Object;[Ljava/lang/Object;)V";
+ private static final String THROWABLE_CLASS_NAME = Throwable.class.getName();
+ private static final String BULKEXCEPTION_CLASS_NAME = BulkAccessorException.class.getName();
+ private static int counter = 0;
+
+ private Class targetBean;
+ private String[] getterNames;
+ private String[] setterNames;
+ private Class[] types;
+ public String writeDirectory;
+
+ BulkAccessorFactory(
+ Class target,
+ String[] getterNames,
+ String[] setterNames,
+ Class[] types) {
+ this.targetBean = target;
+ this.getterNames = getterNames;
+ this.setterNames = setterNames;
+ this.types = types;
+ this.writeDirectory = null;
+ }
+
+ BulkAccessor create() {
+ Method[] getters = new Method[getterNames.length];
+ Method[] setters = new Method[setterNames.length];
+ findAccessors( targetBean, getterNames, setterNames, types, getters, setters );
+
+ Class beanClass;
+ try {
+ ClassFile classfile = make( getters, setters );
+ ClassLoader loader = this.getClassLoader();
+ if ( writeDirectory != null ) {
+ FactoryHelper.writeFile( classfile, writeDirectory );
+ }
+
+ beanClass = FactoryHelper.toClass( classfile, loader, getDomain() );
+ return ( BulkAccessor ) this.newInstance( beanClass );
+ }
+ catch ( Exception e ) {
+ throw new BulkAccessorException( e.getMessage(), e );
+ }
+ }
+
+ private ProtectionDomain getDomain() {
+ Class cl;
+ if ( this.targetBean != null ) {
+ cl = this.targetBean;
+ }
+ else {
+ cl = this.getClass();
+ }
+ return cl.getProtectionDomain();
+ }
+
+ private ClassFile make(Method[] getters, Method[] setters) throws CannotCompileException {
+ String className = targetBean.getName();
+ // set the name of bulk accessor.
+ className = className + "_$$_bulkaccess_" + counter++;
+ if ( className.startsWith( "java." ) ) {
+ className = "org.javassist.tmp." + className;
+ }
+
+ ClassFile classfile = new ClassFile( false, className, BULKACESSOR_CLASS_NAME );
+ classfile.setAccessFlags( AccessFlag.PUBLIC );
+ addDefaultConstructor( classfile );
+ addGetter( classfile, getters );
+ addSetter( classfile, setters );
+ return classfile;
+ }
+
+ private ClassLoader getClassLoader() {
+ if ( targetBean != null && targetBean.getName().equals( OBJECT_CLASS_NAME ) ) {
+ return targetBean.getClassLoader();
+ }
+ else {
+ return getClass().getClassLoader();
+ }
+ }
+
+ private Object newInstance(Class type) throws Exception {
+ BulkAccessor instance = ( BulkAccessor ) type.newInstance();
+ instance.target = targetBean;
+ int len = getterNames.length;
+ instance.getters = new String[len];
+ instance.setters = new String[len];
+ instance.types = new Class[len];
+ for ( int i = 0; i < len; i++ ) {
+ instance.getters[i] = getterNames[i];
+ instance.setters[i] = setterNames[i];
+ instance.types[i] = types[i];
+ }
+
+ return instance;
+ }
+
+ /**
+ * Declares a constructor that takes no parameter.
+ *
+ * @param classfile
+ * @throws CannotCompileException
+ */
+ private void addDefaultConstructor(ClassFile classfile) throws CannotCompileException {
+ ConstPool cp = classfile.getConstPool();
+ String cons_desc = "()V";
+ MethodInfo mi = new MethodInfo( cp, MethodInfo.nameInit, cons_desc );
+
+ Bytecode code = new Bytecode( cp, 0, 1 );
+ // aload_0
+ code.addAload( 0 );
+ // invokespecial
+ code.addInvokespecial( BulkAccessor.class.getName(), MethodInfo.nameInit, cons_desc );
+ // return
+ code.addOpcode( Opcode.RETURN );
+
+ mi.setCodeAttribute( code.toCodeAttribute() );
+ mi.setAccessFlags( AccessFlag.PUBLIC );
+ classfile.addMethod( mi );
+ }
+
+ private void addGetter(ClassFile classfile, final Method[] getters) throws CannotCompileException {
+ ConstPool cp = classfile.getConstPool();
+ int target_type_index = cp.addClassInfo( this.targetBean.getName() );
+ String desc = GET_SETTER_DESC;
+ MethodInfo mi = new MethodInfo( cp, GENERATED_GETTER_NAME, desc );
+
+ Bytecode code = new Bytecode( cp, 6, 4 );
+ /* | this | bean | args | raw bean | */
+ if ( getters.length >= 0 ) {
+ // aload_1 // load bean
+ code.addAload( 1 );
+ // checkcast // cast bean
+ code.addCheckcast( this.targetBean.getName() );
+ // astore_3 // store bean
+ code.addAstore( 3 );
+ for ( int i = 0; i < getters.length; ++i ) {
+ if ( getters[i] != null ) {
+ Method getter = getters[i];
+ // aload_2 // args
+ code.addAload( 2 );
+ // iconst_i // continue to aastore
+ code.addIconst( i ); // growing stack is 1
+ Class returnType = getter.getReturnType();
+ int typeIndex = -1;
+ if ( returnType.isPrimitive() ) {
+ typeIndex = FactoryHelper.typeIndex( returnType );
+ // new
+ code.addNew( FactoryHelper.wrapperTypes[typeIndex] );
+ // dup
+ code.addOpcode( Opcode.DUP );
+ }
+
+ // aload_3 // load the raw bean
+ code.addAload( 3 );
+ String getter_desc = RuntimeSupport.makeDescriptor( getter );
+ String getterName = getter.getName();
+ if ( this.targetBean.isInterface() ) {
+ // invokeinterface
+ code.addInvokeinterface( target_type_index, getterName, getter_desc, 1 );
+ }
+ else {
+ // invokevirtual
+ code.addInvokevirtual( target_type_index, getterName, getter_desc );
+ }
+
+ if ( typeIndex >= 0 ) { // is a primitive type
+ // invokespecial
+ code.addInvokespecial(
+ FactoryHelper.wrapperTypes[typeIndex],
+ MethodInfo.nameInit,
+ FactoryHelper.wrapperDesc[typeIndex]
+ );
+ }
+
+ // aastore // args
+ code.add( Opcode.AASTORE );
+ code.growStack( -3 );
+ }
+ }
+ }
+ // return
+ code.addOpcode( Opcode.RETURN );
+
+ mi.setCodeAttribute( code.toCodeAttribute() );
+ mi.setAccessFlags( AccessFlag.PUBLIC );
+ classfile.addMethod( mi );
+ }
+
+ private void addSetter(ClassFile classfile, final Method[] setters) throws CannotCompileException {
+ ConstPool cp = classfile.getConstPool();
+ int target_type_index = cp.addClassInfo( this.targetBean.getName() );
+ String desc = GET_SETTER_DESC;
+ MethodInfo mi = new MethodInfo( cp, GENERATED_SETTER_NAME, desc );
+
+ Bytecode code = new Bytecode( cp, 4, 6 );
+ /* | this | bean | args | i | raw bean | exception | */
+ if ( setters.length > 0 ) {
+ int start, end; // required to exception table
+ // iconst_0 // i
+ code.addIconst( 0 );
+ // istore_3 // store i
+ code.addIstore( 3 );
+ // aload_1 // load the bean
+ code.addAload( 1 );
+ // checkcast // cast the bean into a raw bean
+ code.addCheckcast( this.targetBean.getName() );
+ // astore 4 // store the raw bean
+ code.addAstore( 4 );
+ /* current stack len = 0 */
+ // start region to handling exception (BulkAccessorException)
+ start = code.currentPc();
+ int lastIndex = 0;
+ for ( int i = 0; i < setters.length; ++i ) {
+ if ( setters[i] != null ) {
+ int diff = i - lastIndex;
+ if ( diff > 0 ) {
+ // iinc 3, 1
+ code.addOpcode( Opcode.IINC );
+ code.add( 3 );
+ code.add( diff );
+ lastIndex = i;
+ }
+ }
+ /* current stack len = 0 */
+ // aload 4 // load the raw bean
+ code.addAload( 4 );
+ // aload_2 // load the args
+ code.addAload( 2 );
+ // iconst_i
+ code.addIconst( i );
+ // aaload
+ code.addOpcode( Opcode.AALOAD );
+ // checkcast
+ Class[] setterParamTypes = setters[i].getParameterTypes();
+ Class setterParamType = setterParamTypes[0];
+ if ( setterParamType.isPrimitive() ) {
+ // checkcast (case of primitive type)
+ // invokevirtual (case of primitive type)
+ this.addUnwrapper( classfile, code, setterParamType );
+ }
+ else {
+ // checkcast (case of reference type)
+ code.addCheckcast( setterParamType.getName() );
+ }
+ /* current stack len = 2 */
+ String rawSetterMethod_desc = RuntimeSupport.makeDescriptor( setters[i] );
+ if ( !this.targetBean.isInterface() ) {
+ // invokevirtual
+ code.addInvokevirtual( target_type_index, setters[i].getName(), rawSetterMethod_desc );
+ }
+ else {
+ // invokeinterface
+ Class[] params = setters[i].getParameterTypes();
+ int size;
+ if ( params[0].equals( Double.TYPE ) || params[0].equals( Long.TYPE ) ) {
+ size = 3;
+ }
+ else {
+ size = 2;
+ }
+
+ code.addInvokeinterface( target_type_index, setters[i].getName(), rawSetterMethod_desc, size );
+ }
+ }
+
+ // end region to handling exception (BulkAccessorException)
+ end = code.currentPc();
+ // return
+ code.addOpcode( Opcode.RETURN );
+ /* current stack len = 0 */
+ // register in exception table
+ int throwableType_index = cp.addClassInfo( THROWABLE_CLASS_NAME );
+ code.addExceptionHandler( start, end, code.currentPc(), throwableType_index );
+ // astore 5 // store exception
+ code.addAstore( 5 );
+ // new // BulkAccessorException
+ code.addNew( BULKEXCEPTION_CLASS_NAME );
+ // dup
+ code.addOpcode( Opcode.DUP );
+ // aload 5 // load exception
+ code.addAload( 5 );
+ // iload_3 // i
+ code.addIload( 3 );
+ // invokespecial // BulkAccessorException.<init>
+ String cons_desc = "(Ljava/lang/Throwable;I)V";
+ code.addInvokespecial( BULKEXCEPTION_CLASS_NAME, MethodInfo.nameInit, cons_desc );
+ // athrow
+ code.addOpcode( Opcode.ATHROW );
+ }
+ else {
+ // return
+ code.addOpcode( Opcode.RETURN );
+ }
+
+ mi.setCodeAttribute( code.toCodeAttribute() );
+ mi.setAccessFlags( AccessFlag.PUBLIC );
+ classfile.addMethod( mi );
+ }
+
+ private void addUnwrapper(
+ ClassFile classfile,
+ Bytecode code,
+ Class type) {
+ int index = FactoryHelper.typeIndex( type );
+ String wrapperType = FactoryHelper.wrapperTypes[index];
+ // checkcast
+ code.addCheckcast( wrapperType );
+ // invokevirtual
+ code.addInvokevirtual( wrapperType, FactoryHelper.unwarpMethods[index], FactoryHelper.unwrapDesc[index] );
+ }
+
+ private static void findAccessors(
+ Class clazz,
+ String[] getterNames,
+ String[] setterNames,
+ Class[] types,
+ Method[] getters,
+ Method[] setters) {
+ int length = types.length;
+ if ( setterNames.length != length || getterNames.length != length ) {
+ throw new BulkAccessorException( "bad number of accessors" );
+ }
+
+ Class[] getParam = new Class[0];
+ Class[] setParam = new Class[1];
+ for ( int i = 0; i < length; i++ ) {
+ if ( getterNames[i] != null ) {
+ Method getter = findAccessor( clazz, getterNames[i], getParam, i );
+ if ( getter.getReturnType() != types[i] ) {
+ throw new BulkAccessorException( "wrong return type: " + getterNames[i], i );
+ }
+
+ getters[i] = getter;
+ }
+
+ if ( setterNames[i] != null ) {
+ setParam[0] = types[i];
+ setters[i] = findAccessor( clazz, setterNames[i], setParam, i );
+ }
+ }
+ }
+
+ private static Method findAccessor(
+ Class clazz,
+ String name,
+ Class[] params,
+ int index) throws BulkAccessorException {
+ try {
+ Method method = clazz.getDeclaredMethod( name, params );
+ if ( Modifier.isPrivate( method.getModifiers() ) ) {
+ throw new BulkAccessorException( "private property", index );
+ }
+
+ return method;
+ }
+ catch ( NoSuchMethodException e ) {
+ throw new BulkAccessorException( "cannot find an accessor", index );
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/BytecodeProviderImpl.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/BytecodeProviderImpl.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/BytecodeProviderImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,84 @@
+package org.hibernate.bytecode.javassist;
+
+import java.lang.reflect.Modifier;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.bytecode.BytecodeProvider;
+import org.hibernate.bytecode.ClassTransformer;
+import org.hibernate.bytecode.ProxyFactoryFactory;
+import org.hibernate.bytecode.ReflectionOptimizer;
+import org.hibernate.bytecode.util.ClassFilter;
+import org.hibernate.bytecode.util.FieldFilter;
+import org.hibernate.util.StringHelper;
+
+/**
+ * Bytecode provider implementation for Javassist.
+ *
+ * @author Steve Ebersole
+ */
+public class BytecodeProviderImpl implements BytecodeProvider {
+
+ private static final Log log = LogFactory.getLog( BytecodeProviderImpl.class );
+
+ public ProxyFactoryFactory getProxyFactoryFactory() {
+ return new ProxyFactoryFactoryImpl();
+ }
+
+ public ReflectionOptimizer getReflectionOptimizer(
+ Class clazz,
+ String[] getterNames,
+ String[] setterNames,
+ Class[] types) {
+ FastClass fastClass;
+ BulkAccessor bulkAccessor;
+ try {
+ fastClass = FastClass.create( clazz );
+ bulkAccessor = BulkAccessor.create( clazz, getterNames, setterNames, types );
+ if ( !clazz.isInterface() && !Modifier.isAbstract( clazz.getModifiers() ) ) {
+ if ( fastClass == null ) {
+ bulkAccessor = null;
+ }
+ else {
+ //test out the optimizer:
+ Object instance = fastClass.newInstance();
+ bulkAccessor.setPropertyValues( instance, bulkAccessor.getPropertyValues( instance ) );
+ }
+ }
+ }
+ catch ( Throwable t ) {
+ fastClass = null;
+ bulkAccessor = null;
+ String message = "reflection optimizer disabled for: " +
+ clazz.getName() +
+ " [" +
+ StringHelper.unqualify( t.getClass().getName() ) +
+ ": " +
+ t.getMessage();
+
+ if ( t instanceof BulkAccessorException ) {
+ int index = ( ( BulkAccessorException ) t ).getIndex();
+ if ( index >= 0 ) {
+ message += " (property " + setterNames[index] + ")";
+ }
+ }
+
+ log.debug( message );
+ }
+
+ if ( fastClass != null && bulkAccessor != null ) {
+ return new ReflectionOptimizerImpl(
+ new InstantiationOptimizerAdapter( fastClass ),
+ new AccessOptimizerAdapter( bulkAccessor, clazz )
+ );
+ }
+ else {
+ return null;
+ }
+ }
+
+ public ClassTransformer getTransformer(ClassFilter classFilter, FieldFilter fieldFilter) {
+ return new JavassistClassTransformer( classFilter, fieldFilter );
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/FastClass.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/FastClass.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/FastClass.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,170 @@
+package org.hibernate.bytecode.javassist;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.io.Serializable;
+
+/**
+ * @author Muga Nishizawa
+ */
+public class FastClass implements Serializable {
+
+ private static final Class[] EMPTY_CLASS_ARRAY = new Class[0];
+
+ private Class type;
+
+ private FastClass() {
+ }
+
+ private FastClass(Class type) {
+ this.type = type;
+ }
+
+ public Object invoke(
+ String name,
+ Class[] parameterTypes,
+ Object obj,
+ Object[] args) throws InvocationTargetException {
+ return this.invoke( this.getIndex( name, parameterTypes ), obj, args );
+ }
+
+ public Object invoke(
+ int index,
+ Object obj,
+ Object[] args) throws InvocationTargetException {
+ Method[] methods = this.type.getMethods();
+ try {
+ return methods[index].invoke( obj, args );
+ }
+ catch ( ArrayIndexOutOfBoundsException e ) {
+ throw new IllegalArgumentException(
+ "Cannot find matching method/constructor"
+ );
+ }
+ catch ( IllegalAccessException e ) {
+ throw new InvocationTargetException( e );
+ }
+ }
+
+ public Object newInstance() throws InvocationTargetException {
+ return this.newInstance( this.getIndex( EMPTY_CLASS_ARRAY ), null );
+ }
+
+ public Object newInstance(
+ Class[] parameterTypes,
+ Object[] args) throws InvocationTargetException {
+ return this.newInstance( this.getIndex( parameterTypes ), args );
+ }
+
+ public Object newInstance(
+ int index,
+ Object[] args) throws InvocationTargetException {
+ Constructor[] conss = this.type.getConstructors();
+ try {
+ return conss[index].newInstance( args );
+ }
+ catch ( ArrayIndexOutOfBoundsException e ) {
+ throw new IllegalArgumentException( "Cannot find matching method/constructor" );
+ }
+ catch ( InstantiationException e ) {
+ throw new InvocationTargetException( e );
+ }
+ catch ( IllegalAccessException e ) {
+ throw new InvocationTargetException( e );
+ }
+ }
+
+ public int getIndex(String name, Class[] parameterTypes) {
+ Method[] methods = this.type.getMethods();
+ boolean eq = true;
+ for ( int i = 0; i < methods.length; ++i ) {
+ if ( !Modifier.isPublic( methods[i].getModifiers() ) ) {
+ continue;
+ }
+ if ( !methods[i].getName().equals( name ) ) {
+ continue;
+ }
+ Class[] params = methods[i].getParameterTypes();
+ if ( params.length != parameterTypes.length ) {
+ continue;
+ }
+ eq = true;
+ for ( int j = 0; j < params.length; ++j ) {
+ if ( !params[j].equals( parameterTypes[j] ) ) {
+ eq = false;
+ break;
+ }
+ }
+ if ( eq ) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public int getIndex(Class[] parameterTypes) {
+ Constructor[] conss = this.type.getConstructors();
+ boolean eq = true;
+ for ( int i = 0; i < conss.length; ++i ) {
+ if ( !Modifier.isPublic( conss[i].getModifiers() ) ) {
+ continue;
+ }
+ Class[] params = conss[i].getParameterTypes();
+ if ( params.length != parameterTypes.length ) {
+ continue;
+ }
+ eq = true;
+ for ( int j = 0; j < params.length; ++j ) {
+ if ( !params[j].equals( parameterTypes[j] ) ) {
+ eq = false;
+ break;
+ }
+ }
+ if ( eq ) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public int getMaxIndex() {
+ Method[] methods = this.type.getMethods();
+ int count = 0;
+ for ( int i = 0; i < methods.length; ++i ) {
+ if ( Modifier.isPublic( methods[i].getModifiers() ) ) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ public String getName() {
+ return this.type.getName();
+ }
+
+ public Class getJavaClass() {
+ return this.type;
+ }
+
+ public String toString() {
+ return this.type.toString();
+ }
+
+ public int hashCode() {
+ return this.type.hashCode();
+ }
+
+ public boolean equals(Object o) {
+ if (! ( o instanceof FastClass ) ) {
+ return false;
+ }
+ return this.type.equals( ( ( FastClass ) o ).type );
+ }
+
+ public static FastClass create(Class type) {
+ FastClass fc = new FastClass( type );
+ return fc;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/FieldFilter.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/FieldFilter.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/FieldFilter.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,33 @@
+package org.hibernate.bytecode.javassist;
+
+/**
+ * Contract for deciding whether fields should be read and/or write intercepted.
+ *
+ * @author Muga Nishizawa
+ * @author Steve Ebersole
+ */
+public interface FieldFilter {
+ /**
+ * Should the given field be read intercepted?
+ *
+ * @param desc
+ * @param name
+ * @return true if the given field should be read intercepted; otherwise
+ * false.
+ */
+ boolean handleRead(String desc, String name);
+
+ /**
+ * Should the given field be write intercepted?
+ *
+ * @param desc
+ * @param name
+ * @return true if the given field should be write intercepted; otherwise
+ * false.
+ */
+ boolean handleWrite(String desc, String name);
+
+ boolean handleReadAccess(String fieldOwnerClassName, String fieldName);
+
+ boolean handleWriteAccess(String fieldOwnerClassName, String fieldName);
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/FieldHandled.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/FieldHandled.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/FieldHandled.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,23 @@
+package org.hibernate.bytecode.javassist;
+
+/**
+ * Interface introduced to the enhanced class in order to be able to
+ * inject a {@link FieldHandler} to define the interception behavior.
+ *
+ * @author Muga Nishizawa
+ */
+public interface FieldHandled {
+ /**
+ * Inject the field interception handler to be used.
+ *
+ * @param handler The field interception handler.
+ */
+ public void setFieldHandler(FieldHandler handler);
+
+ /**
+ * Access to the current field interception handler.
+ *
+ * @return The current field interception handler.
+ */
+ public FieldHandler getFieldHandler();
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/FieldHandler.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/FieldHandler.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/FieldHandler.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,56 @@
+package org.hibernate.bytecode.javassist;
+
+/**
+ * The interface defining how interception of a field should be handled.
+ *
+ * @author Muga Nishizawa
+ */
+public interface FieldHandler {
+
+ /**
+ * Called to handle writing an int value to a given field.
+ *
+ * @param obj ?
+ * @param name The name of the field being written
+ * @param oldValue The old field value
+ * @param newValue The new field value.
+ * @return ?
+ */
+ int writeInt(Object obj, String name, int oldValue, int newValue);
+
+ char writeChar(Object obj, String name, char oldValue, char newValue);
+
+ byte writeByte(Object obj, String name, byte oldValue, byte newValue);
+
+ boolean writeBoolean(Object obj, String name, boolean oldValue,
+ boolean newValue);
+
+ short writeShort(Object obj, String name, short oldValue, short newValue);
+
+ float writeFloat(Object obj, String name, float oldValue, float newValue);
+
+ double writeDouble(Object obj, String name, double oldValue, double newValue);
+
+ long writeLong(Object obj, String name, long oldValue, long newValue);
+
+ Object writeObject(Object obj, String name, Object oldValue, Object newValue);
+
+ int readInt(Object obj, String name, int oldValue);
+
+ char readChar(Object obj, String name, char oldValue);
+
+ byte readByte(Object obj, String name, byte oldValue);
+
+ boolean readBoolean(Object obj, String name, boolean oldValue);
+
+ short readShort(Object obj, String name, short oldValue);
+
+ float readFloat(Object obj, String name, float oldValue);
+
+ double readDouble(Object obj, String name, double oldValue);
+
+ long readLong(Object obj, String name, long oldValue);
+
+ Object readObject(Object obj, String name, Object oldValue);
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/FieldTransformer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/FieldTransformer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/FieldTransformer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,581 @@
+package org.hibernate.bytecode.javassist;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.util.Iterator;
+import java.util.List;
+
+import javassist.CannotCompileException;
+import javassist.bytecode.AccessFlag;
+import javassist.bytecode.BadBytecode;
+import javassist.bytecode.Bytecode;
+import javassist.bytecode.ClassFile;
+import javassist.bytecode.CodeAttribute;
+import javassist.bytecode.CodeIterator;
+import javassist.bytecode.ConstPool;
+import javassist.bytecode.Descriptor;
+import javassist.bytecode.FieldInfo;
+import javassist.bytecode.MethodInfo;
+import javassist.bytecode.Opcode;
+
+/**
+ * The thing that handles actual class enhancement in regards to
+ * intercepting field accesses.
+ *
+ * @author Muga Nishizawa
+ * @author Steve Ebersole
+ */
+public class FieldTransformer {
+
+ private static final String EACH_READ_METHOD_PREFIX = "$javassist_read_";
+
+ private static final String EACH_WRITE_METHOD_PREFIX = "$javassist_write_";
+
+ private static final String FIELD_HANDLED_TYPE_NAME = FieldHandled.class
+ .getName();
+
+ private static final String HANDLER_FIELD_NAME = "$JAVASSIST_READ_WRITE_HANDLER";
+
+ private static final String FIELD_HANDLER_TYPE_NAME = FieldHandler.class
+ .getName();
+
+ private static final String HANDLER_FIELD_DESCRIPTOR = 'L' + FIELD_HANDLER_TYPE_NAME
+ .replace('.', '/') + ';';
+
+ private static final String GETFIELDHANDLER_METHOD_NAME = "getFieldHandler";
+
+ private static final String SETFIELDHANDLER_METHOD_NAME = "setFieldHandler";
+
+ private static final String GETFIELDHANDLER_METHOD_DESCRIPTOR = "()"
+ + HANDLER_FIELD_DESCRIPTOR;
+
+ private static final String SETFIELDHANDLER_METHOD_DESCRIPTOR = "("
+ + HANDLER_FIELD_DESCRIPTOR + ")V";
+
+ private FieldFilter filter;
+
+ public FieldTransformer() {
+ this(null);
+ }
+
+ public FieldTransformer(FieldFilter f) {
+ filter = f;
+ }
+
+ public void setFieldFilter(FieldFilter f) {
+ filter = f;
+ }
+
+ public void transform(File file) throws Exception {
+ DataInputStream in = new DataInputStream(new FileInputStream(file));
+ ClassFile classfile = new ClassFile(in);
+ transform(classfile);
+ DataOutputStream out = new DataOutputStream(new FileOutputStream(file));
+ try {
+ classfile.write(out);
+ } finally {
+ out.close();
+ }
+ }
+
+ public void transform(ClassFile classfile) throws Exception {
+ if (classfile.isInterface()) {
+ return;
+ }
+ try {
+ addFieldHandlerField(classfile);
+ addGetFieldHandlerMethod(classfile);
+ addSetFieldHandlerMethod(classfile);
+ addFieldHandledInterface(classfile);
+ addReadWriteMethods(classfile);
+ transformInvokevirtualsIntoPutAndGetfields(classfile);
+ } catch (CannotCompileException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+
+ private void addFieldHandlerField(ClassFile classfile)
+ throws CannotCompileException {
+ ConstPool cp = classfile.getConstPool();
+ FieldInfo finfo = new FieldInfo(cp, HANDLER_FIELD_NAME,
+ HANDLER_FIELD_DESCRIPTOR);
+ finfo.setAccessFlags(AccessFlag.PRIVATE | AccessFlag.TRANSIENT);
+ classfile.addField(finfo);
+ }
+
+ private void addGetFieldHandlerMethod(ClassFile classfile)
+ throws CannotCompileException {
+ ConstPool cp = classfile.getConstPool();
+ int this_class_index = cp.getThisClassInfo();
+ MethodInfo minfo = new MethodInfo(cp, GETFIELDHANDLER_METHOD_NAME,
+ GETFIELDHANDLER_METHOD_DESCRIPTOR);
+ /* local variable | this | */
+ Bytecode code = new Bytecode(cp, 2, 1);
+ // aload_0 // load this
+ code.addAload(0);
+ // getfield // get field "$JAVASSIST_CALLBACK" defined already
+ code.addOpcode(Opcode.GETFIELD);
+ int field_index = cp.addFieldrefInfo(this_class_index,
+ HANDLER_FIELD_NAME, HANDLER_FIELD_DESCRIPTOR);
+ code.addIndex(field_index);
+ // areturn // return the value of the field
+ code.addOpcode(Opcode.ARETURN);
+ minfo.setCodeAttribute(code.toCodeAttribute());
+ minfo.setAccessFlags(AccessFlag.PUBLIC);
+ classfile.addMethod(minfo);
+ }
+
+ private void addSetFieldHandlerMethod(ClassFile classfile)
+ throws CannotCompileException {
+ ConstPool cp = classfile.getConstPool();
+ int this_class_index = cp.getThisClassInfo();
+ MethodInfo minfo = new MethodInfo(cp, SETFIELDHANDLER_METHOD_NAME,
+ SETFIELDHANDLER_METHOD_DESCRIPTOR);
+ /* local variables | this | callback | */
+ Bytecode code = new Bytecode(cp, 3, 3);
+ // aload_0 // load this
+ code.addAload(0);
+ // aload_1 // load callback
+ code.addAload(1);
+ // putfield // put field "$JAVASSIST_CALLBACK" defined already
+ code.addOpcode(Opcode.PUTFIELD);
+ int field_index = cp.addFieldrefInfo(this_class_index,
+ HANDLER_FIELD_NAME, HANDLER_FIELD_DESCRIPTOR);
+ code.addIndex(field_index);
+ // return
+ code.addOpcode(Opcode.RETURN);
+ minfo.setCodeAttribute(code.toCodeAttribute());
+ minfo.setAccessFlags(AccessFlag.PUBLIC);
+ classfile.addMethod(minfo);
+ }
+
+ private void addFieldHandledInterface(ClassFile classfile) {
+ String[] interfaceNames = classfile.getInterfaces();
+ String[] newInterfaceNames = new String[interfaceNames.length + 1];
+ System.arraycopy(interfaceNames, 0, newInterfaceNames, 0,
+ interfaceNames.length);
+ newInterfaceNames[newInterfaceNames.length - 1] = FIELD_HANDLED_TYPE_NAME;
+ classfile.setInterfaces(newInterfaceNames);
+ }
+
+ private void addReadWriteMethods(ClassFile classfile)
+ throws CannotCompileException {
+ List fields = classfile.getFields();
+ for (Iterator field_iter = fields.iterator(); field_iter.hasNext();) {
+ FieldInfo finfo = (FieldInfo) field_iter.next();
+ if ((finfo.getAccessFlags() & AccessFlag.STATIC) == 0
+ && (!finfo.getName().equals(HANDLER_FIELD_NAME))) {
+ // case of non-static field
+ if (filter.handleRead(finfo.getDescriptor(), finfo
+ .getName())) {
+ addReadMethod(classfile, finfo);
+ }
+ if (filter.handleWrite(finfo.getDescriptor(), finfo
+ .getName())) {
+ addWriteMethod(classfile, finfo);
+ }
+ }
+ }
+ }
+
+ private void addReadMethod(ClassFile classfile, FieldInfo finfo)
+ throws CannotCompileException {
+ ConstPool cp = classfile.getConstPool();
+ int this_class_index = cp.getThisClassInfo();
+ String desc = "()" + finfo.getDescriptor();
+ MethodInfo minfo = new MethodInfo(cp, EACH_READ_METHOD_PREFIX
+ + finfo.getName(), desc);
+ /* local variables | target obj | each oldvalue | */
+ Bytecode code = new Bytecode(cp, 5, 3);
+ // aload_0
+ code.addAload(0);
+ // getfield // get each field
+ code.addOpcode(Opcode.GETFIELD);
+ int base_field_index = cp.addFieldrefInfo(this_class_index, finfo
+ .getName(), finfo.getDescriptor());
+ code.addIndex(base_field_index);
+ // aload_0
+ code.addAload(0);
+ // invokeinterface // invoke Enabled.getInterceptFieldCallback()
+ int enabled_class_index = cp.addClassInfo(FIELD_HANDLED_TYPE_NAME);
+ code.addInvokeinterface(enabled_class_index,
+ GETFIELDHANDLER_METHOD_NAME, GETFIELDHANDLER_METHOD_DESCRIPTOR,
+ 1);
+ // ifnonnull
+ code.addOpcode(Opcode.IFNONNULL);
+ code.addIndex(4);
+ // *return // each type
+ addTypeDependDataReturn(code, finfo.getDescriptor());
+ // *store_1 // each type
+ addTypeDependDataStore(code, finfo.getDescriptor(), 1);
+ // aload_0
+ code.addAload(0);
+ // invokeinterface // invoke Enabled.getInterceptFieldCallback()
+ code.addInvokeinterface(enabled_class_index,
+ GETFIELDHANDLER_METHOD_NAME, GETFIELDHANDLER_METHOD_DESCRIPTOR,
+ 1);
+ // aload_0
+ code.addAload(0);
+ // ldc // name of the field
+ code.addLdc(finfo.getName());
+ // *load_1 // each type
+ addTypeDependDataLoad(code, finfo.getDescriptor(), 1);
+ // invokeinterface // invoke Callback.read*() // each type
+ addInvokeFieldHandlerMethod(classfile, code, finfo.getDescriptor(),
+ true);
+ // *return // each type
+ addTypeDependDataReturn(code, finfo.getDescriptor());
+
+ minfo.setCodeAttribute(code.toCodeAttribute());
+ minfo.setAccessFlags(AccessFlag.PUBLIC);
+ classfile.addMethod(minfo);
+ }
+
+ private void addWriteMethod(ClassFile classfile, FieldInfo finfo)
+ throws CannotCompileException {
+ ConstPool cp = classfile.getConstPool();
+ int this_class_index = cp.getThisClassInfo();
+ String desc = "(" + finfo.getDescriptor() + ")V";
+ MethodInfo minfo = new MethodInfo(cp, EACH_WRITE_METHOD_PREFIX
+ + finfo.getName(), desc);
+ /* local variables | target obj | each oldvalue | */
+ Bytecode code = new Bytecode(cp, 6, 3);
+ // aload_0
+ code.addAload(0);
+ // invokeinterface // enabled.getInterceptFieldCallback()
+ int enabled_class_index = cp.addClassInfo(FIELD_HANDLED_TYPE_NAME);
+ code.addInvokeinterface(enabled_class_index,
+ GETFIELDHANDLER_METHOD_NAME, GETFIELDHANDLER_METHOD_DESCRIPTOR,
+ 1);
+ // ifnonnull (label1)
+ code.addOpcode(Opcode.IFNONNULL);
+ code.addIndex(9);
+ // aload_0
+ code.addAload(0);
+ // *load_1
+ addTypeDependDataLoad(code, finfo.getDescriptor(), 1);
+ // putfield
+ code.addOpcode(Opcode.PUTFIELD);
+ int base_field_index = cp.addFieldrefInfo(this_class_index, finfo
+ .getName(), finfo.getDescriptor());
+ code.addIndex(base_field_index);
+ code.growStack(-Descriptor.dataSize(finfo.getDescriptor()));
+ // return ;
+ code.addOpcode(Opcode.RETURN);
+ // aload_0
+ code.addAload(0);
+ // dup
+ code.addOpcode(Opcode.DUP);
+ // invokeinterface // enabled.getInterceptFieldCallback()
+ code.addInvokeinterface(enabled_class_index,
+ GETFIELDHANDLER_METHOD_NAME, GETFIELDHANDLER_METHOD_DESCRIPTOR,
+ 1);
+ // aload_0
+ code.addAload(0);
+ // ldc // field name
+ code.addLdc(finfo.getName());
+ // aload_0
+ code.addAload(0);
+ // getfield // old value of the field
+ code.addOpcode(Opcode.GETFIELD);
+ code.addIndex(base_field_index);
+ code.growStack(Descriptor.dataSize(finfo.getDescriptor()) - 1);
+ // *load_1
+ addTypeDependDataLoad(code, finfo.getDescriptor(), 1);
+ // invokeinterface // callback.write*(..)
+ addInvokeFieldHandlerMethod(classfile, code, finfo.getDescriptor(),
+ false);
+ // putfield // new value of the field
+ code.addOpcode(Opcode.PUTFIELD);
+ code.addIndex(base_field_index);
+ code.growStack(-Descriptor.dataSize(finfo.getDescriptor()));
+ // return
+ code.addOpcode(Opcode.RETURN);
+
+ minfo.setCodeAttribute(code.toCodeAttribute());
+ minfo.setAccessFlags(AccessFlag.PUBLIC);
+ classfile.addMethod(minfo);
+ }
+
+ private void transformInvokevirtualsIntoPutAndGetfields(ClassFile classfile)
+ throws CannotCompileException {
+ List methods = classfile.getMethods();
+ for (Iterator method_iter = methods.iterator(); method_iter.hasNext();) {
+ MethodInfo minfo = (MethodInfo) method_iter.next();
+ String methodName = minfo.getName();
+ if (methodName.startsWith(EACH_READ_METHOD_PREFIX)
+ || methodName.startsWith(EACH_WRITE_METHOD_PREFIX)
+ || methodName.equals(GETFIELDHANDLER_METHOD_NAME)
+ || methodName.equals(SETFIELDHANDLER_METHOD_NAME)) {
+ continue;
+ }
+ CodeAttribute codeAttr = minfo.getCodeAttribute();
+ if (codeAttr == null) {
+ return;
+ }
+ CodeIterator iter = codeAttr.iterator();
+ while (iter.hasNext()) {
+ try {
+ int pos = iter.next();
+ pos = transformInvokevirtualsIntoGetfields(classfile, iter, pos);
+ pos = transformInvokevirtualsIntoPutfields(classfile, iter, pos);
+
+ } catch (BadBytecode e) {
+ throw new CannotCompileException(e);
+ }
+ }
+ }
+ }
+
+ private int transformInvokevirtualsIntoGetfields(ClassFile classfile, CodeIterator iter, int pos) {
+ ConstPool cp = classfile.getConstPool();
+ int c = iter.byteAt(pos);
+ if (c != Opcode.GETFIELD) {
+ return pos;
+ }
+ int index = iter.u16bitAt(pos + 1);
+ String fieldName = cp.getFieldrefName(index);
+ String className = cp.getFieldrefClassName(index);
+ if ( !filter.handleReadAccess( className, fieldName ) ) {
+ return pos;
+ }
+ String desc = "()" + cp.getFieldrefType( index );
+ int read_method_index = cp.addMethodrefInfo(
+ cp.getThisClassInfo(),
+ EACH_READ_METHOD_PREFIX + fieldName,
+ desc
+ );
+ iter.writeByte(Opcode.INVOKEVIRTUAL, pos);
+ iter.write16bit(read_method_index, pos + 1);
+ return pos;
+ }
+
+ private int transformInvokevirtualsIntoPutfields(
+ ClassFile classfile,
+ CodeIterator iter, int pos) {
+ ConstPool cp = classfile.getConstPool();
+ int c = iter.byteAt(pos);
+ if (c != Opcode.PUTFIELD) {
+ return pos;
+ }
+ int index = iter.u16bitAt(pos + 1);
+ String fieldName = cp.getFieldrefName(index);
+ String className = cp.getFieldrefClassName(index);
+ if ( !filter.handleWriteAccess( className, fieldName ) ) {
+ return pos;
+ }
+ String desc = "(" + cp.getFieldrefType( index ) + ")V";
+ int write_method_index = cp.addMethodrefInfo(
+ cp.getThisClassInfo(),
+ EACH_WRITE_METHOD_PREFIX + fieldName,
+ desc
+ );
+ iter.writeByte(Opcode.INVOKEVIRTUAL, pos);
+ iter.write16bit(write_method_index, pos + 1);
+ return pos;
+ }
+
+ private static void addInvokeFieldHandlerMethod(ClassFile classfile,
+ Bytecode code, String typeName, boolean isReadMethod) {
+ ConstPool cp = classfile.getConstPool();
+ // invokeinterface
+ int callback_type_index = cp.addClassInfo(FIELD_HANDLER_TYPE_NAME);
+ if ((typeName.charAt(0) == 'L')
+ && (typeName.charAt(typeName.length() - 1) == ';')
+ || (typeName.charAt(0) == '[')) {
+ // reference type
+ int indexOfL = typeName.indexOf('L');
+ String type;
+ if (indexOfL == 0) {
+ // not array
+ type = typeName.substring(1, typeName.length() - 1);
+ type = type.replace('/', '.');
+ } else if (indexOfL == -1) {
+ // array of primitive type
+ // do nothing
+ type = typeName;
+ } else {
+ // array of reference type
+ type = typeName.replace('/', '.');
+ }
+ if (isReadMethod) {
+ code
+ .addInvokeinterface(
+ callback_type_index,
+ "readObject",
+ "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;",
+ 4);
+ // checkcast
+ code.addCheckcast(type);
+ } else {
+ code
+ .addInvokeinterface(
+ callback_type_index,
+ "writeObject",
+ "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+ 5);
+ // checkcast
+ code.addCheckcast(type);
+ }
+ } else if (typeName.equals("Z")) {
+ // boolean
+ if (isReadMethod) {
+ code.addInvokeinterface(callback_type_index, "readBoolean",
+ "(Ljava/lang/Object;Ljava/lang/String;Z)Z", 4);
+ } else {
+ code.addInvokeinterface(callback_type_index, "writeBoolean",
+ "(Ljava/lang/Object;Ljava/lang/String;ZZ)Z", 5);
+ }
+ } else if (typeName.equals("B")) {
+ // byte
+ if (isReadMethod) {
+ code.addInvokeinterface(callback_type_index, "readByte",
+ "(Ljava/lang/Object;Ljava/lang/String;B)B", 4);
+ } else {
+ code.addInvokeinterface(callback_type_index, "writeByte",
+ "(Ljava/lang/Object;Ljava/lang/String;BB)B", 5);
+ }
+ } else if (typeName.equals("C")) {
+ // char
+ if (isReadMethod) {
+ code.addInvokeinterface(callback_type_index, "readChar",
+ "(Ljava/lang/Object;Ljava/lang/String;C)C", 4);
+ } else {
+ code.addInvokeinterface(callback_type_index, "writeChar",
+ "(Ljava/lang/Object;Ljava/lang/String;CC)C", 5);
+ }
+ } else if (typeName.equals("I")) {
+ // int
+ if (isReadMethod) {
+ code.addInvokeinterface(callback_type_index, "readInt",
+ "(Ljava/lang/Object;Ljava/lang/String;I)I", 4);
+ } else {
+ code.addInvokeinterface(callback_type_index, "writeInt",
+ "(Ljava/lang/Object;Ljava/lang/String;II)I", 5);
+ }
+ } else if (typeName.equals("S")) {
+ // short
+ if (isReadMethod) {
+ code.addInvokeinterface(callback_type_index, "readShort",
+ "(Ljava/lang/Object;Ljava/lang/String;S)S", 4);
+ } else {
+ code.addInvokeinterface(callback_type_index, "writeShort",
+ "(Ljava/lang/Object;Ljava/lang/String;SS)S", 5);
+ }
+ } else if (typeName.equals("D")) {
+ // double
+ if (isReadMethod) {
+ code.addInvokeinterface(callback_type_index, "readDouble",
+ "(Ljava/lang/Object;Ljava/lang/String;D)D", 5);
+ } else {
+ code.addInvokeinterface(callback_type_index, "writeDouble",
+ "(Ljava/lang/Object;Ljava/lang/String;DD)D", 7);
+ }
+ } else if (typeName.equals("F")) {
+ // float
+ if (isReadMethod) {
+ code.addInvokeinterface(callback_type_index, "readFloat",
+ "(Ljava/lang/Object;Ljava/lang/String;F)F", 4);
+ } else {
+ code.addInvokeinterface(callback_type_index, "writeFloat",
+ "(Ljava/lang/Object;Ljava/lang/String;FF)F", 5);
+ }
+ } else if (typeName.equals("J")) {
+ // long
+ if (isReadMethod) {
+ code.addInvokeinterface(callback_type_index, "readLong",
+ "(Ljava/lang/Object;Ljava/lang/String;J)J", 5);
+ } else {
+ code.addInvokeinterface(callback_type_index, "writeLong",
+ "(Ljava/lang/Object;Ljava/lang/String;JJ)J", 7);
+ }
+ } else {
+ // bad type
+ throw new RuntimeException("bad type: " + typeName);
+ }
+ }
+
+ private static void addTypeDependDataLoad(Bytecode code, String typeName,
+ int i) {
+ if ((typeName.charAt(0) == 'L')
+ && (typeName.charAt(typeName.length() - 1) == ';')
+ || (typeName.charAt(0) == '[')) {
+ // reference type
+ code.addAload(i);
+ } else if (typeName.equals("Z") || typeName.equals("B")
+ || typeName.equals("C") || typeName.equals("I")
+ || typeName.equals("S")) {
+ // boolean, byte, char, int, short
+ code.addIload(i);
+ } else if (typeName.equals("D")) {
+ // double
+ code.addDload(i);
+ } else if (typeName.equals("F")) {
+ // float
+ code.addFload(i);
+ } else if (typeName.equals("J")) {
+ // long
+ code.addLload(i);
+ } else {
+ // bad type
+ throw new RuntimeException("bad type: " + typeName);
+ }
+ }
+
+ private static void addTypeDependDataStore(Bytecode code, String typeName,
+ int i) {
+ if ((typeName.charAt(0) == 'L')
+ && (typeName.charAt(typeName.length() - 1) == ';')
+ || (typeName.charAt(0) == '[')) {
+ // reference type
+ code.addAstore(i);
+ } else if (typeName.equals("Z") || typeName.equals("B")
+ || typeName.equals("C") || typeName.equals("I")
+ || typeName.equals("S")) {
+ // boolean, byte, char, int, short
+ code.addIstore(i);
+ } else if (typeName.equals("D")) {
+ // double
+ code.addDstore(i);
+ } else if (typeName.equals("F")) {
+ // float
+ code.addFstore(i);
+ } else if (typeName.equals("J")) {
+ // long
+ code.addLstore(i);
+ } else {
+ // bad type
+ throw new RuntimeException("bad type: " + typeName);
+ }
+ }
+
+ private static void addTypeDependDataReturn(Bytecode code, String typeName) {
+ if ((typeName.charAt(0) == 'L')
+ && (typeName.charAt(typeName.length() - 1) == ';')
+ || (typeName.charAt(0) == '[')) {
+ // reference type
+ code.addOpcode(Opcode.ARETURN);
+ } else if (typeName.equals("Z") || typeName.equals("B")
+ || typeName.equals("C") || typeName.equals("I")
+ || typeName.equals("S")) {
+ // boolean, byte, char, int, short
+ code.addOpcode(Opcode.IRETURN);
+ } else if (typeName.equals("D")) {
+ // double
+ code.addOpcode(Opcode.DRETURN);
+ } else if (typeName.equals("F")) {
+ // float
+ code.addOpcode(Opcode.FRETURN);
+ } else if (typeName.equals("J")) {
+ // long
+ code.addOpcode(Opcode.LRETURN);
+ } else {
+ // bad type
+ throw new RuntimeException("bad type: " + typeName);
+ }
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/InstantiationOptimizerAdapter.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/InstantiationOptimizerAdapter.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/InstantiationOptimizerAdapter.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,32 @@
+package org.hibernate.bytecode.javassist;
+
+import org.hibernate.bytecode.ReflectionOptimizer;
+import org.hibernate.InstantiationException;
+
+import java.io.Serializable;
+
+/**
+ * The {@link ReflectionOptimizer.InstantiationOptimizer} implementation for Javassist
+ * which simply acts as an adpater to the {@link FastClass} class.
+ *
+ * @author Steve Ebersole
+ */
+public class InstantiationOptimizerAdapter implements ReflectionOptimizer.InstantiationOptimizer, Serializable {
+ private final FastClass fastClass;
+
+ public InstantiationOptimizerAdapter(FastClass fastClass) {
+ this.fastClass = fastClass;
+ }
+
+ public Object newInstance() {
+ try {
+ return fastClass.newInstance();
+ }
+ catch ( Throwable t ) {
+ throw new InstantiationException(
+ "Could not instantiate entity with Javassist optimizer: ",
+ fastClass.getJavaClass(), t
+ );
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/JavassistClassTransformer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/JavassistClassTransformer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/JavassistClassTransformer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,111 @@
+//$Id: $
+package org.hibernate.bytecode.javassist;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.security.ProtectionDomain;
+
+import javassist.bytecode.ClassFile;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.HibernateException;
+import org.hibernate.bytecode.AbstractClassTransformerImpl;
+import org.hibernate.bytecode.util.ClassFilter;
+
+/**
+ * Enhance the classes allowing them to implements InterceptFieldEnabled
+ * This interface is then used by Hibernate for some optimizations.
+ *
+ * @author Emmanuel Bernard
+ * @author Steve Ebersole
+ */
+public class JavassistClassTransformer extends AbstractClassTransformerImpl {
+
+ private static Log log = LogFactory.getLog( JavassistClassTransformer.class.getName() );
+
+ public JavassistClassTransformer(ClassFilter classFilter, org.hibernate.bytecode.util.FieldFilter fieldFilter) {
+ super( classFilter, fieldFilter );
+ }
+
+ protected byte[] doTransform(
+ ClassLoader loader,
+ String className,
+ Class classBeingRedefined,
+ ProtectionDomain protectionDomain,
+ byte[] classfileBuffer) {
+ ClassFile classfile;
+ try {
+ // WARNING: classfile only
+ classfile = new ClassFile( new DataInputStream( new ByteArrayInputStream( classfileBuffer ) ) );
+ }
+ catch (IOException e) {
+ log.error( "Unable to build enhancement metamodel for " + className );
+ return classfileBuffer;
+ }
+ FieldTransformer transformer = getFieldTransformer( classfile );
+ if ( transformer != null ) {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "Enhancing " + className );
+ }
+ DataOutputStream out = null;
+ try {
+ transformer.transform( classfile );
+ ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+ out = new DataOutputStream( byteStream );
+ classfile.write( out );
+ return byteStream.toByteArray();
+ }
+ catch (Exception e) {
+ log.error( "Unable to transform class", e );
+ throw new HibernateException( "Unable to transform class: " + e.getMessage() );
+ }
+ finally {
+ try {
+ if ( out != null ) out.close();
+ }
+ catch (IOException e) {
+ //swallow
+ }
+ }
+ }
+ return classfileBuffer;
+ }
+
+ protected FieldTransformer getFieldTransformer(final ClassFile classfile) {
+ if ( alreadyInstrumented( classfile ) ) {
+ return null;
+ }
+ return new FieldTransformer(
+ new FieldFilter() {
+ public boolean handleRead(String desc, String name) {
+ return fieldFilter.shouldInstrumentField( classfile.getName(), name );
+ }
+
+ public boolean handleWrite(String desc, String name) {
+ return fieldFilter.shouldInstrumentField( classfile.getName(), name );
+ }
+
+ public boolean handleReadAccess(String fieldOwnerClassName, String fieldName) {
+ return fieldFilter.shouldTransformFieldAccess( classfile.getName(), fieldOwnerClassName, fieldName );
+ }
+
+ public boolean handleWriteAccess(String fieldOwnerClassName, String fieldName) {
+ return fieldFilter.shouldTransformFieldAccess( classfile.getName(), fieldOwnerClassName, fieldName );
+ }
+ }
+ );
+ }
+
+ private boolean alreadyInstrumented(ClassFile classfile) {
+ String[] intfs = classfile.getInterfaces();
+ for ( int i = 0; i < intfs.length; i++ ) {
+ if ( FieldHandled.class.getName().equals( intfs[i] ) ) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/ProxyFactoryFactoryImpl.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/ProxyFactoryFactoryImpl.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/ProxyFactoryFactoryImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,123 @@
+package org.hibernate.bytecode.javassist;
+
+import org.hibernate.bytecode.ProxyFactoryFactory;
+import org.hibernate.bytecode.BasicProxyFactory;
+import org.hibernate.proxy.ProxyFactory;
+import org.hibernate.proxy.pojo.javassist.JavassistProxyFactory;
+import org.hibernate.AssertionFailure;
+import org.hibernate.HibernateException;
+import javassist.util.proxy.MethodFilter;
+import javassist.util.proxy.ProxyObject;
+import javassist.util.proxy.MethodHandler;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+
+/**
+ * A factory for Javassist-based {@link ProxyFactory} instances.
+ *
+ * @author Steve Ebersole
+ */
+public class ProxyFactoryFactoryImpl implements ProxyFactoryFactory {
+
+ /**
+ * Builds a Javassist-based proxy factory.
+ *
+ * @return a new Javassist-based proxy factory.
+ */
+ public ProxyFactory buildProxyFactory() {
+ return new JavassistProxyFactory();
+ }
+
+ public BasicProxyFactory buildBasicProxyFactory(Class superClass, Class[] interfaces) {
+ return new BasicProxyFactoryImpl( superClass, interfaces );
+ }
+
+ private static class BasicProxyFactoryImpl implements BasicProxyFactory {
+ private final Class proxyClass;
+
+ public BasicProxyFactoryImpl(Class superClass, Class[] interfaces) {
+ if ( superClass == null && ( interfaces == null || interfaces.length < 1 ) ) {
+ throw new AssertionFailure( "attempting to build proxy without any superclass or interfaces" );
+ }
+ javassist.util.proxy.ProxyFactory factory = new javassist.util.proxy.ProxyFactory();
+ factory.setFilter( FINALIZE_FILTER );
+ if ( superClass != null ) {
+ factory.setSuperclass( superClass );
+ }
+ if ( interfaces != null && interfaces.length > 0 ) {
+ factory.setInterfaces( interfaces );
+ }
+ proxyClass = factory.createClass();
+ }
+
+ public Object getProxy() {
+ try {
+ ProxyObject proxy = ( ProxyObject ) proxyClass.newInstance();
+ proxy.setHandler( new PassThroughHandler( proxy, proxyClass.getName() ) );
+ return proxy;
+ }
+ catch ( Throwable t ) {
+ throw new HibernateException( "Unable to instantiated proxy instance" );
+ }
+ }
+
+ public boolean isInstance(Object object) {
+ return proxyClass.isInstance( object );
+ }
+ }
+
+ private static final MethodFilter FINALIZE_FILTER = new MethodFilter() {
+ public boolean isHandled(Method m) {
+ // skip finalize methods
+ return !( m.getParameterTypes().length == 0 && m.getName().equals( "finalize" ) );
+ }
+ };
+
+ private static class PassThroughHandler implements MethodHandler {
+ private HashMap data = new HashMap();
+ private final Object proxiedObject;
+ private final String proxiedClassName;
+
+ public PassThroughHandler(Object proxiedObject, String proxiedClassName) {
+ this.proxiedObject = proxiedObject;
+ this.proxiedClassName = proxiedClassName;
+ }
+
+ public Object invoke(
+ Object object,
+ Method method,
+ Method method1,
+ Object[] args) throws Exception {
+ String name = method.getName();
+ if ( "toString".equals( name ) ) {
+ return proxiedClassName + "@" + System.identityHashCode( object );
+ }
+ else if ( "equals".equals( name ) ) {
+ return proxiedObject == object ? Boolean.TRUE : Boolean.FALSE;
+ }
+ else if ( "hashCode".equals( name ) ) {
+ return new Integer( System.identityHashCode( object ) );
+ }
+ boolean hasGetterSignature = method.getParameterTypes().length == 0 && method.getReturnType() != null;
+ boolean hasSetterSignature = method.getParameterTypes().length == 1 && ( method.getReturnType() == null || method.getReturnType() == void.class );
+ if ( name.startsWith( "get" ) && hasGetterSignature ) {
+ String propName = name.substring( 3 );
+ return data.get( propName );
+ }
+ else if ( name.startsWith( "is" ) && hasGetterSignature ) {
+ String propName = name.substring( 2 );
+ return data.get( propName );
+ }
+ else if ( name.startsWith( "set" ) && hasSetterSignature) {
+ String propName = name.substring( 3 );
+ data.put( propName, args[0] );
+ return null;
+ }
+ else {
+ // todo : what else to do here?
+ return null;
+ }
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/ReflectionOptimizerImpl.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/ReflectionOptimizerImpl.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/ReflectionOptimizerImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,32 @@
+package org.hibernate.bytecode.javassist;
+
+import org.hibernate.bytecode.ReflectionOptimizer;
+
+import java.io.Serializable;
+
+/**
+ * ReflectionOptimizer implementation for Javassist.
+ *
+ * @author Steve Ebersole
+ */
+public class ReflectionOptimizerImpl implements ReflectionOptimizer, Serializable {
+
+ private final InstantiationOptimizer instantiationOptimizer;
+ private final AccessOptimizer accessOptimizer;
+
+ public ReflectionOptimizerImpl(
+ InstantiationOptimizer instantiationOptimizer,
+ AccessOptimizer accessOptimizer) {
+ this.instantiationOptimizer = instantiationOptimizer;
+ this.accessOptimizer = accessOptimizer;
+ }
+
+ public InstantiationOptimizer getInstantiationOptimizer() {
+ return instantiationOptimizer;
+ }
+
+ public AccessOptimizer getAccessOptimizer() {
+ return accessOptimizer;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/TransformingClassLoader.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/TransformingClassLoader.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/javassist/TransformingClassLoader.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,57 @@
+package org.hibernate.bytecode.javassist;
+
+import javassist.ClassPool;
+import javassist.NotFoundException;
+import javassist.CtClass;
+import javassist.CannotCompileException;
+import org.hibernate.HibernateException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * @author Steve Ebersole
+ */
+public class TransformingClassLoader extends ClassLoader {
+ private ClassLoader parent;
+ private ClassPool classPool;
+
+ /*package*/ TransformingClassLoader(ClassLoader parent, String[] classpath) {
+ this.parent = parent;
+ classPool = new ClassPool( true );
+ for ( int i = 0; i < classpath.length; i++ ) {
+ try {
+ classPool.appendClassPath( classpath[i] );
+ }
+ catch ( NotFoundException e ) {
+ throw new HibernateException(
+ "Unable to resolve requested classpath for transformation [" +
+ classpath[i] + "] : " + e.getMessage()
+ );
+ }
+ }
+ }
+
+ protected Class findClass(String name) throws ClassNotFoundException {
+ try {
+ CtClass cc = classPool.get( name );
+ // todo : modify the class definition if not already transformed...
+ byte[] b = cc.toBytecode();
+ return defineClass( name, b, 0, b.length );
+ }
+ catch ( NotFoundException e ) {
+ throw new ClassNotFoundException();
+ }
+ catch ( IOException e ) {
+ throw new ClassNotFoundException();
+ }
+ catch ( CannotCompileException e ) {
+ throw new ClassNotFoundException();
+ }
+ }
+
+ public void release() {
+ classPool = null;
+ parent = null;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,38 @@
+<html>
+ <head></head>
+ <body>
+ <p>
+ This package defines the API for plugging in bytecode libraries
+ for usage by Hibernate. Hibernate uses these bytecode libraries
+ in three scenarios:<ol>
+ <li>
+ <b>Reflection optimization</b> - to speed up the performance of
+ POJO entity and component conctruction and field/property access
+ </li>
+ <li>
+ <b>Proxy generation</b> - runtime building of proxies used for
+ deferred loading of lazy entities
+ </li>
+ <li>
+ <b>Field-level interception</b> - build-time instrumentation of entity
+ classes for the purpose of intercepting field-level access (read/write)
+ for both lazy loading and dirty tracking.
+ </li>
+ </ol>
+ </p>
+ <p>
+ Currently, both CGLIB and Javassist are supported out-of-the-box.
+ </p>
+ <p>
+ Note that for field-level interception, simply plugging in a new {@link BytecodeProvider}
+ is not enough for Hibernate to be able to recognize new providers. You would additionally
+ need to make appropriate code changes to the {@link org.hibernate.intercept.Helper}
+ class. This is because the detection of these enhanced classes is needed in a static
+ environment (i.e. outside the scope of any {@link org.hibernate.SessionFactory}.
+ </p>
+ <p>
+ Note that in the current form the ability to specify a different bytecode provider
+ is actually considered a global settings (global to the JVM).
+ </p>
+ </body>
+</html>
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/util/BasicClassFilter.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/util/BasicClassFilter.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/util/BasicClassFilter.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,59 @@
+package org.hibernate.bytecode.util;
+
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * BasicClassFilter provides class filtering based on a series of packages to
+ * be included and/or a series of explicit class names to be included. If
+ * neither is specified, then no restrictions are applied.
+ *
+ * @author Steve Ebersole
+ */
+public class BasicClassFilter implements ClassFilter {
+ private final String[] includedPackages;
+ private final Set includedClassNames = new HashSet();
+ private final boolean isAllEmpty;
+
+ public BasicClassFilter() {
+ this( null, null );
+ }
+
+ public BasicClassFilter(String[] includedPackages, String[] includedClassNames) {
+ this.includedPackages = includedPackages;
+ if ( includedClassNames != null ) {
+ for ( int i = 0; i < includedClassNames.length; i++ ) {
+ this.includedClassNames.add( includedClassNames[i] );
+ }
+ }
+
+ isAllEmpty = ( this.includedPackages == null || this.includedPackages.length == 0 )
+ && ( this.includedClassNames.isEmpty() );
+ }
+
+ public boolean shouldInstrumentClass(String className) {
+ if ( isAllEmpty ) {
+ return true;
+ }
+ else if ( includedClassNames.contains( className ) ) {
+ return true;
+ }
+ else if ( isInIncludedPackage( className ) ) {
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ private boolean isInIncludedPackage(String className) {
+ if ( includedPackages != null ) {
+ for ( int i = 0; i < includedPackages.length; i++ ) {
+ if ( className.startsWith( includedPackages[i] ) ) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/util/ByteCodeHelper.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/util/ByteCodeHelper.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/util/ByteCodeHelper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,78 @@
+package org.hibernate.bytecode.util;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.BufferedInputStream;
+import java.util.zip.ZipInputStream;
+
+/**
+ * A helper for reading byte code from various input sources.
+ *
+ * @author Steve Ebersole
+ */
+public class ByteCodeHelper {
+ private ByteCodeHelper() {
+ }
+
+ /**
+ * Reads class byte array info from the given input stream.
+ * <p/>
+ * The stream is closed within this method!
+ *
+ * @param inputStream
+ * @return
+ * @throws IOException
+ */
+ public static byte[] readByteCode(InputStream inputStream) throws IOException {
+ if ( inputStream == null ) {
+ throw new IOException( "null input stream" );
+ }
+
+ byte[] buffer = new byte[409600];
+ byte[] classBytes = new byte[0];
+ int r = 0;
+
+ try {
+ r = inputStream.read( buffer );
+ while ( r >= buffer.length ) {
+ byte[] temp = new byte[ classBytes.length + buffer.length ];
+ System.arraycopy( classBytes, 0, temp, 0, classBytes.length );
+ System.arraycopy( buffer, 0, temp, classBytes.length, buffer.length );
+ classBytes = temp;
+ }
+ if ( r != -1 ) {
+ byte[] temp = new byte[ classBytes.length + r ];
+ System.arraycopy( classBytes, 0, temp, 0, classBytes.length );
+ System.arraycopy( buffer, 0, temp, classBytes.length, r );
+ classBytes = temp;
+ }
+ }
+ finally {
+ try {
+ inputStream.close();
+ }
+ catch (IOException ignore) {
+ // intentionally empty
+ }
+ }
+
+ return classBytes;
+ }
+
+ public static byte[] readByteCode(File file) throws IOException {
+ return ByteCodeHelper.readByteCode( new FileInputStream( file ) );
+ }
+
+ public static byte[] readByteCode(ZipInputStream zip) throws IOException {
+ ByteArrayOutputStream bout = new ByteArrayOutputStream();
+ InputStream in = new BufferedInputStream( zip );
+ int b;
+ while ( ( b = in.read() ) != -1 ) {
+ bout.write( b );
+ }
+ return bout.toByteArray();
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/util/ClassDescriptor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/util/ClassDescriptor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/util/ClassDescriptor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,30 @@
+package org.hibernate.bytecode.util;
+
+/**
+ * Contract describing the information Hibernate needs in terms of instrumenting
+ * a class, either via ant task or dynamic classloader.
+ *
+ * @author Steve Ebersole
+ */
+public interface ClassDescriptor {
+ /**
+ * The name of the class.
+ *
+ * @return The class name.
+ */
+ public String getName();
+
+ /**
+ * Determine if the class is already instrumented.
+ *
+ * @return True if already instrumented; false otherwise.
+ */
+ public boolean isInstrumented();
+
+ /**
+ * The bytes making up the class' bytecode.
+ *
+ * @return The bytecode bytes.
+ */
+ public byte[] getBytes();
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/util/ClassFilter.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/util/ClassFilter.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/util/ClassFilter.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,10 @@
+package org.hibernate.bytecode.util;
+
+/**
+ * Used to determine whether a class should be instrumented.
+ *
+ * @author Steve Ebersole
+ */
+public interface ClassFilter {
+ public boolean shouldInstrumentClass(String className);
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/util/FieldFilter.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/util/FieldFilter.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/bytecode/util/FieldFilter.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,29 @@
+package org.hibernate.bytecode.util;
+
+/**
+ * Used to determine whether a field reference should be instrumented.
+ *
+ * @author Steve Ebersole
+ */
+public interface FieldFilter {
+ /**
+ * Should this field definition be instrumented?
+ *
+ * @param className The name of the class currently being processed
+ * @param fieldName The name of the field being checked.
+ * @return True if we should instrument this field.
+ */
+ public boolean shouldInstrumentField(String className, String fieldName);
+
+ /**
+ * Should we instrument *access to* the given field. This differs from
+ * {@link #shouldInstrumentField} in that here we are talking about a particular usage of
+ * a field.
+ *
+ * @param transformingClassName The class currently being transformed.
+ * @param fieldOwnerClassName The name of the class owning this field being checked.
+ * @param fieldName The name of the field being checked.
+ * @return True if this access should be transformed.
+ */
+ public boolean shouldTransformFieldAccess(String transformingClassName, String fieldOwnerClassName, String fieldName);
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/AbstractJndiBoundCacheProvider.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/AbstractJndiBoundCacheProvider.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/AbstractJndiBoundCacheProvider.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,86 @@
+// $Id: AbstractJndiBoundCacheProvider.java 6079 2005-03-16 06:01:18Z oneovthafew $
+package org.hibernate.cache;
+
+import java.util.Properties;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.cfg.Environment;
+import org.hibernate.util.NamingHelper;
+import org.hibernate.util.StringHelper;
+
+/**
+ * Support for CacheProvider implementations which are backed by caches bound
+ * into JNDI namespace.
+ *
+ * @author Steve Ebersole
+ */
+public abstract class AbstractJndiBoundCacheProvider implements CacheProvider {
+
+ private static final Log log = LogFactory.getLog( AbstractJndiBoundCacheProvider.class );
+ private Object cache;
+
+ protected void prepare(Properties properties) {
+ // Do nothing; subclasses may override.
+ }
+
+ protected void release() {
+ // Do nothing; subclasses may override.
+ }
+
+ /**
+ * Callback to perform any necessary initialization of the underlying cache implementation during SessionFactory
+ * construction.
+ *
+ * @param properties current configuration settings.
+ */
+ public final void start(Properties properties) throws CacheException {
+ String jndiNamespace = properties.getProperty( Environment.CACHE_NAMESPACE );
+ if ( StringHelper.isEmpty( jndiNamespace ) ) {
+ throw new CacheException( "No JNDI namespace specified for cache" );
+ }
+ cache = locateCache( jndiNamespace, NamingHelper.getJndiProperties( properties ) );
+ prepare( properties );
+ }
+
+ /**
+ * Callback to perform any necessary cleanup of the underlying cache
+ * implementation during SessionFactory.close().
+ */
+ public final void stop() {
+ release();
+ cache = null;
+ }
+
+ private Object locateCache(String jndiNamespace, Properties jndiProperties) {
+
+ Context ctx = null;
+ try {
+ ctx = new InitialContext( jndiProperties );
+ return ctx.lookup( jndiNamespace );
+ }
+ catch (NamingException ne) {
+ String msg = "Unable to retreive Cache from JNDI [" + jndiNamespace + "]";
+ log.info( msg, ne );
+ throw new CacheException( msg );
+ }
+ finally {
+ if ( ctx != null ) {
+ try {
+ ctx.close();
+ }
+ catch( NamingException ne ) {
+ log.info( "Unable to release initial context", ne );
+ }
+ }
+ }
+ }
+
+ public Object getCache() {
+ return cache;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/Cache.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/Cache.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/Cache.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,108 @@
+//$Id: Cache.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.cache;
+
+import java.util.Map;
+
+/**
+ * Implementors define a caching algorithm. All implementors
+ * <b>must</b> be threadsafe.
+ *
+ * @deprecated As of 3.3; see <a href="package.html"/> for details.
+ */
+public interface Cache {
+ /**
+ * Get an item from the cache
+ * @param key
+ * @return the cached object or <tt>null</tt>
+ * @throws CacheException
+ */
+ public Object read(Object key) throws CacheException;
+ /**
+ * Get an item from the cache, nontransactionally
+ * @param key
+ * @return the cached object or <tt>null</tt>
+ * @throws CacheException
+ */
+ public Object get(Object key) throws CacheException;
+ /**
+ * Add an item to the cache, nontransactionally, with
+ * failfast semantics
+ * @param key
+ * @param value
+ * @throws CacheException
+ */
+ public void put(Object key, Object value) throws CacheException;
+ /**
+ * Add an item to the cache
+ * @param key
+ * @param value
+ * @throws CacheException
+ */
+ public void update(Object key, Object value) throws CacheException;
+ /**
+ * Remove an item from the cache
+ */
+ public void remove(Object key) throws CacheException;
+ /**
+ * Clear the cache
+ */
+ public void clear() throws CacheException;
+ /**
+ * Clean up
+ */
+ public void destroy() throws CacheException;
+ /**
+ * If this is a clustered cache, lock the item
+ */
+ public void lock(Object key) throws CacheException;
+ /**
+ * If this is a clustered cache, unlock the item
+ */
+ public void unlock(Object key) throws CacheException;
+ /**
+ * Generate a timestamp
+ */
+ public long nextTimestamp();
+ /**
+ * Get a reasonable "lock timeout"
+ */
+ public int getTimeout();
+
+ /**
+ * Get the name of the cache region
+ */
+ public String getRegionName();
+
+ /**
+ * The number of bytes is this cache region currently consuming in memory.
+ *
+ * @return The number of bytes consumed by this region; -1 if unknown or
+ * unsupported.
+ */
+ public long getSizeInMemory();
+
+ /**
+ * The count of entries currently contained in the regions in-memory store.
+ *
+ * @return The count of entries in memory; -1 if unknown or unsupported.
+ */
+ public long getElementCountInMemory();
+
+ /**
+ * The count of entries currently contained in the regions disk store.
+ *
+ * @return The count of entries on disk; -1 if unknown or unsupported.
+ */
+ public long getElementCountOnDisk();
+
+ /**
+ * optional operation
+ */
+ public Map toMap();
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/CacheConcurrencyStrategy.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/CacheConcurrencyStrategy.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/CacheConcurrencyStrategy.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,174 @@
+//$Id: CacheConcurrencyStrategy.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.cache;
+
+import java.util.Comparator;
+
+import org.hibernate.cache.access.SoftLock;
+
+/**
+ * Implementors manage transactional access to cached data. Transactions
+ * pass in a timestamp indicating transaction start time. Two different
+ * implementation patterns are provided for.<ul>
+ * <li>A transaction-aware cache implementation might be wrapped by a
+ * "synchronous" concurrency strategy, where updates to the cache are written
+ * to the cache inside the transaction.</li>
+ * <li>A non transaction-aware cache would be wrapped by an "asynchronous"
+ * concurrency strategy, where items are merely "soft locked" during the
+ * transaction and then updated during the "after transaction completion"
+ * phase; the soft lock is not an actual lock on the database row -
+ * only upon the cached representation of the item.</li>
+ * </ul>
+ * <p/>
+ * In terms of entity caches, the expected call sequences are: <ul>
+ * <li><b>DELETES</b> : {@link #lock} -> {@link #evict} -> {@link #release}</li>
+ * <li><b>UPDATES</b> : {@link #lock} -> {@link #update} -> {@link #afterUpdate}</li>
+ * <li><b>INSERTS</b> : {@link #insert} -> {@link #afterInsert}</li>
+ * </ul>
+ * <p/>
+ * In terms of collection caches, all modification actions actually just
+ * invalidate the entry(s). The call sequence here is:
+ * {@link #lock} -> {@link #evict} -> {@link #release}
+ * <p/>
+ * Note that, for an asynchronous cache, cache invalidation must be a two
+ * step process (lock->release, or lock-afterUpdate), since this is the only
+ * way to guarantee consistency with the database for a nontransactional cache
+ * implementation. For a synchronous cache, cache invalidation is a single
+ * step process (evict, or update). Hence, this interface defines a three
+ * step process, to cater for both models.
+ * <p/>
+ * Note that query result caching does not go through a concurrency strategy; they
+ * are managed directly against the underlying {@link Cache cache regions}.
+ *
+ * @deprecated As of 3.3; see <a href="package.html"/> for details.
+ */
+public interface CacheConcurrencyStrategy {
+
+ /**
+ * Attempt to retrieve an object from the cache. Mainly used in attempting
+ * to resolve entities/collections from the second level cache.
+ *
+ * @param key
+ * @param txTimestamp a timestamp prior to the transaction start time
+ * @return the cached object or <tt>null</tt>
+ * @throws CacheException
+ */
+ public Object get(Object key, long txTimestamp) throws CacheException;
+
+ /**
+ * Attempt to cache an object, after loading from the database.
+ *
+ * @param key
+ * @param value
+ * @param txTimestamp a timestamp prior to the transaction start time
+ * @param version the item version number
+ * @param versionComparator a comparator used to compare version numbers
+ * @param minimalPut indicates that the cache should avoid a put is the item is already cached
+ * @return <tt>true</tt> if the object was successfully cached
+ * @throws CacheException
+ */
+ public boolean put(
+ Object key,
+ Object value,
+ long txTimestamp,
+ Object version,
+ Comparator versionComparator,
+ boolean minimalPut)
+ throws CacheException;
+
+ /**
+ * We are going to attempt to update/delete the keyed object. This
+ * method is used by "asynchronous" concurrency strategies.
+ * <p/>
+ * The returned object must be passed back to release(), to release the
+ * lock. Concurrency strategies which do not support client-visible
+ * locks may silently return null.
+ *
+ * @param key
+ * @param version
+ * @throws CacheException
+ */
+ public SoftLock lock(Object key, Object version) throws CacheException;
+
+ /**
+ * Called after an item has become stale (before the transaction completes).
+ * This method is used by "synchronous" concurrency strategies.
+ */
+ public void evict(Object key) throws CacheException;
+
+ /**
+ * Called after an item has been updated (before the transaction completes),
+ * instead of calling evict().
+ * This method is used by "synchronous" concurrency strategies.
+ */
+ public boolean update(Object key, Object value, Object currentVersion, Object previousVersion) throws CacheException;
+
+ /**
+ * Called after an item has been inserted (before the transaction completes),
+ * instead of calling evict().
+ * This method is used by "synchronous" concurrency strategies.
+ */
+ public boolean insert(Object key, Object value, Object currentVersion) throws CacheException;
+
+
+ /**
+ * Called when we have finished the attempted update/delete (which may or
+ * may not have been successful), after transaction completion.
+ * This method is used by "asynchronous" concurrency strategies.
+ * @param key
+ * @throws CacheException
+ */
+ public void release(Object key, SoftLock lock) throws CacheException;
+ /**
+ * Called after an item has been updated (after the transaction completes),
+ * instead of calling release().
+ * This method is used by "asynchronous" concurrency strategies.
+ */
+ public boolean afterUpdate(Object key, Object value, Object version, SoftLock lock)
+ throws CacheException;
+ /**
+ * Called after an item has been inserted (after the transaction completes),
+ * instead of calling release().
+ * This method is used by "asynchronous" concurrency strategies.
+ */
+ public boolean afterInsert(Object key, Object value, Object version)
+ throws CacheException;
+
+
+ /**
+ * Evict an item from the cache immediately (without regard for transaction
+ * isolation).
+ * @param key
+ * @throws CacheException
+ */
+ public void remove(Object key) throws CacheException;
+ /**
+ * Evict all items from the cache immediately.
+ * @throws CacheException
+ */
+ public void clear() throws CacheException;
+ /**
+ * Clean up all resources.
+ */
+ public void destroy();
+ /**
+ * Set the underlying cache implementation.
+ * @param cache
+ */
+ public void setCache(Cache cache);
+
+ /**
+ * Get the cache region name
+ */
+ public String getRegionName();
+
+ /**
+ * Get the wrapped cache implementation
+ */
+ public Cache getCache();
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/CacheDataDescription.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/CacheDataDescription.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/CacheDataDescription.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,35 @@
+package org.hibernate.cache;
+
+import java.util.Comparator;
+
+/**
+ * Describes attributes regarding the type of data to be cached.
+ *
+ * @author Steve Ebersole
+ */
+public interface CacheDataDescription {
+ /**
+ * Is the data marked as being mutable?
+ *
+ * @return True if the data is mutable; false otherwise.
+ */
+ public boolean isMutable();
+
+ /**
+ * Is the data to be cached considered versioned?
+ * <p/>
+ * If true, it is illegal for {@link #getVersionComparator} to return
+ * null.
+ *
+ * @return True if the data is versioned; false otherwise.
+ */
+ public boolean isVersioned();
+
+ /**
+ * Get the comparator used to compare two different version values.
+ * <p/>
+ * May return null <b>if</b> {@link #isVersioned()} returns false.
+ * @return
+ */
+ public Comparator getVersionComparator();
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/CacheException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/CacheException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/CacheException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,23 @@
+//$Id: CacheException.java 11492 2007-05-09 01:57:11Z steve.ebersole at jboss.com $
+package org.hibernate.cache;
+
+import org.hibernate.HibernateException;
+
+/**
+ * Something went wrong in the cache
+ */
+public class CacheException extends HibernateException {
+
+ public CacheException(String s) {
+ super(s);
+ }
+
+ public CacheException(String s, Throwable e) {
+ super(s, e);
+ }
+
+ public CacheException(Throwable e) {
+ super(e);
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/CacheKey.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/CacheKey.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/CacheKey.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,72 @@
+//$Id: CacheKey.java 11499 2007-05-09 17:35:55Z steve.ebersole at jboss.com $
+package org.hibernate.cache;
+
+import java.io.Serializable;
+
+import org.hibernate.EntityMode;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.type.Type;
+
+/**
+ * Allows multiple entity classes / collection roles to be
+ * stored in the same cache region. Also allows for composite
+ * keys which do not properly implement equals()/hashCode().
+ *
+ * @author Gavin King
+ */
+public class CacheKey implements Serializable {
+ private final Serializable key;
+ private final Type type;
+ private final String entityOrRoleName;
+ private final EntityMode entityMode;
+ private final int hashCode;
+
+ /**
+ * Construct a new key for a collection or entity instance.
+ * Note that an entity name should always be the root entity
+ * name, not a subclass entity name.
+ *
+ * @param id The identifier associated with the cached data
+ * @param type The Hibernate type mapping
+ * @param entityOrRoleName The entity or collection-role name.
+ * @param entityMode The entiyt mode of the originating session
+ * @param factory The session factory for which we are caching
+ */
+ public CacheKey(
+ final Serializable id,
+ final Type type,
+ final String entityOrRoleName,
+ final EntityMode entityMode,
+ final SessionFactoryImplementor factory) {
+ this.key = id;
+ this.type = type;
+ this.entityOrRoleName = entityOrRoleName;
+ this.entityMode = entityMode;
+ hashCode = type.getHashCode( key, entityMode, factory );
+ }
+
+ //Mainly for OSCache
+ public String toString() {
+ return entityOrRoleName + '#' + key.toString();//"CacheKey#" + type.toString(key, sf);
+ }
+
+ public boolean equals(Object other) {
+ if ( !(other instanceof CacheKey) ) return false;
+ CacheKey that = (CacheKey) other;
+ return entityOrRoleName.equals( that.entityOrRoleName )
+ && type.isEqual( key, that.key, entityMode );
+ }
+
+ public int hashCode() {
+ return hashCode;
+ }
+
+ public Serializable getKey() {
+ return key;
+ }
+
+ public String getEntityOrRoleName() {
+ return entityOrRoleName;
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/CacheKey.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/CacheProvider.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/CacheProvider.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/CacheProvider.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,44 @@
+//$Id: CacheProvider.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.cache;
+
+import java.util.Properties;
+
+/**
+ * Support for pluggable caches.
+ *
+ * @author Gavin King
+ * @deprecated As of 3.3; see <a href="package.html"/> for details.
+ */
+public interface CacheProvider {
+
+ /**
+ * Configure the cache
+ *
+ * @param regionName the name of the cache region
+ * @param properties configuration settings
+ * @throws CacheException
+ */
+ public Cache buildCache(String regionName, Properties properties) throws CacheException;
+
+ /**
+ * Generate a timestamp
+ */
+ public long nextTimestamp();
+
+ /**
+ * Callback to perform any necessary initialization of the underlying cache implementation
+ * during SessionFactory construction.
+ *
+ * @param properties current configuration settings.
+ */
+ public void start(Properties properties) throws CacheException;
+
+ /**
+ * Callback to perform any necessary cleanup of the underlying cache implementation
+ * during SessionFactory.close().
+ */
+ public void stop();
+
+ public boolean isMinimalPutsEnabledByDefault();
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/CollectionRegion.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/CollectionRegion.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/CollectionRegion.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,28 @@
+package org.hibernate.cache;
+
+import org.hibernate.cache.access.CollectionRegionAccessStrategy;
+import org.hibernate.cache.access.AccessType;
+
+/**
+ * Defines the contract for a cache region which will specifically be used to
+ * store collection data.
+ * <p/>
+ * Impl note: Hibernate always deals with changes to collections which
+ * (potentially) has its data in the L2 cache by removing that collection
+ * data; in other words it never tries to update the cached state, thereby
+ * allowing it to avoid a bunch of concurrency problems.
+ *
+ * @author Steve Ebersole
+ */
+public interface CollectionRegion extends TransactionalDataRegion {
+
+ /**
+ * Build an access strategy for the requested access type.
+ *
+ * @param accessType The type of access strategy to build; never null.
+ * @return The appropriate strategy contract for accessing this region
+ * for the requested type of access.
+ * @throws CacheException Usually indicates mis-configuration.
+ */
+ public CollectionRegionAccessStrategy buildAccessStrategy(AccessType accessType) throws CacheException;
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/EntityRegion.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/EntityRegion.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/EntityRegion.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,23 @@
+package org.hibernate.cache;
+
+import org.hibernate.cache.access.EntityRegionAccessStrategy;
+import org.hibernate.cache.access.AccessType;
+
+/**
+ * Defines the contract for a cache region which will specifically be used to
+ * store entity data.
+ *
+ * @author Steve Ebersole
+ */
+public interface EntityRegion extends TransactionalDataRegion {
+
+ /**
+ * Build an access strategy for the requested access type.
+ *
+ * @param accessType The type of access strategy to build; never null.
+ * @return The appropriate strategy contract for accessing this region
+ * for the requested type of access.
+ * @throws CacheException Usually indicates mis-configuration.
+ */
+ public EntityRegionAccessStrategy buildAccessStrategy(AccessType accessType) throws CacheException;
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/FilterKey.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/FilterKey.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/FilterKey.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,70 @@
+//$Id: FilterKey.java 8754 2005-12-05 23:36:59Z steveebersole $
+package org.hibernate.cache;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.hibernate.EntityMode;
+import org.hibernate.engine.TypedValue;
+import org.hibernate.impl.FilterImpl;
+import org.hibernate.type.Type;
+
+/**
+ * Allows cached queries to be keyed by enabled filters.
+ *
+ * @author Gavin King
+ */
+public final class FilterKey implements Serializable {
+ private String filterName;
+ private Map filterParameters = new HashMap();
+
+ public FilterKey(String name, Map params, Map types, EntityMode entityMode) {
+ filterName = name;
+ Iterator iter = params.entrySet().iterator();
+ while ( iter.hasNext() ) {
+ Map.Entry me = (Map.Entry) iter.next();
+ Type type = (Type) types.get( me.getKey() );
+ filterParameters.put( me.getKey(), new TypedValue( type, me.getValue(), entityMode ) );
+ }
+ }
+
+ public int hashCode() {
+ int result = 13;
+ result = 37 * result + filterName.hashCode();
+ result = 37 * result + filterParameters.hashCode();
+ return result;
+ }
+
+ public boolean equals(Object other) {
+ if ( !(other instanceof FilterKey) ) return false;
+ FilterKey that = (FilterKey) other;
+ if ( !that.filterName.equals(filterName) ) return false;
+ if ( !that.filterParameters.equals(filterParameters) ) return false;
+ return true;
+ }
+
+ public String toString() {
+ return "FilterKey[" + filterName + filterParameters + ']';
+ }
+
+ public static Set createFilterKeys(Map enabledFilters, EntityMode entityMode) {
+ if ( enabledFilters.size()==0 ) return null;
+ Set result = new HashSet();
+ Iterator iter = enabledFilters.values().iterator();
+ while ( iter.hasNext() ) {
+ FilterImpl filter = (FilterImpl) iter.next();
+ FilterKey key = new FilterKey(
+ filter.getName(),
+ filter.getParameters(),
+ filter.getFilterDefinition().getParameterTypes(),
+ entityMode
+ );
+ result.add(key);
+ }
+ return result;
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/FilterKey.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/GeneralDataRegion.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/GeneralDataRegion.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/GeneralDataRegion.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,44 @@
+package org.hibernate.cache;
+
+/**
+ * Contract for general-purpose cache regions.
+ *
+ * @author Steve Ebersole
+ */
+public interface GeneralDataRegion extends Region {
+
+ /**
+ * Get an item from the cache.
+ *
+ * @param key The key of the item to be retrieved.
+ * @return the cached object or <tt>null</tt>
+ * @throws CacheException Indicates a problem accessing the item or region.
+ */
+ public Object get(Object key) throws CacheException;
+
+ /**
+ * Put an item into the cache.
+ *
+ * @param key The key under which to cache the item.
+ * @param value The item to cache.
+ * @throws CacheException Indicates a problem accessing the region.
+ */
+ public void put(Object key, Object value) throws CacheException;
+
+ /**
+ * Evict an item from the cache immediately (without regard for transaction
+ * isolation).
+ *
+ * @param key The key of the item to remove
+ * @throws CacheException Indicates a problem accessing the item or region.
+ */
+ public void evict(Object key) throws CacheException;
+
+ /**
+ * Evict all contents of this particular cache region (without regard for transaction
+ * isolation).
+ *
+ * @throws CacheException Indicates problem accessing the region.
+ */
+ public void evictAll() throws CacheException;
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/HashtableCache.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/HashtableCache.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/HashtableCache.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,90 @@
+//$Id: HashtableCache.java 6478 2005-04-21 07:57:19Z oneovthafew $
+package org.hibernate.cache;
+
+import java.util.Collections;
+import java.util.Hashtable;
+
+import java.util.Map;
+
+/**
+ * A lightweight implementation of the <tt>Cache</tt> interface
+ * @author Gavin King
+ */
+public class HashtableCache implements Cache {
+
+ private final Map hashtable = new Hashtable();
+ private final String regionName;
+
+ public HashtableCache(String regionName) {
+ this.regionName = regionName;
+ }
+
+ public String getRegionName() {
+ return regionName;
+ }
+
+ public Object read(Object key) throws CacheException {
+ return hashtable.get(key);
+ }
+
+ public Object get(Object key) throws CacheException {
+ return hashtable.get(key);
+ }
+
+ public void update(Object key, Object value) throws CacheException {
+ put(key, value);
+ }
+
+ public void put(Object key, Object value) throws CacheException {
+ hashtable.put(key, value);
+ }
+
+ public void remove(Object key) throws CacheException {
+ hashtable.remove(key);
+ }
+
+ public void clear() throws CacheException {
+ hashtable.clear();
+ }
+
+ public void destroy() throws CacheException {
+
+ }
+
+ public void lock(Object key) throws CacheException {
+ // local cache, so we use synchronization
+ }
+
+ public void unlock(Object key) throws CacheException {
+ // local cache, so we use synchronization
+ }
+
+ public long nextTimestamp() {
+ return Timestamper.next();
+ }
+
+ public int getTimeout() {
+ return Timestamper.ONE_MS * 60000; //ie. 60 seconds
+ }
+
+ public long getSizeInMemory() {
+ return -1;
+ }
+
+ public long getElementCountInMemory() {
+ return hashtable.size();
+ }
+
+ public long getElementCountOnDisk() {
+ return 0;
+ }
+
+ public Map toMap() {
+ return Collections.unmodifiableMap(hashtable);
+ }
+
+ public String toString() {
+ return "HashtableCache(" + regionName + ')';
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/HashtableCacheProvider.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/HashtableCacheProvider.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/HashtableCacheProvider.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,42 @@
+//$Id: HashtableCacheProvider.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate.cache;
+
+import java.util.Properties;
+
+/**
+ * A simple in-memory Hashtable-based cache impl.
+ *
+ * @author Gavin King
+ */
+public class HashtableCacheProvider implements CacheProvider {
+
+ public Cache buildCache(String regionName, Properties properties) throws CacheException {
+ return new HashtableCache( regionName );
+ }
+
+ public long nextTimestamp() {
+ return Timestamper.next();
+ }
+
+ /**
+ * Callback to perform any necessary initialization of the underlying cache implementation
+ * during SessionFactory construction.
+ *
+ * @param properties current configuration settings.
+ */
+ public void start(Properties properties) throws CacheException {
+ }
+
+ /**
+ * Callback to perform any necessary cleanup of the underlying cache implementation
+ * during SessionFactory.close().
+ */
+ public void stop() {
+ }
+
+ public boolean isMinimalPutsEnabledByDefault() {
+ return false;
+ }
+
+}
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/NoCacheProvider.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/NoCacheProvider.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/NoCacheProvider.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,58 @@
+// $Id: NoCacheProvider.java 6433 2005-04-15 18:20:03Z steveebersole $
+package org.hibernate.cache;
+
+import java.util.Properties;
+
+/**
+ * Implementation of NoCacheProvider.
+ *
+ * @author Steve Ebersole
+ */
+public class NoCacheProvider implements CacheProvider {
+ /**
+ * Configure the cache
+ *
+ * @param regionName the name of the cache region
+ * @param properties configuration settings
+ *
+ * @throws CacheException
+ */
+ public Cache buildCache(String regionName, Properties properties) throws CacheException {
+ throw new NoCachingEnabledException();
+ }
+
+ /**
+ * Generate a timestamp
+ */
+ public long nextTimestamp() {
+ // This, is used by SessionFactoryImpl to hand to the generated SessionImpl;
+ // was the only reason I could see that we cannot just use null as
+ // Settings.cacheProvider
+ return System.currentTimeMillis() / 100;
+ }
+
+ /**
+ * Callback to perform any necessary initialization of the underlying cache implementation during SessionFactory
+ * construction.
+ *
+ * @param properties current configuration settings.
+ */
+ public void start(Properties properties) throws CacheException {
+ // this is called by SessionFactory irregardless; we just disregard here;
+ // could also add a check to SessionFactory to only conditionally call start
+ }
+
+ /**
+ * Callback to perform any necessary cleanup of the underlying cache implementation during SessionFactory.close().
+ */
+ public void stop() {
+ // this is called by SessionFactory irregardless; we just disregard here;
+ // could also add a check to SessionFactory to only conditionally call stop
+ }
+
+ public boolean isMinimalPutsEnabledByDefault() {
+ // this is called from SettingsFactory irregardless; trivial to simply disregard
+ return false;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/NoCachingEnabledException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/NoCachingEnabledException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/NoCachingEnabledException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,20 @@
+// $Id: NoCachingEnabledException.java 6433 2005-04-15 18:20:03Z steveebersole $
+package org.hibernate.cache;
+
+import org.hibernate.cfg.Environment;
+
+/**
+ * Implementation of NoCachingEnabledException.
+ *
+ * @author Steve Ebersole
+ */
+public class NoCachingEnabledException extends CacheException {
+ private static final String MSG =
+ "Second-level cache is not enabled for usage [" +
+ Environment.USE_SECOND_LEVEL_CACHE +
+ " | " + Environment.USE_QUERY_CACHE + "]";
+
+ public NoCachingEnabledException() {
+ super( MSG );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/NonstrictReadWriteCache.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/NonstrictReadWriteCache.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/NonstrictReadWriteCache.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,172 @@
+//$Id: NonstrictReadWriteCache.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.cache;
+
+import java.util.Comparator;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.hibernate.cache.access.SoftLock;
+
+/**
+ * Caches data that is sometimes updated without ever locking the cache.
+ * If concurrent access to an item is possible, this concurrency strategy
+ * makes no guarantee that the item returned from the cache is the latest
+ * version available in the database. Configure your cache timeout accordingly!
+ * This is an "asynchronous" concurrency strategy.
+ *
+ * @author Gavin King
+ * @see ReadWriteCache for a much stricter algorithm
+ */
+public class NonstrictReadWriteCache implements CacheConcurrencyStrategy {
+
+ private Cache cache;
+
+ private static final Log log = LogFactory.getLog( NonstrictReadWriteCache.class );
+
+ public NonstrictReadWriteCache() {
+ }
+
+ public void setCache(Cache cache) {
+ this.cache = cache;
+ }
+
+ public Cache getCache() {
+ return cache;
+ }
+
+ /**
+ * Get the most recent version, if available.
+ */
+ public Object get(Object key, long txTimestamp) throws CacheException {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "Cache lookup: " + key );
+ }
+
+ Object result = cache.get( key );
+ if ( result != null ) {
+ log.debug( "Cache hit" );
+ }
+ else {
+ log.debug( "Cache miss" );
+ }
+ return result;
+ }
+
+ /**
+ * Add an item to the cache.
+ */
+ public boolean put(
+ Object key,
+ Object value,
+ long txTimestamp,
+ Object version,
+ Comparator versionComparator,
+ boolean minimalPut) throws CacheException {
+ if ( minimalPut && cache.get( key ) != null ) {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "item already cached: " + key );
+ }
+ return false;
+ }
+ if ( log.isDebugEnabled() ) {
+ log.debug( "Caching: " + key );
+ }
+
+ cache.put( key, value );
+ return true;
+
+ }
+
+ /**
+ * Do nothing.
+ *
+ * @return null, no lock
+ */
+ public SoftLock lock(Object key, Object version) throws CacheException {
+ return null;
+ }
+
+ public void remove(Object key) throws CacheException {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "Removing: " + key );
+ }
+ cache.remove( key );
+ }
+
+ public void clear() throws CacheException {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "Clearing" );
+ }
+ cache.clear();
+ }
+
+ public void destroy() {
+ try {
+ cache.destroy();
+ }
+ catch ( Exception e ) {
+ log.warn( "could not destroy cache", e );
+ }
+ }
+
+ /**
+ * Invalidate the item
+ */
+ public void evict(Object key) throws CacheException {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "Invalidating: " + key );
+ }
+
+ cache.remove( key );
+ }
+
+ /**
+ * Invalidate the item
+ */
+ public boolean insert(Object key, Object value, Object currentVersion) {
+ return false;
+ }
+
+ /**
+ * Do nothing.
+ */
+ public boolean update(Object key, Object value, Object currentVersion, Object previousVersion) {
+ evict( key );
+ return false;
+ }
+
+ /**
+ * Invalidate the item (again, for safety).
+ */
+ public void release(Object key, SoftLock lock) throws CacheException {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "Invalidating (again): " + key );
+ }
+
+ cache.remove( key );
+ }
+
+ /**
+ * Invalidate the item (again, for safety).
+ */
+ public boolean afterUpdate(Object key, Object value, Object version, SoftLock lock) throws CacheException {
+ release( key, lock );
+ return false;
+ }
+
+ /**
+ * Do nothing.
+ */
+ public boolean afterInsert(Object key, Object value, Object version) throws CacheException {
+ return false;
+ }
+
+ public String getRegionName() {
+ return cache.getRegionName();
+ }
+
+ public String toString() {
+ return cache + "(nonstrict-read-write)";
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/OptimisticCache.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/OptimisticCache.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/OptimisticCache.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,64 @@
+package org.hibernate.cache;
+
+/**
+ * A contract for transactional cache implementations which support
+ * optimistic locking of items within the cache.
+ * <p/>
+ * The optimisitic locking capabilities are only utilized for
+ * the entity cache regions.
+ * <p/>
+ * Unlike the methods on the {@link Cache} interface, all the methods
+ * here will only ever be called from access scenarios where versioned
+ * data is actually a possiblity (i.e., entity data). Be sure to consult
+ * with {@link OptimisticCacheSource#isVersioned()} to determine whether
+ * versioning is actually in effect.
+ *
+ * @author Steve Ebersole
+ */
+public interface OptimisticCache extends Cache {
+ /**
+ * Indicates the "source" of the cached data. Currently this will
+ * only ever represent an {@link org.hibernate.persister.entity.EntityPersister}.
+ * <p/>
+ * Made available to the cache so that it can access certain information
+ * about versioning strategy.
+ *
+ * @param source The source.
+ */
+ public void setSource(OptimisticCacheSource source);
+
+ /**
+ * Called during {@link CacheConcurrencyStrategy#insert} processing for
+ * transactional strategies. Indicates we have just performed an insert
+ * into the DB and now need to cache that entity's data.
+ *
+ * @param key The cache key.
+ * @param value The data to be cached.
+ * @param currentVersion The entity's version; or null if not versioned.
+ */
+ public void writeInsert(Object key, Object value, Object currentVersion);
+
+ /**
+ * Called during {@link CacheConcurrencyStrategy#update} processing for
+ * transactional strategies. Indicates we have just performed an update
+ * against the DB and now need to cache the updated state.
+ *
+ * @param key The cache key.
+ * @param value The data to be cached.
+ * @param currentVersion The entity's current version
+ * @param previousVersion The entity's previous version (before the update);
+ * or null if not versioned.
+ */
+ public void writeUpdate(Object key, Object value, Object currentVersion, Object previousVersion);
+
+ /**
+ * Called during {@link CacheConcurrencyStrategy#put} processing for
+ * transactional strategies. Indicates we have just loaded an entity's
+ * state from the database and need it cached.
+ *
+ * @param key The cache key.
+ * @param value The data to be cached.
+ * @param currentVersion The entity's version; or null if not versioned.
+ */
+ public void writeLoad(Object key, Object value, Object currentVersion);
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/OptimisticCacheSource.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/OptimisticCacheSource.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/OptimisticCacheSource.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,32 @@
+package org.hibernate.cache;
+
+import java.util.Comparator;
+
+/**
+ * Contract for sources of optimistically lockable data sent to the second level
+ * cache.
+ * <p/>
+ * Note currently {@link org.hibernate.persister.entity.EntityPersister}s are
+ * the only viable source.
+ *
+ * @author Steve Ebersole
+ */
+public interface OptimisticCacheSource {
+ /**
+ * Is the data to be cached considered versioned?
+ * <p/>
+ * If true, it is illegal for {@link #getVersionComparator} to return
+ * null.
+ *
+ * @return True if the data is versioned; false otherwise.
+ */
+ public boolean isVersioned();
+
+ /**
+ * Get the comparator used to compare two different version values.
+ * <p/>
+ * May return null <b>if</b> {@link #isVersioned()} returns false.
+ * @return
+ */
+ public Comparator getVersionComparator();
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/QueryCache.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/QueryCache.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/QueryCache.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,31 @@
+//$Id: QueryCache.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.cache;
+
+import java.util.List;
+import java.util.Set;
+
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.type.Type;
+
+/**
+ * Defines the contract for caches capable of storing query results. These
+ * caches should only concern themselves with storing the matching result ids.
+ * The transactional semantics are necessarily less strict than the semantics
+ * of an item cache.
+ *
+ * @author Gavin King
+ */
+public interface QueryCache {
+
+ public void clear() throws CacheException;
+
+ public boolean put(QueryKey key, Type[] returnTypes, List result, boolean isNaturalKeyLookup, SessionImplementor session) throws HibernateException;
+
+ public List get(QueryKey key, Type[] returnTypes, boolean isNaturalKeyLookup, Set spaces, SessionImplementor session) throws HibernateException;
+
+ public void destroy();
+
+ public QueryResultsRegion getRegion();
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/QueryCacheFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/QueryCacheFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/QueryCacheFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,24 @@
+// $Id: QueryCacheFactory.java 4690 2004-10-26 09:35:46Z oneovthafew $
+package org.hibernate.cache;
+
+import org.hibernate.HibernateException;
+import org.hibernate.cfg.Settings;
+
+import java.util.Properties;
+
+/**
+ * Defines a factory for query cache instances. These factories are responsible for
+ * creating individual QueryCache instances.
+ *
+ * @author Steve Ebersole
+ */
+public interface QueryCacheFactory {
+
+ public QueryCache getQueryCache(
+ String regionName,
+ UpdateTimestampsCache updateTimestampsCache,
+ Settings settings,
+ Properties props)
+ throws HibernateException;
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/QueryKey.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/QueryKey.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/QueryKey.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,117 @@
+//$Id: QueryKey.java 9636 2006-03-16 14:14:48Z max.andersen at jboss.com $
+package org.hibernate.cache;
+
+import java.io.Serializable;
+import java.util.Map;
+import java.util.Set;
+
+import org.hibernate.EntityMode;
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.RowSelection;
+import org.hibernate.transform.ResultTransformer;
+import org.hibernate.type.Type;
+import org.hibernate.util.EqualsHelper;
+
+/**
+ * A key that identifies a particular query with bound parameter values
+ * @author Gavin King
+ */
+public class QueryKey implements Serializable {
+ private final String sqlQueryString;
+ private final Type[] types;
+ private final Object[] values;
+ private final Integer firstRow;
+ private final Integer maxRows;
+ private final Map namedParameters;
+ private final EntityMode entityMode;
+ private final Set filters;
+ private final int hashCode;
+
+ // the user provided resulttransformer, not the one used with "select new". Here to avoid mangling transformed/non-transformed results.
+ private final ResultTransformer customTransformer;
+
+ public QueryKey(String queryString, QueryParameters queryParameters, Set filters, EntityMode entityMode) {
+ this.sqlQueryString = queryString;
+ this.types = queryParameters.getPositionalParameterTypes();
+ this.values = queryParameters.getPositionalParameterValues();
+ RowSelection selection = queryParameters.getRowSelection();
+ if (selection!=null) {
+ firstRow = selection.getFirstRow();
+ maxRows = selection.getMaxRows();
+ }
+ else {
+ firstRow = null;
+ maxRows = null;
+ }
+ this.namedParameters = queryParameters.getNamedParameters();
+ this.entityMode = entityMode;
+ this.filters = filters;
+ this.customTransformer = queryParameters.getResultTransformer();
+ this.hashCode = getHashCode();
+ }
+
+ public boolean equals(Object other) {
+ QueryKey that = (QueryKey) other;
+ if ( !sqlQueryString.equals(that.sqlQueryString) ) return false;
+ if ( !EqualsHelper.equals(firstRow, that.firstRow) || !EqualsHelper.equals(maxRows, that.maxRows) ) return false;
+ if ( !EqualsHelper.equals(customTransformer, that.customTransformer) ) return false;
+ if (types==null) {
+ if (that.types!=null) return false;
+ }
+ else {
+ if (that.types==null) return false;
+ if ( types.length!=that.types.length ) return false;
+ for ( int i=0; i<types.length; i++ ) {
+ if ( types[i].getReturnedClass() != that.types[i].getReturnedClass() ) return false;
+ if ( !types[i].isEqual( values[i], that.values[i], entityMode ) ) return false;
+ }
+ }
+ if ( !EqualsHelper.equals(filters, that.filters) ) return false;
+ if ( !EqualsHelper.equals(namedParameters, that.namedParameters) ) return false;
+ return true;
+ }
+
+ public int hashCode() {
+ return hashCode;
+ }
+
+ private int getHashCode() {
+ int result = 13;
+ result = 37 * result + ( firstRow==null ? 0 : firstRow.hashCode() );
+ result = 37 * result + ( maxRows==null ? 0 : maxRows.hashCode() );
+ for ( int i=0; i<values.length; i++ ) {
+ result = 37 * result + ( values[i]==null ? 0 : types[i].getHashCode( values[i], entityMode ) );
+ }
+ result = 37 * result + ( namedParameters==null ? 0 : namedParameters.hashCode() );
+ result = 37 * result + ( filters==null ? 0 : filters.hashCode() );
+ result = 37 * result + ( customTransformer==null ? 0 : customTransformer.hashCode() );
+ result = 37 * result + sqlQueryString.hashCode();
+ return result;
+ }
+
+ public String toString() {
+ StringBuffer buf = new StringBuffer()
+ .append("sql: ")
+ .append(sqlQueryString);
+ if (values!=null) {
+ buf.append("; parameters: ");
+ for (int i=0; i<values.length; i++) {
+ buf.append( values[i] )
+ .append(", ");
+ }
+ }
+ if (namedParameters!=null) {
+ buf.append("; named parameters: ")
+ .append(namedParameters);
+ }
+ if (filters!=null) {
+ buf.append("; filters: ")
+ .append(filters);
+ }
+ if (firstRow!=null) buf.append("; first row: ").append(firstRow);
+ if (maxRows!=null) buf.append("; max rows: ").append(maxRows);
+ if (customTransformer!=null) buf.append("; transformer: ").append(customTransformer);
+ return buf.toString();
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/QueryResultsRegion.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/QueryResultsRegion.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/QueryResultsRegion.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,10 @@
+package org.hibernate.cache;
+
+/**
+ * Defines the contract for a cache region which will specifically be used to
+ * store query results.
+ *
+ * @author Steve Ebersole
+ */
+public interface QueryResultsRegion extends GeneralDataRegion {
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/ReadOnlyCache.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/ReadOnlyCache.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/ReadOnlyCache.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,139 @@
+//$Id: ReadOnlyCache.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.cache;
+
+import java.util.Comparator;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.hibernate.cache.access.SoftLock;
+
+/**
+ * Caches data that is never updated.
+ * @see CacheConcurrencyStrategy
+ */
+public class ReadOnlyCache implements CacheConcurrencyStrategy {
+
+ private Cache cache;
+ private static final Log log = LogFactory.getLog(ReadOnlyCache.class);
+
+ public ReadOnlyCache() {}
+
+ public void setCache(Cache cache) {
+ this.cache=cache;
+ }
+
+ public Cache getCache() {
+ return cache;
+ }
+
+ public String getRegionName() {
+ return cache.getRegionName();
+ }
+
+ public synchronized Object get(Object key, long timestamp) throws CacheException {
+ Object result = cache.get(key);
+ if ( result!=null && log.isDebugEnabled() ) log.debug("Cache hit: " + key);
+ return result;
+ }
+
+ /**
+ * Unsupported!
+ */
+ public SoftLock lock(Object key, Object version) {
+ log.error("Application attempted to edit read only item: " + key);
+ throw new UnsupportedOperationException("Can't write to a readonly object");
+ }
+
+ public synchronized boolean put(
+ Object key,
+ Object value,
+ long timestamp,
+ Object version,
+ Comparator versionComparator,
+ boolean minimalPut)
+ throws CacheException {
+ if ( minimalPut && cache.get(key)!=null ) {
+ if ( log.isDebugEnabled() ) log.debug("item already cached: " + key);
+ return false;
+ }
+ if ( log.isDebugEnabled() ) log.debug("Caching: " + key);
+ cache.put(key, value);
+ return true;
+ }
+
+ /**
+ * Unsupported!
+ */
+ public void release(Object key, SoftLock lock) {
+ log.error("Application attempted to edit read only item: " + key);
+ //throw new UnsupportedOperationException("Can't write to a readonly object");
+ }
+
+ public void clear() throws CacheException {
+ cache.clear();
+ }
+
+ public void remove(Object key) throws CacheException {
+ cache.remove(key);
+ }
+
+ public void destroy() {
+ try {
+ cache.destroy();
+ }
+ catch (Exception e) {
+ log.warn("could not destroy cache", e);
+ }
+ }
+
+ /**
+ * Unsupported!
+ */
+ public boolean afterUpdate(Object key, Object value, Object version, SoftLock lock) throws CacheException {
+ log.error("Application attempted to edit read only item: " + key);
+ throw new UnsupportedOperationException("Can't write to a readonly object");
+ }
+
+ /**
+ * Do nothing.
+ */
+ public boolean afterInsert(Object key, Object value, Object version) throws CacheException {
+ if ( log.isDebugEnabled() ) log.debug("Caching after insert: " + key);
+ cache.update(key, value);
+ return true;
+ }
+
+ /**
+ * Do nothing.
+ */
+ public void evict(Object key) throws CacheException {
+ // noop
+ }
+
+ /**
+ * Do nothing.
+ */
+ public boolean insert(Object key, Object value, Object currentVersion) {
+ return false;
+ }
+
+ /**
+ * Unsupported!
+ */
+ public boolean update(Object key, Object value, Object currentVersion, Object previousVersion) {
+ log.error("Application attempted to edit read only item: " + key);
+ throw new UnsupportedOperationException("Can't write to a readonly object");
+ }
+
+ public String toString() {
+ return cache + "(read-only)";
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/ReadWriteCache.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/ReadWriteCache.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/ReadWriteCache.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,508 @@
+//$Id: ReadWriteCache.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.cache;
+
+import java.io.Serializable;
+import java.util.Comparator;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.hibernate.cache.access.SoftLock;
+
+/**
+ * Caches data that is sometimes updated while maintaining the semantics of
+ * "read committed" isolation level. If the database is set to "repeatable
+ * read", this concurrency strategy <em>almost</em> maintains the semantics.
+ * Repeatable read isolation is compromised in the case of concurrent writes.
+ * This is an "asynchronous" concurrency strategy.<br>
+ * <br>
+ * If this strategy is used in a cluster, the underlying cache implementation
+ * must support distributed hard locks (which are held only momentarily). This
+ * strategy also assumes that the underlying cache implementation does not do
+ * asynchronous replication and that state has been fully replicated as soon
+ * as the lock is released.
+ *
+ * @see NonstrictReadWriteCache for a faster algorithm
+ * @see CacheConcurrencyStrategy
+ */
+public class ReadWriteCache implements CacheConcurrencyStrategy {
+
+ private static final Log log = LogFactory.getLog(ReadWriteCache.class);
+
+ private Cache cache;
+ private int nextLockId;
+
+ public ReadWriteCache() {}
+
+ public void setCache(Cache cache) {
+ this.cache=cache;
+ }
+
+ public Cache getCache() {
+ return cache;
+ }
+
+ public String getRegionName() {
+ return cache.getRegionName();
+ }
+
+ /**
+ * Generate an id for a new lock. Uniqueness per cache instance is very
+ * desirable but not absolutely critical. Must be called from one of the
+ * synchronized methods of this class.
+ */
+ private int nextLockId() {
+ if (nextLockId==Integer.MAX_VALUE) nextLockId = Integer.MIN_VALUE;
+ return nextLockId++;
+ }
+
+ /**
+ * Do not return an item whose timestamp is later than the current
+ * transaction timestamp. (Otherwise we might compromise repeatable
+ * read unnecessarily.) Do not return an item which is soft-locked.
+ * Always go straight to the database instead.<br>
+ * <br>
+ * Note that since reading an item from that cache does not actually
+ * go to the database, it is possible to see a kind of phantom read
+ * due to the underlying row being updated after we have read it
+ * from the cache. This would not be possible in a lock-based
+ * implementation of repeatable read isolation. It is also possible
+ * to overwrite changes made and committed by another transaction
+ * after the current transaction read the item from the cache. This
+ * problem would be caught by the update-time version-checking, if
+ * the data is versioned or timestamped.
+ */
+ public synchronized Object get(Object key, long txTimestamp) throws CacheException {
+
+ if ( log.isTraceEnabled() ) log.trace("Cache lookup: " + key);
+
+ /*try {
+ cache.lock(key);*/
+
+ Lockable lockable = (Lockable) cache.get(key);
+
+ boolean gettable = lockable!=null && lockable.isGettable(txTimestamp);
+
+ if (gettable) {
+ if ( log.isTraceEnabled() ) log.trace("Cache hit: " + key);
+ return ( (Item) lockable ).getValue();
+ }
+ else {
+ if ( log.isTraceEnabled() ) {
+ if (lockable==null) {
+ log.trace("Cache miss: " + key);
+ }
+ else {
+ log.trace("Cached item was locked: " + key);
+ }
+ }
+ return null;
+ }
+ /*}
+ finally {
+ cache.unlock(key);
+ }*/
+ }
+
+ /**
+ * Stop any other transactions reading or writing this item to/from
+ * the cache. Send them straight to the database instead. (The lock
+ * does time out eventually.) This implementation tracks concurrent
+ * locks of transactions which simultaneously attempt to write to an
+ * item.
+ */
+ public synchronized SoftLock lock(Object key, Object version) throws CacheException {
+ if ( log.isTraceEnabled() ) log.trace("Invalidating: " + key);
+
+ try {
+ cache.lock(key);
+
+ Lockable lockable = (Lockable) cache.get(key);
+ long timeout = cache.nextTimestamp() + cache.getTimeout();
+ final Lock lock = (lockable==null) ?
+ new Lock( timeout, nextLockId(), version ) :
+ lockable.lock( timeout, nextLockId() );
+ cache.update(key, lock);
+ return lock;
+ }
+ finally {
+ cache.unlock(key);
+ }
+
+ }
+
+ /**
+ * Do not add an item to the cache unless the current transaction
+ * timestamp is later than the timestamp at which the item was
+ * invalidated. (Otherwise, a stale item might be re-added if the
+ * database is operating in repeatable read isolation mode.)
+ * For versioned data, don't add the item unless it is the later
+ * version.
+ */
+ public synchronized boolean put(
+ Object key,
+ Object value,
+ long txTimestamp,
+ Object version,
+ Comparator versionComparator,
+ boolean minimalPut)
+ throws CacheException {
+ if ( log.isTraceEnabled() ) log.trace("Caching: " + key);
+
+ try {
+ cache.lock(key);
+
+ Lockable lockable = (Lockable) cache.get(key);
+
+ boolean puttable = lockable==null ||
+ lockable.isPuttable(txTimestamp, version, versionComparator);
+
+ if (puttable) {
+ cache.put( key, new Item( value, version, cache.nextTimestamp() ) );
+ if ( log.isTraceEnabled() ) log.trace("Cached: " + key);
+ return true;
+ }
+ else {
+ if ( log.isTraceEnabled() ) {
+ if ( lockable.isLock() ) {
+ log.trace("Item was locked: " + key);
+ }
+ else {
+ log.trace("Item was already cached: " + key);
+ }
+ }
+ return false;
+ }
+ }
+ finally {
+ cache.unlock(key);
+ }
+ }
+
+ /**
+ * decrement a lock and put it back in the cache
+ */
+ private void decrementLock(Object key, Lock lock) throws CacheException {
+ //decrement the lock
+ lock.unlock( cache.nextTimestamp() );
+ cache.update(key, lock);
+ }
+
+ /**
+ * Release the soft lock on the item. Other transactions may now
+ * re-cache the item (assuming that no other transaction holds a
+ * simultaneous lock).
+ */
+ public synchronized void release(Object key, SoftLock clientLock) throws CacheException {
+ if ( log.isTraceEnabled() ) log.trace("Releasing: " + key);
+
+ try {
+ cache.lock(key);
+
+ Lockable lockable = (Lockable) cache.get(key);
+ if ( isUnlockable(clientLock, lockable) ) {
+ decrementLock(key, (Lock) lockable);
+ }
+ else {
+ handleLockExpiry(key);
+ }
+ }
+ finally {
+ cache.unlock(key);
+ }
+ }
+
+ void handleLockExpiry(Object key) throws CacheException {
+ log.warn("An item was expired by the cache while it was locked (increase your cache timeout): " + key);
+ long ts = cache.nextTimestamp() + cache.getTimeout();
+ // create new lock that times out immediately
+ Lock lock = new Lock( ts, nextLockId(), null );
+ lock.unlock(ts);
+ cache.update(key, lock);
+ }
+
+ public void clear() throws CacheException {
+ cache.clear();
+ }
+
+ public void remove(Object key) throws CacheException {
+ cache.remove(key);
+ }
+
+ public void destroy() {
+ try {
+ cache.destroy();
+ }
+ catch (Exception e) {
+ log.warn("could not destroy cache", e);
+ }
+ }
+
+ /**
+ * Re-cache the updated state, if and only if there there are
+ * no other concurrent soft locks. Release our lock.
+ */
+ public synchronized boolean afterUpdate(Object key, Object value, Object version, SoftLock clientLock)
+ throws CacheException {
+
+ if ( log.isTraceEnabled() ) log.trace("Updating: " + key);
+
+ try {
+ cache.lock(key);
+
+ Lockable lockable = (Lockable) cache.get(key);
+ if ( isUnlockable(clientLock, lockable) ) {
+ Lock lock = (Lock) lockable;
+ if ( lock.wasLockedConcurrently() ) {
+ // just decrement the lock, don't recache
+ // (we don't know which transaction won)
+ decrementLock(key, lock);
+ return false;
+ }
+ else {
+ //recache the updated state
+ cache.update( key, new Item( value, version, cache.nextTimestamp() ) );
+ if ( log.isTraceEnabled() ) log.trace("Updated: " + key);
+ return true;
+ }
+ }
+ else {
+ handleLockExpiry(key);
+ return false;
+ }
+
+ }
+ finally {
+ cache.unlock(key);
+ }
+ }
+
+ /**
+ * Add the new item to the cache, checking that no other transaction has
+ * accessed the item.
+ */
+ public synchronized boolean afterInsert(Object key, Object value, Object version)
+ throws CacheException {
+
+ if ( log.isTraceEnabled() ) log.trace("Inserting: " + key);
+ try {
+ cache.lock(key);
+
+ Lockable lockable = (Lockable) cache.get(key);
+ if (lockable==null) {
+ cache.update( key, new Item( value, version, cache.nextTimestamp() ) );
+ if ( log.isTraceEnabled() ) log.trace("Inserted: " + key);
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+ finally {
+ cache.unlock(key);
+ }
+ }
+
+ /**
+ * Do nothing.
+ */
+ public void evict(Object key) throws CacheException {
+ // noop
+ }
+
+ /**
+ * Do nothing.
+ */
+ public boolean insert(Object key, Object value, Object currentVersion) {
+ return false;
+ }
+
+ /**
+ * Do nothing.
+ */
+ public boolean update(Object key, Object value, Object currentVersion, Object previousVersion) {
+ return false;
+ }
+
+ /**
+ * Is the client's lock commensurate with the item in the cache?
+ * If it is not, we know that the cache expired the original
+ * lock.
+ */
+ private boolean isUnlockable(SoftLock clientLock, Lockable myLock)
+ throws CacheException {
+ //null clientLock is remotely possible but will never happen in practice
+ return myLock!=null &&
+ myLock.isLock() &&
+ clientLock!=null &&
+ ( (Lock) clientLock ).getId()==( (Lock) myLock ).getId();
+ }
+
+ public static interface Lockable {
+ public Lock lock(long timeout, int id);
+ public boolean isLock();
+ public boolean isGettable(long txTimestamp);
+ public boolean isPuttable(long txTimestamp, Object newVersion, Comparator comparator);
+ }
+
+ /**
+ * An item of cached data, timestamped with the time it was cached,.
+ * @see ReadWriteCache
+ */
+ public static final class Item implements Serializable, Lockable {
+
+ private final long freshTimestamp;
+ private final Object value;
+ private final Object version;
+
+ public Item(Object value, Object version, long currentTimestamp) {
+ this.value = value;
+ this.version = version;
+ freshTimestamp = currentTimestamp;
+ }
+ /**
+ * The timestamp on the cached data
+ */
+ public long getFreshTimestamp() {
+ return freshTimestamp;
+ }
+ /**
+ * The actual cached data
+ */
+ public Object getValue() {
+ return value;
+ }
+
+ /**
+ * Lock the item
+ */
+ public Lock lock(long timeout, int id) {
+ return new Lock(timeout, id, version);
+ }
+ /**
+ * Not a lock!
+ */
+ public boolean isLock() {
+ return false;
+ }
+ /**
+ * Is this item visible to the timestamped
+ * transaction?
+ */
+ public boolean isGettable(long txTimestamp) {
+ return freshTimestamp < txTimestamp;
+ }
+
+ /**
+ * Don't overwite already cached items
+ */
+ public boolean isPuttable(long txTimestamp, Object newVersion, Comparator comparator) {
+ // we really could refresh the item if it
+ // is not a lock, but it might be slower
+ //return freshTimestamp < txTimestamp
+ return version!=null && comparator.compare(version, newVersion) < 0;
+ }
+
+ public String toString() {
+ return "Item{version=" + version +
+ ",freshTimestamp=" + freshTimestamp;
+ }
+ }
+
+ /**
+ * A soft lock which supports concurrent locking,
+ * timestamped with the time it was released
+ * @author Gavin King
+ */
+ public static final class Lock implements Serializable, Lockable, SoftLock {
+ private long unlockTimestamp = -1;
+ private int multiplicity = 1;
+ private boolean concurrentLock = false;
+ private long timeout;
+ private final int id;
+ private final Object version;
+
+ public Lock(long timeout, int id, Object version) {
+ this.timeout = timeout;
+ this.id = id;
+ this.version = version;
+ }
+
+ public long getUnlockTimestamp() {
+ return unlockTimestamp;
+ }
+ /**
+ * Increment the lock, setting the
+ * new lock timeout
+ */
+ public Lock lock(long timeout, int id) {
+ concurrentLock = true;
+ multiplicity++;
+ this.timeout = timeout;
+ return this;
+ }
+ /**
+ * Decrement the lock, setting the unlock
+ * timestamp if now unlocked
+ * @param currentTimestamp
+ */
+ public void unlock(long currentTimestamp) {
+ if ( --multiplicity == 0 ) {
+ unlockTimestamp = currentTimestamp;
+ }
+ }
+
+ /**
+ * Can the timestamped transaction re-cache this
+ * locked item now?
+ */
+ public boolean isPuttable(long txTimestamp, Object newVersion, Comparator comparator) {
+ if (timeout < txTimestamp) return true;
+ if (multiplicity>0) return false;
+ return version==null ?
+ unlockTimestamp < txTimestamp :
+ comparator.compare(version, newVersion) < 0; //by requiring <, we rely on lock timeout in the case of an unsuccessful update!
+ }
+
+ /**
+ * Was this lock held concurrently by multiple
+ * transactions?
+ */
+ public boolean wasLockedConcurrently() {
+ return concurrentLock;
+ }
+ /**
+ * Yes, this is a lock
+ */
+ public boolean isLock() {
+ return true;
+ }
+ /**
+ * locks are not returned to the client!
+ */
+ public boolean isGettable(long txTimestamp) {
+ return false;
+ }
+
+ public int getId() { return id; }
+
+ public String toString() {
+ return "Lock{id=" + id +
+ ",version=" + version +
+ ",multiplicity=" + multiplicity +
+ ",unlockTimestamp=" + unlockTimestamp;
+ }
+
+ }
+
+ public String toString() {
+ return cache + "(read-write)";
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/Region.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/Region.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/Region.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,62 @@
+package org.hibernate.cache;
+
+import java.util.Map;
+
+/**
+ * Defines a contract for accessing a particular named region within the
+ * underlying cache implementation.
+ *
+ * @author Steve Ebersole
+ */
+public interface Region {
+ /**
+ * Retrieve the name of this region.
+ *
+ * @return The region name
+ */
+ public String getName();
+
+ /**
+ * The "end state" contract of the region's lifecycle. Called
+ * during {@link org.hibernate.SessionFactory#close()} to give
+ * the region a chance to cleanup.
+ *
+ * @throws CacheException Indicates problem shutting down
+ */
+ public void destroy() throws CacheException;
+
+ /**
+ * The number of bytes is this cache region currently consuming in memory.
+ *
+ * @return The number of bytes consumed by this region; -1 if unknown or
+ * unsupported.
+ */
+ public long getSizeInMemory();
+
+ /**
+ * The count of entries currently contained in the regions in-memory store.
+ *
+ * @return The count of entries in memory; -1 if unknown or unsupported.
+ */
+ public long getElementCountInMemory();
+
+ /**
+ * The count of entries currently contained in the regions disk store.
+ *
+ * @return The count of entries on disk; -1 if unknown or unsupported.
+ */
+ public long getElementCountOnDisk();
+
+ /**
+ * Get the contents of this region as a map.
+ * <p/>
+ * Implementors which do not support this notion
+ * should simply return an empty map.
+ *
+ * @return The content map.
+ */
+ public Map toMap();
+
+ public long nextTimestamp();
+ public int getTimeout();
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/RegionFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/RegionFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/RegionFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,106 @@
+package org.hibernate.cache;
+
+import java.util.Properties;
+
+import org.hibernate.cfg.Settings;
+
+/**
+ * Contract for building second level cache regions.
+ * <p/>
+ * Implementors should define a constructor in one of two forms:<ul>
+ * <li>MyRegionFactoryImpl({@link java.util.Properties})</li>
+ * <li>MyRegionFactoryImpl()</li>
+ * </ul>
+ * Use the first when we need to read config properties prior to
+ * {@link #start} being called. For an example, have a look at
+ * {@link org.hibernate.cache.impl.bridge.RegionFactoryCacheProviderBridge}
+ * where we need the properties in order to determine which legacy
+ * {@link CacheProvider} to use so that we can answer the
+ * {@link #isMinimalPutsEnabledByDefault()} question for the
+ * {@link org.hibernate.cfg.SettingsFactory}.
+ *
+ * @author Steve Ebersole
+ */
+public interface RegionFactory {
+
+ /**
+ * Lifecycle callback to perform any necessary initialization of the
+ * underlying cache implementation(s). Called exactly once during the
+ * construction of a {@link org.hibernate.impl.SessionFactoryImpl}.
+ *
+ * @param settings The settings in effect.
+ * @param properties The defined cfg properties
+ * @throws CacheException Indicates problems starting the L2 cache impl;
+ * considered as a sign to stop {@link org.hibernate.SessionFactory}
+ * building.
+ */
+ public void start(Settings settings, Properties properties) throws CacheException;
+
+ /**
+ * Lifecycle callback to perform any necessary cleanup of the underlying
+ * cache implementation(s). Called exactly once during
+ * {@link org.hibernate.SessionFactory#close}.
+ */
+ public void stop();
+
+ /**
+ * By default should we perform "minimal puts" when using this second
+ * level cache implementation?
+ *
+ * @return True if "minimal puts" should be performed by default; false
+ * otherwise.
+ */
+ public boolean isMinimalPutsEnabledByDefault();
+
+ /**
+ * Generate a timestamp.
+ * <p/>
+ * This is generally used for cache content locking/unlocking purposes
+ * depending upon the access-strategy being used.
+ *
+ * @return The generated timestamp.
+ */
+ public long nextTimestamp();
+
+ /**
+ * Build a cache region specialized for storing entity data.
+ *
+ * @param regionName The name of the region.
+ * @param properties Configuration properties.
+ * @param metadata Information regarding the type of data to be cached
+ * @return The built region
+ * @throws CacheException Indicates problems building the region.
+ */
+ public EntityRegion buildEntityRegion(String regionName, Properties properties, CacheDataDescription metadata) throws CacheException;
+
+ /**
+ * Build a cache region specialized for storing collection data.
+ *
+ * @param regionName The name of the region.
+ * @param properties Configuration properties.
+ * @param metadata Information regarding the type of data to be cached
+ * @return The built region
+ * @throws CacheException Indicates problems building the region.
+ */
+ public CollectionRegion buildCollectionRegion(String regionName, Properties properties, CacheDataDescription metadata) throws CacheException;
+
+ /**
+ * Build a cache region specialized for storing query results
+ *
+ * @param regionName The name of the region.
+ * @param properties Configuration properties.
+ * @return The built region
+ * @throws CacheException Indicates problems building the region.
+ */
+ public QueryResultsRegion buildQueryResultsRegion(String regionName, Properties properties) throws CacheException;
+
+ /**
+ * Build a cache region specialized for storing update-timestamps data.
+ *
+ * @param regionName The name of the region.
+ * @param properties Configuration properties.
+ * @return The built region
+ * @throws CacheException Indicates problems building the region.
+ */
+ public TimestampsRegion buildTimestampsRegion(String regionName, Properties properties) throws CacheException;
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/StandardQueryCache.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/StandardQueryCache.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/StandardQueryCache.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,184 @@
+//$Id: StandardQueryCache.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.cache;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.hibernate.HibernateException;
+import org.hibernate.UnresolvableObjectException;
+import org.hibernate.cfg.Settings;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.type.Type;
+import org.hibernate.type.TypeFactory;
+
+/**
+ * The standard implementation of the Hibernate QueryCache interface. This
+ * implementation is very good at recognizing stale query results and
+ * and re-running queries when it detects this condition, recaching the new
+ * results.
+ *
+ * @author Gavin King
+ * @author Steve Ebersole
+ */
+public class StandardQueryCache implements QueryCache {
+
+ private static final Log log = LogFactory.getLog( StandardQueryCache.class );
+
+ private QueryResultsRegion cacheRegion;
+ private UpdateTimestampsCache updateTimestampsCache;
+
+ public void clear() throws CacheException {
+ cacheRegion.evictAll();
+ }
+
+ public StandardQueryCache(
+ final Settings settings,
+ final Properties props,
+ final UpdateTimestampsCache updateTimestampsCache,
+ String regionName) throws HibernateException {
+ if ( regionName == null ) {
+ regionName = StandardQueryCache.class.getName();
+ }
+ String prefix = settings.getCacheRegionPrefix();
+ if ( prefix != null ) {
+ regionName = prefix + '.' + regionName;
+ }
+ log.info( "starting query cache at region: " + regionName );
+
+ this.cacheRegion = settings.getRegionFactory().buildQueryResultsRegion( regionName, props );
+ this.updateTimestampsCache = updateTimestampsCache;
+ }
+
+ public boolean put(
+ QueryKey key,
+ Type[] returnTypes,
+ List result,
+ boolean isNaturalKeyLookup,
+ SessionImplementor session) throws HibernateException {
+ if ( isNaturalKeyLookup && result.size() == 0 ) {
+ return false;
+ }
+ else {
+ Long ts = new Long( session.getTimestamp() );
+
+ if ( log.isDebugEnabled() ) {
+ log.debug( "caching query results in region: " + cacheRegion.getName() + "; timestamp=" + ts );
+ }
+
+ List cacheable = new ArrayList( result.size() + 1 );
+ cacheable.add( ts );
+ for ( int i = 0; i < result.size(); i++ ) {
+ if ( returnTypes.length == 1 ) {
+ cacheable.add( returnTypes[0].disassemble( result.get( i ), session, null ) );
+ }
+ else {
+ cacheable.add(
+ TypeFactory.disassemble(
+ ( Object[] ) result.get( i ), returnTypes, null, session, null
+ )
+ );
+ }
+ }
+
+ cacheRegion.put( key, cacheable );
+
+ return true;
+
+ }
+
+ }
+
+ public List get(
+ QueryKey key,
+ Type[] returnTypes,
+ boolean isNaturalKeyLookup,
+ Set spaces,
+ SessionImplementor session) throws HibernateException {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "checking cached query results in region: " + cacheRegion.getName() );
+ }
+
+ List cacheable = ( List ) cacheRegion.get( key );
+ if ( cacheable == null ) {
+ log.debug( "query results were not found in cache" );
+ return null;
+ }
+
+ Long timestamp = ( Long ) cacheable.get( 0 );
+ if ( !isNaturalKeyLookup && !isUpToDate( spaces, timestamp ) ) {
+ log.debug( "cached query results were not up to date" );
+ return null;
+ }
+
+ log.debug( "returning cached query results" );
+ for ( int i = 1; i < cacheable.size(); i++ ) {
+ if ( returnTypes.length == 1 ) {
+ returnTypes[0].beforeAssemble( ( Serializable ) cacheable.get( i ), session );
+ }
+ else {
+ TypeFactory.beforeAssemble( ( Serializable[] ) cacheable.get( i ), returnTypes, session );
+ }
+ }
+ List result = new ArrayList( cacheable.size() - 1 );
+ for ( int i = 1; i < cacheable.size(); i++ ) {
+ try {
+ if ( returnTypes.length == 1 ) {
+ result.add( returnTypes[0].assemble( ( Serializable ) cacheable.get( i ), session, null ) );
+ }
+ else {
+ result.add(
+ TypeFactory.assemble(
+ ( Serializable[] ) cacheable.get( i ), returnTypes, session, null
+ )
+ );
+ }
+ }
+ catch ( UnresolvableObjectException uoe ) {
+ if ( isNaturalKeyLookup ) {
+ //TODO: not really completely correct, since
+ // the uoe could occur while resolving
+ // associations, leaving the PC in an
+ // inconsistent state
+ log.debug( "could not reassemble cached result set" );
+ cacheRegion.evict( key );
+ return null;
+ }
+ else {
+ throw uoe;
+ }
+ }
+ }
+ return result;
+ }
+
+ protected boolean isUpToDate(Set spaces, Long timestamp) {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "Checking query spaces for up-to-dateness: " + spaces );
+ }
+ return updateTimestampsCache.isUpToDate( spaces, timestamp );
+ }
+
+ public void destroy() {
+ try {
+ cacheRegion.destroy();
+ }
+ catch ( Exception e ) {
+ log.warn( "could not destroy query cache: " + cacheRegion.getName(), e );
+ }
+ }
+
+ public QueryResultsRegion getRegion() {
+ return cacheRegion;
+ }
+
+ public String toString() {
+ return "StandardQueryCache(" + cacheRegion.getName() + ')';
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/StandardQueryCacheFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/StandardQueryCacheFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/StandardQueryCacheFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,24 @@
+// $Id: StandardQueryCacheFactory.java 4690 2004-10-26 09:35:46Z oneovthafew $
+package org.hibernate.cache;
+
+import org.hibernate.HibernateException;
+import org.hibernate.cfg.Settings;
+
+import java.util.Properties;
+
+/**
+ * Standard Hibernate implementation of the QueryCacheFactory interface. Returns
+ * instances of {@link StandardQueryCache}.
+ */
+public class StandardQueryCacheFactory implements QueryCacheFactory {
+
+ public QueryCache getQueryCache(
+ final String regionName,
+ final UpdateTimestampsCache updateTimestampsCache,
+ final Settings settings,
+ final Properties props)
+ throws HibernateException {
+ return new StandardQueryCache(settings, props, updateTimestampsCache, regionName);
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/Timestamper.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/Timestamper.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/Timestamper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,37 @@
+//$Id: Timestamper.java 3890 2004-06-03 16:31:32Z steveebersole $
+package org.hibernate.cache;
+
+/**
+ * Generates increasing identifiers (in a single VM only).
+ * Not valid across multiple VMs. Identifiers are not necessarily
+ * strictly increasing, but usually are.
+ */
+public final class Timestamper {
+ private static short counter = 0;
+ private static long time;
+ private static final int BIN_DIGITS = 12;
+ public static final short ONE_MS = 1<<BIN_DIGITS;
+
+ public static long next() {
+ synchronized(Timestamper.class) {
+ long newTime = System.currentTimeMillis() << BIN_DIGITS;
+ if (time<newTime) {
+ time = newTime;
+ counter = 0;
+ }
+ else if (counter < ONE_MS - 1 ) {
+ counter++;
+ }
+
+ return time + counter;
+ }
+ }
+
+ private Timestamper() {}
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/TimestampsRegion.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/TimestampsRegion.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/TimestampsRegion.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,10 @@
+package org.hibernate.cache;
+
+/**
+ * Defines the contract for a cache region which will specifically be used to
+ * store entity "update timestamps".
+ *
+ * @author Steve Ebersole
+ */
+public interface TimestampsRegion extends GeneralDataRegion {
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/TransactionalCache.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/TransactionalCache.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/TransactionalCache.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,171 @@
+//$Id: TransactionalCache.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.cache;
+
+import java.util.Comparator;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.hibernate.cache.access.SoftLock;
+
+/**
+ * Support for fully transactional cache implementations like
+ * JBoss TreeCache. Note that this might be a less scalable
+ * concurrency strategy than <tt>ReadWriteCache</tt>. This is
+ * a "synchronous" concurrency strategy.
+ *
+ * @author Gavin King
+ */
+public class TransactionalCache implements CacheConcurrencyStrategy {
+
+ private static final Log log = LogFactory.getLog( TransactionalCache.class );
+
+ private Cache cache;
+
+ public String getRegionName() {
+ return cache.getRegionName();
+ }
+
+ public Object get(Object key, long txTimestamp) throws CacheException {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "cache lookup: " + key );
+ }
+ Object result = cache.read( key );
+ if ( log.isDebugEnabled() ) {
+ log.debug( result == null ? "cache miss" : "cache hit" );
+ }
+ return result;
+ }
+
+ public boolean put(
+ Object key,
+ Object value,
+ long txTimestamp,
+ Object version,
+ Comparator versionComparator,
+ boolean minimalPut) throws CacheException {
+ if ( minimalPut && cache.read( key ) != null ) {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "item already cached: " + key );
+ }
+ return false;
+ }
+ if ( log.isDebugEnabled() ) {
+ log.debug( "caching: " + key );
+ }
+ if ( cache instanceof OptimisticCache ) {
+ ( ( OptimisticCache ) cache ).writeLoad( key, value, version );
+ }
+ else {
+ cache.put( key, value );
+ }
+ return true;
+ }
+
+ /**
+ * Do nothing, returning null.
+ */
+ public SoftLock lock(Object key, Object version) throws CacheException {
+ //noop
+ return null;
+ }
+
+ /**
+ * Do nothing.
+ */
+ public void release(Object key, SoftLock clientLock) throws CacheException {
+ //noop
+ }
+
+ public boolean update(
+ Object key,
+ Object value,
+ Object currentVersion,
+ Object previousVersion) throws CacheException {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "updating: " + key );
+ }
+ if ( cache instanceof OptimisticCache ) {
+ ( ( OptimisticCache ) cache ).writeUpdate( key, value, currentVersion, previousVersion );
+ }
+ else {
+ cache.update( key, value );
+ }
+ return true;
+ }
+
+ public boolean insert(
+ Object key,
+ Object value,
+ Object currentVersion) throws CacheException {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "inserting: " + key );
+ }
+ if ( cache instanceof OptimisticCache ) {
+ ( ( OptimisticCache ) cache ).writeInsert( key, value, currentVersion );
+ }
+ else {
+ cache.update( key, value );
+ }
+ return true;
+ }
+
+ public void evict(Object key) throws CacheException {
+ cache.remove( key );
+ }
+
+ public void remove(Object key) throws CacheException {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "removing: " + key );
+ }
+ cache.remove( key );
+ }
+
+ public void clear() throws CacheException {
+ log.debug( "clearing" );
+ cache.clear();
+ }
+
+ public void destroy() {
+ try {
+ cache.destroy();
+ }
+ catch ( Exception e ) {
+ log.warn( "could not destroy cache", e );
+ }
+ }
+
+ public void setCache(Cache cache) {
+ this.cache = cache;
+ }
+
+ public Cache getCache() {
+ return cache;
+ }
+
+ /**
+ * Do nothing.
+ */
+ public boolean afterInsert(
+ Object key,
+ Object value,
+ Object version) throws CacheException {
+ return false;
+ }
+
+ /**
+ * Do nothing.
+ */
+ public boolean afterUpdate(
+ Object key,
+ Object value,
+ Object version,
+ SoftLock clientLock) throws CacheException {
+ return false;
+ }
+
+ public String toString() {
+ return cache + "(transactional)";
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/TransactionalDataRegion.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/TransactionalDataRegion.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/TransactionalDataRegion.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,31 @@
+package org.hibernate.cache;
+
+/**
+ * Defines contract for regions which hold transactionally-managed data.
+ * <p/>
+ * The data is not transactionally managed within the region; merely it is
+ * transactionally-managed in relation to its association with a particular
+ * {@link org.hibernate.Session}.
+ *
+ * @author Steve Ebersole
+ */
+public interface TransactionalDataRegion extends Region {
+ /**
+ * Is the underlying cache implementation aware of (and "participating in")
+ * ongoing JTA transactions?
+ * <p/>
+ * Regions which report that they are transaction-aware are considered
+ * "synchronous", in that we assume we can immediately (i.e. synchronously)
+ * write the changes to the cache and that the cache will properly manage
+ * application of the written changes within the bounds of ongoing JTA
+ * transactions. Conversely, regions reporting false are considered
+ * "asynchronous", where it is assumed that changes must be manually
+ * delayed by Hibernate until we are certain that the current transaction
+ * is successful (i.e. maintaining READ_COMMITTED isolation).
+ *
+ * @return True if transaction aware; false otherwise.
+ */
+ public boolean isTransactionAware();
+
+ public CacheDataDescription getCacheDataDescription();
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/UpdateTimestampsCache.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/UpdateTimestampsCache.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/UpdateTimestampsCache.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,110 @@
+//$Id: UpdateTimestampsCache.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.cache;
+
+import java.io.Serializable;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.hibernate.HibernateException;
+import org.hibernate.cfg.Settings;
+
+/**
+ * Tracks the timestamps of the most recent updates to particular tables. It is
+ * important that the cache timeout of the underlying cache implementation be set
+ * to a higher value than the timeouts of any of the query caches. In fact, we
+ * recommend that the the underlying cache not be configured for expiry at all.
+ * Note, in particular, that an LRU cache expiry policy is never appropriate.
+ *
+ * @author Gavin King
+ * @author Mikheil Kapanadze
+ */
+public class UpdateTimestampsCache {
+ public static final String REGION_NAME = UpdateTimestampsCache.class.getName();
+ private static final Log log = LogFactory.getLog( UpdateTimestampsCache.class );
+
+ private final TimestampsRegion region;
+
+ public UpdateTimestampsCache(Settings settings, Properties props) throws HibernateException {
+ String prefix = settings.getCacheRegionPrefix();
+ String regionName = prefix == null ? REGION_NAME : prefix + '.' + REGION_NAME;
+ log.info( "starting update timestamps cache at region: " + regionName );
+ this.region = settings.getRegionFactory().buildTimestampsRegion( regionName, props );
+ }
+
+ public synchronized void preinvalidate(Serializable[] spaces) throws CacheException {
+ //TODO: to handle concurrent writes correctly, this should return a Lock to the client
+ Long ts = new Long( region.nextTimestamp() + region.getTimeout() );
+ for ( int i=0; i<spaces.length; i++ ) {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "Pre-invalidating space [" + spaces[i] + "]" );
+ }
+ //put() has nowait semantics, is this really appropriate?
+ //note that it needs to be async replication, never local or sync
+ region.put( spaces[i], ts );
+ }
+ //TODO: return new Lock(ts);
+ }
+
+ public synchronized void invalidate(Serializable[] spaces) throws CacheException {
+ //TODO: to handle concurrent writes correctly, the client should pass in a Lock
+ Long ts = new Long( region.nextTimestamp() );
+ //TODO: if lock.getTimestamp().equals(ts)
+ for ( int i=0; i<spaces.length; i++ ) {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "Invalidating space [" + spaces[i] + "], timestamp: " + ts);
+ }
+ //put() has nowait semantics, is this really appropriate?
+ //note that it needs to be async replication, never local or sync
+ region.put( spaces[i], ts );
+ }
+ }
+
+ public synchronized boolean isUpToDate(Set spaces, Long timestamp) throws HibernateException {
+ Iterator iter = spaces.iterator();
+ while ( iter.hasNext() ) {
+ Serializable space = (Serializable) iter.next();
+ Long lastUpdate = (Long) region.get(space);
+ if ( lastUpdate==null ) {
+ //the last update timestamp was lost from the cache
+ //(or there were no updates since startup!)
+ //updateTimestamps.put( space, new Long( updateTimestamps.nextTimestamp() ) );
+ //result = false; // safer
+ }
+ else {
+ if ( log.isDebugEnabled() ) {
+ log.debug("[" + space + "] last update timestamp: " + lastUpdate + ", result set timestamp: " + timestamp );
+ }
+ if ( lastUpdate.longValue() >= timestamp.longValue() ) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ public void clear() throws CacheException {
+ region.evictAll();
+ }
+
+ public void destroy() {
+ try {
+ region.destroy();
+ }
+ catch (Exception e) {
+ log.warn("could not destroy UpdateTimestamps cache", e);
+ }
+ }
+
+ public TimestampsRegion getRegion() {
+ return region;
+ }
+
+ public String toString() {
+ return "UpdateTimestampeCache";
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/access/AccessType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/access/AccessType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/access/AccessType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,62 @@
+package org.hibernate.cache.access;
+
+import java.io.Serializable;
+
+/**
+ * The types of access strategies available.
+ *
+ * @author Steve Ebersole
+ */
+public class AccessType implements Serializable {
+ public static final AccessType READ_ONLY = new AccessType( "read-only" );
+ public static final AccessType READ_WRITE = new AccessType( "read-write" );
+ public static final AccessType NONSTRICT_READ_WRITE = new AccessType( "nonstrict-read-write" );
+ public static final AccessType TRANSACTIONAL = new AccessType( "transactional" );
+
+ private final String name;
+
+ private AccessType(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String toString() {
+ return "AccessType[" + name + "]";
+ }
+
+ private static AccessType resolve(String name) {
+ if ( READ_ONLY.name.equals( name ) ) {
+ return READ_ONLY;
+ }
+ else if ( READ_WRITE.name.equals( name ) ) {
+ return READ_WRITE;
+ }
+ else if ( NONSTRICT_READ_WRITE.name.equals( name ) ) {
+ return NONSTRICT_READ_WRITE;
+ }
+ else if ( TRANSACTIONAL.name.equals( name ) ) {
+ return TRANSACTIONAL;
+ }
+ else {
+ return null;
+ }
+ }
+
+ public static AccessType parse(String name) {
+ return resolve( name );
+ }
+
+ private Object readResolve() {
+ return resolve( name );
+ }
+
+ public static String getValidUsageString() {
+ return "cache usage attribute should be " + READ_ONLY.name +
+ ", " + READ_WRITE.name +
+ ", " + NONSTRICT_READ_WRITE.name +
+ ", or " + TRANSACTIONAL.name;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/access/CollectionRegionAccessStrategy.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/access/CollectionRegionAccessStrategy.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/access/CollectionRegionAccessStrategy.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,156 @@
+package org.hibernate.cache.access;
+
+import java.util.Comparator;
+
+import org.hibernate.cache.CacheException;
+import org.hibernate.cache.CollectionRegion;
+
+/**
+ * Contract for managing transactional and concurrent access to cached collection
+ * data. For cached collection data, all modification actions actually just
+ * invalidate the entry(s). The call sequence here is:
+ * {@link #lockItem} -> {@link #remove} -> {@link #unlockItem}
+ * <p/>
+ * There is another usage pattern that is used to invalidate entries
+ * after performing "bulk" HQL/SQL operations:
+ * {@link #lockRegion} -> {@link #removeAll} -> {@link #unlockRegion}
+ *
+ * @author Gavin King
+ * @author Steve Ebersole
+ */
+public interface CollectionRegionAccessStrategy {
+
+ /**
+ * Get the wrapped collection cache region
+ *
+ * @return The underlying region
+ */
+ public CollectionRegion getRegion();
+
+ /**
+ * Attempt to retrieve an object from the cache. Mainly used in attempting
+ * to resolve entities/collections from the second level cache.
+ *
+ * @param key The key of the item to be retrieved.
+ * @param txTimestamp a timestamp prior to the transaction start time
+ * @return the cached object or <tt>null</tt>
+ * @throws org.hibernate.cache.CacheException Propogated from underlying {@link org.hibernate.cache.Region}
+ */
+ public Object get(Object key, long txTimestamp) throws CacheException;
+
+ /**
+ * Attempt to cache an object, after loading from the database.
+ *
+ * @param key The item key
+ * @param value The item
+ * @param txTimestamp a timestamp prior to the transaction start time
+ * @param version the item version number
+ * @return <tt>true</tt> if the object was successfully cached
+ * @throws CacheException Propogated from underlying {@link org.hibernate.cache.Region}
+ */
+ public boolean putFromLoad(
+ Object key,
+ Object value,
+ long txTimestamp,
+ Object version) throws CacheException;
+
+ /**
+ * Attempt to cache an object, after loading from the database, explicitly
+ * specifying the minimalPut behavior.
+ *
+ * @param key The item key
+ * @param value The item
+ * @param txTimestamp a timestamp prior to the transaction start time
+ * @param version the item version number
+ * @param minimalPutOverride Explicit minimalPut flag
+ * @return <tt>true</tt> if the object was successfully cached
+ * @throws CacheException Propogated from underlying {@link org.hibernate.cache.Region}
+ */
+ public boolean putFromLoad(
+ Object key,
+ Object value,
+ long txTimestamp,
+ Object version,
+ boolean minimalPutOverride) throws CacheException;
+
+ /**
+ * We are going to attempt to update/delete the keyed object. This
+ * method is used by "asynchronous" concurrency strategies.
+ * <p/>
+ * The returned object must be passed back to release(), to release the
+ * lock. Concurrency strategies which do not support client-visible
+ * locks may silently return null.
+ *
+ * @param key The key of the item to lock
+ * @param version The item's current version value
+ * @return A representation of our lock on the item; or null.
+ * @throws CacheException Propogated from underlying {@link org.hibernate.cache.Region}
+ */
+ public SoftLock lockItem(Object key, Object version) throws CacheException;
+
+ /**
+ * Lock the entire region
+ *
+ * @return A representation of our lock on the item; or null.
+ * @throws CacheException Propogated from underlying {@link org.hibernate.cache.Region}
+ */
+ public SoftLock lockRegion() throws CacheException;
+
+ /**
+ * Called when we have finished the attempted update/delete (which may or
+ * may not have been successful), after transaction completion. This method
+ * is used by "asynchronous" concurrency strategies.
+ *
+ * @param key The item key
+ * @param lock The lock previously obtained from {@link #lockItem}
+ * @throws CacheException Propogated from underlying {@link org.hibernate.cache.Region}
+ */
+ public void unlockItem(Object key, SoftLock lock) throws CacheException;
+
+ /**
+ * Called after we have finished the attempted invalidation of the entire
+ * region
+ *
+ * @param lock The lock previously obtained from {@link #lockRegion}
+ * @throws CacheException Propogated from underlying {@link org.hibernate.cache.Region}
+ */
+ public void unlockRegion(SoftLock lock) throws CacheException;
+
+ /**
+ * Called after an item has become stale (before the transaction completes).
+ * This method is used by "synchronous" concurrency strategies.
+ *
+ * @param key The key of the item to remove
+ * @throws CacheException Propogated from underlying {@link org.hibernate.cache.Region}
+ */
+ public void remove(Object key) throws CacheException;
+
+ /**
+ * Called to evict data from the entire region
+ *
+ * @throws CacheException Propogated from underlying {@link org.hibernate.cache.Region}
+ */
+ public void removeAll() throws CacheException;
+
+ /**
+ * Forcibly evict an item from the cache immediately without regard for transaction
+ * isolation.
+ *
+ * @param key The key of the item to remove
+ * @throws CacheException Propogated from underlying {@link org.hibernate.cache.Region}
+ */
+ public void evict(Object key) throws CacheException;
+
+ /**
+ * Forcibly evict all items from the cache immediately without regard for transaction
+ * isolation.
+ *
+ * @throws CacheException Propogated from underlying {@link org.hibernate.cache.Region}
+ */
+ public void evictAll() throws CacheException;
+
+ /**
+ * Clean up all resources.
+ */
+ public void destroy();
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/access/EntityRegionAccessStrategy.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/access/EntityRegionAccessStrategy.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/access/EntityRegionAccessStrategy.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,211 @@
+package org.hibernate.cache.access;
+
+import org.hibernate.cache.EntityRegion;
+import org.hibernate.cache.CacheException;
+
+/**
+ * Contract for managing transactional and concurrent access to cached entity
+ * data. The expected call sequences related to various operations are:<ul>
+ * <li><b>INSERTS</b> : {@link #insert} -> {@link #afterInsert}</li>
+ * <li><b>UPDATES</b> : {@link #lockItem} -> {@link #update} -> {@link #afterUpdate}</li>
+ * <li><b>DELETES</b> : {@link #lockItem} -> {@link #remove} -> {@link #unlockItem}</li>
+ * </ul>
+ * <p/>
+ * There is another usage pattern that is used to invalidate entries
+ * after performing "bulk" HQL/SQL operations:
+ * {@link #lockRegion} -> {@link #removeAll} -> {@link #unlockRegion}
+ *
+ * @author Gavin King
+ * @author Steve Ebersole
+ */
+public interface EntityRegionAccessStrategy {
+
+ /**
+ * Get the wrapped entity cache region
+ *
+ * @return The underlying region
+ */
+ public EntityRegion getRegion();
+
+ /**
+ * Attempt to retrieve an object from the cache. Mainly used in attempting
+ * to resolve entities/collections from the second level cache.
+ *
+ * @param key The key of the item to be retrieved.
+ * @param txTimestamp a timestamp prior to the transaction start time
+ * @return the cached object or <tt>null</tt>
+ * @throws CacheException Propogated from underlying {@link org.hibernate.cache.Region}
+ */
+ public Object get(Object key, long txTimestamp) throws CacheException;
+
+ /**
+ * Attempt to cache an object, after loading from the database.
+ *
+ * @param key The item key
+ * @param value The item
+ * @param txTimestamp a timestamp prior to the transaction start time
+ * @param version the item version number
+ * @return <tt>true</tt> if the object was successfully cached
+ * @throws CacheException Propogated from underlying {@link org.hibernate.cache.Region}
+ */
+ public boolean putFromLoad(
+ Object key,
+ Object value,
+ long txTimestamp,
+ Object version) throws CacheException;
+
+ /**
+ * Attempt to cache an object, after loading from the database, explicitly
+ * specifying the minimalPut behavior.
+ *
+ * @param key The item key
+ * @param value The item
+ * @param txTimestamp a timestamp prior to the transaction start time
+ * @param version the item version number
+ * @param minimalPutOverride Explicit minimalPut flag
+ * @return <tt>true</tt> if the object was successfully cached
+ * @throws CacheException Propogated from underlying {@link org.hibernate.cache.Region}
+ */
+ public boolean putFromLoad(
+ Object key,
+ Object value,
+ long txTimestamp,
+ Object version,
+ boolean minimalPutOverride) throws CacheException;
+
+ /**
+ * We are going to attempt to update/delete the keyed object. This
+ * method is used by "asynchronous" concurrency strategies.
+ * <p/>
+ * The returned object must be passed back to release(), to release the
+ * lock. Concurrency strategies which do not support client-visible
+ * locks may silently return null.
+ *
+ * @param key The key of the item to lock
+ * @param version The item's current version value
+ * @return A representation of our lock on the item; or null.
+ * @throws CacheException Propogated from underlying {@link org.hibernate.cache.Region}
+ */
+ public SoftLock lockItem(Object key, Object version) throws CacheException;
+
+ /**
+ * Lock the entire region
+ *
+ * @return A representation of our lock on the item; or null.
+ * @throws CacheException Propogated from underlying {@link org.hibernate.cache.Region}
+ */
+ public SoftLock lockRegion() throws CacheException;
+
+ /**
+ * Called when we have finished the attempted update/delete (which may or
+ * may not have been successful), after transaction completion. This method
+ * is used by "asynchronous" concurrency strategies.
+ *
+ * @param key The item key
+ * @param lock The lock previously obtained from {@link #lockItem}
+ * @throws CacheException Propogated from underlying {@link org.hibernate.cache.Region}
+ */
+ public void unlockItem(Object key, SoftLock lock) throws CacheException;
+
+ /**
+ * Called after we have finished the attempted invalidation of the entire
+ * region
+ *
+ * @param lock The lock previously obtained from {@link #lockRegion}
+ * @throws CacheException Propogated from underlying {@link org.hibernate.cache.Region}
+ */
+ public void unlockRegion(SoftLock lock) throws CacheException;
+
+ /**
+ * Called after an item has been inserted (before the transaction completes),
+ * instead of calling evict().
+ * This method is used by "synchronous" concurrency strategies.
+ *
+ * @param key The item key
+ * @param value The item
+ * @param version The item's version value
+ * @return Were the contents of the cache actual changed by this operation?
+ * @throws CacheException Propogated from underlying {@link org.hibernate.cache.Region}
+ */
+ public boolean insert(Object key, Object value, Object version) throws CacheException;
+
+ /**
+ * Called after an item has been inserted (after the transaction completes),
+ * instead of calling release().
+ * This method is used by "asynchronous" concurrency strategies.
+ *
+ * @param key The item key
+ * @param value The item
+ * @param version The item's version value
+ * @return Were the contents of the cache actual changed by this operation?
+ * @throws CacheException Propogated from underlying {@link org.hibernate.cache.Region}
+ */
+ public boolean afterInsert(Object key, Object value, Object version) throws CacheException;
+
+ /**
+ * Called after an item has been updated (before the transaction completes),
+ * instead of calling evict(). This method is used by "synchronous" concurrency
+ * strategies.
+ *
+ * @param key The item key
+ * @param value The item
+ * @param currentVersion The item's current version value
+ * @param previousVersion The item's previous version value
+ * @return Were the contents of the cache actual changed by this operation?
+ * @throws CacheException Propogated from underlying {@link org.hibernate.cache.Region}
+ */
+ public boolean update(Object key, Object value, Object currentVersion, Object previousVersion) throws CacheException;
+
+ /**
+ * Called after an item has been updated (after the transaction completes),
+ * instead of calling release(). This method is used by "asynchronous"
+ * concurrency strategies.
+ *
+ * @param key The item key
+ * @param value The item
+ * @param currentVersion The item's current version value
+ * @param previousVersion The item's previous version value
+ * @param lock The lock previously obtained from {@link #lockItem}
+ * @return Were the contents of the cache actual changed by this operation?
+ * @throws CacheException Propogated from underlying {@link org.hibernate.cache.Region}
+ */
+ public boolean afterUpdate(Object key, Object value, Object currentVersion, Object previousVersion, SoftLock lock) throws CacheException;
+
+ /**
+ * Called after an item has become stale (before the transaction completes).
+ * This method is used by "synchronous" concurrency strategies.
+ *
+ * @param key The key of the item to remove
+ * @throws CacheException Propogated from underlying {@link org.hibernate.cache.Region}
+ */
+ public void remove(Object key) throws CacheException;
+
+ /**
+ * Called to evict data from the entire region
+ *
+ * @throws CacheException Propogated from underlying {@link org.hibernate.cache.Region}
+ */
+ public void removeAll() throws CacheException;
+
+ /**
+ * Forcibly evict an item from the cache immediately without regard for transaction
+ * isolation.
+ *
+ * @param key The key of the item to remove
+ * @throws CacheException Propogated from underlying {@link org.hibernate.cache.Region}
+ */
+ public void evict(Object key) throws CacheException;
+
+ /**
+ * Forcibly evict all items from the cache immediately without regard for transaction
+ * isolation.
+ *
+ * @throws CacheException Propogated from underlying {@link org.hibernate.cache.Region}
+ */
+ public void evictAll() throws CacheException;
+
+ /**
+ * Clean up all resources.
+ */
+ public void destroy();
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/access/SoftLock.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/access/SoftLock.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/access/SoftLock.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,10 @@
+package org.hibernate.cache.access;
+
+/**
+ * Moved up from inner definition on the now deprecated
+ * {@link org.hibernate.cache.CacheConcurrencyStrategy}.
+ *
+ * @author Steve Ebersole
+ */
+public interface SoftLock {
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/access/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/access/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/access/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,41 @@
+<html>
+<head></head>
+<body>
+<p>
+ Defines contracts for transactional and concurrent access to cached
+ {@link org.hibernate.cache.access.EntityRegionAccessStrategy entity} and
+ {@link org.hibernate.cache.access.CollectionRegionAccessStrategy collection} data. Transactions pass in a
+ timestamp indicating transaction start time which is then used to protect against concurrent access (exactly how
+ that occurs is based on the actual access-strategy impl used). Two different implementation patterns are provided
+ for.
+ <ul>
+ <li>
+ A transaction-aware cache implementation might be wrapped by a <i>synchronous</i> access strategy,
+ where updates to the cache are written to the cache inside the transaction.
+ </li>
+ <li>
+ A non-transaction-aware cache would be wrapped by an <i>asynchronous</i> access strategy, where items
+ are merely "soft locked" during the transaction and then updated during the "after transaction completion"
+ phase; the soft lock is not an actual lock on the database row - only upon the cached representation of the
+ item.
+ </li>
+ </ul>
+ The <i>asynchronous</i> access strategies are: {@link org.hibernate.cache.access.AccessType.READ_ONLY read-only},
+ {@link org.hibernate.cache.access.AccessType.READ_WRITE read-write} and
+ {@link org.hibernate.cache.access.AccessType.NONSTRICT_READ_WRITE nonstrict-read-write}. The only
+ <i>synchronous</i> access strategy is {@link org.hibernate.cache.access.AccessType.TRANSACTIONAL transactional}.
+</p>
+<p>
+ Note that, for an <i>asynchronous</i> cache, cache invalidation must be a two step process (lock->unlock or
+ lock->afterUpdate), since this is the only way to guarantee consistency with the database for a nontransactional
+ cache implementation. For a <i>synchronous</i> cache, cache invalidation is a single step process (evict or update).
+ Hence, these contracts ({@link org.hibernate.cache.access.EntityRegionAcessStrategy} and
+ {@link org.hibernate.cache.access.CollectionRegionAccessStrategy}) define a three step process to cater for both
+ models (see the individual contracts for details).
+</p>
+<p>
+ Note that query result caching does not go through an access strategy; those caches are managed directly against
+ the underlying {@link org.hibernate.cache.QueryResultsRegion}.
+</p>
+</body>
+</html>
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/CacheEntry.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/CacheEntry.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/CacheEntry.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,143 @@
+//$Id: CacheEntry.java 7785 2005-08-08 23:24:44Z oneovthafew $
+package org.hibernate.cache.entry;
+
+import java.io.Serializable;
+
+import org.hibernate.AssertionFailure;
+import org.hibernate.HibernateException;
+import org.hibernate.Interceptor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.event.EventSource;
+import org.hibernate.event.PreLoadEvent;
+import org.hibernate.event.PreLoadEventListener;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.type.TypeFactory;
+import org.hibernate.util.ArrayHelper;
+
+/**
+ * A cached instance of a persistent class
+ *
+ * @author Gavin King
+ */
+public final class CacheEntry implements Serializable {
+
+ private final Serializable[] disassembledState;
+ private final String subclass;
+ private final boolean lazyPropertiesAreUnfetched;
+ private final Object version;
+
+ public String getSubclass() {
+ return subclass;
+ }
+
+ public boolean areLazyPropertiesUnfetched() {
+ return lazyPropertiesAreUnfetched;
+ }
+
+ public CacheEntry(
+ final Object[] state,
+ final EntityPersister persister,
+ final boolean unfetched,
+ final Object version,
+ final SessionImplementor session,
+ final Object owner)
+ throws HibernateException {
+ //disassembled state gets put in a new array (we write to cache by value!)
+ this.disassembledState = TypeFactory.disassemble(
+ state,
+ persister.getPropertyTypes(),
+ persister.isLazyPropertiesCacheable() ?
+ null : persister.getPropertyLaziness(),
+ session,
+ owner
+ );
+ subclass = persister.getEntityName();
+ lazyPropertiesAreUnfetched = unfetched || !persister.isLazyPropertiesCacheable();
+ this.version = version;
+ }
+
+ public Object getVersion() {
+ return version;
+ }
+
+ CacheEntry(Serializable[] state, String subclass, boolean unfetched, Object version) {
+ this.disassembledState = state;
+ this.subclass = subclass;
+ this.lazyPropertiesAreUnfetched = unfetched;
+ this.version = version;
+ }
+
+ public Object[] assemble(
+ final Object instance,
+ final Serializable id,
+ final EntityPersister persister,
+ final Interceptor interceptor,
+ final EventSource session)
+ throws HibernateException {
+
+ if ( !persister.getEntityName().equals(subclass) ) {
+ throw new AssertionFailure("Tried to assemble a different subclass instance");
+ }
+
+ return assemble(disassembledState, instance, id, persister, interceptor, session);
+
+ }
+
+ private static Object[] assemble(
+ final Serializable[] values,
+ final Object result,
+ final Serializable id,
+ final EntityPersister persister,
+ final Interceptor interceptor,
+ final EventSource session)
+ throws HibernateException {
+
+ //assembled state gets put in a new array (we read from cache by value!)
+ Object[] assembledProps = TypeFactory.assemble(
+ values,
+ persister.getPropertyTypes(),
+ session, result
+ );
+
+ //persister.setIdentifier(result, id); //before calling interceptor, for consistency with normal load
+
+ //TODO: reuse the PreLoadEvent
+ PreLoadEvent preLoadEvent = new PreLoadEvent( session )
+ .setEntity(result)
+ .setState(assembledProps)
+ .setId(id)
+ .setPersister(persister);
+
+ PreLoadEventListener[] listeners = session.getListeners().getPreLoadEventListeners();
+ for ( int i = 0; i < listeners.length; i++ ) {
+ listeners[i].onPreLoad(preLoadEvent);
+ }
+
+ persister.setPropertyValues(
+ result,
+ assembledProps,
+ session.getEntityMode()
+ );
+
+ return assembledProps;
+ }
+
+ public Serializable[] getDisassembledState() {
+ // todo: this was added to support initializing an entity's EntityEntry snapshot during reattach;
+ // this should be refactored to instead expose a method to assemble a EntityEntry based on this
+ // state for return.
+ return disassembledState;
+ }
+
+ public String toString() {
+ return "CacheEntry(" + subclass + ')' +
+ ArrayHelper.toString(disassembledState);
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/CacheEntryStructure.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/CacheEntryStructure.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/CacheEntryStructure.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,14 @@
+//$Id: CacheEntryStructure.java 5707 2005-02-13 12:47:01Z oneovthafew $
+package org.hibernate.cache.entry;
+
+import org.hibernate.engine.SessionFactoryImplementor;
+
+
+
+/**
+ * @author Gavin King
+ */
+public interface CacheEntryStructure {
+ public Object structure(Object item);
+ public Object destructure(Object map, SessionFactoryImplementor factory);
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/CacheEntryStructure.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/CollectionCacheEntry.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/CollectionCacheEntry.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/CollectionCacheEntry.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,43 @@
+//$Id: CollectionCacheEntry.java 6838 2005-05-20 19:50:07Z oneovthafew $
+package org.hibernate.cache.entry;
+
+import java.io.Serializable;
+
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.util.ArrayHelper;
+
+/**
+ * @author Gavin King
+ */
+public class CollectionCacheEntry implements Serializable {
+
+ private final Serializable state;
+
+ public Serializable[] getState() {
+ //TODO: assumes all collections disassemble to an array!
+ return (Serializable[]) state;
+ }
+
+ public CollectionCacheEntry(PersistentCollection collection, CollectionPersister persister) {
+ this.state = collection.disassemble(persister);
+ }
+
+ CollectionCacheEntry(Serializable state) {
+ this.state = state;
+ }
+
+ public void assemble(
+ final PersistentCollection collection,
+ final CollectionPersister persister,
+ final Object owner
+ ) {
+ collection.initializeFromCache(persister, state, owner);
+ collection.afterInitialize();
+ }
+
+ public String toString() {
+ return "CollectionCacheEntry" + ArrayHelper.toString( getState() );
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/CollectionCacheEntry.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/StructuredCacheEntry.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/StructuredCacheEntry.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/StructuredCacheEntry.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,48 @@
+//$Id: StructuredCacheEntry.java 7764 2005-08-05 16:16:46Z oneovthafew $
+package org.hibernate.cache.entry;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.persister.entity.EntityPersister;
+
+/**
+ * @author Gavin King
+ */
+public class StructuredCacheEntry implements CacheEntryStructure {
+
+ private EntityPersister persister;
+
+ public StructuredCacheEntry(EntityPersister persister) {
+ this.persister = persister;
+ }
+
+ public Object destructure(Object item, SessionFactoryImplementor factory) {
+ Map map = (Map) item;
+ boolean lazyPropertiesUnfetched = ( (Boolean) map.get("_lazyPropertiesUnfetched") ).booleanValue();
+ String subclass = (String) map.get("_subclass");
+ Object version = map.get("_version");
+ EntityPersister subclassPersister = factory.getEntityPersister(subclass);
+ String[] names = subclassPersister.getPropertyNames();
+ Serializable[] state = new Serializable[names.length];
+ for ( int i=0; i<names.length; i++ ) {
+ state[i] = (Serializable) map.get( names[i] );
+ }
+ return new CacheEntry(state, subclass, lazyPropertiesUnfetched, version);
+ }
+
+ public Object structure(Object item) {
+ CacheEntry entry = (CacheEntry) item;
+ String[] names = persister.getPropertyNames();
+ Map map = new HashMap(names.length+2);
+ map.put( "_subclass", entry.getSubclass() );
+ map.put( "_version", entry.getVersion() );
+ map.put( "_lazyPropertiesUnfetched", entry.areLazyPropertiesUnfetched() ? Boolean.TRUE : Boolean.FALSE );
+ for ( int i=0; i<names.length; i++ ) {
+ map.put( names[i], entry.getDisassembledState()[i] );
+ }
+ return map;
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/StructuredCacheEntry.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/StructuredCollectionCacheEntry.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/StructuredCollectionCacheEntry.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/StructuredCollectionCacheEntry.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,25 @@
+//$Id: StructuredCollectionCacheEntry.java 5707 2005-02-13 12:47:01Z oneovthafew $
+package org.hibernate.cache.entry;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.List;
+
+import org.hibernate.engine.SessionFactoryImplementor;
+
+/**
+ * @author Gavin King
+ */
+public class StructuredCollectionCacheEntry implements CacheEntryStructure {
+
+ public Object structure(Object item) {
+ CollectionCacheEntry entry = (CollectionCacheEntry) item;
+ return Arrays.asList( entry.getState() );
+ }
+
+ public Object destructure(Object item, SessionFactoryImplementor factory) {
+ List list = (List) item;
+ return new CollectionCacheEntry( list.toArray( new Serializable[list.size()] ) );
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/StructuredCollectionCacheEntry.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/StructuredMapCacheEntry.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/StructuredMapCacheEntry.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/StructuredMapCacheEntry.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,39 @@
+//$Id: StructuredMapCacheEntry.java 5707 2005-02-13 12:47:01Z oneovthafew $
+package org.hibernate.cache.entry;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.hibernate.engine.SessionFactoryImplementor;
+
+/**
+ * @author Gavin King
+ */
+public class StructuredMapCacheEntry implements CacheEntryStructure {
+
+ public Object structure(Object item) {
+ CollectionCacheEntry entry = (CollectionCacheEntry) item;
+ Serializable[] state = entry.getState();
+ Map map = new HashMap(state.length);
+ for ( int i=0; i<state.length; ) {
+ map.put( state[i++], state[i++] );
+ }
+ return map;
+ }
+
+ public Object destructure(Object item, SessionFactoryImplementor factory) {
+ Map map = (Map) item;
+ Serializable[] state = new Serializable[ map.size()*2 ];
+ int i=0;
+ Iterator iter = map.entrySet().iterator();
+ while ( iter.hasNext() ) {
+ Map.Entry me = (Map.Entry) iter.next();
+ state[i++] = (Serializable) me.getKey();
+ state[i++] = (Serializable) me.getValue();
+ }
+ return new CollectionCacheEntry(state);
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/StructuredMapCacheEntry.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/UnstructuredCacheEntry.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/UnstructuredCacheEntry.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/UnstructuredCacheEntry.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,20 @@
+//$Id: UnstructuredCacheEntry.java 5707 2005-02-13 12:47:01Z oneovthafew $
+package org.hibernate.cache.entry;
+
+import org.hibernate.engine.SessionFactoryImplementor;
+
+
+/**
+ * @author Gavin King
+ */
+public class UnstructuredCacheEntry implements CacheEntryStructure {
+
+ public Object structure(Object item) {
+ return item;
+ }
+
+ public Object destructure(Object map, SessionFactoryImplementor factory) {
+ return map;
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/UnstructuredCacheEntry.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,10 @@
+<html>
+<head>
+</head>
+<body>
+<p>
+ This package defines formats for disassembled state
+ kept in the second level cache.
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/entry/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/CacheDataDescriptionImpl.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/CacheDataDescriptionImpl.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/CacheDataDescriptionImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,53 @@
+package org.hibernate.cache.impl;
+
+import java.util.Comparator;
+
+import org.hibernate.cache.CacheDataDescription;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Collection;
+import org.hibernate.type.VersionType;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class CacheDataDescriptionImpl implements CacheDataDescription {
+ private final boolean mutable;
+ private final boolean versioned;
+ private final Comparator versionComparator;
+
+ public CacheDataDescriptionImpl(boolean mutable, boolean versioned, Comparator versionComparator) {
+ this.mutable = mutable;
+ this.versioned = versioned;
+ this.versionComparator = versionComparator;
+ }
+
+ public boolean isMutable() {
+ return mutable;
+ }
+
+ public boolean isVersioned() {
+ return versioned;
+ }
+
+ public Comparator getVersionComparator() {
+ return versionComparator;
+ }
+
+ public static CacheDataDescriptionImpl decode(PersistentClass model) {
+ return new CacheDataDescriptionImpl(
+ model.isMutable(),
+ model.isVersioned(),
+ model.isVersioned() ? ( ( VersionType ) model.getVersion().getType() ).getComparator() : null
+ );
+ }
+
+ public static CacheDataDescriptionImpl decode(Collection model) {
+ return new CacheDataDescriptionImpl(
+ model.isMutable(),
+ model.getOwner().isVersioned(),
+ model.getOwner().isVersioned() ? ( ( VersionType ) model.getOwner().getVersion().getType() ).getComparator() : null
+ );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/NoCachingRegionFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/NoCachingRegionFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/NoCachingRegionFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,57 @@
+package org.hibernate.cache.impl;
+
+import java.util.Properties;
+
+import org.hibernate.cache.RegionFactory;
+import org.hibernate.cache.CacheException;
+import org.hibernate.cache.EntityRegion;
+import org.hibernate.cache.CollectionRegion;
+import org.hibernate.cache.QueryResultsRegion;
+import org.hibernate.cache.TimestampsRegion;
+import org.hibernate.cache.NoCachingEnabledException;
+import org.hibernate.cache.CacheDataDescription;
+import org.hibernate.cfg.Settings;
+
+/**
+ * Factory used if no caching enabled in config...
+ *
+ * @author Steve Ebersole
+ */
+public class NoCachingRegionFactory implements RegionFactory {
+
+
+ public NoCachingRegionFactory(Properties properties) {
+ }
+
+ public void start(Settings settings, Properties properties) throws CacheException {
+ }
+
+ public void stop() {
+ }
+
+ public boolean isMinimalPutsEnabledByDefault() {
+ return false;
+ }
+
+ public long nextTimestamp() {
+ return System.currentTimeMillis() / 100;
+ }
+
+ public EntityRegion buildEntityRegion(String regionName, Properties properties, CacheDataDescription metadata)
+ throws CacheException {
+ throw new NoCachingEnabledException();
+ }
+
+ public CollectionRegion buildCollectionRegion(String regionName, Properties properties, CacheDataDescription metadata)
+ throws CacheException {
+ throw new NoCachingEnabledException();
+ }
+
+ public QueryResultsRegion buildQueryResultsRegion(String regionName, Properties properties) throws CacheException {
+ throw new NoCachingEnabledException();
+ }
+
+ public TimestampsRegion buildTimestampsRegion(String regionName, Properties properties) throws CacheException {
+ throw new NoCachingEnabledException();
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/BaseGeneralDataRegionAdapter.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/BaseGeneralDataRegionAdapter.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/BaseGeneralDataRegionAdapter.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,34 @@
+package org.hibernate.cache.impl.bridge;
+
+import org.hibernate.cache.GeneralDataRegion;
+import org.hibernate.cache.Cache;
+import org.hibernate.cache.CacheException;
+import org.hibernate.cfg.Settings;
+
+/**
+ * {@inheritDoc}
+*
+* @author Steve Ebersole
+*/
+public abstract class BaseGeneralDataRegionAdapter extends BaseRegionAdapter implements GeneralDataRegion {
+
+ protected BaseGeneralDataRegionAdapter(Cache underlyingCache, Settings settings) {
+ super( underlyingCache, settings );
+ }
+
+ public Object get(Object key) throws CacheException {
+ return underlyingCache.get( key );
+ }
+
+ public void put(Object key, Object value) throws CacheException {
+ underlyingCache.put( key, value );
+ }
+
+ public void evict(Object key) throws CacheException {
+ underlyingCache.remove( key );
+ }
+
+ public void evictAll() throws CacheException {
+ underlyingCache.clear();
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/BaseRegionAdapter.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/BaseRegionAdapter.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/BaseRegionAdapter.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,59 @@
+package org.hibernate.cache.impl.bridge;
+
+import java.util.Map;
+
+import org.hibernate.cache.Region;
+import org.hibernate.cache.Cache;
+import org.hibernate.cache.CacheException;
+import org.hibernate.cfg.Settings;
+
+/**
+ * Basic adapter bridging between {@link Region} and {@link Cache}.
+ *
+ * @author Steve Ebersole
+ */
+public abstract class BaseRegionAdapter implements Region {
+ protected final Cache underlyingCache;
+ protected final Settings settings;
+
+ protected BaseRegionAdapter(Cache underlyingCache, Settings settings) {
+ this.underlyingCache = underlyingCache;
+ this.settings = settings;
+ }
+
+ public String getName() {
+ return underlyingCache.getRegionName();
+ }
+
+ public void clear() throws CacheException {
+ underlyingCache.clear();
+ }
+
+ public void destroy() throws CacheException {
+ underlyingCache.destroy();
+ }
+
+ public long getSizeInMemory() {
+ return underlyingCache.getSizeInMemory();
+ }
+
+ public long getElementCountInMemory() {
+ return underlyingCache.getElementCountInMemory();
+ }
+
+ public long getElementCountOnDisk() {
+ return underlyingCache.getElementCountOnDisk();
+ }
+
+ public Map toMap() {
+ return underlyingCache.toMap();
+ }
+
+ public long nextTimestamp() {
+ return underlyingCache.nextTimestamp();
+ }
+
+ public int getTimeout() {
+ return underlyingCache.getTimeout();
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/BaseTransactionalDataRegionAdapter.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/BaseTransactionalDataRegionAdapter.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/BaseTransactionalDataRegionAdapter.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,63 @@
+package org.hibernate.cache.impl.bridge;
+
+import org.hibernate.cache.TransactionalDataRegion;
+import org.hibernate.cache.Cache;
+import org.hibernate.cache.CacheDataDescription;
+import org.hibernate.cfg.Settings;
+
+/**
+ * {@inheritDoc}
+*
+* @author Steve Ebersole
+*/
+public abstract class BaseTransactionalDataRegionAdapter extends BaseRegionAdapter implements TransactionalDataRegion {
+
+ protected final CacheDataDescription metadata;
+
+ protected BaseTransactionalDataRegionAdapter(Cache underlyingCache, Settings settings, CacheDataDescription metadata) {
+ super( underlyingCache, settings );
+ this.metadata = metadata;
+ }
+
+ public boolean isTransactionAware() {
+ return underlyingCache instanceof org.hibernate.cache.TreeCache
+ || underlyingCache instanceof org.hibernate.cache.OptimisticTreeCache;
+ }
+
+ public CacheDataDescription getCacheDataDescription() {
+ return metadata;
+ }
+
+// public void lock() throws CacheException {
+// // impl note: unfortunately this was not a capability of the original Cache contract
+// }
+//
+// public void unlock() throws CacheException {
+// // impl note: unfortunately this was not a capability of the original Cache contract
+// }
+//
+// public void lock(Object key) throws CacheException {
+// underlyingCache.lock( key );
+// }
+//
+// public void unlock(Object key) throws CacheException {
+// underlyingCache.unlock( key );
+// }
+//
+// public Object get(Object key) throws CacheException {
+// return underlyingCache.get( key );
+// }
+//
+// public Object read(Object key) throws CacheException {
+// return underlyingCache.read( key );
+// }
+//
+// public void writeLoad(Object key, Object value, Object version) throws CacheException {
+// if ( underlyingCache instanceof OptimisticCache ) {
+// ( ( OptimisticCache ) underlyingCache ).writeLoad( key, value, version );
+// }
+// else {
+// underlyingCache.put( key, value );
+// }
+// }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/CollectionAccessStrategyAdapter.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/CollectionAccessStrategyAdapter.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/CollectionAccessStrategyAdapter.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,91 @@
+package org.hibernate.cache.impl.bridge;
+
+import org.hibernate.cache.access.CollectionRegionAccessStrategy;
+import org.hibernate.cache.access.SoftLock;
+import org.hibernate.cache.CacheConcurrencyStrategy;
+import org.hibernate.cache.CollectionRegion;
+import org.hibernate.cache.CacheException;
+import org.hibernate.cfg.Settings;
+
+/**
+ * Adapter specifically bridging {@link CollectionRegionAccessStrategy} to {@link CacheConcurrencyStrategy}.
+ *
+ * @author Steve Ebersole
+ */
+public class CollectionAccessStrategyAdapter implements CollectionRegionAccessStrategy {
+ private final CollectionRegion region;
+ private final CacheConcurrencyStrategy ccs;
+ private final Settings settings;
+
+ public CollectionAccessStrategyAdapter(CollectionRegion region, CacheConcurrencyStrategy ccs, Settings settings) {
+ this.region = region;
+ this.ccs = ccs;
+ this.settings = settings;
+ }
+
+ public CollectionRegion getRegion() {
+ return region;
+ }
+
+ public Object get(Object key, long txTimestamp) throws CacheException {
+ return ccs.get( key, txTimestamp );
+ }
+
+ public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version) throws CacheException {
+ return putFromLoad( key, value, txTimestamp, version, settings.isMinimalPutsEnabled() );
+ }
+
+ public boolean putFromLoad(
+ Object key,
+ Object value,
+ long txTimestamp,
+ Object version,
+ boolean minimalPutOverride) throws CacheException {
+ return ccs.put( key, value, txTimestamp, version, region.getCacheDataDescription().getVersionComparator(), minimalPutOverride );
+ }
+
+ public SoftLock lockItem(Object key, Object version) throws CacheException {
+ return ccs.lock( key, version );
+ }
+
+ public SoftLock lockRegion() throws CacheException {
+ // no-op; CCS did not have such a concept
+ return null;
+ }
+
+ public void unlockItem(Object key, SoftLock lock) throws CacheException {
+ ccs.release( key, lock );
+ }
+
+ public void unlockRegion(SoftLock lock) throws CacheException {
+ // again, CCS did not have such a concept; but a reasonable
+ // proximity is to clear the cache after transaction *as long as*
+ // the underlying cache is not JTA aware.
+ if ( !region.isTransactionAware() ) {
+ ccs.clear();
+ }
+ }
+
+ public void remove(Object key) throws CacheException {
+ ccs.evict( key );
+ }
+
+ public void removeAll() throws CacheException {
+ // again, CCS did not have such a concept; however a reasonable
+ // proximity is to clear the cache. For non-transaction aware
+ // caches, we will also do a clear at the end of the transaction
+ ccs.clear();
+ }
+
+ public void evict(Object key) throws CacheException {
+ ccs.remove( key );
+ }
+
+ public void evictAll() throws CacheException {
+ ccs.clear();
+ }
+
+ public void destroy() {
+ ccs.destroy();
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/CollectionRegionAdapter.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/CollectionRegionAdapter.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/CollectionRegionAdapter.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,58 @@
+package org.hibernate.cache.impl.bridge;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.hibernate.cache.CollectionRegion;
+import org.hibernate.cache.Cache;
+import org.hibernate.cache.CacheDataDescription;
+import org.hibernate.cache.OptimisticCache;
+import org.hibernate.cache.CacheException;
+import org.hibernate.cache.CacheConcurrencyStrategy;
+import org.hibernate.cache.TransactionalCache;
+import org.hibernate.cache.ReadWriteCache;
+import org.hibernate.cache.NonstrictReadWriteCache;
+import org.hibernate.cache.ReadOnlyCache;
+import org.hibernate.cache.access.CollectionRegionAccessStrategy;
+import org.hibernate.cache.access.AccessType;
+import org.hibernate.cfg.Settings;
+
+/**
+ * Adapter specifically bridging {@link CollectionRegion} to {@link Cache}.
+ *
+ * @author Steve Ebersole
+ */
+public class CollectionRegionAdapter extends BaseTransactionalDataRegionAdapter implements CollectionRegion {
+ private static final Log log = LogFactory.getLog( CollectionRegionAdapter.class );
+
+ public CollectionRegionAdapter(Cache underlyingCache, Settings settings, CacheDataDescription metadata) {
+ super( underlyingCache, settings, metadata );
+ if ( underlyingCache instanceof OptimisticCache ) {
+ ( ( OptimisticCache ) underlyingCache ).setSource( new OptimisticCacheSourceAdapter( metadata ) );
+ }
+ }
+
+ public CollectionRegionAccessStrategy buildAccessStrategy(AccessType accessType) throws CacheException {
+ CacheConcurrencyStrategy ccs;
+ if ( AccessType.READ_ONLY.equals( accessType ) ) {
+ if ( metadata.isMutable() ) {
+ log.warn( "read-only cache configured for mutable collection [" + getName() + "]" );
+ }
+ ccs = new ReadOnlyCache();
+ }
+ else if ( AccessType.READ_WRITE.equals( accessType ) ) {
+ ccs = new ReadWriteCache();
+ }
+ else if ( AccessType.NONSTRICT_READ_WRITE.equals( accessType ) ) {
+ ccs = new NonstrictReadWriteCache();
+ }
+ else if ( AccessType.TRANSACTIONAL.equals( accessType ) ) {
+ ccs = new TransactionalCache();
+ }
+ else {
+ throw new IllegalArgumentException( "unrecognized access strategy type [" + accessType + "]" );
+ }
+ ccs.setCache( underlyingCache );
+ return new CollectionAccessStrategyAdapter( this, ccs, settings );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/EntityAccessStrategyAdapter.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/EntityAccessStrategyAdapter.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/EntityAccessStrategyAdapter.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,109 @@
+package org.hibernate.cache.impl.bridge;
+
+import org.hibernate.cache.CacheConcurrencyStrategy;
+import org.hibernate.cache.EntityRegion;
+import org.hibernate.cache.CacheException;
+import org.hibernate.cache.access.EntityRegionAccessStrategy;
+import org.hibernate.cache.access.SoftLock;
+import org.hibernate.cfg.Settings;
+
+/**
+ * Adapter specifically bridging {@link EntityRegionAccessStrategy} to {@link CacheConcurrencyStrategy}.
+ *
+ * @author Steve Ebersole
+ */
+public class EntityAccessStrategyAdapter implements EntityRegionAccessStrategy {
+ private final EntityRegion region;
+ private final CacheConcurrencyStrategy ccs;
+ private final Settings settings;
+
+ public EntityAccessStrategyAdapter(EntityRegion region, CacheConcurrencyStrategy ccs, Settings settings) {
+ this.region = region;
+ this.ccs = ccs;
+ this.settings = settings;
+ }
+
+ public EntityRegion getRegion() {
+ return region;
+ }
+
+ public Object get(Object key, long txTimestamp) throws CacheException {
+ return ccs.get( key, txTimestamp );
+ }
+
+ public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version) throws CacheException {
+ return putFromLoad( key, value, txTimestamp, version, settings.isMinimalPutsEnabled() );
+ }
+
+ public boolean putFromLoad(
+ Object key,
+ Object value,
+ long txTimestamp,
+ Object version,
+ boolean minimalPutOverride) throws CacheException {
+ return ccs.put( key, value, txTimestamp, version, region.getCacheDataDescription().getVersionComparator(), minimalPutOverride );
+ }
+
+ public SoftLock lockItem(Object key, Object version) throws CacheException {
+ return ccs.lock( key, version );
+ }
+
+ public SoftLock lockRegion() throws CacheException {
+ // no-op; CCS did not have such a concept
+ return null;
+ }
+
+ public void unlockItem(Object key, SoftLock lock) throws CacheException {
+ ccs.release( key, lock );
+ }
+
+ public void unlockRegion(SoftLock lock) throws CacheException {
+ // again, CCS did not have such a concept; but a reasonable
+ // proximity is to clear the cache after transaction *as long as*
+ // the underlying cache is not JTA aware.
+ if ( !region.isTransactionAware() ) {
+ ccs.clear();
+ }
+ }
+
+ public boolean insert(Object key, Object value, Object version) throws CacheException {
+ return ccs.insert( key, value, version );
+ }
+
+ public boolean afterInsert(Object key, Object value, Object version) throws CacheException {
+ return ccs.afterInsert( key, value, version );
+ }
+
+ public boolean update(Object key, Object value, Object currentVersion, Object previousVersion)
+ throws CacheException {
+ return ccs.update( key, value, currentVersion, previousVersion );
+ }
+
+ public boolean afterUpdate(Object key, Object value, Object currentVersion, Object previousVersion, SoftLock lock)
+ throws CacheException {
+ return ccs.afterUpdate( key, value, currentVersion, lock );
+ }
+
+ public void remove(Object key) throws CacheException {
+ ccs.evict( key );
+ }
+
+ public void removeAll() throws CacheException {
+ // again, CCS did not have such a concept; however a reasonable
+ // proximity is to clear the cache. For non-transaction aware
+ // caches, we will also do a clear at the end of the transaction
+ ccs.clear();
+ }
+
+ public void evict(Object key) throws CacheException {
+ ccs.remove( key );
+ }
+
+ public void evictAll() throws CacheException {
+ ccs.clear();
+ }
+
+ public void destroy() {
+ ccs.destroy();
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/EntityRegionAdapter.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/EntityRegionAdapter.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/EntityRegionAdapter.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,59 @@
+package org.hibernate.cache.impl.bridge;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.hibernate.cache.EntityRegion;
+import org.hibernate.cache.Cache;
+import org.hibernate.cache.CacheDataDescription;
+import org.hibernate.cache.OptimisticCache;
+import org.hibernate.cache.CacheException;
+import org.hibernate.cache.CacheConcurrencyStrategy;
+import org.hibernate.cache.ReadOnlyCache;
+import org.hibernate.cache.ReadWriteCache;
+import org.hibernate.cache.NonstrictReadWriteCache;
+import org.hibernate.cache.TransactionalCache;
+import org.hibernate.cache.access.EntityRegionAccessStrategy;
+import org.hibernate.cache.access.AccessType;
+import org.hibernate.cfg.Settings;
+
+/**
+ * Adapter specifically bridging {@link EntityRegion} to {@link Cache}.
+ *
+ * @author Steve Ebersole
+ */
+public class EntityRegionAdapter extends BaseTransactionalDataRegionAdapter implements EntityRegion {
+ private static final Log log = LogFactory.getLog( EntityRegionAdapter.class );
+
+ public EntityRegionAdapter(Cache underlyingCache, Settings settings, CacheDataDescription metadata) {
+ super( underlyingCache, settings, metadata );
+ if ( underlyingCache instanceof OptimisticCache ) {
+ ( ( OptimisticCache ) underlyingCache ).setSource( new OptimisticCacheSourceAdapter( metadata ) );
+ }
+ }
+
+ public EntityRegionAccessStrategy buildAccessStrategy(AccessType accessType) throws CacheException {
+ CacheConcurrencyStrategy ccs;
+ if ( AccessType.READ_ONLY.equals( accessType ) ) {
+ if ( metadata.isMutable() ) {
+ log.warn( "read-only cache configured for mutable entity [" + getName() + "]" );
+ }
+ ccs = new ReadOnlyCache();
+ }
+ else if ( AccessType.READ_WRITE.equals( accessType ) ) {
+ ccs = new ReadWriteCache();
+ }
+ else if ( AccessType.NONSTRICT_READ_WRITE.equals( accessType ) ) {
+ ccs = new NonstrictReadWriteCache();
+ }
+ else if ( AccessType.TRANSACTIONAL.equals( accessType ) ) {
+ ccs = new TransactionalCache();
+ }
+ else {
+ throw new IllegalArgumentException( "unrecognized access strategy type [" + accessType + "]" );
+ }
+ ccs.setCache( underlyingCache );
+ return new EntityAccessStrategyAdapter( this, ccs, settings );
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/OptimisticCacheSourceAdapter.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/OptimisticCacheSourceAdapter.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/OptimisticCacheSourceAdapter.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,27 @@
+package org.hibernate.cache.impl.bridge;
+
+import java.util.Comparator;
+
+import org.hibernate.cache.OptimisticCacheSource;
+import org.hibernate.cache.CacheDataDescription;
+
+/**
+ * {@inheritDoc}
+*
+* @author Steve Ebersole
+*/
+public class OptimisticCacheSourceAdapter implements OptimisticCacheSource {
+ private final CacheDataDescription dataDescription;
+
+ public OptimisticCacheSourceAdapter(CacheDataDescription dataDescription) {
+ this.dataDescription = dataDescription;
+ }
+
+ public boolean isVersioned() {
+ return dataDescription.isVersioned();
+ }
+
+ public Comparator getVersionComparator() {
+ return dataDescription.getVersionComparator();
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/QueryResultsRegionAdapter.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/QueryResultsRegionAdapter.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/QueryResultsRegionAdapter.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,16 @@
+package org.hibernate.cache.impl.bridge;
+
+import org.hibernate.cache.QueryResultsRegion;
+import org.hibernate.cache.Cache;
+import org.hibernate.cfg.Settings;
+
+/**
+ * Adapter specifically briding {@link QueryResultsRegion} to {@link Cache}.
+*
+* @author Steve Ebersole
+ */
+public class QueryResultsRegionAdapter extends BaseGeneralDataRegionAdapter implements QueryResultsRegion {
+ protected QueryResultsRegionAdapter(Cache underlyingCache, Settings settings) {
+ super( underlyingCache, settings );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/RegionFactoryCacheProviderBridge.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/RegionFactoryCacheProviderBridge.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/RegionFactoryCacheProviderBridge.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,91 @@
+package org.hibernate.cache.impl.bridge;
+
+import java.util.Properties;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.hibernate.cache.RegionFactory;
+import org.hibernate.cache.CacheProvider;
+import org.hibernate.cache.CacheException;
+import org.hibernate.cache.EntityRegion;
+import org.hibernate.cache.CollectionRegion;
+import org.hibernate.cache.QueryResultsRegion;
+import org.hibernate.cache.NoCacheProvider;
+import org.hibernate.cache.TimestampsRegion;
+import org.hibernate.cache.CacheDataDescription;
+import org.hibernate.util.PropertiesHelper;
+import org.hibernate.util.ReflectHelper;
+import org.hibernate.cfg.Environment;
+import org.hibernate.cfg.Settings;
+
+/**
+ * Acts as a bridge between the {@link RegionFactory} contract and the older
+ * {@link CacheProvider} contract.
+ *
+ * @author Steve Ebersole
+ */
+public class RegionFactoryCacheProviderBridge implements RegionFactory {
+ public static final String DEF_PROVIDER = NoCacheProvider.class.getName();
+ private static final Log log = LogFactory.getLog( RegionFactoryCacheProviderBridge.class );
+
+ private CacheProvider cacheProvider;
+ private Settings settings;
+
+ public RegionFactoryCacheProviderBridge(Properties properties) {
+ String providerClassName = PropertiesHelper.getString( Environment.CACHE_PROVIDER, properties, DEF_PROVIDER );
+ log.info( "Cache provider: " + providerClassName );
+ try {
+ cacheProvider = ( CacheProvider ) ReflectHelper.classForName( providerClassName ).newInstance();
+ }
+ catch ( Exception cnfe ) {
+ throw new CacheException( "could not instantiate CacheProvider [" + providerClassName + "]", cnfe );
+ }
+ }
+
+ public void start(Settings settings, Properties properties) throws CacheException {
+ this.settings = settings;
+ cacheProvider.start( properties );
+ }
+
+ public void stop() {
+ cacheProvider.stop();
+ cacheProvider = null;
+ }
+
+ public boolean isMinimalPutsEnabledByDefault() {
+ return cacheProvider.isMinimalPutsEnabledByDefault();
+ }
+
+ public long nextTimestamp() {
+ return cacheProvider.nextTimestamp();
+ }
+
+ public CacheProvider getCacheProvider() {
+ return cacheProvider;
+ }
+
+ public EntityRegion buildEntityRegion(
+ String regionName,
+ Properties properties,
+ CacheDataDescription metadata) throws CacheException {
+ return new EntityRegionAdapter( cacheProvider.buildCache( regionName, properties ), settings, metadata );
+ }
+
+ public CollectionRegion buildCollectionRegion(
+ String regionName,
+ Properties properties,
+ CacheDataDescription metadata) throws CacheException {
+ return new CollectionRegionAdapter( cacheProvider.buildCache( regionName, properties ), settings, metadata );
+ }
+
+ public QueryResultsRegion buildQueryResultsRegion(String regionName, Properties properties) throws CacheException {
+ return new QueryResultsRegionAdapter( cacheProvider.buildCache( regionName, properties ), settings );
+ }
+
+ public TimestampsRegion buildTimestampsRegion(String regionName, Properties properties) throws CacheException {
+ return new TimestampsRegionAdapter( cacheProvider.buildCache( regionName, properties ), settings );
+ }
+
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/TimestampsRegionAdapter.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/TimestampsRegionAdapter.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/impl/bridge/TimestampsRegionAdapter.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,16 @@
+package org.hibernate.cache.impl.bridge;
+
+import org.hibernate.cache.TimestampsRegion;
+import org.hibernate.cache.Cache;
+import org.hibernate.cfg.Settings;
+
+/**
+ * Adapter specifically briding {@link TimestampsRegion} to {@link Cache}.
+*
+* @author Steve Ebersole
+ */
+public class TimestampsRegionAdapter extends BaseGeneralDataRegionAdapter implements TimestampsRegion {
+ protected TimestampsRegionAdapter(Cache underlyingCache, Settings settings) {
+ super( underlyingCache, settings );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cache/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cache/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,36 @@
+<html>
+ <head>
+ </head>
+
+ <body>
+ <p>
+ This package defines APIs/SPIs and implementations for the Hibernate second-level cache.
+ </p>
+ <p>
+ The legacy (and now deprecated) approach to caching is defined by the {@link org.hibernate.cache.CacheProvider} and
+ {@link org.hibernate.cache.Cache} interfaces as well as the {@link org.hibernate.cache.CacheConcurrencyStrategy}
+ interface along with the various implementations of all these interfaces. In that scheme, a
+ {@link org.hibernate.cache.CacheProvider} defined how to configure and perform lifecycle operations
+ in regards to a particular underlying caching library; it also defined how to build {@link org.hibernate.cache.Cache}
+ instances which in turn defined how to access the "regions" of the underlying cache instance.
+ For entity and collection data cache regions, {@link org.hibernate.cache.CacheConcurrencyStrategy} wrapped
+ access to those cache regions to apply transactional/concurrent access semantics.
+ </p>
+ <p>
+ The improved approach is based on {@link org.hibernate.cache.RegionFactory}, the various
+ {@link org.hibernate.cache.Region} specializations and the two access strategies contracts
+ ({@link org.hibernate.cache.access.EntityRegionAccessStrategy} and
+ {@link org.hibernate.cache.access.CollectionRegionAccessStrategy}). The general approach here is that
+ {@link org.hibernate.cache.RegionFactory} defined how to configure and perform lifecycle operations
+ in regards to a particular underlying caching library (<b>or libraries</b>).
+ {@link org.hibernate.cache.RegionFactory} also defines how to build specialized
+ {@link org.hibernate.cache.Region} instances based on the type of data we will be storing in that given
+ region. The fact that {@link org.hibernate.cache.RegionFactory} is asked to build <b>specialized</b>
+ regions (as opposed to just general access) is the first <i>improvement</i> over the legacy scheme. The
+ second <i>improvement</i> is the fact that the regions (well the ones like entity and collection regions
+ that are responsible for storing {@link org.hibernate.cache.TransactionalDataRegion transactional} data) are
+ asked to build their own access strategies (see {@link org.hibernate.cache.EntityRegion#buildAccessStrategy}
+ and {@link org.hibernate.cache.CollectionRegion#buildAccessStrategy}).
+ </p>
+ </body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/cache/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/CollectionSecondPass.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/CollectionSecondPass.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/CollectionSecondPass.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,73 @@
+//$Id: CollectionSecondPass.java 10196 2006-08-03 07:53:27Z max.andersen at jboss.com $
+package org.hibernate.cfg;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.MappingException;
+import org.hibernate.mapping.Collection;
+import org.hibernate.mapping.IndexedCollection;
+import org.hibernate.mapping.OneToMany;
+import org.hibernate.mapping.Selectable;
+import org.hibernate.mapping.Value;
+
+/**
+ * Collection second pass
+ *
+ * @author Emmanuel Bernard
+ */
+public abstract class CollectionSecondPass implements SecondPass {
+ private static Log log = LogFactory.getLog( CollectionSecondPass.class );
+ Mappings mappings;
+ Collection collection;
+ private Map localInheritedMetas;
+
+ public CollectionSecondPass(Mappings mappings, Collection collection, java.util.Map inheritedMetas) {
+ this.collection = collection;
+ this.mappings = mappings;
+ this.localInheritedMetas = inheritedMetas;
+ }
+
+ public CollectionSecondPass(Mappings mappings, Collection collection) {
+ this(mappings, collection, Collections.EMPTY_MAP);
+ }
+
+ public void doSecondPass(java.util.Map persistentClasses)
+ throws MappingException {
+ if ( log.isDebugEnabled() )
+ log.debug( "Second pass for collection: " + collection.getRole() );
+
+ secondPass( persistentClasses, localInheritedMetas ); // using local since the inheritedMetas at this point is not the correct map since it is always the empty map
+ collection.createAllKeys();
+
+ if ( log.isDebugEnabled() ) {
+ String msg = "Mapped collection key: " + columns( collection.getKey() );
+ if ( collection.isIndexed() )
+ msg += ", index: " + columns( ( (IndexedCollection) collection ).getIndex() );
+ if ( collection.isOneToMany() ) {
+ msg += ", one-to-many: "
+ + ( (OneToMany) collection.getElement() ).getReferencedEntityName();
+ }
+ else {
+ msg += ", element: " + columns( collection.getElement() );
+ }
+ log.debug( msg );
+ }
+ }
+
+ abstract public void secondPass(java.util.Map persistentClasses, java.util.Map inheritedMetas)
+ throws MappingException;
+
+ private static String columns(Value val) {
+ StringBuffer columns = new StringBuffer();
+ Iterator iter = val.getColumnIterator();
+ while ( iter.hasNext() ) {
+ columns.append( ( (Selectable) iter.next() ).getText() );
+ if ( iter.hasNext() ) columns.append( ", " );
+ }
+ return columns.toString();
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/Configuration.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/Configuration.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/Configuration.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,2103 @@
+//$Id: Configuration.java 10841 2006-11-17 18:29:10Z max.andersen at jboss.com $
+package org.hibernate.cfg;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.io.StringReader;
+import java.lang.reflect.Array;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.jar.JarFile;
+import java.util.zip.ZipEntry;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.dom4j.Attribute;
+import org.dom4j.DocumentException;
+import org.dom4j.Element;
+import org.hibernate.EmptyInterceptor;
+import org.hibernate.HibernateException;
+import org.hibernate.Interceptor;
+import org.hibernate.InvalidMappingException;
+import org.hibernate.MappingException;
+import org.hibernate.MappingNotFoundException;
+import org.hibernate.SessionFactory;
+import org.hibernate.proxy.EntityNotFoundDelegate;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.MySQLDialect;
+import org.hibernate.dialect.function.SQLFunction;
+import org.hibernate.engine.FilterDefinition;
+import org.hibernate.engine.Mapping;
+import org.hibernate.event.AutoFlushEventListener;
+import org.hibernate.event.DeleteEventListener;
+import org.hibernate.event.DirtyCheckEventListener;
+import org.hibernate.event.EventListeners;
+import org.hibernate.event.EvictEventListener;
+import org.hibernate.event.FlushEntityEventListener;
+import org.hibernate.event.FlushEventListener;
+import org.hibernate.event.InitializeCollectionEventListener;
+import org.hibernate.event.LoadEventListener;
+import org.hibernate.event.LockEventListener;
+import org.hibernate.event.MergeEventListener;
+import org.hibernate.event.PersistEventListener;
+import org.hibernate.event.PostDeleteEventListener;
+import org.hibernate.event.PostInsertEventListener;
+import org.hibernate.event.PostLoadEventListener;
+import org.hibernate.event.PostUpdateEventListener;
+import org.hibernate.event.PreDeleteEventListener;
+import org.hibernate.event.PreInsertEventListener;
+import org.hibernate.event.PreLoadEventListener;
+import org.hibernate.event.PreUpdateEventListener;
+import org.hibernate.event.RefreshEventListener;
+import org.hibernate.event.ReplicateEventListener;
+import org.hibernate.event.SaveOrUpdateEventListener;
+import org.hibernate.id.IdentifierGenerator;
+import org.hibernate.id.PersistentIdentifierGenerator;
+import org.hibernate.impl.SessionFactoryImpl;
+import org.hibernate.mapping.AuxiliaryDatabaseObject;
+import org.hibernate.mapping.Collection;
+import org.hibernate.mapping.ForeignKey;
+import org.hibernate.mapping.IdentifierCollection;
+import org.hibernate.mapping.Index;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Property;
+import org.hibernate.mapping.RootClass;
+import org.hibernate.mapping.SimpleValue;
+import org.hibernate.mapping.Table;
+import org.hibernate.mapping.UniqueKey;
+import org.hibernate.secure.JACCConfiguration;
+import org.hibernate.tool.hbm2ddl.DatabaseMetadata;
+import org.hibernate.tool.hbm2ddl.TableMetadata;
+import org.hibernate.type.SerializationException;
+import org.hibernate.type.Type;
+import org.hibernate.util.ArrayHelper;
+import org.hibernate.util.CollectionHelper;
+import org.hibernate.util.ConfigHelper;
+import org.hibernate.util.ReflectHelper;
+import org.hibernate.util.SerializationHelper;
+import org.hibernate.util.StringHelper;
+import org.hibernate.util.XMLHelper;
+import org.hibernate.util.PropertiesHelper;
+import org.w3c.dom.Document;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+
+/**
+ * An instance of <tt>Configuration</tt> allows the application
+ * to specify properties and mapping documents to be used when
+ * creating a <tt>SessionFactory</tt>. Usually an application will create
+ * a single <tt>Configuration</tt>, build a single instance of
+ * <tt>SessionFactory</tt> and then instantiate <tt>Session</tt>s in
+ * threads servicing client requests. The <tt>Configuration</tt> is meant
+ * only as an initialization-time object. <tt>SessionFactory</tt>s are
+ * immutable and do not retain any association back to the
+ * <tt>Configuration</tt>.<br>
+ * <br>
+ * A new <tt>Configuration</tt> will use the properties specified in
+ * <tt>hibernate.properties</tt> by default.
+ *
+ * @author Gavin King
+ * @see org.hibernate.SessionFactory
+ */
+public class Configuration implements Serializable {
+
+ private static Log log = LogFactory.getLog( Configuration.class );
+
+ protected Map classes;
+ protected Map imports;
+ protected Map collections;
+ protected Map tables;
+ protected List auxiliaryDatabaseObjects;
+ protected Map sqlFunctions;
+ protected Map namedQueries;
+ protected Map namedSqlQueries;
+ /**
+ * Map<String, SqlResultSetMapping> result set name, result set description
+ */
+ protected Map sqlResultSetMappings;
+ protected Map filterDefinitions;
+ protected List secondPasses;
+ protected List propertyReferences;
+// protected List extendsQueue;
+ protected Map extendsQueue;
+ protected Map tableNameBinding;
+ protected Map columnNameBindingPerTable;
+ private Interceptor interceptor;
+ private Properties properties;
+ private EntityResolver entityResolver;
+ private EntityNotFoundDelegate entityNotFoundDelegate;
+
+ protected transient XMLHelper xmlHelper;
+ protected transient Map typeDefs;
+
+ protected NamingStrategy namingStrategy;
+
+ private EventListeners eventListeners;
+
+ protected final SettingsFactory settingsFactory;
+
+ protected void reset() {
+ classes = new HashMap();
+ imports = new HashMap();
+ collections = new HashMap();
+ tables = new TreeMap();
+ namedQueries = new HashMap();
+ namedSqlQueries = new HashMap();
+ sqlResultSetMappings = new HashMap();
+ xmlHelper = new XMLHelper();
+ typeDefs = new HashMap();
+ propertyReferences = new ArrayList();
+ secondPasses = new ArrayList();
+ interceptor = EmptyInterceptor.INSTANCE;
+ properties = Environment.getProperties();
+ entityResolver = XMLHelper.DEFAULT_DTD_RESOLVER;
+ eventListeners = new EventListeners();
+ filterDefinitions = new HashMap();
+// extendsQueue = new ArrayList();
+ extendsQueue = new HashMap();
+ auxiliaryDatabaseObjects = new ArrayList();
+ tableNameBinding = new HashMap();
+ columnNameBindingPerTable = new HashMap();
+ namingStrategy = DefaultNamingStrategy.INSTANCE;
+ sqlFunctions = new HashMap();
+ }
+
+ private transient Mapping mapping = buildMapping();
+
+
+
+ protected Configuration(SettingsFactory settingsFactory) {
+ this.settingsFactory = settingsFactory;
+ reset();
+ }
+
+ public Configuration() {
+ this( new SettingsFactory() );
+ }
+
+ /**
+ * Iterate the entity mappings
+ *
+ * @return Iterator of the entity mappings currently contained in the configuration.
+ */
+ public Iterator getClassMappings() {
+ return classes.values().iterator();
+ }
+
+ /**
+ * Iterate the collection mappings
+ *
+ * @return Iterator of the collection mappings currently contained in the configuration.
+ */
+ public Iterator getCollectionMappings() {
+ return collections.values().iterator();
+ }
+
+ /**
+ * Iterate the table mappings
+ *
+ * @return Iterator of the table mappings currently contained in the configuration.
+ */
+ public Iterator getTableMappings() {
+ return tables.values().iterator();
+ }
+
+ /**
+ * Get the mapping for a particular entity
+ *
+ * @param entityName An entity name.
+ * @return the entity mapping information
+ */
+ public PersistentClass getClassMapping(String entityName) {
+ return (PersistentClass) classes.get( entityName );
+ }
+
+ /**
+ * Get the mapping for a particular collection role
+ *
+ * @param role a collection role
+ * @return The collection mapping information
+ */
+ public Collection getCollectionMapping(String role) {
+ return (Collection) collections.get( role );
+ }
+
+ /**
+ * Set a custom entity resolver. This entity resolver must be
+ * set before addXXX(misc) call.
+ * Default value is {@link org.hibernate.util.DTDEntityResolver}
+ *
+ * @param entityResolver entity resolver to use
+ */
+ public void setEntityResolver(EntityResolver entityResolver) {
+ this.entityResolver = entityResolver;
+ }
+
+ public EntityResolver getEntityResolver() {
+ return entityResolver;
+ }
+
+ /**
+ * Retrieve the user-supplied delegate to handle non-existent entity
+ * scenarios. May be null.
+ *
+ * @return The user-supplied delegate
+ */
+ public EntityNotFoundDelegate getEntityNotFoundDelegate() {
+ return entityNotFoundDelegate;
+ }
+
+ /**
+ * Specify a user-supplied delegate to be used to handle scenarios where an entity could not be
+ * located by specified id. This is mainly intended for EJB3 implementations to be able to
+ * control how proxy initialization errors should be handled...
+ *
+ * @param entityNotFoundDelegate The delegate to use
+ */
+ public void setEntityNotFoundDelegate(EntityNotFoundDelegate entityNotFoundDelegate) {
+ this.entityNotFoundDelegate = entityNotFoundDelegate;
+ }
+
+ /**
+ * Read mappings from a particular XML file
+ *
+ * @param xmlFile a path to a file
+ * @return this (for method chaining purposes)
+ * @throws org.hibernate.MappingException Indicates inability to locate or parse
+ * the specified mapping file.
+ * @see #addFile(java.io.File)
+ */
+ public Configuration addFile(String xmlFile) throws MappingException {
+ return addFile( new File( xmlFile ) );
+ }
+
+ /**
+ * Read mappings from a particular XML file
+ *
+ * @param xmlFile a path to a file
+ * @return this (for method chaining purposes)
+ * @throws org.hibernate.MappingException Indicates inability to locate or parse
+ * the specified mapping file.
+ */
+ public Configuration addFile(File xmlFile) throws MappingException {
+ log.info( "Reading mappings from file: " + xmlFile.getPath() );
+ if ( !xmlFile.exists() ) {
+ throw new MappingNotFoundException( "file", xmlFile.toString() );
+ }
+ try {
+ List errors = new ArrayList();
+ org.dom4j.Document doc = xmlHelper.createSAXReader( xmlFile.toString(), errors, entityResolver ).read( xmlFile );
+ if ( errors.size() != 0 ) {
+ throw new InvalidMappingException( "file", xmlFile.toString(), ( Throwable ) errors.get( 0 ) );
+ }
+ add( doc );
+ return this;
+ }
+ catch ( InvalidMappingException e ) {
+ throw e;
+ }
+ catch ( MappingNotFoundException e ) {
+ throw e;
+ }
+ catch ( Exception e ) {
+ throw new InvalidMappingException( "file", xmlFile.toString(), e );
+ }
+ }
+
+ /**
+ * Add a cached mapping file. A cached file is a serialized representation
+ * of the DOM structure of a particular mapping. It is saved from a previous
+ * call as a file with the name <tt>xmlFile + ".bin"</tt> where xmlFile is
+ * the name of the original mapping file.
+ * </p>
+ * If a cached <tt>xmlFile + ".bin"</tt> exists and is newer than
+ * <tt>xmlFile</tt> the <tt>".bin"</tt> file will be read directly. Otherwise
+ * xmlFile is read and then serialized to <tt>xmlFile + ".bin"</tt> for use
+ * the next time.
+ *
+ * @param xmlFile The cacheable mapping file to be added.
+ * @return this (for method chaining purposes)
+ * @throws MappingException Indicates problems reading the cached file or processing
+ * the non-cached file.
+ */
+ public Configuration addCacheableFile(File xmlFile) throws MappingException {
+ try {
+ File cachedFile = new File( xmlFile.getAbsolutePath() + ".bin" );
+ org.dom4j.Document doc = null;
+
+ final boolean useCachedFile = xmlFile.exists() &&
+ cachedFile.exists() &&
+ xmlFile.lastModified() < cachedFile.lastModified();
+
+ if ( useCachedFile ) {
+ try {
+ log.info( "Reading mappings from cache file: " + cachedFile );
+ doc = ( org.dom4j.Document ) SerializationHelper.deserialize( new FileInputStream( cachedFile ) );
+ }
+ catch ( SerializationException e ) {
+ log.warn( "Could not deserialize cache file: " + cachedFile.getPath(), e );
+ }
+ catch ( FileNotFoundException e ) {
+ log.warn( "I/O reported cached file could not be found : " + cachedFile.getPath(), e );
+ }
+ }
+
+ // if doc is null, then for whatever reason, the cached file cannot be used...
+ if ( doc == null ) {
+ if ( !xmlFile.exists() ) {
+ throw new MappingNotFoundException( "file", xmlFile.toString() );
+ }
+
+ log.info( "Reading mappings from file: " + xmlFile );
+ List errors = new ArrayList();
+ try {
+ doc = xmlHelper.createSAXReader( xmlFile.getAbsolutePath(), errors, entityResolver ).read( xmlFile );
+ if ( errors.size() != 0 ) {
+ throw new MappingException( "invalid mapping", ( Throwable ) errors.get( 0 ) );
+ }
+ }
+ catch( DocumentException e){
+ throw new MappingException( "invalid mapping", e );
+ }
+
+ try {
+ log.debug( "Writing cache file for: " + xmlFile + " to: " + cachedFile );
+ SerializationHelper.serialize( ( Serializable ) doc, new FileOutputStream( cachedFile ) );
+ }
+ catch ( SerializationException e ) {
+ log.warn( "Could not write cached file: " + cachedFile, e );
+ }
+ catch ( FileNotFoundException e ) {
+ log.warn( "I/O reported error writing cached file : " + cachedFile.getPath(), e );
+ }
+ }
+
+ add( doc );
+ return this;
+
+ }
+ catch ( InvalidMappingException e ) {
+ throw e;
+ }
+ catch ( MappingNotFoundException e ) {
+ throw e;
+ }
+ catch ( Exception e ) {
+ throw new InvalidMappingException( "file", xmlFile.toString(), e );
+ }
+ }
+
+ /**
+ * Add a cacheable mapping file.
+ *
+ * @param xmlFile The name of the file to be added. This must be in a form
+ * useable to simply construct a {@link java.io.File} instance.
+ * @return this (for method chaining purposes)
+ * @throws MappingException Indicates problems reading the cached file or processing
+ * the non-cached file.
+ * @see #addCacheableFile(java.io.File)
+ */
+ public Configuration addCacheableFile(String xmlFile) throws MappingException {
+ return addCacheableFile( new File( xmlFile ) );
+ }
+
+
+ /**
+ * Read mappings from a <tt>String</tt>
+ *
+ * @param xml an XML string
+ * @return this (for method chaining purposes)
+ * @throws org.hibernate.MappingException Indicates problems parsing the
+ * given XML string
+ */
+ public Configuration addXML(String xml) throws MappingException {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "Mapping XML:\n" + xml );
+ }
+ try {
+ List errors = new ArrayList();
+ org.dom4j.Document doc = xmlHelper.createSAXReader( "XML String", errors, entityResolver )
+ .read( new StringReader( xml ) );
+ if ( errors.size() != 0 ) {
+ throw new MappingException( "invalid mapping", (Throwable) errors.get( 0 ) );
+ }
+ add( doc );
+ }
+ catch (DocumentException e) {
+ throw new MappingException( "Could not parse mapping document in XML string", e );
+ }
+ return this;
+ }
+
+ /**
+ * Read mappings from a <tt>URL</tt>
+ *
+ * @param url The url for the mapping document to be read.
+ * @return this (for method chaining purposes)
+ * @throws MappingException Indicates problems reading the URL or processing
+ * the mapping document.
+ */
+ public Configuration addURL(URL url) throws MappingException {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "Reading mapping document from URL:" + url.toExternalForm() );
+ }
+ try {
+ addInputStream( url.openStream() );
+ }
+ catch ( InvalidMappingException e ) {
+ throw new InvalidMappingException( "URL", url.toExternalForm(), e.getCause() );
+ }
+ catch (Exception e) {
+ throw new InvalidMappingException( "URL", url.toExternalForm(), e );
+ }
+ return this;
+ }
+
+ /**
+ * Read mappings from a DOM <tt>Document</tt>
+ *
+ * @param doc The DOM document
+ * @return this (for method chaining purposes)
+ * @throws MappingException Indicates problems reading the DOM or processing
+ * the mapping document.
+ */
+ public Configuration addDocument(Document doc) throws MappingException {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "Mapping document:\n" + doc );
+ }
+ add( xmlHelper.createDOMReader().read( doc ) );
+ return this;
+ }
+
+ /**
+ * Read mappings from an {@link java.io.InputStream}.
+ *
+ * @param xmlInputStream The input stream containing a DOM.
+ * @return this (for method chaining purposes)
+ * @throws MappingException Indicates problems reading the stream, or
+ * processing the contained mapping document.
+ */
+ public Configuration addInputStream(InputStream xmlInputStream) throws MappingException {
+ try {
+ List errors = new ArrayList();
+ org.dom4j.Document doc = xmlHelper.createSAXReader( "XML InputStream", errors, entityResolver )
+ .read( new InputSource( xmlInputStream ) );
+ if ( errors.size() != 0 ) {
+ throw new InvalidMappingException( "invalid mapping", null, (Throwable) errors.get( 0 ) );
+ }
+ add( doc );
+ return this;
+ }
+ catch (DocumentException e) {
+ throw new InvalidMappingException( "input stream", null, e );
+ }
+ finally {
+ try {
+ xmlInputStream.close();
+ }
+ catch (IOException ioe) {
+ log.warn( "Could not close input stream", ioe );
+ }
+ }
+ }
+
+ /**
+ * Read mappings as a application resource (i.e. classpath lookup).
+ *
+ * @param resourceName The resource name
+ * @param classLoader The class loader to use.
+ * @return this (for method chaining purposes)
+ * @throws MappingException Indicates problems locating the resource or
+ * processing the contained mapping document.
+ */
+ public Configuration addResource(String resourceName, ClassLoader classLoader) throws MappingException {
+ log.info( "Reading mappings from resource: " + resourceName );
+ InputStream rsrc = classLoader.getResourceAsStream( resourceName );
+ if ( rsrc == null ) {
+ throw new MappingNotFoundException( "resource", resourceName );
+ }
+ try {
+ return addInputStream( rsrc );
+ }
+ catch (MappingException me) {
+ throw new InvalidMappingException( "resource", resourceName, me );
+ }
+ }
+
+ /**
+ * Read mappings as a application resourceName (i.e. classpath lookup)
+ * trying different classloaders.
+ *
+ * @param resourceName The resource name
+ * @return this (for method chaining purposes)
+ * @throws MappingException Indicates problems locating the resource or
+ * processing the contained mapping document.
+ */
+ public Configuration addResource(String resourceName) throws MappingException {
+ log.info( "Reading mappings from resource : " + resourceName );
+ ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+ InputStream rsrc = null;
+ if (contextClassLoader!=null) {
+ rsrc = contextClassLoader.getResourceAsStream( resourceName );
+ }
+ if ( rsrc == null ) {
+ rsrc = Environment.class.getClassLoader().getResourceAsStream( resourceName );
+ }
+ if ( rsrc == null ) {
+ throw new MappingNotFoundException( "resource", resourceName );
+ }
+ try {
+ return addInputStream( rsrc );
+ }
+ catch (MappingException me) {
+ throw new InvalidMappingException( "resource", resourceName, me );
+ }
+ }
+
+ /**
+ * Read a mapping as an application resouurce using the convention that a class
+ * named <tt>foo.bar.Foo</tt> is mapped by a file <tt>foo/bar/Foo.hbm.xml</tt>
+ * which can be resolved as a classpath resource.
+ *
+ * @param persistentClass The mapped class
+ * @return this (for method chaining purposes)
+ * @throws MappingException Indicates problems locating the resource or
+ * processing the contained mapping document.
+ */
+ public Configuration addClass(Class persistentClass) throws MappingException {
+ String mappingResourceName = persistentClass.getName().replace( '.', '/' ) + ".hbm.xml";
+ log.info( "Reading mappings from resource: " + mappingResourceName );
+ return addResource( mappingResourceName, persistentClass.getClassLoader() );
+ }
+
+ /**
+ * Read all mappings from a jar file
+ * <p/>
+ * Assumes that any file named <tt>*.hbm.xml</tt> is a mapping document.
+ *
+ * @param jar a jar file
+ * @return this (for method chaining purposes)
+ * @throws MappingException Indicates problems reading the jar file or
+ * processing the contained mapping documents.
+ */
+ public Configuration addJar(File jar) throws MappingException {
+ log.info( "Searching for mapping documents in jar: " + jar.getName() );
+ JarFile jarFile = null;
+ try {
+ try {
+ jarFile = new JarFile( jar );
+ }
+ catch (IOException ioe) {
+ throw new InvalidMappingException(
+ "Could not read mapping documents from jar: " + jar.getName(), "jar", jar.getName(),
+ ioe
+ );
+ }
+ Enumeration jarEntries = jarFile.entries();
+ while ( jarEntries.hasMoreElements() ) {
+ ZipEntry ze = (ZipEntry) jarEntries.nextElement();
+ if ( ze.getName().endsWith( ".hbm.xml" ) ) {
+ log.info( "Found mapping document in jar: " + ze.getName() );
+ try {
+ addInputStream( jarFile.getInputStream( ze ) );
+ }
+ catch (Exception e) {
+ throw new InvalidMappingException(
+ "Could not read mapping documents from jar: " + jar.getName(),
+ "jar",
+ jar.getName(),
+ e
+ );
+ }
+ }
+ }
+ }
+ finally {
+ try {
+ if ( jarFile != null ) {
+ jarFile.close();
+ }
+ }
+ catch (IOException ioe) {
+ log.error("could not close jar", ioe);
+ }
+ }
+
+ return this;
+ }
+
+ /**
+ * Read all mapping documents from a directory tree.
+ * <p/>
+ * Assumes that any file named <tt>*.hbm.xml</tt> is a mapping document.
+ *
+ * @param dir The directory
+ * @return this (for method chaining purposes)
+ * @throws MappingException Indicates problems reading the jar file or
+ * processing the contained mapping documents.
+ */
+ public Configuration addDirectory(File dir) throws MappingException {
+ File[] files = dir.listFiles();
+ for ( int i = 0; i < files.length ; i++ ) {
+ if ( files[i].isDirectory() ) {
+ addDirectory( files[i] );
+ }
+ else if ( files[i].getName().endsWith( ".hbm.xml" ) ) {
+ addFile( files[i] );
+ }
+ }
+ return this;
+ }
+
+ protected void add(org.dom4j.Document doc) throws MappingException {
+ HbmBinder.bindRoot( doc, createMappings(), CollectionHelper.EMPTY_MAP );
+ }
+
+ /**
+ * Create a new <tt>Mappings</tt> to add class and collection
+ * mappings to.
+ */
+ public Mappings createMappings() {
+ return new Mappings(
+ classes,
+ collections,
+ tables,
+ namedQueries,
+ namedSqlQueries,
+ sqlResultSetMappings,
+ imports,
+ secondPasses,
+ propertyReferences,
+ namingStrategy,
+ typeDefs,
+ filterDefinitions,
+ extendsQueue,
+ auxiliaryDatabaseObjects,
+ tableNameBinding,
+ columnNameBindingPerTable
+ );
+ }
+
+
+ private Iterator iterateGenerators(Dialect dialect) throws MappingException {
+
+ TreeMap generators = new TreeMap();
+ String defaultCatalog = properties.getProperty( Environment.DEFAULT_CATALOG );
+ String defaultSchema = properties.getProperty( Environment.DEFAULT_SCHEMA );
+
+ Iterator iter = classes.values().iterator();
+ while ( iter.hasNext() ) {
+ PersistentClass pc = (PersistentClass) iter.next();
+
+ if ( !pc.isInherited() ) {
+
+ IdentifierGenerator ig = pc.getIdentifier()
+ .createIdentifierGenerator(
+ dialect,
+ defaultCatalog,
+ defaultSchema,
+ (RootClass) pc
+ );
+
+ if ( ig instanceof PersistentIdentifierGenerator ) {
+ generators.put( ( (PersistentIdentifierGenerator) ig ).generatorKey(), ig );
+ }
+
+ }
+ }
+
+ iter = collections.values().iterator();
+ while ( iter.hasNext() ) {
+ Collection collection = (Collection) iter.next();
+
+ if ( collection.isIdentified() ) {
+
+ IdentifierGenerator ig = ( (IdentifierCollection) collection ).getIdentifier()
+ .createIdentifierGenerator(
+ dialect,
+ defaultCatalog,
+ defaultSchema,
+ null
+ );
+
+ if ( ig instanceof PersistentIdentifierGenerator ) {
+ generators.put( ( (PersistentIdentifierGenerator) ig ).generatorKey(), ig );
+ }
+
+ }
+ }
+
+ return generators.values().iterator();
+ }
+
+ /**
+ * Generate DDL for dropping tables
+ *
+ * @see org.hibernate.tool.hbm2ddl.SchemaExport
+ */
+ public String[] generateDropSchemaScript(Dialect dialect) throws HibernateException {
+
+ secondPassCompile();
+
+ String defaultCatalog = properties.getProperty( Environment.DEFAULT_CATALOG );
+ String defaultSchema = properties.getProperty( Environment.DEFAULT_SCHEMA );
+
+ ArrayList script = new ArrayList( 50 );
+
+ // drop them in reverse order in case db needs it done that way...
+ ListIterator itr = auxiliaryDatabaseObjects.listIterator( auxiliaryDatabaseObjects.size() );
+ while ( itr.hasPrevious() ) {
+ AuxiliaryDatabaseObject object = (AuxiliaryDatabaseObject) itr.previous();
+ if ( object.appliesToDialect( dialect ) ) {
+ script.add( object.sqlDropString( dialect, defaultCatalog, defaultSchema ) );
+ }
+ }
+
+ if ( dialect.dropConstraints() ) {
+ Iterator iter = getTableMappings();
+ while ( iter.hasNext() ) {
+ Table table = (Table) iter.next();
+ if ( table.isPhysicalTable() ) {
+ Iterator subIter = table.getForeignKeyIterator();
+ while ( subIter.hasNext() ) {
+ ForeignKey fk = (ForeignKey) subIter.next();
+ if ( fk.isPhysicalConstraint() ) {
+ script.add(
+ fk.sqlDropString(
+ dialect,
+ defaultCatalog,
+ defaultSchema
+ )
+ );
+ }
+ }
+ }
+ }
+ }
+
+
+ Iterator iter = getTableMappings();
+ while ( iter.hasNext() ) {
+
+ Table table = (Table) iter.next();
+ if ( table.isPhysicalTable() ) {
+
+ /*Iterator subIter = table.getIndexIterator();
+ while ( subIter.hasNext() ) {
+ Index index = (Index) subIter.next();
+ if ( !index.isForeignKey() || !dialect.hasImplicitIndexForForeignKey() ) {
+ script.add( index.sqlDropString(dialect) );
+ }
+ }*/
+
+ script.add(
+ table.sqlDropString(
+ dialect,
+ defaultCatalog,
+ defaultSchema
+ )
+ );
+
+ }
+
+ }
+
+ iter = iterateGenerators( dialect );
+ while ( iter.hasNext() ) {
+ String[] lines = ( (PersistentIdentifierGenerator) iter.next() ).sqlDropStrings( dialect );
+ for ( int i = 0; i < lines.length ; i++ ) {
+ script.add( lines[i] );
+ }
+ }
+
+ return ArrayHelper.toStringArray( script );
+ }
+
+ /**
+ * Generate DDL for creating tables
+ *
+ * @see org.hibernate.tool.hbm2ddl.SchemaExport
+ */
+ public String[] generateSchemaCreationScript(Dialect dialect) throws HibernateException {
+ secondPassCompile();
+
+ ArrayList script = new ArrayList( 50 );
+ String defaultCatalog = properties.getProperty( Environment.DEFAULT_CATALOG );
+ String defaultSchema = properties.getProperty( Environment.DEFAULT_SCHEMA );
+
+ Iterator iter = getTableMappings();
+ while ( iter.hasNext() ) {
+ Table table = (Table) iter.next();
+ if ( table.isPhysicalTable() ) {
+ script.add(
+ table.sqlCreateString(
+ dialect,
+ mapping,
+ defaultCatalog,
+ defaultSchema
+ )
+ );
+ Iterator comments = table.sqlCommentStrings( dialect, defaultCatalog, defaultSchema );
+ while ( comments.hasNext() ) {
+ script.add( comments.next() );
+ }
+ }
+ }
+
+ iter = getTableMappings();
+ while ( iter.hasNext() ) {
+ Table table = (Table) iter.next();
+ if ( table.isPhysicalTable() ) {
+
+ if ( !dialect.supportsUniqueConstraintInCreateAlterTable() ) {
+ Iterator subIter = table.getUniqueKeyIterator();
+ while ( subIter.hasNext() ) {
+ UniqueKey uk = (UniqueKey) subIter.next();
+ String constraintString = uk.sqlCreateString( dialect, mapping, defaultCatalog, defaultSchema );
+ if (constraintString != null) script.add( constraintString );
+ }
+ }
+
+
+ Iterator subIter = table.getIndexIterator();
+ while ( subIter.hasNext() ) {
+ Index index = (Index) subIter.next();
+ script.add(
+ index.sqlCreateString(
+ dialect,
+ mapping,
+ defaultCatalog,
+ defaultSchema
+ )
+ );
+ }
+
+ if ( dialect.hasAlterTable() ) {
+ subIter = table.getForeignKeyIterator();
+ while ( subIter.hasNext() ) {
+ ForeignKey fk = (ForeignKey) subIter.next();
+ if ( fk.isPhysicalConstraint() ) {
+ script.add(
+ fk.sqlCreateString(
+ dialect, mapping,
+ defaultCatalog,
+ defaultSchema
+ )
+ );
+ }
+ }
+ }
+
+ }
+ }
+
+ iter = iterateGenerators( dialect );
+ while ( iter.hasNext() ) {
+ String[] lines = ( (PersistentIdentifierGenerator) iter.next() ).sqlCreateStrings( dialect );
+ for ( int i = 0; i < lines.length ; i++ ) {
+ script.add( lines[i] );
+ }
+ }
+
+ Iterator itr = auxiliaryDatabaseObjects.iterator();
+ while ( itr.hasNext() ) {
+ AuxiliaryDatabaseObject object = (AuxiliaryDatabaseObject) itr.next();
+ if ( object.appliesToDialect( dialect ) ) {
+ script.add( object.sqlCreateString( dialect, mapping, defaultCatalog, defaultSchema ) );
+ }
+ }
+
+ return ArrayHelper.toStringArray( script );
+ }
+
+ /**
+ * Generate DDL for altering tables
+ *
+ * @see org.hibernate.tool.hbm2ddl.SchemaUpdate
+ */
+ public String[] generateSchemaUpdateScript(Dialect dialect, DatabaseMetadata databaseMetadata)
+ throws HibernateException {
+ secondPassCompile();
+
+ String defaultCatalog = properties.getProperty( Environment.DEFAULT_CATALOG );
+ String defaultSchema = properties.getProperty( Environment.DEFAULT_SCHEMA );
+
+ ArrayList script = new ArrayList( 50 );
+
+ Iterator iter = getTableMappings();
+ while ( iter.hasNext() ) {
+ Table table = (Table) iter.next();
+ if ( table.isPhysicalTable() ) {
+
+ TableMetadata tableInfo = databaseMetadata.getTableMetadata(
+ table.getName(),
+ ( table.getSchema() == null ) ? defaultSchema : table.getSchema(),
+ ( table.getCatalog() == null ) ? defaultCatalog : table.getCatalog(),
+ table.isQuoted()
+
+ );
+ if ( tableInfo == null ) {
+ script.add(
+ table.sqlCreateString(
+ dialect,
+ mapping,
+ defaultCatalog,
+ defaultSchema
+ )
+ );
+ }
+ else {
+ Iterator subiter = table.sqlAlterStrings(
+ dialect,
+ mapping,
+ tableInfo,
+ defaultCatalog,
+ defaultSchema
+ );
+ while ( subiter.hasNext() ) {
+ script.add( subiter.next() );
+ }
+ }
+
+ Iterator comments = table.sqlCommentStrings( dialect, defaultCatalog, defaultSchema );
+ while ( comments.hasNext() ) {
+ script.add( comments.next() );
+ }
+
+ }
+ }
+
+ iter = getTableMappings();
+ while ( iter.hasNext() ) {
+ Table table = (Table) iter.next();
+ if ( table.isPhysicalTable() ) {
+
+ TableMetadata tableInfo = databaseMetadata.getTableMetadata(
+ table.getName(),
+ table.getSchema(),
+ table.getCatalog(),
+ table.isQuoted()
+ );
+
+ if ( dialect.hasAlterTable() ) {
+ Iterator subIter = table.getForeignKeyIterator();
+ while ( subIter.hasNext() ) {
+ ForeignKey fk = (ForeignKey) subIter.next();
+ if ( fk.isPhysicalConstraint() ) {
+ boolean create = tableInfo == null || (
+ tableInfo.getForeignKeyMetadata( fk.getName() ) == null && (
+ //Icky workaround for MySQL bug:
+ !( dialect instanceof MySQLDialect ) ||
+ tableInfo.getIndexMetadata( fk.getName() ) == null
+ )
+ );
+ if ( create ) {
+ script.add(
+ fk.sqlCreateString(
+ dialect,
+ mapping,
+ defaultCatalog,
+ defaultSchema
+ )
+ );
+ }
+ }
+ }
+ }
+
+ }
+
+ /*//broken, 'cos we don't generate these with names in SchemaExport
+ subIter = table.getIndexIterator();
+ while ( subIter.hasNext() ) {
+ Index index = (Index) subIter.next();
+ if ( !index.isForeignKey() || !dialect.hasImplicitIndexForForeignKey() ) {
+ if ( tableInfo==null || tableInfo.getIndexMetadata( index.getFilterName() ) == null ) {
+ script.add( index.sqlCreateString(dialect, mapping) );
+ }
+ }
+ }
+ //broken, 'cos we don't generate these with names in SchemaExport
+ subIter = table.getUniqueKeyIterator();
+ while ( subIter.hasNext() ) {
+ UniqueKey uk = (UniqueKey) subIter.next();
+ if ( tableInfo==null || tableInfo.getIndexMetadata( uk.getFilterName() ) == null ) {
+ script.add( uk.sqlCreateString(dialect, mapping) );
+ }
+ }*/
+ }
+
+ iter = iterateGenerators( dialect );
+ while ( iter.hasNext() ) {
+ PersistentIdentifierGenerator generator = (PersistentIdentifierGenerator) iter.next();
+ Object key = generator.generatorKey();
+ if ( !databaseMetadata.isSequence( key ) && !databaseMetadata.isTable( key ) ) {
+ String[] lines = generator.sqlCreateStrings( dialect );
+ for ( int i = 0; i < lines.length ; i++ ) {
+ script.add( lines[i] );
+ }
+ }
+ }
+
+ return ArrayHelper.toStringArray( script );
+ }
+
+ public void validateSchema(Dialect dialect, DatabaseMetadata databaseMetadata)
+ throws HibernateException {
+ secondPassCompile();
+
+ String defaultCatalog = properties.getProperty( Environment.DEFAULT_CATALOG );
+ String defaultSchema = properties.getProperty( Environment.DEFAULT_SCHEMA );
+
+ Iterator iter = getTableMappings();
+ while ( iter.hasNext() ) {
+ Table table = (Table) iter.next();
+ if ( table.isPhysicalTable() ) {
+
+
+ TableMetadata tableInfo = databaseMetadata.getTableMetadata(
+ table.getName(),
+ ( table.getSchema() == null ) ? defaultSchema : table.getSchema(),
+ ( table.getCatalog() == null ) ? defaultCatalog : table.getCatalog(),
+ table.isQuoted());
+ if ( tableInfo == null ) {
+ throw new HibernateException( "Missing table: " + table.getName() );
+ }
+ else {
+ table.validateColumns( dialect, mapping, tableInfo );
+ }
+
+ }
+ }
+
+ iter = iterateGenerators( dialect );
+ while ( iter.hasNext() ) {
+ PersistentIdentifierGenerator generator = (PersistentIdentifierGenerator) iter.next();
+ Object key = generator.generatorKey();
+ if ( !databaseMetadata.isSequence( key ) && !databaseMetadata.isTable( key ) ) {
+ throw new HibernateException( "Missing sequence or table: " + key );
+ }
+ }
+ }
+
+ private void validate() throws MappingException {
+ Iterator iter = classes.values().iterator();
+ while ( iter.hasNext() ) {
+ ( (PersistentClass) iter.next() ).validate( mapping );
+ }
+ iter = collections.values().iterator();
+ while ( iter.hasNext() ) {
+ ( (Collection) iter.next() ).validate( mapping );
+ }
+ }
+
+ /**
+ * Call this to ensure the mappings are fully compiled/built. Usefull to ensure getting
+ * access to all information in the metamodel when calling e.g. getClassMappings().
+ */
+ public void buildMappings() {
+ secondPassCompile();
+ }
+
+ // This method may be called many times!!
+ protected void secondPassCompile() throws MappingException {
+ log.debug( "processing extends queue" );
+
+ processExtendsQueue();
+
+ log.debug( "processing collection mappings" );
+
+ Iterator iter = secondPasses.iterator();
+ while ( iter.hasNext() ) {
+ SecondPass sp = (SecondPass) iter.next();
+ if ( ! (sp instanceof QuerySecondPass) ) {
+ sp.doSecondPass( classes );
+ iter.remove();
+ }
+ }
+
+ log.debug( "processing native query and ResultSetMapping mappings" );
+ iter = secondPasses.iterator();
+ while ( iter.hasNext() ) {
+ SecondPass sp = (SecondPass) iter.next();
+ sp.doSecondPass( classes );
+ iter.remove();
+ }
+
+ log.debug( "processing association property references" );
+
+ iter = propertyReferences.iterator();
+ while ( iter.hasNext() ) {
+ Mappings.PropertyReference upr = (Mappings.PropertyReference) iter.next();
+
+ PersistentClass clazz = getClassMapping( upr.referencedClass );
+ if ( clazz == null ) {
+ throw new MappingException(
+ "property-ref to unmapped class: " +
+ upr.referencedClass
+ );
+ }
+
+ Property prop = clazz.getReferencedProperty( upr.propertyName );
+ if ( upr.unique ) {
+ ( (SimpleValue) prop.getValue() ).setAlternateUniqueKey( true );
+ }
+ }
+
+ //TODO: Somehow add the newly created foreign keys to the internal collection
+
+ log.debug( "processing foreign key constraints" );
+
+ iter = getTableMappings();
+ Set done = new HashSet();
+ while ( iter.hasNext() ) {
+ secondPassCompileForeignKeys( (Table) iter.next(), done );
+ }
+
+ }
+
+ /**
+ * Try to empty the extends queue.
+ */
+ private void processExtendsQueue() {
+ // todo : would love to have this work on a notification basis
+ // where the successful binding of an entity/subclass would
+ // emit a notification which the extendsQueue entries could
+ // react to...
+ org.dom4j.Document document = findPossibleExtends();
+ while ( document != null ) {
+ add( document );
+ document = findPossibleExtends();
+ }
+
+ if ( extendsQueue.size() > 0 ) {
+// Iterator iterator = extendsQueue.iterator();
+ Iterator iterator = extendsQueue.keySet().iterator();
+ StringBuffer buf = new StringBuffer( "Following superclasses referenced in extends not found: " );
+ while ( iterator.hasNext() ) {
+ final ExtendsQueueEntry entry = ( ExtendsQueueEntry ) iterator.next();
+ buf.append( entry.getExplicitName() );
+ if ( entry.getMappingPackage() != null ) {
+ buf.append( "[" ).append( entry.getMappingPackage() ).append( "]" );
+ }
+ if ( iterator.hasNext() ) {
+ buf.append( "," );
+ }
+ }
+ throw new MappingException( buf.toString() );
+ }
+ }
+
+ /**
+ * Find the first possible element in the queue of extends.
+ */
+ protected org.dom4j.Document findPossibleExtends() {
+// Iterator iter = extendsQueue.iterator();
+ Iterator iter = extendsQueue.keySet().iterator();
+ while ( iter.hasNext() ) {
+ final ExtendsQueueEntry entry = ( ExtendsQueueEntry ) iter.next();
+ if ( getClassMapping( entry.getExplicitName() ) != null ) {
+ // found
+ iter.remove();
+ return entry.getDocument();
+ }
+ else if ( getClassMapping( HbmBinder.getClassName( entry.getExplicitName(), entry.getMappingPackage() ) ) != null ) {
+ // found
+ iter.remove();
+ return entry.getDocument();
+ }
+ }
+ return null;
+ }
+
+ protected void secondPassCompileForeignKeys(Table table, Set done) throws MappingException {
+
+ table.createForeignKeys();
+
+ Iterator iter = table.getForeignKeyIterator();
+ while ( iter.hasNext() ) {
+
+ ForeignKey fk = (ForeignKey) iter.next();
+ if ( !done.contains( fk ) ) {
+ done.add( fk );
+ final String referencedEntityName = fk.getReferencedEntityName();
+ if ( referencedEntityName == null ) {
+ throw new MappingException(
+ "An association from the table " +
+ fk.getTable().getName() +
+ " does not specify the referenced entity"
+ );
+ }
+ if ( log.isDebugEnabled() ) {
+ log.debug( "resolving reference to class: " + referencedEntityName );
+ }
+ PersistentClass referencedClass = (PersistentClass) classes.get( referencedEntityName );
+ if ( referencedClass == null ) {
+ throw new MappingException(
+ "An association from the table " +
+ fk.getTable().getName() +
+ " refers to an unmapped class: " +
+ referencedEntityName
+ );
+ }
+ if ( referencedClass.isJoinedSubclass() ) {
+ secondPassCompileForeignKeys( referencedClass.getSuperclass().getTable(), done );
+ }
+ fk.setReferencedTable( referencedClass.getTable() );
+ fk.alignColumns();
+ }
+ }
+ }
+
+ /**
+ * Get the named queries
+ */
+ public Map getNamedQueries() {
+ return namedQueries;
+ }
+
+ /**
+ * Instantiate a new <tt>SessionFactory</tt>, using the properties and
+ * mappings in this configuration. The <tt>SessionFactory</tt> will be
+ * immutable, so changes made to the <tt>Configuration</tt> after
+ * building the <tt>SessionFactory</tt> will not affect it.
+ *
+ * @return a new factory for <tt>Session</tt>s
+ * @see org.hibernate.SessionFactory
+ */
+ public SessionFactory buildSessionFactory() throws HibernateException {
+ log.debug( "Preparing to build session factory with filters : " + filterDefinitions );
+ secondPassCompile();
+ validate();
+ Environment.verifyProperties( properties );
+ Properties copy = new Properties();
+ copy.putAll( properties );
+ PropertiesHelper.resolvePlaceHolders( copy );
+ Settings settings = buildSettings( copy );
+
+ return new SessionFactoryImpl(
+ this,
+ mapping,
+ settings,
+ getInitializedEventListeners()
+ );
+ }
+
+ private EventListeners getInitializedEventListeners() {
+ EventListeners result = (EventListeners) eventListeners.shallowCopy();
+ result.initializeListeners( this );
+ return result;
+ }
+
+ /**
+ * Return the configured <tt>Interceptor</tt>
+ */
+ public Interceptor getInterceptor() {
+ return interceptor;
+ }
+
+ /**
+ * Get all properties
+ */
+ public Properties getProperties() {
+ return properties;
+ }
+
+ /**
+ * Configure an <tt>Interceptor</tt>
+ */
+ public Configuration setInterceptor(Interceptor interceptor) {
+ this.interceptor = interceptor;
+ return this;
+ }
+
+ /**
+ * Specify a completely new set of properties
+ */
+ public Configuration setProperties(Properties properties) {
+ this.properties = properties;
+ return this;
+ }
+
+ /**
+ * Set the given properties
+ */
+ public Configuration addProperties(Properties extraProperties) {
+ this.properties.putAll( extraProperties );
+ return this;
+ }
+
+ /**
+ * Adds the incoming properties to the internap properties structure,
+ * as long as the internal structure does not already contain an
+ * entry for the given key.
+ *
+ * @param properties
+ * @return this
+ */
+ public Configuration mergeProperties(Properties properties) {
+ Iterator itr = properties.entrySet().iterator();
+ while ( itr.hasNext() ) {
+ final Map.Entry entry = ( Map.Entry ) itr.next();
+ if ( this.properties.containsKey( entry.getKey() ) ) {
+ continue;
+ }
+ this.properties.setProperty( ( String ) entry.getKey(), ( String ) entry.getValue() );
+ }
+ return this;
+ }
+
+ /**
+ * Set a property
+ */
+ public Configuration setProperty(String propertyName, String value) {
+ properties.setProperty( propertyName, value );
+ return this;
+ }
+
+ /**
+ * Get a property
+ */
+ public String getProperty(String propertyName) {
+ return properties.getProperty( propertyName );
+ }
+
+ private void addProperties(Element parent) {
+ Iterator iter = parent.elementIterator( "property" );
+ while ( iter.hasNext() ) {
+ Element node = (Element) iter.next();
+ String name = node.attributeValue( "name" );
+ String value = node.getText().trim();
+ log.debug( name + "=" + value );
+ properties.setProperty( name, value );
+ if ( !name.startsWith( "hibernate" ) ) {
+ properties.setProperty( "hibernate." + name, value );
+ }
+ }
+ Environment.verifyProperties( properties );
+ }
+
+ /**
+ * Get the configuration file as an <tt>InputStream</tt>. Might be overridden
+ * by subclasses to allow the configuration to be located by some arbitrary
+ * mechanism.
+ */
+ protected InputStream getConfigurationInputStream(String resource) throws HibernateException {
+
+ log.info( "Configuration resource: " + resource );
+
+ return ConfigHelper.getResourceAsStream( resource );
+
+ }
+
+ /**
+ * Use the mappings and properties specified in an application
+ * resource named <tt>hibernate.cfg.xml</tt>.
+ */
+ public Configuration configure() throws HibernateException {
+ configure( "/hibernate.cfg.xml" );
+ return this;
+ }
+
+ /**
+ * Use the mappings and properties specified in the given application
+ * resource. The format of the resource is defined in
+ * <tt>hibernate-configuration-3.0.dtd</tt>.
+ * <p/>
+ * The resource is found via <tt>getConfigurationInputStream(resource)</tt>.
+ */
+ public Configuration configure(String resource) throws HibernateException {
+ log.info( "configuring from resource: " + resource );
+ InputStream stream = getConfigurationInputStream( resource );
+ return doConfigure( stream, resource );
+ }
+
+ /**
+ * Use the mappings and properties specified in the given document.
+ * The format of the document is defined in
+ * <tt>hibernate-configuration-3.0.dtd</tt>.
+ *
+ * @param url URL from which you wish to load the configuration
+ * @return A configuration configured via the file
+ * @throws HibernateException
+ */
+ public Configuration configure(URL url) throws HibernateException {
+ log.info( "configuring from url: " + url.toString() );
+ try {
+ return doConfigure( url.openStream(), url.toString() );
+ }
+ catch (IOException ioe) {
+ throw new HibernateException( "could not configure from URL: " + url, ioe );
+ }
+ }
+
+ /**
+ * Use the mappings and properties specified in the given application
+ * file. The format of the file is defined in
+ * <tt>hibernate-configuration-3.0.dtd</tt>.
+ *
+ * @param configFile <tt>File</tt> from which you wish to load the configuration
+ * @return A configuration configured via the file
+ * @throws HibernateException
+ */
+ public Configuration configure(File configFile) throws HibernateException {
+ log.info( "configuring from file: " + configFile.getName() );
+ try {
+ return doConfigure( new FileInputStream( configFile ), configFile.toString() );
+ }
+ catch (FileNotFoundException fnfe) {
+ throw new HibernateException( "could not find file: " + configFile, fnfe );
+ }
+ }
+
+ /**
+ * Use the mappings and properties specified in the given application
+ * resource. The format of the resource is defined in
+ * <tt>hibernate-configuration-3.0.dtd</tt>.
+ *
+ * @param stream Inputstream to be read from
+ * @param resourceName The name to use in warning/error messages
+ * @return A configuration configured via the stream
+ * @throws HibernateException
+ */
+ protected Configuration doConfigure(InputStream stream, String resourceName) throws HibernateException {
+
+ org.dom4j.Document doc;
+ try {
+ List errors = new ArrayList();
+ doc = xmlHelper.createSAXReader( resourceName, errors, entityResolver )
+ .read( new InputSource( stream ) );
+ if ( errors.size() != 0 ) {
+ throw new MappingException(
+ "invalid configuration",
+ (Throwable) errors.get( 0 )
+ );
+ }
+ }
+ catch (DocumentException e) {
+ throw new HibernateException(
+ "Could not parse configuration: " + resourceName,
+ e
+ );
+ }
+ finally {
+ try {
+ stream.close();
+ }
+ catch (IOException ioe) {
+ log.warn( "could not close input stream for: " + resourceName, ioe );
+ }
+ }
+
+ return doConfigure( doc );
+
+ }
+
+ /**
+ * Use the mappings and properties specified in the given XML document.
+ * The format of the file is defined in
+ * <tt>hibernate-configuration-3.0.dtd</tt>.
+ *
+ * @param document an XML document from which you wish to load the configuration
+ * @return A configuration configured via the <tt>Document</tt>
+ * @throws HibernateException if there is problem in accessing the file.
+ */
+ public Configuration configure(Document document) throws HibernateException {
+ log.info( "configuring from XML document" );
+ return doConfigure( xmlHelper.createDOMReader().read( document ) );
+ }
+
+ protected Configuration doConfigure(org.dom4j.Document doc) throws HibernateException {
+
+ Element sfNode = doc.getRootElement().element( "session-factory" );
+ String name = sfNode.attributeValue( "name" );
+ if ( name != null ) {
+ properties.setProperty( Environment.SESSION_FACTORY_NAME, name );
+ }
+ addProperties( sfNode );
+ parseSessionFactory( sfNode, name );
+
+ Element secNode = doc.getRootElement().element( "security" );
+ if ( secNode != null ) {
+ parseSecurity( secNode );
+ }
+
+ log.info( "Configured SessionFactory: " + name );
+ log.debug( "properties: " + properties );
+
+ return this;
+
+ }
+
+
+ private void parseSessionFactory(Element sfNode, String name) {
+ Iterator elements = sfNode.elementIterator();
+ while ( elements.hasNext() ) {
+ Element subelement = (Element) elements.next();
+ String subelementName = subelement.getName();
+ if ( "mapping".equals( subelementName ) ) {
+ parseMappingElement( subelement, name );
+ }
+ else if ( "class-cache".equals( subelementName ) ) {
+ String className = subelement.attributeValue( "class" );
+ Attribute regionNode = subelement.attribute( "region" );
+ final String region = ( regionNode == null ) ? className : regionNode.getValue();
+ boolean includeLazy = !"non-lazy".equals( subelement.attributeValue( "include" ) );
+ setCacheConcurrencyStrategy( className, subelement.attributeValue( "usage" ), region, includeLazy );
+ }
+ else if ( "collection-cache".equals( subelementName ) ) {
+ String role = subelement.attributeValue( "collection" );
+ Attribute regionNode = subelement.attribute( "region" );
+ final String region = ( regionNode == null ) ? role : regionNode.getValue();
+ setCollectionCacheConcurrencyStrategy( role, subelement.attributeValue( "usage" ), region );
+ }
+ else if ( "listener".equals( subelementName ) ) {
+ parseListener( subelement );
+ }
+ else if ( "event".equals( subelementName ) ) {
+ parseEvent( subelement );
+ }
+ }
+ }
+
+ protected void parseMappingElement(Element subelement, String name) {
+ Attribute rsrc = subelement.attribute( "resource" );
+ Attribute file = subelement.attribute( "file" );
+ Attribute jar = subelement.attribute( "jar" );
+ Attribute pkg = subelement.attribute( "package" );
+ Attribute clazz = subelement.attribute( "class" );
+ if ( rsrc != null ) {
+ log.debug( name + "<-" + rsrc );
+ addResource( rsrc.getValue() );
+ }
+ else if ( jar != null ) {
+ log.debug( name + "<-" + jar );
+ addJar( new File( jar.getValue() ) );
+ }
+ else if ( pkg != null ) {
+ throw new MappingException(
+ "An AnnotationConfiguration instance is required to use <mapping package=\"" +
+ pkg.getValue() + "\"/>"
+ );
+ }
+ else if ( clazz != null ) {
+ throw new MappingException(
+ "An AnnotationConfiguration instance is required to use <mapping class=\"" +
+ clazz.getValue() + "\"/>"
+ );
+ }
+ else {
+ if ( file == null ) {
+ throw new MappingException(
+ "<mapping> element in configuration specifies no attributes"
+ );
+ }
+ log.debug( name + "<-" + file );
+ addFile( file.getValue() );
+ }
+ }
+
+ private void parseSecurity(Element secNode) {
+ String contextId = secNode.attributeValue( "context" );
+ setProperty(Environment.JACC_CONTEXTID, contextId);
+ log.info( "JACC contextID: " + contextId );
+ JACCConfiguration jcfg = new JACCConfiguration( contextId );
+ Iterator grantElements = secNode.elementIterator();
+ while ( grantElements.hasNext() ) {
+ Element grantElement = (Element) grantElements.next();
+ String elementName = grantElement.getName();
+ if ( "grant".equals( elementName ) ) {
+ jcfg.addPermission(
+ grantElement.attributeValue( "role" ),
+ grantElement.attributeValue( "entity-name" ),
+ grantElement.attributeValue( "actions" )
+ );
+ }
+ }
+ }
+
+ private void parseEvent(Element element) {
+ String type = element.attributeValue( "type" );
+ List listeners = element.elements();
+ String[] listenerClasses = new String[ listeners.size() ];
+ for ( int i = 0; i < listeners.size() ; i++ ) {
+ listenerClasses[i] = ( (Element) listeners.get( i ) ).attributeValue( "class" );
+ }
+ log.debug( "Event listeners: " + type + "=" + StringHelper.toString( listenerClasses ) );
+ setListeners( type, listenerClasses );
+ }
+
+ private void parseListener(Element element) {
+ String type = element.attributeValue( "type" );
+ if ( type == null ) {
+ throw new MappingException( "No type specified for listener" );
+ }
+ String impl = element.attributeValue( "class" );
+ log.debug( "Event listener: " + type + "=" + impl );
+ setListeners( type, new String[]{impl} );
+ }
+
+ public void setListeners(String type, String[] listenerClasses) {
+ Object[] listeners = (Object[]) Array.newInstance( eventListeners.getListenerClassFor(type), listenerClasses.length );
+ for ( int i = 0; i < listeners.length ; i++ ) {
+ try {
+ listeners[i] = ReflectHelper.classForName( listenerClasses[i] ).newInstance();
+ }
+ catch (Exception e) {
+ throw new MappingException(
+ "Unable to instantiate specified event (" + type + ") listener class: " + listenerClasses[i],
+ e
+ );
+ }
+ }
+ setListeners( type, listeners );
+ }
+
+ public void setListener(String type, Object listener) {
+ if ( listener == null ) {
+ setListener( type, null );
+ }
+ else {
+ Object[] listeners = (Object[]) Array.newInstance( eventListeners.getListenerClassFor(type), 1 );
+ listeners[0] = listener;
+ setListeners( type, listeners );
+ }
+ }
+
+ public void setListeners(String type, Object[] listeners) {
+ if ( "auto-flush".equals( type ) ) {
+ if ( listeners == null ) {
+ eventListeners.setAutoFlushEventListeners( new AutoFlushEventListener[]{} );
+ }
+ else {
+ eventListeners.setAutoFlushEventListeners( (AutoFlushEventListener[]) listeners );
+ }
+ }
+ else if ( "merge".equals( type ) ) {
+ if ( listeners == null ) {
+ eventListeners.setMergeEventListeners( new MergeEventListener[]{} );
+ }
+ else {
+ eventListeners.setMergeEventListeners( (MergeEventListener[]) listeners );
+ }
+ }
+ else if ( "create".equals( type ) ) {
+ if ( listeners == null ) {
+ eventListeners.setPersistEventListeners( new PersistEventListener[]{} );
+ }
+ else {
+ eventListeners.setPersistEventListeners( (PersistEventListener[]) listeners );
+ }
+ }
+ else if ( "create-onflush".equals( type ) ) {
+ if ( listeners == null ) {
+ eventListeners.setPersistOnFlushEventListeners( new PersistEventListener[]{} );
+ }
+ else {
+ eventListeners.setPersistOnFlushEventListeners( (PersistEventListener[]) listeners );
+ }
+ }
+ else if ( "delete".equals( type ) ) {
+ if ( listeners == null ) {
+ eventListeners.setDeleteEventListeners( new DeleteEventListener[]{} );
+ }
+ else {
+ eventListeners.setDeleteEventListeners( (DeleteEventListener[]) listeners );
+ }
+ }
+ else if ( "dirty-check".equals( type ) ) {
+ if ( listeners == null ) {
+ eventListeners.setDirtyCheckEventListeners( new DirtyCheckEventListener[]{} );
+ }
+ else {
+ eventListeners.setDirtyCheckEventListeners( (DirtyCheckEventListener[]) listeners );
+ }
+ }
+ else if ( "evict".equals( type ) ) {
+ if ( listeners == null ) {
+ eventListeners.setEvictEventListeners( new EvictEventListener[]{} );
+ }
+ else {
+ eventListeners.setEvictEventListeners( (EvictEventListener[]) listeners );
+ }
+ }
+ else if ( "flush".equals( type ) ) {
+ if ( listeners == null ) {
+ eventListeners.setFlushEventListeners( new FlushEventListener[]{} );
+ }
+ else {
+ eventListeners.setFlushEventListeners( (FlushEventListener[]) listeners );
+ }
+ }
+ else if ( "flush-entity".equals( type ) ) {
+ if ( listeners == null ) {
+ eventListeners.setFlushEntityEventListeners( new FlushEntityEventListener[]{} );
+ }
+ else {
+ eventListeners.setFlushEntityEventListeners( (FlushEntityEventListener[]) listeners );
+ }
+ }
+ else if ( "load".equals( type ) ) {
+ if ( listeners == null ) {
+ eventListeners.setLoadEventListeners( new LoadEventListener[]{} );
+ }
+ else {
+ eventListeners.setLoadEventListeners( (LoadEventListener[]) listeners );
+ }
+ }
+ else if ( "load-collection".equals( type ) ) {
+ if ( listeners == null ) {
+ eventListeners.setInitializeCollectionEventListeners(
+ new InitializeCollectionEventListener[]{}
+ );
+ }
+ else {
+ eventListeners.setInitializeCollectionEventListeners(
+ (InitializeCollectionEventListener[]) listeners
+ );
+ }
+ }
+ else if ( "lock".equals( type ) ) {
+ if ( listeners == null ) {
+ eventListeners.setLockEventListeners( new LockEventListener[]{} );
+ }
+ else {
+ eventListeners.setLockEventListeners( (LockEventListener[]) listeners );
+ }
+ }
+ else if ( "refresh".equals( type ) ) {
+ if ( listeners == null ) {
+ eventListeners.setRefreshEventListeners( new RefreshEventListener[]{} );
+ }
+ else {
+ eventListeners.setRefreshEventListeners( (RefreshEventListener[]) listeners );
+ }
+ }
+ else if ( "replicate".equals( type ) ) {
+ if ( listeners == null ) {
+ eventListeners.setReplicateEventListeners( new ReplicateEventListener[]{} );
+ }
+ else {
+ eventListeners.setReplicateEventListeners( (ReplicateEventListener[]) listeners );
+ }
+ }
+ else if ( "save-update".equals( type ) ) {
+ if ( listeners == null ) {
+ eventListeners.setSaveOrUpdateEventListeners( new SaveOrUpdateEventListener[]{} );
+ }
+ else {
+ eventListeners.setSaveOrUpdateEventListeners( (SaveOrUpdateEventListener[]) listeners );
+ }
+ }
+ else if ( "save".equals( type ) ) {
+ if ( listeners == null ) {
+ eventListeners.setSaveEventListeners( new SaveOrUpdateEventListener[]{} );
+ }
+ else {
+ eventListeners.setSaveEventListeners( (SaveOrUpdateEventListener[]) listeners );
+ }
+ }
+ else if ( "update".equals( type ) ) {
+ if ( listeners == null ) {
+ eventListeners.setUpdateEventListeners( new SaveOrUpdateEventListener[]{} );
+ }
+ else {
+ eventListeners.setUpdateEventListeners( (SaveOrUpdateEventListener[]) listeners );
+ }
+ }
+ else if ( "pre-load".equals( type ) ) {
+ if ( listeners == null ) {
+ eventListeners.setPreLoadEventListeners( new PreLoadEventListener[]{} );
+ }
+ else {
+ eventListeners.setPreLoadEventListeners( (PreLoadEventListener[]) listeners );
+ }
+ }
+ else if ( "pre-update".equals( type ) ) {
+ if ( listeners == null ) {
+ eventListeners.setPreUpdateEventListeners( new PreUpdateEventListener[]{} );
+ }
+ else {
+ eventListeners.setPreUpdateEventListeners( (PreUpdateEventListener[]) listeners );
+ }
+ }
+ else if ( "pre-delete".equals( type ) ) {
+ if ( listeners == null ) {
+ eventListeners.setPreDeleteEventListeners( new PreDeleteEventListener[]{} );
+ }
+ else {
+ eventListeners.setPreDeleteEventListeners( (PreDeleteEventListener[]) listeners );
+ }
+ }
+ else if ( "pre-insert".equals( type ) ) {
+ if ( listeners == null ) {
+ eventListeners.setPreInsertEventListeners( new PreInsertEventListener[]{} );
+ }
+ else {
+ eventListeners.setPreInsertEventListeners( (PreInsertEventListener[]) listeners );
+ }
+ }
+ else if ( "post-load".equals( type ) ) {
+ if ( listeners == null ) {
+ eventListeners.setPostLoadEventListeners( new PostLoadEventListener[]{} );
+ }
+ else {
+ eventListeners.setPostLoadEventListeners( (PostLoadEventListener[]) listeners );
+ }
+ }
+ else if ( "post-update".equals( type ) ) {
+ if ( listeners == null ) {
+ eventListeners.setPostUpdateEventListeners( new PostUpdateEventListener[]{} );
+ }
+ else {
+ eventListeners.setPostUpdateEventListeners( (PostUpdateEventListener[]) listeners );
+ }
+ }
+ else if ( "post-delete".equals( type ) ) {
+ if ( listeners == null ) {
+ eventListeners.setPostDeleteEventListeners( new PostDeleteEventListener[]{} );
+ }
+ else {
+ eventListeners.setPostDeleteEventListeners( (PostDeleteEventListener[]) listeners );
+ }
+ }
+ else if ( "post-insert".equals( type ) ) {
+ if ( listeners == null ) {
+ eventListeners.setPostInsertEventListeners( new PostInsertEventListener[]{} );
+ }
+ else {
+ eventListeners.setPostInsertEventListeners( (PostInsertEventListener[]) listeners );
+ }
+ }
+ else if ( "post-commit-update".equals( type ) ) {
+ if ( listeners == null ) {
+ eventListeners.setPostCommitUpdateEventListeners(
+ new PostUpdateEventListener[]{}
+ );
+ }
+ else {
+ eventListeners.setPostCommitUpdateEventListeners( (PostUpdateEventListener[]) listeners );
+ }
+ }
+ else if ( "post-commit-delete".equals( type ) ) {
+ if ( listeners == null ) {
+ eventListeners.setPostCommitDeleteEventListeners(
+ new PostDeleteEventListener[]{}
+ );
+ }
+ else {
+ eventListeners.setPostCommitDeleteEventListeners( (PostDeleteEventListener[]) listeners );
+ }
+ }
+ else if ( "post-commit-insert".equals( type ) ) {
+ if ( listeners == null ) {
+ eventListeners.setPostCommitInsertEventListeners(
+ new PostInsertEventListener[]{}
+ );
+ }
+ else {
+ eventListeners.setPostCommitInsertEventListeners( (PostInsertEventListener[]) listeners );
+ }
+ }
+ else {
+ log.warn( "Unrecognized listener type [" + type + "]" );
+ }
+ }
+
+ public EventListeners getEventListeners() {
+ return eventListeners;
+ }
+
+ RootClass getRootClassMapping(String clazz) throws MappingException {
+ try {
+ return (RootClass) getClassMapping( clazz );
+ }
+ catch (ClassCastException cce) {
+ throw new MappingException( "You may only specify a cache for root <class> mappings" );
+ }
+ }
+
+ /**
+ * Set up a cache for an entity class
+ *
+ * @param clazz
+ * @param concurrencyStrategy
+ * @return Configuration
+ * @throws MappingException
+ */
+ public Configuration setCacheConcurrencyStrategy(String clazz, String concurrencyStrategy)
+ throws MappingException {
+ setCacheConcurrencyStrategy( clazz, concurrencyStrategy, clazz );
+ return this;
+ }
+
+ public void setCacheConcurrencyStrategy(String clazz, String concurrencyStrategy, String region)
+ throws MappingException {
+ setCacheConcurrencyStrategy( clazz, concurrencyStrategy, region, true );
+ }
+
+ void setCacheConcurrencyStrategy(String clazz, String concurrencyStrategy, String region, boolean includeLazy)
+ throws MappingException {
+ RootClass rootClass = getRootClassMapping( clazz );
+ if ( rootClass == null ) {
+ throw new MappingException( "Cannot cache an unknown entity: " + clazz );
+ }
+ rootClass.setCacheConcurrencyStrategy( concurrencyStrategy );
+ rootClass.setCacheRegionName( region );
+ rootClass.setLazyPropertiesCacheable( includeLazy );
+ }
+
+ /**
+ * Set up a cache for a collection role
+ *
+ * @param collectionRole
+ * @param concurrencyStrategy
+ * @return Configuration
+ * @throws MappingException
+ */
+ public Configuration setCollectionCacheConcurrencyStrategy(String collectionRole, String concurrencyStrategy)
+ throws MappingException {
+ setCollectionCacheConcurrencyStrategy( collectionRole, concurrencyStrategy, collectionRole );
+ return this;
+ }
+
+ public void setCollectionCacheConcurrencyStrategy(String collectionRole, String concurrencyStrategy, String region)
+ throws MappingException {
+ Collection collection = getCollectionMapping( collectionRole );
+ if ( collection == null ) {
+ throw new MappingException( "Cannot cache an unknown collection: " + collectionRole );
+ }
+ collection.setCacheConcurrencyStrategy( concurrencyStrategy );
+ collection.setCacheRegionName( region );
+ }
+
+ /**
+ * Get the query language imports
+ *
+ * @return a mapping from "import" names to fully qualified class names
+ */
+ public Map getImports() {
+ return imports;
+ }
+
+ /**
+ * Create an object-oriented view of the configuration properties
+ */
+ public Settings buildSettings() throws HibernateException {
+ Properties clone = ( Properties ) properties.clone();
+ PropertiesHelper.resolvePlaceHolders( clone );
+ return settingsFactory.buildSettings( clone );
+ }
+
+ public Settings buildSettings(Properties props) throws HibernateException {
+ return settingsFactory.buildSettings( props );
+ }
+
+ public Map getNamedSQLQueries() {
+ return namedSqlQueries;
+ }
+
+ public Map getSqlResultSetMappings() {
+ return sqlResultSetMappings;
+ }
+
+ /**
+ * @return the NamingStrategy.
+ */
+ public NamingStrategy getNamingStrategy() {
+ return namingStrategy;
+ }
+
+ /**
+ * Set a custom naming strategy
+ *
+ * @param namingStrategy the NamingStrategy to set
+ */
+ public Configuration setNamingStrategy(NamingStrategy namingStrategy) {
+ this.namingStrategy = namingStrategy;
+ return this;
+ }
+
+ public Mapping buildMapping() {
+ return new Mapping() {
+ /**
+ * Returns the identifier type of a mapped class
+ */
+ public Type getIdentifierType(String persistentClass) throws MappingException {
+ PersistentClass pc = ( (PersistentClass) classes.get( persistentClass ) );
+ if ( pc == null ) {
+ throw new MappingException( "persistent class not known: " + persistentClass );
+ }
+ return pc.getIdentifier().getType();
+ }
+
+ public String getIdentifierPropertyName(String persistentClass) throws MappingException {
+ final PersistentClass pc = (PersistentClass) classes.get( persistentClass );
+ if ( pc == null ) {
+ throw new MappingException( "persistent class not known: " + persistentClass );
+ }
+ if ( !pc.hasIdentifierProperty() ) {
+ return null;
+ }
+ return pc.getIdentifierProperty().getName();
+ }
+
+ public Type getReferencedPropertyType(String persistentClass, String propertyName) throws MappingException {
+ final PersistentClass pc = (PersistentClass) classes.get( persistentClass );
+ if ( pc == null ) {
+ throw new MappingException( "persistent class not known: " + persistentClass );
+ }
+ Property prop = pc.getReferencedProperty( propertyName );
+ if ( prop == null ) {
+ throw new MappingException(
+ "property not known: " +
+ persistentClass + '.' + propertyName
+ );
+ }
+ return prop.getType();
+ }
+ };
+ }
+
+ private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
+ ois.defaultReadObject();
+ this.mapping = buildMapping();
+ xmlHelper = new XMLHelper();
+ }
+
+ public Map getFilterDefinitions() {
+ return filterDefinitions;
+ }
+
+ public void addFilterDefinition(FilterDefinition definition) {
+ filterDefinitions.put( definition.getFilterName(), definition );
+ }
+
+ public void addAuxiliaryDatabaseObject(AuxiliaryDatabaseObject object) {
+ auxiliaryDatabaseObjects.add( object );
+ }
+
+ public Map getSqlFunctions() {
+ return sqlFunctions;
+ }
+
+ public void addSqlFunction(String functionName, SQLFunction function) {
+ sqlFunctions.put( functionName, function );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/DefaultNamingStrategy.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/DefaultNamingStrategy.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/DefaultNamingStrategy.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,110 @@
+//$Id: DefaultNamingStrategy.java 9874 2006-05-04 15:10:22Z epbernard $
+package org.hibernate.cfg;
+
+import java.io.Serializable;
+
+import org.hibernate.util.StringHelper;
+import org.hibernate.AssertionFailure;
+
+/**
+ * The default <tt>NamingStrategy</tt>
+ * @see ImprovedNamingStrategy a better alternative
+ * @author Gavin King
+ */
+public class DefaultNamingStrategy implements NamingStrategy, Serializable {
+
+ /**
+ * The singleton instance
+ */
+ public static final NamingStrategy INSTANCE = new DefaultNamingStrategy();
+
+ /**
+ * Return the unqualified class name
+ */
+ public String classToTableName(String className) {
+ return StringHelper.unqualify(className);
+ }
+ /**
+ * Return the unqualified property name
+ */
+ public String propertyToColumnName(String propertyName) {
+ return StringHelper.unqualify(propertyName);
+ }
+ /**
+ * Return the argument
+ */
+ public String tableName(String tableName) {
+ return tableName;
+ }
+ /**
+ * Return the argument
+ */
+ public String columnName(String columnName) {
+ return columnName;
+ }
+
+ /**
+ * Return the unqualified property name, not the best strategy but a backward compatible one
+ */
+ public String collectionTableName(
+ String ownerEntity, String ownerEntityTable, String associatedEntity, String associatedEntityTable,
+ String propertyName
+ ) {
+ //use a degenerated strategy for backward compatibility
+ return StringHelper.unqualify(propertyName);
+ }
+
+ /**
+ * Return the argument
+ */
+ public String joinKeyColumnName(String joinedColumn, String joinedTable) {
+ return columnName( joinedColumn );
+ }
+
+ /**
+ * Return the property name or propertyTableName
+ */
+ public String foreignKeyColumnName(
+ String propertyName, String propertyEntityName, String propertyTableName, String referencedColumnName
+ ) {
+ String header = propertyName != null ? StringHelper.unqualify( propertyName ) : propertyTableName;
+ if (header == null) throw new AssertionFailure("NammingStrategy not properly filled");
+ return columnName( header ); //+ "_" + referencedColumnName not used for backward compatibility
+ }
+
+ /**
+ * Return the column name or the unqualified property name
+ */
+ public String logicalColumnName(String columnName, String propertyName) {
+ return StringHelper.isNotEmpty( columnName ) ? columnName : StringHelper.unqualify( propertyName );
+ }
+
+ /**
+ * Returns either the table name if explicit or
+ * if there is an associated table, the concatenation of owner entity table and associated table
+ * otherwise the concatenation of owner entity table and the unqualified property name
+ */
+ public String logicalCollectionTableName(String tableName,
+ String ownerEntityTable, String associatedEntityTable, String propertyName
+ ) {
+ if ( tableName != null ) {
+ return tableName;
+ }
+ else {
+ //use of a stringbuffer to workaround a JDK bug
+ return new StringBuffer(ownerEntityTable).append("_")
+ .append(
+ associatedEntityTable != null ?
+ associatedEntityTable :
+ StringHelper.unqualify( propertyName )
+ ).toString();
+ }
+ }
+ /**
+ * Return the column name if explicit or the concatenation of the property name and the referenced column
+ *
+ */
+ public String logicalCollectionColumnName(String columnName, String propertyName, String referencedColumn) {
+ return StringHelper.isNotEmpty( columnName ) ? columnName : propertyName + "_" + referencedColumn;
+ }
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/Environment.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/Environment.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/Environment.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,704 @@
+//$Id: Environment.java 11402 2007-04-11 14:24:35Z steve.ebersole at jboss.com $
+package org.hibernate.cfg;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.sql.Connection;
+import java.sql.Statement;
+import java.sql.Timestamp;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.hibernate.HibernateException;
+import org.hibernate.bytecode.BytecodeProvider;
+import org.hibernate.util.ConfigHelper;
+import org.hibernate.util.PropertiesHelper;
+
+
+/**
+ * Provides access to configuration info passed in <tt>Properties</tt> objects.
+ * <br><br>
+ * Hibernate has two property scopes:
+ * <ul>
+ * <li><b>Factory-level</b> properties may be passed to the <tt>SessionFactory</tt> when it
+ * instantiated. Each instance might have different property values. If no
+ * properties are specified, the factory calls <tt>Environment.getProperties()</tt>.
+ * <li><b>System-level</b> properties are shared by all factory instances and are always
+ * determined by the <tt>Environment</tt> properties.
+ * </ul>
+ * The only system-level properties are
+ * <ul>
+ * <li><tt>hibernate.jdbc.use_streams_for_binary</tt>
+ * <li><tt>hibernate.cglib.use_reflection_optimizer</tt>
+ * </ul>
+ * <tt>Environment</tt> properties are populated by calling <tt>System.getProperties()</tt>
+ * and then from a resource named <tt>/hibernate.properties</tt> if it exists. System
+ * properties override properties specified in <tt>hibernate.properties</tt>.<br>
+ * <br>
+ * The <tt>SessionFactory</tt> is controlled by the following properties.
+ * Properties may be either be <tt>System</tt> properties, properties
+ * defined in a resource named <tt>/hibernate.properties</tt> or an instance of
+ * <tt>java.util.Properties</tt> passed to
+ * <tt>Configuration.buildSessionFactory()</tt><br>
+ * <br>
+ * <table>
+ * <tr><td><b>property</b></td><td><b>meaning</b></td></tr>
+ * <tr>
+ * <td><tt>hibernate.dialect</tt></td>
+ * <td>classname of <tt>org.hibernate.dialect.Dialect</tt> subclass</td>
+ * </tr>
+ * <tr>
+ * <td><tt>hibernate.cache.provider_class</tt></td>
+ * <td>classname of <tt>org.hibernate.cache.CacheProvider</tt>
+ * subclass (if not specified EHCache is used)</td>
+ * </tr>
+ * <tr>
+ * <td><tt>hibernate.connection.provider_class</tt></td>
+ * <td>classname of <tt>org.hibernate.connection.ConnectionProvider</tt>
+ * subclass (if not specified hueristics are used)</td>
+ * </tr>
+ * <tr><td><tt>hibernate.connection.username</tt></td><td>database username</td></tr>
+ * <tr><td><tt>hibernate.connection.password</tt></td><td>database password</td></tr>
+ * <tr>
+ * <td><tt>hibernate.connection.url</tt></td>
+ * <td>JDBC URL (when using <tt>java.sql.DriverManager</tt>)</td>
+ * </tr>
+ * <tr>
+ * <td><tt>hibernate.connection.driver_class</tt></td>
+ * <td>classname of JDBC driver</td>
+ * </tr>
+ * <tr>
+ * <td><tt>hibernate.connection.isolation</tt></td>
+ * <td>JDBC transaction isolation level (only when using
+ * <tt>java.sql.DriverManager</tt>)
+ * </td>
+ * </tr>
+ * <td><tt>hibernate.connection.pool_size</tt></td>
+ * <td>the maximum size of the connection pool (only when using
+ * <tt>java.sql.DriverManager</tt>)
+ * </td>
+ * </tr>
+ * <tr>
+ * <td><tt>hibernate.connection.datasource</tt></td>
+ * <td>databasource JNDI name (when using <tt>javax.sql.Datasource</tt>)</td>
+ * </tr>
+ * <tr>
+ * <td><tt>hibernate.jndi.url</tt></td><td>JNDI <tt>InitialContext</tt> URL</td>
+ * </tr>
+ * <tr>
+ * <td><tt>hibernate.jndi.class</tt></td><td>JNDI <tt>InitialContext</tt> classname</td>
+ * </tr>
+ * <tr>
+ * <td><tt>hibernate.max_fetch_depth</tt></td>
+ * <td>maximum depth of outer join fetching</td>
+ * </tr>
+ * <tr>
+ * <td><tt>hibernate.jdbc.batch_size</tt></td>
+ * <td>enable use of JDBC2 batch API for drivers which support it</td>
+ * </tr>
+ * <tr>
+ * <td><tt>hibernate.jdbc.fetch_size</tt></td>
+ * <td>set the JDBC fetch size</td>
+ * </tr>
+ * <tr>
+ * <td><tt>hibernate.jdbc.use_scrollable_resultset</tt></td>
+ * <td>enable use of JDBC2 scrollable resultsets (you only need this specify
+ * this property when using user supplied connections)</td>
+ * </tr>
+ * <tr>
+ * <td><tt>hibernate.jdbc.use_getGeneratedKeys</tt></td>
+ * <td>enable use of JDBC3 PreparedStatement.getGeneratedKeys() to retrieve
+ * natively generated keys after insert. Requires JDBC3+ driver and JRE1.4+</td>
+ * </tr>
+ * <tr>
+ * <td><tt>hibernate.hbm2ddl.auto</tt></td>
+ * <td>enable auto DDL export</td>
+ * </tr>
+ * <tr>
+ * <td><tt>hibernate.default_schema</tt></td>
+ * <td>use given schema name for unqualified tables (always optional)</td>
+ * </tr>
+ * <tr>
+ * <td><tt>hibernate.default_catalog</tt></td>
+ * <td>use given catalog name for unqualified tables (always optional)</td>
+ * </tr>
+ * <tr>
+ * <td><tt>hibernate.session_factory_name</tt></td>
+ * <td>If set, the factory attempts to bind this name to itself in the
+ * JNDI context. This name is also used to support cross JVM <tt>
+ * Session</tt> (de)serialization.</td>
+ * </tr>
+ * <tr>
+ * <td><tt>hibernate.transaction.manager_lookup_class</tt></td>
+ * <td>classname of <tt>org.hibernate.transaction.TransactionManagerLookup</tt>
+ * implementor</td>
+ * </tr>
+ * <tr>
+ * <td><tt>hibernate.transaction.factory_class</tt></td>
+ * <td>the factory to use for instantiating <tt>Transaction</tt>s.
+ * (Defaults to <tt>JDBCTransactionFactory</tt>.)</td>
+ * </tr>
+ * <tr>
+ * <td><tt>hibernate.query.substitutions</tt></td><td>query language token substitutions</td>
+ * </tr>
+ * </table>
+ *
+ * @see org.hibernate.SessionFactory
+ * @author Gavin King
+ */
+public final class Environment {
+
+ public static final String VERSION = "3.2 cr3";
+
+ /**
+ * <tt>ConnectionProvider</tt> implementor to use when obtaining connections
+ */
+ public static final String CONNECTION_PROVIDER ="hibernate.connection.provider_class";
+ /**
+ * JDBC driver class
+ */
+ public static final String DRIVER ="hibernate.connection.driver_class";
+ /**
+ * JDBC transaction isolation level
+ */
+ public static final String ISOLATION ="hibernate.connection.isolation";
+ /**
+ * JDBC URL
+ */
+ public static final String URL ="hibernate.connection.url";
+ /**
+ * JDBC user
+ */
+ public static final String USER ="hibernate.connection.username";
+ /**
+ * JDBC password
+ */
+ public static final String PASS ="hibernate.connection.password";
+ /**
+ * JDBC autocommit mode
+ */
+ public static final String AUTOCOMMIT ="hibernate.connection.autocommit";
+ /**
+ * Maximum number of inactive connections for Hibernate's connection pool
+ */
+ public static final String POOL_SIZE ="hibernate.connection.pool_size";
+ /**
+ * <tt>java.sql.Datasource</tt> JNDI name
+ */
+ public static final String DATASOURCE ="hibernate.connection.datasource";
+ /**
+ * prefix for arbitrary JDBC connection properties
+ */
+ public static final String CONNECTION_PREFIX = "hibernate.connection";
+
+ /**
+ * JNDI initial context class, <tt>Context.INITIAL_CONTEXT_FACTORY</tt>
+ */
+ public static final String JNDI_CLASS ="hibernate.jndi.class";
+ /**
+ * JNDI provider URL, <tt>Context.PROVIDER_URL</tt>
+ */
+ public static final String JNDI_URL ="hibernate.jndi.url";
+ /**
+ * prefix for arbitrary JNDI <tt>InitialContext</tt> properties
+ */
+ public static final String JNDI_PREFIX = "hibernate.jndi";
+ /**
+ * JNDI name to bind to <tt>SessionFactory</tt>
+ */
+ public static final String SESSION_FACTORY_NAME = "hibernate.session_factory_name";
+
+ /**
+ * Hibernate SQL <tt>Dialect</tt> class
+ */
+ public static final String DIALECT ="hibernate.dialect";
+ /**
+ * A default database schema (owner) name to use for unqualified tablenames
+ */
+ public static final String DEFAULT_SCHEMA = "hibernate.default_schema";
+ /**
+ * A default database catalog name to use for unqualified tablenames
+ */
+ public static final String DEFAULT_CATALOG = "hibernate.default_catalog";
+
+ /**
+ * Enable logging of generated SQL to the console
+ */
+ public static final String SHOW_SQL ="hibernate.show_sql";
+ /**
+ * Enable formatting of SQL logged to the console
+ */
+ public static final String FORMAT_SQL ="hibernate.format_sql";
+ /**
+ * Add comments to the generated SQL
+ */
+ public static final String USE_SQL_COMMENTS ="hibernate.use_sql_comments";
+ /**
+ * Maximum depth of outer join fetching
+ */
+ public static final String MAX_FETCH_DEPTH = "hibernate.max_fetch_depth";
+ /**
+ * The default batch size for batch fetching
+ */
+ public static final String DEFAULT_BATCH_FETCH_SIZE = "hibernate.default_batch_fetch_size";
+ /**
+ * Use <tt>java.io</tt> streams to read / write binary data from / to JDBC
+ */
+ public static final String USE_STREAMS_FOR_BINARY = "hibernate.jdbc.use_streams_for_binary";
+ /**
+ * Use JDBC scrollable <tt>ResultSet</tt>s. This property is only necessary when there is
+ * no <tt>ConnectionProvider</tt>, ie. the user is supplying JDBC connections.
+ */
+ public static final String USE_SCROLLABLE_RESULTSET = "hibernate.jdbc.use_scrollable_resultset";
+ /**
+ * Tells the JDBC driver to attempt to retrieve row Id with the JDBC 3.0 PreparedStatement.getGeneratedKeys()
+ * method. In general, performance will be better if this property is set to true and the underlying
+ * JDBC driver supports getGeneratedKeys().
+ */
+ public static final String USE_GET_GENERATED_KEYS = "hibernate.jdbc.use_get_generated_keys";
+ /**
+ * Gives the JDBC driver a hint as to the number of rows that should be fetched from the database
+ * when more rows are needed. If <tt>0</tt>, JDBC driver default settings will be used.
+ */
+ public static final String STATEMENT_FETCH_SIZE = "hibernate.jdbc.fetch_size";
+ /**
+ * Maximum JDBC batch size. A nonzero value enables batch updates.
+ */
+ public static final String STATEMENT_BATCH_SIZE = "hibernate.jdbc.batch_size";
+ /**
+ * Select a custom batcher.
+ */
+ public static final String BATCH_STRATEGY = "hibernate.jdbc.factory_class";
+ /**
+ * Should versioned data be included in batching?
+ */
+ public static final String BATCH_VERSIONED_DATA = "hibernate.jdbc.batch_versioned_data";
+ /**
+ * An XSLT resource used to generate "custom" XML
+ */
+ public static final String OUTPUT_STYLESHEET ="hibernate.xml.output_stylesheet";
+
+ /**
+ * Maximum size of C3P0 connection pool
+ */
+ public static final String C3P0_MAX_SIZE = "hibernate.c3p0.max_size";
+ /**
+ * Minimum size of C3P0 connection pool
+ */
+ public static final String C3P0_MIN_SIZE = "hibernate.c3p0.min_size";
+
+ /**
+ * Maximum idle time for C3P0 connection pool
+ */
+ public static final String C3P0_TIMEOUT = "hibernate.c3p0.timeout";
+ /**
+ * Maximum size of C3P0 statement cache
+ */
+ public static final String C3P0_MAX_STATEMENTS = "hibernate.c3p0.max_statements";
+ /**
+ * Number of connections acquired when pool is exhausted
+ */
+ public static final String C3P0_ACQUIRE_INCREMENT = "hibernate.c3p0.acquire_increment";
+ /**
+ * Idle time before a C3P0 pooled connection is validated
+ */
+ public static final String C3P0_IDLE_TEST_PERIOD = "hibernate.c3p0.idle_test_period";
+
+ /**
+ * Proxool/Hibernate property prefix
+ */
+ public static final String PROXOOL_PREFIX = "hibernate.proxool";
+ /**
+ * Proxool property to configure the Proxool Provider using an XML (<tt>/path/to/file.xml</tt>)
+ */
+ public static final String PROXOOL_XML = "hibernate.proxool.xml";
+ /**
+ * Proxool property to configure the Proxool Provider using a properties file (<tt>/path/to/proxool.properties</tt>)
+ */
+ public static final String PROXOOL_PROPERTIES = "hibernate.proxool.properties";
+ /**
+ * Proxool property to configure the Proxool Provider from an already existing pool (<tt>true</tt> / <tt>false</tt>)
+ */
+ public static final String PROXOOL_EXISTING_POOL = "hibernate.proxool.existing_pool";
+ /**
+ * Proxool property with the Proxool pool alias to use
+ * (Required for <tt>PROXOOL_EXISTING_POOL</tt>, <tt>PROXOOL_PROPERTIES</tt>, or
+ * <tt>PROXOOL_XML</tt>)
+ */
+ public static final String PROXOOL_POOL_ALIAS = "hibernate.proxool.pool_alias";
+
+ /**
+ * Enable automatic session close at end of transaction
+ */
+ public static final String AUTO_CLOSE_SESSION = "hibernate.transaction.auto_close_session";
+ /**
+ * Enable automatic flush during the JTA <tt>beforeCompletion()</tt> callback
+ */
+ public static final String FLUSH_BEFORE_COMPLETION = "hibernate.transaction.flush_before_completion";
+ /**
+ * Specifies how Hibernate should release JDBC connections.
+ */
+ public static final String RELEASE_CONNECTIONS = "hibernate.connection.release_mode";
+ /**
+ * Context scoping impl for {@link org.hibernate.SessionFactory#getCurrentSession()} processing.
+ */
+ public static final String CURRENT_SESSION_CONTEXT_CLASS = "hibernate.current_session_context_class";
+ /**
+ * <tt>TransactionFactory</tt> implementor to use for creating <tt>Transaction</tt>s
+ */
+ public static final String TRANSACTION_STRATEGY = "hibernate.transaction.factory_class";
+ /**
+ * <tt>TransactionManagerLookup</tt> implementor to use for obtaining the <tt>TransactionManager</tt>
+ */
+ public static final String TRANSACTION_MANAGER_STRATEGY = "hibernate.transaction.manager_lookup_class";
+ /**
+ * JNDI name of JTA <tt>UserTransaction</tt> object
+ */
+ public static final String USER_TRANSACTION = "jta.UserTransaction";
+
+ /**
+ * The <tt>CacheProvider</tt> implementation class
+ */
+ public static final String CACHE_PROVIDER = "hibernate.cache.provider_class";
+
+ /**
+ * The {@link org.hibernate.cache.RegionFactory} implementation class
+ */
+ public static final String CACHE_REGION_FACTORY = "hibernate.cache.region.factory_class";
+
+ /**
+ * The <tt>CacheProvider</tt> implementation class
+ */
+ public static final String CACHE_PROVIDER_CONFIG = "hibernate.cache.provider_configuration_file_resource_path";
+ /**
+ * The <tt>CacheProvider</tt> JNDI namespace, if pre-bound to JNDI.
+ */
+ public static final String CACHE_NAMESPACE = "hibernate.cache.jndi";
+ /**
+ * Enable the query cache (disabled by default)
+ */
+ public static final String USE_QUERY_CACHE = "hibernate.cache.use_query_cache";
+ /**
+ * The <tt>QueryCacheFactory</tt> implementation class.
+ */
+ public static final String QUERY_CACHE_FACTORY = "hibernate.cache.query_cache_factory";
+ /**
+ * Enable the second-level cache (enabled by default)
+ */
+ public static final String USE_SECOND_LEVEL_CACHE = "hibernate.cache.use_second_level_cache";
+ /**
+ * Optimize the cache for mimimal puts instead of minimal gets
+ */
+ public static final String USE_MINIMAL_PUTS = "hibernate.cache.use_minimal_puts";
+ /**
+ * The <tt>CacheProvider</tt> region name prefix
+ */
+ public static final String CACHE_REGION_PREFIX = "hibernate.cache.region_prefix";
+ /**
+ * Enable use of structured second-level cache entries
+ */
+ public static final String USE_STRUCTURED_CACHE = "hibernate.cache.use_structured_entries";
+
+ /**
+ * Enable statistics collection
+ */
+ public static final String GENERATE_STATISTICS = "hibernate.generate_statistics";
+
+ public static final String USE_IDENTIFIER_ROLLBACK = "hibernate.use_identifier_rollback";
+
+ /**
+ * Use bytecode libraries optimized property access
+ */
+ public static final String USE_REFLECTION_OPTIMIZER = "hibernate.bytecode.use_reflection_optimizer";
+
+ /**
+ * The classname of the HQL query parser factory
+ */
+ public static final String QUERY_TRANSLATOR = "hibernate.query.factory_class";
+
+ /**
+ * A comma-seperated list of token substitutions to use when translating a Hibernate
+ * query to SQL
+ */
+ public static final String QUERY_SUBSTITUTIONS = "hibernate.query.substitutions";
+
+ /**
+ * Should named queries be checked during startup (the default is enabled).
+ * <p/>
+ * Mainly intended for test environments.
+ */
+ public static final String QUERY_STARTUP_CHECKING = "hibernate.query.startup_check";
+
+ /**
+ * Auto export/update schema using hbm2ddl tool. Valid values are <tt>update</tt>,
+ * <tt>create</tt>, <tt>create-drop</tt> and <tt>validate</tt>.
+ */
+ public static final String HBM2DDL_AUTO = "hibernate.hbm2ddl.auto";
+
+ /**
+ * The {@link org.hibernate.exception.SQLExceptionConverter} to use for converting SQLExceptions
+ * to Hibernate's JDBCException hierarchy. The default is to use the configured
+ * {@link org.hibernate.dialect.Dialect}'s preferred SQLExceptionConverter.
+ */
+ public static final String SQL_EXCEPTION_CONVERTER = "hibernate.jdbc.sql_exception_converter";
+
+ /**
+ * Enable wrapping of JDBC result sets in order to speed up column name lookups for
+ * broken JDBC drivers
+ */
+ public static final String WRAP_RESULT_SETS = "hibernate.jdbc.wrap_result_sets";
+
+ /**
+ * Enable ordering of update statements by primary key value
+ */
+ public static final String ORDER_UPDATES = "hibernate.order_updates";
+
+ /**
+ * Enable ordering of insert statements for the purpose of more effecient JDBC batching.
+ */
+ public static final String ORDER_INSERTS = "hibernate.order_inserts";
+
+ /**
+ * The EntityMode in which set the Session opened from the SessionFactory.
+ */
+ public static final String DEFAULT_ENTITY_MODE = "hibernate.default_entity_mode";
+
+ /**
+ * The jacc context id of the deployment
+ */
+ public static final String JACC_CONTEXTID = "hibernate.jacc_context_id";
+
+ public static final String BYTECODE_PROVIDER = "hibernate.bytecode.provider";
+
+ public static final String JPAQL_STRICT_COMPLIANCE= "hibernate.query.jpaql_strict_compliance";
+
+ private static final BytecodeProvider BYTECODE_PROVIDER_INSTANCE;
+ private static final boolean ENABLE_BINARY_STREAMS;
+ private static final boolean ENABLE_REFLECTION_OPTIMIZER;
+ private static final boolean JVM_SUPPORTS_LINKED_HASH_COLLECTIONS;
+ private static final boolean JVM_HAS_TIMESTAMP_BUG;
+ private static final boolean JVM_HAS_JDK14_TIMESTAMP;
+ private static final boolean JVM_SUPPORTS_GET_GENERATED_KEYS;
+
+ private static final Properties GLOBAL_PROPERTIES;
+ private static final HashMap ISOLATION_LEVELS = new HashMap();
+ private static final Map OBSOLETE_PROPERTIES = new HashMap();
+ private static final Map RENAMED_PROPERTIES = new HashMap();
+
+ private static final Log log = LogFactory.getLog(Environment.class);
+
+ /**
+ * Issues warnings to the user when any obsolete property names are used.
+ */
+ public static void verifyProperties(Properties props) {
+ Iterator iter = props.keySet().iterator();
+ Map propertiesToAdd = new HashMap();
+ while ( iter.hasNext() ) {
+ final Object propertyName = iter.next();
+ Object newPropertyName = OBSOLETE_PROPERTIES.get( propertyName );
+ if ( newPropertyName != null ) {
+ log.warn( "Usage of obsolete property: " + propertyName + " no longer supported, use: " + newPropertyName );
+ }
+ newPropertyName = RENAMED_PROPERTIES.get( propertyName );
+ if ( newPropertyName != null ) {
+ log.warn( "Property [" + propertyName + "] has been renamed to [" + newPropertyName + "]; update your properties appropriately" );
+ if ( ! props.containsKey( newPropertyName ) ) {
+ propertiesToAdd.put( newPropertyName, props.get( propertyName ) );
+ }
+ }
+ }
+ props.putAll(propertiesToAdd);
+ }
+
+ static {
+
+ log.info("Hibernate " + VERSION);
+
+ RENAMED_PROPERTIES.put( "hibernate.cglib.use_reflection_optimizer", USE_REFLECTION_OPTIMIZER );
+
+ ISOLATION_LEVELS.put( new Integer(Connection.TRANSACTION_NONE), "NONE" );
+ ISOLATION_LEVELS.put( new Integer(Connection.TRANSACTION_READ_UNCOMMITTED), "READ_UNCOMMITTED" );
+ ISOLATION_LEVELS.put( new Integer(Connection.TRANSACTION_READ_COMMITTED), "READ_COMMITTED" );
+ ISOLATION_LEVELS.put( new Integer(Connection.TRANSACTION_REPEATABLE_READ), "REPEATABLE_READ" );
+ ISOLATION_LEVELS.put( new Integer(Connection.TRANSACTION_SERIALIZABLE), "SERIALIZABLE" );
+
+ GLOBAL_PROPERTIES = new Properties();
+ //Set USE_REFLECTION_OPTIMIZER to false to fix HHH-227
+ GLOBAL_PROPERTIES.setProperty( USE_REFLECTION_OPTIMIZER, Boolean.FALSE.toString() );
+
+ try {
+ InputStream stream = ConfigHelper.getResourceAsStream("/hibernate.properties");
+ try {
+ GLOBAL_PROPERTIES.load(stream);
+ log.info( "loaded properties from resource hibernate.properties: " + PropertiesHelper.maskOut(GLOBAL_PROPERTIES, PASS) );
+ }
+ catch (Exception e) {
+ log.error("problem loading properties from hibernate.properties");
+ }
+ finally {
+ try{
+ stream.close();
+ }
+ catch (IOException ioe){
+ log.error("could not close stream on hibernate.properties", ioe);
+ }
+ }
+ }
+ catch (HibernateException he) {
+ log.info("hibernate.properties not found");
+ }
+
+ try {
+ GLOBAL_PROPERTIES.putAll( System.getProperties() );
+ }
+ catch (SecurityException se) {
+ log.warn("could not copy system properties, system properties will be ignored");
+ }
+
+ verifyProperties(GLOBAL_PROPERTIES);
+
+ ENABLE_BINARY_STREAMS = PropertiesHelper.getBoolean(USE_STREAMS_FOR_BINARY, GLOBAL_PROPERTIES);
+ ENABLE_REFLECTION_OPTIMIZER = PropertiesHelper.getBoolean(USE_REFLECTION_OPTIMIZER, GLOBAL_PROPERTIES);
+
+ if (ENABLE_BINARY_STREAMS) {
+ log.info("using java.io streams to persist binary types");
+ }
+ if (ENABLE_REFLECTION_OPTIMIZER) {
+ log.info("using bytecode reflection optimizer");
+ }
+ BYTECODE_PROVIDER_INSTANCE = buildBytecodeProvider( GLOBAL_PROPERTIES );
+
+ boolean getGeneratedKeysSupport;
+ try {
+ Statement.class.getMethod("getGeneratedKeys", null);
+ getGeneratedKeysSupport = true;
+ }
+ catch (NoSuchMethodException nsme) {
+ getGeneratedKeysSupport = false;
+ }
+ JVM_SUPPORTS_GET_GENERATED_KEYS = getGeneratedKeysSupport;
+ if (!JVM_SUPPORTS_GET_GENERATED_KEYS) log.info("JVM does not support Statement.getGeneratedKeys()");
+
+ boolean linkedHashSupport;
+ try {
+ Class.forName("java.util.LinkedHashSet");
+ linkedHashSupport = true;
+ }
+ catch (ClassNotFoundException cnfe) {
+ linkedHashSupport = false;
+ }
+ JVM_SUPPORTS_LINKED_HASH_COLLECTIONS = linkedHashSupport;
+ if (!JVM_SUPPORTS_LINKED_HASH_COLLECTIONS) log.info("JVM does not support LinkedHasMap, LinkedHashSet - ordered maps and sets disabled");
+
+ JVM_HAS_TIMESTAMP_BUG = new Timestamp(123456789).getTime() != 123456789;
+ if (JVM_HAS_TIMESTAMP_BUG) log.info("using workaround for JVM bug in java.sql.Timestamp");
+ Timestamp t = new Timestamp(0);
+ t.setNanos(5 * 1000000);
+ JVM_HAS_JDK14_TIMESTAMP = t.getTime() == 5;
+ if (JVM_HAS_JDK14_TIMESTAMP) {
+ log.info("using JDK 1.4 java.sql.Timestamp handling");
+ }
+ else {
+ log.info("using pre JDK 1.4 java.sql.Timestamp handling");
+ }
+ }
+
+ public static BytecodeProvider getBytecodeProvider() {
+ return BYTECODE_PROVIDER_INSTANCE;
+ }
+
+ /**
+ * Does this JVM have the IBM JDK 1.3.1. The bug is <tt>new Timestamp(x).getTime()!=x</tt>.
+ */
+ public static boolean jvmHasTimestampBug() {
+ return JVM_HAS_TIMESTAMP_BUG;
+ }
+
+ /**
+ * Does this JVM handle <tt>Timestamp</tt> in the JDK 1.4 compliant way?
+ */
+ public static boolean jvmHasJDK14Timestamp() {
+ return JVM_HAS_JDK14_TIMESTAMP;
+ }
+
+ /**
+ * Does this JVM support <tt>LinkedHashSet</tt>, <tt>LinkedHashMap</tt>.
+ * @see java.util.LinkedHashSet
+ * @see java.util.LinkedHashMap
+ */
+ public static boolean jvmSupportsLinkedHashCollections() {
+ return JVM_SUPPORTS_LINKED_HASH_COLLECTIONS;
+ }
+
+ public static boolean jvmSupportsGetGeneratedKeys() {
+ return JVM_SUPPORTS_GET_GENERATED_KEYS;
+ }
+
+ /**
+ * Should we use streams to bind binary types to JDBC IN parameters.
+ * Property <tt>hibernate.jdbc.use_streams_for_binary</tt>.
+ * @see Environment#USE_STREAMS_FOR_BINARY
+ */
+ public static boolean useStreamsForBinary() {
+ return ENABLE_BINARY_STREAMS;
+ }
+
+ /**
+ * Should we use CGLIB reflection optimizer.
+ * Property <tt>hibernate.jdbc.use_refection_optimizer</tt>.
+ * @see Environment#USE_REFLECTION_OPTIMIZER
+ */
+ public static boolean useReflectionOptimizer() {
+ return ENABLE_REFLECTION_OPTIMIZER;
+ }
+
+ private Environment() { throw new UnsupportedOperationException(); }
+
+ /**
+ * Return <tt>System</tt> properties, extended by any properties specified
+ * in <tt>hibernate.properties</tt>.
+ * @return Properties
+ */
+ public static Properties getProperties() {
+ Properties copy = new Properties();
+ copy.putAll(GLOBAL_PROPERTIES);
+ return copy;
+ }
+
+ /**
+ * Get the name of a JDBC transaction isolation level
+ *
+ * @see java.sql.Connection
+ * @param isolation as defined by <tt>java.sql.Connection</tt>
+ * @return a human-readable name
+ */
+ public static String isolationLevelToString(int isolation) {
+ return (String) ISOLATION_LEVELS.get( new Integer(isolation) );
+ }
+
+
+ public static BytecodeProvider buildBytecodeProvider(Properties properties) {
+ String provider = PropertiesHelper.getString( Environment.BYTECODE_PROVIDER, properties, "cglib" );
+ log.info( "Bytecode provider name : " + provider );
+ return buildBytecodeProvider( provider );
+ }
+
+ private static BytecodeProvider buildBytecodeProvider(String providerName) {
+ if ( "javassist".equals( providerName ) ) {
+ return new org.hibernate.bytecode.javassist.BytecodeProviderImpl();
+ }
+ else if ( "cglib".equals( providerName ) ) {
+ return new org.hibernate.bytecode.cglib.BytecodeProviderImpl();
+ }
+ else {
+ log.warn( "unrecognized bytecode provider [" + providerName + "], using cglib by default" );
+ return new org.hibernate.bytecode.cglib.BytecodeProviderImpl();
+ }
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/ExtendsQueueEntry.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/ExtendsQueueEntry.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/ExtendsQueueEntry.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,33 @@
+package org.hibernate.cfg;
+
+import org.dom4j.Document;
+
+/**
+ * Represents a mapping queued for delayed processing to await
+ * processing of an extends entity upon which it depends.
+ *
+ * @author Steve Ebersole
+ */
+public class ExtendsQueueEntry {
+ private final String explicitName;
+ private final String mappingPackage;
+ private final Document document;
+
+ public ExtendsQueueEntry(String explicitName, String mappingPackage, Document document) {
+ this.explicitName = explicitName;
+ this.mappingPackage = mappingPackage;
+ this.document = document;
+ }
+
+ public String getExplicitName() {
+ return explicitName;
+ }
+
+ public String getMappingPackage() {
+ return mappingPackage;
+ }
+
+ public Document getDocument() {
+ return document;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/HbmBinder.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/HbmBinder.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/HbmBinder.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,3063 @@
+// $Id: HbmBinder.java 11496 2007-05-09 03:54:06Z steve.ebersole at jboss.com $
+package org.hibernate.cfg;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+import org.apache.commons.collections.SequencedHashMap;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.dom4j.Attribute;
+import org.dom4j.Document;
+import org.dom4j.Element;
+import org.hibernate.CacheMode;
+import org.hibernate.EntityMode;
+import org.hibernate.FetchMode;
+import org.hibernate.FlushMode;
+import org.hibernate.MappingException;
+import org.hibernate.engine.FilterDefinition;
+import org.hibernate.engine.NamedQueryDefinition;
+import org.hibernate.engine.Versioning;
+import org.hibernate.engine.ExecuteUpdateResultCheckStyle;
+import org.hibernate.id.PersistentIdentifierGenerator;
+import org.hibernate.mapping.Any;
+import org.hibernate.mapping.Array;
+import org.hibernate.mapping.AuxiliaryDatabaseObject;
+import org.hibernate.mapping.Backref;
+import org.hibernate.mapping.Bag;
+import org.hibernate.mapping.Collection;
+import org.hibernate.mapping.Column;
+import org.hibernate.mapping.Component;
+import org.hibernate.mapping.DependantValue;
+import org.hibernate.mapping.Fetchable;
+import org.hibernate.mapping.Filterable;
+import org.hibernate.mapping.Formula;
+import org.hibernate.mapping.IdentifierBag;
+import org.hibernate.mapping.IdentifierCollection;
+import org.hibernate.mapping.IndexBackref;
+import org.hibernate.mapping.IndexedCollection;
+import org.hibernate.mapping.Join;
+import org.hibernate.mapping.JoinedSubclass;
+import org.hibernate.mapping.KeyValue;
+import org.hibernate.mapping.List;
+import org.hibernate.mapping.ManyToOne;
+import org.hibernate.mapping.Map;
+import org.hibernate.mapping.MetaAttribute;
+import org.hibernate.mapping.OneToMany;
+import org.hibernate.mapping.OneToOne;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.PrimitiveArray;
+import org.hibernate.mapping.Property;
+import org.hibernate.mapping.PropertyGeneration;
+import org.hibernate.mapping.RootClass;
+import org.hibernate.mapping.Selectable;
+import org.hibernate.mapping.Set;
+import org.hibernate.mapping.SimpleAuxiliaryDatabaseObject;
+import org.hibernate.mapping.SimpleValue;
+import org.hibernate.mapping.SingleTableSubclass;
+import org.hibernate.mapping.Subclass;
+import org.hibernate.mapping.Table;
+import org.hibernate.mapping.ToOne;
+import org.hibernate.mapping.TypeDef;
+import org.hibernate.mapping.UnionSubclass;
+import org.hibernate.mapping.UniqueKey;
+import org.hibernate.mapping.Value;
+import org.hibernate.persister.entity.JoinedSubclassEntityPersister;
+import org.hibernate.persister.entity.SingleTableEntityPersister;
+import org.hibernate.persister.entity.UnionSubclassEntityPersister;
+import org.hibernate.type.DiscriminatorType;
+import org.hibernate.type.ForeignKeyDirection;
+import org.hibernate.type.Type;
+import org.hibernate.type.TypeFactory;
+import org.hibernate.util.JoinedIterator;
+import org.hibernate.util.ReflectHelper;
+import org.hibernate.util.StringHelper;
+
+/**
+ * Walks an XML mapping document and produces the Hibernate configuration-time metamodel (the
+ * classes in the <tt>mapping</tt> package)
+ *
+ * @author Gavin King
+ */
+public final class HbmBinder {
+
+ private static final Log log = LogFactory.getLog( HbmBinder.class );
+
+ /**
+ * Private constructor to disallow instantiation.
+ */
+ private HbmBinder() {
+ }
+
+ /**
+ * The main contract into the hbm.xml-based binder. Performs necessary binding operations
+ * represented by the given DOM.
+ *
+ * @param doc The DOM to be parsed and bound.
+ * @param mappings Current bind state.
+ * @param inheritedMetas Any inherited meta-tag information.
+ * @throws MappingException
+ */
+ public static void bindRoot(Document doc, Mappings mappings, java.util.Map inheritedMetas)
+ throws MappingException {
+
+ java.util.List names = HbmBinder.getExtendsNeeded( doc, mappings );
+ if ( !names.isEmpty() ) {
+ // classes mentioned in extends not available - so put it in queue
+ Element hmNode = doc.getRootElement();
+ Attribute packNode = hmNode.attribute( "package" );
+ String packageName = null;
+ if ( packNode != null ) {
+ packageName = packNode.getValue();
+ }
+ Iterator itr = names.iterator();
+ while ( itr.hasNext() ) {
+ String extendsName = (String) itr.next();
+ mappings.addToExtendsQueue( new ExtendsQueueEntry( extendsName, packageName, doc ) );
+ }
+ return;
+ }
+
+ Element hmNode = doc.getRootElement();
+ // get meta's from <hibernate-mapping>
+ inheritedMetas = getMetas( hmNode, inheritedMetas, true );
+ extractRootAttributes( hmNode, mappings );
+
+ Iterator rootChildren = hmNode.elementIterator();
+ while ( rootChildren.hasNext() ) {
+ final Element element = (Element) rootChildren.next();
+ final String elementName = element.getName();
+
+ if ( "filter-def".equals( elementName ) ) {
+ parseFilterDef( element, mappings );
+ }
+ else if ( "typedef".equals( elementName ) ) {
+ bindTypeDef( element, mappings );
+ }
+ else if ( "class".equals( elementName ) ) {
+ RootClass rootclass = new RootClass();
+ bindRootClass( element, rootclass, mappings, inheritedMetas );
+ mappings.addClass( rootclass );
+ }
+ else if ( "subclass".equals( elementName ) ) {
+ PersistentClass superModel = getSuperclass( mappings, element );
+ handleSubclass( superModel, mappings, element, inheritedMetas );
+ }
+ else if ( "joined-subclass".equals( elementName ) ) {
+ PersistentClass superModel = getSuperclass( mappings, element );
+ handleJoinedSubclass( superModel, mappings, element, inheritedMetas );
+ }
+ else if ( "union-subclass".equals( elementName ) ) {
+ PersistentClass superModel = getSuperclass( mappings, element );
+ handleUnionSubclass( superModel, mappings, element, inheritedMetas );
+ }
+ else if ( "query".equals( elementName ) ) {
+ bindNamedQuery( element, null, mappings );
+ }
+ else if ( "sql-query".equals( elementName ) ) {
+ bindNamedSQLQuery( element, null, mappings );
+ }
+ else if ( "resultset".equals( elementName ) ) {
+ bindResultSetMappingDefinition( element, null, mappings );
+ }
+ else if ( "import".equals( elementName ) ) {
+ bindImport( element, mappings );
+ }
+ else if ( "database-object".equals( elementName ) ) {
+ bindAuxiliaryDatabaseObject( element, mappings );
+ }
+ }
+ }
+
+ private static void bindImport(Element importNode, Mappings mappings) {
+ String className = getClassName( importNode.attribute( "class" ), mappings );
+ Attribute renameNode = importNode.attribute( "rename" );
+ String rename = ( renameNode == null ) ?
+ StringHelper.unqualify( className ) :
+ renameNode.getValue();
+ log.debug( "Import: " + rename + " -> " + className );
+ mappings.addImport( className, rename );
+ }
+
+ private static void bindTypeDef(Element typedefNode, Mappings mappings) {
+ String typeClass = typedefNode.attributeValue( "class" );
+ String typeName = typedefNode.attributeValue( "name" );
+ Iterator paramIter = typedefNode.elementIterator( "param" );
+ Properties parameters = new Properties();
+ while ( paramIter.hasNext() ) {
+ Element param = (Element) paramIter.next();
+ parameters.setProperty( param.attributeValue( "name" ), param.getTextTrim() );
+ }
+ mappings.addTypeDef( typeName, typeClass, parameters );
+ }
+
+ private static void bindAuxiliaryDatabaseObject(Element auxDbObjectNode, Mappings mappings) {
+ AuxiliaryDatabaseObject auxDbObject = null;
+ Element definitionNode = auxDbObjectNode.element( "definition" );
+ if ( definitionNode != null ) {
+ try {
+ auxDbObject = ( AuxiliaryDatabaseObject ) ReflectHelper
+ .classForName( definitionNode.attributeValue( "class" ) )
+ .newInstance();
+ }
+ catch( ClassNotFoundException e ) {
+ throw new MappingException(
+ "could not locate custom database object class [" +
+ definitionNode.attributeValue( "class" ) + "]"
+ );
+ }
+ catch( Throwable t ) {
+ throw new MappingException(
+ "could not instantiate custom database object class [" +
+ definitionNode.attributeValue( "class" ) + "]"
+ );
+ }
+ }
+ else {
+ auxDbObject = new SimpleAuxiliaryDatabaseObject(
+ auxDbObjectNode.elementTextTrim( "create" ),
+ auxDbObjectNode.elementTextTrim( "drop" )
+ );
+ }
+
+ Iterator dialectScopings = auxDbObjectNode.elementIterator( "dialect-scope" );
+ while ( dialectScopings.hasNext() ) {
+ Element dialectScoping = ( Element ) dialectScopings.next();
+ auxDbObject.addDialectScope( dialectScoping.attributeValue( "name" ) );
+ }
+
+ mappings.addAuxiliaryDatabaseObject( auxDbObject );
+ }
+
+ private static void extractRootAttributes(Element hmNode, Mappings mappings) {
+ Attribute schemaNode = hmNode.attribute( "schema" );
+ mappings.setSchemaName( ( schemaNode == null ) ? null : schemaNode.getValue() );
+
+ Attribute catalogNode = hmNode.attribute( "catalog" );
+ mappings.setCatalogName( ( catalogNode == null ) ? null : catalogNode.getValue() );
+
+ Attribute dcNode = hmNode.attribute( "default-cascade" );
+ mappings.setDefaultCascade( ( dcNode == null ) ? "none" : dcNode.getValue() );
+
+ Attribute daNode = hmNode.attribute( "default-access" );
+ mappings.setDefaultAccess( ( daNode == null ) ? "property" : daNode.getValue() );
+
+ Attribute dlNode = hmNode.attribute( "default-lazy" );
+ mappings.setDefaultLazy( dlNode == null || dlNode.getValue().equals( "true" ) );
+
+ Attribute aiNode = hmNode.attribute( "auto-import" );
+ mappings.setAutoImport( ( aiNode == null ) || "true".equals( aiNode.getValue() ) );
+
+ Attribute packNode = hmNode.attribute( "package" );
+ if ( packNode != null ) mappings.setDefaultPackage( packNode.getValue() );
+ }
+
+ /**
+ * Responsible for perfoming the bind operation related to an <class/> mapping element.
+ *
+ * @param node The DOM Element for the <class/> element.
+ * @param rootClass The mapping instance to which to bind the information.
+ * @param mappings The current bind state.
+ * @param inheritedMetas Any inherited meta-tag information.
+ * @throws MappingException
+ */
+ public static void bindRootClass(Element node, RootClass rootClass, Mappings mappings,
+ java.util.Map inheritedMetas) throws MappingException {
+ bindClass( node, rootClass, mappings, inheritedMetas );
+ inheritedMetas = getMetas( node, inheritedMetas, true ); // get meta's from <class>
+ bindRootPersistentClassCommonValues( node, inheritedMetas, mappings, rootClass );
+ }
+
+ private static void bindRootPersistentClassCommonValues(Element node,
+ java.util.Map inheritedMetas, Mappings mappings, RootClass entity)
+ throws MappingException {
+
+ // DB-OBJECTNAME
+
+ Attribute schemaNode = node.attribute( "schema" );
+ String schema = schemaNode == null ?
+ mappings.getSchemaName() : schemaNode.getValue();
+
+ Attribute catalogNode = node.attribute( "catalog" );
+ String catalog = catalogNode == null ?
+ mappings.getCatalogName() : catalogNode.getValue();
+
+ Table table = mappings.addTable(
+ schema,
+ catalog,
+ getClassTableName( entity, node, schema, catalog, null, mappings ),
+ getSubselect( node ),
+ entity.isAbstract() != null && entity.isAbstract().booleanValue()
+ );
+ entity.setTable( table );
+ bindComment(table, node);
+
+ log.info(
+ "Mapping class: " + entity.getEntityName() +
+ " -> " + entity.getTable().getName()
+ );
+
+ // MUTABLE
+ Attribute mutableNode = node.attribute( "mutable" );
+ entity.setMutable( ( mutableNode == null ) || mutableNode.getValue().equals( "true" ) );
+
+ // WHERE
+ Attribute whereNode = node.attribute( "where" );
+ if ( whereNode != null ) entity.setWhere( whereNode.getValue() );
+
+ // CHECK
+ Attribute chNode = node.attribute( "check" );
+ if ( chNode != null ) table.addCheckConstraint( chNode.getValue() );
+
+ // POLYMORPHISM
+ Attribute polyNode = node.attribute( "polymorphism" );
+ entity.setExplicitPolymorphism( ( polyNode != null )
+ && polyNode.getValue().equals( "explicit" ) );
+
+ // ROW ID
+ Attribute rowidNode = node.attribute( "rowid" );
+ if ( rowidNode != null ) table.setRowId( rowidNode.getValue() );
+
+ Iterator subnodes = node.elementIterator();
+ while ( subnodes.hasNext() ) {
+
+ Element subnode = (Element) subnodes.next();
+ String name = subnode.getName();
+
+ if ( "id".equals( name ) ) {
+ // ID
+ bindSimpleId( subnode, entity, mappings, inheritedMetas );
+ }
+ else if ( "composite-id".equals( name ) ) {
+ // COMPOSITE-ID
+ bindCompositeId( subnode, entity, mappings, inheritedMetas );
+ }
+ else if ( "version".equals( name ) || "timestamp".equals( name ) ) {
+ // VERSION / TIMESTAMP
+ bindVersioningProperty( table, subnode, mappings, name, entity, inheritedMetas );
+ }
+ else if ( "discriminator".equals( name ) ) {
+ // DISCRIMINATOR
+ bindDiscriminatorProperty( table, entity, subnode, mappings );
+ }
+ else if ( "cache".equals( name ) ) {
+ entity.setCacheConcurrencyStrategy( subnode.attributeValue( "usage" ) );
+ entity.setCacheRegionName( subnode.attributeValue( "region" ) );
+ entity.setLazyPropertiesCacheable( !"non-lazy".equals( subnode.attributeValue( "include" ) ) );
+ }
+
+ }
+
+ // Primary key constraint
+ entity.createPrimaryKey();
+
+ createClassProperties( node, entity, mappings, inheritedMetas );
+ }
+
+ private static void bindSimpleId(Element idNode, RootClass entity, Mappings mappings,
+ java.util.Map inheritedMetas) throws MappingException {
+ String propertyName = idNode.attributeValue( "name" );
+
+ SimpleValue id = new SimpleValue( entity.getTable() );
+ entity.setIdentifier( id );
+
+ // if ( propertyName == null || entity.getPojoRepresentation() == null ) {
+ // bindSimpleValue( idNode, id, false, RootClass.DEFAULT_IDENTIFIER_COLUMN_NAME, mappings );
+ // if ( !id.isTypeSpecified() ) {
+ // throw new MappingException( "must specify an identifier type: " + entity.getEntityName()
+ // );
+ // }
+ // }
+ // else {
+ // bindSimpleValue( idNode, id, false, propertyName, mappings );
+ // PojoRepresentation pojo = entity.getPojoRepresentation();
+ // id.setTypeUsingReflection( pojo.getClassName(), propertyName );
+ //
+ // Property prop = new Property();
+ // prop.setValue( id );
+ // bindProperty( idNode, prop, mappings, inheritedMetas );
+ // entity.setIdentifierProperty( prop );
+ // }
+
+ if ( propertyName == null ) {
+ bindSimpleValue( idNode, id, false, RootClass.DEFAULT_IDENTIFIER_COLUMN_NAME, mappings );
+ }
+ else {
+ bindSimpleValue( idNode, id, false, propertyName, mappings );
+ }
+
+ if ( propertyName == null || !entity.hasPojoRepresentation() ) {
+ if ( !id.isTypeSpecified() ) {
+ throw new MappingException( "must specify an identifier type: "
+ + entity.getEntityName() );
+ }
+ }
+ else {
+ id.setTypeUsingReflection( entity.getClassName(), propertyName );
+ }
+
+ if ( propertyName != null ) {
+ Property prop = new Property();
+ prop.setValue( id );
+ bindProperty( idNode, prop, mappings, inheritedMetas );
+ entity.setIdentifierProperty( prop );
+ }
+
+ // TODO:
+ /*
+ * if ( id.getHibernateType().getReturnedClass().isArray() ) throw new MappingException(
+ * "illegal use of an array as an identifier (arrays don't reimplement equals)" );
+ */
+ makeIdentifier( idNode, id, mappings );
+ }
+
+ private static void bindCompositeId(Element idNode, RootClass entity, Mappings mappings,
+ java.util.Map inheritedMetas) throws MappingException {
+ String propertyName = idNode.attributeValue( "name" );
+ Component id = new Component( entity );
+ entity.setIdentifier( id );
+ bindCompositeId( idNode, id, entity, propertyName, mappings, inheritedMetas );
+ if ( propertyName == null ) {
+ entity.setEmbeddedIdentifier( id.isEmbedded() );
+ if ( id.isEmbedded() ) {
+ // todo : what is the implication of this?
+ id.setDynamic( !entity.hasPojoRepresentation() );
+ /*
+ * Property prop = new Property(); prop.setName("id");
+ * prop.setPropertyAccessorName("embedded"); prop.setValue(id);
+ * entity.setIdentifierProperty(prop);
+ */
+ }
+ }
+ else {
+ Property prop = new Property();
+ prop.setValue( id );
+ bindProperty( idNode, prop, mappings, inheritedMetas );
+ entity.setIdentifierProperty( prop );
+ }
+
+ makeIdentifier( idNode, id, mappings );
+
+ }
+
+ private static void bindVersioningProperty(Table table, Element subnode, Mappings mappings,
+ String name, RootClass entity, java.util.Map inheritedMetas) {
+
+ String propertyName = subnode.attributeValue( "name" );
+ SimpleValue val = new SimpleValue( table );
+ bindSimpleValue( subnode, val, false, propertyName, mappings );
+ if ( !val.isTypeSpecified() ) {
+ // this is either a <version/> tag with no type attribute,
+ // or a <timestamp/> tag
+ if ( "version".equals( name ) ) {
+ val.setTypeName( "integer" );
+ }
+ else {
+ if ( "db".equals( subnode.attributeValue( "source" ) ) ) {
+ val.setTypeName( "dbtimestamp" );
+ }
+ else {
+ val.setTypeName( "timestamp" );
+ }
+ }
+ }
+ Property prop = new Property();
+ prop.setValue( val );
+ bindProperty( subnode, prop, mappings, inheritedMetas );
+ // for version properties marked as being generated, make sure they are "always"
+ // generated; aka, "insert" is invalid; this is dis-allowed by the DTD,
+ // but just to make sure...
+ if ( prop.getGeneration() == PropertyGeneration.INSERT ) {
+ throw new MappingException( "'generated' attribute cannot be 'insert' for versioning property" );
+ }
+ makeVersion( subnode, val );
+ entity.setVersion( prop );
+ entity.addProperty( prop );
+ }
+
+ private static void bindDiscriminatorProperty(Table table, RootClass entity, Element subnode,
+ Mappings mappings) {
+ SimpleValue discrim = new SimpleValue( table );
+ entity.setDiscriminator( discrim );
+ bindSimpleValue(
+ subnode,
+ discrim,
+ false,
+ RootClass.DEFAULT_DISCRIMINATOR_COLUMN_NAME,
+ mappings
+ );
+ if ( !discrim.isTypeSpecified() ) {
+ discrim.setTypeName( "string" );
+ // ( (Column) discrim.getColumnIterator().next() ).setType(type);
+ }
+ entity.setPolymorphic( true );
+ if ( "true".equals( subnode.attributeValue( "force" ) ) )
+ entity.setForceDiscriminator( true );
+ if ( "false".equals( subnode.attributeValue( "insert" ) ) )
+ entity.setDiscriminatorInsertable( false );
+ }
+
+ public static void bindClass(Element node, PersistentClass persistentClass, Mappings mappings,
+ java.util.Map inheritedMetas) throws MappingException {
+ // transfer an explicitly defined entity name
+ // handle the lazy attribute
+ Attribute lazyNode = node.attribute( "lazy" );
+ boolean lazy = lazyNode == null ?
+ mappings.isDefaultLazy() :
+ "true".equals( lazyNode.getValue() );
+ // go ahead and set the lazy here, since pojo.proxy can override it.
+ persistentClass.setLazy( lazy );
+
+ String entityName = node.attributeValue( "entity-name" );
+ if ( entityName == null ) entityName = getClassName( node.attribute("name"), mappings );
+ if ( entityName==null ) {
+ throw new MappingException( "Unable to determine entity name" );
+ }
+ persistentClass.setEntityName( entityName );
+
+ bindPojoRepresentation( node, persistentClass, mappings, inheritedMetas );
+ bindDom4jRepresentation( node, persistentClass, mappings, inheritedMetas );
+ bindMapRepresentation( node, persistentClass, mappings, inheritedMetas );
+
+ bindPersistentClassCommonValues( node, persistentClass, mappings, inheritedMetas );
+
+ }
+
+ private static void bindPojoRepresentation(Element node, PersistentClass entity,
+ Mappings mappings, java.util.Map metaTags) {
+
+ String className = getClassName( node.attribute( "name" ), mappings );
+ String proxyName = getClassName( node.attribute( "proxy" ), mappings );
+
+ entity.setClassName( className );
+
+ if ( proxyName != null ) {
+ entity.setProxyInterfaceName( proxyName );
+ entity.setLazy( true );
+ }
+ else if ( entity.isLazy() ) {
+ entity.setProxyInterfaceName( className );
+ }
+
+ Element tuplizer = locateTuplizerDefinition( node, EntityMode.POJO );
+ if ( tuplizer != null ) {
+ entity.addTuplizer( EntityMode.POJO, tuplizer.attributeValue( "class" ) );
+ }
+ }
+
+ private static void bindDom4jRepresentation(Element node, PersistentClass entity,
+ Mappings mappings, java.util.Map inheritedMetas) {
+ String nodeName = node.attributeValue( "node" );
+ if (nodeName==null) nodeName = StringHelper.unqualify( entity.getEntityName() );
+ entity.setNodeName(nodeName);
+
+ Element tuplizer = locateTuplizerDefinition( node, EntityMode.DOM4J );
+ if ( tuplizer != null ) {
+ entity.addTuplizer( EntityMode.DOM4J, tuplizer.attributeValue( "class" ) );
+ }
+ }
+
+ private static void bindMapRepresentation(Element node, PersistentClass entity,
+ Mappings mappings, java.util.Map inheritedMetas) {
+ Element tuplizer = locateTuplizerDefinition( node, EntityMode.MAP );
+ if ( tuplizer != null ) {
+ entity.addTuplizer( EntityMode.MAP, tuplizer.attributeValue( "class" ) );
+ }
+ }
+
+ /**
+ * Locate any explicit tuplizer definition in the metadata, for the given entity-mode.
+ *
+ * @param container The containing element (representing the entity/component)
+ * @param entityMode The entity-mode for which to locate the tuplizer element
+ * @return The tuplizer element, or null.
+ */
+ private static Element locateTuplizerDefinition(Element container, EntityMode entityMode) {
+ Iterator itr = container.elements( "tuplizer" ).iterator();
+ while( itr.hasNext() ) {
+ final Element tuplizerElem = ( Element ) itr.next();
+ if ( entityMode.toString().equals( tuplizerElem.attributeValue( "entity-mode") ) ) {
+ return tuplizerElem;
+ }
+ }
+ return null;
+ }
+
+ private static void bindPersistentClassCommonValues(Element node, PersistentClass entity,
+ Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
+ // DISCRIMINATOR
+ Attribute discriminatorNode = node.attribute( "discriminator-value" );
+ entity.setDiscriminatorValue( ( discriminatorNode == null )
+ ? entity.getEntityName()
+ : discriminatorNode.getValue() );
+
+ // DYNAMIC UPDATE
+ Attribute dynamicNode = node.attribute( "dynamic-update" );
+ entity.setDynamicUpdate(
+ dynamicNode != null && "true".equals( dynamicNode.getValue() )
+ );
+
+ // DYNAMIC INSERT
+ Attribute insertNode = node.attribute( "dynamic-insert" );
+ entity.setDynamicInsert(
+ insertNode != null && "true".equals( insertNode.getValue() )
+ );
+
+ // IMPORT
+ mappings.addImport( entity.getEntityName(), entity.getEntityName() );
+ if ( mappings.isAutoImport() && entity.getEntityName().indexOf( '.' ) > 0 ) {
+ mappings.addImport(
+ entity.getEntityName(),
+ StringHelper.unqualify( entity.getEntityName() )
+ );
+ }
+
+ // BATCH SIZE
+ Attribute batchNode = node.attribute( "batch-size" );
+ if ( batchNode != null ) entity.setBatchSize( Integer.parseInt( batchNode.getValue() ) );
+
+ // SELECT BEFORE UPDATE
+ Attribute sbuNode = node.attribute( "select-before-update" );
+ if ( sbuNode != null ) entity.setSelectBeforeUpdate( "true".equals( sbuNode.getValue() ) );
+
+ // OPTIMISTIC LOCK MODE
+ Attribute olNode = node.attribute( "optimistic-lock" );
+ entity.setOptimisticLockMode( getOptimisticLockMode( olNode ) );
+
+ entity.setMetaAttributes( getMetas( node, inheritedMetas ) );
+
+ // PERSISTER
+ Attribute persisterNode = node.attribute( "persister" );
+ if ( persisterNode == null ) {
+ // persister = SingleTableEntityPersister.class;
+ }
+ else {
+ try {
+ entity.setEntityPersisterClass( ReflectHelper.classForName( persisterNode
+ .getValue() ) );
+ }
+ catch (ClassNotFoundException cnfe) {
+ throw new MappingException( "Could not find persister class: "
+ + persisterNode.getValue() );
+ }
+ }
+
+ // CUSTOM SQL
+ handleCustomSQL( node, entity );
+
+ Iterator tables = node.elementIterator( "synchronize" );
+ while ( tables.hasNext() ) {
+ entity.addSynchronizedTable( ( (Element) tables.next() ).attributeValue( "table" ) );
+ }
+
+ Attribute abstractNode = node.attribute( "abstract" );
+ Boolean isAbstract = abstractNode == null
+ ? null
+ : "true".equals( abstractNode.getValue() )
+ ? Boolean.TRUE
+ : "false".equals( abstractNode.getValue() )
+ ? Boolean.FALSE
+ : null;
+ entity.setAbstract( isAbstract );
+ }
+
+ private static void handleCustomSQL(Element node, PersistentClass model)
+ throws MappingException {
+ Element element = node.element( "sql-insert" );
+ if ( element != null ) {
+ boolean callable = isCallable( element );
+ model.setCustomSQLInsert( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) );
+ }
+
+ element = node.element( "sql-delete" );
+ if ( element != null ) {
+ boolean callable = isCallable( element );
+ model.setCustomSQLDelete( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) );
+ }
+
+ element = node.element( "sql-update" );
+ if ( element != null ) {
+ boolean callable = isCallable( element );
+ model.setCustomSQLUpdate( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) );
+ }
+
+ element = node.element( "loader" );
+ if ( element != null ) {
+ model.setLoaderName( element.attributeValue( "query-ref" ) );
+ }
+ }
+
+ private static void handleCustomSQL(Element node, Join model) throws MappingException {
+ Element element = node.element( "sql-insert" );
+ if ( element != null ) {
+ boolean callable = isCallable( element );
+ model.setCustomSQLInsert( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) );
+ }
+
+ element = node.element( "sql-delete" );
+ if ( element != null ) {
+ boolean callable = isCallable( element );
+ model.setCustomSQLDelete( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) );
+ }
+
+ element = node.element( "sql-update" );
+ if ( element != null ) {
+ boolean callable = isCallable( element );
+ model.setCustomSQLUpdate( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) );
+ }
+ }
+
+ private static void handleCustomSQL(Element node, Collection model) throws MappingException {
+ Element element = node.element( "sql-insert" );
+ if ( element != null ) {
+ boolean callable = isCallable( element, true );
+ model.setCustomSQLInsert( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) );
+ }
+
+ element = node.element( "sql-delete" );
+ if ( element != null ) {
+ boolean callable = isCallable( element, true );
+ model.setCustomSQLDelete( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) );
+ }
+
+ element = node.element( "sql-update" );
+ if ( element != null ) {
+ boolean callable = isCallable( element, true );
+ model.setCustomSQLUpdate( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) );
+ }
+
+ element = node.element( "sql-delete-all" );
+ if ( element != null ) {
+ boolean callable = isCallable( element, true );
+ model.setCustomSQLDeleteAll( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) );
+ }
+ }
+
+ private static boolean isCallable(Element e) throws MappingException {
+ return isCallable( e, true );
+ }
+
+ private static boolean isCallable(Element element, boolean supportsCallable)
+ throws MappingException {
+ Attribute attrib = element.attribute( "callable" );
+ if ( attrib != null && "true".equals( attrib.getValue() ) ) {
+ if ( !supportsCallable ) {
+ throw new MappingException( "callable attribute not supported yet!" );
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private static ExecuteUpdateResultCheckStyle getResultCheckStyle(Element element, boolean callable) throws MappingException {
+ Attribute attr = element.attribute( "check" );
+ if ( attr == null ) {
+ // use COUNT as the default. This mimics the old behavior, although
+ // NONE might be a better option moving forward in the case of callable
+ return ExecuteUpdateResultCheckStyle.COUNT;
+ }
+ return ExecuteUpdateResultCheckStyle.parse( attr.getValue() );
+ }
+
+ public static void bindUnionSubclass(Element node, UnionSubclass unionSubclass,
+ Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
+
+ bindClass( node, unionSubclass, mappings, inheritedMetas );
+ inheritedMetas = getMetas( node, inheritedMetas, true ); // get meta's from <subclass>
+
+ if ( unionSubclass.getEntityPersisterClass() == null ) {
+ unionSubclass.getRootClass().setEntityPersisterClass(
+ UnionSubclassEntityPersister.class );
+ }
+
+ Attribute schemaNode = node.attribute( "schema" );
+ String schema = schemaNode == null ?
+ mappings.getSchemaName() : schemaNode.getValue();
+
+ Attribute catalogNode = node.attribute( "catalog" );
+ String catalog = catalogNode == null ?
+ mappings.getCatalogName() : catalogNode.getValue();
+
+ Table denormalizedSuperTable = unionSubclass.getSuperclass().getTable();
+ Table mytable = mappings.addDenormalizedTable(
+ schema,
+ catalog,
+ getClassTableName(unionSubclass, node, schema, catalog, denormalizedSuperTable, mappings ),
+ unionSubclass.isAbstract() != null && unionSubclass.isAbstract().booleanValue(),
+ getSubselect( node ),
+ denormalizedSuperTable
+ );
+ unionSubclass.setTable( mytable );
+
+ log.info(
+ "Mapping union-subclass: " + unionSubclass.getEntityName() +
+ " -> " + unionSubclass.getTable().getName()
+ );
+
+ createClassProperties( node, unionSubclass, mappings, inheritedMetas );
+
+ }
+
+ public static void bindSubclass(Element node, Subclass subclass, Mappings mappings,
+ java.util.Map inheritedMetas) throws MappingException {
+
+ bindClass( node, subclass, mappings, inheritedMetas );
+ inheritedMetas = getMetas( node, inheritedMetas, true ); // get meta's from <subclass>
+
+ if ( subclass.getEntityPersisterClass() == null ) {
+ subclass.getRootClass()
+ .setEntityPersisterClass( SingleTableEntityPersister.class );
+ }
+
+ log.info(
+ "Mapping subclass: " + subclass.getEntityName() +
+ " -> " + subclass.getTable().getName()
+ );
+
+ // properties
+ createClassProperties( node, subclass, mappings, inheritedMetas );
+ }
+
+ private static String getClassTableName(
+ PersistentClass model, Element node, String schema, String catalog, Table denormalizedSuperTable,
+ Mappings mappings
+ ) {
+ Attribute tableNameNode = node.attribute( "table" );
+ String logicalTableName;
+ String physicalTableName;
+ if ( tableNameNode == null ) {
+ logicalTableName = StringHelper.unqualify( model.getEntityName() );
+ physicalTableName = mappings.getNamingStrategy().classToTableName( model.getEntityName() );
+ }
+ else {
+ logicalTableName = tableNameNode.getValue();
+ physicalTableName = mappings.getNamingStrategy().tableName( logicalTableName );
+ }
+ mappings.addTableBinding( schema, catalog, logicalTableName, physicalTableName, denormalizedSuperTable );
+ return physicalTableName;
+ }
+
+ public static void bindJoinedSubclass(Element node, JoinedSubclass joinedSubclass,
+ Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
+
+ bindClass( node, joinedSubclass, mappings, inheritedMetas );
+ inheritedMetas = getMetas( node, inheritedMetas, true ); // get meta's from
+ // <joined-subclass>
+
+ // joined subclasses
+ if ( joinedSubclass.getEntityPersisterClass() == null ) {
+ joinedSubclass.getRootClass()
+ .setEntityPersisterClass( JoinedSubclassEntityPersister.class );
+ }
+
+ Attribute schemaNode = node.attribute( "schema" );
+ String schema = schemaNode == null ?
+ mappings.getSchemaName() : schemaNode.getValue();
+
+ Attribute catalogNode = node.attribute( "catalog" );
+ String catalog = catalogNode == null ?
+ mappings.getCatalogName() : catalogNode.getValue();
+
+ Table mytable = mappings.addTable(
+ schema,
+ catalog,
+ getClassTableName( joinedSubclass, node, schema, catalog, null, mappings ),
+ getSubselect( node ),
+ false
+ );
+ joinedSubclass.setTable( mytable );
+ bindComment(mytable, node);
+
+ log.info(
+ "Mapping joined-subclass: " + joinedSubclass.getEntityName() +
+ " -> " + joinedSubclass.getTable().getName()
+ );
+
+ // KEY
+ Element keyNode = node.element( "key" );
+ SimpleValue key = new DependantValue( mytable, joinedSubclass.getIdentifier() );
+ joinedSubclass.setKey( key );
+ key.setCascadeDeleteEnabled( "cascade".equals( keyNode.attributeValue( "on-delete" ) ) );
+ bindSimpleValue( keyNode, key, false, joinedSubclass.getEntityName(), mappings );
+
+ // model.getKey().setType( new Type( model.getIdentifier() ) );
+ joinedSubclass.createPrimaryKey();
+ joinedSubclass.createForeignKey();
+
+ // CHECK
+ Attribute chNode = node.attribute( "check" );
+ if ( chNode != null ) mytable.addCheckConstraint( chNode.getValue() );
+
+ // properties
+ createClassProperties( node, joinedSubclass, mappings, inheritedMetas );
+
+ }
+
+ private static void bindJoin(Element node, Join join, Mappings mappings,
+ java.util.Map inheritedMetas) throws MappingException {
+
+ PersistentClass persistentClass = join.getPersistentClass();
+ String path = persistentClass.getEntityName();
+
+ // TABLENAME
+
+ Attribute schemaNode = node.attribute( "schema" );
+ String schema = schemaNode == null ?
+ mappings.getSchemaName() : schemaNode.getValue();
+ Attribute catalogNode = node.attribute( "catalog" );
+ String catalog = catalogNode == null ?
+ mappings.getCatalogName() : catalogNode.getValue();
+ Table primaryTable = persistentClass.getTable();
+ Table table = mappings.addTable(
+ schema,
+ catalog,
+ getClassTableName( persistentClass, node, schema, catalog, primaryTable, mappings ),
+ getSubselect( node ),
+ false
+ );
+ join.setTable( table );
+ bindComment(table, node);
+
+ Attribute fetchNode = node.attribute( "fetch" );
+ if ( fetchNode != null ) {
+ join.setSequentialSelect( "select".equals( fetchNode.getValue() ) );
+ }
+
+ Attribute invNode = node.attribute( "inverse" );
+ if ( invNode != null ) {
+ join.setInverse( "true".equals( invNode.getValue() ) );
+ }
+
+ Attribute nullNode = node.attribute( "optional" );
+ if ( nullNode != null ) {
+ join.setOptional( "true".equals( nullNode.getValue() ) );
+ }
+
+ log.info(
+ "Mapping class join: " + persistentClass.getEntityName() +
+ " -> " + join.getTable().getName()
+ );
+
+ // KEY
+ Element keyNode = node.element( "key" );
+ SimpleValue key = new DependantValue( table, persistentClass.getIdentifier() );
+ join.setKey( key );
+ key.setCascadeDeleteEnabled( "cascade".equals( keyNode.attributeValue( "on-delete" ) ) );
+ bindSimpleValue( keyNode, key, false, persistentClass.getEntityName(), mappings );
+
+ // join.getKey().setType( new Type( lazz.getIdentifier() ) );
+ join.createPrimaryKey();
+ join.createForeignKey();
+
+ // PROPERTIES
+ Iterator iter = node.elementIterator();
+ while ( iter.hasNext() ) {
+ Element subnode = (Element) iter.next();
+ String name = subnode.getName();
+ String propertyName = subnode.attributeValue( "name" );
+
+ Value value = null;
+ if ( "many-to-one".equals( name ) ) {
+ value = new ManyToOne( table );
+ bindManyToOne( subnode, (ManyToOne) value, propertyName, true, mappings );
+ }
+ else if ( "any".equals( name ) ) {
+ value = new Any( table );
+ bindAny( subnode, (Any) value, true, mappings );
+ }
+ else if ( "property".equals( name ) ) {
+ value = new SimpleValue( table );
+ bindSimpleValue( subnode, (SimpleValue) value, true, propertyName, mappings );
+ }
+ else if ( "component".equals( name ) || "dynamic-component".equals( name ) ) {
+ String subpath = StringHelper.qualify( path, propertyName );
+ value = new Component( join );
+ bindComponent(
+ subnode,
+ (Component) value,
+ join.getPersistentClass().getClassName(),
+ propertyName,
+ subpath,
+ true,
+ false,
+ mappings,
+ inheritedMetas,
+ false
+ );
+ }
+
+ if ( value != null ) {
+ Property prop = createProperty( value, propertyName, persistentClass
+ .getEntityName(), subnode, mappings, inheritedMetas );
+ prop.setOptional( join.isOptional() );
+ join.addProperty( prop );
+ }
+
+ }
+
+ // CUSTOM SQL
+ handleCustomSQL( node, join );
+
+ }
+
+ public static void bindColumns(final Element node, final SimpleValue simpleValue,
+ final boolean isNullable, final boolean autoColumn, final String propertyPath,
+ final Mappings mappings) throws MappingException {
+
+ Table table = simpleValue.getTable();
+
+ // COLUMN(S)
+ Attribute columnAttribute = node.attribute( "column" );
+ if ( columnAttribute == null ) {
+ Iterator iter = node.elementIterator();
+ int count = 0;
+ while ( iter.hasNext() ) {
+ Element columnElement = (Element) iter.next();
+ if ( columnElement.getName().equals( "column" ) ) {
+ Column column = new Column();
+ column.setValue( simpleValue );
+ column.setTypeIndex( count++ );
+ bindColumn( columnElement, column, isNullable );
+ String logicalColumnName = mappings.getNamingStrategy().logicalColumnName(
+ columnElement.attributeValue( "name" ), propertyPath
+ );
+ column.setName( mappings.getNamingStrategy().columnName(
+ logicalColumnName ) );
+ if ( table != null ) {
+ table.addColumn( column ); // table=null -> an association
+ // - fill it in later
+ //TODO fill in the mappings for table == null
+ mappings.addColumnBinding( logicalColumnName, column, table );
+ }
+
+
+ simpleValue.addColumn( column );
+ // column index
+ bindIndex( columnElement.attribute( "index" ), table, column, mappings );
+ bindIndex( node.attribute( "index" ), table, column, mappings );
+ //column unique-key
+ bindUniqueKey( columnElement.attribute( "unique-key" ), table, column, mappings );
+ bindUniqueKey( node.attribute( "unique-key" ), table, column, mappings );
+ }
+ else if ( columnElement.getName().equals( "formula" ) ) {
+ Formula formula = new Formula();
+ formula.setFormula( columnElement.getText() );
+ simpleValue.addFormula( formula );
+ }
+ }
+ }
+ else {
+ if ( node.elementIterator( "column" ).hasNext() ) {
+ throw new MappingException(
+ "column attribute may not be used together with <column> subelement" );
+ }
+ if ( node.elementIterator( "formula" ).hasNext() ) {
+ throw new MappingException(
+ "column attribute may not be used together with <formula> subelement" );
+ }
+
+ Column column = new Column();
+ column.setValue( simpleValue );
+ bindColumn( node, column, isNullable );
+ String logicalColumnName = mappings.getNamingStrategy().logicalColumnName(
+ columnAttribute.getValue(), propertyPath
+ );
+ column.setName( mappings.getNamingStrategy().columnName( logicalColumnName ) );
+ if ( table != null ) {
+ table.addColumn( column ); // table=null -> an association - fill
+ // it in later
+ //TODO fill in the mappings for table == null
+ mappings.addColumnBinding( logicalColumnName, column, table );
+ }
+ simpleValue.addColumn( column );
+ bindIndex( node.attribute( "index" ), table, column, mappings );
+ bindUniqueKey( node.attribute( "unique-key" ), table, column, mappings );
+ }
+
+ if ( autoColumn && simpleValue.getColumnSpan() == 0 ) {
+ Column column = new Column();
+ column.setValue( simpleValue );
+ bindColumn( node, column, isNullable );
+ column.setName( mappings.getNamingStrategy().propertyToColumnName( propertyPath ) );
+ String logicalName = mappings.getNamingStrategy().logicalColumnName( null, propertyPath );
+ mappings.addColumnBinding( logicalName, column, table );
+ /* TODO: joinKeyColumnName & foreignKeyColumnName should be called either here or at a
+ * slightly higer level in the stack (to get all the information we need)
+ * Right now HbmBinder does not support the
+ */
+ simpleValue.getTable().addColumn( column );
+ simpleValue.addColumn( column );
+ bindIndex( node.attribute( "index" ), table, column, mappings );
+ bindUniqueKey( node.attribute( "unique-key" ), table, column, mappings );
+ }
+
+ }
+
+ private static void bindIndex(Attribute indexAttribute, Table table, Column column, Mappings mappings) {
+ if ( indexAttribute != null && table != null ) {
+ StringTokenizer tokens = new StringTokenizer( indexAttribute.getValue(), ", " );
+ while ( tokens.hasMoreTokens() ) {
+ table.getOrCreateIndex( tokens.nextToken() ).addColumn( column );
+ }
+ }
+ }
+
+ private static void bindUniqueKey(Attribute uniqueKeyAttribute, Table table, Column column, Mappings mappings) {
+ if ( uniqueKeyAttribute != null && table != null ) {
+ StringTokenizer tokens = new StringTokenizer( uniqueKeyAttribute.getValue(), ", " );
+ while ( tokens.hasMoreTokens() ) {
+ table.getOrCreateUniqueKey( tokens.nextToken() ).addColumn( column );
+ }
+ }
+ }
+
+ // automatically makes a column with the default name if none is specifed by XML
+ public static void bindSimpleValue(Element node, SimpleValue simpleValue, boolean isNullable,
+ String path, Mappings mappings) throws MappingException {
+ bindSimpleValueType( node, simpleValue, mappings );
+
+ bindColumnsOrFormula( node, simpleValue, path, isNullable, mappings );
+
+ Attribute fkNode = node.attribute( "foreign-key" );
+ if ( fkNode != null ) simpleValue.setForeignKeyName( fkNode.getValue() );
+ }
+
+ private static void bindSimpleValueType(Element node, SimpleValue simpleValue, Mappings mappings)
+ throws MappingException {
+ String typeName = null;
+
+ Properties parameters = new Properties();
+
+ Attribute typeNode = node.attribute( "type" );
+ if ( typeNode == null ) typeNode = node.attribute( "id-type" ); // for an any
+ if ( typeNode != null ) typeName = typeNode.getValue();
+
+ Element typeChild = node.element( "type" );
+ if ( typeName == null && typeChild != null ) {
+ typeName = typeChild.attribute( "name" ).getValue();
+ Iterator typeParameters = typeChild.elementIterator( "param" );
+
+ while ( typeParameters.hasNext() ) {
+ Element paramElement = (Element) typeParameters.next();
+ parameters.setProperty(
+ paramElement.attributeValue( "name" ),
+ paramElement.getTextTrim()
+ );
+ }
+ }
+
+ TypeDef typeDef = mappings.getTypeDef( typeName );
+ if ( typeDef != null ) {
+ typeName = typeDef.getTypeClass();
+ // parameters on the property mapping should
+ // override parameters in the typedef
+ Properties allParameters = new Properties();
+ allParameters.putAll( typeDef.getParameters() );
+ allParameters.putAll( parameters );
+ parameters = allParameters;
+ }
+
+ if ( !parameters.isEmpty() ) simpleValue.setTypeParameters( parameters );
+
+ if ( typeName != null ) simpleValue.setTypeName( typeName );
+ }
+
+ public static void bindProperty(
+ Element node,
+ Property property,
+ Mappings mappings,
+ java.util.Map inheritedMetas) throws MappingException {
+
+ String propName = node.attributeValue( "name" );
+ property.setName( propName );
+ String nodeName = node.attributeValue( "node" );
+ if (nodeName==null) nodeName = propName;
+ property.setNodeName( nodeName );
+
+ // TODO:
+ //Type type = model.getValue().getType();
+ //if (type==null) throw new MappingException(
+ //"Could not determine a property type for: " + model.getName() );
+
+ Attribute accessNode = node.attribute( "access" );
+ if ( accessNode != null ) {
+ property.setPropertyAccessorName( accessNode.getValue() );
+ }
+ else if ( node.getName().equals( "properties" ) ) {
+ property.setPropertyAccessorName( "embedded" );
+ }
+ else {
+ property.setPropertyAccessorName( mappings.getDefaultAccess() );
+ }
+
+ Attribute cascadeNode = node.attribute( "cascade" );
+ property.setCascade( cascadeNode == null ? mappings.getDefaultCascade() : cascadeNode
+ .getValue() );
+
+ Attribute updateNode = node.attribute( "update" );
+ property.setUpdateable( updateNode == null || "true".equals( updateNode.getValue() ) );
+
+ Attribute insertNode = node.attribute( "insert" );
+ property.setInsertable( insertNode == null || "true".equals( insertNode.getValue() ) );
+
+ Attribute lockNode = node.attribute( "optimistic-lock" );
+ property.setOptimisticLocked( lockNode == null || "true".equals( lockNode.getValue() ) );
+
+ Attribute generatedNode = node.attribute( "generated" );
+ String generationName = generatedNode == null ? null : generatedNode.getValue();
+ PropertyGeneration generation = PropertyGeneration.parse( generationName );
+ property.setGeneration( generation );
+
+ if ( generation == PropertyGeneration.ALWAYS || generation == PropertyGeneration.INSERT ) {
+ // generated properties can *never* be insertable...
+ if ( property.isInsertable() ) {
+ if ( insertNode == null ) {
+ // insertable simply because that is the user did not specify
+ // anything; just override it
+ property.setInsertable( false );
+ }
+ else {
+ // the user specifically supplied insert="true",
+ // which constitutes an illegal combo
+ throw new MappingException(
+ "cannot specify both insert=\"true\" and generated=\"" + generation.getName() +
+ "\" for property: " +
+ propName
+ );
+ }
+ }
+
+ // properties generated on update can never be updateable...
+ if ( property.isUpdateable() && generation == PropertyGeneration.ALWAYS ) {
+ if ( updateNode == null ) {
+ // updateable only because the user did not specify
+ // anything; just override it
+ property.setUpdateable( false );
+ }
+ else {
+ // the user specifically supplied update="true",
+ // which constitutes an illegal combo
+ throw new MappingException(
+ "cannot specify both update=\"true\" and generated=\"" + generation.getName() +
+ "\" for property: " +
+ propName
+ );
+ }
+ }
+ }
+
+ boolean isLazyable = "property".equals( node.getName() ) ||
+ "component".equals( node.getName() ) ||
+ "many-to-one".equals( node.getName() ) ||
+ "one-to-one".equals( node.getName() ) ||
+ "any".equals( node.getName() );
+ if ( isLazyable ) {
+ Attribute lazyNode = node.attribute( "lazy" );
+ property.setLazy( lazyNode != null && "true".equals( lazyNode.getValue() ) );
+ }
+
+ if ( log.isDebugEnabled() ) {
+ String msg = "Mapped property: " + property.getName();
+ String columns = columns( property.getValue() );
+ if ( columns.length() > 0 ) msg += " -> " + columns;
+ // TODO: this fails if we run with debug on!
+ // if ( model.getType()!=null ) msg += ", type: " + model.getType().getName();
+ log.debug( msg );
+ }
+
+ property.setMetaAttributes( getMetas( node, inheritedMetas ) );
+
+ }
+
+ private static String columns(Value val) {
+ StringBuffer columns = new StringBuffer();
+ Iterator iter = val.getColumnIterator();
+ while ( iter.hasNext() ) {
+ columns.append( ( (Selectable) iter.next() ).getText() );
+ if ( iter.hasNext() ) columns.append( ", " );
+ }
+ return columns.toString();
+ }
+
+ /**
+ * Called for all collections
+ */
+ public static void bindCollection(Element node, Collection collection, String className,
+ String path, Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
+
+ // ROLENAME
+ collection.setRole(path);
+
+ Attribute inverseNode = node.attribute( "inverse" );
+ if ( inverseNode != null ) {
+ collection.setInverse( "true".equals( inverseNode.getValue() ) );
+ }
+
+ Attribute mutableNode = node.attribute( "mutable" );
+ if ( mutableNode != null ) {
+ collection.setMutable( !"false".equals( mutableNode.getValue() ) );
+ }
+
+ Attribute olNode = node.attribute( "optimistic-lock" );
+ collection.setOptimisticLocked( olNode == null || "true".equals( olNode.getValue() ) );
+
+ Attribute orderNode = node.attribute( "order-by" );
+ if ( orderNode != null ) {
+ if ( Environment.jvmSupportsLinkedHashCollections() || ( collection instanceof Bag ) ) {
+ collection.setOrderBy( orderNode.getValue() );
+ }
+ else {
+ log.warn( "Attribute \"order-by\" ignored in JDK1.3 or less" );
+ }
+ }
+ Attribute whereNode = node.attribute( "where" );
+ if ( whereNode != null ) {
+ collection.setWhere( whereNode.getValue() );
+ }
+ Attribute batchNode = node.attribute( "batch-size" );
+ if ( batchNode != null ) {
+ collection.setBatchSize( Integer.parseInt( batchNode.getValue() ) );
+ }
+
+ String nodeName = node.attributeValue( "node" );
+ if ( nodeName == null ) nodeName = node.attributeValue( "name" );
+ collection.setNodeName( nodeName );
+ String embed = node.attributeValue( "embed-xml" );
+ collection.setEmbedded( embed==null || "true".equals(embed) );
+
+
+ // PERSISTER
+ Attribute persisterNode = node.attribute( "persister" );
+ if ( persisterNode != null ) {
+ try {
+ collection.setCollectionPersisterClass( ReflectHelper.classForName( persisterNode
+ .getValue() ) );
+ }
+ catch (ClassNotFoundException cnfe) {
+ throw new MappingException( "Could not find collection persister class: "
+ + persisterNode.getValue() );
+ }
+ }
+
+ Attribute typeNode = node.attribute( "collection-type" );
+ if ( typeNode != null ) {
+ String typeName = typeNode.getValue();
+ TypeDef typeDef = mappings.getTypeDef( typeName );
+ if ( typeDef != null ) {
+ collection.setTypeName( typeDef.getTypeClass() );
+ collection.setTypeParameters( typeDef.getParameters() );
+ }
+ else {
+ collection.setTypeName( typeName );
+ }
+ }
+
+ // FETCH STRATEGY
+
+ initOuterJoinFetchSetting( node, collection );
+
+ if ( "subselect".equals( node.attributeValue("fetch") ) ) {
+ collection.setSubselectLoadable(true);
+ collection.getOwner().setSubselectLoadableCollections(true);
+ }
+
+ initLaziness( node, collection, mappings, "true", mappings.isDefaultLazy() );
+ //TODO: suck this into initLaziness!
+ if ( "extra".equals( node.attributeValue("lazy") ) ) {
+ collection.setLazy(true);
+ collection.setExtraLazy(true);
+ }
+
+ Element oneToManyNode = node.element( "one-to-many" );
+ if ( oneToManyNode != null ) {
+ OneToMany oneToMany = new OneToMany( collection.getOwner() );
+ collection.setElement( oneToMany );
+ bindOneToMany( oneToManyNode, oneToMany, mappings );
+ // we have to set up the table later!! yuck
+ }
+ else {
+ // TABLE
+ Attribute tableNode = node.attribute( "table" );
+ String tableName;
+ if ( tableNode != null ) {
+ tableName = mappings.getNamingStrategy().tableName( tableNode.getValue() );
+ }
+ else {
+ //tableName = mappings.getNamingStrategy().propertyToTableName( className, path );
+ Table ownerTable = collection.getOwner().getTable();
+ //TODO mappings.getLogicalTableName(ownerTable)
+ String logicalOwnerTableName = ownerTable.getName();
+ //FIXME we don't have the associated entity table name here, has to be done in a second pass
+ tableName = mappings.getNamingStrategy().collectionTableName(
+ collection.getOwner().getEntityName(),
+ logicalOwnerTableName ,
+ null,
+ null,
+ path
+ );
+ }
+ Attribute schemaNode = node.attribute( "schema" );
+ String schema = schemaNode == null ?
+ mappings.getSchemaName() : schemaNode.getValue();
+
+ Attribute catalogNode = node.attribute( "catalog" );
+ String catalog = catalogNode == null ?
+ mappings.getCatalogName() : catalogNode.getValue();
+
+ Table table = mappings.addTable(
+ schema,
+ catalog,
+ tableName,
+ getSubselect( node ),
+ false
+ );
+ collection.setCollectionTable( table );
+ bindComment(table, node);
+
+ log.info(
+ "Mapping collection: " + collection.getRole() +
+ " -> " + collection.getCollectionTable().getName()
+ );
+ }
+
+ // SORT
+ Attribute sortedAtt = node.attribute( "sort" );
+ // unsorted, natural, comparator.class.name
+ if ( sortedAtt == null || sortedAtt.getValue().equals( "unsorted" ) ) {
+ collection.setSorted( false );
+ }
+ else {
+ collection.setSorted( true );
+ String comparatorClassName = sortedAtt.getValue();
+ if ( !comparatorClassName.equals( "natural" ) ) {
+ collection.setComparatorClassName(comparatorClassName);
+ }
+ }
+
+ // ORPHAN DELETE (used for programmer error detection)
+ Attribute cascadeAtt = node.attribute( "cascade" );
+ if ( cascadeAtt != null && cascadeAtt.getValue().indexOf( "delete-orphan" ) >= 0 ) {
+ collection.setOrphanDelete( true );
+ }
+
+ // CUSTOM SQL
+ handleCustomSQL( node, collection );
+ // set up second pass
+ if ( collection instanceof List ) {
+ mappings.addSecondPass( new ListSecondPass( node, mappings, (List) collection, inheritedMetas ) );
+ }
+ else if ( collection instanceof Map ) {
+ mappings.addSecondPass( new MapSecondPass( node, mappings, (Map) collection, inheritedMetas ) );
+ }
+ else if ( collection instanceof IdentifierCollection ) {
+ mappings.addSecondPass( new IdentifierCollectionSecondPass(
+ node,
+ mappings,
+ collection,
+ inheritedMetas
+ ) );
+ }
+ else {
+ mappings.addSecondPass( new CollectionSecondPass( node, mappings, collection, inheritedMetas ) );
+ }
+
+ Iterator iter = node.elementIterator( "filter" );
+ while ( iter.hasNext() ) {
+ final Element filter = (Element) iter.next();
+ parseFilter( filter, collection, mappings );
+ }
+
+ Iterator tables = node.elementIterator( "synchronize" );
+ while ( tables.hasNext() ) {
+ collection.getSynchronizedTables().add(
+ ( (Element) tables.next() ).attributeValue( "table" ) );
+ }
+
+ Element element = node.element( "loader" );
+ if ( element != null ) {
+ collection.setLoaderName( element.attributeValue( "query-ref" ) );
+ }
+
+ collection.setReferencedPropertyName( node.element( "key" ).attributeValue( "property-ref" ) );
+ }
+
+ private static void initLaziness(
+ Element node,
+ Fetchable fetchable,
+ Mappings mappings,
+ String proxyVal,
+ boolean defaultLazy
+ ) {
+ Attribute lazyNode = node.attribute( "lazy" );
+ boolean isLazyTrue = lazyNode == null ?
+ defaultLazy && fetchable.isLazy() : //fetch="join" overrides default laziness
+ lazyNode.getValue().equals(proxyVal); //fetch="join" overrides default laziness
+ fetchable.setLazy( isLazyTrue );
+ }
+
+ private static void initLaziness(
+ Element node,
+ ToOne fetchable,
+ Mappings mappings,
+ boolean defaultLazy
+ ) {
+ if ( "no-proxy".equals( node.attributeValue( "lazy" ) ) ) {
+ fetchable.setUnwrapProxy(true);
+ fetchable.setLazy(true);
+ //TODO: better to degrade to lazy="false" if uninstrumented
+ }
+ else {
+ initLaziness(node, fetchable, mappings, "proxy", defaultLazy);
+ }
+ }
+
+ private static void bindColumnsOrFormula(Element node, SimpleValue simpleValue, String path,
+ boolean isNullable, Mappings mappings) {
+ Attribute formulaNode = node.attribute( "formula" );
+ if ( formulaNode != null ) {
+ Formula f = new Formula();
+ f.setFormula( formulaNode.getText() );
+ simpleValue.addFormula( f );
+ }
+ else {
+ bindColumns( node, simpleValue, isNullable, true, path, mappings );
+ }
+ }
+
+ private static void bindComment(Table table, Element node) {
+ Element comment = node.element("comment");
+ if (comment!=null) table.setComment( comment.getTextTrim() );
+ }
+
+ public static void bindManyToOne(Element node, ManyToOne manyToOne, String path,
+ boolean isNullable, Mappings mappings) throws MappingException {
+
+ bindColumnsOrFormula( node, manyToOne, path, isNullable, mappings );
+ initOuterJoinFetchSetting( node, manyToOne );
+ initLaziness( node, manyToOne, mappings, true );
+
+ Attribute ukName = node.attribute( "property-ref" );
+ if ( ukName != null ) {
+ manyToOne.setReferencedPropertyName( ukName.getValue() );
+ }
+
+ manyToOne.setReferencedEntityName( getEntityName( node, mappings ) );
+
+ String embed = node.attributeValue( "embed-xml" );
+ manyToOne.setEmbedded( embed == null || "true".equals( embed ) );
+
+ String notFound = node.attributeValue( "not-found" );
+ manyToOne.setIgnoreNotFound( "ignore".equals( notFound ) );
+
+ if( ukName != null && !manyToOne.isIgnoreNotFound() ) {
+ if ( !node.getName().equals("many-to-many") ) { //TODO: really bad, evil hack to fix!!!
+ mappings.addSecondPass( new ManyToOneSecondPass(manyToOne) );
+ }
+ }
+
+ Attribute fkNode = node.attribute( "foreign-key" );
+ if ( fkNode != null ) manyToOne.setForeignKeyName( fkNode.getValue() );
+
+ validateCascade( node, path );
+ }
+
+ private static void validateCascade(Element node, String path) {
+ String cascade = node.attributeValue("cascade");
+ if ( cascade!=null && cascade.indexOf("delete-orphan")>0 ) {
+ throw new MappingException("single-valued associations do not support orphan delete: " + path);
+ }
+ }
+
+ public static void bindAny(Element node, Any any, boolean isNullable, Mappings mappings)
+ throws MappingException {
+ any.setIdentifierType( getTypeFromXML( node ) );
+ Attribute metaAttribute = node.attribute( "meta-type" );
+ if ( metaAttribute != null ) {
+ any.setMetaType( metaAttribute.getValue() );
+
+ Iterator iter = node.elementIterator( "meta-value" );
+ if ( iter.hasNext() ) {
+ HashMap values = new HashMap();
+ org.hibernate.type.Type metaType = TypeFactory.heuristicType( any.getMetaType() );
+ while ( iter.hasNext() ) {
+ Element metaValue = (Element) iter.next();
+ try {
+ Object value = ( (DiscriminatorType) metaType ).stringToObject( metaValue
+ .attributeValue( "value" ) );
+ String entityName = getClassName( metaValue.attribute( "class" ), mappings );
+ values.put( value, entityName );
+ }
+ catch (ClassCastException cce) {
+ throw new MappingException( "meta-type was not a DiscriminatorType: "
+ + metaType.getName() );
+ }
+ catch (Exception e) {
+ throw new MappingException( "could not interpret meta-value", e );
+ }
+ }
+ any.setMetaValues( values );
+ }
+
+ }
+
+ bindColumns( node, any, isNullable, false, null, mappings );
+ }
+
+ public static void bindOneToOne(Element node, OneToOne oneToOne, String path, boolean isNullable,
+ Mappings mappings) throws MappingException {
+
+ bindColumns( node, oneToOne, isNullable, false, null, mappings );
+
+ Attribute constrNode = node.attribute( "constrained" );
+ boolean constrained = constrNode != null && constrNode.getValue().equals( "true" );
+ oneToOne.setConstrained( constrained );
+
+ oneToOne.setForeignKeyType( constrained ?
+ ForeignKeyDirection.FOREIGN_KEY_FROM_PARENT :
+ ForeignKeyDirection.FOREIGN_KEY_TO_PARENT );
+
+ initOuterJoinFetchSetting( node, oneToOne );
+ initLaziness( node, oneToOne, mappings, true );
+
+ oneToOne.setEmbedded( "true".equals( node.attributeValue( "embed-xml" ) ) );
+
+ Attribute fkNode = node.attribute( "foreign-key" );
+ if ( fkNode != null ) oneToOne.setForeignKeyName( fkNode.getValue() );
+
+ Attribute ukName = node.attribute( "property-ref" );
+ if ( ukName != null ) oneToOne.setReferencedPropertyName( ukName.getValue() );
+
+ oneToOne.setPropertyName( node.attributeValue( "name" ) );
+
+ oneToOne.setReferencedEntityName( getEntityName( node, mappings ) );
+
+ validateCascade( node, path );
+ }
+
+ public static void bindOneToMany(Element node, OneToMany oneToMany, Mappings mappings)
+ throws MappingException {
+
+ oneToMany.setReferencedEntityName( getEntityName( node, mappings ) );
+
+ String embed = node.attributeValue( "embed-xml" );
+ oneToMany.setEmbedded( embed == null || "true".equals( embed ) );
+
+ String notFound = node.attributeValue( "not-found" );
+ oneToMany.setIgnoreNotFound( "ignore".equals( notFound ) );
+
+ }
+
+ public static void bindColumn(Element node, Column column, boolean isNullable) {
+ Attribute lengthNode = node.attribute( "length" );
+ if ( lengthNode != null ) column.setLength( Integer.parseInt( lengthNode.getValue() ) );
+ Attribute scalNode = node.attribute( "scale" );
+ if ( scalNode != null ) column.setScale( Integer.parseInt( scalNode.getValue() ) );
+ Attribute precNode = node.attribute( "precision" );
+ if ( precNode != null ) column.setPrecision( Integer.parseInt( precNode.getValue() ) );
+
+ Attribute nullNode = node.attribute( "not-null" );
+ column.setNullable( nullNode == null ? isNullable : nullNode.getValue().equals( "false" ) );
+
+ Attribute unqNode = node.attribute( "unique" );
+ if ( unqNode != null ) column.setUnique( unqNode.getValue().equals( "true" ) );
+
+ column.setCheckConstraint( node.attributeValue( "check" ) );
+ column.setDefaultValue( node.attributeValue( "default" ) );
+
+ Attribute typeNode = node.attribute( "sql-type" );
+ if ( typeNode != null ) column.setSqlType( typeNode.getValue() );
+
+ Element comment = node.element("comment");
+ if (comment!=null) column.setComment( comment.getTextTrim() );
+
+ }
+
+ /**
+ * Called for arrays and primitive arrays
+ */
+ public static void bindArray(Element node, Array array, String prefix, String path,
+ Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
+
+ bindCollection( node, array, prefix, path, mappings, inheritedMetas );
+
+ Attribute att = node.attribute( "element-class" );
+ if ( att != null ) array.setElementClassName( getClassName( att, mappings ) );
+
+ }
+
+ private static Class reflectedPropertyClass(String className, String propertyName)
+ throws MappingException {
+ if ( className == null ) return null;
+ return ReflectHelper.reflectedPropertyClass( className, propertyName );
+ }
+
+ public static void bindComposite(Element node, Component component, String path,
+ boolean isNullable, Mappings mappings, java.util.Map inheritedMetas)
+ throws MappingException {
+ bindComponent(
+ node,
+ component,
+ null,
+ null,
+ path,
+ isNullable,
+ false,
+ mappings,
+ inheritedMetas,
+ false
+ );
+ }
+
+ public static void bindCompositeId(Element node, Component component,
+ PersistentClass persistentClass, String propertyName, Mappings mappings,
+ java.util.Map inheritedMetas) throws MappingException {
+
+ component.setKey( true );
+
+ String path = StringHelper.qualify(
+ persistentClass.getEntityName(),
+ propertyName == null ? "id" : propertyName );
+
+ bindComponent(
+ node,
+ component,
+ persistentClass.getClassName(),
+ propertyName,
+ path,
+ false,
+ node.attribute( "class" ) == null
+ && propertyName == null,
+ mappings,
+ inheritedMetas,
+ false
+ );
+
+ if ( "true".equals( node.attributeValue("mapped") ) ) {
+ if ( propertyName!=null ) {
+ throw new MappingException("cannot combine mapped=\"true\" with specified name");
+ }
+ Component mapper = new Component(persistentClass);
+ bindComponent(
+ node,
+ mapper,
+ persistentClass.getClassName(),
+ null,
+ path,
+ false,
+ true,
+ mappings,
+ inheritedMetas,
+ true
+ );
+ persistentClass.setIdentifierMapper(mapper);
+ Property property = new Property();
+ property.setName("_identifierMapper");
+ property.setNodeName("id");
+ property.setUpdateable(false);
+ property.setInsertable(false);
+ property.setValue(mapper);
+ property.setPropertyAccessorName( "embedded" );
+ persistentClass.addProperty(property);
+ }
+
+ }
+
+ public static void bindComponent(
+ Element node,
+ Component component,
+ String ownerClassName,
+ String parentProperty,
+ String path,
+ boolean isNullable,
+ boolean isEmbedded,
+ Mappings mappings,
+ java.util.Map inheritedMetas,
+ boolean isIdentifierMapper) throws MappingException {
+
+ component.setEmbedded( isEmbedded );
+ component.setRoleName( path );
+
+ inheritedMetas = getMetas( node, inheritedMetas );
+ component.setMetaAttributes( inheritedMetas );
+
+ Attribute classNode = isIdentifierMapper ? null : node.attribute( "class" );
+ if ( classNode != null ) {
+ component.setComponentClassName( getClassName( classNode, mappings ) );
+ }
+ else if ( "dynamic-component".equals( node.getName() ) ) {
+ component.setDynamic( true );
+ }
+ else if ( isEmbedded ) {
+ // an "embedded" component (composite ids and unique)
+ // note that this does not handle nested components
+ if ( component.getOwner().hasPojoRepresentation() ) {
+ component.setComponentClassName( component.getOwner().getClassName() );
+ }
+ else {
+ component.setDynamic(true);
+ }
+ }
+ else {
+ // todo : again, how *should* this work for non-pojo entities?
+ if ( component.getOwner().hasPojoRepresentation() ) {
+ Class reflectedClass = reflectedPropertyClass( ownerClassName, parentProperty );
+ if ( reflectedClass != null ) {
+ component.setComponentClassName( reflectedClass.getName() );
+ }
+ }
+ else {
+ component.setDynamic(true);
+ }
+ }
+
+ String nodeName = node.attributeValue( "node" );
+ if ( nodeName == null ) nodeName = node.attributeValue( "name" );
+ if ( nodeName == null ) nodeName = component.getOwner().getNodeName();
+ component.setNodeName( nodeName );
+
+ Iterator iter = node.elementIterator();
+ while ( iter.hasNext() ) {
+
+ Element subnode = (Element) iter.next();
+ String name = subnode.getName();
+ String propertyName = getPropertyName( subnode );
+ String subpath = propertyName == null ? null : StringHelper
+ .qualify( path, propertyName );
+
+ CollectionType collectType = CollectionType.collectionTypeFromString( name );
+ Value value = null;
+ if ( collectType != null ) {
+ Collection collection = collectType.create(
+ subnode,
+ subpath,
+ component.getOwner(),
+ mappings, inheritedMetas
+ );
+ mappings.addCollection( collection );
+ value = collection;
+ }
+ else if ( "many-to-one".equals( name ) || "key-many-to-one".equals( name ) ) {
+ value = new ManyToOne( component.getTable() );
+ String relativePath;
+ if (isEmbedded) {
+ relativePath = propertyName;
+ }
+ else {
+ relativePath = subpath.substring( component.getOwner().getEntityName().length() + 1 );
+ }
+ bindManyToOne( subnode, (ManyToOne) value, relativePath, isNullable, mappings );
+ }
+ else if ( "one-to-one".equals( name ) ) {
+ value = new OneToOne( component.getTable(), component.getOwner() );
+ String relativePath;
+ if (isEmbedded) {
+ relativePath = propertyName;
+ }
+ else {
+ relativePath = subpath.substring( component.getOwner().getEntityName().length() + 1 );
+ }
+ bindOneToOne( subnode, (OneToOne) value, relativePath, isNullable, mappings );
+ }
+ else if ( "any".equals( name ) ) {
+ value = new Any( component.getTable() );
+ bindAny( subnode, (Any) value, isNullable, mappings );
+ }
+ else if ( "property".equals( name ) || "key-property".equals( name ) ) {
+ value = new SimpleValue( component.getTable() );
+ String relativePath;
+ if (isEmbedded) {
+ relativePath = propertyName;
+ }
+ else {
+ relativePath = subpath.substring( component.getOwner().getEntityName().length() + 1 );
+ }
+ bindSimpleValue( subnode, (SimpleValue) value, isNullable, relativePath, mappings );
+ }
+ else if ( "component".equals( name )
+ || "dynamic-component".equals( name )
+ || "nested-composite-element".equals( name ) ) {
+ value = new Component( component ); // a nested composite element
+ bindComponent(
+ subnode,
+ (Component) value,
+ component.getComponentClassName(),
+ propertyName,
+ subpath,
+ isNullable,
+ isEmbedded,
+ mappings,
+ inheritedMetas,
+ isIdentifierMapper
+ );
+ }
+ else if ( "parent".equals( name ) ) {
+ component.setParentProperty( propertyName );
+ }
+
+ if ( value != null ) {
+ Property property = createProperty( value, propertyName, component
+ .getComponentClassName(), subnode, mappings, inheritedMetas );
+ if (isIdentifierMapper) {
+ property.setInsertable(false);
+ property.setUpdateable(false);
+ }
+ component.addProperty( property );
+ }
+ }
+
+ if ( "true".equals( node.attributeValue( "unique" ) ) ) {
+ iter = component.getColumnIterator();
+ ArrayList cols = new ArrayList();
+ while ( iter.hasNext() ) {
+ cols.add( iter.next() );
+ }
+ component.getOwner().getTable().createUniqueKey( cols );
+ }
+
+ iter = node.elementIterator( "tuplizer" );
+ while ( iter.hasNext() ) {
+ final Element tuplizerElem = ( Element ) iter.next();
+ EntityMode mode = EntityMode.parse( tuplizerElem.attributeValue( "entity-mode" ) );
+ component.addTuplizer( mode, tuplizerElem.attributeValue( "class" ) );
+ }
+ }
+
+ public static String getTypeFromXML(Element node) throws MappingException {
+ // TODO: handle TypeDefs
+ Attribute typeNode = node.attribute( "type" );
+ if ( typeNode == null ) typeNode = node.attribute( "id-type" ); // for an any
+ if ( typeNode == null ) return null; // we will have to use reflection
+ return typeNode.getValue();
+ }
+
+ private static void initOuterJoinFetchSetting(Element node, Fetchable model) {
+ Attribute fetchNode = node.attribute( "fetch" );
+ final FetchMode fetchStyle;
+ boolean lazy = true;
+ if ( fetchNode == null ) {
+ Attribute jfNode = node.attribute( "outer-join" );
+ if ( jfNode == null ) {
+ if ( "many-to-many".equals( node.getName() ) ) {
+ //NOTE SPECIAL CASE:
+ // default to join and non-lazy for the "second join"
+ // of the many-to-many
+ lazy = false;
+ fetchStyle = FetchMode.JOIN;
+ }
+ else if ( "one-to-one".equals( node.getName() ) ) {
+ //NOTE SPECIAL CASE:
+ // one-to-one constrained=false cannot be proxied,
+ // so default to join and non-lazy
+ lazy = ( (OneToOne) model ).isConstrained();
+ fetchStyle = lazy ? FetchMode.DEFAULT : FetchMode.JOIN;
+ }
+ else {
+ fetchStyle = FetchMode.DEFAULT;
+ }
+ }
+ else {
+ // use old (HB 2.1) defaults if outer-join is specified
+ String eoj = jfNode.getValue();
+ if ( "auto".equals( eoj ) ) {
+ fetchStyle = FetchMode.DEFAULT;
+ }
+ else {
+ boolean join = "true".equals( eoj );
+ fetchStyle = join ? FetchMode.JOIN : FetchMode.SELECT;
+ }
+ }
+ }
+ else {
+ boolean join = "join".equals( fetchNode.getValue() );
+ //lazy = !join;
+ fetchStyle = join ? FetchMode.JOIN : FetchMode.SELECT;
+ }
+ model.setFetchMode( fetchStyle );
+ model.setLazy(lazy);
+ }
+
+ private static void makeIdentifier(Element node, SimpleValue model, Mappings mappings) {
+
+ // GENERATOR
+ Element subnode = node.element( "generator" );
+ if ( subnode != null ) {
+ model.setIdentifierGeneratorStrategy( subnode.attributeValue( "class" ) );
+
+ Properties params = new Properties();
+
+ if ( mappings.getSchemaName() != null ) {
+ params.setProperty( PersistentIdentifierGenerator.SCHEMA, mappings.getSchemaName() );
+ }
+ if ( mappings.getCatalogName() != null ) {
+ params.setProperty( PersistentIdentifierGenerator.CATALOG, mappings.getCatalogName() );
+ }
+
+ Iterator iter = subnode.elementIterator( "param" );
+ while ( iter.hasNext() ) {
+ Element childNode = (Element) iter.next();
+ params.setProperty( childNode.attributeValue( "name" ), childNode.getText() );
+ }
+
+ model.setIdentifierGeneratorProperties( params );
+ }
+
+ model.getTable().setIdentifierValue( model );
+
+ // ID UNSAVED-VALUE
+ Attribute nullValueNode = node.attribute( "unsaved-value" );
+ if ( nullValueNode != null ) {
+ model.setNullValue( nullValueNode.getValue() );
+ }
+ else {
+ if ( "assigned".equals( model.getIdentifierGeneratorStrategy() ) ) {
+ model.setNullValue( "undefined" );
+ }
+ else {
+ model.setNullValue( null );
+ }
+ }
+ }
+
+ private static final void makeVersion(Element node, SimpleValue model) {
+
+ // VERSION UNSAVED-VALUE
+ Attribute nullValueNode = node.attribute( "unsaved-value" );
+ if ( nullValueNode != null ) {
+ model.setNullValue( nullValueNode.getValue() );
+ }
+ else {
+ model.setNullValue( "undefined" );
+ }
+
+ }
+
+ protected static void createClassProperties(Element node, PersistentClass persistentClass,
+ Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
+ createClassProperties(node, persistentClass, mappings, inheritedMetas, null, true, true, false);
+ }
+
+ protected static void createClassProperties(Element node, PersistentClass persistentClass,
+ Mappings mappings, java.util.Map inheritedMetas, UniqueKey uniqueKey,
+ boolean mutable, boolean nullable, boolean naturalId) throws MappingException {
+
+ String entityName = persistentClass.getEntityName();
+ Table table = persistentClass.getTable();
+
+ Iterator iter = node.elementIterator();
+ while ( iter.hasNext() ) {
+ Element subnode = (Element) iter.next();
+ String name = subnode.getName();
+ String propertyName = subnode.attributeValue( "name" );
+
+ CollectionType collectType = CollectionType.collectionTypeFromString( name );
+ Value value = null;
+ if ( collectType != null ) {
+ Collection collection = collectType.create(
+ subnode,
+ StringHelper.qualify( entityName, propertyName ),
+ persistentClass,
+ mappings, inheritedMetas
+ );
+ mappings.addCollection( collection );
+ value = collection;
+ }
+ else if ( "many-to-one".equals( name ) ) {
+ value = new ManyToOne( table );
+ bindManyToOne( subnode, (ManyToOne) value, propertyName, nullable, mappings );
+ }
+ else if ( "any".equals( name ) ) {
+ value = new Any( table );
+ bindAny( subnode, (Any) value, nullable, mappings );
+ }
+ else if ( "one-to-one".equals( name ) ) {
+ value = new OneToOne( table, persistentClass );
+ bindOneToOne( subnode, (OneToOne) value, propertyName, true, mappings );
+ }
+ else if ( "property".equals( name ) ) {
+ value = new SimpleValue( table );
+ bindSimpleValue( subnode, (SimpleValue) value, nullable, propertyName, mappings );
+ }
+ else if ( "component".equals( name )
+ || "dynamic-component".equals( name )
+ || "properties".equals( name ) ) {
+ String subpath = StringHelper.qualify( entityName, propertyName );
+ value = new Component( persistentClass );
+
+ bindComponent(
+ subnode,
+ (Component) value,
+ persistentClass.getClassName(),
+ propertyName,
+ subpath,
+ true,
+ "properties".equals( name ),
+ mappings,
+ inheritedMetas,
+ false
+ );
+ }
+ else if ( "join".equals( name ) ) {
+ Join join = new Join();
+ join.setPersistentClass( persistentClass );
+ bindJoin( subnode, join, mappings, inheritedMetas );
+ persistentClass.addJoin( join );
+ }
+ else if ( "subclass".equals( name ) ) {
+ handleSubclass( persistentClass, mappings, subnode, inheritedMetas );
+ }
+ else if ( "joined-subclass".equals( name ) ) {
+ handleJoinedSubclass( persistentClass, mappings, subnode, inheritedMetas );
+ }
+ else if ( "union-subclass".equals( name ) ) {
+ handleUnionSubclass( persistentClass, mappings, subnode, inheritedMetas );
+ }
+ else if ( "filter".equals( name ) ) {
+ parseFilter( subnode, persistentClass, mappings );
+ }
+ else if ( "natural-id".equals( name ) ) {
+ UniqueKey uk = new UniqueKey();
+ uk.setName("_UniqueKey");
+ uk.setTable(table);
+ //by default, natural-ids are "immutable" (constant)
+ boolean mutableId = "true".equals( subnode.attributeValue("mutable") );
+ createClassProperties(
+ subnode,
+ persistentClass,
+ mappings,
+ inheritedMetas,
+ uk,
+ mutableId,
+ false,
+ true
+ );
+ table.addUniqueKey(uk);
+ }
+ else if ( "query".equals(name) ) {
+ bindNamedQuery(subnode, persistentClass.getEntityName(), mappings);
+ }
+ else if ( "sql-query".equals(name) ) {
+ bindNamedSQLQuery(subnode, persistentClass.getEntityName(), mappings);
+ }
+ else if ( "resultset".equals(name) ) {
+ bindResultSetMappingDefinition( subnode, persistentClass.getEntityName(), mappings );
+ }
+
+ if ( value != null ) {
+ Property property = createProperty( value, propertyName, persistentClass
+ .getClassName(), subnode, mappings, inheritedMetas );
+ if ( !mutable ) property.setUpdateable(false);
+ if ( naturalId ) property.setNaturalIdentifier(true);
+ persistentClass.addProperty( property );
+ if ( uniqueKey!=null ) uniqueKey.addColumns( property.getColumnIterator() );
+ }
+
+ }
+ }
+
+ private static Property createProperty(
+ final Value value,
+ final String propertyName,
+ final String className,
+ final Element subnode,
+ final Mappings mappings,
+ java.util.Map inheritedMetas) throws MappingException {
+
+ if ( StringHelper.isEmpty( propertyName ) ) {
+ throw new MappingException( subnode.getName() + " mapping must defined a name attribute [" + className + "]" );
+ }
+
+ value.setTypeUsingReflection( className, propertyName );
+
+ // this is done here 'cos we might only know the type here (ugly!)
+ // TODO: improve this a lot:
+ if ( value instanceof ToOne ) {
+ ToOne toOne = (ToOne) value;
+ String propertyRef = toOne.getReferencedPropertyName();
+ if ( propertyRef != null ) {
+ mappings.addUniquePropertyReference( toOne.getReferencedEntityName(), propertyRef );
+ }
+ }
+ else if ( value instanceof Collection ) {
+ Collection coll = (Collection) value;
+ String propertyRef = coll.getReferencedPropertyName();
+ // not necessarily a *unique* property reference
+ if ( propertyRef != null ) {
+ mappings.addPropertyReference( coll.getOwnerEntityName(), propertyRef );
+ }
+ }
+
+ value.createForeignKey();
+ Property prop = new Property();
+ prop.setValue( value );
+ bindProperty( subnode, prop, mappings, inheritedMetas );
+ return prop;
+ }
+
+ private static void handleUnionSubclass(PersistentClass model, Mappings mappings,
+ Element subnode, java.util.Map inheritedMetas) throws MappingException {
+ UnionSubclass subclass = new UnionSubclass( model );
+ bindUnionSubclass( subnode, subclass, mappings, inheritedMetas );
+ model.addSubclass( subclass );
+ mappings.addClass( subclass );
+ }
+
+ private static void handleJoinedSubclass(PersistentClass model, Mappings mappings,
+ Element subnode, java.util.Map inheritedMetas) throws MappingException {
+ JoinedSubclass subclass = new JoinedSubclass( model );
+ bindJoinedSubclass( subnode, subclass, mappings, inheritedMetas );
+ model.addSubclass( subclass );
+ mappings.addClass( subclass );
+ }
+
+ private static void handleSubclass(PersistentClass model, Mappings mappings, Element subnode,
+ java.util.Map inheritedMetas) throws MappingException {
+ Subclass subclass = new SingleTableSubclass( model );
+ bindSubclass( subnode, subclass, mappings, inheritedMetas );
+ model.addSubclass( subclass );
+ mappings.addClass( subclass );
+ }
+
+ /**
+ * Called for Lists, arrays, primitive arrays
+ */
+ public static void bindListSecondPass(Element node, List list, java.util.Map classes,
+ Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
+
+ bindCollectionSecondPass( node, list, classes, mappings, inheritedMetas );
+
+ Element subnode = node.element( "list-index" );
+ if ( subnode == null ) subnode = node.element( "index" );
+ SimpleValue iv = new SimpleValue( list.getCollectionTable() );
+ bindSimpleValue(
+ subnode,
+ iv,
+ list.isOneToMany(),
+ IndexedCollection.DEFAULT_INDEX_COLUMN_NAME,
+ mappings
+ );
+ iv.setTypeName( "integer" );
+ list.setIndex( iv );
+ String baseIndex = subnode.attributeValue( "base" );
+ if ( baseIndex != null ) list.setBaseIndex( Integer.parseInt( baseIndex ) );
+ list.setIndexNodeName( subnode.attributeValue("node") );
+
+ if ( list.isOneToMany() && !list.getKey().isNullable() && !list.isInverse() ) {
+ String entityName = ( (OneToMany) list.getElement() ).getReferencedEntityName();
+ PersistentClass referenced = mappings.getClass( entityName );
+ IndexBackref ib = new IndexBackref();
+ ib.setName( '_' + node.attributeValue( "name" ) + "IndexBackref" );
+ ib.setUpdateable( false );
+ ib.setSelectable( false );
+ ib.setCollectionRole( list.getRole() );
+ ib.setEntityName( list.getOwner().getEntityName() );
+ ib.setValue( list.getIndex() );
+ // ( (Column) ( (SimpleValue) ic.getIndex() ).getColumnIterator().next()
+ // ).setNullable(false);
+ referenced.addProperty( ib );
+ }
+ }
+
+ public static void bindIdentifierCollectionSecondPass(Element node,
+ IdentifierCollection collection, java.util.Map persistentClasses, Mappings mappings,
+ java.util.Map inheritedMetas) throws MappingException {
+
+ bindCollectionSecondPass( node, collection, persistentClasses, mappings, inheritedMetas );
+
+ Element subnode = node.element( "collection-id" );
+ SimpleValue id = new SimpleValue( collection.getCollectionTable() );
+ bindSimpleValue(
+ subnode,
+ id,
+ false,
+ IdentifierCollection.DEFAULT_IDENTIFIER_COLUMN_NAME,
+ mappings
+ );
+ collection.setIdentifier( id );
+ makeIdentifier( subnode, id, mappings );
+
+ }
+
+ /**
+ * Called for Maps
+ */
+ public static void bindMapSecondPass(Element node, Map map, java.util.Map classes,
+ Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
+
+ bindCollectionSecondPass( node, map, classes, mappings, inheritedMetas );
+
+ Iterator iter = node.elementIterator();
+ while ( iter.hasNext() ) {
+ Element subnode = (Element) iter.next();
+ String name = subnode.getName();
+
+ if ( "index".equals( name ) || "map-key".equals( name ) ) {
+ SimpleValue value = new SimpleValue( map.getCollectionTable() );
+ bindSimpleValue(
+ subnode,
+ value,
+ map.isOneToMany(),
+ IndexedCollection.DEFAULT_INDEX_COLUMN_NAME,
+ mappings
+ );
+ if ( !value.isTypeSpecified() ) {
+ throw new MappingException( "map index element must specify a type: "
+ + map.getRole() );
+ }
+ map.setIndex( value );
+ map.setIndexNodeName( subnode.attributeValue("node") );
+ }
+ else if ( "index-many-to-many".equals( name ) || "map-key-many-to-many".equals( name ) ) {
+ ManyToOne mto = new ManyToOne( map.getCollectionTable() );
+ bindManyToOne(
+ subnode,
+ mto,
+ IndexedCollection.DEFAULT_INDEX_COLUMN_NAME,
+ map.isOneToMany(),
+ mappings
+ );
+ map.setIndex( mto );
+
+ }
+ else if ( "composite-index".equals( name ) || "composite-map-key".equals( name ) ) {
+ Component component = new Component( map );
+ bindComposite(
+ subnode,
+ component,
+ map.getRole() + ".index",
+ map.isOneToMany(),
+ mappings,
+ inheritedMetas
+ );
+ map.setIndex( component );
+ }
+ else if ( "index-many-to-any".equals( name ) ) {
+ Any any = new Any( map.getCollectionTable() );
+ bindAny( subnode, any, map.isOneToMany(), mappings );
+ map.setIndex( any );
+ }
+ }
+
+ // TODO: this is a bit of copy/paste from IndexedCollection.createPrimaryKey()
+ boolean indexIsFormula = false;
+ Iterator colIter = map.getIndex().getColumnIterator();
+ while ( colIter.hasNext() ) {
+ if ( ( (Selectable) colIter.next() ).isFormula() ) indexIsFormula = true;
+ }
+
+ if ( map.isOneToMany() && !map.getKey().isNullable() && !map.isInverse() && !indexIsFormula ) {
+ String entityName = ( (OneToMany) map.getElement() ).getReferencedEntityName();
+ PersistentClass referenced = mappings.getClass( entityName );
+ IndexBackref ib = new IndexBackref();
+ ib.setName( '_' + node.attributeValue( "name" ) + "IndexBackref" );
+ ib.setUpdateable( false );
+ ib.setSelectable( false );
+ ib.setCollectionRole( map.getRole() );
+ ib.setEntityName( map.getOwner().getEntityName() );
+ ib.setValue( map.getIndex() );
+ // ( (Column) ( (SimpleValue) ic.getIndex() ).getColumnIterator().next()
+ // ).setNullable(false);
+ referenced.addProperty( ib );
+ }
+ }
+
+ /**
+ * Called for all collections
+ */
+ public static void bindCollectionSecondPass(Element node, Collection collection,
+ java.util.Map persistentClasses, Mappings mappings, java.util.Map inheritedMetas)
+ throws MappingException {
+
+ if ( collection.isOneToMany() ) {
+ OneToMany oneToMany = (OneToMany) collection.getElement();
+ String assocClass = oneToMany.getReferencedEntityName();
+ PersistentClass persistentClass = (PersistentClass) persistentClasses.get( assocClass );
+ if ( persistentClass == null ) {
+ throw new MappingException( "Association references unmapped class: " + assocClass );
+ }
+ oneToMany.setAssociatedClass( persistentClass );
+ collection.setCollectionTable( persistentClass.getTable() );
+
+ log.info(
+ "Mapping collection: " + collection.getRole() +
+ " -> " + collection.getCollectionTable().getName()
+ );
+ }
+
+ // CHECK
+ Attribute chNode = node.attribute( "check" );
+ if ( chNode != null ) {
+ collection.getCollectionTable().addCheckConstraint( chNode.getValue() );
+ }
+
+ // contained elements:
+ Iterator iter = node.elementIterator();
+ while ( iter.hasNext() ) {
+ Element subnode = (Element) iter.next();
+ String name = subnode.getName();
+
+ if ( "key".equals( name ) ) {
+ KeyValue keyVal;
+ String propRef = collection.getReferencedPropertyName();
+ if ( propRef == null ) {
+ keyVal = collection.getOwner().getIdentifier();
+ }
+ else {
+ keyVal = (KeyValue) collection.getOwner().getRecursiveProperty( propRef ).getValue();
+ }
+ SimpleValue key = new DependantValue( collection.getCollectionTable(), keyVal );
+ key.setCascadeDeleteEnabled( "cascade"
+ .equals( subnode.attributeValue( "on-delete" ) ) );
+ bindSimpleValue(
+ subnode,
+ key,
+ collection.isOneToMany(),
+ Collection.DEFAULT_KEY_COLUMN_NAME,
+ mappings
+ );
+ collection.setKey( key );
+
+ Attribute notNull = subnode.attribute( "not-null" );
+ ( (DependantValue) key ).setNullable( notNull == null
+ || notNull.getValue().equals( "false" ) );
+ Attribute updateable = subnode.attribute( "update" );
+ ( (DependantValue) key ).setUpdateable( updateable == null
+ || updateable.getValue().equals( "true" ) );
+
+ }
+ else if ( "element".equals( name ) ) {
+ SimpleValue elt = new SimpleValue( collection.getCollectionTable() );
+ collection.setElement( elt );
+ bindSimpleValue(
+ subnode,
+ elt,
+ true,
+ Collection.DEFAULT_ELEMENT_COLUMN_NAME,
+ mappings
+ );
+ }
+ else if ( "many-to-many".equals( name ) ) {
+ ManyToOne element = new ManyToOne( collection.getCollectionTable() );
+ collection.setElement( element );
+ bindManyToOne(
+ subnode,
+ element,
+ Collection.DEFAULT_ELEMENT_COLUMN_NAME,
+ false,
+ mappings
+ );
+ bindManyToManySubelements( collection, subnode, mappings );
+ }
+ else if ( "composite-element".equals( name ) ) {
+ Component element = new Component( collection );
+ collection.setElement( element );
+ bindComposite(
+ subnode,
+ element,
+ collection.getRole() + ".element",
+ true,
+ mappings,
+ inheritedMetas
+ );
+ }
+ else if ( "many-to-any".equals( name ) ) {
+ Any element = new Any( collection.getCollectionTable() );
+ collection.setElement( element );
+ bindAny( subnode, element, true, mappings );
+ }
+ else if ( "cache".equals( name ) ) {
+ collection.setCacheConcurrencyStrategy( subnode.attributeValue( "usage" ) );
+ collection.setCacheRegionName( subnode.attributeValue( "region" ) );
+ }
+
+ String nodeName = subnode.attributeValue( "node" );
+ if ( nodeName != null ) collection.setElementNodeName( nodeName );
+
+ }
+
+ if ( collection.isOneToMany()
+ && !collection.isInverse()
+ && !collection.getKey().isNullable() ) {
+ // for non-inverse one-to-many, with a not-null fk, add a backref!
+ String entityName = ( (OneToMany) collection.getElement() ).getReferencedEntityName();
+ PersistentClass referenced = mappings.getClass( entityName );
+ Backref prop = new Backref();
+ prop.setName( '_' + node.attributeValue( "name" ) + "Backref" );
+ prop.setUpdateable( false );
+ prop.setSelectable( false );
+ prop.setCollectionRole( collection.getRole() );
+ prop.setEntityName( collection.getOwner().getEntityName() );
+ prop.setValue( collection.getKey() );
+ referenced.addProperty( prop );
+ }
+ }
+
+ private static void bindManyToManySubelements(
+ Collection collection,
+ Element manyToManyNode,
+ Mappings model) throws MappingException {
+ // Bind the where
+ Attribute where = manyToManyNode.attribute( "where" );
+ String whereCondition = where == null ? null : where.getValue();
+ collection.setManyToManyWhere( whereCondition );
+
+ // Bind the order-by
+ Attribute order = manyToManyNode.attribute( "order-by" );
+ String orderFragment = order == null ? null : order.getValue();
+ collection.setManyToManyOrdering( orderFragment );
+
+ // Bind the filters
+ Iterator filters = manyToManyNode.elementIterator( "filter" );
+ if ( ( filters.hasNext() || whereCondition != null ) &&
+ collection.getFetchMode() == FetchMode.JOIN &&
+ collection.getElement().getFetchMode() != FetchMode.JOIN ) {
+ throw new MappingException(
+ "many-to-many defining filter or where without join fetching " +
+ "not valid within collection using join fetching [" + collection.getRole() + "]"
+ );
+ }
+ while ( filters.hasNext() ) {
+ final Element filterElement = ( Element ) filters.next();
+ final String name = filterElement.attributeValue( "name" );
+ String condition = filterElement.getTextTrim();
+ if ( StringHelper.isEmpty(condition) ) condition = filterElement.attributeValue( "condition" );
+ if ( StringHelper.isEmpty(condition) ) {
+ condition = model.getFilterDefinition(name).getDefaultFilterCondition();
+ }
+ if ( condition==null) {
+ throw new MappingException("no filter condition found for filter: " + name);
+ }
+ log.debug(
+ "Applying many-to-many filter [" + name +
+ "] as [" + condition +
+ "] to role [" + collection.getRole() + "]"
+ );
+ collection.addManyToManyFilter( name, condition );
+ }
+ }
+
+ public static final FlushMode getFlushMode(String flushMode) {
+ if ( flushMode == null ) {
+ return null;
+ }
+ else if ( "auto".equals( flushMode ) ) {
+ return FlushMode.AUTO;
+ }
+ else if ( "commit".equals( flushMode ) ) {
+ return FlushMode.COMMIT;
+ }
+ else if ( "never".equals( flushMode ) ) {
+ return FlushMode.NEVER;
+ }
+ else if ( "manual".equals( flushMode ) ) {
+ return FlushMode.MANUAL;
+ }
+ else if ( "always".equals( flushMode ) ) {
+ return FlushMode.ALWAYS;
+ }
+ else {
+ throw new MappingException( "unknown flushmode" );
+ }
+ }
+
+ private static void bindNamedQuery(Element queryElem, String path, Mappings mappings) {
+ String queryName = queryElem.attributeValue( "name" );
+ if (path!=null) queryName = path + '.' + queryName;
+ String query = queryElem.getText();
+ log.debug( "Named query: " + queryName + " -> " + query );
+
+ boolean cacheable = "true".equals( queryElem.attributeValue( "cacheable" ) );
+ String region = queryElem.attributeValue( "cache-region" );
+ Attribute tAtt = queryElem.attribute( "timeout" );
+ Integer timeout = tAtt == null ? null : new Integer( tAtt.getValue() );
+ Attribute fsAtt = queryElem.attribute( "fetch-size" );
+ Integer fetchSize = fsAtt == null ? null : new Integer( fsAtt.getValue() );
+ Attribute roAttr = queryElem.attribute( "read-only" );
+ boolean readOnly = roAttr != null && "true".equals( roAttr.getValue() );
+ Attribute cacheModeAtt = queryElem.attribute( "cache-mode" );
+ String cacheMode = cacheModeAtt == null ? null : cacheModeAtt.getValue();
+ Attribute cmAtt = queryElem.attribute( "comment" );
+ String comment = cmAtt == null ? null : cmAtt.getValue();
+
+ NamedQueryDefinition namedQuery = new NamedQueryDefinition(
+ query,
+ cacheable,
+ region,
+ timeout,
+ fetchSize,
+ getFlushMode( queryElem.attributeValue( "flush-mode" ) ) ,
+ getCacheMode( cacheMode ),
+ readOnly,
+ comment,
+ getParameterTypes(queryElem)
+ );
+
+ mappings.addQuery( queryName, namedQuery );
+ }
+
+ public static CacheMode getCacheMode(String cacheMode) {
+ if (cacheMode == null) return null;
+ if ( "get".equals( cacheMode ) ) return CacheMode.GET;
+ if ( "ignore".equals( cacheMode ) ) return CacheMode.IGNORE;
+ if ( "normal".equals( cacheMode ) ) return CacheMode.NORMAL;
+ if ( "put".equals( cacheMode ) ) return CacheMode.PUT;
+ if ( "refresh".equals( cacheMode ) ) return CacheMode.REFRESH;
+ throw new MappingException("Unknown Cache Mode: " + cacheMode);
+ }
+
+ public static java.util.Map getParameterTypes(Element queryElem) {
+ java.util.Map result = new SequencedHashMap();
+ Iterator iter = queryElem.elementIterator("query-param");
+ while ( iter.hasNext() ) {
+ Element element = (Element) iter.next();
+ result.put(
+ element.attributeValue("name"),
+ element.attributeValue("type")
+ );
+ }
+ return result;
+ }
+
+ private static void bindResultSetMappingDefinition(Element resultSetElem, String path, Mappings mappings) {
+ mappings.addSecondPass( new ResultSetMappingSecondPass( resultSetElem, path, mappings ) );
+ }
+
+ private static void bindNamedSQLQuery(Element queryElem, String path, Mappings mappings) {
+ mappings.addSecondPass( new NamedSQLQuerySecondPass( queryElem, path, mappings ) );
+ }
+
+ private static String getPropertyName(Element node) {
+ return node.attributeValue( "name" );
+ }
+
+ private static PersistentClass getSuperclass(Mappings mappings, Element subnode)
+ throws MappingException {
+ String extendsName = subnode.attributeValue( "extends" );
+ PersistentClass superModel = mappings.getClass( extendsName );
+ if ( superModel == null ) {
+ String qualifiedExtendsName = getClassName( extendsName, mappings );
+ superModel = mappings.getClass( qualifiedExtendsName );
+ }
+
+ if ( superModel == null ) {
+ throw new MappingException( "Cannot extend unmapped class " + extendsName );
+ }
+ return superModel;
+ }
+
+ static class CollectionSecondPass extends org.hibernate.cfg.CollectionSecondPass {
+ Element node;
+
+ CollectionSecondPass(Element node, Mappings mappings, Collection collection, java.util.Map inheritedMetas) {
+ super(mappings, collection, inheritedMetas);
+ this.node = node;
+ }
+
+ public void secondPass(java.util.Map persistentClasses, java.util.Map inheritedMetas)
+ throws MappingException {
+ HbmBinder.bindCollectionSecondPass(
+ node,
+ collection,
+ persistentClasses,
+ mappings,
+ inheritedMetas
+ );
+ }
+ }
+
+ static class IdentifierCollectionSecondPass extends CollectionSecondPass {
+ IdentifierCollectionSecondPass(Element node, Mappings mappings, Collection collection, java.util.Map inheritedMetas) {
+ super( node, mappings, collection, inheritedMetas );
+ }
+
+ public void secondPass(java.util.Map persistentClasses, java.util.Map inheritedMetas)
+ throws MappingException {
+ HbmBinder.bindIdentifierCollectionSecondPass(
+ node,
+ (IdentifierCollection) collection,
+ persistentClasses,
+ mappings,
+ inheritedMetas
+ );
+ }
+
+ }
+
+ static class MapSecondPass extends CollectionSecondPass {
+ MapSecondPass(Element node, Mappings mappings, Map collection, java.util.Map inheritedMetas) {
+ super( node, mappings, collection, inheritedMetas );
+ }
+
+ public void secondPass(java.util.Map persistentClasses, java.util.Map inheritedMetas)
+ throws MappingException {
+ HbmBinder.bindMapSecondPass(
+ node,
+ (Map) collection,
+ persistentClasses,
+ mappings,
+ inheritedMetas
+ );
+ }
+
+ }
+
+
+ static class ManyToOneSecondPass implements SecondPass {
+ private final ManyToOne manyToOne;
+
+ ManyToOneSecondPass(ManyToOne manyToOne) {
+ this.manyToOne = manyToOne;
+ }
+
+ public void doSecondPass(java.util.Map persistentClasses) throws MappingException {
+ manyToOne.createPropertyRefConstraints(persistentClasses);
+ }
+
+ }
+
+ static class ListSecondPass extends CollectionSecondPass {
+ ListSecondPass(Element node, Mappings mappings, List collection, java.util.Map inheritedMetas) {
+ super( node, mappings, collection, inheritedMetas );
+ }
+
+ public void secondPass(java.util.Map persistentClasses, java.util.Map inheritedMetas)
+ throws MappingException {
+ HbmBinder.bindListSecondPass(
+ node,
+ (List) collection,
+ persistentClasses,
+ mappings,
+ inheritedMetas
+ );
+ }
+
+ }
+
+ // This inner class implements a case statement....perhaps im being a bit over-clever here
+ abstract static class CollectionType {
+ private String xmlTag;
+
+ public abstract Collection create(Element node, String path, PersistentClass owner,
+ Mappings mappings, java.util.Map inheritedMetas) throws MappingException;
+
+ CollectionType(String xmlTag) {
+ this.xmlTag = xmlTag;
+ }
+
+ public String toString() {
+ return xmlTag;
+ }
+
+ private static final CollectionType MAP = new CollectionType( "map" ) {
+ public Collection create(Element node, String path, PersistentClass owner,
+ Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
+ Map map = new Map( owner );
+ bindCollection( node, map, owner.getEntityName(), path, mappings, inheritedMetas );
+ return map;
+ }
+ };
+ private static final CollectionType SET = new CollectionType( "set" ) {
+ public Collection create(Element node, String path, PersistentClass owner,
+ Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
+ Set set = new Set( owner );
+ bindCollection( node, set, owner.getEntityName(), path, mappings, inheritedMetas );
+ return set;
+ }
+ };
+ private static final CollectionType LIST = new CollectionType( "list" ) {
+ public Collection create(Element node, String path, PersistentClass owner,
+ Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
+ List list = new List( owner );
+ bindCollection( node, list, owner.getEntityName(), path, mappings, inheritedMetas );
+ return list;
+ }
+ };
+ private static final CollectionType BAG = new CollectionType( "bag" ) {
+ public Collection create(Element node, String path, PersistentClass owner,
+ Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
+ Bag bag = new Bag( owner );
+ bindCollection( node, bag, owner.getEntityName(), path, mappings, inheritedMetas );
+ return bag;
+ }
+ };
+ private static final CollectionType IDBAG = new CollectionType( "idbag" ) {
+ public Collection create(Element node, String path, PersistentClass owner,
+ Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
+ IdentifierBag bag = new IdentifierBag( owner );
+ bindCollection( node, bag, owner.getEntityName(), path, mappings, inheritedMetas );
+ return bag;
+ }
+ };
+ private static final CollectionType ARRAY = new CollectionType( "array" ) {
+ public Collection create(Element node, String path, PersistentClass owner,
+ Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
+ Array array = new Array( owner );
+ bindArray( node, array, owner.getEntityName(), path, mappings, inheritedMetas );
+ return array;
+ }
+ };
+ private static final CollectionType PRIMITIVE_ARRAY = new CollectionType( "primitive-array" ) {
+ public Collection create(Element node, String path, PersistentClass owner,
+ Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
+ PrimitiveArray array = new PrimitiveArray( owner );
+ bindArray( node, array, owner.getEntityName(), path, mappings, inheritedMetas );
+ return array;
+ }
+ };
+ private static final HashMap INSTANCES = new HashMap();
+
+ static {
+ INSTANCES.put( MAP.toString(), MAP );
+ INSTANCES.put( BAG.toString(), BAG );
+ INSTANCES.put( IDBAG.toString(), IDBAG );
+ INSTANCES.put( SET.toString(), SET );
+ INSTANCES.put( LIST.toString(), LIST );
+ INSTANCES.put( ARRAY.toString(), ARRAY );
+ INSTANCES.put( PRIMITIVE_ARRAY.toString(), PRIMITIVE_ARRAY );
+ }
+
+ public static CollectionType collectionTypeFromString(String xmlTagName) {
+ return (CollectionType) INSTANCES.get( xmlTagName );
+ }
+ }
+
+ private static int getOptimisticLockMode(Attribute olAtt) throws MappingException {
+
+ if ( olAtt == null ) return Versioning.OPTIMISTIC_LOCK_VERSION;
+ String olMode = olAtt.getValue();
+ if ( olMode == null || "version".equals( olMode ) ) {
+ return Versioning.OPTIMISTIC_LOCK_VERSION;
+ }
+ else if ( "dirty".equals( olMode ) ) {
+ return Versioning.OPTIMISTIC_LOCK_DIRTY;
+ }
+ else if ( "all".equals( olMode ) ) {
+ return Versioning.OPTIMISTIC_LOCK_ALL;
+ }
+ else if ( "none".equals( olMode ) ) {
+ return Versioning.OPTIMISTIC_LOCK_NONE;
+ }
+ else {
+ throw new MappingException( "Unsupported optimistic-lock style: " + olMode );
+ }
+ }
+
+ private static final java.util.Map getMetas(Element node, java.util.Map inheritedMeta) {
+ return getMetas( node, inheritedMeta, false );
+ }
+
+ public static final java.util.Map getMetas(Element node, java.util.Map inheritedMeta,
+ boolean onlyInheritable) {
+ java.util.Map map = new HashMap();
+ map.putAll( inheritedMeta );
+
+ Iterator iter = node.elementIterator( "meta" );
+ while ( iter.hasNext() ) {
+ Element metaNode = (Element) iter.next();
+ boolean inheritable = Boolean
+ .valueOf( metaNode.attributeValue( "inherit" ) )
+ .booleanValue();
+ if ( onlyInheritable & !inheritable ) {
+ continue;
+ }
+ String name = metaNode.attributeValue( "attribute" );
+
+ MetaAttribute meta = (MetaAttribute) map.get( name );
+ MetaAttribute inheritedAttribute = (MetaAttribute) inheritedMeta.get( name );
+ if ( meta == null ) {
+ meta = new MetaAttribute( name );
+ map.put( name, meta );
+ } else if (meta == inheritedAttribute) { // overriding inherited meta attribute. HBX-621 & HBX-793
+ meta = new MetaAttribute( name );
+ map.put( name, meta );
+ }
+ meta.addValue( metaNode.getText() );
+ }
+ return map;
+ }
+
+ public static String getEntityName(Element elem, Mappings model) {
+ String entityName = elem.attributeValue( "entity-name" );
+ return entityName == null ? getClassName( elem.attribute( "class" ), model ) : entityName;
+ }
+
+ private static String getClassName(Attribute att, Mappings model) {
+ if ( att == null ) return null;
+ return getClassName( att.getValue(), model );
+ }
+
+ public static String getClassName(String unqualifiedName, Mappings model) {
+ return getClassName( unqualifiedName, model.getDefaultPackage() );
+ }
+
+ public static String getClassName(String unqualifiedName, String defaultPackage) {
+ if ( unqualifiedName == null ) return null;
+ if ( unqualifiedName.indexOf( '.' ) < 0 && defaultPackage != null ) {
+ return defaultPackage + '.' + unqualifiedName;
+ }
+ return unqualifiedName;
+ }
+
+ private static void parseFilterDef(Element element, Mappings mappings) {
+ String name = element.attributeValue( "name" );
+ log.debug( "Parsing filter-def [" + name + "]" );
+ String defaultCondition = element.getTextTrim();
+ if ( StringHelper.isEmpty( defaultCondition ) ) {
+ defaultCondition = element.attributeValue( "condition" );
+ }
+ HashMap paramMappings = new HashMap();
+ Iterator params = element.elementIterator( "filter-param" );
+ while ( params.hasNext() ) {
+ final Element param = (Element) params.next();
+ final String paramName = param.attributeValue( "name" );
+ final String paramType = param.attributeValue( "type" );
+ log.debug( "adding filter parameter : " + paramName + " -> " + paramType );
+ final Type heuristicType = TypeFactory.heuristicType( paramType );
+ log.debug( "parameter heuristic type : " + heuristicType );
+ paramMappings.put( paramName, heuristicType );
+ }
+ log.debug( "Parsed filter-def [" + name + "]" );
+ FilterDefinition def = new FilterDefinition( name, defaultCondition, paramMappings );
+ mappings.addFilterDefinition( def );
+ }
+
+ private static void parseFilter(Element filterElement, Filterable filterable, Mappings model) {
+ final String name = filterElement.attributeValue( "name" );
+ String condition = filterElement.getTextTrim();
+ if ( StringHelper.isEmpty(condition) ) {
+ condition = filterElement.attributeValue( "condition" );
+ }
+ //TODO: bad implementation, cos it depends upon ordering of mapping doc
+ // fixing this requires that Collection/PersistentClass gain access
+ // to the Mappings reference from Configuration (or the filterDefinitions
+ // map directly) sometime during Configuration.buildSessionFactory
+ // (after all the types/filter-defs are known and before building
+ // persisters).
+ if ( StringHelper.isEmpty(condition) ) {
+ condition = model.getFilterDefinition(name).getDefaultFilterCondition();
+ }
+ if ( condition==null) {
+ throw new MappingException("no filter condition found for filter: " + name);
+ }
+ log.debug( "Applying filter [" + name + "] as [" + condition + "]" );
+ filterable.addFilter( name, condition );
+ }
+
+ private static String getSubselect(Element element) {
+ String subselect = element.attributeValue( "subselect" );
+ if ( subselect != null ) {
+ return subselect;
+ }
+ else {
+ Element subselectElement = element.element( "subselect" );
+ return subselectElement == null ? null : subselectElement.getText();
+ }
+ }
+
+ /**
+ * For the given document, locate all extends attributes which refer to
+ * entities (entity-name or class-name) not defined within said document.
+ *
+ * @param doc The document to check
+ * @param mappings The already processed mappings.
+ * @return The list of unresolved extends names.
+ */
+ public static java.util.List getExtendsNeeded(Document doc, Mappings mappings) {
+ java.util.List extendz = new ArrayList();
+ Iterator[] subclasses = new Iterator[3];
+ final Element hmNode = doc.getRootElement();
+
+ Attribute packNode = hmNode.attribute( "package" );
+ final String packageName = packNode == null ? null : packNode.getValue();
+ if ( packageName != null ) {
+ mappings.setDefaultPackage( packageName );
+ }
+
+ // first, iterate over all elements capable of defining an extends attribute
+ // collecting all found extends references if they cannot be resolved
+ // against the already processed mappings.
+ subclasses[0] = hmNode.elementIterator( "subclass" );
+ subclasses[1] = hmNode.elementIterator( "joined-subclass" );
+ subclasses[2] = hmNode.elementIterator( "union-subclass" );
+
+ Iterator iterator = new JoinedIterator( subclasses );
+ while ( iterator.hasNext() ) {
+ final Element element = (Element) iterator.next();
+ final String extendsName = element.attributeValue( "extends" );
+ // mappings might contain either the "raw" extends name (in the case of
+ // an entity-name mapping) or a FQN (in the case of a POJO mapping).
+ if ( mappings.getClass( extendsName ) == null && mappings.getClass( getClassName( extendsName, mappings ) ) == null ) {
+ extendz.add( extendsName );
+ }
+ }
+
+ if ( !extendz.isEmpty() ) {
+ // we found some extends attributes referencing entities which were
+ // not already processed. here we need to locate all entity-names
+ // and class-names contained in this document itself, making sure
+ // that these get removed from the extendz list such that only
+ // extends names which require us to delay processing (i.e.
+ // external to this document and not yet processed) are contained
+ // in the returned result
+ final java.util.Set set = new HashSet( extendz );
+ EntityElementHandler handler = new EntityElementHandler() {
+ public void handleEntity(String entityName, String className, Mappings mappings) {
+ if ( entityName != null ) {
+ set.remove( entityName );
+ }
+ else {
+ String fqn = getClassName( className, packageName );
+ set.remove( fqn );
+ if ( packageName != null ) {
+ set.remove( StringHelper.unqualify( fqn ) );
+ }
+ }
+ }
+ };
+ recognizeEntities( mappings, hmNode, handler );
+ extendz.clear();
+ extendz.addAll( set );
+ }
+
+ return extendz;
+ }
+
+ /**
+ * Given an entity-containing-element (startNode) recursively locate all
+ * entity names defined within that element.
+ *
+ * @param mappings The already processed mappings
+ * @param startNode The containing element
+ * @param handler The thing that knows what to do whenever we recognize an
+ * entity-name
+ */
+ private static void recognizeEntities(
+ Mappings mappings,
+ final Element startNode,
+ EntityElementHandler handler) {
+ Iterator[] classes = new Iterator[4];
+ classes[0] = startNode.elementIterator( "class" );
+ classes[1] = startNode.elementIterator( "subclass" );
+ classes[2] = startNode.elementIterator( "joined-subclass" );
+ classes[3] = startNode.elementIterator( "union-subclass" );
+
+ Iterator classIterator = new JoinedIterator( classes );
+ while ( classIterator.hasNext() ) {
+ Element element = (Element) classIterator.next();
+ handler.handleEntity(
+ element.attributeValue( "entity-name" ),
+ element.attributeValue( "name" ),
+ mappings
+ );
+ recognizeEntities( mappings, element, handler );
+ }
+ }
+
+ private static interface EntityElementHandler {
+ public void handleEntity(String entityName, String className, Mappings mappings);
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/ImprovedNamingStrategy.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/ImprovedNamingStrategy.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/ImprovedNamingStrategy.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,124 @@
+//$Id: ImprovedNamingStrategy.java 9941 2006-05-22 18:28:53Z epbernard $
+package org.hibernate.cfg;
+
+import java.io.Serializable;
+
+import org.hibernate.util.StringHelper;
+import org.hibernate.AssertionFailure;
+
+/**
+ * An improved naming strategy that prefers embedded
+ * underscores to mixed case names
+ * @see DefaultNamingStrategy the default strategy
+ * @author Gavin King
+ */
+public class ImprovedNamingStrategy implements NamingStrategy, Serializable {
+
+ /**
+ * A convenient singleton instance
+ */
+ public static final NamingStrategy INSTANCE = new ImprovedNamingStrategy();
+
+ /**
+ * Return the unqualified class name, mixed case converted to
+ * underscores
+ */
+ public String classToTableName(String className) {
+ return addUnderscores( StringHelper.unqualify(className) );
+ }
+ /**
+ * Return the full property path with underscore seperators, mixed
+ * case converted to underscores
+ */
+ public String propertyToColumnName(String propertyName) {
+ return addUnderscores( StringHelper.unqualify(propertyName) );
+ }
+ /**
+ * Convert mixed case to underscores
+ */
+ public String tableName(String tableName) {
+ return addUnderscores(tableName);
+ }
+ /**
+ * Convert mixed case to underscores
+ */
+ public String columnName(String columnName) {
+ return addUnderscores(columnName);
+ }
+
+ protected static String addUnderscores(String name) {
+ StringBuffer buf = new StringBuffer( name.replace('.', '_') );
+ for (int i=1; i<buf.length()-1; i++) {
+ if (
+ Character.isLowerCase( buf.charAt(i-1) ) &&
+ Character.isUpperCase( buf.charAt(i) ) &&
+ Character.isLowerCase( buf.charAt(i+1) )
+ ) {
+ buf.insert(i++, '_');
+ }
+ }
+ return buf.toString().toLowerCase();
+ }
+
+ public String collectionTableName(
+ String ownerEntity, String ownerEntityTable, String associatedEntity, String associatedEntityTable,
+ String propertyName
+ ) {
+ return tableName( ownerEntityTable + '_' + propertyToColumnName(propertyName) );
+ }
+
+ /**
+ * Return the argument
+ */
+ public String joinKeyColumnName(String joinedColumn, String joinedTable) {
+ return columnName( joinedColumn );
+ }
+
+ /**
+ * Return the property name or propertyTableName
+ */
+ public String foreignKeyColumnName(
+ String propertyName, String propertyEntityName, String propertyTableName, String referencedColumnName
+ ) {
+ String header = propertyName != null ? StringHelper.unqualify( propertyName ) : propertyTableName;
+ if (header == null) throw new AssertionFailure("NamingStrategy not properly filled");
+ return columnName( header ); //+ "_" + referencedColumnName not used for backward compatibility
+ }
+
+ /**
+ * Return the column name or the unqualified property name
+ */
+ public String logicalColumnName(String columnName, String propertyName) {
+ return StringHelper.isNotEmpty( columnName ) ? columnName : StringHelper.unqualify( propertyName );
+ }
+
+ /**
+ * Returns either the table name if explicit or
+ * if there is an associated table, the concatenation of owner entity table and associated table
+ * otherwise the concatenation of owner entity table and the unqualified property name
+ */
+ public String logicalCollectionTableName(String tableName,
+ String ownerEntityTable, String associatedEntityTable, String propertyName
+ ) {
+ if ( tableName != null ) {
+ return tableName;
+ }
+ else {
+ //use of a stringbuffer to workaround a JDK bug
+ return new StringBuffer(ownerEntityTable).append("_")
+ .append(
+ associatedEntityTable != null ?
+ associatedEntityTable :
+ StringHelper.unqualify( propertyName )
+ ).toString();
+ }
+ }
+ /**
+ * Return the column name if explicit or the concatenation of the property name and the referenced column
+ */
+ public String logicalCollectionColumnName(String columnName, String propertyName, String referencedColumn) {
+ return StringHelper.isNotEmpty( columnName ) ?
+ columnName :
+ StringHelper.unqualify( propertyName ) + "_" + referencedColumn;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/Mappings.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/Mappings.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/Mappings.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,542 @@
+//$Id: Mappings.java 11051 2007-01-16 23:24:17Z epbernard $
+package org.hibernate.cfg;
+
+import java.io.Serializable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.HashMap;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.DuplicateMappingException;
+import org.hibernate.MappingException;
+import org.hibernate.engine.FilterDefinition;
+import org.hibernate.engine.NamedQueryDefinition;
+import org.hibernate.engine.NamedSQLQueryDefinition;
+import org.hibernate.engine.ResultSetMappingDefinition;
+import org.hibernate.mapping.Collection;
+import org.hibernate.mapping.DenormalizedTable;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Table;
+import org.hibernate.mapping.TypeDef;
+import org.hibernate.mapping.AuxiliaryDatabaseObject;
+import org.hibernate.mapping.Column;
+import org.hibernate.util.StringHelper;
+
+/**
+ * A collection of mappings from classes and collections to
+ * relational database tables. (Represents a single
+ * <tt><hibernate-mapping></tt> element.)
+ * @author Gavin King
+ */
+public class Mappings implements Serializable {
+
+ private static final Log log = LogFactory.getLog(Mappings.class);
+
+ protected final Map classes;
+ protected final Map collections;
+ protected final Map tables;
+ protected final Map queries;
+ protected final Map sqlqueries;
+ protected final Map resultSetMappings;
+ protected final Map typeDefs;
+ protected final List secondPasses;
+ protected final Map imports;
+ protected String schemaName;
+ protected String catalogName;
+ protected String defaultCascade;
+ protected String defaultPackage;
+ protected String defaultAccess;
+ protected boolean autoImport;
+ protected boolean defaultLazy;
+ protected final List propertyReferences;
+ protected final NamingStrategy namingStrategy;
+ protected final Map filterDefinitions;
+ protected final List auxiliaryDatabaseObjects;
+
+ protected final Map extendsQueue;
+// private final List extendsQueue;
+
+ /**
+ * binding table between the logical column name and the name out of the naming strategy
+ * for each table.
+ * According that when the column name is not set, the property name is considered as such
+ * This means that while theorically possible through the naming strategy contract, it is
+ * forbidden to have 2 real columns having the same logical name
+ * <Table, ColumnNames >
+ */
+ protected final Map columnNameBindingPerTable;
+ /**
+ * binding between logical table name and physical one (ie after the naming strategy has been applied)
+ * <String, TableDescription>
+ */
+ protected final Map tableNameBinding;
+
+
+ Mappings(
+ final Map classes,
+ final Map collections,
+ final Map tables,
+ final Map queries,
+ final Map sqlqueries,
+ final Map sqlResultSetMappings,
+ final Map imports,
+ final List secondPasses,
+ final List propertyReferences,
+ final NamingStrategy namingStrategy,
+ final Map typeDefs,
+ final Map filterDefinitions,
+// final List extendsQueue,
+ final Map extendsQueue,
+ final List auxiliaryDatabaseObjects,
+ final Map tableNamebinding,
+ final Map columnNameBindingPerTable
+ ) {
+ this.classes = classes;
+ this.collections = collections;
+ this.queries = queries;
+ this.sqlqueries = sqlqueries;
+ this.resultSetMappings = sqlResultSetMappings;
+ this.tables = tables;
+ this.imports = imports;
+ this.secondPasses = secondPasses;
+ this.propertyReferences = propertyReferences;
+ this.namingStrategy = namingStrategy;
+ this.typeDefs = typeDefs;
+ this.filterDefinitions = filterDefinitions;
+ this.extendsQueue = extendsQueue;
+ this.auxiliaryDatabaseObjects = auxiliaryDatabaseObjects;
+ this.tableNameBinding = tableNamebinding;
+ this.columnNameBindingPerTable = columnNameBindingPerTable;
+ }
+
+ public void addClass(PersistentClass persistentClass) throws MappingException {
+ Object old = classes.put( persistentClass.getEntityName(), persistentClass );
+ if ( old!=null ) {
+ throw new DuplicateMappingException( "class/entity", persistentClass.getEntityName() );
+ }
+ }
+ public void addCollection(Collection collection) throws MappingException {
+ Object old = collections.put( collection.getRole(), collection );
+ if ( old!=null ) {
+ throw new DuplicateMappingException( "collection role", collection.getRole() );
+ }
+ }
+ public PersistentClass getClass(String className) {
+ return (PersistentClass) classes.get(className);
+ }
+ public Collection getCollection(String role) {
+ return (Collection) collections.get(role);
+ }
+
+ public void addImport(String className, String rename) throws MappingException {
+ String existing = (String) imports.put(rename, className);
+ if ( existing!=null ) {
+ if ( existing.equals(className) ) {
+ log.info( "duplicate import: " + className + "->" + rename );
+ }
+ else {
+ throw new DuplicateMappingException(
+ "duplicate import: " + rename +
+ " refers to both " + className +
+ " and " + existing +
+ " (try using auto-import=\"false\")",
+ "import",
+ rename
+ );
+ }
+ }
+ }
+
+ public Table addTable(String schema,
+ String catalog,
+ String name,
+ String subselect,
+ boolean isAbstract
+ ) {
+ String key = subselect==null ?
+ Table.qualify(catalog, schema, name) :
+ subselect;
+ Table table = (Table) tables.get(key);
+
+ if (table == null) {
+ table = new Table();
+ table.setAbstract(isAbstract);
+ table.setName(name);
+ table.setSchema(schema);
+ table.setCatalog(catalog);
+ table.setSubselect(subselect);
+ tables.put(key, table);
+ }
+ else {
+ if (!isAbstract) table.setAbstract(false);
+ }
+
+ return table;
+ }
+
+ public Table addDenormalizedTable(
+ String schema,
+ String catalog,
+ String name,
+ boolean isAbstract,
+ String subselect,
+ Table includedTable)
+ throws MappingException {
+ String key = subselect==null ?
+ Table.qualify(catalog, schema, name) :
+ subselect;
+ if ( tables.containsKey(key) ) {
+ throw new DuplicateMappingException("table", name);
+ }
+
+ Table table = new DenormalizedTable(includedTable);
+ table.setAbstract(isAbstract);
+ table.setName(name);
+ table.setSchema(schema);
+ table.setCatalog(catalog);
+ table.setSubselect(subselect);
+ tables.put(key, table);
+ return table;
+ }
+
+ public Table getTable(String schema, String catalog, String name) {
+ String key = Table.qualify(catalog, schema, name);
+ return (Table) tables.get(key);
+ }
+
+ public String getSchemaName() {
+ return schemaName;
+ }
+
+ public String getCatalogName() {
+ return catalogName;
+ }
+
+ public String getDefaultCascade() {
+ return defaultCascade;
+ }
+
+ /**
+ * Sets the schemaName.
+ * @param schemaName The schemaName to set
+ */
+ public void setSchemaName(String schemaName) {
+ this.schemaName = schemaName;
+ }
+
+ /**
+ * Sets the catalogName.
+ * @param catalogName The catalogName to set
+ */
+ public void setCatalogName(String catalogName) {
+ this.catalogName = catalogName;
+ }
+
+ /**
+ * Sets the defaultCascade.
+ * @param defaultCascade The defaultCascade to set
+ */
+ public void setDefaultCascade(String defaultCascade) {
+ this.defaultCascade = defaultCascade;
+ }
+
+ /**
+ * sets the default access strategy
+ * @param defaultAccess the default access strategy.
+ */
+ public void setDefaultAccess(String defaultAccess) {
+ this.defaultAccess = defaultAccess;
+ }
+
+ public String getDefaultAccess() {
+ return defaultAccess;
+ }
+
+ public void addQuery(String name, NamedQueryDefinition query) throws MappingException {
+ checkQueryExist(name);
+ queries.put( name.intern(), query );
+ }
+
+ public void addSQLQuery(String name, NamedSQLQueryDefinition query) throws MappingException {
+ checkQueryExist(name);
+ sqlqueries.put( name.intern(), query );
+ }
+
+ private void checkQueryExist(String name) throws MappingException {
+ if ( sqlqueries.containsKey(name) || queries.containsKey(name) ) {
+ throw new DuplicateMappingException("query", name);
+ }
+ }
+
+ public void addResultSetMapping(ResultSetMappingDefinition sqlResultSetMapping) {
+ final String name = sqlResultSetMapping.getName();
+ if ( resultSetMappings.containsKey(name) ) {
+ throw new DuplicateMappingException("resultSet", name);
+ }
+ resultSetMappings.put(name, sqlResultSetMapping);
+ }
+
+ public ResultSetMappingDefinition getResultSetMapping(String name) {
+ return (ResultSetMappingDefinition) resultSetMappings.get(name);
+ }
+
+
+ public NamedQueryDefinition getQuery(String name) {
+ return (NamedQueryDefinition) queries.get(name);
+ }
+
+ public void addSecondPass(SecondPass sp) {
+ addSecondPass(sp, false);
+ }
+
+ public void addSecondPass(SecondPass sp, boolean onTopOfTheQueue) {
+ if (onTopOfTheQueue) {
+ secondPasses.add(0, sp);
+ }
+ else {
+ secondPasses.add(sp);
+ }
+ }
+
+ /**
+ * Returns the autoImport.
+ * @return boolean
+ */
+ public boolean isAutoImport() {
+ return autoImport;
+ }
+
+ /**
+ * Sets the autoImport.
+ * @param autoImport The autoImport to set
+ */
+ public void setAutoImport(boolean autoImport) {
+ this.autoImport = autoImport;
+ }
+
+ void addUniquePropertyReference(String referencedClass, String propertyName) {
+ PropertyReference upr = new PropertyReference();
+ upr.referencedClass = referencedClass;
+ upr.propertyName = propertyName;
+ upr.unique = true;
+ propertyReferences.add(upr);
+ }
+
+ void addPropertyReference(String referencedClass, String propertyName) {
+ PropertyReference upr = new PropertyReference();
+ upr.referencedClass = referencedClass;
+ upr.propertyName = propertyName;
+ propertyReferences.add(upr);
+ }
+
+ private String buildTableNameKey(String schema, String catalog, String finalName) {
+ StringBuffer keyBuilder = new StringBuffer();
+ if (schema != null) keyBuilder.append( schema );
+ keyBuilder.append( ".");
+ if (catalog != null) keyBuilder.append( catalog );
+ keyBuilder.append( ".");
+ keyBuilder.append( finalName );
+ return keyBuilder.toString();
+ }
+
+ static final class PropertyReference implements Serializable {
+ String referencedClass;
+ String propertyName;
+ boolean unique;
+ }
+
+ /**
+ * @return Returns the defaultPackage.
+ */
+ public String getDefaultPackage() {
+ return defaultPackage;
+ }
+
+ /**
+ * @param defaultPackage The defaultPackage to set.
+ */
+ public void setDefaultPackage(String defaultPackage) {
+ this.defaultPackage = defaultPackage;
+ }
+
+ public NamingStrategy getNamingStrategy() {
+ return namingStrategy;
+ }
+
+ public void addTypeDef(String typeName, String typeClass, Properties paramMap) {
+ TypeDef def = new TypeDef(typeClass, paramMap);
+ typeDefs.put(typeName, def);
+ log.debug("Added " + typeName + " with class " + typeClass);
+ }
+
+ public TypeDef getTypeDef(String typeName) {
+ return (TypeDef) typeDefs.get(typeName);
+ }
+
+ public Iterator iterateCollections() {
+ return collections.values().iterator();
+ }
+
+ public Iterator iterateTables() {
+ return tables.values().iterator();
+ }
+
+ public Map getFilterDefinitions() {
+ return filterDefinitions;
+ }
+
+ public void addFilterDefinition(FilterDefinition definition) {
+ filterDefinitions.put( definition.getFilterName(), definition );
+ }
+
+ public FilterDefinition getFilterDefinition(String name) {
+ return (FilterDefinition) filterDefinitions.get(name);
+ }
+
+ public boolean isDefaultLazy() {
+ return defaultLazy;
+ }
+ public void setDefaultLazy(boolean defaultLazy) {
+ this.defaultLazy = defaultLazy;
+ }
+
+ public void addToExtendsQueue(ExtendsQueueEntry entry) {
+ extendsQueue.put( entry, null );
+ }
+
+ public PersistentClass locatePersistentClassByEntityName(String entityName) {
+ PersistentClass persistentClass = ( PersistentClass ) classes.get( entityName );
+ if ( persistentClass == null ) {
+ String actualEntityName = ( String ) imports.get( entityName );
+ if ( StringHelper.isNotEmpty( actualEntityName ) ) {
+ persistentClass = ( PersistentClass ) classes.get( actualEntityName );
+ }
+ }
+ return persistentClass;
+ }
+
+ public void addAuxiliaryDatabaseObject(AuxiliaryDatabaseObject auxiliaryDatabaseObject) {
+ auxiliaryDatabaseObjects.add( auxiliaryDatabaseObject );
+ }
+
+ public void addTableBinding(
+ String schema, String catalog, String logicalName, String physicalName, Table denormalizedSuperTable
+ ) {
+ String key = buildTableNameKey( schema, catalog, physicalName );
+ TableDescription tableDescription = new TableDescription(
+ logicalName, denormalizedSuperTable
+ );
+ TableDescription oldDescriptor = (TableDescription) tableNameBinding.put( key, tableDescription );
+ if ( oldDescriptor != null && ! oldDescriptor.logicalName.equals( logicalName ) ) {
+ //TODO possibly relax that
+ throw new MappingException("Same physical table name reference several logical table names: "
+ + physicalName + " => " + "'" + oldDescriptor.logicalName + "' and '" + logicalName + "'");
+ }
+ }
+
+ public void addColumnBinding(String logicalName, Column finalColumn, Table table) {
+ ColumnNames binding = (ColumnNames) columnNameBindingPerTable.get(table);
+ if (binding == null) {
+ binding = new ColumnNames();
+ columnNameBindingPerTable.put(table, binding);
+ }
+ String oldFinalName = (String) binding.logicalToPhysical.put(
+ logicalName.toLowerCase(),
+ finalColumn.getQuotedName()
+ );
+ if ( oldFinalName != null &&
+ ! ( finalColumn.isQuoted() ?
+ oldFinalName.equals( finalColumn.getQuotedName() ) :
+ oldFinalName.equalsIgnoreCase( finalColumn.getQuotedName() ) ) ) {
+ //TODO possibly relax that
+ throw new MappingException("Same logical column name referenced by different physical ones: "
+ + table.getName() + "." + logicalName + " => '" + oldFinalName + "' and '" + finalColumn.getQuotedName() + "'" );
+ }
+ String oldLogicalName = (String) binding.physicalToLogical.put(
+ finalColumn.getQuotedName(),
+ logicalName
+ );
+ if ( oldLogicalName != null && ! oldLogicalName.equals( logicalName ) ) {
+ //TODO possibly relax that
+ throw new MappingException("Same physical column represented by different logical column names: "
+ + table.getName() + "." + finalColumn.getQuotedName() + " => '" + oldLogicalName + "' and '" + logicalName + "'");
+ }
+ }
+
+ private String getLogicalTableName(String schema, String catalog, String physicalName) {
+ String key = buildTableNameKey( schema, catalog, physicalName );
+ TableDescription descriptor = (TableDescription) tableNameBinding.get( key );
+ if (descriptor == null) {
+ throw new MappingException( "Unable to find physical table: " + physicalName);
+ }
+ return descriptor.logicalName;
+ }
+
+ public String getPhysicalColumnName(String logicalName, Table table) {
+ logicalName = logicalName.toLowerCase();
+ String finalName = null;
+ Table currentTable = table;
+ do {
+ ColumnNames binding = (ColumnNames) columnNameBindingPerTable.get(currentTable);
+ if (binding != null) {
+ finalName = (String) binding.logicalToPhysical.get( logicalName );
+ }
+ String key = buildTableNameKey( currentTable.getSchema(), currentTable.getCatalog(), currentTable.getName() );
+ TableDescription description = (TableDescription) tableNameBinding.get(key);
+ if (description != null) currentTable = description.denormalizedSupertable;
+ }
+ while (finalName == null && currentTable != null);
+ if (finalName == null) {
+ throw new MappingException( "Unable to find column with logical name "
+ + logicalName + " in table " + table.getName() );
+ }
+ return finalName;
+ }
+
+ public String getLogicalColumnName(String physicalName, Table table) {
+ String logical = null;
+ Table currentTable = table;
+ TableDescription description = null;
+ do {
+ ColumnNames binding = (ColumnNames) columnNameBindingPerTable.get(currentTable);
+ if (binding != null) {
+ logical = (String) binding.physicalToLogical.get( physicalName );
+ }
+ String key = buildTableNameKey( currentTable.getSchema(), currentTable.getCatalog(), currentTable.getName() );
+ description = (TableDescription) tableNameBinding.get(key);
+ if (description != null) currentTable = description.denormalizedSupertable;
+ }
+ while (logical == null && currentTable != null && description != null);
+ if (logical == null) {
+ throw new MappingException( "Unable to find logical column name from physical name "
+ + physicalName + " in table " + table.getName() );
+ }
+ return logical;
+ }
+
+ public String getLogicalTableName(Table table) {
+ return getLogicalTableName( table.getQuotedSchema(), table.getCatalog(), table.getQuotedName() );
+ }
+
+ static public class ColumnNames implements Serializable {
+ //<String, String>
+ public Map logicalToPhysical = new HashMap();
+ //<String, String>
+ public Map physicalToLogical = new HashMap();
+ public ColumnNames() {
+ }
+ }
+
+ static public class TableDescription implements Serializable {
+ public TableDescription(String logicalName, Table denormalizedSupertable) {
+ this.logicalName = logicalName;
+ this.denormalizedSupertable = denormalizedSupertable;
+ }
+
+ public String logicalName;
+ public Table denormalizedSupertable;
+ }
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/NamedSQLQuerySecondPass.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/NamedSQLQuerySecondPass.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/NamedSQLQuerySecondPass.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,99 @@
+//$Id: NamedSQLQuerySecondPass.java 10196 2006-08-03 07:53:27Z max.andersen at jboss.com $
+package org.hibernate.cfg;
+
+import java.util.Map;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.hibernate.MappingException;
+import org.hibernate.util.StringHelper;
+import org.hibernate.engine.NamedSQLQueryDefinition;
+import org.hibernate.engine.ResultSetMappingDefinition;
+import org.dom4j.Attribute;
+import org.dom4j.Element;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class NamedSQLQuerySecondPass extends ResultSetMappingBinder implements QuerySecondPass {
+ private static Log log = LogFactory.getLog( NamedSQLQuerySecondPass.class);
+ private Element queryElem;
+ private String path;
+ private Mappings mappings;
+
+ public NamedSQLQuerySecondPass(Element queryElem, String path, Mappings mappings) {
+ this.queryElem = queryElem;
+ this.path = path;
+ this.mappings = mappings;
+ }
+
+ public void doSecondPass(Map persistentClasses) throws MappingException {
+ String queryName = queryElem.attribute( "name" ).getValue();
+ if (path!=null) queryName = path + '.' + queryName;
+
+ boolean cacheable = "true".equals( queryElem.attributeValue( "cacheable" ) );
+ String region = queryElem.attributeValue( "cache-region" );
+ Attribute tAtt = queryElem.attribute( "timeout" );
+ Integer timeout = tAtt == null ? null : new Integer( tAtt.getValue() );
+ Attribute fsAtt = queryElem.attribute( "fetch-size" );
+ Integer fetchSize = fsAtt == null ? null : new Integer( fsAtt.getValue() );
+ Attribute roAttr = queryElem.attribute( "read-only" );
+ boolean readOnly = roAttr != null && "true".equals( roAttr.getValue() );
+ Attribute cacheModeAtt = queryElem.attribute( "cache-mode" );
+ String cacheMode = cacheModeAtt == null ? null : cacheModeAtt.getValue();
+ Attribute cmAtt = queryElem.attribute( "comment" );
+ String comment = cmAtt == null ? null : cmAtt.getValue();
+
+ java.util.List synchronizedTables = new ArrayList();
+ Iterator tables = queryElem.elementIterator( "synchronize" );
+ while ( tables.hasNext() ) {
+ synchronizedTables.add( ( (Element) tables.next() ).attributeValue( "table" ) );
+ }
+ boolean callable = "true".equals( queryElem.attributeValue( "callable" ) );
+
+ NamedSQLQueryDefinition namedQuery;
+ Attribute ref = queryElem.attribute( "resultset-ref" );
+ String resultSetRef = ref == null ? null : ref.getValue();
+ if ( StringHelper.isNotEmpty( resultSetRef ) ) {
+ namedQuery = new NamedSQLQueryDefinition(
+ queryElem.getText(),
+ resultSetRef,
+ synchronizedTables,
+ cacheable,
+ region,
+ timeout,
+ fetchSize,
+ HbmBinder.getFlushMode( queryElem.attributeValue( "flush-mode" ) ),
+ HbmBinder.getCacheMode( cacheMode ),
+ readOnly,
+ comment,
+ HbmBinder.getParameterTypes( queryElem ),
+ callable
+ );
+ //TODO check there is no actual definition elemnents when a ref is defined
+ }
+ else {
+ ResultSetMappingDefinition definition = buildResultSetMappingDefinition( queryElem, path, mappings );
+ namedQuery = new NamedSQLQueryDefinition(
+ queryElem.getText(),
+ definition.getQueryReturns(),
+ synchronizedTables,
+ cacheable,
+ region,
+ timeout,
+ fetchSize,
+ HbmBinder.getFlushMode( queryElem.attributeValue( "flush-mode" ) ),
+ HbmBinder.getCacheMode( cacheMode ),
+ readOnly,
+ comment,
+ HbmBinder.getParameterTypes( queryElem ),
+ callable
+ );
+ }
+
+ log.debug( "Named SQL query: " + queryName + " -> " + namedQuery.getQueryString() );
+ mappings.addSQLQuery( queryName, namedQuery );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/NamingStrategy.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/NamingStrategy.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/NamingStrategy.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,101 @@
+//$Id: NamingStrategy.java 9874 2006-05-04 15:10:22Z epbernard $
+package org.hibernate.cfg;
+
+/**
+ * A set of rules for determining the physical column
+ * and table names given the information in the mapping
+ * document. May be used to implement project-scoped
+ * naming standards for database objects.
+ *
+ * #propertyToTableName(String, String) should be replaced by
+ * {@link #collectionTableName(String,String,String,String,String)}
+ *
+ * @see DefaultNamingStrategy
+ * @see ImprovedNamingStrategy
+ * @author Gavin King
+ * @author Emmanuel Bernard
+ */
+public interface NamingStrategy {
+ /**
+ * Return a table name for an entity class
+ * @param className the fully-qualified class name
+ * @return a table name
+ */
+ public String classToTableName(String className);
+ /**
+ * Return a column name for a property path expression
+ * @param propertyName a property path
+ * @return a column name
+ */
+ public String propertyToColumnName(String propertyName);
+ /**
+ * Alter the table name given in the mapping document
+ * @param tableName a table name
+ * @return a table name
+ */
+ public String tableName(String tableName);
+ /**
+ * Alter the column name given in the mapping document
+ * @param columnName a column name
+ * @return a column name
+ */
+ public String columnName(String columnName);
+ /**
+ * Return a collection table name ie an association having a join table
+ *
+ * @param ownerEntity
+ * @param ownerEntityTable owner side table name
+ * @param associatedEntity
+ * @param associatedEntityTable reverse side table name if any
+ * @param propertyName collection role
+ */
+ public String collectionTableName(
+ String ownerEntity, String ownerEntityTable, String associatedEntity, String associatedEntityTable,
+ String propertyName
+ );
+ /**
+ * Return the join key column name ie a FK column used in a JOINED strategy or for a secondary table
+ *
+ * @param joinedColumn joined column name (logical one) used to join with
+ * @param joinedTable joined table name (ie the referenced table) used to join with
+ */
+ public String joinKeyColumnName(String joinedColumn, String joinedTable);
+ /**
+ * Return the foreign key column name for the given parameters
+ * @param propertyName the property name involved
+ * @param propertyEntityName
+ * @param propertyTableName the property table name involved (logical one)
+ * @param referencedColumnName the referenced column name involved (logical one)
+ */
+ public String foreignKeyColumnName(
+ String propertyName, String propertyEntityName, String propertyTableName, String referencedColumnName
+ );
+ /**
+ * Return the logical column name used to refer to a column in the metadata
+ * (like index, unique constraints etc)
+ * A full bijection is required between logicalNames and physical ones
+ * logicalName have to be case insersitively unique for a given table
+ *
+ * @param columnName given column name if any
+ * @param propertyName property name of this column
+ */
+ public String logicalColumnName(String columnName, String propertyName);
+ /**
+ * Returns the logical collection table name used to refer to a table in the mapping metadata
+ *
+ * @param tableName the metadata explicit name
+ * @param ownerEntityTable owner table entity table name (logical one)
+ * @param associatedEntityTable reverse side table name if any (logical one)
+ * @param propertyName collection role
+ */
+ public String logicalCollectionTableName(String tableName, String ownerEntityTable, String associatedEntityTable, String propertyName);
+
+ /**
+ * Returns the logical foreign key column name used to refer to this column in the mapping metadata
+ *
+ * @param columnName given column name in the metadata if any
+ * @param propertyName property name
+ * @param referencedColumn referenced column name (logical one) in the join
+ */
+ public String logicalCollectionColumnName(String columnName, String propertyName, String referencedColumn);
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/QuerySecondPass.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/QuerySecondPass.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/QuerySecondPass.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,10 @@
+//$Id: QuerySecondPass.java 9019 2006-01-11 18:50:33Z epbernard $
+package org.hibernate.cfg;
+
+/**
+ * Bind query
+ *
+ * @author Emmanuel Bernard
+ */
+public interface QuerySecondPass extends SecondPass {
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/ResultSetMappingBinder.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/ResultSetMappingBinder.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/ResultSetMappingBinder.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,366 @@
+//$Id: ResultSetMappingBinder.java 10180 2006-07-28 20:17:01Z epbernard $
+package org.hibernate.cfg;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Map;
+
+import org.dom4j.Element;
+import org.hibernate.LockMode;
+import org.hibernate.MappingException;
+import org.hibernate.engine.query.sql.NativeSQLQueryCollectionReturn;
+import org.hibernate.engine.ResultSetMappingDefinition;
+import org.hibernate.engine.query.sql.NativeSQLQueryJoinReturn;
+import org.hibernate.engine.query.sql.NativeSQLQueryRootReturn;
+import org.hibernate.engine.query.sql.NativeSQLQueryScalarReturn;
+import org.hibernate.mapping.Component;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Value;
+import org.hibernate.mapping.Property;
+import org.hibernate.mapping.ToOne;
+import org.hibernate.type.Type;
+import org.hibernate.type.TypeFactory;
+import org.hibernate.util.ArrayHelper;
+import org.hibernate.util.CollectionHelper;
+import org.hibernate.util.StringHelper;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public abstract class ResultSetMappingBinder {
+ /**
+ * Build a ResultSetMappingDefinition given a containing element for the "return-XXX" elements
+ *
+ * @param resultSetElem The element containing the return definitions.
+ * @param path No clue...
+ * @param mappings The current processing state.
+ * @return The description of the mappings...
+ */
+ protected static ResultSetMappingDefinition buildResultSetMappingDefinition(Element resultSetElem, String path, Mappings mappings) {
+ String resultSetName = resultSetElem.attribute( "name" ).getValue();
+ if ( path != null ) {
+ resultSetName = path + '.' + resultSetName;
+ }
+ ResultSetMappingDefinition definition = new ResultSetMappingDefinition( resultSetName );
+
+ int cnt = 0;
+ Iterator returns = resultSetElem.elementIterator();
+ while ( returns.hasNext() ) {
+ cnt++;
+ Element returnElem = (Element) returns.next();
+ String name = returnElem.getName();
+ if ( "return-scalar".equals( name ) ) {
+ String column = returnElem.attributeValue( "column" );
+ String typeFromXML = HbmBinder.getTypeFromXML( returnElem );
+ Type type = null;
+ if(typeFromXML!=null) {
+ type = TypeFactory.heuristicType( typeFromXML );
+ if ( type == null ) {
+ throw new MappingException( "could not determine type " + type );
+ }
+ }
+ definition.addQueryReturn( new NativeSQLQueryScalarReturn( column, type ) );
+ }
+ else if ( "return".equals( name ) ) {
+ definition.addQueryReturn( bindReturn( returnElem, mappings, cnt ) );
+ }
+ else if ( "return-join".equals( name ) ) {
+ definition.addQueryReturn( bindReturnJoin( returnElem, mappings ) );
+ }
+ else if ( "load-collection".equals( name ) ) {
+ definition.addQueryReturn( bindLoadCollection( returnElem, mappings ) );
+ }
+ }
+ return definition;
+ }
+
+ private static NativeSQLQueryRootReturn bindReturn(Element returnElem, Mappings mappings, int elementCount) {
+ String alias = returnElem.attributeValue( "alias" );
+ if( StringHelper.isEmpty(alias)) {
+ alias = "alias_" + elementCount; // hack/workaround as sqlquery impl depend on having a key.
+ }
+
+ String entityName = HbmBinder.getEntityName(returnElem, mappings);
+ if(entityName==null) {
+ throw new MappingException( "<return alias='" + alias + "'> must specify either a class or entity-name");
+ }
+ LockMode lockMode = getLockMode( returnElem.attributeValue( "lock-mode" ) );
+
+ PersistentClass pc = mappings.getClass( entityName );
+ java.util.Map propertyResults = bindPropertyResults(alias, returnElem, pc, mappings );
+
+ return new NativeSQLQueryRootReturn(
+ alias,
+ entityName,
+ propertyResults,
+ lockMode
+ );
+ }
+
+ private static NativeSQLQueryJoinReturn bindReturnJoin(Element returnElem, Mappings mappings) {
+ String alias = returnElem.attributeValue( "alias" );
+ String roleAttribute = returnElem.attributeValue( "property" );
+ LockMode lockMode = getLockMode( returnElem.attributeValue( "lock-mode" ) );
+ int dot = roleAttribute.lastIndexOf( '.' );
+ if ( dot == -1 ) {
+ throw new MappingException(
+ "Role attribute for sql query return [alias=" + alias +
+ "] not formatted correctly {owningAlias.propertyName}"
+ );
+ }
+ String roleOwnerAlias = roleAttribute.substring( 0, dot );
+ String roleProperty = roleAttribute.substring( dot + 1 );
+
+ //FIXME: get the PersistentClass
+ java.util.Map propertyResults = bindPropertyResults(alias, returnElem, null, mappings );
+
+ return new NativeSQLQueryJoinReturn(
+ alias,
+ roleOwnerAlias,
+ roleProperty,
+ propertyResults, // TODO: bindpropertyresults(alias, returnElem)
+ lockMode
+ );
+ }
+
+ private static NativeSQLQueryCollectionReturn bindLoadCollection(Element returnElem, Mappings mappings) {
+ String alias = returnElem.attributeValue( "alias" );
+ String collectionAttribute = returnElem.attributeValue( "role" );
+ LockMode lockMode = getLockMode( returnElem.attributeValue( "lock-mode" ) );
+ int dot = collectionAttribute.lastIndexOf( '.' );
+ if ( dot == -1 ) {
+ throw new MappingException(
+ "Collection attribute for sql query return [alias=" + alias +
+ "] not formatted correctly {OwnerClassName.propertyName}"
+ );
+ }
+ String ownerClassName = HbmBinder.getClassName( collectionAttribute.substring( 0, dot ), mappings );
+ String ownerPropertyName = collectionAttribute.substring( dot + 1 );
+
+ //FIXME: get the PersistentClass
+ java.util.Map propertyResults = bindPropertyResults(alias, returnElem, null, mappings );
+
+ return new NativeSQLQueryCollectionReturn(
+ alias,
+ ownerClassName,
+ ownerPropertyName,
+ propertyResults,
+ lockMode
+ );
+ }
+
+ private static java.util.Map bindPropertyResults(
+ String alias, Element returnElement, PersistentClass pc, Mappings mappings
+ ) {
+
+ HashMap propertyresults = new HashMap(); // maybe a concrete SQLpropertyresult type, but Map is exactly what is required at the moment
+
+ Element discriminatorResult = returnElement.element("return-discriminator");
+ if(discriminatorResult!=null) {
+ ArrayList resultColumns = getResultColumns(discriminatorResult);
+ propertyresults.put("class", ArrayHelper.toStringArray(resultColumns) );
+ }
+ Iterator iterator = returnElement.elementIterator("return-property");
+ List properties = new ArrayList();
+ List propertyNames = new ArrayList();
+ while ( iterator.hasNext() ) {
+ Element propertyresult = (Element) iterator.next();
+ String name = propertyresult.attributeValue("name");
+ if ( pc == null || name.indexOf( '.') == -1) { //if dotted and not load-collection nor return-join
+ //regular property
+ properties.add(propertyresult);
+ propertyNames.add(name);
+ }
+ else {
+ /**
+ * Reorder properties
+ * 1. get the parent property
+ * 2. list all the properties following the expected one in the parent property
+ * 3. calculate the lowest index and insert the property
+ */
+ if (pc == null)
+ throw new MappingException("dotted notation in <return-join> or <load_collection> not yet supported");
+ int dotIndex = name.lastIndexOf( '.' );
+ String reducedName = name.substring( 0, dotIndex );
+ Value value = pc.getRecursiveProperty( reducedName ).getValue();
+ Iterator parentPropIter;
+ if ( value instanceof Component ) {
+ Component comp = (Component) value;
+ parentPropIter = comp.getPropertyIterator();
+ }
+ else if ( value instanceof ToOne ) {
+ ToOne toOne = (ToOne) value;
+ PersistentClass referencedPc = mappings.getClass( toOne.getReferencedEntityName() );
+ if ( toOne.getReferencedPropertyName() != null ) {
+ try {
+ parentPropIter = ( (Component) referencedPc.getRecursiveProperty( toOne.getReferencedPropertyName() ).getValue() ).getPropertyIterator();
+ } catch (ClassCastException e) {
+ throw new MappingException("dotted notation reference neither a component nor a many/one to one", e);
+ }
+ }
+ else {
+ try {
+ if ( referencedPc.getIdentifierMapper() == null ) {
+ parentPropIter = ( (Component) referencedPc.getIdentifierProperty().getValue() ).getPropertyIterator();
+ }
+ else {
+ parentPropIter = referencedPc.getIdentifierMapper().getPropertyIterator();
+ }
+ }
+ catch (ClassCastException e) {
+ throw new MappingException("dotted notation reference neither a component nor a many/one to one", e);
+ }
+ }
+ }
+ else {
+ throw new MappingException("dotted notation reference neither a component nor a many/one to one");
+ }
+ boolean hasFollowers = false;
+ List followers = new ArrayList();
+ while ( parentPropIter.hasNext() ) {
+ String currentPropertyName = ( (Property) parentPropIter.next() ).getName();
+ String currentName = reducedName + '.' + currentPropertyName;
+ if (hasFollowers) {
+ followers.add( currentName );
+ }
+ if ( name.equals( currentName ) ) hasFollowers = true;
+ }
+
+ int index = propertyNames.size();
+ int followersSize = followers.size();
+ for (int loop = 0 ; loop < followersSize ; loop++) {
+ String follower = (String) followers.get(loop);
+ int currentIndex = getIndexOfFirstMatchingProperty(propertyNames, follower);
+ index = currentIndex != -1 && currentIndex < index ? currentIndex : index;
+ }
+ propertyNames.add(index, name);
+ properties.add(index, propertyresult);
+ }
+ }
+
+ Set uniqueReturnProperty = new HashSet();
+ iterator = properties.iterator();
+ while ( iterator.hasNext() ) {
+ Element propertyresult = (Element) iterator.next();
+ String name = propertyresult.attributeValue("name");
+ if ( "class".equals(name) ) {
+ throw new MappingException(
+ "class is not a valid property name to use in a <return-property>, use <return-discriminator> instead"
+ );
+ }
+ //TODO: validate existing of property with the chosen name. (secondpass )
+ ArrayList allResultColumns = getResultColumns(propertyresult);
+
+ if ( allResultColumns.isEmpty() ) {
+ throw new MappingException(
+ "return-property for alias " + alias +
+ " must specify at least one column or return-column name"
+ );
+ }
+ if ( uniqueReturnProperty.contains( name ) ) {
+ throw new MappingException(
+ "duplicate return-property for property " + name +
+ " on alias " + alias
+ );
+ }
+ uniqueReturnProperty.add(name);
+
+ // the issue here is that for <return-join/> representing an entity collection,
+ // the collection element values (the property values of the associated entity)
+ // are represented as 'element.{propertyname}'. Thus the StringHelper.root()
+ // here puts everything under 'element' (which additionally has significant
+ // meaning). Probably what we need to do is to something like this instead:
+ // String root = StringHelper.root( name );
+ // String key = root; // by default
+ // if ( !root.equals( name ) ) {
+ // // we had a dot
+ // if ( !root.equals( alias ) {
+ // // the root does not apply to the specific alias
+ // if ( "elements".equals( root ) {
+ // // we specifically have a <return-join/> representing an entity collection
+ // // and this <return-property/> is one of that entity's properties
+ // key = name;
+ // }
+ // }
+ // }
+ // but I am not clear enough on the intended purpose of this code block, especially
+ // in relation to the "Reorder properties" code block above...
+// String key = StringHelper.root( name );
+ String key = name;
+ ArrayList intermediateResults = (ArrayList) propertyresults.get( key );
+ if (intermediateResults == null) {
+ propertyresults.put( key, allResultColumns );
+ }
+ else {
+ intermediateResults.addAll( allResultColumns );
+ }
+ }
+
+ Iterator entries = propertyresults.entrySet().iterator();
+ while ( entries.hasNext() ) {
+ Map.Entry entry = (Map.Entry) entries.next();
+ if (entry.getValue() instanceof ArrayList) {
+ ArrayList list = (ArrayList) entry.getValue();
+ entry.setValue( list.toArray( new String[ list.size() ] ) );
+ }
+ }
+ return propertyresults.isEmpty() ? CollectionHelper.EMPTY_MAP : propertyresults;
+ }
+
+ private static int getIndexOfFirstMatchingProperty(List propertyNames, String follower) {
+ int propertySize = propertyNames.size();
+ for (int propIndex = 0 ; propIndex < propertySize ; propIndex++) {
+ if ( ( (String) propertyNames.get(propIndex) ).startsWith( follower ) ) {
+ return propIndex;
+ }
+ }
+ return -1;
+ }
+
+ private static ArrayList getResultColumns(Element propertyresult) {
+ String column = unquote(propertyresult.attributeValue("column"));
+ ArrayList allResultColumns = new ArrayList();
+ if(column!=null) allResultColumns.add(column);
+ Iterator resultColumns = propertyresult.elementIterator("return-column");
+ while ( resultColumns.hasNext() ) {
+ Element element = (Element) resultColumns.next();
+ allResultColumns.add( unquote(element.attributeValue("name")) );
+ }
+ return allResultColumns;
+ }
+
+ private static String unquote(String name) {
+ if (name!=null && name.charAt(0)=='`') {
+ name=name.substring( 1, name.length()-1 );
+ }
+ return name;
+ }
+
+ private static LockMode getLockMode(String lockMode) {
+ if ( lockMode == null || "read".equals( lockMode ) ) {
+ return LockMode.READ;
+ }
+ else if ( "none".equals( lockMode ) ) {
+ return LockMode.NONE;
+ }
+ else if ( "upgrade".equals( lockMode ) ) {
+ return LockMode.UPGRADE;
+ }
+ else if ( "upgrade-nowait".equals( lockMode ) ) {
+ return LockMode.UPGRADE_NOWAIT;
+ }
+ else if ( "write".equals( lockMode ) ) {
+ return LockMode.WRITE;
+ }
+ else if ( "force".equals( lockMode ) ) {
+ return LockMode.FORCE;
+ }
+ else {
+ throw new MappingException( "unknown lockmode" );
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/ResultSetMappingSecondPass.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/ResultSetMappingSecondPass.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/ResultSetMappingSecondPass.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,28 @@
+//$Id: ResultSetMappingSecondPass.java 10196 2006-08-03 07:53:27Z max.andersen at jboss.com $
+package org.hibernate.cfg;
+
+import java.util.Map;
+
+import org.dom4j.Element;
+import org.hibernate.MappingException;
+import org.hibernate.engine.ResultSetMappingDefinition;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class ResultSetMappingSecondPass extends ResultSetMappingBinder implements QuerySecondPass {
+ private Element element;
+ private String path;
+ private Mappings mappings;
+
+ public ResultSetMappingSecondPass(Element element, String path, Mappings mappings) {
+ this.element = element;
+ this.path = path;
+ this.mappings = mappings;
+ }
+
+ public void doSecondPass(Map persistentClasses) throws MappingException {
+ ResultSetMappingDefinition definition = buildResultSetMappingDefinition( element, path, mappings);
+ mappings.addResultSetMapping( definition );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/SecondPass.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/SecondPass.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/SecondPass.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,18 @@
+//$Id: SecondPass.java 10196 2006-08-03 07:53:27Z max.andersen at jboss.com $
+package org.hibernate.cfg;
+
+import java.io.Serializable;
+
+import org.hibernate.MappingException;
+
+/**
+ * Second pass operation
+ *
+ * @author Emmanuel Bernard
+ */
+public interface SecondPass extends Serializable {
+
+ void doSecondPass(java.util.Map persistentClasses)
+ throws MappingException;
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/Settings.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/Settings.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/Settings.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,451 @@
+//$Id: Settings.java 11402 2007-04-11 14:24:35Z steve.ebersole at jboss.com $
+package org.hibernate.cfg;
+
+import java.util.Map;
+
+import org.hibernate.cache.QueryCacheFactory;
+import org.hibernate.cache.RegionFactory;
+import org.hibernate.connection.ConnectionProvider;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.hql.QueryTranslatorFactory;
+import org.hibernate.jdbc.BatcherFactory;
+import org.hibernate.transaction.TransactionFactory;
+import org.hibernate.transaction.TransactionManagerLookup;
+import org.hibernate.exception.SQLExceptionConverter;
+import org.hibernate.EntityMode;
+import org.hibernate.ConnectionReleaseMode;
+
+/**
+ * Settings that affect the behaviour of Hibernate at runtime.
+ *
+ * @author Gavin King
+ */
+public final class Settings {
+
+ private boolean showSql;
+ private boolean formatSql;
+ private Integer maximumFetchDepth;
+ private Map querySubstitutions;
+ private Dialect dialect;
+ private int jdbcBatchSize;
+ private int defaultBatchFetchSize;
+ private boolean scrollableResultSetsEnabled;
+ private boolean getGeneratedKeysEnabled;
+ private String defaultSchemaName;
+ private String defaultCatalogName;
+ private Integer jdbcFetchSize;
+ private String sessionFactoryName;
+ private boolean autoCreateSchema;
+ private boolean autoDropSchema;
+ private boolean autoUpdateSchema;
+ private boolean autoValidateSchema;
+ private boolean queryCacheEnabled;
+ private boolean structuredCacheEntriesEnabled;
+ private boolean secondLevelCacheEnabled;
+ private String cacheRegionPrefix;
+ private boolean minimalPutsEnabled;
+ private boolean commentsEnabled;
+ private boolean statisticsEnabled;
+ private boolean jdbcBatchVersionedData;
+ private boolean identifierRollbackEnabled;
+ private boolean flushBeforeCompletionEnabled;
+ private boolean autoCloseSessionEnabled;
+ private ConnectionReleaseMode connectionReleaseMode;
+ private RegionFactory regionFactory;
+ private QueryCacheFactory queryCacheFactory;
+ private ConnectionProvider connectionProvider;
+ private TransactionFactory transactionFactory;
+ private TransactionManagerLookup transactionManagerLookup;
+ private BatcherFactory batcherFactory;
+ private QueryTranslatorFactory queryTranslatorFactory;
+ private SQLExceptionConverter sqlExceptionConverter;
+ private boolean wrapResultSetsEnabled;
+ private boolean orderUpdatesEnabled;
+ private boolean orderInsertsEnabled;
+ private EntityMode defaultEntityMode;
+ private boolean dataDefinitionImplicitCommit;
+ private boolean dataDefinitionInTransactionSupported;
+ private boolean strictJPAQLCompliance;
+ private boolean namedQueryStartupCheckingEnabled;
+// private BytecodeProvider bytecodeProvider;
+
+ /**
+ * Package protected constructor
+ */
+ Settings() {
+ }
+
+ // public getters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public String getDefaultSchemaName() {
+ return defaultSchemaName;
+ }
+
+ public String getDefaultCatalogName() {
+ return defaultCatalogName;
+ }
+
+ public Dialect getDialect() {
+ return dialect;
+ }
+
+ public int getJdbcBatchSize() {
+ return jdbcBatchSize;
+ }
+
+ public int getDefaultBatchFetchSize() {
+ return defaultBatchFetchSize;
+ }
+
+ public Map getQuerySubstitutions() {
+ return querySubstitutions;
+ }
+
+ public boolean isShowSqlEnabled() {
+ return showSql;
+ }
+
+ public boolean isFormatSqlEnabled() {
+ return formatSql;
+ }
+
+ public boolean isIdentifierRollbackEnabled() {
+ return identifierRollbackEnabled;
+ }
+
+ public boolean isScrollableResultSetsEnabled() {
+ return scrollableResultSetsEnabled;
+ }
+
+ public boolean isGetGeneratedKeysEnabled() {
+ return getGeneratedKeysEnabled;
+ }
+
+ public boolean isMinimalPutsEnabled() {
+ return minimalPutsEnabled;
+ }
+
+ public Integer getJdbcFetchSize() {
+ return jdbcFetchSize;
+ }
+
+ public ConnectionProvider getConnectionProvider() {
+ return connectionProvider;
+ }
+
+ public TransactionFactory getTransactionFactory() {
+ return transactionFactory;
+ }
+
+ public String getSessionFactoryName() {
+ return sessionFactoryName;
+ }
+
+ public boolean isAutoCreateSchema() {
+ return autoCreateSchema;
+ }
+
+ public boolean isAutoDropSchema() {
+ return autoDropSchema;
+ }
+
+ public boolean isAutoUpdateSchema() {
+ return autoUpdateSchema;
+ }
+
+ public Integer getMaximumFetchDepth() {
+ return maximumFetchDepth;
+ }
+
+ public RegionFactory getRegionFactory() {
+ return regionFactory;
+ }
+
+ public TransactionManagerLookup getTransactionManagerLookup() {
+ return transactionManagerLookup;
+ }
+
+ public boolean isQueryCacheEnabled() {
+ return queryCacheEnabled;
+ }
+
+ public boolean isCommentsEnabled() {
+ return commentsEnabled;
+ }
+
+ public boolean isSecondLevelCacheEnabled() {
+ return secondLevelCacheEnabled;
+ }
+
+ public String getCacheRegionPrefix() {
+ return cacheRegionPrefix;
+ }
+
+ public QueryCacheFactory getQueryCacheFactory() {
+ return queryCacheFactory;
+ }
+
+ public boolean isStatisticsEnabled() {
+ return statisticsEnabled;
+ }
+
+ public boolean isJdbcBatchVersionedData() {
+ return jdbcBatchVersionedData;
+ }
+
+ public boolean isFlushBeforeCompletionEnabled() {
+ return flushBeforeCompletionEnabled;
+ }
+
+ public BatcherFactory getBatcherFactory() {
+ return batcherFactory;
+ }
+
+ public boolean isAutoCloseSessionEnabled() {
+ return autoCloseSessionEnabled;
+ }
+
+ public ConnectionReleaseMode getConnectionReleaseMode() {
+ return connectionReleaseMode;
+ }
+
+ public QueryTranslatorFactory getQueryTranslatorFactory() {
+ return queryTranslatorFactory;
+ }
+
+ public SQLExceptionConverter getSQLExceptionConverter() {
+ return sqlExceptionConverter;
+ }
+
+ public boolean isWrapResultSetsEnabled() {
+ return wrapResultSetsEnabled;
+ }
+
+ public boolean isOrderUpdatesEnabled() {
+ return orderUpdatesEnabled;
+ }
+
+ public boolean isOrderInsertsEnabled() {
+ return orderInsertsEnabled;
+ }
+
+ public boolean isStructuredCacheEntriesEnabled() {
+ return structuredCacheEntriesEnabled;
+ }
+
+ public EntityMode getDefaultEntityMode() {
+ return defaultEntityMode;
+ }
+
+ public boolean isAutoValidateSchema() {
+ return autoValidateSchema;
+ }
+
+ public boolean isDataDefinitionImplicitCommit() {
+ return dataDefinitionImplicitCommit;
+ }
+
+ public boolean isDataDefinitionInTransactionSupported() {
+ return dataDefinitionInTransactionSupported;
+ }
+
+ public boolean isStrictJPAQLCompliance() {
+ return strictJPAQLCompliance;
+ }
+
+ public boolean isNamedQueryStartupCheckingEnabled() {
+ return namedQueryStartupCheckingEnabled;
+ }
+
+
+ // package protected setters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ void setDefaultSchemaName(String string) {
+ defaultSchemaName = string;
+ }
+
+ void setDefaultCatalogName(String string) {
+ defaultCatalogName = string;
+ }
+
+ void setDialect(Dialect dialect) {
+ this.dialect = dialect;
+ }
+
+ void setJdbcBatchSize(int i) {
+ jdbcBatchSize = i;
+ }
+
+ void setDefaultBatchFetchSize(int i) {
+ defaultBatchFetchSize = i;
+ }
+
+ void setQuerySubstitutions(Map map) {
+ querySubstitutions = map;
+ }
+
+ void setShowSqlEnabled(boolean b) {
+ showSql = b;
+ }
+
+ void setFormatSqlEnabled(boolean b) {
+ formatSql = b;
+ }
+
+ void setIdentifierRollbackEnabled(boolean b) {
+ identifierRollbackEnabled = b;
+ }
+
+ void setMinimalPutsEnabled(boolean b) {
+ minimalPutsEnabled = b;
+ }
+
+ void setScrollableResultSetsEnabled(boolean b) {
+ scrollableResultSetsEnabled = b;
+ }
+
+ void setGetGeneratedKeysEnabled(boolean b) {
+ getGeneratedKeysEnabled = b;
+ }
+
+ void setJdbcFetchSize(Integer integer) {
+ jdbcFetchSize = integer;
+ }
+
+ void setConnectionProvider(ConnectionProvider provider) {
+ connectionProvider = provider;
+ }
+
+ void setTransactionFactory(TransactionFactory factory) {
+ transactionFactory = factory;
+ }
+
+ void setSessionFactoryName(String string) {
+ sessionFactoryName = string;
+ }
+
+ void setAutoCreateSchema(boolean b) {
+ autoCreateSchema = b;
+ }
+
+ void setAutoDropSchema(boolean b) {
+ autoDropSchema = b;
+ }
+
+ void setAutoUpdateSchema(boolean b) {
+ autoUpdateSchema = b;
+ }
+
+ void setMaximumFetchDepth(Integer i) {
+ maximumFetchDepth = i;
+ }
+
+ void setRegionFactory(RegionFactory regionFactory) {
+ this.regionFactory = regionFactory;
+ }
+
+ void setTransactionManagerLookup(TransactionManagerLookup lookup) {
+ transactionManagerLookup = lookup;
+ }
+
+ void setQueryCacheEnabled(boolean b) {
+ queryCacheEnabled = b;
+ }
+
+ void setCommentsEnabled(boolean commentsEnabled) {
+ this.commentsEnabled = commentsEnabled;
+ }
+
+ void setSecondLevelCacheEnabled(boolean secondLevelCacheEnabled) {
+ this.secondLevelCacheEnabled = secondLevelCacheEnabled;
+ }
+
+ void setCacheRegionPrefix(String cacheRegionPrefix) {
+ this.cacheRegionPrefix = cacheRegionPrefix;
+ }
+
+ void setQueryCacheFactory(QueryCacheFactory queryCacheFactory) {
+ this.queryCacheFactory = queryCacheFactory;
+ }
+
+ void setStatisticsEnabled(boolean statisticsEnabled) {
+ this.statisticsEnabled = statisticsEnabled;
+ }
+
+ void setJdbcBatchVersionedData(boolean jdbcBatchVersionedData) {
+ this.jdbcBatchVersionedData = jdbcBatchVersionedData;
+ }
+
+ void setFlushBeforeCompletionEnabled(boolean flushBeforeCompletionEnabled) {
+ this.flushBeforeCompletionEnabled = flushBeforeCompletionEnabled;
+ }
+
+ void setBatcherFactory(BatcherFactory batcher) {
+ this.batcherFactory = batcher;
+ }
+
+ void setAutoCloseSessionEnabled(boolean autoCloseSessionEnabled) {
+ this.autoCloseSessionEnabled = autoCloseSessionEnabled;
+ }
+
+ void setConnectionReleaseMode(ConnectionReleaseMode connectionReleaseMode) {
+ this.connectionReleaseMode = connectionReleaseMode;
+ }
+
+ void setQueryTranslatorFactory(QueryTranslatorFactory queryTranslatorFactory) {
+ this.queryTranslatorFactory = queryTranslatorFactory;
+ }
+
+ void setSQLExceptionConverter(SQLExceptionConverter sqlExceptionConverter) {
+ this.sqlExceptionConverter = sqlExceptionConverter;
+ }
+
+ void setWrapResultSetsEnabled(boolean wrapResultSetsEnabled) {
+ this.wrapResultSetsEnabled = wrapResultSetsEnabled;
+ }
+
+ void setOrderUpdatesEnabled(boolean orderUpdatesEnabled) {
+ this.orderUpdatesEnabled = orderUpdatesEnabled;
+ }
+
+ void setOrderInsertsEnabled(boolean orderInsertsEnabled) {
+ this.orderInsertsEnabled = orderInsertsEnabled;
+ }
+
+ void setStructuredCacheEntriesEnabled(boolean structuredCacheEntriesEnabled) {
+ this.structuredCacheEntriesEnabled = structuredCacheEntriesEnabled;
+ }
+
+ void setDefaultEntityMode(EntityMode defaultEntityMode) {
+ this.defaultEntityMode = defaultEntityMode;
+ }
+
+ void setAutoValidateSchema(boolean autoValidateSchema) {
+ this.autoValidateSchema = autoValidateSchema;
+ }
+
+ void setDataDefinitionImplicitCommit(boolean dataDefinitionImplicitCommit) {
+ this.dataDefinitionImplicitCommit = dataDefinitionImplicitCommit;
+ }
+
+ void setDataDefinitionInTransactionSupported(boolean dataDefinitionInTransactionSupported) {
+ this.dataDefinitionInTransactionSupported = dataDefinitionInTransactionSupported;
+ }
+
+ void setStrictJPAQLCompliance(boolean strictJPAQLCompliance) {
+ this.strictJPAQLCompliance = strictJPAQLCompliance;
+ }
+
+ void setNamedQueryStartupCheckingEnabled(boolean namedQueryStartupCheckingEnabled) {
+ this.namedQueryStartupCheckingEnabled = namedQueryStartupCheckingEnabled;
+ }
+
+
+// public BytecodeProvider getBytecodeProvider() {
+// return bytecodeProvider;
+// }
+//
+// void setBytecodeProvider(BytecodeProvider bytecodeProvider) {
+// this.bytecodeProvider = bytecodeProvider;
+// }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/SettingsFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/SettingsFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/SettingsFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,436 @@
+//$Id: SettingsFactory.java 11402 2007-04-11 14:24:35Z steve.ebersole at jboss.com $
+package org.hibernate.cfg;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.ConnectionReleaseMode;
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.bytecode.BytecodeProvider;
+import org.hibernate.cache.QueryCacheFactory;
+import org.hibernate.cache.RegionFactory;
+import org.hibernate.cache.impl.NoCachingRegionFactory;
+import org.hibernate.cache.impl.bridge.RegionFactoryCacheProviderBridge;
+import org.hibernate.connection.ConnectionProvider;
+import org.hibernate.connection.ConnectionProviderFactory;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.DialectFactory;
+import org.hibernate.exception.SQLExceptionConverter;
+import org.hibernate.exception.SQLExceptionConverterFactory;
+import org.hibernate.hql.QueryTranslatorFactory;
+import org.hibernate.jdbc.BatcherFactory;
+import org.hibernate.jdbc.BatchingBatcherFactory;
+import org.hibernate.jdbc.NonBatchingBatcherFactory;
+import org.hibernate.transaction.TransactionFactory;
+import org.hibernate.transaction.TransactionFactoryFactory;
+import org.hibernate.transaction.TransactionManagerLookup;
+import org.hibernate.transaction.TransactionManagerLookupFactory;
+import org.hibernate.util.PropertiesHelper;
+import org.hibernate.util.ReflectHelper;
+import org.hibernate.util.StringHelper;
+
+/**
+ * Reads configuration properties and configures a <tt>Settings</tt> instance.
+ *
+ * @author Gavin King
+ */
+public class SettingsFactory implements Serializable {
+
+ public static final String DEF_CACHE_REG_FACTORY = NoCachingRegionFactory.class.getName();
+ private static final Log log = LogFactory.getLog(SettingsFactory.class);
+
+ protected SettingsFactory() {
+ }
+
+ public Settings buildSettings(Properties props) {
+ Settings settings = new Settings();
+
+ //SessionFactory name:
+
+ String sessionFactoryName = props.getProperty(Environment.SESSION_FACTORY_NAME);
+ settings.setSessionFactoryName(sessionFactoryName);
+
+ //JDBC and connection settings:
+
+ ConnectionProvider connections = createConnectionProvider(props);
+ settings.setConnectionProvider(connections);
+
+ //Interrogate JDBC metadata
+
+ String databaseName = null;
+ int databaseMajorVersion = 0;
+ boolean metaSupportsScrollable = false;
+ boolean metaSupportsGetGeneratedKeys = false;
+ boolean metaSupportsBatchUpdates = false;
+ boolean metaReportsDDLCausesTxnCommit = false;
+ boolean metaReportsDDLInTxnSupported = true;
+
+ // 'hibernate.temp.use_jdbc_metadata_defaults' is a temporary magic value.
+ // The need for it is intended to be alleviated with 3.3 developement, thus it is
+ // not defined as an Environment constant...
+ // it is used to control whether we should consult the JDBC metadata to determine
+ // certain Settings default values; it is useful to *not* do this when the database
+ // may not be available (mainly in tools usage).
+ boolean useJdbcMetadata = PropertiesHelper.getBoolean( "hibernate.temp.use_jdbc_metadata_defaults", props, true );
+ if ( useJdbcMetadata ) {
+ try {
+ Connection conn = connections.getConnection();
+ try {
+ DatabaseMetaData meta = conn.getMetaData();
+ databaseName = meta.getDatabaseProductName();
+ databaseMajorVersion = getDatabaseMajorVersion(meta);
+ log.info("RDBMS: " + databaseName + ", version: " + meta.getDatabaseProductVersion() );
+ log.info("JDBC driver: " + meta.getDriverName() + ", version: " + meta.getDriverVersion() );
+
+ metaSupportsScrollable = meta.supportsResultSetType(ResultSet.TYPE_SCROLL_INSENSITIVE);
+ metaSupportsBatchUpdates = meta.supportsBatchUpdates();
+ metaReportsDDLCausesTxnCommit = meta.dataDefinitionCausesTransactionCommit();
+ metaReportsDDLInTxnSupported = !meta.dataDefinitionIgnoredInTransactions();
+
+ if ( Environment.jvmSupportsGetGeneratedKeys() ) {
+ try {
+ Boolean result = (Boolean) DatabaseMetaData.class.getMethod("supportsGetGeneratedKeys", null)
+ .invoke(meta, null);
+ metaSupportsGetGeneratedKeys = result.booleanValue();
+ }
+ catch (AbstractMethodError ame) {
+ metaSupportsGetGeneratedKeys = false;
+ }
+ catch (Exception e) {
+ metaSupportsGetGeneratedKeys = false;
+ }
+ }
+
+ }
+ finally {
+ connections.closeConnection(conn);
+ }
+ }
+ catch (SQLException sqle) {
+ log.warn("Could not obtain connection metadata", sqle);
+ }
+ catch (UnsupportedOperationException uoe) {
+ // user supplied JDBC connections
+ }
+ }
+ settings.setDataDefinitionImplicitCommit( metaReportsDDLCausesTxnCommit );
+ settings.setDataDefinitionInTransactionSupported( metaReportsDDLInTxnSupported );
+
+
+ //SQL Dialect:
+ Dialect dialect = determineDialect( props, databaseName, databaseMajorVersion );
+ settings.setDialect(dialect);
+
+ //use dialect default properties
+ final Properties properties = new Properties();
+ properties.putAll( dialect.getDefaultProperties() );
+ properties.putAll(props);
+
+ // Transaction settings:
+
+ TransactionFactory transactionFactory = createTransactionFactory(properties);
+ settings.setTransactionFactory(transactionFactory);
+ settings.setTransactionManagerLookup( createTransactionManagerLookup(properties) );
+
+ boolean flushBeforeCompletion = PropertiesHelper.getBoolean(Environment.FLUSH_BEFORE_COMPLETION, properties);
+ log.info("Automatic flush during beforeCompletion(): " + enabledDisabled(flushBeforeCompletion) );
+ settings.setFlushBeforeCompletionEnabled(flushBeforeCompletion);
+
+ boolean autoCloseSession = PropertiesHelper.getBoolean(Environment.AUTO_CLOSE_SESSION, properties);
+ log.info("Automatic session close at end of transaction: " + enabledDisabled(autoCloseSession) );
+ settings.setAutoCloseSessionEnabled(autoCloseSession);
+
+ //JDBC and connection settings:
+
+ int batchSize = PropertiesHelper.getInt(Environment.STATEMENT_BATCH_SIZE, properties, 0);
+ if ( !metaSupportsBatchUpdates ) batchSize = 0;
+ if (batchSize>0) log.info("JDBC batch size: " + batchSize);
+ settings.setJdbcBatchSize(batchSize);
+ boolean jdbcBatchVersionedData = PropertiesHelper.getBoolean(Environment.BATCH_VERSIONED_DATA, properties, false);
+ if (batchSize>0) log.info("JDBC batch updates for versioned data: " + enabledDisabled(jdbcBatchVersionedData) );
+ settings.setJdbcBatchVersionedData(jdbcBatchVersionedData);
+ settings.setBatcherFactory( createBatcherFactory(properties, batchSize) );
+
+ boolean useScrollableResultSets = PropertiesHelper.getBoolean(Environment.USE_SCROLLABLE_RESULTSET, properties, metaSupportsScrollable);
+ log.info("Scrollable result sets: " + enabledDisabled(useScrollableResultSets) );
+ settings.setScrollableResultSetsEnabled(useScrollableResultSets);
+
+ boolean wrapResultSets = PropertiesHelper.getBoolean(Environment.WRAP_RESULT_SETS, properties, false);
+ log.debug( "Wrap result sets: " + enabledDisabled(wrapResultSets) );
+ settings.setWrapResultSetsEnabled(wrapResultSets);
+
+ boolean useGetGeneratedKeys = PropertiesHelper.getBoolean(Environment.USE_GET_GENERATED_KEYS, properties, metaSupportsGetGeneratedKeys);
+ log.info("JDBC3 getGeneratedKeys(): " + enabledDisabled(useGetGeneratedKeys) );
+ settings.setGetGeneratedKeysEnabled(useGetGeneratedKeys);
+
+ Integer statementFetchSize = PropertiesHelper.getInteger(Environment.STATEMENT_FETCH_SIZE, properties);
+ if (statementFetchSize!=null) log.info("JDBC result set fetch size: " + statementFetchSize);
+ settings.setJdbcFetchSize(statementFetchSize);
+
+ String releaseModeName = PropertiesHelper.getString( Environment.RELEASE_CONNECTIONS, properties, "auto" );
+ log.info( "Connection release mode: " + releaseModeName );
+ ConnectionReleaseMode releaseMode;
+ if ( "auto".equals(releaseModeName) ) {
+ releaseMode = transactionFactory.getDefaultReleaseMode();
+ }
+ else {
+ releaseMode = ConnectionReleaseMode.parse( releaseModeName );
+ if ( releaseMode == ConnectionReleaseMode.AFTER_STATEMENT && !connections.supportsAggressiveRelease() ) {
+ log.warn( "Overriding release mode as connection provider does not support 'after_statement'" );
+ releaseMode = ConnectionReleaseMode.AFTER_TRANSACTION;
+ }
+ }
+ settings.setConnectionReleaseMode( releaseMode );
+
+ //SQL Generation settings:
+
+ String defaultSchema = properties.getProperty(Environment.DEFAULT_SCHEMA);
+ String defaultCatalog = properties.getProperty(Environment.DEFAULT_CATALOG);
+ if (defaultSchema!=null) log.info("Default schema: " + defaultSchema);
+ if (defaultCatalog!=null) log.info("Default catalog: " + defaultCatalog);
+ settings.setDefaultSchemaName(defaultSchema);
+ settings.setDefaultCatalogName(defaultCatalog);
+
+ Integer maxFetchDepth = PropertiesHelper.getInteger(Environment.MAX_FETCH_DEPTH, properties);
+ if (maxFetchDepth!=null) log.info("Maximum outer join fetch depth: " + maxFetchDepth);
+ settings.setMaximumFetchDepth(maxFetchDepth);
+ int batchFetchSize = PropertiesHelper.getInt(Environment.DEFAULT_BATCH_FETCH_SIZE, properties, 1);
+ log.info("Default batch fetch size: " + batchFetchSize);
+ settings.setDefaultBatchFetchSize(batchFetchSize);
+
+ boolean comments = PropertiesHelper.getBoolean(Environment.USE_SQL_COMMENTS, properties);
+ log.info( "Generate SQL with comments: " + enabledDisabled(comments) );
+ settings.setCommentsEnabled(comments);
+
+ boolean orderUpdates = PropertiesHelper.getBoolean(Environment.ORDER_UPDATES, properties);
+ log.info( "Order SQL updates by primary key: " + enabledDisabled(orderUpdates) );
+ settings.setOrderUpdatesEnabled(orderUpdates);
+
+ boolean orderInserts = PropertiesHelper.getBoolean(Environment.ORDER_INSERTS, properties);
+ log.info( "Order SQL inserts for batching: " + enabledDisabled( orderInserts ) );
+ settings.setOrderInsertsEnabled( orderInserts );
+
+ //Query parser settings:
+
+ settings.setQueryTranslatorFactory( createQueryTranslatorFactory(properties) );
+
+ Map querySubstitutions = PropertiesHelper.toMap(Environment.QUERY_SUBSTITUTIONS, " ,=;:\n\t\r\f", properties);
+ log.info("Query language substitutions: " + querySubstitutions);
+ settings.setQuerySubstitutions(querySubstitutions);
+
+ boolean jpaqlCompliance = PropertiesHelper.getBoolean( Environment.JPAQL_STRICT_COMPLIANCE, properties, false );
+ settings.setStrictJPAQLCompliance( jpaqlCompliance );
+ log.info( "JPA-QL strict compliance: " + enabledDisabled( jpaqlCompliance ) );
+
+ // Second-level / query cache:
+
+ boolean useSecondLevelCache = PropertiesHelper.getBoolean(Environment.USE_SECOND_LEVEL_CACHE, properties, true);
+ log.info( "Second-level cache: " + enabledDisabled(useSecondLevelCache) );
+ settings.setSecondLevelCacheEnabled(useSecondLevelCache);
+
+ boolean useQueryCache = PropertiesHelper.getBoolean(Environment.USE_QUERY_CACHE, properties);
+ log.info( "Query cache: " + enabledDisabled(useQueryCache) );
+ settings.setQueryCacheEnabled(useQueryCache);
+
+ // The cache provider is needed when we either have second-level cache enabled
+ // or query cache enabled. Note that useSecondLevelCache is enabled by default
+ settings.setRegionFactory( createRegionFactory( properties, ( useSecondLevelCache || useQueryCache ) ) );
+
+ boolean useMinimalPuts = PropertiesHelper.getBoolean(
+ Environment.USE_MINIMAL_PUTS, properties, settings.getRegionFactory().isMinimalPutsEnabledByDefault()
+ );
+ log.info( "Optimize cache for minimal puts: " + enabledDisabled(useMinimalPuts) );
+ settings.setMinimalPutsEnabled(useMinimalPuts);
+
+ String prefix = properties.getProperty(Environment.CACHE_REGION_PREFIX);
+ if ( StringHelper.isEmpty(prefix) ) prefix=null;
+ if (prefix!=null) log.info("Cache region prefix: "+ prefix);
+ settings.setCacheRegionPrefix(prefix);
+
+ boolean useStructuredCacheEntries = PropertiesHelper.getBoolean(Environment.USE_STRUCTURED_CACHE, properties, false);
+ log.info( "Structured second-level cache entries: " + enabledDisabled(useStructuredCacheEntries) );
+ settings.setStructuredCacheEntriesEnabled(useStructuredCacheEntries);
+
+ if (useQueryCache) settings.setQueryCacheFactory( createQueryCacheFactory(properties) );
+
+ //SQL Exception converter:
+
+ SQLExceptionConverter sqlExceptionConverter;
+ try {
+ sqlExceptionConverter = SQLExceptionConverterFactory.buildSQLExceptionConverter( dialect, properties );
+ }
+ catch(HibernateException e) {
+ log.warn("Error building SQLExceptionConverter; using minimal converter");
+ sqlExceptionConverter = SQLExceptionConverterFactory.buildMinimalSQLExceptionConverter();
+ }
+ settings.setSQLExceptionConverter(sqlExceptionConverter);
+
+ //Statistics and logging:
+
+ boolean showSql = PropertiesHelper.getBoolean(Environment.SHOW_SQL, properties);
+ if (showSql) log.info("Echoing all SQL to stdout");
+ settings.setShowSqlEnabled(showSql);
+
+ boolean formatSql = PropertiesHelper.getBoolean(Environment.FORMAT_SQL, properties);
+ settings.setFormatSqlEnabled(formatSql);
+
+ boolean useStatistics = PropertiesHelper.getBoolean(Environment.GENERATE_STATISTICS, properties);
+ log.info( "Statistics: " + enabledDisabled(useStatistics) );
+ settings.setStatisticsEnabled(useStatistics);
+
+ boolean useIdentifierRollback = PropertiesHelper.getBoolean(Environment.USE_IDENTIFIER_ROLLBACK, properties);
+ log.info( "Deleted entity synthetic identifier rollback: " + enabledDisabled(useIdentifierRollback) );
+ settings.setIdentifierRollbackEnabled(useIdentifierRollback);
+
+ //Schema export:
+
+ String autoSchemaExport = properties.getProperty(Environment.HBM2DDL_AUTO);
+ if ( "validate".equals(autoSchemaExport) ) settings.setAutoValidateSchema(true);
+ if ( "update".equals(autoSchemaExport) ) settings.setAutoUpdateSchema(true);
+ if ( "create".equals(autoSchemaExport) ) settings.setAutoCreateSchema(true);
+ if ( "create-drop".equals(autoSchemaExport) ) {
+ settings.setAutoCreateSchema(true);
+ settings.setAutoDropSchema(true);
+ }
+
+ EntityMode defaultEntityMode = EntityMode.parse( properties.getProperty( Environment.DEFAULT_ENTITY_MODE ) );
+ log.info( "Default entity-mode: " + defaultEntityMode );
+ settings.setDefaultEntityMode( defaultEntityMode );
+
+ boolean namedQueryChecking = PropertiesHelper.getBoolean( Environment.QUERY_STARTUP_CHECKING, properties, true );
+ log.info( "Named query checking : " + enabledDisabled( namedQueryChecking ) );
+ settings.setNamedQueryStartupCheckingEnabled( namedQueryChecking );
+
+// String provider = properties.getProperty( Environment.BYTECODE_PROVIDER );
+// log.info( "Bytecode provider name : " + provider );
+// BytecodeProvider bytecodeProvider = buildBytecodeProvider( provider );
+// settings.setBytecodeProvider( bytecodeProvider );
+
+ return settings;
+
+ }
+
+ protected BytecodeProvider buildBytecodeProvider(String providerName) {
+ if ( "javassist".equals( providerName ) ) {
+ return new org.hibernate.bytecode.javassist.BytecodeProviderImpl();
+ }
+ else if ( "cglib".equals( providerName ) ) {
+ return new org.hibernate.bytecode.cglib.BytecodeProviderImpl();
+ }
+ else {
+ log.debug( "using cglib as bytecode provider by default" );
+ return new org.hibernate.bytecode.cglib.BytecodeProviderImpl();
+ }
+ }
+
+ private int getDatabaseMajorVersion(DatabaseMetaData meta) {
+ try {
+ Method gdbmvMethod = DatabaseMetaData.class.getMethod("getDatabaseMajorVersion", null);
+ return ( (Integer) gdbmvMethod.invoke(meta, null) ).intValue();
+ }
+ catch (NoSuchMethodException nsme) {
+ return 0;
+ }
+ catch (Throwable t) {
+ log.debug("could not get database version from JDBC metadata");
+ return 0;
+ }
+ }
+
+ private static String enabledDisabled(boolean value) {
+ return value ? "enabled" : "disabled";
+ }
+
+ protected QueryCacheFactory createQueryCacheFactory(Properties properties) {
+ String queryCacheFactoryClassName = PropertiesHelper.getString(
+ Environment.QUERY_CACHE_FACTORY, properties, "org.hibernate.cache.StandardQueryCacheFactory"
+ );
+ log.info("Query cache factory: " + queryCacheFactoryClassName);
+ try {
+ return (QueryCacheFactory) ReflectHelper.classForName(queryCacheFactoryClassName).newInstance();
+ }
+ catch (Exception cnfe) {
+ throw new HibernateException("could not instantiate QueryCacheFactory: " + queryCacheFactoryClassName, cnfe);
+ }
+ }
+
+ protected RegionFactory createRegionFactory(Properties properties, boolean cachingEnabled) {
+ String regionFactoryClassName = PropertiesHelper.getString( Environment.CACHE_REGION_FACTORY, properties, null );
+ if ( regionFactoryClassName == null && cachingEnabled ) {
+ String providerClassName = PropertiesHelper.getString( Environment.CACHE_PROVIDER, properties, null );
+ if ( providerClassName != null ) {
+ // legacy behavior, apply the bridge...
+ regionFactoryClassName = RegionFactoryCacheProviderBridge.class.getName();
+ }
+ }
+ if ( regionFactoryClassName == null ) {
+ regionFactoryClassName = DEF_CACHE_REG_FACTORY;
+ }
+ log.info( "Cache region factory : " + regionFactoryClassName );
+ try {
+ return ( RegionFactory ) ReflectHelper.classForName( regionFactoryClassName )
+ .getConstructor( new Class[] { Properties.class } )
+ .newInstance( new Object[] { properties } );
+ }
+ catch ( Exception e ) {
+ throw new HibernateException( "could not instantiate RegionFactory [" + regionFactoryClassName + "]", e );
+ }
+ }
+
+ protected QueryTranslatorFactory createQueryTranslatorFactory(Properties properties) {
+ String className = PropertiesHelper.getString(
+ Environment.QUERY_TRANSLATOR, properties, "org.hibernate.hql.ast.ASTQueryTranslatorFactory"
+ );
+ log.info("Query translator: " + className);
+ try {
+ return (QueryTranslatorFactory) ReflectHelper.classForName(className).newInstance();
+ }
+ catch (Exception cnfe) {
+ throw new HibernateException("could not instantiate QueryTranslatorFactory: " + className, cnfe);
+ }
+ }
+
+ protected BatcherFactory createBatcherFactory(Properties properties, int batchSize) {
+ String batcherClass = properties.getProperty(Environment.BATCH_STRATEGY);
+ if (batcherClass==null) {
+ return batchSize==0 ?
+ (BatcherFactory) new NonBatchingBatcherFactory() :
+ (BatcherFactory) new BatchingBatcherFactory();
+ }
+ else {
+ log.info("Batcher factory: " + batcherClass);
+ try {
+ return (BatcherFactory) ReflectHelper.classForName(batcherClass).newInstance();
+ }
+ catch (Exception cnfe) {
+ throw new HibernateException("could not instantiate BatcherFactory: " + batcherClass, cnfe);
+ }
+ }
+ }
+
+ protected ConnectionProvider createConnectionProvider(Properties properties) {
+ return ConnectionProviderFactory.newConnectionProvider(properties);
+ }
+
+ protected TransactionFactory createTransactionFactory(Properties properties) {
+ return TransactionFactoryFactory.buildTransactionFactory(properties);
+ }
+
+ protected TransactionManagerLookup createTransactionManagerLookup(Properties properties) {
+ return TransactionManagerLookupFactory.getTransactionManagerLookup(properties);
+ }
+
+ private Dialect determineDialect(Properties props, String databaseName, int databaseMajorVersion) {
+ return DialectFactory.buildDialect( props, databaseName, databaseMajorVersion );
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,9 @@
+<html>
+<head></head>
+<body>
+<p>
+ This package defines APIs for configuring Hibernate, and classes
+ for building the Hibernate configuration-time metamodel.
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/cfg/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/classic/Lifecycle.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/classic/Lifecycle.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/classic/Lifecycle.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,94 @@
+//$Id: Lifecycle.java 7566 2005-07-20 07:16:33Z oneovthafew $
+package org.hibernate.classic;
+
+import java.io.Serializable;
+
+import org.hibernate.CallbackException;
+import org.hibernate.Session;
+
+/**
+ * Provides callbacks from the <tt>Session</tt> to the persistent object.
+ * Persistent classes <b>may</b> implement this interface but they are not
+ * required to.<br>
+ * <br>
+ * <b>onSave:</b> called just before the object is saved<br>
+ * <b>onUpdate:</b> called just before an object is updated,
+ * ie. when <tt>Session.update()</tt> is called<br>
+ * <b>onDelete:</b> called just before an object is deleted<br>
+ * <b>onLoad:</b> called just after an object is loaded<br>
+ * <br>
+ * <tt>onLoad()</tt> may be used to initialize transient properties of the
+ * object from its persistent state. It may <b>not</b> be used to load
+ * dependent objects since the <tt>Session</tt> interface may not be
+ * invoked from inside this method.<br>
+ * <br>
+ * A further intended usage of <tt>onLoad()</tt>, <tt>onSave()</tt> and
+ * <tt>onUpdate()</tt> is to store a reference to the <tt>Session</tt>
+ * for later use.<br>
+ * <br>
+ * If <tt>onSave()</tt>, <tt>onUpdate()</tt> or <tt>onDelete()</tt> return
+ * <tt>VETO</tt>, the operation is silently vetoed. If a
+ * <tt>CallbackException</tt> is thrown, the operation is vetoed and the
+ * exception is passed back to the application.<br>
+ * <br>
+ * Note that <tt>onSave()</tt> is called after an identifier is assigned
+ * to the object, except when identity column key generation is used.
+ *
+ * @see CallbackException
+ * @author Gavin King
+ */
+public interface Lifecycle {
+
+ /**
+ * Return value to veto the action (true)
+ */
+ public static final boolean VETO = true;
+
+ /**
+ * Return value to accept the action (false)
+ */
+ public static final boolean NO_VETO = false;
+
+ /**
+ * Called when an entity is saved.
+ * @param s the session
+ * @return true to veto save
+ * @throws CallbackException
+ */
+ public boolean onSave(Session s) throws CallbackException;
+
+ /**
+ * Called when an entity is passed to <tt>Session.update()</tt>.
+ * This method is <em>not</em> called every time the object's
+ * state is persisted during a flush.
+ * @param s the session
+ * @return true to veto update
+ * @throws CallbackException
+ */
+ public boolean onUpdate(Session s) throws CallbackException;
+
+ /**
+ * Called when an entity is deleted.
+ * @param s the session
+ * @return true to veto delete
+ * @throws CallbackException
+ */
+ public boolean onDelete(Session s) throws CallbackException;
+
+ /**
+ * Called after an entity is loaded. <em>It is illegal to
+ * access the <tt>Session</tt> from inside this method.</em>
+ * However, the object may keep a reference to the session
+ * for later use.
+ *
+ * @param s the session
+ * @param id the identifier
+ */
+ public void onLoad(Session s, Serializable id);
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/classic/Session.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/classic/Session.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/classic/Session.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,352 @@
+//$Id: Session.java 9652 2006-03-17 18:59:03Z steve.ebersole at jboss.com $
+package org.hibernate.classic;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.hibernate.HibernateException;
+import org.hibernate.Query;
+import org.hibernate.type.Type;
+
+/**
+ * An extension of the <tt>Session</tt> API, including all
+ * deprecated methods from Hibernate2. This interface is
+ * provided to allow easier migration of existing applications.
+ * New code should use <tt>org.hibernate.Session</tt>.
+ * @author Gavin King
+ */
+public interface Session extends org.hibernate.Session {
+
+ /**
+ * Copy the state of the given object onto the persistent object with the same
+ * identifier. If there is no persistent instance currently associated with
+ * the session, it will be loaded. Return the persistent instance. If the
+ * given instance is unsaved or does not exist in the database, save it and
+ * return it as a newly persistent instance. Otherwise, the given instance
+ * does not become associated with the session.
+ *
+ * @deprecated use {@link org.hibernate.Session#merge(Object)}
+ *
+ * @param object a transient instance with state to be copied
+ * @return an updated persistent instance
+ */
+ public Object saveOrUpdateCopy(Object object) throws HibernateException;
+
+ /**
+ * Copy the state of the given object onto the persistent object with the
+ * given identifier. If there is no persistent instance currently associated
+ * with the session, it will be loaded. Return the persistent instance. If
+ * there is no database row with the given identifier, save the given instance
+ * and return it as a newly persistent instance. Otherwise, the given instance
+ * does not become associated with the session.
+ *
+ * @deprecated with no replacement
+ *
+ * @param object a persistent or transient instance with state to be copied
+ * @param id the identifier of the instance to copy to
+ * @return an updated persistent instance
+ */
+ public Object saveOrUpdateCopy(Object object, Serializable id) throws HibernateException;
+
+ /**
+ * Copy the state of the given object onto the persistent object with the same
+ * identifier. If there is no persistent instance currently associated with
+ * the session, it will be loaded. Return the persistent instance. If the
+ * given instance is unsaved or does not exist in the database, save it and
+ * return it as a newly persistent instance. Otherwise, the given instance
+ * does not become associated with the session.
+ *
+ * @deprecated use {@link org.hibernate.Session#merge(String, Object)}
+ *
+ * @param object a transient instance with state to be copied
+ * @return an updated persistent instance
+ */
+ public Object saveOrUpdateCopy(String entityName, Object object) throws HibernateException;
+
+ /**
+ * Copy the state of the given object onto the persistent object with the
+ * given identifier. If there is no persistent instance currently associated
+ * with the session, it will be loaded. Return the persistent instance. If
+ * there is no database row with the given identifier, save the given instance
+ * and return it as a newly persistent instance. Otherwise, the given instance
+ * does not become associated with the session.
+ *
+ * @deprecated with no replacement
+ *
+ * @param object a persistent or transient instance with state to be copied
+ * @param id the identifier of the instance to copy to
+ * @return an updated persistent instance
+ */
+ public Object saveOrUpdateCopy(String entityName, Object object, Serializable id) throws HibernateException;
+
+ /**
+ * Execute a query.
+ *
+ * @deprecated use {@link #createQuery}.{@link Query#list()}
+ *
+ * @param query a query expressed in Hibernate's query language
+ * @return a distinct list of instances (or arrays of instances)
+ * @throws HibernateException
+ */
+ public List find(String query) throws HibernateException;
+
+ /**
+ * Execute a query with bind parameters, binding a value to a "?" parameter
+ * in the query string.
+ *
+ * @deprecated use {@link #createQuery}.setXYZ.{@link Query#list()}
+ *
+ * @param query the query string
+ * @param value a value to be bound to a "?" placeholder (JDBC IN parameter).
+ * @param type the Hibernate type of the value
+ * @see org.hibernate.Hibernate for access to <tt>Type</tt> instances
+ * @return a distinct list of instances (or arrays of instances)
+ * @throws HibernateException
+ */
+ public List find(String query, Object value, Type type) throws HibernateException;
+
+ /**
+ * Execute a query with bind parameters, binding an array of values to "?"
+ * parameters in the query string.
+ *
+ * @deprecated use {@link #createQuery}.setXYZ.{@link Query#list()}
+ *
+ * @param query the query string
+ * @param values an array of values to be bound to the "?" placeholders (JDBC IN parameters).
+ * @param types an array of Hibernate types of the values
+ * @see org.hibernate.Hibernate for access to <tt>Type</tt> instances
+ * @return a distinct list of instances
+ * @throws HibernateException
+ */
+ public List find(String query, Object[] values, Type[] types) throws HibernateException;
+
+ /**
+ * Execute a query and return the results in an iterator. If the query has multiple
+ * return values, values will be returned in an array of type <tt>Object[].</tt><br>
+ * <br>
+ * Entities returned as results are initialized on demand. The first SQL query returns
+ * identifiers only. So <tt>iterate()</tt> is usually a less efficient way to retrieve
+ * objects than <tt>find()</tt>.
+ *
+ * @deprecated use {@link #createQuery}.{@link Query#iterate}
+ *
+ * @param query the query string
+ * @return an iterator
+ * @throws HibernateException
+ */
+ public Iterator iterate(String query) throws HibernateException;
+
+ /**
+ * Execute a query and return the results in an iterator. Write the given value to "?"
+ * in the query string. If the query has multiple return values, values will be returned
+ * in an array of type <tt>Object[]</tt>.<br>
+ * <br>
+ * Entities returned as results are initialized on demand. The first SQL query returns
+ * identifiers only. So <tt>iterate()</tt> is usually a less efficient way to retrieve
+ * objects than <tt>find()</tt>.
+ *
+ * @deprecated use {@link #createQuery}.setXYZ.{@link Query#iterate}
+ *
+ * @param query the query string
+ * @param value a value to be witten to a "?" placeholder in the query string
+ * @param type the hibernate type of value
+ * @return an iterator
+ * @throws HibernateException
+ */
+ public Iterator iterate(String query, Object value, Type type) throws HibernateException;
+
+ /**
+ * Execute a query and return the results in an iterator. Write the given values to "?"
+ * in the query string. If the query has multiple return values, values will be returned
+ * in an array of type <tt>Object[]</tt>.<br>
+ * <br>
+ * Entities returned as results are initialized on demand. The first SQL query returns
+ * identifiers only. So <tt>iterate()</tt> is usually a less efficient way to retrieve
+ * objects than <tt>find()</tt>.
+ *
+ * @deprecated use {@link #createQuery}.setXYZ.{@link Query#iterate}
+ *
+ * @param query the query string
+ * @param values a list of values to be written to "?" placeholders in the query
+ * @param types a list of Hibernate types of the values
+ * @return an iterator
+ * @throws HibernateException
+ */
+ public Iterator iterate(String query, Object[] values, Type[] types) throws HibernateException;
+
+ /**
+ * Apply a filter to a persistent collection. A filter is a Hibernate query that may refer to
+ * <tt>this</tt>, the collection element. Filters allow efficient access to very large lazy
+ * collections. (Executing the filter does not initialize the collection.)
+ *
+ * @deprecated use {@link #createFilter(Object, String)}.{@link Query#list}
+ *
+ * @param collection a persistent collection to filter
+ * @param filter a filter query string
+ * @return Collection the resulting collection
+ * @throws HibernateException
+ */
+ public Collection filter(Object collection, String filter) throws HibernateException;
+
+ /**
+ * Apply a filter to a persistent collection. A filter is a Hibernate query that may refer to
+ * <tt>this</tt>, the collection element.
+ *
+ * @deprecated use {@link #createFilter(Object, String)}.setXYZ.{@link Query#list}
+ *
+ * @param collection a persistent collection to filter
+ * @param filter a filter query string
+ * @param value a value to be witten to a "?" placeholder in the query string
+ * @param type the hibernate type of value
+ * @return Collection
+ * @throws HibernateException
+ */
+ public Collection filter(Object collection, String filter, Object value, Type type) throws HibernateException;
+
+ /**
+ * Apply a filter to a persistent collection.
+ *
+ * Bind the given parameters to "?" placeholders. A filter is a Hibernate query that
+ * may refer to <tt>this</tt>, the collection element.
+ *
+ * @deprecated use {@link #createFilter(Object, String)}.setXYZ.{@link Query#list}
+ *
+ * @param collection a persistent collection to filter
+ * @param filter a filter query string
+ * @param values a list of values to be written to "?" placeholders in the query
+ * @param types a list of Hibernate types of the values
+ * @return Collection
+ * @throws HibernateException
+ */
+ public Collection filter(Object collection, String filter, Object[] values, Type[] types) throws HibernateException;
+
+ /**
+ * Delete all objects returned by the query. Return the number of objects deleted.
+ * <p/>
+ * Note that this is very different from the delete-statement support added in HQL
+ * since 3.1. The functionality here is to actually peform the query and then iterate
+ * the results calling {@link #delete(Object)} individually.
+ *
+ * @deprecated consider using HQL delete statements
+ *
+ * @param query the query string
+ * @return the number of instances deleted
+ * @throws HibernateException
+ */
+ public int delete(String query) throws HibernateException;
+
+ /**
+ * Delete all objects returned by the query. Return the number of objects deleted.
+ * <p/>
+ * Note that this is very different from the delete-statement support added in HQL
+ * since 3.1. The functionality here is to actually peform the query and then iterate
+ * the results calling {@link #delete(Object)} individually.
+ *
+ * @deprecated consider using HQL delete statements
+ *
+ * @param query the query string
+ * @param value a value to be witten to a "?" placeholder in the query string.
+ * @param type the hibernate type of value.
+ * @return the number of instances deleted
+ * @throws HibernateException
+ */
+ public int delete(String query, Object value, Type type) throws HibernateException;
+
+ /**
+ * Delete all objects returned by the query. Return the number of objects deleted.
+ * <p/>
+ * Note that this is very different from the delete-statement support added in HQL
+ * since 3.1. The functionality here is to actually peform the query and then iterate
+ * the results calling {@link #delete(Object)} individually.
+ *
+ * @deprecated consider using HQL delete statements
+ *
+ * @param query the query string
+ * @param values a list of values to be written to "?" placeholders in the query.
+ * @param types a list of Hibernate types of the values
+ * @return the number of instances deleted
+ * @throws HibernateException
+ */
+ public int delete(String query, Object[] values, Type[] types) throws HibernateException;
+
+
+ /**
+ * Create a new instance of <tt>Query</tt> for the given SQL string.
+ *
+ * @deprecated will be replaced with a more Query like interface in later release
+ *
+ * @param sql a query expressed in SQL
+ * @param returnAlias a table alias that appears inside <tt>{}</tt> in the SQL string
+ * @param returnClass the returned persistent class
+ */
+ public Query createSQLQuery(String sql, String returnAlias, Class returnClass);
+
+ /**
+ * Create a new instance of <tt>Query</tt> for the given SQL string.
+ *
+ * @deprecated will be replaced with a more Query like interface in later release
+ *
+ * @param sql a query expressed in SQL
+ * @param returnAliases an array of table aliases that appear inside <tt>{}</tt> in the SQL string
+ * @param returnClasses the returned persistent classes
+ */
+ public Query createSQLQuery(String sql, String[] returnAliases, Class[] returnClasses);
+
+
+ /**
+ * Persist the given transient instance, using the given identifier. This operation
+ * cascades to associated instances if the association is mapped with
+ * <tt>cascade="save-update"</tt>.
+ *
+ * @deprecated declare identifier properties for all classes
+ *
+ * @param object a transient instance of a persistent class
+ * @param id an unused valid identifier
+ * @throws HibernateException
+ */
+ public void save(Object object, Serializable id) throws HibernateException;
+
+ /**
+ * Persist the given transient instance, using the given identifier. This operation
+ * cascades to associated instances if the association is mapped with
+ * <tt>cascade="save-update"</tt>.
+ *
+ * @deprecated declare identifier properties for all classes
+ *
+ * @param object a transient instance of a persistent class
+ * @param id an unused valid identifier
+ * @throws HibernateException
+ */
+ public void save(String entityName, Object object, Serializable id) throws HibernateException;
+
+ /**
+ * Update the persistent state associated with the given identifier. An exception
+ * is thrown if there is a persistent instance with the same identifier in the
+ * current session. This operation cascades to associated instances
+ * if the association is mapped with <tt>cascade="save-update"</tt>.
+ *
+ * @deprecated declare identifier properties for all classes
+ *
+ * @param object a detached instance containing updated state
+ * @param id identifier of persistent instance
+ * @throws HibernateException
+ */
+ public void update(Object object, Serializable id) throws HibernateException;
+
+ /**
+ * Update the persistent state associated with the given identifier. An exception
+ * is thrown if there is a persistent instance with the same identifier in the
+ * current session. This operation cascades to associated instances
+ * if the association is mapped with <tt>cascade="save-update"</tt>.
+ *
+ * @deprecated declare identifier properties for all classes
+ *
+ * @param object a detached instance containing updated state
+ * @param id identifier of persistent instance
+ * @throws HibernateException
+ */
+ public void update(String entityName, Object object, Serializable id) throws HibernateException;
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/classic/Session.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/classic/Validatable.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/classic/Validatable.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/classic/Validatable.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,26 @@
+//$Id: Validatable.java 4112 2004-07-28 03:33:35Z oneovthafew $
+package org.hibernate.classic;
+
+
+/**
+ * Implemented by persistent classes with invariants that must
+ * be checked before inserting into or updating the database.
+ *
+ * @author Gavin King
+ */
+public interface Validatable {
+ /**
+ * Validate the state of the object before persisting it.
+ * If a violation occurs, throw a <tt>ValidationFailure</tt>.
+ * This method must not change the state of the object by
+ * side-effect.
+ * @throws ValidationFailure if an invariant is violated
+ */
+ public void validate() throws ValidationFailure;
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/classic/ValidationFailure.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/classic/ValidationFailure.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/classic/ValidationFailure.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,33 @@
+//$Id: ValidationFailure.java 4112 2004-07-28 03:33:35Z oneovthafew $
+package org.hibernate.classic;
+
+import org.hibernate.HibernateException;
+
+/**
+ * Thrown from <tt>Validatable.validate()</tt> when an invariant
+ * was violated. Some applications might subclass this exception
+ * in order to provide more information about the violation.
+ *
+ * @author Gavin King
+ */
+public class ValidationFailure extends HibernateException {
+
+ public ValidationFailure(String message) {
+ super(message);
+ }
+
+ public ValidationFailure(String message, Exception e) {
+ super(message, e);
+ }
+
+ public ValidationFailure(Exception e) {
+ super("A validation failure occurred", e);
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/classic/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/classic/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/classic/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,9 @@
+<html>
+<head></head>
+<body>
+<p>
+ This package implements backward-compatibility with Hibernate 2.1
+ APIs now deprecated in Hibernate3.
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/classic/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/collection/AbstractPersistentCollection.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/collection/AbstractPersistentCollection.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/collection/AbstractPersistentCollection.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,943 @@
+//$Id: AbstractPersistentCollection.java 11302 2007-03-19 20:44:11Z steve.ebersole at jboss.com $
+package org.hibernate.collection;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+import org.hibernate.AssertionFailure;
+import org.hibernate.HibernateException;
+import org.hibernate.LazyInitializationException;
+import org.hibernate.engine.CollectionEntry;
+import org.hibernate.engine.ForeignKeys;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.TypedValue;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.pretty.MessageHelper;
+import org.hibernate.type.Type;
+import org.hibernate.util.CollectionHelper;
+import org.hibernate.util.EmptyIterator;
+import org.hibernate.util.MarkerObject;
+
+/**
+ * Base class implementing <tt>PersistentCollection</tt>
+ * @see PersistentCollection
+ * @author Gavin King
+ */
+public abstract class AbstractPersistentCollection
+ implements Serializable, PersistentCollection {
+
+ private transient SessionImplementor session;
+ private boolean initialized;
+ private transient List operationQueue;
+ private transient boolean directlyAccessible;
+ private transient boolean initializing;
+ private Object owner;
+ private int cachedSize = -1;
+
+ private String role;
+ private Serializable key;
+ // collections detect changes made via their public interface and mark
+ // themselves as dirty as a performance optimization
+ private boolean dirty;
+ private Serializable storedSnapshot;
+
+ public final String getRole() {
+ return role;
+ }
+
+ public final Serializable getKey() {
+ return key;
+ }
+
+ public final boolean isUnreferenced() {
+ return role==null;
+ }
+
+ public final boolean isDirty() {
+ return dirty;
+ }
+
+ public final void clearDirty() {
+ dirty = false;
+ }
+
+ public final void dirty() {
+ dirty = true;
+ }
+
+ public final Serializable getStoredSnapshot() {
+ return storedSnapshot;
+ }
+
+ //Careful: these methods do not initialize the collection.
+ /**
+ * Is the initialized collection empty?
+ */
+ public abstract boolean empty();
+ /**
+ * Called by any read-only method of the collection interface
+ */
+ protected final void read() {
+ initialize(false);
+ }
+ /**
+ * Called by the <tt>size()</tt> method
+ */
+ protected boolean readSize() {
+ if (!initialized) {
+ if ( cachedSize!=-1 && !hasQueuedOperations() ) {
+ return true;
+ }
+ else {
+ throwLazyInitializationExceptionIfNotConnected();
+ CollectionEntry entry = session.getPersistenceContext().getCollectionEntry(this);
+ CollectionPersister persister = entry.getLoadedPersister();
+ if ( persister.isExtraLazy() ) {
+ if ( hasQueuedOperations() ) {
+ session.flush();
+ }
+ cachedSize = persister.getSize( entry.getLoadedKey(), session );
+ return true;
+ }
+ }
+ }
+ read();
+ return false;
+ }
+
+ protected Boolean readIndexExistence(Object index) {
+ if (!initialized) {
+ throwLazyInitializationExceptionIfNotConnected();
+ CollectionEntry entry = session.getPersistenceContext().getCollectionEntry(this);
+ CollectionPersister persister = entry.getLoadedPersister();
+ if ( persister.isExtraLazy() ) {
+ if ( hasQueuedOperations() ) {
+ session.flush();
+ }
+ return new Boolean( persister.indexExists( entry.getLoadedKey(), index, session ) );
+ }
+ }
+ read();
+ return null;
+
+ }
+
+ protected Boolean readElementExistence(Object element) {
+ if (!initialized) {
+ throwLazyInitializationExceptionIfNotConnected();
+ CollectionEntry entry = session.getPersistenceContext().getCollectionEntry(this);
+ CollectionPersister persister = entry.getLoadedPersister();
+ if ( persister.isExtraLazy() ) {
+ if ( hasQueuedOperations() ) {
+ session.flush();
+ }
+ return new Boolean( persister.elementExists( entry.getLoadedKey(), element, session ) );
+ }
+ }
+ read();
+ return null;
+
+ }
+
+ protected static final Object UNKNOWN = new MarkerObject("UNKNOWN");
+
+ protected Object readElementByIndex(Object index) {
+ if (!initialized) {
+ throwLazyInitializationExceptionIfNotConnected();
+ CollectionEntry entry = session.getPersistenceContext().getCollectionEntry(this);
+ CollectionPersister persister = entry.getLoadedPersister();
+ if ( persister.isExtraLazy() ) {
+ if ( hasQueuedOperations() ) {
+ session.flush();
+ }
+ return persister.getElementByIndex( entry.getLoadedKey(), index, session, owner );
+ }
+ }
+ read();
+ return UNKNOWN;
+
+ }
+
+ protected int getCachedSize() {
+ return cachedSize;
+ }
+
+ /**
+ * Is the collection currently connected to an open session?
+ */
+ private final boolean isConnectedToSession() {
+ return session!=null &&
+ session.isOpen() &&
+ session.getPersistenceContext().containsCollection(this);
+ }
+
+ /**
+ * Called by any writer method of the collection interface
+ */
+ protected final void write() {
+ initialize(true);
+ dirty();
+ }
+ /**
+ * Is this collection in a state that would allow us to
+ * "queue" operations?
+ */
+ protected boolean isOperationQueueEnabled() {
+ return !initialized &&
+ isConnectedToSession() &&
+ isInverseCollection();
+ }
+ /**
+ * Is this collection in a state that would allow us to
+ * "queue" puts? This is a special case, because of orphan
+ * delete.
+ */
+ protected boolean isPutQueueEnabled() {
+ return !initialized &&
+ isConnectedToSession() &&
+ isInverseOneToManyOrNoOrphanDelete();
+ }
+ /**
+ * Is this collection in a state that would allow us to
+ * "queue" clear? This is a special case, because of orphan
+ * delete.
+ */
+ protected boolean isClearQueueEnabled() {
+ return !initialized &&
+ isConnectedToSession() &&
+ isInverseCollectionNoOrphanDelete();
+ }
+
+ /**
+ * Is this the "inverse" end of a bidirectional association?
+ */
+ private boolean isInverseCollection() {
+ CollectionEntry ce = session.getPersistenceContext().getCollectionEntry(this);
+ return ce != null && ce.getLoadedPersister().isInverse();
+ }
+
+ /**
+ * Is this the "inverse" end of a bidirectional association with
+ * no orphan delete enabled?
+ */
+ private boolean isInverseCollectionNoOrphanDelete() {
+ CollectionEntry ce = session.getPersistenceContext().getCollectionEntry(this);
+ return ce != null &&
+ ce.getLoadedPersister().isInverse() &&
+ !ce.getLoadedPersister().hasOrphanDelete();
+ }
+
+ /**
+ * Is this the "inverse" end of a bidirectional one-to-many, or
+ * of a collection with no orphan delete?
+ */
+ private boolean isInverseOneToManyOrNoOrphanDelete() {
+ CollectionEntry ce = session.getPersistenceContext().getCollectionEntry(this);
+ return ce != null && ce.getLoadedPersister().isInverse() && (
+ ce.getLoadedPersister().isOneToMany() ||
+ !ce.getLoadedPersister().hasOrphanDelete()
+ );
+ }
+
+ /**
+ * Queue an addition
+ */
+ protected final void queueOperation(Object element) {
+ if (operationQueue==null) operationQueue = new ArrayList(10);
+ operationQueue.add(element);
+ dirty = true; //needed so that we remove this collection from the second-level cache
+ }
+
+ /**
+ * After reading all existing elements from the database,
+ * add the queued elements to the underlying collection.
+ */
+ protected final void performQueuedOperations() {
+ for ( int i=0; i<operationQueue.size(); i++ ) {
+ ( (DelayedOperation) operationQueue.get(i) ).operate();
+ }
+ }
+
+ /**
+ * After flushing, re-init snapshot state.
+ */
+ public void setSnapshot(Serializable key, String role, Serializable snapshot) {
+ this.key = key;
+ this.role = role;
+ this.storedSnapshot = snapshot;
+ }
+
+ /**
+ * After flushing, clear any "queued" additions, since the
+ * database state is now synchronized with the memory state.
+ */
+ public void postAction() {
+ operationQueue=null;
+ cachedSize = -1;
+ clearDirty();
+ }
+
+ /**
+ * Not called by Hibernate, but used by non-JDK serialization,
+ * eg. SOAP libraries.
+ */
+ public AbstractPersistentCollection() {}
+
+ protected AbstractPersistentCollection(SessionImplementor session) {
+ this.session = session;
+ }
+
+ /**
+ * return the user-visible collection (or array) instance
+ */
+ public Object getValue() {
+ return this;
+ }
+
+ /**
+ * Called just before reading any rows from the JDBC result set
+ */
+ public void beginRead() {
+ // override on some subclasses
+ initializing = true;
+ }
+
+ /**
+ * Called after reading all rows from the JDBC result set
+ */
+ public boolean endRead() {
+ //override on some subclasses
+ return afterInitialize();
+ }
+
+ public boolean afterInitialize() {
+ setInitialized();
+ //do this bit after setting initialized to true or it will recurse
+ if (operationQueue!=null) {
+ performQueuedOperations();
+ operationQueue=null;
+ cachedSize = -1;
+ return false;
+ }
+ else {
+ return true;
+ }
+ }
+
+ /**
+ * Initialize the collection, if possible, wrapping any exceptions
+ * in a runtime exception
+ * @param writing currently obsolete
+ * @throws LazyInitializationException if we cannot initialize
+ */
+ protected final void initialize(boolean writing) {
+ if (!initialized) {
+ if (initializing) {
+ throw new LazyInitializationException("illegal access to loading collection");
+ }
+ throwLazyInitializationExceptionIfNotConnected();
+ session.initializeCollection(this, writing);
+ }
+ }
+
+ private void throwLazyInitializationExceptionIfNotConnected() {
+ if ( !isConnectedToSession() ) {
+ throwLazyInitializationException("no session or session was closed");
+ }
+ if ( !session.isConnected() ) {
+ throwLazyInitializationException("session is disconnected");
+ }
+ }
+
+ private void throwLazyInitializationException(String message) {
+ throw new LazyInitializationException(
+ "failed to lazily initialize a collection" +
+ ( role==null ? "" : " of role: " + role ) +
+ ", " + message
+ );
+ }
+
+ protected final void setInitialized() {
+ this.initializing = false;
+ this.initialized = true;
+ }
+
+ protected final void setDirectlyAccessible(boolean directlyAccessible) {
+ this.directlyAccessible = directlyAccessible;
+ }
+
+ /**
+ * Could the application possibly have a direct reference to
+ * the underlying collection implementation?
+ */
+ public boolean isDirectlyAccessible() {
+ return directlyAccessible;
+ }
+
+ /**
+ * Disassociate this collection from the given session.
+ * @return true if this was currently associated with the given session
+ */
+ public final boolean unsetSession(SessionImplementor currentSession) {
+ if (currentSession==this.session) {
+ this.session=null;
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ /**
+ * Associate the collection with the given session.
+ * @return false if the collection was already associated with the session
+ * @throws HibernateException if the collection was already associated
+ * with another open session
+ */
+ public final boolean setCurrentSession(SessionImplementor session) throws HibernateException {
+ if (session==this.session) {
+ return false;
+ }
+ else {
+ if ( isConnectedToSession() ) {
+ CollectionEntry ce = session.getPersistenceContext().getCollectionEntry(this);
+ if (ce==null) {
+ throw new HibernateException(
+ "Illegal attempt to associate a collection with two open sessions"
+ );
+ }
+ else {
+ throw new HibernateException(
+ "Illegal attempt to associate a collection with two open sessions: " +
+ MessageHelper.collectionInfoString(
+ ce.getLoadedPersister(),
+ ce.getLoadedKey(),
+ session.getFactory()
+ )
+ );
+ }
+ }
+ else {
+ this.session = session;
+ return true;
+ }
+ }
+ }
+
+ /**
+ * Do we need to completely recreate this collection when it changes?
+ */
+ public boolean needsRecreate(CollectionPersister persister) {
+ return false;
+ }
+
+ /**
+ * To be called internally by the session, forcing
+ * immediate initialization.
+ */
+ public final void forceInitialization() throws HibernateException {
+ if (!initialized) {
+ if (initializing) {
+ throw new AssertionFailure("force initialize loading collection");
+ }
+ if (session==null) {
+ throw new HibernateException("collection is not associated with any session");
+ }
+ if ( !session.isConnected() ) {
+ throw new HibernateException("disconnected session");
+ }
+ session.initializeCollection(this, false);
+ }
+ }
+
+
+ /**
+ * Get the current snapshot from the session
+ */
+ protected final Serializable getSnapshot() {
+ return session.getPersistenceContext().getSnapshot(this);
+ }
+
+ /**
+ * Is this instance initialized?
+ */
+ public final boolean wasInitialized() {
+ return initialized;
+ }
+
+ public boolean isRowUpdatePossible() {
+ return true;
+ }
+
+ /**
+ * Does this instance have any "queued" additions?
+ */
+ public final boolean hasQueuedOperations() {
+ return operationQueue!=null;
+ }
+ /**
+ * Iterate the "queued" additions
+ */
+ public final Iterator queuedAdditionIterator() {
+ if ( hasQueuedOperations() ) {
+ return new Iterator() {
+ int i = 0;
+ public Object next() {
+ return ( (DelayedOperation) operationQueue.get(i++) ).getAddedInstance();
+ }
+ public boolean hasNext() {
+ return i<operationQueue.size();
+ }
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+ else {
+ return EmptyIterator.INSTANCE;
+ }
+ }
+ /**
+ * Iterate the "queued" additions
+ */
+ public final Collection getQueuedOrphans(String entityName) {
+ if ( hasQueuedOperations() ) {
+ Collection additions = new ArrayList( operationQueue.size() );
+ Collection removals = new ArrayList( operationQueue.size() );
+ for ( int i = 0; i < operationQueue.size(); i++ ) {
+ DelayedOperation op = (DelayedOperation) operationQueue.get(i);
+ additions.add( op.getAddedInstance() );
+ removals.add( op.getOrphan() );
+ }
+ return getOrphans(removals, additions, entityName, session);
+ }
+ else {
+ return CollectionHelper.EMPTY_COLLECTION;
+ }
+ }
+
+ /**
+ * Called before inserting rows, to ensure that any surrogate keys
+ * are fully generated
+ */
+ public void preInsert(CollectionPersister persister) throws HibernateException {}
+ /**
+ * Called after inserting a row, to fetch the natively generated id
+ */
+ public void afterRowInsert(CollectionPersister persister, Object entry, int i) throws HibernateException {}
+ /**
+ * get all "orphaned" elements
+ */
+ public abstract Collection getOrphans(Serializable snapshot, String entityName) throws HibernateException;
+
+ /**
+ * Get the current session
+ */
+ public final SessionImplementor getSession() {
+ return session;
+ }
+
+ final class IteratorProxy implements Iterator {
+ private final Iterator iter;
+ IteratorProxy(Iterator iter) {
+ this.iter=iter;
+ }
+ public boolean hasNext() {
+ return iter.hasNext();
+ }
+
+ public Object next() {
+ return iter.next();
+ }
+
+ public void remove() {
+ write();
+ iter.remove();
+ }
+
+ }
+
+ final class ListIteratorProxy implements ListIterator {
+ private final ListIterator iter;
+ ListIteratorProxy(ListIterator iter) {
+ this.iter = iter;
+ }
+ public void add(Object o) {
+ write();
+ iter.add(o);
+ }
+
+ public boolean hasNext() {
+ return iter.hasNext();
+ }
+
+ public boolean hasPrevious() {
+ return iter.hasPrevious();
+ }
+
+ public Object next() {
+ return iter.next();
+ }
+
+ public int nextIndex() {
+ return iter.nextIndex();
+ }
+
+ public Object previous() {
+ return iter.previous();
+ }
+
+ public int previousIndex() {
+ return iter.previousIndex();
+ }
+
+ public void remove() {
+ write();
+ iter.remove();
+ }
+
+ public void set(Object o) {
+ write();
+ iter.set(o);
+ }
+
+ }
+
+ class SetProxy implements java.util.Set {
+
+ final Collection set;
+
+ SetProxy(Collection set) {
+ this.set=set;
+ }
+ public boolean add(Object o) {
+ write();
+ return set.add(o);
+ }
+
+ public boolean addAll(Collection c) {
+ write();
+ return set.addAll(c);
+ }
+
+ public void clear() {
+ write();
+ set.clear();
+ }
+
+ public boolean contains(Object o) {
+ return set.contains(o);
+ }
+
+ public boolean containsAll(Collection c) {
+ return set.containsAll(c);
+ }
+
+ public boolean isEmpty() {
+ return set.isEmpty();
+ }
+
+ public Iterator iterator() {
+ return new IteratorProxy( set.iterator() );
+ }
+
+ public boolean remove(Object o) {
+ write();
+ return set.remove(o);
+ }
+
+ public boolean removeAll(Collection c) {
+ write();
+ return set.removeAll(c);
+ }
+
+ public boolean retainAll(Collection c) {
+ write();
+ return set.retainAll(c);
+ }
+
+ public int size() {
+ return set.size();
+ }
+
+ public Object[] toArray() {
+ return set.toArray();
+ }
+
+ public Object[] toArray(Object[] array) {
+ return set.toArray(array);
+ }
+
+ }
+
+ final class ListProxy implements java.util.List {
+
+ private final java.util.List list;
+
+ ListProxy(java.util.List list) {
+ this.list = list;
+ }
+
+ public void add(int index, Object value) {
+ write();
+ list.add(index, value);
+ }
+
+ /**
+ * @see java.util.Collection#add(Object)
+ */
+ public boolean add(Object o) {
+ write();
+ return list.add(o);
+ }
+
+ /**
+ * @see java.util.Collection#addAll(Collection)
+ */
+ public boolean addAll(Collection c) {
+ write();
+ return list.addAll(c);
+ }
+
+ /**
+ * @see java.util.List#addAll(int, Collection)
+ */
+ public boolean addAll(int i, Collection c) {
+ write();
+ return list.addAll(i, c);
+ }
+
+ /**
+ * @see java.util.Collection#clear()
+ */
+ public void clear() {
+ write();
+ list.clear();
+ }
+
+ /**
+ * @see java.util.Collection#contains(Object)
+ */
+ public boolean contains(Object o) {
+ return list.contains(o);
+ }
+
+ /**
+ * @see java.util.Collection#containsAll(Collection)
+ */
+ public boolean containsAll(Collection c) {
+ return list.containsAll(c);
+ }
+
+ /**
+ * @see java.util.List#get(int)
+ */
+ public Object get(int i) {
+ return list.get(i);
+ }
+
+ /**
+ * @see java.util.List#indexOf(Object)
+ */
+ public int indexOf(Object o) {
+ return list.indexOf(o);
+ }
+
+ /**
+ * @see java.util.Collection#isEmpty()
+ */
+ public boolean isEmpty() {
+ return list.isEmpty();
+ }
+
+ /**
+ * @see java.util.Collection#iterator()
+ */
+ public Iterator iterator() {
+ return new IteratorProxy( list.iterator() );
+ }
+
+ /**
+ * @see java.util.List#lastIndexOf(Object)
+ */
+ public int lastIndexOf(Object o) {
+ return list.lastIndexOf(o);
+ }
+
+ /**
+ * @see java.util.List#listIterator()
+ */
+ public ListIterator listIterator() {
+ return new ListIteratorProxy( list.listIterator() );
+ }
+
+ /**
+ * @see java.util.List#listIterator(int)
+ */
+ public ListIterator listIterator(int i) {
+ return new ListIteratorProxy( list.listIterator(i) );
+ }
+
+ /**
+ * @see java.util.List#remove(int)
+ */
+ public Object remove(int i) {
+ write();
+ return list.remove(i);
+ }
+
+ /**
+ * @see java.util.Collection#remove(Object)
+ */
+ public boolean remove(Object o) {
+ write();
+ return list.remove(o);
+ }
+
+ /**
+ * @see java.util.Collection#removeAll(Collection)
+ */
+ public boolean removeAll(Collection c) {
+ write();
+ return list.removeAll(c);
+ }
+
+ /**
+ * @see java.util.Collection#retainAll(Collection)
+ */
+ public boolean retainAll(Collection c) {
+ write();
+ return list.retainAll(c);
+ }
+
+ /**
+ * @see java.util.List#set(int, Object)
+ */
+ public Object set(int i, Object o) {
+ write();
+ return list.set(i, o);
+ }
+
+ /**
+ * @see java.util.Collection#size()
+ */
+ public int size() {
+ return list.size();
+ }
+
+ /**
+ * @see java.util.List#subList(int, int)
+ */
+ public List subList(int i, int j) {
+ return list.subList(i, j);
+ }
+
+ /**
+ * @see java.util.Collection#toArray()
+ */
+ public Object[] toArray() {
+ return list.toArray();
+ }
+
+ /**
+ * @see java.util.Collection#toArray(Object[])
+ */
+ public Object[] toArray(Object[] array) {
+ return list.toArray(array);
+ }
+
+ }
+
+
+ protected interface DelayedOperation {
+ public void operate();
+ public Object getAddedInstance();
+ public Object getOrphan();
+ }
+
+ /**
+ * Given a collection of entity instances that used to
+ * belong to the collection, and a collection of instances
+ * that currently belong, return a collection of orphans
+ */
+ protected static Collection getOrphans(
+ Collection oldElements,
+ Collection currentElements,
+ String entityName,
+ SessionImplementor session)
+ throws HibernateException {
+
+ // short-circuit(s)
+ if ( currentElements.size()==0 ) return oldElements; // no new elements, the old list contains only Orphans
+ if ( oldElements.size()==0) return oldElements; // no old elements, so no Orphans neither
+
+ Type idType = session.getFactory().getEntityPersister(entityName).getIdentifierType();
+
+ // create the collection holding the Orphans
+ Collection res = new ArrayList();
+
+ // collect EntityIdentifier(s) of the *current* elements - add them into a HashSet for fast access
+ java.util.Set currentIds = new HashSet();
+ for ( Iterator it=currentElements.iterator(); it.hasNext(); ) {
+ Object current = it.next();
+ if ( current!=null && ForeignKeys.isNotTransient(entityName, current, null, session) ) {
+ Serializable currentId = ForeignKeys.getEntityIdentifierIfNotUnsaved(entityName, current, session);
+ currentIds.add( new TypedValue( idType, currentId, session.getEntityMode() ) );
+ }
+ }
+
+ // iterate over the *old* list
+ for ( Iterator it=oldElements.iterator(); it.hasNext(); ) {
+ Object old = it.next();
+ Serializable oldId = ForeignKeys.getEntityIdentifierIfNotUnsaved(entityName, old, session);
+ if ( !currentIds.contains( new TypedValue( idType, oldId, session.getEntityMode() ) ) ) {
+ res.add(old);
+ }
+ }
+
+ return res;
+ }
+
+ static void identityRemove(
+ Collection list,
+ Object object,
+ String entityName,
+ SessionImplementor session)
+ throws HibernateException {
+
+ if ( object!=null && ForeignKeys.isNotTransient(entityName, object, null, session) ) {
+
+ Type idType = session.getFactory().getEntityPersister(entityName).getIdentifierType();
+
+ Serializable idOfCurrent = ForeignKeys.getEntityIdentifierIfNotUnsaved(entityName, object, session);
+ Iterator iter = list.iterator();
+ while ( iter.hasNext() ) {
+ Serializable idOfOld = ForeignKeys.getEntityIdentifierIfNotUnsaved(entityName, iter.next(), session);
+ if ( idType.isEqual( idOfCurrent, idOfOld, session.getEntityMode(), session.getFactory() ) ) {
+ iter.remove();
+ break;
+ }
+ }
+
+ }
+ }
+
+ public Object getIdentifier(Object entry, int i) {
+ throw new UnsupportedOperationException();
+ }
+
+ public Object getOwner() {
+ return owner;
+ }
+
+ public void setOwner(Object owner) {
+ this.owner = owner;
+ }
+
+}
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentArrayHolder.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentArrayHolder.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentArrayHolder.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,229 @@
+//$Id: PersistentArrayHolder.java 10086 2006-07-05 18:17:27Z steve.ebersole at jboss.com $
+package org.hibernate.collection;
+
+import java.io.Serializable;
+import java.lang.reflect.Array;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.loader.CollectionAliases;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.type.Type;
+
+/**
+ * A persistent wrapper for an array. Lazy initialization
+ * is NOT supported. Use of Hibernate arrays is not really
+ * recommended.
+ *
+ * @author Gavin King
+ */
+public class PersistentArrayHolder extends AbstractPersistentCollection {
+ protected Object array;
+
+ private static final Log log = LogFactory.getLog(PersistentArrayHolder.class);
+
+ //just to help out during the load (ugly, i know)
+ private transient Class elementClass;
+ private transient java.util.List tempList;
+
+ public PersistentArrayHolder(SessionImplementor session, Object array) {
+ super(session);
+ this.array = array;
+ setInitialized();
+ }
+
+ public Serializable getSnapshot(CollectionPersister persister) throws HibernateException {
+ EntityMode entityMode = getSession().getEntityMode();
+ int length = /*(array==null) ? tempList.size() :*/ Array.getLength(array);
+ Serializable result = (Serializable) Array.newInstance( persister.getElementClass(), length );
+ for ( int i=0; i<length; i++ ) {
+ Object elt = /*(array==null) ? tempList.get(i) :*/ Array.get(array, i);
+ try {
+ Array.set( result, i, persister.getElementType().deepCopy(elt, entityMode, persister.getFactory()) );
+ }
+ catch (IllegalArgumentException iae) {
+ log.error("Array element type error", iae);
+ throw new HibernateException( "Array element type error", iae );
+ }
+ }
+ return result;
+ }
+
+ public boolean isSnapshotEmpty(Serializable snapshot) {
+ return Array.getLength( snapshot ) == 0;
+ }
+
+ public Collection getOrphans(Serializable snapshot, String entityName) throws HibernateException {
+ Object[] sn = (Object[]) snapshot;
+ Object[] arr = (Object[]) array;
+ ArrayList result = new ArrayList();
+ for (int i=0; i<sn.length; i++) result.add( sn[i] );
+ for (int i=0; i<sn.length; i++) identityRemove( result, arr[i], entityName, getSession() );
+ return result;
+ }
+
+ public PersistentArrayHolder(SessionImplementor session, CollectionPersister persister) throws HibernateException {
+ super(session);
+ elementClass = persister.getElementClass();
+ }
+
+ public Object getArray() {
+ return array;
+ }
+
+ public boolean isWrapper(Object collection) {
+ return array==collection;
+ }
+
+ public boolean equalsSnapshot(CollectionPersister persister) throws HibernateException {
+ Type elementType = persister.getElementType();
+ Serializable snapshot = getSnapshot();
+ int xlen = Array.getLength(snapshot);
+ if ( xlen!= Array.getLength(array) ) return false;
+ for ( int i=0; i<xlen; i++) {
+ if ( elementType.isDirty( Array.get(snapshot, i), Array.get(array, i), getSession() ) ) return false;
+ }
+ return true;
+ }
+
+ public Iterator elements() {
+ //if (array==null) return tempList.iterator();
+ int length = Array.getLength(array);
+ java.util.List list = new ArrayList(length);
+ for (int i=0; i<length; i++) {
+ list.add( Array.get(array, i) );
+ }
+ return list.iterator();
+ }
+ public boolean empty() {
+ return false;
+ }
+
+ public Object readFrom(ResultSet rs, CollectionPersister persister, CollectionAliases descriptor, Object owner)
+ throws HibernateException, SQLException {
+
+ Object element = persister.readElement( rs, owner, descriptor.getSuffixedElementAliases(), getSession() );
+ int index = ( (Integer) persister.readIndex( rs, descriptor.getSuffixedIndexAliases(), getSession() ) ).intValue();
+ for ( int i = tempList.size(); i<=index; i++) {
+ tempList.add(i, null);
+ }
+ tempList.set(index, element);
+ return element;
+ }
+
+ public Iterator entries(CollectionPersister persister) {
+ return elements();
+ }
+
+ public void beginRead() {
+ super.beginRead();
+ tempList = new ArrayList();
+ }
+ public boolean endRead() {
+ setInitialized();
+ array = Array.newInstance( elementClass, tempList.size() );
+ for ( int i=0; i<tempList.size(); i++) {
+ Array.set(array, i, tempList.get(i) );
+ }
+ tempList=null;
+ return true;
+ }
+
+ public void beforeInitialize(CollectionPersister persister, int anticipatedSize) {
+ //if (tempList==null) throw new UnsupportedOperationException("Can't lazily initialize arrays");
+ }
+
+ public boolean isDirectlyAccessible() {
+ return true;
+ }
+
+ public void initializeFromCache(CollectionPersister persister, Serializable disassembled, Object owner)
+ throws HibernateException {
+ Serializable[] cached = (Serializable[]) disassembled;
+
+ array = Array.newInstance( persister.getElementClass(), cached.length );
+
+ for ( int i=0; i<cached.length; i++ ) {
+ Array.set( array, i, persister.getElementType().assemble( cached[i], getSession(), owner ) );
+ }
+ }
+
+ public Serializable disassemble(CollectionPersister persister) throws HibernateException {
+ int length = Array.getLength(array);
+ Serializable[] result = new Serializable[length];
+ for ( int i=0; i<length; i++ ) {
+ result[i] = persister.getElementType().disassemble( Array.get(array,i), getSession(), null );
+ }
+
+ /*int length = tempList.size();
+ Serializable[] result = new Serializable[length];
+ for ( int i=0; i<length; i++ ) {
+ result[i] = persister.getElementType().disassemble( tempList.get(i), session );
+ }*/
+
+ return result;
+
+ }
+
+ public Object getValue() {
+ return array;
+ }
+
+ public Iterator getDeletes(CollectionPersister persister, boolean indexIsFormula) throws HibernateException {
+ java.util.List deletes = new ArrayList();
+ Serializable sn = getSnapshot();
+ int snSize = Array.getLength(sn);
+ int arraySize = Array.getLength(array);
+ int end;
+ if ( snSize > arraySize ) {
+ for ( int i=arraySize; i<snSize; i++ ) deletes.add( new Integer(i) );
+ end = arraySize;
+ }
+ else {
+ end = snSize;
+ }
+ for ( int i=0; i<end; i++ ) {
+ if ( Array.get(array, i)==null && Array.get(sn, i)!=null ) deletes.add( new Integer(i) );
+ }
+ return deletes.iterator();
+ }
+
+ public boolean needsInserting(Object entry, int i, Type elemType) throws HibernateException {
+ Serializable sn = getSnapshot();
+ return Array.get(array, i)!=null && ( i >= Array.getLength(sn) || Array.get(sn, i)==null );
+ }
+
+ public boolean needsUpdating(Object entry, int i, Type elemType) throws HibernateException {
+ Serializable sn = getSnapshot();
+ return i<Array.getLength(sn) &&
+ Array.get(sn, i)!=null &&
+ Array.get(array, i)!=null &&
+ elemType.isDirty( Array.get(array, i), Array.get(sn, i), getSession() );
+ }
+
+ public Object getIndex(Object entry, int i, CollectionPersister persister) {
+ return new Integer(i);
+ }
+
+ public Object getElement(Object entry) {
+ return entry;
+ }
+
+ public Object getSnapshotElement(Object entry, int i) {
+ Serializable sn = getSnapshot();
+ return Array.get(sn, i);
+ }
+
+ public boolean entryExists(Object entry, int i) {
+ return entry!=null;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentBag.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentBag.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentBag.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,548 @@
+//$Id: PersistentBag.java 10738 2006-11-06 21:56:38Z steve.ebersole at jboss.com $
+package org.hibernate.collection;
+
+import java.io.Serializable;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.loader.CollectionAliases;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.type.Type;
+
+/**
+ * An unordered, unkeyed collection that can contain the same element
+ * multiple times. The Java collections API, curiously, has no <tt>Bag</tt>.
+ * Most developers seem to use <tt>List</tt>s to represent bag semantics,
+ * so Hibernate follows this practice.
+ *
+ * @author Gavin King
+ */
+public class PersistentBag extends AbstractPersistentCollection implements List {
+
+ protected List bag;
+
+ public PersistentBag(SessionImplementor session) {
+ super(session);
+ }
+
+ public PersistentBag(SessionImplementor session, Collection coll) {
+ super(session);
+ if (coll instanceof List) {
+ bag = (List) coll;
+ }
+ else {
+ bag = new ArrayList();
+ Iterator iter = coll.iterator();
+ while ( iter.hasNext() ) {
+ bag.add( iter.next() );
+ }
+ }
+ setInitialized();
+ setDirectlyAccessible(true);
+ }
+
+ public PersistentBag() {} //needed for SOAP libraries, etc
+
+ public boolean isWrapper(Object collection) {
+ return bag==collection;
+ }
+ public boolean empty() {
+ return bag.isEmpty();
+ }
+
+ public Iterator entries(CollectionPersister persister) {
+ return bag.iterator();
+ }
+
+ public Object readFrom(ResultSet rs, CollectionPersister persister, CollectionAliases descriptor, Object owner)
+ throws HibernateException, SQLException {
+ // note that if we load this collection from a cartesian product
+ // the multiplicity would be broken ... so use an idbag instead
+ Object element = persister.readElement( rs, owner, descriptor.getSuffixedElementAliases(), getSession() ) ;
+ if (element!=null) bag.add(element);
+ return element;
+ }
+
+ public void beforeInitialize(CollectionPersister persister, int anticipatedSize) {
+ this.bag = ( List ) persister.getCollectionType().instantiate( anticipatedSize );
+ }
+
+ public boolean equalsSnapshot(CollectionPersister persister) throws HibernateException {
+ Type elementType = persister.getElementType();
+ EntityMode entityMode = getSession().getEntityMode();
+ List sn = (List) getSnapshot();
+ if ( sn.size()!=bag.size() ) return false;
+ Iterator iter = bag.iterator();
+ while ( iter.hasNext() ) {
+ Object elt = iter.next();
+ final boolean unequal = countOccurrences(elt, bag, elementType, entityMode) !=
+ countOccurrences(elt, sn, elementType, entityMode);
+ if ( unequal ) return false;
+ }
+ return true;
+ }
+
+ public boolean isSnapshotEmpty(Serializable snapshot) {
+ return ( (Collection) snapshot ).isEmpty();
+ }
+
+ private int countOccurrences(Object element, List list, Type elementType, EntityMode entityMode)
+ throws HibernateException {
+ Iterator iter = list.iterator();
+ int result=0;
+ while ( iter.hasNext() ) {
+ if ( elementType.isSame( element, iter.next(), entityMode ) ) result++;
+ }
+ return result;
+ }
+
+ public Serializable getSnapshot(CollectionPersister persister)
+ throws HibernateException {
+ EntityMode entityMode = getSession().getEntityMode();
+ ArrayList clonedList = new ArrayList( bag.size() );
+ Iterator iter = bag.iterator();
+ while ( iter.hasNext() ) {
+ clonedList.add( persister.getElementType().deepCopy( iter.next(), entityMode, persister.getFactory() ) );
+ }
+ return clonedList;
+ }
+
+ public Collection getOrphans(Serializable snapshot, String entityName) throws HibernateException {
+ List sn = (List) snapshot;
+ return getOrphans( sn, bag, entityName, getSession() );
+ }
+
+
+ public Serializable disassemble(CollectionPersister persister)
+ throws HibernateException {
+
+ int length = bag.size();
+ Serializable[] result = new Serializable[length];
+ for ( int i=0; i<length; i++ ) {
+ result[i] = persister.getElementType().disassemble( bag.get(i), getSession(), null );
+ }
+ return result;
+ }
+
+ public void initializeFromCache(CollectionPersister persister, Serializable disassembled, Object owner)
+ throws HibernateException {
+ Serializable[] array = (Serializable[]) disassembled;
+ int size = array.length;
+ beforeInitialize( persister, size );
+ for ( int i = 0; i < size; i++ ) {
+ Object element = persister.getElementType().assemble( array[i], getSession(), owner );
+ if ( element!=null ) {
+ bag.add( element );
+ }
+ }
+ }
+
+ public boolean needsRecreate(CollectionPersister persister) {
+ return !persister.isOneToMany();
+ }
+
+
+ // For a one-to-many, a <bag> is not really a bag;
+ // it is *really* a set, since it can't contain the
+ // same element twice. It could be considered a bug
+ // in the mapping dtd that <bag> allows <one-to-many>.
+
+ // Anyway, here we implement <set> semantics for a
+ // <one-to-many> <bag>!
+
+ public Iterator getDeletes(CollectionPersister persister, boolean indexIsFormula) throws HibernateException {
+ //if ( !persister.isOneToMany() ) throw new AssertionFailure("Not implemented for Bags");
+ Type elementType = persister.getElementType();
+ EntityMode entityMode = getSession().getEntityMode();
+ ArrayList deletes = new ArrayList();
+ List sn = (List) getSnapshot();
+ Iterator olditer = sn.iterator();
+ int i=0;
+ while ( olditer.hasNext() ) {
+ Object old = olditer.next();
+ Iterator newiter = bag.iterator();
+ boolean found = false;
+ if ( bag.size()>i && elementType.isSame( old, bag.get(i++), entityMode ) ) {
+ //a shortcut if its location didn't change!
+ found = true;
+ }
+ else {
+ //search for it
+ //note that this code is incorrect for other than one-to-many
+ while ( newiter.hasNext() ) {
+ if ( elementType.isSame( old, newiter.next(), entityMode ) ) {
+ found = true;
+ break;
+ }
+ }
+ }
+ if (!found) deletes.add(old);
+ }
+ return deletes.iterator();
+ }
+
+ public boolean needsInserting(Object entry, int i, Type elemType) throws HibernateException {
+ //if ( !persister.isOneToMany() ) throw new AssertionFailure("Not implemented for Bags");
+ List sn = (List) getSnapshot();
+ final EntityMode entityMode = getSession().getEntityMode();
+ if ( sn.size()>i && elemType.isSame( sn.get(i), entry, entityMode ) ) {
+ //a shortcut if its location didn't change!
+ return false;
+ }
+ else {
+ //search for it
+ //note that this code is incorrect for other than one-to-many
+ Iterator olditer = sn.iterator();
+ while ( olditer.hasNext() ) {
+ Object old = olditer.next();
+ if ( elemType.isSame( old, entry, entityMode ) ) return false;
+ }
+ return true;
+ }
+ }
+
+ public boolean isRowUpdatePossible() {
+ return false;
+ }
+
+ public boolean needsUpdating(Object entry, int i, Type elemType) {
+ //if ( !persister.isOneToMany() ) throw new AssertionFailure("Not implemented for Bags");
+ return false;
+ }
+
+ /**
+ * @see java.util.Collection#size()
+ */
+ public int size() {
+ return readSize() ? getCachedSize() : bag.size();
+ }
+
+ /**
+ * @see java.util.Collection#isEmpty()
+ */
+ public boolean isEmpty() {
+ return readSize() ? getCachedSize()==0 : bag.isEmpty();
+ }
+
+ /**
+ * @see java.util.Collection#contains(Object)
+ */
+ public boolean contains(Object object) {
+ Boolean exists = readElementExistence(object);
+ return exists==null ?
+ bag.contains(object) :
+ exists.booleanValue();
+ }
+
+ /**
+ * @see java.util.Collection#iterator()
+ */
+ public Iterator iterator() {
+ read();
+ return new IteratorProxy( bag.iterator() );
+ }
+
+ /**
+ * @see java.util.Collection#toArray()
+ */
+ public Object[] toArray() {
+ read();
+ return bag.toArray();
+ }
+
+ /**
+ * @see java.util.Collection#toArray(Object[])
+ */
+ public Object[] toArray(Object[] a) {
+ read();
+ return bag.toArray(a);
+ }
+
+ /**
+ * @see java.util.Collection#add(Object)
+ */
+ public boolean add(Object object) {
+ if ( !isOperationQueueEnabled() ) {
+ write();
+ return bag.add(object);
+ }
+ else {
+ queueOperation( new SimpleAdd(object) );
+ return true;
+ }
+ }
+
+ /**
+ * @see java.util.Collection#remove(Object)
+ */
+ public boolean remove(Object o) {
+ initialize( true );
+ if ( bag.remove( o ) ) {
+ dirty();
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ /**
+ * @see java.util.Collection#containsAll(Collection)
+ */
+ public boolean containsAll(Collection c) {
+ read();
+ return bag.containsAll(c);
+ }
+
+ /**
+ * @see java.util.Collection#addAll(Collection)
+ */
+ public boolean addAll(Collection values) {
+ if ( values.size()==0 ) return false;
+ if ( !isOperationQueueEnabled() ) {
+ write();
+ return bag.addAll(values);
+ }
+ else {
+ Iterator iter = values.iterator();
+ while ( iter.hasNext() ) {
+ queueOperation( new SimpleAdd( iter.next() ) );
+ }
+ return values.size()>0;
+ }
+ }
+
+ /**
+ * @see java.util.Collection#removeAll(Collection)
+ */
+ public boolean removeAll(Collection c) {
+ if ( c.size()>0 ) {
+ initialize( true );
+ if ( bag.removeAll( c ) ) {
+ dirty();
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+ else {
+ return false;
+ }
+ }
+
+ /**
+ * @see java.util.Collection#retainAll(Collection)
+ */
+ public boolean retainAll(Collection c) {
+ initialize( true );
+ if ( bag.retainAll( c ) ) {
+ dirty();
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ /**
+ * @see java.util.Collection#clear()
+ */
+ public void clear() {
+ if ( isClearQueueEnabled() ) {
+ queueOperation( new Clear() );
+ }
+ else {
+ initialize( true );
+ if ( ! bag.isEmpty() ) {
+ bag.clear();
+ dirty();
+ }
+ }
+ }
+
+ public Object getIndex(Object entry, int i, CollectionPersister persister) {
+ throw new UnsupportedOperationException("Bags don't have indexes");
+ }
+
+ public Object getElement(Object entry) {
+ return entry;
+ }
+
+ public Object getSnapshotElement(Object entry, int i) {
+ List sn = (List) getSnapshot();
+ return sn.get(i);
+ }
+
+ public int occurrences(Object o) {
+ read();
+ Iterator iter = bag.iterator();
+ int result=0;
+ while ( iter.hasNext() ) {
+ if ( o.equals( iter.next() ) ) result++;
+ }
+ return result;
+ }
+
+ // List OPERATIONS:
+
+ /**
+ * @see java.util.List#add(int, Object)
+ */
+ public void add(int i, Object o) {
+ write();
+ bag.add(i, o);
+ }
+
+ /**
+ * @see java.util.List#addAll(int, Collection)
+ */
+ public boolean addAll(int i, Collection c) {
+ if ( c.size()>0 ) {
+ write();
+ return bag.addAll(i, c);
+ }
+ else {
+ return false;
+ }
+ }
+
+ /**
+ * @see java.util.List#get(int)
+ */
+ public Object get(int i) {
+ read();
+ return bag.get(i);
+ }
+
+ /**
+ * @see java.util.List#indexOf(Object)
+ */
+ public int indexOf(Object o) {
+ read();
+ return bag.indexOf(o);
+ }
+
+ /**
+ * @see java.util.List#lastIndexOf(Object)
+ */
+ public int lastIndexOf(Object o) {
+ read();
+ return bag.lastIndexOf(o);
+ }
+
+ /**
+ * @see java.util.List#listIterator()
+ */
+ public ListIterator listIterator() {
+ read();
+ return new ListIteratorProxy( bag.listIterator() );
+ }
+
+ /**
+ * @see java.util.List#listIterator(int)
+ */
+ public ListIterator listIterator(int i) {
+ read();
+ return new ListIteratorProxy( bag.listIterator(i) );
+ }
+
+ /**
+ * @see java.util.List#remove(int)
+ */
+ public Object remove(int i) {
+ write();
+ return bag.remove(i);
+ }
+
+ /**
+ * @see java.util.List#set(int, Object)
+ */
+ public Object set(int i, Object o) {
+ write();
+ return bag.set(i, o);
+ }
+
+ /**
+ * @see java.util.List#subList(int, int)
+ */
+ public List subList(int start, int end) {
+ read();
+ return new ListProxy( bag.subList(start, end) );
+ }
+
+ public String toString() {
+ read();
+ return bag.toString();
+ }
+
+ /*public boolean equals(Object other) {
+ read();
+ return bag.equals(other);
+ }
+
+ public int hashCode(Object other) {
+ read();
+ return bag.hashCode();
+ }*/
+
+ public boolean entryExists(Object entry, int i) {
+ return entry!=null;
+ }
+
+ /**
+ * Bag does not respect the collection API and do an
+ * JVM instance comparison to do the equals.
+ * The semantic is broken not to have to initialize a
+ * collection for a simple equals() operation.
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ public boolean equals(Object obj) {
+ return super.equals(obj);
+ }
+
+ /**
+ * @see java.lang.Object#hashCode()
+ */
+ public int hashCode() {
+ return super.hashCode();
+ }
+
+ final class Clear implements DelayedOperation {
+ public void operate() {
+ bag.clear();
+ }
+ public Object getAddedInstance() {
+ return null;
+ }
+ public Object getOrphan() {
+ throw new UnsupportedOperationException("queued clear cannot be used with orphan delete");
+ }
+ }
+
+ final class SimpleAdd implements DelayedOperation {
+ private Object value;
+
+ public SimpleAdd(Object value) {
+ this.value = value;
+ }
+ public void operate() {
+ bag.add(value);
+ }
+ public Object getAddedInstance() {
+ return value;
+ }
+ public Object getOrphan() {
+ return null;
+ }
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentCollection.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentCollection.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentCollection.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,303 @@
+//$Id: PersistentCollection.java 10086 2006-07-05 18:17:27Z steve.ebersole at jboss.com $
+package org.hibernate.collection;
+
+import java.io.Serializable;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.loader.CollectionAliases;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.type.Type;
+
+/**
+ * Persistent collections are treated as value objects by Hibernate.
+ * ie. they have no independent existence beyond the object holding
+ * a reference to them. Unlike instances of entity classes, they are
+ * automatically deleted when unreferenced and automatically become
+ * persistent when held by a persistent object. Collections can be
+ * passed between different objects (change "roles") and this might
+ * cause their elements to move from one database table to another.<br>
+ * <br>
+ * Hibernate "wraps" a java collection in an instance of
+ * PersistentCollection. This mechanism is designed to support
+ * tracking of changes to the collection's persistent state and
+ * lazy instantiation of collection elements. The downside is that
+ * only certain abstract collection types are supported and any
+ * extra semantics are lost<br>
+ * <br>
+ * Applications should <em>never</em> use classes in this package
+ * directly, unless extending the "framework" here.<br>
+ * <br>
+ * Changes to <em>structure</em> of the collection are recorded by the
+ * collection calling back to the session. Changes to mutable
+ * elements (ie. composite elements) are discovered by cloning their
+ * state when the collection is initialized and comparing at flush
+ * time.
+ *
+ * @author Gavin King
+ */
+public interface PersistentCollection {
+
+ /**
+ * Get the owning entity. Note that the owner is only
+ * set during the flush cycle, and when a new collection
+ * wrapper is created while loading an entity.
+ */
+ public Object getOwner();
+ /**
+ * Set the reference to the owning entity
+ */
+ public void setOwner(Object entity);
+
+ /**
+ * Is the collection empty? (don't try to initialize the collection)
+ */
+ public boolean empty();
+
+ /**
+ * After flushing, re-init snapshot state.
+ */
+ public void setSnapshot(Serializable key, String role, Serializable snapshot);
+
+ /**
+ * After flushing, clear any "queued" additions, since the
+ * database state is now synchronized with the memory state.
+ */
+ public void postAction();
+
+ /**
+ * return the user-visible collection (or array) instance
+ */
+ public Object getValue();
+
+ /**
+ * Called just before reading any rows from the JDBC result set
+ */
+ public void beginRead();
+
+ /**
+ * Called after reading all rows from the JDBC result set
+ */
+ public boolean endRead();
+
+ /**
+ * Called after initializing from cache
+ */
+ public boolean afterInitialize();
+
+ /**
+ * Could the application possibly have a direct reference to
+ * the underlying collection implementation?
+ */
+ public boolean isDirectlyAccessible();
+
+ /**
+ * Disassociate this collection from the given session.
+ * @return true if this was currently associated with the given session
+ */
+ public boolean unsetSession(SessionImplementor currentSession);
+
+ /**
+ * Associate the collection with the given session.
+ * @return false if the collection was already associated with the session
+ * @throws HibernateException if the collection was already associated
+ * with another open session
+ */
+ public boolean setCurrentSession(SessionImplementor session)
+ throws HibernateException;
+
+ /**
+ * Read the state of the collection from a disassembled cached value
+ */
+ public void initializeFromCache(CollectionPersister persister,
+ Serializable disassembled, Object owner) throws HibernateException;
+
+ /**
+ * Iterate all collection entries, during update of the database
+ */
+ public Iterator entries(CollectionPersister persister);
+
+ /**
+ * Read a row from the JDBC result set
+ */
+ public Object readFrom(ResultSet rs, CollectionPersister role, CollectionAliases descriptor, Object owner)
+ throws HibernateException, SQLException;
+
+ /**
+ * Get the index of the given collection entry
+ */
+ public Object getIdentifier(Object entry, int i);
+
+ /**
+ * Get the index of the given collection entry
+ * @param persister it was more elegant before we added this...
+ */
+ public Object getIndex(Object entry, int i, CollectionPersister persister);
+
+ /**
+ * Get the value of the given collection entry
+ */
+ public Object getElement(Object entry);
+
+ /**
+ * Get the snapshot value of the given collection entry
+ */
+ public Object getSnapshotElement(Object entry, int i);
+
+ /**
+ * Called before any elements are read into the collection,
+ * allowing appropriate initializations to occur.
+ *
+ * @param persister The underlying collection persister.
+ * @param anticipatedSize The anticipated size of the collection after initilization is complete.
+ */
+ public void beforeInitialize(CollectionPersister persister, int anticipatedSize);
+
+ /**
+ * Does the current state exactly match the snapshot?
+ */
+ public boolean equalsSnapshot(CollectionPersister persister)
+ throws HibernateException;
+
+ /**
+ * Is the snapshot empty?
+ */
+ public boolean isSnapshotEmpty(Serializable snapshot);
+
+ /**
+ * Disassemble the collection, ready for the cache
+ */
+ public Serializable disassemble(CollectionPersister persister)
+ throws HibernateException;
+
+ /**
+ * Do we need to completely recreate this collection when it changes?
+ */
+ public boolean needsRecreate(CollectionPersister persister);
+
+ /**
+ * Return a new snapshot of the current state of the collection
+ */
+ public Serializable getSnapshot(CollectionPersister persister)
+ throws HibernateException;
+
+ /**
+ * To be called internally by the session, forcing
+ * immediate initialization.
+ */
+ public void forceInitialization() throws HibernateException;
+
+ /**
+ * Does an element exist at this entry in the collection?
+ */
+ public boolean entryExists(Object entry, int i); //note that i parameter is now unused (delete it?)
+
+ /**
+ * Do we need to insert this element?
+ */
+ public boolean needsInserting(Object entry, int i, Type elemType)
+ throws HibernateException;
+
+ /**
+ * Do we need to update this element?
+ */
+ public boolean needsUpdating(Object entry, int i, Type elemType)
+ throws HibernateException;
+
+ public boolean isRowUpdatePossible();
+
+ /**
+ * Get all the elements that need deleting
+ */
+ public Iterator getDeletes(CollectionPersister persister, boolean indexIsFormula)
+ throws HibernateException;
+
+ /**
+ * Is this the wrapper for the given underlying collection instance?
+ */
+ public boolean isWrapper(Object collection);
+
+ /**
+ * Is this instance initialized?
+ */
+ public boolean wasInitialized();
+
+ /**
+ * Does this instance have any "queued" additions?
+ */
+ public boolean hasQueuedOperations();
+
+ /**
+ * Iterate the "queued" additions
+ */
+ public Iterator queuedAdditionIterator();
+
+ /**
+ * Get the "queued" orphans
+ */
+ public Collection getQueuedOrphans(String entityName);
+
+ /**
+ * Get the current collection key value
+ */
+ public Serializable getKey();
+
+ /**
+ * Get the current role name
+ */
+ public String getRole();
+
+ /**
+ * Is the collection unreferenced?
+ */
+ public boolean isUnreferenced();
+
+ /**
+ * Is the collection dirty? Note that this is only
+ * reliable during the flush cycle, after the
+ * collection elements are dirty checked against
+ * the snapshot.
+ */
+ public boolean isDirty();
+
+ /**
+ * Clear the dirty flag, after flushing changes
+ * to the database.
+ */
+ public void clearDirty();
+
+ /**
+ * Get the snapshot cached by the collection
+ * instance
+ */
+ public Serializable getStoredSnapshot();
+
+ /**
+ * Mark the collection as dirty
+ */
+ public void dirty();
+
+ /**
+ * Called before inserting rows, to ensure that any surrogate keys
+ * are fully generated
+ */
+ public void preInsert(CollectionPersister persister)
+ throws HibernateException;
+
+ /**
+ * Called after inserting a row, to fetch the natively generated id
+ */
+ public void afterRowInsert(CollectionPersister persister, Object entry, int i)
+ throws HibernateException;
+
+ /**
+ * get all "orphaned" elements
+ */
+ public Collection getOrphans(Serializable snapshot, String entityName)
+ throws HibernateException;
+
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentElementHolder.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentElementHolder.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentElementHolder.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,209 @@
+//$Id: PersistentElementHolder.java 10086 2006-07-05 18:17:27Z steve.ebersole at jboss.com $
+package org.hibernate.collection;
+
+import java.io.Serializable;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.dom4j.Element;
+import org.hibernate.AssertionFailure;
+import org.hibernate.HibernateException;
+import org.hibernate.loader.CollectionAliases;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.type.Type;
+import org.hibernate.util.CollectionHelper;
+
+/**
+ * A persistent wrapper for an XML element
+ *
+ * @author Gavin King
+ */
+public class PersistentElementHolder extends AbstractPersistentCollection {
+ protected Element element;
+
+ public PersistentElementHolder(SessionImplementor session, Element element) {
+ super(session);
+ this.element = element;
+ setInitialized();
+ }
+
+ public Serializable getSnapshot(CollectionPersister persister)
+ throws HibernateException {
+
+ final Type elementType = persister.getElementType();
+ List elements = element.elements( persister.getElementNodeName() );
+ ArrayList snapshot = new ArrayList( elements.size() );
+ for ( int i=0; i<elements.size(); i++ ) {
+ Element elem = (Element) elements.get(i);
+ Object value = elementType.fromXMLNode( elem, persister.getFactory() );
+ Object copy = elementType.deepCopy(value , getSession().getEntityMode(), persister.getFactory() );
+ snapshot.add(copy);
+ }
+ return snapshot;
+
+ }
+
+ public Collection getOrphans(Serializable snapshot, String entityName)
+ throws HibernateException {
+ //orphan delete not supported for EntityMode.DOM4J
+ return CollectionHelper.EMPTY_COLLECTION;
+ }
+
+ public PersistentElementHolder(SessionImplementor session, CollectionPersister persister, Serializable key)
+ throws HibernateException {
+ super(session);
+ Element owner = (Element) session.getPersistenceContext().getCollectionOwner(key, persister);
+ if (owner==null) throw new AssertionFailure("null owner");
+ //element = XMLHelper.generateDom4jElement( persister.getNodeName() );
+ final String nodeName = persister.getNodeName();
+ if ( ".".equals(nodeName) ) {
+ element = owner;
+ }
+ else {
+ element = owner.element( nodeName );
+ if (element==null) element = owner.addElement( nodeName );
+ }
+ }
+
+ public boolean isWrapper(Object collection) {
+ return element==collection;
+ }
+
+ public boolean equalsSnapshot(CollectionPersister persister) throws HibernateException {
+ Type elementType = persister.getElementType();
+
+ ArrayList snapshot = (ArrayList) getSnapshot();
+ List elements = element.elements( persister.getElementNodeName() );
+ if ( snapshot.size()!= elements.size() ) return false;
+ for ( int i=0; i<snapshot.size(); i++ ) {
+ Object old = snapshot.get(i);
+ Element elem = (Element) elements.get(i);
+ Object current = elementType.fromXMLNode( elem, persister.getFactory() );
+ if ( elementType.isDirty( old, current, getSession() ) ) return false;
+ }
+ return true;
+ }
+
+ public boolean isSnapshotEmpty(Serializable snapshot) {
+ return ( (Collection) snapshot ).isEmpty();
+ }
+
+ public boolean empty() {
+ return !element.elementIterator().hasNext();
+ }
+
+ public Object readFrom(ResultSet rs, CollectionPersister persister, CollectionAliases descriptor, Object owner)
+ throws HibernateException, SQLException {
+ Object object = persister.readElement( rs, owner, descriptor.getSuffixedElementAliases(), getSession() );
+ final Type elementType = persister.getElementType();
+ Element subelement = element.addElement( persister.getElementNodeName() );
+ elementType.setToXMLNode( subelement, object, persister.getFactory() );
+ return object;
+ }
+
+ public Iterator entries(CollectionPersister persister) {
+
+ final Type elementType = persister.getElementType();
+ List elements = element.elements( persister.getElementNodeName() );
+ int length = elements.size();
+ List result = new ArrayList(length);
+ for ( int i=0; i<length; i++ ) {
+ Element elem = (Element) elements.get(i);
+ Object object = elementType.fromXMLNode( elem, persister.getFactory() );
+ result.add(object);
+ }
+ return result.iterator();
+ }
+
+ public void beforeInitialize(CollectionPersister persister, int anticipatedSize) {}
+
+ public boolean isDirectlyAccessible() {
+ return true;
+ }
+
+ public void initializeFromCache(CollectionPersister persister, Serializable disassembled, Object owner)
+ throws HibernateException {
+
+ Type elementType = persister.getElementType();
+ Serializable[] cached = (Serializable[]) disassembled;
+ for ( int i=0; i<cached.length; i++ ) {
+ Object object = elementType.assemble( cached[i], getSession(), owner );
+ Element subelement = element.addElement( persister.getElementNodeName() );
+ elementType.setToXMLNode( subelement, object, persister.getFactory() );
+ }
+
+ }
+
+ public Serializable disassemble(CollectionPersister persister) throws HibernateException {
+
+ Type elementType = persister.getElementType();
+ List elements = element.elements( persister.getElementNodeName() );
+ int length = elements.size();
+ Serializable[] result = new Serializable[length];
+ for ( int i=0; i<length; i++ ) {
+ Element elem = (Element) elements.get(i);
+ Object object = elementType.fromXMLNode( elem, persister.getFactory() );
+ result[i] = elementType.disassemble( object, getSession(), null );
+ }
+ return result;
+ }
+
+ public Object getValue() {
+ return element;
+ }
+
+ public Iterator getDeletes(CollectionPersister persister, boolean indexIsFormula)
+ throws HibernateException {
+
+ Type elementType = persister.getElementType();
+ ArrayList snapshot = (ArrayList) getSnapshot();
+ List elements = element.elements( persister.getElementNodeName() );
+ ArrayList result = new ArrayList();
+ for ( int i=0; i<snapshot.size(); i++ ) {
+ Object old = snapshot.get(i);
+ if ( i >= elements.size() ) {
+ result.add(old);
+ }
+ else {
+ Element elem = (Element) elements.get(i);
+ Object object = elementType.fromXMLNode( elem, persister.getFactory() );
+ if ( elementType.isDirty( old, object, getSession() ) ) result.add(old);
+ }
+ }
+ return result.iterator();
+
+ }
+
+ public boolean needsInserting(Object entry, int i, Type elementType)
+ throws HibernateException {
+ ArrayList snapshot = (ArrayList) getSnapshot();
+ return i>=snapshot.size() || elementType.isDirty( snapshot.get(i), entry, getSession() );
+ }
+
+ public boolean needsUpdating(Object entry, int i, Type elementType)
+ throws HibernateException {
+ return false;
+ }
+
+ public Object getIndex(Object entry, int i, CollectionPersister persister) {
+ throw new UnsupportedOperationException();
+ }
+
+ public Object getElement(Object entry) {
+ return entry;
+ }
+
+ public Object getSnapshotElement(Object entry, int i) {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean entryExists(Object entry, int i) {
+ return entry!=null;
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentElementHolder.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentIdentifierBag.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentIdentifierBag.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentIdentifierBag.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,412 @@
+//$Id: PersistentIdentifierBag.java 10738 2006-11-06 21:56:38Z steve.ebersole at jboss.com $
+package org.hibernate.collection;
+
+import java.io.Serializable;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.loader.CollectionAliases;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.type.Type;
+
+/**
+ * An <tt>IdentifierBag</tt> implements "bag" semantics more efficiently than
+ * a regular <tt>Bag</tt> by adding a synthetic identifier column to the
+ * table. This identifier is unique for all rows in the table, allowing very
+ * efficient updates and deletes. The value of the identifier is never exposed
+ * to the application.<br>
+ * <br>
+ * <tt>IdentifierBag</tt>s may not be used for a many-to-one association.
+ * Furthermore, there is no reason to use <tt>inverse="true"</tt>.
+ *
+ * @author Gavin King
+ */
+public class PersistentIdentifierBag extends AbstractPersistentCollection implements List {
+
+ protected List values; //element
+ protected Map identifiers; //index -> id
+
+ public PersistentIdentifierBag(SessionImplementor session) {
+ super(session);
+ }
+
+ public PersistentIdentifierBag() {} //needed for SOAP libraries, etc
+
+ public PersistentIdentifierBag(SessionImplementor session, Collection coll) {
+ super(session);
+ if (coll instanceof List) {
+ values = (List) coll;
+ }
+ else {
+ values = new ArrayList();
+ Iterator iter = coll.iterator();
+ while ( iter.hasNext() ) {
+ values.add( iter.next() );
+ }
+ }
+ setInitialized();
+ setDirectlyAccessible(true);
+ identifiers = new HashMap();
+ }
+
+ public void initializeFromCache(CollectionPersister persister, Serializable disassembled, Object owner)
+ throws HibernateException {
+ Serializable[] array = (Serializable[]) disassembled;
+ int size = array.length;
+ beforeInitialize( persister, size );
+ for ( int i = 0; i < size; i+=2 ) {
+ identifiers.put(
+ new Integer(i/2),
+ persister.getIdentifierType().assemble( array[i], getSession(), owner )
+ );
+ values.add( persister.getElementType().assemble( array[i+1], getSession(), owner ) );
+ }
+ }
+
+ public Object getIdentifier(Object entry, int i) {
+ return identifiers.get( new Integer(i) );
+ }
+
+ public boolean isWrapper(Object collection) {
+ return values==collection;
+ }
+
+ public boolean add(Object o) {
+ write();
+ values.add(o);
+ return true;
+ }
+
+ public void clear() {
+ initialize( true );
+ if ( ! values.isEmpty() || ! identifiers.isEmpty() ) {
+ values.clear();
+ identifiers.clear();
+ dirty();
+ }
+ }
+
+ public boolean contains(Object o) {
+ read();
+ return values.contains(o);
+ }
+
+ public boolean containsAll(Collection c) {
+ read();
+ return values.containsAll(c);
+ }
+
+ public boolean isEmpty() {
+ return readSize() ? getCachedSize()==0 : values.isEmpty();
+ }
+
+ public Iterator iterator() {
+ read();
+ return new IteratorProxy( values.iterator() );
+ }
+
+ public boolean remove(Object o) {
+ initialize( true );
+ int index = values.indexOf(o);
+ if (index>=0) {
+ beforeRemove(index);
+ values.remove(index);
+ dirty();
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ public boolean removeAll(Collection c) {
+ if ( c.size() > 0 ) {
+ boolean result = false;
+ Iterator iter = c.iterator();
+ while ( iter.hasNext() ) {
+ if ( remove( iter.next() ) ) result=true;
+ }
+ return result;
+ }
+ else {
+ return false;
+ }
+ }
+
+ public boolean retainAll(Collection c) {
+ initialize( true );
+ if ( values.retainAll( c ) ) {
+ dirty();
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ public int size() {
+ return readSize() ? getCachedSize() : values.size();
+ }
+
+ public Object[] toArray() {
+ read();
+ return values.toArray();
+ }
+
+ public Object[] toArray(Object[] a) {
+ read();
+ return values.toArray(a);
+ }
+
+ public void beforeInitialize(CollectionPersister persister, int anticipatedSize) {
+ identifiers = anticipatedSize <= 0 ? new HashMap() : new HashMap( anticipatedSize + 1 + (int)( anticipatedSize * .75f ), .75f );
+ values = anticipatedSize <= 0 ? new ArrayList() : new ArrayList( anticipatedSize );
+ }
+
+ public Serializable disassemble(CollectionPersister persister)
+ throws HibernateException {
+ Serializable[] result = new Serializable[ values.size() * 2 ];
+ int i=0;
+ for (int j=0; j< values.size(); j++) {
+ Object value = values.get(j);
+ result[i++] = persister.getIdentifierType().disassemble( identifiers.get( new Integer(j) ), getSession(), null );
+ result[i++] = persister.getElementType().disassemble( value, getSession(), null );
+ }
+ return result;
+ }
+
+ public boolean empty() {
+ return values.isEmpty();
+ }
+
+ public Iterator entries(CollectionPersister persister) {
+ return values.iterator();
+ }
+
+ public boolean entryExists(Object entry, int i) {
+ return entry!=null;
+ }
+
+ public boolean equalsSnapshot(CollectionPersister persister) throws HibernateException {
+ Type elementType = persister.getElementType();
+ Map snap = (Map) getSnapshot();
+ if ( snap.size()!= values.size() ) return false;
+ for ( int i=0; i<values.size(); i++ ) {
+ Object value = values.get(i);
+ Object id = identifiers.get( new Integer(i) );
+ if (id==null) return false;
+ Object old = snap.get(id);
+ if ( elementType.isDirty( old, value, getSession() ) ) return false;
+ }
+ return true;
+ }
+
+ public boolean isSnapshotEmpty(Serializable snapshot) {
+ return ( (Map) snapshot ).isEmpty();
+ }
+
+ public Iterator getDeletes(CollectionPersister persister, boolean indexIsFormula) throws HibernateException {
+ Map snap = (Map) getSnapshot();
+ List deletes = new ArrayList( snap.keySet() );
+ for ( int i=0; i<values.size(); i++ ) {
+ if ( values.get(i)!=null ) deletes.remove( identifiers.get( new Integer(i) ) );
+ }
+ return deletes.iterator();
+ }
+
+ public Object getIndex(Object entry, int i, CollectionPersister persister) {
+ throw new UnsupportedOperationException("Bags don't have indexes");
+ }
+
+ public Object getElement(Object entry) {
+ return entry;
+ }
+
+ public Object getSnapshotElement(Object entry, int i) {
+ Map snap = (Map) getSnapshot();
+ Object id = identifiers.get( new Integer(i) );
+ return snap.get(id);
+ }
+
+ public boolean needsInserting(Object entry, int i, Type elemType)
+ throws HibernateException {
+
+ Map snap = (Map) getSnapshot();
+ Object id = identifiers.get( new Integer(i) );
+ return entry!=null && ( id==null || snap.get(id)==null );
+ }
+
+ public boolean needsUpdating(Object entry, int i, Type elemType) throws HibernateException {
+
+ if (entry==null) return false;
+ Map snap = (Map) getSnapshot();
+ Object id = identifiers.get( new Integer(i) );
+ if (id==null) return false;
+ Object old = snap.get(id);
+ return old!=null && elemType.isDirty( old, entry, getSession() );
+ }
+
+
+ public Object readFrom(
+ ResultSet rs,
+ CollectionPersister persister,
+ CollectionAliases descriptor,
+ Object owner)
+ throws HibernateException, SQLException {
+
+ Object element = persister.readElement( rs, owner, descriptor.getSuffixedElementAliases(), getSession() );
+ Object old = identifiers.put(
+ new Integer( values.size() ),
+ persister.readIdentifier( rs, descriptor.getSuffixedIdentifierAlias(), getSession() )
+ );
+ if ( old==null ) values.add(element); //maintain correct duplication if loaded in a cartesian product
+ return element;
+ }
+
+ public Serializable getSnapshot(CollectionPersister persister)
+ throws HibernateException {
+
+ EntityMode entityMode = getSession().getEntityMode();
+
+ HashMap map = new HashMap( values.size() );
+ Iterator iter = values.iterator();
+ int i=0;
+ while ( iter.hasNext() ) {
+ Object value = iter.next();
+ map.put(
+ identifiers.get( new Integer(i++) ),
+ persister.getElementType().deepCopy(value, entityMode, persister.getFactory())
+ );
+ }
+ return map;
+ }
+
+ public Collection getOrphans(Serializable snapshot, String entityName) throws HibernateException {
+ Map sn = (Map) snapshot;
+ return getOrphans( sn.values(), values, entityName, getSession() );
+ }
+
+ public void preInsert(CollectionPersister persister) throws HibernateException {
+ Iterator iter = values.iterator();
+ int i=0;
+ while ( iter.hasNext() ) {
+ Object entry = iter.next();
+ Integer loc = new Integer(i++);
+ if ( !identifiers.containsKey(loc) ) { //TODO: native ids
+ Serializable id = persister.getIdentifierGenerator().generate( getSession(), entry );
+ identifiers.put(loc, id);
+ }
+ }
+ }
+
+ public void add(int index, Object element) {
+ write();
+ beforeAdd(index);
+ values.add(index, element);
+ }
+
+ public boolean addAll(int index, Collection c) {
+ if ( c.size() > 0 ) {
+ Iterator iter = c.iterator();
+ while ( iter.hasNext() ) {
+ add( index++, iter.next() );
+ }
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ public Object get(int index) {
+ read();
+ return values.get(index);
+ }
+
+ public int indexOf(Object o) {
+ read();
+ return values.indexOf(o);
+ }
+
+ public int lastIndexOf(Object o) {
+ read();
+ return values.lastIndexOf(o);
+ }
+
+ public ListIterator listIterator() {
+ read();
+ return new ListIteratorProxy( values.listIterator() );
+ }
+
+ public ListIterator listIterator(int index) {
+ read();
+ return new ListIteratorProxy( values.listIterator(index) );
+ }
+
+ private void beforeRemove(int index) {
+ Object removedId = identifiers.get( new Integer(index) );
+ int last = values.size()-1;
+ for ( int i=index; i<last; i++ ) {
+ Object id = identifiers.get( new Integer(i+1) );
+ if ( id==null ) {
+ identifiers.remove( new Integer(i) );
+ }
+ else {
+ identifiers.put( new Integer(i), id );
+ }
+ }
+ identifiers.put( new Integer(last), removedId );
+ }
+
+ private void beforeAdd(int index) {
+ for ( int i=index; i<values.size(); i++ ) {
+ identifiers.put( new Integer(i+1), identifiers.get( new Integer(i) ) );
+ }
+ identifiers.remove( new Integer(index) );
+ }
+
+ public Object remove(int index) {
+ write();
+ beforeRemove(index);
+ return values.remove(index);
+ }
+
+ public Object set(int index, Object element) {
+ write();
+ return values.set(index, element);
+ }
+
+ public List subList(int fromIndex, int toIndex) {
+ read();
+ return new ListProxy( values.subList(fromIndex, toIndex) );
+ }
+
+ public boolean addAll(Collection c) {
+ if ( c.size()> 0 ) {
+ write();
+ return values.addAll(c);
+ }
+ else {
+ return false;
+ }
+ }
+
+ public void afterRowInsert(
+ CollectionPersister persister,
+ Object entry,
+ int i)
+ throws HibernateException {
+ //TODO: if we are using identity columns, fetch the identifier
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentIndexedElementHolder.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentIndexedElementHolder.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentIndexedElementHolder.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,228 @@
+//$Id: PersistentIndexedElementHolder.java 10086 2006-07-05 18:17:27Z steve.ebersole at jboss.com $
+package org.hibernate.collection;
+
+import java.io.Serializable;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.dom4j.Element;
+import org.hibernate.AssertionFailure;
+import org.hibernate.HibernateException;
+import org.hibernate.loader.CollectionAliases;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.type.NullableType;
+import org.hibernate.type.Type;
+import org.hibernate.util.CollectionHelper;
+
+/**
+ * A persistent wrapper for an XML element
+ *
+ * @author Gavin King
+ */
+public abstract class PersistentIndexedElementHolder extends AbstractPersistentCollection {
+ protected Element element;
+
+ public PersistentIndexedElementHolder(SessionImplementor session, Element element) {
+ super(session);
+ this.element = element;
+ setInitialized();
+ }
+
+ public static final class IndexedValue {
+ String index;
+ Object value;
+ IndexedValue(String index, Object value) {
+ this.index = index;
+ this.value = value;
+ }
+ }
+
+ protected static String getIndex(Element element, String indexNodeName, int i) {
+ if (indexNodeName!=null) {
+ return element.attributeValue(indexNodeName);
+ }
+ else {
+ return Integer.toString(i);
+ }
+ }
+
+ protected static void setIndex(Element element, String indexNodeName, String index) {
+ if (indexNodeName!=null) element.addAttribute(indexNodeName, index);
+ }
+
+ protected static String getIndexAttributeName(CollectionPersister persister) {
+ String node = persister.getIndexNodeName();
+ return node==null ? null : node.substring(1);
+ }
+
+ public Serializable getSnapshot(CollectionPersister persister)
+ throws HibernateException {
+
+ final Type elementType = persister.getElementType();
+ String indexNode = getIndexAttributeName(persister);
+ List elements = element.elements( persister.getElementNodeName() );
+ HashMap snapshot = new HashMap( elements.size() );
+ for ( int i=0; i<elements.size(); i++ ) {
+ Element elem = (Element) elements.get(i);
+ Object value = elementType.fromXMLNode( elem, persister.getFactory() );
+ Object copy = elementType.deepCopy( value, getSession().getEntityMode(), persister.getFactory() );
+ snapshot.put( getIndex(elem, indexNode, i), copy );
+ }
+ return snapshot;
+
+ }
+
+ public Collection getOrphans(Serializable snapshot, String entityName)
+ throws HibernateException {
+ //orphan delete not supported for EntityMode.DOM4J
+ return CollectionHelper.EMPTY_COLLECTION;
+ }
+
+ public PersistentIndexedElementHolder(SessionImplementor session, CollectionPersister persister, Serializable key)
+ throws HibernateException {
+ super(session);
+ Element owner = (Element) session.getPersistenceContext().getCollectionOwner(key, persister);
+ if (owner==null) throw new AssertionFailure("null owner");
+ //element = XMLHelper.generateDom4jElement( persister.getNodeName() );
+ final String nodeName = persister.getNodeName();
+ if ( ".".equals(nodeName) ) {
+ element = owner;
+ }
+ else {
+ element = owner.element( nodeName );
+ if (element==null) element = owner.addElement( nodeName );
+ }
+ }
+
+ public boolean isWrapper(Object collection) {
+ return element==collection;
+ }
+
+ public boolean equalsSnapshot(CollectionPersister persister) throws HibernateException {
+ Type elementType = persister.getElementType();
+ String indexNode = getIndexAttributeName(persister);
+ HashMap snapshot = (HashMap) getSnapshot();
+ List elements = element.elements( persister.getElementNodeName() );
+ if ( snapshot.size()!= elements.size() ) return false;
+ for ( int i=0; i<snapshot.size(); i++ ) {
+ Element elem = (Element) elements.get(i);
+ Object old = snapshot.get( getIndex(elem, indexNode, i) );
+ Object current = elementType.fromXMLNode( elem, persister.getFactory() );
+ if ( elementType.isDirty( old, current, getSession() ) ) return false;
+ }
+ return true;
+ }
+
+ public boolean isSnapshotEmpty(Serializable snapshot) {
+ return ( (HashMap) snapshot ).isEmpty();
+ }
+
+ public boolean empty() {
+ return !element.elementIterator().hasNext();
+ }
+
+ public Object readFrom(ResultSet rs, CollectionPersister persister, CollectionAliases descriptor, Object owner)
+ throws HibernateException, SQLException {
+ Object object = persister.readElement( rs, owner, descriptor.getSuffixedElementAliases(), getSession() );
+ final Type elementType = persister.getElementType();
+ final SessionFactoryImplementor factory = persister.getFactory();
+ String indexNode = getIndexAttributeName(persister);
+
+ Element elem = element.addElement( persister.getElementNodeName() );
+ elementType.setToXMLNode( elem, object, factory );
+
+ final Type indexType = persister.getIndexType();
+ final Object indexValue = persister.readIndex( rs, descriptor.getSuffixedIndexAliases(), getSession() );
+ final String index = ( (NullableType) indexType ).toXMLString( indexValue, factory );
+ setIndex(elem, indexNode, index);
+ return object;
+ }
+
+ public Iterator entries(CollectionPersister persister) {
+
+ final Type elementType = persister.getElementType();
+ String indexNode = getIndexAttributeName(persister);
+ List elements = element.elements( persister.getElementNodeName() );
+ int length = elements.size();
+ List result = new ArrayList(length);
+ for ( int i=0; i<length; i++ ) {
+ Element elem = (Element) elements.get(i);
+ Object object = elementType.fromXMLNode( elem, persister.getFactory() );
+ result.add( new IndexedValue( getIndex(elem, indexNode, i), object ) );
+ }
+ return result.iterator();
+ }
+
+ public void beforeInitialize(CollectionPersister persister, int anticipatedSize) {}
+
+ public boolean isDirectlyAccessible() {
+ return true;
+ }
+
+ public Object getValue() {
+ return element;
+ }
+
+ public Iterator getDeletes(CollectionPersister persister, boolean indexIsFormula)
+ throws HibernateException {
+
+ final Type indexType = persister.getIndexType();
+ HashMap snapshot = (HashMap) getSnapshot();
+ HashMap deletes = (HashMap) snapshot.clone();
+ deletes.keySet().removeAll( ( (HashMap) getSnapshot(persister) ).keySet() );
+ ArrayList deleteList = new ArrayList( deletes.size() );
+ Iterator iter = deletes.entrySet().iterator();
+ while ( iter.hasNext() ) {
+ Map.Entry me = (Map.Entry) iter.next();
+ final Object object = indexIsFormula ?
+ me.getValue() :
+ ( (NullableType) indexType ).fromXMLString( (String) me.getKey(), persister.getFactory() );
+ if (object!=null) deleteList.add(object);
+ }
+
+ return deleteList.iterator();
+
+ }
+
+ public boolean needsInserting(Object entry, int i, Type elementType)
+ throws HibernateException {
+ HashMap snapshot = (HashMap) getSnapshot();
+ IndexedValue iv = (IndexedValue) entry;
+ return iv.value!=null && snapshot.get( iv.index )==null;
+ }
+
+ public boolean needsUpdating(Object entry, int i, Type elementType)
+ throws HibernateException {
+ HashMap snapshot = (HashMap) getSnapshot();
+ IndexedValue iv = (IndexedValue) entry;
+ Object old = snapshot.get( iv.index );
+ return old!=null && elementType.isDirty( old, iv.value, getSession() );
+ }
+
+ public Object getIndex(Object entry, int i, CollectionPersister persister) {
+ String index = ( (IndexedValue) entry ).index;
+ final Type indexType = persister.getIndexType();
+ return ( (NullableType) indexType ).fromXMLString( index, persister.getFactory() );
+ }
+
+ public Object getElement(Object entry) {
+ return ( (IndexedValue) entry ).value;
+ }
+
+ public Object getSnapshotElement(Object entry, int i) {
+ return ( (HashMap) getSnapshot() ).get( ( (IndexedValue) entry ).index );
+ }
+
+ public boolean entryExists(Object entry, int i) {
+ return entry!=null;
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentIndexedElementHolder.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentList.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentList.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentList.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,579 @@
+//$Id: PersistentList.java 10738 2006-11-06 21:56:38Z steve.ebersole at jboss.com $
+package org.hibernate.collection;
+
+import java.io.Serializable;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.loader.CollectionAliases;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.type.Type;
+
+/**
+ * A persistent wrapper for a <tt>java.util.List</tt>. Underlying
+ * collection is an <tt>ArrayList</tt>.
+ *
+ * @see java.util.ArrayList
+ * @author Gavin King
+ */
+public class PersistentList extends AbstractPersistentCollection implements List {
+
+ protected List list;
+
+ public Serializable getSnapshot(CollectionPersister persister) throws HibernateException {
+
+ EntityMode entityMode = getSession().getEntityMode();
+
+ ArrayList clonedList = new ArrayList( list.size() );
+ Iterator iter = list.iterator();
+ while ( iter.hasNext() ) {
+ Object deepCopy = persister.getElementType()
+ .deepCopy( iter.next(), entityMode, persister.getFactory() );
+ clonedList.add( deepCopy );
+ }
+ return clonedList;
+ }
+
+ public Collection getOrphans(Serializable snapshot, String entityName) throws HibernateException {
+ List sn = (List) snapshot;
+ return getOrphans( sn, list, entityName, getSession() );
+ }
+
+ public boolean equalsSnapshot(CollectionPersister persister) throws HibernateException {
+ Type elementType = persister.getElementType();
+ List sn = (List) getSnapshot();
+ if ( sn.size()!=this.list.size() ) return false;
+ Iterator iter = list.iterator();
+ Iterator sniter = sn.iterator();
+ while ( iter.hasNext() ) {
+ if ( elementType.isDirty( iter.next(), sniter.next(), getSession() ) ) return false;
+ }
+ return true;
+ }
+
+ public boolean isSnapshotEmpty(Serializable snapshot) {
+ return ( (Collection) snapshot ).isEmpty();
+ }
+
+ public PersistentList(SessionImplementor session) {
+ super(session);
+ }
+
+ public PersistentList(SessionImplementor session, List list) {
+ super(session);
+ this.list = list;
+ setInitialized();
+ setDirectlyAccessible(true);
+ }
+
+ public void beforeInitialize(CollectionPersister persister, int anticipatedSize) {
+ this.list = ( List ) persister.getCollectionType().instantiate( anticipatedSize );
+ }
+
+ public boolean isWrapper(Object collection) {
+ return list==collection;
+ }
+
+ public PersistentList() {} //needed for SOAP libraries, etc
+
+ /**
+ * @see java.util.List#size()
+ */
+ public int size() {
+ return readSize() ? getCachedSize() : list.size();
+ }
+
+ /**
+ * @see java.util.List#isEmpty()
+ */
+ public boolean isEmpty() {
+ return readSize() ? getCachedSize()==0 : list.isEmpty();
+ }
+
+ /**
+ * @see java.util.List#contains(Object)
+ */
+ public boolean contains(Object object) {
+ Boolean exists = readElementExistence(object);
+ return exists==null ?
+ list.contains(object) :
+ exists.booleanValue();
+ }
+
+ /**
+ * @see java.util.List#iterator()
+ */
+ public Iterator iterator() {
+ read();
+ return new IteratorProxy( list.iterator() );
+ }
+
+ /**
+ * @see java.util.List#toArray()
+ */
+ public Object[] toArray() {
+ read();
+ return list.toArray();
+ }
+
+ /**
+ * @see java.util.List#toArray(Object[])
+ */
+ public Object[] toArray(Object[] array) {
+ read();
+ return list.toArray(array);
+ }
+
+ /**
+ * @see java.util.List#add(Object)
+ */
+ public boolean add(Object object) {
+ if ( !isOperationQueueEnabled() ) {
+ write();
+ return list.add(object);
+ }
+ else {
+ queueOperation( new SimpleAdd(object) );
+ return true;
+ }
+ }
+
+ /**
+ * @see java.util.List#remove(Object)
+ */
+ public boolean remove(Object value) {
+ Boolean exists = isPutQueueEnabled() ? readElementExistence(value) : null;
+ if ( exists == null ) {
+ initialize( true );
+ if ( list.remove( value ) ) {
+ dirty();
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+ else if ( exists.booleanValue() ) {
+ queueOperation( new SimpleRemove(value) );
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ /**
+ * @see java.util.List#containsAll(Collection)
+ */
+ public boolean containsAll(Collection coll) {
+ read();
+ return list.containsAll(coll);
+ }
+
+ /**
+ * @see java.util.List#addAll(Collection)
+ */
+ public boolean addAll(Collection values) {
+ if ( values.size()==0 ) {
+ return false;
+ }
+ if ( !isOperationQueueEnabled() ) {
+ write();
+ return list.addAll(values);
+ }
+ else {
+ Iterator iter = values.iterator();
+ while ( iter.hasNext() ) {
+ queueOperation( new SimpleAdd( iter.next() ) );
+ }
+ return values.size()>0;
+ }
+ }
+
+ /**
+ * @see java.util.List#addAll(int, Collection)
+ */
+ public boolean addAll(int index, Collection coll) {
+ if ( coll.size()>0 ) {
+ write();
+ return list.addAll(index, coll);
+ }
+ else {
+ return false;
+ }
+ }
+
+ /**
+ * @see java.util.List#removeAll(Collection)
+ */
+ public boolean removeAll(Collection coll) {
+ if ( coll.size()>0 ) {
+ initialize( true );
+ if ( list.removeAll( coll ) ) {
+ dirty();
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+ else {
+ return false;
+ }
+ }
+
+ /**
+ * @see java.util.List#retainAll(Collection)
+ */
+ public boolean retainAll(Collection coll) {
+ initialize( true );
+ if ( list.retainAll( coll ) ) {
+ dirty();
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ /**
+ * @see java.util.List#clear()
+ */
+ public void clear() {
+ if ( isClearQueueEnabled() ) {
+ queueOperation( new Clear() );
+ }
+ else {
+ initialize( true );
+ if ( ! list.isEmpty() ) {
+ list.clear();
+ dirty();
+ }
+ }
+ }
+
+ /**
+ * @see java.util.List#get(int)
+ */
+ public Object get(int index) {
+ if (index<0) {
+ throw new ArrayIndexOutOfBoundsException("negative index");
+ }
+ Object result = readElementByIndex( new Integer(index) );
+ return result==UNKNOWN ? list.get(index) : result;
+ }
+
+ /**
+ * @see java.util.List#set(int, Object)
+ */
+ public Object set(int index, Object value) {
+ if (index<0) {
+ throw new ArrayIndexOutOfBoundsException("negative index");
+ }
+ Object old = isPutQueueEnabled() ? readElementByIndex( new Integer(index) ) : UNKNOWN;
+ if ( old==UNKNOWN ) {
+ write();
+ return list.set(index, value);
+ }
+ else {
+ queueOperation( new Set(index, value, old) );
+ return old;
+ }
+ }
+
+ /**
+ * @see java.util.List#add(int, Object)
+ */
+ public void add(int index, Object value) {
+ if (index<0) {
+ throw new ArrayIndexOutOfBoundsException("negative index");
+ }
+ if ( !isOperationQueueEnabled() ) {
+ write();
+ list.add(index, value);
+ }
+ else {
+ queueOperation( new Add(index, value) );
+ }
+ }
+
+ /**
+ * @see java.util.List#remove(int)
+ */
+ public Object remove(int index) {
+ if (index<0) {
+ throw new ArrayIndexOutOfBoundsException("negative index");
+ }
+ Object old = isPutQueueEnabled() ?
+ readElementByIndex( new Integer(index) ) : UNKNOWN;
+ if ( old==UNKNOWN ) {
+ write();
+ return list.remove(index);
+ }
+ else {
+ queueOperation( new Remove(index, old) );
+ return old;
+ }
+ }
+
+ /**
+ * @see java.util.List#indexOf(Object)
+ */
+ public int indexOf(Object value) {
+ read();
+ return list.indexOf(value);
+ }
+
+ /**
+ * @see java.util.List#lastIndexOf(Object)
+ */
+ public int lastIndexOf(Object value) {
+ read();
+ return list.lastIndexOf(value);
+ }
+
+ /**
+ * @see java.util.List#listIterator()
+ */
+ public ListIterator listIterator() {
+ read();
+ return new ListIteratorProxy( list.listIterator() );
+ }
+
+ /**
+ * @see java.util.List#listIterator(int)
+ */
+ public ListIterator listIterator(int index) {
+ read();
+ return new ListIteratorProxy( list.listIterator(index) );
+ }
+
+ /**
+ * @see java.util.List#subList(int, int)
+ */
+ public java.util.List subList(int from, int to) {
+ read();
+ return new ListProxy( list.subList(from, to) );
+ }
+
+ public boolean empty() {
+ return list.isEmpty();
+ }
+
+ public String toString() {
+ read();
+ return list.toString();
+ }
+
+ public Object readFrom(ResultSet rs, CollectionPersister persister, CollectionAliases descriptor, Object owner)
+ throws HibernateException, SQLException {
+ Object element = persister.readElement( rs, owner, descriptor.getSuffixedElementAliases(), getSession() ) ;
+ int index = ( (Integer) persister.readIndex( rs, descriptor.getSuffixedIndexAliases(), getSession() ) ).intValue();
+
+ //pad with nulls from the current last element up to the new index
+ for ( int i = list.size(); i<=index; i++) {
+ list.add(i, null);
+ }
+
+ list.set(index, element);
+ return element;
+ }
+
+ public Iterator entries(CollectionPersister persister) {
+ return list.iterator();
+ }
+
+ public void initializeFromCache(CollectionPersister persister, Serializable disassembled, Object owner)
+ throws HibernateException {
+ Serializable[] array = ( Serializable[] ) disassembled;
+ int size = array.length;
+ beforeInitialize( persister, size );
+ for ( int i = 0; i < size; i++ ) {
+ list.add( persister.getElementType().assemble( array[i], getSession(), owner ) );
+ }
+ }
+
+ public Serializable disassemble(CollectionPersister persister)
+ throws HibernateException {
+
+ int length = list.size();
+ Serializable[] result = new Serializable[length];
+ for ( int i=0; i<length; i++ ) {
+ result[i] = persister.getElementType().disassemble( list.get(i), getSession(), null );
+ }
+ return result;
+ }
+
+
+ public Iterator getDeletes(CollectionPersister persister, boolean indexIsFormula) throws HibernateException {
+ List deletes = new ArrayList();
+ List sn = (List) getSnapshot();
+ int end;
+ if ( sn.size() > list.size() ) {
+ for ( int i=list.size(); i<sn.size(); i++ ) {
+ deletes.add( indexIsFormula ? sn.get(i) : new Integer(i) );
+ }
+ end = list.size();
+ }
+ else {
+ end = sn.size();
+ }
+ for ( int i=0; i<end; i++ ) {
+ if ( list.get(i)==null && sn.get(i)!=null ) {
+ deletes.add( indexIsFormula ? sn.get(i) : new Integer(i) );
+ }
+ }
+ return deletes.iterator();
+ }
+
+ public boolean needsInserting(Object entry, int i, Type elemType) throws HibernateException {
+ final List sn = (List) getSnapshot();
+ return list.get(i)!=null && ( i >= sn.size() || sn.get(i)==null );
+ }
+
+ public boolean needsUpdating(Object entry, int i, Type elemType) throws HibernateException {
+ final List sn = (List) getSnapshot();
+ return i<sn.size() && sn.get(i)!=null && list.get(i)!=null &&
+ elemType.isDirty( list.get(i), sn.get(i), getSession() );
+ }
+
+ public Object getIndex(Object entry, int i, CollectionPersister persister) {
+ return new Integer(i);
+ }
+
+ public Object getElement(Object entry) {
+ return entry;
+ }
+
+ public Object getSnapshotElement(Object entry, int i) {
+ final List sn = (List) getSnapshot();
+ return sn.get(i);
+ }
+
+ public boolean equals(Object other) {
+ read();
+ return list.equals(other);
+ }
+
+ public int hashCode() {
+ read();
+ return list.hashCode();
+ }
+
+ public boolean entryExists(Object entry, int i) {
+ return entry!=null;
+ }
+
+ final class Clear implements DelayedOperation {
+ public void operate() {
+ list.clear();
+ }
+ public Object getAddedInstance() {
+ return null;
+ }
+ public Object getOrphan() {
+ throw new UnsupportedOperationException("queued clear cannot be used with orphan delete");
+ }
+ }
+
+ final class SimpleAdd implements DelayedOperation {
+ private Object value;
+
+ public SimpleAdd(Object value) {
+ this.value = value;
+ }
+ public void operate() {
+ list.add(value);
+ }
+ public Object getAddedInstance() {
+ return value;
+ }
+ public Object getOrphan() {
+ return null;
+ }
+ }
+
+ final class Add implements DelayedOperation {
+ private int index;
+ private Object value;
+
+ public Add(int index, Object value) {
+ this.index = index;
+ this.value = value;
+ }
+ public void operate() {
+ list.add(index, value);
+ }
+ public Object getAddedInstance() {
+ return value;
+ }
+ public Object getOrphan() {
+ return null;
+ }
+ }
+
+ final class Set implements DelayedOperation {
+ private int index;
+ private Object value;
+ private Object old;
+
+ public Set(int index, Object value, Object old) {
+ this.index = index;
+ this.value = value;
+ this.old = old;
+ }
+ public void operate() {
+ list.set(index, value);
+ }
+ public Object getAddedInstance() {
+ return value;
+ }
+ public Object getOrphan() {
+ return old;
+ }
+ }
+
+ final class Remove implements DelayedOperation {
+ private int index;
+ private Object old;
+
+ public Remove(int index, Object old) {
+ this.index = index;
+ this.old = old;
+ }
+ public void operate() {
+ list.remove(index);
+ }
+ public Object getAddedInstance() {
+ return null;
+ }
+ public Object getOrphan() {
+ return old;
+ }
+ }
+
+ final class SimpleRemove implements DelayedOperation {
+ private Object value;
+
+ public SimpleRemove(Object value) {
+ this.value = value;
+ }
+ public void operate() {
+ list.remove(value);
+ }
+ public Object getAddedInstance() {
+ return null;
+ }
+ public Object getOrphan() {
+ return value;
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentListElementHolder.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentListElementHolder.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentListElementHolder.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,60 @@
+//$Id: PersistentListElementHolder.java 6838 2005-05-20 19:50:07Z oneovthafew $
+package org.hibernate.collection;
+
+import java.io.Serializable;
+import java.util.List;
+
+import org.dom4j.Element;
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.type.Type;
+
+/**
+ * @author Gavin King
+ */
+public class PersistentListElementHolder extends PersistentIndexedElementHolder {
+
+ public PersistentListElementHolder(SessionImplementor session, Element element) {
+ super( session, element );
+ }
+
+ public PersistentListElementHolder(SessionImplementor session, CollectionPersister persister,
+ Serializable key) throws HibernateException {
+ super( session, persister, key );
+ }
+
+ public void initializeFromCache(CollectionPersister persister, Serializable disassembled, Object owner)
+ throws HibernateException {
+
+ Type elementType = persister.getElementType();
+ final String indexNodeName = getIndexAttributeName(persister);
+ Serializable[] cached = (Serializable[]) disassembled;
+ for ( int i=0; i<cached.length; i++ ) {
+ Object object = elementType.assemble( cached[i], getSession(), owner );
+ Element subelement = element.addElement( persister.getElementNodeName() );
+ elementType.setToXMLNode( subelement, object, persister.getFactory() );
+ setIndex( subelement, indexNodeName, Integer.toString(i) );
+ }
+
+ }
+
+ public Serializable disassemble(CollectionPersister persister) throws HibernateException {
+
+ Type elementType = persister.getElementType();
+ final String indexNodeName = getIndexAttributeName(persister);
+ List elements = element.elements( persister.getElementNodeName() );
+ int length = elements.size();
+ Serializable[] result = new Serializable[length];
+ for ( int i=0; i<length; i++ ) {
+ Element elem = (Element) elements.get(i);
+ Object object = elementType.fromXMLNode( elem, persister.getFactory() );
+ Integer index = (Integer) Hibernate.INTEGER.fromStringValue( getIndex(elem, indexNodeName, i) );
+ result[ index.intValue() ] = elementType.disassemble( object, getSession(), null );
+ }
+ return result;
+ }
+
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentListElementHolder.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentMap.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentMap.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentMap.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,491 @@
+//$Id: PersistentMap.java 11255 2007-03-07 19:30:27Z steve.ebersole at jboss.com $
+package org.hibernate.collection;
+
+import java.io.Serializable;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.loader.CollectionAliases;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.type.Type;
+
+
+/**
+ * A persistent wrapper for a <tt>java.util.Map</tt>. Underlying collection
+ * is a <tt>HashMap</tt>.
+ *
+ * @see java.util.HashMap
+ * @author Gavin King
+ */
+public class PersistentMap extends AbstractPersistentCollection implements Map {
+
+ protected Map map;
+
+ /**
+ * Empty constructor.
+ * <p/>
+ * Note: this form is not ever ever ever used by Hibernate; it is, however,
+ * needed for SOAP libraries and other such marshalling code.
+ */
+ public PersistentMap() {
+ // intentionally empty
+ }
+
+ /**
+ * Instantiates a lazy map (the underlying map is un-initialized).
+ *
+ * @param session The session to which this map will belong.
+ */
+ public PersistentMap(SessionImplementor session) {
+ super(session);
+ }
+
+ /**
+ * Instantiates a non-lazy map (the underlying map is constructed
+ * from the incoming map reference).
+ *
+ * @param session The session to which this map will belong.
+ * @param map The underlying map data.
+ */
+ public PersistentMap(SessionImplementor session, Map map) {
+ super(session);
+ this.map = map;
+ setInitialized();
+ setDirectlyAccessible(true);
+ }
+
+ public Serializable getSnapshot(CollectionPersister persister) throws HibernateException {
+ EntityMode entityMode = getSession().getEntityMode();
+ HashMap clonedMap = new HashMap( map.size() );
+ Iterator iter = map.entrySet().iterator();
+ while ( iter.hasNext() ) {
+ Map.Entry e = (Map.Entry) iter.next();
+ final Object copy = persister.getElementType()
+ .deepCopy( e.getValue(), entityMode, persister.getFactory() );
+ clonedMap.put( e.getKey(), copy );
+ }
+ return clonedMap;
+ }
+
+ public Collection getOrphans(Serializable snapshot, String entityName) throws HibernateException {
+ Map sn = (Map) snapshot;
+ return getOrphans( sn.values(), map.values(), entityName, getSession() );
+ }
+
+ public boolean equalsSnapshot(CollectionPersister persister) throws HibernateException {
+ Type elementType = persister.getElementType();
+ Map xmap = (Map) getSnapshot();
+ if ( xmap.size()!=this.map.size() ) return false;
+ Iterator iter = map.entrySet().iterator();
+ while ( iter.hasNext() ) {
+ Map.Entry entry = (Map.Entry) iter.next();
+ if ( elementType.isDirty( entry.getValue(), xmap.get( entry.getKey() ), getSession() ) ) return false;
+ }
+ return true;
+ }
+
+ public boolean isSnapshotEmpty(Serializable snapshot) {
+ return ( (Map) snapshot ).isEmpty();
+ }
+
+ public boolean isWrapper(Object collection) {
+ return map==collection;
+ }
+
+ public void beforeInitialize(CollectionPersister persister, int anticipatedSize) {
+ this.map = ( Map ) persister.getCollectionType().instantiate( anticipatedSize );
+ }
+
+
+ /**
+ * @see java.util.Map#size()
+ */
+ public int size() {
+ return readSize() ? getCachedSize() : map.size();
+ }
+
+ /**
+ * @see java.util.Map#isEmpty()
+ */
+ public boolean isEmpty() {
+ return readSize() ? getCachedSize()==0 : map.isEmpty();
+ }
+
+ /**
+ * @see java.util.Map#containsKey(Object)
+ */
+ public boolean containsKey(Object key) {
+ Boolean exists = readIndexExistence(key);
+ return exists==null ? map.containsKey(key) : exists.booleanValue();
+ }
+
+ /**
+ * @see java.util.Map#containsValue(Object)
+ */
+ public boolean containsValue(Object value) {
+ Boolean exists = readElementExistence(value);
+ return exists==null ?
+ map.containsValue(value) :
+ exists.booleanValue();
+ }
+
+ /**
+ * @see java.util.Map#get(Object)
+ */
+ public Object get(Object key) {
+ Object result = readElementByIndex(key);
+ return result==UNKNOWN ? map.get(key) : result;
+ }
+
+ /**
+ * @see java.util.Map#put(Object, Object)
+ */
+ public Object put(Object key, Object value) {
+ if ( isPutQueueEnabled() ) {
+ Object old = readElementByIndex( key );
+ if ( old != UNKNOWN ) {
+ queueOperation( new Put( key, value, 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;
+ }
+
+ /**
+ * @see java.util.Map#remove(Object)
+ */
+ public Object remove(Object key) {
+ if ( isPutQueueEnabled() ) {
+ Object old = readElementByIndex( key );
+ queueOperation( new Remove( key, old ) );
+ return old;
+ }
+ else {
+ // TODO : safe to interpret "map.remove(key) == null" as non-dirty?
+ initialize( true );
+ if ( map.containsKey( key ) ) {
+ dirty();
+ }
+ return map.remove( key );
+ }
+ }
+
+ /**
+ * @see java.util.Map#putAll(java.util.Map puts)
+ */
+ public void putAll(Map puts) {
+ if ( puts.size()>0 ) {
+ initialize( true );
+ Iterator itr = puts.entrySet().iterator();
+ while ( itr.hasNext() ) {
+ Map.Entry entry = ( Entry ) itr.next();
+ put( entry.getKey(), entry.getValue() );
+ }
+ }
+ }
+
+ /**
+ * @see java.util.Map#clear()
+ */
+ public void clear() {
+ if ( isClearQueueEnabled() ) {
+ queueOperation( new Clear() );
+ }
+ else {
+ initialize( true );
+ if ( ! map.isEmpty() ) {
+ dirty();
+ map.clear();
+ }
+ }
+ }
+
+ /**
+ * @see java.util.Map#keySet()
+ */
+ public Set keySet() {
+ read();
+ return new SetProxy( map.keySet() );
+ }
+
+ /**
+ * @see java.util.Map#values()
+ */
+ public Collection values() {
+ read();
+ return new SetProxy( map.values() );
+ }
+
+ /**
+ * @see java.util.Map#entrySet()
+ */
+ public Set entrySet() {
+ read();
+ return new EntrySetProxy( map.entrySet() );
+ }
+
+ public boolean empty() {
+ return map.isEmpty();
+ }
+
+ public String toString() {
+ read();
+ return map.toString();
+ }
+
+ public Object readFrom(ResultSet rs, CollectionPersister persister, CollectionAliases descriptor, Object owner)
+ throws HibernateException, SQLException {
+ Object element = persister.readElement( rs, owner, descriptor.getSuffixedElementAliases(), getSession() );
+ Object index = persister.readIndex( rs, descriptor.getSuffixedIndexAliases(), getSession() );
+ if ( element!=null ) map.put(index, element);
+ return element;
+ }
+
+ public Iterator entries(CollectionPersister persister) {
+ return map.entrySet().iterator();
+ }
+
+ /** a wrapper for Map.Entry sets */
+ class EntrySetProxy implements Set {
+ private final Set set;
+ EntrySetProxy(Set set) {
+ this.set=set;
+ }
+ public boolean add(Object entry) {
+ //write(); -- doesn't
+ return set.add(entry);
+ }
+ public boolean addAll(Collection entries) {
+ //write(); -- doesn't
+ return set.addAll(entries);
+ }
+ public void clear() {
+ write();
+ set.clear();
+ }
+ public boolean contains(Object entry) {
+ return set.contains(entry);
+ }
+ public boolean containsAll(Collection entries) {
+ return set.containsAll(entries);
+ }
+ public boolean isEmpty() {
+ return set.isEmpty();
+ }
+ public Iterator iterator() {
+ return new EntryIteratorProxy( set.iterator() );
+ }
+ public boolean remove(Object entry) {
+ write();
+ return set.remove(entry);
+ }
+ public boolean removeAll(Collection entries) {
+ write();
+ return set.removeAll(entries);
+ }
+ public boolean retainAll(Collection entries) {
+ write();
+ return set.retainAll(entries);
+ }
+ public int size() {
+ return set.size();
+ }
+ // amazingly, these two will work because AbstractCollection
+ // uses iterator() to fill the array
+ public Object[] toArray() {
+ return set.toArray();
+ }
+ public Object[] toArray(Object[] array) {
+ return set.toArray(array);
+ }
+ }
+ final class EntryIteratorProxy implements Iterator {
+ private final Iterator iter;
+ EntryIteratorProxy(Iterator iter) {
+ this.iter=iter;
+ }
+ public boolean hasNext() {
+ return iter.hasNext();
+ }
+ public Object next() {
+ return new MapEntryProxy( (Map.Entry) iter.next() );
+ }
+ public void remove() {
+ write();
+ iter.remove();
+ }
+ }
+
+ final class MapEntryProxy implements Map.Entry {
+ private final Map.Entry me;
+ MapEntryProxy( Map.Entry me ) {
+ this.me = me;
+ }
+ public Object getKey() { return me.getKey(); }
+ public Object getValue() { return me.getValue(); }
+ public boolean equals(Object o) { return me.equals(o); }
+ public int hashCode() { return me.hashCode(); }
+ // finally, what it's all about...
+ public Object setValue(Object value) {
+ write();
+ return me.setValue(value);
+ }
+ }
+
+ public void initializeFromCache(CollectionPersister persister, Serializable disassembled, Object owner)
+ throws HibernateException {
+ Serializable[] array = ( Serializable[] ) disassembled;
+ int size = array.length;
+ beforeInitialize( persister, size );
+ for ( int i = 0; i < size; i+=2 ) {
+ map.put(
+ persister.getIndexType().assemble( array[i], getSession(), owner ),
+ persister.getElementType().assemble( array[i+1], getSession(), owner )
+ );
+ }
+ }
+
+ public Serializable disassemble(CollectionPersister persister) throws HibernateException {
+
+ Serializable[] result = new Serializable[ map.size() * 2 ];
+ Iterator iter = map.entrySet().iterator();
+ int i=0;
+ while ( iter.hasNext() ) {
+ Map.Entry e = (Map.Entry) iter.next();
+ result[i++] = persister.getIndexType().disassemble( e.getKey(), getSession(), null );
+ result[i++] = persister.getElementType().disassemble( e.getValue(), getSession(), null );
+ }
+ return result;
+
+ }
+
+ public Iterator getDeletes(CollectionPersister persister, boolean indexIsFormula)
+ throws HibernateException {
+ List deletes = new ArrayList();
+ Iterator iter = ( (Map) getSnapshot() ).entrySet().iterator();
+ while ( iter.hasNext() ) {
+ Map.Entry e = (Map.Entry) iter.next();
+ Object key = e.getKey();
+ if ( e.getValue()!=null && map.get(key)==null ) {
+ deletes.add( indexIsFormula ? e.getValue() : key );
+ }
+ }
+ return deletes.iterator();
+ }
+
+ public boolean needsInserting(Object entry, int i, Type elemType)
+ throws HibernateException {
+ final Map sn = (Map) getSnapshot();
+ Map.Entry e = (Map.Entry) entry;
+ return e.getValue()!=null && sn.get( e.getKey() )==null;
+ }
+
+ public boolean needsUpdating(Object entry, int i, Type elemType)
+ throws HibernateException {
+ final Map sn = (Map) getSnapshot();
+ Map.Entry e = (Map.Entry) entry;
+ Object snValue = sn.get( e.getKey() );
+ return e.getValue()!=null &&
+ snValue!=null &&
+ elemType.isDirty( snValue, e.getValue(), getSession() );
+ }
+
+
+ public Object getIndex(Object entry, int i, CollectionPersister persister) {
+ return ( (Map.Entry) entry ).getKey();
+ }
+
+ public Object getElement(Object entry) {
+ return ( (Map.Entry) entry ).getValue();
+ }
+
+ public Object getSnapshotElement(Object entry, int i) {
+ final Map sn = (Map) getSnapshot();
+ return sn.get( ( (Map.Entry) entry ).getKey() );
+ }
+
+ public boolean equals(Object other) {
+ read();
+ return map.equals(other);
+ }
+
+ public int hashCode() {
+ read();
+ return map.hashCode();
+ }
+
+ public boolean entryExists(Object entry, int i) {
+ return ( (Map.Entry) entry ).getValue()!=null;
+ }
+
+ final class Clear implements DelayedOperation {
+ public void operate() {
+ map.clear();
+ }
+ public Object getAddedInstance() {
+ return null;
+ }
+ public Object getOrphan() {
+ throw new UnsupportedOperationException("queued clear cannot be used with orphan delete");
+ }
+ }
+
+ final class Put implements DelayedOperation {
+ private Object index;
+ private Object value;
+ private Object old;
+
+ public Put(Object index, Object value, Object old) {
+ this.index = index;
+ this.value = value;
+ this.old = old;
+ }
+ public void operate() {
+ map.put(index, value);
+ }
+ public Object getAddedInstance() {
+ return value;
+ }
+ public Object getOrphan() {
+ return old;
+ }
+ }
+
+ final class Remove implements DelayedOperation {
+ private Object index;
+ private Object old;
+
+ public Remove(Object index, Object old) {
+ this.index = index;
+ this.old = old;
+ }
+ public void operate() {
+ map.remove(index);
+ }
+ public Object getAddedInstance() {
+ return null;
+ }
+ public Object getOrphan() {
+ return old;
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentMapElementHolder.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentMapElementHolder.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentMapElementHolder.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,71 @@
+//$Id: PersistentMapElementHolder.java 6838 2005-05-20 19:50:07Z oneovthafew $
+package org.hibernate.collection;
+
+import java.io.Serializable;
+import java.util.List;
+
+import org.dom4j.Element;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.type.NullableType;
+import org.hibernate.type.Type;
+
+/**
+ * @author Gavin King
+ */
+public class PersistentMapElementHolder extends PersistentIndexedElementHolder {
+
+ public PersistentMapElementHolder(SessionImplementor session, Element element) {
+ super( session, element );
+ }
+
+ public PersistentMapElementHolder(SessionImplementor session, CollectionPersister persister,
+ Serializable key) throws HibernateException {
+ super( session, persister, key );
+ }
+
+ public void initializeFromCache(CollectionPersister persister, Serializable disassembled, Object owner)
+ throws HibernateException {
+
+ Type elementType = persister.getElementType();
+ Type indexType = persister.getIndexType();
+ final String indexNodeName = getIndexAttributeName(persister);
+
+ Serializable[] cached = (Serializable[]) disassembled;
+
+ for ( int i=0; i<cached.length; ) {
+ Object index = indexType.assemble( cached[i++], getSession(), owner );
+ Object object = elementType.assemble( cached[i++], getSession(), owner );
+
+ Element subelement = element.addElement( persister.getElementNodeName() );
+ elementType.setToXMLNode( subelement, object, persister.getFactory() );
+
+ String indexString = ( (NullableType) indexType ).toXMLString( index, persister.getFactory() );
+ setIndex( subelement, indexNodeName, indexString );
+ }
+
+ }
+
+ public Serializable disassemble(CollectionPersister persister) throws HibernateException {
+
+ Type elementType = persister.getElementType();
+ Type indexType = persister.getIndexType();
+ final String indexNodeName = getIndexAttributeName(persister);
+
+ List elements = element.elements( persister.getElementNodeName() );
+ int length = elements.size();
+ Serializable[] result = new Serializable[length*2];
+ for ( int i=0; i<length*2; ) {
+ Element elem = (Element) elements.get(i/2);
+ Object object = elementType.fromXMLNode( elem, persister.getFactory() );
+ final String indexString = getIndex(elem, indexNodeName, i);
+ Object index = ( (NullableType) indexType ).fromXMLString( indexString, persister.getFactory() );
+ result[i++] = indexType.disassemble( index, getSession(), null );
+ result[i++] = elementType.disassemble( object, getSession(), null );
+ }
+ return result;
+ }
+
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentMapElementHolder.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentSet.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentSet.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentSet.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,468 @@
+//$Id: PersistentSet.java 10589 2006-10-16 15:47:37Z max.andersen at jboss.com $
+package org.hibernate.collection;
+
+import java.io.Serializable;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.loader.CollectionAliases;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.type.Type;
+
+
+/**
+ * A persistent wrapper for a <tt>java.util.Set</tt>. The underlying
+ * collection is a <tt>HashSet</tt>.
+ *
+ * @see java.util.HashSet
+ * @author Gavin King
+ */
+public class PersistentSet extends AbstractPersistentCollection implements java.util.Set {
+
+ protected Set set;
+ protected transient List tempList;
+
+ /**
+ * Empty constructor.
+ * <p/>
+ * Note: this form is not ever ever ever used by Hibernate; it is, however,
+ * needed for SOAP libraries and other such marshalling code.
+ */
+ public PersistentSet() {
+ // intentionally empty
+ }
+
+ /**
+ * Constructor matching super. Instantiates a lazy set (the underlying
+ * set is un-initialized).
+ *
+ * @param session The session to which this set will belong.
+ */
+ public PersistentSet(SessionImplementor session) {
+ super( session );
+ }
+
+ /**
+ * Instantiates a non-lazy set (the underlying set is constructed
+ * from the incoming set reference).
+ *
+ * @param session The session to which this set will belong.
+ * @param set The underlying set data.
+ */
+ public PersistentSet(SessionImplementor session, java.util.Set set) {
+ super(session);
+ // Sets can be just a view of a part of another collection.
+ // do we need to copy it to be sure it won't be changing
+ // underneath us?
+ // ie. this.set.addAll(set);
+ this.set = set;
+ setInitialized();
+ setDirectlyAccessible(true);
+ }
+
+
+ public Serializable getSnapshot(CollectionPersister persister)
+ throws HibernateException {
+ EntityMode entityMode = getSession().getEntityMode();
+
+ //if (set==null) return new Set(session);
+ HashMap clonedSet = new HashMap( set.size() );
+ Iterator iter = set.iterator();
+ while ( iter.hasNext() ) {
+ Object copied = persister.getElementType()
+ .deepCopy( iter.next(), entityMode, persister.getFactory() );
+ clonedSet.put(copied, copied);
+ }
+ return clonedSet;
+ }
+
+ public Collection getOrphans(Serializable snapshot, String entityName) throws HibernateException {
+ java.util.Map sn = (java.util.Map) snapshot;
+ return getOrphans( sn.keySet(), set, entityName, getSession() );
+ }
+
+ public boolean equalsSnapshot(CollectionPersister persister) throws HibernateException {
+ Type elementType = persister.getElementType();
+ java.util.Map sn = (java.util.Map) getSnapshot();
+ if ( sn.size()!=set.size() ) {
+ return false;
+ }
+ else {
+ Iterator iter = set.iterator();
+ while ( iter.hasNext() ) {
+ Object test = iter.next();
+ Object oldValue = sn.get(test);
+ if ( oldValue==null || elementType.isDirty( oldValue, test, getSession() ) ) return false;
+ }
+ return true;
+ }
+ }
+
+ public boolean isSnapshotEmpty(Serializable snapshot) {
+ return ( (java.util.Map) snapshot ).isEmpty();
+ }
+
+ public void beforeInitialize(CollectionPersister persister, int anticipatedSize) {
+ this.set = ( Set ) persister.getCollectionType().instantiate( anticipatedSize );
+ }
+
+ public void initializeFromCache(CollectionPersister persister, Serializable disassembled, Object owner)
+ throws HibernateException {
+ Serializable[] array = ( Serializable[] ) disassembled;
+ int size = array.length;
+ beforeInitialize( persister, size );
+ for (int i = 0; i < size; i++ ) {
+ Object element = persister.getElementType().assemble( array[i], getSession(), owner );
+ if ( element != null ) {
+ set.add( element );
+ }
+ }
+ }
+
+ public boolean empty() {
+ return set.isEmpty();
+ }
+
+ /**
+ * @see java.util.Set#size()
+ */
+ public int size() {
+ return readSize() ? getCachedSize() : set.size();
+ }
+
+ /**
+ * @see java.util.Set#isEmpty()
+ */
+ public boolean isEmpty() {
+ return readSize() ? getCachedSize()==0 : set.isEmpty();
+ }
+
+ /**
+ * @see java.util.Set#contains(Object)
+ */
+ public boolean contains(Object object) {
+ Boolean exists = readElementExistence(object);
+ return exists==null ?
+ set.contains(object) :
+ exists.booleanValue();
+ }
+
+ /**
+ * @see java.util.Set#iterator()
+ */
+ public Iterator iterator() {
+ read();
+ return new IteratorProxy( set.iterator() );
+ }
+
+ /**
+ * @see java.util.Set#toArray()
+ */
+ public Object[] toArray() {
+ read();
+ return set.toArray();
+ }
+
+ /**
+ * @see java.util.Set#toArray(Object[])
+ */
+ public Object[] toArray(Object[] array) {
+ read();
+ return set.toArray(array);
+ }
+
+ /**
+ * @see java.util.Set#add(Object)
+ */
+ public boolean add(Object value) {
+ Boolean exists = isOperationQueueEnabled() ? readElementExistence( value ) : null;
+ if ( exists == null ) {
+ initialize( true );
+ if ( set.add( value ) ) {
+ dirty();
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+ else if ( exists.booleanValue() ) {
+ return false;
+ }
+ else {
+ queueOperation( new SimpleAdd(value) );
+ return true;
+ }
+ }
+
+ /**
+ * @see java.util.Set#remove(Object)
+ */
+ public boolean remove(Object value) {
+ Boolean exists = isPutQueueEnabled() ? readElementExistence( value ) : null;
+ if ( exists==null ) {
+ initialize( true );
+ if ( set.remove( value ) ) {
+ dirty();
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+ else if ( exists.booleanValue() ) {
+ queueOperation( new SimpleRemove(value) );
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ /**
+ * @see java.util.Set#containsAll(Collection)
+ */
+ public boolean containsAll(Collection coll) {
+ read();
+ return set.containsAll(coll);
+ }
+
+ /**
+ * @see java.util.Set#addAll(Collection)
+ */
+ public boolean addAll(Collection coll) {
+ if ( coll.size() > 0 ) {
+ initialize( true );
+ if ( set.addAll( coll ) ) {
+ dirty();
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+ else {
+ return false;
+ }
+ }
+
+ /**
+ * @see java.util.Set#retainAll(Collection)
+ */
+ public boolean retainAll(Collection coll) {
+ initialize( true );
+ if ( set.retainAll( coll ) ) {
+ dirty();
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ /**
+ * @see java.util.Set#removeAll(Collection)
+ */
+ public boolean removeAll(Collection coll) {
+ if ( coll.size() > 0 ) {
+ initialize( true );
+ if ( set.removeAll( coll ) ) {
+ dirty();
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+ else {
+ return false;
+ }
+ }
+
+ /**
+ * @see java.util.Set#clear()
+ */
+ public void clear() {
+ if ( isClearQueueEnabled() ) {
+ queueOperation( new Clear() );
+ }
+ else {
+ initialize( true );
+ if ( !set.isEmpty() ) {
+ set.clear();
+ dirty();
+ }
+ }
+ }
+
+ public String toString() {
+ //if (needLoading) return "asleep";
+ read();
+ return set.toString();
+ }
+
+ public Object readFrom(
+ ResultSet rs,
+ CollectionPersister persister,
+ CollectionAliases descriptor,
+ Object owner) throws HibernateException, SQLException {
+ Object element = persister.readElement( rs, owner, descriptor.getSuffixedElementAliases(), getSession() );
+ if (element!=null) tempList.add(element);
+ return element;
+ }
+
+ public void beginRead() {
+ super.beginRead();
+ tempList = new ArrayList();
+ }
+
+ public boolean endRead() {
+ set.addAll(tempList);
+ tempList = null;
+ setInitialized();
+ return true;
+ }
+
+ public Iterator entries(CollectionPersister persister) {
+ return set.iterator();
+ }
+
+ public Serializable disassemble(CollectionPersister persister)
+ throws HibernateException {
+
+ Serializable[] result = new Serializable[ set.size() ];
+ Iterator iter = set.iterator();
+ int i=0;
+ while ( iter.hasNext() ) {
+ result[i++] = persister.getElementType().disassemble( iter.next(), getSession(), null );
+ }
+ return result;
+
+ }
+
+ public Iterator getDeletes(CollectionPersister persister, boolean indexIsFormula) throws HibernateException {
+ Type elementType = persister.getElementType();
+ final java.util.Map sn = (java.util.Map) getSnapshot();
+ ArrayList deletes = new ArrayList( sn.size() );
+ Iterator iter = sn.keySet().iterator();
+ while ( iter.hasNext() ) {
+ Object test = iter.next();
+ if ( !set.contains(test) ) {
+ // the element has been removed from the set
+ deletes.add(test);
+ }
+ }
+ iter = set.iterator();
+ while ( iter.hasNext() ) {
+ Object test = iter.next();
+ Object oldValue = sn.get(test);
+ if ( oldValue!=null && elementType.isDirty( test, oldValue, getSession() ) ) {
+ // the element has changed
+ deletes.add(oldValue);
+ }
+ }
+ return deletes.iterator();
+ }
+
+ public boolean needsInserting(Object entry, int i, Type elemType) throws HibernateException {
+ final java.util.Map sn = (java.util.Map) getSnapshot();
+ Object oldValue = sn.get(entry);
+ // note that it might be better to iterate the snapshot but this is safe,
+ // assuming the user implements equals() properly, as required by the Set
+ // contract!
+ return oldValue==null || elemType.isDirty( oldValue, entry, getSession() );
+ }
+
+ public boolean needsUpdating(Object entry, int i, Type elemType) {
+ return false;
+ }
+
+ public boolean isRowUpdatePossible() {
+ return false;
+ }
+
+ public Object getIndex(Object entry, int i, CollectionPersister persister) {
+ throw new UnsupportedOperationException("Sets don't have indexes");
+ }
+
+ public Object getElement(Object entry) {
+ return entry;
+ }
+
+ public Object getSnapshotElement(Object entry, int i) {
+ throw new UnsupportedOperationException("Sets don't support updating by element");
+ }
+
+ public boolean equals(Object other) {
+ read();
+ return set.equals(other);
+ }
+
+ public int hashCode() {
+ read();
+ return set.hashCode();
+ }
+
+ public boolean entryExists(Object key, int i) {
+ return true;
+ }
+
+ public boolean isWrapper(Object collection) {
+ return set==collection;
+ }
+
+ final class Clear implements DelayedOperation {
+ public void operate() {
+ set.clear();
+ }
+ public Object getAddedInstance() {
+ return null;
+ }
+ public Object getOrphan() {
+ throw new UnsupportedOperationException("queued clear cannot be used with orphan delete");
+ }
+ }
+
+ final class SimpleAdd implements DelayedOperation {
+ private Object value;
+
+ public SimpleAdd(Object value) {
+ this.value = value;
+ }
+ public void operate() {
+ set.add(value);
+ }
+ public Object getAddedInstance() {
+ return value;
+ }
+ public Object getOrphan() {
+ return null;
+ }
+ }
+
+ final class SimpleRemove implements DelayedOperation {
+ private Object value;
+
+ public SimpleRemove(Object value) {
+ this.value = value;
+ }
+ public void operate() {
+ set.remove(value);
+ }
+ public Object getAddedInstance() {
+ return null;
+ }
+ public Object getOrphan() {
+ return value;
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentSortedMap.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentSortedMap.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentSortedMap.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,191 @@
+//$Id: PersistentSortedMap.java 7714 2005-08-01 16:29:33Z oneovthafew $
+package org.hibernate.collection;
+
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.collection.BasicCollectionPersister;
+
+
+/**
+ * A persistent wrapper for a <tt>java.util.SortedMap</tt>. Underlying
+ * collection is a <tt>TreeMap</tt>.
+ *
+ * @see java.util.TreeMap
+ * @author <a href="mailto:doug.currie at alum.mit.edu">e</a>
+ */
+public class PersistentSortedMap extends PersistentMap implements SortedMap {
+
+ protected Comparator comparator;
+
+ protected Serializable snapshot(BasicCollectionPersister persister, EntityMode entityMode) throws HibernateException {
+ TreeMap clonedMap = new TreeMap(comparator);
+ Iterator iter = map.entrySet().iterator();
+ while ( iter.hasNext() ) {
+ Map.Entry e = (Map.Entry) iter.next();
+ clonedMap.put( e.getKey(), persister.getElementType().deepCopy( e.getValue(), entityMode, persister.getFactory() ) );
+ }
+ return clonedMap;
+ }
+
+ public PersistentSortedMap(SessionImplementor session) {
+ super(session);
+ }
+
+ public void setComparator(Comparator comparator) {
+ this.comparator = comparator;
+ }
+
+ public PersistentSortedMap(SessionImplementor session, SortedMap map) {
+ super(session, map);
+ comparator = map.comparator();
+ }
+
+ public PersistentSortedMap() {} //needed for SOAP libraries, etc
+
+ /**
+ * @see PersistentSortedMap#comparator()
+ */
+ public Comparator comparator() {
+ return comparator;
+ }
+
+ /**
+ * @see PersistentSortedMap#subMap(Object, Object)
+ */
+ public SortedMap subMap(Object fromKey, Object toKey) {
+ read();
+ SortedMap m = ( (SortedMap) map ).subMap(fromKey, toKey);
+ return new SortedSubMap(m);
+ }
+
+ /**
+ * @see PersistentSortedMap#headMap(Object)
+ */
+ public SortedMap headMap(Object toKey) {
+ read();
+ SortedMap m;
+ m = ( (SortedMap) map ).headMap(toKey);
+ return new SortedSubMap(m);
+ }
+
+ /**
+ * @see PersistentSortedMap#tailMap(Object)
+ */
+ public SortedMap tailMap(Object fromKey) {
+ read();
+ SortedMap m;
+ m = ( (SortedMap) map ).tailMap(fromKey);
+ return new SortedSubMap(m);
+ }
+
+ /**
+ * @see PersistentSortedMap#firstKey()
+ */
+ public Object firstKey() {
+ read();
+ return ( (SortedMap) map ).firstKey();
+ }
+
+ /**
+ * @see PersistentSortedMap#lastKey()
+ */
+ public Object lastKey() {
+ read();
+ return ( (SortedMap) map ).lastKey();
+ }
+
+ class SortedSubMap implements SortedMap {
+
+ SortedMap submap;
+
+ SortedSubMap(SortedMap m) {
+ this.submap = m;
+ }
+ // from Map
+ public int size() {
+ return submap.size();
+ }
+ public boolean isEmpty() {
+ return submap.isEmpty();
+ }
+ public boolean containsKey(Object key) {
+ return submap.containsKey(key);
+ }
+ public boolean containsValue(Object key) {
+ return submap.containsValue(key) ;
+ }
+ public Object get(Object key) {
+ return submap.get(key);
+ }
+ public Object put(Object key, Object value) {
+ write();
+ return submap.put(key, value);
+ }
+ public Object remove(Object key) {
+ write();
+ return submap.remove(key);
+ }
+ public void putAll(Map other) {
+ write();
+ submap.putAll(other);
+ }
+ public void clear() {
+ write();
+ submap.clear();
+ }
+ public Set keySet() {
+ return new SetProxy( submap.keySet() );
+ }
+ public Collection values() {
+ return new SetProxy( submap.values() );
+ }
+ public Set entrySet() {
+ return new EntrySetProxy( submap.entrySet() );
+ }
+ // from SortedMap
+ public Comparator comparator() {
+ return submap.comparator();
+ }
+ public SortedMap subMap(Object fromKey, Object toKey) {
+ SortedMap m;
+ m = submap.subMap(fromKey, toKey);
+ return new SortedSubMap( m );
+ }
+ public SortedMap headMap(Object toKey) {
+ SortedMap m;
+ m = submap.headMap(toKey);
+ return new SortedSubMap(m);
+ }
+ public SortedMap tailMap(Object fromKey) {
+ SortedMap m;
+ m = submap.tailMap(fromKey);
+ return new SortedSubMap(m);
+ }
+ public Object firstKey() {
+ return submap.firstKey();
+ }
+ public Object lastKey() {
+ return submap.lastKey();
+ }
+
+ }
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentSortedSet.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentSortedSet.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/collection/PersistentSortedSet.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,145 @@
+//$Id: PersistentSortedSet.java 7714 2005-08-01 16:29:33Z oneovthafew $
+package org.hibernate.collection;
+
+import java.io.Serializable;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.SortedSet;
+import java.util.TreeMap;
+
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.collection.BasicCollectionPersister;
+
+
+/**
+ * A persistent wrapper for a <tt>java.util.SortedSet</tt>. Underlying
+ * collection is a <tt>TreeSet</tt>.
+ *
+ * @see java.util.TreeSet
+ * @author <a href="mailto:doug.currie at alum.mit.edu">e</a>
+ */
+public class PersistentSortedSet extends PersistentSet implements SortedSet {
+
+ protected Comparator comparator;
+
+ protected Serializable snapshot(BasicCollectionPersister persister, EntityMode entityMode)
+ throws HibernateException {
+ //if (set==null) return new Set(session);
+ TreeMap clonedSet = new TreeMap(comparator);
+ Iterator iter = set.iterator();
+ while ( iter.hasNext() ) {
+ Object copy = persister.getElementType().deepCopy( iter.next(), entityMode, persister.getFactory() );
+ clonedSet.put(copy, copy);
+ }
+ return clonedSet;
+ }
+
+ public void setComparator(Comparator comparator) {
+ this.comparator = comparator;
+ }
+
+ public PersistentSortedSet(SessionImplementor session) {
+ super(session);
+ }
+
+ public PersistentSortedSet(SessionImplementor session, SortedSet set) {
+ super(session, set);
+ comparator = set.comparator();
+ }
+
+ public PersistentSortedSet() {} //needed for SOAP libraries, etc
+
+ /**
+ * @see PersistentSortedSet#comparator()
+ */
+ public Comparator comparator() {
+ return comparator;
+ }
+
+ /**
+ * @see PersistentSortedSet#subSet(Object,Object)
+ */
+ public SortedSet subSet(Object fromElement, Object toElement) {
+ read();
+ SortedSet s;
+ s = ( (SortedSet) set ).subSet(fromElement, toElement);
+ return new SubSetProxy(s);
+ }
+
+ /**
+ * @see PersistentSortedSet#headSet(Object)
+ */
+ public SortedSet headSet(Object toElement) {
+ read();
+ SortedSet s = ( (SortedSet) set ).headSet(toElement);
+ return new SubSetProxy(s);
+ }
+
+ /**
+ * @see PersistentSortedSet#tailSet(Object)
+ */
+ public SortedSet tailSet(Object fromElement) {
+ read();
+ SortedSet s = ( (SortedSet) set ).tailSet(fromElement);
+ return new SubSetProxy(s);
+ }
+
+ /**
+ * @see PersistentSortedSet#first()
+ */
+ public Object first() {
+ read();
+ return ( (SortedSet) set ).first();
+ }
+
+ /**
+ * @see PersistentSortedSet#last()
+ */
+ public Object last() {
+ read();
+ return ( (SortedSet) set ).last();
+ }
+
+ /** wrapper for subSets to propagate write to its backing set */
+ class SubSetProxy extends SetProxy implements SortedSet {
+
+ SubSetProxy(SortedSet s) {
+ super(s);
+ }
+
+ public Comparator comparator() {
+ return ( (SortedSet) this.set ).comparator();
+ }
+
+ public Object first() {
+ return ( (SortedSet) this.set ).first();
+ }
+
+ public SortedSet headSet(Object toValue) {
+ return new SubSetProxy( ( (SortedSet) this.set ).headSet(toValue) );
+ }
+
+ public Object last() {
+ return ( (SortedSet) this.set ).last();
+ }
+
+ public SortedSet subSet(Object fromValue, Object toValue) {
+ return new SubSetProxy( ( (SortedSet) this.set ).subSet(fromValue, toValue) );
+ }
+
+ public SortedSet tailSet(Object fromValue) {
+ return new SubSetProxy( ( (SortedSet) this.set ).tailSet(fromValue) );
+ }
+
+ }
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/collection/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/collection/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/collection/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,8 @@
+<html>
+<head></head>
+<body>
+<p>
+ This package defines a framework for collection wrappers.
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/collection/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/connection/ConnectionProvider.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/connection/ConnectionProvider.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/connection/ConnectionProvider.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,71 @@
+//$Id: ConnectionProvider.java 9191 2006-02-01 14:40:34Z epbernard $
+package org.hibernate.connection;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import org.hibernate.HibernateException;
+
+/**
+ * A strategy for obtaining JDBC connections.
+ * <br><br>
+ * Implementors might also implement connection pooling.<br>
+ * <br>
+ * The <tt>ConnectionProvider</tt> interface is not intended to be
+ * exposed to the application. Instead it is used internally by
+ * Hibernate to obtain connections.<br>
+ * <br>
+ * Implementors should provide a public default constructor.
+ *
+ * @see ConnectionProviderFactory
+ * @author Gavin King
+ */
+public interface ConnectionProvider {
+ /**
+ * Initialize the connection provider from given properties.
+ * @param props <tt>SessionFactory</tt> properties
+ */
+ public void configure(Properties props) throws HibernateException;
+ /**
+ * Grab a connection, with the autocommit mode specified by
+ * <tt>hibernate.connection.autocommit</tt>.
+ * @return a JDBC connection
+ * @throws SQLException
+ */
+ public Connection getConnection() throws SQLException;
+ /**
+ * Dispose of a used connection.
+ * @param conn a JDBC connection
+ * @throws SQLException
+ */
+ public void closeConnection(Connection conn) throws SQLException;
+
+ /**
+ * Release all resources held by this provider. JavaDoc requires a second sentence.
+ * @throws HibernateException
+ */
+ public void close() throws HibernateException;
+
+ /**
+ * Does this connection provider support aggressive release of JDBC
+ * connections and re-acquistion of those connections (if need be) later?
+ * <p/>
+ * This is used in conjunction with {@link org.hibernate.cfg.Environment.RELEASE_CONNECTIONS}
+ * to aggressively release JDBC connections. However, the configured ConnectionProvider
+ * must support re-acquisition of the same underlying connection for that semantic to work.
+ * <p/>
+ * Typically, this is only true in managed environments where a container
+ * tracks connections by transaction or thread.
+ *
+ * Note that JTA semantic depends on the fact that the underlying connection provider does
+ * support aggressive release.
+ */
+ public boolean supportsAggressiveRelease();
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/connection/ConnectionProviderFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/connection/ConnectionProviderFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/connection/ConnectionProviderFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,174 @@
+//$Id: ConnectionProviderFactory.java 7541 2005-07-18 22:37:31Z epbernard $
+package org.hibernate.connection;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.Set;
+import java.util.Map;
+import java.beans.Introspector;
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.hibernate.HibernateException;
+import org.hibernate.cfg.Environment;
+import org.hibernate.util.ReflectHelper;
+
+/**
+ * Instantiates a connection provider given either <tt>System</tt> properties or
+ * a <tt>java.util.Properties</tt> instance. The <tt>ConnectionProviderFactory</tt>
+ * first attempts to find a name of a <tt>ConnectionProvider</tt> subclass in the
+ * property <tt>hibernate.connection.provider_class</tt>. If missing, heuristics are used
+ * to choose either <tt>DriverManagerConnectionProvider</tt>,
+ * <tt>DatasourceConnectionProvider</tt>, <tt>C3P0ConnectionProvider</tt> or
+ * <tt>DBCPConnectionProvider</tt>.
+ * @see ConnectionProvider
+ * @author Gavin King
+ */
+
+public final class ConnectionProviderFactory {
+
+ private static final Log log = LogFactory.getLog(ConnectionProviderFactory.class);
+
+ /**
+ * Instantiate a <tt>ConnectionProvider</tt> using <tt>System</tt> properties.
+ * @return ConnectionProvider
+ * @throws HibernateException
+ */
+ public static ConnectionProvider newConnectionProvider() throws HibernateException {
+ return newConnectionProvider( Environment.getProperties() );
+ }
+
+ /**
+ * Instantiate a <tt>ConnectionProvider</tt> using given properties.
+ * Method newConnectionProvider.
+ * @param properties hibernate <tt>SessionFactory</tt> properties
+ * @return ConnectionProvider
+ * @throws HibernateException
+ */
+ public static ConnectionProvider newConnectionProvider(Properties properties) throws HibernateException {
+ return newConnectionProvider( properties, null );
+ }
+
+ /**
+ * Instantiate a <tt>ConnectionProvider</tt> using given properties.
+ * Method newConnectionProvider.
+ * @param properties hibernate <tt>SessionFactory</tt> properties
+ * @Param connectionProviderInjectionData object to be injected in the conenction provided
+ * @return ConnectionProvider
+ * @throws HibernateException
+ */
+ public static ConnectionProvider newConnectionProvider(Properties properties, Map connectionProviderInjectionData) throws HibernateException {
+ ConnectionProvider connections;
+ String providerClass = properties.getProperty(Environment.CONNECTION_PROVIDER);
+ if ( providerClass!=null ) {
+ try {
+ log.info("Initializing connection provider: " + providerClass);
+ connections = (ConnectionProvider) ReflectHelper.classForName(providerClass).newInstance();
+ }
+ catch (Exception e) {
+ log.fatal("Could not instantiate connection provider", e);
+ throw new HibernateException("Could not instantiate connection provider: " + providerClass);
+ }
+ }
+ else if ( properties.getProperty(Environment.DATASOURCE)!=null ) {
+ connections = new DatasourceConnectionProvider();
+ }
+ else if ( properties.getProperty(Environment.C3P0_MAX_SIZE)!=null ) {
+ connections = new C3P0ConnectionProvider();
+ }
+ else if (
+ properties.getProperty(Environment.PROXOOL_XML)!=null ||
+ properties.getProperty(Environment.PROXOOL_PROPERTIES)!=null ||
+ properties.getProperty(Environment.PROXOOL_EXISTING_POOL)!=null
+ ) {
+ connections = new ProxoolConnectionProvider();
+ }
+ else if ( properties.getProperty(Environment.URL)!=null ) {
+ connections = new DriverManagerConnectionProvider();
+ }
+ else {
+ connections = new UserSuppliedConnectionProvider();
+ }
+
+ if ( connectionProviderInjectionData != null && connectionProviderInjectionData.size() != 0 ) {
+ //inject the data
+ try {
+ BeanInfo info = Introspector.getBeanInfo( connections.getClass() );
+ PropertyDescriptor[] descritors = info.getPropertyDescriptors();
+ int size = descritors.length;
+ for (int index = 0 ; index < size ; index++) {
+ String propertyName = descritors[index].getName();
+ if ( connectionProviderInjectionData.containsKey( propertyName ) ) {
+ Method method = descritors[index].getWriteMethod();
+ method.invoke( connections, new Object[] { connectionProviderInjectionData.get( propertyName ) } );
+ }
+ }
+ }
+ catch (IntrospectionException e) {
+ throw new HibernateException("Unable to inject objects into the conenction provider", e);
+ }
+ catch (IllegalAccessException e) {
+ throw new HibernateException("Unable to inject objects into the conenction provider", e);
+ }
+ catch (InvocationTargetException e) {
+ throw new HibernateException("Unable to inject objects into the conenction provider", e);
+ }
+ }
+ connections.configure(properties);
+ return connections;
+ }
+
+ // cannot be instantiated
+ private ConnectionProviderFactory() { throw new UnsupportedOperationException(); }
+
+ /**
+ * Transform JDBC connection properties.
+ *
+ * Passed in the form <tt>hibernate.connection.*</tt> to the
+ * format accepted by <tt>DriverManager</tt> by triming the leading "<tt>hibernate.connection</tt>".
+ */
+ public static Properties getConnectionProperties(Properties properties) {
+
+ Iterator iter = properties.keySet().iterator();
+ Properties result = new Properties();
+ while ( iter.hasNext() ) {
+ String prop = (String) iter.next();
+ if ( prop.indexOf(Environment.CONNECTION_PREFIX) > -1 && !SPECIAL_PROPERTIES.contains(prop) ) {
+ result.setProperty(
+ prop.substring( Environment.CONNECTION_PREFIX.length()+1 ),
+ properties.getProperty(prop)
+ );
+ }
+ }
+ String userName = properties.getProperty(Environment.USER);
+ if (userName!=null) result.setProperty( "user", userName );
+ return result;
+ }
+
+ private static final Set SPECIAL_PROPERTIES;
+ static {
+ SPECIAL_PROPERTIES = new HashSet();
+ SPECIAL_PROPERTIES.add(Environment.DATASOURCE);
+ SPECIAL_PROPERTIES.add(Environment.URL);
+ SPECIAL_PROPERTIES.add(Environment.CONNECTION_PROVIDER);
+ SPECIAL_PROPERTIES.add(Environment.POOL_SIZE);
+ SPECIAL_PROPERTIES.add(Environment.ISOLATION);
+ SPECIAL_PROPERTIES.add(Environment.DRIVER);
+ SPECIAL_PROPERTIES.add(Environment.USER);
+
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/connection/DatasourceConnectionProvider.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/connection/DatasourceConnectionProvider.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/connection/DatasourceConnectionProvider.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,93 @@
+//$Id: DatasourceConnectionProvider.java 10075 2006-07-01 12:50:34Z epbernard $
+package org.hibernate.connection;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import javax.sql.DataSource;
+
+import org.hibernate.HibernateException;
+import org.hibernate.cfg.Environment;
+import org.hibernate.util.NamingHelper;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * A connection provider that uses a <tt>DataSource</tt> registered with JNDI.
+ * Hibernate will use this <tt>ConnectionProvider</tt> by default if the
+ * property <tt>hibernate.connection.datasource</tt> is set.
+ * @see ConnectionProvider
+ * @author Gavin King
+ */
+public class DatasourceConnectionProvider implements ConnectionProvider {
+ private DataSource ds;
+ private String user;
+ private String pass;
+
+ private static final Log log = LogFactory.getLog(DatasourceConnectionProvider.class);
+
+ public DataSource getDataSource() {
+ return ds;
+ }
+
+ public void setDataSource(DataSource ds) {
+ this.ds = ds;
+ }
+
+ public void configure(Properties props) throws HibernateException {
+
+ String jndiName = props.getProperty(Environment.DATASOURCE);
+ if (jndiName==null) {
+ String msg = "datasource JNDI name was not specified by property " + Environment.DATASOURCE;
+ log.fatal(msg);
+ throw new HibernateException(msg);
+ }
+
+ user = props.getProperty(Environment.USER);
+ pass = props.getProperty(Environment.PASS);
+
+ try {
+ ds = (DataSource) NamingHelper.getInitialContext(props).lookup(jndiName);
+ }
+ catch (Exception e) {
+ log.fatal( "Could not find datasource: " + jndiName, e );
+ throw new HibernateException( "Could not find datasource", e );
+ }
+ if (ds==null) {
+ throw new HibernateException( "Could not find datasource: " + jndiName );
+ }
+ log.info( "Using datasource: " + jndiName );
+ }
+
+ public Connection getConnection() throws SQLException {
+ if (user != null || pass != null) {
+ return ds.getConnection(user, pass);
+ }
+ else {
+ return ds.getConnection();
+ }
+ }
+
+ public void closeConnection(Connection conn) throws SQLException {
+ conn.close();
+ }
+
+ public void close() {}
+
+ /**
+ * @see ConnectionProvider#supportsAggressiveRelease()
+ */
+ public boolean supportsAggressiveRelease() {
+ return true;
+ }
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/connection/DriverManagerConnectionProvider.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/connection/DriverManagerConnectionProvider.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/connection/DriverManagerConnectionProvider.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,176 @@
+//$Id: DriverManagerConnectionProvider.java 7888 2005-08-12 21:22:38Z oneovthafew $
+package org.hibernate.connection;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Properties;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.HibernateException;
+import org.hibernate.cfg.Environment;
+import org.hibernate.util.PropertiesHelper;
+import org.hibernate.util.ReflectHelper;
+
+/**
+ * A connection provider that uses <tt>java.sql.DriverManager</tt>. This provider
+ * also implements a very rudimentary connection pool.
+ * @see ConnectionProvider
+ * @author Gavin King
+ */
+public class DriverManagerConnectionProvider implements ConnectionProvider {
+
+ private String url;
+ private Properties connectionProps;
+ private Integer isolation;
+ private final ArrayList pool = new ArrayList();
+ private int poolSize;
+ private int checkedOut = 0;
+ private boolean autocommit;
+
+ private static final Log log = LogFactory.getLog(DriverManagerConnectionProvider.class);
+
+ public void configure(Properties props) throws HibernateException {
+
+ String driverClass = props.getProperty(Environment.DRIVER);
+
+ poolSize = PropertiesHelper.getInt(Environment.POOL_SIZE, props, 20); //default pool size 20
+ log.info("Using Hibernate built-in connection pool (not for production use!)");
+ log.info("Hibernate connection pool size: " + poolSize);
+
+ autocommit = PropertiesHelper.getBoolean(Environment.AUTOCOMMIT, props);
+ log.info("autocommit mode: " + autocommit);
+
+ isolation = PropertiesHelper.getInteger(Environment.ISOLATION, props);
+ if (isolation!=null)
+ log.info( "JDBC isolation level: " + Environment.isolationLevelToString( isolation.intValue() ) );
+
+ if (driverClass==null) {
+ log.warn("no JDBC Driver class was specified by property " + Environment.DRIVER);
+ }
+ else {
+ try {
+ // trying via forName() first to be as close to DriverManager's semantics
+ Class.forName(driverClass);
+ }
+ catch (ClassNotFoundException cnfe) {
+ try {
+ ReflectHelper.classForName(driverClass);
+ }
+ catch (ClassNotFoundException e) {
+ String msg = "JDBC Driver class not found: " + driverClass;
+ log.fatal(msg, e);
+ throw new HibernateException(msg, e);
+ }
+ }
+ }
+
+ url = props.getProperty(Environment.URL);
+ if (url==null) {
+ String msg = "JDBC URL was not specified by property " + Environment.URL;
+ log.fatal(msg);
+ throw new HibernateException(msg);
+ }
+
+ connectionProps = ConnectionProviderFactory.getConnectionProperties(props);
+
+ log.info( "using driver: " + driverClass + " at URL: " + url );
+ // if debug level is enabled, then log the password, otherwise mask it
+ if ( log.isDebugEnabled() ) {
+ log.info( "connection properties: " + connectionProps );
+ }
+ else if ( log.isInfoEnabled() ) {
+ log.info( "connection properties: " + PropertiesHelper.maskOut(connectionProps, "password") );
+ }
+
+ }
+
+ public Connection getConnection() throws SQLException {
+
+ if ( log.isTraceEnabled() ) log.trace( "total checked-out connections: " + checkedOut );
+
+ synchronized (pool) {
+ if ( !pool.isEmpty() ) {
+ int last = pool.size() - 1;
+ if ( log.isTraceEnabled() ) {
+ log.trace("using pooled JDBC connection, pool size: " + last);
+ checkedOut++;
+ }
+ Connection pooled = (Connection) pool.remove(last);
+ if (isolation!=null) pooled.setTransactionIsolation( isolation.intValue() );
+ if ( pooled.getAutoCommit()!=autocommit ) pooled.setAutoCommit(autocommit);
+ return pooled;
+ }
+ }
+
+ log.debug("opening new JDBC connection");
+ Connection conn = DriverManager.getConnection(url, connectionProps);
+ if (isolation!=null) conn.setTransactionIsolation( isolation.intValue() );
+ if ( conn.getAutoCommit()!=autocommit ) conn.setAutoCommit(autocommit);
+
+ if ( log.isDebugEnabled() ) {
+ log.debug( "created connection to: " + url + ", Isolation Level: " + conn.getTransactionIsolation() );
+ }
+ if ( log.isTraceEnabled() ) checkedOut++;
+
+ return conn;
+ }
+
+ public void closeConnection(Connection conn) throws SQLException {
+
+ if ( log.isDebugEnabled() ) checkedOut--;
+
+ synchronized (pool) {
+ int currentSize = pool.size();
+ if ( currentSize < poolSize ) {
+ if ( log.isTraceEnabled() ) log.trace("returning connection to pool, pool size: " + (currentSize + 1) );
+ pool.add(conn);
+ return;
+ }
+ }
+
+ log.debug("closing JDBC connection");
+
+ conn.close();
+
+ }
+
+ protected void finalize() {
+ close();
+ }
+
+ public void close() {
+
+ log.info("cleaning up connection pool: " + url);
+
+ Iterator iter = pool.iterator();
+ while ( iter.hasNext() ) {
+ try {
+ ( (Connection) iter.next() ).close();
+ }
+ catch (SQLException sqle) {
+ log.warn("problem closing pooled connection", sqle);
+ }
+ }
+ pool.clear();
+
+ }
+
+ /**
+ * @see ConnectionProvider#supportsAggressiveRelease()
+ */
+ public boolean supportsAggressiveRelease() {
+ return false;
+ }
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/connection/UserSuppliedConnectionProvider.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/connection/UserSuppliedConnectionProvider.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/connection/UserSuppliedConnectionProvider.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,56 @@
+//$Id: UserSuppliedConnectionProvider.java 6463 2005-04-19 15:39:07Z steveebersole $
+package org.hibernate.connection;
+
+import java.sql.Connection;
+import java.util.Properties;
+
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.HibernateException;
+
+/**
+ * An implementation of the <literal>ConnectionProvider</literal> interface that
+ * simply throws an exception when a connection is requested. This implementation
+ * indicates that the user is expected to supply a JDBC connection.
+ * @see ConnectionProvider
+ * @author Gavin King
+ */
+public class UserSuppliedConnectionProvider implements ConnectionProvider {
+
+ /**
+ * @see org.hibernate.connection.ConnectionProvider#configure(Properties)
+ */
+ public void configure(Properties props) throws HibernateException {
+ LogFactory.getLog(UserSuppliedConnectionProvider.class).warn("No connection properties specified - the user must supply JDBC connections");
+ }
+
+ /**
+ * @see org.hibernate.connection.ConnectionProvider#getConnection()
+ */
+ public Connection getConnection() {
+ throw new UnsupportedOperationException("The user must supply a JDBC connection");
+ }
+
+ /**
+ * @see org.hibernate.connection.ConnectionProvider#closeConnection(Connection)
+ */
+ public void closeConnection(Connection conn) {
+ throw new UnsupportedOperationException("The user must supply a JDBC connection");
+ }
+
+ public void close() {
+ }
+
+ /**
+ * @see ConnectionProvider#supportsAggressiveRelease()
+ */
+ public boolean supportsAggressiveRelease() {
+ return false;
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/connection/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/connection/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/connection/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,13 @@
+<html>
+<head></head>
+<body>
+<p>
+ This package abstracts the mechanism for obtaining
+ a JDBC connection.
+</p>
+<p>
+ A concrete implementation of <tt>ConnectionProvider</tt> may be
+ selected by specifying <tt>hibernate.connection.provider_class</tt>.
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/connection/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/context/CurrentSessionContext.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/context/CurrentSessionContext.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/context/CurrentSessionContext.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,37 @@
+package org.hibernate.context;
+
+import org.hibernate.HibernateException;
+
+import java.io.Serializable;
+
+/**
+ * Defines the contract for implementations which know how to
+ * scope the notion of a {@link org.hibernate.SessionFactory#getCurrentSession() current session}.
+ * <p/>
+ * Implementations should adhere to the following:
+ * <ul>
+ * <li>contain a constructor accepting a single argument of type
+ * {@link org.hibernate.engine.SessionFactoryImplementor}
+ * <li>should be thread safe
+ * <li>should be fully serializable
+ * </ul>
+ * <p/>
+ * Implementors should be aware that they are also fully responsible for
+ * cleanup of any generated current-sessions.
+ * <p/>
+ * Note that there will be exactly one instance of the configured
+ * CurrentSessionContext implementation per {@link org.hibernate.SessionFactory}.
+ *
+ * @author <a href="mailto:steve at hibernate.org">Steve Ebersole </a>
+ */
+public interface CurrentSessionContext extends Serializable {
+ /**
+ * Retrieve the current session according to the scoping defined
+ * by this implementation.
+ *
+ * @return The current session.
+ * @throws org.hibernate.HibernateException Typically indicates an issue
+ * locating or creating the current session.
+ */
+ public org.hibernate.classic.Session currentSession() throws HibernateException;
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/context/JTASessionContext.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/context/JTASessionContext.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/context/JTASessionContext.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,168 @@
+package org.hibernate.context;
+
+import org.hibernate.HibernateException;
+import org.hibernate.ConnectionReleaseMode;
+import org.hibernate.classic.Session;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.util.JTAHelper;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import javax.transaction.Transaction;
+import javax.transaction.TransactionManager;
+import javax.transaction.Synchronization;
+import java.util.Map;
+import java.util.Hashtable;
+
+/**
+ * An implementation of {@link CurrentSessionContext} which scopes the notion
+ * of a current session to a JTA transaction. Because JTA gives us a nice
+ * tie-in to clean up after ourselves, this implementation will generate
+ * Sessions as needed provided a JTA transaction is in effect. If a session
+ * is not already associated with the current JTA transaction at the time
+ * {@link #currentSession()} is called, a new session will be opened and it
+ * will be associated with that JTA transaction.
+ * <p/>
+ * Note that the sessions returned from this method are automatically configured with
+ * both the {@link org.hibernate.cfg.Environment#FLUSH_BEFORE_COMPLETION auto-flush} and
+ * {@link org.hibernate.cfg.Environment#AUTO_CLOSE_SESSION auto-close} attributes set to
+ * true, meaning that the Session will be automatically flushed and closed
+ * as part of the lifecycle for the JTA transaction to which it is associated.
+ * Additionally, it will also be configured to aggressively release JDBC
+ * connections after each statement is executed. These settings are governed
+ * by the {@link #isAutoFlushEnabled()}, {@link #isAutoCloseEnabled()}, and
+ * {@link #getConnectionReleaseMode()} methods; these are provided (along with
+ * the {@link #buildOrObtainSession()} method) for easier subclassing for custom
+ * JTA-based session tracking logic (like maybe long-session semantics).
+ *
+ * @author <a href="mailto:steve at hibernate.org">Steve Ebersole </a>
+ */
+public class JTASessionContext implements CurrentSessionContext {
+
+ private static final Log log = LogFactory.getLog( JTASessionContext.class );
+
+ protected final SessionFactoryImplementor factory;
+ private transient Map currentSessionMap = new Hashtable();
+
+ public JTASessionContext(SessionFactoryImplementor factory) {
+ this.factory = factory;
+ }
+
+ public Session currentSession() throws HibernateException {
+ TransactionManager transactionManager = factory.getTransactionManager();
+ if ( transactionManager == null ) {
+ throw new HibernateException( "No TransactionManagerLookup specified" );
+ }
+
+ Transaction txn = null;
+ try {
+ txn = transactionManager.getTransaction();
+ if ( txn == null ) {
+ throw new HibernateException( "Unable to locate current JTA transaction" );
+ }
+ if ( !JTAHelper.isInProgress( txn.getStatus() ) ) {
+ // We could register the session against the transaction even though it is
+ // not started, but we'd have no guarentee of ever getting the map
+ // entries cleaned up (aside from spawning threads).
+ throw new HibernateException( "Current transaction is not in progress" );
+ }
+ }
+ catch ( HibernateException e ) {
+ throw e;
+ }
+ catch ( Throwable t ) {
+ throw new HibernateException( "Problem locating/validating JTA transaction", t );
+ }
+
+ Session currentSession = ( Session ) currentSessionMap.get( txn );
+
+ if ( currentSession == null ) {
+ currentSession = buildOrObtainSession();
+
+ try {
+ txn.registerSynchronization( buildCleanupSynch( txn ) );
+ }
+ catch ( Throwable t ) {
+ try {
+ currentSession.close();
+ }
+ catch ( Throwable ignore ) {
+ log.debug( "Unable to release generated current-session on failed synch registration", ignore );
+ }
+ throw new HibernateException( "Unable to register cleanup Synchronization with TransactionManager" );
+ }
+
+ currentSessionMap.put( txn, currentSession );
+ }
+
+ return currentSession;
+ }
+
+ private CleanupSynch buildCleanupSynch(Transaction txn) {
+ return new CleanupSynch( txn, this );
+ }
+
+ /**
+ * Strictly provided for subclassing purposes; specifically to allow long-session
+ * support.
+ * <p/>
+ * This implementation always just opens a new session.
+ *
+ * @return the built or (re)obtained session.
+ */
+ protected Session buildOrObtainSession() {
+ return factory.openSession(
+ null,
+ isAutoFlushEnabled(),
+ isAutoCloseEnabled(),
+ getConnectionReleaseMode()
+ );
+ }
+
+ /**
+ * Mainly for subclass usage. This impl always returns true.
+ *
+ * @return Whether or not the the session should be closed by transaction completion.
+ */
+ protected boolean isAutoCloseEnabled() {
+ return true;
+ }
+
+ /**
+ * Mainly for subclass usage. This impl always returns true.
+ *
+ * @return Whether or not the the session should be flushed prior transaction completion.
+ */
+ protected boolean isAutoFlushEnabled() {
+ return true;
+ }
+
+ /**
+ * Mainly for subclass usage. This impl always returns after_statement.
+ *
+ * @return The connection release mode for any built sessions.
+ */
+ protected ConnectionReleaseMode getConnectionReleaseMode() {
+ return ConnectionReleaseMode.AFTER_STATEMENT;
+ }
+
+ /**
+ * JTA transaction synch used for cleanup of the internal session map.
+ */
+ protected static class CleanupSynch implements Synchronization {
+ private Transaction txn;
+ private JTASessionContext context;
+
+ public CleanupSynch(Transaction txn, JTASessionContext context) {
+ this.txn = txn;
+ this.context = context;
+ }
+
+ public void beforeCompletion() {
+ }
+
+ public void afterCompletion(int i) {
+ context.currentSessionMap.remove( txn );
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/context/ManagedSessionContext.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/context/ManagedSessionContext.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/context/ManagedSessionContext.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,125 @@
+package org.hibernate.context;
+
+import org.hibernate.classic.Session;
+import org.hibernate.HibernateException;
+import org.hibernate.SessionFactory;
+import org.hibernate.engine.SessionFactoryImplementor;
+
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * Represents a {@link CurrentSessionContext} the notion of a contextual session
+ * is managed by some external entity (generally some form of interceptor, etc).
+ * This external manager is responsible for scoping these contextual sessions
+ * appropriately binding/unbinding them here for exposure to the application
+ * through {@link SessionFactory#getCurrentSession} calls.
+ * <p/>
+ * Basically exposes two interfaces. <ul>
+ * <li>First is the implementation of CurrentSessionContext which is then used
+ * by the {@link SessionFactory#getCurrentSession()} calls. This
+ * portion is instance-based specific to the session factory owning the given
+ * instance of this impl (there will be one instance of this per each session
+ * factory using this strategy).
+ * <li>Second is the externally facing methods {@link #hasBind}, {@link #bind},
+ * and {@link #unbind} used by the external thing to manage exposure of the
+ * current session it is scoping. This portion is static to allow easy
+ * reference from that external thing.
+ * </ul>
+ * The underlying storage of the current sessions here is a static
+ * {@link ThreadLocal}-based map where the sessions are keyed by the
+ * the owning session factory.
+ *
+ * @author Steve Ebersole
+ */
+public class ManagedSessionContext implements CurrentSessionContext {
+
+ private static final ThreadLocal context = new ThreadLocal();
+ private final SessionFactoryImplementor factory;
+
+ public ManagedSessionContext(SessionFactoryImplementor factory) {
+ this.factory = factory;
+ }
+
+ /**
+ * @see CurrentSessionContext#currentSession
+ */
+ public Session currentSession() {
+ Session current = existingSession( factory );
+ if ( current == null ) {
+ throw new HibernateException( "No session currently bound to execution context" );
+ }
+ return current;
+ }
+
+ /**
+ * Check to see if there is already a session associated with the current
+ * thread for the given session factory.
+ *
+ * @param factory The factory against which to check for a given session
+ * within the current thread.
+ * @return True if there is currently a session bound.
+ */
+ public static boolean hasBind(SessionFactory factory) {
+ return existingSession( factory ) != null;
+ }
+
+ /**
+ * Binds the given session to the current context for its session factory.
+ *
+ * @param session The session to be bound.
+ * @return Any previously bound session (should be null in most cases).
+ */
+ public static Session bind(Session session) {
+ return ( Session ) sessionMap( true ).put( session.getSessionFactory(), session );
+ }
+
+ /**
+ * Unbinds the session (if one) current associated with the context for the
+ * given session.
+ *
+ * @param factory The factory for which to unbind the current session.
+ * @return The bound session if one, else null.
+ */
+ public static Session unbind(SessionFactory factory) {
+ Session existing = null;
+ Map sessionMap = sessionMap();
+ if ( sessionMap != null ) {
+ existing = ( Session ) sessionMap.remove( factory );
+ doCleanup();
+ }
+ return existing;
+ }
+
+ private static Session existingSession(SessionFactory factory) {
+ Map sessionMap = sessionMap();
+ if ( sessionMap == null ) {
+ return null;
+ }
+ else {
+ return ( Session ) sessionMap.get( factory );
+ }
+ }
+
+ protected static Map sessionMap() {
+ return sessionMap( false );
+ }
+
+ private static synchronized Map sessionMap(boolean createMap) {
+ Map sessionMap = ( Map ) context.get();
+ if ( sessionMap == null && createMap ) {
+ sessionMap = new HashMap();
+ context.set( sessionMap );
+ }
+ return sessionMap;
+ }
+
+ private static synchronized void doCleanup() {
+ Map sessionMap = sessionMap( false );
+ if ( sessionMap != null ) {
+ if ( sessionMap.isEmpty() ) {
+ context.set( null );
+ }
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/context/ThreadLocalSessionContext.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/context/ThreadLocalSessionContext.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/context/ThreadLocalSessionContext.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,339 @@
+package org.hibernate.context;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.HashMap;
+import java.util.Map;
+import javax.transaction.Synchronization;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.ConnectionReleaseMode;
+import org.hibernate.HibernateException;
+import org.hibernate.SessionFactory;
+import org.hibernate.classic.Session;
+import org.hibernate.engine.SessionFactoryImplementor;
+
+/**
+ * A {@link CurrentSessionContext} impl which scopes the notion of current
+ * session by the current thread of execution. Unlike the JTA counterpart,
+ * threads do not give us a nice hook to perform any type of cleanup making
+ * it questionable for this impl to actually generate Session instances. In
+ * the interest of usability, it was decided to have this default impl
+ * actually generate a session upon first request and then clean it up
+ * after the {@link org.hibernate.Transaction} associated with that session
+ * is committed/rolled-back. In order for ensuring that happens, the sessions
+ * generated here are unusable until after {@link Session#beginTransaction()}
+ * has been called. If <tt>close()</tt> is called on a session managed by
+ * this class, it will be automatically unbound.
+ * <p/>
+ * Additionally, the static {@link #bind} and {@link #unbind} methods are
+ * provided to allow application code to explicitly control opening and
+ * closing of these sessions. This, with some from of interception,
+ * is the preferred approach. It also allows easy framework integration
+ * and one possible approach for implementing long-sessions.
+ * <p/>
+ * The {@link #buildOrObtainSession}, {@link #isAutoCloseEnabled},
+ * {@link #isAutoFlushEnabled}, {@link #getConnectionReleaseMode}, and
+ * {@link #buildCleanupSynch} methods are all provided to allow easy
+ * subclassing (for long- running session scenarios, for example).
+ *
+ * @author <a href="mailto:steve at hibernate.org">Steve Ebersole </a>
+ */
+public class ThreadLocalSessionContext implements CurrentSessionContext {
+
+ private static final Log log = LogFactory.getLog( ThreadLocalSessionContext.class );
+ private static final Class[] SESS_PROXY_INTERFACES = new Class[] {
+ org.hibernate.classic.Session.class,
+ org.hibernate.engine.SessionImplementor.class,
+ org.hibernate.jdbc.JDBCContext.Context.class,
+ org.hibernate.event.EventSource.class
+ };
+
+ /**
+ * A ThreadLocal maintaining current sessions for the given execution thread.
+ * The actual ThreadLocal variable is a java.util.Map to account for
+ * the possibility for multiple SessionFactorys being used during execution
+ * of the given thread.
+ */
+ private static final ThreadLocal context = new ThreadLocal();
+
+ protected final SessionFactoryImplementor factory;
+
+ public ThreadLocalSessionContext(SessionFactoryImplementor factory) {
+ this.factory = factory;
+ }
+
+ public final Session currentSession() throws HibernateException {
+ Session current = existingSession( factory );
+ if (current == null) {
+ current = buildOrObtainSession();
+ // register a cleanup synch
+ current.getTransaction().registerSynchronization( buildCleanupSynch() );
+ // wrap the session in the transaction-protection proxy
+ if ( needsWrapping( current ) ) {
+ current = wrap( current );
+ }
+ // then bind it
+ doBind( current, factory );
+ }
+ return current;
+ }
+
+ private boolean needsWrapping(Session session) {
+ // try to make sure we don't wrap and already wrapped session
+ return session != null
+ && ! Proxy.isProxyClass( session.getClass() )
+ || ( Proxy.getInvocationHandler( session ) != null
+ && ! ( Proxy.getInvocationHandler( session ) instanceof TransactionProtectionWrapper ) );
+ }
+
+ protected SessionFactoryImplementor getFactory() {
+ return factory;
+ }
+
+ /**
+ * Strictly provided for subclassing purposes; specifically to allow long-session
+ * support.
+ * <p/>
+ * This implementation always just opens a new session.
+ *
+ * @return the built or (re)obtained session.
+ */
+ protected Session buildOrObtainSession() {
+ return factory.openSession(
+ null,
+ isAutoFlushEnabled(),
+ isAutoCloseEnabled(),
+ getConnectionReleaseMode()
+ );
+ }
+
+ protected CleanupSynch buildCleanupSynch() {
+ return new CleanupSynch( factory );
+ }
+
+ /**
+ * Mainly for subclass usage. This impl always returns true.
+ *
+ * @return Whether or not the the session should be closed by transaction completion.
+ */
+ protected boolean isAutoCloseEnabled() {
+ return true;
+ }
+
+ /**
+ * Mainly for subclass usage. This impl always returns true.
+ *
+ * @return Whether or not the the session should be flushed prior transaction completion.
+ */
+ protected boolean isAutoFlushEnabled() {
+ return true;
+ }
+
+ /**
+ * Mainly for subclass usage. This impl always returns after_transaction.
+ *
+ * @return The connection release mode for any built sessions.
+ */
+ protected ConnectionReleaseMode getConnectionReleaseMode() {
+ return factory.getSettings().getConnectionReleaseMode();
+ }
+
+ protected Session wrap(Session session) {
+ TransactionProtectionWrapper wrapper = new TransactionProtectionWrapper( session );
+ Session wrapped = ( Session ) Proxy.newProxyInstance(
+ Session.class.getClassLoader(),
+ SESS_PROXY_INTERFACES,
+ wrapper
+ );
+ // yick! need this for proper serialization/deserialization handling...
+ wrapper.setWrapped( wrapped );
+ return wrapped;
+ }
+
+ /**
+ * Associates the given session with the current thread of execution.
+ *
+ * @param session The session to bind.
+ */
+ public static void bind(org.hibernate.Session session) {
+ SessionFactory factory = session.getSessionFactory();
+ cleanupAnyOrphanedSession( factory );
+ doBind( session, factory );
+ }
+
+ private static void cleanupAnyOrphanedSession(SessionFactory factory) {
+ Session orphan = doUnbind( factory, false );
+ if ( orphan != null ) {
+ log.warn( "Already session bound on call to bind(); make sure you clean up your sessions!" );
+ try {
+ if ( orphan.getTransaction() != null && orphan.getTransaction().isActive() ) {
+ try {
+ orphan.getTransaction().rollback();
+ }
+ catch( Throwable t ) {
+ log.debug( "Unable to rollback transaction for orphaned session", t );
+ }
+ }
+ orphan.close();
+ }
+ catch( Throwable t ) {
+ log.debug( "Unable to close orphaned session", t );
+ }
+ }
+ }
+
+ /**
+ * Unassociate a previously bound session from the current thread of execution.
+ *
+ * @return The session which was unbound.
+ */
+ public static Session unbind(SessionFactory factory) {
+ return doUnbind( factory, true );
+ }
+
+ private static Session existingSession(SessionFactory factory) {
+ Map sessionMap = sessionMap();
+ if ( sessionMap == null ) {
+ return null;
+ }
+ else {
+ return ( Session ) sessionMap.get( factory );
+ }
+ }
+
+ protected static Map sessionMap() {
+ return ( Map ) context.get();
+ }
+
+ private static void doBind(org.hibernate.Session session, SessionFactory factory) {
+ Map sessionMap = sessionMap();
+ if ( sessionMap == null ) {
+ sessionMap = new HashMap();
+ context.set( sessionMap );
+ }
+ sessionMap.put( factory, session );
+ }
+
+ private static Session doUnbind(SessionFactory factory, boolean releaseMapIfEmpty) {
+ Map sessionMap = sessionMap();
+ Session session = null;
+ if ( sessionMap != null ) {
+ session = ( Session ) sessionMap.remove( factory );
+ if ( releaseMapIfEmpty && sessionMap.isEmpty() ) {
+ context.set( null );
+ }
+ }
+ return session;
+ }
+
+ /**
+ * JTA transaction synch used for cleanup of the internal session map.
+ */
+ protected static class CleanupSynch implements Synchronization, Serializable {
+ protected final SessionFactory factory;
+
+ public CleanupSynch(SessionFactory factory) {
+ this.factory = factory;
+ }
+
+ public void beforeCompletion() {
+ }
+
+ public void afterCompletion(int i) {
+ unbind( factory );
+ }
+ }
+
+ private class TransactionProtectionWrapper implements InvocationHandler, Serializable {
+ private final Session realSession;
+ private Session wrappedSession;
+
+ public TransactionProtectionWrapper(Session realSession) {
+ this.realSession = realSession;
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ try {
+ // If close() is called, guarantee unbind()
+ if ( "close".equals( method.getName()) ) {
+ unbind( realSession.getSessionFactory() );
+ }
+ else if ( "toString".equals( method.getName() )
+ || "equals".equals( method.getName() )
+ || "hashCode".equals( method.getName() )
+ || "getStatistics".equals( method.getName() )
+ || "isOpen".equals( method.getName() ) ) {
+ // allow these to go through the the real session no matter what
+ }
+ else if ( !realSession.isOpen() ) {
+ // essentially, if the real session is closed allow any
+ // method call to pass through since the real session
+ // will complain by throwing an appropriate exception;
+ // NOTE that allowing close() above has the same basic effect,
+ // but we capture that there simply to perform the unbind...
+ }
+ else if ( !realSession.getTransaction().isActive() ) {
+ // limit the methods available if no transaction is active
+ if ( "beginTransaction".equals( method.getName() )
+ || "getTransaction".equals( method.getName() )
+ || "isTransactionInProgress".equals( method.getName() )
+ || "setFlushMode".equals( method.getName() )
+ || "getSessionFactory".equals( method.getName() ) ) {
+ log.trace( "allowing method [" + method.getName() + "] in non-transacted context" );
+ }
+ else if ( "reconnect".equals( method.getName() )
+ || "disconnect".equals( method.getName() ) ) {
+ // allow these (deprecated) methods to pass through
+ }
+ else {
+ throw new HibernateException( method.getName() + " is not valid without active transaction" );
+ }
+ }
+ log.trace( "allowing proxied method [" + method.getName() + "] to proceed to real session" );
+ return method.invoke( realSession, args );
+ }
+ catch ( InvocationTargetException e ) {
+ if ( e.getTargetException() instanceof RuntimeException ) {
+ throw ( RuntimeException ) e.getTargetException();
+ }
+ else {
+ throw e;
+ }
+ }
+ }
+
+ public void setWrapped(Session wrapped) {
+ this.wrappedSession = wrapped;
+ }
+
+
+ // serialization ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ private void writeObject(ObjectOutputStream oos) throws IOException {
+ // if a ThreadLocalSessionContext-bound session happens to get
+ // serialized, to be completely correct, we need to make sure
+ // that unbinding of that session occurs.
+ oos.defaultWriteObject();
+ if ( existingSession( factory ) == wrappedSession ) {
+ unbind( factory );
+ }
+ }
+
+ private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
+ // on the inverse, it makes sense that if a ThreadLocalSessionContext-
+ // bound session then gets deserialized to go ahead and re-bind it to
+ // the ThreadLocalSessionContext session map.
+ ois.defaultReadObject();
+ realSession.getTransaction().registerSynchronization( buildCleanupSynch() );
+ doBind( wrappedSession, factory );
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/AbstractEmptinessExpression.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/AbstractEmptinessExpression.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/AbstractEmptinessExpression.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,86 @@
+// $Id: AbstractEmptinessExpression.java 6670 2005-05-03 22:19:00Z steveebersole $
+package org.hibernate.criterion;
+
+import org.hibernate.Criteria;
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.QueryException;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.TypedValue;
+import org.hibernate.persister.collection.QueryableCollection;
+import org.hibernate.persister.entity.Loadable;
+import org.hibernate.persister.entity.PropertyMapping;
+import org.hibernate.sql.ConditionFragment;
+import org.hibernate.type.CollectionType;
+import org.hibernate.type.Type;
+
+/**
+ * Implementation of AbstractEmptinessExpression.
+ *
+ * @author Steve Ebersole
+ */
+public abstract class AbstractEmptinessExpression implements Criterion {
+
+ private static final TypedValue[] NO_VALUES = new TypedValue[0];
+
+ protected final String propertyName;
+
+ protected AbstractEmptinessExpression(String propertyName) {
+ this.propertyName = propertyName;
+ }
+
+ protected abstract boolean excludeEmpty();
+
+ public final String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException {
+ String entityName = criteriaQuery.getEntityName( criteria, propertyName );
+ String actualPropertyName = criteriaQuery.getPropertyName( propertyName );
+ String sqlAlias = criteriaQuery.getSQLAlias( criteria, propertyName );
+
+ SessionFactoryImplementor factory = criteriaQuery.getFactory();
+ QueryableCollection collectionPersister = getQueryableCollection( entityName, actualPropertyName, factory );
+
+ String[] collectionKeys = collectionPersister.getKeyColumnNames();
+ String[] ownerKeys = ( ( Loadable ) factory.getEntityPersister( entityName ) ).getIdentifierColumnNames();
+
+ String innerSelect = "(select 1 from " + collectionPersister.getTableName()
+ + " where "
+ + new ConditionFragment().setTableAlias( sqlAlias ).setCondition( ownerKeys, collectionKeys ).toFragmentString()
+ + ")";
+
+ return excludeEmpty()
+ ? "exists " + innerSelect
+ : "not exists " + innerSelect;
+ }
+
+
+ protected QueryableCollection getQueryableCollection(String entityName, String propertyName, SessionFactoryImplementor factory)
+ throws HibernateException {
+ PropertyMapping ownerMapping = ( PropertyMapping ) factory.getEntityPersister( entityName );
+ Type type = ownerMapping.toType( propertyName );
+ if ( !type.isCollectionType() ) {
+ throw new MappingException(
+ "Property path [" + entityName + "." + propertyName + "] does not reference a collection"
+ );
+ }
+
+ String role = ( ( CollectionType ) type ).getRole();
+ try {
+ return ( QueryableCollection ) factory.getCollectionPersister( role );
+ }
+ catch ( ClassCastException cce ) {
+ throw new QueryException( "collection role is not queryable: " + role );
+ }
+ catch ( Exception e ) {
+ throw new QueryException( "collection role not found: " + role );
+ }
+ }
+
+ public final TypedValue[] getTypedValues(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ return NO_VALUES;
+ }
+
+ public final String toString() {
+ return propertyName + ( excludeEmpty() ? " is not empty" : " is empty" );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/AggregateProjection.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/AggregateProjection.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/AggregateProjection.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,43 @@
+//$Id: AggregateProjection.java 9908 2006-05-08 20:59:20Z max.andersen at jboss.com $
+package org.hibernate.criterion;
+
+import org.hibernate.Criteria;
+import org.hibernate.HibernateException;
+import org.hibernate.type.Type;
+
+/**
+ * An aggregation
+ * @author max
+ */
+public class AggregateProjection extends SimpleProjection {
+
+ protected final String propertyName;
+ private final String aggregate;
+
+ protected AggregateProjection(String aggregate, String propertyName) {
+ this.aggregate = aggregate;
+ this.propertyName = propertyName;
+ }
+
+ public String toString() {
+ return aggregate + "(" + propertyName + ')';
+ }
+
+ public Type[] getTypes(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ return new Type[] { criteriaQuery.getType(criteria, propertyName) };
+ }
+
+ public String toSqlString(Criteria criteria, int loc, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ return new StringBuffer()
+ .append(aggregate)
+ .append("(")
+ .append( criteriaQuery.getColumn(criteria, propertyName) )
+ .append(") as y")
+ .append(loc)
+ .append('_')
+ .toString();
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/AliasedProjection.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/AliasedProjection.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/AliasedProjection.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,65 @@
+//$Id: AliasedProjection.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate.criterion;
+
+import org.hibernate.Criteria;
+import org.hibernate.HibernateException;
+import org.hibernate.type.Type;
+
+/**
+ * @author Gavin King
+ */
+public class AliasedProjection implements Projection {
+
+ private final Projection projection;
+ private final String alias;
+
+ public String toString() {
+ return projection.toString() + " as " + alias;
+ }
+
+ protected AliasedProjection(Projection projection, String alias) {
+ this.projection = projection;
+ this.alias = alias;
+ }
+
+ public String toSqlString(Criteria criteria, int position, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ return projection.toSqlString(criteria, position, criteriaQuery);
+ }
+
+ public String toGroupSqlString(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ return projection.toGroupSqlString(criteria, criteriaQuery);
+ }
+
+ public Type[] getTypes(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ return projection.getTypes(criteria, criteriaQuery);
+ }
+
+ public String[] getColumnAliases(int loc) {
+ return projection.getColumnAliases(loc);
+ }
+
+ public Type[] getTypes(String alias, Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ return this.alias.equals(alias) ?
+ getTypes(criteria, criteriaQuery) :
+ null;
+ }
+
+ public String[] getColumnAliases(String alias, int loc) {
+ return this.alias.equals(alias) ?
+ getColumnAliases(loc) :
+ null;
+ }
+
+ public String[] getAliases() {
+ return new String[]{ alias };
+ }
+
+ public boolean isGrouped() {
+ return projection.isGrouped();
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/AliasedProjection.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/AvgProjection.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/AvgProjection.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/AvgProjection.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+//$Id: AvgProjection.java 9908 2006-05-08 20:59:20Z max.andersen at jboss.com $
+package org.hibernate.criterion;
+
+import org.hibernate.Criteria;
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.type.Type;
+
+/**
+ * @author Gavin King
+ */
+public class AvgProjection extends AggregateProjection {
+
+ public AvgProjection(String propertyName) {
+ super("avg", propertyName);
+ }
+
+ public Type[] getTypes(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ return new Type[] { Hibernate.DOUBLE };
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/AvgProjection.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/BetweenExpression.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/BetweenExpression.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/BetweenExpression.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,48 @@
+//$Id: BetweenExpression.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate.criterion;
+
+
+import org.hibernate.Criteria;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.TypedValue;
+import org.hibernate.util.StringHelper;
+
+/**
+ * Constrains a property to between two values
+ * @author Gavin King
+ */
+public class BetweenExpression implements Criterion {
+
+ private final String propertyName;
+ private final Object lo;
+ private final Object hi;
+
+ protected BetweenExpression(String propertyName, Object lo, Object hi) {
+ this.propertyName = propertyName;
+ this.lo = lo;
+ this.hi = hi;
+ }
+
+ public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ return StringHelper.join(
+ " and ",
+ StringHelper.suffix( criteriaQuery.getColumnsUsingProjection(criteria, propertyName), " between ? and ?" )
+ );
+
+ //TODO: get SQL rendering out of this package!
+ }
+
+ public TypedValue[] getTypedValues(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ return new TypedValue[] {
+ criteriaQuery.getTypedValue(criteria, propertyName, lo),
+ criteriaQuery.getTypedValue(criteria, propertyName, hi)
+ };
+ }
+
+ public String toString() {
+ return propertyName + " between " + lo + " and " + hi;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Conjunction.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Conjunction.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Conjunction.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,13 @@
+package org.hibernate.criterion;
+
+
+/**
+ * @author Gavin King
+ */
+public class Conjunction extends Junction {
+
+ public Conjunction() {
+ super("and");
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/CountProjection.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/CountProjection.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/CountProjection.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,51 @@
+//$Id: CountProjection.java 9913 2006-05-09 07:40:11Z max.andersen at jboss.com $
+package org.hibernate.criterion;
+
+import org.hibernate.Criteria;
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.type.Type;
+
+/**
+ * A count
+ * @author Gavin King
+ */
+public class CountProjection extends AggregateProjection {
+
+ private boolean distinct;
+
+ protected CountProjection(String prop) {
+ super("count", prop);
+ }
+
+ public String toString() {
+ if(distinct) {
+ return "distinct " + super.toString();
+ } else {
+ return super.toString();
+ }
+ }
+
+ public Type[] getTypes(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ return new Type[] { Hibernate.INTEGER };
+ }
+
+ public String toSqlString(Criteria criteria, int position, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ StringBuffer buf = new StringBuffer();
+ buf.append("count(");
+ if (distinct) buf.append("distinct ");
+ return buf.append( criteriaQuery.getColumn(criteria, propertyName) )
+ .append(") as y")
+ .append(position)
+ .append('_')
+ .toString();
+ }
+
+ public CountProjection setDistinct() {
+ distinct = true;
+ return this;
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/CountProjection.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/CriteriaQuery.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/CriteriaQuery.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/CriteriaQuery.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,92 @@
+//$Id: CriteriaQuery.java 6970 2005-05-31 20:24:41Z oneovthafew $
+package org.hibernate.criterion;
+
+import org.hibernate.Criteria;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.TypedValue;
+import org.hibernate.type.Type;
+
+/**
+ * An instance of <tt>CriteriaQuery</tt> is passed to criterion,
+ * order and projection instances when actually compiling and
+ * executing the query. This interface is not used by application
+ * code.
+ *
+ * @author Gavin King
+ */
+public interface CriteriaQuery {
+ public SessionFactoryImplementor getFactory();
+
+ /**
+ * Get the names of the columns mapped by a property path,
+ * ignoring projection aliases
+ */
+ public String getColumn(Criteria criteria, String propertyPath)
+ throws HibernateException;
+
+ /**
+ * Get the type of a property path, ignoring projection aliases
+ */
+ public Type getType(Criteria criteria, String propertyPath)
+ throws HibernateException;
+
+ /**
+ * Get the names of the columns mapped by a property path
+ */
+ public String[] getColumnsUsingProjection(Criteria criteria, String propertyPath)
+ throws HibernateException;
+
+ /**
+ * Get the type of a property path
+ */
+ public Type getTypeUsingProjection(Criteria criteria, String propertyPath)
+ throws HibernateException;
+
+ /**
+ * Get the a typed value for the given property value.
+ */
+ public TypedValue getTypedValue(Criteria criteria, String propertyPath, Object value)
+ throws HibernateException;
+
+ /**
+ * Get the entity name of an entity
+ */
+ public String getEntityName(Criteria criteria);
+
+ /**
+ * Get the entity name of an entity, taking into account
+ * the qualifier of the property path
+ */
+ public String getEntityName(Criteria criteria, String propertyPath);
+
+ /**
+ * Get the root table alias of an entity
+ */
+ public String getSQLAlias(Criteria subcriteria);
+
+ /**
+ * Get the root table alias of an entity, taking into account
+ * the qualifier of the property path
+ */
+ public String getSQLAlias(Criteria criteria, String propertyPath);
+
+ /**
+ * Get the property name, given a possibly qualified property name
+ */
+ public String getPropertyName(String propertyName);
+
+ /**
+ * Get the identifier column names of this entity
+ */
+ public String[] getIdentifierColumns(Criteria subcriteria);
+
+ /**
+ * Get the identifier type of this entity
+ */
+ public Type getIdentifierType(Criteria subcriteria);
+
+ public TypedValue getTypedIdentifierValue(Criteria subcriteria, Object value);
+
+ public String generateSQLAlias();
+}
\ No newline at end of file
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/CriteriaQuery.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/CriteriaSpecification.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/CriteriaSpecification.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/CriteriaSpecification.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,55 @@
+//$Id: CriteriaSpecification.java 9116 2006-01-23 21:21:01Z steveebersole $
+package org.hibernate.criterion;
+
+import org.hibernate.transform.AliasToEntityMapResultTransformer;
+import org.hibernate.transform.DistinctRootEntityResultTransformer;
+import org.hibernate.transform.PassThroughResultTransformer;
+import org.hibernate.transform.ResultTransformer;
+import org.hibernate.transform.RootEntityResultTransformer;
+
+/**
+ * @author Gavin King
+ */
+public interface CriteriaSpecification {
+
+ /**
+ * The alias that refers to the "root" entity of the criteria query.
+ */
+ public static final String ROOT_ALIAS = "this";
+
+ /**
+ * Each row of results is a <tt>Map</tt> from alias to entity instance
+ */
+ public static final ResultTransformer ALIAS_TO_ENTITY_MAP = new AliasToEntityMapResultTransformer();
+
+ /**
+ * Each row of results is an instance of the root entity
+ */
+ public static final ResultTransformer ROOT_ENTITY = new RootEntityResultTransformer();
+
+ /**
+ * Each row of results is a distinct instance of the root entity
+ */
+ public static final ResultTransformer DISTINCT_ROOT_ENTITY = new DistinctRootEntityResultTransformer();
+
+ /**
+ * This result transformer is selected implicitly by calling <tt>setProjection()</tt>
+ */
+ public static final ResultTransformer PROJECTION = new PassThroughResultTransformer();
+
+ /**
+ * Specifies joining to an entity based on an inner join.
+ */
+ public static final int INNER_JOIN = org.hibernate.sql.JoinFragment.INNER_JOIN;
+
+ /**
+ * Specifies joining to an entity based on a full join.
+ */
+ public static final int FULL_JOIN = org.hibernate.sql.JoinFragment.FULL_JOIN;
+
+ /**
+ * Specifies joining to an entity based on a left outer join.
+ */
+ public static final int LEFT_JOIN = org.hibernate.sql.JoinFragment.LEFT_OUTER_JOIN;
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/CriteriaSpecification.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Criterion.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Criterion.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Criterion.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,44 @@
+//$Id: Criterion.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate.criterion;
+
+
+import java.io.Serializable;
+
+import org.hibernate.Criteria;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.TypedValue;
+
+/**
+ * An object-oriented representation of a query criterion that may be used
+ * as a restriction in a <tt>Criteria</tt> query.
+ * Built-in criterion types are provided by the <tt>Restrictions</tt> factory
+ * class. This interface might be implemented by application classes that
+ * define custom restriction criteria.
+ *
+ * @see Restrictions
+ * @see org.hibernate.Criteria
+ * @author Gavin King
+ */
+public interface Criterion extends Serializable {
+
+ /**
+ * Render the SQL fragment
+ * @param criteriaQuery
+ * @param alias
+ * @return String
+ * @throws HibernateException
+ */
+ public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException;
+
+ /**
+ * Return typed values for all parameters in the rendered SQL fragment
+ * @param criteria TODO
+ * @param criteriaQuery
+ * @return TypedValue[]
+ * @throws HibernateException
+ */
+ public TypedValue[] getTypedValues(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException;
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/DetachedCriteria.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/DetachedCriteria.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/DetachedCriteria.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,125 @@
+//$Id: DetachedCriteria.java 8757 2005-12-06 03:35:50Z steveebersole $
+package org.hibernate.criterion;
+
+import java.io.Serializable;
+
+import org.hibernate.Criteria;
+import org.hibernate.FetchMode;
+import org.hibernate.HibernateException;
+import org.hibernate.Session;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.impl.CriteriaImpl;
+import org.hibernate.impl.SessionImpl;
+import org.hibernate.transform.ResultTransformer;
+
+/**
+ * Some applications need to create criteria queries in "detached
+ * mode", where the Hibernate session is not available. This class
+ * may be instantiated anywhere, and then a <literal>Criteria</literal>
+ * may be obtained by passing a session to
+ * <literal>getExecutableCriteria()</literal>. All methods have the
+ * same semantics and behavior as the corresponding methods of the
+ * <literal>Criteria</literal> interface.
+ *
+ * @see org.hibernate.Criteria
+ * @author Gavin King
+ */
+public class DetachedCriteria implements CriteriaSpecification, Serializable {
+
+ private final CriteriaImpl impl;
+ private final Criteria criteria;
+
+ protected DetachedCriteria(String entityName) {
+ impl = new CriteriaImpl(entityName, null);
+ criteria = impl;
+ }
+
+ protected DetachedCriteria(String entityName, String alias) {
+ impl = new CriteriaImpl(entityName, alias, null);
+ criteria = impl;
+ }
+
+ protected DetachedCriteria(CriteriaImpl impl, Criteria criteria) {
+ this.impl = impl;
+ this.criteria = criteria;
+ }
+
+ /**
+ * Get an executable instance of <literal>Criteria</literal>,
+ * to actually run the query.
+ */
+ public Criteria getExecutableCriteria(Session session) {
+ impl.setSession( ( SessionImplementor ) session );
+ return impl;
+ }
+
+ public static DetachedCriteria forEntityName(String entityName) {
+ return new DetachedCriteria(entityName);
+ }
+
+ public static DetachedCriteria forEntityName(String entityName, String alias) {
+ return new DetachedCriteria(entityName, alias);
+ }
+
+ public static DetachedCriteria forClass(Class clazz) {
+ return new DetachedCriteria( clazz.getName() );
+ }
+
+ public static DetachedCriteria forClass(Class clazz, String alias) {
+ return new DetachedCriteria( clazz.getName() , alias );
+ }
+
+ public DetachedCriteria add(Criterion criterion) {
+ criteria.add(criterion);
+ return this;
+ }
+
+ public DetachedCriteria addOrder(Order order) {
+ criteria.addOrder(order);
+ return this;
+ }
+
+ public DetachedCriteria createAlias(String associationPath, String alias)
+ throws HibernateException {
+ criteria.createAlias(associationPath, alias);
+ return this;
+ }
+
+ public DetachedCriteria createCriteria(String associationPath, String alias)
+ throws HibernateException {
+ return new DetachedCriteria( impl, criteria.createCriteria(associationPath) );
+ }
+
+ public DetachedCriteria createCriteria(String associationPath)
+ throws HibernateException {
+ return new DetachedCriteria( impl, criteria.createCriteria(associationPath) );
+ }
+
+ public String getAlias() {
+ return criteria.getAlias();
+ }
+
+ public DetachedCriteria setFetchMode(String associationPath, FetchMode mode)
+ throws HibernateException {
+ criteria.setFetchMode(associationPath, mode);
+ return this;
+ }
+
+ public DetachedCriteria setProjection(Projection projection) {
+ criteria.setProjection(projection);
+ return this;
+ }
+
+ public DetachedCriteria setResultTransformer(ResultTransformer resultTransformer) {
+ criteria.setResultTransformer(resultTransformer);
+ return this;
+ }
+
+ public String toString() {
+ return "DetachableCriteria(" + criteria.toString() + ')';
+ }
+
+ CriteriaImpl getCriteriaImpl() {
+ return impl;
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/DetachedCriteria.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Disjunction.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Disjunction.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Disjunction.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,13 @@
+package org.hibernate.criterion;
+
+
+/**
+ * @author Gavin King
+ */
+public class Disjunction extends Junction {
+
+ protected Disjunction() {
+ super("or");
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Distinct.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Distinct.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Distinct.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,58 @@
+//$Id: Distinct.java 6452 2005-04-18 17:16:57Z oneovthafew $
+package org.hibernate.criterion;
+
+import org.hibernate.Criteria;
+import org.hibernate.HibernateException;
+import org.hibernate.type.Type;
+
+/**
+ * @author Gavin King
+ */
+public class Distinct implements Projection {
+
+ private final Projection projection;
+
+ public Distinct(Projection proj) {
+ this.projection = proj;
+ }
+
+ public String toSqlString(Criteria criteria, int position, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ return "distinct " + projection.toSqlString(criteria, position, criteriaQuery);
+ }
+
+ public String toGroupSqlString(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ return projection.toGroupSqlString(criteria, criteriaQuery);
+ }
+
+ public Type[] getTypes(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ return projection.getTypes(criteria, criteriaQuery);
+ }
+
+ public Type[] getTypes(String alias, Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ return projection.getTypes(alias, criteria, criteriaQuery);
+ }
+
+ public String[] getColumnAliases(int loc) {
+ return projection.getColumnAliases(loc);
+ }
+
+ public String[] getColumnAliases(String alias, int loc) {
+ return projection.getColumnAliases(alias, loc);
+ }
+
+ public String[] getAliases() {
+ return projection.getAliases();
+ }
+
+ public boolean isGrouped() {
+ return projection.isGrouped();
+ }
+
+ public String toString() {
+ return "distinct " + projection.toString();
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Distinct.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/EmptyExpression.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/EmptyExpression.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/EmptyExpression.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,17 @@
+//$Id: EmptyExpression.java 6661 2005-05-03 20:12:20Z steveebersole $
+package org.hibernate.criterion;
+
+/**
+ * @author Gavin King
+ */
+public class EmptyExpression extends AbstractEmptinessExpression implements Criterion {
+
+ protected EmptyExpression(String propertyName) {
+ super( propertyName );
+ }
+
+ protected boolean excludeEmpty() {
+ return false;
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/EmptyExpression.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Example.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Example.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Example.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,374 @@
+//$Id: Example.java 10061 2006-06-28 05:20:51Z steve.ebersole at jboss.com $
+package org.hibernate.criterion;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.hibernate.Criteria;
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.TypedValue;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.type.AbstractComponentType;
+import org.hibernate.type.Type;
+import org.hibernate.util.StringHelper;
+
+/**
+ * Support for query by example.
+ * <pre>
+ * List results = session.createCriteria(Parent.class)
+ * .add( Example.create(parent).ignoreCase() )
+ * .createCriteria("child")
+ * .add( Example.create( parent.getChild() ) )
+ * .list();
+ * </pre>
+ * "Examples" may be mixed and matched with "Expressions" in the same <tt>Criteria</tt>.
+ * @see org.hibernate.Criteria
+ * @author Gavin King
+ */
+
+public class Example implements Criterion {
+
+ private final Object entity;
+ private final Set excludedProperties = new HashSet();
+ private PropertySelector selector;
+ private boolean isLikeEnabled;
+ private Character escapeCharacter;
+ private boolean isIgnoreCaseEnabled;
+ private MatchMode matchMode;
+
+ /**
+ * A strategy for choosing property values for inclusion in the query
+ * criteria
+ */
+
+ public static interface PropertySelector extends Serializable {
+ public boolean include(Object propertyValue, String propertyName, Type type);
+ }
+
+ private static final PropertySelector NOT_NULL = new NotNullPropertySelector();
+ private static final PropertySelector ALL = new AllPropertySelector();
+ private static final PropertySelector NOT_NULL_OR_ZERO = new NotNullOrZeroPropertySelector();
+
+ static final class AllPropertySelector implements PropertySelector {
+ public boolean include(Object object, String propertyName, Type type) {
+ return true;
+ }
+
+ private Object readResolve() {
+ return ALL;
+ }
+ }
+
+ static final class NotNullPropertySelector implements PropertySelector {
+ public boolean include(Object object, String propertyName, Type type) {
+ return object!=null;
+ }
+
+ private Object readResolve() {
+ return NOT_NULL;
+ }
+ }
+
+ static final class NotNullOrZeroPropertySelector implements PropertySelector {
+ public boolean include(Object object, String propertyName, Type type) {
+ return object!=null && (
+ !(object instanceof Number) || ( (Number) object ).longValue()!=0
+ );
+ }
+
+ private Object readResolve() {
+ return NOT_NULL_OR_ZERO;
+ }
+ }
+
+ /**
+ * Set escape character for "like" clause
+ */
+ public Example setEscapeCharacter(Character escapeCharacter) {
+ this.escapeCharacter = escapeCharacter;
+ return this;
+ }
+
+ /**
+ * Set the property selector
+ */
+ public Example setPropertySelector(PropertySelector selector) {
+ this.selector = selector;
+ return this;
+ }
+
+ /**
+ * Exclude zero-valued properties
+ */
+ public Example excludeZeroes() {
+ setPropertySelector(NOT_NULL_OR_ZERO);
+ return this;
+ }
+
+ /**
+ * Don't exclude null or zero-valued properties
+ */
+ public Example excludeNone() {
+ setPropertySelector(ALL);
+ return this;
+ }
+
+ /**
+ * Use the "like" operator for all string-valued properties
+ */
+ public Example enableLike(MatchMode matchMode) {
+ isLikeEnabled = true;
+ this.matchMode = matchMode;
+ return this;
+ }
+
+ /**
+ * Use the "like" operator for all string-valued properties
+ */
+ public Example enableLike() {
+ return enableLike(MatchMode.EXACT);
+ }
+
+ /**
+ * Ignore case for all string-valued properties
+ */
+ public Example ignoreCase() {
+ isIgnoreCaseEnabled = true;
+ return this;
+ }
+
+ /**
+ * Exclude a particular named property
+ */
+ public Example excludeProperty(String name) {
+ excludedProperties.add(name);
+ return this;
+ }
+
+ /**
+ * Create a new instance, which includes all non-null properties
+ * by default
+ * @param entity
+ * @return a new instance of <tt>Example</tt>
+ */
+ public static Example create(Object entity) {
+ if (entity==null) throw new NullPointerException("null example");
+ return new Example(entity, NOT_NULL);
+ }
+
+ protected Example(Object entity, PropertySelector selector) {
+ this.entity = entity;
+ this.selector = selector;
+ }
+
+ public String toString() {
+ return "example (" + entity + ')';
+ }
+
+ private boolean isPropertyIncluded(Object value, String name, Type type) {
+ return !excludedProperties.contains(name) &&
+ !type.isAssociationType() &&
+ selector.include(value, name, type);
+ }
+
+ public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+
+ StringBuffer buf = new StringBuffer().append('(');
+ EntityPersister meta = criteriaQuery.getFactory().getEntityPersister( criteriaQuery.getEntityName(criteria) );
+ String[] propertyNames = meta.getPropertyNames();
+ Type[] propertyTypes = meta.getPropertyTypes();
+ //TODO: get all properties, not just the fetched ones!
+ Object[] propertyValues = meta.getPropertyValues( entity, getEntityMode(criteria, criteriaQuery) );
+ for (int i=0; i<propertyNames.length; i++) {
+ Object propertyValue = propertyValues[i];
+ String propertyName = propertyNames[i];
+
+ boolean isPropertyIncluded = i!=meta.getVersionProperty() &&
+ isPropertyIncluded( propertyValue, propertyName, propertyTypes[i] );
+ if (isPropertyIncluded) {
+ if ( propertyTypes[i].isComponentType() ) {
+ appendComponentCondition(
+ propertyName,
+ propertyValue,
+ (AbstractComponentType) propertyTypes[i],
+ criteria,
+ criteriaQuery,
+ buf
+ );
+ }
+ else {
+ appendPropertyCondition(
+ propertyName,
+ propertyValue,
+ criteria,
+ criteriaQuery,
+ buf
+ );
+ }
+ }
+ }
+ if ( buf.length()==1 ) buf.append("1=1"); //yuck!
+ return buf.append(')').toString();
+ }
+
+ private static final Object[] TYPED_VALUES = new TypedValue[0];
+
+ public TypedValue[] getTypedValues(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+
+ EntityPersister meta = criteriaQuery.getFactory()
+ .getEntityPersister( criteriaQuery.getEntityName(criteria) );
+ String[] propertyNames = meta.getPropertyNames();
+ Type[] propertyTypes = meta.getPropertyTypes();
+ //TODO: get all properties, not just the fetched ones!
+ Object[] values = meta.getPropertyValues( entity, getEntityMode(criteria, criteriaQuery) );
+ List list = new ArrayList();
+ for (int i=0; i<propertyNames.length; i++) {
+ Object value = values[i];
+ Type type = propertyTypes[i];
+ String name = propertyNames[i];
+
+ boolean isPropertyIncluded = i!=meta.getVersionProperty() &&
+ isPropertyIncluded(value, name, type);
+
+ if (isPropertyIncluded) {
+ if ( propertyTypes[i].isComponentType() ) {
+ addComponentTypedValues(name, value, (AbstractComponentType) type, list, criteria, criteriaQuery);
+ }
+ else {
+ addPropertyTypedValue(value, type, list);
+ }
+ }
+ }
+ return (TypedValue[]) list.toArray(TYPED_VALUES);
+ }
+
+ private EntityMode getEntityMode(Criteria criteria, CriteriaQuery criteriaQuery) {
+ EntityPersister meta = criteriaQuery.getFactory()
+ .getEntityPersister( criteriaQuery.getEntityName(criteria) );
+ EntityMode result = meta.guessEntityMode(entity);
+ if (result==null) {
+ throw new ClassCastException( entity.getClass().getName() );
+ }
+ return result;
+ }
+
+ protected void addPropertyTypedValue(Object value, Type type, List list) {
+ if ( value!=null ) {
+ if ( value instanceof String ) {
+ String string = (String) value;
+ if (isIgnoreCaseEnabled) string = string.toLowerCase();
+ if (isLikeEnabled) string = matchMode.toMatchString(string);
+ value = string;
+ }
+ list.add( new TypedValue(type, value, null) );
+ }
+ }
+
+ protected void addComponentTypedValues(
+ String path,
+ Object component,
+ AbstractComponentType type,
+ List list,
+ Criteria criteria,
+ CriteriaQuery criteriaQuery)
+ throws HibernateException {
+
+ if (component!=null) {
+ String[] propertyNames = type.getPropertyNames();
+ Type[] subtypes = type.getSubtypes();
+ Object[] values = type.getPropertyValues( component, getEntityMode(criteria, criteriaQuery) );
+ for (int i=0; i<propertyNames.length; i++) {
+ Object value = values[i];
+ Type subtype = subtypes[i];
+ String subpath = StringHelper.qualify( path, propertyNames[i] );
+ if ( isPropertyIncluded(value, subpath, subtype) ) {
+ if ( subtype.isComponentType() ) {
+ addComponentTypedValues(subpath, value, (AbstractComponentType) subtype, list, criteria, criteriaQuery);
+ }
+ else {
+ addPropertyTypedValue(value, subtype, list);
+ }
+ }
+ }
+ }
+ }
+
+ protected void appendPropertyCondition(
+ String propertyName,
+ Object propertyValue,
+ Criteria criteria,
+ CriteriaQuery cq,
+ StringBuffer buf)
+ throws HibernateException {
+ Criterion crit;
+ if ( propertyValue!=null ) {
+ boolean isString = propertyValue instanceof String;
+ if ( isLikeEnabled && isString ) {
+ crit = new LikeExpression(
+ propertyName,
+ ( String ) propertyValue,
+ matchMode,
+ escapeCharacter,
+ isIgnoreCaseEnabled
+ );
+ }
+ else {
+ crit = new SimpleExpression( propertyName, propertyValue, "=", isIgnoreCaseEnabled && isString );
+ }
+ }
+ else {
+ crit = new NullExpression(propertyName);
+ }
+ String critCondition = crit.toSqlString(criteria, cq);
+ if ( buf.length()>1 && critCondition.trim().length()>0 ) buf.append(" and ");
+ buf.append(critCondition);
+ }
+
+ protected void appendComponentCondition(
+ String path,
+ Object component,
+ AbstractComponentType type,
+ Criteria criteria,
+ CriteriaQuery criteriaQuery,
+ StringBuffer buf)
+ throws HibernateException {
+
+ if (component!=null) {
+ String[] propertyNames = type.getPropertyNames();
+ Object[] values = type.getPropertyValues( component, getEntityMode(criteria, criteriaQuery) );
+ Type[] subtypes = type.getSubtypes();
+ for (int i=0; i<propertyNames.length; i++) {
+ String subpath = StringHelper.qualify( path, propertyNames[i] );
+ Object value = values[i];
+ if ( isPropertyIncluded( value, subpath, subtypes[i] ) ) {
+ Type subtype = subtypes[i];
+ if ( subtype.isComponentType() ) {
+ appendComponentCondition(
+ subpath,
+ value,
+ (AbstractComponentType) subtype,
+ criteria,
+ criteriaQuery,
+ buf
+ );
+ }
+ else {
+ appendPropertyCondition(
+ subpath,
+ value,
+ criteria,
+ criteriaQuery,
+ buf
+ );
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/ExistsSubqueryExpression.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/ExistsSubqueryExpression.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/ExistsSubqueryExpression.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,18 @@
+//$Id: ExistsSubqueryExpression.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate.criterion;
+
+import org.hibernate.Criteria;
+
+/**
+ * @author Gavin King
+ */
+public class ExistsSubqueryExpression extends SubqueryExpression {
+
+ protected String toLeftSqlString(Criteria criteria, CriteriaQuery outerQuery) {
+ return "";
+ }
+
+ protected ExistsSubqueryExpression(String quantifier, DetachedCriteria dc) {
+ super(null, quantifier, dc);
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/ExistsSubqueryExpression.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Expression.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Expression.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Expression.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,57 @@
+//$Id: Expression.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate.criterion;
+
+import org.hibernate.type.Type;
+import org.hibernate.util.ArrayHelper;
+
+
+/**
+ * This class is semi-deprecated. Use <tt>Restrictions</tt>.
+ *
+ * @see Restrictions
+ * @author Gavin King
+ */
+public final class Expression extends Restrictions {
+
+ private Expression() {
+ //cannot be instantiated
+ }
+
+ /**
+ * Apply a constraint expressed in SQL, with the given JDBC
+ * parameters. Any occurrences of <tt>{alias}</tt> will be
+ * replaced by the table alias.
+ *
+ * @param sql
+ * @param values
+ * @param types
+ * @return Criterion
+ */
+ public static Criterion sql(String sql, Object[] values, Type[] types) {
+ return new SQLCriterion(sql, values, types);
+ }
+ /**
+ * Apply a constraint expressed in SQL, with the given JDBC
+ * parameter. Any occurrences of <tt>{alias}</tt> will be replaced
+ * by the table alias.
+ *
+ * @param sql
+ * @param value
+ * @param type
+ * @return Criterion
+ */
+ public static Criterion sql(String sql, Object value, Type type) {
+ return new SQLCriterion(sql, new Object[] { value }, new Type[] { type } );
+ }
+ /**
+ * Apply a constraint expressed in SQL. Any occurrences of <tt>{alias}</tt>
+ * will be replaced by the table alias.
+ *
+ * @param sql
+ * @return Criterion
+ */
+ public static Criterion sql(String sql) {
+ return new SQLCriterion(sql, ArrayHelper.EMPTY_OBJECT_ARRAY, ArrayHelper.EMPTY_TYPE_ARRAY);
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/IdentifierEqExpression.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/IdentifierEqExpression.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/IdentifierEqExpression.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,46 @@
+//$Id: IdentifierEqExpression.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate.criterion;
+
+
+import org.hibernate.Criteria;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.TypedValue;
+import org.hibernate.util.StringHelper;
+
+/**
+ * An identifier constraint
+ * @author Gavin King
+ */
+public class IdentifierEqExpression implements Criterion {
+
+ private final Object value;
+
+ protected IdentifierEqExpression(Object value) {
+ this.value = value;
+ }
+
+ public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+
+ String[] columns = criteriaQuery.getIdentifierColumns(criteria);
+
+ String result = StringHelper.join(
+ " and ",
+ StringHelper.suffix( columns, " = ?" )
+ );
+ if (columns.length>1) result = '(' + result + ')';
+ return result;
+
+ //TODO: get SQL rendering out of this package!
+ }
+
+ public TypedValue[] getTypedValues(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ return new TypedValue[] { criteriaQuery.getTypedIdentifierValue(criteria, value) };
+ }
+
+ public String toString() {
+ return "id = " + value;
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/IdentifierEqExpression.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/IdentifierProjection.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/IdentifierProjection.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/IdentifierProjection.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,61 @@
+//$Id: IdentifierProjection.java 6970 2005-05-31 20:24:41Z oneovthafew $
+package org.hibernate.criterion;
+
+import org.hibernate.Criteria;
+import org.hibernate.HibernateException;
+import org.hibernate.type.Type;
+import org.hibernate.util.StringHelper;
+
+/**
+ * A property value, or grouped property value
+ * @author Gavin King
+ */
+public class IdentifierProjection extends SimpleProjection {
+
+ private boolean grouped;
+
+ protected IdentifierProjection(boolean grouped) {
+ this.grouped = grouped;
+ }
+
+ protected IdentifierProjection() {
+ this(false);
+ }
+
+ public String toString() {
+ return "id";
+ }
+
+ public Type[] getTypes(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ return new Type[] { criteriaQuery.getIdentifierType(criteria) };
+ }
+
+ public String toSqlString(Criteria criteria, int position, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ StringBuffer buf = new StringBuffer();
+ String[] cols = criteriaQuery.getIdentifierColumns(criteria);
+ for ( int i=0; i<cols.length; i++ ) {
+ buf.append( cols[i] )
+ .append(" as y")
+ .append(position + i)
+ .append('_');
+ }
+ return buf.toString();
+ }
+
+ public boolean isGrouped() {
+ return grouped;
+ }
+
+ public String toGroupSqlString(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ if (!grouped) {
+ return super.toGroupSqlString(criteria, criteriaQuery);
+ }
+ else {
+ return StringHelper.join( ", ", criteriaQuery.getIdentifierColumns(criteria) );
+ }
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/IdentifierProjection.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/IlikeExpression.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/IlikeExpression.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/IlikeExpression.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,53 @@
+//$Id: IlikeExpression.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate.criterion;
+
+
+import org.hibernate.Criteria;
+import org.hibernate.HibernateException;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.PostgreSQLDialect;
+import org.hibernate.engine.TypedValue;
+
+/**
+ * A case-insensitive "like"
+ * @author Gavin King
+ */
+public class IlikeExpression implements Criterion {
+
+ private final String propertyName;
+ private final Object value;
+
+ protected IlikeExpression(String propertyName, Object value) {
+ this.propertyName = propertyName;
+ this.value = value;
+ }
+
+ protected IlikeExpression(String propertyName, String value, MatchMode matchMode) {
+ this( propertyName, matchMode.toMatchString(value) );
+ }
+
+ public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ Dialect dialect = criteriaQuery.getFactory().getDialect();
+ String[] columns = criteriaQuery.getColumnsUsingProjection(criteria, propertyName);
+ if (columns.length!=1) throw new HibernateException("ilike may only be used with single-column properties");
+ if ( dialect instanceof PostgreSQLDialect ) {
+ return columns[0] + " ilike ?";
+ }
+ else {
+ return dialect.getLowercaseFunction() + '(' + columns[0] + ") like ?";
+ }
+
+ //TODO: get SQL rendering out of this package!
+ }
+
+ public TypedValue[] getTypedValues(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ return new TypedValue[] { criteriaQuery.getTypedValue( criteria, propertyName, value.toString().toLowerCase() ) };
+ }
+
+ public String toString() {
+ return propertyName + " ilike " + value;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/InExpression.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/InExpression.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/InExpression.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,74 @@
+//$Id: InExpression.java 7557 2005-07-19 23:25:36Z oneovthafew $
+package org.hibernate.criterion;
+
+import java.util.ArrayList;
+
+
+
+import org.hibernate.Criteria;
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+
+
+import org.hibernate.engine.TypedValue;
+
+import org.hibernate.type.AbstractComponentType;
+import org.hibernate.type.Type;
+import org.hibernate.util.StringHelper;
+
+/**
+ * Constrains the property to a specified list of values
+ * @author Gavin King
+ */
+public class InExpression implements Criterion {
+
+ private final String propertyName;
+ private final Object[] values;
+
+ protected InExpression(String propertyName, Object[] values) {
+ this.propertyName = propertyName;
+ this.values = values;
+ }
+
+ public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ String[] columns = criteriaQuery.getColumnsUsingProjection(criteria, propertyName);
+ String singleValueParam = StringHelper.repeat( "?, ", columns.length-1 ) + "?";
+ if ( columns.length>1 ) singleValueParam = '(' + singleValueParam + ')';
+ String params = values.length>0 ?
+ StringHelper.repeat( singleValueParam + ", ", values.length-1 ) + singleValueParam :
+ "";
+ String cols = StringHelper.join(", ", columns);
+ if ( columns.length>1 ) cols = '(' + cols + ')';
+ return cols + " in (" + params + ')';
+ }
+
+ public TypedValue[] getTypedValues(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ ArrayList list = new ArrayList();
+ Type type = criteriaQuery.getTypeUsingProjection(criteria, propertyName);
+ if ( type.isComponentType() ) {
+ AbstractComponentType actype = (AbstractComponentType) type;
+ Type[] types = actype.getSubtypes();
+ for ( int i=0; i<types.length; i++ ) {
+ for ( int j=0; j<values.length; j++ ) {
+ Object subval = values[j]==null ?
+ null :
+ actype.getPropertyValues( values[j], EntityMode.POJO )[i];
+ list.add( new TypedValue( types[i], subval, EntityMode.POJO ) );
+ }
+ }
+ }
+ else {
+ for ( int j=0; j<values.length; j++ ) {
+ list.add( new TypedValue( type, values[j], EntityMode.POJO ) );
+ }
+ }
+ return (TypedValue[]) list.toArray( new TypedValue[ list.size() ] );
+ }
+
+ public String toString() {
+ return propertyName + " in (" + StringHelper.toString(values) + ')';
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Junction.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Junction.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Junction.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,71 @@
+package org.hibernate.criterion;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.hibernate.Criteria;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.TypedValue;
+import org.hibernate.util.StringHelper;
+
+/**
+ * A sequence of a logical expressions combined by some
+ * associative logical operator
+ *
+ * @author Gavin King
+ */
+public class Junction implements Criterion {
+
+ private final List criteria = new ArrayList();
+ private final String op;
+
+ protected Junction(String op) {
+ this.op = op;
+ }
+
+ public Junction add(Criterion criterion) {
+ criteria.add(criterion);
+ return this;
+ }
+
+ public String getOp() {
+ return op;
+ }
+
+ public TypedValue[] getTypedValues(Criteria crit, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ ArrayList typedValues = new ArrayList();
+ Iterator iter = criteria.iterator();
+ while ( iter.hasNext() ) {
+ TypedValue[] subvalues = ( (Criterion) iter.next() ).getTypedValues(crit, criteriaQuery);
+ for ( int i=0; i<subvalues.length; i++ ) {
+ typedValues.add( subvalues[i] );
+ }
+ }
+ return (TypedValue[]) typedValues.toArray( new TypedValue[ typedValues.size() ] );
+ }
+
+ public String toSqlString(Criteria crit, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+
+ if ( criteria.size()==0 ) return "1=1";
+
+ StringBuffer buffer = new StringBuffer()
+ .append('(');
+ Iterator iter = criteria.iterator();
+ while ( iter.hasNext() ) {
+ buffer.append( ( (Criterion) iter.next() ).toSqlString(crit, criteriaQuery) );
+ if ( iter.hasNext() ) buffer.append(' ').append(op).append(' ');
+ }
+ return buffer.append(')').toString();
+ }
+
+ /**
+ * @see java.lang.Object#toString()
+ */
+ public String toString() {
+ return '(' + StringHelper.join( ' ' + op + ' ', criteria.iterator() ) + ')';
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/LikeExpression.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/LikeExpression.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/LikeExpression.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,75 @@
+package org.hibernate.criterion;
+
+import org.hibernate.Criteria;
+import org.hibernate.HibernateException;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.TypedValue;
+
+/**
+ * A criterion representing a "like" expression
+ *
+ * @author Scott Marlow
+ * @author Steve Ebersole
+ */
+public class LikeExpression implements Criterion {
+ private final String propertyName;
+ private final Object value;
+ private final Character escapeChar;
+ private final boolean ignoreCase;
+
+ protected LikeExpression(
+ String propertyName,
+ String value,
+ Character escapeChar,
+ boolean ignoreCase) {
+ this.propertyName = propertyName;
+ this.value = value;
+ this.escapeChar = escapeChar;
+ this.ignoreCase = ignoreCase;
+ }
+
+ protected LikeExpression(
+ String propertyName,
+ String value) {
+ this( propertyName, value, null, false );
+ }
+
+ protected LikeExpression(
+ String propertyName,
+ String value,
+ MatchMode matchMode) {
+ this( propertyName, matchMode.toMatchString( value ) );
+ }
+
+ protected LikeExpression(
+ String propertyName,
+ String value,
+ MatchMode matchMode,
+ Character escapeChar,
+ boolean ignoreCase) {
+ this( propertyName, matchMode.toMatchString( value ), escapeChar, ignoreCase );
+ }
+
+ public String toSqlString(
+ Criteria criteria,
+ CriteriaQuery criteriaQuery) throws HibernateException {
+ Dialect dialect = criteriaQuery.getFactory().getDialect();
+ String[] columns = criteriaQuery.getColumnsUsingProjection( criteria, propertyName );
+ if ( columns.length != 1 ) {
+ throw new HibernateException( "Like may only be used with single-column properties" );
+ }
+ String lhs = ignoreCase
+ ? dialect.getLowercaseFunction() + '(' + columns[0] + ')'
+ : columns[0];
+ return lhs + " like ?" + ( escapeChar == null ? "" : " escape \'" + escapeChar + "\'" );
+
+ }
+
+ public TypedValue[] getTypedValues(
+ Criteria criteria,
+ CriteriaQuery criteriaQuery) throws HibernateException {
+ return new TypedValue[] {
+ criteriaQuery.getTypedValue( criteria, propertyName, value.toString().toLowerCase() )
+ };
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/LogicalExpression.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/LogicalExpression.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/LogicalExpression.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,55 @@
+//$Id: LogicalExpression.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate.criterion;
+
+
+import org.hibernate.Criteria;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.TypedValue;
+
+/**
+ * Superclass of binary logical expressions
+ * @author Gavin King
+ */
+public class LogicalExpression implements Criterion {
+
+ private final Criterion lhs;
+ private final Criterion rhs;
+ private final String op;
+
+ protected LogicalExpression(Criterion lhs, Criterion rhs, String op) {
+ this.lhs = lhs;
+ this.rhs = rhs;
+ this.op = op;
+ }
+
+ public TypedValue[] getTypedValues(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+
+ TypedValue[] lhstv = lhs.getTypedValues(criteria, criteriaQuery);
+ TypedValue[] rhstv = rhs.getTypedValues(criteria, criteriaQuery);
+ TypedValue[] result = new TypedValue[ lhstv.length + rhstv.length ];
+ System.arraycopy(lhstv, 0, result, 0, lhstv.length);
+ System.arraycopy(rhstv, 0, result, lhstv.length, rhstv.length);
+ return result;
+ }
+
+ public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+
+ return '(' +
+ lhs.toSqlString(criteria, criteriaQuery) +
+ ' ' +
+ getOp() +
+ ' ' +
+ rhs.toSqlString(criteria, criteriaQuery) +
+ ')';
+ }
+
+ public String getOp() {
+ return op;
+ }
+
+ public String toString() {
+ return lhs.toString() + ' ' + getOp() + ' ' + rhs.toString();
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/MatchMode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/MatchMode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/MatchMode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,82 @@
+//$Id: MatchMode.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate.criterion;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Represents an strategy for matching strings using "like".
+ *
+ * @see Example#enableLike(MatchMode)
+ * @author Gavin King
+ */
+public abstract class MatchMode implements Serializable {
+ private final String name;
+ private static final Map INSTANCES = new HashMap();
+
+ protected MatchMode(String name) {
+ this.name=name;
+ }
+ public String toString() {
+ return name;
+ }
+
+ /**
+ * Match the entire string to the pattern
+ */
+ public static final MatchMode EXACT = new MatchMode("EXACT") {
+ public String toMatchString(String pattern) {
+ return pattern;
+ }
+ };
+
+ /**
+ * Match the start of the string to the pattern
+ */
+ public static final MatchMode START = new MatchMode("START") {
+ public String toMatchString(String pattern) {
+ return pattern + '%';
+ }
+ };
+
+ /**
+ * Match the end of the string to the pattern
+ */
+ public static final MatchMode END = new MatchMode("END") {
+ public String toMatchString(String pattern) {
+ return '%' + pattern;
+ }
+ };
+
+ /**
+ * Match the pattern anywhere in the string
+ */
+ public static final MatchMode ANYWHERE = new MatchMode("ANYWHERE") {
+ public String toMatchString(String pattern) {
+ return '%' + pattern + '%';
+ }
+ };
+
+ static {
+ INSTANCES.put( EXACT.name, EXACT );
+ INSTANCES.put( END.name, END );
+ INSTANCES.put( START.name, START );
+ INSTANCES.put( ANYWHERE.name, ANYWHERE );
+ }
+
+ private Object readResolve() {
+ return INSTANCES.get(name);
+ }
+
+ /**
+ * convert the pattern, by appending/prepending "%"
+ */
+ public abstract String toMatchString(String pattern);
+
+}
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/NaturalIdentifier.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/NaturalIdentifier.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/NaturalIdentifier.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,28 @@
+//$Id: NaturalIdentifier.java 6899 2005-05-25 01:22:07Z oneovthafew $
+package org.hibernate.criterion;
+
+import org.hibernate.Criteria;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.TypedValue;
+
+/**
+ * @author Gavin King
+ */
+public class NaturalIdentifier implements Criterion {
+
+ private Junction conjunction = new Conjunction();
+
+ public TypedValue[] getTypedValues(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException {
+ return conjunction.getTypedValues(criteria, criteriaQuery);
+ }
+
+ public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException {
+ return conjunction.toSqlString(criteria, criteriaQuery);
+ }
+
+ public NaturalIdentifier set(String property, Object value) {
+ conjunction.add( Restrictions.eq(property, value) );
+ return this;
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/NaturalIdentifier.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/NotEmptyExpression.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/NotEmptyExpression.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/NotEmptyExpression.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,17 @@
+//$Id: NotEmptyExpression.java 6661 2005-05-03 20:12:20Z steveebersole $
+package org.hibernate.criterion;
+
+/**
+ * @author Gavin King
+ */
+public class NotEmptyExpression extends AbstractEmptinessExpression implements Criterion {
+
+ protected NotEmptyExpression(String propertyName) {
+ super( propertyName );
+ }
+
+ protected boolean excludeEmpty() {
+ return true;
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/NotEmptyExpression.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/NotExpression.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/NotExpression.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/NotExpression.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,42 @@
+//$Id: NotExpression.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate.criterion;
+
+
+import org.hibernate.Criteria;
+import org.hibernate.HibernateException;
+import org.hibernate.dialect.MySQLDialect;
+import org.hibernate.engine.TypedValue;
+
+/**
+ * Negates another criterion
+ * @author Gavin King
+ */
+public class NotExpression implements Criterion {
+
+ private Criterion criterion;
+
+ protected NotExpression(Criterion criterion) {
+ this.criterion = criterion;
+ }
+
+ public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ if ( criteriaQuery.getFactory().getDialect() instanceof MySQLDialect ) {
+ return "not (" + criterion.toSqlString(criteria, criteriaQuery) + ')';
+ }
+ else {
+ return "not " + criterion.toSqlString(criteria, criteriaQuery);
+ }
+ }
+
+ public TypedValue[] getTypedValues(
+ Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ return criterion.getTypedValues(criteria, criteriaQuery);
+ }
+
+ public String toString() {
+ return "not " + criterion.toString();
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/NotNullExpression.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/NotNullExpression.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/NotNullExpression.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,46 @@
+//$Id: NotNullExpression.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate.criterion;
+
+
+import org.hibernate.Criteria;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.TypedValue;
+import org.hibernate.util.StringHelper;
+
+/**
+ * Constrains a property to be non-null
+ * @author Gavin King
+ */
+public class NotNullExpression implements Criterion {
+
+ private final String propertyName;
+
+ private static final TypedValue[] NO_VALUES = new TypedValue[0];
+
+ protected NotNullExpression(String propertyName) {
+ this.propertyName = propertyName;
+ }
+
+ public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ String[] columns = criteriaQuery.getColumnsUsingProjection(criteria, propertyName);
+ String result = StringHelper.join(
+ " or ",
+ StringHelper.suffix( columns, " is not null" )
+ );
+ if (columns.length>1) result = '(' + result + ')';
+ return result;
+
+ //TODO: get SQL rendering out of this package!
+ }
+
+ public TypedValue[] getTypedValues(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ return NO_VALUES;
+ }
+
+ public String toString() {
+ return propertyName + " is not null";
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/NullExpression.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/NullExpression.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/NullExpression.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,46 @@
+//$Id: NullExpression.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate.criterion;
+
+
+import org.hibernate.Criteria;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.TypedValue;
+import org.hibernate.util.StringHelper;
+
+/**
+ * Constrains a property to be null
+ * @author Gavin King
+ */
+public class NullExpression implements Criterion {
+
+ private final String propertyName;
+
+ private static final TypedValue[] NO_VALUES = new TypedValue[0];
+
+ protected NullExpression(String propertyName) {
+ this.propertyName = propertyName;
+ }
+
+ public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ String[] columns = criteriaQuery.getColumnsUsingProjection(criteria, propertyName);
+ String result = StringHelper.join(
+ " and ",
+ StringHelper.suffix( columns, " is null" )
+ );
+ if (columns.length>1) result = '(' + result + ')';
+ return result;
+
+ //TODO: get SQL rendering out of this package!
+ }
+
+ public TypedValue[] getTypedValues(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ return NO_VALUES;
+ }
+
+ public String toString() {
+ return propertyName + " is null";
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Order.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Order.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Order.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,83 @@
+//$Id: Order.java 7495 2005-07-15 16:52:10Z oneovthafew $
+package org.hibernate.criterion;
+
+import java.io.Serializable;
+import java.sql.Types;
+
+import org.hibernate.Criteria;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.type.Type;
+
+/**
+ * Represents an order imposed upon a <tt>Criteria</tt> result set
+ * @author Gavin King
+ */
+public class Order implements Serializable {
+
+ private boolean ascending;
+ private boolean ignoreCase;
+ private String propertyName;
+
+ public String toString() {
+ return propertyName + ' ' + (ascending?"asc":"desc");
+ }
+
+ public Order ignoreCase() {
+ ignoreCase = true;
+ return this;
+ }
+
+ /**
+ * Constructor for Order.
+ */
+ protected Order(String propertyName, boolean ascending) {
+ this.propertyName = propertyName;
+ this.ascending = ascending;
+ }
+
+ /**
+ * Render the SQL fragment
+ *
+ */
+ public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ String[] columns = criteriaQuery.getColumnsUsingProjection(criteria, propertyName);
+ Type type = criteriaQuery.getTypeUsingProjection(criteria, propertyName);
+ StringBuffer fragment = new StringBuffer();
+ for ( int i=0; i<columns.length; i++ ) {
+ SessionFactoryImplementor factory = criteriaQuery.getFactory();
+ boolean lower = ignoreCase && type.sqlTypes( factory )[i]==Types.VARCHAR;
+ if (lower) {
+ fragment.append( factory.getDialect().getLowercaseFunction() )
+ .append('(');
+ }
+ fragment.append( columns[i] );
+ if (lower) fragment.append(')');
+ fragment.append( ascending ? " asc" : " desc" );
+ if ( i<columns.length-1 ) fragment.append(", ");
+ }
+ return fragment.toString();
+ }
+
+ /**
+ * Ascending order
+ *
+ * @param propertyName
+ * @return Order
+ */
+ public static Order asc(String propertyName) {
+ return new Order(propertyName, true);
+ }
+
+ /**
+ * Descending order
+ *
+ * @param propertyName
+ * @return Order
+ */
+ public static Order desc(String propertyName) {
+ return new Order(propertyName, false);
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Projection.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Projection.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Projection.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,81 @@
+//$Id: Projection.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate.criterion;
+
+
+import java.io.Serializable;
+
+import org.hibernate.Criteria;
+import org.hibernate.HibernateException;
+import org.hibernate.type.Type;
+
+/**
+ * An object-oriented representation of a query result set projection
+ * in a <tt>Criteria</tt> query. Built-in projection types are provided
+ * by the <tt>Projections</tt> factory class.
+ * This interface might be implemented by application classes that
+ * define custom projections.
+ *
+ * @see Projections
+ * @see org.hibernate.Criteria
+ * @author Gavin King
+ */
+public interface Projection extends Serializable {
+
+ /**
+ * Render the SQL fragment
+ * @param criteriaQuery
+ * @param columnAlias
+ * @return String
+ * @throws HibernateException
+ */
+ public String toSqlString(Criteria criteria, int position, CriteriaQuery criteriaQuery)
+ throws HibernateException;
+
+ /**
+ * Render the SQL fragment to be used in the group by clause
+ * @param criteriaQuery
+ * @param columnAlias
+ * @return String
+ * @throws HibernateException
+ */
+ public String toGroupSqlString(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException;
+
+ /**
+ * Return types returned by the rendered SQL fragment
+ * @param criteria
+ * @param criteriaQuery
+ * @return Type[]
+ * @throws HibernateException
+ */
+ public Type[] getTypes(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException;
+ /**
+ * Return types for a particular user-visible alias
+ */
+ public Type[] getTypes(String alias, Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException;
+
+ /**
+ * Get the SQL select clause column aliases
+ */
+ public String[] getColumnAliases(int loc);
+ /**
+ * Get the SQL select clause column aliases for a particular
+ * user-visible alias
+ */
+ public String[] getColumnAliases(String alias, int loc);
+
+ /**
+ * Get the user-visible aliases for this projection
+ * (ie. the ones that will be passed to the
+ * <tt>ResultTransformer</tt>)
+ */
+ public String[] getAliases();
+
+ /**
+ * Does this projection specify grouping attributes?
+ */
+ public boolean isGrouped();
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Projection.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/ProjectionList.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/ProjectionList.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/ProjectionList.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,125 @@
+//$Id: ProjectionList.java 6490 2005-04-23 07:09:03Z oneovthafew $
+package org.hibernate.criterion;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.hibernate.Criteria;
+import org.hibernate.HibernateException;
+import org.hibernate.type.Type;
+import org.hibernate.util.ArrayHelper;
+
+/**
+ * @author Gavin King
+ */
+public class ProjectionList implements Projection {
+
+ private List elements = new ArrayList();
+
+ protected ProjectionList() {}
+
+ public ProjectionList create() {
+ return new ProjectionList();
+ }
+
+ public ProjectionList add(Projection proj) {
+ elements.add(proj);
+ return this;
+ }
+
+ public ProjectionList add(Projection projection, String alias) {
+ return add( Projections.alias(projection, alias) );
+ }
+
+ public Type[] getTypes(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ List types = new ArrayList( getLength() );
+ for ( int i=0; i<getLength(); i++ ) {
+ Type[] elemTypes = getProjection(i).getTypes(criteria, criteriaQuery);
+ ArrayHelper.addAll(types, elemTypes);
+ }
+ return ArrayHelper.toTypeArray(types);
+ }
+
+ public String toSqlString(Criteria criteria, int loc, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ StringBuffer buf = new StringBuffer();
+ for ( int i=0; i<getLength(); i++ ) {
+ Projection proj = getProjection(i);
+ buf.append( proj.toSqlString(criteria, loc, criteriaQuery) );
+ loc += proj.getColumnAliases(loc).length;
+ if ( i<elements.size()-1 ) buf.append(", ");
+ }
+ return buf.toString();
+ }
+
+ public String toGroupSqlString(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ StringBuffer buf = new StringBuffer();
+ for ( int i=0; i<getLength(); i++ ) {
+ Projection proj = getProjection(i);
+ if ( proj.isGrouped() ) {
+ buf.append( proj.toGroupSqlString(criteria, criteriaQuery) )
+ .append(", ");
+ }
+ }
+ if ( buf.length()>2 ) buf.setLength( buf.length()-2 ); //pull off the last ", "
+ return buf.toString();
+ }
+
+ public String[] getColumnAliases(int loc) {
+ List result = new ArrayList( getLength() );
+ for ( int i=0; i<getLength(); i++ ) {
+ String[] colAliases = getProjection(i).getColumnAliases(loc);
+ ArrayHelper.addAll(result, colAliases);
+ loc+=colAliases.length;
+ }
+ return ArrayHelper.toStringArray(result);
+ }
+
+ public String[] getColumnAliases(String alias, int loc) {
+ for ( int i=0; i<getLength(); i++ ) {
+ String[] result = getProjection(i).getColumnAliases(alias, loc);
+ if (result!=null) return result;
+ loc += getProjection(i).getColumnAliases(loc).length;
+ }
+ return null;
+ }
+
+ public Type[] getTypes(String alias, Criteria criteria, CriteriaQuery criteriaQuery) {
+ for ( int i=0; i<getLength(); i++ ) {
+ Type[] result = getProjection(i).getTypes(alias, criteria, criteriaQuery);
+ if (result!=null) return result;
+ }
+ return null;
+ }
+
+ public String[] getAliases() {
+ List result = new ArrayList( getLength() );
+ for ( int i=0; i<getLength(); i++ ) {
+ String[] aliases = getProjection(i).getAliases();
+ ArrayHelper.addAll(result, aliases);
+ }
+ return ArrayHelper.toStringArray(result);
+
+ }
+
+ public Projection getProjection(int i) {
+ return (Projection) elements.get(i);
+ }
+
+ public int getLength() {
+ return elements.size();
+ }
+
+ public String toString() {
+ return elements.toString();
+ }
+
+ public boolean isGrouped() {
+ for ( int i=0; i<getLength(); i++ ) {
+ if ( getProjection(i).isGrouped() ) return true;
+ }
+ return false;
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/ProjectionList.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Projections.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Projections.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Projections.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,128 @@
+//$Id: Projections.java 6970 2005-05-31 20:24:41Z oneovthafew $
+package org.hibernate.criterion;
+
+import org.hibernate.type.Type;
+
+/**
+ * The <tt>criterion</tt> package may be used by applications as a framework for building
+ * new kinds of <tt>Projection</tt>. However, it is intended that most applications will
+ * simply use the built-in projection types via the static factory methods of this class.<br/>
+ * <br/>
+ * The factory methods that take an alias allow the projected value to be referred to by
+ * criterion and order instances.
+ *
+ * @see org.hibernate.Criteria
+ * @see Restrictions factory methods for <tt>Criterion</tt> instances
+ * @author Gavin King
+ */
+public final class Projections {
+
+ private Projections() {
+ //cannot be instantiated
+ }
+
+ /**
+ * Create a distinct projection from a projection
+ */
+ public static Projection distinct(Projection proj) {
+ return new Distinct(proj);
+ }
+
+ /**
+ * Create a new projection list
+ */
+ public static ProjectionList projectionList() {
+ return new ProjectionList();
+ }
+
+ /**
+ * The query row count, ie. <tt>count(*)</tt>
+ */
+ public static Projection rowCount() {
+ return new RowCountProjection();
+ }
+
+ /**
+ * A property value count
+ */
+ public static CountProjection count(String propertyName) {
+ return new CountProjection(propertyName);
+ }
+
+ /**
+ * A distinct property value count
+ */
+ public static CountProjection countDistinct(String propertyName) {
+ return new CountProjection(propertyName).setDistinct();
+ }
+
+ /**
+ * A property maximum value
+ */
+ public static AggregateProjection max(String propertyName) {
+ return new AggregateProjection("max", propertyName);
+ }
+
+ /**
+ * A property minimum value
+ */
+ public static AggregateProjection min(String propertyName) {
+ return new AggregateProjection("min", propertyName);
+ }
+
+ /**
+ * A property average value
+ */
+ public static AggregateProjection avg(String propertyName) {
+ return new AvgProjection(propertyName);
+ }
+
+ /**
+ * A property value sum
+ */
+ public static AggregateProjection sum(String propertyName) {
+ return new AggregateProjection("sum", propertyName);
+ }
+
+ /**
+ * A SQL projection, a typed select clause fragment
+ */
+ public static Projection sqlProjection(String sql, String[] columnAliases, Type[] types) {
+ return new SQLProjection(sql, columnAliases, types);
+ }
+
+ /**
+ * A grouping SQL projection, specifying both select clause and group by clause fragments
+ */
+ public static Projection sqlGroupProjection(String sql, String groupBy, String[] columnAliases, Type[] types) {
+ return new SQLProjection(sql, groupBy, columnAliases, types);
+ }
+
+ /**
+ * A grouping property value
+ */
+ public static PropertyProjection groupProperty(String propertyName) {
+ return new PropertyProjection(propertyName, true);
+ }
+
+ /**
+ * A projected property value
+ */
+ public static PropertyProjection property(String propertyName) {
+ return new PropertyProjection(propertyName);
+ }
+
+ /**
+ * A projected identifier value
+ */
+ public static IdentifierProjection id() {
+ return new IdentifierProjection();
+ }
+
+ /**
+ * Assign an alias to a projection, by wrapping it
+ */
+ public static Projection alias(Projection projection, String alias) {
+ return new AliasedProjection(projection, alias);
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Projections.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Property.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Property.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Property.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,235 @@
+//$Id: Property.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate.criterion;
+
+import java.util.Collection;
+
+/**
+ * A factory for property-specific criterion and projection instances
+ * @author Gavin King
+ */
+public class Property extends PropertyProjection {
+ //private String propertyName;
+ protected Property(String propertyName) {
+ super(propertyName);
+ }
+
+ public Criterion between(Object min, Object max) {
+ return Restrictions.between(getPropertyName(), min, max);
+ }
+
+ public Criterion in(Collection values) {
+ return Restrictions.in(getPropertyName(), values);
+ }
+
+ public Criterion in(Object[] values) {
+ return Restrictions.in(getPropertyName(), values);
+ }
+
+ public SimpleExpression like(Object value) {
+ return Restrictions.like(getPropertyName(), value);
+ }
+
+ public SimpleExpression like(String value, MatchMode matchMode) {
+ return Restrictions.like(getPropertyName(), value, matchMode);
+ }
+
+ public SimpleExpression eq(Object value) {
+ return Restrictions.eq(getPropertyName(), value);
+ }
+
+ public SimpleExpression ne(Object value) {
+ return Restrictions.ne(getPropertyName(), value);
+ }
+
+ public SimpleExpression gt(Object value) {
+ return Restrictions.gt(getPropertyName(), value);
+ }
+
+ public SimpleExpression lt(Object value) {
+ return Restrictions.lt(getPropertyName(), value);
+ }
+
+ public SimpleExpression le(Object value) {
+ return Restrictions.le(getPropertyName(), value);
+ }
+
+ public SimpleExpression ge(Object value) {
+ return Restrictions.ge(getPropertyName(), value);
+ }
+
+ public PropertyExpression eqProperty(Property other) {
+ return Restrictions.eqProperty( getPropertyName(), other.getPropertyName() );
+ }
+
+ public PropertyExpression neProperty(Property other) {
+ return Restrictions.neProperty( getPropertyName(), other.getPropertyName() );
+ }
+
+ public PropertyExpression leProperty(Property other) {
+ return Restrictions.leProperty( getPropertyName(), other.getPropertyName() );
+ }
+
+ public PropertyExpression geProperty(Property other) {
+ return Restrictions.geProperty( getPropertyName(), other.getPropertyName() );
+ }
+
+ public PropertyExpression ltProperty(Property other) {
+ return Restrictions.ltProperty( getPropertyName(), other.getPropertyName() );
+ }
+
+ public PropertyExpression gtProperty(Property other) {
+ return Restrictions.gtProperty( getPropertyName(), other.getPropertyName() );
+ }
+
+ public PropertyExpression eqProperty(String other) {
+ return Restrictions.eqProperty( getPropertyName(), other );
+ }
+
+ public PropertyExpression neProperty(String other) {
+ return Restrictions.neProperty( getPropertyName(), other );
+ }
+
+ public PropertyExpression leProperty(String other) {
+ return Restrictions.leProperty( getPropertyName(), other );
+ }
+
+ public PropertyExpression geProperty(String other) {
+ return Restrictions.geProperty( getPropertyName(), other );
+ }
+
+ public PropertyExpression ltProperty(String other) {
+ return Restrictions.ltProperty( getPropertyName(), other );
+ }
+
+ public PropertyExpression gtProperty(String other) {
+ return Restrictions.gtProperty( getPropertyName(), other );
+ }
+
+ public Criterion isNull() {
+ return Restrictions.isNull(getPropertyName());
+ }
+
+ public Criterion isNotNull() {
+ return Restrictions.isNotNull(getPropertyName());
+ }
+
+ public Criterion isEmpty() {
+ return Restrictions.isEmpty(getPropertyName());
+ }
+
+ public Criterion isNotEmpty() {
+ return Restrictions.isNotEmpty(getPropertyName());
+ }
+
+ public CountProjection count() {
+ return Projections.count(getPropertyName());
+ }
+
+ public AggregateProjection max() {
+ return Projections.max(getPropertyName());
+ }
+
+ public AggregateProjection min() {
+ return Projections.min(getPropertyName());
+ }
+
+ public AggregateProjection avg() {
+ return Projections.avg(getPropertyName());
+ }
+
+ /*public PropertyProjection project() {
+ return Projections.property(getPropertyName());
+ }*/
+
+ public PropertyProjection group() {
+ return Projections.groupProperty(getPropertyName());
+ }
+
+ public Order asc() {
+ return Order.asc(getPropertyName());
+ }
+
+ public Order desc() {
+ return Order.desc(getPropertyName());
+ }
+
+ public static Property forName(String propertyName) {
+ return new Property(propertyName);
+ }
+
+ /**
+ * Get a component attribute of this property
+ */
+ public Property getProperty(String propertyName) {
+ return forName( getPropertyName() + '.' + propertyName );
+ }
+
+ public Criterion eq(DetachedCriteria subselect) {
+ return Subqueries.propertyEq( getPropertyName(), subselect );
+ }
+
+ public Criterion ne(DetachedCriteria subselect) {
+ return Subqueries.propertyNe( getPropertyName(), subselect );
+ }
+
+ public Criterion lt(DetachedCriteria subselect) {
+ return Subqueries.propertyLt( getPropertyName(), subselect );
+ }
+
+ public Criterion le(DetachedCriteria subselect) {
+ return Subqueries.propertyLe( getPropertyName(), subselect );
+ }
+
+ public Criterion gt(DetachedCriteria subselect) {
+ return Subqueries.propertyGt( getPropertyName(), subselect );
+ }
+
+ public Criterion ge(DetachedCriteria subselect) {
+ return Subqueries.propertyGe( getPropertyName(), subselect );
+ }
+
+ public Criterion notIn(DetachedCriteria subselect) {
+ return Subqueries.propertyNotIn( getPropertyName(), subselect );
+ }
+
+ public Criterion in(DetachedCriteria subselect) {
+ return Subqueries.propertyIn( getPropertyName(), subselect );
+ }
+
+ public Criterion eqAll(DetachedCriteria subselect) {
+ return Subqueries.propertyEqAll( getPropertyName(), subselect );
+ }
+
+ public Criterion gtAll(DetachedCriteria subselect) {
+ return Subqueries.propertyGtAll( getPropertyName(), subselect );
+ }
+
+ public Criterion ltAll(DetachedCriteria subselect) {
+ return Subqueries.propertyLtAll( getPropertyName(), subselect );
+ }
+
+ public Criterion leAll(DetachedCriteria subselect) {
+ return Subqueries.propertyLeAll( getPropertyName(), subselect );
+ }
+
+ public Criterion geAll(DetachedCriteria subselect) {
+ return Subqueries.propertyGeAll( getPropertyName(), subselect );
+ }
+
+ public Criterion gtSome(DetachedCriteria subselect) {
+ return Subqueries.propertyGtSome( getPropertyName(), subselect );
+ }
+
+ public Criterion ltSome(DetachedCriteria subselect) {
+ return Subqueries.propertyLtSome( getPropertyName(), subselect );
+ }
+
+ public Criterion leSome(DetachedCriteria subselect) {
+ return Subqueries.propertyLeSome( getPropertyName(), subselect );
+ }
+
+ public Criterion geSome(DetachedCriteria subselect) {
+ return Subqueries.propertyGeSome( getPropertyName(), subselect );
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Property.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/PropertyExpression.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/PropertyExpression.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/PropertyExpression.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,54 @@
+//$Id: PropertyExpression.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate.criterion;
+
+
+import org.hibernate.Criteria;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.TypedValue;
+import org.hibernate.util.StringHelper;
+
+/**
+ * superclass for comparisons between two properties (with SQL binary operators)
+ * @author Gavin King
+ */
+public class PropertyExpression implements Criterion {
+
+ private final String propertyName;
+ private final String otherPropertyName;
+ private final String op;
+
+ private static final TypedValue[] NO_TYPED_VALUES = new TypedValue[0];
+
+ protected PropertyExpression(String propertyName, String otherPropertyName, String op) {
+ this.propertyName = propertyName;
+ this.otherPropertyName = otherPropertyName;
+ this.op = op;
+ }
+
+ public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ String[] xcols = criteriaQuery.getColumnsUsingProjection(criteria, propertyName);
+ String[] ycols = criteriaQuery.getColumnsUsingProjection(criteria, otherPropertyName);
+ String result = StringHelper.join(
+ " and ",
+ StringHelper.add(xcols, getOp(), ycols)
+ );
+ if (xcols.length>1) result = '(' + result + ')';
+ return result;
+ //TODO: get SQL rendering out of this package!
+ }
+
+ public TypedValue[] getTypedValues(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ return NO_TYPED_VALUES;
+ }
+
+ public String toString() {
+ return propertyName + getOp() + otherPropertyName;
+ }
+
+ public String getOp() {
+ return op;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/PropertyProjection.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/PropertyProjection.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/PropertyProjection.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,63 @@
+//$Id: PropertyProjection.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate.criterion;
+
+import org.hibernate.Criteria;
+import org.hibernate.HibernateException;
+import org.hibernate.type.Type;
+
+/**
+ * A property value, or grouped property value
+ * @author Gavin King
+ */
+public class PropertyProjection extends SimpleProjection {
+
+ private String propertyName;
+ private boolean grouped;
+
+ protected PropertyProjection(String prop, boolean grouped) {
+ this.propertyName = prop;
+ this.grouped = grouped;
+ }
+
+ protected PropertyProjection(String prop) {
+ this(prop, false);
+ }
+
+ public String getPropertyName() {
+ return propertyName;
+ }
+
+ public String toString() {
+ return propertyName;
+ }
+
+ public Type[] getTypes(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ return new Type[] { criteriaQuery.getType(criteria, propertyName) };
+ }
+
+ public String toSqlString(Criteria criteria, int position, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ return new StringBuffer()
+ .append( criteriaQuery.getColumn(criteria, propertyName) )
+ .append(" as y")
+ .append(position)
+ .append('_')
+ .toString();
+ }
+
+ public boolean isGrouped() {
+ return grouped;
+ }
+
+ public String toGroupSqlString(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ if (!grouped) {
+ return super.toGroupSqlString(criteria, criteriaQuery);
+ }
+ else {
+ return criteriaQuery.getColumn(criteria, propertyName);
+ }
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/PropertyProjection.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/PropertySubqueryExpression.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/PropertySubqueryExpression.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/PropertySubqueryExpression.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,23 @@
+//$Id: PropertySubqueryExpression.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate.criterion;
+
+import org.hibernate.Criteria;
+
+/**
+ * A comparison between a property value in the outer query and the
+ * result of a subquery
+ * @author Gavin King
+ */
+public class PropertySubqueryExpression extends SubqueryExpression {
+ private String propertyName;
+
+ protected PropertySubqueryExpression(String propertyName, String op, String quantifier, DetachedCriteria dc) {
+ super(op, quantifier, dc);
+ this.propertyName = propertyName;
+ }
+
+ protected String toLeftSqlString(Criteria criteria, CriteriaQuery criteriaQuery) {
+ return criteriaQuery.getColumn(criteria, propertyName);
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/PropertySubqueryExpression.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Restrictions.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Restrictions.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Restrictions.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,368 @@
+//$Id: Restrictions.java 7844 2005-08-11 07:26:26Z oneovthafew $
+package org.hibernate.criterion;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.hibernate.type.Type;
+import org.hibernate.util.ArrayHelper;
+
+/**
+ * The <tt>criterion</tt> package may be used by applications as a framework for building
+ * new kinds of <tt>Criterion</tt>. However, it is intended that most applications will
+ * simply use the built-in criterion types via the static factory methods of this class.
+ *
+ * @see org.hibernate.Criteria
+ * @see Projections factory methods for <tt>Projection</tt> instances
+ * @author Gavin King
+ */
+public class Restrictions {
+
+ Restrictions() {
+ //cannot be instantiated
+ }
+
+ /**
+ * Apply an "equal" constraint to the identifier property
+ * @param propertyName
+ * @param value
+ * @return Criterion
+ */
+ public static Criterion idEq(Object value) {
+ return new IdentifierEqExpression(value);
+ }
+ /**
+ * Apply an "equal" constraint to the named property
+ * @param propertyName
+ * @param value
+ * @return Criterion
+ */
+ public static SimpleExpression eq(String propertyName, Object value) {
+ return new SimpleExpression(propertyName, value, "=");
+ }
+ /**
+ * Apply a "not equal" constraint to the named property
+ * @param propertyName
+ * @param value
+ * @return Criterion
+ */
+ public static SimpleExpression ne(String propertyName, Object value) {
+ return new SimpleExpression(propertyName, value, "<>");
+ }
+ /**
+ * Apply a "like" constraint to the named property
+ * @param propertyName
+ * @param value
+ * @return Criterion
+ */
+ public static SimpleExpression like(String propertyName, Object value) {
+ return new SimpleExpression(propertyName, value, " like ");
+ }
+ /**
+ * Apply a "like" constraint to the named property
+ * @param propertyName
+ * @param value
+ * @return Criterion
+ */
+ public static SimpleExpression like(String propertyName, String value, MatchMode matchMode) {
+ return new SimpleExpression(propertyName, matchMode.toMatchString(value), " like " );
+ }
+ /**
+ * A case-insensitive "like", similar to Postgres <tt>ilike</tt>
+ * operator
+ *
+ * @param propertyName
+ * @param value
+ * @return Criterion
+ */
+ public static Criterion ilike(String propertyName, String value, MatchMode matchMode) {
+ return new IlikeExpression(propertyName, value, matchMode);
+ }
+ /**
+ * A case-insensitive "like", similar to Postgres <tt>ilike</tt>
+ * operator
+ *
+ * @param propertyName
+ * @param value
+ * @return Criterion
+ */
+ public static Criterion ilike(String propertyName, Object value) {
+ return new IlikeExpression(propertyName, value);
+ }
+ /**
+ * Apply a "greater than" constraint to the named property
+ * @param propertyName
+ * @param value
+ * @return Criterion
+ */
+ public static SimpleExpression gt(String propertyName, Object value) {
+ return new SimpleExpression(propertyName, value, ">");
+ }
+ /**
+ * Apply a "less than" constraint to the named property
+ * @param propertyName
+ * @param value
+ * @return Criterion
+ */
+ public static SimpleExpression lt(String propertyName, Object value) {
+ return new SimpleExpression(propertyName, value, "<");
+ }
+ /**
+ * Apply a "less than or equal" constraint to the named property
+ * @param propertyName
+ * @param value
+ * @return Criterion
+ */
+ public static SimpleExpression le(String propertyName, Object value) {
+ return new SimpleExpression(propertyName, value, "<=");
+ }
+ /**
+ * Apply a "greater than or equal" constraint to the named property
+ * @param propertyName
+ * @param value
+ * @return Criterion
+ */
+ public static SimpleExpression ge(String propertyName, Object value) {
+ return new SimpleExpression(propertyName, value, ">=");
+ }
+ /**
+ * Apply a "between" constraint to the named property
+ * @param propertyName
+ * @param lo value
+ * @param hi value
+ * @return Criterion
+ */
+ public static Criterion between(String propertyName, Object lo, Object hi) {
+ return new BetweenExpression(propertyName, lo, hi);
+ }
+ /**
+ * Apply an "in" constraint to the named property
+ * @param propertyName
+ * @param values
+ * @return Criterion
+ */
+ public static Criterion in(String propertyName, Object[] values) {
+ return new InExpression(propertyName, values);
+ }
+ /**
+ * Apply an "in" constraint to the named property
+ * @param propertyName
+ * @param values
+ * @return Criterion
+ */
+ public static Criterion in(String propertyName, Collection values) {
+ return new InExpression( propertyName, values.toArray() );
+ }
+ /**
+ * Apply an "is null" constraint to the named property
+ * @return Criterion
+ */
+ public static Criterion isNull(String propertyName) {
+ return new NullExpression(propertyName);
+ }
+ /**
+ * Apply an "equal" constraint to two properties
+ */
+ public static PropertyExpression eqProperty(String propertyName, String otherPropertyName) {
+ return new PropertyExpression(propertyName, otherPropertyName, "=");
+ }
+ /**
+ * Apply a "not equal" constraint to two properties
+ */
+ public static PropertyExpression neProperty(String propertyName, String otherPropertyName) {
+ return new PropertyExpression(propertyName, otherPropertyName, "<>");
+ }
+ /**
+ * Apply a "less than" constraint to two properties
+ */
+ public static PropertyExpression ltProperty(String propertyName, String otherPropertyName) {
+ return new PropertyExpression(propertyName, otherPropertyName, "<");
+ }
+ /**
+ * Apply a "less than or equal" constraint to two properties
+ */
+ public static PropertyExpression leProperty(String propertyName, String otherPropertyName) {
+ return new PropertyExpression(propertyName, otherPropertyName, "<=");
+ }
+ /**
+ * Apply a "greater than" constraint to two properties
+ */
+ public static PropertyExpression gtProperty(String propertyName, String otherPropertyName) {
+ return new PropertyExpression(propertyName, otherPropertyName, ">");
+ }
+ /**
+ * Apply a "greater than or equal" constraint to two properties
+ */
+ public static PropertyExpression geProperty(String propertyName, String otherPropertyName) {
+ return new PropertyExpression(propertyName, otherPropertyName, ">=");
+ }
+ /**
+ * Apply an "is not null" constraint to the named property
+ * @return Criterion
+ */
+ public static Criterion isNotNull(String propertyName) {
+ return new NotNullExpression(propertyName);
+ }
+ /**
+ * Return the conjuction of two expressions
+ *
+ * @param lhs
+ * @param rhs
+ * @return Criterion
+ */
+ public static LogicalExpression and(Criterion lhs, Criterion rhs) {
+ return new LogicalExpression(lhs, rhs, "and");
+ }
+ /**
+ * Return the disjuction of two expressions
+ *
+ * @param lhs
+ * @param rhs
+ * @return Criterion
+ */
+ public static LogicalExpression or(Criterion lhs, Criterion rhs) {
+ return new LogicalExpression(lhs, rhs, "or");
+ }
+ /**
+ * Return the negation of an expression
+ *
+ * @param expression
+ * @return Criterion
+ */
+ public static Criterion not(Criterion expression) {
+ return new NotExpression(expression);
+ }
+ /**
+ * Apply a constraint expressed in SQL, with the given JDBC
+ * parameters. Any occurrences of <tt>{alias}</tt> will be
+ * replaced by the table alias.
+ *
+ * @param sql
+ * @param values
+ * @param types
+ * @return Criterion
+ */
+ public static Criterion sqlRestriction(String sql, Object[] values, Type[] types) {
+ return new SQLCriterion(sql, values, types);
+ }
+ /**
+ * Apply a constraint expressed in SQL, with the given JDBC
+ * parameter. Any occurrences of <tt>{alias}</tt> will be replaced
+ * by the table alias.
+ *
+ * @param sql
+ * @param value
+ * @param type
+ * @return Criterion
+ */
+ public static Criterion sqlRestriction(String sql, Object value, Type type) {
+ return new SQLCriterion(sql, new Object[] { value }, new Type[] { type } );
+ }
+ /**
+ * Apply a constraint expressed in SQL. Any occurrences of <tt>{alias}</tt>
+ * will be replaced by the table alias.
+ *
+ * @param sql
+ * @return Criterion
+ */
+ public static Criterion sqlRestriction(String sql) {
+ return new SQLCriterion(sql, ArrayHelper.EMPTY_OBJECT_ARRAY, ArrayHelper.EMPTY_TYPE_ARRAY);
+ }
+
+ /**
+ * Group expressions together in a single conjunction (A and B and C...)
+ *
+ * @return Conjunction
+ */
+ public static Conjunction conjunction() {
+ return new Conjunction();
+ }
+
+ /**
+ * Group expressions together in a single disjunction (A or B or C...)
+ *
+ * @return Conjunction
+ */
+ public static Disjunction disjunction() {
+ return new Disjunction();
+ }
+
+ /**
+ * Apply an "equals" constraint to each property in the
+ * key set of a <tt>Map</tt>
+ *
+ * @param propertyNameValues a map from property names to values
+ * @return Criterion
+ */
+ public static Criterion allEq(Map propertyNameValues) {
+ Conjunction conj = conjunction();
+ Iterator iter = propertyNameValues.entrySet().iterator();
+ while ( iter.hasNext() ) {
+ Map.Entry me = (Map.Entry) iter.next();
+ conj.add( eq( (String) me.getKey(), me.getValue() ) );
+ }
+ return conj;
+ }
+
+ /**
+ * Constrain a collection valued property to be empty
+ */
+ public static Criterion isEmpty(String propertyName) {
+ return new EmptyExpression(propertyName);
+ }
+
+ /**
+ * Constrain a collection valued property to be non-empty
+ */
+ public static Criterion isNotEmpty(String propertyName) {
+ return new NotEmptyExpression(propertyName);
+ }
+
+ /**
+ * Constrain a collection valued property by size
+ */
+ public static Criterion sizeEq(String propertyName, int size) {
+ return new SizeExpression(propertyName, size, "=");
+ }
+
+ /**
+ * Constrain a collection valued property by size
+ */
+ public static Criterion sizeNe(String propertyName, int size) {
+ return new SizeExpression(propertyName, size, "<>");
+ }
+
+ /**
+ * Constrain a collection valued property by size
+ */
+ public static Criterion sizeGt(String propertyName, int size) {
+ return new SizeExpression(propertyName, size, "<");
+ }
+
+ /**
+ * Constrain a collection valued property by size
+ */
+ public static Criterion sizeLt(String propertyName, int size) {
+ return new SizeExpression(propertyName, size, ">");
+ }
+
+ /**
+ * Constrain a collection valued property by size
+ */
+ public static Criterion sizeGe(String propertyName, int size) {
+ return new SizeExpression(propertyName, size, "<=");
+ }
+
+ /**
+ * Constrain a collection valued property by size
+ */
+ public static Criterion sizeLe(String propertyName, int size) {
+ return new SizeExpression(propertyName, size, ">=");
+ }
+
+ public static NaturalIdentifier naturalId() {
+ return new NaturalIdentifier();
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Restrictions.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/RowCountProjection.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/RowCountProjection.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/RowCountProjection.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,35 @@
+//$Id: RowCountProjection.java 9908 2006-05-08 20:59:20Z max.andersen at jboss.com $
+package org.hibernate.criterion;
+
+import org.hibernate.Criteria;
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.type.Type;
+
+/**
+ * A row count
+ * @author Gavin King
+ */
+public class RowCountProjection extends SimpleProjection {
+
+ protected RowCountProjection() {}
+
+ public String toString() {
+ return "count(*)";
+ }
+
+ public Type[] getTypes(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ return new Type[] { Hibernate.INTEGER };
+ }
+
+ public String toSqlString(Criteria criteria, int position, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ return new StringBuffer()
+ .append("count(*) as y")
+ .append(position)
+ .append('_')
+ .toString();
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/RowCountProjection.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/SQLCriterion.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/SQLCriterion.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/SQLCriterion.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,45 @@
+//$Id: SQLCriterion.java 5757 2005-02-18 03:47:27Z oneovthafew $
+package org.hibernate.criterion;
+
+
+import org.hibernate.Criteria;
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.TypedValue;
+import org.hibernate.type.Type;
+import org.hibernate.util.StringHelper;
+
+/**
+ * A SQL fragment. The string {alias} will be replaced by the
+ * alias of the root entity.
+ */
+public class SQLCriterion implements Criterion {
+
+ private final String sql;
+ private final TypedValue[] typedValues;
+
+ public String toSqlString(
+ Criteria criteria,
+ CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ return StringHelper.replace( sql, "{alias}", criteriaQuery.getSQLAlias(criteria) );
+ }
+
+ public TypedValue[] getTypedValues(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ return typedValues;
+ }
+
+ public String toString() {
+ return sql;
+ }
+
+ protected SQLCriterion(String sql, Object[] values, Type[] types) {
+ this.sql = sql;
+ typedValues = new TypedValue[values.length];
+ for ( int i=0; i<typedValues.length; i++ ) {
+ typedValues[i] = new TypedValue( types[i], values[i], EntityMode.POJO );
+ }
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/SQLProjection.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/SQLProjection.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/SQLProjection.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,77 @@
+//$Id: SQLProjection.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate.criterion;
+
+
+import org.hibernate.Criteria;
+import org.hibernate.HibernateException;
+import org.hibernate.type.Type;
+import org.hibernate.util.StringHelper;
+
+/**
+ * A SQL fragment. The string {alias} will be replaced by the
+ * alias of the root entity.
+ */
+public class SQLProjection implements Projection {
+
+ private final String sql;
+ private final String groupBy;
+ private final Type[] types;
+ private String[] aliases;
+ private String[] columnAliases;
+ private boolean grouped;
+
+ public String toSqlString(
+ Criteria criteria,
+ int loc,
+ CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ return StringHelper.replace( sql, "{alias}", criteriaQuery.getSQLAlias(criteria) );
+ }
+
+ public String toGroupSqlString(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ return StringHelper.replace( groupBy, "{alias}", criteriaQuery.getSQLAlias(criteria) );
+ }
+
+ public Type[] getTypes(Criteria crit, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ return types;
+ }
+
+ public String toString() {
+ return sql;
+ }
+
+ protected SQLProjection(String sql, String[] columnAliases, Type[] types) {
+ this(sql, null, columnAliases, types);
+ }
+
+ protected SQLProjection(String sql, String groupBy, String[] columnAliases, Type[] types) {
+ this.sql = sql;
+ this.types = types;
+ this.aliases = columnAliases;
+ this.columnAliases = columnAliases;
+ this.grouped = groupBy!=null;
+ this.groupBy = groupBy;
+ }
+
+ public String[] getAliases() {
+ return aliases;
+ }
+
+ public String[] getColumnAliases(int loc) {
+ return columnAliases;
+ }
+
+ public boolean isGrouped() {
+ return grouped;
+ }
+
+ public Type[] getTypes(String alias, Criteria crit, CriteriaQuery criteriaQuery) {
+ return null; //unsupported
+ }
+
+ public String[] getColumnAliases(String alias, int loc) {
+ return null; //unsupported
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/SQLProjection.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/SimpleExpression.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/SimpleExpression.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/SimpleExpression.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,82 @@
+//$Id: SimpleExpression.java 7641 2005-07-25 04:57:05Z oneovthafew $
+package org.hibernate.criterion;
+
+
+import java.sql.Types;
+
+import org.hibernate.Criteria;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.TypedValue;
+import org.hibernate.type.Type;
+
+/**
+ * superclass for "simple" comparisons (with SQL binary operators)
+ * @author Gavin King
+ */
+public class SimpleExpression implements Criterion {
+
+ private final String propertyName;
+ private final Object value;
+ private boolean ignoreCase;
+ private final String op;
+
+ protected SimpleExpression(String propertyName, Object value, String op) {
+ this.propertyName = propertyName;
+ this.value = value;
+ this.op = op;
+ }
+
+ protected SimpleExpression(String propertyName, Object value, String op, boolean ignoreCase) {
+ this.propertyName = propertyName;
+ this.value = value;
+ this.ignoreCase = ignoreCase;
+ this.op = op;
+ }
+
+ public SimpleExpression ignoreCase() {
+ ignoreCase = true;
+ return this;
+ }
+
+ public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+
+ String[] columns = criteriaQuery.getColumnsUsingProjection(criteria, propertyName);
+ Type type = criteriaQuery.getTypeUsingProjection(criteria, propertyName);
+ StringBuffer fragment = new StringBuffer();
+ if (columns.length>1) fragment.append('(');
+ SessionFactoryImplementor factory = criteriaQuery.getFactory();
+ int[] sqlTypes = type.sqlTypes( factory );
+ for ( int i=0; i<columns.length; i++ ) {
+ boolean lower = ignoreCase &&
+ ( sqlTypes[i]==Types.VARCHAR || sqlTypes[i]==Types.CHAR );
+ if (lower) {
+ fragment.append( factory.getDialect().getLowercaseFunction() )
+ .append('(');
+ }
+ fragment.append( columns[i] );
+ if (lower) fragment.append(')');
+ fragment.append( getOp() ).append("?");
+ if ( i<columns.length-1 ) fragment.append(" and ");
+ }
+ if (columns.length>1) fragment.append(')');
+ return fragment.toString();
+
+ }
+
+ public TypedValue[] getTypedValues(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ Object icvalue = ignoreCase ? value.toString().toLowerCase() : value;
+ return new TypedValue[] { criteriaQuery.getTypedValue(criteria, propertyName, icvalue) };
+ }
+
+ public String toString() {
+ return propertyName + getOp() + value;
+ }
+
+ protected final String getOp() {
+ return op;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/SimpleProjection.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/SimpleProjection.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/SimpleProjection.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,45 @@
+//$Id: SimpleProjection.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate.criterion;
+
+import org.hibernate.Criteria;
+import org.hibernate.HibernateException;
+import org.hibernate.type.Type;
+
+
+/**
+ * A single-column projection that may be aliased
+ * @author Gavin King
+ */
+public abstract class SimpleProjection implements Projection {
+
+ public Projection as(String alias) {
+ return Projections.alias(this, alias);
+ }
+
+ public String[] getColumnAliases(String alias, int loc) {
+ return null;
+ }
+
+ public Type[] getTypes(String alias, Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ return null;
+ }
+
+ public String[] getColumnAliases(int loc) {
+ return new String[] { "y" + loc + "_" };
+ }
+
+ public String[] getAliases() {
+ return new String[1];
+ }
+
+ public String toGroupSqlString(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ throw new UnsupportedOperationException("not a grouping projection");
+ }
+
+ public boolean isGrouped() {
+ return false;
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/SimpleProjection.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/SimpleSubqueryExpression.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/SimpleSubqueryExpression.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/SimpleSubqueryExpression.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,35 @@
+//$Id: SimpleSubqueryExpression.java 5757 2005-02-18 03:47:27Z oneovthafew $
+package org.hibernate.criterion;
+
+import org.hibernate.Criteria;
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.TypedValue;
+
+/**
+ * A comparison between a constant value and the the result of a subquery
+ * @author Gavin King
+ */
+public class SimpleSubqueryExpression extends SubqueryExpression {
+
+ private Object value;
+
+ protected SimpleSubqueryExpression(Object value, String op, String quantifier, DetachedCriteria dc) {
+ super(op, quantifier, dc);
+ this.value = value;
+ }
+
+
+ public TypedValue[] getTypedValues(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ TypedValue[] superTv = super.getTypedValues(criteria, criteriaQuery);
+ TypedValue[] result = new TypedValue[superTv.length+1];
+ System.arraycopy(superTv, 0, result, 1, superTv.length);
+ result[0] = new TypedValue( getTypes()[0], value, EntityMode.POJO );
+ return result;
+ }
+
+ protected String toLeftSqlString(Criteria criteria, CriteriaQuery criteriaQuery) {
+ return "?";
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/SimpleSubqueryExpression.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/SizeExpression.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/SizeExpression.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/SizeExpression.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,63 @@
+//$Id: SizeExpression.java 7844 2005-08-11 07:26:26Z oneovthafew $
+package org.hibernate.criterion;
+
+
+import org.hibernate.Criteria;
+import org.hibernate.EntityMode;
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.TypedValue;
+import org.hibernate.persister.collection.QueryableCollection;
+import org.hibernate.persister.entity.Loadable;
+import org.hibernate.sql.ConditionFragment;
+
+/**
+ * @author Gavin King
+ */
+public class SizeExpression implements Criterion {
+
+ private final String propertyName;
+ private final int size;
+ private final String op;
+
+ protected SizeExpression(String propertyName, int size, String op) {
+ this.propertyName = propertyName;
+ this.size = size;
+ this.op = op;
+ }
+
+ public String toString() {
+ return propertyName + ".size" + op + size;
+ }
+
+ public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ String role = criteriaQuery.getEntityName(criteria, propertyName) +
+ '.' +
+ criteriaQuery.getPropertyName(propertyName);
+ QueryableCollection cp = (QueryableCollection) criteriaQuery.getFactory()
+ .getCollectionPersister(role);
+ //String[] fk = StringHelper.qualify( "collection_", cp.getKeyColumnNames() );
+ String[] fk = cp.getKeyColumnNames();
+ String[] pk = ( (Loadable) cp.getOwnerEntityPersister() ).getIdentifierColumnNames(); //TODO: handle property-ref
+ return "? " +
+ op +
+ " (select count(*) from " +
+ cp.getTableName() +
+ //" collection_ where " +
+ " where " +
+ new ConditionFragment()
+ .setTableAlias( criteriaQuery.getSQLAlias(criteria, propertyName) )
+ .setCondition(pk, fk)
+ .toFragmentString() +
+ ")";
+ }
+
+ public TypedValue[] getTypedValues(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ return new TypedValue[] {
+ new TypedValue( Hibernate.INTEGER, new Integer(size), EntityMode.POJO )
+ };
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/SizeExpression.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Subqueries.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Subqueries.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Subqueries.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,160 @@
+//$Id: Subqueries.java 8467 2005-10-26 21:20:17Z oneovthafew $
+package org.hibernate.criterion;
+
+/**
+ * Factory class for criterion instances that represent expressions
+ * involving subqueries.
+ *
+ * @see Restriction
+ * @see Projection
+ * @see org.hibernate.Criteria
+ * @author Gavin King
+ */
+public class Subqueries {
+
+ public static Criterion exists(DetachedCriteria dc) {
+ return new ExistsSubqueryExpression("exists", dc);
+ }
+
+ public static Criterion notExists(DetachedCriteria dc) {
+ return new ExistsSubqueryExpression("not exists", dc);
+ }
+
+ public static Criterion propertyEqAll(String propertyName, DetachedCriteria dc) {
+ return new PropertySubqueryExpression(propertyName, "=", "all", dc);
+ }
+
+ public static Criterion propertyIn(String propertyName, DetachedCriteria dc) {
+ return new PropertySubqueryExpression(propertyName, "in", null, dc);
+ }
+
+ public static Criterion propertyNotIn(String propertyName, DetachedCriteria dc) {
+ return new PropertySubqueryExpression(propertyName, "not in", null, dc);
+ }
+
+ public static Criterion propertyEq(String propertyName, DetachedCriteria dc) {
+ return new PropertySubqueryExpression(propertyName, "=", null, dc);
+ }
+
+ public static Criterion propertyNe(String propertyName, DetachedCriteria dc) {
+ return new PropertySubqueryExpression(propertyName, "<>", null, dc);
+ }
+
+ public static Criterion propertyGt(String propertyName, DetachedCriteria dc) {
+ return new PropertySubqueryExpression(propertyName, ">", null, dc);
+ }
+
+ public static Criterion propertyLt(String propertyName, DetachedCriteria dc) {
+ return new PropertySubqueryExpression(propertyName, "<", null, dc);
+ }
+
+ public static Criterion propertyGe(String propertyName, DetachedCriteria dc) {
+ return new PropertySubqueryExpression(propertyName, ">=", null, dc);
+ }
+
+ public static Criterion propertyLe(String propertyName, DetachedCriteria dc) {
+ return new PropertySubqueryExpression(propertyName, "<=", null, dc);
+ }
+
+ public static Criterion propertyGtAll(String propertyName, DetachedCriteria dc) {
+ return new PropertySubqueryExpression(propertyName, ">", "all", dc);
+ }
+
+ public static Criterion propertyLtAll(String propertyName, DetachedCriteria dc) {
+ return new PropertySubqueryExpression(propertyName, "<", "all", dc);
+ }
+
+ public static Criterion propertyGeAll(String propertyName, DetachedCriteria dc) {
+ return new PropertySubqueryExpression(propertyName, ">=", "all", dc);
+ }
+
+ public static Criterion propertyLeAll(String propertyName, DetachedCriteria dc) {
+ return new PropertySubqueryExpression(propertyName, "<=", "all", dc);
+ }
+
+ public static Criterion propertyGtSome(String propertyName, DetachedCriteria dc) {
+ return new PropertySubqueryExpression(propertyName, ">", "some", dc);
+ }
+
+ public static Criterion propertyLtSome(String propertyName, DetachedCriteria dc) {
+ return new PropertySubqueryExpression(propertyName, "<", "some", dc);
+ }
+
+ public static Criterion propertyGeSome(String propertyName, DetachedCriteria dc) {
+ return new PropertySubqueryExpression(propertyName, ">=", "some", dc);
+ }
+
+ public static Criterion propertyLeSome(String propertyName, DetachedCriteria dc) {
+ return new PropertySubqueryExpression(propertyName, "<=", "some", dc);
+ }
+
+ public static Criterion eqAll(Object value, DetachedCriteria dc) {
+ return new SimpleSubqueryExpression(value, "=", "all", dc);
+ }
+
+ public static Criterion in(Object value, DetachedCriteria dc) {
+ return new SimpleSubqueryExpression(value, "in", null, dc);
+ }
+
+ public static Criterion notIn(Object value, DetachedCriteria dc) {
+ return new SimpleSubqueryExpression(value, "not in", null, dc);
+ }
+
+ public static Criterion eq(Object value, DetachedCriteria dc) {
+ return new SimpleSubqueryExpression(value, "=", null, dc);
+ }
+
+ public static Criterion gt(Object value, DetachedCriteria dc) {
+ return new SimpleSubqueryExpression(value, ">", null, dc);
+ }
+
+ public static Criterion lt(Object value, DetachedCriteria dc) {
+ return new SimpleSubqueryExpression(value, "<", null, dc);
+ }
+
+ public static Criterion ge(Object value, DetachedCriteria dc) {
+ return new SimpleSubqueryExpression(value, ">=", null, dc);
+ }
+
+ public static Criterion le(Object value, DetachedCriteria dc) {
+ return new SimpleSubqueryExpression(value, "<=", null, dc);
+ }
+
+ public static Criterion ne(Object value, DetachedCriteria dc) {
+ return new SimpleSubqueryExpression(value, "<>", null, dc);
+ }
+
+ public static Criterion gtAll(Object value, DetachedCriteria dc) {
+ return new SimpleSubqueryExpression(value, ">", "all", dc);
+ }
+
+ public static Criterion ltAll(Object value, DetachedCriteria dc) {
+ return new SimpleSubqueryExpression(value, "<", "all", dc);
+ }
+
+ public static Criterion geAll(Object value, DetachedCriteria dc) {
+ return new SimpleSubqueryExpression(value, ">=", "all", dc);
+ }
+
+ public static Criterion leAll(Object value, DetachedCriteria dc) {
+ return new SimpleSubqueryExpression(value, "<=", "all", dc);
+ }
+
+ public static Criterion gtSome(Object value, DetachedCriteria dc) {
+ return new SimpleSubqueryExpression(value, ">", "some", dc);
+ }
+
+ public static Criterion ltSome(Object value, DetachedCriteria dc) {
+ return new SimpleSubqueryExpression(value, "<", "some", dc);
+ }
+
+ public static Criterion geSome(Object value, DetachedCriteria dc) {
+ return new SimpleSubqueryExpression(value, ">=", "some", dc);
+ }
+
+ public static Criterion leSome(Object value, DetachedCriteria dc) {
+ return new SimpleSubqueryExpression(value, "<=", "some", dc);
+ }
+
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/Subqueries.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/SubqueryExpression.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/SubqueryExpression.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/SubqueryExpression.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,89 @@
+//$Id: SubqueryExpression.java 7365 2005-07-04 02:40:29Z oneovthafew $
+package org.hibernate.criterion;
+
+import org.hibernate.Criteria;
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.TypedValue;
+import org.hibernate.impl.CriteriaImpl;
+import org.hibernate.loader.criteria.CriteriaQueryTranslator;
+import org.hibernate.persister.entity.OuterJoinLoadable;
+import org.hibernate.sql.Select;
+import org.hibernate.type.Type;
+
+/**
+ * @author Gavin King
+ */
+public abstract class SubqueryExpression implements Criterion {
+
+ private CriteriaImpl criteriaImpl;
+ private String quantifier;
+ private String op;
+ private QueryParameters params;
+ private Type[] types;
+
+ protected Type[] getTypes() {
+ return types;
+ }
+
+ protected SubqueryExpression(String op, String quantifier, DetachedCriteria dc) {
+ this.criteriaImpl = dc.getCriteriaImpl();
+ this.quantifier = quantifier;
+ this.op = op;
+ }
+
+ protected abstract String toLeftSqlString(Criteria criteria, CriteriaQuery outerQuery);
+
+ public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+
+ final SessionImplementor session = ( (CriteriaImpl) criteria ).getSession(); //ugly!
+ final SessionFactoryImplementor factory = session.getFactory();
+
+ final OuterJoinLoadable persister = (OuterJoinLoadable) factory.getEntityPersister( criteriaImpl.getEntityOrClassName() );
+ CriteriaQueryTranslator innerQuery = new CriteriaQueryTranslator(
+ factory,
+ criteriaImpl,
+ criteriaImpl.getEntityOrClassName(), //implicit polymorphism not supported (would need a union)
+ criteriaQuery.generateSQLAlias(),
+ criteriaQuery
+ );
+
+ params = innerQuery.getQueryParameters(); //TODO: bad lifecycle....
+ types = innerQuery.getProjectedTypes();
+
+ //String filter = persister.filterFragment( innerQuery.getRootSQLALias(), session.getEnabledFilters() );
+
+ String sql = new Select( factory.getDialect() )
+ .setWhereClause( innerQuery.getWhereCondition() )
+ .setGroupByClause( innerQuery.getGroupBy() )
+ .setSelectClause( innerQuery.getSelect() )
+ .setFromClause(
+ persister.fromTableFragment( innerQuery.getRootSQLALias() ) +
+ persister.fromJoinFragment( innerQuery.getRootSQLALias(), true, false )
+ )
+ .toStatementString();
+
+ final StringBuffer buf = new StringBuffer()
+ .append( toLeftSqlString(criteria, criteriaQuery) );
+ if (op!=null) buf.append(' ').append(op).append(' ');
+ if (quantifier!=null) buf.append(quantifier).append(' ');
+ return buf.append('(').append(sql).append(')')
+ .toString();
+ }
+
+ public TypedValue[] getTypedValues(Criteria criteria, CriteriaQuery criteriaQuery)
+ throws HibernateException {
+ Type[] types = params.getPositionalParameterTypes();
+ Object[] values = params.getPositionalParameterValues();
+ TypedValue[] tv = new TypedValue[types.length];
+ for ( int i=0; i<types.length; i++ ) {
+ tv[i] = new TypedValue( types[i], values[i], EntityMode.POJO );
+ }
+ return tv;
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/SubqueryExpression.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,8 @@
+<html>
+<head></head>
+<body>
+<p>
+ A framework for defining restriction criteria and order criteria.
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/criterion/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/Cache71Dialect.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/Cache71Dialect.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/Cache71Dialect.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,655 @@
+//$Id: $
+package org.hibernate.dialect;
+
+import java.sql.CallableStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+
+import org.hibernate.Hibernate;
+import org.hibernate.LockMode;
+import org.hibernate.MappingException;
+import org.hibernate.cfg.Environment;
+import org.hibernate.dialect.function.NoArgSQLFunction;
+import org.hibernate.dialect.function.NvlFunction;
+import org.hibernate.dialect.function.SQLFunctionTemplate;
+import org.hibernate.dialect.function.StandardSQLFunction;
+import org.hibernate.dialect.function.VarArgsSQLFunction;
+import org.hibernate.dialect.function.StandardJDBCEscapeFunction;
+import org.hibernate.dialect.function.ConvertFunction;
+import org.hibernate.dialect.function.ConditionalParenthesisFunction;
+import org.hibernate.dialect.lock.LockingStrategy;
+import org.hibernate.dialect.lock.SelectLockingStrategy;
+import org.hibernate.dialect.lock.UpdateLockingStrategy;
+import org.hibernate.exception.CacheSQLStateConverter;
+import org.hibernate.exception.SQLExceptionConverter;
+import org.hibernate.exception.TemplatedViolatedConstraintNameExtracter;
+import org.hibernate.exception.ViolatedConstraintNameExtracter;
+import org.hibernate.id.IdentityGenerator;
+import org.hibernate.persister.entity.Lockable;
+import org.hibernate.sql.CacheJoinFragment;
+import org.hibernate.sql.JoinFragment;
+import org.hibernate.util.StringHelper;
+
+/**
+ * Caché 2007.1 dialect. This class is required in order to use Hibernate with Intersystems Caché SQL.<br>
+ * <br>
+ * Compatible with Caché 2007.1.
+ * <br>
+ * <head>
+ * <title>Caché and Hibernate</title>
+ * </head>
+ * <body>
+ * <h1>Caché and Hibernate</h1>
+ * <h2>PREREQUISITES</h2>
+ * These setup instructions assume that both Caché and Hibernate are installed and operational.
+ * <br>
+ * <h2>HIBERNATE DIRECTORIES AND FILES</h2>
+ * JBoss distributes the InterSystems Cache' dialect for Hibernate 3.2.1
+ * For earlier versions of Hibernate please contact
+ * <a href="http://www.intersystems.com/support/cache-support.html">InterSystems Worldwide Response Center</A> (WRC)
+ * for the appropriate source files.
+ * <br>
+ * <h2>CACHÉ DOCUMENTATION</h2>
+ * Documentation for Caché is available online when Caché is running.
+ * It can also be obtained from the
+ * <a href="http://www.intersystems.com/cache/downloads/documentation.html">InterSystems</A> website.
+ * The book, "Object-oriented Application Development Using the Caché Post-relational Database:
+ * is also available from Springer-Verlag.
+ * <br>
+ * <h2>HIBERNATE DOCUMENTATION</h2>
+ * Hibernate comes with extensive electronic documentation.
+ * In addition, several books on Hibernate are available from
+ * <a href="http://www.manning.com">Manning Publications Co</a>.
+ * Three available titles are "Hibernate Quickly", "Hibernate in Action", and "Java Persistence with Hibernate".
+ * <br>
+ * <h2>TO SET UP HIBERNATE FOR USE WITH CACHÉ</h2>
+ * The following steps assume that the directory where Caché was installed is C:\CacheSys.
+ * This is the default installation directory for Caché.
+ * The default installation directory for Hibernate is assumed to be C:\Hibernate.
+ * <p/>
+ * If either product is installed in a different location, the pathnames that follow should be modified appropriately.
+ * <p/>
+ * Caché version 2007.1 and above is recommended for use with
+ * Hibernate. The next step depends on the location of your
+ * CacheDB.jar depending on your version of Caché.
+ * <ol>
+ * <li>Copy C:\CacheSys\dev\java\lib\JDK15\CacheDB.jar to C:\Hibernate\lib\CacheDB.jar.</li>
+ * <p/>
+ * <li>Insert the following files into your Java classpath:
+ * <p/>
+ * <ul>
+ * <li>All jar files in the directory C:\Hibernate\lib</li>
+ * <li>The directory (or directories) where hibernate.properties and/or hibernate.cfg.xml are kept.</li>
+ * </ul>
+ * </li>
+ * <p/>
+ * <li>In the file, hibernate.properties (or hibernate.cfg.xml),
+ * specify the Caché dialect and the Caché version URL settings.</li>
+ * </ol>
+ * <p/>
+ * For example, in Hibernate 3.2, typical entries in hibernate.properties would have the following
+ * "name=value" pairs:
+ * <p/>
+ * <table cols=3 border cellpadding=5 cellspacing=0>
+ * <tr>
+ * <th>Property Name</th>
+ * <th>Property Value</th>
+ * </tr>
+ * <tr>
+ * <td>hibernate.dialect</td>
+ * <td>org.hibernate.dialect.Cache71Dialect</td>
+ * </tr>
+ * <tr>
+ * <td>hibernate.connection.driver_class</td>
+ * <td>com.intersys.jdbc.CacheDriver</td>
+ * </tr>
+ * <tr>
+ * <td>hibernate.connection.username</td>
+ * <td>(see note 1)</td>
+ * </tr>
+ * <tr>
+ * <td>hibernate.connection.password</td>
+ * <td>(see note 1)</td>
+ * </tr>
+ * <tr>
+ * <td>hibernate.connection.url</td>
+ * <td>jdbc:Cache://127.0.0.1:1972/USER</td>
+ * </tr>
+ * </table>
+ * <p/>
+ * <dl>
+ * <dt><b>Note 1</b></dt>
+ * <dd>Please contact your administrator for the userid and password you should use when attempting access via JDBC.
+ * By default, these are chosen to be "_SYSTEM" and "SYS" respectively as noted in the SQL standard.</dd>
+ * </dl>
+ * <br>
+ * <h2>CACHÉ VERSION URL</h2>
+ * This is the standard URL for the JDBC driver.
+ * For a JDBC driver on the machine hosting Caché, use the IP "loopback" address, 127.0.0.1.
+ * For 1972, the default port, specify the super server port of your Caché instance.
+ * For USER, substitute the NAMESPACE which contains your Caché database data.
+ * <br>
+ * <h2>CACHÉ DIALECTS</h2>
+ * Choices for Dialect are:
+ * <br>
+ * <p/>
+ * <ol>
+ * <li>org.hibernate.dialect.Cache71Dialect (requires Caché
+ * 2007.1 or above)</li>
+ * <p/>
+ * </ol>
+ * <br>
+ * <h2>SUPPORT FOR IDENTITY COLUMNS</h2>
+ * Caché 2007.1 or later supports identity columns. For
+ * Hibernate to use identity columns, specify "native" as the
+ * generator.
+ * <br>
+ * <h2>SEQUENCE DIALECTS SUPPORT SEQUENCES</h2>
+ * <p/>
+ * To use Hibernate sequence support with Caché in a namespace, you must FIRST load the following file into that namespace:
+ * <pre>
+ * etc\CacheSequences.xml
+ * </pre>
+ * For example, at the COS terminal prompt in the namespace, run the
+ * following command:
+ * <p>
+ * d LoadFile^%apiOBJ("c:\hibernate\etc\CacheSequences.xml","ck")
+ * <p>
+ * In your Hibernate mapping you can specify sequence use.
+ * <p>
+ * For example, the following shows the use of a sequence generator in a Hibernate mapping:
+ * <pre>
+ * <id name="id" column="uid" type="long" unsaved-value="null">
+ * <generator class="sequence"/>
+ * </id>
+ * </pre>
+ * <br>
+ * <p/>
+ * Some versions of Hibernate under some circumstances call
+ * getSelectSequenceNextValString() in the dialect. If this happens
+ * you will receive the error message: new MappingException( "Dialect
+ * does not support sequences" ).
+ * <br>
+ * <h2>HIBERNATE FILES ASSOCIATED WITH CACHÉ DIALECT</h2>
+ * The following files are associated with Caché dialect:
+ * <p/>
+ * <ol>
+ * <li>src\org\hibernate\dialect\Cache71Dialect.java</li>
+ * <li>src\org\hibernate\dialect\function\ConditionalParenthesisFunction.java</li>
+ * <li>src\org\hibernate\dialect\function\ConvertFunction.java</li>
+ * <li>src\org\hibernate\exception\CacheSQLStateConverter.java</li>
+ * <li>src\org\hibernate\sql\CacheJoinFragment.java</li>
+ * </ol>
+ * Cache71Dialect ships with Hibernate 3.2. All other dialects are distributed by InterSystems and subclass Cache71Dialect.
+ *
+ * @author Jonathan Levinson
+ */
+
+public class Cache71Dialect extends Dialect {
+
+ /**
+ * Creates new <code>Caché71Dialect</code> instance. Sets up the JDBC /
+ * Caché type mappings.
+ */
+ public Cache71Dialect() {
+ super();
+ commonRegistration();
+ register71Functions();
+ }
+
+ protected final void commonRegistration() {
+ // Note: For object <-> SQL datatype mappings see:
+ // Configuration Manager | Advanced | SQL | System DDL Datatype Mappings
+ //
+ // TBD registerColumnType(Types.BINARY, "binary($1)");
+ // changed 08-11-2005, jsl
+ registerColumnType( Types.BINARY, "varbinary($1)" );
+ registerColumnType( Types.BIGINT, "BigInt" );
+ registerColumnType( Types.BIT, "bit" );
+ registerColumnType( Types.CHAR, "char(1)" );
+ registerColumnType( Types.DATE, "date" );
+ registerColumnType( Types.DECIMAL, "decimal" );
+ registerColumnType( Types.DOUBLE, "double" );
+ registerColumnType( Types.FLOAT, "float" );
+ registerColumnType( Types.INTEGER, "integer" );
+ registerColumnType( Types.LONGVARBINARY, "longvarbinary" ); // binary %Stream
+ registerColumnType( Types.LONGVARCHAR, "longvarchar" ); // character %Stream
+ registerColumnType( Types.NUMERIC, "numeric($p,$s)" );
+ registerColumnType( Types.REAL, "real" );
+ registerColumnType( Types.SMALLINT, "smallint" );
+ registerColumnType( Types.TIMESTAMP, "timestamp" );
+ registerColumnType( Types.TIME, "time" );
+ registerColumnType( Types.TINYINT, "tinyint" );
+ // TBD should this be varbinary($1)?
+ // registerColumnType(Types.VARBINARY, "binary($1)");
+ registerColumnType( Types.VARBINARY, "longvarbinary" );
+ registerColumnType( Types.VARCHAR, "varchar($l)" );
+ registerColumnType( Types.BLOB, "longvarbinary" );
+ registerColumnType( Types.CLOB, "longvarchar" );
+
+ getDefaultProperties().setProperty( Environment.USE_STREAMS_FOR_BINARY, "false" );
+ getDefaultProperties().setProperty( Environment.STATEMENT_BATCH_SIZE, DEFAULT_BATCH_SIZE );
+ //getDefaultProperties().setProperty(Environment.STATEMENT_BATCH_SIZE, NO_BATCH);
+
+ getDefaultProperties().setProperty( Environment.USE_SQL_COMMENTS, "false" );
+
+ registerFunction( "abs", new StandardSQLFunction( "abs" ) );
+ registerFunction( "acos", new StandardJDBCEscapeFunction( "acos", Hibernate.DOUBLE ) );
+ registerFunction( "%alphaup", new StandardSQLFunction( "%alphaup", Hibernate.STRING ) );
+ registerFunction( "ascii", new StandardSQLFunction( "ascii", Hibernate.STRING ) );
+ registerFunction( "asin", new StandardJDBCEscapeFunction( "asin", Hibernate.DOUBLE ) );
+ registerFunction( "atan", new StandardJDBCEscapeFunction( "atan", Hibernate.DOUBLE ) );
+ registerFunction( "bit_length", new SQLFunctionTemplate( Hibernate.INTEGER, "($length(?1)*8)" ) );
+ // hibernate impelemnts cast in Dialect.java
+ registerFunction( "ceiling", new StandardSQLFunction( "ceiling", Hibernate.INTEGER ) );
+ registerFunction( "char", new StandardJDBCEscapeFunction( "char", Hibernate.CHARACTER ) );
+ registerFunction( "character_length", new StandardSQLFunction( "character_length", Hibernate.INTEGER ) );
+ registerFunction( "char_length", new StandardSQLFunction( "char_length", Hibernate.INTEGER ) );
+ registerFunction( "cos", new StandardJDBCEscapeFunction( "cos", Hibernate.DOUBLE ) );
+ registerFunction( "cot", new StandardJDBCEscapeFunction( "cot", Hibernate.DOUBLE ) );
+ registerFunction( "coalesce", new VarArgsSQLFunction( "coalesce(", ",", ")" ) );
+ registerFunction( "concat", new VarArgsSQLFunction( Hibernate.STRING, "", "||", "" ) );
+ registerFunction( "convert", new ConvertFunction() );
+ registerFunction( "curdate", new StandardJDBCEscapeFunction( "curdate", Hibernate.DATE ) );
+ registerFunction( "current_date", new NoArgSQLFunction( "current_date", Hibernate.DATE, false ) );
+ registerFunction( "current_time", new NoArgSQLFunction( "current_time", Hibernate.TIME, false ) );
+ registerFunction(
+ "current_timestamp", new ConditionalParenthesisFunction( "current_timestamp", Hibernate.TIMESTAMP )
+ );
+ registerFunction( "curtime", new StandardJDBCEscapeFunction( "curtime", Hibernate.TIME ) );
+ registerFunction( "database", new StandardJDBCEscapeFunction( "database", Hibernate.STRING ) );
+ registerFunction( "dateadd", new VarArgsSQLFunction( Hibernate.TIMESTAMP, "dateadd(", ",", ")" ) );
+ registerFunction( "datediff", new VarArgsSQLFunction( Hibernate.INTEGER, "datediff(", ",", ")" ) );
+ registerFunction( "datename", new VarArgsSQLFunction( Hibernate.STRING, "datename(", ",", ")" ) );
+ registerFunction( "datepart", new VarArgsSQLFunction( Hibernate.INTEGER, "datepart(", ",", ")" ) );
+ registerFunction( "day", new StandardSQLFunction( "day", Hibernate.INTEGER ) );
+ registerFunction( "dayname", new StandardJDBCEscapeFunction( "dayname", Hibernate.STRING ) );
+ registerFunction( "dayofmonth", new StandardJDBCEscapeFunction( "dayofmonth", Hibernate.INTEGER ) );
+ registerFunction( "dayofweek", new StandardJDBCEscapeFunction( "dayofweek", Hibernate.INTEGER ) );
+ registerFunction( "dayofyear", new StandardJDBCEscapeFunction( "dayofyear", Hibernate.INTEGER ) );
+ // is it necessary to register %exact since it can only appear in a where clause?
+ registerFunction( "%exact", new StandardSQLFunction( "%exact", Hibernate.STRING ) );
+ registerFunction( "exp", new StandardJDBCEscapeFunction( "exp", Hibernate.DOUBLE ) );
+ registerFunction( "%external", new StandardSQLFunction( "%external", Hibernate.STRING ) );
+ registerFunction( "$extract", new VarArgsSQLFunction( Hibernate.INTEGER, "$extract(", ",", ")" ) );
+ registerFunction( "$find", new VarArgsSQLFunction( Hibernate.INTEGER, "$find(", ",", ")" ) );
+ registerFunction( "floor", new StandardSQLFunction( "floor", Hibernate.INTEGER ) );
+ registerFunction( "getdate", new StandardSQLFunction( "getdate", Hibernate.TIMESTAMP ) );
+ registerFunction( "hour", new StandardJDBCEscapeFunction( "hour", Hibernate.INTEGER ) );
+ registerFunction( "ifnull", new VarArgsSQLFunction( "ifnull(", ",", ")" ) );
+ registerFunction( "%internal", new StandardSQLFunction( "%internal" ) );
+ registerFunction( "isnull", new VarArgsSQLFunction( "isnull(", ",", ")" ) );
+ registerFunction( "isnumeric", new StandardSQLFunction( "isnumeric", Hibernate.INTEGER ) );
+ registerFunction( "lcase", new StandardJDBCEscapeFunction( "lcase", Hibernate.STRING ) );
+ registerFunction( "left", new StandardJDBCEscapeFunction( "left", Hibernate.STRING ) );
+ registerFunction( "len", new StandardSQLFunction( "len", Hibernate.INTEGER ) );
+ registerFunction( "length", new StandardSQLFunction( "length", Hibernate.INTEGER ) );
+ registerFunction( "$length", new VarArgsSQLFunction( "$length(", ",", ")" ) );
+ // aggregate functions shouldn't be registered, right?
+ //registerFunction( "list", new StandardSQLFunction("list",Hibernate.STRING) );
+ // stopped on $list
+ registerFunction( "$list", new VarArgsSQLFunction( "$list(", ",", ")" ) );
+ registerFunction( "$listdata", new VarArgsSQLFunction( "$listdata(", ",", ")" ) );
+ registerFunction( "$listfind", new VarArgsSQLFunction( "$listfind(", ",", ")" ) );
+ registerFunction( "$listget", new VarArgsSQLFunction( "$listget(", ",", ")" ) );
+ registerFunction( "$listlength", new StandardSQLFunction( "$listlength", Hibernate.INTEGER ) );
+ registerFunction( "locate", new StandardSQLFunction( "$FIND", Hibernate.INTEGER ) );
+ registerFunction( "log", new StandardJDBCEscapeFunction( "log", Hibernate.DOUBLE ) );
+ registerFunction( "log10", new StandardJDBCEscapeFunction( "log", Hibernate.DOUBLE ) );
+ registerFunction( "lower", new StandardSQLFunction( "lower" ) );
+ registerFunction( "ltrim", new StandardSQLFunction( "ltrim" ) );
+ registerFunction( "minute", new StandardJDBCEscapeFunction( "minute", Hibernate.INTEGER ) );
+ registerFunction( "mod", new StandardJDBCEscapeFunction( "mod", Hibernate.DOUBLE ) );
+ registerFunction( "month", new StandardJDBCEscapeFunction( "month", Hibernate.INTEGER ) );
+ registerFunction( "monthname", new StandardJDBCEscapeFunction( "monthname", Hibernate.STRING ) );
+ registerFunction( "now", new StandardJDBCEscapeFunction( "monthname", Hibernate.TIMESTAMP ) );
+ registerFunction( "nullif", new VarArgsSQLFunction( "nullif(", ",", ")" ) );
+ registerFunction( "nvl", new NvlFunction() );
+ registerFunction( "%odbcin", new StandardSQLFunction( "%odbcin" ) );
+ registerFunction( "%odbcout", new StandardSQLFunction( "%odbcin" ) );
+ registerFunction( "%pattern", new VarArgsSQLFunction( Hibernate.STRING, "", "%pattern", "" ) );
+ registerFunction( "pi", new StandardJDBCEscapeFunction( "pi", Hibernate.DOUBLE ) );
+ registerFunction( "$piece", new VarArgsSQLFunction( Hibernate.STRING, "$piece(", ",", ")" ) );
+ registerFunction( "position", new VarArgsSQLFunction( Hibernate.INTEGER, "position(", " in ", ")" ) );
+ registerFunction( "power", new VarArgsSQLFunction( Hibernate.STRING, "power(", ",", ")" ) );
+ registerFunction( "quarter", new StandardJDBCEscapeFunction( "quarter", Hibernate.INTEGER ) );
+ registerFunction( "repeat", new VarArgsSQLFunction( Hibernate.STRING, "repeat(", ",", ")" ) );
+ registerFunction( "replicate", new VarArgsSQLFunction( Hibernate.STRING, "replicate(", ",", ")" ) );
+ registerFunction( "right", new StandardJDBCEscapeFunction( "right", Hibernate.STRING ) );
+ registerFunction( "round", new VarArgsSQLFunction( Hibernate.FLOAT, "round(", ",", ")" ) );
+ registerFunction( "rtrim", new StandardSQLFunction( "rtrim", Hibernate.STRING ) );
+ registerFunction( "second", new StandardJDBCEscapeFunction( "second", Hibernate.INTEGER ) );
+ registerFunction( "sign", new StandardSQLFunction( "sign", Hibernate.INTEGER ) );
+ registerFunction( "sin", new StandardJDBCEscapeFunction( "sin", Hibernate.DOUBLE ) );
+ registerFunction( "space", new StandardSQLFunction( "space", Hibernate.STRING ) );
+ registerFunction( "%sqlstring", new VarArgsSQLFunction( Hibernate.STRING, "%sqlstring(", ",", ")" ) );
+ registerFunction( "%sqlupper", new VarArgsSQLFunction( Hibernate.STRING, "%sqlupper(", ",", ")" ) );
+ registerFunction( "sqrt", new StandardJDBCEscapeFunction( "SQRT", Hibernate.DOUBLE ) );
+ registerFunction( "%startswith", new VarArgsSQLFunction( Hibernate.STRING, "", "%startswith", "" ) );
+ // below is for Cache' that don't have str in 2007.1 there is str and we register str directly
+ registerFunction( "str", new SQLFunctionTemplate( Hibernate.STRING, "cast(?1 as char varying)" ) );
+ registerFunction( "string", new VarArgsSQLFunction( Hibernate.STRING, "string(", ",", ")" ) );
+ // note that %string is deprecated
+ registerFunction( "%string", new VarArgsSQLFunction( Hibernate.STRING, "%string(", ",", ")" ) );
+ registerFunction( "substr", new VarArgsSQLFunction( Hibernate.STRING, "substr(", ",", ")" ) );
+ registerFunction( "substring", new VarArgsSQLFunction( Hibernate.STRING, "substring(", ",", ")" ) );
+ registerFunction( "sysdate", new NoArgSQLFunction( "sysdate", Hibernate.TIMESTAMP, false ) );
+ registerFunction( "tan", new StandardJDBCEscapeFunction( "tan", Hibernate.DOUBLE ) );
+ registerFunction( "timestampadd", new StandardJDBCEscapeFunction( "timestampadd", Hibernate.DOUBLE ) );
+ registerFunction( "timestampdiff", new StandardJDBCEscapeFunction( "timestampdiff", Hibernate.DOUBLE ) );
+ registerFunction( "tochar", new VarArgsSQLFunction( Hibernate.STRING, "tochar(", ",", ")" ) );
+ registerFunction( "to_char", new VarArgsSQLFunction( Hibernate.STRING, "to_char(", ",", ")" ) );
+ registerFunction( "todate", new VarArgsSQLFunction( Hibernate.STRING, "todate(", ",", ")" ) );
+ registerFunction( "to_date", new VarArgsSQLFunction( Hibernate.STRING, "todate(", ",", ")" ) );
+ registerFunction( "tonumber", new StandardSQLFunction( "tonumber" ) );
+ registerFunction( "to_number", new StandardSQLFunction( "tonumber" ) );
+ // TRIM(end_keyword string-expression-1 FROM string-expression-2)
+ // use Hibernate implementation "From" is one of the parameters they pass in position ?3
+ //registerFunction( "trim", new SQLFunctionTemplate(Hibernate.STRING, "trim(?1 ?2 from ?3)") );
+ registerFunction( "truncate", new StandardJDBCEscapeFunction( "truncate", Hibernate.STRING ) );
+ registerFunction( "ucase", new StandardJDBCEscapeFunction( "ucase", Hibernate.STRING ) );
+ registerFunction( "upper", new StandardSQLFunction( "upper" ) );
+ // %upper is deprecated
+ registerFunction( "%upper", new StandardSQLFunction( "%upper" ) );
+ registerFunction( "user", new StandardJDBCEscapeFunction( "user", Hibernate.STRING ) );
+ registerFunction( "week", new StandardJDBCEscapeFunction( "user", Hibernate.INTEGER ) );
+ registerFunction( "xmlconcat", new VarArgsSQLFunction( Hibernate.STRING, "xmlconcat(", ",", ")" ) );
+ registerFunction( "xmlelement", new VarArgsSQLFunction( Hibernate.STRING, "xmlelement(", ",", ")" ) );
+ // xmlforest requires a new kind of function constructor
+ registerFunction( "year", new StandardJDBCEscapeFunction( "year", Hibernate.INTEGER ) );
+ }
+
+ protected final void register71Functions() {
+ this.registerFunction( "str", new VarArgsSQLFunction( Hibernate.STRING, "str(", ",", ")" ) );
+ }
+
+ // DDL support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public boolean hasAlterTable() {
+ // Does this dialect support the ALTER TABLE syntax?
+ return true;
+ }
+
+ public boolean qualifyIndexName() {
+ // Do we need to qualify index names with the schema name?
+ return false;
+ }
+
+ public boolean supportsUnique() {
+ // Does this dialect support the UNIQUE column syntax?
+ return true;
+ }
+
+ /**
+ * The syntax used to add a foreign key constraint to a table.
+ *
+ * @return String
+ */
+ public String getAddForeignKeyConstraintString(
+ String constraintName,
+ String[] foreignKey,
+ String referencedTable,
+ String[] primaryKey,
+ boolean referencesPrimaryKey) {
+ // The syntax used to add a foreign key constraint to a table.
+ return new StringBuffer( 300 )
+ .append( " ADD CONSTRAINT " )
+ .append( constraintName )
+ .append( " FOREIGN KEY " )
+ .append( constraintName )
+ .append( " (" )
+ .append( StringHelper.join( ", ", foreignKey ) ) // identifier-commalist
+ .append( ") REFERENCES " )
+ .append( referencedTable )
+ .append( " (" )
+ .append( StringHelper.join( ", ", primaryKey ) ) // identifier-commalist
+ .append( ") " )
+ .toString();
+ }
+
+ public boolean supportsCheck() {
+ // Does this dialect support check constraints?
+ return false;
+ }
+
+ public String getAddColumnString() {
+ // The syntax used to add a column to a table
+ return " add column";
+ }
+
+ public String getCascadeConstraintsString() {
+ // Completely optional cascading drop clause.
+ return "";
+ }
+
+ public boolean dropConstraints() {
+ // Do we need to drop constraints before dropping tables in this dialect?
+ return true;
+ }
+
+ public boolean supportsCascadeDelete() {
+ return true;
+ }
+
+ public boolean hasSelfReferentialForeignKeyBug() {
+ return true;
+ }
+
+ // temporary table support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public boolean supportsTemporaryTables() {
+ return true;
+ }
+
+ public String generateTemporaryTableName(String baseTableName) {
+ String name = super.generateTemporaryTableName( baseTableName );
+ return name.length() > 25 ? name.substring( 1, 25 ) : name;
+ }
+
+ public String getCreateTemporaryTableString() {
+ return "create global temporary table";
+ }
+
+ public Boolean performTemporaryTableDDLInIsolation() {
+ return Boolean.FALSE;
+ }
+
+ public String getCreateTemporaryTablePostfix() {
+ return "";
+ }
+
+ public boolean dropTemporaryTableAfterUse() {
+ return true;
+ }
+
+ // IDENTITY support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public boolean supportsIdentityColumns() {
+ return true;
+ }
+
+ public Class getNativeIdentifierGeneratorClass() {
+ return IdentityGenerator.class;
+ }
+
+ public boolean hasDataTypeInIdentityColumn() {
+ // Whether this dialect has an Identity clause added to the data type or a completely seperate identity
+ // data type
+ return true;
+ }
+
+ public String getIdentityColumnString() throws MappingException {
+ // The keyword used to specify an identity column, if identity column key generation is supported.
+ return "identity";
+ }
+
+ public String getIdentitySelectString() {
+ return "SELECT LAST_IDENTITY() FROM %TSQL_sys.snf";
+ }
+
+ // SEQUENCE support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public boolean supportsSequences() {
+ return false;
+ }
+
+// It really does support sequences, but InterSystems elects to suggest usage of IDENTITY instead :/
+// Anyway, below are the actual support overrides for users wanting to use this combo...
+//
+// public String getSequenceNextValString(String sequenceName) {
+// return "select InterSystems.Sequences_GetNext('" + sequenceName + "') from InterSystems.Sequences where ucase(name)=ucase('" + sequenceName + "')";
+// }
+//
+// public String getSelectSequenceNextValString(String sequenceName) {
+// return "(select InterSystems.Sequences_GetNext('" + sequenceName + "') from InterSystems.Sequences where ucase(name)=ucase('" + sequenceName + "'))";
+// }
+//
+// public String getCreateSequenceString(String sequenceName) {
+// return "insert into InterSystems.Sequences(Name) values (ucase('" + sequenceName + "'))";
+// }
+//
+// public String getDropSequenceString(String sequenceName) {
+// return "delete from InterSystems.Sequences where ucase(name)=ucase('" + sequenceName + "')";
+// }
+//
+// public String getQuerySequencesString() {
+// return "select name from InterSystems.Sequences";
+// }
+
+ // lock acquisition support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public boolean supportsForUpdate() {
+ // Does this dialect support the FOR UPDATE syntax?
+ return false;
+ }
+
+ public boolean supportsForUpdateOf() {
+ // Does this dialect support FOR UPDATE OF, allowing particular rows to be locked?
+ return false;
+ }
+
+ public boolean supportsForUpdateNowait() {
+ // Does this dialect support the Oracle-style FOR UPDATE NOWAIT syntax?
+ return false;
+ }
+
+ public boolean supportsOuterJoinForUpdate() {
+ return false;
+ }
+
+ public LockingStrategy getLockingStrategy(Lockable lockable, LockMode lockMode) {
+ // InterSystems Cache' does not current support "SELECT ... FOR UPDATE" syntax...
+ // Set your transaction mode to READ_COMMITTED before using
+ if ( lockMode.greaterThan( LockMode.READ ) ) {
+ return new UpdateLockingStrategy( lockable, lockMode );
+ }
+ else {
+ return new SelectLockingStrategy( lockable, lockMode );
+ }
+ }
+
+ // LIMIT support (ala TOP) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public boolean supportsLimit() {
+ return true;
+ }
+
+ public boolean supportsLimitOffset() {
+ return false;
+ }
+
+ public boolean supportsVariableLimit() {
+ return true;
+ }
+
+ public boolean bindLimitParametersFirst() {
+ // Does the LIMIT clause come at the start of the SELECT statement, rather than at the end?
+ return true;
+ }
+
+ public boolean useMaxForLimit() {
+ // Does the LIMIT clause take a "maximum" row number instead of a total number of returned rows?
+ return true;
+ }
+
+ public String getLimitString(String sql, boolean hasOffset) {
+ if ( hasOffset ) {
+ throw new UnsupportedOperationException( "An offset may not be specified to <TOP n> in Cache SQL" );
+ }
+
+ // This does not support the Cache SQL 'DISTINCT BY (comma-list)' extensions,
+ // but this extension is not supported through Hibernate anyway.
+ int insertionPoint = sql.startsWith( "select distinct" ) ? 15 : 6;
+
+ return new StringBuffer( sql.length() + 8 )
+ .append( sql )
+ .insert( insertionPoint, " TOP ? " )
+ .toString();
+ }
+
+ // callable statement support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public int registerResultSetOutParameter(CallableStatement statement, int col) throws SQLException {
+ return col;
+ }
+
+ public ResultSet getResultSet(CallableStatement ps) throws SQLException {
+ ps.execute();
+ return ( ResultSet ) ps.getObject( 1 );
+ }
+
+ // miscellaneous support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public String getLowercaseFunction() {
+ // The name of the SQL function that transforms a string to lowercase
+ return "lower";
+ }
+
+ public String getNullColumnString() {
+ // The keyword used to specify a nullable column.
+ return " null";
+ }
+
+ public JoinFragment createOuterJoinFragment() {
+ // Create an OuterJoinGenerator for this dialect.
+ return new CacheJoinFragment();
+ }
+
+ public String getNoColumnsInsertString() {
+ // The keyword used to insert a row without specifying
+ // any column values
+ return " default values";
+ }
+
+ public SQLExceptionConverter buildSQLExceptionConverter() {
+ return new CacheSQLStateConverter( EXTRACTER );
+ }
+
+ public static final ViolatedConstraintNameExtracter EXTRACTER = new TemplatedViolatedConstraintNameExtracter() {
+ /**
+ * Extract the name of the violated constraint from the given SQLException.
+ *
+ * @param sqle The exception that was the result of the constraint violation.
+ * @return The extracted constraint name.
+ */
+ public String extractConstraintName(SQLException sqle) {
+ return extractUsingTemplate( "constraint (", ") violated", sqle.getMessage() );
+ }
+ };
+
+
+ // Overridden informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public boolean supportsEmptyInList() {
+ return false;
+ }
+
+ public boolean areStringComparisonsCaseInsensitive() {
+ return true;
+ }
+
+ public boolean supportsResultSetPositionQueryMethodsOnForwardOnlyCursor() {
+ return false;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/DB2390Dialect.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/DB2390Dialect.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/DB2390Dialect.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,45 @@
+//$Id: DB2390Dialect.java 5712 2005-02-13 13:48:39Z oneovthafew $
+package org.hibernate.dialect;
+
+/**
+ * An SQL dialect for DB2/390. This class provides support for
+ * DB2 Universal Database for OS/390, also known as DB2/390.
+ *
+ * @author Kristoffer Dyrkorn
+ */
+public class DB2390Dialect extends DB2Dialect {
+
+ public boolean supportsSequences() {
+ return false;
+ }
+
+ public String getIdentitySelectString() {
+ return "select identity_val_local() from sysibm.sysdummy1";
+ }
+
+ public boolean supportsLimit() {
+ return true;
+ }
+
+ public boolean supportsLimitOffset() {
+ return false;
+ }
+
+ public String getLimitString(String sql, int offset, int limit) {
+ return new StringBuffer(sql.length() + 40)
+ .append(sql)
+ .append(" fetch first ")
+ .append(limit)
+ .append(" rows only ")
+ .toString();
+ }
+
+ public boolean useMaxForLimit() {
+ return true;
+ }
+
+ public boolean supportsVariableLimit() {
+ return false;
+ }
+
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/DB2400Dialect.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/DB2400Dialect.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/DB2400Dialect.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,44 @@
+package org.hibernate.dialect;
+
+/**
+* An SQL dialect for DB2/400
+* @author Peter DeGregorio (pdegregorio)
+* This class provides support for DB2 Universal Database for iSeries,
+* also known as DB2/400.
+*/
+public class DB2400Dialect extends DB2Dialect {
+
+ public boolean supportsSequences() {
+ return false;
+ }
+
+ public String getIdentitySelectString() {
+ return "select identity_val_local() from sysibm.sysdummy1";
+ }
+
+ public boolean supportsLimit() {
+ return true;
+ }
+
+ public boolean supportsLimitOffset() {
+ return false;
+ }
+
+ public String getLimitString(String sql, int offset, int limit) {
+ return new StringBuffer(sql.length() + 40)
+ .append(sql)
+ .append(" fetch first ")
+ .append(limit)
+ .append(" rows only ")
+ .toString();
+ }
+
+ public boolean useMaxForLimit() {
+ return true;
+ }
+
+ public boolean supportsVariableLimit() {
+ return false;
+ }
+
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/DB2Dialect.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/DB2Dialect.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/DB2Dialect.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,369 @@
+//$Id: DB2Dialect.java 10963 2006-12-08 16:06:14Z steve.ebersole at jboss.com $
+package org.hibernate.dialect;
+
+import java.sql.CallableStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+
+import org.hibernate.Hibernate;
+import org.hibernate.cfg.Environment;
+import org.hibernate.dialect.function.NoArgSQLFunction;
+import org.hibernate.dialect.function.SQLFunctionTemplate;
+import org.hibernate.dialect.function.StandardSQLFunction;
+import org.hibernate.dialect.function.VarArgsSQLFunction;
+import org.hibernate.dialect.function.AnsiTrimEmulationFunction;
+
+/**
+ * An SQL dialect for DB2.
+ * @author Gavin King
+ */
+public class DB2Dialect extends Dialect {
+
+ public DB2Dialect() {
+ super();
+ registerColumnType( Types.BIT, "smallint" );
+ registerColumnType( Types.BIGINT, "bigint" );
+ registerColumnType( Types.SMALLINT, "smallint" );
+ registerColumnType( Types.TINYINT, "smallint" );
+ registerColumnType( Types.INTEGER, "integer" );
+ registerColumnType( Types.CHAR, "char(1)" );
+ registerColumnType( Types.VARCHAR, "varchar($l)" );
+ registerColumnType( Types.FLOAT, "float" );
+ registerColumnType( Types.DOUBLE, "double" );
+ registerColumnType( Types.DATE, "date" );
+ registerColumnType( Types.TIME, "time" );
+ registerColumnType( Types.TIMESTAMP, "timestamp" );
+ registerColumnType( Types.VARBINARY, "varchar($l) for bit data" );
+ registerColumnType( Types.NUMERIC, "numeric($p,$s)" );
+ registerColumnType( Types.BLOB, "blob($l)" );
+ registerColumnType( Types.CLOB, "clob($l)" );
+
+ registerFunction("abs", new StandardSQLFunction("abs") );
+ registerFunction("absval", new StandardSQLFunction("absval") );
+ registerFunction("sign", new StandardSQLFunction("sign", Hibernate.INTEGER) );
+
+ registerFunction("ceiling", new StandardSQLFunction("ceiling") );
+ registerFunction("ceil", new StandardSQLFunction("ceil") );
+ registerFunction("floor", new StandardSQLFunction("floor") );
+ registerFunction("round", new StandardSQLFunction("round") );
+
+ registerFunction("acos", new StandardSQLFunction("acos", Hibernate.DOUBLE) );
+ registerFunction("asin", new StandardSQLFunction("asin", Hibernate.DOUBLE) );
+ registerFunction("atan", new StandardSQLFunction("atan", Hibernate.DOUBLE) );
+ registerFunction("cos", new StandardSQLFunction("cos", Hibernate.DOUBLE) );
+ registerFunction("cot", new StandardSQLFunction("cot", Hibernate.DOUBLE) );
+ registerFunction("degrees", new StandardSQLFunction("degrees", Hibernate.DOUBLE) );
+ registerFunction("exp", new StandardSQLFunction("exp", Hibernate.DOUBLE) );
+ registerFunction("float", new StandardSQLFunction("float", Hibernate.DOUBLE) );
+ registerFunction("hex", new StandardSQLFunction("hex", Hibernate.STRING) );
+ registerFunction("ln", new StandardSQLFunction("ln", Hibernate.DOUBLE) );
+ registerFunction("log", new StandardSQLFunction("log", Hibernate.DOUBLE) );
+ registerFunction("log10", new StandardSQLFunction("log10", Hibernate.DOUBLE) );
+ registerFunction("radians", new StandardSQLFunction("radians", Hibernate.DOUBLE) );
+ registerFunction("rand", new NoArgSQLFunction("rand", Hibernate.DOUBLE) );
+ registerFunction("sin", new StandardSQLFunction("sin", Hibernate.DOUBLE) );
+ registerFunction("soundex", new StandardSQLFunction("soundex", Hibernate.STRING) );
+ registerFunction("sqrt", new StandardSQLFunction("sqrt", Hibernate.DOUBLE) );
+ registerFunction("stddev", new StandardSQLFunction("stddev", Hibernate.DOUBLE) );
+ registerFunction("tan", new StandardSQLFunction("tan", Hibernate.DOUBLE) );
+ registerFunction("variance", new StandardSQLFunction("variance", Hibernate.DOUBLE) );
+
+ registerFunction("julian_day", new StandardSQLFunction("julian_day", Hibernate.INTEGER) );
+ registerFunction("microsecond", new StandardSQLFunction("microsecond", Hibernate.INTEGER) );
+ registerFunction("midnight_seconds", new StandardSQLFunction("midnight_seconds", Hibernate.INTEGER) );
+ registerFunction("minute", new StandardSQLFunction("minute", Hibernate.INTEGER) );
+ registerFunction("month", new StandardSQLFunction("month", Hibernate.INTEGER) );
+ registerFunction("monthname", new StandardSQLFunction("monthname", Hibernate.STRING) );
+ registerFunction("quarter", new StandardSQLFunction("quarter", Hibernate.INTEGER) );
+ registerFunction("hour", new StandardSQLFunction("hour", Hibernate.INTEGER) );
+ registerFunction("second", new StandardSQLFunction("second", Hibernate.INTEGER) );
+ registerFunction("current_date", new NoArgSQLFunction("current date", Hibernate.DATE, false) );
+ registerFunction("date", new StandardSQLFunction("date", Hibernate.DATE) );
+ registerFunction("day", new StandardSQLFunction("day", Hibernate.INTEGER) );
+ registerFunction("dayname", new StandardSQLFunction("dayname", Hibernate.STRING) );
+ registerFunction("dayofweek", new StandardSQLFunction("dayofweek", Hibernate.INTEGER) );
+ registerFunction("dayofweek_iso", new StandardSQLFunction("dayofweek_iso", Hibernate.INTEGER) );
+ registerFunction("dayofyear", new StandardSQLFunction("dayofyear", Hibernate.INTEGER) );
+ registerFunction("days", new StandardSQLFunction("days", Hibernate.LONG) );
+ registerFunction("current_time", new NoArgSQLFunction("current time", Hibernate.TIME, false) );
+ registerFunction("time", new StandardSQLFunction("time", Hibernate.TIME) );
+ registerFunction("current_timestamp", new NoArgSQLFunction("current timestamp", Hibernate.TIMESTAMP, false) );
+ registerFunction("timestamp", new StandardSQLFunction("timestamp", Hibernate.TIMESTAMP) );
+ registerFunction("timestamp_iso", new StandardSQLFunction("timestamp_iso", Hibernate.TIMESTAMP) );
+ registerFunction("week", new StandardSQLFunction("week", Hibernate.INTEGER) );
+ registerFunction("week_iso", new StandardSQLFunction("week_iso", Hibernate.INTEGER) );
+ registerFunction("year", new StandardSQLFunction("year", Hibernate.INTEGER) );
+
+ registerFunction("double", new StandardSQLFunction("double", Hibernate.DOUBLE) );
+ registerFunction("varchar", new StandardSQLFunction("varchar", Hibernate.STRING) );
+ registerFunction("real", new StandardSQLFunction("real", Hibernate.FLOAT) );
+ registerFunction("bigint", new StandardSQLFunction("bigint", Hibernate.LONG) );
+ registerFunction("char", new StandardSQLFunction("char", Hibernate.CHARACTER) );
+ registerFunction("integer", new StandardSQLFunction("integer", Hibernate.INTEGER) );
+ registerFunction("smallint", new StandardSQLFunction("smallint", Hibernate.SHORT) );
+
+ registerFunction("digits", new StandardSQLFunction("digits", Hibernate.STRING) );
+ registerFunction("chr", new StandardSQLFunction("chr", Hibernate.CHARACTER) );
+ registerFunction("upper", new StandardSQLFunction("upper") );
+ registerFunction("lower", new StandardSQLFunction("lower") );
+ registerFunction("ucase", new StandardSQLFunction("ucase") );
+ registerFunction("lcase", new StandardSQLFunction("lcase") );
+ registerFunction("length", new StandardSQLFunction("length", Hibernate.LONG) );
+ registerFunction("ltrim", new StandardSQLFunction("ltrim") );
+ registerFunction("rtrim", new StandardSQLFunction("rtrim") );
+ registerFunction( "substr", new StandardSQLFunction( "substr", Hibernate.STRING ) );
+ registerFunction( "posstr", new StandardSQLFunction( "posstr", Hibernate.INTEGER ) );
+
+ registerFunction( "substring", new StandardSQLFunction( "substr", Hibernate.STRING ) );
+ registerFunction( "bit_length", new SQLFunctionTemplate( Hibernate.INTEGER, "length(?1)*8" ) );
+ registerFunction( "trim", new AnsiTrimEmulationFunction() );
+
+ registerFunction( "concat", new VarArgsSQLFunction(Hibernate.STRING, "", "||", "") );
+
+ registerFunction( "str", new SQLFunctionTemplate( Hibernate.STRING, "rtrim(char(?1))" ) );
+
+ registerKeyword("current");
+ registerKeyword("date");
+ registerKeyword("time");
+ registerKeyword("timestamp");
+ registerKeyword("fetch");
+ registerKeyword("first");
+ registerKeyword("rows");
+ registerKeyword("only");
+
+ getDefaultProperties().setProperty(Environment.STATEMENT_BATCH_SIZE, NO_BATCH);
+ }
+
+ public String getLowercaseFunction() {
+ return "lcase";
+ }
+
+ public String getAddColumnString() {
+ return "add column";
+ }
+ public boolean dropConstraints() {
+ return false;
+ }
+ public boolean supportsIdentityColumns() {
+ return true;
+ }
+ public String getIdentitySelectString() {
+ return "values identity_val_local()";
+ }
+ public String getIdentityColumnString() {
+ return "generated by default as identity"; //not null ... (start with 1) is implicit
+ }
+ public String getIdentityInsertString() {
+ return "default";
+ }
+
+ public String getSequenceNextValString(String sequenceName) {
+ return "values nextval for " + sequenceName;
+ }
+ public String getCreateSequenceString(String sequenceName) {
+ return "create sequence " + sequenceName;
+ }
+ public String getDropSequenceString(String sequenceName) {
+ return "drop sequence " + sequenceName + " restrict";
+ }
+
+ public boolean supportsSequences() {
+ return true;
+ }
+
+ public String getQuerySequencesString() {
+ return "select seqname from sysibm.syssequences";
+ }
+
+ public boolean supportsLimit() {
+ return true;
+ }
+
+ /*public String getLimitString(String sql, boolean hasOffset) {
+ StringBuffer rownumber = new StringBuffer(50)
+ .append(" rownumber() over(");
+ int orderByIndex = sql.toLowerCase().indexOf("order by");
+ if (orderByIndex>0) rownumber.append( sql.substring(orderByIndex) );
+ rownumber.append(") as row_,");
+ StringBuffer pagingSelect = new StringBuffer( sql.length()+100 )
+ .append("select * from ( ")
+ .append(sql)
+ .insert( getAfterSelectInsertPoint(sql)+16, rownumber.toString() )
+ .append(" ) as temp_ where row_ ");
+ if (hasOffset) {
+ pagingSelect.append("between ?+1 and ?");
+ }
+ else {
+ pagingSelect.append("<= ?");
+ }
+ return pagingSelect.toString();
+ }*/
+
+ /**
+ * Render the <tt>rownumber() over ( .... ) as rownumber_,</tt>
+ * bit, that goes in the select list
+ */
+ private String getRowNumber(String sql) {
+ StringBuffer rownumber = new StringBuffer(50)
+ .append("rownumber() over(");
+
+ int orderByIndex = sql.toLowerCase().indexOf("order by");
+
+ if ( orderByIndex>0 && !hasDistinct(sql) ) {
+ rownumber.append( sql.substring(orderByIndex) );
+ }
+
+ rownumber.append(") as rownumber_,");
+
+ return rownumber.toString();
+ }
+
+ public String getLimitString(String sql, boolean hasOffset) {
+
+ int startOfSelect = sql.toLowerCase().indexOf("select");
+
+ StringBuffer pagingSelect = new StringBuffer( sql.length()+100 )
+ .append( sql.substring(0, startOfSelect) ) //add the comment
+ .append("select * from ( select ") //nest the main query in an outer select
+ .append( getRowNumber(sql) ); //add the rownnumber bit into the outer query select list
+
+ if ( hasDistinct(sql) ) {
+ pagingSelect.append(" row_.* from ( ") //add another (inner) nested select
+ .append( sql.substring(startOfSelect) ) //add the main query
+ .append(" ) as row_"); //close off the inner nested select
+ }
+ else {
+ pagingSelect.append( sql.substring( startOfSelect + 6 ) ); //add the main query
+ }
+
+ pagingSelect.append(" ) as temp_ where rownumber_ ");
+
+ //add the restriction to the outer select
+ if (hasOffset) {
+ pagingSelect.append("between ?+1 and ?");
+ }
+ else {
+ pagingSelect.append("<= ?");
+ }
+
+ return pagingSelect.toString();
+ }
+
+ private static boolean hasDistinct(String sql) {
+ return sql.toLowerCase().indexOf("select distinct")>=0;
+ }
+
+ public String getForUpdateString() {
+ return " for read only with rs";
+ }
+
+ public boolean useMaxForLimit() {
+ return true;
+ }
+
+ public boolean supportsOuterJoinForUpdate() {
+ return false;
+ }
+
+ public boolean supportsNotNullUnique() {
+ return false;
+ }
+
+ public String getSelectClauseNullString(int sqlType) {
+ String literal;
+ switch(sqlType) {
+ case Types.VARCHAR:
+ case Types.CHAR:
+ literal = "'x'";
+ break;
+ case Types.DATE:
+ literal = "'2000-1-1'";
+ break;
+ case Types.TIMESTAMP:
+ literal = "'2000-1-1 00:00:00'";
+ break;
+ case Types.TIME:
+ literal = "'00:00:00'";
+ break;
+ default:
+ literal = "0";
+ }
+ return "nullif(" + literal + ',' + literal + ')';
+ }
+
+ public static void main(String[] args) {
+ System.out.println( new DB2Dialect().getLimitString("/*foo*/ select * from foos", true) );
+ System.out.println( new DB2Dialect().getLimitString("/*foo*/ select distinct * from foos", true) );
+ System.out.println( new DB2Dialect().getLimitString("/*foo*/ select * from foos foo order by foo.bar, foo.baz", true) );
+ System.out.println( new DB2Dialect().getLimitString("/*foo*/ select distinct * from foos foo order by foo.bar, foo.baz", true) );
+ }
+
+ public boolean supportsUnionAll() {
+ return true;
+ }
+
+ public int registerResultSetOutParameter(CallableStatement statement, int col) throws SQLException {
+ return col;
+ }
+
+ public ResultSet getResultSet(CallableStatement ps) throws SQLException {
+ boolean isResultSet = ps.execute();
+ // This assumes you will want to ignore any update counts
+ while (!isResultSet && ps.getUpdateCount() != -1) {
+ isResultSet = ps.getMoreResults();
+ }
+ ResultSet rs = ps.getResultSet();
+ // You may still have other ResultSets or update counts left to process here
+ // but you can't do it now or the ResultSet you just got will be closed
+ return rs;
+ }
+
+ public boolean supportsCommentOn() {
+ return true;
+ }
+
+ public boolean supportsTemporaryTables() {
+ return true;
+ }
+
+ public String getCreateTemporaryTableString() {
+ return "declare global temporary table";
+ }
+
+ public String getCreateTemporaryTablePostfix() {
+ return "not logged";
+ }
+
+ public String generateTemporaryTableName(String baseTableName) {
+ return "session." + super.generateTemporaryTableName(baseTableName);
+ }
+
+ public boolean supportsCurrentTimestampSelection() {
+ return true;
+ }
+
+ public String getCurrentTimestampSelectString() {
+ return "values current timestamp";
+ }
+
+ public boolean isCurrentTimestampSelectStringCallable() {
+ return false;
+ }
+
+ public boolean supportsParametersInInsertSelect() {
+ // DB2 known to not support parameters within the select
+ // clause of an SQL INSERT ... SELECT ... statement
+ return false;
+ }
+
+ public String getCurrentTimestampSQLFunctionName() {
+ return "sysdate";
+ }
+
+ // Overridden informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public boolean supportsEmptyInList() {
+ return false;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/DataDirectOracle9Dialect.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/DataDirectOracle9Dialect.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/DataDirectOracle9Dialect.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,25 @@
+package org.hibernate.dialect;
+
+import java.sql.CallableStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+public class DataDirectOracle9Dialect extends Oracle9Dialect {
+
+ public int registerResultSetOutParameter(CallableStatement statement, int col) throws SQLException {
+ return col; // sql server just returns automatically
+ }
+
+ public ResultSet getResultSet(CallableStatement ps) throws SQLException {
+ boolean isResultSet = ps.execute();
+// This assumes you will want to ignore any update counts
+ while (!isResultSet && ps.getUpdateCount() != -1) {
+ isResultSet = ps.getMoreResults();
+ }
+ ResultSet rs = ps.getResultSet();
+// You may still have other ResultSets or update counts left to process here
+// but you can't do it now or the ResultSet you just got will be closed
+ return rs;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/DerbyDialect.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/DerbyDialect.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/DerbyDialect.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,198 @@
+//$Id: DerbyDialect.java 10988 2006-12-13 18:37:01Z steve.ebersole at jboss.com $
+package org.hibernate.dialect;
+
+import org.hibernate.Hibernate;
+import org.hibernate.QueryException;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.Mapping;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.type.Type;
+import org.hibernate.dialect.function.VarArgsSQLFunction;
+import org.hibernate.dialect.function.SQLFunction;
+import org.hibernate.dialect.function.SQLFunctionTemplate;
+import org.hibernate.id.TableHiLoGenerator;
+import org.hibernate.sql.CaseFragment;
+import org.hibernate.sql.DerbyCaseFragment;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * @author Simon Johnston
+ *
+ * Hibernate Dialect for Cloudscape 10 - aka Derby. This implements both an
+ * override for the identity column generator as well as for the case statement
+ * issue documented at:
+ * http://www.jroller.com/comments/kenlars99/Weblog/cloudscape_soon_to_be_derby
+ */
+public class DerbyDialect extends DB2Dialect {
+
+ public DerbyDialect() {
+ super();
+ registerFunction( "concat", new VarArgsSQLFunction( Hibernate.STRING, "(","||",")" ) );
+ registerFunction( "trim", new DerbyTrimFunctionEmulation() );
+ }
+
+ /**
+ * This is different in Cloudscape to DB2.
+ */
+ public String getIdentityColumnString() {
+ return "not null generated always as identity"; //$NON-NLS-1
+ }
+
+ /**
+ * Return the case statement modified for Cloudscape.
+ */
+ public CaseFragment createCaseFragment() {
+ return new DerbyCaseFragment();
+ }
+
+ public boolean dropConstraints() {
+ return true;
+ }
+
+ public Class getNativeIdentifierGeneratorClass() {
+ return TableHiLoGenerator.class;
+ }
+
+ public boolean supportsSequences() {
+ return false;
+ }
+
+ public boolean supportsLimit() {
+ return false;
+ }
+
+ public boolean supportsLimitOffset() {
+ return false;
+ }
+
+ public String getQuerySequencesString() {
+ return null ;
+ }
+
+ /**
+ * A specialized function template to emulate the ANSI trim function on Derby DB
+ * since it does not support the full trim specification. However, we cannot even
+ * fully emulate it because there is not standard 'replace' function either. :(
+ */
+ public static class DerbyTrimFunctionEmulation implements SQLFunction {
+ private static final SQLFunction LEADING_SPACE_TRIM = new SQLFunctionTemplate( Hibernate.STRING, "ltrim( ?1 )");
+ private static final SQLFunction TRAILING_SPACE_TRIM = new SQLFunctionTemplate( Hibernate.STRING, "rtrim( ?1 )");
+ private static final SQLFunction BOTH_SPACE_TRIM = new SQLFunctionTemplate( Hibernate.STRING, "ltrim( rtrim( ?1 ) )");
+ private static final SQLFunction BOTH_SPACE_TRIM_FROM = new SQLFunctionTemplate( Hibernate.STRING, "ltrim( rtrim( ?2 ) )");
+
+ public Type getReturnType(Type columnType, Mapping mapping) throws QueryException {
+ return Hibernate.STRING;
+ }
+
+ public boolean hasArguments() {
+ return true;
+ }
+
+ public boolean hasParenthesesIfNoArguments() {
+ return false;
+ }
+
+ public String render(List args, SessionFactoryImplementor factory) throws QueryException {
+ // according to both the ANSI-SQL and EJB3 specs, trim can either take
+ // exactly one parameter or a variable number of parameters between 1 and 4.
+ // from the SQL spec:
+ //
+ // <trim function> ::=
+ // TRIM <left paren> <trim operands> <right paren>
+ //
+ // <trim operands> ::=
+ // [ [ <trim specification> ] [ <trim character> ] FROM ] <trim source>
+ //
+ // <trim specification> ::=
+ // LEADING
+ // | TRAILING
+ // | BOTH
+ //
+ // If only <trim specification> is omitted, BOTH is assumed;
+ // if <trim character> is omitted, space is assumed
+ if ( args.size() == 1 ) {
+ // we have the form: trim(trimSource)
+ // so we trim leading and trailing spaces
+ return BOTH_SPACE_TRIM.render( args, factory );
+ }
+ else if ( "from".equalsIgnoreCase( ( String ) args.get( 0 ) ) ) {
+ // we have the form: trim(from trimSource).
+ // This is functionally equivalent to trim(trimSource)
+ return BOTH_SPACE_TRIM_FROM.render( args, factory );
+ }
+ else {
+ // otherwise, a trim-specification and/or a trim-character
+ // have been specified; we need to decide which options
+ // are present and "do the right thing"
+ boolean leading = true; // should leading trim-characters be trimmed?
+ boolean trailing = true; // should trailing trim-characters be trimmed?
+ String trimCharacter; // the trim-character
+ String trimSource; // the trim-source
+
+ // potentialTrimCharacterArgIndex = 1 assumes that a
+ // trim-specification has been specified. we handle the
+ // exception to that explicitly
+ int potentialTrimCharacterArgIndex = 1;
+ String firstArg = ( String ) args.get( 0 );
+ if ( "leading".equalsIgnoreCase( firstArg ) ) {
+ trailing = false;
+ }
+ else if ( "trailing".equalsIgnoreCase( firstArg ) ) {
+ leading = false;
+ }
+ else if ( "both".equalsIgnoreCase( firstArg ) ) {
+ }
+ else {
+ potentialTrimCharacterArgIndex = 0;
+ }
+
+ String potentialTrimCharacter = ( String ) args.get( potentialTrimCharacterArgIndex );
+ if ( "from".equalsIgnoreCase( potentialTrimCharacter ) ) {
+ trimCharacter = "' '";
+ trimSource = ( String ) args.get( potentialTrimCharacterArgIndex + 1 );
+ }
+ else if ( potentialTrimCharacterArgIndex + 1 >= args.size() ) {
+ trimCharacter = "' '";
+ trimSource = potentialTrimCharacter;
+ }
+ else {
+ trimCharacter = potentialTrimCharacter;
+ if ( "from".equalsIgnoreCase( ( String ) args.get( potentialTrimCharacterArgIndex + 1 ) ) ) {
+ trimSource = ( String ) args.get( potentialTrimCharacterArgIndex + 2 );
+ }
+ else {
+ trimSource = ( String ) args.get( potentialTrimCharacterArgIndex + 1 );
+ }
+ }
+
+ List argsToUse = new ArrayList();
+ argsToUse.add( trimSource );
+ argsToUse.add( trimCharacter );
+
+ if ( trimCharacter.equals( "' '" ) ) {
+ if ( leading && trailing ) {
+ return BOTH_SPACE_TRIM.render( argsToUse, factory );
+ }
+ else if ( leading ) {
+ return LEADING_SPACE_TRIM.render( argsToUse, factory );
+ }
+ else {
+ return TRAILING_SPACE_TRIM.render( argsToUse, factory );
+ }
+ }
+ else {
+ throw new HibernateException( "cannot specify trim character when using Derby as Derby does not support the ANSI trim function, not does it support a replace function to properly emmulate it" );
+ }
+ }
+ }
+ }
+
+
+ // Overridden informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public boolean supportsLobValueChangePropogation() {
+ return false;
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/DerbyDialect.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/Dialect.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/Dialect.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/Dialect.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1763 @@
+//$Id: Dialect.java 11303 2007-03-19 22:06:14Z steve.ebersole at jboss.com $
+package org.hibernate.dialect;
+
+import java.sql.CallableStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.LockMode;
+import org.hibernate.MappingException;
+import org.hibernate.QueryException;
+import org.hibernate.cfg.Environment;
+import org.hibernate.dialect.function.CastFunction;
+import org.hibernate.dialect.function.SQLFunction;
+import org.hibernate.dialect.function.SQLFunctionTemplate;
+import org.hibernate.dialect.function.StandardSQLFunction;
+import org.hibernate.dialect.lock.LockingStrategy;
+import org.hibernate.dialect.lock.SelectLockingStrategy;
+import org.hibernate.engine.Mapping;
+import org.hibernate.exception.SQLExceptionConverter;
+import org.hibernate.exception.SQLStateConverter;
+import org.hibernate.exception.ViolatedConstraintNameExtracter;
+import org.hibernate.id.IdentityGenerator;
+import org.hibernate.id.SequenceGenerator;
+import org.hibernate.id.TableHiLoGenerator;
+import org.hibernate.mapping.Column;
+import org.hibernate.persister.entity.Lockable;
+import org.hibernate.sql.ANSICaseFragment;
+import org.hibernate.sql.ANSIJoinFragment;
+import org.hibernate.sql.CaseFragment;
+import org.hibernate.sql.JoinFragment;
+import org.hibernate.sql.ForUpdateFragment;
+import org.hibernate.type.Type;
+import org.hibernate.util.ReflectHelper;
+import org.hibernate.util.StringHelper;
+
+/**
+ * Represents a dialect of SQL implemented by a particular RDBMS.
+ * Subclasses implement Hibernate compatibility with different systems.<br>
+ * <br>
+ * Subclasses should provide a public default constructor that <tt>register()</tt>
+ * a set of type mappings and default Hibernate properties.<br>
+ * <br>
+ * Subclasses should be immutable.
+ *
+ * @author Gavin King, David Channon
+ */
+public abstract class Dialect {
+
+ private static final Log log = LogFactory.getLog( Dialect.class );
+
+ public static final String DEFAULT_BATCH_SIZE = "15";
+ public static final String NO_BATCH = "0";
+
+ /**
+ * Characters used for quoting SQL identifiers
+ */
+ public static final String QUOTE = "`\"[";
+ public static final String CLOSED_QUOTE = "`\"]";
+
+
+ // build the map of standard ANSI SQL aggregation functions ~~~~~~~~~~~~~~~
+
+ private static final Map STANDARD_AGGREGATE_FUNCTIONS = new HashMap();
+ static {
+ STANDARD_AGGREGATE_FUNCTIONS.put( "count", new StandardSQLFunction("count") {
+ public Type getReturnType(Type columnType, Mapping mapping) {
+ return Hibernate.LONG;
+ }
+ } );
+
+ STANDARD_AGGREGATE_FUNCTIONS.put( "avg", new StandardSQLFunction("avg") {
+ public Type getReturnType(Type columnType, Mapping mapping) throws QueryException {
+ int[] sqlTypes;
+ try {
+ sqlTypes = columnType.sqlTypes( mapping );
+ }
+ catch ( MappingException me ) {
+ throw new QueryException( me );
+ }
+ if ( sqlTypes.length != 1 ) throw new QueryException( "multi-column type in avg()" );
+ return Hibernate.DOUBLE;
+ }
+ } );
+
+ STANDARD_AGGREGATE_FUNCTIONS.put( "max", new StandardSQLFunction("max") );
+ STANDARD_AGGREGATE_FUNCTIONS.put( "min", new StandardSQLFunction("min") );
+ STANDARD_AGGREGATE_FUNCTIONS.put( "sum", new StandardSQLFunction("sum") {
+ public Type getReturnType(Type columnType, Mapping mapping) {
+ //pre H3.2 behavior: super.getReturnType(ct, m);
+ int[] sqlTypes;
+ try {
+ sqlTypes = columnType.sqlTypes( mapping );
+ }
+ catch ( MappingException me ) {
+ throw new QueryException( me );
+ }
+ if ( sqlTypes.length != 1 ) throw new QueryException( "multi-column type in sum()" );
+ int sqlType = sqlTypes[0];
+
+ // First allow the actual type to control the return value. (the actual underlying sqltype could actually be different)
+ if ( columnType == Hibernate.BIG_INTEGER ) {
+ return Hibernate.BIG_INTEGER;
+ }
+ else if ( columnType == Hibernate.BIG_DECIMAL ) {
+ return Hibernate.BIG_DECIMAL;
+ }
+ else if ( columnType == Hibernate.LONG || columnType == Hibernate.SHORT || columnType == Hibernate.INTEGER) {
+ return Hibernate.LONG;
+ }
+ else if ( columnType == Hibernate.FLOAT || columnType == Hibernate.DOUBLE) {
+ return Hibernate.DOUBLE;
+ }
+
+ // finally use the sqltype if == on Hibernate types did not find a match.
+ if ( sqlType == Types.NUMERIC ) {
+ return columnType; //because numeric can be anything
+ }
+ else if ( sqlType == Types.FLOAT || sqlType == Types.DOUBLE || sqlType == Types.DECIMAL || sqlType == Types.REAL) {
+ return Hibernate.DOUBLE;
+ }
+ else if ( sqlType == Types.BIGINT || sqlType == Types.INTEGER || sqlType == Types.SMALLINT || sqlType == Types.TINYINT ) {
+ return Hibernate.LONG;
+ }
+ else {
+ return columnType;
+ }
+ }
+ });
+ }
+
+ private final TypeNames typeNames = new TypeNames();
+ private final TypeNames hibernateTypeNames = new TypeNames();
+
+ private final Properties properties = new Properties();
+ private final Map sqlFunctions = new HashMap();
+ private final Set sqlKeywords = new HashSet();
+
+
+ // constructors and factory methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ protected Dialect() {
+ log.info( "Using dialect: " + this );
+ sqlFunctions.putAll( STANDARD_AGGREGATE_FUNCTIONS );
+
+ // standard sql92 functions (can be overridden by subclasses)
+ registerFunction( "substring", new SQLFunctionTemplate( Hibernate.STRING, "substring(?1, ?2, ?3)" ) );
+ registerFunction( "locate", new SQLFunctionTemplate( Hibernate.INTEGER, "locate(?1, ?2, ?3)" ) );
+ registerFunction( "trim", new SQLFunctionTemplate( Hibernate.STRING, "trim(?1 ?2 ?3 ?4)" ) );
+ registerFunction( "length", new StandardSQLFunction( "length", Hibernate.INTEGER ) );
+ registerFunction( "bit_length", new StandardSQLFunction( "bit_length", Hibernate.INTEGER ) );
+ registerFunction( "coalesce", new StandardSQLFunction( "coalesce" ) );
+ registerFunction( "nullif", new StandardSQLFunction( "nullif" ) );
+ registerFunction( "abs", new StandardSQLFunction( "abs" ) );
+ registerFunction( "mod", new StandardSQLFunction( "mod", Hibernate.INTEGER) );
+ registerFunction( "sqrt", new StandardSQLFunction( "sqrt", Hibernate.DOUBLE) );
+ registerFunction( "upper", new StandardSQLFunction("upper") );
+ registerFunction( "lower", new StandardSQLFunction("lower") );
+ registerFunction( "cast", new CastFunction() );
+ registerFunction( "extract", new SQLFunctionTemplate(Hibernate.INTEGER, "extract(?1 ?2 ?3)") );
+
+ //map second/minute/hour/day/month/year to ANSI extract(), override on subclasses
+ registerFunction( "second", new SQLFunctionTemplate(Hibernate.INTEGER, "extract(second from ?1)") );
+ registerFunction( "minute", new SQLFunctionTemplate(Hibernate.INTEGER, "extract(minute from ?1)") );
+ registerFunction( "hour", new SQLFunctionTemplate(Hibernate.INTEGER, "extract(hour from ?1)") );
+ registerFunction( "day", new SQLFunctionTemplate(Hibernate.INTEGER, "extract(day from ?1)") );
+ registerFunction( "month", new SQLFunctionTemplate(Hibernate.INTEGER, "extract(month from ?1)") );
+ registerFunction( "year", new SQLFunctionTemplate(Hibernate.INTEGER, "extract(year from ?1)") );
+
+ registerFunction( "str", new SQLFunctionTemplate(Hibernate.STRING, "cast(?1 as char)") );
+
+ // register hibernate types for default use in scalar sqlquery type auto detection
+ registerHibernateType( Types.BIGINT, Hibernate.BIG_INTEGER.getName() );
+ registerHibernateType( Types.BINARY, Hibernate.BINARY.getName() );
+ registerHibernateType( Types.BIT, Hibernate.BOOLEAN.getName() );
+ registerHibernateType( Types.CHAR, Hibernate.CHARACTER.getName() );
+ registerHibernateType( Types.DATE, Hibernate.DATE.getName() );
+ registerHibernateType( Types.DOUBLE, Hibernate.DOUBLE.getName() );
+ registerHibernateType( Types.FLOAT, Hibernate.FLOAT.getName() );
+ registerHibernateType( Types.INTEGER, Hibernate.INTEGER.getName() );
+ registerHibernateType( Types.SMALLINT, Hibernate.SHORT.getName() );
+ registerHibernateType( Types.TINYINT, Hibernate.BYTE.getName() );
+ registerHibernateType( Types.TIME, Hibernate.TIME.getName() );
+ registerHibernateType( Types.TIMESTAMP, Hibernate.TIMESTAMP.getName() );
+ registerHibernateType( Types.VARCHAR, Hibernate.STRING.getName() );
+ registerHibernateType( Types.VARBINARY, Hibernate.BINARY.getName() );
+ registerHibernateType( Types.NUMERIC, Hibernate.BIG_DECIMAL.getName() );
+ registerHibernateType( Types.DECIMAL, Hibernate.BIG_DECIMAL.getName() );
+ registerHibernateType( Types.BLOB, Hibernate.BLOB.getName() );
+ registerHibernateType( Types.CLOB, Hibernate.CLOB.getName() );
+ }
+
+ /**
+ * Get an instance of the dialect specified by the current <tt>System</tt> properties.
+ *
+ * @return The specified Dialect
+ * @throws HibernateException If no dialect was specified, or if it could not be instantiated.
+ */
+ public static Dialect getDialect() throws HibernateException {
+ String dialectName = Environment.getProperties().getProperty( Environment.DIALECT );
+ return instantiateDialect( dialectName );
+ }
+
+
+ /**
+ * Get an instance of the dialect specified by the given properties or by
+ * the current <tt>System</tt> properties.
+ *
+ * @param props The properties to use for finding the dialect class to use.
+ * @return The specified Dialect
+ * @throws HibernateException If no dialect was specified, or if it could not be instantiated.
+ */
+ public static Dialect getDialect(Properties props) throws HibernateException {
+ String dialectName = props.getProperty( Environment.DIALECT );
+ if ( dialectName == null ) {
+ return getDialect();
+ }
+ return instantiateDialect( dialectName );
+ }
+
+ private static Dialect instantiateDialect(String dialectName) throws HibernateException {
+ if ( dialectName == null ) {
+ throw new HibernateException( "The dialect was not set. Set the property hibernate.dialect." );
+ }
+ try {
+ return ( Dialect ) ReflectHelper.classForName( dialectName ).newInstance();
+ }
+ catch ( ClassNotFoundException cnfe ) {
+ throw new HibernateException( "Dialect class not found: " + dialectName );
+ }
+ catch ( Exception e ) {
+ throw new HibernateException( "Could not instantiate dialect class", e );
+ }
+ }
+
+ /**
+ * Retrieve a set of default Hibernate properties for this database.
+ *
+ * @return a set of Hibernate properties
+ */
+ public final Properties getDefaultProperties() {
+ return properties;
+ }
+
+ public String toString() {
+ return getClass().getName();
+ }
+
+
+ // database type mapping support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Get the name of the database type associated with the given
+ * {@link java.sql.Types} typecode.
+ *
+ * @param code The {@link java.sql.Types} typecode
+ * @return the database type name
+ * @throws HibernateException If no mapping was specified for that type.
+ */
+ public String getTypeName(int code) throws HibernateException {
+ String result = typeNames.get( code );
+ if ( result == null ) {
+ throw new HibernateException( "No default type mapping for (java.sql.Types) " + code );
+ }
+ return result;
+ }
+
+ /**
+ * Get the name of the database type associated with the given
+ * {@link java.sql.Types} typecode with the given storage specification
+ * parameters.
+ *
+ * @param code The {@link java.sql.Types} typecode
+ * @param length The datatype length
+ * @param precision The datatype precision
+ * @param scale The datatype scale
+ * @return the database type name
+ * @throws HibernateException If no mapping was specified for that type.
+ */
+ public String getTypeName(int code, int length, int precision, int scale) throws HibernateException {
+ String result = typeNames.get( code, length, precision, scale );
+ if ( result == null ) {
+ throw new HibernateException(
+ "No type mapping for java.sql.Types code: " +
+ code +
+ ", length: " +
+ length
+ );
+ }
+ return result;
+ }
+
+ /**
+ * Get the name of the database type appropriate for casting operations
+ * (via the CAST() SQL function) for the given {@link java.sql.Types} typecode.
+ *
+ * @param code The {@link java.sql.Types} typecode
+ * @return The database type name
+ */
+ public String getCastTypeName(int code) {
+ return getTypeName( code, Column.DEFAULT_LENGTH, Column.DEFAULT_PRECISION, Column.DEFAULT_SCALE );
+ }
+
+ /**
+ * Subclasses register a type name for the given type code and maximum
+ * column length. <tt>$l</tt> in the type name with be replaced by the
+ * column length (if appropriate).
+ *
+ * @param code The {@link java.sql.Types} typecode
+ * @param capacity The maximum length of database type
+ * @param name The database type name
+ */
+ protected void registerColumnType(int code, int capacity, String name) {
+ typeNames.put( code, capacity, name );
+ }
+
+ /**
+ * Subclasses register a type name for the given type code. <tt>$l</tt> in
+ * the type name with be replaced by the column length (if appropriate).
+ *
+ * @param code The {@link java.sql.Types} typecode
+ * @param name The database type name
+ */
+ protected void registerColumnType(int code, String name) {
+ typeNames.put( code, name );
+ }
+
+
+ // hibernate type mapping support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Get the name of the Hibernate {@link org.hibernate.type.Type} associated with th given
+ * {@link java.sql.Types} typecode.
+ *
+ * @param code The {@link java.sql.Types} typecode
+ * @return The Hibernate {@link org.hibernate.type.Type} name.
+ * @throws HibernateException If no mapping was specified for that type.
+ */
+ public String getHibernateTypeName(int code) throws HibernateException {
+ String result = hibernateTypeNames.get( code );
+ if ( result == null ) {
+ throw new HibernateException( "No Hibernate type mapping for java.sql.Types code: " + code );
+ }
+ return result;
+ }
+
+ /**
+ * Get the name of the Hibernate {@link org.hibernate.type.Type} associated
+ * with the given {@link java.sql.Types} typecode with the given storage
+ * specification parameters.
+ *
+ * @param code The {@link java.sql.Types} typecode
+ * @param length The datatype length
+ * @param precision The datatype precision
+ * @param scale The datatype scale
+ * @return The Hibernate {@link org.hibernate.type.Type} name.
+ * @throws HibernateException If no mapping was specified for that type.
+ */
+ public String getHibernateTypeName(int code, int length, int precision, int scale) throws HibernateException {
+ String result = hibernateTypeNames.get( code, length, precision, scale );
+ if ( result == null ) {
+ throw new HibernateException(
+ "No Hibernate type mapping for java.sql.Types code: " +
+ code +
+ ", length: " +
+ length
+ );
+ }
+ return result;
+ }
+
+ /**
+ * Registers a Hibernate {@link org.hibernate.type.Type} name for the given
+ * {@link java.sql.Types} type code and maximum column length.
+ *
+ * @param code The {@link java.sql.Types} typecode
+ * @param capacity The maximum length of database type
+ * @param name The Hibernate {@link org.hibernate.type.Type} name
+ */
+ protected void registerHibernateType(int code, int capacity, String name) {
+ hibernateTypeNames.put( code, capacity, name);
+ }
+
+ /**
+ * Registers a Hibernate {@link org.hibernate.type.Type} name for the given
+ * {@link java.sql.Types} type code.
+ *
+ * @param code The {@link java.sql.Types} typecode
+ * @param name The Hibernate {@link org.hibernate.type.Type} name
+ */
+ protected void registerHibernateType(int code, String name) {
+ hibernateTypeNames.put( code, name);
+ }
+
+
+ // function support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ protected void registerFunction(String name, SQLFunction function) {
+ sqlFunctions.put( name, function );
+ }
+
+ /**
+ * Retrieves a map of the dialect's registered fucntions
+ * (functionName => {@link org.hibernate.dialect.function.SQLFunction}).
+ *
+ * @return The map of registered functions.
+ */
+ public final Map getFunctions() {
+ return sqlFunctions;
+ }
+
+
+ // keyword support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ protected void registerKeyword(String word) {
+ sqlKeywords.add(word);
+ }
+
+ public Set getKeywords() {
+ return sqlKeywords;
+ }
+
+
+ // native identifier generatiion ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * The class (which implements {@link org.hibernate.id.IdentifierGenerator})
+ * which acts as this dialects native generation strategy.
+ * <p/>
+ * Comes into play whenever the user specifies the native generator.
+ *
+ * @return The native generator class.
+ */
+ public Class getNativeIdentifierGeneratorClass() {
+ if ( supportsIdentityColumns() ) {
+ return IdentityGenerator.class;
+ }
+ else if ( supportsSequences() ) {
+ return SequenceGenerator.class;
+ }
+ else {
+ return TableHiLoGenerator.class;
+ }
+ }
+
+
+ // IDENTITY support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Does this dialect support identity column key generation?
+ *
+ * @return True if IDENTITY columns are supported; false otherwise.
+ */
+ public boolean supportsIdentityColumns() {
+ return false;
+ }
+
+ /**
+ * Does the dialect support some form of inserting and selecting
+ * the generated IDENTITY value all in the same statement.
+ *
+ * @return True if the dialect supports selecting the just
+ * generated IDENTITY in the insert statement.
+ */
+ public boolean supportsInsertSelectIdentity() {
+ return false;
+ }
+
+ /**
+ * Whether this dialect have an Identity clause added to the data type or a
+ * completely seperate identity data type
+ *
+ * @return boolean
+ */
+ public boolean hasDataTypeInIdentityColumn() {
+ return true;
+ }
+
+ /**
+ * Provided we {@link #supportsInsertSelectIdentity}, then attch the
+ * "select identity" clause to the insert statement.
+ * <p/>
+ * Note, if {@link #supportsInsertSelectIdentity} == false then
+ * the insert-string should be returned without modification.
+ *
+ * @param insertString The insert command
+ * @return The insert command with any necessary identity select
+ * clause attached.
+ */
+ public String appendIdentitySelectToInsert(String insertString) {
+ return insertString;
+ }
+
+ /**
+ * Get the select command to use to retrieve the last generated IDENTITY
+ * value for a particuar table
+ *
+ * @param table The table into which the insert was done
+ * @param column The PK column.
+ * @param type The {@link java.sql.Types} type code.
+ * @return The appropriate select command
+ * @throws MappingException If IDENTITY generation is not supported.
+ */
+ public String getIdentitySelectString(String table, String column, int type) throws MappingException {
+ return getIdentitySelectString();
+ }
+
+ /**
+ * Get the select command to use to retrieve the last generated IDENTITY
+ * value.
+ *
+ * @return The appropriate select command
+ * @throws MappingException If IDENTITY generation is not supported.
+ */
+ protected String getIdentitySelectString() throws MappingException {
+ throw new MappingException( "Dialect does not support identity key generation" );
+ }
+
+ /**
+ * The syntax used during DDL to define a column as being an IDENTITY of
+ * a particular type.
+ *
+ * @param type The {@link java.sql.Types} type code.
+ * @return The appropriate DDL fragment.
+ * @throws MappingException If IDENTITY generation is not supported.
+ */
+ public String getIdentityColumnString(int type) throws MappingException {
+ return getIdentityColumnString();
+ }
+
+ /**
+ * The syntax used during DDL to define a column as being an IDENTITY.
+ *
+ * @return The appropriate DDL fragment.
+ * @throws MappingException If IDENTITY generation is not supported.
+ */
+ protected String getIdentityColumnString() throws MappingException {
+ throw new MappingException( "Dialect does not support identity key generation" );
+ }
+
+ /**
+ * The keyword used to insert a generated value into an identity column (or null).
+ * Need if the dialect does not support inserts that specify no column values.
+ *
+ * @return The appropriate keyword.
+ */
+ public String getIdentityInsertString() {
+ return null;
+ }
+
+
+ // SEQUENCE support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Does this dialect support sequences?
+ *
+ * @return True if sequences supported; false otherwise.
+ */
+ public boolean supportsSequences() {
+ return false;
+ }
+
+ /**
+ * 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/>
+ * This should be a "stand alone" select statement.
+ *
+ * @param sequenceName the name of the sequence
+ * @return String The "nextval" select string.
+ * @throws MappingException If sequences are not supported.
+ */
+ public String getSequenceNextValString(String sequenceName) throws MappingException {
+ throw new MappingException( "Dialect does not support sequences" );
+ }
+
+ /**
+ * Generate the select expression fragment that will retreive the next
+ * value of a sequence as part of another (typically DML) statement.
+ * <p/>
+ * This differs from {@link #getSequenceNextValString(String)} in that this
+ * should return an expression usable within another statement.
+ *
+ * @param sequenceName the name of the sequence
+ * @return The "nextval" fragment.
+ * @throws MappingException If sequences are not supported.
+ */
+ public String getSelectSequenceNextValString(String sequenceName) throws MappingException {
+ throw new MappingException( "Dialect does not support sequences" );
+ }
+
+ /**
+ * The multiline script used to create a sequence.
+ *
+ * @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.
+ * <p/>
+ * Dialects which support sequences and can create a sequence in a
+ * single command need *only* override this method. Dialects
+ * which support sequences but require multiple commands to create
+ * a sequence should instead override {@link #getCreateSequenceStrings}.
+ *
+ * @param sequenceName The name of the sequence
+ * @return The sequence creation command
+ * @throws MappingException If sequences are not supported.
+ */
+ protected String getCreateSequenceString(String sequenceName) throws MappingException {
+ throw new MappingException( "Dialect does not support sequences" );
+ }
+
+ /**
+ * 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
+ * @return The sequence drop commands
+ * @throws MappingException If sequences are not supported.
+ */
+ public String[] getDropSequenceStrings(String sequenceName) throws MappingException {
+ return new String[]{getDropSequenceString( sequenceName )};
+ }
+
+ /**
+ * Typically dialects which support sequences can drop a sequence
+ * with a single command. This is convenience form of
+ * {@link #getDropSequenceStrings} to help facilitate that.
+ * <p/>
+ * Dialects which support sequences and can drop a sequence in a
+ * single command need *only* override this method. Dialects
+ * which support sequences but require multiple commands to drop
+ * a sequence should instead override {@link #getDropSequenceStrings}.
+ *
+ * @param sequenceName The name of the sequence
+ * @return The sequence drop commands
+ * @throws MappingException If sequences are not supported.
+ */
+ protected String getDropSequenceString(String sequenceName) throws MappingException {
+ throw new MappingException( "Dialect does not support sequences" );
+ }
+
+ /**
+ * Get the select command used retrieve the names of all sequences.
+ *
+ * @return The select command; or null if sequences are not supported.
+ * @see org.hibernate.tool.hbm2ddl.SchemaUpdate
+ */
+ public String getQuerySequencesString() {
+ return null;
+ }
+
+
+ // GUID support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Get the command used to select a GUID from the underlying database.
+ * <p/>
+ * Optional operation.
+ *
+ * @return The appropriate command.
+ */
+ public String getSelectGUIDString() {
+ throw new UnsupportedOperationException( "dialect does not support GUIDs" );
+ }
+
+
+ // limit/offset support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Does this dialect support some form of limiting query results
+ * via a SQL clause?
+ *
+ * @return True if this dialect supports some form of LIMIT.
+ */
+ public boolean supportsLimit() {
+ return false;
+ }
+
+ /**
+ * Does this dialect's LIMIT support (if any) additionally
+ * support specifying an offset?
+ *
+ * @return True if the dialect supports an offset within the limit support.
+ */
+ public boolean supportsLimitOffset() {
+ return supportsLimit();
+ }
+
+ /**
+ * Does this dialect support bind variables (i.e., prepared statememnt
+ * parameters) for its limit/offset?
+ *
+ * @return True if bind variables can be used; false otherwise.
+ */
+ public boolean supportsVariableLimit() {
+ return supportsLimit();
+ }
+
+ /**
+ * ANSI SQL defines the LIMIT clause to be in the form LIMIT offset, limit.
+ * Does this dialect require us to bind the parameters in reverse order?
+ *
+ * @return true if the correct order is limit, offset
+ */
+ public boolean bindLimitParametersInReverseOrder() {
+ return false;
+ }
+
+ /**
+ * Does the <tt>LIMIT</tt> clause come at the start of the
+ * <tt>SELECT</tt> statement, rather than at the end?
+ *
+ * @return true if limit parameters should come before other parameters
+ */
+ public boolean bindLimitParametersFirst() {
+ return false;
+ }
+
+ /**
+ * Does the <tt>LIMIT</tt> clause take a "maximum" row number instead
+ * of a total number of returned rows?
+ * <p/>
+ * This is easiest understood via an example. Consider you have a table
+ * with 20 rows, but you only want to retrieve rows number 11 through 20.
+ * Generally, a limit with offset would say that the offset = 11 and the
+ * limit = 10 (we only want 10 rows at a time); this is specifying the
+ * total number of returned rows. Some dialects require that we instead
+ * specify offset = 11 and limit = 20, where 20 is the "last" row we want
+ * relative to offset (i.e. total number of rows = 20 - 11 = 9)
+ * <p/>
+ * So essentially, is limit relative from offset? Or is limit absolute?
+ *
+ * @return True if limit is relative from offset; false otherwise.
+ */
+ public boolean useMaxForLimit() {
+ return false;
+ }
+
+ /**
+ * Given a limit and an offset, apply the limit clause to the query.
+ *
+ * @param query The query to which to apply the limit.
+ * @param offset The offset of the limit
+ * @param limit The limit of the limit ;)
+ * @return The modified query statement with the limit applied.
+ */
+ public String getLimitString(String query, int offset, int limit) {
+ return getLimitString( query, offset > 0 );
+ }
+
+ /**
+ * Apply s limit clause to the query.
+ * <p/>
+ * Typically dialects utilize {@link #supportsVariableLimit() variable}
+ * limit caluses when they support limits. Thus, when building the
+ * select command we do not actually need to know the limit or the offest
+ * since we will just be using placeholders.
+ * <p/>
+ * Here we do still pass along whether or not an offset was specified
+ * so that dialects not supporting offsets can generate proper exceptions.
+ * In general, dialects will override one or the other of this method and
+ * {@link #getLimitString(String, int, int)}.
+ *
+ * @param query The query to which to apply the limit.
+ * @param hasOffset Is the query requesting an offset?
+ * @return the modified SQL
+ */
+ protected String getLimitString(String query, boolean hasOffset) {
+ throw new UnsupportedOperationException( "paged queries not supported" );
+ }
+
+
+ // lock acquisition support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Get a strategy instance which knows how to acquire a database-level lock
+ * of the specified mode for this dialect.
+ *
+ * @param lockable The persister for the entity to be locked.
+ * @param lockMode The type of lock to be acquired.
+ * @return The appropriate locking strategy.
+ * @since 3.2
+ */
+ public LockingStrategy getLockingStrategy(Lockable lockable, LockMode lockMode) {
+ return new SelectLockingStrategy( lockable, lockMode );
+ }
+
+ /**
+ * Given a lock mode, determine the appropriate for update fragment to use.
+ *
+ * @param lockMode The lock mode to apply.
+ * @return The appropriate for update fragment.
+ */
+ public String getForUpdateString(LockMode lockMode) {
+ if ( lockMode==LockMode.UPGRADE ) {
+ return getForUpdateString();
+ }
+ else if ( lockMode==LockMode.UPGRADE_NOWAIT ) {
+ return getForUpdateNowaitString();
+ }
+ else if ( lockMode==LockMode.FORCE ) {
+ return getForUpdateNowaitString();
+ }
+ else {
+ return "";
+ }
+ }
+
+ /**
+ * Get the string to append to SELECT statements to acquire locks
+ * for this dialect.
+ *
+ * @return The appropriate <tt>FOR UPDATE</tt> clause string.
+ */
+ public String getForUpdateString() {
+ return " for update";
+ }
+
+ /**
+ * Is <tt>FOR UPDATE OF</tt> syntax supported?
+ *
+ * @return True if the database supports <tt>FOR UPDATE OF</tt> syntax;
+ * false otherwise.
+ */
+ public boolean forUpdateOfColumns() {
+ // by default we report no support
+ return false;
+ }
+
+ /**
+ * Does this dialect support <tt>FOR UPDATE</tt> in conjunction with
+ * outer joined rows?
+ *
+ * @return True if outer joined rows can be locked via <tt>FOR UPDATE</tt>.
+ */
+ public boolean supportsOuterJoinForUpdate() {
+ return true;
+ }
+
+ /**
+ * Get the <tt>FOR UPDATE OF column_list</tt> fragment appropriate for this
+ * dialect given the aliases of the columns to be write locked.
+ *
+ * @param aliases The columns to be write locked.
+ * @return The appropriate <tt>FOR UPDATE OF column_list</tt> clause string.
+ */
+ public String getForUpdateString(String aliases) {
+ // by default we simply return the getForUpdateString() result since
+ // the default is to say no support for "FOR UPDATE OF ..."
+ return getForUpdateString();
+ }
+
+ /**
+ * Retrieves the <tt>FOR UPDATE NOWAIT</tt> syntax specific to this dialect.
+ *
+ * @return The appropriate <tt>FOR UPDATE NOWAIT</tt> clause string.
+ */
+ public String getForUpdateNowaitString() {
+ // by default we report no support for NOWAIT lock semantics
+ return getForUpdateString();
+ }
+
+ /**
+ * Get the <tt>FOR UPDATE OF column_list NOWAIT</tt> fragment appropriate
+ * for this dialect given the aliases of the columns to be write locked.
+ *
+ * @param aliases The columns to be write locked.
+ * @return The appropriate <tt>FOR UPDATE colunm_list NOWAIT</tt> clause string.
+ */
+ public String getForUpdateNowaitString(String aliases) {
+ return getForUpdateString( aliases );
+ }
+
+ /**
+ * Some dialects support an alternative means to <tt>SELECT FOR UPDATE</tt>,
+ * whereby a "lock hint" is appends to the table name in the from clause.
+ * <p/>
+ * contributed by <a href="http://sourceforge.net/users/heschulz">Helge Schulz</a>
+ *
+ * @param mode The lock mode to apply
+ * @param tableName The name of the table to which to apply the lock hint.
+ * @return The table with any required lock hints.
+ */
+ public String appendLockHint(LockMode mode, String tableName) {
+ return tableName;
+ }
+
+ /**
+ * Modifies the given SQL by applying the appropriate updates for the specified
+ * lock modes and key columns.
+ * <p/>
+ * The behavior here is that of an ANSI SQL <tt>SELECT FOR UPDATE</tt>. This
+ * method is really intended to allow dialects which do not support
+ * <tt>SELECT FOR UPDATE</tt> to achieve this in their own fashion.
+ *
+ * @param sql the SQL string to modify
+ * @param aliasedLockModes a map of lock modes indexed by aliased table names.
+ * @param keyColumnNames a map of key columns indexed by aliased table names.
+ * @return the modified SQL string.
+ */
+ public String applyLocksToSql(String sql, Map aliasedLockModes, Map keyColumnNames) {
+ return sql + new ForUpdateFragment( this, aliasedLockModes, keyColumnNames ).toFragmentString();
+ }
+
+
+ // table support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Command used to create a table.
+ *
+ * @return The command used to create a table.
+ */
+ public String getCreateTableString() {
+ return "create table";
+ }
+
+ /**
+ * Slight variation on {@link #getCreateTableString}. Here, we have the
+ * command used to create a table when there is no primary key and
+ * duplicate rows are expected.
+ * <p/>
+ * Most databases do not care about the distinction; originally added for
+ * Teradata support which does care.
+ *
+ * @return The command used to create a multiset table.
+ */
+ public String getCreateMultisetTableString() {
+ return getCreateTableString();
+ }
+
+
+ // temporary table support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Does this dialect support temporary tables?
+ *
+ * @return True if temp tables are supported; false otherwise.
+ */
+ public boolean supportsTemporaryTables() {
+ return false;
+ }
+
+ /**
+ * Generate a temporary table name given the bas table.
+ *
+ * @param baseTableName The table name from which to base the temp table name.
+ * @return The generated temp table name.
+ */
+ public String generateTemporaryTableName(String baseTableName) {
+ return "HT_" + baseTableName;
+ }
+
+ /**
+ * Command used to create a temporary table.
+ *
+ * @return The command used to create a temporary table.
+ */
+ public String getCreateTemporaryTableString() {
+ return "create table";
+ }
+
+ /**
+ * Get any fragments needing to be postfixed to the command for
+ * temporary table creation.
+ *
+ * @return Any required postfix.
+ */
+ public String getCreateTemporaryTablePostfix() {
+ return "";
+ }
+
+ /**
+ * Does the dialect require that temporary table DDL statements occur in
+ * isolation from other statements? This would be the case if the creation
+ * would cause any current transaction to get committed implicitly.
+ * <p/>
+ * JDBC defines a standard way to query for this information via the
+ * {@link java.sql.DatabaseMetaData#dataDefinitionCausesTransactionCommit()}
+ * method. However, that does not distinguish between temporary table
+ * DDL and other forms of DDL; MySQL, for example, reports DDL causing a
+ * transaction commit via its driver, even though that is not the case for
+ * temporary table DDL.
+ * <p/>
+ * Possible return values and their meanings:<ul>
+ * <li>{@link Boolean#TRUE} - Unequivocally, perform the temporary table DDL
+ * in isolation.</li>
+ * <li>{@link Boolean#FALSE} - Unequivocally, do <b>not</b> perform the
+ * temporary table DDL in isolation.</li>
+ * <li><i>null</i> - defer to the JDBC driver response in regards to
+ * {@link java.sql.DatabaseMetaData#dataDefinitionCausesTransactionCommit()}</li>
+ * </ul>
+ *
+ * @return see the result matrix above.
+ */
+ public Boolean performTemporaryTableDDLInIsolation() {
+ return null;
+ }
+
+ /**
+ * Do we need to drop the temporary table after use?
+ *
+ * @return True if the table should be dropped.
+ */
+ public boolean dropTemporaryTableAfterUse() {
+ return true;
+ }
+
+
+ // callable statement support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Registers an OUT parameter which will be returing a
+ * {@link java.sql.ResultSet}. How this is accomplished varies greatly
+ * from DB to DB, hence its inclusion (along with {@link #getResultSet}) here.
+ *
+ * @param statement The callable statement.
+ * @param position The bind position at which to register the OUT param.
+ * @return The number of (contiguous) bind positions used.
+ * @throws SQLException Indicates problems registering the OUT param.
+ */
+ public int registerResultSetOutParameter(CallableStatement statement, int position) throws SQLException {
+ throw new UnsupportedOperationException(
+ getClass().getName() +
+ " does not support resultsets via stored procedures"
+ );
+ }
+
+ /**
+ * Given a callable statement previously processed by {@link #registerResultSetOutParameter},
+ * extract the {@link java.sql.ResultSet} from the OUT parameter.
+ *
+ * @param statement The callable statement.
+ * @return The extracted result set.
+ * @throws SQLException Indicates problems extracting the result set.
+ */
+ public ResultSet getResultSet(CallableStatement statement) throws SQLException {
+ throw new UnsupportedOperationException(
+ getClass().getName() +
+ " does not support resultsets via stored procedures"
+ );
+ }
+
+ // current timestamp support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Does this dialect support a way to retrieve the database's current
+ * timestamp value?
+ *
+ * @return True if the current timestamp can be retrieved; false otherwise.
+ */
+ public boolean supportsCurrentTimestampSelection() {
+ return false;
+ }
+
+ /**
+ * Should the value returned by {@link #getCurrentTimestampSelectString}
+ * be treated as callable. Typically this indicates that JDBC escape
+ * sytnax is being used...
+ *
+ * @return True if the {@link #getCurrentTimestampSelectString} return
+ * is callable; false otherwise.
+ */
+ public boolean isCurrentTimestampSelectStringCallable() {
+ throw new UnsupportedOperationException( "Database not known to define a current timestamp function" );
+ }
+
+ /**
+ * Retrieve the command used to retrieve the current timestammp from the
+ * database.
+ *
+ * @return The command.
+ */
+ public String getCurrentTimestampSelectString() {
+ throw new UnsupportedOperationException( "Database not known to define a current timestamp function" );
+ }
+
+ /**
+ * The name of the database-specific SQL function for retrieving the
+ * current timestamp.
+ *
+ * @return The function name.
+ */
+ public String getCurrentTimestampSQLFunctionName() {
+ // the standard SQL function name is current_timestamp...
+ return "current_timestamp";
+ }
+
+
+ // SQLException support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Build an instance of the SQLExceptionConverter preferred by this dialect for
+ * converting SQLExceptions into Hibernate's JDBCException hierarchy. The default
+ * Dialect implementation simply returns a converter based on X/Open SQLState codes.
+ * <p/>
+ * It is strongly recommended that specific Dialect implementations override this
+ * method, since interpretation of a SQL error is much more accurate when based on
+ * the ErrorCode rather than the SQLState. Unfortunately, the ErrorCode is a vendor-
+ * specific approach.
+ *
+ * @return The Dialect's preferred SQLExceptionConverter.
+ */
+ public SQLExceptionConverter buildSQLExceptionConverter() {
+ // The default SQLExceptionConverter for all dialects is based on SQLState
+ // since SQLErrorCode is extremely vendor-specific. Specific Dialects
+ // may override to return whatever is most appropriate for that vendor.
+ return new SQLStateConverter( getViolatedConstraintNameExtracter() );
+ }
+
+ private static final ViolatedConstraintNameExtracter EXTRACTER = new ViolatedConstraintNameExtracter() {
+ public String extractConstraintName(SQLException sqle) {
+ return null;
+ }
+ };
+
+ public ViolatedConstraintNameExtracter getViolatedConstraintNameExtracter() {
+ return EXTRACTER;
+ }
+
+
+ // union subclass support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Given a {@link java.sql.Types} type code, determine an appropriate
+ * null value to use in a select clause.
+ * <p/>
+ * One thing to consider here is that certain databases might
+ * require proper casting for the nulls here since the select here
+ * will be part of a UNION/UNION ALL.
+ *
+ * @param sqlType The {@link java.sql.Types} type code.
+ * @return The appropriate select clause value fragment.
+ */
+ public String getSelectClauseNullString(int sqlType) {
+ return "null";
+ }
+
+ /**
+ * Does this dialect support UNION ALL, which is generally a faster
+ * variant of UNION?
+ *
+ * @return True if UNION ALL is supported; false otherwise.
+ */
+ public boolean supportsUnionAll() {
+ return false;
+ }
+
+
+ // miscellaneous support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+ /**
+ * Create a {@link org.hibernate.sql.JoinFragment} strategy responsible
+ * for handling this dialect's variations in how joins are handled.
+ *
+ * @return This dialect's {@link org.hibernate.sql.JoinFragment} strategy.
+ */
+ public JoinFragment createOuterJoinFragment() {
+ return new ANSIJoinFragment();
+ }
+
+ /**
+ * Create a {@link org.hibernate.sql.CaseFragment} strategy responsible
+ * for handling this dialect's variations in how CASE statements are
+ * handled.
+ *
+ * @return This dialect's {@link org.hibernate.sql.CaseFragment} strategy.
+ */
+ public CaseFragment createCaseFragment() {
+ return new ANSICaseFragment();
+ }
+
+ /**
+ * The fragment used to insert a row without specifying any column values.
+ * This is not possible on some databases.
+ *
+ * @return The appropriate empty values clause.
+ */
+ public String getNoColumnsInsertString() {
+ return "values ( )";
+ }
+
+ /**
+ * The name of the SQL function that transforms a string to
+ * lowercase
+ *
+ * @return The dialect-specific lowercase function.
+ */
+ public String getLowercaseFunction() {
+ return "lower";
+ }
+
+ /**
+ * Meant as a means for end users to affect the select strings being sent
+ * to the database and perhaps manipulate them in some fashion.
+ * <p/>
+ * The recommend approach is to instead use
+ * {@link org.hibernate.Interceptor#onPrepareStatement(String)}.
+ *
+ * @param select The select command
+ * @return The mutated select command, or the same as was passed in.
+ */
+ public String transformSelectString(String select) {
+ return select;
+ }
+
+ /**
+ * What is the maximum length Hibernate can use for generated aliases?
+ *
+ * @return The maximum length.
+ */
+ public int getMaxAliasLength() {
+ return 10;
+ }
+
+ /**
+ * The SQL literal value to which this database maps boolean values.
+ *
+ * @param bool The boolean value
+ * @return The appropriate SQL literal.
+ */
+ public String toBooleanValueString(boolean bool) {
+ return bool ? "1" : "0";
+ }
+
+
+ // identifier quoting support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * The character specific to this dialect used to begin a quoted identifier.
+ *
+ * @return The dialect's specific open quote character.
+ */
+ public char openQuote() {
+ return '"';
+ }
+
+ /**
+ * The character specific to this dialect used to close a quoted identifier.
+ *
+ * @return The dialect's specific close quote character.
+ */
+ public char closeQuote() {
+ return '"';
+ }
+
+ /**
+ * Apply dialect-specific quoting.
+ * <p/>
+ * By default, the incoming value is checked to see if its first character
+ * is the back-tick (`). If so, the dialect specific quoting is applied.
+ *
+ * @param column The value to be quoted.
+ * @return The quoted (or unmodified, if not starting with back-tick) value.
+ * @see #openQuote()
+ * @see #closeQuote()
+ */
+ public final String quote(String column) {
+ if ( column.charAt( 0 ) == '`' ) {
+ return openQuote() + column.substring( 1, column.length() - 1 ) + closeQuote();
+ }
+ else {
+ return column;
+ }
+ }
+
+
+ // DDL support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Does this dialect support the <tt>ALTER TABLE</tt> syntax?
+ *
+ * @return True if we support altering of tables; false otherwise.
+ */
+ public boolean hasAlterTable() {
+ return true;
+ }
+
+ /**
+ * Do we need to drop constraints before dropping tables in this dialect?
+ *
+ * @return True if constraints must be dropped prior to dropping
+ * the table; false otherwise.
+ */
+ public boolean dropConstraints() {
+ return true;
+ }
+
+ /**
+ * Do we need to qualify index names with the schema name?
+ *
+ * @return boolean
+ */
+ public boolean qualifyIndexName() {
+ return true;
+ }
+
+ /**
+ * Does this dialect support the <tt>UNIQUE</tt> column syntax?
+ *
+ * @return boolean
+ */
+ public boolean supportsUnique() {
+ return true;
+ }
+
+ /**
+ * Does this dialect support adding Unique constraints via create and alter table ?
+ * @return boolean
+ */
+ public boolean supportsUniqueConstraintInCreateAlterTable() {
+ return true;
+ }
+
+ /**
+ * The syntax used to add a column to a table (optional).
+ *
+ * @return The "add column" fragment.
+ */
+ public String getAddColumnString() {
+ throw new UnsupportedOperationException( "No add column syntax supported by Dialect" );
+ }
+
+ public String getDropForeignKeyString() {
+ return " drop constraint ";
+ }
+
+ public String getTableTypeString() {
+ // grrr... for differentiation of mysql storage engines
+ return "";
+ }
+
+ /**
+ * The syntax used to add a foreign key constraint to a table.
+ *
+ * @param constraintName The FK constraint name.
+ * @param foreignKey The names of the columns comprising the FK
+ * @param referencedTable The table referenced by the FK
+ * @param primaryKey The explicit columns in the referencedTable referenced
+ * by this FK.
+ * @param referencesPrimaryKey if false, constraint should be
+ * explicit about which column names the constraint refers to
+ *
+ * @return the "add FK" fragment
+ */
+ public String getAddForeignKeyConstraintString(
+ String constraintName,
+ String[] foreignKey,
+ String referencedTable,
+ String[] primaryKey,
+ boolean referencesPrimaryKey) {
+ StringBuffer res = new StringBuffer( 30 );
+
+ res.append( " add constraint " )
+ .append( constraintName )
+ .append( " foreign key (" )
+ .append( StringHelper.join( ", ", foreignKey ) )
+ .append( ") references " )
+ .append( referencedTable );
+
+ if ( !referencesPrimaryKey ) {
+ res.append( " (" )
+ .append( StringHelper.join( ", ", primaryKey ) )
+ .append( ')' );
+ }
+
+ return res.toString();
+ }
+
+ /**
+ * The syntax used to add a primary key constraint to a table.
+ *
+ * @param constraintName The name of the PK constraint.
+ * @return The "add PK" fragment
+ */
+ public String getAddPrimaryKeyConstraintString(String constraintName) {
+ return " add constraint " + constraintName + " primary key ";
+ }
+
+ public boolean hasSelfReferentialForeignKeyBug() {
+ return false;
+ }
+
+ /**
+ * The keyword used to specify a nullable column.
+ *
+ * @return String
+ */
+ public String getNullColumnString() {
+ return "";
+ }
+
+ public boolean supportsCommentOn() {
+ return false;
+ }
+
+ public String getTableComment(String comment) {
+ return "";
+ }
+
+ public String getColumnComment(String comment) {
+ return "";
+ }
+
+ public boolean supportsIfExistsBeforeTableName() {
+ return false;
+ }
+
+ public boolean supportsIfExistsAfterTableName() {
+ return false;
+ }
+
+ /**
+ * Does this dialect support column-level check constraints?
+ *
+ * @return True if column-level CHECK constraints are supported; false
+ * otherwise.
+ */
+ public boolean supportsColumnCheck() {
+ return true;
+ }
+
+ /**
+ * Does this dialect support table-level check constraints?
+ *
+ * @return True if table-level CHECK constraints are supported; false
+ * otherwise.
+ */
+ public boolean supportsTableCheck() {
+ return true;
+ }
+
+ public boolean supportsCascadeDelete() {
+ return true;
+ }
+
+ public boolean supportsNotNullUnique() {
+ return true;
+ }
+
+ /**
+ * Completely optional cascading drop clause
+ *
+ * @return String
+ */
+ public String getCascadeConstraintsString() {
+ return "";
+ }
+
+
+ // Informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Does this dialect support empty IN lists?
+ * <p/>
+ * For example, is [where XYZ in ()] a supported construct?
+ *
+ * @return True if empty in lists are supported; false otherwise.
+ * @since 3.2
+ */
+ public boolean supportsEmptyInList() {
+ return true;
+ }
+
+ /**
+ * Are string comparisons implicitly case insensitive.
+ * <p/>
+ * In other words, does [where 'XYZ' = 'xyz'] resolve to true?
+ *
+ * @return True if comparisons are case insensitive.
+ * @since 3.2
+ */
+ public boolean areStringComparisonsCaseInsensitive() {
+ return false;
+ }
+
+ /**
+ * Is this dialect known to support what ANSI-SQL terms "row value
+ * constructor" syntax; sometimes called tuple syntax.
+ * <p/>
+ * Basically, does it support syntax like
+ * "... where (FIRST_NAME, LAST_NAME) = ('Steve', 'Ebersole') ...".
+ *
+ * @return True if this SQL dialect is known to support "row value
+ * constructor" syntax; false otherwise.
+ * @since 3.2
+ */
+ public boolean supportsRowValueConstructorSyntax() {
+ // return false here, as most databases do not properly support this construct...
+ return false;
+ }
+
+ /**
+ * If the dialect supports {@link #supportsRowValueConstructorSyntax() row values},
+ * does it offer such support in IN lists as well?
+ * <p/>
+ * For example, "... where (FIRST_NAME, LAST_NAME) IN ( (?, ?), (?, ?) ) ..."
+ *
+ * @return True if this SQL dialect is known to support "row value
+ * constructor" syntax in the IN list; false otherwise.
+ * @since 3.2
+ */
+ public boolean supportsRowValueConstructorSyntaxInInList() {
+ return false;
+ }
+
+ /**
+ * Should LOBs (both BLOB and CLOB) be bound using stream operations (i.e.
+ * {@link java.sql.PreparedStatement#setBinaryStream}).
+ *
+ * @return True if BLOBs and CLOBs should be bound using stream operations.
+ * @since 3.2
+ */
+ public boolean useInputStreamToInsertBlob() {
+ return true;
+ }
+
+ /**
+ * Does this dialect support parameters within the select clause of
+ * INSERT ... SELECT ... statements?
+ *
+ * @return True if this is supported; false otherwise.
+ * @since 3.2
+ */
+ public boolean supportsParametersInInsertSelect() {
+ return true;
+ }
+
+ /**
+ * Does this dialect support asking the result set its positioning
+ * information on forward only cursors. Specifically, in the case of
+ * scrolling fetches, Hibernate needs to use
+ * {@link java.sql.ResultSet#isAfterLast} and
+ * {@link java.sql.ResultSet#isBeforeFirst}. Certain drivers do not
+ * allow access to these methods for forward only cursors.
+ * <p/>
+ * NOTE : this is highly driver dependent!
+ *
+ * @return True if methods like {@link java.sql.ResultSet#isAfterLast} and
+ * {@link java.sql.ResultSet#isBeforeFirst} are supported for forward
+ * only cursors; false otherwise.
+ * @since 3.2
+ */
+ public boolean supportsResultSetPositionQueryMethodsOnForwardOnlyCursor() {
+ return true;
+ }
+
+ /**
+ * Does this dialect support definition of cascade delete constraints
+ * which can cause circular chains?
+ *
+ * @return True if circular cascade delete constraints are supported; false
+ * otherwise.
+ * @since 3.2
+ */
+ public boolean supportsCircularCascadeDeleteConstraints() {
+ return true;
+ }
+
+ /**
+ * Are subselects supported as the left-hand-side (LHS) of
+ * IN-predicates.
+ * <p/>
+ * In other words, is syntax like "... <subquery> IN (1, 2, 3) ..." supported?
+ *
+ * @return True if subselects can appear as the LHS of an in-predicate;
+ * false otherwise.
+ * @since 3.2
+ */
+ public boolean supportsSubselectAsInPredicateLHS() {
+ return true;
+ }
+
+ /**
+ * Expected LOB usage pattern is such that I can perform an insert
+ * via prepared statement with a parameter binding for a LOB value
+ * without crazy casting to JDBC driver implementation-specific classes...
+ * <p/>
+ * Part of the trickiness here is the fact that this is largely
+ * driver dependent. For example, Oracle (which is notoriously bad with
+ * LOB support in their drivers historically) actually does a pretty good
+ * job with LOB support as of the 10.2.x versions of their drivers...
+ *
+ * @return True if normal LOB usage patterns can be used with this driver;
+ * false if driver-specific hookiness needs to be applied.
+ * @since 3.2
+ */
+ public boolean supportsExpectedLobUsagePattern() {
+ return true;
+ }
+
+ /**
+ * Does the dialect support propogating changes to LOB
+ * values back to the database? Talking about mutating the
+ * internal value of the locator as opposed to supplying a new
+ * locator instance...
+ * <p/>
+ * For BLOBs, the internal value might be changed by:
+ * {@link java.sql.Blob#setBinaryStream},
+ * {@link java.sql.Blob#setBytes(long, byte[])},
+ * {@link java.sql.Blob#setBytes(long, byte[], int, int)},
+ * or {@link java.sql.Blob#truncate(long)}.
+ * <p/>
+ * For CLOBs, the internal value might be changed by:
+ * {@link java.sql.Clob#setAsciiStream(long)},
+ * {@link java.sql.Clob#setCharacterStream(long)},
+ * {@link java.sql.Clob#setString(long, String)},
+ * {@link java.sql.Clob#setString(long, String, int, int)},
+ * or {@link java.sql.Clob#truncate(long)}.
+ * <p/>
+ * NOTE : I do not know the correct answer currently for
+ * databases which (1) are not part of the cruise control process
+ * or (2) do not {@link #supportsExpectedLobUsagePattern}.
+ *
+ * @return True if the changes are propogated back to the
+ * database; false otherwise.
+ * @since 3.2
+ */
+ public boolean supportsLobValueChangePropogation() {
+ return true;
+ }
+
+ /**
+ * Is it supported to materialize a LOB locator outside the transaction in
+ * which it was created?
+ * <p/>
+ * Again, part of the trickiness here is the fact that this is largely
+ * driver dependent.
+ * <p/>
+ * NOTE: all database I have tested which {@link #supportsExpectedLobUsagePattern()}
+ * also support the ability to materialize a LOB outside the owning transaction...
+ *
+ * @return True if unbounded materialization is supported; false otherwise.
+ * @since 3.2
+ */
+ public boolean supportsUnboundedLobLocatorMaterialization() {
+ return true;
+ }
+
+ /**
+ * Does this dialect support referencing the table being mutated in
+ * a subquery. The "table being mutated" is the table referenced in
+ * an UPDATE or a DELETE query. And so can that table then be
+ * referenced in a subquery of said UPDATE/DELETE query.
+ * <p/>
+ * For example, would the following two syntaxes be supported:<ul>
+ * <li>delete from TABLE_A where ID not in ( select ID from TABLE_A )</li>
+ * <li>update TABLE_A set NON_ID = 'something' where ID in ( select ID from TABLE_A)</li>
+ * </ul>
+ *
+ * @return True if this dialect allows references the mutating table from
+ * a subquery.
+ */
+ public boolean supportsSubqueryOnMutatingTable() {
+ return true;
+ }
+
+ /**
+ * Does the dialect support an exists statement in the select clause?
+ *
+ * @return True if exists checks are allowed in the select clause; false otherwise.
+ */
+ public boolean supportsExistsInSelect() {
+ return true;
+ }
+
+ /**
+ * For the underlying database, is READ_COMMITTED isolation implemented by
+ * forcing readers to wait for write locks to be released?
+ *
+ * @return True if writers block readers to achieve READ_COMMITTED; false otherwise.
+ */
+ public boolean doesReadCommittedCauseWritersToBlockReaders() {
+ return false;
+ }
+
+ /**
+ * For the underlying database, is REPEATABLE_READ isolation implemented by
+ * forcing writers to wait for read locks to be released?
+ *
+ * @return True if readers block writers to achieve REPEATABLE_READ; false otherwise.
+ */
+ public boolean doesRepeatableReadCauseReadersToBlockWriters() {
+ return false;
+ }
+
+ /**
+ * Does this dialect support using a JDBC bind parameter as an argument
+ * to a function or procedure call?
+ *
+ * @return True if the database supports accepting bind params as args; false otherwise.
+ */
+ public boolean supportsBindAsCallableArgument() {
+ return true;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/DialectFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/DialectFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/DialectFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,136 @@
+// $Id: DialectFactory.java 9789 2006-04-25 17:06:55Z epbernard $
+package org.hibernate.dialect;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import org.hibernate.HibernateException;
+import org.hibernate.cfg.Environment;
+import org.hibernate.util.ReflectHelper;
+
+/**
+ * A factory for generating Dialect instances.
+ *
+ * @author Steve Ebersole
+ */
+public class DialectFactory {
+
+ /**
+ * Builds an appropriate Dialect instance.
+ * <p/>
+ * If a dialect is explicitly named in the incoming properties, it is used. Otherwise, the database name and version
+ * (obtained from connection metadata) are used to make the dertemination.
+ * <p/>
+ * An exception is thrown if a dialect was not explicitly set and the database name is not known.
+ *
+ * @param props The configuration properties.
+ * @param databaseName The name of the database product (obtained from metadata).
+ * @param databaseMajorVersion The major version of the database product (obtained from metadata).
+ *
+ * @return The appropriate dialect.
+ *
+ * @throws HibernateException No dialect specified and database name not known.
+ */
+ public static Dialect buildDialect(Properties props, String databaseName, int databaseMajorVersion)
+ throws HibernateException {
+ String dialectName = props.getProperty( Environment.DIALECT );
+ if ( dialectName == null ) {
+ return determineDialect( databaseName, databaseMajorVersion );
+ }
+ else {
+ return buildDialect( dialectName );
+ }
+ }
+
+ /**
+ * Determine the appropriate Dialect to use given the database product name
+ * and major version.
+ *
+ * @param databaseName The name of the database product (obtained from metadata).
+ * @param databaseMajorVersion The major version of the database product (obtained from metadata).
+ *
+ * @return An appropriate dialect instance.
+ */
+ public static Dialect determineDialect(String databaseName, int databaseMajorVersion) {
+ if ( databaseName == null ) {
+ throw new HibernateException( "Hibernate Dialect must be explicitly set" );
+ }
+
+ DatabaseDialectMapper mapper = ( DatabaseDialectMapper ) MAPPERS.get( databaseName );
+ if ( mapper == null ) {
+ throw new HibernateException( "Hibernate Dialect must be explicitly set for database: " + databaseName );
+ }
+
+ String dialectName = mapper.getDialectClass( databaseMajorVersion );
+ return buildDialect( dialectName );
+ }
+
+ /**
+ * Returns a dialect instance given the name of the class to use.
+ *
+ * @param dialectName The name of the dialect class.
+ *
+ * @return The dialect instance.
+ */
+ public static Dialect buildDialect(String dialectName) {
+ try {
+ return ( Dialect ) ReflectHelper.classForName( dialectName ).newInstance();
+ }
+ catch ( ClassNotFoundException cnfe ) {
+ throw new HibernateException( "Dialect class not found: " + dialectName );
+ }
+ catch ( Exception e ) {
+ throw new HibernateException( "Could not instantiate dialect class", e );
+ }
+ }
+
+ /**
+ * For a given database product name, instances of
+ * DatabaseDialectMapper know which Dialect to use for different versions.
+ */
+ public static interface DatabaseDialectMapper {
+ public String getDialectClass(int majorVersion);
+ }
+
+ /**
+ * A simple DatabaseDialectMapper for dialects which are independent
+ * of the underlying database product version.
+ */
+ public static class VersionInsensitiveMapper implements DatabaseDialectMapper {
+ private String dialectClassName;
+
+ public VersionInsensitiveMapper(String dialectClassName) {
+ this.dialectClassName = dialectClassName;
+ }
+
+ public String getDialectClass(int majorVersion) {
+ return dialectClassName;
+ }
+ }
+
+ private static final Map MAPPERS = new HashMap();
+ static {
+ // TODO : this is the stuff it'd be nice to move to a properties file or some other easily user-editable place
+ MAPPERS.put( "HSQL Database Engine", new VersionInsensitiveMapper( "org.hibernate.dialect.HSQLDialect" ) );
+ MAPPERS.put( "DB2/NT", new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" ) );
+ MAPPERS.put( "MySQL", new VersionInsensitiveMapper( "org.hibernate.dialect.MySQLDialect" ) );
+ MAPPERS.put( "PostgreSQL", new VersionInsensitiveMapper( "org.hibernate.dialect.PostgreSQLDialect" ) );
+ MAPPERS.put( "Microsoft SQL Server Database", new VersionInsensitiveMapper( "org.hibernate.dialect.SQLServerDialect" ) );
+ MAPPERS.put( "Microsoft SQL Server", new VersionInsensitiveMapper( "org.hibernate.dialect.SQLServerDialect" ) );
+ MAPPERS.put( "Sybase SQL Server", new VersionInsensitiveMapper( "org.hibernate.dialect.SybaseDialect" ) );
+ MAPPERS.put( "Informix Dynamic Server", new VersionInsensitiveMapper( "org.hibernate.dialect.InformixDialect" ) );
+ MAPPERS.put( "Apache Derby", new VersionInsensitiveMapper( "org.hibernate.dialect.DerbyDialect" ) );
+
+ MAPPERS.put(
+ "Oracle",
+ new DatabaseDialectMapper() {
+ public String getDialectClass(int majorVersion) {
+ return majorVersion > 8
+ ? "org.hibernate.dialect.Oracle9Dialect"
+ : "org.hibernate.dialect.OracleDialect";
+ }
+ }
+ );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/FirebirdDialect.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/FirebirdDialect.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/FirebirdDialect.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,29 @@
+//$Id: FirebirdDialect.java 4202 2004-08-09 06:38:52Z oneovthafew $
+package org.hibernate.dialect;
+
+/**
+ * An SQL dialect for Firebird.
+ * @author Reha CENANI
+ */
+public class FirebirdDialect extends InterbaseDialect {
+
+ public String getDropSequenceString(String sequenceName) {
+ return "drop generator " + sequenceName;
+ }
+
+ public String getLimitString(String sql, boolean hasOffset) {
+ return new StringBuffer( sql.length()+20 )
+ .append(sql)
+ .insert(6, hasOffset ? " first ? skip ?" : " first ?")
+ .toString();
+ }
+
+ public boolean bindLimitParametersFirst() {
+ return true;
+ }
+
+ public boolean bindLimitParametersInReverseOrder() {
+ return true;
+ }
+
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/FrontBaseDialect.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/FrontBaseDialect.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/FrontBaseDialect.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,91 @@
+//$Id: FrontBaseDialect.java 9328 2006-02-23 17:32:47Z steveebersole $
+package org.hibernate.dialect;
+
+import org.hibernate.dialect.lock.LockingStrategy;
+import org.hibernate.dialect.lock.UpdateLockingStrategy;
+import org.hibernate.dialect.lock.SelectLockingStrategy;
+import org.hibernate.persister.entity.Lockable;
+import org.hibernate.LockMode;
+
+import java.sql.Types;
+
+/**
+ * An SQL Dialect for Frontbase. Assumes you're using the latest version
+ * of the FrontBase JDBC driver, available from <tt>http://frontbase.com/</tt>
+ * <p>
+ * <b>NOTE</b>: The latest JDBC driver is not always included with the
+ * latest release of FrontBase. Download the driver separately, and enjoy
+ * the informative release notes.
+ * <p>
+ * This dialect was tested with JDBC driver version 2.3.1. This driver
+ * contains a bug that causes batches of updates to fail. (The bug should be
+ * fixed in the next release of the JDBC driver.) If you are using JDBC driver
+ * 2.3.1, you can work-around this problem by setting the following in your
+ * <tt>hibernate.properties</tt> file: <tt>hibernate.jdbc.batch_size=15</tt>
+ *
+ * @author Ron Lussier <tt>rlussier at lenscraft.com</tt>
+ */
+public class FrontBaseDialect extends Dialect {
+
+ public FrontBaseDialect() {
+ super();
+
+ registerColumnType( Types.BIT, "bit" );
+ registerColumnType( Types.BIGINT, "longint" );
+ registerColumnType( Types.SMALLINT, "smallint" );
+ registerColumnType( Types.TINYINT, "tinyint" );
+ registerColumnType( Types.INTEGER, "integer" );
+ registerColumnType( Types.CHAR, "char(1)" );
+ registerColumnType( Types.VARCHAR, "varchar($l)" );
+ registerColumnType( Types.FLOAT, "float" );
+ registerColumnType( Types.DOUBLE, "double precision" );
+ registerColumnType( Types.DATE, "date" );
+ registerColumnType( Types.TIME, "time" );
+ registerColumnType( Types.TIMESTAMP, "timestamp" );
+ registerColumnType( Types.VARBINARY, "bit varying($l)" );
+ registerColumnType( Types.NUMERIC, "numeric($p,$s)" );
+ registerColumnType( Types.BLOB, "blob" );
+ registerColumnType( Types.CLOB, "clob" );
+ }
+
+ public String getAddColumnString() {
+ return "add column";
+ }
+
+ public String getCascadeConstraintsString() {
+ return " cascade";
+ }
+
+ public boolean dropConstraints() {
+ return false;
+ }
+
+ /**
+ * Does this dialect support the <tt>FOR UPDATE</tt> syntax. No!
+ *
+ * @return false always. FrontBase doesn't support this syntax,
+ * which was dropped with SQL92
+ */
+ public String getForUpdateString() {
+ return "";
+ }
+
+ public String getCurrentTimestampCallString() {
+ // TODO : not sure this is correct, could not find docs on how to do this.
+ return "{?= call current_timestamp}";
+ }
+
+ public boolean isCurrentTimestampSelectStringCallable() {
+ return true;
+ }
+
+ public LockingStrategy getLockingStrategy(Lockable lockable, LockMode lockMode) {
+ // Frontbase has no known variation of a "SELECT ... FOR UPDATE" syntax...
+ if ( lockMode.greaterThan( LockMode.READ ) ) {
+ return new UpdateLockingStrategy( lockable, lockMode );
+ }
+ else {
+ return new SelectLockingStrategy( lockable, lockMode );
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/H2Dialect.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/H2Dialect.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/H2Dialect.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,298 @@
+package org.hibernate.dialect;
+
+import java.sql.SQLException;
+import java.sql.Types;
+
+import org.hibernate.Hibernate;
+import org.hibernate.cfg.Environment;
+import org.hibernate.dialect.function.NoArgSQLFunction;
+import org.hibernate.dialect.function.StandardSQLFunction;
+import org.hibernate.dialect.function.VarArgsSQLFunction;
+import org.hibernate.exception.TemplatedViolatedConstraintNameExtracter;
+import org.hibernate.exception.ViolatedConstraintNameExtracter;
+import org.hibernate.util.ReflectHelper;
+
+/**
+ * A dialect compatible with the H2 database.
+ *
+ * @author Thomas Mueller
+ *
+ */
+public class H2Dialect extends Dialect {
+
+ private String querySequenceString;
+ public H2Dialect() {
+ super();
+
+ querySequenceString = "select sequence_name from information_schema.sequences";
+ try {
+ // HHH-2300
+ Class constants = ReflectHelper.classForName( "org.h2.engine.Constants" );
+ Integer build = (Integer)constants.getDeclaredField("BUILD_ID" ).get(null);
+ int buildid = build.intValue();
+ if(buildid < 32) {
+ querySequenceString = "select name from information_schema.sequences";
+ }
+ } catch(Throwable e) {
+ // ignore (probably H2 not in the classpath)
+ }
+ registerColumnType(Types.BOOLEAN, "boolean");
+ registerColumnType(Types.BIGINT, "bigint");
+ registerColumnType(Types.BINARY, "binary");
+ registerColumnType(Types.BIT, "bit");
+ registerColumnType(Types.CHAR, "char($l)");
+ registerColumnType(Types.DATE, "date");
+ registerColumnType(Types.DECIMAL, "decimal($p,$s)");
+ registerColumnType(Types.DOUBLE, "double");
+ registerColumnType(Types.FLOAT, "float");
+ registerColumnType(Types.INTEGER, "integer");
+ registerColumnType(Types.LONGVARBINARY, "longvarbinary");
+ registerColumnType(Types.LONGVARCHAR, "longvarchar");
+ registerColumnType(Types.REAL, "real");
+ registerColumnType(Types.SMALLINT, "smallint");
+ registerColumnType(Types.TINYINT, "tinyint");
+ registerColumnType(Types.TIME, "time");
+ registerColumnType(Types.TIMESTAMP, "timestamp");
+ registerColumnType(Types.VARCHAR, "varchar($l)");
+ registerColumnType(Types.VARBINARY, "binary($l)");
+ registerColumnType(Types.NUMERIC, "numeric");
+ registerColumnType(Types.BLOB, "blob");
+ registerColumnType(Types.CLOB, "clob");
+
+ // select topic, syntax from information_schema.help
+ // where section like 'Function%' order by section, topic
+
+// registerFunction("abs", new StandardSQLFunction("abs"));
+ registerFunction("acos", new StandardSQLFunction("acos", Hibernate.DOUBLE));
+ registerFunction("asin", new StandardSQLFunction("asin", Hibernate.DOUBLE));
+ registerFunction("atan", new StandardSQLFunction("atan", Hibernate.DOUBLE));
+ registerFunction("atan2", new StandardSQLFunction("atan2", Hibernate.DOUBLE));
+ registerFunction("bitand", new StandardSQLFunction("bitand", Hibernate.INTEGER));
+ registerFunction("bitor", new StandardSQLFunction("bitor", Hibernate.INTEGER));
+ registerFunction("bitxor", new StandardSQLFunction("bitxor", Hibernate.INTEGER));
+ registerFunction("ceiling", new StandardSQLFunction("ceiling", Hibernate.DOUBLE));
+ registerFunction("cos", new StandardSQLFunction("cos", Hibernate.DOUBLE));
+ registerFunction("cot", new StandardSQLFunction("cot", Hibernate.DOUBLE));
+ registerFunction("degrees", new StandardSQLFunction("degrees", Hibernate.DOUBLE));
+ registerFunction("exp", new StandardSQLFunction("exp", Hibernate.DOUBLE));
+ registerFunction("floor", new StandardSQLFunction("floor", Hibernate.DOUBLE));
+ registerFunction("log", new StandardSQLFunction("log", Hibernate.DOUBLE));
+ registerFunction("log10", new StandardSQLFunction("log10", Hibernate.DOUBLE));
+// registerFunction("mod", new StandardSQLFunction("mod", Hibernate.INTEGER));
+ registerFunction("pi", new NoArgSQLFunction("pi", Hibernate.DOUBLE));
+ registerFunction("power", new StandardSQLFunction("power", Hibernate.DOUBLE));
+ registerFunction("radians", new StandardSQLFunction("radians", Hibernate.DOUBLE));
+ registerFunction("rand", new NoArgSQLFunction("rand", Hibernate.DOUBLE));
+ registerFunction("round", new StandardSQLFunction("round", Hibernate.DOUBLE));
+ registerFunction("roundmagic", new StandardSQLFunction("roundmagic", Hibernate.DOUBLE));
+ registerFunction("sign", new StandardSQLFunction("sign", Hibernate.INTEGER));
+ registerFunction("sin", new StandardSQLFunction("sin", Hibernate.DOUBLE));
+// registerFunction("sqrt", new StandardSQLFunction("sqrt", Hibernate.DOUBLE));
+ registerFunction("tan", new StandardSQLFunction("tan", Hibernate.DOUBLE));
+ registerFunction("truncate", new StandardSQLFunction("truncate", Hibernate.DOUBLE));
+
+ registerFunction("compress", new StandardSQLFunction("compress", Hibernate.BINARY));
+ registerFunction("expand", new StandardSQLFunction("compress", Hibernate.BINARY));
+ registerFunction("decrypt", new StandardSQLFunction("decrypt", Hibernate.BINARY));
+ registerFunction("encrypt", new StandardSQLFunction("encrypt", Hibernate.BINARY));
+ registerFunction("hash", new StandardSQLFunction("hash", Hibernate.BINARY));
+
+ registerFunction("ascii", new StandardSQLFunction("ascii", Hibernate.INTEGER));
+// registerFunction("bit_length", new StandardSQLFunction("bit_length", Hibernate.INTEGER));
+ registerFunction("char", new StandardSQLFunction("char", Hibernate.CHARACTER));
+ registerFunction("concat", new VarArgsSQLFunction(Hibernate.STRING, "(", "||", ")"));
+ registerFunction("difference", new StandardSQLFunction("difference", Hibernate.INTEGER));
+ registerFunction("hextoraw", new StandardSQLFunction("hextoraw", Hibernate.STRING));
+ registerFunction("lower", new StandardSQLFunction("lower", Hibernate.STRING));
+ registerFunction("insert", new StandardSQLFunction("lower", Hibernate.STRING));
+ registerFunction("left", new StandardSQLFunction("left", Hibernate.STRING));
+// registerFunction("length", new StandardSQLFunction("length", Hibernate.INTEGER));
+// registerFunction("locate", new StandardSQLFunction("locate", Hibernate.INTEGER));
+// registerFunction("lower", new StandardSQLFunction("lower", Hibernate.STRING));
+ registerFunction("lcase", new StandardSQLFunction("lcase", Hibernate.STRING));
+ registerFunction("ltrim", new StandardSQLFunction("ltrim", Hibernate.STRING));
+ registerFunction("octet_length", new StandardSQLFunction("octet_length", Hibernate.INTEGER));
+ registerFunction("position", new StandardSQLFunction("position", Hibernate.INTEGER));
+ registerFunction("rawtohex", new StandardSQLFunction("rawtohex", Hibernate.STRING));
+ registerFunction("repeat", new StandardSQLFunction("repeat", Hibernate.STRING));
+ registerFunction("replace", new StandardSQLFunction("replace", Hibernate.STRING));
+ registerFunction("right", new StandardSQLFunction("right", Hibernate.STRING));
+ registerFunction("rtrim", new StandardSQLFunction("rtrim", Hibernate.STRING));
+ registerFunction("soundex", new StandardSQLFunction("soundex", Hibernate.STRING));
+ registerFunction("space", new StandardSQLFunction("space", Hibernate.STRING));
+ registerFunction("stringencode", new StandardSQLFunction("stringencode", Hibernate.STRING));
+ registerFunction("stringdecode", new StandardSQLFunction("stringdecode", Hibernate.STRING));
+// registerFunction("substring", new StandardSQLFunction("substring", Hibernate.STRING));
+// registerFunction("upper", new StandardSQLFunction("upper", Hibernate.STRING));
+ registerFunction("ucase", new StandardSQLFunction("ucase", Hibernate.STRING));
+
+ registerFunction("stringtoutf8", new StandardSQLFunction("stringtoutf8", Hibernate.BINARY));
+ registerFunction("utf8tostring", new StandardSQLFunction("utf8tostring", Hibernate.STRING));
+
+ registerFunction("current_date", new NoArgSQLFunction("current_date", Hibernate.DATE));
+ registerFunction("current_time", new NoArgSQLFunction("current_time", Hibernate.TIME));
+ registerFunction("current_timestamp", new NoArgSQLFunction("current_timestamp", Hibernate.TIMESTAMP));
+ registerFunction("datediff", new NoArgSQLFunction("datediff", Hibernate.INTEGER));
+ registerFunction("dayname", new StandardSQLFunction("dayname", Hibernate.STRING));
+ registerFunction("dayofmonth", new StandardSQLFunction("dayofmonth", Hibernate.INTEGER));
+ registerFunction("dayofweek", new StandardSQLFunction("dayofweek", Hibernate.INTEGER));
+ registerFunction("dayofyear", new StandardSQLFunction("dayofyear", Hibernate.INTEGER));
+// registerFunction("hour", new StandardSQLFunction("hour", Hibernate.INTEGER));
+// registerFunction("minute", new StandardSQLFunction("minute", Hibernate.INTEGER));
+// registerFunction("month", new StandardSQLFunction("month", Hibernate.INTEGER));
+ registerFunction("monthname", new StandardSQLFunction("monthname", Hibernate.STRING));
+ registerFunction("quater", new StandardSQLFunction("quater", Hibernate.INTEGER));
+// registerFunction("second", new StandardSQLFunction("second", Hibernate.INTEGER));
+ registerFunction("week", new StandardSQLFunction("week", Hibernate.INTEGER));
+// registerFunction("year", new StandardSQLFunction("year", Hibernate.INTEGER));
+
+ registerFunction("curdate", new NoArgSQLFunction("curdate", Hibernate.DATE));
+ registerFunction("curtime", new NoArgSQLFunction("curtime", Hibernate.TIME));
+ registerFunction("curtimestamp", new NoArgSQLFunction("curtimestamp", Hibernate.TIME));
+ registerFunction("now", new NoArgSQLFunction("now", Hibernate.TIMESTAMP));
+
+ registerFunction("database", new NoArgSQLFunction("database", Hibernate.STRING));
+ registerFunction("user", new NoArgSQLFunction("user", Hibernate.STRING));
+
+ getDefaultProperties().setProperty(Environment.STATEMENT_BATCH_SIZE, DEFAULT_BATCH_SIZE);
+
+ }
+
+ public String getAddColumnString() {
+ return "add column";
+ }
+
+ public boolean supportsIdentityColumns() {
+ return true;
+ }
+
+ public String getIdentityColumnString() {
+ return "generated by default as identity"; // not null is implicit
+ }
+
+ public String getIdentitySelectString() {
+ return "call identity()";
+ }
+
+ public String getIdentityInsertString() {
+ return "null";
+ }
+
+ public String getForUpdateString() {
+ return " for update";
+ }
+
+ public boolean supportsUnique() {
+ return true;
+ }
+
+ public boolean supportsLimit() {
+ return true;
+ }
+
+ public String getLimitString(String sql, boolean hasOffset) {
+ return new StringBuffer(sql.length() + 20).
+ append(sql).
+ append(hasOffset ? " limit ? offset ?" : " limit ?").
+ toString();
+ }
+
+ public boolean bindLimitParametersInReverseOrder() {
+ return true;
+ }
+
+ public boolean bindLimitParametersFirst() {
+ return false;
+ }
+
+ public boolean supportsIfExistsAfterTableName() {
+ return true;
+ }
+
+ public boolean supportsSequences() {
+ return true;
+ }
+
+ public boolean supportsPooledSequences() {
+ return true;
+ }
+
+ public String getCreateSequenceString(String sequenceName) {
+ return "create sequence " + sequenceName;
+ }
+
+ public String getDropSequenceString(String sequenceName) {
+ return "drop sequence " + sequenceName;
+ }
+
+ public String getSelectSequenceNextValString(String sequenceName) {
+ return "next value for " + sequenceName;
+ }
+
+ public String getSequenceNextValString(String sequenceName) {
+ return "call next value for " + sequenceName;
+ }
+
+ public String getQuerySequencesString() {
+ return querySequenceString;
+ }
+
+ public ViolatedConstraintNameExtracter getViolatedConstraintNameExtracter() {
+ return EXTRACTER;
+ }
+
+ private static ViolatedConstraintNameExtracter EXTRACTER = new TemplatedViolatedConstraintNameExtracter() {
+
+ /**
+ * Extract the name of the violated constraint from the given SQLException.
+ *
+ * @param sqle The exception that was the result of the constraint violation.
+ * @return The extracted constraint name.
+ */
+ public String extractConstraintName(SQLException sqle) {
+ String constraintName = null;
+ // 23000: Check constraint violation: {0}
+ // 23001: Unique index or primary key violation: {0}
+ if(sqle.getSQLState().startsWith("23")) {
+ String message = sqle.getMessage();
+ int idx = message.indexOf("violation: ");
+ if(idx > 0) {
+ constraintName = message.substring(idx + "violation: ".length());
+ }
+ }
+ return constraintName;
+ }
+
+ };
+
+ public boolean supportsTemporaryTables() {
+ return true;
+ }
+
+ public String getCreateTemporaryTableString() {
+ return "create temporary table if not exists";
+ }
+
+ public boolean supportsCurrentTimestampSelection() {
+ return true;
+ }
+
+ public boolean isCurrentTimestampSelectStringCallable() {
+ return false;
+ }
+
+ public String getCurrentTimestampSelectString() {
+ return "call current_timestamp()";
+ }
+
+ public boolean supportsUnionAll() {
+ return true;
+ }
+
+
+ // Overridden informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public boolean supportsLobValueChangePropogation() {
+ return false;
+ }
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/HSQLDialect.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/HSQLDialect.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/HSQLDialect.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,325 @@
+//$Id: HSQLDialect.java 11259 2007-03-07 22:55:12Z steve.ebersole at jboss.com $
+package org.hibernate.dialect;
+
+import java.sql.SQLException;
+import java.sql.Types;
+import java.io.Serializable;
+
+import org.hibernate.Hibernate;
+import org.hibernate.LockMode;
+import org.hibernate.StaleObjectStateException;
+import org.hibernate.JDBCException;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.entity.Lockable;
+import org.hibernate.util.ReflectHelper;
+import org.hibernate.cfg.Environment;
+import org.hibernate.dialect.function.NoArgSQLFunction;
+import org.hibernate.dialect.function.StandardSQLFunction;
+import org.hibernate.dialect.function.VarArgsSQLFunction;
+import org.hibernate.dialect.lock.LockingStrategy;
+import org.hibernate.dialect.lock.SelectLockingStrategy;
+import org.hibernate.exception.JDBCExceptionHelper;
+import org.hibernate.exception.TemplatedViolatedConstraintNameExtracter;
+import org.hibernate.exception.ViolatedConstraintNameExtracter;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * An SQL dialect compatible with HSQLDB (Hypersonic SQL).
+ * <p/>
+ * Note this version supports HSQLDB version 1.8 and higher, only.
+ *
+ * @author Christoph Sturm, Phillip Baird
+ */
+public class HSQLDialect extends Dialect {
+
+ private static final Log log = LogFactory.getLog( HSQLDialect.class );
+
+ private boolean schemaSupport;
+
+ public HSQLDialect() {
+ super();
+ registerColumnType( Types.BIGINT, "bigint" );
+ registerColumnType( Types.BINARY, "binary" );
+ registerColumnType( Types.BIT, "bit" );
+ registerColumnType( Types.CHAR, "char(1)" );
+ registerColumnType( Types.DATE, "date" );
+ registerColumnType( Types.DECIMAL, "decimal" );
+ registerColumnType( Types.DOUBLE, "double" );
+ registerColumnType( Types.FLOAT, "float" );
+ registerColumnType( Types.INTEGER, "integer" );
+ registerColumnType( Types.LONGVARBINARY, "longvarbinary" );
+ registerColumnType( Types.LONGVARCHAR, "longvarchar" );
+ registerColumnType( Types.SMALLINT, "smallint" );
+ registerColumnType( Types.TINYINT, "tinyint" );
+ registerColumnType( Types.TIME, "time" );
+ registerColumnType( Types.TIMESTAMP, "timestamp" );
+ registerColumnType( Types.VARCHAR, "varchar($l)" );
+ registerColumnType( Types.VARBINARY, "varbinary($l)" );
+ registerColumnType( Types.NUMERIC, "numeric" );
+ //HSQL has no Blob/Clob support .... but just put these here for now!
+ registerColumnType( Types.BLOB, "longvarbinary" );
+ registerColumnType( Types.CLOB, "longvarchar" );
+
+ registerFunction( "ascii", new StandardSQLFunction( "ascii", Hibernate.INTEGER ) );
+ registerFunction( "char", new StandardSQLFunction( "char", Hibernate.CHARACTER ) );
+ registerFunction( "length", new StandardSQLFunction( "length", Hibernate.LONG ) );
+ registerFunction( "lower", new StandardSQLFunction( "lower" ) );
+ registerFunction( "upper", new StandardSQLFunction( "upper" ) );
+ registerFunction( "lcase", new StandardSQLFunction( "lcase" ) );
+ registerFunction( "ucase", new StandardSQLFunction( "ucase" ) );
+ registerFunction( "soundex", new StandardSQLFunction( "soundex", Hibernate.STRING ) );
+ registerFunction( "ltrim", new StandardSQLFunction( "ltrim" ) );
+ registerFunction( "rtrim", new StandardSQLFunction( "rtrim" ) );
+ registerFunction( "reverse", new StandardSQLFunction( "reverse" ) );
+ registerFunction( "space", new StandardSQLFunction( "space", Hibernate.STRING ) );
+ registerFunction( "rawtohex", new StandardSQLFunction( "rawtohex" ) );
+ registerFunction( "hextoraw", new StandardSQLFunction( "hextoraw" ) );
+
+ registerFunction( "user", new NoArgSQLFunction( "user", Hibernate.STRING ) );
+ registerFunction( "database", new NoArgSQLFunction( "database", Hibernate.STRING ) );
+
+ registerFunction( "current_date", new NoArgSQLFunction( "current_date", Hibernate.DATE, false ) );
+ registerFunction( "curdate", new NoArgSQLFunction( "curdate", Hibernate.DATE ) );
+ registerFunction( "current_timestamp", new NoArgSQLFunction( "current_timestamp", Hibernate.TIMESTAMP, false ) );
+ registerFunction( "now", new NoArgSQLFunction( "now", Hibernate.TIMESTAMP ) );
+ registerFunction( "current_time", new NoArgSQLFunction( "current_time", Hibernate.TIME, false ) );
+ registerFunction( "curtime", new NoArgSQLFunction( "curtime", Hibernate.TIME ) );
+ registerFunction( "day", new StandardSQLFunction( "day", Hibernate.INTEGER ) );
+ registerFunction( "dayofweek", new StandardSQLFunction( "dayofweek", Hibernate.INTEGER ) );
+ registerFunction( "dayofyear", new StandardSQLFunction( "dayofyear", Hibernate.INTEGER ) );
+ registerFunction( "dayofmonth", new StandardSQLFunction( "dayofmonth", Hibernate.INTEGER ) );
+ registerFunction( "month", new StandardSQLFunction( "month", Hibernate.INTEGER ) );
+ registerFunction( "year", new StandardSQLFunction( "year", Hibernate.INTEGER ) );
+ registerFunction( "week", new StandardSQLFunction( "week", Hibernate.INTEGER ) );
+ registerFunction( "quater", new StandardSQLFunction( "quater", Hibernate.INTEGER ) );
+ registerFunction( "hour", new StandardSQLFunction( "hour", Hibernate.INTEGER ) );
+ registerFunction( "minute", new StandardSQLFunction( "minute", Hibernate.INTEGER ) );
+ registerFunction( "second", new StandardSQLFunction( "second", Hibernate.INTEGER ) );
+ registerFunction( "dayname", new StandardSQLFunction( "dayname", Hibernate.STRING ) );
+ registerFunction( "monthname", new StandardSQLFunction( "monthname", Hibernate.STRING ) );
+
+ registerFunction( "abs", new StandardSQLFunction( "abs" ) );
+ registerFunction( "sign", new StandardSQLFunction( "sign", Hibernate.INTEGER ) );
+
+ registerFunction( "acos", new StandardSQLFunction( "acos", Hibernate.DOUBLE ) );
+ registerFunction( "asin", new StandardSQLFunction( "asin", Hibernate.DOUBLE ) );
+ registerFunction( "atan", new StandardSQLFunction( "atan", Hibernate.DOUBLE ) );
+ registerFunction( "cos", new StandardSQLFunction( "cos", Hibernate.DOUBLE ) );
+ registerFunction( "cot", new StandardSQLFunction( "cot", Hibernate.DOUBLE ) );
+ registerFunction( "exp", new StandardSQLFunction( "exp", Hibernate.DOUBLE ) );
+ registerFunction( "log", new StandardSQLFunction( "log", Hibernate.DOUBLE ) );
+ registerFunction( "log10", new StandardSQLFunction( "log10", Hibernate.DOUBLE ) );
+ registerFunction( "sin", new StandardSQLFunction( "sin", Hibernate.DOUBLE ) );
+ registerFunction( "sqrt", new StandardSQLFunction( "sqrt", Hibernate.DOUBLE ) );
+ registerFunction( "tan", new StandardSQLFunction( "tan", Hibernate.DOUBLE ) );
+ registerFunction( "pi", new NoArgSQLFunction( "pi", Hibernate.DOUBLE ) );
+ registerFunction( "rand", new StandardSQLFunction( "rand", Hibernate.FLOAT ) );
+
+ registerFunction( "radians", new StandardSQLFunction( "radians", Hibernate.DOUBLE ) );
+ registerFunction( "degrees", new StandardSQLFunction( "degrees", Hibernate.DOUBLE ) );
+ registerFunction( "roundmagic", new StandardSQLFunction( "roundmagic" ) );
+
+ registerFunction( "ceiling", new StandardSQLFunction( "ceiling" ) );
+ registerFunction( "floor", new StandardSQLFunction( "floor" ) );
+
+ // Multi-param dialect functions...
+ registerFunction( "mod", new StandardSQLFunction( "mod", Hibernate.INTEGER ) );
+
+ // function templates
+ registerFunction( "concat", new VarArgsSQLFunction( Hibernate.STRING, "(", "||", ")" ) );
+
+ getDefaultProperties().setProperty( Environment.STATEMENT_BATCH_SIZE, DEFAULT_BATCH_SIZE );
+
+ try {
+ // Does present HSQLDB Database class support schemas?
+ // yuck! Perhaps we should think about a new dialect? Especially
+ // if more things are going to break back-compat moving forward
+ ReflectHelper.classForName( "org.hsqldb.Database" ).getDeclaredField( "schemaManager" );
+ schemaSupport = true;
+ }
+ catch (Throwable t) {
+ schemaSupport = false;
+ }
+ }
+
+ public String getAddColumnString() {
+ return "add column";
+ }
+
+ public boolean supportsIdentityColumns() {
+ return true;
+ }
+
+ public String getIdentityColumnString() {
+ return "generated by default as identity (start with 1)"; //not null is implicit
+ }
+
+ public String getIdentitySelectString() {
+ return "call identity()";
+ }
+
+ public String getIdentityInsertString() {
+ return "null";
+ }
+
+ public String getForUpdateString() {
+ return "";
+ }
+
+ public boolean supportsUnique() {
+ return false;
+ }
+
+ public boolean supportsLimit() {
+ return true;
+ }
+
+ public String getLimitString(String sql, boolean hasOffset) {
+ return new StringBuffer( sql.length() + 10 )
+ .append( sql )
+ .insert( sql.toLowerCase().indexOf( "select" ) + 6, hasOffset ? " limit ? ?" : " top ?" )
+ .toString();
+ }
+
+ public boolean bindLimitParametersFirst() {
+ return true;
+ }
+
+ public boolean supportsIfExistsAfterTableName() {
+ return true;
+ }
+
+ public boolean supportsColumnCheck() {
+ 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 " + initialValue + " increment by " + incrementSize
+ };
+ }
+
+ public String[] getDropSequenceStrings(String sequenceName) {
+ return new String[] {
+ "drop table dual_" + sequenceName + " if exists",
+ "drop sequence " + sequenceName
+ };
+ }
+
+ public String getSelectSequenceNextValString(String sequenceName) {
+ return "next value for " + sequenceName;
+ }
+
+ public String getSequenceNextValString(String sequenceName) {
+ return "select next value for " + sequenceName + " from dual_" + sequenceName;
+ }
+
+ public String getQuerySequencesString() {
+ if ( schemaSupport ) {
+ return "select sequence_name from information_schema.system_sequences";
+ }
+ else {
+ return "select sequence_name from system_sequences";
+ }
+ }
+
+ public ViolatedConstraintNameExtracter getViolatedConstraintNameExtracter() {
+ return EXTRACTER;
+ }
+
+ private static ViolatedConstraintNameExtracter EXTRACTER = new TemplatedViolatedConstraintNameExtracter() {
+
+ /**
+ * Extract the name of the violated constraint from the given SQLException.
+ *
+ * @param sqle The exception that was the result of the constraint violation.
+ * @return The extracted constraint name.
+ */
+ public String extractConstraintName(SQLException sqle) {
+ String constraintName = null;
+
+ int errorCode = JDBCExceptionHelper.extractErrorCode( sqle );
+
+ if ( errorCode == -8 ) {
+ constraintName = extractUsingTemplate(
+ "Integrity constraint violation ", " table:", sqle.getMessage()
+ );
+ }
+ else if ( errorCode == -9 ) {
+ constraintName = extractUsingTemplate(
+ "Violation of unique index: ", " in statement [", sqle.getMessage()
+ );
+ }
+ else if ( errorCode == -104 ) {
+ constraintName = extractUsingTemplate(
+ "Unique constraint violation: ", " in statement [", sqle.getMessage()
+ );
+ }
+ else if ( errorCode == -177 ) {
+ constraintName = extractUsingTemplate(
+ "Integrity constraint violation - no parent ", " table:", sqle.getMessage()
+ );
+ }
+
+ return constraintName;
+ }
+
+ };
+
+ /**
+ * HSQL does not really support temp tables; just take advantage of the
+ * fact that it is a single user db...
+ */
+ public boolean supportsTemporaryTables() {
+ return true;
+ }
+
+ public boolean supportsCurrentTimestampSelection() {
+ return false;
+ }
+
+ public LockingStrategy getLockingStrategy(Lockable lockable, LockMode lockMode) {
+ // HSQLDB only supports READ_UNCOMMITTED transaction isolation
+ return new ReadUncommittedLockingStrategy( lockable, lockMode );
+ }
+
+ public static class ReadUncommittedLockingStrategy extends SelectLockingStrategy {
+ public ReadUncommittedLockingStrategy(Lockable lockable, LockMode lockMode) {
+ super( lockable, lockMode );
+ }
+
+ public void lock(Serializable id, Object version, Object object, SessionImplementor session)
+ throws StaleObjectStateException, JDBCException {
+ if ( getLockMode().greaterThan( LockMode.READ ) ) {
+ log.warn( "HSQLDB supports only READ_UNCOMMITTED isolation" );
+ }
+ super.lock( id, version, object, session );
+ }
+ }
+
+
+ // Overridden informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public boolean supportsEmptyInList() {
+ return false;
+ }
+
+ public boolean supportsLobValueChangePropogation() {
+ return false;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/InformixDialect.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/InformixDialect.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/InformixDialect.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,213 @@
+//$Id: InformixDialect.java 11177 2007-02-09 15:43:04Z steve.ebersole at jboss.com $
+package org.hibernate.dialect;
+
+import java.sql.SQLException;
+import java.sql.Types;
+
+import org.hibernate.MappingException;
+import org.hibernate.Hibernate;
+import org.hibernate.dialect.function.VarArgsSQLFunction;
+import org.hibernate.exception.JDBCExceptionHelper;
+import org.hibernate.exception.TemplatedViolatedConstraintNameExtracter;
+import org.hibernate.exception.ViolatedConstraintNameExtracter;
+import org.hibernate.util.StringHelper;
+
+/**
+ * Informix dialect.<br>
+ * <br>
+ * Seems to work with Informix Dynamic Server Version 7.31.UD3,
+ * Informix JDBC driver version 2.21JC3.
+ * @author Steve Molitor
+ */
+public class InformixDialect extends Dialect {
+
+ /**
+ * Creates new <code>InformixDialect</code> instance. Sets up the JDBC /
+ * Informix type mappings.
+ */
+ public InformixDialect() {
+ super();
+
+ registerColumnType(Types.BIGINT, "int8");
+ registerColumnType(Types.BINARY, "byte");
+ registerColumnType(Types.BIT, "smallint"); // Informix doesn't have a bit type
+ registerColumnType(Types.CHAR, "char($l)");
+ registerColumnType(Types.DATE, "date");
+ registerColumnType(Types.DECIMAL, "decimal");
+ registerColumnType(Types.DOUBLE, "float");
+ registerColumnType(Types.FLOAT, "smallfloat");
+ registerColumnType(Types.INTEGER, "integer");
+ registerColumnType(Types.LONGVARBINARY, "blob"); // or BYTE
+ registerColumnType(Types.LONGVARCHAR, "clob"); // or TEXT?
+ registerColumnType(Types.NUMERIC, "decimal"); // or MONEY
+ registerColumnType(Types.REAL, "smallfloat");
+ registerColumnType(Types.SMALLINT, "smallint");
+ registerColumnType(Types.TIMESTAMP, "datetime year to fraction(5)");
+ registerColumnType(Types.TIME, "datetime hour to second");
+ registerColumnType(Types.TINYINT, "smallint");
+ registerColumnType(Types.VARBINARY, "byte");
+ registerColumnType(Types.VARCHAR, "varchar($l)");
+ registerColumnType(Types.VARCHAR, 255, "varchar($l)");
+ registerColumnType(Types.VARCHAR, 32739, "lvarchar($l)");
+
+ registerFunction( "concat", new VarArgsSQLFunction( Hibernate.STRING, "(", "||", ")" ) );
+ }
+
+ public String getAddColumnString() {
+ return "add";
+ }
+
+ public boolean supportsIdentityColumns() {
+ return true;
+ }
+
+ public String getIdentitySelectString(String table, String column, int type)
+ throws MappingException {
+ return type==Types.BIGINT ?
+ "select dbinfo('serial8') from systables where tabid=1" :
+ "select dbinfo('sqlca.sqlerrd1') from systables where tabid=1";
+ }
+
+ public String getIdentityColumnString(int type) throws MappingException {
+ return type==Types.BIGINT ?
+ "serial8 not null" :
+ "serial not null";
+ }
+
+ public boolean hasDataTypeInIdentityColumn() {
+ return false;
+ }
+
+ /**
+ * The syntax used to add a foreign key constraint to a table.
+ * Informix constraint name must be at the end.
+ * @return String
+ */
+ public String getAddForeignKeyConstraintString(
+ String constraintName,
+ String[] foreignKey,
+ String referencedTable,
+ String[] primaryKey, boolean referencesPrimaryKey
+ ) {
+ StringBuffer result = new StringBuffer(30);
+
+ result.append(" add constraint ")
+ .append(" foreign key (")
+ .append( StringHelper.join(", ", foreignKey) )
+ .append(") references ")
+ .append(referencedTable);
+
+ if(!referencesPrimaryKey) {
+ result.append(" (")
+ .append( StringHelper.join(", ", primaryKey) )
+ .append(')');
+ }
+
+ result.append(" constraint ").append(constraintName);
+
+ return result.toString();
+ }
+
+ /**
+ * The syntax used to add a primary key constraint to a table.
+ * Informix constraint name must be at the end.
+ * @return String
+ */
+ public String getAddPrimaryKeyConstraintString(String constraintName) {
+ return " add constraint primary key constraint " + constraintName + " ";
+ }
+
+ public String getCreateSequenceString(String sequenceName) {
+ return "create sequence " + sequenceName;
+ }
+ public String getDropSequenceString(String sequenceName) {
+ return "drop sequence " + sequenceName + " restrict";
+ }
+
+ public String getSequenceNextValString(String sequenceName) {
+ return "select " + getSelectSequenceNextValString( sequenceName ) + " from systables where tabid=1";
+ }
+
+ public String getSelectSequenceNextValString(String sequenceName) {
+ return sequenceName + ".nextval";
+ }
+
+ public boolean supportsSequences() {
+ return true;
+ }
+
+ public boolean supportsLimit() {
+ return true;
+ }
+
+ public boolean useMaxForLimit() {
+ return true;
+ }
+
+ public boolean supportsLimitOffset() {
+ return false;
+ }
+
+ public String getLimitString(String querySelect, int offset, int limit) {
+ if (offset>0) throw new UnsupportedOperationException("informix has no offset");
+ return new StringBuffer( querySelect.length()+8 )
+ .append(querySelect)
+ .insert( querySelect.toLowerCase().indexOf( "select" ) + 6, " first " + limit )
+ .toString();
+ }
+
+ public boolean supportsVariableLimit() {
+ return false;
+ }
+
+ public ViolatedConstraintNameExtracter getViolatedConstraintNameExtracter() {
+ return EXTRACTER;
+ }
+
+ private static ViolatedConstraintNameExtracter EXTRACTER = new TemplatedViolatedConstraintNameExtracter() {
+
+ /**
+ * Extract the name of the violated constraint from the given SQLException.
+ *
+ * @param sqle The exception that was the result of the constraint violation.
+ * @return The extracted constraint name.
+ */
+ public String extractConstraintName(SQLException sqle) {
+ String constraintName = null;
+
+ int errorCode = JDBCExceptionHelper.extractErrorCode(sqle);
+ if ( errorCode == -268 ) {
+ constraintName = extractUsingTemplate( "Unique constraint (", ") violated.", sqle.getMessage() );
+ }
+ else if ( errorCode == -691 ) {
+ constraintName = extractUsingTemplate( "Missing key in referenced table for referential constraint (", ").", sqle.getMessage() );
+ }
+ else if ( errorCode == -692 ) {
+ constraintName = extractUsingTemplate( "Key value for constraint (", ") is still being referenced.", sqle.getMessage() );
+ }
+
+ if (constraintName != null) {
+ // strip table-owner because Informix always returns constraint names as "<table-owner>.<constraint-name>"
+ int i = constraintName.indexOf('.');
+ if (i != -1) {
+ constraintName = constraintName.substring(i + 1);
+ }
+ }
+
+ return constraintName;
+ }
+
+ };
+
+ public boolean supportsCurrentTimestampSelection() {
+ return true;
+ }
+
+ public boolean isCurrentTimestampSelectStringCallable() {
+ return false;
+ }
+
+ public String getCurrentTimestampSelectString() {
+ return "select distinct current timestamp from informix.systables";
+ }
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/IngresDialect.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/IngresDialect.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/IngresDialect.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,292 @@
+//$Id: IngresDialect.java 10963 2006-12-08 16:06:14Z steve.ebersole at jboss.com $
+package org.hibernate.dialect;
+
+import java.sql.Types;
+
+import org.hibernate.Hibernate;
+import org.hibernate.dialect.function.SQLFunctionTemplate;
+import org.hibernate.dialect.function.NoArgSQLFunction;
+import org.hibernate.dialect.function.StandardSQLFunction;
+import org.hibernate.dialect.function.VarArgsSQLFunction;
+
+
+/**
+ * An Ingres SQL dialect.
+ * <p/>
+ * Known limitations:
+ * - only supports simple constants or columns on the left side of an IN, making (1,2,3) in (...) or (<subselect) in (...) non-supported
+ * - supports only 31 digits in decimal
+ *
+ * @author Ian Booth, Bruce Lunsford, Max Rydahl Andersen
+ */
+public class IngresDialect extends Dialect {
+
+ public IngresDialect() {
+ super();
+ registerColumnType( Types.BIT, "tinyint" );
+ registerColumnType( Types.TINYINT, "tinyint" );
+ registerColumnType( Types.SMALLINT, "smallint" );
+ registerColumnType( Types.INTEGER, "integer" );
+ registerColumnType( Types.BIGINT, "bigint" );
+ registerColumnType( Types.REAL, "real" );
+ registerColumnType( Types.FLOAT, "float" );
+ registerColumnType( Types.DOUBLE, "float" );
+ registerColumnType( Types.NUMERIC, "decimal($p, $s)" );
+ registerColumnType( Types.DECIMAL, "decimal($p, $s)" );
+ registerColumnType( Types.BINARY, 32000, "byte($l)" );
+ registerColumnType( Types.BINARY, "long byte" );
+ registerColumnType( Types.VARBINARY, 32000, "varbyte($l)" );
+ registerColumnType( Types.VARBINARY, "long byte" );
+ registerColumnType( Types.LONGVARBINARY, "long byte" );
+ registerColumnType( Types.CHAR, "char(1)" );
+ registerColumnType( Types.VARCHAR, 32000, "varchar($l)" );
+ registerColumnType( Types.VARCHAR, "long varchar" );
+ registerColumnType( Types.LONGVARCHAR, "long varchar" );
+ registerColumnType( Types.DATE, "date" );
+ registerColumnType( Types.TIME, "date" );
+ registerColumnType( Types.TIMESTAMP, "date" );
+ registerColumnType( Types.BLOB, "long byte" );
+ registerColumnType( Types.CLOB, "long varchar" );
+
+ registerFunction( "abs", new StandardSQLFunction( "abs" ) );
+ registerFunction( "atan", new StandardSQLFunction( "atan", Hibernate.DOUBLE ) );
+ registerFunction( "bit_add", new StandardSQLFunction( "bit_add" ) );
+ registerFunction( "bit_and", new StandardSQLFunction( "bit_and" ) );
+ registerFunction( "bit_length", new StandardSQLFunction( "bit_length" ) );
+ registerFunction( "bit_not", new StandardSQLFunction( "bit_not" ) );
+ registerFunction( "bit_or", new StandardSQLFunction( "bit_or" ) );
+ registerFunction( "bit_xor", new StandardSQLFunction( "bit_xor" ) );
+ registerFunction( "character_length", new StandardSQLFunction( "character_length", Hibernate.LONG ) );
+ registerFunction( "charextract", new StandardSQLFunction( "charextract", Hibernate.STRING ) );
+ registerFunction( "concat", new VarArgsSQLFunction( Hibernate.STRING, "(", "+", ")" ) );
+ registerFunction( "cos", new StandardSQLFunction( "cos", Hibernate.DOUBLE ) );
+ registerFunction( "current_user", new NoArgSQLFunction( "current_user", Hibernate.STRING, false ) );
+ registerFunction( "current_time", new NoArgSQLFunction( "date('now')", Hibernate.TIMESTAMP, false ) );
+ registerFunction( "current_timestamp", new NoArgSQLFunction( "date('now')", Hibernate.TIMESTAMP, false ) );
+ registerFunction( "current_date", new NoArgSQLFunction( "date('now')", Hibernate.TIMESTAMP, false ) );
+ registerFunction( "date_trunc", new StandardSQLFunction( "date_trunc", Hibernate.TIMESTAMP ) );
+ registerFunction( "day", new StandardSQLFunction( "day", Hibernate.INTEGER ) );
+ registerFunction( "dba", new NoArgSQLFunction( "dba", Hibernate.STRING, true ) );
+ registerFunction( "dow", new StandardSQLFunction( "dow", Hibernate.STRING ) );
+ registerFunction( "extract", new SQLFunctionTemplate( Hibernate.INTEGER, "date_part('?1', ?3)" ) );
+ registerFunction( "exp", new StandardSQLFunction( "exp", Hibernate.DOUBLE ) );
+ registerFunction( "gmt_timestamp", new StandardSQLFunction( "gmt_timestamp", Hibernate.STRING ) );
+ registerFunction( "hash", new StandardSQLFunction( "hash", Hibernate.INTEGER ) );
+ registerFunction( "hex", new StandardSQLFunction( "hex", Hibernate.STRING ) );
+ registerFunction( "hour", new StandardSQLFunction( "hour", Hibernate.INTEGER ) );
+ registerFunction( "initial_user", new NoArgSQLFunction( "initial_user", Hibernate.STRING, false ) );
+ registerFunction( "intextract", new StandardSQLFunction( "intextract", Hibernate.INTEGER ) );
+ registerFunction( "left", new StandardSQLFunction( "left", Hibernate.STRING ) );
+ registerFunction( "locate", new SQLFunctionTemplate( Hibernate.LONG, "locate(?1, ?2)" ) );
+ registerFunction( "length", new StandardSQLFunction( "length", Hibernate.LONG ) );
+ registerFunction( "ln", new StandardSQLFunction( "ln", Hibernate.DOUBLE ) );
+ registerFunction( "log", new StandardSQLFunction( "log", Hibernate.DOUBLE ) );
+ registerFunction( "lower", new StandardSQLFunction( "lower" ) );
+ registerFunction( "lowercase", new StandardSQLFunction( "lowercase" ) );
+ registerFunction( "minute", new StandardSQLFunction( "minute", Hibernate.INTEGER ) );
+ registerFunction( "month", new StandardSQLFunction( "month", Hibernate.INTEGER ) );
+ registerFunction( "octet_length", new StandardSQLFunction( "octet_length", Hibernate.LONG ) );
+ registerFunction( "pad", new StandardSQLFunction( "pad", Hibernate.STRING ) );
+ registerFunction( "position", new StandardSQLFunction( "position", Hibernate.LONG ) );
+ registerFunction( "power", new StandardSQLFunction( "power", Hibernate.DOUBLE ) );
+ registerFunction( "random", new NoArgSQLFunction( "random", Hibernate.LONG, true ) );
+ registerFunction( "randomf", new NoArgSQLFunction( "randomf", Hibernate.DOUBLE, true ) );
+ registerFunction( "right", new StandardSQLFunction( "right", Hibernate.STRING ) );
+ registerFunction( "session_user", new NoArgSQLFunction( "session_user", Hibernate.STRING, false ) );
+ registerFunction( "second", new StandardSQLFunction( "second", Hibernate.INTEGER ) );
+ registerFunction( "size", new NoArgSQLFunction( "size", Hibernate.LONG, true ) );
+ registerFunction( "squeeze", new StandardSQLFunction( "squeeze" ) );
+ registerFunction( "sin", new StandardSQLFunction( "sin", Hibernate.DOUBLE ) );
+ registerFunction( "soundex", new StandardSQLFunction( "soundex", Hibernate.STRING ) );
+ registerFunction( "sqrt", new StandardSQLFunction( "sqrt", Hibernate.DOUBLE ) );
+ registerFunction( "substring", new SQLFunctionTemplate( Hibernate.STRING, "substring(?1 FROM ?2 FOR ?3)" ) );
+ registerFunction( "system_user", new NoArgSQLFunction( "system_user", Hibernate.STRING, false ) );
+ registerFunction( "trim", new StandardSQLFunction( "trim", Hibernate.STRING ) );
+ registerFunction( "unhex", new StandardSQLFunction( "unhex", Hibernate.STRING ) );
+ registerFunction( "upper", new StandardSQLFunction( "upper" ) );
+ registerFunction( "uppercase", new StandardSQLFunction( "uppercase" ) );
+ registerFunction( "user", new NoArgSQLFunction( "user", Hibernate.STRING, false ) );
+ registerFunction( "usercode", new NoArgSQLFunction( "usercode", Hibernate.STRING, true ) );
+ registerFunction( "username", new NoArgSQLFunction( "username", Hibernate.STRING, true ) );
+ registerFunction( "uuid_create", new StandardSQLFunction( "uuid_create", Hibernate.BYTE ) );
+ registerFunction( "uuid_compare", new StandardSQLFunction( "uuid_compare", Hibernate.INTEGER ) );
+ registerFunction( "uuid_from_char", new StandardSQLFunction( "uuid_from_char", Hibernate.BYTE ) );
+ registerFunction( "uuid_to_char", new StandardSQLFunction( "uuid_to_char", Hibernate.STRING ) );
+ registerFunction( "year", new StandardSQLFunction( "year", Hibernate.INTEGER ) );
+ }
+
+ /**
+ * Do we need to drop constraints before dropping tables in this dialect?
+ *
+ * @return boolean
+ */
+ public boolean dropConstraints() {
+ return false;
+ }
+
+ /**
+ * Does this dialect support <tt>FOR UPDATE OF</tt>, allowing
+ * particular rows to be locked?
+ *
+ * @return True (Ingres does support "for update of" syntax...)
+ */
+ public boolean supportsForUpdateOf() {
+ return true;
+ }
+
+ /**
+ * The syntax used to add a column to a table (optional).
+ */
+ public String getAddColumnString() {
+ return "add column";
+ }
+
+ /**
+ * The keyword used to specify a nullable column.
+ *
+ * @return String
+ */
+ public String getNullColumnString() {
+ return " with null";
+ }
+
+ /**
+ * Does this dialect support sequences?
+ *
+ * @return boolean
+ */
+ public boolean supportsSequences() {
+ return true;
+ }
+
+ /**
+ * The syntax that fetches the next value of a sequence, if sequences are supported.
+ *
+ * @param sequenceName the name of the sequence
+ *
+ * @return String
+ */
+ public String getSequenceNextValString(String sequenceName) {
+ return "select nextval for " + sequenceName;
+ }
+
+ /**
+ * The syntax used to create a sequence, if sequences are supported.
+ *
+ * @param sequenceName the name of the sequence
+ *
+ * @return String
+ */
+ public String getCreateSequenceString(String sequenceName) {
+ return "create sequence " + sequenceName;
+ }
+
+ /**
+ * The syntax used to drop a sequence, if sequences are supported.
+ *
+ * @param sequenceName the name of the sequence
+ *
+ * @return String
+ */
+ public String getDropSequenceString(String sequenceName) {
+ return "drop sequence " + sequenceName + " restrict";
+ }
+
+ /**
+ * A query used to find all sequences
+ */
+ public String getQuerySequencesString() {
+ return "select seq_name from iisequence";
+ }
+
+ /**
+ * The name of the SQL function that transforms a string to
+ * lowercase
+ *
+ * @return String
+ */
+ public String getLowercaseFunction() {
+ return "lowercase";
+ }
+
+ /**
+ * Does this <tt>Dialect</tt> have some kind of <tt>LIMIT</tt> syntax?
+ */
+ public boolean supportsLimit() {
+ return true;
+ }
+
+ /**
+ * Does this dialect support an offset?
+ */
+ public boolean supportsLimitOffset() {
+ return false;
+ }
+
+ /**
+ * Add a <tt>LIMIT</tt> clause to the given SQL <tt>SELECT</tt>
+ *
+ * @return the modified SQL
+ */
+ public String getLimitString(String querySelect, int offset, int limit) {
+ if ( offset > 0 ) {
+ throw new UnsupportedOperationException( "offset not supported" );
+ }
+ return new StringBuffer( querySelect.length() + 16 )
+ .append( querySelect )
+ .insert( 6, " first " + limit )
+ .toString();
+ }
+
+ public boolean supportsVariableLimit() {
+ return false;
+ }
+
+ /**
+ * Does the <tt>LIMIT</tt> clause take a "maximum" row number instead
+ * of a total number of returned rows?
+ */
+ public boolean useMaxForLimit() {
+ return true;
+ }
+
+ /**
+ * Ingres explicitly needs "unique not null", because "with null" is default
+ */
+ public boolean supportsNotNullUnique() {
+ return false;
+ }
+
+ /**
+ * Does this dialect support temporary tables?
+ */
+ public boolean supportsTemporaryTables() {
+ return true;
+ }
+
+ public String getCreateTemporaryTableString() {
+ return "declare global temporary table";
+ }
+
+ public String getCreateTemporaryTablePostfix() {
+ return "on commit preserve rows with norecovery";
+ }
+
+ public String generateTemporaryTableName(String baseTableName) {
+ return "session." + super.generateTemporaryTableName( baseTableName );
+ }
+
+
+ /**
+ * Expression for current_timestamp
+ */
+ public String getCurrentTimestampSQLFunctionName() {
+ return "date(now)";
+ }
+
+ // Overridden informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public boolean supportsSubselectAsInPredicateLHS() {
+ return false;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/InterbaseDialect.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/InterbaseDialect.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/InterbaseDialect.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,105 @@
+//$Id: InterbaseDialect.java 7746 2005-08-03 23:29:32Z oneovthafew $
+package org.hibernate.dialect;
+
+import java.sql.Types;
+
+import org.hibernate.Hibernate;
+import org.hibernate.cfg.Environment;
+import org.hibernate.dialect.function.VarArgsSQLFunction;
+
+/**
+ * An SQL dialect for Interbase.
+ * @author Gavin King
+ */
+public class InterbaseDialect extends Dialect {
+
+ public InterbaseDialect() {
+ super();
+ registerColumnType( Types.BIT, "smallint" );
+ registerColumnType( Types.BIGINT, "numeric(18,0)" );
+ registerColumnType( Types.SMALLINT, "smallint" );
+ registerColumnType( Types.TINYINT, "smallint" );
+ registerColumnType( Types.INTEGER, "integer" );
+ registerColumnType( Types.CHAR, "char(1)" );
+ registerColumnType( Types.VARCHAR, "varchar($l)" );
+ registerColumnType( Types.FLOAT, "float" );
+ registerColumnType( Types.DOUBLE, "double precision" );
+ registerColumnType( Types.DATE, "date" );
+ registerColumnType( Types.TIME, "time" );
+ registerColumnType( Types.TIMESTAMP, "timestamp" );
+ registerColumnType( Types.VARBINARY, "blob" );
+ registerColumnType( Types.NUMERIC, "numeric($p,$s)" );
+ registerColumnType( Types.BLOB, "blob" );
+ registerColumnType( Types.CLOB, "blob sub_type 1" );
+
+ registerFunction( "concat", new VarArgsSQLFunction( Hibernate.STRING, "(","||",")" ) );
+
+ getDefaultProperties().setProperty(Environment.STATEMENT_BATCH_SIZE, NO_BATCH);
+ }
+
+ public String getAddColumnString() {
+ return "add";
+ }
+
+ public String getSequenceNextValString(String sequenceName) {
+ return "select " + getSelectSequenceNextValString( sequenceName ) + " from RDB$DATABASE";
+ }
+
+ public String getSelectSequenceNextValString(String sequenceName) {
+ return "gen_id( " + sequenceName + ", 1 )";
+ }
+
+ public String getCreateSequenceString(String sequenceName) {
+ return "create generator " + sequenceName;
+ }
+
+ public String getDropSequenceString(String sequenceName) {
+ return "delete from RDB$GENERATORS where RDB$GENERATOR_NAME = '" + sequenceName.toUpperCase() + "'";
+ }
+
+ public String getQuerySequencesString() {
+ return "select RDB$GENERATOR_NAME from RDB$GENERATORS";
+ }
+
+ public String getForUpdateString() {
+ return " with lock";
+ }
+ public String getForUpdateString(String aliases) {
+ return " for update of " + aliases + " with lock";
+ }
+
+ public boolean supportsSequences() {
+ return true;
+ }
+
+ public boolean supportsLimit() {
+ return true;
+ }
+
+ public String getLimitString(String sql, boolean hasOffset) {
+ return new StringBuffer( sql.length()+15 )
+ .append(sql)
+ .append(hasOffset ? " rows ? to ?" : " rows ?")
+ .toString();
+ }
+
+ public boolean bindLimitParametersFirst() {
+ return false;
+ }
+
+ public boolean bindLimitParametersInReverseOrder() {
+ return false;
+ }
+
+ public String getCurrentTimestampCallString() {
+ // TODO : not sure which (either?) is correct, could not find docs on how to do this.
+ // did find various blogs and forums mentioning that select CURRENT_TIMESTAMP
+ // does not work...
+ return "{?= call CURRENT_TIMESTAMP }";
+// return "select CURRENT_TIMESTAMP from RDB$DATABASE";
+ }
+
+ public boolean isCurrentTimestampSelectStringCallable() {
+ return true;
+ }
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/JDataStoreDialect.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/JDataStoreDialect.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/JDataStoreDialect.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,78 @@
+// $Id: JDataStoreDialect.java 7075 2005-06-08 07:06:50Z oneovthafew $
+package org.hibernate.dialect;
+
+import java.sql.Types;
+
+import org.hibernate.cfg.Environment;
+
+/**
+ * A <tt>Dialect</tt> for JDataStore.
+ *
+ * @author Vishy Kasar
+ */
+public class JDataStoreDialect extends Dialect {
+
+ /**
+ * Creates new JDataStoreDialect
+ */
+ public JDataStoreDialect() {
+ super();
+
+ registerColumnType( Types.BIT, "tinyint" );
+ registerColumnType( Types.BIGINT, "bigint" );
+ registerColumnType( Types.SMALLINT, "smallint" );
+ registerColumnType( Types.TINYINT, "tinyint" );
+ registerColumnType( Types.INTEGER, "integer" );
+ registerColumnType( Types.CHAR, "char(1)" );
+ registerColumnType( Types.VARCHAR, "varchar($l)" );
+ registerColumnType( Types.FLOAT, "float" );
+ registerColumnType( Types.DOUBLE, "double" );
+ registerColumnType( Types.DATE, "date" );
+ registerColumnType( Types.TIME, "time" );
+ registerColumnType( Types.TIMESTAMP, "timestamp" );
+ registerColumnType( Types.VARBINARY, "varbinary($l)" );
+ registerColumnType( Types.NUMERIC, "numeric($p, $s)" );
+
+ registerColumnType( Types.BLOB, "varbinary" );
+ registerColumnType( Types.CLOB, "varchar" );
+
+ getDefaultProperties().setProperty( Environment.STATEMENT_BATCH_SIZE, DEFAULT_BATCH_SIZE );
+ }
+
+ public String getAddColumnString() {
+ return "add";
+ }
+
+ public boolean dropConstraints() {
+ return false;
+ }
+
+ public String getCascadeConstraintsString() {
+ return " cascade";
+ }
+
+ public boolean supportsIdentityColumns() {
+ return true;
+ }
+
+ public String getIdentitySelectString() {
+ return null; // NOT_SUPPORTED_SHOULD_USE_JDBC3_PreparedStatement.getGeneratedKeys_method
+ }
+
+ public String getIdentityColumnString() {
+ return "autoincrement";
+ }
+
+ public String getNoColumnsInsertString() {
+ return "default values";
+ }
+
+ public boolean supportsColumnCheck() {
+ return false;
+ }
+
+ public boolean supportsTableCheck() {
+ return false;
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/JDataStoreDialect.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/MckoiDialect.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/MckoiDialect.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/MckoiDialect.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,98 @@
+//$Id: MckoiDialect.java 9328 2006-02-23 17:32:47Z steveebersole $
+package org.hibernate.dialect;
+
+import java.sql.Types;
+
+import org.hibernate.Hibernate;
+import org.hibernate.LockMode;
+import org.hibernate.persister.entity.Lockable;
+import org.hibernate.cfg.Environment;
+import org.hibernate.dialect.function.StandardSQLFunction;
+import org.hibernate.dialect.lock.LockingStrategy;
+import org.hibernate.dialect.lock.UpdateLockingStrategy;
+import org.hibernate.dialect.lock.SelectLockingStrategy;
+import org.hibernate.sql.CaseFragment;
+import org.hibernate.sql.MckoiCaseFragment;
+
+/**
+ * An SQL dialect compatible with McKoi SQL
+ * @author Doug Currie, Gabe Hicks
+ */
+public class MckoiDialect extends Dialect {
+ public MckoiDialect() {
+ super();
+ registerColumnType( Types.BIT, "bit" );
+ registerColumnType( Types.BIGINT, "bigint" );
+ registerColumnType( Types.SMALLINT, "smallint" );
+ registerColumnType( Types.TINYINT, "tinyint" );
+ registerColumnType( Types.INTEGER, "integer" );
+ registerColumnType( Types.CHAR, "char(1)" );
+ registerColumnType( Types.VARCHAR, "varchar($l)" );
+ registerColumnType( Types.FLOAT, "float" );
+ registerColumnType( Types.DOUBLE, "double" );
+ registerColumnType( Types.DATE, "date" );
+ registerColumnType( Types.TIME, "time" );
+ registerColumnType( Types.TIMESTAMP, "timestamp" );
+ registerColumnType( Types.VARBINARY, "varbinary" );
+ registerColumnType( Types.NUMERIC, "numeric" );
+ registerColumnType( Types.BLOB, "blob" );
+ registerColumnType( Types.CLOB, "clob" );
+
+ registerFunction( "upper", new StandardSQLFunction("upper") );
+ registerFunction( "lower", new StandardSQLFunction("lower") );
+ registerFunction( "sqrt", new StandardSQLFunction("sqrt", Hibernate.DOUBLE) );
+ registerFunction( "abs", new StandardSQLFunction("abs") );
+ registerFunction( "sign", new StandardSQLFunction( "sign", Hibernate.INTEGER ) );
+ registerFunction( "length", new StandardSQLFunction( "length", Hibernate.INTEGER ) );
+ registerFunction( "round", new StandardSQLFunction( "round", Hibernate.INTEGER ) );
+ registerFunction( "mod", new StandardSQLFunction( "mod", Hibernate.INTEGER ) );
+ registerFunction( "least", new StandardSQLFunction("least") );
+ registerFunction( "greatest", new StandardSQLFunction("greatest") );
+ registerFunction( "user", new StandardSQLFunction( "user", Hibernate.STRING ) );
+ registerFunction( "concat", new StandardSQLFunction( "concat", Hibernate.STRING ) );
+
+ getDefaultProperties().setProperty(Environment.STATEMENT_BATCH_SIZE, NO_BATCH);
+ }
+
+ public String getAddColumnString() {
+ return "add column";
+ }
+
+ public String getSequenceNextValString(String sequenceName) {
+ return "select " + getSelectSequenceNextValString( sequenceName );
+ }
+
+ public String getSelectSequenceNextValString(String sequenceName) {
+ return "nextval('" + sequenceName + "')";
+ }
+
+ public String getCreateSequenceString(String sequenceName) {
+ return "create sequence " + sequenceName;
+ }
+
+ public String getDropSequenceString(String sequenceName) {
+ return "drop sequence " + sequenceName;
+ }
+
+ public String getForUpdateString() {
+ return "";
+ }
+
+ public boolean supportsSequences() {
+ return true;
+ }
+
+ public CaseFragment createCaseFragment() {
+ return new MckoiCaseFragment();
+ }
+
+ public LockingStrategy getLockingStrategy(Lockable lockable, LockMode lockMode) {
+ // Mckoi has no known variation of a "SELECT ... FOR UPDATE" syntax...
+ if ( lockMode.greaterThan( LockMode.READ ) ) {
+ return new UpdateLockingStrategy( lockable, lockMode );
+ }
+ else {
+ return new SelectLockingStrategy( lockable, lockMode );
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/MimerSQLDialect.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/MimerSQLDialect.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/MimerSQLDialect.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,221 @@
+//$Id: MimerSQLDialect.java 7822 2005-08-10 19:49:36Z oneovthafew $
+package org.hibernate.dialect;
+
+import java.sql.Types;
+
+import org.hibernate.Hibernate;
+import org.hibernate.cfg.Environment;
+import org.hibernate.dialect.function.*;
+
+/**
+ * An Hibernate 3 SQL dialect for Mimer SQL. This dialect requires Mimer SQL 9.2.1 or later
+ * because of the mappings to NCLOB, BINARY, and BINARY VARYING.
+ * @author Fredrik Ålund <fredrik.alund at mimer.se>
+ */
+public class MimerSQLDialect extends Dialect {
+
+ private static final int NATIONAL_CHAR_LENGTH = 2000;
+ private static final int BINARY_MAX_LENGTH = 2000;
+
+ /**
+ * Even thoug Mimer SQL supports character and binary columns up to 15 000 in lenght,
+ * this is also the maximum width of the table (exluding LOBs). To avoid breaking the limit all the
+ * time we limit the length of the character columns to CHAR_MAX_LENTH, NATIONAL_CHAR_LENGTH for national
+ * characters, and BINARY_MAX_LENGTH for binary types.
+ *
+ */
+ public MimerSQLDialect() {
+ super();
+ registerColumnType( Types.BIT, "ODBC.BIT" );
+ registerColumnType( Types.BIGINT, "BIGINT" );
+ registerColumnType( Types.SMALLINT, "SMALLINT" );
+ registerColumnType( Types.TINYINT, "ODBC.TINYINT" );
+ registerColumnType( Types.INTEGER, "INTEGER" );
+ registerColumnType( Types.CHAR, "NCHAR(1)" );
+ registerColumnType( Types.VARCHAR, NATIONAL_CHAR_LENGTH, "NATIONAL CHARACTER VARYING($l)" );
+ registerColumnType( Types.VARCHAR, "NCLOB($l)" );
+ registerColumnType( Types.LONGVARCHAR, "CLOB($1)");
+ registerColumnType( Types.FLOAT, "FLOAT" );
+ registerColumnType( Types.DOUBLE, "DOUBLE PRECISION" );
+ registerColumnType( Types.DATE, "DATE" );
+ registerColumnType( Types.TIME, "TIME" );
+ registerColumnType( Types.TIMESTAMP, "TIMESTAMP" );
+ registerColumnType( Types.VARBINARY, BINARY_MAX_LENGTH, "BINARY VARYING($l)" );
+ registerColumnType( Types.VARBINARY, "BLOB($1)" );
+ registerColumnType( Types.LONGVARBINARY, "BLOB($1)");
+ registerColumnType( Types.BINARY, BINARY_MAX_LENGTH, "BINARY" );
+ registerColumnType( Types.BINARY, "BLOB($1)" );
+ registerColumnType( Types.NUMERIC, "NUMERIC(19, $l)" );
+ registerColumnType( Types.BLOB, "BLOB($l)" );
+ registerColumnType( Types.CLOB, "NCLOB($l)" );
+
+ registerFunction("abs", new StandardSQLFunction("abs") );
+ registerFunction("sign", new StandardSQLFunction("sign", Hibernate.INTEGER) );
+ registerFunction("ceiling", new StandardSQLFunction("ceiling") );
+ registerFunction("floor", new StandardSQLFunction("floor") );
+ registerFunction("round", new StandardSQLFunction("round") );
+
+ registerFunction("dacos", new StandardSQLFunction("dacos", Hibernate.DOUBLE) );
+ registerFunction("acos", new StandardSQLFunction("dacos", Hibernate.DOUBLE) );
+ registerFunction("dasin", new StandardSQLFunction("dasin", Hibernate.DOUBLE) );
+ registerFunction("asin", new StandardSQLFunction("dasin", Hibernate.DOUBLE) );
+ registerFunction("datan", new StandardSQLFunction("datan", Hibernate.DOUBLE) );
+ registerFunction("atan", new StandardSQLFunction("datan", Hibernate.DOUBLE) );
+ registerFunction("datan2", new StandardSQLFunction("datan2", Hibernate.DOUBLE) );
+ registerFunction("atan2", new StandardSQLFunction("datan2", Hibernate.DOUBLE) );
+ registerFunction("dcos", new StandardSQLFunction("dcos", Hibernate.DOUBLE) );
+ registerFunction("cos", new StandardSQLFunction("dcos", Hibernate.DOUBLE) );
+ registerFunction("dcot", new StandardSQLFunction("dcot", Hibernate.DOUBLE) );
+ registerFunction("cot", new StandardSQLFunction("dcot", Hibernate.DOUBLE) );
+ registerFunction("ddegrees", new StandardSQLFunction("ddegrees", Hibernate.DOUBLE) );
+ registerFunction("degrees", new StandardSQLFunction("ddegrees", Hibernate.DOUBLE) );
+ registerFunction("dexp", new StandardSQLFunction("dexp", Hibernate.DOUBLE) );
+ registerFunction("exp", new StandardSQLFunction("dexp", Hibernate.DOUBLE) );
+ registerFunction("dlog", new StandardSQLFunction("dlog", Hibernate.DOUBLE) );
+ registerFunction("log", new StandardSQLFunction("dlog", Hibernate.DOUBLE) );
+ registerFunction("dlog10", new StandardSQLFunction("dlog10", Hibernate.DOUBLE) );
+ registerFunction("log10", new StandardSQLFunction("dlog10", Hibernate.DOUBLE) );
+ registerFunction("dradian", new StandardSQLFunction("dradian", Hibernate.DOUBLE) );
+ registerFunction("radian", new StandardSQLFunction("dradian", Hibernate.DOUBLE) );
+ registerFunction("dsin", new StandardSQLFunction("dsin", Hibernate.DOUBLE) );
+ registerFunction("sin", new StandardSQLFunction("dsin", Hibernate.DOUBLE) );
+ registerFunction("soundex", new StandardSQLFunction("soundex", Hibernate.STRING) );
+ registerFunction("dsqrt", new StandardSQLFunction("dsqrt", Hibernate.DOUBLE) );
+ registerFunction("sqrt", new StandardSQLFunction("dsqrt", Hibernate.DOUBLE) );
+ registerFunction("dtan", new StandardSQLFunction("dtan", Hibernate.DOUBLE) );
+ registerFunction("tan", new StandardSQLFunction("dtan", Hibernate.DOUBLE) );
+ registerFunction("dpower", new StandardSQLFunction("dpower") );
+ registerFunction("power", new StandardSQLFunction("dpower") );
+
+ registerFunction("date", new StandardSQLFunction("date", Hibernate.DATE) );
+ registerFunction("dayofweek", new StandardSQLFunction("dayofweek", Hibernate.INTEGER) );
+ registerFunction("dayofyear", new StandardSQLFunction("dayofyear", Hibernate.INTEGER) );
+ registerFunction("time", new StandardSQLFunction("time", Hibernate.TIME) );
+ registerFunction("timestamp", new StandardSQLFunction("timestamp", Hibernate.TIMESTAMP) );
+ registerFunction("week", new StandardSQLFunction("week", Hibernate.INTEGER) );
+
+
+ registerFunction("varchar", new StandardSQLFunction("varchar", Hibernate.STRING) );
+ registerFunction("real", new StandardSQLFunction("real", Hibernate.FLOAT) );
+ registerFunction("bigint", new StandardSQLFunction("bigint", Hibernate.LONG) );
+ registerFunction("char", new StandardSQLFunction("char", Hibernate.CHARACTER) );
+ registerFunction("integer", new StandardSQLFunction("integer", Hibernate.INTEGER) );
+ registerFunction("smallint", new StandardSQLFunction("smallint", Hibernate.SHORT) );
+
+ registerFunction("ascii_char", new StandardSQLFunction("ascii_char", Hibernate.CHARACTER) );
+ registerFunction("ascii_code", new StandardSQLFunction("ascii_code", Hibernate.STRING));
+ registerFunction("unicode_char", new StandardSQLFunction("unicode_char", Hibernate.LONG));
+ registerFunction("unicode_code", new StandardSQLFunction("unicode_code", Hibernate.STRING));
+ registerFunction("upper", new StandardSQLFunction("upper") );
+ registerFunction("lower", new StandardSQLFunction("lower") );
+ registerFunction("char_length", new StandardSQLFunction("char_length", Hibernate.LONG) );
+ registerFunction("bit_length", new StandardSQLFunction("bit_length", Hibernate.STRING));
+
+ getDefaultProperties().setProperty(Environment.USE_STREAMS_FOR_BINARY, "true");
+ getDefaultProperties().setProperty(Environment.STATEMENT_BATCH_SIZE, "50");
+ }
+
+ /**
+ * The syntax used to add a column to a table
+ */
+ public String getAddColumnString() {
+ return "add column";
+ }
+
+ /**
+ * We do not have to drop constraints before we drop the table
+ */
+ public boolean dropConstraints() {
+ return false;
+ }
+
+ /**
+ * TODO: Check if Mimer SQL cannot handle the way DB2 does
+ */
+ public boolean supportsIdentityColumns() {
+ return false;
+ }
+
+ /**
+ * Mimer SQL supports sequences
+ * @return boolean
+ */
+ public boolean supportsSequences() {
+ return true;
+ }
+
+ /**
+ * The syntax used to get the next value of a sequence in Mimer SQL
+ */
+ public String getSequenceNextValString(String sequenceName) {
+ return "select next_value of " + sequenceName + " from system.onerow";
+ }
+
+ /**
+ * The syntax used to create a sequence. Since we presume the sequences will be used as keys,
+ * we make them unique.
+ */
+ public String getCreateSequenceString(String sequenceName) {
+ return "create unique sequence " + sequenceName;
+ }
+
+ /**
+ * The syntax used to drop sequences
+ */
+ public String getDropSequenceString(String sequenceName) {
+ return "drop sequence " + sequenceName + " restrict";
+ }
+
+ /**
+ * Mimer SQL does not support limit
+ */
+ public boolean supportsLimit() {
+ return false;
+ }
+
+ /**
+ * The syntax for using cascade on constraints
+ */
+ public String getCascadeConstraintsString() {
+ return " cascade";
+ }
+
+ /**
+ * The syntax for fetching all sequnces avialable in the current schema.
+ */
+ public String getQuerySequencesString() {
+ return "select sequence_schema || '.' || sequence_name from information_schema.ext_sequences";
+ }
+
+ /**
+ * Does the <tt>FOR UPDATE OF</tt> syntax specify particular
+ * columns?
+ */
+ public boolean forUpdateOfColumns() {
+ return false;
+ }
+
+ /**
+ * Support the FOR UPDATE syntax? For now, returns false since
+ * the current version of the Mimer SQL JDBC Driver does not support
+ * updatable resultsets. Otherwise, Mimer SQL actually supports the for update syntax.
+ * @return boolean
+ */
+ public boolean supportsForUpdate() {
+ return false;
+ }
+
+
+ /**
+ * For now, simply return false since we don't updatable result sets.
+ */
+ public boolean supportsOuterJoinForUpdate() {
+ return false;
+ }
+}
+
+
+
+
+
+
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/MimerSQLDialect.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/MySQL5Dialect.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/MySQL5Dialect.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/MySQL5Dialect.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,16 @@
+package org.hibernate.dialect;
+
+import java.sql.Types;
+
+/**
+ * An SQL dialect for MySQL 5.x specific features.
+ *
+ * @author Steve Ebersole
+ */
+public class MySQL5Dialect extends MySQLDialect {
+ protected void registerVarcharTypes() {
+ registerColumnType( Types.VARCHAR, "longtext" );
+ registerColumnType( Types.VARCHAR, 16777215, "mediumtext" );
+ registerColumnType( Types.VARCHAR, 65535, "varchar($l)" );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/MySQL5InnoDBDialect.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/MySQL5InnoDBDialect.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/MySQL5InnoDBDialect.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,21 @@
+//$Id: $
+package org.hibernate.dialect;
+
+/**
+ * @author Gavin King, Scott Marlow
+ */
+public class MySQL5InnoDBDialect extends MySQL5Dialect {
+
+ public boolean supportsCascadeDelete() {
+ return true;
+ }
+
+ public String getTableTypeString() {
+ return " ENGINE=InnoDB";
+ }
+
+ public boolean hasSelfReferentialForeignKeyBug() {
+ return true;
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/MySQL5InnoDBDialect.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/MySQLDialect.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/MySQLDialect.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/MySQLDialect.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,350 @@
+//$Id: MySQLDialect.java 10963 2006-12-08 16:06:14Z steve.ebersole at jboss.com $
+package org.hibernate.dialect;
+
+import java.sql.CallableStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+
+import org.hibernate.Hibernate;
+import org.hibernate.cfg.Environment;
+import org.hibernate.dialect.function.NoArgSQLFunction;
+import org.hibernate.dialect.function.StandardSQLFunction;
+import org.hibernate.util.StringHelper;
+
+/**
+ * An SQL dialect for MySQL (prior to 5.x).
+ *
+ * @author Gavin King
+ */
+public class MySQLDialect extends Dialect {
+
+ public MySQLDialect() {
+ super();
+ registerColumnType( Types.BIT, "bit" );
+ registerColumnType( Types.BIGINT, "bigint" );
+ registerColumnType( Types.SMALLINT, "smallint" );
+ registerColumnType( Types.TINYINT, "tinyint" );
+ registerColumnType( Types.INTEGER, "integer" );
+ registerColumnType( Types.CHAR, "char(1)" );
+ registerColumnType( Types.FLOAT, "float" );
+ registerColumnType( Types.DOUBLE, "double precision" );
+ registerColumnType( Types.DATE, "date" );
+ registerColumnType( Types.TIME, "time" );
+ registerColumnType( Types.TIMESTAMP, "datetime" );
+ registerColumnType( Types.VARBINARY, "longblob" );
+ registerColumnType( Types.VARBINARY, 16777215, "mediumblob" );
+ registerColumnType( Types.VARBINARY, 65535, "blob" );
+ registerColumnType( Types.VARBINARY, 255, "tinyblob" );
+ registerColumnType( Types.NUMERIC, "numeric($p,$s)" );
+ registerColumnType( Types.BLOB, "longblob" );
+ registerColumnType( Types.BLOB, 16777215, "mediumblob" );
+ registerColumnType( Types.BLOB, 65535, "blob" );
+ registerColumnType( Types.CLOB, "longtext" );
+ registerColumnType( Types.CLOB, 16777215, "mediumtext" );
+ registerColumnType( Types.CLOB, 65535, "text" );
+ registerVarcharTypes();
+
+ registerFunction("ascii", new StandardSQLFunction("ascii", Hibernate.INTEGER) );
+ registerFunction("bin", new StandardSQLFunction("bin", Hibernate.STRING) );
+ registerFunction("char_length", new StandardSQLFunction("char_length", Hibernate.LONG) );
+ registerFunction("character_length", new StandardSQLFunction("character_length", Hibernate.LONG) );
+ registerFunction("lcase", new StandardSQLFunction("lcase") );
+ registerFunction("lower", new StandardSQLFunction("lower") );
+ registerFunction("length", new StandardSQLFunction("length", Hibernate.LONG) );
+ registerFunction("ltrim", new StandardSQLFunction("ltrim") );
+ registerFunction("ord", new StandardSQLFunction("ord", Hibernate.INTEGER) );
+ registerFunction("quote", new StandardSQLFunction("quote") );
+ registerFunction("reverse", new StandardSQLFunction("reverse") );
+ registerFunction("rtrim", new StandardSQLFunction("rtrim") );
+ registerFunction("soundex", new StandardSQLFunction("soundex") );
+ registerFunction("space", new StandardSQLFunction("space", Hibernate.STRING) );
+ registerFunction("ucase", new StandardSQLFunction("ucase") );
+ registerFunction("upper", new StandardSQLFunction("upper") );
+ registerFunction("unhex", new StandardSQLFunction("unhex", Hibernate.STRING) );
+
+ registerFunction("abs", new StandardSQLFunction("abs") );
+ registerFunction("sign", new StandardSQLFunction("sign", Hibernate.INTEGER) );
+
+ registerFunction("acos", new StandardSQLFunction("acos", Hibernate.DOUBLE) );
+ registerFunction("asin", new StandardSQLFunction("asin", Hibernate.DOUBLE) );
+ registerFunction("atan", new StandardSQLFunction("atan", Hibernate.DOUBLE) );
+ registerFunction("cos", new StandardSQLFunction("cos", Hibernate.DOUBLE) );
+ registerFunction("cot", new StandardSQLFunction("cot", Hibernate.DOUBLE) );
+ registerFunction("crc32", new StandardSQLFunction("crc32", Hibernate.LONG) );
+ registerFunction("exp", new StandardSQLFunction("exp", Hibernate.DOUBLE) );
+ registerFunction("ln", new StandardSQLFunction("ln", Hibernate.DOUBLE) );
+ registerFunction("log", new StandardSQLFunction("log", Hibernate.DOUBLE) );
+ registerFunction("log2", new StandardSQLFunction("log2", Hibernate.DOUBLE) );
+ registerFunction("log10", new StandardSQLFunction("log10", Hibernate.DOUBLE) );
+ registerFunction("pi", new NoArgSQLFunction("pi", Hibernate.DOUBLE) );
+ registerFunction("rand", new NoArgSQLFunction("rand", Hibernate.DOUBLE) );
+ registerFunction("sin", new StandardSQLFunction("sin", Hibernate.DOUBLE) );
+ registerFunction("sqrt", new StandardSQLFunction("sqrt", Hibernate.DOUBLE) );
+ registerFunction("tan", new StandardSQLFunction("tan", Hibernate.DOUBLE) );
+
+ registerFunction("radians", new StandardSQLFunction("radians", Hibernate.DOUBLE) );
+ registerFunction("degrees", new StandardSQLFunction("degrees", Hibernate.DOUBLE) );
+
+ registerFunction("ceiling", new StandardSQLFunction("ceiling", Hibernate.INTEGER) );
+ registerFunction("ceil", new StandardSQLFunction("ceil", Hibernate.INTEGER) );
+ registerFunction("floor", new StandardSQLFunction("floor", Hibernate.INTEGER) );
+ registerFunction("round", new StandardSQLFunction("round", Hibernate.INTEGER) );
+
+ registerFunction("datediff", new StandardSQLFunction("datediff", Hibernate.INTEGER) );
+ registerFunction("timediff", new StandardSQLFunction("timediff", Hibernate.TIME) );
+ registerFunction("date_format", new StandardSQLFunction("date_format", Hibernate.STRING) );
+
+ registerFunction("curdate", new NoArgSQLFunction("curdate", Hibernate.DATE) );
+ registerFunction("curtime", new NoArgSQLFunction("curtime", Hibernate.TIME) );
+ registerFunction("current_date", new NoArgSQLFunction("current_date", Hibernate.DATE, false) );
+ registerFunction("current_time", new NoArgSQLFunction("current_time", Hibernate.TIME, false) );
+ registerFunction("current_timestamp", new NoArgSQLFunction("current_timestamp", Hibernate.TIMESTAMP, false) );
+ registerFunction("date", new StandardSQLFunction("date", Hibernate.DATE) );
+ registerFunction("day", new StandardSQLFunction("day", Hibernate.INTEGER) );
+ registerFunction("dayofmonth", new StandardSQLFunction("dayofmonth", Hibernate.INTEGER) );
+ registerFunction("dayname", new StandardSQLFunction("dayname", Hibernate.STRING) );
+ registerFunction("dayofweek", new StandardSQLFunction("dayofweek", Hibernate.INTEGER) );
+ registerFunction("dayofyear", new StandardSQLFunction("dayofyear", Hibernate.INTEGER) );
+ registerFunction("from_days", new StandardSQLFunction("from_days", Hibernate.DATE) );
+ registerFunction("from_unixtime", new StandardSQLFunction("from_unixtime", Hibernate.TIMESTAMP) );
+ registerFunction("hour", new StandardSQLFunction("hour", Hibernate.INTEGER) );
+ registerFunction("last_day", new StandardSQLFunction("last_day", Hibernate.DATE) );
+ registerFunction("localtime", new NoArgSQLFunction("localtime", Hibernate.TIMESTAMP) );
+ registerFunction("localtimestamp", new NoArgSQLFunction("localtimestamp", Hibernate.TIMESTAMP) );
+ registerFunction("microseconds", new StandardSQLFunction("microseconds", Hibernate.INTEGER) );
+ registerFunction("minute", new StandardSQLFunction("minute", Hibernate.INTEGER) );
+ registerFunction("month", new StandardSQLFunction("month", Hibernate.INTEGER) );
+ registerFunction("monthname", new StandardSQLFunction("monthname", Hibernate.STRING) );
+ registerFunction("now", new NoArgSQLFunction("now", Hibernate.TIMESTAMP) );
+ registerFunction("quarter", new StandardSQLFunction("quarter", Hibernate.INTEGER) );
+ registerFunction("second", new StandardSQLFunction("second", Hibernate.INTEGER) );
+ registerFunction("sec_to_time", new StandardSQLFunction("sec_to_time", Hibernate.TIME) );
+ registerFunction("sysdate", new NoArgSQLFunction("sysdate", Hibernate.TIMESTAMP) );
+ registerFunction("time", new StandardSQLFunction("time", Hibernate.TIME) );
+ registerFunction("timestamp", new StandardSQLFunction("timestamp", Hibernate.TIMESTAMP) );
+ registerFunction("time_to_sec", new StandardSQLFunction("time_to_sec", Hibernate.INTEGER) );
+ registerFunction("to_days", new StandardSQLFunction("to_days", Hibernate.LONG) );
+ registerFunction("unix_timestamp", new StandardSQLFunction("unix_timestamp", Hibernate.LONG) );
+ registerFunction("utc_date", new NoArgSQLFunction("utc_date", Hibernate.STRING) );
+ registerFunction("utc_time", new NoArgSQLFunction("utc_time", Hibernate.STRING) );
+ registerFunction("utc_timestamp", new NoArgSQLFunction("utc_timestamp", Hibernate.STRING) );
+ registerFunction("week", new StandardSQLFunction("week", Hibernate.INTEGER) );
+ registerFunction("weekday", new StandardSQLFunction("weekday", Hibernate.INTEGER) );
+ registerFunction("weekofyear", new StandardSQLFunction("weekofyear", Hibernate.INTEGER) );
+ registerFunction("year", new StandardSQLFunction("year", Hibernate.INTEGER) );
+ registerFunction("yearweek", new StandardSQLFunction("yearweek", Hibernate.INTEGER) );
+
+ registerFunction("hex", new StandardSQLFunction("hex", Hibernate.STRING) );
+ registerFunction("oct", new StandardSQLFunction("oct", Hibernate.STRING) );
+
+ registerFunction("octet_length", new StandardSQLFunction("octet_length", Hibernate.LONG) );
+ registerFunction("bit_length", new StandardSQLFunction("bit_length", Hibernate.LONG) );
+
+ registerFunction("bit_count", new StandardSQLFunction("bit_count", Hibernate.LONG) );
+ registerFunction("encrypt", new StandardSQLFunction("encrypt", Hibernate.STRING) );
+ registerFunction("md5", new StandardSQLFunction("md5", Hibernate.STRING) );
+ registerFunction("sha1", new StandardSQLFunction("sha1", Hibernate.STRING) );
+ registerFunction("sha", new StandardSQLFunction("sha", Hibernate.STRING) );
+
+ registerFunction( "concat", new StandardSQLFunction( "concat", Hibernate.STRING ) );
+
+ getDefaultProperties().setProperty(Environment.MAX_FETCH_DEPTH, "2");
+ getDefaultProperties().setProperty(Environment.STATEMENT_BATCH_SIZE, DEFAULT_BATCH_SIZE);
+ }
+
+ protected void registerVarcharTypes() {
+ registerColumnType( Types.VARCHAR, "longtext" );
+ registerColumnType( Types.VARCHAR, 16777215, "mediumtext" );
+ registerColumnType( Types.VARCHAR, 65535, "text" );
+ registerColumnType( Types.VARCHAR, 255, "varchar($l)" );
+ }
+
+ public String getAddColumnString() {
+ return "add column";
+ }
+
+ public boolean qualifyIndexName() {
+ return false;
+ }
+
+ public boolean supportsIdentityColumns() {
+ return true;
+ }
+
+ public String getIdentitySelectString() {
+ return "select last_insert_id()";
+ }
+
+ public String getIdentityColumnString() {
+ return "not null auto_increment"; //starts with 1, implicitly
+ }
+
+ public String getAddForeignKeyConstraintString(
+ String constraintName,
+ String[] foreignKey,
+ String referencedTable,
+ String[] primaryKey, boolean referencesPrimaryKey
+ ) {
+ String cols = StringHelper.join(", ", foreignKey);
+ return new StringBuffer(30)
+ .append(" add index ")
+ .append(constraintName)
+ .append(" (")
+ .append(cols)
+ .append("), add constraint ")
+ .append(constraintName)
+ .append(" foreign key (")
+ .append(cols)
+ .append(") references ")
+ .append(referencedTable)
+ .append(" (")
+ .append( StringHelper.join(", ", primaryKey) )
+ .append(')')
+ .toString();
+ }
+
+ public boolean supportsLimit() {
+ return true;
+ }
+
+ public String getDropForeignKeyString() {
+ return " drop foreign key ";
+ }
+
+ public String getLimitString(String sql, boolean hasOffset) {
+ return new StringBuffer( sql.length()+20 )
+ .append(sql)
+ .append( hasOffset ? " limit ?, ?" : " limit ?")
+ .toString();
+ }
+
+ /*
+ * Temporary, until MySQL fix Connector/J bug
+ */
+ /*public String getLimitString(String sql, int offset, int limit) {
+ StringBuffer buf = new StringBuffer( sql.length()+20 )
+ .append(sql);
+ if (offset>0) {
+ buf.append(" limit ")
+ .append(offset)
+ .append(", ")
+ .append(limit);
+ }
+ else {
+ buf.append(" limit ")
+ .append(limit);
+ }
+ return buf.toString();
+ }*/
+
+ /*
+ * Temporary, until MySQL fix Connector/J bug
+ */
+ /*public boolean supportsVariableLimit() {
+ return false;
+ }*/
+
+ public char closeQuote() {
+ return '`';
+ }
+
+ public char openQuote() {
+ return '`';
+ }
+
+ public boolean supportsIfExistsBeforeTableName() {
+ return true;
+ }
+
+ public String getSelectGUIDString() {
+ return "select uuid()";
+ }
+
+ public boolean supportsCascadeDelete() {
+ return false;
+ }
+
+ public String getTableComment(String comment) {
+ return " comment='" + comment + "'";
+ }
+
+ public String getColumnComment(String comment) {
+ return " comment '" + comment + "'";
+ }
+
+ public boolean supportsTemporaryTables() {
+ return true;
+ }
+
+ public String getCreateTemporaryTableString() {
+ return "create temporary table if not exists";
+ }
+
+ public String getCastTypeName(int code) {
+ if ( code==Types.INTEGER ) {
+ return "signed";
+ }
+ else if ( code==Types.VARCHAR ) {
+ return "char";
+ }
+ else if ( code==Types.VARBINARY ) {
+ return "binary";
+ }
+ else {
+ return super.getCastTypeName( code );
+ }
+ }
+
+ public boolean supportsCurrentTimestampSelection() {
+ return true;
+ }
+
+ public boolean isCurrentTimestampSelectStringCallable() {
+ return false;
+ }
+
+ public String getCurrentTimestampSelectString() {
+ return "select now()";
+ }
+
+ public int registerResultSetOutParameter(CallableStatement statement, int col) throws SQLException {
+ return col;
+ }
+
+ public ResultSet getResultSet(CallableStatement ps) throws SQLException {
+ boolean isResultSet = ps.execute();
+ while (!isResultSet && ps.getUpdateCount() != -1) {
+ isResultSet = ps.getMoreResults();
+ }
+ return ps.getResultSet();
+ }
+
+ public boolean supportsRowValueConstructorSyntax() {
+ return true;
+ }
+
+ public Boolean performTemporaryTableDDLInIsolation() {
+ return Boolean.FALSE;
+ }
+
+
+ // Overridden informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public boolean supportsEmptyInList() {
+ return false;
+ }
+
+ public boolean areStringComparisonsCaseInsensitive() {
+ return true;
+ }
+
+ public boolean supportsLobValueChangePropogation() {
+ // note: at least my local MySQL 5.1 install shows this not working...
+ return false;
+ }
+
+ public boolean supportsSubqueryOnMutatingTable() {
+ return false;
+ }
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/MySQLInnoDBDialect.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/MySQLInnoDBDialect.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/MySQLInnoDBDialect.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,21 @@
+//$Id: MySQLInnoDBDialect.java 7118 2005-06-12 21:55:12Z oneovthafew $
+package org.hibernate.dialect;
+
+/**
+ * @author Gavin King
+ */
+public class MySQLInnoDBDialect extends MySQLDialect {
+
+ public boolean supportsCascadeDelete() {
+ return true;
+ }
+
+ public String getTableTypeString() {
+ return " type=InnoDB";
+ }
+
+ public boolean hasSelfReferentialForeignKeyBug() {
+ return true;
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/MySQLInnoDBDialect.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/MySQLMyISAMDialect.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/MySQLMyISAMDialect.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/MySQLMyISAMDialect.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,17 @@
+//$Id: MySQLMyISAMDialect.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate.dialect;
+
+/**
+ * @author Gavin King
+ */
+public class MySQLMyISAMDialect extends MySQLDialect {
+
+ public String getTableTypeString() {
+ return " type=MyISAM";
+ }
+
+ public boolean dropConstraints() {
+ return false;
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/MySQLMyISAMDialect.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/Oracle9Dialect.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/Oracle9Dialect.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/Oracle9Dialect.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,343 @@
+//$Id: Oracle9Dialect.java 11259 2007-03-07 22:55:12Z steve.ebersole at jboss.com $
+package org.hibernate.dialect;
+
+import java.sql.CallableStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.cfg.Environment;
+import org.hibernate.dialect.function.NoArgSQLFunction;
+import org.hibernate.dialect.function.NvlFunction;
+import org.hibernate.dialect.function.SQLFunctionTemplate;
+import org.hibernate.dialect.function.StandardSQLFunction;
+import org.hibernate.dialect.function.VarArgsSQLFunction;
+import org.hibernate.exception.JDBCExceptionHelper;
+import org.hibernate.exception.TemplatedViolatedConstraintNameExtracter;
+import org.hibernate.exception.ViolatedConstraintNameExtracter;
+import org.hibernate.util.ReflectHelper;
+
+/**
+ * An SQL dialect for Oracle 9 (uses ANSI-style syntax where possible).
+ * @author Gavin King, David Channon
+ */
+public class Oracle9Dialect extends Dialect {
+
+ public Oracle9Dialect() {
+ super();
+ registerColumnType( Types.BIT, "number(1,0)" );
+ registerColumnType( Types.BIGINT, "number(19,0)" );
+ registerColumnType( Types.SMALLINT, "number(5,0)" );
+ registerColumnType( Types.TINYINT, "number(3,0)" );
+ registerColumnType( Types.INTEGER, "number(10,0)" );
+ registerColumnType( Types.CHAR, "char(1 char)" );
+ registerColumnType( Types.VARCHAR, 4000, "varchar2($l char)" );
+ registerColumnType( Types.VARCHAR, "long" );
+ registerColumnType( Types.FLOAT, "float" );
+ registerColumnType( Types.DOUBLE, "double precision" );
+ registerColumnType( Types.DATE, "date" );
+ registerColumnType( Types.TIME, "date" );
+ registerColumnType( Types.TIMESTAMP, "timestamp" );
+ registerColumnType( Types.VARBINARY, 2000, "raw($l)" );
+ registerColumnType( Types.VARBINARY, "long raw" );
+ registerColumnType( Types.NUMERIC, "number($p,$s)" );
+ registerColumnType( Types.DECIMAL, "number($p,$s)" );
+ registerColumnType( Types.BLOB, "blob" );
+ registerColumnType( Types.CLOB, "clob" );
+
+ // Oracle driver reports to support getGeneratedKeys(), but they only
+ // support the version taking an array of the names of the columns to
+ // be returned (via its RETURNING clause). No other driver seems to
+ // support this overloaded version.
+ getDefaultProperties().setProperty(Environment.USE_GET_GENERATED_KEYS, "false");
+ getDefaultProperties().setProperty(Environment.USE_STREAMS_FOR_BINARY, "true");
+ getDefaultProperties().setProperty(Environment.STATEMENT_BATCH_SIZE, DEFAULT_BATCH_SIZE);
+
+ registerFunction( "abs", new StandardSQLFunction("abs") );
+ registerFunction( "sign", new StandardSQLFunction("sign", Hibernate.INTEGER) );
+
+ registerFunction( "acos", new StandardSQLFunction("acos", Hibernate.DOUBLE) );
+ registerFunction( "asin", new StandardSQLFunction("asin", Hibernate.DOUBLE) );
+ registerFunction( "atan", new StandardSQLFunction("atan", Hibernate.DOUBLE) );
+ registerFunction( "cos", new StandardSQLFunction("cos", Hibernate.DOUBLE) );
+ registerFunction( "cosh", new StandardSQLFunction("cosh", Hibernate.DOUBLE) );
+ registerFunction( "exp", new StandardSQLFunction("exp", Hibernate.DOUBLE) );
+ registerFunction( "ln", new StandardSQLFunction("ln", Hibernate.DOUBLE) );
+ registerFunction( "sin", new StandardSQLFunction("sin", Hibernate.DOUBLE) );
+ registerFunction( "sinh", new StandardSQLFunction("sinh", Hibernate.DOUBLE) );
+ registerFunction( "stddev", new StandardSQLFunction("stddev", Hibernate.DOUBLE) );
+ registerFunction( "sqrt", new StandardSQLFunction("sqrt", Hibernate.DOUBLE) );
+ registerFunction( "tan", new StandardSQLFunction("tan", Hibernate.DOUBLE) );
+ registerFunction( "tanh", new StandardSQLFunction("tanh", Hibernate.DOUBLE) );
+ registerFunction( "variance", new StandardSQLFunction("variance", Hibernate.DOUBLE) );
+
+ registerFunction( "round", new StandardSQLFunction("round") );
+ registerFunction( "trunc", new StandardSQLFunction("trunc") );
+ registerFunction( "ceil", new StandardSQLFunction("ceil") );
+ registerFunction( "floor", new StandardSQLFunction("floor") );
+
+ registerFunction( "chr", new StandardSQLFunction("chr", Hibernate.CHARACTER) );
+ registerFunction( "initcap", new StandardSQLFunction("initcap") );
+ registerFunction( "lower", new StandardSQLFunction("lower") );
+ registerFunction( "ltrim", new StandardSQLFunction("ltrim") );
+ registerFunction( "rtrim", new StandardSQLFunction("rtrim") );
+ registerFunction( "soundex", new StandardSQLFunction("soundex") );
+ registerFunction( "upper", new StandardSQLFunction("upper") );
+ registerFunction( "ascii", new StandardSQLFunction("ascii", Hibernate.INTEGER) );
+ registerFunction( "length", new StandardSQLFunction("length", Hibernate.LONG) );
+
+ registerFunction( "to_char", new StandardSQLFunction("to_char", Hibernate.STRING) );
+ registerFunction( "to_date", new StandardSQLFunction("to_date", Hibernate.TIMESTAMP) );
+
+ registerFunction( "current_date", new NoArgSQLFunction("current_date", Hibernate.DATE, false) );
+ registerFunction( "current_time", new NoArgSQLFunction("current_timestamp", Hibernate.TIME, false) );
+ registerFunction( "current_timestamp", new NoArgSQLFunction("current_timestamp", Hibernate.TIMESTAMP, false) );
+
+ registerFunction( "lastday", new StandardSQLFunction("lastday", Hibernate.DATE) );
+ registerFunction( "sysdate", new NoArgSQLFunction("sysdate", Hibernate.DATE, false) );
+ registerFunction( "systimestamp", new NoArgSQLFunction("systimestamp", Hibernate.TIMESTAMP, false) );
+ registerFunction( "uid", new NoArgSQLFunction("uid", Hibernate.INTEGER, false) );
+ registerFunction( "user", new NoArgSQLFunction("user", Hibernate.STRING, false) );
+
+ registerFunction( "rowid", new NoArgSQLFunction("rowid", Hibernate.LONG, false) );
+ registerFunction( "rownum", new NoArgSQLFunction("rownum", Hibernate.LONG, false) );
+
+ // Multi-param string dialect functions...
+ registerFunction( "concat", new VarArgsSQLFunction(Hibernate.STRING, "", "||", "") );
+ registerFunction( "instr", new StandardSQLFunction("instr", Hibernate.INTEGER) );
+ registerFunction( "instrb", new StandardSQLFunction("instrb", Hibernate.INTEGER) );
+ registerFunction( "lpad", new StandardSQLFunction("lpad", Hibernate.STRING) );
+ registerFunction( "replace", new StandardSQLFunction("replace", Hibernate.STRING) );
+ registerFunction( "rpad", new StandardSQLFunction("rpad", Hibernate.STRING) );
+ registerFunction( "substr", new StandardSQLFunction("substr", Hibernate.STRING) );
+ registerFunction( "substrb", new StandardSQLFunction("substrb", Hibernate.STRING) );
+ registerFunction( "translate", new StandardSQLFunction("translate", Hibernate.STRING) );
+
+ registerFunction( "substring", new StandardSQLFunction( "substr", Hibernate.STRING ) );
+ registerFunction( "locate", new SQLFunctionTemplate( Hibernate.INTEGER, "instr(?2,?1)" ) );
+ registerFunction( "bit_length", new SQLFunctionTemplate( Hibernate.INTEGER, "vsize(?1)*8" ) );
+ registerFunction( "coalesce", new NvlFunction() );
+
+ // Multi-param numeric dialect functions...
+ registerFunction( "atan2", new StandardSQLFunction("atan2", Hibernate.FLOAT) );
+ registerFunction( "log", new StandardSQLFunction("log", Hibernate.INTEGER) );
+ registerFunction( "mod", new StandardSQLFunction("mod", Hibernate.INTEGER) );
+ registerFunction( "nvl", new StandardSQLFunction("nvl") );
+ registerFunction( "nvl2", new StandardSQLFunction("nvl2") );
+ registerFunction( "power", new StandardSQLFunction("power", Hibernate.FLOAT) );
+
+ // Multi-param date dialect functions...
+ registerFunction( "add_months", new StandardSQLFunction("add_months", Hibernate.DATE) );
+ registerFunction( "months_between", new StandardSQLFunction("months_between", Hibernate.FLOAT) );
+ registerFunction( "next_day", new StandardSQLFunction("next_day", Hibernate.DATE) );
+
+ registerFunction( "str", new StandardSQLFunction("to_char", Hibernate.STRING) );
+ }
+
+ public String getAddColumnString() {
+ return "add";
+ }
+
+ public String getSequenceNextValString(String sequenceName) {
+ return "select " + getSelectSequenceNextValString( sequenceName ) + " from dual";
+ }
+
+ public String getSelectSequenceNextValString(String sequenceName) {
+ return sequenceName + ".nextval";
+ }
+
+ public String getCreateSequenceString(String sequenceName) {
+ return "create sequence " + sequenceName; //starts with 1, implicitly
+ }
+
+ public String getDropSequenceString(String sequenceName) {
+ return "drop sequence " + sequenceName;
+ }
+
+ public String getCascadeConstraintsString() {
+ return " cascade constraints";
+ }
+
+ public boolean dropConstraints() {
+ return false;
+ }
+
+ public String getForUpdateNowaitString() {
+ return " for update nowait";
+ }
+
+ public boolean supportsSequences() {
+ return true;
+ }
+
+ public boolean supportsPooledSequences() {
+ return true;
+ }
+
+ public boolean supportsLimit() {
+ return true;
+ }
+
+ public String getLimitString(String sql, boolean hasOffset) {
+
+ sql = sql.trim();
+ boolean isForUpdate = false;
+ if ( sql.toLowerCase().endsWith(" for update") ) {
+ sql = sql.substring( 0, sql.length()-11 );
+ isForUpdate = true;
+ }
+
+ StringBuffer pagingSelect = new StringBuffer( sql.length()+100 );
+ if (hasOffset) {
+ pagingSelect.append("select * from ( select row_.*, rownum rownum_ from ( ");
+ }
+ else {
+ pagingSelect.append("select * from ( ");
+ }
+ pagingSelect.append(sql);
+ if (hasOffset) {
+ pagingSelect.append(" ) row_ where rownum <= ?) where rownum_ > ?");
+ }
+ else {
+ pagingSelect.append(" ) where rownum <= ?");
+ }
+
+ if ( isForUpdate ) {
+ pagingSelect.append( " for update" );
+ }
+
+ return pagingSelect.toString();
+ }
+
+ public String getForUpdateString(String aliases) {
+ return getForUpdateString() + " of " + aliases;
+ }
+
+ public String getForUpdateNowaitString(String aliases) {
+ return getForUpdateString() + " of " + aliases + " nowait";
+ }
+
+ public boolean bindLimitParametersInReverseOrder() {
+ return true;
+ }
+
+ public boolean useMaxForLimit() {
+ return true;
+ }
+
+ public boolean forUpdateOfColumns() {
+ return true;
+ }
+
+ public String getQuerySequencesString() {
+ return "select sequence_name from user_sequences";
+ }
+
+ public String getSelectGUIDString() {
+ return "select rawtohex(sys_guid()) from dual";
+ }
+
+ public ViolatedConstraintNameExtracter getViolatedConstraintNameExtracter() {
+ return EXTRACTER;
+ }
+
+ private static ViolatedConstraintNameExtracter EXTRACTER = new TemplatedViolatedConstraintNameExtracter() {
+
+ /**
+ * Extract the name of the violated constraint from the given SQLException.
+ *
+ * @param sqle The exception that was the result of the constraint violation.
+ * @return The extracted constraint name.
+ */
+ public String extractConstraintName(SQLException sqle) {
+ int errorCode = JDBCExceptionHelper.extractErrorCode(sqle);
+ if ( errorCode == 1 || errorCode == 2291 || errorCode == 2292 ) {
+ return extractUsingTemplate( "constraint (", ") violated", sqle.getMessage() );
+ }
+ else if ( errorCode == 1400 ) {
+ // simple nullability constraint
+ return null;
+ }
+ else {
+ return null;
+ }
+ }
+
+ };
+
+ // not final-static to avoid possible classcast exceptions if using different oracle drivers.
+ int oracletypes_cursor_value = 0;
+ public int registerResultSetOutParameter(java.sql.CallableStatement statement,int col) throws SQLException {
+ if(oracletypes_cursor_value==0) {
+ try {
+ Class types = ReflectHelper.classForName("oracle.jdbc.driver.OracleTypes");
+ oracletypes_cursor_value = types.getField("CURSOR").getInt(types.newInstance());
+ } catch (Exception se) {
+ throw new HibernateException("Problem while trying to load or access OracleTypes.CURSOR value",se);
+ }
+ }
+ // register the type of the out param - an Oracle specific type
+ statement.registerOutParameter(col, oracletypes_cursor_value);
+ col++;
+ return col;
+ }
+
+ public ResultSet getResultSet(CallableStatement ps) throws SQLException {
+ ps.execute();
+ return ( ResultSet ) ps.getObject( 1 );
+ }
+
+ public boolean supportsUnionAll() {
+ return true;
+ }
+
+ public boolean supportsCommentOn() {
+ return true;
+ }
+
+ public boolean supportsTemporaryTables() {
+ return true;
+ }
+
+ public String generateTemporaryTableName(String baseTableName) {
+ String name = super.generateTemporaryTableName(baseTableName);
+ return name.length() > 30 ? name.substring( 1, 30 ) : name;
+ }
+
+ public String getCreateTemporaryTableString() {
+ return "create global temporary table";
+ }
+
+ public String getCreateTemporaryTablePostfix() {
+ return "on commit delete rows";
+ }
+
+ public boolean dropTemporaryTableAfterUse() {
+ return false;
+ }
+
+ public boolean supportsCurrentTimestampSelection() {
+ return true;
+ }
+
+ public String getCurrentTimestampSelectString() {
+ return "select systimestamp from dual";
+ }
+
+ public boolean isCurrentTimestampSelectStringCallable() {
+ return false;
+ }
+
+
+ // Overridden informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public boolean supportsEmptyInList() {
+ return false;
+ }
+
+ public boolean supportsExistsInSelect() {
+ return false;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/OracleDialect.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/OracleDialect.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/OracleDialect.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,85 @@
+//$Id: OracleDialect.java 8610 2005-11-18 18:30:27Z steveebersole $
+package org.hibernate.dialect;
+
+import java.sql.Types;
+
+import org.hibernate.sql.CaseFragment;
+import org.hibernate.sql.DecodeCaseFragment;
+import org.hibernate.sql.JoinFragment;
+import org.hibernate.sql.OracleJoinFragment;
+
+/**
+ * An SQL dialect for Oracle, compatible with Oracle 8.
+ * @author Gavin King
+ */
+public class OracleDialect extends Oracle9Dialect {
+
+ public OracleDialect() {
+ super();
+ // Oracle8 and previous define only a "DATE" type which
+ // is used to represent all aspects of date/time
+ registerColumnType( Types.TIMESTAMP, "date" );
+ registerColumnType( Types.CHAR, "char(1)" );
+ registerColumnType( Types.VARCHAR, 4000, "varchar2($l)" );
+ }
+
+ public JoinFragment createOuterJoinFragment() {
+ return new OracleJoinFragment();
+ }
+ public CaseFragment createCaseFragment() {
+ return new DecodeCaseFragment();
+ }
+
+ public String getLimitString(String sql, boolean hasOffset) {
+
+ sql = sql.trim();
+ boolean isForUpdate = false;
+ if ( sql.toLowerCase().endsWith(" for update") ) {
+ sql = sql.substring( 0, sql.length()-11 );
+ isForUpdate = true;
+ }
+
+ StringBuffer pagingSelect = new StringBuffer( sql.length()+100 );
+ if (hasOffset) {
+ pagingSelect.append("select * from ( select row_.*, rownum rownum_ from ( ");
+ }
+ else {
+ pagingSelect.append("select * from ( ");
+ }
+ pagingSelect.append(sql);
+ if (hasOffset) {
+ pagingSelect.append(" ) row_ ) where rownum_ <= ? and rownum_ > ?");
+ }
+ else {
+ pagingSelect.append(" ) where rownum <= ?");
+ }
+
+ if ( isForUpdate ) {
+ pagingSelect.append( " for update" );
+ }
+
+ return pagingSelect.toString();
+ }
+
+ public String getSelectClauseNullString(int sqlType) {
+ switch(sqlType) {
+ case Types.VARCHAR:
+ case Types.CHAR:
+ return "to_char(null)";
+ case Types.DATE:
+ case Types.TIMESTAMP:
+ case Types.TIME:
+ return "to_date(null)";
+ default:
+ return "to_number(null)";
+ }
+ }
+
+ public String getCurrentTimestampSelectString() {
+ return "select sysdate from dual";
+ }
+
+ public String getCurrentTimestampSQLFunctionName() {
+ return "sysdate";
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/PointbaseDialect.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/PointbaseDialect.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/PointbaseDialect.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,69 @@
+//$Id: PointbaseDialect.java 9328 2006-02-23 17:32:47Z steveebersole $
+//Created on 04 February 2002, 17:35
+package org.hibernate.dialect;
+
+import org.hibernate.dialect.lock.LockingStrategy;
+import org.hibernate.dialect.lock.UpdateLockingStrategy;
+import org.hibernate.dialect.lock.SelectLockingStrategy;
+import org.hibernate.persister.entity.Lockable;
+import org.hibernate.LockMode;
+
+import java.sql.Types;
+
+/**
+ * A <tt>Dialect</tt> for Pointbase.
+ * @author Ed Mackenzie
+ */
+public class PointbaseDialect extends org.hibernate.dialect.Dialect {
+
+ /**
+ * Creates new PointbaseDialect
+ */
+ public PointbaseDialect() {
+ super();
+ registerColumnType( Types.BIT, "smallint" ); //no pointbase BIT
+ registerColumnType( Types.BIGINT, "bigint" );
+ registerColumnType( Types.SMALLINT, "smallint" );
+ registerColumnType( Types.TINYINT, "smallint" ); //no pointbase TINYINT
+ registerColumnType( Types.INTEGER, "integer" );
+ registerColumnType( Types.CHAR, "char(1)" );
+ registerColumnType( Types.VARCHAR, "varchar($l)" );
+ registerColumnType( Types.FLOAT, "float" );
+ registerColumnType( Types.DOUBLE, "double precision" );
+ registerColumnType( Types.DATE, "date" );
+ registerColumnType( Types.TIME, "time" );
+ registerColumnType( Types.TIMESTAMP, "timestamp" );
+ //the BLOB type requires a size arguement - this defaults to
+ //bytes - no arg defaults to 1 whole byte!
+ //other argument mods include K - kilobyte, M - megabyte, G - gigabyte.
+ //refer to the PBdevelopers guide for more info.
+ registerColumnType( Types.VARBINARY, "blob($l)" );
+ registerColumnType( Types.NUMERIC, "numeric($p,$s)" );
+ }
+
+ public String getAddColumnString() {
+ return "add";
+ }
+
+ public boolean dropConstraints() {
+ return false;
+ }
+
+ public String getCascadeConstraintsString() {
+ return " cascade";
+ }
+
+ public String getForUpdateString() {
+ return "";
+ }
+
+ public LockingStrategy getLockingStrategy(Lockable lockable, LockMode lockMode) {
+ // Pointbase has no known variation of a "SELECT ... FOR UPDATE" syntax...
+ if ( lockMode.greaterThan( LockMode.READ ) ) {
+ return new UpdateLockingStrategy( lockable, lockMode );
+ }
+ else {
+ return new SelectLockingStrategy( lockable, lockMode );
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,312 @@
+//$Id: PostgreSQLDialect.java 11367 2007-03-29 13:26:40Z steve.ebersole at jboss.com $
+package org.hibernate.dialect;
+
+import java.sql.Types;
+import java.sql.SQLException;
+
+import org.hibernate.Hibernate;
+import org.hibernate.exception.ViolatedConstraintNameExtracter;
+import org.hibernate.exception.TemplatedViolatedConstraintNameExtracter;
+import org.hibernate.exception.JDBCExceptionHelper;
+import org.hibernate.cfg.Environment;
+import org.hibernate.dialect.function.NoArgSQLFunction;
+import org.hibernate.dialect.function.PositionSubstringFunction;
+import org.hibernate.dialect.function.SQLFunctionTemplate;
+import org.hibernate.dialect.function.StandardSQLFunction;
+import org.hibernate.dialect.function.VarArgsSQLFunction;
+import org.hibernate.id.SequenceGenerator;
+
+/**
+ * An SQL dialect for Postgres
+ * @author Gavin King
+ */
+public class PostgreSQLDialect extends Dialect {
+
+ public PostgreSQLDialect() {
+ super();
+ registerColumnType( Types.BIT, "bool" );
+ registerColumnType( Types.BIGINT, "int8" );
+ registerColumnType( Types.SMALLINT, "int2" );
+ registerColumnType( Types.TINYINT, "int2" );
+ registerColumnType( Types.INTEGER, "int4" );
+ registerColumnType( Types.CHAR, "char(1)" );
+ registerColumnType( Types.VARCHAR, "varchar($l)" );
+ registerColumnType( Types.FLOAT, "float4" );
+ registerColumnType( Types.DOUBLE, "float8" );
+ registerColumnType( Types.DATE, "date" );
+ registerColumnType( Types.TIME, "time" );
+ registerColumnType( Types.TIMESTAMP, "timestamp" );
+ registerColumnType( Types.VARBINARY, "bytea" );
+ registerColumnType( Types.CLOB, "text" );
+ registerColumnType( Types.BLOB, "oid" );
+ registerColumnType( Types.NUMERIC, "numeric($p, $s)" );
+
+ registerFunction( "abs", new StandardSQLFunction("abs") );
+ registerFunction( "sign", new StandardSQLFunction("sign", Hibernate.INTEGER) );
+
+ registerFunction( "acos", new StandardSQLFunction("acos", Hibernate.DOUBLE) );
+ registerFunction( "asin", new StandardSQLFunction("asin", Hibernate.DOUBLE) );
+ registerFunction( "atan", new StandardSQLFunction("atan", Hibernate.DOUBLE) );
+ registerFunction( "cos", new StandardSQLFunction("cos", Hibernate.DOUBLE) );
+ registerFunction( "cot", new StandardSQLFunction("cot", Hibernate.DOUBLE) );
+ registerFunction( "exp", new StandardSQLFunction("exp", Hibernate.DOUBLE) );
+ registerFunction( "ln", new StandardSQLFunction("ln", Hibernate.DOUBLE) );
+ registerFunction( "log", new StandardSQLFunction("log", Hibernate.DOUBLE) );
+ registerFunction( "sin", new StandardSQLFunction("sin", Hibernate.DOUBLE) );
+ registerFunction( "sqrt", new StandardSQLFunction("sqrt", Hibernate.DOUBLE) );
+ registerFunction( "cbrt", new StandardSQLFunction("cbrt", Hibernate.DOUBLE) );
+ registerFunction( "tan", new StandardSQLFunction("tan", Hibernate.DOUBLE) );
+ registerFunction( "radians", new StandardSQLFunction("radians", Hibernate.DOUBLE) );
+ registerFunction( "degrees", new StandardSQLFunction("degrees", Hibernate.DOUBLE) );
+
+ registerFunction( "stddev", new StandardSQLFunction("stddev", Hibernate.DOUBLE) );
+ registerFunction( "variance", new StandardSQLFunction("variance", Hibernate.DOUBLE) );
+
+ registerFunction( "random", new NoArgSQLFunction("random", Hibernate.DOUBLE) );
+
+ registerFunction( "round", new StandardSQLFunction("round") );
+ registerFunction( "trunc", new StandardSQLFunction("trunc") );
+ registerFunction( "ceil", new StandardSQLFunction("ceil") );
+ registerFunction( "floor", new StandardSQLFunction("floor") );
+
+ registerFunction( "chr", new StandardSQLFunction("chr", Hibernate.CHARACTER) );
+ registerFunction( "lower", new StandardSQLFunction("lower") );
+ registerFunction( "upper", new StandardSQLFunction("upper") );
+ registerFunction( "substr", new StandardSQLFunction("substr", Hibernate.STRING) );
+ registerFunction( "initcap", new StandardSQLFunction("initcap") );
+ registerFunction( "to_ascii", new StandardSQLFunction("to_ascii") );
+ registerFunction( "quote_ident", new StandardSQLFunction("quote_ident", Hibernate.STRING) );
+ registerFunction( "quote_literal", new StandardSQLFunction("quote_literal", Hibernate.STRING) );
+ registerFunction( "md5", new StandardSQLFunction("md5") );
+ registerFunction( "ascii", new StandardSQLFunction("ascii", Hibernate.INTEGER) );
+ registerFunction( "length", new StandardSQLFunction("length", Hibernate.LONG) );
+ registerFunction( "char_length", new StandardSQLFunction("char_length", Hibernate.LONG) );
+ registerFunction( "bit_length", new StandardSQLFunction("bit_length", Hibernate.LONG) );
+ registerFunction( "octet_length", new StandardSQLFunction("octet_length", Hibernate.LONG) );
+
+ registerFunction( "current_date", new NoArgSQLFunction("current_date", Hibernate.DATE, false) );
+ registerFunction( "current_time", new NoArgSQLFunction("current_time", Hibernate.TIME, false) );
+ registerFunction( "current_timestamp", new NoArgSQLFunction("current_timestamp", Hibernate.TIMESTAMP, false) );
+ registerFunction( "localtime", new NoArgSQLFunction("localtime", Hibernate.TIME, false) );
+ registerFunction( "localtimestamp", new NoArgSQLFunction("localtimestamp", Hibernate.TIMESTAMP, false) );
+ registerFunction( "now", new NoArgSQLFunction("now", Hibernate.TIMESTAMP) );
+ registerFunction( "timeofday", new NoArgSQLFunction("timeofday", Hibernate.STRING) );
+ registerFunction( "age", new StandardSQLFunction("age") );
+
+ registerFunction( "current_user", new NoArgSQLFunction("current_user", Hibernate.STRING, false) );
+ registerFunction( "session_user", new NoArgSQLFunction("session_user", Hibernate.STRING, false) );
+ registerFunction( "user", new NoArgSQLFunction("user", Hibernate.STRING, false) );
+ registerFunction( "current_database", new NoArgSQLFunction("current_database", Hibernate.STRING, true) );
+ registerFunction( "current_schema", new NoArgSQLFunction("current_schema", Hibernate.STRING, true) );
+
+ registerFunction( "to_char", new StandardSQLFunction("to_char", Hibernate.STRING) );
+ registerFunction( "to_date", new StandardSQLFunction("to_date", Hibernate.DATE) );
+ registerFunction( "to_timestamp", new StandardSQLFunction("to_timestamp", Hibernate.TIMESTAMP) );
+ registerFunction( "to_number", new StandardSQLFunction("to_number", Hibernate.BIG_DECIMAL) );
+
+ registerFunction( "concat", new VarArgsSQLFunction( Hibernate.STRING, "(","||",")" ) );
+
+ registerFunction( "locate", new PositionSubstringFunction() );
+
+ registerFunction( "str", new SQLFunctionTemplate(Hibernate.STRING, "cast(?1 as varchar)") );
+
+ getDefaultProperties().setProperty(Environment.STATEMENT_BATCH_SIZE, DEFAULT_BATCH_SIZE);
+ }
+
+ public String getAddColumnString() {
+ return "add column";
+ }
+
+ public String getSequenceNextValString(String sequenceName) {
+ return "select " + getSelectSequenceNextValString( sequenceName );
+ }
+
+ public String getSelectSequenceNextValString(String sequenceName) {
+ return "nextval ('" + sequenceName + "')";
+ }
+
+ public String getCreateSequenceString(String sequenceName) {
+ return "create sequence " + sequenceName; //starts with 1, implicitly
+ }
+
+ public String getDropSequenceString(String sequenceName) {
+ return "drop sequence " + sequenceName;
+ }
+
+ public String getCascadeConstraintsString() {
+ return "";//" cascade";
+ }
+ public boolean dropConstraints() {
+ return true;
+ }
+
+ public boolean supportsSequences() {
+ return true;
+ }
+
+ public String getQuerySequencesString() {
+ return "select relname from pg_class where relkind='S'";
+ }
+
+ public boolean supportsLimit() {
+ return true;
+ }
+
+ public String getLimitString(String sql, boolean hasOffset) {
+ return new StringBuffer( sql.length()+20 )
+ .append(sql)
+ .append(hasOffset ? " limit ? offset ?" : " limit ?")
+ .toString();
+ }
+
+ public boolean bindLimitParametersInReverseOrder() {
+ return true;
+ }
+
+ public boolean supportsIdentityColumns() {
+ return true;
+ }
+
+ public String getForUpdateString(String aliases) {
+ return getForUpdateString() + " of " + aliases;
+ }
+
+ public String getIdentitySelectString(String table, String column, int type) {
+ return new StringBuffer().append("select currval('")
+ .append(table)
+ .append('_')
+ .append(column)
+ .append("_seq')")
+ .toString();
+ }
+
+ public String getIdentityColumnString(int type) {
+ return type==Types.BIGINT ?
+ "bigserial not null" :
+ "serial not null";
+ }
+
+ public boolean hasDataTypeInIdentityColumn() {
+ return false;
+ }
+
+ public String getNoColumnsInsertString() {
+ return "default values";
+ }
+
+ public Class getNativeIdentifierGeneratorClass() {
+ return SequenceGenerator.class;
+ }
+
+ public boolean supportsOuterJoinForUpdate() {
+ return false;
+ }
+
+ public boolean useInputStreamToInsertBlob() {
+ return false;
+ }
+
+ public boolean supportsUnionAll() {
+ return true;
+ }
+
+ /**
+ * Workaround for postgres bug #1453
+ */
+ public String getSelectClauseNullString(int sqlType) {
+ String typeName = getTypeName(sqlType, 1, 1, 0);
+ //trim off the length/precision/scale
+ int loc = typeName.indexOf('(');
+ if (loc>-1) {
+ typeName = typeName.substring(0, loc);
+ }
+ return "null::" + typeName;
+ }
+
+ public boolean supportsCommentOn() {
+ return true;
+ }
+
+ public boolean supportsTemporaryTables() {
+ return true;
+ }
+
+ public String getCreateTemporaryTableString() {
+ return "create local temporary table";
+ }
+
+ public String getCreateTemporaryTablePostfix() {
+ return "on commit drop";
+ }
+
+ /*public boolean dropTemporaryTableAfterUse() {
+ //we have to, because postgres sets current tx
+ //to rollback only after a failed create table
+ return true;
+ }*/
+
+ public boolean supportsCurrentTimestampSelection() {
+ return true;
+ }
+
+ public boolean isCurrentTimestampSelectStringCallable() {
+ return false;
+ }
+
+ public String getCurrentTimestampSelectString() {
+ return "select now()";
+ }
+
+ public String toBooleanValueString(boolean bool) {
+ return bool ? "true" : "false";
+ }
+
+ public ViolatedConstraintNameExtracter getViolatedConstraintNameExtracter() {
+ return EXTRACTER;
+ }
+
+ /**
+ * Constraint-name extractor for Postgres contraint violation exceptions.
+ * Orginally contributed by Denny Bartelt.
+ */
+ private static ViolatedConstraintNameExtracter EXTRACTER = new TemplatedViolatedConstraintNameExtracter() {
+ public String extractConstraintName(SQLException sqle) {
+ try {
+ int sqlState = Integer.valueOf( JDBCExceptionHelper.extractSqlState(sqle)).intValue();
+ switch (sqlState) {
+ // CHECK VIOLATION
+ case 23514: return extractUsingTemplate("violates check constraint \"","\"", sqle.getMessage());
+ // UNIQUE VIOLATION
+ case 23505: return extractUsingTemplate("violates unique constraint \"","\"", sqle.getMessage());
+ // FOREIGN KEY VIOLATION
+ case 23503: return extractUsingTemplate("violates foreign key constraint \"","\"", sqle.getMessage());
+ // NOT NULL VIOLATION
+ case 23502: return extractUsingTemplate("null value in column \"","\" violates not-null constraint", sqle.getMessage());
+ // TODO: RESTRICT VIOLATION
+ case 23001: return null;
+ // ALL OTHER
+ default: return null;
+ }
+ } catch (NumberFormatException nfe) {
+ return null;
+ }
+ }
+ };
+
+
+ // Overridden informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+// seems to not really...
+// public boolean supportsRowValueConstructorSyntax() {
+// return true;
+// }
+
+ public boolean supportsEmptyInList() {
+ return false;
+ }
+
+ public boolean supportsExpectedLobUsagePattern() {
+ // seems to have spotty LOB suppport
+ return false;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/ProgressDialect.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/ProgressDialect.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/ProgressDialect.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,60 @@
+//$Id: ProgressDialect.java 4609 2004-09-27 03:17:46Z oneovthafew $
+// contributed by Phillip Baird
+package org.hibernate.dialect;
+
+import java.sql.Types;
+
+/**
+ * An SQL dialect compatible with Progress 9.1C<br>
+ *<br>
+ * Connection Parameters required:
+ *<ul>
+ * <li>hibernate.dialect org.hibernate.sql.ProgressDialect
+ * <li>hibernate.driver com.progress.sql.jdbc.JdbcProgressDriver
+ * <li>hibernate.url jdbc:JdbcProgress:T:host:port:dbname;WorkArounds=536870912
+ * <li>hibernate.username username
+ * <li>hibernate.password password
+ *</ul>
+ * The WorkArounds parameter in the URL is required to avoid an error
+ * in the Progress 9.1C JDBC driver related to PreparedStatements.
+ * @author Phillip Baird
+ *
+ */
+public class ProgressDialect extends Dialect {
+ public ProgressDialect() {
+ super();
+ registerColumnType( Types.BIT, "bit" );
+ registerColumnType( Types.BIGINT, "numeric" );
+ registerColumnType( Types.SMALLINT, "smallint" );
+ registerColumnType( Types.TINYINT, "tinyint" );
+ registerColumnType( Types.INTEGER, "integer" );
+ registerColumnType( Types.CHAR, "character(1)" );
+ registerColumnType( Types.VARCHAR, "varchar($l)" );
+ registerColumnType( Types.FLOAT, "real" );
+ registerColumnType( Types.DOUBLE, "double precision" );
+ registerColumnType( Types.DATE, "date" );
+ registerColumnType( Types.TIME, "time" );
+ registerColumnType( Types.TIMESTAMP, "timestamp" );
+ registerColumnType( Types.VARBINARY, "varbinary($l)" );
+ registerColumnType( Types.NUMERIC, "numeric($p,$s)" );
+ }
+
+ public boolean hasAlterTable(){
+ return false;
+ }
+
+ public String getAddColumnString() {
+ return "add column";
+ }
+
+ public boolean qualifyIndexName() {
+ return false;
+ }
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/RDMSOS2200Dialect.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/RDMSOS2200Dialect.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/RDMSOS2200Dialect.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,322 @@
+/*
+ * Created on Aug 24, 2005
+ * This is the Hibernate dialect for the Unisys 2200 Relational Database (RDMS).
+ * This dialect was developed for use with Hibernate 3.0.5. Other versions may
+ * require modifications to the dialect.
+ *
+ * Version History:
+ * Also change the version displayed below in the constructor
+ * 1.1
+ * 1.0 2005-10-24 CDH - First dated version for use with CP 11
+ */
+package org.hibernate.dialect;
+
+import org.hibernate.dialect.function.NoArgSQLFunction;
+import org.hibernate.dialect.function.StandardSQLFunction;
+import org.hibernate.dialect.function.SQLFunctionTemplate;
+import org.hibernate.dialect.lock.LockingStrategy;
+import org.hibernate.dialect.lock.UpdateLockingStrategy;
+import org.hibernate.dialect.lock.SelectLockingStrategy;
+
+import java.sql.Types;
+import org.hibernate.Hibernate;
+import org.hibernate.LockMode;
+import org.hibernate.persister.entity.Lockable;
+import org.hibernate.sql.CaseFragment;
+import org.hibernate.sql.DecodeCaseFragment;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * @author Ploski and Hanson
+ */
+public class RDMSOS2200Dialect extends Dialect {
+ private static Log log = LogFactory.getLog(RDMSOS2200Dialect.class);
+
+ public RDMSOS2200Dialect() {
+ super();
+ // Display the dialect version.
+ log.info("RDMSOS2200Dialect version: 1.0");
+
+ /**
+ * This section registers RDMS Biult-in Functions (BIFs) with Hibernate.
+ * The first parameter is the 'register' function name with Hibernate.
+ * The second parameter is the defined RDMS SQL Function and it's
+ * characteristics. If StandardSQLFunction(...) is used, the RDMS BIF
+ * name and the return type (if any) is specified. If
+ * SQLFunctionTemplate(...) is used, the return type and a template
+ * string is provided, plus an optional hasParenthesesIfNoArgs flag.
+ */
+ registerFunction( "abs", new StandardSQLFunction("abs") );
+ registerFunction( "sign", new StandardSQLFunction("sign", Hibernate.INTEGER) );
+
+ registerFunction("ascii", new StandardSQLFunction("ascii", Hibernate.INTEGER) );
+ registerFunction("char_length", new StandardSQLFunction("char_length", Hibernate.INTEGER) );
+ registerFunction("character_length", new StandardSQLFunction("character_length", Hibernate.INTEGER) );
+ registerFunction("length", new StandardSQLFunction("length", Hibernate.INTEGER) );
+
+ // The RDMS concat() function only supports 2 parameters
+ registerFunction( "concat", new SQLFunctionTemplate(Hibernate.STRING, "concat(?1, ?2)") );
+ registerFunction( "instr", new StandardSQLFunction("instr", Hibernate.STRING) );
+ registerFunction( "lpad", new StandardSQLFunction("lpad", Hibernate.STRING) );
+ registerFunction( "replace", new StandardSQLFunction("replace", Hibernate.STRING) );
+ registerFunction( "rpad", new StandardSQLFunction("rpad", Hibernate.STRING) );
+ registerFunction( "substr", new StandardSQLFunction("substr", Hibernate.STRING) );
+
+ registerFunction("lcase", new StandardSQLFunction("lcase") );
+ registerFunction("lower", new StandardSQLFunction("lower") );
+ registerFunction("ltrim", new StandardSQLFunction("ltrim") );
+ registerFunction("reverse", new StandardSQLFunction("reverse") );
+ registerFunction("rtrim", new StandardSQLFunction("rtrim") );
+
+ // RDMS does not directly support the trim() function, we use rtrim() and ltrim()
+ registerFunction("trim", new SQLFunctionTemplate(Hibernate.INTEGER, "ltrim(rtrim(?1))" ) );
+ registerFunction("soundex", new StandardSQLFunction("soundex") );
+ registerFunction("space", new StandardSQLFunction("space", Hibernate.STRING) );
+ registerFunction("ucase", new StandardSQLFunction("ucase") );
+ registerFunction("upper", new StandardSQLFunction("upper") );
+
+ registerFunction("acos", new StandardSQLFunction("acos", Hibernate.DOUBLE) );
+ registerFunction("asin", new StandardSQLFunction("asin", Hibernate.DOUBLE) );
+ registerFunction("atan", new StandardSQLFunction("atan", Hibernate.DOUBLE) );
+ registerFunction("cos", new StandardSQLFunction("cos", Hibernate.DOUBLE) );
+ registerFunction("cosh", new StandardSQLFunction("cosh", Hibernate.DOUBLE) );
+ registerFunction("cot", new StandardSQLFunction("cot", Hibernate.DOUBLE) );
+ registerFunction("exp", new StandardSQLFunction("exp", Hibernate.DOUBLE) );
+ registerFunction("ln", new StandardSQLFunction("ln", Hibernate.DOUBLE) );
+ registerFunction("log", new StandardSQLFunction("log", Hibernate.DOUBLE) );
+ registerFunction("log10", new StandardSQLFunction("log10", Hibernate.DOUBLE) );
+ registerFunction("pi", new NoArgSQLFunction("pi", Hibernate.DOUBLE) );
+ registerFunction("rand", new NoArgSQLFunction("rand", Hibernate.DOUBLE) );
+ registerFunction("sin", new StandardSQLFunction("sin", Hibernate.DOUBLE) );
+ registerFunction("sinh", new StandardSQLFunction("sinh", Hibernate.DOUBLE) );
+ registerFunction("sqrt", new StandardSQLFunction("sqrt", Hibernate.DOUBLE) );
+ registerFunction("tan", new StandardSQLFunction("tan", Hibernate.DOUBLE) );
+ registerFunction("tanh", new StandardSQLFunction("tanh", Hibernate.DOUBLE) );
+
+ registerFunction( "round", new StandardSQLFunction("round") );
+ registerFunction( "trunc", new StandardSQLFunction("trunc") );
+ registerFunction( "ceil", new StandardSQLFunction("ceil") );
+ registerFunction( "floor", new StandardSQLFunction("floor") );
+
+ registerFunction( "chr", new StandardSQLFunction("chr", Hibernate.CHARACTER) );
+ registerFunction( "initcap", new StandardSQLFunction("initcap") );
+
+ registerFunction( "user", new NoArgSQLFunction("user", Hibernate.STRING, false) );
+
+ registerFunction( "current_date", new NoArgSQLFunction("current_date", Hibernate.DATE, false) );
+ registerFunction( "current_time", new NoArgSQLFunction("current_timestamp", Hibernate.TIME, false) );
+ registerFunction( "current_timestamp", new NoArgSQLFunction("current_timestamp", Hibernate.TIMESTAMP, false) );
+ registerFunction("curdate", new NoArgSQLFunction("curdate",Hibernate.DATE) );
+ registerFunction("curtime", new NoArgSQLFunction("curtime",Hibernate.TIME) );
+ registerFunction("days", new StandardSQLFunction("days",Hibernate.INTEGER) );
+ registerFunction("dayofmonth", new StandardSQLFunction("dayofmonth",Hibernate.INTEGER) );
+ registerFunction("dayname", new StandardSQLFunction("dayname",Hibernate.STRING) );
+ registerFunction("dayofweek", new StandardSQLFunction("dayofweek",Hibernate.INTEGER) );
+ registerFunction("dayofyear", new StandardSQLFunction("dayofyear",Hibernate.INTEGER) );
+ registerFunction("hour", new StandardSQLFunction("hour",Hibernate.INTEGER) );
+ registerFunction("last_day", new StandardSQLFunction("last_day",Hibernate.DATE) );
+ registerFunction("microsecond", new StandardSQLFunction("microsecond",Hibernate.INTEGER) );
+ registerFunction("minute", new StandardSQLFunction("minute",Hibernate.INTEGER) );
+ registerFunction("month", new StandardSQLFunction("month",Hibernate.INTEGER) );
+ registerFunction("monthname", new StandardSQLFunction("monthname",Hibernate.STRING) );
+ registerFunction("now", new NoArgSQLFunction("now",Hibernate.TIMESTAMP) );
+ registerFunction("quarter", new StandardSQLFunction("quarter",Hibernate.INTEGER) );
+ registerFunction("second", new StandardSQLFunction("second",Hibernate.INTEGER) );
+ registerFunction("time", new StandardSQLFunction("time",Hibernate.TIME) );
+ registerFunction("timestamp", new StandardSQLFunction("timestamp",Hibernate.TIMESTAMP) );
+ registerFunction("week", new StandardSQLFunction("week",Hibernate.INTEGER) );
+ registerFunction("year", new StandardSQLFunction("year",Hibernate.INTEGER) );
+
+ registerFunction("atan2", new StandardSQLFunction("atan2",Hibernate.DOUBLE) );
+ registerFunction( "mod", new StandardSQLFunction("mod",Hibernate.INTEGER) );
+ registerFunction( "nvl", new StandardSQLFunction("nvl") );
+ registerFunction( "power", new StandardSQLFunction("power", Hibernate.DOUBLE) );
+
+ /**
+ * For a list of column types to register, see section A-1
+ * in 7862 7395, the Unisys JDBC manual.
+ *
+ * Here are column sizes as documented in Table A-1 of
+ * 7831 0760, "Enterprise Relational Database Server
+ * for ClearPath OS2200 Administration Guide"
+ * Numeric - 21
+ * Decimal - 22 (21 digits plus one for sign)
+ * Float - 60 bits
+ * Char - 28000
+ * NChar - 14000
+ * BLOB+ - 4294967296 (4 Gb)
+ * + RDMS JDBC driver does not support BLOBs
+ *
+ * DATE, TIME and TIMESTAMP literal formats are
+ * are all described in section 2.3.4 DATE Literal Format
+ * in 7830 8160.
+ * The DATE literal format is: YYYY-MM-DD
+ * The TIME literal format is: HH:MM:SS[.[FFFFFF]]
+ * The TIMESTAMP literal format is: YYYY-MM-DD HH:MM:SS[.[FFFFFF]]
+ *
+ * Note that $l (dollar-L) will use the length value if provided.
+ * Also new for Hibernate3 is the $p percision and $s (scale) parameters
+ */
+ registerColumnType(Types.BIT, "SMALLINT");
+ registerColumnType(Types.TINYINT, "SMALLINT");
+ registerColumnType(Types.BIGINT, "NUMERIC(21,0)");
+ registerColumnType(Types.SMALLINT, "SMALLINT");
+ registerColumnType(Types.CHAR, "CHARACTER(1)");
+ registerColumnType(Types.DOUBLE, "DOUBLE PRECISION");
+ registerColumnType(Types.FLOAT, "FLOAT");
+ registerColumnType(Types.REAL, "REAL");
+ registerColumnType(Types.INTEGER, "INTEGER");
+ registerColumnType(Types.NUMERIC, "NUMERIC(21,$l)");
+ registerColumnType(Types.DECIMAL, "NUMERIC(21,$l)");
+ registerColumnType(Types.DATE, "DATE");
+ registerColumnType(Types.TIME, "TIME");
+ registerColumnType(Types.TIMESTAMP, "TIMESTAMP");
+ registerColumnType(Types.VARCHAR, "CHARACTER($l)");
+ registerColumnType(Types.BLOB, "BLOB($l)" );
+ /*
+ * The following types are not supported in RDMS/JDBC and therefore commented out.
+ * However, in some cases, mapping them to CHARACTER columns works
+ * for many applications, but does not work for all cases.
+ */
+ // registerColumnType(Types.VARBINARY, "CHARACTER($l)");
+ // registerColumnType(Types.BLOB, "CHARACTER($l)" ); // For use prior to CP 11.0
+ // registerColumnType(Types.CLOB, "CHARACTER($l)" );
+ }
+
+
+ // Dialect method overrides ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * RDMS does not support qualifing index names with the schema name.
+ */
+ public boolean qualifyIndexName() {
+ return false;
+ }
+
+ /**
+ * The RDMS DB supports the 'FOR UPDATE OF' clause. However, the RDMS-JDBC
+ * driver does not support this feature, so a false is return.
+ * The base dialect also returns a false, but we will leave this over-ride
+ * in to make sure it stays false.
+ */
+ public boolean forUpdateOfColumns() {
+ return false;
+ }
+
+ /**
+ * Since the RDMS-JDBC driver does not support for updates, this string is
+ * set to an empty string. Whenever, the driver does support this feature,
+ * the returned string should be " FOR UPDATE OF". Note that RDMS does not
+ * support the string 'FOR UPDATE' string.
+ */
+ public String getForUpdateString() {
+ return ""; // Original Dialect.java returns " for update";
+ }
+
+ /**
+ * RDMS does not support adding Unique constraints via create and alter table.
+ */
+ public boolean supportsUniqueConstraintInCreateAlterTable() {
+ return true;
+ }
+
+ // Verify the state of this new method in Hibernate 3.0 Dialect.java
+ /**
+ * RDMS does not support Cascade Deletes.
+ * Need to review this in the future when support is provided.
+ */
+ public boolean supportsCascadeDelete() {
+ return false; // Origial Dialect.java returns true;
+ }
+
+ /**
+ * Currently, RDMS-JDBC does not support ForUpdate.
+ * Need to review this in the future when support is provided.
+ */
+ public boolean supportsOuterJoinForUpdate() {
+ return false;
+ }
+
+ public String getAddColumnString() {
+ return "add";
+ }
+
+ public String getNullColumnString() {
+ // The keyword used to specify a nullable column.
+ return " null";
+ }
+
+ // *** Sequence methods - start. The RDMS dialect needs these
+
+ // methods to make it possible to use the Native Id generator
+
+ public boolean supportsSequences() {
+ return true;
+ }
+
+ public String getSequenceNextValString(String sequenceName) {
+ // The where clause was added to eliminate this statement from Brute Force Searches.
+ return "select permuted_id('NEXT',31) from rdms.rdms_dummy where key_col = 1 ";
+ }
+
+ public String getCreateSequenceString(String sequenceName) {
+ // We must return a valid RDMS/RSA command from this method to
+ // prevent RDMS/RSA from issuing *ERROR 400
+ return "";
+ }
+
+ public String getDropSequenceString(String sequenceName) {
+ // We must return a valid RDMS/RSA command from this method to
+ // prevent RDMS/RSA from issuing *ERROR 400
+ return "";
+ }
+
+ // *** Sequence methods - end
+
+ public String getCascadeConstraintsString() {
+ // Used with DROP TABLE to delete all records in the table.
+ return " including contents";
+ }
+
+ public CaseFragment createCaseFragment() {
+ return new DecodeCaseFragment();
+ }
+
+ public boolean supportsLimit() {
+ return true;
+ }
+
+ public boolean supportsLimitOffset() {
+ return false;
+ }
+
+ public String getLimitString(String sql, int offset, int limit) {
+ if (offset>0) throw new UnsupportedOperationException("RDMS does not support paged queries");
+ return new StringBuffer(sql.length() + 40)
+ .append(sql)
+ .append(" fetch first ")
+ .append(limit)
+ .append(" rows only ")
+ .toString();
+ }
+
+ public boolean supportsVariableLimit() {
+ return false;
+ }
+
+ public boolean supportsUnionAll() {
+ // RDMS supports the UNION ALL clause.
+ return true;
+ }
+
+ public LockingStrategy getLockingStrategy(Lockable lockable, LockMode lockMode) {
+ // RDMS has no known variation of a "SELECT ... FOR UPDATE" syntax...
+ if ( lockMode.greaterThan( LockMode.READ ) ) {
+ return new UpdateLockingStrategy( lockable, lockMode );
+ }
+ else {
+ return new SelectLockingStrategy( lockable, lockMode );
+ }
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/RDMSOS2200Dialect.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/ResultColumnReferenceStrategy.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/ResultColumnReferenceStrategy.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/ResultColumnReferenceStrategy.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,67 @@
+package org.hibernate.dialect;
+
+import java.io.Serializable;
+import java.io.ObjectStreamException;
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * Defines how we need to reference columns in the group-by, having, and order-by
+ * clauses.
+ *
+ * @author Steve Ebersole
+ */
+public class ResultColumnReferenceStrategy implements Serializable {
+
+ private static final Map INSTANCES = new HashMap();
+
+ /**
+ * This strategy says to reference the result columns by the qualified column name
+ * found in the result source. This strategy is not strictly allowed by ANSI SQL
+ * but is Hibernate's legacy behavior and is also the fastest of the strategies; thus
+ * it should be used if supported by the underlying database.
+ */
+ public static final ResultColumnReferenceStrategy SOURCE = new ResultColumnReferenceStrategy( "source");
+
+ /**
+ * For databases which do not support {@link #SOURCE}, ANSI SQL defines two allowable
+ * approaches. One is to reference the result column by the alias it is given in the
+ * result source (if it is given an alias). This strategy says to use this approach.
+ * <p/>
+ * The other QNSI SQL compliant approach is {@link #ORDINAL}.
+ */
+ public static final ResultColumnReferenceStrategy ALIAS = new ResultColumnReferenceStrategy( "alias" );
+
+ /**
+ * For databases which do not support {@link #SOURCE}, ANSI SQL defines two allowable
+ * approaches. One is to reference the result column by the ordinal position at which
+ * it appears in the result source. This strategy says to use this approach.
+ * <p/>
+ * The other QNSI SQL compliant approach is {@link #ALIAS}.
+ */
+ public static final ResultColumnReferenceStrategy ORDINAL = new ResultColumnReferenceStrategy( "ordinal" );
+
+ static {
+ ResultColumnReferenceStrategy.INSTANCES.put( ResultColumnReferenceStrategy.SOURCE.name, ResultColumnReferenceStrategy.SOURCE );
+ ResultColumnReferenceStrategy.INSTANCES.put( ResultColumnReferenceStrategy.ALIAS.name, ResultColumnReferenceStrategy.ALIAS );
+ ResultColumnReferenceStrategy.INSTANCES.put( ResultColumnReferenceStrategy.ORDINAL.name, ResultColumnReferenceStrategy.ORDINAL );
+ }
+
+ private final String name;
+
+ public ResultColumnReferenceStrategy(String name) {
+ this.name = name;
+ }
+
+ public String toString() {
+ return name;
+ }
+
+ private Object readResolve() throws ObjectStreamException {
+ return parse( name );
+ }
+
+ public static ResultColumnReferenceStrategy parse(String name) {
+ return ( ResultColumnReferenceStrategy ) ResultColumnReferenceStrategy.INSTANCES.get( name );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/SAPDBDialect.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/SAPDBDialect.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/SAPDBDialect.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,195 @@
+//$Id: SAPDBDialect.java 8749 2005-12-04 17:32:04Z oneovthafew $
+// contributed by Brad Clow
+package org.hibernate.dialect;
+
+import java.sql.Types;
+
+import org.hibernate.Hibernate;
+import org.hibernate.cfg.Environment;
+import org.hibernate.dialect.function.NoArgSQLFunction;
+import org.hibernate.dialect.function.StandardSQLFunction;
+import org.hibernate.dialect.function.VarArgsSQLFunction;
+import org.hibernate.sql.CaseFragment;
+import org.hibernate.sql.DecodeCaseFragment;
+import org.hibernate.sql.OracleJoinFragment;
+import org.hibernate.sql.JoinFragment;
+import org.hibernate.util.StringHelper;
+
+/**
+ * An SQL dialect compatible with SAP DB.
+ * @author Brad Clow
+ */
+public class SAPDBDialect extends Dialect {
+
+ public SAPDBDialect() {
+ super();
+ registerColumnType( Types.BIT, "boolean" );
+ registerColumnType( Types.BIGINT, "fixed(19,0)" );
+ registerColumnType( Types.SMALLINT, "smallint" );
+ registerColumnType( Types.TINYINT, "fixed(3,0)" );
+ registerColumnType( Types.INTEGER, "int" );
+ registerColumnType( Types.CHAR, "char(1)" );
+ registerColumnType( Types.VARCHAR, "varchar($l)" );
+ registerColumnType( Types.FLOAT, "float" );
+ registerColumnType( Types.DOUBLE, "double precision" );
+ registerColumnType( Types.DATE, "date" );
+ registerColumnType( Types.TIME, "time" );
+ registerColumnType( Types.TIMESTAMP, "timestamp" );
+ registerColumnType( Types.VARBINARY, "long byte" );
+ registerColumnType( Types.NUMERIC, "fixed($p,$s)" );
+ registerColumnType( Types.CLOB, "long varchar" );
+ registerColumnType( Types.BLOB, "long byte" );
+
+ registerFunction( "abs", new StandardSQLFunction("abs") );
+ registerFunction( "sign", new StandardSQLFunction("sign", Hibernate.INTEGER) );
+
+ registerFunction( "exp", new StandardSQLFunction("exp", Hibernate.DOUBLE) );
+ registerFunction( "ln", new StandardSQLFunction("ln", Hibernate.DOUBLE) );
+ registerFunction( "log", new StandardSQLFunction("ln", Hibernate.DOUBLE) );
+ registerFunction( "pi", new NoArgSQLFunction("pi", Hibernate.DOUBLE) );
+ registerFunction( "power", new StandardSQLFunction("power") );
+ registerFunction( "acos", new StandardSQLFunction("acos", Hibernate.DOUBLE) );
+ registerFunction( "asin", new StandardSQLFunction("asin", Hibernate.DOUBLE) );
+ registerFunction( "atan", new StandardSQLFunction("atan", Hibernate.DOUBLE) );
+ registerFunction( "cos", new StandardSQLFunction("cos", Hibernate.DOUBLE) );
+ registerFunction( "cosh", new StandardSQLFunction("cosh", Hibernate.DOUBLE) );
+ registerFunction( "cot", new StandardSQLFunction("cos", Hibernate.DOUBLE) );
+ registerFunction( "sin", new StandardSQLFunction("sin", Hibernate.DOUBLE) );
+ registerFunction( "sinh", new StandardSQLFunction("sinh", Hibernate.DOUBLE) );
+ registerFunction( "tan", new StandardSQLFunction("tan", Hibernate.DOUBLE) );
+ registerFunction( "tanh", new StandardSQLFunction("tanh", Hibernate.DOUBLE) );
+ registerFunction( "radians", new StandardSQLFunction("radians", Hibernate.DOUBLE) );
+ registerFunction( "degrees", new StandardSQLFunction("degrees", Hibernate.DOUBLE) );
+ registerFunction( "atan2", new StandardSQLFunction("atan2", Hibernate.DOUBLE) );
+
+ registerFunction( "round", new StandardSQLFunction("round") );
+ registerFunction( "trunc", new StandardSQLFunction("trunc") );
+ registerFunction( "ceil", new StandardSQLFunction("ceil") );
+ registerFunction( "floor", new StandardSQLFunction("floor") );
+ registerFunction( "greatest", new StandardSQLFunction("greatest") );
+ registerFunction( "least", new StandardSQLFunction("least") );
+
+ registerFunction("time", new StandardSQLFunction("time", Hibernate.TIME) );
+ registerFunction("timestamp", new StandardSQLFunction("timestamp", Hibernate.TIMESTAMP) );
+ registerFunction("date", new StandardSQLFunction("date", Hibernate.DATE) );
+ registerFunction("microsecond", new StandardSQLFunction("microsecond", Hibernate.INTEGER) );
+
+ registerFunction("dayname", new StandardSQLFunction("dayname", Hibernate.STRING) );
+ registerFunction("monthname", new StandardSQLFunction("monthname", Hibernate.STRING) );
+ registerFunction("dayofmonth", new StandardSQLFunction("dayofmonth", Hibernate.INTEGER) );
+ registerFunction("dayofweek", new StandardSQLFunction("dayofweek", Hibernate.INTEGER) );
+ registerFunction("dayofyear", new StandardSQLFunction("dayofyear", Hibernate.INTEGER) );
+ registerFunction("weekofyear", new StandardSQLFunction("weekofyear", Hibernate.INTEGER) );
+
+ registerFunction( "replace", new StandardSQLFunction("replace", Hibernate.STRING) );
+ registerFunction( "translate", new StandardSQLFunction("translate", Hibernate.STRING) );
+ registerFunction( "lpad", new StandardSQLFunction("lpad", Hibernate.STRING) );
+ registerFunction( "rpad", new StandardSQLFunction("rpad", Hibernate.STRING) );
+ registerFunction( "substr", new StandardSQLFunction("substr", Hibernate.STRING) );
+ registerFunction( "initcap", new StandardSQLFunction("initcap", Hibernate.STRING) );
+ registerFunction( "lower", new StandardSQLFunction("lower", Hibernate.STRING) );
+ registerFunction( "ltrim", new StandardSQLFunction("ltrim", Hibernate.STRING) );
+ registerFunction( "rtrim", new StandardSQLFunction("rtrim", Hibernate.STRING) );
+ registerFunction( "lfill", new StandardSQLFunction("ltrim", Hibernate.STRING) );
+ registerFunction( "rfill", new StandardSQLFunction("rtrim", Hibernate.STRING) );
+ registerFunction( "soundex", new StandardSQLFunction("soundex", Hibernate.STRING) );
+ registerFunction( "upper", new StandardSQLFunction("upper", Hibernate.STRING) );
+ registerFunction( "ascii", new StandardSQLFunction("ascii", Hibernate.STRING) );
+ registerFunction( "index", new StandardSQLFunction("index", Hibernate.INTEGER) );
+
+ registerFunction( "value", new StandardSQLFunction( "value" ) );
+
+ registerFunction( "concat", new VarArgsSQLFunction( Hibernate.STRING, "(", "||", ")" ) );
+ registerFunction( "substring", new StandardSQLFunction( "substr", Hibernate.STRING ) );
+ registerFunction( "locate", new StandardSQLFunction("index", Hibernate.INTEGER) );
+ registerFunction( "coalesce", new StandardSQLFunction( "value" ) );
+
+ getDefaultProperties().setProperty(Environment.STATEMENT_BATCH_SIZE, DEFAULT_BATCH_SIZE);
+
+ }
+
+ public boolean dropConstraints() {
+ return false;
+ }
+
+ public String getAddColumnString() {
+ return "add";
+ }
+
+ public String getAddForeignKeyConstraintString(
+ String constraintName,
+ String[] foreignKey,
+ String referencedTable,
+ String[] primaryKey, boolean referencesPrimaryKey
+ ) {
+ StringBuffer res = new StringBuffer(30)
+ .append(" foreign key ")
+ .append(constraintName)
+ .append(" (")
+ .append( StringHelper.join(", ", foreignKey) )
+ .append(") references ")
+ .append(referencedTable);
+
+ if(!referencesPrimaryKey) {
+ res.append(" (")
+ .append( StringHelper.join(", ", primaryKey) )
+ .append(')');
+ }
+
+ return res.toString();
+ }
+
+ public String getAddPrimaryKeyConstraintString(String constraintName) {
+ return " primary key ";
+ }
+
+ public String getNullColumnString() {
+ return " null";
+ }
+
+ public String getSequenceNextValString(String sequenceName) {
+ return "select " + getSelectSequenceNextValString( sequenceName ) + " from dual";
+ }
+
+ public String getSelectSequenceNextValString(String sequenceName) {
+ return sequenceName + ".nextval";
+ }
+
+ public String getCreateSequenceString(String sequenceName) {
+ return "create sequence " + sequenceName;
+ }
+
+ public String getDropSequenceString(String sequenceName) {
+ return "drop sequence " + sequenceName;
+ }
+
+ public String getQuerySequencesString() {
+ return "select sequence_name from domain.sequences";
+ }
+
+ public JoinFragment createOuterJoinFragment() {
+ return new OracleJoinFragment();
+ }
+
+
+ public boolean supportsSequences() {
+ return true;
+ }
+
+ public CaseFragment createCaseFragment() {
+ return new DecodeCaseFragment();
+ }
+
+ public boolean supportsTemporaryTables() {
+ return true;
+ }
+
+ public String getCreateTemporaryTablePostfix() {
+ return "ignore rollback";
+ }
+
+ public String generateTemporaryTableName(String baseTableName) {
+ return "temp." + super.generateTemporaryTableName(baseTableName);
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/SQLServerDialect.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/SQLServerDialect.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/SQLServerDialect.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,140 @@
+//$Id: SQLServerDialect.java 11303 2007-03-19 22:06:14Z steve.ebersole at jboss.com $
+package org.hibernate.dialect;
+
+import java.sql.Types;
+import java.util.Map;
+import java.util.Iterator;
+
+import org.hibernate.Hibernate;
+import org.hibernate.LockMode;
+import org.hibernate.util.StringHelper;
+import org.hibernate.dialect.function.SQLFunctionTemplate;
+import org.hibernate.dialect.function.StandardSQLFunction;
+import org.hibernate.dialect.function.AnsiTrimEmulationFunction;
+
+/**
+ * A dialect for Microsoft SQL Server 2000 and 2005
+ *
+ * @author Gavin King
+ */
+public class SQLServerDialect extends SybaseDialect {
+
+ public SQLServerDialect() {
+ registerColumnType( Types.VARBINARY, "image" );
+ registerColumnType( Types.VARBINARY, 8000, "varbinary($l)" );
+
+ registerFunction( "second", new SQLFunctionTemplate( Hibernate.INTEGER, "datepart(second, ?1)" ) );
+ registerFunction( "minute", new SQLFunctionTemplate( Hibernate.INTEGER, "datepart(minute, ?1)" ) );
+ registerFunction( "hour", new SQLFunctionTemplate( Hibernate.INTEGER, "datepart(hour, ?1)" ) );
+ registerFunction( "locate", new StandardSQLFunction( "charindex", Hibernate.INTEGER ) );
+
+ registerFunction( "extract", new SQLFunctionTemplate( Hibernate.INTEGER, "datepart(?1, ?3)" ) );
+ registerFunction( "mod", new SQLFunctionTemplate( Hibernate.INTEGER, "?1 % ?2" ) );
+ registerFunction( "bit_length", new SQLFunctionTemplate( Hibernate.INTEGER, "datalength(?1) * 8" ) );
+
+ registerFunction( "trim", new AnsiTrimEmulationFunction() );
+
+ registerKeyword( "top" );
+ }
+
+ public String getNoColumnsInsertString() {
+ return "default values";
+ }
+
+ static int getAfterSelectInsertPoint(String sql) {
+ int selectIndex = sql.toLowerCase().indexOf( "select" );
+ final int selectDistinctIndex = sql.toLowerCase().indexOf( "select distinct" );
+ return selectIndex + ( selectDistinctIndex == selectIndex ? 15 : 6 );
+ }
+
+ public String getLimitString(String querySelect, int offset, int limit) {
+ if ( offset > 0 ) {
+ throw new UnsupportedOperationException( "sql server has no offset" );
+ }
+ return new StringBuffer( querySelect.length() + 8 )
+ .append( querySelect )
+ .insert( getAfterSelectInsertPoint( querySelect ), " top " + limit )
+ .toString();
+ }
+
+ /**
+ * Use <tt>insert table(...) values(...) select SCOPE_IDENTITY()</tt>
+ */
+ public String appendIdentitySelectToInsert(String insertSQL) {
+ return insertSQL + " select scope_identity()";
+ }
+
+ public boolean supportsLimit() {
+ return true;
+ }
+
+ public boolean useMaxForLimit() {
+ return true;
+ }
+
+ public boolean supportsLimitOffset() {
+ return false;
+ }
+
+ public boolean supportsVariableLimit() {
+ return false;
+ }
+
+ public char closeQuote() {
+ return ']';
+ }
+
+ public char openQuote() {
+ return '[';
+ }
+
+ public String appendLockHint(LockMode mode, String tableName) {
+ if ( mode.greaterThan( LockMode.READ ) ) {
+ // does this need holdlock also? : return tableName + " with (updlock, rowlock, holdlock)";
+ return tableName + " with (updlock, rowlock)";
+ }
+ else {
+ return tableName;
+ }
+ }
+
+ public String getSelectGUIDString() {
+ return "select newid()";
+ }
+
+ // The current_timestamp is more accurate, but only known to be supported
+ // in SQL Server 7.0 and later (i.e., Sybase not known to support it at all)
+ public String getCurrentTimestampSelectString() {
+ return "select current_timestamp";
+ }
+
+ // Overridden informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public boolean areStringComparisonsCaseInsensitive() {
+ return true;
+ }
+
+ public boolean supportsResultSetPositionQueryMethodsOnForwardOnlyCursor() {
+ return false;
+ }
+
+ public boolean supportsCircularCascadeDeleteConstraints() {
+ // SQL Server (at least up through 2005) does not support defining
+ // cascade delete constraints which can circel back to the mutating
+ // table
+ return false;
+ }
+
+ public boolean supportsLobValueChangePropogation() {
+ // note: at least my local SQL Server 2005 Express shows this not working...
+ return false;
+ }
+
+ public boolean doesReadCommittedCauseWritersToBlockReaders() {
+ return false; // here assume SQLServer2005 using snapshot isolation, which does not have this problem
+ }
+
+ public boolean doesRepeatableReadCauseReadersToBlockWriters() {
+ return false; // here assume SQLServer2005 using snapshot isolation, which does not have this problem
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/Sybase11Dialect.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/Sybase11Dialect.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/Sybase11Dialect.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,20 @@
+//$Id: Sybase11Dialect.java 3890 2004-06-03 16:31:32Z steveebersole $
+package org.hibernate.dialect;
+
+import org.hibernate.sql.JoinFragment;
+import org.hibernate.sql.Sybase11JoinFragment;
+
+/**
+ * A SQL dialect suitable for use with Sybase 11.9.2 (specifically: avoids ANSI JOIN syntax)
+ * @author Colm O' Flaherty
+ */
+public class Sybase11Dialect extends SybaseDialect {
+ public Sybase11Dialect() {
+ super();
+ }
+
+ public JoinFragment createOuterJoinFragment() {
+ return new Sybase11JoinFragment();
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/SybaseAnywhereDialect.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/SybaseAnywhereDialect.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/SybaseAnywhereDialect.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,32 @@
+package org.hibernate.dialect;
+
+/**
+ * SQL Dialect for Sybase Anywhere
+ * extending Sybase (Enterprise) Dialect
+ * (Tested on ASA 8.x)
+ * @author ?
+ */
+public class SybaseAnywhereDialect extends SybaseDialect {
+
+ /**
+ * Sybase Anywhere syntax would require a "DEFAULT" for each column specified,
+ * but I suppose Hibernate use this syntax only with tables with just 1 column
+ */
+ public String getNoColumnsInsertString() {
+ return "values (default)";
+ }
+
+
+ /**
+ * ASA does not require to drop constraint before dropping tables, and DROP statement
+ * syntax used by Hibernate to drop constraint is not compatible with ASA, so disable it
+ */
+ public boolean dropConstraints() {
+ return false;
+ }
+
+ public boolean supportsInsertSelectIdentity() {
+ return false;
+ }
+
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/SybaseDialect.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/SybaseDialect.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/SybaseDialect.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,238 @@
+//$Id: SybaseDialect.java 11303 2007-03-19 22:06:14Z steve.ebersole at jboss.com $
+package org.hibernate.dialect;
+
+import java.sql.CallableStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.Map;
+import java.util.Iterator;
+
+import org.hibernate.Hibernate;
+import org.hibernate.LockMode;
+import org.hibernate.cfg.Environment;
+import org.hibernate.dialect.function.CharIndexFunction;
+import org.hibernate.dialect.function.NoArgSQLFunction;
+import org.hibernate.dialect.function.SQLFunctionTemplate;
+import org.hibernate.dialect.function.StandardSQLFunction;
+import org.hibernate.dialect.function.VarArgsSQLFunction;
+
+/**
+ * An SQL dialect compatible with Sybase and MS SQL Server.
+ * @author Gavin King
+ */
+
+public class SybaseDialect extends Dialect {
+ public SybaseDialect() {
+ super();
+ registerColumnType( Types.BIT, "tinyint" ); //Sybase BIT type does not support null values
+ registerColumnType( Types.BIGINT, "numeric(19,0)" );
+ registerColumnType( Types.SMALLINT, "smallint" );
+ registerColumnType( Types.TINYINT, "tinyint" );
+ registerColumnType( Types.INTEGER, "int" );
+ registerColumnType( Types.CHAR, "char(1)" );
+ registerColumnType( Types.VARCHAR, "varchar($l)" );
+ registerColumnType( Types.FLOAT, "float" );
+ registerColumnType( Types.DOUBLE, "double precision" );
+ registerColumnType( Types.DATE, "datetime" );
+ registerColumnType( Types.TIME, "datetime" );
+ registerColumnType( Types.TIMESTAMP, "datetime" );
+ registerColumnType( Types.VARBINARY, "varbinary($l)" );
+ registerColumnType( Types.NUMERIC, "numeric($p,$s)" );
+ registerColumnType( Types.BLOB, "image" );
+ registerColumnType( Types.CLOB, "text" );
+
+ registerFunction( "ascii", new StandardSQLFunction("ascii", Hibernate.INTEGER) );
+ registerFunction( "char", new StandardSQLFunction("char", Hibernate.CHARACTER) );
+ registerFunction( "len", new StandardSQLFunction("len", Hibernate.LONG) );
+ registerFunction( "lower", new StandardSQLFunction("lower") );
+ registerFunction( "upper", new StandardSQLFunction("upper") );
+ registerFunction( "str", new StandardSQLFunction("str", Hibernate.STRING) );
+ registerFunction( "ltrim", new StandardSQLFunction("ltrim") );
+ registerFunction( "rtrim", new StandardSQLFunction("rtrim") );
+ registerFunction( "reverse", new StandardSQLFunction("reverse") );
+ registerFunction( "space", new StandardSQLFunction("space", Hibernate.STRING) );
+
+ registerFunction( "user", new NoArgSQLFunction("user", Hibernate.STRING) );
+
+ registerFunction( "current_timestamp", new NoArgSQLFunction("getdate", Hibernate.TIMESTAMP) );
+ registerFunction( "current_time", new NoArgSQLFunction("getdate", Hibernate.TIME) );
+ registerFunction( "current_date", new NoArgSQLFunction("getdate", Hibernate.DATE) );
+
+ registerFunction( "getdate", new NoArgSQLFunction("getdate", Hibernate.TIMESTAMP) );
+ registerFunction( "getutcdate", new NoArgSQLFunction("getutcdate", Hibernate.TIMESTAMP) );
+ registerFunction( "day", new StandardSQLFunction("day", Hibernate.INTEGER) );
+ registerFunction( "month", new StandardSQLFunction("month", Hibernate.INTEGER) );
+ registerFunction( "year", new StandardSQLFunction("year", Hibernate.INTEGER) );
+ registerFunction( "datename", new StandardSQLFunction("datename", Hibernate.STRING) );
+
+ registerFunction( "abs", new StandardSQLFunction("abs") );
+ registerFunction( "sign", new StandardSQLFunction("sign", Hibernate.INTEGER) );
+
+ registerFunction( "acos", new StandardSQLFunction("acos", Hibernate.DOUBLE) );
+ registerFunction( "asin", new StandardSQLFunction("asin", Hibernate.DOUBLE) );
+ registerFunction( "atan", new StandardSQLFunction("atan", Hibernate.DOUBLE) );
+ registerFunction( "cos", new StandardSQLFunction("cos", Hibernate.DOUBLE) );
+ registerFunction( "cot", new StandardSQLFunction("cot", Hibernate.DOUBLE) );
+ registerFunction( "exp", new StandardSQLFunction("exp", Hibernate.DOUBLE) );
+ registerFunction( "log", new StandardSQLFunction( "log", Hibernate.DOUBLE) );
+ registerFunction( "log10", new StandardSQLFunction("log10", Hibernate.DOUBLE) );
+ registerFunction( "sin", new StandardSQLFunction("sin", Hibernate.DOUBLE) );
+ registerFunction( "sqrt", new StandardSQLFunction("sqrt", Hibernate.DOUBLE) );
+ registerFunction( "tan", new StandardSQLFunction("tan", Hibernate.DOUBLE) );
+ registerFunction( "pi", new NoArgSQLFunction("pi", Hibernate.DOUBLE) );
+ registerFunction( "square", new StandardSQLFunction("square") );
+ registerFunction( "rand", new StandardSQLFunction("rand", Hibernate.FLOAT) );
+
+ registerFunction("radians", new StandardSQLFunction("radians", Hibernate.DOUBLE) );
+ registerFunction("degrees", new StandardSQLFunction("degrees", Hibernate.DOUBLE) );
+
+ registerFunction( "round", new StandardSQLFunction("round") );
+ registerFunction( "ceiling", new StandardSQLFunction("ceiling") );
+ registerFunction( "floor", new StandardSQLFunction("floor") );
+
+ registerFunction( "isnull", new StandardSQLFunction("isnull") );
+
+ registerFunction( "concat", new VarArgsSQLFunction( Hibernate.STRING, "(","+",")" ) );
+
+ registerFunction( "length", new StandardSQLFunction( "len", Hibernate.INTEGER ) );
+ registerFunction( "trim", new SQLFunctionTemplate( Hibernate.STRING, "ltrim(rtrim(?1))") );
+ registerFunction( "locate", new CharIndexFunction() );
+
+ getDefaultProperties().setProperty(Environment.STATEMENT_BATCH_SIZE, NO_BATCH);
+ }
+
+ public String getAddColumnString() {
+ return "add";
+ }
+ public String getNullColumnString() {
+ return " null";
+ }
+ public boolean qualifyIndexName() {
+ return false;
+ }
+
+ public String getForUpdateString() {
+ return "";
+ }
+
+ public boolean supportsIdentityColumns() {
+ return true;
+ }
+ public String getIdentitySelectString() {
+ return "select @@identity";
+ }
+ public String getIdentityColumnString() {
+ return "identity not null"; //starts with 1, implicitly
+ }
+
+ public boolean supportsInsertSelectIdentity() {
+ return true;
+ }
+
+ public String appendIdentitySelectToInsert(String insertSQL) {
+ return insertSQL + "\nselect @@identity";
+ }
+
+ public String appendLockHint(LockMode mode, String tableName) {
+ if ( mode.greaterThan( LockMode.READ ) ) {
+ return tableName + " holdlock";
+ }
+ else {
+ return tableName;
+ }
+ }
+
+ public String applyLocksToSql(String sql, Map aliasedLockModes, Map keyColumnNames) {
+ Iterator itr = aliasedLockModes.entrySet().iterator();
+ StringBuffer buffer = new StringBuffer( sql );
+ int correction = 0;
+ while ( itr.hasNext() ) {
+ final Map.Entry entry = ( Map.Entry ) itr.next();
+ final LockMode lockMode = ( LockMode ) entry.getValue();
+ if ( lockMode.greaterThan( LockMode.READ ) ) {
+ final String alias = ( String ) entry.getKey();
+ int start = -1, end = -1;
+ if ( sql.endsWith( " " + alias ) ) {
+ start = ( sql.length() - alias.length() ) + correction;
+ end = start + alias.length();
+ }
+ else {
+ int position = sql.indexOf( " " + alias + " " );
+ if ( position <= -1 ) {
+ position = sql.indexOf( " " + alias + "," );
+ }
+ if ( position > -1 ) {
+ start = position + correction + 1;
+ end = start + alias.length();
+ }
+ }
+
+ if ( start > -1 ) {
+ final String lockHint = appendLockHint( lockMode, alias );
+ buffer.replace( start, end, lockHint );
+ correction += ( lockHint.length() - alias.length() );
+ }
+ }
+ }
+ return buffer.toString();
+ }
+
+ public int registerResultSetOutParameter(CallableStatement statement, int col) throws SQLException {
+ return col; // sql server just returns automatically
+ }
+
+ public ResultSet getResultSet(CallableStatement ps) throws SQLException {
+ boolean isResultSet = ps.execute();
+// This assumes you will want to ignore any update counts
+ while ( !isResultSet && ps.getUpdateCount() != -1 ) {
+ isResultSet = ps.getMoreResults();
+ }
+// You may still have other ResultSets or update counts left to process here
+// but you can't do it now or the ResultSet you just got will be closed
+ return ps.getResultSet();
+ }
+
+ public boolean supportsCurrentTimestampSelection() {
+ return true;
+ }
+
+ public boolean isCurrentTimestampSelectStringCallable() {
+ return false;
+ }
+
+ public String getCurrentTimestampSelectString() {
+ return "select getdate()";
+ }
+
+ public boolean supportsTemporaryTables() {
+ return true;
+ }
+
+ public String generateTemporaryTableName(String baseTableName) {
+ return "#" + baseTableName;
+ }
+
+ public boolean dropTemporaryTableAfterUse() {
+ return true; // sql-server, at least needed this dropped after use; strange!
+ }
+
+
+ // Overridden informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public boolean supportsEmptyInList() {
+ return false;
+ }
+
+ public boolean supportsExistsInSelect() {
+ return false;
+ }
+
+ public boolean doesReadCommittedCauseWritersToBlockReaders() {
+ return true;
+ }
+
+ public boolean doesRepeatableReadCauseReadersToBlockWriters() {
+ return true;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/TeradataDialect.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/TeradataDialect.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/TeradataDialect.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,237 @@
+package org.hibernate.dialect;
+
+import java.sql.Types;
+
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.cfg.Environment;
+import org.hibernate.dialect.function.SQLFunctionTemplate;
+import org.hibernate.dialect.function.VarArgsSQLFunction;
+
+/**
+ * A dialect for the Teradata database created by MCR as part of the
+ * dialect certification process.
+ *
+ * @author Jay Nance
+ */
+public class TeradataDialect extends Dialect {
+
+ /**
+ * Constructor
+ */
+ public TeradataDialect() {
+ super();
+ //registerColumnType data types
+ registerColumnType( Types.NUMERIC, "NUMERIC($p,$s)" );
+ registerColumnType( Types.DOUBLE, "DOUBLE PRECISION" );
+ registerColumnType( Types.BIGINT, "NUMERIC(18,0)" );
+ registerColumnType( Types.BIT, "BYTEINT" );
+ registerColumnType( Types.TINYINT, "BYTEINT" );
+ registerColumnType( Types.VARBINARY, "VARBYTE($l)" );
+ registerColumnType( Types.BINARY, "BYTEINT" );
+ registerColumnType( Types.LONGVARCHAR, "LONG VARCHAR" );
+ registerColumnType( Types.CHAR, "CHAR(1)" );
+ registerColumnType( Types.DECIMAL, "DECIMAL" );
+ registerColumnType( Types.INTEGER, "INTEGER" );
+ registerColumnType( Types.SMALLINT, "SMALLINT" );
+ registerColumnType( Types.FLOAT, "FLOAT" );
+ registerColumnType( Types.VARCHAR, "VARCHAR($l)" );
+ registerColumnType( Types.DATE, "DATE" );
+ registerColumnType( Types.TIME, "TIME" );
+ registerColumnType( Types.TIMESTAMP, "TIMESTAMP" );
+ registerColumnType( Types.BOOLEAN, "BYTEINT" ); // hibernate seems to ignore this type...
+ registerColumnType( Types.BLOB, "BLOB" );
+ registerColumnType( Types.CLOB, "CLOB" );
+
+ registerFunction( "year", new SQLFunctionTemplate( Hibernate.INTEGER, "extract(year from ?1)" ) );
+ registerFunction( "length", new SQLFunctionTemplate( Hibernate.INTEGER, "character_length(?1)" ) );
+ registerFunction( "concat", new VarArgsSQLFunction( Hibernate.STRING, "(", "||", ")" ) );
+ registerFunction( "substring", new SQLFunctionTemplate( Hibernate.STRING, "substring(?1 from ?2 for ?3)" ) );
+ registerFunction( "locate", new SQLFunctionTemplate( Hibernate.STRING, "position(?1 in ?2)" ) );
+ registerFunction( "mod", new SQLFunctionTemplate( Hibernate.STRING, "?1 mod ?2" ) );
+ registerFunction( "str", new SQLFunctionTemplate( Hibernate.STRING, "cast(?1 as varchar(255))" ) );
+
+ // bit_length feels a bit broken to me. We have to cast to char in order to
+ // pass when a numeric value is supplied. But of course the answers given will
+ // be wildly different for these two datatypes. 1234.5678 will be 9 bytes as
+ // a char string but will be 8 or 16 bytes as a true numeric.
+ // Jay Nance 2006-09-22
+ registerFunction(
+ "bit_length", new SQLFunctionTemplate( Hibernate.INTEGER, "octet_length(cast(?1 as char))*4" )
+ );
+
+ // The preference here would be
+ // SQLFunctionTemplate( Hibernate.TIMESTAMP, "current_timestamp(?1)", false)
+ // but this appears not to work.
+ // Jay Nance 2006-09-22
+ registerFunction( "current_timestamp", new SQLFunctionTemplate( Hibernate.TIMESTAMP, "current_timestamp" ) );
+ registerFunction( "current_time", new SQLFunctionTemplate( Hibernate.TIMESTAMP, "current_time" ) );
+ registerFunction( "current_date", new SQLFunctionTemplate( Hibernate.TIMESTAMP, "current_date" ) );
+ // IBID for current_time and current_date
+
+ registerKeyword( "password" );
+ registerKeyword( "type" );
+ registerKeyword( "title" );
+ registerKeyword( "year" );
+ registerKeyword( "month" );
+ registerKeyword( "summary" );
+ registerKeyword( "alias" );
+ registerKeyword( "value" );
+ registerKeyword( "first" );
+ registerKeyword( "role" );
+ registerKeyword( "account" );
+ registerKeyword( "class" );
+
+ // Tell hibernate to use getBytes instead of getBinaryStream
+ getDefaultProperties().setProperty( Environment.USE_STREAMS_FOR_BINARY, "false" );
+ // No batch statements
+ getDefaultProperties().setProperty( Environment.STATEMENT_BATCH_SIZE, NO_BATCH );
+ }
+
+ /**
+ * Does this dialect support the <tt>FOR UPDATE</tt> syntax?
+ *
+ * @return empty string ... Teradata does not support <tt>FOR UPDATE<tt> syntax
+ */
+ public String getForUpdateString() {
+ return "";
+ }
+
+ public boolean supportsIdentityColumns() {
+ return false;
+ }
+
+ public boolean supportsSequences() {
+ return false;
+ }
+
+ public String getAddColumnString() {
+ return "Add Column";
+ }
+
+ public boolean supportsTemporaryTables() {
+ return true;
+ }
+
+ public String getCreateTemporaryTableString() {
+ return "create global temporary table";
+ }
+
+ public String getCreateTemporaryTablePostfix() {
+ return " on commit preserve rows";
+ }
+
+ public Boolean performTemporaryTableDDLInIsolation() {
+ return Boolean.TRUE;
+ }
+
+ public boolean dropTemporaryTableAfterUse() {
+ return false;
+ }
+
+ /**
+ * Get the name of the database type associated with the given
+ * <tt>java.sql.Types</tt> typecode.
+ *
+ * @param code <tt>java.sql.Types</tt> typecode
+ * @param length the length or precision of the column
+ * @param precision the precision of the column
+ * @param scale the scale of the column
+ *
+ * @return the database type name
+ *
+ * @throws HibernateException
+ */
+ public String getTypeName(int code, int length, int precision, int scale) throws HibernateException {
+ /*
+ * We might want a special case for 19,2. This is very common for money types
+ * and here it is converted to 18,1
+ */
+ float f = precision > 0 ? ( float ) scale / ( float ) precision : 0;
+ int p = ( precision > 18 ? 18 : precision );
+ int s = ( precision > 18 ? ( int ) ( 18.0 * f ) : ( scale > 18 ? 18 : scale ) );
+
+ return super.getTypeName( code, length, p, s );
+ }
+
+ public boolean supportsCascadeDelete() {
+ return false;
+ }
+
+ public boolean supportsCircularCascadeDeleteConstraints() {
+ return false;
+ }
+
+ public boolean areStringComparisonsCaseInsensitive() {
+ return true;
+ }
+
+ public boolean supportsEmptyInList() {
+ return false;
+ }
+
+ public String getSelectClauseNullString(int sqlType) {
+ String v = "null";
+
+ switch ( sqlType ) {
+ case Types.BIT:
+ case Types.TINYINT:
+ case Types.SMALLINT:
+ case Types.INTEGER:
+ case Types.BIGINT:
+ case Types.FLOAT:
+ case Types.REAL:
+ case Types.DOUBLE:
+ case Types.NUMERIC:
+ case Types.DECIMAL:
+ v = "cast(null as decimal)";
+ break;
+ case Types.CHAR:
+ case Types.VARCHAR:
+ case Types.LONGVARCHAR:
+ v = "cast(null as varchar(255))";
+ break;
+ case Types.DATE:
+ case Types.TIME:
+ case Types.TIMESTAMP:
+ v = "cast(null as timestamp)";
+ break;
+ case Types.BINARY:
+ case Types.VARBINARY:
+ case Types.LONGVARBINARY:
+ case Types.NULL:
+ case Types.OTHER:
+ case Types.JAVA_OBJECT:
+ case Types.DISTINCT:
+ case Types.STRUCT:
+ case Types.ARRAY:
+ case Types.BLOB:
+ case Types.CLOB:
+ case Types.REF:
+ case Types.DATALINK:
+ case Types.BOOLEAN:
+ break;
+ }
+ return v;
+ }
+
+ public String getCreateMultisetTableString() {
+ return "create multiset table ";
+ }
+
+ public boolean supportsLobValueChangePropogation() {
+ return false;
+ }
+
+ public boolean doesReadCommittedCauseWritersToBlockReaders() {
+ return true;
+ }
+
+ public boolean doesRepeatableReadCauseReadersToBlockWriters() {
+ return true;
+ }
+
+ public boolean supportsBindAsCallableArgument() {
+ return false;
+ }
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/TimesTenDialect.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/TimesTenDialect.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/TimesTenDialect.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,205 @@
+package org.hibernate.dialect;
+
+import java.sql.Types;
+
+import org.hibernate.Hibernate;
+import org.hibernate.LockMode;
+import org.hibernate.persister.entity.Lockable;
+import org.hibernate.cfg.Environment;
+import org.hibernate.dialect.function.NoArgSQLFunction;
+import org.hibernate.dialect.function.StandardSQLFunction;
+import org.hibernate.dialect.lock.LockingStrategy;
+import org.hibernate.dialect.lock.UpdateLockingStrategy;
+import org.hibernate.dialect.lock.SelectLockingStrategy;
+import org.hibernate.sql.JoinFragment;
+import org.hibernate.sql.OracleJoinFragment;
+
+/**
+ * A SQL dialect for TimesTen 5.1.
+ *
+ * Known limitations:
+ * joined-subclass support because of no CASE support in TimesTen
+ * No support for subqueries that includes aggregation
+ * - size() in HQL not supported
+ * - user queries that does subqueries with aggregation
+ * No CLOB/BLOB support
+ * No cascade delete support.
+ * No Calendar support
+ * No support for updating primary keys.
+ *
+ * @author Sherry Listgarten and Max Andersen
+ */
+public class TimesTenDialect extends Dialect {
+
+ public TimesTenDialect() {
+ super();
+ registerColumnType( Types.BIT, "TINYINT" );
+ registerColumnType( Types.BIGINT, "BIGINT" );
+ registerColumnType( Types.SMALLINT, "SMALLINT" );
+ registerColumnType( Types.TINYINT, "TINYINT" );
+ registerColumnType( Types.INTEGER, "INTEGER" );
+ registerColumnType( Types.CHAR, "CHAR(1)" );
+ registerColumnType( Types.VARCHAR, "VARCHAR($l)" );
+ registerColumnType( Types.FLOAT, "FLOAT" );
+ registerColumnType( Types.DOUBLE, "DOUBLE" );
+ registerColumnType( Types.DATE, "DATE" );
+ registerColumnType( Types.TIME, "TIME" );
+ registerColumnType( Types.TIMESTAMP, "TIMESTAMP" );
+ registerColumnType( Types.VARBINARY, "VARBINARY($l)" );
+ registerColumnType( Types.NUMERIC, "DECIMAL($p, $s)" );
+ // TimesTen has no BLOB/CLOB support, but these types may be suitable
+ // for some applications. The length is limited to 4 million bytes.
+ registerColumnType( Types.BLOB, "VARBINARY(4000000)" );
+ registerColumnType( Types.CLOB, "VARCHAR(4000000)" );
+
+ getDefaultProperties().setProperty(Environment.USE_STREAMS_FOR_BINARY, "true");
+ getDefaultProperties().setProperty(Environment.STATEMENT_BATCH_SIZE, DEFAULT_BATCH_SIZE);
+ registerFunction( "lower", new StandardSQLFunction("lower") );
+ registerFunction( "upper", new StandardSQLFunction("upper") );
+ registerFunction( "rtrim", new StandardSQLFunction("rtrim") );
+ registerFunction( "concat", new StandardSQLFunction("concat", Hibernate.STRING) );
+ registerFunction( "mod", new StandardSQLFunction("mod") );
+ registerFunction( "to_char", new StandardSQLFunction("to_char",Hibernate.STRING) );
+ registerFunction( "to_date", new StandardSQLFunction("to_date",Hibernate.TIMESTAMP) );
+ registerFunction( "sysdate", new NoArgSQLFunction("sysdate", Hibernate.TIMESTAMP, false) );
+ registerFunction( "getdate", new NoArgSQLFunction("getdate", Hibernate.TIMESTAMP, false) );
+ registerFunction( "nvl", new StandardSQLFunction("nvl") );
+
+ }
+
+ public boolean dropConstraints() {
+ return true;
+ }
+
+ public boolean qualifyIndexName() {
+ return false;
+ }
+
+ public boolean supportsUnique() {
+ return false;
+ }
+
+ public boolean supportsUniqueConstraintInCreateAlterTable() {
+ return false;
+ }
+
+ public String getAddColumnString() {
+ return "add";
+ }
+
+ public boolean supportsSequences() {
+ return true;
+ }
+
+ public String getSelectSequenceNextValString(String sequenceName) {
+ return sequenceName + ".nextval";
+ }
+
+ public String getSequenceNextValString(String sequenceName) {
+ return "select first 1 " + sequenceName + ".nextval from sys.tables";
+ }
+
+ public String getCreateSequenceString(String sequenceName) {
+ return "create sequence " + sequenceName;
+ }
+
+ public String getDropSequenceString(String sequenceName) {
+ return "drop sequence " + sequenceName;
+ }
+
+ public String getQuerySequencesString() {
+ return "select NAME from sys.sequences";
+ }
+
+ public JoinFragment createOuterJoinFragment() {
+ return new OracleJoinFragment();
+ }
+
+ // new methods in dialect3
+ /*public boolean supportsForUpdateNowait() {
+ return false;
+ }*/
+
+ public String getForUpdateString() {
+ return "";
+ }
+
+ public boolean supportsColumnCheck() {
+ return false;
+ }
+
+ public boolean supportsTableCheck() {
+ return false;
+ }
+
+ public boolean supportsLimitOffset() {
+ return false;
+ }
+
+ public boolean supportsVariableLimit() {
+ return false;
+ }
+
+ public boolean supportsLimit() {
+ return true;
+ }
+
+ public boolean useMaxForLimit() {
+ return true;
+ }
+
+ public String getLimitString(String querySelect, int offset, int limit) {
+ if ( offset > 0 ) {
+ throw new UnsupportedOperationException( "TimesTen does not support offset" );
+ }
+ return new StringBuffer( querySelect.length()+8 )
+ .append(querySelect)
+ .insert( 6, " first " + limit )
+ .toString();
+ }
+
+ public boolean supportsCurrentTimestampSelection() {
+ return true;
+ }
+
+ public String getCurrentTimestampSelectString() {
+ return "select first 1 sysdate from sys.tables";
+ }
+
+ public boolean isCurrentTimestampSelectStringCallable() {
+ return false;
+ }
+
+ public boolean supportsTemporaryTables() {
+ return true;
+ }
+
+ public String generateTemporaryTableName(String baseTableName) {
+ String name = super.generateTemporaryTableName(baseTableName);
+ return name.length() > 30 ? name.substring( 1, 30 ) : name;
+ }
+
+ public String getCreateTemporaryTableString() {
+ return "create global temporary table";
+ }
+
+ public String getCreateTemporaryTablePostfix() {
+ return "on commit delete rows";
+ }
+
+ public LockingStrategy getLockingStrategy(Lockable lockable, LockMode lockMode) {
+ // TimesTen has no known variation of a "SELECT ... FOR UPDATE" syntax...
+ if ( lockMode.greaterThan( LockMode.READ ) ) {
+ return new UpdateLockingStrategy( lockable, lockMode );
+ }
+ else {
+ return new SelectLockingStrategy( lockable, lockMode );
+ }
+ }
+
+ // Overridden informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public boolean supportsEmptyInList() {
+ return false;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/TypeNames.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/TypeNames.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/TypeNames.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,116 @@
+//$Id: TypeNames.java 6254 2005-03-30 18:01:41Z oneovthafew $
+package org.hibernate.dialect;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.TreeMap;
+import java.util.Iterator;
+
+import org.hibernate.MappingException;
+import org.hibernate.util.StringHelper;
+
+/**
+ * This class maps a type to names. Associations
+ * may be marked with a capacity. Calling the get()
+ * method with a type and actual size n will return
+ * the associated name with smallest capacity >= n,
+ * if available and an unmarked default type otherwise.
+ * Eg, setting
+ * <pre>
+ * names.put(type, "TEXT" );
+ * names.put(type, 255, "VARCHAR($l)" );
+ * names.put(type, 65534, "LONGVARCHAR($l)" );
+ * </pre>
+ * will give you back the following:
+ * <pre>
+ * names.get(type) // --> "TEXT" (default)
+ * names.get(type, 100) // --> "VARCHAR(100)" (100 is in [0:255])
+ * names.get(type, 1000) // --> "LONGVARCHAR(1000)" (1000 is in [256:65534])
+ * names.get(type, 100000) // --> "TEXT" (default)
+ * </pre>
+ * On the other hand, simply putting
+ * <pre>
+ * names.put(type, "VARCHAR($l)" );
+ * </pre>
+ * would result in
+ * <pre>
+ * names.get(type) // --> "VARCHAR($l)" (will cause trouble)
+ * names.get(type, 100) // --> "VARCHAR(100)"
+ * names.get(type, 10000) // --> "VARCHAR(10000)"
+ * </pre>
+ *
+ * @author Christoph Beck
+ */
+public class TypeNames {
+
+ private HashMap weighted = new HashMap();
+ private HashMap defaults = new HashMap();
+
+ /**
+ * get default type name for specified type
+ * @param typecode the type key
+ * @return the default type name associated with specified key
+ */
+ public String get(int typecode) throws MappingException {
+ String result = (String) defaults.get( new Integer(typecode) );
+ if (result==null) throw new MappingException("No Dialect mapping for JDBC type: " + typecode);
+ return result;
+ }
+
+ /**
+ * get type name for specified type and size
+ * @param typecode the type key
+ * @param size the SQL length
+ * @param scale the SQL scale
+ * @param precision the SQL precision
+ * @return the associated name with smallest capacity >= size,
+ * if available and the default type name otherwise
+ */
+ public String get(int typecode, int size, int precision, int scale) throws MappingException {
+ Map map = (Map) weighted.get( new Integer(typecode) );
+ if ( map!=null && map.size()>0 ) {
+ // iterate entries ordered by capacity to find first fit
+ Iterator entries = map.entrySet().iterator();
+ while ( entries.hasNext() ) {
+ Map.Entry entry = (Map.Entry)entries.next();
+ if ( size <= ( (Integer) entry.getKey() ).intValue() ) {
+ return replace( (String) entry.getValue(), size, precision, scale );
+ }
+ }
+ }
+ return replace( get(typecode), size, precision, scale );
+ }
+
+ private static String replace(String type, int size, int precision, int scale) {
+ type = StringHelper.replaceOnce(type, "$s", Integer.toString(scale) );
+ type = StringHelper.replaceOnce(type, "$l", Integer.toString(size) );
+ return StringHelper.replaceOnce(type, "$p", Integer.toString(precision) );
+ }
+
+ /**
+ * set a type name for specified type key and capacity
+ * @param typecode the type key
+ */
+ public void put(int typecode, int capacity, String value) {
+ TreeMap map = (TreeMap)weighted.get( new Integer(typecode) );
+ if (map == null) {// add new ordered map
+ map = new TreeMap();
+ weighted.put( new Integer(typecode), map );
+ }
+ map.put(new Integer(capacity), value);
+ }
+
+ /**
+ * set a default type name for specified type key
+ * @param typecode the type key
+ */
+ public void put(int typecode, String value) {
+ defaults.put( new Integer(typecode), value );
+ }
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/AnsiTrimEmulationFunction.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/AnsiTrimEmulationFunction.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/AnsiTrimEmulationFunction.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,146 @@
+package org.hibernate.dialect.function;
+
+import org.hibernate.Hibernate;
+import org.hibernate.QueryException;
+import org.hibernate.engine.Mapping;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.type.Type;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * A {@link SQLFunction} implementation that emulates the ANSI SQL trim function
+ * on dialects which do not support the full definition. However, this function
+ * definition does assume the availability of ltrim, rtrim, and replace functions
+ * which it uses in various combinations to emulate the desired ANSI trim()
+ * functionality.
+ *
+ * @author Steve Ebersole
+ */
+public class AnsiTrimEmulationFunction implements SQLFunction {
+
+ private static final SQLFunction LEADING_SPACE_TRIM = new SQLFunctionTemplate( Hibernate.STRING, "ltrim( ?1 )");
+ private static final SQLFunction TRAILING_SPACE_TRIM = new SQLFunctionTemplate( Hibernate.STRING, "rtrim( ?1 )");
+ private static final SQLFunction BOTH_SPACE_TRIM = new SQLFunctionTemplate( Hibernate.STRING, "ltrim( rtrim( ?1 ) )");
+ private static final SQLFunction BOTH_SPACE_TRIM_FROM = new SQLFunctionTemplate( Hibernate.STRING, "ltrim( rtrim( ?2 ) )");
+
+ private static final SQLFunction LEADING_TRIM = new SQLFunctionTemplate( Hibernate.STRING, "replace( replace( rtrim( replace( replace( ?1, ' ', '${space}$' ), ?2, ' ' ) ), ' ', ?2 ), '${space}$', ' ' )" );
+ private static final SQLFunction TRAILING_TRIM = new SQLFunctionTemplate( Hibernate.STRING, "replace( replace( ltrim( replace( replace( ?1, ' ', '${space}$' ), ?2, ' ' ) ), ' ', ?2 ), '${space}$', ' ' )" );
+ private static final SQLFunction BOTH_TRIM = new SQLFunctionTemplate( Hibernate.STRING, "replace( replace( ltrim( rtrim( replace( replace( ?1, ' ', '${space}$' ), ?2, ' ' ) ) ), ' ', ?2 ), '${space}$', ' ' )" );
+
+ public Type getReturnType(Type columnType, Mapping mapping) throws QueryException {
+ return Hibernate.STRING;
+ }
+
+ public boolean hasArguments() {
+ return true;
+ }
+
+ public boolean hasParenthesesIfNoArguments() {
+ return false;
+ }
+
+ public String render(List args, SessionFactoryImplementor factory) throws QueryException {
+ // according to both the ANSI-SQL and EJB3 specs, trim can either take
+ // exactly one parameter or a variable number of parameters between 1 and 4.
+ // from the SQL spec:
+ //
+ // <trim function> ::=
+ // TRIM <left paren> <trim operands> <right paren>
+ //
+ // <trim operands> ::=
+ // [ [ <trim specification> ] [ <trim character> ] FROM ] <trim source>
+ //
+ // <trim specification> ::=
+ // LEADING
+ // | TRAILING
+ // | BOTH
+ //
+ // If only <trim specification> is omitted, BOTH is assumed;
+ // if <trim character> is omitted, space is assumed
+ if ( args.size() == 1 ) {
+ // we have the form: trim(trimSource)
+ // so we trim leading and trailing spaces
+ return BOTH_SPACE_TRIM.render( args, factory );
+ }
+ else if ( "from".equalsIgnoreCase( ( String ) args.get( 0 ) ) ) {
+ // we have the form: trim(from trimSource).
+ // This is functionally equivalent to trim(trimSource)
+ return BOTH_SPACE_TRIM_FROM.render( args, factory );
+ }
+ else {
+ // otherwise, a trim-specification and/or a trim-character
+ // have been specified; we need to decide which options
+ // are present and "do the right thing"
+ boolean leading = true; // should leading trim-characters be trimmed?
+ boolean trailing = true; // should trailing trim-characters be trimmed?
+ String trimCharacter = null; // the trim-character
+ String trimSource = null; // the trim-source
+
+ // potentialTrimCharacterArgIndex = 1 assumes that a
+ // trim-specification has been specified. we handle the
+ // exception to that explicitly
+ int potentialTrimCharacterArgIndex = 1;
+ String firstArg = ( String ) args.get( 0 );
+ if ( "leading".equalsIgnoreCase( firstArg ) ) {
+ trailing = false;
+ }
+ else if ( "trailing".equalsIgnoreCase( firstArg ) ) {
+ leading = false;
+ }
+ else if ( "both".equalsIgnoreCase( firstArg ) ) {
+ }
+ else {
+ potentialTrimCharacterArgIndex = 0;
+ }
+
+ String potentialTrimCharacter = ( String ) args.get( potentialTrimCharacterArgIndex );
+ if ( "from".equalsIgnoreCase( potentialTrimCharacter ) ) {
+ trimCharacter = "' '";
+ trimSource = ( String ) args.get( potentialTrimCharacterArgIndex + 1 );
+ }
+ else if ( potentialTrimCharacterArgIndex + 1 >= args.size() ) {
+ trimCharacter = "' '";
+ trimSource = potentialTrimCharacter;
+ }
+ else {
+ trimCharacter = potentialTrimCharacter;
+ if ( "from".equalsIgnoreCase( ( String ) args.get( potentialTrimCharacterArgIndex + 1 ) ) ) {
+ trimSource = ( String ) args.get( potentialTrimCharacterArgIndex + 2 );
+ }
+ else {
+ trimSource = ( String ) args.get( potentialTrimCharacterArgIndex + 1 );
+ }
+ }
+
+ List argsToUse = null;
+ argsToUse = new ArrayList();
+ argsToUse.add( trimSource );
+ argsToUse.add( trimCharacter );
+
+ if ( trimCharacter.equals( "' '" ) ) {
+ if ( leading && trailing ) {
+ return BOTH_SPACE_TRIM.render( argsToUse, factory );
+ }
+ else if ( leading ) {
+ return LEADING_SPACE_TRIM.render( argsToUse, factory );
+ }
+ else {
+ return TRAILING_SPACE_TRIM.render( argsToUse, factory );
+ }
+ }
+ else {
+ if ( leading && trailing ) {
+ return BOTH_TRIM.render( argsToUse, factory );
+ }
+ else if ( leading ) {
+ return LEADING_TRIM.render( argsToUse, factory );
+ }
+ else {
+ return TRAILING_TRIM.render( argsToUse, factory );
+ }
+ }
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/CastFunction.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/CastFunction.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/CastFunction.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,55 @@
+//$Id: CastFunction.java 7368 2005-07-04 02:54:27Z oneovthafew $
+package org.hibernate.dialect.function;
+
+import java.util.List;
+
+import org.hibernate.QueryException;
+import org.hibernate.engine.Mapping;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.type.Type;
+import org.hibernate.type.TypeFactory;
+
+/**
+ * ANSI-SQL style <tt>cast(foo as type)</tt> where the type is
+ * a Hibernate type
+ * @author Gavin King
+ */
+public class CastFunction implements SQLFunction {
+
+ public Type getReturnType(Type columnType, Mapping mapping) throws QueryException {
+ return columnType; //note there is a wierd implementation in the client side
+ }
+
+ public boolean hasArguments() {
+ return true;
+ }
+
+ public boolean hasParenthesesIfNoArguments() {
+ return true;
+ }
+
+ public String render(List args, SessionFactoryImplementor factory) throws QueryException {
+ if ( args.size()!=2 ) {
+ throw new QueryException("cast() requires two arguments");
+ }
+ String type = (String) args.get(1);
+ int[] sqlTypeCodes = TypeFactory.heuristicType(type).sqlTypes(factory);
+ if ( sqlTypeCodes.length!=1 ) {
+ throw new QueryException("invalid Hibernate type for cast()");
+ }
+ String sqlType = factory.getDialect().getCastTypeName( sqlTypeCodes[0] );
+ if (sqlType==null) {
+ //TODO: never reached, since getTypeName() actually throws an exception!
+ sqlType = type;
+ }
+ /*else {
+ //trim off the length/precision/scale
+ int loc = sqlType.indexOf('(');
+ if (loc>-1) {
+ sqlType = sqlType.substring(0, loc);
+ }
+ }*/
+ return "cast(" + args.get(0) + " as " + sqlType + ')';
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/CastFunction.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/CharIndexFunction.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/CharIndexFunction.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/CharIndexFunction.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,45 @@
+//$Id: CharIndexFunction.java 8470 2005-10-26 22:12:27Z oneovthafew $
+package org.hibernate.dialect.function;
+
+import java.util.List;
+
+import org.hibernate.Hibernate;
+import org.hibernate.QueryException;
+import org.hibernate.engine.Mapping;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.type.Type;
+
+/**
+ * Emulation of <tt>locate()</tt> on Sybase
+ * @author Nathan Moon
+ */
+public class CharIndexFunction implements SQLFunction {
+
+ public Type getReturnType(Type columnType, Mapping mapping) throws QueryException {
+ return Hibernate.INTEGER;
+ }
+
+ public boolean hasArguments() {
+ return true;
+ }
+
+ public boolean hasParenthesesIfNoArguments() {
+ return true;
+ }
+
+ public String render(List args, SessionFactoryImplementor factory) throws QueryException {
+ boolean threeArgs = args.size() > 2;
+ Object pattern = args.get(0);
+ Object string = args.get(1);
+ Object start = threeArgs ? args.get(2) : null;
+
+ StringBuffer buf = new StringBuffer();
+ buf.append("charindex(").append( pattern ).append(", ");
+ if (threeArgs) buf.append( "right(");
+ buf.append( string );
+ if (threeArgs) buf.append( ", char_length(" ).append( string ).append(")-(").append( start ).append("-1))");
+ buf.append(')');
+ return buf.toString();
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/CharIndexFunction.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/ClassicAvgFunction.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/ClassicAvgFunction.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/ClassicAvgFunction.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,42 @@
+/**
+ *
+ */
+package org.hibernate.dialect.function;
+
+import java.sql.Types;
+
+import org.hibernate.Hibernate;
+import org.hibernate.MappingException;
+import org.hibernate.QueryException;
+import org.hibernate.engine.Mapping;
+import org.hibernate.type.Type;
+
+/**
+ * Classic AVG sqlfunction that return types as it was done in Hibernate 3.1
+ *
+ * @author Max Rydahl Andersen
+ *
+ */
+public class ClassicAvgFunction extends StandardSQLFunction {
+ public ClassicAvgFunction() {
+ super( "avg" );
+ }
+
+ public Type getReturnType(Type columnType, Mapping mapping) throws QueryException {
+ int[] sqlTypes;
+ try {
+ sqlTypes = columnType.sqlTypes( mapping );
+ }
+ catch ( MappingException me ) {
+ throw new QueryException( me );
+ }
+ if ( sqlTypes.length != 1 ) throw new QueryException( "multi-column type in avg()" );
+ int sqlType = sqlTypes[0];
+ if ( sqlType == Types.INTEGER || sqlType == Types.BIGINT || sqlType == Types.TINYINT ) {
+ return Hibernate.FLOAT;
+ }
+ else {
+ return columnType;
+ }
+ }
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/ClassicCountFunction.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/ClassicCountFunction.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/ClassicCountFunction.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,25 @@
+/**
+ *
+ */
+package org.hibernate.dialect.function;
+
+import org.hibernate.Hibernate;
+import org.hibernate.engine.Mapping;
+import org.hibernate.type.Type;
+
+
+/**
+ * Classic COUNT sqlfunction that return types as it was done in Hibernate 3.1
+ *
+ * @author Max Rydahl Andersen
+ *
+ */
+public class ClassicCountFunction extends StandardSQLFunction {
+ public ClassicCountFunction() {
+ super( "count" );
+ }
+
+ public Type getReturnType(Type columnType, Mapping mapping) {
+ return Hibernate.INTEGER;
+ }
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/ClassicSumFunction.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/ClassicSumFunction.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/ClassicSumFunction.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,17 @@
+/**
+ *
+ */
+package org.hibernate.dialect.function;
+
+
+/**
+ * Classic SUM sqlfunction that return types as it was done in Hibernate 3.1
+ *
+ * @author Max Rydahl Andersen
+ *
+ */
+public class ClassicSumFunction extends StandardSQLFunction {
+ public ClassicSumFunction() {
+ super( "sum" );
+ }
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/ConditionalParenthesisFunction.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/ConditionalParenthesisFunction.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/ConditionalParenthesisFunction.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,45 @@
+//$Id: ConditionalParenthesisFunction.java,v 1.4 2005/04/26 18:08:01 oneovthafew Exp $
+package org.hibernate.dialect.function;
+
+import java.util.List;
+
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.type.Type;
+
+/**
+ * Essentially the same as {@link org.hibernate.dialect.function.StandardSQLFunction},
+ * except that here the parentheses are not included when no arguments are given.
+ *
+ * @author Jonathan Levinson
+ */
+public class ConditionalParenthesisFunction extends StandardSQLFunction {
+
+ public ConditionalParenthesisFunction(String name) {
+ super( name );
+ }
+
+ public ConditionalParenthesisFunction(String name, Type type) {
+ super( name, type );
+ }
+
+ public boolean hasParenthesesIfNoArguments() {
+ return false;
+ }
+
+ public String render(List args, SessionFactoryImplementor factory) {
+ final boolean hasArgs = !args.isEmpty();
+ StringBuffer buf = new StringBuffer();
+ buf.append( getName() );
+ if ( hasArgs ) {
+ buf.append( "(" );
+ for ( int i = 0; i < args.size(); i++ ) {
+ buf.append( args.get( i ) );
+ if ( i < args.size() - 1 ) {
+ buf.append( ", " );
+ }
+ }
+ buf.append( ")" );
+ }
+ return buf.toString();
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/ConvertFunction.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/ConvertFunction.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/ConvertFunction.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,45 @@
+//$Id: CastFunction.java 7368 2005-07-04 02:54:27Z oneovthafew $
+package org.hibernate.dialect.function;
+
+import java.util.List;
+
+import org.hibernate.QueryException;
+import org.hibernate.Hibernate;
+import org.hibernate.engine.Mapping;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.type.Type;
+
+/**
+ * A Caché defintion of a convert function.
+ *
+ * @author Jonathan Levinson
+ */
+public class ConvertFunction implements SQLFunction {
+
+ public Type getReturnType(Type columnType, Mapping mapping) throws QueryException {
+ return Hibernate.STRING;
+ }
+
+ public boolean hasArguments() {
+ return true;
+ }
+
+ public boolean hasParenthesesIfNoArguments() {
+ return true;
+ }
+
+ public String render(List args, SessionFactoryImplementor factory) throws QueryException {
+ if ( args.size() != 2 && args.size() != 3 ) {
+ throw new QueryException( "convert() requires two or three arguments" );
+ }
+ String type = ( String ) args.get( 1 );
+
+ if ( args.size() == 2 ) {
+ return "{fn convert(" + args.get( 0 ) + " , " + type + ")}";
+ }
+ else {
+ return "convert(" + args.get( 0 ) + " , " + type + "," + args.get( 2 ) + ")";
+ }
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/NoArgSQLFunction.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/NoArgSQLFunction.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/NoArgSQLFunction.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,49 @@
+//$Id: NoArgSQLFunction.java 6608 2005-04-29 15:32:30Z oneovthafew $
+package org.hibernate.dialect.function;
+
+import java.util.List;
+
+import org.hibernate.QueryException;
+import org.hibernate.engine.Mapping;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.type.Type;
+
+
+/**
+ * A function which takes no arguments
+ * @author Michi
+ */
+public class NoArgSQLFunction implements SQLFunction {
+ private Type returnType;
+ private boolean hasParenthesesIfNoArguments;
+ private String name;
+
+ public NoArgSQLFunction(String name, Type returnType) {
+ this(name, returnType, true);
+ }
+
+ public NoArgSQLFunction(String name, Type returnType, boolean hasParenthesesIfNoArguments) {
+ this.returnType = returnType;
+ this.hasParenthesesIfNoArguments = hasParenthesesIfNoArguments;
+ this.name = name;
+ }
+
+ public Type getReturnType(Type columnType, Mapping mapping) throws QueryException {
+ return returnType;
+ }
+
+ public boolean hasArguments() {
+ return false;
+ }
+
+ public boolean hasParenthesesIfNoArguments() {
+ return hasParenthesesIfNoArguments;
+ }
+
+ public String render(List args, SessionFactoryImplementor factory) throws QueryException {
+ if ( args.size()>0 ) {
+ throw new QueryException("function takes no arguments: " + name);
+ }
+ return hasParenthesesIfNoArguments ? name + "()" : name;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/NvlFunction.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/NvlFunction.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/NvlFunction.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,42 @@
+//$Id: NvlFunction.java 6608 2005-04-29 15:32:30Z oneovthafew $
+package org.hibernate.dialect.function;
+
+import java.util.List;
+
+import org.hibernate.QueryException;
+import org.hibernate.engine.Mapping;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.type.Type;
+
+/**
+ * Emulation of <tt>coalesce()</tt> on Oracle, using multiple
+ * <tt>nvl()</tt> calls
+ * @author Gavin King
+ */
+public class NvlFunction implements SQLFunction {
+
+ public Type getReturnType(Type columnType, Mapping mapping) throws QueryException {
+ return columnType;
+ }
+
+ public boolean hasArguments() {
+ return true;
+ }
+
+ public boolean hasParenthesesIfNoArguments() {
+ return true;
+ }
+
+ public String render(List args, SessionFactoryImplementor factory) throws QueryException {
+ int lastIndex = args.size()-1;
+ Object last = args.remove(lastIndex);
+ if ( lastIndex==0 ) return last.toString();
+ Object secondLast = args.get(lastIndex-1);
+ String nvl = "nvl(" + secondLast + ", " + last + ")";
+ args.set(lastIndex-1, nvl);
+ return render(args, factory);
+ }
+
+
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/NvlFunction.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/PositionSubstringFunction.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/PositionSubstringFunction.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/PositionSubstringFunction.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,48 @@
+//$Id: PositionSubstringFunction.java 6608 2005-04-29 15:32:30Z oneovthafew $
+package org.hibernate.dialect.function;
+
+import java.util.List;
+
+import org.hibernate.Hibernate;
+import org.hibernate.QueryException;
+import org.hibernate.engine.Mapping;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.type.Type;
+
+/**
+ * Emulation of <tt>locate()</tt> on PostgreSQL
+ * @author Gavin King
+ */
+public class PositionSubstringFunction implements SQLFunction {
+
+ public Type getReturnType(Type columnType, Mapping mapping) throws QueryException {
+ return Hibernate.INTEGER;
+ }
+
+ public boolean hasArguments() {
+ return true;
+ }
+
+ public boolean hasParenthesesIfNoArguments() {
+ return true;
+ }
+
+ public String render(List args, SessionFactoryImplementor factory) throws QueryException {
+ boolean threeArgs = args.size() > 2;
+ Object pattern = args.get(0);
+ Object string = args.get(1);
+ Object start = threeArgs ? args.get(2) : null;
+
+ StringBuffer buf = new StringBuffer();
+ if (threeArgs) buf.append('(');
+ buf.append("position(").append( pattern ).append(" in ");
+ if (threeArgs) buf.append( "substring(");
+ buf.append( string );
+ if (threeArgs) buf.append( ", " ).append( start ).append(')');
+ buf.append(')');
+ if (threeArgs) buf.append('+').append( start ).append("-1)");
+ return buf.toString();
+ }
+
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/PositionSubstringFunction.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/SQLFunction.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/SQLFunction.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/SQLFunction.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,58 @@
+//$Id: SQLFunction.java 10774 2006-11-08 16:54:55Z steve.ebersole at jboss.com $
+package org.hibernate.dialect.function;
+
+import java.util.List;
+
+import org.hibernate.QueryException;
+import org.hibernate.engine.Mapping;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.type.Type;
+
+/**
+ * Provides support routines for the HQL functions as used
+ * in the various SQL Dialects
+ *
+ * Provides an interface for supporting various HQL functions that are
+ * translated to SQL. The Dialect and its sub-classes use this interface to
+ * provide details required for processing of the function.
+ *
+ * @author David Channon
+ */
+public interface SQLFunction {
+ /**
+ * The return type of the function. May be either a concrete type which
+ * is preset, or variable depending upon the type of the first function
+ * argument.
+ *
+ * @param columnType the type of the first argument
+ * @param mapping The mapping source.
+ * @return The type to be expected as a return.
+ * @throws org.hibernate.QueryException Indicates an issue resolving the return type.
+ */
+ public Type getReturnType(Type columnType, Mapping mapping) throws QueryException;
+
+ /**
+ * Does this function have any arguments?
+ *
+ * @return True if the function expects to have parameters; false otherwise.
+ */
+ public boolean hasArguments();
+
+ /**
+ * If there are no arguments, are parens required?
+ *
+ * @return True if a no-arg call of this function requires parentheses.
+ */
+ public boolean hasParenthesesIfNoArguments();
+
+ /**
+ * Render the function call as SQL fragment.
+ *
+ * @param args The function arguments
+ * @param factory The SessionFactory
+ * @return The rendered function call
+ * @throws org.hibernate.QueryException Indicates a problem rendering the
+ * function call.
+ */
+ public String render(List args, SessionFactoryImplementor factory) throws QueryException;
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/SQLFunctionRegistry.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/SQLFunctionRegistry.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/SQLFunctionRegistry.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,33 @@
+package org.hibernate.dialect.function;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.hibernate.dialect.Dialect;
+
+public class SQLFunctionRegistry {
+
+ private final Dialect dialect;
+ private final Map userFunctions;
+
+ public SQLFunctionRegistry(Dialect dialect, Map userFunctions) {
+ this.dialect = dialect;
+ this.userFunctions = new HashMap();
+ this.userFunctions.putAll( userFunctions );
+ }
+
+ public SQLFunction findSQLFunction(String functionName) {
+ String name = functionName.toLowerCase();
+ SQLFunction userFunction = (SQLFunction) userFunctions.get( name );
+
+ return userFunction!=null?userFunction:(SQLFunction) dialect.getFunctions().get(name); // TODO: lowercasing done here. Was done "at random" before; maybe not needed at all ?
+ }
+
+ public boolean hasFunction(String functionName) {
+ String name = functionName.toLowerCase();
+ boolean hasUserFunction = userFunctions.containsKey ( name );
+
+ return hasUserFunction || dialect.getFunctions().containsKey ( name ); // TODO: toLowerCase was not done before. Only used in Template.
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/SQLFunctionTemplate.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/SQLFunctionTemplate.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/SQLFunctionTemplate.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,123 @@
+//$Id: SQLFunctionTemplate.java 6608 2005-04-29 15:32:30Z oneovthafew $
+package org.hibernate.dialect.function;
+
+import org.hibernate.QueryException;
+import org.hibernate.engine.Mapping;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.type.Type;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents HQL functions that can have different representations in different SQL dialects.
+ * E.g. in HQL we can define function <code>concat(?1, ?2)</code> to concatenate two strings
+ * p1 and p2. Target SQL function will be dialect-specific, e.g. <code>(?1 || ?2)</code> for
+ * Oracle, <code>concat(?1, ?2)</code> for MySql, <code>(?1 + ?2)</code> for MS SQL.
+ * Each dialect will define a template as a string (exactly like above) marking function
+ * parameters with '?' followed by parameter's index (first index is 1).
+ *
+ * @author <a href="mailto:alex at jboss.org">Alexey Loubyansky</a>
+ * @version <tt>$Revision: 6608 $</tt>
+ */
+public class SQLFunctionTemplate implements SQLFunction {
+ private final Type type;
+ private final boolean hasArguments;
+ private final boolean hasParenthesesIfNoArgs;
+
+ private final String template;
+ private final String[] chunks;
+ private final int[] paramIndexes;
+
+ public SQLFunctionTemplate(Type type, String template) {
+ this( type, template, true );
+ }
+
+ public SQLFunctionTemplate(Type type, String template, boolean hasParenthesesIfNoArgs) {
+ this.type = type;
+ this.template = template;
+
+ List chunkList = new ArrayList();
+ List paramList = new ArrayList();
+ StringBuffer chunk = new StringBuffer( 10 );
+ StringBuffer index = new StringBuffer( 2 );
+
+ for ( int i = 0; i < template.length(); ++i ) {
+ char c = template.charAt( i );
+ if ( c == '?' ) {
+ chunkList.add( chunk.toString() );
+ chunk.delete( 0, chunk.length() );
+
+ while ( ++i < template.length() ) {
+ c = template.charAt( i );
+ if ( Character.isDigit( c ) ) {
+ index.append( c );
+ }
+ else {
+ chunk.append( c );
+ break;
+ }
+ }
+
+ paramList.add( new Integer( Integer.parseInt( index.toString() ) - 1 ) );
+ index.delete( 0, index.length() );
+ }
+ else {
+ chunk.append( c );
+ }
+ }
+
+ if ( chunk.length() > 0 ) {
+ chunkList.add( chunk.toString() );
+ }
+
+ chunks = ( String[] ) chunkList.toArray( new String[chunkList.size()] );
+ paramIndexes = new int[paramList.size()];
+ for ( int i = 0; i < paramIndexes.length; ++i ) {
+ paramIndexes[i] = ( ( Integer ) paramList.get( i ) ).intValue();
+ }
+
+ hasArguments = paramIndexes.length > 0;
+ this.hasParenthesesIfNoArgs = hasParenthesesIfNoArgs;
+ }
+
+ /**
+ * Applies the template to passed in arguments.
+ * @param args function arguments
+ *
+ * @return generated SQL function call
+ */
+ public String render(List args, SessionFactoryImplementor factory) {
+ StringBuffer buf = new StringBuffer();
+ for ( int i = 0; i < chunks.length; ++i ) {
+ if ( i < paramIndexes.length ) {
+ Object arg = paramIndexes[i] < args.size() ? args.get( paramIndexes[i] ) : null;
+ if ( arg != null ) {
+ buf.append( chunks[i] ).append( arg );
+ }
+ }
+ else {
+ buf.append( chunks[i] );
+ }
+ }
+ return buf.toString();
+ }
+
+ // SQLFunction implementation
+
+ public Type getReturnType(Type columnType, Mapping mapping) throws QueryException {
+ return type;
+ }
+
+ public boolean hasArguments() {
+ return hasArguments;
+ }
+
+ public boolean hasParenthesesIfNoArguments() {
+ return hasParenthesesIfNoArgs;
+ }
+
+ public String toString() {
+ return template;
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/SQLFunctionTemplate.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/StandardJDBCEscapeFunction.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/StandardJDBCEscapeFunction.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/StandardJDBCEscapeFunction.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,31 @@
+package org.hibernate.dialect.function;
+
+import java.util.List;
+
+import org.hibernate.type.Type;
+import org.hibernate.engine.SessionFactoryImplementor;
+
+/**
+ * Analogous to {@link org.hibernate.dialect.function.StandardSQLFunction}
+ * except that standard JDBC escape sequences (i.e. {fn blah}) are used when
+ * rendering the SQL.
+ *
+ * @author Steve Ebersole
+ */
+public class StandardJDBCEscapeFunction extends StandardSQLFunction {
+ public StandardJDBCEscapeFunction(String name) {
+ super( name );
+ }
+
+ public StandardJDBCEscapeFunction(String name, Type typeValue) {
+ super( name, typeValue );
+ }
+
+ public String render(List args, SessionFactoryImplementor factory) {
+ return "{fn " + super.render( args, factory ) + "}";
+ }
+
+ public String toString() {
+ return "{fn " + getName() + "...}";
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/StandardSQLFunction.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/StandardSQLFunction.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/StandardSQLFunction.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,107 @@
+//$Id: StandardSQLFunction.java 10774 2006-11-08 16:54:55Z steve.ebersole at jboss.com $
+package org.hibernate.dialect.function;
+
+import java.util.List;
+
+import org.hibernate.engine.Mapping;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.type.Type;
+
+/**
+ * Provides a standard implementation that supports the majority of the HQL
+ * functions that are translated to SQL. The Dialect and its sub-classes use
+ * this class to provide details required for processing of the associated
+ * function.
+ *
+ * @author David Channon
+ */
+public class StandardSQLFunction implements SQLFunction {
+ private final String name;
+ private final Type type;
+
+ /**
+ * Construct a standard SQL function definition with a variable return type;
+ * the actual return type will depend on the types to which the function
+ * is applied.
+ * <p/>
+ * Using this form, the return type is considered non-static and assumed
+ * to be the type of the first argument.
+ *
+ * @param name The name of the function.
+ */
+ public StandardSQLFunction(String name) {
+ this( name, null );
+ }
+
+ /**
+ * Construct a standard SQL function definition with a static return type.
+ *
+ * @param name The name of the function.
+ * @param type The static return type.
+ */
+ public StandardSQLFunction(String name, Type type) {
+ this.name = name;
+ this.type = type;
+ }
+
+ /**
+ * Function name accessor
+ *
+ * @return The function name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Function static return type accessor.
+ *
+ * @return The static function return type; or null if return type is
+ * not static.
+ */
+ public Type getType() {
+ return type;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Type getReturnType(Type columnType, Mapping mapping) {
+ // return the concrete type, or the underlying type if a concrete type
+ // was not specified
+ return type == null ? columnType : type;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean hasArguments() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean hasParenthesesIfNoArguments() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String render(List args, SessionFactoryImplementor factory) {
+ StringBuffer buf = new StringBuffer();
+ buf.append( name ).append( '(' );
+ for ( int i = 0; i < args.size(); i++ ) {
+ buf.append( args.get( i ) );
+ if ( i < args.size() - 1 ) {
+ buf.append( ", " );
+ }
+ }
+ return buf.append( ')' ).toString();
+ }
+
+ public String toString() {
+ return name;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/VarArgsSQLFunction.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/VarArgsSQLFunction.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/VarArgsSQLFunction.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,58 @@
+//$Id: VarArgsSQLFunction.java 6608 2005-04-29 15:32:30Z oneovthafew $
+package org.hibernate.dialect.function;
+
+import java.util.List;
+
+import org.hibernate.QueryException;
+import org.hibernate.engine.Mapping;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.type.Type;
+
+/**
+ * Support for slightly more general templating than <tt>StandardSQLFunction</tt>,
+ * with an unlimited number of arguments.
+ * @author Gavin King
+ */
+public class VarArgsSQLFunction implements SQLFunction {
+
+ private final String begin;
+ private final String sep;
+ private final String end;
+ private final Type type;
+
+ public VarArgsSQLFunction(Type type, String begin, String sep, String end) {
+ this.begin = begin;
+ this.sep = sep;
+ this.end = end;
+ this.type = type;
+ }
+
+ public VarArgsSQLFunction(String begin, String sep, String end) {
+ this.begin = begin;
+ this.sep = sep;
+ this.end = end;
+ this.type = null;
+ }
+
+ public Type getReturnType(Type columnType, Mapping mapping) throws QueryException {
+ return type==null ? columnType : type;
+ }
+
+ public boolean hasArguments() {
+ return true;
+ }
+
+ public boolean hasParenthesesIfNoArguments() {
+ return true;
+ }
+
+ public String render(List args, SessionFactoryImplementor factory) throws QueryException {
+ StringBuffer buf = new StringBuffer().append(begin);
+ for ( int i=0; i<args.size(); i++ ) {
+ buf.append( args.get(i) );
+ if (i<args.size()-1) buf.append(sep);
+ }
+ return buf.append(end).toString();
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/VarArgsSQLFunction.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,9 @@
+<html>
+<head></head>
+<body>
+<p>
+ A framework for defining database-specific SQL functions
+ that are available via the dialect.
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/function/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/lock/LockingStrategy.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/lock/LockingStrategy.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/lock/LockingStrategy.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,37 @@
+package org.hibernate.dialect.lock;
+
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.StaleObjectStateException;
+import org.hibernate.JDBCException;
+
+import java.io.Serializable;
+
+/**
+ * A strategy abstraction for how locks are obtained in the underlying database.
+ * <p/>
+ * All locking provided implemenations assume the underlying database supports
+ * (and that the connection is in) at least read-committed transaction isolation.
+ * The most glaring exclusion to this is HSQLDB which only offers support for
+ * READ_UNCOMMITTED isolation.
+ *
+ * @see org.hibernate.dialect.Dialect#getLockingStrategy
+ * @since 3.2
+ *
+ * @author Steve Ebersole
+ */
+public interface LockingStrategy {
+ /**
+ * Acquire an appropriate type of lock on the underlying data that will
+ * endure until the end of the current transaction.
+ *
+ * @param id The id of the row to be locked
+ * @param version The current version (or null if not versioned)
+ * @param object The object logically being locked (currently not used)
+ * @param session The session from which the lock request originated
+ * @throws StaleObjectStateException Indicates an optimisitic lock failure
+ * as part of acquiring the requested database lock.
+ * @throws JDBCException
+ */
+ public void lock(Serializable id, Object version, Object object, SessionImplementor session)
+ throws StaleObjectStateException, JDBCException;
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/lock/SelectLockingStrategy.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/lock/SelectLockingStrategy.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/lock/SelectLockingStrategy.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,119 @@
+package org.hibernate.dialect.lock;
+
+import org.hibernate.persister.entity.Lockable;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.StaleObjectStateException;
+import org.hibernate.JDBCException;
+import org.hibernate.LockMode;
+import org.hibernate.sql.SimpleSelect;
+import org.hibernate.pretty.MessageHelper;
+import org.hibernate.exception.JDBCExceptionHelper;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+/**
+ * A locking strategy where the locks are obtained through select statements.
+ * <p/>
+ * For non-read locks, this is achieved through the Dialect's specific
+ * SELECT ... FOR UPDATE syntax.
+ *
+ * @see org.hibernate.dialect.Dialect#getForUpdateString(org.hibernate.LockMode)
+ * @see org.hibernate.dialect.Dialect#appendLockHint(org.hibernate.LockMode, String)
+ * @since 3.2
+ *
+ * @author Steve Ebersole
+ */
+public class SelectLockingStrategy implements LockingStrategy {
+
+ private final Lockable lockable;
+ private final LockMode lockMode;
+ private final String sql;
+
+ /**
+ * Construct a locking strategy based on SQL SELECT statements.
+ *
+ * @param lockable The metadata for the entity to be locked.
+ * @param lockMode Indictates the type of lock to be acquired.
+ */
+ public SelectLockingStrategy(Lockable lockable, LockMode lockMode) {
+ this.lockable = lockable;
+ this.lockMode = lockMode;
+ this.sql = generateLockString();
+ }
+
+ /**
+ * @see LockingStrategy#lock
+ */
+ public void lock(
+ Serializable id,
+ Object version,
+ Object object,
+ SessionImplementor session) throws StaleObjectStateException, JDBCException {
+
+ SessionFactoryImplementor factory = session.getFactory();
+ try {
+ PreparedStatement st = session.getBatcher().prepareSelectStatement( sql );
+ try {
+ lockable.getIdentifierType().nullSafeSet( st, id, 1, session );
+ if ( lockable.isVersioned() ) {
+ lockable.getVersionType().nullSafeSet(
+ st,
+ version,
+ lockable.getIdentifierType().getColumnSpan( factory ) + 1,
+ session
+ );
+ }
+
+ ResultSet rs = st.executeQuery();
+ try {
+ if ( !rs.next() ) {
+ if ( factory.getStatistics().isStatisticsEnabled() ) {
+ factory.getStatisticsImplementor()
+ .optimisticFailure( lockable.getEntityName() );
+ }
+ throw new StaleObjectStateException( lockable.getEntityName(), id );
+ }
+ }
+ finally {
+ rs.close();
+ }
+ }
+ finally {
+ session.getBatcher().closeStatement( st );
+ }
+
+ }
+ catch ( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ session.getFactory().getSQLExceptionConverter(),
+ sqle,
+ "could not lock: " + MessageHelper.infoString( lockable, id, session.getFactory() ),
+ sql
+ );
+ }
+ }
+
+ protected LockMode getLockMode() {
+ return lockMode;
+ }
+
+ protected String generateLockString() {
+ SessionFactoryImplementor factory = lockable.getFactory();
+ SimpleSelect select = new SimpleSelect( factory.getDialect() )
+ .setLockMode( lockMode )
+ .setTableName( lockable.getRootTableName() )
+ .addColumn( lockable.getRootTableIdentifierColumnNames()[0] )
+ .addCondition( lockable.getRootTableIdentifierColumnNames(), "=?" );
+ if ( lockable.isVersioned() ) {
+ select.addCondition( lockable.getVersionColumnName(), "=?" );
+ }
+ if ( factory.getSettings().isCommentsEnabled() ) {
+ select.setComment( lockMode + " lock " + lockable.getEntityName() );
+ }
+ return select.toStatementString();
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/lock/UpdateLockingStrategy.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/lock/UpdateLockingStrategy.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/lock/UpdateLockingStrategy.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,122 @@
+package org.hibernate.dialect.lock;
+
+import org.hibernate.persister.entity.Lockable;
+import org.hibernate.LockMode;
+import org.hibernate.HibernateException;
+import org.hibernate.StaleObjectStateException;
+import org.hibernate.JDBCException;
+import org.hibernate.pretty.MessageHelper;
+import org.hibernate.exception.JDBCExceptionHelper;
+import org.hibernate.sql.Update;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+/**
+ * A locking strategy where the locks are obtained through update statements.
+ * <p/>
+ * This strategy is not valid for read style locks.
+ *
+ * @since 3.2
+ *
+ * @author Steve Ebersole
+ */
+public class UpdateLockingStrategy implements LockingStrategy {
+ private static final Log log = LogFactory.getLog( UpdateLockingStrategy.class );
+
+ private final Lockable lockable;
+ private final LockMode lockMode;
+ private final String sql;
+
+ /**
+ * Construct a locking strategy based on SQL UPDATE statements.
+ *
+ * @param lockable The metadata for the entity to be locked.
+ * @param lockMode Indictates the type of lock to be acquired. Note that
+ * read-locks are not valid for this strategy.
+ */
+ public UpdateLockingStrategy(Lockable lockable, LockMode lockMode) {
+ this.lockable = lockable;
+ this.lockMode = lockMode;
+ if ( lockMode.lessThan( LockMode.UPGRADE ) ) {
+ throw new HibernateException( "[" + lockMode + "] not valid for update statement" );
+ }
+ if ( !lockable.isVersioned() ) {
+ log.warn( "write locks via update not supported for non-versioned entities [" + lockable.getEntityName() + "]" );
+ this.sql = null;
+ }
+ else {
+ this.sql = generateLockString();
+ }
+ }
+
+ /**
+ * @see LockingStrategy#lock
+ */
+ public void lock(
+ Serializable id,
+ Object version,
+ Object object,
+ SessionImplementor session) throws StaleObjectStateException, JDBCException {
+ if ( !lockable.isVersioned() ) {
+ throw new HibernateException( "write locks via update not supported for non-versioned entities [" + lockable.getEntityName() + "]" );
+ }
+ // todo : should we additionally check the current isolation mode explicitly?
+ SessionFactoryImplementor factory = session.getFactory();
+ try {
+ PreparedStatement st = session.getBatcher().prepareSelectStatement( sql );
+ try {
+ lockable.getVersionType().nullSafeSet( st, version, 1, session );
+ int offset = 2;
+
+ lockable.getIdentifierType().nullSafeSet( st, id, offset, session );
+ offset += lockable.getIdentifierType().getColumnSpan( factory );
+
+ if ( lockable.isVersioned() ) {
+ lockable.getVersionType().nullSafeSet( st, version, offset, session );
+ }
+
+ int affected = st.executeUpdate();
+ if ( affected < 0 ) {
+ factory.getStatisticsImplementor().optimisticFailure( lockable.getEntityName() );
+ throw new StaleObjectStateException( lockable.getEntityName(), id );
+ }
+
+ }
+ finally {
+ session.getBatcher().closeStatement( st );
+ }
+
+ }
+ catch ( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ session.getFactory().getSQLExceptionConverter(),
+ sqle,
+ "could not lock: " + MessageHelper.infoString( lockable, id, session.getFactory() ),
+ sql
+ );
+ }
+ }
+
+ protected String generateLockString() {
+ SessionFactoryImplementor factory = lockable.getFactory();
+ Update update = new Update( factory.getDialect() );
+ update.setTableName( lockable.getRootTableName() );
+ update.setPrimaryKeyColumnNames( lockable.getRootTableIdentifierColumnNames() );
+ update.setVersionColumnName( lockable.getVersionColumnName() );
+ update.addColumn( lockable.getVersionColumnName() );
+ if ( factory.getSettings().isCommentsEnabled() ) {
+ update.setComment( lockMode + " lock " + lockable.getEntityName() );
+ }
+ return update.toStatementString();
+ }
+
+ protected LockMode getLockMode() {
+ return lockMode;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,11 @@
+<html>
+<head></head>
+<body>
+<p>
+ This package abstracts the SQL dialect of the underlying database.
+</p>
+<p>
+ A concrete <tt>Dialect</tt> may be specifed using <tt>hibernate.dialect</tt>.
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/dialect/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/ActionQueue.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/ActionQueue.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/ActionQueue.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,531 @@
+// $Id: ActionQueue.java 11402 2007-04-11 14:24:35Z steve.ebersole at jboss.com $
+package org.hibernate.engine;
+
+import org.hibernate.action.EntityInsertAction;
+import org.hibernate.action.EntityDeleteAction;
+import org.hibernate.action.Executable;
+import org.hibernate.action.EntityUpdateAction;
+import org.hibernate.action.CollectionRecreateAction;
+import org.hibernate.action.CollectionRemoveAction;
+import org.hibernate.action.CollectionUpdateAction;
+import org.hibernate.action.EntityIdentityInsertAction;
+import org.hibernate.action.BulkOperationCleanupAction;
+import org.hibernate.HibernateException;
+import org.hibernate.AssertionFailure;
+import org.hibernate.cache.CacheException;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.io.ObjectInputStream;
+import java.io.IOException;
+import java.io.Serializable;
+import java.io.ObjectOutputStream;
+
+/**
+ * Responsible for maintaining the queue of actions related to events.
+ * </p>
+ * The ActionQueue holds the DML operations queued as part of a session's
+ * transactional-write-behind semantics. DML operations are queued here
+ * until a flush forces them to be executed against the database.
+ *
+ * @author Steve Ebersole
+ */
+public class ActionQueue {
+
+ private static final Log log = LogFactory.getLog( ActionQueue.class );
+ private static final int INIT_QUEUE_LIST_SIZE = 5;
+
+ private SessionImplementor session;
+
+ // Object insertions, updates, and deletions have list semantics because
+ // they must happen in the right order so as to respect referential
+ // integrity
+ private ArrayList insertions;
+ private ArrayList deletions;
+ private ArrayList updates;
+ // Actually the semantics of the next three are really "Bag"
+ // Note that, unlike objects, collection insertions, updates,
+ // deletions are not really remembered between flushes. We
+ // just re-use the same Lists for convenience.
+ private ArrayList collectionCreations;
+ private ArrayList collectionUpdates;
+ private ArrayList collectionRemovals;
+
+ private ArrayList executions;
+
+ /**
+ * Constructs an action queue bound to the given session.
+ *
+ * @param session The session "owning" this queue.
+ */
+ public ActionQueue(SessionImplementor session) {
+ this.session = session;
+ init();
+ }
+
+ private void init() {
+ insertions = new ArrayList( INIT_QUEUE_LIST_SIZE );
+ deletions = new ArrayList( INIT_QUEUE_LIST_SIZE );
+ updates = new ArrayList( INIT_QUEUE_LIST_SIZE );
+
+ collectionCreations = new ArrayList( INIT_QUEUE_LIST_SIZE );
+ collectionRemovals = new ArrayList( INIT_QUEUE_LIST_SIZE );
+ collectionUpdates = new ArrayList( INIT_QUEUE_LIST_SIZE );
+
+ executions = new ArrayList( INIT_QUEUE_LIST_SIZE * 3 );
+ }
+
+ public void clear() {
+ updates.clear();
+ insertions.clear();
+ deletions.clear();
+
+ collectionCreations.clear();
+ collectionRemovals.clear();
+ collectionUpdates.clear();
+ }
+
+ public void addAction(EntityInsertAction action) {
+ insertions.add( action );
+ }
+
+ public void addAction(EntityDeleteAction action) {
+ deletions.add( action );
+ }
+
+ public void addAction(EntityUpdateAction action) {
+ updates.add( action );
+ }
+
+ public void addAction(CollectionRecreateAction action) {
+ collectionCreations.add( action );
+ }
+
+ public void addAction(CollectionRemoveAction action) {
+ collectionRemovals.add( action );
+ }
+
+ public void addAction(CollectionUpdateAction action) {
+ collectionUpdates.add( action );
+ }
+
+ public void addAction(EntityIdentityInsertAction insert) {
+ insertions.add( insert );
+ }
+
+ public void addAction(BulkOperationCleanupAction cleanupAction) {
+ // Add these directly to the executions queue
+ executions.add( cleanupAction );
+ }
+
+ /**
+ * Perform all currently queued entity-insertion actions.
+ *
+ * @throws HibernateException error executing queued insertion actions.
+ */
+ public void executeInserts() throws HibernateException {
+ executeActions( insertions );
+ }
+
+ /**
+ * Perform all currently queued actions.
+ *
+ * @throws HibernateException error executing queued actions.
+ */
+ public void executeActions() throws HibernateException {
+ executeActions( insertions );
+ executeActions( updates );
+ executeActions( collectionRemovals );
+ executeActions( collectionUpdates );
+ executeActions( collectionCreations );
+ executeActions( deletions );
+ }
+
+ /**
+ * Prepares the internal action queues for execution.
+ *
+ * @throws HibernateException error preparing actions.
+ */
+ public void prepareActions() throws HibernateException {
+ prepareActions( collectionRemovals );
+ prepareActions( collectionUpdates );
+ prepareActions( collectionCreations );
+ }
+
+ /**
+ * Performs cleanup of any held cache softlocks.
+ *
+ * @param success Was the transaction successful.
+ */
+ public void afterTransactionCompletion(boolean success) {
+ int size = executions.size();
+ final boolean invalidateQueryCache = session.getFactory().getSettings().isQueryCacheEnabled();
+ for ( int i = 0; i < size; i++ ) {
+ try {
+ Executable exec = ( Executable ) executions.get(i);
+ try {
+ exec.afterTransactionCompletion( success );
+ }
+ finally {
+ if ( invalidateQueryCache ) {
+ session.getFactory().getUpdateTimestampsCache().invalidate( exec.getPropertySpaces() );
+ }
+ }
+ }
+ catch (CacheException ce) {
+ log.error( "could not release a cache lock", ce );
+ // continue loop
+ }
+ catch (Exception e) {
+ throw new AssertionFailure( "Exception releasing cache locks", e );
+ }
+ }
+ executions.clear();
+ }
+
+ /**
+ * Check whether the given tables/query-spaces are to be executed against
+ * given the currently queued actions.
+ *
+ * @param tables The table/query-spaces to check.
+ * @return True if we contain pending actions against any of the given
+ * tables; false otherwise.
+ */
+ public boolean areTablesToBeUpdated(Set tables) {
+ return areTablesToUpdated( updates, tables ) ||
+ areTablesToUpdated( insertions, tables ) ||
+ areTablesToUpdated( deletions, tables ) ||
+ areTablesToUpdated( collectionUpdates, tables ) ||
+ areTablesToUpdated( collectionCreations, tables ) ||
+ areTablesToUpdated( collectionRemovals, tables );
+ }
+
+ /**
+ * Check whether any insertion or deletion actions are currently queued.
+ *
+ * @return True if insertions or deletions are currently queued; false otherwise.
+ */
+ public boolean areInsertionsOrDeletionsQueued() {
+ return ( insertions.size() > 0 || deletions.size() > 0 );
+ }
+
+ private static boolean areTablesToUpdated(List executables, Set tablespaces) {
+ int size = executables.size();
+ for ( int j = 0; j < size; j++ ) {
+ Serializable[] spaces = ( (Executable) executables.get(j) ).getPropertySpaces();
+ for ( int i = 0; i < spaces.length; i++ ) {
+ if ( tablespaces.contains( spaces[i] ) ) {
+ if ( log.isDebugEnabled() ) log.debug( "changes must be flushed to space: " + spaces[i] );
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private void executeActions(List list) throws HibernateException {
+ int size = list.size();
+ for ( int i = 0; i < size; i++ ) {
+ execute( (Executable) list.get(i) );
+ }
+ list.clear();
+ session.getBatcher().executeBatch();
+ }
+
+ public void execute(Executable executable) {
+ final boolean lockQueryCache = session.getFactory().getSettings().isQueryCacheEnabled();
+ if ( executable.hasAfterTransactionCompletion() || lockQueryCache ) {
+ executions.add( executable );
+ }
+ if (lockQueryCache) {
+ session.getFactory()
+ .getUpdateTimestampsCache()
+ .preinvalidate( executable.getPropertySpaces() );
+ }
+ executable.execute();
+ }
+
+ private void prepareActions(List queue) throws HibernateException {
+ int size = queue.size();
+ for ( int i=0; i<size; i++ ) {
+ Executable executable = ( Executable ) queue.get(i);
+ executable.beforeExecutions();
+ }
+ }
+
+ /**
+ * Returns a string representation of the object.
+ *
+ * @return a string representation of the object.
+ */
+ public String toString() {
+ return new StringBuffer()
+ .append("ActionQueue[insertions=").append(insertions)
+ .append(" updates=").append(updates)
+ .append(" deletions=").append(deletions)
+ .append(" collectionCreations=").append(collectionCreations)
+ .append(" collectionRemovals=").append(collectionRemovals)
+ .append(" collectionUpdates=").append(collectionUpdates)
+ .append("]")
+ .toString();
+ }
+
+ public int numberOfCollectionRemovals() {
+ return collectionRemovals.size();
+ }
+
+ public int numberOfCollectionUpdates() {
+ return collectionUpdates.size();
+ }
+
+ public int numberOfCollectionCreations() {
+ return collectionCreations.size();
+ }
+
+ public int numberOfDeletions() {
+ return deletions.size();
+ }
+
+ public int numberOfUpdates() {
+ return updates.size();
+ }
+
+ public int numberOfInsertions() {
+ return insertions.size();
+ }
+
+ public void sortCollectionActions() {
+ if ( session.getFactory().getSettings().isOrderUpdatesEnabled() ) {
+ //sort the updates by fk
+ java.util.Collections.sort( collectionCreations );
+ java.util.Collections.sort( collectionUpdates );
+ java.util.Collections.sort( collectionRemovals );
+ }
+ }
+
+ public void sortActions() {
+ if ( session.getFactory().getSettings().isOrderUpdatesEnabled() ) {
+ //sort the updates by pk
+ java.util.Collections.sort( updates );
+ }
+ if ( session.getFactory().getSettings().isOrderInsertsEnabled() ) {
+ sortInsertActions();
+ }
+ }
+
+ /**
+ * Provided the option is set ({@link org.hibernate.cfg.Environment#ORDER_INSERTS}),
+ * then order the {@link #insertions} queue such that we group inserts
+ * against the same entity together (without violating constraints). The
+ * original order is generated by cascade order, which in turn is based on
+ * the directionality of foreign-keys. So even though we will be changing
+ * the ordering here, we need to make absolutely certain that we do not
+ * circumvent this FK ordering to the extent of causing constraint
+ * violations
+ */
+ private void sortInsertActions() {
+ // IMPLEMENTATION NOTES:
+ //
+ // The main data structure in this ordering algorithm is the 'positionToAction'
+ // map. Essentially this can be thought of as an put-ordered map (the problem with
+ // actually implementing it that way and doing away with the 'nameList' is that
+ // we'd end up having potential duplicate key values). 'positionToAction' maitains
+ // a mapping from a position within the 'nameList' structure to a "partial queue"
+ // of actions.
+
+ HashMap positionToAction = new HashMap();
+ List nameList = new ArrayList();
+
+ loopInsertion: while( !insertions.isEmpty() ) {
+ EntityInsertAction action = ( EntityInsertAction ) insertions.remove( 0 );
+ String thisEntityName = action.getEntityName();
+
+ // see if we have already encountered this entity-name...
+ if ( ! nameList.contains( thisEntityName ) ) {
+ // we have not, so create the proper entries in nameList and positionToAction
+ ArrayList segmentedActionQueue = new ArrayList();
+ segmentedActionQueue.add( action );
+ nameList.add( thisEntityName );
+ positionToAction.put( new Integer( nameList.indexOf( thisEntityName ) ), segmentedActionQueue );
+ }
+ else {
+ // we have seen it before, so we need to determine if this insert action is
+ // is depenedent upon a previously processed action in terms of FK
+ // relationships (this FK checking is done against the entity's property-state
+ // associated with the action...)
+ int lastPos = nameList.lastIndexOf( thisEntityName );
+ Object[] states = action.getState();
+ for ( int i = 0; i < states.length; i++ ) {
+ for ( int j = 0; j < nameList.size(); j++ ) {
+ ArrayList tmpList = ( ArrayList ) positionToAction.get( new Integer( j ) );
+ for ( int k = 0; k < tmpList.size(); k++ ) {
+ final EntityInsertAction checkAction = ( EntityInsertAction ) tmpList.get( k );
+ if ( checkAction.getInstance() == states[i] && j > lastPos ) {
+ // 'checkAction' is inserting an entity upon which 'action'
+ // depends...
+ // note: this is an assumption and may not be correct in the case of one-to-one
+ ArrayList segmentedActionQueue = new ArrayList();
+ segmentedActionQueue.add( action );
+ nameList.add( thisEntityName );
+ positionToAction.put(new Integer( nameList.lastIndexOf( thisEntityName ) ), segmentedActionQueue );
+ continue loopInsertion;
+ }
+ }
+ }
+ }
+
+ ArrayList actionQueue = ( ArrayList ) positionToAction.get( new Integer( lastPos ) );
+ actionQueue.add( action );
+ }
+ }
+
+ // now iterate back through positionToAction map and move entityInsertAction back to insertion list
+ for ( int p = 0; p < nameList.size(); p++ ) {
+ ArrayList actionQueue = ( ArrayList ) positionToAction.get( new Integer( p ) );
+ Iterator itr = actionQueue.iterator();
+ while ( itr.hasNext() ) {
+ insertions.add( itr.next() );
+ }
+ }
+ }
+
+ public ArrayList cloneDeletions() {
+ return (ArrayList) deletions.clone();
+ }
+
+ public void clearFromFlushNeededCheck(int previousCollectionRemovalSize) {
+ collectionCreations.clear();
+ collectionUpdates.clear();
+ updates.clear();
+ // collection deletions are a special case since update() can add
+ // deletions of collections not loaded by the session.
+ for ( int i = collectionRemovals.size()-1; i >= previousCollectionRemovalSize; i-- ) {
+ collectionRemovals.remove(i);
+ }
+ }
+
+ public boolean hasAnyQueuedActions() {
+ return updates.size() > 0 ||
+ insertions.size() > 0 ||
+ deletions.size() > 0 ||
+ collectionUpdates.size() > 0 ||
+ collectionRemovals.size() > 0 ||
+ collectionCreations.size() > 0;
+ }
+
+ /**
+ * Used by the owning session to explicitly control serialization of the
+ * action queue
+ *
+ * @param oos The stream to which the action queue should get written
+ * @throws IOException
+ */
+ public void serialize(ObjectOutputStream oos) throws IOException {
+ log.trace( "serializing action-queue" );
+
+ int queueSize = insertions.size();
+ log.trace( "starting serialization of [" + queueSize + "] insertions entries" );
+ oos.writeInt( queueSize );
+ for ( int i = 0; i < queueSize; i++ ) {
+ oos.writeObject( insertions.get( i ) );
+ }
+
+ queueSize = deletions.size();
+ log.trace( "starting serialization of [" + queueSize + "] deletions entries" );
+ oos.writeInt( queueSize );
+ for ( int i = 0; i < queueSize; i++ ) {
+ oos.writeObject( deletions.get( i ) );
+ }
+
+ queueSize = updates.size();
+ log.trace( "starting serialization of [" + queueSize + "] updates entries" );
+ oos.writeInt( queueSize );
+ for ( int i = 0; i < queueSize; i++ ) {
+ oos.writeObject( updates.get( i ) );
+ }
+
+ queueSize = collectionUpdates.size();
+ log.trace( "starting serialization of [" + queueSize + "] collectionUpdates entries" );
+ oos.writeInt( queueSize );
+ for ( int i = 0; i < queueSize; i++ ) {
+ oos.writeObject( collectionUpdates.get( i ) );
+ }
+
+ queueSize = collectionRemovals.size();
+ log.trace( "starting serialization of [" + queueSize + "] collectionRemovals entries" );
+ oos.writeInt( queueSize );
+ for ( int i = 0; i < queueSize; i++ ) {
+ oos.writeObject( collectionRemovals.get( i ) );
+ }
+
+ queueSize = collectionCreations.size();
+ log.trace( "starting serialization of [" + queueSize + "] collectionCreations entries" );
+ oos.writeInt( queueSize );
+ for ( int i = 0; i < queueSize; i++ ) {
+ oos.writeObject( collectionCreations.get( i ) );
+ }
+ }
+
+ /**
+ * Used by the owning session to explicitly control deserialization of the
+ * action queue
+ *
+ * @param ois The stream from which to read the action queue
+ * @throws IOException
+ */
+ public static ActionQueue deserialize(
+ ObjectInputStream ois,
+ SessionImplementor session) throws IOException, ClassNotFoundException {
+ log.trace( "deserializing action-queue" );
+ ActionQueue rtn = new ActionQueue( session );
+
+ int queueSize = ois.readInt();
+ log.trace( "starting deserialization of [" + queueSize + "] insertions entries" );
+ rtn.insertions = new ArrayList( queueSize );
+ for ( int i = 0; i < queueSize; i++ ) {
+ rtn.insertions.add( ois.readObject() );
+ }
+
+ queueSize = ois.readInt();
+ log.trace( "starting deserialization of [" + queueSize + "] deletions entries" );
+ rtn.deletions = new ArrayList( queueSize );
+ for ( int i = 0; i < queueSize; i++ ) {
+ rtn.deletions.add( ois.readObject() );
+ }
+
+ queueSize = ois.readInt();
+ log.trace( "starting deserialization of [" + queueSize + "] updates entries" );
+ rtn.updates = new ArrayList( queueSize );
+ for ( int i = 0; i < queueSize; i++ ) {
+ rtn.updates.add( ois.readObject() );
+ }
+
+ queueSize = ois.readInt();
+ log.trace( "starting deserialization of [" + queueSize + "] collectionUpdates entries" );
+ rtn.collectionUpdates = new ArrayList( queueSize );
+ for ( int i = 0; i < queueSize; i++ ) {
+ rtn.collectionUpdates.add( ois.readObject() );
+ }
+
+ queueSize = ois.readInt();
+ log.trace( "starting deserialization of [" + queueSize + "] collectionRemovals entries" );
+ rtn.collectionRemovals = new ArrayList( queueSize );
+ for ( int i = 0; i < queueSize; i++ ) {
+ rtn.collectionRemovals.add( ois.readObject() );
+ }
+
+ queueSize = ois.readInt();
+ log.trace( "starting deserialization of [" + queueSize + "] collectionCreations entries" );
+ rtn.collectionCreations = new ArrayList( queueSize );
+ for ( int i = 0; i < queueSize; i++ ) {
+ rtn.collectionCreations.add( ois.readObject() );
+ }
+ return rtn;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/AssociationKey.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/AssociationKey.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/AssociationKey.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,31 @@
+//$Id: AssociationKey.java 7458 2005-07-12 20:12:57Z oneovthafew $
+package org.hibernate.engine;
+
+import java.io.Serializable;
+
+/**
+ * Identifies a named association belonging to a particular
+ * entity instance. Used to record the fact that an association
+ * is null during loading.
+ *
+ * @author Gavin King
+ */
+final class AssociationKey implements Serializable {
+ private EntityKey ownerKey;
+ private String propertyName;
+
+ public AssociationKey(EntityKey ownerKey, String propertyName) {
+ this.ownerKey = ownerKey;
+ this.propertyName = propertyName;
+ }
+
+ public boolean equals(Object that) {
+ AssociationKey key = (AssociationKey) that;
+ return key.propertyName.equals(propertyName) &&
+ key.ownerKey.equals(ownerKey);
+ }
+
+ public int hashCode() {
+ return ownerKey.hashCode() + propertyName.hashCode();
+ }
+}
\ No newline at end of file
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/AssociationKey.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/BatchFetchQueue.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/BatchFetchQueue.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/BatchFetchQueue.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,276 @@
+//$Id: BatchFetchQueue.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.engine;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.commons.collections.SequencedHashMap;
+import org.hibernate.EntityMode;
+import org.hibernate.cache.CacheKey;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.util.MarkerObject;
+
+/**
+ * Tracks entity and collection keys that are available for batch
+ * fetching, and the queries which were used to load entities, which
+ * can be re-used as a subquery for loading owned collections.
+ *
+ * @author Gavin King
+ */
+public class BatchFetchQueue {
+
+ public static final Object MARKER = new MarkerObject( "MARKER" );
+
+ /**
+ * Defines a sequence of {@link EntityKey} elements that are currently
+ * elegible for batch-fetching.
+ * <p/>
+ * Even though this is a map, we only use the keys. A map was chosen in
+ * order to utilize a {@link SequencedHashMap} to maintain sequencing
+ * as well as uniqueness.
+ * <p/>
+ * TODO : this would be better as a SequencedReferenceSet, but no such beast exists!
+ */
+ private final Map batchLoadableEntityKeys = new SequencedHashMap(8);
+
+ /**
+ * A map of {@link SubselectFetch subselect-fetch descriptors} keyed by the
+ * {@link EntityKey) against which the descriptor is registered.
+ */
+ private final Map subselectsByEntityKey = new HashMap(8);
+
+ /**
+ * The owning persistence context.
+ */
+ private final PersistenceContext context;
+
+ /**
+ * Constructs a queue for the given context.
+ *
+ * @param context The owning context.
+ */
+ public BatchFetchQueue(PersistenceContext context) {
+ this.context = context;
+ }
+
+ /**
+ * Clears all entries from this fetch queue.
+ */
+ public void clear() {
+ batchLoadableEntityKeys.clear();
+ subselectsByEntityKey.clear();
+ }
+
+ /**
+ * Retrieve the fetch descriptor associated with the given entity key.
+ *
+ * @param key The entity key for which to locate any defined subselect fetch.
+ * @return The fetch descriptor; may return null if no subselect fetch queued for
+ * this entity key.
+ */
+ public SubselectFetch getSubselect(EntityKey key) {
+ return (SubselectFetch) subselectsByEntityKey.get(key);
+ }
+
+ /**
+ * Adds a subselect fetch decriptor for the given entity key.
+ *
+ * @param key The entity for which to register the subselect fetch.
+ * @param subquery The fetch descriptor.
+ */
+ public void addSubselect(EntityKey key, SubselectFetch subquery) {
+ subselectsByEntityKey.put(key, subquery);
+ }
+
+ /**
+ * After evicting or deleting an entity, we don't need to
+ * know the query that was used to load it anymore (don't
+ * call this after loading the entity, since we might still
+ * need to load its collections)
+ */
+ public void removeSubselect(EntityKey key) {
+ subselectsByEntityKey.remove(key);
+ }
+
+ /**
+ * Clears all pending subselect fetches from the queue.
+ * <p/>
+ * Called after flushing.
+ */
+ public void clearSubselects() {
+ subselectsByEntityKey.clear();
+ }
+
+ /**
+ * If an EntityKey represents a batch loadable entity, add
+ * it to the queue.
+ * <p/>
+ * Note that the contract here is such that any key passed in should
+ * previously have been been checked for existence within the
+ * {@link PersistenceContext}; failure to do so may cause the
+ * referenced entity to be included in a batch even though it is
+ * already associated with the {@link PersistenceContext}.
+ */
+ public void addBatchLoadableEntityKey(EntityKey key) {
+ if ( key.isBatchLoadable() ) {
+ batchLoadableEntityKeys.put( key, MARKER );
+ }
+ }
+
+ /**
+ * After evicting or deleting or loading an entity, we don't
+ * need to batch fetch it anymore, remove it from the queue
+ * if necessary
+ */
+ public void removeBatchLoadableEntityKey(EntityKey key) {
+ if ( key.isBatchLoadable() ) batchLoadableEntityKeys.remove(key);
+ }
+
+ /**
+ * Get a batch of uninitialized collection keys for a given role
+ *
+ * @param collectionPersister The persister for the collection role.
+ * @param id A key that must be included in the batch fetch
+ * @param batchSize the maximum number of keys to return
+ * @return an array of collection keys, of length batchSize (padded with nulls)
+ */
+ public Serializable[] getCollectionBatch(
+ final CollectionPersister collectionPersister,
+ final Serializable id,
+ final int batchSize,
+ final EntityMode entityMode) {
+ Serializable[] keys = new Serializable[batchSize];
+ keys[0] = id;
+ int i = 1;
+ //int count = 0;
+ int end = -1;
+ boolean checkForEnd = false;
+ // this only works because collection entries are kept in a sequenced
+ // map by persistence context (maybe we should do like entities and
+ // keep a separate sequences set...)
+ Iterator iter = context.getCollectionEntries().entrySet().iterator(); //TODO: calling entrySet on an IdentityMap is SLOW!!
+ while ( iter.hasNext() ) {
+ Map.Entry me = (Map.Entry) iter.next();
+
+ CollectionEntry ce = (CollectionEntry) me.getValue();
+ PersistentCollection collection = (PersistentCollection) me.getKey();
+ if ( !collection.wasInitialized() && ce.getLoadedPersister() == collectionPersister ) {
+
+ if ( checkForEnd && i == end ) {
+ return keys; //the first key found after the given key
+ }
+
+ //if ( end == -1 && count > batchSize*10 ) return keys; //try out ten batches, max
+
+ final boolean isEqual = collectionPersister.getKeyType().isEqual(
+ id,
+ ce.getLoadedKey(),
+ entityMode,
+ collectionPersister.getFactory()
+ );
+
+ if ( isEqual ) {
+ end = i;
+ //checkForEnd = false;
+ }
+ else if ( !isCached( ce.getLoadedKey(), collectionPersister, entityMode ) ) {
+ keys[i++] = ce.getLoadedKey();
+ //count++;
+ }
+
+ if ( i == batchSize ) {
+ i = 1; //end of array, start filling again from start
+ if ( end != -1 ) {
+ checkForEnd = true;
+ }
+ }
+ }
+
+ }
+ return keys; //we ran out of keys to try
+ }
+
+ /**
+ * Get a batch of unloaded identifiers for this class, using a slightly
+ * complex algorithm that tries to grab keys registered immediately after
+ * the given key.
+ *
+ * @param persister The persister for the entities being loaded.
+ * @param id The identifier of the entity currently demanding load.
+ * @param batchSize The maximum number of keys to return
+ * @return an array of identifiers, of length batchSize (possibly padded with nulls)
+ */
+ public Serializable[] getEntityBatch(
+ final EntityPersister persister,
+ final Serializable id,
+ final int batchSize,
+ final EntityMode entityMode) {
+ Serializable[] ids = new Serializable[batchSize];
+ ids[0] = id; //first element of array is reserved for the actual instance we are loading!
+ int i = 1;
+ int end = -1;
+ boolean checkForEnd = false;
+
+ Iterator iter = batchLoadableEntityKeys.keySet().iterator();
+ while ( iter.hasNext() ) {
+ EntityKey key = (EntityKey) iter.next();
+ if ( key.getEntityName().equals( persister.getEntityName() ) ) { //TODO: this needn't exclude subclasses...
+ if ( checkForEnd && i == end ) {
+ //the first id found after the given id
+ return ids;
+ }
+ if ( persister.getIdentifierType().isEqual( id, key.getIdentifier(), entityMode ) ) {
+ end = i;
+ }
+ else {
+ if ( !isCached( key, persister, entityMode ) ) {
+ ids[i++] = key.getIdentifier();
+ }
+ }
+ if ( i == batchSize ) {
+ i = 1; //end of array, start filling again from start
+ if (end!=-1) checkForEnd = true;
+ }
+ }
+ }
+ return ids; //we ran out of ids to try
+ }
+
+ private boolean isCached(
+ EntityKey entityKey,
+ EntityPersister persister,
+ EntityMode entityMode) {
+ if ( persister.hasCache() ) {
+ CacheKey key = new CacheKey(
+ entityKey.getIdentifier(),
+ persister.getIdentifierType(),
+ entityKey.getEntityName(),
+ entityMode,
+ context.getSession().getFactory()
+ );
+ return persister.getCacheAccessStrategy().get( key, context.getSession().getTimestamp() ) != null;
+ }
+ return false;
+ }
+
+ private boolean isCached(
+ Serializable collectionKey,
+ CollectionPersister persister,
+ EntityMode entityMode) {
+ if ( persister.hasCache() ) {
+ CacheKey cacheKey = new CacheKey(
+ collectionKey,
+ persister.getKeyType(),
+ persister.getRole(),
+ entityMode,
+ context.getSession().getFactory()
+ );
+ return persister.getCacheAccessStrategy().get( cacheKey, context.getSession().getTimestamp() ) != null;
+ }
+ return false;
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/BatchFetchQueue.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/Cascade.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/Cascade.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/Cascade.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,360 @@
+//$Id: Cascade.java 10720 2006-11-06 11:35:40Z max.andersen at jboss.com $
+package org.hibernate.engine;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.event.EventSource;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.type.AbstractComponentType;
+import org.hibernate.type.AssociationType;
+import org.hibernate.type.CollectionType;
+import org.hibernate.type.EntityType;
+import org.hibernate.type.Type;
+import org.hibernate.util.CollectionHelper;
+
+/**
+ * Delegate responsible for, in conjunction with the various
+ * {@link CascadingAction actions}, implementing cascade processing.
+ *
+ * @author Gavin King
+ * @see CascadingAction
+ */
+public final class Cascade {
+
+ /**
+ * A cascade point that occurs just after the insertion of the parent entity and
+ * just before deletion
+ */
+ public static final int AFTER_INSERT_BEFORE_DELETE = 1;
+ /**
+ * A cascade point that occurs just before the insertion of the parent entity and
+ * just after deletion
+ */
+ public static final int BEFORE_INSERT_AFTER_DELETE = 2;
+ /**
+ * A cascade point that occurs just after the insertion of the parent entity and
+ * just before deletion, inside a collection
+ */
+ public static final int AFTER_INSERT_BEFORE_DELETE_VIA_COLLECTION = 3;
+ /**
+ * A cascade point that occurs just after update of the parent entity
+ */
+ public static final int AFTER_UPDATE = 0;
+ /**
+ * A cascade point that occurs just before the session is flushed
+ */
+ public static final int BEFORE_FLUSH = 0;
+ /**
+ * A cascade point that occurs just after eviction of the parent entity from the
+ * session cache
+ */
+ public static final int AFTER_EVICT = 0;
+ /**
+ * A cascade point that occurs just after locking a transient parent entity into the
+ * session cache
+ */
+ public static final int BEFORE_REFRESH = 0;
+ /**
+ * A cascade point that occurs just after refreshing a parent entity
+ */
+ public static final int AFTER_LOCK = 0;
+ /**
+ * A cascade point that occurs just before merging from a transient parent entity into
+ * the object in the session cache
+ */
+ public static final int BEFORE_MERGE = 0;
+
+
+ private static final Log log = LogFactory.getLog( Cascade.class );
+
+
+ private int cascadeTo;
+ private EventSource eventSource;
+ private CascadingAction action;
+
+ public Cascade(final CascadingAction action, final int cascadeTo, final EventSource eventSource) {
+ this.cascadeTo = cascadeTo;
+ this.eventSource = eventSource;
+ this.action = action;
+ }
+
+ /**
+ * Cascade an action from the parent entity instance to all its children.
+ *
+ * @param persister The parent's entity persister
+ * @param parent The parent reference.
+ * @throws HibernateException
+ */
+ public void cascade(final EntityPersister persister, final Object parent)
+ throws HibernateException {
+ cascade( persister, parent, null );
+ }
+
+ /**
+ * Cascade an action from the parent entity instance to all its children. This
+ * form is typicaly called from within cascade actions.
+ *
+ * @param persister The parent's entity persister
+ * @param parent The parent reference.
+ * @param anything Anything ;) Typically some form of cascade-local cache
+ * which is specific to each CascadingAction type
+ * @throws HibernateException
+ */
+ public void cascade(final EntityPersister persister, final Object parent, final Object anything)
+ throws HibernateException {
+
+ if ( persister.hasCascades() || action.requiresNoCascadeChecking() ) { // performance opt
+ if ( log.isTraceEnabled() ) {
+ log.trace( "processing cascade " + action + " for: " + persister.getEntityName() );
+ }
+
+ Type[] types = persister.getPropertyTypes();
+ CascadeStyle[] cascadeStyles = persister.getPropertyCascadeStyles();
+ EntityMode entityMode = eventSource.getEntityMode();
+ boolean hasUninitializedLazyProperties = persister.hasUninitializedLazyProperties( parent, entityMode );
+ for ( int i=0; i<types.length; i++) {
+ CascadeStyle style = cascadeStyles[i];
+ if ( hasUninitializedLazyProperties && persister.getPropertyLaziness()[i] && ! action.performOnLazyProperty() ) {
+ //do nothing to avoid a lazy property initialization
+ continue;
+ }
+
+ if ( style.doCascade( action ) ) {
+ cascadeProperty(
+ persister.getPropertyValue( parent, i, entityMode ),
+ types[i],
+ style,
+ anything,
+ false
+ );
+ }
+ else if ( action.requiresNoCascadeChecking() ) {
+ action.noCascade(
+ eventSource,
+ persister.getPropertyValue( parent, i, entityMode ),
+ parent,
+ persister,
+ i
+ );
+ }
+ }
+
+ if ( log.isTraceEnabled() ) {
+ log.trace( "done processing cascade " + action + " for: " + persister.getEntityName() );
+ }
+ }
+ }
+
+ /**
+ * Cascade an action to the child or children
+ */
+ private void cascadeProperty(
+ final Object child,
+ final Type type,
+ final CascadeStyle style,
+ final Object anything,
+ final boolean isCascadeDeleteEnabled) throws HibernateException {
+
+ if (child!=null) {
+ if ( type.isAssociationType() ) {
+ AssociationType associationType = (AssociationType) type;
+ if ( cascadeAssociationNow( associationType ) ) {
+ cascadeAssociation(
+ child,
+ type,
+ style,
+ anything,
+ isCascadeDeleteEnabled
+ );
+ }
+ }
+ else if ( type.isComponentType() ) {
+ cascadeComponent( child, (AbstractComponentType) type, anything );
+ }
+ }
+ }
+
+ private boolean cascadeAssociationNow(AssociationType associationType) {
+ return associationType.getForeignKeyDirection().cascadeNow(cascadeTo) &&
+ ( eventSource.getEntityMode()!=EntityMode.DOM4J || associationType.isEmbeddedInXML() );
+ }
+
+ private void cascadeComponent(
+ final Object child,
+ final AbstractComponentType componentType,
+ final Object anything) {
+ Object[] children = componentType.getPropertyValues(child, eventSource);
+ Type[] types = componentType.getSubtypes();
+ for ( int i=0; i<types.length; i++ ) {
+ CascadeStyle componentPropertyStyle = componentType.getCascadeStyle(i);
+ if ( componentPropertyStyle.doCascade(action) ) {
+ cascadeProperty(
+ children[i],
+ types[i],
+ componentPropertyStyle,
+ anything,
+ false
+ );
+ }
+ }
+ }
+
+ private void cascadeAssociation(
+ final Object child,
+ final Type type,
+ final CascadeStyle style,
+ final Object anything,
+ final boolean isCascadeDeleteEnabled) {
+ if ( type.isEntityType() || type.isAnyType() ) {
+ cascadeToOne( child, type, style, anything, isCascadeDeleteEnabled );
+ }
+ else if ( type.isCollectionType() ) {
+ cascadeCollection( child, style, anything, (CollectionType) type );
+ }
+ }
+
+ /**
+ * Cascade an action to a collection
+ */
+ private void cascadeCollection(
+ final Object child,
+ final CascadeStyle style,
+ final Object anything,
+ final CollectionType type) {
+ CollectionPersister persister = eventSource.getFactory()
+ .getCollectionPersister( type.getRole() );
+ Type elemType = persister.getElementType();
+
+ final int oldCascadeTo = cascadeTo;
+ if ( cascadeTo==AFTER_INSERT_BEFORE_DELETE) {
+ cascadeTo = AFTER_INSERT_BEFORE_DELETE_VIA_COLLECTION;
+ }
+
+ //cascade to current collection elements
+ if ( elemType.isEntityType() || elemType.isAnyType() || elemType.isComponentType() ) {
+ cascadeCollectionElements(
+ child,
+ type,
+ style,
+ elemType,
+ anything,
+ persister.isCascadeDeleteEnabled()
+ );
+ }
+
+ cascadeTo = oldCascadeTo;
+ }
+
+ /**
+ * Cascade an action to a to-one association or any type
+ */
+ private void cascadeToOne(
+ final Object child,
+ final Type type,
+ final CascadeStyle style,
+ final Object anything,
+ final boolean isCascadeDeleteEnabled) {
+ final String entityName = type.isEntityType()
+ ? ( (EntityType) type ).getAssociatedEntityName()
+ : null;
+ if ( style.reallyDoCascade(action) ) { //not really necessary, but good for consistency...
+ action.cascade(eventSource, child, entityName, anything, isCascadeDeleteEnabled);
+ }
+ }
+
+ /**
+ * Cascade to the collection elements
+ */
+ private void cascadeCollectionElements(
+ final Object child,
+ final CollectionType collectionType,
+ final CascadeStyle style,
+ final Type elemType,
+ final Object anything,
+ final boolean isCascadeDeleteEnabled) throws HibernateException {
+ // we can't cascade to non-embedded elements
+ boolean embeddedElements = eventSource.getEntityMode()!=EntityMode.DOM4J ||
+ ( (EntityType) collectionType.getElementType( eventSource.getFactory() ) ).isEmbeddedInXML();
+
+ boolean reallyDoCascade = style.reallyDoCascade(action) &&
+ embeddedElements && child!=CollectionType.UNFETCHED_COLLECTION;
+
+ if ( reallyDoCascade ) {
+ if ( log.isTraceEnabled() ) {
+ log.trace( "cascade " + action + " for collection: " + collectionType.getRole() );
+ }
+
+ Iterator iter = action.getCascadableChildrenIterator(eventSource, collectionType, child);
+ while ( iter.hasNext() ) {
+ cascadeProperty(
+ iter.next(),
+ elemType,
+ style,
+ anything,
+ isCascadeDeleteEnabled
+ );
+ }
+
+ if ( log.isTraceEnabled() ) {
+ log.trace( "done cascade " + action + " for collection: " + collectionType.getRole() );
+ }
+ }
+
+ final boolean deleteOrphans = style.hasOrphanDelete() &&
+ action.deleteOrphans() &&
+ elemType.isEntityType() &&
+ child instanceof PersistentCollection; //a newly instantiated collection can't have orphans
+
+ if ( deleteOrphans ) { // handle orphaned entities!!
+ if ( log.isTraceEnabled() ) {
+ log.trace( "deleting orphans for collection: " + collectionType.getRole() );
+ }
+
+ // we can do the cast since orphan-delete does not apply to:
+ // 1. newly instantiated collections
+ // 2. arrays (we can't track orphans for detached arrays)
+ final String entityName = collectionType.getAssociatedEntityName( eventSource.getFactory() );
+ deleteOrphans( entityName, (PersistentCollection) child );
+
+ if ( log.isTraceEnabled() ) {
+ log.trace( "done deleting orphans for collection: " + collectionType.getRole() );
+ }
+ }
+ }
+
+ /**
+ * Delete any entities that were removed from the collection
+ */
+ private void deleteOrphans(String entityName, PersistentCollection pc) throws HibernateException {
+ //TODO: suck this logic into the collection!
+ final Collection orphans;
+ if ( pc.wasInitialized() ) {
+ CollectionEntry ce = eventSource.getPersistenceContext().getCollectionEntry(pc);
+ orphans = ce==null ?
+ CollectionHelper.EMPTY_COLLECTION :
+ ce.getOrphans(entityName, pc);
+ }
+ else {
+ orphans = pc.getQueuedOrphans(entityName);
+ }
+
+ final Iterator orphanIter = orphans.iterator();
+ while ( orphanIter.hasNext() ) {
+ Object orphan = orphanIter.next();
+ if (orphan!=null) {
+ if ( log.isTraceEnabled() ) {
+ log.trace("deleting orphaned entity instance: " + entityName);
+ }
+ eventSource.delete( entityName, orphan, false, null );
+ }
+ }
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/CascadeStyle.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/CascadeStyle.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/CascadeStyle.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,275 @@
+//$Id: CascadeStyle.java 10799 2006-11-13 19:34:33Z steve.ebersole at jboss.com $
+package org.hibernate.engine;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.hibernate.MappingException;
+import org.hibernate.util.ArrayHelper;
+
+/**
+ * A contract for defining the aspects of cascading various persistence actions.
+ *
+ * @see CascadingAction
+ *
+ * @author Gavin King
+ */
+public abstract class CascadeStyle implements Serializable {
+
+ /**
+ * For this style, should the given action be cascaded?
+ *
+ * @param action The action to be checked for cascade-ability.
+ * @return True if the action should be cascaded under this style; false otherwise.
+ */
+ public abstract boolean doCascade(CascadingAction action);
+
+ /**
+ * Probably more aptly named something like doCascadeToCollectionElements(); it is
+ * however used from both the collection and to-one logic branches...
+ * <p/>
+ * For this style, should the given action really be cascaded? The default
+ * implementation is simply to return {@link #doCascade}; for certain
+ * styles (currently only delete-orphan), however, we need to be able to
+ * control this seperately.
+ *
+ * @param action The action to be checked for cascade-ability.
+ * @return True if the action should be really cascaded under this style;
+ * false otherwise.
+ */
+ public boolean reallyDoCascade(CascadingAction action) {
+ return doCascade(action);
+ }
+
+ /**
+ * Do we need to delete orphaned collection elements?
+ *
+ * @return True if this style need to account for orphan delete
+ * operations; false othwerwise.
+ */
+ public boolean hasOrphanDelete() {
+ return false;
+ }
+
+ public static final class MultipleCascadeStyle extends CascadeStyle {
+ private final CascadeStyle[] styles;
+ public MultipleCascadeStyle(CascadeStyle[] styles) {
+ this.styles = styles;
+ }
+ public boolean doCascade(CascadingAction action) {
+ for (int i=0; i<styles.length; i++) {
+ if ( styles[i].doCascade(action) ) return true;
+ }
+ return false;
+ }
+ public boolean reallyDoCascade(CascadingAction action) {
+ for (int i=0; i<styles.length; i++) {
+ if ( styles[i].reallyDoCascade(action) ) return true;
+ }
+ return false;
+ }
+ public boolean hasOrphanDelete() {
+ for (int i=0; i<styles.length; i++) {
+ if ( styles[i].hasOrphanDelete() ) return true;
+ }
+ return false;
+ }
+ public String toString() {
+ return ArrayHelper.toString(styles);
+ }
+ }
+
+ /**
+ * save / delete / update / evict / lock / replicate / merge / persist + delete orphans
+ */
+ public static final CascadeStyle ALL_DELETE_ORPHAN = new CascadeStyle() {
+ public boolean doCascade(CascadingAction action) {
+ return true;
+ }
+ public boolean hasOrphanDelete() {
+ return true;
+ }
+ public String toString() {
+ return "STYLE_ALL_DELETE_ORPHAN";
+ }
+ };
+
+ /**
+ * save / delete / update / evict / lock / replicate / merge / persist
+ */
+ public static final CascadeStyle ALL = new CascadeStyle() {
+ public boolean doCascade(CascadingAction action) {
+ return true;
+ }
+ public String toString() {
+ return "STYLE_ALL";
+ }
+ };
+
+ /**
+ * save / update
+ */
+ public static final CascadeStyle UPDATE = new CascadeStyle() {
+ public boolean doCascade(CascadingAction action) {
+ return action==CascadingAction.SAVE_UPDATE || action==CascadingAction.SAVE_UPDATE_COPY;
+ }
+ public String toString() {
+ return "STYLE_SAVE_UPDATE";
+ }
+ };
+
+ /**
+ * lock
+ */
+ public static final CascadeStyle LOCK = new CascadeStyle() {
+ public boolean doCascade(CascadingAction action) {
+ return action==CascadingAction.LOCK;
+ }
+ public String toString() {
+ return "STYLE_LOCK";
+ }
+ };
+
+ /**
+ * refresh
+ */
+ public static final CascadeStyle REFRESH = new CascadeStyle() {
+ public boolean doCascade(CascadingAction action) {
+ return action==CascadingAction.REFRESH;
+ }
+ public String toString() {
+ return "STYLE_REFRESH";
+ }
+ };
+
+ /**
+ * evict
+ */
+ public static final CascadeStyle EVICT = new CascadeStyle() {
+ public boolean doCascade(CascadingAction action) {
+ return action==CascadingAction.EVICT;
+ }
+ public String toString() {
+ return "STYLE_EVICT";
+ }
+ };
+
+ /**
+ * replicate
+ */
+ public static final CascadeStyle REPLICATE = new CascadeStyle() {
+ public boolean doCascade(CascadingAction action) {
+ return action==CascadingAction.REPLICATE;
+ }
+ public String toString() {
+ return "STYLE_REPLICATE";
+ }
+ };
+ /**
+ * merge
+ */
+ public static final CascadeStyle MERGE = new CascadeStyle() {
+ public boolean doCascade(CascadingAction action) {
+ return action==CascadingAction.MERGE;
+ }
+ public String toString() {
+ return "STYLE_MERGE";
+ }
+ };
+
+ /**
+ * create
+ */
+ public static final CascadeStyle PERSIST = new CascadeStyle() {
+ public boolean doCascade(CascadingAction action) {
+ return action==CascadingAction.PERSIST
+ || action==CascadingAction.PERSIST_ON_FLUSH;
+ }
+ public String toString() {
+ return "STYLE_PERSIST";
+ }
+ };
+
+ /**
+ * delete
+ */
+ public static final CascadeStyle DELETE = new CascadeStyle() {
+ public boolean doCascade(CascadingAction action) {
+ return action==CascadingAction.DELETE;
+ }
+ public String toString() {
+ return "STYLE_DELETE";
+ }
+ };
+
+ /**
+ * delete + delete orphans
+ */
+ public static final CascadeStyle DELETE_ORPHAN = new CascadeStyle() {
+ public boolean doCascade(CascadingAction action) {
+ return action==CascadingAction.DELETE || action==CascadingAction.SAVE_UPDATE;
+ }
+ public boolean reallyDoCascade(CascadingAction action) {
+ return action==CascadingAction.DELETE;
+ }
+ public boolean hasOrphanDelete() {
+ return true;
+ }
+ public String toString() {
+ return "STYLE_DELETE_ORPHAN";
+ }
+ };
+
+ /**
+ * no cascades
+ */
+ public static final CascadeStyle NONE = new CascadeStyle() {
+ public boolean doCascade(CascadingAction action) {
+ return false;
+ }
+ public String toString() {
+ return "STYLE_NONE";
+ }
+ };
+
+ /**
+ * package-protected constructor
+ */
+ CascadeStyle() {
+ }
+
+ static final Map STYLES = new HashMap();
+
+ static {
+ STYLES.put( "all", ALL );
+ STYLES.put( "all-delete-orphan", ALL_DELETE_ORPHAN );
+ STYLES.put( "save-update", UPDATE );
+ STYLES.put( "persist", PERSIST );
+ STYLES.put( "merge", MERGE );
+ STYLES.put( "lock", LOCK );
+ STYLES.put( "refresh", REFRESH );
+ STYLES.put( "replicate", REPLICATE );
+ STYLES.put( "evict", EVICT );
+ STYLES.put( "delete", DELETE );
+ STYLES.put( "remove", DELETE ); // adds remove as a sort-of alias for delete...
+ STYLES.put( "delete-orphan", DELETE_ORPHAN );
+ STYLES.put( "none", NONE );
+ }
+
+ /**
+ * Factory method for obtaining named cascade styles
+ *
+ * @param cascade The named cascade style name.
+ * @return The appropriate CascadeStyle
+ */
+ public static CascadeStyle getCascadeStyle(String cascade) {
+ CascadeStyle style = (CascadeStyle) STYLES.get(cascade);
+ if (style==null) {
+ throw new MappingException("Unsupported cascade style: " + cascade);
+ }
+ else {
+ return style;
+ }
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/CascadeStyle.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/CascadingAction.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/CascadingAction.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/CascadingAction.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,438 @@
+//$Id: CascadingAction.java 10451 2006-09-05 06:53:46Z epbernard $
+package org.hibernate.engine;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.HibernateException;
+import org.hibernate.LockMode;
+import org.hibernate.ReplicationMode;
+import org.hibernate.TransientObjectException;
+import org.hibernate.proxy.HibernateProxy;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.event.EventSource;
+import org.hibernate.type.CollectionType;
+import org.hibernate.type.Type;
+import org.hibernate.type.EntityType;
+
+/**
+ * A session action that may be cascaded from parent entity to its children
+ *
+ * @author Gavin King
+ */
+public abstract class CascadingAction {
+
+ private static final Log log = LogFactory.getLog( CascadingAction.class );
+
+
+ // the CascadingAction contract ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * protected constructor
+ */
+ CascadingAction() {
+ }
+
+ /**
+ * Cascade the action to the child object.
+ *
+ * @param session The session within which the cascade is occuring.
+ * @param child The child to which cascading should be performed.
+ * @param entityName The child's entity name
+ * @param anything Anything ;) Typically some form of cascade-local cache
+ * which is specific to each CascadingAction type
+ * @param isCascadeDeleteEnabled Are cascading deletes enabled.
+ * @throws HibernateException
+ */
+ public abstract void cascade(
+ EventSource session,
+ Object child,
+ String entityName,
+ Object anything,
+ boolean isCascadeDeleteEnabled) throws HibernateException;
+
+ /**
+ * Given a collection, get an iterator of the children upon which the
+ * current cascading action should be visited.
+ *
+ * @param session The session within which the cascade is occuring.
+ * @param collectionType The mapping type of the collection.
+ * @param collection The collection instance.
+ * @return The children iterator.
+ */
+ public abstract Iterator getCascadableChildrenIterator(
+ EventSource session,
+ CollectionType collectionType,
+ Object collection);
+
+ /**
+ * Does this action potentially extrapolate to orphan deletes?
+ *
+ * @return True if this action can lead to deletions of orphans.
+ */
+ public abstract boolean deleteOrphans();
+
+
+ /**
+ * Does the specified cascading action require verification of no cascade validity?
+ *
+ * @return True if this action requires no-cascade verification; false otherwise.
+ */
+ public boolean requiresNoCascadeChecking() {
+ return false;
+ }
+
+ /**
+ * Called (in the case of {@link #requiresNoCascadeChecking} returning true) to validate
+ * that no cascade on the given property is considered a valid semantic.
+ *
+ * @param session The session witin which the cascade is occurring.
+ * @param child The property value
+ * @param parent The property value owner
+ * @param persister The entity persister for the owner
+ * @param propertyIndex The index of the property within the owner.
+ */
+ public void noCascade(EventSource session, Object child, Object parent, EntityPersister persister, int propertyIndex) {
+ }
+
+ /**
+ * Should this action be performed (or noCascade consulted) in the case of lazy properties.
+ */
+ public boolean performOnLazyProperty() {
+ return true;
+ }
+
+
+ // the CascadingAction implementations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * @see org.hibernate.Session#delete(Object)
+ */
+ public static final CascadingAction DELETE = new CascadingAction() {
+ public void cascade(EventSource session, Object child, String entityName, Object anything, boolean isCascadeDeleteEnabled)
+ throws HibernateException {
+ if ( log.isTraceEnabled() ) {
+ log.trace("cascading to delete: " + entityName);
+ }
+ session.delete( entityName, child, isCascadeDeleteEnabled, ( Set ) anything );
+ }
+ public Iterator getCascadableChildrenIterator(EventSource session, CollectionType collectionType, Object collection) {
+ // delete does cascade to uninitialized collections
+ return CascadingAction.getAllElementsIterator(session, collectionType, collection);
+ }
+ public boolean deleteOrphans() {
+ // orphans should be deleted during delete
+ return true;
+ }
+ public String toString() {
+ return "ACTION_DELETE";
+ }
+ };
+
+ /**
+ * @see org.hibernate.Session#lock(Object, LockMode)
+ */
+ public static final CascadingAction LOCK = new CascadingAction() {
+ public void cascade(EventSource session, Object child, String entityName, Object anything, boolean isCascadeDeleteEnabled)
+ throws HibernateException {
+ if ( log.isTraceEnabled() ) {
+ log.trace( "cascading to lock: " + entityName );
+ }
+ session.lock( entityName, child, LockMode.NONE/*(LockMode) anything*/ );
+ }
+ public Iterator getCascadableChildrenIterator(EventSource session, CollectionType collectionType, Object collection) {
+ // lock doesn't cascade to uninitialized collections
+ return getLoadedElementsIterator(session, collectionType, collection);
+ }
+ public boolean deleteOrphans() {
+ //TODO: should orphans really be deleted during lock???
+ return false;
+ }
+ public String toString() {
+ return "ACTION_LOCK";
+ }
+ };
+
+ /**
+ * @see org.hibernate.Session#refresh(Object)
+ */
+ public static final CascadingAction REFRESH = new CascadingAction() {
+ public void cascade(EventSource session, Object child, String entityName, Object anything, boolean isCascadeDeleteEnabled)
+ throws HibernateException {
+ if ( log.isTraceEnabled() ) {
+ log.trace( "cascading to refresh: " + entityName );
+ }
+ session.refresh( child, (Map) anything );
+ }
+ public Iterator getCascadableChildrenIterator(EventSource session, CollectionType collectionType, Object collection) {
+ // refresh doesn't cascade to uninitialized collections
+ return getLoadedElementsIterator(session, collectionType, collection);
+ }
+ public boolean deleteOrphans() {
+ return false;
+ }
+ public String toString() {
+ return "ACTION_REFRESH";
+ }
+ };
+
+ /**
+ * @see org.hibernate.Session#evict(Object)
+ */
+ public static final CascadingAction EVICT = new CascadingAction() {
+ public void cascade(EventSource session, Object child, String entityName, Object anything, boolean isCascadeDeleteEnabled)
+ throws HibernateException {
+ if ( log.isTraceEnabled() ) {
+ log.trace( "cascading to evict: " + entityName );
+ }
+ session.evict(child);
+ }
+ public Iterator getCascadableChildrenIterator(EventSource session, CollectionType collectionType, Object collection) {
+ // evicts don't cascade to uninitialized collections
+ return getLoadedElementsIterator(session, collectionType, collection);
+ }
+ public boolean deleteOrphans() {
+ return false;
+ }
+ public boolean performOnLazyProperty() {
+ return false;
+ }
+ public String toString() {
+ return "ACTION_EVICT";
+ }
+ };
+
+ /**
+ * @see org.hibernate.Session#saveOrUpdate(Object)
+ */
+ public static final CascadingAction SAVE_UPDATE = new CascadingAction() {
+ public void cascade(EventSource session, Object child, String entityName, Object anything, boolean isCascadeDeleteEnabled)
+ throws HibernateException {
+ if ( log.isTraceEnabled() ) {
+ log.trace( "cascading to saveOrUpdate: " + entityName );
+ }
+ session.saveOrUpdate(entityName, child);
+ }
+ public Iterator getCascadableChildrenIterator(EventSource session, CollectionType collectionType, Object collection) {
+ // saves / updates don't cascade to uninitialized collections
+ return getLoadedElementsIterator(session, collectionType, collection);
+ }
+ public boolean deleteOrphans() {
+ // orphans should be deleted during save/update
+ return true;
+ }
+ public boolean performOnLazyProperty() {
+ return false;
+ }
+ public String toString() {
+ return "ACTION_SAVE_UPDATE";
+ }
+ };
+
+ /**
+ * @see org.hibernate.Session#merge(Object)
+ */
+ public static final CascadingAction MERGE = new CascadingAction() {
+ public void cascade(EventSource session, Object child, String entityName, Object anything, boolean isCascadeDeleteEnabled)
+ throws HibernateException {
+ if ( log.isTraceEnabled() ) {
+ log.trace( "cascading to merge: " + entityName );
+ }
+ session.merge( entityName, child, (Map) anything );
+ }
+ public Iterator getCascadableChildrenIterator(EventSource session, CollectionType collectionType, Object collection) {
+ // merges don't cascade to uninitialized collections
+// //TODO: perhaps this does need to cascade after all....
+ return getLoadedElementsIterator(session, collectionType, collection);
+ }
+ public boolean deleteOrphans() {
+ // orphans should not be deleted during merge??
+ return false;
+ }
+ public String toString() {
+ return "ACTION_MERGE";
+ }
+ };
+
+ /**
+ * @see org.hibernate.classic.Session#saveOrUpdateCopy(Object)
+ */
+ public static final CascadingAction SAVE_UPDATE_COPY = new CascadingAction() {
+ // for deprecated saveOrUpdateCopy()
+ public void cascade(EventSource session, Object child, String entityName, Object anything, boolean isCascadeDeleteEnabled)
+ throws HibernateException {
+ if ( log.isTraceEnabled() ) {
+ log.trace( "cascading to saveOrUpdateCopy: " + entityName );
+ }
+ session.saveOrUpdateCopy( entityName, child, (Map) anything );
+ }
+ public Iterator getCascadableChildrenIterator(EventSource session, CollectionType collectionType, Object collection) {
+ // saves / updates don't cascade to uninitialized collections
+ return getLoadedElementsIterator(session, collectionType, collection);
+ }
+ public boolean deleteOrphans() {
+ // orphans should not be deleted during copy??
+ return false;
+ }
+ public String toString() {
+ return "ACTION_SAVE_UPDATE_COPY";
+ }
+ };
+
+ /**
+ * @see org.hibernate.Session#persist(Object)
+ */
+ public static final CascadingAction PERSIST = new CascadingAction() {
+ public void cascade(EventSource session, Object child, String entityName, Object anything, boolean isCascadeDeleteEnabled)
+ throws HibernateException {
+ if ( log.isTraceEnabled() ) {
+ log.trace( "cascading to persist: " + entityName );
+ }
+ session.persist( entityName, child, (Map) anything );
+ }
+ public Iterator getCascadableChildrenIterator(EventSource session, CollectionType collectionType, Object collection) {
+ // persists don't cascade to uninitialized collections
+ return CascadingAction.getAllElementsIterator(session, collectionType, collection);
+ }
+ public boolean deleteOrphans() {
+ return false;
+ }
+ public boolean performOnLazyProperty() {
+ return false;
+ }
+ public String toString() {
+ return "ACTION_PERSIST";
+ }
+ };
+
+ /**
+ * Execute persist during flush time
+ *
+ * @see org.hibernate.Session#persist(Object)
+ */
+ public static final CascadingAction PERSIST_ON_FLUSH = new CascadingAction() {
+ public void cascade(EventSource session, Object child, String entityName, Object anything, boolean isCascadeDeleteEnabled)
+ throws HibernateException {
+ if ( log.isTraceEnabled() ) {
+ log.trace( "cascading to persistOnFlush: " + entityName );
+ }
+ session.persistOnFlush( entityName, child, (Map) anything );
+ }
+ public Iterator getCascadableChildrenIterator(EventSource session, CollectionType collectionType, Object collection) {
+ // persists don't cascade to uninitialized collections
+ return CascadingAction.getLoadedElementsIterator(session, collectionType, collection);
+ }
+ public boolean deleteOrphans() {
+ return true;
+ }
+ public boolean requiresNoCascadeChecking() {
+ return true;
+ }
+ public void noCascade(
+ EventSource session,
+ Object child,
+ Object parent,
+ EntityPersister persister,
+ int propertyIndex) {
+ if ( child == null ) {
+ return;
+ }
+ Type type = persister.getPropertyTypes()[propertyIndex];
+ if ( type.isEntityType() ) {
+ String childEntityName = ( ( EntityType ) type ).getAssociatedEntityName( session.getFactory() );
+
+ if ( ! isInManagedState( child, session )
+ && ! ( child instanceof HibernateProxy ) //a proxy cannot be transient and it breaks ForeignKeys.isTransient
+ && ForeignKeys.isTransient( childEntityName, child, null, session ) ) {
+ String parentEntiytName = persister.getEntityName();
+ String propertyName = persister.getPropertyNames()[propertyIndex];
+ throw new TransientObjectException(
+ "object references an unsaved transient instance - " +
+ "save the transient instance before flushing: " +
+ parentEntiytName + "." + propertyName + " -> " + childEntityName
+ );
+
+ }
+ }
+ }
+ public boolean performOnLazyProperty() {
+ return false;
+ }
+
+ private boolean isInManagedState(Object child, EventSource session) {
+ EntityEntry entry = session.getPersistenceContext().getEntry( child );
+ return entry != null && (entry.getStatus() == Status.MANAGED || entry.getStatus() == Status.READ_ONLY);
+ }
+
+ public String toString() {
+ return "ACTION_PERSIST_ON_FLUSH";
+ }
+ };
+
+ /**
+ * @see org.hibernate.Session#replicate(Object, org.hibernate.ReplicationMode)
+ */
+ public static final CascadingAction REPLICATE = new CascadingAction() {
+ public void cascade(EventSource session, Object child, String entityName, Object anything, boolean isCascadeDeleteEnabled)
+ throws HibernateException {
+ if ( log.isTraceEnabled() ) {
+ log.trace( "cascading to replicate: " + entityName );
+ }
+ session.replicate( entityName, child, (ReplicationMode) anything );
+ }
+ public Iterator getCascadableChildrenIterator(EventSource session, CollectionType collectionType, Object collection) {
+ // replicate does cascade to uninitialized collections
+ return getLoadedElementsIterator(session, collectionType, collection);
+ }
+ public boolean deleteOrphans() {
+ return false; //I suppose?
+ }
+ public String toString() {
+ return "ACTION_REPLICATE";
+ }
+ };
+
+
+ // static helper methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Given a collection, get an iterator of all its children, loading them
+ * from the database if necessary.
+ *
+ * @param session The session within which the cascade is occuring.
+ * @param collectionType The mapping type of the collection.
+ * @param collection The collection instance.
+ * @return The children iterator.
+ */
+ private static Iterator getAllElementsIterator(
+ EventSource session,
+ CollectionType collectionType,
+ Object collection) {
+ return collectionType.getElementsIterator( collection, session );
+ }
+
+ /**
+ * Iterate just the elements of the collection that are already there. Don't load
+ * any new elements from the database.
+ */
+ public static Iterator getLoadedElementsIterator(SessionImplementor session, CollectionType collectionType, Object collection) {
+ if ( collectionIsInitialized(collection) ) {
+ // handles arrays and newly instantiated collections
+ return collectionType.getElementsIterator(collection, session);
+ }
+ else {
+ // does not handle arrays (thats ok, cos they can't be lazy)
+ // or newly instantiated collections, so we can do the cast
+ return ( (PersistentCollection) collection ).queuedAdditionIterator();
+ }
+ }
+
+ private static boolean collectionIsInitialized(Object collection) {
+ return !(collection instanceof PersistentCollection) || ( (PersistentCollection) collection ).wasInitialized();
+ }
+
+}
\ No newline at end of file
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/CascadingAction.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/CollectionEntry.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/CollectionEntry.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/CollectionEntry.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,397 @@
+//$Id: CollectionEntry.java 9551 2006-03-04 03:49:55Z steve.ebersole at jboss.com $
+package org.hibernate.engine;
+
+import java.io.Serializable;
+import java.io.ObjectOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.util.Collection;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.AssertionFailure;
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.pretty.MessageHelper;
+
+/**
+ * We need an entry to tell us all about the current state
+ * of a collection with respect to its persistent state
+ *
+ * @author Gavin King
+ */
+public final class CollectionEntry implements Serializable {
+
+ private static final Log log = LogFactory.getLog(CollectionEntry.class);
+
+ //ATTRIBUTES MAINTAINED BETWEEN FLUSH CYCLES
+
+ // session-start/post-flush persistent state
+ private Serializable snapshot;
+ // allow the CollectionSnapshot to be serialized
+ private String role;
+
+ // "loaded" means the reference that is consistent
+ // with the current database state
+ private transient CollectionPersister loadedPersister;
+ private Serializable loadedKey;
+
+ // ATTRIBUTES USED ONLY DURING FLUSH CYCLE
+
+ // during flush, we navigate the object graph to
+ // collections and decide what to do with them
+ private transient boolean reached;
+ private transient boolean processed;
+ private transient boolean doupdate;
+ private transient boolean doremove;
+ private transient boolean dorecreate;
+ // if we instantiate a collection during the flush() process,
+ // we must ignore it for the rest of the flush()
+ private transient boolean ignore;
+
+ // "current" means the reference that was found during flush()
+ private transient CollectionPersister currentPersister;
+ private transient Serializable currentKey;
+
+ /**
+ * For newly wrapped collections, or dereferenced collection wrappers
+ */
+ public CollectionEntry(CollectionPersister persister, PersistentCollection collection) {
+ // new collections that get found + wrapped
+ // during flush shouldn't be ignored
+ ignore = false;
+
+ collection.clearDirty(); //a newly wrapped collection is NOT dirty (or we get unnecessary version updates)
+
+ snapshot = persister.isMutable() ?
+ collection.getSnapshot(persister) :
+ null;
+ collection.setSnapshot(loadedKey, role, snapshot);
+ }
+
+ /**
+ * For collections just loaded from the database
+ */
+ public CollectionEntry(
+ final PersistentCollection collection,
+ final CollectionPersister loadedPersister,
+ final Serializable loadedKey,
+ final boolean ignore
+ ) {
+ this.ignore=ignore;
+
+ //collection.clearDirty()
+
+ this.loadedKey = loadedKey;
+ setLoadedPersister(loadedPersister);
+
+ collection.setSnapshot(loadedKey, role, null);
+
+ //postInitialize() will be called after initialization
+ }
+
+ /**
+ * For uninitialized detached collections
+ */
+ public CollectionEntry(CollectionPersister loadedPersister, Serializable loadedKey) {
+ // detached collection wrappers that get found + reattached
+ // during flush shouldn't be ignored
+ ignore = false;
+
+ //collection.clearDirty()
+
+ this.loadedKey = loadedKey;
+ setLoadedPersister(loadedPersister);
+ }
+
+ /**
+ * For initialized detached collections
+ */
+ CollectionEntry(PersistentCollection collection, SessionFactoryImplementor factory)
+ throws MappingException {
+ // detached collections that get found + reattached
+ // during flush shouldn't be ignored
+ ignore = false;
+
+ loadedKey = collection.getKey();
+ setLoadedPersister( factory.getCollectionPersister( collection.getRole() ) );
+
+ snapshot = collection.getStoredSnapshot();
+ }
+
+ /**
+ * Used from custom serialization.
+ *
+ * @see #serialize
+ * @see #deserialize
+ */
+ private CollectionEntry(
+ String role,
+ Serializable snapshot,
+ Serializable loadedKey,
+ SessionFactoryImplementor factory) {
+ this.role = role;
+ this.snapshot = snapshot;
+ this.loadedKey = loadedKey;
+ if ( role != null ) {
+ afterDeserialize( factory );
+ }
+ }
+
+ /**
+ * Determine if the collection is "really" dirty, by checking dirtiness
+ * of the collection elements, if necessary
+ */
+ private void dirty(PersistentCollection collection) throws HibernateException {
+
+ boolean forceDirty = collection.wasInitialized() &&
+ !collection.isDirty() && //optimization
+ getLoadedPersister() != null &&
+ getLoadedPersister().isMutable() && //optimization
+ ( collection.isDirectlyAccessible() || getLoadedPersister().getElementType().isMutable() ) && //optimization
+ !collection.equalsSnapshot( getLoadedPersister() );
+
+ if ( forceDirty ) {
+ collection.dirty();
+ }
+
+ }
+
+ public void preFlush(PersistentCollection collection) throws HibernateException {
+
+ boolean nonMutableChange = collection.isDirty() &&
+ getLoadedPersister()!=null &&
+ !getLoadedPersister().isMutable();
+ if (nonMutableChange) {
+ throw new HibernateException(
+ "changed an immutable collection instance: " +
+ MessageHelper.collectionInfoString( getLoadedPersister().getRole(), getLoadedKey() )
+ );
+ }
+
+ dirty(collection);
+
+ if ( log.isDebugEnabled() && collection.isDirty() && getLoadedPersister() != null ) {
+ log.debug(
+ "Collection dirty: " +
+ MessageHelper.collectionInfoString( getLoadedPersister().getRole(), getLoadedKey() )
+ );
+ }
+
+ setDoupdate(false);
+ setDoremove(false);
+ setDorecreate(false);
+ setReached(false);
+ setProcessed(false);
+ }
+
+ public void postInitialize(PersistentCollection collection) throws HibernateException {
+ snapshot = getLoadedPersister().isMutable() ?
+ collection.getSnapshot( getLoadedPersister() ) :
+ null;
+ collection.setSnapshot(loadedKey, role, snapshot);
+ }
+
+ /**
+ * Called after a successful flush
+ */
+ public void postFlush(PersistentCollection collection) throws HibernateException {
+ if ( isIgnore() ) {
+ ignore = false;
+ }
+ else if ( !isProcessed() ) {
+ throw new AssertionFailure( "collection [" + collection.getRole() + "] was not processed by flush()" );
+ }
+ collection.setSnapshot(loadedKey, role, snapshot);
+ }
+
+ /**
+ * Called after execution of an action
+ */
+ public void afterAction(PersistentCollection collection) {
+ loadedKey = getCurrentKey();
+ setLoadedPersister( getCurrentPersister() );
+
+ boolean resnapshot = collection.wasInitialized() &&
+ ( isDoremove() || isDorecreate() || isDoupdate() );
+ if ( resnapshot ) {
+ snapshot = loadedPersister==null || !loadedPersister.isMutable() ?
+ null :
+ collection.getSnapshot(loadedPersister); //re-snapshot
+ }
+
+ collection.postAction();
+ }
+
+ public Serializable getKey() {
+ return getLoadedKey();
+ }
+
+ public String getRole() {
+ return role;
+ }
+
+ public Serializable getSnapshot() {
+ return snapshot;
+ }
+
+ private void setLoadedPersister(CollectionPersister persister) {
+ loadedPersister = persister;
+ setRole( persister == null ? null : persister.getRole() );
+ }
+
+ void afterDeserialize(SessionFactoryImplementor factory) {
+ loadedPersister = factory.getCollectionPersister(role);
+ }
+
+ public boolean wasDereferenced() {
+ return getLoadedKey() == null;
+ }
+
+ public boolean isReached() {
+ return reached;
+ }
+
+ public void setReached(boolean reached) {
+ this.reached = reached;
+ }
+
+ public boolean isProcessed() {
+ return processed;
+ }
+
+ public void setProcessed(boolean processed) {
+ this.processed = processed;
+ }
+
+ public boolean isDoupdate() {
+ return doupdate;
+ }
+
+ public void setDoupdate(boolean doupdate) {
+ this.doupdate = doupdate;
+ }
+
+ public boolean isDoremove() {
+ return doremove;
+ }
+
+ public void setDoremove(boolean doremove) {
+ this.doremove = doremove;
+ }
+
+ public boolean isDorecreate() {
+ return dorecreate;
+ }
+
+ public void setDorecreate(boolean dorecreate) {
+ this.dorecreate = dorecreate;
+ }
+
+ public boolean isIgnore() {
+ return ignore;
+ }
+
+ public CollectionPersister getCurrentPersister() {
+ return currentPersister;
+ }
+
+ public void setCurrentPersister(CollectionPersister currentPersister) {
+ this.currentPersister = currentPersister;
+ }
+
+ /**
+ * This is only available late during the flush
+ * cycle
+ */
+ public Serializable getCurrentKey() {
+ return currentKey;
+ }
+
+ public void setCurrentKey(Serializable currentKey) {
+ this.currentKey = currentKey;
+ }
+
+ /**
+ * This is only available late during the flush cycle
+ */
+ public CollectionPersister getLoadedPersister() {
+ return loadedPersister;
+ }
+
+ public Serializable getLoadedKey() {
+ return loadedKey;
+ }
+
+ public void setRole(String role) {
+ this.role = role;
+ }
+
+ public String toString() {
+ String result = "CollectionEntry" +
+ MessageHelper.collectionInfoString( loadedPersister.getRole(), loadedKey );
+ if (currentPersister!=null) {
+ result += "->" +
+ MessageHelper.collectionInfoString( currentPersister.getRole(), currentKey );
+ }
+ return result;
+ }
+
+ /**
+ * Get the collection orphans (entities which were removed from the collection)
+ */
+ public Collection getOrphans(String entityName, PersistentCollection collection)
+ throws HibernateException {
+ if (snapshot==null) {
+ throw new AssertionFailure("no collection snapshot for orphan delete");
+ }
+ return collection.getOrphans( snapshot, entityName );
+ }
+
+ public boolean isSnapshotEmpty(PersistentCollection collection) {
+ //TODO: does this really need to be here?
+ // does the collection already have
+ // it's own up-to-date snapshot?
+ return collection.wasInitialized() &&
+ ( getLoadedPersister()==null || getLoadedPersister().isMutable() ) &&
+ collection.isSnapshotEmpty( getSnapshot() );
+ }
+
+
+
+ /**
+ * Custom serialization routine used during serialization of a
+ * Session/PersistenceContext for increased performance.
+ *
+ * @param oos The stream to which we should write the serial data.
+ * @throws java.io.IOException
+ */
+ void serialize(ObjectOutputStream oos) throws IOException {
+ oos.writeObject( role );
+ oos.writeObject( snapshot );
+ oos.writeObject( loadedKey );
+ }
+
+ /**
+ * Custom deserialization routine used during deserialization of a
+ * Session/PersistenceContext for increased performance.
+ *
+ * @param ois The stream from which to read the entry.
+ * @param session The session being deserialized.
+ * @return The deserialized CollectionEntry
+ * @throws IOException
+ * @throws ClassNotFoundException
+ */
+ static CollectionEntry deserialize(
+ ObjectInputStream ois,
+ SessionImplementor session) throws IOException, ClassNotFoundException {
+ return new CollectionEntry(
+ ( String ) ois.readObject(),
+ ( Serializable ) ois.readObject(),
+ ( Serializable ) ois.readObject(),
+ session.getFactory()
+ );
+ }
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/CollectionKey.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/CollectionKey.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/CollectionKey.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,113 @@
+//$Id: CollectionKey.java 9194 2006-02-01 19:59:07Z steveebersole $
+package org.hibernate.engine;
+
+import org.hibernate.EntityMode;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.pretty.MessageHelper;
+import org.hibernate.type.Type;
+
+
+
+import java.io.Serializable;
+import java.io.ObjectOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+
+/**
+ * Uniquely identifies a collection instance in a particular session.
+ *
+ * @author Gavin King
+ */
+public final class CollectionKey implements Serializable {
+
+ private final String role;
+ private final Serializable key;
+ private final Type keyType;
+ private final SessionFactoryImplementor factory;
+ private final int hashCode;
+ private EntityMode entityMode;
+
+ public CollectionKey(CollectionPersister persister, Serializable key, EntityMode em) {
+ this( persister.getRole(), key, persister.getKeyType(), em, persister.getFactory() );
+ }
+
+ private CollectionKey(
+ String role,
+ Serializable key,
+ Type keyType,
+ EntityMode entityMode,
+ SessionFactoryImplementor factory) {
+ this.role = role;
+ this.key = key;
+ this.keyType = keyType;
+ this.entityMode = entityMode;
+ this.factory = factory;
+ this.hashCode = generateHashCode(); //cache the hashcode
+ }
+
+ public boolean equals(Object other) {
+ CollectionKey that = (CollectionKey) other;
+ return that.role.equals(role) &&
+ keyType.isEqual(that.key, key, entityMode, factory);
+ }
+
+ public int generateHashCode() {
+ int result = 17;
+ result = 37 * result + role.hashCode();
+ result = 37 * result + keyType.getHashCode(key, entityMode, factory);
+ return result;
+ }
+
+ public int hashCode() {
+ return hashCode;
+ }
+
+ public String getRole() {
+ return role;
+ }
+
+ public Serializable getKey() {
+ return key;
+ }
+
+ public String toString() {
+ return "CollectionKey" +
+ MessageHelper.collectionInfoString( factory.getCollectionPersister(role), key, factory );
+ }
+
+ /**
+ * Custom serialization routine used during serialization of a
+ * Session/PersistenceContext for increased performance.
+ *
+ * @param oos The stream to which we should write the serial data.
+ * @throws java.io.IOException
+ */
+ void serialize(ObjectOutputStream oos) throws IOException {
+ oos.writeObject( role );
+ oos.writeObject( key );
+ oos.writeObject( keyType );
+ oos.writeObject( entityMode.toString() );
+ }
+
+ /**
+ * Custom deserialization routine used during deserialization of a
+ * Session/PersistenceContext for increased performance.
+ *
+ * @param ois The stream from which to read the entry.
+ * @param session The session being deserialized.
+ * @return The deserialized CollectionKey
+ * @throws IOException
+ * @throws ClassNotFoundException
+ */
+ static CollectionKey deserialize(
+ ObjectInputStream ois,
+ SessionImplementor session) throws IOException, ClassNotFoundException {
+ return new CollectionKey(
+ ( String ) ois.readObject(),
+ ( Serializable ) ois.readObject(),
+ ( Type ) ois.readObject(),
+ EntityMode.parse( ( String ) ois.readObject() ),
+ session.getFactory()
+ );
+ }
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/Collections.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/Collections.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/Collections.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,254 @@
+//$Id: Collections.java 8694 2005-11-28 19:28:17Z steveebersole $
+package org.hibernate.engine;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.AssertionFailure;
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.pretty.MessageHelper;
+import org.hibernate.type.CollectionType;
+
+import java.io.Serializable;
+
+/**
+ * Implements book-keeping for the collection persistence by reachability algorithm
+ * @author Gavin King
+ */
+public final class Collections {
+
+ private Collections() {}
+
+ private static final Log log = LogFactory.getLog(Collections.class);
+
+ /**
+ * record the fact that this collection was dereferenced
+ *
+ * @param coll The collection to be updated by unreachability.
+ * @throws HibernateException
+ */
+ public static void processUnreachableCollection(PersistentCollection coll, SessionImplementor session)
+ throws HibernateException {
+
+ if ( coll.getOwner()==null ) {
+ processNeverReferencedCollection(coll, session);
+ }
+ else {
+ processDereferencedCollection(coll, session);
+ }
+
+ }
+
+ private static void processDereferencedCollection(PersistentCollection coll, SessionImplementor session)
+ throws HibernateException {
+
+ final PersistenceContext persistenceContext = session.getPersistenceContext();
+ CollectionEntry entry = persistenceContext.getCollectionEntry(coll);
+ final CollectionPersister loadedPersister = entry.getLoadedPersister();
+
+ if ( log.isDebugEnabled() && loadedPersister != null )
+ log.debug(
+ "Collection dereferenced: " +
+ MessageHelper.collectionInfoString(
+ loadedPersister,
+ entry.getLoadedKey(),
+ session.getFactory()
+ )
+ );
+
+ // do a check
+ boolean hasOrphanDelete = loadedPersister != null &&
+ loadedPersister.hasOrphanDelete();
+ if (hasOrphanDelete) {
+ Serializable ownerId = loadedPersister.getOwnerEntityPersister()
+ .getIdentifier( coll.getOwner(), session.getEntityMode() );
+ if ( ownerId == null ) {
+ // the owning entity may have been deleted and its identifier unset due to
+ // identifier-rollback; in which case, try to look up its identifier from
+ // the persistence context
+ if ( session.getFactory().getSettings().isIdentifierRollbackEnabled() ) {
+ EntityEntry ownerEntry = persistenceContext.getEntry( coll.getOwner() );
+ if ( ownerEntry != null ) {
+ ownerId = ownerEntry.getId();
+ }
+ }
+ if ( ownerId == null ) {
+ throw new AssertionFailure( "Unable to determine collection owner identifier for orphan-delete processing" );
+ }
+ }
+ EntityKey key = new EntityKey(
+ ownerId,
+ loadedPersister.getOwnerEntityPersister(),
+ session.getEntityMode()
+ );
+ Object owner = persistenceContext.getEntity(key);
+ if ( owner == null ) {
+ throw new AssertionFailure(
+ "collection owner not associated with session: " +
+ loadedPersister.getRole()
+ );
+ }
+ EntityEntry e = persistenceContext.getEntry(owner);
+ //only collections belonging to deleted entities are allowed to be dereferenced in the case of orphan delete
+ if ( e != null && e.getStatus() != Status.DELETED && e.getStatus() != Status.GONE ) {
+ throw new HibernateException(
+ "A collection with cascade=\"all-delete-orphan\" was no longer referenced by the owning entity instance: " +
+ loadedPersister.getRole()
+ );
+ }
+ }
+
+ // do the work
+ entry.setCurrentPersister(null);
+ entry.setCurrentKey(null);
+ prepareCollectionForUpdate( coll, entry, session.getEntityMode(), session.getFactory() );
+
+ }
+
+ private static void processNeverReferencedCollection(PersistentCollection coll, SessionImplementor session)
+ throws HibernateException {
+
+ final PersistenceContext persistenceContext = session.getPersistenceContext();
+ CollectionEntry entry = persistenceContext.getCollectionEntry(coll);
+
+ log.debug(
+ "Found collection with unloaded owner: " +
+ MessageHelper.collectionInfoString(
+ entry.getLoadedPersister(),
+ entry.getLoadedKey(),
+ session.getFactory()
+ )
+ );
+
+ entry.setCurrentPersister( entry.getLoadedPersister() );
+ entry.setCurrentKey( entry.getLoadedKey() );
+
+ prepareCollectionForUpdate( coll, entry, session.getEntityMode(), session.getFactory() );
+
+ }
+
+ /**
+ * Initialize the role of the collection.
+ *
+ * @param collection The collection to be updated by reachibility.
+ * @param type The type of the collection.
+ * @param entity The owner of the collection.
+ * @throws HibernateException
+ */
+ public static void processReachableCollection(
+ PersistentCollection collection,
+ CollectionType type,
+ Object entity,
+ SessionImplementor session)
+ throws HibernateException {
+
+ collection.setOwner(entity);
+
+ CollectionEntry ce = session.getPersistenceContext().getCollectionEntry(collection);
+
+ if ( ce == null ) {
+ // refer to comment in StatefulPersistenceContext.addCollection()
+ throw new HibernateException(
+ "Found two representations of same collection: " +
+ type.getRole()
+ );
+ }
+
+ // The CollectionEntry.isReached() stuff is just to detect any silly users
+ // who set up circular or shared references between/to collections.
+ if ( ce.isReached() ) {
+ // We've been here before
+ throw new HibernateException(
+ "Found shared references to a collection: " +
+ type.getRole()
+ );
+ }
+ ce.setReached(true);
+
+ SessionFactoryImplementor factory = session.getFactory();
+ CollectionPersister persister = factory.getCollectionPersister( type.getRole() );
+ ce.setCurrentPersister(persister);
+ ce.setCurrentKey( type.getKeyOfOwner(entity, session) ); //TODO: better to pass the id in as an argument?
+
+ if ( log.isDebugEnabled() ) {
+ log.debug(
+ "Collection found: " +
+ MessageHelper.collectionInfoString( persister, ce.getCurrentKey(), factory ) +
+ ", was: " +
+ MessageHelper.collectionInfoString( ce.getLoadedPersister(), ce.getLoadedKey(), factory ) +
+ ( collection.wasInitialized() ? " (initialized)" : " (uninitialized)" )
+ );
+ }
+
+ prepareCollectionForUpdate( collection, ce, session.getEntityMode(), factory );
+
+ }
+
+ /**
+ * 1. record the collection role that this collection is referenced by
+ * 2. decide if the collection needs deleting/creating/updating (but
+ * don't actually schedule the action yet)
+ */
+ private static void prepareCollectionForUpdate(
+ PersistentCollection collection,
+ CollectionEntry entry,
+ EntityMode entityMode,
+ SessionFactoryImplementor factory)
+ throws HibernateException {
+
+ if ( entry.isProcessed() ) {
+ throw new AssertionFailure( "collection was processed twice by flush()" );
+ }
+ entry.setProcessed(true);
+
+ final CollectionPersister loadedPersister = entry.getLoadedPersister();
+ final CollectionPersister currentPersister = entry.getCurrentPersister();
+ if ( loadedPersister != null || currentPersister != null ) { // it is or was referenced _somewhere_
+
+ boolean ownerChanged = loadedPersister != currentPersister || // if either its role changed,
+ !currentPersister
+ .getKeyType().isEqual( // or its key changed
+ entry.getLoadedKey(),
+ entry.getCurrentKey(),
+ entityMode, factory
+ );
+
+ if (ownerChanged) {
+
+ // do a check
+ final boolean orphanDeleteAndRoleChanged = loadedPersister != null &&
+ currentPersister != null &&
+ loadedPersister.hasOrphanDelete();
+
+ if (orphanDeleteAndRoleChanged) {
+ throw new HibernateException(
+ "Don't change the reference to a collection with cascade=\"all-delete-orphan\": " +
+ loadedPersister.getRole()
+ );
+ }
+
+ // do the work
+ if ( currentPersister != null ) {
+ entry.setDorecreate(true); // we will need to create new entries
+ }
+
+ if ( loadedPersister != null ) {
+ entry.setDoremove(true); // we will need to remove ye olde entries
+ if ( entry.isDorecreate() ) {
+ log.trace( "Forcing collection initialization" );
+ collection.forceInitialization(); // force initialize!
+ }
+ }
+
+ }
+ else if ( collection.isDirty() ) { // else if it's elements changed
+ entry.setDoupdate(true);
+ }
+
+ }
+
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/Collections.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/EntityEntry.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/EntityEntry.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/EntityEntry.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,316 @@
+//$Id: EntityEntry.java 9283 2006-02-14 03:24:18Z steveebersole $
+package org.hibernate.engine;
+
+import java.io.Serializable;
+import java.io.ObjectOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+
+
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.LockMode;
+import org.hibernate.intercept.FieldInterceptionHelper;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.persister.entity.UniqueKeyLoadable;
+import org.hibernate.pretty.MessageHelper;
+
+/**
+ * We need an entry to tell us all about the current state
+ * of an object with respect to its persistent state
+ *
+ * @author Gavin King
+ */
+public final class EntityEntry implements Serializable {
+
+ private LockMode lockMode;
+ private Status status;
+ private final Serializable id;
+ private Object[] loadedState;
+ private Object[] deletedState;
+ private boolean existsInDatabase;
+ private Object version;
+ private transient EntityPersister persister; // for convenience to save some lookups
+ private final EntityMode entityMode;
+ private final String entityName;
+ private boolean isBeingReplicated;
+ private boolean loadedWithLazyPropertiesUnfetched; //NOTE: this is not updated when properties are fetched lazily!
+ private final transient Object rowId;
+
+ EntityEntry(
+ final Status status,
+ final Object[] loadedState,
+ final Object rowId,
+ final Serializable id,
+ final Object version,
+ final LockMode lockMode,
+ final boolean existsInDatabase,
+ final EntityPersister persister,
+ final EntityMode entityMode,
+ final boolean disableVersionIncrement,
+ final boolean lazyPropertiesAreUnfetched) {
+ this.status=status;
+ this.loadedState=loadedState;
+ this.id=id;
+ this.rowId=rowId;
+ this.existsInDatabase=existsInDatabase;
+ this.version=version;
+ this.lockMode=lockMode;
+ this.isBeingReplicated=disableVersionIncrement;
+ this.loadedWithLazyPropertiesUnfetched = lazyPropertiesAreUnfetched;
+ this.persister=persister;
+ this.entityMode = entityMode;
+ this.entityName = persister == null ?
+ null : persister.getEntityName();
+ }
+
+ /**
+ * Used during custom deserialization
+ */
+ private EntityEntry(
+ final SessionFactoryImplementor factory,
+ final String entityName,
+ final Serializable id,
+ final EntityMode entityMode,
+ final Status status,
+ final Object[] loadedState,
+ final Object[] deletedState,
+ final Object version,
+ final LockMode lockMode,
+ final boolean existsInDatabase,
+ final boolean isBeingReplicated,
+ final boolean loadedWithLazyPropertiesUnfetched) {
+ this.entityName = entityName;
+ this.persister = factory.getEntityPersister( entityName );
+ this.id = id;
+ this.entityMode = entityMode;
+ this.status = status;
+ this.loadedState = loadedState;
+ this.deletedState = deletedState;
+ this.version = version;
+ this.lockMode = lockMode;
+ this.existsInDatabase = existsInDatabase;
+ this.isBeingReplicated = isBeingReplicated;
+ this.loadedWithLazyPropertiesUnfetched = loadedWithLazyPropertiesUnfetched;
+ this.rowId = null; // this is equivalent to the old behavior...
+ }
+
+ public LockMode getLockMode() {
+ return lockMode;
+ }
+
+ public void setLockMode(LockMode lockMode) {
+ this.lockMode = lockMode;
+ }
+
+ public Status getStatus() {
+ return status;
+ }
+
+ public void setStatus(Status status) {
+ if (status==Status.READ_ONLY) {
+ loadedState = null; //memory optimization
+ }
+ this.status = status;
+ }
+
+ public Serializable getId() {
+ return id;
+ }
+
+ public Object[] getLoadedState() {
+ return loadedState;
+ }
+
+ public Object[] getDeletedState() {
+ return deletedState;
+ }
+
+ public void setDeletedState(Object[] deletedState) {
+ this.deletedState = deletedState;
+ }
+
+ public boolean isExistsInDatabase() {
+ return existsInDatabase;
+ }
+
+ public Object getVersion() {
+ return version;
+ }
+
+ public EntityPersister getPersister() {
+ return persister;
+ }
+
+ void afterDeserialize(SessionFactoryImplementor factory) {
+ persister = factory.getEntityPersister( entityName );
+ }
+
+ public String getEntityName() {
+ return entityName;
+ }
+
+ public boolean isBeingReplicated() {
+ return isBeingReplicated;
+ }
+
+ public Object getRowId() {
+ return rowId;
+ }
+
+ /**
+ * After actually updating the database, update the snapshot information,
+ * and escalate the lock mode
+ */
+ public void postUpdate(Object entity, Object[] updatedState, Object nextVersion) {
+ this.loadedState = updatedState;
+
+ setLockMode(LockMode.WRITE);
+
+ if ( getPersister().isVersioned() ) {
+ this.version = nextVersion;
+ getPersister().setPropertyValue(
+ entity,
+ getPersister().getVersionProperty(),
+ nextVersion,
+ entityMode
+ );
+ }
+
+ FieldInterceptionHelper.clearDirty( entity );
+ }
+
+ /**
+ * After actually deleting a row, record the fact that the instance no longer
+ * exists in the database
+ */
+ public void postDelete() {
+ status = Status.GONE;
+ existsInDatabase = false;
+ }
+
+ /**
+ * After actually inserting a row, record the fact that the instance exists on the
+ * database (needed for identity-column key generation)
+ */
+ public void postInsert() {
+ existsInDatabase = true;
+ }
+
+ public boolean isNullifiable(boolean earlyInsert, SessionImplementor session) {
+ return getStatus() == Status.SAVING || (
+ earlyInsert ?
+ !isExistsInDatabase() :
+ session.getPersistenceContext().getNullifiableEntityKeys()
+ .contains( new EntityKey( getId(), getPersister(), entityMode ) )
+ );
+ }
+
+ public Object getLoadedValue(String propertyName) {
+ int propertyIndex = ( (UniqueKeyLoadable) persister ).getPropertyIndex(propertyName);
+ return loadedState[propertyIndex];
+ }
+
+
+ public boolean requiresDirtyCheck(Object entity) {
+
+ boolean isMutableInstance =
+ status != Status.READ_ONLY &&
+ persister.isMutable();
+
+ return isMutableInstance && (
+ getPersister().hasMutableProperties() ||
+ !FieldInterceptionHelper.isInstrumented( entity ) ||
+ FieldInterceptionHelper.extractFieldInterceptor( entity).isDirty()
+ );
+
+ }
+
+ public void forceLocked(Object entity, Object nextVersion) {
+ version = nextVersion;
+ loadedState[ persister.getVersionProperty() ] = version;
+ setLockMode( LockMode.FORCE );
+ persister.setPropertyValue(
+ entity,
+ getPersister().getVersionProperty(),
+ nextVersion,
+ entityMode
+ );
+ }
+
+ public void setReadOnly(boolean readOnly, Object entity) {
+ if (status!=Status.MANAGED && status!=Status.READ_ONLY) {
+ throw new HibernateException("instance was not in a valid state");
+ }
+ if (readOnly) {
+ setStatus(Status.READ_ONLY);
+ loadedState = null;
+ }
+ else {
+ setStatus(Status.MANAGED);
+ loadedState = getPersister().getPropertyValues(entity, entityMode);
+ }
+ }
+
+ public String toString() {
+ return "EntityEntry" +
+ MessageHelper.infoString(entityName, id) +
+ '(' + status + ')';
+ }
+
+ public boolean isLoadedWithLazyPropertiesUnfetched() {
+ return loadedWithLazyPropertiesUnfetched;
+ }
+
+
+ /**
+ * Custom serialization routine used during serialization of a
+ * Session/PersistenceContext for increased performance.
+ *
+ * @param oos The stream to which we should write the serial data.
+ * @throws java.io.IOException
+ */
+ void serialize(ObjectOutputStream oos) throws IOException {
+ oos.writeObject( entityName );
+ oos.writeObject( id );
+ oos.writeObject( entityMode.toString() );
+ oos.writeObject( status.toString() );
+ // todo : potentially look at optimizing these two arrays
+ oos.writeObject( loadedState );
+ oos.writeObject( deletedState );
+ oos.writeObject( version );
+ oos.writeObject( lockMode.toString() );
+ oos.writeBoolean( existsInDatabase );
+ oos.writeBoolean( isBeingReplicated );
+ oos.writeBoolean( loadedWithLazyPropertiesUnfetched );
+ }
+
+ /**
+ * Custom deserialization routine used during deserialization of a
+ * Session/PersistenceContext for increased performance.
+ *
+ * @param ois The stream from which to read the entry.
+ * @param session The session being deserialized.
+ * @return The deserialized EntityEntry
+ * @throws IOException
+ * @throws ClassNotFoundException
+ */
+ static EntityEntry deserialize(
+ ObjectInputStream ois,
+ SessionImplementor session) throws IOException, ClassNotFoundException {
+ return new EntityEntry(
+ session.getFactory(),
+ ( String ) ois.readObject(),
+ ( Serializable ) ois.readObject(),
+ EntityMode.parse( ( String ) ois.readObject() ),
+ Status.parse( ( String ) ois.readObject() ),
+ ( Object[] ) ois.readObject(),
+ ( Object[] ) ois.readObject(),
+ ( Object ) ois.readObject(),
+ LockMode.parse( ( String ) ois.readObject() ),
+ ois.readBoolean(),
+ ois.readBoolean(),
+ ois.readBoolean()
+ );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/EntityKey.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/EntityKey.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/EntityKey.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,156 @@
+//$Id: EntityKey.java 9194 2006-02-01 19:59:07Z steveebersole $
+package org.hibernate.engine;
+
+import java.io.Serializable;
+import java.io.ObjectOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+
+import org.hibernate.AssertionFailure;
+import org.hibernate.EntityMode;
+import org.hibernate.util.SerializationHelper;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.pretty.MessageHelper;
+import org.hibernate.type.Type;
+
+/**
+ * Uniquely identifies of an entity instance in a particular session by identifier.
+ * <p/>
+ * Uniqueing information consists of the entity-name and the identifier value.
+ *
+ * @see EntityUniqueKey
+ * @author Gavin King
+ */
+public final class EntityKey implements Serializable {
+ private final Serializable identifier;
+ private final String rootEntityName;
+ private final String entityName;
+ private final Type identifierType;
+ private final boolean isBatchLoadable;
+ private final SessionFactoryImplementor factory;
+ private final int hashCode;
+ private final EntityMode entityMode;
+
+ /**
+ * Construct a unique identifier for an entity class instance
+ */
+ public EntityKey(Serializable id, EntityPersister persister, EntityMode entityMode) {
+ if ( id == null ) {
+ throw new AssertionFailure( "null identifier" );
+ }
+ this.identifier = id;
+ this.entityMode = entityMode;
+ this.rootEntityName = persister.getRootEntityName();
+ this.entityName = persister.getEntityName();
+ this.identifierType = persister.getIdentifierType();
+ this.isBatchLoadable = persister.isBatchLoadable();
+ this.factory = persister.getFactory();
+ hashCode = generateHashCode(); //cache the hashcode
+ }
+
+ /**
+ * Used to reconstruct an EntityKey during deserialization.
+ *
+ * @param identifier The identifier value
+ * @param rootEntityName The root entity name
+ * @param entityName The specific entity name
+ * @param identifierType The type of the identifier value
+ * @param batchLoadable Whether represented entity is eligible for batch loading
+ * @param factory The session factory
+ * @param entityMode The entity's entity mode
+ */
+ private EntityKey(
+ Serializable identifier,
+ String rootEntityName,
+ String entityName,
+ Type identifierType,
+ boolean batchLoadable,
+ SessionFactoryImplementor factory,
+ EntityMode entityMode) {
+ this.identifier = identifier;
+ this.rootEntityName = rootEntityName;
+ this.entityName = entityName;
+ this.identifierType = identifierType;
+ this.isBatchLoadable = batchLoadable;
+ this.factory = factory;
+ this.entityMode = entityMode;
+ this.hashCode = generateHashCode();
+ }
+
+ public boolean isBatchLoadable() {
+ return isBatchLoadable;
+ }
+
+ /**
+ * Get the user-visible identifier
+ */
+ public Serializable getIdentifier() {
+ return identifier;
+ }
+
+ public String getEntityName() {
+ return entityName;
+ }
+
+ public boolean equals(Object other) {
+ EntityKey otherKey = (EntityKey) other;
+ return otherKey.rootEntityName.equals(this.rootEntityName) &&
+ identifierType.isEqual(otherKey.identifier, this.identifier, entityMode, factory);
+ }
+
+ private int generateHashCode() {
+ int result = 17;
+ result = 37 * result + rootEntityName.hashCode();
+ result = 37 * result + identifierType.getHashCode( identifier, entityMode, factory );
+ return result;
+ }
+
+ public int hashCode() {
+ return hashCode;
+ }
+
+ public String toString() {
+ return "EntityKey" +
+ MessageHelper.infoString( factory.getEntityPersister( entityName ), identifier, factory );
+ }
+
+ /**
+ * Custom serialization routine used during serialization of a
+ * Session/PersistenceContext for increased performance.
+ *
+ * @param oos The stream to which we should write the serial data.
+ * @throws IOException
+ */
+ void serialize(ObjectOutputStream oos) throws IOException {
+ oos.writeObject( identifier );
+ oos.writeObject( rootEntityName );
+ oos.writeObject( entityName );
+ oos.writeObject( identifierType );
+ oos.writeBoolean( isBatchLoadable );
+ oos.writeObject( entityMode.toString() );
+ }
+
+ /**
+ * Custom deserialization routine used during deserialization of a
+ * Session/PersistenceContext for increased performance.
+ *
+ * @param ois The stream from which to read the entry.
+ * @param session The session being deserialized.
+ * @return The deserialized EntityEntry
+ * @throws IOException
+ * @throws ClassNotFoundException
+ */
+ static EntityKey deserialize(
+ ObjectInputStream ois,
+ SessionImplementor session) throws IOException, ClassNotFoundException {
+ return new EntityKey(
+ ( Serializable ) ois.readObject(),
+ ( String ) ois.readObject(),
+ ( String ) ois.readObject(),
+ ( Type ) ois.readObject(),
+ ois.readBoolean(),
+ session.getFactory(),
+ EntityMode.parse( ( String ) ois.readObject() )
+ );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/EntityUniqueKey.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/EntityUniqueKey.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/EntityUniqueKey.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,137 @@
+//$Id: EntityUniqueKey.java 9194 2006-02-01 19:59:07Z steveebersole $
+package org.hibernate.engine;
+
+import org.hibernate.EntityMode;
+import org.hibernate.pretty.MessageHelper;
+import org.hibernate.type.Type;
+
+import java.io.Serializable;
+import java.io.ObjectOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+
+/**
+ * Used to uniquely key an entity instance in relation to a particular session
+ * by some unique property reference, as opposed to identifier.
+ * <p/>
+ * Uniqueing information consists of the entity-name, the referenced
+ * property name, and the referenced property value.
+ *
+ * @see EntityKey
+ * @author Gavin King
+ */
+public class EntityUniqueKey implements Serializable {
+ private final String uniqueKeyName;
+ private final String entityName;
+ private final Object key;
+ private final Type keyType;
+ private final EntityMode entityMode;
+ private final int hashCode;
+
+ public EntityUniqueKey(
+ final String entityName,
+ final String uniqueKeyName,
+ final Object semiResolvedKey,
+ final Type keyType,
+ final EntityMode entityMode,
+ final SessionFactoryImplementor factory
+ ) {
+ this.uniqueKeyName = uniqueKeyName;
+ this.entityName = entityName;
+ this.key = semiResolvedKey;
+ this.keyType = keyType.getSemiResolvedType(factory);
+ this.entityMode = entityMode;
+ this.hashCode = generateHashCode(factory);
+ }
+
+ public String getEntityName() {
+ return entityName;
+ }
+
+ public Object getKey() {
+ return key;
+ }
+
+ public String getUniqueKeyName() {
+ return uniqueKeyName;
+ }
+
+ public int generateHashCode(SessionFactoryImplementor factory) {
+ int result = 17;
+ result = 37 * result + entityName.hashCode();
+ result = 37 * result + uniqueKeyName.hashCode();
+ result = 37 * result + keyType.getHashCode(key, entityMode, factory);
+ return result;
+ }
+
+ public int hashCode() {
+ return hashCode;
+ }
+
+ public boolean equals(Object other) {
+ EntityUniqueKey that = (EntityUniqueKey) other;
+ return that.entityName.equals(entityName) &&
+ that.uniqueKeyName.equals(uniqueKeyName) &&
+ keyType.isEqual(that.key, key, entityMode);
+ }
+
+ public String toString() {
+ return "EntityUniqueKey" + MessageHelper.infoString(entityName, uniqueKeyName, key);
+ }
+
+ private void writeObject(ObjectOutputStream oos) throws IOException {
+ checkAbilityToSerialize();
+ oos.defaultWriteObject();
+ }
+
+ private void checkAbilityToSerialize() {
+ // The unique property value represented here may or may not be
+ // serializable, so we do an explicit check here in order to generate
+ // a better error message
+ if ( key != null && ! Serializable.class.isAssignableFrom( key.getClass() ) ) {
+ throw new IllegalStateException(
+ "Cannot serialize an EntityUniqueKey which represents a non " +
+ "serializable property value [" + entityName + "." + uniqueKeyName + "]"
+ );
+ }
+ }
+
+ /**
+ * Custom serialization routine used during serialization of a
+ * Session/PersistenceContext for increased performance.
+ *
+ * @param oos The stream to which we should write the serial data.
+ * @throws IOException
+ */
+ void serialize(ObjectOutputStream oos) throws IOException {
+ checkAbilityToSerialize();
+ oos.writeObject( uniqueKeyName );
+ oos.writeObject( entityName );
+ oos.writeObject( key );
+ oos.writeObject( keyType );
+ oos.writeObject( entityMode );
+ }
+
+ /**
+ * Custom deserialization routine used during deserialization of a
+ * Session/PersistenceContext for increased performance.
+ *
+ * @param ois The stream from which to read the entry.
+ * @param session The session being deserialized.
+ * @return The deserialized EntityEntry
+ * @throws IOException
+ * @throws ClassNotFoundException
+ */
+ static EntityUniqueKey deserialize(
+ ObjectInputStream ois,
+ SessionImplementor session) throws IOException, ClassNotFoundException {
+ return new EntityUniqueKey(
+ ( String ) ois.readObject(),
+ ( String ) ois.readObject(),
+ ois.readObject(),
+ ( Type ) ois.readObject(),
+ ( EntityMode ) ois.readObject(),
+ session.getFactory()
+ );
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/EntityUniqueKey.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/ExecuteUpdateResultCheckStyle.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/ExecuteUpdateResultCheckStyle.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/ExecuteUpdateResultCheckStyle.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,73 @@
+package org.hibernate.engine;
+
+import java.io.Serializable;
+import java.io.ObjectStreamException;
+import java.io.InvalidObjectException;
+
+/**
+ * For persistence operations (INSERT, UPDATE, DELETE) what style of determining
+ * results (success/failure) is to be used.
+ *
+ * @author Steve Ebersole
+ */
+public class ExecuteUpdateResultCheckStyle implements Serializable {
+ /**
+ * Do not perform checking. Either user simply does not want checking, or is
+ * indicating a {@link java.sql.CallableStatement} execution in which the
+ * checks are being performed explicitly and failures are handled through
+ * propogation of {@link java.sql.SQLException}s.
+ */
+ public static final ExecuteUpdateResultCheckStyle NONE = new ExecuteUpdateResultCheckStyle( "none" );
+ /**
+ * Perform row-count checking. Row counts are the int values returned by both
+ * {@link java.sql.PreparedStatement#executeUpdate()} and
+ * {@link java.sql.Statement#executeBatch()}. These values are checked
+ * against some expected count.
+ */
+ public static final ExecuteUpdateResultCheckStyle COUNT = new ExecuteUpdateResultCheckStyle( "rowcount" );
+ /**
+ * Essentially the same as {@link #COUNT} except that the row count actually
+ * comes from an output parameter registered as part of a
+ * {@link java.sql.CallableStatement}. This style explicitly prohibits
+ * statement batching from being used...
+ */
+ public static final ExecuteUpdateResultCheckStyle PARAM = new ExecuteUpdateResultCheckStyle( "param" );
+
+ private final String name;
+
+ private ExecuteUpdateResultCheckStyle(String name) {
+ this.name = name;
+ }
+
+ private Object readResolve() throws ObjectStreamException {
+ Object resolved = parse( name );
+ if ( resolved == null ) {
+ throw new InvalidObjectException( "unknown result style [" + name + "]" );
+ }
+ return resolved;
+ }
+
+ public static ExecuteUpdateResultCheckStyle parse(String name) {
+ if ( name.equals( NONE.name ) ) {
+ return NONE;
+ }
+ else if ( name.equals( COUNT.name ) ) {
+ return COUNT;
+ }
+ else if ( name.equals( PARAM.name ) ) {
+ return PARAM;
+ }
+ else {
+ return null;
+ }
+ }
+
+ public static ExecuteUpdateResultCheckStyle determineDefault(String customSql, boolean callable) {
+ if ( customSql == null ) {
+ return COUNT;
+ }
+ else {
+ return callable ? PARAM : COUNT;
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/FilterDefinition.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/FilterDefinition.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/FilterDefinition.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,69 @@
+// $Id: FilterDefinition.java 9765 2006-04-19 01:45:07Z max.andersen at jboss.com $
+package org.hibernate.engine;
+
+import org.hibernate.type.Type;
+
+import java.io.Serializable;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+
+/**
+ * A FilterDefinition defines the global attributes of a dynamic filter. This
+ * information includes its name as well as its defined parameters (name and type).
+ *
+ * @author Steve Ebersole
+ */
+public class FilterDefinition implements Serializable {
+ private final String filterName;
+ private final String defaultFilterCondition;
+ private final Map parameterTypes = new HashMap();
+
+ /**
+ * Construct a new FilterDefinition instance.
+ *
+ * @param name The name of the filter for which this configuration is in effect.
+ */
+ public FilterDefinition(String name, String defaultCondition, Map parameterTypes) {
+ this.filterName = name;
+ this.defaultFilterCondition = defaultCondition;
+ this.parameterTypes.putAll( parameterTypes );
+ }
+
+ /**
+ * Get the name of the filter this configuration defines.
+ *
+ * @return The filter name for this configuration.
+ */
+ public String getFilterName() {
+ return filterName;
+ }
+
+ /**
+ * Get a set of the parameters defined by this configuration.
+ *
+ * @return The parameters named by this configuration.
+ */
+ public Set getParameterNames() {
+ return parameterTypes.keySet();
+ }
+
+ /**
+ * Retreive the type of the named parameter defined for this filter.
+ *
+ * @param parameterName The name of the filter parameter for which to return the type.
+ * @return The type of the named parameter.
+ */
+ public Type getParameterType(String parameterName) {
+ return (Type) parameterTypes.get(parameterName);
+ }
+
+ public String getDefaultFilterCondition() {
+ return defaultFilterCondition;
+ }
+
+ public Map getParameterTypes() {
+ return parameterTypes;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/ForeignKeys.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/ForeignKeys.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/ForeignKeys.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,230 @@
+//$Id: ForeignKeys.java 10133 2006-07-24 10:35:25Z max.andersen at jboss.com $
+package org.hibernate.engine;
+
+import java.io.Serializable;
+
+import org.hibernate.HibernateException;
+import org.hibernate.TransientObjectException;
+import org.hibernate.intercept.LazyPropertyInitializer;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.proxy.HibernateProxy;
+import org.hibernate.proxy.LazyInitializer;
+import org.hibernate.type.AbstractComponentType;
+import org.hibernate.type.EntityType;
+import org.hibernate.type.Type;
+
+/**
+ * Algorithms related to foreign key constraint transparency
+ *
+ * @author Gavin King
+ */
+public final class ForeignKeys {
+
+ private ForeignKeys() {}
+
+ public static class Nullifier {
+
+ private final boolean isDelete;
+ private final boolean isEarlyInsert;
+ private final SessionImplementor session;
+ private final Object self;
+
+ public Nullifier(Object self, boolean isDelete, boolean isEarlyInsert, SessionImplementor session) {
+ this.isDelete = isDelete;
+ this.isEarlyInsert = isEarlyInsert;
+ this.session = session;
+ this.self = self;
+ }
+
+ /**
+ * Nullify all references to entities that have not yet
+ * been inserted in the database, where the foreign key
+ * points toward that entity
+ */
+ public void nullifyTransientReferences(final Object[] values, final Type[] types)
+ throws HibernateException {
+ for ( int i = 0; i < types.length; i++ ) {
+ values[i] = nullifyTransientReferences( values[i], types[i] );
+ }
+ }
+
+ /**
+ * Return null if the argument is an "unsaved" entity (ie.
+ * one with no existing database row), or the input argument
+ * otherwise. This is how Hibernate avoids foreign key constraint
+ * violations.
+ */
+ private Object nullifyTransientReferences(final Object value, final Type type)
+ throws HibernateException {
+ if ( value == null ) {
+ return null;
+ }
+ else if ( type.isEntityType() ) {
+ EntityType entityType = (EntityType) type;
+ if ( entityType.isOneToOne() ) {
+ return value;
+ }
+ else {
+ String entityName = entityType.getAssociatedEntityName();
+ return isNullifiable(entityName, value) ? null : value;
+ }
+ }
+ else if ( type.isAnyType() ) {
+ return isNullifiable(null, value) ? null : value;
+ }
+ else if ( type.isComponentType() ) {
+ AbstractComponentType actype = (AbstractComponentType) type;
+ Object[] subvalues = actype.getPropertyValues(value, session);
+ Type[] subtypes = actype.getSubtypes();
+ boolean substitute = false;
+ for ( int i = 0; i < subvalues.length; i++ ) {
+ Object replacement = nullifyTransientReferences( subvalues[i], subtypes[i] );
+ if ( replacement != subvalues[i] ) {
+ substitute = true;
+ subvalues[i] = replacement;
+ }
+ }
+ if (substitute) actype.setPropertyValues( value, subvalues, session.getEntityMode() );
+ return value;
+ }
+ else {
+ return value;
+ }
+ }
+
+ /**
+ * Determine if the object already exists in the database,
+ * using a "best guess"
+ */
+ private boolean isNullifiable(final String entityName, Object object)
+ throws HibernateException {
+
+ if (object==LazyPropertyInitializer.UNFETCHED_PROPERTY) return false; //this is kinda the best we can do...
+
+ if ( object instanceof HibernateProxy ) {
+ // if its an uninitialized proxy it can't be transient
+ LazyInitializer li = ( (HibernateProxy) object ).getHibernateLazyInitializer();
+ if ( li.getImplementation(session) == null ) {
+ return false;
+ // ie. we never have to null out a reference to
+ // an uninitialized proxy
+ }
+ else {
+ //unwrap it
+ object = li.getImplementation();
+ }
+ }
+
+ // if it was a reference to self, don't need to nullify
+ // unless we are using native id generation, in which
+ // case we definitely need to nullify
+ if ( object == self ) {
+ return isEarlyInsert || (
+ isDelete &&
+ session.getFactory()
+ .getDialect()
+ .hasSelfReferentialForeignKeyBug()
+ );
+ }
+
+ // See if the entity is already bound to this session, if not look at the
+ // entity identifier and assume that the entity is persistent if the
+ // id is not "unsaved" (that is, we rely on foreign keys to keep
+ // database integrity)
+
+ EntityEntry entityEntry = session.getPersistenceContext().getEntry(object);
+ if ( entityEntry==null ) {
+ return isTransient(entityName, object, null, session);
+ }
+ else {
+ return entityEntry.isNullifiable(isEarlyInsert, session);
+ }
+
+ }
+
+ }
+
+ /**
+ * Is this instance persistent or detached?
+ * If <tt>assumed</tt> is non-null, don't hit the database to make the
+ * determination, instead assume that value; the client code must be
+ * prepared to "recover" in the case that this assumed result is incorrect.
+ */
+ public static boolean isNotTransient(String entityName, Object entity, Boolean assumed, SessionImplementor session)
+ throws HibernateException {
+ if (entity instanceof HibernateProxy) return true;
+ if ( session.getPersistenceContext().isEntryFor(entity) ) return true;
+ return !isTransient(entityName, entity, assumed, session);
+ }
+
+ /**
+ * Is this instance, which we know is not persistent, actually transient?
+ * If <tt>assumed</tt> is non-null, don't hit the database to make the
+ * determination, instead assume that value; the client code must be
+ * prepared to "recover" in the case that this assumed result is incorrect.
+ */
+ public static boolean isTransient(String entityName, Object entity, Boolean assumed, SessionImplementor session)
+ throws HibernateException {
+
+ if (entity==LazyPropertyInitializer.UNFETCHED_PROPERTY) {
+ // an unfetched association can only point to
+ // an entity that already exists in the db
+ return false;
+ }
+
+ // let the interceptor inspect the instance to decide
+ Boolean isUnsaved = session.getInterceptor().isTransient(entity);
+ if (isUnsaved!=null) return isUnsaved.booleanValue();
+
+ // let the persister inspect the instance to decide
+ EntityPersister persister = session.getEntityPersister(entityName, entity);
+ isUnsaved = persister.isTransient(entity, session);
+ if (isUnsaved!=null) return isUnsaved.booleanValue();
+
+ // we use the assumed value, if there is one, to avoid hitting
+ // the database
+ if (assumed!=null) return assumed.booleanValue();
+
+ // hit the database, after checking the session cache for a snapshot
+ Object[] snapshot = session.getPersistenceContext()
+ .getDatabaseSnapshot( persister.getIdentifier( entity, session.getEntityMode() ), persister );
+ return snapshot==null;
+
+ }
+
+ /**
+ * Return the identifier of the persistent or transient object, or throw
+ * an exception if the instance is "unsaved"
+ *
+ * Used by OneToOneType and ManyToOneType to determine what id value should
+ * be used for an object that may or may not be associated with the session.
+ * This does a "best guess" using any/all info available to use (not just the
+ * EntityEntry).
+ */
+ public static Serializable getEntityIdentifierIfNotUnsaved(
+ final String entityName,
+ final Object object,
+ final SessionImplementor session)
+ throws HibernateException {
+ if ( object == null ) {
+ return null;
+ }
+ else {
+ Serializable id = session.getContextEntityIdentifier( object );
+ if ( id == null ) {
+ // context-entity-identifier returns null explicitly if the entity
+ // is not associated with the persistence context; so make some
+ // deeper checks...
+ if ( isTransient(entityName, object, Boolean.FALSE, session) ) {
+ throw new TransientObjectException(
+ "object references an unsaved transient instance - save the transient instance before flushing: " +
+ (entityName == null ? session.guessEntityName( object ) : entityName)
+ );
+ }
+ id = session.getEntityPersister( entityName, object ).getIdentifier( object, session.getEntityMode() );
+ }
+ return id;
+ }
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/ForeignKeys.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/HibernateIterator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/HibernateIterator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/HibernateIterator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,15 @@
+//$Id: HibernateIterator.java 4782 2004-11-21 00:11:27Z pgmjsd $
+package org.hibernate.engine;
+
+import org.hibernate.JDBCException;
+
+import java.util.Iterator;
+
+/**
+ * An iterator that may be "closed"
+ * @see org.hibernate.Hibernate#close(java.util.Iterator)
+ * @author Gavin King
+ */
+public interface HibernateIterator extends Iterator {
+ public void close() throws JDBCException;
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/IdentifierValue.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/IdentifierValue.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/IdentifierValue.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,115 @@
+//$Id: IdentifierValue.java 7017 2005-06-05 04:31:34Z oneovthafew $
+package org.hibernate.engine;
+
+import java.io.Serializable;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * A strategy for determining if an identifier value is an identifier of
+ * a new transient instance or a previously persistent transient instance.
+ * The strategy is determined by the <tt>unsaved-value</tt> attribute in
+ * the mapping file.
+ *
+ * @author Gavin King
+ */
+public class IdentifierValue {
+
+ private static final Log log = LogFactory.getLog(IdentifierValue.class);
+
+ private final Serializable value;
+
+ /**
+ * Always assume the transient instance is newly instantiated
+ */
+ public static final IdentifierValue ANY = new IdentifierValue() {
+ public final Boolean isUnsaved(Serializable id) {
+ log.trace("id unsaved-value strategy ANY");
+ return Boolean.TRUE;
+ }
+ public Serializable getDefaultValue(Serializable currentValue) {
+ return currentValue;
+ }
+ public String toString() {
+ return "SAVE_ANY";
+ }
+ };
+
+ /**
+ * Never assume the transient instance is newly instantiated
+ */
+ public static final IdentifierValue NONE = new IdentifierValue() {
+ public final Boolean isUnsaved(Serializable id) {
+ log.trace("id unsaved-value strategy NONE");
+ return Boolean.FALSE;
+ }
+ public Serializable getDefaultValue(Serializable currentValue) {
+ return currentValue;
+ }
+ public String toString() {
+ return "SAVE_NONE";
+ }
+ };
+
+ /**
+ * Assume the transient instance is newly instantiated if the identifier
+ * is null.
+ */
+ public static final IdentifierValue NULL = new IdentifierValue() {
+ public final Boolean isUnsaved(Serializable id) {
+ log.trace("id unsaved-value strategy NULL");
+ return id==null ? Boolean.TRUE : Boolean.FALSE;
+ }
+ public Serializable getDefaultValue(Serializable currentValue) {
+ return null;
+ }
+ public String toString() {
+ return "SAVE_NULL";
+ }
+ };
+
+ /**
+ * Assume nothing.
+ */
+ public static final IdentifierValue UNDEFINED = new IdentifierValue() {
+ public final Boolean isUnsaved(Serializable id) {
+ log.trace("id unsaved-value strategy UNDEFINED");
+ return null;
+ }
+ public Serializable getDefaultValue(Serializable currentValue) {
+ return null;
+ }
+ public String toString() {
+ return "UNDEFINED";
+ }
+ };
+
+ protected IdentifierValue() {
+ this.value = null;
+ }
+
+ /**
+ * Assume the transient instance is newly instantiated if
+ * its identifier is null or equal to <tt>value</tt>
+ */
+ public IdentifierValue(Serializable value) {
+ this.value = value;
+ }
+
+ /**
+ * Does the given identifier belong to a new instance?
+ */
+ public Boolean isUnsaved(Serializable id) {
+ if ( log.isTraceEnabled() ) log.trace("id unsaved-value: " + value);
+ return id==null || id.equals(value) ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ public Serializable getDefaultValue(Serializable currentValue) {
+ return value;
+ }
+
+ public String toString() {
+ return "identifier unsaved-value: " + value;
+ }
+}
\ No newline at end of file
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/IdentifierValue.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/JoinHelper.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/JoinHelper.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/JoinHelper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,157 @@
+//$Id: JoinHelper.java 7586 2005-07-21 01:11:52Z oneovthafew $
+package org.hibernate.engine;
+
+import org.hibernate.persister.entity.Joinable;
+import org.hibernate.persister.entity.OuterJoinLoadable;
+import org.hibernate.persister.entity.PropertyMapping;
+import org.hibernate.type.AssociationType;
+import org.hibernate.util.ArrayHelper;
+import org.hibernate.util.StringHelper;
+
+/**
+ * @author Gavin King
+ */
+public final class JoinHelper {
+
+ private JoinHelper() {}
+
+ /**
+ * Get the aliased columns of the owning entity which are to
+ * be used in the join
+ */
+ public static String[] getAliasedLHSColumnNames(
+ AssociationType type,
+ String alias,
+ int property,
+ OuterJoinLoadable lhsPersister,
+ Mapping mapping
+ ) {
+ return getAliasedLHSColumnNames(type, alias, property, 0, lhsPersister, mapping);
+ }
+
+ /**
+ * Get the columns of the owning entity which are to
+ * be used in the join
+ */
+ public static String[] getLHSColumnNames(
+ AssociationType type,
+ int property,
+ OuterJoinLoadable lhsPersister,
+ Mapping mapping
+ ) {
+ return getLHSColumnNames(type, property, 0, lhsPersister, mapping);
+ }
+
+ /**
+ * Get the aliased columns of the owning entity which are to
+ * be used in the join
+ */
+ public static String[] getAliasedLHSColumnNames(
+ AssociationType type,
+ String alias,
+ int property,
+ int begin,
+ OuterJoinLoadable lhsPersister,
+ Mapping mapping
+ ) {
+ if ( type.useLHSPrimaryKey() ) {
+ return StringHelper.qualify( alias, lhsPersister.getIdentifierColumnNames() );
+ }
+ else {
+ String propertyName = type.getLHSPropertyName();
+ if (propertyName==null) {
+ return ArrayHelper.slice(
+ lhsPersister.toColumns(alias, property),
+ begin,
+ type.getColumnSpan(mapping)
+ );
+ }
+ else {
+ return ( (PropertyMapping) lhsPersister ).toColumns(alias, propertyName); //bad cast
+ }
+ }
+ }
+
+ /**
+ * Get the columns of the owning entity which are to
+ * be used in the join
+ */
+ public static String[] getLHSColumnNames(
+ AssociationType type,
+ int property,
+ int begin,
+ OuterJoinLoadable lhsPersister,
+ Mapping mapping
+ ) {
+ if ( type.useLHSPrimaryKey() ) {
+ //return lhsPersister.getSubclassPropertyColumnNames(property);
+ return lhsPersister.getIdentifierColumnNames();
+ }
+ else {
+ String propertyName = type.getLHSPropertyName();
+ if (propertyName==null) {
+ //slice, to get the columns for this component
+ //property
+ return ArrayHelper.slice(
+ lhsPersister.getSubclassPropertyColumnNames(property),
+ begin,
+ type.getColumnSpan(mapping)
+ );
+ }
+ else {
+ //property-refs for associations defined on a
+ //component are not supported, so no need to slice
+ return lhsPersister.getPropertyColumnNames(propertyName);
+ }
+ }
+ }
+
+ public static String getLHSTableName(
+ AssociationType type,
+ int property,
+ OuterJoinLoadable lhsPersister
+ ) {
+ if ( type.useLHSPrimaryKey() ) {
+ return lhsPersister.getTableName();
+ }
+ else {
+ String propertyName = type.getLHSPropertyName();
+ if (propertyName==null) {
+ //if there is no property-ref, assume the join
+ //is to the subclass table (ie. the table of the
+ //subclass that the association belongs to)
+ return lhsPersister.getSubclassPropertyTableName(property);
+ }
+ else {
+ //handle a property-ref
+ String propertyRefTable = lhsPersister.getPropertyTableName(propertyName);
+ if (propertyRefTable==null) {
+ //it is possible that the tree-walking in OuterJoinLoader can get to
+ //an association defined by a subclass, in which case the property-ref
+ //might refer to a property defined on a subclass of the current class
+ //in this case, the table name is not known - this temporary solution
+ //assumes that the property-ref refers to a property of the subclass
+ //table that the association belongs to (a reasonable guess)
+ //TODO: fix this, add: OuterJoinLoadable.getSubclassPropertyTableName(String propertyName)
+ propertyRefTable = lhsPersister.getSubclassPropertyTableName(property);
+ }
+ return propertyRefTable;
+ }
+ }
+ }
+
+ /**
+ * Get the columns of the associated table which are to
+ * be used in the join
+ */
+ public static String[] getRHSColumnNames(AssociationType type, SessionFactoryImplementor factory) {
+ String uniqueKeyPropertyName = type.getRHSUniqueKeyPropertyName();
+ Joinable joinable = type.getAssociatedJoinable(factory);
+ if (uniqueKeyPropertyName==null) {
+ return joinable.getKeyColumnNames();
+ }
+ else {
+ return ( (OuterJoinLoadable) joinable ).getPropertyColumnNames(uniqueKeyPropertyName);
+ }
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/JoinHelper.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/JoinSequence.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/JoinSequence.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/JoinSequence.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,267 @@
+//$Id: JoinSequence.java 9336 2006-02-24 22:12:13Z steveebersole $
+package org.hibernate.engine;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.hibernate.MappingException;
+import org.hibernate.persister.entity.Joinable;
+import org.hibernate.persister.collection.QueryableCollection;
+import org.hibernate.sql.JoinFragment;
+import org.hibernate.sql.QueryJoinFragment;
+import org.hibernate.type.AssociationType;
+import org.hibernate.util.CollectionHelper;
+
+/**
+ * @author Gavin King
+ */
+public class JoinSequence {
+
+ private final SessionFactoryImplementor factory;
+ private final List joins = new ArrayList();
+ private boolean useThetaStyle = false;
+ private final StringBuffer conditions = new StringBuffer();
+ private String rootAlias;
+ private Joinable rootJoinable;
+ private Selector selector;
+ private JoinSequence next;
+ private boolean isFromPart = false;
+
+ public String toString() {
+ StringBuffer buf = new StringBuffer();
+ buf.append( "JoinSequence{" );
+ if ( rootJoinable != null ) {
+ buf.append( rootJoinable )
+ .append( '[' )
+ .append( rootAlias )
+ .append( ']' );
+ }
+ for ( int i = 0; i < joins.size(); i++ ) {
+ buf.append( "->" ).append( joins.get( i ) );
+ }
+ return buf.append( '}' ).toString();
+ }
+
+ final class Join {
+
+ private final AssociationType associationType;
+ private final Joinable joinable;
+ private final int joinType;
+ private final String alias;
+ private final String[] lhsColumns;
+
+ Join(AssociationType associationType, String alias, int joinType, String[] lhsColumns)
+ throws MappingException {
+ this.associationType = associationType;
+ this.joinable = associationType.getAssociatedJoinable( factory );
+ this.alias = alias;
+ this.joinType = joinType;
+ this.lhsColumns = lhsColumns;
+ }
+
+ String getAlias() {
+ return alias;
+ }
+
+ AssociationType getAssociationType() {
+ return associationType;
+ }
+
+ Joinable getJoinable() {
+ return joinable;
+ }
+
+ int getJoinType() {
+ return joinType;
+ }
+
+ String[] getLHSColumns() {
+ return lhsColumns;
+ }
+
+ public String toString() {
+ return joinable.toString() + '[' + alias + ']';
+ }
+ }
+
+ public JoinSequence(SessionFactoryImplementor factory) {
+ this.factory = factory;
+ }
+
+ public JoinSequence getFromPart() {
+ JoinSequence fromPart = new JoinSequence( factory );
+ fromPart.joins.addAll( this.joins );
+ fromPart.useThetaStyle = this.useThetaStyle;
+ fromPart.rootAlias = this.rootAlias;
+ fromPart.rootJoinable = this.rootJoinable;
+ fromPart.selector = this.selector;
+ fromPart.next = this.next == null ? null : this.next.getFromPart();
+ fromPart.isFromPart = true;
+ return fromPart;
+ }
+
+ public JoinSequence copy() {
+ JoinSequence copy = new JoinSequence( factory );
+ copy.joins.addAll( this.joins );
+ copy.useThetaStyle = this.useThetaStyle;
+ copy.rootAlias = this.rootAlias;
+ copy.rootJoinable = this.rootJoinable;
+ copy.selector = this.selector;
+ copy.next = this.next == null ? null : this.next.copy();
+ copy.isFromPart = this.isFromPart;
+ copy.conditions.append( this.conditions.toString() );
+ return copy;
+ }
+
+ public JoinSequence addJoin(AssociationType associationType, String alias, int joinType, String[] referencingKey)
+ throws MappingException {
+ joins.add( new Join( associationType, alias, joinType, referencingKey ) );
+ return this;
+ }
+
+ public JoinFragment toJoinFragment() throws MappingException {
+ return toJoinFragment( CollectionHelper.EMPTY_MAP, true );
+ }
+
+ public JoinFragment toJoinFragment(Map enabledFilters, boolean includeExtraJoins) throws MappingException {
+ return toJoinFragment( enabledFilters, includeExtraJoins, null, null );
+ }
+
+ public JoinFragment toJoinFragment(
+ Map enabledFilters,
+ boolean includeExtraJoins,
+ String withClauseFragment,
+ String withClauseJoinAlias) throws MappingException {
+ QueryJoinFragment joinFragment = new QueryJoinFragment( factory.getDialect(), useThetaStyle );
+ if ( rootJoinable != null ) {
+ joinFragment.addCrossJoin( rootJoinable.getTableName(), rootAlias );
+ String filterCondition = rootJoinable.filterFragment( rootAlias, enabledFilters );
+ // JoinProcessor needs to know if the where clause fragment came from a dynamic filter or not so it
+ // can put the where clause fragment in the right place in the SQL AST. 'hasFilterCondition' keeps track
+ // of that fact.
+ joinFragment.setHasFilterCondition( joinFragment.addCondition( filterCondition ) );
+ if (includeExtraJoins) { //TODO: not quite sure about the full implications of this!
+ addExtraJoins( joinFragment, rootAlias, rootJoinable, true );
+ }
+ }
+
+ Joinable last = rootJoinable;
+
+ for ( int i = 0; i < joins.size(); i++ ) {
+ Join join = ( Join ) joins.get( i );
+ String on = join.getAssociationType().getOnCondition( join.getAlias(), factory, enabledFilters );
+ String condition = null;
+ if ( last != null &&
+ isManyToManyRoot( last ) &&
+ ( ( QueryableCollection ) last ).getElementType() == join.getAssociationType() ) {
+ // the current join represents the join between a many-to-many association table
+ // and its "target" table. Here we need to apply any additional filters
+ // defined specifically on the many-to-many
+ String manyToManyFilter = ( ( QueryableCollection ) last )
+ .getManyToManyFilterFragment( join.getAlias(), enabledFilters );
+ condition = "".equals( manyToManyFilter )
+ ? on
+ : "".equals( on )
+ ? manyToManyFilter
+ : on + " and " + manyToManyFilter;
+ }
+ else {
+ condition = on;
+ }
+ if ( withClauseFragment != null ) {
+ if ( join.getAlias().equals( withClauseJoinAlias ) ) {
+ condition += " and " + withClauseFragment;
+ }
+ }
+ joinFragment.addJoin(
+ join.getJoinable().getTableName(),
+ join.getAlias(),
+ join.getLHSColumns(),
+ JoinHelper.getRHSColumnNames( join.getAssociationType(), factory ),
+ join.joinType,
+ condition
+ );
+ if (includeExtraJoins) { //TODO: not quite sure about the full implications of this!
+ addExtraJoins( joinFragment, join.getAlias(), join.getJoinable(), join.joinType == JoinFragment.INNER_JOIN );
+ }
+ last = join.getJoinable();
+ }
+ if ( next != null ) {
+ joinFragment.addFragment( next.toJoinFragment( enabledFilters, includeExtraJoins ) );
+ }
+ joinFragment.addCondition( conditions.toString() );
+ if ( isFromPart ) joinFragment.clearWherePart();
+ return joinFragment;
+ }
+
+ private boolean isManyToManyRoot(Joinable joinable) {
+ if ( joinable != null && joinable.isCollection() ) {
+ QueryableCollection persister = ( QueryableCollection ) joinable;
+ return persister.isManyToMany();
+ }
+ return false;
+ }
+
+ private boolean isIncluded(String alias) {
+ return selector != null && selector.includeSubclasses( alias );
+ }
+
+ private void addExtraJoins(JoinFragment joinFragment, String alias, Joinable joinable, boolean innerJoin) {
+ boolean include = isIncluded( alias );
+ joinFragment.addJoins( joinable.fromJoinFragment( alias, innerJoin, include ),
+ joinable.whereJoinFragment( alias, innerJoin, include ) );
+ }
+
+ public JoinSequence addCondition(String condition) {
+ if ( condition.trim().length() != 0 ) {
+ if ( !condition.startsWith( " and " ) ) conditions.append( " and " );
+ conditions.append( condition );
+ }
+ return this;
+ }
+
+ public JoinSequence addCondition(String alias, String[] columns, String condition) {
+ for ( int i = 0; i < columns.length; i++ ) {
+ conditions.append( " and " )
+ .append( alias )
+ .append( '.' )
+ .append( columns[i] )
+ .append( condition );
+ }
+ return this;
+ }
+
+ public JoinSequence setRoot(Joinable joinable, String alias) {
+ this.rootAlias = alias;
+ this.rootJoinable = joinable;
+ return this;
+ }
+
+ public JoinSequence setNext(JoinSequence next) {
+ this.next = next;
+ return this;
+ }
+
+ public JoinSequence setSelector(Selector s) {
+ this.selector = s;
+ return this;
+ }
+
+ public JoinSequence setUseThetaStyle(boolean useThetaStyle) {
+ this.useThetaStyle = useThetaStyle;
+ return this;
+ }
+
+ public boolean isThetaStyle() {
+ return useThetaStyle;
+ }
+
+ public int getJoinCount() {
+ return joins.size();
+ }
+
+ public static interface Selector {
+ public boolean includeSubclasses(String alias);
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/Mapping.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/Mapping.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/Mapping.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,21 @@
+//$Id: Mapping.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.engine;
+
+import org.hibernate.MappingException;
+import org.hibernate.type.Type;
+
+/**
+ * Defines operations common to "compiled" mappings (ie. <tt>SessionFactory</tt>)
+ * and "uncompiled" mappings (ie. <tt>Configuration</tt>) that are used by
+ * implementors of <tt>Type</tt>.
+ *
+ * @see org.hibernate.type.Type
+ * @see org.hibernate.impl.SessionFactoryImpl
+ * @see org.hibernate.cfg.Configuration
+ * @author Gavin King
+ */
+public interface Mapping {
+ public Type getIdentifierType(String className) throws MappingException;
+ public String getIdentifierPropertyName(String className) throws MappingException;
+ public Type getReferencedPropertyType(String className, String propertyName) throws MappingException;
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/NamedQueryDefinition.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/NamedQueryDefinition.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/NamedQueryDefinition.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,123 @@
+//$Id: NamedQueryDefinition.java 7966 2005-08-19 23:40:24Z epbernard $
+package org.hibernate.engine;
+
+import java.io.Serializable;
+import java.util.Map;
+
+import org.hibernate.CacheMode;
+import org.hibernate.FlushMode;
+
+
+/**
+ * Definition of a named query, defined in the mapping metadata.
+ *
+ * @author Gavin King
+ */
+public class NamedQueryDefinition implements Serializable {
+ private final String query;
+ private final boolean cacheable;
+ private final String cacheRegion;
+ private final Integer timeout;
+ private final Integer fetchSize;
+ private final FlushMode flushMode;
+ private final Map parameterTypes;
+ private CacheMode cacheMode;
+ private boolean readOnly;
+ private String comment;
+
+ // kept for backward compatibility until after the 3.1beta5 release of HA
+ public NamedQueryDefinition(
+ String query,
+ boolean cacheable,
+ String cacheRegion,
+ Integer timeout,
+ Integer fetchSize,
+ FlushMode flushMode,
+ Map parameterTypes
+ ) {
+ this(
+ query,
+ cacheable,
+ cacheRegion,
+ timeout,
+ fetchSize,
+ flushMode,
+ null,
+ false,
+ null,
+ parameterTypes
+ );
+ }
+
+ public NamedQueryDefinition(
+ String query,
+ boolean cacheable,
+ String cacheRegion,
+ Integer timeout,
+ Integer fetchSize,
+ FlushMode flushMode,
+ CacheMode cacheMode,
+ boolean readOnly,
+ String comment,
+ Map parameterTypes
+ ) {
+ this.query = query;
+ this.cacheable = cacheable;
+ this.cacheRegion = cacheRegion;
+ this.timeout = timeout;
+ this.fetchSize = fetchSize;
+ this.flushMode = flushMode;
+ this.parameterTypes = parameterTypes;
+ this.cacheMode = cacheMode;
+ this.readOnly = readOnly;
+ this.comment = comment;
+ }
+
+ public String getQueryString() {
+ return query;
+ }
+
+ public boolean isCacheable() {
+ return cacheable;
+ }
+
+ public String getCacheRegion() {
+ return cacheRegion;
+ }
+
+ public Integer getFetchSize() {
+ return fetchSize;
+ }
+
+ public Integer getTimeout() {
+ return timeout;
+ }
+
+ public FlushMode getFlushMode() {
+ return flushMode;
+ }
+
+ public String toString() {
+ return getClass().getName() + '(' + query + ')';
+ }
+
+ public Map getParameterTypes() {
+ return parameterTypes;
+ }
+
+ public String getQuery() {
+ return query;
+ }
+
+ public CacheMode getCacheMode() {
+ return cacheMode;
+ }
+
+ public boolean isReadOnly() {
+ return readOnly;
+ }
+
+ public String getComment() {
+ return comment;
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/NamedQueryDefinition.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/NamedSQLQueryDefinition.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/NamedSQLQueryDefinition.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/NamedSQLQueryDefinition.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,185 @@
+//$Id: NamedSQLQueryDefinition.java 11198 2007-02-13 21:04:10Z epbernard $
+package org.hibernate.engine;
+
+import java.util.List;
+import java.util.Map;
+
+import org.hibernate.FlushMode;
+import org.hibernate.CacheMode;
+import org.hibernate.engine.query.sql.NativeSQLQueryReturn;
+
+/**
+ * Definition of a named native SQL query, defined
+ * in the mapping metadata.
+ *
+ * @author Max Andersen
+ */
+public class NamedSQLQueryDefinition extends NamedQueryDefinition {
+
+ private NativeSQLQueryReturn[] queryReturns;
+ private final List querySpaces;
+ private final boolean callable;
+ private String resultSetRef;
+
+ /**
+ * This form used to construct a NamedSQLQueryDefinition from the binder
+ * code when a the result-set mapping information is explicitly
+ * provided in the query definition (i.e., no resultset-mapping used)
+ *
+ * @param query The sql query string
+ * @param queryReturns The in-lined query return definitions
+ * @param querySpaces Any specified query spaces (used for auto-flushing)
+ * @param cacheable Whether the query results are cacheable
+ * @param cacheRegion If cacheable, the region into which to store the results
+ * @param timeout A JDBC-level timeout to be applied
+ * @param fetchSize A JDBC-level fetch-size to be applied
+ * @param flushMode The flush mode to use for this query
+ * @param cacheMode The cache mode to use during execution and subsequent result loading
+ * @param readOnly Whether returned entities should be marked as read-only in the session
+ * @param comment Any sql comment to be applied to the query
+ * @param parameterTypes parameter type map
+ * @param callable Does the query string represent a callable object (i.e., proc)
+ */
+ public NamedSQLQueryDefinition(
+ String query,
+ NativeSQLQueryReturn[] queryReturns,
+ List querySpaces,
+ boolean cacheable,
+ String cacheRegion,
+ Integer timeout,
+ Integer fetchSize,
+ FlushMode flushMode,
+ CacheMode cacheMode,
+ boolean readOnly,
+ String comment,
+ Map parameterTypes,
+ boolean callable) {
+ super(
+ query.trim(), /* trim done to workaround stupid oracle bug that cant handle whitespaces before a { in a sp */
+ cacheable,
+ cacheRegion,
+ timeout,
+ fetchSize,
+ flushMode,
+ cacheMode,
+ readOnly,
+ comment,
+ parameterTypes
+ );
+ this.queryReturns = queryReturns;
+ this.querySpaces = querySpaces;
+ this.callable = callable;
+ }
+
+ /**
+ * This form used to construct a NamedSQLQueryDefinition from the binder
+ * code when a resultset-mapping reference is used.
+ *
+ * @param query The sql query string
+ * @param resultSetRef The resultset-mapping name
+ * @param querySpaces Any specified query spaces (used for auto-flushing)
+ * @param cacheable Whether the query results are cacheable
+ * @param cacheRegion If cacheable, the region into which to store the results
+ * @param timeout A JDBC-level timeout to be applied
+ * @param fetchSize A JDBC-level fetch-size to be applied
+ * @param flushMode The flush mode to use for this query
+ * @param cacheMode The cache mode to use during execution and subsequent result loading
+ * @param readOnly Whether returned entities should be marked as read-only in the session
+ * @param comment Any sql comment to be applied to the query
+ * @param parameterTypes parameter type map
+ * @param callable Does the query string represent a callable object (i.e., proc)
+ */
+ public NamedSQLQueryDefinition(
+ String query,
+ String resultSetRef,
+ List querySpaces,
+ boolean cacheable,
+ String cacheRegion,
+ Integer timeout,
+ Integer fetchSize,
+ FlushMode flushMode,
+ CacheMode cacheMode,
+ boolean readOnly,
+ String comment,
+ Map parameterTypes,
+ boolean callable) {
+ super(
+ query.trim(), /* trim done to workaround stupid oracle bug that cant handle whitespaces before a { in a sp */
+ cacheable,
+ cacheRegion,
+ timeout,
+ fetchSize,
+ flushMode,
+ cacheMode,
+ readOnly,
+ comment,
+ parameterTypes
+ );
+ this.resultSetRef = resultSetRef;
+ this.querySpaces = querySpaces;
+ this.callable = callable;
+ }
+
+ /**
+ * This form used from annotations (?). Essentially the same as the above using a
+ * resultset-mapping reference, but without cacheMode, readOnly, and comment.
+ *
+ * FIXME: annotations do not use it, so it can be remove from my POV
+ * @deprecated
+ *
+ *
+ * @param query The sql query string
+ * @param resultSetRef The result-set-mapping name
+ * @param querySpaces Any specified query spaces (used for auto-flushing)
+ * @param cacheable Whether the query results are cacheable
+ * @param cacheRegion If cacheable, the region into which to store the results
+ * @param timeout A JDBC-level timeout to be applied
+ * @param fetchSize A JDBC-level fetch-size to be applied
+ * @param flushMode The flush mode to use for this query
+ * @param parameterTypes parameter type map
+ * @param callable Does the query string represent a callable object (i.e., proc)
+ */
+ public NamedSQLQueryDefinition(
+ String query,
+ String resultSetRef,
+ List querySpaces,
+ boolean cacheable,
+ String cacheRegion,
+ Integer timeout,
+ Integer fetchSize,
+ FlushMode flushMode,
+ Map parameterTypes,
+ boolean callable) {
+ this(
+ query,
+ resultSetRef,
+ querySpaces,
+ cacheable,
+ cacheRegion,
+ timeout,
+ fetchSize,
+ flushMode,
+ null,
+ false,
+ null,
+ parameterTypes,
+ callable
+ );
+ }
+
+ public NativeSQLQueryReturn[] getQueryReturns() {
+ return queryReturns;
+ }
+
+ public List getQuerySpaces() {
+ return querySpaces;
+ }
+
+ public boolean isCallable() {
+ return callable;
+ }
+
+ public String getResultSetRef() {
+ return resultSetRef;
+ }
+}
\ No newline at end of file
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/NamedSQLQueryDefinition.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/Nullability.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/Nullability.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/Nullability.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,183 @@
+//$Id: Nullability.java 7566 2005-07-20 07:16:33Z oneovthafew $
+package org.hibernate.engine;
+
+import java.util.Iterator;
+
+import org.hibernate.HibernateException;
+import org.hibernate.PropertyValueException;
+import org.hibernate.intercept.LazyPropertyInitializer;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.type.AbstractComponentType;
+import org.hibernate.type.CollectionType;
+import org.hibernate.type.Type;
+
+/**
+ * Implements the algorithm for validating property values
+ * for illegal null values
+ * @author Gavin King
+ */
+public final class Nullability {
+
+ private final SessionImplementor session;
+
+ public Nullability(SessionImplementor session) {
+ this.session = session;
+ }
+ /**
+ * Check nullability of the class persister properties
+ *
+ * @param values entity properties
+ * @param persister class persister
+ * @param isUpdate wether it is intended to be updated or saved
+ * @throws org.hibernate.PropertyValueException Break the nullability of one property
+ * @throws HibernateException error while getting Component values
+ */
+ public void checkNullability(
+ final Object[] values,
+ final EntityPersister persister,
+ final boolean isUpdate)
+ throws PropertyValueException, HibernateException {
+
+ /*
+ * Algorithm
+ * Check for any level one nullability breaks
+ * Look at non null components to
+ * recursively check next level of nullability breaks
+ * Look at Collections contraining component to
+ * recursively check next level of nullability breaks
+ *
+ *
+ * In the previous implementation, not-null stuffs where checked
+ * filtering by level one only updateable
+ * or insertable columns. So setting a sub component as update="false"
+ * has no effect on not-null check if the main component had good checkeability
+ * In this implementation, we keep this feature.
+ * However, I never see any documentation mentioning that, but it's for
+ * sure a limitation.
+ */
+
+ final boolean[] nullability = persister.getPropertyNullability();
+ final boolean[] checkability = isUpdate ?
+ persister.getPropertyUpdateability() :
+ persister.getPropertyInsertability();
+ final Type[] propertyTypes = persister.getPropertyTypes();
+
+ for ( int i = 0; i < values.length; i++ ) {
+
+ if ( checkability[i] && values[i]!=LazyPropertyInitializer.UNFETCHED_PROPERTY ) {
+ final Object value = values[i];
+ if ( !nullability[i] && value == null ) {
+
+ //check basic level one nullablilty
+ throw new PropertyValueException(
+ "not-null property references a null or transient value",
+ persister.getEntityName(),
+ persister.getPropertyNames()[i]
+ );
+
+ }
+ else if ( value != null ) {
+
+ //values is not null and is checkable, we'll look deeper
+ String breakProperties = checkSubElementsNullability( propertyTypes[i], value );
+ if ( breakProperties != null ) {
+ throw new PropertyValueException(
+ "not-null property references a null or transient value",
+ persister.getEntityName(),
+ buildPropertyPath( persister.getPropertyNames()[i], breakProperties )
+ );
+ }
+
+ }
+ }
+
+ }
+ }
+
+ /**
+ * check sub elements-nullability. Returns property path that break
+ * nullability or null if none
+ *
+ * @param propertyType type to check
+ * @param value value to check
+ *
+ * @return property path
+ * @throws HibernateException error while getting subcomponent values
+ */
+ private String checkSubElementsNullability(final Type propertyType, final Object value)
+ throws HibernateException {
+ //for non null args, check for components and elements containing components
+ if ( propertyType.isComponentType() ) {
+ return checkComponentNullability( value, (AbstractComponentType) propertyType );
+ }
+ else if ( propertyType.isCollectionType() ) {
+
+ //persistent collections may have components
+ CollectionType collectionType = (CollectionType) propertyType;
+ Type collectionElementType = collectionType.getElementType( session.getFactory() );
+ if ( collectionElementType.isComponentType() ) {
+ //check for all components values in the collection
+
+ AbstractComponentType componentType = (AbstractComponentType) collectionElementType;
+ Iterator iter = CascadingAction.getLoadedElementsIterator(session, collectionType, value);
+ while ( iter.hasNext() ) {
+ Object compValue = iter.next();
+ if (compValue != null) {
+ return checkComponentNullability(compValue, componentType);
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * check component nullability. Returns property path that break
+ * nullability or null if none
+ *
+ * @param value component properties
+ * @param compType component not-nullable type
+ *
+ * @return property path
+ * @throws HibernateException error while getting subcomponent values
+ */
+ private String checkComponentNullability(final Object value, final AbstractComponentType compType)
+ throws HibernateException {
+ /* will check current level if some of them are not null
+ * or sublevels if they exist
+ */
+ boolean[] nullability = compType.getPropertyNullability();
+ if ( nullability!=null ) {
+ //do the test
+ final Object[] values = compType.getPropertyValues( value, session.getEntityMode() );
+ final Type[] propertyTypes = compType.getSubtypes();
+ for ( int i=0; i<values.length; i++ ) {
+ final Object subvalue = values[i];
+ if ( !nullability[i] && subvalue==null ) {
+ return compType.getPropertyNames()[i];
+ }
+ else if ( subvalue != null ) {
+ String breakProperties = checkSubElementsNullability( propertyTypes[i], subvalue );
+ if ( breakProperties != null ) {
+ return buildPropertyPath( compType.getPropertyNames()[i], breakProperties );
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Return a well formed property path.
+ * Basicaly, it will return parent.child
+ *
+ * @param parent parent in path
+ * @param child child in path
+ * @return parent-child path
+ */
+ private static String buildPropertyPath(String parent, String child) {
+ return new StringBuffer( parent.length() + child.length() + 1 )
+ .append(parent).append('.').append(child).toString();
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/Nullability.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/ParameterBinder.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/ParameterBinder.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/ParameterBinder.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,114 @@
+// $Id: ParameterBinder.java 7385 2005-07-06 17:13:15Z steveebersole $
+package org.hibernate.engine;
+
+import org.hibernate.HibernateException;
+import org.hibernate.type.Type;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.Map;
+import java.util.Iterator;
+
+/**
+ * Centralizes the commonality regarding binding of parameter values into
+ * PreparedStatements as this logic is used in many places.
+ * <p/>
+ * Ideally would like to move to the parameter handling as it is done in
+ * the hql.ast package.
+ *
+ * @author Steve Ebersole
+ */
+public class ParameterBinder {
+
+ private static final Log log = LogFactory.getLog( ParameterBinder.class );
+
+ public static interface NamedParameterSource {
+ public int[] getNamedParameterLocations(String name);
+ }
+
+ private ParameterBinder() {
+ }
+
+ public static int bindQueryParameters(
+ final PreparedStatement st,
+ final QueryParameters queryParameters,
+ final int start,
+ final NamedParameterSource source,
+ SessionImplementor session) throws SQLException, HibernateException {
+ int col = start;
+ col += bindPositionalParameters( st, queryParameters, col, session );
+ col += bindNamedParameters( st, queryParameters, col, source, session );
+ return col;
+ }
+
+ public static int bindPositionalParameters(
+ final PreparedStatement st,
+ final QueryParameters queryParameters,
+ final int start,
+ final SessionImplementor session) throws SQLException, HibernateException {
+ return bindPositionalParameters(
+ st,
+ queryParameters.getPositionalParameterValues(),
+ queryParameters.getPositionalParameterTypes(),
+ start,
+ session
+ );
+ }
+
+ public static int bindPositionalParameters(
+ final PreparedStatement st,
+ final Object[] values,
+ final Type[] types,
+ final int start,
+ final SessionImplementor session) throws SQLException, HibernateException {
+ int span = 0;
+ for ( int i = 0; i < values.length; i++ ) {
+ types[i].nullSafeSet( st, values[i], start + span, session );
+ span += types[i].getColumnSpan( session.getFactory() );
+ }
+ return span;
+ }
+
+ public static int bindNamedParameters(
+ final PreparedStatement ps,
+ final QueryParameters queryParameters,
+ final int start,
+ final NamedParameterSource source,
+ final SessionImplementor session) throws SQLException, HibernateException {
+ return bindNamedParameters( ps, queryParameters.getNamedParameters(), start, source, session );
+ }
+
+ public static int bindNamedParameters(
+ final PreparedStatement ps,
+ final Map namedParams,
+ final int start,
+ final NamedParameterSource source,
+ final SessionImplementor session) throws SQLException, HibernateException {
+ if ( namedParams != null ) {
+ // assumes that types are all of span 1
+ Iterator iter = namedParams.entrySet().iterator();
+ int result = 0;
+ while ( iter.hasNext() ) {
+ Map.Entry e = ( Map.Entry ) iter.next();
+ String name = ( String ) e.getKey();
+ TypedValue typedval = ( TypedValue ) e.getValue();
+ int[] locations = source.getNamedParameterLocations( name );
+ for ( int i = 0; i < locations.length; i++ ) {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "bindNamedParameters() " +
+ typedval.getValue() + " -> " + name +
+ " [" + ( locations[i] + start ) + "]" );
+ }
+ typedval.getType().nullSafeSet( ps, typedval.getValue(), locations[i] + start, session );
+ }
+ result += locations.length;
+ }
+ return result;
+ }
+ else {
+ return 0;
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/PersistenceContext.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/PersistenceContext.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/PersistenceContext.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,440 @@
+//$Id: PersistenceContext.java 11319 2007-03-20 03:12:56Z steve.ebersole at jboss.com $
+package org.hibernate.engine;
+
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Map;
+
+import org.hibernate.HibernateException;
+import org.hibernate.LockMode;
+import org.hibernate.MappingException;
+import org.hibernate.engine.loading.LoadContexts;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.persister.entity.EntityPersister;
+
+/**
+ * Holds the state of the persistence context, including the
+ * first-level cache, entries, snapshots, proxies, etc.
+ *
+ * @author Gavin King
+ */
+public interface PersistenceContext {
+
+ public boolean isStateless();
+
+ /**
+ * Get the session to which this persistence context is bound.
+ *
+ * @return The session.
+ */
+ public SessionImplementor getSession();
+
+ /**
+ * Retrieve this persistence context's managed load context.
+ *
+ * @return The load context
+ */
+ public LoadContexts getLoadContexts();
+
+ /**
+ * Add a collection which has no owner loaded
+ */
+ public void addUnownedCollection(CollectionKey key, PersistentCollection collection);
+
+ /**
+ * Get and remove a collection whose owner is not yet loaded,
+ * when its owner is being loaded
+ */
+ public PersistentCollection useUnownedCollection(CollectionKey key);
+
+ /**
+ * Get the <tt>BatchFetchQueue</tt>, instantiating one if
+ * necessary.
+ */
+ public BatchFetchQueue getBatchFetchQueue();
+
+ /**
+ * Clear the state of the persistence context
+ */
+ public void clear();
+
+ /**
+ * @return false if we know for certain that all the entities are read-only
+ */
+ public boolean hasNonReadOnlyEntities();
+
+ /**
+ * Set the status of an entry
+ */
+ public void setEntryStatus(EntityEntry entry, Status status);
+
+ /**
+ * Called after transactions end
+ */
+ public void afterTransactionCompletion();
+
+ /**
+ * Get the current state of the entity as known to the underlying
+ * database, or null if there is no corresponding row
+ */
+ public Object[] getDatabaseSnapshot(Serializable id, EntityPersister persister)
+ throws HibernateException;
+
+ public Object[] getCachedDatabaseSnapshot(EntityKey key);
+
+ public Object[] getNaturalIdSnapshot(Serializable id, EntityPersister persister)
+ throws HibernateException;
+
+ /**
+ * Add a canonical mapping from entity key to entity instance
+ */
+ public void addEntity(EntityKey key, Object entity);
+
+ /**
+ * Get the entity instance associated with the given
+ * <tt>EntityKey</tt>
+ */
+ public Object getEntity(EntityKey key);
+
+ /**
+ * Is there an entity with the given key in the persistence context
+ */
+ public boolean containsEntity(EntityKey key);
+
+ /**
+ * Remove an entity from the session cache, also clear
+ * up other state associated with the entity, all except
+ * for the <tt>EntityEntry</tt>
+ */
+ public Object removeEntity(EntityKey key);
+
+ /**
+ * Get an entity cached by unique key
+ */
+ public Object getEntity(EntityUniqueKey euk);
+
+ /**
+ * Add an entity to the cache by unique key
+ */
+ public void addEntity(EntityUniqueKey euk, Object entity);
+
+ /**
+ * Retreive the EntityEntry representation of the given entity.
+ *
+ * @param entity The entity for which to locate the EntityEntry.
+ * @return The EntityEntry for the given entity.
+ */
+ public EntityEntry getEntry(Object entity);
+
+ /**
+ * Remove an entity entry from the session cache
+ */
+ public EntityEntry removeEntry(Object entity);
+
+ /**
+ * Is there an EntityEntry for this instance?
+ */
+ public boolean isEntryFor(Object entity);
+
+ /**
+ * Get the collection entry for a persistent collection
+ */
+ public CollectionEntry getCollectionEntry(PersistentCollection coll);
+
+ /**
+ * Adds an entity to the internal caches.
+ */
+ public EntityEntry addEntity(final Object entity, final Status status,
+ final Object[] loadedState, final EntityKey entityKey, final Object version,
+ final LockMode lockMode, final boolean existsInDatabase,
+ final EntityPersister persister, final boolean disableVersionIncrement, boolean lazyPropertiesAreUnfetched);
+
+ /**
+ * Generates an appropriate EntityEntry instance and adds it
+ * to the event source's internal caches.
+ */
+ public EntityEntry addEntry(final Object entity, final Status status,
+ final Object[] loadedState, final Object rowId, final Serializable id,
+ final Object version, final LockMode lockMode, final boolean existsInDatabase,
+ final EntityPersister persister, final boolean disableVersionIncrement, boolean lazyPropertiesAreUnfetched);
+
+ /**
+ * Is the given collection associated with this persistence context?
+ */
+ public boolean containsCollection(PersistentCollection collection);
+
+ /**
+ * Is the given proxy associated with this persistence context?
+ */
+ public boolean containsProxy(Object proxy);
+
+ /**
+ * Takes the given object and, if it represents a proxy, reassociates it with this event source.
+ *
+ * @param value The possible proxy to be reassociated.
+ * @return Whether the passed value represented an actual proxy which got initialized.
+ * @throws MappingException
+ */
+ public boolean reassociateIfUninitializedProxy(Object value) throws MappingException;
+
+ /**
+ * If a deleted entity instance is re-saved, and it has a proxy, we need to
+ * reset the identifier of the proxy
+ */
+ public void reassociateProxy(Object value, Serializable id) throws MappingException;
+
+ /**
+ * Get the entity instance underlying the given proxy, throwing
+ * an exception if the proxy is uninitialized. If the given object
+ * is not a proxy, simply return the argument.
+ */
+ public Object unproxy(Object maybeProxy) throws HibernateException;
+
+ /**
+ * Possibly unproxy the given reference and reassociate it with the current session.
+ *
+ * @param maybeProxy The reference to be unproxied if it currently represents a proxy.
+ * @return The unproxied instance.
+ * @throws HibernateException
+ */
+ public Object unproxyAndReassociate(Object maybeProxy) throws HibernateException;
+
+ /**
+ * Attempts to check whether the given key represents an entity already loaded within the
+ * current session.
+ * @param object The entity reference against which to perform the uniqueness check.
+ * @throws HibernateException
+ */
+ public void checkUniqueness(EntityKey key, Object object) throws HibernateException;
+
+ /**
+ * If the existing proxy is insufficiently "narrow" (derived), instantiate a new proxy
+ * and overwrite the registration of the old one. This breaks == and occurs only for
+ * "class" proxies rather than "interface" proxies. Also init the proxy to point to
+ * the given target implementation if necessary.
+ *
+ * @param proxy The proxy instance to be narrowed.
+ * @param persister The persister for the proxied entity.
+ * @param key The internal cache key for the proxied entity.
+ * @param object (optional) the actual proxied entity instance.
+ * @return An appropriately narrowed instance.
+ * @throws HibernateException
+ */
+ public Object narrowProxy(Object proxy, EntityPersister persister, EntityKey key, Object object)
+ throws HibernateException;
+
+ /**
+ * Return the existing proxy associated with the given <tt>EntityKey</tt>, or the
+ * third argument (the entity associated with the key) if no proxy exists. Init
+ * the proxy to the target implementation, if necessary.
+ */
+ public Object proxyFor(EntityPersister persister, EntityKey key, Object impl)
+ throws HibernateException;
+
+ /**
+ * Return the existing proxy associated with the given <tt>EntityKey</tt>, or the
+ * argument (the entity associated with the key) if no proxy exists.
+ * (slower than the form above)
+ */
+ public Object proxyFor(Object impl) throws HibernateException;
+
+ /**
+ * Get the entity that owns this persistent collection
+ */
+ public Object getCollectionOwner(Serializable key, CollectionPersister collectionPersister)
+ throws MappingException;
+
+ /**
+ * add a collection we just loaded up (still needs initializing)
+ */
+ public void addUninitializedCollection(CollectionPersister persister,
+ PersistentCollection collection, Serializable id);
+
+ /**
+ * add a detached uninitialized collection
+ */
+ public void addUninitializedDetachedCollection(CollectionPersister persister,
+ PersistentCollection collection);
+
+ /**
+ * Add a new collection (ie. a newly created one, just instantiated by the
+ * application, with no database state or snapshot)
+ * @param collection The collection to be associated with the persistence context
+ */
+ public void addNewCollection(CollectionPersister persister, PersistentCollection collection)
+ throws HibernateException;
+
+ /**
+ * add an (initialized) collection that was created by another session and passed
+ * into update() (ie. one with a snapshot and existing state on the database)
+ */
+ public void addInitializedDetachedCollection(CollectionPersister collectionPersister,
+ PersistentCollection collection) throws HibernateException;
+
+ /**
+ * add a collection we just pulled out of the cache (does not need initializing)
+ */
+ public CollectionEntry addInitializedCollection(CollectionPersister persister,
+ PersistentCollection collection, Serializable id) throws HibernateException;
+
+ /**
+ * Get the collection instance associated with the <tt>CollectionKey</tt>
+ */
+ public PersistentCollection getCollection(CollectionKey collectionKey);
+
+ /**
+ * Register a collection for non-lazy loading at the end of the
+ * two-phase load
+ */
+ public void addNonLazyCollection(PersistentCollection collection);
+
+ /**
+ * Force initialization of all non-lazy collections encountered during
+ * the current two-phase load (actually, this is a no-op, unless this
+ * is the "outermost" load)
+ */
+ public void initializeNonLazyCollections() throws HibernateException;
+
+ /**
+ * Get the <tt>PersistentCollection</tt> object for an array
+ */
+ public PersistentCollection getCollectionHolder(Object array);
+
+ /**
+ * Register a <tt>PersistentCollection</tt> object for an array.
+ * Associates a holder with an array - MUST be called after loading
+ * array, since the array instance is not created until endLoad().
+ */
+ public void addCollectionHolder(PersistentCollection holder);
+
+ /**
+ * Remove the mapping of collection to holder during eviction
+ * of the owning entity
+ */
+ public PersistentCollection removeCollectionHolder(Object array);
+
+ /**
+ * Get the snapshot of the pre-flush collection state
+ */
+ public Serializable getSnapshot(PersistentCollection coll);
+
+ /**
+ * Get the collection entry for a collection passed to filter,
+ * which might be a collection wrapper, an array, or an unwrapped
+ * collection. Return null if there is no entry.
+ */
+ public CollectionEntry getCollectionEntryOrNull(Object collection);
+
+ /**
+ * Get an existing proxy by key
+ */
+ public Object getProxy(EntityKey key);
+
+ /**
+ * Add a proxy to the session cache
+ */
+ public void addProxy(EntityKey key, Object proxy);
+
+ /**
+ * Remove a proxy from the session cache
+ */
+ public Object removeProxy(EntityKey key);
+
+ /**
+ * Retrieve the set of EntityKeys representing nullifiable references
+ */
+ public HashSet getNullifiableEntityKeys();
+
+ /**
+ * Get the mapping from key value to entity instance
+ */
+ public Map getEntitiesByKey();
+
+ /**
+ * Get the mapping from entity instance to entity entry
+ */
+ public Map getEntityEntries();
+
+ /**
+ * Get the mapping from collection instance to collection entry
+ */
+ public Map getCollectionEntries();
+
+ /**
+ * Get the mapping from collection key to collection instance
+ */
+ public Map getCollectionsByKey();
+
+ /**
+ * How deep are we cascaded?
+ */
+ public int getCascadeLevel();
+
+ /**
+ * Called before cascading
+ */
+ public int incrementCascadeLevel();
+
+ /**
+ * Called after cascading
+ */
+ public int decrementCascadeLevel();
+
+ /**
+ * Is a flush cycle currently in process?
+ */
+ public boolean isFlushing();
+
+ /**
+ * Called before and after the flushcycle
+ */
+ public void setFlushing(boolean flushing);
+
+ /**
+ * Call this before begining a two-phase load
+ */
+ public void beforeLoad();
+
+ /**
+ * Call this after finishing a two-phase load
+ */
+ public void afterLoad();
+
+ /**
+ * Returns a string representation of the object.
+ *
+ * @return a string representation of the object.
+ */
+ public String toString();
+
+ /**
+ * Search the persistence context for an owner for the child object,
+ * given a collection role
+ */
+ public Serializable getOwnerId(String entity, String property, Object childObject, Map mergeMap);
+
+ /**
+ * Search the persistence context for an index of the child object,
+ * given a collection role
+ */
+ public Object getIndexInOwner(String entity, String property, Object childObject, Map mergeMap);
+
+ /**
+ * Record the fact that the association belonging to the keyed
+ * entity is null.
+ */
+ public void addNullProperty(EntityKey ownerKey, String propertyName);
+
+ /**
+ * Is the association property belonging to the keyed entity null?
+ */
+ public boolean isPropertyNull(EntityKey ownerKey, String propertyName);
+
+ /**
+ * Set the object to read only and discard it's snapshot
+ */
+ public void setReadOnly(Object entity, boolean readOnly);
+
+ void replaceDelayedEntityIdentityInsertKeys(EntityKey oldKey, Serializable generatedId);
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/QueryParameters.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/QueryParameters.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/QueryParameters.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,482 @@
+//$Id: QueryParameters.java 9636 2006-03-16 14:14:48Z max.andersen at jboss.com $
+package org.hibernate.engine;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.hibernate.HibernateException;
+import org.hibernate.QueryException;
+import org.hibernate.ScrollMode;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.hql.classic.ParserHelper;
+import org.hibernate.pretty.Printer;
+import org.hibernate.transform.ResultTransformer;
+import org.hibernate.type.Type;
+import org.hibernate.util.ArrayHelper;
+
+/**
+ * @author Gavin King
+ */
+public final class QueryParameters {
+ private static final Log log = LogFactory.getLog(QueryParameters.class);
+
+ private Type[] positionalParameterTypes;
+ private Object[] positionalParameterValues;
+ private Map namedParameters;
+ private Map lockModes;
+ private RowSelection rowSelection;
+ private boolean cacheable;
+ private String cacheRegion;
+ private String comment;
+ private ScrollMode scrollMode;
+ private Serializable[] collectionKeys;
+ private Object optionalObject;
+ private String optionalEntityName;
+ private Serializable optionalId;
+ private boolean readOnly;
+ private boolean callable = false;
+ private boolean autodiscovertypes = false;
+ private boolean isNaturalKeyLookup;
+
+ private final ResultTransformer resultTransformer; // why is all others non final ?
+
+ private String processedSQL;
+ private Type[] processedPositionalParameterTypes;
+ private Object[] processedPositionalParameterValues;
+
+ public QueryParameters() {
+ this( ArrayHelper.EMPTY_TYPE_ARRAY, ArrayHelper.EMPTY_OBJECT_ARRAY );
+ }
+
+ public QueryParameters(Type type, Object value) {
+ this( new Type[] {type}, new Object[] {value} );
+ }
+
+ public QueryParameters(
+ final Type[] positionalParameterTypes,
+ final Object[] postionalParameterValues,
+ final Object optionalObject,
+ final String optionalEntityName,
+ final Serializable optionalObjectId
+ ) {
+ this(positionalParameterTypes, postionalParameterValues);
+ this.optionalObject = optionalObject;
+ this.optionalId = optionalObjectId;
+ this.optionalEntityName = optionalEntityName;
+
+ }
+
+ public QueryParameters(
+ final Type[] positionalParameterTypes,
+ final Object[] postionalParameterValues
+ ) {
+ this(
+ positionalParameterTypes,
+ postionalParameterValues,
+ null,
+ null,
+ false,
+ null,
+ null,
+ false,
+ null
+ );
+ }
+
+ public QueryParameters(
+ final Type[] positionalParameterTypes,
+ final Object[] postionalParameterValues,
+ final Serializable[] collectionKeys
+ ) {
+ this(
+ positionalParameterTypes,
+ postionalParameterValues,
+ null,
+ collectionKeys
+ );
+ }
+
+ public QueryParameters(
+ final Type[] positionalParameterTypes,
+ final Object[] postionalParameterValues,
+ final Map namedParameters,
+ final Serializable[] collectionKeys
+ ) {
+ this(
+ positionalParameterTypes,
+ postionalParameterValues,
+ namedParameters,
+ null,
+ null,
+ false,
+ false,
+ null,
+ null,
+ collectionKeys,
+ null
+ );
+ }
+
+ public QueryParameters(
+ final Type[] positionalParameterTypes,
+ final Object[] positionalParameterValues,
+ final Map lockModes,
+ final RowSelection rowSelection,
+ final boolean cacheable,
+ final String cacheRegion,
+ //final boolean forceCacheRefresh,
+ final String comment,
+ final boolean isLookupByNaturalKey,
+ final ResultTransformer transformer
+ ) {
+ this(
+ positionalParameterTypes,
+ positionalParameterValues,
+ null,
+ lockModes,
+ rowSelection,
+ false,
+ cacheable,
+ cacheRegion,
+ comment,
+ null,
+ transformer
+ );
+ isNaturalKeyLookup = isLookupByNaturalKey;
+ }
+
+ public QueryParameters(
+ final Type[] positionalParameterTypes,
+ final Object[] positionalParameterValues,
+ final Map namedParameters,
+ final Map lockModes,
+ final RowSelection rowSelection,
+ final boolean readOnly,
+ final boolean cacheable,
+ final String cacheRegion,
+ //final boolean forceCacheRefresh,
+ final String comment,
+ final Serializable[] collectionKeys,
+ ResultTransformer transformer
+ ) {
+ this.positionalParameterTypes = positionalParameterTypes;
+ this.positionalParameterValues = positionalParameterValues;
+ this.namedParameters = namedParameters;
+ this.lockModes = lockModes;
+ this.rowSelection = rowSelection;
+ this.cacheable = cacheable;
+ this.cacheRegion = cacheRegion;
+ //this.forceCacheRefresh = forceCacheRefresh;
+ this.comment = comment;
+ this.collectionKeys = collectionKeys;
+ this.readOnly = readOnly;
+ this.resultTransformer = transformer;
+ }
+
+ public QueryParameters(
+ final Type[] positionalParameterTypes,
+ final Object[] positionalParameterValues,
+ final Map namedParameters,
+ final Map lockModes,
+ final RowSelection rowSelection,
+ final boolean readOnly,
+ final boolean cacheable,
+ final String cacheRegion,
+ //final boolean forceCacheRefresh,
+ final String comment,
+ final Serializable[] collectionKeys,
+ final Object optionalObject,
+ final String optionalEntityName,
+ final Serializable optionalId,
+ final ResultTransformer transformer
+ ) {
+ this(
+ positionalParameterTypes,
+ positionalParameterValues,
+ namedParameters,
+ lockModes,
+ rowSelection,
+ readOnly,
+ cacheable,
+ cacheRegion,
+ comment,
+ collectionKeys,
+ transformer
+ );
+ this.optionalEntityName = optionalEntityName;
+ this.optionalId = optionalId;
+ this.optionalObject = optionalObject;
+ }
+
+ public boolean hasRowSelection() {
+ return rowSelection!=null;
+ }
+
+ public Map getNamedParameters() {
+ return namedParameters;
+ }
+
+ public Type[] getPositionalParameterTypes() {
+ return positionalParameterTypes;
+ }
+
+ public Object[] getPositionalParameterValues() {
+ return positionalParameterValues;
+ }
+
+ public RowSelection getRowSelection() {
+ return rowSelection;
+ }
+
+ public ResultTransformer getResultTransformer() {
+ return resultTransformer;
+ }
+
+ public void setNamedParameters(Map map) {
+ namedParameters = map;
+ }
+
+ public void setPositionalParameterTypes(Type[] types) {
+ positionalParameterTypes = types;
+ }
+
+ public void setPositionalParameterValues(Object[] objects) {
+ positionalParameterValues = objects;
+ }
+
+ public void setRowSelection(RowSelection selection) {
+ rowSelection = selection;
+ }
+
+ public Map getLockModes() {
+ return lockModes;
+ }
+
+ public void setLockModes(Map map) {
+ lockModes = map;
+ }
+
+ public void traceParameters(SessionFactoryImplementor factory) throws HibernateException {
+ Printer print = new Printer(factory);
+ if (positionalParameterValues.length!=0) {
+ log.trace(
+ "parameters: " +
+ print.toString(positionalParameterTypes, positionalParameterValues)
+ );
+ }
+ if (namedParameters!=null) {
+ log.trace( "named parameters: " + print.toString(namedParameters) );
+ }
+ }
+
+ public boolean isCacheable() {
+ return cacheable;
+ }
+
+ public void setCacheable(boolean b) {
+ cacheable = b;
+ }
+
+ public String getCacheRegion() {
+ return cacheRegion;
+ }
+
+ public void setCacheRegion(String cacheRegion) {
+ this.cacheRegion = cacheRegion;
+ }
+
+ public void validateParameters() throws QueryException {
+ int types = positionalParameterTypes==null ? 0 : positionalParameterTypes.length;
+ int values = positionalParameterValues==null ? 0 : positionalParameterValues.length;
+ if (types!=values) {
+ throw new QueryException(
+ "Number of positional parameter types:" + types +
+ " does not match number of positional parameters: " + values
+ );
+ }
+ }
+
+ public String getComment() {
+ return comment;
+ }
+
+ public void setComment(String comment) {
+ this.comment = comment;
+ }
+
+ public ScrollMode getScrollMode() {
+ return scrollMode;
+ }
+
+ public void setScrollMode(ScrollMode scrollMode) {
+ this.scrollMode = scrollMode;
+ }
+
+ public Serializable[] getCollectionKeys() {
+ return collectionKeys;
+ }
+
+ public void setCollectionKeys(Serializable[] collectionKeys) {
+ this.collectionKeys = collectionKeys;
+ }
+
+ public String getOptionalEntityName() {
+ return optionalEntityName;
+ }
+
+ public void setOptionalEntityName(String optionalEntityName) {
+ this.optionalEntityName = optionalEntityName;
+ }
+
+ public Serializable getOptionalId() {
+ return optionalId;
+ }
+
+ public void setOptionalId(Serializable optionalId) {
+ this.optionalId = optionalId;
+ }
+
+ public Object getOptionalObject() {
+ return optionalObject;
+ }
+
+ public void setOptionalObject(Object optionalObject) {
+ this.optionalObject = optionalObject;
+ }
+
+ public boolean isReadOnly() {
+ return readOnly;
+ }
+
+ public void setReadOnly(boolean readOnly) {
+ this.readOnly = readOnly;
+ }
+
+ public void setCallable(boolean callable) {
+ this.callable = callable;
+ }
+
+ public boolean isCallable() {
+ return callable;
+ }
+
+ public boolean hasAutoDiscoverScalarTypes() {
+ return autodiscovertypes;
+ }
+
+ public void processFilters(String sql, SessionImplementor session) {
+
+ if ( session.getEnabledFilters().size()==0 || sql.indexOf(ParserHelper.HQL_VARIABLE_PREFIX)<0 ) {
+ // HELLA IMPORTANT OPTIMIZATION!!!
+ processedPositionalParameterValues = getPositionalParameterValues();
+ processedPositionalParameterTypes = getPositionalParameterTypes();
+ processedSQL = sql;
+ }
+ else {
+
+ Dialect dialect = session.getFactory().getDialect();
+ String symbols = new StringBuffer().append( ParserHelper.HQL_SEPARATORS )
+ .append( dialect.openQuote() )
+ .append( dialect.closeQuote() )
+ .toString();
+ StringTokenizer tokens = new StringTokenizer( sql, symbols, true );
+ StringBuffer result = new StringBuffer();
+
+ List parameters = new ArrayList();
+ List parameterTypes = new ArrayList();
+
+ while ( tokens.hasMoreTokens() ) {
+ final String token = tokens.nextToken();
+ if ( token.startsWith( ParserHelper.HQL_VARIABLE_PREFIX ) ) {
+ String filterParameterName = token.substring( 1 );
+ Object value = session.getFilterParameterValue( filterParameterName );
+ Type type = session.getFilterParameterType( filterParameterName );
+ if ( value != null && Collection.class.isAssignableFrom( value.getClass() ) ) {
+ Iterator itr = ( ( Collection ) value ).iterator();
+ while ( itr.hasNext() ) {
+ Object elementValue = itr.next();
+ result.append( '?' );
+ parameters.add( elementValue );
+ parameterTypes.add( type );
+ if ( itr.hasNext() ) {
+ result.append( ", " );
+ }
+ }
+ }
+ else {
+ result.append( '?' );
+ parameters.add( value );
+ parameterTypes.add( type );
+ }
+ }
+ else {
+ result.append( token );
+ }
+ }
+ parameters.addAll( Arrays.asList( getPositionalParameterValues() ) );
+ parameterTypes.addAll( Arrays.asList( getPositionalParameterTypes() ) );
+ processedPositionalParameterValues = parameters.toArray();
+ processedPositionalParameterTypes = ( Type[] ) parameterTypes.toArray( new Type[0] );
+ processedSQL = result.toString();
+
+ }
+ }
+
+ public String getFilteredSQL() {
+ return processedSQL;
+ }
+
+ public Object[] getFilteredPositionalParameterValues() {
+ return processedPositionalParameterValues;
+ }
+
+ public Type[] getFilteredPositionalParameterTypes() {
+ return processedPositionalParameterTypes;
+ }
+
+ public boolean isNaturalKeyLookup() {
+ return isNaturalKeyLookup;
+ }
+
+ public void setNaturalKeyLookup(boolean isNaturalKeyLookup) {
+ this.isNaturalKeyLookup = isNaturalKeyLookup;
+ }
+
+ public void setAutoDiscoverScalarTypes(boolean autodiscovertypes) {
+ this.autodiscovertypes = autodiscovertypes;
+ }
+
+ public QueryParameters createCopyUsing(RowSelection selection) {
+ QueryParameters copy = new QueryParameters(
+ this.positionalParameterTypes,
+ this.positionalParameterValues,
+ this.namedParameters,
+ this.lockModes,
+ selection,
+ this.readOnly,
+ this.cacheable,
+ this.cacheRegion,
+ this.comment,
+ this.collectionKeys,
+ this.optionalObject,
+ this.optionalEntityName,
+ this.optionalId,
+ this.resultTransformer
+ );
+ copy.processedSQL = this.processedSQL;
+ copy.processedPositionalParameterTypes = this.processedPositionalParameterTypes;
+ copy.processedPositionalParameterValues = this.processedPositionalParameterValues;
+ return copy;
+ }
+
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/ResultSetMappingDefinition.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/ResultSetMappingDefinition.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/ResultSetMappingDefinition.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,46 @@
+//$Id: ResultSetMappingDefinition.java 10018 2006-06-15 05:21:06Z steve.ebersole at jboss.com $
+package org.hibernate.engine;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.io.Serializable;
+
+import org.hibernate.engine.query.sql.NativeSQLQueryReturn;
+
+/**
+ * Keep a description of the resultset mapping
+ *
+ * @author Emmanuel Bernard
+ */
+public class ResultSetMappingDefinition implements Serializable {
+
+ private final String name;
+ private final List /*NativeSQLQueryReturn*/ queryReturns = new ArrayList();
+
+ public ResultSetMappingDefinition(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void addQueryReturn(NativeSQLQueryReturn queryReturn) {
+ queryReturns.add( queryReturn );
+ }
+
+// We could also keep these if needed for binary compatibility with annotations, provided
+// it only uses the addXXX() methods...
+// public void addEntityQueryReturn(NativeSQLQueryNonScalarReturn entityQueryReturn) {
+// entityQueryReturns.add(entityQueryReturn);
+// }
+//
+// public void addScalarQueryReturn(NativeSQLQueryScalarReturn scalarQueryReturn) {
+// scalarQueryReturns.add(scalarQueryReturn);
+// }
+
+ public NativeSQLQueryReturn[] getQueryReturns() {
+ return ( NativeSQLQueryReturn[] ) queryReturns.toArray( new NativeSQLQueryReturn[0] );
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/RowSelection.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/RowSelection.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/RowSelection.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,50 @@
+//$Id: RowSelection.java 9299 2006-02-16 22:51:00Z steveebersole $
+package org.hibernate.engine;
+
+/**
+ * Represents a selection of rows in a JDBC <tt>ResultSet</tt>
+ * @author Gavin King
+ */
+public final class RowSelection {
+ private Integer firstRow;
+ private Integer maxRows;
+ private Integer timeout;
+ private Integer fetchSize;
+
+ public void setFirstRow(Integer firstRow) {
+ this.firstRow = firstRow;
+ }
+
+ public Integer getFirstRow() {
+ return firstRow;
+ }
+
+ public void setMaxRows(Integer maxRows) {
+ this.maxRows = maxRows;
+ }
+
+ public Integer getMaxRows() {
+ return maxRows;
+ }
+
+ public void setTimeout(Integer timeout) {
+ this.timeout = timeout;
+ }
+
+ public Integer getTimeout() {
+ return timeout;
+ }
+
+ public Integer getFetchSize() {
+ return fetchSize;
+ }
+
+ public void setFetchSize(Integer fetchSize) {
+ this.fetchSize = fetchSize;
+ }
+
+ public boolean definesLimits() {
+ return maxRows != null ||
+ ( firstRow != null && firstRow.intValue() <= 0 );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/SessionFactoryImplementor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/SessionFactoryImplementor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/SessionFactoryImplementor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,200 @@
+//$Id: SessionFactoryImplementor.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.engine;
+
+import java.util.Map;
+import java.util.Set;
+import java.sql.Connection;
+
+import javax.transaction.TransactionManager;
+
+import org.hibernate.HibernateException;
+import org.hibernate.Interceptor;
+import org.hibernate.MappingException;
+import org.hibernate.SessionFactory;
+import org.hibernate.ConnectionReleaseMode;
+import org.hibernate.proxy.EntityNotFoundDelegate;
+import org.hibernate.engine.query.QueryPlanCache;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.cache.QueryCache;
+import org.hibernate.cache.UpdateTimestampsCache;
+import org.hibernate.cache.Region;
+import org.hibernate.cfg.Settings;
+import org.hibernate.connection.ConnectionProvider;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.function.SQLFunctionRegistry;
+import org.hibernate.exception.SQLExceptionConverter;
+import org.hibernate.id.IdentifierGenerator;
+import org.hibernate.stat.StatisticsImplementor;
+import org.hibernate.type.Type;
+
+/**
+ * Defines the internal contract between the <tt>SessionFactory</tt> and other parts of
+ * Hibernate such as implementors of <tt>Type</tt>.
+ *
+ * @see org.hibernate.SessionFactory
+ * @see org.hibernate.impl.SessionFactoryImpl
+ * @author Gavin King
+ */
+public interface SessionFactoryImplementor extends Mapping, SessionFactory {
+
+ /**
+ * Get the persister for the named entity
+ *
+ * @param entityName The name of the entity for which to retrieve the persister.
+ * @return The persister
+ * @throws MappingException Indicates persister could not be found with that name.
+ */
+ public EntityPersister getEntityPersister(String entityName) throws MappingException;
+
+ /**
+ * Get the persister object for a collection role.
+ *
+ * @param role The role (name) of the collection for which to retrieve the
+ * persister.
+ * @return The persister
+ * @throws MappingException Indicates persister could not be found with that role.
+ */
+ public CollectionPersister getCollectionPersister(String role) throws MappingException;
+
+ /**
+ * Get the SQL dialect.
+ * <p/>
+ * Shorthand for {@link #getSettings()}.{@link Settings#getDialect()}
+ *
+ * @return The dialect
+ */
+ public Dialect getDialect();
+
+ /**
+ * Get the factory scoped interceptor for this factory.
+ *
+ * @return The factory scope interceptor, or null if none.
+ */
+ public Interceptor getInterceptor();
+
+ public QueryPlanCache getQueryPlanCache();
+
+ /**
+ * Get the return types of a query
+ */
+ public Type[] getReturnTypes(String queryString) throws HibernateException;
+
+ /**
+ * Get the return aliases of a query
+ */
+ public String[] getReturnAliases(String queryString) throws HibernateException;
+
+ /**
+ * Get the connection provider
+ */
+ public ConnectionProvider getConnectionProvider();
+ /**
+ * Get the names of all persistent classes that implement/extend the given interface/class
+ */
+ public String[] getImplementors(String className) throws MappingException;
+ /**
+ * Get a class name, using query language imports
+ */
+ public String getImportedClassName(String name);
+
+
+ /**
+ * Get the JTA transaction manager
+ */
+ public TransactionManager getTransactionManager();
+
+
+ /**
+ * Get the default query cache
+ */
+ public QueryCache getQueryCache();
+ /**
+ * Get a particular named query cache, or the default cache
+ * @param regionName the name of the cache region, or null for the default query cache
+ * @return the existing cache, or a newly created cache if none by that region name
+ */
+ public QueryCache getQueryCache(String regionName) throws HibernateException;
+
+ /**
+ * Get the cache of table update timestamps
+ */
+ public UpdateTimestampsCache getUpdateTimestampsCache();
+ /**
+ * Statistics SPI
+ */
+ public StatisticsImplementor getStatisticsImplementor();
+
+ public NamedQueryDefinition getNamedQuery(String queryName);
+ public NamedSQLQueryDefinition getNamedSQLQuery(String queryName);
+ public ResultSetMappingDefinition getResultSetMapping(String name);
+
+ /**
+ * Get the identifier generator for the hierarchy
+ */
+ public IdentifierGenerator getIdentifierGenerator(String rootEntityName);
+
+ /**
+ * Get a named second-level cache region
+ *
+ * @param regionName The name of the region to retrieve.
+ * @return The region
+ */
+ public Region getSecondLevelCacheRegion(String regionName);
+
+ /**
+ * Get a map of all the second level cache regions currently maintained in
+ * this session factory. The map is structured with the region name as the
+ * key and the {@link Region} instances as the values.
+ *
+ * @return The map of regions
+ */
+ public Map getAllSecondLevelCacheRegions();
+
+ /**
+ * Retrieves the SQLExceptionConverter in effect for this SessionFactory.
+ *
+ * @return The SQLExceptionConverter for this SessionFactory.
+ */
+ public SQLExceptionConverter getSQLExceptionConverter();
+
+ public Settings getSettings();
+
+ /**
+ * Get a nontransactional "current" session for Hibernate EntityManager
+ */
+ public org.hibernate.classic.Session openTemporarySession() throws HibernateException;
+
+ /**
+ * Open a session conforming to the given parameters. Used mainly by
+ * {@link org.hibernate.context.JTASessionContext} for current session processing.
+ *
+ * @param connection The external jdbc connection to use, if one (i.e., optional).
+ * @param flushBeforeCompletionEnabled Should the session be auto-flushed
+ * prior to transaction completion?
+ * @param autoCloseSessionEnabled Should the session be auto-closed after
+ * transaction completion?
+ * @param connectionReleaseMode The release mode for managed jdbc connections.
+ * @return An appropriate session.
+ * @throws HibernateException
+ */
+ public org.hibernate.classic.Session openSession(
+ final Connection connection,
+ final boolean flushBeforeCompletionEnabled,
+ final boolean autoCloseSessionEnabled,
+ final ConnectionReleaseMode connectionReleaseMode) throws HibernateException;
+
+ /**
+ * Retrieves a set of all the collection roles in which the given entity
+ * is a participant, as either an index or an element.
+ *
+ * @param entityName The entity name for which to get the collection roles.
+ * @return set of all the collection roles in which the given entityName participates.
+ */
+ public Set getCollectionRolesByEntityParticipant(String entityName);
+
+ public EntityNotFoundDelegate getEntityNotFoundDelegate();
+
+ public SQLFunctionRegistry getSqlFunctionRegistry();
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/SessionImplementor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/SessionImplementor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/SessionImplementor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,302 @@
+//$Id: SessionImplementor.java 10018 2006-06-15 05:21:06Z steve.ebersole at jboss.com $
+package org.hibernate.engine;
+
+import java.io.Serializable;
+import java.sql.Connection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.hibernate.CacheMode;
+import org.hibernate.EntityMode;
+import org.hibernate.FlushMode;
+import org.hibernate.HibernateException;
+import org.hibernate.Interceptor;
+import org.hibernate.Query;
+import org.hibernate.ScrollMode;
+import org.hibernate.ScrollableResults;
+import org.hibernate.Transaction;
+import org.hibernate.engine.query.sql.NativeSQLQuerySpecification;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.event.EventListeners;
+import org.hibernate.impl.CriteriaImpl;
+import org.hibernate.jdbc.Batcher;
+import org.hibernate.jdbc.JDBCContext;
+import org.hibernate.loader.custom.CustomQuery;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.type.Type;
+
+
+/**
+ * Defines the internal contract between the <tt>Session</tt> and other parts of
+ * Hibernate such as implementors of <tt>Type</tt> or <tt>EntityPersister</tt>.
+ *
+ * @see org.hibernate.Session the interface to the application
+ * @see org.hibernate.impl.SessionImpl the actual implementation
+ * @author Gavin King
+ */
+public interface SessionImplementor extends Serializable {
+
+ /**
+ * Retrieves the interceptor currently in use by this event source.
+ *
+ * @return The interceptor.
+ */
+ public Interceptor getInterceptor();
+
+ /**
+ * Enable/disable automatic cache clearing from after transaction
+ * completion (for EJB3)
+ */
+ public void setAutoClear(boolean enabled);
+
+ /**
+ * Does this <tt>Session</tt> have an active Hibernate transaction
+ * or is there a JTA transaction in progress?
+ */
+ public boolean isTransactionInProgress();
+
+ /**
+ * Initialize the collection (if not already initialized)
+ */
+ public void initializeCollection(PersistentCollection collection, boolean writing)
+ throws HibernateException;
+
+ /**
+ * Load an instance without checking if it was deleted.
+ *
+ * When <tt>nullable</tt> is disabled this method may create a new proxy or
+ * return an existing proxy; if it does not exist, throw an exception.
+ *
+ * When <tt>nullable</tt> is enabled, the method does not create new proxies
+ * (but might return an existing proxy); if it does not exist, return
+ * <tt>null</tt>.
+ *
+ * When <tt>eager</tt> is enabled, the object is eagerly fetched
+ */
+ public Object internalLoad(String entityName, Serializable id, boolean eager, boolean nullable)
+ throws HibernateException;
+
+ /**
+ * Load an instance immediately. This method is only called when lazily initializing a proxy.
+ * Do not return the proxy.
+ */
+ public Object immediateLoad(String entityName, Serializable id) throws HibernateException;
+
+ /**
+ * System time before the start of the transaction
+ */
+ public long getTimestamp();
+ /**
+ * Get the creating <tt>SessionFactoryImplementor</tt>
+ */
+ public SessionFactoryImplementor getFactory();
+ /**
+ * Get the prepared statement <tt>Batcher</tt> for this session
+ */
+ public Batcher getBatcher();
+
+ /**
+ * Execute a <tt>find()</tt> query
+ */
+ public List list(String query, QueryParameters queryParameters) throws HibernateException;
+ /**
+ * Execute an <tt>iterate()</tt> query
+ */
+ public Iterator iterate(String query, QueryParameters queryParameters) throws HibernateException;
+ /**
+ * Execute a <tt>scroll()</tt> query
+ */
+ public ScrollableResults scroll(String query, QueryParameters queryParameters) throws HibernateException;
+ /**
+ * Execute a criteria query
+ */
+ public ScrollableResults scroll(CriteriaImpl criteria, ScrollMode scrollMode);
+ /**
+ * Execute a criteria query
+ */
+ public List list(CriteriaImpl criteria);
+
+ /**
+ * Execute a filter
+ */
+ public List listFilter(Object collection, String filter, QueryParameters queryParameters) throws HibernateException;
+ /**
+ * Iterate a filter
+ */
+ public Iterator iterateFilter(Object collection, String filter, QueryParameters queryParameters) throws HibernateException;
+
+ /**
+ * Get the <tt>EntityPersister</tt> for any instance
+ * @param entityName optional entity name
+ * @param object the entity instance
+ */
+ public EntityPersister getEntityPersister(String entityName, Object object) throws HibernateException;
+
+ /**
+ * Get the entity instance associated with the given <tt>Key</tt>,
+ * calling the Interceptor if necessary
+ */
+ public Object getEntityUsingInterceptor(EntityKey key) throws HibernateException;
+
+ /**
+ * Notify the session that the transaction completed, so we no longer
+ * own the old locks. (Also we should release cache softlocks.) May
+ * be called multiple times during the transaction completion process.
+ * Also called after an autocommit, in which case the second argument
+ * is null.
+ */
+ public void afterTransactionCompletion(boolean successful, Transaction tx);
+
+ /**
+ * Notify the session that the transaction is about to complete
+ */
+ public void beforeTransactionCompletion(Transaction tx);
+
+ /**
+ * Return the identifier of the persistent object, or null if
+ * not associated with the session
+ */
+ public Serializable getContextEntityIdentifier(Object object);
+
+ /**
+ * The best guess entity name for an entity not in an association
+ */
+ public String bestGuessEntityName(Object object);
+
+ /**
+ * The guessed entity name for an entity not in an association
+ */
+ public String guessEntityName(Object entity) throws HibernateException;
+
+ /**
+ * Instantiate the entity class, initializing with the given identifier
+ */
+ public Object instantiate(String entityName, Serializable id) throws HibernateException;
+
+ /**
+ * Execute an SQL Query
+ */
+ public List listCustomQuery(CustomQuery customQuery, QueryParameters queryParameters)
+ throws HibernateException;
+
+ /**
+ * Execute an SQL Query
+ */
+ public ScrollableResults scrollCustomQuery(CustomQuery customQuery, QueryParameters queryParameters)
+ throws HibernateException;
+
+ /**
+ * Execute a native SQL query, and return the results as a fully built list.
+ *
+ * @param spec The specification of the native SQL query to execute.
+ * @param queryParameters The parameters by which to perform the execution.
+ * @return The result list.
+ * @throws HibernateException
+ */
+ public List list(NativeSQLQuerySpecification spec, QueryParameters queryParameters)
+ throws HibernateException;
+
+ /**
+ * Execute a native SQL query, and return the results as a scrollable result.
+ *
+ * @param spec The specification of the native SQL query to execute.
+ * @param queryParameters The parameters by which to perform the execution.
+ * @return The resulting scrollable result.
+ * @throws HibernateException
+ */
+ public ScrollableResults scroll(NativeSQLQuerySpecification spec, QueryParameters queryParameters)
+ throws HibernateException;
+
+ /**
+ * Retreive the currently set value for a filter parameter.
+ *
+ * @param filterParameterName The filter parameter name in the format
+ * {FILTER_NAME.PARAMETER_NAME}.
+ * @return The filter parameter value.
+ */
+ public Object getFilterParameterValue(String filterParameterName);
+
+ /**
+ * Retreive the type for a given filter parrameter.
+ *
+ * @param filterParameterName The filter parameter name in the format
+ * {FILTER_NAME.PARAMETER_NAME}.
+ */
+ public Type getFilterParameterType(String filterParameterName);
+
+ /**
+ * Return the currently enabled filters. The filter map is keyed by filter
+ * name, with values corresponding to the {@link org.hibernate.impl.FilterImpl}
+ * instance.
+ * @return The currently enabled filters.
+ */
+ public Map getEnabledFilters();
+
+ public int getDontFlushFromFind();
+
+ /**
+ * Retrieves the configured event listeners from this event source.
+ *
+ * @return The configured event listeners.
+ */
+ public EventListeners getListeners();
+
+ //TODO: temporary
+
+ /**
+ * Get the persistence context for this session
+ */
+ public PersistenceContext getPersistenceContext();
+
+ /**
+ * Execute a HQL update or delete query
+ */
+ int executeUpdate(String query, QueryParameters queryParameters) throws HibernateException;
+
+ /**
+ * Execute a native SQL update or delete query
+ */
+ int executeNativeUpdate(NativeSQLQuerySpecification specification, QueryParameters queryParameters) throws HibernateException;
+
+ // copied from Session:
+
+ public EntityMode getEntityMode();
+ public CacheMode getCacheMode();
+ public void setCacheMode(CacheMode cm);
+ public boolean isOpen();
+ public boolean isConnected();
+ public FlushMode getFlushMode();
+ public void setFlushMode(FlushMode fm);
+ public Connection connection();
+ public void flush();
+
+ /**
+ * Get a Query instance for a named query or named native SQL query
+ */
+ public Query getNamedQuery(String name);
+ /**
+ * Get a Query instance for a named native SQL query
+ */
+ public Query getNamedSQLQuery(String name);
+
+ public boolean isEventSource();
+
+ public void afterScrollOperation();
+
+ public void setFetchProfile(String name);
+
+ public String getFetchProfile();
+
+ public JDBCContext getJDBCContext();
+
+ /**
+ * Determine whether the session is closed. Provided seperately from
+ * {@link #isOpen()} as this method does not attempt any JTA synch
+ * registration, where as {@link #isOpen()} does; which makes this one
+ * nicer to use for most internal purposes.
+ *
+ * @return True if the session is closed; false otherwise.
+ */
+ public boolean isClosed();
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/StatefulPersistenceContext.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/StatefulPersistenceContext.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/StatefulPersistenceContext.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1363 @@
+// $Id: StatefulPersistenceContext.java 11490 2007-05-09 01:43:11Z steve.ebersole at jboss.com $
+package org.hibernate.engine;
+
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.collections.ReferenceMap;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.AssertionFailure;
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.LockMode;
+import org.hibernate.MappingException;
+import org.hibernate.NonUniqueObjectException;
+import org.hibernate.PersistentObjectException;
+import org.hibernate.TransientObjectException;
+import org.hibernate.engine.loading.LoadContexts;
+import org.hibernate.pretty.MessageHelper;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.proxy.HibernateProxy;
+import org.hibernate.proxy.LazyInitializer;
+import org.hibernate.tuple.ElementWrapper;
+import org.hibernate.util.IdentityMap;
+import org.hibernate.util.MarkerObject;
+
+/**
+ * A <tt>PersistenceContext</tt> represents the state of persistent "stuff" which
+ * Hibernate is tracking. This includes persistent entities, collections,
+ * as well as proxies generated.
+ * </p>
+ * There is meant to be a one-to-one correspondence between a SessionImpl and
+ * a PersistentContext. The SessionImpl uses the PersistentContext to track
+ * the current state of its context. Event-listeners then use the
+ * PersistentContext to drive their processing.
+ *
+ * @author Steve Ebersole
+ */
+public class StatefulPersistenceContext implements PersistenceContext {
+
+ public static final Object NO_ROW = new MarkerObject( "NO_ROW" );
+
+ private static final Log log = LogFactory.getLog( StatefulPersistenceContext.class );
+ private static final Log PROXY_WARN_LOG = LogFactory.getLog( StatefulPersistenceContext.class.getName() + ".ProxyWarnLog" );
+ private static final int INIT_COLL_SIZE = 8;
+
+ private SessionImplementor session;
+
+ // Loaded entity instances, by EntityKey
+ private Map entitiesByKey;
+
+ // Loaded entity instances, by EntityUniqueKey
+ private Map entitiesByUniqueKey;
+
+ // Identity map of EntityEntry instances, by the entity instance
+ private Map entityEntries;
+
+ // Entity proxies, by EntityKey
+ private Map proxiesByKey;
+
+ // Snapshots of current database state for entities
+ // that have *not* been loaded
+ private Map entitySnapshotsByKey;
+
+ // Identity map of array holder ArrayHolder instances, by the array instance
+ private Map arrayHolders;
+
+ // Identity map of CollectionEntry instances, by the collection wrapper
+ private Map collectionEntries;
+
+ // Collection wrappers, by the CollectionKey
+ private Map collectionsByKey; //key=CollectionKey, value=PersistentCollection
+
+ // Set of EntityKeys of deleted objects
+ private HashSet nullifiableEntityKeys;
+
+ // properties that we have tried to load, and not found in the database
+ private HashSet nullAssociations;
+
+ // A list of collection wrappers that were instantiating during result set
+ // processing, that we will need to initialize at the end of the query
+ private List nonlazyCollections;
+
+ // A container for collections we load up when the owning entity is not
+ // yet loaded ... for now, this is purely transient!
+ private Map unownedCollections;
+
+ private int cascading = 0;
+ private int loadCounter = 0;
+ private boolean flushing = false;
+
+ private boolean hasNonReadOnlyEntities = false;
+
+ private LoadContexts loadContexts;
+ private BatchFetchQueue batchFetchQueue;
+
+
+
+ /**
+ * Constructs a PersistentContext, bound to the given session.
+ *
+ * @param session The session "owning" this context.
+ */
+ public StatefulPersistenceContext(SessionImplementor session) {
+ this.session = session;
+
+ entitiesByKey = new HashMap( INIT_COLL_SIZE );
+ entitiesByUniqueKey = new HashMap( INIT_COLL_SIZE );
+ proxiesByKey = new ReferenceMap( ReferenceMap.HARD, ReferenceMap.WEAK );
+ entitySnapshotsByKey = new HashMap( INIT_COLL_SIZE );
+
+ entityEntries = IdentityMap.instantiateSequenced( INIT_COLL_SIZE );
+ collectionEntries = IdentityMap.instantiateSequenced( INIT_COLL_SIZE );
+ collectionsByKey = new HashMap( INIT_COLL_SIZE );
+ arrayHolders = IdentityMap.instantiate( INIT_COLL_SIZE );
+
+ nullifiableEntityKeys = new HashSet();
+
+ initTransientState();
+ }
+
+ private void initTransientState() {
+ nullAssociations = new HashSet( INIT_COLL_SIZE );
+ nonlazyCollections = new ArrayList( INIT_COLL_SIZE );
+ }
+
+ public boolean isStateless() {
+ return false;
+ }
+
+ public SessionImplementor getSession() {
+ return session;
+ }
+
+ public LoadContexts getLoadContexts() {
+ if ( loadContexts == null ) {
+ loadContexts = new LoadContexts( this );
+ }
+ return loadContexts;
+ }
+
+ public void addUnownedCollection(CollectionKey key, PersistentCollection collection) {
+ if (unownedCollections==null) {
+ unownedCollections = new HashMap(8);
+ }
+ unownedCollections.put(key, collection);
+ }
+
+ public PersistentCollection useUnownedCollection(CollectionKey key) {
+ if (unownedCollections==null) {
+ return null;
+ }
+ else {
+ return (PersistentCollection) unownedCollections.remove(key);
+ }
+ }
+
+ /**
+ * Get the <tt>BatchFetchQueue</tt>, instantiating one if
+ * necessary.
+ */
+ public BatchFetchQueue getBatchFetchQueue() {
+ if (batchFetchQueue==null) {
+ batchFetchQueue = new BatchFetchQueue(this);
+ }
+ return batchFetchQueue;
+ }
+
+ public void clear() {
+ Iterator itr = proxiesByKey.values().iterator();
+ while ( itr.hasNext() ) {
+ final LazyInitializer li = ( ( HibernateProxy ) itr.next() ).getHibernateLazyInitializer();
+ li.setSession( null );
+ }
+ Map.Entry[] collectionEntryArray = IdentityMap.concurrentEntries( collectionEntries );
+ for ( int i = 0; i < collectionEntryArray.length; i++ ) {
+ ( ( PersistentCollection ) collectionEntryArray[i].getKey() ).unsetSession( getSession() );
+ }
+ arrayHolders.clear();
+ entitiesByKey.clear();
+ entitiesByUniqueKey.clear();
+ entityEntries.clear();
+ entitySnapshotsByKey.clear();
+ collectionsByKey.clear();
+ collectionEntries.clear();
+ if ( unownedCollections != null ) {
+ unownedCollections.clear();
+ }
+ proxiesByKey.clear();
+ nullifiableEntityKeys.clear();
+ if ( batchFetchQueue != null ) {
+ batchFetchQueue.clear();
+ }
+ hasNonReadOnlyEntities = false;
+ }
+
+ public boolean hasNonReadOnlyEntities() {
+ return hasNonReadOnlyEntities;
+ }
+
+ public void setEntryStatus(EntityEntry entry, Status status) {
+ entry.setStatus(status);
+ setHasNonReadOnlyEnties(status);
+ }
+
+ private void setHasNonReadOnlyEnties(Status status) {
+ if ( status==Status.DELETED || status==Status.MANAGED || status==Status.SAVING ) {
+ hasNonReadOnlyEntities = true;
+ }
+ }
+
+ public void afterTransactionCompletion() {
+ // Downgrade locks
+ Iterator iter = entityEntries.values().iterator();
+ while ( iter.hasNext() ) {
+ ( (EntityEntry) iter.next() ).setLockMode(LockMode.NONE);
+ }
+ }
+
+ /**
+ * Get the current state of the entity as known to the underlying
+ * database, or null if there is no corresponding row
+ */
+ public Object[] getDatabaseSnapshot(Serializable id, EntityPersister persister)
+ throws HibernateException {
+ EntityKey key = new EntityKey( id, persister, session.getEntityMode() );
+ Object cached = entitySnapshotsByKey.get(key);
+ if (cached!=null) {
+ return cached==NO_ROW ? null : (Object[]) cached;
+ }
+ else {
+ Object[] snapshot = persister.getDatabaseSnapshot( id, session );
+ entitySnapshotsByKey.put( key, snapshot==null ? NO_ROW : snapshot );
+ return snapshot;
+ }
+ }
+
+ public Object[] getNaturalIdSnapshot(Serializable id, EntityPersister persister)
+ throws HibernateException {
+ if ( !persister.hasNaturalIdentifier() ) {
+ return null;
+ }
+
+ // if the natural-id is marked as non-mutable, it is not retrieved during a
+ // normal database-snapshot operation...
+ int[] props = persister.getNaturalIdentifierProperties();
+ boolean[] updateable = persister.getPropertyUpdateability();
+ boolean allNatualIdPropsAreUpdateable = true;
+ for ( int i = 0; i < props.length; i++ ) {
+ if ( !updateable[ props[i] ] ) {
+ allNatualIdPropsAreUpdateable = false;
+ break;
+ }
+ }
+
+ if ( allNatualIdPropsAreUpdateable ) {
+ // do this when all the properties are updateable since there is
+ // a certain likelihood that the information will already be
+ // snapshot-cached.
+ Object[] entitySnapshot = getDatabaseSnapshot( id, persister );
+ if ( entitySnapshot == NO_ROW ) {
+ return null;
+ }
+ Object[] naturalIdSnapshot = new Object[ props.length ];
+ for ( int i = 0; i < props.length; i++ ) {
+ naturalIdSnapshot[i] = entitySnapshot[ props[i] ];
+ }
+ return naturalIdSnapshot;
+ }
+ else {
+ return persister.getNaturalIdentifierSnapshot( id, session );
+ }
+ }
+
+ /**
+ * Retrieve the cached database snapshot for the requested entity key.
+ * <p/>
+ * This differs from {@link #getDatabaseSnapshot} is two important respects:<ol>
+ * <li>no snapshot is obtained from the database if not already cached</li>
+ * <li>an entry of {@link #NO_ROW} here is interpretet as an exception</li>
+ * </ol>
+ * @param key The entity key for which to retrieve the cached snapshot
+ * @return The cached snapshot
+ * @throws IllegalStateException if the cached snapshot was == {@link #NO_ROW}.
+ */
+ public Object[] getCachedDatabaseSnapshot(EntityKey key) {
+ Object snapshot = entitySnapshotsByKey.get( key );
+ if ( snapshot == NO_ROW ) {
+ throw new IllegalStateException( "persistence context reported no row snapshot for " + MessageHelper.infoString( key.getEntityName(), key.getIdentifier() ) );
+ }
+ return ( Object[] ) snapshot;
+ }
+
+ /*public void removeDatabaseSnapshot(EntityKey key) {
+ entitySnapshotsByKey.remove(key);
+ }*/
+
+ public void addEntity(EntityKey key, Object entity) {
+ entitiesByKey.put(key, entity);
+ getBatchFetchQueue().removeBatchLoadableEntityKey(key);
+ }
+
+ /**
+ * Get the entity instance associated with the given
+ * <tt>EntityKey</tt>
+ */
+ public Object getEntity(EntityKey key) {
+ return entitiesByKey.get(key);
+ }
+
+ public boolean containsEntity(EntityKey key) {
+ return entitiesByKey.containsKey(key);
+ }
+
+ /**
+ * Remove an entity from the session cache, also clear
+ * up other state associated with the entity, all except
+ * for the <tt>EntityEntry</tt>
+ */
+ public Object removeEntity(EntityKey key) {
+ Object entity = entitiesByKey.remove(key);
+ Iterator iter = entitiesByUniqueKey.values().iterator();
+ while ( iter.hasNext() ) {
+ if ( iter.next()==entity ) iter.remove();
+ }
+ entitySnapshotsByKey.remove(key);
+ nullifiableEntityKeys.remove(key);
+ getBatchFetchQueue().removeBatchLoadableEntityKey(key);
+ getBatchFetchQueue().removeSubselect(key);
+ return entity;
+ }
+
+ /**
+ * Get an entity cached by unique key
+ */
+ public Object getEntity(EntityUniqueKey euk) {
+ return entitiesByUniqueKey.get(euk);
+ }
+
+ /**
+ * Add an entity to the cache by unique key
+ */
+ public void addEntity(EntityUniqueKey euk, Object entity) {
+ entitiesByUniqueKey.put(euk, entity);
+ }
+
+ /**
+ * Retreive the EntityEntry representation of the given entity.
+ *
+ * @param entity The entity for which to locate the EntityEntry.
+ * @return The EntityEntry for the given entity.
+ */
+ public EntityEntry getEntry(Object entity) {
+ return (EntityEntry) entityEntries.get(entity);
+ }
+
+ /**
+ * Remove an entity entry from the session cache
+ */
+ public EntityEntry removeEntry(Object entity) {
+ return (EntityEntry) entityEntries.remove(entity);
+ }
+
+ /**
+ * Is there an EntityEntry for this instance?
+ */
+ public boolean isEntryFor(Object entity) {
+ return entityEntries.containsKey(entity);
+ }
+
+ /**
+ * Get the collection entry for a persistent collection
+ */
+ public CollectionEntry getCollectionEntry(PersistentCollection coll) {
+ return (CollectionEntry) collectionEntries.get(coll);
+ }
+
+ /**
+ * Adds an entity to the internal caches.
+ */
+ public EntityEntry addEntity(
+ final Object entity,
+ final Status status,
+ final Object[] loadedState,
+ final EntityKey entityKey,
+ final Object version,
+ final LockMode lockMode,
+ final boolean existsInDatabase,
+ final EntityPersister persister,
+ final boolean disableVersionIncrement,
+ boolean lazyPropertiesAreUnfetched
+ ) {
+
+ addEntity( entityKey, entity );
+
+ return addEntry(
+ entity,
+ status,
+ loadedState,
+ null,
+ entityKey.getIdentifier(),
+ version,
+ lockMode,
+ existsInDatabase,
+ persister,
+ disableVersionIncrement,
+ lazyPropertiesAreUnfetched
+ );
+ }
+
+
+ /**
+ * Generates an appropriate EntityEntry instance and adds it
+ * to the event source's internal caches.
+ */
+ public EntityEntry addEntry(
+ final Object entity,
+ final Status status,
+ final Object[] loadedState,
+ final Object rowId,
+ final Serializable id,
+ final Object version,
+ final LockMode lockMode,
+ final boolean existsInDatabase,
+ final EntityPersister persister,
+ final boolean disableVersionIncrement,
+ boolean lazyPropertiesAreUnfetched) {
+
+ EntityEntry e = new EntityEntry(
+ status,
+ loadedState,
+ rowId,
+ id,
+ version,
+ lockMode,
+ existsInDatabase,
+ persister,
+ session.getEntityMode(),
+ disableVersionIncrement,
+ lazyPropertiesAreUnfetched
+ );
+ entityEntries.put(entity, e);
+
+ setHasNonReadOnlyEnties(status);
+ return e;
+ }
+
+ public boolean containsCollection(PersistentCollection collection) {
+ return collectionEntries.containsKey(collection);
+ }
+
+ public boolean containsProxy(Object entity) {
+ return proxiesByKey.containsValue( entity );
+ }
+
+ /**
+ * Takes the given object and, if it represents a proxy, reassociates it with this event source.
+ *
+ * @param value The possible proxy to be reassociated.
+ * @return Whether the passed value represented an actual proxy which got initialized.
+ * @throws MappingException
+ */
+ public boolean reassociateIfUninitializedProxy(Object value) throws MappingException {
+ if ( value instanceof ElementWrapper ) {
+ value = ( (ElementWrapper) value ).getElement();
+ }
+
+ if ( !Hibernate.isInitialized(value) ) {
+ HibernateProxy proxy = (HibernateProxy) value;
+ LazyInitializer li = proxy.getHibernateLazyInitializer();
+ reassociateProxy(li, proxy);
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ /**
+ * If a deleted entity instance is re-saved, and it has a proxy, we need to
+ * reset the identifier of the proxy
+ */
+ public void reassociateProxy(Object value, Serializable id) throws MappingException {
+ if ( value instanceof ElementWrapper ) {
+ value = ( (ElementWrapper) value ).getElement();
+ }
+
+ if ( value instanceof HibernateProxy ) {
+ if ( log.isDebugEnabled() ) log.debug("setting proxy identifier: " + id);
+ HibernateProxy proxy = (HibernateProxy) value;
+ LazyInitializer li = proxy.getHibernateLazyInitializer();
+ li.setIdentifier(id);
+ reassociateProxy(li, proxy);
+ }
+ }
+
+ /**
+ * Associate a proxy that was instantiated by another session with this session
+ *
+ * @param li The proxy initializer.
+ * @param proxy The proxy to reassociate.
+ */
+ private void reassociateProxy(LazyInitializer li, HibernateProxy proxy) {
+ if ( li.getSession() != this.getSession() ) {
+ EntityPersister persister = session.getFactory().getEntityPersister( li.getEntityName() );
+ EntityKey key = new EntityKey( li.getIdentifier(), persister, session.getEntityMode() );
+ // any earlier proxy takes precedence
+ if ( !proxiesByKey.containsKey( key ) ) {
+ proxiesByKey.put( key, proxy );
+ }
+ proxy.getHibernateLazyInitializer().setSession( session );
+ }
+ }
+
+ /**
+ * Get the entity instance underlying the given proxy, throwing
+ * an exception if the proxy is uninitialized. If the given object
+ * is not a proxy, simply return the argument.
+ */
+ public Object unproxy(Object maybeProxy) throws HibernateException {
+ if ( maybeProxy instanceof ElementWrapper ) {
+ maybeProxy = ( (ElementWrapper) maybeProxy ).getElement();
+ }
+
+ if ( maybeProxy instanceof HibernateProxy ) {
+ HibernateProxy proxy = (HibernateProxy) maybeProxy;
+ LazyInitializer li = proxy.getHibernateLazyInitializer();
+ if ( li.isUninitialized() ) {
+ throw new PersistentObjectException(
+ "object was an uninitialized proxy for " +
+ li.getEntityName()
+ );
+ }
+ return li.getImplementation(); //unwrap the object
+ }
+ else {
+ return maybeProxy;
+ }
+ }
+
+ /**
+ * Possibly unproxy the given reference and reassociate it with the current session.
+ *
+ * @param maybeProxy The reference to be unproxied if it currently represents a proxy.
+ * @return The unproxied instance.
+ * @throws HibernateException
+ */
+ public Object unproxyAndReassociate(Object maybeProxy) throws HibernateException {
+ if ( maybeProxy instanceof ElementWrapper ) {
+ maybeProxy = ( (ElementWrapper) maybeProxy ).getElement();
+ }
+
+ if ( maybeProxy instanceof HibernateProxy ) {
+ HibernateProxy proxy = (HibernateProxy) maybeProxy;
+ LazyInitializer li = proxy.getHibernateLazyInitializer();
+ reassociateProxy(li, proxy);
+ return li.getImplementation(); //initialize + unwrap the object
+ }
+ else {
+ return maybeProxy;
+ }
+ }
+
+ /**
+ * Attempts to check whether the given key represents an entity already loaded within the
+ * current session.
+ * @param object The entity reference against which to perform the uniqueness check.
+ * @throws HibernateException
+ */
+ public void checkUniqueness(EntityKey key, Object object) throws HibernateException {
+ Object entity = getEntity(key);
+ if ( entity == object ) {
+ throw new AssertionFailure( "object already associated, but no entry was found" );
+ }
+ if ( entity != null ) {
+ throw new NonUniqueObjectException( key.getIdentifier(), key.getEntityName() );
+ }
+ }
+
+ /**
+ * If the existing proxy is insufficiently "narrow" (derived), instantiate a new proxy
+ * and overwrite the registration of the old one. This breaks == and occurs only for
+ * "class" proxies rather than "interface" proxies. Also init the proxy to point to
+ * the given target implementation if necessary.
+ *
+ * @param proxy The proxy instance to be narrowed.
+ * @param persister The persister for the proxied entity.
+ * @param key The internal cache key for the proxied entity.
+ * @param object (optional) the actual proxied entity instance.
+ * @return An appropriately narrowed instance.
+ * @throws HibernateException
+ */
+ public Object narrowProxy(Object proxy, EntityPersister persister, EntityKey key, Object object)
+ throws HibernateException {
+
+ boolean alreadyNarrow = persister.getConcreteProxyClass( session.getEntityMode() )
+ .isAssignableFrom( proxy.getClass() );
+
+ if ( !alreadyNarrow ) {
+ if ( PROXY_WARN_LOG.isWarnEnabled() ) {
+ PROXY_WARN_LOG.warn(
+ "Narrowing proxy to " +
+ persister.getConcreteProxyClass( session.getEntityMode() ) +
+ " - this operation breaks =="
+ );
+ }
+
+ if ( object != null ) {
+ proxiesByKey.remove(key);
+ return object; //return the proxied object
+ }
+ else {
+ proxy = persister.createProxy( key.getIdentifier(), session );
+ proxiesByKey.put(key, proxy); //overwrite old proxy
+ return proxy;
+ }
+
+ }
+ else {
+
+ if ( object != null ) {
+ LazyInitializer li = ( (HibernateProxy) proxy ).getHibernateLazyInitializer();
+ li.setImplementation(object);
+ }
+
+ return proxy;
+
+ }
+
+ }
+
+ /**
+ * Return the existing proxy associated with the given <tt>EntityKey</tt>, or the
+ * third argument (the entity associated with the key) if no proxy exists. Init
+ * the proxy to the target implementation, if necessary.
+ */
+ public Object proxyFor(EntityPersister persister, EntityKey key, Object impl)
+ throws HibernateException {
+ if ( !persister.hasProxy() ) return impl;
+ Object proxy = proxiesByKey.get(key);
+ if ( proxy != null ) {
+ return narrowProxy(proxy, persister, key, impl);
+ }
+ else {
+ return impl;
+ }
+ }
+
+ /**
+ * Return the existing proxy associated with the given <tt>EntityKey</tt>, or the
+ * argument (the entity associated with the key) if no proxy exists.
+ * (slower than the form above)
+ */
+ public Object proxyFor(Object impl) throws HibernateException {
+ EntityEntry e = getEntry(impl);
+ EntityPersister p = e.getPersister();
+ return proxyFor( p, new EntityKey( e.getId(), p, session.getEntityMode() ), impl );
+ }
+
+ /**
+ * Get the entity that owns this persistent collection
+ */
+ public Object getCollectionOwner(Serializable key, CollectionPersister collectionPersister) throws MappingException {
+ return getEntity( new EntityKey( key, collectionPersister.getOwnerEntityPersister(), session.getEntityMode() ) );
+ }
+
+ /**
+ * add a collection we just loaded up (still needs initializing)
+ */
+ public void addUninitializedCollection(CollectionPersister persister, PersistentCollection collection, Serializable id) {
+ CollectionEntry ce = new CollectionEntry(collection, persister, id, flushing);
+ addCollection(collection, ce, id);
+ }
+
+ /**
+ * add a detached uninitialized collection
+ */
+ public void addUninitializedDetachedCollection(CollectionPersister persister, PersistentCollection collection) {
+ CollectionEntry ce = new CollectionEntry( persister, collection.getKey() );
+ addCollection( collection, ce, collection.getKey() );
+ }
+
+ /**
+ * Add a new collection (ie. a newly created one, just instantiated by the
+ * application, with no database state or snapshot)
+ * @param collection The collection to be associated with the persistence context
+ */
+ public void addNewCollection(CollectionPersister persister, PersistentCollection collection)
+ throws HibernateException {
+ addCollection(collection, persister);
+ }
+
+ /**
+ * Add an collection to the cache, with a given collection entry.
+ *
+ * @param coll The collection for which we are adding an entry.
+ * @param entry The entry representing the collection.
+ * @param key The key of the collection's entry.
+ */
+ private void addCollection(PersistentCollection coll, CollectionEntry entry, Serializable key) {
+ collectionEntries.put( coll, entry );
+ CollectionKey collectionKey = new CollectionKey( entry.getLoadedPersister(), key, session.getEntityMode() );
+ PersistentCollection old = ( PersistentCollection ) collectionsByKey.put( collectionKey, coll );
+ if ( old != null ) {
+ if ( old == coll ) {
+ throw new AssertionFailure("bug adding collection twice");
+ }
+ // or should it actually throw an exception?
+ old.unsetSession( session );
+ collectionEntries.remove( old );
+ // watch out for a case where old is still referenced
+ // somewhere in the object graph! (which is a user error)
+ }
+ }
+
+ /**
+ * Add a collection to the cache, creating a new collection entry for it
+ *
+ * @param collection The collection for which we are adding an entry.
+ * @param persister The collection persister
+ */
+ private void addCollection(PersistentCollection collection, CollectionPersister persister) {
+ CollectionEntry ce = new CollectionEntry( persister, collection );
+ collectionEntries.put( collection, ce );
+ }
+
+ /**
+ * add an (initialized) collection that was created by another session and passed
+ * into update() (ie. one with a snapshot and existing state on the database)
+ */
+ public void addInitializedDetachedCollection(CollectionPersister collectionPersister, PersistentCollection collection)
+ throws HibernateException {
+ if ( collection.isUnreferenced() ) {
+ //treat it just like a new collection
+ addCollection( collection, collectionPersister );
+ }
+ else {
+ CollectionEntry ce = new CollectionEntry( collection, session.getFactory() );
+ addCollection( collection, ce, collection.getKey() );
+ }
+ }
+
+ /**
+ * add a collection we just pulled out of the cache (does not need initializing)
+ */
+ public CollectionEntry addInitializedCollection(CollectionPersister persister, PersistentCollection collection, Serializable id)
+ throws HibernateException {
+ CollectionEntry ce = new CollectionEntry(collection, persister, id, flushing);
+ ce.postInitialize(collection);
+ addCollection(collection, ce, id);
+ return ce;
+ }
+
+ /**
+ * Get the collection instance associated with the <tt>CollectionKey</tt>
+ */
+ public PersistentCollection getCollection(CollectionKey collectionKey) {
+ return (PersistentCollection) collectionsByKey.get(collectionKey);
+ }
+
+ /**
+ * Register a collection for non-lazy loading at the end of the
+ * two-phase load
+ */
+ public void addNonLazyCollection(PersistentCollection collection) {
+ nonlazyCollections.add(collection);
+ }
+
+ /**
+ * Force initialization of all non-lazy collections encountered during
+ * the current two-phase load (actually, this is a no-op, unless this
+ * is the "outermost" load)
+ */
+ public void initializeNonLazyCollections() throws HibernateException {
+ if ( loadCounter == 0 ) {
+ log.debug( "initializing non-lazy collections" );
+ //do this work only at the very highest level of the load
+ loadCounter++; //don't let this method be called recursively
+ try {
+ int size;
+ while ( ( size = nonlazyCollections.size() ) > 0 ) {
+ //note that each iteration of the loop may add new elements
+ ( (PersistentCollection) nonlazyCollections.remove( size - 1 ) ).forceInitialization();
+ }
+ }
+ finally {
+ loadCounter--;
+ clearNullProperties();
+ }
+ }
+ }
+
+
+ /**
+ * Get the <tt>PersistentCollection</tt> object for an array
+ */
+ public PersistentCollection getCollectionHolder(Object array) {
+ return (PersistentCollection) arrayHolders.get(array);
+ }
+
+ /**
+ * Register a <tt>PersistentCollection</tt> object for an array.
+ * Associates a holder with an array - MUST be called after loading
+ * array, since the array instance is not created until endLoad().
+ */
+ public void addCollectionHolder(PersistentCollection holder) {
+ //TODO:refactor + make this method private
+ arrayHolders.put( holder.getValue(), holder );
+ }
+
+ public PersistentCollection removeCollectionHolder(Object array) {
+ return (PersistentCollection) arrayHolders.remove(array);
+ }
+
+ /**
+ * Get the snapshot of the pre-flush collection state
+ */
+ public Serializable getSnapshot(PersistentCollection coll) {
+ return getCollectionEntry(coll).getSnapshot();
+ }
+
+ /**
+ * Get the collection entry for a collection passed to filter,
+ * which might be a collection wrapper, an array, or an unwrapped
+ * collection. Return null if there is no entry.
+ */
+ public CollectionEntry getCollectionEntryOrNull(Object collection) {
+ PersistentCollection coll;
+ if ( collection instanceof PersistentCollection ) {
+ coll = (PersistentCollection) collection;
+ //if (collection==null) throw new TransientObjectException("Collection was not yet persistent");
+ }
+ else {
+ coll = getCollectionHolder(collection);
+ if ( coll == null ) {
+ //it might be an unwrapped collection reference!
+ //try to find a wrapper (slowish)
+ Iterator wrappers = IdentityMap.keyIterator(collectionEntries);
+ while ( wrappers.hasNext() ) {
+ PersistentCollection pc = (PersistentCollection) wrappers.next();
+ if ( pc.isWrapper(collection) ) {
+ coll = pc;
+ break;
+ }
+ }
+ }
+ }
+
+ return (coll == null) ? null : getCollectionEntry(coll);
+ }
+
+ /**
+ * Get an existing proxy by key
+ */
+ public Object getProxy(EntityKey key) {
+ return proxiesByKey.get(key);
+ }
+
+ /**
+ * Add a proxy to the session cache
+ */
+ public void addProxy(EntityKey key, Object proxy) {
+ proxiesByKey.put(key, proxy);
+ }
+
+ /**
+ * Remove a proxy from the session cache.
+ * <p/>
+ * Additionally, ensure that any load optimization references
+ * such as batch or subselect loading get cleaned up as well.
+ *
+ * @param key The key of the entity proxy to be removed
+ * @return The proxy reference.
+ */
+ public Object removeProxy(EntityKey key) {
+ if ( batchFetchQueue != null ) {
+ batchFetchQueue.removeBatchLoadableEntityKey( key );
+ batchFetchQueue.removeSubselect( key );
+ }
+ return proxiesByKey.remove( key );
+ }
+
+ /**
+ * Record the fact that an entity does not exist in the database
+ *
+ * @param key the primary key of the entity
+ */
+ /*public void addNonExistantEntityKey(EntityKey key) {
+ nonExistantEntityKeys.add(key);
+ }*/
+
+ /**
+ * Record the fact that an entity does not exist in the database
+ *
+ * @param key a unique key of the entity
+ */
+ /*public void addNonExistantEntityUniqueKey(EntityUniqueKey key) {
+ nonExistentEntityUniqueKeys.add(key);
+ }*/
+
+ /*public void removeNonExist(EntityKey key) {
+ nonExistantEntityKeys.remove(key);
+ }*/
+
+ /**
+ * Retrieve the set of EntityKeys representing nullifiable references
+ */
+ public HashSet getNullifiableEntityKeys() {
+ return nullifiableEntityKeys;
+ }
+
+ public Map getEntitiesByKey() {
+ return entitiesByKey;
+ }
+
+ public Map getEntityEntries() {
+ return entityEntries;
+ }
+
+ public Map getCollectionEntries() {
+ return collectionEntries;
+ }
+
+ public Map getCollectionsByKey() {
+ return collectionsByKey;
+ }
+
+ /**
+ * Do we already know that the entity does not exist in the
+ * database?
+ */
+ /*public boolean isNonExistant(EntityKey key) {
+ return nonExistantEntityKeys.contains(key);
+ }*/
+
+ /**
+ * Do we already know that the entity does not exist in the
+ * database?
+ */
+ /*public boolean isNonExistant(EntityUniqueKey key) {
+ return nonExistentEntityUniqueKeys.contains(key);
+ }*/
+
+ public int getCascadeLevel() {
+ return cascading;
+ }
+
+ public int incrementCascadeLevel() {
+ return ++cascading;
+ }
+
+ public int decrementCascadeLevel() {
+ return --cascading;
+ }
+
+ public boolean isFlushing() {
+ return flushing;
+ }
+
+ public void setFlushing(boolean flushing) {
+ this.flushing = flushing;
+ }
+
+ /**
+ * Call this before begining a two-phase load
+ */
+ public void beforeLoad() {
+ loadCounter++;
+ }
+
+ /**
+ * Call this after finishing a two-phase load
+ */
+ public void afterLoad() {
+ loadCounter--;
+ }
+
+ /**
+ * Returns a string representation of the object.
+ *
+ * @return a string representation of the object.
+ */
+ public String toString() {
+ return new StringBuffer()
+ .append("PersistenceContext[entityKeys=")
+ .append(entitiesByKey.keySet())
+ .append(",collectionKeys=")
+ .append(collectionsByKey.keySet())
+ .append("]")
+ .toString();
+ }
+
+ /**
+ * Search the persistence context for an owner for the child object,
+ * given a collection role. If <tt>mergeMap</tt> is non-null, also
+ * check the detached graph being merged for a parent.
+ */
+ public Serializable getOwnerId(String entity, String property, Object childEntity, Map mergeMap) {
+
+ EntityPersister persister = session.getFactory()
+ .getEntityPersister(entity);
+ final CollectionPersister collectionPersister = session.getFactory()
+ .getCollectionPersister(entity + '.' + property);
+
+ Iterator entities = entityEntries.entrySet().iterator();
+ while ( entities.hasNext() ) {
+ Map.Entry me = (Map.Entry) entities.next();
+ EntityEntry ee = (EntityEntry) me.getValue();
+ if ( persister.isSubclassEntityName( ee.getEntityName() ) ) {
+ Object instance = me.getKey();
+
+ //check if the managed object is the parent
+ boolean found = isFoundInParent(
+ property,
+ childEntity,
+ persister,
+ collectionPersister,
+ instance
+ );
+
+ if (!found && mergeMap!=null) {
+ //check if the detached object being merged is the parent
+ Object unmergedInstance = mergeMap.get(instance);
+ Object unmergedChild = mergeMap.get(childEntity);
+ if ( unmergedInstance!=null && unmergedChild!=null ) {
+ found = isFoundInParent(
+ property,
+ unmergedChild,
+ persister,
+ collectionPersister,
+ unmergedInstance
+ );
+ }
+ }
+
+ if ( found ) {
+ return ee.getId();
+ }
+
+ }
+ }
+ return null;
+ }
+
+ private boolean isFoundInParent(
+ String property,
+ Object childEntity,
+ EntityPersister persister,
+ CollectionPersister collectionPersister,
+ Object potentialParent
+ ) {
+ Object collection = persister.getPropertyValue(
+ potentialParent,
+ property,
+ session.getEntityMode()
+ );
+ return collection!=null && Hibernate.isInitialized(collection) &&
+ collectionPersister.getCollectionType()
+ .contains(collection, childEntity, session);
+ }
+
+ /**
+ * Search the persistence context for an index of the child object,
+ * given a collection role
+ */
+ public Object getIndexInOwner(String entity, String property, Object childEntity, Map mergeMap) {
+
+ EntityPersister persister = session.getFactory()
+ .getEntityPersister(entity);
+ CollectionPersister cp = session.getFactory()
+ .getCollectionPersister(entity + '.' + property);
+ Iterator entities = entityEntries.entrySet().iterator();
+ while ( entities.hasNext() ) {
+ Map.Entry me = (Map.Entry) entities.next();
+ EntityEntry ee = (EntityEntry) me.getValue();
+ if ( persister.isSubclassEntityName( ee.getEntityName() ) ) {
+ Object instance = me.getKey();
+
+ Object index = getIndexInParent(property, childEntity, persister, cp, instance);
+
+ if (index==null && mergeMap!=null) {
+ Object unmergedInstance = mergeMap.get(instance);
+ Object unmergedChild = mergeMap.get(childEntity);
+ if ( unmergedInstance!=null && unmergedChild!=null ) {
+ index = getIndexInParent(property, unmergedChild, persister, cp, unmergedInstance);
+ }
+ }
+
+ if (index!=null) return index;
+ }
+ }
+ return null;
+ }
+
+ private Object getIndexInParent(
+ String property,
+ Object childEntity,
+ EntityPersister persister,
+ CollectionPersister collectionPersister,
+ Object potentialParent
+ ){
+ Object collection = persister.getPropertyValue( potentialParent, property, session.getEntityMode() );
+ if ( collection!=null && Hibernate.isInitialized(collection) ) {
+ return collectionPersister.getCollectionType().indexOf(collection, childEntity);
+ }
+ else {
+ return null;
+ }
+ }
+
+ /**
+ * Record the fact that the association belonging to the keyed
+ * entity is null.
+ */
+ public void addNullProperty(EntityKey ownerKey, String propertyName) {
+ nullAssociations.add( new AssociationKey(ownerKey, propertyName) );
+ }
+
+ /**
+ * Is the association property belonging to the keyed entity null?
+ */
+ public boolean isPropertyNull(EntityKey ownerKey, String propertyName) {
+ return nullAssociations.contains( new AssociationKey(ownerKey, propertyName) );
+ }
+
+ private void clearNullProperties() {
+ nullAssociations.clear();
+ }
+
+ public void setReadOnly(Object entity, boolean readOnly) {
+ EntityEntry entry = getEntry(entity);
+ if (entry==null) {
+ throw new TransientObjectException("Instance was not associated with the session");
+ }
+ entry.setReadOnly(readOnly, entity);
+ hasNonReadOnlyEntities = hasNonReadOnlyEntities || !readOnly;
+ }
+
+ public void replaceDelayedEntityIdentityInsertKeys(EntityKey oldKey, Serializable generatedId) {
+ Object entity = entitiesByKey.remove( oldKey );
+ EntityEntry oldEntry = ( EntityEntry ) entityEntries.remove( entity );
+
+ EntityKey newKey = new EntityKey( generatedId, oldEntry.getPersister(), getSession().getEntityMode() );
+ addEntity( newKey, entity );
+ addEntry(
+ entity,
+ oldEntry.getStatus(),
+ oldEntry.getLoadedState(),
+ oldEntry.getRowId(),
+ generatedId,
+ oldEntry.getVersion(),
+ oldEntry.getLockMode(),
+ oldEntry.isExistsInDatabase(),
+ oldEntry.getPersister(),
+ oldEntry.isBeingReplicated(),
+ oldEntry.isLoadedWithLazyPropertiesUnfetched()
+ );
+ }
+
+ /**
+ * Used by the owning session to explicitly control serialization of the
+ * persistence context.
+ *
+ * @param oos The stream to which the persistence context should get written
+ * @throws IOException serialization errors.
+ */
+ public void serialize(ObjectOutputStream oos) throws IOException {
+ log.trace( "serializing persistent-context" );
+
+ oos.writeBoolean( hasNonReadOnlyEntities );
+
+ oos.writeInt( entitiesByKey.size() );
+ log.trace( "starting serialization of [" + entitiesByKey.size() + "] entitiesByKey entries" );
+ Iterator itr = entitiesByKey.entrySet().iterator();
+ while ( itr.hasNext() ) {
+ Map.Entry entry = ( Map.Entry ) itr.next();
+ ( ( EntityKey ) entry.getKey() ).serialize( oos );
+ oos.writeObject( entry.getValue() );
+ }
+
+ oos.writeInt( entitiesByUniqueKey.size() );
+ log.trace( "starting serialization of [" + entitiesByUniqueKey.size() + "] entitiesByUniqueKey entries" );
+ itr = entitiesByUniqueKey.entrySet().iterator();
+ while ( itr.hasNext() ) {
+ Map.Entry entry = ( Map.Entry ) itr.next();
+ ( ( EntityUniqueKey ) entry.getKey() ).serialize( oos );
+ oos.writeObject( entry.getValue() );
+ }
+
+ oos.writeInt( proxiesByKey.size() );
+ log.trace( "starting serialization of [" + proxiesByKey.size() + "] proxiesByKey entries" );
+ itr = proxiesByKey.entrySet().iterator();
+ while ( itr.hasNext() ) {
+ Map.Entry entry = ( Map.Entry ) itr.next();
+ ( ( EntityKey ) entry.getKey() ).serialize( oos );
+ oos.writeObject( entry.getValue() );
+ }
+
+ oos.writeInt( entitySnapshotsByKey.size() );
+ log.trace( "starting serialization of [" + entitySnapshotsByKey.size() + "] entitySnapshotsByKey entries" );
+ itr = entitySnapshotsByKey.entrySet().iterator();
+ while ( itr.hasNext() ) {
+ Map.Entry entry = ( Map.Entry ) itr.next();
+ ( ( EntityKey ) entry.getKey() ).serialize( oos );
+ oos.writeObject( entry.getValue() );
+ }
+
+ oos.writeInt( entityEntries.size() );
+ log.trace( "starting serialization of [" + entityEntries.size() + "] entityEntries entries" );
+ itr = entityEntries.entrySet().iterator();
+ while ( itr.hasNext() ) {
+ Map.Entry entry = ( Map.Entry ) itr.next();
+ oos.writeObject( entry.getKey() );
+ ( ( EntityEntry ) entry.getValue() ).serialize( oos );
+ }
+
+ oos.writeInt( collectionsByKey.size() );
+ log.trace( "starting serialization of [" + collectionsByKey.size() + "] collectionsByKey entries" );
+ itr = collectionsByKey.entrySet().iterator();
+ while ( itr.hasNext() ) {
+ Map.Entry entry = ( Map.Entry ) itr.next();
+ ( ( CollectionKey ) entry.getKey() ).serialize( oos );
+ oos.writeObject( entry.getValue() );
+ }
+
+ oos.writeInt( collectionEntries.size() );
+ log.trace( "starting serialization of [" + collectionEntries.size() + "] collectionEntries entries" );
+ itr = collectionEntries.entrySet().iterator();
+ while ( itr.hasNext() ) {
+ Map.Entry entry = ( Map.Entry ) itr.next();
+ oos.writeObject( entry.getKey() );
+ ( ( CollectionEntry ) entry.getValue() ).serialize( oos );
+ }
+
+ oos.writeInt( arrayHolders.size() );
+ log.trace( "starting serialization of [" + arrayHolders.size() + "] arrayHolders entries" );
+ itr = arrayHolders.entrySet().iterator();
+ while ( itr.hasNext() ) {
+ Map.Entry entry = ( Map.Entry ) itr.next();
+ oos.writeObject( entry.getKey() );
+ oos.writeObject( entry.getValue() );
+ }
+
+ oos.writeInt( nullifiableEntityKeys.size() );
+ log.trace( "starting serialization of [" + nullifiableEntityKeys.size() + "] nullifiableEntityKeys entries" );
+ itr = nullifiableEntityKeys.iterator();
+ while ( itr.hasNext() ) {
+ EntityKey entry = ( EntityKey ) itr.next();
+ entry.serialize( oos );
+ }
+ }
+
+ public static StatefulPersistenceContext deserialize(
+ ObjectInputStream ois,
+ SessionImplementor session) throws IOException, ClassNotFoundException {
+ log.trace( "deserializing persistent-context" );
+ StatefulPersistenceContext rtn = new StatefulPersistenceContext( session );
+
+ // during deserialization, we need to reconnect all proxies and
+ // collections to this session, as well as the EntityEntry and
+ // CollectionEntry instances; these associations are transient
+ // because serialization is used for different things.
+
+ try {
+ // todo : we can actually just determine this from the incoming EntityEntry-s
+ rtn.hasNonReadOnlyEntities = ois.readBoolean();
+
+ int count = ois.readInt();
+ log.trace( "staring deserialization of [" + count + "] entitiesByKey entries" );
+ rtn.entitiesByKey = new HashMap( count < INIT_COLL_SIZE ? INIT_COLL_SIZE : count );
+ for ( int i = 0; i < count; i++ ) {
+ rtn.entitiesByKey.put( EntityKey.deserialize( ois, session ), ois.readObject() );
+ }
+
+ count = ois.readInt();
+ log.trace( "staring deserialization of [" + count + "] entitiesByUniqueKey entries" );
+ rtn.entitiesByUniqueKey = new HashMap( count < INIT_COLL_SIZE ? INIT_COLL_SIZE : count );
+ for ( int i = 0; i < count; i++ ) {
+ rtn.entitiesByUniqueKey.put( EntityUniqueKey.deserialize( ois, session ), ois.readObject() );
+ }
+
+ count = ois.readInt();
+ log.trace( "staring deserialization of [" + count + "] proxiesByKey entries" );
+ rtn.proxiesByKey = new ReferenceMap( ReferenceMap.HARD, ReferenceMap.WEAK, count < INIT_COLL_SIZE ? INIT_COLL_SIZE : count, .75f );
+ for ( int i = 0; i < count; i++ ) {
+ EntityKey ek = EntityKey.deserialize( ois, session );
+ Object proxy = ois.readObject();
+ if ( proxy instanceof HibernateProxy ) {
+ ( ( HibernateProxy ) proxy ).getHibernateLazyInitializer().setSession( session );
+ rtn.proxiesByKey.put( ek, proxy );
+ }
+ else {
+ log.trace( "encountered prunded proxy" );
+ }
+ // otherwise, the proxy was pruned during the serialization process
+ }
+
+ count = ois.readInt();
+ log.trace( "staring deserialization of [" + count + "] entitySnapshotsByKey entries" );
+ rtn.entitySnapshotsByKey = new HashMap( count < INIT_COLL_SIZE ? INIT_COLL_SIZE : count );
+ for ( int i = 0; i < count; i++ ) {
+ rtn.entitySnapshotsByKey.put( EntityKey.deserialize( ois, session ), ois.readObject() );
+ }
+
+ count = ois.readInt();
+ log.trace( "staring deserialization of [" + count + "] entityEntries entries" );
+ rtn.entityEntries = IdentityMap.instantiateSequenced( count < INIT_COLL_SIZE ? INIT_COLL_SIZE : count );
+ for ( int i = 0; i < count; i++ ) {
+ Object entity = ois.readObject();
+ EntityEntry entry = EntityEntry.deserialize( ois, session );
+ rtn.entityEntries.put( entity, entry );
+ }
+
+ count = ois.readInt();
+ log.trace( "staring deserialization of [" + count + "] collectionsByKey entries" );
+ rtn.collectionsByKey = new HashMap( count < INIT_COLL_SIZE ? INIT_COLL_SIZE : count );
+ for ( int i = 0; i < count; i++ ) {
+ rtn.collectionsByKey.put( CollectionKey.deserialize( ois, session ), ois.readObject() );
+ }
+
+ count = ois.readInt();
+ log.trace( "staring deserialization of [" + count + "] collectionEntries entries" );
+ rtn.collectionEntries = IdentityMap.instantiateSequenced( count < INIT_COLL_SIZE ? INIT_COLL_SIZE : count );
+ for ( int i = 0; i < count; i++ ) {
+ final PersistentCollection pc = ( PersistentCollection ) ois.readObject();
+ final CollectionEntry ce = CollectionEntry.deserialize( ois, session );
+ pc.setCurrentSession( session );
+ rtn.collectionEntries.put( pc, ce );
+ }
+
+ count = ois.readInt();
+ log.trace( "staring deserialization of [" + count + "] arrayHolders entries" );
+ rtn.arrayHolders = IdentityMap.instantiate( count < INIT_COLL_SIZE ? INIT_COLL_SIZE : count );
+ for ( int i = 0; i < count; i++ ) {
+ rtn.arrayHolders.put( ois.readObject(), ois.readObject() );
+ }
+
+ count = ois.readInt();
+ log.trace( "staring deserialization of [" + count + "] nullifiableEntityKeys entries" );
+ rtn.nullifiableEntityKeys = new HashSet();
+ for ( int i = 0; i < count; i++ ) {
+ rtn.nullifiableEntityKeys.add( EntityKey.deserialize( ois, session ) );
+ }
+
+ }
+ catch ( HibernateException he ) {
+ throw new InvalidObjectException( he.getMessage() );
+ }
+
+ return rtn;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/Status.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/Status.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/Status.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,46 @@
+//$Id: Status.java 9194 2006-02-01 19:59:07Z steveebersole $
+package org.hibernate.engine;
+
+import java.io.Serializable;
+import java.io.ObjectStreamException;
+import java.io.InvalidObjectException;
+
+/**
+ * Represents the status of an entity with respect to
+ * this session. These statuses are for internal
+ * book-keeping only and are not intended to represent
+ * any notion that is visible to the _application_.
+ */
+public final class Status implements Serializable {
+
+ public static final Status MANAGED = new Status( "MANAGED" );
+ public static final Status READ_ONLY = new Status( "READ_ONLY" );
+ public static final Status DELETED = new Status( "DELETED" );
+ public static final Status GONE = new Status( "GONE" );
+ public static final Status LOADING = new Status( "LOADING" );
+ public static final Status SAVING = new Status( "SAVING" );
+
+ private String name;
+
+ private Status(String name) {
+ this.name = name;
+ }
+
+ public String toString() {
+ return name;
+ }
+
+ private Object readResolve() throws ObjectStreamException {
+ return parse( name );
+ }
+
+ public static Status parse(String name) throws InvalidObjectException {
+ if ( name.equals(MANAGED.name) ) return MANAGED;
+ if ( name.equals(READ_ONLY.name) ) return READ_ONLY;
+ if ( name.equals(DELETED.name) ) return DELETED;
+ if ( name.equals(GONE.name) ) return GONE;
+ if ( name.equals(LOADING.name) ) return LOADING;
+ if ( name.equals(SAVING.name) ) return SAVING;
+ throw new InvalidObjectException( "invalid Status" );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/SubselectFetch.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/SubselectFetch.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/SubselectFetch.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,78 @@
+//$Id: SubselectFetch.java 7670 2005-07-29 05:36:14Z oneovthafew $
+package org.hibernate.engine;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.hibernate.persister.entity.Loadable;
+import org.hibernate.persister.entity.PropertyMapping;
+import org.hibernate.util.StringHelper;
+
+/**
+ * @author Gavin King
+ */
+public class SubselectFetch {
+ private final Set resultingEntityKeys;
+ private final String queryString;
+ private final String alias;
+ private final Loadable loadable;
+ private final QueryParameters queryParameters;
+ private final Map namedParameterLocMap;
+
+ public SubselectFetch(
+ //final String queryString,
+ final String alias,
+ final Loadable loadable,
+ final QueryParameters queryParameters,
+ final Set resultingEntityKeys,
+ final Map namedParameterLocMap
+ ) {
+ this.resultingEntityKeys = resultingEntityKeys;
+ this.queryParameters = queryParameters;
+ this.namedParameterLocMap = namedParameterLocMap;
+ this.loadable = loadable;
+ this.alias = alias;
+
+ //TODO: ugly here:
+ final String queryString = queryParameters.getFilteredSQL();
+ int fromIndex = queryString.indexOf(" from ");
+ int orderByIndex = queryString.lastIndexOf("order by");
+ this.queryString = orderByIndex>0 ?
+ queryString.substring(fromIndex, orderByIndex) :
+ queryString.substring(fromIndex);
+
+ }
+
+ public QueryParameters getQueryParameters() {
+ return queryParameters;
+ }
+
+ /**
+ * Get the Set of EntityKeys
+ */
+ public Set getResult() {
+ return resultingEntityKeys;
+ }
+
+ public String toSubselectString(String ukname) {
+
+ String[] joinColumns = ukname==null ?
+ StringHelper.qualify( alias, loadable.getIdentifierColumnNames() ) :
+ ( (PropertyMapping) loadable ).toColumns(alias, ukname);
+
+ return new StringBuffer()
+ .append("select ")
+ .append( StringHelper.join(", ", joinColumns) )
+ .append(queryString)
+ .toString();
+ }
+
+ public String toString() {
+ return "SubselectFetch(" + queryString + ')';
+ }
+
+ public Map getNamedParameterLocMap() {
+ return namedParameterLocMap;
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/SubselectFetch.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/TransactionHelper.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/TransactionHelper.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/TransactionHelper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,54 @@
+//$Id: TransactionHelper.java 9056 2006-01-13 19:40:15Z steveebersole $
+package org.hibernate.engine;
+
+import java.io.Serializable;
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import org.hibernate.HibernateException;
+import org.hibernate.engine.transaction.IsolatedWork;
+import org.hibernate.engine.transaction.Isolater;
+import org.hibernate.exception.JDBCExceptionHelper;
+
+/**
+ * Allows work to be done outside the current transaction, by suspending it,
+ * and performing work in a new transaction
+ *
+ * @author Emmanuel Bernard
+ */
+public abstract class TransactionHelper {
+
+ // todo : remove this and just have subclasses use Isolater/IsolatedWork directly...
+
+ /**
+ * The work to be done
+ */
+ protected abstract Serializable doWorkInCurrentTransaction(Connection conn, String sql) throws SQLException;
+
+ /**
+ * Suspend the current transaction and perform work in a new transaction
+ */
+ public Serializable doWorkInNewTransaction(final SessionImplementor session)
+ throws HibernateException {
+ class Work implements IsolatedWork {
+ Serializable generatedValue;
+ public void doWork(Connection connection) throws HibernateException {
+ String sql = null;
+ try {
+ generatedValue = doWorkInCurrentTransaction( connection, sql );
+ }
+ catch( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ session.getFactory().getSQLExceptionConverter(),
+ sqle,
+ "could not get or update next value",
+ sql
+ );
+ }
+ }
+ }
+ Work work = new Work();
+ Isolater.doIsolatedWork( work, session );
+ return work.generatedValue;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/TwoPhaseLoad.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/TwoPhaseLoad.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/TwoPhaseLoad.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,273 @@
+//$Id: TwoPhaseLoad.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.engine;
+
+import java.io.Serializable;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.AssertionFailure;
+import org.hibernate.CacheMode;
+import org.hibernate.HibernateException;
+import org.hibernate.LockMode;
+import org.hibernate.cache.CacheKey;
+import org.hibernate.cache.entry.CacheEntry;
+import org.hibernate.event.PostLoadEvent;
+import org.hibernate.event.PostLoadEventListener;
+import org.hibernate.event.PreLoadEvent;
+import org.hibernate.event.PreLoadEventListener;
+import org.hibernate.intercept.LazyPropertyInitializer;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.pretty.MessageHelper;
+import org.hibernate.property.BackrefPropertyAccessor;
+import org.hibernate.type.Type;
+import org.hibernate.type.TypeFactory;
+
+/**
+ * Functionality relating to Hibernate's two-phase loading process,
+ * that may be reused by persisters that do not use the Loader
+ * framework
+ *
+ * @author Gavin King
+ */
+public final class TwoPhaseLoad {
+
+ private static final Log log = LogFactory.getLog(TwoPhaseLoad.class);
+
+ private TwoPhaseLoad() {}
+
+ /**
+ * Register the "hydrated" state of an entity instance, after the first step of 2-phase loading.
+ *
+ * Add the "hydrated state" (an array) of an uninitialized entity to the session. We don't try
+ * to resolve any associations yet, because there might be other entities waiting to be
+ * read from the JDBC result set we are currently processing
+ */
+ public static void postHydrate(
+ final EntityPersister persister,
+ final Serializable id,
+ final Object[] values,
+ final Object rowId,
+ final Object object,
+ final LockMode lockMode,
+ final boolean lazyPropertiesAreUnfetched,
+ final SessionImplementor session)
+ throws HibernateException {
+
+ Object version = Versioning.getVersion(values, persister);
+ session.getPersistenceContext().addEntry(
+ object,
+ Status.LOADING,
+ values,
+ rowId,
+ id,
+ version,
+ lockMode,
+ true,
+ persister,
+ false,
+ lazyPropertiesAreUnfetched
+ );
+
+ if ( log.isTraceEnabled() && version!=null ) {
+ String versionStr = persister.isVersioned()
+ ? persister.getVersionType().toLoggableString( version, session.getFactory() )
+ : "null";
+ log.trace( "Version: " + versionStr );
+ }
+
+ }
+
+ /**
+ * Perform the second step of 2-phase load. Fully initialize the entity
+ * instance.
+ *
+ * After processing a JDBC result set, we "resolve" all the associations
+ * between the entities which were instantiated and had their state
+ * "hydrated" into an array
+ */
+ public static void initializeEntity(
+ final Object entity,
+ final boolean readOnly,
+ final SessionImplementor session,
+ final PreLoadEvent preLoadEvent,
+ final PostLoadEvent postLoadEvent) throws HibernateException {
+
+ //TODO: Should this be an InitializeEntityEventListener??? (watch out for performance!)
+
+ final PersistenceContext persistenceContext = session.getPersistenceContext();
+ EntityEntry entityEntry = persistenceContext.getEntry(entity);
+ if ( entityEntry == null ) {
+ throw new AssertionFailure( "possible non-threadsafe access to the session" );
+ }
+ EntityPersister persister = entityEntry.getPersister();
+ Serializable id = entityEntry.getId();
+ Object[] hydratedState = entityEntry.getLoadedState();
+
+ if ( log.isDebugEnabled() )
+ log.debug(
+ "resolving associations for " +
+ MessageHelper.infoString(persister, id, session.getFactory())
+ );
+
+ Type[] types = persister.getPropertyTypes();
+ for ( int i = 0; i < hydratedState.length; i++ ) {
+ final Object value = hydratedState[i];
+ if ( value!=LazyPropertyInitializer.UNFETCHED_PROPERTY && value!=BackrefPropertyAccessor.UNKNOWN ) {
+ hydratedState[i] = types[i].resolve( value, session, entity );
+ }
+ }
+
+ //Must occur after resolving identifiers!
+ if ( session.isEventSource() ) {
+ preLoadEvent.setEntity(entity).setState(hydratedState).setId(id).setPersister(persister);
+ PreLoadEventListener[] listeners = session.getListeners().getPreLoadEventListeners();
+ for ( int i = 0; i < listeners.length; i++ ) {
+ listeners[i].onPreLoad(preLoadEvent);
+ }
+ }
+
+ persister.setPropertyValues( entity, hydratedState, session.getEntityMode() );
+
+ final SessionFactoryImplementor factory = session.getFactory();
+ if ( persister.hasCache() && session.getCacheMode().isPutEnabled() ) {
+
+ if ( log.isDebugEnabled() )
+ log.debug(
+ "adding entity to second-level cache: " +
+ MessageHelper.infoString( persister, id, session.getFactory() )
+ );
+
+ Object version = Versioning.getVersion(hydratedState, persister);
+ CacheEntry entry = new CacheEntry(
+ hydratedState,
+ persister,
+ entityEntry.isLoadedWithLazyPropertiesUnfetched(),
+ version,
+ session,
+ entity
+ );
+ CacheKey cacheKey = new CacheKey(
+ id,
+ persister.getIdentifierType(),
+ persister.getRootEntityName(),
+ session.getEntityMode(),
+ session.getFactory()
+ );
+ boolean put = persister.getCacheAccessStrategy().putFromLoad(
+ cacheKey,
+ persister.getCacheEntryStructure().structure( entry ),
+ session.getTimestamp(),
+ version,
+ useMinimalPuts( session, entityEntry )
+ );
+
+ if ( put && factory.getStatistics().isStatisticsEnabled() ) {
+ factory.getStatisticsImplementor().secondLevelCachePut( persister.getCacheAccessStrategy().getRegion().getName() );
+ }
+ }
+
+ if ( readOnly || !persister.isMutable() ) {
+ //no need to take a snapshot - this is a
+ //performance optimization, but not really
+ //important, except for entities with huge
+ //mutable property values
+ persistenceContext.setEntryStatus(entityEntry, Status.READ_ONLY);
+ }
+ else {
+ //take a snapshot
+ TypeFactory.deepCopy(
+ hydratedState,
+ persister.getPropertyTypes(),
+ persister.getPropertyUpdateability(),
+ hydratedState, //after setting values to object, entityMode
+ session
+ );
+ persistenceContext.setEntryStatus(entityEntry, Status.MANAGED);
+ }
+
+ persister.afterInitialize(
+ entity,
+ entityEntry.isLoadedWithLazyPropertiesUnfetched(),
+ session
+ );
+
+ if ( session.isEventSource() ) {
+ postLoadEvent.setEntity(entity).setId(id).setPersister(persister);
+ PostLoadEventListener[] listeners = session.getListeners().getPostLoadEventListeners();
+ for ( int i = 0; i < listeners.length; i++ ) {
+ listeners[i].onPostLoad(postLoadEvent);
+ }
+ }
+
+ if ( log.isDebugEnabled() )
+ log.debug(
+ "done materializing entity " +
+ MessageHelper.infoString( persister, id, session.getFactory() )
+ );
+
+ if ( factory.getStatistics().isStatisticsEnabled() ) {
+ factory.getStatisticsImplementor().loadEntity( persister.getEntityName() );
+ }
+
+ }
+
+ private static boolean useMinimalPuts(SessionImplementor session, EntityEntry entityEntry) {
+ return ( session.getFactory().getSettings().isMinimalPutsEnabled() &&
+ session.getCacheMode()!=CacheMode.REFRESH ) ||
+ ( entityEntry.getPersister().hasLazyProperties() &&
+ entityEntry.isLoadedWithLazyPropertiesUnfetched() &&
+ entityEntry.getPersister().isLazyPropertiesCacheable() );
+ }
+
+ /**
+ * Add an uninitialized instance of an entity class, as a placeholder to ensure object
+ * identity. Must be called before <tt>postHydrate()</tt>.
+ *
+ * Create a "temporary" entry for a newly instantiated entity. The entity is uninitialized,
+ * but we need the mapping from id to instance in order to guarantee uniqueness.
+ */
+ public static void addUninitializedEntity(
+ final EntityKey key,
+ final Object object,
+ final EntityPersister persister,
+ final LockMode lockMode,
+ final boolean lazyPropertiesAreUnfetched,
+ final SessionImplementor session
+ ) {
+ session.getPersistenceContext().addEntity(
+ object,
+ Status.LOADING,
+ null,
+ key,
+ null,
+ lockMode,
+ true,
+ persister,
+ false,
+ lazyPropertiesAreUnfetched
+ );
+ }
+
+ public static void addUninitializedCachedEntity(
+ final EntityKey key,
+ final Object object,
+ final EntityPersister persister,
+ final LockMode lockMode,
+ final boolean lazyPropertiesAreUnfetched,
+ final Object version,
+ final SessionImplementor session
+ ) {
+ session.getPersistenceContext().addEntity(
+ object,
+ Status.LOADING,
+ null,
+ key,
+ version,
+ lockMode,
+ true,
+ persister,
+ false,
+ lazyPropertiesAreUnfetched
+ );
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/TwoPhaseLoad.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/TypedValue.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/TypedValue.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/TypedValue.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,60 @@
+//$Id: TypedValue.java 6638 2005-05-02 14:34:17Z oneovthafew $
+package org.hibernate.engine;
+
+import java.io.Serializable;
+
+import org.hibernate.EntityMode;
+import org.hibernate.type.Type;
+
+/**
+ * An ordered pair of a value and its Hibernate type.
+ *
+ * @see org.hibernate.type.Type
+ * @author Gavin King
+ */
+public final class TypedValue implements Serializable {
+ private final Type type;
+ private final Object value;
+ private final EntityMode entityMode;
+
+ public TypedValue(Type type, Object value, EntityMode entityMode) {
+ this.type = type;
+ this.value=value;
+ this.entityMode = entityMode;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ public Type getType() {
+ return type;
+ }
+
+ public String toString() {
+ return value==null ? "null" : value.toString();
+ }
+
+ public int hashCode() {
+ //int result = 17;
+ //result = 37 * result + type.hashCode();
+ //result = 37 * result + ( value==null ? 0 : value.hashCode() );
+ //return result;
+ return value==null ? 0 : type.getHashCode(value, entityMode);
+ }
+
+ public boolean equals(Object other) {
+ if ( !(other instanceof TypedValue) ) return false;
+ TypedValue that = (TypedValue) other;
+ /*return that.type.equals(type) &&
+ EqualsHelper.equals(that.value, value);*/
+ return type.getReturnedClass() == that.type.getReturnedClass() &&
+ type.isEqual(that.value, value, entityMode);
+ }
+
+}
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/UnsavedValueFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/UnsavedValueFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/UnsavedValueFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,115 @@
+//$Id: UnsavedValueFactory.java 7736 2005-08-03 20:03:34Z steveebersole $
+package org.hibernate.engine;
+
+import java.io.Serializable;
+import java.lang.reflect.Constructor;
+
+import org.hibernate.InstantiationException;
+import org.hibernate.MappingException;
+import org.hibernate.property.Getter;
+import org.hibernate.type.IdentifierType;
+import org.hibernate.type.PrimitiveType;
+import org.hibernate.type.Type;
+import org.hibernate.type.VersionType;
+
+/**
+ * @author Gavin King
+ */
+public class UnsavedValueFactory {
+
+ private static Object instantiate(Constructor constructor) {
+ try {
+ return constructor.newInstance(null);
+ }
+ catch (Exception e) {
+ throw new InstantiationException( "could not instantiate test object", constructor.getDeclaringClass(), e );
+ }
+ }
+
+ /**
+ * Return an IdentifierValue for the specified unsaved-value. If none is specified,
+ * guess the unsaved value by instantiating a test instance of the class and
+ * reading it's id property, or if that is not possible, using the java default
+ * value for the type
+ */
+ public static IdentifierValue getUnsavedIdentifierValue(
+ String unsavedValue,
+ Getter identifierGetter,
+ Type identifierType,
+ Constructor constructor) {
+
+ if ( unsavedValue == null ) {
+ if ( identifierGetter!=null && constructor!=null ) {
+ // use the id value of a newly instantiated instance as the unsaved-value
+ Serializable defaultValue = (Serializable) identifierGetter.get( instantiate(constructor) );
+ return new IdentifierValue( defaultValue );
+ }
+ else if ( identifierGetter != null && (identifierType instanceof PrimitiveType) ) {
+ Serializable defaultValue = ( ( PrimitiveType ) identifierType ).getDefaultValue();
+ return new IdentifierValue( defaultValue );
+ }
+ else {
+ return IdentifierValue.NULL;
+ }
+ }
+ else if ( "null".equals( unsavedValue ) ) {
+ return IdentifierValue.NULL;
+ }
+ else if ( "undefined".equals( unsavedValue ) ) {
+ return IdentifierValue.UNDEFINED;
+ }
+ else if ( "none".equals( unsavedValue ) ) {
+ return IdentifierValue.NONE;
+ }
+ else if ( "any".equals( unsavedValue ) ) {
+ return IdentifierValue.ANY;
+ }
+ else {
+ try {
+ return new IdentifierValue( ( Serializable ) ( ( IdentifierType ) identifierType ).stringToObject( unsavedValue ) );
+ }
+ catch ( ClassCastException cce ) {
+ throw new MappingException( "Bad identifier type: " + identifierType.getName() );
+ }
+ catch ( Exception e ) {
+ throw new MappingException( "Could not parse identifier unsaved-value: " + unsavedValue );
+ }
+ }
+ }
+
+ public static VersionValue getUnsavedVersionValue(
+ String versionUnsavedValue,
+ Getter versionGetter,
+ VersionType versionType,
+ Constructor constructor) {
+
+ if ( versionUnsavedValue == null ) {
+ if ( constructor!=null ) {
+ Object defaultValue = versionGetter.get( instantiate(constructor) );
+ // if the version of a newly instantiated object is not the same
+ // as the version seed value, use that as the unsaved-value
+ return versionType.isEqual( versionType.seed( null ), defaultValue ) ?
+ VersionValue.UNDEFINED :
+ new VersionValue( defaultValue );
+ }
+ else {
+ return VersionValue.UNDEFINED;
+ }
+ }
+ else if ( "undefined".equals( versionUnsavedValue ) ) {
+ return VersionValue.UNDEFINED;
+ }
+ else if ( "null".equals( versionUnsavedValue ) ) {
+ return VersionValue.NULL;
+ }
+ else if ( "negative".equals( versionUnsavedValue ) ) {
+ return VersionValue.NEGATIVE;
+ }
+ else {
+ // this should not happen since the DTD prevents it
+ throw new MappingException( "Could not parse version unsaved-value: " + versionUnsavedValue );
+ }
+
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/UnsavedValueFactory.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/ValueInclusion.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/ValueInclusion.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/ValueInclusion.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,51 @@
+package org.hibernate.engine;
+
+import java.io.Serializable;
+import java.io.ObjectStreamException;
+import java.io.StreamCorruptedException;
+
+/**
+ * An enum of the different ways a value might be "included".
+ * <p/>
+ * This is really an expanded true/false notion with "PARTIAL" being the
+ * expansion. PARTIAL deals with components in the cases where
+ * parts of the referenced component might define inclusion, but the
+ * component overall does not.
+ *
+ * @author Steve Ebersole
+ */
+public class ValueInclusion implements Serializable {
+
+ public static final ValueInclusion NONE = new ValueInclusion( "none" );
+ public static final ValueInclusion FULL = new ValueInclusion( "full" );
+ public static final ValueInclusion PARTIAL = new ValueInclusion( "partial" );
+
+ private final String name;
+
+ public ValueInclusion(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String toString() {
+ return "ValueInclusion[" + name + "]";
+ }
+
+ private Object readResolve() throws ObjectStreamException {
+ if ( name.equals( NONE.name ) ) {
+ return NONE;
+ }
+ else if ( name.equals( FULL.name ) ) {
+ return FULL;
+ }
+ else if ( name.equals( PARTIAL.name ) ) {
+ return PARTIAL;
+ }
+ else {
+ throw new StreamCorruptedException( "unrecognized value inclusion [" + name + "]" );
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/VersionValue.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/VersionValue.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/VersionValue.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,109 @@
+//$Id: VersionValue.java 7017 2005-06-05 04:31:34Z oneovthafew $
+package org.hibernate.engine;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.MappingException;
+import org.hibernate.id.IdentifierGeneratorFactory;
+
+/**
+ * A strategy for determining if a version value is an version of
+ * a new transient instance or a previously persistent transient instance.
+ * The strategy is determined by the <tt>unsaved-value</tt> attribute in
+ * the mapping file.
+ *
+ * @author Gavin King
+ */
+public class VersionValue {
+
+ private static final Log log = LogFactory.getLog(VersionValue.class);
+
+ private final Object value;
+ /**
+ * Assume the transient instance is newly instantiated if the version
+ * is null, otherwise assume it is a detached instance.
+ */
+ public static final VersionValue NULL = new VersionValue() {
+ public final Boolean isUnsaved(Object version) {
+ log.trace("version unsaved-value strategy NULL");
+ return version==null ? Boolean.TRUE : Boolean.FALSE;
+ }
+ public Object getDefaultValue(Object currentValue) {
+ return null;
+ }
+ public String toString() {
+ return "VERSION_SAVE_NULL";
+ }
+ };
+ /**
+ * Assume the transient instance is newly instantiated if the version
+ * is null, otherwise defer to the identifier unsaved-value.
+ */
+ public static final VersionValue UNDEFINED = new VersionValue() {
+ public final Boolean isUnsaved(Object version) {
+ log.trace("version unsaved-value strategy UNDEFINED");
+ return version==null ? Boolean.TRUE : null;
+ }
+ public Object getDefaultValue(Object currentValue) {
+ return currentValue;
+ }
+ public String toString() {
+ return "VERSION_UNDEFINED";
+ }
+ };
+ /**
+ * Assume the transient instance is newly instantiated if the version
+ * is negative, otherwise assume it is a detached instance.
+ */
+ public static final VersionValue NEGATIVE = new VersionValue() {
+
+ public final Boolean isUnsaved(Object version) throws MappingException {
+ log.trace("version unsaved-value strategy NEGATIVE");
+ if (version==null) return Boolean.TRUE;
+ if (version instanceof Number) {
+ return ( (Number) version ).longValue() < 0l ? Boolean.TRUE : Boolean.FALSE;
+ }
+ else {
+ throw new MappingException("unsaved-value NEGATIVE may only be used with short, int and long types");
+ }
+ }
+ public Object getDefaultValue(Object currentValue) {
+ return IdentifierGeneratorFactory.createNumber( -1l, currentValue.getClass() );
+ }
+ public String toString() {
+ return "VERSION_NEGATIVE";
+ }
+ };
+
+ protected VersionValue() {
+ this.value = null;
+ }
+
+ /**
+ * Assume the transient instance is newly instantiated if
+ * its version is null or equal to <tt>value</tt>
+ * @param value value to compare to
+ */
+ public VersionValue(Object value) {
+ this.value = value;
+ }
+
+ /**
+ * Does the given version belong to a new instance?
+ *
+ * @param version version to check
+ * @return true is unsaved, false is saved, null is undefined
+ */
+ public Boolean isUnsaved(Object version) throws MappingException {
+ if ( log.isTraceEnabled() ) log.trace("version unsaved-value: " + value);
+ return version==null || version.equals(value) ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ public Object getDefaultValue(Object currentValue) {
+ return value;
+ }
+
+ public String toString() {
+ return "version unsaved-value: " + value;
+ }
+}
\ No newline at end of file
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/VersionValue.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/Versioning.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/Versioning.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/Versioning.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,172 @@
+//$Id: Versioning.java 10857 2006-11-21 23:28:07Z steve.ebersole at jboss.com $
+package org.hibernate.engine;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.type.VersionType;
+
+/**
+ * Utilities for dealing with optimisitic locking values.
+ *
+ * @author Gavin King
+ */
+public final class Versioning {
+ /**
+ * Apply no optimistic locking
+ */
+ public static final int OPTIMISTIC_LOCK_NONE = -1;
+
+ /**
+ * Apply optimisitc locking based on the defined version or timestamp
+ * property.
+ */
+ public static final int OPTIMISTIC_LOCK_VERSION = 0;
+
+ /**
+ * Apply optimisitc locking based on the a current vs. snapshot comparison
+ * of <b>all</b> properties.
+ */
+ public static final int OPTIMISTIC_LOCK_ALL = 2;
+
+ /**
+ * Apply optimisitc locking based on the a current vs. snapshot comparison
+ * of <b>dirty</b> properties.
+ */
+ public static final int OPTIMISTIC_LOCK_DIRTY = 1;
+
+ private static final Log log = LogFactory.getLog( Versioning.class );
+
+ /**
+ * Private constructor disallowing instantiation.
+ */
+ private Versioning() {}
+
+ /**
+ * Create an initial optimisitc locking value according the {@link VersionType}
+ * contract for the version property.
+ *
+ * @param versionType The version type.
+ * @param session The originating session
+ * @return The initial optimisitc locking value
+ */
+ private static Object seed(VersionType versionType, SessionImplementor session) {
+ Object seed = versionType.seed( session );
+ if ( log.isTraceEnabled() ) log.trace("Seeding: " + seed);
+ return seed;
+ }
+
+ /**
+ * Create an initial optimisitc locking value according the {@link VersionType}
+ * contract for the version property <b>if required</b> and inject it into
+ * the snapshot state.
+ *
+ * @param fields The current snapshot state
+ * @param versionProperty The index of the version property
+ * @param versionType The version type
+ * @param session The orginating session
+ * @return True if we injected a new version value into the fields array; false
+ * otherwise.
+ */
+ public static boolean seedVersion(
+ Object[] fields,
+ int versionProperty,
+ VersionType versionType,
+ SessionImplementor session) {
+ Object initialVersion = fields[versionProperty];
+ if (
+ initialVersion==null ||
+ // This next bit is to allow for both unsaved-value="negative"
+ // and for "older" behavior where version number did not get
+ // seeded if it was already set in the object
+ // TODO: shift it into unsaved-value strategy
+ ( (initialVersion instanceof Number) && ( (Number) initialVersion ).longValue()<0 )
+ ) {
+ fields[versionProperty] = seed( versionType, session );
+ return true;
+ }
+ else {
+ if ( log.isTraceEnabled() ) {
+ log.trace( "using initial version: " + initialVersion );
+ }
+ return false;
+ }
+ }
+
+
+ /**
+ * Generate the next increment in the optimisitc locking value according
+ * the {@link VersionType} contract for the version property.
+ *
+ * @param version The current version
+ * @param versionType The version type
+ * @param session The originating session
+ * @return The incremented optimistic locking value.
+ */
+ public static Object increment(Object version, VersionType versionType, SessionImplementor session) {
+ Object next = versionType.next( version, session );
+ if ( log.isTraceEnabled() ) {
+ log.trace(
+ "Incrementing: " +
+ versionType.toLoggableString( version, session.getFactory() ) +
+ " to " +
+ versionType.toLoggableString( next, session.getFactory() )
+ );
+ }
+ return next;
+ }
+
+ /**
+ * Inject the optimisitc locking value into the entity state snapshot.
+ *
+ * @param fields The state snapshot
+ * @param version The optimisitc locking value
+ * @param persister The entity persister
+ */
+ public static void setVersion(Object[] fields, Object version, EntityPersister persister) {
+ if ( !persister.isVersioned() ) {
+ return;
+ }
+ fields[ persister.getVersionProperty() ] = version;
+ }
+
+ /**
+ * Extract the optimisitc locking value out of the entity state snapshot.
+ *
+ * @param fields The state snapshot
+ * @param persister The entity persister
+ * @return The extracted optimisitc locking value
+ */
+ public static Object getVersion(Object[] fields, EntityPersister persister) {
+ if ( !persister.isVersioned() ) {
+ return null;
+ }
+ return fields[ persister.getVersionProperty() ];
+ }
+
+ /**
+ * Do we need to increment the version number, given the dirty properties?
+ *
+ * @param dirtyProperties The array of property indexes which were deemed dirty
+ * @param hasDirtyCollections Were any collections found to be dirty (structurally changed)
+ * @param propertyVersionability An array indicating versionability of each property.
+ * @return True if a version increment is required; false otherwise.
+ */
+ public static boolean isVersionIncrementRequired(
+ final int[] dirtyProperties,
+ final boolean hasDirtyCollections,
+ final boolean[] propertyVersionability) {
+ if ( hasDirtyCollections ) {
+ return true;
+ }
+ for ( int i = 0; i < dirtyProperties.length; i++ ) {
+ if ( propertyVersionability[ dirtyProperties[i] ] ) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/loading/CollectionLoadContext.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/loading/CollectionLoadContext.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/loading/CollectionLoadContext.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,322 @@
+package org.hibernate.engine.loading;
+
+import java.sql.ResultSet;
+import java.io.Serializable;
+import java.util.Map;
+import java.util.List;
+import java.util.Iterator;
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.HashSet;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.EntityMode;
+import org.hibernate.CacheMode;
+import org.hibernate.cache.entry.CollectionCacheEntry;
+import org.hibernate.cache.CacheKey;
+import org.hibernate.pretty.MessageHelper;
+import org.hibernate.engine.CollectionKey;
+import org.hibernate.engine.Status;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.CollectionEntry;
+import org.hibernate.engine.SessionFactoryImplementor;
+
+/**
+ * Represents state associated with the processing of a given {@link ResultSet}
+ * in regards to loading collections.
+ * <p/>
+ * Another implementation option to consider is to not expose {@link ResultSet}s
+ * directly (in the JDBC redesign) but to always "wrap" them and apply a
+ * [series of] context[s] to that wrapper.
+ *
+ * @author Steve Ebersole
+ */
+public class CollectionLoadContext {
+ private static final Log log = LogFactory.getLog( CollectionLoadContext.class );
+
+ private final LoadContexts loadContexts;
+ private final ResultSet resultSet;
+ private Set localLoadingCollectionKeys = new HashSet();
+
+ /**
+ * Creates a collection load context for the given result set.
+ *
+ * @param loadContexts Callback to other collection load contexts.
+ * @param resultSet The result set this is "wrapping".
+ */
+ public CollectionLoadContext(LoadContexts loadContexts, ResultSet resultSet) {
+ this.loadContexts = loadContexts;
+ this.resultSet = resultSet;
+ }
+
+ public ResultSet getResultSet() {
+ return resultSet;
+ }
+
+ public LoadContexts getLoadContext() {
+ return loadContexts;
+ }
+
+ /**
+ * Retrieve the collection that is being loaded as part of processing this
+ * result set.
+ * <p/>
+ * Basically, there are two valid return values from this method:<ul>
+ * <li>an instance of {@link PersistentCollection} which indicates to
+ * continue loading the result set row data into that returned collection
+ * instance; this may be either an instance already associated and in the
+ * midst of being loaded, or a newly instantiated instance as a matching
+ * associated collection was not found.</li>
+ * <li><i>null</i> indicates to ignore the corresponding result set row
+ * data relating to the requested collection; this indicates that either
+ * the collection was found to already be associated with the persistence
+ * context in a fully loaded state, or it was found in a loading state
+ * associated with another result set processing context.</li>
+ * </ul>
+ *
+ * @param persister The persister for the collection being requested.
+ * @param key The key of the collection being requested.
+ *
+ * @return The loading collection (see discussion above).
+ */
+ public PersistentCollection getLoadingCollection(final CollectionPersister persister, final Serializable key) {
+ final EntityMode em = loadContexts.getPersistenceContext().getSession().getEntityMode();
+ final CollectionKey collectionKey = new CollectionKey( persister, key, em );
+ if ( log.isTraceEnabled() ) {
+ log.trace( "starting attempt to find loading collection [" + MessageHelper.collectionInfoString( persister.getRole(), key ) + "]" );
+ }
+ final LoadingCollectionEntry loadingCollectionEntry = loadContexts.locateLoadingCollectionEntry( collectionKey );
+ if ( loadingCollectionEntry == null ) {
+ // look for existing collection as part of the persistence context
+ PersistentCollection collection = loadContexts.getPersistenceContext().getCollection( collectionKey );
+ if ( collection != null ) {
+ if ( collection.wasInitialized() ) {
+ log.trace( "collection already initialized; ignoring" );
+ return null; // ignore this row of results! Note the early exit
+ }
+ else {
+ // initialize this collection
+ log.trace( "collection not yet initialized; initializing" );
+ }
+ }
+ else {
+ Object owner = loadContexts.getPersistenceContext().getCollectionOwner( key, persister );
+ final boolean newlySavedEntity = owner != null
+ && loadContexts.getPersistenceContext().getEntry( owner ).getStatus() != Status.LOADING
+ && em != EntityMode.DOM4J;
+ if ( newlySavedEntity ) {
+ // important, to account for newly saved entities in query
+ // todo : some kind of check for new status...
+ log.trace( "owning entity already loaded; ignoring" );
+ return null;
+ }
+ else {
+ // create one
+ if ( log.isTraceEnabled() ) {
+ log.trace( "instantiating new collection [key=" + key + ", rs=" + resultSet + "]" );
+ }
+ collection = persister.getCollectionType()
+ .instantiate( loadContexts.getPersistenceContext().getSession(), persister, key );
+ }
+ }
+ collection.beforeInitialize( persister, -1 );
+ collection.beginRead();
+ localLoadingCollectionKeys.add( collectionKey );
+ loadContexts.registerLoadingCollectionEntry( collectionKey, new LoadingCollectionEntry( resultSet, persister, key, collection ) );
+ return collection;
+ }
+ else {
+ if ( loadingCollectionEntry.getResultSet() == resultSet ) {
+ log.trace( "found loading collection bound to current result set processing; reading row" );
+ return loadingCollectionEntry.getCollection();
+ }
+ else {
+ // ignore this row, the collection is in process of
+ // being loaded somewhere further "up" the stack
+ log.trace( "collection is already being initialized; ignoring row" );
+ return null;
+ }
+ }
+ }
+
+ /**
+ * Finish the process of collection-loading for this bound result set. Mainly this
+ * involves cleaning up resources and notifying the collections that loading is
+ * complete.
+ *
+ * @param persister The persister for which to complete loading.
+ */
+ public void endLoadingCollections(CollectionPersister persister) {
+ SessionImplementor session = getLoadContext().getPersistenceContext().getSession();
+ if ( loadContexts.getLoadingCollectionEntryMap() == null
+ || loadContexts.getLoadingCollectionEntryMap().isEmpty()
+ || localLoadingCollectionKeys.isEmpty() ) {
+ return;
+ }
+
+ // in an effort to avoid concurrent-modification-exceptions (from
+ // potential recursive calls back through here as a result of the
+ // eventual call to PersistentCollection#endRead), we scan the
+ // internal loadingCollections map for matches and store those matches
+ // in a temp collection. the temp collection is then used to "drive"
+ // the #endRead processing.
+ List matches = null;
+ Iterator iter = loadContexts.getLoadingCollectionEntryMap().entrySet().iterator();
+ while ( iter.hasNext() ) {
+ final Map.Entry mapEntry = ( Map.Entry ) iter.next();
+ final CollectionKey collectionKey = ( CollectionKey ) mapEntry.getKey();
+ final LoadingCollectionEntry lce = ( LoadingCollectionEntry ) mapEntry.getValue();
+ if ( localLoadingCollectionKeys.contains( collectionKey ) && lce.getResultSet() == resultSet && lce.getPersister() == persister) {
+ if ( matches == null ) {
+ matches = new ArrayList();
+ }
+ matches.add( lce );
+ if ( lce.getCollection().getOwner() == null ) {
+ session.getPersistenceContext().addUnownedCollection(
+ new CollectionKey( persister, lce.getKey(), session.getEntityMode() ),
+ lce.getCollection()
+ );
+ }
+ if ( log.isTraceEnabled() ) {
+ log.trace( "removing collection load entry [" + lce + "]" );
+ }
+ iter.remove();
+ }
+ }
+
+ endLoadingCollections( persister, matches );
+ }
+
+ private void endLoadingCollections(CollectionPersister persister, List matchedCollectionEntries) {
+ if ( matchedCollectionEntries == null ) {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "no collections were found in result set for role: " + persister.getRole() );
+ }
+ return;
+ }
+
+ final int count = matchedCollectionEntries.size();
+ if ( log.isDebugEnabled() ) {
+ log.debug( count + " collections were found in result set for role: " + persister.getRole() );
+ }
+
+ for ( int i = 0; i < count; i++ ) {
+ LoadingCollectionEntry lce = ( LoadingCollectionEntry ) matchedCollectionEntries.get( i );
+ endLoadingCollection( lce, persister );
+ }
+
+ if ( log.isDebugEnabled() ) {
+ log.debug( count + " collections initialized for role: " + persister.getRole() );
+ }
+ }
+
+ private void endLoadingCollection(LoadingCollectionEntry lce, CollectionPersister persister) {
+ if ( log.isTraceEnabled() ) {
+ log.debug( "ending loading collection [" + lce + "]" );
+ }
+ final SessionImplementor session = getLoadContext().getPersistenceContext().getSession();
+ final EntityMode em = session.getEntityMode();
+
+ boolean hasNoQueuedAdds = lce.getCollection().endRead(); // warning: can cause a recursive calls! (proxy initialization)
+
+ if ( persister.getCollectionType().hasHolder( em ) ) {
+ getLoadContext().getPersistenceContext().addCollectionHolder( lce.getCollection() );
+ }
+
+ CollectionEntry ce = getLoadContext().getPersistenceContext().getCollectionEntry( lce.getCollection() );
+ if ( ce == null ) {
+ ce = getLoadContext().getPersistenceContext().addInitializedCollection( persister, lce.getCollection(), lce.getKey() );
+ }
+ else {
+ ce.postInitialize( lce.getCollection() );
+ }
+
+ boolean addToCache = hasNoQueuedAdds && // there were no queued additions
+ persister.hasCache() && // and the role has a cache
+ session.getCacheMode().isPutEnabled() &&
+ !ce.isDoremove(); // and this is not a forced initialization during flush
+ if ( addToCache ) {
+ addCollectionToCache( lce, persister );
+ }
+
+ if ( log.isDebugEnabled() ) {
+ log.debug( "collection fully initialized: " + MessageHelper.collectionInfoString(persister, lce.getKey(), session.getFactory() ) );
+ }
+
+ if ( session.getFactory().getStatistics().isStatisticsEnabled() ) {
+ session.getFactory().getStatisticsImplementor().loadCollection( persister.getRole() );
+ }
+ }
+
+ /**
+ * Add the collection to the second-level cache
+ *
+ * @param lce The entry representing the collection to add
+ * @param persister The persister
+ */
+ private void addCollectionToCache(LoadingCollectionEntry lce, CollectionPersister persister) {
+ final SessionImplementor session = getLoadContext().getPersistenceContext().getSession();
+ final SessionFactoryImplementor factory = session.getFactory();
+
+ if ( log.isDebugEnabled() ) {
+ log.debug( "Caching collection: " + MessageHelper.collectionInfoString( persister, lce.getKey(), factory ) );
+ }
+
+ if ( !session.getEnabledFilters().isEmpty() && persister.isAffectedByEnabledFilters( session ) ) {
+ // some filters affecting the collection are enabled on the session, so do not do the put into the cache.
+ log.debug( "Refusing to add to cache due to enabled filters" );
+ // todo : add the notion of enabled filters to the CacheKey to differentiate filtered collections from non-filtered;
+ // but CacheKey is currently used for both collections and entities; would ideally need to define two seperate ones;
+ // currently this works in conjuction with the check on
+ // DefaultInitializeCollectionEventHandler.initializeCollectionFromCache() (which makes sure to not read from
+ // cache with enabled filters).
+ return; // EARLY EXIT!!!!!
+ }
+
+ final Object version;
+ if ( persister.isVersioned() ) {
+ final Object collectionOwner = getLoadContext().getPersistenceContext().getCollectionOwner( lce.getKey(), persister );
+ version = getLoadContext().getPersistenceContext().getEntry( collectionOwner ).getVersion();
+ }
+ else {
+ version = null;
+ }
+
+ CollectionCacheEntry entry = new CollectionCacheEntry( lce.getCollection(), persister );
+ CacheKey cacheKey = new CacheKey(
+ lce.getKey(),
+ persister.getKeyType(),
+ persister.getRole(),
+ session.getEntityMode(),
+ session.getFactory()
+ );
+ boolean put = persister.getCacheAccessStrategy().putFromLoad(
+ cacheKey,
+ persister.getCacheEntryStructure().structure(entry),
+ session.getTimestamp(),
+ version,
+ factory.getSettings().isMinimalPutsEnabled() && session.getCacheMode()!= CacheMode.REFRESH
+ );
+
+ if ( put && factory.getStatistics().isStatisticsEnabled() ) {
+ factory.getStatisticsImplementor().secondLevelCachePut( persister.getCacheAccessStrategy().getRegion().getName() );
+ }
+ }
+
+ void cleanup() {
+ if ( !localLoadingCollectionKeys.isEmpty() ) {
+ log.warn( "On CollectionLoadContext#clear, loadingCollections contained [" + localLoadingCollectionKeys.size() + "] entries" );
+ }
+ loadContexts.cleanupCollectionEntries( localLoadingCollectionKeys );
+ localLoadingCollectionKeys.clear();
+ }
+
+
+ public String toString() {
+ return super.toString() + "<rs=" + resultSet + ">";
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/loading/EntityLoadContext.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/loading/EntityLoadContext.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/loading/EntityLoadContext.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,33 @@
+package org.hibernate.engine.loading;
+
+import java.sql.ResultSet;
+import java.util.List;
+import java.util.ArrayList;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class EntityLoadContext {
+ private static final Log log = LogFactory.getLog( EntityLoadContext.class );
+
+ private final LoadContexts loadContexts;
+ private final ResultSet resultSet;
+ private final List hydratingEntities = new ArrayList( 20 ); // todo : need map? the prob is a proper key, right?
+
+ public EntityLoadContext(LoadContexts loadContexts, ResultSet resultSet) {
+ this.loadContexts = loadContexts;
+ this.resultSet = resultSet;
+ }
+
+ void cleanup() {
+ if ( !hydratingEntities.isEmpty() ) {
+ log.warn( "On EntityLoadContext#clear, hydratingEntities contained [" + hydratingEntities.size() + "] entries" );
+ }
+ hydratingEntities.clear();
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/loading/LoadContexts.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/loading/LoadContexts.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/loading/LoadContexts.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,199 @@
+package org.hibernate.engine.loading;
+
+import java.sql.ResultSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.Iterator;
+import java.util.HashMap;
+import java.io.Serializable;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.hibernate.util.IdentityMap;
+import org.hibernate.engine.PersistenceContext;
+import org.hibernate.engine.CollectionKey;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.pretty.MessageHelper;
+import org.hibernate.EntityMode;
+
+/**
+ * Maps {@link ResultSet result-sets} to specific contextual data
+ * related to processing that {@link ResultSet result-sets}.
+ * <p/>
+ * Implementation note: internally an {@link IdentityMap} is used to maintain
+ * the mappings; {@link IdentityMap} was chosen because I'd rather not be
+ * dependent upon potentially bad {@link ResultSet#equals} and {ResultSet#hashCode}
+ * implementations.
+ * <p/>
+ * Considering the JDBC-redesign work, would further like this contextual info
+ * not mapped seperately, but available based on the result set being processed.
+ * This would also allow maintaining a single mapping as we could reliably get
+ * notification of the result-set closing...
+ *
+ * @author Steve Ebersole
+ */
+public class LoadContexts {
+ private static final Log log = LogFactory.getLog( LoadContexts.class );
+
+ private final PersistenceContext persistenceContext;
+ private Map collectionLoadContexts;
+ private Map entityLoadContexts;
+
+ private Map xrefLoadingCollectionEntries;
+
+ /**
+ * Creates and binds this to the given persistence context.
+ *
+ * @param persistenceContext The persistence context to which this
+ * will be bound.
+ */
+ public LoadContexts(PersistenceContext persistenceContext) {
+ this.persistenceContext = persistenceContext;
+ }
+
+ /**
+ * Retrieves the persistence context to which this is bound.
+ *
+ * @return The persistence context to which this is bound.
+ */
+ public PersistenceContext getPersistenceContext() {
+ return persistenceContext;
+ }
+
+ /**
+ * Get the {@link CollectionLoadContext} associated with the given
+ * {@link ResultSet}, creating one if needed.
+ *
+ * @param resultSet The result set for which to retrieve the context.
+ * @return The processing context.
+ */
+ public CollectionLoadContext getCollectionLoadContext(ResultSet resultSet) {
+ CollectionLoadContext context = null;
+ if ( collectionLoadContexts == null ) {
+ collectionLoadContexts = IdentityMap.instantiate( 8 );
+ }
+ else {
+ context = ( CollectionLoadContext ) collectionLoadContexts.get( resultSet );
+ }
+ if ( context == null ) {
+ if ( log.isTraceEnabled() ) {
+ log.trace( "constructing collection load context for result set [" + resultSet + "]" );
+ }
+ context = new CollectionLoadContext( this, resultSet );
+ collectionLoadContexts.put( resultSet, context );
+ }
+ return context;
+ }
+
+ /**
+ * Attempt to locate the loading collection given the owner's key. The lookup here
+ * occurs against all result-set contexts...
+ *
+ * @param persister The collection persister
+ * @param ownerKey The owner key
+ * @return The loading collection, or null if not found.
+ */
+ public PersistentCollection locateLoadingCollection(CollectionPersister persister, Serializable ownerKey) {
+ LoadingCollectionEntry lce = locateLoadingCollectionEntry( new CollectionKey( persister, ownerKey, getEntityMode() ) );
+ if ( lce != null ) {
+ if ( log.isTraceEnabled() ) {
+ log.trace( "returning loading collection:" + MessageHelper.collectionInfoString( persister, ownerKey, getSession().getFactory() ) );
+ }
+ return lce.getCollection();
+ }
+ else {
+ // todo : should really move this log statement to CollectionType, where this is used from...
+ if ( log.isTraceEnabled() ) {
+ log.trace( "creating collection wrapper:" + MessageHelper.collectionInfoString( persister, ownerKey, getSession().getFactory() ) );
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Locate the LoadingCollectionEntry within *any* of the tracked
+ * {@link CollectionLoadContext}s.
+ * <p/>
+ * Implementation note: package protected, as this is meant solely for use
+ * by {@link CollectionLoadContext} to be able to locate collections
+ * being loaded by other {@link CollectionLoadContext}s/{@link ResultSet}s.
+ *
+ * @param key The collection key.
+ * @return The located entry; or null.
+ */
+ LoadingCollectionEntry locateLoadingCollectionEntry(CollectionKey key) {
+ if ( xrefLoadingCollectionEntries == null ) {
+ return null;
+ }
+ if ( log.isTraceEnabled() ) {
+ log.trace( "attempting to locate loading collection entry [" + key + "] in any result-set context" );
+ }
+ LoadingCollectionEntry rtn = ( LoadingCollectionEntry ) xrefLoadingCollectionEntries.get( key );
+ if ( log.isTraceEnabled() ) {
+ if ( rtn == null ) {
+ log.trace( "collection [" + key + "] located in load context" );
+ }
+ else {
+ log.trace( "collection [" + key + "] not located in load context" );
+ }
+ }
+ return rtn;
+ }
+
+ /*package*/void registerLoadingCollectionEntry(CollectionKey entryKey, LoadingCollectionEntry entry) {
+ if ( xrefLoadingCollectionEntries == null ) {
+ xrefLoadingCollectionEntries = new HashMap();
+ }
+ xrefLoadingCollectionEntries.put( entryKey, entry );
+ }
+
+ /*package*/Map getLoadingCollectionEntryMap() {
+ return xrefLoadingCollectionEntries;
+ }
+
+ /*package*/void cleanupCollectionEntries(Set entryKeys) {
+ Iterator itr = entryKeys.iterator();
+ while ( itr.hasNext() ) {
+ final CollectionKey entryKey = ( CollectionKey ) itr.next();
+ xrefLoadingCollectionEntries.remove( entryKey );
+ }
+ }
+
+ public EntityLoadContext getEntityLoadContext(ResultSet resultSet) {
+ EntityLoadContext context = null;
+ if ( entityLoadContexts == null ) {
+ entityLoadContexts = IdentityMap.instantiate( 8 );
+ }
+ else {
+ context = ( EntityLoadContext ) entityLoadContexts.get( resultSet );
+ }
+ if ( context == null ) {
+ context = new EntityLoadContext( this, resultSet );
+ entityLoadContexts.put( resultSet, context );
+ }
+ return context;
+ }
+
+ public void cleanup(ResultSet resultSet) {
+ if ( collectionLoadContexts != null ) {
+ CollectionLoadContext collectionLoadContext = ( CollectionLoadContext ) collectionLoadContexts.remove( resultSet );
+ collectionLoadContext.cleanup();
+ }
+ if ( entityLoadContexts != null ) {
+ EntityLoadContext entityLoadContext = ( EntityLoadContext ) entityLoadContexts.remove( resultSet );
+ entityLoadContext.cleanup();
+ }
+ }
+
+ private SessionImplementor getSession() {
+ return getPersistenceContext().getSession();
+ }
+
+ private EntityMode getEntityMode() {
+ return getSession().getEntityMode();
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/loading/LoadingCollectionEntry.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/loading/LoadingCollectionEntry.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/loading/LoadingCollectionEntry.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,51 @@
+package org.hibernate.engine.loading;
+
+import java.io.Serializable;
+import java.sql.ResultSet;
+
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.pretty.MessageHelper;
+
+/**
+ * Represents a collection currently being loaded.
+ *
+ * @author Steve Ebersole
+ */
+public class LoadingCollectionEntry {
+ private final ResultSet resultSet;
+ private final CollectionPersister persister;
+ private final Serializable key;
+ private final PersistentCollection collection;
+
+ public LoadingCollectionEntry(
+ ResultSet resultSet,
+ CollectionPersister persister,
+ Serializable key,
+ PersistentCollection collection) {
+ this.resultSet = resultSet;
+ this.persister = persister;
+ this.key = key;
+ this.collection = collection;
+ }
+
+ public ResultSet getResultSet() {
+ return resultSet;
+ }
+
+ public CollectionPersister getPersister() {
+ return persister;
+ }
+
+ public Serializable getKey() {
+ return key;
+ }
+
+ public PersistentCollection getCollection() {
+ return collection;
+ }
+
+ public String toString() {
+ return getClass().getName() + "<rs=" + resultSet + ", coll=" + MessageHelper.collectionInfoString( persister.getRole(), key ) + ">@" + Integer.toHexString( hashCode() );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,9 @@
+<html>
+<head></head>
+<body>
+<p>
+ This package contains classes that are "shared" by other packages,
+ and implementations of some key algorithms.
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/FilterQueryPlan.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/FilterQueryPlan.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/FilterQueryPlan.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,31 @@
+package org.hibernate.engine.query;
+
+import org.hibernate.engine.SessionFactoryImplementor;
+
+import java.io.Serializable;
+import java.util.Map;
+
+/**
+ * Extends an HQLQueryPlan to maintain a reference to the collection-role name
+ * being filtered.
+ *
+ * @author <a href="mailto:steve at hibernate.org">Steve Ebersole </a>
+ */
+public class FilterQueryPlan extends HQLQueryPlan implements Serializable {
+
+ private final String collectionRole;
+
+ public FilterQueryPlan(
+ String hql,
+ String collectionRole,
+ boolean shallow,
+ Map enabledFilters,
+ SessionFactoryImplementor factory) {
+ super( hql, collectionRole, shallow, enabledFilters, factory );
+ this.collectionRole = collectionRole;
+ }
+
+ public String getCollectionRole() {
+ return collectionRole;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/HQLQueryPlan.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/HQLQueryPlan.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/HQLQueryPlan.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,315 @@
+package org.hibernate.engine.query;
+
+import org.hibernate.hql.QuerySplitter;
+import org.hibernate.hql.QueryTranslator;
+import org.hibernate.hql.ParameterTranslations;
+import org.hibernate.hql.FilterTranslator;
+import org.hibernate.util.ArrayHelper;
+import org.hibernate.util.EmptyIterator;
+import org.hibernate.util.JoinedIterator;
+import org.hibernate.util.IdentitySet;
+import org.hibernate.HibernateException;
+import org.hibernate.ScrollableResults;
+import org.hibernate.QueryException;
+import org.hibernate.type.Type;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.RowSelection;
+import org.hibernate.event.EventSource;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.Serializable;
+import java.util.Map;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.HashMap;
+
+/**
+ * Defines a query execution plan for an HQL query (or filter).
+ *
+ * @author <a href="mailto:steve at hibernate.org">Steve Ebersole </a>
+ */
+public class HQLQueryPlan implements Serializable {
+
+ // TODO : keep seperate notions of QT[] here for shallow/non-shallow queries...
+
+ private static final Log log = LogFactory.getLog( HQLQueryPlan.class );
+
+ private final String sourceQuery;
+ private final QueryTranslator[] translators;
+ private final String[] sqlStrings;
+
+ private final ParameterMetadata parameterMetadata;
+ private final ReturnMetadata returnMetadata;
+ private final Set querySpaces;
+
+ private final Set enabledFilterNames;
+ private final boolean shallow;
+
+
+ public HQLQueryPlan(String hql, boolean shallow, Map enabledFilters, SessionFactoryImplementor factory) {
+ this( hql, null, shallow, enabledFilters, factory );
+ }
+
+ protected HQLQueryPlan(String hql, String collectionRole, boolean shallow, Map enabledFilters, SessionFactoryImplementor factory) {
+ this.sourceQuery = hql;
+ this.shallow = shallow;
+
+ Set copy = new HashSet();
+ copy.addAll( enabledFilters.keySet() );
+ this.enabledFilterNames = java.util.Collections.unmodifiableSet( copy );
+
+ Set combinedQuerySpaces = new HashSet();
+ String[] concreteQueryStrings = QuerySplitter.concreteQueries( hql, factory );
+ final int length = concreteQueryStrings.length;
+ translators = new QueryTranslator[length];
+ List sqlStringList = new ArrayList();
+ for ( int i=0; i<length; i++ ) {
+ if ( collectionRole == null ) {
+ translators[i] = factory.getSettings()
+ .getQueryTranslatorFactory()
+ .createQueryTranslator( hql, concreteQueryStrings[i], enabledFilters, factory );
+ translators[i].compile( factory.getSettings().getQuerySubstitutions(), shallow );
+ }
+ else {
+ translators[i] = factory.getSettings()
+ .getQueryTranslatorFactory()
+ .createFilterTranslator( hql, concreteQueryStrings[i], enabledFilters, factory );
+ ( ( FilterTranslator ) translators[i] ).compile( collectionRole, factory.getSettings().getQuerySubstitutions(), shallow );
+ }
+ combinedQuerySpaces.addAll( translators[i].getQuerySpaces() );
+ sqlStringList.addAll( translators[i].collectSqlStrings() );
+ }
+
+ this.sqlStrings = ArrayHelper.toStringArray( sqlStringList );
+ this.querySpaces = combinedQuerySpaces;
+
+ if ( length == 0 ) {
+ parameterMetadata = new ParameterMetadata( null, null );
+ returnMetadata = null;
+ }
+ else {
+ this.parameterMetadata = buildParameterMetadata( translators[0].getParameterTranslations(), hql );
+ if ( translators[0].isManipulationStatement() ) {
+ returnMetadata = null;
+ }
+ else {
+ if ( length > 1 ) {
+ final int returns = translators[0].getReturnTypes().length;
+ returnMetadata = new ReturnMetadata( translators[0].getReturnAliases(), new Type[returns] );
+ }
+ else {
+ returnMetadata = new ReturnMetadata( translators[0].getReturnAliases(), translators[0].getReturnTypes() );
+ }
+ }
+ }
+ }
+
+ public String getSourceQuery() {
+ return sourceQuery;
+ }
+
+ public Set getQuerySpaces() {
+ return querySpaces;
+ }
+
+ public ParameterMetadata getParameterMetadata() {
+ return parameterMetadata;
+ }
+
+ public ReturnMetadata getReturnMetadata() {
+ return returnMetadata;
+ }
+
+ public Set getEnabledFilterNames() {
+ return enabledFilterNames;
+ }
+
+ public String[] getSqlStrings() {
+ return sqlStrings;
+ }
+
+ public Set getUtilizedFilterNames() {
+ // TODO : add this info to the translator and aggregate it here...
+ return null;
+ }
+
+ public boolean isShallow() {
+ return shallow;
+ }
+
+ public List performList(
+ QueryParameters queryParameters,
+ SessionImplementor session) throws HibernateException {
+ if ( log.isTraceEnabled() ) {
+ log.trace( "find: " + getSourceQuery() );
+ queryParameters.traceParameters( session.getFactory() );
+ }
+ boolean hasLimit = queryParameters.getRowSelection() != null &&
+ queryParameters.getRowSelection().definesLimits();
+ boolean needsLimit = hasLimit && translators.length > 1;
+ QueryParameters queryParametersToUse;
+ if ( needsLimit ) {
+ log.warn( "firstResult/maxResults specified on polymorphic query; applying in memory!" );
+ RowSelection selection = new RowSelection();
+ selection.setFetchSize( queryParameters.getRowSelection().getFetchSize() );
+ selection.setTimeout( queryParameters.getRowSelection().getTimeout() );
+ queryParametersToUse = queryParameters.createCopyUsing( selection );
+ }
+ else {
+ queryParametersToUse = queryParameters;
+ }
+
+ List combinedResults = new ArrayList();
+ IdentitySet distinction = new IdentitySet();
+ int includedCount = -1;
+ translator_loop: for ( int i = 0; i < translators.length; i++ ) {
+ List tmp = translators[i].list( session, queryParametersToUse );
+ if ( needsLimit ) {
+ // NOTE : firstRow is zero-based
+ int first = queryParameters.getRowSelection().getFirstRow() == null
+ ? 0
+ : queryParameters.getRowSelection().getFirstRow().intValue();
+ int max = queryParameters.getRowSelection().getMaxRows() == null
+ ? -1
+ : queryParameters.getRowSelection().getMaxRows().intValue();
+ final int size = tmp.size();
+ for ( int x = 0; x < size; x++ ) {
+ final Object result = tmp.get( x );
+ if ( distinction.add( result ) ) {
+ continue;
+ }
+ includedCount++;
+ if ( includedCount < first ) {
+ continue;
+ }
+ combinedResults.add( result );
+ if ( max >= 0 && includedCount > max ) {
+ // break the outer loop !!!
+ break translator_loop;
+ }
+ }
+ }
+ else {
+ combinedResults.addAll( tmp );
+ }
+ }
+ return combinedResults;
+ }
+
+ public Iterator performIterate(
+ QueryParameters queryParameters,
+ EventSource session) throws HibernateException {
+ if ( log.isTraceEnabled() ) {
+ log.trace( "iterate: " + getSourceQuery() );
+ queryParameters.traceParameters( session.getFactory() );
+ }
+ if ( translators.length == 0 ) {
+ return EmptyIterator.INSTANCE;
+ }
+
+ Iterator[] results = null;
+ boolean many = translators.length > 1;
+ if (many) {
+ results = new Iterator[translators.length];
+ }
+
+ Iterator result = null;
+ for ( int i = 0; i < translators.length; i++ ) {
+ result = translators[i].iterate( queryParameters, session );
+ if (many) results[i] = result;
+ }
+
+ return many ? new JoinedIterator(results) : result;
+ }
+
+ public ScrollableResults performScroll(
+ QueryParameters queryParameters,
+ SessionImplementor session) throws HibernateException {
+ if ( log.isTraceEnabled() ) {
+ log.trace( "iterate: " + getSourceQuery() );
+ queryParameters.traceParameters( session.getFactory() );
+ }
+ if ( translators.length != 1 ) {
+ throw new QueryException( "implicit polymorphism not supported for scroll() queries" );
+ }
+ if ( queryParameters.getRowSelection().definesLimits() && translators[0].containsCollectionFetches() ) {
+ throw new QueryException( "firstResult/maxResults not supported in conjunction with scroll() of a query containing collection fetches" );
+ }
+
+ return translators[0].scroll( queryParameters, session );
+ }
+
+ public int performExecuteUpdate(QueryParameters queryParameters, SessionImplementor session)
+ throws HibernateException {
+ if ( log.isTraceEnabled() ) {
+ log.trace( "executeUpdate: " + getSourceQuery() );
+ queryParameters.traceParameters( session.getFactory() );
+ }
+ if ( translators.length != 1 ) {
+ log.warn( "manipulation query [" + getSourceQuery() + "] resulted in [" + translators.length + "] split queries" );
+ }
+ int result = 0;
+ for ( int i = 0; i < translators.length; i++ ) {
+ result += translators[i].executeUpdate( queryParameters, session );
+ }
+ return result;
+ }
+
+ private ParameterMetadata buildParameterMetadata(ParameterTranslations parameterTranslations, String hql) {
+ long start = System.currentTimeMillis();
+ ParamLocationRecognizer recognizer = ParamLocationRecognizer.parseLocations( hql );
+ long end = System.currentTimeMillis();
+ if ( log.isTraceEnabled() ) {
+ log.trace( "HQL param location recognition took " + (end - start) + " mills (" + hql + ")" );
+ }
+
+ int ordinalParamCount = parameterTranslations.getOrdinalParameterCount();
+ int[] locations = ArrayHelper.toIntArray( recognizer.getOrdinalParameterLocationList() );
+ if ( parameterTranslations.supportsOrdinalParameterMetadata() && locations.length != ordinalParamCount ) {
+ throw new HibernateException( "ordinal parameter mismatch" );
+ }
+ ordinalParamCount = locations.length;
+ OrdinalParameterDescriptor[] ordinalParamDescriptors = new OrdinalParameterDescriptor[ordinalParamCount];
+ for ( int i = 1; i <= ordinalParamCount; i++ ) {
+ ordinalParamDescriptors[ i - 1 ] = new OrdinalParameterDescriptor(
+ i,
+ parameterTranslations.supportsOrdinalParameterMetadata()
+ ? parameterTranslations.getOrdinalParameterExpectedType( i )
+ : null,
+ locations[ i - 1 ]
+ );
+ }
+
+ Iterator itr = recognizer.getNamedParameterDescriptionMap().entrySet().iterator();
+ Map namedParamDescriptorMap = new HashMap();
+ while( itr.hasNext() ) {
+ final Map.Entry entry = ( Map.Entry ) itr.next();
+ final String name = ( String ) entry.getKey();
+ final ParamLocationRecognizer.NamedParameterDescription description =
+ ( ParamLocationRecognizer.NamedParameterDescription ) entry.getValue();
+ namedParamDescriptorMap.put(
+ name,
+ new NamedParameterDescriptor(
+ name,
+ parameterTranslations.getNamedParameterExpectedType( name ),
+ description.buildPositionsArray(),
+ description.isJpaStyle()
+ )
+ );
+ }
+
+ return new ParameterMetadata( ordinalParamDescriptors, namedParamDescriptorMap );
+ }
+
+ public QueryTranslator[] getTranslators() {
+ QueryTranslator[] copy = new QueryTranslator[translators.length];
+ System.arraycopy(translators, 0, copy, 0, copy.length);
+ return copy;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/NamedParameterDescriptor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/NamedParameterDescriptor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/NamedParameterDescriptor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,40 @@
+package org.hibernate.engine.query;
+
+import org.hibernate.type.Type;
+
+import java.io.Serializable;
+
+/**
+ * Descriptor regarding a named parameter.
+ *
+ * @author Steve Ebersole
+ */
+public class NamedParameterDescriptor implements Serializable {
+ private final String name;
+ private final Type expectedType;
+ private final int[] sourceLocations;
+ private final boolean jpaStyle;
+
+ public NamedParameterDescriptor(String name, Type expectedType, int[] sourceLocations, boolean jpaStyle) {
+ this.name = name;
+ this.expectedType = expectedType;
+ this.sourceLocations = sourceLocations;
+ this.jpaStyle = jpaStyle;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Type getExpectedType() {
+ return expectedType;
+ }
+
+ public int[] getSourceLocations() {
+ return sourceLocations;
+ }
+
+ public boolean isJpaStyle() {
+ return jpaStyle;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/NativeSQLQueryPlan.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/NativeSQLQueryPlan.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/NativeSQLQueryPlan.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,182 @@
+package org.hibernate.engine.query;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.HibernateException;
+import org.hibernate.QueryException;
+import org.hibernate.engine.query.sql.NativeSQLQuerySpecification;
+import org.hibernate.action.BulkOperationCleanupAction;
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.TypedValue;
+import org.hibernate.event.EventSource;
+import org.hibernate.exception.JDBCExceptionHelper;
+import org.hibernate.loader.custom.sql.SQLCustomQuery;
+import org.hibernate.type.Type;
+import org.hibernate.util.ArrayHelper;
+
+/**
+ * Defines a query execution plan for a native-SQL query.
+ *
+ * @author Steve Ebersole
+ */
+public class NativeSQLQueryPlan implements Serializable {
+ private final String sourceQuery;
+
+ private final SQLCustomQuery customQuery;
+
+ private static final Log log = LogFactory.getLog(NativeSQLQueryPlan.class);
+
+ public NativeSQLQueryPlan(
+ NativeSQLQuerySpecification specification,
+ SessionFactoryImplementor factory) {
+ this.sourceQuery = specification.getQueryString();
+
+ customQuery = new SQLCustomQuery(
+ specification.getQueryString(),
+ specification.getQueryReturns(),
+ specification.getQuerySpaces(),
+ factory );
+ }
+
+ public String getSourceQuery() {
+ return sourceQuery;
+ }
+
+ public SQLCustomQuery getCustomQuery() {
+ return customQuery;
+ }
+
+ private int[] getNamedParameterLocs(String name) throws QueryException {
+ Object loc = customQuery.getNamedParameterBindPoints().get( name );
+ if ( loc == null ) {
+ throw new QueryException(
+ "Named parameter does not appear in Query: " + name,
+ customQuery.getSQL() );
+ }
+ if ( loc instanceof Integer ) {
+ return new int[] { ((Integer) loc ).intValue() };
+ }
+ else {
+ return ArrayHelper.toIntArray( (List) loc );
+ }
+ }
+
+ /**
+ * Bind positional parameter values to the <tt>PreparedStatement</tt>
+ * (these are parameters specified by a JDBC-style ?).
+ */
+ private int bindPositionalParameters(final PreparedStatement st,
+ final QueryParameters queryParameters, final int start,
+ final SessionImplementor session) throws SQLException,
+ HibernateException {
+
+ final Object[] values = queryParameters
+ .getFilteredPositionalParameterValues();
+ final Type[] types = queryParameters
+ .getFilteredPositionalParameterTypes();
+ int span = 0;
+ for (int i = 0; i < values.length; i++) {
+ types[i].nullSafeSet( st, values[i], start + span, session );
+ span += types[i].getColumnSpan( session.getFactory() );
+ }
+ return span;
+ }
+
+ /**
+ * Bind named parameters to the <tt>PreparedStatement</tt>. This has an
+ * empty implementation on this superclass and should be implemented by
+ * subclasses (queries) which allow named parameters.
+ */
+ private int bindNamedParameters(final PreparedStatement ps,
+ final Map namedParams, final int start,
+ final SessionImplementor session) throws SQLException,
+ HibernateException {
+
+ if ( namedParams != null ) {
+ // assumes that types are all of span 1
+ Iterator iter = namedParams.entrySet().iterator();
+ int result = 0;
+ while ( iter.hasNext() ) {
+ Map.Entry e = (Map.Entry) iter.next();
+ String name = (String) e.getKey();
+ TypedValue typedval = (TypedValue) e.getValue();
+ int[] locs = getNamedParameterLocs( name );
+ for (int i = 0; i < locs.length; i++) {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "bindNamedParameters() "
+ + typedval.getValue() + " -> " + name + " ["
+ + (locs[i] + start ) + "]" );
+ }
+ typedval.getType().nullSafeSet( ps, typedval.getValue(),
+ locs[i] + start, session );
+ }
+ result += locs.length;
+ }
+ return result;
+ }
+ else {
+ return 0;
+ }
+ }
+
+ protected void coordinateSharedCacheCleanup(SessionImplementor session) {
+ BulkOperationCleanupAction action = new BulkOperationCleanupAction( session, getCustomQuery().getQuerySpaces() );
+
+ action.init();
+
+ if ( session.isEventSource() ) {
+ ( ( EventSource ) session ).getActionQueue().addAction( action );
+ }
+ }
+
+ public int performExecuteUpdate(QueryParameters queryParameters,
+ SessionImplementor session) throws HibernateException {
+
+ coordinateSharedCacheCleanup( session );
+
+ if(queryParameters.isCallable()) {
+ throw new IllegalArgumentException("callable not yet supported for native queries");
+ }
+
+ int result = 0;
+ PreparedStatement ps;
+ try {
+ queryParameters.processFilters( this.customQuery.getSQL(),
+ session );
+ String sql = queryParameters.getFilteredSQL();
+
+ ps = session.getBatcher().prepareStatement( sql );
+
+ try {
+ int col = 1;
+ col += bindPositionalParameters( ps, queryParameters, col,
+ session );
+ col += bindNamedParameters( ps, queryParameters
+ .getNamedParameters(), col, session );
+ result = ps.executeUpdate();
+ }
+ finally {
+ if ( ps != null ) {
+ session.getBatcher().closeStatement( ps );
+ }
+ }
+ }
+ catch (SQLException sqle) {
+ throw JDBCExceptionHelper.convert( session.getFactory()
+ .getSQLExceptionConverter(), sqle,
+ "could not execute native bulk manipulation query", this.sourceQuery );
+ }
+
+ return result;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/OrdinalParameterDescriptor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/OrdinalParameterDescriptor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/OrdinalParameterDescriptor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,32 @@
+package org.hibernate.engine.query;
+
+import org.hibernate.type.Type;
+
+import java.io.Serializable;
+
+/**
+ * @author <a href="mailto:steve at hibernate.org">Steve Ebersole </a>
+ */
+public class OrdinalParameterDescriptor implements Serializable {
+ private final int ordinalPosition;
+ private final Type expectedType;
+ private final int sourceLocation;
+
+ public OrdinalParameterDescriptor(int ordinalPosition, Type expectedType, int sourceLocation) {
+ this.ordinalPosition = ordinalPosition;
+ this.expectedType = expectedType;
+ this.sourceLocation = sourceLocation;
+ }
+
+ public int getOrdinalPosition() {
+ return ordinalPosition;
+ }
+
+ public Type getExpectedType() {
+ return expectedType;
+ }
+
+ public int getSourceLocation() {
+ return sourceLocation;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/ParamLocationRecognizer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/ParamLocationRecognizer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/ParamLocationRecognizer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,108 @@
+package org.hibernate.engine.query;
+
+import org.hibernate.util.ArrayHelper;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * Implements a parameter parser recognizer specifically for the purpose
+ * of journaling parameter locations.
+ *
+ * @author Steve Ebersole
+ */
+public class ParamLocationRecognizer implements ParameterParser.Recognizer {
+
+ public static class NamedParameterDescription {
+ private final boolean jpaStyle;
+ private final List positions = new ArrayList();
+
+ public NamedParameterDescription(boolean jpaStyle) {
+ this.jpaStyle = jpaStyle;
+ }
+
+ public boolean isJpaStyle() {
+ return jpaStyle;
+ }
+
+ private void add(int position) {
+ positions.add( new Integer( position ) );
+ }
+
+ public int[] buildPositionsArray() {
+ return ArrayHelper.toIntArray( positions );
+ }
+ }
+
+ private Map namedParameterDescriptions = new HashMap();
+ private List ordinalParameterLocationList = new ArrayList();
+
+ /**
+ * Convenience method for creating a param location recognizer and
+ * initiating the parse.
+ *
+ * @param query The query to be parsed for parameter locations.
+ * @return The generated recognizer, with journaled location info.
+ */
+ public static ParamLocationRecognizer parseLocations(String query) {
+ ParamLocationRecognizer recognizer = new ParamLocationRecognizer();
+ ParameterParser.parse( query, recognizer );
+ return recognizer;
+ }
+
+ /**
+ * Returns the map of named parameter locations. The map is keyed by
+ * parameter name; the corresponding value is a (@link NamedParameterDescription}.
+ *
+ * @return The map of named parameter locations.
+ */
+ public Map getNamedParameterDescriptionMap() {
+ return namedParameterDescriptions;
+ }
+
+ /**
+ * Returns the list of ordinal parameter locations. The list elements
+ * are Integers, representing the location for that given ordinal. Thus
+ * {@link #getOrdinalParameterLocationList()}.elementAt(n) represents the
+ * location for the nth parameter.
+ *
+ * @return The list of ordinal parameter locations.
+ */
+ public List getOrdinalParameterLocationList() {
+ return ordinalParameterLocationList;
+ }
+
+
+ // Recognition code ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public void ordinalParameter(int position) {
+ ordinalParameterLocationList.add( new Integer( position ) );
+ }
+
+ public void namedParameter(String name, int position) {
+ getOrBuildNamedParameterDescription( name, false ).add( position );
+ }
+
+ public void jpaPositionalParameter(String name, int position) {
+ getOrBuildNamedParameterDescription( name, true ).add( position );
+ }
+
+ private NamedParameterDescription getOrBuildNamedParameterDescription(String name, boolean jpa) {
+ NamedParameterDescription desc = ( NamedParameterDescription ) namedParameterDescriptions.get( name );
+ if ( desc == null ) {
+ desc = new NamedParameterDescription( jpa );
+ namedParameterDescriptions.put( name, desc );
+ }
+ return desc;
+ }
+
+ public void other(char character) {
+ // don't care...
+ }
+
+ public void outParameter(int position) {
+ // don't care...
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/ParameterMetadata.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/ParameterMetadata.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/ParameterMetadata.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,88 @@
+package org.hibernate.engine.query;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.hibernate.QueryParameterException;
+import org.hibernate.type.Type;
+
+/**
+ * Encapsulates metadata about parameters encountered within a query.
+ *
+ * @author <a href="mailto:steve at hibernate.org">Steve Ebersole </a>
+ */
+public class ParameterMetadata implements Serializable {
+
+ private static final OrdinalParameterDescriptor[] EMPTY_ORDINALS = new OrdinalParameterDescriptor[0];
+
+ private final OrdinalParameterDescriptor[] ordinalDescriptors;
+ private final Map namedDescriptorMap;
+
+ /**
+ * Instantiates a ParameterMetadata container.
+ *
+ * @param ordinalDescriptors
+ * @param namedDescriptorMap
+ */
+ public ParameterMetadata(OrdinalParameterDescriptor[] ordinalDescriptors, Map namedDescriptorMap) {
+ if ( ordinalDescriptors == null ) {
+ this.ordinalDescriptors = EMPTY_ORDINALS;
+ }
+ else {
+ OrdinalParameterDescriptor[] copy = new OrdinalParameterDescriptor[ ordinalDescriptors.length ];
+ System.arraycopy( ordinalDescriptors, 0, copy, 0, ordinalDescriptors.length );
+ this.ordinalDescriptors = copy;
+ }
+ if ( namedDescriptorMap == null ) {
+ this.namedDescriptorMap = java.util.Collections.EMPTY_MAP;
+ }
+ else {
+ int size = ( int ) ( ( namedDescriptorMap.size() / .75 ) + 1 );
+ Map copy = new HashMap( size );
+ copy.putAll( namedDescriptorMap );
+ this.namedDescriptorMap = java.util.Collections.unmodifiableMap( copy );
+ }
+ }
+
+ public int getOrdinalParameterCount() {
+ return ordinalDescriptors.length;
+ }
+
+ public OrdinalParameterDescriptor getOrdinalParameterDescriptor(int position) {
+ if ( position < 1 || position > ordinalDescriptors.length ) {
+ throw new IndexOutOfBoundsException( "Remember that ordinal parameters are 1-based!" );
+ }
+ return ordinalDescriptors[position - 1];
+ }
+
+ public Type getOrdinalParameterExpectedType(int position) {
+ return getOrdinalParameterDescriptor( position ).getExpectedType();
+ }
+
+ public int getOrdinalParameterSourceLocation(int position) {
+ return getOrdinalParameterDescriptor( position ).getSourceLocation();
+ }
+
+ public Set getNamedParameterNames() {
+ return namedDescriptorMap.keySet();
+ }
+
+ public NamedParameterDescriptor getNamedParameterDescriptor(String name) {
+ NamedParameterDescriptor meta = ( NamedParameterDescriptor ) namedDescriptorMap.get( name );
+ if ( meta == null ) {
+ throw new QueryParameterException( "could not locate named parameter [" + name + "]" );
+ }
+ return meta;
+ }
+
+ public Type getNamedParameterExpectedType(String name) {
+ return getNamedParameterDescriptor( name ).getExpectedType();
+ }
+
+ public int[] getNamedParameterSourceLocations(String name) {
+ return getNamedParameterDescriptor( name ).getSourceLocations();
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/ParameterParser.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/ParameterParser.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/ParameterParser.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,108 @@
+package org.hibernate.engine.query;
+
+import org.hibernate.QueryException;
+import org.hibernate.hql.classic.ParserHelper;
+import org.hibernate.util.StringHelper;
+
+/**
+ * The single available method {@link #parse} is responsible for parsing a
+ * query string and recognizing tokens in relation to parameters (either
+ * named, JPA-style, or ordinal) and providing callbacks about such
+ * recognitions.
+ *
+ * @author <a href="mailto:steve at hibernate.org">Steve Ebersole </a>
+ */
+public class ParameterParser {
+
+ public static interface Recognizer {
+ public void outParameter(int position);
+ public void ordinalParameter(int position);
+ public void namedParameter(String name, int position);
+ public void jpaPositionalParameter(String name, int position);
+ public void other(char character);
+ }
+
+ private ParameterParser() {
+ // disallow instantiation
+ }
+
+ /**
+ * Performs the actual parsing and tokenizing of the query string making appropriate
+ * callbacks to the given recognizer upon recognition of the various tokens.
+ * <p/>
+ * Note that currently, this only knows how to deal with a single output
+ * parameter (for callable statements). If we later add support for
+ * multiple output params, this, obviously, needs to change.
+ *
+ * @param sqlString The string to be parsed/tokenized.
+ * @param recognizer The thing which handles recognition events.
+ * @throws QueryException
+ */
+ public static void parse(String sqlString, Recognizer recognizer) throws QueryException {
+ boolean hasMainOutputParameter = sqlString.indexOf( "call" ) > 0 &&
+ sqlString.indexOf( "?" ) < sqlString.indexOf( "call" ) &&
+ sqlString.indexOf( "=" ) < sqlString.indexOf( "call" );
+ boolean foundMainOutputParam = false;
+
+ int stringLength = sqlString.length();
+ boolean inQuote = false;
+ for ( int indx = 0; indx < stringLength; indx++ ) {
+ char c = sqlString.charAt( indx );
+ if ( inQuote ) {
+ if ( '\'' == c ) {
+ inQuote = false;
+ }
+ recognizer.other( c );
+ }
+ else if ( '\'' == c ) {
+ inQuote = true;
+ recognizer.other( c );
+ }
+ else {
+ if ( c == ':' ) {
+ // named parameter
+ int right = StringHelper.firstIndexOfChar( sqlString, ParserHelper.HQL_SEPARATORS, indx + 1 );
+ int chopLocation = right < 0 ? sqlString.length() : right;
+ String param = sqlString.substring( indx + 1, chopLocation );
+ if ( StringHelper.isEmpty( param ) ) {
+ throw new QueryException("Space is not allowed after parameter prefix ':' '"
+ + sqlString + "'");
+ }
+ recognizer.namedParameter( param, indx );
+ indx = chopLocation - 1;
+ }
+ else if ( c == '?' ) {
+ // could be either an ordinal or JPA-positional parameter
+ if ( indx < stringLength - 1 && Character.isDigit( sqlString.charAt( indx + 1 ) ) ) {
+ // a peek ahead showed this as an JPA-positional parameter
+ int right = StringHelper.firstIndexOfChar( sqlString, ParserHelper.HQL_SEPARATORS, indx + 1 );
+ int chopLocation = right < 0 ? sqlString.length() : right;
+ String param = sqlString.substring( indx + 1, chopLocation );
+ // make sure this "name" is an integral
+ try {
+ new Integer( param );
+ }
+ catch( NumberFormatException e ) {
+ throw new QueryException( "JPA-style positional param was not an integral ordinal" );
+ }
+ recognizer.jpaPositionalParameter( param, indx );
+ indx = chopLocation - 1;
+ }
+ else {
+ if ( hasMainOutputParameter && !foundMainOutputParam ) {
+ foundMainOutputParam = true;
+ recognizer.outParameter( indx );
+ }
+ else {
+ recognizer.ordinalParameter( indx );
+ }
+ }
+ }
+ else {
+ recognizer.other( c );
+ }
+ }
+ }
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/QueryMetadata.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/QueryMetadata.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/QueryMetadata.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,72 @@
+package org.hibernate.engine.query;
+
+import org.hibernate.type.Type;
+
+import java.io.Serializable;
+import java.util.Set;
+
+/**
+ * Defines metadata regarding a translated HQL or native-SQL query.
+ *
+ * @author <a href="mailto:steve at hibernate.org">Steve Ebersole </a>
+ */
+public class QueryMetadata implements Serializable {
+ private final String sourceQuery;
+ private final ParameterMetadata parameterMetadata;
+ private final String[] returnAliases;
+ private final Type[] returnTypes;
+ private final Set querySpaces;
+
+ public QueryMetadata(
+ String sourceQuery,
+ ParameterMetadata parameterMetadata,
+ String[] returnAliases,
+ Type[] returnTypes,
+ Set querySpaces) {
+ this.sourceQuery = sourceQuery;
+ this.parameterMetadata = parameterMetadata;
+ this.returnAliases = returnAliases;
+ this.returnTypes = returnTypes;
+ this.querySpaces = querySpaces;
+ }
+
+ /**
+ * Get the source HQL or native-SQL query.
+ *
+ * @return The source query.
+ */
+ public String getSourceQuery() {
+ return sourceQuery;
+ }
+
+ public ParameterMetadata getParameterMetadata() {
+ return parameterMetadata;
+ }
+
+ /**
+ * Return source query select clause aliases (if any)
+ *
+ * @return an array of aliases as strings.
+ */
+ public String[] getReturnAliases() {
+ return returnAliases;
+ }
+
+ /**
+ * An array of types describing the returns of the source query.
+ *
+ * @return The return type array.
+ */
+ public Type[] getReturnTypes() {
+ return returnTypes;
+ }
+
+ /**
+ * The set of query spaces affected by this source query.
+ *
+ * @return The set of query spaces.
+ */
+ public Set getQuerySpaces() {
+ return querySpaces;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/QueryPlanCache.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/QueryPlanCache.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/QueryPlanCache.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,263 @@
+package org.hibernate.engine.query;
+
+import org.hibernate.util.ArrayHelper;
+import org.hibernate.util.SimpleMRUCache;
+import org.hibernate.util.SoftLimitMRUCache;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.query.sql.NativeSQLQuerySpecification;
+import org.hibernate.QueryException;
+import org.hibernate.MappingException;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.Serializable;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Collections;
+
+/**
+ * Acts as a cache for compiled query plans, as well as query-parameter metadata.
+ *
+ * @author <a href="mailto:steve at hibernate.org">Steve Ebersole </a>
+ */
+public class QueryPlanCache implements Serializable {
+
+ private static final Log log = LogFactory.getLog( QueryPlanCache.class );
+
+ private SessionFactoryImplementor factory;
+
+ public QueryPlanCache(SessionFactoryImplementor factory) {
+ this.factory = factory;
+ }
+
+ // simple cache of param metadata based on query string. Ideally, the
+ // original "user-supplied query" string should be used to retreive this
+ // metadata (i.e., not the para-list-expanded query string) to avoid
+ // unnecessary cache entries.
+ // Used solely for caching param metadata for native-sql queries, see
+ // getSQLParameterMetadata() for a discussion as to why...
+ private final SimpleMRUCache sqlParamMetadataCache = new SimpleMRUCache();
+
+ // the cache of the actual plans...
+ private final SoftLimitMRUCache planCache = new SoftLimitMRUCache( 128 );
+
+
+ public ParameterMetadata getSQLParameterMetadata(String query) {
+ ParameterMetadata metadata = ( ParameterMetadata ) sqlParamMetadataCache.get( query );
+ if ( metadata == null ) {
+ // for native-sql queries, the param metadata is determined outside
+ // any relation to a query plan, because query plan creation and/or
+ // retreival for a native-sql query depends on all of the return
+ // types having been set, which might not be the case up-front when
+ // param metadata would be most useful
+ metadata = buildNativeSQLParameterMetadata( query );
+ sqlParamMetadataCache.put( query, metadata );
+ }
+ return metadata;
+ }
+
+ public HQLQueryPlan getHQLQueryPlan(String queryString, boolean shallow, Map enabledFilters)
+ throws QueryException, MappingException {
+ HQLQueryPlanKey key = new HQLQueryPlanKey( queryString, shallow, enabledFilters );
+ HQLQueryPlan plan = ( HQLQueryPlan ) planCache.get ( key );
+
+ if ( plan == null ) {
+ if ( log.isTraceEnabled() ) {
+ log.trace( "unable to locate HQL query plan in cache; generating (" + queryString + ")" );
+ }
+ plan = new HQLQueryPlan(queryString, shallow, enabledFilters, factory );
+ }
+ else {
+ if ( log.isTraceEnabled() ) {
+ log.trace( "located HQL query plan in cache (" + queryString + ")" );
+ }
+ }
+
+ planCache.put( key, plan );
+
+ return plan;
+ }
+
+ public FilterQueryPlan getFilterQueryPlan(String filterString, String collectionRole, boolean shallow, Map enabledFilters)
+ throws QueryException, MappingException {
+ FilterQueryPlanKey key = new FilterQueryPlanKey( filterString, collectionRole, shallow, enabledFilters );
+ FilterQueryPlan plan = ( FilterQueryPlan ) planCache.get ( key );
+
+ if ( plan == null ) {
+ if ( log.isTraceEnabled() ) {
+ log.trace( "unable to locate collection-filter query plan in cache; generating (" + collectionRole + " : " + filterString + ")" );
+ }
+ plan = new FilterQueryPlan( filterString, collectionRole, shallow, enabledFilters, factory );
+ }
+ else {
+ if ( log.isTraceEnabled() ) {
+ log.trace( "located collection-filter query plan in cache (" + collectionRole + " : " + filterString + ")" );
+ }
+ }
+
+ planCache.put( key, plan );
+
+ return plan;
+ }
+
+ public NativeSQLQueryPlan getNativeSQLQueryPlan(NativeSQLQuerySpecification spec) {
+ NativeSQLQueryPlan plan = ( NativeSQLQueryPlan ) planCache.get( spec );
+
+ if ( plan == null ) {
+ if ( log.isTraceEnabled() ) {
+ log.trace( "unable to locate native-sql query plan in cache; generating (" + spec.getQueryString() + ")" );
+ }
+ plan = new NativeSQLQueryPlan( spec, factory );
+ }
+ else {
+ if ( log.isTraceEnabled() ) {
+ log.trace( "located native-sql query plan in cache (" + spec.getQueryString() + ")" );
+ }
+ }
+
+ planCache.put( spec, plan );
+ return plan;
+ }
+
+ private ParameterMetadata buildNativeSQLParameterMetadata(String sqlString) {
+ ParamLocationRecognizer recognizer = ParamLocationRecognizer.parseLocations( sqlString );
+
+ OrdinalParameterDescriptor[] ordinalDescriptors =
+ new OrdinalParameterDescriptor[ recognizer.getOrdinalParameterLocationList().size() ];
+ for ( int i = 0; i < recognizer.getOrdinalParameterLocationList().size(); i++ ) {
+ final Integer position = ( Integer ) recognizer.getOrdinalParameterLocationList().get( i );
+ ordinalDescriptors[i] = new OrdinalParameterDescriptor( i, null, position.intValue() );
+ }
+
+ Iterator itr = recognizer.getNamedParameterDescriptionMap().entrySet().iterator();
+ Map namedParamDescriptorMap = new HashMap();
+ while( itr.hasNext() ) {
+ final Map.Entry entry = ( Map.Entry ) itr.next();
+ final String name = ( String ) entry.getKey();
+ final ParamLocationRecognizer.NamedParameterDescription description =
+ ( ParamLocationRecognizer.NamedParameterDescription ) entry.getValue();
+ namedParamDescriptorMap.put(
+ name ,
+ new NamedParameterDescriptor( name, null, description.buildPositionsArray(), description.isJpaStyle() )
+ );
+ }
+
+ return new ParameterMetadata( ordinalDescriptors, namedParamDescriptorMap );
+ }
+
+ private static class HQLQueryPlanKey implements Serializable {
+ private final String query;
+ private final boolean shallow;
+ private final Set filterNames;
+ private final int hashCode;
+
+ public HQLQueryPlanKey(String query, boolean shallow, Map enabledFilters) {
+ this.query = query;
+ this.shallow = shallow;
+
+ if ( enabledFilters == null || enabledFilters.isEmpty() ) {
+ filterNames = Collections.EMPTY_SET;
+ }
+ else {
+ Set tmp = new HashSet();
+ tmp.addAll( enabledFilters.keySet() );
+ this.filterNames = Collections.unmodifiableSet( tmp );
+ }
+
+ int hash = query.hashCode();
+ hash = 29 * hash + ( shallow ? 1 : 0 );
+ hash = 29 * hash + filterNames.hashCode();
+ this.hashCode = hash;
+ }
+
+ public boolean equals(Object o) {
+ if ( this == o ) {
+ return true;
+ }
+ if ( o == null || getClass() != o.getClass() ) {
+ return false;
+ }
+
+ final HQLQueryPlanKey that = ( HQLQueryPlanKey ) o;
+
+ if ( shallow != that.shallow ) {
+ return false;
+ }
+ if ( !filterNames.equals( that.filterNames ) ) {
+ return false;
+ }
+ if ( !query.equals( that.query ) ) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public int hashCode() {
+ return hashCode;
+ }
+ }
+
+ private static class FilterQueryPlanKey implements Serializable {
+ private final String query;
+ private final String collectionRole;
+ private final boolean shallow;
+ private final Set filterNames;
+ private final int hashCode;
+
+ public FilterQueryPlanKey(String query, String collectionRole, boolean shallow, Map enabledFilters) {
+ this.query = query;
+ this.collectionRole = collectionRole;
+ this.shallow = shallow;
+
+ if ( enabledFilters == null || enabledFilters.isEmpty() ) {
+ filterNames = Collections.EMPTY_SET;
+ }
+ else {
+ Set tmp = new HashSet();
+ tmp.addAll( enabledFilters.keySet() );
+ this.filterNames = Collections.unmodifiableSet( tmp );
+ }
+
+ int hash = query.hashCode();
+ hash = 29 * hash + collectionRole.hashCode();
+ hash = 29 * hash + ( shallow ? 1 : 0 );
+ hash = 29 * hash + filterNames.hashCode();
+ this.hashCode = hash;
+ }
+
+ public boolean equals(Object o) {
+ if ( this == o ) {
+ return true;
+ }
+ if ( o == null || getClass() != o.getClass() ) {
+ return false;
+ }
+
+ final FilterQueryPlanKey that = ( FilterQueryPlanKey ) o;
+
+ if ( shallow != that.shallow ) {
+ return false;
+ }
+ if ( !filterNames.equals( that.filterNames ) ) {
+ return false;
+ }
+ if ( !query.equals( that.query ) ) {
+ return false;
+ }
+ if ( !collectionRole.equals( that.collectionRole ) ) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public int hashCode() {
+ return hashCode;
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/ReturnMetadata.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/ReturnMetadata.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/ReturnMetadata.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,26 @@
+package org.hibernate.engine.query;
+
+import org.hibernate.type.Type;
+
+import java.io.Serializable;
+
+/**
+ * @author <a href="mailto:steve at hibernate.org">Steve Ebersole </a>
+ */
+public class ReturnMetadata implements Serializable {
+ private final String[] returnAliases;
+ private final Type[] returnTypes;
+
+ public ReturnMetadata(String[] returnAliases, Type[] returnTypes) {
+ this.returnAliases = returnAliases;
+ this.returnTypes = returnTypes;
+ }
+
+ public String[] getReturnAliases() {
+ return returnAliases;
+ }
+
+ public Type[] getReturnTypes() {
+ return returnTypes;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/sql/NativeSQLQueryCollectionReturn.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/sql/NativeSQLQueryCollectionReturn.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/sql/NativeSQLQueryCollectionReturn.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,60 @@
+// $Id: NativeSQLQueryCollectionReturn.java 7232 2005-06-19 17:16:40 -0500 (Sun, 19 Jun 2005) maxcsaucdk $
+package org.hibernate.engine.query.sql;
+
+import java.util.Map;
+
+import org.hibernate.LockMode;
+
+/**
+ * Represents a return defined as part of a native sql query which
+ * names a collection role in the form {classname}.{collectionrole}; it
+ * is used in defining a custom sql query for loading an entity's
+ * collection in non-fetching scenarios (i.e., loading the collection
+ * itself as the "root" of the result).
+ *
+ * @author Steve Ebersole
+ */
+public class NativeSQLQueryCollectionReturn extends NativeSQLQueryNonScalarReturn {
+ private String ownerEntityName;
+ private String ownerProperty;
+
+ /**
+ * Construct a native-sql return representing a collection initializer
+ *
+ * @param alias The result alias
+ * @param ownerEntityName The entity-name of the entity owning the collection
+ * to be initialized.
+ * @param ownerProperty The property name (on the owner) which represents
+ * the collection to be initialized.
+ * @param propertyResults Any user-supplied column->property mappings
+ * @param lockMode The lock mode to apply to the collection.
+ */
+ public NativeSQLQueryCollectionReturn(
+ String alias,
+ String ownerEntityName,
+ String ownerProperty,
+ Map propertyResults,
+ LockMode lockMode) {
+ super( alias, propertyResults, lockMode );
+ this.ownerEntityName = ownerEntityName;
+ this.ownerProperty = ownerProperty;
+ }
+
+ /**
+ * Returns the class owning the collection.
+ *
+ * @return The class owning the collection.
+ */
+ public String getOwnerEntityName() {
+ return ownerEntityName;
+ }
+
+ /**
+ * Returns the name of the property representing the collection from the {@link #getOwnerEntityName}.
+ *
+ * @return The name of the property representing the collection on the owner class.
+ */
+ public String getOwnerProperty() {
+ return ownerProperty;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/sql/NativeSQLQueryJoinReturn.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/sql/NativeSQLQueryJoinReturn.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/sql/NativeSQLQueryJoinReturn.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,56 @@
+// $Id: NativeSQLQueryJoinReturn.java 7232 2005-06-19 17:16:40 -0500 (Sun, 19 Jun 2005) maxcsaucdk $
+package org.hibernate.engine.query.sql;
+
+import java.util.Map;
+
+import org.hibernate.LockMode;
+
+/**
+ * Represents a return defined as part of a native sql query which
+ * names a fetched role.
+ *
+ * @author Steve Ebersole
+ */
+public class NativeSQLQueryJoinReturn extends NativeSQLQueryNonScalarReturn {
+ private String ownerAlias;
+ private String ownerProperty;
+
+ /**
+ * Construct a return descriptor representing some form of fetch.
+ *
+ * @param alias The result alias
+ * @param ownerAlias The owner's result alias
+ * @param ownerProperty The owner's property representing the thing to be fetched
+ * @param propertyResults Any user-supplied column->property mappings
+ * @param lockMode The lock mode to apply
+ */
+ public NativeSQLQueryJoinReturn(
+ String alias,
+ String ownerAlias,
+ String ownerProperty,
+ Map propertyResults,
+ LockMode lockMode) {
+ super( alias, propertyResults, lockMode );
+ this.ownerAlias = ownerAlias;
+ this.ownerProperty = ownerProperty;
+ }
+
+ /**
+ * Retrieve the alias of the owner of this fetched association.
+ *
+ * @return The owner's alias.
+ */
+ public String getOwnerAlias() {
+ return ownerAlias;
+ }
+
+ /**
+ * Retrieve the property name (relative to the owner) which maps to
+ * the association to be fetched.
+ *
+ * @return The property name.
+ */
+ public String getOwnerProperty() {
+ return ownerProperty;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/sql/NativeSQLQueryNonScalarReturn.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/sql/NativeSQLQueryNonScalarReturn.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/sql/NativeSQLQueryNonScalarReturn.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,67 @@
+// $Id: NativeSQLQueryNonScalarReturn.java 7232 2005-06-19 17:16:40 -0500 (Sun, 19 Jun 2005) maxcsaucdk $
+package org.hibernate.engine.query.sql;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.hibernate.HibernateException;
+import org.hibernate.LockMode;
+
+/**
+ * Represents the base information for a non-scalar return defined as part of
+ * a native sql query.
+ *
+ * @author Steve Ebersole
+ */
+public abstract class NativeSQLQueryNonScalarReturn implements NativeSQLQueryReturn, Serializable {
+ private final String alias;
+ private final LockMode lockMode;
+ private final Map propertyResults = new HashMap();
+
+ /**
+ * Constructs some form of non-scalar return descriptor
+ *
+ * @param alias The result alias
+ * @param propertyResults Any user-supplied column->property mappings
+ * @param lockMode The lock mode to apply to the return.
+ */
+ protected NativeSQLQueryNonScalarReturn(String alias, Map propertyResults, LockMode lockMode) {
+ this.alias = alias;
+ if ( alias == null ) {
+ throw new HibernateException("alias must be specified");
+ }
+ this.lockMode = lockMode;
+ if ( propertyResults != null ) {
+ this.propertyResults.putAll( propertyResults );
+ }
+ }
+
+ /**
+ * Retrieve the defined result alias
+ *
+ * @return The result alias.
+ */
+ public String getAlias() {
+ return alias;
+ }
+
+ /**
+ * Retrieve the lock-mode to apply to this return
+ *
+ * @return The lock mode
+ */
+ public LockMode getLockMode() {
+ return lockMode;
+ }
+
+ /**
+ * Retrieve the user-supplied column->property mappings.
+ *
+ * @return The property mappings.
+ */
+ public Map getPropertyResultsMap() {
+ return Collections.unmodifiableMap( propertyResults );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/sql/NativeSQLQueryReturn.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/sql/NativeSQLQueryReturn.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/sql/NativeSQLQueryReturn.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,9 @@
+package org.hibernate.engine.query.sql;
+
+/**
+ * Describes a return in a native SQL query.
+ *
+ * @author Steve Ebersole
+ */
+public interface NativeSQLQueryReturn {
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/sql/NativeSQLQueryRootReturn.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/sql/NativeSQLQueryRootReturn.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/sql/NativeSQLQueryRootReturn.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,52 @@
+// $Id: NativeSQLQueryRootReturn.java 7232 2005-06-19 17:16:40 -0500 (Sun, 19 Jun 2005) maxcsaucdk $
+package org.hibernate.engine.query.sql;
+
+import java.util.Map;
+
+import org.hibernate.LockMode;
+
+/**
+ * Represents a return defined as part of a native sql query which
+ * names a "root" entity. A root entity means it is explicitly a
+ * "column" in the result, as opposed to a fetched relationship or role.
+ *
+ * @author Steve Ebersole
+ */
+public class NativeSQLQueryRootReturn extends NativeSQLQueryNonScalarReturn {
+ private String returnEntityName;
+
+ /**
+ * Construct a return representing an entity returned at the root
+ * of the result.
+ *
+ * @param alias The result alias
+ * @param entityName The entity name.
+ * @param lockMode The lock mode to apply
+ */
+ public NativeSQLQueryRootReturn(String alias, String entityName, LockMode lockMode) {
+ this(alias, entityName, null, lockMode);
+ }
+
+ /**
+ *
+ * @param alias The result alias
+ * @param entityName The entity name.
+ * @param propertyResults Any user-supplied column->property mappings
+ * @param lockMode The lock mode to apply
+ */
+ public NativeSQLQueryRootReturn(String alias, String entityName, Map propertyResults, LockMode lockMode) {
+ super( alias, propertyResults, lockMode );
+ this.returnEntityName = entityName;
+
+ }
+
+ /**
+ * The name of the entity to be returned.
+ *
+ * @return The entity name
+ */
+ public String getReturnEntityName() {
+ return returnEntityName;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/sql/NativeSQLQueryScalarReturn.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/sql/NativeSQLQueryScalarReturn.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/sql/NativeSQLQueryScalarReturn.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,27 @@
+package org.hibernate.engine.query.sql;
+
+import org.hibernate.type.Type;
+
+/**
+ * Describes a scalar return in a native SQL query.
+ *
+ * @author gloegl
+ */
+public class NativeSQLQueryScalarReturn implements NativeSQLQueryReturn {
+ private Type type;
+ private String columnAlias;
+
+ public NativeSQLQueryScalarReturn(String alias, Type type) {
+ this.type = type;
+ this.columnAlias = alias;
+ }
+
+ public String getColumnAlias() {
+ return columnAlias;
+ }
+
+ public Type getType() {
+ return type;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/sql/NativeSQLQuerySpecification.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/sql/NativeSQLQuerySpecification.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/query/sql/NativeSQLQuerySpecification.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,80 @@
+package org.hibernate.engine.query.sql;
+
+import org.hibernate.util.ArrayHelper;
+
+import java.util.Set;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Arrays;
+import java.util.Collections;
+
+/**
+ * Defines the specification or blue-print for a native-sql query.
+ * Essentially a simple struct containing the information needed to "translate"
+ * a native-sql query and cache that translated representation. Also used as
+ * the key by which the native-sql query plans are cached.
+ *
+ * @author Steve Ebersole
+ */
+public class NativeSQLQuerySpecification {
+ private final String queryString;
+ private final NativeSQLQueryReturn[] queryReturns;
+ private final Set querySpaces;
+ private final int hashCode;
+
+ public NativeSQLQuerySpecification(
+ String queryString,
+ NativeSQLQueryReturn[] queryReturns,
+ Collection querySpaces) {
+ this.queryString = queryString;
+ this.queryReturns = queryReturns;
+ if ( querySpaces == null ) {
+ this.querySpaces = Collections.EMPTY_SET;
+ }
+ else {
+ Set tmp = new HashSet();
+ tmp.addAll( querySpaces );
+ this.querySpaces = Collections.unmodifiableSet( tmp );
+ }
+
+ // pre-determine and cache the hashcode
+ int hashCode = queryString.hashCode();
+ hashCode = 29 * hashCode + this.querySpaces.hashCode();
+ if ( this.queryReturns != null ) {
+ hashCode = 29 * hashCode + ArrayHelper.toList( this.queryReturns ).hashCode();
+ }
+ this.hashCode = hashCode;
+ }
+
+ public String getQueryString() {
+ return queryString;
+ }
+
+ public NativeSQLQueryReturn[] getQueryReturns() {
+ return queryReturns;
+ }
+
+ public Set getQuerySpaces() {
+ return querySpaces;
+ }
+
+ public boolean equals(Object o) {
+ if ( this == o ) {
+ return true;
+ }
+ if ( o == null || getClass() != o.getClass() ) {
+ return false;
+ }
+
+ final NativeSQLQuerySpecification that = ( NativeSQLQuerySpecification ) o;
+
+ return querySpaces.equals( that.querySpaces ) &&
+ queryString.equals( that.queryString ) &&
+ Arrays.equals( queryReturns, that.queryReturns );
+ }
+
+
+ public int hashCode() {
+ return hashCode;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/transaction/IsolatedWork.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/transaction/IsolatedWork.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/transaction/IsolatedWork.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+package org.hibernate.engine.transaction;
+
+import org.hibernate.HibernateException;
+
+import java.sql.Connection;
+
+/**
+ * Represents work that needs to be performed in a manner
+ * which isolates it from any current application unit of
+ * work transaction.
+ *
+ * @author Steve Ebersole
+ */
+public interface IsolatedWork {
+ /**
+ * Perform the actual work to be done.
+ *
+ * @param connection The JDBC connection to use.
+ * @throws HibernateException
+ */
+ public void doWork(Connection connection) throws HibernateException;
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/engine/transaction/Isolater.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/engine/transaction/Isolater.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/engine/transaction/Isolater.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,230 @@
+package org.hibernate.engine.transaction;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import javax.transaction.Transaction;
+import javax.transaction.TransactionManager;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.exception.JDBCExceptionHelper;
+
+/**
+ * Class which provides the isolation semantics required by
+ * an {@link IsolatedWork}. Processing comes in two flavors:<ul>
+ * <li>{@link #doIsolatedWork} : makes sure the work to be done is
+ * performed in a seperate, distinct transaction</li>
+ * <li>{@link #doNonTransactedWork} : makes sure the work to be
+ * done is performed outside the scope of any transaction</li>
+ * </ul>
+ *
+ * @author Steve Ebersole
+ */
+public class Isolater {
+
+ private static final Log log = LogFactory.getLog( Isolater.class );
+
+ /**
+ * Ensures that all processing actually performed by the given work will
+ * occur on a seperate transaction.
+ *
+ * @param work The work to be performed.
+ * @param session The session from which this request is originating.
+ * @throws HibernateException
+ */
+ public static void doIsolatedWork(IsolatedWork work, SessionImplementor session) throws HibernateException {
+ boolean isJta = session.getFactory().getTransactionManager() != null;
+ if ( isJta ) {
+ new JtaDelegate( session ).delegateWork( work, true );
+ }
+ else {
+ new JdbcDelegate( session ).delegateWork( work, true );
+ }
+ }
+
+ /**
+ * Ensures that all processing actually performed by the given work will
+ * occur outside of a transaction.
+ *
+ * @param work The work to be performed.
+ * @param session The session from which this request is originating.
+ * @throws HibernateException
+ */
+ public static void doNonTransactedWork(IsolatedWork work, SessionImplementor session) throws HibernateException {
+ boolean isJta = session.getFactory().getTransactionManager() != null;
+ if ( isJta ) {
+ new JtaDelegate( session ).delegateWork( work, false );
+ }
+ else {
+ new JdbcDelegate( session ).delegateWork( work, false );
+ }
+ }
+
+ // should be ok performance-wise to generate new delegate instances for each
+ // request since these are locally stack-scoped. Besides, it makes the code
+ // much easier to read than the old TransactionHelper stuff...
+
+ private static interface Delegate {
+ public void delegateWork(IsolatedWork work, boolean transacted) throws HibernateException;
+ }
+
+ /**
+ * An isolation delegate for JTA-based transactions. Essentially susepnds
+ * any current transaction, does the work in a new transaction, and then
+ * resumes the initial transaction (if there was one).
+ */
+ public static class JtaDelegate implements Delegate {
+ private final SessionImplementor session;
+
+ public JtaDelegate(SessionImplementor session) {
+ this.session = session;
+ }
+
+ public void delegateWork(IsolatedWork work, boolean transacted) throws HibernateException {
+ TransactionManager transactionManager = session.getFactory().getTransactionManager();
+ Transaction surroundingTransaction = null;
+ Connection connection = null;
+ boolean caughtException = false;
+
+ try {
+ // First we need to suspend any current JTA transaction and obtain
+ // a JDBC connection
+ surroundingTransaction = transactionManager.suspend();
+ if ( log.isDebugEnabled() ) {
+ log.debug( "surrounding JTA transaction suspended [" + surroundingTransaction + "]" );
+ }
+
+ if ( transacted ) {
+ transactionManager.begin();
+ }
+
+ connection = session.getBatcher().openConnection();
+
+ // perform the actual work
+ work.doWork( connection );
+
+ // if everything went ok, commit the transaction and close the obtained
+ // connection handle...
+ session.getBatcher().closeConnection( connection );
+
+ if ( transacted ) {
+ transactionManager.commit();
+ }
+ }
+ catch( Throwable t ) {
+ // at some point the processing went bad, so we need to:
+ // 1) make sure the connection handle gets released
+ // 2) try to cleanup the JTA context as much as possible
+ caughtException = true;
+ try {
+ if ( connection != null && !connection.isClosed() ) {
+ session.getBatcher().closeConnection( connection );
+ }
+ }
+ catch( Throwable ignore ) {
+ log.trace( "unable to release connection on exception [" + ignore + "]" );
+ }
+ if ( transacted ) {
+ try {
+ transactionManager.rollback();
+ }
+ catch( Throwable ignore ) {
+ log.trace( "unable to rollback new transaction on exception [" + ignore + "]" );
+ }
+ }
+ // finally handle the exception
+ if ( t instanceof HibernateException ) {
+ throw ( HibernateException ) t;
+ }
+ else {
+ throw new HibernateException( "error performing isolated work", t );
+ }
+ }
+ finally {
+ if ( surroundingTransaction != null ) {
+ try {
+ transactionManager.resume( surroundingTransaction );
+ if ( log.isDebugEnabled() ) {
+ log.debug( "surrounding JTA transaction resumed [" + surroundingTransaction + "]" );
+ }
+ }
+ catch( Throwable t ) {
+ if ( !caughtException ) {
+ throw new HibernateException( "unable to resume previously suspended transaction", t );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * An isolation delegate for JDBC-based transactions. Basically just
+ * grabs a new connection and does the work on that.
+ */
+ public static class JdbcDelegate implements Delegate {
+ private final SessionImplementor session;
+
+ public JdbcDelegate(SessionImplementor session) {
+ this.session = session;
+ }
+
+ public void delegateWork(IsolatedWork work, boolean transacted) throws HibernateException {
+ Connection connection = null;
+ boolean wasAutoCommit = false;
+ try {
+ connection = session.getBatcher().openConnection();
+
+ if ( transacted ) {
+ if ( connection.getAutoCommit() ) {
+ wasAutoCommit = true;
+ connection.setAutoCommit( false );
+ }
+ }
+
+ work.doWork( connection );
+
+ if ( transacted ) {
+ connection.commit();
+ }
+ }
+ catch( Throwable t ) {
+ try {
+ if ( transacted && connection != null && !connection.isClosed() ) {
+ connection.rollback();
+ }
+ }
+ catch( Throwable ignore ) {
+ log.trace( "unable to release connection on exception [" + ignore + "]" );
+ }
+
+ if ( t instanceof HibernateException ) {
+ throw ( HibernateException ) t;
+ }
+ else if ( t instanceof SQLException ) {
+ throw JDBCExceptionHelper.convert(
+ session.getFactory().getSQLExceptionConverter(),
+ ( SQLException ) t,
+ "error performing isolated work"
+ );
+ }
+ else {
+ throw new HibernateException( "error performing isolated work", t );
+ }
+ }
+ finally {
+ if ( transacted && wasAutoCommit ) {
+ try {
+ connection.setAutoCommit( true );
+ }
+ catch( Throwable ignore ) {
+ log.trace( "was unable to reset connection back to auto-commit" );
+ }
+ }
+ session.getBatcher().closeConnection( connection );
+ }
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/AbstractEvent.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/AbstractEvent.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/AbstractEvent.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,35 @@
+//$Id: AbstractEvent.java 6929 2005-05-27 03:54:08Z oneovthafew $
+package org.hibernate.event;
+
+import java.io.Serializable;
+
+
+/**
+ * Defines a base class for Session generated events.
+ *
+ * @author Steve Ebersole
+ */
+public abstract class AbstractEvent implements Serializable {
+
+ private final EventSource session;
+
+ /**
+ * Constructs an event from the given event session.
+ *
+ * @param source The session event source.
+ */
+ public AbstractEvent(EventSource source) {
+ this.session = source;
+ }
+
+ /**
+ * Returns the session event source for this event. This is the underlying
+ * session from which this event was generated.
+ *
+ * @return The session event source.
+ */
+ public final EventSource getSession() {
+ return session;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/AutoFlushEvent.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/AutoFlushEvent.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/AutoFlushEvent.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,36 @@
+///$Id: AutoFlushEvent.java 7785 2005-08-08 23:24:44Z oneovthafew $
+package org.hibernate.event;
+
+import java.util.Set;
+
+
+/** Defines an event class for the auto-flushing of a session.
+ *
+ * @author Steve Ebersole
+ */
+public class AutoFlushEvent extends FlushEvent {
+
+ private Set querySpaces;
+ private boolean flushRequired;
+
+ public AutoFlushEvent(Set querySpaces, EventSource source) {
+ super(source);
+ this.querySpaces = querySpaces;
+ }
+
+ public Set getQuerySpaces() {
+ return querySpaces;
+ }
+
+ public void setQuerySpaces(Set querySpaces) {
+ this.querySpaces = querySpaces;
+ }
+
+ public boolean isFlushRequired() {
+ return flushRequired;
+ }
+
+ public void setFlushRequired(boolean dirty) {
+ this.flushRequired = dirty;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/AutoFlushEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/AutoFlushEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/AutoFlushEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,21 @@
+//$Id: AutoFlushEventListener.java 7785 2005-08-08 23:24:44Z oneovthafew $
+package org.hibernate.event;
+
+import org.hibernate.HibernateException;
+
+import java.io.Serializable;
+
+/**
+ * Defines the contract for handling of session auto-flush events.
+ *
+ * @author Steve Ebersole
+ */
+public interface AutoFlushEventListener extends Serializable {
+
+ /** Handle the given auto-flush event.
+ *
+ * @param event The auto-flush event to be handled.
+ * @throws HibernateException
+ */
+ public void onAutoFlush(AutoFlushEvent event) throws HibernateException;
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/DeleteEvent.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/DeleteEvent.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/DeleteEvent.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,59 @@
+//$Id: DeleteEvent.java 7450 2005-07-11 20:33:59Z steveebersole $
+package org.hibernate.event;
+
+
+/** Defines an event class for the deletion of an entity.
+ *
+ * @author Steve Ebersole
+ */
+public class DeleteEvent extends AbstractEvent {
+
+ private Object object;
+ private String entityName;
+ private boolean cascadeDeleteEnabled;
+
+ /**
+ * Constructs a new DeleteEvent instance.
+ *
+ * @param object The entity to be deleted.
+ * @param source The session from which the delete event was generated.
+ */
+ public DeleteEvent(Object object, EventSource source) {
+ super(source);
+ if (object == null) {
+ throw new IllegalArgumentException(
+ "attempt to create delete event with null entity"
+ );
+ }
+ this.object = object;
+ }
+
+ public DeleteEvent(String entityName, Object object, EventSource source) {
+ this(object, source);
+ this.entityName = entityName;
+ }
+
+ public DeleteEvent(String entityName, Object object, boolean isCascadeDeleteEnabled, EventSource source) {
+ this(object, source);
+ this.entityName = entityName;
+ cascadeDeleteEnabled = isCascadeDeleteEnabled;
+ }
+
+ /**
+ * Returns the encapsulated entity to be deleed.
+ *
+ * @return The entity to be deleted.
+ */
+ public Object getObject() {
+ return object;
+ }
+
+ public String getEntityName() {
+ return entityName;
+ }
+
+ public boolean isCascadeDeleteEnabled() {
+ return cascadeDeleteEnabled;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/DeleteEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/DeleteEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/DeleteEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,24 @@
+//$Id: DeleteEventListener.java 9944 2006-05-24 21:14:56Z steve.ebersole at jboss.com $
+package org.hibernate.event;
+
+import org.hibernate.HibernateException;
+
+import java.io.Serializable;
+import java.util.Set;
+
+/**
+ * Defines the contract for handling of deletion events generated from a session.
+ *
+ * @author Steve Ebersole
+ */
+public interface DeleteEventListener extends Serializable {
+
+ /** Handle the given delete event.
+ *
+ * @param event The delete event to be handled.
+ * @throws HibernateException
+ */
+ public void onDelete(DeleteEvent event) throws HibernateException;
+
+ public void onDelete(DeleteEvent event, Set transientEntities) throws HibernateException;
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/DirtyCheckEvent.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/DirtyCheckEvent.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/DirtyCheckEvent.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,25 @@
+//$Id: DirtyCheckEvent.java 7785 2005-08-08 23:24:44Z oneovthafew $
+package org.hibernate.event;
+
+
+/** Defines an event class for the dirty-checking of a session.
+ *
+ * @author Steve Ebersole
+ */
+public class DirtyCheckEvent extends FlushEvent {
+
+ private boolean dirty;
+
+ public DirtyCheckEvent(EventSource source) {
+ super(source);
+ }
+
+ public boolean isDirty() {
+ return dirty;
+ }
+
+ public void setDirty(boolean dirty) {
+ this.dirty = dirty;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/DirtyCheckEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/DirtyCheckEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/DirtyCheckEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+//$Id: DirtyCheckEventListener.java 7785 2005-08-08 23:24:44Z oneovthafew $
+package org.hibernate.event;
+
+import org.hibernate.HibernateException;
+
+import java.io.Serializable;
+
+/**
+ * Defines the contract for handling of session dirty-check events.
+ *
+ * @author Steve Ebersole
+ */
+public interface DirtyCheckEventListener extends Serializable {
+
+ /** Handle the given dirty-check event.
+ *
+ * @param event The dirty-check event to be handled.
+ * @throws HibernateException
+ */
+ public void onDirtyCheck(DirtyCheckEvent event) throws HibernateException;
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/EventListeners.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/EventListeners.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/EventListeners.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,390 @@
+//$Id: EventListeners.java 8416 2005-10-16 13:27:54Z epbernard $
+package org.hibernate.event;
+
+import java.io.Serializable;
+import java.lang.reflect.Field;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.hibernate.AssertionFailure;
+import org.hibernate.MappingException;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.event.def.DefaultAutoFlushEventListener;
+import org.hibernate.event.def.DefaultDeleteEventListener;
+import org.hibernate.event.def.DefaultDirtyCheckEventListener;
+import org.hibernate.event.def.DefaultEvictEventListener;
+import org.hibernate.event.def.DefaultFlushEntityEventListener;
+import org.hibernate.event.def.DefaultFlushEventListener;
+import org.hibernate.event.def.DefaultInitializeCollectionEventListener;
+import org.hibernate.event.def.DefaultLoadEventListener;
+import org.hibernate.event.def.DefaultLockEventListener;
+import org.hibernate.event.def.DefaultMergeEventListener;
+import org.hibernate.event.def.DefaultPersistEventListener;
+import org.hibernate.event.def.DefaultPostLoadEventListener;
+import org.hibernate.event.def.DefaultPreLoadEventListener;
+import org.hibernate.event.def.DefaultRefreshEventListener;
+import org.hibernate.event.def.DefaultReplicateEventListener;
+import org.hibernate.event.def.DefaultSaveEventListener;
+import org.hibernate.event.def.DefaultSaveOrUpdateCopyEventListener;
+import org.hibernate.event.def.DefaultSaveOrUpdateEventListener;
+import org.hibernate.event.def.DefaultUpdateEventListener;
+import org.hibernate.event.def.DefaultPersistOnFlushEventListener;
+import org.hibernate.util.Cloneable;
+
+/**
+ * A convience holder for all defined session event listeners.
+ *
+ * @author Steve Ebersole
+ */
+public class EventListeners extends Cloneable implements Serializable {
+
+ private LoadEventListener[] loadEventListeners = { new DefaultLoadEventListener() };
+ private SaveOrUpdateEventListener[] saveOrUpdateEventListeners = { new DefaultSaveOrUpdateEventListener() };
+ private MergeEventListener[] mergeEventListeners = { new DefaultMergeEventListener() };
+ private PersistEventListener[] persistEventListeners = { new DefaultPersistEventListener() };
+ private PersistEventListener[] persistOnFlushEventListeners = { new DefaultPersistOnFlushEventListener() };
+ private ReplicateEventListener[] replicateEventListeners = { new DefaultReplicateEventListener() };
+ private DeleteEventListener[] deleteEventListeners = { new DefaultDeleteEventListener() };
+ private AutoFlushEventListener[] autoFlushEventListeners = { new DefaultAutoFlushEventListener() };
+ private DirtyCheckEventListener[] dirtyCheckEventListeners = { new DefaultDirtyCheckEventListener() };
+ private FlushEventListener[] flushEventListeners = { new DefaultFlushEventListener() };
+ private EvictEventListener[] evictEventListeners = { new DefaultEvictEventListener() };
+ private LockEventListener[] lockEventListeners = { new DefaultLockEventListener() };
+ private RefreshEventListener[] refreshEventListeners = { new DefaultRefreshEventListener() };
+ private FlushEntityEventListener[] flushEntityEventListeners = { new DefaultFlushEntityEventListener() };
+ private InitializeCollectionEventListener[] initializeCollectionEventListeners =
+ { new DefaultInitializeCollectionEventListener() };
+
+ private PostLoadEventListener[] postLoadEventListeners = { new DefaultPostLoadEventListener() };
+ private PreLoadEventListener[] preLoadEventListeners = { new DefaultPreLoadEventListener() };
+
+ private PreDeleteEventListener[] preDeleteEventListeners = {};
+ private PreUpdateEventListener[] preUpdateEventListeners = {};
+ private PreInsertEventListener[] preInsertEventListeners = {};
+ private PostDeleteEventListener[] postDeleteEventListeners = {};
+ private PostUpdateEventListener[] postUpdateEventListeners = {};
+ private PostInsertEventListener[] postInsertEventListeners = {};
+ private PostDeleteEventListener[] postCommitDeleteEventListeners = {};
+ private PostUpdateEventListener[] postCommitUpdateEventListeners = {};
+ private PostInsertEventListener[] postCommitInsertEventListeners = {};
+
+ private SaveOrUpdateEventListener[] saveEventListeners = { new DefaultSaveEventListener() };
+ private SaveOrUpdateEventListener[] updateEventListeners = { new DefaultUpdateEventListener() };
+ private MergeEventListener[] saveOrUpdateCopyEventListeners = { new DefaultSaveOrUpdateCopyEventListener() };//saveOrUpdateCopy() is deprecated!
+
+ private static Map eventInterfaceFromType;
+
+ static {
+ eventInterfaceFromType = new HashMap();
+
+ eventInterfaceFromType.put("auto-flush", AutoFlushEventListener.class);
+ eventInterfaceFromType.put("merge", MergeEventListener.class);
+ eventInterfaceFromType.put("create", PersistEventListener.class);
+ eventInterfaceFromType.put("create-onflush", PersistEventListener.class);
+ eventInterfaceFromType.put("delete", DeleteEventListener.class);
+ eventInterfaceFromType.put("dirty-check", DirtyCheckEventListener.class);
+ eventInterfaceFromType.put("evict", EvictEventListener.class);
+ eventInterfaceFromType.put("flush", FlushEventListener.class);
+ eventInterfaceFromType.put("flush-entity", FlushEntityEventListener.class);
+ eventInterfaceFromType.put("load", LoadEventListener.class);
+ eventInterfaceFromType.put("load-collection", InitializeCollectionEventListener.class);
+ eventInterfaceFromType.put("lock", LockEventListener.class);
+ eventInterfaceFromType.put("refresh", RefreshEventListener.class);
+ eventInterfaceFromType.put("replicate", ReplicateEventListener.class);
+ eventInterfaceFromType.put("save-update", SaveOrUpdateEventListener.class);
+ eventInterfaceFromType.put("save", SaveOrUpdateEventListener.class);
+ eventInterfaceFromType.put("update", SaveOrUpdateEventListener.class);
+ eventInterfaceFromType.put("pre-load", PreLoadEventListener.class);
+ eventInterfaceFromType.put("pre-update", PreUpdateEventListener.class);
+ eventInterfaceFromType.put("pre-delete", PreDeleteEventListener.class);
+ eventInterfaceFromType.put("pre-insert", PreInsertEventListener.class);
+ eventInterfaceFromType.put("post-load", PostLoadEventListener.class);
+ eventInterfaceFromType.put("post-update", PostUpdateEventListener.class);
+ eventInterfaceFromType.put("post-delete", PostDeleteEventListener.class);
+ eventInterfaceFromType.put("post-insert", PostInsertEventListener.class);
+ eventInterfaceFromType.put("post-commit-update", PostUpdateEventListener.class);
+ eventInterfaceFromType.put("post-commit-delete", PostDeleteEventListener.class);
+ eventInterfaceFromType.put("post-commit-insert", PostInsertEventListener.class);
+ eventInterfaceFromType = Collections.unmodifiableMap( eventInterfaceFromType );
+ }
+
+ public Class getListenerClassFor(String type) {
+ Class clazz = (Class) eventInterfaceFromType.get(type);
+
+ if (clazz == null) {
+ throw new MappingException("Unrecognized listener type [" + type + "]");
+ }
+
+ return clazz;
+ }
+
+ public LoadEventListener[] getLoadEventListeners() {
+ return loadEventListeners;
+ }
+
+ public void setLoadEventListeners(LoadEventListener[] loadEventListener) {
+ this.loadEventListeners = loadEventListener;
+ }
+
+ public ReplicateEventListener[] getReplicateEventListeners() {
+ return replicateEventListeners;
+ }
+
+ public void setReplicateEventListeners(ReplicateEventListener[] replicateEventListener) {
+ this.replicateEventListeners = replicateEventListener;
+ }
+
+ public DeleteEventListener[] getDeleteEventListeners() {
+ return deleteEventListeners;
+ }
+
+ public void setDeleteEventListeners(DeleteEventListener[] deleteEventListener) {
+ this.deleteEventListeners = deleteEventListener;
+ }
+
+ public AutoFlushEventListener[] getAutoFlushEventListeners() {
+ return autoFlushEventListeners;
+ }
+
+ public void setAutoFlushEventListeners(AutoFlushEventListener[] autoFlushEventListener) {
+ this.autoFlushEventListeners = autoFlushEventListener;
+ }
+
+ public DirtyCheckEventListener[] getDirtyCheckEventListeners() {
+ return dirtyCheckEventListeners;
+ }
+
+ public void setDirtyCheckEventListeners(DirtyCheckEventListener[] dirtyCheckEventListener) {
+ this.dirtyCheckEventListeners = dirtyCheckEventListener;
+ }
+
+ public FlushEventListener[] getFlushEventListeners() {
+ return flushEventListeners;
+ }
+
+ public void setFlushEventListeners(FlushEventListener[] flushEventListener) {
+ this.flushEventListeners = flushEventListener;
+ }
+
+ public EvictEventListener[] getEvictEventListeners() {
+ return evictEventListeners;
+ }
+
+ public void setEvictEventListeners(EvictEventListener[] evictEventListener) {
+ this.evictEventListeners = evictEventListener;
+ }
+
+ public LockEventListener[] getLockEventListeners() {
+ return lockEventListeners;
+ }
+
+ public void setLockEventListeners(LockEventListener[] lockEventListener) {
+ this.lockEventListeners = lockEventListener;
+ }
+
+ public RefreshEventListener[] getRefreshEventListeners() {
+ return refreshEventListeners;
+ }
+
+ public void setRefreshEventListeners(RefreshEventListener[] refreshEventListener) {
+ this.refreshEventListeners = refreshEventListener;
+ }
+
+ public InitializeCollectionEventListener[] getInitializeCollectionEventListeners() {
+ return initializeCollectionEventListeners;
+ }
+
+ public void setInitializeCollectionEventListeners(InitializeCollectionEventListener[] initializeCollectionEventListener) {
+ this.initializeCollectionEventListeners = initializeCollectionEventListener;
+ }
+
+ public FlushEntityEventListener[] getFlushEntityEventListeners() {
+ return flushEntityEventListeners;
+ }
+
+ public void setFlushEntityEventListeners(FlushEntityEventListener[] flushEntityEventListener) {
+ this.flushEntityEventListeners = flushEntityEventListener;
+ }
+
+ public SaveOrUpdateEventListener[] getSaveOrUpdateEventListeners() {
+ return saveOrUpdateEventListeners;
+ }
+
+ public void setSaveOrUpdateEventListeners(SaveOrUpdateEventListener[] saveOrUpdateEventListener) {
+ this.saveOrUpdateEventListeners = saveOrUpdateEventListener;
+ }
+
+ public MergeEventListener[] getMergeEventListeners() {
+ return mergeEventListeners;
+ }
+
+ public void setMergeEventListeners(MergeEventListener[] mergeEventListener) {
+ this.mergeEventListeners = mergeEventListener;
+ }
+
+ public PersistEventListener[] getPersistEventListeners() {
+ return persistEventListeners;
+ }
+
+ public void setPersistEventListeners(PersistEventListener[] createEventListener) {
+ this.persistEventListeners = createEventListener;
+ }
+
+ public PersistEventListener[] getPersistOnFlushEventListeners() {
+ return persistOnFlushEventListeners;
+ }
+
+ public void setPersistOnFlushEventListeners(PersistEventListener[] createEventListener) {
+ this.persistOnFlushEventListeners = createEventListener;
+ }
+
+ public MergeEventListener[] getSaveOrUpdateCopyEventListeners() {
+ return saveOrUpdateCopyEventListeners;
+ }
+
+ public void setSaveOrUpdateCopyEventListeners(MergeEventListener[] saveOrUpdateCopyEventListener) {
+ this.saveOrUpdateCopyEventListeners = saveOrUpdateCopyEventListener;
+ }
+
+ public SaveOrUpdateEventListener[] getSaveEventListeners() {
+ return saveEventListeners;
+ }
+
+ public void setSaveEventListeners(SaveOrUpdateEventListener[] saveEventListener) {
+ this.saveEventListeners = saveEventListener;
+ }
+
+ public SaveOrUpdateEventListener[] getUpdateEventListeners() {
+ return updateEventListeners;
+ }
+
+ public void setUpdateEventListeners(SaveOrUpdateEventListener[] updateEventListener) {
+ this.updateEventListeners = updateEventListener;
+ }
+
+ public PostLoadEventListener[] getPostLoadEventListeners() {
+ return postLoadEventListeners;
+ }
+
+ public void setPostLoadEventListeners(PostLoadEventListener[] postLoadEventListener) {
+ this.postLoadEventListeners = postLoadEventListener;
+ }
+
+ public PreLoadEventListener[] getPreLoadEventListeners() {
+ return preLoadEventListeners;
+ }
+
+ public void setPreLoadEventListeners(PreLoadEventListener[] preLoadEventListener) {
+ this.preLoadEventListeners = preLoadEventListener;
+ }
+
+ public PostDeleteEventListener[] getPostDeleteEventListeners() {
+ return postDeleteEventListeners;
+ }
+
+ public PostInsertEventListener[] getPostInsertEventListeners() {
+ return postInsertEventListeners;
+ }
+
+ public PostUpdateEventListener[] getPostUpdateEventListeners() {
+ return postUpdateEventListeners;
+ }
+
+ public void setPostDeleteEventListeners(PostDeleteEventListener[] postDeleteEventListener) {
+ this.postDeleteEventListeners = postDeleteEventListener;
+ }
+
+ public void setPostInsertEventListeners(PostInsertEventListener[] postInsertEventListener) {
+ this.postInsertEventListeners = postInsertEventListener;
+ }
+
+ public void setPostUpdateEventListeners(PostUpdateEventListener[] postUpdateEventListener) {
+ this.postUpdateEventListeners = postUpdateEventListener;
+ }
+
+ public PreDeleteEventListener[] getPreDeleteEventListeners() {
+ return preDeleteEventListeners;
+ }
+
+ public void setPreDeleteEventListeners(PreDeleteEventListener[] preDeleteEventListener) {
+ this.preDeleteEventListeners = preDeleteEventListener;
+ }
+
+ public PreInsertEventListener[] getPreInsertEventListeners() {
+ return preInsertEventListeners;
+ }
+
+ public void setPreInsertEventListeners(PreInsertEventListener[] preInsertEventListener) {
+ this.preInsertEventListeners = preInsertEventListener;
+ }
+
+ public PreUpdateEventListener[] getPreUpdateEventListeners() {
+ return preUpdateEventListeners;
+ }
+
+ public void setPreUpdateEventListeners(PreUpdateEventListener[] preUpdateEventListener) {
+ this.preUpdateEventListeners = preUpdateEventListener;
+ }
+
+ /**
+ * Call <tt>initialize()</tt> on any listeners that implement
+ * <tt>Initializable</tt>.
+ * @see Initializable
+ */
+ public void initializeListeners(Configuration cfg) {
+ Field[] fields = getClass().getDeclaredFields();
+ for ( int i = 0; i < fields.length; i++ ) {
+ Object[] listeners;
+ try {
+ Object listener = fields[i].get(this);
+ if (listener instanceof Object[]) {
+ listeners = (Object[]) listener;
+ }
+ else {
+ continue;
+ }
+
+ }
+ catch (Exception e) {
+ throw new AssertionFailure("could not init listeners");
+ }
+ int length = listeners.length;
+ for (int index = 0 ; index < length ; index++) {
+ Object listener = listeners[index];
+ if (listener instanceof Initializable ) {
+ ( (Initializable) listener ).initialize(cfg);
+ }
+ }
+
+ }
+ }
+
+ public PostDeleteEventListener[] getPostCommitDeleteEventListeners() {
+ return postCommitDeleteEventListeners;
+ }
+
+ public void setPostCommitDeleteEventListeners(
+ PostDeleteEventListener[] postCommitDeleteEventListeners) {
+ this.postCommitDeleteEventListeners = postCommitDeleteEventListeners;
+ }
+
+ public PostInsertEventListener[] getPostCommitInsertEventListeners() {
+ return postCommitInsertEventListeners;
+ }
+
+ public void setPostCommitInsertEventListeners(
+ PostInsertEventListener[] postCommitInsertEventListeners) {
+ this.postCommitInsertEventListeners = postCommitInsertEventListeners;
+ }
+
+ public PostUpdateEventListener[] getPostCommitUpdateEventListeners() {
+ return postCommitUpdateEventListeners;
+ }
+
+ public void setPostCommitUpdateEventListeners(
+ PostUpdateEventListener[] postCommitUpdateEventListeners) {
+ this.postCommitUpdateEventListeners = postCommitUpdateEventListeners;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/EventSource.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/EventSource.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/EventSource.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,63 @@
+//$Id: EventSource.java 9944 2006-05-24 21:14:56Z steve.ebersole at jboss.com $
+package org.hibernate.event;
+
+import java.io.Serializable;
+import java.util.Map;
+import java.util.Set;
+
+import org.hibernate.HibernateException;
+import org.hibernate.Session;
+import org.hibernate.engine.ActionQueue;
+import org.hibernate.engine.EntityEntry;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.entity.EntityPersister;
+
+/**
+ * @author Gavin King
+ */
+public interface EventSource extends SessionImplementor, Session {
+
+ /**
+ * Get the ActionQueue for this session
+ */
+ public ActionQueue getActionQueue();
+
+ /**
+ * Instantiate an entity instance, using either an interceptor,
+ * or the given persister
+ */
+ public Object instantiate(EntityPersister persister, Serializable id) throws HibernateException;
+
+ /**
+ * Force an immediate flush
+ */
+ public void forceFlush(EntityEntry e) throws HibernateException;
+
+ /**
+ * Cascade merge an entity instance
+ */
+ public void merge(String entityName, Object object, Map copiedAlready) throws HibernateException;
+ /**
+ * Cascade persist an entity instance
+ */
+ public void persist(String entityName, Object object, Map createdAlready) throws HibernateException;
+
+ /**
+ * Cascade persist an entity instance during the flush process
+ */
+ public void persistOnFlush(String entityName, Object object, Map copiedAlready);
+ /**
+ * Cascade refesh an entity instance
+ */
+ public void refresh(Object object, Map refreshedAlready) throws HibernateException;
+ /**
+ * Cascade copy an entity instance
+ */
+ public void saveOrUpdateCopy(String entityName, Object object, Map copiedAlready) throws HibernateException;
+
+ /**
+ * Cascade delete an entity instance
+ */
+ public void delete(String entityName, Object child, boolean isCascadeDeleteEnabled, Set transientEntities);
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/event/EventSource.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/EvictEvent.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/EvictEvent.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/EvictEvent.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,26 @@
+//$Id: EvictEvent.java 6929 2005-05-27 03:54:08Z oneovthafew $
+package org.hibernate.event;
+
+
+/**
+ * Defines an event class for the evicting of an entity.
+ *
+ * @author Steve Ebersole
+ */
+public class EvictEvent extends AbstractEvent {
+
+ private Object object;
+
+ public EvictEvent(Object object, EventSource source) {
+ super(source);
+ this.object = object;
+ }
+
+ public Object getObject() {
+ return object;
+ }
+
+ public void setObject(Object object) {
+ this.object = object;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/EvictEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/EvictEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/EvictEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+//$Id: EvictEventListener.java 4533 2004-09-12 03:02:54Z oneovthafew $
+package org.hibernate.event;
+
+import org.hibernate.HibernateException;
+
+import java.io.Serializable;
+
+/**
+ * Defines the contract for handling of evict events generated from a session.
+ *
+ * @author Steve Ebersole
+ */
+public interface EvictEventListener extends Serializable {
+
+ /**
+ * Handle the given evict event.
+ *
+ * @param event The evict event to be handled.
+ * @throws HibernateException
+ */
+ public void onEvict(EvictEvent event) throws HibernateException;
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/FlushEntityEvent.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/FlushEntityEvent.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/FlushEntityEvent.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,71 @@
+//$Id: FlushEntityEvent.java 6929 2005-05-27 03:54:08Z oneovthafew $
+package org.hibernate.event;
+
+import org.hibernate.engine.EntityEntry;
+
+/**
+ * @author Gavin King
+ */
+public class FlushEntityEvent extends AbstractEvent {
+
+ private Object entity;
+ private Object[] propertyValues;
+ private Object[] databaseSnapshot;
+ private int[] dirtyProperties;
+ private boolean hasDirtyCollection;
+ private boolean dirtyCheckPossible;
+ private boolean dirtyCheckHandledByInterceptor;
+ private EntityEntry entityEntry;
+
+ public FlushEntityEvent(EventSource source, Object entity, EntityEntry entry) {
+ super(source);
+ this.entity = entity;
+ this.entityEntry = entry;
+ }
+
+ public EntityEntry getEntityEntry() {
+ return entityEntry;
+ }
+ public Object[] getDatabaseSnapshot() {
+ return databaseSnapshot;
+ }
+ public void setDatabaseSnapshot(Object[] databaseSnapshot) {
+ this.databaseSnapshot = databaseSnapshot;
+ }
+ public boolean hasDatabaseSnapshot() {
+ return databaseSnapshot!=null;
+ }
+ public boolean isDirtyCheckHandledByInterceptor() {
+ return dirtyCheckHandledByInterceptor;
+ }
+ public void setDirtyCheckHandledByInterceptor(boolean dirtyCheckHandledByInterceptor) {
+ this.dirtyCheckHandledByInterceptor = dirtyCheckHandledByInterceptor;
+ }
+ public boolean isDirtyCheckPossible() {
+ return dirtyCheckPossible;
+ }
+ public void setDirtyCheckPossible(boolean dirtyCheckPossible) {
+ this.dirtyCheckPossible = dirtyCheckPossible;
+ }
+ public int[] getDirtyProperties() {
+ return dirtyProperties;
+ }
+ public void setDirtyProperties(int[] dirtyProperties) {
+ this.dirtyProperties = dirtyProperties;
+ }
+ public boolean hasDirtyCollection() {
+ return hasDirtyCollection;
+ }
+ public void setHasDirtyCollection(boolean hasDirtyCollection) {
+ this.hasDirtyCollection = hasDirtyCollection;
+ }
+ public Object[] getPropertyValues() {
+ return propertyValues;
+ }
+ public void setPropertyValues(Object[] propertyValues) {
+ this.propertyValues = propertyValues;
+ }
+ public Object getEntity() {
+ return entity;
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/event/FlushEntityEvent.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/FlushEntityEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/FlushEntityEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/FlushEntityEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,13 @@
+//$Id: FlushEntityEventListener.java 7785 2005-08-08 23:24:44Z oneovthafew $
+package org.hibernate.event;
+
+import java.io.Serializable;
+
+import org.hibernate.HibernateException;
+
+/**
+ * @author Gavin King
+ */
+public interface FlushEntityEventListener extends Serializable {
+ public void onFlushEntity(FlushEntityEvent event) throws HibernateException;
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/event/FlushEntityEventListener.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/FlushEvent.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/FlushEvent.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/FlushEvent.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,16 @@
+//$Id: FlushEvent.java 6929 2005-05-27 03:54:08Z oneovthafew $
+package org.hibernate.event;
+
+
+/**
+ * Defines an event class for the flushing of a session.
+ *
+ * @author Steve Ebersole
+ */
+public class FlushEvent extends AbstractEvent {
+
+ public FlushEvent(EventSource source) {
+ super(source);
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/FlushEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/FlushEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/FlushEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,21 @@
+//$Id: FlushEventListener.java 4185 2004-08-08 11:24:56Z oneovthafew $
+package org.hibernate.event;
+
+import org.hibernate.HibernateException;
+
+import java.io.Serializable;
+
+/**
+ * Defines the contract for handling of session flush events.
+ *
+ * @author Steve Ebersole
+ */
+public interface FlushEventListener extends Serializable {
+
+ /** Handle the given flush event.
+ *
+ * @param event The flush event to be handled.
+ * @throws HibernateException
+ */
+ public void onFlush(FlushEvent event) throws HibernateException;
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/Initializable.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/Initializable.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/Initializable.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,13 @@
+//$Id: Initializable.java 7793 2005-08-10 05:06:40Z oneovthafew $
+package org.hibernate.event;
+
+import org.hibernate.cfg.Configuration;
+
+/**
+ * An event listener that requires access to mappings to
+ * initialize state at initialization time.
+ * @author Gavin King
+ */
+public interface Initializable {
+ public void initialize(Configuration cfg);
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/event/Initializable.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/InitializeCollectionEvent.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/InitializeCollectionEvent.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/InitializeCollectionEvent.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,24 @@
+//$Id: InitializeCollectionEvent.java 6929 2005-05-27 03:54:08Z oneovthafew $
+package org.hibernate.event;
+
+import org.hibernate.collection.PersistentCollection;
+
+/**
+ * An event that occurs when a collection wants to be
+ * initialized
+ *
+ * @author Gavin King
+ */
+public class InitializeCollectionEvent extends AbstractEvent {
+
+ private final PersistentCollection collection;
+
+ public InitializeCollectionEvent(PersistentCollection collection, EventSource source) {
+ super(source);
+ this.collection = collection;
+ }
+
+ public PersistentCollection getCollection() {
+ return collection;
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/event/InitializeCollectionEvent.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/InitializeCollectionEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/InitializeCollectionEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/InitializeCollectionEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,18 @@
+//$Id: InitializeCollectionEventListener.java 4345 2004-08-16 12:12:12Z oneovthafew $
+package org.hibernate.event;
+
+import org.hibernate.HibernateException;
+
+import java.io.Serializable;
+
+/**
+ * Defines the contract for handling of collection initialization events
+ * generated by a session.
+ *
+ * @author Gavin King
+ */
+public interface InitializeCollectionEventListener extends Serializable {
+
+ public void onInitializeCollection(InitializeCollectionEvent event) throws HibernateException;
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/event/InitializeCollectionEventListener.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/LoadEvent.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/LoadEvent.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/LoadEvent.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,107 @@
+//$Id: LoadEvent.java 7785 2005-08-08 23:24:44Z oneovthafew $
+package org.hibernate.event;
+
+import java.io.Serializable;
+
+import org.hibernate.LockMode;
+
+/**
+ * Defines an event class for the loading of an entity.
+ *
+ * @author Steve Ebersole
+ */
+public class LoadEvent extends AbstractEvent {
+
+ public static final LockMode DEFAULT_LOCK_MODE = LockMode.NONE;
+
+ private Serializable entityId;
+ private String entityClassName;
+ private Object instanceToLoad;
+ private LockMode lockMode;
+ private boolean isAssociationFetch;
+ private Object result;
+
+ public LoadEvent(Serializable entityId, Object instanceToLoad, EventSource source) {
+ this(entityId, null, instanceToLoad, null, false, source);
+ }
+
+ public LoadEvent(Serializable entityId, String entityClassName, LockMode lockMode, EventSource source) {
+ this(entityId, entityClassName, null, lockMode, false, source);
+ }
+
+ public LoadEvent(Serializable entityId, String entityClassName, boolean isAssociationFetch, EventSource source) {
+ this(entityId, entityClassName, null, null, isAssociationFetch, source);
+ }
+
+ public boolean isAssociationFetch() {
+ return isAssociationFetch;
+ }
+
+ private LoadEvent(
+ Serializable entityId,
+ String entityClassName,
+ Object instanceToLoad,
+ LockMode lockMode,
+ boolean isAssociationFetch,
+ EventSource source) {
+
+ super(source);
+
+ if ( entityId == null ) {
+ throw new IllegalArgumentException("id to load is required for loading");
+ }
+
+ if ( lockMode == LockMode.WRITE ) {
+ throw new IllegalArgumentException("Invalid lock mode for loading");
+ }
+ else if ( lockMode == null ) {
+ lockMode = DEFAULT_LOCK_MODE;
+ }
+
+ this.entityId = entityId;
+ this.entityClassName = entityClassName;
+ this.instanceToLoad = instanceToLoad;
+ this.lockMode = lockMode;
+ this.isAssociationFetch = isAssociationFetch;
+ }
+
+ public Serializable getEntityId() {
+ return entityId;
+ }
+
+ public void setEntityId(Serializable entityId) {
+ this.entityId = entityId;
+ }
+
+ public String getEntityClassName() {
+ return entityClassName;
+ }
+
+ public void setEntityClassName(String entityClassName) {
+ this.entityClassName = entityClassName;
+ }
+
+ public Object getInstanceToLoad() {
+ return instanceToLoad;
+ }
+
+ public void setInstanceToLoad(Object instanceToLoad) {
+ this.instanceToLoad = instanceToLoad;
+ }
+
+ public LockMode getLockMode() {
+ return lockMode;
+ }
+
+ public void setLockMode(LockMode lockMode) {
+ this.lockMode = lockMode;
+ }
+
+ public Object getResult() {
+ return result;
+ }
+
+ public void setResult(Object result) {
+ this.result = result;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/LoadEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/LoadEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/LoadEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,122 @@
+//$Id: LoadEventListener.java 7785 2005-08-08 23:24:44Z oneovthafew $
+package org.hibernate.event;
+
+import org.hibernate.HibernateException;
+
+import java.io.Serializable;
+
+/**
+ * Defines the contract for handling of load events generated from a session.
+ *
+ * @author Steve Ebersole
+ */
+public interface LoadEventListener extends Serializable {
+
+ /**
+ * Handle the given load event.
+ *
+ * @param event The load event to be handled.
+ * @return The result (i.e., the loaded entity).
+ * @throws HibernateException
+ */
+ public void onLoad(LoadEvent event, LoadType loadType) throws HibernateException;
+
+ public static final LoadType RELOAD = new LoadType("GET")
+ .setAllowNulls(false)
+ .setAllowProxyCreation(false)
+ .setCheckDeleted(true)
+ .setNakedEntityReturned(false);
+
+ public static final LoadType GET = new LoadType("GET")
+ .setAllowNulls(true)
+ .setAllowProxyCreation(false)
+ .setCheckDeleted(true)
+ .setNakedEntityReturned(false);
+
+ public static final LoadType LOAD = new LoadType("LOAD")
+ .setAllowNulls(false)
+ .setAllowProxyCreation(true)
+ .setCheckDeleted(true)
+ .setNakedEntityReturned(false);
+
+ public static final LoadType IMMEDIATE_LOAD = new LoadType("IMMEDIATE_LOAD")
+ .setAllowNulls(true)
+ .setAllowProxyCreation(false)
+ .setCheckDeleted(false)
+ .setNakedEntityReturned(true);
+
+ public static final LoadType INTERNAL_LOAD_EAGER = new LoadType("INTERNAL_LOAD_EAGER")
+ .setAllowNulls(false)
+ .setAllowProxyCreation(false)
+ .setCheckDeleted(false)
+ .setNakedEntityReturned(false);
+
+ public static final LoadType INTERNAL_LOAD_LAZY = new LoadType("INTERNAL_LOAD_LAZY")
+ .setAllowNulls(false)
+ .setAllowProxyCreation(true)
+ .setCheckDeleted(false)
+ .setNakedEntityReturned(false);
+
+ public static final LoadType INTERNAL_LOAD_NULLABLE = new LoadType("INTERNAL_LOAD_NULLABLE")
+ .setAllowNulls(true)
+ .setAllowProxyCreation(false)
+ .setCheckDeleted(false)
+ .setNakedEntityReturned(false);
+
+ public static final class LoadType {
+ private String name;
+
+ private boolean nakedEntityReturned;
+ private boolean allowNulls;
+ private boolean checkDeleted;
+ private boolean allowProxyCreation;
+
+ private LoadType(String name) {
+ this.name = name;
+ }
+
+ public boolean isAllowNulls() {
+ return allowNulls;
+ }
+
+ private LoadType setAllowNulls(boolean allowNulls) {
+ this.allowNulls = allowNulls;
+ return this;
+ }
+
+ public boolean isNakedEntityReturned() {
+ return nakedEntityReturned;
+ }
+
+ private LoadType setNakedEntityReturned(boolean immediateLoad) {
+ this.nakedEntityReturned = immediateLoad;
+ return this;
+ }
+
+ public boolean isCheckDeleted() {
+ return checkDeleted;
+ }
+
+ private LoadType setCheckDeleted(boolean checkDeleted) {
+ this.checkDeleted = checkDeleted;
+ return this;
+ }
+
+ public boolean isAllowProxyCreation() {
+ return allowProxyCreation;
+ }
+
+ private LoadType setAllowProxyCreation(boolean allowProxyCreation) {
+ this.allowProxyCreation = allowProxyCreation;
+ return this;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String toString() {
+ return name;
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/LockEvent.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/LockEvent.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/LockEvent.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,52 @@
+//$Id: LockEvent.java 6929 2005-05-27 03:54:08Z oneovthafew $
+package org.hibernate.event;
+
+import org.hibernate.LockMode;
+
+/**
+ * Defines an event class for the locking of an entity.
+ *
+ * @author Steve Ebersole
+ */
+public class LockEvent extends AbstractEvent {
+
+ private Object object;
+ private LockMode lockMode;
+ private String entityName;
+
+ public LockEvent(String entityName, Object original, LockMode lockMode, EventSource source) {
+ this(original, lockMode, source);
+ this.entityName = entityName;
+ }
+
+ public LockEvent(Object object, LockMode lockMode, EventSource source) {
+ super(source);
+ this.object = object;
+ this.lockMode = lockMode;
+ }
+
+ public Object getObject() {
+ return object;
+ }
+
+ public void setObject(Object object) {
+ this.object = object;
+ }
+
+ public LockMode getLockMode() {
+ return lockMode;
+ }
+
+ public void setLockMode(LockMode lockMode) {
+ this.lockMode = lockMode;
+ }
+
+ public String getEntityName() {
+ return entityName;
+ }
+
+ public void setEntityName(String entityName) {
+ this.entityName = entityName;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/LockEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/LockEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/LockEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+//$Id: LockEventListener.java 4185 2004-08-08 11:24:56Z oneovthafew $
+package org.hibernate.event;
+
+import org.hibernate.HibernateException;
+
+import java.io.Serializable;
+
+/**
+ * Defines the contract for handling of lock events generated from a session.
+ *
+ * @author Steve Ebersole
+ */
+public interface LockEventListener extends Serializable {
+
+ /** Handle the given lock event.
+ *
+ * @param event The lock event to be handled.
+ * @throws HibernateException
+ */
+ public void onLock(LockEvent event) throws HibernateException;
+}
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/MergeEvent.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/MergeEvent.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/MergeEvent.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,82 @@
+//$Id: MergeEvent.java 7785 2005-08-08 23:24:44Z oneovthafew $
+package org.hibernate.event;
+
+import java.io.Serializable;
+
+/**
+ * An event class for merge() and saveOrUpdateCopy()
+ *
+ * @author Gavin King
+ */
+public class MergeEvent extends AbstractEvent {
+
+ private Object original;
+ private Serializable requestedId;
+ private String entityName;
+ private Object entity;
+ private Object result;
+
+ public MergeEvent(String entityName, Object original, EventSource source) {
+ this(original, source);
+ this.entityName = entityName;
+ }
+
+ public MergeEvent(String entityName, Object original, Serializable id, EventSource source) {
+ this(entityName, original, source);
+ this.requestedId = id;
+ if ( requestedId == null ) {
+ throw new IllegalArgumentException(
+ "attempt to create merge event with null identifier"
+ );
+ }
+ }
+
+ public MergeEvent(Object object, EventSource source) {
+ super(source);
+ if ( object == null ) {
+ throw new IllegalArgumentException(
+ "attempt to create merge event with null entity"
+ );
+ }
+ this.original = object;
+ }
+
+ public Object getOriginal() {
+ return original;
+ }
+
+ public void setOriginal(Object object) {
+ this.original = object;
+ }
+
+ public Serializable getRequestedId() {
+ return requestedId;
+ }
+
+ public void setRequestedId(Serializable requestedId) {
+ this.requestedId = requestedId;
+ }
+
+ public String getEntityName() {
+ return entityName;
+ }
+
+ public void setEntityName(String entityName) {
+ this.entityName = entityName;
+ }
+
+ public Object getEntity() {
+ return entity;
+ }
+ public void setEntity(Object entity) {
+ this.entity = entity;
+ }
+
+ public Object getResult() {
+ return result;
+ }
+
+ public void setResult(Object result) {
+ this.result = result;
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/event/MergeEvent.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/MergeEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/MergeEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/MergeEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,32 @@
+//$Id: MergeEventListener.java 7785 2005-08-08 23:24:44Z oneovthafew $
+package org.hibernate.event;
+
+import java.io.Serializable;
+import java.util.Map;
+
+import org.hibernate.HibernateException;
+
+/**
+ * Defines the contract for handling of merge events generated from a session.
+ *
+ * @author Gavin King
+ */
+public interface MergeEventListener extends Serializable {
+
+ /**
+ * Handle the given merge event.
+ *
+ * @param event The merge event to be handled.
+ * @throws HibernateException
+ */
+ public void onMerge(MergeEvent event) throws HibernateException;
+
+ /**
+ * Handle the given merge event.
+ *
+ * @param event The merge event to be handled.
+ * @throws HibernateException
+ */
+ public void onMerge(MergeEvent event, Map copiedAlready) throws HibernateException;
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/event/MergeEventListener.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/PersistEvent.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/PersistEvent.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/PersistEvent.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,47 @@
+//$Id: PersistEvent.java 6929 2005-05-27 03:54:08Z oneovthafew $
+package org.hibernate.event;
+
+
+
+/**
+ * An event class for persist()
+ *
+ * @author Gavin King
+ */
+public class PersistEvent extends AbstractEvent {
+
+ private Object object;
+ private String entityName;
+
+ public PersistEvent(String entityName, Object original, EventSource source) {
+ this(original, source);
+ this.entityName = entityName;
+ }
+
+ public PersistEvent(Object object, EventSource source) {
+ super(source);
+ if ( object == null ) {
+ throw new IllegalArgumentException(
+ "attempt to create create event with null entity"
+ );
+ }
+ this.object = object;
+ }
+
+ public Object getObject() {
+ return object;
+ }
+
+ public void setObject(Object object) {
+ this.object = object;
+ }
+
+ public String getEntityName() {
+ return entityName;
+ }
+
+ public void setEntityName(String entityName) {
+ this.entityName = entityName;
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/event/PersistEvent.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/PersistEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/PersistEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/PersistEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,32 @@
+//$Id: PersistEventListener.java 5835 2005-02-21 14:39:02Z oneovthafew $
+package org.hibernate.event;
+
+import java.io.Serializable;
+import java.util.Map;
+
+import org.hibernate.HibernateException;
+
+/**
+ * Defines the contract for handling of create events generated from a session.
+ *
+ * @author Gavin King
+ */
+public interface PersistEventListener extends Serializable {
+
+ /**
+ * Handle the given create event.
+ *
+ * @param event The create event to be handled.
+ * @throws HibernateException
+ */
+ public void onPersist(PersistEvent event) throws HibernateException;
+
+ /**
+ * Handle the given create event.
+ *
+ * @param event The create event to be handled.
+ * @throws HibernateException
+ */
+ public void onPersist(PersistEvent event, Map createdAlready) throws HibernateException;
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/event/PersistEventListener.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/PostDeleteEvent.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/PostDeleteEvent.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/PostDeleteEvent.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,45 @@
+//$Id: PostDeleteEvent.java 10680 2006-11-01 22:53:30Z epbernard $
+package org.hibernate.event;
+
+import java.io.Serializable;
+
+import org.hibernate.persister.entity.EntityPersister;
+
+/**
+ * Occurs after deleting an item from the datastore
+ *
+ * @author Gavin King
+ */
+public class PostDeleteEvent extends AbstractEvent {
+ private Object entity;
+ private EntityPersister persister;
+ private Serializable id;
+ private Object[] deletedState;
+
+ public PostDeleteEvent(
+ Object entity,
+ Serializable id,
+ Object[] deletedState,
+ EntityPersister persister,
+ EventSource source
+ ) {
+ super(source);
+ this.entity = entity;
+ this.id = id;
+ this.persister = persister;
+ this.deletedState = deletedState;
+ }
+
+ public Serializable getId() {
+ return id;
+ }
+ public EntityPersister getPersister() {
+ return persister;
+ }
+ public Object getEntity() {
+ return entity;
+ }
+ public Object[] getDeletedState() {
+ return deletedState;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/PostDeleteEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/PostDeleteEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/PostDeleteEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,13 @@
+//$Id: PostDeleteEventListener.java 7581 2005-07-20 22:48:22Z oneovthafew $
+package org.hibernate.event;
+
+import java.io.Serializable;
+
+/**
+ * Called after deleting an item from the datastore
+ *
+ * @author Gavin King
+ */
+public interface PostDeleteEventListener extends Serializable {
+ public void onPostDelete(PostDeleteEvent event);
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/PostInsertEvent.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/PostInsertEvent.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/PostInsertEvent.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,45 @@
+//$Id: PostInsertEvent.java 10680 2006-11-01 22:53:30Z epbernard $
+package org.hibernate.event;
+
+import java.io.Serializable;
+
+import org.hibernate.persister.entity.EntityPersister;
+
+/**
+ * Occurs after inserting an item in the datastore
+ *
+ * @author Gavin King
+ */
+public class PostInsertEvent extends AbstractEvent {
+ private Object entity;
+ private EntityPersister persister;
+ private Object[] state;
+ private Serializable id;
+
+ public PostInsertEvent(
+ Object entity,
+ Serializable id,
+ Object[] state,
+ EntityPersister persister,
+ EventSource source
+ ) {
+ super(source);
+ this.entity = entity;
+ this.id = id;
+ this.state = state;
+ this.persister = persister;
+ }
+
+ public Object getEntity() {
+ return entity;
+ }
+ public Serializable getId() {
+ return id;
+ }
+ public EntityPersister getPersister() {
+ return persister;
+ }
+ public Object[] getState() {
+ return state;
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/event/PostInsertEvent.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/PostInsertEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/PostInsertEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/PostInsertEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,13 @@
+//$Id: PostInsertEventListener.java 7581 2005-07-20 22:48:22Z oneovthafew $
+package org.hibernate.event;
+
+import java.io.Serializable;
+
+/**
+ * Called after insterting an item in the datastore
+ *
+ * @author Gavin King
+ */
+public interface PostInsertEventListener extends Serializable {
+ public void onPostInsert(PostInsertEvent event);
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/event/PostInsertEventListener.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/PostLoadEvent.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/PostLoadEvent.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/PostLoadEvent.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,49 @@
+//$Id: PostLoadEvent.java 6929 2005-05-27 03:54:08Z oneovthafew $
+package org.hibernate.event;
+
+import java.io.Serializable;
+
+import org.hibernate.persister.entity.EntityPersister;
+
+/**
+ * Occurs after an an entity instance is fully loaded.
+ *
+ * @author <a href="mailto:kabir.khan at jboss.org">Kabir Khan</a>, Gavin King
+ */
+public class PostLoadEvent extends AbstractEvent {
+ private Object entity;
+ private Serializable id;
+ private EntityPersister persister;
+
+ public PostLoadEvent(EventSource session) {
+ super(session);
+ }
+
+ public Object getEntity() {
+ return entity;
+ }
+
+ public EntityPersister getPersister() {
+ return persister;
+ }
+
+ public Serializable getId() {
+ return id;
+ }
+
+ public PostLoadEvent setEntity(Object entity) {
+ this.entity = entity;
+ return this;
+ }
+
+ public PostLoadEvent setId(Serializable id) {
+ this.id = id;
+ return this;
+ }
+
+ public PostLoadEvent setPersister(EntityPersister persister) {
+ this.persister = persister;
+ return this;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/PostLoadEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/PostLoadEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/PostLoadEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,13 @@
+//$Id: PostLoadEventListener.java 5006 2004-12-19 20:15:13Z oneovthafew $
+package org.hibernate.event;
+
+import java.io.Serializable;
+
+/**
+ * Occurs after an an entity instance is fully loaded.
+ *
+ * @author <a href="mailto:kabir.khan at jboss.org">Kabir Khan</a>
+ */
+public interface PostLoadEventListener extends Serializable {
+ public void onPostLoad(PostLoadEvent event);
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/PostUpdateEvent.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/PostUpdateEvent.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/PostUpdateEvent.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,51 @@
+//$Id: PostUpdateEvent.java 9964 2006-05-30 15:40:54Z epbernard $
+package org.hibernate.event;
+
+import java.io.Serializable;
+
+import org.hibernate.persister.entity.EntityPersister;
+
+/**
+ * Occurs after the datastore is updated
+ *
+ * @author Gavin King
+ */
+public class PostUpdateEvent extends AbstractEvent {
+ private Object entity;
+ private EntityPersister persister;
+ private Object[] state;
+ private Object[] oldState;
+ private Serializable id;
+
+ public PostUpdateEvent(
+ Object entity,
+ Serializable id,
+ Object[] state,
+ Object[] oldState,
+ EntityPersister persister,
+ EventSource source
+ ) {
+ super(source);
+ this.entity = entity;
+ this.id = id;
+ this.state = state;
+ this.oldState = oldState;
+ this.persister = persister;
+ }
+
+ public Object getEntity() {
+ return entity;
+ }
+ public Serializable getId() {
+ return id;
+ }
+ public Object[] getOldState() {
+ return oldState;
+ }
+ public EntityPersister getPersister() {
+ return persister;
+ }
+ public Object[] getState() {
+ return state;
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/event/PostUpdateEvent.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/PostUpdateEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/PostUpdateEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/PostUpdateEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,13 @@
+//$Id: PostUpdateEventListener.java 7581 2005-07-20 22:48:22Z oneovthafew $
+package org.hibernate.event;
+
+import java.io.Serializable;
+
+/**
+ * Called after updating the datastore
+ *
+ * @author Gavin King
+ */
+public interface PostUpdateEventListener extends Serializable {
+ public void onPostUpdate(PostUpdateEvent event);
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/event/PostUpdateEventListener.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreDeleteEvent.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreDeleteEvent.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreDeleteEvent.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,44 @@
+//$Id: PreDeleteEvent.java 7581 2005-07-20 22:48:22Z oneovthafew $
+package org.hibernate.event;
+
+import java.io.Serializable;
+
+import org.hibernate.persister.entity.EntityPersister;
+
+/**
+ * Occurs before deleting an item from the datastore
+ *
+ * @author Gavin King
+ */
+public class PreDeleteEvent {
+ private Object entity;
+ private EntityPersister persister;
+ private Serializable id;
+ private Object[] deletedState;
+
+ public Object getEntity() {
+ return entity;
+ }
+ public Serializable getId() {
+ return id;
+ }
+ public EntityPersister getPersister() {
+ return persister;
+ }
+ public Object[] getDeletedState() {
+ return deletedState;
+ }
+
+ public PreDeleteEvent(
+ Object entity,
+ Serializable id,
+ Object[] deletedState,
+ EntityPersister persister
+ ) {
+ this.entity = entity;
+ this.persister = persister;
+ this.id = id;
+ this.deletedState = deletedState;
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreDeleteEvent.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreDeleteEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreDeleteEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreDeleteEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,16 @@
+//$Id: PreDeleteEventListener.java 11272 2007-03-12 00:17:45Z epbernard $
+package org.hibernate.event;
+
+import java.io.Serializable;
+
+/**
+ * Called before deleting an item from the datastore
+ *
+ * @author Gavin King
+ */
+public interface PreDeleteEventListener extends Serializable {
+ /**
+ * Return true if the operation should be vetoed
+ */
+ public boolean onPreDelete(PreDeleteEvent event);
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreDeleteEventListener.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreInsertEvent.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreInsertEvent.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreInsertEvent.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,50 @@
+//$Id: PreInsertEvent.java 7850 2005-08-11 19:37:08Z epbernard $
+package org.hibernate.event;
+
+import java.io.Serializable;
+
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.engine.SessionImplementor;
+
+/**
+ * Occurs before inserting an item in the datastore
+ *
+ * @author Gavin King
+ */
+public class PreInsertEvent {
+ private Object entity;
+ private EntityPersister persister;
+ private Object[] state;
+ private Serializable id;
+ private SessionImplementor source;
+
+ public PreInsertEvent(
+ Object entity,
+ Serializable id,
+ Object[] state,
+ EntityPersister persister,
+ SessionImplementor source
+ ) {
+ this.source = source;
+ this.entity = entity;
+ this.id = id;
+ this.state = state;
+ this.persister = persister;
+ }
+
+ public Object getEntity() {
+ return entity;
+ }
+ public Serializable getId() {
+ return id;
+ }
+ public EntityPersister getPersister() {
+ return persister;
+ }
+ public Object[] getState() {
+ return state;
+ }
+ public SessionImplementor getSource() {
+ return source;
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreInsertEvent.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreInsertEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreInsertEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreInsertEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,16 @@
+//$Id: PreInsertEventListener.java 11272 2007-03-12 00:17:45Z epbernard $
+package org.hibernate.event;
+
+import java.io.Serializable;
+
+/**
+ * Called before inserting an item in the datastore
+ *
+ * @author Gavin King
+ */
+public interface PreInsertEventListener extends Serializable {
+ /**
+ * Return true if the operation should be vetoed
+ */
+ public boolean onPreInsert(PreInsertEvent event);
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreInsertEventListener.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreLoadEvent.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreLoadEvent.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreLoadEvent.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,60 @@
+//$Id: PreLoadEvent.java 6929 2005-05-27 03:54:08Z oneovthafew $
+package org.hibernate.event;
+
+import java.io.Serializable;
+
+import org.hibernate.persister.entity.EntityPersister;
+
+/**
+ * Called before injecting property values into a newly
+ * loaded entity instance.
+ *
+ * @author Gavin King
+ */
+public class PreLoadEvent extends AbstractEvent {
+ private Object entity;
+ private Object[] state;
+ private Serializable id;
+ private EntityPersister persister;
+
+ public PreLoadEvent(EventSource session) {
+ super(session);
+ }
+
+ public Object getEntity() {
+ return entity;
+ }
+
+ public Serializable getId() {
+ return id;
+ }
+
+ public EntityPersister getPersister() {
+ return persister;
+ }
+
+ public Object[] getState() {
+ return state;
+ }
+
+ public PreLoadEvent setEntity(Object entity) {
+ this.entity = entity;
+ return this;
+ }
+
+ public PreLoadEvent setId(Serializable id) {
+ this.id = id;
+ return this;
+ }
+
+ public PreLoadEvent setPersister(EntityPersister persister) {
+ this.persister = persister;
+ return this;
+ }
+
+ public PreLoadEvent setState(Object[] state) {
+ this.state = state;
+ return this;
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreLoadEvent.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreLoadEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreLoadEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreLoadEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,14 @@
+//$Id: PreLoadEventListener.java 5006 2004-12-19 20:15:13Z oneovthafew $
+package org.hibernate.event;
+
+import java.io.Serializable;
+
+/**
+ * Called before injecting property values into a newly
+ * loaded entity instance.
+ *
+ * @author Gavin King
+ */
+public interface PreLoadEventListener extends Serializable {
+ public void onPreLoad(PreLoadEvent event);
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreLoadEventListener.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreUpdateEvent.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreUpdateEvent.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreUpdateEvent.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,56 @@
+//$Id: PreUpdateEvent.java 7850 2005-08-11 19:37:08Z epbernard $
+package org.hibernate.event;
+
+import java.io.Serializable;
+
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.engine.SessionImplementor;
+
+/**
+ * Occurs before updating the datastore
+ *
+ * @author Gavin King
+ */
+public class PreUpdateEvent {
+ private Object entity;
+ private EntityPersister persister;
+ private Object[] state;
+ private Object[] oldState;
+ private Serializable id;
+ private SessionImplementor source;
+
+ public PreUpdateEvent(
+ Object entity,
+ Serializable id,
+ Object[] state,
+ Object[] oldState,
+ EntityPersister persister,
+ SessionImplementor source
+ ) {
+ this.source = source;
+ this.entity = entity;
+ this.id = id;
+ this.state = state;
+ this.oldState = oldState;
+ this.persister = persister;
+ }
+
+ public Object getEntity() {
+ return entity;
+ }
+ public Serializable getId() {
+ return id;
+ }
+ public Object[] getOldState() {
+ return oldState;
+ }
+ public EntityPersister getPersister() {
+ return persister;
+ }
+ public Object[] getState() {
+ return state;
+ }
+ public SessionImplementor getSource() {
+ return source;
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreUpdateEvent.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreUpdateEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreUpdateEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreUpdateEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,16 @@
+//$Id: PreUpdateEventListener.java 11272 2007-03-12 00:17:45Z epbernard $
+package org.hibernate.event;
+
+import java.io.Serializable;
+
+/**
+ * Called before updating the datastore
+ *
+ * @author Gavin King
+ */
+public interface PreUpdateEventListener extends Serializable {
+ /**
+ * Return true if the operation should be vetoed
+ */
+ public boolean onPreUpdate(PreUpdateEvent event);
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/event/PreUpdateEventListener.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/RefreshEvent.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/RefreshEvent.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/RefreshEvent.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,39 @@
+//$Id: RefreshEvent.java 6929 2005-05-27 03:54:08Z oneovthafew $
+package org.hibernate.event;
+
+import org.hibernate.LockMode;
+
+/**
+ * Defines an event class for the refreshing of an object.
+ *
+ * @author Steve Ebersole
+ */
+public class RefreshEvent extends AbstractEvent {
+
+ private Object object;
+ private LockMode lockMode = LockMode.READ;
+
+ public RefreshEvent(Object object, EventSource source) {
+ super(source);
+ if (object == null) {
+ throw new IllegalArgumentException("Attempt to generate refresh event with null object");
+ }
+ this.object = object;
+ }
+
+ public RefreshEvent(Object object, LockMode lockMode, EventSource source) {
+ this(object, source);
+ if (lockMode == null) {
+ throw new IllegalArgumentException("Attempt to generate refresh event with null lock mode");
+ }
+ this.lockMode = lockMode;
+ }
+
+ public Object getObject() {
+ return object;
+ }
+
+ public LockMode getLockMode() {
+ return lockMode;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/RefreshEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/RefreshEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/RefreshEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,26 @@
+//$Id: RefreshEventListener.java 7485 2005-07-15 03:35:18Z oneovthafew $
+package org.hibernate.event;
+
+import org.hibernate.HibernateException;
+
+import java.io.Serializable;
+import java.util.Map;
+
+/**
+ * Defines the contract for handling of refresh events generated from a session.
+ *
+ * @author Steve Ebersole
+ */
+public interface RefreshEventListener extends Serializable {
+
+ /**
+ * Handle the given refresh event.
+ *
+ * @param event The refresh event to be handled.
+ * @throws HibernateException
+ */
+ public void onRefresh(RefreshEvent event) throws HibernateException;
+
+ public void onRefresh(RefreshEvent event, Map refreshedAlready) throws HibernateException;
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/ReplicateEvent.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/ReplicateEvent.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/ReplicateEvent.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,62 @@
+//$Id: ReplicateEvent.java 6929 2005-05-27 03:54:08Z oneovthafew $
+package org.hibernate.event;
+
+import org.hibernate.ReplicationMode;
+
+/**
+ * Defines an event class for the replication of an entity.
+ *
+ * @author Steve Ebersole
+ */
+public class ReplicateEvent extends AbstractEvent {
+
+ private Object object;
+ private ReplicationMode replicationMode;
+ private String entityName;
+
+ public ReplicateEvent(Object object, ReplicationMode replicationMode, EventSource source) {
+ this(null, object, replicationMode, source);
+ }
+
+ public ReplicateEvent(String entityName, Object object, ReplicationMode replicationMode, EventSource source) {
+ super(source);
+ this.entityName = entityName;
+
+ if ( object == null ) {
+ throw new IllegalArgumentException(
+ "attempt to create replication strategy with null entity"
+ );
+ }
+ if ( replicationMode == null ) {
+ throw new IllegalArgumentException(
+ "attempt to create replication strategy with null replication mode"
+ );
+ }
+
+ this.object = object;
+ this.replicationMode = replicationMode;
+ }
+
+ public Object getObject() {
+ return object;
+ }
+
+ public void setObject(Object object) {
+ this.object = object;
+ }
+
+ public ReplicationMode getReplicationMode() {
+ return replicationMode;
+ }
+
+ public void setReplicationMode(ReplicationMode replicationMode) {
+ this.replicationMode = replicationMode;
+ }
+
+ public String getEntityName() {
+ return entityName;
+ }
+ public void setEntityName(String entityName) {
+ this.entityName = entityName;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/ReplicateEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/ReplicateEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/ReplicateEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+//$Id: ReplicateEventListener.java 4185 2004-08-08 11:24:56Z oneovthafew $
+package org.hibernate.event;
+
+import org.hibernate.HibernateException;
+
+import java.io.Serializable;
+
+/**
+ * Defines the contract for handling of replicate events generated from a session.
+ *
+ * @author Steve Ebersole
+ */
+public interface ReplicateEventListener extends Serializable {
+
+ /** Handle the given replicate event.
+ *
+ * @param event The replicate event to be handled.
+ * @throws HibernateException
+ */
+ public void onReplicate(ReplicateEvent event) throws HibernateException;
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/SaveOrUpdateEvent.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/SaveOrUpdateEvent.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/SaveOrUpdateEvent.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,94 @@
+//$Id: SaveOrUpdateEvent.java 7785 2005-08-08 23:24:44Z oneovthafew $
+package org.hibernate.event;
+
+import java.io.Serializable;
+
+import org.hibernate.engine.EntityEntry;
+
+/**
+ * An event class for saveOrUpdate()
+ *
+ * @author Steve Ebersole
+ */
+public class SaveOrUpdateEvent extends AbstractEvent {
+
+ private Object object;
+ private Serializable requestedId;
+ private String entityName;
+ private Object entity;
+ private EntityEntry entry;
+ private Serializable resultId;
+
+ public SaveOrUpdateEvent(String entityName, Object original, EventSource source) {
+ this(original, source);
+ this.entityName = entityName;
+ }
+
+ public SaveOrUpdateEvent(String entityName, Object original, Serializable id, EventSource source) {
+ this(entityName, original, source);
+ this.requestedId = id;
+ if ( requestedId == null ) {
+ throw new IllegalArgumentException(
+ "attempt to create saveOrUpdate event with null identifier"
+ );
+ }
+ }
+
+ public SaveOrUpdateEvent(Object object, EventSource source) {
+ super(source);
+ if ( object == null ) {
+ throw new IllegalArgumentException(
+ "attempt to create saveOrUpdate event with null entity"
+ );
+ }
+ this.object = object;
+ }
+
+ public Object getObject() {
+ return object;
+ }
+
+ public void setObject(Object object) {
+ this.object = object;
+ }
+
+ public Serializable getRequestedId() {
+ return requestedId;
+ }
+
+ public void setRequestedId(Serializable requestedId) {
+ this.requestedId = requestedId;
+ }
+
+ public String getEntityName() {
+ return entityName;
+ }
+
+ public void setEntityName(String entityName) {
+ this.entityName = entityName;
+ }
+
+ public Object getEntity() {
+ return entity;
+ }
+
+ public void setEntity(Object entity) {
+ this.entity = entity;
+ }
+
+ public EntityEntry getEntry() {
+ return entry;
+ }
+
+ public void setEntry(EntityEntry entry) {
+ this.entry = entry;
+ }
+
+ public Serializable getResultId() {
+ return resultId;
+ }
+
+ public void setResultId(Serializable resultId) {
+ this.resultId = resultId;
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/event/SaveOrUpdateEvent.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/SaveOrUpdateEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/SaveOrUpdateEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/SaveOrUpdateEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,23 @@
+//$Id: SaveOrUpdateEventListener.java 7785 2005-08-08 23:24:44Z oneovthafew $
+package org.hibernate.event;
+
+import java.io.Serializable;
+
+import org.hibernate.HibernateException;
+
+/**
+ * Defines the contract for handling of update events generated from a session.
+ *
+ * @author Steve Ebersole
+ */
+public interface SaveOrUpdateEventListener extends Serializable {
+
+ /**
+ * Handle the given update event.
+ *
+ * @param event The update event to be handled.
+ * @throws HibernateException
+ */
+ public void onSaveOrUpdate(SaveOrUpdateEvent event) throws HibernateException;
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/event/SaveOrUpdateEventListener.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/AbstractFlushingEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/AbstractFlushingEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/AbstractFlushingEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,356 @@
+//$Id: AbstractFlushingEventListener.java 11402 2007-04-11 14:24:35Z steve.ebersole at jboss.com $
+package org.hibernate.event.def;
+
+import java.io.Serializable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.HibernateException;
+import org.hibernate.action.CollectionRecreateAction;
+import org.hibernate.action.CollectionRemoveAction;
+import org.hibernate.action.CollectionUpdateAction;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.engine.ActionQueue;
+import org.hibernate.engine.Cascade;
+import org.hibernate.engine.CascadingAction;
+import org.hibernate.engine.CollectionEntry;
+import org.hibernate.engine.CollectionKey;
+import org.hibernate.engine.Collections;
+import org.hibernate.engine.EntityEntry;
+import org.hibernate.engine.PersistenceContext;
+import org.hibernate.engine.Status;
+import org.hibernate.event.EventSource;
+import org.hibernate.event.FlushEntityEvent;
+import org.hibernate.event.FlushEntityEventListener;
+import org.hibernate.event.FlushEvent;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.pretty.Printer;
+import org.hibernate.util.IdentityMap;
+import org.hibernate.util.LazyIterator;
+
+/**
+ * A convenience base class for listeners whose functionality results in flushing.
+ *
+ * @author Steve Eberole
+ */
+public abstract class AbstractFlushingEventListener implements Serializable {
+
+ private static final Log log = LogFactory.getLog(AbstractFlushingEventListener.class);
+
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ // Pre-flushing section
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Coordinates the processing necessary to get things ready for executions
+ * as db calls by preping the session caches and moving the appropriate
+ * entities and collections to their respective execution queues.
+ *
+ * @param event The flush event.
+ * @throws HibernateException Error flushing caches to execution queues.
+ */
+ protected void flushEverythingToExecutions(FlushEvent event) throws HibernateException {
+
+ log.trace("flushing session");
+
+ EventSource session = event.getSession();
+
+ final PersistenceContext persistenceContext = session.getPersistenceContext();
+ session.getInterceptor().preFlush( new LazyIterator( persistenceContext.getEntitiesByKey() ) );
+
+ prepareEntityFlushes(session);
+ // we could move this inside if we wanted to
+ // tolerate collection initializations during
+ // collection dirty checking:
+ prepareCollectionFlushes(session);
+ // now, any collections that are initialized
+ // inside this block do not get updated - they
+ // are ignored until the next flush
+
+ persistenceContext.setFlushing(true);
+ try {
+ flushEntities(event);
+ flushCollections(session);
+ }
+ finally {
+ persistenceContext.setFlushing(false);
+ }
+
+ //some statistics
+ if ( log.isDebugEnabled() ) {
+ log.debug( "Flushed: " +
+ session.getActionQueue().numberOfInsertions() + " insertions, " +
+ session.getActionQueue().numberOfUpdates() + " updates, " +
+ session.getActionQueue().numberOfDeletions() + " deletions to " +
+ persistenceContext.getEntityEntries().size() + " objects"
+ );
+ log.debug( "Flushed: " +
+ session.getActionQueue().numberOfCollectionCreations() + " (re)creations, " +
+ session.getActionQueue().numberOfCollectionUpdates() + " updates, " +
+ session.getActionQueue().numberOfCollectionRemovals() + " removals to " +
+ persistenceContext.getCollectionEntries().size() + " collections"
+ );
+ new Printer( session.getFactory() ).toString(
+ persistenceContext.getEntitiesByKey().values().iterator(),
+ session.getEntityMode()
+ );
+ }
+ }
+
+ /**
+ * process cascade save/update at the start of a flush to discover
+ * any newly referenced entity that must be passed to saveOrUpdate(),
+ * and also apply orphan delete
+ */
+ private void prepareEntityFlushes(EventSource session) throws HibernateException {
+
+ log.debug("processing flush-time cascades");
+
+ final Map.Entry[] list = IdentityMap.concurrentEntries( session.getPersistenceContext().getEntityEntries() );
+ //safe from concurrent modification because of how entryList() is implemented on IdentityMap
+ final int size = list.length;
+ final Object anything = getAnything();
+ for ( int i=0; i<size; i++ ) {
+ Map.Entry me = list[i];
+ EntityEntry entry = (EntityEntry) me.getValue();
+ Status status = entry.getStatus();
+ if ( status == Status.MANAGED || status == Status.SAVING ) {
+ cascadeOnFlush( session, entry.getPersister(), me.getKey(), anything );
+ }
+ }
+ }
+
+ private void cascadeOnFlush(EventSource session, EntityPersister persister, Object object, Object anything)
+ throws HibernateException {
+ session.getPersistenceContext().incrementCascadeLevel();
+ try {
+ new Cascade( getCascadingAction(), Cascade.BEFORE_FLUSH, session )
+ .cascade( persister, object, anything );
+ }
+ finally {
+ session.getPersistenceContext().decrementCascadeLevel();
+ }
+ }
+
+ protected Object getAnything() { return null; }
+
+ protected CascadingAction getCascadingAction() {
+ return CascadingAction.SAVE_UPDATE;
+ }
+
+ /**
+ * Initialize the flags of the CollectionEntry, including the
+ * dirty check.
+ */
+ private void prepareCollectionFlushes(SessionImplementor session) throws HibernateException {
+
+ // Initialize dirty flags for arrays + collections with composite elements
+ // and reset reached, doupdate, etc.
+
+ log.debug("dirty checking collections");
+
+ final List list = IdentityMap.entries( session.getPersistenceContext().getCollectionEntries() );
+ final int size = list.size();
+ for ( int i = 0; i < size; i++ ) {
+ Map.Entry e = ( Map.Entry ) list.get( i );
+ ( (CollectionEntry) e.getValue() ).preFlush( (PersistentCollection) e.getKey() );
+ }
+ }
+
+ /**
+ * 1. detect any dirty entities
+ * 2. schedule any entity updates
+ * 3. search out any reachable collections
+ */
+ private void flushEntities(FlushEvent event) throws HibernateException {
+
+ log.trace("Flushing entities and processing referenced collections");
+
+ // Among other things, updateReachables() will recursively load all
+ // collections that are moving roles. This might cause entities to
+ // be loaded.
+
+ // So this needs to be safe from concurrent modification problems.
+ // It is safe because of how IdentityMap implements entrySet()
+
+ final EventSource source = event.getSession();
+
+ final Map.Entry[] list = IdentityMap.concurrentEntries( source.getPersistenceContext().getEntityEntries() );
+ final int size = list.length;
+ for ( int i = 0; i < size; i++ ) {
+
+ // Update the status of the object and if necessary, schedule an update
+
+ Map.Entry me = list[i];
+ EntityEntry entry = (EntityEntry) me.getValue();
+ Status status = entry.getStatus();
+
+ if ( status != Status.LOADING && status != Status.GONE ) {
+ FlushEntityEvent entityEvent = new FlushEntityEvent( source, me.getKey(), entry );
+ FlushEntityEventListener[] listeners = source.getListeners().getFlushEntityEventListeners();
+ for ( int j = 0; j < listeners.length; j++ ) {
+ listeners[j].onFlushEntity(entityEvent);
+ }
+ }
+ }
+
+ source.getActionQueue().sortActions();
+ }
+
+ /**
+ * process any unreferenced collections and then inspect all known collections,
+ * scheduling creates/removes/updates
+ */
+ private void flushCollections(EventSource session) throws HibernateException {
+
+ log.trace("Processing unreferenced collections");
+
+ List list = IdentityMap.entries( session.getPersistenceContext().getCollectionEntries() );
+ int size = list.size();
+ for ( int i = 0; i < size; i++ ) {
+ Map.Entry me = ( Map.Entry ) list.get( i );
+ CollectionEntry ce = (CollectionEntry) me.getValue();
+ if ( !ce.isReached() && !ce.isIgnore() ) {
+ Collections.processUnreachableCollection( (PersistentCollection) me.getKey(), session );
+ }
+ }
+
+ // Schedule updates to collections:
+
+ log.trace( "Scheduling collection removes/(re)creates/updates" );
+
+ list = IdentityMap.entries( session.getPersistenceContext().getCollectionEntries() );
+ size = list.size();
+ ActionQueue actionQueue = session.getActionQueue();
+ for ( int i = 0; i < size; i++ ) {
+ Map.Entry me = (Map.Entry) list.get(i);
+ PersistentCollection coll = (PersistentCollection) me.getKey();
+ CollectionEntry ce = (CollectionEntry) me.getValue();
+
+ if ( ce.isDorecreate() ) {
+ session.getInterceptor().onCollectionRecreate( coll, ce.getCurrentKey() );
+ actionQueue.addAction(
+ new CollectionRecreateAction(
+ coll,
+ ce.getCurrentPersister(),
+ ce.getCurrentKey(),
+ session
+ )
+ );
+ }
+ if ( ce.isDoremove() ) {
+ session.getInterceptor().onCollectionRemove( coll, ce.getLoadedKey() );
+ actionQueue.addAction(
+ new CollectionRemoveAction(
+ coll,
+ ce.getLoadedPersister(),
+ ce.getLoadedKey(),
+ ce.isSnapshotEmpty(coll),
+ session
+ )
+ );
+ }
+ if ( ce.isDoupdate() ) {
+ session.getInterceptor().onCollectionUpdate( coll, ce.getLoadedKey() );
+ actionQueue.addAction(
+ new CollectionUpdateAction(
+ coll,
+ ce.getLoadedPersister(),
+ ce.getLoadedKey(),
+ ce.isSnapshotEmpty(coll),
+ session
+ )
+ );
+ }
+
+ }
+
+ actionQueue.sortCollectionActions();
+
+ }
+
+ /**
+ * Execute all SQL and second-level cache updates, in a
+ * special order so that foreign-key constraints cannot
+ * be violated:
+ * <ol>
+ * <li> Inserts, in the order they were performed
+ * <li> Updates
+ * <li> Deletion of collection elements
+ * <li> Insertion of collection elements
+ * <li> Deletes, in the order they were performed
+ * </ol>
+ */
+ protected void performExecutions(EventSource session) throws HibernateException {
+
+ log.trace("executing flush");
+
+ try {
+ session.getJDBCContext().getConnectionManager().flushBeginning();
+ // we need to lock the collection caches before
+ // executing entity inserts/updates in order to
+ // account for bidi associations
+ session.getActionQueue().prepareActions();
+ session.getActionQueue().executeActions();
+ }
+ catch (HibernateException he) {
+ log.error("Could not synchronize database state with session", he);
+ throw he;
+ }
+ finally {
+ session.getJDBCContext().getConnectionManager().flushEnding();
+ }
+ }
+
+
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ // Post-flushing section
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * 1. Recreate the collection key -> collection map
+ * 2. rebuild the collection entries
+ * 3. call Interceptor.postFlush()
+ */
+ protected void postFlush(SessionImplementor session) throws HibernateException {
+
+ log.trace( "post flush" );
+
+ final PersistenceContext persistenceContext = session.getPersistenceContext();
+ persistenceContext.getCollectionsByKey().clear();
+ persistenceContext.getBatchFetchQueue()
+ .clearSubselects(); //the database has changed now, so the subselect results need to be invalidated
+
+ Iterator iter = persistenceContext.getCollectionEntries().entrySet().iterator();
+ while ( iter.hasNext() ) {
+ Map.Entry me = (Map.Entry) iter.next();
+ CollectionEntry collectionEntry = (CollectionEntry) me.getValue();
+ PersistentCollection persistentCollection = (PersistentCollection) me.getKey();
+ collectionEntry.postFlush(persistentCollection);
+ if ( collectionEntry.getLoadedPersister() == null ) {
+ //if the collection is dereferenced, remove from the session cache
+ //iter.remove(); //does not work, since the entrySet is not backed by the set
+ persistenceContext.getCollectionEntries()
+ .remove(persistentCollection);
+ }
+ else {
+ //otherwise recreate the mapping between the collection and its key
+ CollectionKey collectionKey = new CollectionKey(
+ collectionEntry.getLoadedPersister(),
+ collectionEntry.getLoadedKey(),
+ session.getEntityMode()
+ );
+ persistenceContext.getCollectionsByKey()
+ .put(collectionKey, persistentCollection);
+ }
+ }
+
+ session.getInterceptor().postFlush( new LazyIterator( persistenceContext.getEntitiesByKey() ) );
+
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/AbstractLockUpgradeEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/AbstractLockUpgradeEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/AbstractLockUpgradeEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,100 @@
+//$Id: AbstractLockUpgradeEventListener.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.event.def;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.hibernate.LockMode;
+import org.hibernate.ObjectDeletedException;
+import org.hibernate.cache.CacheKey;
+import org.hibernate.cache.access.SoftLock;
+import org.hibernate.engine.EntityEntry;
+import org.hibernate.engine.Status;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.pretty.MessageHelper;
+
+/**
+ * A convenience base class for listeners that respond to requests to perform a
+ * pessimistic lock upgrade on an entity.
+ *
+ * @author Gavin King
+ */
+public class AbstractLockUpgradeEventListener extends AbstractReassociateEventListener {
+
+ private static final Log log = LogFactory.getLog(AbstractLockUpgradeEventListener.class);
+
+ /**
+ * Performs a pessimistic lock upgrade on a given entity, if needed.
+ *
+ * @param object The entity for which to upgrade the lock.
+ * @param entry The entity's EntityEntry instance.
+ * @param requestedLockMode The lock mode being requested for locking.
+ * @param source The session which is the source of the event being processed.
+ */
+ protected void upgradeLock(Object object, EntityEntry entry, LockMode requestedLockMode, SessionImplementor source) {
+
+ if ( requestedLockMode.greaterThan( entry.getLockMode() ) ) {
+ // The user requested a "greater" (i.e. more restrictive) form of
+ // pessimistic lock
+
+ if ( entry.getStatus() != Status.MANAGED ) {
+ throw new ObjectDeletedException(
+ "attempted to lock a deleted instance",
+ entry.getId(),
+ entry.getPersister().getEntityName()
+ );
+ }
+
+ final EntityPersister persister = entry.getPersister();
+
+ if ( log.isTraceEnabled() )
+ log.trace(
+ "locking " +
+ MessageHelper.infoString( persister, entry.getId(), source.getFactory() ) +
+ " in mode: " +
+ requestedLockMode
+ );
+
+ final SoftLock lock;
+ final CacheKey ck;
+ if ( persister.hasCache() ) {
+ ck = new CacheKey(
+ entry.getId(),
+ persister.getIdentifierType(),
+ persister.getRootEntityName(),
+ source.getEntityMode(),
+ source.getFactory()
+ );
+ lock = persister.getCacheAccessStrategy().lockItem( ck, entry.getVersion() );
+ }
+ else {
+ ck = null;
+ lock = null;
+ }
+
+ try {
+ if ( persister.isVersioned() && requestedLockMode == LockMode.FORCE ) {
+ // todo : should we check the current isolation mode explicitly?
+ Object nextVersion = persister.forceVersionIncrement(
+ entry.getId(), entry.getVersion(), source
+ );
+ entry.forceLocked( object, nextVersion );
+ }
+ else {
+ persister.lock( entry.getId(), entry.getVersion(), object, requestedLockMode, source );
+ }
+ entry.setLockMode(requestedLockMode);
+ }
+ finally {
+ // the database now holds a lock + the object is flushed from the cache,
+ // so release the soft lock
+ if ( persister.hasCache() ) {
+ persister.getCacheAccessStrategy().unlockItem( ck, lock );
+ }
+ }
+
+ }
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/AbstractReassociateEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/AbstractReassociateEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/AbstractReassociateEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,87 @@
+//$Id: AbstractReassociateEventListener.java 10948 2006-12-07 21:53:10Z steve.ebersole at jboss.com $
+package org.hibernate.event.def;
+
+import java.io.Serializable;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.hibernate.LockMode;
+import org.hibernate.engine.EntityEntry;
+import org.hibernate.engine.EntityKey;
+import org.hibernate.engine.Status;
+import org.hibernate.engine.Versioning;
+import org.hibernate.event.AbstractEvent;
+import org.hibernate.event.EventSource;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.pretty.MessageHelper;
+import org.hibernate.type.TypeFactory;
+
+/**
+ * A convenience base class for listeners that respond to requests to reassociate an entity
+ * to a session ( such as through lock() or update() ).
+ *
+ * @author Gavin King
+ */
+public class AbstractReassociateEventListener implements Serializable {
+
+ private static final Log log = LogFactory.getLog( AbstractReassociateEventListener.class );
+
+ /**
+ * Associates a given entity (either transient or associated with another session) to
+ * the given session.
+ *
+ * @param event The event triggering the re-association
+ * @param object The entity to be associated
+ * @param id The id of the entity.
+ * @param persister The entity's persister instance.
+ *
+ * @return An EntityEntry representing the entity within this session.
+ */
+ protected final EntityEntry reassociate(AbstractEvent event, Object object, Serializable id, EntityPersister persister) {
+
+ if ( log.isTraceEnabled() ) {
+ log.trace(
+ "reassociating transient instance: " +
+ MessageHelper.infoString( persister, id, event.getSession().getFactory() )
+ );
+ }
+
+ EventSource source = event.getSession();
+ EntityKey key = new EntityKey( id, persister, source.getEntityMode() );
+
+ source.getPersistenceContext().checkUniqueness( key, object );
+
+ //get a snapshot
+ Object[] values = persister.getPropertyValues( object, source.getEntityMode() );
+ TypeFactory.deepCopy(
+ values,
+ persister.getPropertyTypes(),
+ persister.getPropertyUpdateability(),
+ values,
+ source
+ );
+ Object version = Versioning.getVersion( values, persister );
+
+ EntityEntry newEntry = source.getPersistenceContext().addEntity(
+ object,
+ Status.MANAGED,
+ values,
+ key,
+ version,
+ LockMode.NONE,
+ true,
+ persister,
+ false,
+ true //will be ignored, using the existing Entry instead
+ );
+
+ new OnLockVisitor( source, id, object ).process( object, persister );
+
+ persister.afterReassociate( object, source );
+
+ return newEntry;
+
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/AbstractSaveEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/AbstractSaveEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/AbstractSaveEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,542 @@
+//$Id: AbstractSaveEventListener.java 10948 2006-12-07 21:53:10Z steve.ebersole at jboss.com $
+package org.hibernate.event.def;
+
+import java.io.Serializable;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.hibernate.LockMode;
+import org.hibernate.NonUniqueObjectException;
+import org.hibernate.action.EntityIdentityInsertAction;
+import org.hibernate.action.EntityInsertAction;
+import org.hibernate.classic.Lifecycle;
+import org.hibernate.classic.Validatable;
+import org.hibernate.engine.Cascade;
+import org.hibernate.engine.CascadingAction;
+import org.hibernate.engine.EntityEntry;
+import org.hibernate.engine.EntityKey;
+import org.hibernate.engine.ForeignKeys;
+import org.hibernate.engine.Nullability;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.Status;
+import org.hibernate.engine.Versioning;
+import org.hibernate.event.EventSource;
+import org.hibernate.id.IdentifierGenerationException;
+import org.hibernate.id.IdentifierGeneratorFactory;
+import org.hibernate.intercept.FieldInterceptionHelper;
+import org.hibernate.intercept.FieldInterceptor;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.pretty.MessageHelper;
+import org.hibernate.type.Type;
+import org.hibernate.type.TypeFactory;
+
+/**
+ * A convenience bas class for listeners responding to save events.
+ *
+ * @author Steve Ebersole.
+ */
+public abstract class AbstractSaveEventListener extends AbstractReassociateEventListener {
+
+ protected static final int PERSISTENT = 0;
+ protected static final int TRANSIENT = 1;
+ protected static final int DETACHED = 2;
+ protected static final int DELETED = 3;
+
+ private static final Log log = LogFactory.getLog( AbstractSaveEventListener.class );
+
+ /**
+ * Prepares the save call using the given requested id.
+ *
+ * @param entity The entity to be saved.
+ * @param requestedId The id to which to associate the entity.
+ * @param entityName The name of the entity being saved.
+ * @param anything Generally cascade-specific information.
+ * @param source The session which is the source of this save event.
+ *
+ * @return The id used to save the entity.
+ */
+ protected Serializable saveWithRequestedId(
+ Object entity,
+ Serializable requestedId,
+ String entityName,
+ Object anything,
+ EventSource source) {
+ return performSave(
+ entity,
+ requestedId,
+ source.getEntityPersister( entityName, entity ),
+ false,
+ anything,
+ source,
+ true
+ );
+ }
+
+ /**
+ * Prepares the save call using a newly generated id.
+ *
+ * @param entity The entity to be saved
+ * @param entityName The entity-name for the entity to be saved
+ * @param anything Generally cascade-specific information.
+ * @param source The session which is the source of this save event.
+ * @param requiresImmediateIdAccess does the event context require
+ * access to the identifier immediately after execution of this method (if
+ * not, post-insert style id generators may be postponed if we are outside
+ * a transaction).
+ *
+ * @return The id used to save the entity; may be null depending on the
+ * type of id generator used and the requiresImmediateIdAccess value
+ */
+ protected Serializable saveWithGeneratedId(
+ Object entity,
+ String entityName,
+ Object anything,
+ EventSource source,
+ boolean requiresImmediateIdAccess) {
+ EntityPersister persister = source.getEntityPersister( entityName, entity );
+ Serializable generatedId = persister.getIdentifierGenerator().generate( source, entity );
+ if ( generatedId == null ) {
+ throw new IdentifierGenerationException( "null id generated for:" + entity.getClass() );
+ }
+ else if ( generatedId == IdentifierGeneratorFactory.SHORT_CIRCUIT_INDICATOR ) {
+ return source.getIdentifier( entity );
+ }
+ else if ( generatedId == IdentifierGeneratorFactory.POST_INSERT_INDICATOR ) {
+ return performSave( entity, null, persister, true, anything, source, requiresImmediateIdAccess );
+ }
+ else {
+
+ if ( log.isDebugEnabled() ) {
+ log.debug(
+ "generated identifier: " +
+ persister.getIdentifierType().toLoggableString( generatedId, source.getFactory() ) +
+ ", using strategy: " +
+ persister.getIdentifierGenerator().getClass().getName()
+ //TODO: define toString()s for generators
+ );
+ }
+
+ return performSave( entity, generatedId, persister, false, anything, source, true );
+ }
+ }
+
+ /**
+ * Ppepares the save call by checking the session caches for a pre-existing
+ * entity and performing any lifecycle callbacks.
+ *
+ * @param entity The entity to be saved.
+ * @param id The id by which to save the entity.
+ * @param persister The entity's persister instance.
+ * @param useIdentityColumn Is an identity column being used?
+ * @param anything Generally cascade-specific information.
+ * @param source The session from which the event originated.
+ * @param requiresImmediateIdAccess does the event context require
+ * access to the identifier immediately after execution of this method (if
+ * not, post-insert style id generators may be postponed if we are outside
+ * a transaction).
+ *
+ * @return The id used to save the entity; may be null depending on the
+ * type of id generator used and the requiresImmediateIdAccess value
+ */
+ protected Serializable performSave(
+ Object entity,
+ Serializable id,
+ EntityPersister persister,
+ boolean useIdentityColumn,
+ Object anything,
+ EventSource source,
+ boolean requiresImmediateIdAccess) {
+
+ if ( log.isTraceEnabled() ) {
+ log.trace(
+ "saving " +
+ MessageHelper.infoString( persister, id, source.getFactory() )
+ );
+ }
+
+ EntityKey key;
+ if ( !useIdentityColumn ) {
+ key = new EntityKey( id, persister, source.getEntityMode() );
+ Object old = source.getPersistenceContext().getEntity( key );
+ if ( old != null ) {
+ if ( source.getPersistenceContext().getEntry( old ).getStatus() == Status.DELETED ) {
+ source.forceFlush( source.getPersistenceContext().getEntry( old ) );
+ }
+ else {
+ throw new NonUniqueObjectException( id, persister.getEntityName() );
+ }
+ }
+ persister.setIdentifier( entity, id, source.getEntityMode() );
+ }
+ else {
+ key = null;
+ }
+
+ if ( invokeSaveLifecycle( entity, persister, source ) ) {
+ return id; //EARLY EXIT
+ }
+
+ return performSaveOrReplicate(
+ entity,
+ key,
+ persister,
+ useIdentityColumn,
+ anything,
+ source,
+ requiresImmediateIdAccess
+ );
+ }
+
+ protected boolean invokeSaveLifecycle(Object entity, EntityPersister persister, EventSource source) {
+ // Sub-insertions should occur before containing insertion so
+ // Try to do the callback now
+ if ( persister.implementsLifecycle( source.getEntityMode() ) ) {
+ log.debug( "calling onSave()" );
+ if ( ( ( Lifecycle ) entity ).onSave( source ) ) {
+ log.debug( "insertion vetoed by onSave()" );
+ return true;
+ }
+ }
+ return false;
+ }
+
+ protected void validate(Object entity, EntityPersister persister, EventSource source) {
+ if ( persister.implementsValidatable( source.getEntityMode() ) ) {
+ ( ( Validatable ) entity ).validate();
+ }
+ }
+
+ /**
+ * Performs all the actual work needed to save an entity (well to get the save moved to
+ * the execution queue).
+ *
+ * @param entity The entity to be saved
+ * @param key The id to be used for saving the entity (or null, in the case of identity columns)
+ * @param persister The entity's persister instance.
+ * @param useIdentityColumn Should an identity column be used for id generation?
+ * @param anything Generally cascade-specific information.
+ * @param source The session which is the source of the current event.
+ * @param requiresImmediateIdAccess Is access to the identifier required immediately
+ * after the completion of the save? persist(), for example, does not require this...
+ *
+ * @return The id used to save the entity; may be null depending on the
+ * type of id generator used and the requiresImmediateIdAccess value
+ */
+ protected Serializable performSaveOrReplicate(
+ Object entity,
+ EntityKey key,
+ EntityPersister persister,
+ boolean useIdentityColumn,
+ Object anything,
+ EventSource source,
+ boolean requiresImmediateIdAccess) {
+
+ validate( entity, persister, source );
+
+ Serializable id = key == null ? null : key.getIdentifier();
+
+ boolean inTxn = source.getJDBCContext().isTransactionInProgress();
+ boolean shouldDelayIdentityInserts = !inTxn && !requiresImmediateIdAccess;
+
+ if ( useIdentityColumn && !shouldDelayIdentityInserts ) {
+ log.trace( "executing insertions" );
+ source.getActionQueue().executeInserts();
+ }
+
+ // Put a placeholder in entries, so we don't recurse back and try to save() the
+ // same object again. QUESTION: should this be done before onSave() is called?
+ // likewise, should it be done before onUpdate()?
+ source.getPersistenceContext().addEntry(
+ entity,
+ Status.SAVING,
+ null,
+ null,
+ id,
+ null,
+ LockMode.WRITE,
+ useIdentityColumn,
+ persister,
+ false,
+ false
+ );
+
+ cascadeBeforeSave( source, persister, entity, anything );
+
+ Object[] values = persister.getPropertyValuesToInsert( entity, getMergeMap( anything ), source );
+ Type[] types = persister.getPropertyTypes();
+
+ boolean substitute = substituteValuesIfNecessary( entity, id, values, persister, source );
+
+ if ( persister.hasCollections() ) {
+ substitute = substitute || visitCollectionsBeforeSave( entity, id, values, types, source );
+ }
+
+ if ( substitute ) {
+ persister.setPropertyValues( entity, values, source.getEntityMode() );
+ }
+
+ TypeFactory.deepCopy(
+ values,
+ types,
+ persister.getPropertyUpdateability(),
+ values,
+ source
+ );
+
+ new ForeignKeys.Nullifier( entity, false, useIdentityColumn, source )
+ .nullifyTransientReferences( values, types );
+ new Nullability( source ).checkNullability( values, persister, false );
+
+ if ( useIdentityColumn ) {
+ EntityIdentityInsertAction insert = new EntityIdentityInsertAction(
+ values, entity, persister, source, shouldDelayIdentityInserts
+ );
+ if ( !shouldDelayIdentityInserts ) {
+ log.debug( "executing identity-insert immediately" );
+ source.getActionQueue().execute( insert );
+ id = insert.getGeneratedId();
+ //now done in EntityIdentityInsertAction
+ //persister.setIdentifier( entity, id, source.getEntityMode() );
+ key = new EntityKey( id, persister, source.getEntityMode() );
+ source.getPersistenceContext().checkUniqueness( key, entity );
+ //source.getBatcher().executeBatch(); //found another way to ensure that all batched joined inserts have been executed
+ }
+ else {
+ log.debug( "delaying identity-insert due to no transaction in progress" );
+ source.getActionQueue().addAction( insert );
+ key = insert.getDelayedEntityKey();
+ }
+ }
+
+ Object version = Versioning.getVersion( values, persister );
+ source.getPersistenceContext().addEntity(
+ entity,
+ Status.MANAGED,
+ values,
+ key,
+ version,
+ LockMode.WRITE,
+ useIdentityColumn,
+ persister,
+ isVersionIncrementDisabled(),
+ false
+ );
+ //source.getPersistenceContext().removeNonExist( new EntityKey( id, persister, source.getEntityMode() ) );
+
+ if ( !useIdentityColumn ) {
+ source.getActionQueue().addAction(
+ new EntityInsertAction( id, values, entity, version, persister, source )
+ );
+ }
+
+ cascadeAfterSave( source, persister, entity, anything );
+
+ markInterceptorDirty( entity, persister, source );
+
+ return id;
+ }
+
+ private void markInterceptorDirty(Object entity, EntityPersister persister, EventSource source) {
+ if ( FieldInterceptionHelper.isInstrumented( entity ) ) {
+ FieldInterceptor interceptor = FieldInterceptionHelper.injectFieldInterceptor(
+ entity,
+ persister.getEntityName(),
+ null,
+ source
+ );
+ interceptor.dirty();
+ }
+ }
+
+ protected Map getMergeMap(Object anything) {
+ return null;
+ }
+
+ /**
+ * After the save, will te version number be incremented
+ * if the instance is modified?
+ *
+ * @return True if the version will be incremented on an entity change after save;
+ * false otherwise.
+ */
+ protected boolean isVersionIncrementDisabled() {
+ return false;
+ }
+
+ protected boolean visitCollectionsBeforeSave(Object entity, Serializable id, Object[] values, Type[] types, EventSource source) {
+ WrapVisitor visitor = new WrapVisitor( source );
+ // substitutes into values by side-effect
+ visitor.processEntityPropertyValues( values, types );
+ return visitor.isSubstitutionRequired();
+ }
+
+ /**
+ * Perform any property value substitution that is necessary
+ * (interceptor callback, version initialization...)
+ *
+ * @param entity The entity
+ * @param id The entity identifier
+ * @param values The snapshot entity state
+ * @param persister The entity persister
+ * @param source The originating session
+ *
+ * @return True if the snapshot state changed such that
+ * reinjection of the values into the entity is required.
+ */
+ protected boolean substituteValuesIfNecessary(
+ Object entity,
+ Serializable id,
+ Object[] values,
+ EntityPersister persister,
+ SessionImplementor source) {
+ boolean substitute = source.getInterceptor().onSave(
+ entity,
+ id,
+ values,
+ persister.getPropertyNames(),
+ persister.getPropertyTypes()
+ );
+
+ //keep the existing version number in the case of replicate!
+ if ( persister.isVersioned() ) {
+ substitute = Versioning.seedVersion(
+ values,
+ persister.getVersionProperty(),
+ persister.getVersionType(),
+ source
+ ) || substitute;
+ }
+ return substitute;
+ }
+
+ /**
+ * Handles the calls needed to perform pre-save cascades for the given entity.
+ *
+ * @param source The session from whcih the save event originated.
+ * @param persister The entity's persister instance.
+ * @param entity The entity to be saved.
+ * @param anything Generally cascade-specific data
+ */
+ protected void cascadeBeforeSave(
+ EventSource source,
+ EntityPersister persister,
+ Object entity,
+ Object anything) {
+
+ // cascade-save to many-to-one BEFORE the parent is saved
+ source.getPersistenceContext().incrementCascadeLevel();
+ try {
+ new Cascade( getCascadeAction(), Cascade.BEFORE_INSERT_AFTER_DELETE, source )
+ .cascade( persister, entity, anything );
+ }
+ finally {
+ source.getPersistenceContext().decrementCascadeLevel();
+ }
+ }
+
+ /**
+ * Handles to calls needed to perform post-save cascades.
+ *
+ * @param source The session from which the event originated.
+ * @param persister The entity's persister instance.
+ * @param entity The entity beng saved.
+ * @param anything Generally cascade-specific data
+ */
+ protected void cascadeAfterSave(
+ EventSource source,
+ EntityPersister persister,
+ Object entity,
+ Object anything) {
+
+ // cascade-save to collections AFTER the collection owner was saved
+ source.getPersistenceContext().incrementCascadeLevel();
+ try {
+ new Cascade( getCascadeAction(), Cascade.AFTER_INSERT_BEFORE_DELETE, source )
+ .cascade( persister, entity, anything );
+ }
+ finally {
+ source.getPersistenceContext().decrementCascadeLevel();
+ }
+ }
+
+ protected abstract CascadingAction getCascadeAction();
+
+ /**
+ * Determine whether the entity is persistent, detached, or transient
+ *
+ * @param entity The entity to check
+ * @param entityName The name of the entity
+ * @param entry The entity's entry in the persistence context
+ * @param source The originating session.
+ *
+ * @return The state.
+ */
+ protected int getEntityState(
+ Object entity,
+ String entityName,
+ EntityEntry entry, //pass this as an argument only to avoid double looking
+ SessionImplementor source) {
+
+ if ( entry != null ) { // the object is persistent
+
+ //the entity is associated with the session, so check its status
+ if ( entry.getStatus() != Status.DELETED ) {
+ // do nothing for persistent instances
+ if ( log.isTraceEnabled() ) {
+ log.trace(
+ "persistent instance of: " +
+ getLoggableName( entityName, entity )
+ );
+ }
+ return PERSISTENT;
+ }
+ else {
+ //ie. e.status==DELETED
+ if ( log.isTraceEnabled() ) {
+ log.trace(
+ "deleted instance of: " +
+ getLoggableName( entityName, entity )
+ );
+ }
+ return DELETED;
+ }
+
+ }
+ else { // the object is transient or detached
+
+ //the entity is not associated with the session, so
+ //try interceptor and unsaved-value
+
+ if ( ForeignKeys.isTransient( entityName, entity, getAssumedUnsaved(), source ) ) {
+ if ( log.isTraceEnabled() ) {
+ log.trace(
+ "transient instance of: " +
+ getLoggableName( entityName, entity )
+ );
+ }
+ return TRANSIENT;
+ }
+ else {
+ if ( log.isTraceEnabled() ) {
+ log.trace(
+ "detached instance of: " +
+ getLoggableName( entityName, entity )
+ );
+ }
+ return DETACHED;
+ }
+
+ }
+ }
+
+ protected String getLoggableName(String entityName, Object entity) {
+ return entityName == null ? entity.getClass().getName() : entityName;
+ }
+
+ protected Boolean getAssumedUnsaved() {
+ return null;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/AbstractVisitor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/AbstractVisitor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/AbstractVisitor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,157 @@
+//$Id: AbstractVisitor.java 7546 2005-07-19 18:17:15Z oneovthafew $
+package org.hibernate.event.def;
+
+import org.hibernate.HibernateException;
+import org.hibernate.event.EventSource;
+import org.hibernate.intercept.LazyPropertyInitializer;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.type.AbstractComponentType;
+import org.hibernate.type.CollectionType;
+import org.hibernate.type.EntityType;
+import org.hibernate.type.Type;
+
+
+/**
+ * Abstract superclass of algorithms that walk
+ * a tree of property values of an entity, and
+ * perform specific functionality for collections,
+ * components and associated entities.
+ *
+ * @author Gavin King
+ */
+public abstract class AbstractVisitor {
+
+ private final EventSource session;
+
+ AbstractVisitor(EventSource session) {
+ this.session = session;
+ }
+
+ /**
+ * Dispatch each property value to processValue().
+ *
+ * @param values
+ * @param types
+ * @throws HibernateException
+ */
+ void processValues(Object[] values, Type[] types) throws HibernateException {
+ for ( int i=0; i<types.length; i++ ) {
+ if ( includeProperty(values, i) ) {
+ processValue( i, values, types );
+ }
+ }
+ }
+
+ /**
+ * Dispatch each property value to processValue().
+ *
+ * @param values
+ * @param types
+ * @throws HibernateException
+ */
+ public void processEntityPropertyValues(Object[] values, Type[] types) throws HibernateException {
+ for ( int i=0; i<types.length; i++ ) {
+ if ( includeEntityProperty(values, i) ) {
+ processValue( i, values, types );
+ }
+ }
+ }
+
+ void processValue(int i, Object[] values, Type[] types) {
+ processValue( values[i], types[i] );
+ }
+
+ boolean includeEntityProperty(Object[] values, int i) {
+ return includeProperty(values, i);
+ }
+
+ boolean includeProperty(Object[] values, int i) {
+ return values[i]!=LazyPropertyInitializer.UNFETCHED_PROPERTY;
+ }
+
+ /**
+ * Visit a component. Dispatch each property
+ * to processValue().
+ * @param component
+ * @param componentType
+ * @throws HibernateException
+ */
+ Object processComponent(Object component, AbstractComponentType componentType)
+ throws HibernateException {
+ if (component!=null) {
+ processValues(
+ componentType.getPropertyValues(component, session),
+ componentType.getSubtypes()
+ );
+ }
+ return null;
+ }
+
+ /**
+ * Visit a property value. Dispatch to the
+ * correct handler for the property type.
+ * @param value
+ * @param type
+ * @throws HibernateException
+ */
+ final Object processValue(Object value, Type type) throws HibernateException {
+
+ if ( type.isCollectionType() ) {
+ //even process null collections
+ return processCollection( value, (CollectionType) type );
+ }
+ else if ( type.isEntityType() ) {
+ return processEntity( value, (EntityType) type );
+ }
+ else if ( type.isComponentType() ) {
+ return processComponent( value, (AbstractComponentType) type );
+ }
+ else {
+ return null;
+ }
+ }
+
+ /**
+ * Walk the tree starting from the given entity.
+ *
+ * @param object
+ * @param persister
+ * @throws HibernateException
+ */
+ void process(Object object, EntityPersister persister)
+ throws HibernateException {
+ processEntityPropertyValues(
+ persister.getPropertyValues( object, getSession().getEntityMode() ),
+ persister.getPropertyTypes()
+ );
+ }
+
+ /**
+ * Visit a collection. Default superclass
+ * implementation is a no-op.
+ * @param collection
+ * @param type
+ * @throws HibernateException
+ */
+ Object processCollection(Object collection, CollectionType type)
+ throws HibernateException {
+ return null;
+ }
+
+ /**
+ * Visit a many-to-one or one-to-one associated
+ * entity. Default superclass implementation is
+ * a no-op.
+ * @param value
+ * @param entityType
+ * @throws HibernateException
+ */
+ Object processEntity(Object value, EntityType entityType)
+ throws HibernateException {
+ return null;
+ }
+
+ final EventSource getSession() {
+ return session;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultAutoFlushEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultAutoFlushEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultAutoFlushEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,75 @@
+//$Id: DefaultAutoFlushEventListener.java 7785 2005-08-08 23:24:44Z oneovthafew $
+package org.hibernate.event.def;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.FlushMode;
+import org.hibernate.HibernateException;
+import org.hibernate.event.AutoFlushEvent;
+import org.hibernate.event.AutoFlushEventListener;
+import org.hibernate.event.EventSource;
+
+/**
+ * Defines the default flush event listeners used by hibernate for
+ * flushing session state in response to generated auto-flush events.
+ *
+ * @author Steve Ebersole
+ */
+public class DefaultAutoFlushEventListener extends AbstractFlushingEventListener implements AutoFlushEventListener {
+
+ private static final Log log = LogFactory.getLog(DefaultAutoFlushEventListener.class);
+
+ /** Handle the given auto-flush event.
+ *
+ * @param event The auto-flush event to be handled.
+ * @throws HibernateException
+ */
+ public void onAutoFlush(AutoFlushEvent event) throws HibernateException {
+
+ final EventSource source = event.getSession();
+
+ if ( flushMightBeNeeded(source) ) {
+
+ final int oldSize = source.getActionQueue().numberOfCollectionRemovals();
+
+ flushEverythingToExecutions(event);
+
+ if ( flushIsReallyNeeded(event, source) ) {
+
+ log.trace("Need to execute flush");
+
+ performExecutions(source);
+ postFlush(source);
+ // note: performExecutions() clears all collectionXxxxtion
+ // collections (the collection actions) in the session
+
+ if ( source.getFactory().getStatistics().isStatisticsEnabled() ) {
+ source.getFactory().getStatisticsImplementor().flush();
+ }
+
+ }
+ else {
+
+ log.trace("Dont need to execute flush");
+ source.getActionQueue().clearFromFlushNeededCheck( oldSize );
+ }
+
+ event.setFlushRequired( flushIsReallyNeeded( event, source ) );
+
+ }
+
+ }
+
+ private boolean flushIsReallyNeeded(AutoFlushEvent event, final EventSource source) {
+ return source.getActionQueue()
+ .areTablesToBeUpdated( event.getQuerySpaces() ) ||
+ source.getFlushMode()==FlushMode.ALWAYS;
+ }
+
+ private boolean flushMightBeNeeded(final EventSource source) {
+ return !source.getFlushMode().lessThan(FlushMode.AUTO) &&
+ source.getDontFlushFromFind() == 0 &&
+ source.getPersistenceContext().hasNonReadOnlyEntities();
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultDeleteEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultDeleteEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultDeleteEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,336 @@
+//$Id: DefaultDeleteEventListener.java 10948 2006-12-07 21:53:10Z steve.ebersole at jboss.com $
+package org.hibernate.event.def;
+
+import java.io.Serializable;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.hibernate.CacheMode;
+import org.hibernate.HibernateException;
+import org.hibernate.LockMode;
+import org.hibernate.TransientObjectException;
+import org.hibernate.util.IdentitySet;
+import org.hibernate.action.EntityDeleteAction;
+import org.hibernate.classic.Lifecycle;
+import org.hibernate.engine.Cascade;
+import org.hibernate.engine.CascadingAction;
+import org.hibernate.engine.EntityEntry;
+import org.hibernate.engine.EntityKey;
+import org.hibernate.engine.ForeignKeys;
+import org.hibernate.engine.Nullability;
+import org.hibernate.engine.PersistenceContext;
+import org.hibernate.engine.Status;
+import org.hibernate.event.DeleteEvent;
+import org.hibernate.event.DeleteEventListener;
+import org.hibernate.event.EventSource;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.pretty.MessageHelper;
+import org.hibernate.type.Type;
+import org.hibernate.type.TypeFactory;
+
+
+/**
+ * Defines the default delete event listener used by hibernate for deleting entities
+ * from the datastore in response to generated delete events.
+ *
+ * @author Steve Ebersole
+ */
+public class DefaultDeleteEventListener implements DeleteEventListener {
+
+ private static final Log log = LogFactory.getLog( DefaultDeleteEventListener.class );
+
+ /**
+ * Handle the given delete event.
+ *
+ * @param event The delete event to be handled.
+ *
+ * @throws HibernateException
+ */
+ public void onDelete(DeleteEvent event) throws HibernateException {
+ onDelete( event, new IdentitySet() );
+ }
+
+ /**
+ * Handle the given delete event. This is the cascaded form.
+ *
+ * @param event The delete event.
+ * @param transientEntities The cache of entities already deleted
+ *
+ * @throws HibernateException
+ */
+ public void onDelete(DeleteEvent event, Set transientEntities) throws HibernateException {
+
+ final EventSource source = event.getSession();
+
+ final PersistenceContext persistenceContext = source.getPersistenceContext();
+ Object entity = persistenceContext.unproxyAndReassociate( event.getObject() );
+
+ EntityEntry entityEntry = persistenceContext.getEntry( entity );
+ final EntityPersister persister;
+ final Serializable id;
+ final Object version;
+
+ if ( entityEntry == null ) {
+ log.trace( "entity was not persistent in delete processing" );
+
+ persister = source.getEntityPersister( event.getEntityName(), entity );
+
+ if ( ForeignKeys.isTransient( persister.getEntityName(), entity, null, source ) ) {
+ deleteTransientEntity( source, entity, event.isCascadeDeleteEnabled(), persister, transientEntities );
+ // EARLY EXIT!!!
+ return;
+ }
+ else {
+ performDetachedEntityDeletionCheck( event );
+ }
+
+ id = persister.getIdentifier( entity, source.getEntityMode() );
+
+ if ( id == null ) {
+ throw new TransientObjectException(
+ "the detached instance passed to delete() had a null identifier"
+ );
+ }
+
+ EntityKey key = new EntityKey( id, persister, source.getEntityMode() );
+
+ persistenceContext.checkUniqueness( key, entity );
+
+ new OnUpdateVisitor( source, id, entity ).process( entity, persister );
+
+ version = persister.getVersion( entity, source.getEntityMode() );
+
+ entityEntry = persistenceContext.addEntity(
+ entity,
+ Status.MANAGED,
+ persister.getPropertyValues( entity, source.getEntityMode() ),
+ key,
+ version,
+ LockMode.NONE,
+ true,
+ persister,
+ false,
+ false
+ );
+ }
+ else {
+ log.trace( "deleting a persistent instance" );
+
+ if ( entityEntry.getStatus() == Status.DELETED || entityEntry.getStatus() == Status.GONE ) {
+ log.trace( "object was already deleted" );
+ return;
+ }
+ persister = entityEntry.getPersister();
+ id = entityEntry.getId();
+ version = entityEntry.getVersion();
+ }
+
+ /*if ( !persister.isMutable() ) {
+ throw new HibernateException(
+ "attempted to delete an object of immutable class: " +
+ MessageHelper.infoString(persister)
+ );
+ }*/
+
+ if ( invokeDeleteLifecycle( source, entity, persister ) ) {
+ return;
+ }
+
+ deleteEntity( source, entity, entityEntry, event.isCascadeDeleteEnabled(), persister, transientEntities );
+
+ if ( source.getFactory().getSettings().isIdentifierRollbackEnabled() ) {
+ persister.resetIdentifier( entity, id, version, source.getEntityMode() );
+ }
+ }
+
+ /**
+ * Called when we have recognized an attempt to delete a detached entity.
+ * <p/>
+ * This is perfectly valid in Hibernate usage; JPA, however, forbids this.
+ * Thus, this is a hook for HEM to affect this behavior.
+ *
+ * @param event The event.
+ */
+ protected void performDetachedEntityDeletionCheck(DeleteEvent event) {
+ // ok in normal Hibernate usage to delete a detached entity; JPA however
+ // forbids it, thus this is a hook for HEM to affect this behavior
+ }
+
+ /**
+ * We encountered a delete request on a transient instance.
+ * <p/>
+ * This is a deviation from historical Hibernate (pre-3.2) behavior to
+ * align with the JPA spec, which states that transient entities can be
+ * passed to remove operation in which case cascades still need to be
+ * performed.
+ *
+ * @param session The session which is the source of the event
+ * @param entity The entity being delete processed
+ * @param cascadeDeleteEnabled Is cascading of deletes enabled
+ * @param persister The entity persister
+ * @param transientEntities A cache of already visited transient entities
+ * (to avoid infinite recursion).
+ */
+ protected void deleteTransientEntity(
+ EventSource session,
+ Object entity,
+ boolean cascadeDeleteEnabled,
+ EntityPersister persister,
+ Set transientEntities) {
+ log.info( "handling transient entity in delete processing" );
+ if ( transientEntities.contains( entity ) ) {
+ log.trace( "already handled transient entity; skipping" );
+ return;
+ }
+ transientEntities.add( entity );
+ cascadeBeforeDelete( session, persister, entity, null, transientEntities );
+ cascadeAfterDelete( session, persister, entity, transientEntities );
+ }
+
+ /**
+ * Perform the entity deletion. Well, as with most operations, does not
+ * really perform it; just schedules an action/execution with the
+ * {@link org.hibernate.engine.ActionQueue} for execution during flush.
+ *
+ * @param session The originating session
+ * @param entity The entity to delete
+ * @param entityEntry The entity's entry in the {@link PersistenceContext}
+ * @param isCascadeDeleteEnabled Is delete cascading enabled?
+ * @param persister The entity persister.
+ * @param transientEntities A cache of already deleted entities.
+ */
+ protected final void deleteEntity(
+ final EventSource session,
+ final Object entity,
+ final EntityEntry entityEntry,
+ final boolean isCascadeDeleteEnabled,
+ final EntityPersister persister,
+ final Set transientEntities) {
+
+ if ( log.isTraceEnabled() ) {
+ log.trace(
+ "deleting " +
+ MessageHelper.infoString( persister, entityEntry.getId(), session.getFactory() )
+ );
+ }
+
+ final PersistenceContext persistenceContext = session.getPersistenceContext();
+ final Type[] propTypes = persister.getPropertyTypes();
+ final Object version = entityEntry.getVersion();
+
+ final Object[] currentState;
+ if ( entityEntry.getLoadedState() == null ) { //ie. the entity came in from update()
+ currentState = persister.getPropertyValues( entity, session.getEntityMode() );
+ }
+ else {
+ currentState = entityEntry.getLoadedState();
+ }
+
+ final Object[] deletedState = createDeletedState( persister, currentState, session );
+ entityEntry.setDeletedState( deletedState );
+
+ session.getInterceptor().onDelete(
+ entity,
+ entityEntry.getId(),
+ deletedState,
+ persister.getPropertyNames(),
+ propTypes
+ );
+
+ // before any callbacks, etc, so subdeletions see that this deletion happened first
+ persistenceContext.setEntryStatus( entityEntry, Status.DELETED );
+ EntityKey key = new EntityKey( entityEntry.getId(), persister, session.getEntityMode() );
+
+ cascadeBeforeDelete( session, persister, entity, entityEntry, transientEntities );
+
+ new ForeignKeys.Nullifier( entity, true, false, session )
+ .nullifyTransientReferences( entityEntry.getDeletedState(), propTypes );
+ new Nullability( session ).checkNullability( entityEntry.getDeletedState(), persister, true );
+ persistenceContext.getNullifiableEntityKeys().add( key );
+
+ // Ensures that containing deletions happen before sub-deletions
+ session.getActionQueue().addAction(
+ new EntityDeleteAction(
+ entityEntry.getId(),
+ deletedState,
+ version,
+ entity,
+ persister,
+ isCascadeDeleteEnabled,
+ session
+ )
+ );
+
+ cascadeAfterDelete( session, persister, entity, transientEntities );
+
+ // the entry will be removed after the flush, and will no longer
+ // override the stale snapshot
+ // This is now handled by removeEntity() in EntityDeleteAction
+ //persistenceContext.removeDatabaseSnapshot(key);
+ }
+
+ private Object[] createDeletedState(EntityPersister persister, Object[] currentState, EventSource session) {
+ Type[] propTypes = persister.getPropertyTypes();
+ final Object[] deletedState = new Object[propTypes.length];
+// TypeFactory.deepCopy( currentState, propTypes, persister.getPropertyUpdateability(), deletedState, session );
+ boolean[] copyability = new boolean[propTypes.length];
+ java.util.Arrays.fill( copyability, true );
+ TypeFactory.deepCopy( currentState, propTypes, copyability, deletedState, session );
+ return deletedState;
+ }
+
+ protected boolean invokeDeleteLifecycle(EventSource session, Object entity, EntityPersister persister) {
+ if ( persister.implementsLifecycle( session.getEntityMode() ) ) {
+ log.debug( "calling onDelete()" );
+ if ( ( ( Lifecycle ) entity ).onDelete( session ) ) {
+ log.debug( "deletion vetoed by onDelete()" );
+ return true;
+ }
+ }
+ return false;
+ }
+
+ protected void cascadeBeforeDelete(
+ EventSource session,
+ EntityPersister persister,
+ Object entity,
+ EntityEntry entityEntry,
+ Set transientEntities) throws HibernateException {
+
+ CacheMode cacheMode = session.getCacheMode();
+ session.setCacheMode( CacheMode.GET );
+ session.getPersistenceContext().incrementCascadeLevel();
+ try {
+ // cascade-delete to collections BEFORE the collection owner is deleted
+ new Cascade( CascadingAction.DELETE, Cascade.AFTER_INSERT_BEFORE_DELETE, session )
+ .cascade( persister, entity, transientEntities );
+ }
+ finally {
+ session.getPersistenceContext().decrementCascadeLevel();
+ session.setCacheMode( cacheMode );
+ }
+ }
+
+ protected void cascadeAfterDelete(
+ EventSource session,
+ EntityPersister persister,
+ Object entity,
+ Set transientEntities) throws HibernateException {
+
+ CacheMode cacheMode = session.getCacheMode();
+ session.setCacheMode( CacheMode.GET );
+ session.getPersistenceContext().incrementCascadeLevel();
+ try {
+ // cascade-delete to many-to-one AFTER the parent was deleted
+ new Cascade( CascadingAction.DELETE, Cascade.BEFORE_INSERT_AFTER_DELETE, session )
+ .cascade( persister, entity, transientEntities );
+ }
+ finally {
+ session.getPersistenceContext().decrementCascadeLevel();
+ session.setCacheMode( cacheMode );
+ }
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultDirtyCheckEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultDirtyCheckEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultDirtyCheckEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,41 @@
+//$Id: DefaultDirtyCheckEventListener.java 7785 2005-08-08 23:24:44Z oneovthafew $
+package org.hibernate.event.def;
+
+import org.hibernate.HibernateException;
+import org.hibernate.event.DirtyCheckEvent;
+import org.hibernate.event.DirtyCheckEventListener;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Defines the default dirty-check event listener used by hibernate for
+ * checking the session for dirtiness in response to generated dirty-check
+ * events.
+ *
+ * @author Steve Ebersole
+ */
+public class DefaultDirtyCheckEventListener extends AbstractFlushingEventListener implements DirtyCheckEventListener {
+
+ private static final Log log = LogFactory.getLog(DefaultDirtyCheckEventListener.class);
+
+ /** Handle the given dirty-check event.
+ *
+ * @param event The dirty-check event to be handled.
+ * @throws HibernateException
+ */
+ public void onDirtyCheck(DirtyCheckEvent event) throws HibernateException {
+
+ int oldSize = event.getSession().getActionQueue().numberOfCollectionRemovals();
+
+ try {
+ flushEverythingToExecutions(event);
+ boolean wasNeeded = event.getSession().getActionQueue().hasAnyQueuedActions();
+ log.debug( wasNeeded ? "session dirty" : "session not dirty" );
+ event.setDirty( wasNeeded );
+ }
+ finally {
+ event.getSession().getActionQueue().clearFromFlushNeededCheck( oldSize );
+ }
+
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultEvictEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultEvictEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultEvictEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,100 @@
+//$Id: DefaultEvictEventListener.java 10224 2006-08-04 20:29:45Z steve.ebersole at jboss.com $
+package org.hibernate.event.def;
+
+import java.io.Serializable;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.Cascade;
+import org.hibernate.engine.CascadingAction;
+import org.hibernate.engine.EntityEntry;
+import org.hibernate.engine.EntityKey;
+import org.hibernate.engine.PersistenceContext;
+import org.hibernate.event.EventSource;
+import org.hibernate.event.EvictEvent;
+import org.hibernate.event.EvictEventListener;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.pretty.MessageHelper;
+import org.hibernate.proxy.HibernateProxy;
+import org.hibernate.proxy.LazyInitializer;
+
+/**
+ * Defines the default evict event listener used by hibernate for evicting entities
+ * in response to generated flush events. In particular, this implementation will
+ * remove any hard references to the entity that are held by the infrastructure
+ * (references held by application or other persistent instances are okay)
+ *
+ * @author Steve Ebersole
+ */
+public class DefaultEvictEventListener implements EvictEventListener {
+
+ private static final Log log = LogFactory.getLog(DefaultEvictEventListener.class);
+
+ /**
+ * Handle the given evict event.
+ *
+ * @param event The evict event to be handled.
+ * @throws HibernateException
+ */
+ public void onEvict(EvictEvent event) throws HibernateException {
+ EventSource source = event.getSession();
+ final Object object = event.getObject();
+ final PersistenceContext persistenceContext = source.getPersistenceContext();
+
+ if ( object instanceof HibernateProxy ) {
+ LazyInitializer li = ( (HibernateProxy) object ).getHibernateLazyInitializer();
+ Serializable id = li.getIdentifier();
+ EntityPersister persister = source.getFactory().getEntityPersister( li.getEntityName() );
+ if ( id == null ) {
+ throw new IllegalArgumentException("null identifier");
+ }
+
+ EntityKey key = new EntityKey( id, persister, source.getEntityMode() );
+ persistenceContext.removeProxy( key );
+
+ if ( !li.isUninitialized() ) {
+ final Object entity = persistenceContext.removeEntity( key );
+ if ( entity != null ) {
+ EntityEntry e = event.getSession().getPersistenceContext().removeEntry( entity );
+ doEvict( entity, key, e.getPersister(), event.getSession() );
+ }
+ }
+ li.setSession( null );
+ }
+ else {
+ EntityEntry e = persistenceContext.removeEntry( object );
+ if ( e != null ) {
+ EntityKey key = new EntityKey( e.getId(), e.getPersister(), source.getEntityMode() );
+ persistenceContext.removeEntity( key );
+ doEvict( object, key, e.getPersister(), source );
+ }
+ }
+ }
+
+ protected void doEvict(
+ final Object object,
+ final EntityKey key,
+ final EntityPersister persister,
+ final EventSource session)
+ throws HibernateException {
+
+ if ( log.isTraceEnabled() ) {
+ log.trace( "evicting " + MessageHelper.infoString(persister) );
+ }
+
+ // remove all collections for the entity from the session-level cache
+ if ( persister.hasCollections() ) {
+ new EvictVisitor( session ).process( object, persister );
+ }
+
+ // remove any snapshot, not really for memory management purposes, but
+ // rather because it might now be stale, and there is no longer any
+ // EntityEntry to take precedence
+ // This is now handled by removeEntity()
+ //session.getPersistenceContext().removeDatabaseSnapshot(key);
+
+ new Cascade( CascadingAction.EVICT, Cascade.AFTER_EVICT, session )
+ .cascade( persister, object );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultFlushEntityEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultFlushEntityEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultFlushEntityEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,520 @@
+//$Id: DefaultFlushEntityEventListener.java 10784 2006-11-11 05:13:01Z steve.ebersole at jboss.com $
+package org.hibernate.event.def;
+
+import java.io.Serializable;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.AssertionFailure;
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.StaleObjectStateException;
+import org.hibernate.action.EntityUpdateAction;
+import org.hibernate.action.DelayedPostInsertIdentifier;
+import org.hibernate.classic.Validatable;
+import org.hibernate.engine.EntityEntry;
+import org.hibernate.engine.EntityKey;
+import org.hibernate.engine.Nullability;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.Status;
+import org.hibernate.engine.Versioning;
+import org.hibernate.event.EventSource;
+import org.hibernate.event.FlushEntityEvent;
+import org.hibernate.event.FlushEntityEventListener;
+import org.hibernate.intercept.FieldInterceptionHelper;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.pretty.MessageHelper;
+import org.hibernate.type.Type;
+import org.hibernate.util.ArrayHelper;
+
+/**
+ * An event that occurs for each entity instance at flush time
+ *
+ * @author Gavin King
+ */
+public class DefaultFlushEntityEventListener implements FlushEntityEventListener {
+
+ private static final Log log = LogFactory.getLog(DefaultFlushEntityEventListener.class);
+
+ /**
+ * make sure user didn't mangle the id
+ */
+ public void checkId(Object object, EntityPersister persister, Serializable id, EntityMode entityMode)
+ throws HibernateException {
+
+ if ( id != null && id instanceof DelayedPostInsertIdentifier ) {
+ // this is a situation where the entity id is assigned by a post-insert generator
+ // and was saved outside the transaction forcing it to be delayed
+ return;
+ }
+
+ if ( persister.canExtractIdOutOfEntity() ) {
+
+ Serializable oid = persister.getIdentifier( object, entityMode );
+ if (id==null) {
+ throw new AssertionFailure("null id in " + persister.getEntityName() + " entry (don't flush the Session after an exception occurs)");
+ }
+ if ( !persister.getIdentifierType().isEqual(id, oid, entityMode) ) {
+ throw new HibernateException(
+ "identifier of an instance of " +
+ persister.getEntityName() +
+ " was altered from " + id +
+ " to " + oid
+ );
+ }
+ }
+
+ }
+
+ private void checkNaturalId(
+ EntityPersister persister,
+ Serializable identifier,
+ Object[] current,
+ Object[] loaded,
+ EntityMode entityMode,
+ SessionImplementor session) {
+ if ( persister.hasNaturalIdentifier() ) {
+ if ( loaded == null ) {
+ loaded = session.getPersistenceContext().getNaturalIdSnapshot( identifier, persister );
+ }
+ Type[] types = persister.getPropertyTypes();
+ int[] props = persister.getNaturalIdentifierProperties();
+ boolean[] updateable = persister.getPropertyUpdateability();
+ for ( int i=0; i<props.length; i++ ) {
+ int prop = props[i];
+ if ( !updateable[prop] ) {
+ if ( !types[prop].isEqual( current[prop], loaded[prop], entityMode ) ) {
+ throw new HibernateException(
+ "immutable natural identifier of an instance of " +
+ persister.getEntityName() +
+ " was altered"
+ );
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Flushes a single entity's state to the database, by scheduling
+ * an update action, if necessary
+ */
+ public void onFlushEntity(FlushEntityEvent event) throws HibernateException {
+ final Object entity = event.getEntity();
+ final EntityEntry entry = event.getEntityEntry();
+ final EventSource session = event.getSession();
+ final EntityPersister persister = entry.getPersister();
+ final Status status = entry.getStatus();
+ final EntityMode entityMode = session.getEntityMode();
+ final Type[] types = persister.getPropertyTypes();
+
+ final boolean mightBeDirty = entry.requiresDirtyCheck(entity);
+
+ final Object[] values = getValues( entity, entry, entityMode, mightBeDirty, session );
+
+ event.setPropertyValues(values);
+
+ //TODO: avoid this for non-new instances where mightBeDirty==false
+ boolean substitute = wrapCollections( session, persister, types, values);
+
+ if ( isUpdateNecessary( event, mightBeDirty ) ) {
+ substitute = scheduleUpdate( event ) || substitute;
+ }
+
+ if ( status != Status.DELETED ) {
+ // now update the object .. has to be outside the main if block above (because of collections)
+ if (substitute) persister.setPropertyValues( entity, values, entityMode );
+
+ // Search for collections by reachability, updating their role.
+ // We don't want to touch collections reachable from a deleted object
+ if ( persister.hasCollections() ) {
+ new FlushVisitor(session, entity).processEntityPropertyValues(values, types);
+ }
+ }
+
+ }
+
+ private Object[] getValues(
+ Object entity,
+ EntityEntry entry,
+ EntityMode entityMode,
+ boolean mightBeDirty,
+ SessionImplementor session
+ ) {
+ final Object[] loadedState = entry.getLoadedState();
+ final Status status = entry.getStatus();
+ final EntityPersister persister = entry.getPersister();
+
+ final Object[] values;
+ if ( status == Status.DELETED ) {
+ //grab its state saved at deletion
+ values = entry.getDeletedState();
+ }
+ else if ( !mightBeDirty && loadedState!=null ) {
+ values = loadedState;
+ }
+ else {
+ checkId( entity, persister, entry.getId(), entityMode );
+
+ // grab its current state
+ values = persister.getPropertyValues( entity, entityMode );
+
+ checkNaturalId( persister, entry.getId(), values, loadedState, entityMode, session );
+ }
+ return values;
+ }
+
+ private boolean wrapCollections(
+ EventSource session,
+ EntityPersister persister,
+ Type[] types,
+ Object[] values
+ ) {
+ if ( persister.hasCollections() ) {
+
+ // wrap up any new collections directly referenced by the object
+ // or its components
+
+ // NOTE: we need to do the wrap here even if its not "dirty",
+ // because collections need wrapping but changes to _them_
+ // don't dirty the container. Also, for versioned data, we
+ // need to wrap before calling searchForDirtyCollections
+
+ WrapVisitor visitor = new WrapVisitor(session);
+ // substitutes into values by side-effect
+ visitor.processEntityPropertyValues(values, types);
+ return visitor.isSubstitutionRequired();
+ }
+ else {
+ return false;
+ }
+ }
+
+ private boolean isUpdateNecessary(final FlushEntityEvent event, final boolean mightBeDirty) {
+ final Status status = event.getEntityEntry().getStatus();
+ if ( mightBeDirty || status==Status.DELETED ) {
+ // compare to cached state (ignoring collections unless versioned)
+ dirtyCheck(event);
+ if ( isUpdateNecessary(event) ) {
+ return true;
+ }
+ else {
+ FieldInterceptionHelper.clearDirty( event.getEntity() );
+ return false;
+ }
+ }
+ else {
+ return hasDirtyCollections( event, event.getEntityEntry().getPersister(), status );
+ }
+ }
+
+ private boolean scheduleUpdate(final FlushEntityEvent event) {
+
+ final EntityEntry entry = event.getEntityEntry();
+ final EventSource session = event.getSession();
+ final Object entity = event.getEntity();
+ final Status status = entry.getStatus();
+ final EntityMode entityMode = session.getEntityMode();
+ final EntityPersister persister = entry.getPersister();
+ final Object[] values = event.getPropertyValues();
+
+ if ( log.isTraceEnabled() ) {
+ if ( status == Status.DELETED ) {
+ log.trace(
+ "Updating deleted entity: " +
+ MessageHelper.infoString( persister, entry.getId(), session.getFactory() )
+ );
+ }
+ else {
+ log.trace(
+ "Updating entity: " +
+ MessageHelper.infoString( persister, entry.getId(), session.getFactory() )
+ );
+ }
+ }
+
+ final boolean intercepted;
+ if ( !entry.isBeingReplicated() ) {
+ // give the Interceptor a chance to process property values, if the properties
+ // were modified by the Interceptor, we need to set them back to the object
+ intercepted = handleInterception( event );
+ }
+ else {
+ intercepted = false;
+ }
+
+ validate( entity, persister, status, entityMode );
+
+ // increment the version number (if necessary)
+ final Object nextVersion = getNextVersion(event);
+
+ // if it was dirtied by a collection only
+ int[] dirtyProperties = event.getDirtyProperties();
+ if ( event.isDirtyCheckPossible() && dirtyProperties == null ) {
+ if ( ! intercepted && !event.hasDirtyCollection() ) {
+ throw new AssertionFailure( "dirty, but no dirty properties" );
+ }
+ dirtyProperties = ArrayHelper.EMPTY_INT_ARRAY;
+ }
+
+ // check nullability but do not perform command execute
+ // we'll use scheduled updates for that.
+ new Nullability(session).checkNullability( values, persister, true );
+
+ // schedule the update
+ // note that we intentionally do _not_ pass in currentPersistentState!
+ session.getActionQueue().addAction(
+ new EntityUpdateAction(
+ entry.getId(),
+ values,
+ dirtyProperties,
+ event.hasDirtyCollection(),
+ entry.getLoadedState(),
+ entry.getVersion(),
+ nextVersion,
+ entity,
+ entry.getRowId(),
+ persister,
+ session
+ )
+ );
+
+ return intercepted;
+ }
+
+ protected void validate(Object entity, EntityPersister persister, Status status, EntityMode entityMode) {
+ // validate() instances of Validatable
+ if ( status == Status.MANAGED && persister.implementsValidatable( entityMode ) ) {
+ ( (Validatable) entity ).validate();
+ }
+ }
+
+ protected boolean handleInterception(FlushEntityEvent event) {
+ SessionImplementor session = event.getSession();
+ EntityEntry entry = event.getEntityEntry();
+ EntityPersister persister = entry.getPersister();
+ Object entity = event.getEntity();
+
+ //give the Interceptor a chance to modify property values
+ final Object[] values = event.getPropertyValues();
+ final boolean intercepted = invokeInterceptor( session, entity, entry, values, persister );
+
+ //now we might need to recalculate the dirtyProperties array
+ if ( intercepted && event.isDirtyCheckPossible() && !event.isDirtyCheckHandledByInterceptor() ) {
+ int[] dirtyProperties;
+ if ( event.hasDatabaseSnapshot() ) {
+ dirtyProperties = persister.findModified( event.getDatabaseSnapshot(), values, entity, session );
+ }
+ else {
+ dirtyProperties = persister.findDirty( values, entry.getLoadedState(), entity, session );
+ }
+ event.setDirtyProperties(dirtyProperties);
+ }
+
+ return intercepted;
+ }
+
+ protected boolean invokeInterceptor(
+ SessionImplementor session,
+ Object entity,
+ EntityEntry entry,
+ final Object[] values,
+ EntityPersister persister) {
+ return session.getInterceptor().onFlushDirty(
+ entity,
+ entry.getId(),
+ values,
+ entry.getLoadedState(),
+ persister.getPropertyNames(),
+ persister.getPropertyTypes()
+ );
+ }
+
+ /**
+ * Convience method to retreive an entities next version value
+ */
+ private Object getNextVersion(FlushEntityEvent event) throws HibernateException {
+
+ EntityEntry entry = event.getEntityEntry();
+ EntityPersister persister = entry.getPersister();
+ if ( persister.isVersioned() ) {
+
+ Object[] values = event.getPropertyValues();
+
+ if ( entry.isBeingReplicated() ) {
+ return Versioning.getVersion(values, persister);
+ }
+ else {
+ int[] dirtyProperties = event.getDirtyProperties();
+
+ final boolean isVersionIncrementRequired = isVersionIncrementRequired(
+ event,
+ entry,
+ persister,
+ dirtyProperties
+ );
+
+ final Object nextVersion = isVersionIncrementRequired ?
+ Versioning.increment( entry.getVersion(), persister.getVersionType(), event.getSession() ) :
+ entry.getVersion(); //use the current version
+
+ Versioning.setVersion(values, nextVersion, persister);
+
+ return nextVersion;
+ }
+ }
+ else {
+ return null;
+ }
+
+ }
+
+ private boolean isVersionIncrementRequired(
+ FlushEntityEvent event,
+ EntityEntry entry,
+ EntityPersister persister,
+ int[] dirtyProperties
+ ) {
+ final boolean isVersionIncrementRequired = entry.getStatus()!=Status.DELETED && (
+ dirtyProperties==null ||
+ Versioning.isVersionIncrementRequired(
+ dirtyProperties,
+ event.hasDirtyCollection(),
+ persister.getPropertyVersionability()
+ )
+ );
+ return isVersionIncrementRequired;
+ }
+
+ /**
+ * Performs all necessary checking to determine if an entity needs an SQL update
+ * to synchronize its state to the database. Modifies the event by side-effect!
+ * Note: this method is quite slow, avoid calling if possible!
+ */
+ protected final boolean isUpdateNecessary(FlushEntityEvent event) throws HibernateException {
+
+ EntityPersister persister = event.getEntityEntry().getPersister();
+ Status status = event.getEntityEntry().getStatus();
+
+ if ( !event.isDirtyCheckPossible() ) {
+ return true;
+ }
+ else {
+
+ int[] dirtyProperties = event.getDirtyProperties();
+ if ( dirtyProperties!=null && dirtyProperties.length!=0 ) {
+ return true; //TODO: suck into event class
+ }
+ else {
+ return hasDirtyCollections( event, persister, status );
+ }
+
+ }
+ }
+
+ private boolean hasDirtyCollections(FlushEntityEvent event, EntityPersister persister, Status status) {
+ if ( isCollectionDirtyCheckNecessary(persister, status) ) {
+ DirtyCollectionSearchVisitor visitor = new DirtyCollectionSearchVisitor(
+ event.getSession(),
+ persister.getPropertyVersionability()
+ );
+ visitor.processEntityPropertyValues( event.getPropertyValues(), persister.getPropertyTypes() );
+ boolean hasDirtyCollections = visitor.wasDirtyCollectionFound();
+ event.setHasDirtyCollection(hasDirtyCollections);
+ return hasDirtyCollections;
+ }
+ else {
+ return false;
+ }
+ }
+
+ private boolean isCollectionDirtyCheckNecessary(EntityPersister persister, Status status) {
+ return status==Status.MANAGED &&
+ persister.isVersioned() &&
+ persister.hasCollections();
+ }
+
+ /**
+ * Perform a dirty check, and attach the results to the event
+ */
+ protected void dirtyCheck(FlushEntityEvent event) throws HibernateException {
+
+ final Object entity = event.getEntity();
+ final Object[] values = event.getPropertyValues();
+ final SessionImplementor session = event.getSession();
+ final EntityEntry entry = event.getEntityEntry();
+ final EntityPersister persister = entry.getPersister();
+ final Serializable id = entry.getId();
+ final Object[] loadedState = entry.getLoadedState();
+
+ int[] dirtyProperties = session.getInterceptor().findDirty(
+ entity,
+ id,
+ values,
+ loadedState,
+ persister.getPropertyNames(),
+ persister.getPropertyTypes()
+ );
+
+ event.setDatabaseSnapshot(null);
+
+ final boolean interceptorHandledDirtyCheck;
+ boolean cannotDirtyCheck;
+
+ if ( dirtyProperties==null ) {
+ // Interceptor returned null, so do the dirtycheck ourself, if possible
+ interceptorHandledDirtyCheck = false;
+
+ cannotDirtyCheck = loadedState==null; // object loaded by update()
+ if ( !cannotDirtyCheck ) {
+ // dirty check against the usual snapshot of the entity
+ dirtyProperties = persister.findDirty( values, loadedState, entity, session );
+
+ }
+ else {
+ // dirty check against the database snapshot, if possible/necessary
+ final Object[] databaseSnapshot = getDatabaseSnapshot(session, persister, id);
+ if ( databaseSnapshot != null ) {
+ dirtyProperties = persister.findModified(databaseSnapshot, values, entity, session);
+ cannotDirtyCheck = false;
+ event.setDatabaseSnapshot(databaseSnapshot);
+ }
+ }
+ }
+ else {
+ // the Interceptor handled the dirty checking
+ cannotDirtyCheck = false;
+ interceptorHandledDirtyCheck = true;
+ }
+
+ event.setDirtyProperties(dirtyProperties);
+ event.setDirtyCheckHandledByInterceptor(interceptorHandledDirtyCheck);
+ event.setDirtyCheckPossible(!cannotDirtyCheck);
+
+ }
+
+ private Object[] getDatabaseSnapshot(SessionImplementor session, EntityPersister persister, Serializable id) {
+ if ( persister.isSelectBeforeUpdateRequired() ) {
+ Object[] snapshot = session.getPersistenceContext()
+ .getDatabaseSnapshot(id, persister);
+ if (snapshot==null) {
+ //do we even really need this? the update will fail anyway....
+ if ( session.getFactory().getStatistics().isStatisticsEnabled() ) {
+ session.getFactory().getStatisticsImplementor()
+ .optimisticFailure( persister.getEntityName() );
+ }
+ throw new StaleObjectStateException( persister.getEntityName(), id );
+ }
+ else {
+ return snapshot;
+ }
+ }
+ else {
+ //TODO: optimize away this lookup for entities w/o unsaved-value="undefined"
+ EntityKey entityKey = new EntityKey( id, persister, session.getEntityMode() );
+ return session.getPersistenceContext()
+ .getCachedDatabaseSnapshot( entityKey );
+ }
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultFlushEntityEventListener.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultFlushEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultFlushEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultFlushEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,36 @@
+//$Id: DefaultFlushEventListener.java 6929 2005-05-27 03:54:08Z oneovthafew $
+package org.hibernate.event.def;
+
+import org.hibernate.HibernateException;
+import org.hibernate.event.EventSource;
+import org.hibernate.event.FlushEvent;
+import org.hibernate.event.FlushEventListener;
+
+/**
+ * Defines the default flush event listeners used by hibernate for
+ * flushing session state in response to generated flush events.
+ *
+ * @author Steve Ebersole
+ */
+public class DefaultFlushEventListener extends AbstractFlushingEventListener implements FlushEventListener {
+
+ /** Handle the given flush event.
+ *
+ * @param event The flush event to be handled.
+ * @throws HibernateException
+ */
+ public void onFlush(FlushEvent event) throws HibernateException {
+ final EventSource source = event.getSession();
+ if ( source.getPersistenceContext().hasNonReadOnlyEntities() ) {
+
+ flushEverythingToExecutions(event);
+ performExecutions(source);
+ postFlush(source);
+
+ if ( source.getFactory().getStatistics().isStatisticsEnabled() ) {
+ source.getFactory().getStatisticsImplementor().flush();
+ }
+
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultInitializeCollectionEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultInitializeCollectionEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultInitializeCollectionEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,150 @@
+//$Id: DefaultInitializeCollectionEventListener.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.event.def;
+
+import java.io.Serializable;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.HibernateException;
+import org.hibernate.cache.CacheKey;
+import org.hibernate.cache.entry.CollectionCacheEntry;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.engine.CollectionEntry;
+import org.hibernate.engine.PersistenceContext;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.event.InitializeCollectionEvent;
+import org.hibernate.event.InitializeCollectionEventListener;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.pretty.MessageHelper;
+
+/**
+ * @author Gavin King
+ */
+public class DefaultInitializeCollectionEventListener implements InitializeCollectionEventListener {
+
+ private static final Log log = LogFactory.getLog(DefaultInitializeCollectionEventListener.class);
+
+ /**
+ * called by a collection that wants to initialize itself
+ */
+ public void onInitializeCollection(InitializeCollectionEvent event)
+ throws HibernateException {
+
+ PersistentCollection collection = event.getCollection();
+ SessionImplementor source = event.getSession();
+
+ CollectionEntry ce = source.getPersistenceContext().getCollectionEntry(collection);
+ if (ce==null) throw new HibernateException("collection was evicted");
+ if ( !collection.wasInitialized() ) {
+ if ( log.isTraceEnabled() ) {
+ log.trace(
+ "initializing collection " +
+ MessageHelper.collectionInfoString( ce.getLoadedPersister(), ce.getLoadedKey(), source.getFactory() )
+ );
+ }
+
+ log.trace("checking second-level cache");
+ final boolean foundInCache = initializeCollectionFromCache(
+ ce.getLoadedKey(),
+ ce.getLoadedPersister(),
+ collection,
+ source
+ );
+
+ if (foundInCache) {
+ log.trace("collection initialized from cache");
+ }
+ else {
+ log.trace("collection not cached");
+ ce.getLoadedPersister().initialize( ce.getLoadedKey(), source );
+ log.trace("collection initialized");
+
+ if ( source.getFactory().getStatistics().isStatisticsEnabled() ) {
+ source.getFactory().getStatisticsImplementor().fetchCollection(
+ ce.getLoadedPersister().getRole()
+ );
+ }
+ }
+ }
+ }
+
+ /**
+ * Try to initialize a collection from the cache
+ *
+ * @param id The id of the collection of initialize
+ * @param persister The collection persister
+ * @param collection The collection to initialize
+ * @param source The originating session
+ * @return true if we were able to initialize the collection from the cache;
+ * false otherwise.
+ */
+ private boolean initializeCollectionFromCache(
+ Serializable id,
+ CollectionPersister persister,
+ PersistentCollection collection,
+ SessionImplementor source) {
+
+ if ( !source.getEnabledFilters().isEmpty() && persister.isAffectedByEnabledFilters( source ) ) {
+ log.trace( "disregarding cached version (if any) of collection due to enabled filters ");
+ return false;
+ }
+
+ final boolean useCache = persister.hasCache() &&
+ source.getCacheMode().isGetEnabled();
+
+ if ( !useCache ) {
+ return false;
+ }
+ else {
+
+ final SessionFactoryImplementor factory = source.getFactory();
+
+ final CacheKey ck = new CacheKey(
+ id,
+ persister.getKeyType(),
+ persister.getRole(),
+ source.getEntityMode(),
+ source.getFactory()
+ );
+ Object ce = persister.getCacheAccessStrategy().get( ck, source.getTimestamp() );
+
+ if ( factory.getStatistics().isStatisticsEnabled() ) {
+ if ( ce == null ) {
+ factory.getStatisticsImplementor().secondLevelCacheMiss(
+ persister.getCacheAccessStrategy().getRegion().getName()
+ );
+ }
+ else {
+ factory.getStatisticsImplementor().secondLevelCacheHit(
+ persister.getCacheAccessStrategy().getRegion().getName()
+ );
+ }
+
+
+ }
+
+ if (ce==null) {
+ return false;
+ }
+ else {
+
+ CollectionCacheEntry cacheEntry = (CollectionCacheEntry) persister.getCacheEntryStructure()
+ .destructure(ce, factory);
+
+ final PersistenceContext persistenceContext = source.getPersistenceContext();
+ cacheEntry.assemble(
+ collection,
+ persister,
+ persistenceContext.getCollectionOwner(id, persister)
+ );
+ persistenceContext.getCollectionEntry(collection).postInitialize(collection);
+ //addInitializedCollection(collection, persister, id);
+ return true;
+ }
+
+ }
+ }
+
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultInitializeCollectionEventListener.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultLoadEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultLoadEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultLoadEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,607 @@
+//$Id: DefaultLoadEventListener.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.event.def;
+
+import java.io.Serializable;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.HibernateException;
+import org.hibernate.LockMode;
+import org.hibernate.NonUniqueObjectException;
+import org.hibernate.PersistentObjectException;
+import org.hibernate.TypeMismatchException;
+import org.hibernate.EntityMode;
+import org.hibernate.cache.CacheKey;
+import org.hibernate.cache.access.SoftLock;
+import org.hibernate.cache.entry.CacheEntry;
+import org.hibernate.engine.EntityEntry;
+import org.hibernate.engine.EntityKey;
+import org.hibernate.engine.PersistenceContext;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.Status;
+import org.hibernate.engine.TwoPhaseLoad;
+import org.hibernate.engine.Versioning;
+import org.hibernate.event.EventSource;
+import org.hibernate.event.LoadEvent;
+import org.hibernate.event.LoadEventListener;
+import org.hibernate.event.PostLoadEvent;
+import org.hibernate.event.PostLoadEventListener;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.pretty.MessageHelper;
+import org.hibernate.proxy.HibernateProxy;
+import org.hibernate.proxy.LazyInitializer;
+import org.hibernate.type.Type;
+import org.hibernate.type.TypeFactory;
+
+/**
+ * Defines the default load event listeners used by hibernate for loading entities
+ * in response to generated load events.
+ *
+ * @author Steve Ebersole
+ */
+public class DefaultLoadEventListener extends AbstractLockUpgradeEventListener implements LoadEventListener {
+
+ public static final Object REMOVED_ENTITY_MARKER = new Object();
+ public static final Object INCONSISTENT_RTN_CLASS_MARKER = new Object();
+ public static final LockMode DEFAULT_LOCK_MODE = LockMode.NONE;
+
+ private static final Log log = LogFactory.getLog(DefaultLoadEventListener.class);
+
+
+ /**
+ * Handle the given load event.
+ *
+ * @param event The load event to be handled.
+ * @throws HibernateException
+ */
+ public void onLoad(LoadEvent event, LoadEventListener.LoadType loadType) throws HibernateException {
+
+ final SessionImplementor source = event.getSession();
+
+ EntityPersister persister;
+ if ( event.getInstanceToLoad() != null ) {
+ persister = source.getEntityPersister( null, event.getInstanceToLoad() ); //the load() which takes an entity does not pass an entityName
+ event.setEntityClassName( event.getInstanceToLoad().getClass().getName() );
+ }
+ else {
+ persister = source.getFactory().getEntityPersister( event.getEntityClassName() );
+ }
+
+ if ( persister == null ) {
+ throw new HibernateException(
+ "Unable to locate persister: " +
+ event.getEntityClassName()
+ );
+ }
+
+ if ( persister.getIdentifierType().isComponentType() && EntityMode.DOM4J == event.getSession().getEntityMode() ) {
+ // skip this check for composite-ids relating to dom4j entity-mode;
+ // alternatively, we could add a check to make sure the incoming id value is
+ // an instance of Element...
+ }
+ else {
+ Class idClass = persister.getIdentifierType().getReturnedClass();
+ if ( idClass != null && ! idClass.isInstance( event.getEntityId() ) ) {
+ throw new TypeMismatchException(
+ "Provided id of the wrong type. Expected: " + idClass + ", got " + event.getEntityId().getClass()
+ );
+ }
+ }
+
+ EntityKey keyToLoad = new EntityKey( event.getEntityId(), persister, source.getEntityMode() );
+
+ try {
+ if ( loadType.isNakedEntityReturned() ) {
+ //do not return a proxy!
+ //(this option indicates we are initializing a proxy)
+ event.setResult( load(event, persister, keyToLoad, loadType) );
+ }
+ else {
+ //return a proxy if appropriate
+ if ( event.getLockMode() == LockMode.NONE ) {
+ event.setResult( proxyOrLoad(event, persister, keyToLoad, loadType) );
+ }
+ else {
+ event.setResult( lockAndLoad(event, persister, keyToLoad, loadType, source) );
+ }
+ }
+ }
+ catch(HibernateException e) {
+ log.info("Error performing load command", e);
+ throw e;
+ }
+ }
+
+ /**
+ * Perfoms the load of an entity.
+ *
+ * @param event The initiating load request event
+ * @param persister The persister corresponding to the entity to be loaded
+ * @param keyToLoad The key of the entity to be loaded
+ * @param options The defined load options
+ * @return The loaded entity.
+ * @throws HibernateException
+ */
+ protected Object load(
+ final LoadEvent event,
+ final EntityPersister persister,
+ final EntityKey keyToLoad,
+ final LoadEventListener.LoadType options) {
+
+ if ( event.getInstanceToLoad() != null ) {
+ if ( event.getSession().getPersistenceContext().getEntry( event.getInstanceToLoad() ) != null ) {
+ throw new PersistentObjectException(
+ "attempted to load into an instance that was already associated with the session: " +
+ MessageHelper.infoString( persister, event.getEntityId(), event.getSession().getFactory() )
+ );
+ }
+ persister.setIdentifier( event.getInstanceToLoad(), event.getEntityId(), event.getSession().getEntityMode() );
+ }
+
+ Object entity = doLoad(event, persister, keyToLoad, options);
+
+ boolean isOptionalInstance = event.getInstanceToLoad() != null;
+
+ if ( !options.isAllowNulls() || isOptionalInstance ) {
+ if ( entity == null ) {
+ event.getSession().getFactory().getEntityNotFoundDelegate().handleEntityNotFound( event.getEntityClassName(), event.getEntityId() );
+ }
+ }
+
+ if ( isOptionalInstance && entity != event.getInstanceToLoad() ) {
+ throw new NonUniqueObjectException( event.getEntityId(), event.getEntityClassName() );
+ }
+
+ return entity;
+ }
+
+ /**
+ * Based on configured options, will either return a pre-existing proxy,
+ * generate a new proxy, or perform an actual load.
+ *
+ * @param event The initiating load request event
+ * @param persister The persister corresponding to the entity to be loaded
+ * @param keyToLoad The key of the entity to be loaded
+ * @param options The defined load options
+ * @return The result of the proxy/load operation.
+ */
+ protected Object proxyOrLoad(
+ final LoadEvent event,
+ final EntityPersister persister,
+ final EntityKey keyToLoad,
+ final LoadEventListener.LoadType options) {
+
+ if ( log.isTraceEnabled() ) {
+ log.trace(
+ "loading entity: " +
+ MessageHelper.infoString( persister, event.getEntityId(), event.getSession().getFactory() )
+ );
+ }
+
+ if ( !persister.hasProxy() ) {
+ // this class has no proxies (so do a shortcut)
+ return load(event, persister, keyToLoad, options);
+ }
+ else {
+ final PersistenceContext persistenceContext = event.getSession().getPersistenceContext();
+
+ // look for a proxy
+ Object proxy = persistenceContext.getProxy(keyToLoad);
+ if ( proxy != null ) {
+ return returnNarrowedProxy( event, persister, keyToLoad, options, persistenceContext, proxy );
+ }
+ else {
+ if ( options.isAllowProxyCreation() ) {
+ return createProxyIfNecessary( event, persister, keyToLoad, options, persistenceContext );
+ }
+ else {
+ // return a newly loaded object
+ return load(event, persister, keyToLoad, options);
+ }
+ }
+
+ }
+ }
+
+ /**
+ * Given a proxy, initialize it and/or narrow it provided either
+ * is necessary.
+ *
+ * @param event The initiating load request event
+ * @param persister The persister corresponding to the entity to be loaded
+ * @param keyToLoad The key of the entity to be loaded
+ * @param options The defined load options
+ * @param persistenceContext The originating session
+ * @param proxy The proxy to narrow
+ * @return The created/existing proxy
+ */
+ private Object returnNarrowedProxy(
+ final LoadEvent event,
+ final EntityPersister persister,
+ final EntityKey keyToLoad,
+ final LoadEventListener.LoadType options,
+ final PersistenceContext persistenceContext,
+ final Object proxy) {
+ log.trace("entity proxy found in session cache");
+ LazyInitializer li = ( (HibernateProxy) proxy ).getHibernateLazyInitializer();
+ if ( li.isUnwrap() ) {
+ return li.getImplementation();
+ }
+ Object impl = null;
+ if ( !options.isAllowProxyCreation() ) {
+ impl = load( event, persister, keyToLoad, options );
+ if ( impl == null ) {
+ event.getSession().getFactory().getEntityNotFoundDelegate().handleEntityNotFound( persister.getEntityName(), keyToLoad.getIdentifier());
+ }
+ }
+ return persistenceContext.narrowProxy( proxy, persister, keyToLoad, impl );
+ }
+
+ /**
+ * If there is already a corresponding proxy associated with the
+ * persistence context, return it; otherwise create a proxy, associate it
+ * with the persistence context, and return the just-created proxy.
+ *
+ * @param event The initiating load request event
+ * @param persister The persister corresponding to the entity to be loaded
+ * @param keyToLoad The key of the entity to be loaded
+ * @param options The defined load options
+ * @param persistenceContext The originating session
+ * @return The created/existing proxy
+ */
+ private Object createProxyIfNecessary(
+ final LoadEvent event,
+ final EntityPersister persister,
+ final EntityKey keyToLoad,
+ final LoadEventListener.LoadType options,
+ final PersistenceContext persistenceContext) {
+ Object existing = persistenceContext.getEntity( keyToLoad );
+ if ( existing != null ) {
+ // return existing object or initialized proxy (unless deleted)
+ log.trace( "entity found in session cache" );
+ if ( options.isCheckDeleted() ) {
+ EntityEntry entry = persistenceContext.getEntry( existing );
+ Status status = entry.getStatus();
+ if ( status == Status.DELETED || status == Status.GONE ) {
+ return null;
+ }
+ }
+ return existing;
+ }
+ else {
+ log.trace( "creating new proxy for entity" );
+ // return new uninitialized proxy
+ Object proxy = persister.createProxy( event.getEntityId(), event.getSession() );
+ persistenceContext.getBatchFetchQueue().addBatchLoadableEntityKey(keyToLoad);
+ persistenceContext.addProxy(keyToLoad, proxy);
+ return proxy;
+ }
+ }
+
+ /**
+ * If the class to be loaded has been configured with a cache, then lock
+ * given id in that cache and then perform the load.
+ *
+ * @param event The initiating load request event
+ * @param persister The persister corresponding to the entity to be loaded
+ * @param keyToLoad The key of the entity to be loaded
+ * @param options The defined load options
+ * @param source The originating session
+ * @return The loaded entity
+ * @throws HibernateException
+ */
+ protected Object lockAndLoad(
+ final LoadEvent event,
+ final EntityPersister persister,
+ final EntityKey keyToLoad,
+ final LoadEventListener.LoadType options,
+ final SessionImplementor source) {
+ SoftLock lock = null;
+ final CacheKey ck;
+ if ( persister.hasCache() ) {
+ ck = new CacheKey(
+ event.getEntityId(),
+ persister.getIdentifierType(),
+ persister.getRootEntityName(),
+ source.getEntityMode(),
+ source.getFactory()
+ );
+ lock = persister.getCacheAccessStrategy().lockItem( ck, null );
+ }
+ else {
+ ck = null;
+ }
+
+ Object entity;
+ try {
+ entity = load(event, persister, keyToLoad, options);
+ }
+ finally {
+ if ( persister.hasCache() ) {
+ persister.getCacheAccessStrategy().unlockItem( ck, lock );
+ }
+ }
+
+ return event.getSession().getPersistenceContext().proxyFor( persister, keyToLoad, entity );
+ }
+
+
+ /**
+ * Coordinates the efforts to load a given entity. First, an attempt is
+ * made to load the entity from the session-level cache. If not found there,
+ * an attempt is made to locate it in second-level cache. Lastly, an
+ * attempt is made to load it directly from the datasource.
+ *
+ * @param event The load event
+ * @param persister The persister for the entity being requested for load
+ * @param keyToLoad The EntityKey representing the entity to be loaded.
+ * @param options The load options.
+ * @return The loaded entity, or null.
+ */
+ protected Object doLoad(
+ final LoadEvent event,
+ final EntityPersister persister,
+ final EntityKey keyToLoad,
+ final LoadEventListener.LoadType options) {
+
+ if ( log.isTraceEnabled() ) {
+ log.trace(
+ "attempting to resolve: " +
+ MessageHelper.infoString( persister, event.getEntityId(), event.getSession().getFactory() )
+ );
+ }
+
+ Object entity = loadFromSessionCache( event, keyToLoad, options );
+ if ( entity == REMOVED_ENTITY_MARKER ) {
+ log.debug( "load request found matching entity in context, but it is scheduled for removal; returning null" );
+ return null;
+ }
+ if ( entity == INCONSISTENT_RTN_CLASS_MARKER ) {
+ log.debug( "load request found matching entity in context, but the matched entity was of an inconsistent return type; returning null" );
+ return null;
+ }
+ if ( entity != null ) {
+ if ( log.isTraceEnabled() ) {
+ log.trace(
+ "resolved object in session cache: " +
+ MessageHelper.infoString( persister, event.getEntityId(), event.getSession().getFactory() )
+ );
+ }
+ return entity;
+ }
+
+ entity = loadFromSecondLevelCache(event, persister, options);
+ if ( entity != null ) {
+ if ( log.isTraceEnabled() ) {
+ log.trace(
+ "resolved object in second-level cache: " +
+ MessageHelper.infoString( persister, event.getEntityId(), event.getSession().getFactory() )
+ );
+ }
+ return entity;
+ }
+
+ if ( log.isTraceEnabled() ) {
+ log.trace(
+ "object not resolved in any cache: " +
+ MessageHelper.infoString( persister, event.getEntityId(), event.getSession().getFactory() )
+ );
+ }
+
+ return loadFromDatasource(event, persister, keyToLoad, options);
+ }
+
+ /**
+ * Performs the process of loading an entity from the configured
+ * underlying datasource.
+ *
+ * @param event The load event
+ * @param persister The persister for the entity being requested for load
+ * @param keyToLoad The EntityKey representing the entity to be loaded.
+ * @param options The load options.
+ * @return The object loaded from the datasource, or null if not found.
+ */
+ protected Object loadFromDatasource(
+ final LoadEvent event,
+ final EntityPersister persister,
+ final EntityKey keyToLoad,
+ final LoadEventListener.LoadType options) {
+ final SessionImplementor source = event.getSession();
+ Object entity = persister.load(
+ event.getEntityId(),
+ event.getInstanceToLoad(),
+ event.getLockMode(),
+ source
+ );
+
+ if ( event.isAssociationFetch() && source.getFactory().getStatistics().isStatisticsEnabled() ) {
+ source.getFactory().getStatisticsImplementor().fetchEntity( event.getEntityClassName() );
+ }
+
+ return entity;
+ }
+
+ /**
+ * Attempts to locate the entity in the session-level cache.
+ * <p/>
+ * If allowed to return nulls, then if the entity happens to be found in
+ * the session cache, we check the entity type for proper handling
+ * of entity hierarchies.
+ * <p/>
+ * If checkDeleted was set to true, then if the entity is found in the
+ * session-level cache, it's current status within the session cache
+ * is checked to see if it has previously been scheduled for deletion.
+ *
+ * @param event The load event
+ * @param keyToLoad The EntityKey representing the entity to be loaded.
+ * @param options The load options.
+ * @return The entity from the session-level cache, or null.
+ * @throws HibernateException Generally indicates problems applying a lock-mode.
+ */
+ protected Object loadFromSessionCache(
+ final LoadEvent event,
+ final EntityKey keyToLoad,
+ final LoadEventListener.LoadType options) throws HibernateException {
+
+ SessionImplementor session = event.getSession();
+ Object old = session.getEntityUsingInterceptor( keyToLoad );
+
+ if ( old != null ) {
+ // this object was already loaded
+ EntityEntry oldEntry = session.getPersistenceContext().getEntry( old );
+ if ( options.isCheckDeleted() ) {
+ Status status = oldEntry.getStatus();
+ if ( status == Status.DELETED || status == Status.GONE ) {
+ return REMOVED_ENTITY_MARKER;
+ }
+ }
+ if ( options.isAllowNulls() ) {
+ EntityPersister persister = event.getSession().getFactory().getEntityPersister( event.getEntityClassName() );
+ if ( ! persister.isInstance( old, event.getSession().getEntityMode() ) ) {
+ return INCONSISTENT_RTN_CLASS_MARKER;
+ }
+ }
+ upgradeLock( old, oldEntry, event.getLockMode(), session );
+ }
+
+ return old;
+ }
+
+ /**
+ * Attempts to load the entity from the second-level cache.
+ *
+ * @param event The load event
+ * @param persister The persister for the entity being requested for load
+ * @param options The load options.
+ * @return The entity from the second-level cache, or null.
+ */
+ protected Object loadFromSecondLevelCache(
+ final LoadEvent event,
+ final EntityPersister persister,
+ final LoadEventListener.LoadType options) {
+
+ final SessionImplementor source = event.getSession();
+
+ final boolean useCache = persister.hasCache()
+ && source.getCacheMode().isGetEnabled()
+ && event.getLockMode().lessThan(LockMode.READ);
+
+ if ( useCache ) {
+
+ final SessionFactoryImplementor factory = source.getFactory();
+
+ final CacheKey ck = new CacheKey(
+ event.getEntityId(),
+ persister.getIdentifierType(),
+ persister.getRootEntityName(),
+ source.getEntityMode(),
+ source.getFactory()
+ );
+ Object ce = persister.getCacheAccessStrategy().get( ck, source.getTimestamp() );
+ if ( factory.getStatistics().isStatisticsEnabled() ) {
+ if ( ce == null ) {
+ factory.getStatisticsImplementor().secondLevelCacheMiss(
+ persister.getCacheAccessStrategy().getRegion().getName()
+ );
+ }
+ else {
+ factory.getStatisticsImplementor().secondLevelCacheHit(
+ persister.getCacheAccessStrategy().getRegion().getName()
+ );
+ }
+ }
+
+ if ( ce != null ) {
+ CacheEntry entry = (CacheEntry) persister.getCacheEntryStructure().destructure( ce, factory );
+
+ // Entity was found in second-level cache...
+ return assembleCacheEntry(
+ entry,
+ event.getEntityId(),
+ persister,
+ event
+ );
+ }
+ }
+
+ return null;
+ }
+
+ private Object assembleCacheEntry(
+ final CacheEntry entry,
+ final Serializable id,
+ final EntityPersister persister,
+ final LoadEvent event) throws HibernateException {
+
+ final Object optionalObject = event.getInstanceToLoad();
+ final EventSource session = event.getSession();
+ final SessionFactoryImplementor factory = session.getFactory();
+
+ if ( log.isTraceEnabled() ) {
+ log.trace(
+ "assembling entity from second-level cache: " +
+ MessageHelper.infoString( persister, id, factory )
+ );
+ }
+
+ EntityPersister subclassPersister = factory.getEntityPersister( entry.getSubclass() );
+ Object result = optionalObject == null ?
+ session.instantiate( subclassPersister, id ) : optionalObject;
+
+ // make it circular-reference safe
+ TwoPhaseLoad.addUninitializedCachedEntity(
+ new EntityKey( id, subclassPersister, session.getEntityMode() ),
+ result,
+ subclassPersister,
+ LockMode.NONE,
+ entry.areLazyPropertiesUnfetched(),
+ entry.getVersion(),
+ session
+ );
+
+ Type[] types = subclassPersister.getPropertyTypes();
+ Object[] values = entry.assemble( result, id, subclassPersister, session.getInterceptor(), session ); // intializes result by side-effect
+ TypeFactory.deepCopy(
+ values,
+ types,
+ subclassPersister.getPropertyUpdateability(),
+ values,
+ session
+ );
+
+ Object version = Versioning.getVersion( values, subclassPersister );
+ if ( log.isTraceEnabled() ) log.trace( "Cached Version: " + version );
+
+ final PersistenceContext persistenceContext = session.getPersistenceContext();
+ persistenceContext.addEntry(
+ result,
+ Status.MANAGED,
+ values,
+ null,
+ id,
+ version,
+ LockMode.NONE,
+ true,
+ subclassPersister,
+ false,
+ entry.areLazyPropertiesUnfetched()
+ );
+ subclassPersister.afterInitialize( result, entry.areLazyPropertiesUnfetched(), session );
+ persistenceContext.initializeNonLazyCollections();
+ // upgrade the lock if necessary:
+ //lock(result, lockMode);
+
+ //PostLoad is needed for EJB3
+ //TODO: reuse the PostLoadEvent...
+ PostLoadEvent postLoadEvent = new PostLoadEvent(session).setEntity(result)
+ .setId(id).setPersister(persister);
+ PostLoadEventListener[] listeners = session.getListeners().getPostLoadEventListeners();
+ for ( int i = 0; i < listeners.length; i++ ) {
+ listeners[i].onPostLoad(postLoadEvent);
+ }
+
+ return result;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultLockEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultLockEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultLockEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,79 @@
+//$Id: DefaultLockEventListener.java 7019 2005-06-05 05:09:58Z oneovthafew $
+package org.hibernate.event.def;
+
+import java.io.Serializable;
+
+import org.hibernate.HibernateException;
+import org.hibernate.LockMode;
+import org.hibernate.TransientObjectException;
+import org.hibernate.engine.Cascade;
+import org.hibernate.engine.CascadingAction;
+import org.hibernate.engine.EntityEntry;
+import org.hibernate.engine.ForeignKeys;
+import org.hibernate.event.EventSource;
+import org.hibernate.event.LockEvent;
+import org.hibernate.event.LockEventListener;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.entity.EntityPersister;
+
+/**
+ * Defines the default lock event listeners used by hibernate to lock entities
+ * in response to generated lock events.
+ *
+ * @author Steve Ebersole
+ */
+public class DefaultLockEventListener extends AbstractLockUpgradeEventListener implements LockEventListener {
+
+ /** Handle the given lock event.
+ *
+ * @param event The lock event to be handled.
+ * @throws HibernateException
+ */
+ public void onLock(LockEvent event) throws HibernateException {
+
+ if ( event.getObject() == null ) {
+ throw new NullPointerException( "attempted to lock null" );
+ }
+
+ if ( event.getLockMode() == LockMode.WRITE ) {
+ throw new HibernateException( "Invalid lock mode for lock()" );
+ }
+
+ SessionImplementor source = event.getSession();
+
+ Object entity = source.getPersistenceContext().unproxyAndReassociate( event.getObject() );
+ //TODO: if object was an uninitialized proxy, this is inefficient,
+ // resulting in two SQL selects
+
+ EntityEntry entry = source.getPersistenceContext().getEntry(entity);
+ if (entry==null) {
+ final EntityPersister persister = source.getEntityPersister( event.getEntityName(), entity );
+ final Serializable id = persister.getIdentifier( entity, source.getEntityMode() );
+ if ( !ForeignKeys.isNotTransient( event.getEntityName(), entity, Boolean.FALSE, source ) ) {
+ throw new TransientObjectException(
+ "cannot lock an unsaved transient instance: " +
+ persister.getEntityName()
+ );
+ }
+
+ entry = reassociate(event, entity, id, persister);
+
+ cascadeOnLock(event, persister, entity);
+ }
+
+ upgradeLock( entity, entry, event.getLockMode(), source );
+ }
+
+ private void cascadeOnLock(LockEvent event, EntityPersister persister, Object entity) {
+ EventSource source = event.getSession();
+ source.getPersistenceContext().incrementCascadeLevel();
+ try {
+ new Cascade(CascadingAction.LOCK, Cascade.AFTER_LOCK, source)
+ .cascade( persister, entity, event.getLockMode() );
+ }
+ finally {
+ source.getPersistenceContext().decrementCascadeLevel();
+ }
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultMergeEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultMergeEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultMergeEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,440 @@
+//$Id: DefaultMergeEventListener.java 10784 2006-11-11 05:13:01Z steve.ebersole at jboss.com $
+package org.hibernate.event.def;
+
+import java.io.Serializable;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.AssertionFailure;
+import org.hibernate.HibernateException;
+import org.hibernate.ObjectDeletedException;
+import org.hibernate.StaleObjectStateException;
+import org.hibernate.WrongClassException;
+import org.hibernate.engine.Cascade;
+import org.hibernate.engine.CascadingAction;
+import org.hibernate.event.EventSource;
+import org.hibernate.event.MergeEvent;
+import org.hibernate.event.MergeEventListener;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.EntityEntry;
+import org.hibernate.engine.EntityKey;
+import org.hibernate.intercept.FieldInterceptionHelper;
+import org.hibernate.intercept.FieldInterceptor;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.proxy.HibernateProxy;
+import org.hibernate.proxy.LazyInitializer;
+import org.hibernate.type.ForeignKeyDirection;
+import org.hibernate.type.TypeFactory;
+import org.hibernate.util.IdentityMap;
+
+/**
+ * Defines the default copy event listener used by hibernate for copying entities
+ * in response to generated copy events.
+ *
+ * @author Gavin King
+ */
+public class DefaultMergeEventListener extends AbstractSaveEventListener
+ implements MergeEventListener {
+
+ private static final Log log = LogFactory.getLog(DefaultMergeEventListener.class);
+
+ protected Map getMergeMap(Object anything) {
+ return IdentityMap.invert( (Map) anything );
+ }
+
+ /**
+ * Handle the given merge event.
+ *
+ * @param event The merge event to be handled.
+ * @throws HibernateException
+ */
+ public void onMerge(MergeEvent event) throws HibernateException {
+ onMerge( event, IdentityMap.instantiate(10) );
+ }
+
+ /**
+ * Handle the given merge event.
+ *
+ * @param event The merge event to be handled.
+ * @throws HibernateException
+ */
+ public void onMerge(MergeEvent event, Map copyCache) throws HibernateException {
+
+ final EventSource source = event.getSession();
+ final Object original = event.getOriginal();
+
+ if ( original != null ) {
+
+ final Object entity;
+ if ( original instanceof HibernateProxy ) {
+ LazyInitializer li = ( (HibernateProxy) original ).getHibernateLazyInitializer();
+ if ( li.isUninitialized() ) {
+ log.trace("ignoring uninitialized proxy");
+ event.setResult( source.load( li.getEntityName(), li.getIdentifier() ) );
+ return; //EARLY EXIT!
+ }
+ else {
+ entity = li.getImplementation();
+ }
+ }
+ else {
+ entity = original;
+ }
+
+ if ( copyCache.containsKey(entity) ) {
+ log.trace("already merged");
+ event.setResult(entity);
+ }
+ else {
+
+ event.setEntity( entity );
+
+ int entityState = -1;
+
+ // Check the persistence context for an entry relating to this
+ // entity to be merged...
+ EntityEntry entry = source.getPersistenceContext().getEntry( entity );
+ if ( entry == null ) {
+ EntityPersister persister = source.getEntityPersister( event.getEntityName(), entity );
+ Serializable id = persister.getIdentifier( entity, source.getEntityMode() );
+ if ( id != null ) {
+ EntityKey key = new EntityKey( id, persister, source.getEntityMode() );
+ Object managedEntity = source.getPersistenceContext().getEntity( key );
+ entry = source.getPersistenceContext().getEntry( managedEntity );
+ if ( entry != null ) {
+ // we have specialized case of a detached entity from the
+ // perspective of the merge operation. Specifically, we
+ // have an incoming entity instance which has a corresponding
+ // entry in the current persistence context, but registered
+ // under a different entity instance
+ entityState = DETACHED;
+ }
+ }
+ }
+
+ if ( entityState == -1 ) {
+ entityState = getEntityState( entity, event.getEntityName(), entry, source );
+ }
+
+ switch (entityState) {
+ case DETACHED:
+ entityIsDetached(event, copyCache);
+ break;
+ case TRANSIENT:
+ entityIsTransient(event, copyCache);
+ break;
+ case PERSISTENT:
+ entityIsPersistent(event, copyCache);
+ break;
+ default: //DELETED
+ throw new ObjectDeletedException(
+ "deleted instance passed to merge",
+ null,
+ getLoggableName( event.getEntityName(), entity )
+ );
+ }
+ }
+
+ }
+
+ }
+
+ protected void entityIsPersistent(MergeEvent event, Map copyCache) {
+ log.trace("ignoring persistent instance");
+
+ //TODO: check that entry.getIdentifier().equals(requestedId)
+
+ final Object entity = event.getEntity();
+ final EventSource source = event.getSession();
+ final EntityPersister persister = source.getEntityPersister( event.getEntityName(), entity );
+
+ copyCache.put(entity, entity); //before cascade!
+
+ cascadeOnMerge(source, persister, entity, copyCache);
+ copyValues(persister, entity, entity, source, copyCache);
+
+ event.setResult(entity);
+ }
+
+ protected void entityIsTransient(MergeEvent event, Map copyCache) {
+
+ log.trace("merging transient instance");
+
+ final Object entity = event.getEntity();
+ final EventSource source = event.getSession();
+
+ final EntityPersister persister = source.getEntityPersister( event.getEntityName(), entity );
+ final String entityName = persister.getEntityName();
+
+ final Serializable id = persister.hasIdentifierProperty() ?
+ persister.getIdentifier( entity, source.getEntityMode() ) :
+ null;
+
+ final Object copy = persister.instantiate( id, source.getEntityMode() ); //TODO: should this be Session.instantiate(Persister, ...)?
+ copyCache.put(entity, copy); //before cascade!
+
+ // cascade first, so that all unsaved objects get their
+ // copy created before we actually copy
+ //cascadeOnMerge(event, persister, entity, copyCache, Cascades.CASCADE_BEFORE_MERGE);
+ super.cascadeBeforeSave(source, persister, entity, copyCache);
+ copyValues(persister, entity, copy, source, copyCache, ForeignKeyDirection.FOREIGN_KEY_FROM_PARENT);
+
+ //this bit is only *really* absolutely necessary for handling
+ //requestedId, but is also good if we merge multiple object
+ //graphs, since it helps ensure uniqueness
+ final Serializable requestedId = event.getRequestedId();
+ if (requestedId==null) {
+ saveWithGeneratedId( copy, entityName, copyCache, source, false );
+ }
+ else {
+ saveWithRequestedId( copy, requestedId, entityName, copyCache, source );
+ }
+
+ // cascade first, so that all unsaved objects get their
+ // copy created before we actually copy
+ super.cascadeAfterSave(source, persister, entity, copyCache);
+ copyValues(persister, entity, copy, source, copyCache, ForeignKeyDirection.FOREIGN_KEY_TO_PARENT);
+
+ event.setResult(copy);
+
+ }
+
+ protected void entityIsDetached(MergeEvent event, Map copyCache) {
+
+ log.trace("merging detached instance");
+
+ final Object entity = event.getEntity();
+ final EventSource source = event.getSession();
+
+ final EntityPersister persister = source.getEntityPersister( event.getEntityName(), entity );
+ final String entityName = persister.getEntityName();
+
+ Serializable id = event.getRequestedId();
+ if ( id == null ) {
+ id = persister.getIdentifier( entity, source.getEntityMode() );
+ }
+ else {
+ // check that entity id = requestedId
+ Serializable entityId = persister.getIdentifier( entity, source.getEntityMode() );
+ if ( !persister.getIdentifierType().isEqual( id, entityId, source.getEntityMode(), source.getFactory() ) ) {
+ throw new HibernateException( "merge requested with id not matching id of passed entity" );
+ }
+ }
+
+ String previousFetchProfile = source.getFetchProfile();
+ source.setFetchProfile("merge");
+ //we must clone embedded composite identifiers, or
+ //we will get back the same instance that we pass in
+ final Serializable clonedIdentifier = (Serializable) persister.getIdentifierType()
+ .deepCopy( id, source.getEntityMode(), source.getFactory() );
+ final Object result = source.get(entityName, clonedIdentifier);
+ source.setFetchProfile(previousFetchProfile);
+
+ if ( result == null ) {
+ //TODO: we should throw an exception if we really *know* for sure
+ // that this is a detached instance, rather than just assuming
+ //throw new StaleObjectStateException(entityName, id);
+
+ // we got here because we assumed that an instance
+ // with an assigned id was detached, when it was
+ // really persistent
+ entityIsTransient(event, copyCache);
+ }
+ else {
+ copyCache.put(entity, result); //before cascade!
+
+ final Object target = source.getPersistenceContext().unproxy(result);
+ if ( target == entity ) {
+ throw new AssertionFailure("entity was not detached");
+ }
+ else if ( !source.getEntityName(target).equals(entityName) ) {
+ throw new WrongClassException(
+ "class of the given object did not match class of persistent copy",
+ event.getRequestedId(),
+ entityName
+ );
+ }
+ else if ( isVersionChanged( entity, source, persister, target ) ) {
+ if ( source.getFactory().getStatistics().isStatisticsEnabled() ) {
+ source.getFactory().getStatisticsImplementor()
+ .optimisticFailure( entityName );
+ }
+ throw new StaleObjectStateException( entityName, id );
+ }
+
+ // cascade first, so that all unsaved objects get their
+ // copy created before we actually copy
+ cascadeOnMerge(source, persister, entity, copyCache);
+ copyValues(persister, entity, target, source, copyCache);
+
+ //copyValues works by reflection, so explicitly mark the entity instance dirty
+ markInterceptorDirty( entity, target );
+
+ event.setResult(result);
+ }
+
+ }
+
+ private void markInterceptorDirty(final Object entity, final Object target) {
+ if ( FieldInterceptionHelper.isInstrumented( entity ) ) {
+ FieldInterceptor interceptor = FieldInterceptionHelper.extractFieldInterceptor( target );
+ if ( interceptor != null ) {
+ interceptor.dirty();
+ }
+ }
+ }
+
+ private boolean isVersionChanged(Object entity, EventSource source, EntityPersister persister, Object target) {
+ if ( ! persister.isVersioned() ) {
+ return false;
+ }
+ // for merging of versioned entities, we consider the version having
+ // been changed only when:
+ // 1) the two version values are different;
+ // *AND*
+ // 2) The target actually represents database state!
+ //
+ // This second condition is a special case which allows
+ // an entity to be merged during the same transaction
+ // (though during a seperate operation) in which it was
+ // originally persisted/saved
+ boolean changed = ! persister.getVersionType().isSame(
+ persister.getVersion( target, source.getEntityMode() ),
+ persister.getVersion( entity, source.getEntityMode() ),
+ source.getEntityMode()
+ );
+
+ // TODO : perhaps we should additionally require that the incoming entity
+ // version be equivalent to the defined unsaved-value?
+ return changed && existsInDatabase( target, source, persister );
+ }
+
+ private boolean existsInDatabase(Object entity, EventSource source, EntityPersister persister) {
+ EntityEntry entry = source.getPersistenceContext().getEntry( entity );
+ if ( entry == null ) {
+ Serializable id = persister.getIdentifier( entity, source.getEntityMode() );
+ if ( id != null ) {
+ EntityKey key = new EntityKey( id, persister, source.getEntityMode() );
+ Object managedEntity = source.getPersistenceContext().getEntity( key );
+ entry = source.getPersistenceContext().getEntry( managedEntity );
+ }
+ }
+
+ if ( entry == null ) {
+ // perhaps this should be an exception since it is only ever used
+ // in the above method?
+ return false;
+ }
+ else {
+ return entry.isExistsInDatabase();
+ }
+ }
+
+ protected void copyValues(
+ final EntityPersister persister,
+ final Object entity,
+ final Object target,
+ final SessionImplementor source,
+ final Map copyCache
+ ) {
+
+ final Object[] copiedValues = TypeFactory.replace(
+ persister.getPropertyValues( entity, source.getEntityMode() ),
+ persister.getPropertyValues( target, source.getEntityMode() ),
+ persister.getPropertyTypes(),
+ source,
+ target,
+ copyCache
+ );
+
+ persister.setPropertyValues( target, copiedValues, source.getEntityMode() );
+ }
+
+ protected void copyValues(
+ final EntityPersister persister,
+ final Object entity,
+ final Object target,
+ final SessionImplementor source,
+ final Map copyCache,
+ final ForeignKeyDirection foreignKeyDirection) {
+
+ final Object[] copiedValues;
+
+ if ( foreignKeyDirection == ForeignKeyDirection.FOREIGN_KEY_TO_PARENT ) {
+ // this is the second pass through on a merge op, so here we limit the
+ // replacement to associations types (value types were already replaced
+ // during the first pass)
+ copiedValues = TypeFactory.replaceAssociations(
+ persister.getPropertyValues( entity, source.getEntityMode() ),
+ persister.getPropertyValues( target, source.getEntityMode() ),
+ persister.getPropertyTypes(),
+ source,
+ target,
+ copyCache,
+ foreignKeyDirection
+ );
+ }
+ else {
+ copiedValues = TypeFactory.replace(
+ persister.getPropertyValues( entity, source.getEntityMode() ),
+ persister.getPropertyValues( target, source.getEntityMode() ),
+ persister.getPropertyTypes(),
+ source,
+ target,
+ copyCache,
+ foreignKeyDirection
+ );
+ }
+
+ persister.setPropertyValues( target, copiedValues, source.getEntityMode() );
+ }
+
+ /**
+ * Perform any cascades needed as part of this copy event.
+ *
+ * @param source The merge event being processed.
+ * @param persister The persister of the entity being copied.
+ * @param entity The entity being copied.
+ * @param copyCache A cache of already copied instance.
+ */
+ protected void cascadeOnMerge(
+ final EventSource source,
+ final EntityPersister persister,
+ final Object entity,
+ final Map copyCache
+ ) {
+ source.getPersistenceContext().incrementCascadeLevel();
+ try {
+ new Cascade( getCascadeAction(), Cascade.BEFORE_MERGE, source )
+ .cascade(persister, entity, copyCache);
+ }
+ finally {
+ source.getPersistenceContext().decrementCascadeLevel();
+ }
+ }
+
+
+ protected CascadingAction getCascadeAction() {
+ return CascadingAction.MERGE;
+ }
+
+ protected Boolean getAssumedUnsaved() {
+ return Boolean.FALSE;
+ }
+
+ /**
+ * Cascade behavior is redefined by this subclass, disable superclass behavior
+ */
+ protected void cascadeAfterSave(EventSource source, EntityPersister persister, Object entity, Object anything)
+ throws HibernateException {
+ }
+
+ /**
+ * Cascade behavior is redefined by this subclass, disable superclass behavior
+ */
+ protected void cascadeBeforeSave(EventSource source, EntityPersister persister, Object entity, Object anything)
+ throws HibernateException {
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultMergeEventListener.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultPersistEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultPersistEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultPersistEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,144 @@
+// $Id: DefaultPersistEventListener.java 9673 2006-03-22 14:57:59Z steve.ebersole at jboss.com $
+package org.hibernate.event.def;
+
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.HibernateException;
+import org.hibernate.ObjectDeletedException;
+import org.hibernate.PersistentObjectException;
+import org.hibernate.engine.CascadingAction;
+import org.hibernate.event.EventSource;
+import org.hibernate.event.PersistEvent;
+import org.hibernate.event.PersistEventListener;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.proxy.HibernateProxy;
+import org.hibernate.proxy.LazyInitializer;
+import org.hibernate.util.IdentityMap;
+
+/**
+ * Defines the default create event listener used by hibernate for creating
+ * transient entities in response to generated create events.
+ *
+ * @author Gavin King
+ */
+public class DefaultPersistEventListener extends AbstractSaveEventListener implements PersistEventListener {
+
+ private static final Log log = LogFactory.getLog(DefaultPersistEventListener.class);
+
+ /**
+ * Handle the given create event.
+ *
+ * @param event The create event to be handled.
+ * @throws HibernateException
+ */
+ public void onPersist(PersistEvent event) throws HibernateException {
+ onPersist( event, IdentityMap.instantiate(10) );
+ }
+
+
+ /**
+ * Handle the given create event.
+ *
+ * @param event The create event to be handled.
+ * @throws HibernateException
+ */
+ public void onPersist(PersistEvent event, Map createCache) throws HibernateException {
+
+ final SessionImplementor source = event.getSession();
+ final Object object = event.getObject();
+
+ final Object entity;
+ if (object instanceof HibernateProxy) {
+ LazyInitializer li = ( (HibernateProxy) object ).getHibernateLazyInitializer();
+ if ( li.isUninitialized() ) {
+ if ( li.getSession()==source ) {
+ return; //NOTE EARLY EXIT!
+ }
+ else {
+ throw new PersistentObjectException("uninitialized proxy passed to persist()");
+ }
+ }
+ entity = li.getImplementation();
+ }
+ else {
+ entity = object;
+ }
+
+ int entityState = getEntityState(
+ entity,
+ event.getEntityName(),
+ source.getPersistenceContext().getEntry(entity),
+ source
+ );
+
+ switch (entityState) {
+ case DETACHED:
+ throw new PersistentObjectException(
+ "detached entity passed to persist: " +
+ getLoggableName( event.getEntityName(), entity )
+ );
+ case PERSISTENT:
+ entityIsPersistent(event, createCache);
+ break;
+ case TRANSIENT:
+ entityIsTransient(event, createCache);
+ break;
+ default:
+ throw new ObjectDeletedException(
+ "deleted entity passed to persist",
+ null,
+ getLoggableName( event.getEntityName(), entity )
+ );
+ }
+
+ }
+
+ protected void entityIsPersistent(PersistEvent event, Map createCache) {
+ log.trace("ignoring persistent instance");
+ final EventSource source = event.getSession();
+
+ //TODO: check that entry.getIdentifier().equals(requestedId)
+
+ final Object entity = source.getPersistenceContext().unproxy( event.getObject() );
+ final EntityPersister persister = source.getEntityPersister( event.getEntityName(), entity );
+
+ if ( createCache.put(entity, entity)==null ) {
+ //TODO: merge into one method!
+ cascadeBeforeSave(source, persister, entity, createCache);
+ cascadeAfterSave(source, persister, entity, createCache);
+ }
+
+ }
+
+ /**
+ * Handle the given create event.
+ *
+ * @param event The save event to be handled.
+ * @throws HibernateException
+ */
+ protected void entityIsTransient(PersistEvent event, Map createCache) throws HibernateException {
+
+ log.trace("saving transient instance");
+
+ final EventSource source = event.getSession();
+
+ final Object entity = source.getPersistenceContext().unproxy( event.getObject() );
+
+ if ( createCache.put(entity, entity)==null ) {
+ saveWithGeneratedId( entity, event.getEntityName(), createCache, source, false );
+ }
+
+ }
+
+ protected CascadingAction getCascadeAction() {
+ return CascadingAction.PERSIST;
+ }
+
+ protected Boolean getAssumedUnsaved() {
+ return Boolean.TRUE;
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultPersistEventListener.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultPersistOnFlushEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultPersistOnFlushEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultPersistOnFlushEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,14 @@
+//$Id: DefaultPersistOnFlushEventListener.java 9019 2006-01-11 18:50:33Z epbernard $
+package org.hibernate.event.def;
+
+import org.hibernate.engine.CascadingAction;
+
+/**
+ * When persist is used as the cascade action, persistOnFlush should be used
+ * @author Emmanuel Bernard
+ */
+public class DefaultPersistOnFlushEventListener extends DefaultPersistEventListener {
+ protected CascadingAction getCascadeAction() {
+ return CascadingAction.PERSIST_ON_FLUSH;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultPostLoadEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultPostLoadEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultPostLoadEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+//$Id: DefaultPostLoadEventListener.java 7785 2005-08-08 23:24:44Z oneovthafew $
+package org.hibernate.event.def;
+
+import org.hibernate.classic.Lifecycle;
+import org.hibernate.event.PostLoadEvent;
+import org.hibernate.event.PostLoadEventListener;
+
+/**
+ * Call <tt>Lifecycle</tt> interface if necessary
+ *
+ * @author Gavin King
+ */
+public class DefaultPostLoadEventListener implements PostLoadEventListener {
+
+ public void onPostLoad(PostLoadEvent event) {
+ if ( event.getPersister().implementsLifecycle( event.getSession().getEntityMode() ) ) {
+ //log.debug( "calling onLoad()" );
+ ( ( Lifecycle ) event.getEntity() ).onLoad( event.getSession(), event.getId() );
+ }
+
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultPreLoadEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultPreLoadEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultPreLoadEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,29 @@
+//$Id: DefaultPreLoadEventListener.java 7785 2005-08-08 23:24:44Z oneovthafew $
+package org.hibernate.event.def;
+
+import org.hibernate.event.PreLoadEvent;
+import org.hibernate.event.PreLoadEventListener;
+import org.hibernate.persister.entity.EntityPersister;
+
+/**
+ * Called before injecting property values into a newly
+ * loaded entity instance.
+ *
+ * @author Gavin King
+ */
+public class DefaultPreLoadEventListener implements PreLoadEventListener {
+
+ public void onPreLoad(PreLoadEvent event) {
+ EntityPersister persister = event.getPersister();
+ event.getSession()
+ .getInterceptor()
+ .onLoad(
+ event.getEntity(),
+ event.getId(),
+ event.getState(),
+ persister.getPropertyNames(),
+ persister.getPropertyTypes()
+ );
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultPreLoadEventListener.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultRefreshEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultRefreshEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultRefreshEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,146 @@
+//$Id: DefaultRefreshEventListener.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.event.def;
+
+import java.io.Serializable;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.HibernateException;
+import org.hibernate.PersistentObjectException;
+import org.hibernate.UnresolvableObjectException;
+import org.hibernate.cache.CacheKey;
+import org.hibernate.engine.Cascade;
+import org.hibernate.engine.CascadingAction;
+import org.hibernate.engine.EntityEntry;
+import org.hibernate.engine.EntityKey;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.event.EventSource;
+import org.hibernate.event.RefreshEvent;
+import org.hibernate.event.RefreshEventListener;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.pretty.MessageHelper;
+import org.hibernate.type.AbstractComponentType;
+import org.hibernate.type.CollectionType;
+import org.hibernate.type.Type;
+import org.hibernate.util.IdentityMap;
+
+/**
+ * Defines the default refresh event listener used by hibernate for refreshing entities
+ * in response to generated refresh events.
+ *
+ * @author Steve Ebersole
+ */
+public class DefaultRefreshEventListener implements RefreshEventListener {
+
+ private static final Log log = LogFactory.getLog(DefaultRefreshEventListener.class);
+
+ public void onRefresh(RefreshEvent event) throws HibernateException {
+ onRefresh( event, IdentityMap.instantiate(10) );
+ }
+
+ /**
+ * Handle the given refresh event.
+ *
+ * @param event The refresh event to be handled.
+ */
+ public void onRefresh(RefreshEvent event, Map refreshedAlready) {
+
+ final EventSource source = event.getSession();
+
+ if ( source.getPersistenceContext().reassociateIfUninitializedProxy( event.getObject() ) ) return;
+
+ final Object object = source.getPersistenceContext().unproxyAndReassociate( event.getObject() );
+
+ if ( refreshedAlready.containsKey(object) ) {
+ log.trace("already refreshed");
+ return;
+ }
+
+ final EntityEntry e = source.getPersistenceContext().getEntry( object );
+ final EntityPersister persister;
+ final Serializable id;
+
+ if ( e == null ) {
+ persister = source.getEntityPersister(null, object); //refresh() does not pass an entityName
+ id = persister.getIdentifier( object, event.getSession().getEntityMode() );
+ if ( log.isTraceEnabled() ) {
+ log.trace(
+ "refreshing transient " +
+ MessageHelper.infoString( persister, id, source.getFactory() )
+ );
+ }
+ EntityKey key = new EntityKey( id, persister, source.getEntityMode() );
+ if ( source.getPersistenceContext().getEntry(key) != null ) {
+ throw new PersistentObjectException(
+ "attempted to refresh transient instance when persistent instance was already associated with the Session: " +
+ MessageHelper.infoString(persister, id, source.getFactory() )
+ );
+ }
+ }
+ else {
+ if ( log.isTraceEnabled() ) {
+ log.trace(
+ "refreshing " +
+ MessageHelper.infoString( e.getPersister(), e.getId(), source.getFactory() )
+ );
+ }
+ if ( !e.isExistsInDatabase() ) {
+ throw new HibernateException( "this instance does not yet exist as a row in the database" );
+ }
+
+ persister = e.getPersister();
+ id = e.getId();
+ }
+
+ // cascade the refresh prior to refreshing this entity
+ refreshedAlready.put(object, object);
+ new Cascade(CascadingAction.REFRESH, Cascade.BEFORE_REFRESH, source)
+ .cascade( persister, object, refreshedAlready );
+
+ if ( e != null ) {
+ EntityKey key = new EntityKey( id, persister, source.getEntityMode() );
+ source.getPersistenceContext().removeEntity(key);
+ if ( persister.hasCollections() ) new EvictVisitor( source ).process(object, persister);
+ }
+
+ if ( persister.hasCache() ) {
+ final CacheKey ck = new CacheKey(
+ id,
+ persister.getIdentifierType(),
+ persister.getRootEntityName(),
+ source.getEntityMode(),
+ source.getFactory()
+ );
+ persister.getCacheAccessStrategy().evict( ck );
+ }
+
+ evictCachedCollections( persister, id, source.getFactory() );
+
+ String previousFetchProfile = source.getFetchProfile();
+ source.setFetchProfile("refresh");
+ Object result = persister.load( id, object, event.getLockMode(), source );
+ source.setFetchProfile(previousFetchProfile);
+
+ UnresolvableObjectException.throwIfNull( result, id, persister.getEntityName() );
+
+ }
+
+ private void evictCachedCollections(EntityPersister persister, Serializable id, SessionFactoryImplementor factory) {
+ evictCachedCollections( persister.getPropertyTypes(), id, factory );
+ }
+
+ private void evictCachedCollections(Type[] types, Serializable id, SessionFactoryImplementor factory)
+ throws HibernateException {
+ for ( int i = 0; i < types.length; i++ ) {
+ if ( types[i].isCollectionType() ) {
+ factory.evictCollection( ( (CollectionType) types[i] ).getRole(), id );
+ }
+ else if ( types[i].isComponentType() ) {
+ AbstractComponentType actype = (AbstractComponentType) types[i];
+ evictCachedCollections( actype.getSubtypes(), id, factory );
+ }
+ }
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultReplicateEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultReplicateEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultReplicateEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,206 @@
+//$Id: DefaultReplicateEventListener.java 11089 2007-01-24 14:34:22Z max.andersen at jboss.com $
+package org.hibernate.event.def;
+
+import org.hibernate.HibernateException;
+import org.hibernate.TransientObjectException;
+import org.hibernate.ReplicationMode;
+import org.hibernate.LockMode;
+import org.hibernate.engine.Cascade;
+import org.hibernate.engine.CascadingAction;
+import org.hibernate.engine.EntityKey;
+import org.hibernate.engine.Status;
+import org.hibernate.event.EventSource;
+import org.hibernate.event.ReplicateEvent;
+import org.hibernate.event.ReplicateEventListener;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.pretty.MessageHelper;
+import org.hibernate.type.Type;
+
+import java.io.Serializable;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Defines the default replicate event listener used by Hibernate to replicate
+ * entities in response to generated replicate events.
+ *
+ * @author Steve Ebersole
+ */
+public class DefaultReplicateEventListener extends AbstractSaveEventListener implements ReplicateEventListener {
+
+ private static final Log log = LogFactory.getLog( DefaultReplicateEventListener.class );
+
+ /**
+ * Handle the given replicate event.
+ *
+ * @param event The replicate event to be handled.
+ *
+ * @throws TransientObjectException An invalid attempt to replicate a transient entity.
+ */
+ public void onReplicate(ReplicateEvent event) {
+ final EventSource source = event.getSession();
+ if ( source.getPersistenceContext().reassociateIfUninitializedProxy( event.getObject() ) ) {
+ log.trace( "uninitialized proxy passed to replicate()" );
+ return;
+ }
+
+ Object entity = source.getPersistenceContext().unproxyAndReassociate( event.getObject() );
+
+ if ( source.getPersistenceContext().isEntryFor( entity ) ) {
+ log.trace( "ignoring persistent instance passed to replicate()" );
+ //hum ... should we cascade anyway? throw an exception? fine like it is?
+ return;
+ }
+
+ EntityPersister persister = source.getEntityPersister( event.getEntityName(), entity );
+
+ // get the id from the object
+ /*if ( persister.isUnsaved(entity, source) ) {
+ throw new TransientObjectException("transient instance passed to replicate()");
+ }*/
+ Serializable id = persister.getIdentifier( entity, source.getEntityMode() );
+ if ( id == null ) {
+ throw new TransientObjectException( "instance with null id passed to replicate()" );
+ }
+
+ final ReplicationMode replicationMode = event.getReplicationMode();
+
+ final Object oldVersion;
+ if ( replicationMode == ReplicationMode.EXCEPTION ) {
+ //always do an INSERT, and let it fail by constraint violation
+ oldVersion = null;
+ }
+ else {
+ //what is the version on the database?
+ oldVersion = persister.getCurrentVersion( id, source );
+ }
+
+ if ( oldVersion != null ) {
+ if ( log.isTraceEnabled() ) {
+ log.trace(
+ "found existing row for " +
+ MessageHelper.infoString( persister, id, source.getFactory() )
+ );
+ }
+
+ /// HHH-2378
+ final Object realOldVersion = persister.isVersioned() ? oldVersion : null;
+
+ boolean canReplicate = replicationMode.shouldOverwriteCurrentVersion(
+ entity,
+ realOldVersion,
+ persister.getVersion( entity, source.getEntityMode() ),
+ persister.getVersionType()
+ );
+
+ if ( canReplicate ) {
+ //will result in a SQL UPDATE:
+ performReplication( entity, id, realOldVersion, persister, replicationMode, source );
+ }
+ else {
+ //else do nothing (don't even reassociate object!)
+ log.trace( "no need to replicate" );
+ }
+
+ //TODO: would it be better to do a refresh from db?
+ }
+ else {
+ // no existing row - do an insert
+ if ( log.isTraceEnabled() ) {
+ log.trace(
+ "no existing row, replicating new instance " +
+ MessageHelper.infoString( persister, id, source.getFactory() )
+ );
+ }
+
+ final boolean regenerate = persister.isIdentifierAssignedByInsert(); // prefer re-generation of identity!
+ final EntityKey key = regenerate ?
+ null : new EntityKey( id, persister, source.getEntityMode() );
+
+ performSaveOrReplicate(
+ entity,
+ key,
+ persister,
+ regenerate,
+ replicationMode,
+ source,
+ true
+ );
+
+ }
+ }
+
+ protected boolean visitCollectionsBeforeSave(Object entity, Serializable id, Object[] values, Type[] types, EventSource source) {
+ //TODO: we use two visitors here, inefficient!
+ OnReplicateVisitor visitor = new OnReplicateVisitor( source, id, entity, false );
+ visitor.processEntityPropertyValues( values, types );
+ return super.visitCollectionsBeforeSave( entity, id, values, types, source );
+ }
+
+ protected boolean substituteValuesIfNecessary(
+ Object entity,
+ Serializable id,
+ Object[] values,
+ EntityPersister persister,
+ SessionImplementor source) {
+ return false;
+ }
+
+ protected boolean isVersionIncrementDisabled() {
+ return true;
+ }
+
+ private void performReplication(
+ Object entity,
+ Serializable id,
+ Object version,
+ EntityPersister persister,
+ ReplicationMode replicationMode,
+ EventSource source) throws HibernateException {
+
+ if ( log.isTraceEnabled() ) {
+ log.trace(
+ "replicating changes to " +
+ MessageHelper.infoString( persister, id, source.getFactory() )
+ );
+ }
+
+ new OnReplicateVisitor( source, id, entity, true ).process( entity, persister );
+
+ source.getPersistenceContext().addEntity(
+ entity,
+ Status.MANAGED,
+ null,
+ new EntityKey( id, persister, source.getEntityMode() ),
+ version,
+ LockMode.NONE,
+ true,
+ persister,
+ true,
+ false
+ );
+
+ cascadeAfterReplicate( entity, persister, replicationMode, source );
+ }
+
+ private void cascadeAfterReplicate(
+ Object entity,
+ EntityPersister persister,
+ ReplicationMode replicationMode,
+ EventSource source) {
+ source.getPersistenceContext().incrementCascadeLevel();
+ try {
+ new Cascade( CascadingAction.REPLICATE, Cascade.AFTER_UPDATE, source )
+ .cascade( persister, entity, replicationMode );
+ }
+ finally {
+ source.getPersistenceContext().decrementCascadeLevel();
+ }
+ }
+
+ protected CascadingAction getCascadeAction() {
+ return CascadingAction.REPLICATE;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultSaveEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultSaveEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultSaveEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,57 @@
+//$Id: DefaultSaveEventListener.java 8486 2005-10-29 10:32:45Z oneovthafew $
+package org.hibernate.event.def;
+
+import java.io.Serializable;
+
+import org.hibernate.Hibernate;
+import org.hibernate.PersistentObjectException;
+import org.hibernate.engine.EntityEntry;
+import org.hibernate.engine.Status;
+import org.hibernate.event.SaveOrUpdateEvent;
+import org.hibernate.engine.SessionImplementor;
+
+/**
+ * An event handler for save() events
+ * @author Gavin King
+ */
+public class DefaultSaveEventListener extends DefaultSaveOrUpdateEventListener {
+
+ protected Serializable performSaveOrUpdate(SaveOrUpdateEvent event) {
+ // this implementation is supposed to tolerate incorrect unsaved-value
+ // mappings, for the purpose of backward-compatibility
+ EntityEntry entry = event.getSession().getPersistenceContext().getEntry( event.getEntity() );
+ if ( entry!=null && entry.getStatus() != Status.DELETED ) {
+ return entityIsPersistent(event);
+ }
+ else {
+ return entityIsTransient(event);
+ }
+ }
+
+ protected Serializable saveWithGeneratedOrRequestedId(SaveOrUpdateEvent event) {
+ if ( event.getRequestedId() == null ) {
+ return super.saveWithGeneratedOrRequestedId(event);
+ }
+ else {
+ return saveWithRequestedId(
+ event.getEntity(),
+ event.getRequestedId(),
+ event.getEntityName(),
+ null,
+ event.getSession()
+ );
+ }
+
+ }
+
+ protected boolean reassociateIfUninitializedProxy(Object object, SessionImplementor source) {
+ if ( !Hibernate.isInitialized(object) ) {
+ throw new PersistentObjectException("uninitialized proxy passed to save()");
+ }
+ else {
+ return false;
+ }
+ }
+
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultSaveOrUpdateCopyEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultSaveOrUpdateCopyEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultSaveOrUpdateCopyEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,12 @@
+//$Id: DefaultSaveOrUpdateCopyEventListener.java 7793 2005-08-10 05:06:40Z oneovthafew $
+package org.hibernate.event.def;
+
+import org.hibernate.engine.CascadingAction;
+
+public class DefaultSaveOrUpdateCopyEventListener extends DefaultMergeEventListener {
+
+ protected CascadingAction getCascadeAction() {
+ return CascadingAction.SAVE_UPDATE_COPY;
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultSaveOrUpdateCopyEventListener.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultSaveOrUpdateEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultSaveOrUpdateEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultSaveOrUpdateEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,368 @@
+// $Id: DefaultSaveOrUpdateEventListener.java 10948 2006-12-07 21:53:10Z steve.ebersole at jboss.com $
+package org.hibernate.event.def;
+
+import java.io.Serializable;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.hibernate.AssertionFailure;
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.LockMode;
+import org.hibernate.PersistentObjectException;
+import org.hibernate.TransientObjectException;
+import org.hibernate.classic.Lifecycle;
+import org.hibernate.engine.Cascade;
+import org.hibernate.engine.CascadingAction;
+import org.hibernate.engine.EntityEntry;
+import org.hibernate.engine.EntityKey;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.Status;
+import org.hibernate.event.EventSource;
+import org.hibernate.event.SaveOrUpdateEvent;
+import org.hibernate.event.SaveOrUpdateEventListener;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.pretty.MessageHelper;
+import org.hibernate.proxy.HibernateProxy;
+
+/**
+ * Defines the default listener used by Hibernate for handling save-update
+ * events.
+ *
+ * @author Steve Ebersole
+ * @author Gavin King
+ */
+public class DefaultSaveOrUpdateEventListener extends AbstractSaveEventListener implements SaveOrUpdateEventListener {
+
+ private static final Log log = LogFactory.getLog( DefaultSaveOrUpdateEventListener.class );
+
+ /**
+ * Handle the given update event.
+ *
+ * @param event The update event to be handled.
+ */
+ public void onSaveOrUpdate(SaveOrUpdateEvent event) {
+ final SessionImplementor source = event.getSession();
+ final Object object = event.getObject();
+ final Serializable requestedId = event.getRequestedId();
+
+ if ( requestedId != null ) {
+ //assign the requested id to the proxy, *before*
+ //reassociating the proxy
+ if ( object instanceof HibernateProxy ) {
+ ( ( HibernateProxy ) object ).getHibernateLazyInitializer().setIdentifier( requestedId );
+ }
+ }
+
+ if ( reassociateIfUninitializedProxy( object, source ) ) {
+ log.trace( "reassociated uninitialized proxy" );
+ // an uninitialized proxy, noop, don't even need to
+ // return an id, since it is never a save()
+ }
+ else {
+ //initialize properties of the event:
+ final Object entity = source.getPersistenceContext().unproxyAndReassociate( object );
+ event.setEntity( entity );
+ event.setEntry( source.getPersistenceContext().getEntry( entity ) );
+ //return the id in the event object
+ event.setResultId( performSaveOrUpdate( event ) );
+ }
+
+ }
+
+ protected boolean reassociateIfUninitializedProxy(Object object, SessionImplementor source) {
+ return source.getPersistenceContext().reassociateIfUninitializedProxy( object );
+ }
+
+ protected Serializable performSaveOrUpdate(SaveOrUpdateEvent event) {
+ int entityState = getEntityState(
+ event.getEntity(),
+ event.getEntityName(),
+ event.getEntry(),
+ event.getSession()
+ );
+
+ switch ( entityState ) {
+ case DETACHED:
+ entityIsDetached( event );
+ return null;
+ case PERSISTENT:
+ return entityIsPersistent( event );
+ default: //TRANSIENT or DELETED
+ return entityIsTransient( event );
+ }
+ }
+
+ protected Serializable entityIsPersistent(SaveOrUpdateEvent event) throws HibernateException {
+ log.trace( "ignoring persistent instance" );
+
+ EntityEntry entityEntry = event.getEntry();
+ if ( entityEntry == null ) {
+ throw new AssertionFailure( "entity was transient or detached" );
+ }
+ else {
+
+ if ( entityEntry.getStatus() == Status.DELETED ) {
+ throw new AssertionFailure( "entity was deleted" );
+ }
+
+ final SessionFactoryImplementor factory = event.getSession().getFactory();
+
+ Serializable requestedId = event.getRequestedId();
+
+ Serializable savedId;
+ if ( requestedId == null ) {
+ savedId = entityEntry.getId();
+ }
+ else {
+
+ final boolean isEqual = !entityEntry.getPersister().getIdentifierType()
+ .isEqual( requestedId, entityEntry.getId(), event.getSession().getEntityMode(), factory );
+
+ if ( isEqual ) {
+ throw new PersistentObjectException(
+ "object passed to save() was already persistent: " +
+ MessageHelper.infoString( entityEntry.getPersister(), requestedId, factory )
+ );
+ }
+
+ savedId = requestedId;
+
+ }
+
+ if ( log.isTraceEnabled() ) {
+ log.trace(
+ "object already associated with session: " +
+ MessageHelper.infoString( entityEntry.getPersister(), savedId, factory )
+ );
+ }
+
+ return savedId;
+
+ }
+ }
+
+ /**
+ * The given save-update event named a transient entity.
+ * <p/>
+ * Here, we will perform the save processing.
+ *
+ * @param event The save event to be handled.
+ *
+ * @return The entity's identifier after saving.
+ */
+ protected Serializable entityIsTransient(SaveOrUpdateEvent event) {
+
+ log.trace( "saving transient instance" );
+
+ final EventSource source = event.getSession();
+
+ EntityEntry entityEntry = event.getEntry();
+ if ( entityEntry != null ) {
+ if ( entityEntry.getStatus() == Status.DELETED ) {
+ source.forceFlush( entityEntry );
+ }
+ else {
+ throw new AssertionFailure( "entity was persistent" );
+ }
+ }
+
+ Serializable id = saveWithGeneratedOrRequestedId( event );
+
+ source.getPersistenceContext().reassociateProxy( event.getObject(), id );
+
+ return id;
+ }
+
+ /**
+ * Save the transient instance, assigning the right identifier
+ *
+ * @param event The initiating event.
+ *
+ * @return The entity's identifier value after saving.
+ */
+ protected Serializable saveWithGeneratedOrRequestedId(SaveOrUpdateEvent event) {
+ return saveWithGeneratedId(
+ event.getEntity(),
+ event.getEntityName(),
+ null,
+ event.getSession(),
+ true
+ );
+ }
+
+ /**
+ * The given save-update event named a detached entity.
+ * <p/>
+ * Here, we will perform the update processing.
+ *
+ * @param event The update event to be handled.
+ */
+ protected void entityIsDetached(SaveOrUpdateEvent event) {
+
+ log.trace( "updating detached instance" );
+
+
+ if ( event.getSession().getPersistenceContext().isEntryFor( event.getEntity() ) ) {
+ //TODO: assertion only, could be optimized away
+ throw new AssertionFailure( "entity was persistent" );
+ }
+
+ Object entity = event.getEntity();
+
+ EntityPersister persister = event.getSession().getEntityPersister( event.getEntityName(), entity );
+
+ event.setRequestedId(
+ getUpdateId(
+ entity, persister, event.getRequestedId(), event.getSession().getEntityMode()
+ )
+ );
+
+ performUpdate( event, entity, persister );
+
+ }
+
+ /**
+ * Determine the id to use for updating.
+ *
+ * @param entity The entity.
+ * @param persister The entity persister
+ * @param requestedId The requested identifier
+ * @param entityMode The entity mode.
+ *
+ * @return The id.
+ *
+ * @throws TransientObjectException If the entity is considered transient.
+ */
+ protected Serializable getUpdateId(
+ Object entity,
+ EntityPersister persister,
+ Serializable requestedId,
+ EntityMode entityMode) {
+ // use the id assigned to the instance
+ Serializable id = persister.getIdentifier( entity, entityMode );
+ if ( id == null ) {
+ // assume this is a newly instantiated transient object
+ // which should be saved rather than updated
+ throw new TransientObjectException(
+ "The given object has a null identifier: " +
+ persister.getEntityName()
+ );
+ }
+ else {
+ return id;
+ }
+
+ }
+
+ protected void performUpdate(
+ SaveOrUpdateEvent event,
+ Object entity,
+ EntityPersister persister) throws HibernateException {
+
+ if ( !persister.isMutable() ) {
+ log.trace( "immutable instance passed to doUpdate(), locking" );
+ reassociate( event, entity, event.getRequestedId(), persister );
+ }
+ else {
+
+ if ( log.isTraceEnabled() ) {
+ log.trace(
+ "updating " +
+ MessageHelper.infoString(
+ persister, event.getRequestedId(), event.getSession().getFactory()
+ )
+ );
+ }
+
+ final EventSource source = event.getSession();
+
+ EntityKey key = new EntityKey( event.getRequestedId(), persister, source.getEntityMode() );
+
+ source.getPersistenceContext().checkUniqueness( key, entity );
+
+ if ( invokeUpdateLifecycle( entity, persister, source ) ) {
+ reassociate( event, event.getObject(), event.getRequestedId(), persister );
+ return;
+ }
+
+ // this is a transient object with existing persistent state not loaded by the session
+
+ new OnUpdateVisitor( source, event.getRequestedId(), entity ).process( entity, persister );
+
+ //TODO: put this stuff back in to read snapshot from
+ // the second-level cache (needs some extra work)
+ /*Object[] cachedState = null;
+
+ if ( persister.hasCache() ) {
+ CacheEntry entry = (CacheEntry) persister.getCache()
+ .get( event.getRequestedId(), source.getTimestamp() );
+ cachedState = entry==null ?
+ null :
+ entry.getState(); //TODO: half-assemble this stuff
+ }*/
+
+ source.getPersistenceContext().addEntity(
+ entity,
+ Status.MANAGED,
+ null, //cachedState,
+ key,
+ persister.getVersion( entity, source.getEntityMode() ),
+ LockMode.NONE,
+ true,
+ persister,
+ false,
+ true //assume true, since we don't really know, and it doesn't matter
+ );
+
+ persister.afterReassociate( entity, source );
+
+ if ( log.isTraceEnabled() ) {
+ log.trace(
+ "updating " +
+ MessageHelper.infoString( persister, event.getRequestedId(), source.getFactory() )
+ );
+ }
+
+ cascadeOnUpdate( event, persister, entity );
+
+ }
+ }
+
+ protected boolean invokeUpdateLifecycle(Object entity, EntityPersister persister, EventSource source) {
+ if ( persister.implementsLifecycle( source.getEntityMode() ) ) {
+ log.debug( "calling onUpdate()" );
+ if ( ( ( Lifecycle ) entity ).onUpdate( source ) ) {
+ log.debug( "update vetoed by onUpdate()" );
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Handles the calls needed to perform cascades as part of an update request
+ * for the given entity.
+ *
+ * @param event The event currently being processed.
+ * @param persister The defined persister for the entity being updated.
+ * @param entity The entity being updated.
+ */
+ private void cascadeOnUpdate(SaveOrUpdateEvent event, EntityPersister persister, Object entity) {
+ EventSource source = event.getSession();
+ source.getPersistenceContext().incrementCascadeLevel();
+ try {
+ new Cascade( CascadingAction.SAVE_UPDATE, Cascade.AFTER_UPDATE, source )
+ .cascade( persister, entity );
+ }
+ finally {
+ source.getPersistenceContext().decrementCascadeLevel();
+ }
+ }
+
+ protected CascadingAction getCascadeAction() {
+ return CascadingAction.SAVE_UPDATE;
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultSaveOrUpdateEventListener.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultUpdateEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultUpdateEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DefaultUpdateEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,54 @@
+//$Id: DefaultUpdateEventListener.java 5839 2005-02-22 03:09:35Z oneovthafew $
+package org.hibernate.event.def;
+
+import java.io.Serializable;
+
+import org.hibernate.HibernateException;
+import org.hibernate.ObjectDeletedException;
+import org.hibernate.EntityMode;
+import org.hibernate.engine.EntityEntry;
+import org.hibernate.engine.Status;
+import org.hibernate.event.SaveOrUpdateEvent;
+import org.hibernate.persister.entity.EntityPersister;
+
+/**
+ * An event handler for update() events
+ * @author Gavin King
+ */
+public class DefaultUpdateEventListener extends DefaultSaveOrUpdateEventListener {
+
+ protected Serializable performSaveOrUpdate(SaveOrUpdateEvent event) {
+ // this implementation is supposed to tolerate incorrect unsaved-value
+ // mappings, for the purpose of backward-compatibility
+ EntityEntry entry = event.getSession().getPersistenceContext().getEntry( event.getEntity() );
+ if ( entry!=null ) {
+ if ( entry.getStatus()==Status.DELETED ) {
+ throw new ObjectDeletedException( "deleted instance passed to update()", null, event.getEntityName() );
+ }
+ else {
+ return entityIsPersistent(event);
+ }
+ }
+ else {
+ entityIsDetached(event);
+ return null;
+ }
+ }
+
+ /**
+ * If the user specified an id, assign it to the instance and use that,
+ * otherwise use the id already assigned to the instance
+ */
+ protected Serializable getUpdateId(Object entity, EntityPersister persister, Serializable requestedId, EntityMode entityMode)
+ throws HibernateException {
+
+ if ( requestedId==null ) {
+ return super.getUpdateId(entity, persister, requestedId, entityMode);
+ }
+ else {
+ persister.setIdentifier(entity, requestedId, entityMode);
+ return requestedId;
+ }
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DirtyCollectionSearchVisitor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DirtyCollectionSearchVisitor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/DirtyCollectionSearchVisitor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,66 @@
+//$Id: DirtyCollectionSearchVisitor.java 7675 2005-07-29 06:25:23Z oneovthafew $
+package org.hibernate.event.def;
+
+import org.hibernate.HibernateException;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.event.EventSource;
+import org.hibernate.type.CollectionType;
+
+/**
+ * Do we have a dirty collection here?
+ * 1. if it is a new application-instantiated collection, return true (does not occur anymore!)
+ * 2. if it is a component, recurse
+ * 3. if it is a wrappered collection, ask the collection entry
+ *
+ * @author Gavin King
+ */
+public class DirtyCollectionSearchVisitor extends AbstractVisitor {
+
+ private boolean dirty = false;
+ private boolean[] propertyVersionability;
+
+ DirtyCollectionSearchVisitor(EventSource session, boolean[] propertyVersionability) {
+ super(session);
+ this.propertyVersionability = propertyVersionability;
+ }
+
+ boolean wasDirtyCollectionFound() {
+ return dirty;
+ }
+
+ Object processCollection(Object collection, CollectionType type)
+ throws HibernateException {
+
+ if (collection!=null) {
+
+ SessionImplementor session = getSession();
+
+ final PersistentCollection persistentCollection;
+ if ( type.isArrayType() ) {
+ persistentCollection = session.getPersistenceContext().getCollectionHolder(collection);
+ // if no array holder we found an unwrappered array (this can't occur,
+ // because we now always call wrap() before getting to here)
+ // return (ah==null) ? true : searchForDirtyCollections(ah, type);
+ }
+ else {
+ // if not wrappered yet, its dirty (this can't occur, because
+ // we now always call wrap() before getting to here)
+ // return ( ! (obj instanceof PersistentCollection) ) ?
+ //true : searchForDirtyCollections( (PersistentCollection) obj, type );
+ persistentCollection = (PersistentCollection) collection;
+ }
+
+ if ( persistentCollection.isDirty() ) { //we need to check even if it was not initialized, because of delayed adds!
+ dirty=true;
+ return null; //NOTE: EARLY EXIT!
+ }
+ }
+
+ return null;
+ }
+
+ boolean includeEntityProperty(Object[] values, int i) {
+ return propertyVersionability[i] && super.includeEntityProperty(values, i);
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/EvictVisitor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/EvictVisitor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/EvictVisitor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,68 @@
+//$Id: EvictVisitor.java 6929 2005-05-27 03:54:08Z oneovthafew $
+package org.hibernate.event.def;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.HibernateException;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.engine.CollectionEntry;
+import org.hibernate.engine.CollectionKey;
+import org.hibernate.event.EventSource;
+import org.hibernate.pretty.MessageHelper;
+import org.hibernate.type.CollectionType;
+
+/**
+ * Evict any collections referenced by the object from the session cache.
+ * This will NOT pick up any collections that were dereferenced, so they
+ * will be deleted (suboptimal but not exactly incorrect).
+ *
+ * @author Gavin King
+ */
+public class EvictVisitor extends AbstractVisitor {
+
+ private static final Log log = LogFactory.getLog(EvictVisitor.class);
+
+ EvictVisitor(EventSource session) {
+ super(session);
+ }
+
+ Object processCollection(Object collection, CollectionType type)
+ throws HibernateException {
+
+ if (collection!=null) evictCollection(collection, type);
+
+ return null;
+ }
+ public void evictCollection(Object value, CollectionType type) {
+
+ final Object pc;
+ if ( type.hasHolder( getSession().getEntityMode() ) ) {
+ pc = getSession().getPersistenceContext().removeCollectionHolder(value);
+ }
+ else if ( value instanceof PersistentCollection ) {
+ pc = value;
+ }
+ else {
+ return; //EARLY EXIT!
+ }
+
+ PersistentCollection collection = (PersistentCollection) pc;
+ if ( collection.unsetSession( getSession() ) ) evictCollection(collection);
+ }
+
+ private void evictCollection(PersistentCollection collection) {
+ CollectionEntry ce = (CollectionEntry) getSession().getPersistenceContext().getCollectionEntries().remove(collection);
+ if ( log.isDebugEnabled() )
+ log.debug(
+ "evicting collection: " +
+ MessageHelper.collectionInfoString( ce.getLoadedPersister(), ce.getLoadedKey(), getSession().getFactory() )
+ );
+ if ( ce.getLoadedPersister() != null && ce.getLoadedKey() != null ) {
+ //TODO: is this 100% correct?
+ getSession().getPersistenceContext().getCollectionsByKey().remove(
+ new CollectionKey( ce.getLoadedPersister(), ce.getLoadedKey(), getSession().getEntityMode() )
+ );
+ }
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/FlushVisitor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/FlushVisitor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/FlushVisitor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,49 @@
+//$Id: FlushVisitor.java 6929 2005-05-27 03:54:08Z oneovthafew $
+package org.hibernate.event.def;
+
+import org.hibernate.HibernateException;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.engine.Collections;
+import org.hibernate.event.EventSource;
+import org.hibernate.type.CollectionType;
+
+/**
+ * Process collections reachable from an entity. This
+ * visitor assumes that wrap was already performed for
+ * the entity.
+ *
+ * @author Gavin King
+ */
+public class FlushVisitor extends AbstractVisitor {
+
+ private Object owner;
+
+ Object processCollection(Object collection, CollectionType type)
+ throws HibernateException {
+
+ if (collection==CollectionType.UNFETCHED_COLLECTION) {
+ return null;
+ }
+
+ if (collection!=null) {
+ final PersistentCollection coll;
+ if ( type.hasHolder( getSession().getEntityMode() ) ) {
+ coll = getSession().getPersistenceContext().getCollectionHolder(collection);
+ }
+ else {
+ coll = (PersistentCollection) collection;
+ }
+
+ Collections.processReachableCollection( coll, type, owner, getSession() );
+ }
+
+ return null;
+
+ }
+
+ FlushVisitor(EventSource session, Object owner) {
+ super(session);
+ this.owner = owner;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/OnLockVisitor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/OnLockVisitor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/OnLockVisitor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,68 @@
+//$Id: OnLockVisitor.java 10948 2006-12-07 21:53:10Z steve.ebersole at jboss.com $
+package org.hibernate.event.def;
+
+import java.io.Serializable;
+
+import org.hibernate.HibernateException;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.event.EventSource;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.type.CollectionType;
+
+/**
+ * When a transient entity is passed to lock(), we must inspect all its collections and
+ * 1. associate any uninitialized PersistentCollections with this session
+ * 2. associate any initialized PersistentCollections with this session, using the
+ * existing snapshot
+ * 3. throw an exception for each "new" collection
+ *
+ * @author Gavin King
+ */
+public class OnLockVisitor extends ReattachVisitor {
+
+ public OnLockVisitor(EventSource session, Serializable key, Object owner) {
+ super( session, key, owner );
+ }
+
+ Object processCollection(Object collection, CollectionType type) throws HibernateException {
+
+ SessionImplementor session = getSession();
+ CollectionPersister persister = session.getFactory().getCollectionPersister( type.getRole() );
+
+ if ( collection == null ) {
+ //do nothing
+ }
+ else if ( collection instanceof PersistentCollection ) {
+ PersistentCollection persistentCollection = ( PersistentCollection ) collection;
+ if ( persistentCollection.setCurrentSession( session ) ) {
+ if ( isOwnerUnchanged( persistentCollection, persister, extractCollectionKeyFromOwner( persister ) ) ) {
+ // a "detached" collection that originally belonged to the same entity
+ if ( persistentCollection.isDirty() ) {
+ throw new HibernateException( "reassociated object has dirty collection" );
+ }
+ reattachCollection( persistentCollection, type );
+ }
+ else {
+ // a "detached" collection that belonged to a different entity
+ throw new HibernateException( "reassociated object has dirty collection reference" );
+ }
+ }
+ else {
+ // a collection loaded in the current session
+ // can not possibly be the collection belonging
+ // to the entity passed to update()
+ throw new HibernateException( "reassociated object has dirty collection reference" );
+ }
+ }
+ else {
+ // brand new collection
+ //TODO: or an array!! we can't lock objects with arrays now??
+ throw new HibernateException( "reassociated object has dirty collection reference (or an array)" );
+ }
+
+ return null;
+
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/OnReplicateVisitor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/OnReplicateVisitor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/OnReplicateVisitor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,66 @@
+//$Id: OnReplicateVisitor.java 10948 2006-12-07 21:53:10Z steve.ebersole at jboss.com $
+package org.hibernate.event.def;
+
+import java.io.Serializable;
+
+import org.hibernate.HibernateException;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.event.EventSource;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.type.CollectionType;
+
+/**
+ * When an entity is passed to replicate(), and there is an existing row, we must
+ * inspect all its collections and
+ * 1. associate any uninitialized PersistentCollections with this session
+ * 2. associate any initialized PersistentCollections with this session, using the
+ * existing snapshot
+ * 3. execute a collection removal (SQL DELETE) for each null collection property
+ * or "new" collection
+ *
+ * @author Gavin King
+ */
+public class OnReplicateVisitor extends ReattachVisitor {
+
+ private boolean isUpdate;
+
+ OnReplicateVisitor(EventSource session, Serializable key, Object owner, boolean isUpdate) {
+ super( session, key, owner );
+ this.isUpdate = isUpdate;
+ }
+
+ Object processCollection(Object collection, CollectionType type)
+ throws HibernateException {
+
+ if ( collection == CollectionType.UNFETCHED_COLLECTION ) {
+ return null;
+ }
+
+ EventSource session = getSession();
+ CollectionPersister persister = session.getFactory().getCollectionPersister( type.getRole() );
+
+ if ( isUpdate ) {
+ removeCollection( persister, extractCollectionKeyFromOwner( persister ), session );
+ }
+ if ( collection != null && ( collection instanceof PersistentCollection ) ) {
+ PersistentCollection wrapper = ( PersistentCollection ) collection;
+ wrapper.setCurrentSession( session );
+ if ( wrapper.wasInitialized() ) {
+ session.getPersistenceContext().addNewCollection( persister, wrapper );
+ }
+ else {
+ reattachCollection( wrapper, type );
+ }
+ }
+ else {
+ // otherwise a null or brand new collection
+ // this will also (inefficiently) handle arrays, which
+ // have no snapshot, so we can't do any better
+ //processArrayOrNewCollection(collection, type);
+ }
+
+ return null;
+
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/OnUpdateVisitor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/OnUpdateVisitor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/OnUpdateVisitor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,69 @@
+//$Id: OnUpdateVisitor.java 10948 2006-12-07 21:53:10Z steve.ebersole at jboss.com $
+package org.hibernate.event.def;
+
+import java.io.Serializable;
+
+import org.hibernate.HibernateException;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.event.EventSource;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.type.CollectionType;
+
+/**
+ * When an entity is passed to update(), we must inspect all its collections and
+ * 1. associate any uninitialized PersistentCollections with this session
+ * 2. associate any initialized PersistentCollections with this session, using the
+ * existing snapshot
+ * 3. execute a collection removal (SQL DELETE) for each null collection property
+ * or "new" collection
+ *
+ * @author Gavin King
+ */
+public class OnUpdateVisitor extends ReattachVisitor {
+
+ OnUpdateVisitor(EventSource session, Serializable key, Object owner) {
+ super( session, key, owner );
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ Object processCollection(Object collection, CollectionType type) throws HibernateException {
+
+ if ( collection == CollectionType.UNFETCHED_COLLECTION ) {
+ return null;
+ }
+
+ EventSource session = getSession();
+ CollectionPersister persister = session.getFactory().getCollectionPersister( type.getRole() );
+
+ final Serializable collectionKey = extractCollectionKeyFromOwner( persister );
+ if ( collection!=null && (collection instanceof PersistentCollection) ) {
+ PersistentCollection wrapper = (PersistentCollection) collection;
+ if ( wrapper.setCurrentSession(session) ) {
+ //a "detached" collection!
+ if ( !isOwnerUnchanged( wrapper, persister, collectionKey ) ) {
+ // if the collection belonged to a different entity,
+ // clean up the existing state of the collection
+ removeCollection( persister, collectionKey, session );
+ }
+ reattachCollection(wrapper, type);
+ }
+ else {
+ // a collection loaded in the current session
+ // can not possibly be the collection belonging
+ // to the entity passed to update()
+ removeCollection(persister, collectionKey, session);
+ }
+ }
+ else {
+ // null or brand new collection
+ // this will also (inefficiently) handle arrays, which have
+ // no snapshot, so we can't do any better
+ removeCollection(persister, collectionKey, session);
+ }
+
+ return null;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/ProxyVisitor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/ProxyVisitor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/ProxyVisitor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,79 @@
+//$Id: ProxyVisitor.java 7181 2005-06-17 19:36:08Z oneovthafew $
+package org.hibernate.event.def;
+
+import java.io.Serializable;
+
+import org.hibernate.HibernateException;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.event.EventSource;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.type.CollectionType;
+import org.hibernate.type.EntityType;
+
+/**
+ * Reassociates uninitialized proxies with the session
+ * @author Gavin King
+ */
+public abstract class ProxyVisitor extends AbstractVisitor {
+
+
+ public ProxyVisitor(EventSource session) {
+ super(session);
+ }
+
+ Object processEntity(Object value, EntityType entityType) throws HibernateException {
+
+ if (value!=null) {
+ getSession().getPersistenceContext().reassociateIfUninitializedProxy(value);
+ // if it is an initialized proxy, let cascade
+ // handle it later on
+ }
+
+ return null;
+ }
+
+ /**
+ * Has the owner of the collection changed since the collection
+ * was snapshotted and detached?
+ */
+ protected static boolean isOwnerUnchanged(
+ final PersistentCollection snapshot,
+ final CollectionPersister persister,
+ final Serializable id
+ ) {
+ return isCollectionSnapshotValid(snapshot) &&
+ persister.getRole().equals( snapshot.getRole() ) &&
+ id.equals( snapshot.getKey() );
+ }
+
+ private static boolean isCollectionSnapshotValid(PersistentCollection snapshot) {
+ return snapshot != null &&
+ snapshot.getRole() != null &&
+ snapshot.getKey() != null;
+ }
+
+ /**
+ * Reattach a detached (disassociated) initialized or uninitialized
+ * collection wrapper, using a snapshot carried with the collection
+ * wrapper
+ */
+ protected void reattachCollection(PersistentCollection collection, CollectionType type)
+ throws HibernateException {
+ if ( collection.wasInitialized() ) {
+ CollectionPersister collectionPersister = getSession().getFactory()
+ .getCollectionPersister( type.getRole() );
+ getSession().getPersistenceContext()
+ .addInitializedDetachedCollection( collectionPersister, collection );
+ }
+ else {
+ if ( !isCollectionSnapshotValid(collection) ) {
+ throw new HibernateException( "could not reassociate uninitialized transient collection" );
+ }
+ CollectionPersister collectionPersister = getSession().getFactory()
+ .getCollectionPersister( collection.getRole() );
+ getSession().getPersistenceContext()
+ .addUninitializedDetachedCollection( collectionPersister, collection );
+ }
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/ReattachVisitor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/ReattachVisitor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/ReattachVisitor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,103 @@
+//$Id: ReattachVisitor.java 10948 2006-12-07 21:53:10Z steve.ebersole at jboss.com $
+package org.hibernate.event.def;
+
+import java.io.Serializable;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.HibernateException;
+import org.hibernate.action.CollectionRemoveAction;
+import org.hibernate.event.EventSource;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.pretty.MessageHelper;
+import org.hibernate.type.AbstractComponentType;
+import org.hibernate.type.Type;
+
+/**
+ * Abstract superclass of visitors that reattach collections.
+ *
+ * @author Gavin King
+ */
+public abstract class ReattachVisitor extends ProxyVisitor {
+
+ private static final Log log = LogFactory.getLog( ReattachVisitor.class );
+
+ private final Serializable ownerIdentifier;
+ private final Object owner;
+
+ public ReattachVisitor(EventSource session, Serializable ownerIdentifier, Object owner) {
+ super( session );
+ this.ownerIdentifier = ownerIdentifier;
+ this.owner = owner;
+ }
+
+ /**
+ * Retrieve the identifier of the entity being visited.
+ *
+ * @return The entity's identifier.
+ */
+ final Serializable getOwnerIdentifier() {
+ return ownerIdentifier;
+ }
+
+ /**
+ * Retrieve the entity being visited.
+ *
+ * @return The entity.
+ */
+ final Object getOwner() {
+ return owner;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ Object processComponent(Object component, AbstractComponentType componentType) throws HibernateException {
+ Type[] types = componentType.getSubtypes();
+ if ( component == null ) {
+ processValues( new Object[types.length], types );
+ }
+ else {
+ super.processComponent( component, componentType );
+ }
+
+ return null;
+ }
+
+ /**
+ * Schedules a collection for deletion.
+ *
+ * @param role The persister representing the collection to be removed.
+ * @param collectionKey The collection key (differs from owner-id in the case of property-refs).
+ * @param source The session from which the request originated.
+ * @throws HibernateException
+ */
+ void removeCollection(CollectionPersister role, Serializable collectionKey, EventSource source) throws HibernateException {
+ if ( log.isTraceEnabled() ) {
+ log.trace(
+ "collection dereferenced while transient " +
+ MessageHelper.collectionInfoString( role, ownerIdentifier, source.getFactory() )
+ );
+ }
+ source.getActionQueue().addAction( new CollectionRemoveAction( null, role, collectionKey, false, source ) );
+ }
+
+ /**
+ * This version is slightly different for say
+ * {@link org.hibernate.type.CollectionType#getKeyOfOwner} in that here we
+ * need to assume that the owner is not yet associated with the session,
+ * and thus we cannot rely on the owner's EntityEntry snapshot...
+ *
+ * @param role The persister for the collection role being processed.
+ * @return
+ */
+ final Serializable extractCollectionKeyFromOwner(CollectionPersister role) {
+ if ( role.getCollectionType().useLHSPrimaryKey() ) {
+ return ownerIdentifier;
+ }
+ else {
+ return ( Serializable ) role.getOwnerEntityPersister().getPropertyValue( owner, role.getCollectionType().getLHSPropertyName(), getSession().getEntityMode() );
+ }
+
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/WrapVisitor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/WrapVisitor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/WrapVisitor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,137 @@
+//$Id: WrapVisitor.java 7181 2005-06-17 19:36:08Z oneovthafew $
+package org.hibernate.event.def;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.engine.PersistenceContext;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.event.EventSource;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.type.AbstractComponentType;
+import org.hibernate.type.CollectionType;
+import org.hibernate.type.Type;
+
+/**
+ * Wrap collections in a Hibernate collection
+ * wrapper.
+ * @author Gavin King
+ */
+public class WrapVisitor extends ProxyVisitor {
+
+ private static final Log log = LogFactory.getLog(WrapVisitor.class);
+
+ boolean substitute = false;
+
+ boolean isSubstitutionRequired() {
+ return substitute;
+ }
+
+ WrapVisitor(EventSource session) {
+ super(session);
+ }
+
+ Object processCollection(Object collection, CollectionType collectionType)
+ throws HibernateException {
+
+ if ( collection!=null && (collection instanceof PersistentCollection) ) {
+
+ final SessionImplementor session = getSession();
+ PersistentCollection coll = (PersistentCollection) collection;
+ if ( coll.setCurrentSession(session) ) {
+ reattachCollection( coll, collectionType );
+ }
+ return null;
+
+ }
+ else {
+ return processArrayOrNewCollection(collection, collectionType);
+ }
+
+ }
+
+ final Object processArrayOrNewCollection(Object collection, CollectionType collectionType)
+ throws HibernateException {
+
+ final SessionImplementor session = getSession();
+
+ if (collection==null) {
+ //do nothing
+ return null;
+ }
+ else {
+ CollectionPersister persister = session.getFactory().getCollectionPersister( collectionType.getRole() );
+
+ final PersistenceContext persistenceContext = session.getPersistenceContext();
+ //TODO: move into collection type, so we can use polymorphism!
+ if ( collectionType.hasHolder( session.getEntityMode() ) ) {
+
+ if (collection==CollectionType.UNFETCHED_COLLECTION) return null;
+
+ PersistentCollection ah = persistenceContext.getCollectionHolder(collection);
+ if (ah==null) {
+ ah = collectionType.wrap(session, collection);
+ persistenceContext.addNewCollection( persister, ah );
+ persistenceContext.addCollectionHolder(ah);
+ }
+ return null;
+ }
+ else {
+
+ PersistentCollection persistentCollection = collectionType.wrap(session, collection);
+ persistenceContext.addNewCollection( persister, persistentCollection );
+
+ if ( log.isTraceEnabled() ) log.trace( "Wrapped collection in role: " + collectionType.getRole() );
+
+ return persistentCollection; //Force a substitution!
+
+ }
+
+ }
+
+ }
+
+ void processValue(int i, Object[] values, Type[] types) {
+ Object result = processValue( values[i], types[i] );
+ if (result!=null) {
+ substitute = true;
+ values[i] = result;
+ }
+ }
+
+ Object processComponent(Object component, AbstractComponentType componentType)
+ throws HibernateException {
+
+ if (component!=null) {
+ Object[] values = componentType.getPropertyValues( component, getSession() );
+ Type[] types = componentType.getSubtypes();
+ boolean substituteComponent = false;
+ for ( int i=0; i<types.length; i++ ) {
+ Object result = processValue( values[i], types[i] );
+ if (result!=null) {
+ values[i] = result;
+ substituteComponent = true;
+ }
+ }
+ if (substituteComponent) {
+ componentType.setPropertyValues( component, values, getSession().getEntityMode() );
+ }
+ }
+
+ return null;
+ }
+
+ void process(Object object, EntityPersister persister) throws HibernateException {
+ EntityMode entityMode = getSession().getEntityMode();
+ Object[] values = persister.getPropertyValues( object, entityMode );
+ Type[] types = persister.getPropertyTypes();
+ processEntityPropertyValues(values, types);
+ if ( isSubstitutionRequired() ) {
+ persister.setPropertyValues( object, values, entityMode );
+ }
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,9 @@
+<html>
+<head></head>
+<body>
+<p>
+ This package defines a default set of event listeners that
+ implements the default behaviors of Hibernate.
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/event/def/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/event/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/event/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/event/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,8 @@
+<html>
+<head></head>
+<body>
+<p>
+ This package defines an event framework for Hibernate.
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/event/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/exception/CacheSQLStateConverter.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/exception/CacheSQLStateConverter.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/exception/CacheSQLStateConverter.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,93 @@
+// $Id: $
+package org.hibernate.exception;
+
+import org.hibernate.JDBCException;
+
+import java.sql.SQLException;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A SQLExceptionConverter implementation specific to Caché SQL,
+ * accounting for its custom integrity constraint violation error codes.
+ *
+ * @author Jonathan Levinson
+ */
+public class CacheSQLStateConverter implements SQLExceptionConverter {
+
+ private ViolatedConstraintNameExtracter extracter;
+
+ private static final Set SQL_GRAMMAR_CATEGORIES = new HashSet();
+ private static final Set DATA_CATEGORIES = new HashSet();
+ private static final Set INTEGRITY_VIOLATION_CATEGORIES = new HashSet();
+ private static final Set CONNECTION_CATEGORIES = new HashSet();
+
+ static {
+ SQL_GRAMMAR_CATEGORIES.add( "07" );
+ SQL_GRAMMAR_CATEGORIES.add( "37" );
+ SQL_GRAMMAR_CATEGORIES.add( "42" );
+ SQL_GRAMMAR_CATEGORIES.add( "65" );
+ SQL_GRAMMAR_CATEGORIES.add( "S0" );
+ SQL_GRAMMAR_CATEGORIES.add( "20" );
+
+ DATA_CATEGORIES.add( "22" );
+ DATA_CATEGORIES.add( "21" );
+ DATA_CATEGORIES.add( "02" );
+
+ INTEGRITY_VIOLATION_CATEGORIES.add( new Integer( 119 ) );
+ INTEGRITY_VIOLATION_CATEGORIES.add( new Integer( 120 ) );
+ INTEGRITY_VIOLATION_CATEGORIES.add( new Integer( 121 ) );
+ INTEGRITY_VIOLATION_CATEGORIES.add( new Integer( 122 ) );
+ INTEGRITY_VIOLATION_CATEGORIES.add( new Integer( 123 ) );
+ INTEGRITY_VIOLATION_CATEGORIES.add( new Integer( 124 ) );
+ INTEGRITY_VIOLATION_CATEGORIES.add( new Integer( 125 ) );
+ INTEGRITY_VIOLATION_CATEGORIES.add( new Integer( 127 ) );
+
+ CONNECTION_CATEGORIES.add( "08" );
+ }
+
+ public CacheSQLStateConverter(ViolatedConstraintNameExtracter extracter) {
+ this.extracter = extracter;
+ }
+
+ /**
+ * Convert the given SQLException into Hibernate's JDBCException hierarchy.
+ *
+ * @param sqlException The SQLException to be converted.
+ * @param message An optional error message.
+ * @param sql Optionally, the sql being performed when the exception occurred.
+ * @return The resulting JDBCException.
+ */
+ public JDBCException convert(SQLException sqlException, String message, String sql) {
+ String sqlStateClassCode = JDBCExceptionHelper.extractSqlStateClassCode( sqlException );
+ Integer errorCode = new Integer( JDBCExceptionHelper.extractErrorCode( sqlException ) );
+ if ( sqlStateClassCode != null ) {
+ if ( SQL_GRAMMAR_CATEGORIES.contains( sqlStateClassCode ) ) {
+ return new SQLGrammarException( message, sqlException, sql );
+ }
+ else if ( INTEGRITY_VIOLATION_CATEGORIES.contains( errorCode ) ) {
+ String constraintName = extracter.extractConstraintName( sqlException );
+ return new ConstraintViolationException( message, sqlException, sql, constraintName );
+ }
+ else if ( CONNECTION_CATEGORIES.contains( sqlStateClassCode ) ) {
+ return new JDBCConnectionException( message, sqlException, sql );
+ }
+ else if ( DATA_CATEGORIES.contains( sqlStateClassCode ) ) {
+ return new DataException( message, sqlException, sql );
+ }
+ }
+ return handledNonSpecificException( sqlException, message, sql );
+ }
+
+ /**
+ * Handle an exception not converted to a specific type based on the SQLState.
+ *
+ * @param sqlException The exception to be handled.
+ * @param message An optional message
+ * @param sql Optionally, the sql being performed when the exception occurred.
+ * @return The converted exception; should <b>never</b> be null.
+ */
+ protected JDBCException handledNonSpecificException(SQLException sqlException, String message, String sql) {
+ return new GenericJDBCException( message, sqlException, sql );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/exception/Configurable.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/exception/Configurable.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/exception/Configurable.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,24 @@
+// $Id: Configurable.java 4746 2004-11-11 20:57:28Z steveebersole $
+package org.hibernate.exception;
+
+import org.hibernate.HibernateException;
+
+import java.util.Properties;
+
+/**
+ * The Configurable interface defines the contract for SQLExceptionConverter impls that
+ * want to be configured prior to usage given the currently defined Hibernate properties.
+ *
+ * @author Steve Ebersole
+ */
+public interface Configurable {
+ // todo: this might really even be moved into the cfg package and used as the basis for all things which are configurable.
+
+ /**
+ * Configure the component, using the given settings and properties.
+ *
+ * @param properties All defined startup properties.
+ * @throws HibernateException Indicates a configuration exception.
+ */
+ public void configure(Properties properties) throws HibernateException;
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/exception/ConstraintViolationException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/exception/ConstraintViolationException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/exception/ConstraintViolationException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,36 @@
+// $Id: ConstraintViolationException.java 4782 2004-11-21 00:11:27Z pgmjsd $
+package org.hibernate.exception;
+
+import org.hibernate.JDBCException;
+
+import java.sql.SQLException;
+
+/**
+ * Implementation of JDBCException indicating that the requested DML operation
+ * resulted in a violation of a defined integrity constraint.
+ *
+ * @author Steve Ebersole
+ */
+public class ConstraintViolationException extends JDBCException {
+
+ private String constraintName;
+
+ public ConstraintViolationException(String message, SQLException root, String constraintName) {
+ super( message, root );
+ this.constraintName = constraintName;
+ }
+
+ public ConstraintViolationException(String message, SQLException root, String sql, String constraintName) {
+ super( message, root, sql );
+ this.constraintName = constraintName;
+ }
+
+ /**
+ * Returns the name of the violated constraint, if known.
+ *
+ * @return The name of the violated constraint, or null if not known.
+ */
+ public String getConstraintName() {
+ return constraintName;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/exception/DataException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/exception/DataException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/exception/DataException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,34 @@
+// $Id: DataException.java 8062 2005-09-01 15:41:46Z oneovthafew $
+package org.hibernate.exception;
+
+import org.hibernate.JDBCException;
+
+import java.sql.SQLException;
+
+/**
+ * Implementation of JDBCException indicating that evaluation of the
+ * valid SQL statement against the given data resulted in some
+ * illegal operation, mismatched types or incorrect cardinality.
+ *
+ * @author Gavin King
+ */
+public class DataException extends JDBCException {
+ /**
+ * Constructor for JDBCException.
+ *
+ * @param root The underlying exception.
+ */
+ public DataException(String message, SQLException root) {
+ super( message, root );
+ }
+
+ /**
+ * Constructor for JDBCException.
+ *
+ * @param message Optional message.
+ * @param root The underlying exception.
+ */
+ public DataException(String message, SQLException root, String sql) {
+ super( message, root, sql );
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/exception/DataException.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/exception/ExceptionUtils.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/exception/ExceptionUtils.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/exception/ExceptionUtils.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,734 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2002-2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ * any, must include the following acknowledgement:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgement may appear in the software itself,
+ * if and wherever such third-party acknowledgements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ * Foundation" must not be used to endorse or promote products derived
+ * from this software without prior written permission. For written
+ * permission, please contact apache at apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+package org.hibernate.exception;
+
+import org.hibernate.util.ArrayHelper;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+/**
+ * <p>Provides utilities for manipulating and examining
+ * <code>Throwable</code> objects.</p>
+ *
+ * @author <a href="mailto:dlr at finemaltcoding.com">Daniel Rall</a>
+ * @author Dmitri Plotnikov
+ * @author Stephen Colebourne
+ * @author <a href="mailto:ggregory at seagullsw.com">Gary Gregory</a>
+ * @author Pete Gieser
+ * @version $Id: ExceptionUtils.java 4782 2004-11-21 00:11:27Z pgmjsd $
+ * @since 1.0
+ */
+public final class ExceptionUtils {
+
+ private static final String LINE_SEPARATOR = System.getProperty( "line.separator" );
+
+ /**
+ * <p>Used when printing stack frames to denote the start of a
+ * wrapped exception.</p>
+ * <p/>
+ * <p>Package private for accessibility by test suite.</p>
+ */
+ static final String WRAPPED_MARKER = " [wrapped] ";
+
+ /**
+ * <p>The names of methods commonly used to access a wrapped exception.</p>
+ */
+ private static final String[] CAUSE_METHOD_NAMES = {
+ "getCause",
+ "getNextException",
+ "getTargetException",
+ "getException",
+ "getSourceException",
+ "getRootCause",
+ "getCausedByException",
+ "getNested"
+ };
+
+ /**
+ * <p>The Method object for JDK1.4 getCause.</p>
+ */
+ private static final Method THROWABLE_CAUSE_METHOD;
+
+ static {
+ Method getCauseMethod;
+ try {
+ getCauseMethod = Throwable.class.getMethod( "getCause", null );
+ }
+ catch ( Exception e ) {
+ getCauseMethod = null;
+ }
+ THROWABLE_CAUSE_METHOD = getCauseMethod;
+ }
+
+ private ExceptionUtils() {
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * <p>Adds to the list of method names used in the search for <code>Throwable</code>
+ * objects.</p>
+ *
+ * @param methodName the methodName to add to the list, <code>null</code>
+ * and empty strings are ignored
+ * @since 2.0
+ */
+ /*public static void addCauseMethodName(String methodName) {
+ if ( StringHelper.isNotEmpty(methodName) ) {
+ List list = new ArrayList( Arrays.asList(CAUSE_METHOD_NAMES );
+ list.add(methodName);
+ CAUSE_METHOD_NAMES = (String[]) list.toArray(new String[list.size()]);
+ }
+ }*/
+
+ /**
+ * <p>Introspects the <code>Throwable</code> to obtain the cause.</p>
+ * <p/>
+ * <p>The method searches for methods with specific names that return a
+ * <code>Throwable</code> object. This will pick up most wrapping exceptions,
+ * including those from JDK 1.4, and
+ * {@link org.apache.commons.lang.exception.NestableException NestableException}.
+ * The method names can be added to using {@link #addCauseMethodName(String)}.</p>
+ * <p/>
+ * <p>The default list searched for are:</p>
+ * <ul>
+ * <li><code>getCause()</code></li>
+ * <li><code>getNextException()</code></li>
+ * <li><code>getTargetException()</code></li>
+ * <li><code>getException()</code></li>
+ * <li><code>getSourceException()</code></li>
+ * <li><code>getRootCause()</code></li>
+ * <li><code>getCausedByException()</code></li>
+ * <li><code>getNested()</code></li>
+ * </ul>
+ * <p/>
+ * <p>In the absence of any such method, the object is inspected for a
+ * <code>detail</code> field assignable to a <code>Throwable</code>.</p>
+ * <p/>
+ * <p>If none of the above is found, returns <code>null</code>.</p>
+ *
+ * @param throwable the throwable to introspect for a cause, may be null
+ * @return the cause of the <code>Throwable</code>,
+ * <code>null</code> if none found or null throwable input
+ */
+ public static Throwable getCause(Throwable throwable) {
+ return getCause( throwable, CAUSE_METHOD_NAMES );
+ }
+
+ /**
+ * <p>Introspects the <code>Throwable</code> to obtain the cause.</p>
+ * <p/>
+ * <ol>
+ * <li>Try known exception types.</li>
+ * <li>Try the supplied array of method names.</li>
+ * <li>Try the field 'detail'.</li>
+ * </ol>
+ * <p/>
+ * <p>A <code>null</code> set of method names means use the default set.
+ * A <code>null</code> in the set of method names will be ignored.</p>
+ *
+ * @param throwable the throwable to introspect for a cause, may be null
+ * @param methodNames the method names, null treated as default set
+ * @return the cause of the <code>Throwable</code>,
+ * <code>null</code> if none found or null throwable input
+ */
+ public static Throwable getCause(Throwable throwable, String[] methodNames) {
+ if ( throwable == null ) {
+ return null;
+ }
+ Throwable cause = getCauseUsingWellKnownTypes( throwable );
+ if ( cause == null ) {
+ if ( methodNames == null ) {
+ methodNames = CAUSE_METHOD_NAMES;
+ }
+ for ( int i = 0; i < methodNames.length; i++ ) {
+ String methodName = methodNames[i];
+ if ( methodName != null ) {
+ cause = getCauseUsingMethodName( throwable, methodName );
+ if ( cause != null ) {
+ break;
+ }
+ }
+ }
+
+ if ( cause == null ) {
+ cause = getCauseUsingFieldName( throwable, "detail" );
+ }
+ }
+ return cause;
+ }
+
+ /**
+ * <p>Introspects the <code>Throwable</code> to obtain the root cause.</p>
+ * <p/>
+ * <p>This method walks through the exception chain to the last element,
+ * "root" of the tree, using {@link #getCause(Throwable)}, and
+ * returns that exception.</p>
+ *
+ * @param throwable the throwable to get the root cause for, may be null
+ * @return the root cause of the <code>Throwable</code>,
+ * <code>null</code> if none found or null throwable input
+ */
+ public static Throwable getRootCause(Throwable throwable) {
+ Throwable cause = getCause( throwable );
+ if ( cause != null ) {
+ throwable = cause;
+ while ( ( throwable = getCause( throwable ) ) != null ) {
+ cause = throwable;
+ }
+ }
+ return cause;
+ }
+
+ /**
+ * <p>Finds a <code>Throwable</code> for known types.</p>
+ * <p/>
+ * <p>Uses <code>instanceof</code> checks to examine the exception,
+ * looking for well known types which could contain chained or
+ * wrapped exceptions.</p>
+ *
+ * @param throwable the exception to examine
+ * @return the wrapped exception, or <code>null</code> if not found
+ */
+ private static Throwable getCauseUsingWellKnownTypes(Throwable throwable) {
+ if ( throwable instanceof Nestable ) {
+ return ( ( Nestable ) throwable ).getCause();
+ }
+ else if ( throwable instanceof SQLException ) {
+ return ( ( SQLException ) throwable ).getNextException();
+ }
+ else if ( throwable instanceof InvocationTargetException ) {
+ return ( ( InvocationTargetException ) throwable ).getTargetException();
+ }
+ else {
+ return null;
+ }
+ }
+
+ /**
+ * <p>Finds a <code>Throwable</code> by method name.</p>
+ *
+ * @param throwable the exception to examine
+ * @param methodName the name of the method to find and invoke
+ * @return the wrapped exception, or <code>null</code> if not found
+ */
+ private static Throwable getCauseUsingMethodName(Throwable throwable, String methodName) {
+ Method method = null;
+ try {
+ method = throwable.getClass().getMethod( methodName, null );
+ }
+ catch ( NoSuchMethodException ignored ) {
+ }
+ catch ( SecurityException ignored ) {
+ }
+
+ if ( method != null && Throwable.class.isAssignableFrom( method.getReturnType() ) ) {
+ try {
+ return ( Throwable ) method.invoke( throwable, ArrayHelper.EMPTY_OBJECT_ARRAY );
+ }
+ catch ( IllegalAccessException ignored ) {
+ }
+ catch ( IllegalArgumentException ignored ) {
+ }
+ catch ( InvocationTargetException ignored ) {
+ }
+ }
+ return null;
+ }
+
+ /**
+ * <p>Finds a <code>Throwable</code> by field name.</p>
+ *
+ * @param throwable the exception to examine
+ * @param fieldName the name of the attribute to examine
+ * @return the wrapped exception, or <code>null</code> if not found
+ */
+ private static Throwable getCauseUsingFieldName(Throwable throwable, String fieldName) {
+ Field field = null;
+ try {
+ field = throwable.getClass().getField( fieldName );
+ }
+ catch ( NoSuchFieldException ignored ) {
+ }
+ catch ( SecurityException ignored ) {
+ }
+
+ if ( field != null && Throwable.class.isAssignableFrom( field.getType() ) ) {
+ try {
+ return ( Throwable ) field.get( throwable );
+ }
+ catch ( IllegalAccessException ignored ) {
+ }
+ catch ( IllegalArgumentException ignored ) {
+ }
+ }
+ return null;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * <p>Checks if the Throwable class has a <code>getCause</code> method.</p>
+ * <p/>
+ * <p>This is true for JDK 1.4 and above.</p>
+ *
+ * @return true if Throwable is nestable
+ * @since 2.0
+ */
+ public static boolean isThrowableNested() {
+ return ( THROWABLE_CAUSE_METHOD != null );
+ }
+
+ /**
+ * <p>Checks whether this <code>Throwable</code> class can store a cause.</p>
+ * <p/>
+ * <p>This method does <b>not</b> check whether it actually does store a cause.<p>
+ *
+ * @param throwable the <code>Throwable</code> to examine, may be null
+ * @return boolean <code>true</code> if nested otherwise <code>false</code>
+ * @since 2.0
+ */
+ public static boolean isNestedThrowable(Throwable throwable) {
+ if ( throwable == null ) {
+ return false;
+ }
+
+ if ( throwable instanceof Nestable ) {
+ return true;
+ }
+ else if ( throwable instanceof SQLException ) {
+ return true;
+ }
+ else if ( throwable instanceof InvocationTargetException ) {
+ return true;
+ }
+ else if ( isThrowableNested() ) {
+ return true;
+ }
+
+ Class cls = throwable.getClass();
+ for ( int i = 0, isize = CAUSE_METHOD_NAMES.length; i < isize; i++ ) {
+ try {
+ Method method = cls.getMethod( CAUSE_METHOD_NAMES[i], null );
+ if ( method != null && Throwable.class.isAssignableFrom( method.getReturnType() ) ) {
+ return true;
+ }
+ }
+ catch ( NoSuchMethodException ignored ) {
+ }
+ catch ( SecurityException ignored ) {
+ }
+ }
+
+ try {
+ Field field = cls.getField( "detail" );
+ if ( field != null ) {
+ return true;
+ }
+ }
+ catch ( NoSuchFieldException ignored ) {
+ }
+ catch ( SecurityException ignored ) {
+ }
+
+ return false;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * <p>Counts the number of <code>Throwable</code> objects in the
+ * exception chain.</p>
+ * <p/>
+ * <p>A throwable without cause will return <code>1</code>.
+ * A throwable with one cause will return <code>2</code> and so on.
+ * A <code>null</code> throwable will return <code>0</code>.</p>
+ *
+ * @param throwable the throwable to inspect, may be null
+ * @return the count of throwables, zero if null input
+ */
+ public static int getThrowableCount(Throwable throwable) {
+ int count = 0;
+ while ( throwable != null ) {
+ count++;
+ throwable = ExceptionUtils.getCause( throwable );
+ }
+ return count;
+ }
+
+ /**
+ * <p>Returns the list of <code>Throwable</code> objects in the
+ * exception chain.</p>
+ * <p/>
+ * <p>A throwable without cause will return an array containing
+ * one element - the input throwable.
+ * A throwable with one cause will return an array containing
+ * two elements. - the input throwable and the cause throwable.
+ * A <code>null</code> throwable will return an array size zero.</p>
+ *
+ * @param throwable the throwable to inspect, may be null
+ * @return the array of throwables, never null
+ */
+ public static Throwable[] getThrowables(Throwable throwable) {
+ List list = new ArrayList();
+ while ( throwable != null ) {
+ list.add( throwable );
+ throwable = ExceptionUtils.getCause( throwable );
+ }
+ return ( Throwable[] ) list.toArray( new Throwable[list.size()] );
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * <p>Returns the (zero based) index of the first <code>Throwable</code>
+ * that matches the specified type in the exception chain.</p>
+ * <p/>
+ * <p>A <code>null</code> throwable returns <code>-1</code>.
+ * A <code>null</code> type returns <code>-1</code>.
+ * No match in the chain returns <code>-1</code>.</p>
+ *
+ * @param throwable the throwable to inspect, may be null
+ * @param type the type to search for
+ * @return the index into the throwable chain, -1 if no match or null input
+ */
+ public static int indexOfThrowable(Throwable throwable, Class type) {
+ return indexOfThrowable( throwable, type, 0 );
+ }
+
+ /**
+ * <p>Returns the (zero based) index of the first <code>Throwable</code>
+ * that matches the specified type in the exception chain from
+ * a specified index.</p>
+ * <p/>
+ * <p>A <code>null</code> throwable returns <code>-1</code>.
+ * A <code>null</code> type returns <code>-1</code>.
+ * No match in the chain returns <code>-1</code>.
+ * A negative start index is treated as zero.
+ * A start index greater than the number of throwables returns <code>-1</code>.</p>
+ *
+ * @param throwable the throwable to inspect, may be null
+ * @param type the type to search for
+ * @param fromIndex the (zero based) index of the starting position,
+ * negative treated as zero, larger than chain size returns -1
+ * @return the index into the throwable chain, -1 if no match or null input
+ */
+ public static int indexOfThrowable(Throwable throwable, Class type, int fromIndex) {
+ if ( throwable == null ) {
+ return -1;
+ }
+ if ( fromIndex < 0 ) {
+ fromIndex = 0;
+ }
+ Throwable[] throwables = ExceptionUtils.getThrowables( throwable );
+ if ( fromIndex >= throwables.length ) {
+ return -1;
+ }
+ for ( int i = fromIndex; i < throwables.length; i++ ) {
+ if ( throwables[i].getClass().equals( type ) ) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * <p>Prints a compact stack trace for the root cause of a throwable
+ * to <code>System.err</code>.</p>
+ * <p/>
+ * <p>The compact stack trace starts with the root cause and prints
+ * stack frames up to the place where it was caught and wrapped.
+ * Then it prints the wrapped exception and continues with stack frames
+ * until the wrapper exception is caught and wrapped again, etc.</p>
+ * <p/>
+ * <p>The method is equivalent to <code>printStackTrace</code> for throwables
+ * that don't have nested causes.</p>
+ *
+ * @param throwable the throwable to output
+ * @since 2.0
+ */
+ public static void printRootCauseStackTrace(Throwable throwable) {
+ printRootCauseStackTrace( throwable, System.err );
+ }
+
+ /**
+ * <p>Prints a compact stack trace for the root cause of a throwable.</p>
+ * <p/>
+ * <p>The compact stack trace starts with the root cause and prints
+ * stack frames up to the place where it was caught and wrapped.
+ * Then it prints the wrapped exception and continues with stack frames
+ * until the wrapper exception is caught and wrapped again, etc.</p>
+ * <p/>
+ * <p>The method is equivalent to <code>printStackTrace</code> for throwables
+ * that don't have nested causes.</p>
+ *
+ * @param throwable the throwable to output, may be null
+ * @param stream the stream to output to, may not be null
+ * @throws IllegalArgumentException if the stream is <code>null</code>
+ * @since 2.0
+ */
+ public static void printRootCauseStackTrace(Throwable throwable, PrintStream stream) {
+ if ( throwable == null ) {
+ return;
+ }
+ if ( stream == null ) {
+ throw new IllegalArgumentException( "The PrintStream must not be null" );
+ }
+ String trace[] = getRootCauseStackTrace( throwable );
+ for ( int i = 0; i < trace.length; i++ ) {
+ stream.println( trace[i] );
+ }
+ stream.flush();
+ }
+
+ /**
+ * <p>Prints a compact stack trace for the root cause of a throwable.</p>
+ * <p/>
+ * <p>The compact stack trace starts with the root cause and prints
+ * stack frames up to the place where it was caught and wrapped.
+ * Then it prints the wrapped exception and continues with stack frames
+ * until the wrapper exception is caught and wrapped again, etc.</p>
+ * <p/>
+ * <p>The method is equivalent to <code>printStackTrace</code> for throwables
+ * that don't have nested causes.</p>
+ *
+ * @param throwable the throwable to output, may be null
+ * @param writer the writer to output to, may not be null
+ * @throws IllegalArgumentException if the writer is <code>null</code>
+ * @since 2.0
+ */
+ public static void printRootCauseStackTrace(Throwable throwable, PrintWriter writer) {
+ if ( throwable == null ) {
+ return;
+ }
+ if ( writer == null ) {
+ throw new IllegalArgumentException( "The PrintWriter must not be null" );
+ }
+ String trace[] = getRootCauseStackTrace( throwable );
+ for ( int i = 0; i < trace.length; i++ ) {
+ writer.println( trace[i] );
+ }
+ writer.flush();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * <p>Creates a compact stack trace for the root cause of the supplied
+ * <code>Throwable</code>.</p>
+ *
+ * @param throwable the throwable to examine, may be null
+ * @return an array of stack trace frames, never null
+ * @since 2.0
+ */
+ public static String[] getRootCauseStackTrace(Throwable throwable) {
+ if ( throwable == null ) {
+ return ArrayHelper.EMPTY_STRING_ARRAY;
+ }
+ Throwable throwables[] = getThrowables( throwable );
+ int count = throwables.length;
+ ArrayList frames = new ArrayList();
+ List nextTrace = getStackFrameList( throwables[count - 1] );
+ for ( int i = count; --i >= 0; ) {
+ List trace = nextTrace;
+ if ( i != 0 ) {
+ nextTrace = getStackFrameList( throwables[i - 1] );
+ removeCommonFrames( trace, nextTrace );
+ }
+ if ( i == count - 1 ) {
+ frames.add( throwables[i].toString() );
+ }
+ else {
+ frames.add( WRAPPED_MARKER + throwables[i].toString() );
+ }
+ for ( int j = 0; j < trace.size(); j++ ) {
+ frames.add( trace.get( j ) );
+ }
+ }
+ return ( String[] ) frames.toArray( new String[0] );
+ }
+
+ /**
+ * <p>Removes common frames from the cause trace given the two stack traces.</p>
+ *
+ * @param causeFrames stack trace of a cause throwable
+ * @param wrapperFrames stack trace of a wrapper throwable
+ * @throws IllegalArgumentException if either argument is null
+ * @since 2.0
+ */
+ public static void removeCommonFrames(List causeFrames, List wrapperFrames) {
+ if ( causeFrames == null || wrapperFrames == null ) {
+ throw new IllegalArgumentException( "The List must not be null" );
+ }
+ int causeFrameIndex = causeFrames.size() - 1;
+ int wrapperFrameIndex = wrapperFrames.size() - 1;
+ while ( causeFrameIndex >= 0 && wrapperFrameIndex >= 0 ) {
+ // Remove the frame from the cause trace if it is the same
+ // as in the wrapper trace
+ String causeFrame = ( String ) causeFrames.get( causeFrameIndex );
+ String wrapperFrame = ( String ) wrapperFrames.get( wrapperFrameIndex );
+ if ( causeFrame.equals( wrapperFrame ) ) {
+ causeFrames.remove( causeFrameIndex );
+ }
+ causeFrameIndex--;
+ wrapperFrameIndex--;
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * <p>Gets the stack trace from a Throwable as a String.</p>
+ *
+ * @param throwable the <code>Throwable</code> to be examined
+ * @return the stack trace as generated by the exception's
+ * <code>printStackTrace(PrintWriter)</code> method
+ */
+ public static String getStackTrace(Throwable throwable) {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter( sw, true );
+ throwable.printStackTrace( pw );
+ return sw.getBuffer().toString();
+ }
+
+ /**
+ * <p>A way to get the entire nested stack-trace of an throwable.</p>
+ *
+ * @param throwable the <code>Throwable</code> to be examined
+ * @return the nested stack trace, with the root cause first
+ * @since 2.0
+ */
+ public static String getFullStackTrace(Throwable throwable) {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter( sw, true );
+ Throwable[] ts = getThrowables( throwable );
+ for ( int i = 0; i < ts.length; i++ ) {
+ ts[i].printStackTrace( pw );
+ if ( isNestedThrowable( ts[i] ) ) {
+ break;
+ }
+ }
+ return sw.getBuffer().toString();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * <p>Captures the stack trace associated with the specified
+ * <code>Throwable</code> object, decomposing it into a list of
+ * stack frames.</p>
+ *
+ * @param throwable the <code>Throwable</code> to exaamine, may be null
+ * @return an array of strings describing each stack frame, never null
+ */
+ public static String[] getStackFrames(Throwable throwable) {
+ if ( throwable == null ) {
+ return ArrayHelper.EMPTY_STRING_ARRAY;
+ }
+ return getStackFrames( getStackTrace( throwable ) );
+ }
+
+ /**
+ * <p>Functionality shared between the
+ * <code>getStackFrames(Throwable)</code> methods of this and the
+ * {@link org.apache.commons.lang.exception.NestableDelegate}
+ * classes.</p>
+ */
+ static String[] getStackFrames(String stackTrace) {
+ String linebreak = LINE_SEPARATOR;
+ StringTokenizer frames = new StringTokenizer( stackTrace, linebreak );
+ List list = new LinkedList();
+ while ( frames.hasMoreTokens() ) {
+ list.add( frames.nextToken() );
+ }
+ return ( String[] ) list.toArray( new String[list.size()] );
+ }
+
+ /**
+ * <p>Produces a <code>List</code> of stack frames - the message
+ * is not included.</p>
+ * <p/>
+ * <p>This works in most cases - it will only fail if the exception
+ * message contains a line that starts with:
+ * <code>" at".</code></p>
+ *
+ * @param t is any throwable
+ * @return List of stack frames
+ */
+ static List getStackFrameList(Throwable t) {
+ String stackTrace = getStackTrace( t );
+ String linebreak = LINE_SEPARATOR;
+ StringTokenizer frames = new StringTokenizer( stackTrace, linebreak );
+ List list = new LinkedList();
+ boolean traceStarted = false;
+ while ( frames.hasMoreTokens() ) {
+ String token = frames.nextToken();
+ // Determine if the line starts with <whitespace>at
+ int at = token.indexOf( "at" );
+ if ( at != -1 && token.substring( 0, at ).trim().length() == 0 ) {
+ traceStarted = true;
+ list.add( token );
+ }
+ else if ( traceStarted ) {
+ break;
+ }
+ }
+ return list;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/exception/GenericJDBCException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/exception/GenericJDBCException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/exception/GenericJDBCException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,21 @@
+// $Id: GenericJDBCException.java 4782 2004-11-21 00:11:27Z pgmjsd $
+package org.hibernate.exception;
+
+import org.hibernate.JDBCException;
+
+import java.sql.SQLException;
+
+/**
+ * Generic, non-specific JDBCException.
+ *
+ * @author Steve Ebersole
+ */
+public class GenericJDBCException extends JDBCException {
+ public GenericJDBCException(String string, SQLException root) {
+ super( string, root );
+ }
+
+ public GenericJDBCException(String string, SQLException root, String sql) {
+ super( string, root, sql );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/exception/JDBCConnectionException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/exception/JDBCConnectionException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/exception/JDBCConnectionException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+// $Id: JDBCConnectionException.java 4782 2004-11-21 00:11:27Z pgmjsd $
+package org.hibernate.exception;
+
+import org.hibernate.JDBCException;
+
+import java.sql.SQLException;
+
+/**
+ * Implementation of JDBCException indicating problems with communicating with the
+ * database (can also include incorrect JDBC setup).
+ *
+ * @author Steve Ebersole
+ */
+public class JDBCConnectionException extends JDBCException {
+ public JDBCConnectionException(String string, SQLException root) {
+ super( string, root );
+ }
+
+ public JDBCConnectionException(String string, SQLException root, String sql) {
+ super( string, root, sql );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/exception/JDBCExceptionHelper.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/exception/JDBCExceptionHelper.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/exception/JDBCExceptionHelper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,94 @@
+// $Id: JDBCExceptionHelper.java 9557 2006-03-06 15:16:27Z steve.ebersole at jboss.com $
+package org.hibernate.exception;
+
+import org.hibernate.JDBCException;
+import org.hibernate.util.JDBCExceptionReporter;
+
+import java.sql.SQLException;
+
+/**
+ * Implementation of JDBCExceptionHelper.
+ *
+ * @author Steve Ebersole
+ */
+public final class JDBCExceptionHelper {
+
+ private JDBCExceptionHelper() {
+ }
+
+ /**
+ * Converts the given SQLException into Hibernate's JDBCException hierarchy, as well as performing
+ * appropriate logging.
+ *
+ * @param converter The converter to use.
+ * @param sqlException The exception to convert.
+ * @param message An optional error message.
+ * @return The converted JDBCException.
+ */
+ public static JDBCException convert(SQLExceptionConverter converter, SQLException sqlException, String message) {
+ return convert( converter, sqlException, message, "???" );
+ }
+
+ /**
+ * Converts the given SQLException into Hibernate's JDBCException hierarchy, as well as performing
+ * appropriate logging.
+ *
+ * @param converter The converter to use.
+ * @param sqlException The exception to convert.
+ * @param message An optional error message.
+ * @return The converted JDBCException.
+ */
+ public static JDBCException convert(SQLExceptionConverter converter, SQLException sqlException, String message, String sql) {
+ JDBCExceptionReporter.logExceptions( sqlException, message + " [" + sql + "]" );
+ return converter.convert( sqlException, message, sql );
+ }
+
+ /**
+ * For the given SQLException, locates the vendor-specific error code.
+ *
+ * @param sqlException The exception from which to extract the SQLState
+ * @return The error code.
+ */
+ public static int extractErrorCode(SQLException sqlException) {
+ int errorCode = sqlException.getErrorCode();
+ SQLException nested = sqlException.getNextException();
+ while ( errorCode == 0 && nested != null ) {
+ errorCode = nested.getErrorCode();
+ nested = nested.getNextException();
+ }
+ return errorCode;
+ }
+
+ /**
+ * For the given SQLException, locates the X/Open-compliant SQLState.
+ *
+ * @param sqlException The exception from which to extract the SQLState
+ * @return The SQLState code, or null.
+ */
+ public static String extractSqlState(SQLException sqlException) {
+ String sqlState = sqlException.getSQLState();
+ SQLException nested = sqlException.getNextException();
+ while ( sqlState == null && nested != null ) {
+ sqlState = nested.getSQLState();
+ nested = nested.getNextException();
+ }
+ return sqlState;
+ }
+
+ /**
+ * For the given SQLException, locates the X/Open-compliant SQLState's class code.
+ *
+ * @param sqlException The exception from which to extract the SQLState class code
+ * @return The SQLState class code, or null.
+ */
+ public static String extractSqlStateClassCode(SQLException sqlException) {
+ return determineSqlStateClassCode( extractSqlState( sqlException ) );
+ }
+
+ public static String determineSqlStateClassCode(String sqlState) {
+ if ( sqlState == null || sqlState.length() < 2 ) {
+ return sqlState;
+ }
+ return sqlState.substring( 0, 2 );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/exception/LockAcquisitionException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/exception/LockAcquisitionException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/exception/LockAcquisitionException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+// $Id: LockAcquisitionException.java 4782 2004-11-21 00:11:27Z pgmjsd $
+package org.hibernate.exception;
+
+import org.hibernate.JDBCException;
+
+import java.sql.SQLException;
+
+/**
+ * Implementation of JDBCException indicating a problem acquiring lock
+ * on the database.
+ *
+ * @author Steve Ebersole
+ */
+public class LockAcquisitionException extends JDBCException {
+ public LockAcquisitionException(String string, SQLException root) {
+ super( string, root );
+ }
+
+ public LockAcquisitionException(String string, SQLException root, String sql) {
+ super( string, root, sql );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/exception/Nestable.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/exception/Nestable.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/exception/Nestable.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,203 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2002-2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ * any, must include the following acknowledgement:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgement may appear in the software itself,
+ * if and wherever such third-party acknowledgements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ * Foundation" must not be used to endorse or promote products derived
+ * from this software without prior written permission. For written
+ * permission, please contact apache at apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+package org.hibernate.exception;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+
+/**
+ * An interface to be implemented by {@link java.lang.Throwable}
+ * extensions which would like to be able to nest root exceptions
+ * inside themselves.
+ *
+ * @author <a href="mailto:dlr at collab.net">Daniel Rall</a>
+ * @author <a href="mailto:knielsen at apache.org">Kasper Nielsen</a>
+ * @author <a href="mailto:steven at caswell.name">Steven Caswell</a>
+ * @author Pete Gieser
+ * @version $Id: Nestable.java 4782 2004-11-21 00:11:27Z pgmjsd $
+ * @since 1.0
+ */
+public interface Nestable {
+
+ /**
+ * Returns the reference to the exception or error that caused the
+ * exception implementing the <code>Nestable</code> to be thrown.
+ *
+ * @return throwable that caused the original exception
+ */
+ public Throwable getCause();
+
+ /**
+ * Returns the error message of this and any nested
+ * <code>Throwable</code>.
+ *
+ * @return the error message
+ */
+ public String getMessage();
+
+ /**
+ * Returns the error message of the <code>Throwable</code> in the chain
+ * of <code>Throwable</code>s at the specified index, numbererd from 0.
+ *
+ * @param index the index of the <code>Throwable</code> in the chain of
+ * <code>Throwable</code>s
+ * @return the error message, or null if the <code>Throwable</code> at the
+ * specified index in the chain does not contain a message
+ * @throws IndexOutOfBoundsException if the <code>index</code> argument is
+ * negative or not less than the count of <code>Throwable</code>s in the
+ * chain
+ */
+ public String getMessage(int index);
+
+ /**
+ * Returns the error message of this and any nested <code>Throwable</code>s
+ * in an array of Strings, one element for each message. Any
+ * <code>Throwable</code> not containing a message is represented in the
+ * array by a null. This has the effect of cause the length of the returned
+ * array to be equal to the result of the {@link #getThrowableCount()}
+ * operation.
+ *
+ * @return the error messages
+ */
+ public String[] getMessages();
+
+ /**
+ * Returns the <code>Throwable</code> in the chain of
+ * <code>Throwable</code>s at the specified index, numbererd from 0.
+ *
+ * @param index the index, numbered from 0, of the <code>Throwable</code> in
+ * the chain of <code>Throwable</code>s
+ * @return the <code>Throwable</code>
+ * @throws IndexOutOfBoundsException if the <code>index</code> argument is
+ * negative or not less than the count of <code>Throwable</code>s in the
+ * chain
+ */
+ public Throwable getThrowable(int index);
+
+ /**
+ * Returns the number of nested <code>Throwable</code>s represented by
+ * this <code>Nestable</code>, including this <code>Nestable</code>.
+ *
+ * @return the throwable count
+ */
+ public int getThrowableCount();
+
+ /**
+ * Returns this <code>Nestable</code> and any nested <code>Throwable</code>s
+ * in an array of <code>Throwable</code>s, one element for each
+ * <code>Throwable</code>.
+ *
+ * @return the <code>Throwable</code>s
+ */
+ public Throwable[] getThrowables();
+
+ /**
+ * Returns the index, numbered from 0, of the first occurrence of the
+ * specified type in the chain of <code>Throwable</code>s, or -1 if the
+ * specified type is not found in the chain.
+ *
+ * @param type <code>Class</code> to be found
+ * @return index of the first occurrence of the type in the chain, or -1 if
+ * the type is not found
+ */
+ public int indexOfThrowable(Class type);
+
+ /**
+ * Returns the index, numbered from 0, of the first <code>Throwable</code>
+ * that matches the specified type in the chain of <code>Throwable</code>s
+ * with an index greater than or equal to the specified index, or -1 if
+ * the type is not found.
+ *
+ * @param type <code>Class</code> to be found
+ * @param fromIndex the index, numbered from 0, of the starting position in
+ * the chain to be searched
+ * @return index of the first occurrence of the type in the chain, or -1 if
+ * the type is not found
+ * @throws IndexOutOfBoundsException if the <code>fromIndex</code> argument
+ * is negative or not less than the count of <code>Throwable</code>s in the
+ * chain
+ */
+ public int indexOfThrowable(Class type, int fromIndex);
+
+ /**
+ * Prints the stack trace of this exception to the specified print
+ * writer. Includes information from the exception, if any,
+ * which caused this exception.
+ *
+ * @param out <code>PrintWriter</code> to use for output.
+ */
+ public void printStackTrace(PrintWriter out);
+
+ /**
+ * Prints the stack trace of this exception to the specified print
+ * stream. Includes inforamation from the exception, if any,
+ * which caused this exception.
+ *
+ * @param out <code>PrintStream</code> to use for output.
+ */
+ public void printStackTrace(PrintStream out);
+
+ /**
+ * Prints the stack trace for this exception only--root cause not
+ * included--using the provided writer. Used by {@link
+ * org.apache.commons.lang.exception.NestableDelegate} to write
+ * individual stack traces to a buffer. The implementation of
+ * this method should call
+ * <code>super.printStackTrace(out);</code> in most cases.
+ *
+ * @param out The writer to use.
+ */
+ public void printPartialStackTrace(PrintWriter out);
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/exception/NestableDelegate.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/exception/NestableDelegate.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/exception/NestableDelegate.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,412 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2002-2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ * any, must include the following acknowledgement:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgement may appear in the software itself,
+ * if and wherever such third-party acknowledgements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ * Foundation" must not be used to endorse or promote products derived
+ * from this software without prior written permission. For written
+ * permission, please contact apache at apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+package org.hibernate.exception;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.Serializable;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * <p>A shared implementation of the nestable exception functionality.</p>
+ * <p/>
+ * The code is shared between
+ * {@link org.apache.commons.lang.exception.NestableError NestableError},
+ * {@link org.apache.commons.lang.exception.NestableException NestableException} and
+ * {@link org.apache.commons.lang.exception.NestableRuntimeException NestableRuntimeException}.
+ * </p>
+ *
+ * @author <a href="mailto:Rafal.Krzewski at e-point.pl">Rafal Krzewski</a>
+ * @author <a href="mailto:dlr at collab.net">Daniel Rall</a>
+ * @author <a href="mailto:knielsen at apache.org">Kasper Nielsen</a>
+ * @author <a href="mailto:steven at caswell.name">Steven Caswell</a>
+ * @author Sean C. Sullivan
+ * @author Stephen Colebourne
+ * @version $Id: NestableDelegate.java 4782 2004-11-21 00:11:27Z pgmjsd $
+ * @since 1.0
+ */
+public class NestableDelegate implements Serializable {
+
+ /**
+ * Constructor error message.
+ */
+ private static final String MUST_BE_THROWABLE =
+ "The Nestable implementation passed to the NestableDelegate(Nestable) "
+ + "constructor must extend java.lang.Throwable";
+
+ /**
+ * Holds the reference to the exception or error that we're
+ * wrapping (which must be a {@link
+ * org.apache.commons.lang.exception.Nestable} implementation).
+ */
+ private Throwable nestable = null;
+
+ /**
+ * Whether to print the stack trace top-down.
+ * This public flag may be set by calling code, typically in initialisation.
+ *
+ * @since 2.0
+ */
+ private static boolean topDown = true;
+
+ /**
+ * Whether to trim the repeated stack trace.
+ * This public flag may be set by calling code, typically in initialisation.
+ *
+ * @since 2.0
+ */
+ private static boolean trimStackFrames = true;
+
+ /**
+ * Constructs a new <code>NestableDelegate</code> instance to manage the
+ * specified <code>Nestable</code>.
+ *
+ * @param nestable the Nestable implementation (<i>must</i> extend
+ * {@link java.lang.Throwable})
+ * @since 2.0
+ */
+ public NestableDelegate(Nestable nestable) {
+ if ( nestable instanceof Throwable ) {
+ this.nestable = ( Throwable ) nestable;
+ }
+ else {
+ throw new IllegalArgumentException( MUST_BE_THROWABLE );
+ }
+ }
+
+ /**
+ * Returns the error message of the <code>Throwable</code> in the chain
+ * of <code>Throwable</code>s at the specified index, numbererd from 0.
+ *
+ * @param index the index of the <code>Throwable</code> in the chain of
+ * <code>Throwable</code>s
+ * @return the error message, or null if the <code>Throwable</code> at the
+ * specified index in the chain does not contain a message
+ * @throws IndexOutOfBoundsException if the <code>index</code> argument is
+ * negative or not less than the count of <code>Throwable</code>s in the
+ * chain
+ * @since 2.0
+ */
+ public String getMessage(int index) {
+ Throwable t = this.getThrowable( index );
+ if ( Nestable.class.isInstance( t ) ) {
+ return ( ( Nestable ) t ).getMessage( 0 );
+ }
+ else {
+ return t.getMessage();
+ }
+ }
+
+ /**
+ * Returns the full message contained by the <code>Nestable</code>
+ * and any nested <code>Throwable</code>s.
+ *
+ * @param baseMsg the base message to use when creating the full
+ * message. Should be generally be called via
+ * <code>nestableHelper.getMessage( super.getMessage() )</code>,
+ * where <code>super</code> is an instance of {@link
+ * java.lang.Throwable}.
+ * @return The concatenated message for this and all nested
+ * <code>Throwable</code>s
+ * @since 2.0
+ */
+ public String getMessage(String baseMsg) {
+ StringBuffer msg = new StringBuffer();
+ if ( baseMsg != null ) {
+ msg.append( baseMsg );
+ }
+
+ Throwable nestedCause = ExceptionUtils.getCause( this.nestable );
+ if ( nestedCause != null ) {
+ String causeMsg = nestedCause.getMessage();
+ if ( causeMsg != null ) {
+ if ( baseMsg != null ) {
+ msg.append( ": " );
+ }
+ msg.append( causeMsg );
+ }
+
+ }
+ return ( msg.length() > 0 ? msg.toString() : null );
+ }
+
+ /**
+ * Returns the error message of this and any nested <code>Throwable</code>s
+ * in an array of Strings, one element for each message. Any
+ * <code>Throwable</code> not containing a message is represented in the
+ * array by a null. This has the effect of cause the length of the returned
+ * array to be equal to the result of the {@link #getThrowableCount()}
+ * operation.
+ *
+ * @return the error messages
+ * @since 2.0
+ */
+ public String[] getMessages() {
+ Throwable[] throwables = this.getThrowables();
+ String[] msgs = new String[throwables.length];
+ for ( int i = 0; i < throwables.length; i++ ) {
+ msgs[i] = Nestable.class.isInstance( throwables[i] ) ?
+ ( ( Nestable ) throwables[i] ).getMessage( 0 ) :
+ throwables[i].getMessage();
+ }
+ return msgs;
+ }
+
+ /**
+ * Returns the <code>Throwable</code> in the chain of
+ * <code>Throwable</code>s at the specified index, numbererd from 0.
+ *
+ * @param index the index, numbered from 0, of the <code>Throwable</code> in
+ * the chain of <code>Throwable</code>s
+ * @return the <code>Throwable</code>
+ * @throws IndexOutOfBoundsException if the <code>index</code> argument is
+ * negative or not less than the count of <code>Throwable</code>s in the
+ * chain
+ * @since 2.0
+ */
+ public Throwable getThrowable(int index) {
+ if ( index == 0 ) {
+ return this.nestable;
+ }
+ Throwable[] throwables = this.getThrowables();
+ return throwables[index];
+ }
+
+ /**
+ * Returns the number of <code>Throwable</code>s contained in the
+ * <code>Nestable</code> contained by this delegate.
+ *
+ * @return the throwable count
+ * @since 2.0
+ */
+ public int getThrowableCount() {
+ return ExceptionUtils.getThrowableCount( this.nestable );
+ }
+
+ /**
+ * Returns this delegate's <code>Nestable</code> and any nested
+ * <code>Throwable</code>s in an array of <code>Throwable</code>s, one
+ * element for each <code>Throwable</code>.
+ *
+ * @return the <code>Throwable</code>s
+ * @since 2.0
+ */
+ public Throwable[] getThrowables() {
+ return ExceptionUtils.getThrowables( this.nestable );
+ }
+
+ /**
+ * Returns the index, numbered from 0, of the first <code>Throwable</code>
+ * that matches the specified type in the chain of <code>Throwable</code>s
+ * held in this delegate's <code>Nestable</code> with an index greater than
+ * or equal to the specified index, or -1 if the type is not found.
+ *
+ * @param type <code>Class</code> to be found
+ * @param fromIndex the index, numbered from 0, of the starting position in
+ * the chain to be searched
+ * @return index of the first occurrence of the type in the chain, or -1 if
+ * the type is not found
+ * @throws IndexOutOfBoundsException if the <code>fromIndex</code> argument
+ * is negative or not less than the count of <code>Throwable</code>s in the
+ * chain
+ * @since 2.0
+ */
+ public int indexOfThrowable(Class type, int fromIndex) {
+ if ( fromIndex < 0 ) {
+ throw new IndexOutOfBoundsException( "The start index was out of bounds: " + fromIndex );
+ }
+ Throwable[] throwables = ExceptionUtils.getThrowables( this.nestable );
+ if ( fromIndex >= throwables.length ) {
+ throw new IndexOutOfBoundsException( "The start index was out of bounds: "
+ + fromIndex + " >= " + throwables.length );
+ }
+ for ( int i = fromIndex; i < throwables.length; i++ ) {
+ if ( throwables[i].getClass().equals( type ) ) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Prints the stack trace of this exception the the standar error
+ * stream.
+ */
+ public void printStackTrace() {
+ printStackTrace( System.err );
+ }
+
+ /**
+ * Prints the stack trace of this exception to the specified
+ * stream.
+ *
+ * @param out <code>PrintStream</code> to use for output.
+ * @see #printStackTrace(PrintWriter)
+ */
+ public void printStackTrace(PrintStream out) {
+ synchronized ( out ) {
+ PrintWriter pw = new PrintWriter( out, false );
+ printStackTrace( pw );
+ // Flush the PrintWriter before it's GC'ed.
+ pw.flush();
+ }
+ }
+
+ /**
+ * Prints the stack trace of this exception to the specified
+ * writer. If the Throwable class has a <code>getCause</code>
+ * method (i.e. running on jre1.4 or higher), this method just
+ * uses Throwable's printStackTrace() method. Otherwise, generates
+ * the stack-trace, by taking into account the 'topDown' and
+ * 'trimStackFrames' parameters. The topDown and trimStackFrames
+ * are set to 'true' by default (produces jre1.4-like stack trace).
+ *
+ * @param out <code>PrintWriter</code> to use for output.
+ */
+ public void printStackTrace(PrintWriter out) {
+ Throwable throwable = this.nestable;
+ // if running on jre1.4 or higher, use default printStackTrace
+ if ( ExceptionUtils.isThrowableNested() ) {
+ if ( throwable instanceof Nestable ) {
+ ( ( Nestable ) throwable ).printPartialStackTrace( out );
+ }
+ else {
+ throwable.printStackTrace( out );
+ }
+ return;
+ }
+
+ // generating the nested stack trace
+ List stacks = new ArrayList();
+ while ( throwable != null ) {
+ String[] st = getStackFrames( throwable );
+ stacks.add( st );
+ throwable = ExceptionUtils.getCause( throwable );
+ }
+
+ // If NOT topDown, reverse the stack
+ String separatorLine = "Caused by: ";
+ if ( !topDown ) {
+ separatorLine = "Rethrown as: ";
+ Collections.reverse( stacks );
+ }
+
+ // Remove the repeated lines in the stack
+ if ( trimStackFrames ) trimStackFrames( stacks );
+
+ synchronized ( out ) {
+ for ( Iterator iter = stacks.iterator(); iter.hasNext(); ) {
+ String[] st = ( String[] ) iter.next();
+ for ( int i = 0, len = st.length; i < len; i++ ) {
+ out.println( st[i] );
+ }
+ if ( iter.hasNext() ) out.print( separatorLine );
+ }
+ }
+ }
+
+ /**
+ * Captures the stack trace associated with the specified
+ * <code>Throwable</code> object, decomposing it into a list of
+ * stack frames.
+ *
+ * @param t The <code>Throwable</code>.
+ * @return An array of strings describing each stack frame.
+ * @since 2.0
+ */
+ protected String[] getStackFrames(Throwable t) {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter( sw, true );
+
+ // Avoid infinite loop between decompose() and printStackTrace().
+ if ( t instanceof Nestable ) {
+ ( ( Nestable ) t ).printPartialStackTrace( pw );
+ }
+ else {
+ t.printStackTrace( pw );
+ }
+ return ExceptionUtils.getStackFrames( sw.getBuffer().toString() );
+ }
+
+ /**
+ * Trims the stack frames. The first set is left untouched. The rest
+ * of the frames are truncated from the bottom by comparing with
+ * one just on top.
+ *
+ * @param stacks The list containing String[] elements
+ * @since 2.0
+ */
+ protected void trimStackFrames(List stacks) {
+ for ( int size = stacks.size(), i = size - 1; i > 0; i-- ) {
+ String[] curr = ( String[] ) stacks.get( i );
+ String[] next = ( String[] ) stacks.get( i - 1 );
+
+ List currList = new ArrayList( Arrays.asList( curr ) );
+ List nextList = new ArrayList( Arrays.asList( next ) );
+ ExceptionUtils.removeCommonFrames( currList, nextList );
+
+ int trimmed = curr.length - currList.size();
+ if ( trimmed > 0 ) {
+ currList.add( "\t... " + trimmed + " more" );
+ stacks.set( i,
+ currList.toArray( new String[currList.size()] ) );
+ }
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/exception/NestableException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/exception/NestableException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/exception/NestableException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,254 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2002-2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ * any, must include the following acknowledgement:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgement may appear in the software itself,
+ * if and wherever such third-party acknowledgements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ * Foundation" must not be used to endorse or promote products derived
+ * from this software without prior written permission. For written
+ * permission, please contact apache at apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+package org.hibernate.exception;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+
+/**
+ * The base class of all exceptions which can contain other exceptions.
+ * <p/>
+ * It is intended to ease the debugging by carrying on the information
+ * about the exception which was caught and provoked throwing the
+ * current exception. Catching and rethrowing may occur multiple
+ * times, and provided that all exceptions except the first one
+ * are descendands of <code>NestedException</code>, when the
+ * exception is finally printed out using any of the <code>
+ * printStackTrace()</code> methods, the stacktrace will contain
+ * the information about all exceptions thrown and caught on
+ * the way.
+ * <p> Running the following program
+ * <p><blockquote><pre>
+ * 1 import org.apache.commons.lang.exception.NestableException;
+ * 2
+ * 3 public class Test {
+ * 4 public static void main( String[] args ) {
+ * 5 try {
+ * 6 a();
+ * 7 } catch(Exception e) {
+ * 8 e.printStackTrace();
+ * 9 }
+ * 10 }
+ * 11
+ * 12 public static void a() throws Exception {
+ * 13 try {
+ * 14 b();
+ * 15 } catch(Exception e) {
+ * 16 throw new NestableException("foo", e);
+ * 17 }
+ * 18 }
+ * 19
+ * 20 public static void b() throws Exception {
+ * 21 try {
+ * 22 c();
+ * 23 } catch(Exception e) {
+ * 24 throw new NestableException("bar", e);
+ * 25 }
+ * 26 }
+ * 27
+ * 28 public static void c() throws Exception {
+ * 29 throw new Exception("baz");
+ * 30 }
+ * 31 }
+ * </pre></blockquote>
+ * <p>Yields the following stacktrace:
+ * <p><blockquote><pre>
+ * org.apache.commons.lang.exception.NestableException: foo
+ * at Test.a(Test.java:16)
+ * at Test.main(Test.java:6)
+ * Caused by: org.apache.commons.lang.exception.NestableException: bar
+ * at Test.b(Test.java:24)
+ * at Test.a(Test.java:14)
+ * ... 1 more
+ * Caused by: java.lang.Exception: baz
+ * at Test.c(Test.java:29)
+ * at Test.b(Test.java:22)
+ * ... 2 more
+ * </pre></blockquote><br>
+ *
+ * @author <a href="mailto:Rafal.Krzewski at e-point.pl">Rafal Krzewski</a>
+ * @author <a href="mailto:dlr at collab.net">Daniel Rall</a>
+ * @author <a href="mailto:knielsen at apache.org">Kasper Nielsen</a>
+ * @author <a href="mailto:steven at caswell.name">Steven Caswell</a>
+ * @version $Id: NestableException.java 4782 2004-11-21 00:11:27Z pgmjsd $
+ * @since 1.0
+ */
+public class NestableException extends Exception implements Nestable {
+
+ /**
+ * The helper instance which contains much of the code which we
+ * delegate to.
+ */
+ protected NestableDelegate delegate = new NestableDelegate( this );
+
+ /**
+ * Holds the reference to the exception or error that caused
+ * this exception to be thrown.
+ */
+ private Throwable cause = null;
+
+ /**
+ * Constructs a new <code>NestableException</code> without specified
+ * detail message.
+ */
+ public NestableException() {
+ super();
+ }
+
+ /**
+ * Constructs a new <code>NestableException</code> with specified
+ * detail message.
+ *
+ * @param msg The error message.
+ */
+ public NestableException(String msg) {
+ super( msg );
+ }
+
+ /**
+ * Constructs a new <code>NestableException</code> with specified
+ * nested <code>Throwable</code>.
+ *
+ * @param cause the exception or error that caused this exception to be
+ * thrown
+ */
+ public NestableException(Throwable cause) {
+ super();
+ this.cause = cause;
+ }
+
+ /**
+ * Constructs a new <code>NestableException</code> with specified
+ * detail message and nested <code>Throwable</code>.
+ *
+ * @param msg the error message
+ * @param cause the exception or error that caused this exception to be
+ * thrown
+ */
+ public NestableException(String msg, Throwable cause) {
+ super( msg );
+ this.cause = cause;
+ }
+
+ public Throwable getCause() {
+ return cause;
+ }
+
+ /**
+ * Returns the detail message string of this throwable. If it was
+ * created with a null message, returns the following:
+ * ( cause==null ? null : cause.toString() ).
+ */
+ public String getMessage() {
+ if ( super.getMessage() != null ) {
+ return super.getMessage();
+ }
+ else if ( cause != null ) {
+ return cause.toString();
+ }
+ else {
+ return null;
+ }
+ }
+
+ public String getMessage(int index) {
+ if ( index == 0 ) {
+ return super.getMessage();
+ }
+ else {
+ return delegate.getMessage( index );
+ }
+ }
+
+ public String[] getMessages() {
+ return delegate.getMessages();
+ }
+
+ public Throwable getThrowable(int index) {
+ return delegate.getThrowable( index );
+ }
+
+ public int getThrowableCount() {
+ return delegate.getThrowableCount();
+ }
+
+ public Throwable[] getThrowables() {
+ return delegate.getThrowables();
+ }
+
+ public int indexOfThrowable(Class type) {
+ return delegate.indexOfThrowable( type, 0 );
+ }
+
+ public int indexOfThrowable(Class type, int fromIndex) {
+ return delegate.indexOfThrowable( type, fromIndex );
+ }
+
+ public void printStackTrace() {
+ delegate.printStackTrace();
+ }
+
+ public void printStackTrace(PrintStream out) {
+ delegate.printStackTrace( out );
+ }
+
+ public void printStackTrace(PrintWriter out) {
+ delegate.printStackTrace( out );
+ }
+
+ public final void printPartialStackTrace(PrintWriter out) {
+ super.printStackTrace( out );
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/exception/NestableRuntimeException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/exception/NestableRuntimeException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/exception/NestableRuntimeException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,214 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2002-2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ * any, must include the following acknowledgement:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgement may appear in the software itself,
+ * if and wherever such third-party acknowledgements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ * Foundation" must not be used to endorse or promote products derived
+ * from this software without prior written permission. For written
+ * permission, please contact apache at apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+package org.hibernate.exception;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.ObjectOutputStream;
+import java.io.IOException;
+
+import antlr.RecognitionException;
+
+/**
+ * The base class of all runtime exceptions which can contain other
+ * exceptions.
+ *
+ * @author <a href="mailto:Rafal.Krzewski at e-point.pl">Rafal Krzewski</a>
+ * @author <a href="mailto:dlr at collab.net">Daniel Rall</a>
+ * @author <a href="mailto:knielsen at apache.org">Kasper Nielsen</a>
+ * @author <a href="mailto:steven at caswell.name">Steven Caswell</a>
+ * @version $Id: NestableRuntimeException.java 8137 2005-09-09 15:21:10Z epbernard $
+ * @see org.apache.commons.lang.exception.NestableException
+ * @since 1.0
+ */
+public class NestableRuntimeException extends RuntimeException implements Nestable {
+
+ /**
+ * The helper instance which contains much of the code which we
+ * delegate to.
+ */
+ protected NestableDelegate delegate = new NestableDelegate( this );
+
+ /**
+ * Holds the reference to the exception or error that caused
+ * this exception to be thrown.
+ */
+ private Throwable cause = null;
+
+ /**
+ * Constructs a new <code>NestableRuntimeException</code> without specified
+ * detail message.
+ */
+ public NestableRuntimeException() {
+ super();
+ }
+
+ /**
+ * Constructs a new <code>NestableRuntimeException</code> with specified
+ * detail message.
+ *
+ * @param msg the error message
+ */
+ public NestableRuntimeException(String msg) {
+ super( msg );
+ }
+
+ /**
+ * Constructs a new <code>NestableRuntimeException</code> with specified
+ * nested <code>Throwable</code>.
+ *
+ * @param cause the exception or error that caused this exception to be
+ * thrown
+ */
+ public NestableRuntimeException(Throwable cause) {
+ super();
+ this.cause = cause;
+ }
+
+ /**
+ * Constructs a new <code>NestableRuntimeException</code> with specified
+ * detail message and nested <code>Throwable</code>.
+ *
+ * @param msg the error message
+ * @param cause the exception or error that caused this exception to be
+ * thrown
+ */
+ public NestableRuntimeException(String msg, Throwable cause) {
+ super( msg );
+ this.cause = cause;
+ }
+
+ public Throwable getCause() {
+ return cause;
+ }
+
+ /**
+ * Returns the detail message string of this throwable. If it was
+ * created with a null message, returns the following:
+ * ( cause==null ? null : cause.toString( ).
+ */
+ public String getMessage() {
+ if ( super.getMessage() != null ) {
+ return super.getMessage();
+ }
+ else if ( cause != null ) {
+ return cause.toString();
+ }
+ else {
+ return null;
+ }
+ }
+
+ public String getMessage(int index) {
+ if ( index == 0 ) {
+ return super.getMessage();
+ }
+ else {
+ return delegate.getMessage( index );
+ }
+ }
+
+ public String[] getMessages() {
+ return delegate.getMessages();
+ }
+
+ public Throwable getThrowable(int index) {
+ return delegate.getThrowable( index );
+ }
+
+ public int getThrowableCount() {
+ return delegate.getThrowableCount();
+ }
+
+ public Throwable[] getThrowables() {
+ return delegate.getThrowables();
+ }
+
+ public int indexOfThrowable(Class type) {
+ return delegate.indexOfThrowable( type, 0 );
+ }
+
+ public int indexOfThrowable(Class type, int fromIndex) {
+ return delegate.indexOfThrowable( type, fromIndex );
+ }
+
+ public void printStackTrace() {
+ delegate.printStackTrace();
+ }
+
+ public void printStackTrace(PrintStream out) {
+ delegate.printStackTrace( out );
+ }
+
+ public void printStackTrace(PrintWriter out) {
+ delegate.printStackTrace( out );
+ }
+
+ public final void printPartialStackTrace(PrintWriter out) {
+ super.printStackTrace( out );
+ }
+
+
+
+ private void writeObject(ObjectOutputStream oos) throws IOException {
+ Throwable tempCause = cause;
+ //don't propagate RecognitionException, might be not serializable
+ if ( cause instanceof RecognitionException ) {
+ cause = null;
+ }
+ oos.defaultWriteObject();
+ cause = tempCause;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/exception/SQLExceptionConverter.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/exception/SQLExceptionConverter.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/exception/SQLExceptionConverter.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,32 @@
+// $Id: SQLExceptionConverter.java 4782 2004-11-21 00:11:27Z pgmjsd $
+package org.hibernate.exception;
+
+import org.hibernate.JDBCException;
+
+import java.sql.SQLException;
+
+/**
+ * Defines a contract for implementations that know how to convert a SQLException
+ * into Hibernate's JDBCException hierarchy. Inspired by Spring's
+ * SQLExceptionTranslator.
+ * <p/>
+ * Implementations <b>must</b> have a constructor which takes a
+ * {@link ViolatedConstraintNameExtracter} parameter.
+ * <p/>
+ * Implementations may implement {@link Configurable} if they need to perform
+ * configuration steps prior to first use.
+ *
+ * @author Steve Ebersole
+ * @see SQLExceptionConverterFactory
+ */
+public interface SQLExceptionConverter {
+ /**
+ * Convert the given SQLException into Hibernate's JDBCException hierarchy.
+ *
+ * @param sqlException The SQLException to be converted.
+ * @param message An optional error message.
+ * @return The resulting JDBCException.
+ * @see ConstraintViolationException, JDBCConnectionException, SQLGrammarException, LockAcquisitionException
+ */
+ public JDBCException convert(SQLException sqlException, String message, String sql);
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/exception/SQLExceptionConverterFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/exception/SQLExceptionConverterFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/exception/SQLExceptionConverterFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,114 @@
+// $Id: SQLExceptionConverterFactory.java 4782 2004-11-21 00:11:27Z pgmjsd $
+package org.hibernate.exception;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.HibernateException;
+import org.hibernate.JDBCException;
+import org.hibernate.cfg.Environment;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.util.ReflectHelper;
+import org.hibernate.util.StringHelper;
+
+import java.lang.reflect.Constructor;
+import java.sql.SQLException;
+import java.util.Properties;
+
+/**
+ * A factory for building SQLExceptionConverter instances.
+ *
+ * @author Steve Ebersole
+ */
+public class SQLExceptionConverterFactory {
+
+ private static final Log log = LogFactory.getLog( SQLExceptionConverterFactory.class );
+
+ private SQLExceptionConverterFactory() {
+ // Private constructor - stops checkstyle from complaining.
+ }
+
+ /**
+ * Build a SQLExceptionConverter instance.
+ * <p/>
+ * First, looks for a {@link Environment.SQL_EXCEPTION_CONVERTER} property to see
+ * if the configuration specified the class of a specific converter to use. If this
+ * property is set, attempt to construct an instance of that class. If not set, or
+ * if construction fails, the converter specific to the dialect will be used.
+ *
+ * @param dialect The defined dialect.
+ * @param properties The configuration properties.
+ * @return An appropriate SQLExceptionConverter instance.
+ * @throws HibernateException There was an error building the SQLExceptionConverter.
+ */
+ public static SQLExceptionConverter buildSQLExceptionConverter(Dialect dialect, Properties properties) throws HibernateException {
+ SQLExceptionConverter converter = null;
+
+ String converterClassName = ( String ) properties.get( Environment.SQL_EXCEPTION_CONVERTER );
+ if ( StringHelper.isNotEmpty( converterClassName ) ) {
+ converter = constructConverter( converterClassName, dialect.getViolatedConstraintNameExtracter() );
+ }
+
+ if ( converter == null ) {
+ log.trace( "Using dialect defined converter" );
+ converter = dialect.buildSQLExceptionConverter();
+ }
+
+ if ( converter instanceof Configurable ) {
+ try {
+ ( ( Configurable ) converter ).configure( properties );
+ }
+ catch ( HibernateException e ) {
+ log.warn( "Unable to configure SQLExceptionConverter", e );
+ throw e;
+ }
+ }
+
+ return converter;
+ }
+
+ /**
+ * Builds a minimal converter. The instance returned here just always converts to
+ * {@link GenericJDBCException}.
+ *
+ * @return The minimal converter.
+ */
+ public static SQLExceptionConverter buildMinimalSQLExceptionConverter() {
+ return new SQLExceptionConverter() {
+ public JDBCException convert(SQLException sqlException, String message, String sql) {
+ return new GenericJDBCException( message, sqlException, sql );
+ }
+ };
+ }
+
+ private static SQLExceptionConverter constructConverter(String converterClassName, ViolatedConstraintNameExtracter violatedConstraintNameExtracter) {
+ try {
+ log.trace( "Attempting to construct instance of specified SQLExceptionConverter [" + converterClassName + "]" );
+ Class converterClass = ReflectHelper.classForName( converterClassName );
+
+ // First, try to find a matching constructor accepting a ViolatedConstraintNameExtracter param...
+ Constructor[] ctors = converterClass.getDeclaredConstructors();
+ for ( int i = 0; i < ctors.length; i++ ) {
+ if ( ctors[i].getParameterTypes() != null && ctors[i].getParameterTypes().length == 1 ) {
+ if ( ViolatedConstraintNameExtracter.class.isAssignableFrom( ctors[i].getParameterTypes()[0] ) ) {
+ try {
+ return ( SQLExceptionConverter )
+ ctors[i].newInstance( new Object[]{violatedConstraintNameExtracter} );
+ }
+ catch ( Throwable t ) {
+ // eat it and try next
+ }
+ }
+ }
+ }
+
+ // Otherwise, try to use the no-arg constructor
+ return ( SQLExceptionConverter ) converterClass.newInstance();
+
+ }
+ catch ( Throwable t ) {
+ log.warn( "Unable to construct instance of specified SQLExceptionConverter", t );
+ }
+
+ return null;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/exception/SQLGrammarException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/exception/SQLGrammarException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/exception/SQLGrammarException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,33 @@
+// $Id: SQLGrammarException.java 4782 2004-11-21 00:11:27Z pgmjsd $
+package org.hibernate.exception;
+
+import org.hibernate.JDBCException;
+
+import java.sql.SQLException;
+
+/**
+ * Implementation of JDBCException indicating that the SQL sent to the database
+ * server was invalid (syntax error, invalid object references, etc).
+ *
+ * @author Steve Ebersole
+ */
+public class SQLGrammarException extends JDBCException {
+ /**
+ * Constructor for JDBCException.
+ *
+ * @param root The underlying exception.
+ */
+ public SQLGrammarException(String message, SQLException root) {
+ super( message, root );
+ }
+
+ /**
+ * Constructor for JDBCException.
+ *
+ * @param message Optional message.
+ * @param root The underlying exception.
+ */
+ public SQLGrammarException(String message, SQLException root, String sql) {
+ super( message, root, sql );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/exception/SQLStateConverter.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/exception/SQLStateConverter.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/exception/SQLStateConverter.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,105 @@
+// $Id: SQLStateConverter.java 9557 2006-03-06 15:16:27Z steve.ebersole at jboss.com $
+package org.hibernate.exception;
+
+import org.hibernate.JDBCException;
+
+import java.sql.SQLException;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A SQLExceptionConverter implementation which performs converion based on
+ * the underlying SQLState. Interpretation of a SQL error based on SQLState
+ * is not nearly as accurate as using the ErrorCode (which is, however, vendor-
+ * specific). Use of a ErrorCode-based converter should be preferred approach
+ * for converting/interpreting SQLExceptions.
+ *
+ * @author Steve Ebersole
+ */
+public class SQLStateConverter implements SQLExceptionConverter {
+
+ private ViolatedConstraintNameExtracter extracter;
+
+ private static final Set SQL_GRAMMAR_CATEGORIES = new HashSet();
+ private static final Set DATA_CATEGORIES = new HashSet();
+ private static final Set INTEGRITY_VIOLATION_CATEGORIES = new HashSet();
+ private static final Set CONNECTION_CATEGORIES = new HashSet();
+
+ static {
+ SQL_GRAMMAR_CATEGORIES.add( "07" );
+ SQL_GRAMMAR_CATEGORIES.add( "37" );
+ SQL_GRAMMAR_CATEGORIES.add( "42" );
+ SQL_GRAMMAR_CATEGORIES.add( "65" );
+ SQL_GRAMMAR_CATEGORIES.add( "S0" );
+ SQL_GRAMMAR_CATEGORIES.add( "20" );
+
+ DATA_CATEGORIES.add("22");
+ DATA_CATEGORIES.add("21");
+ DATA_CATEGORIES.add("02");
+
+ INTEGRITY_VIOLATION_CATEGORIES.add( "23" );
+ INTEGRITY_VIOLATION_CATEGORIES.add( "27" );
+ INTEGRITY_VIOLATION_CATEGORIES.add( "44" );
+
+ CONNECTION_CATEGORIES.add( "08" );
+ }
+
+ public SQLStateConverter(ViolatedConstraintNameExtracter extracter) {
+ this.extracter = extracter;
+ }
+
+ /**
+ * Convert the given SQLException into Hibernate's JDBCException hierarchy.
+ *
+ * @param sqlException The SQLException to be converted.
+ * @param message An optional error message.
+ * @param sql Optionally, the sql being performed when the exception occurred.
+ * @return The resulting JDBCException.
+ */
+ public JDBCException convert(SQLException sqlException, String message, String sql) {
+ String sqlState = JDBCExceptionHelper.extractSqlState( sqlException );
+
+ if ( sqlState != null ) {
+ String sqlStateClassCode = JDBCExceptionHelper.determineSqlStateClassCode( sqlState );
+
+ if ( sqlStateClassCode != null ) {
+ if ( SQL_GRAMMAR_CATEGORIES.contains( sqlStateClassCode ) ) {
+ return new SQLGrammarException( message, sqlException, sql );
+ }
+ else if ( INTEGRITY_VIOLATION_CATEGORIES.contains( sqlStateClassCode ) ) {
+ String constraintName = extracter.extractConstraintName( sqlException );
+ return new ConstraintViolationException( message, sqlException, sql, constraintName );
+ }
+ else if ( CONNECTION_CATEGORIES.contains( sqlStateClassCode ) ) {
+ return new JDBCConnectionException( message, sqlException, sql );
+ }
+ else if ( DATA_CATEGORIES.contains( sqlStateClassCode ) ) {
+ return new DataException( message, sqlException, sql );
+ }
+ }
+
+ if ( "40001".equals( sqlState ) ) {
+ return new LockAcquisitionException( message, sqlException, sql );
+ }
+
+ if ( "61000".equals( sqlState ) ) {
+ // oracle sql-state code for deadlock
+ return new LockAcquisitionException( message, sqlException, sql );
+ }
+ }
+
+ return handledNonSpecificException( sqlException, message, sql );
+ }
+
+ /**
+ * Handle an exception not converted to a specific type based on the SQLState.
+ *
+ * @param sqlException The exception to be handled.
+ * @param message An optional message
+ * @param sql Optionally, the sql being performed when the exception occurred.
+ * @return The converted exception; should <b>never</b> be null.
+ */
+ protected JDBCException handledNonSpecificException(SQLException sqlException, String message, String sql) {
+ return new GenericJDBCException( message, sqlException, sql );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/exception/TemplatedViolatedConstraintNameExtracter.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/exception/TemplatedViolatedConstraintNameExtracter.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/exception/TemplatedViolatedConstraintNameExtracter.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,37 @@
+// $Id: TemplatedViolatedConstraintNameExtracter.java 4782 2004-11-21 00:11:27Z pgmjsd $
+package org.hibernate.exception;
+
+
+
+/**
+ * Knows how to extract a violated constraint name from an error message based on the
+ * fact that the constraint name is templated within the message.
+ *
+ * @author Steve Ebersole
+ */
+public abstract class TemplatedViolatedConstraintNameExtracter implements ViolatedConstraintNameExtracter {
+
+ /**
+ * Extracts the constraint name based on a template (i.e., <i>templateStart</i><b>constraintName</b><i>templateEnd</i>).
+ *
+ * @param templateStart The pattern denoting the start of the constraint name within the message.
+ * @param templateEnd The pattern denoting the end of the constraint name within the message.
+ * @param message The templated error message containing the constraint name.
+ * @return The found constraint name, or null.
+ */
+ protected String extractUsingTemplate(String templateStart, String templateEnd, String message) {
+ int templateStartPosition = message.indexOf( templateStart );
+ if ( templateStartPosition < 0 ) {
+ return null;
+ }
+
+ int start = templateStartPosition + templateStart.length();
+ int end = message.indexOf( templateEnd, start );
+ if ( end < 0 ) {
+ end = message.length();
+ }
+
+ return message.substring( start, end );
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/exception/ViolatedConstraintNameExtracter.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/exception/ViolatedConstraintNameExtracter.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/exception/ViolatedConstraintNameExtracter.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,20 @@
+// $Id: ViolatedConstraintNameExtracter.java 4746 2004-11-11 20:57:28Z steveebersole $
+package org.hibernate.exception;
+
+import java.sql.SQLException;
+
+/**
+ * Defines a contract for implementations that can extract the name of a violated
+ * constraint from a SQLException that is the result of that constraint violation.
+ *
+ * @author Steve Ebersole
+ */
+public interface ViolatedConstraintNameExtracter {
+ /**
+ * Extract the name of the violated constraint from the given SQLException.
+ *
+ * @param sqle The exception that was the result of the constraint violation.
+ * @return The extracted constraint name.
+ */
+ public String extractConstraintName(SQLException sqle);
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/exception/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/exception/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/exception/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,8 @@
+<html>
+<head></head>
+<body>
+<p>
+ This package is a fork of Apache commons-lang nestable exceptions.
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/exception/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/CollectionProperties.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/CollectionProperties.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/CollectionProperties.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,53 @@
+// $Id: CollectionProperties.java 5699 2005-02-13 11:50:11Z oneovthafew $
+package org.hibernate.hql;
+
+import org.hibernate.persister.collection.CollectionPropertyNames;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Provides a map of collection function names to the corresponding property names.
+ *
+ * @author josh Aug 16, 2004 7:51:45 PM
+ */
+public final class CollectionProperties {
+ public static final Map HQL_COLLECTION_PROPERTIES;
+
+ private static final String COLLECTION_INDEX_LOWER = CollectionPropertyNames.COLLECTION_INDEX.toLowerCase();
+
+ static {
+ HQL_COLLECTION_PROPERTIES = new HashMap();
+ HQL_COLLECTION_PROPERTIES.put( CollectionPropertyNames.COLLECTION_ELEMENTS.toLowerCase(), CollectionPropertyNames.COLLECTION_ELEMENTS );
+ HQL_COLLECTION_PROPERTIES.put( CollectionPropertyNames.COLLECTION_INDICES.toLowerCase(), CollectionPropertyNames.COLLECTION_INDICES );
+ HQL_COLLECTION_PROPERTIES.put( CollectionPropertyNames.COLLECTION_SIZE.toLowerCase(), CollectionPropertyNames.COLLECTION_SIZE );
+ HQL_COLLECTION_PROPERTIES.put( CollectionPropertyNames.COLLECTION_MAX_INDEX.toLowerCase(), CollectionPropertyNames.COLLECTION_MAX_INDEX );
+ HQL_COLLECTION_PROPERTIES.put( CollectionPropertyNames.COLLECTION_MIN_INDEX.toLowerCase(), CollectionPropertyNames.COLLECTION_MIN_INDEX );
+ HQL_COLLECTION_PROPERTIES.put( CollectionPropertyNames.COLLECTION_MAX_ELEMENT.toLowerCase(), CollectionPropertyNames.COLLECTION_MAX_ELEMENT );
+ HQL_COLLECTION_PROPERTIES.put( CollectionPropertyNames.COLLECTION_MIN_ELEMENT.toLowerCase(), CollectionPropertyNames.COLLECTION_MIN_ELEMENT );
+ HQL_COLLECTION_PROPERTIES.put( COLLECTION_INDEX_LOWER, CollectionPropertyNames.COLLECTION_INDEX );
+ }
+
+ private CollectionProperties() {
+ }
+
+ public static boolean isCollectionProperty(String name) {
+ String key = name.toLowerCase();
+ // CollectionPropertyMapping processes everything except 'index'.
+ if ( COLLECTION_INDEX_LOWER.equals( key ) ) {
+ return false;
+ }
+ else {
+ return HQL_COLLECTION_PROPERTIES.containsKey( key );
+ }
+ }
+
+ public static String getNormalizedPropertyName(String name) {
+ return ( String ) HQL_COLLECTION_PROPERTIES.get( name );
+ }
+
+ public static boolean isAnyCollectionProperty(String name) {
+ String key = name.toLowerCase();
+ return HQL_COLLECTION_PROPERTIES.containsKey( key );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/CollectionSubqueryFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/CollectionSubqueryFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/CollectionSubqueryFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,44 @@
+// $Id: CollectionSubqueryFactory.java 9046 2006-01-13 03:10:57Z steveebersole $
+package org.hibernate.hql;
+
+import org.hibernate.engine.JoinSequence;
+import org.hibernate.sql.JoinFragment;
+import org.hibernate.MappingException;
+import org.hibernate.QueryException;
+import org.hibernate.util.StringHelper;
+
+import java.util.Map;
+
+/**
+ * Provides the SQL for collection subqueries.
+ * <br>
+ * Moved here from PathExpressionParser to make it re-useable.
+ *
+ * @author josh
+ */
+public final class CollectionSubqueryFactory {
+
+ //TODO: refactor to .sql package
+
+ private CollectionSubqueryFactory() {
+ }
+
+ public static String createCollectionSubquery(
+ JoinSequence joinSequence,
+ Map enabledFilters,
+ String[] columns) {
+ try {
+ JoinFragment join = joinSequence.toJoinFragment( enabledFilters, true );
+ return new StringBuffer( "select " )
+ .append( StringHelper.join( ", ", columns ) )
+ .append( " from " )
+ .append( join.toFromFragmentString().substring( 2 ) )// remove initial ", "
+ .append( " where " )
+ .append( join.toWhereFragmentString().substring( 5 ) )// remove initial " and "
+ .toString();
+ }
+ catch ( MappingException me ) {
+ throw new QueryException( me );
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/FilterTranslator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/FilterTranslator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/FilterTranslator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,28 @@
+// $Id: FilterTranslator.java 4899 2004-12-06 14:17:24Z pgmjsd $
+package org.hibernate.hql;
+
+import org.hibernate.MappingException;
+import org.hibernate.QueryException;
+
+import java.util.Map;
+
+
+/**
+ * Specialized interface for filters.
+ *
+ * @author josh Mar 14, 2004 11:33:35 AM
+ */
+public interface FilterTranslator extends QueryTranslator {
+ /**
+ * Compile a filter. This method may be called multiple
+ * times. Subsequent invocations are no-ops.
+ *
+ * @param collectionRole the role name of the collection used as the basis for the filter.
+ * @param replacements Defined query substitutions.
+ * @param shallow Does this represent a shallow (scalar or entity-id) select?
+ * @throws QueryException There was a problem parsing the query string.
+ * @throws MappingException There was a problem querying defined mappings.
+ */
+ void compile(String collectionRole, Map replacements, boolean shallow)
+ throws QueryException, MappingException;
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/HolderInstantiator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/HolderInstantiator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/HolderInstantiator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,81 @@
+//$Id: HolderInstantiator.java 9636 2006-03-16 14:14:48Z max.andersen at jboss.com $
+package org.hibernate.hql;
+
+import java.lang.reflect.Constructor;
+
+import org.hibernate.transform.AliasToBeanConstructorResultTransformer;
+import org.hibernate.transform.ResultTransformer;
+import org.hibernate.transform.Transformers;
+
+/**
+ * @author Gavin King
+ */
+public final class HolderInstantiator {
+
+ public static final HolderInstantiator NOOP_INSTANTIATOR = new HolderInstantiator(null,null);
+
+ private final ResultTransformer transformer;
+ private final String[] queryReturnAliases;
+
+ public static HolderInstantiator getHolderInstantiator(ResultTransformer selectNewTransformer, ResultTransformer customTransformer, String[] queryReturnAliases) {
+ if(selectNewTransformer!=null) {
+ return new HolderInstantiator(selectNewTransformer, queryReturnAliases);
+ } else {
+ return new HolderInstantiator(customTransformer, queryReturnAliases);
+ }
+ }
+
+ public static ResultTransformer createSelectNewTransformer(Constructor constructor, boolean returnMaps, boolean returnLists) {
+ if ( constructor != null ) {
+ return new AliasToBeanConstructorResultTransformer(constructor);
+ }
+ else if ( returnMaps ) {
+ return Transformers.ALIAS_TO_ENTITY_MAP;
+ }
+ else if ( returnLists ) {
+ return Transformers.TO_LIST;
+ }
+ else {
+ return null;
+ }
+ }
+
+ static public HolderInstantiator createClassicHolderInstantiator(Constructor constructor,
+ ResultTransformer transformer) {
+ if ( constructor != null ) {
+ return new HolderInstantiator(new AliasToBeanConstructorResultTransformer(constructor), null);
+ }
+ else {
+ return new HolderInstantiator(transformer, null);
+ }
+ }
+
+ public HolderInstantiator(
+ ResultTransformer transformer,
+ String[] queryReturnAliases
+ ) {
+ this.transformer = transformer;
+ this.queryReturnAliases = queryReturnAliases;
+ }
+
+ public boolean isRequired() {
+ return transformer!=null;
+ }
+
+ public Object instantiate(Object[] row) {
+ if(transformer==null) {
+ return row;
+ } else {
+ return transformer.transformTuple(row, queryReturnAliases);
+ }
+ }
+
+ public String[] getQueryReturnAliases() {
+ return queryReturnAliases;
+ }
+
+ public ResultTransformer getResultTransformer() {
+ return transformer;
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/HolderInstantiator.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/NameGenerator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/NameGenerator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/NameGenerator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,41 @@
+// $Id: NameGenerator.java 4899 2004-12-06 14:17:24Z pgmjsd $
+package org.hibernate.hql;
+
+import org.hibernate.MappingException;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.type.Type;
+
+/**
+ * Provides utility methods for generating HQL / SQL names. Shared by both the 'classic' and 'new' query translators.
+ *
+ * @author josh Mar 18, 2004 7:17:25 AM
+ */
+public final class NameGenerator {
+ /**
+ * Private empty constructor (checkstyle says utility classes should not have default constructors).
+ */
+ private NameGenerator() {
+ }
+
+ public static String[][] generateColumnNames(Type[] types, SessionFactoryImplementor f) throws MappingException {
+ String[][] columnNames = new String[types.length][];
+ for ( int i = 0; i < types.length; i++ ) {
+ int span = types[i].getColumnSpan( f );
+ columnNames[i] = new String[span];
+ for ( int j = 0; j < span; j++ ) {
+ columnNames[i][j] = NameGenerator.scalarName( i, j );
+ }
+ }
+ return columnNames;
+ }
+
+ public static String scalarName(int x, int y) {
+ return new StringBuffer()
+ .append( "col_" )
+ .append( x )
+ .append( '_' )
+ .append( y )
+ .append( '_' )
+ .toString();
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ParameterTranslations.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ParameterTranslations.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ParameterTranslations.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,27 @@
+package org.hibernate.hql;
+
+import org.hibernate.type.Type;
+import java.util.Set;
+
+/**
+ * Defines available information about the parameters encountered during
+ * query translation.
+ *
+ * @author <a href="mailto:steve at hibernate.org">Steve Ebersole </a>
+ */
+public interface ParameterTranslations {
+
+ public boolean supportsOrdinalParameterMetadata();
+
+ public int getOrdinalParameterCount();
+
+ public int getOrdinalParameterSqlLocation(int ordinalPosition);
+
+ public Type getOrdinalParameterExpectedType(int ordinalPosition);
+
+ public Set getNamedParameterNames();
+
+ public int[] getNamedParameterSqlLocations(String name);
+
+ public Type getNamedParameterExpectedType(String name);
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/QueryExecutionRequestException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/QueryExecutionRequestException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/QueryExecutionRequestException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,16 @@
+//$Id: $
+package org.hibernate.hql;
+
+import org.hibernate.QueryException;
+
+/**
+ * Expecting to execute an illegal operation regarding the query type
+ *
+ * @author Emmanuel Bernard
+ */
+public class QueryExecutionRequestException extends QueryException {
+
+ public QueryExecutionRequestException(String message, String queryString) {
+ super( message, queryString );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/QuerySplitter.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/QuerySplitter.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/QuerySplitter.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,134 @@
+//$Id: QuerySplitter.java 7646 2005-07-25 07:37:13Z oneovthafew $
+package org.hibernate.hql;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.MappingException;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.hql.classic.ParserHelper;
+import org.hibernate.util.StringHelper;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Provides query splitting methods, which were originally in QueryTranslator.
+ * <br>
+ * TODO: This will need to be refactored at some point.
+ *
+ * @author josh Mar 14, 2004 10:50:23 AM
+ */
+public final class QuerySplitter {
+
+ private static final Log log = LogFactory.getLog( QuerySplitter.class );
+
+ private static final Set BEFORE_CLASS_TOKENS = new HashSet();
+ private static final Set NOT_AFTER_CLASS_TOKENS = new HashSet();
+
+ static {
+ BEFORE_CLASS_TOKENS.add( "from" );
+ BEFORE_CLASS_TOKENS.add( "delete" );
+ BEFORE_CLASS_TOKENS.add( "update" );
+ //beforeClassTokens.add("new"); DEFINITELY DON'T HAVE THIS!!
+ BEFORE_CLASS_TOKENS.add( "," );
+ NOT_AFTER_CLASS_TOKENS.add( "in" );
+ //notAfterClassTokens.add(",");
+ NOT_AFTER_CLASS_TOKENS.add( "from" );
+ NOT_AFTER_CLASS_TOKENS.add( ")" );
+ }
+
+ /**
+ * Private empty constructor.
+ * (or else checkstyle says: 'warning: Utility classes should not have a public or default constructor.')
+ */
+ private QuerySplitter() {
+ }
+
+ /**
+ * Handle Hibernate "implicit" polymorphism, by translating the query string into
+ * several "concrete" queries against mapped classes.
+ */
+ public static String[] concreteQueries(String query, SessionFactoryImplementor factory) throws MappingException {
+
+ //scan the query string for class names appearing in the from clause and replace
+ //with all persistent implementors of the class/interface, returning multiple
+ //query strings (make sure we don't pick up a class in the select clause!)
+
+ //TODO: this is one of the ugliest and most fragile pieces of code in Hibernate....
+
+ String[] tokens = StringHelper.split( StringHelper.WHITESPACE + "(),", query, true );
+ if ( tokens.length == 0 ) return new String[]{query}; // just especially for the trivial collection filter
+ ArrayList placeholders = new ArrayList();
+ ArrayList replacements = new ArrayList();
+ StringBuffer templateQuery = new StringBuffer( 40 );
+ int count = 0;
+ String last = null;
+ int nextIndex = 0;
+ String next = null;
+ boolean isSelectClause = false;
+
+ templateQuery.append( tokens[0] );
+ if ( "select".equals( tokens[0].toLowerCase() ) ) isSelectClause = true;
+
+ for ( int i = 1; i < tokens.length; i++ ) {
+
+ //update last non-whitespace token, if necessary
+ if ( !ParserHelper.isWhitespace( tokens[i - 1] ) ) last = tokens[i - 1].toLowerCase();
+
+ // select-range is terminated by declaration of "from"
+ if ( "from".equals( tokens[i].toLowerCase() ) ) isSelectClause = false;
+
+ String token = tokens[i];
+ if ( !ParserHelper.isWhitespace( token ) || last == null ) {
+
+ //scan for next non-whitespace token
+ if ( nextIndex <= i ) {
+ for ( nextIndex = i + 1; nextIndex < tokens.length; nextIndex++ ) {
+ next = tokens[nextIndex].toLowerCase();
+ if ( !ParserHelper.isWhitespace( next ) ) break;
+ }
+ }
+
+ boolean process = !isSelectClause &&
+ isJavaIdentifier( token ) &&
+ isPossiblyClassName( last, next );
+
+ if (process) {
+ String importedClassName = getImportedClass( token, factory );
+ if ( importedClassName != null ) {
+ String[] implementors = factory.getImplementors( importedClassName );
+ String placeholder = "$clazz" + count++ + "$";
+ if ( implementors != null ) {
+ placeholders.add( placeholder );
+ replacements.add( implementors );
+ }
+ token = placeholder; // Note this!!
+ }
+ }
+
+ }
+
+ templateQuery.append( token );
+
+ }
+ String[] results = StringHelper.multiply( templateQuery.toString(), placeholders.iterator(), replacements.iterator() );
+ if ( results.length == 0 ) log.warn( "no persistent classes found for query class: " + query );
+ return results;
+ }
+
+ private static boolean isPossiblyClassName(String last, String next) {
+ return "class".equals( last ) || (
+ BEFORE_CLASS_TOKENS.contains( last ) &&
+ !NOT_AFTER_CLASS_TOKENS.contains( next )
+ );
+ }
+
+ private static boolean isJavaIdentifier(String token) {
+ return Character.isJavaIdentifierStart( token.charAt( 0 ) );
+ }
+
+ public static String getImportedClass(String name, SessionFactoryImplementor factory) {
+ return factory.getImportedClassName( name );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/QueryTranslator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/QueryTranslator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/QueryTranslator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,167 @@
+//$Id: QueryTranslator.java 9162 2006-01-27 23:40:32Z steveebersole $
+package org.hibernate.hql;
+
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.QueryException;
+import org.hibernate.ScrollableResults;
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.event.EventSource;
+import org.hibernate.type.Type;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Defines the constract of an HQL->SQL translator.
+ *
+ * @author josh
+ */
+public interface QueryTranslator {
+
+ // Error message constants.
+ public static final String ERROR_CANNOT_FETCH_WITH_ITERATE = "fetch may not be used with scroll() or iterate()";
+ public static final String ERROR_NAMED_PARAMETER_DOES_NOT_APPEAR = "Named parameter does not appear in Query: ";
+ public static final String ERROR_CANNOT_DETERMINE_TYPE = "Could not determine type of: ";
+ public static final String ERROR_CANNOT_FORMAT_LITERAL = "Could not format constant value to SQL literal: ";
+
+ /**
+ * Compile a "normal" query. This method may be called multiple
+ * times. Subsequent invocations are no-ops.
+ *
+ * @param replacements Defined query substitutions.
+ * @param shallow Does this represent a shallow (scalar or entity-id) select?
+ * @throws QueryException There was a problem parsing the query string.
+ * @throws MappingException There was a problem querying defined mappings.
+ */
+ void compile(Map replacements, boolean shallow) throws QueryException, MappingException;
+
+ /**
+ * Perform a list operation given the underlying query definition.
+ *
+ * @param session The session owning this query.
+ * @param queryParameters The query bind parameters.
+ * @return The query list results.
+ * @throws HibernateException
+ */
+ List list(SessionImplementor session, QueryParameters queryParameters)
+ throws HibernateException;
+
+ /**
+ * Perform an iterate operation given the underlying query defintion.
+ *
+ * @param queryParameters The query bind parameters.
+ * @param session The session owning this query.
+ * @return An iterator over the query results.
+ * @throws HibernateException
+ */
+ Iterator iterate(QueryParameters queryParameters, EventSource session)
+ throws HibernateException;
+
+ /**
+ * Perform a scroll operation given the underlying query defintion.
+ *
+ * @param queryParameters The query bind parameters.
+ * @param session The session owning this query.
+ * @return The ScrollableResults wrapper around the query results.
+ * @throws HibernateException
+ */
+ ScrollableResults scroll(QueryParameters queryParameters, SessionImplementor session)
+ throws HibernateException;
+
+ /**
+ * Perform a bulk update/delete operation given the underlying query defintion.
+ *
+ * @param queryParameters The query bind parameters.
+ * @param session The session owning this query.
+ * @return The number of entities updated or deleted.
+ * @throws HibernateException
+ */
+ int executeUpdate(QueryParameters queryParameters, SessionImplementor session)
+ throws HibernateException;
+
+ /**
+ * Returns the set of query spaces (table names) that the query referrs to.
+ *
+ * @return A set of query spaces (table names).
+ */
+ Set getQuerySpaces();
+
+ /**
+ * Retrieve the query identifier for this translator. The query identifier is
+ * used in stats collection.
+ *
+ * @return the identifier
+ */
+ String getQueryIdentifier();
+
+ /**
+ * Returns the SQL string generated by the translator.
+ *
+ * @return the SQL string generated by the translator.
+ */
+ String getSQLString();
+
+ List collectSqlStrings();
+
+ /**
+ * Returns the HQL string processed by the translator.
+ *
+ * @return the HQL string processed by the translator.
+ */
+ String getQueryString();
+
+ /**
+ * Returns the filters enabled for this query translator.
+ *
+ * @return Filters enabled for this query execution.
+ */
+ Map getEnabledFilters();
+
+ /**
+ * Returns an array of Types represented in the query result.
+ *
+ * @return Query return types.
+ */
+ Type[] getReturnTypes();
+
+ /**
+ * Returns an array of HQL aliases
+ */
+ String[] getReturnAliases();
+
+ /**
+ * Returns the column names in the generated SQL.
+ *
+ * @return the column names in the generated SQL.
+ */
+ String[][] getColumnNames();
+
+ /**
+ * Return information about any parameters encountered during
+ * translation.
+ *
+ * @return The parameter information.
+ */
+ ParameterTranslations getParameterTranslations();
+
+ /**
+ * Validate the scrollability of the translated query.
+ *
+ * @throws HibernateException
+ */
+ void validateScrollability() throws HibernateException;
+
+ /**
+ * Does the translated query contain collection fetches?
+ *
+ * @return tru if the query does contain collection fetched;
+ * false otherwise.
+ */
+ boolean containsCollectionFetches();
+
+ boolean isManipulationStatement();
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/QueryTranslatorFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/QueryTranslatorFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/QueryTranslatorFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,37 @@
+//$Id: QueryTranslatorFactory.java 9162 2006-01-27 23:40:32Z steveebersole $
+package org.hibernate.hql;
+
+import org.hibernate.engine.SessionFactoryImplementor;
+
+import java.util.Map;
+
+/**
+ * Facade for generation of {@link QueryTranslator} and {@link FilterTranslator} instances.
+ *
+ * @author Gavin King
+ */
+public interface QueryTranslatorFactory {
+ /**
+ * Construct a {@link QueryTranslator} instance capable of translating
+ * an HQL query string.
+ *
+ * @param queryIdentifier The query-identifier (used in
+ * {@link org.hibernate.stat.QueryStatistics} collection). This is
+ * typically the same as the queryString parameter except for the case of
+ * split polymorphic queries which result in multiple physical sql
+ * queries.
+ * @param queryString The query string to be translated
+ * @param filters Currently enabled filters
+ * @param factory The session factory.
+ * @return an appropriate translator.
+ */
+ public QueryTranslator createQueryTranslator(String queryIdentifier, String queryString, Map filters, SessionFactoryImplementor factory);
+
+ /**
+ * Construct a {@link FilterTranslator} instance capable of translating
+ * an HQL filter string.
+ *
+ * @see #createQueryTranslator
+ */
+ public FilterTranslator createFilterTranslator(String queryIdentifier, String queryString, Map filters, SessionFactoryImplementor factory);
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/antlr/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/antlr/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/antlr/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,8 @@
+<html>
+<head></head>
+<body>
+<p>A special package for ANTLR-generated parser classes.</p>
+<p><i>NOTE: The classes in this package are generated from the ANTLR grammar files,
+do not register them into version control.</i></p>
+</body>
+</html>
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/ASTQueryTranslatorFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/ASTQueryTranslatorFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/ASTQueryTranslatorFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,49 @@
+//$Id: ASTQueryTranslatorFactory.java 9162 2006-01-27 23:40:32Z steveebersole $
+package org.hibernate.hql.ast;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.hql.FilterTranslator;
+import org.hibernate.hql.QueryTranslator;
+import org.hibernate.hql.QueryTranslatorFactory;
+
+import java.util.Map;
+
+/**
+ * Generates translators which uses the Antlr-based parser to perform
+ * the translation.
+ *
+ * @author Gavin King
+ */
+public class ASTQueryTranslatorFactory implements QueryTranslatorFactory {
+
+ private static final Log log = LogFactory.getLog( ASTQueryTranslatorFactory.class );
+
+ public ASTQueryTranslatorFactory() {
+ log.info( "Using ASTQueryTranslatorFactory" );
+ }
+
+ /**
+ * @see QueryTranslatorFactory#createQueryTranslator
+ */
+ public QueryTranslator createQueryTranslator(
+ String queryIdentifier,
+ String queryString,
+ Map filters,
+ SessionFactoryImplementor factory) {
+ return new QueryTranslatorImpl( queryIdentifier, queryString, filters, factory );
+ }
+
+ /**
+ * @see QueryTranslatorFactory#createFilterTranslator
+ */
+ public FilterTranslator createFilterTranslator(
+ String queryIdentifier,
+ String queryString,
+ Map filters,
+ SessionFactoryImplementor factory) {
+ return new QueryTranslatorImpl( queryIdentifier, queryString, filters, factory );
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/ASTQueryTranslatorFactory.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/DetailedSemanticException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/DetailedSemanticException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/DetailedSemanticException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,77 @@
+// $Id: DetailedSemanticException.java 5690 2005-02-12 20:27:50Z pgmjsd $
+package org.hibernate.hql.ast;
+
+import antlr.SemanticException;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+
+/**
+ * Thrown when a call to the underlying Hibernate engine fails, indicating
+ * some form of semantic exception (e.g. a class name was not found in the
+ * current mappings, etc.).
+ */
+public class DetailedSemanticException extends SemanticException {
+ private Throwable cause;
+ private boolean showCauseMessage = true;
+
+ public DetailedSemanticException(String message) {
+ super( message );
+ }
+
+ public DetailedSemanticException(String s, Throwable e) {
+ super( s );
+ cause = e;
+ }
+
+ /**
+ * Converts everything to a string.
+ *
+ * @return a string.
+ */
+ public String toString() {
+ if ( cause == null || ( !showCauseMessage ) ) {
+ return super.toString();
+ }
+ else {
+ return super.toString() + "\n[cause=" + cause.toString() + "]";
+ }
+ }
+
+ /**
+ * Prints a stack trace.
+ */
+ public void printStackTrace() {
+ super.printStackTrace();
+ if ( cause != null ) {
+ cause.printStackTrace();
+ }
+ }
+
+ /**
+ * Prints a stack trace to the specified print stream.
+ *
+ * @param s the print stream.
+ */
+ public void printStackTrace(PrintStream s) {
+ super.printStackTrace( s );
+ if ( cause != null ) {
+ s.println( "Cause:" );
+ cause.printStackTrace( s );
+ }
+ }
+
+ /**
+ * Prints this throwable and its backtrace to the specified print writer.
+ *
+ * @param w the print writer.s
+ */
+ public void printStackTrace(PrintWriter w) {
+ super.printStackTrace( w );
+ if ( cause != null ) {
+ w.println( "Cause:" );
+ cause.printStackTrace( w );
+ }
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/ErrorCounter.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/ErrorCounter.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/ErrorCounter.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,72 @@
+// $Id: ErrorCounter.java 9242 2006-02-09 12:37:36Z steveebersole $
+package org.hibernate.hql.ast;
+
+import antlr.RecognitionException;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.QueryException;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * An error handler that counts parsing errors and warnings.
+ */
+public class ErrorCounter implements ParseErrorHandler {
+ private Log log = LogFactory.getLog( ErrorCounter.class );
+ private Log hqlLog = LogFactory.getLog( "org.hibernate.hql.PARSER" );
+
+ private List errorList = new ArrayList();
+ private List warningList = new ArrayList();
+ private List recognitionExceptions = new ArrayList();
+
+ public void reportError(RecognitionException e) {
+ reportError( e.toString() );
+ recognitionExceptions.add( e );
+ if ( log.isDebugEnabled() ) {
+ log.debug( e, e );
+ }
+ }
+
+ public void reportError(String message) {
+ hqlLog.error( message );
+ errorList.add( message );
+ }
+
+ public int getErrorCount() {
+ return errorList.size();
+ }
+
+ public void reportWarning(String message) {
+ hqlLog.debug( message );
+ warningList.add( message );
+ }
+
+ private String getErrorString() {
+ StringBuffer buf = new StringBuffer();
+ for ( Iterator iterator = errorList.iterator(); iterator.hasNext(); ) {
+ buf.append( ( String ) iterator.next() );
+ if ( iterator.hasNext() ) buf.append( "\n" );
+
+ }
+ return buf.toString();
+ }
+
+ public void throwQueryException() throws QueryException {
+ if ( getErrorCount() > 0 ) {
+ if ( recognitionExceptions.size() > 0 ) {
+ throw QuerySyntaxException.convert( ( RecognitionException ) recognitionExceptions.get( 0 ) );
+ }
+ else {
+ throw new QueryException( getErrorString() );
+ }
+ }
+ else {
+ // all clear
+ if ( log.isDebugEnabled() ) {
+ log.debug( "throwQueryException() : no errors" );
+ }
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/ErrorReporter.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/ErrorReporter.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/ErrorReporter.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,17 @@
+// $Id: ErrorReporter.java 3974 2004-06-29 02:40:43Z pgmjsd $
+package org.hibernate.hql.ast;
+
+import antlr.RecognitionException;
+
+/**
+ * Implementations will report or handle errors invoked by an ANTLR base parser.
+ *
+ * @author josh Jun 27, 2004 9:49:55 PM
+ */
+public interface ErrorReporter {
+ void reportError(RecognitionException e);
+
+ void reportError(String s);
+
+ void reportWarning(String s);
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/HqlASTFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/HqlASTFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/HqlASTFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+package org.hibernate.hql.ast;
+
+import antlr.ASTFactory;
+import org.hibernate.hql.ast.tree.Node;
+
+/**
+ * User: Joshua Davis<br>
+ * Date: Sep 23, 2005<br>
+ * Time: 12:30:01 PM<br>
+ */
+public class HqlASTFactory extends ASTFactory {
+
+ /**
+ * Returns the class for a given token type (a.k.a. AST node type).
+ *
+ * @param tokenType The token type.
+ * @return Class - The AST node class to instantiate.
+ */
+ public Class getASTNodeType(int tokenType) {
+ return Node.class;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/HqlLexer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/HqlLexer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/HqlLexer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,59 @@
+// $Id: HqlLexer.java 7511 2005-07-16 17:28:40Z epbernard $
+package org.hibernate.hql.ast;
+
+import java.io.InputStream;
+import java.io.Reader;
+
+import antlr.Token;
+import org.hibernate.QueryException;
+import org.hibernate.hql.antlr.HqlBaseLexer;
+
+/**
+ * Custom lexer for the HQL grammar. Extends the base lexer generated by ANTLR
+ * in order to keep the grammar source file clean.
+ */
+class HqlLexer extends HqlBaseLexer {
+ /**
+ * A logger for this class. *
+ */
+ private boolean possibleID = false;
+
+ public HqlLexer(InputStream in) {
+ super( in );
+ }
+
+ public HqlLexer(Reader in) {
+ super(in);
+ }
+
+ public void setTokenObjectClass(String cl) {
+ // Ignore the token class name parameter, and use a specific token class.
+ super.setTokenObjectClass( HqlToken.class.getName() );
+ }
+
+ protected void setPossibleID(boolean possibleID) {
+ this.possibleID = possibleID;
+ }
+
+ protected Token makeToken(int i) {
+ HqlToken token = ( HqlToken ) super.makeToken( i );
+ token.setPossibleID( possibleID );
+ possibleID = false;
+ return token;
+ }
+
+ public int testLiteralsTable(int i) {
+ int ttype = super.testLiteralsTable( i );
+ return ttype;
+ }
+
+ public void panic() {
+ //overriden to avoid System.exit
+ panic("CharScanner: panic");
+ }
+
+ public void panic(String s) {
+ //overriden to avoid System.exit
+ throw new QueryException(s);
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/HqlParser.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/HqlParser.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/HqlParser.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,338 @@
+// $Id: HqlParser.java 8475 2005-10-27 11:43:19Z oneovthafew $
+package org.hibernate.hql.ast;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringReader;
+
+import antlr.ASTPair;
+import antlr.MismatchedTokenException;
+import antlr.RecognitionException;
+import antlr.Token;
+import antlr.TokenStream;
+import antlr.TokenStreamException;
+import antlr.collections.AST;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.hql.antlr.HqlBaseParser;
+import org.hibernate.hql.antlr.HqlTokenTypes;
+import org.hibernate.hql.ast.util.ASTPrinter;
+import org.hibernate.hql.ast.util.ASTUtil;
+import org.hibernate.QueryException;
+
+/**
+ * Implements the semantic action methods defined in the HQL base parser to keep the grammar
+ * source file a little cleaner. Extends the parser class generated by ANTLR.
+ *
+ * @author Joshua Davis (pgmjsd at sourceforge.net)
+ */
+public final class HqlParser extends HqlBaseParser {
+ /**
+ * A logger for this class.
+ */
+ private static final Log log = LogFactory.getLog( HqlParser.class );
+
+ private ParseErrorHandler parseErrorHandler;
+ private ASTPrinter printer = getASTPrinter();
+
+ private static ASTPrinter getASTPrinter() {
+ return new ASTPrinter( org.hibernate.hql.antlr.HqlTokenTypes.class );
+ }
+
+ public static HqlParser getInstance(String hql) {
+ // [jsd] The fix for HHH-558...
+ HqlLexer lexer = new HqlLexer( new StringReader( hql ) );
+ return new HqlParser( lexer );
+ }
+
+ private HqlParser(TokenStream lexer) {
+ super( lexer );
+ initialize();
+ }
+
+ public void reportError(RecognitionException e) {
+ parseErrorHandler.reportError( e ); // Use the delegate.
+ }
+
+ public void reportError(String s) {
+ parseErrorHandler.reportError( s ); // Use the delegate.
+ }
+
+ public void reportWarning(String s) {
+ parseErrorHandler.reportWarning( s );
+ }
+
+ public ParseErrorHandler getParseErrorHandler() {
+ return parseErrorHandler;
+ }
+
+ /**
+ * Overrides the base behavior to retry keywords as identifiers.
+ *
+ * @param token The token.
+ * @param ex The recognition exception.
+ * @return AST - The new AST.
+ * @throws antlr.RecognitionException if the substitution was not possible.
+ * @throws antlr.TokenStreamException if the substitution was not possible.
+ */
+ public AST handleIdentifierError(Token token, RecognitionException ex) throws RecognitionException, TokenStreamException {
+ // If the token can tell us if it could be an identifier...
+ if ( token instanceof HqlToken ) {
+ HqlToken hqlToken = ( HqlToken ) token;
+ // ... and the token could be an identifer and the error is
+ // a mismatched token error ...
+ if ( hqlToken.isPossibleID() && ( ex instanceof MismatchedTokenException ) ) {
+ MismatchedTokenException mte = ( MismatchedTokenException ) ex;
+ // ... and the expected token type was an identifier, then:
+ if ( mte.expecting == HqlTokenTypes.IDENT ) {
+ // Use the token as an identifier.
+ reportWarning( "Keyword '"
+ + token.getText()
+ + "' is being interpreted as an identifier due to: " + mte.getMessage() );
+ // Add the token to the AST.
+ ASTPair currentAST = new ASTPair();
+ token.setType( HqlTokenTypes.WEIRD_IDENT );
+ astFactory.addASTChild( currentAST, astFactory.create( token ) );
+ consume();
+ AST identifierAST = currentAST.root;
+ return identifierAST;
+ }
+ } // if
+ } // if
+ // Otherwise, handle the error normally.
+ return super.handleIdentifierError( token, ex );
+ }
+
+ /**
+ * Returns an equivalent tree for (NOT (a relop b) ), for example:<pre>
+ * (NOT (GT a b) ) => (LE a b)
+ * </pre>
+ *
+ * @param x The sub tree to transform, the parent is assumed to be NOT.
+ * @return AST - The equivalent sub-tree.
+ */
+ public AST negateNode(AST x) {
+ //TODO: switch statements are always evil! We already had bugs because
+ // of forgotten token types. Use polymorphism for this!
+ switch ( x.getType() ) {
+ case OR:
+ x.setType(AND);
+ x.setText("{and}");
+ negateNode( x.getFirstChild() );
+ negateNode( x.getFirstChild().getNextSibling() );
+ return x;
+ case AND:
+ x.setType(OR);
+ x.setText("{or}");
+ negateNode( x.getFirstChild() );
+ negateNode( x.getFirstChild().getNextSibling() );
+ return x;
+ case EQ:
+ x.setType( NE );
+ x.setText( "{not}" + x.getText() );
+ return x; // (NOT (EQ a b) ) => (NE a b)
+ case NE:
+ x.setType( EQ );
+ x.setText( "{not}" + x.getText() );
+ return x; // (NOT (NE a b) ) => (EQ a b)
+ case GT:
+ x.setType( LE );
+ x.setText( "{not}" + x.getText() );
+ return x; // (NOT (GT a b) ) => (LE a b)
+ case LT:
+ x.setType( GE );
+ x.setText( "{not}" + x.getText() );
+ return x; // (NOT (LT a b) ) => (GE a b)
+ case GE:
+ x.setType( LT );
+ x.setText( "{not}" + x.getText() );
+ return x; // (NOT (GE a b) ) => (LT a b)
+ case LE:
+ x.setType( GT );
+ x.setText( "{not}" + x.getText() );
+ return x; // (NOT (LE a b) ) => (GT a b)
+ case LIKE:
+ x.setType( NOT_LIKE );
+ x.setText( "{not}" + x.getText() );
+ return x; // (NOT (LIKE a b) ) => (NOT_LIKE a b)
+ case NOT_LIKE:
+ x.setType( LIKE );
+ x.setText( "{not}" + x.getText() );
+ return x; // (NOT (NOT_LIKE a b) ) => (LIKE a b)
+ case IN:
+ x.setType( NOT_IN );
+ x.setText( "{not}" + x.getText() );
+ return x;
+ case NOT_IN:
+ x.setType( IN );
+ x.setText( "{not}" + x.getText() );
+ return x;
+ case IS_NULL:
+ x.setType( IS_NOT_NULL );
+ x.setText( "{not}" + x.getText() );
+ return x; // (NOT (IS_NULL a b) ) => (IS_NOT_NULL a b)
+ case IS_NOT_NULL:
+ x.setType( IS_NULL );
+ x.setText( "{not}" + x.getText() );
+ return x; // (NOT (IS_NOT_NULL a b) ) => (IS_NULL a b)
+ case BETWEEN:
+ x.setType( NOT_BETWEEN );
+ x.setText( "{not}" + x.getText() );
+ return x; // (NOT (BETWEEN a b) ) => (NOT_BETWEEN a b)
+ case NOT_BETWEEN:
+ x.setType( BETWEEN );
+ x.setText( "{not}" + x.getText() );
+ return x; // (NOT (NOT_BETWEEN a b) ) => (BETWEEN a b)
+/* This can never happen because this rule will always eliminate the child NOT.
+ case NOT:
+ return x.getFirstChild(); // (NOT (NOT x) ) => (x)
+*/
+ default:
+ return super.negateNode( x ); // Just add a 'not' parent.
+ }
+ }
+
+ /**
+ * Post process equality expressions, clean up the subtree.
+ *
+ * @param x The equality expression.
+ * @return AST - The clean sub-tree.
+ */
+ public AST processEqualityExpression(AST x) {
+ if ( x == null ) {
+ log.warn( "processEqualityExpression() : No expression to process!" );
+ return null;
+ }
+
+ int type = x.getType();
+ if ( type == EQ || type == NE ) {
+ boolean negated = type == NE;
+ if ( x.getNumberOfChildren() == 2 ) {
+ AST a = x.getFirstChild();
+ AST b = a.getNextSibling();
+ // (EQ NULL b) => (IS_NULL b)
+ if ( a.getType() == NULL && b.getType() != NULL ) {
+ return createIsNullParent( b, negated );
+ }
+ // (EQ a NULL) => (IS_NULL a)
+ else if ( b.getType() == NULL && a.getType() != NULL ) {
+ return createIsNullParent( a, negated );
+ }
+ else if ( b.getType() == EMPTY ) {
+ return processIsEmpty( a, negated );
+ }
+ else {
+ return x;
+ }
+ }
+ else {
+ return x;
+ }
+ }
+ else {
+ return x;
+ }
+ }
+
+ private AST createIsNullParent(AST node, boolean negated) {
+ node.setNextSibling( null );
+ int type = negated ? IS_NOT_NULL : IS_NULL;
+ String text = negated ? "is not null" : "is null";
+ return ASTUtil.createParent( astFactory, type, text, node );
+ }
+
+ private AST processIsEmpty(AST node, boolean negated) {
+ node.setNextSibling( null );
+ // NOTE: Because we're using ASTUtil.createParent(), the tree must be created from the bottom up.
+ // IS EMPTY x => (EXISTS (QUERY (SELECT_FROM (FROM x) ) ) )
+ AST ast = createSubquery( node );
+ ast = ASTUtil.createParent( astFactory, EXISTS, "exists", ast );
+ // Add NOT if it's negated.
+ if ( !negated ) {
+ ast = ASTUtil.createParent( astFactory, NOT, "not", ast );
+ }
+ return ast;
+ }
+
+ private AST createSubquery(AST node) {
+ AST ast = ASTUtil.createParent( astFactory, RANGE, "RANGE", node );
+ ast = ASTUtil.createParent( astFactory, FROM, "from", ast );
+ ast = ASTUtil.createParent( astFactory, SELECT_FROM, "SELECT_FROM", ast );
+ ast = ASTUtil.createParent( astFactory, QUERY, "QUERY", ast );
+ return ast;
+ }
+
+ public void showAst(AST ast, PrintStream out) {
+ showAst( ast, new PrintWriter( out ) );
+ }
+
+ private void showAst(AST ast, PrintWriter pw) {
+ printer.showAst( ast, pw );
+ }
+
+ private void initialize() {
+ // Initialize the error handling delegate.
+ parseErrorHandler = new ErrorCounter();
+ setASTFactory(new HqlASTFactory()); // Create nodes that track line and column number.
+ }
+
+ public void weakKeywords() throws TokenStreamException {
+
+ int t = LA( 1 );
+ switch ( t ) {
+ case ORDER:
+ case GROUP:
+ // Case 1: Multi token keywords GROUP BY and ORDER BY
+ // The next token ( LT(2) ) should be 'by'... otherwise, this is just an ident.
+ if ( LA( 2 ) != LITERAL_by ) {
+ LT( 1 ).setType( IDENT );
+ if ( log.isDebugEnabled() ) {
+ log.debug( "weakKeywords() : new LT(1) token - " + LT( 1 ) );
+ }
+ }
+ break;
+ default:
+ // Case 2: The current token is after FROM and before '.'.
+ if (LA(0) == FROM && t != IDENT && LA(2) == DOT) {
+ HqlToken hqlToken = (HqlToken)LT(1);
+ if (hqlToken.isPossibleID()) {
+ hqlToken.setType(IDENT);
+ if ( log.isDebugEnabled() ) {
+ log.debug( "weakKeywords() : new LT(1) token - " + LT( 1 ) );
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ public void handleDotIdent() throws TokenStreamException {
+ // This handles HHH-354, where there is a strange property name in a where clause.
+ // If the lookahead contains a DOT then something that isn't an IDENT...
+ if (LA(1) == DOT && LA(2) != IDENT) {
+ // See if the second lookahed token can be an identifier.
+ HqlToken t = (HqlToken)LT(2);
+ if (t.isPossibleID())
+ {
+ // Set it!
+ LT( 2 ).setType( IDENT );
+ if ( log.isDebugEnabled() ) {
+ log.debug( "handleDotIdent() : new LT(2) token - " + LT( 1 ) );
+ }
+ }
+ }
+ }
+
+ public void processMemberOf(Token n, AST p, ASTPair currentAST) {
+ AST inAst = n == null ? astFactory.create( IN, "in" ) : astFactory.create( NOT_IN, "not in" );
+ astFactory.makeASTRoot( currentAST, inAst );
+ AST ast = createSubquery( p );
+ ast = ASTUtil.createParent( astFactory, IN_LIST, "inList", ast );
+ inAst.addChild( ast );
+ }
+
+ static public void panic() {
+ //overriden to avoid System.exit
+ throw new QueryException("Parser: panic");
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/HqlSqlWalker.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/HqlSqlWalker.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/HqlSqlWalker.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1016 @@
+// $Id: HqlSqlWalker.java 10945 2006-12-07 14:50:42Z steve.ebersole at jboss.com $
+package org.hibernate.hql.ast;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.QueryException;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.JoinSequence;
+import org.hibernate.engine.ParameterBinder;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.hql.QueryTranslator;
+import org.hibernate.hql.antlr.HqlSqlBaseWalker;
+import org.hibernate.hql.antlr.HqlSqlTokenTypes;
+import org.hibernate.hql.antlr.HqlTokenTypes;
+import org.hibernate.hql.antlr.SqlTokenTypes;
+import org.hibernate.hql.ast.tree.AssignmentSpecification;
+import org.hibernate.hql.ast.tree.CollectionFunction;
+import org.hibernate.hql.ast.tree.ConstructorNode;
+import org.hibernate.hql.ast.tree.DeleteStatement;
+import org.hibernate.hql.ast.tree.DotNode;
+import org.hibernate.hql.ast.tree.FromClause;
+import org.hibernate.hql.ast.tree.FromElement;
+import org.hibernate.hql.ast.tree.FromReferenceNode;
+import org.hibernate.hql.ast.tree.IdentNode;
+import org.hibernate.hql.ast.tree.IndexNode;
+import org.hibernate.hql.ast.tree.InsertStatement;
+import org.hibernate.hql.ast.tree.IntoClause;
+import org.hibernate.hql.ast.tree.MethodNode;
+import org.hibernate.hql.ast.tree.ParameterNode;
+import org.hibernate.hql.ast.tree.QueryNode;
+import org.hibernate.hql.ast.tree.ResolvableNode;
+import org.hibernate.hql.ast.tree.RestrictableStatement;
+import org.hibernate.hql.ast.tree.SelectClause;
+import org.hibernate.hql.ast.tree.SelectExpression;
+import org.hibernate.hql.ast.tree.UpdateStatement;
+import org.hibernate.hql.ast.tree.Node;
+import org.hibernate.hql.ast.tree.OperatorNode;
+import org.hibernate.hql.ast.util.ASTPrinter;
+import org.hibernate.hql.ast.util.ASTUtil;
+import org.hibernate.hql.ast.util.AliasGenerator;
+import org.hibernate.hql.ast.util.JoinProcessor;
+import org.hibernate.hql.ast.util.LiteralProcessor;
+import org.hibernate.hql.ast.util.SessionFactoryHelper;
+import org.hibernate.hql.ast.util.SyntheticAndFactory;
+import org.hibernate.hql.ast.util.NodeTraverser;
+import org.hibernate.id.IdentifierGenerator;
+import org.hibernate.id.PostInsertIdentifierGenerator;
+import org.hibernate.id.SequenceGenerator;
+import org.hibernate.param.NamedParameterSpecification;
+import org.hibernate.param.ParameterSpecification;
+import org.hibernate.param.PositionalParameterSpecification;
+import org.hibernate.param.VersionTypeSeedParameterSpecification;
+import org.hibernate.param.CollectionFilterKeyParameterSpecification;
+import org.hibernate.persister.collection.QueryableCollection;
+import org.hibernate.persister.entity.Queryable;
+import org.hibernate.sql.JoinFragment;
+import org.hibernate.type.AssociationType;
+import org.hibernate.type.Type;
+import org.hibernate.type.VersionType;
+import org.hibernate.type.DbTimestampType;
+import org.hibernate.usertype.UserVersionType;
+import org.hibernate.util.ArrayHelper;
+
+import antlr.ASTFactory;
+import antlr.RecognitionException;
+import antlr.SemanticException;
+import antlr.collections.AST;
+
+/**
+ * Implements methods used by the HQL->SQL tree transform grammar (a.k.a. the second phase).
+ * <ul>
+ * <li>Isolates the Hibernate API-specific code from the ANTLR generated code.</li>
+ * <li>Handles the SQL framgents generated by the persisters in order to create the SELECT and FROM clauses,
+ * taking into account the joins and projections that are implied by the mappings (persister/queryable).</li>
+ * <li>Uses SqlASTFactory to create customized AST nodes.</li>
+ * </ul>
+ *
+ * @see SqlASTFactory
+ */
+public class HqlSqlWalker extends HqlSqlBaseWalker implements ErrorReporter, ParameterBinder.NamedParameterSource {
+ private static final Log log = LogFactory.getLog( HqlSqlWalker.class );
+
+ private final QueryTranslatorImpl queryTranslatorImpl;
+ private final HqlParser hqlParser;
+ private final SessionFactoryHelper sessionFactoryHelper;
+ private final Map tokenReplacements;
+ private final AliasGenerator aliasGenerator = new AliasGenerator();
+ private final LiteralProcessor literalProcessor;
+ private final ParseErrorHandler parseErrorHandler;
+ private final ASTPrinter printer;
+ private final String collectionFilterRole;
+
+ private FromClause currentFromClause = null;
+ private SelectClause selectClause;
+
+ private Set querySpaces = new HashSet();
+
+ private int parameterCount;
+ private Map namedParameters = new HashMap();
+ private ArrayList parameters = new ArrayList();
+ private int numberOfParametersInSetClause;
+ private int positionalParameterCount;
+
+ private ArrayList assignmentSpecifications = new ArrayList();
+
+ private int impliedJoinType;
+
+ /**
+ * Create a new tree transformer.
+ *
+ * @param qti Back pointer to the query translator implementation that is using this tree transform.
+ * @param sfi The session factory implementor where the Hibernate mappings can be found.
+ * @param parser A reference to the phase-1 parser
+ * @param tokenReplacements Registers the token replacement map with the walker. This map will
+ * be used to substitute function names and constants.
+ * @param collectionRole The collection role name of the collection used as the basis for the
+ * filter, NULL if this is not a collection filter compilation.
+ */
+ public HqlSqlWalker(
+ QueryTranslatorImpl qti,
+ SessionFactoryImplementor sfi,
+ HqlParser parser,
+ Map tokenReplacements,
+ String collectionRole) {
+ setASTFactory( new SqlASTFactory( this ) );
+ // Initialize the error handling delegate.
+ this.parseErrorHandler = new ErrorCounter();
+ this.queryTranslatorImpl = qti;
+ this.sessionFactoryHelper = new SessionFactoryHelper( sfi );
+ this.literalProcessor = new LiteralProcessor( this );
+ this.tokenReplacements = tokenReplacements;
+ this.collectionFilterRole = collectionRole;
+ this.hqlParser = parser;
+ this.printer = new ASTPrinter( SqlTokenTypes.class );
+ }
+
+
+ protected void prepareFromClauseInputTree(AST fromClauseInput) {
+ if ( !isSubQuery() ) {
+// // inject param specifications to account for dynamic filter param values
+// if ( ! getEnabledFilters().isEmpty() ) {
+// Iterator filterItr = getEnabledFilters().values().iterator();
+// while ( filterItr.hasNext() ) {
+// FilterImpl filter = ( FilterImpl ) filterItr.next();
+// if ( ! filter.getFilterDefinition().getParameterNames().isEmpty() ) {
+// Iterator paramItr = filter.getFilterDefinition().getParameterNames().iterator();
+// while ( paramItr.hasNext() ) {
+// String parameterName = ( String ) paramItr.next();
+// // currently param filters *only* work with single-column parameter types;
+// // if that limitation is ever lifted, this logic will need to change to account for that
+// ParameterNode collectionFilterKeyParameter = ( ParameterNode ) astFactory.create( PARAM, "?" );
+// DynamicFilterParameterSpecification paramSpec = new DynamicFilterParameterSpecification(
+// filter.getName(),
+// parameterName,
+// filter.getFilterDefinition().getParameterType( parameterName ),
+// positionalParameterCount++
+// );
+// collectionFilterKeyParameter.setHqlParameterSpecification( paramSpec );
+// parameters.add( paramSpec );
+// }
+// }
+// }
+// }
+
+ if ( isFilter() ) {
+ // Handle collection-fiter compilation.
+ // IMPORTANT NOTE: This is modifying the INPUT (HQL) tree, not the output tree!
+ QueryableCollection persister = sessionFactoryHelper.getCollectionPersister( collectionFilterRole );
+ Type collectionElementType = persister.getElementType();
+ if ( !collectionElementType.isEntityType() ) {
+ throw new QueryException( "collection of values in filter: this" );
+ }
+
+ String collectionElementEntityName = persister.getElementPersister().getEntityName();
+ ASTFactory inputAstFactory = hqlParser.getASTFactory();
+ AST fromElement = ASTUtil.create( inputAstFactory, HqlTokenTypes.FILTER_ENTITY, collectionElementEntityName );
+ ASTUtil.createSibling( inputAstFactory, HqlTokenTypes.ALIAS, "this", fromElement );
+ fromClauseInput.addChild( fromElement );
+ // Show the modified AST.
+ if ( log.isDebugEnabled() ) {
+ log.debug( "prepareFromClauseInputTree() : Filter - Added 'this' as a from element..." );
+ }
+ queryTranslatorImpl.showHqlAst( hqlParser.getAST() );
+
+ // Create a parameter specification for the collection filter...
+ Type collectionFilterKeyType = sessionFactoryHelper.requireQueryableCollection( collectionFilterRole ).getKeyType();
+ ParameterNode collectionFilterKeyParameter = ( ParameterNode ) astFactory.create( PARAM, "?" );
+ CollectionFilterKeyParameterSpecification collectionFilterKeyParameterSpec = new CollectionFilterKeyParameterSpecification(
+ collectionFilterRole, collectionFilterKeyType, positionalParameterCount++
+ );
+ collectionFilterKeyParameter.setHqlParameterSpecification( collectionFilterKeyParameterSpec );
+ parameters.add( collectionFilterKeyParameterSpec );
+ }
+ }
+ }
+
+ public boolean isFilter() {
+ return collectionFilterRole != null;
+ }
+
+ public SessionFactoryHelper getSessionFactoryHelper() {
+ return sessionFactoryHelper;
+ }
+
+ public Map getTokenReplacements() {
+ return tokenReplacements;
+ }
+
+ public AliasGenerator getAliasGenerator() {
+ return aliasGenerator;
+ }
+
+ public FromClause getCurrentFromClause() {
+ return currentFromClause;
+ }
+
+ public ParseErrorHandler getParseErrorHandler() {
+ return parseErrorHandler;
+ }
+
+ public void reportError(RecognitionException e) {
+ parseErrorHandler.reportError( e ); // Use the delegate.
+ }
+
+ public void reportError(String s) {
+ parseErrorHandler.reportError( s ); // Use the delegate.
+ }
+
+ public void reportWarning(String s) {
+ parseErrorHandler.reportWarning( s );
+ }
+
+ /**
+ * Returns the set of unique query spaces (a.k.a.
+ * table names) that occurred in the query.
+ *
+ * @return A set of table names (Strings).
+ */
+ public Set getQuerySpaces() {
+ return querySpaces;
+ }
+
+ protected AST createFromElement(String path, AST alias, AST propertyFetch) throws SemanticException {
+ FromElement fromElement = currentFromClause.addFromElement( path, alias );
+ fromElement.setAllPropertyFetch(propertyFetch!=null);
+ return fromElement;
+ }
+
+ protected AST createFromFilterElement(AST filterEntity, AST alias) throws SemanticException {
+ FromElement fromElement = currentFromClause.addFromElement( filterEntity.getText(), alias );
+ FromClause fromClause = fromElement.getFromClause();
+ QueryableCollection persister = sessionFactoryHelper.getCollectionPersister( collectionFilterRole );
+ // Get the names of the columns used to link between the collection
+ // owner and the collection elements.
+ String[] keyColumnNames = persister.getKeyColumnNames();
+ String fkTableAlias = persister.isOneToMany()
+ ? fromElement.getTableAlias()
+ : fromClause.getAliasGenerator().createName( collectionFilterRole );
+ JoinSequence join = sessionFactoryHelper.createJoinSequence();
+ join.setRoot( persister, fkTableAlias );
+ if ( !persister.isOneToMany() ) {
+ join.addJoin( ( AssociationType ) persister.getElementType(),
+ fromElement.getTableAlias(),
+ JoinFragment.INNER_JOIN,
+ persister.getElementColumnNames( fkTableAlias ) );
+ }
+ join.addCondition( fkTableAlias, keyColumnNames, " = ?" );
+ fromElement.setJoinSequence( join );
+ fromElement.setFilter( true );
+ if ( log.isDebugEnabled() ) {
+ log.debug( "createFromFilterElement() : processed filter FROM element." );
+ }
+ return fromElement;
+ }
+
+ protected void createFromJoinElement(
+ AST path,
+ AST alias,
+ int joinType,
+ AST fetchNode,
+ AST propertyFetch,
+ AST with) throws SemanticException {
+ boolean fetch = fetchNode != null;
+ if ( fetch && isSubQuery() ) {
+ throw new QueryException( "fetch not allowed in subquery from-elements" );
+ }
+ // The path AST should be a DotNode, and it should have been evaluated already.
+ if ( path.getType() != SqlTokenTypes.DOT ) {
+ throw new SemanticException( "Path expected for join!" );
+ }
+ DotNode dot = ( DotNode ) path;
+ int hibernateJoinType = JoinProcessor.toHibernateJoinType( joinType );
+ dot.setJoinType( hibernateJoinType ); // Tell the dot node about the join type.
+ dot.setFetch( fetch );
+ // Generate an explicit join for the root dot node. The implied joins will be collected and passed up
+ // to the root dot node.
+ dot.resolve( true, false, alias == null ? null : alias.getText() );
+ FromElement fromElement = dot.getImpliedJoin();
+ fromElement.setAllPropertyFetch(propertyFetch!=null);
+
+ if ( with != null ) {
+ if ( fetch ) {
+ throw new SemanticException( "with-clause not allowed on fetched associations; use filters" );
+ }
+ handleWithFragment( fromElement, with );
+ }
+
+ if ( log.isDebugEnabled() ) {
+ log.debug( "createFromJoinElement() : " + getASTPrinter().showAsString( fromElement, "-- join tree --" ) );
+ }
+ }
+
+ private void handleWithFragment(FromElement fromElement, AST hqlWithNode) throws SemanticException
+ {
+ try {
+ withClause( hqlWithNode );
+ AST hqlSqlWithNode = returnAST;
+ if ( log.isDebugEnabled() ) {
+ log.debug( "handleWithFragment() : " + getASTPrinter().showAsString( hqlSqlWithNode, "-- with clause --" ) );
+ }
+ WithClauseVisitor visitor = new WithClauseVisitor();
+ NodeTraverser traverser = new NodeTraverser( visitor );
+ traverser.traverseDepthFirst( hqlSqlWithNode );
+ FromElement referencedFromElement = visitor.getReferencedFromElement();
+ if ( referencedFromElement != fromElement ) {
+ throw new InvalidWithClauseException( "with-clause expressions did not reference from-clause element to which the with-clause was associated" );
+ }
+ SqlGenerator sql = new SqlGenerator( getSessionFactoryHelper().getFactory() );
+ sql.whereExpr( hqlSqlWithNode.getFirstChild() );
+ fromElement.setWithClauseFragment( visitor.getJoinAlias(), "(" + sql.getSQL() + ")" );
+
+ }
+ catch( SemanticException e ) {
+ throw e;
+ }
+ catch( InvalidWithClauseException e ) {
+ throw e;
+ }
+ catch ( Exception e) {
+ throw new SemanticException( e.getMessage() );
+ }
+ }
+
+ private static class WithClauseVisitor implements NodeTraverser.VisitationStrategy {
+ private FromElement referencedFromElement;
+ private String joinAlias;
+
+ public void visit(AST node) {
+ // todo : currently expects that the individual with expressions apply to the same sql table join.
+ // This may not be the case for joined-subclass where the property values
+ // might be coming from different tables in the joined hierarchy. At some
+ // point we should expand this to support that capability. However, that has
+ // some difficulties:
+ // 1) the biggest is how to handle ORs when the individual comparisons are
+ // linked to different sql joins.
+ // 2) here we would need to track each comparison individually, along with
+ // the join alias to which it applies and then pass that information
+ // back to the FromElement so it can pass it along to the JoinSequence
+
+ if ( node instanceof DotNode ) {
+ DotNode dotNode = ( DotNode ) node;
+ FromElement fromElement = dotNode.getFromElement();
+ if ( referencedFromElement != null ) {
+ if ( fromElement != referencedFromElement ) {
+ throw new HibernateException( "with-clause referenced two different from-clause elements" );
+ }
+ }
+ else {
+ referencedFromElement = fromElement;
+ joinAlias = extractAppliedAlias( dotNode );
+ // todo : temporary
+ // needed because currently persister is the one that
+ // creates and renders the join fragments for inheritence
+ // hierarchies...
+ if ( !joinAlias.equals( referencedFromElement.getTableAlias() ) ) {
+ throw new InvalidWithClauseException( "with clause can only reference columns in the driving table" );
+ }
+ }
+ }
+ }
+
+ private String extractAppliedAlias(DotNode dotNode) {
+ return dotNode.getText().substring( 0, dotNode.getText().indexOf( '.' ) );
+ }
+
+ public FromElement getReferencedFromElement() {
+ return referencedFromElement;
+ }
+
+ public String getJoinAlias() {
+ return joinAlias;
+ }
+ }
+
+ /**
+ * Sets the current 'FROM' context.
+ *
+ * @param fromNode The new 'FROM' context.
+ * @param inputFromNode The from node from the input AST.
+ */
+ protected void pushFromClause(AST fromNode, AST inputFromNode) {
+ FromClause newFromClause = ( FromClause ) fromNode;
+ newFromClause.setParentFromClause( currentFromClause );
+ currentFromClause = newFromClause;
+ }
+
+ /**
+ * Returns to the previous 'FROM' context.
+ */
+ private void popFromClause() {
+ currentFromClause = currentFromClause.getParentFromClause();
+ }
+
+ protected void lookupAlias(AST aliasRef)
+ throws SemanticException {
+ FromElement alias = currentFromClause.getFromElement( aliasRef.getText() );
+ FromReferenceNode aliasRefNode = ( FromReferenceNode ) aliasRef;
+ aliasRefNode.setFromElement( alias );
+ }
+
+ protected void setImpliedJoinType(int joinType) {
+ impliedJoinType = JoinProcessor.toHibernateJoinType( joinType );
+ }
+
+ public int getImpliedJoinType() {
+ return impliedJoinType;
+ }
+
+ protected AST lookupProperty(AST dot, boolean root, boolean inSelect) throws SemanticException {
+ DotNode dotNode = ( DotNode ) dot;
+ FromReferenceNode lhs = dotNode.getLhs();
+ AST rhs = lhs.getNextSibling();
+ switch ( rhs.getType() ) {
+ case SqlTokenTypes.ELEMENTS:
+ case SqlTokenTypes.INDICES:
+ if ( log.isDebugEnabled() ) {
+ log.debug( "lookupProperty() " + dotNode.getPath() + " => " + rhs.getText() + "(" + lhs.getPath() + ")" );
+ }
+ CollectionFunction f = ( CollectionFunction ) rhs;
+ // Re-arrange the tree so that the collection function is the root and the lhs is the path.
+ f.setFirstChild( lhs );
+ lhs.setNextSibling( null );
+ dotNode.setFirstChild( f );
+ resolve( lhs ); // Don't forget to resolve the argument!
+ f.resolve( inSelect ); // Resolve the collection function now.
+ return f;
+ default:
+ // Resolve everything up to this dot, but don't resolve the placeholders yet.
+ dotNode.resolveFirstChild();
+ return dotNode;
+ }
+ }
+
+ protected boolean isNonQualifiedPropertyRef(AST ident) {
+ final String identText = ident.getText();
+ if ( currentFromClause.isFromElementAlias( identText ) ) {
+ return false;
+ }
+
+ List fromElements = currentFromClause.getExplicitFromElements();
+ if ( fromElements.size() == 1 ) {
+ final FromElement fromElement = ( FromElement ) fromElements.get( 0 );
+ try {
+ log.trace( "attempting to resolve property [" + identText + "] as a non-qualified ref" );
+ return fromElement.getPropertyMapping( identText ).toType( identText ) != null;
+ }
+ catch( QueryException e ) {
+ // Should mean that no such property was found
+ }
+ }
+
+ return false;
+ }
+
+ protected AST lookupNonQualifiedProperty(AST property) throws SemanticException {
+ final FromElement fromElement = ( FromElement ) currentFromClause.getExplicitFromElements().get( 0 );
+ AST syntheticDotNode = generateSyntheticDotNodeForNonQualifiedPropertyRef( property, fromElement );
+ return lookupProperty( syntheticDotNode, false, getCurrentClauseType() == HqlSqlTokenTypes.SELECT );
+ }
+
+ private AST generateSyntheticDotNodeForNonQualifiedPropertyRef(AST property, FromElement fromElement) {
+ AST dot = getASTFactory().create( DOT, "{non-qualified-property-ref}" );
+ // TODO : better way?!?
+ ( ( DotNode ) dot ).setPropertyPath( ( ( FromReferenceNode ) property ).getPath() );
+
+ IdentNode syntheticAlias = ( IdentNode ) getASTFactory().create( IDENT, "{synthetic-alias}" );
+ syntheticAlias.setFromElement( fromElement );
+ syntheticAlias.setResolved();
+
+ dot.setFirstChild( syntheticAlias );
+ dot.addChild( property );
+
+ return dot;
+ }
+
+ protected void processQuery(AST select, AST query) throws SemanticException {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "processQuery() : " + query.toStringTree() );
+ }
+
+ try {
+ QueryNode qn = ( QueryNode ) query;
+
+ // Was there an explicit select expression?
+ boolean explicitSelect = select != null && select.getNumberOfChildren() > 0;
+
+ if ( !explicitSelect ) {
+ // No explicit select expression; render the id and properties
+ // projection lists for every persister in the from clause into
+ // a single 'token node'.
+ //TODO: the only reason we need this stuff now is collection filters,
+ // we should get rid of derived select clause completely!
+ createSelectClauseFromFromClause( qn );
+ }
+ else {
+ // Use the explicitly declared select expression; determine the
+ // return types indicated by each select token
+ useSelectClause( select );
+ }
+
+ // After that, process the JOINs.
+ // Invoke a delegate to do the work, as this is farily complex.
+ JoinProcessor joinProcessor = new JoinProcessor( astFactory, queryTranslatorImpl );
+ joinProcessor.processJoins( qn, isSubQuery() );
+
+ // Attach any mapping-defined "ORDER BY" fragments
+ Iterator itr = qn.getFromClause().getProjectionList().iterator();
+ while ( itr.hasNext() ) {
+ final FromElement fromElement = ( FromElement ) itr.next();
+// if ( fromElement.isFetch() && fromElement.isCollectionJoin() ) {
+ if ( fromElement.isFetch() && fromElement.getQueryableCollection() != null ) {
+ // Does the collection referenced by this FromElement
+ // specify an order-by attribute? If so, attach it to
+ // the query's order-by
+ if ( fromElement.getQueryableCollection().hasOrdering() ) {
+ String orderByFragment = fromElement
+ .getQueryableCollection()
+ .getSQLOrderByString( fromElement.getCollectionTableAlias() );
+ qn.getOrderByClause().addOrderFragment( orderByFragment );
+ }
+ if ( fromElement.getQueryableCollection().hasManyToManyOrdering() ) {
+ String orderByFragment = fromElement.getQueryableCollection()
+ .getManyToManyOrderByString( fromElement.getTableAlias() );
+ qn.getOrderByClause().addOrderFragment( orderByFragment );
+ }
+ }
+ }
+ }
+ finally {
+ popFromClause();
+ }
+ }
+
+ protected void postProcessDML(RestrictableStatement statement) throws SemanticException {
+ statement.getFromClause().resolve();
+
+ FromElement fromElement = ( FromElement ) statement.getFromClause().getFromElements().get( 0 );
+ Queryable persister = fromElement.getQueryable();
+ // Make #@%$^#^&# sure no alias is applied to the table name
+ fromElement.setText( persister.getTableName() );
+
+ // append any filter fragments; the EMPTY_MAP is used under the assumption that
+ // currently enabled filters should not affect this process
+ if ( persister.getDiscriminatorType() != null ) {
+ new SyntheticAndFactory( getASTFactory() ).addDiscriminatorWhereFragment(
+ statement,
+ persister,
+ java.util.Collections.EMPTY_MAP,
+ fromElement.getTableAlias()
+ );
+ }
+
+ }
+
+ protected void postProcessUpdate(AST update) throws SemanticException {
+ UpdateStatement updateStatement = ( UpdateStatement ) update;
+
+ postProcessDML( updateStatement );
+ }
+
+ protected void postProcessDelete(AST delete) throws SemanticException {
+ postProcessDML( ( DeleteStatement ) delete );
+ }
+
+ public static boolean supportsIdGenWithBulkInsertion(IdentifierGenerator generator) {
+ return SequenceGenerator.class.isAssignableFrom( generator.getClass() )
+ || PostInsertIdentifierGenerator.class.isAssignableFrom( generator.getClass() );
+ }
+
+ protected void postProcessInsert(AST insert) throws SemanticException, QueryException {
+ InsertStatement insertStatement = ( InsertStatement ) insert;
+ insertStatement.validate();
+
+ SelectClause selectClause = insertStatement.getSelectClause();
+ Queryable persister = insertStatement.getIntoClause().getQueryable();
+
+ if ( !insertStatement.getIntoClause().isExplicitIdInsertion() ) {
+ // We need to generate ids as part of this bulk insert.
+ //
+ // Note that this is only supported for sequence-style generators and
+ // post-insert-style generators; basically, only in-db generators
+ IdentifierGenerator generator = persister.getIdentifierGenerator();
+ if ( !supportsIdGenWithBulkInsertion( generator ) ) {
+ throw new QueryException( "can only generate ids as part of bulk insert with either sequence or post-insert style generators" );
+ }
+
+ AST idSelectExprNode = null;
+
+ if ( SequenceGenerator.class.isAssignableFrom( generator.getClass() ) ) {
+ String seqName = ( String ) ( ( SequenceGenerator ) generator ).generatorKey();
+ String nextval = sessionFactoryHelper.getFactory().getDialect().getSelectSequenceNextValString( seqName );
+ idSelectExprNode = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, nextval );
+ }
+ else {
+ //Don't need this, because we should never ever be selecting no columns in an insert ... select...
+ //and because it causes a bug on DB2
+ /*String idInsertString = sessionFactoryHelper.getFactory().getDialect().getIdentityInsertString();
+ if ( idInsertString != null ) {
+ idSelectExprNode = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, idInsertString );
+ }*/
+ }
+
+ if ( idSelectExprNode != null ) {
+ AST currentFirstSelectExprNode = selectClause.getFirstChild();
+ selectClause.setFirstChild( idSelectExprNode );
+ idSelectExprNode.setNextSibling( currentFirstSelectExprNode );
+
+ insertStatement.getIntoClause().prependIdColumnSpec();
+ }
+ }
+
+ final boolean includeVersionProperty = persister.isVersioned() &&
+ !insertStatement.getIntoClause().isExplicitVersionInsertion() &&
+ persister.isVersionPropertyInsertable();
+ if ( includeVersionProperty ) {
+ // We need to seed the version value as part of this bulk insert
+ VersionType versionType = persister.getVersionType();
+ AST versionValueNode = null;
+
+ if ( sessionFactoryHelper.getFactory().getDialect().supportsParametersInInsertSelect() ) {
+ versionValueNode = getASTFactory().create( HqlSqlTokenTypes.PARAM, "?" );
+ ParameterSpecification paramSpec = new VersionTypeSeedParameterSpecification( versionType );
+ ( ( ParameterNode ) versionValueNode ).setHqlParameterSpecification( paramSpec );
+ parameters.add( 0, paramSpec );
+ }
+ else {
+ if ( isIntegral( versionType ) ) {
+ try {
+ Object seedValue = versionType.seed( null );
+ versionValueNode = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, seedValue.toString() );
+ }
+ catch( Throwable t ) {
+ throw new QueryException( "could not determine seed value for version on bulk insert [" + versionType + "]" );
+ }
+ }
+ else if ( isDatabaseGeneratedTimestamp( versionType ) ) {
+ String functionName = sessionFactoryHelper.getFactory().getDialect().getCurrentTimestampSQLFunctionName();
+ versionValueNode = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, functionName );
+ }
+ else {
+ throw new QueryException( "cannot handle version type [" + versionType + "] on bulk inserts with dialects not supporting parameters in insert-select statements" );
+ }
+ }
+
+ AST currentFirstSelectExprNode = selectClause.getFirstChild();
+ selectClause.setFirstChild( versionValueNode );
+ versionValueNode.setNextSibling( currentFirstSelectExprNode );
+
+ insertStatement.getIntoClause().prependVersionColumnSpec();
+ }
+
+ if ( insertStatement.getIntoClause().isDiscriminated() ) {
+ String sqlValue = insertStatement.getIntoClause().getQueryable().getDiscriminatorSQLValue();
+ AST discrimValue = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, sqlValue );
+ insertStatement.getSelectClause().addChild( discrimValue );
+ }
+
+ }
+
+ private boolean isDatabaseGeneratedTimestamp(Type type) {
+ // currently only the Hibernate-supplied DbTimestampType is supported here
+ return DbTimestampType.class.isAssignableFrom( type.getClass() );
+ }
+
+ private boolean isIntegral(Type type) {
+ return Long.class.isAssignableFrom( type.getReturnedClass() )
+ || Integer.class.isAssignableFrom( type.getReturnedClass() )
+ || long.class.isAssignableFrom( type.getReturnedClass() )
+ || int.class.isAssignableFrom( type.getReturnedClass() );
+ }
+
+ private void useSelectClause(AST select) throws SemanticException {
+ selectClause = ( SelectClause ) select;
+ selectClause.initializeExplicitSelectClause( currentFromClause );
+ }
+
+ private void createSelectClauseFromFromClause(QueryNode qn) throws SemanticException {
+ AST select = astFactory.create( SELECT_CLAUSE, "{derived select clause}" );
+ AST sibling = qn.getFromClause();
+ qn.setFirstChild( select );
+ select.setNextSibling( sibling );
+ selectClause = ( SelectClause ) select;
+ selectClause.initializeDerivedSelectClause( currentFromClause );
+ if ( log.isDebugEnabled() ) {
+ log.debug( "Derived SELECT clause created." );
+ }
+ }
+
+ protected void resolve(AST node) throws SemanticException {
+ if ( node != null ) {
+ // This is called when it's time to fully resolve a path expression.
+ ResolvableNode r = ( ResolvableNode ) node;
+ if ( isInFunctionCall() ) {
+ r.resolveInFunctionCall( false, true );
+ }
+ else {
+ r.resolve( false, true ); // Generate implicit joins, only if necessary.
+ }
+ }
+ }
+
+ protected void resolveSelectExpression(AST node) throws SemanticException {
+ // This is called when it's time to fully resolve a path expression.
+ int type = node.getType();
+ switch ( type ) {
+ case DOT:
+ DotNode dot = ( DotNode ) node;
+ dot.resolveSelectExpression();
+ break;
+ case ALIAS_REF:
+ // Notify the FROM element that it is being referenced by the select.
+ FromReferenceNode aliasRefNode = ( FromReferenceNode ) node;
+ //aliasRefNode.resolve( false, false, aliasRefNode.getText() ); //TODO: is it kosher to do it here?
+ aliasRefNode.resolve( false, false ); //TODO: is it kosher to do it here?
+ FromElement fromElement = aliasRefNode.getFromElement();
+ if ( fromElement != null ) {
+ fromElement.setIncludeSubclasses( true );
+ }
+ default:
+ break;
+ }
+ }
+
+ protected void beforeSelectClause() throws SemanticException {
+ // Turn off includeSubclasses on all FromElements.
+ FromClause from = getCurrentFromClause();
+ List fromElements = from.getFromElements();
+ for ( Iterator iterator = fromElements.iterator(); iterator.hasNext(); ) {
+ FromElement fromElement = ( FromElement ) iterator.next();
+ fromElement.setIncludeSubclasses( false );
+ }
+ }
+
+ protected AST generatePositionalParameter(AST inputNode) throws SemanticException {
+ if ( namedParameters.size() > 0 ) {
+ throw new SemanticException( "cannot define positional parameter after any named parameters have been defined" );
+ }
+ ParameterNode parameter = ( ParameterNode ) astFactory.create( PARAM, "?" );
+ PositionalParameterSpecification paramSpec = new PositionalParameterSpecification(
+ ( ( Node ) inputNode ).getLine(),
+ ( ( Node ) inputNode ).getColumn(),
+ positionalParameterCount++
+ );
+ parameter.setHqlParameterSpecification( paramSpec );
+ parameters.add( paramSpec );
+ return parameter;
+ }
+
+ protected AST generateNamedParameter(AST delimiterNode, AST nameNode) throws SemanticException {
+ String name = nameNode.getText();
+ trackNamedParameterPositions( name );
+
+ // create the node initially with the param name so that it shows
+ // appropriately in the "original text" attribute
+ ParameterNode parameter = ( ParameterNode ) astFactory.create( NAMED_PARAM, name );
+ parameter.setText( "?" );
+
+ NamedParameterSpecification paramSpec = new NamedParameterSpecification(
+ ( ( Node ) delimiterNode ).getLine(),
+ ( ( Node ) delimiterNode ).getColumn(),
+ name
+ );
+ parameter.setHqlParameterSpecification( paramSpec );
+ parameters.add( paramSpec );
+ return parameter;
+ }
+
+ private void trackNamedParameterPositions(String name) {
+ Integer loc = new Integer( parameterCount++ );
+ Object o = namedParameters.get( name );
+ if ( o == null ) {
+ namedParameters.put( name, loc );
+ }
+ else if ( o instanceof Integer ) {
+ ArrayList list = new ArrayList( 4 );
+ list.add( o );
+ list.add( loc );
+ namedParameters.put( name, list );
+ }
+ else {
+ ( ( ArrayList ) o ).add( loc );
+ }
+ }
+
+ protected void processConstant(AST constant) throws SemanticException {
+ literalProcessor.processConstant( constant, true ); // Use the delegate, resolve identifiers as FROM element aliases.
+ }
+
+ protected void processBoolean(AST constant) throws SemanticException {
+ literalProcessor.processBoolean( constant ); // Use the delegate.
+ }
+
+ protected void processNumericLiteral(AST literal) {
+ literalProcessor.processNumeric( literal );
+ }
+
+ protected void processIndex(AST indexOp) throws SemanticException {
+ IndexNode indexNode = ( IndexNode ) indexOp;
+ indexNode.resolve( true, true );
+ }
+
+ protected void processFunction(AST functionCall, boolean inSelect) throws SemanticException {
+ MethodNode methodNode = ( MethodNode ) functionCall;
+ methodNode.resolve( inSelect );
+ }
+
+ protected void processConstructor(AST constructor) throws SemanticException {
+ ConstructorNode constructorNode = ( ConstructorNode ) constructor;
+ constructorNode.prepare();
+ }
+
+ protected void setAlias(AST selectExpr, AST ident) {
+ ((SelectExpression) selectExpr).setAlias(ident.getText());
+ }
+
+ /**
+ * Returns the locations of all occurrences of the named parameter.
+ */
+ public int[] getNamedParameterLocations(String name) throws QueryException {
+ Object o = namedParameters.get( name );
+ if ( o == null ) {
+ QueryException qe = new QueryException( QueryTranslator.ERROR_NAMED_PARAMETER_DOES_NOT_APPEAR + name );
+ qe.setQueryString( queryTranslatorImpl.getQueryString() );
+ throw qe;
+ }
+ if ( o instanceof Integer ) {
+ return new int[]{( ( Integer ) o ).intValue()};
+ }
+ else {
+ return ArrayHelper.toIntArray( ( ArrayList ) o );
+ }
+ }
+
+ public void addQuerySpaces(Serializable[] spaces) {
+ for ( int i = 0; i < spaces.length; i++ ) {
+ querySpaces.add( spaces[i] );
+ }
+ }
+
+ public Type[] getReturnTypes() {
+ return selectClause.getQueryReturnTypes();
+ }
+
+ public String[] getReturnAliases() {
+ return selectClause.getQueryReturnAliases();
+ }
+
+ public SelectClause getSelectClause() {
+ return selectClause;
+ }
+
+ public FromClause getFinalFromClause() {
+ FromClause top = currentFromClause;
+ while ( top.getParentFromClause() != null ) {
+ top = top.getParentFromClause();
+ }
+ return top;
+ }
+
+ public boolean isShallowQuery() {
+ // select clauses for insert statements should alwasy be treated as shallow
+ return getStatementType() == INSERT || queryTranslatorImpl.isShallowQuery();
+ }
+
+ public Map getEnabledFilters() {
+ return queryTranslatorImpl.getEnabledFilters();
+ }
+
+ public LiteralProcessor getLiteralProcessor() {
+ return literalProcessor;
+ }
+
+ public ASTPrinter getASTPrinter() {
+ return printer;
+ }
+
+ public ArrayList getParameters() {
+ return parameters;
+ }
+
+ public int getNumberOfParametersInSetClause() {
+ return numberOfParametersInSetClause;
+ }
+
+ protected void evaluateAssignment(AST eq) throws SemanticException {
+ prepareLogicOperator( eq );
+ Queryable persister = getCurrentFromClause().getFromElement().getQueryable();
+ evaluateAssignment( eq, persister, -1 );
+ }
+
+ private void evaluateAssignment(AST eq, Queryable persister, int targetIndex) {
+ if ( persister.isMultiTable() ) {
+ // no need to even collect this information if the persister is considered multi-table
+ AssignmentSpecification specification = new AssignmentSpecification( eq, persister );
+ if ( targetIndex >= 0 ) {
+ assignmentSpecifications.add( targetIndex, specification );
+ }
+ else {
+ assignmentSpecifications.add( specification );
+ }
+ numberOfParametersInSetClause += specification.getParameters().length;
+ }
+ }
+
+ public ArrayList getAssignmentSpecifications() {
+ return assignmentSpecifications;
+ }
+
+ protected AST createIntoClause(String path, AST propertySpec) throws SemanticException {
+ Queryable persister = ( Queryable ) getSessionFactoryHelper().requireClassPersister( path );
+
+ IntoClause intoClause = ( IntoClause ) getASTFactory().create( INTO, persister.getEntityName() );
+ intoClause.setFirstChild( propertySpec );
+ intoClause.initialize( persister );
+
+ addQuerySpaces( persister.getQuerySpaces() );
+
+ return intoClause;
+ }
+
+ protected void prepareVersioned(AST updateNode, AST versioned) throws SemanticException {
+ UpdateStatement updateStatement = ( UpdateStatement ) updateNode;
+ FromClause fromClause = updateStatement.getFromClause();
+ if ( versioned != null ) {
+ // Make sure that the persister is versioned
+ Queryable persister = fromClause.getFromElement().getQueryable();
+ if ( !persister.isVersioned() ) {
+ throw new SemanticException( "increment option specified for update of non-versioned entity" );
+ }
+
+ VersionType versionType = persister.getVersionType();
+ if ( versionType instanceof UserVersionType ) {
+ throw new SemanticException( "user-defined version types not supported for increment option" );
+ }
+
+ AST eq = getASTFactory().create( HqlSqlTokenTypes.EQ, "=" );
+ AST versionPropertyNode = generateVersionPropertyNode( persister );
+
+ eq.setFirstChild( versionPropertyNode );
+
+ AST versionIncrementNode = null;
+ if ( Date.class.isAssignableFrom( versionType.getReturnedClass() ) ) {
+ versionIncrementNode = getASTFactory().create( HqlSqlTokenTypes.PARAM, "?" );
+ ParameterSpecification paramSpec = new VersionTypeSeedParameterSpecification( versionType );
+ ( ( ParameterNode ) versionIncrementNode ).setHqlParameterSpecification( paramSpec );
+ parameters.add( 0, paramSpec );
+ }
+ else {
+ // Not possible to simply re-use the versionPropertyNode here as it causes
+ // OOM errors due to circularity :(
+ versionIncrementNode = getASTFactory().create( HqlSqlTokenTypes.PLUS, "+" );
+ versionIncrementNode.setFirstChild( generateVersionPropertyNode( persister ) );
+ versionIncrementNode.addChild( getASTFactory().create( HqlSqlTokenTypes.IDENT, "1" ) );
+ }
+
+ eq.addChild( versionIncrementNode );
+
+ evaluateAssignment( eq, persister, 0 );
+
+ AST setClause = updateStatement.getSetClause();
+ AST currentFirstSetElement = setClause.getFirstChild();
+ setClause.setFirstChild( eq );
+ eq.setNextSibling( currentFirstSetElement );
+ }
+ }
+
+ private AST generateVersionPropertyNode(Queryable persister) throws SemanticException {
+ String versionPropertyName = persister.getPropertyNames()[ persister.getVersionProperty() ];
+ AST versionPropertyRef = getASTFactory().create( HqlSqlTokenTypes.IDENT, versionPropertyName );
+ AST versionPropertyNode = lookupNonQualifiedProperty( versionPropertyRef );
+ resolve( versionPropertyNode );
+ return versionPropertyNode;
+ }
+
+ protected void prepareLogicOperator(AST operator) throws SemanticException {
+ ( ( OperatorNode ) operator ).initialize();
+ }
+
+ protected void prepareArithmeticOperator(AST operator) throws SemanticException {
+ ( ( OperatorNode ) operator ).initialize();
+ }
+
+ public static void panic() {
+ throw new QueryException( "TreeWalker: panic" );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/HqlToken.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/HqlToken.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/HqlToken.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,71 @@
+// $Id: HqlToken.java 4335 2004-08-15 17:03:43Z pgmjsd $
+package org.hibernate.hql.ast;
+
+/**
+ * A custom token class for the HQL grammar.
+ * <p><i>NOTE:<i> This class must be public becuase it is instantiated by the ANTLR library. Ignore any suggestions
+ * by various code 'analyzers' about this class being package local.</p>
+ */
+public class HqlToken extends antlr.CommonToken {
+ /**
+ * True if this token could be an identifier. *
+ */
+ private boolean possibleID = false;
+ /**
+ * The previous token type. *
+ */
+ private int tokenType;
+
+ /**
+ * Returns true if the token could be an identifier.
+ *
+ * @return True if the token could be interpreted as in identifier,
+ * false if not.
+ */
+ public boolean isPossibleID() {
+ return possibleID;
+ }
+
+ /**
+ * Sets the type of the token, remembering the previous type.
+ *
+ * @param t The new token type.
+ */
+ public void setType(int t) {
+ this.tokenType = getType();
+ super.setType( t );
+ }
+
+ /**
+ * Returns the previous token type.
+ *
+ * @return int - The old token type.
+ */
+ private int getPreviousType() {
+ return tokenType;
+ }
+
+ /**
+ * Set to true if this token can be interpreted as an identifier,
+ * false if not.
+ *
+ * @param possibleID True if this is a keyword/identifier, false if not.
+ */
+ public void setPossibleID(boolean possibleID) {
+ this.possibleID = possibleID;
+ }
+
+ /**
+ * Returns a string representation of the object.
+ *
+ * @return String - The debug string.
+ */
+ public String toString() {
+ return "[\""
+ + getText()
+ + "\",<" + getType() + "> previously: <" + getPreviousType() + ">,line="
+ + line + ",col="
+ + col + ",possibleID=" + possibleID + "]";
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/InvalidPathException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/InvalidPathException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/InvalidPathException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,15 @@
+// $Id: InvalidPathException.java 4903 2004-12-07 07:53:10Z pgmjsd $
+package org.hibernate.hql.ast;
+
+import antlr.SemanticException;
+
+/**
+ * Exception thrown when an invalid path is found in a query.
+ *
+ * @author josh Dec 5, 2004 7:05:34 PM
+ */
+public class InvalidPathException extends SemanticException {
+ public InvalidPathException(String s) {
+ super( s );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/InvalidWithClauseException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/InvalidWithClauseException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/InvalidWithClauseException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,18 @@
+package org.hibernate.hql.ast;
+
+import org.hibernate.QueryException;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class InvalidWithClauseException extends QuerySyntaxException {
+ public InvalidWithClauseException(String message) {
+ super( message );
+ }
+
+ public InvalidWithClauseException(String message, String queryString) {
+ super( message, queryString );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/ParameterTranslationsImpl.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/ParameterTranslationsImpl.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/ParameterTranslationsImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,149 @@
+package org.hibernate.hql.ast;
+
+import org.hibernate.hql.ParameterTranslations;
+import org.hibernate.type.Type;
+import org.hibernate.param.ParameterSpecification;
+import org.hibernate.param.PositionalParameterSpecification;
+import org.hibernate.param.NamedParameterSpecification;
+import org.hibernate.util.ArrayHelper;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.io.Serializable;
+
+/**
+ * Defines the information available for parameters encountered during
+ * query translation through the antlr-based parser.
+ *
+ * @author <a href="mailto:steve at hibernate.org">Steve Ebersole </a>
+ */
+public class ParameterTranslationsImpl implements ParameterTranslations {
+
+ private final Map namedParameters;
+ private final ParameterInfo[] ordinalParameters;
+
+ public boolean supportsOrdinalParameterMetadata() {
+ return true;
+ }
+
+ public int getOrdinalParameterCount() {
+ return ordinalParameters.length;
+ }
+
+ public ParameterInfo getOrdinalParameterInfo(int ordinalPosition) {
+ // remember that ordinal parameters numbers are 1-based!!!
+ return ordinalParameters[ordinalPosition - 1];
+ }
+
+ public int getOrdinalParameterSqlLocation(int ordinalPosition) {
+ return getOrdinalParameterInfo( ordinalPosition ).getSqlLocations()[0];
+ }
+
+ public Type getOrdinalParameterExpectedType(int ordinalPosition) {
+ return getOrdinalParameterInfo( ordinalPosition ).getExpectedType();
+ }
+
+ public Set getNamedParameterNames() {
+ return namedParameters.keySet();
+ }
+
+ public ParameterInfo getNamedParameterInfo(String name) {
+ return ( ParameterInfo ) namedParameters.get( name );
+ }
+
+ public int[] getNamedParameterSqlLocations(String name) {
+ return getNamedParameterInfo( name ).getSqlLocations();
+ }
+
+ public Type getNamedParameterExpectedType(String name) {
+ return getNamedParameterInfo( name ).getExpectedType();
+ }
+
+ /**
+ * Constructs a parameter metadata object given a list of parameter
+ * specifications.
+ * </p>
+ * Note: the order in the incoming list denotes the parameter's
+ * psudeo-position within the resulting sql statement.
+ *
+ * @param parameterSpecifications
+ */
+ public ParameterTranslationsImpl(List parameterSpecifications) {
+
+ class NamedParamTempHolder {
+ String name;
+ Type type;
+ List positions = new ArrayList();
+ }
+
+ int size = parameterSpecifications.size();
+ List ordinalParameterList = new ArrayList();
+ Map namedParameterMap = new HashMap();
+ for ( int i = 0; i < size; i++ ) {
+ final ParameterSpecification spec = ( ParameterSpecification ) parameterSpecifications.get( i );
+ if ( PositionalParameterSpecification.class.isAssignableFrom( spec.getClass() ) ) {
+ PositionalParameterSpecification ordinalSpec = ( PositionalParameterSpecification ) spec;
+ ordinalParameterList.add( new ParameterInfo( i, ordinalSpec.getExpectedType() ) );
+ }
+ else if ( NamedParameterSpecification.class.isAssignableFrom( spec.getClass() ) ) {
+ NamedParameterSpecification namedSpec = ( NamedParameterSpecification ) spec;
+ NamedParamTempHolder paramHolder = ( NamedParamTempHolder ) namedParameterMap.get( namedSpec.getName() );
+ if ( paramHolder == null ) {
+ paramHolder = new NamedParamTempHolder();
+ paramHolder.name = namedSpec.getName();
+ paramHolder.type = namedSpec.getExpectedType();
+ namedParameterMap.put( namedSpec.getName(), paramHolder );
+ }
+ paramHolder.positions.add( new Integer( i ) );
+ }
+ else {
+ // don't care about other param types here, just those explicitly user-defined...
+ }
+ }
+
+ ordinalParameters = ( ParameterInfo[] ) ordinalParameterList.toArray( new ParameterInfo[ordinalParameterList.size()] );
+
+ if ( namedParameterMap.isEmpty() ) {
+ namedParameters = java.util.Collections.EMPTY_MAP;
+ }
+ else {
+ Map namedParametersBacking = new HashMap( namedParameterMap.size() );
+ Iterator itr = namedParameterMap.values().iterator();
+ while( itr.hasNext() ) {
+ final NamedParamTempHolder holder = ( NamedParamTempHolder ) itr.next();
+ namedParametersBacking.put(
+ holder.name,
+ new ParameterInfo( ArrayHelper.toIntArray( holder.positions ), holder.type )
+ );
+ }
+ namedParameters = java.util.Collections.unmodifiableMap( namedParametersBacking );
+ }
+ }
+
+ public static class ParameterInfo implements Serializable {
+ private final int[] sqlLocations;
+ private final Type expectedType;
+
+ public ParameterInfo(int[] sqlPositions, Type expectedType) {
+ this.sqlLocations = sqlPositions;
+ this.expectedType = expectedType;
+ }
+
+ public ParameterInfo(int sqlPosition, Type expectedType) {
+ this.sqlLocations = new int[] { sqlPosition };
+ this.expectedType = expectedType;
+ }
+
+ public int[] getSqlLocations() {
+ return sqlLocations;
+ }
+
+ public Type getExpectedType() {
+ return expectedType;
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/ParseErrorHandler.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/ParseErrorHandler.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/ParseErrorHandler.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,21 @@
+// $Id: ParseErrorHandler.java 4941 2004-12-11 16:31:18Z pgmjsd $
+
+package org.hibernate.hql.ast;
+
+import org.hibernate.QueryException;
+
+
+/**
+ * Defines the behavior of an error handler for the HQL parsers.
+ * User: josh
+ * Date: Dec 6, 2003
+ * Time: 12:20:43 PM
+ */
+public interface ParseErrorHandler extends ErrorReporter {
+
+ int getErrorCount();
+
+ // --Commented out by Inspection (12/11/04 10:56 AM): int getWarningCount();
+
+ void throwQueryException() throws QueryException;
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/QuerySyntaxException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/QuerySyntaxException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/QuerySyntaxException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,34 @@
+// $Id: QuerySyntaxException.java 9242 2006-02-09 12:37:36Z steveebersole $
+package org.hibernate.hql.ast;
+
+import antlr.RecognitionException;
+import org.hibernate.QueryException;
+
+/**
+ * Exception thrown when there is a syntax error in the HQL.
+ *
+ * @author josh
+ */
+public class QuerySyntaxException extends QueryException {
+
+ public QuerySyntaxException(String message) {
+ super( message );
+ }
+
+ public QuerySyntaxException(String message, String hql) {
+ this( message );
+ setQueryString( hql );
+ }
+
+ public static QuerySyntaxException convert(RecognitionException e) {
+ return convert( e, null );
+ }
+
+ public static QuerySyntaxException convert(RecognitionException e, String hql) {
+ String positionInfo = e.getLine() > 0 && e.getColumn() > 0
+ ? " near line " + e.getLine() + ", column " + e.getColumn()
+ : "";
+ return new QuerySyntaxException( e.getMessage() + positionInfo, hql );
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/QueryTranslatorImpl.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/QueryTranslatorImpl.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/QueryTranslatorImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,577 @@
+// $Id: QueryTranslatorImpl.java 10000 2006-06-08 21:04:45Z steve.ebersole at jboss.com $
+package org.hibernate.hql.ast;
+
+import antlr.ANTLRException;
+import antlr.RecognitionException;
+import antlr.TokenStreamException;
+import antlr.collections.AST;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.QueryException;
+import org.hibernate.ScrollableResults;
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.RowSelection;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.event.EventSource;
+import org.hibernate.hql.FilterTranslator;
+import org.hibernate.hql.QueryExecutionRequestException;
+import org.hibernate.hql.ParameterTranslations;
+import org.hibernate.hql.antlr.HqlSqlTokenTypes;
+import org.hibernate.hql.antlr.HqlTokenTypes;
+import org.hibernate.hql.antlr.SqlTokenTypes;
+import org.hibernate.hql.ast.exec.BasicExecutor;
+import org.hibernate.hql.ast.exec.MultiTableDeleteExecutor;
+import org.hibernate.hql.ast.exec.MultiTableUpdateExecutor;
+import org.hibernate.hql.ast.exec.StatementExecutor;
+import org.hibernate.hql.ast.tree.FromElement;
+import org.hibernate.hql.ast.tree.InsertStatement;
+import org.hibernate.hql.ast.tree.QueryNode;
+import org.hibernate.hql.ast.tree.Statement;
+import org.hibernate.hql.ast.util.ASTPrinter;
+import org.hibernate.hql.ast.util.NodeTraverser;
+import org.hibernate.hql.ast.util.ASTUtil;
+import org.hibernate.loader.hql.QueryLoader;
+import org.hibernate.persister.entity.Queryable;
+import org.hibernate.type.Type;
+import org.hibernate.util.IdentitySet;
+import org.hibernate.util.StringHelper;
+import org.hibernate.util.ReflectHelper;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.ArrayList;
+
+/**
+ * A QueryTranslator that uses an Antlr-based parser.
+ *
+ * @author Joshua Davis (pgmjsd at sourceforge.net)
+ */
+public class QueryTranslatorImpl implements FilterTranslator {
+
+ private static final Log log = LogFactory.getLog( QueryTranslatorImpl.class );
+ private static final Log AST_LOG = LogFactory.getLog( "org.hibernate.hql.ast.AST" );
+
+ private SessionFactoryImplementor factory;
+
+ private final String queryIdentifier;
+ private String hql;
+ private boolean shallowQuery;
+ private Map tokenReplacements;
+
+ private Map enabledFilters; //TODO:this is only needed during compilation .. can we eliminate the instvar?
+
+ private boolean compiled;
+ private QueryLoader queryLoader;
+ private StatementExecutor statementExecutor;
+
+ private Statement sqlAst;
+ private String sql;
+
+ private ParameterTranslations paramTranslations;
+
+ /**
+ * Creates a new AST-based query translator.
+ *
+ * @param queryIdentifier The query-identifier (used in stats collection)
+ * @param query The hql query to translate
+ * @param enabledFilters Currently enabled filters
+ * @param factory The session factory constructing this translator instance.
+ */
+ public QueryTranslatorImpl(
+ String queryIdentifier,
+ String query,
+ Map enabledFilters,
+ SessionFactoryImplementor factory) {
+ this.queryIdentifier = queryIdentifier;
+ this.hql = query;
+ this.compiled = false;
+ this.shallowQuery = false;
+ this.enabledFilters = enabledFilters;
+ this.factory = factory;
+ }
+
+ /**
+ * Compile a "normal" query. This method may be called multiple
+ * times. Subsequent invocations are no-ops.
+ *
+ * @param replacements Defined query substitutions.
+ * @param shallow Does this represent a shallow (scalar or entity-id) select?
+ * @throws QueryException There was a problem parsing the query string.
+ * @throws MappingException There was a problem querying defined mappings.
+ */
+ public void compile(
+ Map replacements,
+ boolean shallow) throws QueryException, MappingException {
+ doCompile( replacements, shallow, null );
+ }
+
+ /**
+ * Compile a filter. This method may be called multiple
+ * times. Subsequent invocations are no-ops.
+ *
+ * @param collectionRole the role name of the collection used as the basis for the filter.
+ * @param replacements Defined query substitutions.
+ * @param shallow Does this represent a shallow (scalar or entity-id) select?
+ * @throws QueryException There was a problem parsing the query string.
+ * @throws MappingException There was a problem querying defined mappings.
+ */
+ public void compile(
+ String collectionRole,
+ Map replacements,
+ boolean shallow) throws QueryException, MappingException {
+ doCompile( replacements, shallow, collectionRole );
+ }
+
+ /**
+ * Performs both filter and non-filter compiling.
+ *
+ * @param replacements Defined query substitutions.
+ * @param shallow Does this represent a shallow (scalar or entity-id) select?
+ * @param collectionRole the role name of the collection used as the basis for the filter, NULL if this
+ * is not a filter.
+ */
+ private synchronized void doCompile(Map replacements, boolean shallow, String collectionRole) {
+ // If the query is already compiled, skip the compilation.
+ if ( compiled ) {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "compile() : The query is already compiled, skipping..." );
+ }
+ return;
+ }
+
+ // Remember the parameters for the compilation.
+ this.tokenReplacements = replacements;
+ if ( tokenReplacements == null ) {
+ tokenReplacements = new HashMap();
+ }
+ this.shallowQuery = shallow;
+
+ try {
+ // PHASE 1 : Parse the HQL into an AST.
+ HqlParser parser = parse( true );
+
+ // PHASE 2 : Analyze the HQL AST, and produce an SQL AST.
+ HqlSqlWalker w = analyze( parser, collectionRole );
+
+ sqlAst = ( Statement ) w.getAST();
+
+ // at some point the generate phase needs to be moved out of here,
+ // because a single object-level DML might spawn multiple SQL DML
+ // command executions.
+ //
+ // Possible to just move the sql generation for dml stuff, but for
+ // consistency-sake probably best to just move responsiblity for
+ // the generation phase completely into the delegates
+ // (QueryLoader/StatementExecutor) themselves. Also, not sure why
+ // QueryLoader currently even has a dependency on this at all; does
+ // it need it? Ideally like to see the walker itself given to the delegates directly...
+
+ if ( sqlAst.needsExecutor() ) {
+ statementExecutor = buildAppropriateStatementExecutor( w );
+ }
+ else {
+ // PHASE 3 : Generate the SQL.
+ generate( ( QueryNode ) sqlAst );
+ queryLoader = new QueryLoader( this, factory, w.getSelectClause() );
+ }
+
+ compiled = true;
+ }
+ catch ( QueryException qe ) {
+ qe.setQueryString( hql );
+ throw qe;
+ }
+ catch ( RecognitionException e ) {
+ // we do not actually propogate ANTLRExceptions as a cause, so
+ // log it here for diagnostic purposes
+ if ( log.isTraceEnabled() ) {
+ log.trace( "converted antlr.RecognitionException", e );
+ }
+ throw QuerySyntaxException.convert( e, hql );
+ }
+ catch ( ANTLRException e ) {
+ // we do not actually propogate ANTLRExceptions as a cause, so
+ // log it here for diagnostic purposes
+ if ( log.isTraceEnabled() ) {
+ log.trace( "converted antlr.ANTLRException", e );
+ }
+ throw new QueryException( e.getMessage(), hql );
+ }
+
+ this.enabledFilters = null; //only needed during compilation phase...
+ }
+
+ private void generate(AST sqlAst) throws QueryException, RecognitionException {
+ if ( sql == null ) {
+ SqlGenerator gen = new SqlGenerator(factory);
+ gen.statement( sqlAst );
+ sql = gen.getSQL();
+ if ( log.isDebugEnabled() ) {
+ log.debug( "HQL: " + hql );
+ log.debug( "SQL: " + sql );
+ }
+ gen.getParseErrorHandler().throwQueryException();
+ }
+ }
+
+ private HqlSqlWalker analyze(HqlParser parser, String collectionRole) throws QueryException, RecognitionException {
+ HqlSqlWalker w = new HqlSqlWalker( this, factory, parser, tokenReplacements, collectionRole );
+ AST hqlAst = parser.getAST();
+
+ // Transform the tree.
+ w.statement( hqlAst );
+
+ if ( AST_LOG.isDebugEnabled() ) {
+ ASTPrinter printer = new ASTPrinter( SqlTokenTypes.class );
+ AST_LOG.debug( printer.showAsString( w.getAST(), "--- SQL AST ---" ) );
+ }
+
+ w.getParseErrorHandler().throwQueryException();
+
+ return w;
+ }
+
+ private HqlParser parse(boolean filter) throws TokenStreamException, RecognitionException {
+ // Parse the query string into an HQL AST.
+ HqlParser parser = HqlParser.getInstance( hql );
+ parser.setFilter( filter );
+
+ if ( log.isDebugEnabled() ) {
+ log.debug( "parse() - HQL: " + hql );
+ }
+ parser.statement();
+
+ AST hqlAst = parser.getAST();
+
+ JavaConstantConverter converter = new JavaConstantConverter();
+ NodeTraverser walker = new NodeTraverser( converter );
+ walker.traverseDepthFirst( hqlAst );
+
+ showHqlAst( hqlAst );
+
+ parser.getParseErrorHandler().throwQueryException();
+ return parser;
+ }
+
+ void showHqlAst(AST hqlAst) {
+ if ( AST_LOG.isDebugEnabled() ) {
+ ASTPrinter printer = new ASTPrinter( HqlTokenTypes.class );
+ printer.setShowClassNames( false ); // The class names aren't interesting in the first tree.
+ AST_LOG.debug( printer.showAsString( hqlAst, "--- HQL AST ---" ) );
+ }
+ }
+
+ private void errorIfDML() throws HibernateException {
+ if ( sqlAst.needsExecutor() ) {
+ throw new QueryExecutionRequestException( "Not supported for DML operations", hql );
+ }
+ }
+
+ private void errorIfSelect() throws HibernateException {
+ if ( !sqlAst.needsExecutor() ) {
+ throw new QueryExecutionRequestException( "Not supported for select queries", hql );
+ }
+ }
+
+ public String getQueryIdentifier() {
+ return queryIdentifier;
+ }
+
+ public Statement getSqlAST() {
+ return sqlAst;
+ }
+
+ private HqlSqlWalker getWalker() {
+ return sqlAst.getWalker();
+ }
+
+ /**
+ * Types of the return values of an <tt>iterate()</tt> style query.
+ *
+ * @return an array of <tt>Type</tt>s.
+ */
+ public Type[] getReturnTypes() {
+ errorIfDML();
+ return getWalker().getReturnTypes();
+ }
+
+ public String[] getReturnAliases() {
+ errorIfDML();
+ return getWalker().getReturnAliases();
+ }
+
+ public String[][] getColumnNames() {
+ errorIfDML();
+ return getWalker().getSelectClause().getColumnNames();
+ }
+
+ public Set getQuerySpaces() {
+ return getWalker().getQuerySpaces();
+ }
+
+ public List list(SessionImplementor session, QueryParameters queryParameters)
+ throws HibernateException {
+ // Delegate to the QueryLoader...
+ errorIfDML();
+ QueryNode query = ( QueryNode ) sqlAst;
+ boolean hasLimit = queryParameters.getRowSelection() != null && queryParameters.getRowSelection().definesLimits();
+ boolean needsDistincting = ( query.getSelectClause().isDistinct() || hasLimit ) && containsCollectionFetches();
+
+ QueryParameters queryParametersToUse;
+ if ( hasLimit && containsCollectionFetches() ) {
+ log.warn( "firstResult/maxResults specified with collection fetch; applying in memory!" );
+ RowSelection selection = new RowSelection();
+ selection.setFetchSize( queryParameters.getRowSelection().getFetchSize() );
+ selection.setTimeout( queryParameters.getRowSelection().getTimeout() );
+ queryParametersToUse = queryParameters.createCopyUsing( selection );
+ }
+ else {
+ queryParametersToUse = queryParameters;
+ }
+
+ List results = queryLoader.list( session, queryParametersToUse );
+
+ if ( needsDistincting ) {
+ int includedCount = -1;
+ // NOTE : firstRow is zero-based
+ int first = !hasLimit || queryParameters.getRowSelection().getFirstRow() == null
+ ? 0
+ : queryParameters.getRowSelection().getFirstRow().intValue();
+ int max = !hasLimit || queryParameters.getRowSelection().getMaxRows() == null
+ ? -1
+ : queryParameters.getRowSelection().getMaxRows().intValue();
+ int size = results.size();
+ List tmp = new ArrayList();
+ IdentitySet distinction = new IdentitySet();
+ for ( int i = 0; i < size; i++ ) {
+ final Object result = results.get( i );
+ if ( !distinction.add( result ) ) {
+ continue;
+ }
+ includedCount++;
+ if ( includedCount < first ) {
+ continue;
+ }
+ tmp.add( result );
+ // NOTE : ( max - 1 ) because first is zero-based while max is not...
+ if ( max >= 0 && ( includedCount - first ) >= ( max - 1 ) ) {
+ break;
+ }
+ }
+ results = tmp;
+ }
+
+ return results;
+ }
+
+ /**
+ * Return the query results as an iterator
+ */
+ public Iterator iterate(QueryParameters queryParameters, EventSource session)
+ throws HibernateException {
+ // Delegate to the QueryLoader...
+ errorIfDML();
+ return queryLoader.iterate( queryParameters, session );
+ }
+
+ /**
+ * Return the query results, as an instance of <tt>ScrollableResults</tt>
+ */
+ public ScrollableResults scroll(QueryParameters queryParameters, SessionImplementor session)
+ throws HibernateException {
+ // Delegate to the QueryLoader...
+ errorIfDML();
+ return queryLoader.scroll( queryParameters, session );
+ }
+
+ public int executeUpdate(QueryParameters queryParameters, SessionImplementor session)
+ throws HibernateException {
+ errorIfSelect();
+ return statementExecutor.execute( queryParameters, session );
+ }
+
+ /**
+ * The SQL query string to be called; implemented by all subclasses
+ */
+ public String getSQLString() {
+ return sql;
+ }
+
+ public List collectSqlStrings() {
+ ArrayList list = new ArrayList();
+ if ( isManipulationStatement() ) {
+ String[] sqlStatements = statementExecutor.getSqlStatements();
+ for ( int i = 0; i < sqlStatements.length; i++ ) {
+ list.add( sqlStatements[i] );
+ }
+ }
+ else {
+ list.add( sql );
+ }
+ return list;
+ }
+
+ // -- Package local methods for the QueryLoader delegate --
+
+ public boolean isShallowQuery() {
+ return shallowQuery;
+ }
+
+ public String getQueryString() {
+ return hql;
+ }
+
+ public Map getEnabledFilters() {
+ return enabledFilters;
+ }
+
+ public int[] getNamedParameterLocs(String name) {
+ return getWalker().getNamedParameterLocations( name );
+ }
+
+ public boolean containsCollectionFetches() {
+ errorIfDML();
+ List collectionFetches = ( ( QueryNode ) sqlAst ).getFromClause().getCollectionFetches();
+ return collectionFetches != null && collectionFetches.size() > 0;
+ }
+
+ public boolean isManipulationStatement() {
+ return sqlAst.needsExecutor();
+ }
+
+ public void validateScrollability() throws HibernateException {
+ // Impl Note: allows multiple collection fetches as long as the
+ // entire fecthed graph still "points back" to a single
+ // root entity for return
+
+ errorIfDML();
+
+ QueryNode query = ( QueryNode ) sqlAst;
+
+ // If there are no collection fetches, then no further checks are needed
+ List collectionFetches = query.getFromClause().getCollectionFetches();
+ if ( collectionFetches.isEmpty() ) {
+ return;
+ }
+
+ // A shallow query is ok (although technically there should be no fetching here...)
+ if ( isShallowQuery() ) {
+ return;
+ }
+
+ // Otherwise, we have a non-scalar select with defined collection fetch(es).
+ // Make sure that there is only a single root entity in the return (no tuples)
+ if ( getReturnTypes().length > 1 ) {
+ throw new HibernateException( "cannot scroll with collection fetches and returned tuples" );
+ }
+
+ FromElement owner = null;
+ Iterator itr = query.getSelectClause().getFromElementsForLoad().iterator();
+ while ( itr.hasNext() ) {
+ // should be the first, but just to be safe...
+ final FromElement fromElement = ( FromElement ) itr.next();
+ if ( fromElement.getOrigin() == null ) {
+ owner = fromElement;
+ break;
+ }
+ }
+
+ if ( owner == null ) {
+ throw new HibernateException( "unable to locate collection fetch(es) owner for scrollability checks" );
+ }
+
+ // This is not strictly true. We actually just need to make sure that
+ // it is ordered by root-entity PK and that that order-by comes before
+ // any non-root-entity ordering...
+
+ AST primaryOrdering = query.getOrderByClause().getFirstChild();
+ if ( primaryOrdering != null ) {
+ // TODO : this is a bit dodgy, come up with a better way to check this (plus see above comment)
+ String [] idColNames = owner.getQueryable().getIdentifierColumnNames();
+ String expectedPrimaryOrderSeq = StringHelper.join(
+ ", ",
+ StringHelper.qualify( owner.getTableAlias(), idColNames )
+ );
+ if ( !primaryOrdering.getText().startsWith( expectedPrimaryOrderSeq ) ) {
+ throw new HibernateException( "cannot scroll results with collection fetches which are not ordered primarily by the root entity's PK" );
+ }
+ }
+ }
+
+ private StatementExecutor buildAppropriateStatementExecutor(HqlSqlWalker walker) {
+ Statement statement = ( Statement ) walker.getAST();
+ if ( walker.getStatementType() == HqlSqlTokenTypes.DELETE ) {
+ FromElement fromElement = walker.getFinalFromClause().getFromElement();
+ Queryable persister = fromElement.getQueryable();
+ if ( persister.isMultiTable() ) {
+ return new MultiTableDeleteExecutor( walker );
+ }
+ else {
+ return new BasicExecutor( walker, persister );
+ }
+ }
+ else if ( walker.getStatementType() == HqlSqlTokenTypes.UPDATE ) {
+ FromElement fromElement = walker.getFinalFromClause().getFromElement();
+ Queryable persister = fromElement.getQueryable();
+ if ( persister.isMultiTable() ) {
+ // even here, if only properties mapped to the "base table" are referenced
+ // in the set and where clauses, this could be handled by the BasicDelegate.
+ // TODO : decide if it is better performance-wise to perform that check, or to simply use the MultiTableUpdateDelegate
+ return new MultiTableUpdateExecutor( walker );
+ }
+ else {
+ return new BasicExecutor( walker, persister );
+ }
+ }
+ else if ( walker.getStatementType() == HqlSqlTokenTypes.INSERT ) {
+ return new BasicExecutor( walker, ( ( InsertStatement ) statement ).getIntoClause().getQueryable() );
+ }
+ else {
+ throw new QueryException( "Unexpected statement type" );
+ }
+ }
+
+ public ParameterTranslations getParameterTranslations() {
+ if ( paramTranslations == null ) {
+ paramTranslations = new ParameterTranslationsImpl( getWalker().getParameters() );
+ }
+ return paramTranslations;
+ }
+
+ public static class JavaConstantConverter implements NodeTraverser.VisitationStrategy {
+ private AST dotRoot;
+ public void visit(AST node) {
+ if ( dotRoot != null ) {
+ // we are already processing a dot-structure
+ if ( ASTUtil.isSubtreeChild( dotRoot, node ) ) {
+ // igndore it...
+ return;
+ }
+ else {
+ // we are now at a new tree level
+ dotRoot = null;
+ }
+ }
+
+ if ( dotRoot == null && node.getType() == HqlTokenTypes.DOT ) {
+ dotRoot = node;
+ handleDotStructure( dotRoot );
+ }
+ }
+ private void handleDotStructure(AST dotStructureRoot) {
+ String expression = ASTUtil.getPathText( dotStructureRoot );
+ Object constant = ReflectHelper.getConstantValue( expression );
+ if ( constant != null ) {
+ dotStructureRoot.setFirstChild( null );
+ dotStructureRoot.setType( HqlTokenTypes.JAVA_CONSTANT );
+ dotStructureRoot.setText( expression );
+ }
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/SqlASTFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/SqlASTFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/SqlASTFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,221 @@
+// $Id: SqlASTFactory.java 10060 2006-06-28 02:53:39Z steve.ebersole at jboss.com $
+package org.hibernate.hql.ast;
+
+import antlr.ASTFactory;
+import antlr.Token;
+import antlr.collections.AST;
+import org.hibernate.hql.antlr.HqlSqlTokenTypes;
+import org.hibernate.hql.ast.tree.AggregateNode;
+import org.hibernate.hql.ast.tree.BinaryArithmeticOperatorNode;
+import org.hibernate.hql.ast.tree.BinaryLogicOperatorNode;
+import org.hibernate.hql.ast.tree.Case2Node;
+import org.hibernate.hql.ast.tree.CaseNode;
+import org.hibernate.hql.ast.tree.CollectionFunction;
+import org.hibernate.hql.ast.tree.ConstructorNode;
+import org.hibernate.hql.ast.tree.CountNode;
+import org.hibernate.hql.ast.tree.DeleteStatement;
+import org.hibernate.hql.ast.tree.DotNode;
+import org.hibernate.hql.ast.tree.FromClause;
+import org.hibernate.hql.ast.tree.FromElement;
+import org.hibernate.hql.ast.tree.IdentNode;
+import org.hibernate.hql.ast.tree.ImpliedFromElement;
+import org.hibernate.hql.ast.tree.IndexNode;
+import org.hibernate.hql.ast.tree.InitializeableNode;
+import org.hibernate.hql.ast.tree.InsertStatement;
+import org.hibernate.hql.ast.tree.IntoClause;
+import org.hibernate.hql.ast.tree.LiteralNode;
+import org.hibernate.hql.ast.tree.MethodNode;
+import org.hibernate.hql.ast.tree.OrderByClause;
+import org.hibernate.hql.ast.tree.ParameterNode;
+import org.hibernate.hql.ast.tree.QueryNode;
+import org.hibernate.hql.ast.tree.SelectClause;
+import org.hibernate.hql.ast.tree.SelectExpressionImpl;
+import org.hibernate.hql.ast.tree.SqlFragment;
+import org.hibernate.hql.ast.tree.SqlNode;
+import org.hibernate.hql.ast.tree.UnaryArithmeticNode;
+import org.hibernate.hql.ast.tree.UpdateStatement;
+import org.hibernate.hql.ast.tree.BetweenOperatorNode;
+import org.hibernate.hql.ast.tree.UnaryLogicOperatorNode;
+import org.hibernate.hql.ast.tree.InLogicOperatorNode;
+import org.hibernate.hql.ast.tree.JavaConstantNode;
+import org.hibernate.hql.ast.tree.SessionFactoryAwareNode;
+import org.hibernate.hql.ast.tree.BooleanLiteralNode;
+
+import java.lang.reflect.Constructor;
+
+/**
+ * Custom AST factory the intermediate tree that causes ANTLR to create specialized
+ * AST nodes, given the AST node type (from HqlSqlTokenTypes). HqlSqlWalker registers
+ * this factory with itself when it is initialized.
+ *
+ * @author Joshua
+ */
+public class SqlASTFactory extends ASTFactory implements HqlSqlTokenTypes {
+ private HqlSqlWalker walker;
+
+ /**
+ * Create factory with a specific mapping from token type
+ * to Java AST node type. Your subclasses of ASTFactory
+ * can override and reuse the map stuff.
+ */
+ public SqlASTFactory(HqlSqlWalker walker) {
+ super();
+ this.walker = walker;
+ }
+
+ /**
+ * Returns the class for a given token type (a.k.a. AST node type).
+ *
+ * @param tokenType The token type.
+ * @return Class - The AST node class to instantiate.
+ */
+ public Class getASTNodeType(int tokenType) {
+ switch ( tokenType ) {
+ case SELECT:
+ case QUERY:
+ return QueryNode.class;
+ case UPDATE:
+ return UpdateStatement.class;
+ case DELETE:
+ return DeleteStatement.class;
+ case INSERT:
+ return InsertStatement.class;
+ case INTO:
+ return IntoClause.class;
+ case FROM:
+ return FromClause.class;
+ case FROM_FRAGMENT:
+ return FromElement.class;
+ case IMPLIED_FROM:
+ return ImpliedFromElement.class;
+ case DOT:
+ return DotNode.class;
+ case INDEX_OP:
+ return IndexNode.class;
+ // Alias references and identifiers use the same node class.
+ case ALIAS_REF:
+ case IDENT:
+ return IdentNode.class;
+ case SQL_TOKEN:
+ return SqlFragment.class;
+ case METHOD_CALL:
+ return MethodNode.class;
+ case ELEMENTS:
+ case INDICES:
+ return CollectionFunction.class;
+ case SELECT_CLAUSE:
+ return SelectClause.class;
+ case SELECT_EXPR:
+ return SelectExpressionImpl.class;
+ case AGGREGATE:
+ return AggregateNode.class;
+ case COUNT:
+ return CountNode.class;
+ case CONSTRUCTOR:
+ return ConstructorNode.class;
+ case NUM_INT:
+ case NUM_FLOAT:
+ case NUM_LONG:
+ case NUM_DOUBLE:
+ case QUOTED_STRING:
+ return LiteralNode.class;
+ case TRUE:
+ case FALSE:
+ return BooleanLiteralNode.class;
+ case JAVA_CONSTANT:
+ return JavaConstantNode.class;
+ case ORDER:
+ return OrderByClause.class;
+ case PLUS:
+ case MINUS:
+ case STAR:
+ case DIV:
+ return BinaryArithmeticOperatorNode.class;
+ case UNARY_MINUS:
+ case UNARY_PLUS:
+ return UnaryArithmeticNode.class;
+ case CASE2:
+ return Case2Node.class;
+ case CASE:
+ return CaseNode.class;
+ case PARAM:
+ case NAMED_PARAM:
+ return ParameterNode.class;
+ case EQ:
+ case NE:
+ case LT:
+ case GT:
+ case LE:
+ case GE:
+ case LIKE:
+ case NOT_LIKE:
+ return BinaryLogicOperatorNode.class;
+ case IN:
+ case NOT_IN:
+ return InLogicOperatorNode.class;
+ case BETWEEN:
+ case NOT_BETWEEN:
+ return BetweenOperatorNode.class;
+ case IS_NULL:
+ case IS_NOT_NULL:
+ case EXISTS:
+ return UnaryLogicOperatorNode.class;
+ default:
+ return SqlNode.class;
+ } // switch
+ }
+
+ protected AST createUsingCtor(Token token, String className) {
+ Class c;
+ AST t;
+ try {
+ c = Class.forName( className );
+ Class[] tokenArgType = new Class[]{antlr.Token.class};
+ Constructor ctor = c.getConstructor( tokenArgType );
+ if ( ctor != null ) {
+ t = ( AST ) ctor.newInstance( new Object[]{token} ); // make a new one
+ initializeSqlNode( t );
+ }
+ else {
+ // just do the regular thing if you can't find the ctor
+ // Your AST must have default ctor to use this.
+ t = create( c );
+ }
+ }
+ catch ( Exception e ) {
+ throw new IllegalArgumentException( "Invalid class or can't make instance, " + className );
+ }
+ return t;
+ }
+
+ private void initializeSqlNode(AST t) {
+ // Initialize SQL nodes here.
+ if ( t instanceof InitializeableNode ) {
+ InitializeableNode initializeableNode = ( InitializeableNode ) t;
+ initializeableNode.initialize( walker );
+ }
+ if ( t instanceof SessionFactoryAwareNode ) {
+ ( ( SessionFactoryAwareNode ) t ).setSessionFactory( walker.getSessionFactoryHelper().getFactory() );
+ }
+ }
+
+ /**
+ * Actually instantiate the AST node.
+ *
+ * @param c The class to instantiate.
+ * @return The instantiated and initialized node.
+ */
+ protected AST create(Class c) {
+ AST t;
+ try {
+ t = ( AST ) c.newInstance(); // make a new one
+ initializeSqlNode( t );
+ }
+ catch ( Exception e ) {
+ error( "Can't create AST Node " + c.getName() );
+ return null;
+ }
+ return t;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/SqlGenerator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/SqlGenerator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/SqlGenerator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,267 @@
+// $Id: SqlGenerator.java 10060 2006-06-28 02:53:39Z steve.ebersole at jboss.com $
+package org.hibernate.hql.ast;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import antlr.RecognitionException;
+import antlr.collections.AST;
+import org.hibernate.QueryException;
+import org.hibernate.dialect.function.SQLFunction;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.hql.antlr.SqlGeneratorBase;
+import org.hibernate.hql.ast.tree.MethodNode;
+import org.hibernate.hql.ast.tree.FromElement;
+import org.hibernate.hql.ast.tree.Node;
+
+/**
+ * Generates SQL by overriding callback methods in the base class, which does
+ * the actual SQL AST walking.
+ *
+ * @author Joshua Davis
+ * @author Steve Ebersole
+ */
+public class SqlGenerator extends SqlGeneratorBase implements ErrorReporter {
+ /**
+ * Handles parser errors.
+ */
+ private ParseErrorHandler parseErrorHandler;
+
+ /**
+ * all append invocations on the buf should go through this Output instance variable.
+ * The value of this variable may be temporarily substitued by sql function processing code
+ * to catch generated arguments.
+ * This is because sql function templates need arguments as seperate string chunks
+ * that will be assembled into the target dialect-specific function call.
+ */
+ private SqlWriter writer = new DefaultWriter();
+
+ private SessionFactoryImplementor sessionFactory;
+
+ private LinkedList outputStack = new LinkedList();
+
+ protected void out(String s) {
+ writer.clause( s );
+ }
+
+ protected void out(AST n) {
+ if ( n instanceof Node ) {
+ out( ( ( Node ) n ).getRenderText( sessionFactory ) );
+ }
+ else {
+ super.out( n );
+ }
+ }
+
+ protected void commaBetweenParameters(String comma) {
+ writer.commaBetweenParameters( comma );
+ }
+
+ public void reportError(RecognitionException e) {
+ parseErrorHandler.reportError( e ); // Use the delegate.
+ }
+
+ public void reportError(String s) {
+ parseErrorHandler.reportError( s ); // Use the delegate.
+ }
+
+ public void reportWarning(String s) {
+ parseErrorHandler.reportWarning( s );
+ }
+
+ public ParseErrorHandler getParseErrorHandler() {
+ return parseErrorHandler;
+ }
+
+ public SqlGenerator(SessionFactoryImplementor sfi) {
+ super();
+ parseErrorHandler = new ErrorCounter();
+ sessionFactory = sfi;
+ }
+
+ public String getSQL() {
+ return getStringBuffer().toString();
+ }
+
+ protected void optionalSpace() {
+ int c = getLastChar();
+ switch ( c ) {
+ case -1:
+ return;
+ case ' ':
+ return;
+ case ')':
+ return;
+ case '(':
+ return;
+ default:
+ out( " " );
+ }
+ }
+
+ protected void beginFunctionTemplate(AST m, AST i) {
+ MethodNode methodNode = ( MethodNode ) m;
+ SQLFunction template = methodNode.getSQLFunction();
+ if ( template == null ) {
+ // if template is null we just write the function out as it appears in the hql statement
+ super.beginFunctionTemplate( m, i );
+ }
+ else {
+ // this function has a template -> redirect output and catch the arguments
+ outputStack.addFirst( writer );
+ writer = new FunctionArguments();
+ }
+ }
+
+ protected void endFunctionTemplate(AST m) {
+ MethodNode methodNode = ( MethodNode ) m;
+ SQLFunction template = methodNode.getSQLFunction();
+ if ( template == null ) {
+ super.endFunctionTemplate( m );
+ }
+ else {
+ // this function has a template -> restore output, apply the template and write the result out
+ FunctionArguments functionArguments = ( FunctionArguments ) writer; // TODO: Downcast to avoid using an interface? Yuck.
+ writer = ( SqlWriter ) outputStack.removeFirst();
+ out( template.render( functionArguments.getArgs(), sessionFactory ) );
+ }
+ }
+
+ // --- Inner classes (moved here from sql-gen.g) ---
+
+ /**
+ * Writes SQL fragments.
+ */
+ interface SqlWriter {
+ void clause(String clause);
+
+ /**
+ * todo remove this hack
+ * The parameter is either ", " or " , ". This is needed to pass sql generating tests as the old
+ * sql generator uses " , " in the WHERE and ", " in SELECT.
+ *
+ * @param comma either " , " or ", "
+ */
+ void commaBetweenParameters(String comma);
+ }
+
+ /**
+ * SQL function processing code redirects generated SQL output to an instance of this class
+ * which catches function arguments.
+ */
+ class FunctionArguments implements SqlWriter {
+ private int argInd;
+ private final List args = new ArrayList( 3 );
+
+ public void clause(String clause) {
+ if ( argInd == args.size() ) {
+ args.add( clause );
+ }
+ else {
+ args.set( argInd, args.get( argInd ) + clause );
+ }
+ }
+
+ public void commaBetweenParameters(String comma) {
+ ++argInd;
+ }
+
+ public List getArgs() {
+ return args;
+ }
+ }
+
+ /**
+ * The default SQL writer.
+ */
+ class DefaultWriter implements SqlWriter {
+ public void clause(String clause) {
+ getStringBuffer().append( clause );
+ }
+
+ public void commaBetweenParameters(String comma) {
+ getStringBuffer().append( comma );
+ }
+ }
+
+ public static void panic() {
+ throw new QueryException( "TreeWalker: panic" );
+ }
+
+ protected void fromFragmentSeparator(AST a) {
+ // check two "adjecent" nodes at the top of the from-clause tree
+ AST next = a.getNextSibling();
+ if ( next == null || !hasText( a ) ) {
+ return;
+ }
+
+ FromElement left = ( FromElement ) a;
+ FromElement right = ( FromElement ) next;
+
+ ///////////////////////////////////////////////////////////////////////
+ // HACK ALERT !!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ // Attempt to work around "ghost" ImpliedFromElements that occasionally
+ // show up between the actual things being joined. This consistently
+ // occurs from index nodes (at least against many-to-many). Not sure
+ // if there are other conditions
+ //
+ // Essentially, look-ahead to the next FromElement that actually
+ // writes something to the SQL
+ while ( right != null && !hasText( right ) ) {
+ right = ( FromElement ) right.getNextSibling();
+ }
+ if ( right == null ) {
+ return;
+ }
+ ///////////////////////////////////////////////////////////////////////
+
+ if ( !hasText( right ) ) {
+ return;
+ }
+
+ if ( right.getRealOrigin() == left ||
+ ( right.getRealOrigin() != null && right.getRealOrigin() == left.getRealOrigin() ) ) {
+ // right represents a joins originating from left; or
+ // both right and left reprersent joins originating from the same FromElement
+ if ( right.getJoinSequence() != null && right.getJoinSequence().isThetaStyle() ) {
+ out( ", " );
+ }
+ else {
+ out( " " );
+ }
+ }
+ else {
+ // these are just two unrelated table references
+ out( ", " );
+ }
+ }
+
+ protected void nestedFromFragment(AST d, AST parent) {
+ // check a set of parent/child nodes in the from-clause tree
+ // to determine if a comma is required between them
+ if ( d != null && hasText( d ) ) {
+ if ( parent != null && hasText( parent ) ) {
+ // again, both should be FromElements
+ FromElement left = ( FromElement ) parent;
+ FromElement right = ( FromElement ) d;
+ if ( right.getRealOrigin() == left ) {
+ // right represents a joins originating from left...
+ if ( right.getJoinSequence() != null && right.getJoinSequence().isThetaStyle() ) {
+ out( ", " );
+ }
+ else {
+ out( " " );
+ }
+ }
+ else {
+ // not so sure this is even valid subtree. but if it was, it'd
+ // represent two unrelated table references...
+ out( ", " );
+ }
+ }
+ out( d );
+ }
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/exec/AbstractStatementExecutor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/exec/AbstractStatementExecutor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/exec/AbstractStatementExecutor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,227 @@
+// $Id: AbstractStatementExecutor.java 11288 2007-03-15 11:38:45Z steve.ebersole at jboss.com $
+package org.hibernate.hql.ast.exec;
+
+import java.sql.PreparedStatement;
+import java.sql.Connection;
+import java.sql.Statement;
+
+import org.hibernate.HibernateException;
+import org.hibernate.action.BulkOperationCleanupAction;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.transaction.Isolater;
+import org.hibernate.engine.transaction.IsolatedWork;
+import org.hibernate.event.EventSource;
+import org.hibernate.hql.ast.HqlSqlWalker;
+import org.hibernate.hql.ast.SqlGenerator;
+import org.hibernate.persister.entity.Queryable;
+import org.hibernate.sql.InsertSelect;
+import org.hibernate.sql.Select;
+import org.hibernate.sql.SelectFragment;
+import org.hibernate.util.StringHelper;
+
+import antlr.RecognitionException;
+import antlr.collections.AST;
+
+import org.apache.commons.logging.Log;
+
+/**
+ * Implementation of AbstractStatementExecutor.
+ *
+ * @author Steve Ebersole
+ */
+public abstract class AbstractStatementExecutor implements StatementExecutor {
+
+ private final Log log;
+ private final HqlSqlWalker walker;
+
+ public AbstractStatementExecutor(HqlSqlWalker walker, Log log) {
+ this.walker = walker;
+ this.log = log;
+ }
+
+ protected HqlSqlWalker getWalker() {
+ return walker;
+ }
+
+ protected SessionFactoryImplementor getFactory() {
+ return walker.getSessionFactoryHelper().getFactory();
+ }
+
+ protected abstract Queryable[] getAffectedQueryables();
+
+ protected String generateIdInsertSelect(Queryable persister, String tableAlias, AST whereClause) {
+ Select select = new Select( getFactory().getDialect() );
+ SelectFragment selectFragment = new SelectFragment()
+ .addColumns( tableAlias, persister.getIdentifierColumnNames(), persister.getIdentifierColumnNames() );
+ select.setSelectClause( selectFragment.toFragmentString().substring( 2 ) );
+
+ String rootTableName = persister.getTableName();
+ String fromJoinFragment = persister.fromJoinFragment( tableAlias, true, false );
+ String whereJoinFragment = persister.whereJoinFragment( tableAlias, true, false );
+
+ select.setFromClause( rootTableName + ' ' + tableAlias + fromJoinFragment );
+
+ if ( whereJoinFragment == null ) {
+ whereJoinFragment = "";
+ }
+ else {
+ whereJoinFragment = whereJoinFragment.trim();
+ if ( whereJoinFragment.startsWith( "and" ) ) {
+ whereJoinFragment = whereJoinFragment.substring( 4 );
+ }
+ }
+
+ String userWhereClause = "";
+ if ( whereClause.getNumberOfChildren() != 0 ) {
+ // If a where clause was specified in the update/delete query, use it to limit the
+ // returned ids here...
+ try {
+ SqlGenerator sqlGenerator = new SqlGenerator( getFactory() );
+ sqlGenerator.whereClause( whereClause );
+ userWhereClause = sqlGenerator.getSQL().substring( 7 ); // strip the " where "
+ }
+ catch ( RecognitionException e ) {
+ throw new HibernateException( "Unable to generate id select for DML operation", e );
+ }
+ if ( whereJoinFragment.length() > 0 ) {
+ whereJoinFragment += " and ";
+ }
+ }
+
+ select.setWhereClause( whereJoinFragment + userWhereClause );
+
+ InsertSelect insert = new InsertSelect( getFactory().getDialect() );
+ if ( getFactory().getSettings().isCommentsEnabled() ) {
+ insert.setComment( "insert-select for " + persister.getEntityName() + " ids" );
+ }
+ insert.setTableName( persister.getTemporaryIdTableName() );
+ insert.setSelect( select );
+ return insert.toStatementString();
+ }
+
+ protected String generateIdSubselect(Queryable persister) {
+ return "select " + StringHelper.join( ", ", persister.getIdentifierColumnNames() ) +
+ " from " + persister.getTemporaryIdTableName();
+ }
+
+ protected void createTemporaryTableIfNecessary(final Queryable persister, final SessionImplementor session) {
+ // Don't really know all the codes required to adequately decipher returned jdbc exceptions here.
+ // simply allow the failure to be eaten and the subsequent insert-selects/deletes should fail
+ IsolatedWork work = new IsolatedWork() {
+ public void doWork(Connection connection) throws HibernateException {
+ Statement stmnt = null;
+ try {
+ stmnt = connection.createStatement();
+ stmnt.executeUpdate( persister.getTemporaryIdTableDDL() );
+ }
+ catch( Throwable t ) {
+ log.debug( "unable to create temporary id table [" + t.getMessage() + "]" );
+ }
+ finally {
+ if ( stmnt != null ) {
+ try {
+ stmnt.close();
+ }
+ catch( Throwable ignore ) {
+ // ignore
+ }
+ }
+ }
+ }
+ };
+ if ( shouldIsolateTemporaryTableDDL() ) {
+ if ( getFactory().getSettings().isDataDefinitionInTransactionSupported() ) {
+ Isolater.doIsolatedWork( work, session );
+ }
+ else {
+ Isolater.doNonTransactedWork( work, session );
+ }
+ }
+ else {
+ work.doWork( session.getJDBCContext().getConnectionManager().getConnection() );
+ session.getJDBCContext().getConnectionManager().afterStatement();
+ }
+ }
+
+ protected void dropTemporaryTableIfNecessary(final Queryable persister, final SessionImplementor session) {
+ if ( getFactory().getDialect().dropTemporaryTableAfterUse() ) {
+ IsolatedWork work = new IsolatedWork() {
+ public void doWork(Connection connection) throws HibernateException {
+ Statement stmnt = null;
+ try {
+ stmnt = connection.createStatement();
+ stmnt.executeUpdate( "drop table " + persister.getTemporaryIdTableName() );
+ }
+ catch( Throwable t ) {
+ log.warn( "unable to drop temporary id table after use [" + t.getMessage() + "]" );
+ }
+ finally {
+ if ( stmnt != null ) {
+ try {
+ stmnt.close();
+ }
+ catch( Throwable ignore ) {
+ // ignore
+ }
+ }
+ }
+ }
+ };
+
+ if ( shouldIsolateTemporaryTableDDL() ) {
+ if ( getFactory().getSettings().isDataDefinitionInTransactionSupported() ) {
+ Isolater.doIsolatedWork( work, session );
+ }
+ else {
+ Isolater.doNonTransactedWork( work, session );
+ }
+ }
+ else {
+ work.doWork( session.getJDBCContext().getConnectionManager().getConnection() );
+ session.getJDBCContext().getConnectionManager().afterStatement();
+ }
+ }
+ else {
+ // at the very least cleanup the data :)
+ PreparedStatement ps = null;
+ try {
+ ps = session.getBatcher().prepareStatement( "delete from " + persister.getTemporaryIdTableName() );
+ ps.executeUpdate();
+ }
+ catch( Throwable t ) {
+ log.warn( "unable to cleanup temporary id table after use [" + t + "]" );
+ }
+ finally {
+ if ( ps != null ) {
+ try {
+ session.getBatcher().closeStatement( ps );
+ }
+ catch( Throwable ignore ) {
+ // ignore
+ }
+ }
+ }
+ }
+ }
+
+ protected void coordinateSharedCacheCleanup(SessionImplementor session) {
+ BulkOperationCleanupAction action = new BulkOperationCleanupAction( session, getAffectedQueryables() );
+
+ action.init();
+
+ if ( session.isEventSource() ) {
+ ( ( EventSource ) session ).getActionQueue().addAction( action );
+ }
+ }
+
+ protected boolean shouldIsolateTemporaryTableDDL() {
+ Boolean dialectVote = getFactory().getDialect().performTemporaryTableDDLInIsolation();
+ if ( dialectVote != null ) {
+ return dialectVote.booleanValue();
+ }
+ else {
+ return getFactory().getSettings().isDataDefinitionImplicitCommit();
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/exec/BasicExecutor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/exec/BasicExecutor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/exec/BasicExecutor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,96 @@
+// $Id: BasicExecutor.java 9242 2006-02-09 12:37:36Z steveebersole $
+package org.hibernate.hql.ast.exec;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.Iterator;
+
+import org.hibernate.HibernateException;
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.RowSelection;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.exception.JDBCExceptionHelper;
+import org.hibernate.hql.ast.HqlSqlWalker;
+import org.hibernate.hql.ast.QuerySyntaxException;
+import org.hibernate.hql.ast.SqlGenerator;
+import org.hibernate.param.ParameterSpecification;
+import org.hibernate.persister.entity.Queryable;
+
+import antlr.RecognitionException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Implementation of BasicExecutor.
+ *
+ * @author Steve Ebersole
+ */
+public class BasicExecutor extends AbstractStatementExecutor {
+ private static final Log log = LogFactory.getLog( BasicExecutor.class );
+
+ private final Queryable persister;
+ private final String sql;
+
+ public BasicExecutor(HqlSqlWalker walker, Queryable persister) {
+ super( walker, log );
+ this.persister = persister;
+ try {
+ SqlGenerator gen = new SqlGenerator( getFactory() );
+ gen.statement( walker.getAST() );
+ sql = gen.getSQL();
+ gen.getParseErrorHandler().throwQueryException();
+ }
+ catch ( RecognitionException e ) {
+ throw QuerySyntaxException.convert( e );
+ }
+ }
+
+ public String[] getSqlStatements() {
+ return new String[] { sql };
+ }
+
+ public int execute(QueryParameters parameters, SessionImplementor session) throws HibernateException {
+
+ coordinateSharedCacheCleanup( session );
+
+ PreparedStatement st = null;
+ RowSelection selection = parameters.getRowSelection();
+
+ try {
+ try {
+ st = session.getBatcher().prepareStatement( sql );
+ Iterator paramSpecifications = getWalker().getParameters().iterator();
+ int pos = 1;
+ while ( paramSpecifications.hasNext() ) {
+ final ParameterSpecification paramSpec = ( ParameterSpecification ) paramSpecifications.next();
+ pos += paramSpec.bind( st, parameters, session, pos );
+ }
+ if ( selection != null ) {
+ if ( selection.getTimeout() != null ) {
+ st.setQueryTimeout( selection.getTimeout().intValue() );
+ }
+ }
+
+ return st.executeUpdate();
+ }
+ finally {
+ if ( st != null ) {
+ session.getBatcher().closeStatement( st );
+ }
+ }
+ }
+ catch( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ getFactory().getSQLExceptionConverter(),
+ sqle,
+ "could not execute update query",
+ sql
+ );
+ }
+ }
+
+ protected Queryable[] getAffectedQueryables() {
+ return new Queryable[] { persister };
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/exec/MultiTableDeleteExecutor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/exec/MultiTableDeleteExecutor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/exec/MultiTableDeleteExecutor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,143 @@
+// $Id: MultiTableDeleteExecutor.java 11288 2007-03-15 11:38:45Z steve.ebersole at jboss.com $
+package org.hibernate.hql.ast.exec;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.Iterator;
+
+import org.hibernate.HibernateException;
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.exception.JDBCExceptionHelper;
+import org.hibernate.hql.ast.HqlSqlWalker;
+import org.hibernate.hql.ast.tree.DeleteStatement;
+import org.hibernate.hql.ast.tree.FromElement;
+import org.hibernate.param.ParameterSpecification;
+import org.hibernate.persister.entity.Queryable;
+import org.hibernate.sql.Delete;
+import org.hibernate.util.StringHelper;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Implementation of MultiTableDeleteExecutor.
+ *
+ * @author Steve Ebersole
+ */
+public class MultiTableDeleteExecutor extends AbstractStatementExecutor {
+ private static final Log log = LogFactory.getLog( MultiTableDeleteExecutor.class );
+
+ private final Queryable persister;
+ private final String idInsertSelect;
+ private final String[] deletes;
+
+ public MultiTableDeleteExecutor(HqlSqlWalker walker) {
+ super( walker, log );
+
+ if ( !walker.getSessionFactoryHelper().getFactory().getDialect().supportsTemporaryTables() ) {
+ throw new HibernateException( "cannot perform multi-table deletes using dialect not supporting temp tables" );
+ }
+
+ DeleteStatement deleteStatement = ( DeleteStatement ) walker.getAST();
+ FromElement fromElement = deleteStatement.getFromClause().getFromElement();
+ String bulkTargetAlias = fromElement.getTableAlias();
+ this.persister = fromElement.getQueryable();
+
+ this.idInsertSelect = generateIdInsertSelect( persister, bulkTargetAlias, deleteStatement.getWhereClause() );
+ log.trace( "Generated ID-INSERT-SELECT SQL (multi-table delete) : " + idInsertSelect );
+
+ String[] tableNames = persister.getConstraintOrderedTableNameClosure();
+ String[][] columnNames = persister.getContraintOrderedTableKeyColumnClosure();
+ String idSubselect = generateIdSubselect( persister );
+
+ deletes = new String[tableNames.length];
+ for ( int i = tableNames.length - 1; i >= 0; i-- ) {
+ // TODO : an optimization here would be to consider cascade deletes and not gen those delete statements;
+ // the difficulty is the ordering of the tables here vs the cascade attributes on the persisters ->
+ // the table info gotten here should really be self-contained (i.e., a class representation
+ // defining all the needed attributes), then we could then get an array of those
+ final Delete delete = new Delete()
+ .setTableName( tableNames[i] )
+ .setWhere( "(" + StringHelper.join( ", ", columnNames[i] ) + ") IN (" + idSubselect + ")" );
+ if ( getFactory().getSettings().isCommentsEnabled() ) {
+ delete.setComment( "bulk delete" );
+ }
+
+ deletes[i] = delete.toStatementString();
+ }
+ }
+
+ public String[] getSqlStatements() {
+ return deletes;
+ }
+
+ public int execute(QueryParameters parameters, SessionImplementor session) throws HibernateException {
+ coordinateSharedCacheCleanup( session );
+
+ createTemporaryTableIfNecessary( persister, session );
+
+ try {
+ // First, save off the pertinent ids, saving the number of pertinent ids for return
+ PreparedStatement ps = null;
+ int resultCount = 0;
+ try {
+ try {
+ ps = session.getBatcher().prepareStatement( idInsertSelect );
+ Iterator paramSpecifications = getWalker().getParameters().iterator();
+ int pos = 1;
+ while ( paramSpecifications.hasNext() ) {
+ final ParameterSpecification paramSpec = ( ParameterSpecification ) paramSpecifications.next();
+ pos += paramSpec.bind( ps, parameters, session, pos );
+ }
+ resultCount = ps.executeUpdate();
+ }
+ finally {
+ if ( ps != null ) {
+ session.getBatcher().closeStatement( ps );
+ }
+ }
+ }
+ catch( SQLException e ) {
+ throw JDBCExceptionHelper.convert(
+ getFactory().getSQLExceptionConverter(),
+ e,
+ "could not insert/select ids for bulk delete",
+ idInsertSelect
+ );
+ }
+
+ // Start performing the deletes
+ for ( int i = 0; i < deletes.length; i++ ) {
+ try {
+ try {
+ ps = session.getBatcher().prepareStatement( deletes[i] );
+ ps.executeUpdate();
+ }
+ finally {
+ if ( ps != null ) {
+ session.getBatcher().closeStatement( ps );
+ }
+ }
+ }
+ catch( SQLException e ) {
+ throw JDBCExceptionHelper.convert(
+ getFactory().getSQLExceptionConverter(),
+ e,
+ "error performing bulk delete",
+ deletes[i]
+ );
+ }
+ }
+
+ return resultCount;
+ }
+ finally {
+ dropTemporaryTableIfNecessary( persister, session );
+ }
+ }
+
+ protected Queryable[] getAffectedQueryables() {
+ return new Queryable[] { persister };
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/exec/MultiTableUpdateExecutor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/exec/MultiTableUpdateExecutor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/exec/MultiTableUpdateExecutor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,177 @@
+// $Id: MultiTableUpdateExecutor.java 11288 2007-03-15 11:38:45Z steve.ebersole at jboss.com $
+package org.hibernate.hql.ast.exec;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.hibernate.HibernateException;
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.exception.JDBCExceptionHelper;
+import org.hibernate.hql.ast.HqlSqlWalker;
+import org.hibernate.hql.ast.tree.AssignmentSpecification;
+import org.hibernate.hql.ast.tree.FromElement;
+import org.hibernate.hql.ast.tree.UpdateStatement;
+import org.hibernate.param.ParameterSpecification;
+import org.hibernate.persister.entity.Queryable;
+import org.hibernate.sql.Update;
+import org.hibernate.util.StringHelper;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Implementation of MultiTableUpdateExecutor.
+ *
+ * @author Steve Ebersole
+ */
+public class MultiTableUpdateExecutor extends AbstractStatementExecutor {
+ private static final Log log = LogFactory.getLog( MultiTableUpdateExecutor.class );
+
+ private final Queryable persister;
+ private final String idInsertSelect;
+ private final String[] updates;
+ private final ParameterSpecification[][] hqlParameters;
+
+ public MultiTableUpdateExecutor(HqlSqlWalker walker) {
+ super( walker, log );
+
+ if ( !walker.getSessionFactoryHelper().getFactory().getDialect().supportsTemporaryTables() ) {
+ throw new HibernateException( "cannot perform multi-table updates using dialect not supporting temp tables" );
+ }
+
+ UpdateStatement updateStatement = ( UpdateStatement ) walker.getAST();
+ FromElement fromElement = updateStatement.getFromClause().getFromElement();
+ String bulkTargetAlias = fromElement.getTableAlias();
+ this.persister = fromElement.getQueryable();
+
+ this.idInsertSelect = generateIdInsertSelect( persister, bulkTargetAlias, updateStatement.getWhereClause() );
+ log.trace( "Generated ID-INSERT-SELECT SQL (multi-table update) : " + idInsertSelect );
+
+ String[] tableNames = persister.getConstraintOrderedTableNameClosure();
+ String[][] columnNames = persister.getContraintOrderedTableKeyColumnClosure();
+
+ String idSubselect = generateIdSubselect( persister );
+ List assignmentSpecifications = walker.getAssignmentSpecifications();
+
+ updates = new String[tableNames.length];
+ hqlParameters = new ParameterSpecification[tableNames.length][];
+ for ( int tableIndex = 0; tableIndex < tableNames.length; tableIndex++ ) {
+ boolean affected = false;
+ List parameterList = new ArrayList();
+ Update update = new Update( getFactory().getDialect() )
+ .setTableName( tableNames[tableIndex] )
+ .setWhere( "(" + StringHelper.join( ", ", columnNames[tableIndex] ) + ") IN (" + idSubselect + ")" );
+ if ( getFactory().getSettings().isCommentsEnabled() ) {
+ update.setComment( "bulk update" );
+ }
+ final Iterator itr = assignmentSpecifications.iterator();
+ while ( itr.hasNext() ) {
+ final AssignmentSpecification specification = ( AssignmentSpecification ) itr.next();
+ if ( specification.affectsTable( tableNames[tableIndex] ) ) {
+ affected = true;
+ update.appendAssignmentFragment( specification.getSqlAssignmentFragment() );
+ if ( specification.getParameters() != null ) {
+ for ( int paramIndex = 0; paramIndex < specification.getParameters().length; paramIndex++ ) {
+ parameterList.add( specification.getParameters()[paramIndex] );
+ }
+ }
+ }
+ }
+ if ( affected ) {
+ updates[tableIndex] = update.toStatementString();
+ hqlParameters[tableIndex] = ( ParameterSpecification[] ) parameterList.toArray( new ParameterSpecification[0] );
+ }
+ }
+ }
+
+ public Queryable getAffectedQueryable() {
+ return persister;
+ }
+
+ public String[] getSqlStatements() {
+ return updates;
+ }
+
+ public int execute(QueryParameters parameters, SessionImplementor session) throws HibernateException {
+ coordinateSharedCacheCleanup( session );
+
+ createTemporaryTableIfNecessary( persister, session );
+
+ try {
+ // First, save off the pertinent ids, as the return value
+ PreparedStatement ps = null;
+ int resultCount = 0;
+ try {
+ try {
+ ps = session.getBatcher().prepareStatement( idInsertSelect );
+ int parameterStart = getWalker().getNumberOfParametersInSetClause();
+ List allParams = getWalker().getParameters();
+ Iterator whereParams = allParams.subList( parameterStart, allParams.size() ).iterator();
+ int sum = 1; // jdbc params are 1-based
+ while ( whereParams.hasNext() ) {
+ sum += ( ( ParameterSpecification ) whereParams.next() ).bind( ps, parameters, session, sum );
+ }
+ resultCount = ps.executeUpdate();
+ }
+ finally {
+ if ( ps != null ) {
+ session.getBatcher().closeStatement( ps );
+ }
+ }
+ }
+ catch( SQLException e ) {
+ throw JDBCExceptionHelper.convert(
+ getFactory().getSQLExceptionConverter(),
+ e,
+ "could not insert/select ids for bulk update",
+ idInsertSelect
+ );
+ }
+
+ // Start performing the updates
+ for ( int i = 0; i < updates.length; i++ ) {
+ if ( updates[i] == null ) {
+ continue;
+ }
+ try {
+ try {
+ ps = session.getBatcher().prepareStatement( updates[i] );
+ if ( hqlParameters[i] != null ) {
+ int position = 1; // jdbc params are 1-based
+ for ( int x = 0; x < hqlParameters[i].length; x++ ) {
+ position += hqlParameters[i][x].bind( ps, parameters, session, position );
+ }
+ }
+ ps.executeUpdate();
+ }
+ finally {
+ if ( ps != null ) {
+ session.getBatcher().closeStatement( ps );
+ }
+ }
+ }
+ catch( SQLException e ) {
+ throw JDBCExceptionHelper.convert(
+ getFactory().getSQLExceptionConverter(),
+ e,
+ "error performing bulk update",
+ updates[i]
+ );
+ }
+ }
+
+ return resultCount;
+ }
+ finally {
+ dropTemporaryTableIfNecessary( persister, session );
+ }
+ }
+
+ protected Queryable[] getAffectedQueryables() {
+ return new Queryable[] { persister };
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/exec/StatementExecutor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/exec/StatementExecutor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/exec/StatementExecutor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,28 @@
+// $Id: StatementExecutor.java 8631 2005-11-21 17:02:24Z steveebersole $
+package org.hibernate.hql.ast.exec;
+
+import org.hibernate.HibernateException;
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.SessionImplementor;
+
+/**
+ * Encapsulates the strategy required to execute various types of update, delete,
+ * and insert statements issued through HQL.
+ *
+ * @author Steve Ebersole
+ */
+public interface StatementExecutor {
+
+ public String[] getSqlStatements();
+
+ /**
+ * Execute the sql managed by this executor using the given parameters.
+ *
+ * @param parameters Essentially bind information for this processing.
+ * @param session The session originating the request.
+ * @return The number of entities updated/deleted.
+ * @throws HibernateException
+ */
+ public int execute(QueryParameters parameters, SessionImplementor session) throws HibernateException;
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,6 @@
+<html><head></head><body>
+<p>An ANTLR-based parser for Hibernate Query Language.</p>
+<p>
+ Classes in this package extend the ANTLR-generated parser classes.
+</p>
+</body></html>
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/AbstractRestrictableStatement.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/AbstractRestrictableStatement.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/AbstractRestrictableStatement.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,64 @@
+// $Id: AbstractRestrictableStatement.java 7460 2005-07-12 20:27:29Z steveebersole $
+package org.hibernate.hql.ast.tree;
+
+import org.hibernate.hql.antlr.HqlSqlTokenTypes;
+import org.hibernate.hql.ast.util.ASTUtil;
+
+import antlr.collections.AST;
+
+import org.apache.commons.logging.Log;
+
+/**
+ * Convenience implementation of RestrictableStatement to centralize common functionality.
+ *
+ * @author Steve Ebersole
+ */
+public abstract class AbstractRestrictableStatement extends AbstractStatement implements RestrictableStatement {
+
+ private FromClause fromClause;
+ private AST whereClause;
+
+ protected abstract int getWhereClauseParentTokenType();
+ protected abstract Log getLog();
+
+ /**
+ * @see org.hibernate.hql.ast.tree.RestrictableStatement#getFromClause
+ */
+ public final FromClause getFromClause() {
+ if ( fromClause == null ) {
+ fromClause = ( FromClause ) ASTUtil.findTypeInChildren( this, HqlSqlTokenTypes.FROM );
+ }
+ return fromClause;
+ }
+
+ /**
+ * @see RestrictableStatement#hasWhereClause
+ */
+ public final boolean hasWhereClause() {
+ AST whereClause = locateWhereClause();
+ return whereClause != null && whereClause.getNumberOfChildren() > 0;
+ }
+
+ /**
+ * @see org.hibernate.hql.ast.tree.RestrictableStatement#getWhereClause
+ */
+ public final AST getWhereClause() {
+ if ( whereClause == null ) {
+ whereClause = locateWhereClause();
+ // If there is no WHERE node, make one.
+ if ( whereClause == null ) {
+ getLog().debug( "getWhereClause() : Creating a new WHERE clause..." );
+ whereClause = ASTUtil.create( getWalker().getASTFactory(), HqlSqlTokenTypes.WHERE, "WHERE" );
+ // inject the WHERE after the parent
+ AST parent = ASTUtil.findTypeInChildren( this, getWhereClauseParentTokenType() );
+ whereClause.setNextSibling( parent.getNextSibling() );
+ parent.setNextSibling( whereClause );
+ }
+ }
+ return whereClause;
+ }
+
+ protected AST locateWhereClause() {
+ return ASTUtil.findTypeInChildren( this, HqlSqlTokenTypes.WHERE );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/AbstractSelectExpression.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/AbstractSelectExpression.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/AbstractSelectExpression.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,43 @@
+// $Id: AbstractSelectExpression.java 7460 2005-07-12 20:27:29Z steveebersole $
+package org.hibernate.hql.ast.tree;
+
+import org.hibernate.type.Type;
+
+import antlr.SemanticException;
+
+/**
+ * Partial implementation of SelectExpression for all the nodes that aren't constructors.
+ *
+ * @author josh Nov 11, 2004 7:09:11 AM
+ */
+public abstract class AbstractSelectExpression extends HqlSqlWalkerNode implements SelectExpression {
+
+ private String alias;
+
+ public final void setAlias(String alias) {
+ this.alias = alias;
+ }
+
+ public final String getAlias() {
+ return alias;
+ }
+
+ public boolean isConstructor() {
+ return false;
+ }
+
+ public boolean isReturnableEntity() throws SemanticException {
+ return false;
+ }
+
+ public FromElement getFromElement() {
+ return null;
+ }
+
+ public boolean isScalar() throws SemanticException {
+ // Default implementation:
+ // If this node has a data type, and that data type is not an association, then this is scalar.
+ Type type = getDataType();
+ return type != null && !type.isAssociationType(); // Moved here from SelectClause [jsd]
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/AbstractStatement.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/AbstractStatement.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/AbstractStatement.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,32 @@
+// $Id: AbstractStatement.java 7486 2005-07-15 04:39:41Z oneovthafew $
+package org.hibernate.hql.ast.tree;
+
+import java.util.Iterator;
+
+/**
+ * Convenience implementation of Statement to centralize common functionality.
+ *
+ * @author Steve Ebersole
+ */
+public abstract class AbstractStatement extends HqlSqlWalkerNode implements DisplayableNode, Statement {
+
+ /**
+ * Returns additional display text for the AST node.
+ *
+ * @return String - The additional display text.
+ */
+ public String getDisplayText() {
+ StringBuffer buf = new StringBuffer();
+ if ( getWalker().getQuerySpaces().size() > 0 ) {
+ buf.append( " querySpaces (" );
+ for ( Iterator iterator = getWalker().getQuerySpaces().iterator(); iterator.hasNext(); ) {
+ buf.append( iterator.next() );
+ if ( iterator.hasNext() ) {
+ buf.append( "," );
+ }
+ }
+ buf.append( ")" );
+ }
+ return buf.toString();
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/AggregateNode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/AggregateNode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/AggregateNode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,27 @@
+// $Id: AggregateNode.java 7460 2005-07-12 20:27:29Z steveebersole $
+package org.hibernate.hql.ast.tree;
+
+import org.hibernate.hql.ast.util.ColumnHelper;
+import org.hibernate.type.Type;
+
+import antlr.SemanticException;
+
+/**
+ * Represents an aggregate function i.e. min, max, sum, avg.
+ *
+ * @author josh Sep 21, 2004 9:22:02 PM
+ */
+public class AggregateNode extends AbstractSelectExpression implements SelectExpression {
+
+ public AggregateNode() {
+ }
+
+ public Type getDataType() {
+ // Get the function return value type, based on the type of the first argument.
+ return getSessionFactoryHelper().findFunctionReturnType( getText(), getFirstChild() );
+ }
+
+ public void setScalarColumnText(int i) throws SemanticException {
+ ColumnHelper.generateSingleScalarColumn( this, i );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/AssignmentSpecification.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/AssignmentSpecification.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/AssignmentSpecification.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,143 @@
+// $Id: AssignmentSpecification.java 8273 2005-09-30 17:54:42Z steveebersole $
+package org.hibernate.hql.ast.tree;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Collections;
+
+import org.hibernate.QueryException;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.hql.antlr.HqlSqlTokenTypes;
+import org.hibernate.hql.ast.SqlGenerator;
+import org.hibernate.hql.ast.util.ASTUtil;
+import org.hibernate.param.ParameterSpecification;
+import org.hibernate.persister.entity.Queryable;
+import org.hibernate.persister.entity.UnionSubclassEntityPersister;
+
+import antlr.collections.AST;
+
+/**
+ * Encapsulates the information relating to an individual assignment within the
+ * set clause of an HQL update statement. This information is used during execution
+ * of the update statements when the updates occur against "multi-table" stuff.
+ *
+ * @author Steve Ebersole
+ */
+public class AssignmentSpecification {
+
+ private final Set tableNames;
+ private final ParameterSpecification[] hqlParameters;
+ private final AST eq;
+ private final SessionFactoryImplementor factory;
+
+ private String sqlAssignmentString;
+
+ public AssignmentSpecification(AST eq, Queryable persister) {
+ if ( eq.getType() != HqlSqlTokenTypes.EQ ) {
+ throw new QueryException( "assignment in set-clause not associated with equals" );
+ }
+
+ this.eq = eq;
+ this.factory = persister.getFactory();
+
+ // Needed to bump this up to DotNode, because that is the only thing which currently
+ // knows about the property-ref path in the correct format; it is either this, or
+ // recurse over the DotNodes constructing the property path just like DotNode does
+ // internally
+ DotNode lhs = ( DotNode ) eq.getFirstChild();
+ SqlNode rhs = ( SqlNode ) lhs.getNextSibling();
+
+ validateLhs( lhs );
+
+ final String propertyPath = lhs.getPropertyPath();
+ Set temp = new HashSet();
+ // yuck!
+ if ( persister instanceof UnionSubclassEntityPersister ) {
+ UnionSubclassEntityPersister usep = ( UnionSubclassEntityPersister ) persister;
+ String[] tables = persister.getConstraintOrderedTableNameClosure();
+ int size = tables.length;
+ for ( int i = 0; i < size; i ++ ) {
+ temp.add( tables[i] );
+ }
+ }
+ else {
+ temp.add(
+ persister.getSubclassTableName( persister.getSubclassPropertyTableNumber( propertyPath ) )
+ );
+ }
+ this.tableNames = Collections.unmodifiableSet( temp );
+
+ if (rhs==null) {
+ hqlParameters = new ParameterSpecification[0];
+ }
+ else if ( isParam( rhs ) ) {
+ hqlParameters = new ParameterSpecification[] { ( ( ParameterNode ) rhs ).getHqlParameterSpecification() };
+ }
+ else {
+ List parameterList = ASTUtil.collectChildren(
+ rhs,
+ new ASTUtil.IncludePredicate() {
+ public boolean include(AST node) {
+ return isParam( node );
+ }
+ }
+ );
+ hqlParameters = new ParameterSpecification[ parameterList.size() ];
+ Iterator itr = parameterList.iterator();
+ int i = 0;
+ while( itr.hasNext() ) {
+ hqlParameters[i++] = ( ( ParameterNode ) itr.next() ).getHqlParameterSpecification();
+ }
+ }
+ }
+
+ public boolean affectsTable(String tableName) {
+ return this.tableNames.contains( tableName );
+ }
+
+ public ParameterSpecification[] getParameters() {
+ return hqlParameters;
+ }
+
+ public String getSqlAssignmentFragment() {
+ if ( sqlAssignmentString == null ) {
+ try {
+ SqlGenerator sqlGenerator = new SqlGenerator( factory );
+ sqlGenerator.comparisonExpr( eq, false ); // false indicates to not generate parens around the assignment
+ sqlAssignmentString = sqlGenerator.getSQL();
+ }
+ catch( Throwable t ) {
+ throw new QueryException( "cannot interpret set-clause assignment" );
+ }
+ }
+ return sqlAssignmentString;
+ }
+
+ private static boolean isParam(AST node) {
+ return node.getType() == HqlSqlTokenTypes.PARAM || node.getType() == HqlSqlTokenTypes.NAMED_PARAM;
+ }
+
+ private void validateLhs(FromReferenceNode lhs) {
+ // make sure the lhs is "assignable"...
+ if ( !lhs.isResolved() ) {
+ throw new UnsupportedOperationException( "cannot validate assignablity of unresolved node" );
+ }
+
+ if ( lhs.getDataType().isCollectionType() ) {
+ throw new QueryException( "collections not assignable in update statements" );
+ }
+ else if ( lhs.getDataType().isComponentType() ) {
+ throw new QueryException( "Components currently not assignable in update statements" );
+ }
+ else if ( lhs.getDataType().isEntityType() ) {
+ // currently allowed...
+ }
+
+ // TODO : why aren't these the same?
+ if ( lhs.getImpliedJoin() != null || lhs.getFromElement().isImplied() ) {
+ throw new QueryException( "Implied join paths are not assignable in update statements" );
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/BetweenOperatorNode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/BetweenOperatorNode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/BetweenOperatorNode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,61 @@
+package org.hibernate.hql.ast.tree;
+
+import org.hibernate.type.Type;
+import org.hibernate.Hibernate;
+import antlr.SemanticException;
+
+/**
+ * Contract for nodes representing logcial BETWEEN (ternary) operators.
+ *
+ * @author Steve Ebersole
+ */
+public class BetweenOperatorNode extends SqlNode implements OperatorNode {
+
+ public void initialize() throws SemanticException {
+ Node fixture = getFixtureOperand();
+ if ( fixture == null ) {
+ throw new SemanticException( "fixture operand of a between operator was null" );
+ }
+ Node low = getLowOperand();
+ if ( low == null ) {
+ throw new SemanticException( "low operand of a between operator was null" );
+ }
+ Node high = getHighOperand();
+ if ( high == null ) {
+ throw new SemanticException( "high operand of a between operator was null" );
+ }
+ check( fixture, low, high );
+ check( low, high, fixture );
+ check( high, fixture, low );
+ }
+
+ public Type getDataType() {
+ // logic operators by definition resolve to boolean.
+ return Hibernate.BOOLEAN;
+ }
+
+ public Node getFixtureOperand() {
+ return ( Node ) getFirstChild();
+ }
+
+ public Node getLowOperand() {
+ return ( Node ) getFirstChild().getNextSibling();
+ }
+
+ public Node getHighOperand() {
+ return ( Node ) getFirstChild().getNextSibling().getNextSibling();
+ }
+
+ private void check(Node check, Node first, Node second) {
+ if ( ExpectedTypeAwareNode.class.isAssignableFrom( check.getClass() ) ) {
+ Type expectedType = null;
+ if ( SqlNode.class.isAssignableFrom( first.getClass() ) ) {
+ expectedType = ( ( SqlNode ) first ).getDataType();
+ }
+ if ( expectedType == null && SqlNode.class.isAssignableFrom( second.getClass() ) ) {
+ expectedType = ( ( SqlNode ) second ).getDataType();
+ }
+ ( ( ExpectedTypeAwareNode ) check ).setExpectedType( expectedType );
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/BinaryArithmeticOperatorNode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/BinaryArithmeticOperatorNode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/BinaryArithmeticOperatorNode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,191 @@
+//$Id: BinaryArithmeticOperatorNode.java 10000 2006-06-08 21:04:45Z steve.ebersole at jboss.com $
+package org.hibernate.hql.ast.tree;
+
+import org.hibernate.Hibernate;
+import org.hibernate.hql.ast.util.ColumnHelper;
+import org.hibernate.hql.antlr.HqlSqlTokenTypes;
+import org.hibernate.type.Type;
+
+import antlr.SemanticException;
+
+/**
+ * Nodes which represent binary arithmetic operators.
+ *
+ * @author Gavin King
+ */
+public class BinaryArithmeticOperatorNode extends AbstractSelectExpression implements BinaryOperatorNode, DisplayableNode {
+
+ public void initialize() throws SemanticException {
+ Node lhs = getLeftHandOperand();
+ Node rhs = getRightHandOperand();
+ if ( lhs == null ) {
+ throw new SemanticException( "left-hand operand of a binary operator was null" );
+ }
+ if ( rhs == null ) {
+ throw new SemanticException( "right-hand operand of a binary operator was null" );
+ }
+
+ Type lhType = ( lhs instanceof SqlNode ) ? ( ( SqlNode ) lhs ).getDataType() : null;
+ Type rhType = ( rhs instanceof SqlNode ) ? ( ( SqlNode ) rhs ).getDataType() : null;
+
+ if ( ExpectedTypeAwareNode.class.isAssignableFrom( lhs.getClass() ) && rhType != null ) {
+ Type expectedType = null;
+ // we have something like : "? [op] rhs"
+ if ( isDateTimeType( rhType ) ) {
+ // more specifically : "? [op] datetime"
+ // 1) if the operator is MINUS, the param needs to be of
+ // some datetime type
+ // 2) if the operator is PLUS, the param needs to be of
+ // some numeric type
+ expectedType = getType() == HqlSqlTokenTypes.PLUS ? Hibernate.DOUBLE : rhType;
+ }
+ else {
+ expectedType = rhType;
+ }
+ ( ( ExpectedTypeAwareNode ) lhs ).setExpectedType( expectedType );
+ }
+ else if ( ParameterNode.class.isAssignableFrom( rhs.getClass() ) && lhType != null ) {
+ Type expectedType = null;
+ // we have something like : "lhs [op] ?"
+ if ( isDateTimeType( lhType ) ) {
+ // more specifically : "datetime [op] ?"
+ // 1) if the operator is MINUS, we really cannot determine
+ // the expected type as either another datetime or
+ // numeric would be valid
+ // 2) if the operator is PLUS, the param needs to be of
+ // some numeric type
+ if ( getType() == HqlSqlTokenTypes.PLUS ) {
+ expectedType = Hibernate.DOUBLE;
+ }
+ }
+ else {
+ expectedType = lhType;
+ }
+ ( ( ExpectedTypeAwareNode ) rhs ).setExpectedType( expectedType );
+ }
+ }
+
+ /**
+ * Figure out the type of the binary expression by looking at
+ * the types of the operands. Sometimes we don't know both types,
+ * if, for example, one is a parameter.
+ */
+ public Type getDataType() {
+ if ( super.getDataType() == null ) {
+ super.setDataType( resolveDataType() );
+ }
+ return super.getDataType();
+ }
+
+ private Type resolveDataType() {
+ // TODO : we may also want to check that the types here map to exactly one column/JDBC-type
+ // can't think of a situation where arithmetic expression between multi-column mappings
+ // makes any sense.
+ Node lhs = getLeftHandOperand();
+ Node rhs = getRightHandOperand();
+ Type lhType = ( lhs instanceof SqlNode ) ? ( ( SqlNode ) lhs ).getDataType() : null;
+ Type rhType = ( rhs instanceof SqlNode ) ? ( ( SqlNode ) rhs ).getDataType() : null;
+ if ( isDateTimeType( lhType ) || isDateTimeType( rhType ) ) {
+ return resolveDateTimeArithmeticResultType( lhType, rhType );
+ }
+ else {
+ if ( lhType == null ) {
+ if ( rhType == null ) {
+ // we do not know either type
+ return Hibernate.DOUBLE; //BLIND GUESS!
+ }
+ else {
+ // we know only the rhs-hand type, so use that
+ return rhType;
+ }
+ }
+ else {
+ if ( rhType == null ) {
+ // we know only the lhs-hand type, so use that
+ return lhType;
+ }
+ else {
+ if ( lhType==Hibernate.DOUBLE || rhType==Hibernate.DOUBLE ) return Hibernate.DOUBLE;
+ if ( lhType==Hibernate.FLOAT || rhType==Hibernate.FLOAT ) return Hibernate.FLOAT;
+ if ( lhType==Hibernate.BIG_DECIMAL || rhType==Hibernate.BIG_DECIMAL ) return Hibernate.BIG_DECIMAL;
+ if ( lhType==Hibernate.BIG_INTEGER || rhType==Hibernate.BIG_INTEGER ) return Hibernate.BIG_INTEGER;
+ if ( lhType==Hibernate.LONG || rhType==Hibernate.LONG ) return Hibernate.LONG;
+ if ( lhType==Hibernate.INTEGER || rhType==Hibernate.INTEGER ) return Hibernate.INTEGER;
+ return lhType;
+ }
+ }
+ }
+ }
+
+ private boolean isDateTimeType(Type type) {
+ if ( type == null ) {
+ return false;
+ }
+ return java.util.Date.class.isAssignableFrom( type.getReturnedClass() ) ||
+ java.util.Calendar.class.isAssignableFrom( type.getReturnedClass() );
+ }
+
+ private Type resolveDateTimeArithmeticResultType(Type lhType, Type rhType) {
+ // here, we work under the following assumptions:
+ // ------------ valid cases --------------------------------------
+ // 1) datetime + {something other than datetime} : always results
+ // in a datetime ( db will catch invalid conversions )
+ // 2) datetime - datetime : always results in a DOUBLE
+ // 3) datetime - {something other than datetime} : always results
+ // in a datetime ( db will catch invalid conversions )
+ // ------------ invalid cases ------------------------------------
+ // 4) datetime + datetime
+ // 5) {something other than datetime} - datetime
+ // 6) datetime * {any type}
+ // 7) datetime / {any type}
+ // 8) {any type} / datetime
+ // doing so allows us to properly handle parameters as either the left
+ // or right side here in the majority of cases
+ boolean lhsIsDateTime = isDateTimeType( lhType );
+ boolean rhsIsDateTime = isDateTimeType( rhType );
+
+ // handle the (assumed) valid cases:
+ // #1 - the only valid datetime addition synatx is one or the other is a datetime (but not both)
+ if ( getType() == HqlSqlTokenTypes.PLUS ) {
+ // one or the other needs to be a datetime for us to get into this method in the first place...
+ return lhsIsDateTime ? lhType : rhType;
+ }
+ else if ( getType() == HqlSqlTokenTypes.MINUS ) {
+ // #3 - note that this is also true of "datetime - :param"...
+ if ( lhsIsDateTime && !rhsIsDateTime ) {
+ return lhType;
+ }
+ // #2
+ if ( lhsIsDateTime && rhsIsDateTime ) {
+ return Hibernate.DOUBLE;
+ }
+ }
+ return null;
+ }
+
+ public void setScalarColumnText(int i) throws SemanticException {
+ ColumnHelper.generateSingleScalarColumn( this, i );
+ }
+
+ /**
+ * Retrieves the left-hand operand of the operator.
+ *
+ * @return The left-hand operand
+ */
+ public Node getLeftHandOperand() {
+ return ( Node ) getFirstChild();
+ }
+
+ /**
+ * Retrieves the right-hand operand of the operator.
+ *
+ * @return The right-hand operand
+ */
+ public Node getRightHandOperand() {
+ return ( Node ) getFirstChild().getNextSibling();
+ }
+
+ public String getDisplayText() {
+ return "{dataType=" + getDataType() + "}";
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/BinaryLogicOperatorNode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/BinaryLogicOperatorNode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/BinaryLogicOperatorNode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,198 @@
+package org.hibernate.hql.ast.tree;
+
+import org.hibernate.type.Type;
+import org.hibernate.Hibernate;
+import org.hibernate.TypeMismatchException;
+import org.hibernate.HibernateException;
+import org.hibernate.util.StringHelper;
+import org.hibernate.hql.antlr.HqlSqlTokenTypes;
+import org.hibernate.dialect.HSQLDialect;
+import org.hibernate.engine.SessionFactoryImplementor;
+import antlr.SemanticException;
+import antlr.collections.AST;
+
+/**
+ * Contract for nodes representing binary operators.
+ *
+ * @author Steve Ebersole
+ */
+public class BinaryLogicOperatorNode extends HqlSqlWalkerNode implements BinaryOperatorNode {
+ /**
+ * Performs the operator node initialization by seeking out any parameter
+ * nodes and setting their expected type, if possible.
+ */
+ public void initialize() throws SemanticException {
+ Node lhs = getLeftHandOperand();
+ if ( lhs == null ) {
+ throw new SemanticException( "left-hand operand of a binary operator was null" );
+ }
+ Node rhs = getRightHandOperand();
+ if ( rhs == null ) {
+ throw new SemanticException( "right-hand operand of a binary operator was null" );
+ }
+
+ Type lhsType = extractDataType( lhs );
+ Type rhsType = extractDataType( rhs );
+
+ if ( lhsType == null ) {
+ lhsType = rhsType;
+ }
+ if ( rhsType == null ) {
+ rhsType = lhsType;
+ }
+
+ if ( ExpectedTypeAwareNode.class.isAssignableFrom( lhs.getClass() ) ) {
+ ( ( ExpectedTypeAwareNode ) lhs ).setExpectedType( rhsType );
+ }
+ if ( ExpectedTypeAwareNode.class.isAssignableFrom( rhs.getClass() ) ) {
+ ( ( ExpectedTypeAwareNode ) rhs ).setExpectedType( lhsType );
+ }
+
+ mutateRowValueConstructorSyntaxesIfNecessary( lhsType, rhsType );
+ }
+
+ protected final void mutateRowValueConstructorSyntaxesIfNecessary(Type lhsType, Type rhsType) {
+ // TODO : this really needs to be delayed unitl after we definitively know all node types
+ // where this is currently a problem is parameters for which where we cannot unequivocally
+ // resolve an expected type
+ SessionFactoryImplementor sessionFactory = getSessionFactoryHelper().getFactory();
+ if ( lhsType != null && rhsType != null ) {
+ int lhsColumnSpan = lhsType.getColumnSpan( sessionFactory );
+ if ( lhsColumnSpan != rhsType.getColumnSpan( sessionFactory ) ) {
+ throw new TypeMismatchException(
+ "left and right hand sides of a binary logic operator were incompatibile [" +
+ lhsType.getName() + " : "+ rhsType.getName() + "]"
+ );
+ }
+ if ( lhsColumnSpan > 1 ) {
+ // for dialects which are known to not support ANSI-SQL row-value-constructor syntax,
+ // we should mutate the tree.
+ if ( !sessionFactory.getDialect().supportsRowValueConstructorSyntax() ) {
+ mutateRowValueConstructorSyntax( lhsColumnSpan );
+ }
+ }
+ }
+ }
+
+ /**
+ * Mutate the subtree relating to a row-value-constructor to instead use
+ * a series of ANDed predicates. This allows multi-column type comparisons
+ * and explicit row-value-constructor syntax even on databases which do
+ * not support row-value-constructor.
+ * <p/>
+ * For example, here we'd mutate "... where (col1, col2) = ('val1', 'val2) ..." to
+ * "... where col1 = 'val1' and col2 = 'val2' ..."
+ *
+ * @param valueElements The number of elements in the row value constructor list.
+ */
+ private void mutateRowValueConstructorSyntax(int valueElements) {
+ // mutation depends on the types of nodes invloved...
+ int comparisonType = getType();
+ String comparisonText = getText();
+ setType( HqlSqlTokenTypes.AND );
+ setText( "AND" );
+ String[] lhsElementTexts = extractMutationTexts( getLeftHandOperand(), valueElements );
+ String[] rhsElementTexts = extractMutationTexts( getRightHandOperand(), valueElements );
+
+ AST container = this;
+ for ( int i = valueElements - 1; i > 0; i-- ) {
+
+ if ( i == 1 ) {
+ AST op1 = getASTFactory().create( comparisonType, comparisonText );
+ AST lhs1 = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, lhsElementTexts[0] );
+ AST rhs1 = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, rhsElementTexts[0] );
+ op1.setFirstChild( lhs1 );
+ lhs1.setNextSibling( rhs1 );
+ container.setFirstChild( op1 );
+ AST op2 = getASTFactory().create( comparisonType, comparisonText );
+ AST lhs2 = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, lhsElementTexts[1] );
+ AST rhs2 = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, rhsElementTexts[1] );
+ op2.setFirstChild( lhs2 );
+ lhs2.setNextSibling( rhs2 );
+ op1.setNextSibling( op2 );
+ }
+ else {
+ AST op = getASTFactory().create( comparisonType, comparisonText );
+ AST lhs = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, lhsElementTexts[i] );
+ AST rhs = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, rhsElementTexts[i] );
+ op.setFirstChild( lhs );
+ lhs.setNextSibling( rhs );
+ AST newContainer = getASTFactory().create( HqlSqlTokenTypes.AND, "AND" );
+ container.setFirstChild( newContainer );
+ newContainer.setNextSibling( op );
+ container = newContainer;
+ }
+ }
+ }
+
+ private static String[] extractMutationTexts(Node operand, int count) {
+ if ( operand instanceof ParameterNode ) {
+ String[] rtn = new String[count];
+ for ( int i = 0; i < count; i++ ) {
+ rtn[i] = "?";
+ }
+ return rtn;
+ }
+ else if ( operand.getType() == HqlSqlTokenTypes.VECTOR_EXPR ) {
+ String[] rtn = new String[ operand.getNumberOfChildren() ];
+ int x = 0;
+ AST node = operand.getFirstChild();
+ while ( node != null ) {
+ rtn[ x++ ] = node.getText();
+ node = node.getNextSibling();
+ }
+ return rtn;
+ }
+ else if ( operand instanceof SqlNode ) {
+ String nodeText = operand.getText();
+ if ( nodeText.startsWith( "(" ) ) {
+ nodeText = nodeText.substring( 1 );
+ }
+ if ( nodeText.endsWith( ")" ) ) {
+ nodeText = nodeText.substring( 0, nodeText.length() - 1 );
+ }
+ String[] splits = StringHelper.split( ", ", nodeText );
+ if ( count != splits.length ) {
+ throw new HibernateException( "SqlNode's text did not reference expected number of columns" );
+ }
+ return splits;
+ }
+ else {
+ throw new HibernateException( "dont know how to extract row value elements from node : " + operand );
+ }
+ }
+
+ protected Type extractDataType(Node operand) {
+ Type type = null;
+ if ( operand instanceof SqlNode ) {
+ type = ( ( SqlNode ) operand ).getDataType();
+ }
+ if ( type == null && operand instanceof ExpectedTypeAwareNode ) {
+ type = ( ( ExpectedTypeAwareNode ) operand ).getExpectedType();
+ }
+ return type;
+ }
+
+ public Type getDataType() {
+ // logic operators by definition resolve to booleans
+ return Hibernate.BOOLEAN;
+ }
+
+ /**
+ * Retrieves the left-hand operand of the operator.
+ *
+ * @return The left-hand operand
+ */
+ public Node getLeftHandOperand() {
+ return ( Node ) getFirstChild();
+ }
+
+ /**
+ * Retrieves the right-hand operand of the operator.
+ *
+ * @return The right-hand operand
+ */
+ public Node getRightHandOperand() {
+ return ( Node ) getFirstChild().getNextSibling();
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/BinaryOperatorNode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/BinaryOperatorNode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/BinaryOperatorNode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+package org.hibernate.hql.ast.tree;
+
+/**
+ * Contract for nodes representing binary operators.
+ *
+ * @author <a href="mailto:steve at hibernate.org">Steve Ebersole </a>
+ */
+public interface BinaryOperatorNode extends OperatorNode {
+ /**
+ * Retrieves the left-hand operand of the operator.
+ *
+ * @return The left-hand operand
+ */
+ public Node getLeftHandOperand();
+
+ /**
+ * Retrieves the right-hand operand of the operator.
+ *
+ * @return The right-hand operand
+ */
+ public Node getRightHandOperand();
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/BooleanLiteralNode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/BooleanLiteralNode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/BooleanLiteralNode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,50 @@
+package org.hibernate.hql.ast.tree;
+
+import org.hibernate.type.Type;
+import org.hibernate.type.BooleanType;
+import org.hibernate.Hibernate;
+import org.hibernate.QueryException;
+import org.hibernate.engine.SessionFactoryImplementor;
+
+/**
+ * Represents a boolean literal within a query.
+ *
+ * @author Steve Ebersole
+ */
+public class BooleanLiteralNode extends LiteralNode implements ExpectedTypeAwareNode {
+ private Type expectedType;
+
+ public Type getDataType() {
+ return expectedType == null ? Hibernate.BOOLEAN : expectedType;
+ }
+
+ public BooleanType getTypeInternal() {
+ return ( BooleanType ) getDataType();
+ }
+
+ public Boolean getValue() {
+ return getType() == TRUE ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ /**
+ * Expected-types really only pertinent here for boolean literals...
+ *
+ * @param expectedType
+ */
+ public void setExpectedType(Type expectedType) {
+ this.expectedType = expectedType;
+ }
+
+ public Type getExpectedType() {
+ return expectedType;
+ }
+
+ public String getRenderText(SessionFactoryImplementor sessionFactory) {
+ try {
+ return getTypeInternal().objectToSQLString( getValue(), sessionFactory.getDialect() );
+ }
+ catch( Throwable t ) {
+ throw new QueryException( "Unable to render boolean literal value", t );
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/Case2Node.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/Case2Node.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/Case2Node.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,28 @@
+// $Id: Case2Node.java 7460 2005-07-12 20:27:29Z steveebersole $
+package org.hibernate.hql.ast.tree;
+
+import org.hibernate.hql.ast.util.ColumnHelper;
+import org.hibernate.type.Type;
+
+import antlr.SemanticException;
+
+/**
+ * Represents a case ... when .. then ... else ... end expression in a select.
+ *
+ * @author Gavin King
+ */
+public class Case2Node extends AbstractSelectExpression implements SelectExpression {
+
+ public Type getDataType() {
+ return getFirstThenNode().getDataType();
+ }
+
+ private SelectExpression getFirstThenNode() {
+ return (SelectExpression) getFirstChild().getNextSibling().getFirstChild().getNextSibling();
+ }
+
+ public void setScalarColumnText(int i) throws SemanticException {
+ ColumnHelper.generateSingleScalarColumn( this, i );
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/CaseNode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/CaseNode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/CaseNode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,28 @@
+// $Id: CaseNode.java 7460 2005-07-12 20:27:29Z steveebersole $
+package org.hibernate.hql.ast.tree;
+
+import org.hibernate.hql.ast.util.ColumnHelper;
+import org.hibernate.type.Type;
+
+import antlr.SemanticException;
+
+/**
+ * Represents a case ... when .. then ... else ... end expression in a select.
+ *
+ * @author Gavin King
+ */
+public class CaseNode extends AbstractSelectExpression implements SelectExpression {
+
+ public Type getDataType() {
+ return getFirstThenNode().getDataType();
+ }
+
+ private SelectExpression getFirstThenNode() {
+ return (SelectExpression) getFirstChild().getFirstChild().getNextSibling();
+ }
+
+ public void setScalarColumnText(int i) throws SemanticException {
+ ColumnHelper.generateSingleScalarColumn( this, i );
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/CollectionFunction.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/CollectionFunction.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/CollectionFunction.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,33 @@
+// $Id: CollectionFunction.java 7460 2005-07-12 20:27:29Z steveebersole $
+package org.hibernate.hql.ast.tree;
+
+import antlr.SemanticException;
+import antlr.collections.AST;
+
+/**
+ * Represents 'elements()' or 'indices()'.
+ *
+ * @author josh Dec 6, 2004 8:36:42 AM
+ */
+public class CollectionFunction extends MethodNode implements DisplayableNode {
+ public void resolve(boolean inSelect) throws SemanticException {
+ initializeMethodNode( this, inSelect );
+ if ( !isCollectionPropertyMethod() ) {
+ throw new SemanticException( this.getText() + " is not a collection property name!" );
+ }
+ AST expr = getFirstChild();
+ if ( expr == null ) {
+ throw new SemanticException( this.getText() + " requires a path!" );
+ }
+ resolveCollectionProperty( expr );
+ }
+
+ protected void prepareSelectColumns(String[] selectColumns) {
+ // we need to strip off the embedded parens so that sql-gen does not double these up
+ String subselect = selectColumns[0].trim();
+ if ( subselect.startsWith( "(") && subselect.endsWith( ")" ) ) {
+ subselect = subselect.substring( 1, subselect.length() -1 );
+ }
+ selectColumns[0] = subselect;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/ConstructorNode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/ConstructorNode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/ConstructorNode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,158 @@
+// $Id: ConstructorNode.java 7460 2005-07-12 20:27:29Z steveebersole $
+package org.hibernate.hql.ast.tree;
+
+import java.lang.reflect.Constructor;
+import java.util.Arrays;
+import java.util.List;
+
+import org.hibernate.PropertyNotFoundException;
+import org.hibernate.hql.ast.DetailedSemanticException;
+import org.hibernate.type.Type;
+import org.hibernate.util.ReflectHelper;
+import org.hibernate.util.StringHelper;
+
+import antlr.SemanticException;
+import antlr.collections.AST;
+
+/**
+ * Represents a constructor (new) in a SELECT.
+ *
+ * @author josh Sep 24, 2004 6:46:08 PM
+ */
+public class ConstructorNode extends SelectExpressionList implements SelectExpression {
+
+ private Constructor constructor;
+ private Type[] constructorArgumentTypes;
+ private boolean isMap;
+ private boolean isList;
+
+ public boolean isMap() {
+ return isMap;
+ }
+
+ public boolean isList() {
+ return isList;
+ }
+
+ public String[] getAliases() {
+ SelectExpression[] selectExpressions = collectSelectExpressions();
+ String[] aliases = new String[selectExpressions.length] ;
+ for ( int i=0; i<selectExpressions.length; i++ ) {
+ String alias = selectExpressions[i].getAlias();
+ aliases[i] = alias==null ? Integer.toString(i) : alias;
+ }
+ return aliases;
+ }
+
+ public void setScalarColumnText(int i) throws SemanticException {
+ SelectExpression[] selectExpressions = collectSelectExpressions();
+ // Invoke setScalarColumnText on each constructor argument.
+ for ( int j = 0; j < selectExpressions.length; j++ ) {
+ SelectExpression selectExpression = selectExpressions[j];
+ selectExpression.setScalarColumnText( j );
+ }
+ }
+
+ protected AST getFirstSelectExpression() {
+ // Collect the select expressions, skip the first child because it is the class name.
+ return getFirstChild().getNextSibling();
+ }
+
+ /**
+ * @deprecated (tell clover to ignore this method)
+ */
+ public Type getDataType() {
+/*
+ // Return the type of the object created by the constructor.
+ AST firstChild = getFirstChild();
+ String text = firstChild.getText();
+ if ( firstChild.getType() == SqlTokenTypes.DOT ) {
+ DotNode dot = ( DotNode ) firstChild;
+ text = dot.getPath();
+ }
+ return getSessionFactoryHelper().requireEntityType( text );
+*/
+ throw new UnsupportedOperationException( "getDataType() is not supported by ConstructorNode!" );
+ }
+
+ public void prepare() throws SemanticException {
+ constructorArgumentTypes = resolveConstructorArgumentTypes();
+ String path = ( ( PathNode ) getFirstChild() ).getPath();
+ if ( "map".equals( path.toLowerCase() ) ) {
+ isMap = true;
+ }
+ else if ( "list".equals( path.toLowerCase() ) ) {
+ isList = true;
+ }
+ else {
+ constructor = resolveConstructor(path);
+ }
+ }
+
+ private Type[] resolveConstructorArgumentTypes() throws SemanticException {
+ SelectExpression[] argumentExpressions = collectSelectExpressions();
+ if ( argumentExpressions == null ) {
+ // return an empty Type array
+ return new Type[]{};
+ }
+
+ Type[] types = new Type[argumentExpressions.length];
+ for ( int x = 0; x < argumentExpressions.length; x++ ) {
+ types[x] = argumentExpressions[x].getDataType();
+ }
+ return types;
+ }
+
+ private Constructor resolveConstructor(String path) throws SemanticException {
+ String importedClassName = getSessionFactoryHelper().getImportedClassName( path );
+ String className = StringHelper.isEmpty( importedClassName ) ? path : importedClassName;
+ if ( className == null ) {
+ throw new SemanticException( "Unable to locate class [" + path + "]" );
+ }
+ try {
+ Class holderClass = ReflectHelper.classForName( className );
+ return ReflectHelper.getConstructor( holderClass, constructorArgumentTypes );
+ }
+ catch ( ClassNotFoundException e ) {
+ throw new DetailedSemanticException( "Unable to locate class [" + className + "]", e );
+ }
+ catch ( PropertyNotFoundException e ) {
+ // this is the exception returned by ReflectHelper.getConstructor() if it cannot
+ // locate an appropriate constructor
+ throw new DetailedSemanticException( "Unable to locate appropriate constructor on class [" + className + "]", e );
+ }
+ }
+
+ public Constructor getConstructor() {
+ return constructor;
+ }
+
+ public List getConstructorArgumentTypeList() {
+ return Arrays.asList( constructorArgumentTypes );
+ }
+
+ public FromElement getFromElement() {
+ return null;
+ }
+
+ public boolean isConstructor() {
+ return true;
+ }
+
+ public boolean isReturnableEntity() throws SemanticException {
+ return false;
+ }
+
+ public boolean isScalar() {
+ // Constructors are always considered scalar results.
+ return true;
+ }
+
+ public void setAlias(String alias) {
+ throw new UnsupportedOperationException("constructor may not be aliased");
+ }
+
+ public String getAlias() {
+ throw new UnsupportedOperationException("constructor may not be aliased");
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/CountNode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/CountNode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/CountNode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,24 @@
+// $Id: CountNode.java 7460 2005-07-12 20:27:29Z steveebersole $
+package org.hibernate.hql.ast.tree;
+
+import org.hibernate.hql.ast.util.ColumnHelper;
+import org.hibernate.type.Type;
+
+import antlr.SemanticException;
+
+/**
+ * Represents a COUNT expression in a select.
+ *
+ * @author josh Sep 21, 2004 9:23:40 PM
+ */
+public class CountNode extends AbstractSelectExpression implements SelectExpression {
+
+ public Type getDataType() {
+ return getSessionFactoryHelper().findFunctionReturnType( getText(), null );
+ }
+
+ public void setScalarColumnText(int i) throws SemanticException {
+ ColumnHelper.generateSingleScalarColumn( this, i );
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/DeleteStatement.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/DeleteStatement.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/DeleteStatement.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,40 @@
+// $Id: DeleteStatement.java 7460 2005-07-12 20:27:29Z steveebersole $
+package org.hibernate.hql.ast.tree;
+
+import org.hibernate.hql.antlr.HqlSqlTokenTypes;
+import org.hibernate.hql.antlr.SqlTokenTypes;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Defines a top-level AST node representing an HQL delete statement.
+ *
+ * @author Steve Ebersole
+ */
+public class DeleteStatement extends AbstractRestrictableStatement {
+
+ private static final Log log = LogFactory.getLog( DeleteStatement.class );
+
+ /**
+ * @see org.hibernate.hql.ast.tree.Statement#getStatementType()
+ */
+ public int getStatementType() {
+ return HqlSqlTokenTypes.DELETE;
+ }
+
+ /**
+ * @see org.hibernate.hql.ast.tree.Statement#needsExecutor()
+ */
+ public boolean needsExecutor() {
+ return true;
+ }
+
+ protected int getWhereClauseParentTokenType() {
+ return SqlTokenTypes.FROM;
+ }
+
+ protected Log getLog() {
+ return log;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/DisplayableNode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/DisplayableNode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/DisplayableNode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,16 @@
+// $Id: DisplayableNode.java 7460 2005-07-12 20:27:29Z steveebersole $
+package org.hibernate.hql.ast.tree;
+
+/**
+ * Implementors will return additional display text, which will be used
+ * by the ASTPrinter to display information (besides the node type and node
+ * text).
+ */
+public interface DisplayableNode {
+ /**
+ * Returns additional display text for the AST node.
+ *
+ * @return String - The additional display text.
+ */
+ String getDisplayText();
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/DotNode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/DotNode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/DotNode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,666 @@
+// $Id: DotNode.java 11378 2007-03-30 11:23:48Z steve.ebersole at jboss.com $
+package org.hibernate.hql.ast.tree;
+
+import org.hibernate.QueryException;
+import org.hibernate.engine.JoinSequence;
+import org.hibernate.hql.CollectionProperties;
+import org.hibernate.hql.antlr.SqlTokenTypes;
+import org.hibernate.hql.ast.util.ASTPrinter;
+import org.hibernate.hql.ast.util.ASTUtil;
+import org.hibernate.hql.ast.util.ColumnHelper;
+import org.hibernate.persister.collection.QueryableCollection;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.sql.JoinFragment;
+import org.hibernate.type.CollectionType;
+import org.hibernate.type.EntityType;
+import org.hibernate.type.Type;
+import org.hibernate.util.StringHelper;
+
+import antlr.SemanticException;
+import antlr.collections.AST;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Represents a reference to a property or alias expression. This should duplicate the relevant behaviors in
+ * PathExpressionParser.
+ *
+ * @author Joshua Davis
+ */
+public class DotNode extends FromReferenceNode implements DisplayableNode, SelectExpression {
+
+ ///////////////////////////////////////////////////////////////////////////
+ // USED ONLY FOR REGRESSION TESTING!!!!
+ //
+ // todo : obviously get rid of all this junk ;)
+ ///////////////////////////////////////////////////////////////////////////
+ public static boolean useThetaStyleImplicitJoins = false;
+ public static boolean REGRESSION_STYLE_JOIN_SUPPRESSION = false;
+ public static interface IllegalCollectionDereferenceExceptionBuilder {
+ public QueryException buildIllegalCollectionDereferenceException(String collectionPropertyName, FromReferenceNode lhs);
+ }
+ public static final IllegalCollectionDereferenceExceptionBuilder DEF_ILLEGAL_COLL_DEREF_EXCP_BUILDER = new IllegalCollectionDereferenceExceptionBuilder() {
+ public QueryException buildIllegalCollectionDereferenceException(String propertyName, FromReferenceNode lhs) {
+ String lhsPath = ASTUtil.getPathText( lhs );
+ return new QueryException( "illegal attempt to dereference collection [" + lhsPath + "] with element property reference [" + propertyName + "]" );
+ }
+ };
+ public static IllegalCollectionDereferenceExceptionBuilder ILLEGAL_COLL_DEREF_EXCP_BUILDER = DEF_ILLEGAL_COLL_DEREF_EXCP_BUILDER;
+ ///////////////////////////////////////////////////////////////////////////
+
+ private static final Log log = LogFactory.getLog( DotNode.class );
+
+ private static final int DEREF_UNKNOWN = 0;
+ private static final int DEREF_ENTITY = 1;
+ private static final int DEREF_COMPONENT = 2;
+ private static final int DEREF_COLLECTION = 3;
+ private static final int DEREF_PRIMITIVE = 4;
+ private static final int DEREF_IDENTIFIER = 5;
+ private static final int DEREF_JAVA_CONSTANT = 6;
+
+ /**
+ * The identifier that is the name of the property.
+ */
+ private String propertyName;
+ /**
+ * The full path, to the root alias of this dot node.
+ */
+ private String path;
+ /**
+ * The unresolved property path relative to this dot node.
+ */
+ private String propertyPath;
+
+ /**
+ * The column names that this resolves to.
+ */
+ private String[] columns;
+
+ /**
+ * The type of join to create. Default is an inner join.
+ */
+ private int joinType = JoinFragment.INNER_JOIN;
+
+ /**
+ * Fetch join or not.
+ */
+ private boolean fetch = false;
+
+ /**
+ * The type of dereference that hapened (DEREF_xxx).
+ */
+ private int dereferenceType = DEREF_UNKNOWN;
+
+ private FromElement impliedJoin;
+
+ /**
+ * Sets the join type for this '.' node structure.
+ *
+ * @param joinType The type of join to use.
+ * @see JoinFragment
+ */
+ public void setJoinType(int joinType) {
+ this.joinType = joinType;
+ }
+
+ private String[] getColumns() throws QueryException {
+ if ( columns == null ) {
+ // Use the table fromElement and the property name to get the array of column names.
+ String tableAlias = getLhs().getFromElement().getTableAlias();
+ columns = getFromElement().toColumns( tableAlias, propertyPath, false );
+ }
+ return columns;
+ }
+
+ public String getDisplayText() {
+ StringBuffer buf = new StringBuffer();
+ FromElement fromElement = getFromElement();
+ buf.append( "{propertyName=" ).append( propertyName );
+ buf.append( ",dereferenceType=" ).append( ASTPrinter.getConstantName( getClass(), dereferenceType ) );
+ buf.append( ",propertyPath=" ).append( propertyPath );
+ buf.append( ",path=" ).append( getPath() );
+ if ( fromElement != null ) {
+ buf.append( ",tableAlias=" ).append( fromElement.getTableAlias() );
+ buf.append( ",className=" ).append( fromElement.getClassName() );
+ buf.append( ",classAlias=" ).append( fromElement.getClassAlias() );
+ }
+ else {
+ buf.append( ",no from element" );
+ }
+ buf.append( '}' );
+ return buf.toString();
+ }
+
+ /**
+ * Resolves the left hand side of the DOT.
+ *
+ * @throws SemanticException
+ */
+ public void resolveFirstChild() throws SemanticException {
+ FromReferenceNode lhs = ( FromReferenceNode ) getFirstChild();
+ SqlNode property = ( SqlNode ) lhs.getNextSibling();
+
+ // Set the attributes of the property reference expression.
+ String propName = property.getText();
+ propertyName = propName;
+ // If the uresolved property path isn't set yet, just use the property name.
+ if ( propertyPath == null ) {
+ propertyPath = propName;
+ }
+ // Resolve the LHS fully, generate implicit joins. Pass in the property name so that the resolver can
+ // discover foreign key (id) properties.
+ lhs.resolve( true, true, null, this );
+ setFromElement( lhs.getFromElement() ); // The 'from element' that the property is in.
+
+ checkSubclassOrSuperclassPropertyReference( lhs, propName );
+ }
+
+ public void resolveInFunctionCall(boolean generateJoin, boolean implicitJoin) throws SemanticException {
+ if ( isResolved() ) {
+ return;
+ }
+ Type propertyType = prepareLhs(); // Prepare the left hand side and get the data type.
+ if ( propertyType!=null && propertyType.isCollectionType() ) {
+ resolveIndex(null);
+ }
+ else {
+ resolveFirstChild();
+ super.resolve(generateJoin, implicitJoin);
+ }
+ }
+
+
+ public void resolveIndex(AST parent) throws SemanticException {
+ if ( isResolved() ) {
+ return;
+ }
+ Type propertyType = prepareLhs(); // Prepare the left hand side and get the data type.
+ dereferenceCollection( ( CollectionType ) propertyType, true, true, null, parent );
+ }
+
+ public void resolve(boolean generateJoin, boolean implicitJoin, String classAlias, AST parent)
+ throws SemanticException {
+ // If this dot has already been resolved, stop now.
+ if ( isResolved() ) {
+ return;
+ }
+ Type propertyType = prepareLhs(); // Prepare the left hand side and get the data type.
+
+ // If there is no data type for this node, and we're at the end of the path (top most dot node), then
+ // this might be a Java constant.
+ if ( propertyType == null ) {
+ if ( parent == null ) {
+ getWalker().getLiteralProcessor().lookupConstant( this );
+ }
+ // If the propertyType is null and there isn't a parent, just
+ // stop now... there was a problem resolving the node anyway.
+ return;
+ }
+
+ if ( propertyType.isComponentType() ) {
+ // The property is a component...
+ checkLhsIsNotCollection();
+ dereferenceComponent( parent );
+ initText();
+ }
+ else if ( propertyType.isEntityType() ) {
+ // The property is another class..
+ checkLhsIsNotCollection();
+ dereferenceEntity( ( EntityType ) propertyType, implicitJoin, classAlias, generateJoin, parent );
+ initText();
+ }
+ else if ( propertyType.isCollectionType() ) {
+ // The property is a collection...
+ checkLhsIsNotCollection();
+ dereferenceCollection( ( CollectionType ) propertyType, implicitJoin, false, classAlias, parent );
+ }
+ else {
+ // Otherwise, this is a primitive type.
+ if ( ! CollectionProperties.isAnyCollectionProperty( propertyName ) ) {
+ checkLhsIsNotCollection();
+ }
+ dereferenceType = DEREF_PRIMITIVE;
+ initText();
+ }
+ setResolved();
+ }
+
+ private void initText() {
+ String[] cols = getColumns();
+ String text = StringHelper.join( ", ", cols );
+ if ( cols.length > 1 && getWalker().isComparativeExpressionClause() ) {
+ text = "(" + text + ")";
+ }
+ setText( text );
+ }
+
+ private Type prepareLhs() throws SemanticException {
+ FromReferenceNode lhs = getLhs();
+ lhs.prepareForDot( propertyName );
+ return getDataType();
+ }
+
+ private void dereferenceCollection(CollectionType collectionType, boolean implicitJoin, boolean indexed, String classAlias, AST parent)
+ throws SemanticException {
+
+ dereferenceType = DEREF_COLLECTION;
+ String role = collectionType.getRole();
+
+ //foo.bars.size (also handles deprecated stuff like foo.bars.maxelement for backwardness)
+ boolean isSizeProperty = getNextSibling()!=null &&
+ CollectionProperties.isAnyCollectionProperty( getNextSibling().getText() );
+
+ if ( isSizeProperty ) indexed = true; //yuck!
+
+ QueryableCollection queryableCollection = getSessionFactoryHelper().requireQueryableCollection( role );
+ String propName = getPath();
+ FromClause currentFromClause = getWalker().getCurrentFromClause();
+
+ if ( getWalker().getStatementType() != SqlTokenTypes.SELECT && indexed && classAlias == null ) {
+ // should indicate that we are processing an INSERT/UPDATE/DELETE
+ // query with a subquery implied via a collection property
+ // function. Here, we need to use the table name itself as the
+ // qualification alias.
+ // TODO : verify this works for all databases...
+ // TODO : is this also the case in non-"indexed" scenarios?
+ String alias = getLhs().getFromElement().getQueryable().getTableName();
+ columns = getFromElement().toColumns( alias, propertyPath, false, true );
+ }
+
+ //We do not look for an existing join on the same path, because
+ //it makes sense to join twice on the same collection role
+ FromElementFactory factory = new FromElementFactory(
+ currentFromClause,
+ getLhs().getFromElement(),
+ propName,
+ classAlias,
+ getColumns(),
+ implicitJoin
+ );
+ FromElement elem = factory.createCollection( queryableCollection, role, joinType, fetch, indexed );
+
+ if ( log.isDebugEnabled() ) {
+ log.debug( "dereferenceCollection() : Created new FROM element for " + propName + " : " + elem );
+ }
+
+ setImpliedJoin( elem );
+ setFromElement( elem ); // This 'dot' expression now refers to the resulting from element.
+
+ if ( isSizeProperty ) {
+ elem.setText("");
+ elem.setUseWhereFragment(false);
+ }
+
+ if ( !implicitJoin ) {
+ EntityPersister entityPersister = elem.getEntityPersister();
+ if ( entityPersister != null ) {
+ getWalker().addQuerySpaces( entityPersister.getQuerySpaces() );
+ }
+ }
+ getWalker().addQuerySpaces( queryableCollection.getCollectionSpaces() ); // Always add the collection's query spaces.
+ }
+
+ private void dereferenceEntity(EntityType entityType, boolean implicitJoin, String classAlias, boolean generateJoin, AST parent) throws SemanticException {
+ checkForCorrelatedSubquery( "dereferenceEntity" );
+ // three general cases we check here as to whether to render a physical SQL join:
+ // 1) is our parent a DotNode as well? If so, our property reference is
+ // being further de-referenced...
+ // 2) is this a DML statement
+ // 3) we were asked to generate any needed joins (generateJoins==true) *OR*
+ // we are currently processing a select or from clause
+ // (an additional check is the REGRESSION_STYLE_JOIN_SUPPRESSION check solely intended for the test suite)
+ //
+ // The REGRESSION_STYLE_JOIN_SUPPRESSION is an additional check
+ // intended solely for use within the test suite. This forces the
+ // implicit join resolution to behave more like the classic parser.
+ // The underlying issue is that classic translator is simply wrong
+ // about its decisions on whether or not to render an implicit join
+ // into a physical SQL join in a lot of cases. The piece it generally
+ // tends to miss is that INNER joins effect the results by further
+ // restricting the data set! A particular manifestation of this is
+ // the fact that the classic translator will skip the physical join
+ // for ToOne implicit joins *if the query is shallow*; the result
+ // being that Query.list() and Query.iterate() could return
+ // different number of results!
+ DotNode parentAsDotNode = null;
+ String property = propertyName;
+ final boolean joinIsNeeded;
+
+ if ( isDotNode( parent ) ) {
+ // our parent is another dot node, meaning we are being further dereferenced.
+ // thus we need to generate a join unless the parent refers to the associated
+ // entity's PK (because 'our' table would know the FK).
+ parentAsDotNode = ( DotNode ) parent;
+ property = parentAsDotNode.propertyName;
+ joinIsNeeded = generateJoin && !isReferenceToPrimaryKey( parentAsDotNode.propertyName, entityType );
+ }
+ else if ( ! getWalker().isSelectStatement() ) {
+ joinIsNeeded = false;
+ }
+ else if ( REGRESSION_STYLE_JOIN_SUPPRESSION ) {
+ // this is the regression style determination which matches the logic of the classic translator
+ joinIsNeeded = generateJoin && ( !getWalker().isInSelect() || !getWalker().isShallowQuery() );
+ }
+ else {
+ joinIsNeeded = generateJoin || ( getWalker().isInSelect() || getWalker().isInFrom() );
+ }
+
+ if ( joinIsNeeded ) {
+ dereferenceEntityJoin( classAlias, entityType, implicitJoin, parent );
+ }
+ else {
+ dereferenceEntityIdentifier( property, parentAsDotNode );
+ }
+
+ }
+
+ private boolean isDotNode(AST n) {
+ return n != null && n.getType() == SqlTokenTypes.DOT;
+ }
+
+ private void dereferenceEntityJoin(String classAlias, EntityType propertyType, boolean impliedJoin, AST parent)
+ throws SemanticException {
+ dereferenceType = DEREF_ENTITY;
+ if ( log.isDebugEnabled() ) {
+ log.debug( "dereferenceEntityJoin() : generating join for " + propertyName + " in "
+ + getFromElement().getClassName() + " "
+ + ( ( classAlias == null ) ? "{no alias}" : "(" + classAlias + ")" )
+ + " parent = " + ASTUtil.getDebugString( parent )
+ );
+ }
+ // Create a new FROM node for the referenced class.
+ String associatedEntityName = propertyType.getAssociatedEntityName();
+ String tableAlias = getAliasGenerator().createName( associatedEntityName );
+
+ String[] joinColumns = getColumns();
+ String joinPath = getPath();
+
+ if ( impliedJoin && getWalker().isInFrom() ) {
+ joinType = getWalker().getImpliedJoinType();
+ }
+
+ FromClause currentFromClause = getWalker().getCurrentFromClause();
+ FromElement elem = currentFromClause.findJoinByPath( joinPath );
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// This is the piece which recognizes the condition where an implicit join path
+// resolved earlier in a correlated subquery is now being referenced in the
+// outer query. For 3.0final, we just let this generate a second join (which
+// is exactly how the old parser handles this). Eventually we need to add this
+// logic back in and complete the logic in FromClause.promoteJoin; however,
+// FromClause.promoteJoin has its own difficulties (see the comments in
+// FromClause.promoteJoin).
+//
+// if ( elem == null ) {
+// // see if this joinPath has been used in a "child" FromClause, and if so
+// // promote that element to the outer query
+// FromClause currentNodeOwner = getFromElement().getFromClause();
+// FromClause currentJoinOwner = currentNodeOwner.locateChildFromClauseWithJoinByPath( joinPath );
+// if ( currentJoinOwner != null && currentNodeOwner != currentJoinOwner ) {
+// elem = currentJoinOwner.findJoinByPathLocal( joinPath );
+// if ( elem != null ) {
+// currentFromClause.promoteJoin( elem );
+// // EARLY EXIT!!!
+// return;
+// }
+// }
+// }
+//
+///////////////////////////////////////////////////////////////////////////////
+
+ if ( elem == null ) {
+ // If this is an implied join in a from element, then use the impled join type which is part of the
+ // tree parser's state (set by the gramamar actions).
+ JoinSequence joinSequence = getSessionFactoryHelper()
+ .createJoinSequence( impliedJoin, propertyType, tableAlias, joinType, joinColumns );
+
+ FromElementFactory factory = new FromElementFactory(
+ currentFromClause,
+ getLhs().getFromElement(),
+ joinPath,
+ classAlias,
+ joinColumns,
+ impliedJoin
+ );
+ elem = factory.createEntityJoin(
+ associatedEntityName,
+ tableAlias,
+ joinSequence,
+ fetch,
+ getWalker().isInFrom(),
+ propertyType
+ );
+ }
+ else {
+ currentFromClause.addDuplicateAlias(classAlias, elem);
+ }
+ setImpliedJoin( elem );
+ getWalker().addQuerySpaces( elem.getEntityPersister().getQuerySpaces() );
+ setFromElement( elem ); // This 'dot' expression now refers to the resulting from element.
+ }
+
+ private void setImpliedJoin(FromElement elem) {
+ this.impliedJoin = elem;
+ if ( getFirstChild().getType() == SqlTokenTypes.DOT ) {
+ DotNode dotLhs = ( DotNode ) getFirstChild();
+ if ( dotLhs.getImpliedJoin() != null ) {
+ this.impliedJoin = dotLhs.getImpliedJoin();
+ }
+ }
+ }
+
+ public FromElement getImpliedJoin() {
+ return impliedJoin;
+ }
+
+ /**
+ * Is the given property name a reference to the primary key of the associated
+ * entity construed by the given entity type?
+ * <p/>
+ * For example, consider a fragment like order.customer.id
+ * (where order is a from-element alias). Here, we'd have:
+ * propertyName = "id" AND
+ * owningType = ManyToOneType(Customer)
+ * and are being asked to determine whether "customer.id" is a reference
+ * to customer's PK...
+ *
+ * @param propertyName The name of the property to check.
+ * @param owningType The type represeting the entity "owning" the property
+ * @return True if propertyName references the entity's (owningType->associatedEntity)
+ * primary key; false otherwise.
+ */
+ private boolean isReferenceToPrimaryKey(String propertyName, EntityType owningType) {
+ EntityPersister persister = getSessionFactoryHelper()
+ .getFactory()
+ .getEntityPersister( owningType.getAssociatedEntityName() );
+ if ( persister.getEntityMetamodel().hasNonIdentifierPropertyNamedId() ) {
+ // only the identifier property field name can be a reference to the associated entity's PK...
+ return propertyName.equals( persister.getIdentifierPropertyName() ) && owningType.isReferenceToPrimaryKey();
+ }
+ else {
+ // here, we have two possibilities:
+ // 1) the property-name matches the explicitly identifier property name
+ // 2) the property-name matches the implicit 'id' property name
+ if ( EntityPersister.ENTITY_ID.equals( propertyName ) ) {
+ // the referenced node text is the special 'id'
+ return owningType.isReferenceToPrimaryKey();
+ }
+ else {
+ String keyPropertyName = getSessionFactoryHelper().getIdentifierOrUniqueKeyPropertyName( owningType );
+ return keyPropertyName != null && keyPropertyName.equals( propertyName ) && owningType.isReferenceToPrimaryKey();
+ }
+ }
+ }
+
+ private void checkForCorrelatedSubquery(String methodName) {
+ if ( isCorrelatedSubselect() ) {
+ if ( log.isDebugEnabled() ) {
+ log.debug( methodName + "() : correlated subquery" );
+ }
+ }
+ }
+
+ private boolean isCorrelatedSubselect() {
+ return getWalker().isSubQuery() &&
+ getFromElement().getFromClause() != getWalker().getCurrentFromClause();
+ }
+
+ private void checkLhsIsNotCollection() throws SemanticException {
+ if ( getLhs().getDataType() != null && getLhs().getDataType().isCollectionType() ) {
+ throw ILLEGAL_COLL_DEREF_EXCP_BUILDER.buildIllegalCollectionDereferenceException( propertyName, getLhs() );
+ }
+ }
+ private void dereferenceComponent(AST parent) {
+ dereferenceType = DEREF_COMPONENT;
+ setPropertyNameAndPath( parent );
+ }
+
+ private void dereferenceEntityIdentifier(String propertyName, DotNode dotParent) {
+ // special shortcut for id properties, skip the join!
+ // this must only occur at the _end_ of a path expression
+ if ( log.isDebugEnabled() ) {
+ log.debug( "dereferenceShortcut() : property " +
+ propertyName + " in " + getFromElement().getClassName() +
+ " does not require a join." );
+ }
+
+ initText();
+ setPropertyNameAndPath( dotParent ); // Set the unresolved path in this node and the parent.
+ // Set the text for the parent.
+ if ( dotParent != null ) {
+ dotParent.dereferenceType = DEREF_IDENTIFIER;
+ dotParent.setText( getText() );
+ dotParent.columns = getColumns();
+ }
+ }
+
+ private void setPropertyNameAndPath(AST parent) {
+ if ( isDotNode( parent ) ) {
+ DotNode dotNode = ( DotNode ) parent;
+ AST lhs = dotNode.getFirstChild();
+ AST rhs = lhs.getNextSibling();
+ propertyName = rhs.getText();
+ propertyPath = propertyPath + "." + propertyName; // Append the new property name onto the unresolved path.
+ dotNode.propertyPath = propertyPath;
+ if ( log.isDebugEnabled() ) {
+ log.debug( "Unresolved property path is now '" + dotNode.propertyPath + "'" );
+ }
+ }
+ else {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "terminal propertyPath = [" + propertyPath + "]" );
+ }
+ }
+ }
+
+ public Type getDataType() {
+ if ( super.getDataType() == null ) {
+ FromElement fromElement = getLhs().getFromElement();
+ if ( fromElement == null ) {
+ return null;
+ }
+ // If the lhs is a collection, use CollectionPropertyMapping
+ Type propertyType = fromElement.getPropertyType( propertyName, propertyPath );
+ if ( log.isDebugEnabled() ) {
+ log.debug( "getDataType() : " + propertyPath + " -> " + propertyType );
+ }
+ super.setDataType( propertyType );
+ }
+ return super.getDataType();
+ }
+
+ public void setPropertyPath(String propertyPath) {
+ this.propertyPath = propertyPath;
+ }
+
+ public String getPropertyPath() {
+ return propertyPath;
+ }
+
+ public FromReferenceNode getLhs() {
+ FromReferenceNode lhs = ( ( FromReferenceNode ) getFirstChild() );
+ if ( lhs == null ) {
+ throw new IllegalStateException( "DOT node with no left-hand-side!" );
+ }
+ return lhs;
+ }
+
+ /**
+ * Returns the full path of the node.
+ *
+ * @return the full path of the node.
+ */
+ public String getPath() {
+ if ( path == null ) {
+ FromReferenceNode lhs = getLhs();
+ if ( lhs == null ) {
+ path = getText();
+ }
+ else {
+ SqlNode rhs = ( SqlNode ) lhs.getNextSibling();
+ path = lhs.getPath() + "." + rhs.getOriginalText();
+ }
+ }
+ return path;
+ }
+
+ public void setFetch(boolean fetch) {
+ this.fetch = fetch;
+ }
+
+ public void setScalarColumnText(int i) throws SemanticException {
+ String[] sqlColumns = getColumns();
+ ColumnHelper.generateScalarColumns( this, sqlColumns, i );
+ }
+
+ /**
+ * Special method to resolve expressions in the SELECT list.
+ *
+ * @throws SemanticException if this cannot be resolved.
+ */
+ public void resolveSelectExpression() throws SemanticException {
+ if ( getWalker().isShallowQuery() || getWalker().getCurrentFromClause().isSubQuery() ) {
+ resolve(false, true);
+ }
+ else {
+ resolve(true, false);
+ Type type = getDataType();
+ if ( type.isEntityType() ) {
+ FromElement fromElement = getFromElement();
+ fromElement.setIncludeSubclasses( true ); // Tell the destination fromElement to 'includeSubclasses'.
+ if ( useThetaStyleImplicitJoins ) {
+ fromElement.getJoinSequence().setUseThetaStyle( true ); // Use theta style (for regression)
+ // Move the node up, after the origin node.
+ FromElement origin = fromElement.getOrigin();
+ if ( origin != null ) {
+ ASTUtil.makeSiblingOfParent( origin, fromElement );
+ }
+ }
+ }
+ }
+
+ FromReferenceNode lhs = getLhs();
+ while ( lhs != null ) {
+ checkSubclassOrSuperclassPropertyReference( lhs, lhs.getNextSibling().getText() );
+ lhs = ( FromReferenceNode ) lhs.getFirstChild();
+ }
+ }
+
+ public void setResolvedConstant(String text) {
+ path = text;
+ dereferenceType = DEREF_JAVA_CONSTANT;
+ setResolved(); // Don't resolve the node again.
+ }
+
+ private boolean checkSubclassOrSuperclassPropertyReference(FromReferenceNode lhs, String propertyName) {
+ if ( lhs != null && !( lhs instanceof IndexNode ) ) {
+ final FromElement source = lhs.getFromElement();
+ if ( source != null ) {
+ source.handlePropertyBeingDereferenced( lhs.getDataType(), propertyName );
+ }
+ }
+ return false;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/ExpectedTypeAwareNode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/ExpectedTypeAwareNode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/ExpectedTypeAwareNode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,14 @@
+package org.hibernate.hql.ast.tree;
+
+import org.hibernate.type.Type;
+
+/**
+ * Interface for nodes which wish to be made aware of any determined "expected
+ * type" based on the context within they appear in the query.
+ *
+ * @author Steve Ebersole
+ */
+public interface ExpectedTypeAwareNode {
+ public void setExpectedType(Type expectedType);
+ public Type getExpectedType();
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/FromClause.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/FromClause.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/FromClause.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,364 @@
+// $Id: FromClause.java 10172 2006-07-26 18:01:49Z steve.ebersole at jboss.com $
+package org.hibernate.hql.ast.tree;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.hql.antlr.HqlSqlTokenTypes;
+import org.hibernate.hql.ast.util.ASTIterator;
+import org.hibernate.hql.ast.util.ASTUtil;
+
+import antlr.SemanticException;
+import antlr.collections.AST;
+
+/**
+ * Represents the 'FROM' part of a query or subquery, containing all mapped class references.
+ *
+ * @author josh
+ */
+public class FromClause extends HqlSqlWalkerNode implements HqlSqlTokenTypes, DisplayableNode {
+ private static Log log = LogFactory.getLog( FromClause.class );
+ public static final int ROOT_LEVEL = 1;
+
+ private int level = ROOT_LEVEL;
+ private Set fromElements = new HashSet();
+ private Map fromElementByClassAlias = new HashMap();
+ private Map fromElementByTableAlias = new HashMap();
+ private Map fromElementsByPath = new HashMap();
+
+ /**
+ * All of the implicit FROM xxx JOIN yyy elements that are the destination of a collection. These are created from
+ * index operators on collection property references.
+ */
+ private Map collectionJoinFromElementsByPath = new HashMap();
+ /**
+ * Pointer to the parent FROM clause, if there is one.
+ */
+ private FromClause parentFromClause;
+ /**
+ * Collection of FROM clauses of which this is the parent.
+ */
+ private Set childFromClauses;
+ /**
+ * Counts the from elements as they are added.
+ */
+ private int fromElementCounter = 0;
+ /**
+ * Implied FROM elements to add onto the end of the FROM clause.
+ */
+ private List impliedElements = new LinkedList();
+
+ /**
+ * Adds a new from element to the from node.
+ *
+ * @param path The reference to the class.
+ * @param alias The alias AST.
+ * @return FromElement - The new FROM element.
+ */
+ public FromElement addFromElement(String path, AST alias) throws SemanticException {
+ // The path may be a reference to an alias defined in the parent query.
+ String classAlias = ( alias == null ) ? null : alias.getText();
+ checkForDuplicateClassAlias( classAlias );
+ FromElementFactory factory = new FromElementFactory( this, null, path, classAlias, null, false );
+ return factory.addFromElement();
+ }
+
+ void registerFromElement(FromElement element) {
+ fromElements.add( element );
+ String classAlias = element.getClassAlias();
+ if ( classAlias != null ) {
+ // The HQL class alias refers to the class name.
+ fromElementByClassAlias.put( classAlias, element );
+ }
+ // Associate the table alias with the element.
+ String tableAlias = element.getTableAlias();
+ if ( tableAlias != null ) {
+ fromElementByTableAlias.put( tableAlias, element );
+ }
+ }
+
+ void addDuplicateAlias(String alias, FromElement element) {
+ fromElementByClassAlias.put( alias, element );
+ }
+
+ private void checkForDuplicateClassAlias(String classAlias) throws SemanticException {
+ if ( classAlias != null && fromElementByClassAlias.containsKey( classAlias ) ) {
+ throw new SemanticException( "Duplicate definition of alias '"
+ + classAlias + "'" );
+ }
+ }
+
+ /**
+ * Retreives the from-element represented by the given alias.
+ *
+ * @param aliasOrClassName The alias by which to locate the from-element.
+ * @return The from-element assigned the given alias, or null if none.
+ */
+ public FromElement getFromElement(String aliasOrClassName) {
+ FromElement fromElement = ( FromElement ) fromElementByClassAlias.get( aliasOrClassName );
+ if ( fromElement == null && getSessionFactoryHelper().isStrictJPAQLComplianceEnabled() ) {
+ fromElement = findIntendedAliasedFromElementBasedOnCrazyJPARequirements( aliasOrClassName );
+ }
+ if ( fromElement == null && parentFromClause != null ) {
+ fromElement = parentFromClause.getFromElement( aliasOrClassName );
+ }
+ return fromElement;
+ }
+
+ private FromElement findIntendedAliasedFromElementBasedOnCrazyJPARequirements(String specifiedAlias) {
+ Iterator itr = fromElementByClassAlias.entrySet().iterator();
+ while ( itr.hasNext() ) {
+ Map.Entry entry = ( Map.Entry ) itr.next();
+ String alias = ( String ) entry.getKey();
+ if ( alias.equalsIgnoreCase( specifiedAlias ) ) {
+ return ( FromElement ) entry.getValue();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Convenience method to check whether a given token represents a from-element alias.
+ *
+ * @param possibleAlias The potential from-element alias to check.
+ * @return True if the possibleAlias is an alias to a from-element visible
+ * from this point in the query graph.
+ */
+ public boolean isFromElementAlias(String possibleAlias) {
+ boolean isAlias = containsClassAlias( possibleAlias );
+ if ( !isAlias && parentFromClause != null ) {
+ // try the parent FromClause...
+ isAlias = parentFromClause.isFromElementAlias( possibleAlias );
+ }
+ return isAlias;
+ }
+
+ /**
+ * Returns the list of from elements in order.
+ *
+ * @return the list of from elements (instances of FromElement).
+ */
+ public List getFromElements() {
+ return ASTUtil.collectChildren( this, fromElementPredicate );
+ }
+
+ public FromElement getFromElement() {
+ // TODO: not sure about this one
+// List fromElements = getFromElements();
+// if ( fromElements == null || fromElements.isEmpty() ) {
+// throw new QueryException( "Unable to locate from element" );
+// }
+ return (FromElement) getFromElements().get(0);
+ }
+
+ /**
+ * Returns the list of from elements that will be part of the result set.
+ *
+ * @return the list of from elements that will be part of the result set.
+ */
+ public List getProjectionList() {
+ return ASTUtil.collectChildren( this, projectionListPredicate );
+ }
+
+ public List getCollectionFetches() {
+ return ASTUtil.collectChildren( this, collectionFetchPredicate );
+ }
+
+ public boolean hasCollectionFecthes() {
+ return getCollectionFetches().size() > 0;
+ }
+
+ public List getExplicitFromElements() {
+ return ASTUtil.collectChildren( this, explicitFromPredicate );
+ }
+
+ private static ASTUtil.FilterPredicate fromElementPredicate = new ASTUtil.IncludePredicate() {
+ public boolean include(AST node) {
+ FromElement fromElement = ( FromElement ) node;
+ return fromElement.isFromOrJoinFragment();
+ }
+ };
+
+ private static ASTUtil.FilterPredicate projectionListPredicate = new ASTUtil.IncludePredicate() {
+ public boolean include(AST node) {
+ FromElement fromElement = ( FromElement ) node;
+ return fromElement.inProjectionList();
+ }
+ };
+
+ private static ASTUtil.FilterPredicate collectionFetchPredicate = new ASTUtil.IncludePredicate() {
+ public boolean include(AST node) {
+ FromElement fromElement = ( FromElement ) node;
+ return fromElement.isFetch() && fromElement.getQueryableCollection() != null;
+ }
+ };
+
+ private static ASTUtil.FilterPredicate explicitFromPredicate = new ASTUtil.IncludePredicate() {
+ public boolean include(AST node) {
+ final FromElement fromElement = ( FromElement ) node;
+ return !fromElement.isImplied();
+ }
+ };
+
+ FromElement findCollectionJoin(String path) {
+ return ( FromElement ) collectionJoinFromElementsByPath.get( path );
+ }
+
+ /**
+ * Look for an existing implicit or explicit join by the
+ * given path.
+ */
+ FromElement findJoinByPath(String path) {
+ FromElement elem = findJoinByPathLocal( path );
+ if ( elem == null && parentFromClause != null ) {
+ elem = parentFromClause.findJoinByPath( path );
+ }
+ return elem;
+ }
+
+ FromElement findJoinByPathLocal(String path) {
+ Map joinsByPath = fromElementsByPath;
+ return ( FromElement ) joinsByPath.get( path );
+ }
+
+ void addJoinByPathMap(String path, FromElement destination) {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "addJoinByPathMap() : " + path + " -> " + destination );
+ }
+ fromElementsByPath.put( path, destination );
+ }
+
+ /**
+ * Returns true if the from node contains the class alias name.
+ *
+ * @param alias The HQL class alias name.
+ * @return true if the from node contains the class alias name.
+ */
+ public boolean containsClassAlias(String alias) {
+ boolean isAlias = fromElementByClassAlias.containsKey( alias );
+ if ( !isAlias && getSessionFactoryHelper().isStrictJPAQLComplianceEnabled() ) {
+ isAlias = findIntendedAliasedFromElementBasedOnCrazyJPARequirements( alias ) != null;
+ }
+ return isAlias;
+ }
+
+ /**
+ * Returns true if the from node contains the table alias name.
+ *
+ * @param alias The SQL table alias name.
+ * @return true if the from node contains the table alias name.
+ */
+ public boolean containsTableAlias(String alias) {
+ return fromElementByTableAlias.keySet().contains( alias );
+ }
+
+ public String getDisplayText() {
+ return "FromClause{" +
+ "level=" + level +
+ ", fromElementCounter=" + fromElementCounter +
+ ", fromElements=" + fromElements.size() +
+ ", fromElementByClassAlias=" + fromElementByClassAlias.keySet() +
+ ", fromElementByTableAlias=" + fromElementByTableAlias.keySet() +
+ ", fromElementsByPath=" + fromElementsByPath.keySet() +
+ ", collectionJoinFromElementsByPath=" + collectionJoinFromElementsByPath.keySet() +
+ ", impliedElements=" + impliedElements +
+ "}";
+ }
+
+ public void setParentFromClause(FromClause parentFromClause) {
+ this.parentFromClause = parentFromClause;
+ if ( parentFromClause != null ) {
+ level = parentFromClause.getLevel() + 1;
+ parentFromClause.addChild( this );
+ }
+ }
+
+ private void addChild(FromClause fromClause) {
+ if ( childFromClauses == null ) {
+ childFromClauses = new HashSet();
+ }
+ childFromClauses.add( fromClause );
+ }
+
+ public FromClause locateChildFromClauseWithJoinByPath(String path) {
+ if ( childFromClauses != null && !childFromClauses.isEmpty() ) {
+ Iterator children = childFromClauses.iterator();
+ while ( children.hasNext() ) {
+ FromClause child = ( FromClause ) children.next();
+ if ( child.findJoinByPathLocal( path ) != null ) {
+ return child;
+ }
+ }
+ }
+ return null;
+ }
+
+ public void promoteJoin(FromElement elem) {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "Promoting [" + elem + "] to [" + this + "]" );
+ }
+ //TODO: implement functionality
+ // this might be painful to do here, as the "join post processing" for
+ // the subquery has already been performed (meaning that for
+ // theta-join dialects, the join conditions have already been moved
+ // over to the where clause). A "simple" solution here might to
+ // perform "join post processing" once for the entire query (including
+ // any subqueries) at one fell swoop
+ }
+
+ public boolean isSubQuery() {
+ // TODO : this is broke for subqueries in statements other than selects...
+ return parentFromClause != null;
+ }
+
+ void addCollectionJoinFromElementByPath(String path, FromElement destination) {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "addCollectionJoinFromElementByPath() : " + path + " -> " + destination );
+ }
+ collectionJoinFromElementsByPath.put( path, destination ); // Add the new node to the map so that we don't create it twice.
+ }
+
+ public FromClause getParentFromClause() {
+ return parentFromClause;
+ }
+
+ public int getLevel() {
+ return level;
+ }
+
+ public int nextFromElementCounter() {
+ return fromElementCounter++;
+ }
+
+ public void resolve() {
+ // Make sure that all from elements registered with this FROM clause are actually in the AST.
+ ASTIterator iter = new ASTIterator( this.getFirstChild() );
+ Set childrenInTree = new HashSet();
+ while ( iter.hasNext() ) {
+ childrenInTree.add( iter.next() );
+ }
+ for ( Iterator iterator = fromElements.iterator(); iterator.hasNext(); ) {
+ FromElement fromElement = ( FromElement ) iterator.next();
+ if ( !childrenInTree.contains( fromElement ) ) {
+ throw new IllegalStateException( "Element not in AST: " + fromElement );
+ }
+ }
+ }
+
+ public void addImpliedFromElement(FromElement element) {
+ impliedElements.add( element );
+ }
+
+ public String toString() {
+ return "FromClause{" +
+ "level=" + level +
+ "}";
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/FromElement.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/FromElement.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/FromElement.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,551 @@
+// $Id: FromElement.java 10852 2006-11-21 17:39:14Z steve.ebersole at jboss.com $
+package org.hibernate.hql.ast.tree;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.hibernate.QueryException;
+import org.hibernate.engine.JoinSequence;
+import org.hibernate.hql.QueryTranslator;
+import org.hibernate.hql.CollectionProperties;
+import org.hibernate.hql.antlr.SqlTokenTypes;
+import org.hibernate.hql.ast.util.ASTUtil;
+import org.hibernate.hql.ast.HqlSqlWalker;
+import org.hibernate.persister.collection.QueryableCollection;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.persister.entity.PropertyMapping;
+import org.hibernate.persister.entity.Queryable;
+import org.hibernate.type.EntityType;
+import org.hibernate.type.Type;
+import org.hibernate.util.StringHelper;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Represents a single mapped class mentioned in an HQL FROM clause. Each
+ * class reference will have the following symbols:
+ * <ul>
+ * <li>A class name - This is the name of the Java class that is mapped by Hibernate.</li>
+ * <li>[optional] an HQL alias for the mapped class.</li>
+ * <li>A table name - The name of the table that is mapped to the Java class.</li>
+ * <li>A table alias - The alias for the table that will be used in the resulting SQL.</li>
+ * </ul>
+ * <br>
+ * User: josh<br>
+ * Date: Dec 6, 2003<br>
+ * Time: 10:28:17 AM<br>
+ */
+public class FromElement extends HqlSqlWalkerNode implements DisplayableNode {
+ private static final Log log = LogFactory.getLog( FromElement.class );
+
+ private String className;
+ private String classAlias;
+ private String tableAlias;
+ private String collectionTableAlias;
+ private FromClause fromClause;
+ private boolean includeSubclasses = true;
+ private boolean collectionJoin = false;
+ private FromElement origin;
+ private String[] columns;
+ private String role;
+ private boolean fetch;
+ private boolean isAllPropertyFetch;
+ private boolean filter = false;
+ private int sequence = -1;
+ private boolean useFromFragment = false;
+ private boolean initialized = false;
+ private FromElementType elementType;
+ private boolean useWhereFragment = true;
+ private List destinations = new LinkedList();
+ private boolean manyToMany = false;
+ private String withClauseFragment = null;
+ private String withClauseJoinAlias;
+ private boolean dereferencedBySuperclassProperty;
+ private boolean dereferencedBySubclassProperty;
+
+ public FromElement() {
+ }
+
+ public String getCollectionSuffix() {
+ return elementType.getCollectionSuffix();
+ }
+
+ public void setCollectionSuffix(String suffix) {
+ elementType.setCollectionSuffix(suffix);
+ }
+
+ public void initializeCollection(FromClause fromClause, String classAlias, String tableAlias) {
+ doInitialize( fromClause, tableAlias, null, classAlias, null, null );
+ initialized = true;
+ }
+
+ public void initializeEntity(
+ FromClause fromClause,
+ String className,
+ EntityPersister persister,
+ EntityType type,
+ String classAlias,
+ String tableAlias) {
+ doInitialize( fromClause, tableAlias, className, classAlias, persister, type );
+ this.sequence = fromClause.nextFromElementCounter();
+ initialized = true;
+ }
+
+ private void doInitialize(FromClause fromClause, String tableAlias, String className, String classAlias,
+ EntityPersister persister, EntityType type) {
+ if ( initialized ) {
+ throw new IllegalStateException( "Already initialized!!" );
+ }
+ this.fromClause = fromClause;
+ this.tableAlias = tableAlias;
+ this.className = className;
+ this.classAlias = classAlias;
+ this.elementType = new FromElementType( this, persister, type );
+ // Register the FromElement with the FROM clause, now that we have the names and aliases.
+ fromClause.registerFromElement( this );
+ if ( log.isDebugEnabled() ) {
+ log.debug( fromClause + " : " + className + " ("
+ + ( classAlias == null ? "no alias" : classAlias ) + ") -> " + tableAlias );
+ }
+ }
+
+ public EntityPersister getEntityPersister() {
+ return elementType.getEntityPersister();
+ }
+
+ public Type getDataType() {
+ return elementType.getDataType();
+ }
+
+ public Type getSelectType() {
+ return elementType.getSelectType();
+ }
+
+ public Queryable getQueryable() {
+ return elementType.getQueryable();
+ }
+
+ public String getClassName() {
+ return className;
+ }
+
+ public String getClassAlias() {
+ return classAlias;
+ //return classAlias == null ? className : classAlias;
+ }
+
+ private String getTableName() {
+ Queryable queryable = getQueryable();
+ return ( queryable != null ) ? queryable.getTableName() : "{none}";
+ }
+
+ public String getTableAlias() {
+ return tableAlias;
+ }
+
+ /**
+ * Render the identifier select, but in a 'scalar' context (i.e. generate the column alias).
+ *
+ * @param i the sequence of the returned type
+ * @return the identifier select with the column alias.
+ */
+ String renderScalarIdentifierSelect(int i) {
+ return elementType.renderScalarIdentifierSelect( i );
+ }
+
+ void checkInitialized() {
+ if ( !initialized ) {
+ throw new IllegalStateException( "FromElement has not been initialized!" );
+ }
+ }
+
+ /**
+ * Returns the identifier select SQL fragment.
+ *
+ * @param size The total number of returned types.
+ * @param k The sequence of the current returned type.
+ * @return the identifier select SQL fragment.
+ */
+ String renderIdentifierSelect(int size, int k) {
+ return elementType.renderIdentifierSelect( size, k );
+ }
+
+ /**
+ * Returns the property select SQL fragment.
+ *
+ * @param size The total number of returned types.
+ * @param k The sequence of the current returned type.
+ * @return the property select SQL fragment.
+ */
+ String renderPropertySelect(int size, int k) {
+ return elementType.renderPropertySelect( size, k, isAllPropertyFetch );
+ }
+
+ String renderCollectionSelectFragment(int size, int k) {
+ return elementType.renderCollectionSelectFragment( size, k );
+ }
+
+ String renderValueCollectionSelectFragment(int size, int k) {
+ return elementType.renderValueCollectionSelectFragment( size, k );
+ }
+
+ public FromClause getFromClause() {
+ return fromClause;
+ }
+
+ /**
+ * Returns true if this FromElement was implied by a path, or false if this FROM element is explicitly declared in
+ * the FROM clause.
+ *
+ * @return true if this FromElement was implied by a path, or false if this FROM element is explicitly declared
+ */
+ public boolean isImplied() {
+ return false; // This is an explicit FROM element.
+ }
+
+ /**
+ * Returns additional display text for the AST node.
+ *
+ * @return String - The additional display text.
+ */
+ public String getDisplayText() {
+ StringBuffer buf = new StringBuffer();
+ buf.append( "FromElement{" );
+ appendDisplayText( buf );
+ buf.append( "}" );
+ return buf.toString();
+ }
+
+ protected void appendDisplayText(StringBuffer buf) {
+ buf.append( isImplied() ? (
+ isImpliedInFromClause() ? "implied in FROM clause" : "implied" )
+ : "explicit" );
+ buf.append( "," ).append( isCollectionJoin() ? "collection join" : "not a collection join" );
+ buf.append( "," ).append( fetch ? "fetch join" : "not a fetch join" );
+ buf.append( "," ).append( isAllPropertyFetch ? "fetch all properties" : "fetch non-lazy properties" );
+ buf.append( ",classAlias=" ).append( getClassAlias() );
+ buf.append( ",role=" ).append( role );
+ buf.append( ",tableName=" ).append( getTableName() );
+ buf.append( ",tableAlias=" ).append( getTableAlias() );
+ FromElement origin = getRealOrigin();
+ buf.append( ",origin=" ).append( origin == null ? "null" : origin.getText() );
+ buf.append( ",colums={" );
+ if ( columns != null ) {
+ for ( int i = 0; i < columns.length; i++ ) {
+ buf.append( columns[i] );
+ if ( i < columns.length ) {
+ buf.append( " " );
+ }
+ }
+ }
+ buf.append( ",className=" ).append( className );
+ buf.append( "}" );
+ }
+
+ public int hashCode() {
+ return super.hashCode();
+ }
+
+ public boolean equals(Object obj) {
+ return super.equals( obj );
+ }
+
+
+ public void setJoinSequence(JoinSequence joinSequence) {
+ elementType.setJoinSequence( joinSequence );
+ }
+
+ public JoinSequence getJoinSequence() {
+ return elementType.getJoinSequence();
+ }
+
+ public void setIncludeSubclasses(boolean includeSubclasses) {
+ if ( isDereferencedBySuperclassOrSubclassProperty() ) {
+ if ( !includeSubclasses && log.isTraceEnabled() ) {
+ log.trace( "attempt to disable subclass-inclusions", new Exception( "stack-trace source" ) );
+ }
+ }
+ this.includeSubclasses = includeSubclasses;
+ }
+
+ public boolean isIncludeSubclasses() {
+ return includeSubclasses;
+ }
+
+ public boolean isDereferencedBySuperclassOrSubclassProperty() {
+ return dereferencedBySubclassProperty || dereferencedBySuperclassProperty;
+ }
+
+ public String getIdentityColumn() {
+ checkInitialized();
+ String table = getTableAlias();
+ if ( table == null ) {
+ throw new IllegalStateException( "No table alias for node " + this );
+ }
+ String[] cols;
+ String propertyName;
+ if ( getEntityPersister() != null && getEntityPersister().getEntityMetamodel() != null
+ && getEntityPersister().getEntityMetamodel().hasNonIdentifierPropertyNamedId() ) {
+ propertyName = getEntityPersister().getIdentifierPropertyName();
+ }
+ else {
+ propertyName = EntityPersister.ENTITY_ID;
+ }
+ if ( getWalker().getStatementType() == HqlSqlWalker.SELECT ) {
+ cols = getPropertyMapping( propertyName ).toColumns( table, propertyName );
+ }
+ else {
+ cols = getPropertyMapping( propertyName ).toColumns( propertyName );
+ }
+ String result = StringHelper.join( ", ", cols );
+ return cols.length == 1 ? result : "(" + result + ")";
+ }
+
+ public void setCollectionJoin(boolean collectionJoin) {
+ this.collectionJoin = collectionJoin;
+ }
+
+ public boolean isCollectionJoin() {
+ return collectionJoin;
+ }
+
+ public void setRole(String role) {
+ this.role = role;
+ }
+
+ public void setQueryableCollection(QueryableCollection queryableCollection) {
+ elementType.setQueryableCollection( queryableCollection );
+ }
+
+ public QueryableCollection getQueryableCollection() {
+ return elementType.getQueryableCollection();
+ }
+
+ public void setColumns(String[] columns) {
+ this.columns = columns;
+ }
+
+ public void setOrigin(FromElement origin, boolean manyToMany) {
+ this.origin = origin;
+ this.manyToMany = manyToMany;
+ origin.addDestination( this );
+ if ( origin.getFromClause() == this.getFromClause() ) {
+ // TODO: Figure out a better way to get the FROM elements in a proper tree structure.
+ // If this is not the destination of a many-to-many, add it as a child of the origin.
+ if ( manyToMany ) {
+ ASTUtil.appendSibling( origin, this );
+ }
+ else {
+ if ( !getWalker().isInFrom() && !getWalker().isInSelect() ) {
+ getFromClause().addChild( this );
+ }
+ else {
+ origin.addChild( this );
+ }
+ }
+ }
+ else if ( !getWalker().isInFrom() ) {
+ // HHH-276 : implied joins in a subselect where clause - The destination needs to be added
+ // to the destination's from clause.
+ getFromClause().addChild( this ); // Not sure if this is will fix everything, but it works.
+ }
+ else {
+ // Otherwise, the destination node was implied by the FROM clause and the FROM clause processor
+ // will automatically add it in the right place.
+ }
+ }
+
+ public boolean isManyToMany() {
+ return manyToMany;
+ }
+
+ private void addDestination(FromElement fromElement) {
+ destinations.add( fromElement );
+ }
+
+ public List getDestinations() {
+ return destinations;
+ }
+
+ public FromElement getOrigin() {
+ return origin;
+ }
+
+ public FromElement getRealOrigin() {
+ if ( origin == null ) {
+ return null;
+ }
+ if ( origin.getText() == null || "".equals( origin.getText() ) ) {
+ return origin.getRealOrigin();
+ }
+ return origin;
+ }
+
+ public Type getPropertyType(String propertyName, String propertyPath) {
+ return elementType.getPropertyType( propertyName, propertyPath );
+ }
+
+ public String[] toColumns(String tableAlias, String path, boolean inSelect) {
+ return elementType.toColumns( tableAlias, path, inSelect );
+ }
+
+ public String[] toColumns(String tableAlias, String path, boolean inSelect, boolean forceAlias) {
+ return elementType.toColumns( tableAlias, path, inSelect, forceAlias );
+ }
+
+ public PropertyMapping getPropertyMapping(String propertyName) {
+ return elementType.getPropertyMapping( propertyName );
+ }
+
+ public void setFetch(boolean fetch) {
+ this.fetch = fetch;
+ // Fetch can't be used with scroll() or iterate().
+ if ( fetch && getWalker().isShallowQuery() ) {
+ throw new QueryException( QueryTranslator.ERROR_CANNOT_FETCH_WITH_ITERATE );
+ }
+ }
+
+ public boolean isFetch() {
+ return fetch;
+ }
+
+ public int getSequence() {
+ return sequence;
+ }
+
+ public void setFilter(boolean b) {
+ filter = b;
+ }
+
+ public boolean isFilter() {
+ return filter;
+ }
+
+ public boolean useFromFragment() {
+ checkInitialized();
+ // If it's not implied or it is implied and it's a many to many join where the target wasn't found.
+ return !isImplied() || this.useFromFragment;
+ }
+
+ public void setUseFromFragment(boolean useFromFragment) {
+ this.useFromFragment = useFromFragment;
+ }
+
+ public boolean useWhereFragment() {
+ return useWhereFragment;
+ }
+
+ public void setUseWhereFragment(boolean b) {
+ useWhereFragment = b;
+ }
+
+
+ public void setCollectionTableAlias(String collectionTableAlias) {
+ this.collectionTableAlias = collectionTableAlias;
+ }
+
+ public String getCollectionTableAlias() {
+ return collectionTableAlias;
+ }
+
+ public boolean isCollectionOfValuesOrComponents() {
+ return elementType.isCollectionOfValuesOrComponents();
+ }
+
+ public boolean isEntity() {
+ return elementType.isEntity();
+ }
+
+ public void setImpliedInFromClause(boolean flag) {
+ throw new UnsupportedOperationException( "Explicit FROM elements can't be implied in the FROM clause!" );
+ }
+
+ public boolean isImpliedInFromClause() {
+ return false; // Since this is an explicit FROM element, it can't be implied in the FROM clause.
+ }
+
+ public void setInProjectionList(boolean inProjectionList) {
+ // Do nothing, eplicit from elements are *always* in the projection list.
+ }
+
+ public boolean inProjectionList() {
+ return !isImplied() && isFromOrJoinFragment();
+ }
+
+ public boolean isFromOrJoinFragment() {
+ return getType() == SqlTokenTypes.FROM_FRAGMENT || getType() == SqlTokenTypes.JOIN_FRAGMENT;
+ }
+
+ public boolean isAllPropertyFetch() {
+ return isAllPropertyFetch;
+ }
+
+ public void setAllPropertyFetch(boolean fetch) {
+ isAllPropertyFetch = fetch;
+ }
+
+ public String getWithClauseFragment() {
+ return withClauseFragment;
+ }
+
+ public String getWithClauseJoinAlias() {
+ return withClauseJoinAlias;
+ }
+
+ public void setWithClauseFragment(String withClauseJoinAlias, String withClauseFragment) {
+ this.withClauseJoinAlias = withClauseJoinAlias;
+ this.withClauseFragment = withClauseFragment;
+ }
+
+ public boolean hasCacheablePersister() {
+ if ( getQueryableCollection() != null ) {
+ return getQueryableCollection().hasCache();
+ }
+ else {
+ return getQueryable().hasCache();
+ }
+ }
+
+ public void handlePropertyBeingDereferenced(Type propertySource, String propertyName) {
+ if ( getQueryableCollection() != null && CollectionProperties.isCollectionProperty( propertyName ) ) {
+ // propertyName refers to something like collection.size...
+ return;
+ }
+ if ( propertySource.isComponentType() ) {
+ // property name is a sub-path of a component...
+ return;
+ }
+
+ Queryable persister = getQueryable();
+ if ( persister != null ) {
+ try {
+ Queryable.Declarer propertyDeclarer = persister.getSubclassPropertyDeclarer( propertyName );
+ if ( log.isTraceEnabled() ) {
+ log.trace( "handling property dereference [" + persister.getEntityName() + " (" + getClassAlias() + ") -> " + propertyName + " (" + propertyDeclarer + ")]" );
+ }
+ if ( propertyDeclarer == Queryable.Declarer.SUBCLASS ) {
+ dereferencedBySubclassProperty = true;
+ includeSubclasses = true;
+ }
+ else if ( propertyDeclarer == Queryable.Declarer.SUPERCLASS ) {
+ dereferencedBySuperclassProperty = true;
+ }
+ }
+ catch( QueryException ignore ) {
+ // ignore it; the incoming property could not be found so we
+ // cannot be sure what to do here. At the very least, the
+ // safest is to simply not apply any dereference toggling...
+
+ }
+ }
+ }
+
+ public boolean isDereferencedBySuperclassProperty() {
+ return dereferencedBySuperclassProperty;
+ }
+
+ public boolean isDereferencedBySubclassProperty() {
+ return dereferencedBySubclassProperty;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/FromElementFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/FromElementFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/FromElementFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,500 @@
+// $Id: FromElementFactory.java 9586 2006-03-09 21:11:44Z steve.ebersole at jboss.com $
+package org.hibernate.hql.ast.tree;
+
+import org.hibernate.engine.JoinSequence;
+import org.hibernate.hql.antlr.SqlTokenTypes;
+import org.hibernate.hql.ast.util.ASTUtil;
+import org.hibernate.hql.ast.util.AliasGenerator;
+import org.hibernate.hql.ast.util.PathHelper;
+import org.hibernate.hql.ast.util.SessionFactoryHelper;
+import org.hibernate.persister.collection.QueryableCollection;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.persister.entity.Joinable;
+import org.hibernate.persister.entity.Queryable;
+import org.hibernate.sql.JoinFragment;
+import org.hibernate.type.AssociationType;
+import org.hibernate.type.CollectionType;
+import org.hibernate.type.EntityType;
+import org.hibernate.type.Type;
+import org.hibernate.util.StringHelper;
+
+import antlr.ASTFactory;
+import antlr.SemanticException;
+import antlr.collections.AST;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Encapsulates the creation of FromElements and JoinSequences.
+ *
+ * @author josh Oct 12, 2004 4:54:25 AM
+ */
+class FromElementFactory implements SqlTokenTypes {
+
+ private static final Log log = LogFactory.getLog( FromElementFactory.class );
+
+ private FromClause fromClause;
+ private FromElement origin;
+ private String path;
+
+ private String classAlias;
+ private String[] columns;
+ private boolean implied;
+ private boolean inElementsFunction;
+ private boolean collection;
+ private QueryableCollection queryableCollection;
+ private CollectionType collectionType;
+
+ /**
+ * Creates entity from elements.
+ */
+ public FromElementFactory(FromClause fromClause, FromElement origin, String path) {
+ this.fromClause = fromClause;
+ this.origin = origin;
+ this.path = path;
+ collection = false;
+ }
+
+ /**
+ * Creates collection from elements.
+ */
+ public FromElementFactory(
+ FromClause fromClause,
+ FromElement origin,
+ String path,
+ String classAlias,
+ String[] columns,
+ boolean implied) {
+ this( fromClause, origin, path );
+ this.classAlias = classAlias;
+ this.columns = columns;
+ this.implied = implied;
+ collection = true;
+ }
+
+ FromElement addFromElement() throws SemanticException {
+ FromClause parentFromClause = fromClause.getParentFromClause();
+ if ( parentFromClause != null ) {
+ // Look up class name using the first identifier in the path.
+ String pathAlias = PathHelper.getAlias( path );
+ FromElement parentFromElement = parentFromClause.getFromElement( pathAlias );
+ if ( parentFromElement != null ) {
+ return createFromElementInSubselect( path, pathAlias, parentFromElement, classAlias );
+ }
+ }
+
+ EntityPersister entityPersister = fromClause.getSessionFactoryHelper().requireClassPersister( path );
+
+ FromElement elem = createAndAddFromElement( path,
+ classAlias,
+ entityPersister,
+ ( EntityType ) ( ( Queryable ) entityPersister ).getType(),
+ null );
+
+ // Add to the query spaces.
+ fromClause.getWalker().addQuerySpaces( entityPersister.getQuerySpaces() );
+
+ return elem;
+ }
+
+ private FromElement createFromElementInSubselect(
+ String path,
+ String pathAlias,
+ FromElement parentFromElement,
+ String classAlias) throws SemanticException {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "createFromElementInSubselect() : path = " + path );
+ }
+ // Create an DotNode AST for the path and resolve it.
+ FromElement fromElement = evaluateFromElementPath( path, classAlias );
+ EntityPersister entityPersister = fromElement.getEntityPersister();
+
+ // If the first identifier in the path referrs to the class alias (not the class name), then this
+ // is a correlated subselect. If it's a correlated sub-select, use the existing table alias. Otherwise
+ // generate a new one.
+ String tableAlias = null;
+ boolean correlatedSubselect = pathAlias.equals( parentFromElement.getClassAlias() );
+ if ( correlatedSubselect ) {
+ tableAlias = fromElement.getTableAlias();
+ }
+ else {
+ tableAlias = null;
+ }
+
+ // If the from element isn't in the same clause, create a new from element.
+ if ( fromElement.getFromClause() != fromClause ) {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "createFromElementInSubselect() : creating a new FROM element..." );
+ }
+ fromElement = createFromElement( entityPersister );
+ initializeAndAddFromElement( fromElement,
+ path,
+ classAlias,
+ entityPersister,
+ ( EntityType ) ( ( Queryable ) entityPersister ).getType(),
+ tableAlias
+ );
+ }
+ if ( log.isDebugEnabled() ) {
+ log.debug( "createFromElementInSubselect() : " + path + " -> " + fromElement );
+ }
+ return fromElement;
+ }
+
+ private FromElement evaluateFromElementPath(String path, String classAlias) throws SemanticException {
+ ASTFactory factory = fromClause.getASTFactory();
+ FromReferenceNode pathNode = ( FromReferenceNode ) PathHelper.parsePath( path, factory );
+ pathNode.recursiveResolve( FromReferenceNode.ROOT_LEVEL, // This is the root level node.
+ false, // Generate an explicit from clause at the root.
+ classAlias,
+ null
+ );
+ if ( pathNode.getImpliedJoin() != null ) {
+ return pathNode.getImpliedJoin();
+ }
+ else {
+ return pathNode.getFromElement();
+ }
+ }
+
+ FromElement createCollectionElementsJoin(
+ QueryableCollection queryableCollection,
+ String collectionName) throws SemanticException {
+ JoinSequence collectionJoinSequence = fromClause.getSessionFactoryHelper()
+ .createCollectionJoinSequence( queryableCollection, collectionName );
+ this.queryableCollection = queryableCollection;
+ return createCollectionJoin( collectionJoinSequence, null );
+ }
+
+ FromElement createCollection(
+ QueryableCollection queryableCollection,
+ String role,
+ int joinType,
+ boolean fetchFlag,
+ boolean indexed)
+ throws SemanticException {
+ if ( !collection ) {
+ throw new IllegalStateException( "FromElementFactory not initialized for collections!" );
+ }
+ this.inElementsFunction = indexed;
+ FromElement elem;
+ this.queryableCollection = queryableCollection;
+ collectionType = queryableCollection.getCollectionType();
+ String roleAlias = fromClause.getAliasGenerator().createName( role );
+
+ // Correlated subqueries create 'special' implied from nodes
+ // because correlated subselects can't use an ANSI-style join
+ boolean explicitSubqueryFromElement = fromClause.isSubQuery() && !implied;
+ if ( explicitSubqueryFromElement ) {
+ String pathRoot = StringHelper.root( path );
+ FromElement origin = fromClause.getFromElement( pathRoot );
+ if ( origin == null || origin.getFromClause() != fromClause ) {
+ implied = true;
+ }
+ }
+
+ // super-duper-classic-parser-regression-testing-mojo-magic...
+ if ( explicitSubqueryFromElement && DotNode.useThetaStyleImplicitJoins ) {
+ implied = true;
+ }
+
+ Type elementType = queryableCollection.getElementType();
+ if ( elementType.isEntityType() ) { // A collection of entities...
+ elem = createEntityAssociation( role, roleAlias, joinType );
+ }
+ else if ( elementType.isComponentType() ) { // A collection of components...
+ JoinSequence joinSequence = createJoinSequence( roleAlias, joinType );
+ elem = createCollectionJoin( joinSequence, roleAlias );
+ }
+ else { // A collection of scalar elements...
+ JoinSequence joinSequence = createJoinSequence( roleAlias, joinType );
+ elem = createCollectionJoin( joinSequence, roleAlias );
+ }
+
+ elem.setRole( role );
+ elem.setQueryableCollection( queryableCollection );
+ // Don't include sub-classes for implied collection joins or subquery joins.
+ if ( implied ) {
+ elem.setIncludeSubclasses( false );
+ }
+
+ if ( explicitSubqueryFromElement ) {
+ elem.setInProjectionList( true ); // Treat explict from elements in sub-queries properly.
+ }
+
+ if ( fetchFlag ) {
+ elem.setFetch( true );
+ }
+ return elem;
+ }
+
+ FromElement createEntityJoin(
+ String entityClass,
+ String tableAlias,
+ JoinSequence joinSequence,
+ boolean fetchFlag,
+ boolean inFrom,
+ EntityType type) throws SemanticException {
+ FromElement elem = createJoin( entityClass, tableAlias, joinSequence, type, false );
+ elem.setFetch( fetchFlag );
+ EntityPersister entityPersister = elem.getEntityPersister();
+ int numberOfTables = entityPersister.getQuerySpaces().length;
+ if ( numberOfTables > 1 && implied && !elem.useFromFragment() ) {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "createEntityJoin() : Implied multi-table entity join" );
+ }
+ elem.setUseFromFragment( true );
+ }
+
+ // If this is an implied join in a FROM clause, then use ANSI-style joining, and set the
+ // flag on the FromElement that indicates that it was implied in the FROM clause itself.
+ if ( implied && inFrom ) {
+ joinSequence.setUseThetaStyle( false );
+ elem.setUseFromFragment( true );
+ elem.setImpliedInFromClause( true );
+ }
+ if ( elem.getWalker().isSubQuery() ) {
+ // two conditions where we need to transform this to a theta-join syntax:
+ // 1) 'elem' is the "root from-element" in correlated subqueries
+ // 2) The DotNode.useThetaStyleImplicitJoins has been set to true
+ // and 'elem' represents an implicit join
+ if ( elem.getFromClause() != elem.getOrigin().getFromClause() ||
+// ( implied && DotNode.useThetaStyleImplicitJoins ) ) {
+ DotNode.useThetaStyleImplicitJoins ) {
+ // the "root from-element" in correlated subqueries do need this piece
+ elem.setType( FROM_FRAGMENT );
+ joinSequence.setUseThetaStyle( true );
+ elem.setUseFromFragment( false );
+ }
+ }
+
+ return elem;
+ }
+
+ FromElement createElementJoin(QueryableCollection queryableCollection) throws SemanticException {
+ FromElement elem;
+
+ implied = true; //TODO: always true for now, but not if we later decide to support elements() in the from clause
+ inElementsFunction = true;
+ Type elementType = queryableCollection.getElementType();
+ if ( !elementType.isEntityType() ) {
+ throw new IllegalArgumentException( "Cannot create element join for a collection of non-entities!" );
+ }
+ this.queryableCollection = queryableCollection;
+ SessionFactoryHelper sfh = fromClause.getSessionFactoryHelper();
+ FromElement destination = null;
+ String tableAlias = null;
+ EntityPersister entityPersister = queryableCollection.getElementPersister();
+ tableAlias = fromClause.getAliasGenerator().createName( entityPersister.getEntityName() );
+ String associatedEntityName = entityPersister.getEntityName();
+ EntityPersister targetEntityPersister = sfh.requireClassPersister( associatedEntityName );
+ // Create the FROM element for the target (the elements of the collection).
+ destination = createAndAddFromElement(
+ associatedEntityName,
+ classAlias,
+ targetEntityPersister,
+ ( EntityType ) queryableCollection.getElementType(),
+ tableAlias
+ );
+ // If the join is implied, then don't include sub-classes on the element.
+ if ( implied ) {
+ destination.setIncludeSubclasses( false );
+ }
+ fromClause.addCollectionJoinFromElementByPath( path, destination );
+// origin.addDestination(destination);
+ // Add the query spaces.
+ fromClause.getWalker().addQuerySpaces( entityPersister.getQuerySpaces() );
+
+ CollectionType type = queryableCollection.getCollectionType();
+ String role = type.getRole();
+ String roleAlias = origin.getTableAlias();
+
+ String[] targetColumns = sfh.getCollectionElementColumns( role, roleAlias );
+ AssociationType elementAssociationType = sfh.getElementAssociationType( type );
+
+ // Create the join element under the from element.
+ int joinType = JoinFragment.INNER_JOIN;
+ JoinSequence joinSequence = sfh.createJoinSequence( implied, elementAssociationType, tableAlias, joinType, targetColumns );
+ elem = initializeJoin( path, destination, joinSequence, targetColumns, origin, false );
+ elem.setUseFromFragment( true ); // The associated entity is implied, but it must be included in the FROM.
+ elem.setCollectionTableAlias( roleAlias ); // The collection alias is the role.
+ return elem;
+ }
+
+ private FromElement createCollectionJoin(JoinSequence collectionJoinSequence, String tableAlias) throws SemanticException {
+ String text = queryableCollection.getTableName();
+ AST ast = createFromElement( text );
+ FromElement destination = ( FromElement ) ast;
+ Type elementType = queryableCollection.getElementType();
+ if ( elementType.isCollectionType() ) {
+ throw new SemanticException( "Collections of collections are not supported!" );
+ }
+ destination.initializeCollection( fromClause, classAlias, tableAlias );
+ destination.setType( JOIN_FRAGMENT ); // Tag this node as a JOIN.
+ destination.setIncludeSubclasses( false ); // Don't include subclasses in the join.
+ destination.setCollectionJoin( true ); // This is a clollection join.
+ destination.setJoinSequence( collectionJoinSequence );
+ destination.setOrigin( origin, false );
+ destination.setCollectionTableAlias(tableAlias);
+// origin.addDestination( destination );
+// This was the cause of HHH-242
+// origin.setType( FROM_FRAGMENT ); // Set the parent node type so that the AST is properly formed.
+ origin.setText( "" ); // The destination node will have all the FROM text.
+ origin.setCollectionJoin( true ); // The parent node is a collection join too (voodoo - see JoinProcessor)
+ fromClause.addCollectionJoinFromElementByPath( path, destination );
+ fromClause.getWalker().addQuerySpaces( queryableCollection.getCollectionSpaces() );
+ return destination;
+ }
+
+ private FromElement createEntityAssociation(
+ String role,
+ String roleAlias,
+ int joinType) throws SemanticException {
+ FromElement elem;
+ Queryable entityPersister = ( Queryable ) queryableCollection.getElementPersister();
+ String associatedEntityName = entityPersister.getEntityName();
+ // Get the class name of the associated entity.
+ if ( queryableCollection.isOneToMany() ) {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "createEntityAssociation() : One to many - path = " + path + " role = " + role + " associatedEntityName = " + associatedEntityName );
+ }
+ JoinSequence joinSequence = createJoinSequence( roleAlias, joinType );
+
+ elem = createJoin( associatedEntityName, roleAlias, joinSequence, ( EntityType ) queryableCollection.getElementType(), false );
+ }
+ else {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "createManyToMany() : path = " + path + " role = " + role + " associatedEntityName = " + associatedEntityName );
+ }
+ elem = createManyToMany( role, associatedEntityName,
+ roleAlias, entityPersister, ( EntityType ) queryableCollection.getElementType(), joinType );
+ fromClause.getWalker().addQuerySpaces( queryableCollection.getCollectionSpaces() );
+ }
+ elem.setCollectionTableAlias( roleAlias );
+ return elem;
+ }
+
+ private FromElement createJoin(
+ String entityClass,
+ String tableAlias,
+ JoinSequence joinSequence,
+ EntityType type,
+ boolean manyToMany) throws SemanticException {
+ // origin, path, implied, columns, classAlias,
+ EntityPersister entityPersister = fromClause.getSessionFactoryHelper().requireClassPersister( entityClass );
+ FromElement destination = createAndAddFromElement( entityClass,
+ classAlias,
+ entityPersister,
+ type,
+ tableAlias );
+ return initializeJoin( path, destination, joinSequence, getColumns(), origin, manyToMany );
+ }
+
+ private FromElement createManyToMany(
+ String role,
+ String associatedEntityName,
+ String roleAlias,
+ Queryable entityPersister,
+ EntityType type,
+ int joinType) throws SemanticException {
+ FromElement elem;
+ SessionFactoryHelper sfh = fromClause.getSessionFactoryHelper();
+ if ( inElementsFunction /*implied*/ ) {
+ // For implied many-to-many, just add the end join.
+ JoinSequence joinSequence = createJoinSequence( roleAlias, joinType );
+ elem = createJoin( associatedEntityName, roleAlias, joinSequence, type, true );
+ }
+ else {
+ // For an explicit many-to-many relationship, add a second join from the intermediate
+ // (many-to-many) table to the destination table. Also, make sure that the from element's
+ // idea of the destination is the destination table.
+ String tableAlias = fromClause.getAliasGenerator().createName( entityPersister.getEntityName() );
+ String[] secondJoinColumns = sfh.getCollectionElementColumns( role, roleAlias );
+ // Add the second join, the one that ends in the destination table.
+ JoinSequence joinSequence = createJoinSequence( roleAlias, joinType );
+ joinSequence.addJoin( sfh.getElementAssociationType( collectionType ), tableAlias, joinType, secondJoinColumns );
+ elem = createJoin( associatedEntityName, tableAlias, joinSequence, type, false );
+ elem.setUseFromFragment( true );
+ }
+ return elem;
+ }
+
+ private JoinSequence createJoinSequence(String roleAlias, int joinType) {
+ SessionFactoryHelper sessionFactoryHelper = fromClause.getSessionFactoryHelper();
+ String[] joinColumns = getColumns();
+ if ( collectionType == null ) {
+ throw new IllegalStateException( "collectionType is null!" );
+ }
+ return sessionFactoryHelper.createJoinSequence( implied, collectionType, roleAlias, joinType, joinColumns );
+ }
+
+ private FromElement createAndAddFromElement(
+ String className,
+ String classAlias,
+ EntityPersister entityPersister,
+ EntityType type,
+ String tableAlias) {
+ if ( !( entityPersister instanceof Joinable ) ) {
+ throw new IllegalArgumentException( "EntityPersister " + entityPersister + " does not implement Joinable!" );
+ }
+ FromElement element = createFromElement( entityPersister );
+ initializeAndAddFromElement( element, className, classAlias, entityPersister, type, tableAlias );
+ return element;
+ }
+
+ private void initializeAndAddFromElement(
+ FromElement element,
+ String className,
+ String classAlias,
+ EntityPersister entityPersister,
+ EntityType type,
+ String tableAlias) {
+ if ( tableAlias == null ) {
+ AliasGenerator aliasGenerator = fromClause.getAliasGenerator();
+ tableAlias = aliasGenerator.createName( entityPersister.getEntityName() );
+ }
+ element.initializeEntity( fromClause, className, entityPersister, type, classAlias, tableAlias );
+ }
+
+ private FromElement createFromElement(EntityPersister entityPersister) {
+ Joinable joinable = ( Joinable ) entityPersister;
+ String text = joinable.getTableName();
+ AST ast = createFromElement( text );
+ FromElement element = ( FromElement ) ast;
+ return element;
+ }
+
+ private AST createFromElement(String text) {
+ AST ast = ASTUtil.create( fromClause.getASTFactory(),
+ implied ? IMPLIED_FROM : FROM_FRAGMENT, // This causes the factory to instantiate the desired class.
+ text );
+ // Reset the node type, because the rest of the system is expecting FROM_FRAGMENT, all we wanted was
+ // for the factory to create the right sub-class. This might get reset again later on anyway to make the
+ // SQL generation simpler.
+ ast.setType( FROM_FRAGMENT );
+ return ast;
+ }
+
+ private FromElement initializeJoin(
+ String path,
+ FromElement destination,
+ JoinSequence joinSequence,
+ String[] columns,
+ FromElement origin,
+ boolean manyToMany) {
+ destination.setType( JOIN_FRAGMENT );
+ destination.setJoinSequence( joinSequence );
+ destination.setColumns( columns );
+ destination.setOrigin( origin, manyToMany );
+ fromClause.addJoinByPathMap( path, destination );
+ return destination;
+ }
+
+ private String[] getColumns() {
+ if ( columns == null ) {
+ throw new IllegalStateException( "No foriegn key columns were supplied!" );
+ }
+ return columns;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/FromElementType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/FromElementType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/FromElementType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,415 @@
+// $Id: FromElementType.java 10824 2006-11-16 19:32:48Z steve.ebersole at jboss.com $
+package org.hibernate.hql.ast.tree;
+
+import java.util.Map;
+
+import org.hibernate.MappingException;
+import org.hibernate.QueryException;
+import org.hibernate.util.ArrayHelper;
+import org.hibernate.engine.JoinSequence;
+import org.hibernate.hql.CollectionProperties;
+import org.hibernate.hql.CollectionSubqueryFactory;
+import org.hibernate.hql.NameGenerator;
+import org.hibernate.hql.antlr.HqlSqlTokenTypes;
+import org.hibernate.persister.collection.CollectionPropertyMapping;
+import org.hibernate.persister.collection.QueryableCollection;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.persister.entity.Joinable;
+import org.hibernate.persister.entity.PropertyMapping;
+import org.hibernate.persister.entity.Queryable;
+import org.hibernate.type.EntityType;
+import org.hibernate.type.Type;
+import org.hibernate.type.TypeFactory;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Delegate that handles the type and join sequence information for a FromElement.
+ *
+ * @author josh Feb 12, 2005 10:17:34 AM
+ */
+class FromElementType {
+ private static final Log log = LogFactory.getLog( FromElementType.class );
+
+ private FromElement fromElement;
+ private EntityType entityType;
+ private EntityPersister persister;
+ private QueryableCollection queryableCollection;
+ private CollectionPropertyMapping collectionPropertyMapping;
+ private JoinSequence joinSequence;
+ private String collectionSuffix;
+
+ public FromElementType(FromElement fromElement, EntityPersister persister, EntityType entityType) {
+ this.fromElement = fromElement;
+ this.persister = persister;
+ this.entityType = entityType;
+ if ( persister != null ) {
+ fromElement.setText( ( ( Queryable ) persister ).getTableName() + " " + getTableAlias() );
+ }
+ }
+
+ private String getTableAlias() {
+ return fromElement.getTableAlias();
+ }
+
+ private String getCollectionTableAlias() {
+ return fromElement.getCollectionTableAlias();
+ }
+
+ public String getCollectionSuffix() {
+ return collectionSuffix;
+ }
+
+ public void setCollectionSuffix(String suffix) {
+ collectionSuffix = suffix;
+ }
+
+ public EntityPersister getEntityPersister() {
+ return persister;
+ }
+
+ public Type getDataType() {
+ if ( persister == null ) {
+ if ( queryableCollection == null ) {
+ return null;
+ }
+ return queryableCollection.getType();
+ }
+ else {
+ return entityType;
+ }
+ }
+
+ public Type getSelectType() {
+ if (entityType==null) return null;
+ boolean shallow = fromElement.getFromClause().getWalker().isShallowQuery();
+ return TypeFactory.manyToOne( entityType.getAssociatedEntityName(), shallow );
+ }
+
+ /**
+ * Returns the Hibernate queryable implementation for the HQL class.
+ *
+ * @return the Hibernate queryable implementation for the HQL class.
+ */
+ public Queryable getQueryable() {
+ return ( persister instanceof Queryable ) ? ( Queryable ) persister : null;
+ }
+
+ /**
+ * Render the identifier select, but in a 'scalar' context (i.e. generate the column alias).
+ *
+ * @param i the sequence of the returned type
+ * @return the identifier select with the column alias.
+ */
+ String renderScalarIdentifierSelect(int i) {
+ checkInitialized();
+ String[] cols = getPropertyMapping( EntityPersister.ENTITY_ID ).toColumns( getTableAlias(), EntityPersister.ENTITY_ID );
+ StringBuffer buf = new StringBuffer();
+ // For property references generate <tablealias>.<columnname> as <projectionalias>
+ for ( int j = 0; j < cols.length; j++ ) {
+ String column = cols[j];
+ if ( j > 0 ) {
+ buf.append( ", " );
+ }
+ buf.append( column ).append( " as " ).append( NameGenerator.scalarName( i, j ) );
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Returns the identifier select SQL fragment.
+ *
+ * @param size The total number of returned types.
+ * @param k The sequence of the current returned type.
+ * @return the identifier select SQL fragment.
+ */
+ String renderIdentifierSelect(int size, int k) {
+ checkInitialized();
+ // Render the identifier select fragment using the table alias.
+ if ( fromElement.getFromClause().isSubQuery() ) {
+ // TODO: Replace this with a more elegant solution.
+ String[] idColumnNames = ( persister != null ) ?
+ ( ( Queryable ) persister ).getIdentifierColumnNames() : new String[0];
+ StringBuffer buf = new StringBuffer();
+ for ( int i = 0; i < idColumnNames.length; i++ ) {
+ buf.append( fromElement.getTableAlias() ).append( '.' ).append( idColumnNames[i] );
+ if ( i != idColumnNames.length - 1 ) buf.append( ", " );
+ }
+ return buf.toString();
+ }
+ else {
+ if (persister==null) {
+ throw new QueryException( "not an entity" );
+ }
+ String fragment = ( ( Queryable ) persister ).identifierSelectFragment( getTableAlias(), getSuffix( size, k ) );
+ return trimLeadingCommaAndSpaces( fragment );
+ }
+ }
+
+ private String getSuffix(int size, int sequence) {
+ return generateSuffix( size, sequence );
+ }
+
+ private static String generateSuffix(int size, int k) {
+ String suffix = size == 1 ? "" : Integer.toString( k ) + '_';
+ return suffix;
+ }
+
+ private void checkInitialized() {
+ fromElement.checkInitialized();
+ }
+
+ /**
+ * Returns the property select SQL fragment.
+ * @param size The total number of returned types.
+ * @param k The sequence of the current returned type.
+ * @return the property select SQL fragment.
+ */
+ String renderPropertySelect(int size, int k, boolean allProperties) {
+ checkInitialized();
+ if ( persister == null ) {
+ return "";
+ }
+ else {
+ String fragment = ( ( Queryable ) persister ).propertySelectFragment(
+ getTableAlias(),
+ getSuffix( size, k ),
+ allProperties
+ );
+ return trimLeadingCommaAndSpaces( fragment );
+ }
+ }
+
+ String renderCollectionSelectFragment(int size, int k) {
+ if ( queryableCollection == null ) {
+ return "";
+ }
+ else {
+ if ( collectionSuffix == null ) {
+ collectionSuffix = generateSuffix( size, k );
+ }
+ String fragment = queryableCollection.selectFragment( getCollectionTableAlias(), collectionSuffix );
+ return trimLeadingCommaAndSpaces( fragment );
+ }
+ }
+
+ public String renderValueCollectionSelectFragment(int size, int k) {
+ if ( queryableCollection == null ) {
+ return "";
+ }
+ else {
+ if ( collectionSuffix == null ) {
+ collectionSuffix = generateSuffix( size, k );
+ }
+ String fragment = queryableCollection.selectFragment( getTableAlias(), collectionSuffix );
+ return trimLeadingCommaAndSpaces( fragment );
+ }
+ }
+
+ /**
+ * This accounts for a quirk in Queryable, where it sometimes generates ', ' in front of the
+ * SQL fragment. :-P
+ *
+ * @param fragment An SQL fragment.
+ * @return The fragment, without the leading comma and spaces.
+ */
+ private static String trimLeadingCommaAndSpaces(String fragment) {
+ if ( fragment.length() > 0 && fragment.charAt( 0 ) == ',' ) {
+ fragment = fragment.substring( 1 );
+ }
+ fragment = fragment.trim();
+ return fragment.trim();
+ }
+
+ public void setJoinSequence(JoinSequence joinSequence) {
+ this.joinSequence = joinSequence;
+ }
+
+ public JoinSequence getJoinSequence() {
+ if ( joinSequence != null ) {
+ return joinSequence;
+ }
+
+ // Class names in the FROM clause result in a JoinSequence (the old FromParser does this).
+ if ( persister instanceof Joinable ) {
+ Joinable joinable = ( Joinable ) persister;
+ return fromElement.getSessionFactoryHelper().createJoinSequence().setRoot( joinable, getTableAlias() );
+ }
+ else {
+ return null; // TODO: Should this really return null? If not, figure out something better to do here.
+ }
+ }
+
+ public void setQueryableCollection(QueryableCollection queryableCollection) {
+ if ( this.queryableCollection != null ) {
+ throw new IllegalStateException( "QueryableCollection is already defined for " + this + "!" );
+ }
+ this.queryableCollection = queryableCollection;
+ if ( !queryableCollection.isOneToMany() ) {
+ // For many-to-many joins, use the tablename from the queryable collection for the default text.
+ fromElement.setText( queryableCollection.getTableName() + " " + getTableAlias() );
+ }
+ }
+
+ public QueryableCollection getQueryableCollection() {
+ return queryableCollection;
+ }
+
+ /**
+ * Returns the type of a property, given it's name (the last part) and the full path.
+ *
+ * @param propertyName The last part of the full path to the property.
+ * @return The type.
+ * @0param propertyPath The full property path.
+ */
+ public Type getPropertyType(String propertyName, String propertyPath) {
+ checkInitialized();
+ Type type = null;
+ // If this is an entity and the property is the identifier property, then use getIdentifierType().
+ // Note that the propertyName.equals( propertyPath ) checks whether we have a component
+ // key reference, where the component class property name is the same as the
+ // entity id property name; if the two are not equal, this is the case and
+ // we'd need to "fall through" to using the property mapping.
+ if ( persister != null && propertyName.equals( propertyPath ) && propertyName.equals( persister.getIdentifierPropertyName() ) ) {
+ type = persister.getIdentifierType();
+ }
+ else { // Otherwise, use the property mapping.
+ PropertyMapping mapping = getPropertyMapping( propertyName );
+ type = mapping.toType( propertyPath );
+ }
+ if ( type == null ) {
+ throw new MappingException( "Property " + propertyName + " does not exist in " +
+ ( ( queryableCollection == null ) ? "class" : "collection" ) + " "
+ + ( ( queryableCollection == null ) ? fromElement.getClassName() : queryableCollection.getRole() ) );
+ }
+ return type;
+ }
+
+ String[] toColumns(String tableAlias, String path, boolean inSelect) {
+ return toColumns( tableAlias, path, inSelect, false );
+ }
+
+ String[] toColumns(String tableAlias, String path, boolean inSelect, boolean forceAlias) {
+ checkInitialized();
+ PropertyMapping propertyMapping = getPropertyMapping( path );
+ // If this from element is a collection and the path is a collection property (maxIndex, etc.) then
+ // generate a sub-query.
+ if ( !inSelect && queryableCollection != null && CollectionProperties.isCollectionProperty( path ) ) {
+ Map enabledFilters = fromElement.getWalker().getEnabledFilters();
+ String subquery = CollectionSubqueryFactory.createCollectionSubquery(
+ joinSequence,
+ enabledFilters,
+ propertyMapping.toColumns( tableAlias, path )
+ );
+ if ( log.isDebugEnabled() ) {
+ log.debug( "toColumns(" + tableAlias + "," + path + ") : subquery = " + subquery );
+ }
+ return new String[]{"(" + subquery + ")"};
+ }
+ else {
+ if ( forceAlias ) {
+ return propertyMapping.toColumns( tableAlias, path );
+ }
+ else if ( fromElement.getWalker().getStatementType() == HqlSqlTokenTypes.SELECT ) {
+ return propertyMapping.toColumns( tableAlias, path );
+ }
+ else if ( fromElement.getWalker().getCurrentClauseType() == HqlSqlTokenTypes.SELECT ) {
+ return propertyMapping.toColumns( tableAlias, path );
+ }
+ else if ( fromElement.getWalker().isSubQuery() ) {
+ // for a subquery, the alias to use depends on a few things (we
+ // already know this is not an overall SELECT):
+ // 1) if this FROM_ELEMENT represents a correlation to the
+ // outer-most query
+ // A) if the outer query represents a multi-table
+ // persister, we need to use the given alias
+ // in anticipation of one of the multi-table
+ // executors being used (as this subquery will
+ // actually be used in the "id select" phase
+ // of that multi-table executor)
+ // B) otherwise, we need to use the persister's
+ // table name as the column qualification
+ // 2) otherwise (not correlated), use the given alias
+ if ( isCorrelation() ) {
+ if ( isMultiTable() ) {
+ return propertyMapping.toColumns( tableAlias, path );
+ }
+ else {
+ return propertyMapping.toColumns( extractTableName(), path );
+ }
+ }
+ else {
+ return propertyMapping.toColumns( tableAlias, path );
+ }
+ }
+ else {
+ String[] columns = propertyMapping.toColumns( path );
+ log.trace( "Using non-qualified column reference [" + path + " -> (" + ArrayHelper.toString( columns ) + ")]" );
+ return columns;
+ }
+ }
+ }
+
+ private boolean isCorrelation() {
+ FromClause top = fromElement.getWalker().getFinalFromClause();
+ return fromElement.getFromClause() != fromElement.getWalker().getCurrentFromClause() &&
+ fromElement.getFromClause() == top;
+ }
+
+ private boolean isMultiTable() {
+ // should be safe to only ever expect EntityPersister references here
+ return fromElement.getQueryable() != null &&
+ fromElement.getQueryable().isMultiTable();
+ }
+
+ private String extractTableName() {
+ // should be safe to only ever expect EntityPersister references here
+ return fromElement.getQueryable().getTableName();
+ }
+
+ PropertyMapping getPropertyMapping(String propertyName) {
+ checkInitialized();
+ if ( queryableCollection == null ) { // Not a collection?
+ return ( PropertyMapping ) persister; // Return the entity property mapping.
+ }
+ // If the property is a special collection property name, return a CollectionPropertyMapping.
+ if ( CollectionProperties.isCollectionProperty( propertyName ) ) {
+ if ( collectionPropertyMapping == null ) {
+ collectionPropertyMapping = new CollectionPropertyMapping( queryableCollection );
+ }
+ return collectionPropertyMapping;
+ }
+ if ( queryableCollection.getElementType().isAnyType() ) {
+ // collection of <many-to-any/> mappings...
+ // used to circumvent the component-collection check below...
+ return queryableCollection;
+
+ }
+ if ( queryableCollection.getElementType().isComponentType() ) {
+ // Collection of components.
+ if ( propertyName.equals( EntityPersister.ENTITY_ID ) ) {
+ return ( PropertyMapping ) queryableCollection.getOwnerEntityPersister();
+ }
+ }
+ return queryableCollection;
+ }
+
+ public boolean isCollectionOfValuesOrComponents() {
+ if ( persister == null ) {
+ if ( queryableCollection == null ) {
+ return false;
+ }
+ else {
+ return !queryableCollection.getElementType().isEntityType();
+ }
+ }
+ else {
+ return false;
+ }
+ }
+
+ public boolean isEntity() {
+ return persister != null;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/FromReferenceNode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/FromReferenceNode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/FromReferenceNode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,109 @@
+// $Id: FromReferenceNode.java 7494 2005-07-15 16:20:04Z steveebersole $
+package org.hibernate.hql.ast.tree;
+
+import antlr.SemanticException;
+import antlr.collections.AST;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Represents a reference to a FROM element, for example a class alias in a WHERE clause.
+ *
+ * @author josh Jul 21, 2004 7:02:04 AM
+ */
+public abstract class FromReferenceNode extends AbstractSelectExpression
+ implements ResolvableNode, DisplayableNode, InitializeableNode, PathNode {
+
+ private static final Log log = LogFactory.getLog( FromReferenceNode.class );
+
+ private FromElement fromElement;
+ private boolean resolved = false;
+ public static final int ROOT_LEVEL = 0;
+
+ public FromElement getFromElement() {
+ return fromElement;
+ }
+
+ public void setFromElement(FromElement fromElement) {
+ this.fromElement = fromElement;
+ }
+
+ /**
+ * Resolves the left hand side of the DOT.
+ *
+ * @throws SemanticException
+ */
+ public void resolveFirstChild() throws SemanticException {
+ }
+
+ public String getPath() {
+ return getOriginalText();
+ }
+
+ public boolean isResolved() {
+ return resolved;
+ }
+
+ public void setResolved() {
+ this.resolved = true;
+ if ( log.isDebugEnabled() ) {
+ log.debug( "Resolved : " + this.getPath() + " -> " + this.getText() );
+ }
+ }
+
+ public String getDisplayText() {
+ StringBuffer buf = new StringBuffer();
+ buf.append( "{" ).append( ( fromElement == null ) ? "no fromElement" : fromElement.getDisplayText() );
+ buf.append( "}" );
+ return buf.toString();
+ }
+
+ public void recursiveResolve(int level, boolean impliedAtRoot, String classAlias) throws SemanticException {
+ recursiveResolve( level, impliedAtRoot, classAlias, this );
+ }
+
+ public void recursiveResolve(int level, boolean impliedAtRoot, String classAlias, AST parent) throws SemanticException {
+ AST lhs = getFirstChild();
+ int nextLevel = level + 1;
+ if ( lhs != null ) {
+ FromReferenceNode n = ( FromReferenceNode ) lhs;
+ n.recursiveResolve( nextLevel, impliedAtRoot, null, this );
+ }
+ resolveFirstChild();
+ boolean impliedJoin = true;
+ if ( level == ROOT_LEVEL && !impliedAtRoot ) {
+ impliedJoin = false;
+ }
+ resolve( true, impliedJoin, classAlias, parent );
+ }
+
+ public boolean isReturnableEntity() throws SemanticException {
+ return !isScalar() && fromElement.isEntity();
+ }
+
+ public void resolveInFunctionCall(boolean generateJoin, boolean implicitJoin) throws SemanticException {
+ resolve( generateJoin, implicitJoin );
+ }
+
+ public void resolve(boolean generateJoin, boolean implicitJoin) throws SemanticException {
+ resolve( generateJoin, implicitJoin, null );
+ }
+
+ public void resolve(boolean generateJoin, boolean implicitJoin, String classAlias) throws SemanticException {
+ resolve( generateJoin, implicitJoin, classAlias, null );
+ }
+
+ public void prepareForDot(String propertyName) throws SemanticException {
+ }
+
+ /**
+ * Sub-classes can override this method if they produce implied joins (e.g. DotNode).
+ *
+ * @return an implied join created by this from reference.
+ */
+ public FromElement getImpliedJoin() {
+ return null;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/HqlSqlWalkerNode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/HqlSqlWalkerNode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/HqlSqlWalkerNode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,40 @@
+// $Id: HqlSqlWalkerNode.java 7460 2005-07-12 20:27:29Z steveebersole $
+package org.hibernate.hql.ast.tree;
+
+import org.hibernate.hql.ast.HqlSqlWalker;
+import org.hibernate.hql.ast.util.AliasGenerator;
+import org.hibernate.hql.ast.util.SessionFactoryHelper;
+
+import antlr.ASTFactory;
+
+/**
+ * A semantic analysis node, that points back to the main analyzer.
+ *
+ * @author josh Sep 24, 2004 4:08:13 PM
+ */
+public class HqlSqlWalkerNode extends SqlNode implements InitializeableNode {
+ /**
+ * A pointer back to the phase 2 processor.
+ */
+ private HqlSqlWalker walker;
+
+ public void initialize(Object param) {
+ walker = ( HqlSqlWalker ) param;
+ }
+
+ public HqlSqlWalker getWalker() {
+ return walker;
+ }
+
+ public SessionFactoryHelper getSessionFactoryHelper() {
+ return walker.getSessionFactoryHelper();
+ }
+
+ public ASTFactory getASTFactory() {
+ return walker.getASTFactory();
+ }
+
+ public AliasGenerator getAliasGenerator() {
+ return walker.getAliasGenerator();
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/IdentNode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/IdentNode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/IdentNode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,307 @@
+// $Id: IdentNode.java 10059 2006-06-28 02:37:56Z steve.ebersole at jboss.com $
+package org.hibernate.hql.ast.tree;
+
+import antlr.SemanticException;
+import antlr.collections.AST;
+import org.hibernate.QueryException;
+import org.hibernate.dialect.function.SQLFunction;
+import org.hibernate.hql.antlr.SqlTokenTypes;
+import org.hibernate.hql.ast.util.ColumnHelper;
+import org.hibernate.persister.collection.QueryableCollection;
+import org.hibernate.persister.entity.Queryable;
+import org.hibernate.sql.JoinFragment;
+import org.hibernate.type.CollectionType;
+import org.hibernate.type.Type;
+import org.hibernate.util.StringHelper;
+
+import java.util.List;
+
+/**
+ * Represents an identifier all by itself, which may be a function name,
+ * a class alias, or a form of naked property-ref depending on the
+ * context.
+ *
+ * @author josh Aug 16, 2004 7:20:55 AM
+ */
+public class IdentNode extends FromReferenceNode implements SelectExpression {
+
+ private static final int UNKNOWN = 0;
+ private static final int PROPERTY_REF = 1;
+ private static final int COMPONENT_REF = 2;
+
+ private boolean nakedPropertyRef = false;
+
+ public void resolveIndex(AST parent) throws SemanticException {
+ // An ident node can represent an index expression if the ident
+ // represents a naked property ref
+ // *Note: this makes the assumption (which is currently the case
+ // in the hql-sql grammar) that the ident is first resolved
+ // itself (addrExpr -> resolve()). The other option, if that
+ // changes, is to call resolve from here; but it is
+ // currently un-needed overhead.
+ if (!(isResolved() && nakedPropertyRef)) {
+ throw new UnsupportedOperationException();
+ }
+
+ String propertyName = getOriginalText();
+ if (!getDataType().isCollectionType()) {
+ throw new SemanticException("Collection expected; [" + propertyName + "] does not refer to a collection property");
+ }
+
+ // TODO : most of below was taken verbatim from DotNode; should either delegate this logic or super-type it
+ CollectionType type = (CollectionType) getDataType();
+ String role = type.getRole();
+ QueryableCollection queryableCollection = getSessionFactoryHelper().requireQueryableCollection(role);
+
+ String alias = null; // DotNode uses null here...
+ String columnTableAlias = getFromElement().getTableAlias();
+ int joinType = JoinFragment.INNER_JOIN;
+ boolean fetch = false;
+
+ FromElementFactory factory = new FromElementFactory(
+ getWalker().getCurrentFromClause(),
+ getFromElement(),
+ propertyName,
+ alias,
+ getFromElement().toColumns(columnTableAlias, propertyName, false),
+ true
+ );
+ FromElement elem = factory.createCollection(queryableCollection, role, joinType, fetch, true);
+ setFromElement(elem);
+ getWalker().addQuerySpaces(queryableCollection.getCollectionSpaces()); // Always add the collection's query spaces.
+ }
+
+ public void resolve(boolean generateJoin, boolean implicitJoin, String classAlias, AST parent) {
+ if (!isResolved()) {
+ if (getWalker().getCurrentFromClause().isFromElementAlias(getText())) {
+ if (resolveAsAlias()) {
+ setResolved();
+ // We represent a from-clause alias
+ }
+ }
+ else if (parent != null && parent.getType() == SqlTokenTypes.DOT) {
+ DotNode dot = (DotNode) parent;
+ if (parent.getFirstChild() == this) {
+ if (resolveAsNakedComponentPropertyRefLHS(dot)) {
+ // we are the LHS of the DOT representing a naked comp-prop-ref
+ setResolved();
+ }
+ }
+ else {
+ if (resolveAsNakedComponentPropertyRefRHS(dot)) {
+ // we are the RHS of the DOT representing a naked comp-prop-ref
+ setResolved();
+ }
+ }
+ }
+ else {
+ int result = resolveAsNakedPropertyRef();
+ if (result == PROPERTY_REF) {
+ // we represent a naked (simple) prop-ref
+ setResolved();
+ }
+ else if (result == COMPONENT_REF) {
+ // EARLY EXIT!!! return so the resolve call explicitly coming from DotNode can
+ // resolve this...
+ return;
+ }
+ }
+
+ // if we are still not resolved, we might represent a constant.
+ // needed to add this here because the allowance of
+ // naked-prop-refs in the grammar collides with the
+ // definition of literals/constants ("nondeterminism").
+ // TODO: cleanup the grammar so that "processConstants" is always just handled from here
+ if (!isResolved()) {
+ try {
+ getWalker().getLiteralProcessor().processConstant(this, false);
+ }
+ catch (Throwable ignore) {
+ // just ignore it for now, it'll get resolved later...
+ }
+ }
+ }
+ }
+
+ private boolean resolveAsAlias() {
+ // This is not actually a constant, but a reference to FROM element.
+ FromElement element = getWalker().getCurrentFromClause().getFromElement(getText());
+ if (element != null) {
+ setFromElement(element);
+ setText(element.getIdentityColumn());
+ setType(SqlTokenTypes.ALIAS_REF);
+ return true;
+ }
+ return false;
+ }
+
+ private Type getNakedPropertyType(FromElement fromElement)
+ {
+ if (fromElement == null) {
+ return null;
+ }
+ String property = getOriginalText();
+ Type propertyType = null;
+ try {
+ propertyType = fromElement.getPropertyType(property, property);
+ }
+ catch (Throwable t) {
+ }
+ return propertyType;
+ }
+
+ private int resolveAsNakedPropertyRef() {
+ FromElement fromElement = locateSingleFromElement();
+ if (fromElement == null) {
+ return UNKNOWN;
+ }
+ Queryable persister = fromElement.getQueryable();
+ if (persister == null) {
+ return UNKNOWN;
+ }
+ Type propertyType = getNakedPropertyType(fromElement);
+ if (propertyType == null) {
+ // assume this ident's text does *not* refer to a property on the given persister
+ return UNKNOWN;
+ }
+
+ if ((propertyType.isComponentType() || propertyType.isAssociationType() )) {
+ return COMPONENT_REF;
+ }
+
+ setFromElement(fromElement);
+ String property = getText();
+ String[] columns = getWalker().isSelectStatement()
+ ? persister.toColumns(fromElement.getTableAlias(), property)
+ : persister.toColumns(property);
+ String text = StringHelper.join(", ", columns);
+ setText(columns.length == 1 ? text : "(" + text + ")");
+ setType(SqlTokenTypes.SQL_TOKEN);
+
+ // these pieces are needed for usage in select clause
+ super.setDataType(propertyType);
+ nakedPropertyRef = true;
+
+ return PROPERTY_REF;
+ }
+
+ private boolean resolveAsNakedComponentPropertyRefLHS(DotNode parent) {
+ FromElement fromElement = locateSingleFromElement();
+ if (fromElement == null) {
+ return false;
+ }
+
+ Type componentType = getNakedPropertyType(fromElement);
+ if ( componentType == null ) {
+ throw new QueryException( "Unable to resolve path [" + parent.getPath() + "], unexpected token [" + getOriginalText() + "]" );
+ }
+ if (!componentType.isComponentType()) {
+ throw new QueryException("Property '" + getOriginalText() + "' is not a component. Use an alias to reference associations or collections.");
+ }
+
+ Type propertyType = null; // used to set the type of the parent dot node
+ String propertyPath = getText() + "." + getNextSibling().getText();
+ try {
+ // check to see if our "propPath" actually
+ // represents a property on the persister
+ propertyType = fromElement.getPropertyType(getText(), propertyPath);
+ }
+ catch (Throwable t) {
+ // assume we do *not* refer to a property on the given persister
+ return false;
+ }
+
+ setFromElement(fromElement);
+ parent.setPropertyPath(propertyPath);
+ parent.setDataType(propertyType);
+
+ return true;
+ }
+
+ private boolean resolveAsNakedComponentPropertyRefRHS(DotNode parent) {
+ FromElement fromElement = locateSingleFromElement();
+ if (fromElement == null) {
+ return false;
+ }
+
+ Type propertyType = null;
+ String propertyPath = parent.getLhs().getText() + "." + getText();
+ try {
+ // check to see if our "propPath" actually
+ // represents a property on the persister
+ propertyType = fromElement.getPropertyType(getText(), propertyPath);
+ }
+ catch (Throwable t) {
+ // assume we do *not* refer to a property on the given persister
+ return false;
+ }
+
+ setFromElement(fromElement);
+ // this piece is needed for usage in select clause
+ super.setDataType(propertyType);
+ nakedPropertyRef = true;
+
+ return true;
+ }
+
+ private FromElement locateSingleFromElement() {
+ List fromElements = getWalker().getCurrentFromClause().getFromElements();
+ if (fromElements == null || fromElements.size() != 1) {
+ // TODO : should this be an error?
+ return null;
+ }
+ FromElement element = (FromElement) fromElements.get(0);
+ if (element.getClassAlias() != null) {
+ // naked property-refs cannot be used with an aliased from element
+ return null;
+ }
+ return element;
+ }
+
+ public Type getDataType() {
+ Type type = super.getDataType();
+ if (type != null) return type;
+ FromElement fe = getFromElement();
+ if (fe != null) return fe.getDataType();
+ SQLFunction sf = getWalker().getSessionFactoryHelper().findSQLFunction(getText());
+ return sf == null ? null : sf.getReturnType(null, null);
+ }
+
+ public void setScalarColumnText(int i) throws SemanticException {
+ if (nakedPropertyRef) {
+ // do *not* over-write the column text, as that has already been
+ // "rendered" during resolve
+ ColumnHelper.generateSingleScalarColumn(this, i);
+ }
+ else {
+ FromElement fe = getFromElement();
+ if (fe != null) {
+ setText(fe.renderScalarIdentifierSelect(i));
+ }
+ else {
+ ColumnHelper.generateSingleScalarColumn(this, i);
+ }
+ }
+ }
+
+ public String getDisplayText() {
+ StringBuffer buf = new StringBuffer();
+
+ if (getType() == SqlTokenTypes.ALIAS_REF) {
+ buf.append("{alias=").append(getOriginalText());
+ if (getFromElement() == null) {
+ buf.append(", no from element");
+ }
+ else {
+ buf.append(", className=").append(getFromElement().getClassName());
+ buf.append(", tableAlias=").append(getFromElement().getTableAlias());
+ }
+ buf.append("}");
+ }
+ else {
+ buf.append("{originalText=" + getOriginalText()).append("}");
+ }
+ return buf.toString();
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/ImpliedFromElement.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/ImpliedFromElement.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/ImpliedFromElement.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,57 @@
+// $Id: ImpliedFromElement.java 7460 2005-07-12 20:27:29Z steveebersole $
+package org.hibernate.hql.ast.tree;
+
+/**
+ * Represents a FROM element implied by a path expression or a collection reference.
+ *
+ * @author josh Feb 10, 2005 12:31:03 AM
+ */
+public class ImpliedFromElement extends FromElement {
+ /**
+ * True if this from element was implied from a path in the FROM clause, but not
+ * explicitly declard in the from clause.
+ */
+ private boolean impliedInFromClause = false;
+
+ /**
+ * True if this implied from element should be included in the projection list.
+ */
+ private boolean inProjectionList = false;
+
+ public boolean isImplied() {
+ return true;
+ }
+
+ public void setImpliedInFromClause(boolean flag) {
+ impliedInFromClause = flag;
+ }
+
+ public boolean isImpliedInFromClause() {
+ return impliedInFromClause;
+ }
+
+ public void setInProjectionList(boolean inProjectionList) {
+ this.inProjectionList = inProjectionList;
+ }
+
+ public boolean inProjectionList() {
+ return inProjectionList && isFromOrJoinFragment();
+ }
+
+ public boolean isIncludeSubclasses() {
+ return false; // Never include subclasses for implied from elements.
+ }
+
+ /**
+ * Returns additional display text for the AST node.
+ *
+ * @return String - The additional display text.
+ */
+ public String getDisplayText() {
+ StringBuffer buf = new StringBuffer();
+ buf.append( "ImpliedFromElement{" );
+ appendDisplayText( buf );
+ buf.append( "}" );
+ return buf.toString();
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/InLogicOperatorNode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/InLogicOperatorNode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/InLogicOperatorNode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,40 @@
+package org.hibernate.hql.ast.tree;
+
+import antlr.SemanticException;
+import antlr.collections.AST;
+import org.hibernate.type.Type;
+
+/**
+ * @author Steve Ebersole
+ */
+public class InLogicOperatorNode extends BinaryLogicOperatorNode implements BinaryOperatorNode {
+
+ public Node getInList() {
+ return getRightHandOperand();
+ }
+
+ public void initialize() throws SemanticException {
+ Node lhs = getLeftHandOperand();
+ if ( lhs == null ) {
+ throw new SemanticException( "left-hand operand of in operator was null" );
+ }
+ Node inList = getInList();
+ if ( inList == null ) {
+ throw new SemanticException( "right-hand operand of in operator was null" );
+ }
+
+ // for expected parameter type injection, we expect that the lhs represents
+ // some form of property ref and that the children of the in-list represent
+ // one-or-more params.
+ if ( SqlNode.class.isAssignableFrom( lhs.getClass() ) ) {
+ Type lhsType = ( ( SqlNode ) lhs ).getDataType();
+ AST inListChild = inList.getFirstChild();
+ while ( inListChild != null ) {
+ if ( ExpectedTypeAwareNode.class.isAssignableFrom( inListChild.getClass() ) ) {
+ ( ( ExpectedTypeAwareNode ) inListChild ).setExpectedType( lhsType );
+ }
+ inListChild = inListChild.getNextSibling();
+ }
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/IndexNode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/IndexNode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/IndexNode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,134 @@
+// $Id: IndexNode.java 7460 2005-07-12 20:27:29Z steveebersole $
+package org.hibernate.hql.ast.tree;
+
+import org.hibernate.QueryException;
+import org.hibernate.engine.JoinSequence;
+import org.hibernate.hql.ast.SqlGenerator;
+import org.hibernate.hql.ast.util.SessionFactoryHelper;
+import org.hibernate.persister.collection.QueryableCollection;
+import org.hibernate.type.CollectionType;
+import org.hibernate.type.Type;
+
+import antlr.RecognitionException;
+import antlr.SemanticException;
+import antlr.collections.AST;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Represents the [] operator and provides it's semantics.
+ *
+ * @author josh Aug 14, 2004 7:07:10 AM
+ */
+public class IndexNode extends FromReferenceNode {
+
+ private static final Log log = LogFactory.getLog( IndexNode.class );
+
+ public void setScalarColumnText(int i) throws SemanticException {
+ throw new UnsupportedOperationException( "An IndexNode cannot generate column text!" );
+ }
+
+ public void prepareForDot(String propertyName) throws SemanticException {
+ FromElement fromElement = getFromElement();
+ if ( fromElement == null ) {
+ throw new IllegalStateException( "No FROM element for index operator!" );
+ }
+ QueryableCollection queryableCollection = fromElement.getQueryableCollection();
+ if ( queryableCollection != null && !queryableCollection.isOneToMany() ) {
+
+ FromReferenceNode collectionNode = ( FromReferenceNode ) getFirstChild();
+ String path = collectionNode.getPath() + "[]." + propertyName;
+ if ( log.isDebugEnabled() ) {
+ log.debug( "Creating join for many-to-many elements for " + path );
+ }
+ FromElementFactory factory = new FromElementFactory( fromElement.getFromClause(), fromElement, path );
+ // This will add the new from element to the origin.
+ FromElement elementJoin = factory.createElementJoin( queryableCollection );
+ setFromElement( elementJoin );
+ }
+ }
+
+ public void resolveIndex(AST parent) throws SemanticException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void resolve(boolean generateJoin, boolean implicitJoin, String classAlias, AST parent)
+ throws SemanticException {
+ if ( isResolved() ) {
+ return;
+ }
+ FromReferenceNode collectionNode = ( FromReferenceNode ) getFirstChild();
+ SessionFactoryHelper sessionFactoryHelper = getSessionFactoryHelper();
+ collectionNode.resolveIndex( this ); // Fully resolve the map reference, create implicit joins.
+
+ Type type = collectionNode.getDataType();
+ if ( !type.isCollectionType() ) {
+ throw new SemanticException( "The [] operator cannot be applied to type " + type.toString() );
+ }
+ String collectionRole = ( ( CollectionType ) type ).getRole();
+ QueryableCollection queryableCollection = sessionFactoryHelper.requireQueryableCollection( collectionRole );
+ if ( !queryableCollection.hasIndex() ) {
+ throw new QueryException( "unindexed fromElement before []: " + collectionNode.getPath() );
+ }
+
+ // Generate the inner join -- The elements need to be joined to the collection they are in.
+ FromElement fromElement = collectionNode.getFromElement();
+ String elementTable = fromElement.getTableAlias();
+ FromClause fromClause = fromElement.getFromClause();
+ String path = collectionNode.getPath();
+
+ FromElement elem = fromClause.findCollectionJoin( path );
+ if ( elem == null ) {
+ FromElementFactory factory = new FromElementFactory( fromClause, fromElement, path );
+ elem = factory.createCollectionElementsJoin( queryableCollection, elementTable );
+ if ( log.isDebugEnabled() ) {
+ log.debug( "No FROM element found for the elements of collection join path " + path
+ + ", created " + elem );
+ }
+ }
+ else {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "FROM element found for collection join path " + path );
+ }
+ }
+
+ // Add the condition to the join sequence that qualifies the indexed element.
+ AST index = collectionNode.getNextSibling(); // The index should be a constant, which will have been processed already.
+ if ( index == null ) {
+ throw new QueryException( "No index value!" );
+ }
+
+ setFromElement( fromElement ); // The 'from element' that represents the elements of the collection.
+
+ // Sometimes use the element table alias, sometimes use the... umm... collection table alias (many to many)
+ String collectionTableAlias = elementTable;
+ if ( elem.getCollectionTableAlias() != null ) {
+ collectionTableAlias = elem.getCollectionTableAlias();
+ }
+
+ // TODO: get SQL rendering out of here, create an AST for the join expressions.
+ // Use the SQL generator grammar to generate the SQL text for the index expression.
+ JoinSequence joinSequence = fromElement.getJoinSequence();
+ String[] indexCols = queryableCollection.getIndexColumnNames();
+ if ( indexCols.length != 1 ) {
+ throw new QueryException( "composite-index appears in []: " + collectionNode.getPath() );
+ }
+ SqlGenerator gen = new SqlGenerator( getSessionFactoryHelper().getFactory() );
+ try {
+ gen.simpleExpr( index ); //TODO: used to be exprNoParens! was this needed?
+ }
+ catch ( RecognitionException e ) {
+ throw new QueryException( e.getMessage(), e );
+ }
+ String expression = gen.getSQL();
+ joinSequence.addCondition( collectionTableAlias + '.' + indexCols[0] + " = " + expression );
+
+ // Now, set the text for this node. It should be the element columns.
+ String[] elementColumns = queryableCollection.getElementColumnNames( elementTable );
+ setText( elementColumns[0] );
+ setResolved();
+ }
+
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/InitializeableNode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/InitializeableNode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/InitializeableNode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,15 @@
+// $Id: InitializeableNode.java 7460 2005-07-12 20:27:29Z steveebersole $
+
+package org.hibernate.hql.ast.tree;
+
+/**
+ * An interface for initializeable AST nodes.
+ */
+public interface InitializeableNode {
+ /**
+ * Initializes the node with the parameter.
+ *
+ * @param param the initialization parameter.
+ */
+ void initialize(Object param);
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/InsertStatement.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/InsertStatement.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/InsertStatement.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,55 @@
+// $Id: InsertStatement.java 7460 2005-07-12 20:27:29Z steveebersole $
+package org.hibernate.hql.ast.tree;
+
+import org.hibernate.QueryException;
+import org.hibernate.hql.antlr.HqlSqlTokenTypes;
+
+/**
+ * Defines a top-level AST node representing an HQL "insert select" statement.
+ *
+ * @author Steve Ebersole
+ */
+public class InsertStatement extends AbstractStatement {
+
+ /**
+ * @see Statement#getStatementType()
+ */
+ public int getStatementType() {
+ return HqlSqlTokenTypes.INSERT;
+ }
+
+ /**
+ * @see Statement#needsExecutor()
+ */
+ public boolean needsExecutor() {
+ return true;
+ }
+
+ /**
+ * Performs detailed semantic validation on this insert statement tree.
+ *
+ * @throws QueryException Indicates validation failure.
+ */
+ public void validate() throws QueryException {
+ getIntoClause().validateTypes( getSelectClause() );
+ }
+
+ /**
+ * Retreive this insert statement's into-clause.
+ *
+ * @return The into-clause
+ */
+ public IntoClause getIntoClause() {
+ return ( IntoClause ) getFirstChild();
+ }
+
+ /**
+ * Retreive this insert statement's select-clause.
+ *
+ * @return The select-clause.
+ */
+ public SelectClause getSelectClause() {
+ return ( ( QueryNode ) getIntoClause().getNextSibling() ).getSelectClause();
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/IntoClause.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/IntoClause.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/IntoClause.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,234 @@
+// $Id: IntoClause.java 8050 2005-08-31 15:02:23Z steveebersole $
+package org.hibernate.hql.ast.tree;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.sql.Types;
+
+import org.hibernate.QueryException;
+import org.hibernate.persister.entity.Queryable;
+import org.hibernate.type.Type;
+import org.hibernate.util.ArrayHelper;
+
+import antlr.collections.AST;
+
+/**
+ * Represents an entity referenced in the INTO clause of an HQL
+ * INSERT statement.
+ *
+ * @author Steve Ebersole
+ */
+public class IntoClause extends HqlSqlWalkerNode implements DisplayableNode {
+
+ private Queryable persister;
+ private String columnSpec = "";
+ private Type[] types;
+
+ private boolean discriminated;
+ private boolean explicitIdInsertion;
+ private boolean explicitVersionInsertion;
+
+
+ public void initialize(Queryable persister) {
+ if ( persister.isAbstract() ) {
+ throw new QueryException( "cannot insert into abstract class (no table)" );
+ }
+ this.persister = persister;
+ initializeColumns();
+
+ if ( getWalker().getSessionFactoryHelper().hasPhysicalDiscriminatorColumn( persister ) ) {
+ discriminated = true;
+ columnSpec += ", " + persister.getDiscriminatorColumnName();
+ }
+
+ resetText();
+ }
+
+ private void resetText() {
+ setText( "into " + getTableName() + " ( " + columnSpec + " )" );
+ }
+
+ public String getTableName() {
+ return persister.getSubclassTableName( 0 );
+ }
+
+ public Queryable getQueryable() {
+ return persister;
+ }
+
+ public String getEntityName() {
+ return persister.getEntityName();
+ }
+
+ public Type[] getInsertionTypes() {
+ return types;
+ }
+
+ public boolean isDiscriminated() {
+ return discriminated;
+ }
+
+ public boolean isExplicitIdInsertion() {
+ return explicitIdInsertion;
+ }
+
+ public boolean isExplicitVersionInsertion() {
+ return explicitVersionInsertion;
+ }
+
+ public void prependIdColumnSpec() {
+ columnSpec = persister.getIdentifierColumnNames()[0] + ", " + columnSpec;
+ resetText();
+ }
+
+ public void prependVersionColumnSpec() {
+ columnSpec = persister.getPropertyColumnNames( persister.getVersionProperty() )[0] + ", " + columnSpec;
+ resetText();
+ }
+
+ public void validateTypes(SelectClause selectClause) throws QueryException {
+ Type[] selectTypes = selectClause.getQueryReturnTypes();
+ if ( selectTypes.length != types.length ) {
+ throw new QueryException( "number of select types did not match those for insert" );
+ }
+
+ for ( int i = 0; i < types.length; i++ ) {
+ if ( !areCompatible( types[i], selectTypes[i] ) ) {
+ throw new QueryException(
+ "insertion type [" + types[i] + "] and selection type [" +
+ selectTypes[i] + "] at position " + i + " are not compatible"
+ );
+ }
+ }
+
+ // otherwise, everything ok.
+ }
+
+ /**
+ * Returns additional display text for the AST node.
+ *
+ * @return String - The additional display text.
+ */
+ public String getDisplayText() {
+ StringBuffer buf = new StringBuffer();
+ buf.append( "IntoClause{" );
+ buf.append( "entityName=" ).append( getEntityName() );
+ buf.append( ",tableName=" ).append( getTableName() );
+ buf.append( ",columns={" ).append( columnSpec ).append( "}" );
+ buf.append( "}" );
+ return buf.toString();
+ }
+
+ private void initializeColumns() {
+ AST propertySpec = getFirstChild();
+ List types = new ArrayList();
+ visitPropertySpecNodes( propertySpec.getFirstChild(), types );
+ this.types = ArrayHelper.toTypeArray( types );
+ columnSpec = columnSpec.substring( 0, columnSpec.length() - 2 );
+ }
+
+ private void visitPropertySpecNodes(AST propertyNode, List types) {
+ if ( propertyNode == null ) {
+ return;
+ }
+ // TODO : we really need to be able to deal with component paths here also;
+ // this is difficult because the hql-sql grammar expects all those node types
+ // to be FromReferenceNodes. One potential fix here would be to convert the
+ // IntoClause to just use a FromClause/FromElement combo (as a child of the
+ // InsertStatement) and move all this logic into the InsertStatement. That's
+ // probably the easiest approach (read: least amount of changes to the grammar
+ // and code), but just doesn't feel right as then an insert would contain
+ // 2 from-clauses
+ String name = propertyNode.getText();
+ if ( isSuperclassProperty( name ) ) {
+ throw new QueryException( "INSERT statements cannot refer to superclass/joined properties [" + name + "]" );
+ }
+
+ if ( name.equals( persister.getIdentifierPropertyName() ) ) {
+ explicitIdInsertion = true;
+ }
+
+ if ( persister.isVersioned() ) {
+ if ( name.equals( persister.getPropertyNames()[ persister.getVersionProperty() ] ) ) {
+ explicitVersionInsertion = true;
+ }
+ }
+
+ String[] columnNames = persister.toColumns( name );
+ renderColumns( columnNames );
+ types.add( persister.toType( name ) );
+
+ // visit width-first, then depth
+ visitPropertySpecNodes( propertyNode.getNextSibling(), types );
+ visitPropertySpecNodes( propertyNode.getFirstChild(), types );
+ }
+
+ private void renderColumns(String[] columnNames) {
+ for ( int i = 0; i < columnNames.length; i++ ) {
+ columnSpec += columnNames[i] + ", ";
+ }
+ }
+
+ private boolean isSuperclassProperty(String propertyName) {
+ // really there are two situations where it should be ok to allow the insertion
+ // into properties defined on a superclass:
+ // 1) union-subclass with an abstract root entity
+ // 2) discrim-subclass
+ //
+ // #1 is handled already because of the fact that
+ // UnionSubclassPersister alreay always returns 0
+ // for this call...
+ //
+ // we may want to disallow it for discrim-subclass just for
+ // consistency-sake (currently does not work anyway)...
+ return persister.getSubclassPropertyTableNumber( propertyName ) != 0;
+ }
+
+ /**
+ * Determine whether the two types are "assignment compatible".
+ *
+ * @param target The type defined in the into-clause.
+ * @param source The type defined in the select clause.
+ * @return True if they are assignment compatible.
+ */
+ private boolean areCompatible(Type target, Type source) {
+ if ( target.equals( source ) ) {
+ // if the types report logical equivalence, return true...
+ return true;
+ }
+
+ // otherwise, perform a "deep equivalence" check...
+
+ if ( !target.getReturnedClass().isAssignableFrom( source.getReturnedClass() ) ) {
+ return false;
+ }
+
+ int[] targetDatatypes = target.sqlTypes( getSessionFactoryHelper().getFactory() );
+ int[] sourceDatatypes = source.sqlTypes( getSessionFactoryHelper().getFactory() );
+
+ if ( targetDatatypes.length != sourceDatatypes.length ) {
+ return false;
+ }
+
+ for ( int i = 0; i < targetDatatypes.length; i++ ) {
+ if ( !areSqlTypesCompatible( targetDatatypes[i], sourceDatatypes[i] ) ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private boolean areSqlTypesCompatible(int target, int source) {
+ switch ( target ) {
+ case Types.TIMESTAMP:
+ return source == Types.DATE || source == Types.TIME || source == Types.TIMESTAMP;
+ case Types.DATE:
+ return source == Types.DATE || source == Types.TIMESTAMP;
+ case Types.TIME:
+ return source == Types.TIME || source == Types.TIMESTAMP;
+ default:
+ return target == source;
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/JavaConstantNode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/JavaConstantNode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/JavaConstantNode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,66 @@
+package org.hibernate.hql.ast.tree;
+
+import org.hibernate.type.Type;
+import org.hibernate.type.TypeFactory;
+import org.hibernate.type.LiteralType;
+import org.hibernate.util.ReflectHelper;
+import org.hibernate.util.StringHelper;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.QueryException;
+import org.hibernate.hql.QueryTranslator;
+
+/**
+ * A node representing a static Java constant.
+ *
+ * @author Steve Ebersole
+ */
+public class JavaConstantNode extends Node implements ExpectedTypeAwareNode, SessionFactoryAwareNode {
+
+ private SessionFactoryImplementor factory;
+
+ private String constantExpression;
+ private Object constantValue;
+ private Type heuristicType;
+
+ private Type expectedType;
+
+ public void setText(String s) {
+ // for some reason the antlr.CommonAST initialization routines force
+ // this method to get called twice. The first time with an empty string
+ if ( StringHelper.isNotEmpty( s ) ) {
+ constantExpression = s;
+ constantValue = ReflectHelper.getConstantValue( s );
+ heuristicType = TypeFactory.heuristicType( constantValue.getClass().getName() );
+ super.setText( s );
+ }
+ }
+
+ public void setExpectedType(Type expectedType) {
+ this.expectedType = expectedType;
+ }
+
+ public Type getExpectedType() {
+ return expectedType;
+ }
+
+ public void setSessionFactory(SessionFactoryImplementor factory) {
+ this.factory = factory;
+ }
+
+ private String resolveToLiteralString(Type type) {
+ try {
+ LiteralType literalType = ( LiteralType ) type;
+ Dialect dialect = factory.getDialect();
+ return literalType.objectToSQLString( constantValue, dialect );
+ }
+ catch ( Throwable t ) {
+ throw new QueryException( QueryTranslator.ERROR_CANNOT_FORMAT_LITERAL + constantExpression, t );
+ }
+ }
+
+ public String getRenderText(SessionFactoryImplementor sessionFactory) {
+ Type type = expectedType == null ? heuristicType : expectedType;
+ return resolveToLiteralString( type );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/LiteralNode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/LiteralNode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/LiteralNode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,41 @@
+// $Id: LiteralNode.java 10060 2006-06-28 02:53:39Z steve.ebersole at jboss.com $
+package org.hibernate.hql.ast.tree;
+
+import org.hibernate.Hibernate;
+import org.hibernate.hql.antlr.HqlSqlTokenTypes;
+import org.hibernate.hql.ast.util.ColumnHelper;
+import org.hibernate.type.Type;
+
+import antlr.SemanticException;
+
+/**
+ * Represents a literal.
+ *
+ * @author josh Jan 8, 2005 10:09:53 AM
+ */
+public class LiteralNode extends AbstractSelectExpression implements HqlSqlTokenTypes {
+
+ public void setScalarColumnText(int i) throws SemanticException {
+ ColumnHelper.generateSingleScalarColumn( this, i );
+ }
+
+ public Type getDataType() {
+ switch ( getType() ) {
+ case NUM_INT:
+ return Hibernate.INTEGER;
+ case NUM_FLOAT:
+ return Hibernate.FLOAT;
+ case NUM_LONG:
+ return Hibernate.LONG;
+ case NUM_DOUBLE:
+ return Hibernate.DOUBLE;
+ case QUOTED_STRING:
+ return Hibernate.STRING;
+ case TRUE:
+ case FALSE:
+ return Hibernate.BOOLEAN;
+ default:
+ return null;
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/MethodNode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/MethodNode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/MethodNode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,190 @@
+// $Id: MethodNode.java 7460 2005-07-12 20:27:29Z steveebersole $
+package org.hibernate.hql.ast.tree;
+
+import java.util.Arrays;
+
+import org.hibernate.dialect.function.SQLFunction;
+import org.hibernate.hql.CollectionProperties;
+import org.hibernate.hql.antlr.SqlTokenTypes;
+import org.hibernate.hql.ast.util.ASTUtil;
+import org.hibernate.hql.ast.util.ColumnHelper;
+import org.hibernate.persister.collection.CollectionPropertyNames;
+import org.hibernate.persister.collection.QueryableCollection;
+import org.hibernate.type.Type;
+
+import antlr.SemanticException;
+import antlr.collections.AST;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Represents a method call.
+ *
+ * @author josh Aug 16, 2004 7:59:42 AM
+ */
+public class MethodNode extends AbstractSelectExpression implements SelectExpression {
+
+ private static final Log log = LogFactory.getLog( MethodNode.class );
+
+ private String methodName;
+ private FromElement fromElement;
+ private String[] selectColumns;
+ private SQLFunction function;
+ private boolean inSelect;
+
+ public void resolve(boolean inSelect) throws SemanticException {
+ // Get the function name node.
+ AST name = getFirstChild();
+ initializeMethodNode( name, inSelect );
+ AST exprList = name.getNextSibling();
+ // If the expression list has exactly one expression, and the type of the expression is a collection
+ // then this might be a collection function, such as index(c) or size(c).
+ if ( ASTUtil.hasExactlyOneChild( exprList ) && isCollectionPropertyMethod() ) {
+ collectionProperty( exprList.getFirstChild(), name );
+ }
+ else {
+ dialectFunction( exprList );
+ }
+ }
+
+ public SQLFunction getSQLFunction() {
+ return function;
+ }
+
+ private void dialectFunction(AST exprList) {
+ function = getSessionFactoryHelper().findSQLFunction( methodName );
+ if ( function != null ) {
+ AST firstChild = exprList != null ? exprList.getFirstChild() : null;
+ Type functionReturnType = getSessionFactoryHelper()
+ .findFunctionReturnType( methodName, firstChild );
+ setDataType( functionReturnType );
+ }
+ //TODO:
+ /*else {
+ methodName = (String) getWalker().getTokenReplacements().get( methodName );
+ }*/
+ }
+
+ public boolean isCollectionPropertyMethod() {
+ return CollectionProperties.isAnyCollectionProperty( methodName );
+ }
+
+ public void initializeMethodNode(AST name, boolean inSelect) {
+ name.setType( SqlTokenTypes.METHOD_NAME );
+ String text = name.getText();
+ methodName = text.toLowerCase(); // Use the lower case function name.
+ this.inSelect = inSelect; // Remember whether we're in a SELECT clause or not.
+ }
+
+ private String getMethodName() {
+ return methodName;
+ }
+
+ private void collectionProperty(AST path, AST name) throws SemanticException {
+ if ( path == null ) {
+ throw new SemanticException( "Collection function " + name.getText() + " has no path!" );
+ }
+
+ SqlNode expr = ( SqlNode ) path;
+ Type type = expr.getDataType();
+ if ( log.isDebugEnabled() ) {
+ log.debug( "collectionProperty() : name=" + name + " type=" + type );
+ }
+
+ resolveCollectionProperty( expr );
+ }
+
+ public boolean isScalar() throws SemanticException {
+ // Method expressions in a SELECT should always be considered scalar.
+ return true;
+ }
+
+ public void resolveCollectionProperty(AST expr) throws SemanticException {
+ String propertyName = CollectionProperties.getNormalizedPropertyName( getMethodName() );
+ if ( expr instanceof FromReferenceNode ) {
+ FromReferenceNode collectionNode = ( FromReferenceNode ) expr;
+ // If this is 'elements' then create a new FROM element.
+ if ( CollectionPropertyNames.COLLECTION_ELEMENTS.equals( propertyName ) ) {
+ handleElements( collectionNode, propertyName );
+ }
+ else {
+ // Not elements(x)
+ fromElement = collectionNode.getFromElement();
+ setDataType( fromElement.getPropertyType( propertyName, propertyName ) );
+ selectColumns = fromElement.toColumns( fromElement.getTableAlias(), propertyName, inSelect );
+ }
+ if ( collectionNode instanceof DotNode ) {
+ prepareAnyImplicitJoins( ( DotNode ) collectionNode );
+ }
+ if ( !inSelect ) {
+ fromElement.setText( "" );
+ fromElement.setUseWhereFragment( false );
+ }
+ prepareSelectColumns( selectColumns );
+ setText( selectColumns[0] );
+ setType( SqlTokenTypes.SQL_TOKEN );
+ }
+ else {
+ throw new SemanticException(
+ "Unexpected expression " + expr +
+ " found for collection function " + propertyName
+ );
+ }
+ }
+
+ private void prepareAnyImplicitJoins(DotNode dotNode) throws SemanticException {
+ if ( dotNode.getLhs() instanceof DotNode ) {
+ DotNode lhs = ( DotNode ) dotNode.getLhs();
+ FromElement lhsOrigin = lhs.getFromElement();
+ if ( lhsOrigin != null && "".equals( lhsOrigin.getText() ) ) {
+ String lhsOriginText = lhsOrigin.getQueryable().getTableName() +
+ " " + lhsOrigin.getTableAlias();
+ lhsOrigin.setText( lhsOriginText );
+ }
+ prepareAnyImplicitJoins( lhs );
+ }
+ }
+
+ private void handleElements(FromReferenceNode collectionNode, String propertyName) {
+ FromElement collectionFromElement = collectionNode.getFromElement();
+ QueryableCollection queryableCollection = collectionFromElement.getQueryableCollection();
+
+ String path = collectionNode.getPath() + "[]." + propertyName;
+ log.debug( "Creating elements for " + path );
+
+ fromElement = collectionFromElement;
+ if ( !collectionFromElement.isCollectionOfValuesOrComponents() ) {
+ getWalker().addQuerySpaces( queryableCollection.getElementPersister().getQuerySpaces() );
+ }
+
+ setDataType( queryableCollection.getElementType() );
+ selectColumns = collectionFromElement.toColumns( fromElement.getTableAlias(), propertyName, inSelect );
+ }
+
+ public void setScalarColumnText(int i) throws SemanticException {
+ if ( selectColumns == null ) { // Dialect function
+ ColumnHelper.generateSingleScalarColumn( this, i );
+ }
+ else { // Collection 'property function'
+ ColumnHelper.generateScalarColumns( this, selectColumns, i );
+ }
+ }
+
+ protected void prepareSelectColumns(String[] columns) {
+ return;
+ }
+
+ public FromElement getFromElement() {
+ return fromElement;
+ }
+
+ public String getDisplayText() {
+ return "{" +
+ "method=" + getMethodName() +
+ ",selectColumns=" + ( selectColumns == null ?
+ null : Arrays.asList( selectColumns ) ) +
+ ",fromElement=" + fromElement.getTableAlias() +
+ "}";
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/Node.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/Node.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/Node.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,74 @@
+package org.hibernate.hql.ast.tree;
+
+import antlr.collections.AST;
+import antlr.Token;
+import org.hibernate.util.StringHelper;
+import org.hibernate.engine.SessionFactoryImplementor;
+
+/**
+ * Base node class for use by Hibernate within its AST trees.
+ *
+ * @author Joshua Davis
+ * @author Steve Ebersole
+ */
+public class Node extends antlr.CommonAST {
+ private String filename;
+ private int line;
+ private int column;
+ private int textLength;
+
+ public Node() {
+ super();
+ }
+
+ public Node(Token tok) {
+ super(tok); // This will call initialize(tok)!
+ }
+
+ /**
+ * Retrieve the text to be used for rendering this particular node.
+ *
+ * @param sessionFactory The session factory
+ * @return The text to use for rendering
+ */
+ public String getRenderText(SessionFactoryImplementor sessionFactory) {
+ // The basic implementation is to simply use the node's text
+ return getText();
+ }
+
+ public void initialize(Token tok) {
+ super.initialize(tok);
+ filename = tok.getFilename();
+ line = tok.getLine();
+ column = tok.getColumn();
+ String text = tok.getText();
+ textLength = StringHelper.isEmpty(text) ? 0 : text.length();
+ }
+
+ public void initialize(AST t) {
+ super.initialize( t );
+ if ( t instanceof Node ) {
+ Node n = (Node)t;
+ filename = n.filename;
+ line = n.line;
+ column = n.column;
+ textLength = n.textLength;
+ }
+ }
+
+ public String getFilename() {
+ return filename;
+ }
+
+ public int getLine() {
+ return line;
+ }
+
+ public int getColumn() {
+ return column;
+ }
+
+ public int getTextLength() {
+ return textLength;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/OperatorNode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/OperatorNode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/OperatorNode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,24 @@
+package org.hibernate.hql.ast.tree;
+
+import org.hibernate.type.Type;
+import antlr.SemanticException;
+
+/**
+ * Contract for nodes representing operators (logic or arithmetic).
+ *
+ * @author <a href="mailto:steve at hibernate.org">Steve Ebersole </a>
+ */
+public interface OperatorNode {
+ /**
+ * Called by the tree walker during hql-sql semantic analysis
+ * after the operator sub-tree is completely built.
+ */
+ public abstract void initialize() throws SemanticException;
+
+ /**
+ * Retrieves the data type for the overall operator expression.
+ *
+ * @return The expression's data type.
+ */
+ public Type getDataType();
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/OrderByClause.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/OrderByClause.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/OrderByClause.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,26 @@
+// $Id: OrderByClause.java 7460 2005-07-12 20:27:29Z steveebersole $
+package org.hibernate.hql.ast.tree;
+
+import org.hibernate.hql.antlr.HqlSqlTokenTypes;
+import org.hibernate.hql.ast.util.ASTUtil;
+
+import antlr.collections.AST;
+
+/**
+ * Implementation of OrderByClause.
+ *
+ * @author Steve Ebersole
+ */
+public class OrderByClause extends HqlSqlWalkerNode implements HqlSqlTokenTypes {
+
+ public void addOrderFragment(String orderByFragment) {
+ AST fragment = ASTUtil.create( getASTFactory(), SQL_TOKEN, orderByFragment );
+ if ( getFirstChild() == null ) {
+ setFirstChild( fragment );
+ }
+ else {
+ addChild( fragment );
+ }
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/ParameterNode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/ParameterNode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/ParameterNode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,52 @@
+// $Id: ParameterNode.java 10060 2006-06-28 02:53:39Z steve.ebersole at jboss.com $
+package org.hibernate.hql.ast.tree;
+
+import org.hibernate.param.ParameterSpecification;
+import org.hibernate.type.Type;
+import org.hibernate.engine.SessionFactoryImplementor;
+
+/**
+ * Implementation of ParameterNode.
+ *
+ * @author Steve Ebersole
+ */
+public class ParameterNode extends HqlSqlWalkerNode implements DisplayableNode, ExpectedTypeAwareNode {
+ private ParameterSpecification parameterSpecification;
+
+ public ParameterSpecification getHqlParameterSpecification() {
+ return parameterSpecification;
+ }
+
+ public void setHqlParameterSpecification(ParameterSpecification parameterSpecification) {
+ this.parameterSpecification = parameterSpecification;
+ }
+
+ public String getDisplayText() {
+ return "{" + ( parameterSpecification == null ? "???" : parameterSpecification.renderDisplayInfo() ) + "}";
+ }
+
+ public void setExpectedType(Type expectedType) {
+ getHqlParameterSpecification().setExpectedType( expectedType );
+ setDataType( expectedType );
+ }
+
+ public Type getExpectedType() {
+ return getHqlParameterSpecification() == null ? null : getHqlParameterSpecification().getExpectedType();
+ }
+
+ public String getRenderText(SessionFactoryImplementor sessionFactory) {
+ int count = 0;
+ if ( getExpectedType() != null && ( count = getExpectedType().getColumnSpan( sessionFactory ) ) > 1 ) {
+ StringBuffer buffer = new StringBuffer();
+ buffer.append( "(?" );
+ for ( int i = 1; i < count; i++ ) {
+ buffer.append( ", ?" );
+ }
+ buffer.append( ")" );
+ return buffer.toString();
+ }
+ else {
+ return "?";
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/PathNode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/PathNode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/PathNode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,16 @@
+// $Id: PathNode.java 7460 2005-07-12 20:27:29Z steveebersole $
+package org.hibernate.hql.ast.tree;
+
+/**
+ * An AST node with a path property. This path property will be the fully qualified name.
+ *
+ * @author josh Nov 7, 2004 10:56:49 AM
+ */
+public interface PathNode {
+ /**
+ * Returns the full path name represented by the node.
+ *
+ * @return the full path name represented by the node.
+ */
+ String getPath();
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/QueryNode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/QueryNode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/QueryNode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,133 @@
+// $Id: QueryNode.java 7486 2005-07-15 04:39:41Z oneovthafew $
+package org.hibernate.hql.ast.tree;
+
+import org.hibernate.hql.antlr.HqlSqlTokenTypes;
+import org.hibernate.hql.antlr.SqlTokenTypes;
+import org.hibernate.hql.ast.util.ASTUtil;
+import org.hibernate.hql.ast.util.ColumnHelper;
+import org.hibernate.type.Type;
+
+import antlr.SemanticException;
+import antlr.collections.AST;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Defines a top-level AST node representing an HQL select statement.
+ *
+ * @author Joshua Davis
+ */
+public class QueryNode extends AbstractRestrictableStatement implements SelectExpression {
+
+ private static final Log log = LogFactory.getLog( QueryNode.class );
+
+ private OrderByClause orderByClause;
+
+ /**
+ * @see Statement#getStatementType()
+ */
+ public int getStatementType() {
+ return HqlSqlTokenTypes.QUERY;
+ }
+
+ /**
+ * @see Statement#needsExecutor()
+ */
+ public boolean needsExecutor() {
+ return false;
+ }
+
+ protected int getWhereClauseParentTokenType() {
+ return SqlTokenTypes.FROM;
+ }
+
+ protected Log getLog() {
+ return log;
+ }
+
+ /**
+ * Locate the select clause that is part of this select statement.
+ * </p>
+ * Note, that this might return null as derived select clauses (i.e., no
+ * select clause at the HQL-level) get generated much later than when we
+ * get created; thus it depends upon lifecycle.
+ *
+ * @return Our select clause, or null.
+ */
+ public final SelectClause getSelectClause() {
+ // Due to the complexity in initializing the SelectClause, do not generate one here.
+ // If it is not found; simply return null...
+ //
+ // Also, do not cache since it gets generated well after we are created.
+ return ( SelectClause ) ASTUtil.findTypeInChildren( this, SqlTokenTypes.SELECT_CLAUSE );
+ }
+
+ public final boolean hasOrderByClause() {
+ OrderByClause orderByClause = locateOrderByClause();
+ return orderByClause != null && orderByClause.getNumberOfChildren() > 0;
+ }
+
+ public final OrderByClause getOrderByClause() {
+ if ( orderByClause == null ) {
+ orderByClause = locateOrderByClause();
+
+ // if there is no order by, make one
+ if ( orderByClause == null ) {
+ log.debug( "getOrderByClause() : Creating a new ORDER BY clause" );
+ orderByClause = ( OrderByClause ) ASTUtil.create( getWalker().getASTFactory(), SqlTokenTypes.ORDER, "ORDER" );
+
+ // Find the WHERE; if there is no WHERE, find the FROM...
+ AST prevSibling = ASTUtil.findTypeInChildren( this, SqlTokenTypes.WHERE );
+ if ( prevSibling == null ) {
+ prevSibling = ASTUtil.findTypeInChildren( this, SqlTokenTypes.FROM );
+ }
+
+ // Now, inject the newly built ORDER BY into the tree
+ orderByClause.setNextSibling( prevSibling.getNextSibling() );
+ prevSibling.setNextSibling( orderByClause );
+ }
+ }
+ return orderByClause;
+ }
+
+ private OrderByClause locateOrderByClause() {
+ return ( OrderByClause ) ASTUtil.findTypeInChildren( this, SqlTokenTypes.ORDER );
+ }
+
+
+ private String alias;
+
+ public String getAlias() {
+ return alias;
+ }
+
+ public FromElement getFromElement() {
+ return null;
+ }
+
+ public boolean isConstructor() {
+ return false;
+ }
+
+ public boolean isReturnableEntity() throws SemanticException {
+ return false;
+ }
+
+ public boolean isScalar() throws SemanticException {
+ return true;
+ }
+
+ public void setAlias(String alias) {
+ this.alias = alias;
+ }
+
+ public void setScalarColumnText(int i) throws SemanticException {
+ ColumnHelper.generateSingleScalarColumn( this, i );
+ }
+
+ public Type getDataType() {
+ return ( (SelectExpression) getSelectClause().getFirstSelectExpression() ).getDataType();
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/ResolvableNode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/ResolvableNode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/ResolvableNode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,38 @@
+// $Id: ResolvableNode.java 7460 2005-07-12 20:27:29Z steveebersole $
+package org.hibernate.hql.ast.tree;
+
+import antlr.SemanticException;
+import antlr.collections.AST;
+
+/**
+ * The contract for expression sub-trees that can resolve themselves.
+ *
+ * @author josh Sep 25, 2004 11:27:36 AM
+ */
+public interface ResolvableNode {
+ /**
+ * Does the work of resolving an identifier or a dot
+ */
+ void resolve(boolean generateJoin, boolean implicitJoin, String classAlias, AST parent) throws SemanticException;
+
+ /**
+ * Does the work of resolving an identifier or a dot, but without a parent node
+ */
+ void resolve(boolean generateJoin, boolean implicitJoin, String classAlias) throws SemanticException;
+
+ /**
+ * Does the work of resolving an identifier or a dot, but without a parent node or alias
+ */
+ void resolve(boolean generateJoin, boolean implicitJoin) throws SemanticException;
+
+ /**
+ * Does the work of resolving inside of the scope of a function call
+ */
+ void resolveInFunctionCall(boolean generateJoin, boolean implicitJoin) throws SemanticException;
+
+ /**
+ * Does the work of resolving an an index [].
+ */
+ void resolveIndex(AST parent) throws SemanticException;
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/RestrictableStatement.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/RestrictableStatement.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/RestrictableStatement.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,41 @@
+// $Id: RestrictableStatement.java 7460 2005-07-12 20:27:29Z steveebersole $
+package org.hibernate.hql.ast.tree;
+
+import antlr.collections.AST;
+
+/**
+ * Type definition for Statements which are restrictable via a where-clause (and
+ * thus also having a from-clause).
+ *
+ * @author Steve Ebersole
+ */
+public interface RestrictableStatement extends Statement {
+ /**
+ * Retreives the from-clause in effect for this statement.
+ *
+ * @return The from-clause for this statement; could be null if the from-clause
+ * has not yet been parsed/generated.
+ */
+ public FromClause getFromClause();
+
+ /**
+ * Does this statement tree currently contain a where clause?
+ *
+ * @return True if a where-clause is found in the statement tree and
+ * that where clause actually defines restrictions; false otherwise.
+ */
+ public boolean hasWhereClause();
+
+ /**
+ * Retreives the where-clause defining the restriction(s) in effect for
+ * this statement.
+ * <p/>
+ * Note that this will generate a where-clause if one was not found, so caution
+ * needs to taken prior to calling this that restrictions will actually exist
+ * in the resulting statement tree (otherwise "unexpected end of subtree" errors
+ * might occur during rendering).
+ *
+ * @return The where clause.
+ */
+ public AST getWhereClause();
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/SelectClause.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/SelectClause.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/SelectClause.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,442 @@
+// $Id: SelectClause.java 10527 2006-09-25 15:13:41Z epbernard $
+package org.hibernate.hql.ast.tree;
+
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.hibernate.hql.antlr.SqlTokenTypes;
+import org.hibernate.hql.ast.util.ASTAppender;
+import org.hibernate.hql.ast.util.ASTIterator;
+import org.hibernate.hql.ast.util.ASTPrinter;
+import org.hibernate.hql.ast.QuerySyntaxException;
+import org.hibernate.type.Type;
+import org.hibernate.QueryException;
+
+import antlr.SemanticException;
+import antlr.collections.AST;
+
+/**
+ * Represents the list of expressions in a SELECT clause.
+ *
+ * @author josh Sep 21, 2004 7:53:55 AM
+ */
+public class SelectClause extends SelectExpressionList {
+
+ private boolean prepared = false;
+ private boolean scalarSelect;
+
+ private List fromElementsForLoad = new ArrayList();
+ //private Type[] sqlResultTypes;
+ private Type[] queryReturnTypes;
+ private String[][] columnNames;
+ private ConstructorNode constructorNode;
+ private List collectionFromElements;
+ private String[] aliases;
+
+ /**
+ * Does this SelectClause represent a scalar query
+ *
+ * @return True if this is a scalara select clause; false otherwise.
+ */
+ public boolean isScalarSelect() {
+ return scalarSelect;
+ }
+
+ public boolean isDistinct() {
+ return getFirstChild() != null && getFirstChild().getType() == SqlTokenTypes.DISTINCT;
+ }
+
+ /**
+ * FromElements which need to be accounted for in the load phase (either for return or for fetch).
+ *
+ * @return List of appropriate FromElements.
+ */
+ public List getFromElementsForLoad() {
+ return fromElementsForLoad;
+ }
+
+ /*
+ * The types represented in the SQL result set.
+ *
+ * @return The types represented in the SQL result set.
+ */
+ /*public Type[] getSqlResultTypes() {
+ return sqlResultTypes;
+ }*/
+
+ /**
+ * The types actually being returned from this query at the "object level".
+ *
+ * @return The query return types.
+ */
+ public Type[] getQueryReturnTypes() {
+ return queryReturnTypes;
+ }
+
+ /**
+ * The HQL aliases, or generated aliases
+ */
+ public String[] getQueryReturnAliases() {
+ return aliases;
+ }
+
+ /**
+ * The column alias names being used in the generated SQL.
+ *
+ * @return The SQL column aliases.
+ */
+ public String[][] getColumnNames() {
+ return columnNames;
+ }
+
+ /**
+ * The constructor to use for dynamic instantiation queries.
+ *
+ * @return The appropriate Constructor reference, or null if not a
+ * dynamic instantiation query.
+ */
+ public Constructor getConstructor() {
+ return constructorNode == null ? null : constructorNode.getConstructor();
+ }
+
+ public boolean isMap() {
+ return constructorNode == null ? false : constructorNode.isMap();
+ }
+
+ public boolean isList() {
+ return constructorNode == null ? false : constructorNode.isList();
+ }
+
+ /**
+ * Prepares an explicitly defined select clause.
+ *
+ * @param fromClause The from clause linked to this select clause.
+ * @throws SemanticException
+ */
+ public void initializeExplicitSelectClause(FromClause fromClause) throws SemanticException {
+ if ( prepared ) {
+ throw new IllegalStateException( "SelectClause was already prepared!" );
+ }
+
+ //explicit = true; // This is an explict Select.
+ //ArrayList sqlResultTypeList = new ArrayList();
+ ArrayList queryReturnTypeList = new ArrayList();
+
+ // First, collect all of the select expressions.
+ // NOTE: This must be done *before* invoking setScalarColumnText() because setScalarColumnText()
+ // changes the AST!!!
+ SelectExpression[] selectExpressions = collectSelectExpressions();
+
+ for ( int i = 0; i < selectExpressions.length; i++ ) {
+ SelectExpression expr = selectExpressions[i];
+
+ if ( expr.isConstructor() ) {
+ constructorNode = ( ConstructorNode ) expr;
+ List constructorArgumentTypeList = constructorNode.getConstructorArgumentTypeList();
+ //sqlResultTypeList.addAll( constructorArgumentTypeList );
+ queryReturnTypeList.addAll( constructorArgumentTypeList );
+ scalarSelect = true;
+ }
+ else {
+ Type type = expr.getDataType();
+ if ( type == null ) {
+ throw new IllegalStateException( "No data type for node: " + expr.getClass().getName() + " "
+ + new ASTPrinter( SqlTokenTypes.class ).showAsString( ( AST ) expr, "" ) );
+ }
+ //sqlResultTypeList.add( type );
+
+ // If the data type is not an association type, it could not have been in the FROM clause.
+ if ( expr.isScalar() ) {
+ scalarSelect = true;
+ }
+
+ if ( isReturnableEntity( expr ) ) {
+ fromElementsForLoad.add( expr.getFromElement() );
+ }
+
+ // Always add the type to the return type list.
+ queryReturnTypeList.add( type );
+ }
+ }
+
+ //init the aliases, after initing the constructornode
+ initAliases(selectExpressions);
+
+ if ( !getWalker().isShallowQuery() ) {
+ // add the fetched entities
+ List fromElements = fromClause.getProjectionList();
+
+ ASTAppender appender = new ASTAppender( getASTFactory(), this ); // Get ready to start adding nodes.
+ int size = fromElements.size();
+
+ Iterator iterator = fromElements.iterator();
+ for ( int k = 0; iterator.hasNext(); k++ ) {
+ FromElement fromElement = ( FromElement ) iterator.next();
+
+ if ( fromElement.isFetch() ) {
+ FromElement origin = null;
+ if ( fromElement.getRealOrigin() == null ) {
+ // work around that crazy issue where the tree contains
+ // "empty" FromElements (no text); afaict, this is caused
+ // by FromElementFactory.createCollectionJoin()
+ if ( fromElement.getOrigin() == null ) {
+ throw new QueryException( "Unable to determine origin of join fetch [" + fromElement.getDisplayText() + "]" );
+ }
+ else {
+ origin = fromElement.getOrigin();
+ }
+ }
+ else {
+ origin = fromElement.getRealOrigin();
+ }
+ if ( !fromElementsForLoad.contains( origin ) ) {
+ throw new QueryException(
+ "query specified join fetching, but the owner " +
+ "of the fetched association was not present in the select list " +
+ "[" + fromElement.getDisplayText() + "]"
+ );
+ }
+ Type type = fromElement.getSelectType();
+ addCollectionFromElement( fromElement );
+ if ( type != null ) {
+ boolean collectionOfElements = fromElement.isCollectionOfValuesOrComponents();
+ if ( !collectionOfElements ) {
+ // Add the type to the list of returned sqlResultTypes.
+ fromElement.setIncludeSubclasses( true );
+ fromElementsForLoad.add( fromElement );
+ //sqlResultTypeList.add( type );
+ // Generate the select expression.
+ String text = fromElement.renderIdentifierSelect( size, k );
+ SelectExpressionImpl generatedExpr = ( SelectExpressionImpl ) appender.append( SqlTokenTypes.SELECT_EXPR, text, false );
+ if ( generatedExpr != null ) {
+ generatedExpr.setFromElement( fromElement );
+ }
+ }
+ }
+ }
+ }
+
+ // generate id select fragment and then property select fragment for
+ // each expression, just like generateSelectFragments().
+ renderNonScalarSelects( collectSelectExpressions(), fromClause );
+ }
+
+ if ( scalarSelect || getWalker().isShallowQuery() ) {
+ // If there are any scalars (non-entities) selected, render the select column aliases.
+ renderScalarSelects( selectExpressions, fromClause );
+ }
+
+ finishInitialization( /*sqlResultTypeList,*/ queryReturnTypeList );
+ }
+
+ private void finishInitialization(/*ArrayList sqlResultTypeList,*/ ArrayList queryReturnTypeList) {
+ //sqlResultTypes = ( Type[] ) sqlResultTypeList.toArray( new Type[sqlResultTypeList.size()] );
+ queryReturnTypes = ( Type[] ) queryReturnTypeList.toArray( new Type[queryReturnTypeList.size()] );
+ initializeColumnNames();
+ prepared = true;
+ }
+
+ private void initializeColumnNames() {
+ // Generate an 2d array of column names, the first dimension is parallel with the
+ // return types array. The second dimension is the list of column names for each
+ // type.
+
+ // todo: we should really just collect these from the various SelectExpressions, rather than regenerating here
+ columnNames = getSessionFactoryHelper().generateColumnNames( queryReturnTypes );
+ }
+
+ /**
+ * Prepares a derived (i.e., not explicitly defined in the query) select clause.
+ *
+ * @param fromClause The from clause to which this select clause is linked.
+ */
+ public void initializeDerivedSelectClause(FromClause fromClause) throws SemanticException {
+ if ( prepared ) {
+ throw new IllegalStateException( "SelectClause was already prepared!" );
+ }
+ //Used to be tested by the TCK but the test is no longer here
+// if ( getSessionFactoryHelper().isStrictJPAQLComplianceEnabled() && !getWalker().isSubQuery() ) {
+// // NOTE : the isSubQuery() bit is a temporary hack...
+// throw new QuerySyntaxException( "JPA-QL compliance requires select clause" );
+// }
+ List fromElements = fromClause.getProjectionList();
+
+ ASTAppender appender = new ASTAppender( getASTFactory(), this ); // Get ready to start adding nodes.
+ int size = fromElements.size();
+ ArrayList sqlResultTypeList = new ArrayList( size );
+ ArrayList queryReturnTypeList = new ArrayList( size );
+
+ Iterator iterator = fromElements.iterator();
+ for ( int k = 0; iterator.hasNext(); k++ ) {
+ FromElement fromElement = ( FromElement ) iterator.next();
+ Type type = fromElement.getSelectType();
+
+ addCollectionFromElement( fromElement );
+
+ if ( type != null ) {
+ boolean collectionOfElements = fromElement.isCollectionOfValuesOrComponents();
+ if ( !collectionOfElements ) {
+ if ( !fromElement.isFetch() ) {
+ // Add the type to the list of returned sqlResultTypes.
+ queryReturnTypeList.add( type );
+ }
+ fromElementsForLoad.add( fromElement );
+ sqlResultTypeList.add( type );
+ // Generate the select expression.
+ String text = fromElement.renderIdentifierSelect( size, k );
+ SelectExpressionImpl generatedExpr = ( SelectExpressionImpl ) appender.append( SqlTokenTypes.SELECT_EXPR, text, false );
+ if ( generatedExpr != null ) {
+ generatedExpr.setFromElement( fromElement );
+ }
+ }
+ }
+ }
+
+ // Get all the select expressions (that we just generated) and render the select.
+ SelectExpression[] selectExpressions = collectSelectExpressions();
+
+ if ( getWalker().isShallowQuery() ) {
+ renderScalarSelects( selectExpressions, fromClause );
+ }
+ else {
+ renderNonScalarSelects( selectExpressions, fromClause );
+ }
+ finishInitialization( /*sqlResultTypeList,*/ queryReturnTypeList );
+ }
+
+ public static boolean VERSION2_SQL = false;
+
+ private void addCollectionFromElement(FromElement fromElement) {
+ if ( fromElement.isFetch() ) {
+ if ( fromElement.isCollectionJoin() || fromElement.getQueryableCollection() != null ) {
+ String suffix;
+ if (collectionFromElements==null) {
+ collectionFromElements = new ArrayList();
+ suffix = VERSION2_SQL ? "__" : "0__";
+ }
+ else {
+ suffix = Integer.toString( collectionFromElements.size() ) + "__";
+ }
+ collectionFromElements.add( fromElement );
+ fromElement.setCollectionSuffix( suffix );
+ }
+ }
+ }
+
+ protected AST getFirstSelectExpression() {
+ AST n = getFirstChild();
+ // Skip 'DISTINCT' and 'ALL', so we return the first expression node.
+ while ( n != null && ( n.getType() == SqlTokenTypes.DISTINCT || n.getType() == SqlTokenTypes.ALL ) ) {
+ n = n.getNextSibling();
+ }
+ return n;
+ }
+
+ private boolean isReturnableEntity(SelectExpression selectExpression) throws SemanticException {
+ FromElement fromElement = selectExpression.getFromElement();
+ boolean isFetchOrValueCollection = fromElement != null &&
+ ( fromElement.isFetch() || fromElement.isCollectionOfValuesOrComponents() );
+ if ( isFetchOrValueCollection ) {
+ return false;
+ }
+ else {
+ return selectExpression.isReturnableEntity();
+ }
+ }
+
+ private void renderScalarSelects(SelectExpression[] se, FromClause currentFromClause) throws SemanticException {
+ if ( !currentFromClause.isSubQuery() ) {
+ for ( int i = 0; i < se.length; i++ ) {
+ SelectExpression expr = se[i];
+ expr.setScalarColumnText( i ); // Create SQL_TOKEN nodes for the columns.
+ }
+ }
+ }
+
+ private void initAliases(SelectExpression[] selectExpressions) {
+ if (constructorNode==null) {
+ aliases = new String[selectExpressions.length];
+ for ( int i=0; i<selectExpressions.length; i++ ) {
+ String alias = selectExpressions[i].getAlias();
+ aliases[i] = alias==null ? Integer.toString(i) : alias;
+ }
+ }
+ else {
+ aliases = constructorNode.getAliases();
+ }
+ }
+
+ private void renderNonScalarSelects(SelectExpression[] selectExpressions, FromClause currentFromClause)
+ throws SemanticException {
+ ASTAppender appender = new ASTAppender( getASTFactory(), this );
+ final int size = selectExpressions.length;
+ int nonscalarSize = 0;
+ for ( int i = 0; i < size; i++ ) {
+ if ( !selectExpressions[i].isScalar() ) nonscalarSize++;
+ }
+
+ int j = 0;
+ for ( int i = 0; i < size; i++ ) {
+ if ( !selectExpressions[i].isScalar() ) {
+ SelectExpression expr = selectExpressions[i];
+ FromElement fromElement = expr.getFromElement();
+ if ( fromElement != null ) {
+ renderNonScalarIdentifiers( fromElement, nonscalarSize, j, expr, appender );
+ j++;
+ }
+ }
+ }
+
+ if ( !currentFromClause.isSubQuery() ) {
+ // Generate the property select tokens.
+ int k = 0;
+ for ( int i = 0; i < size; i++ ) {
+ if ( !selectExpressions[i].isScalar() ) {
+ FromElement fromElement = selectExpressions[i].getFromElement();
+ if ( fromElement != null ) {
+ renderNonScalarProperties( appender, fromElement, nonscalarSize, k );
+ k++;
+ }
+ }
+ }
+ }
+ }
+
+ private void renderNonScalarIdentifiers(FromElement fromElement, int nonscalarSize, int j, SelectExpression expr, ASTAppender appender) {
+ String text = fromElement.renderIdentifierSelect( nonscalarSize, j );
+ if ( !fromElement.getFromClause().isSubQuery() ) {
+ if ( !scalarSelect && !getWalker().isShallowQuery() ) {
+ //TODO: is this a bit ugly?
+ expr.setText( text );
+ }
+ else {
+ appender.append( SqlTokenTypes.SQL_TOKEN, text, false );
+ }
+ }
+ }
+
+ private void renderNonScalarProperties(ASTAppender appender, FromElement fromElement, int nonscalarSize, int k) {
+ String text = fromElement.renderPropertySelect( nonscalarSize, k );
+ appender.append( SqlTokenTypes.SQL_TOKEN, text, false );
+ if ( fromElement.getQueryableCollection() != null && fromElement.isFetch() ) {
+ text = fromElement.renderCollectionSelectFragment( nonscalarSize, k );
+ appender.append( SqlTokenTypes.SQL_TOKEN, text, false );
+ }
+ // Look through the FromElement's children to find any collections of values that should be fetched...
+ ASTIterator iter = new ASTIterator( fromElement );
+ while ( iter.hasNext() ) {
+ FromElement child = ( FromElement ) iter.next();
+ if ( child.isCollectionOfValuesOrComponents() && child.isFetch() ) {
+ // Need a better way to define the suffixes here...
+ text = child.renderValueCollectionSelectFragment( nonscalarSize, nonscalarSize + k );
+ appender.append( SqlTokenTypes.SQL_TOKEN, text, false );
+ }
+ }
+ }
+
+ public List getCollectionFromElements() {
+ return collectionFromElements;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/SelectExpression.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/SelectExpression.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/SelectExpression.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,61 @@
+// $Id: SelectExpression.java 7460 2005-07-12 20:27:29Z steveebersole $
+package org.hibernate.hql.ast.tree;
+
+import org.hibernate.type.Type;
+
+import antlr.SemanticException;
+
+/**
+ * Represents an element of a projection list, i.e. a select expression.
+ *
+ * @author josh Sep 21, 2004 9:00:13 PM
+ */
+public interface SelectExpression {
+ /**
+ * Returns the data type of the select expression.
+ *
+ * @return The data type of the select expression.
+ */
+ Type getDataType();
+
+ /**
+ * Appends AST nodes that represent the columns after the current AST node.
+ * (e.g. 'as col0_O_')
+ *
+ * @param i The index of the select expression in the projection list.
+ */
+ void setScalarColumnText(int i) throws SemanticException;
+
+ /**
+ * Returns the FROM element that this expression refers to.
+ *
+ * @return The FROM element.
+ */
+ FromElement getFromElement();
+
+ /**
+ * Returns true if the element is a constructor (e.g. new Foo).
+ *
+ * @return true if the element is a constructor (e.g. new Foo).
+ */
+ boolean isConstructor();
+
+ /**
+ * Returns true if this select expression represents an entity that can be returned.
+ *
+ * @return true if this select expression represents an entity that can be returned.
+ */
+ boolean isReturnableEntity() throws SemanticException;
+
+ /**
+ * Sets the text of the node.
+ *
+ * @param text the new node text.
+ */
+ void setText(String text);
+
+ boolean isScalar() throws SemanticException;
+
+ void setAlias(String alias);
+ String getAlias();
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/SelectExpressionImpl.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/SelectExpressionImpl.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/SelectExpressionImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,27 @@
+// $Id: SelectExpressionImpl.java 7460 2005-07-12 20:27:29Z steveebersole $
+package org.hibernate.hql.ast.tree;
+
+import antlr.SemanticException;
+import antlr.collections.AST;
+
+/**
+ * A select expression that was generated by a FROM element.
+ *
+ * @author josh Nov 6, 2004 8:27:38 AM
+ */
+public class SelectExpressionImpl extends FromReferenceNode implements SelectExpression {
+
+ public void resolveIndex(AST parent) throws SemanticException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setScalarColumnText(int i) throws SemanticException {
+ String text = getFromElement().renderScalarIdentifierSelect( i );
+ setText( text );
+ }
+
+ public void resolve(boolean generateJoin, boolean implicitJoin, String classAlias, AST parent) throws SemanticException {
+ // Generated select expressions are already resolved, nothing to do.
+ return;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/SelectExpressionList.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/SelectExpressionList.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/SelectExpressionList.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,48 @@
+// $Id: SelectExpressionList.java 7460 2005-07-12 20:27:29Z steveebersole $
+package org.hibernate.hql.ast.tree;
+
+import java.util.ArrayList;
+
+import org.hibernate.hql.antlr.SqlTokenTypes;
+import org.hibernate.hql.ast.util.ASTPrinter;
+
+import antlr.collections.AST;
+
+/**
+ * Common behavior - a node that contains a list of select expressions.
+ *
+ * @author josh Nov 6, 2004 8:51:00 AM
+ */
+public abstract class SelectExpressionList extends HqlSqlWalkerNode {
+ /**
+ * Returns an array of SelectExpressions gathered from the children of the given parent AST node.
+ *
+ * @return an array of SelectExpressions gathered from the children of the given parent AST node.
+ */
+ public SelectExpression[] collectSelectExpressions() {
+ // Get the first child to be considered. Sub-classes may do this differently in order to skip nodes that
+ // are not select expressions (e.g. DISTINCT).
+ AST firstChild = getFirstSelectExpression();
+ AST parent = this;
+ ArrayList list = new ArrayList( parent.getNumberOfChildren() );
+ for ( AST n = firstChild; n != null; n = n.getNextSibling() ) {
+ if ( n instanceof SelectExpression ) {
+ list.add( n );
+ }
+ else {
+ throw new IllegalStateException( "Unexpected AST: " + n.getClass().getName() + " " + new ASTPrinter( SqlTokenTypes.class ).showAsString( n, "" ) );
+ }
+ }
+ return ( SelectExpression[] ) list.toArray( new SelectExpression[list.size()] );
+ }
+
+ /**
+ * Returns the first select expression node that should be considered when building the array of select
+ * expressions.
+ *
+ * @return the first select expression node that should be considered when building the array of select
+ * expressions
+ */
+ protected abstract AST getFirstSelectExpression();
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/SessionFactoryAwareNode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/SessionFactoryAwareNode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/SessionFactoryAwareNode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,12 @@
+package org.hibernate.hql.ast.tree;
+
+import org.hibernate.engine.SessionFactoryImplementor;
+
+/**
+ * Interface for nodes which require access to the SessionFactory
+ *
+ * @author Steve Ebersole
+ */
+public interface SessionFactoryAwareNode {
+ public void setSessionFactory(SessionFactoryImplementor sessionFactory);
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/SqlFragment.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/SqlFragment.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/SqlFragment.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,30 @@
+// $Id: SqlFragment.java 8215 2005-09-23 16:51:56Z pgmjsd $
+package org.hibernate.hql.ast.tree;
+
+import org.hibernate.sql.JoinFragment;
+
+/**
+ * Represents an SQL fragment in the AST.
+ *
+ * @author josh Dec 5, 2004 9:01:52 AM
+ */
+public class SqlFragment extends Node {
+ private JoinFragment joinFragment;
+ private FromElement fromElement;
+
+ public void setJoinFragment(JoinFragment joinFragment) {
+ this.joinFragment = joinFragment;
+ }
+
+ public boolean hasFilterCondition() {
+ return joinFragment.hasFilterCondition();
+ }
+
+ public void setFromElement(FromElement fromElement) {
+ this.fromElement = fromElement;
+ }
+
+ public FromElement getFromElement() {
+ return fromElement;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/SqlNode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/SqlNode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/SqlNode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,41 @@
+// $Id: SqlNode.java 8215 2005-09-23 16:51:56Z pgmjsd $
+package org.hibernate.hql.ast.tree;
+
+import org.hibernate.type.Type;
+
+/**
+ * A base AST node for the intermediate tree.
+ * User: josh
+ * Date: Dec 6, 2003
+ * Time: 10:29:14 AM
+ */
+public class SqlNode extends Node {
+ /**
+ * The original text for the node, mostly for debugging.
+ */
+ private String originalText;
+ /**
+ * The data type of this node. Null for 'no type'.
+ */
+ private Type dataType;
+
+ public void setText(String s) {
+ super.setText( s );
+ if ( s != null && s.length() > 0 && originalText == null ) {
+ originalText = s;
+ }
+ }
+
+ public String getOriginalText() {
+ return originalText;
+ }
+
+ public Type getDataType() {
+ return dataType;
+ }
+
+ public void setDataType(Type dataType) {
+ this.dataType = dataType;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/Statement.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/Statement.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/Statement.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,36 @@
+// $Id: Statement.java 7460 2005-07-12 20:27:29Z steveebersole $
+package org.hibernate.hql.ast.tree;
+
+import org.hibernate.hql.ast.HqlSqlWalker;
+
+/**
+ * Common interface modeling the different HQL statements (i.e., INSERT, UPDATE, DELETE, SELECT).
+ *
+ * @author Steve Ebersole
+ */
+public interface Statement {
+
+ /**
+ * Retreive the "phase 2" walker which generated this statement tree.
+ *
+ * @return The HqlSqlWalker instance which generated this statement tree.
+ */
+ public HqlSqlWalker getWalker();
+
+ /**
+ * Return the main token type representing the type of this statement.
+ *
+ * @return The corresponding token type.
+ */
+ public int getStatementType();
+
+ /**
+ * Does this statement require the StatementExecutor?
+ * </p>
+ * Essentially, at the JDBC level, does this require an executeUpdate()?
+ *
+ * @return True if this statement should be handed off to the
+ * StatementExecutor to be executed; false otherwise.
+ */
+ public boolean needsExecutor();
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/UnaryArithmeticNode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/UnaryArithmeticNode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/UnaryArithmeticNode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,27 @@
+//$Id: UnaryArithmeticNode.java 8407 2005-10-14 17:23:18Z steveebersole $
+package org.hibernate.hql.ast.tree;
+
+import org.hibernate.hql.ast.util.ColumnHelper;
+import org.hibernate.type.Type;
+
+import antlr.SemanticException;
+
+public class UnaryArithmeticNode extends AbstractSelectExpression implements UnaryOperatorNode {
+
+ public Type getDataType() {
+ return ( ( SqlNode ) getOperand() ).getDataType();
+ }
+
+ public void setScalarColumnText(int i) throws SemanticException {
+ ColumnHelper.generateSingleScalarColumn( this, i );
+ }
+
+ public void initialize() {
+ // nothing to do; even if the operand is a parameter, no way we could
+ // infer an appropriate expected type here
+ }
+
+ public Node getOperand() {
+ return ( Node ) getFirstChild();
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/UnaryLogicOperatorNode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/UnaryLogicOperatorNode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/UnaryLogicOperatorNode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,23 @@
+package org.hibernate.hql.ast.tree;
+
+import org.hibernate.type.Type;
+import org.hibernate.Hibernate;
+
+/**
+ * @author <a href="mailto:steve at hibernate.org">Steve Ebersole </a>
+ */
+public class UnaryLogicOperatorNode extends SqlNode implements UnaryOperatorNode {
+ public Node getOperand() {
+ return ( Node ) getFirstChild();
+ }
+
+ public void initialize() {
+ // nothing to do; even if the operand is a parameter, no way we could
+ // infer an appropriate expected type here
+ }
+
+ public Type getDataType() {
+ // logic operators by definition resolve to booleans
+ return Hibernate.BOOLEAN;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/UnaryOperatorNode.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/UnaryOperatorNode.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/UnaryOperatorNode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,15 @@
+package org.hibernate.hql.ast.tree;
+
+/**
+ * Contract for nodes representing unary operators.
+ *
+ * @author <a href="mailto:steve at hibernate.org">Steve Ebersole </a>
+ */
+public interface UnaryOperatorNode extends OperatorNode {
+ /**
+ * Retrievs the node representing the operator's single operand.
+ *
+ * @return The operator's single operand.
+ */
+ public Node getOperand();
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/UpdateStatement.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/UpdateStatement.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/tree/UpdateStatement.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,47 @@
+// $Id: UpdateStatement.java 7460 2005-07-12 20:27:29Z steveebersole $
+package org.hibernate.hql.ast.tree;
+
+import org.hibernate.hql.antlr.HqlSqlTokenTypes;
+import org.hibernate.hql.antlr.SqlTokenTypes;
+import org.hibernate.hql.ast.util.ASTUtil;
+
+import antlr.collections.AST;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Defines a top-level AST node representing an HQL update statement.
+ *
+ * @author Steve Ebersole
+ */
+public class UpdateStatement extends AbstractRestrictableStatement {
+
+ private static final Log log = LogFactory.getLog( UpdateStatement.class );
+
+ /**
+ * @see org.hibernate.hql.ast.tree.Statement#getStatementType()
+ */
+ public int getStatementType() {
+ return SqlTokenTypes.UPDATE;
+ }
+
+ /**
+ * @see org.hibernate.hql.ast.tree.Statement#needsExecutor()
+ */
+ public boolean needsExecutor() {
+ return true;
+ }
+
+ protected int getWhereClauseParentTokenType() {
+ return SqlTokenTypes.SET;
+ }
+
+ protected Log getLog() {
+ return log;
+ }
+
+ public AST getSetClause() {
+ return ASTUtil.findTypeInChildren( this, HqlSqlTokenTypes.SET );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/ASTAppender.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/ASTAppender.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/ASTAppender.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,46 @@
+// $Id: ASTAppender.java 7460 2005-07-12 20:27:29Z steveebersole $
+package org.hibernate.hql.ast.util;
+
+import antlr.ASTFactory;
+import antlr.collections.AST;
+
+/**
+ * Appends child nodes to a parent efficiently.
+ *
+ * @author josh Jul 24, 2004 8:28:23 AM
+ */
+public class ASTAppender {
+ private AST parent;
+ private AST last;
+ private ASTFactory factory;
+
+ public ASTAppender(ASTFactory factory, AST parent) {
+ this( parent );
+ this.factory = factory;
+ }
+
+ public ASTAppender(AST parent) {
+ this.parent = parent;
+ this.last = ASTUtil.getLastChild( parent );
+ }
+
+ public AST append(int type, String text, boolean appendIfEmpty) {
+ if ( text != null && ( appendIfEmpty || text.length() > 0 ) ) {
+ return append( factory.create( type, text ) );
+ }
+ else {
+ return null;
+ }
+ }
+
+ public AST append(AST child) {
+ if ( last == null ) {
+ parent.setFirstChild( child );
+ }
+ else {
+ last.setNextSibling( child );
+ }
+ last = child;
+ return last;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/ASTIterator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/ASTIterator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/ASTIterator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,70 @@
+// $Id: ASTIterator.java 7460 2005-07-12 20:27:29Z steveebersole $
+package org.hibernate.hql.ast.util;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+
+import antlr.collections.AST;
+
+/**
+ * Depth first iteration of an ANTLR AST.
+ *
+ * @author josh Sep 25, 2004 7:44:39 AM
+ */
+public class ASTIterator implements Iterator {
+ private AST next, current;
+ private LinkedList parents = new LinkedList();
+
+ public void remove() {
+ throw new UnsupportedOperationException( "remove() is not supported" );
+ }
+
+ public boolean hasNext() {
+ return next != null;
+ }
+
+ public Object next() {
+ return nextNode();
+ }
+
+ public ASTIterator(AST tree) {
+ next = tree;
+ down();
+ }
+
+ public AST nextNode() {
+ current = next;
+ if ( next != null ) {
+ AST nextSibling = next.getNextSibling();
+ if ( nextSibling == null ) {
+ next = pop();
+ }
+ else {
+ next = nextSibling;
+ down();
+ }
+ }
+ return current;
+ }
+
+ private void down() {
+ while ( next != null && next.getFirstChild() != null ) {
+ push( next );
+ next = next.getFirstChild();
+ }
+ }
+
+ private void push(AST parent) {
+ parents.addFirst( parent );
+ }
+
+ private AST pop() {
+ if ( parents.size() == 0 ) {
+ return null;
+ }
+ else {
+ return ( AST ) parents.removeFirst();
+ }
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/ASTParentsFirstIterator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/ASTParentsFirstIterator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/ASTParentsFirstIterator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,73 @@
+// $Id: ASTParentsFirstIterator.java 7460 2005-07-12 20:27:29Z steveebersole $
+package org.hibernate.hql.ast.util;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+
+import antlr.collections.AST;
+
+/**
+ * Depth first iteration of an ANTLR AST.
+ *
+ * @author josh Sep 25, 2004 7:44:39 AM
+ */
+public class ASTParentsFirstIterator implements Iterator {
+ private AST next, current, tree;
+ private LinkedList parents = new LinkedList();
+
+ public void remove() {
+ throw new UnsupportedOperationException( "remove() is not supported" );
+ }
+
+ public boolean hasNext() {
+ return next != null;
+ }
+
+ public Object next() {
+ return nextNode();
+ }
+
+ public ASTParentsFirstIterator(AST tree) {
+ this.tree = next = tree;
+ }
+
+ public AST nextNode() {
+ current = next;
+ if ( next != null ) {
+ AST child = next.getFirstChild();
+ if ( child == null ) {
+ AST sibling = next.getNextSibling();
+ if ( sibling == null ) {
+ AST parent = pop();
+ while ( parent != null && parent.getNextSibling() == null )
+ parent = pop();
+ next = ( parent != null ) ? parent.getNextSibling() : null;
+ }
+ else {
+ next = sibling;
+ }
+ }
+ else {
+ if ( next != tree ) {
+ push( next );
+ }
+ next = child;
+ }
+ }
+ return current;
+ }
+
+ private void push(AST parent) {
+ parents.addFirst( parent );
+ }
+
+ private AST pop() {
+ if ( parents.size() == 0 ) {
+ return null;
+ }
+ else {
+ return ( AST ) parents.removeFirst();
+ }
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/ASTPrinter.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/ASTPrinter.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/ASTPrinter.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,262 @@
+// $Id: ASTPrinter.java 7460 2005-07-12 20:27:29Z steveebersole $
+package org.hibernate.hql.ast.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.hibernate.hql.ast.tree.DisplayableNode;
+import org.hibernate.util.StringHelper;
+
+import antlr.collections.AST;
+
+/**
+ * An 'ASCII art' AST printer for debugging ANTLR grammars.
+ *
+ * @author Joshua Davis (pgmjsd at sourceforge.net)
+ */
+public class ASTPrinter {
+ private Map tokenTypeNamesByTokenType;
+ private Class tokenTypeConstants;
+ private boolean showClassNames = true;
+
+ /**
+ * Constructs an org.hibernate.hql.antlr.ASTPrinter, given the class that contains the token type
+ * constants (typically the '{grammar}TokenTypes' interface generated by
+ * ANTLR).
+ *
+ * @param tokenTypeConstants The class with token type constants in it.
+ */
+ public ASTPrinter(Class tokenTypeConstants) {
+ this.tokenTypeConstants = tokenTypeConstants;
+ }
+
+ /**
+ * Returns true if the node class names will be displayed.
+ *
+ * @return true if the node class names will be displayed.
+ */
+ public boolean isShowClassNames() {
+ return showClassNames;
+ }
+
+ /**
+ * Enables or disables AST node class name display.
+ *
+ * @param showClassNames true to enable class name display, false to disable
+ */
+ public void setShowClassNames(boolean showClassNames) {
+ this.showClassNames = showClassNames;
+ }
+
+ /**
+ * Prints the AST in 'ASCII art' tree form to the specified print stream.
+ *
+ * @param ast The AST to print.
+ * @param out The print stream.
+ */
+ private void showAst(AST ast, PrintStream out) {
+ showAst( ast, new PrintWriter( out ) );
+ }
+
+ /**
+ * Prints the AST in 'ASCII art' tree form to the specified print writer.
+ *
+ * @param ast The AST to print.
+ * @param pw The print writer.
+ */
+ public void showAst(AST ast, PrintWriter pw) {
+ ArrayList parents = new ArrayList();
+ showAst( parents, pw, ast );
+ pw.flush();
+ }
+
+ /**
+ * Prints the AST in 'ASCII art' tree form into a string.
+ *
+ * @param ast The AST to display.
+ * @param header The header for the display.
+ * @return The AST in 'ASCII art' form, as a string.
+ */
+ public String showAsString(AST ast, String header) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PrintStream ps = new PrintStream( baos );
+ ps.println( header );
+ showAst( ast, ps );
+ ps.flush();
+ return new String( baos.toByteArray() );
+ }
+
+ /**
+ * Get a single token type name in the specified set of token type constants (interface).
+ *
+ * @param tokenTypeConstants Token type constants interface (e.g. HqlSqlTokenTypes.class).
+ * @param type The token type ( typically from ast.getType() ).
+ * @return The token type name, *or* the integer value if the name could not be found for some reason.
+ */
+ public static String getConstantName(Class tokenTypeConstants, int type) {
+ String tokenTypeName = null;
+ if ( tokenTypeConstants != null ) {
+ Field[] fields = tokenTypeConstants.getFields();
+ for ( int i = 0; i < fields.length; i++ ) {
+ Field field = fields[i];
+ tokenTypeName = getTokenTypeName( field, type, true );
+ if ( tokenTypeName != null ) {
+ break; // Stop if found.
+ }
+ } // for
+ } // if type constants were provided
+
+ // Use the integer value if no token type name was found
+ if ( tokenTypeName == null ) {
+ tokenTypeName = Integer.toString( type );
+ }
+
+ return tokenTypeName;
+ }
+
+ private static String getTokenTypeName(Field field, int type, boolean checkType) {
+ if ( Modifier.isStatic( field.getModifiers() ) ) {
+ try {
+ Object value = field.get( null );
+ if ( !checkType ) {
+ return field.getName();
+ }
+ else if ( value instanceof Integer ) {
+ Integer integer = ( Integer ) value;
+ if ( integer.intValue() == type ) {
+ return field.getName();
+ }
+ } // if value is an integer
+ } // try
+ catch ( IllegalArgumentException ignore ) {
+ }
+ catch ( IllegalAccessException ignore ) {
+ }
+ } // if the field is static
+ return null;
+ }
+
+ /**
+ * Returns the token type name for the given token type.
+ *
+ * @param type The token type.
+ * @return String - The token type name from the token type constant class,
+ * or just the integer as a string if none exists.
+ */
+ private String getTokenTypeName(int type) {
+ // If the class with the constants in it was not supplied, just
+ // use the integer token type as the token type name.
+ if ( tokenTypeConstants == null ) {
+ return Integer.toString( type );
+ }
+
+ // Otherwise, create a type id -> name map from the class if it
+ // hasn't already been created.
+ if ( tokenTypeNamesByTokenType == null ) {
+ Field[] fields = tokenTypeConstants.getFields();
+ tokenTypeNamesByTokenType = new HashMap();
+ String tokenTypeName = null;
+ for ( int i = 0; i < fields.length; i++ ) {
+ Field field = fields[i];
+ tokenTypeName = getTokenTypeName( field, type, false );
+ if ( tokenTypeName != null ) {
+ try {
+ tokenTypeNamesByTokenType.put( field.get( null ), field.getName() );
+ }
+ catch ( IllegalAccessException ignore ) {
+ }
+ }
+ } // for
+ } // if the map hasn't been created.
+
+ return ( String ) tokenTypeNamesByTokenType.get( new Integer( type ) );
+ }
+
+ private void showAst(ArrayList parents, PrintWriter pw, AST ast) {
+ if ( ast == null ) {
+ pw.println( "AST is null!" );
+ return;
+ }
+
+ for ( int i = 0; i < parents.size(); i++ ) {
+ AST parent = ( AST ) parents.get( i );
+ if ( parent.getNextSibling() == null ) {
+
+ pw.print( " " );
+ }
+ else {
+ pw.print( " | " );
+ }
+ }
+
+ if ( ast.getNextSibling() == null ) {
+ pw.print( " \\-" );
+ }
+ else {
+ pw.print( " +-" );
+ }
+
+ showNode( pw, ast );
+
+ ArrayList newParents = new ArrayList( parents );
+ newParents.add( ast );
+ for ( AST child = ast.getFirstChild(); child != null; child = child.getNextSibling() ) {
+ showAst( newParents, pw, child );
+ }
+ newParents.clear();
+ }
+
+ private void showNode(PrintWriter pw, AST ast) {
+ String s = nodeToString( ast, isShowClassNames() );
+ pw.println( s );
+ }
+
+ public String nodeToString(AST ast, boolean showClassName) {
+ if ( ast == null ) {
+ return "{null}";
+ }
+ StringBuffer buf = new StringBuffer();
+ buf.append( "[" ).append( getTokenTypeName( ast.getType() ) ).append( "] " );
+ if ( showClassName ) {
+ buf.append( StringHelper.unqualify( ast.getClass().getName() ) ).append( ": " );
+ }
+
+ buf.append( "'" );
+ String text = ast.getText();
+ appendEscapedMultibyteChars(text, buf);
+ buf.append( "'" );
+ if ( ast instanceof DisplayableNode ) {
+ DisplayableNode displayableNode = ( DisplayableNode ) ast;
+ // Add a space before the display text.
+ buf.append( " " ).append( displayableNode.getDisplayText() );
+ }
+ String s = buf.toString();
+ return s;
+ }
+
+ public static void appendEscapedMultibyteChars(String text, StringBuffer buf) {
+ char[] chars = text.toCharArray();
+ for (int i = 0; i < chars.length; i++) {
+ char aChar = chars[i];
+ if (aChar > 256) {
+ buf.append("\\u");
+ buf.append(Integer.toHexString(aChar));
+ }
+ else
+ buf.append(aChar);
+ }
+ }
+
+ public static String escapeMultibyteChars(String text)
+ {
+ StringBuffer buf = new StringBuffer();
+ appendEscapedMultibyteChars(text,buf);
+ return buf.toString();
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/ASTUtil.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/ASTUtil.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/ASTUtil.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,360 @@
+// $Id: ASTUtil.java 10000 2006-06-08 21:04:45Z steve.ebersole at jboss.com $
+package org.hibernate.hql.ast.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import antlr.ASTFactory;
+import antlr.collections.AST;
+import antlr.collections.impl.ASTArray;
+
+/**
+ * Provides utility methods for AST traversal and manipulation.
+ *
+ * @author Joshua Davis (pgmjsd at sourceforge.net)
+ */
+public final class ASTUtil {
+ /**
+ * Private empty constructor.
+ * (or else checkstyle says: 'warning: Utility classes should not have a public or default constructor.')
+ *
+ * @deprecated (tell clover to ignore this)
+ */
+ private ASTUtil() {
+ }
+
+ /**
+ * Creates a single node AST.
+ *
+ * @param astFactory The factory.
+ * @param type The node type.
+ * @param text The node text.
+ * @return AST - A single node tree.
+ */
+ public static AST create(ASTFactory astFactory, int type, String text) {
+ AST node = astFactory.create( type, text );
+ return node;
+ }
+
+ /**
+ * Creates a single node AST as a sibling.
+ *
+ * @param astFactory The factory.
+ * @param type The node type.
+ * @param text The node text.
+ * @param prevSibling The previous sibling.
+ * @return AST - A single node tree.
+ */
+ public static AST createSibling(ASTFactory astFactory, int type, String text, AST prevSibling) {
+ AST node = astFactory.create( type, text );
+ node.setNextSibling( prevSibling.getNextSibling() );
+ prevSibling.setNextSibling( node );
+ return node;
+ }
+
+ public static AST insertSibling(AST node, AST prevSibling) {
+ node.setNextSibling( prevSibling.getNextSibling() );
+ prevSibling.setNextSibling( node );
+ return node;
+ }
+
+ /**
+ * Creates a 'binary operator' subtree, given the information about the
+ * parent and the two child nodex.
+ *
+ * @param factory The AST factory.
+ * @param parentType The type of the parent node.
+ * @param parentText The text of the parent node.
+ * @param child1 The first child.
+ * @param child2 The second child.
+ * @return AST - A new sub-tree of the form "(parent child1 child2)"
+ */
+ public static AST createBinarySubtree(ASTFactory factory, int parentType, String parentText, AST child1, AST child2) {
+ ASTArray array = createAstArray( factory, 3, parentType, parentText, child1 );
+ array.add( child2 );
+ return factory.make( array );
+ }
+
+ /**
+ * Creates a single parent of the specified child (i.e. a 'unary operator'
+ * subtree).
+ *
+ * @param factory The AST factory.
+ * @param parentType The type of the parent node.
+ * @param parentText The text of the parent node.
+ * @param child The child.
+ * @return AST - A new sub-tree of the form "(parent child)"
+ */
+ public static AST createParent(ASTFactory factory, int parentType, String parentText, AST child) {
+ ASTArray array = createAstArray( factory, 2, parentType, parentText, child );
+ return factory.make( array );
+ }
+
+ public static AST createTree(ASTFactory factory, AST[] nestedChildren) {
+ AST[] array = new AST[2];
+ int limit = nestedChildren.length - 1;
+ for ( int i = limit; i >= 0; i-- ) {
+ if ( i != limit ) {
+ array[1] = nestedChildren[i + 1];
+ array[0] = nestedChildren[i];
+ factory.make( array );
+ }
+ }
+ return array[0];
+ }
+
+ /**
+ * Finds the first node of the specified type in the chain of children.
+ *
+ * @param parent The parent
+ * @param type The type to find.
+ * @return The first node of the specified type, or null if not found.
+ */
+ public static AST findTypeInChildren(AST parent, int type) {
+ AST n = parent.getFirstChild();
+ while ( n != null && n.getType() != type ) {
+ n = n.getNextSibling();
+ }
+ return n;
+ }
+
+ /**
+ * Returns the last direct child of 'n'.
+ *
+ * @param n The parent
+ * @return The last direct child of 'n'.
+ */
+ public static AST getLastChild(AST n) {
+ return getLastSibling( n.getFirstChild() );
+ }
+
+ /**
+ * Returns the last sibling of 'a'.
+ *
+ * @param a The sibling.
+ * @return The last sibling of 'a'.
+ */
+ private static AST getLastSibling(AST a) {
+ AST last = null;
+ while ( a != null ) {
+ last = a;
+ a = a.getNextSibling();
+ }
+ return last;
+ }
+
+ /**
+ * Returns the 'list' representation with some brackets around it for debugging.
+ *
+ * @param n The tree.
+ * @return The list representation of the tree.
+ */
+ public static String getDebugString(AST n) {
+ StringBuffer buf = new StringBuffer();
+ buf.append( "[ " );
+ buf.append( ( n == null ) ? "{null}" : n.toStringTree() );
+ buf.append( " ]" );
+ return buf.toString();
+ }
+
+ /**
+ * Find the previous sibling in the parent for the given child.
+ *
+ * @param parent the parent node
+ * @param child the child to find the previous sibling of
+ * @return the previous sibling of the child
+ */
+ public static AST findPreviousSibling(AST parent, AST child) {
+ AST prev = null;
+ AST n = parent.getFirstChild();
+ while ( n != null ) {
+ if ( n == child ) {
+ return prev;
+ }
+ prev = n;
+ n = n.getNextSibling();
+ }
+ throw new IllegalArgumentException( "Child not found in parent!" );
+ }
+
+ /**
+ * Determine if a given node (test) is a direct (throtle to one level down)
+ * child of another given node (fixture).
+ *
+ * @param fixture The node against which to testto be checked for children.
+ * @param test The node to be tested as being a child of the parent.
+ * @return True if test is contained in the fixtures's direct children;
+ * false otherwise.
+ */
+ public static boolean isDirectChild(AST fixture, AST test) {
+ AST n = fixture.getFirstChild();
+ while ( n != null ) {
+ if ( n == test ) {
+ return true;
+ }
+ n = n.getNextSibling();
+ }
+ return false;
+ }
+
+ /**
+ * Determine if a given node (test) is contained anywhere in the subtree
+ * of another given node (fixture).
+ *
+ * @param fixture The node against which to testto be checked for children.
+ * @param test The node to be tested as being a subtree child of the parent.
+ * @return True if child is contained in the parent's collection of children.
+ */
+ public static boolean isSubtreeChild(AST fixture, AST test) {
+ AST n = fixture.getFirstChild();
+ while ( n != null ) {
+ if ( n == test ) {
+ return true;
+ }
+ if ( n.getFirstChild() != null && isSubtreeChild( n, test ) ) {
+ return true;
+ }
+ n = n.getNextSibling();
+ }
+ return false;
+ }
+
+ /**
+ * Makes the child node a sibling of the parent, reconnecting all siblings.
+ *
+ * @param parent the parent
+ * @param child the child
+ */
+ public static void makeSiblingOfParent(AST parent, AST child) {
+ AST prev = findPreviousSibling( parent, child );
+ if ( prev != null ) {
+ prev.setNextSibling( child.getNextSibling() );
+ }
+ else { // child == parent.getFirstChild()
+ parent.setFirstChild( child.getNextSibling() );
+ }
+ child.setNextSibling( parent.getNextSibling() );
+ parent.setNextSibling( child );
+ }
+
+ public static String getPathText(AST n) {
+ StringBuffer buf = new StringBuffer();
+ getPathText( buf, n );
+ return buf.toString();
+ }
+
+ private static void getPathText(StringBuffer buf, AST n) {
+ AST firstChild = n.getFirstChild();
+ // If the node has a first child, recurse into the first child.
+ if ( firstChild != null ) {
+ getPathText( buf, firstChild );
+ }
+ // Append the text of the current node.
+ buf.append( n.getText() );
+ // If there is a second child (RHS), recurse into that child.
+ if ( firstChild != null && firstChild.getNextSibling() != null ) {
+ getPathText( buf, firstChild.getNextSibling() );
+ }
+ }
+
+ public static boolean hasExactlyOneChild(AST n) {
+ return n != null && n.getFirstChild() != null && n.getFirstChild().getNextSibling() == null;
+ }
+
+ public static void appendSibling(AST n, AST s) {
+ while ( n.getNextSibling() != null ) {
+ n = n.getNextSibling();
+ }
+ n.setNextSibling( s );
+ }
+
+ /**
+ * Inserts the child as the first child of the parent, all other children are shifted over to the 'right'.
+ *
+ * @param parent the parent
+ * @param child the new first child
+ */
+ public static void insertChild(AST parent, AST child) {
+ if ( parent.getFirstChild() == null ) {
+ parent.setFirstChild( child );
+ }
+ else {
+ AST n = parent.getFirstChild();
+ parent.setFirstChild( child );
+ child.setNextSibling( n );
+ }
+ }
+
+ /**
+ * Filters nodes out of a tree.
+ */
+ public static interface FilterPredicate {
+ /**
+ * Returns true if the node should be filtered out.
+ *
+ * @param n The node.
+ * @return true if the node should be filtered out, false to keep the node.
+ */
+ boolean exclude(AST n);
+ }
+
+ /**
+ * A predicate that uses inclusion, rather than exclusion semantics.
+ */
+ public abstract static class IncludePredicate implements FilterPredicate {
+ public final boolean exclude(AST node) {
+ return !include( node );
+ }
+
+ public abstract boolean include(AST node);
+ }
+
+ private static ASTArray createAstArray(ASTFactory factory, int size, int parentType, String parentText, AST child1) {
+ ASTArray array = new ASTArray( size );
+ array.add( factory.create( parentType, parentText ) );
+ array.add( child1 );
+ return array;
+ }
+
+ public static List collectChildren(AST root, FilterPredicate predicate) {
+// List children = new ArrayList();
+// collectChildren( children, root, predicate );
+// return children;
+ return new CollectingNodeVisitor( predicate ).collect( root );
+ }
+
+ private static class CollectingNodeVisitor implements NodeTraverser.VisitationStrategy {
+ private final FilterPredicate predicate;
+ private final List collectedNodes = new ArrayList();
+
+ public CollectingNodeVisitor(FilterPredicate predicate) {
+ this.predicate = predicate;
+ }
+
+ public void visit(AST node) {
+ if ( predicate == null || !predicate.exclude( node ) ) {
+ collectedNodes.add( node );
+ }
+ }
+
+ public List getCollectedNodes() {
+ return collectedNodes;
+ }
+
+ public List collect(AST root) {
+ NodeTraverser traverser = new NodeTraverser( this );
+ traverser.traverseDepthFirst( root );
+ return collectedNodes;
+ }
+ }
+
+ private static void collectChildren(List children, AST root, FilterPredicate predicate) {
+ for ( AST n = root.getFirstChild(); n != null; n = n.getNextSibling() ) {
+ if ( predicate == null || !predicate.exclude( n ) ) {
+ children.add( n );
+ }
+ collectChildren( children, n, predicate );
+ }
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/AliasGenerator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/AliasGenerator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/AliasGenerator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+// $Id: AliasGenerator.java 7460 2005-07-12 20:27:29Z steveebersole $
+package org.hibernate.hql.ast.util;
+
+import org.hibernate.util.StringHelper;
+
+/**
+ * Generates class/table/column aliases during semantic analysis and SQL rendering.
+ * <p/>
+ * Its essential purpose is to keep an internal counter to ensure that the
+ * generated aliases are unique.
+ */
+public class AliasGenerator {
+ private int next = 0;
+
+ private int nextCount() {
+ return next++;
+ }
+
+ public String createName(String name) {
+ return StringHelper.generateAlias( name, nextCount() );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/ColumnHelper.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/ColumnHelper.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/ColumnHelper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,49 @@
+// $Id: ColumnHelper.java 7460 2005-07-12 20:27:29Z steveebersole $
+package org.hibernate.hql.ast.util;
+
+import org.hibernate.hql.NameGenerator;
+import org.hibernate.hql.antlr.SqlTokenTypes;
+import org.hibernate.hql.ast.tree.HqlSqlWalkerNode;
+
+import antlr.ASTFactory;
+import antlr.collections.AST;
+
+/**
+ * Provides utility methods for dealing with arrays of SQL column names.
+ *
+ * @author josh Jan 3, 2005 9:08:47 AM
+ */
+public final class ColumnHelper {
+
+ /**
+ * @deprecated (tell clover to filter this out)
+ */
+ private ColumnHelper() {
+ }
+
+ public static void generateSingleScalarColumn(HqlSqlWalkerNode node, int i) {
+ ASTFactory factory = node.getASTFactory();
+ ASTUtil.createSibling( factory, SqlTokenTypes.SELECT_COLUMNS, " as " + NameGenerator.scalarName( i, 0 ), node );
+ }
+
+ /**
+ * Generates the scalar column AST nodes for a given array of SQL columns
+ */
+ public static void generateScalarColumns(HqlSqlWalkerNode node, String sqlColumns[], int i) {
+ if ( sqlColumns.length == 1 ) {
+ generateSingleScalarColumn( node, i );
+ }
+ else {
+ ASTFactory factory = node.getASTFactory();
+ AST n = node;
+ n.setText( sqlColumns[0] ); // Use the DOT node to emit the first column name.
+ // Create the column names, folled by the column aliases.
+ for ( int j = 0; j < sqlColumns.length; j++ ) {
+ if ( j > 0 ) {
+ n = ASTUtil.createSibling( factory, SqlTokenTypes.SQL_TOKEN, sqlColumns[j], n );
+ }
+ n = ASTUtil.createSibling( factory, SqlTokenTypes.SELECT_COLUMNS, " as " + NameGenerator.scalarName( i, j ), n );
+ }
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/JoinProcessor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/JoinProcessor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/JoinProcessor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,164 @@
+// $Id: JoinProcessor.java 10824 2006-11-16 19:32:48Z steve.ebersole at jboss.com $
+package org.hibernate.hql.ast.util;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.ListIterator;
+import java.util.Collections;
+import java.util.List;
+
+import org.hibernate.AssertionFailure;
+import org.hibernate.engine.JoinSequence;
+import org.hibernate.hql.antlr.SqlTokenTypes;
+import org.hibernate.hql.ast.QueryTranslatorImpl;
+import org.hibernate.hql.ast.tree.FromClause;
+import org.hibernate.hql.ast.tree.FromElement;
+import org.hibernate.hql.ast.tree.QueryNode;
+import org.hibernate.hql.ast.tree.DotNode;
+import org.hibernate.sql.JoinFragment;
+import org.hibernate.util.StringHelper;
+
+import antlr.ASTFactory;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Performs the post-processing of the join information gathered during semantic analysis.
+ * The join generating classes are complex, this encapsulates some of the JoinSequence-related
+ * code.
+ *
+ * @author Joshua Davis
+ */
+public class JoinProcessor implements SqlTokenTypes {
+
+ private static final Log log = LogFactory.getLog( JoinProcessor.class );
+
+ private QueryTranslatorImpl queryTranslatorImpl;
+ private SyntheticAndFactory andFactory;
+
+ /**
+ * Constructs a new JoinProcessor.
+ *
+ * @param astFactory The factory for AST node creation.
+ * @param queryTranslatorImpl The query translator.
+ */
+ public JoinProcessor(ASTFactory astFactory, QueryTranslatorImpl queryTranslatorImpl) {
+ this.andFactory = new SyntheticAndFactory( astFactory );
+ this.queryTranslatorImpl = queryTranslatorImpl;
+ }
+
+ /**
+ * Translates an AST join type (i.e., the token type) into a JoinFragment.XXX join type.
+ *
+ * @param astJoinType The AST join type (from HqlSqlTokenTypes or SqlTokenTypes)
+ * @return a JoinFragment.XXX join type.
+ * @see JoinFragment
+ * @see SqlTokenTypes
+ */
+ public static int toHibernateJoinType(int astJoinType) {
+ switch ( astJoinType ) {
+ case LEFT_OUTER:
+ return JoinFragment.LEFT_OUTER_JOIN;
+ case INNER:
+ return JoinFragment.INNER_JOIN;
+ case RIGHT_OUTER:
+ return JoinFragment.RIGHT_OUTER_JOIN;
+ default:
+ throw new AssertionFailure( "undefined join type " + astJoinType );
+ }
+ }
+
+ public void processJoins(QueryNode query, boolean inSubquery) {
+ final FromClause fromClause = query.getFromClause();
+
+ final List fromElements;
+ if ( DotNode.useThetaStyleImplicitJoins ) {
+ // for regression testing against output from the old parser...
+ // found it easiest to simply reorder the FromElements here into ascending order
+ // in terms of injecting them into the resulting sql ast in orders relative to those
+ // expected by the old parser; this is definitely another of those "only needed
+ // for regression purposes". The SyntheticAndFactory, then, simply injects them as it
+ // encounters them.
+ fromElements = new ArrayList();
+ ListIterator liter = fromClause.getFromElements().listIterator( fromClause.getFromElements().size() );
+ while ( liter.hasPrevious() ) {
+ fromElements.add( liter.previous() );
+ }
+ }
+ else {
+ fromElements = fromClause.getFromElements();
+ }
+
+ // Iterate through the alias,JoinSequence pairs and generate SQL token nodes.
+ Iterator iter = fromElements.iterator();
+ while ( iter.hasNext() ) {
+ final FromElement fromElement = ( FromElement ) iter.next();
+ JoinSequence join = fromElement.getJoinSequence();
+ join.setSelector(
+ new JoinSequence.Selector() {
+ public boolean includeSubclasses(String alias) {
+ // The uber-rule here is that we need to include subclass joins if
+ // the FromElement is in any way dereferenced by a property from
+ // the subclass table; otherwise we end up with column references
+ // qualified by a non-existent table reference in the resulting SQL...
+ boolean containsTableAlias = fromClause.containsTableAlias( alias );
+ if ( fromElement.isDereferencedBySubclassProperty() ) {
+ // TODO : or should we return 'containsTableAlias'??
+ log.trace( "forcing inclusion of extra joins [alias=" + alias + ", containsTableAlias=" + containsTableAlias + "]" );
+ return true;
+ }
+ boolean shallowQuery = queryTranslatorImpl.isShallowQuery();
+ boolean includeSubclasses = fromElement.isIncludeSubclasses();
+ boolean subQuery = fromClause.isSubQuery();
+ return includeSubclasses && containsTableAlias && !subQuery && !shallowQuery;
+ }
+ }
+ );
+ addJoinNodes( query, join, fromElement, inSubquery );
+ }
+
+ }
+
+ private void addJoinNodes(QueryNode query, JoinSequence join, FromElement fromElement, boolean inSubquery) {
+ // Generate FROM and WHERE fragments for the from element.
+ JoinFragment joinFragment = join.toJoinFragment(
+ inSubquery ? Collections.EMPTY_MAP : queryTranslatorImpl.getEnabledFilters(),
+ fromElement.useFromFragment() || fromElement.isDereferencedBySuperclassOrSubclassProperty(),
+ fromElement.getWithClauseFragment(),
+ fromElement.getWithClauseJoinAlias()
+ );
+
+ String frag = joinFragment.toFromFragmentString();
+ String whereFrag = joinFragment.toWhereFragmentString();
+
+ // If the from element represents a JOIN_FRAGMENT and it is
+ // a theta-style join, convert its type from JOIN_FRAGMENT
+ // to FROM_FRAGMENT
+ if ( fromElement.getType() == JOIN_FRAGMENT &&
+ ( join.isThetaStyle() || StringHelper.isNotEmpty( whereFrag ) ) ) {
+ fromElement.setType( FROM_FRAGMENT );
+ fromElement.getJoinSequence().setUseThetaStyle( true ); // this is used during SqlGenerator processing
+ }
+
+ // If there is a FROM fragment and the FROM element is an explicit, then add the from part.
+ if ( fromElement.useFromFragment() /*&& StringHelper.isNotEmpty( frag )*/ ) {
+ String fromFragment = processFromFragment( frag, join );
+ if ( log.isDebugEnabled() ) {
+ log.debug( "Using FROM fragment [" + fromFragment + "]" );
+ }
+ fromElement.setText( fromFragment.trim() ); // Set the text of the fromElement.
+ }
+ andFactory.addWhereFragment( joinFragment, whereFrag, query, fromElement );
+ }
+
+ private String processFromFragment(String frag, JoinSequence join) {
+ String fromFragment = frag.trim();
+ // The FROM fragment will probably begin with ', '. Remove this if it is present.
+ if ( fromFragment.startsWith( ", " ) ) {
+ fromFragment = fromFragment.substring( 2 );
+ }
+ return fromFragment;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/LiteralProcessor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/LiteralProcessor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/LiteralProcessor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,297 @@
+// $Id: LiteralProcessor.java 11257 2007-03-07 22:39:25Z steve.ebersole at jboss.com $
+package org.hibernate.hql.ast.util;
+
+import org.hibernate.MappingException;
+import org.hibernate.QueryException;
+import org.hibernate.HibernateException;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.hql.QueryTranslator;
+import org.hibernate.hql.antlr.HqlSqlTokenTypes;
+import org.hibernate.hql.antlr.SqlTokenTypes;
+import org.hibernate.hql.ast.HqlSqlWalker;
+import org.hibernate.hql.ast.InvalidPathException;
+import org.hibernate.hql.ast.tree.DotNode;
+import org.hibernate.hql.ast.tree.FromClause;
+import org.hibernate.hql.ast.tree.IdentNode;
+import org.hibernate.persister.entity.Queryable;
+import org.hibernate.sql.InFragment;
+import org.hibernate.type.LiteralType;
+import org.hibernate.type.Type;
+import org.hibernate.type.TypeFactory;
+import org.hibernate.util.ReflectHelper;
+
+import antlr.SemanticException;
+import antlr.collections.AST;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.math.BigDecimal;
+import java.text.DecimalFormat;
+
+/**
+ * A delegate that handles literals and constants for HqlSqlWalker, performing the token replacement functions and
+ * classifying literals.
+ *
+ * @author josh Sep 2, 2004 7:15:30 AM
+ */
+public class LiteralProcessor implements HqlSqlTokenTypes {
+ /**
+ * Indicates that Float and Double literal values should
+ * be treated using the SQL "exact" format (i.e., '.001')
+ */
+ public static final int EXACT = 0;
+ /**
+ * Indicates that Float and Double literal values should
+ * be treated using the SQL "approximate" format (i.e., '1E-3')
+ */
+ public static final int APPROXIMATE = 1;
+ /**
+ * In what format should Float and Double literal values be sent
+ * to the database?
+ * @see #EXACT, #APPROXIMATE
+ */
+ public static int DECIMAL_LITERAL_FORMAT = EXACT;
+
+ private static final Log log = LogFactory.getLog( LiteralProcessor.class );
+
+ private HqlSqlWalker walker;
+
+ public LiteralProcessor(HqlSqlWalker hqlSqlWalker) {
+ this.walker = hqlSqlWalker;
+ }
+
+ public boolean isAlias(String alias) {
+ FromClause from = walker.getCurrentFromClause();
+ while ( from.isSubQuery() ) {
+ if ( from.containsClassAlias(alias) ) {
+ return true;
+ }
+ from = from.getParentFromClause();
+ }
+ return from.containsClassAlias(alias);
+ }
+
+ public void processConstant(AST constant, boolean resolveIdent) throws SemanticException {
+ // If the constant is an IDENT, figure out what it means...
+ boolean isIdent = ( constant.getType() == IDENT || constant.getType() == WEIRD_IDENT );
+ if ( resolveIdent && isIdent && isAlias( constant.getText() ) ) { // IDENT is a class alias in the FROM.
+ IdentNode ident = ( IdentNode ) constant;
+ // Resolve to an identity column.
+ ident.resolve(false, true);
+ }
+ else { // IDENT might be the name of a class.
+ Queryable queryable = walker.getSessionFactoryHelper().findQueryableUsingImports( constant.getText() );
+ if ( isIdent && queryable != null ) {
+ constant.setText( queryable.getDiscriminatorSQLValue() );
+ }
+ // Otherwise, it's a literal.
+ else {
+ processLiteral( constant );
+ }
+ }
+ }
+
+ public void lookupConstant(DotNode node) throws SemanticException {
+ 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 + "'" );
+ }
+ else {
+ setSQLValue( node, text, discrim ); //the class discriminator value
+ }
+ }
+ else {
+ Object value = ReflectHelper.getConstantValue( text );
+ if ( value == null ) {
+ throw new InvalidPathException( "Invalid path: '" + text + "'" );
+ }
+ else {
+ setConstantValue( node, text, value );
+ }
+ }
+ }
+
+ private void setSQLValue(DotNode node, String text, String value) {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "setSQLValue() " + text + " -> " + value );
+ }
+ node.setFirstChild( null ); // Chop off the rest of the tree.
+ node.setType( SqlTokenTypes.SQL_TOKEN );
+ node.setText(value);
+ node.setResolvedConstant( text );
+ }
+
+ private void setConstantValue(DotNode node, String text, Object value) {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "setConstantValue() " + text + " -> " + value + " " + value.getClass().getName() );
+ }
+ node.setFirstChild( null ); // Chop off the rest of the tree.
+ if ( value instanceof String ) {
+ node.setType( SqlTokenTypes.QUOTED_STRING );
+ }
+ else if ( value instanceof Character ) {
+ node.setType( SqlTokenTypes.QUOTED_STRING );
+ }
+ else if ( value instanceof Byte ) {
+ node.setType( SqlTokenTypes.NUM_INT );
+ }
+ else if ( value instanceof Short ) {
+ node.setType( SqlTokenTypes.NUM_INT );
+ }
+ else if ( value instanceof Integer ) {
+ node.setType( SqlTokenTypes.NUM_INT );
+ }
+ else if ( value instanceof Long ) {
+ node.setType( SqlTokenTypes.NUM_LONG );
+ }
+ else if ( value instanceof Double ) {
+ node.setType( SqlTokenTypes.NUM_DOUBLE );
+ }
+ else if ( value instanceof Float ) {
+ node.setType( SqlTokenTypes.NUM_FLOAT );
+ }
+ else {
+ node.setType( SqlTokenTypes.CONSTANT );
+ }
+ Type type;
+ try {
+ type = TypeFactory.heuristicType( value.getClass().getName() );
+ }
+ catch ( MappingException me ) {
+ throw new QueryException( me );
+ }
+ if ( type == null ) {
+ throw new QueryException( QueryTranslator.ERROR_CANNOT_DETERMINE_TYPE + node.getText() );
+ }
+ try {
+ LiteralType literalType = ( LiteralType ) type;
+ Dialect dialect = walker.getSessionFactoryHelper().getFactory().getDialect();
+ node.setText( literalType.objectToSQLString( value, dialect ) );
+ }
+ catch ( Exception e ) {
+ throw new QueryException( QueryTranslator.ERROR_CANNOT_FORMAT_LITERAL + node.getText(), e );
+ }
+ node.setDataType( type );
+ node.setResolvedConstant( text );
+ }
+
+ 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.
+ String replacement = ( String ) walker.getTokenReplacements().get( constant.getText() );
+ if ( replacement != null ) {
+ constant.setText( replacement );
+ }
+ else {
+ boolean bool = "true".equals( constant.getText().toLowerCase() );
+ Dialect dialect = walker.getSessionFactoryHelper().getFactory().getDialect();
+ constant.setText( dialect.toBooleanValueString(bool) );
+ }
+ }
+
+ private void processLiteral(AST constant) {
+ String replacement = ( String ) walker.getTokenReplacements().get( constant.getText() );
+ if ( replacement != null ) {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "processConstant() : Replacing '" + constant.getText() + "' with '" + replacement + "'" );
+ }
+ constant.setText( replacement );
+ }
+ }
+
+ public void processNumeric(AST literal) {
+ if ( literal.getType() == NUM_INT || literal.getType() == NUM_LONG ) {
+ literal.setText( determineIntegerRepresentation( literal.getText(), literal.getType() ) );
+ }
+ else if ( literal.getType() == NUM_FLOAT || literal.getType() == NUM_DOUBLE ) {
+ literal.setText( determineDecimalRepresentation( literal.getText(), literal.getType() ) );
+ }
+ else {
+ log.warn( "Unexpected literal token type [" + literal.getType() + "] passed for numeric processing" );
+ }
+ }
+
+ private String determineIntegerRepresentation(String text, int type) {
+ try {
+ if ( type == NUM_INT ) {
+ try {
+ return Integer.valueOf( text ).toString();
+ }
+ catch( NumberFormatException e ) {
+ log.trace( "could not format incoming text [" + text + "] as a NUM_INT; assuming numeric overflow and attempting as NUM_LONG" );
+ }
+ }
+ String literalValue = text;
+ if ( literalValue.endsWith( "l" ) || literalValue.endsWith( "L" ) ) {
+ literalValue = literalValue.substring( 0, literalValue.length() - 1 );
+ }
+ return Long.valueOf( literalValue ).toString();
+ }
+ catch( Throwable t ) {
+ throw new HibernateException( "Could not parse literal [" + text + "] as integer", t );
+ }
+ }
+
+ public String determineDecimalRepresentation(String text, int type) {
+ String literalValue = text;
+ if ( type == NUM_FLOAT ) {
+ if ( literalValue.endsWith( "f" ) || literalValue.endsWith( "F" ) ) {
+ literalValue = literalValue.substring( 0, literalValue.length() - 1 );
+ }
+ }
+ else if ( type == NUM_DOUBLE ) {
+ if ( literalValue.endsWith( "d" ) || literalValue.endsWith( "D" ) ) {
+ literalValue = literalValue.substring( 0, literalValue.length() - 1 );
+ }
+ }
+
+ BigDecimal number = null;
+ try {
+ number = new BigDecimal( literalValue );
+ }
+ catch( Throwable t ) {
+ throw new HibernateException( "Could not parse literal [" + text + "] as big-decimal", t );
+ }
+
+ return formatters[ DECIMAL_LITERAL_FORMAT ].format( number );
+ }
+
+ private static interface DecimalFormatter {
+ String format(BigDecimal number);
+ }
+
+ private static class ExactDecimalFormatter implements DecimalFormatter {
+ public String format(BigDecimal number) {
+ return number.toString();
+ }
+ }
+
+ private static class ApproximateDecimalFormatter implements DecimalFormatter {
+ private static final String FORMAT_STRING = "#0.0E0";
+ public String format(BigDecimal number) {
+ try {
+ // TODO : what amount of significant digits need to be supported here?
+ // - from the DecimalFormat docs:
+ // [significant digits] = [minimum integer digits] + [maximum fraction digits]
+ DecimalFormat jdkFormatter = new DecimalFormat( FORMAT_STRING );
+ jdkFormatter.setMinimumIntegerDigits( 1 );
+ jdkFormatter.setMaximumFractionDigits( Integer.MAX_VALUE );
+ return jdkFormatter.format( number );
+ }
+ catch( Throwable t ) {
+ throw new HibernateException( "Unable to format decimal literal in approximate format [" + number.toString() + "]", t );
+ }
+ }
+ }
+
+ private static final DecimalFormatter[] formatters = new DecimalFormatter[] {
+ new ExactDecimalFormatter(),
+ new ApproximateDecimalFormatter()
+ };
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/NodeTraverser.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/NodeTraverser.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/NodeTraverser.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,44 @@
+package org.hibernate.hql.ast.util;
+
+import antlr.collections.AST;
+
+/**
+ * A visitor for traversing an AST tree.
+ *
+ * @author Steve Ebersole
+ */
+public class NodeTraverser {
+ public static interface VisitationStrategy {
+ public void visit(AST node);
+ }
+
+ private final VisitationStrategy strategy;
+
+ public NodeTraverser(VisitationStrategy strategy) {
+ this.strategy = strategy;
+ }
+
+ /**
+ * Traverse the AST tree depth first.
+ * <p/>
+ * Note that the AST passed in is not visited itself. Visitation starts
+ * with its children.
+ *
+ * @param ast
+ */
+ public void traverseDepthFirst(AST ast) {
+ if ( ast == null ) {
+ throw new IllegalArgumentException( "node to traverse cannot be null!" );
+ }
+ visitDepthFirst( ast.getFirstChild() );
+ }
+
+ private void visitDepthFirst(AST ast) {
+ if ( ast == null ) {
+ return;
+ }
+ strategy.visit( ast );
+ visitDepthFirst( ast.getFirstChild() );
+ visitDepthFirst( ast.getNextSibling() );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/PathHelper.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/PathHelper.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/PathHelper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,54 @@
+// $Id: PathHelper.java 7460 2005-07-12 20:27:29Z steveebersole $
+package org.hibernate.hql.ast.util;
+
+import org.hibernate.hql.antlr.HqlSqlTokenTypes;
+import org.hibernate.util.StringHelper;
+
+import antlr.ASTFactory;
+import antlr.collections.AST;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Provides utility methods for paths.
+ *
+ * @author josh Sep 14, 2004 8:16:29 AM
+ */
+public final class PathHelper {
+
+ private static final Log log = LogFactory.getLog( PathHelper.class );
+
+ private PathHelper() {
+ }
+
+ /**
+ * Turns a path into an AST.
+ *
+ * @param path The path.
+ * @param factory The AST factory to use.
+ * @return An HQL AST representing the path.
+ */
+ public static AST parsePath(String path, ASTFactory factory) {
+ String[] identifiers = StringHelper.split( ".", path );
+ AST lhs = null;
+ for ( int i = 0; i < identifiers.length; i++ ) {
+ String identifier = identifiers[i];
+ AST child = ASTUtil.create( factory, HqlSqlTokenTypes.IDENT, identifier );
+ if ( i == 0 ) {
+ lhs = child;
+ }
+ else {
+ lhs = ASTUtil.createBinarySubtree( factory, HqlSqlTokenTypes.DOT, ".", lhs, child );
+ }
+ }
+ if ( log.isDebugEnabled() ) {
+ log.debug( "parsePath() : " + path + " -> " + ASTUtil.getDebugString( lhs ) );
+ }
+ return lhs;
+ }
+
+ public static String getAlias(String path) {
+ return StringHelper.root( path );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/SessionFactoryHelper.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/SessionFactoryHelper.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/SessionFactoryHelper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,392 @@
+// $Id: SessionFactoryHelper.java 10824 2006-11-16 19:32:48Z steve.ebersole at jboss.com $
+package org.hibernate.hql.ast.util;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.hibernate.MappingException;
+import org.hibernate.QueryException;
+import org.hibernate.dialect.function.SQLFunction;
+import org.hibernate.engine.JoinSequence;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.hql.NameGenerator;
+import org.hibernate.hql.ast.DetailedSemanticException;
+import org.hibernate.hql.ast.QuerySyntaxException;
+import org.hibernate.hql.ast.tree.SqlNode;
+import org.hibernate.persister.collection.CollectionPropertyMapping;
+import org.hibernate.persister.collection.CollectionPropertyNames;
+import org.hibernate.persister.collection.QueryableCollection;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.persister.entity.PropertyMapping;
+import org.hibernate.persister.entity.Queryable;
+import org.hibernate.type.AssociationType;
+import org.hibernate.type.CollectionType;
+import org.hibernate.type.EntityType;
+import org.hibernate.type.Type;
+import org.hibernate.type.TypeFactory;
+
+import antlr.SemanticException;
+import antlr.collections.AST;
+
+/**
+ * Helper for performing common and/or complex operations with the
+ * {@link SessionFactoryImplementor} during translation of an HQL query.
+ *
+ * @author Joshua Davis
+ */
+public class SessionFactoryHelper {
+
+ private SessionFactoryImplementor sfi;
+ private Map collectionPropertyMappingByRole;
+
+ /**
+ * Construct a new SessionFactoryHelper instance.
+ *
+ * @param sfi The SessionFactory impl to be encapsualted.
+ */
+ public SessionFactoryHelper(SessionFactoryImplementor sfi) {
+ this.sfi = sfi;
+ collectionPropertyMappingByRole = new HashMap();
+ }
+
+ /**
+ * Get a handle to the encapsulated SessionFactory.
+ *
+ * @return The encapsulated SessionFactory.
+ */
+ public SessionFactoryImplementor getFactory() {
+ return sfi;
+ }
+
+ /**
+ * Does the given persister define a physical discriminator column
+ * for the purpose of inheritence discrimination?
+ *
+ * @param persister The persister to be checked.
+ * @return True if the persister does define an actual discriminator column.
+ */
+ public boolean hasPhysicalDiscriminatorColumn(Queryable persister) {
+ if ( persister.getDiscriminatorType() != null ) {
+ String discrimColumnName = persister.getDiscriminatorColumnName();
+ // Needed the "clazz_" check to work around union-subclasses
+ // TODO : is there a way to tell whether a persister is truly discrim-column based inheritence?
+ if ( discrimColumnName != null && !"clazz_".equals( discrimColumnName ) ) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Given a (potentially unqualified) class name, locate its imported qualified name.
+ *
+ * @param className The potentially unqualified class name
+ * @return The qualified class name.
+ */
+ public String getImportedClassName(String className) {
+ return sfi.getImportedClassName( className );
+ }
+
+ /**
+ * Given a (potentially unqualified) class name, locate its persister.
+ *
+ * @param className The (potentially unqualified) class name.
+ * @return The defined persister for this class, or null if none found.
+ */
+ public Queryable findQueryableUsingImports(String className) {
+ return findQueryableUsingImports( sfi, className );
+ }
+
+
+ /**
+ * Given a (potentially unqualified) class name, locate its persister.
+ *
+ * @param sfi The session factory implementor.
+ * @param className The (potentially unqualified) class name.
+ * @return The defined persister for this class, or null if none found.
+ */
+ public static Queryable findQueryableUsingImports(SessionFactoryImplementor sfi, String className) {
+ final String importedClassName = sfi.getImportedClassName( className );
+ if ( importedClassName == null ) {
+ return null;
+ }
+ try {
+ return ( Queryable ) sfi.getEntityPersister( importedClassName );
+ }
+ catch ( MappingException me ) {
+ return null;
+ }
+ }
+
+ /**
+ * Locate the persister by class or entity name.
+ *
+ * @param name The class or entity name
+ * @return The defined persister for this entity, or null if none found.
+ * @throws MappingException
+ */
+ private EntityPersister findEntityPersisterByName(String name) throws MappingException {
+ // First, try to get the persister using the given name directly.
+ try {
+ return sfi.getEntityPersister( name );
+ }
+ catch ( MappingException ignore ) {
+ // unable to locate it using this name
+ }
+
+ // If that didn't work, try using the 'import' name.
+ String importedClassName = sfi.getImportedClassName( name );
+ if ( importedClassName == null ) {
+ return null;
+ }
+ return sfi.getEntityPersister( importedClassName );
+ }
+
+ /**
+ * Locate the persister by class or entity name, requiring that such a persister
+ * exist.
+ *
+ * @param name The class or entity name
+ * @return The defined persister for this entity
+ * @throws SemanticException Indicates the persister could not be found
+ */
+ public EntityPersister requireClassPersister(String name) throws SemanticException {
+ EntityPersister cp;
+ try {
+ cp = findEntityPersisterByName( name );
+ if ( cp == null ) {
+ throw new QuerySyntaxException( name + " is not mapped" );
+ }
+ }
+ catch ( MappingException e ) {
+ throw new DetailedSemanticException( e.getMessage(), e );
+ }
+ return cp;
+ }
+
+ /**
+ * Locate the collection persister by the collection role.
+ *
+ * @param role The collection role name.
+ * @return The defined CollectionPersister for this collection role, or null.
+ */
+ public QueryableCollection getCollectionPersister(String role) {
+ try {
+ return ( QueryableCollection ) sfi.getCollectionPersister( role );
+ }
+ catch ( ClassCastException cce ) {
+ throw new QueryException( "collection is not queryable: " + role );
+ }
+ catch ( Exception e ) {
+ throw new QueryException( "collection not found: " + role );
+ }
+ }
+
+ /**
+ * Locate the collection persister by the collection role, requiring that
+ * such a persister exist.
+ *
+ * @param role The collection role name.
+ * @return The defined CollectionPersister for this collection role.
+ * @throws QueryException Indicates that the collection persister could not be found.
+ */
+ public QueryableCollection requireQueryableCollection(String role) throws QueryException {
+ try {
+ QueryableCollection queryableCollection = ( QueryableCollection ) sfi.getCollectionPersister( role );
+ if ( queryableCollection != null ) {
+ collectionPropertyMappingByRole.put( role, new CollectionPropertyMapping( queryableCollection ) );
+ }
+ return queryableCollection;
+ }
+ catch ( ClassCastException cce ) {
+ throw new QueryException( "collection role is not queryable: " + role );
+ }
+ catch ( Exception e ) {
+ throw new QueryException( "collection role not found: " + role );
+ }
+ }
+
+ /**
+ * Retreive a PropertyMapping describing the given collection role.
+ *
+ * @param role The collection role for whcih to retrieve the property mapping.
+ * @return The property mapping.
+ */
+ private PropertyMapping getCollectionPropertyMapping(String role) {
+ return ( PropertyMapping ) collectionPropertyMappingByRole.get( role );
+ }
+
+ /**
+ * Retrieves the column names corresponding to the collection elements for the given
+ * collection role.
+ *
+ * @param role The collection role
+ * @param roleAlias The sql column-qualification alias (i.e., the table alias)
+ * @return the collection element columns
+ */
+ public String[] getCollectionElementColumns(String role, String roleAlias) {
+ return getCollectionPropertyMapping( role ).toColumns( roleAlias, CollectionPropertyNames.COLLECTION_ELEMENTS );
+ }
+
+ /**
+ * Generate an empty join sequence instance.
+ *
+ * @return The generate join sequence.
+ */
+ public JoinSequence createJoinSequence() {
+ return new JoinSequence( sfi );
+ }
+
+ /**
+ * Generate a join sequence representing the given association type.
+ *
+ * @param implicit Should implicit joins (theta-style) or explicit joins (ANSI-style) be rendered
+ * @param associationType The type representing the thing to be joined into.
+ * @param tableAlias The table alias to use in qualifing the join conditions
+ * @param joinType The type of join to render (inner, outer, etc); see {@link org.hibernate.sql.JoinFragment}
+ * @param columns The columns making up the condition of the join.
+ * @return The generated join sequence.
+ */
+ public JoinSequence createJoinSequence(boolean implicit, AssociationType associationType, String tableAlias, int joinType, String[] columns) {
+ JoinSequence joinSequence = createJoinSequence();
+ joinSequence.setUseThetaStyle( implicit ); // Implicit joins use theta style (WHERE pk = fk), explicit joins use JOIN (after from)
+ joinSequence.addJoin( associationType, tableAlias, joinType, columns );
+ return joinSequence;
+ }
+
+ /**
+ * Create a join sequence rooted at the given collection.
+ *
+ * @param collPersister The persister for the collection at which the join should be rooted.
+ * @param collectionName The alias to use for qualifying column references.
+ * @return The generated join sequence.
+ */
+ public JoinSequence createCollectionJoinSequence(QueryableCollection collPersister, String collectionName) {
+ JoinSequence joinSequence = createJoinSequence();
+ joinSequence.setRoot( collPersister, collectionName );
+ joinSequence.setUseThetaStyle( true ); // TODO: figure out how this should be set.
+///////////////////////////////////////////////////////////////////////////////
+// This was the reason for failures regarding INDEX_OP and subclass joins on
+// theta-join dialects; not sure what behaviour we were trying to emulate ;)
+// joinSequence = joinSequence.getFromPart(); // Emulate the old addFromOnly behavior.
+ return joinSequence;
+ }
+
+ /**
+ * Determine the name of the property for the entity encapsulated by the
+ * given type which represents the id or unique-key.
+ *
+ * @param entityType The type representing the entity.
+ * @return The corresponding property name
+ * @throws QueryException Indicates such a property could not be found.
+ */
+ public String getIdentifierOrUniqueKeyPropertyName(EntityType entityType) {
+ try {
+ return entityType.getIdentifierOrUniqueKeyPropertyName( sfi );
+ }
+ catch ( MappingException me ) {
+ throw new QueryException( me );
+ }
+ }
+
+ /**
+ * Retreive the number of columns represented by this type.
+ *
+ * @param type The type.
+ * @return The number of columns.
+ */
+ public int getColumnSpan(Type type) {
+ return type.getColumnSpan( sfi );
+ }
+
+ /**
+ * Given a collection type, determine the entity name of the elements
+ * contained within instance of that collection.
+ *
+ * @param collectionType The collection type to check.
+ * @return The entity name of the elements of this collection.
+ */
+ public String getAssociatedEntityName(CollectionType collectionType) {
+ return collectionType.getAssociatedEntityName( sfi );
+ }
+
+ /**
+ * Given a collection type, determine the Type representing elements
+ * within instances of that collection.
+ *
+ * @param collectionType The collection type to be checked.
+ * @return The Type of the elements of the collection.
+ */
+ private Type getElementType(CollectionType collectionType) {
+ return collectionType.getElementType( sfi );
+ }
+
+ /**
+ * Essentially the same as {@link #getElementType}, but requiring that the
+ * element type be an association type.
+ *
+ * @param collectionType The collection type to be checked.
+ * @return The AssociationType of the elements of the collection.
+ */
+ public AssociationType getElementAssociationType(CollectionType collectionType) {
+ return ( AssociationType ) getElementType( collectionType );
+ }
+
+ /**
+ * Locate a registered sql function by name.
+ *
+ * @param functionName The name of the function to locate
+ * @return The sql function, or null if not found.
+ */
+ public SQLFunction findSQLFunction(String functionName) {
+ return sfi.getSqlFunctionRegistry().findSQLFunction( functionName.toLowerCase() );
+ }
+
+ /**
+ * Locate a registered sql function by name, requiring that such a registered function exist.
+ *
+ * @param functionName The name of the function to locate
+ * @return The sql function.
+ * @throws QueryException Indicates no matching sql functions could be found.
+ */
+ private SQLFunction requireSQLFunction(String functionName) {
+ SQLFunction f = findSQLFunction( functionName );
+ if ( f == null ) {
+ throw new QueryException( "Unable to find SQL function: " + functionName );
+ }
+ return f;
+ }
+
+ /**
+ * Find the function return type given the function name and the first argument expression node.
+ *
+ * @param functionName The function name.
+ * @param first The first argument expression.
+ * @return the function return type given the function name and the first argument expression node.
+ */
+ public Type findFunctionReturnType(String functionName, AST first) {
+ // locate the registered function by the given name
+ SQLFunction sqlFunction = requireSQLFunction( functionName );
+
+ // determine the type of the first argument...
+ Type argumentType = null;
+ if ( first != null ) {
+ if ( "cast".equals(functionName) ) {
+ argumentType = TypeFactory.heuristicType( first.getNextSibling().getText() );
+ }
+ else if ( first instanceof SqlNode ) {
+ argumentType = ( (SqlNode) first ).getDataType();
+ }
+ }
+
+ return sqlFunction.getReturnType( argumentType, sfi );
+ }
+
+ public String[][] generateColumnNames(Type[] sqlResultTypes) {
+ return NameGenerator.generateColumnNames( sqlResultTypes, sfi );
+ }
+
+ public boolean isStrictJPAQLComplianceEnabled() {
+ return sfi.getSettings().isStrictJPAQLCompliance();
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/SyntheticAndFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/SyntheticAndFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/ast/util/SyntheticAndFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,134 @@
+// $Id: SyntheticAndFactory.java 8755 2005-12-06 00:18:35Z steveebersole $
+package org.hibernate.hql.ast.util;
+
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import org.hibernate.hql.antlr.HqlSqlTokenTypes;
+import org.hibernate.hql.ast.tree.FromElement;
+import org.hibernate.hql.ast.tree.QueryNode;
+import org.hibernate.hql.ast.tree.RestrictableStatement;
+import org.hibernate.hql.ast.tree.SqlFragment;
+import org.hibernate.persister.entity.Queryable;
+import org.hibernate.sql.JoinFragment;
+import org.hibernate.util.StringHelper;
+
+import antlr.ASTFactory;
+import antlr.collections.AST;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Creates synthetic and nodes based on the where fragment part of a JoinSequence.
+ *
+ * @author josh Dec 5, 2004 12:25:20 PM
+ */
+public class SyntheticAndFactory implements HqlSqlTokenTypes {
+ private static final Log log = LogFactory.getLog( SyntheticAndFactory.class );
+
+ private ASTFactory astFactory;
+ private AST thetaJoins;
+ private AST filters;
+
+ public SyntheticAndFactory(ASTFactory astFactory) {
+ this.astFactory = astFactory;
+ }
+
+ public void addWhereFragment(JoinFragment joinFragment, String whereFragment, QueryNode query, FromElement fromElement) {
+
+ if ( whereFragment == null ) {
+ return;
+ }
+
+ whereFragment = whereFragment.trim();
+ if ( StringHelper.isEmpty( whereFragment ) ) {
+ return;
+ }
+ else if ( !fromElement.useWhereFragment() && !joinFragment.hasThetaJoins() ) {
+ return;
+ }
+
+ // Forcefully remove leading ands from where fragments; the grammar will
+ // handle adding them
+ if ( whereFragment.startsWith( "and" ) ) {
+ whereFragment = whereFragment.substring( 4 );
+ }
+
+ if ( log.isDebugEnabled() ) log.debug( "Using WHERE fragment [" + whereFragment + "]" );
+
+ SqlFragment fragment = ( SqlFragment ) ASTUtil.create( astFactory, SQL_TOKEN, whereFragment );
+ fragment.setJoinFragment( joinFragment );
+ fragment.setFromElement( fromElement );
+
+ // Filter conditions need to be inserted before the HQL where condition and the
+ // theta join node. This is because org.hibernate.loader.Loader binds the filter parameters first,
+ // then it binds all the HQL query parameters, see org.hibernate.loader.Loader.processFilterParameters().
+ if ( fragment.getFromElement().isFilter() || fragment.hasFilterCondition() ) {
+ if ( filters == null ) {
+ // Find or create the WHERE clause
+ AST where = query.getWhereClause();
+ // Create a new FILTERS node as a parent of all filters
+ filters = astFactory.create( FILTERS, "{filter conditions}" );
+ // Put the FILTERS node before the HQL condition and theta joins
+ ASTUtil.insertChild( where, filters );
+ }
+
+ // add the current fragment to the FILTERS node
+ filters.addChild( fragment );
+ }
+ else {
+ if ( thetaJoins == null ) {
+ // Find or create the WHERE clause
+ AST where = query.getWhereClause();
+ // Create a new THETA_JOINS node as a parent of all filters
+ thetaJoins = astFactory.create( THETA_JOINS, "{theta joins}" );
+ // Put the THETA_JOINS node before the HQL condition, after the filters.
+ if (filters==null) {
+ ASTUtil.insertChild( where, thetaJoins );
+ }
+ else {
+ ASTUtil.insertSibling( thetaJoins, filters );
+ }
+ }
+
+ // add the current fragment to the THETA_JOINS node
+ thetaJoins.addChild(fragment);
+ }
+
+ }
+
+ public void addDiscriminatorWhereFragment(RestrictableStatement statement, Queryable persister, Map enabledFilters, String alias) {
+ String whereFragment = persister.filterFragment( alias, enabledFilters ).trim();
+ if ( "".equals( whereFragment ) ) {
+ return;
+ }
+ if ( whereFragment.startsWith( "and" ) ) {
+ whereFragment = whereFragment.substring( 4 );
+ }
+
+ // Need to parse off the column qualifiers; this is assuming (which is true as of now)
+ // that this is only used from update and delete HQL statement parsing
+ whereFragment = StringHelper.replace( whereFragment, persister.generateFilterConditionAlias( alias ) + ".", "" );
+
+ // Note: this simply constructs a "raw" SQL_TOKEN representing the
+ // where fragment and injects this into the tree. This "works";
+ // however it is probably not the best long-term solution.
+ //
+ // At some point we probably want to apply an additional grammar to
+ // properly tokenize this where fragment into constituent parts
+ // focused on the operators embedded within the fragment.
+ AST discrimNode = astFactory.create( SQL_TOKEN, whereFragment );
+
+ if ( statement.getWhereClause().getNumberOfChildren() == 0 ) {
+ statement.getWhereClause().setFirstChild( discrimNode );
+ }
+ else {
+ AST and = astFactory.create( AND, "{and}" );
+ AST currentFirstChild = statement.getWhereClause().getFirstChild();
+ and.setFirstChild( discrimNode );
+ and.addChild( currentFirstChild );
+ statement.getWhereClause().setFirstChild( and );
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/ClassicQueryTranslatorFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/ClassicQueryTranslatorFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/ClassicQueryTranslatorFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,41 @@
+//$Id: ClassicQueryTranslatorFactory.java 9162 2006-01-27 23:40:32Z steveebersole $
+package org.hibernate.hql.classic;
+
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.hql.FilterTranslator;
+import org.hibernate.hql.QueryTranslator;
+import org.hibernate.hql.QueryTranslatorFactory;
+
+import java.util.Map;
+
+/**
+ * Generates translators which uses the older hand-written parser to perform
+ * the translation.
+ *
+ * @author Gavin King
+ */
+public class ClassicQueryTranslatorFactory implements QueryTranslatorFactory {
+
+ /**
+ * @see QueryTranslatorFactory#createQueryTranslator
+ */
+ public QueryTranslator createQueryTranslator(
+ String queryIdentifier,
+ String queryString,
+ Map filters,
+ SessionFactoryImplementor factory) {
+ return new QueryTranslatorImpl( queryIdentifier, queryString, filters, factory );
+ }
+
+ /**
+ * @see QueryTranslatorFactory#createFilterTranslator
+ */
+ public FilterTranslator createFilterTranslator(
+ String queryIdentifier,
+ String queryString,
+ Map filters,
+ SessionFactoryImplementor factory) {
+ return new QueryTranslatorImpl( queryIdentifier, queryString, filters, factory );
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/ClassicQueryTranslatorFactory.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/ClauseParser.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/ClauseParser.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/ClauseParser.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,129 @@
+//$Id: ClauseParser.java 4907 2004-12-08 00:24:14Z oneovthafew $
+package org.hibernate.hql.classic;
+
+import org.hibernate.QueryException;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Parses the Hibernate query into its constituent clauses.
+ */
+public class ClauseParser implements Parser {
+
+ private Parser child;
+ private List selectTokens;
+ private boolean cacheSelectTokens = false;
+ private boolean byExpected = false;
+ private int parenCount = 0;
+
+ public void token(String token, QueryTranslatorImpl q) throws QueryException {
+ String lcToken = token.toLowerCase();
+
+ if ( "(".equals( token ) ) {
+ parenCount++;
+ }
+ else if ( ")".equals( token ) ) {
+ parenCount--;
+ }
+
+ if ( byExpected && !lcToken.equals( "by" ) ) {
+ throw new QueryException( "BY expected after GROUP or ORDER: " + token );
+ }
+
+ boolean isClauseStart = parenCount == 0; //ignore subselect keywords
+
+ if ( isClauseStart ) {
+ if ( lcToken.equals( "select" ) ) {
+ selectTokens = new ArrayList();
+ cacheSelectTokens = true;
+ }
+ else if ( lcToken.equals( "from" ) ) {
+ child = new FromParser();
+ child.start( q );
+ cacheSelectTokens = false;
+ }
+ else if ( lcToken.equals( "where" ) ) {
+ endChild( q );
+ child = new WhereParser();
+ child.start( q );
+ }
+ else if ( lcToken.equals( "order" ) ) {
+ endChild( q );
+ child = new OrderByParser();
+ byExpected = true;
+ }
+ else if ( lcToken.equals( "having" ) ) {
+ endChild( q );
+ child = new HavingParser();
+ child.start( q );
+ }
+ else if ( lcToken.equals( "group" ) ) {
+ endChild( q );
+ child = new GroupByParser();
+ byExpected = true;
+ }
+ else if ( lcToken.equals( "by" ) ) {
+ if ( !byExpected ) throw new QueryException( "GROUP or ORDER expected before BY" );
+ child.start( q );
+ byExpected = false;
+ }
+ else {
+ isClauseStart = false;
+ }
+ }
+
+ if ( !isClauseStart ) {
+ if ( cacheSelectTokens ) {
+ selectTokens.add( token );
+ }
+ else {
+ if ( child == null ) {
+ throw new QueryException( "query must begin with SELECT or FROM: " + token );
+ }
+ else {
+ child.token( token, q );
+ }
+ }
+ }
+
+ }
+
+ private void endChild(QueryTranslatorImpl q) throws QueryException {
+ if ( child == null ) {
+ //null child could occur for no from clause in a filter
+ cacheSelectTokens = false;
+ }
+ else {
+ child.end( q );
+ }
+ }
+
+ public void start(QueryTranslatorImpl q) {
+ }
+
+ public void end(QueryTranslatorImpl q) throws QueryException {
+ endChild( q );
+ if ( selectTokens != null ) {
+ child = new SelectParser();
+ child.start( q );
+ Iterator iter = selectTokens.iterator();
+ while ( iter.hasNext() ) {
+ token( ( String ) iter.next(), q );
+ }
+ child.end( q );
+ }
+ byExpected = false;
+ parenCount = 0;
+ cacheSelectTokens = false;
+ }
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/FromParser.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/FromParser.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/FromParser.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,231 @@
+//$Id: FromParser.java 5699 2005-02-13 11:50:11Z oneovthafew $
+package org.hibernate.hql.classic;
+
+import org.hibernate.QueryException;
+import org.hibernate.hql.QueryTranslator;
+import org.hibernate.persister.entity.Queryable;
+import org.hibernate.sql.JoinFragment;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Parses the from clause of a hibernate query, looking for tables and
+ * aliases for the SQL query.
+ */
+
+public class FromParser implements Parser {
+
+ private final PathExpressionParser peParser = new FromPathExpressionParser();
+ private String entityName;
+ private String alias;
+ private boolean afterIn;
+ private boolean afterAs;
+ private boolean afterClass;
+ private boolean expectingJoin;
+ private boolean expectingIn;
+ private boolean expectingAs;
+ private boolean afterJoinType;
+ private int joinType;
+ private boolean afterFetch;
+
+ private static final int NONE = -666;
+
+ private static final Map JOIN_TYPES = new HashMap();
+
+ static {
+ JOIN_TYPES.put( "left", new Integer( JoinFragment.LEFT_OUTER_JOIN ) );
+ JOIN_TYPES.put( "right", new Integer( JoinFragment.RIGHT_OUTER_JOIN ) );
+ JOIN_TYPES.put( "full", new Integer( JoinFragment.FULL_JOIN ) );
+ JOIN_TYPES.put( "inner", new Integer( JoinFragment.INNER_JOIN ) );
+ }
+
+ public void token(String token, QueryTranslatorImpl q) throws QueryException {
+
+ // start by looking for HQL keywords...
+ String lcToken = token.toLowerCase();
+ if ( lcToken.equals( "," ) ) {
+ if ( !( expectingJoin | expectingAs ) ) throw new QueryException( "unexpected token: ," );
+ expectingJoin = false;
+ expectingAs = false;
+ }
+ else if ( lcToken.equals( "join" ) ) {
+ if ( !afterJoinType ) {
+ if ( !( expectingJoin | expectingAs ) ) throw new QueryException( "unexpected token: join" );
+ // inner joins can be abbreviated to 'join'
+ joinType = JoinFragment.INNER_JOIN;
+ expectingJoin = false;
+ expectingAs = false;
+ }
+ else {
+ afterJoinType = false;
+ }
+ }
+ else if ( lcToken.equals( "fetch" ) ) {
+ if ( q.isShallowQuery() ) throw new QueryException( QueryTranslator.ERROR_CANNOT_FETCH_WITH_ITERATE );
+ if ( joinType == NONE ) throw new QueryException( "unexpected token: fetch" );
+ if ( joinType == JoinFragment.FULL_JOIN || joinType == JoinFragment.RIGHT_OUTER_JOIN ) {
+ throw new QueryException( "fetch may only be used with inner join or left outer join" );
+ }
+ afterFetch = true;
+ }
+ else if ( lcToken.equals( "outer" ) ) {
+ // 'outer' is optional and is ignored
+ if ( !afterJoinType ||
+ ( joinType != JoinFragment.LEFT_OUTER_JOIN && joinType != JoinFragment.RIGHT_OUTER_JOIN )
+ ) {
+ throw new QueryException( "unexpected token: outer" );
+ }
+ }
+ else if ( JOIN_TYPES.containsKey( lcToken ) ) {
+ if ( !( expectingJoin | expectingAs ) ) throw new QueryException( "unexpected token: " + token );
+ joinType = ( ( Integer ) JOIN_TYPES.get( lcToken ) ).intValue();
+ afterJoinType = true;
+ expectingJoin = false;
+ expectingAs = false;
+ }
+ else if ( lcToken.equals( "class" ) ) {
+ if ( !afterIn ) throw new QueryException( "unexpected token: class" );
+ if ( joinType != NONE ) throw new QueryException( "outer or full join must be followed by path expression" );
+ afterClass = true;
+ }
+ else if ( lcToken.equals( "in" ) ) {
+ if ( !expectingIn ) throw new QueryException( "unexpected token: in" );
+ afterIn = true;
+ expectingIn = false;
+ }
+ else if ( lcToken.equals( "as" ) ) {
+ if ( !expectingAs ) throw new QueryException( "unexpected token: as" );
+ afterAs = true;
+ expectingAs = false;
+ }
+ else {
+
+ if ( afterJoinType ) throw new QueryException( "join expected: " + token );
+ if ( expectingJoin ) throw new QueryException( "unexpected token: " + token );
+ if ( expectingIn ) throw new QueryException( "in expected: " + token );
+
+ // now anything that is not a HQL keyword
+
+ if ( afterAs || expectingAs ) {
+
+ // (AS is always optional, for consistency with SQL/OQL)
+
+ // process the "new" HQL style where aliases are assigned
+ // _after_ the class name or path expression ie. using
+ // the AS construction
+
+ if ( entityName != null ) {
+ q.setAliasName( token, entityName );
+ }
+ else {
+ throw new QueryException( "unexpected: as " + token );
+ }
+ afterAs = false;
+ expectingJoin = true;
+ expectingAs = false;
+ entityName = null;
+
+ }
+ else if ( afterIn ) {
+
+ // process the "old" HQL style where aliases appear _first_
+ // ie. using the IN or IN CLASS constructions
+
+ if ( alias == null ) throw new QueryException( "alias not specified for: " + token );
+
+ if ( joinType != NONE ) throw new QueryException( "outer or full join must be followed by path expression" );
+
+ if ( afterClass ) {
+ // treat it as a classname
+ Queryable p = q.getEntityPersisterUsingImports( token );
+ if ( p == null ) throw new QueryException( "persister not found: " + token );
+ q.addFromClass( alias, p );
+ }
+ else {
+ // treat it as a path expression
+ peParser.setJoinType( JoinFragment.INNER_JOIN );
+ peParser.setUseThetaStyleJoin( true );
+ ParserHelper.parse( peParser, q.unalias( token ), ParserHelper.PATH_SEPARATORS, q );
+ if ( !peParser.isCollectionValued() ) throw new QueryException( "path expression did not resolve to collection: " + token );
+ String nm = peParser.addFromCollection( q );
+ q.setAliasName( alias, nm );
+ }
+
+ alias = null;
+ afterIn = false;
+ afterClass = false;
+ expectingJoin = true;
+ }
+ else {
+
+ // handle a path expression or class name that
+ // appears at the start, in the "new" HQL
+ // style or an alias that appears at the start
+ // in the "old" HQL style
+
+ Queryable p = q.getEntityPersisterUsingImports( token );
+ if ( p != null ) {
+ // starts with the name of a mapped class (new style)
+ if ( joinType != NONE ) throw new QueryException( "outer or full join must be followed by path expression" );
+ entityName = q.createNameFor( p.getEntityName() );
+ q.addFromClass( entityName, p );
+ expectingAs = true;
+ }
+ else if ( token.indexOf( '.' ) < 0 ) {
+ // starts with an alias (old style)
+ // semi-bad thing about this: can't re-alias another alias.....
+ alias = token;
+ expectingIn = true;
+ }
+ else {
+
+ // starts with a path expression (new style)
+
+ // force HQL style: from Person p inner join p.cars c
+ //if (joinType==NONE) throw new QueryException("path expression must be preceded by full, left, right or inner join");
+
+ //allow ODMG OQL style: from Person p, p.cars c
+ if ( joinType != NONE ) {
+ peParser.setJoinType( joinType );
+ }
+ else {
+ peParser.setJoinType( JoinFragment.INNER_JOIN );
+ }
+ peParser.setUseThetaStyleJoin( q.isSubquery() );
+
+ ParserHelper.parse( peParser, q.unalias( token ), ParserHelper.PATH_SEPARATORS, q );
+ entityName = peParser.addFromAssociation( q );
+
+ joinType = NONE;
+ peParser.setJoinType( JoinFragment.INNER_JOIN );
+
+ if ( afterFetch ) {
+ peParser.fetch( q, entityName );
+ afterFetch = false;
+ }
+
+ expectingAs = true;
+
+ }
+ }
+ }
+
+ }
+
+ public void start(QueryTranslatorImpl q) {
+ entityName = null;
+ alias = null;
+ afterIn = false;
+ afterAs = false;
+ afterClass = false;
+ expectingJoin = false;
+ expectingIn = false;
+ expectingAs = false;
+ joinType = NONE;
+ }
+
+ public void end(QueryTranslatorImpl q) {
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/FromPathExpressionParser.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/FromPathExpressionParser.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/FromPathExpressionParser.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,32 @@
+//$Id: FromPathExpressionParser.java 5861 2005-02-22 14:07:36Z oneovthafew $
+package org.hibernate.hql.classic;
+
+import org.hibernate.QueryException;
+import org.hibernate.persister.collection.CollectionPropertyNames;
+import org.hibernate.type.Type;
+
+public class FromPathExpressionParser extends PathExpressionParser {
+
+ public void end(QueryTranslatorImpl q) throws QueryException {
+ if ( !isCollectionValued() ) {
+ Type type = getPropertyType();
+ if ( type.isEntityType() ) {
+ // "finish off" the join
+ token( ".", q );
+ token( null, q );
+ }
+ else if ( type.isCollectionType() ) {
+ // default to element set if no elements() specified
+ token( ".", q );
+ token( CollectionPropertyNames.COLLECTION_ELEMENTS, q );
+ }
+ }
+ super.end( q );
+ }
+
+ protected void setExpectingCollectionIndex() throws QueryException {
+ throw new QueryException( "illegal syntax near collection-valued path expression in from: " + getCollectionName() );
+ }
+
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/GroupByParser.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/GroupByParser.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/GroupByParser.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,53 @@
+//$Id: GroupByParser.java 4907 2004-12-08 00:24:14Z oneovthafew $
+package org.hibernate.hql.classic;
+
+import org.hibernate.QueryException;
+import org.hibernate.util.StringHelper;
+
+/**
+ * Parses the GROUP BY clause of an aggregate query
+ */
+public class GroupByParser implements Parser {
+
+ //this is basically a copy/paste of OrderByParser ... might be worth refactoring
+
+ // This uses a PathExpressionParser but notice that compound paths are not valid,
+ // only bare names and simple paths:
+
+ // SELECT p FROM p IN CLASS eg.Person GROUP BY p.Name, p.Address, p
+
+ // The reason for this is SQL doesn't let you sort by an expression you are
+ // not returning in the result set.
+
+ private final PathExpressionParser pathExpressionParser;
+
+ {
+ pathExpressionParser = new PathExpressionParser();
+ pathExpressionParser.setUseThetaStyleJoin( true ); //TODO: would be nice to use false, but issues with MS SQL
+ }
+
+ public void token(String token, QueryTranslatorImpl q) throws QueryException {
+
+ if ( q.isName( StringHelper.root( token ) ) ) {
+ ParserHelper.parse( pathExpressionParser, q.unalias( token ), ParserHelper.PATH_SEPARATORS, q );
+ q.appendGroupByToken( pathExpressionParser.getWhereColumn() );
+ pathExpressionParser.addAssociation( q );
+ }
+ else {
+ q.appendGroupByToken( token );
+ }
+ }
+
+ public void start(QueryTranslatorImpl q) throws QueryException {
+ }
+
+ public void end(QueryTranslatorImpl q) throws QueryException {
+ }
+
+
+}
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/HavingParser.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/HavingParser.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/HavingParser.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,15 @@
+//$Id: HavingParser.java 4907 2004-12-08 00:24:14Z oneovthafew $
+package org.hibernate.hql.classic;
+
+
+/**
+ * Parses the having clause of a hibernate query and translates it to an
+ * SQL having clause.
+ */
+public class HavingParser extends WhereParser {
+
+ void appendToken(QueryTranslatorImpl q, String token) {
+ q.appendHavingToken( token );
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/OrderByParser.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/OrderByParser.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/OrderByParser.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,50 @@
+//$Id: OrderByParser.java 4907 2004-12-08 00:24:14Z oneovthafew $
+package org.hibernate.hql.classic;
+
+import org.hibernate.QueryException;
+import org.hibernate.util.StringHelper;
+
+/**
+ * Parses the ORDER BY clause of a query
+ */
+
+public class OrderByParser implements Parser {
+
+ // This uses a PathExpressionParser but notice that compound paths are not valid,
+ // only bare names and simple paths:
+
+ // SELECT p FROM p IN CLASS eg.Person ORDER BY p.Name, p.Address, p
+
+ // The reason for this is SQL doesn't let you sort by an expression you are
+ // not returning in the result set.
+
+ private final PathExpressionParser pathExpressionParser;
+
+ {
+ pathExpressionParser = new PathExpressionParser();
+ pathExpressionParser.setUseThetaStyleJoin( true ); //TODO: would be nice to use false, but issues with MS SQL
+ }
+
+ public void token(String token, QueryTranslatorImpl q) throws QueryException {
+
+ if ( q.isName( StringHelper.root( token ) ) ) {
+ ParserHelper.parse( pathExpressionParser, q.unalias( token ), ParserHelper.PATH_SEPARATORS, q );
+ q.appendOrderByToken( pathExpressionParser.getWhereColumn() );
+ pathExpressionParser.addAssociation( q );
+ }
+ else if ( token.startsWith( ParserHelper.HQL_VARIABLE_PREFIX ) ) { //named query parameter
+ q.addNamedParameter( token.substring( 1 ) );
+ q.appendOrderByToken( "?" );
+ }
+ else {
+ q.appendOrderByToken( token );
+ }
+ }
+
+ public void start(QueryTranslatorImpl q) throws QueryException {
+ }
+
+ public void end(QueryTranslatorImpl q) throws QueryException {
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/Parser.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/Parser.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/Parser.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,26 @@
+//$Id: Parser.java 4907 2004-12-08 00:24:14Z oneovthafew $
+package org.hibernate.hql.classic;
+
+import org.hibernate.QueryException;
+
+/**
+ * A parser is a state machine that accepts a string of tokens,
+ * bounded by start() and end() and modifies a QueryTranslator. Parsers
+ * are NOT intended to be threadsafe. They SHOULD be reuseable
+ * for more than one token stream.
+ */
+
+public interface Parser {
+ public void token(String token, QueryTranslatorImpl q) throws QueryException;
+
+ public void start(QueryTranslatorImpl q) throws QueryException;
+
+ public void end(QueryTranslatorImpl q) throws QueryException;
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/ParserHelper.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/ParserHelper.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/ParserHelper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,38 @@
+//$Id: ParserHelper.java 6879 2005-05-23 19:54:13Z oneovthafew $
+package org.hibernate.hql.classic;
+
+import org.hibernate.QueryException;
+import org.hibernate.util.StringHelper;
+
+import java.util.StringTokenizer;
+
+public final class ParserHelper {
+
+ public static final String HQL_VARIABLE_PREFIX = ":";
+
+ public static final String HQL_SEPARATORS = " \n\r\f\t,()=<>&|+-=/*'^![]#~\\";
+ //NOTICE: no " or . since they are part of (compound) identifiers
+ public static final String PATH_SEPARATORS = ".";
+
+ public static boolean isWhitespace(String str) {
+ return StringHelper.WHITESPACE.indexOf( str ) > -1;
+ }
+
+ private ParserHelper() {
+ //cannot instantiate
+ }
+
+ public static void parse(Parser p, String text, String seperators, QueryTranslatorImpl q) throws QueryException {
+ StringTokenizer tokens = new StringTokenizer( text, seperators, true );
+ p.start( q );
+ while ( tokens.hasMoreElements() ) p.token( tokens.nextToken(), q );
+ p.end( q );
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/PathExpressionParser.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/PathExpressionParser.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/PathExpressionParser.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,504 @@
+//$Id: PathExpressionParser.java 10830 2006-11-16 20:22:07Z steve.ebersole at jboss.com $
+package org.hibernate.hql.classic;
+
+import java.util.LinkedList;
+import java.util.Map;
+
+import org.hibernate.MappingException;
+import org.hibernate.QueryException;
+import org.hibernate.engine.JoinSequence;
+import org.hibernate.hql.CollectionSubqueryFactory;
+import org.hibernate.persister.collection.CollectionPropertyMapping;
+import org.hibernate.persister.collection.QueryableCollection;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.persister.entity.PropertyMapping;
+import org.hibernate.persister.entity.Queryable;
+import org.hibernate.sql.JoinFragment;
+import org.hibernate.type.AssociationType;
+import org.hibernate.type.CollectionType;
+import org.hibernate.type.EntityType;
+import org.hibernate.type.Type;
+import org.hibernate.type.TypeFactory;
+
+/**
+ * Parses an expression of the form foo.bar.baz and builds up an expression
+ * involving two less table joins than there are path components.
+ */
+public class PathExpressionParser implements Parser {
+
+ //TODO: this class does too many things! we need a different
+ //kind of path expression parser for each of the diffferent
+ //ways in which path expressions can occur
+
+ //We should actually rework this class to not implement Parser
+ //and just process path expressions in the most convenient way.
+
+ //The class is now way to complex!
+
+ private int dotcount;
+ private String currentName;
+ private String currentProperty;
+ private String oneToOneOwnerName;
+ private AssociationType ownerAssociationType;
+ private String[] columns;
+ private String collectionName;
+ private String collectionOwnerName;
+ private String collectionRole;
+ private final StringBuffer componentPath = new StringBuffer();
+ private Type type;
+ private final StringBuffer path = new StringBuffer();
+ private boolean ignoreInitialJoin;
+ private boolean continuation;
+ private int joinType = JoinFragment.INNER_JOIN; //default mode
+ private boolean useThetaStyleJoin = true;
+ private PropertyMapping currentPropertyMapping;
+ private JoinSequence joinSequence;
+
+ private boolean expectingCollectionIndex;
+ private LinkedList collectionElements = new LinkedList();
+
+ void setJoinType(int joinType) {
+ this.joinType = joinType;
+ }
+
+ void setUseThetaStyleJoin(boolean useThetaStyleJoin) {
+ this.useThetaStyleJoin = useThetaStyleJoin;
+ }
+
+ private void addJoin(String name, AssociationType joinableType) throws QueryException {
+ try {
+ joinSequence.addJoin( joinableType, name, joinType, currentColumns() );
+ }
+ catch ( MappingException me ) {
+ throw new QueryException( me );
+ }
+ }
+
+ private void addJoin(String name, AssociationType joinableType, String[] foreignKeyColumns) throws QueryException {
+ try {
+ joinSequence.addJoin( joinableType, name, joinType, foreignKeyColumns );
+ }
+ catch ( MappingException me ) {
+ throw new QueryException( me );
+ }
+ }
+
+ String continueFromManyToMany(String entityName, String[] joinColumns, QueryTranslatorImpl q) throws QueryException {
+ start( q );
+ continuation = true;
+ currentName = q.createNameFor( entityName );
+ q.addType( currentName, entityName );
+ Queryable classPersister = q.getEntityPersister( entityName );
+ //QueryJoinFragment join = q.createJoinFragment(useThetaStyleJoin);
+ addJoin( currentName, TypeFactory.manyToOne( entityName ), joinColumns );
+ currentPropertyMapping = classPersister;
+ return currentName;
+ }
+
+ public void ignoreInitialJoin() {
+ ignoreInitialJoin = true;
+ }
+
+ public void token(String token, QueryTranslatorImpl q) throws QueryException {
+
+ if ( token != null ) path.append( token );
+
+ String alias = q.getPathAlias( path.toString() );
+ if ( alias != null ) {
+ reset( q ); //reset the dotcount (but not the path)
+ currentName = alias; //after reset!
+ currentPropertyMapping = q.getPropertyMapping( currentName );
+ if ( !ignoreInitialJoin ) {
+ JoinSequence ojf = q.getPathJoin( path.toString() );
+ try {
+ joinSequence.addCondition( ojf.toJoinFragment( q.getEnabledFilters(), true ).toWhereFragmentString() ); //after reset!
+ }
+ catch ( MappingException me ) {
+ throw new QueryException( me );
+ }
+ // we don't need to worry about any condition in the ON clause
+ // here (toFromFragmentString), since anything in the ON condition
+ // is already applied to the whole query
+ }
+ }
+ else if ( ".".equals( token ) ) {
+ dotcount++;
+ }
+ else {
+ if ( dotcount == 0 ) {
+ if ( !continuation ) {
+ if ( !q.isName( token ) ) throw new QueryException( "undefined alias: " + token );
+ currentName = token;
+ currentPropertyMapping = q.getPropertyMapping( currentName );
+ }
+ }
+ else if ( dotcount == 1 ) {
+ if ( currentName != null ) {
+ currentProperty = token;
+ }
+ else if ( collectionName != null ) {
+ //processCollectionProperty(token, q.getCollectionPersister(collectionRole), collectionName);
+ continuation = false;
+ }
+ else {
+ throw new QueryException( "unexpected" );
+ }
+ }
+ else { // dotcount>=2
+
+ // Do the corresponding RHS
+ Type propertyType = getPropertyType();
+
+ if ( propertyType == null ) {
+ throw new QueryException( "unresolved property: " + path );
+ }
+
+ if ( propertyType.isComponentType() ) {
+ dereferenceComponent( token );
+ }
+ else if ( propertyType.isEntityType() ) {
+ if ( !isCollectionValued() ) dereferenceEntity( token, ( EntityType ) propertyType, q );
+ }
+ else if ( propertyType.isCollectionType() ) {
+ dereferenceCollection( token, ( ( CollectionType ) propertyType ).getRole(), q );
+
+ }
+ else {
+ if ( token != null ) throw new QueryException( "dereferenced: " + path );
+ }
+
+ }
+ }
+ }
+
+ private void dereferenceEntity(String propertyName, EntityType propertyType, QueryTranslatorImpl q)
+ throws QueryException {
+ //NOTE: we avoid joining to the next table if the named property is just the foreign key value
+
+ //if its "id"
+ boolean isIdShortcut = EntityPersister.ENTITY_ID.equals( propertyName ) &&
+ propertyType.isReferenceToPrimaryKey();
+
+ //or its the id property name
+ final String idPropertyName;
+ try {
+ idPropertyName = propertyType.getIdentifierOrUniqueKeyPropertyName( q.getFactory() );
+ }
+ catch ( MappingException me ) {
+ throw new QueryException( me );
+ }
+ boolean isNamedIdPropertyShortcut = idPropertyName != null
+ && idPropertyName.equals( propertyName )
+ && propertyType.isReferenceToPrimaryKey();
+
+
+ if ( isIdShortcut || isNamedIdPropertyShortcut ) {
+ // special shortcut for id properties, skip the join!
+ // this must only occur at the _end_ of a path expression
+ if ( componentPath.length() > 0 ) componentPath.append( '.' );
+ componentPath.append( propertyName );
+ }
+ else {
+ String entityClass = propertyType.getAssociatedEntityName();
+ String name = q.createNameFor( entityClass );
+ q.addType( name, entityClass );
+ addJoin( name, propertyType );
+ if ( propertyType.isOneToOne() ) oneToOneOwnerName = currentName;
+ ownerAssociationType = propertyType;
+ currentName = name;
+ currentProperty = propertyName;
+ q.addPathAliasAndJoin( path.substring( 0, path.toString().lastIndexOf( '.' ) ), name, joinSequence.copy() );
+ componentPath.setLength( 0 );
+ currentPropertyMapping = q.getEntityPersister( entityClass );
+ }
+ }
+
+ private void dereferenceComponent(String propertyName) {
+ if ( propertyName != null ) {
+ if ( componentPath.length() > 0 ) componentPath.append( '.' );
+ componentPath.append( propertyName );
+ }
+ }
+
+ private void dereferenceCollection(String propertyName, String role, QueryTranslatorImpl q) throws QueryException {
+ collectionRole = role;
+ QueryableCollection collPersister = q.getCollectionPersister( role );
+ String name = q.createNameForCollection( role );
+ addJoin( name, collPersister.getCollectionType() );
+ //if ( collPersister.hasWhere() ) join.addCondition( collPersister.getSQLWhereString(name) );
+ collectionName = name;
+ collectionOwnerName = currentName;
+ currentName = name;
+ currentProperty = propertyName;
+ componentPath.setLength( 0 );
+ currentPropertyMapping = new CollectionPropertyMapping( collPersister );
+ }
+
+ private String getPropertyPath() {
+ if ( currentProperty == null ) {
+ return EntityPersister.ENTITY_ID;
+ }
+ else {
+ if ( componentPath.length() > 0 ) {
+ return new StringBuffer()
+ .append( currentProperty )
+ .append( '.' )
+ .append( componentPath.toString() )
+ .toString();
+ }
+ else {
+ return currentProperty;
+ }
+ }
+ }
+
+ private PropertyMapping getPropertyMapping() {
+ return currentPropertyMapping;
+ }
+
+ private void setType() throws QueryException {
+ if ( currentProperty == null ) {
+ type = getPropertyMapping().getType();
+ }
+ else {
+ type = getPropertyType();
+ }
+ }
+
+ protected Type getPropertyType() throws QueryException {
+ String propertyPath = getPropertyPath();
+ Type propertyType = getPropertyMapping().toType( propertyPath );
+ if ( propertyType == null ) {
+ throw new QueryException( "could not resolve property type: " + propertyPath );
+ }
+ return propertyType;
+ }
+
+ protected String[] currentColumns() throws QueryException {
+ String propertyPath = getPropertyPath();
+ String[] propertyColumns = getPropertyMapping().toColumns( currentName, propertyPath );
+ if ( propertyColumns == null ) {
+ throw new QueryException( "could not resolve property columns: " + propertyPath );
+ }
+ return propertyColumns;
+ }
+
+ private void reset(QueryTranslatorImpl q) {
+ //join = q.createJoinFragment(useThetaStyleJoin);
+ dotcount = 0;
+ currentName = null;
+ currentProperty = null;
+ collectionName = null;
+ collectionRole = null;
+ componentPath.setLength( 0 );
+ type = null;
+ collectionName = null;
+ columns = null;
+ expectingCollectionIndex = false;
+ continuation = false;
+ currentPropertyMapping = null;
+ }
+
+ public void start(QueryTranslatorImpl q) {
+ if ( !continuation ) {
+ reset( q );
+ path.setLength( 0 );
+ joinSequence = new JoinSequence( q.getFactory() ).setUseThetaStyle( useThetaStyleJoin );
+ }
+ }
+
+ public void end(QueryTranslatorImpl q) throws QueryException {
+ ignoreInitialJoin = false;
+
+ Type propertyType = getPropertyType();
+ if ( propertyType != null && propertyType.isCollectionType() ) {
+ collectionRole = ( ( CollectionType ) propertyType ).getRole();
+ collectionName = q.createNameForCollection( collectionRole );
+ prepareForIndex( q );
+ }
+ else {
+ columns = currentColumns();
+ setType();
+ }
+
+ //important!!
+ continuation = false;
+
+ }
+
+ private void prepareForIndex(QueryTranslatorImpl q) throws QueryException {
+
+ QueryableCollection collPersister = q.getCollectionPersister( collectionRole );
+
+ if ( !collPersister.hasIndex() ) throw new QueryException( "unindexed collection before []: " + path );
+ String[] indexCols = collPersister.getIndexColumnNames();
+ if ( indexCols.length != 1 ) throw new QueryException( "composite-index appears in []: " + path );
+ //String[] keyCols = collPersister.getKeyColumnNames();
+
+ JoinSequence fromJoins = new JoinSequence( q.getFactory() )
+ .setUseThetaStyle( useThetaStyleJoin )
+ .setRoot( collPersister, collectionName )
+ .setNext( joinSequence.copy() );
+
+ if ( !continuation ) addJoin( collectionName, collPersister.getCollectionType() );
+
+ joinSequence.addCondition( collectionName + '.' + indexCols[0] + " = " ); //TODO: get SQL rendering out of here
+
+ CollectionElement elem = new CollectionElement();
+ elem.elementColumns = collPersister.getElementColumnNames(collectionName);
+ elem.elementType = collPersister.getElementType();
+ elem.isOneToMany = collPersister.isOneToMany();
+ elem.alias = collectionName;
+ elem.joinSequence = joinSequence;
+ collectionElements.addLast( elem );
+ setExpectingCollectionIndex();
+
+ q.addCollection( collectionName, collectionRole );
+ q.addFromJoinOnly( collectionName, fromJoins );
+ }
+
+ static final class CollectionElement {
+ Type elementType;
+ boolean isOneToMany;
+ String alias;
+ String[] elementColumns;
+ JoinSequence joinSequence;
+ StringBuffer indexValue = new StringBuffer();
+ }
+
+ public CollectionElement lastCollectionElement() {
+ return ( CollectionElement ) collectionElements.removeLast();
+ }
+
+ public void setLastCollectionElementIndexValue(String value) {
+ ( ( CollectionElement ) collectionElements.getLast() ).indexValue.append( value );
+ }
+
+ public boolean isExpectingCollectionIndex() {
+ return expectingCollectionIndex;
+ }
+
+ protected void setExpectingCollectionIndex() throws QueryException {
+ expectingCollectionIndex = true;
+ }
+
+ public JoinSequence getWhereJoin() {
+ return joinSequence;
+ }
+
+ public String getWhereColumn() throws QueryException {
+ if ( columns.length != 1 ) {
+ throw new QueryException( "path expression ends in a composite value: " + path );
+ }
+ return columns[0];
+ }
+
+ public String[] getWhereColumns() {
+ return columns;
+ }
+
+ public Type getWhereColumnType() {
+ return type;
+ }
+
+ public String getName() {
+ return currentName == null ? collectionName : currentName;
+ }
+
+
+ public String getCollectionSubquery(Map enabledFilters) throws QueryException {
+ return CollectionSubqueryFactory.createCollectionSubquery( joinSequence, enabledFilters, currentColumns() );
+ }
+
+ public boolean isCollectionValued() throws QueryException {
+ //TODO: is there a better way?
+ return collectionName != null && !getPropertyType().isCollectionType();
+ }
+
+ public void addAssociation(QueryTranslatorImpl q) throws QueryException {
+ q.addJoin( getName(), joinSequence );
+ }
+
+ public String addFromAssociation(QueryTranslatorImpl q) throws QueryException {
+ if ( isCollectionValued() ) {
+ return addFromCollection( q );
+ }
+ else {
+ q.addFrom( currentName, joinSequence );
+ return currentName;
+ }
+ }
+
+ public String addFromCollection(QueryTranslatorImpl q) throws QueryException {
+ Type collectionElementType = getPropertyType();
+
+ if ( collectionElementType == null ) {
+ throw new QueryException( "must specify 'elements' for collection valued property in from clause: " + path );
+ }
+
+ if ( collectionElementType.isEntityType() ) {
+ // an association
+ QueryableCollection collectionPersister = q.getCollectionPersister( collectionRole );
+ Queryable entityPersister = ( Queryable ) collectionPersister.getElementPersister();
+ String clazz = entityPersister.getEntityName();
+
+ final String elementName;
+ if ( collectionPersister.isOneToMany() ) {
+ elementName = collectionName;
+ //allow index() function:
+ q.decoratePropertyMapping( elementName, collectionPersister );
+ }
+ else { //many-to-many
+ q.addCollection( collectionName, collectionRole );
+ elementName = q.createNameFor( clazz );
+ addJoin( elementName, ( AssociationType ) collectionElementType );
+ }
+ q.addFrom( elementName, clazz, joinSequence );
+ currentPropertyMapping = new CollectionPropertyMapping( collectionPersister );
+ return elementName;
+ }
+ else {
+ // collections of values
+ q.addFromCollection( collectionName, collectionRole, joinSequence );
+ return collectionName;
+ }
+
+ }
+
+ String getCollectionName() {
+ return collectionName;
+ }
+
+ String getCollectionRole() {
+ return collectionRole;
+ }
+
+ String getCollectionOwnerName() {
+ return collectionOwnerName;
+ }
+
+ String getOneToOneOwnerName() {
+ return oneToOneOwnerName;
+ }
+
+ AssociationType getOwnerAssociationType() {
+ return ownerAssociationType;
+ }
+
+ String getCurrentProperty() {
+ return currentProperty;
+ }
+
+ String getCurrentName() {
+ return currentName;
+ }
+
+ public void fetch(QueryTranslatorImpl q, String entityName) throws QueryException {
+ if ( isCollectionValued() ) {
+ q.setCollectionToFetch( getCollectionRole(), getCollectionName(), getCollectionOwnerName(), entityName );
+ }
+ else {
+ q.addEntityToFetch( entityName, getOneToOneOwnerName(), getOwnerAssociationType() );
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/PreprocessingParser.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/PreprocessingParser.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/PreprocessingParser.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,133 @@
+//$Id: PreprocessingParser.java 5063 2004-12-24 03:51:20Z pgmjsd $
+package org.hibernate.hql.classic;
+
+import org.hibernate.QueryException;
+import org.hibernate.hql.CollectionProperties;
+import org.hibernate.util.StringHelper;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ *
+ */
+public class PreprocessingParser implements Parser {
+
+ private static final Set HQL_OPERATORS;
+
+ static {
+ HQL_OPERATORS = new HashSet();
+ HQL_OPERATORS.add( "<=" );
+ HQL_OPERATORS.add( ">=" );
+ HQL_OPERATORS.add( "=>" );
+ HQL_OPERATORS.add( "=<" );
+ HQL_OPERATORS.add( "!=" );
+ HQL_OPERATORS.add( "<>" );
+ HQL_OPERATORS.add( "!#" );
+ HQL_OPERATORS.add( "!~" );
+ HQL_OPERATORS.add( "!<" );
+ HQL_OPERATORS.add( "!>" );
+ HQL_OPERATORS.add( "is not" );
+ HQL_OPERATORS.add( "not like" );
+ HQL_OPERATORS.add( "not in" );
+ HQL_OPERATORS.add( "not between" );
+ HQL_OPERATORS.add( "not exists" );
+ }
+
+ private Map replacements;
+ private boolean quoted;
+ private StringBuffer quotedString;
+ private ClauseParser parser = new ClauseParser();
+ private String lastToken;
+ private String currentCollectionProp;
+
+ public PreprocessingParser(Map replacements) {
+ this.replacements = replacements;
+ }
+
+ public void token(String token, QueryTranslatorImpl q) throws QueryException {
+
+ //handle quoted strings
+ if ( quoted ) {
+ quotedString.append( token );
+ }
+ if ( "'".equals( token ) ) {
+ if ( quoted ) {
+ token = quotedString.toString();
+ }
+ else {
+ quotedString = new StringBuffer( 20 ).append( token );
+ }
+ quoted = !quoted;
+ }
+ if ( quoted ) return;
+
+ //ignore whitespace
+ if ( ParserHelper.isWhitespace( token ) ) return;
+
+ //do replacements
+ String substoken = ( String ) replacements.get( token );
+ token = ( substoken == null ) ? token : substoken;
+
+ //handle HQL2 collection syntax
+ if ( currentCollectionProp != null ) {
+ if ( "(".equals( token ) ) {
+ return;
+ }
+ else if ( ")".equals( token ) ) {
+ currentCollectionProp = null;
+ return;
+ }
+ else {
+ token = StringHelper.qualify( token, currentCollectionProp );
+ }
+ }
+ else {
+ String prop = CollectionProperties.getNormalizedPropertyName( token.toLowerCase() );
+ if ( prop != null ) {
+ currentCollectionProp = prop;
+ return;
+ }
+ }
+
+
+ //handle <=, >=, !=, is not, not between, not in
+ if ( lastToken == null ) {
+ lastToken = token;
+ }
+ else {
+ String doubleToken = ( token.length() > 1 ) ?
+ lastToken + ' ' + token :
+ lastToken + token;
+ if ( HQL_OPERATORS.contains( doubleToken.toLowerCase() ) ) {
+ parser.token( doubleToken, q );
+ lastToken = null;
+ }
+ else {
+ parser.token( lastToken, q );
+ lastToken = token;
+ }
+ }
+
+ }
+
+ public void start(QueryTranslatorImpl q) throws QueryException {
+ quoted = false;
+ parser.start( q );
+ }
+
+ public void end(QueryTranslatorImpl q) throws QueryException {
+ if ( lastToken != null ) parser.token( lastToken, q );
+ parser.end( q );
+ lastToken = null;
+ currentCollectionProp = null;
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/QueryTranslatorImpl.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/QueryTranslatorImpl.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/QueryTranslatorImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1161 @@
+//$Id: QueryTranslatorImpl.java 11080 2007-01-23 16:29:18Z steve.ebersole at jboss.com $
+package org.hibernate.hql.classic;
+
+import java.io.Serializable;
+import java.lang.reflect.Constructor;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.collections.SequencedHashMap;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.hibernate.HibernateException;
+import org.hibernate.LockMode;
+import org.hibernate.MappingException;
+import org.hibernate.QueryException;
+import org.hibernate.ScrollableResults;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.JoinSequence;
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.event.EventSource;
+import org.hibernate.exception.JDBCExceptionHelper;
+import org.hibernate.hql.FilterTranslator;
+import org.hibernate.hql.HolderInstantiator;
+import org.hibernate.hql.NameGenerator;
+import org.hibernate.hql.ParameterTranslations;
+import org.hibernate.impl.IteratorImpl;
+import org.hibernate.loader.BasicLoader;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.persister.collection.QueryableCollection;
+import org.hibernate.persister.entity.Loadable;
+import org.hibernate.persister.entity.PropertyMapping;
+import org.hibernate.persister.entity.Queryable;
+import org.hibernate.sql.JoinFragment;
+import org.hibernate.sql.QuerySelect;
+import org.hibernate.transform.ResultTransformer;
+import org.hibernate.type.AssociationType;
+import org.hibernate.type.EntityType;
+import org.hibernate.type.Type;
+import org.hibernate.type.TypeFactory;
+import org.hibernate.util.ArrayHelper;
+import org.hibernate.util.ReflectHelper;
+import org.hibernate.util.StringHelper;
+
+/**
+ * An instance of <tt>QueryTranslator</tt> translates a Hibernate
+ * query string to SQL.
+ */
+public class QueryTranslatorImpl extends BasicLoader implements FilterTranslator {
+
+ private static final String[] NO_RETURN_ALIASES = new String[] {};
+
+ private final String queryIdentifier;
+ private final String queryString;
+
+ private final Map typeMap = new SequencedHashMap();
+ private final Map collections = new SequencedHashMap();
+ private List returnedTypes = new ArrayList();
+ private final List fromTypes = new ArrayList();
+ private final List scalarTypes = new ArrayList();
+ private final Map namedParameters = new HashMap();
+ private final Map aliasNames = new HashMap();
+ private final Map oneToOneOwnerNames = new HashMap();
+ private final Map uniqueKeyOwnerReferences = new HashMap();
+ private final Map decoratedPropertyMappings = new HashMap();
+
+ private final List scalarSelectTokens = new ArrayList();
+ private final List whereTokens = new ArrayList();
+ private final List havingTokens = new ArrayList();
+ private final Map joins = new SequencedHashMap();
+ private final List orderByTokens = new ArrayList();
+ private final List groupByTokens = new ArrayList();
+ private final Set querySpaces = new HashSet();
+ private final Set entitiesToFetch = new HashSet();
+
+ private final Map pathAliases = new HashMap();
+ private final Map pathJoins = new HashMap();
+
+ private Queryable[] persisters;
+ private int[] owners;
+ private EntityType[] ownerAssociationTypes;
+ private String[] names;
+ private boolean[] includeInSelect;
+ private int selectLength;
+ private Type[] returnTypes;
+ private Type[] actualReturnTypes;
+ private String[][] scalarColumnNames;
+ private Map tokenReplacements;
+ private int nameCount = 0;
+ private int parameterCount = 0;
+ private boolean distinct = false;
+ private boolean compiled;
+ private String sqlString;
+ private Class holderClass;
+ private Constructor holderConstructor;
+ private boolean hasScalars;
+ private boolean shallowQuery;
+ private QueryTranslatorImpl superQuery;
+
+ private QueryableCollection collectionPersister;
+ private int collectionOwnerColumn = -1;
+ private String collectionOwnerName;
+ private String fetchName;
+
+ private String[] suffixes;
+
+ private Map enabledFilters;
+
+ private static final Log log = LogFactory.getLog( QueryTranslatorImpl.class );
+
+ /**
+ * Construct a query translator
+ *
+ * @param queryIdentifier A unique identifier for the query of which this
+ * translation is part; typically this is the original, user-supplied query string.
+ * @param queryString The "preprocessed" query string; at the very least
+ * already processed by {@link org.hibernate.hql.QuerySplitter}.
+ * @param enabledFilters Any enabled filters.
+ * @param factory The session factory.
+ */
+ public QueryTranslatorImpl(
+ String queryIdentifier,
+ String queryString,
+ Map enabledFilters,
+ SessionFactoryImplementor factory) {
+ super( factory );
+ this.queryIdentifier = queryIdentifier;
+ this.queryString = queryString;
+ this.enabledFilters = enabledFilters;
+ }
+
+ /**
+ * Construct a query translator; this form used internally.
+ *
+ * @param queryString The query string to process.
+ * @param enabledFilters Any enabled filters.
+ * @param factory The session factory.
+ */
+ public QueryTranslatorImpl(
+ String queryString,
+ Map enabledFilters,
+ SessionFactoryImplementor factory) {
+ this( queryString, queryString, enabledFilters, factory );
+ }
+
+ /**
+ * Compile a subquery.
+ *
+ * @param superquery The containing query of the query to be compiled.
+ *
+ * @throws org.hibernate.MappingException Indicates problems resolving
+ * things referenced in the query.
+ * @throws org.hibernate.QueryException Generally some form of syntatic
+ * failure.
+ */
+ void compile(QueryTranslatorImpl superquery) throws QueryException, MappingException {
+ this.tokenReplacements = superquery.tokenReplacements;
+ this.superQuery = superquery;
+ this.shallowQuery = true;
+ this.enabledFilters = superquery.getEnabledFilters();
+ compile();
+ }
+
+
+ /**
+ * Compile a "normal" query. This method may be called multiple
+ * times. Subsequent invocations are no-ops.
+ */
+ public synchronized void compile(
+ Map replacements,
+ boolean scalar) throws QueryException, MappingException {
+ if ( !compiled ) {
+ this.tokenReplacements = replacements;
+ this.shallowQuery = scalar;
+ compile();
+ }
+ }
+
+ /**
+ * Compile a filter. This method may be called multiple
+ * times. Subsequent invocations are no-ops.
+ */
+ public synchronized void compile(
+ String collectionRole,
+ Map replacements,
+ boolean scalar) throws QueryException, MappingException {
+
+ if ( !isCompiled() ) {
+ addFromAssociation( "this", collectionRole );
+ compile( replacements, scalar );
+ }
+ }
+
+ /**
+ * Compile the query (generate the SQL).
+ *
+ * @throws org.hibernate.MappingException Indicates problems resolving
+ * things referenced in the query.
+ * @throws org.hibernate.QueryException Generally some form of syntatic
+ * failure.
+ */
+ private void compile() throws QueryException, MappingException {
+
+ log.trace( "compiling query" );
+ try {
+ ParserHelper.parse( new PreprocessingParser( tokenReplacements ),
+ queryString,
+ ParserHelper.HQL_SEPARATORS,
+ this );
+ renderSQL();
+ }
+ catch ( QueryException qe ) {
+ qe.setQueryString( queryString );
+ throw qe;
+ }
+ catch ( MappingException me ) {
+ throw me;
+ }
+ catch ( Exception e ) {
+ log.debug( "unexpected query compilation problem", e );
+ e.printStackTrace();
+ QueryException qe = new QueryException( "Incorrect query syntax", e );
+ qe.setQueryString( queryString );
+ throw qe;
+ }
+
+ postInstantiate();
+
+ compiled = true;
+
+ }
+
+ public String getSQLString() {
+ return sqlString;
+ }
+
+ public List collectSqlStrings() {
+ return ArrayHelper.toList( new String[] { sqlString } );
+ }
+
+ public String getQueryString() {
+ return queryString;
+ }
+
+ /**
+ * Persisters for the return values of a <tt>find()</tt> style query.
+ *
+ * @return an array of <tt>EntityPersister</tt>s.
+ */
+ protected Loadable[] getEntityPersisters() {
+ return persisters;
+ }
+
+ /**
+ * Types of the return values of an <tt>iterate()</tt> style query.
+ *
+ * @return an array of <tt>Type</tt>s.
+ */
+ public Type[] getReturnTypes() {
+ return actualReturnTypes;
+ }
+
+ public String[] getReturnAliases() {
+ // return aliases not supported in classic translator!
+ return NO_RETURN_ALIASES;
+ }
+
+ public String[][] getColumnNames() {
+ return scalarColumnNames;
+ }
+
+ private static void logQuery(String hql, String sql) {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "HQL: " + hql );
+ log.debug( "SQL: " + sql );
+ }
+ }
+
+ void setAliasName(String alias, String name) {
+ aliasNames.put( alias, name );
+ }
+
+ public String getAliasName(String alias) {
+ String name = ( String ) aliasNames.get( alias );
+ if ( name == null ) {
+ if ( superQuery != null ) {
+ name = superQuery.getAliasName( alias );
+ }
+ else {
+ name = alias;
+ }
+ }
+ return name;
+ }
+
+ String unalias(String path) {
+ String alias = StringHelper.root( path );
+ String name = getAliasName( alias );
+ if ( name != null ) {
+ return name + path.substring( alias.length() );
+ }
+ else {
+ return path;
+ }
+ }
+
+ void addEntityToFetch(String name, String oneToOneOwnerName, AssociationType ownerAssociationType) {
+ addEntityToFetch( name );
+ if ( oneToOneOwnerName != null ) oneToOneOwnerNames.put( name, oneToOneOwnerName );
+ if ( ownerAssociationType != null ) uniqueKeyOwnerReferences.put( name, ownerAssociationType );
+ }
+
+ private void addEntityToFetch(String name) {
+ entitiesToFetch.add( name );
+ }
+
+ private int nextCount() {
+ return ( superQuery == null ) ? nameCount++ : superQuery.nameCount++;
+ }
+
+ String createNameFor(String type) {
+ return StringHelper.generateAlias( type, nextCount() );
+ }
+
+ String createNameForCollection(String role) {
+ return StringHelper.generateAlias( role, nextCount() );
+ }
+
+ private String getType(String name) {
+ String type = ( String ) typeMap.get( name );
+ if ( type == null && superQuery != null ) {
+ type = superQuery.getType( name );
+ }
+ return type;
+ }
+
+ private String getRole(String name) {
+ String role = ( String ) collections.get( name );
+ if ( role == null && superQuery != null ) {
+ role = superQuery.getRole( name );
+ }
+ return role;
+ }
+
+ boolean isName(String name) {
+ return aliasNames.containsKey( name ) ||
+ typeMap.containsKey( name ) ||
+ collections.containsKey( name ) || (
+ superQuery != null && superQuery.isName( name )
+ );
+ }
+
+ PropertyMapping getPropertyMapping(String name) throws QueryException {
+ PropertyMapping decorator = getDecoratedPropertyMapping( name );
+ if ( decorator != null ) return decorator;
+
+ String type = getType( name );
+ if ( type == null ) {
+ String role = getRole( name );
+ if ( role == null ) {
+ throw new QueryException( "alias not found: " + name );
+ }
+ return getCollectionPersister( role ); //.getElementPropertyMapping();
+ }
+ else {
+ Queryable persister = getEntityPersister( type );
+ if ( persister == null ) throw new QueryException( "persistent class not found: " + type );
+ return persister;
+ }
+ }
+
+ private PropertyMapping getDecoratedPropertyMapping(String name) {
+ return ( PropertyMapping ) decoratedPropertyMappings.get( name );
+ }
+
+ void decoratePropertyMapping(String name, PropertyMapping mapping) {
+ decoratedPropertyMappings.put( name, mapping );
+ }
+
+ private Queryable getEntityPersisterForName(String name) throws QueryException {
+ String type = getType( name );
+ Queryable persister = getEntityPersister( type );
+ if ( persister == null ) throw new QueryException( "persistent class not found: " + type );
+ return persister;
+ }
+
+ Queryable getEntityPersisterUsingImports(String className) {
+ final String importedClassName = getFactory().getImportedClassName( className );
+ if ( importedClassName == null ) {
+ return null;
+ }
+ try {
+ return ( Queryable ) getFactory().getEntityPersister( importedClassName );
+ }
+ catch ( MappingException me ) {
+ return null;
+ }
+ }
+
+ Queryable getEntityPersister(String entityName) throws QueryException {
+ try {
+ return ( Queryable ) getFactory().getEntityPersister( entityName );
+ }
+ catch ( Exception e ) {
+ throw new QueryException( "persistent class not found: " + entityName );
+ }
+ }
+
+ QueryableCollection getCollectionPersister(String role) throws QueryException {
+ try {
+ return ( QueryableCollection ) getFactory().getCollectionPersister( role );
+ }
+ catch ( ClassCastException cce ) {
+ throw new QueryException( "collection role is not queryable: " + role );
+ }
+ catch ( Exception e ) {
+ throw new QueryException( "collection role not found: " + role );
+ }
+ }
+
+ void addType(String name, String type) {
+ typeMap.put( name, type );
+ }
+
+ void addCollection(String name, String role) {
+ collections.put( name, role );
+ }
+
+ void addFrom(String name, String type, JoinSequence joinSequence)
+ throws QueryException {
+ addType( name, type );
+ addFrom( name, joinSequence );
+ }
+
+ void addFromCollection(String name, String collectionRole, JoinSequence joinSequence)
+ throws QueryException {
+ //register collection role
+ addCollection( name, collectionRole );
+ addJoin( name, joinSequence );
+ }
+
+ void addFrom(String name, JoinSequence joinSequence)
+ throws QueryException {
+ fromTypes.add( name );
+ addJoin( name, joinSequence );
+ }
+
+ void addFromClass(String name, Queryable classPersister)
+ throws QueryException {
+ JoinSequence joinSequence = new JoinSequence( getFactory() )
+ .setRoot( classPersister, name );
+ //crossJoins.add(name);
+ addFrom( name, classPersister.getEntityName(), joinSequence );
+ }
+
+ void addSelectClass(String name) {
+ returnedTypes.add( name );
+ }
+
+ void addSelectScalar(Type type) {
+ scalarTypes.add( type );
+ }
+
+ void appendWhereToken(String token) {
+ whereTokens.add( token );
+ }
+
+ void appendHavingToken(String token) {
+ havingTokens.add( token );
+ }
+
+ void appendOrderByToken(String token) {
+ orderByTokens.add( token );
+ }
+
+ void appendGroupByToken(String token) {
+ groupByTokens.add( token );
+ }
+
+ void appendScalarSelectToken(String token) {
+ scalarSelectTokens.add( token );
+ }
+
+ void appendScalarSelectTokens(String[] tokens) {
+ scalarSelectTokens.add( tokens );
+ }
+
+ void addFromJoinOnly(String name, JoinSequence joinSequence) throws QueryException {
+ addJoin( name, joinSequence.getFromPart() );
+ }
+
+ void addJoin(String name, JoinSequence joinSequence) throws QueryException {
+ if ( !joins.containsKey( name ) ) joins.put( name, joinSequence );
+ }
+
+ void addNamedParameter(String name) {
+ if ( superQuery != null ) superQuery.addNamedParameter( name );
+ Integer loc = new Integer( parameterCount++ );
+ Object o = namedParameters.get( name );
+ if ( o == null ) {
+ namedParameters.put( name, loc );
+ }
+ else if ( o instanceof Integer ) {
+ ArrayList list = new ArrayList( 4 );
+ list.add( o );
+ list.add( loc );
+ namedParameters.put( name, list );
+ }
+ else {
+ ( ( ArrayList ) o ).add( loc );
+ }
+ }
+
+ public int[] getNamedParameterLocs(String name) throws QueryException {
+ Object o = namedParameters.get( name );
+ if ( o == null ) {
+ QueryException qe = new QueryException( ERROR_NAMED_PARAMETER_DOES_NOT_APPEAR + name );
+ qe.setQueryString( queryString );
+ throw qe;
+ }
+ if ( o instanceof Integer ) {
+ return new int[]{ ( ( Integer ) o ).intValue() };
+ }
+ else {
+ return ArrayHelper.toIntArray( ( ArrayList ) o );
+ }
+ }
+
+ private void renderSQL() throws QueryException, MappingException {
+
+ final int rtsize;
+ if ( returnedTypes.size() == 0 && scalarTypes.size() == 0 ) {
+ //ie no select clause in HQL
+ returnedTypes = fromTypes;
+ rtsize = returnedTypes.size();
+ }
+ else {
+ rtsize = returnedTypes.size();
+ Iterator iter = entitiesToFetch.iterator();
+ while ( iter.hasNext() ) {
+ returnedTypes.add( iter.next() );
+ }
+ }
+ int size = returnedTypes.size();
+ persisters = new Queryable[size];
+ names = new String[size];
+ owners = new int[size];
+ ownerAssociationTypes = new EntityType[size];
+ suffixes = new String[size];
+ includeInSelect = new boolean[size];
+ for ( int i = 0; i < size; i++ ) {
+ String name = ( String ) returnedTypes.get( i );
+ //if ( !isName(name) ) throw new QueryException("unknown type: " + name);
+ persisters[i] = getEntityPersisterForName( name );
+ // TODO: cannot use generateSuffixes() - it handles the initial suffix differently.
+ suffixes[i] = ( size == 1 ) ? "" : Integer.toString( i ) + '_';
+ names[i] = name;
+ includeInSelect[i] = !entitiesToFetch.contains( name );
+ if ( includeInSelect[i] ) selectLength++;
+ if ( name.equals( collectionOwnerName ) ) collectionOwnerColumn = i;
+ String oneToOneOwner = ( String ) oneToOneOwnerNames.get( name );
+ owners[i] = ( oneToOneOwner == null ) ? -1 : returnedTypes.indexOf( oneToOneOwner );
+ ownerAssociationTypes[i] = (EntityType) uniqueKeyOwnerReferences.get( name );
+ }
+
+ if ( ArrayHelper.isAllNegative( owners ) ) owners = null;
+
+ String scalarSelect = renderScalarSelect(); //Must be done here because of side-effect! yuck...
+
+ int scalarSize = scalarTypes.size();
+ hasScalars = scalarTypes.size() != rtsize;
+
+ returnTypes = new Type[scalarSize];
+ for ( int i = 0; i < scalarSize; i++ ) {
+ returnTypes[i] = ( Type ) scalarTypes.get( i );
+ }
+
+ QuerySelect sql = new QuerySelect( getFactory().getDialect() );
+ sql.setDistinct( distinct );
+
+ if ( !shallowQuery ) {
+ renderIdentifierSelect( sql );
+ renderPropertiesSelect( sql );
+ }
+
+ if ( collectionPersister != null ) {
+ sql.addSelectFragmentString( collectionPersister.selectFragment( fetchName, "__" ) );
+ }
+
+ if ( hasScalars || shallowQuery ) sql.addSelectFragmentString( scalarSelect );
+
+ //TODO: for some dialects it would be appropriate to add the renderOrderByPropertiesSelect() to other select strings
+ mergeJoins( sql.getJoinFragment() );
+
+ sql.setWhereTokens( whereTokens.iterator() );
+
+ sql.setGroupByTokens( groupByTokens.iterator() );
+ sql.setHavingTokens( havingTokens.iterator() );
+ sql.setOrderByTokens( orderByTokens.iterator() );
+
+ if ( collectionPersister != null && collectionPersister.hasOrdering() ) {
+ sql.addOrderBy( collectionPersister.getSQLOrderByString( fetchName ) );
+ }
+
+ scalarColumnNames = NameGenerator.generateColumnNames( returnTypes, getFactory() );
+
+ // initialize the Set of queried identifier spaces (ie. tables)
+ Iterator iter = collections.values().iterator();
+ while ( iter.hasNext() ) {
+ CollectionPersister p = getCollectionPersister( ( String ) iter.next() );
+ addQuerySpaces( p.getCollectionSpaces() );
+ }
+ iter = typeMap.keySet().iterator();
+ while ( iter.hasNext() ) {
+ Queryable p = getEntityPersisterForName( ( String ) iter.next() );
+ addQuerySpaces( p.getQuerySpaces() );
+ }
+
+ sqlString = sql.toQueryString();
+
+ if ( holderClass != null ) holderConstructor = ReflectHelper.getConstructor( holderClass, returnTypes );
+
+ if ( hasScalars ) {
+ actualReturnTypes = returnTypes;
+ }
+ else {
+ actualReturnTypes = new Type[selectLength];
+ int j = 0;
+ for ( int i = 0; i < persisters.length; i++ ) {
+ if ( includeInSelect[i] ) {
+ actualReturnTypes[j++] = TypeFactory.manyToOne( persisters[i].getEntityName(), shallowQuery );
+ }
+ }
+ }
+
+ }
+
+ private void renderIdentifierSelect(QuerySelect sql) {
+ int size = returnedTypes.size();
+
+ for ( int k = 0; k < size; k++ ) {
+ String name = ( String ) returnedTypes.get( k );
+ String suffix = size == 1 ? "" : Integer.toString( k ) + '_';
+ sql.addSelectFragmentString( persisters[k].identifierSelectFragment( name, suffix ) );
+ }
+
+ }
+
+ /*private String renderOrderByPropertiesSelect() {
+ StringBuffer buf = new StringBuffer(10);
+
+ //add the columns we are ordering by to the select ID select clause
+ Iterator iter = orderByTokens.iterator();
+ while ( iter.hasNext() ) {
+ String token = (String) iter.next();
+ if ( token.lastIndexOf(".") > 0 ) {
+ //ie. it is of form "foo.bar", not of form "asc" or "desc"
+ buf.append(StringHelper.COMMA_SPACE).append(token);
+ }
+ }
+
+ return buf.toString();
+ }*/
+
+ private void renderPropertiesSelect(QuerySelect sql) {
+ int size = returnedTypes.size();
+ for ( int k = 0; k < size; k++ ) {
+ String suffix = size == 1 ? "" : Integer.toString( k ) + '_';
+ String name = ( String ) returnedTypes.get( k );
+ sql.addSelectFragmentString( persisters[k].propertySelectFragment( name, suffix, false ) );
+ }
+ }
+
+ /**
+ * WARNING: side-effecty
+ */
+ private String renderScalarSelect() {
+
+ boolean isSubselect = superQuery != null;
+
+ StringBuffer buf = new StringBuffer( 20 );
+
+ if ( scalarTypes.size() == 0 ) {
+ //ie. no select clause
+ int size = returnedTypes.size();
+ for ( int k = 0; k < size; k++ ) {
+
+ scalarTypes.add( TypeFactory.manyToOne( persisters[k].getEntityName(), shallowQuery ) );
+
+ String[] idColumnNames = persisters[k].getIdentifierColumnNames();
+ for ( int i = 0; i < idColumnNames.length; i++ ) {
+ buf.append( returnedTypes.get( k ) ).append( '.' ).append( idColumnNames[i] );
+ if ( !isSubselect ) buf.append( " as " ).append( NameGenerator.scalarName( k, i ) );
+ if ( i != idColumnNames.length - 1 || k != size - 1 ) buf.append( ", " );
+ }
+
+ }
+
+ }
+ else {
+ //there _was_ a select clause
+ Iterator iter = scalarSelectTokens.iterator();
+ int c = 0;
+ boolean nolast = false; //real hacky...
+ int parenCount = 0; // used to count the nesting of parentheses
+ while ( iter.hasNext() ) {
+ Object next = iter.next();
+ if ( next instanceof String ) {
+ String token = ( String ) next;
+
+ if ( "(".equals( token ) ) {
+ parenCount++;
+ }
+ else if ( ")".equals( token ) ) {
+ parenCount--;
+ }
+
+ String lc = token.toLowerCase();
+ if ( lc.equals( ", " ) ) {
+ if ( nolast ) {
+ nolast = false;
+ }
+ else {
+ if ( !isSubselect && parenCount == 0 ) {
+ int x = c++;
+ buf.append( " as " )
+ .append( NameGenerator.scalarName( x, 0 ) );
+ }
+ }
+ }
+ buf.append( token );
+ if ( lc.equals( "distinct" ) || lc.equals( "all" ) ) {
+ buf.append( ' ' );
+ }
+ }
+ else {
+ nolast = true;
+ String[] tokens = ( String[] ) next;
+ for ( int i = 0; i < tokens.length; i++ ) {
+ buf.append( tokens[i] );
+ if ( !isSubselect ) {
+ buf.append( " as " )
+ .append( NameGenerator.scalarName( c, i ) );
+ }
+ if ( i != tokens.length - 1 ) buf.append( ", " );
+ }
+ c++;
+ }
+ }
+ if ( !isSubselect && !nolast ) {
+ int x = c++;
+ buf.append( " as " )
+ .append( NameGenerator.scalarName( x, 0 ) );
+ }
+
+ }
+
+ return buf.toString();
+ }
+
+ private void mergeJoins(JoinFragment ojf) throws MappingException, QueryException {
+
+ Iterator iter = joins.entrySet().iterator();
+ while ( iter.hasNext() ) {
+ Map.Entry me = ( Map.Entry ) iter.next();
+ String name = ( String ) me.getKey();
+ JoinSequence join = ( JoinSequence ) me.getValue();
+ join.setSelector( new JoinSequence.Selector() {
+ public boolean includeSubclasses(String alias) {
+ boolean include = returnedTypes.contains( alias ) && !isShallowQuery();
+ return include;
+ }
+ } );
+
+ if ( typeMap.containsKey( name ) ) {
+ ojf.addFragment( join.toJoinFragment( enabledFilters, true ) );
+ }
+ else if ( collections.containsKey( name ) ) {
+ ojf.addFragment( join.toJoinFragment( enabledFilters, true ) );
+ }
+ else {
+ //name from a super query (a bit inelegant that it shows up here)
+ }
+
+ }
+
+ }
+
+ public final Set getQuerySpaces() {
+ return querySpaces;
+ }
+
+ /**
+ * Is this query called by scroll() or iterate()?
+ *
+ * @return true if it is, false if it is called by find() or list()
+ */
+ boolean isShallowQuery() {
+ return shallowQuery;
+ }
+
+ void addQuerySpaces(Serializable[] spaces) {
+ for ( int i = 0; i < spaces.length; i++ ) {
+ querySpaces.add( spaces[i] );
+ }
+ if ( superQuery != null ) superQuery.addQuerySpaces( spaces );
+ }
+
+ void setDistinct(boolean distinct) {
+ this.distinct = distinct;
+ }
+
+ boolean isSubquery() {
+ return superQuery != null;
+ }
+
+ /**
+ * Overrides method from Loader
+ */
+ public CollectionPersister[] getCollectionPersisters() {
+ return collectionPersister == null ? null : new CollectionPersister[] { collectionPersister };
+ }
+
+ protected String[] getCollectionSuffixes() {
+ return collectionPersister == null ? null : new String[] { "__" };
+ }
+
+ void setCollectionToFetch(String role, String name, String ownerName, String entityName)
+ throws QueryException {
+ fetchName = name;
+ collectionPersister = getCollectionPersister( role );
+ collectionOwnerName = ownerName;
+ if ( collectionPersister.getElementType().isEntityType() ) {
+ addEntityToFetch( entityName );
+ }
+ }
+
+ protected String[] getSuffixes() {
+ return suffixes;
+ }
+
+ protected String[] getAliases() {
+ return names;
+ }
+
+ /**
+ * Used for collection filters
+ */
+ private void addFromAssociation(final String elementName, final String collectionRole)
+ throws QueryException {
+ //q.addCollection(collectionName, collectionRole);
+ QueryableCollection persister = getCollectionPersister( collectionRole );
+ Type collectionElementType = persister.getElementType();
+ if ( !collectionElementType.isEntityType() ) {
+ throw new QueryException( "collection of values in filter: " + elementName );
+ }
+
+ String[] keyColumnNames = persister.getKeyColumnNames();
+ //if (keyColumnNames.length!=1) throw new QueryException("composite-key collection in filter: " + collectionRole);
+
+ String collectionName;
+ JoinSequence join = new JoinSequence( getFactory() );
+ collectionName = persister.isOneToMany() ?
+ elementName :
+ createNameForCollection( collectionRole );
+ join.setRoot( persister, collectionName );
+ if ( !persister.isOneToMany() ) {
+ //many-to-many
+ addCollection( collectionName, collectionRole );
+ try {
+ join.addJoin( ( AssociationType ) persister.getElementType(),
+ elementName,
+ JoinFragment.INNER_JOIN,
+ persister.getElementColumnNames(collectionName) );
+ }
+ catch ( MappingException me ) {
+ throw new QueryException( me );
+ }
+ }
+ join.addCondition( collectionName, keyColumnNames, " = ?" );
+ //if ( persister.hasWhere() ) join.addCondition( persister.getSQLWhereString(collectionName) );
+ EntityType elemType = ( EntityType ) collectionElementType;
+ addFrom( elementName, elemType.getAssociatedEntityName(), join );
+
+ }
+
+ String getPathAlias(String path) {
+ return ( String ) pathAliases.get( path );
+ }
+
+ JoinSequence getPathJoin(String path) {
+ return ( JoinSequence ) pathJoins.get( path );
+ }
+
+ void addPathAliasAndJoin(String path, String alias, JoinSequence joinSequence) {
+ pathAliases.put( path, alias );
+ pathJoins.put( path, joinSequence );
+ }
+
+ public List list(SessionImplementor session, QueryParameters queryParameters)
+ throws HibernateException {
+ return list( session, queryParameters, getQuerySpaces(), actualReturnTypes );
+ }
+
+ /**
+ * Return the query results as an iterator
+ */
+ public Iterator iterate(QueryParameters queryParameters, EventSource session)
+ throws HibernateException {
+
+ boolean stats = session.getFactory().getStatistics().isStatisticsEnabled();
+ long startTime = 0;
+ if ( stats ) startTime = System.currentTimeMillis();
+
+ try {
+
+ PreparedStatement st = prepareQueryStatement( queryParameters, false, session );
+ ResultSet rs = getResultSet( st, queryParameters.hasAutoDiscoverScalarTypes(), false, queryParameters.getRowSelection(), session );
+ HolderInstantiator hi = HolderInstantiator.createClassicHolderInstantiator(holderConstructor, queryParameters.getResultTransformer());
+ Iterator result = new IteratorImpl( rs, st, session, returnTypes, getColumnNames(), hi );
+
+ if ( stats ) {
+ session.getFactory().getStatisticsImplementor().queryExecuted(
+ "HQL: " + queryString,
+ 0,
+ System.currentTimeMillis() - startTime
+ );
+ }
+
+ return result;
+
+ }
+ catch ( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ getFactory().getSQLExceptionConverter(),
+ sqle,
+ "could not execute query using iterate",
+ getSQLString()
+ );
+ }
+
+ }
+
+ public int executeUpdate(QueryParameters queryParameters, SessionImplementor session) throws HibernateException {
+ throw new UnsupportedOperationException( "Not supported! Use the AST translator...");
+ }
+
+ protected Object getResultColumnOrRow(Object[] row, ResultTransformer transformer, ResultSet rs, SessionImplementor session)
+ throws SQLException, HibernateException {
+ row = toResultRow( row );
+ if ( hasScalars ) {
+ String[][] scalarColumns = getColumnNames();
+ int queryCols = returnTypes.length;
+ if ( holderClass == null && queryCols == 1 ) {
+ return returnTypes[0].nullSafeGet( rs, scalarColumns[0], session, null );
+ }
+ else {
+ row = new Object[queryCols];
+ for ( int i = 0; i < queryCols; i++ )
+ row[i] = returnTypes[i].nullSafeGet( rs, scalarColumns[i], session, null );
+ return row;
+ }
+ }
+ else if ( holderClass == null ) {
+ return row.length == 1 ? row[0] : row;
+ }
+ else {
+ return row;
+ }
+
+ }
+
+ protected List getResultList(List results, ResultTransformer resultTransformer) throws QueryException {
+ if ( holderClass != null ) {
+ for ( int i = 0; i < results.size(); i++ ) {
+ Object[] row = ( Object[] ) results.get( i );
+ try {
+ results.set( i, holderConstructor.newInstance( row ) );
+ }
+ catch ( Exception e ) {
+ throw new QueryException( "could not instantiate: " + holderClass, e );
+ }
+ }
+ }
+ return results;
+ }
+
+ private Object[] toResultRow(Object[] row) {
+ if ( selectLength == row.length ) {
+ return row;
+ }
+ else {
+ Object[] result = new Object[selectLength];
+ int j = 0;
+ for ( int i = 0; i < row.length; i++ ) {
+ if ( includeInSelect[i] ) result[j++] = row[i];
+ }
+ return result;
+ }
+ }
+
+ void setHolderClass(Class clazz) {
+ holderClass = clazz;
+ }
+
+ protected LockMode[] getLockModes(Map lockModes) {
+ // unfortunately this stuff can't be cached because
+ // it is per-invocation, not constant for the
+ // QueryTranslator instance
+ HashMap nameLockModes = new HashMap();
+ if ( lockModes != null ) {
+ Iterator iter = lockModes.entrySet().iterator();
+ while ( iter.hasNext() ) {
+ Map.Entry me = ( Map.Entry ) iter.next();
+ nameLockModes.put( getAliasName( ( String ) me.getKey() ),
+ me.getValue() );
+ }
+ }
+ LockMode[] lockModeArray = new LockMode[names.length];
+ for ( int i = 0; i < names.length; i++ ) {
+ LockMode lm = ( LockMode ) nameLockModes.get( names[i] );
+ if ( lm == null ) lm = LockMode.NONE;
+ lockModeArray[i] = lm;
+ }
+ return lockModeArray;
+ }
+
+ protected String applyLocks(String sql, Map lockModes, Dialect dialect) throws QueryException {
+ // can't cache this stuff either (per-invocation)
+ final String result;
+ if ( lockModes == null || lockModes.size() == 0 ) {
+ result = sql;
+ }
+ else {
+ Map aliasedLockModes = new HashMap();
+ Iterator iter = lockModes.entrySet().iterator();
+ while ( iter.hasNext() ) {
+ Map.Entry me = ( Map.Entry ) iter.next();
+ aliasedLockModes.put( getAliasName( ( String ) me.getKey() ), me.getValue() );
+ }
+ Map keyColumnNames = null;
+ if ( dialect.forUpdateOfColumns() ) {
+ keyColumnNames = new HashMap();
+ for ( int i = 0; i < names.length; i++ ) {
+ keyColumnNames.put( names[i], persisters[i].getIdentifierColumnNames() );
+ }
+ }
+ result = dialect.applyLocksToSql( sql, aliasedLockModes, keyColumnNames );
+ }
+ logQuery( queryString, result );
+ return result;
+ }
+
+ protected boolean upgradeLocks() {
+ return true;
+ }
+
+ protected int[] getCollectionOwners() {
+ return new int[] { collectionOwnerColumn };
+ }
+
+ protected boolean isCompiled() {
+ return compiled;
+ }
+
+ public String toString() {
+ return queryString;
+ }
+
+ protected int[] getOwners() {
+ return owners;
+ }
+
+ protected EntityType[] getOwnerAssociationTypes() {
+ return ownerAssociationTypes;
+ }
+
+ public Class getHolderClass() {
+ return holderClass;
+ }
+
+ public Map getEnabledFilters() {
+ return enabledFilters;
+ }
+
+ public ScrollableResults scroll(final QueryParameters queryParameters,
+ final SessionImplementor session)
+ throws HibernateException {
+ HolderInstantiator hi = HolderInstantiator.createClassicHolderInstantiator(holderConstructor, queryParameters.getResultTransformer());
+ return scroll( queryParameters, returnTypes, hi, session );
+ }
+
+ public String getQueryIdentifier() {
+ return queryIdentifier;
+ }
+
+ protected boolean isSubselectLoadingEnabled() {
+ return hasSubselectLoadableCollections();
+ }
+
+ public void validateScrollability() throws HibernateException {
+ // This is the legacy behaviour for HQL queries...
+ if ( getCollectionPersisters() != null ) {
+ throw new HibernateException( "Cannot scroll queries which initialize collections" );
+ }
+ }
+
+ public boolean containsCollectionFetches() {
+ return false;
+ }
+
+ public boolean isManipulationStatement() {
+ // classic parser does not support bulk manipulation statements
+ return false;
+ }
+
+ public ParameterTranslations getParameterTranslations() {
+ return new ParameterTranslations() {
+
+ public boolean supportsOrdinalParameterMetadata() {
+ // classic translator does not support collection of ordinal
+ // param metadata
+ return false;
+ }
+
+ public int getOrdinalParameterCount() {
+ return 0; // not known!
+ }
+
+ public int getOrdinalParameterSqlLocation(int ordinalPosition) {
+ return 0; // not known!
+ }
+
+ public Type getOrdinalParameterExpectedType(int ordinalPosition) {
+ return null; // not known!
+ }
+
+ public Set getNamedParameterNames() {
+ return namedParameters.keySet();
+ }
+
+ public int[] getNamedParameterSqlLocations(String name) {
+ return getNamedParameterLocs( name );
+ }
+
+ public Type getNamedParameterExpectedType(String name) {
+ return null; // not known!
+ }
+ };
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/SelectParser.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/SelectParser.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/SelectParser.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,228 @@
+//$Id: SelectParser.java 9915 2006-05-09 09:38:15Z max.andersen at jboss.com $
+package org.hibernate.hql.classic;
+
+import org.hibernate.Hibernate;
+import org.hibernate.QueryException;
+import org.hibernate.dialect.function.SQLFunction;
+import org.hibernate.hql.QuerySplitter;
+import org.hibernate.type.Type;
+import org.hibernate.util.ReflectHelper;
+
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Parsers the select clause of a Hibernate query.
+ *
+ * @author Gavin King, David Channon
+ */
+public class SelectParser implements Parser {
+
+ //TODO: arithmetic expressions, multiple new Foo(...)
+
+ private static final Set COUNT_MODIFIERS = new HashSet();
+
+ static {
+ COUNT_MODIFIERS.add( "distinct" );
+ COUNT_MODIFIERS.add( "all" );
+ COUNT_MODIFIERS.add( "*" );
+ }
+
+ private LinkedList aggregateFuncTokenList = new LinkedList();
+
+ private boolean ready;
+ private boolean aggregate;
+ private boolean first;
+ private boolean afterNew;
+ private boolean insideNew;
+ private boolean aggregateAddSelectScalar;
+ private Class holderClass;
+
+ private final SelectPathExpressionParser pathExpressionParser;
+ private final PathExpressionParser aggregatePathExpressionParser;
+
+ {
+ pathExpressionParser = new SelectPathExpressionParser();
+ aggregatePathExpressionParser = new PathExpressionParser();
+ //TODO: would be nice to use false, but issues with MS SQL
+ pathExpressionParser.setUseThetaStyleJoin( true );
+ aggregatePathExpressionParser.setUseThetaStyleJoin( true );
+ }
+
+ public void token(String token, QueryTranslatorImpl q) throws QueryException {
+
+ String lctoken = token.toLowerCase();
+
+ if ( first ) {
+ first = false;
+ if ( "distinct".equals( lctoken ) ) {
+ q.setDistinct( true );
+ return;
+ }
+ else if ( "all".equals( lctoken ) ) {
+ q.setDistinct( false );
+ return;
+ }
+ }
+
+ if ( afterNew ) {
+ afterNew = false;
+ try {
+ holderClass = ReflectHelper.classForName( QuerySplitter.getImportedClass( token, q.getFactory() ) );
+ }
+ catch ( ClassNotFoundException cnfe ) {
+ throw new QueryException( cnfe );
+ }
+ if ( holderClass == null ) throw new QueryException( "class not found: " + token );
+ q.setHolderClass( holderClass );
+ insideNew = true;
+ }
+ else if ( token.equals( "," ) ) {
+ if ( !aggregate && ready ) throw new QueryException( "alias or expression expected in SELECT" );
+ q.appendScalarSelectToken( ", " );
+ ready = true;
+ }
+ else if ( "new".equals( lctoken ) ) {
+ afterNew = true;
+ ready = false;
+ }
+ else if ( "(".equals( token ) ) {
+ if ( insideNew && !aggregate && !ready ) {
+ //opening paren in new Foo ( ... )
+ ready = true;
+ }
+ else if ( aggregate ) {
+ q.appendScalarSelectToken( token );
+ }
+ else {
+ throw new QueryException( "aggregate function expected before ( in SELECT" );
+ }
+ ready = true;
+ }
+ else if ( ")".equals( token ) ) {
+ if ( insideNew && !aggregate && !ready ) {
+ //if we are inside a new Result(), but not inside a nested function
+ insideNew = false;
+ }
+ else if ( aggregate && ready ) {
+ q.appendScalarSelectToken( token );
+ aggregateFuncTokenList.removeLast();
+ if ( aggregateFuncTokenList.size() < 1 ) {
+ aggregate = false;
+ ready = false;
+ }
+ }
+ else {
+ throw new QueryException( "( expected before ) in select" );
+ }
+ }
+ else if ( COUNT_MODIFIERS.contains( lctoken ) ) {
+ if ( !ready || !aggregate ) throw new QueryException( token + " only allowed inside aggregate function in SELECT" );
+ q.appendScalarSelectToken( token );
+ if ( "*".equals( token ) ) q.addSelectScalar( getFunction( "count", q ).getReturnType( Hibernate.LONG, q.getFactory() ) ); //special case
+ }
+ else if ( getFunction( lctoken, q ) != null && token.equals( q.unalias( token ) ) ) {
+ // the name of an SQL function
+ if ( !ready ) throw new QueryException( ", expected before aggregate function in SELECT: " + token );
+ aggregate = true;
+ aggregateAddSelectScalar = true;
+ aggregateFuncTokenList.add( lctoken );
+ ready = false;
+ q.appendScalarSelectToken( token );
+ if ( !aggregateHasArgs( lctoken, q ) ) {
+ q.addSelectScalar( aggregateType( aggregateFuncTokenList, null, q ) );
+ if ( !aggregateFuncNoArgsHasParenthesis( lctoken, q ) ) {
+ aggregateFuncTokenList.removeLast();
+ if ( aggregateFuncTokenList.size() < 1 ) {
+ aggregate = false;
+ ready = false;
+ }
+ else {
+ ready = true;
+ }
+ }
+ }
+ }
+ else if ( aggregate ) {
+ boolean constantToken = false;
+ if ( !ready ) throw new QueryException( "( expected after aggregate function in SELECT" );
+ try {
+ ParserHelper.parse( aggregatePathExpressionParser, q.unalias( token ), ParserHelper.PATH_SEPARATORS, q );
+ }
+ catch ( QueryException qex ) {
+ constantToken = true;
+ }
+
+ if ( constantToken ) {
+ q.appendScalarSelectToken( token );
+ }
+ else {
+ if ( aggregatePathExpressionParser.isCollectionValued() ) {
+ q.addCollection( aggregatePathExpressionParser.getCollectionName(),
+ aggregatePathExpressionParser.getCollectionRole() );
+ }
+ q.appendScalarSelectToken( aggregatePathExpressionParser.getWhereColumn() );
+ if ( aggregateAddSelectScalar ) {
+ q.addSelectScalar( aggregateType( aggregateFuncTokenList, aggregatePathExpressionParser.getWhereColumnType(), q ) );
+ aggregateAddSelectScalar = false;
+ }
+ aggregatePathExpressionParser.addAssociation( q );
+ }
+ }
+ else {
+ if ( !ready ) throw new QueryException( ", expected in SELECT" );
+ ParserHelper.parse( pathExpressionParser, q.unalias( token ), ParserHelper.PATH_SEPARATORS, q );
+ if ( pathExpressionParser.isCollectionValued() ) {
+ q.addCollection( pathExpressionParser.getCollectionName(),
+ pathExpressionParser.getCollectionRole() );
+ }
+ else if ( pathExpressionParser.getWhereColumnType().isEntityType() ) {
+ q.addSelectClass( pathExpressionParser.getSelectName() );
+ }
+ q.appendScalarSelectTokens( pathExpressionParser.getWhereColumns() );
+ q.addSelectScalar( pathExpressionParser.getWhereColumnType() );
+ pathExpressionParser.addAssociation( q );
+
+ ready = false;
+ }
+ }
+
+ public boolean aggregateHasArgs(String funcToken, QueryTranslatorImpl q) {
+ return getFunction( funcToken, q ).hasArguments();
+ }
+
+ public boolean aggregateFuncNoArgsHasParenthesis(String funcToken, QueryTranslatorImpl q) {
+ return getFunction( funcToken, q ).hasParenthesesIfNoArguments();
+ }
+
+ public Type aggregateType(List funcTokenList, Type type, QueryTranslatorImpl q) throws QueryException {
+ Type retType = type;
+ Type argType;
+ for ( int i = funcTokenList.size() - 1; i >= 0; i-- ) {
+ argType = retType;
+ String funcToken = ( String ) funcTokenList.get( i );
+ retType = getFunction( funcToken, q ).getReturnType( argType, q.getFactory() );
+ }
+ return retType;
+ }
+
+ private SQLFunction getFunction(String name, QueryTranslatorImpl q) {
+ return q.getFactory().getSqlFunctionRegistry().findSQLFunction( name );
+ }
+
+ public void start(QueryTranslatorImpl q) {
+ ready = true;
+ first = true;
+ aggregate = false;
+ afterNew = false;
+ insideNew = false;
+ holderClass = null;
+ aggregateFuncTokenList.clear();
+ }
+
+ public void end(QueryTranslatorImpl q) {
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/SelectPathExpressionParser.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/SelectPathExpressionParser.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/SelectPathExpressionParser.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,31 @@
+//$Id: SelectPathExpressionParser.java 5861 2005-02-22 14:07:36Z oneovthafew $
+package org.hibernate.hql.classic;
+
+import org.hibernate.QueryException;
+
+public class SelectPathExpressionParser extends PathExpressionParser {
+
+ public void end(QueryTranslatorImpl q) throws QueryException {
+ if ( getCurrentProperty() != null && !q.isShallowQuery() ) {
+ // "finish off" the join
+ token( ".", q );
+ token( null, q );
+ }
+ super.end( q );
+ }
+
+ protected void setExpectingCollectionIndex() throws QueryException {
+ throw new QueryException( "illegal syntax near collection-valued path expression in select: " + getCollectionName() );
+ }
+
+ public String getSelectName() {
+ return getCurrentName();
+ }
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/WhereParser.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/WhereParser.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/WhereParser.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,493 @@
+//$Id: WhereParser.java 7825 2005-08-10 20:23:55Z oneovthafew $
+package org.hibernate.hql.classic;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import org.hibernate.MappingException;
+import org.hibernate.QueryException;
+import org.hibernate.engine.JoinSequence;
+import org.hibernate.hql.QueryTranslator;
+import org.hibernate.persister.collection.CollectionPropertyNames;
+import org.hibernate.persister.entity.Queryable;
+import org.hibernate.sql.InFragment;
+import org.hibernate.type.EntityType;
+import org.hibernate.type.LiteralType;
+import org.hibernate.type.Type;
+import org.hibernate.type.TypeFactory;
+import org.hibernate.util.ReflectHelper;
+import org.hibernate.util.StringHelper;
+
+/**
+ * Parses the where clause of a hibernate query and translates it to an
+ * SQL where clause.
+ */
+
+// We should reengineer this class so that, rather than the current ad -
+// hoc linear approach to processing a stream of tokens, we instead
+// build up a tree of expressions.
+
+// We would probably refactor to have LogicParser (builds a tree of simple
+// expressions connected by and, or, not), ExpressionParser (translates
+// from OO terms like foo, foo.Bar, foo.Bar.Baz to SQL terms like
+// FOOS.ID, FOOS.BAR_ID, etc) and PathExpressionParser (which does much
+// the same thing it does now)
+
+public class WhereParser implements Parser {
+
+ private final PathExpressionParser pathExpressionParser;
+
+ {
+ pathExpressionParser = new PathExpressionParser();
+ pathExpressionParser.setUseThetaStyleJoin( true ); //Need this, since join condition can appear inside parens!
+ }
+
+ private static final Set EXPRESSION_TERMINATORS = new HashSet(); //tokens that close a sub expression
+ private static final Set EXPRESSION_OPENERS = new HashSet(); //tokens that open a sub expression
+ private static final Set BOOLEAN_OPERATORS = new HashSet(); //tokens that would indicate a sub expression is a boolean expression
+ private static final Map NEGATIONS = new HashMap();
+
+ static {
+ EXPRESSION_TERMINATORS.add( "and" );
+ EXPRESSION_TERMINATORS.add( "or" );
+ EXPRESSION_TERMINATORS.add( ")" );
+ //expressionTerminators.add(","); // deliberately excluded
+
+ EXPRESSION_OPENERS.add( "and" );
+ EXPRESSION_OPENERS.add( "or" );
+ EXPRESSION_OPENERS.add( "(" );
+ //expressionOpeners.add(","); // deliberately excluded
+
+ BOOLEAN_OPERATORS.add( "<" );
+ BOOLEAN_OPERATORS.add( "=" );
+ BOOLEAN_OPERATORS.add( ">" );
+ BOOLEAN_OPERATORS.add( "#" );
+ BOOLEAN_OPERATORS.add( "~" );
+ BOOLEAN_OPERATORS.add( "like" );
+ BOOLEAN_OPERATORS.add( "ilike" );
+ BOOLEAN_OPERATORS.add( "regexp" );
+ BOOLEAN_OPERATORS.add( "rlike" );
+ BOOLEAN_OPERATORS.add( "is" );
+ BOOLEAN_OPERATORS.add( "in" );
+ BOOLEAN_OPERATORS.add( "any" );
+ BOOLEAN_OPERATORS.add( "some" );
+ BOOLEAN_OPERATORS.add( "all" );
+ BOOLEAN_OPERATORS.add( "exists" );
+ BOOLEAN_OPERATORS.add( "between" );
+ BOOLEAN_OPERATORS.add( "<=" );
+ BOOLEAN_OPERATORS.add( ">=" );
+ BOOLEAN_OPERATORS.add( "=>" );
+ BOOLEAN_OPERATORS.add( "=<" );
+ BOOLEAN_OPERATORS.add( "!=" );
+ BOOLEAN_OPERATORS.add( "<>" );
+ BOOLEAN_OPERATORS.add( "!#" );
+ BOOLEAN_OPERATORS.add( "!~" );
+ BOOLEAN_OPERATORS.add( "!<" );
+ BOOLEAN_OPERATORS.add( "!>" );
+ BOOLEAN_OPERATORS.add( "is not" );
+ BOOLEAN_OPERATORS.add( "not like" );
+ BOOLEAN_OPERATORS.add( "not ilike" );
+ BOOLEAN_OPERATORS.add( "not regexp" );
+ BOOLEAN_OPERATORS.add( "not rlike" );
+ BOOLEAN_OPERATORS.add( "not in" );
+ BOOLEAN_OPERATORS.add( "not between" );
+ BOOLEAN_OPERATORS.add( "not exists" );
+
+ NEGATIONS.put( "and", "or" );
+ NEGATIONS.put( "or", "and" );
+ NEGATIONS.put( "<", ">=" );
+ NEGATIONS.put( "=", "<>" );
+ NEGATIONS.put( ">", "<=" );
+ NEGATIONS.put( "#", "!#" );
+ NEGATIONS.put( "~", "!~" );
+ NEGATIONS.put( "like", "not like" );
+ NEGATIONS.put( "ilike", "not ilike" );
+ NEGATIONS.put( "regexp", "not regexp" );
+ NEGATIONS.put( "rlike", "not rlike" );
+ NEGATIONS.put( "is", "is not" );
+ NEGATIONS.put( "in", "not in" );
+ NEGATIONS.put( "exists", "not exists" );
+ NEGATIONS.put( "between", "not between" );
+ NEGATIONS.put( "<=", ">" );
+ NEGATIONS.put( ">=", "<" );
+ NEGATIONS.put( "=>", "<" );
+ NEGATIONS.put( "=<", ">" );
+ NEGATIONS.put( "!=", "=" );
+ NEGATIONS.put( "<>", "=" );
+ NEGATIONS.put( "!#", "#" );
+ NEGATIONS.put( "!~", "~" );
+ NEGATIONS.put( "!<", "<" );
+ NEGATIONS.put( "!>", ">" );
+ NEGATIONS.put( "is not", "is" );
+ NEGATIONS.put( "not like", "like" );
+ NEGATIONS.put( "not ilike", "ilike" );
+ NEGATIONS.put( "not regexp", "regexp" );
+ NEGATIONS.put( "not rlike", "rlike" );
+ NEGATIONS.put( "not in", "in" );
+ NEGATIONS.put( "not between", "between" );
+ NEGATIONS.put( "not exists", "exists" );
+
+ }
+ // Handles things like:
+ // a and b or c
+ // a and ( b or c )
+ // not a and not b
+ // not ( a and b )
+ // x between y and z (overloaded "and")
+ // x in ( a, b, c ) (overloaded brackets)
+ // not not a
+ // a is not null (overloaded "not")
+ // etc......
+ // and expressions like
+ // foo = bar (maps to: foo.id = bar.id)
+ // foo.Bar = 'foo' (maps to: foo.bar = 'foo')
+ // foo.Bar.Baz = 1.0 (maps to: foo.bar = bar.id and bar.baz = 1.0)
+ // 1.0 = foo.Bar.Baz (maps to: bar.baz = 1.0 and foo.Bar = bar.id)
+ // foo.Bar.Baz = a.B.C (maps to: bar.Baz = b.C and foo.Bar = bar.id and a.B = b.id)
+ // foo.Bar.Baz + a.B.C (maps to: bar.Baz + b.C and foo.Bar = bar.id and a.B = b.id)
+ // ( foo.Bar.Baz + 1.0 ) < 2.0 (maps to: ( bar.Baz + 1.0 ) < 2.0 and foo.Bar = bar.id)
+
+ private boolean betweenSpecialCase = false; //Inside a BETWEEN ... AND ... expression
+ private boolean negated = false;
+
+ private boolean inSubselect = false;
+ private int bracketsSinceSelect = 0;
+ private StringBuffer subselect;
+
+ private boolean expectingPathContinuation = false;
+ private int expectingIndex = 0;
+
+ // The following variables are stacks that keep information about each subexpression
+ // in the list of nested subexpressions we are currently processing.
+
+ private LinkedList nots = new LinkedList(); //were an odd or even number of NOTs encountered
+ private LinkedList joins = new LinkedList(); //the join string built up by compound paths inside this expression
+ private LinkedList booleanTests = new LinkedList(); //a flag indicating if the subexpression is known to be boolean
+
+ private String getElementName(PathExpressionParser.CollectionElement element, QueryTranslatorImpl q) throws QueryException {
+ String name;
+ if ( element.isOneToMany ) {
+ name = element.alias;
+ }
+ else {
+ Type type = element.elementType;
+ if ( type.isEntityType() ) { //ie. a many-to-many
+ String entityName = ( ( EntityType ) type ).getAssociatedEntityName();
+ name = pathExpressionParser.continueFromManyToMany( entityName, element.elementColumns, q );
+ }
+ else {
+ throw new QueryException( "illegally dereferenced collection element" );
+ }
+ }
+ return name;
+ }
+
+ public void token(String token, QueryTranslatorImpl q) throws QueryException {
+
+ String lcToken = token.toLowerCase();
+
+ //Cope with [,]
+ if ( token.equals( "[" ) && !expectingPathContinuation ) {
+ expectingPathContinuation = false;
+ if ( expectingIndex == 0 ) throw new QueryException( "unexpected [" );
+ return;
+ }
+ else if ( token.equals( "]" ) ) {
+ expectingIndex--;
+ expectingPathContinuation = true;
+ return;
+ }
+
+ //Cope with a continued path expression (ie. ].baz)
+ if ( expectingPathContinuation ) {
+ boolean pathExpressionContinuesFurther = continuePathExpression( token, q );
+ if ( pathExpressionContinuesFurther ) return; //NOTE: early return
+ }
+
+ //Cope with a subselect
+ if ( !inSubselect && ( lcToken.equals( "select" ) || lcToken.equals( "from" ) ) ) {
+ inSubselect = true;
+ subselect = new StringBuffer( 20 );
+ }
+ if ( inSubselect && token.equals( ")" ) ) {
+ bracketsSinceSelect--;
+
+ if ( bracketsSinceSelect == -1 ) {
+ QueryTranslatorImpl subq = new QueryTranslatorImpl(
+ subselect.toString(),
+ q.getEnabledFilters(),
+ q.getFactory()
+ );
+ try {
+ subq.compile( q );
+ }
+ catch ( MappingException me ) {
+ throw new QueryException( "MappingException occurred compiling subquery", me );
+ }
+ appendToken( q, subq.getSQLString() );
+ inSubselect = false;
+ bracketsSinceSelect = 0;
+ }
+ }
+ if ( inSubselect ) {
+ if ( token.equals( "(" ) ) bracketsSinceSelect++;
+ subselect.append( token ).append( ' ' );
+ return;
+ }
+
+ //Cope with special cases of AND, NOT, ()
+ specialCasesBefore( lcToken );
+
+ //Close extra brackets we opened
+ if ( !betweenSpecialCase && EXPRESSION_TERMINATORS.contains( lcToken ) ) {
+ closeExpression( q, lcToken );
+ }
+
+ //take note when this is a boolean expression
+ if ( BOOLEAN_OPERATORS.contains( lcToken ) ) {
+ booleanTests.removeLast();
+ booleanTests.addLast( Boolean.TRUE );
+ }
+
+ if ( lcToken.equals( "not" ) ) {
+ nots.addLast( new Boolean( !( ( Boolean ) nots.removeLast() ).booleanValue() ) );
+ negated = !negated;
+ return; //NOTE: early return
+ }
+
+ //process a token, mapping OO path expressions to SQL expressions
+ doToken( token, q );
+
+ //Open any extra brackets we might need.
+ if ( !betweenSpecialCase && EXPRESSION_OPENERS.contains( lcToken ) ) {
+ openExpression( q, lcToken );
+ }
+
+ //Cope with special cases of AND, NOT, )
+ specialCasesAfter( lcToken );
+
+ }
+
+ public void start(QueryTranslatorImpl q) throws QueryException {
+ token( "(", q );
+ }
+
+ public void end(QueryTranslatorImpl q) throws QueryException {
+ if ( expectingPathContinuation ) {
+ expectingPathContinuation = false;
+ PathExpressionParser.CollectionElement element = pathExpressionParser.lastCollectionElement();
+ if ( element.elementColumns.length != 1 ) throw new QueryException( "path expression ended in composite collection element" );
+ appendToken( q, element.elementColumns[0] );
+ addToCurrentJoin( element );
+ }
+ token( ")", q );
+ }
+
+ private void closeExpression(QueryTranslatorImpl q, String lcToken) {
+ if ( ( ( Boolean ) booleanTests.removeLast() ).booleanValue() ) { //it was a boolean expression
+
+ if ( booleanTests.size() > 0 ) {
+ // the next one up must also be
+ booleanTests.removeLast();
+ booleanTests.addLast( Boolean.TRUE );
+ }
+
+ // Add any joins
+ appendToken( q, ( joins.removeLast() ).toString() );
+
+ }
+ else {
+ StringBuffer join = ( StringBuffer ) joins.removeLast();
+ ( ( StringBuffer ) joins.getLast() ).append( join.toString() );
+ }
+
+ if ( ( ( Boolean ) nots.removeLast() ).booleanValue() ) negated = !negated;
+
+ if ( !")".equals( lcToken ) ) appendToken( q, ")" );
+ }
+
+ private void openExpression(QueryTranslatorImpl q, String lcToken) {
+ nots.addLast( Boolean.FALSE );
+ booleanTests.addLast( Boolean.FALSE );
+ joins.addLast( new StringBuffer() );
+ if ( !"(".equals( lcToken ) ) appendToken( q, "(" );
+ }
+
+ private void preprocess(String token, QueryTranslatorImpl q) throws QueryException {
+ // ugly hack for cases like "elements(foo.bar.collection)"
+ // (multi-part path expression ending in elements or indices)
+ String[] tokens = StringHelper.split( ".", token, true );
+ if (
+ tokens.length > 5 &&
+ ( CollectionPropertyNames.COLLECTION_ELEMENTS.equals( tokens[tokens.length - 1] )
+ || CollectionPropertyNames.COLLECTION_INDICES.equals( tokens[tokens.length - 1] ) )
+ ) {
+ pathExpressionParser.start( q );
+ for ( int i = 0; i < tokens.length - 3; i++ ) {
+ pathExpressionParser.token( tokens[i], q );
+ }
+ pathExpressionParser.token( null, q );
+ pathExpressionParser.end( q );
+ addJoin( pathExpressionParser.getWhereJoin(), q );
+ pathExpressionParser.ignoreInitialJoin();
+ }
+ }
+
+ private void doPathExpression(String token, QueryTranslatorImpl q) throws QueryException {
+
+ preprocess( token, q );
+
+ StringTokenizer tokens = new StringTokenizer( token, ".", true );
+ pathExpressionParser.start( q );
+ while ( tokens.hasMoreTokens() ) {
+ pathExpressionParser.token( tokens.nextToken(), q );
+ }
+ pathExpressionParser.end( q );
+ if ( pathExpressionParser.isCollectionValued() ) {
+ openExpression( q, "" );
+ appendToken( q, pathExpressionParser.getCollectionSubquery( q.getEnabledFilters() ) );
+ closeExpression( q, "" );
+ // this is ugly here, but needed because its a subquery
+ q.addQuerySpaces( q.getCollectionPersister( pathExpressionParser.getCollectionRole() ).getCollectionSpaces() );
+ }
+ else {
+ if ( pathExpressionParser.isExpectingCollectionIndex() ) {
+ expectingIndex++;
+ }
+ else {
+ addJoin( pathExpressionParser.getWhereJoin(), q );
+ appendToken( q, pathExpressionParser.getWhereColumn() );
+ }
+ }
+ }
+
+ private void addJoin(JoinSequence joinSequence, QueryTranslatorImpl q) throws QueryException {
+ //JoinFragment fromClause = q.createJoinFragment(true);
+ //fromClause.addJoins( join.toJoinFragment().toFromFragmentString(), StringHelper.EMPTY_STRING );
+ q.addFromJoinOnly( pathExpressionParser.getName(), joinSequence );
+ try {
+ addToCurrentJoin( joinSequence.toJoinFragment( q.getEnabledFilters(), true ).toWhereFragmentString() );
+ }
+ catch ( MappingException me ) {
+ throw new QueryException( me );
+ }
+ }
+
+ private void doToken(String token, QueryTranslatorImpl q) throws QueryException {
+ if ( q.isName( StringHelper.root( token ) ) ) { //path expression
+ doPathExpression( q.unalias( token ), q );
+ }
+ else if ( token.startsWith( ParserHelper.HQL_VARIABLE_PREFIX ) ) { //named query parameter
+ q.addNamedParameter( token.substring( 1 ) );
+ appendToken( q, "?" );
+ }
+ else {
+ Queryable persister = q.getEntityPersisterUsingImports( token );
+ if ( persister != null ) { // the name of a class
+ final String discrim = persister.getDiscriminatorSQLValue();
+ if ( InFragment.NULL.equals(discrim) || InFragment.NOT_NULL.equals(discrim) ) {
+ throw new QueryException( "subclass test not allowed for null or not null discriminator" );
+ }
+ else {
+ appendToken( q, discrim );
+ }
+ }
+ else {
+ Object constant;
+ if (
+ token.indexOf( '.' ) > -1 &&
+ ( constant = ReflectHelper.getConstantValue( token ) ) != null
+ ) {
+ Type type;
+ try {
+ type = TypeFactory.heuristicType( constant.getClass().getName() );
+ }
+ catch ( MappingException me ) {
+ throw new QueryException( me );
+ }
+ if ( type == null ) throw new QueryException( QueryTranslator.ERROR_CANNOT_DETERMINE_TYPE + token );
+ try {
+ appendToken( q, ( ( LiteralType ) type ).objectToSQLString( constant, q.getFactory().getDialect() ) );
+ }
+ catch ( Exception e ) {
+ throw new QueryException( QueryTranslator.ERROR_CANNOT_FORMAT_LITERAL + token, e );
+ }
+ }
+ else { //anything else
+
+ String negatedToken = negated ? ( String ) NEGATIONS.get( token.toLowerCase() ) : null;
+ if ( negatedToken != null && ( !betweenSpecialCase || !"or".equals( negatedToken ) ) ) {
+ appendToken( q, negatedToken );
+ }
+ else {
+ appendToken( q, token );
+ }
+ }
+ }
+ }
+ }
+
+ private void addToCurrentJoin(String sql) {
+ ( ( StringBuffer ) joins.getLast() ).append( sql );
+ }
+
+ private void addToCurrentJoin(PathExpressionParser.CollectionElement ce)
+ throws QueryException {
+ try {
+ addToCurrentJoin( ce.joinSequence.toJoinFragment().toWhereFragmentString() + ce.indexValue.toString() );
+ }
+ catch ( MappingException me ) {
+ throw new QueryException( me );
+ }
+ }
+
+ private void specialCasesBefore(String lcToken) {
+ if ( lcToken.equals( "between" ) || lcToken.equals( "not between" ) ) {
+ betweenSpecialCase = true;
+ }
+ }
+
+ private void specialCasesAfter(String lcToken) {
+ if ( betweenSpecialCase && lcToken.equals( "and" ) ) {
+ betweenSpecialCase = false;
+ }
+ }
+
+ void appendToken(QueryTranslatorImpl q, String token) {
+ if ( expectingIndex > 0 ) {
+ pathExpressionParser.setLastCollectionElementIndexValue( token );
+ }
+ else {
+ q.appendWhereToken( token );
+ }
+ }
+
+ private boolean continuePathExpression(String token, QueryTranslatorImpl q) throws QueryException {
+
+ expectingPathContinuation = false;
+
+ PathExpressionParser.CollectionElement element = pathExpressionParser.lastCollectionElement();
+
+ if ( token.startsWith( "." ) ) { // the path expression continues after a ]
+
+ doPathExpression( getElementName( element, q ) + token, q ); // careful with this!
+
+ addToCurrentJoin( element );
+ return true; //NOTE: EARLY EXIT!
+
+ }
+
+ else { // the path expression ends at the ]
+ if ( element.elementColumns.length != 1 ) {
+ throw new QueryException( "path expression ended in composite collection element" );
+ }
+ appendToken( q, element.elementColumns[0] );
+ addToCurrentJoin( element );
+ return false;
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,9 @@
+<html>
+<head></head>
+<body>
+<p>
+ This package contains the Hibernate 2.x query parser which
+ is being end-of-lifed.
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/classic/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/hql/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/hql/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,10 @@
+<html>
+<head></head>
+<body>
+<p>
+ This package defines the interface between Hibernate and
+ the HQL query parser implementation (to allow switching
+ between the 2.x and 3.0 HQL parsers).
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/hql/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/id/AbstractPostInsertGenerator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/id/AbstractPostInsertGenerator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/id/AbstractPostInsertGenerator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,15 @@
+//$Id: AbstractPostInsertGenerator.java 9681 2006-03-24 18:10:04Z steve.ebersole at jboss.com $
+package org.hibernate.id;
+
+import java.io.Serializable;
+
+import org.hibernate.engine.SessionImplementor;
+
+/**
+ * @author Gavin King
+ */
+public abstract class AbstractPostInsertGenerator implements PostInsertIdentifierGenerator{
+ public Serializable generate(SessionImplementor s, Object obj) {
+ return IdentifierGeneratorFactory.POST_INSERT_INDICATOR;
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/id/AbstractPostInsertGenerator.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/id/AbstractUUIDGenerator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/id/AbstractUUIDGenerator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/id/AbstractUUIDGenerator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,77 @@
+//$Id: AbstractUUIDGenerator.java 5002 2004-12-19 16:25:07Z oneovthafew $
+package org.hibernate.id;
+
+import java.net.InetAddress;
+import org.hibernate.util.BytesHelper;
+
+/**
+ * The base class for identifier generators that use a UUID algorithm. This
+ * class implements the algorithm, subclasses define the identifier
+ * format.
+ *
+ * @see UUIDHexGenerator
+ * @author Gavin King
+ */
+
+public abstract class AbstractUUIDGenerator implements IdentifierGenerator {
+
+ private static final int IP;
+ static {
+ int ipadd;
+ try {
+ ipadd = BytesHelper.toInt( InetAddress.getLocalHost().getAddress() );
+ }
+ catch (Exception e) {
+ ipadd = 0;
+ }
+ IP = ipadd;
+ }
+ private static short counter = (short) 0;
+ private static final int JVM = (int) ( System.currentTimeMillis() >>> 8 );
+
+ public AbstractUUIDGenerator() {
+ }
+
+ /**
+ * Unique across JVMs on this machine (unless they load this class
+ * in the same quater second - very unlikely)
+ */
+ protected int getJVM() {
+ return JVM;
+ }
+
+ /**
+ * Unique in a millisecond for this JVM instance (unless there
+ * are > Short.MAX_VALUE instances created in a millisecond)
+ */
+ protected short getCount() {
+ synchronized(AbstractUUIDGenerator.class) {
+ if (counter<0) counter=0;
+ return counter++;
+ }
+ }
+
+ /**
+ * Unique in a local network
+ */
+ protected int getIP() {
+ return IP;
+ }
+
+ /**
+ * Unique down to millisecond
+ */
+ protected short getHiTime() {
+ return (short) ( System.currentTimeMillis() >>> 32 );
+ }
+ protected int getLoTime() {
+ return (int) System.currentTimeMillis();
+ }
+
+
+}
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/id/Assigned.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/id/Assigned.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/id/Assigned.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,55 @@
+//$Id: Assigned.java 6914 2005-05-26 03:58:24Z oneovthafew $
+package org.hibernate.id;
+
+import java.io.Serializable;
+import java.util.Properties;
+
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.type.Type;
+
+/**
+ * <b>assigned</b><br>
+ * <br>
+ * An <tt>IdentifierGenerator</tt> that returns the current identifier assigned
+ * to an instance.
+ *
+ * @author Gavin King
+ */
+
+public class Assigned implements IdentifierGenerator, Configurable {
+
+ private String entityName;
+
+ public Serializable generate(SessionImplementor session, Object obj) throws HibernateException {
+
+ final Serializable id = session.getEntityPersister( entityName, obj )
+ //TODO: cache the persister, this shows up in yourkit
+ .getIdentifier( obj, session.getEntityMode() );
+
+ if (id==null) {
+ throw new IdentifierGenerationException(
+ "ids for this class must be manually assigned before calling save(): " +
+ entityName
+ );
+ }
+
+ return id;
+ }
+
+ public void configure(Type type, Properties params, Dialect d)
+ throws MappingException {
+ entityName = params.getProperty(ENTITY_NAME);
+ if (entityName==null) {
+ throw new MappingException("no entity name");
+ }
+ }
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/id/Configurable.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/id/Configurable.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/id/Configurable.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,27 @@
+//$Id: Configurable.java 3890 2004-06-03 16:31:32Z steveebersole $
+package org.hibernate.id;
+
+import java.util.Properties;
+
+import org.hibernate.MappingException;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.type.Type;
+
+/**
+ * An <tt>IdentifierGenerator</tt> that supports "configuration".
+ *
+ * @see IdentifierGenerator
+ * @author Gavin King
+ */
+public interface Configurable {
+
+ /**
+ * Configure this instance, given the value of parameters
+ * specified by the user as <tt><param></tt> elements.
+ * This method is called just once, following instantiation.
+ *
+ * @param params param values, keyed by parameter name
+ */
+ public void configure(Type type, Properties params, Dialect d) throws MappingException;
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/id/ForeignGenerator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/id/ForeignGenerator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/id/ForeignGenerator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,87 @@
+package org.hibernate.id;
+
+import java.io.Serializable;
+import java.util.Properties;
+
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.Session;
+import org.hibernate.TransientObjectException;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.ForeignKeys;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.type.EntityType;
+import org.hibernate.type.Type;
+
+/**
+ * <b>foreign</b><br>
+ * <br>
+ * An <tt>Identifier</tt> generator that uses the value of the id property of an
+ * associated object<br>
+ * <br>
+ * One mapping parameter is required: property.
+ *
+ * @author Gavin King
+ */
+public class ForeignGenerator implements IdentifierGenerator, Configurable {
+
+ private String propertyName;
+ private String entityName;
+
+ /**
+ * @see org.hibernate.id.IdentifierGenerator#generate(org.hibernate.engine.SessionImplementor, java.lang.Object)
+ */
+ public Serializable generate(SessionImplementor sessionImplementor, Object object)
+ throws HibernateException {
+
+ Session session = (Session) sessionImplementor;
+
+ Object associatedObject = sessionImplementor.getFactory()
+ .getClassMetadata( entityName )
+ .getPropertyValue( object, propertyName, session.getEntityMode() );
+
+ if ( associatedObject == null ) {
+ throw new IdentifierGenerationException(
+ "attempted to assign id from null one-to-one property: " +
+ propertyName
+ );
+ }
+
+ EntityType type = (EntityType) sessionImplementor.getFactory()
+ .getClassMetadata( entityName )
+ .getPropertyType( propertyName );
+
+ Serializable id;
+ try {
+ id = ForeignKeys.getEntityIdentifierIfNotUnsaved(
+ type.getAssociatedEntityName(),
+ associatedObject,
+ sessionImplementor
+ );
+ }
+ catch (TransientObjectException toe) {
+ id = session.save( type.getAssociatedEntityName(), associatedObject );
+ }
+
+ if ( session.contains(object) ) {
+ //abort the save (the object is already saved by a circular cascade)
+ return IdentifierGeneratorFactory.SHORT_CIRCUIT_INDICATOR;
+ //throw new IdentifierGenerationException("save associated object first, or disable cascade for inverse association");
+ }
+ return id;
+ }
+
+ /**
+ * @see org.hibernate.id.Configurable#configure(org.hibernate.type.Type, java.util.Properties, org.hibernate.dialect.Dialect)
+ */
+ public void configure(Type type, Properties params, Dialect d)
+ throws MappingException {
+
+ propertyName = params.getProperty("property");
+ entityName = params.getProperty(ENTITY_NAME);
+ if (propertyName==null) throw new MappingException(
+ "param named \"property\" is required for foreign id generation strategy"
+ );
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/id/GUIDGenerator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/id/GUIDGenerator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/id/GUIDGenerator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,57 @@
+//$Id: GUIDGenerator.java 7265 2005-06-22 04:19:34Z oneovthafew $
+package org.hibernate.id;
+
+import java.io.Serializable;
+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.HibernateException;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.exception.JDBCExceptionHelper;
+
+/**
+ * Generates <tt>string</tt> values using the SQL Server NEWID() function.
+ *
+ * @author Joseph Fifield
+ */
+public class GUIDGenerator implements IdentifierGenerator {
+
+ private static final Log log = LogFactory.getLog(GUIDGenerator.class);
+
+ public Serializable generate(SessionImplementor session, Object obj)
+ throws HibernateException {
+
+ final String sql = session.getFactory().getDialect().getSelectGUIDString();
+ try {
+ PreparedStatement st = session.getBatcher().prepareSelectStatement(sql);
+ try {
+ ResultSet rs = st.executeQuery();
+ final String result;
+ try {
+ rs.next();
+ result = rs.getString(1);
+ }
+ finally {
+ rs.close();
+ }
+ log.debug("GUID identifier generated: " + result);
+ return result;
+ }
+ finally {
+ session.getBatcher().closeStatement(st);
+ }
+ }
+ catch (SQLException sqle) {
+ throw JDBCExceptionHelper.convert(
+ session.getFactory().getSQLExceptionConverter(),
+ sqle,
+ "could not retrieve GUID",
+ sql
+ );
+ }
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/id/IdentifierGenerationException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/id/IdentifierGenerationException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/id/IdentifierGenerationException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,30 @@
+//$Id: IdentifierGenerationException.java 3890 2004-06-03 16:31:32Z steveebersole $
+package org.hibernate.id;
+
+import org.hibernate.HibernateException;
+
+/**
+ * Thrown by <tt>IdentifierGenerator</tt> implementation class when
+ * ID generation fails.
+ *
+ * @see IdentifierGenerator
+ * @author Gavin King
+ */
+
+public class IdentifierGenerationException extends HibernateException {
+
+ public IdentifierGenerationException(String msg) {
+ super(msg);
+ }
+
+ public IdentifierGenerationException(String msg, Throwable t) {
+ super(msg, t);
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/id/IdentifierGenerator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/id/IdentifierGenerator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/id/IdentifierGenerator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,45 @@
+//$Id: IdentifierGenerator.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate.id;
+
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionImplementor;
+
+import java.io.Serializable;
+
+/**
+ * The general contract between a class that generates unique
+ * identifiers and the <tt>Session</tt>. It is not intended that
+ * this interface ever be exposed to the application. It <b>is</b>
+ * intended that users implement this interface to provide
+ * custom identifier generation strategies.<br>
+ * <br>
+ * Implementors should provide a public default constructor.<br>
+ * <br>
+ * Implementations that accept configuration parameters should
+ * also implement <tt>Configurable</tt>.
+ * <br>
+ * Implementors <em>must</em> be threadsafe
+ *
+ * @author Gavin King
+ * @see PersistentIdentifierGenerator
+ * @see Configurable
+ */
+public interface IdentifierGenerator {
+
+ /**
+ * The configuration parameter holding the entity name
+ */
+ public static final String ENTITY_NAME = "entity_name";
+
+ /**
+ * Generate a new identifier.
+ * @param session
+ * @param object the entity or toplevel collection for which the id is being generated
+ *
+ * @return a new identifier
+ * @throws HibernateException
+ */
+ public Serializable generate(SessionImplementor session, Object object)
+ throws HibernateException;
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/id/IdentifierGeneratorFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/id/IdentifierGeneratorFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/id/IdentifierGeneratorFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,131 @@
+//$Id: IdentifierGeneratorFactory.java 9686 2006-03-27 16:47:06Z steve.ebersole at jboss.com $
+package org.hibernate.id;
+
+import java.io.Serializable;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.Properties;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.type.Type;
+import org.hibernate.util.ReflectHelper;
+
+/**
+ * Factory and helper methods for <tt>IdentifierGenerator</tt> framework.
+ *
+ * @author Gavin King
+ */
+public final class IdentifierGeneratorFactory {
+
+ private static final Log log = LogFactory.getLog(IdentifierGeneratorFactory.class);
+
+ /**
+ * Get the generated identifier when using identity columns
+ */
+ public static Serializable getGeneratedIdentity(ResultSet rs, Type type)
+ throws SQLException, HibernateException, IdentifierGenerationException {
+ if ( !rs.next() ) {
+ throw new HibernateException( "The database returned no natively generated identity value" );
+ }
+ final Serializable id = IdentifierGeneratorFactory.get( rs, type );
+
+ if ( log.isDebugEnabled() ) log.debug( "Natively generated identity: " + id );
+ return id;
+ }
+
+ // unhappy about this being public ... is there a better way?
+ public static Serializable get(ResultSet rs, Type type)
+ throws SQLException, IdentifierGenerationException {
+
+ Class clazz = type.getReturnedClass();
+ if ( clazz==Long.class ) {
+ return new Long( rs.getLong(1) );
+ }
+ else if ( clazz==Integer.class ) {
+ return new Integer( rs.getInt(1) );
+ }
+ else if ( clazz==Short.class ) {
+ return new Short( rs.getShort(1) );
+ }
+ else if ( clazz==String.class ) {
+ return rs.getString(1);
+ }
+ else {
+ throw new IdentifierGenerationException("this id generator generates long, integer, short or string");
+ }
+
+ }
+
+ private static final HashMap GENERATORS = new HashMap();
+
+ public static final Serializable SHORT_CIRCUIT_INDICATOR = new Serializable() {
+ public String toString() { return "SHORT_CIRCUIT_INDICATOR"; }
+ };
+
+ public static final Serializable POST_INSERT_INDICATOR = new Serializable() {
+ public String toString() { return "POST_INSERT_INDICATOR"; }
+ };
+
+ static {
+ GENERATORS.put("uuid", UUIDHexGenerator.class);
+ GENERATORS.put("hilo", TableHiLoGenerator.class);
+ GENERATORS.put("assigned", Assigned.class);
+ GENERATORS.put("identity", IdentityGenerator.class);
+ GENERATORS.put("select", SelectGenerator.class);
+ GENERATORS.put("sequence", SequenceGenerator.class);
+ GENERATORS.put("seqhilo", SequenceHiLoGenerator.class);
+ GENERATORS.put("increment", IncrementGenerator.class);
+ GENERATORS.put("foreign", ForeignGenerator.class);
+ GENERATORS.put("guid", GUIDGenerator.class);
+ GENERATORS.put("uuid.hex", UUIDHexGenerator.class); //uuid.hex is deprecated
+ GENERATORS.put("sequence-identity", SequenceIdentityGenerator.class);
+ }
+
+ public static IdentifierGenerator create(String strategy, Type type, Properties params, Dialect dialect)
+ throws MappingException {
+ try {
+ Class clazz = getIdentifierGeneratorClass( strategy, dialect );
+ IdentifierGenerator idgen = (IdentifierGenerator) clazz.newInstance();
+ if (idgen instanceof Configurable) ( (Configurable) idgen).configure(type, params, dialect);
+ return idgen;
+ }
+ catch (Exception e) {
+ throw new MappingException("could not instantiate id generator", e);
+ }
+ }
+
+ public static Class getIdentifierGeneratorClass(String strategy, Dialect dialect) {
+ Class clazz = (Class) GENERATORS.get(strategy);
+ if ( "native".equals(strategy) ) clazz = dialect.getNativeIdentifierGeneratorClass();
+ try {
+ if (clazz==null) clazz = ReflectHelper.classForName(strategy);
+ }
+ catch (ClassNotFoundException e) {
+ throw new MappingException("could not interpret id generator strategy: " + strategy);
+ }
+ return clazz;
+ }
+
+ public static Number createNumber(long value, Class clazz) throws IdentifierGenerationException {
+ if ( clazz==Long.class ) {
+ return new Long(value);
+ }
+ else if ( clazz==Integer.class ) {
+ return new Integer( (int) value );
+ }
+ else if ( clazz==Short.class ) {
+ return new Short( (short) value );
+ }
+ else {
+ throw new IdentifierGenerationException("this id generator generates long, integer, short");
+ }
+ }
+
+ private IdentifierGeneratorFactory() {} //cannot be instantiated
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/id/IdentityGenerator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/id/IdentityGenerator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/id/IdentityGenerator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,161 @@
+//$Id: IdentityGenerator.java 9681 2006-03-24 18:10:04Z steve.ebersole at jboss.com $
+package org.hibernate.id;
+
+import java.io.Serializable;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.PreparedStatement;
+
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
+import org.hibernate.id.insert.IdentifierGeneratingInsert;
+import org.hibernate.id.insert.AbstractSelectingDelegate;
+import org.hibernate.id.insert.AbstractReturningDelegate;
+import org.hibernate.id.insert.InsertSelectIdentityInsert;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.HibernateException;
+import org.hibernate.AssertionFailure;
+import org.hibernate.util.GetGeneratedKeysHelper;
+
+
+/**
+ * A generator for use with ANSI-SQL IDENTITY columns used as the primary key.
+ * The IdentityGenerator for autoincrement/identity key generation.
+ * <br><br>
+ * Indicates to the <tt>Session</tt> that identity (ie. identity/autoincrement
+ * column) key generation should be used.
+ *
+ * @author Christoph Sturm
+ */
+public class IdentityGenerator extends AbstractPostInsertGenerator {
+
+ public InsertGeneratedIdentifierDelegate getInsertGeneratedIdentifierDelegate(
+ PostInsertIdentityPersister persister,
+ Dialect dialect,
+ boolean isGetGeneratedKeysEnabled) throws HibernateException {
+ if ( isGetGeneratedKeysEnabled ) {
+ return new GetGeneratedKeysDelegate( persister, dialect );
+ }
+ else if ( dialect.supportsInsertSelectIdentity() ) {
+ return new InsertSelectDelegate( persister, dialect );
+ }
+ else {
+ return new BasicDelegate( persister, dialect );
+ }
+ }
+
+ /**
+ * Delegate for dealing with IDENTITY columns using JDBC3 getGeneratedKeys
+ */
+ public static class GetGeneratedKeysDelegate
+ extends AbstractReturningDelegate
+ implements InsertGeneratedIdentifierDelegate {
+ private final PostInsertIdentityPersister persister;
+ private final Dialect dialect;
+
+ public GetGeneratedKeysDelegate(PostInsertIdentityPersister persister, Dialect dialect) {
+ super( persister );
+ this.persister = persister;
+ this.dialect = dialect;
+ }
+
+ public IdentifierGeneratingInsert prepareIdentifierGeneratingInsert() {
+ IdentifierGeneratingInsert insert = new IdentifierGeneratingInsert( dialect );
+ insert.addIdentityColumn( persister.getRootTableKeyColumnNames()[0] );
+ return insert;
+ }
+
+ protected PreparedStatement prepare(String insertSQL, SessionImplementor session) throws SQLException {
+ return session.getBatcher().prepareStatement( insertSQL, true );
+ }
+
+ public Serializable executeAndExtract(PreparedStatement insert) throws SQLException {
+ insert.executeUpdate();
+ return IdentifierGeneratorFactory.getGeneratedIdentity(
+ GetGeneratedKeysHelper.getGeneratedKey( insert ),
+ persister.getIdentifierType()
+ );
+ }
+ }
+
+ /**
+ * Delegate for dealing with IDENTITY columns where the dialect supports returning
+ * the generated IDENTITY value directly from the insert statement.
+ */
+ public static class InsertSelectDelegate
+ extends AbstractReturningDelegate
+ implements InsertGeneratedIdentifierDelegate {
+ private final PostInsertIdentityPersister persister;
+ private final Dialect dialect;
+
+ public InsertSelectDelegate(PostInsertIdentityPersister persister, Dialect dialect) {
+ super( persister );
+ this.persister = persister;
+ this.dialect = dialect;
+ }
+
+ public IdentifierGeneratingInsert prepareIdentifierGeneratingInsert() {
+ InsertSelectIdentityInsert insert = new InsertSelectIdentityInsert( dialect );
+ insert.addIdentityColumn( persister.getRootTableKeyColumnNames()[0] );
+ return insert;
+ }
+
+ protected PreparedStatement prepare(String insertSQL, SessionImplementor session) throws SQLException {
+ return session.getBatcher().prepareStatement( insertSQL, false );
+ }
+
+ public Serializable executeAndExtract(PreparedStatement insert) throws SQLException {
+ if ( !insert.execute() ) {
+ while ( !insert.getMoreResults() && insert.getUpdateCount() != -1 ) {
+ // do nothing until we hit the rsult set containing the generated id
+ }
+ }
+ ResultSet rs = insert.getResultSet();
+ try {
+ return IdentifierGeneratorFactory.getGeneratedIdentity( rs, persister.getIdentifierType() );
+ }
+ finally {
+ rs.close();
+ }
+ }
+
+ public Serializable determineGeneratedIdentifier(SessionImplementor session, Object entity) {
+ throw new AssertionFailure( "insert statement returns generated value" );
+ }
+ }
+
+ /**
+ * Delegate for dealing with IDENTITY columns where the dialect requires an
+ * additional command execution to retrieve the generated IDENTITY value
+ */
+ public static class BasicDelegate
+ extends AbstractSelectingDelegate
+ implements InsertGeneratedIdentifierDelegate {
+ private final PostInsertIdentityPersister persister;
+ private final Dialect dialect;
+
+ public BasicDelegate(PostInsertIdentityPersister persister, Dialect dialect) {
+ super( persister );
+ this.persister = persister;
+ this.dialect = dialect;
+ }
+
+ public IdentifierGeneratingInsert prepareIdentifierGeneratingInsert() {
+ IdentifierGeneratingInsert insert = new IdentifierGeneratingInsert( dialect );
+ insert.addIdentityColumn( persister.getRootTableKeyColumnNames()[0] );
+ return insert;
+ }
+
+ protected String getSelectSQL() {
+ return persister.getIdentitySelectString();
+ }
+
+ protected Serializable getResult(
+ SessionImplementor session,
+ ResultSet rs,
+ Object object) throws SQLException {
+ return IdentifierGeneratorFactory.getGeneratedIdentity( rs, persister.getIdentifierType() );
+ }
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/id/IncrementGenerator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/id/IncrementGenerator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/id/IncrementGenerator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,116 @@
+package org.hibernate.id;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.exception.JDBCExceptionHelper;
+import org.hibernate.mapping.Table;
+import org.hibernate.type.Type;
+import org.hibernate.util.StringHelper;
+
+/**
+ * <b>increment</b><br>
+ * <br>
+ * An <tt>IdentifierGenerator</tt> that returns a <tt>long</tt>, constructed by
+ * counting from the maximum primary key value at startup. Not safe for use in a
+ * cluster!<br>
+ * <br>
+ * Mapping parameters supported, but not usually needed: tables, column.
+ * (The tables parameter specified a comma-separated list of table names.)
+ *
+ * @author Gavin King
+ */
+public class IncrementGenerator implements IdentifierGenerator, Configurable {
+
+ private static final Log log = LogFactory.getLog(IncrementGenerator.class);
+
+ private long next;
+ private String sql;
+ private Class returnClass;
+
+ public synchronized Serializable generate(SessionImplementor session, Object object)
+ throws HibernateException {
+
+ if (sql!=null) {
+ getNext( session );
+ }
+ return IdentifierGeneratorFactory.createNumber(next++, returnClass);
+ }
+
+ public void configure(Type type, Properties params, Dialect dialect)
+ throws MappingException {
+
+ String tableList = params.getProperty("tables");
+ if (tableList==null) tableList = params.getProperty(PersistentIdentifierGenerator.TABLES);
+ String[] tables = StringHelper.split(", ", tableList);
+ String column = params.getProperty("column");
+ if (column==null) column = params.getProperty(PersistentIdentifierGenerator.PK);
+ String schema = params.getProperty(PersistentIdentifierGenerator.SCHEMA);
+ String catalog = params.getProperty(PersistentIdentifierGenerator.CATALOG);
+ returnClass = type.getReturnedClass();
+
+
+ StringBuffer buf = new StringBuffer();
+ for ( int i=0; i<tables.length; i++ ) {
+ if (tables.length>1) {
+ buf.append("select ").append(column).append(" from ");
+ }
+ buf.append( Table.qualify( catalog, schema, tables[i] ) );
+ if ( i<tables.length-1) buf.append(" union ");
+ }
+ if (tables.length>1) {
+ buf.insert(0, "( ").append(" ) ids_");
+ column = "ids_." + column;
+ }
+
+ sql = "select max(" + column + ") from " + buf.toString();
+ }
+
+ private void getNext( SessionImplementor session ) {
+
+ log.debug("fetching initial value: " + sql);
+
+ try {
+ PreparedStatement st = session.getBatcher().prepareSelectStatement(sql);
+ try {
+ ResultSet rs = st.executeQuery();
+ try {
+ if ( rs.next() ) {
+ next = rs.getLong(1) + 1;
+ if ( rs.wasNull() ) next = 1;
+ }
+ else {
+ next = 1;
+ }
+ sql=null;
+ log.debug("first free id: " + next);
+ }
+ finally {
+ rs.close();
+ }
+ }
+ finally {
+ session.getBatcher().closeStatement(st);
+ }
+
+ }
+ catch (SQLException sqle) {
+ throw JDBCExceptionHelper.convert(
+ session.getFactory().getSQLExceptionConverter(),
+ sqle,
+ "could not fetch initial value for increment generator",
+ sql
+ );
+ }
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/id/MultipleHiLoPerTableGenerator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/id/MultipleHiLoPerTableGenerator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/id/MultipleHiLoPerTableGenerator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,241 @@
+//$Id: MultipleHiLoPerTableGenerator.java 11320 2007-03-20 11:50:53Z steve.ebersole at jboss.com $
+package org.hibernate.id;
+
+import java.io.Serializable;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.Properties;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.HibernateException;
+import org.hibernate.LockMode;
+import org.hibernate.MappingException;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.TransactionHelper;
+import org.hibernate.mapping.Table;
+import org.hibernate.type.Type;
+import org.hibernate.util.PropertiesHelper;
+
+/**
+ *
+ * A hilo <tt>IdentifierGenerator</tt> that returns a <tt>Long</tt>, constructed using
+ * a hi/lo algorithm. The hi value MUST be fetched in a seperate transaction
+ * to the <tt>Session</tt> transaction so the generator must be able to obtain
+ * a new connection and commit it. Hence this implementation may not
+ * be used when the user is supplying connections. In this
+ * case a <tt>SequenceHiLoGenerator</tt> would be a better choice (where
+ * supported).<br>
+ * <br>
+ *
+ * A hilo <tt>IdentifierGenerator</tt> that uses a database
+ * table to store the last generated values. A table can contains
+ * several hi values. They are distinct from each other through a key
+ * <p/>
+ * <p>This implementation is not compliant with a user connection</p>
+ * <p/>
+ *
+ * <p>Allowed parameters (all of them are optional):</p>
+ * <ul>
+ * <li>table: table name (default <tt>hibernate_sequences</tt>)</li>
+ * <li>primary_key_column: key column name (default <tt>sequence_name</tt>)</li>
+ * <li>value_column: hi value column name(default <tt>sequence_next_hi_value</tt>)</li>
+ * <li>primary_key_value: key value for the current entity (default to the entity's primary table name)</li>
+ * <li>primary_key_length: length of the key column in DB represented as a varchar (default to 255)</li>
+ * <li>max_lo: max low value before increasing hi (default to Short.MAX_VALUE)</li>
+ * </ul>
+ *
+ * @author Emmanuel Bernard
+ * @author <a href="mailto:kr at hbt.de">Klaus Richarz</a>.
+ */
+public class MultipleHiLoPerTableGenerator
+ extends TransactionHelper
+ implements PersistentIdentifierGenerator, Configurable {
+
+ private static final Log log = LogFactory.getLog(MultipleHiLoPerTableGenerator.class);
+
+ public static final String ID_TABLE = "table";
+ public static final String PK_COLUMN_NAME = "primary_key_column";
+ public static final String PK_VALUE_NAME = "primary_key_value";
+ public static final String VALUE_COLUMN_NAME = "value_column";
+ public static final String PK_LENGTH_NAME = "primary_key_length";
+
+ private static final int DEFAULT_PK_LENGTH = 255;
+ public static final String DEFAULT_TABLE = "hibernate_sequences";
+ private static final String DEFAULT_PK_COLUMN = "sequence_name";
+ private static final String DEFAULT_VALUE_COLUMN = "sequence_next_hi_value";
+
+ private String tableName;
+ private String pkColumnName;
+ private String valueColumnName;
+ private String query;
+ private String insert;
+ private String update;
+
+ //hilo params
+ public static final String MAX_LO = "max_lo";
+
+ private long hi;
+ private int lo;
+ private int maxLo;
+ private Class returnClass;
+ private int keySize;
+
+
+ public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
+ return new String[] {
+ new StringBuffer( dialect.getCreateTableString() )
+ .append( ' ' )
+ .append( tableName )
+ .append( " ( " )
+ .append( pkColumnName )
+ .append( ' ' )
+ .append( dialect.getTypeName( Types.VARCHAR, keySize, 0, 0 ) )
+ .append( ", " )
+ .append( valueColumnName )
+ .append( ' ' )
+ .append( dialect.getTypeName( Types.INTEGER ) )
+ .append( " ) " )
+ .toString()
+ };
+ }
+
+ public String[] sqlDropStrings(Dialect dialect) throws HibernateException {
+ StringBuffer sqlDropString = new StringBuffer( "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;
+ }
+
+ public Serializable doWorkInCurrentTransaction(Connection conn, String sql) throws SQLException {
+ int result;
+ int rows;
+ do {
+ // The loop ensures atomicity of the
+ // select + update even for no transaction
+ // or read committed isolation level
+
+ //sql = query;
+ SQL.debug(query);
+ PreparedStatement qps = conn.prepareStatement(query);
+ PreparedStatement ips = null;
+ try {
+ //qps.setString(1, key);
+ ResultSet rs = qps.executeQuery();
+ boolean isInitialized = rs.next();
+ if ( !isInitialized ) {
+ result = 0;
+ ips = conn.prepareStatement(insert);
+ //ips.setString(1, key);
+ ips.setInt(1, result);
+ ips.execute();
+ }
+ else {
+ result = rs.getInt(1);
+ }
+ rs.close();
+ }
+ catch (SQLException sqle) {
+ log.error("could not read or init a hi value", sqle);
+ throw sqle;
+ }
+ finally {
+ if (ips != null) {
+ ips.close();
+ }
+ qps.close();
+ }
+
+ //sql = update;
+ PreparedStatement ups = conn.prepareStatement(update);
+ try {
+ ups.setInt( 1, result + 1 );
+ ups.setInt( 2, result );
+ //ups.setString( 3, key );
+ rows = ups.executeUpdate();
+ }
+ catch (SQLException sqle) {
+ log.error("could not update hi value in: " + tableName, sqle);
+ throw sqle;
+ }
+ finally {
+ ups.close();
+ }
+ }
+ while (rows==0);
+ return new Integer(result);
+ }
+
+ public synchronized Serializable generate(SessionImplementor session, Object obj)
+ throws HibernateException {
+ if (maxLo < 1) {
+ //keep the behavior consistent even for boundary usages
+ int val = ( (Integer) doWorkInNewTransaction(session) ).intValue();
+ if (val == 0) val = ( (Integer) doWorkInNewTransaction(session) ).intValue();
+ return IdentifierGeneratorFactory.createNumber( val, returnClass );
+ }
+ if (lo>maxLo) {
+ int hival = ( (Integer) doWorkInNewTransaction(session) ).intValue();
+ lo = (hival == 0) ? 1 : 0;
+ hi = hival * (maxLo+1);
+ log.debug("new hi value: " + hival);
+ }
+ return IdentifierGeneratorFactory.createNumber( hi + lo++, returnClass );
+ }
+
+ public void configure(Type type, Properties params, Dialect dialect) throws MappingException {
+ tableName = PropertiesHelper.getString(ID_TABLE, params, DEFAULT_TABLE);
+ pkColumnName = PropertiesHelper.getString(PK_COLUMN_NAME, params, DEFAULT_PK_COLUMN);
+ valueColumnName = PropertiesHelper.getString(VALUE_COLUMN_NAME, params, DEFAULT_VALUE_COLUMN);
+ String schemaName = params.getProperty(SCHEMA);
+ String catalogName = params.getProperty(CATALOG);
+ keySize = PropertiesHelper.getInt(PK_LENGTH_NAME, params, DEFAULT_PK_LENGTH);
+ String keyValue = PropertiesHelper.getString(PK_VALUE_NAME, params, params.getProperty(TABLE) );
+
+ if ( tableName.indexOf( '.' )<0 ) {
+ tableName = Table.qualify( catalogName, schemaName, tableName );
+ }
+
+ query = "select " +
+ valueColumnName +
+ " from " +
+ dialect.appendLockHint(LockMode.UPGRADE, tableName) +
+ " where " + pkColumnName + " = '" + keyValue + "'" +
+ dialect.getForUpdateString();
+
+ update = "update " +
+ tableName +
+ " set " +
+ valueColumnName +
+ " = ? where " +
+ valueColumnName +
+ " = ? and " +
+ pkColumnName +
+ " = '" +
+ keyValue
+ + "'";
+
+ insert = "insert into " + tableName +
+ "(" + pkColumnName + ", " + valueColumnName + ") " +
+ "values('"+ keyValue +"', ?)";
+
+
+ //hilo config
+ maxLo = PropertiesHelper.getInt(MAX_LO, params, Short.MAX_VALUE);
+ lo = maxLo + 1; // so we "clock over" on the first invocation
+ returnClass = type.getReturnedClass();
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/id/PersistentIdentifierGenerator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/id/PersistentIdentifierGenerator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/id/PersistentIdentifierGenerator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,80 @@
+//$Id: PersistentIdentifierGenerator.java 6514 2005-04-26 06:37:54Z oneovthafew $
+package org.hibernate.id;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.HibernateException;
+import org.hibernate.dialect.Dialect;
+
+/**
+ * An <tt>IdentifierGenerator</tt> that requires creation of database objects.
+ * <br><br>
+ * All <tt>PersistentIdentifierGenerator</tt>s that also implement
+ * <tt>Configurable</tt> have access to a special mapping parameter: schema
+ *
+ * @see IdentifierGenerator
+ * @see Configurable
+ * @author Gavin King
+ */
+public interface PersistentIdentifierGenerator extends IdentifierGenerator {
+
+ /**
+ * The configuration parameter holding the schema name
+ */
+ public static final String SCHEMA = "schema";
+
+ /**
+ * The configuration parameter holding the table name for the
+ * generated id
+ */
+ public static final String TABLE = "target_table";
+
+ /**
+ * The configuration parameter holding the table names for all
+ * tables for which the id must be unique
+ */
+ public static final String TABLES = "identity_tables";
+
+ /**
+ * The configuration parameter holding the primary key column
+ * name of the generated id
+ */
+ public static final String PK = "target_column";
+
+ /**
+ * The configuration parameter holding the catalog name
+ */
+ public static final String CATALOG = "catalog";
+
+ /**
+ * The SQL required to create the underlying database objects.
+ * @param dialect
+ * @return String[]
+ * @throws HibernateException
+ */
+ public String[] sqlCreateStrings(Dialect dialect) throws HibernateException;
+
+ /**
+ * The SQL required to remove the underlying database objects.
+ * @param dialect
+ * @return String
+ * @throws HibernateException
+ */
+ public String[] sqlDropStrings(Dialect dialect) throws HibernateException;
+
+ /**
+ * Return a key unique to the underlying database objects. Prevents us from
+ * trying to create/remove them multiple times.
+ * @return Object an identifying key for this generator
+ */
+ public Object generatorKey();
+
+ static final Log SQL = LogFactory.getLog("org.hibernate.SQL");
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/id/PostInsertIdentifierGenerator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/id/PostInsertIdentifierGenerator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/id/PostInsertIdentifierGenerator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,16 @@
+//$Id: PostInsertIdentifierGenerator.java 9681 2006-03-24 18:10:04Z steve.ebersole at jboss.com $
+package org.hibernate.id;
+
+import org.hibernate.HibernateException;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
+
+/**
+ * @author Gavin King
+ */
+public interface PostInsertIdentifierGenerator extends IdentifierGenerator {
+ public InsertGeneratedIdentifierDelegate getInsertGeneratedIdentifierDelegate(
+ PostInsertIdentityPersister persister,
+ Dialect dialect,
+ boolean isGetGeneratedKeysEnabled) throws HibernateException;
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/id/PostInsertIdentifierGenerator.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/id/PostInsertIdentityPersister.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/id/PostInsertIdentityPersister.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/id/PostInsertIdentityPersister.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,37 @@
+//$Id: PostInsertIdentityPersister.java 9681 2006-03-24 18:10:04Z steve.ebersole at jboss.com $
+package org.hibernate.id;
+
+import org.hibernate.persister.entity.EntityPersister;
+
+/**
+ * A persister that may have an identity assigned by execution of
+ * a SQL <tt>INSERT</tt>.
+ *
+ * @author Gavin King
+ */
+public interface PostInsertIdentityPersister extends EntityPersister {
+ /**
+ * Get a SQL select string that performs a select based on a unique
+ * key determined by the given property name).
+ *
+ * @param propertyName The name of the property which maps to the
+ * column(s) to use in the select statement restriction.
+ * @return The SQL select string
+ */
+ public String getSelectByUniqueKeyString(String propertyName);
+
+ /**
+ * Get the database-specific SQL command to retrieve the last
+ * generated IDENTITY value.
+ *
+ * @return The SQL command string
+ */
+ public String getIdentitySelectString();
+
+ /**
+ * The names of the primary key columns in the root table.
+ *
+ * @return The primary key column names.
+ */
+ public String[] getRootTableKeyColumnNames();
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/id/PostInsertIdentityPersister.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/id/SelectGenerator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/id/SelectGenerator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/id/SelectGenerator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,138 @@
+//$Id: SelectGenerator.java 11060 2007-01-19 12:51:31Z steve.ebersole at jboss.com $
+package org.hibernate.id;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import org.hibernate.MappingException;
+import org.hibernate.HibernateException;
+import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
+import org.hibernate.id.insert.IdentifierGeneratingInsert;
+import org.hibernate.id.insert.AbstractSelectingDelegate;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.ValueInclusion;
+import org.hibernate.type.Type;
+
+/**
+ * A generator that selects the just inserted row to determine the identifier
+ * value assigned by the database. The correct row is located using a unique
+ * key.
+ * <p/>
+ * One mapping parameter is required: key (unless a natural-id is defined in the mapping).
+ *
+ * @author Gavin King
+ */
+public class SelectGenerator extends AbstractPostInsertGenerator implements Configurable {
+
+ private String uniqueKeyPropertyName;
+
+ public void configure(Type type, Properties params, Dialect d) throws MappingException {
+ uniqueKeyPropertyName = params.getProperty( "key" );
+ }
+
+ public InsertGeneratedIdentifierDelegate getInsertGeneratedIdentifierDelegate(
+ PostInsertIdentityPersister persister,
+ Dialect dialect,
+ boolean isGetGeneratedKeysEnabled) throws HibernateException {
+ return new SelectGeneratorDelegate( persister, dialect, uniqueKeyPropertyName );
+ }
+
+ private static String determineNameOfPropertyToUse(PostInsertIdentityPersister persister, String supplied) {
+ if ( supplied != null ) {
+ return supplied;
+ }
+ int[] naturalIdPropertyIndices = persister.getNaturalIdentifierProperties();
+ if ( naturalIdPropertyIndices == null ){
+ throw new IdentifierGenerationException(
+ "no natural-id property defined; need to specify [key] in " +
+ "generator parameters"
+ );
+ }
+ if ( naturalIdPropertyIndices.length > 1 ) {
+ throw new IdentifierGenerationException(
+ "select generator does not currently support composite " +
+ "natural-id properties; need to specify [key] in generator parameters"
+ );
+ }
+ ValueInclusion inclusion = persister.getPropertyInsertGenerationInclusions() [ naturalIdPropertyIndices[0] ];
+ if ( inclusion != ValueInclusion.NONE ) {
+ throw new IdentifierGenerationException(
+ "natural-id also defined as insert-generated; need to specify [key] " +
+ "in generator parameters"
+ );
+ }
+ return persister.getPropertyNames() [ naturalIdPropertyIndices[0] ];
+ }
+
+
+ /**
+ * The delegate for the select generation strategy.
+ */
+ public static class SelectGeneratorDelegate
+ extends AbstractSelectingDelegate
+ implements InsertGeneratedIdentifierDelegate {
+ private final PostInsertIdentityPersister persister;
+ private final Dialect dialect;
+
+ private final String uniqueKeyPropertyName;
+ private final Type uniqueKeyType;
+ private final Type idType;
+
+ private final String idSelectString;
+
+ private SelectGeneratorDelegate(
+ PostInsertIdentityPersister persister,
+ Dialect dialect,
+ String suppliedUniqueKeyPropertyName) {
+ super( persister );
+ this.persister = persister;
+ this.dialect = dialect;
+ this.uniqueKeyPropertyName = determineNameOfPropertyToUse( persister, suppliedUniqueKeyPropertyName );
+
+ idSelectString = persister.getSelectByUniqueKeyString( uniqueKeyPropertyName );
+ uniqueKeyType = persister.getPropertyType( uniqueKeyPropertyName );
+ idType = persister.getIdentifierType();
+ }
+
+ public IdentifierGeneratingInsert prepareIdentifierGeneratingInsert() {
+ return new IdentifierGeneratingInsert( dialect );
+ }
+
+
+ // AbstractSelectingDelegate impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ protected String getSelectSQL() {
+ return idSelectString;
+ }
+
+ protected void bindParameters(
+ SessionImplementor session,
+ PreparedStatement ps,
+ Object entity) throws SQLException {
+ Object uniqueKeyValue = persister.getPropertyValue( entity, uniqueKeyPropertyName, session.getEntityMode() );
+ uniqueKeyType.nullSafeSet( ps, uniqueKeyValue, 1, session );
+ }
+
+ protected Serializable getResult(
+ SessionImplementor session,
+ ResultSet rs,
+ Object entity) throws SQLException {
+ if ( !rs.next() ) {
+ throw new IdentifierGenerationException(
+ "the inserted row could not be located by the unique key: " +
+ uniqueKeyPropertyName
+ );
+ }
+ return ( Serializable ) idType.nullSafeGet(
+ rs,
+ persister.getRootTableKeyColumnNames(),
+ session,
+ entity
+ );
+ }
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/id/SelectGenerator.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/id/SequenceGenerator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/id/SequenceGenerator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/id/SequenceGenerator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,126 @@
+//$Id: SequenceGenerator.java 9686 2006-03-27 16:47:06Z steve.ebersole at jboss.com $
+package org.hibernate.id;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.exception.JDBCExceptionHelper;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.mapping.Table;
+import org.hibernate.type.Type;
+import org.hibernate.util.PropertiesHelper;
+
+/**
+ * <b>sequence</b><br>
+ * <br>
+ * Generates <tt>long</tt> values using an oracle-style sequence. A higher
+ * performance algorithm is <tt>SequenceHiLoGenerator</tt>.<br>
+ * <br>
+ * Mapping parameters supported: sequence, parameters.
+ *
+ * @see SequenceHiLoGenerator
+ * @see TableHiLoGenerator
+ * @author Gavin King
+ */
+
+public class SequenceGenerator implements PersistentIdentifierGenerator, Configurable {
+
+ /**
+ * The sequence parameter
+ */
+ public static final String SEQUENCE = "sequence";
+
+ /**
+ * The parameters parameter, appended to the create sequence DDL.
+ * For example (Oracle): <tt>INCREMENT BY 1 START WITH 1 MAXVALUE 100 NOCACHE</tt>.
+ */
+ public static final String PARAMETERS = "parameters";
+
+ private String sequenceName;
+ private String parameters;
+ private Type identifierType;
+ private String sql;
+
+ private static final Log log = LogFactory.getLog(SequenceGenerator.class);
+
+ public void configure(Type type, Properties params, Dialect dialect) throws MappingException {
+ sequenceName = PropertiesHelper.getString(SEQUENCE, params, "hibernate_sequence");
+ parameters = params.getProperty(PARAMETERS);
+ String schemaName = params.getProperty(SCHEMA);
+ String catalogName = params.getProperty(CATALOG);
+
+ if (sequenceName.indexOf( '.' ) < 0) {
+ sequenceName = Table.qualify( catalogName, schemaName, sequenceName );
+ }
+
+ this.identifierType = type;
+ sql = dialect.getSequenceNextValString(sequenceName);
+ }
+
+ public Serializable generate(SessionImplementor session, Object obj)
+ throws HibernateException {
+
+ try {
+
+ PreparedStatement st = session.getBatcher().prepareSelectStatement(sql);
+ try {
+ ResultSet rs = st.executeQuery();
+ try {
+ rs.next();
+ Serializable result = IdentifierGeneratorFactory.get(
+ rs, identifierType
+ );
+ if ( log.isDebugEnabled() ) {
+ log.debug("Sequence identifier generated: " + result);
+ }
+ return result;
+ }
+ finally {
+ rs.close();
+ }
+ }
+ finally {
+ session.getBatcher().closeStatement(st);
+ }
+
+ }
+ catch (SQLException sqle) {
+ throw JDBCExceptionHelper.convert(
+ session.getFactory().getSQLExceptionConverter(),
+ sqle,
+ "could not get next sequence value",
+ sql
+ );
+ }
+
+ }
+
+ public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
+ String[] ddl = dialect.getCreateSequenceStrings(sequenceName);
+ if ( parameters != null ) {
+ ddl[ddl.length - 1] += ' ' + parameters;
+ }
+ return ddl;
+ }
+
+ public String[] sqlDropStrings(Dialect dialect) throws HibernateException {
+ return dialect.getDropSequenceStrings(sequenceName);
+ }
+
+ public Object generatorKey() {
+ return sequenceName;
+ }
+
+ public String getSequenceName() {
+ return sequenceName;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/id/SequenceHiLoGenerator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/id/SequenceHiLoGenerator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/id/SequenceHiLoGenerator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,68 @@
+//$Id: SequenceHiLoGenerator.java 9720 2006-03-31 00:11:54Z epbernard $
+package org.hibernate.id;
+
+import java.io.Serializable;
+import java.util.Properties;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.type.Type;
+import org.hibernate.util.PropertiesHelper;
+
+/**
+ * <b>seqhilo</b><br>
+ * <br>
+ * An <tt>IdentifierGenerator</tt> that combines a hi/lo algorithm with an underlying
+ * oracle-style sequence that generates hi values. The user may specify a
+ * maximum lo value to determine how often new hi values are fetched.<br>
+ * <br>
+ * If sequences are not available, <tt>TableHiLoGenerator</tt> might be an
+ * alternative.<br>
+ * <br>
+ * Mapping parameters supported: sequence, max_lo, parameters.
+ *
+ * @see TableHiLoGenerator
+ * @author Gavin King
+ */
+public class SequenceHiLoGenerator extends SequenceGenerator {
+
+ public static final String MAX_LO = "max_lo";
+
+ private static final Log log = LogFactory.getLog(SequenceHiLoGenerator.class);
+
+ private int maxLo;
+ private int lo;
+ private long hi;
+ private Class returnClass;
+
+ public void configure(Type type, Properties params, Dialect d) throws MappingException {
+ super.configure(type, params, d);
+ maxLo = PropertiesHelper.getInt(MAX_LO, params, 9);
+ lo = maxLo + 1; // so we "clock over" on the first invocation
+ returnClass = type.getReturnedClass();
+ }
+
+ public synchronized Serializable generate(SessionImplementor session, Object obj)
+ throws HibernateException {
+ if (maxLo < 1) {
+ //keep the behavior consistent even for boundary usages
+ long val = ( (Number) super.generate(session, obj) ).longValue();
+ if (val == 0) val = ( (Number) super.generate(session, obj) ).longValue();
+ return IdentifierGeneratorFactory.createNumber( val, returnClass );
+ }
+ if ( lo>maxLo ) {
+ long hival = ( (Number) super.generate(session, obj) ).longValue();
+ lo = (hival == 0) ? 1 : 0;
+ hi = hival * ( maxLo+1 );
+ if ( log.isDebugEnabled() )
+ log.debug("new hi value: " + hival);
+ }
+
+ return IdentifierGeneratorFactory.createNumber( hi + lo++, returnClass );
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/id/SequenceIdentityGenerator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/id/SequenceIdentityGenerator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/id/SequenceIdentityGenerator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,100 @@
+package org.hibernate.id;
+
+import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
+import org.hibernate.id.insert.AbstractReturningDelegate;
+import org.hibernate.id.insert.IdentifierGeneratingInsert;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.sql.Insert;
+import org.hibernate.util.NamedGeneratedKeysHelper;
+import org.hibernate.type.Type;
+import org.hibernate.engine.SessionImplementor;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.Properties;
+
+/**
+ * A generator which combines sequence generation with immediate retrieval
+ * through JDBC3 {@link java.sql.Connection#prepareStatement(String, String[]) getGeneratedKeys}.
+ * In this respect it works much like ANSI-SQL IDENTITY generation.
+ * <p/>
+ * This generator only known to work with newer Oracle drivers compiled for
+ * JDK 1.4 (JDBC3).
+ * <p/>
+ * Note: Due to a bug in Oracle drivers, sql comments on these insert statements
+ * are completely disabled.
+ *
+ * @author Steve Ebersole
+ */
+public class SequenceIdentityGenerator extends SequenceGenerator
+ implements PostInsertIdentifierGenerator {
+
+ private static final Log log = LogFactory.getLog( SequenceIdentityGenerator.class );
+
+ public Serializable generate(SessionImplementor s, Object obj) {
+ return IdentifierGeneratorFactory.POST_INSERT_INDICATOR;
+ }
+
+ public InsertGeneratedIdentifierDelegate getInsertGeneratedIdentifierDelegate(
+ PostInsertIdentityPersister persister,
+ Dialect dialect,
+ boolean isGetGeneratedKeysEnabled) throws HibernateException {
+ return new Delegate( persister, dialect, getSequenceName() );
+ }
+
+ public void configure(Type type, Properties params, Dialect dialect) throws MappingException {
+ super.configure( type, params, dialect );
+ }
+
+ public static class Delegate extends AbstractReturningDelegate {
+ private final Dialect dialect;
+ private final String sequenceNextValFragment;
+ private final String[] keyColumns;
+
+ public Delegate(PostInsertIdentityPersister persister, Dialect dialect, String sequenceName) {
+ super( persister );
+ this.dialect = dialect;
+ this.sequenceNextValFragment = dialect.getSelectSequenceNextValString( sequenceName );
+ this.keyColumns = getPersister().getRootTableKeyColumnNames();
+ if ( keyColumns.length > 1 ) {
+ throw new HibernateException( "sequence-identity generator cannot be used with with multi-column keys" );
+ }
+ }
+
+ public IdentifierGeneratingInsert prepareIdentifierGeneratingInsert() {
+ NoCommentsInsert insert = new NoCommentsInsert( dialect );
+ insert.addColumn( getPersister().getRootTableKeyColumnNames()[0], sequenceNextValFragment );
+ return insert;
+ }
+
+ protected PreparedStatement prepare(String insertSQL, SessionImplementor session) throws SQLException {
+ return session.getBatcher().prepareStatement( insertSQL, keyColumns );
+ }
+
+ protected Serializable executeAndExtract(PreparedStatement insert) throws SQLException {
+ insert.executeUpdate();
+ return IdentifierGeneratorFactory.getGeneratedIdentity(
+ NamedGeneratedKeysHelper.getGeneratedKey( insert ),
+ getPersister().getIdentifierType()
+ );
+ }
+ }
+
+ public static class NoCommentsInsert extends IdentifierGeneratingInsert {
+ public NoCommentsInsert(Dialect dialect) {
+ super( dialect );
+ }
+
+ public Insert setComment(String comment) {
+ // don't allow comments on these insert statements as comments totally
+ // blow up the Oracle getGeneratedKeys "support" :(
+ log.info( "disallowing insert statement comment for select-identity due to Oracle driver bug" );
+ return this;
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/id/TableGenerator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/id/TableGenerator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/id/TableGenerator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,170 @@
+//$Id: TableGenerator.java 11303 2007-03-19 22:06:14Z steve.ebersole at jboss.com $
+package org.hibernate.id;
+
+import java.io.Serializable;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.Properties;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.HibernateException;
+import org.hibernate.LockMode;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.TransactionHelper;
+import org.hibernate.mapping.Table;
+import org.hibernate.type.Type;
+import org.hibernate.util.PropertiesHelper;
+
+/**
+ * An <tt>IdentifierGenerator</tt> that uses a database
+ * table to store the last generated value. It is not
+ * intended that applications use this strategy directly.
+ * However, it may be used to build other (efficient)
+ * strategies. The returned type is <tt>Integer</tt>.<br>
+ * <br>
+ * The hi value MUST be fetched in a seperate transaction
+ * to the <tt>Session</tt> transaction so the generator must
+ * be able to obtain a new connection and commit it. Hence
+ * this implementation may not be used when Hibernate is
+ * fetching connections when the user is supplying
+ * connections.<br>
+ * <br>
+ * The returned value is of type <tt>integer</tt>.<br>
+ * <br>
+ * Mapping parameters supported: table, column
+ *
+ * @see TableHiLoGenerator
+ * @author Gavin King
+ */
+public class TableGenerator extends TransactionHelper
+ implements PersistentIdentifierGenerator, Configurable {
+ /* COLUMN and TABLE should be renamed but it would break the public API */
+ /** The column parameter */
+ public static final String COLUMN = "column";
+
+ /** Default column name */
+ public static final String DEFAULT_COLUMN_NAME = "next_hi";
+
+ /** The table parameter */
+ public static final String TABLE = "table";
+
+ /** Default table name */
+ public static final String DEFAULT_TABLE_NAME = "hibernate_unique_key";
+
+ private static final Log log = LogFactory.getLog(TableGenerator.class);
+
+ private String tableName;
+ private String columnName;
+ private String query;
+ private String update;
+
+ public void configure(Type type, Properties params, Dialect dialect) {
+
+ tableName = PropertiesHelper.getString(TABLE, params, DEFAULT_TABLE_NAME);
+ columnName = PropertiesHelper.getString(COLUMN, params, DEFAULT_COLUMN_NAME);
+ String schemaName = params.getProperty(SCHEMA);
+ String catalogName = params.getProperty(CATALOG);
+
+ if ( tableName.indexOf( '.' )<0 ) {
+ tableName = Table.qualify( catalogName, schemaName, tableName );
+ }
+
+ query = "select " +
+ columnName +
+ " from " +
+ dialect.appendLockHint(LockMode.UPGRADE, tableName) +
+ dialect.getForUpdateString();
+
+ update = "update " +
+ tableName +
+ " set " +
+ columnName +
+ " = ? where " +
+ columnName +
+ " = ?";
+ }
+
+ public synchronized Serializable generate(SessionImplementor session, Object object)
+ throws HibernateException {
+ int result = ( (Integer) doWorkInNewTransaction(session) ).intValue();
+ return new Integer(result);
+ }
+
+
+ public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
+ return new String[] {
+ dialect.getCreateTableString() + " " + tableName + " ( " + columnName + " " + dialect.getTypeName(Types.INTEGER) + " )",
+ "insert into " + tableName + " values ( 0 )"
+ };
+ }
+
+ public String[] sqlDropStrings(Dialect dialect) {
+ StringBuffer sqlDropString = new StringBuffer( "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;
+ }
+
+ public Serializable doWorkInCurrentTransaction(Connection conn, String sql) throws SQLException {
+ int result;
+ int rows;
+ do {
+ // The loop ensures atomicity of the
+ // select + update even for no transaction
+ // or read committed isolation level
+
+ sql = query;
+ SQL.debug(query);
+ PreparedStatement qps = conn.prepareStatement(query);
+ 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.getInt(1);
+ rs.close();
+ }
+ catch (SQLException sqle) {
+ log.error("could not read a hi value", sqle);
+ throw sqle;
+ }
+ finally {
+ qps.close();
+ }
+
+ sql = update;
+ SQL.debug(update);
+ PreparedStatement ups = conn.prepareStatement(update);
+ try {
+ ups.setInt( 1, result + 1 );
+ ups.setInt( 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);
+ return new Integer(result);
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/id/TableHiLoGenerator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/id/TableHiLoGenerator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/id/TableHiLoGenerator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,72 @@
+//$Id: TableHiLoGenerator.java 11123 2007-01-31 23:46:11Z steve.ebersole at jboss.com $
+package org.hibernate.id;
+
+import java.io.Serializable;
+import java.util.Properties;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.HibernateException;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.type.Type;
+import org.hibernate.util.PropertiesHelper;
+
+/**
+ * <b>hilo</b><br>
+ * <br>
+ * An <tt>IdentifierGenerator</tt> that returns a <tt>Long</tt>, constructed using
+ * a hi/lo algorithm. The hi value MUST be fetched in a seperate transaction
+ * to the <tt>Session</tt> transaction so the generator must be able to obtain
+ * a new connection and commit it. Hence this implementation may not
+ * be used when the user is supplying connections. In this
+ * case a <tt>SequenceHiLoGenerator</tt> would be a better choice (where
+ * supported).<br>
+ * <br>
+ * Mapping parameters supported: table, column, max_lo
+ *
+ * @see SequenceHiLoGenerator
+ * @author Gavin King
+ */
+
+public class TableHiLoGenerator extends TableGenerator {
+
+ /**
+ * The max_lo parameter
+ */
+ public static final String MAX_LO = "max_lo";
+
+ private long hi;
+ private int lo;
+ private int maxLo;
+ private Class returnClass;
+
+ private static final Log log = LogFactory.getLog(TableHiLoGenerator.class);
+
+ public void configure(Type type, Properties params, Dialect d) {
+ super.configure(type, params, d);
+ maxLo = PropertiesHelper.getInt(MAX_LO, params, Short.MAX_VALUE);
+ lo = maxLo + 1; // so we "clock over" on the first invocation
+ returnClass = type.getReturnedClass();
+ }
+
+ public synchronized Serializable generate(SessionImplementor session, Object obj)
+ throws HibernateException {
+ if (maxLo < 1) {
+ //keep the behavior consistent even for boundary usages
+ long val = ( (Number) super.generate(session, obj) ).longValue();
+ if (val == 0) val = ( (Number) super.generate(session, obj) ).longValue();
+ return IdentifierGeneratorFactory.createNumber( val, returnClass );
+ }
+ if (lo>maxLo) {
+ long hival = ( (Number) super.generate(session, obj) ).longValue();
+ lo = (hival == 0) ? 1 : 0;
+ hi = hival * (maxLo+1);
+ log.debug("new hi value: " + hival);
+ }
+
+ return IdentifierGeneratorFactory.createNumber( hi + lo++, returnClass );
+
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/id/UUIDHexGenerator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/id/UUIDHexGenerator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/id/UUIDHexGenerator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,75 @@
+//$Id: UUIDHexGenerator.java 8049 2005-08-30 23:28:50Z turin42 $
+package org.hibernate.id;
+
+import java.io.Serializable;
+import java.util.Properties;
+
+import org.hibernate.Hibernate;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.type.Type;
+import org.hibernate.util.PropertiesHelper;
+
+/**
+ * <b>uuid</b><br>
+ * <br>
+ * A <tt>UUIDGenerator</tt> that returns a string of length 32,
+ * This string will consist of only hex digits. Optionally,
+ * the string may be generated with separators between each
+ * component of the UUID.
+ *
+ * Mapping parameters supported: separator.
+ *
+ * @author Gavin King
+ */
+
+public class UUIDHexGenerator extends AbstractUUIDGenerator implements Configurable {
+
+ private String sep = "";
+
+ protected String format(int intval) {
+ String formatted = Integer.toHexString(intval);
+ StringBuffer buf = new StringBuffer("00000000");
+ buf.replace( 8-formatted.length(), 8, formatted );
+ return buf.toString();
+ }
+
+ protected String format(short shortval) {
+ String formatted = Integer.toHexString(shortval);
+ StringBuffer buf = new StringBuffer("0000");
+ buf.replace( 4-formatted.length(), 4, formatted );
+ return buf.toString();
+ }
+
+ public Serializable generate(SessionImplementor session, Object obj) {
+ return new StringBuffer(36)
+ .append( format( getIP() ) ).append(sep)
+ .append( format( getJVM() ) ).append(sep)
+ .append( format( getHiTime() ) ).append(sep)
+ .append( format( getLoTime() ) ).append(sep)
+ .append( format( getCount() ) )
+ .toString();
+ }
+
+ public void configure(Type type, Properties params, Dialect d) {
+ sep = PropertiesHelper.getString("separator", params, "");
+ }
+
+ public static void main( String[] args ) throws Exception {
+ Properties props = new Properties();
+ props.setProperty("separator", "/");
+ IdentifierGenerator gen = new UUIDHexGenerator();
+ ( (Configurable) gen ).configure(Hibernate.STRING, props, null);
+ IdentifierGenerator gen2 = new UUIDHexGenerator();
+ ( (Configurable) gen2 ).configure(Hibernate.STRING, props, null);
+
+ for ( int i=0; i<10; i++) {
+ String id = (String) gen.generate(null, null);
+ System.out.println(id);
+ String id2 = (String) gen2.generate(null, null);
+ System.out.println(id2);
+ }
+
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/id/enhanced/AccessCallback.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/id/enhanced/AccessCallback.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/id/enhanced/AccessCallback.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -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/core/src/main/java/org/hibernate/id/enhanced/DatabaseStructure.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/id/enhanced/DatabaseStructure.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/id/enhanced/DatabaseStructure.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -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/core/src/main/java/org/hibernate/id/enhanced/Optimizer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/id/enhanced/Optimizer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/id/enhanced/Optimizer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -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/core/src/main/java/org/hibernate/id/enhanced/OptimizerFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/id/enhanced/OptimizerFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/id/enhanced/OptimizerFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -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/core/src/main/java/org/hibernate/id/enhanced/SequenceStructure.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/id/enhanced/SequenceStructure.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/id/enhanced/SequenceStructure.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -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/core/src/main/java/org/hibernate/id/enhanced/SequenceStyleGenerator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/id/enhanced/SequenceStyleGenerator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/id/enhanced/SequenceStyleGenerator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -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/core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,321 @@
+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;
+
+/**
+ * An enhanced version of explicit table-based generator. The main basis
+ * conceptualization is similiar to the legacy
+ * {@link org.hibernate.id.MultipleHiLoPerTableGenerator} in terms of the
+ * underlying storage structure (namely a single table capable of holding
+ * multiple generator values). The differentiator is, as with
+ * {@link SequenceStyleGenerator} as well, the externalization of the notion
+ * of an optimizer.
+ * <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 TableGenerator extends TransactionHelper implements PersistentIdentifierGenerator, Configurable {
+ private static final Log log = LogFactory.getLog( TableGenerator.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;
+ private long accessCount = 0;
+
+ 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 Optimizer getOptimizer() {
+ return optimizer;
+ }
+
+ public long getTableAccessCount() {
+ return accessCount;
+ }
+
+ 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 );
+
+ accessCount++;
+
+ return new Integer( result );
+ }
+
+ public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
+ return new String[] {
+ new StringBuffer()
+ .append( dialect.getCreateTableString() )
+ .append( ' ' )
+ .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/core/src/main/java/org/hibernate/id/enhanced/TableStructure.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/id/enhanced/TableStructure.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/id/enhanced/TableStructure.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -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[] {
+ dialect.getCreateTableString() + " " + 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/core/src/main/java/org/hibernate/id/insert/AbstractReturningDelegate.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/id/insert/AbstractReturningDelegate.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/id/insert/AbstractReturningDelegate.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,60 @@
+package org.hibernate.id.insert;
+
+import org.hibernate.id.PostInsertIdentityPersister;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.exception.JDBCExceptionHelper;
+import org.hibernate.pretty.MessageHelper;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+/**
+ * Abstract InsertGeneratedIdentifierDelegate implementation where the
+ * underlying strategy causes the enerated identitifer to be returned as an
+ * effect of performing the insert statement. Thus, there is no need for an
+ * additional sql statement to determine the generated identitifer.
+ *
+ * @author Steve Ebersole
+ */
+public abstract class AbstractReturningDelegate implements InsertGeneratedIdentifierDelegate {
+ private final PostInsertIdentityPersister persister;
+
+ public AbstractReturningDelegate(PostInsertIdentityPersister persister) {
+ this.persister = persister;
+ }
+
+ public final Serializable performInsert(String insertSQL, SessionImplementor session, Binder binder) {
+ try {
+ // prepare and execute the insert
+ PreparedStatement insert = prepare( insertSQL, session );
+ try {
+ binder.bindValues( insert );
+ return executeAndExtract( insert );
+ }
+ finally {
+ releaseStatement( insert, session );
+ }
+ }
+ catch ( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ session.getFactory().getSQLExceptionConverter(),
+ sqle,
+ "could not insert: " + MessageHelper.infoString( persister ),
+ insertSQL
+ );
+ }
+ }
+
+ protected PostInsertIdentityPersister getPersister() {
+ return persister;
+ }
+
+ protected abstract PreparedStatement prepare(String insertSQL, SessionImplementor session) throws SQLException;
+
+ protected abstract Serializable executeAndExtract(PreparedStatement insert) throws SQLException;
+
+ protected void releaseStatement(PreparedStatement insert, SessionImplementor session) throws SQLException {
+ session.getBatcher().closeStatement( insert );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/id/insert/AbstractSelectingDelegate.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/id/insert/AbstractSelectingDelegate.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/id/insert/AbstractSelectingDelegate.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,113 @@
+package org.hibernate.id.insert;
+
+import org.hibernate.exception.JDBCExceptionHelper;
+import org.hibernate.pretty.MessageHelper;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.id.PostInsertIdentityPersister;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.ResultSet;
+import java.io.Serializable;
+
+/**
+ * Abstract InsertGeneratedIdentifierDelegate implementation where the
+ * underlying strategy requires an subsequent select after the insert
+ * to determine the generated identifier.
+ *
+ * @author Steve Ebersole
+ */
+public abstract class AbstractSelectingDelegate implements InsertGeneratedIdentifierDelegate {
+ private final PostInsertIdentityPersister persister;
+
+ protected AbstractSelectingDelegate(PostInsertIdentityPersister persister) {
+ this.persister = persister;
+ }
+
+ public final Serializable performInsert(String insertSQL, SessionImplementor session, Binder binder) {
+ try {
+ // prepare and execute the insert
+ PreparedStatement insert = session.getBatcher().prepareStatement( insertSQL, false );
+ try {
+ binder.bindValues( insert );
+ insert.executeUpdate();
+ }
+ finally {
+ session.getBatcher().closeStatement( insert );
+ }
+ }
+ catch ( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ session.getFactory().getSQLExceptionConverter(),
+ sqle,
+ "could not insert: " + MessageHelper.infoString( persister ),
+ insertSQL
+ );
+ }
+
+ final String selectSQL = getSelectSQL();
+
+ try {
+ //fetch the generated id in a separate query
+ PreparedStatement idSelect = session.getBatcher().prepareStatement( selectSQL );
+ try {
+ bindParameters( session, idSelect, binder.getEntity() );
+ ResultSet rs = idSelect.executeQuery();
+ try {
+ return getResult( session, rs, binder.getEntity() );
+ }
+ finally {
+ rs.close();
+ }
+ }
+ finally {
+ session.getBatcher().closeStatement( idSelect );
+ }
+
+ }
+ catch ( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ session.getFactory().getSQLExceptionConverter(),
+ sqle,
+ "could not retrieve generated id after insert: " + MessageHelper.infoString( persister ),
+ insertSQL
+ );
+ }
+ }
+
+ /**
+ * Get the SQL statement to be used to retrieve generated key values.
+ *
+ * @return The SQL command string
+ */
+ protected abstract String getSelectSQL();
+
+ /**
+ * Bind any required parameter values into the SQL command {@link #getSelectSQL}.
+ *
+ * @param session The session
+ * @param ps The prepared {@link #getSelectSQL SQL} command
+ * @param entity The entity being saved.
+ * @throws SQLException
+ */
+ protected void bindParameters(
+ SessionImplementor session,
+ PreparedStatement ps,
+ Object entity) throws SQLException {
+ }
+
+ /**
+ * Extract the generated key value from the given result set.
+ *
+ * @param session The session
+ * @param rs The result set containing the generated primay key values.
+ * @param entity The entity being saved.
+ * @return The generated identifier
+ * @throws SQLException
+ */
+ protected abstract Serializable getResult(
+ SessionImplementor session,
+ ResultSet rs,
+ Object entity) throws SQLException;
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/id/insert/Binder.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/id/insert/Binder.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/id/insert/Binder.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,12 @@
+package org.hibernate.id.insert;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+/**
+ * @author Steve Ebersole
+ */
+public interface Binder {
+ public void bindValues(PreparedStatement ps) throws SQLException;
+ public Object getEntity();
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/id/insert/IdentifierGeneratingInsert.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/id/insert/IdentifierGeneratingInsert.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/id/insert/IdentifierGeneratingInsert.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,17 @@
+package org.hibernate.id.insert;
+
+import org.hibernate.sql.Insert;
+import org.hibernate.dialect.Dialect;
+
+/**
+ * Nothing more than a distinguishing subclass of Insert used to indicate
+ * intent. Some subclasses of this also provided some additional
+ * functionality or semantic to the genernated SQL statement string.
+ *
+ * @author Steve Ebersole
+ */
+public class IdentifierGeneratingInsert extends Insert {
+ public IdentifierGeneratingInsert(Dialect dialect) {
+ super( dialect );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/id/insert/InsertGeneratedIdentifierDelegate.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/id/insert/InsertGeneratedIdentifierDelegate.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/id/insert/InsertGeneratedIdentifierDelegate.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,39 @@
+package org.hibernate.id.insert;
+
+import org.hibernate.engine.SessionImplementor;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+/**
+ * Responsible for handling delegation relating to variants in how
+ * insert-generated-identifier generator strategies dictate processing:<ul>
+ * <li>building the sql insert statement
+ * <li>determination of the generated identifier value
+ * </ul>
+ *
+ * @author Steve Ebersole
+ */
+public interface InsertGeneratedIdentifierDelegate {
+
+ /**
+ * Build a {@link org.hibernate.sql.Insert} specific to the delegate's mode
+ * of handling generated key values.
+ *
+ * @return The insert object.
+ */
+ public IdentifierGeneratingInsert prepareIdentifierGeneratingInsert();
+
+ /**
+ * Perform the indicated insert SQL statement and determine the identifier value
+ * generated.
+ *
+ * @param insertSQL
+ * @param session
+ * @param binder
+ * @return The generated identifier value.
+ */
+ public Serializable performInsert(String insertSQL, SessionImplementor session, Binder binder);
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/id/insert/InsertSelectIdentityInsert.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/id/insert/InsertSelectIdentityInsert.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/id/insert/InsertSelectIdentityInsert.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,20 @@
+package org.hibernate.id.insert;
+
+import org.hibernate.dialect.Dialect;
+
+/**
+ * Specialized IdentifierGeneratingInsert which appends the database
+ * specific clause which signifies to return generated IDENTITY values
+ * to the end of the insert statement.
+ *
+ * @author Steve Ebersole
+ */
+public class InsertSelectIdentityInsert extends IdentifierGeneratingInsert {
+ public InsertSelectIdentityInsert(Dialect dialect) {
+ super( dialect );
+ }
+
+ public String toStatementString() {
+ return getDialect().appendIdentitySelectToInsert( super.toStatementString() );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/id/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/id/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/id/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,9 @@
+<html>
+<head></head>
+<body>
+<p>
+ This package contains internal implementation classes for the
+ main API interfaces.
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/id/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/impl/AbstractQueryImpl.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/impl/AbstractQueryImpl.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/impl/AbstractQueryImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,878 @@
+//$Id: AbstractQueryImpl.java 10856 2006-11-21 21:57:42Z steve.ebersole at jboss.com $
+package org.hibernate.impl;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import org.hibernate.CacheMode;
+import org.hibernate.FlushMode;
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.NonUniqueResultException;
+import org.hibernate.PropertyNotFoundException;
+import org.hibernate.Query;
+import org.hibernate.QueryException;
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.RowSelection;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.TypedValue;
+import org.hibernate.engine.query.ParameterMetadata;
+import org.hibernate.hql.classic.ParserHelper;
+import org.hibernate.property.Getter;
+import org.hibernate.proxy.HibernateProxyHelper;
+import org.hibernate.transform.ResultTransformer;
+import org.hibernate.type.SerializableType;
+import org.hibernate.type.Type;
+import org.hibernate.type.TypeFactory;
+import org.hibernate.util.ArrayHelper;
+import org.hibernate.util.MarkerObject;
+import org.hibernate.util.ReflectHelper;
+import org.hibernate.util.StringHelper;
+
+/**
+ * Abstract implementation of the Query interface.
+ *
+ * @author Gavin King, Max Andersen
+ */
+public abstract class AbstractQueryImpl implements Query {
+
+ private static final Object UNSET_PARAMETER = new MarkerObject("<unset parameter>");
+ private static final Object UNSET_TYPE = new MarkerObject("<unset type>");
+
+ private final String queryString;
+ protected final SessionImplementor session;
+ protected final ParameterMetadata parameterMetadata;
+
+ // parameter bind values...
+ private List values = new ArrayList(4);
+ private List types = new ArrayList(4);
+ private Map namedParameters = new HashMap(4);
+ private Map namedParameterLists = new HashMap(4);
+
+ private Object optionalObject;
+ private Serializable optionalId;
+ private String optionalEntityName;
+
+ private RowSelection selection;
+ private boolean cacheable;
+ private String cacheRegion;
+ private String comment;
+ private FlushMode flushMode;
+ private CacheMode cacheMode;
+ private FlushMode sessionFlushMode;
+ private CacheMode sessionCacheMode;
+ private Serializable collectionKey;
+ private boolean readOnly;
+ private ResultTransformer resultTransformer;
+
+ public AbstractQueryImpl(
+ String queryString,
+ FlushMode flushMode,
+ SessionImplementor session,
+ ParameterMetadata parameterMetadata) {
+ this.session = session;
+ this.queryString = queryString;
+ this.selection = new RowSelection();
+ this.flushMode = flushMode;
+ this.cacheMode = null;
+ this.parameterMetadata = parameterMetadata;
+ }
+
+ public String toString() {
+ return StringHelper.unqualify( getClass().getName() ) + '(' + queryString + ')';
+ }
+
+ public final String getQueryString() {
+ return queryString;
+ }
+
+ //TODO: maybe call it getRowSelection() ?
+ public RowSelection getSelection() {
+ return selection;
+ }
+
+ public Query setFlushMode(FlushMode flushMode) {
+ this.flushMode = flushMode;
+ return this;
+ }
+
+ public Query setCacheMode(CacheMode cacheMode) {
+ this.cacheMode = cacheMode;
+ return this;
+ }
+
+ public Query setCacheable(boolean cacheable) {
+ this.cacheable = cacheable;
+ return this;
+ }
+
+ public Query setCacheRegion(String cacheRegion) {
+ if (cacheRegion != null)
+ this.cacheRegion = cacheRegion.trim();
+ return this;
+ }
+
+ public Query setComment(String comment) {
+ this.comment = comment;
+ return this;
+ }
+
+ public Query setFirstResult(int firstResult) {
+ selection.setFirstRow( new Integer(firstResult) );
+ return this;
+ }
+
+ public Query setMaxResults(int maxResults) {
+ selection.setMaxRows( new Integer(maxResults) );
+ return this;
+ }
+
+ public Query setTimeout(int timeout) {
+ selection.setTimeout( new Integer(timeout) );
+ return this;
+ }
+ public Query setFetchSize(int fetchSize) {
+ selection.setFetchSize( new Integer(fetchSize) );
+ return this;
+ }
+
+ public Type[] getReturnTypes() throws HibernateException {
+ return session.getFactory().getReturnTypes( queryString );
+ }
+
+ public String[] getReturnAliases() throws HibernateException {
+ return session.getFactory().getReturnAliases( queryString );
+ }
+
+ public Query setCollectionKey(Serializable collectionKey) {
+ this.collectionKey = collectionKey;
+ return this;
+ }
+
+ public boolean isReadOnly() {
+ return readOnly;
+ }
+
+ public Query setReadOnly(boolean readOnly) {
+ this.readOnly = readOnly;
+ return this;
+ }
+
+ public Query setResultTransformer(ResultTransformer transformer) {
+ this.resultTransformer = transformer;
+ return this;
+ }
+
+ public void setOptionalEntityName(String optionalEntityName) {
+ this.optionalEntityName = optionalEntityName;
+ }
+
+ public void setOptionalId(Serializable optionalId) {
+ this.optionalId = optionalId;
+ }
+
+ public void setOptionalObject(Object optionalObject) {
+ this.optionalObject = optionalObject;
+ }
+
+ SessionImplementor getSession() {
+ return session;
+ }
+
+ protected abstract Map getLockModes();
+
+
+ // Parameter handling code ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Returns a shallow copy of the named parameter value map.
+ *
+ * @return Shallow copy of the named parameter value map
+ */
+ protected Map getNamedParams() {
+ return new HashMap( namedParameters );
+ }
+
+ /**
+ * Returns an array representing all named parameter names encountered
+ * during (intial) parsing of the query.
+ * <p/>
+ * Note <i>initial</i> here means different things depending on whether
+ * this is a native-sql query or an HQL/filter query. For native-sql, a
+ * precursory inspection of the query string is performed specifically to
+ * locate defined parameters. For HQL/filter queries, this is the
+ * information returned from the query-translator. This distinction
+ * holds true for all parameter metadata exposed here.
+ *
+ * @return Array of named parameter names.
+ * @throws HibernateException
+ */
+ public String[] getNamedParameters() throws HibernateException {
+ return ArrayHelper.toStringArray( parameterMetadata.getNamedParameterNames() );
+ }
+
+ /**
+ * Does this query contain named parameters?
+ *
+ * @return True if the query was found to contain named parameters; false
+ * otherwise;
+ */
+ public boolean hasNamedParameters() {
+ return parameterMetadata.getNamedParameterNames().size() > 0;
+ }
+
+ /**
+ * Retreive the value map for any named parameter lists (i.e., for
+ * auto-expansion) bound to this query.
+ *
+ * @return The parameter list value map.
+ */
+ protected Map getNamedParameterLists() {
+ return namedParameterLists;
+ }
+
+ /**
+ * Retreives the list of parameter values bound to this query for
+ * ordinal parameters.
+ *
+ * @return The ordinal parameter values.
+ */
+ protected List getValues() {
+ return values;
+ }
+
+ /**
+ * Retreives the list of parameter {@link Type type}s bound to this query for
+ * ordinal parameters.
+ *
+ * @return The ordinal parameter types.
+ */
+ protected List getTypes() {
+ return types;
+ }
+
+ /**
+ * Perform parameter validation. Used prior to executing the encapsulated
+ * query.
+ *
+ * @throws QueryException
+ */
+ protected void verifyParameters() throws QueryException {
+ verifyParameters(false);
+ }
+
+ /**
+ * Perform parameter validation. Used prior to executing the encapsulated
+ * query.
+ *
+ * @param reserveFirstParameter if true, the first ? will not be verified since
+ * its needed for e.g. callable statements returning a out parameter
+ * @throws HibernateException
+ */
+ protected void verifyParameters(boolean reserveFirstParameter) throws HibernateException {
+ if ( parameterMetadata.getNamedParameterNames().size() != namedParameters.size() + namedParameterLists.size() ) {
+ Set missingParams = new HashSet( parameterMetadata.getNamedParameterNames() );
+ missingParams.removeAll( namedParameterLists.keySet() );
+ missingParams.removeAll( namedParameters.keySet() );
+ throw new QueryException( "Not all named parameters have been set: " + missingParams, getQueryString() );
+ }
+
+ int positionalValueSpan = 0;
+ for ( int i = 0; i < values.size(); i++ ) {
+ Object object = types.get( i );
+ if( values.get( i ) == UNSET_PARAMETER || object == UNSET_TYPE ) {
+ if ( reserveFirstParameter && i==0 ) {
+ continue;
+ }
+ else {
+ throw new QueryException( "Unset positional parameter at position: " + i, getQueryString() );
+ }
+ }
+ positionalValueSpan += ( (Type) object ).getColumnSpan( session.getFactory() );
+ }
+
+ if ( parameterMetadata.getOrdinalParameterCount() != positionalValueSpan ) {
+ if ( reserveFirstParameter && parameterMetadata.getOrdinalParameterCount() - 1 != positionalValueSpan ) {
+ throw new QueryException(
+ "Expected positional parameter count: " +
+ (parameterMetadata.getOrdinalParameterCount()-1) +
+ ", actual parameters: " +
+ values,
+ getQueryString()
+ );
+ }
+ else if ( !reserveFirstParameter ) {
+ throw new QueryException(
+ "Expected positional parameter count: " +
+ parameterMetadata.getOrdinalParameterCount() +
+ ", actual parameters: " +
+ values,
+ getQueryString()
+ );
+ }
+ }
+ }
+
+ public Query setParameter(int position, Object val, Type type) {
+ if ( parameterMetadata.getOrdinalParameterCount() == 0 ) {
+ throw new IllegalArgumentException("No positional parameters in query: " + getQueryString() );
+ }
+ if ( position < 0 || position > parameterMetadata.getOrdinalParameterCount() - 1 ) {
+ throw new IllegalArgumentException("Positional parameter does not exist: " + position + " in query: " + getQueryString() );
+ }
+ int size = values.size();
+ if ( position < size ) {
+ values.set( position, val );
+ types.set( position, type );
+ }
+ else {
+ // prepend value and type list with null for any positions before the wanted position.
+ for ( int i = 0; i < position - size; i++ ) {
+ values.add( UNSET_PARAMETER );
+ types.add( UNSET_TYPE );
+ }
+ values.add( val );
+ types.add( type );
+ }
+ return this;
+ }
+
+ public Query setParameter(String name, Object val, Type type) {
+ if ( !parameterMetadata.getNamedParameterNames().contains( name ) ) {
+ throw new IllegalArgumentException("Parameter " + name + " does not exist as a named parameter in [" + getQueryString() + "]");
+ }
+ else {
+ namedParameters.put( name, new TypedValue( type, val, session.getEntityMode() ) );
+ return this;
+ }
+ }
+
+ public Query setParameter(int position, Object val) throws HibernateException {
+ if (val == null) {
+ setParameter( position, val, Hibernate.SERIALIZABLE );
+ }
+ else {
+ setParameter( position, val, determineType( position, val ) );
+ }
+ return this;
+ }
+
+ public Query setParameter(String name, Object val) throws HibernateException {
+ if (val == null) {
+ Type type = parameterMetadata.getNamedParameterExpectedType( name );
+ if ( type == null ) {
+ type = Hibernate.SERIALIZABLE;
+ }
+ setParameter( name, val, type );
+ }
+ else {
+ setParameter( name, val, determineType( name, val ) );
+ }
+ return this;
+ }
+
+ protected Type determineType(int paramPosition, Object paramValue, Type defaultType) {
+ Type type = parameterMetadata.getOrdinalParameterExpectedType( paramPosition + 1 );
+ if ( type == null ) {
+ type = defaultType;
+ }
+ return type;
+ }
+
+ protected Type determineType(int paramPosition, Object paramValue) throws HibernateException {
+ Type type = parameterMetadata.getOrdinalParameterExpectedType( paramPosition + 1 );
+ if ( type == null ) {
+ type = guessType( paramValue );
+ }
+ return type;
+ }
+
+ protected Type determineType(String paramName, Object paramValue, Type defaultType) {
+ Type type = parameterMetadata.getNamedParameterExpectedType( paramName );
+ if ( type == null ) {
+ type = defaultType;
+ }
+ return type;
+ }
+
+ protected Type determineType(String paramName, Object paramValue) throws HibernateException {
+ Type type = parameterMetadata.getNamedParameterExpectedType( paramName );
+ if ( type == null ) {
+ type = guessType( paramValue );
+ }
+ return type;
+ }
+
+ protected Type determineType(String paramName, Class clazz) throws HibernateException {
+ Type type = parameterMetadata.getNamedParameterExpectedType( paramName );
+ if ( type == null ) {
+ type = guessType( clazz );
+ }
+ return type;
+ }
+
+ private Type guessType(Object param) throws HibernateException {
+ Class clazz = HibernateProxyHelper.getClassWithoutInitializingProxy( param );
+ return guessType( clazz );
+ }
+
+ private Type guessType(Class clazz) throws HibernateException {
+ String typename = clazz.getName();
+ Type type = TypeFactory.heuristicType(typename);
+ boolean serializable = type!=null && type instanceof SerializableType;
+ if (type==null || serializable) {
+ try {
+ session.getFactory().getEntityPersister( clazz.getName() );
+ }
+ catch (MappingException me) {
+ if (serializable) {
+ return type;
+ }
+ else {
+ throw new HibernateException("Could not determine a type for class: " + typename);
+ }
+ }
+ return Hibernate.entity(clazz);
+ }
+ else {
+ return type;
+ }
+ }
+
+ public Query setString(int position, String val) {
+ setParameter(position, val, Hibernate.STRING);
+ return this;
+ }
+
+ public Query setCharacter(int position, char val) {
+ setParameter(position, new Character(val), Hibernate.CHARACTER);
+ return this;
+ }
+
+ public Query setBoolean(int position, boolean val) {
+ Boolean valueToUse = val ? Boolean.TRUE : Boolean.FALSE;
+ Type typeToUse = determineType( position, valueToUse, Hibernate.BOOLEAN );
+ setParameter( position, valueToUse, typeToUse );
+ return this;
+ }
+
+ public Query setByte(int position, byte val) {
+ setParameter(position, new Byte(val), Hibernate.BYTE);
+ return this;
+ }
+
+ public Query setShort(int position, short val) {
+ setParameter(position, new Short(val), Hibernate.SHORT);
+ return this;
+ }
+
+ public Query setInteger(int position, int val) {
+ setParameter(position, new Integer(val), Hibernate.INTEGER);
+ return this;
+ }
+
+ public Query setLong(int position, long val) {
+ setParameter(position, new Long(val), Hibernate.LONG);
+ return this;
+ }
+
+ public Query setFloat(int position, float val) {
+ setParameter(position, new Float(val), Hibernate.FLOAT);
+ return this;
+ }
+
+ public Query setDouble(int position, double val) {
+ setParameter(position, new Double(val), Hibernate.DOUBLE);
+ return this;
+ }
+
+ public Query setBinary(int position, byte[] val) {
+ setParameter(position, val, Hibernate.BINARY);
+ return this;
+ }
+
+ public Query setText(int position, String val) {
+ setParameter(position, val, Hibernate.TEXT);
+ return this;
+ }
+
+ public Query setSerializable(int position, Serializable val) {
+ setParameter(position, val, Hibernate.SERIALIZABLE);
+ return this;
+ }
+
+ public Query setDate(int position, Date date) {
+ setParameter(position, date, Hibernate.DATE);
+ return this;
+ }
+
+ public Query setTime(int position, Date date) {
+ setParameter(position, date, Hibernate.TIME);
+ return this;
+ }
+
+ public Query setTimestamp(int position, Date date) {
+ setParameter(position, date, Hibernate.TIMESTAMP);
+ return this;
+ }
+
+ public Query setEntity(int position, Object val) {
+ setParameter( position, val, Hibernate.entity( resolveEntityName( val ) ) );
+ return this;
+ }
+
+ private String resolveEntityName(Object val) {
+ if ( val == null ) {
+ throw new IllegalArgumentException( "entity for parameter binding cannot be null" );
+ }
+ return session.bestGuessEntityName( val );
+ }
+
+ public Query setLocale(int position, Locale locale) {
+ setParameter(position, locale, Hibernate.LOCALE);
+ return this;
+ }
+
+ public Query setCalendar(int position, Calendar calendar) {
+ setParameter(position, calendar, Hibernate.CALENDAR);
+ return this;
+ }
+
+ public Query setCalendarDate(int position, Calendar calendar) {
+ setParameter(position, calendar, Hibernate.CALENDAR_DATE);
+ return this;
+ }
+
+ public Query setBinary(String name, byte[] val) {
+ setParameter(name, val, Hibernate.BINARY);
+ return this;
+ }
+
+ public Query setText(String name, String val) {
+ setParameter(name, val, Hibernate.TEXT);
+ return this;
+ }
+
+ public Query setBoolean(String name, boolean val) {
+ Boolean valueToUse = val ? Boolean.TRUE : Boolean.FALSE;
+ Type typeToUse = determineType( name, valueToUse, Hibernate.BOOLEAN );
+ setParameter( name, valueToUse, typeToUse );
+ return this;
+ }
+
+ public Query setByte(String name, byte val) {
+ setParameter(name, new Byte(val), Hibernate.BYTE);
+ return this;
+ }
+
+ public Query setCharacter(String name, char val) {
+ setParameter(name, new Character(val), Hibernate.CHARACTER);
+ return this;
+ }
+
+ public Query setDate(String name, Date date) {
+ setParameter(name, date, Hibernate.DATE);
+ return this;
+ }
+
+ public Query setDouble(String name, double val) {
+ setParameter(name, new Double(val), Hibernate.DOUBLE);
+ return this;
+ }
+
+ public Query setEntity(String name, Object val) {
+ setParameter( name, val, Hibernate.entity( resolveEntityName( val ) ) );
+ return this;
+ }
+
+ public Query setFloat(String name, float val) {
+ setParameter(name, new Float(val), Hibernate.FLOAT);
+ return this;
+ }
+
+ public Query setInteger(String name, int val) {
+ setParameter(name, new Integer(val), Hibernate.INTEGER);
+ return this;
+ }
+
+ public Query setLocale(String name, Locale locale) {
+ setParameter(name, locale, Hibernate.LOCALE);
+ return this;
+ }
+
+ public Query setCalendar(String name, Calendar calendar) {
+ setParameter(name, calendar, Hibernate.CALENDAR);
+ return this;
+ }
+
+ public Query setCalendarDate(String name, Calendar calendar) {
+ setParameter(name, calendar, Hibernate.CALENDAR_DATE);
+ return this;
+ }
+
+ public Query setLong(String name, long val) {
+ setParameter(name, new Long(val), Hibernate.LONG);
+ return this;
+ }
+
+ public Query setSerializable(String name, Serializable val) {
+ setParameter(name, val, Hibernate.SERIALIZABLE);
+ return this;
+ }
+
+ public Query setShort(String name, short val) {
+ setParameter(name, new Short(val), Hibernate.SHORT);
+ return this;
+ }
+
+ public Query setString(String name, String val) {
+ setParameter(name, val, Hibernate.STRING);
+ return this;
+ }
+
+ public Query setTime(String name, Date date) {
+ setParameter(name, date, Hibernate.TIME);
+ return this;
+ }
+
+ public Query setTimestamp(String name, Date date) {
+ setParameter(name, date, Hibernate.TIMESTAMP);
+ return this;
+ }
+
+ public Query setBigDecimal(int position, BigDecimal number) {
+ setParameter(position, number, Hibernate.BIG_DECIMAL);
+ return this;
+ }
+
+ public Query setBigDecimal(String name, BigDecimal number) {
+ setParameter(name, number, Hibernate.BIG_DECIMAL);
+ return this;
+ }
+
+ public Query setBigInteger(int position, BigInteger number) {
+ setParameter(position, number, Hibernate.BIG_INTEGER);
+ return this;
+ }
+
+ public Query setBigInteger(String name, BigInteger number) {
+ setParameter(name, number, Hibernate.BIG_INTEGER);
+ return this;
+ }
+
+ public Query setParameterList(String name, Collection vals, Type type) throws HibernateException {
+ if ( !parameterMetadata.getNamedParameterNames().contains( name ) ) {
+ throw new IllegalArgumentException("Parameter " + name + " does not exist as a named parameter in [" + getQueryString() + "]");
+ }
+ namedParameterLists.put( name, new TypedValue( type, vals, session.getEntityMode() ) );
+ return this;
+ }
+
+ /**
+ * Warning: adds new parameters to the argument by side-effect, as well as
+ * mutating the query string!
+ */
+ protected String expandParameterLists(Map namedParamsCopy) {
+ String query = this.queryString;
+ Iterator iter = namedParameterLists.entrySet().iterator();
+ while ( iter.hasNext() ) {
+ Map.Entry me = (Map.Entry) iter.next();
+ query = expandParameterList( query, (String) me.getKey(), (TypedValue) me.getValue(), namedParamsCopy );
+ }
+ return query;
+ }
+
+ /**
+ * Warning: adds new parameters to the argument by side-effect, as well as
+ * mutating the query string!
+ */
+ private String expandParameterList(String query, String name, TypedValue typedList, Map namedParamsCopy) {
+ Collection vals = (Collection) typedList.getValue();
+ Type type = typedList.getType();
+ if ( vals.size() == 1 ) {
+ // short-circuit for performance...
+ namedParamsCopy.put( name, new TypedValue( type, vals.iterator().next(), session.getEntityMode() ) );
+ return query;
+ }
+
+ StringBuffer list = new StringBuffer( 16 );
+ Iterator iter = vals.iterator();
+ int i = 0;
+ boolean isJpaPositionalParam = parameterMetadata.getNamedParameterDescriptor( name ).isJpaStyle();
+ while ( iter.hasNext() ) {
+ String alias = ( isJpaPositionalParam ? 'x' + name : name ) + i++ + '_';
+ namedParamsCopy.put( alias, new TypedValue( type, iter.next(), session.getEntityMode() ) );
+ list.append( ParserHelper.HQL_VARIABLE_PREFIX ).append( alias );
+ if ( iter.hasNext() ) {
+ list.append( ", " );
+ }
+ }
+ String paramPrefix = isJpaPositionalParam ? "?" : ParserHelper.HQL_VARIABLE_PREFIX;
+ return StringHelper.replace( query, paramPrefix + name, list.toString(), true );
+ }
+
+ public Query setParameterList(String name, Collection vals) throws HibernateException {
+ if ( vals == null ) {
+ throw new QueryException( "Collection must be not null!" );
+ }
+
+ if( vals.size() == 0 ) {
+ setParameterList( name, vals, null );
+ }
+ else {
+ setParameterList(name, vals, determineType( name, vals.iterator().next() ) );
+ }
+
+ return this;
+ }
+
+ public Query setParameterList(String name, Object[] vals, Type type) throws HibernateException {
+ return setParameterList( name, Arrays.asList(vals), type );
+ }
+
+ public Query setParameterList(String name, Object[] vals) throws HibernateException {
+ return setParameterList( name, Arrays.asList(vals) );
+ }
+
+ public Query setProperties(Map map) throws HibernateException {
+ String[] params = getNamedParameters();
+ for (int i = 0; i < params.length; i++) {
+ String namedParam = params[i];
+ final Object object = map.get(namedParam);
+ if(object==null) {
+ continue;
+ }
+ Class retType = object.getClass();
+ if ( Collection.class.isAssignableFrom( retType ) ) {
+ setParameterList( namedParam, ( Collection ) object );
+ }
+ else if ( retType.isArray() ) {
+ setParameterList( namedParam, ( Object[] ) object );
+ }
+ else {
+ setParameter( namedParam, object, determineType( namedParam, retType ) );
+ }
+
+
+ }
+ return this;
+ }
+
+ public Query setProperties(Object bean) throws HibernateException {
+ Class clazz = bean.getClass();
+ String[] params = getNamedParameters();
+ for (int i = 0; i < params.length; i++) {
+ String namedParam = params[i];
+ try {
+ Getter getter = ReflectHelper.getGetter( clazz, namedParam );
+ Class retType = getter.getReturnType();
+ final Object object = getter.get( bean );
+ if ( Collection.class.isAssignableFrom( retType ) ) {
+ setParameterList( namedParam, ( Collection ) object );
+ }
+ else if ( retType.isArray() ) {
+ setParameterList( namedParam, ( Object[] ) object );
+ }
+ else {
+ setParameter( namedParam, object, determineType( namedParam, retType ) );
+ }
+ }
+ catch (PropertyNotFoundException pnfe) {
+ // ignore
+ }
+ }
+ return this;
+ }
+
+ public Query setParameters(Object[] values, Type[] types) {
+ this.values = Arrays.asList(values);
+ this.types = Arrays.asList(types);
+ return this;
+ }
+
+
+ // Execution methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public Object uniqueResult() throws HibernateException {
+ return uniqueElement( list() );
+ }
+
+ static Object uniqueElement(List list) throws NonUniqueResultException {
+ int size = list.size();
+ if (size==0) return null;
+ Object first = list.get(0);
+ for ( int i=1; i<size; i++ ) {
+ if ( list.get(i)!=first ) {
+ throw new NonUniqueResultException( list.size() );
+ }
+ }
+ return first;
+ }
+
+ protected RowSelection getRowSelection() {
+ return selection;
+ }
+
+ public Type[] typeArray() {
+ return ArrayHelper.toTypeArray( getTypes() );
+ }
+
+ public Object[] valueArray() {
+ return getValues().toArray();
+ }
+
+ public QueryParameters getQueryParameters(Map namedParams) {
+ return new QueryParameters(
+ typeArray(),
+ valueArray(),
+ namedParams,
+ getLockModes(),
+ getSelection(),
+ readOnly,
+ cacheable,
+ cacheRegion,
+ comment,
+ collectionKey == null ? null : new Serializable[] { collectionKey },
+ optionalObject,
+ optionalEntityName,
+ optionalId,
+ resultTransformer
+ );
+ }
+
+ protected void before() {
+ if ( flushMode!=null ) {
+ sessionFlushMode = getSession().getFlushMode();
+ getSession().setFlushMode(flushMode);
+ }
+ if ( cacheMode!=null ) {
+ sessionCacheMode = getSession().getCacheMode();
+ getSession().setCacheMode(cacheMode);
+ }
+ }
+
+ protected void after() {
+ if (sessionFlushMode!=null) {
+ getSession().setFlushMode(sessionFlushMode);
+ sessionFlushMode = null;
+ }
+ if (sessionCacheMode!=null) {
+ getSession().setCacheMode(sessionCacheMode);
+ sessionCacheMode = null;
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/impl/AbstractScrollableResults.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/impl/AbstractScrollableResults.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/impl/AbstractScrollableResults.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,254 @@
+//$Id: AbstractScrollableResults.java 7469 2005-07-14 13:12:19Z steveebersole $
+package org.hibernate.impl;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.sql.Blob;
+import java.sql.Clob;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.ScrollableResults;
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.exception.JDBCExceptionHelper;
+import org.hibernate.hql.HolderInstantiator;
+import org.hibernate.loader.Loader;
+import org.hibernate.type.Type;
+
+/**
+ * Implementation of the <tt>ScrollableResults</tt> interface
+ *
+ * @author Steve Ebersole
+ */
+public abstract class AbstractScrollableResults implements ScrollableResults {
+
+ private final ResultSet resultSet;
+ private final PreparedStatement ps;
+ private final SessionImplementor session;
+ private final Loader loader;
+ private final QueryParameters queryParameters;
+ private final Type[] types;
+ private HolderInstantiator holderInstantiator;
+
+ public AbstractScrollableResults(
+ ResultSet rs,
+ PreparedStatement ps,
+ SessionImplementor sess,
+ Loader loader,
+ QueryParameters queryParameters,
+ Type[] types,
+ HolderInstantiator holderInstantiator) throws MappingException {
+ this.resultSet=rs;
+ this.ps=ps;
+ this.session = sess;
+ this.loader = loader;
+ this.queryParameters = queryParameters;
+ this.types = types;
+ this.holderInstantiator = holderInstantiator!=null && holderInstantiator.isRequired()
+ ? holderInstantiator
+ : null;
+ }
+
+ protected abstract Object[] getCurrentRow();
+
+ protected ResultSet getResultSet() {
+ return resultSet;
+ }
+
+ protected PreparedStatement getPs() {
+ return ps;
+ }
+
+ protected SessionImplementor getSession() {
+ return session;
+ }
+
+ protected Loader getLoader() {
+ return loader;
+ }
+
+ protected QueryParameters getQueryParameters() {
+ return queryParameters;
+ }
+
+ protected Type[] getTypes() {
+ return types;
+ }
+
+ protected HolderInstantiator getHolderInstantiator() {
+ return holderInstantiator;
+ }
+
+ public final void close() throws HibernateException {
+ try {
+ // not absolutely necessary, but does help with aggressive release
+ session.getBatcher().closeQueryStatement(ps, resultSet);
+ }
+ catch (SQLException sqle) {
+ throw JDBCExceptionHelper.convert(
+ session.getFactory().getSQLExceptionConverter(),
+ sqle,
+ "could not close results"
+ );
+ }
+ }
+
+ public final Object[] get() throws HibernateException {
+ return getCurrentRow();
+ }
+
+ public final Object get(int col) throws HibernateException {
+ return getCurrentRow()[col];
+ }
+
+ /**
+ * Check that the requested type is compatible with the result type, and
+ * return the column value. This version makes sure the the classes
+ * are identical.
+ *
+ * @param col the column
+ * @param returnType a "final" type
+ */
+ protected final Object getFinal(int col, Type returnType) throws HibernateException {
+ if ( holderInstantiator!=null ) {
+ throw new HibernateException("query specifies a holder class");
+ }
+
+ if ( returnType.getReturnedClass()==types[col].getReturnedClass() ) {
+ return get(col);
+ }
+ else {
+ return throwInvalidColumnTypeException(col, types[col], returnType);
+ }
+ }
+
+ /**
+ * Check that the requested type is compatible with the result type, and
+ * return the column value. This version makes sure the the classes
+ * are "assignable".
+ *
+ * @param col the column
+ * @param returnType any type
+ */
+ protected final Object getNonFinal(int col, Type returnType) throws HibernateException {
+ if ( holderInstantiator!=null ) {
+ throw new HibernateException("query specifies a holder class");
+ }
+
+ if ( returnType.getReturnedClass().isAssignableFrom( types[col].getReturnedClass() ) ) {
+ return get(col);
+ }
+ else {
+ return throwInvalidColumnTypeException(col, types[col], returnType);
+ }
+ }
+
+ public final BigDecimal getBigDecimal(int col) throws HibernateException {
+ return (BigDecimal) getFinal(col, Hibernate.BIG_DECIMAL);
+ }
+
+ public final BigInteger getBigInteger(int col) throws HibernateException {
+ return (BigInteger) getFinal(col, Hibernate.BIG_INTEGER);
+ }
+
+ public final byte[] getBinary(int col) throws HibernateException {
+ return (byte[]) getFinal(col, Hibernate.BINARY);
+ }
+
+ public final String getText(int col) throws HibernateException {
+ return (String) getFinal(col, Hibernate.TEXT);
+ }
+
+ public final Blob getBlob(int col) throws HibernateException {
+ return (Blob) getNonFinal(col, Hibernate.BLOB);
+ }
+
+ public final Clob getClob(int col) throws HibernateException {
+ return (Clob) getNonFinal(col, Hibernate.CLOB);
+ }
+
+ public final Boolean getBoolean(int col) throws HibernateException {
+ return (Boolean) getFinal(col, Hibernate.BOOLEAN);
+ }
+
+ public final Byte getByte(int col) throws HibernateException {
+ return (Byte) getFinal(col, Hibernate.BYTE);
+ }
+
+ public final Character getCharacter(int col) throws HibernateException {
+ return (Character) getFinal(col, Hibernate.CHARACTER);
+ }
+
+ public final Date getDate(int col) throws HibernateException {
+ return (Date) getNonFinal(col, Hibernate.TIMESTAMP);
+ }
+
+ public final Calendar getCalendar(int col) throws HibernateException {
+ return (Calendar) getNonFinal(col, Hibernate.CALENDAR);
+ }
+
+ public final Double getDouble(int col) throws HibernateException {
+ return (Double) getFinal(col, Hibernate.DOUBLE);
+ }
+
+ public final Float getFloat(int col) throws HibernateException {
+ return (Float) getFinal(col, Hibernate.FLOAT);
+ }
+
+ public final Integer getInteger(int col) throws HibernateException {
+ return (Integer) getFinal(col, Hibernate.INTEGER);
+ }
+
+ public final Long getLong(int col) throws HibernateException {
+ return (Long) getFinal(col, Hibernate.LONG);
+ }
+
+ public final Short getShort(int col) throws HibernateException {
+ return (Short) getFinal(col, Hibernate.SHORT);
+ }
+
+ public final String getString(int col) throws HibernateException {
+ return (String) getFinal(col, Hibernate.STRING);
+ }
+
+ public final Locale getLocale(int col) throws HibernateException {
+ return (Locale) getFinal(col, Hibernate.LOCALE);
+ }
+
+ /*public final Currency getCurrency(int col) throws HibernateException {
+ return (Currency) get(col);
+ }*/
+
+ public final TimeZone getTimeZone(int col) throws HibernateException {
+ return (TimeZone) getNonFinal(col, Hibernate.TIMEZONE);
+ }
+
+ public final Type getType(int i) {
+ return types[i];
+ }
+
+ private Object throwInvalidColumnTypeException(
+ int i,
+ Type type,
+ Type returnType) throws HibernateException {
+ throw new HibernateException(
+ "incompatible column types: " +
+ type.getName() +
+ ", " +
+ returnType.getName()
+ );
+ }
+
+ protected void afterScrollOperation() {
+ session.afterScrollOperation();
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/impl/AbstractSessionImpl.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/impl/AbstractSessionImpl.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/impl/AbstractSessionImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,150 @@
+//$Id: AbstractSessionImpl.java 10018 2006-06-15 05:21:06Z steve.ebersole at jboss.com $
+package org.hibernate.impl;
+
+import org.hibernate.MappingException;
+import org.hibernate.Query;
+import org.hibernate.SQLQuery;
+import org.hibernate.HibernateException;
+import org.hibernate.ScrollableResults;
+import org.hibernate.SessionException;
+import org.hibernate.engine.query.sql.NativeSQLQuerySpecification;
+import org.hibernate.engine.NamedQueryDefinition;
+import org.hibernate.engine.NamedSQLQueryDefinition;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.query.HQLQueryPlan;
+import org.hibernate.engine.query.NativeSQLQueryPlan;
+
+import java.util.List;
+
+/**
+ * Functionality common to stateless and stateful sessions
+ *
+ * @author Gavin King
+ */
+public abstract class AbstractSessionImpl implements SessionImplementor {
+
+ protected transient SessionFactoryImpl factory;
+ private boolean closed = false;
+
+ protected AbstractSessionImpl(SessionFactoryImpl factory) {
+ this.factory = factory;
+ }
+
+ public SessionFactoryImplementor getFactory() {
+ return factory;
+ }
+
+ public boolean isClosed() {
+ return closed;
+ }
+
+ protected void setClosed() {
+ closed = true;
+ }
+
+ protected void errorIfClosed() {
+ if ( closed ) {
+ throw new SessionException( "Session is closed!" );
+ }
+ }
+
+ public Query getNamedQuery(String queryName) throws MappingException {
+ errorIfClosed();
+ NamedQueryDefinition nqd = factory.getNamedQuery( queryName );
+ final Query query;
+ if ( nqd != null ) {
+ String queryString = nqd.getQueryString();
+ query = new QueryImpl(
+ queryString,
+ nqd.getFlushMode(),
+ this,
+ getHQLQueryPlan( queryString, false ).getParameterMetadata()
+ );
+ query.setComment( "named HQL query " + queryName );
+ }
+ else {
+ NamedSQLQueryDefinition nsqlqd = factory.getNamedSQLQuery( queryName );
+ if ( nsqlqd==null ) {
+ throw new MappingException( "Named query not known: " + queryName );
+ }
+ query = new SQLQueryImpl(
+ nsqlqd,
+ this,
+ factory.getQueryPlanCache().getSQLParameterMetadata( nsqlqd.getQueryString() )
+ );
+ query.setComment( "named native SQL query " + queryName );
+ nqd = nsqlqd;
+ }
+ initQuery( query, nqd );
+ return query;
+ }
+
+ public Query getNamedSQLQuery(String queryName) throws MappingException {
+ errorIfClosed();
+ NamedSQLQueryDefinition nsqlqd = factory.getNamedSQLQuery( queryName );
+ if ( nsqlqd==null ) {
+ throw new MappingException( "Named SQL query not known: " + queryName );
+ }
+ Query query = new SQLQueryImpl(
+ nsqlqd,
+ this,
+ factory.getQueryPlanCache().getSQLParameterMetadata( nsqlqd.getQueryString() )
+ );
+ query.setComment( "named native SQL query " + queryName );
+ initQuery( query, nsqlqd );
+ return query;
+ }
+
+ private void initQuery(Query query, NamedQueryDefinition nqd) {
+ query.setCacheable( nqd.isCacheable() );
+ query.setCacheRegion( nqd.getCacheRegion() );
+ if ( nqd.getTimeout()!=null ) query.setTimeout( nqd.getTimeout().intValue() );
+ if ( nqd.getFetchSize()!=null ) query.setFetchSize( nqd.getFetchSize().intValue() );
+ if ( nqd.getCacheMode() != null ) query.setCacheMode( nqd.getCacheMode() );
+ query.setReadOnly( nqd.isReadOnly() );
+ if ( nqd.getComment() != null ) query.setComment( nqd.getComment() );
+ }
+
+ public Query createQuery(String queryString) {
+ errorIfClosed();
+ QueryImpl query = new QueryImpl(
+ queryString,
+ this,
+ getHQLQueryPlan( queryString, false ).getParameterMetadata()
+ );
+ query.setComment( queryString );
+ return query;
+ }
+
+ public SQLQuery createSQLQuery(String sql) {
+ errorIfClosed();
+ SQLQueryImpl query = new SQLQueryImpl(
+ sql,
+ this,
+ factory.getQueryPlanCache().getSQLParameterMetadata( sql )
+ );
+ query.setComment( "dynamic native SQL query" );
+ return query;
+ }
+
+ protected HQLQueryPlan getHQLQueryPlan(String query, boolean shallow) throws HibernateException {
+ return factory.getQueryPlanCache().getHQLQueryPlan( query, shallow, getEnabledFilters() );
+ }
+
+ protected NativeSQLQueryPlan getNativeSQLQueryPlan(NativeSQLQuerySpecification spec) throws HibernateException {
+ return factory.getQueryPlanCache().getNativeSQLQueryPlan( spec );
+ }
+
+ public List list(NativeSQLQuerySpecification spec, QueryParameters queryParameters)
+ throws HibernateException {
+ return listCustomQuery( getNativeSQLQueryPlan( spec ).getCustomQuery(), queryParameters );
+ }
+
+ public ScrollableResults scroll(NativeSQLQuerySpecification spec, QueryParameters queryParameters)
+ throws HibernateException {
+ return scrollCustomQuery( getNativeSQLQueryPlan( spec ).getCustomQuery(), queryParameters );
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/impl/AbstractSessionImpl.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/impl/CollectionFilterImpl.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/impl/CollectionFilterImpl.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/impl/CollectionFilterImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,81 @@
+//$Id: CollectionFilterImpl.java 8524 2005-11-04 21:28:49Z steveebersole $
+package org.hibernate.impl;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.hibernate.HibernateException;
+import org.hibernate.ScrollableResults;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.query.ParameterMetadata;
+import org.hibernate.type.Type;
+
+/**
+ * implementation of the <tt>Query</tt> interface for collection filters
+ * @author Gavin King
+ */
+public class CollectionFilterImpl extends QueryImpl {
+
+ private Object collection;
+
+ public CollectionFilterImpl(
+ String queryString,
+ Object collection,
+ SessionImplementor session,
+ ParameterMetadata parameterMetadata) {
+ super( queryString, session, parameterMetadata );
+ this.collection = collection;
+ }
+
+
+ /**
+ * @see org.hibernate.Query#iterate()
+ */
+ public Iterator iterate() throws HibernateException {
+ verifyParameters();
+ Map namedParams = getNamedParams();
+ return getSession().iterateFilter(
+ collection,
+ expandParameterLists(namedParams),
+ getQueryParameters(namedParams)
+ );
+ }
+
+ /**
+ * @see org.hibernate.Query#list()
+ */
+ public List list() throws HibernateException {
+ verifyParameters();
+ Map namedParams = getNamedParams();
+ return getSession().listFilter(
+ collection,
+ expandParameterLists(namedParams),
+ getQueryParameters(namedParams)
+ );
+ }
+
+ /**
+ * @see org.hibernate.Query#scroll()
+ */
+ public ScrollableResults scroll() throws HibernateException {
+ throw new UnsupportedOperationException("Can't scroll filters");
+ }
+
+ public Type[] typeArray() {
+ List typeList = getTypes();
+ int size = typeList.size();
+ Type[] result = new Type[size+1];
+ for (int i=0; i<size; i++) result[i+1] = (Type) typeList.get(i);
+ return result;
+ }
+
+ public Object[] valueArray() {
+ List valueList = getValues();
+ int size = valueList.size();
+ Object[] result = new Object[size+1];
+ for (int i=0; i<size; i++) result[i+1] = valueList.get(i);
+ return result;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/impl/CriteriaImpl.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/impl/CriteriaImpl.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/impl/CriteriaImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,575 @@
+//$Id: CriteriaImpl.java 9116 2006-01-23 21:21:01Z steveebersole $
+package org.hibernate.impl;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.hibernate.CacheMode;
+import org.hibernate.Criteria;
+import org.hibernate.FetchMode;
+import org.hibernate.FlushMode;
+import org.hibernate.HibernateException;
+import org.hibernate.LockMode;
+import org.hibernate.ScrollMode;
+import org.hibernate.ScrollableResults;
+import org.hibernate.criterion.Criterion;
+import org.hibernate.criterion.NaturalIdentifier;
+import org.hibernate.criterion.Order;
+import org.hibernate.criterion.Projection;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.transform.ResultTransformer;
+import org.hibernate.util.StringHelper;
+
+/**
+ * Implementation of the <tt>Criteria</tt> interface
+ * @author Gavin King
+ */
+public class CriteriaImpl implements Criteria, Serializable {
+
+ private final String entityOrClassName;
+ private transient SessionImplementor session;
+ private final String rootAlias;
+
+ private List criterionEntries = new ArrayList();
+ private List orderEntries = new ArrayList();
+ private Projection projection;
+ private Criteria projectionCriteria;
+
+ private List subcriteriaList = new ArrayList();
+
+ private Map fetchModes = new HashMap();
+ private Map lockModes = new HashMap();
+
+ private Integer maxResults;
+ private Integer firstResult;
+ private Integer timeout;
+ private Integer fetchSize;
+
+ private boolean cacheable;
+ private String cacheRegion;
+ private String comment;
+
+ private FlushMode flushMode;
+ private CacheMode cacheMode;
+ private FlushMode sessionFlushMode;
+ private CacheMode sessionCacheMode;
+
+ private ResultTransformer resultTransformer = Criteria.ROOT_ENTITY;
+
+
+ // Constructors ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public CriteriaImpl(String entityOrClassName, SessionImplementor session) {
+ this(entityOrClassName, ROOT_ALIAS, session);
+ }
+
+ public CriteriaImpl(String entityOrClassName, String alias, SessionImplementor session) {
+ this.session = session;
+ this.entityOrClassName = entityOrClassName;
+ this.cacheable = false;
+ this.rootAlias = alias;
+ }
+
+ public String toString() {
+ return "CriteriaImpl(" +
+ entityOrClassName + ":" +
+ (rootAlias==null ? "" : rootAlias) +
+ subcriteriaList.toString() +
+ criterionEntries.toString() +
+ ( projection==null ? "" : projection.toString() ) +
+ ')';
+ }
+
+
+ // State ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public SessionImplementor getSession() {
+ return session;
+ }
+
+ public void setSession(SessionImplementor session) {
+ this.session = session;
+ }
+
+ public String getEntityOrClassName() {
+ return entityOrClassName;
+ }
+
+ public Map getLockModes() {
+ return lockModes;
+ }
+
+ public Criteria getProjectionCriteria() {
+ return projectionCriteria;
+ }
+
+ public Iterator iterateSubcriteria() {
+ return subcriteriaList.iterator();
+ }
+
+ public Iterator iterateExpressionEntries() {
+ return criterionEntries.iterator();
+ }
+
+ public Iterator iterateOrderings() {
+ return orderEntries.iterator();
+ }
+
+ public Criteria add(Criteria criteriaInst, Criterion expression) {
+ criterionEntries.add( new CriterionEntry(expression, criteriaInst) );
+ return this;
+ }
+
+
+ // Criteria impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public String getAlias() {
+ return rootAlias;
+ }
+
+ public Projection getProjection() {
+ return projection;
+ }
+
+ public Criteria setProjection(Projection projection) {
+ this.projection = projection;
+ this.projectionCriteria = this;
+ setResultTransformer( PROJECTION );
+ return this;
+ }
+
+ public Criteria add(Criterion expression) {
+ add( this, expression );
+ return this;
+ }
+
+ public Criteria addOrder(Order ordering) {
+ orderEntries.add( new OrderEntry( ordering, this ) );
+ return this;
+ }
+
+ public FetchMode getFetchMode(String path) {
+ return (FetchMode) fetchModes.get(path);
+ }
+
+ public Criteria setFetchMode(String associationPath, FetchMode mode) {
+ fetchModes.put( associationPath, mode );
+ return this;
+ }
+
+ public Criteria setLockMode(LockMode lockMode) {
+ return setLockMode( getAlias(), lockMode );
+ }
+
+ public Criteria setLockMode(String alias, LockMode lockMode) {
+ lockModes.put( alias, lockMode );
+ return this;
+ }
+
+ public Criteria createAlias(String associationPath, String alias) {
+ return createAlias( associationPath, alias, INNER_JOIN );
+ }
+
+ public Criteria createAlias(String associationPath, String alias, int joinType) {
+ new Subcriteria( this, associationPath, alias, joinType );
+ return this;
+ }
+
+ public Criteria createCriteria(String associationPath) {
+ return createCriteria( associationPath, INNER_JOIN );
+ }
+
+ public Criteria createCriteria(String associationPath, int joinType) {
+ return new Subcriteria( this, associationPath, joinType );
+ }
+
+ public Criteria createCriteria(String associationPath, String alias) {
+ return createCriteria( associationPath, alias, INNER_JOIN );
+ }
+
+ public Criteria createCriteria(String associationPath, String alias, int joinType) {
+ return new Subcriteria( this, associationPath, alias, joinType );
+ }
+
+ public ResultTransformer getResultTransformer() {
+ return resultTransformer;
+ }
+
+ public Criteria setResultTransformer(ResultTransformer tupleMapper) {
+ this.resultTransformer = tupleMapper;
+ return this;
+ }
+
+ public Integer getMaxResults() {
+ return maxResults;
+ }
+
+ public Criteria setMaxResults(int maxResults) {
+ this.maxResults = new Integer(maxResults);
+ return this;
+ }
+
+ public Integer getFirstResult() {
+ return firstResult;
+ }
+
+ public Criteria setFirstResult(int firstResult) {
+ this.firstResult = new Integer(firstResult);
+ return this;
+ }
+
+ public Integer getFetchSize() {
+ return fetchSize;
+ }
+
+ public Criteria setFetchSize(int fetchSize) {
+ this.fetchSize = new Integer(fetchSize);
+ return this;
+ }
+
+ public Integer getTimeout() {
+ return timeout;
+ }
+
+ public Criteria setTimeout(int timeout) {
+ this.timeout = new Integer(timeout);
+ return this;
+ }
+
+ public boolean getCacheable() {
+ return this.cacheable;
+ }
+
+ public Criteria setCacheable(boolean cacheable) {
+ this.cacheable = cacheable;
+ return this;
+ }
+
+ public String getCacheRegion() {
+ return this.cacheRegion;
+ }
+
+ public Criteria setCacheRegion(String cacheRegion) {
+ this.cacheRegion = cacheRegion.trim();
+ return this;
+ }
+
+ public String getComment() {
+ return comment;
+ }
+
+ public Criteria setComment(String comment) {
+ this.comment = comment;
+ return this;
+ }
+
+ public Criteria setFlushMode(FlushMode flushMode) {
+ this.flushMode = flushMode;
+ return this;
+ }
+
+ public Criteria setCacheMode(CacheMode cacheMode) {
+ this.cacheMode = cacheMode;
+ return this;
+ }
+
+ public List list() throws HibernateException {
+ before();
+ try {
+ return session.list( this );
+ }
+ finally {
+ after();
+ }
+ }
+
+ public ScrollableResults scroll() {
+ return scroll( ScrollMode.SCROLL_INSENSITIVE );
+ }
+
+ public ScrollableResults scroll(ScrollMode scrollMode) {
+ before();
+ try {
+ return session.scroll(this, scrollMode);
+ }
+ finally {
+ after();
+ }
+ }
+
+ public Object uniqueResult() throws HibernateException {
+ return AbstractQueryImpl.uniqueElement( list() );
+ }
+
+ protected void before() {
+ if ( flushMode != null ) {
+ sessionFlushMode = getSession().getFlushMode();
+ getSession().setFlushMode( flushMode );
+ }
+ if ( cacheMode != null ) {
+ sessionCacheMode = getSession().getCacheMode();
+ getSession().setCacheMode( cacheMode );
+ }
+ }
+
+ protected void after() {
+ if ( sessionFlushMode != null ) {
+ getSession().setFlushMode( sessionFlushMode );
+ sessionFlushMode = null;
+ }
+ if ( sessionCacheMode != null ) {
+ getSession().setCacheMode( sessionCacheMode );
+ sessionCacheMode = null;
+ }
+ }
+
+ public boolean isLookupByNaturalKey() {
+ if ( projection != null ) {
+ return false;
+ }
+ if ( subcriteriaList.size() > 0 ) {
+ return false;
+ }
+ if ( criterionEntries.size() != 1 ) {
+ return false;
+ }
+ CriterionEntry ce = (CriterionEntry) criterionEntries.get(0);
+ return ce.getCriterion() instanceof NaturalIdentifier;
+ }
+
+
+ // Inner classes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public final class Subcriteria implements Criteria, Serializable {
+
+ private String alias;
+ private String path;
+ private Criteria parent;
+ private LockMode lockMode;
+ private int joinType;
+
+
+ // Constructors ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ private Subcriteria(Criteria parent, String path, String alias, int joinType) {
+ this.alias = alias;
+ this.path = path;
+ this.parent = parent;
+ this.joinType = joinType;
+ CriteriaImpl.this.subcriteriaList.add(this);
+ }
+
+ private Subcriteria(Criteria parent, String path, int joinType) {
+ this( parent, path, null, joinType );
+ }
+
+ public String toString() {
+ return "Subcriteria(" +
+ path + ":" +
+ (alias==null ? "" : alias) +
+ ')';
+ }
+
+
+ // State ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public String getAlias() {
+ return alias;
+ }
+
+ public void setAlias(String alias) {
+ this.alias = alias;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ public Criteria getParent() {
+ return parent;
+ }
+
+ public LockMode getLockMode() {
+ return lockMode;
+ }
+
+ public Criteria setLockMode(LockMode lockMode) {
+ this.lockMode = lockMode;
+ return this;
+ }
+
+ public int getJoinType() {
+ return joinType;
+ }
+
+
+ // Criteria impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public Criteria add(Criterion expression) {
+ CriteriaImpl.this.add(this, expression);
+ return this;
+ }
+
+ public Criteria addOrder(Order order) {
+ CriteriaImpl.this.orderEntries.add( new OrderEntry(order, this) );
+ return this;
+ }
+
+ public Criteria createAlias(String associationPath, String alias) {
+ return createAlias( associationPath, alias, INNER_JOIN );
+ }
+
+ public Criteria createAlias(String associationPath, String alias, int joinType) throws HibernateException {
+ new Subcriteria( this, associationPath, alias, joinType );
+ return this;
+ }
+
+ public Criteria createCriteria(String associationPath) {
+ return createCriteria( associationPath, INNER_JOIN );
+ }
+
+ public Criteria createCriteria(String associationPath, int joinType) throws HibernateException {
+ return new Subcriteria( Subcriteria.this, associationPath, joinType );
+ }
+
+ public Criteria createCriteria(String associationPath, String alias) {
+ return createCriteria( associationPath, alias, INNER_JOIN );
+ }
+
+ public Criteria createCriteria(String associationPath, String alias, int joinType) throws HibernateException {
+ return new Subcriteria( Subcriteria.this, associationPath, alias, joinType );
+ }
+
+ public Criteria setCacheable(boolean cacheable) {
+ CriteriaImpl.this.setCacheable(cacheable);
+ return this;
+ }
+
+ public Criteria setCacheRegion(String cacheRegion) {
+ CriteriaImpl.this.setCacheRegion(cacheRegion);
+ return this;
+ }
+
+ public List list() throws HibernateException {
+ return CriteriaImpl.this.list();
+ }
+
+ public ScrollableResults scroll() throws HibernateException {
+ return CriteriaImpl.this.scroll();
+ }
+
+ public ScrollableResults scroll(ScrollMode scrollMode) throws HibernateException {
+ return CriteriaImpl.this.scroll(scrollMode);
+ }
+
+ public Object uniqueResult() throws HibernateException {
+ return CriteriaImpl.this.uniqueResult();
+ }
+
+ public Criteria setFetchMode(String associationPath, FetchMode mode)
+ throws HibernateException {
+ CriteriaImpl.this.setFetchMode( StringHelper.qualify(path, associationPath), mode);
+ return this;
+ }
+
+ public Criteria setFlushMode(FlushMode flushMode) {
+ CriteriaImpl.this.setFlushMode(flushMode);
+ return this;
+ }
+
+ public Criteria setCacheMode(CacheMode cacheMode) {
+ CriteriaImpl.this.setCacheMode(cacheMode);
+ return this;
+ }
+
+ public Criteria setFirstResult(int firstResult) {
+ CriteriaImpl.this.setFirstResult(firstResult);
+ return this;
+ }
+
+ public Criteria setMaxResults(int maxResults) {
+ CriteriaImpl.this.setMaxResults(maxResults);
+ return this;
+ }
+
+ public Criteria setTimeout(int timeout) {
+ CriteriaImpl.this.setTimeout(timeout);
+ return this;
+ }
+
+ public Criteria setFetchSize(int fetchSize) {
+ CriteriaImpl.this.setFetchSize(fetchSize);
+ return this;
+ }
+
+ public Criteria setLockMode(String alias, LockMode lockMode) {
+ CriteriaImpl.this.setLockMode(alias, lockMode);
+ return this;
+ }
+
+ public Criteria setResultTransformer(ResultTransformer resultProcessor) {
+ CriteriaImpl.this.setResultTransformer(resultProcessor);
+ return this;
+ }
+
+ public Criteria setComment(String comment) {
+ CriteriaImpl.this.setComment(comment);
+ return this;
+ }
+
+ public Criteria setProjection(Projection projection) {
+ CriteriaImpl.this.projection = projection;
+ CriteriaImpl.this.projectionCriteria = this;
+ setResultTransformer(PROJECTION);
+ return this;
+ }
+ }
+
+ public static final class CriterionEntry implements Serializable {
+ private final Criterion criterion;
+ private final Criteria criteria;
+
+ private CriterionEntry(Criterion criterion, Criteria criteria) {
+ this.criteria = criteria;
+ this.criterion = criterion;
+ }
+
+ public Criterion getCriterion() {
+ return criterion;
+ }
+
+ public Criteria getCriteria() {
+ return criteria;
+ }
+
+ public String toString() {
+ return criterion.toString();
+ }
+ }
+
+ public static final class OrderEntry implements Serializable {
+ private final Order order;
+ private final Criteria criteria;
+
+ private OrderEntry(Order order, Criteria criteria) {
+ this.criteria = criteria;
+ this.order = order;
+ }
+
+ public Order getOrder() {
+ return order;
+ }
+
+ public Criteria getCriteria() {
+ return criteria;
+ }
+
+ public String toString() {
+ return order.toString();
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/impl/FetchingScrollableResultsImpl.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/impl/FetchingScrollableResultsImpl.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/impl/FetchingScrollableResultsImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,293 @@
+// $Id: FetchingScrollableResultsImpl.java 7469 2005-07-14 13:12:19Z steveebersole $
+package org.hibernate.impl;
+
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.exception.JDBCExceptionHelper;
+import org.hibernate.hql.HolderInstantiator;
+import org.hibernate.type.Type;
+import org.hibernate.loader.Loader;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.QueryParameters;
+
+import java.sql.ResultSet;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+/**
+ * Implementation of ScrollableResults which can handle collection fetches.
+ *
+ * @author Steve Ebersole
+ */
+public class FetchingScrollableResultsImpl extends AbstractScrollableResults {
+
+ public FetchingScrollableResultsImpl(
+ ResultSet rs,
+ PreparedStatement ps,
+ SessionImplementor sess,
+ Loader loader,
+ QueryParameters queryParameters,
+ Type[] types,
+ HolderInstantiator holderInstantiator) throws MappingException {
+ super( rs, ps, sess, loader, queryParameters, types, holderInstantiator );
+ }
+
+ private Object[] currentRow = null;
+ private int currentPosition = 0;
+ private Integer maxPosition = null;
+
+ protected Object[] getCurrentRow() {
+ return currentRow;
+ }
+
+ /**
+ * Advance to the next result
+ *
+ * @return <tt>true</tt> if there is another result
+ */
+ public boolean next() throws HibernateException {
+ if ( maxPosition != null && maxPosition.intValue() <= currentPosition ) {
+ currentRow = null;
+ currentPosition = maxPosition.intValue() + 1;
+ return false;
+ }
+
+ Object row = getLoader().loadSequentialRowsForward(
+ getResultSet(),
+ getSession(),
+ getQueryParameters(),
+ false
+ );
+
+
+ boolean afterLast;
+ try {
+ afterLast = getResultSet().isAfterLast();
+ }
+ catch( SQLException e ) {
+ throw JDBCExceptionHelper.convert(
+ getSession().getFactory().getSQLExceptionConverter(),
+ e,
+ "exception calling isAfterLast()"
+ );
+ }
+
+ currentPosition++;
+ currentRow = new Object[] { row };
+
+ if ( afterLast ) {
+ if ( maxPosition == null ) {
+ // we just hit the last position
+ maxPosition = new Integer( currentPosition );
+ }
+ }
+
+ afterScrollOperation();
+
+ return true;
+ }
+
+ /**
+ * Retreat to the previous result
+ *
+ * @return <tt>true</tt> if there is a previous result
+ */
+ public boolean previous() throws HibernateException {
+ if ( currentPosition <= 1 ) {
+ currentPosition = 0;
+ currentRow = null;
+ return false;
+ }
+
+ Object loadResult = getLoader().loadSequentialRowsReverse(
+ getResultSet(),
+ getSession(),
+ getQueryParameters(),
+ false,
+ ( maxPosition != null && currentPosition > maxPosition.intValue() )
+ );
+
+ currentRow = new Object[] { loadResult };
+ currentPosition--;
+
+ afterScrollOperation();
+
+ return true;
+
+ }
+
+ /**
+ * Scroll an arbitrary number of locations
+ *
+ * @param positions a positive (forward) or negative (backward) number of rows
+ *
+ * @return <tt>true</tt> if there is a result at the new location
+ */
+ public boolean scroll(int positions) throws HibernateException {
+ boolean more = false;
+ if ( positions > 0 ) {
+ // scroll ahead
+ for ( int i = 0; i < positions; i++ ) {
+ more = next();
+ if ( !more ) {
+ break;
+ }
+ }
+ }
+ else if ( positions < 0 ) {
+ // scroll backward
+ for ( int i = 0; i < ( 0 - positions ); i++ ) {
+ more = previous();
+ if ( !more ) {
+ break;
+ }
+ }
+ }
+ else {
+ throw new HibernateException( "scroll(0) not valid" );
+ }
+
+ afterScrollOperation();
+
+ return more;
+ }
+
+ /**
+ * Go to the last result
+ *
+ * @return <tt>true</tt> if there are any results
+ */
+ public boolean last() throws HibernateException {
+ boolean more = false;
+ if ( maxPosition != null ) {
+ for ( int i = currentPosition; i < maxPosition.intValue(); i++ ) {
+ more = next();
+ }
+ }
+ else {
+ try {
+ if ( getResultSet().isAfterLast() ) {
+ // should not be able to reach last without maxPosition being set
+ // unless there are no results
+ return false;
+ }
+
+ while ( !getResultSet().isAfterLast() ) {
+ more = next();
+ }
+ }
+ catch( SQLException e ) {
+ throw JDBCExceptionHelper.convert(
+ getSession().getFactory().getSQLExceptionConverter(),
+ e,
+ "exception calling isAfterLast()"
+ );
+ }
+ }
+
+ afterScrollOperation();
+
+ return more;
+ }
+
+ /**
+ * Go to the first result
+ *
+ * @return <tt>true</tt> if there are any results
+ */
+ public boolean first() throws HibernateException {
+ beforeFirst();
+ boolean more = next();
+
+ afterScrollOperation();
+
+ return more;
+ }
+
+ /**
+ * Go to a location just before first result (this is the initial location)
+ */
+ public void beforeFirst() throws HibernateException {
+ try {
+ getResultSet().beforeFirst();
+ }
+ catch( SQLException e ) {
+ throw JDBCExceptionHelper.convert(
+ getSession().getFactory().getSQLExceptionConverter(),
+ e,
+ "exception calling beforeFirst()"
+ );
+ }
+ currentRow = null;
+ currentPosition = 0;
+ }
+
+ /**
+ * Go to a location just after the last result
+ */
+ public void afterLast() throws HibernateException {
+ // TODO : not sure the best way to handle this.
+ // The non-performant way :
+ last();
+ next();
+ afterScrollOperation();
+ }
+
+ /**
+ * Is this the first result?
+ *
+ * @return <tt>true</tt> if this is the first row of results
+ *
+ * @throws org.hibernate.HibernateException
+ */
+ public boolean isFirst() throws HibernateException {
+ return currentPosition == 1;
+ }
+
+ /**
+ * Is this the last result?
+ *
+ * @return <tt>true</tt> if this is the last row of results
+ *
+ * @throws org.hibernate.HibernateException
+ */
+ public boolean isLast() throws HibernateException {
+ if ( maxPosition == null ) {
+ // we have not yet hit the last result...
+ return false;
+ }
+ else {
+ return currentPosition == maxPosition.intValue();
+ }
+ }
+
+ /**
+ * Get the current location in the result set. The first row is number <tt>0</tt>, contrary to JDBC.
+ *
+ * @return the row number, numbered from <tt>0</tt>, or <tt>-1</tt> if there is no current row
+ */
+ public int getRowNumber() throws HibernateException {
+ return currentPosition;
+ }
+
+ /**
+ * Set the current location in the result set, numbered from either the first row (row number <tt>0</tt>), or the last
+ * row (row number <tt>-1</tt>).
+ *
+ * @param rowNumber the row number, numbered from the last row, in the case of a negative row number
+ *
+ * @return true if there is a row at that row number
+ */
+ public boolean setRowNumber(int rowNumber) throws HibernateException {
+ if ( rowNumber == 1 ) {
+ return first();
+ }
+ else if ( rowNumber == -1 ) {
+ return last();
+ }
+ else if ( maxPosition != null && rowNumber == maxPosition.intValue() ) {
+ return last();
+ }
+ return scroll( rowNumber - currentPosition );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/impl/FilterImpl.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/impl/FilterImpl.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/impl/FilterImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,151 @@
+// $Id: FilterImpl.java 8754 2005-12-05 23:36:59Z steveebersole $
+package org.hibernate.impl;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.hibernate.Filter;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.FilterDefinition;
+import org.hibernate.type.Type;
+
+/**
+ * Implementation of FilterImpl. FilterImpl implements the user's
+ * view into enabled dynamic filters, allowing them to set filter parameter values.
+ *
+ * @author Steve Ebersole
+ */
+public class FilterImpl implements Filter, Serializable {
+ public static final String MARKER = "$FILTER_PLACEHOLDER$";
+
+ private transient FilterDefinition definition;
+ private String filterName;
+ private Map parameters = new HashMap();
+
+ void afterDeserialize(SessionFactoryImpl factory) {
+ definition = factory.getFilterDefinition(filterName);
+ }
+
+ /**
+ * Constructs a new FilterImpl.
+ *
+ * @param configuration The filter's global configuration.
+ */
+ public FilterImpl(FilterDefinition configuration) {
+ this.definition = configuration;
+ filterName = definition.getFilterName();
+ }
+
+ public FilterDefinition getFilterDefinition() {
+ return definition;
+ }
+
+ /**
+ * Get the name of this filter.
+ *
+ * @return This filter's name.
+ */
+ public String getName() {
+ return definition.getFilterName();
+ }
+
+ public Map getParameters() {
+ return parameters;
+ }
+
+ /**
+ * Set the named parameter's value for this filter.
+ *
+ * @param name The parameter's name.
+ * @param value The value to be applied.
+ * @return This FilterImpl instance (for method chaining).
+ * @throws IllegalArgumentException Indicates that either the parameter was undefined or that the type
+ * of the passed value did not match the configured type.
+ */
+ public Filter setParameter(String name, Object value) throws IllegalArgumentException {
+ // Make sure this is a defined parameter and check the incoming value type
+ // TODO: what should be the actual exception type here?
+ Type type = definition.getParameterType( name );
+ if ( type == null ) {
+ throw new IllegalArgumentException( "Undefined filter parameter [" + name + "]" );
+ }
+ if ( value != null && !type.getReturnedClass().isAssignableFrom( value.getClass() ) ) {
+ throw new IllegalArgumentException( "Incorrect type for parameter [" + name + "]" );
+ }
+ parameters.put( name, value );
+ return this;
+ }
+
+ /**
+ * Set the named parameter's value list for this filter. Used
+ * in conjunction with IN-style filter criteria.
+ *
+ * @param name The parameter's name.
+ * @param values The values to be expanded into an SQL IN list.
+ * @return This FilterImpl instance (for method chaining).
+ */
+ public Filter setParameterList(String name, Collection values) throws HibernateException {
+ // Make sure this is a defined parameter and check the incoming value type
+ if ( values == null ) {
+ throw new IllegalArgumentException( "Collection must be not null!" );
+ }
+ Type type = definition.getParameterType( name );
+ if ( type == null ) {
+ throw new HibernateException( "Undefined filter parameter [" + name + "]" );
+ }
+ if ( values.size() > 0 ) {
+ Class elementClass = values.iterator().next().getClass();
+ if ( !type.getReturnedClass().isAssignableFrom( elementClass ) ) {
+ throw new HibernateException( "Incorrect type for parameter [" + name + "]" );
+ }
+ }
+ parameters.put( name, values );
+ return this;
+ }
+
+ /**
+ * Set the named parameter's value list for this filter. Used
+ * in conjunction with IN-style filter criteria.
+ *
+ * @param name The parameter's name.
+ * @param values The values to be expanded into an SQL IN list.
+ * @return This FilterImpl instance (for method chaining).
+ */
+ public Filter setParameterList(String name, Object[] values) throws IllegalArgumentException {
+ return setParameterList( name, Arrays.asList( values ) );
+ }
+
+ /**
+ * Get the value of the named parameter for the current filter.
+ *
+ * @param name The name of the parameter for which to return the value.
+ * @return The value of the named parameter.
+ */
+ public Object getParameter(String name) {
+ return parameters.get( name );
+ }
+
+ /**
+ * Perform validation of the filter state. This is used to verify the
+ * state of the filter after its enablement and before its use.
+ *
+ * @throws HibernateException If the state is not currently valid.
+ */
+ public void validate() throws HibernateException {
+ // for each of the defined parameters, make sure its value
+ // has been set
+ Iterator itr = definition.getParameterNames().iterator();
+ while ( itr.hasNext() ) {
+ final String parameterName = (String) itr.next();
+ if ( parameters.get( parameterName ) == null ) {
+ throw new HibernateException(
+ "Filter [" + getName() + "] parameter [" + parameterName + "] value not set"
+ );
+ }
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/impl/IteratorImpl.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/impl/IteratorImpl.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/impl/IteratorImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,152 @@
+//$Id: IteratorImpl.java 9944 2006-05-24 21:14:56Z steve.ebersole at jboss.com $
+package org.hibernate.impl;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.NoSuchElementException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.HibernateException;
+import org.hibernate.JDBCException;
+import org.hibernate.engine.HibernateIterator;
+import org.hibernate.event.EventSource;
+import org.hibernate.exception.JDBCExceptionHelper;
+import org.hibernate.hql.HolderInstantiator;
+import org.hibernate.type.EntityType;
+import org.hibernate.type.Type;
+
+/**
+ * An implementation of <tt>java.util.Iterator</tt> that is
+ * returned by <tt>iterate()</tt> query execution methods.
+ * @author Gavin King
+ */
+public final class IteratorImpl implements HibernateIterator {
+
+ private static final Log log = LogFactory.getLog(IteratorImpl.class);
+
+ private ResultSet rs;
+ private final EventSource session;
+ private final Type[] types;
+ private final boolean single;
+ private Object currentResult;
+ private boolean hasNext;
+ private final String[][] names;
+ private PreparedStatement ps;
+ private Object nextResult;
+ private HolderInstantiator holderInstantiator;
+
+ public IteratorImpl(
+ ResultSet rs,
+ PreparedStatement ps,
+ EventSource sess,
+ Type[] types,
+ String[][] columnNames,
+ HolderInstantiator holderInstantiator)
+ throws HibernateException, SQLException {
+
+ this.rs=rs;
+ this.ps=ps;
+ this.session = sess;
+ this.types = types;
+ this.names = columnNames;
+ this.holderInstantiator = holderInstantiator;
+
+ single = types.length==1;
+
+ postNext();
+ }
+
+ public void close() throws JDBCException {
+ if (ps!=null) {
+ try {
+ log.debug("closing iterator");
+ nextResult = null;
+ session.getBatcher().closeQueryStatement(ps, rs);
+ ps = null;
+ rs = null;
+ hasNext = false;
+ }
+ catch (SQLException e) {
+ log.info( "Unable to close iterator", e );
+ throw JDBCExceptionHelper.convert(
+ session.getFactory().getSQLExceptionConverter(),
+ e,
+ "Unable to close iterator"
+ );
+ }
+ }
+ }
+
+ private void postNext() throws HibernateException, SQLException {
+ this.hasNext = rs.next();
+ if (!hasNext) {
+ log.debug("exhausted results");
+ close();
+ }
+ else {
+ log.debug("retrieving next results");
+ boolean isHolder = holderInstantiator.isRequired();
+
+ if ( single && !isHolder ) {
+ nextResult = types[0].nullSafeGet( rs, names[0], session, null );
+ }
+ else {
+ Object[] nextResults = new Object[types.length];
+ for (int i=0; i<types.length; i++) {
+ nextResults[i] = types[i].nullSafeGet( rs, names[i], session, null );
+ }
+
+ if (isHolder) {
+ nextResult = holderInstantiator.instantiate(nextResults);
+ }
+ else {
+ nextResult = nextResults;
+ }
+ }
+
+ }
+ }
+
+ public boolean hasNext() {
+ return hasNext;
+ }
+
+ public Object next() {
+ if ( !hasNext ) throw new NoSuchElementException("No more results");
+ try {
+ currentResult = nextResult;
+ postNext();
+ log.debug("returning current results");
+ return currentResult;
+ }
+ catch (SQLException sqle) {
+ throw JDBCExceptionHelper.convert(
+ session.getFactory().getSQLExceptionConverter(),
+ sqle,
+ "could not get next iterator result"
+ );
+ }
+ }
+
+ public void remove() {
+ if (!single) {
+ throw new UnsupportedOperationException("Not a single column hibernate query result set");
+ }
+ if (currentResult==null) {
+ throw new IllegalStateException("Called Iterator.remove() before next()");
+ }
+ if ( !( types[0] instanceof EntityType ) ) {
+ throw new UnsupportedOperationException("Not an entity");
+ }
+
+ session.delete(
+ ( (EntityType) types[0] ).getAssociatedEntityName(),
+ currentResult,
+ false,
+ null
+ );
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/impl/QueryImpl.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/impl/QueryImpl.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/impl/QueryImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,119 @@
+//$Id: QueryImpl.java 8524 2005-11-04 21:28:49Z steveebersole $
+package org.hibernate.impl;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.hibernate.FlushMode;
+import org.hibernate.HibernateException;
+import org.hibernate.LockMode;
+import org.hibernate.Query;
+import org.hibernate.ScrollMode;
+import org.hibernate.ScrollableResults;
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.query.ParameterMetadata;
+
+/**
+ * default implementation of the <tt>Query</tt> interface,
+ * for "ordinary" HQL queries (not collection filters)
+ * @see CollectionFilterImpl
+ * @author Gavin King
+ */
+public class QueryImpl extends AbstractQueryImpl {
+
+ private Map lockModes = new HashMap(2);
+
+ public QueryImpl(
+ String queryString,
+ FlushMode flushMode,
+ SessionImplementor session,
+ ParameterMetadata parameterMetadata) {
+ super( queryString, flushMode, session, parameterMetadata );
+ }
+
+ public QueryImpl(String queryString, SessionImplementor session, ParameterMetadata parameterMetadata) {
+ this( queryString, null, session, parameterMetadata );
+ }
+
+ public Iterator iterate() throws HibernateException {
+ verifyParameters();
+ Map namedParams = getNamedParams();
+ before();
+ try {
+ return getSession().iterate(
+ expandParameterLists(namedParams),
+ getQueryParameters(namedParams)
+ );
+ }
+ finally {
+ after();
+ }
+ }
+
+ public ScrollableResults scroll() throws HibernateException {
+ return scroll( ScrollMode.SCROLL_INSENSITIVE );
+ }
+
+ public ScrollableResults scroll(ScrollMode scrollMode) throws HibernateException {
+ verifyParameters();
+ Map namedParams = getNamedParams();
+ before();
+ QueryParameters qp = getQueryParameters(namedParams);
+ qp.setScrollMode(scrollMode);
+ try {
+ return getSession().scroll( expandParameterLists(namedParams), qp );
+ }
+ finally {
+ after();
+ }
+ }
+
+ public List list() throws HibernateException {
+ verifyParameters();
+ Map namedParams = getNamedParams();
+ before();
+ try {
+ return getSession().list(
+ expandParameterLists(namedParams),
+ getQueryParameters(namedParams)
+ );
+ }
+ finally {
+ after();
+ }
+ }
+
+ public int executeUpdate() throws HibernateException {
+ verifyParameters();
+ Map namedParams = getNamedParams();
+ before();
+ try {
+ return getSession().executeUpdate(
+ expandParameterLists( namedParams ),
+ getQueryParameters( namedParams )
+ );
+ }
+ finally {
+ after();
+ }
+ }
+
+ public Query setLockMode(String alias, LockMode lockMode) {
+ lockModes.put(alias, lockMode);
+ return this;
+ }
+
+ protected Map getLockModes() {
+ return lockModes;
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/impl/SQLQueryImpl.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/impl/SQLQueryImpl.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/impl/SQLQueryImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,344 @@
+//$Id: SQLQueryImpl.java 10862 2006-11-22 00:11:35Z steve.ebersole at jboss.com $
+package org.hibernate.impl;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.io.Serializable;
+
+import org.hibernate.FlushMode;
+import org.hibernate.HibernateException;
+import org.hibernate.LockMode;
+import org.hibernate.Query;
+import org.hibernate.QueryException;
+import org.hibernate.SQLQuery;
+import org.hibernate.ScrollMode;
+import org.hibernate.ScrollableResults;
+import org.hibernate.MappingException;
+import org.hibernate.engine.query.sql.NativeSQLQuerySpecification;
+import org.hibernate.engine.ResultSetMappingDefinition;
+import org.hibernate.engine.NamedSQLQueryDefinition;
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.query.ParameterMetadata;
+import org.hibernate.engine.query.sql.NativeSQLQueryJoinReturn;
+import org.hibernate.engine.query.sql.NativeSQLQueryScalarReturn;
+import org.hibernate.engine.query.sql.NativeSQLQueryRootReturn;
+import org.hibernate.engine.query.sql.NativeSQLQueryReturn;
+import org.hibernate.type.Type;
+import org.hibernate.util.CollectionHelper;
+import org.hibernate.util.StringHelper;
+
+/**
+ * Implements SQL query passthrough.
+ *
+ * <pre>
+ * <sql-query name="mySqlQuery">
+ * <return alias="person" class="eg.Person"/>
+ * SELECT {person}.NAME AS {person.name}, {person}.AGE AS {person.age}, {person}.SEX AS {person.sex}
+ * FROM PERSON {person} WHERE {person}.NAME LIKE 'Hiber%'
+ * </sql-query>
+ * </pre>
+ *
+ * @author Max Andersen
+ */
+public class SQLQueryImpl extends AbstractQueryImpl implements SQLQuery {
+
+ private final List queryReturns;
+ private Collection querySpaces;
+ private final boolean callable;
+ private boolean autodiscovertypes;
+
+ /**
+ * Constructs a SQLQueryImpl given a sql query defined in the mappings.
+ *
+ * @param queryDef The representation of the defined <sql-query/>.
+ * @param session The session to which this SQLQueryImpl belongs.
+ * @param parameterMetadata Metadata about parameters found in the query.
+ */
+ SQLQueryImpl(NamedSQLQueryDefinition queryDef, SessionImplementor session, ParameterMetadata parameterMetadata) {
+ super( queryDef.getQueryString(), queryDef.getFlushMode(), session, parameterMetadata );
+ if ( queryDef.getResultSetRef() != null ) {
+ ResultSetMappingDefinition definition = session.getFactory()
+ .getResultSetMapping( queryDef.getResultSetRef() );
+ if (definition == null) {
+ throw new MappingException(
+ "Unable to find resultset-ref definition: " +
+ queryDef.getResultSetRef()
+ );
+ }
+ this.queryReturns = Arrays.asList( definition.getQueryReturns() );
+ }
+ else {
+ this.queryReturns = Arrays.asList( queryDef.getQueryReturns() );
+ }
+
+ this.querySpaces = queryDef.getQuerySpaces();
+ this.callable = queryDef.isCallable();
+ }
+
+ SQLQueryImpl(
+ final String sql,
+ final List queryReturns,
+ final Collection querySpaces,
+ final FlushMode flushMode,
+ boolean callable,
+ final SessionImplementor session,
+ ParameterMetadata parameterMetadata) {
+ // TODO : absolutely no usages of this constructor form; can it go away?
+ super( sql, flushMode, session, parameterMetadata );
+ this.queryReturns = queryReturns;
+ this.querySpaces = querySpaces;
+ this.callable = callable;
+ }
+
+ SQLQueryImpl(
+ final String sql,
+ final String returnAliases[],
+ final Class returnClasses[],
+ final LockMode[] lockModes,
+ final SessionImplementor session,
+ final Collection querySpaces,
+ final FlushMode flushMode,
+ ParameterMetadata parameterMetadata) {
+ // TODO : this constructor form is *only* used from constructor directly below us; can it go away?
+ super( sql, flushMode, session, parameterMetadata );
+ queryReturns = new ArrayList(returnAliases.length);
+ for ( int i=0; i<returnAliases.length; i++ ) {
+ NativeSQLQueryRootReturn ret = new NativeSQLQueryRootReturn(
+ returnAliases[i],
+ returnClasses[i].getName(),
+ lockModes==null ? LockMode.NONE : lockModes[i]
+ );
+ queryReturns.add(ret);
+ }
+ this.querySpaces = querySpaces;
+ this.callable = false;
+ }
+
+ SQLQueryImpl(
+ final String sql,
+ final String returnAliases[],
+ final Class returnClasses[],
+ final SessionImplementor session,
+ ParameterMetadata parameterMetadata) {
+ this( sql, returnAliases, returnClasses, null, session, null, null, parameterMetadata );
+ }
+
+ SQLQueryImpl(String sql, SessionImplementor session, ParameterMetadata parameterMetadata) {
+ super( sql, null, session, parameterMetadata );
+ queryReturns = new ArrayList();
+ querySpaces = null;
+ callable = false;
+ }
+
+ private static final NativeSQLQueryReturn[] NO_SQL_RETURNS = new NativeSQLQueryReturn[0];
+
+ private NativeSQLQueryReturn[] getQueryReturns() {
+ return ( NativeSQLQueryReturn[] ) queryReturns.toArray( NO_SQL_RETURNS );
+ }
+
+ public List list() throws HibernateException {
+ verifyParameters();
+ before();
+
+ Map namedParams = getNamedParams();
+ NativeSQLQuerySpecification spec = generateQuerySpecification( namedParams );
+
+ try {
+ return getSession().list( spec, getQueryParameters( namedParams ) );
+ }
+ finally {
+ after();
+ }
+ }
+
+ private NativeSQLQuerySpecification generateQuerySpecification(Map namedParams) {
+ return new NativeSQLQuerySpecification(
+ expandParameterLists(namedParams),
+ getQueryReturns(),
+ querySpaces
+ );
+ }
+
+ public ScrollableResults scroll(ScrollMode scrollMode) throws HibernateException {
+ verifyParameters();
+ before();
+
+ Map namedParams = getNamedParams();
+ NativeSQLQuerySpecification spec = generateQuerySpecification( namedParams );
+
+ QueryParameters qp = getQueryParameters( namedParams );
+ qp.setScrollMode( scrollMode );
+
+ try {
+ return getSession().scroll( spec, qp );
+ }
+ finally {
+ after();
+ }
+ }
+
+ public ScrollableResults scroll() throws HibernateException {
+ return scroll(ScrollMode.SCROLL_INSENSITIVE);
+ }
+
+ public Iterator iterate() throws HibernateException {
+ throw new UnsupportedOperationException("SQL queries do not currently support iteration");
+ }
+
+ public QueryParameters getQueryParameters(Map namedParams) {
+ QueryParameters qp = super.getQueryParameters(namedParams);
+ qp.setCallable(callable);
+ qp.setAutoDiscoverScalarTypes(autodiscovertypes);
+ return qp;
+ }
+
+ protected void verifyParameters() {
+ verifyParameters( callable );
+ boolean noReturns = queryReturns==null || queryReturns.isEmpty();
+ if ( noReturns ) {
+ this.autodiscovertypes = noReturns;
+ }
+ else {
+ Iterator itr = queryReturns.iterator();
+ while ( itr.hasNext() ) {
+ NativeSQLQueryReturn rtn = ( NativeSQLQueryReturn ) itr.next();
+ if ( rtn instanceof NativeSQLQueryScalarReturn ) {
+ NativeSQLQueryScalarReturn scalar = ( NativeSQLQueryScalarReturn ) rtn;
+ if ( scalar.getType() == null ) {
+ autodiscovertypes = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ public String[] getReturnAliases() throws HibernateException {
+ throw new UnsupportedOperationException("SQL queries do not currently support returning aliases");
+ }
+
+ public Type[] getReturnTypes() throws HibernateException {
+ throw new UnsupportedOperationException("not yet implemented for SQL queries");
+ }
+
+ public Query setLockMode(String alias, LockMode lockMode) {
+ throw new UnsupportedOperationException("cannot set the lock mode for a native SQL query");
+ }
+
+ protected Map getLockModes() {
+ //we never need to apply locks to the SQL
+ return CollectionHelper.EMPTY_MAP;
+ }
+
+ public SQLQuery addScalar(String columnAlias, Type type) {
+ queryReturns.add( new NativeSQLQueryScalarReturn( columnAlias, type ) );
+ return this;
+ }
+
+ public SQLQuery addScalar(String columnAlias) {
+ autodiscovertypes = true;
+ queryReturns.add( new NativeSQLQueryScalarReturn( columnAlias, null ) );
+ return this;
+ }
+
+ public SQLQuery addJoin(String alias, String path) {
+ return addJoin(alias, path, LockMode.READ);
+ }
+
+ public SQLQuery addEntity(Class entityClass) {
+ return addEntity( StringHelper.unqualify( entityClass.getName() ), entityClass );
+ }
+
+ public SQLQuery addEntity(String entityName) {
+ return addEntity( StringHelper.unqualify( entityName ), entityName );
+ }
+
+ public SQLQuery addEntity(String alias, String entityName) {
+ return addEntity(alias, entityName, LockMode.READ);
+ }
+
+ public SQLQuery addEntity(String alias, Class entityClass) {
+ return addEntity( alias, entityClass.getName() );
+ }
+
+ public SQLQuery addJoin(String alias, String path, LockMode lockMode) {
+ int loc = path.indexOf('.');
+ if ( loc < 0 ) {
+ throw new QueryException( "not a property path: " + path );
+ }
+ String ownerAlias = path.substring(0, loc);
+ String role = path.substring(loc+1);
+ queryReturns.add( new NativeSQLQueryJoinReturn(alias, ownerAlias, role, CollectionHelper.EMPTY_MAP, lockMode) );
+ return this;
+ }
+
+ public SQLQuery addEntity(String alias, String entityName, LockMode lockMode) {
+ queryReturns.add( new NativeSQLQueryRootReturn(alias, entityName, lockMode) );
+ return this;
+ }
+
+ public SQLQuery addEntity(String alias, Class entityClass, LockMode lockMode) {
+ return addEntity( alias, entityClass.getName(), lockMode );
+ }
+
+ public SQLQuery setResultSetMapping(String name) {
+ ResultSetMappingDefinition mapping = session.getFactory().getResultSetMapping( name );
+ if ( mapping == null ) {
+ throw new MappingException( "Unknown SqlResultSetMapping [" + name + "]" );
+ }
+ NativeSQLQueryReturn[] returns = mapping.getQueryReturns();
+ int length = returns.length;
+ for ( int index = 0 ; index < length ; index++ ) {
+ queryReturns.add( returns[index] );
+ }
+ return this;
+ }
+
+ public SQLQuery addSynchronizedQuerySpace(String querySpace) {
+ if ( querySpaces == null ) {
+ querySpaces = new ArrayList();
+ }
+ querySpaces.add( querySpace );
+ return this;
+ }
+
+ public SQLQuery addSynchronizedEntityName(String entityName) {
+ return addQuerySpaces( getSession().getFactory().getEntityPersister( entityName ).getQuerySpaces() );
+ }
+
+ public SQLQuery addSynchronizedEntityClass(Class entityClass) {
+ return addQuerySpaces( getSession().getFactory().getEntityPersister( entityClass.getName() ).getQuerySpaces() );
+ }
+
+ private SQLQuery addQuerySpaces(Serializable[] spaces) {
+ if ( spaces != null ) {
+ if ( querySpaces == null ) {
+ querySpaces = new ArrayList();
+ }
+ for ( int i = 0; i < spaces.length; i++ ) {
+ querySpaces.add( spaces[i] );
+ }
+ }
+ return this;
+ }
+
+ public int executeUpdate() throws HibernateException {
+ Map namedParams = getNamedParams();
+ before();
+ try {
+ return getSession().executeNativeUpdate(
+ generateQuerySpecification( namedParams ),
+ getQueryParameters( namedParams )
+ );
+ }
+ finally {
+ after();
+ }
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/impl/ScrollableResultsImpl.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/impl/ScrollableResultsImpl.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/impl/ScrollableResultsImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,251 @@
+//$Id: ScrollableResultsImpl.java 7469 2005-07-14 13:12:19Z steveebersole $
+package org.hibernate.impl;
+
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.ScrollableResults;
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.exception.JDBCExceptionHelper;
+import org.hibernate.hql.HolderInstantiator;
+import org.hibernate.loader.Loader;
+import org.hibernate.type.Type;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+/**
+ * Implementation of the <tt>ScrollableResults</tt> interface
+ * @author Gavin King
+ */
+public class ScrollableResultsImpl extends AbstractScrollableResults implements ScrollableResults {
+
+ private Object[] currentRow;
+
+ public ScrollableResultsImpl(
+ ResultSet rs,
+ PreparedStatement ps,
+ SessionImplementor sess,
+ Loader loader,
+ QueryParameters queryParameters,
+ Type[] types, HolderInstantiator holderInstantiator) throws MappingException {
+ super( rs, ps, sess, loader, queryParameters, types, holderInstantiator );
+ }
+
+ protected Object[] getCurrentRow() {
+ return currentRow;
+ }
+
+ /**
+ * @see org.hibernate.ScrollableResults#scroll(int)
+ */
+ public boolean scroll(int i) throws HibernateException {
+ try {
+ boolean result = getResultSet().relative(i);
+ prepareCurrentRow(result);
+ return result;
+ }
+ catch (SQLException sqle) {
+ throw JDBCExceptionHelper.convert(
+ getSession().getFactory().getSQLExceptionConverter(),
+ sqle,
+ "could not advance using scroll()"
+ );
+ }
+ }
+
+ /**
+ * @see org.hibernate.ScrollableResults#first()
+ */
+ public boolean first() throws HibernateException {
+ try {
+ boolean result = getResultSet().first();
+ prepareCurrentRow(result);
+ return result;
+ }
+ catch (SQLException sqle) {
+ throw JDBCExceptionHelper.convert(
+ getSession().getFactory().getSQLExceptionConverter(),
+ sqle,
+ "could not advance using first()"
+ );
+ }
+ }
+
+ /**
+ * @see org.hibernate.ScrollableResults#last()
+ */
+ public boolean last() throws HibernateException {
+ try {
+ boolean result = getResultSet().last();
+ prepareCurrentRow(result);
+ return result;
+ }
+ catch (SQLException sqle) {
+ throw JDBCExceptionHelper.convert(
+ getSession().getFactory().getSQLExceptionConverter(),
+ sqle,
+ "could not advance using last()"
+ );
+ }
+ }
+
+ /**
+ * @see org.hibernate.ScrollableResults#next()
+ */
+ public boolean next() throws HibernateException {
+ try {
+ boolean result = getResultSet().next();
+ prepareCurrentRow(result);
+ return result;
+ }
+ catch (SQLException sqle) {
+ throw JDBCExceptionHelper.convert(
+ getSession().getFactory().getSQLExceptionConverter(),
+ sqle,
+ "could not advance using next()"
+ );
+ }
+ }
+
+ /**
+ * @see org.hibernate.ScrollableResults#previous()
+ */
+ public boolean previous() throws HibernateException {
+ try {
+ boolean result = getResultSet().previous();
+ prepareCurrentRow(result);
+ return result;
+ }
+ catch (SQLException sqle) {
+ throw JDBCExceptionHelper.convert(
+ getSession().getFactory().getSQLExceptionConverter(),
+ sqle,
+ "could not advance using previous()"
+ );
+ }
+ }
+
+ /**
+ * @see org.hibernate.ScrollableResults#afterLast()
+ */
+ public void afterLast() throws HibernateException {
+ try {
+ getResultSet().afterLast();
+ }
+ catch (SQLException sqle) {
+ throw JDBCExceptionHelper.convert(
+ getSession().getFactory().getSQLExceptionConverter(),
+ sqle,
+ "exception calling afterLast()"
+ );
+ }
+ }
+
+ /**
+ * @see org.hibernate.ScrollableResults#beforeFirst()
+ */
+ public void beforeFirst() throws HibernateException {
+ try {
+ getResultSet().beforeFirst();
+ }
+ catch (SQLException sqle) {
+ throw JDBCExceptionHelper.convert(
+ getSession().getFactory().getSQLExceptionConverter(),
+ sqle,
+ "exception calling beforeFirst()"
+ );
+ }
+ }
+
+ /**
+ * @see org.hibernate.ScrollableResults#isFirst()
+ */
+ public boolean isFirst() throws HibernateException {
+ try {
+ return getResultSet().isFirst();
+ }
+ catch (SQLException sqle) {
+ throw JDBCExceptionHelper.convert(
+ getSession().getFactory().getSQLExceptionConverter(),
+ sqle,
+ "exception calling isFirst()"
+ );
+ }
+ }
+
+ /**
+ * @see org.hibernate.ScrollableResults#isLast()
+ */
+ public boolean isLast() throws HibernateException {
+ try {
+ return getResultSet().isLast();
+ }
+ catch (SQLException sqle) {
+ throw JDBCExceptionHelper.convert(
+ getSession().getFactory().getSQLExceptionConverter(),
+ sqle,
+ "exception calling isLast()"
+ );
+ }
+ }
+
+ public int getRowNumber() throws HibernateException {
+ try {
+ return getResultSet().getRow()-1;
+ }
+ catch (SQLException sqle) {
+ throw JDBCExceptionHelper.convert(
+ getSession().getFactory().getSQLExceptionConverter(),
+ sqle,
+ "exception calling getRow()"
+ );
+ }
+ }
+
+ public boolean setRowNumber(int rowNumber) throws HibernateException {
+ if (rowNumber>=0) rowNumber++;
+ try {
+ boolean result = getResultSet().absolute(rowNumber);
+ prepareCurrentRow(result);
+ return result;
+ }
+ catch (SQLException sqle) {
+ throw JDBCExceptionHelper.convert(
+ getSession().getFactory().getSQLExceptionConverter(),
+ sqle,
+ "could not advance using absolute()"
+ );
+ }
+ }
+
+ private void prepareCurrentRow(boolean underlyingScrollSuccessful)
+ throws HibernateException {
+
+ if (!underlyingScrollSuccessful) {
+ currentRow = null;
+ return;
+ }
+
+ Object result = getLoader().loadSingleRow(
+ getResultSet(),
+ getSession(),
+ getQueryParameters(),
+ false
+ );
+ if ( result != null && result.getClass().isArray() ) {
+ currentRow = (Object[]) result;
+ }
+ else {
+ currentRow = new Object[] { result };
+ }
+
+ if ( getHolderInstantiator() != null ) {
+ currentRow = new Object[] { getHolderInstantiator().instantiate(currentRow) };
+ }
+
+ afterScrollOperation();
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/impl/SessionFactoryImpl.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/impl/SessionFactoryImpl.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/impl/SessionFactoryImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1069 @@
+//$Id: SessionFactoryImpl.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.impl;
+
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.sql.Connection;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import javax.naming.NamingException;
+import javax.naming.Reference;
+import javax.naming.StringRefAddr;
+import javax.transaction.TransactionManager;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.AssertionFailure;
+import org.hibernate.ConnectionReleaseMode;
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.Interceptor;
+import org.hibernate.MappingException;
+import org.hibernate.ObjectNotFoundException;
+import org.hibernate.QueryException;
+import org.hibernate.SessionFactory;
+import org.hibernate.StatelessSession;
+import org.hibernate.engine.query.sql.NativeSQLQuerySpecification;
+import org.hibernate.cache.CacheKey;
+import org.hibernate.cache.QueryCache;
+import org.hibernate.cache.UpdateTimestampsCache;
+import org.hibernate.cache.Region;
+import org.hibernate.cache.EntityRegion;
+import org.hibernate.cache.CollectionRegion;
+import org.hibernate.cache.impl.CacheDataDescriptionImpl;
+import org.hibernate.cache.access.EntityRegionAccessStrategy;
+import org.hibernate.cache.access.AccessType;
+import org.hibernate.cache.access.CollectionRegionAccessStrategy;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.cfg.Settings;
+import org.hibernate.connection.ConnectionProvider;
+import org.hibernate.context.CurrentSessionContext;
+import org.hibernate.context.JTASessionContext;
+import org.hibernate.context.ManagedSessionContext;
+import org.hibernate.context.ThreadLocalSessionContext;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.function.SQLFunctionRegistry;
+import org.hibernate.engine.FilterDefinition;
+import org.hibernate.engine.Mapping;
+import org.hibernate.engine.NamedQueryDefinition;
+import org.hibernate.engine.NamedSQLQueryDefinition;
+import org.hibernate.engine.ResultSetMappingDefinition;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.query.QueryPlanCache;
+import org.hibernate.event.EventListeners;
+import org.hibernate.exception.SQLExceptionConverter;
+import org.hibernate.id.IdentifierGenerator;
+import org.hibernate.id.UUIDHexGenerator;
+import org.hibernate.jdbc.BatcherFactory;
+import org.hibernate.mapping.Collection;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.RootClass;
+import org.hibernate.metadata.ClassMetadata;
+import org.hibernate.metadata.CollectionMetadata;
+import org.hibernate.persister.PersisterFactory;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.persister.entity.Queryable;
+import org.hibernate.pretty.MessageHelper;
+import org.hibernate.proxy.EntityNotFoundDelegate;
+import org.hibernate.stat.Statistics;
+import org.hibernate.stat.StatisticsImpl;
+import org.hibernate.stat.StatisticsImplementor;
+import org.hibernate.tool.hbm2ddl.SchemaExport;
+import org.hibernate.tool.hbm2ddl.SchemaUpdate;
+import org.hibernate.tool.hbm2ddl.SchemaValidator;
+import org.hibernate.transaction.TransactionFactory;
+import org.hibernate.type.AssociationType;
+import org.hibernate.type.Type;
+import org.hibernate.util.CollectionHelper;
+import org.hibernate.util.ReflectHelper;
+
+
+/**
+ * Concrete implementation of the <tt>SessionFactory</tt> interface. Has the following
+ * responsibilites
+ * <ul>
+ * <li>caches configuration settings (immutably)
+ * <li>caches "compiled" mappings ie. <tt>EntityPersister</tt>s and
+ * <tt>CollectionPersister</tt>s (immutable)
+ * <li>caches "compiled" queries (memory sensitive cache)
+ * <li>manages <tt>PreparedStatement</tt>s
+ * <li> delegates JDBC <tt>Connection</tt> management to the <tt>ConnectionProvider</tt>
+ * <li>factory for instances of <tt>SessionImpl</tt>
+ * </ul>
+ * This class must appear immutable to clients, even if it does all kinds of caching
+ * and pooling under the covers. It is crucial that the class is not only thread
+ * safe, but also highly concurrent. Synchronization must be used extremely sparingly.
+ *
+ * @see org.hibernate.connection.ConnectionProvider
+ * @see org.hibernate.classic.Session
+ * @see org.hibernate.hql.QueryTranslator
+ * @see org.hibernate.persister.entity.EntityPersister
+ * @see org.hibernate.persister.collection.CollectionPersister
+ * @author Gavin King
+ */
+public final class SessionFactoryImpl implements SessionFactory, SessionFactoryImplementor {
+
+ private final String name;
+ private final String uuid;
+
+ private final transient Map entityPersisters;
+ private final transient Map classMetadata;
+ private final transient Map collectionPersisters;
+ private final transient Map collectionMetadata;
+ private final transient Map collectionRolesByEntityParticipant;
+ private final transient Map identifierGenerators;
+ private final transient Map namedQueries;
+ private final transient Map namedSqlQueries;
+ private final transient Map sqlResultSetMappings;
+ private final transient Map filters;
+ private final transient Map imports;
+ private final transient Interceptor interceptor;
+ private final transient Settings settings;
+ private final transient Properties properties;
+ private transient SchemaExport schemaExport;
+ private final transient TransactionManager transactionManager;
+ private final transient QueryCache queryCache;
+ private final transient UpdateTimestampsCache updateTimestampsCache;
+ private final transient Map queryCaches;
+ private final transient Map allCacheRegions = new HashMap();
+ private final transient StatisticsImpl statistics = new StatisticsImpl(this);
+ private final transient EventListeners eventListeners;
+ private final transient CurrentSessionContext currentSessionContext;
+ private final transient EntityNotFoundDelegate entityNotFoundDelegate;
+ private final transient SQLFunctionRegistry sqlFunctionRegistry;
+
+ private final QueryPlanCache queryPlanCache = new QueryPlanCache( this );
+
+ private transient boolean isClosed = false;
+
+
+ private static final IdentifierGenerator UUID_GENERATOR = new UUIDHexGenerator();
+
+ private static final Log log = LogFactory.getLog(SessionFactoryImpl.class);
+
+ public SessionFactoryImpl(
+ Configuration cfg,
+ Mapping mapping,
+ Settings settings,
+ EventListeners listeners)
+ throws HibernateException {
+
+ log.info("building session factory");
+
+ this.properties = new Properties();
+ this.properties.putAll( cfg.getProperties() );
+ this.interceptor = cfg.getInterceptor();
+ this.settings = settings;
+ this.sqlFunctionRegistry = new SQLFunctionRegistry(settings.getDialect(), cfg.getSqlFunctions());
+ this.eventListeners = listeners;
+ this.filters = new HashMap();
+ this.filters.putAll( cfg.getFilterDefinitions() );
+
+ if ( log.isDebugEnabled() ) {
+ log.debug("Session factory constructed with filter configurations : " + filters);
+ }
+
+ if ( log.isDebugEnabled() ) {
+ log.debug(
+ "instantiating session factory with properties: " + properties
+ );
+ }
+
+ // Caches
+ settings.getRegionFactory().start( settings, properties );
+
+ //Generators:
+
+ identifierGenerators = new HashMap();
+ Iterator classes = cfg.getClassMappings();
+ while ( classes.hasNext() ) {
+ PersistentClass model = (PersistentClass) classes.next();
+ if ( !model.isInherited() ) {
+ IdentifierGenerator generator = model.getIdentifier().createIdentifierGenerator(
+ settings.getDialect(),
+ settings.getDefaultCatalogName(),
+ settings.getDefaultSchemaName(),
+ (RootClass) model
+ );
+ identifierGenerators.put( model.getEntityName(), generator );
+ }
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////
+ // Prepare persisters and link them up with their cache
+ // region/access-strategy
+
+ entityPersisters = new HashMap();
+ Map entityAccessStrategies = new HashMap();
+ Map classMeta = new HashMap();
+ classes = cfg.getClassMappings();
+ while ( classes.hasNext() ) {
+ final PersistentClass model = (PersistentClass) classes.next();
+ model.prepareTemporaryTables( mapping, settings.getDialect() );
+ // cache region is defined by the root-class in the hierarchy...
+ final String cacheRegionName = model.getRootClass().getCacheRegionName();
+ EntityRegionAccessStrategy accessStrategy = ( EntityRegionAccessStrategy ) entityAccessStrategies.get( cacheRegionName );
+ if ( accessStrategy == null && settings.isSecondLevelCacheEnabled() ) {
+ final AccessType accessType = AccessType.parse( model.getCacheConcurrencyStrategy() );
+ if ( accessType != null ) {
+ log.trace( "Building cache for entity data [" + model.getEntityName() + "]" );
+ EntityRegion entityRegion = settings.getRegionFactory().buildEntityRegion( cacheRegionName, properties, CacheDataDescriptionImpl.decode( model ) );
+ accessStrategy = entityRegion.buildAccessStrategy( accessType );
+ entityAccessStrategies.put( cacheRegionName, accessStrategy );
+ allCacheRegions.put( cacheRegionName, entityRegion );
+ }
+ }
+ EntityPersister cp = PersisterFactory.createClassPersister( model, accessStrategy, this, mapping );
+ entityPersisters.put( model.getEntityName(), cp );
+ classMeta.put( model.getEntityName(), cp.getClassMetadata() );
+ }
+ classMetadata = Collections.unmodifiableMap(classMeta);
+
+ Map tmpEntityToCollectionRoleMap = new HashMap();
+ collectionPersisters = new HashMap();
+ Iterator collections = cfg.getCollectionMappings();
+ while ( collections.hasNext() ) {
+ Collection model = (Collection) collections.next();
+ final String cacheRegionName = model.getCacheRegionName();
+ final AccessType accessType = AccessType.parse( model.getCacheConcurrencyStrategy() );
+ CollectionRegionAccessStrategy accessStrategy = null;
+ if ( accessType != null && settings.isSecondLevelCacheEnabled() ) {
+ log.trace( "Building cache for collection data [" + model.getRole() + "]" );
+ CollectionRegion collectionRegion = settings.getRegionFactory().buildCollectionRegion( cacheRegionName, properties, CacheDataDescriptionImpl.decode( model ) );
+ accessStrategy = collectionRegion.buildAccessStrategy( accessType );
+ entityAccessStrategies.put( cacheRegionName, accessStrategy );
+ allCacheRegions.put( cacheRegionName, collectionRegion );
+ }
+ CollectionPersister persister = PersisterFactory.createCollectionPersister( cfg, model, accessStrategy, this) ;
+ collectionPersisters.put( model.getRole(), persister.getCollectionMetadata() );
+ Type indexType = persister.getIndexType();
+ if ( indexType != null && indexType.isAssociationType() && !indexType.isAnyType() ) {
+ String entityName = ( ( AssociationType ) indexType ).getAssociatedEntityName( this );
+ Set roles = ( Set ) tmpEntityToCollectionRoleMap.get( entityName );
+ if ( roles == null ) {
+ roles = new HashSet();
+ tmpEntityToCollectionRoleMap.put( entityName, roles );
+ }
+ roles.add( persister.getRole() );
+ }
+ Type elementType = persister.getElementType();
+ if ( elementType.isAssociationType() && !elementType.isAnyType() ) {
+ String entityName = ( ( AssociationType ) elementType ).getAssociatedEntityName( this );
+ Set roles = ( Set ) tmpEntityToCollectionRoleMap.get( entityName );
+ if ( roles == null ) {
+ roles = new HashSet();
+ tmpEntityToCollectionRoleMap.put( entityName, roles );
+ }
+ roles.add( persister.getRole() );
+ }
+ }
+ collectionMetadata = Collections.unmodifiableMap(collectionPersisters);
+ Iterator itr = tmpEntityToCollectionRoleMap.entrySet().iterator();
+ while ( itr.hasNext() ) {
+ final Map.Entry entry = ( Map.Entry ) itr.next();
+ entry.setValue( Collections.unmodifiableSet( ( Set ) entry.getValue() ) );
+ }
+ collectionRolesByEntityParticipant = Collections.unmodifiableMap( tmpEntityToCollectionRoleMap );
+
+ //Named Queries:
+ namedQueries = new HashMap( cfg.getNamedQueries() );
+ namedSqlQueries = new HashMap( cfg.getNamedSQLQueries() );
+ sqlResultSetMappings = new HashMap( cfg.getSqlResultSetMappings() );
+ imports = new HashMap( cfg.getImports() );
+
+ // after *all* persisters and named queries are registered
+ Iterator iter = entityPersisters.values().iterator();
+ while ( iter.hasNext() ) {
+ ( (EntityPersister) iter.next() ).postInstantiate();
+ }
+ iter = collectionPersisters.values().iterator();
+ while ( iter.hasNext() ) {
+ ( (CollectionPersister) iter.next() ).postInstantiate();
+ }
+
+ //JNDI + Serialization:
+
+ name = settings.getSessionFactoryName();
+ try {
+ uuid = (String) UUID_GENERATOR.generate(null, null);
+ }
+ catch (Exception e) {
+ throw new AssertionFailure("Could not generate UUID");
+ }
+ SessionFactoryObjectFactory.addInstance(uuid, name, this, properties);
+
+ log.debug("instantiated session factory");
+
+ if ( settings.isAutoCreateSchema() ) {
+ new SchemaExport( cfg, settings ).create( false, true );
+ }
+ if ( settings.isAutoUpdateSchema() ) {
+ new SchemaUpdate( cfg, settings ).execute( false, true );
+ }
+ if ( settings.isAutoValidateSchema() ) {
+ new SchemaValidator( cfg, settings ).validate();
+ }
+ if ( settings.isAutoDropSchema() ) {
+ schemaExport = new SchemaExport( cfg, settings );
+ }
+
+ if ( settings.getTransactionManagerLookup()!=null ) {
+ log.debug("obtaining JTA TransactionManager");
+ transactionManager = settings.getTransactionManagerLookup().getTransactionManager(properties);
+ }
+ else {
+ if ( settings.getTransactionFactory().isTransactionManagerRequired() ) {
+ throw new HibernateException("The chosen transaction strategy requires access to the JTA TransactionManager");
+ }
+ transactionManager = null;
+ }
+
+ currentSessionContext = buildCurrentSessionContext();
+
+ if ( settings.isQueryCacheEnabled() ) {
+ updateTimestampsCache = new UpdateTimestampsCache(settings, properties);
+ queryCache = settings.getQueryCacheFactory()
+ .getQueryCache(null, updateTimestampsCache, settings, properties);
+ queryCaches = new HashMap();
+ allCacheRegions.put( updateTimestampsCache.getRegion().getName(), updateTimestampsCache.getRegion() );
+ allCacheRegions.put( queryCache.getRegion().getName(), queryCache.getRegion() );
+ }
+ else {
+ updateTimestampsCache = null;
+ queryCache = null;
+ queryCaches = null;
+ }
+
+ //checking for named queries
+ if ( settings.isNamedQueryStartupCheckingEnabled() ) {
+ Map errors = checkNamedQueries();
+ if ( !errors.isEmpty() ) {
+ Set keys = errors.keySet();
+ StringBuffer failingQueries = new StringBuffer( "Errors in named queries: " );
+ for ( Iterator iterator = keys.iterator() ; iterator.hasNext() ; ) {
+ String queryName = ( String ) iterator.next();
+ HibernateException e = ( HibernateException ) errors.get( queryName );
+ failingQueries.append( queryName );
+ if ( iterator.hasNext() ) {
+ failingQueries.append( ", " );
+ }
+ log.error( "Error in named query: " + queryName, e );
+ }
+ throw new HibernateException( failingQueries.toString() );
+ }
+ }
+
+ //stats
+ getStatistics().setStatisticsEnabled( settings.isStatisticsEnabled() );
+
+ // EntityNotFoundDelegate
+ EntityNotFoundDelegate entityNotFoundDelegate = cfg.getEntityNotFoundDelegate();
+ if ( entityNotFoundDelegate == null ) {
+ entityNotFoundDelegate = new EntityNotFoundDelegate() {
+ public void handleEntityNotFound(String entityName, Serializable id) {
+ throw new ObjectNotFoundException( id, entityName );
+ }
+ };
+ }
+ this.entityNotFoundDelegate = entityNotFoundDelegate;
+ }
+
+ public QueryPlanCache getQueryPlanCache() {
+ return queryPlanCache;
+ }
+
+ private Map checkNamedQueries() throws HibernateException {
+ Map errors = new HashMap();
+
+ // Check named HQL queries
+ log.debug("Checking " + namedQueries.size() + " named HQL queries");
+ Iterator itr = namedQueries.entrySet().iterator();
+ while ( itr.hasNext() ) {
+ final Map.Entry entry = ( Map.Entry ) itr.next();
+ final String queryName = ( String ) entry.getKey();
+ final NamedQueryDefinition qd = ( NamedQueryDefinition ) entry.getValue();
+ // this will throw an error if there's something wrong.
+ try {
+ log.debug("Checking named query: " + queryName);
+ //TODO: BUG! this currently fails for named queries for non-POJO entities
+ queryPlanCache.getHQLQueryPlan( qd.getQueryString(), false, CollectionHelper.EMPTY_MAP );
+ }
+ catch ( QueryException e ) {
+ errors.put( queryName, e );
+ }
+ catch ( MappingException e ) {
+ errors.put( queryName, e );
+ }
+ }
+
+ log.debug("Checking " + namedSqlQueries.size() + " named SQL queries");
+ itr = namedSqlQueries.entrySet().iterator();
+ while ( itr.hasNext() ) {
+ final Map.Entry entry = ( Map.Entry ) itr.next();
+ final String queryName = ( String ) entry.getKey();
+ final NamedSQLQueryDefinition qd = ( NamedSQLQueryDefinition ) entry.getValue();
+ // this will throw an error if there's something wrong.
+ try {
+ log.debug("Checking named SQL query: " + queryName);
+ // TODO : would be really nice to cache the spec on the query-def so as to not have to re-calc the hash;
+ // currently not doable though because of the resultset-ref stuff...
+ NativeSQLQuerySpecification spec;
+ if ( qd.getResultSetRef() != null ) {
+ ResultSetMappingDefinition definition = ( ResultSetMappingDefinition ) sqlResultSetMappings.get( qd.getResultSetRef() );
+ if ( definition == null ) {
+ throw new MappingException( "Unable to find resultset-ref definition: " + qd.getResultSetRef() );
+ }
+ spec = new NativeSQLQuerySpecification(
+ qd.getQueryString(),
+ definition.getQueryReturns(),
+ qd.getQuerySpaces()
+ );
+ }
+ else {
+ spec = new NativeSQLQuerySpecification(
+ qd.getQueryString(),
+ qd.getQueryReturns(),
+ qd.getQuerySpaces()
+ );
+ }
+ queryPlanCache.getNativeSQLQueryPlan( spec );
+ }
+ catch ( QueryException e ) {
+ errors.put( queryName, e );
+ }
+ catch ( MappingException e ) {
+ errors.put( queryName, e );
+ }
+ }
+
+ return errors;
+ }
+
+ public StatelessSession openStatelessSession() {
+ return new StatelessSessionImpl( null, this );
+ }
+
+ public StatelessSession openStatelessSession(Connection connection) {
+ return new StatelessSessionImpl( connection, this );
+ }
+
+ private SessionImpl openSession(
+ Connection connection,
+ boolean autoClose,
+ long timestamp,
+ Interceptor sessionLocalInterceptor
+ ) {
+ return new SessionImpl(
+ connection,
+ this,
+ autoClose,
+ timestamp,
+ sessionLocalInterceptor == null ? interceptor : sessionLocalInterceptor,
+ settings.getDefaultEntityMode(),
+ settings.isFlushBeforeCompletionEnabled(),
+ settings.isAutoCloseSessionEnabled(),
+ settings.getConnectionReleaseMode()
+ );
+ }
+
+ public org.hibernate.classic.Session openSession(Connection connection, Interceptor sessionLocalInterceptor) {
+ return openSession(connection, false, Long.MIN_VALUE, sessionLocalInterceptor);
+ }
+
+ public org.hibernate.classic.Session openSession(Interceptor sessionLocalInterceptor)
+ throws HibernateException {
+ // note that this timestamp is not correct if the connection provider
+ // returns an older JDBC connection that was associated with a
+ // transaction that was already begun before openSession() was called
+ // (don't know any possible solution to this!)
+ long timestamp = settings.getRegionFactory().nextTimestamp();
+ return openSession( null, true, timestamp, sessionLocalInterceptor );
+ }
+
+ public org.hibernate.classic.Session openSession(Connection connection) {
+ return openSession(connection, interceptor); //prevents this session from adding things to cache
+ }
+
+ public org.hibernate.classic.Session openSession() throws HibernateException {
+ return openSession(interceptor);
+ }
+
+ public org.hibernate.classic.Session openTemporarySession() throws HibernateException {
+ return new SessionImpl(
+ null,
+ this,
+ true,
+ settings.getRegionFactory().nextTimestamp(),
+ interceptor,
+ settings.getDefaultEntityMode(),
+ false,
+ false,
+ ConnectionReleaseMode.AFTER_STATEMENT
+ );
+ }
+
+ public org.hibernate.classic.Session openSession(
+ final Connection connection,
+ final boolean flushBeforeCompletionEnabled,
+ final boolean autoCloseSessionEnabled,
+ final ConnectionReleaseMode connectionReleaseMode) throws HibernateException {
+ return new SessionImpl(
+ connection,
+ this,
+ true,
+ settings.getRegionFactory().nextTimestamp(),
+ interceptor,
+ settings.getDefaultEntityMode(),
+ flushBeforeCompletionEnabled,
+ autoCloseSessionEnabled,
+ connectionReleaseMode
+ );
+ }
+
+ public org.hibernate.classic.Session getCurrentSession() throws HibernateException {
+ if ( currentSessionContext == null ) {
+ throw new HibernateException( "No CurrentSessionContext configured!" );
+ }
+ return currentSessionContext.currentSession();
+ }
+
+ public EntityPersister getEntityPersister(String entityName) throws MappingException {
+ EntityPersister result = (EntityPersister) entityPersisters.get(entityName);
+ if (result==null) {
+ throw new MappingException( "Unknown entity: " + entityName );
+ }
+ return result;
+ }
+
+ public CollectionPersister getCollectionPersister(String role) throws MappingException {
+ CollectionPersister result = (CollectionPersister) collectionPersisters.get(role);
+ if (result==null) {
+ throw new MappingException( "Unknown collection role: " + role );
+ }
+ return result;
+ }
+
+ public Settings getSettings() {
+ return settings;
+ }
+
+ public Dialect getDialect() {
+ return settings.getDialect();
+ }
+
+ public Interceptor getInterceptor()
+ {
+ return interceptor;
+ }
+
+ public TransactionFactory getTransactionFactory() {
+ return settings.getTransactionFactory();
+ }
+
+ public TransactionManager getTransactionManager() {
+ return transactionManager;
+ }
+
+ public SQLExceptionConverter getSQLExceptionConverter() {
+ return settings.getSQLExceptionConverter();
+ }
+
+ public Set getCollectionRolesByEntityParticipant(String entityName) {
+ return ( Set ) collectionRolesByEntityParticipant.get( entityName );
+ }
+
+ // from javax.naming.Referenceable
+ public Reference getReference() throws NamingException {
+ log.debug("Returning a Reference to the SessionFactory");
+ return new Reference(
+ SessionFactoryImpl.class.getName(),
+ new StringRefAddr("uuid", uuid),
+ SessionFactoryObjectFactory.class.getName(),
+ null
+ );
+ }
+
+ private Object readResolve() throws ObjectStreamException {
+ log.trace("Resolving serialized SessionFactory");
+ // look for the instance by uuid
+ Object result = SessionFactoryObjectFactory.getInstance(uuid);
+ if (result==null) {
+ // in case we were deserialized in a different JVM, look for an instance with the same name
+ // (alternatively we could do an actual JNDI lookup here....)
+ result = SessionFactoryObjectFactory.getNamedInstance(name);
+ if (result==null) {
+ throw new InvalidObjectException("Could not find a SessionFactory named: " + name);
+ }
+ else {
+ log.debug("resolved SessionFactory by name");
+ }
+ }
+ else {
+ log.debug("resolved SessionFactory by uid");
+ }
+ return result;
+ }
+
+ public NamedQueryDefinition getNamedQuery(String queryName) {
+ return (NamedQueryDefinition) namedQueries.get(queryName);
+ }
+
+ public NamedSQLQueryDefinition getNamedSQLQuery(String queryName) {
+ return (NamedSQLQueryDefinition) namedSqlQueries.get(queryName);
+ }
+
+ public ResultSetMappingDefinition getResultSetMapping(String resultSetName) {
+ return (ResultSetMappingDefinition) sqlResultSetMappings.get(resultSetName);
+ }
+
+ public Type getIdentifierType(String className) throws MappingException {
+ return getEntityPersister(className).getIdentifierType();
+ }
+ public String getIdentifierPropertyName(String className) throws MappingException {
+ return getEntityPersister(className).getIdentifierPropertyName();
+ }
+
+ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+ log.trace("deserializing");
+ in.defaultReadObject();
+ log.debug("deserialized: " + uuid);
+ }
+
+ private void writeObject(ObjectOutputStream out) throws IOException {
+ log.debug("serializing: " + uuid);
+ out.defaultWriteObject();
+ log.trace("serialized");
+ }
+
+ public Type[] getReturnTypes(String queryString) throws HibernateException {
+ return queryPlanCache.getHQLQueryPlan( queryString, false, CollectionHelper.EMPTY_MAP ).getReturnMetadata().getReturnTypes();
+ }
+
+ public String[] getReturnAliases(String queryString) throws HibernateException {
+ return queryPlanCache.getHQLQueryPlan( queryString, false, CollectionHelper.EMPTY_MAP ).getReturnMetadata().getReturnAliases();
+ }
+
+ public ClassMetadata getClassMetadata(Class persistentClass) throws HibernateException {
+ return getClassMetadata( persistentClass.getName() );
+ }
+
+ public CollectionMetadata getCollectionMetadata(String roleName) throws HibernateException {
+ return (CollectionMetadata) collectionMetadata.get(roleName);
+ }
+
+ public ClassMetadata getClassMetadata(String entityName) throws HibernateException {
+ return (ClassMetadata) classMetadata.get(entityName);
+ }
+
+ /**
+ * Return the names of all persistent (mapped) classes that extend or implement the
+ * given class or interface, accounting for implicit/explicit polymorphism settings
+ * and excluding mapped subclasses/joined-subclasses of other classes in the result.
+ */
+ public String[] getImplementors(String className) throws MappingException {
+
+ final Class clazz;
+ try {
+ clazz = ReflectHelper.classForName(className);
+ }
+ catch (ClassNotFoundException cnfe) {
+ return new String[] { className }; //for a dynamic-class
+ }
+
+ ArrayList results = new ArrayList();
+ Iterator iter = entityPersisters.values().iterator();
+ while ( iter.hasNext() ) {
+ //test this entity to see if we must query it
+ EntityPersister testPersister = (EntityPersister) iter.next();
+ if ( testPersister instanceof Queryable ) {
+ Queryable testQueryable = (Queryable) testPersister;
+ String testClassName = testQueryable.getEntityName();
+ boolean isMappedClass = className.equals(testClassName);
+ if ( testQueryable.isExplicitPolymorphism() ) {
+ if ( isMappedClass ) {
+ return new String[] {className}; //NOTE EARLY EXIT
+ }
+ }
+ else {
+ if (isMappedClass) {
+ results.add(testClassName);
+ }
+ else {
+ final Class mappedClass = testQueryable.getMappedClass( EntityMode.POJO );
+ if ( mappedClass!=null && clazz.isAssignableFrom( mappedClass ) ) {
+ final boolean assignableSuperclass;
+ if ( testQueryable.isInherited() ) {
+ Class mappedSuperclass = getEntityPersister( testQueryable.getMappedSuperclass() ).getMappedClass( EntityMode.POJO);
+ assignableSuperclass = clazz.isAssignableFrom(mappedSuperclass);
+ }
+ else {
+ assignableSuperclass = false;
+ }
+ if ( !assignableSuperclass ) {
+ results.add( testClassName );
+ }
+ }
+ }
+ }
+ }
+ }
+ return (String[]) results.toArray( new String[ results.size() ] );
+ }
+
+ public String getImportedClassName(String className) {
+ String result = (String) imports.get(className);
+ if (result==null) {
+ try {
+ ReflectHelper.classForName(className);
+ return className;
+ }
+ catch (ClassNotFoundException cnfe) {
+ return null;
+ }
+ }
+ else {
+ return result;
+ }
+ }
+
+ public Map getAllClassMetadata() throws HibernateException {
+ return classMetadata;
+ }
+
+ public Map getAllCollectionMetadata() throws HibernateException {
+ return collectionMetadata;
+ }
+
+ /**
+ * Closes the session factory, releasing all held resources.
+ *
+ * <ol>
+ * <li>cleans up used cache regions and "stops" the cache provider.
+ * <li>close the JDBC connection
+ * <li>remove the JNDI binding
+ * </ol>
+ *
+ * Note: Be aware that the sessionfactory instance still can
+ * be a "heavy" object memory wise after close() has been called. Thus
+ * it is important to not keep referencing the instance to let the garbage
+ * collector release the memory.
+ */
+ public void close() throws HibernateException {
+
+ log.info("closing");
+
+ isClosed = true;
+
+ Iterator iter = entityPersisters.values().iterator();
+ while ( iter.hasNext() ) {
+ EntityPersister p = (EntityPersister) iter.next();
+ if ( p.hasCache() ) {
+ p.getCacheAccessStrategy().destroy();
+ }
+ }
+
+ iter = collectionPersisters.values().iterator();
+ while ( iter.hasNext() ) {
+ CollectionPersister p = (CollectionPersister) iter.next();
+ if ( p.hasCache() ) {
+ p.getCacheAccessStrategy().destroy();
+ }
+ }
+
+ if ( settings.isQueryCacheEnabled() ) {
+ queryCache.destroy();
+
+ iter = queryCaches.values().iterator();
+ while ( iter.hasNext() ) {
+ QueryCache cache = (QueryCache) iter.next();
+ cache.destroy();
+ }
+ updateTimestampsCache.destroy();
+ }
+
+ settings.getRegionFactory().stop();
+
+ try {
+ settings.getConnectionProvider().close();
+ }
+ finally {
+ SessionFactoryObjectFactory.removeInstance(uuid, name, properties);
+ }
+
+ if ( settings.isAutoDropSchema() ) {
+ schemaExport.drop( false, true );
+ }
+
+ }
+
+ public void evictEntity(String entityName, Serializable id) throws HibernateException {
+ EntityPersister p = getEntityPersister( entityName );
+ if ( p.hasCache() ) {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "evicting second-level cache: " + MessageHelper.infoString(p, id, this) );
+ }
+ CacheKey cacheKey = new CacheKey( id, p.getIdentifierType(), p.getRootEntityName(), EntityMode.POJO, this );
+ p.getCacheAccessStrategy().evict( cacheKey );
+ }
+ }
+
+ public void evictEntity(String entityName) throws HibernateException {
+ EntityPersister p = getEntityPersister( entityName );
+ if ( p.hasCache() ) {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "evicting second-level cache: " + p.getEntityName() );
+ }
+ p.getCacheAccessStrategy().evictAll();
+ }
+ }
+
+ public void evict(Class persistentClass, Serializable id) throws HibernateException {
+ EntityPersister p = getEntityPersister( persistentClass.getName() );
+ if ( p.hasCache() ) {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "evicting second-level cache: " + MessageHelper.infoString(p, id, this) );
+ }
+ CacheKey cacheKey = new CacheKey( id, p.getIdentifierType(), p.getRootEntityName(), EntityMode.POJO, this );
+ p.getCacheAccessStrategy().evict( cacheKey );
+ }
+ }
+
+ public void evict(Class persistentClass) throws HibernateException {
+ EntityPersister p = getEntityPersister( persistentClass.getName() );
+ if ( p.hasCache() ) {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "evicting second-level cache: " + p.getEntityName() );
+ }
+ p.getCacheAccessStrategy().evictAll();
+ }
+ }
+
+ public void evictCollection(String roleName, Serializable id) throws HibernateException {
+ CollectionPersister p = getCollectionPersister( roleName );
+ if ( p.hasCache() ) {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "evicting second-level cache: " + MessageHelper.collectionInfoString(p, id, this) );
+ }
+ CacheKey cacheKey = new CacheKey( id, p.getKeyType(), p.getRole(), EntityMode.POJO, this );
+ p.getCacheAccessStrategy().evict( cacheKey );
+ }
+ }
+
+ public void evictCollection(String roleName) throws HibernateException {
+ CollectionPersister p = getCollectionPersister( roleName );
+ if ( p.hasCache() ) {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "evicting second-level cache: " + p.getRole() );
+ }
+ p.getCacheAccessStrategy().evictAll();
+ }
+ }
+
+ public Type getReferencedPropertyType(String className, String propertyName)
+ throws MappingException {
+ return getEntityPersister(className).getPropertyType(propertyName);
+ }
+
+ public ConnectionProvider getConnectionProvider() {
+ return settings.getConnectionProvider();
+ }
+
+ public UpdateTimestampsCache getUpdateTimestampsCache() {
+ return updateTimestampsCache;
+ }
+
+ public QueryCache getQueryCache() {
+ return queryCache;
+ }
+
+ public QueryCache getQueryCache(String regionName) throws HibernateException {
+ if ( regionName == null ) {
+ return getQueryCache();
+ }
+
+ if ( !settings.isQueryCacheEnabled() ) {
+ return null;
+ }
+
+ synchronized ( allCacheRegions ) {
+ QueryCache currentQueryCache = ( QueryCache ) queryCaches.get( regionName );
+ if ( currentQueryCache == null ) {
+ currentQueryCache = settings.getQueryCacheFactory().getQueryCache( regionName, updateTimestampsCache, settings, properties );
+ queryCaches.put( regionName, currentQueryCache );
+ allCacheRegions.put( currentQueryCache.getRegion().getName(), currentQueryCache.getRegion() );
+ }
+ return currentQueryCache;
+ }
+ }
+
+ public Region getSecondLevelCacheRegion(String regionName) {
+ synchronized ( allCacheRegions ) {
+ return ( Region ) allCacheRegions.get( regionName );
+ }
+ }
+
+ public Map getAllSecondLevelCacheRegions() {
+ synchronized ( allCacheRegions ) {
+ return new HashMap( allCacheRegions );
+ }
+ }
+
+ public boolean isClosed() {
+ return isClosed;
+ }
+
+ public Statistics getStatistics() {
+ return statistics;
+ }
+
+ public StatisticsImplementor getStatisticsImplementor() {
+ return statistics;
+ }
+
+ public void evictQueries() throws HibernateException {
+ if ( settings.isQueryCacheEnabled() ) {
+ queryCache.clear();
+ }
+ }
+
+ public void evictQueries(String cacheRegion) throws HibernateException {
+ if (cacheRegion==null) {
+ throw new NullPointerException("use the zero-argument form to evict the default query cache");
+ }
+ else {
+ synchronized (allCacheRegions) {
+ if ( settings.isQueryCacheEnabled() ) {
+ QueryCache currentQueryCache = (QueryCache) queryCaches.get(cacheRegion);
+ if ( currentQueryCache != null ) {
+ currentQueryCache.clear();
+ }
+ }
+ }
+ }
+ }
+
+ public FilterDefinition getFilterDefinition(String filterName) throws HibernateException {
+ FilterDefinition def = ( FilterDefinition ) filters.get( filterName );
+ if ( def == null ) {
+ throw new HibernateException( "No such filter configured [" + filterName + "]" );
+ }
+ return def;
+ }
+
+ public Set getDefinedFilterNames() {
+ return filters.keySet();
+ }
+
+ public BatcherFactory getBatcherFactory() {
+ return settings.getBatcherFactory();
+ }
+
+ public IdentifierGenerator getIdentifierGenerator(String rootEntityName) {
+ return (IdentifierGenerator) identifierGenerators.get(rootEntityName);
+ }
+
+ private CurrentSessionContext buildCurrentSessionContext() {
+ String impl = properties.getProperty( Environment.CURRENT_SESSION_CONTEXT_CLASS );
+ // for backward-compatability
+ if ( impl == null && transactionManager != null ) {
+ impl = "jta";
+ }
+
+ if ( impl == null ) {
+ return null;
+ }
+ else if ( "jta".equals( impl ) ) {
+ if ( settings.getTransactionFactory().areCallbacksLocalToHibernateTransactions() ) {
+ log.warn( "JTASessionContext being used with JDBCTransactionFactory; auto-flush will not operate correctly with getCurrentSession()" );
+ }
+ return new JTASessionContext( this );
+ }
+ else if ( "thread".equals( impl ) ) {
+ return new ThreadLocalSessionContext( this );
+ }
+ else if ( "managed".equals( impl ) ) {
+ return new ManagedSessionContext( this );
+ }
+ else {
+ try {
+ Class implClass = ReflectHelper.classForName( impl );
+ return ( CurrentSessionContext ) implClass
+ .getConstructor( new Class[] { SessionFactoryImplementor.class } )
+ .newInstance( new Object[] { this } );
+ }
+ catch( Throwable t ) {
+ log.error( "Unable to construct current session context [" + impl + "]", t );
+ return null;
+ }
+ }
+ }
+
+ public EventListeners getEventListeners()
+ {
+ return eventListeners;
+ }
+
+ public EntityNotFoundDelegate getEntityNotFoundDelegate() {
+ return entityNotFoundDelegate;
+ }
+
+ /**
+ * Custom serialization hook used during Session serialization.
+ *
+ * @param oos The stream to which to write the factory
+ * @throws IOException Indicates problems writing out the serial data stream
+ */
+ void serialize(ObjectOutputStream oos) throws IOException {
+ oos.writeUTF( uuid );
+ oos.writeBoolean( name != null );
+ if ( name != null ) {
+ oos.writeUTF( name );
+ }
+ }
+
+ /**
+ * Custom deserialization hook used during Session deserialization.
+ *
+ * @param ois The stream from which to "read" the factory
+ * @return The deserialized factory
+ * @throws IOException indicates problems reading back serial data stream
+ * @throws ClassNotFoundException indicates problems reading back serial data stream
+ */
+ static SessionFactoryImpl deserialize(ObjectInputStream ois) throws IOException, ClassNotFoundException {
+ String uuid = ois.readUTF();
+ boolean isNamed = ois.readBoolean();
+ String name = null;
+ if ( isNamed ) {
+ name = ois.readUTF();
+ }
+ Object result = SessionFactoryObjectFactory.getInstance( uuid );
+ if ( result == null ) {
+ log.trace( "could not locate session factory by uuid [" + uuid + "] during session deserialization; trying name" );
+ if ( isNamed ) {
+ result = SessionFactoryObjectFactory.getNamedInstance( name );
+ }
+ if ( result == null ) {
+ throw new InvalidObjectException( "could not resolve session factory during session deserialization [uuid=" + uuid + ", name=" + name + "]" );
+ }
+ }
+ return ( SessionFactoryImpl ) result;
+ }
+
+ public SQLFunctionRegistry getSqlFunctionRegistry() {
+ return sqlFunctionRegistry;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/impl/SessionFactoryObjectFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/impl/SessionFactoryObjectFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/impl/SessionFactoryObjectFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,161 @@
+//$Id: SessionFactoryObjectFactory.java 11337 2007-03-22 22:42:58Z epbernard $
+package org.hibernate.impl;
+
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Properties;
+
+import javax.naming.Context;
+import javax.naming.InvalidNameException;
+import javax.naming.Name;
+import javax.naming.NamingException;
+import javax.naming.Reference;
+import javax.naming.event.EventContext;
+import javax.naming.event.NamespaceChangeListener;
+import javax.naming.event.NamingEvent;
+import javax.naming.event.NamingExceptionEvent;
+import javax.naming.event.NamingListener;
+import javax.naming.spi.ObjectFactory;
+
+import org.hibernate.SessionFactory;
+import org.hibernate.util.FastHashMap;
+import org.hibernate.util.NamingHelper;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Resolves <tt>SessionFactory</tt> JNDI lookups and deserialization
+ */
+public class SessionFactoryObjectFactory implements ObjectFactory {
+
+ private static final SessionFactoryObjectFactory INSTANCE; //to stop the class from being unloaded
+
+ private static final Log log;
+
+ static {
+ log = LogFactory.getLog(SessionFactoryObjectFactory.class);
+ INSTANCE = new SessionFactoryObjectFactory();
+ log.debug("initializing class SessionFactoryObjectFactory");
+ }
+
+ private static final FastHashMap INSTANCES = new FastHashMap();
+ private static final FastHashMap NAMED_INSTANCES = new FastHashMap();
+
+ private static final NamingListener LISTENER = new NamespaceChangeListener() {
+ public void objectAdded(NamingEvent evt) {
+ log.debug( "A factory was successfully bound to name: " + evt.getNewBinding().getName() );
+ }
+ public void objectRemoved(NamingEvent evt) {
+ String name = evt.getOldBinding().getName();
+ log.info("A factory was unbound from name: " + name);
+ Object instance = NAMED_INSTANCES.remove(name);
+ Iterator iter = INSTANCES.values().iterator();
+ while ( iter.hasNext() ) {
+ if ( iter.next()==instance ) iter.remove();
+ }
+ }
+ public void objectRenamed(NamingEvent evt) {
+ String name = evt.getOldBinding().getName();
+ log.info("A factory was renamed from name: " + name);
+ NAMED_INSTANCES.put( evt.getNewBinding().getName(), NAMED_INSTANCES.remove(name) );
+ }
+ public void namingExceptionThrown(NamingExceptionEvent evt) {
+ log.warn( "Naming exception occurred accessing factory: " + evt.getException() );
+ }
+ };
+
+ public Object getObjectInstance(Object reference, Name name, Context ctx, Hashtable env) throws Exception {
+ log.debug("JNDI lookup: " + name);
+ String uid = (String) ( (Reference) reference ).get(0).getContent();
+ return getInstance(uid);
+ }
+
+ public static void addInstance(String uid, String name, SessionFactory instance, Properties properties) {
+
+ log.debug("registered: " + uid + " (" + ( (name==null) ? "unnamed" : name ) + ')');
+ INSTANCES.put(uid, instance);
+ if (name!=null) NAMED_INSTANCES.put(name, instance);
+
+ //must add to JNDI _after_ adding to HashMaps, because some JNDI servers use serialization
+ if (name==null) {
+ log.info("Not binding factory to JNDI, no JNDI name configured");
+ }
+ else {
+
+ log.info("Factory name: " + name);
+
+ try {
+ Context ctx = NamingHelper.getInitialContext(properties);
+ NamingHelper.bind(ctx, name, instance);
+ log.info("Bound factory to JNDI name: " + name);
+ ( (EventContext) ctx ).addNamingListener(name, EventContext.OBJECT_SCOPE, LISTENER);
+ }
+ catch (InvalidNameException ine) {
+ log.error("Invalid JNDI name: " + name, ine);
+ }
+ catch (NamingException ne) {
+ log.warn("Could not bind factory to JNDI", ne);
+ }
+ catch(ClassCastException cce) {
+ log.warn("InitialContext did not implement EventContext");
+ }
+
+ }
+
+ }
+
+ public static void removeInstance(String uid, String name, Properties properties) {
+ //TODO: theoretically non-threadsafe...
+
+ if (name!=null) {
+ log.info("Unbinding factory from JNDI name: " + name);
+
+ try {
+ Context ctx = NamingHelper.getInitialContext(properties);
+ ctx.unbind(name);
+ log.info("Unbound factory from JNDI name: " + name);
+ }
+ catch (InvalidNameException ine) {
+ log.error("Invalid JNDI name: " + name, ine);
+ }
+ catch (NamingException ne) {
+ log.warn("Could not unbind factory from JNDI", ne);
+ }
+
+ NAMED_INSTANCES.remove(name);
+
+ }
+
+ INSTANCES.remove(uid);
+
+ }
+
+ public static Object getNamedInstance(String name) {
+ log.debug("lookup: name=" + name);
+ Object result = NAMED_INSTANCES.get(name);
+ if (result==null) {
+ log.debug("Not found: " + name);
+ log.debug(NAMED_INSTANCES);
+ }
+ return result;
+ }
+
+ public static Object getInstance(String uid) {
+ log.debug("lookup: uid=" + uid);
+ Object result = INSTANCES.get(uid);
+ if (result==null) {
+ log.debug("Not found: " + uid);
+ log.debug(INSTANCES);
+ }
+ return result;
+ }
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/impl/SessionImpl.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/impl/SessionImpl.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/impl/SessionImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1958 @@
+//$Id: SessionImpl.java 10688 2006-11-02 18:53:25Z steve.ebersole at jboss.com $
+package org.hibernate.impl;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.sql.Connection;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.dom4j.Element;
+import org.hibernate.CacheMode;
+import org.hibernate.ConnectionReleaseMode;
+import org.hibernate.Criteria;
+import org.hibernate.EntityMode;
+import org.hibernate.Filter;
+import org.hibernate.FlushMode;
+import org.hibernate.HibernateException;
+import org.hibernate.Interceptor;
+import org.hibernate.LockMode;
+import org.hibernate.MappingException;
+import org.hibernate.ObjectDeletedException;
+import org.hibernate.Query;
+import org.hibernate.QueryException;
+import org.hibernate.ReplicationMode;
+import org.hibernate.SQLQuery;
+import org.hibernate.ScrollMode;
+import org.hibernate.ScrollableResults;
+import org.hibernate.Session;
+import org.hibernate.SessionException;
+import org.hibernate.SessionFactory;
+import org.hibernate.Transaction;
+import org.hibernate.TransientObjectException;
+import org.hibernate.UnresolvableObjectException;
+import org.hibernate.engine.query.sql.NativeSQLQuerySpecification;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.engine.ActionQueue;
+import org.hibernate.engine.CollectionEntry;
+import org.hibernate.engine.EntityEntry;
+import org.hibernate.engine.EntityKey;
+import org.hibernate.engine.FilterDefinition;
+import org.hibernate.engine.PersistenceContext;
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.StatefulPersistenceContext;
+import org.hibernate.engine.Status;
+import org.hibernate.engine.query.FilterQueryPlan;
+import org.hibernate.engine.query.HQLQueryPlan;
+import org.hibernate.engine.query.NativeSQLQueryPlan;
+import org.hibernate.event.AutoFlushEvent;
+import org.hibernate.event.AutoFlushEventListener;
+import org.hibernate.event.DeleteEvent;
+import org.hibernate.event.DeleteEventListener;
+import org.hibernate.event.DirtyCheckEvent;
+import org.hibernate.event.DirtyCheckEventListener;
+import org.hibernate.event.EventListeners;
+import org.hibernate.event.EventSource;
+import org.hibernate.event.EvictEvent;
+import org.hibernate.event.EvictEventListener;
+import org.hibernate.event.FlushEvent;
+import org.hibernate.event.FlushEventListener;
+import org.hibernate.event.InitializeCollectionEvent;
+import org.hibernate.event.InitializeCollectionEventListener;
+import org.hibernate.event.LoadEvent;
+import org.hibernate.event.LoadEventListener;
+import org.hibernate.event.LockEvent;
+import org.hibernate.event.LockEventListener;
+import org.hibernate.event.MergeEvent;
+import org.hibernate.event.MergeEventListener;
+import org.hibernate.event.PersistEvent;
+import org.hibernate.event.PersistEventListener;
+import org.hibernate.event.RefreshEvent;
+import org.hibernate.event.RefreshEventListener;
+import org.hibernate.event.ReplicateEvent;
+import org.hibernate.event.ReplicateEventListener;
+import org.hibernate.event.SaveOrUpdateEvent;
+import org.hibernate.event.SaveOrUpdateEventListener;
+import org.hibernate.event.LoadEventListener.LoadType;
+import org.hibernate.jdbc.Batcher;
+import org.hibernate.jdbc.JDBCContext;
+import org.hibernate.loader.criteria.CriteriaLoader;
+import org.hibernate.loader.custom.CustomLoader;
+import org.hibernate.loader.custom.CustomQuery;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.persister.entity.OuterJoinLoadable;
+import org.hibernate.pretty.MessageHelper;
+import org.hibernate.proxy.HibernateProxy;
+import org.hibernate.proxy.LazyInitializer;
+import org.hibernate.stat.SessionStatistics;
+import org.hibernate.stat.SessionStatisticsImpl;
+import org.hibernate.tuple.DynamicMapInstantiator;
+import org.hibernate.type.Type;
+import org.hibernate.util.ArrayHelper;
+import org.hibernate.util.CollectionHelper;
+import org.hibernate.util.StringHelper;
+
+
+/**
+ * Concrete implementation of a Session, and also the central, organizing component
+ * of Hibernate's internal implementation. As such, this class exposes two interfaces;
+ * Session itself, to the application, and SessionImplementor, to other components
+ * of Hibernate. This class is not threadsafe.
+ *
+ * @author Gavin King
+ */
+public final class SessionImpl extends AbstractSessionImpl
+ implements EventSource, org.hibernate.classic.Session, JDBCContext.Context {
+
+ // todo : need to find a clean way to handle the "event source" role
+ // a seperate classs responsible for generating/dispatching events just duplicates most of the Session methods...
+ // passing around seperate reto interceptor, factory, actionQueue, and persistentContext is not manageable...
+
+ private static final Log log = LogFactory.getLog(SessionImpl.class);
+
+ private transient EntityMode entityMode = EntityMode.POJO;
+ private transient boolean autoClear; //for EJB3
+
+ private transient long timestamp;
+ private transient FlushMode flushMode = FlushMode.AUTO;
+ private transient CacheMode cacheMode = CacheMode.NORMAL;
+
+ private transient Interceptor interceptor;
+
+ private transient int dontFlushFromFind = 0;
+
+ private transient ActionQueue actionQueue;
+ private transient StatefulPersistenceContext persistenceContext;
+ private transient JDBCContext jdbcContext;
+ private transient EventListeners listeners;
+
+ private transient boolean flushBeforeCompletionEnabled;
+ private transient boolean autoCloseSessionEnabled;
+ private transient ConnectionReleaseMode connectionReleaseMode;
+
+ private transient String fetchProfile;
+
+ private transient Map enabledFilters = new HashMap();
+
+ private transient Session rootSession;
+ private transient Map childSessionsByEntityMode;
+
+ /**
+ * Constructor used in building "child sessions".
+ *
+ * @param parent The parent session
+ * @param entityMode
+ */
+ private SessionImpl(SessionImpl parent, EntityMode entityMode) {
+ super( parent.factory );
+ this.rootSession = parent;
+ this.timestamp = parent.timestamp;
+ this.jdbcContext = parent.jdbcContext;
+ this.interceptor = parent.interceptor;
+ this.listeners = parent.listeners;
+ this.actionQueue = new ActionQueue( this );
+ this.entityMode = entityMode;
+ this.persistenceContext = new StatefulPersistenceContext( this );
+ this.flushBeforeCompletionEnabled = false;
+ this.autoCloseSessionEnabled = false;
+ this.connectionReleaseMode = null;
+
+ if ( factory.getStatistics().isStatisticsEnabled() ) {
+ factory.getStatisticsImplementor().openSession();
+ }
+
+ log.debug( "opened session [" + entityMode + "]" );
+ }
+
+ /**
+ * Constructor used for openSession(...) processing, as well as construction
+ * of sessions for getCurrentSession().
+ *
+ * @param connection The user-supplied connection to use for this session.
+ * @param factory The factory from which this session was obtained
+ * @param autoclose NOT USED
+ * @param timestamp The timestamp for this session
+ * @param interceptor The interceptor to be applied to this session
+ * @param entityMode The entity-mode for this session
+ * @param flushBeforeCompletionEnabled Should we auto flush before completion of transaction
+ * @param autoCloseSessionEnabled Should we auto close after completion of transaction
+ * @param connectionReleaseMode The mode by which we should release JDBC connections.
+ */
+ SessionImpl(
+ final Connection connection,
+ final SessionFactoryImpl factory,
+ final boolean autoclose,
+ final long timestamp,
+ final Interceptor interceptor,
+ final EntityMode entityMode,
+ final boolean flushBeforeCompletionEnabled,
+ final boolean autoCloseSessionEnabled,
+ final ConnectionReleaseMode connectionReleaseMode) {
+ super( factory );
+ this.rootSession = null;
+ this.timestamp = timestamp;
+ this.entityMode = entityMode;
+ this.interceptor = interceptor;
+ this.listeners = factory.getEventListeners();
+ this.actionQueue = new ActionQueue( this );
+ this.persistenceContext = new StatefulPersistenceContext( this );
+ this.flushBeforeCompletionEnabled = flushBeforeCompletionEnabled;
+ this.autoCloseSessionEnabled = autoCloseSessionEnabled;
+ this.connectionReleaseMode = connectionReleaseMode;
+ this.jdbcContext = new JDBCContext( this, connection, interceptor );
+
+ if ( factory.getStatistics().isStatisticsEnabled() ) {
+ factory.getStatisticsImplementor().openSession();
+ }
+
+ if ( log.isDebugEnabled() ) {
+ log.debug( "opened session at timestamp: " + timestamp );
+ }
+ }
+
+ public Session getSession(EntityMode entityMode) {
+ if ( this.entityMode == entityMode ) {
+ return this;
+ }
+
+ if ( rootSession != null ) {
+ rootSession.getSession( entityMode );
+ }
+
+ errorIfClosed();
+ checkTransactionSynchStatus();
+
+ SessionImpl rtn = null;
+ if ( childSessionsByEntityMode == null ) {
+ childSessionsByEntityMode = new HashMap();
+ }
+ else {
+ rtn = (SessionImpl) childSessionsByEntityMode.get( entityMode );
+ }
+
+ if ( rtn == null ) {
+ rtn = new SessionImpl( this, entityMode );
+ childSessionsByEntityMode.put( entityMode, rtn );
+ }
+
+ return rtn;
+ }
+
+ public void clear() {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ persistenceContext.clear();
+ actionQueue.clear();
+ }
+
+ public Batcher getBatcher() {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ // TODO : should remove this exposure
+ // and have all references to the session's batcher use the ConnectionManager.
+ return jdbcContext.getConnectionManager().getBatcher();
+ }
+
+ public long getTimestamp() {
+ checkTransactionSynchStatus();
+ return timestamp;
+ }
+
+ public Connection close() throws HibernateException {
+ log.trace( "closing session" );
+ if ( isClosed() ) {
+ throw new SessionException( "Session was already closed" );
+ }
+
+
+ if ( factory.getStatistics().isStatisticsEnabled() ) {
+ factory.getStatisticsImplementor().closeSession();
+ }
+
+ try {
+ try {
+ if ( childSessionsByEntityMode != null ) {
+ Iterator childSessions = childSessionsByEntityMode.values().iterator();
+ while ( childSessions.hasNext() ) {
+ final SessionImpl child = ( SessionImpl ) childSessions.next();
+ child.close();
+ }
+ }
+ }
+ catch( Throwable t ) {
+ // just ignore
+ }
+
+ if ( rootSession == null ) {
+ return jdbcContext.getConnectionManager().close();
+ }
+ else {
+ return null;
+ }
+ }
+ finally {
+ setClosed();
+ cleanup();
+ }
+ }
+
+ public ConnectionReleaseMode getConnectionReleaseMode() {
+ checkTransactionSynchStatus();
+ return connectionReleaseMode;
+ }
+
+ public boolean isAutoCloseSessionEnabled() {
+ return autoCloseSessionEnabled;
+ }
+
+ public boolean isOpen() {
+ checkTransactionSynchStatus();
+ return !isClosed();
+ }
+
+ public boolean isFlushModeNever() {
+ return FlushMode.isManualFlushMode( getFlushMode() );
+ }
+
+ public boolean isFlushBeforeCompletionEnabled() {
+ return flushBeforeCompletionEnabled;
+ }
+
+ public void managedFlush() {
+ if ( isClosed() ) {
+ log.trace( "skipping auto-flush due to session closed" );
+ return;
+ }
+ log.trace("automatically flushing session");
+ flush();
+
+ if ( childSessionsByEntityMode != null ) {
+ Iterator iter = childSessionsByEntityMode.values().iterator();
+ while ( iter.hasNext() ) {
+ ( (Session) iter.next() ).flush();
+ }
+ }
+ }
+
+ public boolean shouldAutoClose() {
+ return isAutoCloseSessionEnabled() && !isClosed();
+ }
+
+ public void managedClose() {
+ log.trace( "automatically closing session" );
+ close();
+ }
+
+ public Connection connection() throws HibernateException {
+ errorIfClosed();
+ return jdbcContext.borrowConnection();
+ }
+
+ public boolean isConnected() {
+ checkTransactionSynchStatus();
+ return !isClosed() && jdbcContext.getConnectionManager().isCurrentlyConnected();
+ }
+
+ public boolean isTransactionInProgress() {
+ checkTransactionSynchStatus();
+ return !isClosed() && jdbcContext.isTransactionInProgress();
+ }
+
+ public Connection disconnect() throws HibernateException {
+ errorIfClosed();
+ log.debug( "disconnecting session" );
+ return jdbcContext.getConnectionManager().manualDisconnect();
+ }
+
+ public void reconnect() throws HibernateException {
+ errorIfClosed();
+ log.debug( "reconnecting session" );
+ checkTransactionSynchStatus();
+ jdbcContext.getConnectionManager().manualReconnect();
+ }
+
+ public void reconnect(Connection conn) throws HibernateException {
+ errorIfClosed();
+ log.debug( "reconnecting session" );
+ checkTransactionSynchStatus();
+ jdbcContext.getConnectionManager().manualReconnect( conn );
+ }
+
+ public void beforeTransactionCompletion(Transaction tx) {
+ log.trace( "before transaction completion" );
+ if ( rootSession == null ) {
+ try {
+ interceptor.beforeTransactionCompletion(tx);
+ }
+ catch (Throwable t) {
+ log.error("exception in interceptor beforeTransactionCompletion()", t);
+ }
+ }
+ }
+
+ public void setAutoClear(boolean enabled) {
+ errorIfClosed();
+ autoClear = enabled;
+ }
+
+ /**
+ * Check if there is a Hibernate or JTA transaction in progress and,
+ * if there is not, flush if necessary, make sure the connection has
+ * been committed (if it is not in autocommit mode) and run the after
+ * completion processing
+ */
+ public void afterOperation(boolean success) {
+ if ( !jdbcContext.isTransactionInProgress() ) {
+ jdbcContext.afterNontransactionalQuery( success );
+ }
+ }
+
+ public void afterTransactionCompletion(boolean success, Transaction tx) {
+ log.trace( "after transaction completion" );
+ persistenceContext.afterTransactionCompletion();
+ actionQueue.afterTransactionCompletion(success);
+ if ( rootSession == null && tx != null ) {
+ try {
+ interceptor.afterTransactionCompletion(tx);
+ }
+ catch (Throwable t) {
+ log.error("exception in interceptor afterTransactionCompletion()", t);
+ }
+ }
+ if ( autoClear ) {
+ clear();
+ }
+ }
+
+ /**
+ * clear all the internal collections, just
+ * to help the garbage collector, does not
+ * clear anything that is needed during the
+ * afterTransactionCompletion() phase
+ */
+ private void cleanup() {
+ persistenceContext.clear();
+ }
+
+ public LockMode getCurrentLockMode(Object object) throws HibernateException {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ if ( object == null ) {
+ throw new NullPointerException( "null object passed to getCurrentLockMode()" );
+ }
+ if ( object instanceof HibernateProxy ) {
+ object = ( (HibernateProxy) object ).getHibernateLazyInitializer().getImplementation(this);
+ if ( object == null ) {
+ return LockMode.NONE;
+ }
+ }
+ EntityEntry e = persistenceContext.getEntry(object);
+ if ( e == null ) {
+ throw new TransientObjectException( "Given object not associated with the session" );
+ }
+ if ( e.getStatus() != Status.MANAGED ) {
+ throw new ObjectDeletedException(
+ "The given object was deleted",
+ e.getId(),
+ e.getPersister().getEntityName()
+ );
+ }
+ return e.getLockMode();
+ }
+
+ public Object getEntityUsingInterceptor(EntityKey key) throws HibernateException {
+ errorIfClosed();
+ // todo : should this get moved to PersistentContext?
+ // logically, is PersistentContext the "thing" to which an interceptor gets attached?
+ final Object result = persistenceContext.getEntity(key);
+ if ( result == null ) {
+ final Object newObject = interceptor.getEntity( key.getEntityName(), key.getIdentifier() );
+ if ( newObject != null ) {
+ lock( newObject, LockMode.NONE );
+ }
+ return newObject;
+ }
+ else {
+ return result;
+ }
+ }
+
+
+ // saveOrUpdate() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public void saveOrUpdate(Object object) throws HibernateException {
+ saveOrUpdate(null, object);
+ }
+
+ public void saveOrUpdate(String entityName, Object obj) throws HibernateException {
+ fireSaveOrUpdate( new SaveOrUpdateEvent(entityName, obj, this) );
+ }
+
+ private void fireSaveOrUpdate(SaveOrUpdateEvent event) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ SaveOrUpdateEventListener[] saveOrUpdateEventListener = listeners.getSaveOrUpdateEventListeners();
+ for ( int i = 0; i < saveOrUpdateEventListener.length; i++ ) {
+ saveOrUpdateEventListener[i].onSaveOrUpdate(event);
+ }
+ }
+
+
+ // save() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public void save(Object obj, Serializable id) throws HibernateException {
+ save(null, obj, id);
+ }
+
+ public Serializable save(Object obj) throws HibernateException {
+ return save(null, obj);
+ }
+
+ public Serializable save(String entityName, Object object) throws HibernateException {
+ return fireSave( new SaveOrUpdateEvent(entityName, object, this) );
+ }
+
+ public void save(String entityName, Object object, Serializable id) throws HibernateException {
+ fireSave( new SaveOrUpdateEvent(entityName, object, id, this) );
+ }
+
+ private Serializable fireSave(SaveOrUpdateEvent event) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ SaveOrUpdateEventListener[] saveEventListener = listeners.getSaveEventListeners();
+ for ( int i = 0; i < saveEventListener.length; i++ ) {
+ saveEventListener[i].onSaveOrUpdate(event);
+ }
+ return event.getResultId();
+ }
+
+
+ // update() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public void update(Object obj) throws HibernateException {
+ update(null, obj);
+ }
+
+ public void update(Object obj, Serializable id) throws HibernateException {
+ update(null, obj, id);
+ }
+
+ public void update(String entityName, Object object) throws HibernateException {
+ fireUpdate( new SaveOrUpdateEvent(entityName, object, this) );
+ }
+
+ public void update(String entityName, Object object, Serializable id) throws HibernateException {
+ fireUpdate(new SaveOrUpdateEvent(entityName, object, id, this));
+ }
+
+ private void fireUpdate(SaveOrUpdateEvent event) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ SaveOrUpdateEventListener[] updateEventListener = listeners.getUpdateEventListeners();
+ for ( int i = 0; i < updateEventListener.length; i++ ) {
+ updateEventListener[i].onSaveOrUpdate(event);
+ }
+ }
+
+
+ // lock() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public void lock(String entityName, Object object, LockMode lockMode) throws HibernateException {
+ fireLock( new LockEvent(entityName, object, lockMode, this) );
+ }
+
+ public void lock(Object object, LockMode lockMode) throws HibernateException {
+ fireLock( new LockEvent(object, lockMode, this) );
+ }
+
+ private void fireLock(LockEvent lockEvent) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ LockEventListener[] lockEventListener = listeners.getLockEventListeners();
+ for ( int i = 0; i < lockEventListener.length; i++ ) {
+ lockEventListener[i].onLock( lockEvent );
+ }
+ }
+
+
+ // persist() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public void persist(String entityName, Object object) throws HibernateException {
+ firePersist( new PersistEvent(entityName, object, this) );
+ }
+
+ public void persist(Object object) throws HibernateException {
+ persist(null, object);
+ }
+
+ public void persist(String entityName, Object object, Map copiedAlready)
+ throws HibernateException {
+ firePersist( copiedAlready, new PersistEvent(entityName, object, this) );
+ }
+
+ private void firePersist(Map copiedAlready, PersistEvent event) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ PersistEventListener[] persistEventListener = listeners.getPersistEventListeners();
+ for ( int i = 0; i < persistEventListener.length; i++ ) {
+ persistEventListener[i].onPersist(event, copiedAlready);
+ }
+ }
+
+ private void firePersist(PersistEvent event) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ PersistEventListener[] createEventListener = listeners.getPersistEventListeners();
+ for ( int i = 0; i < createEventListener.length; i++ ) {
+ createEventListener[i].onPersist(event);
+ }
+ }
+
+
+ // persistOnFlush() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public void persistOnFlush(String entityName, Object object)
+ throws HibernateException {
+ firePersistOnFlush( new PersistEvent(entityName, object, this) );
+ }
+
+ public void persistOnFlush(Object object) throws HibernateException {
+ persist(null, object);
+ }
+
+ public void persistOnFlush(String entityName, Object object, Map copiedAlready)
+ throws HibernateException {
+ firePersistOnFlush( copiedAlready, new PersistEvent(entityName, object, this) );
+ }
+
+ private void firePersistOnFlush(Map copiedAlready, PersistEvent event) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ PersistEventListener[] persistEventListener = listeners.getPersistOnFlushEventListeners();
+ for ( int i = 0; i < persistEventListener.length; i++ ) {
+ persistEventListener[i].onPersist(event, copiedAlready);
+ }
+ }
+
+ private void firePersistOnFlush(PersistEvent event) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ PersistEventListener[] createEventListener = listeners.getPersistOnFlushEventListeners();
+ for ( int i = 0; i < createEventListener.length; i++ ) {
+ createEventListener[i].onPersist(event);
+ }
+ }
+
+
+ // merge() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public Object merge(String entityName, Object object) throws HibernateException {
+ return fireMerge( new MergeEvent(entityName, object, this) );
+ }
+
+ public Object merge(Object object) throws HibernateException {
+ return merge(null, object);
+ }
+
+ public void merge(String entityName, Object object, Map copiedAlready) throws HibernateException {
+ fireMerge( copiedAlready, new MergeEvent(entityName, object, this) );
+ }
+
+ private Object fireMerge(MergeEvent event) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ MergeEventListener[] mergeEventListener = listeners.getMergeEventListeners();
+ for ( int i = 0; i < mergeEventListener.length; i++ ) {
+ mergeEventListener[i].onMerge(event);
+ }
+ return event.getResult();
+ }
+
+ private void fireMerge(Map copiedAlready, MergeEvent event) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ MergeEventListener[] mergeEventListener = listeners.getMergeEventListeners();
+ for ( int i = 0; i < mergeEventListener.length; i++ ) {
+ mergeEventListener[i].onMerge(event, copiedAlready);
+ }
+ }
+
+
+ // saveOrUpdateCopy() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public Object saveOrUpdateCopy(String entityName, Object object)
+ throws HibernateException {
+ return fireSaveOrUpdateCopy( new MergeEvent(entityName, object, this) );
+ }
+
+ public Object saveOrUpdateCopy(Object object) throws HibernateException {
+ return saveOrUpdateCopy( null, object );
+ }
+
+ public Object saveOrUpdateCopy(String entityName, Object object, Serializable id)
+ throws HibernateException {
+ return fireSaveOrUpdateCopy( new MergeEvent(entityName, object, id, this) );
+ }
+
+ public Object saveOrUpdateCopy(Object object, Serializable id)
+ throws HibernateException {
+ return saveOrUpdateCopy( null, object, id );
+ }
+
+ public void saveOrUpdateCopy(String entityName, Object object, Map copiedAlready)
+ throws HibernateException {
+ fireSaveOrUpdateCopy( copiedAlready, new MergeEvent( entityName, object, this ) );
+ }
+
+ private void fireSaveOrUpdateCopy(Map copiedAlready, MergeEvent event) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ MergeEventListener[] saveOrUpdateCopyEventListener = listeners.getSaveOrUpdateCopyEventListeners();
+ for ( int i = 0; i < saveOrUpdateCopyEventListener.length; i++ ) {
+ saveOrUpdateCopyEventListener[i].onMerge(event, copiedAlready);
+ }
+ }
+
+ private Object fireSaveOrUpdateCopy(MergeEvent event) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ MergeEventListener[] saveOrUpdateCopyEventListener = listeners.getSaveOrUpdateCopyEventListeners();
+ for ( int i = 0; i < saveOrUpdateCopyEventListener.length; i++ ) {
+ saveOrUpdateCopyEventListener[i].onMerge(event);
+ }
+ return event.getResult();
+ }
+
+
+ // delete() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Delete a persistent object
+ */
+ public void delete(Object object) throws HibernateException {
+ fireDelete( new DeleteEvent(object, this) );
+ }
+
+ /**
+ * Delete a persistent object (by explicit entity name)
+ */
+ public void delete(String entityName, Object object) throws HibernateException {
+ fireDelete( new DeleteEvent( entityName, object, this ) );
+ }
+
+ /**
+ * Delete a persistent object
+ */
+ public void delete(String entityName, Object object, boolean isCascadeDeleteEnabled, Set transientEntities) throws HibernateException {
+ fireDelete( new DeleteEvent( entityName, object, isCascadeDeleteEnabled, this ), transientEntities );
+ }
+
+ private void fireDelete(DeleteEvent event) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ DeleteEventListener[] deleteEventListener = listeners.getDeleteEventListeners();
+ for ( int i = 0; i < deleteEventListener.length; i++ ) {
+ deleteEventListener[i].onDelete( event );
+ }
+ }
+
+ private void fireDelete(DeleteEvent event, Set transientEntities) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ DeleteEventListener[] deleteEventListener = listeners.getDeleteEventListeners();
+ for ( int i = 0; i < deleteEventListener.length; i++ ) {
+ deleteEventListener[i].onDelete( event, transientEntities );
+ }
+ }
+
+
+ // load()/get() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public void load(Object object, Serializable id) throws HibernateException {
+ LoadEvent event = new LoadEvent(id, object, this);
+ fireLoad( event, LoadEventListener.RELOAD );
+ }
+
+ public Object load(Class entityClass, Serializable id) throws HibernateException {
+ return load( entityClass.getName(), id );
+ }
+
+ public Object load(String entityName, Serializable id) throws HibernateException {
+ LoadEvent event = new LoadEvent(id, entityName, false, this);
+ boolean success = false;
+ try {
+ fireLoad( event, LoadEventListener.LOAD );
+ if ( event.getResult() == null ) {
+ getFactory().getEntityNotFoundDelegate().handleEntityNotFound( entityName, id );
+ }
+ success = true;
+ return event.getResult();
+ }
+ finally {
+ afterOperation(success);
+ }
+ }
+
+ public Object get(Class entityClass, Serializable id) throws HibernateException {
+ return get( entityClass.getName(), id );
+ }
+
+ public Object get(String entityName, Serializable id) throws HibernateException {
+ LoadEvent event = new LoadEvent(id, entityName, false, this);
+ boolean success = false;
+ try {
+ fireLoad(event, LoadEventListener.GET);
+ success = true;
+ return event.getResult();
+ }
+ finally {
+ afterOperation(success);
+ }
+ }
+
+ /**
+ * Load the data for the object with the specified id into a newly created object.
+ * This is only called when lazily initializing a proxy.
+ * Do NOT return a proxy.
+ */
+ public Object immediateLoad(String entityName, Serializable id) throws HibernateException {
+ if ( log.isDebugEnabled() ) {
+ EntityPersister persister = getFactory().getEntityPersister(entityName);
+ log.debug( "initializing proxy: " + MessageHelper.infoString( persister, id, getFactory() ) );
+ }
+
+ LoadEvent event = new LoadEvent(id, entityName, true, this);
+ fireLoad(event, LoadEventListener.IMMEDIATE_LOAD);
+ return event.getResult();
+ }
+
+ public Object internalLoad(String entityName, Serializable id, boolean eager, boolean nullable) throws HibernateException {
+ // todo : remove
+ LoadEventListener.LoadType type = nullable ?
+ LoadEventListener.INTERNAL_LOAD_NULLABLE :
+ eager ? LoadEventListener.INTERNAL_LOAD_EAGER : LoadEventListener.INTERNAL_LOAD_LAZY;
+ LoadEvent event = new LoadEvent(id, entityName, true, this);
+ fireLoad(event, type);
+ if ( !nullable ) {
+ UnresolvableObjectException.throwIfNull( event.getResult(), id, entityName );
+ }
+ return event.getResult();
+ }
+
+ public Object load(Class entityClass, Serializable id, LockMode lockMode) throws HibernateException {
+ return load( entityClass.getName(), id, lockMode );
+ }
+
+ public Object load(String entityName, Serializable id, LockMode lockMode) throws HibernateException {
+ LoadEvent event = new LoadEvent(id, entityName, lockMode, this);
+ fireLoad( event, LoadEventListener.LOAD );
+ return event.getResult();
+ }
+
+ public Object get(Class entityClass, Serializable id, LockMode lockMode) throws HibernateException {
+ return get( entityClass.getName(), id, lockMode );
+ }
+
+ public Object get(String entityName, Serializable id, LockMode lockMode) throws HibernateException {
+ LoadEvent event = new LoadEvent(id, entityName, lockMode, this);
+ fireLoad(event, LoadEventListener.GET);
+ return event.getResult();
+ }
+
+ private void fireLoad(LoadEvent event, LoadType loadType) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ LoadEventListener[] loadEventListener = listeners.getLoadEventListeners();
+ for ( int i = 0; i < loadEventListener.length; i++ ) {
+ loadEventListener[i].onLoad(event, loadType);
+ }
+ }
+
+
+ // refresh() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public void refresh(Object object) throws HibernateException {
+ fireRefresh( new RefreshEvent(object, this) );
+ }
+
+ public void refresh(Object object, LockMode lockMode) throws HibernateException {
+ fireRefresh( new RefreshEvent(object, lockMode, this) );
+ }
+
+ public void refresh(Object object, Map refreshedAlready) throws HibernateException {
+ fireRefresh( refreshedAlready, new RefreshEvent(object, this) );
+ }
+
+ private void fireRefresh(RefreshEvent refreshEvent) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ RefreshEventListener[] refreshEventListener = listeners.getRefreshEventListeners();
+ for ( int i = 0; i < refreshEventListener.length; i++ ) {
+ refreshEventListener[i].onRefresh( refreshEvent );
+ }
+ }
+
+ private void fireRefresh(Map refreshedAlready, RefreshEvent refreshEvent) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ RefreshEventListener[] refreshEventListener = listeners.getRefreshEventListeners();
+ for ( int i = 0; i < refreshEventListener.length; i++ ) {
+ refreshEventListener[i].onRefresh( refreshEvent, refreshedAlready );
+ }
+ }
+
+
+ // replicate() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public void replicate(Object obj, ReplicationMode replicationMode) throws HibernateException {
+ fireReplicate( new ReplicateEvent(obj, replicationMode, this) );
+ }
+
+ public void replicate(String entityName, Object obj, ReplicationMode replicationMode)
+ throws HibernateException {
+ fireReplicate( new ReplicateEvent(entityName, obj, replicationMode, this) );
+ }
+
+ private void fireReplicate(ReplicateEvent event) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ ReplicateEventListener[] replicateEventListener = listeners.getReplicateEventListeners();
+ for ( int i = 0; i < replicateEventListener.length; i++ ) {
+ replicateEventListener[i].onReplicate(event);
+ }
+ }
+
+
+ // evict() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * remove any hard references to the entity that are held by the infrastructure
+ * (references held by application or other persistant instances are okay)
+ */
+ public void evict(Object object) throws HibernateException {
+ fireEvict( new EvictEvent(object, this) );
+ }
+
+ private void fireEvict(EvictEvent evictEvent) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ EvictEventListener[] evictEventListener = listeners.getEvictEventListeners();
+ for ( int i = 0; i < evictEventListener.length; i++ ) {
+ evictEventListener[i].onEvict( evictEvent );
+ }
+ }
+
+ /**
+ * detect in-memory changes, determine if the changes are to tables
+ * named in the query and, if so, complete execution the flush
+ */
+ protected boolean autoFlushIfRequired(Set querySpaces) throws HibernateException {
+ errorIfClosed();
+ if ( ! isTransactionInProgress() ) {
+ // do not auto-flush while outside a transaction
+ return false;
+ }
+ AutoFlushEvent event = new AutoFlushEvent(querySpaces, this);
+ AutoFlushEventListener[] autoFlushEventListener = listeners.getAutoFlushEventListeners();
+ for ( int i = 0; i < autoFlushEventListener.length; i++ ) {
+ autoFlushEventListener[i].onAutoFlush(event);
+ }
+ return event.isFlushRequired();
+ }
+
+ public boolean isDirty() throws HibernateException {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ log.debug("checking session dirtiness");
+ if ( actionQueue.areInsertionsOrDeletionsQueued() ) {
+ log.debug("session dirty (scheduled updates and insertions)");
+ return true;
+ }
+ else {
+ DirtyCheckEvent event = new DirtyCheckEvent(this);
+ DirtyCheckEventListener[] dirtyCheckEventListener = listeners.getDirtyCheckEventListeners();
+ for ( int i = 0; i < dirtyCheckEventListener.length; i++ ) {
+ dirtyCheckEventListener[i].onDirtyCheck(event);
+ }
+ return event.isDirty();
+ }
+ }
+
+ public void flush() throws HibernateException {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ if ( persistenceContext.getCascadeLevel() > 0 ) {
+ throw new HibernateException("Flush during cascade is dangerous");
+ }
+ FlushEventListener[] flushEventListener = listeners.getFlushEventListeners();
+ for ( int i = 0; i < flushEventListener.length; i++ ) {
+ flushEventListener[i].onFlush( new FlushEvent(this) );
+ }
+ }
+
+ public void forceFlush(EntityEntry entityEntry) throws HibernateException {
+ errorIfClosed();
+ if ( log.isDebugEnabled() ) {
+ log.debug(
+ "flushing to force deletion of re-saved object: " +
+ MessageHelper.infoString( entityEntry.getPersister(), entityEntry.getId(), getFactory() )
+ );
+ }
+
+ if ( persistenceContext.getCascadeLevel() > 0 ) {
+ throw new ObjectDeletedException(
+ "deleted object would be re-saved by cascade (remove deleted object from associations)",
+ entityEntry.getId(),
+ entityEntry.getPersister().getEntityName()
+ );
+ }
+
+ flush();
+ }
+
+ public Filter getEnabledFilter(String filterName) {
+ checkTransactionSynchStatus();
+ return (Filter) enabledFilters.get(filterName);
+ }
+
+ public Filter enableFilter(String filterName) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ FilterImpl filter = new FilterImpl( factory.getFilterDefinition(filterName) );
+ enabledFilters.put(filterName, filter);
+ return filter;
+ }
+
+ public void disableFilter(String filterName) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ enabledFilters.remove(filterName);
+ }
+
+ public Object getFilterParameterValue(String filterParameterName) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ String[] parsed = parseFilterParameterName(filterParameterName);
+ FilterImpl filter = (FilterImpl) enabledFilters.get( parsed[0] );
+ if (filter == null) {
+ throw new IllegalArgumentException("Filter [" + parsed[0] + "] currently not enabled");
+ }
+ return filter.getParameter( parsed[1] );
+ }
+
+ public Type getFilterParameterType(String filterParameterName) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ String[] parsed = parseFilterParameterName(filterParameterName);
+ FilterDefinition filterDef = factory.getFilterDefinition( parsed[0] );
+ if (filterDef == null) {
+ throw new IllegalArgumentException("Filter [" + parsed[0] + "] not defined");
+ }
+ Type type = filterDef.getParameterType( parsed[1] );
+ if (type == null) {
+ // this is an internal error of some sort...
+ throw new InternalError("Unable to locate type for filter parameter");
+ }
+ return type;
+ }
+
+ public Map getEnabledFilters() {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ // First, validate all the enabled filters...
+ //TODO: this implementation has bad performance
+ Iterator itr = enabledFilters.values().iterator();
+ while ( itr.hasNext() ) {
+ final Filter filter = (Filter) itr.next();
+ filter.validate();
+ }
+ return enabledFilters;
+ }
+
+ private String[] parseFilterParameterName(String filterParameterName) {
+ int dot = filterParameterName.indexOf('.');
+ if (dot <= 0) {
+ throw new IllegalArgumentException("Invalid filter-parameter name format"); // TODO: what type?
+ }
+ String filterName = filterParameterName.substring(0, dot);
+ String parameterName = filterParameterName.substring(dot+1);
+ return new String[] {filterName, parameterName};
+ }
+
+
+ /**
+ * Retrieve a list of persistent objects using a hibernate query
+ */
+ public List find(String query) throws HibernateException {
+ return list( query, new QueryParameters() );
+ }
+
+ public List find(String query, Object value, Type type) throws HibernateException {
+ return list( query, new QueryParameters(type, value) );
+ }
+
+ public List find(String query, Object[] values, Type[] types) throws HibernateException {
+ return list( query, new QueryParameters(types, values) );
+ }
+
+ public List list(String query, QueryParameters queryParameters) throws HibernateException {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ queryParameters.validateParameters();
+ HQLQueryPlan plan = getHQLQueryPlan( query, false );
+ autoFlushIfRequired( plan.getQuerySpaces() );
+
+ List results = CollectionHelper.EMPTY_LIST;
+ boolean success = false;
+
+ dontFlushFromFind++; //stops flush being called multiple times if this method is recursively called
+ try {
+ results = plan.performList( queryParameters, this );
+ success = true;
+ }
+ finally {
+ dontFlushFromFind--;
+ afterOperation(success);
+ }
+ return results;
+ }
+
+ public int executeUpdate(String query, QueryParameters queryParameters) throws HibernateException {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ queryParameters.validateParameters();
+ HQLQueryPlan plan = getHQLQueryPlan( query, false );
+ autoFlushIfRequired( plan.getQuerySpaces() );
+
+ boolean success = false;
+ int result = 0;
+ try {
+ result = plan.performExecuteUpdate( queryParameters, this );
+ success = true;
+ }
+ finally {
+ afterOperation(success);
+ }
+ return result;
+ }
+
+ public int executeNativeUpdate(NativeSQLQuerySpecification nativeQuerySpecification,
+ QueryParameters queryParameters) throws HibernateException {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ queryParameters.validateParameters();
+ NativeSQLQueryPlan plan = getNativeSQLQueryPlan(nativeQuerySpecification);
+
+
+ autoFlushIfRequired( plan.getCustomQuery().getQuerySpaces() );
+
+ boolean success = false;
+ int result = 0;
+ try {
+ result = plan.performExecuteUpdate(queryParameters, this);
+ success = true;
+ } finally {
+ afterOperation(success);
+ }
+ return result;
+ }
+
+ public Iterator iterate(String query) throws HibernateException {
+ return iterate( query, new QueryParameters() );
+ }
+
+ public Iterator iterate(String query, Object value, Type type) throws HibernateException {
+ return iterate( query, new QueryParameters(type, value) );
+ }
+
+ public Iterator iterate(String query, Object[] values, Type[] types) throws HibernateException {
+ return iterate( query, new QueryParameters(types, values) );
+ }
+
+ public Iterator iterate(String query, QueryParameters queryParameters) throws HibernateException {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ queryParameters.validateParameters();
+ HQLQueryPlan plan = getHQLQueryPlan( query, true );
+ autoFlushIfRequired( plan.getQuerySpaces() );
+
+ dontFlushFromFind++; //stops flush being called multiple times if this method is recursively called
+ try {
+ return plan.performIterate( queryParameters, this );
+ }
+ finally {
+ dontFlushFromFind--;
+ }
+ }
+
+ public ScrollableResults scroll(String query, QueryParameters queryParameters) throws HibernateException {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ HQLQueryPlan plan = getHQLQueryPlan( query, false );
+ autoFlushIfRequired( plan.getQuerySpaces() );
+ dontFlushFromFind++;
+ try {
+ return plan.performScroll( queryParameters, this );
+ }
+ finally {
+ dontFlushFromFind--;
+ }
+ }
+
+ public int delete(String query) throws HibernateException {
+ return delete( query, ArrayHelper.EMPTY_OBJECT_ARRAY, ArrayHelper.EMPTY_TYPE_ARRAY );
+ }
+
+ public int delete(String query, Object value, Type type) throws HibernateException {
+ return delete( query, new Object[]{value}, new Type[]{type} );
+ }
+
+ public int delete(String query, Object[] values, Type[] types) throws HibernateException {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ if ( query == null ) {
+ throw new IllegalArgumentException("attempt to perform delete-by-query with null query");
+ }
+
+ if ( log.isTraceEnabled() ) {
+ log.trace( "delete: " + query );
+ if ( values.length != 0 ) {
+ log.trace( "parameters: " + StringHelper.toString( values ) );
+ }
+ }
+
+ List list = find( query, values, types );
+ int deletionCount = list.size();
+ for ( int i = 0; i < deletionCount; i++ ) {
+ delete( list.get( i ) );
+ }
+
+ return deletionCount;
+ }
+
+ public Query createFilter(Object collection, String queryString) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ CollectionFilterImpl filter = new CollectionFilterImpl(
+ queryString,
+ collection,
+ this,
+ getFilterQueryPlan( collection, queryString, null, false ).getParameterMetadata()
+ );
+ filter.setComment( queryString );
+ return filter;
+ }
+
+ public Query getNamedQuery(String queryName) throws MappingException {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ return super.getNamedQuery(queryName);
+ }
+
+ public Object instantiate(String entityName, Serializable id) throws HibernateException {
+ return instantiate( factory.getEntityPersister(entityName), id );
+ }
+
+ /**
+ * give the interceptor an opportunity to override the default instantiation
+ */
+ public Object instantiate(EntityPersister persister, Serializable id) throws HibernateException {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ Object result = interceptor.instantiate( persister.getEntityName(), entityMode, id );
+ if ( result == null ) {
+ result = persister.instantiate( id, entityMode );
+ }
+ return result;
+ }
+
+ public EntityMode getEntityMode() {
+ checkTransactionSynchStatus();
+ return entityMode;
+ }
+
+ public void setFlushMode(FlushMode flushMode) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ if ( log.isTraceEnabled() ) {
+ log.trace("setting flush mode to: " + flushMode);
+ }
+ this.flushMode = flushMode;
+ }
+
+ public FlushMode getFlushMode() {
+ checkTransactionSynchStatus();
+ return flushMode;
+ }
+
+ public CacheMode getCacheMode() {
+ checkTransactionSynchStatus();
+ return cacheMode;
+ }
+
+ public void setCacheMode(CacheMode cacheMode) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ if ( log.isTraceEnabled() ) {
+ log.trace("setting cache mode to: " + cacheMode);
+ }
+ this.cacheMode= cacheMode;
+ }
+
+ public Transaction getTransaction() throws HibernateException {
+ errorIfClosed();
+ return jdbcContext.getTransaction();
+ }
+
+ public Transaction beginTransaction() throws HibernateException {
+ errorIfClosed();
+ if ( rootSession != null ) {
+ // todo : should seriously consider not allowing a txn to begin from a child session
+ // can always route the request to the root session...
+ log.warn( "Transaction started on non-root session" );
+ }
+ Transaction result = getTransaction();
+ result.begin();
+ return result;
+ }
+
+ public void afterTransactionBegin(Transaction tx) {
+ errorIfClosed();
+ interceptor.afterTransactionBegin(tx);
+ }
+
+ public EntityPersister getEntityPersister(final String entityName, final Object object) {
+ errorIfClosed();
+ if (entityName==null) {
+ return factory.getEntityPersister( guessEntityName( object ) );
+ }
+ else {
+ // try block is a hack around fact that currently tuplizers are not
+ // given the opportunity to resolve a subclass entity name. this
+ // allows the (we assume custom) interceptor the ability to
+ // influence this decision if we were not able to based on the
+ // given entityName
+ try {
+ return factory.getEntityPersister( entityName )
+ .getSubclassEntityPersister( object, getFactory(), entityMode );
+ }
+ catch( HibernateException e ) {
+ try {
+ return getEntityPersister( null, object );
+ }
+ catch( HibernateException e2 ) {
+ throw e;
+ }
+ }
+ }
+ }
+
+ // not for internal use:
+ public Serializable getIdentifier(Object object) throws HibernateException {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ if ( object instanceof HibernateProxy ) {
+ LazyInitializer li = ( (HibernateProxy) object ).getHibernateLazyInitializer();
+ if ( li.getSession() != this ) {
+ throw new TransientObjectException( "The proxy was not associated with this session" );
+ }
+ return li.getIdentifier();
+ }
+ else {
+ EntityEntry entry = persistenceContext.getEntry(object);
+ if ( entry == null ) {
+ throw new TransientObjectException( "The instance was not associated with this session" );
+ }
+ return entry.getId();
+ }
+ }
+
+ /**
+ * Get the id value for an object that is actually associated with the session. This
+ * is a bit stricter than getEntityIdentifierIfNotUnsaved().
+ */
+ public Serializable getContextEntityIdentifier(Object object) {
+ errorIfClosed();
+ if ( object instanceof HibernateProxy ) {
+ return getProxyIdentifier(object);
+ }
+ else {
+ EntityEntry entry = persistenceContext.getEntry(object);
+ return entry != null ? entry.getId() : null;
+ }
+ }
+
+ private Serializable getProxyIdentifier(Object proxy) {
+ return ( (HibernateProxy) proxy ).getHibernateLazyInitializer().getIdentifier();
+ }
+
+ public Collection filter(Object collection, String filter) throws HibernateException {
+ return listFilter( collection, filter, new QueryParameters( new Type[1], new Object[1] ) );
+ }
+
+ public Collection filter(Object collection, String filter, Object value, Type type) throws HibernateException {
+ return listFilter( collection, filter, new QueryParameters( new Type[]{null, type}, new Object[]{null, value} ) );
+ }
+
+ public Collection filter(Object collection, String filter, Object[] values, Type[] types)
+ throws HibernateException {
+ Object[] vals = new Object[values.length + 1];
+ Type[] typs = new Type[types.length + 1];
+ System.arraycopy( values, 0, vals, 1, values.length );
+ System.arraycopy( types, 0, typs, 1, types.length );
+ return listFilter( collection, filter, new QueryParameters( typs, vals ) );
+ }
+
+ private FilterQueryPlan getFilterQueryPlan(
+ Object collection,
+ String filter,
+ QueryParameters parameters,
+ boolean shallow) throws HibernateException {
+ if ( collection == null ) {
+ throw new NullPointerException( "null collection passed to filter" );
+ }
+
+ CollectionEntry entry = persistenceContext.getCollectionEntryOrNull( collection );
+ final CollectionPersister roleBeforeFlush = (entry == null) ? null : entry.getLoadedPersister();
+
+ FilterQueryPlan plan = null;
+ if ( roleBeforeFlush == null ) {
+ // if it was previously unreferenced, we need to flush in order to
+ // get its state into the database in order to execute query
+ flush();
+ entry = persistenceContext.getCollectionEntryOrNull( collection );
+ CollectionPersister roleAfterFlush = (entry == null) ? null : entry.getLoadedPersister();
+ if ( roleAfterFlush == null ) {
+ throw new QueryException( "The collection was unreferenced" );
+ }
+ plan = factory.getQueryPlanCache().getFilterQueryPlan( filter, roleAfterFlush.getRole(), shallow, getEnabledFilters() );
+ }
+ else {
+ // otherwise, we only need to flush if there are in-memory changes
+ // to the queried tables
+ plan = factory.getQueryPlanCache().getFilterQueryPlan( filter, roleBeforeFlush.getRole(), shallow, getEnabledFilters() );
+ if ( autoFlushIfRequired( plan.getQuerySpaces() ) ) {
+ // might need to run a different filter entirely after the flush
+ // because the collection role may have changed
+ entry = persistenceContext.getCollectionEntryOrNull( collection );
+ CollectionPersister roleAfterFlush = (entry == null) ? null : entry.getLoadedPersister();
+ if ( roleBeforeFlush != roleAfterFlush ) {
+ if ( roleAfterFlush == null ) {
+ throw new QueryException( "The collection was dereferenced" );
+ }
+ plan = factory.getQueryPlanCache().getFilterQueryPlan( filter, roleAfterFlush.getRole(), shallow, getEnabledFilters() );
+ }
+ }
+ }
+
+ if ( parameters != null ) {
+ parameters.getPositionalParameterValues()[0] = entry.getLoadedKey();
+ parameters.getPositionalParameterTypes()[0] = entry.getLoadedPersister().getKeyType();
+ }
+
+ return plan;
+ }
+
+ public List listFilter(Object collection, String filter, QueryParameters queryParameters)
+ throws HibernateException {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ FilterQueryPlan plan = getFilterQueryPlan( collection, filter, queryParameters, false );
+ List results = CollectionHelper.EMPTY_LIST;
+
+ boolean success = false;
+ dontFlushFromFind++; //stops flush being called multiple times if this method is recursively called
+ try {
+ results = plan.performList( queryParameters, this );
+ success = true;
+ }
+ finally {
+ dontFlushFromFind--;
+ afterOperation(success);
+ }
+ return results;
+ }
+
+ public Iterator iterateFilter(Object collection, String filter, QueryParameters queryParameters)
+ throws HibernateException {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ FilterQueryPlan plan = getFilterQueryPlan( collection, filter, queryParameters, true );
+ return plan.performIterate( queryParameters, this );
+ }
+
+ public Criteria createCriteria(Class persistentClass, String alias) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ return new CriteriaImpl( persistentClass.getName(), alias, this );
+ }
+
+ public Criteria createCriteria(String entityName, String alias) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ return new CriteriaImpl(entityName, alias, this);
+ }
+
+ public Criteria createCriteria(Class persistentClass) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ return new CriteriaImpl( persistentClass.getName(), this );
+ }
+
+ public Criteria createCriteria(String entityName) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ return new CriteriaImpl(entityName, this);
+ }
+
+ public ScrollableResults scroll(CriteriaImpl criteria, ScrollMode scrollMode) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ String entityName = criteria.getEntityOrClassName();
+ CriteriaLoader loader = new CriteriaLoader(
+ getOuterJoinLoadable(entityName),
+ factory,
+ criteria,
+ entityName,
+ getEnabledFilters()
+ );
+ autoFlushIfRequired( loader.getQuerySpaces() );
+ dontFlushFromFind++;
+ try {
+ return loader.scroll(this, scrollMode);
+ }
+ finally {
+ dontFlushFromFind--;
+ }
+ }
+
+ public List list(CriteriaImpl criteria) throws HibernateException {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ String[] implementors = factory.getImplementors( criteria.getEntityOrClassName() );
+ int size = implementors.length;
+
+ CriteriaLoader[] loaders = new CriteriaLoader[size];
+ Set spaces = new HashSet();
+ for( int i=0; i <size; i++ ) {
+
+ loaders[i] = new CriteriaLoader(
+ getOuterJoinLoadable( implementors[i] ),
+ factory,
+ criteria,
+ implementors[i],
+ getEnabledFilters()
+ );
+
+ spaces.addAll( loaders[i].getQuerySpaces() );
+
+ }
+
+ autoFlushIfRequired(spaces);
+
+ List results = Collections.EMPTY_LIST;
+ dontFlushFromFind++;
+ boolean success = false;
+ try {
+ for( int i=0; i<size; i++ ) {
+ final List currentResults = loaders[i].list(this);
+ currentResults.addAll(results);
+ results = currentResults;
+ }
+ success = true;
+ }
+ finally {
+ dontFlushFromFind--;
+ afterOperation(success);
+ }
+
+ return results;
+ }
+
+ private OuterJoinLoadable getOuterJoinLoadable(String entityName) throws MappingException {
+ EntityPersister persister = factory.getEntityPersister(entityName);
+ if ( !(persister instanceof OuterJoinLoadable) ) {
+ throw new MappingException( "class persister is not OuterJoinLoadable: " + entityName );
+ }
+ return ( OuterJoinLoadable ) persister;
+ }
+
+ public boolean contains(Object object) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ if ( object instanceof HibernateProxy ) {
+ //do not use proxiesByKey, since not all
+ //proxies that point to this session's
+ //instances are in that collection!
+ LazyInitializer li = ( (HibernateProxy) object ).getHibernateLazyInitializer();
+ if ( li.isUninitialized() ) {
+ //if it is an uninitialized proxy, pointing
+ //with this session, then when it is accessed,
+ //the underlying instance will be "contained"
+ return li.getSession()==this;
+ }
+ else {
+ //if it is initialized, see if the underlying
+ //instance is contained, since we need to
+ //account for the fact that it might have been
+ //evicted
+ object = li.getImplementation();
+ }
+ }
+ // A session is considered to contain an entity only if the entity has
+ // an entry in the session's persistence context and the entry reports
+ // that the entity has not been removed
+ EntityEntry entry = persistenceContext.getEntry( object );
+ return entry != null && entry.getStatus() != Status.DELETED && entry.getStatus() != Status.GONE;
+ }
+
+ public Query createQuery(String queryString) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ return super.createQuery(queryString);
+ }
+
+ public SQLQuery createSQLQuery(String sql) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ return super.createSQLQuery(sql);
+ }
+
+ public Query createSQLQuery(String sql, String returnAlias, Class returnClass) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ return new SQLQueryImpl(
+ sql,
+ new String[] { returnAlias },
+ new Class[] { returnClass },
+ this,
+ factory.getQueryPlanCache().getSQLParameterMetadata( sql )
+ );
+ }
+
+ public Query createSQLQuery(String sql, String returnAliases[], Class returnClasses[]) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ return new SQLQueryImpl(
+ sql,
+ returnAliases,
+ returnClasses,
+ this,
+ factory.getQueryPlanCache().getSQLParameterMetadata( sql )
+ );
+ }
+
+ public ScrollableResults scrollCustomQuery(CustomQuery customQuery, QueryParameters queryParameters)
+ throws HibernateException {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+
+ if ( log.isTraceEnabled() ) {
+ log.trace( "scroll SQL query: " + customQuery.getSQL() );
+ }
+
+ CustomLoader loader = new CustomLoader( customQuery, getFactory() );
+
+ autoFlushIfRequired( loader.getQuerySpaces() );
+
+ dontFlushFromFind++; //stops flush being called multiple times if this method is recursively called
+ try {
+ return loader.scroll(queryParameters, this);
+ }
+ finally {
+ dontFlushFromFind--;
+ }
+ }
+
+ // basically just an adapted copy of find(CriteriaImpl)
+ public List listCustomQuery(CustomQuery customQuery, QueryParameters queryParameters)
+ throws HibernateException {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+
+ if ( log.isTraceEnabled() ) {
+ log.trace( "SQL query: " + customQuery.getSQL() );
+ }
+
+ CustomLoader loader = new CustomLoader( customQuery, getFactory() );
+
+ autoFlushIfRequired( loader.getQuerySpaces() );
+
+ dontFlushFromFind++;
+ boolean success = false;
+ try {
+ List results = loader.list(this, queryParameters);
+ success = true;
+ return results;
+ }
+ finally {
+ dontFlushFromFind--;
+ afterOperation(success);
+ }
+ }
+
+ public SessionFactory getSessionFactory() {
+ checkTransactionSynchStatus();
+ return factory;
+ }
+
+ public void initializeCollection(PersistentCollection collection, boolean writing)
+ throws HibernateException {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ InitializeCollectionEventListener[] listener = listeners.getInitializeCollectionEventListeners();
+ for ( int i = 0; i < listener.length; i++ ) {
+ listener[i].onInitializeCollection( new InitializeCollectionEvent(collection, this) );
+ }
+ }
+
+ public String bestGuessEntityName(Object object) {
+ if (object instanceof HibernateProxy) {
+ LazyInitializer initializer = ( ( HibernateProxy ) object ).getHibernateLazyInitializer();
+ // it is possible for this method to be called during flush processing,
+ // so make certain that we do not accidently initialize an uninitialized proxy
+ if ( initializer.isUninitialized() ) {
+ return initializer.getEntityName();
+ }
+ object = initializer.getImplementation();
+ }
+ EntityEntry entry = persistenceContext.getEntry(object);
+ if (entry==null) {
+ return guessEntityName(object);
+ }
+ else {
+ return entry.getPersister().getEntityName();
+ }
+ }
+
+ public String getEntityName(Object object) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ if (object instanceof HibernateProxy) {
+ if ( !persistenceContext.containsProxy( object ) ) {
+ throw new TransientObjectException("proxy was not associated with the session");
+ }
+ object = ( (HibernateProxy) object ).getHibernateLazyInitializer().getImplementation();
+ }
+
+ EntityEntry entry = persistenceContext.getEntry(object);
+ if ( entry == null ) {
+ throwTransientObjectException( object );
+ }
+ return entry.getPersister().getEntityName();
+ }
+
+ private void throwTransientObjectException(Object object) throws HibernateException {
+ throw new TransientObjectException(
+ "object references an unsaved transient instance - save the transient instance before flushing: " +
+ guessEntityName(object)
+ );
+ }
+
+ public String guessEntityName(Object object) throws HibernateException {
+ errorIfClosed();
+ String entity = interceptor.getEntityName( object );
+ if ( entity == null ) {
+ if ( object instanceof Map ) {
+ entity = (String) ( (Map) object ).get( DynamicMapInstantiator.KEY );
+ if ( entity == null ) {
+ throw new HibernateException( "could not determine type of dynamic entity" );
+ }
+ }
+ else if ( object instanceof Element ) {
+ // TODO : really need to keep a map of nodeName -> entityName, but that would mean nodeName being distinct
+ entity = ( (Element) object ).getName();
+ }
+ else {
+ entity = object.getClass().getName();
+ }
+ }
+ return entity;
+ }
+
+ public void cancelQuery() throws HibernateException {
+ errorIfClosed();
+ getBatcher().cancelLastQuery();
+ }
+
+ public Interceptor getInterceptor() {
+ checkTransactionSynchStatus();
+ return interceptor;
+ }
+
+ public int getDontFlushFromFind() {
+ return dontFlushFromFind;
+ }
+
+ public String toString() {
+ StringBuffer buf = new StringBuffer(500)
+ .append( "SessionImpl(" );
+ if ( !isClosed() ) {
+ buf.append(persistenceContext)
+ .append(";")
+ .append(actionQueue);
+ }
+ else {
+ buf.append("<closed>");
+ }
+ return buf.append(')').toString();
+ }
+
+ public EventListeners getListeners() {
+ return listeners;
+ }
+
+ public ActionQueue getActionQueue() {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ return actionQueue;
+ }
+
+ public PersistenceContext getPersistenceContext() {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ return persistenceContext;
+ }
+
+ public SessionStatistics getStatistics() {
+ checkTransactionSynchStatus();
+ return new SessionStatisticsImpl(this);
+ }
+
+ public boolean isEventSource() {
+ checkTransactionSynchStatus();
+ return true;
+ }
+
+ public void setReadOnly(Object entity, boolean readOnly) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ persistenceContext.setReadOnly(entity, readOnly);
+ }
+
+ public void afterScrollOperation() {
+ // nothing to do in a stateful session
+ }
+
+ public String getFetchProfile() {
+ checkTransactionSynchStatus();
+ return fetchProfile;
+ }
+
+ public JDBCContext getJDBCContext() {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ return jdbcContext;
+ }
+
+ public void setFetchProfile(String fetchProfile) {
+ errorIfClosed();
+ checkTransactionSynchStatus();
+ this.fetchProfile = fetchProfile;
+ }
+
+ private void checkTransactionSynchStatus() {
+ if ( jdbcContext != null && !isClosed() ) {
+ jdbcContext.registerSynchronizationIfPossible();
+ }
+ }
+
+ /**
+ * Used by JDK serialization...
+ *
+ * @param ois The input stream from which we are being read...
+ * @throws IOException Indicates a general IO stream exception
+ * @throws ClassNotFoundException Indicates a class resolution issue
+ */
+ private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
+ log.trace( "deserializing session" );
+
+ boolean isRootSession = ois.readBoolean();
+ connectionReleaseMode = ConnectionReleaseMode.parse( ( String ) ois.readObject() );
+ entityMode = EntityMode.parse( ( String ) ois.readObject() );
+ autoClear = ois.readBoolean();
+ flushMode = FlushMode.parse( ( String ) ois.readObject() );
+ cacheMode = CacheMode.parse( ( String ) ois.readObject() );
+ flushBeforeCompletionEnabled = ois.readBoolean();
+ autoCloseSessionEnabled = ois.readBoolean();
+ fetchProfile = ( String ) ois.readObject();
+ interceptor = ( Interceptor ) ois.readObject();
+
+ factory = SessionFactoryImpl.deserialize( ois );
+ listeners = factory.getEventListeners();
+
+ if ( isRootSession ) {
+ jdbcContext = JDBCContext.deserialize( ois, this, interceptor );
+ }
+
+ persistenceContext = StatefulPersistenceContext.deserialize( ois, this );
+ actionQueue = ActionQueue.deserialize( ois, this );
+
+ enabledFilters = ( Map ) ois.readObject();
+ childSessionsByEntityMode = ( Map ) ois.readObject();
+
+ Iterator iter = enabledFilters.values().iterator();
+ while ( iter.hasNext() ) {
+ ( ( FilterImpl ) iter.next() ).afterDeserialize(factory);
+ }
+
+ if ( isRootSession && childSessionsByEntityMode != null ) {
+ iter = childSessionsByEntityMode.values().iterator();
+ while ( iter.hasNext() ) {
+ final SessionImpl child = ( ( SessionImpl ) iter.next() );
+ child.rootSession = this;
+ child.jdbcContext = this.jdbcContext;
+ }
+ }
+ }
+
+ /**
+ * Used by JDK serialization...
+ *
+ * @param oos The output stream to which we are being written...
+ * @throws IOException Indicates a general IO stream exception
+ */
+ private void writeObject(ObjectOutputStream oos) throws IOException {
+ if ( !jdbcContext.getConnectionManager().isReadyForSerialization() ) {
+ throw new IllegalStateException( "Cannot serialize a session while connected" );
+ }
+
+ log.trace( "serializing session" );
+
+ oos.writeBoolean( rootSession == null );
+ oos.writeObject( connectionReleaseMode.toString() );
+ oos.writeObject( entityMode.toString() );
+ oos.writeBoolean( autoClear );
+ oos.writeObject( flushMode.toString() );
+ oos.writeObject( cacheMode.toString() );
+ oos.writeBoolean( flushBeforeCompletionEnabled );
+ oos.writeBoolean( autoCloseSessionEnabled );
+ oos.writeObject( fetchProfile );
+ // we need to writeObject() on this since interceptor is user defined
+ oos.writeObject( interceptor );
+
+ factory.serialize( oos );
+
+ if ( rootSession == null ) {
+ jdbcContext.serialize( oos );
+ }
+
+ persistenceContext.serialize( oos );
+ actionQueue.serialize( oos );
+
+ // todo : look at optimizing these...
+ oos.writeObject( enabledFilters );
+ oos.writeObject( childSessionsByEntityMode );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/impl/StatelessSessionImpl.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/impl/StatelessSessionImpl.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/impl/StatelessSessionImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,630 @@
+//$Id: StatelessSessionImpl.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.impl;
+
+import java.io.Serializable;
+import java.sql.Connection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.CacheMode;
+import org.hibernate.ConnectionReleaseMode;
+import org.hibernate.Criteria;
+import org.hibernate.EmptyInterceptor;
+import org.hibernate.EntityMode;
+import org.hibernate.FlushMode;
+import org.hibernate.HibernateException;
+import org.hibernate.Interceptor;
+import org.hibernate.LockMode;
+import org.hibernate.MappingException;
+import org.hibernate.ScrollMode;
+import org.hibernate.ScrollableResults;
+import org.hibernate.SessionException;
+import org.hibernate.StatelessSession;
+import org.hibernate.Transaction;
+import org.hibernate.UnresolvableObjectException;
+import org.hibernate.cache.CacheKey;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.engine.EntityKey;
+import org.hibernate.engine.PersistenceContext;
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.StatefulPersistenceContext;
+import org.hibernate.engine.Versioning;
+import org.hibernate.engine.query.HQLQueryPlan;
+import org.hibernate.engine.query.NativeSQLQueryPlan;
+import org.hibernate.engine.query.sql.NativeSQLQuerySpecification;
+import org.hibernate.event.EventListeners;
+import org.hibernate.id.IdentifierGeneratorFactory;
+import org.hibernate.jdbc.Batcher;
+import org.hibernate.jdbc.JDBCContext;
+import org.hibernate.loader.criteria.CriteriaLoader;
+import org.hibernate.loader.custom.CustomLoader;
+import org.hibernate.loader.custom.CustomQuery;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.persister.entity.OuterJoinLoadable;
+import org.hibernate.pretty.MessageHelper;
+import org.hibernate.proxy.HibernateProxy;
+import org.hibernate.type.Type;
+import org.hibernate.util.CollectionHelper;
+
+/**
+ * @author Gavin King
+ */
+public class StatelessSessionImpl extends AbstractSessionImpl
+ implements JDBCContext.Context, StatelessSession {
+
+ private static final Log log = LogFactory.getLog( StatelessSessionImpl.class );
+
+ private JDBCContext jdbcContext;
+ private PersistenceContext temporaryPersistenceContext = new StatefulPersistenceContext( this );
+
+ StatelessSessionImpl(Connection connection, SessionFactoryImpl factory) {
+ super( factory );
+ this.jdbcContext = new JDBCContext( this, connection, EmptyInterceptor.INSTANCE );
+ }
+
+
+ // inserts ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public Serializable insert(Object entity) {
+ errorIfClosed();
+ return insert(null, entity);
+ }
+
+ public Serializable insert(String entityName, Object entity) {
+ errorIfClosed();
+ EntityPersister persister = getEntityPersister(entityName, entity);
+ Serializable id = persister.getIdentifierGenerator().generate(this, entity);
+ Object[] state = persister.getPropertyValues(entity, EntityMode.POJO);
+ if ( persister.isVersioned() ) {
+ boolean substitute = Versioning.seedVersion(state, persister.getVersionProperty(), persister.getVersionType(), this);
+ if ( substitute ) {
+ persister.setPropertyValues( entity, state, EntityMode.POJO );
+ }
+ }
+ if ( id == IdentifierGeneratorFactory.POST_INSERT_INDICATOR ) {
+ id = persister.insert(state, entity, this);
+ }
+ else {
+ persister.insert(id, state, entity, this);
+ }
+ persister.setIdentifier(entity, id, EntityMode.POJO);
+ return id;
+ }
+
+
+ // deletes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public void delete(Object entity) {
+ errorIfClosed();
+ delete(null, entity);
+ }
+
+ public void delete(String entityName, Object entity) {
+ errorIfClosed();
+ EntityPersister persister = getEntityPersister(entityName, entity);
+ Serializable id = persister.getIdentifier(entity, EntityMode.POJO);
+ Object version = persister.getVersion(entity, EntityMode.POJO);
+ persister.delete(id, version, entity, this);
+ }
+
+
+ // updates ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public void update(Object entity) {
+ errorIfClosed();
+ update(null, entity);
+ }
+
+ public void update(String entityName, Object entity) {
+ errorIfClosed();
+ EntityPersister persister = getEntityPersister(entityName, entity);
+ Serializable id = persister.getIdentifier(entity, EntityMode.POJO);
+ Object[] state = persister.getPropertyValues(entity, EntityMode.POJO);
+ Object oldVersion;
+ if ( persister.isVersioned() ) {
+ oldVersion = persister.getVersion(entity, EntityMode.POJO);
+ Object newVersion = Versioning.increment( oldVersion, persister.getVersionType(), this );
+ Versioning.setVersion(state, newVersion, persister);
+ persister.setPropertyValues(entity, state, EntityMode.POJO);
+ }
+ else {
+ oldVersion = null;
+ }
+ persister.update(id, state, null, false, null, oldVersion, entity, null, this);
+ }
+
+
+ // loading ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public Object get(Class entityClass, Serializable id) {
+ return get( entityClass.getName(), id );
+ }
+
+ public Object get(Class entityClass, Serializable id, LockMode lockMode) {
+ return get( entityClass.getName(), id, lockMode );
+ }
+
+ public Object get(String entityName, Serializable id) {
+ return get(entityName, id, LockMode.NONE);
+ }
+
+ public Object get(String entityName, Serializable id, LockMode lockMode) {
+ errorIfClosed();
+ Object result = getFactory().getEntityPersister(entityName)
+ .load(id, null, lockMode, this);
+ temporaryPersistenceContext.clear();
+ return result;
+ }
+
+ public void refresh(Object entity) {
+ refresh( bestGuessEntityName( entity ), entity, LockMode.NONE );
+ }
+
+ public void refresh(String entityName, Object entity) {
+ refresh( entityName, entity, LockMode.NONE );
+ }
+
+ public void refresh(Object entity, LockMode lockMode) {
+ refresh( bestGuessEntityName( entity ), entity, lockMode );
+ }
+
+ public void refresh(String entityName, Object entity, LockMode lockMode) {
+ final EntityPersister persister = this.getEntityPersister( entityName, entity );
+ final Serializable id = persister.getIdentifier( entity, getEntityMode() );
+ if ( log.isTraceEnabled() ) {
+ log.trace(
+ "refreshing transient " +
+ MessageHelper.infoString( persister, id, this.getFactory() )
+ );
+ }
+ // TODO : can this ever happen???
+// EntityKey key = new EntityKey( id, persister, source.getEntityMode() );
+// if ( source.getPersistenceContext().getEntry( key ) != null ) {
+// throw new PersistentObjectException(
+// "attempted to refresh transient instance when persistent " +
+// "instance was already associated with the Session: " +
+// MessageHelper.infoString( persister, id, source.getFactory() )
+// );
+// }
+
+ if ( persister.hasCache() ) {
+ final CacheKey ck = new CacheKey(
+ id,
+ persister.getIdentifierType(),
+ persister.getRootEntityName(),
+ this.getEntityMode(),
+ this.getFactory()
+ );
+ persister.getCacheAccessStrategy().evict( ck );
+ }
+
+ String previousFetchProfile = this.getFetchProfile();
+ Object result = null;
+ try {
+ this.setFetchProfile( "refresh" );
+ result = persister.load( id, entity, lockMode, this );
+ }
+ finally {
+ this.setFetchProfile( previousFetchProfile );
+ }
+ UnresolvableObjectException.throwIfNull( result, id, persister.getEntityName() );
+ }
+
+ public Object immediateLoad(String entityName, Serializable id)
+ throws HibernateException {
+ throw new SessionException("proxies cannot be fetched by a stateless session");
+ }
+
+ public void initializeCollection(
+ PersistentCollection collection,
+ boolean writing) throws HibernateException {
+ throw new SessionException("collections cannot be fetched by a stateless session");
+ }
+
+ public Object instantiate(
+ String entityName,
+ Serializable id) throws HibernateException {
+ errorIfClosed();
+ return getFactory().getEntityPersister( entityName )
+ .instantiate( id, EntityMode.POJO );
+ }
+
+ public Object internalLoad(
+ String entityName,
+ Serializable id,
+ boolean eager,
+ boolean nullable) throws HibernateException {
+ errorIfClosed();
+ EntityPersister persister = getFactory().getEntityPersister(entityName);
+ if ( !eager && persister.hasProxy() ) {
+ return persister.createProxy(id, this);
+ }
+ Object loaded = temporaryPersistenceContext.getEntity( new EntityKey(id, persister, EntityMode.POJO) );
+ //TODO: if not loaded, throw an exception
+ return loaded==null ? get( entityName, id ) : loaded;
+ }
+
+ public Iterator iterate(String query, QueryParameters queryParameters) throws HibernateException {
+ throw new UnsupportedOperationException();
+ }
+
+ public Iterator iterateFilter(Object collection, String filter, QueryParameters queryParameters)
+ throws HibernateException {
+ throw new UnsupportedOperationException();
+ }
+
+ public List listFilter(Object collection, String filter, QueryParameters queryParameters)
+ throws HibernateException {
+ throw new UnsupportedOperationException();
+ }
+
+
+ public boolean isOpen() {
+ return !isClosed();
+ }
+
+ public void close() {
+ managedClose();
+ }
+
+ public ConnectionReleaseMode getConnectionReleaseMode() {
+ return factory.getSettings().getConnectionReleaseMode();
+ }
+
+ public boolean isAutoCloseSessionEnabled() {
+ return factory.getSettings().isAutoCloseSessionEnabled();
+ }
+
+ public boolean isFlushBeforeCompletionEnabled() {
+ return true;
+ }
+
+ public boolean isFlushModeNever() {
+ return false;
+ }
+
+ public void managedClose() {
+ if ( isClosed() ) {
+ throw new SessionException( "Session was already closed!" );
+ }
+ jdbcContext.getConnectionManager().close();
+ setClosed();
+ }
+
+ public void managedFlush() {
+ errorIfClosed();
+ getBatcher().executeBatch();
+ }
+
+ public boolean shouldAutoClose() {
+ return isAutoCloseSessionEnabled() && !isClosed();
+ }
+
+ public void afterTransactionCompletion(boolean successful, Transaction tx) {}
+
+ public void beforeTransactionCompletion(Transaction tx) {}
+
+ public String bestGuessEntityName(Object object) {
+ if (object instanceof HibernateProxy) {
+ object = ( (HibernateProxy) object ).getHibernateLazyInitializer().getImplementation();
+ }
+ return guessEntityName(object);
+ }
+
+ public Connection connection() {
+ errorIfClosed();
+ return jdbcContext.borrowConnection();
+ }
+
+ public int executeUpdate(String query, QueryParameters queryParameters)
+ throws HibernateException {
+ errorIfClosed();
+ queryParameters.validateParameters();
+ HQLQueryPlan plan = getHQLQueryPlan( query, false );
+ boolean success = false;
+ int result = 0;
+ try {
+ result = plan.performExecuteUpdate( queryParameters, this );
+ success = true;
+ }
+ finally {
+ afterOperation(success);
+ }
+ temporaryPersistenceContext.clear();
+ return result;
+ }
+
+ public Batcher getBatcher() {
+ errorIfClosed();
+ return jdbcContext.getConnectionManager()
+ .getBatcher();
+ }
+
+ public CacheMode getCacheMode() {
+ return CacheMode.IGNORE;
+ }
+
+ public int getDontFlushFromFind() {
+ return 0;
+ }
+
+ public Map getEnabledFilters() {
+ return CollectionHelper.EMPTY_MAP;
+ }
+
+ public Serializable getContextEntityIdentifier(Object object) {
+ errorIfClosed();
+ return null;
+ }
+
+ public EntityMode getEntityMode() {
+ return EntityMode.POJO;
+ }
+
+ public EntityPersister getEntityPersister(String entityName, Object object)
+ throws HibernateException {
+ errorIfClosed();
+ if ( entityName==null ) {
+ return factory.getEntityPersister( guessEntityName( object ) );
+ }
+ else {
+ return factory.getEntityPersister( entityName )
+ .getSubclassEntityPersister( object, getFactory(), EntityMode.POJO );
+ }
+ }
+
+ public Object getEntityUsingInterceptor(EntityKey key) throws HibernateException {
+ errorIfClosed();
+ return null;
+ }
+
+ public Type getFilterParameterType(String filterParameterName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public Object getFilterParameterValue(String filterParameterName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public FlushMode getFlushMode() {
+ return FlushMode.COMMIT;
+ }
+
+ public Interceptor getInterceptor() {
+ return EmptyInterceptor.INSTANCE;
+ }
+
+ public EventListeners getListeners() {
+ throw new UnsupportedOperationException();
+ }
+
+ public PersistenceContext getPersistenceContext() {
+ return temporaryPersistenceContext;
+ }
+
+ public long getTimestamp() {
+ throw new UnsupportedOperationException();
+ }
+
+ public String guessEntityName(Object entity) throws HibernateException {
+ errorIfClosed();
+ return entity.getClass().getName();
+ }
+
+
+ public boolean isConnected() {
+ return jdbcContext.getConnectionManager().isCurrentlyConnected();
+ }
+
+ public boolean isTransactionInProgress() {
+ return jdbcContext.isTransactionInProgress();
+ }
+
+ public void setAutoClear(boolean enabled) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setCacheMode(CacheMode cm) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setFlushMode(FlushMode fm) {
+ throw new UnsupportedOperationException();
+ }
+
+ public Transaction getTransaction() throws HibernateException {
+ errorIfClosed();
+ return jdbcContext.getTransaction();
+ }
+
+ public Transaction beginTransaction() throws HibernateException {
+ errorIfClosed();
+ Transaction result = getTransaction();
+ result.begin();
+ return result;
+ }
+
+ public boolean isEventSource() {
+ return false;
+ }
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ //TODO: COPY/PASTE FROM SessionImpl, pull up!
+
+ public List list(String query, QueryParameters queryParameters) throws HibernateException {
+ errorIfClosed();
+ queryParameters.validateParameters();
+ HQLQueryPlan plan = getHQLQueryPlan( query, false );
+ boolean success = false;
+ List results = CollectionHelper.EMPTY_LIST;
+ try {
+ results = plan.performList( queryParameters, this );
+ success = true;
+ }
+ finally {
+ afterOperation(success);
+ }
+ temporaryPersistenceContext.clear();
+ return results;
+ }
+
+ public void afterOperation(boolean success) {
+ if ( !jdbcContext.isTransactionInProgress() ) {
+ jdbcContext.afterNontransactionalQuery(success);
+ }
+ }
+
+ public Criteria createCriteria(Class persistentClass, String alias) {
+ errorIfClosed();
+ return new CriteriaImpl( persistentClass.getName(), alias, this );
+ }
+
+ public Criteria createCriteria(String entityName, String alias) {
+ errorIfClosed();
+ return new CriteriaImpl(entityName, alias, this);
+ }
+
+ public Criteria createCriteria(Class persistentClass) {
+ errorIfClosed();
+ return new CriteriaImpl( persistentClass.getName(), this );
+ }
+
+ public Criteria createCriteria(String entityName) {
+ errorIfClosed();
+ return new CriteriaImpl(entityName, this);
+ }
+
+ public ScrollableResults scroll(CriteriaImpl criteria, ScrollMode scrollMode) {
+ errorIfClosed();
+ String entityName = criteria.getEntityOrClassName();
+ CriteriaLoader loader = new CriteriaLoader(
+ getOuterJoinLoadable(entityName),
+ factory,
+ criteria,
+ entityName,
+ getEnabledFilters()
+ );
+ return loader.scroll(this, scrollMode);
+ }
+
+ public List list(CriteriaImpl criteria) throws HibernateException {
+ errorIfClosed();
+ String[] implementors = factory.getImplementors( criteria.getEntityOrClassName() );
+ int size = implementors.length;
+
+ CriteriaLoader[] loaders = new CriteriaLoader[size];
+ for( int i=0; i <size; i++ ) {
+ loaders[i] = new CriteriaLoader(
+ getOuterJoinLoadable( implementors[i] ),
+ factory,
+ criteria,
+ implementors[i],
+ getEnabledFilters()
+ );
+ }
+
+
+ List results = Collections.EMPTY_LIST;
+ boolean success = false;
+ try {
+ for( int i=0; i<size; i++ ) {
+ final List currentResults = loaders[i].list(this);
+ currentResults.addAll(results);
+ results = currentResults;
+ }
+ success = true;
+ }
+ finally {
+ afterOperation(success);
+ }
+ temporaryPersistenceContext.clear();
+ return results;
+ }
+
+ private OuterJoinLoadable getOuterJoinLoadable(String entityName) throws MappingException {
+ EntityPersister persister = factory.getEntityPersister(entityName);
+ if ( !(persister instanceof OuterJoinLoadable) ) {
+ throw new MappingException( "class persister is not OuterJoinLoadable: " + entityName );
+ }
+ return ( OuterJoinLoadable ) persister;
+ }
+
+ public List listCustomQuery(CustomQuery customQuery, QueryParameters queryParameters)
+ throws HibernateException {
+ errorIfClosed();
+ CustomLoader loader = new CustomLoader( customQuery, getFactory() );
+
+ boolean success = false;
+ List results;
+ try {
+ results = loader.list(this, queryParameters);
+ success = true;
+ }
+ finally {
+ afterOperation(success);
+ }
+ temporaryPersistenceContext.clear();
+ return results;
+ }
+
+ public ScrollableResults scrollCustomQuery(CustomQuery customQuery, QueryParameters queryParameters)
+ throws HibernateException {
+ errorIfClosed();
+ CustomLoader loader = new CustomLoader( customQuery, getFactory() );
+ return loader.scroll(queryParameters, this);
+ }
+
+ public ScrollableResults scroll(String query, QueryParameters queryParameters) throws HibernateException {
+ errorIfClosed();
+ HQLQueryPlan plan = getHQLQueryPlan( query, false );
+ return plan.performScroll( queryParameters, this );
+ }
+
+ public void afterScrollOperation() {
+ temporaryPersistenceContext.clear();
+ }
+
+ public void flush() {}
+
+ public String getFetchProfile() {
+ return null;
+ }
+
+ public JDBCContext getJDBCContext() {
+ return jdbcContext;
+ }
+
+ public void setFetchProfile(String name) {}
+
+ public void afterTransactionBegin(Transaction tx) {}
+
+ protected boolean autoFlushIfRequired(Set querySpaces) throws HibernateException {
+ // no auto-flushing to support in stateless session
+ return false;
+ }
+
+ public int executeNativeUpdate(NativeSQLQuerySpecification nativeSQLQuerySpecification,
+ QueryParameters queryParameters) throws HibernateException {
+ errorIfClosed();
+ queryParameters.validateParameters();
+ NativeSQLQueryPlan plan = getNativeSQLQueryPlan(nativeSQLQuerySpecification);
+
+ boolean success = false;
+ int result = 0;
+ try {
+ result = plan.performExecuteUpdate(queryParameters, this);
+ success = true;
+ } finally {
+ afterOperation(success);
+ }
+ temporaryPersistenceContext.clear();
+ return result;
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/impl/StatelessSessionImpl.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/impl/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/impl/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/impl/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,9 @@
+<html>
+<head></head>
+<body>
+<p>
+ This package contains implementations of the
+ central Hibernate APIs, especially the
+ Hibernate session.
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/impl/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/AbstractFieldInterceptor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/AbstractFieldInterceptor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/AbstractFieldInterceptor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,103 @@
+package org.hibernate.intercept;
+
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.LazyInitializationException;
+
+import java.util.Set;
+import java.io.Serializable;
+
+/**
+ * @author Steve Ebersole
+ */
+public abstract class AbstractFieldInterceptor implements FieldInterceptor, Serializable {
+
+ private transient SessionImplementor session;
+ private Set uninitializedFields;
+ private final String entityName;
+
+ private transient boolean initializing;
+ private boolean dirty;
+
+ protected AbstractFieldInterceptor(SessionImplementor session, Set uninitializedFields, String entityName) {
+ this.session = session;
+ this.uninitializedFields = uninitializedFields;
+ this.entityName = entityName;
+ }
+
+
+ // FieldInterceptor impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public final void setSession(SessionImplementor session) {
+ this.session = session;
+ }
+
+ public final boolean isInitialized() {
+ return uninitializedFields == null || uninitializedFields.size() == 0;
+ }
+
+ public final boolean isInitialized(String field) {
+ return uninitializedFields == null || !uninitializedFields.contains( field );
+ }
+
+ public final void dirty() {
+ dirty = true;
+ }
+
+ public final boolean isDirty() {
+ return dirty;
+ }
+
+ public final void clearDirty() {
+ dirty = false;
+ }
+
+
+ // subclass accesses ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ protected final Object intercept(Object target, String fieldName, Object value) {
+ if ( initializing ) {
+ return value;
+ }
+
+ if ( uninitializedFields != null && uninitializedFields.contains( fieldName ) ) {
+ if ( session == null ) {
+ throw new LazyInitializationException( "entity with lazy properties is not associated with a session" );
+ }
+ else if ( !session.isOpen() || !session.isConnected() ) {
+ throw new LazyInitializationException( "session is not connected" );
+ }
+
+ final Object result;
+ initializing = true;
+ try {
+ result = ( ( LazyPropertyInitializer ) session.getFactory()
+ .getEntityPersister( entityName ) )
+ .initializeLazyProperty( fieldName, target, session );
+ }
+ finally {
+ initializing = false;
+ }
+ uninitializedFields = null; //let's assume that there is only one lazy fetch group, for now!
+ return result;
+ }
+ else {
+ return value;
+ }
+ }
+
+ public final SessionImplementor getSession() {
+ return session;
+ }
+
+ public final Set getUninitializedFields() {
+ return uninitializedFields;
+ }
+
+ public final String getEntityName() {
+ return entityName;
+ }
+
+ public final boolean isInitializing() {
+ return initializing;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/FieldInterceptionHelper.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/FieldInterceptionHelper.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/FieldInterceptionHelper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,93 @@
+package org.hibernate.intercept;
+
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.intercept.cglib.CGLIBHelper;
+import org.hibernate.intercept.javassist.JavassistHelper;
+
+import java.util.Set;
+
+/**
+ * Helper class for dealing with enhanced entity classes.
+ *
+ * @author Steve Ebersole
+ */
+public class FieldInterceptionHelper {
+
+ // VERY IMPORTANT!!!! - This class needs to be free of any static references
+ // to any CGLIB or Javassist classes. Otherwise, users will always need both
+ // on their classpaths no matter which (if either) they use.
+ //
+ // Another option here would be to remove the Hibernate.isPropertyInitialized()
+ // method and have the users go through the SessionFactory to get this information.
+
+ private FieldInterceptionHelper() {
+ }
+
+ public static boolean isInstrumented(Class entityClass) {
+ Class[] definedInterfaces = entityClass.getInterfaces();
+ for ( int i = 0; i < definedInterfaces.length; i++ ) {
+ if ( "net.sf.cglib.transform.impl.InterceptFieldEnabled".equals( definedInterfaces[i].getName() )
+ || "org.hibernate.bytecode.javassist.FieldHandled".equals( definedInterfaces[i].getName() ) ) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean isInstrumented(Object entity) {
+ return entity != null && isInstrumented( entity.getClass() );
+ }
+
+ public static FieldInterceptor extractFieldInterceptor(Object entity) {
+ if ( entity == null ) {
+ return null;
+ }
+ Class[] definedInterfaces = entity.getClass().getInterfaces();
+ for ( int i = 0; i < definedInterfaces.length; i++ ) {
+ if ( "net.sf.cglib.transform.impl.InterceptFieldEnabled".equals( definedInterfaces[i].getName() ) ) {
+ // we have a CGLIB enhanced entity
+ return CGLIBHelper.extractFieldInterceptor( entity );
+ }
+ else if ( "org.hibernate.bytecode.javassist.FieldHandled".equals( definedInterfaces[i].getName() ) ) {
+ // we have a Javassist enhanced entity
+ return JavassistHelper.extractFieldInterceptor( entity );
+ }
+ }
+ return null;
+ }
+
+ public static FieldInterceptor injectFieldInterceptor(
+ Object entity,
+ String entityName,
+ Set uninitializedFieldNames,
+ SessionImplementor session) {
+ if ( entity != null ) {
+ Class[] definedInterfaces = entity.getClass().getInterfaces();
+ for ( int i = 0; i < definedInterfaces.length; i++ ) {
+ if ( "net.sf.cglib.transform.impl.InterceptFieldEnabled".equals( definedInterfaces[i].getName() ) ) {
+ // we have a CGLIB enhanced entity
+ return CGLIBHelper.injectFieldInterceptor( entity, entityName, uninitializedFieldNames, session );
+ }
+ else if ( "org.hibernate.bytecode.javassist.FieldHandled".equals( definedInterfaces[i].getName() ) ) {
+ // we have a Javassist enhanced entity
+ return JavassistHelper.injectFieldInterceptor( entity, entityName, uninitializedFieldNames, session );
+ }
+ }
+ }
+ return null;
+ }
+
+ public static void clearDirty(Object entity) {
+ FieldInterceptor interceptor = extractFieldInterceptor( entity );
+ if ( interceptor != null ) {
+ interceptor.clearDirty();
+ }
+ }
+
+ public static void markDirty(Object entity) {
+ FieldInterceptor interceptor = extractFieldInterceptor( entity );
+ if ( interceptor != null ) {
+ interceptor.dirty();
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/FieldInterceptor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/FieldInterceptor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/FieldInterceptor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,50 @@
+package org.hibernate.intercept;
+
+import org.hibernate.engine.SessionImplementor;
+
+/**
+ * Contract for field interception handlers.
+ *
+ * @author Steve Ebersole
+ */
+public interface FieldInterceptor {
+
+ /**
+ * Use to associate the entity to which we are bound to the given session.
+ *
+ * @param session The session to which we are now associated.
+ */
+ public void setSession(SessionImplementor session);
+
+ /**
+ * Is the entity to which we are bound completely initialized?
+ *
+ * @return True if the entity is initialized; otherwise false.
+ */
+ public boolean isInitialized();
+
+ /**
+ * The the given field initialized for the entity to which we are bound?
+ *
+ * @param field The name of the field to check
+ * @return True if the given field is initialized; otherwise false.
+ */
+ public boolean isInitialized(String field);
+
+ /**
+ * Forcefully mark the entity as being dirty.
+ */
+ public void dirty();
+
+ /**
+ * Is the entity considered dirty?
+ *
+ * @return True if the entity is dirty; otherwise false.
+ */
+ public boolean isDirty();
+
+ /**
+ * Clear the internal dirty flag.
+ */
+ public void clearDirty();
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/FieldInterceptor.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/LazyPropertyInitializer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/LazyPropertyInitializer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/LazyPropertyInitializer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,34 @@
+//$Id: LazyPropertyInitializer.java 9210 2006-02-03 22:15:19Z steveebersole $
+package org.hibernate.intercept;
+
+import java.io.Serializable;
+
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionImplementor;
+
+/**
+ * Contract for controlling how lazy properties get initialized.
+ *
+ * @author Gavin King
+ */
+public interface LazyPropertyInitializer {
+
+ /**
+ * Marker value for uninitialized properties
+ */
+ public static final Serializable UNFETCHED_PROPERTY = new Serializable() {
+ public String toString() {
+ return "<lazy>";
+ }
+ public Object readResolve() {
+ return UNFETCHED_PROPERTY;
+ }
+ };
+
+ /**
+ * Initialize the property, and return its new value
+ */
+ public Object initializeLazyProperty(String fieldName, Object entity, SessionImplementor session)
+ throws HibernateException;
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/LazyPropertyInitializer.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/cglib/CGLIBHelper.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/cglib/CGLIBHelper.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/cglib/CGLIBHelper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,32 @@
+package org.hibernate.intercept.cglib;
+
+import org.hibernate.intercept.FieldInterceptor;
+import org.hibernate.engine.SessionImplementor;
+import net.sf.cglib.transform.impl.InterceptFieldEnabled;
+
+import java.util.Set;
+
+/**
+ * @author Steve Ebersole
+ */
+public class CGLIBHelper {
+ private CGLIBHelper() {
+ }
+
+ public static FieldInterceptor extractFieldInterceptor(Object entity) {
+ return ( FieldInterceptor ) ( ( InterceptFieldEnabled ) entity ).getInterceptFieldCallback();
+ }
+
+ public static FieldInterceptor injectFieldInterceptor(
+ Object entity,
+ String entityName,
+ Set uninitializedFieldNames,
+ SessionImplementor session) {
+ FieldInterceptorImpl fieldInterceptor = new FieldInterceptorImpl(
+ session, uninitializedFieldNames, entityName
+ );
+ ( ( InterceptFieldEnabled ) entity ).setInterceptFieldCallback( fieldInterceptor );
+ return fieldInterceptor;
+
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/cglib/FieldInterceptorImpl.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/cglib/FieldInterceptorImpl.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/cglib/FieldInterceptorImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,147 @@
+//$Id: FieldInterceptorImpl.java 9210 2006-02-03 22:15:19Z steveebersole $
+package org.hibernate.intercept.cglib;
+
+import java.io.Serializable;
+import java.util.Set;
+
+import net.sf.cglib.transform.impl.InterceptFieldCallback;
+
+import org.hibernate.intercept.AbstractFieldInterceptor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.proxy.HibernateProxy;
+import org.hibernate.proxy.LazyInitializer;
+
+/**
+ * A field-level interceptor that initializes lazily fetched properties.
+ * This interceptor can be attached to classes instrumented by CGLIB.
+ * Note that this implementation assumes that the instance variable
+ * name is the same as the name of the persistent property that must
+ * be loaded.
+ *
+ * @author Gavin King
+ */
+public final class FieldInterceptorImpl extends AbstractFieldInterceptor implements InterceptFieldCallback, Serializable {
+
+ /**
+ * Package-protected constructor
+ *
+ * @param session
+ * @param uninitializedFields
+ * @param entityName
+ */
+ FieldInterceptorImpl(SessionImplementor session, Set uninitializedFields, String entityName) {
+ super( session, uninitializedFields, entityName );
+ }
+
+ public boolean readBoolean(Object target, String name, boolean oldValue) {
+ return ( ( Boolean ) intercept( target, name, oldValue ? Boolean.TRUE : Boolean.FALSE ) )
+ .booleanValue();
+ }
+
+ public byte readByte(Object target, String name, byte oldValue) {
+ return ( ( Byte ) intercept( target, name, new Byte( oldValue ) ) ).byteValue();
+ }
+
+ public char readChar(Object target, String name, char oldValue) {
+ return ( ( Character ) intercept( target, name, new Character( oldValue ) ) )
+ .charValue();
+ }
+
+ public double readDouble(Object target, String name, double oldValue) {
+ return ( ( Double ) intercept( target, name, new Double( oldValue ) ) )
+ .doubleValue();
+ }
+
+ public float readFloat(Object target, String name, float oldValue) {
+ return ( ( Float ) intercept( target, name, new Float( oldValue ) ) )
+ .floatValue();
+ }
+
+ public int readInt(Object target, String name, int oldValue) {
+ return ( ( Integer ) intercept( target, name, new Integer( oldValue ) ) )
+ .intValue();
+ }
+
+ public long readLong(Object target, String name, long oldValue) {
+ return ( ( Long ) intercept( target, name, new Long( oldValue ) ) ).longValue();
+ }
+
+ public short readShort(Object target, String name, short oldValue) {
+ return ( ( Short ) intercept( target, name, new Short( oldValue ) ) )
+ .shortValue();
+ }
+
+ public Object readObject(Object target, String name, Object oldValue) {
+ Object value = intercept( target, name, oldValue );
+ if (value instanceof HibernateProxy) {
+ LazyInitializer li = ( (HibernateProxy) value ).getHibernateLazyInitializer();
+ if ( li.isUnwrap() ) {
+ value = li.getImplementation();
+ }
+ }
+ return value;
+ }
+
+ public boolean writeBoolean(Object target, String name, boolean oldValue, boolean newValue) {
+ dirty();
+ intercept( target, name, oldValue ? Boolean.TRUE : Boolean.FALSE );
+ return newValue;
+ }
+
+ public byte writeByte(Object target, String name, byte oldValue, byte newValue) {
+ dirty();
+ intercept( target, name, new Byte( oldValue ) );
+ return newValue;
+ }
+
+ public char writeChar(Object target, String name, char oldValue, char newValue) {
+ dirty();
+ intercept( target, name, new Character( oldValue ) );
+ return newValue;
+ }
+
+ public double writeDouble(Object target, String name, double oldValue, double newValue) {
+ dirty();
+ intercept( target, name, new Double( oldValue ) );
+ return newValue;
+ }
+
+ public float writeFloat(Object target, String name, float oldValue, float newValue) {
+ dirty();
+ intercept( target, name, new Float( oldValue ) );
+ return newValue;
+ }
+
+ public int writeInt(Object target, String name, int oldValue, int newValue) {
+ dirty();
+ intercept( target, name, new Integer( oldValue ) );
+ return newValue;
+ }
+
+ public long writeLong(Object target, String name, long oldValue, long newValue) {
+ dirty();
+ intercept( target, name, new Long( oldValue ) );
+ return newValue;
+ }
+
+ public short writeShort(Object target, String name, short oldValue, short newValue) {
+ dirty();
+ intercept( target, name, new Short( oldValue ) );
+ return newValue;
+ }
+
+ public Object writeObject(Object target, String name, Object oldValue, Object newValue) {
+ dirty();
+ intercept( target, name, oldValue );
+ return newValue;
+ }
+
+ public String toString() {
+ return "FieldInterceptorImpl(" +
+ "entityName=" + getEntityName() +
+ ",dirty=" + isDirty() +
+ ",uninitializedFields=" + getUninitializedFields() +
+ ')';
+ }
+
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/javassist/FieldInterceptorImpl.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/javassist/FieldInterceptorImpl.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/javassist/FieldInterceptorImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,154 @@
+//$Id: FieldInterceptorImpl.java 10206 2006-08-03 19:59:42Z steve.ebersole at jboss.com $
+package org.hibernate.intercept.javassist;
+
+import java.io.Serializable;
+import java.util.Set;
+
+import org.hibernate.bytecode.javassist.FieldHandler;
+import org.hibernate.intercept.AbstractFieldInterceptor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.proxy.HibernateProxy;
+import org.hibernate.proxy.LazyInitializer;
+
+/**
+ * A field-level interceptor that initializes lazily fetched properties.
+ * This interceptor can be attached to classes instrumented by Javassist.
+ * Note that this implementation assumes that the instance variable
+ * name is the same as the name of the persistent property that must
+ * be loaded.
+ * </p>
+ * Note: most of the interesting functionality here is farmed off
+ * to the super-class. The stuff here mainly acts as an adapter to the
+ * Javassist-specific functionality, routing interception through
+ * the super-class's intercept() method
+ *
+ * @author Steve Ebersole
+ */
+public final class FieldInterceptorImpl extends AbstractFieldInterceptor implements FieldHandler, Serializable {
+
+ /**
+ * Package-protected constructor.
+ *
+ * @param session
+ * @param uninitializedFields
+ * @param entityName
+ */
+ FieldInterceptorImpl(SessionImplementor session, Set uninitializedFields, String entityName) {
+ super( session, uninitializedFields, entityName );
+ }
+
+
+ // FieldHandler impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public boolean readBoolean(Object target, String name, boolean oldValue) {
+ return ( ( Boolean ) intercept( target, name, oldValue ? Boolean.TRUE : Boolean.FALSE ) )
+ .booleanValue();
+ }
+
+ public byte readByte(Object target, String name, byte oldValue) {
+ return ( ( Byte ) intercept( target, name, new Byte( oldValue ) ) ).byteValue();
+ }
+
+ public char readChar(Object target, String name, char oldValue) {
+ return ( ( Character ) intercept( target, name, new Character( oldValue ) ) )
+ .charValue();
+ }
+
+ public double readDouble(Object target, String name, double oldValue) {
+ return ( ( Double ) intercept( target, name, new Double( oldValue ) ) )
+ .doubleValue();
+ }
+
+ public float readFloat(Object target, String name, float oldValue) {
+ return ( ( Float ) intercept( target, name, new Float( oldValue ) ) )
+ .floatValue();
+ }
+
+ public int readInt(Object target, String name, int oldValue) {
+ return ( ( Integer ) intercept( target, name, new Integer( oldValue ) ) )
+ .intValue();
+ }
+
+ public long readLong(Object target, String name, long oldValue) {
+ return ( ( Long ) intercept( target, name, new Long( oldValue ) ) ).longValue();
+ }
+
+ public short readShort(Object target, String name, short oldValue) {
+ return ( ( Short ) intercept( target, name, new Short( oldValue ) ) )
+ .shortValue();
+ }
+
+ public Object readObject(Object target, String name, Object oldValue) {
+ Object value = intercept( target, name, oldValue );
+ if (value instanceof HibernateProxy) {
+ LazyInitializer li = ( (HibernateProxy) value ).getHibernateLazyInitializer();
+ if ( li.isUnwrap() ) {
+ value = li.getImplementation();
+ }
+ }
+ return value;
+ }
+
+ public boolean writeBoolean(Object target, String name, boolean oldValue, boolean newValue) {
+ dirty();
+ intercept( target, name, oldValue ? Boolean.TRUE : Boolean.FALSE );
+ return newValue;
+ }
+
+ public byte writeByte(Object target, String name, byte oldValue, byte newValue) {
+ dirty();
+ intercept( target, name, new Byte( oldValue ) );
+ return newValue;
+ }
+
+ public char writeChar(Object target, String name, char oldValue, char newValue) {
+ dirty();
+ intercept( target, name, new Character( oldValue ) );
+ return newValue;
+ }
+
+ public double writeDouble(Object target, String name, double oldValue, double newValue) {
+ dirty();
+ intercept( target, name, new Double( oldValue ) );
+ return newValue;
+ }
+
+ public float writeFloat(Object target, String name, float oldValue, float newValue) {
+ dirty();
+ intercept( target, name, new Float( oldValue ) );
+ return newValue;
+ }
+
+ public int writeInt(Object target, String name, int oldValue, int newValue) {
+ dirty();
+ intercept( target, name, new Integer( oldValue ) );
+ return newValue;
+ }
+
+ public long writeLong(Object target, String name, long oldValue, long newValue) {
+ dirty();
+ intercept( target, name, new Long( oldValue ) );
+ return newValue;
+ }
+
+ public short writeShort(Object target, String name, short oldValue, short newValue) {
+ dirty();
+ intercept( target, name, new Short( oldValue ) );
+ return newValue;
+ }
+
+ public Object writeObject(Object target, String name, Object oldValue, Object newValue) {
+ dirty();
+ intercept( target, name, oldValue );
+ return newValue;
+ }
+
+ public String toString() {
+ return "FieldInterceptorImpl(" +
+ "entityName=" + getEntityName() +
+ ",dirty=" + isDirty() +
+ ",uninitializedFields=" + getUninitializedFields() +
+ ')';
+ }
+
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/javassist/JavassistHelper.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/javassist/JavassistHelper.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/javassist/JavassistHelper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,29 @@
+package org.hibernate.intercept.javassist;
+
+import org.hibernate.intercept.FieldInterceptor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.bytecode.javassist.FieldHandled;
+
+import java.util.Set;
+
+/**
+ * @author Steve Ebersole
+ */
+public class JavassistHelper {
+ private JavassistHelper() {
+ }
+
+ public static FieldInterceptor extractFieldInterceptor(Object entity) {
+ return ( FieldInterceptor ) ( ( FieldHandled ) entity ).getFieldHandler();
+ }
+
+ public static FieldInterceptor injectFieldInterceptor(
+ Object entity,
+ String entityName,
+ Set uninitializedFieldNames,
+ SessionImplementor session) {
+ FieldInterceptorImpl fieldInterceptor = new FieldInterceptorImpl( session, uninitializedFieldNames, entityName );
+ ( ( FieldHandled ) entity ).setFieldHandler( fieldInterceptor );
+ return fieldInterceptor;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,10 @@
+<html>
+<head></head>
+<body>
+<p>
+ This package implements an interception
+ mechanism for lazy property fetching,
+ based on CGLIB bytecode instrumentation.
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/intercept/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/AbstractBatcher.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/AbstractBatcher.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/AbstractBatcher.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,620 @@
+//$Id: AbstractBatcher.java 11332 2007-03-22 17:34:55Z steve.ebersole at jboss.com $
+package org.hibernate.jdbc;
+
+import java.sql.CallableStatement;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.HashSet;
+import java.util.Iterator;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.AssertionFailure;
+import org.hibernate.HibernateException;
+import org.hibernate.Interceptor;
+import org.hibernate.ScrollMode;
+import org.hibernate.TransactionException;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.exception.JDBCExceptionHelper;
+import org.hibernate.pretty.Formatter;
+import org.hibernate.util.GetGeneratedKeysHelper;
+import org.hibernate.util.JDBCExceptionReporter;
+import org.hibernate.util.NamedGeneratedKeysHelper;
+
+/**
+ * Manages prepared statements and batching.
+ *
+ * @author Gavin King
+ */
+public abstract class AbstractBatcher implements Batcher {
+
+ private static int globalOpenPreparedStatementCount;
+ private static int globalOpenResultSetCount;
+
+ private int openPreparedStatementCount;
+ private int openResultSetCount;
+
+ protected static final Log log = LogFactory.getLog(AbstractBatcher.class);
+ protected static final Log SQL_LOG = LogFactory.getLog("org.hibernate.SQL");
+
+ private final ConnectionManager connectionManager;
+ private final SessionFactoryImplementor factory;
+
+ private PreparedStatement batchUpdate;
+ private String batchUpdateSQL;
+
+ private HashSet statementsToClose = new HashSet();
+ private HashSet resultSetsToClose = new HashSet();
+ private PreparedStatement lastQuery;
+
+ private boolean releasing = false;
+ private final Interceptor interceptor;
+
+ private long transactionTimeout = -1;
+ boolean isTransactionTimeoutSet;
+
+ public AbstractBatcher(ConnectionManager connectionManager, Interceptor interceptor) {
+ this.connectionManager = connectionManager;
+ this.interceptor = interceptor;
+ this.factory = connectionManager.getFactory();
+ }
+
+ public void setTransactionTimeout(int seconds) {
+ isTransactionTimeoutSet = true;
+ transactionTimeout = System.currentTimeMillis() / 1000 + seconds;
+ }
+
+ public void unsetTransactionTimeout() {
+ isTransactionTimeoutSet = false;
+ }
+
+ protected PreparedStatement getStatement() {
+ return batchUpdate;
+ }
+
+ public CallableStatement prepareCallableStatement(String sql)
+ throws SQLException, HibernateException {
+ executeBatch();
+ logOpenPreparedStatement();
+ return getCallableStatement( connectionManager.getConnection(), sql, false);
+ }
+
+ public PreparedStatement prepareStatement(String sql)
+ throws SQLException, HibernateException {
+ return prepareStatement( sql, false );
+ }
+
+ public PreparedStatement prepareStatement(String sql, boolean getGeneratedKeys)
+ throws SQLException, HibernateException {
+ executeBatch();
+ logOpenPreparedStatement();
+ return getPreparedStatement(
+ connectionManager.getConnection(),
+ sql,
+ false,
+ getGeneratedKeys,
+ null,
+ null,
+ false
+ );
+ }
+
+ public PreparedStatement prepareStatement(String sql, String[] columnNames)
+ throws SQLException, HibernateException {
+ executeBatch();
+ logOpenPreparedStatement();
+ return getPreparedStatement(
+ connectionManager.getConnection(),
+ sql,
+ false,
+ false,
+ columnNames,
+ null,
+ false
+ );
+ }
+
+ public PreparedStatement prepareSelectStatement(String sql)
+ throws SQLException, HibernateException {
+ logOpenPreparedStatement();
+ return getPreparedStatement(
+ connectionManager.getConnection(),
+ sql,
+ false,
+ false,
+ null,
+ null,
+ false
+ );
+ }
+
+ public PreparedStatement prepareQueryStatement(
+ String sql,
+ boolean scrollable,
+ ScrollMode scrollMode) throws SQLException, HibernateException {
+ logOpenPreparedStatement();
+ PreparedStatement ps = getPreparedStatement(
+ connectionManager.getConnection(),
+ sql,
+ scrollable,
+ scrollMode
+ );
+ setStatementFetchSize( ps );
+ statementsToClose.add( ps );
+ lastQuery = ps;
+ return ps;
+ }
+
+ public CallableStatement prepareCallableQueryStatement(
+ String sql,
+ boolean scrollable,
+ ScrollMode scrollMode) throws SQLException, HibernateException {
+ logOpenPreparedStatement();
+ CallableStatement ps = ( CallableStatement ) getPreparedStatement(
+ connectionManager.getConnection(),
+ sql,
+ scrollable,
+ false,
+ null,
+ scrollMode,
+ true
+ );
+ setStatementFetchSize( ps );
+ statementsToClose.add( ps );
+ lastQuery = ps;
+ return ps;
+ }
+
+ public void abortBatch(SQLException sqle) {
+ try {
+ if (batchUpdate!=null) closeStatement(batchUpdate);
+ }
+ catch (SQLException e) {
+ //noncritical, swallow and let the other propagate!
+ JDBCExceptionReporter.logExceptions(e);
+ }
+ finally {
+ batchUpdate=null;
+ batchUpdateSQL=null;
+ }
+ }
+
+ public ResultSet getResultSet(PreparedStatement ps) throws SQLException {
+ ResultSet rs = ps.executeQuery();
+ resultSetsToClose.add(rs);
+ logOpenResults();
+ return rs;
+ }
+
+ public ResultSet getResultSet(CallableStatement ps, Dialect dialect) throws SQLException {
+ ResultSet rs = dialect.getResultSet(ps);
+ resultSetsToClose.add(rs);
+ logOpenResults();
+ return rs;
+
+ }
+
+ public void closeQueryStatement(PreparedStatement ps, ResultSet rs) throws SQLException {
+ boolean psStillThere = statementsToClose.remove( ps );
+ try {
+ if ( rs != null ) {
+ if ( resultSetsToClose.remove( rs ) ) {
+ logCloseResults();
+ rs.close();
+ }
+ }
+ }
+ finally {
+ if ( psStillThere ) {
+ closeQueryStatement( ps );
+ }
+ }
+ }
+
+ public PreparedStatement prepareBatchStatement(String sql)
+ throws SQLException, HibernateException {
+ sql = getSQL( sql );
+
+ if ( !sql.equals(batchUpdateSQL) ) {
+ batchUpdate=prepareStatement(sql); // calls executeBatch()
+ batchUpdateSQL=sql;
+ }
+ else {
+ log.debug("reusing prepared statement");
+ log(sql);
+ }
+ return batchUpdate;
+ }
+
+ public CallableStatement prepareBatchCallableStatement(String sql)
+ throws SQLException, HibernateException {
+ if ( !sql.equals(batchUpdateSQL) ) { // TODO: what if batchUpdate is a callablestatement ?
+ batchUpdate=prepareCallableStatement(sql); // calls executeBatch()
+ batchUpdateSQL=sql;
+ }
+ return (CallableStatement)batchUpdate;
+ }
+
+
+ public void executeBatch() throws HibernateException {
+ if (batchUpdate!=null) {
+ try {
+ try {
+ doExecuteBatch(batchUpdate);
+ }
+ finally {
+ closeStatement(batchUpdate);
+ }
+ }
+ catch (SQLException sqle) {
+ throw JDBCExceptionHelper.convert(
+ factory.getSQLExceptionConverter(),
+ sqle,
+ "Could not execute JDBC batch update",
+ batchUpdateSQL
+ );
+ }
+ finally {
+ batchUpdate=null;
+ batchUpdateSQL=null;
+ }
+ }
+ }
+
+ public void closeStatement(PreparedStatement ps) throws SQLException {
+ logClosePreparedStatement();
+ closePreparedStatement(ps);
+ }
+
+ private void closeQueryStatement(PreparedStatement ps) throws SQLException {
+
+ try {
+ //work around a bug in all known connection pools....
+ if ( ps.getMaxRows()!=0 ) ps.setMaxRows(0);
+ if ( ps.getQueryTimeout()!=0 ) ps.setQueryTimeout(0);
+ }
+ catch (Exception e) {
+ log.warn("exception clearing maxRows/queryTimeout", e);
+// ps.close(); //just close it; do NOT try to return it to the pool!
+ return; //NOTE: early exit!
+ }
+ finally {
+ closeStatement(ps);
+ }
+
+ if ( lastQuery==ps ) lastQuery = null;
+
+ }
+
+ /**
+ * Actually releases the batcher, allowing it to cleanup internally held
+ * resources.
+ */
+ public void closeStatements() {
+ try {
+ releasing = true;
+
+ try {
+ if (batchUpdate!=null) batchUpdate.close();
+ }
+ catch (SQLException sqle) {
+ //no big deal
+ log.warn("Could not close a JDBC prepared statement", sqle);
+ }
+ batchUpdate=null;
+ batchUpdateSQL=null;
+
+ Iterator iter = resultSetsToClose.iterator();
+ while ( iter.hasNext() ) {
+ try {
+ logCloseResults();
+ ( (ResultSet) iter.next() ).close();
+ }
+ catch (SQLException e) {
+ // no big deal
+ log.warn("Could not close a JDBC result set", e);
+ }
+ catch (Throwable e) {
+ // sybase driver (jConnect) throwing NPE here in certain cases
+ log.warn("Could not close a JDBC result set", e);
+ }
+ }
+ resultSetsToClose.clear();
+
+ iter = statementsToClose.iterator();
+ while ( iter.hasNext() ) {
+ try {
+ closeQueryStatement( (PreparedStatement) iter.next() );
+ }
+ catch (SQLException e) {
+ // no big deal
+ log.warn("Could not close a JDBC statement", e);
+ }
+ }
+ statementsToClose.clear();
+ }
+ finally {
+ releasing = false;
+ }
+ }
+
+ protected abstract void doExecuteBatch(PreparedStatement ps) throws SQLException, HibernateException;
+
+ private String preparedStatementCountsToString() {
+ return
+ " (open PreparedStatements: " +
+ openPreparedStatementCount +
+ ", globally: " +
+ globalOpenPreparedStatementCount +
+ ")";
+ }
+
+ private String resultSetCountsToString() {
+ return
+ " (open ResultSets: " +
+ openResultSetCount +
+ ", globally: " +
+ globalOpenResultSetCount +
+ ")";
+ }
+
+ private void logOpenPreparedStatement() {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "about to open PreparedStatement" + preparedStatementCountsToString() );
+ openPreparedStatementCount++;
+ globalOpenPreparedStatementCount++;
+ }
+ }
+
+ private void logClosePreparedStatement() {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "about to close PreparedStatement" + preparedStatementCountsToString() );
+ openPreparedStatementCount--;
+ globalOpenPreparedStatementCount--;
+ }
+ }
+
+ private void logOpenResults() {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "about to open ResultSet" + resultSetCountsToString() );
+ openResultSetCount++;
+ globalOpenResultSetCount++;
+ }
+ }
+ private void logCloseResults() {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "about to close ResultSet" + resultSetCountsToString() );
+ openResultSetCount--;
+ globalOpenResultSetCount--;
+ }
+ }
+
+ protected SessionFactoryImplementor getFactory() {
+ return factory;
+ }
+
+ private void log(String sql) {
+ if ( SQL_LOG.isDebugEnabled() ) {
+ SQL_LOG.debug( format(sql) );
+ }
+ if ( factory.getSettings().isShowSqlEnabled() ) {
+ System.out.println( "Hibernate: " + format(sql) );
+ }
+ }
+
+ private String format(String sql) {
+ if ( factory.getSettings().isFormatSqlEnabled() ) {
+ return new Formatter(sql).format();
+ }
+ else {
+ return sql;
+ }
+ }
+
+ private PreparedStatement getPreparedStatement(
+ final Connection conn,
+ final String sql,
+ final boolean scrollable,
+ final ScrollMode scrollMode)
+ throws SQLException {
+ return getPreparedStatement(
+ conn,
+ sql,
+ scrollable,
+ false,
+ null,
+ scrollMode,
+ false
+ );
+ }
+
+ private CallableStatement getCallableStatement(
+ final Connection conn,
+ String sql,
+ boolean scrollable) throws SQLException {
+ if ( scrollable && !factory.getSettings().isScrollableResultSetsEnabled() ) {
+ throw new AssertionFailure("scrollable result sets are not enabled");
+ }
+
+ sql = getSQL( sql );
+ log( sql );
+
+ log.trace("preparing callable statement");
+ if ( scrollable ) {
+ return conn.prepareCall(
+ sql,
+ ResultSet.TYPE_SCROLL_INSENSITIVE,
+ ResultSet.CONCUR_READ_ONLY
+ );
+ }
+ else {
+ return conn.prepareCall( sql );
+ }
+ }
+
+ private String getSQL(String sql) {
+ sql = interceptor.onPrepareStatement( sql );
+ if ( sql==null || sql.length() == 0 ) {
+ throw new AssertionFailure( "Interceptor.onPrepareStatement() returned null or empty string." );
+ }
+ return sql;
+ }
+
+ private PreparedStatement getPreparedStatement(
+ final Connection conn,
+ String sql,
+ boolean scrollable,
+ final boolean useGetGeneratedKeys,
+ final String[] namedGeneratedKeys,
+ final ScrollMode scrollMode,
+ final boolean callable) throws SQLException {
+ if ( scrollable && !factory.getSettings().isScrollableResultSetsEnabled() ) {
+ throw new AssertionFailure("scrollable result sets are not enabled");
+ }
+ if ( useGetGeneratedKeys && !factory.getSettings().isGetGeneratedKeysEnabled() ) {
+ throw new AssertionFailure("getGeneratedKeys() support is not enabled");
+ }
+
+ sql = getSQL( sql );
+ log( sql );
+
+ log.trace( "preparing statement" );
+ PreparedStatement result;
+ if ( scrollable ) {
+ if ( callable ) {
+ result = conn.prepareCall( sql, scrollMode.toResultSetType(), ResultSet.CONCUR_READ_ONLY );
+ }
+ else {
+ result = conn.prepareStatement( sql, scrollMode.toResultSetType(), ResultSet.CONCUR_READ_ONLY );
+ }
+ }
+ else if ( useGetGeneratedKeys ) {
+ result = GetGeneratedKeysHelper.prepareStatement( conn, sql );
+ }
+ else if ( namedGeneratedKeys != null ) {
+ result = NamedGeneratedKeysHelper.prepareStatement( conn, sql, namedGeneratedKeys );
+ }
+ else {
+ if ( callable ) {
+ result = conn.prepareCall( sql );
+ }
+ else {
+ result = conn.prepareStatement( sql );
+ }
+ }
+
+ setTimeout( result );
+
+ if ( factory.getStatistics().isStatisticsEnabled() ) {
+ factory.getStatisticsImplementor().prepareStatement();
+ }
+
+ return result;
+
+ }
+
+ private void setTimeout(PreparedStatement result) throws SQLException {
+ if ( isTransactionTimeoutSet ) {
+ int timeout = (int) ( transactionTimeout - ( System.currentTimeMillis() / 1000 ) );
+ if (timeout<=0) {
+ throw new TransactionException("transaction timeout expired");
+ }
+ else {
+ result.setQueryTimeout(timeout);
+ }
+ }
+ }
+
+ private void closePreparedStatement(PreparedStatement ps) throws SQLException {
+ try {
+ log.trace("closing statement");
+ ps.close();
+ if ( factory.getStatistics().isStatisticsEnabled() ) {
+ factory.getStatisticsImplementor().closeStatement();
+ }
+ }
+ finally {
+ if ( !releasing ) {
+ // If we are in the process of releasing, no sense
+ // checking for aggressive-release possibility.
+ connectionManager.afterStatement();
+ }
+ }
+ }
+
+ private void setStatementFetchSize(PreparedStatement statement) throws SQLException {
+ Integer statementFetchSize = factory.getSettings().getJdbcFetchSize();
+ if ( statementFetchSize!=null ) {
+ statement.setFetchSize( statementFetchSize.intValue() );
+ }
+ }
+
+ public Connection openConnection() throws HibernateException {
+ log.debug("opening JDBC connection");
+ try {
+ return factory.getConnectionProvider().getConnection();
+ }
+ catch (SQLException sqle) {
+ throw JDBCExceptionHelper.convert(
+ factory.getSQLExceptionConverter(),
+ sqle,
+ "Cannot open connection"
+ );
+ }
+ }
+
+ public void closeConnection(Connection conn) throws HibernateException {
+ if ( log.isDebugEnabled() ) {
+ log.debug(
+ "closing JDBC connection" +
+ preparedStatementCountsToString() +
+ resultSetCountsToString()
+ );
+ }
+
+ try {
+ if ( !conn.isClosed() ) {
+ JDBCExceptionReporter.logAndClearWarnings(conn);
+ }
+ factory.getConnectionProvider().closeConnection(conn);
+ }
+ catch (SQLException sqle) {
+ throw JDBCExceptionHelper.convert(
+ factory.getSQLExceptionConverter(),
+ sqle,
+ "Cannot close connection"
+ );
+ }
+ }
+
+ public void cancelLastQuery() throws HibernateException {
+ try {
+ if (lastQuery!=null) lastQuery.cancel();
+ }
+ catch (SQLException sqle) {
+ throw JDBCExceptionHelper.convert(
+ factory.getSQLExceptionConverter(),
+ sqle,
+ "Cannot cancel query"
+ );
+ }
+ }
+
+ public boolean hasOpenResources() {
+ return resultSetsToClose.size() > 0 || statementsToClose.size() > 0;
+ }
+
+ public String openResourceStatsAsString() {
+ return preparedStatementCountsToString() + resultSetCountsToString();
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/BatchFailedException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/BatchFailedException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/BatchFailedException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,18 @@
+package org.hibernate.jdbc;
+
+import org.hibernate.HibernateException;
+
+/**
+ * Indicates a failed batch entry (-3 return).
+ *
+ * @author Steve Ebersole
+ */
+public class BatchFailedException extends HibernateException {
+ public BatchFailedException(String s) {
+ super( s );
+ }
+
+ public BatchFailedException(String string, Throwable root) {
+ super( string, root );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/BatchedTooManyRowsAffectedException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/BatchedTooManyRowsAffectedException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/BatchedTooManyRowsAffectedException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,21 @@
+package org.hibernate.jdbc;
+
+/**
+ * Much like {@link TooManyRowsAffectedException}, indicates that more
+ * rows than what we were expcecting were affected. Additionally, this form
+ * occurs from a batch and carries along the batch positon that failed.
+ *
+ * @author Steve Ebersole
+ */
+public class BatchedTooManyRowsAffectedException extends TooManyRowsAffectedException {
+ private final int batchPosition;
+
+ public BatchedTooManyRowsAffectedException(String message, int expectedRowCount, int actualRowCount, int batchPosition) {
+ super( message, expectedRowCount, actualRowCount );
+ this.batchPosition = batchPosition;
+ }
+
+ public int getBatchPosition() {
+ return batchPosition;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/Batcher.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/Batcher.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/Batcher.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,163 @@
+//$Id: Batcher.java 10040 2006-06-22 19:51:43Z steve.ebersole at jboss.com $
+package org.hibernate.jdbc;
+
+import java.sql.CallableStatement;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.hibernate.HibernateException;
+import org.hibernate.ScrollMode;
+import org.hibernate.dialect.Dialect;
+
+/**
+ * Manages <tt>PreparedStatement</tt>s for a session. Abstracts JDBC
+ * batching to maintain the illusion that a single logical batch
+ * exists for the whole session, even when batching is disabled.
+ * Provides transparent <tt>PreparedStatement</tt> caching.
+ *
+ * @see java.sql.PreparedStatement
+ * @see org.hibernate.impl.SessionImpl
+ * @author Gavin King
+ */
+public interface Batcher {
+ /**
+ * Get a prepared statement for use in loading / querying. If not explicitly
+ * released by <tt>closeQueryStatement()</tt>, it will be released when the
+ * session is closed or disconnected.
+ */
+ public PreparedStatement prepareQueryStatement(String sql, boolean scrollable, ScrollMode scrollMode) throws SQLException, HibernateException;
+ /**
+ * Close a prepared statement opened with <tt>prepareQueryStatement()</tt>
+ */
+ public void closeQueryStatement(PreparedStatement ps, ResultSet rs) throws SQLException;
+ /**
+ * Get a prepared statement for use in loading / querying. If not explicitly
+ * released by <tt>closeQueryStatement()</tt>, it will be released when the
+ * session is closed or disconnected.
+ */
+ public CallableStatement prepareCallableQueryStatement(String sql, boolean scrollable, ScrollMode scrollMode) throws SQLException, HibernateException;
+
+
+ /**
+ * Get a non-batchable prepared statement to use for selecting. Does not
+ * result in execution of the current batch.
+ */
+ public PreparedStatement prepareSelectStatement(String sql) throws SQLException, HibernateException;
+
+ /**
+ * Get a non-batchable prepared statement to use for inserting / deleting / updating,
+ * using JDBC3 getGeneratedKeys ({@link Connection#prepareStatement(String, int)}).
+ * <p/>
+ * Must be explicitly released by {@link #closeStatement} after use.
+ */
+ public PreparedStatement prepareStatement(String sql, boolean useGetGeneratedKeys) throws SQLException, HibernateException;
+
+ /**
+ * Get a non-batchable prepared statement to use for inserting / deleting / updating.
+ * using JDBC3 getGeneratedKeys ({@link Connection#prepareStatement(String, String[])}).
+ * <p/>
+ * Must be explicitly released by {@link #closeStatement} after use.
+ */
+ public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException, HibernateException;
+
+ /**
+ * Get a non-batchable prepared statement to use for inserting / deleting / updating.
+ * <p/>
+ * Must be explicitly released by {@link #closeStatement} after use.
+ */
+ public PreparedStatement prepareStatement(String sql) throws SQLException, HibernateException;
+
+ /**
+ * Get a non-batchable callable statement to use for inserting / deleting / updating.
+ * <p/>
+ * Must be explicitly released by {@link #closeStatement} after use.
+ */
+ public CallableStatement prepareCallableStatement(String sql) throws SQLException, HibernateException;
+
+ /**
+ * Close a prepared or callable statement opened using <tt>prepareStatement()</tt> or <tt>prepareCallableStatement()</tt>
+ */
+ public void closeStatement(PreparedStatement ps) throws SQLException;
+
+ /**
+ * Get a batchable prepared statement to use for inserting / deleting / updating
+ * (might be called many times before a single call to <tt>executeBatch()</tt>).
+ * After setting parameters, call <tt>addToBatch</tt> - do not execute the
+ * statement explicitly.
+ * @see Batcher#addToBatch
+ */
+ public PreparedStatement prepareBatchStatement(String sql) throws SQLException, HibernateException;
+
+ /**
+ * Get a batchable callable statement to use for inserting / deleting / updating
+ * (might be called many times before a single call to <tt>executeBatch()</tt>).
+ * After setting parameters, call <tt>addToBatch</tt> - do not execute the
+ * statement explicitly.
+ * @see Batcher#addToBatch
+ */
+ public CallableStatement prepareBatchCallableStatement(String sql) throws SQLException, HibernateException;
+
+ /**
+ * Add an insert / delete / update to the current batch (might be called multiple times
+ * for single <tt>prepareBatchStatement()</tt>)
+ */
+ public void addToBatch(Expectation expectation) throws SQLException, HibernateException;
+
+ /**
+ * Execute the batch
+ */
+ public void executeBatch() throws HibernateException;
+
+ /**
+ * Close any query statements that were left lying around
+ */
+ public void closeStatements();
+ /**
+ * Execute the statement and return the result set
+ */
+ public ResultSet getResultSet(PreparedStatement ps) throws SQLException;
+ /**
+ * Execute the statement and return the result set from a callable statement
+ */
+ public ResultSet getResultSet(CallableStatement ps, Dialect dialect) throws SQLException;
+
+ /**
+ * Must be called when an exception occurs
+ * @param sqle the (not null) exception that is the reason for aborting
+ */
+ public void abortBatch(SQLException sqle);
+
+ /**
+ * Cancel the current query statement
+ */
+ public void cancelLastQuery() throws HibernateException;
+
+ public boolean hasOpenResources();
+
+ public String openResourceStatsAsString();
+
+ // TODO : remove these last two as batcher is no longer managing connections
+
+ /**
+ * Obtain a JDBC connection
+ */
+ public Connection openConnection() throws HibernateException;
+ /**
+ * Dispose of the JDBC connection
+ */
+ public void closeConnection(Connection conn) throws HibernateException;
+
+ /**
+ * Set the transaction timeout to <tt>seconds</tt> later
+ * than the current system time.
+ */
+ public void setTransactionTimeout(int seconds);
+ /**
+ * Unset the transaction timeout, called after the end of a
+ * transaction.
+ */
+ public void unsetTransactionTimeout();
+}
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/BatcherFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/BatcherFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/BatcherFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,13 @@
+//$Id: BatcherFactory.java 7683 2005-07-29 19:10:20Z maxcsaucdk $
+package org.hibernate.jdbc;
+
+import org.hibernate.Interceptor;
+
+
+/**
+ * Factory for <tt>Batcher</tt> instances.
+ * @author Gavin King
+ */
+public interface BatcherFactory {
+ public Batcher createBatcher(ConnectionManager connectionManager, Interceptor interceptor);
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/BatcherFactory.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/BatchingBatcher.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/BatchingBatcher.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/BatchingBatcher.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,78 @@
+//$Id: BatchingBatcher.java 10040 2006-06-22 19:51:43Z steve.ebersole at jboss.com $
+package org.hibernate.jdbc;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+import org.hibernate.HibernateException;
+import org.hibernate.Interceptor;
+import org.hibernate.StaleStateException;
+
+/**
+ * An implementation of the <tt>Batcher</tt> interface that
+ * actually uses batching
+ * @author Gavin King
+ */
+public class BatchingBatcher extends AbstractBatcher {
+
+ private int batchSize;
+ private Expectation[] expectations;
+
+ public BatchingBatcher(ConnectionManager connectionManager, Interceptor interceptor) {
+ super( connectionManager, interceptor );
+ expectations = new Expectation[ getFactory().getSettings().getJdbcBatchSize() ];
+ }
+
+ public void addToBatch(Expectation expectation) throws SQLException, HibernateException {
+ if ( !expectation.canBeBatched() ) {
+ throw new HibernateException( "attempting to batch an operation which cannot be batched" );
+ }
+ PreparedStatement batchUpdate = getStatement();
+ batchUpdate.addBatch();
+ expectations[ batchSize++ ] = expectation;
+ if ( batchSize == getFactory().getSettings().getJdbcBatchSize() ) {
+ doExecuteBatch( batchUpdate );
+ }
+ }
+
+ protected void doExecuteBatch(PreparedStatement ps) throws SQLException, HibernateException {
+ if ( batchSize == 0 ) {
+ log.debug( "no batched statements to execute" );
+ }
+ else {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "Executing batch size: " + batchSize );
+ }
+
+ try {
+ checkRowCounts( ps.executeBatch(), ps );
+ }
+ catch (RuntimeException re) {
+ log.error( "Exception executing batch: ", re );
+ throw re;
+ }
+ finally {
+ batchSize = 0;
+ }
+
+ }
+
+ }
+
+ private void checkRowCounts(int[] rowCounts, PreparedStatement ps) throws SQLException, HibernateException {
+ int numberOfRowCounts = rowCounts.length;
+ if ( numberOfRowCounts != batchSize ) {
+ log.warn( "JDBC driver did not return the expected number of row counts" );
+ }
+ for ( int i = 0; i < numberOfRowCounts; i++ ) {
+ expectations[i].verifyOutcome( rowCounts[i], ps, i );
+ }
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/BatchingBatcherFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/BatchingBatcherFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/BatchingBatcherFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,19 @@
+//$Id: BatchingBatcherFactory.java 7683 2005-07-29 19:10:20Z maxcsaucdk $
+package org.hibernate.jdbc;
+
+import org.hibernate.Interceptor;
+
+
+/**
+ * A BatcherFactory implementation which constructs Batcher instances
+ * capable of actually performing batch operations.
+ *
+ * @author Gavin King
+ */
+public class BatchingBatcherFactory implements BatcherFactory {
+
+ public Batcher createBatcher(ConnectionManager connectionManager, Interceptor interceptor) {
+ return new BatchingBatcher( connectionManager, interceptor );
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/BatchingBatcherFactory.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/BorrowedConnectionProxy.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/BorrowedConnectionProxy.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/BorrowedConnectionProxy.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,117 @@
+package org.hibernate.jdbc;
+
+import org.hibernate.HibernateException;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.lang.reflect.InvocationTargetException;
+import java.sql.Connection;
+
+/**
+ * A proxy for <i>borrowed</i> connections which funnels all requests back
+ * into the ConnectionManager from which it was borrowed to be properly
+ * handled (in terms of connection release modes).
+ * <p/>
+ * Note: the term borrowed here refers to connection references obtained
+ * via {@link org.hibernate.Session#connection()} for application usage.
+ *
+ * @author Steve Ebersole
+ */
+public class BorrowedConnectionProxy implements InvocationHandler {
+
+ private static final Class[] PROXY_INTERFACES = new Class[] { Connection.class, ConnectionWrapper.class };
+
+ private final ConnectionManager connectionManager;
+ private boolean useable = true;
+
+ public BorrowedConnectionProxy(ConnectionManager connectionManager) {
+ this.connectionManager = connectionManager;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ if ( "close".equals( method.getName() ) ) {
+ connectionManager.releaseBorrowedConnection();
+ return null;
+ }
+ // should probably no-op commit/rollback here, at least in JTA scenarios
+ if ( !useable ) {
+ throw new HibernateException( "connnection proxy not usable after transaction completion" );
+ }
+
+ if ( "getWrappedConnection".equals( method.getName() ) ) {
+ return connectionManager.getConnection();
+ }
+
+ try {
+ return method.invoke( connectionManager.getConnection(), args );
+ }
+ catch( InvocationTargetException e ) {
+ throw e.getTargetException();
+ }
+ }
+
+ /**
+ * Generates a Connection proxy wrapping the connection managed by the passed
+ * connection manager.
+ *
+ * @param connectionManager The connection manager to wrap with the
+ * connection proxy.
+ * @return The generated proxy.
+ */
+ public static Connection generateProxy(ConnectionManager connectionManager) {
+ BorrowedConnectionProxy handler = new BorrowedConnectionProxy( connectionManager );
+ return ( Connection ) Proxy.newProxyInstance(
+ getProxyClassLoader(),
+ PROXY_INTERFACES,
+ handler
+ );
+ }
+
+ /**
+ * Marks a borrowed connection as no longer usable.
+ *
+ * @param connection The connection (proxy) to be marked.
+ */
+ public static void renderUnuseable(Connection connection) {
+ if ( connection != null && Proxy.isProxyClass( connection.getClass() ) ) {
+ InvocationHandler handler = Proxy.getInvocationHandler( connection );
+ if ( BorrowedConnectionProxy.class.isAssignableFrom( handler.getClass() ) ) {
+ ( ( BorrowedConnectionProxy ) handler ).useable = false;
+ }
+ }
+ }
+
+ /**
+ * Convience method for unwrapping a connection proxy and getting a
+ * handle to an underlying connection.
+ *
+ * @param connection The connection (proxy) to be unwrapped.
+ * @return The unwrapped connection.
+ */
+ public static Connection getWrappedConnection(Connection connection) {
+ if ( connection != null && connection instanceof ConnectionWrapper ) {
+ return ( ( ConnectionWrapper ) connection ).getWrappedConnection();
+ }
+ else {
+ return connection;
+ }
+ }
+
+ /**
+ * Determines the appropriate class loader to which the generated proxy
+ * should be scoped.
+ *
+ * @return The class loader appropriate for proxy construction.
+ */
+ public static ClassLoader getProxyClassLoader() {
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ if ( cl == null ) {
+ cl = BorrowedConnectionProxy.class.getClassLoader();
+ }
+ return cl;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/ColumnNameCache.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/ColumnNameCache.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/ColumnNameCache.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,33 @@
+// $Id: ColumnNameCache.java 5811 2005-02-20 23:02:37Z oneovthafew $
+package org.hibernate.jdbc;
+
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Implementation of ColumnNameCache.
+ *
+ * @author Steve Ebersole
+ */
+public class ColumnNameCache {
+
+ private final Map columnNameToIndexCache;
+
+ public ColumnNameCache(int columnCount) {
+ // should *not* need to grow beyond the size of the total number of columns in the rs
+ this.columnNameToIndexCache = new HashMap( columnCount );
+ }
+
+ public int getIndexForColumnName(String columnName, ResultSetWrapper rs)throws SQLException {
+ Integer cached = ( Integer ) columnNameToIndexCache.get( columnName );
+ if ( cached != null ) {
+ return cached.intValue();
+ }
+ else {
+ int index = rs.getTarget().findColumn( columnName );
+ columnNameToIndexCache.put( columnName, new Integer(index) );
+ return index;
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/ConnectionManager.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/ConnectionManager.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/ConnectionManager.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,539 @@
+// $Id: ConnectionManager.java 11303 2007-03-19 22:06:14Z steve.ebersole at jboss.com $
+package org.hibernate.jdbc;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.ConnectionReleaseMode;
+import org.hibernate.HibernateException;
+import org.hibernate.Interceptor;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.exception.JDBCExceptionHelper;
+import org.hibernate.util.JDBCExceptionReporter;
+
+/**
+ * Encapsulates JDBC Connection management logic needed by Hibernate.
+ * <p/>
+ * The lifecycle is intended to span a logical series of interactions with the
+ * database. Internally, this means the the lifecycle of the Session.
+ *
+ * @author Steve Ebersole
+ */
+public class ConnectionManager implements Serializable {
+
+ private static final Log log = LogFactory.getLog( ConnectionManager.class );
+
+ public static interface Callback {
+ public void connectionOpened();
+ public void connectionCleanedUp();
+ public boolean isTransactionInProgress();
+ }
+
+ private transient SessionFactoryImplementor factory;
+ private final Callback callback;
+
+ private final ConnectionReleaseMode releaseMode;
+ private transient Connection connection;
+ private transient Connection borrowedConnection;
+
+ private final boolean wasConnectionSupplied;
+ private transient Batcher batcher;
+ private transient Interceptor interceptor;
+ private boolean isClosed;
+ private transient boolean isFlushing;
+
+ /**
+ * Constructs a ConnectionManager.
+ * <p/>
+ * This is the form used internally.
+ *
+ * @param factory The SessionFactory.
+ * @param callback An observer for internal state change.
+ * @param releaseMode The mode by which to release JDBC connections.
+ * @param connection An externally supplied connection.
+ */
+ public ConnectionManager(
+ SessionFactoryImplementor factory,
+ Callback callback,
+ ConnectionReleaseMode releaseMode,
+ Connection connection,
+ Interceptor interceptor) {
+ this.factory = factory;
+ this.callback = callback;
+
+ this.interceptor = interceptor;
+ this.batcher = factory.getSettings().getBatcherFactory().createBatcher( this, interceptor );
+
+ this.connection = connection;
+ wasConnectionSupplied = ( connection != null );
+
+ this.releaseMode = wasConnectionSupplied ? ConnectionReleaseMode.ON_CLOSE : releaseMode;
+ }
+
+ /**
+ * Private constructor used exclusively from custom serialization
+ */
+ private ConnectionManager(
+ SessionFactoryImplementor factory,
+ Callback callback,
+ ConnectionReleaseMode releaseMode,
+ Interceptor interceptor,
+ boolean wasConnectionSupplied,
+ boolean isClosed) {
+ this.factory = factory;
+ this.callback = callback;
+
+ this.interceptor = interceptor;
+ this.batcher = factory.getSettings().getBatcherFactory().createBatcher( this, interceptor );
+
+ this.wasConnectionSupplied = wasConnectionSupplied;
+ this.isClosed = isClosed;
+ this.releaseMode = wasConnectionSupplied ? ConnectionReleaseMode.ON_CLOSE : releaseMode;
+ }
+
+ /**
+ * The session factory.
+ *
+ * @return the session factory.
+ */
+ public SessionFactoryImplementor getFactory() {
+ return factory;
+ }
+
+ /**
+ * The batcher managed by this ConnectionManager.
+ *
+ * @return The batcher.
+ */
+ public Batcher getBatcher() {
+ return batcher;
+ }
+
+ /**
+ * Was the connection being used here supplied by the user?
+ *
+ * @return True if the user supplied the JDBC connection; false otherwise
+ */
+ public boolean isSuppliedConnection() {
+ return wasConnectionSupplied;
+ }
+
+ /**
+ * Retrieves the connection currently managed by this ConnectionManager.
+ * <p/>
+ * Note, that we may need to obtain a connection to return here if a
+ * connection has either not yet been obtained (non-UserSuppliedConnectionProvider)
+ * or has previously been aggressively released (if supported in this environment).
+ *
+ * @return The current Connection.
+ *
+ * @throws HibernateException Indicates a connection is currently not
+ * available (we are currently manually disconnected).
+ */
+ public Connection getConnection() throws HibernateException {
+ if ( isClosed ) {
+ throw new HibernateException( "connection manager has been closed" );
+ }
+ if ( connection == null ) {
+ openConnection();
+ }
+ return connection;
+ }
+
+ public boolean hasBorrowedConnection() {
+ // used from testsuite
+ return borrowedConnection != null;
+ }
+
+ public Connection borrowConnection() {
+ if ( isClosed ) {
+ throw new HibernateException( "connection manager has been closed" );
+ }
+ if ( isSuppliedConnection() ) {
+ return connection;
+ }
+ else {
+ if ( borrowedConnection == null ) {
+ borrowedConnection = BorrowedConnectionProxy.generateProxy( this );
+ }
+ return borrowedConnection;
+ }
+ }
+
+ public void releaseBorrowedConnection() {
+ if ( borrowedConnection != null ) {
+ try {
+ BorrowedConnectionProxy.renderUnuseable( borrowedConnection );
+ }
+ finally {
+ borrowedConnection = null;
+ }
+ }
+ }
+
+ /**
+ * Is the connection considered "auto-commit"?
+ *
+ * @return True if we either do not have a connection, or the connection
+ * really is in auto-commit mode.
+ *
+ * @throws SQLException Can be thrown by the Connection.isAutoCommit() check.
+ */
+ public boolean isAutoCommit() throws SQLException {
+ return connection == null
+ || connection.isClosed()
+ || connection.getAutoCommit();
+ }
+
+ /**
+ * Will connections be released after each statement execution?
+ * <p/>
+ * Connections will be released after each statement if either:<ul>
+ * <li>the defined release-mode is {@link ConnectionReleaseMode#AFTER_STATEMENT}; or
+ * <li>the defined release-mode is {@link ConnectionReleaseMode#AFTER_TRANSACTION} but
+ * we are in auto-commit mode.
+ * <p/>
+ * release-mode = {@link ConnectionReleaseMode#ON_CLOSE} should [b]never[/b] release
+ * a connection.
+ *
+ * @return True if the connections will be released after each statement; false otherwise.
+ */
+ public boolean isAggressiveRelease() {
+ if ( releaseMode == ConnectionReleaseMode.AFTER_STATEMENT ) {
+ return true;
+ }
+ else if ( releaseMode == ConnectionReleaseMode.AFTER_TRANSACTION ) {
+ boolean inAutoCommitState;
+ try {
+ inAutoCommitState = isAutoCommit()&& !callback.isTransactionInProgress();
+ }
+ catch( SQLException e ) {
+ // assume we are in an auto-commit state
+ inAutoCommitState = true;
+ }
+ return inAutoCommitState;
+ }
+ return false;
+ }
+
+ /**
+ * Modified version of {@link #isAggressiveRelease} which does not force a
+ * transaction check. This is solely used from our {@link #afterTransaction}
+ * callback, so no need to do the check; plus it seems to cause problems on
+ * websphere (god i love websphere ;)
+ * </p>
+ * It uses this information to decide if an aggressive release was skipped
+ * do to open resources, and if so forces a release.
+ *
+ * @return True if the connections will be released after each statement; false otherwise.
+ */
+ private boolean isAggressiveReleaseNoTransactionCheck() {
+ if ( releaseMode == ConnectionReleaseMode.AFTER_STATEMENT ) {
+ return true;
+ }
+ else {
+ boolean inAutoCommitState;
+ try {
+ inAutoCommitState = isAutoCommit();
+ }
+ catch( SQLException e ) {
+ // assume we are in an auto-commit state
+ inAutoCommitState = true;
+ }
+ return releaseMode == ConnectionReleaseMode.AFTER_TRANSACTION && inAutoCommitState;
+ }
+ }
+
+ /**
+ * Is this ConnectionManager instance "logically" connected. Meaning
+ * do we either have a cached connection available or do we have the
+ * ability to obtain a connection on demand.
+ *
+ * @return True if logically connected; false otherwise.
+ */
+ public boolean isCurrentlyConnected() {
+ return wasConnectionSupplied ? connection != null : !isClosed;
+ }
+
+ /**
+ * To be called after execution of each JDBC statement. Used to
+ * conditionally release the JDBC connection aggressively if
+ * the configured release mode indicates.
+ */
+ public void afterStatement() {
+ if ( isAggressiveRelease() ) {
+ if ( isFlushing ) {
+ log.debug( "skipping aggressive-release due to flush cycle" );
+ }
+ else if ( batcher.hasOpenResources() ) {
+ log.debug( "skipping aggresive-release due to open resources on batcher" );
+ }
+ else if ( borrowedConnection != null ) {
+ log.debug( "skipping aggresive-release due to borrowed connection" );
+ }
+ else {
+ aggressiveRelease();
+ }
+ }
+ }
+
+ /**
+ * To be called after local transaction completion. Used to conditionally
+ * release the JDBC connection aggressively if the configured release mode
+ * indicates.
+ */
+ public void afterTransaction() {
+ if ( isAfterTransactionRelease() ) {
+ aggressiveRelease();
+ }
+ else if ( isAggressiveReleaseNoTransactionCheck() && batcher.hasOpenResources() ) {
+ log.info( "forcing batcher resource cleanup on transaction completion; forgot to close ScrollableResults/Iterator?" );
+ batcher.closeStatements();
+ aggressiveRelease();
+ }
+ else if ( isOnCloseRelease() ) {
+ // log a message about potential connection leaks
+ log.debug( "transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources!" );
+ }
+ batcher.unsetTransactionTimeout();
+ }
+
+ private boolean isAfterTransactionRelease() {
+ return releaseMode == ConnectionReleaseMode.AFTER_TRANSACTION;
+ }
+
+ private boolean isOnCloseRelease() {
+ return releaseMode == ConnectionReleaseMode.ON_CLOSE;
+ }
+
+ /**
+ * To be called after Session completion. Used to release the JDBC
+ * connection.
+ *
+ * @return The connection mantained here at time of close. Null if
+ * there was no connection cached internally.
+ */
+ public Connection close() {
+ try {
+ return cleanup();
+ }
+ finally {
+ isClosed = true;
+ }
+ }
+
+ /**
+ * Manually disconnect the underlying JDBC Connection. The assumption here
+ * is that the manager will be reconnected at a later point in time.
+ *
+ * @return The connection mantained here at time of disconnect. Null if
+ * there was no connection cached internally.
+ */
+ public Connection manualDisconnect() {
+ return cleanup();
+ }
+
+ /**
+ * Manually reconnect the underlying JDBC Connection. Should be called at
+ * some point after manualDisconnect().
+ * <p/>
+ * This form is used for ConnectionProvider-supplied connections.
+ */
+ public void manualReconnect() {
+ }
+
+ /**
+ * Manually reconnect the underlying JDBC Connection. Should be called at
+ * some point after manualDisconnect().
+ * <p/>
+ * This form is used for user-supplied connections.
+ */
+ public void manualReconnect(Connection suppliedConnection) {
+ this.connection = suppliedConnection;
+ }
+
+ /**
+ * Releases the Connection and cleans up any resources associated with
+ * that Connection. This is intended for use:
+ * 1) at the end of the session
+ * 2) on a manual disconnect of the session
+ * 3) from afterTransaction(), in the case of skipped aggressive releasing
+ *
+ * @return The released connection.
+ * @throws HibernateException
+ */
+ private Connection cleanup() throws HibernateException {
+ releaseBorrowedConnection();
+
+ if ( connection == null ) {
+ log.trace( "connection already null in cleanup : no action");
+ return null;
+ }
+
+ try {
+ log.trace( "performing cleanup" );
+
+ batcher.closeStatements();
+ Connection c = null;
+ if ( !wasConnectionSupplied ) {
+ closeConnection();
+ }
+ else {
+ c = connection;
+ }
+ connection = null;
+ return c;
+ }
+ finally {
+ callback.connectionCleanedUp();
+ }
+ }
+
+ /**
+ * Performs actions required to perform an aggressive release of the
+ * JDBC Connection.
+ */
+ private void aggressiveRelease() {
+ if ( !wasConnectionSupplied ) {
+ log.debug( "aggressively releasing JDBC connection" );
+ if ( connection != null ) {
+ closeConnection();
+ }
+ }
+ }
+
+ /**
+ * Pysically opens a JDBC Connection.
+ *
+ * @throws HibernateException
+ */
+ private void openConnection() throws HibernateException {
+ if ( connection != null ) {
+ return;
+ }
+
+ log.debug("opening JDBC connection");
+ try {
+ connection = factory.getConnectionProvider().getConnection();
+ }
+ catch (SQLException sqle) {
+ throw JDBCExceptionHelper.convert(
+ factory.getSQLExceptionConverter(),
+ sqle,
+ "Cannot open connection"
+ );
+ }
+
+ callback.connectionOpened(); // register synch; stats.connect()
+ }
+
+ /**
+ * Physically closes the JDBC Connection.
+ */
+ private void closeConnection() {
+ if ( log.isDebugEnabled() ) {
+ log.debug(
+ "releasing JDBC connection [" +
+ batcher.openResourceStatsAsString() + "]"
+ );
+ }
+
+ try {
+ if ( !connection.isClosed() ) {
+ JDBCExceptionReporter.logAndClearWarnings( connection );
+ }
+ factory.getConnectionProvider().closeConnection( connection );
+ connection = null;
+ }
+ catch (SQLException sqle) {
+ throw JDBCExceptionHelper.convert(
+ factory.getSQLExceptionConverter(),
+ sqle,
+ "Cannot release connection"
+ );
+ }
+ }
+
+ /**
+ * Callback to let us know that a flush is beginning. We use this fact
+ * to temporarily circumvent aggressive connection releasing until after
+ * the flush cycle is complete {@link #flushEnding()}
+ */
+ public void flushBeginning() {
+ log.trace( "registering flush begin" );
+ isFlushing = true;
+ }
+
+ /**
+ * Callback to let us know that a flush is ending. We use this fact to
+ * stop circumventing aggressive releasing connections.
+ */
+ public void flushEnding() {
+ log.trace( "registering flush end" );
+ isFlushing = false;
+ afterStatement();
+ }
+
+ public boolean isReadyForSerialization() {
+ return wasConnectionSupplied ? connection == null : !batcher.hasOpenResources();
+ }
+
+ /**
+ * Used during serialization.
+ *
+ * @param oos The stream to which we are being written.
+ * @throws IOException Indicates an I/O error writing to the stream
+ */
+ private void writeObject(ObjectOutputStream oos) throws IOException {
+ if ( !isReadyForSerialization() ) {
+ throw new IllegalStateException( "Cannot serialize a ConnectionManager while connected" );
+ }
+
+ oos.writeObject( factory );
+ oos.writeObject( interceptor );
+ oos.defaultWriteObject();
+ }
+
+ /**
+ * Used during deserialization.
+ *
+ * @param ois The stream from which we are being read.
+ * @throws IOException Indicates an I/O error reading the stream
+ * @throws ClassNotFoundException Indicates resource class resolution.
+ */
+ private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
+ factory = (SessionFactoryImplementor) ois.readObject();
+ interceptor = (Interceptor) ois.readObject();
+ ois.defaultReadObject();
+
+ this.batcher = factory.getSettings().getBatcherFactory().createBatcher( this, interceptor );
+ }
+
+ public void serialize(ObjectOutputStream oos) throws IOException {
+ oos.writeBoolean( wasConnectionSupplied );
+ oos.writeBoolean( isClosed );
+ }
+
+ public static ConnectionManager deserialize(
+ ObjectInputStream ois,
+ SessionFactoryImplementor factory,
+ Interceptor interceptor,
+ ConnectionReleaseMode connectionReleaseMode,
+ JDBCContext jdbcContext) throws IOException {
+ return new ConnectionManager(
+ factory,
+ jdbcContext,
+ connectionReleaseMode,
+ interceptor,
+ ois.readBoolean(),
+ ois.readBoolean()
+ );
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/ConnectionWrapper.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/ConnectionWrapper.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/ConnectionWrapper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,18 @@
+package org.hibernate.jdbc;
+
+import java.sql.Connection;
+
+/**
+ * Interface implemented by JDBC connection wrappers in order to give
+ * access to the underlying wrapped connection.
+ *
+ * @author Steve Ebersole
+ */
+public interface ConnectionWrapper {
+ /**
+ * Get a reference to the wrapped connection.
+ *
+ * @return The wrapped connection.
+ */
+ public Connection getWrappedConnection();
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/Expectation.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/Expectation.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/Expectation.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,17 @@
+package org.hibernate.jdbc;
+
+import org.hibernate.HibernateException;
+
+import java.sql.SQLException;
+import java.sql.PreparedStatement;
+
+/**
+ * Defines an expected DML operation outcome.
+ *
+ * @author Steve Ebersole
+ */
+public interface Expectation {
+ public void verifyOutcome(int rowCount, PreparedStatement statement, int batchPosition) throws SQLException, HibernateException;
+ public int prepare(PreparedStatement statement) throws SQLException, HibernateException;
+ public boolean canBeBatched();
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/Expectations.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/Expectations.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/Expectations.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,174 @@
+package org.hibernate.jdbc;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.StaleStateException;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.ExecuteUpdateResultCheckStyle;
+import org.hibernate.util.JDBCExceptionReporter;
+import org.hibernate.exception.GenericJDBCException;
+
+import java.sql.CallableStatement;
+import java.sql.SQLException;
+import java.sql.PreparedStatement;
+import java.sql.Types;
+
+/**
+ * Holds various often used {@link Expectation} definitions.
+ *
+ * @author Steve Ebersole
+ */
+public class Expectations {
+ private static final Log log = LogFactory.getLog( Expectations.class );
+
+ public static final int USUAL_EXPECTED_COUNT = 1;
+ public static final int USUAL_PARAM_POSITION = 1;
+
+
+ // Base Expectation impls ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public static class BasicExpectation implements Expectation {
+ private final int expectedRowCount;
+
+ protected BasicExpectation(int expectedRowCount) {
+ this.expectedRowCount = expectedRowCount;
+ if ( expectedRowCount < 0 ) {
+ throw new IllegalArgumentException( "Expected row count must be greater than zero" );
+ }
+ }
+
+ public final void verifyOutcome(int rowCount, PreparedStatement statement, int batchPosition) {
+ rowCount = determineRowCount( rowCount, statement );
+ if ( batchPosition < 0 ) {
+ checkNonBatched( rowCount );
+ }
+ else {
+ checkBatched( rowCount, batchPosition );
+ }
+ }
+
+ private void checkBatched(int rowCount, int batchPosition) {
+ if ( rowCount == -2 ) {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "success of batch update unknown: " + batchPosition );
+ }
+ }
+ else if ( rowCount == -3 ) {
+ throw new BatchFailedException( "Batch update failed: " + batchPosition );
+ }
+ else {
+ if ( expectedRowCount > rowCount ) {
+ throw new StaleStateException(
+ "Batch update returned unexpected row count from update [" + batchPosition +
+ "]; actual row count: " + rowCount +
+ "; expected: " + expectedRowCount
+ );
+ }
+ if ( expectedRowCount < rowCount ) {
+ String msg = "Batch update returned unexpected row count from update [" +
+ batchPosition + "]; actual row count: " + rowCount +
+ "; expected: " + expectedRowCount;
+ throw new BatchedTooManyRowsAffectedException( msg, expectedRowCount, rowCount, batchPosition );
+ }
+ }
+ }
+
+ private void checkNonBatched(int rowCount) {
+ if ( expectedRowCount > rowCount ) {
+ throw new StaleStateException(
+ "Unexpected row count: " + rowCount + "; expected: " + expectedRowCount
+ );
+ }
+ if ( expectedRowCount < rowCount ) {
+ String msg = "Unexpected row count: " + rowCount + "; expected: " + expectedRowCount;
+ throw new TooManyRowsAffectedException( msg, expectedRowCount, rowCount );
+ }
+ }
+
+ public int prepare(PreparedStatement statement) throws SQLException, HibernateException {
+ return 0;
+ }
+
+ public boolean canBeBatched() {
+ return true;
+ }
+
+ protected int determineRowCount(int reportedRowCount, PreparedStatement statement) {
+ return reportedRowCount;
+ }
+ }
+
+ public static class BasicParamExpectation extends BasicExpectation {
+ private final int parameterPosition;
+ protected BasicParamExpectation(int expectedRowCount, int parameterPosition) {
+ super( expectedRowCount );
+ this.parameterPosition = parameterPosition;
+ }
+
+ public int prepare(PreparedStatement statement) throws SQLException, HibernateException {
+ toCallableStatement( statement ).registerOutParameter( parameterPosition, Types.NUMERIC );
+ return 1;
+ }
+
+ public boolean canBeBatched() {
+ return false;
+ }
+
+ protected int determineRowCount(int reportedRowCount, PreparedStatement statement) {
+ try {
+ return toCallableStatement( statement ).getInt( parameterPosition );
+ }
+ catch( SQLException sqle ) {
+ JDBCExceptionReporter.logExceptions( sqle, "could not extract row counts from CallableStatement" );
+ throw new GenericJDBCException( "could not extract row counts from CallableStatement", sqle );
+ }
+ }
+
+ private CallableStatement toCallableStatement(PreparedStatement statement) {
+ if ( ! CallableStatement.class.isInstance( statement ) ) {
+ throw new HibernateException( "BasicParamExpectation operates exclusively on CallableStatements : " + statement.getClass() );
+ }
+ return ( CallableStatement ) statement;
+ }
+ }
+
+
+ // Various Expectation instances ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public static final Expectation NONE = new Expectation() {
+ public void verifyOutcome(int rowCount, PreparedStatement statement, int batchPosition) {
+ // explicitly perform no checking...
+ }
+
+ public int prepare(PreparedStatement statement) {
+ return 0;
+ }
+
+ public boolean canBeBatched() {
+ return true;
+ }
+ };
+
+ public static final Expectation BASIC = new BasicExpectation( USUAL_EXPECTED_COUNT );
+
+ public static final Expectation PARAM = new BasicParamExpectation( USUAL_EXPECTED_COUNT, USUAL_PARAM_POSITION );
+
+
+ public static Expectation appropriateExpectation(ExecuteUpdateResultCheckStyle style) {
+ if ( style == ExecuteUpdateResultCheckStyle.NONE ) {
+ return NONE;
+ }
+ else if ( style == ExecuteUpdateResultCheckStyle.COUNT ) {
+ return BASIC;
+ }
+ else if ( style == ExecuteUpdateResultCheckStyle.PARAM ) {
+ return PARAM;
+ }
+ else {
+ throw new HibernateException( "unknown check style : " + style );
+ }
+ }
+
+ private Expectations() {
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/JDBCContext.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/JDBCContext.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/JDBCContext.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,313 @@
+// $Id: JDBCContext.java 10476 2006-09-08 21:00:48Z epbernard $
+package org.hibernate.jdbc;
+
+import java.io.Serializable;
+import java.io.ObjectOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import javax.transaction.TransactionManager;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.ConnectionReleaseMode;
+import org.hibernate.HibernateException;
+import org.hibernate.Interceptor;
+import org.hibernate.SessionException;
+import org.hibernate.Transaction;
+import org.hibernate.TransactionException;
+import org.hibernate.util.JTAHelper;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.exception.JDBCExceptionHelper;
+import org.hibernate.transaction.CacheSynchronization;
+import org.hibernate.transaction.TransactionFactory;
+
+/**
+ * Acts as the mediary between "entity-mode related" sessions in terms of
+ * their interaction with the JDBC data store.
+ *
+ * @author Steve Ebersole
+ */
+public class JDBCContext implements Serializable, ConnectionManager.Callback {
+
+ // TODO : make this the factory for "entity mode related" sessions;
+ // also means making this the target of transaction-synch and the
+ // thing that knows how to cascade things between related sessions
+ //
+ // At that point, perhaps this thing is a "SessionContext", and
+ // ConnectionManager is a "JDBCContext"? A "SessionContext" should
+ // live in the impl package...
+
+ private static final Log log = LogFactory.getLog( JDBCContext.class );
+
+ public static interface Context extends TransactionFactory.Context {
+ /**
+ * We cannot rely upon this method being called! It is only
+ * called if we are using Hibernate Transaction API.
+ */
+ public void afterTransactionBegin(Transaction tx);
+ public void beforeTransactionCompletion(Transaction tx);
+ public void afterTransactionCompletion(boolean success, Transaction tx);
+ public ConnectionReleaseMode getConnectionReleaseMode();
+ public boolean isAutoCloseSessionEnabled();
+ }
+
+ private Context owner;
+ private ConnectionManager connectionManager;
+ private transient boolean isTransactionCallbackRegistered;
+ private transient Transaction hibernateTransaction;
+
+ public JDBCContext(Context owner, Connection connection, Interceptor interceptor) {
+ this.owner = owner;
+ this.connectionManager = new ConnectionManager(
+ owner.getFactory(),
+ this,
+ owner.getConnectionReleaseMode(),
+ connection,
+ interceptor
+ );
+
+ final boolean registerSynchronization = owner.isAutoCloseSessionEnabled()
+ || owner.isFlushBeforeCompletionEnabled()
+ || owner.getConnectionReleaseMode() == ConnectionReleaseMode.AFTER_TRANSACTION;
+ if ( registerSynchronization ) {
+ registerSynchronizationIfPossible();
+ }
+ }
+
+ /**
+ * Private constructor used exclusively for custom serialization...
+ *
+ */
+ private JDBCContext() {
+ }
+
+ // ConnectionManager.Callback implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public void connectionOpened() {
+ if ( owner.getFactory().getStatistics().isStatisticsEnabled() ) {
+ owner.getFactory().getStatisticsImplementor().connect();
+ }
+ }
+
+ public void connectionCleanedUp() {
+ if ( !isTransactionCallbackRegistered ) {
+ afterTransactionCompletion( false, null );
+ // Note : success = false, because we don't know the outcome of the transaction
+ }
+ }
+
+ public SessionFactoryImplementor getFactory() {
+ return owner.getFactory();
+ }
+
+ public ConnectionManager getConnectionManager() {
+ return connectionManager;
+ }
+
+ public Connection borrowConnection() {
+ return connectionManager.borrowConnection();
+ }
+
+ public Connection connection() throws HibernateException {
+ if ( owner.isClosed() ) {
+ throw new SessionException( "Session is closed" );
+ }
+
+ return connectionManager.getConnection();
+ }
+
+ public boolean registerCallbackIfNecessary() {
+ if ( isTransactionCallbackRegistered ) {
+ return false;
+ }
+ else {
+ isTransactionCallbackRegistered = true;
+ return true;
+ }
+
+ }
+
+ public boolean registerSynchronizationIfPossible() {
+ if ( isTransactionCallbackRegistered ) {
+ // we already have a callback registered; either a local
+ // (org.hibernate.Transaction) transaction has accepted
+ // callback responsibilities, or we have previously
+ // registered a transaction synch.
+ return true;
+ }
+ boolean localCallbacksOnly = owner.getFactory().getSettings()
+ .getTransactionFactory()
+ .areCallbacksLocalToHibernateTransactions();
+ if ( localCallbacksOnly ) {
+ // the configured transaction-factory says it only supports
+ // local callback mode, so no sense attempting to register a
+ // JTA Synchronization
+ return false;
+ }
+ TransactionManager tm = owner.getFactory().getTransactionManager();
+ if ( tm == null ) {
+ // if there is no TM configured, we will not be able to access
+ // the javax.transaction.Transaction object in order to
+ // register a synch anyway.
+ return false;
+ }
+ else {
+ try {
+ if ( !isTransactionInProgress() ) {
+ log.trace( "TransactionFactory reported no active transaction; Synchronization not registered" );
+ return false;
+ }
+ else {
+ javax.transaction.Transaction tx = tm.getTransaction();
+ if ( JTAHelper.isMarkedForRollback( tx ) ) {
+ log.debug( "Transaction is marked for rollback; skipping Synchronization registration" );
+ return false;
+ }
+ else {
+ tx.registerSynchronization( new CacheSynchronization(owner, this, tx, null) );
+ isTransactionCallbackRegistered = true;
+ log.debug("successfully registered Synchronization");
+ return true;
+ }
+ }
+ }
+ catch( HibernateException e ) {
+ throw e;
+ }
+ catch (Exception e) {
+ throw new TransactionException( "could not register synchronization with JTA TransactionManager", e );
+ }
+ }
+ }
+
+ public boolean isTransactionInProgress() {
+ return owner.getFactory().getSettings().getTransactionFactory()
+ .isTransactionInProgress( this, owner, hibernateTransaction );
+ }
+
+ public Transaction getTransaction() throws HibernateException {
+ if (hibernateTransaction==null) {
+ hibernateTransaction = owner.getFactory().getSettings()
+ .getTransactionFactory()
+ .createTransaction( this, owner );
+ }
+ return hibernateTransaction;
+ }
+
+ public void beforeTransactionCompletion(Transaction tx) {
+ log.trace( "before transaction completion" );
+ owner.beforeTransactionCompletion(tx);
+ }
+
+ /**
+ * We cannot rely upon this method being called! It is only
+ * called if we are using Hibernate Transaction API.
+ */
+ public void afterTransactionBegin(Transaction tx) {
+ log.trace( "after transaction begin" );
+ owner.afterTransactionBegin(tx);
+ }
+
+ public void afterTransactionCompletion(boolean success, Transaction tx) {
+ log.trace( "after transaction completion" );
+
+ if ( getFactory().getStatistics().isStatisticsEnabled() ) {
+ getFactory().getStatisticsImplementor().endTransaction(success);
+ }
+
+ connectionManager.afterTransaction();
+
+ isTransactionCallbackRegistered = false;
+ hibernateTransaction = null;
+ owner.afterTransactionCompletion(success, tx);
+ }
+
+ /**
+ * Called after executing a query outside the scope of
+ * a Hibernate or JTA transaction
+ */
+ public void afterNontransactionalQuery(boolean success) {
+ log.trace( "after autocommit" );
+ try {
+ // check to see if the connection is in auto-commit
+ // mode (no connection means aggressive connection
+ // release outside a JTA transaction context, so MUST
+ // be autocommit mode)
+ boolean isAutocommit = connectionManager.isAutoCommit();
+
+ connectionManager.afterTransaction();
+
+ if ( isAutocommit ) {
+ owner.afterTransactionCompletion(success, null);
+ }
+ }
+ catch (SQLException sqle) {
+ throw JDBCExceptionHelper.convert(
+ owner.getFactory().getSQLExceptionConverter(),
+ sqle,
+ "could not inspect JDBC autocommit mode"
+ );
+ }
+ }
+
+
+ // serialization ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ private void writeObject(ObjectOutputStream oos) throws IOException {
+ // isTransactionCallbackRegistered denotes whether any Hibernate
+ // Transaction has registered as a callback against this
+ // JDBCContext; only one such callback is allowed. Directly
+ // serializing this value causes problems with JDBCTransaction,
+ // or really any Transaction impl where the callback is local
+ // to the Transaction instance itself, since that Transaction
+ // is not serialized along with the JDBCContext. Thus we
+ // handle that fact here explicitly...
+ oos.defaultWriteObject();
+ boolean deserHasCallbackRegistered = isTransactionCallbackRegistered
+ && ! owner.getFactory().getSettings().getTransactionFactory()
+ .areCallbacksLocalToHibernateTransactions();
+ oos.writeBoolean( deserHasCallbackRegistered );
+ }
+
+ private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
+ ois.defaultReadObject();
+ isTransactionCallbackRegistered = ois.readBoolean();
+ }
+
+ /**
+ * Custom serialization routine used during serialization of a
+ * Session/PersistenceContext for increased performance.
+ *
+ * @param oos The stream to which we should write the serial data.
+ * @throws IOException
+ */
+ public void serialize(ObjectOutputStream oos) throws IOException {
+ connectionManager.serialize( oos );
+ }
+
+ /**
+ * Custom deserialization routine used during deserialization of a
+ * Session/PersistenceContext for increased performance.
+ *
+ * @param ois The stream from which to read the entry.
+ * @throws IOException
+ */
+ public static JDBCContext deserialize(
+ ObjectInputStream ois,
+ Context context,
+ Interceptor interceptor) throws IOException {
+ JDBCContext jdbcContext = new JDBCContext();
+ jdbcContext.owner = context;
+ jdbcContext.connectionManager = ConnectionManager.deserialize(
+ ois,
+ context.getFactory(),
+ interceptor,
+ context.getConnectionReleaseMode(),
+ jdbcContext
+ );
+ return jdbcContext;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/NonBatchingBatcher.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/NonBatchingBatcher.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/NonBatchingBatcher.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,30 @@
+//$Id: NonBatchingBatcher.java 10040 2006-06-22 19:51:43Z steve.ebersole at jboss.com $
+package org.hibernate.jdbc;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+import org.hibernate.HibernateException;
+import org.hibernate.Interceptor;
+
+/**
+ * An implementation of the <tt>Batcher</tt> interface that does no batching
+ *
+ * @author Gavin King
+ */
+public class NonBatchingBatcher extends AbstractBatcher {
+
+ public NonBatchingBatcher(ConnectionManager connectionManager, Interceptor interceptor) {
+ super( connectionManager, interceptor );
+ }
+
+ public void addToBatch(Expectation expectation) throws SQLException, HibernateException {
+ PreparedStatement statement = getStatement();
+ final int rowCount = statement.executeUpdate();
+ expectation.verifyOutcome( rowCount, statement, 0 );
+ }
+
+ protected void doExecuteBatch(PreparedStatement ps) throws SQLException, HibernateException {
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/NonBatchingBatcherFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/NonBatchingBatcherFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/NonBatchingBatcherFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,19 @@
+//$Id: NonBatchingBatcherFactory.java 7683 2005-07-29 19:10:20Z maxcsaucdk $
+package org.hibernate.jdbc;
+
+import org.hibernate.Interceptor;
+
+
+/**
+ * A BatcherFactory implementation which constructs Batcher instances
+ * that do not perform batch operations.
+ *
+ * @author Gavin King
+ */
+public class NonBatchingBatcherFactory implements BatcherFactory {
+
+ public Batcher createBatcher(ConnectionManager connectionManager, Interceptor interceptor) {
+ return new NonBatchingBatcher( connectionManager, interceptor );
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/NonBatchingBatcherFactory.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/ResultSetWrapper.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/ResultSetWrapper.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/ResultSetWrapper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,619 @@
+// $Id: ResultSetWrapper.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate.jdbc;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.math.BigDecimal;
+import java.net.URL;
+import java.sql.Array;
+import java.sql.Blob;
+import java.sql.Clob;
+import java.sql.Date;
+import java.sql.Ref;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+import java.sql.Statement;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.util.Calendar;
+import java.util.Map;
+
+/**
+ * A ResultSet delegate, responsible for locally caching the columnName-to-columnIndex
+ * resolution that has been found to be inefficient in a few vendor's drivers (i.e., Oracle
+ * and Postgres).
+ *
+ * @author Steve Ebersole
+ */
+public class ResultSetWrapper implements ResultSet {
+
+ private ResultSet rs;
+ private ColumnNameCache columnNameCache;
+
+ public ResultSetWrapper(ResultSet resultSet, ColumnNameCache columnNameCache) {
+ this.rs = resultSet;
+ this.columnNameCache = columnNameCache;
+ }
+
+ /*package*/ ResultSet getTarget() {
+ return rs;
+ }
+
+
+ // ResultSet impl ("overridden") ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Overridden version to utilize local caching of the column indexes by name
+ * to improve performance for those drivers which are known to not support
+ * such caching by themselves.
+ * <p/>
+ * This implementation performs the caching based on the upper case version
+ * of the given column name.
+ *
+ * @param columnName The column name to resolve into an index.
+ * @return The column index corresponding to the given column name.
+ * @throws SQLException - if the ResultSet object does not contain
+ * columnName or a database access error occurs
+ */
+ public int findColumn(String columnName) throws SQLException {
+ return columnNameCache.getIndexForColumnName( columnName, this );
+ }
+
+ public Array getArray(String colName) throws SQLException {
+ return rs.getArray( findColumn(colName) );
+ }
+
+ public void updateArray(String columnName, Array x) throws SQLException {
+ rs.updateArray( findColumn(columnName), x );
+ }
+
+ public InputStream getAsciiStream(String columnName) throws SQLException {
+ return rs.getAsciiStream( findColumn(columnName) );
+ }
+
+ public void updateAsciiStream(String columnName, InputStream x, int length) throws SQLException {
+ rs.updateAsciiStream( findColumn(columnName), x, length );
+ }
+
+ public BigDecimal getBigDecimal(String columnName) throws SQLException {
+ return rs.getBigDecimal( findColumn(columnName) );
+ }
+
+ public BigDecimal getBigDecimal(String columnName, int scale) throws SQLException {
+ return rs.getBigDecimal( findColumn(columnName), scale );
+ }
+
+ public void updateBigDecimal(String columnName, BigDecimal x) throws SQLException {
+ rs.updateBigDecimal( findColumn(columnName), x );
+ }
+
+ public InputStream getBinaryStream(String columnName) throws SQLException {
+ return rs.getBinaryStream( findColumn(columnName) );
+ }
+
+ public void updateBinaryStream(String columnName, InputStream x, int length) throws SQLException {
+ rs.updateBinaryStream( findColumn(columnName), x, length );
+ }
+
+ public Blob getBlob(String columnName) throws SQLException {
+ return rs.getBlob( findColumn(columnName) );
+ }
+
+ public void updateBlob(String columnName, Blob x) throws SQLException {
+ rs.updateBlob( findColumn(columnName), x );
+ }
+
+ public boolean getBoolean(String columnName) throws SQLException {
+ return rs.getBoolean( findColumn(columnName) );
+ }
+
+ public void updateBoolean(String columnName, boolean x) throws SQLException {
+ rs.updateBoolean( findColumn(columnName), x );
+ }
+
+ public byte getByte(String columnName) throws SQLException {
+ return rs.getByte( findColumn(columnName) );
+ }
+
+ public void updateByte(String columnName, byte x) throws SQLException {
+ rs.updateByte( findColumn(columnName), x );
+ }
+
+ public byte[] getBytes(String columnName) throws SQLException {
+ return rs.getBytes( findColumn(columnName) );
+ }
+
+ public void updateBytes(String columnName, byte[] x) throws SQLException {
+ rs.updateBytes( findColumn(columnName), x );
+ }
+
+ public Reader getCharacterStream(String columnName) throws SQLException {
+ return rs.getCharacterStream( findColumn(columnName) );
+ }
+
+ public void updateCharacterStream(String columnName, Reader x, int length) throws SQLException {
+ rs.updateCharacterStream( findColumn(columnName), x, length );
+ }
+
+ public Clob getClob(String columnName) throws SQLException {
+ return rs.getClob( findColumn(columnName) );
+ }
+
+ public void updateClob(String columnName, Clob x) throws SQLException {
+ rs.updateClob( findColumn(columnName), x );
+ }
+
+ public Date getDate(String columnName) throws SQLException {
+ return rs.getDate( findColumn(columnName) );
+ }
+
+ public Date getDate(String columnName, Calendar cal) throws SQLException {
+ return rs.getDate( findColumn(columnName), cal );
+ }
+
+ public void updateDate(String columnName, Date x) throws SQLException {
+ rs.updateDate( findColumn(columnName), x );
+ }
+
+ public double getDouble(String columnName) throws SQLException {
+ return rs.getDouble( findColumn(columnName) );
+ }
+
+ public void updateDouble(String columnName, double x) throws SQLException {
+ rs.updateDouble( findColumn(columnName), x );
+ }
+
+ public float getFloat(String columnName) throws SQLException {
+ return rs.getFloat( findColumn(columnName) );
+ }
+
+ public void updateFloat(String columnName, float x) throws SQLException {
+ rs.updateFloat( findColumn(columnName), x );
+ }
+
+ public int getInt(String columnName) throws SQLException {
+ return rs.getInt( findColumn(columnName) );
+ }
+
+ public void updateInt(String columnName, int x) throws SQLException {
+ rs.updateInt( findColumn(columnName), x );
+ }
+
+ public long getLong(String columnName) throws SQLException {
+ return rs.getLong( findColumn(columnName) );
+ }
+
+ public void updateLong(String columnName, long x) throws SQLException {
+ rs.updateLong( findColumn(columnName), x );
+ }
+
+ public Object getObject(String columnName) throws SQLException {
+ return rs.getObject( findColumn(columnName) );
+ }
+
+ public Object getObject(String columnName, Map map) throws SQLException {
+ return rs.getObject( findColumn(columnName), map );
+ }
+
+ public void updateObject(String columnName, Object x) throws SQLException {
+ rs.updateObject( findColumn(columnName), x );
+ }
+
+ public void updateObject(String columnName, Object x, int scale) throws SQLException {
+ rs.updateObject( findColumn(columnName), x, scale );
+ }
+
+ public Ref getRef(String columnName) throws SQLException {
+ return rs.getRef( findColumn(columnName) );
+ }
+
+ public void updateRef(String columnName, Ref x) throws SQLException {
+ rs.updateRef( findColumn(columnName), x );
+ }
+
+ public short getShort(String columnName) throws SQLException {
+ return rs.getShort( findColumn(columnName) );
+ }
+
+ public void updateShort(String columnName, short x) throws SQLException {
+ rs.updateShort( findColumn(columnName), x );
+ }
+
+ public String getString(String columnName) throws SQLException {
+ return rs.getString( findColumn(columnName) );
+ }
+
+ public void updateString(String columnName, String x) throws SQLException {
+ rs.updateString( findColumn(columnName), x );
+ }
+
+ public Time getTime(String columnName) throws SQLException {
+ return rs.getTime( findColumn(columnName) );
+ }
+
+ public Time getTime(String columnName, Calendar cal) throws SQLException {
+ return rs.getTime( findColumn(columnName), cal );
+ }
+
+ public void updateTime(String columnName, Time x) throws SQLException {
+ rs.updateTime( findColumn(columnName), x );
+ }
+
+ public Timestamp getTimestamp(String columnName) throws SQLException {
+ return rs.getTimestamp( findColumn(columnName) );
+ }
+
+ public void updateTimestamp(String columnName, Timestamp x) throws SQLException {
+ rs.updateTimestamp( findColumn(columnName), x );
+ }
+
+ public Timestamp getTimestamp(String columnName, Calendar cal) throws SQLException {
+ return rs.getTimestamp( findColumn(columnName), cal );
+ }
+
+ public InputStream getUnicodeStream(String columnName) throws SQLException {
+ return rs.getUnicodeStream( findColumn(columnName) );
+ }
+
+ public URL getURL(String columnName) throws SQLException {
+ return rs.getURL( findColumn(columnName) );
+ }
+
+ public void updateNull(String columnName) throws SQLException {
+ rs.updateNull( findColumn(columnName) );
+ }
+
+
+ // ResultSet impl (delegated) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public int getConcurrency() throws SQLException {
+ return rs.getConcurrency();
+ }
+
+ public int getFetchDirection() throws SQLException {
+ return rs.getFetchDirection();
+ }
+
+ public int getFetchSize() throws SQLException {
+ return rs.getFetchSize();
+ }
+
+ public int getRow() throws SQLException {
+ return rs.getRow();
+ }
+
+ public int getType() throws SQLException {
+ return rs.getType();
+ }
+
+ public void afterLast() throws SQLException {
+ rs.afterLast();
+ }
+
+ public void beforeFirst() throws SQLException {
+ rs.beforeFirst();
+ }
+
+ public void cancelRowUpdates() throws SQLException {
+ rs.cancelRowUpdates();
+ }
+
+ public void clearWarnings() throws SQLException {
+ rs.clearWarnings();
+ }
+
+ public void close() throws SQLException {
+ rs.close();
+ }
+
+ public void deleteRow() throws SQLException {
+ rs.deleteRow();
+ }
+
+ public void insertRow() throws SQLException {
+ rs.insertRow();
+ }
+
+ public void moveToCurrentRow() throws SQLException {
+ rs.moveToCurrentRow();
+ }
+
+ public void moveToInsertRow() throws SQLException {
+ rs.moveToInsertRow();
+ }
+
+ public void refreshRow() throws SQLException {
+ rs.refreshRow();
+ }
+
+ public void updateRow() throws SQLException {
+ rs.updateRow();
+ }
+
+ public boolean first() throws SQLException {
+ return rs.first();
+ }
+
+ public boolean isAfterLast() throws SQLException {
+ return rs.isAfterLast();
+ }
+
+ public boolean isBeforeFirst() throws SQLException {
+ return rs.isBeforeFirst();
+ }
+
+ public boolean isFirst() throws SQLException {
+ return rs.isFirst();
+ }
+
+ public boolean isLast() throws SQLException {
+ return rs.isLast();
+ }
+
+ public boolean last() throws SQLException {
+ return rs.last();
+ }
+
+ public boolean next() throws SQLException {
+ return rs.next();
+ }
+
+ public boolean previous() throws SQLException {
+ return rs.previous();
+ }
+
+ public boolean rowDeleted() throws SQLException {
+ return rs.rowDeleted();
+ }
+
+ public boolean rowInserted() throws SQLException {
+ return rs.rowInserted();
+ }
+
+ public boolean rowUpdated() throws SQLException {
+ return rs.rowUpdated();
+ }
+
+ public boolean wasNull() throws SQLException {
+ return rs.wasNull();
+ }
+
+ public byte getByte(int columnIndex) throws SQLException {
+ return rs.getByte(columnIndex);
+ }
+
+ public double getDouble(int columnIndex) throws SQLException {
+ return rs.getDouble(columnIndex);
+ }
+
+ public float getFloat(int columnIndex) throws SQLException {
+ return rs.getFloat(columnIndex);
+ }
+
+ public int getInt(int columnIndex) throws SQLException {
+ return rs.getInt(columnIndex);
+ }
+
+ public long getLong(int columnIndex) throws SQLException {
+ return rs.getLong(columnIndex);
+ }
+
+ public short getShort(int columnIndex) throws SQLException {
+ return rs.getShort(columnIndex);
+ }
+
+ public void setFetchDirection(int direction) throws SQLException {
+ rs.setFetchDirection(direction);
+ }
+
+ public void setFetchSize(int rows) throws SQLException {
+ rs.setFetchSize(rows);
+ }
+
+ public void updateNull(int columnIndex) throws SQLException {
+ rs.updateNull(columnIndex);
+ }
+
+ public boolean absolute(int row) throws SQLException {
+ return rs.absolute(row);
+ }
+
+ public boolean getBoolean(int columnIndex) throws SQLException {
+ return rs.getBoolean(columnIndex);
+ }
+
+ public boolean relative(int rows) throws SQLException {
+ return rs.relative(rows);
+ }
+
+ public byte[] getBytes(int columnIndex) throws SQLException {
+ return rs.getBytes(columnIndex);
+ }
+
+ public void updateByte(int columnIndex, byte x) throws SQLException {
+ rs.updateByte(columnIndex, x);
+ }
+
+ public void updateDouble(int columnIndex, double x) throws SQLException {
+ rs.updateDouble(columnIndex, x);
+ }
+
+ public void updateFloat(int columnIndex, float x) throws SQLException {
+ rs.updateFloat(columnIndex, x);
+ }
+
+ public void updateInt(int columnIndex, int x) throws SQLException {
+ rs.updateInt(columnIndex, x);
+ }
+
+ public void updateLong(int columnIndex, long x) throws SQLException {
+ rs.updateLong(columnIndex, x);
+ }
+
+ public void updateShort(int columnIndex, short x) throws SQLException {
+ rs.updateShort(columnIndex, x);
+ }
+
+ public void updateBoolean(int columnIndex, boolean x) throws SQLException {
+ rs.updateBoolean(columnIndex, x);
+ }
+
+ public void updateBytes(int columnIndex, byte[] x) throws SQLException {
+ rs.updateBytes(columnIndex, x);
+ }
+
+ public InputStream getAsciiStream(int columnIndex) throws SQLException {
+ return rs.getAsciiStream(columnIndex);
+ }
+
+ public InputStream getBinaryStream(int columnIndex) throws SQLException {
+ return rs.getBinaryStream(columnIndex);
+ }
+
+ public InputStream getUnicodeStream(int columnIndex) throws SQLException {
+ return rs.getUnicodeStream(columnIndex);
+ }
+
+ public void updateAsciiStream(int columnIndex, InputStream x, int length) throws SQLException {
+ rs.updateAsciiStream(columnIndex, x, length);
+ }
+
+ public void updateBinaryStream(int columnIndex, InputStream x, int length) throws SQLException {
+ rs.updateBinaryStream(columnIndex, x, length);
+ }
+
+ public Reader getCharacterStream(int columnIndex) throws SQLException {
+ return rs.getCharacterStream(columnIndex);
+ }
+
+ public void updateCharacterStream(int columnIndex, Reader x, int length) throws SQLException {
+ rs.updateCharacterStream(columnIndex, x, length);
+ }
+
+ public Object getObject(int columnIndex) throws SQLException {
+ return rs.getObject(columnIndex);
+ }
+
+ public void updateObject(int columnIndex, Object x) throws SQLException {
+ rs.updateObject(columnIndex, x);
+ }
+
+ public void updateObject(int columnIndex, Object x, int scale) throws SQLException {
+ rs.updateObject(columnIndex, x, scale);
+ }
+
+ public String getCursorName() throws SQLException {
+ return rs.getCursorName();
+ }
+
+ public String getString(int columnIndex) throws SQLException {
+ return rs.getString(columnIndex);
+ }
+
+ public void updateString(int columnIndex, String x) throws SQLException {
+ rs.updateString(columnIndex, x);
+ }
+
+ public BigDecimal getBigDecimal(int columnIndex) throws SQLException {
+ return rs.getBigDecimal(columnIndex);
+ }
+
+ public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException {
+ return rs.getBigDecimal(columnIndex, scale);
+ }
+
+ public void updateBigDecimal(int columnIndex, BigDecimal x) throws SQLException {
+ rs.updateBigDecimal(columnIndex, x);
+ }
+
+ public URL getURL(int columnIndex) throws SQLException {
+ return rs.getURL(columnIndex);
+ }
+
+ public Array getArray(int columnIndex) throws SQLException {
+ return rs.getArray(columnIndex);
+ }
+
+ public void updateArray(int columnIndex, Array x) throws SQLException {
+ rs.updateArray(columnIndex, x);
+ }
+
+ public Blob getBlob(int columnIndex) throws SQLException {
+ return rs.getBlob(columnIndex);
+ }
+
+ public void updateBlob(int columnIndex, Blob x) throws SQLException {
+ rs.updateBlob(columnIndex, x);
+ }
+
+ public Clob getClob(int columnIndex) throws SQLException {
+ return rs.getClob(columnIndex);
+ }
+
+ public void updateClob(int columnIndex, Clob x) throws SQLException {
+ rs.updateClob(columnIndex, x);
+ }
+
+ public Date getDate(int columnIndex) throws SQLException {
+ return rs.getDate(columnIndex);
+ }
+
+ public void updateDate(int columnIndex, Date x) throws SQLException {
+ rs.updateDate(columnIndex, x);
+ }
+
+ public Ref getRef(int columnIndex) throws SQLException {
+ return rs.getRef(columnIndex);
+ }
+
+ public void updateRef(int columnIndex, Ref x) throws SQLException {
+ rs.updateRef(columnIndex, x);
+ }
+
+ public ResultSetMetaData getMetaData() throws SQLException {
+ return rs.getMetaData();
+ }
+
+ public SQLWarning getWarnings() throws SQLException {
+ return rs.getWarnings();
+ }
+
+ public Statement getStatement() throws SQLException {
+ return rs.getStatement();
+ }
+
+ public Time getTime(int columnIndex) throws SQLException {
+ return rs.getTime(columnIndex);
+ }
+
+ public void updateTime(int columnIndex, Time x) throws SQLException {
+ rs.updateTime(columnIndex, x);
+ }
+
+ public Timestamp getTimestamp(int columnIndex) throws SQLException {
+ return rs.getTimestamp(columnIndex);
+ }
+
+ public void updateTimestamp(int columnIndex, Timestamp x) throws SQLException {
+ rs.updateTimestamp(columnIndex, x);
+ }
+
+ public Object getObject(int columnIndex, Map map) throws SQLException {
+ return rs.getObject( columnIndex, map );
+ }
+
+ public Date getDate(int columnIndex, Calendar cal) throws SQLException {
+ return rs.getDate(columnIndex, cal);
+ }
+
+ public Time getTime(int columnIndex, Calendar cal) throws SQLException {
+ return rs.getTime(columnIndex, cal);
+ }
+
+ public Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException {
+ return rs.getTimestamp(columnIndex, cal);
+ }
+}
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/TooManyRowsAffectedException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/TooManyRowsAffectedException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/TooManyRowsAffectedException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,29 @@
+package org.hibernate.jdbc;
+
+import org.hibernate.HibernateException;
+
+/**
+ * Indicates that more rows were affected then we were expecting to be.
+ * Typically indicates presence of duplicate "PK" values in the
+ * given table.
+ *
+ * @author Steve Ebersole
+ */
+public class TooManyRowsAffectedException extends HibernateException {
+ private final int expectedRowCount;
+ private final int actualRowCount;
+
+ public TooManyRowsAffectedException(String message, int expectedRowCount, int actualRowCount) {
+ super( message );
+ this.expectedRowCount = expectedRowCount;
+ this.actualRowCount = actualRowCount;
+ }
+
+ public int getExpectedRowCount() {
+ return expectedRowCount;
+ }
+
+ public int getActualRowCount() {
+ return actualRowCount;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,13 @@
+<html>
+<head></head>
+<body>
+<p>
+ This package abstracts the mechanism for dispatching SQL statements
+ to the database, and implements interaction with JDBC.
+</p>
+<p>
+ Concrete implementations of the <tt>Batcher</tt> interface may be
+ selected by specifying <tt>hibernate.jdbc.factory_class</tt>.
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/jdbc/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/AbstractEntityJoinWalker.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/AbstractEntityJoinWalker.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/AbstractEntityJoinWalker.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,143 @@
+//$Id: AbstractEntityJoinWalker.java 11080 2007-01-23 16:29:18Z steve.ebersole at jboss.com $
+package org.hibernate.loader;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.hibernate.FetchMode;
+import org.hibernate.LockMode;
+import org.hibernate.MappingException;
+import org.hibernate.engine.CascadeStyle;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.persister.entity.Loadable;
+import org.hibernate.persister.entity.OuterJoinLoadable;
+import org.hibernate.sql.JoinFragment;
+import org.hibernate.sql.Select;
+import org.hibernate.type.AssociationType;
+import org.hibernate.util.CollectionHelper;
+
+/**
+ * Abstract walker for walkers which begin at an entity (criteria
+ * queries and entity loaders).
+ *
+ * @author Gavin King
+ */
+public abstract class AbstractEntityJoinWalker extends JoinWalker {
+
+ private final OuterJoinLoadable persister;
+ private String alias;
+
+ public AbstractEntityJoinWalker(OuterJoinLoadable persister, SessionFactoryImplementor factory, Map enabledFilters) {
+ super( factory, enabledFilters );
+ this.persister = persister;
+ alias = generateRootAlias( persister.getEntityName() );
+ }
+
+ protected final void initAll(
+ final String whereString,
+ final String orderByString,
+ final LockMode lockMode)
+ throws MappingException {
+
+ walkEntityTree( persister, getAlias() );
+
+ List allAssociations = new ArrayList();
+ allAssociations.addAll(associations);
+ allAssociations.add( new OuterJoinableAssociation(
+ persister.getEntityType(),
+ null,
+ null,
+ alias,
+ JoinFragment.LEFT_OUTER_JOIN,
+ getFactory(),
+ CollectionHelper.EMPTY_MAP
+ ) );
+
+ initPersisters(allAssociations, lockMode);
+ initStatementString( whereString, orderByString, lockMode);
+ }
+
+ protected final void initProjection(
+ final String projectionString,
+ final String whereString,
+ final String orderByString,
+ final String groupByString,
+ final LockMode lockMode)
+ throws MappingException {
+ walkEntityTree( persister, getAlias() );
+ persisters = new Loadable[0];
+ initStatementString(projectionString, whereString, orderByString, groupByString, lockMode);
+ }
+
+ private void initStatementString(
+ final String condition,
+ final String orderBy,
+ final LockMode lockMode)
+ throws MappingException {
+ initStatementString(null, condition, orderBy, "", lockMode);
+ }
+
+ private void initStatementString(
+ final String projection,
+ final String condition,
+ final String orderBy,
+ final String groupBy,
+ final LockMode lockMode) throws MappingException {
+
+ final int joins = countEntityPersisters( associations );
+ suffixes = BasicLoader.generateSuffixes( joins + 1 );
+
+ JoinFragment ojf = mergeOuterJoins( associations );
+
+ Select select = new Select( getDialect() )
+ .setLockMode( lockMode )
+ .setSelectClause(
+ projection == null ?
+ persister.selectFragment( alias, suffixes[joins] ) + selectString( associations ) :
+ projection
+ )
+ .setFromClause(
+ getDialect().appendLockHint( lockMode, persister.fromTableFragment( alias ) ) +
+ persister.fromJoinFragment( alias, true, true )
+ )
+ .setWhereClause( condition )
+ .setOuterJoins(
+ ojf.toFromFragmentString(),
+ ojf.toWhereFragmentString() + getWhereFragment()
+ )
+ .setOrderByClause( orderBy( associations, orderBy ) )
+ .setGroupByClause( groupBy );
+
+ if ( getFactory().getSettings().isCommentsEnabled() ) {
+ select.setComment( getComment() );
+ }
+ sql = select.toStatementString();
+ }
+
+ protected String getWhereFragment() throws MappingException {
+ // here we do not bother with the discriminator.
+ return persister.whereJoinFragment(alias, true, true);
+ }
+
+ /**
+ * The superclass deliberately excludes collections
+ */
+ protected boolean isJoinedFetchEnabled(AssociationType type, FetchMode config, CascadeStyle cascadeStyle) {
+ return isJoinedFetchEnabledInMapping(config, type);
+ }
+
+ public abstract String getComment();
+
+ protected final Loadable getPersister() {
+ return persister;
+ }
+
+ protected final String getAlias() {
+ return alias;
+ }
+
+ public String toString() {
+ return getClass().getName() + '(' + getPersister().getEntityName() + ')';
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/AbstractEntityJoinWalker.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/BasicLoader.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/BasicLoader.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/BasicLoader.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,93 @@
+//$Id: BasicLoader.java 9870 2006-05-04 11:57:58Z steve.ebersole at jboss.com $
+package org.hibernate.loader;
+
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.persister.entity.Loadable;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.type.BagType;
+import org.hibernate.HibernateException;
+
+/**
+ * Uses the default mapping from property to result set column
+ * alias defined by the entities' persisters. Used when Hibernate
+ * is generating result set column aliases.
+ *
+ * @author Gavin King
+ */
+public abstract class BasicLoader extends Loader {
+
+ protected static final String[] NO_SUFFIX = {""};
+
+ private EntityAliases[] descriptors;
+ private CollectionAliases[] collectionDescriptors;
+
+ public BasicLoader(SessionFactoryImplementor factory) {
+ super(factory);
+ }
+
+ protected final EntityAliases[] getEntityAliases() {
+ return descriptors;
+ }
+
+ protected final CollectionAliases[] getCollectionAliases() {
+ return collectionDescriptors;
+ }
+
+ protected abstract String[] getSuffixes();
+ protected abstract String[] getCollectionSuffixes();
+
+ protected void postInstantiate() {
+ Loadable[] persisters = getEntityPersisters();
+ String[] suffixes = getSuffixes();
+ descriptors = new EntityAliases[persisters.length];
+ for ( int i=0; i<descriptors.length; i++ ) {
+ descriptors[i] = new DefaultEntityAliases( persisters[i], suffixes[i] );
+ }
+
+ CollectionPersister[] collectionPersisters = getCollectionPersisters();
+ int bagCount = 0;
+ if ( collectionPersisters != null ) {
+ String[] collectionSuffixes = getCollectionSuffixes();
+ collectionDescriptors = new CollectionAliases[collectionPersisters.length];
+ for ( int i = 0; i < collectionPersisters.length; i++ ) {
+ if ( isBag( collectionPersisters[i] ) ) {
+ bagCount++;
+ }
+ collectionDescriptors[i] = new GeneratedCollectionAliases(
+ collectionPersisters[i],
+ collectionSuffixes[i]
+ );
+ }
+ }
+ else {
+ collectionDescriptors = null;
+ }
+ if ( bagCount > 1 ) {
+ throw new HibernateException( "cannot simultaneously fetch multiple bags" );
+ }
+ }
+
+ private boolean isBag(CollectionPersister collectionPersister) {
+ return collectionPersister.getCollectionType().getClass().isAssignableFrom( BagType.class );
+ }
+
+ /**
+ * Utility method that generates 0_, 1_ suffixes. Subclasses don't
+ * necessarily need to use this algorithm, but it is intended that
+ * they will in most cases.
+ */
+ public static String[] generateSuffixes(int length) {
+ return generateSuffixes( 0, length );
+ }
+
+ public static String[] generateSuffixes(int seed, int length) {
+ if ( length == 0 ) return NO_SUFFIX;
+
+ String[] suffixes = new String[length];
+ for ( int i = 0; i < length; i++ ) {
+ suffixes[i] = Integer.toString( i + seed ) + "_";
+ }
+ return suffixes;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/CollectionAliases.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/CollectionAliases.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/CollectionAliases.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,49 @@
+// $Id: CollectionAliases.java 6828 2005-05-19 07:28:59Z steveebersole $
+package org.hibernate.loader;
+
+/**
+ * Type definition of CollectionAliases.
+ *
+ * @author Steve Ebersole
+ */
+public interface CollectionAliases {
+ /**
+ * Returns the suffixed result-set column-aliases for columns making
+ * up the key for this collection (i.e., its FK to its owner).
+ *
+ * @return The key result-set column aliases.
+ */
+ public String[] getSuffixedKeyAliases();
+
+ /**
+ * Returns the suffixed result-set column-aliases for the collumns
+ * making up the collection's index (map or list).
+ *
+ * @return The index result-set column aliases.
+ */
+ public String[] getSuffixedIndexAliases();
+
+ /**
+ * Returns the suffixed result-set column-aliases for the columns
+ * making up the collection's elements.
+ *
+ * @return The element result-set column aliases.
+ */
+ public String[] getSuffixedElementAliases();
+
+ /**
+ * Returns the suffixed result-set column-aliases for the column
+ * defining the collection's identifier (if any).
+ *
+ * @return The identifier result-set column aliases.
+ */
+ public String getSuffixedIdentifierAlias();
+
+ /**
+ * Returns the suffix used to unique the column aliases for this
+ * particular alias set.
+ *
+ * @return The uniqued column alias suffix.
+ */
+ public String getSuffix();
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/ColumnEntityAliases.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/ColumnEntityAliases.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/ColumnEntityAliases.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,32 @@
+package org.hibernate.loader;
+
+import java.util.Map;
+
+import org.hibernate.persister.entity.Loadable;
+
+/**
+ * EntityAliases that chooses the column names over the alias names.
+ *
+ * @author max
+ *
+ */
+public class ColumnEntityAliases extends DefaultEntityAliases {
+
+ public ColumnEntityAliases(Map returnProperties, Loadable persister, String suffix) {
+ super(returnProperties, persister, suffix);
+ }
+
+ protected String[] getIdentifierAliases(Loadable persister, String suffix) {
+ return persister.getIdentifierColumnNames();
+ }
+
+ protected String getDiscriminatorAlias(Loadable persister, String suffix) {
+ return persister.getDiscriminatorColumnName();
+ }
+
+ protected String[] getPropertyAliases(Loadable persister, int j) {
+ return persister.getPropertyColumnNames(j);
+ }
+
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/DefaultEntityAliases.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/DefaultEntityAliases.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/DefaultEntityAliases.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,137 @@
+package org.hibernate.loader;
+
+import java.util.Map;
+
+import org.hibernate.persister.entity.Loadable;
+import org.hibernate.util.CollectionHelper;
+
+/**
+ * EntityAliases which handles the logic of selecting user provided aliases (via return-property),
+ * before using the default aliases.
+ *
+ * @author max
+ *
+ */
+public class DefaultEntityAliases implements EntityAliases {
+
+ private final String[] suffixedKeyColumns;
+ private final String[] suffixedVersionColumn;
+ private final String[][] suffixedPropertyColumns;
+ private final String suffixedDiscriminatorColumn;
+ private final String suffix;
+ private final String rowIdAlias;
+ private final Map userProvidedAliases;
+
+ public DefaultEntityAliases(Loadable persister, String suffix) {
+ this(CollectionHelper.EMPTY_MAP, persister, suffix);
+ }
+
+ /**
+ * Calculate and cache select-clause suffixes.
+ * @param map
+ */
+ public DefaultEntityAliases(Map userProvidedAliases, Loadable persister, String suffix) {
+ this.suffix = suffix;
+ this.userProvidedAliases = userProvidedAliases;
+
+ String[] keyColumnsCandidates = getUserProvidedAliases(
+ persister.getIdentifierPropertyName(),
+ (String[]) null
+ );
+ if (keyColumnsCandidates==null) {
+ suffixedKeyColumns = getUserProvidedAliases(
+ "id",
+ getIdentifierAliases(persister, suffix)
+ );
+ }
+ else {
+ suffixedKeyColumns = keyColumnsCandidates;
+ }
+ intern(suffixedKeyColumns);
+
+ suffixedPropertyColumns = getSuffixedPropertyAliases(persister);
+ suffixedDiscriminatorColumn = getUserProvidedAlias(
+ "class",
+ getDiscriminatorAlias(persister, suffix)
+ );
+ if ( persister.isVersioned() ) {
+ suffixedVersionColumn = suffixedPropertyColumns[ persister.getVersionProperty() ];
+ }
+ else {
+ suffixedVersionColumn = null;
+ }
+ rowIdAlias = Loadable.ROWID_ALIAS + suffix; // TODO: not visible to the user!
+ }
+
+ protected String getDiscriminatorAlias(Loadable persister, String suffix) {
+ return persister.getDiscriminatorAlias(suffix);
+ }
+
+ protected String[] getIdentifierAliases(Loadable persister, String suffix) {
+ return persister.getIdentifierAliases(suffix);
+ }
+
+ protected String[] getPropertyAliases(Loadable persister, int j) {
+ return persister.getPropertyAliases(suffix, j);
+ }
+
+ private String[] getUserProvidedAliases(String propertyPath, String[] defaultAliases) {
+ String[] result = (String[]) userProvidedAliases.get(propertyPath);
+ if (result==null) {
+ return defaultAliases;
+ }
+ else {
+ return result;
+ }
+ }
+
+ private String getUserProvidedAlias(String propertyPath, String defaultAlias) {
+ String[] columns = (String[]) userProvidedAliases.get(propertyPath);
+ if (columns==null) {
+ return defaultAlias;
+ }
+ else {
+ return columns[0];
+ }
+ }
+
+ public String[][] getSuffixedPropertyAliases(Loadable persister) {
+ int size = persister.getPropertyNames().length;
+ String[][] suffixedPropertyAliases = new String[size][];
+ for ( int j = 0; j < size; j++ ) {
+ suffixedPropertyAliases[j] = getUserProvidedAliases(
+ persister.getPropertyNames()[j],
+ getPropertyAliases(persister, j)
+ );
+ intern( suffixedPropertyAliases[j] );
+ }
+ return suffixedPropertyAliases;
+ }
+
+ public String[] getSuffixedVersionAliases() {
+ return suffixedVersionColumn;
+ }
+
+ public String[][] getSuffixedPropertyAliases() {
+ return suffixedPropertyColumns;
+ }
+
+ public String getSuffixedDiscriminatorAlias() {
+ return suffixedDiscriminatorColumn;
+ }
+
+ public String[] getSuffixedKeyAliases() {
+ return suffixedKeyColumns;
+ }
+
+ public String getRowIdAlias() {
+ return rowIdAlias;
+ }
+
+ private static void intern(String[] strings) {
+ for (int i=0; i<strings.length; i++ ) {
+ strings[i] = strings[i].intern();
+ }
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/EntityAliases.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/EntityAliases.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/EntityAliases.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,38 @@
+//$Id: EntityAliases.java 5699 2005-02-13 11:50:11Z oneovthafew $
+package org.hibernate.loader;
+
+import org.hibernate.persister.entity.Loadable;
+
+/**
+ * Metadata describing the SQL result set column aliases
+ * for a particular entity.
+ *
+ * @author Gavin King
+ */
+public interface EntityAliases {
+ /**
+ * The result set column aliases for the primary key columns
+ */
+ public String[] getSuffixedKeyAliases();
+ /**
+ * The result set column aliases for the discriminator columns
+ */
+ public String getSuffixedDiscriminatorAlias();
+ /**
+ * The result set column aliases for the version columns
+ */
+ public String[] getSuffixedVersionAliases();
+ /**
+ * The result set column aliases for the property columns
+ */
+ public String[][] getSuffixedPropertyAliases();
+ /**
+ * The result set column aliases for the property columns of a subclass
+ */
+ public String[][] getSuffixedPropertyAliases(Loadable persister);
+ /**
+ * The result set column alias for the Oracle row id
+ */
+ public String getRowIdAlias();
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/EntityAliases.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/GeneratedCollectionAliases.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/GeneratedCollectionAliases.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/GeneratedCollectionAliases.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,131 @@
+// $Id: GeneratedCollectionAliases.java 8410 2005-10-14 22:43:44Z maxcsaucdk $
+package org.hibernate.loader;
+
+import java.util.Map;
+
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.util.CollectionHelper;
+import org.hibernate.util.StringHelper;
+
+/**
+ * CollectionAliases which handles the logic of selecting user provided aliases (via return-property),
+ * before using the default aliases.
+ *
+ * @author Steve Ebersole and Max Rydahl Andersen
+ */
+public class GeneratedCollectionAliases implements CollectionAliases {
+ private final String suffix;
+ private final String[] keyAliases;
+ private final String[] indexAliases;
+ private final String[] elementAliases;
+ private final String identifierAlias;
+ private Map userProvidedAliases;
+
+ public GeneratedCollectionAliases(Map userProvidedAliases, CollectionPersister persister, String suffix) {
+ this.suffix = suffix;
+ this.userProvidedAliases = userProvidedAliases;
+
+ this.keyAliases = getUserProvidedAliases(
+ "key",
+ persister.getKeyColumnAliases( suffix )
+ );
+
+ this.indexAliases = getUserProvidedAliases(
+ "index",
+ persister.getIndexColumnAliases( suffix )
+ );
+
+ this.elementAliases = getUserProvidedAliases( "element",
+ persister.getElementColumnAliases( suffix )
+ );
+
+ this.identifierAlias = getUserProvidedAlias( "id",
+ persister.getIdentifierColumnAlias( suffix )
+ );
+ }
+
+ public GeneratedCollectionAliases(CollectionPersister persister, String string) {
+ this(CollectionHelper.EMPTY_MAP, persister, string);
+ }
+
+ /**
+ * Returns the suffixed result-set column-aliases for columns making up the key for this collection (i.e., its FK to
+ * its owner).
+ *
+ * @return The key result-set column aliases.
+ */
+ public String[] getSuffixedKeyAliases() {
+ return keyAliases;
+ }
+
+ /**
+ * Returns the suffixed result-set column-aliases for the collumns making up the collection's index (map or list).
+ *
+ * @return The index result-set column aliases.
+ */
+ public String[] getSuffixedIndexAliases() {
+ return indexAliases;
+ }
+
+ /**
+ * Returns the suffixed result-set column-aliases for the columns making up the collection's elements.
+ *
+ * @return The element result-set column aliases.
+ */
+ public String[] getSuffixedElementAliases() {
+ return elementAliases;
+ }
+
+ /**
+ * Returns the suffixed result-set column-aliases for the column defining the collection's identifier (if any).
+ *
+ * @return The identifier result-set column aliases.
+ */
+ public String getSuffixedIdentifierAlias() {
+ return identifierAlias;
+ }
+
+ /**
+ * Returns the suffix used to unique the column aliases for this particular alias set.
+ *
+ * @return The uniqued column alias suffix.
+ */
+ public String getSuffix() {
+ return suffix;
+ }
+
+ public String toString() {
+ return super.toString() + " [suffix=" + suffix +
+ ", suffixedKeyAliases=[" + join( keyAliases ) +
+ "], suffixedIndexAliases=[" + join( indexAliases ) +
+ "], suffixedElementAliases=[" + join( elementAliases ) +
+ "], suffixedIdentifierAlias=[" + identifierAlias + "]]";
+ }
+
+ private String join(String[] aliases) {
+ if ( aliases == null) return null;
+
+ return StringHelper.join( ", ", aliases );
+ }
+
+ private String[] getUserProvidedAliases(String propertyPath, String[] defaultAliases) {
+ String[] result = (String[]) userProvidedAliases.get(propertyPath);
+ if (result==null) {
+ return defaultAliases;
+ }
+ else {
+ return result;
+ }
+ }
+
+ private String getUserProvidedAlias(String propertyPath, String defaultAlias) {
+ String[] columns = (String[]) userProvidedAliases.get(propertyPath);
+ if (columns==null) {
+ return defaultAlias;
+ }
+ else {
+ return columns[0];
+ }
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/JoinWalker.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/JoinWalker.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/JoinWalker.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,967 @@
+//$Id: JoinWalker.java 9889 2006-05-05 01:24:12Z steve.ebersole at jboss.com $
+package org.hibernate.loader;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.hibernate.FetchMode;
+import org.hibernate.LockMode;
+import org.hibernate.MappingException;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.CascadeStyle;
+import org.hibernate.engine.JoinHelper;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.persister.collection.QueryableCollection;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.persister.entity.Joinable;
+import org.hibernate.persister.entity.Loadable;
+import org.hibernate.persister.entity.OuterJoinLoadable;
+import org.hibernate.sql.ConditionFragment;
+import org.hibernate.sql.DisjunctionFragment;
+import org.hibernate.sql.InFragment;
+import org.hibernate.sql.JoinFragment;
+import org.hibernate.type.AbstractComponentType;
+import org.hibernate.type.AssociationType;
+import org.hibernate.type.EntityType;
+import org.hibernate.type.ForeignKeyDirection;
+import org.hibernate.type.Type;
+import org.hibernate.util.ArrayHelper;
+import org.hibernate.util.StringHelper;
+
+/**
+ * Walks the metamodel, searching for joins, and collecting
+ * together information needed by <tt>OuterJoinLoader</tt>.
+ *
+ * @see OuterJoinLoader
+ * @author Gavin King, Jon Lipsky
+ */
+public class JoinWalker {
+
+ private final SessionFactoryImplementor factory;
+ protected final List associations = new ArrayList();
+ private final Set visitedAssociationKeys = new HashSet();
+ private final Map enabledFilters;
+
+ protected String[] suffixes;
+ protected String[] collectionSuffixes;
+ protected Loadable[] persisters;
+ protected int[] owners;
+ protected EntityType[] ownerAssociationTypes;
+ protected CollectionPersister[] collectionPersisters;
+ protected int[] collectionOwners;
+ protected String[] aliases;
+ protected LockMode[] lockModeArray;
+ protected String sql;
+
+ public String[] getCollectionSuffixes() {
+ return collectionSuffixes;
+ }
+
+ public void setCollectionSuffixes(String[] collectionSuffixes) {
+ this.collectionSuffixes = collectionSuffixes;
+ }
+
+ public LockMode[] getLockModeArray() {
+ return lockModeArray;
+ }
+
+ public void setLockModeArray(LockMode[] lockModeArray) {
+ this.lockModeArray = lockModeArray;
+ }
+
+ public String[] getSuffixes() {
+ return suffixes;
+ }
+
+ public void setSuffixes(String[] suffixes) {
+ this.suffixes = suffixes;
+ }
+
+ public String[] getAliases() {
+ return aliases;
+ }
+
+ public void setAliases(String[] aliases) {
+ this.aliases = aliases;
+ }
+
+ public int[] getCollectionOwners() {
+ return collectionOwners;
+ }
+
+ public void setCollectionOwners(int[] collectionOwners) {
+ this.collectionOwners = collectionOwners;
+ }
+
+ public CollectionPersister[] getCollectionPersisters() {
+ return collectionPersisters;
+ }
+
+ public void setCollectionPersisters(CollectionPersister[] collectionPersisters) {
+ this.collectionPersisters = collectionPersisters;
+ }
+
+ public EntityType[] getOwnerAssociationTypes() {
+ return ownerAssociationTypes;
+ }
+
+ public void setOwnerAssociationTypes(EntityType[] ownerAssociationType) {
+ this.ownerAssociationTypes = ownerAssociationType;
+ }
+
+ public int[] getOwners() {
+ return owners;
+ }
+
+ public void setOwners(int[] owners) {
+ this.owners = owners;
+ }
+
+ public Loadable[] getPersisters() {
+ return persisters;
+ }
+
+ public void setPersisters(Loadable[] persisters) {
+ this.persisters = persisters;
+ }
+
+ public String getSQLString() {
+ return sql;
+ }
+
+ public void setSql(String sql) {
+ this.sql = sql;
+ }
+
+ protected SessionFactoryImplementor getFactory() {
+ return factory;
+ }
+
+ protected Dialect getDialect() {
+ return factory.getDialect();
+ }
+
+ protected Map getEnabledFilters() {
+ return enabledFilters;
+ }
+
+ protected JoinWalker(SessionFactoryImplementor factory, Map enabledFilters) {
+ this.factory = factory;
+ this.enabledFilters = enabledFilters;
+ }
+
+ /**
+ * Add on association (one-to-one, many-to-one, or a collection) to a list
+ * of associations to be fetched by outerjoin (if necessary)
+ */
+ private void addAssociationToJoinTreeIfNecessary(
+ final AssociationType type,
+ final String[] aliasedLhsColumns,
+ final String alias,
+ final String path,
+ int currentDepth,
+ final int joinType)
+ throws MappingException {
+
+ if (joinType>=0) {
+ addAssociationToJoinTree(
+ type,
+ aliasedLhsColumns,
+ alias,
+ path,
+ currentDepth,
+ joinType
+ );
+ }
+
+ }
+
+ /**
+ * Add on association (one-to-one, many-to-one, or a collection) to a list
+ * of associations to be fetched by outerjoin
+ */
+ private void addAssociationToJoinTree(
+ final AssociationType type,
+ final String[] aliasedLhsColumns,
+ final String alias,
+ final String path,
+ final int currentDepth,
+ final int joinType)
+ throws MappingException {
+
+ Joinable joinable = type.getAssociatedJoinable( getFactory() );
+
+ String subalias = generateTableAlias(
+ associations.size()+1, //before adding to collection!
+ path,
+ joinable
+ );
+
+ OuterJoinableAssociation assoc = new OuterJoinableAssociation(
+ type,
+ alias,
+ aliasedLhsColumns,
+ subalias,
+ joinType,
+ getFactory(),
+ enabledFilters
+ );
+ assoc.validateJoin(path);
+ associations.add(assoc);
+
+ int nextDepth = currentDepth+1;
+ if ( !joinable.isCollection() ) {
+ if (joinable instanceof OuterJoinLoadable) {
+ walkEntityTree(
+ (OuterJoinLoadable) joinable,
+ subalias,
+ path,
+ nextDepth
+ );
+ }
+ }
+ else {
+ if (joinable instanceof QueryableCollection) {
+ walkCollectionTree(
+ (QueryableCollection) joinable,
+ subalias,
+ path,
+ nextDepth
+ );
+ }
+ }
+
+ }
+
+ /**
+ * For an entity class, return a list of associations to be fetched by outerjoin
+ */
+ protected final void walkEntityTree(OuterJoinLoadable persister, String alias)
+ throws MappingException {
+ walkEntityTree(persister, alias, "", 0);
+ }
+
+ /**
+ * For a collection role, return a list of associations to be fetched by outerjoin
+ */
+ protected final void walkCollectionTree(QueryableCollection persister, String alias)
+ throws MappingException {
+ walkCollectionTree(persister, alias, "", 0);
+ //TODO: when this is the entry point, we should use an INNER_JOIN for fetching the many-to-many elements!
+ }
+
+ /**
+ * For a collection role, return a list of associations to be fetched by outerjoin
+ */
+ private void walkCollectionTree(
+ final QueryableCollection persister,
+ final String alias,
+ final String path,
+ final int currentDepth)
+ throws MappingException {
+
+ if ( persister.isOneToMany() ) {
+ walkEntityTree(
+ (OuterJoinLoadable) persister.getElementPersister(),
+ alias,
+ path,
+ currentDepth
+ );
+ }
+ else {
+ Type type = persister.getElementType();
+ if ( type.isAssociationType() ) {
+ // a many-to-many;
+ // decrement currentDepth here to allow join across the association table
+ // without exceeding MAX_FETCH_DEPTH (i.e. the "currentDepth - 1" bit)
+ AssociationType associationType = (AssociationType) type;
+ String[] aliasedLhsColumns = persister.getElementColumnNames(alias);
+ String[] lhsColumns = persister.getElementColumnNames();
+ // if the current depth is 0, the root thing being loaded is the
+ // many-to-many collection itself. Here, it is alright to use
+ // an inner join...
+ boolean useInnerJoin = currentDepth == 0;
+ final int joinType = getJoinType(
+ associationType,
+ persister.getFetchMode(),
+ path,
+ persister.getTableName(),
+ lhsColumns,
+ !useInnerJoin,
+ currentDepth - 1,
+ null //operations which cascade as far as the collection also cascade to collection elements
+ );
+ addAssociationToJoinTreeIfNecessary(
+ associationType,
+ aliasedLhsColumns,
+ alias,
+ path,
+ currentDepth - 1,
+ joinType
+ );
+ }
+ else if ( type.isComponentType() ) {
+ walkCompositeElementTree(
+ (AbstractComponentType) type,
+ persister.getElementColumnNames(),
+ persister,
+ alias,
+ path,
+ currentDepth
+ );
+ }
+ }
+
+ }
+
+ /**
+ * Walk the tree for a particular entity association
+ */
+ private final void walkEntityAssociationTree(
+ final AssociationType associationType,
+ final OuterJoinLoadable persister,
+ final int propertyNumber,
+ final String alias,
+ final String path,
+ final boolean nullable,
+ final int currentDepth)
+ throws MappingException {
+
+ String[] aliasedLhsColumns = JoinHelper.getAliasedLHSColumnNames(
+ associationType, alias, propertyNumber, persister, getFactory()
+ );
+
+ String[] lhsColumns = JoinHelper.getLHSColumnNames(
+ associationType, propertyNumber, persister, getFactory()
+ );
+ String lhsTable = JoinHelper.getLHSTableName(associationType, propertyNumber, persister);
+
+ String subpath = subPath( path, persister.getSubclassPropertyName(propertyNumber) );
+ int joinType = getJoinType(
+ associationType,
+ persister.getFetchMode(propertyNumber),
+ subpath,
+ lhsTable,
+ lhsColumns,
+ nullable,
+ currentDepth,
+ persister.getCascadeStyle(propertyNumber)
+ );
+ addAssociationToJoinTreeIfNecessary(
+ associationType,
+ aliasedLhsColumns,
+ alias,
+ subpath,
+ currentDepth,
+ joinType
+ );
+
+ }
+
+ /**
+ * For an entity class, add to a list of associations to be fetched
+ * by outerjoin
+ */
+ private final void walkEntityTree(
+ final OuterJoinLoadable persister,
+ final String alias,
+ final String path,
+ final int currentDepth)
+ throws MappingException {
+
+ int n = persister.countSubclassProperties();
+ for ( int i=0; i<n; i++ ) {
+ Type type = persister.getSubclassPropertyType(i);
+ if ( type.isAssociationType() ) {
+ walkEntityAssociationTree(
+ (AssociationType) type,
+ persister,
+ i,
+ alias,
+ path,
+ persister.isSubclassPropertyNullable(i),
+ currentDepth
+ );
+ }
+ else if ( type.isComponentType() ) {
+ walkComponentTree(
+ (AbstractComponentType) type,
+ i,
+ 0,
+ persister,
+ alias,
+ subPath( path, persister.getSubclassPropertyName(i) ),
+ currentDepth
+ );
+ }
+ }
+ }
+
+ /**
+ * For a component, add to a list of associations to be fetched by outerjoin
+ */
+ private void walkComponentTree(
+ final AbstractComponentType componentType,
+ final int propertyNumber,
+ int begin,
+ final OuterJoinLoadable persister,
+ final String alias,
+ final String path,
+ final int currentDepth
+ ) throws MappingException {
+
+ Type[] types = componentType.getSubtypes();
+ String[] propertyNames = componentType.getPropertyNames();
+ for ( int i=0; i <types.length; i++ ) {
+
+ if ( types[i].isAssociationType() ) {
+ AssociationType associationType = (AssociationType) types[i];
+
+ String[] aliasedLhsColumns = JoinHelper.getAliasedLHSColumnNames(
+ associationType, alias, propertyNumber, begin, persister, getFactory()
+ );
+
+ String[] lhsColumns = JoinHelper.getLHSColumnNames(
+ associationType, propertyNumber, begin, persister, getFactory()
+ );
+ String lhsTable = JoinHelper.getLHSTableName(associationType, propertyNumber, persister);
+
+ String subpath = subPath( path, propertyNames[i] );
+ final boolean[] propertyNullability = componentType.getPropertyNullability();
+ final int joinType = getJoinType(
+ associationType,
+ componentType.getFetchMode(i),
+ subpath,
+ lhsTable,
+ lhsColumns,
+ propertyNullability==null || propertyNullability[i],
+ currentDepth,
+ componentType.getCascadeStyle(i)
+ );
+ addAssociationToJoinTreeIfNecessary(
+ associationType,
+ aliasedLhsColumns,
+ alias,
+ subpath,
+ currentDepth,
+ joinType
+ );
+
+ }
+ else if ( types[i].isComponentType() ) {
+ String subpath = subPath( path, propertyNames[i] );
+ walkComponentTree(
+ (AbstractComponentType) types[i],
+ propertyNumber,
+ begin,
+ persister,
+ alias,
+ subpath,
+ currentDepth
+ );
+ }
+
+ begin+=types[i].getColumnSpan( getFactory() );
+ }
+
+ }
+
+ /**
+ * For a composite element, add to a list of associations to be fetched by outerjoin
+ */
+ private void walkCompositeElementTree(
+ final AbstractComponentType compositeType,
+ final String[] cols,
+ final QueryableCollection persister,
+ final String alias,
+ final String path,
+ final int currentDepth)
+ throws MappingException {
+
+ Type[] types = compositeType.getSubtypes();
+ String[] propertyNames = compositeType.getPropertyNames();
+ int begin = 0;
+ for ( int i=0; i <types.length; i++ ) {
+ int length = types[i].getColumnSpan( getFactory() );
+ String[] lhsColumns = ArrayHelper.slice(cols, begin, length);
+
+ if ( types[i].isAssociationType() ) {
+ AssociationType associationType = (AssociationType) types[i];
+
+ // simple, because we can't have a one-to-one or a collection
+ // (or even a property-ref) in a composite-element:
+ String[] aliasedLhsColumns = StringHelper.qualify(alias, lhsColumns);
+
+ String subpath = subPath( path, propertyNames[i] );
+ final boolean[] propertyNullability = compositeType.getPropertyNullability();
+ final int joinType = getJoinType(
+ associationType,
+ compositeType.getFetchMode(i),
+ subpath,
+ persister.getTableName(),
+ lhsColumns,
+ propertyNullability==null || propertyNullability[i],
+ currentDepth,
+ compositeType.getCascadeStyle(i)
+ );
+ addAssociationToJoinTreeIfNecessary(
+ associationType,
+ aliasedLhsColumns,
+ alias,
+ subpath,
+ currentDepth,
+ joinType
+ );
+ }
+ else if ( types[i].isComponentType() ) {
+ String subpath = subPath( path, propertyNames[i] );
+ walkCompositeElementTree(
+ (AbstractComponentType) types[i],
+ lhsColumns,
+ persister,
+ alias,
+ subpath,
+ currentDepth
+ );
+ }
+ begin+=length;
+ }
+
+ }
+
+ /**
+ * Extend the path by the given property name
+ */
+ private static String subPath(String path, String property) {
+ if ( path==null || path.length()==0) {
+ return property;
+ }
+ else {
+ return StringHelper.qualify(path, property);
+ }
+ }
+
+ /**
+ * Get the join type (inner, outer, etc) or -1 if the
+ * association should not be joined. Override on
+ * subclasses.
+ */
+ protected int getJoinType(
+ AssociationType type,
+ FetchMode config,
+ String path,
+ String lhsTable,
+ String[] lhsColumns,
+ boolean nullable,
+ int currentDepth,
+ CascadeStyle cascadeStyle)
+ throws MappingException {
+
+ if ( !isJoinedFetchEnabled(type, config, cascadeStyle) ) return -1;
+
+ if ( isTooDeep(currentDepth) || ( type.isCollectionType() && isTooManyCollections() ) ) return -1;
+
+ final boolean dupe = isDuplicateAssociation(lhsTable, lhsColumns, type);
+ if (dupe) return -1;
+
+ return getJoinType(nullable, currentDepth);
+
+ }
+
+ /**
+ * Use an inner join if it is a non-null association and this
+ * is the "first" join in a series
+ */
+ protected int getJoinType(boolean nullable, int currentDepth) {
+ //TODO: this is too conservative; if all preceding joins were
+ // also inner joins, we could use an inner join here
+ return !nullable && currentDepth==0 ?
+ JoinFragment.INNER_JOIN :
+ JoinFragment.LEFT_OUTER_JOIN;
+ }
+
+ protected boolean isTooDeep(int currentDepth) {
+ Integer maxFetchDepth = getFactory().getSettings().getMaximumFetchDepth();
+ return maxFetchDepth!=null && currentDepth >= maxFetchDepth.intValue();
+ }
+
+ protected boolean isTooManyCollections() {
+ return false;
+ }
+
+ /**
+ * Does the mapping, and Hibernate default semantics, specify that
+ * this association should be fetched by outer joining
+ */
+ protected boolean isJoinedFetchEnabledInMapping(FetchMode config, AssociationType type)
+ throws MappingException {
+ if ( !type.isEntityType() && !type.isCollectionType() ) {
+ return false;
+ }
+ else {
+ if (config==FetchMode.JOIN) return true;
+ if (config==FetchMode.SELECT) return false;
+ if ( type.isEntityType() ) {
+ //TODO: look at the owning property and check that it
+ // isn't lazy (by instrumentation)
+ EntityType entityType =(EntityType) type;
+ EntityPersister persister = getFactory().getEntityPersister( entityType.getAssociatedEntityName() );
+ return !persister.hasProxy();
+ }
+ else {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Override on subclasses to enable or suppress joining
+ * of certain association types
+ */
+ protected boolean isJoinedFetchEnabled(AssociationType type, FetchMode config, CascadeStyle cascadeStyle) {
+ return type.isEntityType() && isJoinedFetchEnabledInMapping(config, type) ;
+ }
+
+ protected String generateTableAlias(
+ final int n,
+ final String path,
+ final Joinable joinable
+ ) {
+ return StringHelper.generateAlias( joinable.getName(), n );
+ }
+
+ protected String generateRootAlias(final String description) {
+ return StringHelper.generateAlias(description, 0);
+ }
+
+ /**
+ * Used to detect circularities in the joined graph, note that
+ * this method is side-effecty
+ */
+ protected boolean isDuplicateAssociation(
+ final String foreignKeyTable,
+ final String[] foreignKeyColumns
+ ) {
+ AssociationKey associationKey = new AssociationKey(foreignKeyColumns, foreignKeyTable);
+ return !visitedAssociationKeys.add( associationKey );
+ }
+
+ /**
+ * Used to detect circularities in the joined graph, note that
+ * this method is side-effecty
+ */
+ protected boolean isDuplicateAssociation(
+ final String lhsTable,
+ final String[] lhsColumnNames,
+ final AssociationType type
+ ) {
+ final String foreignKeyTable;
+ final String[] foreignKeyColumns;
+ if ( type.getForeignKeyDirection()==ForeignKeyDirection.FOREIGN_KEY_FROM_PARENT ) {
+ foreignKeyTable = lhsTable;
+ foreignKeyColumns = lhsColumnNames;
+ }
+ else {
+ foreignKeyTable = type.getAssociatedJoinable( getFactory() ).getTableName();
+ foreignKeyColumns = JoinHelper.getRHSColumnNames( type, getFactory() );
+ }
+ return isDuplicateAssociation(foreignKeyTable, foreignKeyColumns);
+ }
+
+ /**
+ * Uniquely identifier a foreign key, so that we don't
+ * join it more than once, and create circularities
+ */
+ private static final class AssociationKey {
+ private String[] columns;
+ private String table;
+ private AssociationKey(String[] columns, String table) {
+ this.columns = columns;
+ this.table = table;
+ }
+ public boolean equals(Object other) {
+ AssociationKey that = (AssociationKey) other;
+ return that.table.equals(table) && Arrays.equals(columns, that.columns);
+ }
+ public int hashCode() {
+ return table.hashCode(); //TODO: inefficient
+ }
+ }
+
+ /**
+ * Should we join this association?
+ */
+ protected boolean isJoinable(
+ final int joinType,
+ final Set visitedAssociationKeys,
+ final String lhsTable,
+ final String[] lhsColumnNames,
+ final AssociationType type,
+ final int depth
+ ) {
+ if (joinType<0) return false;
+
+ if (joinType==JoinFragment.INNER_JOIN) return true;
+
+ Integer maxFetchDepth = getFactory().getSettings().getMaximumFetchDepth();
+ final boolean tooDeep = maxFetchDepth!=null &&
+ depth >= maxFetchDepth.intValue();
+
+ return !tooDeep && !isDuplicateAssociation(lhsTable, lhsColumnNames, type);
+ }
+
+ protected String orderBy(final List associations, final String orderBy) {
+ return mergeOrderings( orderBy( associations ), orderBy );
+ }
+
+ protected static String mergeOrderings(String ordering1, String ordering2) {
+ if ( ordering1.length() == 0 ) {
+ return ordering2;
+ }
+ else if ( ordering2.length() == 0 ) {
+ return ordering1;
+ }
+ else {
+ return ordering1 + ", " + ordering2;
+ }
+ }
+
+ /**
+ * Generate a sequence of <tt>LEFT OUTER JOIN</tt> clauses for the given associations.
+ */
+ protected final JoinFragment mergeOuterJoins(List associations)
+ throws MappingException {
+ JoinFragment outerjoin = getDialect().createOuterJoinFragment();
+ Iterator iter = associations.iterator();
+ OuterJoinableAssociation last = null;
+ while ( iter.hasNext() ) {
+ OuterJoinableAssociation oj = (OuterJoinableAssociation) iter.next();
+ if ( last != null && last.isManyToManyWith( oj ) ) {
+ oj.addManyToManyJoin( outerjoin, ( QueryableCollection ) last.getJoinable() );
+ }
+ else {
+ oj.addJoins(outerjoin);
+ }
+ last = oj;
+ }
+ last = null;
+ return outerjoin;
+ }
+
+ /**
+ * Count the number of instances of Joinable which are actually
+ * also instances of Loadable, or are one-to-many associations
+ */
+ protected static final int countEntityPersisters(List associations)
+ throws MappingException {
+ int result = 0;
+ Iterator iter = associations.iterator();
+ while ( iter.hasNext() ) {
+ OuterJoinableAssociation oj = (OuterJoinableAssociation) iter.next();
+ if ( oj.getJoinable().consumesEntityAlias() ) {
+ result++;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Count the number of instances of Joinable which are actually
+ * also instances of PersistentCollection which are being fetched
+ * by outer join
+ */
+ protected static final int countCollectionPersisters(List associations)
+ throws MappingException {
+ int result = 0;
+ Iterator iter = associations.iterator();
+ while ( iter.hasNext() ) {
+ OuterJoinableAssociation oj = (OuterJoinableAssociation) iter.next();
+ if ( oj.getJoinType()==JoinFragment.LEFT_OUTER_JOIN && oj.getJoinable().isCollection() ) {
+ result++;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Get the order by string required for collection fetching
+ */
+ protected static final String orderBy(List associations)
+ throws MappingException {
+ StringBuffer buf = new StringBuffer();
+ Iterator iter = associations.iterator();
+ OuterJoinableAssociation last = null;
+ while ( iter.hasNext() ) {
+ OuterJoinableAssociation oj = (OuterJoinableAssociation) iter.next();
+ if ( oj.getJoinType() == JoinFragment.LEFT_OUTER_JOIN ) { // why does this matter?
+ if ( oj.getJoinable().isCollection() ) {
+ final QueryableCollection queryableCollection = (QueryableCollection) oj.getJoinable();
+ if ( queryableCollection.hasOrdering() ) {
+ final String orderByString = queryableCollection.getSQLOrderByString( oj.getRHSAlias() );
+ buf.append( orderByString ).append(", ");
+ }
+ }
+ else {
+ // it might still need to apply a collection ordering based on a
+ // many-to-many defined order-by...
+ if ( last != null && last.getJoinable().isCollection() ) {
+ final QueryableCollection queryableCollection = (QueryableCollection) last.getJoinable();
+ if ( queryableCollection.isManyToMany() && last.isManyToManyWith( oj ) ) {
+ if ( queryableCollection.hasManyToManyOrdering() ) {
+ final String orderByString = queryableCollection.getManyToManyOrderByString( oj.getRHSAlias() );
+ buf.append( orderByString ).append(", ");
+ }
+ }
+ }
+ }
+ }
+ last = oj;
+ }
+ if ( buf.length()>0 ) buf.setLength( buf.length()-2 );
+ return buf.toString();
+ }
+
+ /**
+ * Render the where condition for a (batch) load by identifier / collection key
+ */
+ protected StringBuffer whereString(String alias, String[] columnNames, int batchSize) {
+ if ( columnNames.length==1 ) {
+ // if not a composite key, use "foo in (?, ?, ?)" for batching
+ // if no batch, and not a composite key, use "foo = ?"
+ InFragment in = new InFragment().setColumn( alias, columnNames[0] );
+ for ( int i=0; i<batchSize; i++ ) in.addValue("?");
+ return new StringBuffer( in.toFragmentString() );
+ }
+ else {
+ //a composite key
+ ConditionFragment byId = new ConditionFragment()
+ .setTableAlias(alias)
+ .setCondition( columnNames, "?" );
+
+ StringBuffer whereString = new StringBuffer();
+ if ( batchSize==1 ) {
+ // if no batch, use "foo = ? and bar = ?"
+ whereString.append( byId.toFragmentString() );
+ }
+ else {
+ // if a composite key, use "( (foo = ? and bar = ?) or (foo = ? and bar = ?) )" for batching
+ whereString.append('('); //TODO: unnecessary for databases with ANSI-style joins
+ DisjunctionFragment df = new DisjunctionFragment();
+ for ( int i=0; i<batchSize; i++ ) {
+ df.addCondition(byId);
+ }
+ whereString.append( df.toFragmentString() );
+ whereString.append(')'); //TODO: unnecessary for databases with ANSI-style joins
+ }
+ return whereString;
+ }
+ }
+
+ protected void initPersisters(final List associations, final LockMode lockMode) throws MappingException {
+
+ final int joins = countEntityPersisters(associations);
+ final int collections = countCollectionPersisters(associations);
+
+ collectionOwners = collections==0 ? null : new int[collections];
+ collectionPersisters = collections==0 ? null : new CollectionPersister[collections];
+ collectionSuffixes = BasicLoader.generateSuffixes( joins + 1, collections );
+
+ persisters = new Loadable[joins];
+ aliases = new String[joins];
+ owners = new int[joins];
+ ownerAssociationTypes = new EntityType[joins];
+ lockModeArray = ArrayHelper.fillArray(lockMode, joins);
+
+ int i=0;
+ int j=0;
+ Iterator iter = associations.iterator();
+ while ( iter.hasNext() ) {
+ final OuterJoinableAssociation oj = (OuterJoinableAssociation) iter.next();
+ if ( !oj.isCollection() ) {
+
+ persisters[i] = (Loadable) oj.getJoinable();
+ aliases[i] = oj.getRHSAlias();
+ owners[i] = oj.getOwner(associations);
+ ownerAssociationTypes[i] = (EntityType) oj.getJoinableType();
+ i++;
+
+ }
+ else {
+
+ QueryableCollection collPersister = (QueryableCollection) oj.getJoinable();
+ if ( oj.getJoinType()==JoinFragment.LEFT_OUTER_JOIN ) {
+ //it must be a collection fetch
+ collectionPersisters[j] = collPersister;
+ collectionOwners[j] = oj.getOwner(associations);
+ j++;
+ }
+
+ if ( collPersister.isOneToMany() ) {
+ persisters[i] = (Loadable) collPersister.getElementPersister();
+ aliases[i] = oj.getRHSAlias();
+ i++;
+ }
+ }
+ }
+
+ if ( ArrayHelper.isAllNegative(owners) ) owners = null;
+ if ( collectionOwners!=null && ArrayHelper.isAllNegative(collectionOwners) ) {
+ collectionOwners = null;
+ }
+ }
+
+ /**
+ * Generate a select list of columns containing all properties of the entity classes
+ */
+ protected final String selectString(List associations)
+ throws MappingException {
+
+ if ( associations.size()==0 ) {
+ return "";
+ }
+ else {
+ StringBuffer buf = new StringBuffer( associations.size() * 100 )
+ .append(", ");
+ int entityAliasCount=0;
+ int collectionAliasCount=0;
+ for ( int i=0; i<associations.size(); i++ ) {
+ OuterJoinableAssociation join = (OuterJoinableAssociation) associations.get(i);
+ OuterJoinableAssociation next = (i == associations.size() - 1)
+ ? null
+ : ( OuterJoinableAssociation ) associations.get( i + 1 );
+ final Joinable joinable = join.getJoinable();
+ final String entitySuffix = ( suffixes == null || entityAliasCount >= suffixes.length )
+ ? null
+ : suffixes[entityAliasCount];
+ final String collectionSuffix = ( collectionSuffixes == null || collectionAliasCount >= collectionSuffixes.length )
+ ? null
+ : collectionSuffixes[collectionAliasCount];
+ final String selectFragment = joinable.selectFragment(
+ next == null ? null : next.getJoinable(),
+ next == null ? null : next.getRHSAlias(),
+ join.getRHSAlias(),
+ entitySuffix,
+ collectionSuffix,
+ join.getJoinType()==JoinFragment.LEFT_OUTER_JOIN
+ );
+ buf.append(selectFragment);
+ if ( joinable.consumesEntityAlias() ) entityAliasCount++;
+ if ( joinable.consumesCollectionAlias() && join.getJoinType()==JoinFragment.LEFT_OUTER_JOIN ) collectionAliasCount++;
+ if (
+ i<associations.size()-1 &&
+ selectFragment.trim().length()>0
+ ) {
+ buf.append(", ");
+ }
+ }
+ return buf.toString();
+ }
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/JoinWalker.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/Loader.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/Loader.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/Loader.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,2365 @@
+//$Id: Loader.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.loader;
+
+import java.io.Serializable;
+import java.sql.CallableStatement;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.AssertionFailure;
+import org.hibernate.HibernateException;
+import org.hibernate.LockMode;
+import org.hibernate.QueryException;
+import org.hibernate.ScrollMode;
+import org.hibernate.ScrollableResults;
+import org.hibernate.StaleObjectStateException;
+import org.hibernate.WrongClassException;
+import org.hibernate.cache.FilterKey;
+import org.hibernate.cache.QueryCache;
+import org.hibernate.cache.QueryKey;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.EntityKey;
+import org.hibernate.engine.EntityUniqueKey;
+import org.hibernate.engine.PersistenceContext;
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.RowSelection;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.SubselectFetch;
+import org.hibernate.engine.TwoPhaseLoad;
+import org.hibernate.engine.TypedValue;
+import org.hibernate.event.EventSource;
+import org.hibernate.event.PostLoadEvent;
+import org.hibernate.event.PreLoadEvent;
+import org.hibernate.exception.JDBCExceptionHelper;
+import org.hibernate.hql.HolderInstantiator;
+import org.hibernate.impl.FetchingScrollableResultsImpl;
+import org.hibernate.impl.ScrollableResultsImpl;
+import org.hibernate.jdbc.ColumnNameCache;
+import org.hibernate.jdbc.ResultSetWrapper;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.persister.entity.Loadable;
+import org.hibernate.persister.entity.UniqueKeyLoadable;
+import org.hibernate.pretty.MessageHelper;
+import org.hibernate.proxy.HibernateProxy;
+import org.hibernate.transform.ResultTransformer;
+import org.hibernate.type.AssociationType;
+import org.hibernate.type.EntityType;
+import org.hibernate.type.Type;
+import org.hibernate.type.VersionType;
+import org.hibernate.util.StringHelper;
+
+/**
+ * Abstract superclass of object loading (and querying) strategies. This class implements
+ * useful common functionality that concrete loaders delegate to. It is not intended that this
+ * functionality would be directly accessed by client code. (Hence, all methods of this class
+ * are declared <tt>protected</tt> or <tt>private</tt>.) This class relies heavily upon the
+ * <tt>Loadable</tt> interface, which is the contract between this class and
+ * <tt>EntityPersister</tt>s that may be loaded by it.<br>
+ * <br>
+ * The present implementation is able to load any number of columns of entities and at most
+ * one collection role per query.
+ *
+ * @author Gavin King
+ * @see org.hibernate.persister.entity.Loadable
+ */
+public abstract class Loader {
+
+ private static final Log log = LogFactory.getLog( Loader.class );
+
+ private final SessionFactoryImplementor factory;
+ private ColumnNameCache columnNameCache;
+
+ public Loader(SessionFactoryImplementor factory) {
+ this.factory = factory;
+ }
+
+ /**
+ * The SQL query string to be called; implemented by all subclasses
+ *
+ * @return The sql command this loader should use to get its {@link ResultSet}.
+ */
+ protected abstract String getSQLString();
+
+ /**
+ * An array of persisters of entity classes contained in each row of results;
+ * implemented by all subclasses
+ *
+ * @return The entity persisters.
+ */
+ protected abstract Loadable[] getEntityPersisters();
+
+ /**
+ * An array indicating whether the entities have eager property fetching
+ * enabled.
+ *
+ * @return Eager property fetching indicators.
+ */
+ protected boolean[] getEntityEagerPropertyFetches() {
+ return null;
+ }
+
+ /**
+ * An array of indexes of the entity that owns a one-to-one association
+ * to the entity at the given index (-1 if there is no "owner"). The
+ * indexes contained here are relative to the result of
+ * {@link #getEntityPersisters}.
+ *
+ * @return The owner indicators (see discussion above).
+ */
+ protected int[] getOwners() {
+ return null;
+ }
+
+ /**
+ * An array of the owner types corresponding to the {@link #getOwners()}
+ * returns. Indices indicating no owner would be null here.
+ *
+ * @return The types for the owners.
+ */
+ protected EntityType[] getOwnerAssociationTypes() {
+ return null;
+ }
+
+ /**
+ * An (optional) persister for a collection to be initialized; only
+ * collection loaders return a non-null value
+ */
+ protected CollectionPersister[] getCollectionPersisters() {
+ return null;
+ }
+
+ /**
+ * Get the index of the entity that owns the collection, or -1
+ * if there is no owner in the query results (ie. in the case of a
+ * collection initializer) or no collection.
+ */
+ protected int[] getCollectionOwners() {
+ return null;
+ }
+
+ /**
+ * What lock mode does this load entities with?
+ *
+ * @param lockModes a collection of lock modes specified dynamically via the Query interface
+ */
+ protected abstract LockMode[] getLockModes(Map lockModes);
+
+ /**
+ * Append <tt>FOR UPDATE OF</tt> clause, if necessary. This
+ * empty superclass implementation merely returns its first
+ * argument.
+ */
+ protected String applyLocks(String sql, Map lockModes, Dialect dialect) throws HibernateException {
+ return sql;
+ }
+
+ /**
+ * Does this query return objects that might be already cached
+ * by the session, whose lock mode may need upgrading
+ */
+ protected boolean upgradeLocks() {
+ return false;
+ }
+
+ /**
+ * Return false is this loader is a batch entity loader
+ */
+ protected boolean isSingleRowLoader() {
+ return false;
+ }
+
+ /**
+ * Get the SQL table aliases of entities whose
+ * associations are subselect-loadable, returning
+ * null if this loader does not support subselect
+ * loading
+ */
+ protected String[] getAliases() {
+ return null;
+ }
+
+ /**
+ * Modify the SQL, adding lock hints and comments, if necessary
+ */
+ protected String preprocessSQL(String sql, QueryParameters parameters, Dialect dialect)
+ throws HibernateException {
+
+ sql = applyLocks( sql, parameters.getLockModes(), dialect );
+
+ return getFactory().getSettings().isCommentsEnabled() ?
+ prependComment( sql, parameters ) : sql;
+ }
+
+ private String prependComment(String sql, QueryParameters parameters) {
+ String comment = parameters.getComment();
+ if ( comment == null ) {
+ return sql;
+ }
+ else {
+ return new StringBuffer( comment.length() + sql.length() + 5 )
+ .append( "/* " )
+ .append( comment )
+ .append( " */ " )
+ .append( sql )
+ .toString();
+ }
+ }
+
+ /**
+ * Execute an SQL query and attempt to instantiate instances of the class mapped by the given
+ * persister from each row of the <tt>ResultSet</tt>. If an object is supplied, will attempt to
+ * initialize that object. If a collection is supplied, attempt to initialize that collection.
+ */
+ private List doQueryAndInitializeNonLazyCollections(final SessionImplementor session,
+ final QueryParameters queryParameters,
+ final boolean returnProxies)
+ throws HibernateException, SQLException {
+
+ final PersistenceContext persistenceContext = session.getPersistenceContext();
+ persistenceContext.beforeLoad();
+ List result;
+ try {
+ result = doQuery( session, queryParameters, returnProxies );
+ }
+ finally {
+ persistenceContext.afterLoad();
+ }
+ persistenceContext.initializeNonLazyCollections();
+ return result;
+ }
+
+ /**
+ * Loads a single row from the result set. This is the processing used from the
+ * ScrollableResults where no collection fetches were encountered.
+ *
+ * @param resultSet The result set from which to do the load.
+ * @param session The session from which the request originated.
+ * @param queryParameters The query parameters specified by the user.
+ * @param returnProxies Should proxies be generated
+ * @return The loaded "row".
+ * @throws HibernateException
+ */
+ public Object loadSingleRow(
+ final ResultSet resultSet,
+ final SessionImplementor session,
+ final QueryParameters queryParameters,
+ final boolean returnProxies) throws HibernateException {
+
+ final int entitySpan = getEntityPersisters().length;
+ final List hydratedObjects = entitySpan == 0 ?
+ null : new ArrayList( entitySpan );
+
+ final Object result;
+ try {
+ result = getRowFromResultSet(
+ resultSet,
+ session,
+ queryParameters,
+ getLockModes( queryParameters.getLockModes() ),
+ null,
+ hydratedObjects,
+ new EntityKey[entitySpan],
+ returnProxies
+ );
+ }
+ catch ( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ factory.getSQLExceptionConverter(),
+ sqle,
+ "could not read next row of results",
+ getSQLString()
+ );
+ }
+
+ initializeEntitiesAndCollections(
+ hydratedObjects,
+ resultSet,
+ session,
+ queryParameters.isReadOnly()
+ );
+ session.getPersistenceContext().initializeNonLazyCollections();
+ return result;
+ }
+
+ private Object sequentialLoad(
+ final ResultSet resultSet,
+ final SessionImplementor session,
+ final QueryParameters queryParameters,
+ final boolean returnProxies,
+ final EntityKey keyToRead) throws HibernateException {
+
+ final int entitySpan = getEntityPersisters().length;
+ final List hydratedObjects = entitySpan == 0 ?
+ null : new ArrayList( entitySpan );
+
+ Object result = null;
+ final EntityKey[] loadedKeys = new EntityKey[entitySpan];
+
+ try {
+ do {
+ Object loaded = getRowFromResultSet(
+ resultSet,
+ session,
+ queryParameters,
+ getLockModes( queryParameters.getLockModes() ),
+ null,
+ hydratedObjects,
+ loadedKeys,
+ returnProxies
+ );
+ if ( result == null ) {
+ result = loaded;
+ }
+ }
+ while ( keyToRead.equals( loadedKeys[0] ) && resultSet.next() );
+ }
+ catch ( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ factory.getSQLExceptionConverter(),
+ sqle,
+ "could not perform sequential read of results (forward)",
+ getSQLString()
+ );
+ }
+
+ initializeEntitiesAndCollections(
+ hydratedObjects,
+ resultSet,
+ session,
+ queryParameters.isReadOnly()
+ );
+ session.getPersistenceContext().initializeNonLazyCollections();
+ return result;
+ }
+
+ /**
+ * Loads a single logical row from the result set moving forward. This is the
+ * processing used from the ScrollableResults where there were collection fetches
+ * encountered; thus a single logical row may have multiple rows in the underlying
+ * result set.
+ *
+ * @param resultSet The result set from which to do the load.
+ * @param session The session from which the request originated.
+ * @param queryParameters The query parameters specified by the user.
+ * @param returnProxies Should proxies be generated
+ * @return The loaded "row".
+ * @throws HibernateException
+ */
+ public Object loadSequentialRowsForward(
+ final ResultSet resultSet,
+ final SessionImplementor session,
+ final QueryParameters queryParameters,
+ final boolean returnProxies) throws HibernateException {
+
+ // note that for sequential scrolling, we make the assumption that
+ // the first persister element is the "root entity"
+
+ try {
+ if ( resultSet.isAfterLast() ) {
+ // don't even bother trying to read further
+ return null;
+ }
+
+ if ( resultSet.isBeforeFirst() ) {
+ resultSet.next();
+ }
+
+ // We call getKeyFromResultSet() here so that we can know the
+ // key value upon which to perform the breaking logic. However,
+ // it is also then called from getRowFromResultSet() which is certainly
+ // not the most efficient. But the call here is needed, and there
+ // currently is no other way without refactoring of the doQuery()/getRowFromResultSet()
+ // methods
+ final EntityKey currentKey = getKeyFromResultSet(
+ 0,
+ getEntityPersisters()[0],
+ null,
+ resultSet,
+ session
+ );
+
+ return sequentialLoad( resultSet, session, queryParameters, returnProxies, currentKey );
+ }
+ catch ( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ factory.getSQLExceptionConverter(),
+ sqle,
+ "could not perform sequential read of results (forward)",
+ getSQLString()
+ );
+ }
+ }
+
+ /**
+ * Loads a single logical row from the result set moving forward. This is the
+ * processing used from the ScrollableResults where there were collection fetches
+ * encountered; thus a single logical row may have multiple rows in the underlying
+ * result set.
+ *
+ * @param resultSet The result set from which to do the load.
+ * @param session The session from which the request originated.
+ * @param queryParameters The query parameters specified by the user.
+ * @param returnProxies Should proxies be generated
+ * @return The loaded "row".
+ * @throws HibernateException
+ */
+ public Object loadSequentialRowsReverse(
+ final ResultSet resultSet,
+ final SessionImplementor session,
+ final QueryParameters queryParameters,
+ final boolean returnProxies,
+ final boolean isLogicallyAfterLast) throws HibernateException {
+
+ // note that for sequential scrolling, we make the assumption that
+ // the first persister element is the "root entity"
+
+ try {
+ if ( resultSet.isFirst() ) {
+ // don't even bother trying to read any further
+ return null;
+ }
+
+ EntityKey keyToRead = null;
+ // This check is needed since processing leaves the cursor
+ // after the last physical row for the current logical row;
+ // thus if we are after the last physical row, this might be
+ // caused by either:
+ // 1) scrolling to the last logical row
+ // 2) scrolling past the last logical row
+ // In the latter scenario, the previous logical row
+ // really is the last logical row.
+ //
+ // In all other cases, we should process back two
+ // logical records (the current logic row, plus the
+ // previous logical row).
+ if ( resultSet.isAfterLast() && isLogicallyAfterLast ) {
+ // position cursor to the last row
+ resultSet.last();
+ keyToRead = getKeyFromResultSet(
+ 0,
+ getEntityPersisters()[0],
+ null,
+ resultSet,
+ session
+ );
+ }
+ else {
+ // Since the result set cursor is always left at the first
+ // physical row after the "last processed", we need to jump
+ // back one position to get the key value we are interested
+ // in skipping
+ resultSet.previous();
+
+ // sequentially read the result set in reverse until we recognize
+ // a change in the key value. At that point, we are pointed at
+ // the last physical sequential row for the logical row in which
+ // we are interested in processing
+ boolean firstPass = true;
+ final EntityKey lastKey = getKeyFromResultSet(
+ 0,
+ getEntityPersisters()[0],
+ null,
+ resultSet,
+ session
+ );
+ while ( resultSet.previous() ) {
+ EntityKey checkKey = getKeyFromResultSet(
+ 0,
+ getEntityPersisters()[0],
+ null,
+ resultSet,
+ session
+ );
+
+ if ( firstPass ) {
+ firstPass = false;
+ keyToRead = checkKey;
+ }
+
+ if ( !lastKey.equals( checkKey ) ) {
+ break;
+ }
+ }
+
+ }
+
+ // Read backwards until we read past the first physical sequential
+ // row with the key we are interested in loading
+ while ( resultSet.previous() ) {
+ EntityKey checkKey = getKeyFromResultSet(
+ 0,
+ getEntityPersisters()[0],
+ null,
+ resultSet,
+ session
+ );
+
+ if ( !keyToRead.equals( checkKey ) ) {
+ break;
+ }
+ }
+
+ // Finally, read ahead one row to position result set cursor
+ // at the first physical row we are interested in loading
+ resultSet.next();
+
+ // and perform the load
+ return sequentialLoad( resultSet, session, queryParameters, returnProxies, keyToRead );
+ }
+ catch ( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ factory.getSQLExceptionConverter(),
+ sqle,
+ "could not perform sequential read of results (forward)",
+ getSQLString()
+ );
+ }
+ }
+
+ private static EntityKey getOptionalObjectKey(QueryParameters queryParameters, SessionImplementor session) {
+ final Object optionalObject = queryParameters.getOptionalObject();
+ final Serializable optionalId = queryParameters.getOptionalId();
+ final String optionalEntityName = queryParameters.getOptionalEntityName();
+
+ if ( optionalObject != null && optionalEntityName != null ) {
+ return new EntityKey(
+ optionalId,
+ session.getEntityPersister( optionalEntityName, optionalObject ),
+ session.getEntityMode()
+ );
+ }
+ else {
+ return null;
+ }
+
+ }
+
+ private Object getRowFromResultSet(
+ final ResultSet resultSet,
+ final SessionImplementor session,
+ final QueryParameters queryParameters,
+ final LockMode[] lockModeArray,
+ final EntityKey optionalObjectKey,
+ final List hydratedObjects,
+ final EntityKey[] keys,
+ boolean returnProxies) throws SQLException, HibernateException {
+
+ final Loadable[] persisters = getEntityPersisters();
+ final int entitySpan = persisters.length;
+
+ for ( int i = 0; i < entitySpan; i++ ) {
+ keys[i] = getKeyFromResultSet(
+ i,
+ persisters[i],
+ i == entitySpan - 1 ?
+ queryParameters.getOptionalId() :
+ null,
+ resultSet,
+ session
+ );
+ //TODO: the i==entitySpan-1 bit depends upon subclass implementation (very bad)
+ }
+
+ registerNonExists( keys, persisters, session );
+
+ // this call is side-effecty
+ Object[] row = getRow(
+ resultSet,
+ persisters,
+ keys,
+ queryParameters.getOptionalObject(),
+ optionalObjectKey,
+ lockModeArray,
+ hydratedObjects,
+ session
+ );
+
+ readCollectionElements( row, resultSet, session );
+
+ if ( returnProxies ) {
+ // now get an existing proxy for each row element (if there is one)
+ for ( int i = 0; i < entitySpan; i++ ) {
+ Object entity = row[i];
+ Object proxy = session.getPersistenceContext().proxyFor( persisters[i], keys[i], entity );
+ if ( entity != proxy ) {
+ // force the proxy to resolve itself
+ ( (HibernateProxy) proxy ).getHibernateLazyInitializer().setImplementation(entity);
+ row[i] = proxy;
+ }
+ }
+ }
+
+ return getResultColumnOrRow( row, queryParameters.getResultTransformer(), resultSet, session );
+
+ }
+
+ /**
+ * Read any collection elements contained in a single row of the result set
+ */
+ private void readCollectionElements(Object[] row, ResultSet resultSet, SessionImplementor session)
+ throws SQLException, HibernateException {
+
+ //TODO: make this handle multiple collection roles!
+
+ final CollectionPersister[] collectionPersisters = getCollectionPersisters();
+ if ( collectionPersisters != null ) {
+
+ final CollectionAliases[] descriptors = getCollectionAliases();
+ final int[] collectionOwners = getCollectionOwners();
+
+ for ( int i=0; i<collectionPersisters.length; i++ ) {
+
+ final boolean hasCollectionOwners = collectionOwners !=null &&
+ collectionOwners[i] > -1;
+ //true if this is a query and we are loading multiple instances of the same collection role
+ //otherwise this is a CollectionInitializer and we are loading up a single collection or batch
+
+ final Object owner = hasCollectionOwners ?
+ row[ collectionOwners[i] ] :
+ null; //if null, owner will be retrieved from session
+
+ final CollectionPersister collectionPersister = collectionPersisters[i];
+ final Serializable key;
+ if ( owner == null ) {
+ key = null;
+ }
+ else {
+ key = collectionPersister.getCollectionType().getKeyOfOwner( owner, session );
+ //TODO: old version did not require hashmap lookup:
+ //keys[collectionOwner].getIdentifier()
+ }
+
+ readCollectionElement(
+ owner,
+ key,
+ collectionPersister,
+ descriptors[i],
+ resultSet,
+ session
+ );
+
+ }
+
+ }
+ }
+
+ private List doQuery(
+ final SessionImplementor session,
+ final QueryParameters queryParameters,
+ final boolean returnProxies) throws SQLException, HibernateException {
+
+ final RowSelection selection = queryParameters.getRowSelection();
+ final int maxRows = hasMaxRows( selection ) ?
+ selection.getMaxRows().intValue() :
+ Integer.MAX_VALUE;
+
+ final int entitySpan = getEntityPersisters().length;
+
+ final ArrayList hydratedObjects = entitySpan == 0 ? null : new ArrayList( entitySpan * 10 );
+ final PreparedStatement st = prepareQueryStatement( queryParameters, false, session );
+ final ResultSet rs = getResultSet( st, queryParameters.hasAutoDiscoverScalarTypes(), queryParameters.isCallable(), selection, session );
+
+// would be great to move all this below here into another method that could also be used
+// from the new scrolling stuff.
+//
+// Would need to change the way the max-row stuff is handled (i.e. behind an interface) so
+// that I could do the control breaking at the means to know when to stop
+ final LockMode[] lockModeArray = getLockModes( queryParameters.getLockModes() );
+ final EntityKey optionalObjectKey = getOptionalObjectKey( queryParameters, session );
+
+ final boolean createSubselects = isSubselectLoadingEnabled();
+ final List subselectResultKeys = createSubselects ? new ArrayList() : null;
+ final List results = new ArrayList();
+
+ try {
+
+ handleEmptyCollections( queryParameters.getCollectionKeys(), rs, session );
+
+ EntityKey[] keys = new EntityKey[entitySpan]; //we can reuse it for each row
+
+ if ( log.isTraceEnabled() ) log.trace( "processing result set" );
+
+ int count;
+ for ( count = 0; count < maxRows && rs.next(); count++ ) {
+
+ if ( log.isTraceEnabled() ) log.debug("result set row: " + count);
+
+ Object result = getRowFromResultSet(
+ rs,
+ session,
+ queryParameters,
+ lockModeArray,
+ optionalObjectKey,
+ hydratedObjects,
+ keys,
+ returnProxies
+ );
+ results.add( result );
+
+ if ( createSubselects ) {
+ subselectResultKeys.add(keys);
+ keys = new EntityKey[entitySpan]; //can't reuse in this case
+ }
+
+ }
+
+ if ( log.isTraceEnabled() ) {
+ log.trace( "done processing result set (" + count + " rows)" );
+ }
+
+ }
+ finally {
+ session.getBatcher().closeQueryStatement( st, rs );
+ }
+
+ initializeEntitiesAndCollections( hydratedObjects, rs, session, queryParameters.isReadOnly() );
+
+ if ( createSubselects ) createSubselects( subselectResultKeys, queryParameters, session );
+
+ return results; //getResultList(results);
+
+ }
+
+ protected boolean isSubselectLoadingEnabled() {
+ return false;
+ }
+
+ protected boolean hasSubselectLoadableCollections() {
+ final Loadable[] loadables = getEntityPersisters();
+ for (int i=0; i<loadables.length; i++ ) {
+ if ( loadables[i].hasSubselectLoadableCollections() ) return true;
+ }
+ return false;
+ }
+
+ private static Set[] transpose( List keys ) {
+ Set[] result = new Set[ ( ( EntityKey[] ) keys.get(0) ).length ];
+ for ( int j=0; j<result.length; j++ ) {
+ result[j] = new HashSet( keys.size() );
+ for ( int i=0; i<keys.size(); i++ ) {
+ result[j].add( ( ( EntityKey[] ) keys.get(i) ) [j] );
+ }
+ }
+ return result;
+ }
+
+ private void createSubselects(List keys, QueryParameters queryParameters, SessionImplementor session) {
+ if ( keys.size() > 1 ) { //if we only returned one entity, query by key is more efficient
+
+ Set[] keySets = transpose(keys);
+
+ Map namedParameterLocMap = buildNamedParameterLocMap( queryParameters );
+
+ final Loadable[] loadables = getEntityPersisters();
+ final String[] aliases = getAliases();
+ final Iterator iter = keys.iterator();
+ while ( iter.hasNext() ) {
+
+ final EntityKey[] rowKeys = (EntityKey[]) iter.next();
+ for ( int i=0; i<rowKeys.length; i++ ) {
+
+ if ( rowKeys[i]!=null && loadables[i].hasSubselectLoadableCollections() ) {
+
+ SubselectFetch subselectFetch = new SubselectFetch(
+ //getSQLString(),
+ aliases[i],
+ loadables[i],
+ queryParameters,
+ keySets[i],
+ namedParameterLocMap
+ );
+
+ session.getPersistenceContext()
+ .getBatchFetchQueue()
+ .addSubselect( rowKeys[i], subselectFetch );
+ }
+
+ }
+
+ }
+ }
+ }
+
+ private Map buildNamedParameterLocMap(QueryParameters queryParameters) {
+ if ( queryParameters.getNamedParameters()!=null ) {
+ final Map namedParameterLocMap = new HashMap();
+ Iterator piter = queryParameters.getNamedParameters().keySet().iterator();
+ while ( piter.hasNext() ) {
+ String name = (String) piter.next();
+ namedParameterLocMap.put(
+ name,
+ getNamedParameterLocs(name)
+ );
+ }
+ return namedParameterLocMap;
+ }
+ else {
+ return null;
+ }
+ }
+
+ private void initializeEntitiesAndCollections(
+ final List hydratedObjects,
+ final Object resultSetId,
+ final SessionImplementor session,
+ final boolean readOnly)
+ throws HibernateException {
+
+ final CollectionPersister[] collectionPersisters = getCollectionPersisters();
+ if ( collectionPersisters != null ) {
+ for ( int i=0; i<collectionPersisters.length; i++ ) {
+ if ( collectionPersisters[i].isArray() ) {
+ //for arrays, we should end the collection load before resolving
+ //the entities, since the actual array instances are not instantiated
+ //during loading
+ //TODO: or we could do this polymorphically, and have two
+ // different operations implemented differently for arrays
+ endCollectionLoad( resultSetId, session, collectionPersisters[i] );
+ }
+ }
+ }
+
+ //important: reuse the same event instances for performance!
+ final PreLoadEvent pre;
+ final PostLoadEvent post;
+ if ( session.isEventSource() ) {
+ pre = new PreLoadEvent( (EventSource) session );
+ post = new PostLoadEvent( (EventSource) session );
+ }
+ else {
+ pre = null;
+ post = null;
+ }
+
+ if ( hydratedObjects!=null ) {
+ int hydratedObjectsSize = hydratedObjects.size();
+ if ( log.isTraceEnabled() ) {
+ log.trace( "total objects hydrated: " + hydratedObjectsSize );
+ }
+ for ( int i = 0; i < hydratedObjectsSize; i++ ) {
+ TwoPhaseLoad.initializeEntity( hydratedObjects.get(i), readOnly, session, pre, post );
+ }
+ }
+
+ if ( collectionPersisters != null ) {
+ for ( int i=0; i<collectionPersisters.length; i++ ) {
+ if ( !collectionPersisters[i].isArray() ) {
+ //for sets, we should end the collection load after resolving
+ //the entities, since we might call hashCode() on the elements
+ //TODO: or we could do this polymorphically, and have two
+ // different operations implemented differently for arrays
+ endCollectionLoad( resultSetId, session, collectionPersisters[i] );
+ }
+ }
+ }
+
+ }
+
+ private void endCollectionLoad(
+ final Object resultSetId,
+ final SessionImplementor session,
+ final CollectionPersister collectionPersister) {
+ //this is a query and we are loading multiple instances of the same collection role
+ session.getPersistenceContext()
+ .getLoadContexts()
+ .getCollectionLoadContext( ( ResultSet ) resultSetId )
+ .endLoadingCollections( collectionPersister );
+ }
+
+ protected List getResultList(List results, ResultTransformer resultTransformer) throws QueryException {
+ return results;
+ }
+
+ /**
+ * Get the actual object that is returned in the user-visible result list.
+ * This empty implementation merely returns its first argument. This is
+ * overridden by some subclasses.
+ */
+ protected Object getResultColumnOrRow(Object[] row, ResultTransformer transformer, ResultSet rs, SessionImplementor session)
+ throws SQLException, HibernateException {
+ return row;
+ }
+
+ /**
+ * For missing objects associated by one-to-one with another object in the
+ * result set, register the fact that the the object is missing with the
+ * session.
+ */
+ private void registerNonExists(
+ final EntityKey[] keys,
+ final Loadable[] persisters,
+ final SessionImplementor session) {
+
+ final int[] owners = getOwners();
+ if ( owners != null ) {
+
+ EntityType[] ownerAssociationTypes = getOwnerAssociationTypes();
+ for ( int i = 0; i < keys.length; i++ ) {
+
+ int owner = owners[i];
+ if ( owner > -1 ) {
+ EntityKey ownerKey = keys[owner];
+ if ( keys[i] == null && ownerKey != null ) {
+
+ final PersistenceContext persistenceContext = session.getPersistenceContext();
+
+ /*final boolean isPrimaryKey;
+ final boolean isSpecialOneToOne;
+ if ( ownerAssociationTypes == null || ownerAssociationTypes[i] == null ) {
+ isPrimaryKey = true;
+ isSpecialOneToOne = false;
+ }
+ else {
+ isPrimaryKey = ownerAssociationTypes[i].getRHSUniqueKeyPropertyName()==null;
+ isSpecialOneToOne = ownerAssociationTypes[i].getLHSPropertyName()!=null;
+ }*/
+
+ //TODO: can we *always* use the "null property" approach for everything?
+ /*if ( isPrimaryKey && !isSpecialOneToOne ) {
+ persistenceContext.addNonExistantEntityKey(
+ new EntityKey( ownerKey.getIdentifier(), persisters[i], session.getEntityMode() )
+ );
+ }
+ else if ( isSpecialOneToOne ) {*/
+ boolean isOneToOneAssociation = ownerAssociationTypes!=null &&
+ ownerAssociationTypes[i]!=null &&
+ ownerAssociationTypes[i].isOneToOne();
+ if ( isOneToOneAssociation ) {
+ persistenceContext.addNullProperty( ownerKey,
+ ownerAssociationTypes[i].getPropertyName() );
+ }
+ /*}
+ else {
+ persistenceContext.addNonExistantEntityUniqueKey( new EntityUniqueKey(
+ persisters[i].getEntityName(),
+ ownerAssociationTypes[i].getRHSUniqueKeyPropertyName(),
+ ownerKey.getIdentifier(),
+ persisters[owner].getIdentifierType(),
+ session.getEntityMode()
+ ) );
+ }*/
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Read one collection element from the current row of the JDBC result set
+ */
+ private void readCollectionElement(
+ final Object optionalOwner,
+ final Serializable optionalKey,
+ final CollectionPersister persister,
+ final CollectionAliases descriptor,
+ final ResultSet rs,
+ final SessionImplementor session)
+ throws HibernateException, SQLException {
+
+ final PersistenceContext persistenceContext = session.getPersistenceContext();
+
+ final Serializable collectionRowKey = (Serializable) persister.readKey(
+ rs,
+ descriptor.getSuffixedKeyAliases(),
+ session
+ );
+
+ if ( collectionRowKey != null ) {
+ // we found a collection element in the result set
+
+ if ( log.isDebugEnabled() ) {
+ log.debug(
+ "found row of collection: " +
+ MessageHelper.collectionInfoString( persister, collectionRowKey, getFactory() )
+ );
+ }
+
+ Object owner = optionalOwner;
+ if ( owner == null ) {
+ owner = persistenceContext.getCollectionOwner( collectionRowKey, persister );
+ if ( owner == null ) {
+ //TODO: This is assertion is disabled because there is a bug that means the
+ // original owner of a transient, uninitialized collection is not known
+ // if the collection is re-referenced by a different object associated
+ // with the current Session
+ //throw new AssertionFailure("bug loading unowned collection");
+ }
+ }
+
+ PersistentCollection rowCollection = persistenceContext.getLoadContexts()
+ .getCollectionLoadContext( rs )
+ .getLoadingCollection( persister, collectionRowKey );
+
+ if ( rowCollection != null ) {
+ rowCollection.readFrom( rs, persister, descriptor, owner );
+ }
+
+ }
+ else if ( optionalKey != null ) {
+ // we did not find a collection element in the result set, so we
+ // ensure that a collection is created with the owner's identifier,
+ // since what we have is an empty collection
+
+ if ( log.isDebugEnabled() ) {
+ log.debug(
+ "result set contains (possibly empty) collection: " +
+ MessageHelper.collectionInfoString( persister, optionalKey, getFactory() )
+ );
+ }
+
+ persistenceContext.getLoadContexts()
+ .getCollectionLoadContext( rs )
+ .getLoadingCollection( persister, optionalKey ); // handle empty collection
+
+ }
+
+ // else no collection element, but also no owner
+
+ }
+
+ /**
+ * If this is a collection initializer, we need to tell the session that a collection
+ * is being initilized, to account for the possibility of the collection having
+ * no elements (hence no rows in the result set).
+ */
+ private void handleEmptyCollections(
+ final Serializable[] keys,
+ final Object resultSetId,
+ final SessionImplementor session) {
+
+ if ( keys != null ) {
+ // this is a collection initializer, so we must create a collection
+ // for each of the passed-in keys, to account for the possibility
+ // that the collection is empty and has no rows in the result set
+
+ CollectionPersister[] collectionPersisters = getCollectionPersisters();
+ for ( int j=0; j<collectionPersisters.length; j++ ) {
+ for ( int i = 0; i < keys.length; i++ ) {
+ //handle empty collections
+
+ if ( log.isDebugEnabled() ) {
+ log.debug(
+ "result set contains (possibly empty) collection: " +
+ MessageHelper.collectionInfoString( collectionPersisters[j], keys[i], getFactory() )
+ );
+ }
+
+ session.getPersistenceContext()
+ .getLoadContexts()
+ .getCollectionLoadContext( ( ResultSet ) resultSetId )
+ .getLoadingCollection( collectionPersisters[j], keys[i] );
+ }
+ }
+ }
+
+ // else this is not a collection initializer (and empty collections will
+ // be detected by looking for the owner's identifier in the result set)
+ }
+
+ /**
+ * Read a row of <tt>Key</tt>s from the <tt>ResultSet</tt> into the given array.
+ * Warning: this method is side-effecty.
+ * <p/>
+ * If an <tt>id</tt> is given, don't bother going to the <tt>ResultSet</tt>.
+ */
+ private EntityKey getKeyFromResultSet(
+ final int i,
+ final Loadable persister,
+ final Serializable id,
+ final ResultSet rs,
+ final SessionImplementor session) throws HibernateException, SQLException {
+
+ Serializable resultId;
+
+ // if we know there is exactly 1 row, we can skip.
+ // it would be great if we could _always_ skip this;
+ // it is a problem for <key-many-to-one>
+
+ if ( isSingleRowLoader() && id != null ) {
+ resultId = id;
+ }
+ else {
+
+ Type idType = persister.getIdentifierType();
+ resultId = (Serializable) idType.nullSafeGet(
+ rs,
+ getEntityAliases()[i].getSuffixedKeyAliases(),
+ session,
+ null //problematic for <key-many-to-one>!
+ );
+
+ final boolean idIsResultId = id != null &&
+ resultId != null &&
+ idType.isEqual( id, resultId, session.getEntityMode(), factory );
+
+ if ( idIsResultId ) resultId = id; //use the id passed in
+ }
+
+ return resultId == null ?
+ null :
+ new EntityKey( resultId, persister, session.getEntityMode() );
+ }
+
+ /**
+ * Check the version of the object in the <tt>ResultSet</tt> against
+ * the object version in the session cache, throwing an exception
+ * if the version numbers are different
+ */
+ private void checkVersion(
+ final int i,
+ final Loadable persister,
+ final Serializable id,
+ final Object entity,
+ final ResultSet rs,
+ final SessionImplementor session)
+ throws HibernateException, SQLException {
+
+ Object version = session.getPersistenceContext().getEntry( entity ).getVersion();
+
+ if ( version != null ) { //null version means the object is in the process of being loaded somewhere else in the ResultSet
+ VersionType versionType = persister.getVersionType();
+ Object currentVersion = versionType.nullSafeGet(
+ rs,
+ getEntityAliases()[i].getSuffixedVersionAliases(),
+ session,
+ null
+ );
+ if ( !versionType.isEqual(version, currentVersion) ) {
+ if ( session.getFactory().getStatistics().isStatisticsEnabled() ) {
+ session.getFactory().getStatisticsImplementor()
+ .optimisticFailure( persister.getEntityName() );
+ }
+ throw new StaleObjectStateException( persister.getEntityName(), id );
+ }
+ }
+
+ }
+
+ /**
+ * Resolve any ids for currently loaded objects, duplications within the
+ * <tt>ResultSet</tt>, etc. Instantiate empty objects to be initialized from the
+ * <tt>ResultSet</tt>. Return an array of objects (a row of results) and an
+ * array of booleans (by side-effect) that determine whether the corresponding
+ * object should be initialized.
+ */
+ private Object[] getRow(
+ final ResultSet rs,
+ final Loadable[] persisters,
+ final EntityKey[] keys,
+ final Object optionalObject,
+ final EntityKey optionalObjectKey,
+ final LockMode[] lockModes,
+ final List hydratedObjects,
+ final SessionImplementor session)
+ throws HibernateException, SQLException {
+
+ final int cols = persisters.length;
+ final EntityAliases[] descriptors = getEntityAliases();
+
+ if ( log.isDebugEnabled() ) {
+ log.debug(
+ "result row: " +
+ StringHelper.toString( keys )
+ );
+ }
+
+ final Object[] rowResults = new Object[cols];
+
+ for ( int i = 0; i < cols; i++ ) {
+
+ Object object = null;
+ EntityKey key = keys[i];
+
+ if ( keys[i] == null ) {
+ //do nothing
+ }
+ else {
+
+ //If the object is already loaded, return the loaded one
+ object = session.getEntityUsingInterceptor( key );
+ if ( object != null ) {
+ //its already loaded so don't need to hydrate it
+ instanceAlreadyLoaded(
+ rs,
+ i,
+ persisters[i],
+ key,
+ object,
+ lockModes[i],
+ session
+ );
+ }
+ else {
+ object = instanceNotYetLoaded(
+ rs,
+ i,
+ persisters[i],
+ descriptors[i].getRowIdAlias(),
+ key,
+ lockModes[i],
+ optionalObjectKey,
+ optionalObject,
+ hydratedObjects,
+ session
+ );
+ }
+
+ }
+
+ rowResults[i] = object;
+
+ }
+
+ return rowResults;
+ }
+
+ /**
+ * The entity instance is already in the session cache
+ */
+ private void instanceAlreadyLoaded(
+ final ResultSet rs,
+ final int i,
+ final Loadable persister,
+ final EntityKey key,
+ final Object object,
+ final LockMode lockMode,
+ final SessionImplementor session)
+ throws HibernateException, SQLException {
+
+ if ( !persister.isInstance( object, session.getEntityMode() ) ) {
+ throw new WrongClassException(
+ "loaded object was of wrong class " + object.getClass(),
+ key.getIdentifier(),
+ persister.getEntityName()
+ );
+ }
+
+ if ( LockMode.NONE != lockMode && upgradeLocks() ) { //no point doing this if NONE was requested
+
+ final boolean isVersionCheckNeeded = persister.isVersioned() &&
+ session.getPersistenceContext().getEntry(object)
+ .getLockMode().lessThan( lockMode );
+ // we don't need to worry about existing version being uninitialized
+ // because this block isn't called by a re-entrant load (re-entrant
+ // loads _always_ have lock mode NONE)
+ if (isVersionCheckNeeded) {
+ //we only check the version when _upgrading_ lock modes
+ checkVersion( i, persister, key.getIdentifier(), object, rs, session );
+ //we need to upgrade the lock mode to the mode requested
+ session.getPersistenceContext().getEntry(object)
+ .setLockMode(lockMode);
+ }
+ }
+ }
+
+ /**
+ * The entity instance is not in the session cache
+ */
+ private Object instanceNotYetLoaded(
+ final ResultSet rs,
+ final int i,
+ final Loadable persister,
+ final String rowIdAlias,
+ final EntityKey key,
+ final LockMode lockMode,
+ final EntityKey optionalObjectKey,
+ final Object optionalObject,
+ final List hydratedObjects,
+ final SessionImplementor session)
+ throws HibernateException, SQLException {
+
+ final String instanceClass = getInstanceClass(
+ rs,
+ i,
+ persister,
+ key.getIdentifier(),
+ session
+ );
+
+ final Object object;
+ if ( optionalObjectKey != null && key.equals( optionalObjectKey ) ) {
+ //its the given optional object
+ object = optionalObject;
+ }
+ else {
+ // instantiate a new instance
+ object = session.instantiate( instanceClass, key.getIdentifier() );
+ }
+
+ //need to hydrate it.
+
+ // grab its state from the ResultSet and keep it in the Session
+ // (but don't yet initialize the object itself)
+ // note that we acquire LockMode.READ even if it was not requested
+ LockMode acquiredLockMode = lockMode == LockMode.NONE ? LockMode.READ : lockMode;
+ loadFromResultSet(
+ rs,
+ i,
+ object,
+ instanceClass,
+ key,
+ rowIdAlias,
+ acquiredLockMode,
+ persister,
+ session
+ );
+
+ //materialize associations (and initialize the object) later
+ hydratedObjects.add( object );
+
+ return object;
+ }
+
+ private boolean isEagerPropertyFetchEnabled(int i) {
+ boolean[] array = getEntityEagerPropertyFetches();
+ return array!=null && array[i];
+ }
+
+
+ /**
+ * Hydrate the state an object from the SQL <tt>ResultSet</tt>, into
+ * an array or "hydrated" values (do not resolve associations yet),
+ * and pass the hydrates state to the session.
+ */
+ private void loadFromResultSet(
+ final ResultSet rs,
+ final int i,
+ final Object object,
+ final String instanceEntityName,
+ final EntityKey key,
+ final String rowIdAlias,
+ final LockMode lockMode,
+ final Loadable rootPersister,
+ final SessionImplementor session)
+ throws SQLException, HibernateException {
+
+ final Serializable id = key.getIdentifier();
+
+ // Get the persister for the _subclass_
+ final Loadable persister = (Loadable) getFactory().getEntityPersister( instanceEntityName );
+
+ if ( log.isTraceEnabled() ) {
+ log.trace(
+ "Initializing object from ResultSet: " +
+ MessageHelper.infoString( persister, id, getFactory() )
+ );
+ }
+
+ boolean eagerPropertyFetch = isEagerPropertyFetchEnabled(i);
+
+ // add temp entry so that the next step is circular-reference
+ // safe - only needed because some types don't take proper
+ // advantage of two-phase-load (esp. components)
+ TwoPhaseLoad.addUninitializedEntity(
+ key,
+ object,
+ persister,
+ lockMode,
+ !eagerPropertyFetch,
+ session
+ );
+
+ //This is not very nice (and quite slow):
+ final String[][] cols = persister == rootPersister ?
+ getEntityAliases()[i].getSuffixedPropertyAliases() :
+ getEntityAliases()[i].getSuffixedPropertyAliases(persister);
+
+ final Object[] values = persister.hydrate(
+ rs,
+ id,
+ object,
+ rootPersister,
+ cols,
+ eagerPropertyFetch,
+ session
+ );
+
+ final Object rowId = persister.hasRowId() ? rs.getObject(rowIdAlias) : null;
+
+ final AssociationType[] ownerAssociationTypes = getOwnerAssociationTypes();
+ if ( ownerAssociationTypes != null && ownerAssociationTypes[i] != null ) {
+ String ukName = ownerAssociationTypes[i].getRHSUniqueKeyPropertyName();
+ if (ukName!=null) {
+ final int index = ( (UniqueKeyLoadable) persister ).getPropertyIndex(ukName);
+ final Type type = persister.getPropertyTypes()[index];
+
+ // polymorphism not really handled completely correctly,
+ // perhaps...well, actually its ok, assuming that the
+ // entity name used in the lookup is the same as the
+ // the one used here, which it will be
+
+ EntityUniqueKey euk = new EntityUniqueKey(
+ rootPersister.getEntityName(), //polymorphism comment above
+ ukName,
+ type.semiResolve( values[index], session, object ),
+ type,
+ session.getEntityMode(), session.getFactory()
+ );
+ session.getPersistenceContext().addEntity( euk, object );
+ }
+ }
+
+ TwoPhaseLoad.postHydrate(
+ persister,
+ id,
+ values,
+ rowId,
+ object,
+ lockMode,
+ !eagerPropertyFetch,
+ session
+ );
+
+ }
+
+ /**
+ * Determine the concrete class of an instance in the <tt>ResultSet</tt>
+ */
+ private String getInstanceClass(
+ final ResultSet rs,
+ final int i,
+ final Loadable persister,
+ final Serializable id,
+ final SessionImplementor session)
+ throws HibernateException, SQLException {
+
+ if ( persister.hasSubclasses() ) {
+
+ // Code to handle subclasses of topClass
+ Object discriminatorValue = persister.getDiscriminatorType().nullSafeGet(
+ rs,
+ getEntityAliases()[i].getSuffixedDiscriminatorAlias(),
+ session,
+ null
+ );
+
+ final String result = persister.getSubclassForDiscriminatorValue( discriminatorValue );
+
+ if ( result == null ) {
+ //woops we got an instance of another class hierarchy branch
+ throw new WrongClassException(
+ "Discriminator: " + discriminatorValue,
+ id,
+ persister.getEntityName()
+ );
+ }
+
+ return result;
+
+ }
+ else {
+ return persister.getEntityName();
+ }
+ }
+
+ /**
+ * Advance the cursor to the first required row of the <tt>ResultSet</tt>
+ */
+ private void advance(final ResultSet rs, final RowSelection selection)
+ throws SQLException {
+
+ final int firstRow = getFirstRow( selection );
+ if ( firstRow != 0 ) {
+ if ( getFactory().getSettings().isScrollableResultSetsEnabled() ) {
+ // we can go straight to the first required row
+ rs.absolute( firstRow );
+ }
+ else {
+ // we need to step through the rows one row at a time (slow)
+ for ( int m = 0; m < firstRow; m++ ) rs.next();
+ }
+ }
+ }
+
+ private static boolean hasMaxRows(RowSelection selection) {
+ return selection != null && selection.getMaxRows() != null;
+ }
+
+ private static int getFirstRow(RowSelection selection) {
+ if ( selection == null || selection.getFirstRow() == null ) {
+ return 0;
+ }
+ else {
+ return selection.getFirstRow().intValue();
+ }
+ }
+
+ /**
+ * Should we pre-process the SQL string, adding a dialect-specific
+ * LIMIT clause.
+ */
+ private static boolean useLimit(final RowSelection selection, final Dialect dialect) {
+ return dialect.supportsLimit() && hasMaxRows( selection );
+ }
+
+ /**
+ * Obtain a <tt>PreparedStatement</tt> with all parameters pre-bound.
+ * Bind JDBC-style <tt>?</tt> parameters, named parameters, and
+ * limit parameters.
+ */
+ protected final PreparedStatement prepareQueryStatement(
+ final QueryParameters queryParameters,
+ final boolean scroll,
+ final SessionImplementor session) throws SQLException, HibernateException {
+
+ queryParameters.processFilters( getSQLString(), session );
+ String sql = queryParameters.getFilteredSQL();
+ final Dialect dialect = getFactory().getDialect();
+ final RowSelection selection = queryParameters.getRowSelection();
+ boolean useLimit = useLimit( selection, dialect );
+ boolean hasFirstRow = getFirstRow( selection ) > 0;
+ boolean useOffset = hasFirstRow && useLimit && dialect.supportsLimitOffset();
+ boolean callable = queryParameters.isCallable();
+
+ boolean useScrollableResultSetToSkip = hasFirstRow &&
+ !useOffset &&
+ getFactory().getSettings().isScrollableResultSetsEnabled();
+ ScrollMode scrollMode = scroll ? queryParameters.getScrollMode() : ScrollMode.SCROLL_INSENSITIVE;
+
+ if ( useLimit ) {
+ sql = dialect.getLimitString(
+ sql.trim(), //use of trim() here is ugly?
+ useOffset ? getFirstRow(selection) : 0,
+ getMaxOrLimit(selection, dialect)
+ );
+ }
+
+ sql = preprocessSQL( sql, queryParameters, dialect );
+
+ PreparedStatement st = null;
+
+ if (callable) {
+ st = session.getBatcher()
+ .prepareCallableQueryStatement( sql, scroll || useScrollableResultSetToSkip, scrollMode );
+ }
+ else {
+ st = session.getBatcher()
+ .prepareQueryStatement( sql, scroll || useScrollableResultSetToSkip, scrollMode );
+ }
+
+
+ try {
+
+ int col = 1;
+ //TODO: can we limit stored procedures ?!
+ if ( useLimit && dialect.bindLimitParametersFirst() ) {
+ col += bindLimitParameters( st, col, selection );
+ }
+ if (callable) {
+ col = dialect.registerResultSetOutParameter( (CallableStatement)st, col );
+ }
+
+ col += bindParameterValues( st, queryParameters, col, session );
+
+ if ( useLimit && !dialect.bindLimitParametersFirst() ) {
+ col += bindLimitParameters( st, col, selection );
+ }
+
+ if ( !useLimit ) {
+ setMaxRows( st, selection );
+ }
+
+ if ( selection != null ) {
+ if ( selection.getTimeout() != null ) {
+ st.setQueryTimeout( selection.getTimeout().intValue() );
+ }
+ if ( selection.getFetchSize() != null ) {
+ st.setFetchSize( selection.getFetchSize().intValue() );
+ }
+ }
+ }
+ catch ( SQLException sqle ) {
+ session.getBatcher().closeQueryStatement( st, null );
+ throw sqle;
+ }
+ catch ( HibernateException he ) {
+ session.getBatcher().closeQueryStatement( st, null );
+ throw he;
+ }
+
+ return st;
+ }
+
+ /**
+ * Some dialect-specific LIMIT clauses require the maximium last row number
+ * (aka, first_row_number + total_row_count), while others require the maximum
+ * returned row count (the total maximum number of rows to return).
+ *
+ * @param selection The selection criteria
+ * @param dialect The dialect
+ * @return The appropriate value to bind into the limit clause.
+ */
+ private static int getMaxOrLimit(final RowSelection selection, final Dialect dialect) {
+ final int firstRow = getFirstRow( selection );
+ final int lastRow = selection.getMaxRows().intValue();
+ if ( dialect.useMaxForLimit() ) {
+ return lastRow + firstRow;
+ }
+ else {
+ return lastRow;
+ }
+ }
+
+ /**
+ * Bind parameter values needed by the dialect-specific LIMIT clause.
+ *
+ * @param statement The statement to which to bind limit param values.
+ * @param index The bind position from which to start binding
+ * @param selection The selection object containing the limit information.
+ * @return The number of parameter values bound.
+ * @throws java.sql.SQLException Indicates problems binding parameter values.
+ */
+ private int bindLimitParameters(
+ final PreparedStatement statement,
+ final int index,
+ final RowSelection selection) throws SQLException {
+ Dialect dialect = getFactory().getDialect();
+ if ( !dialect.supportsVariableLimit() ) {
+ return 0;
+ }
+ if ( !hasMaxRows( selection ) ) {
+ throw new AssertionFailure( "no max results set" );
+ }
+ int firstRow = getFirstRow( selection );
+ int lastRow = getMaxOrLimit( selection, dialect );
+ boolean hasFirstRow = firstRow > 0 && dialect.supportsLimitOffset();
+ boolean reverse = dialect.bindLimitParametersInReverseOrder();
+ if ( hasFirstRow ) {
+ statement.setInt( index + ( reverse ? 1 : 0 ), firstRow );
+ }
+ statement.setInt( index + ( reverse || !hasFirstRow ? 0 : 1 ), lastRow );
+ return hasFirstRow ? 2 : 1;
+ }
+
+ /**
+ * Use JDBC API to limit the number of rows returned by the SQL query if necessary
+ */
+ private void setMaxRows(
+ final PreparedStatement st,
+ final RowSelection selection) throws SQLException {
+ if ( hasMaxRows( selection ) ) {
+ st.setMaxRows( selection.getMaxRows().intValue() + getFirstRow( selection ) );
+ }
+ }
+
+ /**
+ * Bind all parameter values into the prepared statement in preparation
+ * for execution.
+ *
+ * @param statement The JDBC prepared statement
+ * @param queryParameters The encapsulation of the parameter values to be bound.
+ * @param startIndex The position from which to start binding parameter values.
+ * @param session The originating session.
+ * @return The number of JDBC bind positions actually bound during this method execution.
+ * @throws SQLException Indicates problems performing the binding.
+ */
+ protected int bindParameterValues(
+ PreparedStatement statement,
+ QueryParameters queryParameters,
+ int startIndex,
+ SessionImplementor session) throws SQLException {
+ int span = 0;
+ span += bindPositionalParameters( statement, queryParameters, startIndex, session );
+ span += bindNamedParameters( statement, queryParameters.getNamedParameters(), startIndex + span, session );
+ return span;
+ }
+
+ /**
+ * Bind positional parameter values to the JDBC prepared statement.
+ * <p/>
+ * Postional parameters are those specified by JDBC-style ? parameters
+ * in the source query. It is (currently) expected that these come
+ * before any named parameters in the source query.
+ *
+ * @param statement The JDBC prepared statement
+ * @param queryParameters The encapsulation of the parameter values to be bound.
+ * @param startIndex The position from which to start binding parameter values.
+ * @param session The originating session.
+ * @return The number of JDBC bind positions actually bound during this method execution.
+ * @throws SQLException Indicates problems performing the binding.
+ * @throws org.hibernate.HibernateException Indicates problems delegating binding to the types.
+ */
+ protected int bindPositionalParameters(
+ final PreparedStatement statement,
+ final QueryParameters queryParameters,
+ final int startIndex,
+ final SessionImplementor session) throws SQLException, HibernateException {
+ final Object[] values = queryParameters.getFilteredPositionalParameterValues();
+ final Type[] types = queryParameters.getFilteredPositionalParameterTypes();
+ int span = 0;
+ for ( int i = 0; i < values.length; i++ ) {
+ types[i].nullSafeSet( statement, values[i], startIndex + span, session );
+ span += types[i].getColumnSpan( getFactory() );
+ }
+ return span;
+ }
+
+ /**
+ * Bind named parameters to the JDBC prepared statement.
+ * <p/>
+ * This is a generic implementation, the problem being that in the
+ * general case we do not know enough information about the named
+ * parameters to perform this in a complete manner here. Thus this
+ * is generally overridden on subclasses allowing named parameters to
+ * apply the specific behavior. The most usual limitation here is that
+ * we need to assume the type span is always one...
+ *
+ * @param statement The JDBC prepared statement
+ * @param namedParams A map of parameter names to values
+ * @param startIndex The position from which to start binding parameter values.
+ * @param session The originating session.
+ * @return The number of JDBC bind positions actually bound during this method execution.
+ * @throws SQLException Indicates problems performing the binding.
+ * @throws org.hibernate.HibernateException Indicates problems delegating binding to the types.
+ */
+ protected int bindNamedParameters(
+ final PreparedStatement statement,
+ final Map namedParams,
+ final int startIndex,
+ final SessionImplementor session) throws SQLException, HibernateException {
+ if ( namedParams != null ) {
+ // assumes that types are all of span 1
+ Iterator iter = namedParams.entrySet().iterator();
+ int result = 0;
+ while ( iter.hasNext() ) {
+ Map.Entry e = ( Map.Entry ) iter.next();
+ String name = ( String ) e.getKey();
+ TypedValue typedval = ( TypedValue ) e.getValue();
+ int[] locs = getNamedParameterLocs( name );
+ for ( int i = 0; i < locs.length; i++ ) {
+ if ( log.isDebugEnabled() ) {
+ log.debug(
+ "bindNamedParameters() " +
+ typedval.getValue() + " -> " + name +
+ " [" + ( locs[i] + startIndex ) + "]"
+ );
+ }
+ typedval.getType().nullSafeSet( statement, typedval.getValue(), locs[i] + startIndex, session );
+ }
+ result += locs.length;
+ }
+ return result;
+ }
+ else {
+ return 0;
+ }
+ }
+
+ public int[] getNamedParameterLocs(String name) {
+ throw new AssertionFailure("no named parameters");
+ }
+
+ /**
+ * Fetch a <tt>PreparedStatement</tt>, call <tt>setMaxRows</tt> and then execute it,
+ * advance to the first result and return an SQL <tt>ResultSet</tt>
+ */
+ protected final ResultSet getResultSet(
+ final PreparedStatement st,
+ final boolean autodiscovertypes,
+ final boolean callable,
+ final RowSelection selection,
+ final SessionImplementor session)
+ throws SQLException, HibernateException {
+
+ ResultSet rs = null;
+ try {
+ Dialect dialect = getFactory().getDialect();
+ if (callable) {
+ rs = session.getBatcher().getResultSet( (CallableStatement) st, dialect );
+ }
+ else {
+ rs = session.getBatcher().getResultSet( st );
+ }
+ rs = wrapResultSetIfEnabled( rs , session );
+
+ if ( !dialect.supportsLimitOffset() || !useLimit( selection, dialect ) ) {
+ advance( rs, selection );
+ }
+
+ if ( autodiscovertypes ) {
+ autoDiscoverTypes( rs );
+ }
+ return rs;
+ }
+ catch ( SQLException sqle ) {
+ session.getBatcher().closeQueryStatement( st, rs );
+ throw sqle;
+ }
+ }
+
+ protected void autoDiscoverTypes(ResultSet rs) {
+ throw new AssertionFailure("Auto discover types not supported in this loader");
+
+ }
+
+ private synchronized ResultSet wrapResultSetIfEnabled(final ResultSet rs, final SessionImplementor session) {
+ // synchronized to avoid multi-thread access issues; defined as method synch to avoid
+ // potential deadlock issues due to nature of code.
+ if ( session.getFactory().getSettings().isWrapResultSetsEnabled() ) {
+ try {
+ log.debug("Wrapping result set [" + rs + "]");
+ return new ResultSetWrapper( rs, retreiveColumnNameToIndexCache( rs ) );
+ }
+ catch(SQLException e) {
+ log.info("Error wrapping result set", e);
+ return rs;
+ }
+ }
+ else {
+ return rs;
+ }
+ }
+
+ private ColumnNameCache retreiveColumnNameToIndexCache(ResultSet rs) throws SQLException {
+ if ( columnNameCache == null ) {
+ log.trace("Building columnName->columnIndex cache");
+ columnNameCache = new ColumnNameCache( rs.getMetaData().getColumnCount() );
+ }
+
+ return columnNameCache;
+ }
+
+ /**
+ * Called by subclasses that load entities
+ * @param persister only needed for logging
+ */
+ protected final List loadEntity(
+ final SessionImplementor session,
+ final Object id,
+ final Type identifierType,
+ final Object optionalObject,
+ final String optionalEntityName,
+ final Serializable optionalIdentifier,
+ final EntityPersister persister) throws HibernateException {
+
+ if ( log.isDebugEnabled() ) {
+ log.debug(
+ "loading entity: " +
+ MessageHelper.infoString( persister, id, identifierType, getFactory() )
+ );
+ }
+
+ List result;
+ try {
+ result = doQueryAndInitializeNonLazyCollections(
+ session,
+ new QueryParameters(
+ new Type[] { identifierType },
+ new Object[] { id },
+ optionalObject,
+ optionalEntityName,
+ optionalIdentifier
+ ),
+ false
+ );
+ }
+ catch ( SQLException sqle ) {
+ final Loadable[] persisters = getEntityPersisters();
+ throw JDBCExceptionHelper.convert(
+ factory.getSQLExceptionConverter(),
+ sqle,
+ "could not load an entity: " +
+ MessageHelper.infoString( persisters[persisters.length-1], id, identifierType, getFactory() ),
+ getSQLString()
+ );
+ }
+
+ log.debug("done entity load");
+
+ return result;
+
+ }
+
+ /**
+ * Called by subclasses that load entities
+ * @param persister only needed for logging
+ */
+ protected final List loadEntity(
+ final SessionImplementor session,
+ final Object key,
+ final Object index,
+ final Type keyType,
+ final Type indexType,
+ final EntityPersister persister) throws HibernateException {
+
+ if ( log.isDebugEnabled() ) {
+ log.debug( "loading collection element by index" );
+ }
+
+ List result;
+ try {
+ result = doQueryAndInitializeNonLazyCollections(
+ session,
+ new QueryParameters(
+ new Type[] { keyType, indexType },
+ new Object[] { key, index }
+ ),
+ false
+ );
+ }
+ catch ( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ factory.getSQLExceptionConverter(),
+ sqle,
+ "could not collection element by index",
+ getSQLString()
+ );
+ }
+
+ log.debug("done entity load");
+
+ return result;
+
+ }
+
+ /**
+ * Called by wrappers that batch load entities
+ * @param persister only needed for logging
+ */
+ public final List loadEntityBatch(
+ final SessionImplementor session,
+ final Serializable[] ids,
+ final Type idType,
+ final Object optionalObject,
+ final String optionalEntityName,
+ final Serializable optionalId,
+ final EntityPersister persister) throws HibernateException {
+
+ if ( log.isDebugEnabled() ) {
+ log.debug(
+ "batch loading entity: " +
+ MessageHelper.infoString(persister, ids, getFactory() )
+ );
+ }
+
+ Type[] types = new Type[ids.length];
+ Arrays.fill( types, idType );
+ List result;
+ try {
+ result = doQueryAndInitializeNonLazyCollections(
+ session,
+ new QueryParameters( types, ids, optionalObject, optionalEntityName, optionalId ),
+ false
+ );
+ }
+ catch ( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ factory.getSQLExceptionConverter(),
+ sqle,
+ "could not load an entity batch: " +
+ MessageHelper.infoString( getEntityPersisters()[0], ids, getFactory() ),
+ getSQLString()
+ );
+ }
+
+ log.debug("done entity batch load");
+
+ return result;
+
+ }
+
+ /**
+ * Called by subclasses that initialize collections
+ */
+ public final void loadCollection(
+ final SessionImplementor session,
+ final Serializable id,
+ final Type type) throws HibernateException {
+
+ if ( log.isDebugEnabled() ) {
+ log.debug(
+ "loading collection: "+
+ MessageHelper.collectionInfoString( getCollectionPersisters()[0], id, getFactory() )
+ );
+ }
+
+ Serializable[] ids = new Serializable[]{id};
+ try {
+ doQueryAndInitializeNonLazyCollections(
+ session,
+ new QueryParameters( new Type[]{type}, ids, ids ),
+ true
+ );
+ }
+ catch ( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ factory.getSQLExceptionConverter(),
+ sqle,
+ "could not initialize a collection: " +
+ MessageHelper.collectionInfoString( getCollectionPersisters()[0], id, getFactory() ),
+ getSQLString()
+ );
+ }
+
+ log.debug("done loading collection");
+
+ }
+
+ /**
+ * Called by wrappers that batch initialize collections
+ */
+ public final void loadCollectionBatch(
+ final SessionImplementor session,
+ final Serializable[] ids,
+ final Type type) throws HibernateException {
+
+ if ( log.isDebugEnabled() ) {
+ log.debug(
+ "batch loading collection: "+
+ MessageHelper.collectionInfoString( getCollectionPersisters()[0], ids, getFactory() )
+ );
+ }
+
+ Type[] idTypes = new Type[ids.length];
+ Arrays.fill( idTypes, type );
+ try {
+ doQueryAndInitializeNonLazyCollections(
+ session,
+ new QueryParameters( idTypes, ids, ids ),
+ true
+ );
+ }
+ catch ( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ factory.getSQLExceptionConverter(),
+ sqle,
+ "could not initialize a collection batch: " +
+ MessageHelper.collectionInfoString( getCollectionPersisters()[0], ids, getFactory() ),
+ getSQLString()
+ );
+ }
+
+ log.debug("done batch load");
+
+ }
+
+ /**
+ * Called by subclasses that batch initialize collections
+ */
+ protected final void loadCollectionSubselect(
+ final SessionImplementor session,
+ final Serializable[] ids,
+ final Object[] parameterValues,
+ final Type[] parameterTypes,
+ final Map namedParameters,
+ final Type type) throws HibernateException {
+
+ Type[] idTypes = new Type[ids.length];
+ Arrays.fill( idTypes, type );
+ try {
+ doQueryAndInitializeNonLazyCollections( session,
+ new QueryParameters( parameterTypes, parameterValues, namedParameters, ids ),
+ true
+ );
+ }
+ catch ( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ factory.getSQLExceptionConverter(),
+ sqle,
+ "could not load collection by subselect: " +
+ MessageHelper.collectionInfoString( getCollectionPersisters()[0], ids, getFactory() ),
+ getSQLString()
+ );
+ }
+ }
+
+ /**
+ * Return the query results, using the query cache, called
+ * by subclasses that implement cacheable queries
+ */
+ protected List list(
+ final SessionImplementor session,
+ final QueryParameters queryParameters,
+ final Set querySpaces,
+ final Type[] resultTypes) throws HibernateException {
+
+ final boolean cacheable = factory.getSettings().isQueryCacheEnabled() &&
+ queryParameters.isCacheable();
+
+ if ( cacheable ) {
+ return listUsingQueryCache( session, queryParameters, querySpaces, resultTypes );
+ }
+ else {
+ return listIgnoreQueryCache( session, queryParameters );
+ }
+ }
+
+ private List listIgnoreQueryCache(SessionImplementor session, QueryParameters queryParameters) {
+ return getResultList( doList( session, queryParameters ), queryParameters.getResultTransformer() );
+ }
+
+ private List listUsingQueryCache(
+ final SessionImplementor session,
+ final QueryParameters queryParameters,
+ final Set querySpaces,
+ final Type[] resultTypes) {
+
+ QueryCache queryCache = factory.getQueryCache( queryParameters.getCacheRegion() );
+
+ Set filterKeys = FilterKey.createFilterKeys(
+ session.getEnabledFilters(),
+ session.getEntityMode()
+ );
+ QueryKey key = new QueryKey(
+ getSQLString(),
+ queryParameters,
+ filterKeys,
+ session.getEntityMode()
+ );
+
+ List result = getResultFromQueryCache(
+ session,
+ queryParameters,
+ querySpaces,
+ resultTypes,
+ queryCache,
+ key
+ );
+
+ if ( result == null ) {
+ result = doList( session, queryParameters );
+
+ putResultInQueryCache(
+ session,
+ queryParameters,
+ resultTypes,
+ queryCache,
+ key,
+ result
+ );
+ }
+
+ return getResultList( result, queryParameters.getResultTransformer() );
+ }
+
+ private List getResultFromQueryCache(
+ final SessionImplementor session,
+ final QueryParameters queryParameters,
+ final Set querySpaces,
+ final Type[] resultTypes,
+ final QueryCache queryCache,
+ final QueryKey key) {
+ List result = null;
+
+ if ( session.getCacheMode().isGetEnabled() ) {
+ result = queryCache.get(
+ key,
+ resultTypes,
+ queryParameters.isNaturalKeyLookup(),
+ querySpaces,
+ session
+ );
+
+ if ( factory.getStatistics().isStatisticsEnabled() ) {
+ if ( result == null ) {
+ factory.getStatisticsImplementor()
+ .queryCacheMiss( getQueryIdentifier(), queryCache.getRegion().getName() );
+ }
+ else {
+ factory.getStatisticsImplementor()
+ .queryCacheHit( getQueryIdentifier(), queryCache.getRegion().getName() );
+ }
+ }
+
+ }
+
+ return result;
+ }
+
+ private void putResultInQueryCache(
+ final SessionImplementor session,
+ final QueryParameters queryParameters,
+ final Type[] resultTypes,
+ final QueryCache queryCache,
+ final QueryKey key,
+ final List result) {
+
+ if ( session.getCacheMode().isPutEnabled() ) {
+ boolean put = queryCache.put(
+ key,
+ resultTypes,
+ result,
+ queryParameters.isNaturalKeyLookup(),
+ session
+ );
+ if ( put && factory.getStatistics().isStatisticsEnabled() ) {
+ factory.getStatisticsImplementor()
+ .queryCachePut( getQueryIdentifier(), queryCache.getRegion().getName() );
+ }
+ }
+ }
+
+ /**
+ * Actually execute a query, ignoring the query cache
+ */
+ protected List doList(final SessionImplementor session, final QueryParameters queryParameters)
+ throws HibernateException {
+
+ final boolean stats = getFactory().getStatistics().isStatisticsEnabled();
+ long startTime = 0;
+ if ( stats ) startTime = System.currentTimeMillis();
+
+ List result;
+ try {
+ result = doQueryAndInitializeNonLazyCollections( session, queryParameters, true );
+ }
+ catch ( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ factory.getSQLExceptionConverter(),
+ sqle,
+ "could not execute query",
+ getSQLString()
+ );
+ }
+
+ if ( stats ) {
+ getFactory().getStatisticsImplementor().queryExecuted(
+ getQueryIdentifier(),
+ result.size(),
+ System.currentTimeMillis() - startTime
+ );
+ }
+
+ return result;
+ }
+
+ /**
+ * Check whether the current loader can support returning ScrollableResults.
+ *
+ * @throws HibernateException
+ */
+ protected void checkScrollability() throws HibernateException {
+ // Allows various loaders (ok mainly the QueryLoader :) to check
+ // whether scrolling of their result set should be allowed.
+ //
+ // By default it is allowed.
+ return;
+ }
+
+ /**
+ * Does the result set to be scrolled contain collection fetches?
+ *
+ * @return True if it does, and thus needs the special fetching scroll
+ * functionality; false otherwise.
+ */
+ protected boolean needsFetchingScroll() {
+ return false;
+ }
+
+ /**
+ * Return the query results, as an instance of <tt>ScrollableResults</tt>
+ *
+ * @param queryParameters The parameters with which the query should be executed.
+ * @param returnTypes The expected return types of the query
+ * @param holderInstantiator If the return values are expected to be wrapped
+ * in a holder, this is the thing that knows how to wrap them.
+ * @param session The session from which the scroll request originated.
+ * @return The ScrollableResults instance.
+ * @throws HibernateException Indicates an error executing the query, or constructing
+ * the ScrollableResults.
+ */
+ protected ScrollableResults scroll(
+ final QueryParameters queryParameters,
+ final Type[] returnTypes,
+ final HolderInstantiator holderInstantiator,
+ final SessionImplementor session) throws HibernateException {
+
+ checkScrollability();
+
+ final boolean stats = getQueryIdentifier() != null &&
+ getFactory().getStatistics().isStatisticsEnabled();
+ long startTime = 0;
+ if ( stats ) startTime = System.currentTimeMillis();
+
+ try {
+
+ PreparedStatement st = prepareQueryStatement( queryParameters, true, session );
+ ResultSet rs = getResultSet(st, queryParameters.hasAutoDiscoverScalarTypes(), queryParameters.isCallable(), queryParameters.getRowSelection(), session);
+
+ if ( stats ) {
+ getFactory().getStatisticsImplementor().queryExecuted(
+ getQueryIdentifier(),
+ 0,
+ System.currentTimeMillis() - startTime
+ );
+ }
+
+ if ( needsFetchingScroll() ) {
+ return new FetchingScrollableResultsImpl(
+ rs,
+ st,
+ session,
+ this,
+ queryParameters,
+ returnTypes,
+ holderInstantiator
+ );
+ }
+ else {
+ return new ScrollableResultsImpl(
+ rs,
+ st,
+ session,
+ this,
+ queryParameters,
+ returnTypes,
+ holderInstantiator
+ );
+ }
+
+ }
+ catch ( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ factory.getSQLExceptionConverter(),
+ sqle,
+ "could not execute query using scroll",
+ getSQLString()
+ );
+ }
+
+ }
+
+ /**
+ * Calculate and cache select-clause suffixes. Must be
+ * called by subclasses after instantiation.
+ */
+ protected void postInstantiate() {}
+
+ /**
+ * Get the result set descriptor
+ */
+ protected abstract EntityAliases[] getEntityAliases();
+
+ protected abstract CollectionAliases[] getCollectionAliases();
+
+ /**
+ * Identifies the query for statistics reporting, if null,
+ * no statistics will be reported
+ */
+ protected String getQueryIdentifier() {
+ return null;
+ }
+
+ public final SessionFactoryImplementor getFactory() {
+ return factory;
+ }
+
+ public String toString() {
+ return getClass().getName() + '(' + getSQLString() + ')';
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/OuterJoinLoader.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/OuterJoinLoader.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/OuterJoinLoader.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,102 @@
+//$Id: OuterJoinLoader.java 7124 2005-06-13 20:27:16Z oneovthafew $
+package org.hibernate.loader;
+
+import java.util.Map;
+
+import org.hibernate.LockMode;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.persister.entity.Loadable;
+import org.hibernate.type.EntityType;
+
+/**
+ * Implements logic for walking a tree of associated classes.
+ *
+ * Generates an SQL select string containing all properties of those classes.
+ * Tables are joined using an ANSI-style left outer join.
+ *
+ * @author Gavin King
+ */
+public abstract class OuterJoinLoader extends BasicLoader {
+
+ protected Loadable[] persisters;
+ protected CollectionPersister[] collectionPersisters;
+ protected int[] collectionOwners;
+ protected String[] aliases;
+ protected LockMode[] lockModeArray;
+ protected int[] owners;
+ protected EntityType[] ownerAssociationTypes;
+ protected String sql;
+ protected String[] suffixes;
+ protected String[] collectionSuffixes;
+
+ private Map enabledFilters;
+
+ protected final Dialect getDialect() {
+ return getFactory().getDialect();
+ }
+
+ public OuterJoinLoader(SessionFactoryImplementor factory, Map enabledFilters) {
+ super(factory);
+ this.enabledFilters = enabledFilters;
+ }
+
+ protected String[] getSuffixes() {
+ return suffixes;
+ }
+
+ protected String[] getCollectionSuffixes() {
+ return collectionSuffixes;
+ }
+
+ protected final String getSQLString() {
+ return sql;
+ }
+
+ protected final Loadable[] getEntityPersisters() {
+ return persisters;
+ }
+
+ protected int[] getOwners() {
+ return owners;
+ }
+
+ protected EntityType[] getOwnerAssociationTypes() {
+ return ownerAssociationTypes;
+ }
+
+ protected LockMode[] getLockModes(Map lockModes) {
+ return lockModeArray;
+ }
+
+ public Map getEnabledFilters() {
+ return enabledFilters;
+ }
+
+ protected final String[] getAliases() {
+ return aliases;
+ }
+
+ protected final CollectionPersister[] getCollectionPersisters() {
+ return collectionPersisters;
+ }
+
+ protected final int[] getCollectionOwners() {
+ return collectionOwners;
+ }
+
+ protected void initFromWalker(JoinWalker walker) {
+ persisters = walker.getPersisters();
+ collectionPersisters = walker.getCollectionPersisters();
+ ownerAssociationTypes = walker.getOwnerAssociationTypes();
+ lockModeArray = walker.getLockModeArray();
+ suffixes = walker.getSuffixes();
+ collectionSuffixes = walker.getCollectionSuffixes();
+ owners = walker.getOwners();
+ collectionOwners = walker.getCollectionOwners();
+ sql = walker.getSQLString();
+ aliases = walker.getAliases();
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/OuterJoinableAssociation.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/OuterJoinableAssociation.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/OuterJoinableAssociation.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,163 @@
+//$Id: OuterJoinableAssociation.java 6828 2005-05-19 07:28:59Z steveebersole $
+package org.hibernate.loader;
+
+import java.util.List;
+import java.util.Map;
+
+import org.hibernate.MappingException;
+import org.hibernate.engine.JoinHelper;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.persister.collection.QueryableCollection;
+import org.hibernate.persister.entity.Joinable;
+import org.hibernate.sql.JoinFragment;
+import org.hibernate.type.AssociationType;
+import org.hibernate.type.EntityType;
+
+public final class OuterJoinableAssociation {
+ private final AssociationType joinableType;
+ private final Joinable joinable;
+ private final String lhsAlias; // belong to other persister
+ private final String[] lhsColumns; // belong to other persister
+ private final String rhsAlias;
+ private final String[] rhsColumns;
+ private final int joinType;
+ private final String on;
+ private final Map enabledFilters;
+
+ public OuterJoinableAssociation(
+ AssociationType joinableType,
+ String lhsAlias,
+ String[] lhsColumns,
+ String rhsAlias,
+ int joinType,
+ SessionFactoryImplementor factory,
+ Map enabledFilters)
+ throws MappingException {
+ this.joinableType = joinableType;
+ this.lhsAlias = lhsAlias;
+ this.lhsColumns = lhsColumns;
+ this.rhsAlias = rhsAlias;
+ this.joinType = joinType;
+ this.joinable = joinableType.getAssociatedJoinable(factory);
+ this.rhsColumns = JoinHelper.getRHSColumnNames(joinableType, factory);
+ this.on = joinableType.getOnCondition(rhsAlias, factory, enabledFilters);
+ this.enabledFilters = enabledFilters; // needed later for many-to-many/filter application
+ }
+
+ public int getJoinType() {
+ return joinType;
+ }
+
+ public String getRHSAlias() {
+ return rhsAlias;
+ }
+
+ private boolean isOneToOne() {
+ if ( joinableType.isEntityType() ) {
+ EntityType etype = (EntityType) joinableType;
+ return etype.isOneToOne() /*&& etype.isReferenceToPrimaryKey()*/;
+ }
+ else {
+ return false;
+ }
+
+ }
+
+ public AssociationType getJoinableType() {
+ return joinableType;
+ }
+
+ public String getRHSUniqueKeyName() {
+ return joinableType.getRHSUniqueKeyPropertyName();
+ }
+
+ public boolean isCollection() {
+ return joinableType.isCollectionType();
+ }
+
+ public Joinable getJoinable() {
+ return joinable;
+ }
+
+ public int getOwner(final List associations) {
+ if ( isOneToOne() || isCollection() ) {
+ return getPosition(lhsAlias, associations);
+ }
+ else {
+ return -1;
+ }
+ }
+
+ /**
+ * Get the position of the join with the given alias in the
+ * list of joins
+ */
+ private static int getPosition(String lhsAlias, List associations) {
+ int result = 0;
+ for ( int i=0; i<associations.size(); i++ ) {
+ OuterJoinableAssociation oj = (OuterJoinableAssociation) associations.get(i);
+ if ( oj.getJoinable().consumesEntityAlias() /*|| oj.getJoinable().consumesCollectionAlias() */ ) {
+ if ( oj.rhsAlias.equals(lhsAlias) ) return result;
+ result++;
+ }
+ }
+ return -1;
+ }
+
+ public void addJoins(JoinFragment outerjoin) throws MappingException {
+ outerjoin.addJoin(
+ joinable.getTableName(),
+ rhsAlias,
+ lhsColumns,
+ rhsColumns,
+ joinType,
+ on
+ );
+ outerjoin.addJoins(
+ joinable.fromJoinFragment(rhsAlias, false, true),
+ joinable.whereJoinFragment(rhsAlias, false, true)
+ );
+ }
+
+ public void validateJoin(String path) throws MappingException {
+ if (
+ rhsColumns==null ||
+ lhsColumns==null ||
+ lhsColumns.length!=rhsColumns.length ||
+ lhsColumns.length==0
+ ) {
+ throw new MappingException("invalid join columns for association: " + path);
+ }
+ }
+
+ public boolean isManyToManyWith(OuterJoinableAssociation other) {
+ if ( joinable.isCollection() ) {
+ QueryableCollection persister = ( QueryableCollection ) joinable;
+ if ( persister.isManyToMany() ) {
+ return persister.getElementType() == other.getJoinableType();
+ }
+ }
+ return false;
+ }
+
+ public void addManyToManyJoin(JoinFragment outerjoin, QueryableCollection collection) throws MappingException {
+ String manyToManyFilter = collection.getManyToManyFilterFragment( rhsAlias, enabledFilters );
+ String condition = "".equals( manyToManyFilter )
+ ? on
+ : "".equals( on )
+ ? manyToManyFilter
+ : on + " and " + manyToManyFilter;
+ outerjoin.addJoin(
+ joinable.getTableName(),
+ rhsAlias,
+ lhsColumns,
+ rhsColumns,
+ joinType,
+ condition
+ );
+ outerjoin.addJoins(
+ joinable.fromJoinFragment(rhsAlias, false, true),
+ joinable.whereJoinFragment(rhsAlias, false, true)
+ );
+ }
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/BasicCollectionJoinWalker.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/BasicCollectionJoinWalker.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/BasicCollectionJoinWalker.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,167 @@
+//$Id: BasicCollectionJoinWalker.java 9875 2006-05-04 16:23:44Z steve.ebersole at jboss.com $
+package org.hibernate.loader.collection;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.hibernate.FetchMode;
+import org.hibernate.LockMode;
+import org.hibernate.MappingException;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.loader.BasicLoader;
+import org.hibernate.loader.OuterJoinableAssociation;
+import org.hibernate.persister.collection.QueryableCollection;
+import org.hibernate.sql.JoinFragment;
+import org.hibernate.sql.Select;
+import org.hibernate.type.AssociationType;
+import org.hibernate.util.CollectionHelper;
+import org.hibernate.util.StringHelper;
+
+/**
+ * Walker for collections of values and many-to-many associations
+ *
+ * @see BasicCollectionLoader
+ * @author Gavin King
+ */
+public class BasicCollectionJoinWalker extends CollectionJoinWalker {
+
+ private final QueryableCollection collectionPersister;
+
+ public BasicCollectionJoinWalker(
+ QueryableCollection collectionPersister,
+ int batchSize,
+ String subquery,
+ SessionFactoryImplementor factory,
+ Map enabledFilters)
+ throws MappingException {
+
+ super(factory, enabledFilters);
+
+ this.collectionPersister = collectionPersister;
+
+ String alias = generateRootAlias( collectionPersister.getRole() );
+
+ walkCollectionTree(collectionPersister, alias);
+
+ List allAssociations = new ArrayList();
+ allAssociations.addAll(associations);
+ allAssociations.add( new OuterJoinableAssociation(
+ collectionPersister.getCollectionType(),
+ null,
+ null,
+ alias,
+ JoinFragment.LEFT_OUTER_JOIN,
+ getFactory(),
+ CollectionHelper.EMPTY_MAP
+ ) );
+
+ initPersisters(allAssociations, LockMode.NONE);
+ initStatementString(alias, batchSize, subquery);
+
+ }
+
+ private void initStatementString(
+ final String alias,
+ final int batchSize,
+ final String subquery)
+ throws MappingException {
+
+ final int joins = countEntityPersisters( associations );
+ final int collectionJoins = countCollectionPersisters( associations ) + 1;
+
+ suffixes = BasicLoader.generateSuffixes( joins );
+ collectionSuffixes = BasicLoader.generateSuffixes( joins, collectionJoins );
+
+ StringBuffer whereString = whereString(
+ alias,
+ collectionPersister.getKeyColumnNames(),
+ subquery,
+ batchSize
+ );
+
+ String manyToManyOrderBy = "";
+ String filter = collectionPersister.filterFragment( alias, getEnabledFilters() );
+ if ( collectionPersister.isManyToMany() ) {
+ // from the collection of associations, locate OJA for the
+ // ManyToOne corresponding to this persister to fully
+ // define the many-to-many; we need that OJA so that we can
+ // use its alias here
+ // TODO : is there a better way here?
+ Iterator itr = associations.iterator();
+ AssociationType associationType = ( AssociationType ) collectionPersister.getElementType();
+ while ( itr.hasNext() ) {
+ OuterJoinableAssociation oja = ( OuterJoinableAssociation ) itr.next();
+ if ( oja.getJoinableType() == associationType ) {
+ // we found it
+ filter += collectionPersister.getManyToManyFilterFragment(
+ oja.getRHSAlias(),
+ getEnabledFilters()
+ );
+ manyToManyOrderBy += collectionPersister.getManyToManyOrderByString( oja.getRHSAlias() );
+ }
+ }
+ }
+ whereString.insert( 0, StringHelper.moveAndToBeginning( filter ) );
+
+ JoinFragment ojf = mergeOuterJoins(associations);
+ Select select = new Select( getDialect() )
+ .setSelectClause(
+ collectionPersister.selectFragment(alias, collectionSuffixes[0] ) +
+ selectString(associations)
+ )
+ .setFromClause( collectionPersister.getTableName(), alias )
+ .setWhereClause( whereString.toString() )
+ .setOuterJoins(
+ ojf.toFromFragmentString(),
+ ojf.toWhereFragmentString()
+ );
+
+ select.setOrderByClause( orderBy( associations, mergeOrderings( collectionPersister.getSQLOrderByString(alias), manyToManyOrderBy ) ) );
+
+ if ( getFactory().getSettings().isCommentsEnabled() ) {
+ select.setComment( "load collection " + collectionPersister.getRole() );
+ }
+
+ sql = select.toStatementString();
+ }
+
+ /**
+ * We can use an inner join for first many-to-many association
+ */
+ protected int getJoinType(
+ AssociationType type,
+ FetchMode config,
+ String path,
+ Set visitedAssociations,
+ String lhsTable,
+ String[] lhsColumns,
+ boolean nullable,
+ int currentDepth)
+ throws MappingException {
+
+ int joinType = super.getJoinType(
+ type,
+ config,
+ path,
+ lhsTable,
+ lhsColumns,
+ nullable,
+ currentDepth,
+ null
+ );
+ //we can use an inner join for the many-to-many
+ if ( joinType==JoinFragment.LEFT_OUTER_JOIN && "".equals(path) ) {
+ joinType=JoinFragment.INNER_JOIN;
+ }
+ return joinType;
+ }
+
+ public String toString() {
+ return getClass().getName() + '(' + collectionPersister.getRole() + ')';
+ }
+
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/BasicCollectionJoinWalker.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/BasicCollectionLoader.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/BasicCollectionLoader.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/BasicCollectionLoader.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,67 @@
+//$Id: BasicCollectionLoader.java 7123 2005-06-13 20:10:20Z oneovthafew $
+package org.hibernate.loader.collection;
+
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.MappingException;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.loader.JoinWalker;
+import org.hibernate.persister.collection.QueryableCollection;
+
+/**
+ * Loads a collection of values or a many-to-many association.
+ * <br>
+ * The collection persister must implement <tt>QueryableCOllection<tt>. For
+ * other collections, create a customized subclass of <tt>Loader</tt>.
+ *
+ * @see OneToManyLoader
+ * @author Gavin King
+ */
+public class BasicCollectionLoader extends CollectionLoader {
+
+ private static final Log log = LogFactory.getLog(BasicCollectionLoader.class);
+
+ public BasicCollectionLoader(
+ QueryableCollection collectionPersister,
+ SessionFactoryImplementor session,
+ Map enabledFilters)
+ throws MappingException {
+ this(collectionPersister, 1, session, enabledFilters);
+ }
+
+ public BasicCollectionLoader(
+ QueryableCollection collectionPersister,
+ int batchSize,
+ SessionFactoryImplementor factory,
+ Map enabledFilters)
+ throws MappingException {
+ this(collectionPersister, batchSize, null, factory, enabledFilters);
+ }
+
+ protected BasicCollectionLoader(
+ QueryableCollection collectionPersister,
+ int batchSize,
+ String subquery,
+ SessionFactoryImplementor factory,
+ Map enabledFilters)
+ throws MappingException {
+
+ super(collectionPersister, factory, enabledFilters);
+
+ JoinWalker walker = new BasicCollectionJoinWalker(
+ collectionPersister,
+ batchSize,
+ subquery,
+ factory,
+ enabledFilters
+ );
+ initFromWalker( walker );
+
+ postInstantiate();
+
+ log.debug( "Static select for collection " + collectionPersister.getRole() + ": " + getSQLString() );
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/BatchingCollectionInitializer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/BatchingCollectionInitializer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/BatchingCollectionInitializer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,96 @@
+//$Id: BatchingCollectionInitializer.java 7123 2005-06-13 20:10:20Z oneovthafew $
+package org.hibernate.loader.collection;
+
+import java.io.Serializable;
+import java.util.Map;
+
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.loader.Loader;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.persister.collection.QueryableCollection;
+import org.hibernate.util.ArrayHelper;
+
+/**
+ * "Batch" loads collections, using multiple foreign key values in the
+ * SQL <tt>where</tt> clause.
+ *
+ * @see BasicCollectionLoader
+ * @see OneToManyLoader
+ * @author Gavin King
+ */
+public class BatchingCollectionInitializer implements CollectionInitializer {
+
+ private final Loader[] loaders;
+ private final int[] batchSizes;
+ private final CollectionPersister collectionPersister;
+
+ public BatchingCollectionInitializer(CollectionPersister collPersister, int[] batchSizes, Loader[] loaders) {
+ this.loaders = loaders;
+ this.batchSizes = batchSizes;
+ this.collectionPersister = collPersister;
+ }
+
+ public void initialize(Serializable id, SessionImplementor session)
+ throws HibernateException {
+
+ Serializable[] batch = session.getPersistenceContext().getBatchFetchQueue()
+ .getCollectionBatch( collectionPersister, id, batchSizes[0], session.getEntityMode() );
+
+ for ( int i=0; i<batchSizes.length-1; i++) {
+ final int smallBatchSize = batchSizes[i];
+ if ( batch[smallBatchSize-1]!=null ) {
+ Serializable[] smallBatch = new Serializable[smallBatchSize];
+ System.arraycopy(batch, 0, smallBatch, 0, smallBatchSize);
+ loaders[i].loadCollectionBatch( session, smallBatch, collectionPersister.getKeyType() );
+ return; //EARLY EXIT!
+ }
+ }
+
+ loaders[batchSizes.length-1].loadCollection( session, id, collectionPersister.getKeyType() );
+
+ }
+
+ public static CollectionInitializer createBatchingOneToManyInitializer(
+ final QueryableCollection persister,
+ final int maxBatchSize,
+ final SessionFactoryImplementor factory,
+ final Map enabledFilters)
+ throws MappingException {
+
+ if ( maxBatchSize>1 ) {
+ int[] batchSizesToCreate = ArrayHelper.getBatchSizes(maxBatchSize);
+ Loader[] loadersToCreate = new Loader[ batchSizesToCreate.length ];
+ for ( int i=0; i<batchSizesToCreate.length; i++ ) {
+ loadersToCreate[i] = new OneToManyLoader(persister, batchSizesToCreate[i], factory, enabledFilters);
+ }
+ return new BatchingCollectionInitializer(persister, batchSizesToCreate, loadersToCreate);
+ }
+ else {
+ return new OneToManyLoader(persister, factory, enabledFilters);
+ }
+ }
+
+ public static CollectionInitializer createBatchingCollectionInitializer(
+ final QueryableCollection persister,
+ final int maxBatchSize,
+ final SessionFactoryImplementor factory,
+ final Map enabledFilters)
+ throws MappingException {
+
+ if ( maxBatchSize>1 ) {
+ int[] batchSizesToCreate = ArrayHelper.getBatchSizes(maxBatchSize);
+ Loader[] loadersToCreate = new Loader[ batchSizesToCreate.length ];
+ for ( int i=0; i<batchSizesToCreate.length; i++ ) {
+ loadersToCreate[i] = new BasicCollectionLoader(persister, batchSizesToCreate[i], factory, enabledFilters);
+ }
+ return new BatchingCollectionInitializer(persister, batchSizesToCreate, loadersToCreate);
+ }
+ else {
+ return new BasicCollectionLoader(persister, factory, enabledFilters);
+ }
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/CollectionInitializer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/CollectionInitializer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/CollectionInitializer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,26 @@
+//$Id: CollectionInitializer.java 7123 2005-06-13 20:10:20Z oneovthafew $
+package org.hibernate.loader.collection;
+
+import java.io.Serializable;
+
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionImplementor;
+
+/**
+ * An interface for collection loaders
+ * @see BasicCollectionLoader
+ * @see OneToManyLoader
+ * @author Gavin King
+ */
+public interface CollectionInitializer {
+ /**
+ * Initialize the given collection
+ */
+ public void initialize(Serializable id, SessionImplementor session) throws HibernateException;
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/CollectionJoinWalker.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/CollectionJoinWalker.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/CollectionJoinWalker.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,40 @@
+//$Id: CollectionJoinWalker.java 7627 2005-07-24 06:53:06Z oneovthafew $
+package org.hibernate.loader.collection;
+
+import java.util.Map;
+
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.loader.JoinWalker;
+import org.hibernate.util.StringHelper;
+
+/**
+ * Superclass of walkers for collection initializers
+ *
+ * @see CollectionLoader
+ * @see OneToManyJoinWalker
+ * @see BasicCollectionJoinWalker
+ * @author Gavin King
+ */
+public abstract class CollectionJoinWalker extends JoinWalker {
+
+ public CollectionJoinWalker(SessionFactoryImplementor factory, Map enabledFilters) {
+ super( factory, enabledFilters );
+ }
+
+ protected StringBuffer whereString(String alias, String[] columnNames, String subselect, int batchSize) {
+ if (subselect==null) {
+ return super.whereString(alias, columnNames, batchSize);
+ }
+ else {
+ StringBuffer buf = new StringBuffer();
+ if (columnNames.length>1) buf.append('(');
+ buf.append( StringHelper.join(", ", StringHelper.qualify(alias, columnNames) ) );
+ if (columnNames.length>1) buf.append(')');
+ buf.append(" in ")
+ .append('(')
+ .append(subselect)
+ .append(')');
+ return buf;
+ }
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/CollectionJoinWalker.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/CollectionLoader.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/CollectionLoader.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/CollectionLoader.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,46 @@
+//$Id: CollectionLoader.java 7124 2005-06-13 20:27:16Z oneovthafew $
+package org.hibernate.loader.collection;
+
+import java.io.Serializable;
+import java.util.Map;
+
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.loader.OuterJoinLoader;
+import org.hibernate.persister.collection.QueryableCollection;
+import org.hibernate.type.Type;
+
+/**
+ * Superclass for loaders that initialize collections
+ *
+ * @see OneToManyLoader
+ * @see BasicCollectionLoader
+ * @author Gavin King
+ */
+public class CollectionLoader extends OuterJoinLoader implements CollectionInitializer {
+
+ private final QueryableCollection collectionPersister;
+
+ public CollectionLoader(QueryableCollection collectionPersister, SessionFactoryImplementor factory, Map enabledFilters) {
+ super( factory, enabledFilters );
+ this.collectionPersister = collectionPersister;
+ }
+
+ protected boolean isSubselectLoadingEnabled() {
+ return hasSubselectLoadableCollections();
+ }
+
+ public void initialize(Serializable id, SessionImplementor session)
+ throws HibernateException {
+ loadCollection( session, id, getKeyType() );
+ }
+
+ protected Type getKeyType() {
+ return collectionPersister.getKeyType();
+ }
+
+ public String toString() {
+ return getClass().getName() + '(' + collectionPersister.getRole() + ')';
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/OneToManyJoinWalker.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/OneToManyJoinWalker.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/OneToManyJoinWalker.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,128 @@
+//$Id: OneToManyJoinWalker.java 7627 2005-07-24 06:53:06Z oneovthafew $
+package org.hibernate.loader.collection;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import org.hibernate.LockMode;
+import org.hibernate.MappingException;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.loader.BasicLoader;
+import org.hibernate.loader.OuterJoinableAssociation;
+import org.hibernate.persister.collection.QueryableCollection;
+import org.hibernate.persister.entity.OuterJoinLoadable;
+import org.hibernate.sql.JoinFragment;
+import org.hibernate.sql.Select;
+import org.hibernate.util.CollectionHelper;
+import org.hibernate.util.StringHelper;
+
+/**
+ * Walker for one-to-many associations
+ *
+ * @see OneToManyLoader
+ * @author Gavin King
+ */
+public class OneToManyJoinWalker extends CollectionJoinWalker {
+
+ private final QueryableCollection oneToManyPersister;
+
+ protected boolean isDuplicateAssociation(
+ final String foreignKeyTable,
+ final String[] foreignKeyColumns
+ ) {
+ //disable a join back to this same association
+ final boolean isSameJoin = oneToManyPersister.getTableName().equals(foreignKeyTable) &&
+ Arrays.equals( foreignKeyColumns, oneToManyPersister.getKeyColumnNames() );
+ return isSameJoin ||
+ super.isDuplicateAssociation(foreignKeyTable, foreignKeyColumns);
+ }
+
+ public OneToManyJoinWalker(
+ QueryableCollection oneToManyPersister,
+ int batchSize,
+ String subquery,
+ SessionFactoryImplementor factory,
+ Map enabledFilters)
+ throws MappingException {
+
+ super(factory, enabledFilters);
+
+ this.oneToManyPersister = oneToManyPersister;
+
+ final OuterJoinLoadable elementPersister = (OuterJoinLoadable) oneToManyPersister.getElementPersister();
+ final String alias = generateRootAlias( oneToManyPersister.getRole() );
+
+ walkEntityTree(elementPersister, alias);
+
+ List allAssociations = new ArrayList();
+ allAssociations.addAll(associations);
+ allAssociations.add( new OuterJoinableAssociation(
+ oneToManyPersister.getCollectionType(),
+ null,
+ null,
+ alias,
+ JoinFragment.LEFT_OUTER_JOIN,
+ getFactory(),
+ CollectionHelper.EMPTY_MAP
+ ) );
+
+ initPersisters(allAssociations, LockMode.NONE);
+ initStatementString(elementPersister, alias, batchSize, subquery);
+
+ }
+
+ private void initStatementString(
+ final OuterJoinLoadable elementPersister,
+ final String alias,
+ final int batchSize,
+ final String subquery)
+ throws MappingException {
+
+ final int joins = countEntityPersisters( associations );
+ suffixes = BasicLoader.generateSuffixes( joins + 1 );
+
+ final int collectionJoins = countCollectionPersisters( associations ) + 1;
+ collectionSuffixes = BasicLoader.generateSuffixes( joins + 1, collectionJoins );
+
+ StringBuffer whereString = whereString(
+ alias,
+ oneToManyPersister.getKeyColumnNames(),
+ subquery,
+ batchSize
+ );
+ String filter = oneToManyPersister.filterFragment( alias, getEnabledFilters() );
+ whereString.insert( 0, StringHelper.moveAndToBeginning(filter) );
+
+ JoinFragment ojf = mergeOuterJoins(associations);
+ Select select = new Select( getDialect() )
+ .setSelectClause(
+ oneToManyPersister.selectFragment(null, null, alias, suffixes[joins], collectionSuffixes[0], true) +
+ selectString(associations)
+ )
+ .setFromClause(
+ elementPersister.fromTableFragment(alias) +
+ elementPersister.fromJoinFragment(alias, true, true)
+ )
+ .setWhereClause( whereString.toString() )
+ .setOuterJoins(
+ ojf.toFromFragmentString(),
+ ojf.toWhereFragmentString() +
+ elementPersister.whereJoinFragment(alias, true, true)
+ );
+
+ select.setOrderByClause( orderBy( associations, oneToManyPersister.getSQLOrderByString(alias) ) );
+
+ if ( getFactory().getSettings().isCommentsEnabled() ) {
+ select.setComment( "load one-to-many " + oneToManyPersister.getRole() );
+ }
+
+ sql = select.toStatementString();
+ }
+
+ public String toString() {
+ return getClass().getName() + '(' + oneToManyPersister.getRole() + ')';
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/OneToManyJoinWalker.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/OneToManyLoader.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/OneToManyLoader.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/OneToManyLoader.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,67 @@
+//$Id: OneToManyLoader.java 7123 2005-06-13 20:10:20Z oneovthafew $
+package org.hibernate.loader.collection;
+
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.MappingException;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.loader.JoinWalker;
+import org.hibernate.persister.collection.QueryableCollection;
+
+/**
+ * Loads one-to-many associations<br>
+ * <br>
+ * The collection persister must implement <tt>QueryableCOllection<tt>. For
+ * other collections, create a customized subclass of <tt>Loader</tt>.
+ *
+ * @see BasicCollectionLoader
+ * @author Gavin King
+ */
+public class OneToManyLoader extends CollectionLoader {
+
+ private static final Log log = LogFactory.getLog(OneToManyLoader.class);
+
+ public OneToManyLoader(
+ QueryableCollection oneToManyPersister,
+ SessionFactoryImplementor session,
+ Map enabledFilters)
+ throws MappingException {
+ this(oneToManyPersister, 1, session, enabledFilters);
+ }
+
+ public OneToManyLoader(
+ QueryableCollection oneToManyPersister,
+ int batchSize,
+ SessionFactoryImplementor factory,
+ Map enabledFilters)
+ throws MappingException {
+ this(oneToManyPersister, batchSize, null, factory, enabledFilters);
+ }
+
+ public OneToManyLoader(
+ QueryableCollection oneToManyPersister,
+ int batchSize,
+ String subquery,
+ SessionFactoryImplementor factory,
+ Map enabledFilters)
+ throws MappingException {
+
+ super(oneToManyPersister, factory, enabledFilters);
+
+ JoinWalker walker = new OneToManyJoinWalker(
+ oneToManyPersister,
+ batchSize,
+ subquery,
+ factory,
+ enabledFilters
+ );
+ initFromWalker( walker );
+
+ postInstantiate();
+
+ log.debug( "Static select for one-to-many " + oneToManyPersister.getRole() + ": " + getSQLString() );
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/SubselectCollectionLoader.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/SubselectCollectionLoader.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/SubselectCollectionLoader.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,72 @@
+//$Id: SubselectCollectionLoader.java 7670 2005-07-29 05:36:14Z oneovthafew $
+package org.hibernate.loader.collection;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.engine.EntityKey;
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.collection.QueryableCollection;
+import org.hibernate.type.Type;
+
+/**
+ * Implements subselect fetching for a collection
+ * @author Gavin King
+ */
+public class SubselectCollectionLoader extends BasicCollectionLoader {
+
+ private final Serializable[] keys;
+ private final Type[] types;
+ private final Object[] values;
+ private final Map namedParameters;
+ private final Map namedParameterLocMap;
+
+ public SubselectCollectionLoader(
+ QueryableCollection persister,
+ String subquery,
+ Collection entityKeys,
+ QueryParameters queryParameters,
+ Map namedParameterLocMap,
+ SessionFactoryImplementor factory,
+ Map enabledFilters)
+ throws MappingException {
+
+ super(persister, 1, subquery, factory, enabledFilters);
+
+ keys = new Serializable[ entityKeys.size() ];
+ Iterator iter = entityKeys.iterator();
+ int i=0;
+ while ( iter.hasNext() ) {
+ keys[i++] = ( (EntityKey) iter.next() ).getIdentifier();
+ }
+
+ this.namedParameters = queryParameters.getNamedParameters();
+ this.types = queryParameters.getFilteredPositionalParameterTypes();
+ this.values = queryParameters.getFilteredPositionalParameterValues();
+ this.namedParameterLocMap = namedParameterLocMap;
+
+ }
+
+ public void initialize(Serializable id, SessionImplementor session)
+ throws HibernateException {
+ loadCollectionSubselect(
+ session,
+ keys,
+ values,
+ types,
+ namedParameters,
+ getKeyType()
+ );
+ }
+
+ public int[] getNamedParameterLocs(String name) {
+ return (int[]) namedParameterLocMap.get( name );
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/SubselectCollectionLoader.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/SubselectOneToManyLoader.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/SubselectOneToManyLoader.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/SubselectOneToManyLoader.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,72 @@
+//$Id: SubselectOneToManyLoader.java 7670 2005-07-29 05:36:14Z oneovthafew $
+package org.hibernate.loader.collection;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.engine.EntityKey;
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.collection.QueryableCollection;
+import org.hibernate.type.Type;
+
+/**
+ * Implements subselect fetching for a one to many association
+ * @author Gavin King
+ */
+public class SubselectOneToManyLoader extends OneToManyLoader {
+
+ private final Serializable[] keys;
+ private final Type[] types;
+ private final Object[] values;
+ private final Map namedParameters;
+ private final Map namedParameterLocMap;
+
+ public SubselectOneToManyLoader(
+ QueryableCollection persister,
+ String subquery,
+ Collection entityKeys,
+ QueryParameters queryParameters,
+ Map namedParameterLocMap,
+ SessionFactoryImplementor factory,
+ Map enabledFilters)
+ throws MappingException {
+
+ super(persister, 1, subquery, factory, enabledFilters);
+
+ keys = new Serializable[ entityKeys.size() ];
+ Iterator iter = entityKeys.iterator();
+ int i=0;
+ while ( iter.hasNext() ) {
+ keys[i++] = ( (EntityKey) iter.next() ).getIdentifier();
+ }
+
+ this.namedParameters = queryParameters.getNamedParameters();
+ this.types = queryParameters.getFilteredPositionalParameterTypes();
+ this.values = queryParameters.getFilteredPositionalParameterValues();
+ this.namedParameterLocMap = namedParameterLocMap;
+
+ }
+
+ public void initialize(Serializable id, SessionImplementor session)
+ throws HibernateException {
+ loadCollectionSubselect(
+ session,
+ keys,
+ values,
+ types,
+ namedParameters,
+ getKeyType()
+ );
+ }
+
+ public int[] getNamedParameterLocs(String name) {
+ return (int[]) namedParameterLocMap.get( name );
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/SubselectOneToManyLoader.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,8 @@
+<html>
+<head></head>
+<body>
+<p>
+ This package defines collection initializers
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/collection/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/criteria/CriteriaJoinWalker.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/criteria/CriteriaJoinWalker.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/criteria/CriteriaJoinWalker.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,174 @@
+//$Id: CriteriaJoinWalker.java 9116 2006-01-23 21:21:01Z steveebersole $
+package org.hibernate.loader.criteria;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.hibernate.Criteria;
+import org.hibernate.FetchMode;
+import org.hibernate.HibernateException;
+import org.hibernate.LockMode;
+import org.hibernate.MappingException;
+import org.hibernate.engine.CascadeStyle;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.impl.CriteriaImpl;
+import org.hibernate.loader.AbstractEntityJoinWalker;
+import org.hibernate.persister.entity.Joinable;
+import org.hibernate.persister.entity.OuterJoinLoadable;
+import org.hibernate.persister.entity.Queryable;
+import org.hibernate.sql.JoinFragment;
+import org.hibernate.type.AssociationType;
+import org.hibernate.type.Type;
+import org.hibernate.type.TypeFactory;
+import org.hibernate.util.ArrayHelper;
+
+/**
+ * A <tt>JoinWalker</tt> for <tt>Criteria</tt> queries.
+ *
+ * @see CriteriaLoader
+ * @author Gavin King
+ */
+public class CriteriaJoinWalker extends AbstractEntityJoinWalker {
+
+ //TODO: add a CriteriaImplementor interface
+ // this class depends directly upon CriteriaImpl in the impl package...
+
+ private final CriteriaQueryTranslator translator;
+ private final Set querySpaces;
+ private final Type[] resultTypes;
+ //the user visible aliases, which are unknown to the superclass,
+ //these are not the actual "physical" SQL aliases
+ private final String[] userAliases;
+ private final List userAliasList = new ArrayList();
+
+ public Type[] getResultTypes() {
+ return resultTypes;
+ }
+
+ public String[] getUserAliases() {
+ return userAliases;
+ }
+
+ public CriteriaJoinWalker(
+ final OuterJoinLoadable persister,
+ final CriteriaQueryTranslator translator,
+ final SessionFactoryImplementor factory,
+ final CriteriaImpl criteria,
+ final String rootEntityName,
+ final Map enabledFilters)
+ throws HibernateException {
+ super(persister, factory, enabledFilters);
+
+ this.translator = translator;
+
+ querySpaces = translator.getQuerySpaces();
+
+ if ( translator.hasProjection() ) {
+ resultTypes = translator.getProjectedTypes();
+
+ initProjection(
+ translator.getSelect(),
+ translator.getWhereCondition(),
+ translator.getOrderBy(),
+ translator.getGroupBy(),
+ LockMode.NONE
+ );
+ }
+ else {
+ resultTypes = new Type[] { TypeFactory.manyToOne( persister.getEntityName() ) };
+
+ initAll( translator.getWhereCondition(), translator.getOrderBy(), LockMode.NONE );
+ }
+
+ userAliasList.add( criteria.getAlias() ); //root entity comes *last*
+ userAliases = ArrayHelper.toStringArray(userAliasList);
+
+ }
+
+ protected int getJoinType(
+ AssociationType type,
+ FetchMode config,
+ String path,
+ String lhsTable,
+ String[] lhsColumns,
+ boolean nullable,
+ int currentDepth, CascadeStyle cascadeStyle)
+ throws MappingException {
+
+ if ( translator.isJoin(path) ) {
+ return translator.getJoinType( path );
+ }
+ else {
+ if ( translator.hasProjection() ) {
+ return -1;
+ }
+ else {
+ FetchMode fetchMode = translator.getRootCriteria()
+ .getFetchMode(path);
+ if ( isDefaultFetchMode(fetchMode) ) {
+ return super.getJoinType(
+ type,
+ config,
+ path,
+ lhsTable,
+ lhsColumns,
+ nullable,
+ currentDepth, cascadeStyle
+ );
+ }
+ else {
+ if ( fetchMode==FetchMode.JOIN ) {
+ isDuplicateAssociation(lhsTable, lhsColumns, type); //deliberately ignore return value!
+ return getJoinType(nullable, currentDepth);
+ }
+ else {
+ return -1;
+ }
+ }
+ }
+ }
+ }
+
+ private static boolean isDefaultFetchMode(FetchMode fetchMode) {
+ return fetchMode==null || fetchMode==FetchMode.DEFAULT;
+ }
+
+ /**
+ * Use the discriminator, to narrow the select to instances
+ * of the queried subclass, also applying any filters.
+ */
+ protected String getWhereFragment() throws MappingException {
+ return super.getWhereFragment() +
+ ( (Queryable) getPersister() ).filterFragment( getAlias(), getEnabledFilters() );
+ }
+
+ protected String generateTableAlias(int n, String path, Joinable joinable) {
+ if ( joinable.consumesEntityAlias() ) {
+ final Criteria subcriteria = translator.getCriteria(path);
+ String sqlAlias = subcriteria==null ? null : translator.getSQLAlias(subcriteria);
+ if (sqlAlias!=null) {
+ userAliasList.add( subcriteria.getAlias() ); //alias may be null
+ return sqlAlias; //EARLY EXIT
+ }
+ else {
+ userAliasList.add(null);
+ }
+ }
+ return super.generateTableAlias( n + translator.getSQLAliasCount(), path, joinable );
+ }
+
+ protected String generateRootAlias(String tableName) {
+ return CriteriaQueryTranslator.ROOT_SQL_ALIAS;
+ }
+
+ public Set getQuerySpaces() {
+ return querySpaces;
+ }
+
+ public String getComment() {
+ return "criteria query";
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/criteria/CriteriaJoinWalker.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/criteria/CriteriaLoader.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/criteria/CriteriaLoader.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/criteria/CriteriaLoader.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,169 @@
+//$Id: CriteriaLoader.java 11320 2007-03-20 11:50:53Z steve.ebersole at jboss.com $
+package org.hibernate.loader.criteria;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Iterator;
+
+import org.hibernate.HibernateException;
+import org.hibernate.LockMode;
+import org.hibernate.QueryException;
+import org.hibernate.ScrollMode;
+import org.hibernate.ScrollableResults;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.impl.CriteriaImpl;
+import org.hibernate.loader.OuterJoinLoader;
+import org.hibernate.persister.entity.Loadable;
+import org.hibernate.persister.entity.OuterJoinLoadable;
+import org.hibernate.persister.entity.Lockable;
+import org.hibernate.transform.ResultTransformer;
+import org.hibernate.type.Type;
+
+/**
+ * A <tt>Loader</tt> for <tt>Criteria</tt> queries. Note that criteria queries are
+ * more like multi-object <tt>load()</tt>s than like HQL queries.
+ *
+ * @author Gavin King
+ */
+public class CriteriaLoader extends OuterJoinLoader {
+
+ //TODO: this class depends directly upon CriteriaImpl,
+ // in the impl package ... add a CriteriaImplementor
+ // interface
+
+ //NOTE: unlike all other Loaders, this one is NOT
+ // multithreaded, or cacheable!!
+
+ private final CriteriaQueryTranslator translator;
+ private final Set querySpaces;
+ private final Type[] resultTypes;
+ //the user visible aliases, which are unknown to the superclass,
+ //these are not the actual "physical" SQL aliases
+ private final String[] userAliases;
+
+ public CriteriaLoader(
+ final OuterJoinLoadable persister,
+ final SessionFactoryImplementor factory,
+ final CriteriaImpl criteria,
+ final String rootEntityName,
+ final Map enabledFilters)
+ throws HibernateException {
+ super(factory, enabledFilters);
+
+ translator = new CriteriaQueryTranslator(
+ factory,
+ criteria,
+ rootEntityName,
+ CriteriaQueryTranslator.ROOT_SQL_ALIAS
+ );
+
+ querySpaces = translator.getQuerySpaces();
+
+ CriteriaJoinWalker walker = new CriteriaJoinWalker(
+ persister,
+ translator,
+ factory,
+ criteria,
+ rootEntityName,
+ enabledFilters
+ );
+
+ initFromWalker(walker);
+
+ userAliases = walker.getUserAliases();
+ resultTypes = walker.getResultTypes();
+
+ postInstantiate();
+
+ }
+
+ public ScrollableResults scroll(SessionImplementor session, ScrollMode scrollMode)
+ throws HibernateException {
+ QueryParameters qp = translator.getQueryParameters();
+ qp.setScrollMode(scrollMode);
+ return scroll(qp, resultTypes, null, session);
+ }
+
+ public List list(SessionImplementor session)
+ throws HibernateException {
+ return list( session, translator.getQueryParameters(), querySpaces, resultTypes );
+
+ }
+
+ protected Object getResultColumnOrRow(Object[] row, ResultTransformer transformer, ResultSet rs, SessionImplementor session)
+ throws SQLException, HibernateException {
+ final Object[] result;
+ final String[] aliases;
+ if ( translator.hasProjection() ) {
+ Type[] types = translator.getProjectedTypes();
+ result = new Object[types.length];
+ String[] columnAliases = translator.getProjectedColumnAliases();
+ for ( int i=0; i<result.length; i++ ) {
+ result[i] = types[i].nullSafeGet(rs, columnAliases[i], session, null);
+ }
+ aliases = translator.getProjectedAliases();
+ }
+ else {
+ result = row;
+ aliases = userAliases;
+ }
+ return translator.getRootCriteria().getResultTransformer()
+ .transformTuple(result, aliases);
+ }
+
+ public Set getQuerySpaces() {
+ return querySpaces;
+ }
+
+ protected String applyLocks(String sqlSelectString, Map lockModes, Dialect dialect) throws QueryException {
+ if ( lockModes == null || lockModes.isEmpty() ) {
+ return sqlSelectString;
+ }
+
+ final Map aliasedLockModes = new HashMap();
+ final Map keyColumnNames = dialect.forUpdateOfColumns() ? new HashMap() : null;
+ final String[] drivingSqlAliases = getAliases();
+ for ( int i = 0; i < drivingSqlAliases.length; i++ ) {
+ final LockMode lockMode = ( LockMode ) lockModes.get( drivingSqlAliases[i] );
+ if ( lockMode != null ) {
+ final Lockable drivingPersister = ( Lockable ) getEntityPersisters()[i];
+ final String rootSqlAlias = drivingPersister.getRootTableAlias( drivingSqlAliases[i] );
+ aliasedLockModes.put( rootSqlAlias, lockMode );
+ if ( keyColumnNames != null ) {
+ keyColumnNames.put( rootSqlAlias, drivingPersister.getRootTableIdentifierColumnNames() );
+ }
+ }
+ }
+ return dialect.applyLocksToSql( sqlSelectString, aliasedLockModes, keyColumnNames );
+ }
+
+ protected LockMode[] getLockModes(Map lockModes) {
+ final String[] entityAliases = getAliases();
+ if ( entityAliases == null ) {
+ return null;
+ }
+ final int size = entityAliases.length;
+ LockMode[] lockModesArray = new LockMode[size];
+ for ( int i=0; i<size; i++ ) {
+ LockMode lockMode = (LockMode) lockModes.get( entityAliases[i] );
+ lockModesArray[i] = lockMode==null ? LockMode.NONE : lockMode;
+ }
+ return lockModesArray;
+ }
+
+ protected boolean isSubselectLoadingEnabled() {
+ return hasSubselectLoadableCollections();
+ }
+
+ protected List getResultList(List results, ResultTransformer resultTransformer) {
+ return translator.getRootCriteria().getResultTransformer().transformList( results );
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/criteria/CriteriaQueryTranslator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/criteria/CriteriaQueryTranslator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/criteria/CriteriaQueryTranslator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,556 @@
+//$Id: CriteriaQueryTranslator.java 9636 2006-03-16 14:14:48Z max.andersen at jboss.com $
+package org.hibernate.loader.criteria;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import org.apache.commons.collections.SequencedHashMap;
+import org.hibernate.Criteria;
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.LockMode;
+import org.hibernate.MappingException;
+import org.hibernate.QueryException;
+import org.hibernate.hql.ast.util.SessionFactoryHelper;
+import org.hibernate.criterion.CriteriaQuery;
+import org.hibernate.criterion.Projection;
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.RowSelection;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.TypedValue;
+import org.hibernate.impl.CriteriaImpl;
+import org.hibernate.persister.entity.Loadable;
+import org.hibernate.persister.entity.PropertyMapping;
+import org.hibernate.persister.entity.Queryable;
+import org.hibernate.type.AssociationType;
+import org.hibernate.type.Type;
+import org.hibernate.type.NullableType;
+import org.hibernate.util.ArrayHelper;
+import org.hibernate.util.StringHelper;
+
+/**
+ * @author Gavin King
+ */
+public class CriteriaQueryTranslator implements CriteriaQuery {
+
+ public static final String ROOT_SQL_ALIAS = Criteria.ROOT_ALIAS + '_';
+
+ private CriteriaQuery outerQueryTranslator;
+
+ private final CriteriaImpl rootCriteria;
+ private final String rootEntityName;
+ private final String rootSQLAlias;
+ private int aliasCount = 0;
+
+ private final Map criteriaEntityNames = new SequencedHashMap();
+ private final Map criteriaSQLAliasMap = new HashMap();
+ private final Map aliasCriteriaMap = new HashMap();
+ private final Map associationPathCriteriaMap = new SequencedHashMap();
+ private final Map associationPathJoinTypesMap = new SequencedHashMap();
+
+ private final SessionFactoryImplementor sessionFactory;
+
+ public CriteriaQueryTranslator(
+ final SessionFactoryImplementor factory,
+ final CriteriaImpl criteria,
+ final String rootEntityName,
+ final String rootSQLAlias,
+ CriteriaQuery outerQuery) throws HibernateException {
+ this( factory, criteria, rootEntityName, rootSQLAlias );
+ outerQueryTranslator = outerQuery;
+ }
+
+ public CriteriaQueryTranslator(
+ final SessionFactoryImplementor factory,
+ final CriteriaImpl criteria,
+ final String rootEntityName,
+ final String rootSQLAlias) throws HibernateException {
+ this.rootCriteria = criteria;
+ this.rootEntityName = rootEntityName;
+ this.sessionFactory = factory;
+ this.rootSQLAlias = rootSQLAlias;
+ createAliasCriteriaMap();
+ createAssociationPathCriteriaMap();
+ createCriteriaEntityNameMap();
+ createCriteriaSQLAliasMap();
+ }
+
+ public String generateSQLAlias() {
+ return StringHelper.generateAlias( Criteria.ROOT_ALIAS, aliasCount ) + '_';
+ }
+
+ public String getRootSQLALias() {
+ return rootSQLAlias;
+ }
+
+ private Criteria getAliasedCriteria(String alias) {
+ return ( Criteria ) aliasCriteriaMap.get( alias );
+ }
+
+ public boolean isJoin(String path) {
+ return associationPathCriteriaMap.containsKey( path );
+ }
+
+ public int getJoinType(String path) {
+ Integer result = ( Integer ) associationPathJoinTypesMap.get( path );
+ return ( result == null ? Criteria.INNER_JOIN : result.intValue() );
+ }
+
+ public Criteria getCriteria(String path) {
+ return ( Criteria ) associationPathCriteriaMap.get( path );
+ }
+
+ public Set getQuerySpaces() {
+ Set result = new HashSet();
+ Iterator iter = criteriaEntityNames.values().iterator();
+ while ( iter.hasNext() ) {
+ String entityName = ( String ) iter.next();
+ result.addAll( Arrays.asList( getFactory().getEntityPersister( entityName ).getQuerySpaces() ) );
+ }
+ return result;
+ }
+
+ private void createAliasCriteriaMap() {
+ aliasCriteriaMap.put( rootCriteria.getAlias(), rootCriteria );
+ Iterator iter = rootCriteria.iterateSubcriteria();
+ while ( iter.hasNext() ) {
+ Criteria subcriteria = ( Criteria ) iter.next();
+ if ( subcriteria.getAlias() != null ) {
+ Object old = aliasCriteriaMap.put( subcriteria.getAlias(), subcriteria );
+ if ( old != null ) {
+ throw new QueryException( "duplicate alias: " + subcriteria.getAlias() );
+ }
+ }
+ }
+ }
+
+ private void createAssociationPathCriteriaMap() {
+ Iterator iter = rootCriteria.iterateSubcriteria();
+ while ( iter.hasNext() ) {
+ CriteriaImpl.Subcriteria crit = ( CriteriaImpl.Subcriteria ) iter.next();
+ String wholeAssociationPath = getWholeAssociationPath( crit );
+ Object old = associationPathCriteriaMap.put( wholeAssociationPath, crit );
+ if ( old != null ) {
+ throw new QueryException( "duplicate association path: " + wholeAssociationPath );
+ }
+ int joinType = crit.getJoinType();
+ old = associationPathJoinTypesMap.put( wholeAssociationPath, new Integer( joinType ) );
+ if ( old != null ) {
+ // TODO : not so sure this is needed...
+ throw new QueryException( "duplicate association path: " + wholeAssociationPath );
+ }
+ }
+ }
+
+ private String getWholeAssociationPath(CriteriaImpl.Subcriteria subcriteria) {
+ String path = subcriteria.getPath();
+
+ // some messy, complex stuff here, since createCriteria() can take an
+ // aliased path, or a path rooted at the creating criteria instance
+ Criteria parent = null;
+ if ( path.indexOf( '.' ) > 0 ) {
+ // if it is a compound path
+ String testAlias = StringHelper.root( path );
+ if ( !testAlias.equals( subcriteria.getAlias() ) ) {
+ // and the qualifier is not the alias of this criteria
+ // -> check to see if we belong to some criteria other
+ // than the one that created us
+ parent = ( Criteria ) aliasCriteriaMap.get( testAlias );
+ }
+ }
+ if ( parent == null ) {
+ // otherwise assume the parent is the the criteria that created us
+ parent = subcriteria.getParent();
+ }
+ else {
+ path = StringHelper.unroot( path );
+ }
+
+ if ( parent.equals( rootCriteria ) ) {
+ // if its the root criteria, we are done
+ return path;
+ }
+ else {
+ // otherwise, recurse
+ return getWholeAssociationPath( ( CriteriaImpl.Subcriteria ) parent ) + '.' + path;
+ }
+ }
+
+ private void createCriteriaEntityNameMap() {
+ criteriaEntityNames.put( rootCriteria, rootEntityName );
+ Iterator iter = associationPathCriteriaMap.entrySet().iterator();
+ while ( iter.hasNext() ) {
+ Map.Entry me = ( Map.Entry ) iter.next();
+ criteriaEntityNames.put(
+ me.getValue(), //the criteria instance
+ getPathEntityName( ( String ) me.getKey() )
+ );
+ }
+ }
+
+ private String getPathEntityName(String path) {
+ Queryable persister = ( Queryable ) sessionFactory.getEntityPersister( rootEntityName );
+ StringTokenizer tokens = new StringTokenizer( path, "." );
+ String componentPath = "";
+ while ( tokens.hasMoreTokens() ) {
+ componentPath += tokens.nextToken();
+ Type type = persister.toType( componentPath );
+ if ( type.isAssociationType() ) {
+ AssociationType atype = ( AssociationType ) type;
+ persister = ( Queryable ) sessionFactory.getEntityPersister(
+ atype.getAssociatedEntityName( sessionFactory )
+ );
+ componentPath = "";
+ }
+ else if ( type.isComponentType() ) {
+ componentPath += '.';
+ }
+ else {
+ throw new QueryException( "not an association: " + componentPath );
+ }
+ }
+ return persister.getEntityName();
+ }
+
+ public int getSQLAliasCount() {
+ return criteriaSQLAliasMap.size();
+ }
+
+ private void createCriteriaSQLAliasMap() {
+ int i = 0;
+ Iterator criteriaIterator = criteriaEntityNames.entrySet().iterator();
+ while ( criteriaIterator.hasNext() ) {
+ Map.Entry me = ( Map.Entry ) criteriaIterator.next();
+ Criteria crit = ( Criteria ) me.getKey();
+ String alias = crit.getAlias();
+ if ( alias == null ) {
+ alias = ( String ) me.getValue(); // the entity name
+ }
+ criteriaSQLAliasMap.put( crit, StringHelper.generateAlias( alias, i++ ) );
+ }
+ criteriaSQLAliasMap.put( rootCriteria, rootSQLAlias );
+ }
+
+ public CriteriaImpl getRootCriteria() {
+ return rootCriteria;
+ }
+
+ public QueryParameters getQueryParameters() {
+ List values = new ArrayList();
+ List types = new ArrayList();
+ Iterator iter = rootCriteria.iterateExpressionEntries();
+ while ( iter.hasNext() ) {
+ CriteriaImpl.CriterionEntry ce = ( CriteriaImpl.CriterionEntry ) iter.next();
+ TypedValue[] tv = ce.getCriterion().getTypedValues( ce.getCriteria(), this );
+ for ( int i = 0; i < tv.length; i++ ) {
+ values.add( tv[i].getValue() );
+ types.add( tv[i].getType() );
+ }
+ }
+ Object[] valueArray = values.toArray();
+ Type[] typeArray = ArrayHelper.toTypeArray( types );
+
+ RowSelection selection = new RowSelection();
+ selection.setFirstRow( rootCriteria.getFirstResult() );
+ selection.setMaxRows( rootCriteria.getMaxResults() );
+ selection.setTimeout( rootCriteria.getTimeout() );
+ selection.setFetchSize( rootCriteria.getFetchSize() );
+
+ Map lockModes = new HashMap();
+ iter = rootCriteria.getLockModes().entrySet().iterator();
+ while ( iter.hasNext() ) {
+ Map.Entry me = ( Map.Entry ) iter.next();
+ final Criteria subcriteria = getAliasedCriteria( ( String ) me.getKey() );
+ lockModes.put( getSQLAlias( subcriteria ), me.getValue() );
+ }
+ iter = rootCriteria.iterateSubcriteria();
+ while ( iter.hasNext() ) {
+ CriteriaImpl.Subcriteria subcriteria = ( CriteriaImpl.Subcriteria ) iter.next();
+ LockMode lm = subcriteria.getLockMode();
+ if ( lm != null ) {
+ lockModes.put( getSQLAlias( subcriteria ), lm );
+ }
+ }
+
+ return new QueryParameters(
+ typeArray,
+ valueArray,
+ lockModes,
+ selection,
+ rootCriteria.getCacheable(),
+ rootCriteria.getCacheRegion(),
+ rootCriteria.getComment(),
+ rootCriteria.isLookupByNaturalKey(),
+ rootCriteria.getResultTransformer()
+ );
+ }
+
+ public boolean hasProjection() {
+ return rootCriteria.getProjection() != null;
+ }
+
+ public String getGroupBy() {
+ if ( rootCriteria.getProjection().isGrouped() ) {
+ return rootCriteria.getProjection()
+ .toGroupSqlString( rootCriteria.getProjectionCriteria(), this );
+ }
+ else {
+ return "";
+ }
+ }
+
+ public String getSelect() {
+ return rootCriteria.getProjection().toSqlString(
+ rootCriteria.getProjectionCriteria(),
+ 0,
+ this
+ );
+ }
+
+ public Type[] getProjectedTypes() {
+ return rootCriteria.getProjection().getTypes( rootCriteria, this );
+ }
+
+ public String[] getProjectedColumnAliases() {
+ return rootCriteria.getProjection().getColumnAliases( 0 );
+ }
+
+ public String[] getProjectedAliases() {
+ return rootCriteria.getProjection().getAliases();
+ }
+
+ public String getWhereCondition() {
+ StringBuffer condition = new StringBuffer( 30 );
+ Iterator criterionIterator = rootCriteria.iterateExpressionEntries();
+ while ( criterionIterator.hasNext() ) {
+ CriteriaImpl.CriterionEntry entry = ( CriteriaImpl.CriterionEntry ) criterionIterator.next();
+ String sqlString = entry.getCriterion().toSqlString( entry.getCriteria(), this );
+ condition.append( sqlString );
+ if ( criterionIterator.hasNext() ) {
+ condition.append( " and " );
+ }
+ }
+ return condition.toString();
+ }
+
+ public String getOrderBy() {
+ StringBuffer orderBy = new StringBuffer( 30 );
+ Iterator criterionIterator = rootCriteria.iterateOrderings();
+ while ( criterionIterator.hasNext() ) {
+ CriteriaImpl.OrderEntry oe = ( CriteriaImpl.OrderEntry ) criterionIterator.next();
+ orderBy.append( oe.getOrder().toSqlString( oe.getCriteria(), this ) );
+ if ( criterionIterator.hasNext() ) {
+ orderBy.append( ", " );
+ }
+ }
+ return orderBy.toString();
+ }
+
+ public SessionFactoryImplementor getFactory() {
+ return sessionFactory;
+ }
+
+ public String getSQLAlias(Criteria criteria) {
+ return ( String ) criteriaSQLAliasMap.get( criteria );
+ }
+
+ public String getEntityName(Criteria criteria) {
+ return ( String ) criteriaEntityNames.get( criteria );
+ }
+
+ public String getColumn(Criteria criteria, String propertyName) {
+ String[] cols = getColumns( propertyName, criteria );
+ if ( cols.length != 1 ) {
+ throw new QueryException( "property does not map to a single column: " + propertyName );
+ }
+ return cols[0];
+ }
+
+ /**
+ * Get the names of the columns constrained
+ * by this criterion.
+ */
+ public String[] getColumnsUsingProjection(
+ Criteria subcriteria,
+ String propertyName) throws HibernateException {
+
+ //first look for a reference to a projection alias
+ final Projection projection = rootCriteria.getProjection();
+ String[] projectionColumns = projection == null ?
+ null :
+ projection.getColumnAliases( propertyName, 0 );
+
+ if ( projectionColumns == null ) {
+ //it does not refer to an alias of a projection,
+ //look for a property
+ try {
+ return getColumns( propertyName, subcriteria );
+ }
+ catch ( HibernateException he ) {
+ //not found in inner query , try the outer query
+ if ( outerQueryTranslator != null ) {
+ return outerQueryTranslator.getColumnsUsingProjection( subcriteria, propertyName );
+ }
+ else {
+ throw he;
+ }
+ }
+ }
+ else {
+ //it refers to an alias of a projection
+ return projectionColumns;
+ }
+ }
+
+ public String[] getIdentifierColumns(Criteria subcriteria) {
+ String[] idcols =
+ ( ( Loadable ) getPropertyMapping( getEntityName( subcriteria ) ) ).getIdentifierColumnNames();
+ return StringHelper.qualify( getSQLAlias( subcriteria ), idcols );
+ }
+
+ public Type getIdentifierType(Criteria subcriteria) {
+ return ( ( Loadable ) getPropertyMapping( getEntityName( subcriteria ) ) ).getIdentifierType();
+ }
+
+ public TypedValue getTypedIdentifierValue(Criteria subcriteria, Object value) {
+ final Loadable loadable = ( Loadable ) getPropertyMapping( getEntityName( subcriteria ) );
+ return new TypedValue(
+ loadable.getIdentifierType(),
+ value,
+ EntityMode.POJO
+ );
+ }
+
+ private String[] getColumns(
+ String propertyName,
+ Criteria subcriteria) throws HibernateException {
+ return getPropertyMapping( getEntityName( subcriteria, propertyName ) )
+ .toColumns(
+ getSQLAlias( subcriteria, propertyName ),
+ getPropertyName( propertyName )
+ );
+ }
+
+ public Type getTypeUsingProjection(Criteria subcriteria, String propertyName)
+ throws HibernateException {
+
+ //first look for a reference to a projection alias
+ final Projection projection = rootCriteria.getProjection();
+ Type[] projectionTypes = projection == null ?
+ null :
+ projection.getTypes( propertyName, subcriteria, this );
+
+ if ( projectionTypes == null ) {
+ try {
+ //it does not refer to an alias of a projection,
+ //look for a property
+ return getType( subcriteria, propertyName );
+ }
+ catch ( HibernateException he ) {
+ //not found in inner query , try the outer query
+ if ( outerQueryTranslator != null ) {
+ return outerQueryTranslator.getType( subcriteria, propertyName );
+ }
+ else {
+ throw he;
+ }
+ }
+ }
+ else {
+ if ( projectionTypes.length != 1 ) {
+ //should never happen, i think
+ throw new QueryException( "not a single-length projection: " + propertyName );
+ }
+ return projectionTypes[0];
+ }
+ }
+
+ public Type getType(Criteria subcriteria, String propertyName)
+ throws HibernateException {
+ return getPropertyMapping( getEntityName( subcriteria, propertyName ) )
+ .toType( getPropertyName( propertyName ) );
+ }
+
+ /**
+ * Get the a typed value for the given property value.
+ */
+ public TypedValue getTypedValue(Criteria subcriteria, String propertyName, Object value)
+ throws HibernateException {
+ // Detect discriminator values...
+ if ( value instanceof Class ) {
+ Class entityClass = ( Class ) value;
+ Queryable q = SessionFactoryHelper.findQueryableUsingImports( sessionFactory, entityClass.getName() );
+ if ( q != null ) {
+ Type type = q.getDiscriminatorType();
+ String stringValue = q.getDiscriminatorSQLValue();
+ // Convert the string value into the proper type.
+ if ( type instanceof NullableType ) {
+ NullableType nullableType = ( NullableType ) type;
+ value = nullableType.fromStringValue( stringValue );
+ }
+ else {
+ throw new QueryException( "Unsupported discriminator type " + type );
+ }
+ return new TypedValue(
+ type,
+ value,
+ EntityMode.POJO
+ );
+ }
+ }
+ // Otherwise, this is an ordinary value.
+ return new TypedValue(
+ getTypeUsingProjection( subcriteria, propertyName ),
+ value,
+ EntityMode.POJO
+ );
+ }
+
+ private PropertyMapping getPropertyMapping(String entityName)
+ throws MappingException {
+ return ( PropertyMapping ) sessionFactory.getEntityPersister( entityName );
+ }
+
+ //TODO: use these in methods above
+
+ public String getEntityName(Criteria subcriteria, String propertyName) {
+ if ( propertyName.indexOf( '.' ) > 0 ) {
+ String root = StringHelper.root( propertyName );
+ Criteria crit = getAliasedCriteria( root );
+ if ( crit != null ) {
+ return getEntityName( crit );
+ }
+ }
+ return getEntityName( subcriteria );
+ }
+
+ public String getSQLAlias(Criteria criteria, String propertyName) {
+ if ( propertyName.indexOf( '.' ) > 0 ) {
+ String root = StringHelper.root( propertyName );
+ Criteria subcriteria = getAliasedCriteria( root );
+ if ( subcriteria != null ) {
+ return getSQLAlias( subcriteria );
+ }
+ }
+ return getSQLAlias( criteria );
+ }
+
+ public String getPropertyName(String propertyName) {
+ if ( propertyName.indexOf( '.' ) > 0 ) {
+ String root = StringHelper.root( propertyName );
+ Criteria crit = getAliasedCriteria( root );
+ if ( crit != null ) {
+ return propertyName.substring( root.length() + 1 );
+ }
+ }
+ return propertyName;
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/criteria/CriteriaQueryTranslator.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/criteria/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/criteria/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/criteria/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,8 @@
+<html>
+<head></head>
+<body>
+<p>
+ This package defines the criteria query compiler and loader
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/criteria/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/CollectionFetchReturn.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/CollectionFetchReturn.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/CollectionFetchReturn.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,35 @@
+package org.hibernate.loader.custom;
+
+import org.hibernate.loader.CollectionAliases;
+import org.hibernate.loader.EntityAliases;
+import org.hibernate.LockMode;
+
+/**
+ * Spefically a fetch return that refers to a collection association.
+ *
+ * @author Steve Ebersole
+ */
+public class CollectionFetchReturn extends FetchReturn {
+ private final CollectionAliases collectionAliases;
+ private final EntityAliases elementEntityAliases;
+
+ public CollectionFetchReturn(
+ String alias,
+ NonScalarReturn owner,
+ String ownerProperty,
+ CollectionAliases collectionAliases,
+ EntityAliases elementEntityAliases,
+ LockMode lockMode) {
+ super( owner, ownerProperty, alias, lockMode );
+ this.collectionAliases = collectionAliases;
+ this.elementEntityAliases = elementEntityAliases;
+ }
+
+ public CollectionAliases getCollectionAliases() {
+ return collectionAliases;
+ }
+
+ public EntityAliases getElementEntityAliases() {
+ return elementEntityAliases;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/CollectionReturn.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/CollectionReturn.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/CollectionReturn.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,60 @@
+package org.hibernate.loader.custom;
+
+import org.hibernate.LockMode;
+import org.hibernate.loader.CollectionAliases;
+import org.hibernate.loader.EntityAliases;
+
+/**
+ * Represents a return which names a collection role; it
+ * is used in defining a custom query for loading an entity's
+ * collection in non-fetching scenarios (i.e., loading the collection
+ * itself as the "root" of the result).
+ *
+ * @author Steve Ebersole
+ */
+public class CollectionReturn extends NonScalarReturn {
+ private final String ownerEntityName;
+ private final String ownerProperty;
+ private final CollectionAliases collectionAliases;
+ private final EntityAliases elementEntityAliases;
+
+ public CollectionReturn(
+ String alias,
+ String ownerEntityName,
+ String ownerProperty,
+ CollectionAliases collectionAliases,
+ EntityAliases elementEntityAliases,
+ LockMode lockMode) {
+ super( alias, lockMode );
+ this.ownerEntityName = ownerEntityName;
+ this.ownerProperty = ownerProperty;
+ this.collectionAliases = collectionAliases;
+ this.elementEntityAliases = elementEntityAliases;
+ }
+
+ /**
+ * Returns the class owning the collection.
+ *
+ * @return The class owning the collection.
+ */
+ public String getOwnerEntityName() {
+ return ownerEntityName;
+ }
+
+ /**
+ * Returns the name of the property representing the collection from the {@link #getOwnerEntityName}.
+ *
+ * @return The name of the property representing the collection on the owner class.
+ */
+ public String getOwnerProperty() {
+ return ownerProperty;
+ }
+
+ public CollectionAliases getCollectionAliases() {
+ return collectionAliases;
+ }
+
+ public EntityAliases getElementEntityAliases() {
+ return elementEntityAliases;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/ColumnCollectionAliases.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/ColumnCollectionAliases.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/ColumnCollectionAliases.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,127 @@
+package org.hibernate.loader.custom;
+
+import java.util.Map;
+
+import org.hibernate.loader.CollectionAliases;
+import org.hibernate.persister.collection.SQLLoadableCollection;
+import org.hibernate.util.StringHelper;
+
+/**
+ * CollectionAliases that uses columnnames instead of generated aliases.
+ * Aliases can still be overwritten via <return-property>
+ *
+ * @author Max Rydahl Andersen
+ *
+ */
+public class ColumnCollectionAliases implements CollectionAliases {
+ private final String[] keyAliases;
+ private final String[] indexAliases;
+ private final String[] elementAliases;
+ private final String identifierAlias;
+ private Map userProvidedAliases;
+
+
+ public ColumnCollectionAliases(Map userProvidedAliases, SQLLoadableCollection persister) {
+ this.userProvidedAliases = userProvidedAliases;
+
+ this.keyAliases = getUserProvidedAliases(
+ "key",
+ persister.getKeyColumnNames()
+ );
+
+ this.indexAliases = getUserProvidedAliases(
+ "index",
+ persister.getIndexColumnNames()
+ );
+
+ this.elementAliases = getUserProvidedAliases( "element",
+ persister.getElementColumnNames()
+ );
+
+ this.identifierAlias = getUserProvidedAlias( "id",
+ persister.getIdentifierColumnName()
+ );
+
+ }
+
+
+ /**
+ * Returns the suffixed result-set column-aliases for columns making up the key for this collection (i.e., its FK to
+ * its owner).
+ *
+ * @return The key result-set column aliases.
+ */
+ public String[] getSuffixedKeyAliases() {
+ return keyAliases;
+ }
+
+ /**
+ * Returns the suffixed result-set column-aliases for the collumns making up the collection's index (map or list).
+ *
+ * @return The index result-set column aliases.
+ */
+ public String[] getSuffixedIndexAliases() {
+ return indexAliases;
+ }
+
+ /**
+ * Returns the suffixed result-set column-aliases for the columns making up the collection's elements.
+ *
+ * @return The element result-set column aliases.
+ */
+ public String[] getSuffixedElementAliases() {
+ return elementAliases;
+ }
+
+ /**
+ * Returns the suffixed result-set column-aliases for the column defining the collection's identifier (if any).
+ *
+ * @return The identifier result-set column aliases.
+ */
+ public String getSuffixedIdentifierAlias() {
+ return identifierAlias;
+ }
+
+ /**
+ * Returns the suffix used to unique the column aliases for this particular alias set.
+ *
+ * @return The uniqued column alias suffix.
+ */
+ public String getSuffix() {
+ return "";
+ }
+
+ public String toString() {
+ return super.toString() + " [ suffixedKeyAliases=[" + join( keyAliases ) +
+ "], suffixedIndexAliases=[" + join( indexAliases ) +
+ "], suffixedElementAliases=[" + join( elementAliases ) +
+ "], suffixedIdentifierAlias=[" + identifierAlias + "]]";
+ }
+
+ private String join(String[] aliases) {
+ if ( aliases == null) return null;
+
+ return StringHelper.join( ", ", aliases );
+ }
+
+ private String[] getUserProvidedAliases(String propertyPath, String[] defaultAliases) {
+ String[] result = (String[]) userProvidedAliases.get(propertyPath);
+ if (result==null) {
+ return defaultAliases;
+ }
+ else {
+ return result;
+ }
+ }
+
+ private String getUserProvidedAlias(String propertyPath, String defaultAlias) {
+ String[] columns = (String[]) userProvidedAliases.get(propertyPath);
+ if (columns==null) {
+ return defaultAlias;
+ }
+ else {
+ return columns[0];
+ }
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/CustomLoader.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/CustomLoader.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/CustomLoader.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,569 @@
+//$Id: CustomLoader.java 10087 2006-07-06 12:21:30Z steve.ebersole at jboss.com $
+package org.hibernate.loader.custom;
+
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.HashSet;
+
+import org.hibernate.HibernateException;
+import org.hibernate.LockMode;
+import org.hibernate.QueryException;
+import org.hibernate.ScrollableResults;
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.hql.HolderInstantiator;
+import org.hibernate.loader.CollectionAliases;
+import org.hibernate.loader.EntityAliases;
+import org.hibernate.loader.Loader;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.persister.collection.QueryableCollection;
+import org.hibernate.persister.entity.Loadable;
+import org.hibernate.persister.entity.Queryable;
+import org.hibernate.transform.ResultTransformer;
+import org.hibernate.type.Type;
+import org.hibernate.type.TypeFactory;
+import org.hibernate.type.EntityType;
+import org.hibernate.type.CollectionType;
+import org.hibernate.util.ArrayHelper;
+
+/**
+ * Extension point for loaders which use a SQL result set with "unexpected" column aliases.
+ *
+ * @author Gavin King
+ * @author Steve Ebersole
+ */
+public class CustomLoader extends Loader {
+
+ // Currently *not* cachable if autodiscover types is in effect (e.g. "select * ...")
+
+ private final String sql;
+ private final Set querySpaces = new HashSet();
+ private final Map namedParameterBindPoints;
+
+ private final Queryable[] entityPersisters;
+ private final int[] entiytOwners;
+ private final EntityAliases[] entityAliases;
+
+ private final QueryableCollection[] collectionPersisters;
+ private final int[] collectionOwners;
+ private final CollectionAliases[] collectionAliases;
+
+ private final LockMode[] lockModes;
+// private final String[] sqlAliases;
+// private final String[] sqlAliasSuffixes;
+ private final ResultRowProcessor rowProcessor;
+
+ // this is only needed (afaict) for processing results from the query cache;
+ // however, this cannot possibly work in the case of discovered types...
+ private Type[] resultTypes;
+
+ // this is only needed (afaict) for ResultTransformer processing...
+ private String[] transformerAliases;
+
+
+ public CustomLoader(CustomQuery customQuery, SessionFactoryImplementor factory) {
+ super( factory );
+
+ this.sql = customQuery.getSQL();
+ this.querySpaces.addAll( customQuery.getQuerySpaces() );
+ this.namedParameterBindPoints = customQuery.getNamedParameterBindPoints();
+
+ List entityPersisters = new ArrayList();
+ List entityOwners = new ArrayList();
+ List entityAliases = new ArrayList();
+
+ List collectionPersisters = new ArrayList();
+ List collectionOwners = new ArrayList();
+ List collectionAliases = new ArrayList();
+
+ List lockModes = new ArrayList();
+ List resultColumnProcessors = new ArrayList();
+ List nonScalarReturnList = new ArrayList();
+ List resultTypes = new ArrayList();
+ List specifiedAliases = new ArrayList();
+ int returnableCounter = 0;
+ boolean hasScalars = false;
+
+ Iterator itr = customQuery.getCustomQueryReturns().iterator();
+ while ( itr.hasNext() ) {
+ final Return rtn = ( Return ) itr.next();
+ if ( rtn instanceof ScalarReturn ) {
+ ScalarReturn scalarRtn = ( ScalarReturn ) rtn;
+ resultTypes.add( scalarRtn.getType() );
+ specifiedAliases.add( scalarRtn.getColumnAlias() );
+ resultColumnProcessors.add(
+ new ScalarResultColumnProcessor(
+ scalarRtn.getColumnAlias(),
+ scalarRtn.getType()
+ )
+ );
+ hasScalars = true;
+ }
+ else if ( rtn instanceof RootReturn ) {
+ RootReturn rootRtn = ( RootReturn ) rtn;
+ Queryable persister = ( Queryable ) factory.getEntityPersister( rootRtn.getEntityName() );
+ entityPersisters.add( persister );
+ lockModes.add( rootRtn.getLockMode() );
+ resultColumnProcessors.add( new NonScalarResultColumnProcessor( returnableCounter++ ) );
+ nonScalarReturnList.add( rtn );
+ entityOwners.add( new Integer( -1 ) );
+ resultTypes.add( persister.getType() );
+ specifiedAliases.add( rootRtn.getAlias() );
+ entityAliases.add( rootRtn.getEntityAliases() );
+ ArrayHelper.addAll( querySpaces, persister.getQuerySpaces() );
+ }
+ else if ( rtn instanceof CollectionReturn ) {
+ CollectionReturn collRtn = ( CollectionReturn ) rtn;
+ String role = collRtn.getOwnerEntityName() + "." + collRtn.getOwnerProperty();
+ QueryableCollection persister = ( QueryableCollection ) factory.getCollectionPersister( role );
+ collectionPersisters.add( persister );
+ lockModes.add( collRtn.getLockMode() );
+ resultColumnProcessors.add( new NonScalarResultColumnProcessor( returnableCounter++ ) );
+ nonScalarReturnList.add( rtn );
+ collectionOwners.add( new Integer( -1 ) );
+ resultTypes.add( persister.getType() );
+ specifiedAliases.add( collRtn.getAlias() );
+ collectionAliases.add( collRtn.getCollectionAliases() );
+ // determine if the collection elements are entities...
+ Type elementType = persister.getElementType();
+ if ( elementType.isEntityType() ) {
+ Queryable elementPersister = ( Queryable ) ( ( EntityType ) elementType ).getAssociatedJoinable( factory );
+ entityPersisters.add( elementPersister );
+ entityOwners.add( new Integer( -1 ) );
+ entityAliases.add( collRtn.getElementEntityAliases() );
+ ArrayHelper.addAll( querySpaces, elementPersister.getQuerySpaces() );
+ }
+ }
+ else if ( rtn instanceof EntityFetchReturn ) {
+ EntityFetchReturn fetchRtn = ( EntityFetchReturn ) rtn;
+ NonScalarReturn ownerDescriptor = fetchRtn.getOwner();
+ int ownerIndex = nonScalarReturnList.indexOf( ownerDescriptor );
+ entityOwners.add( new Integer( ownerIndex ) );
+ lockModes.add( fetchRtn.getLockMode() );
+ Queryable ownerPersister = determineAppropriateOwnerPersister( ownerDescriptor );
+ EntityType fetchedType = ( EntityType ) ownerPersister.getPropertyType( fetchRtn.getOwnerProperty() );
+ String entityName = fetchedType.getAssociatedEntityName( getFactory() );
+ Queryable persister = ( Queryable ) factory.getEntityPersister( entityName );
+ entityPersisters.add( persister );
+ nonScalarReturnList.add( rtn );
+ specifiedAliases.add( fetchRtn.getAlias() );
+ entityAliases.add( fetchRtn.getEntityAliases() );
+ ArrayHelper.addAll( querySpaces, persister.getQuerySpaces() );
+ }
+ else if ( rtn instanceof CollectionFetchReturn ) {
+ CollectionFetchReturn fetchRtn = ( CollectionFetchReturn ) rtn;
+ NonScalarReturn ownerDescriptor = fetchRtn.getOwner();
+ int ownerIndex = nonScalarReturnList.indexOf( ownerDescriptor );
+ collectionOwners.add( new Integer( ownerIndex ) );
+ lockModes.add( fetchRtn.getLockMode() );
+ Queryable ownerPersister = determineAppropriateOwnerPersister( ownerDescriptor );
+ String role = ownerPersister.getEntityName() + '.' + fetchRtn.getOwnerProperty();
+ QueryableCollection persister = ( QueryableCollection ) factory.getCollectionPersister( role );
+ collectionPersisters.add( persister );
+ nonScalarReturnList.add( rtn );
+ specifiedAliases.add( fetchRtn.getAlias() );
+ collectionAliases.add( fetchRtn.getCollectionAliases() );
+ // determine if the collection elements are entities...
+ Type elementType = persister.getElementType();
+ if ( elementType.isEntityType() ) {
+ Queryable elementPersister = ( Queryable ) ( ( EntityType ) elementType ).getAssociatedJoinable( factory );
+ entityPersisters.add( elementPersister );
+ entityOwners.add( new Integer( ownerIndex ) );
+ entityAliases.add( fetchRtn.getElementEntityAliases() );
+ ArrayHelper.addAll( querySpaces, elementPersister.getQuerySpaces() );
+ }
+ }
+ else {
+ throw new HibernateException( "unexpected custom query return type : " + rtn.getClass().getName() );
+ }
+ }
+
+ this.entityPersisters = new Queryable[ entityPersisters.size() ];
+ for ( int i = 0; i < entityPersisters.size(); i++ ) {
+ this.entityPersisters[i] = ( Queryable ) entityPersisters.get( i );
+ }
+ this.entiytOwners = ArrayHelper.toIntArray( entityOwners );
+ this.entityAliases = new EntityAliases[ entityAliases.size() ];
+ for ( int i = 0; i < entityAliases.size(); i++ ) {
+ this.entityAliases[i] = ( EntityAliases ) entityAliases.get( i );
+ }
+
+ this.collectionPersisters = new QueryableCollection[ collectionPersisters.size() ];
+ for ( int i = 0; i < collectionPersisters.size(); i++ ) {
+ this.collectionPersisters[i] = ( QueryableCollection ) collectionPersisters.get( i );
+ }
+ this.collectionOwners = ArrayHelper.toIntArray( collectionOwners );
+ this.collectionAliases = new CollectionAliases[ collectionAliases.size() ];
+ for ( int i = 0; i < collectionAliases.size(); i++ ) {
+ this.collectionAliases[i] = ( CollectionAliases ) collectionAliases.get( i );
+ }
+
+ this.lockModes = new LockMode[ lockModes.size() ];
+ for ( int i = 0; i < lockModes.size(); i++ ) {
+ this.lockModes[i] = ( LockMode ) lockModes.get( i );
+ }
+
+ this.resultTypes = ArrayHelper.toTypeArray( resultTypes );
+ this.transformerAliases = ArrayHelper.toStringArray( specifiedAliases );
+
+ this.rowProcessor = new ResultRowProcessor(
+ hasScalars,
+ ( ResultColumnProcessor[] ) resultColumnProcessors.toArray( new ResultColumnProcessor[ resultColumnProcessors.size() ] )
+ );
+ }
+
+ private Queryable determineAppropriateOwnerPersister(NonScalarReturn ownerDescriptor) {
+ String entityName = null;
+ if ( ownerDescriptor instanceof RootReturn ) {
+ entityName = ( ( RootReturn ) ownerDescriptor ).getEntityName();
+ }
+ else if ( ownerDescriptor instanceof CollectionReturn ) {
+ CollectionReturn collRtn = ( CollectionReturn ) ownerDescriptor;
+ String role = collRtn.getOwnerEntityName() + "." + collRtn.getOwnerProperty();
+ CollectionPersister persister = getFactory().getCollectionPersister( role );
+ EntityType ownerType = ( EntityType ) persister.getElementType();
+ entityName = ownerType.getAssociatedEntityName( getFactory() );
+ }
+ else if ( ownerDescriptor instanceof FetchReturn ) {
+ FetchReturn fetchRtn = ( FetchReturn ) ownerDescriptor;
+ Queryable persister = determineAppropriateOwnerPersister( fetchRtn.getOwner() );
+ Type ownerType = persister.getPropertyType( fetchRtn.getOwnerProperty() );
+ if ( ownerType.isEntityType() ) {
+ entityName = ( ( EntityType ) ownerType ).getAssociatedEntityName( getFactory() );
+ }
+ else if ( ownerType.isCollectionType() ) {
+ Type ownerCollectionElementType = ( ( CollectionType ) ownerType ).getElementType( getFactory() );
+ if ( ownerCollectionElementType.isEntityType() ) {
+ entityName = ( ( EntityType ) ownerCollectionElementType ).getAssociatedEntityName( getFactory() );
+ }
+ }
+ }
+
+ if ( entityName == null ) {
+ throw new HibernateException( "Could not determine fetch owner : " + ownerDescriptor );
+ }
+
+ return ( Queryable ) getFactory().getEntityPersister( entityName );
+ }
+
+ protected String getQueryIdentifier() {
+ return sql;
+ }
+
+ protected String getSQLString() {
+ return sql;
+ }
+
+ public Set getQuerySpaces() {
+ return querySpaces;
+ }
+
+ protected LockMode[] getLockModes(Map lockModesMap) {
+ return lockModes;
+ }
+
+ protected Loadable[] getEntityPersisters() {
+ return entityPersisters;
+ }
+
+ protected CollectionPersister[] getCollectionPersisters() {
+ return collectionPersisters;
+ }
+
+ protected int[] getCollectionOwners() {
+ return collectionOwners;
+ }
+
+ protected int[] getOwners() {
+ return entiytOwners;
+ }
+
+ public List list(SessionImplementor session, QueryParameters queryParameters) throws HibernateException {
+ return list( session, queryParameters, querySpaces, resultTypes );
+ }
+
+ public ScrollableResults scroll(
+ final QueryParameters queryParameters,
+ final SessionImplementor session) throws HibernateException {
+ return scroll(
+ queryParameters,
+ resultTypes,
+ getHolderInstantiator( queryParameters.getResultTransformer(), getReturnAliasesForTransformer() ),
+ session
+ );
+ }
+
+ static private HolderInstantiator getHolderInstantiator(ResultTransformer resultTransformer, String[] queryReturnAliases) {
+ if ( resultTransformer != null ) {
+ return HolderInstantiator.NOOP_INSTANTIATOR;
+ }
+ else {
+ return new HolderInstantiator(resultTransformer, queryReturnAliases);
+ }
+ }
+
+ protected Object getResultColumnOrRow(
+ Object[] row,
+ ResultTransformer transformer,
+ ResultSet rs,
+ SessionImplementor session) throws SQLException, HibernateException {
+ return rowProcessor.buildResultRow( row, rs, transformer != null, session );
+ }
+
+ protected List getResultList(List results, ResultTransformer resultTransformer) throws QueryException {
+ // meant to handle dynamic instantiation queries...(Copy from QueryLoader)
+ HolderInstantiator holderInstantiator = HolderInstantiator.getHolderInstantiator(
+ null,
+ resultTransformer,
+ getReturnAliasesForTransformer()
+ );
+ if ( holderInstantiator.isRequired() ) {
+ for ( int i = 0; i < results.size(); i++ ) {
+ Object[] row = ( Object[] ) results.get( i );
+ Object result = holderInstantiator.instantiate(row);
+ results.set( i, result );
+ }
+
+ return resultTransformer.transformList(results);
+ }
+ else {
+ return results;
+ }
+ }
+
+ private String[] getReturnAliasesForTransformer() {
+ return transformerAliases;
+ }
+
+ protected EntityAliases[] getEntityAliases() {
+ return entityAliases;
+ }
+
+ protected CollectionAliases[] getCollectionAliases() {
+ return collectionAliases;
+ }
+
+ public int[] getNamedParameterLocs(String name) throws QueryException {
+ Object loc = namedParameterBindPoints.get( name );
+ if ( loc == null ) {
+ throw new QueryException(
+ "Named parameter does not appear in Query: " + name,
+ sql
+ );
+ }
+ if ( loc instanceof Integer ) {
+ return new int[] { ( ( Integer ) loc ).intValue() };
+ }
+ else {
+ return ArrayHelper.toIntArray( ( List ) loc );
+ }
+ }
+
+
+ public class ResultRowProcessor {
+ private final boolean hasScalars;
+ private ResultColumnProcessor[] columnProcessors;
+
+ public ResultRowProcessor(boolean hasScalars, ResultColumnProcessor[] columnProcessors) {
+ this.hasScalars = hasScalars || ( columnProcessors == null || columnProcessors.length == 0 );
+ this.columnProcessors = columnProcessors;
+ }
+
+ public void prepareForAutoDiscovery(Metadata metadata) throws SQLException {
+ if ( columnProcessors == null || columnProcessors.length == 0 ) {
+ int columns = metadata.getColumnCount();
+ columnProcessors = new ResultColumnProcessor[ columns ];
+ for ( int i = 1; i <= columns; i++ ) {
+ columnProcessors[ i - 1 ] = new ScalarResultColumnProcessor( i );
+ }
+
+ }
+ }
+
+ /**
+ * Build a logical result row.
+ * <p/>
+ * At this point, Loader has already processed all non-scalar result data. We
+ * just need to account for scalar result data here...
+ *
+ * @param data Entity data defined as "root returns" and already handled by the
+ * normal Loader mechanism.
+ * @param resultSet The JDBC result set (positioned at the row currently being processed).
+ * @param hasTransformer Does this query have an associated {@link ResultTransformer}
+ * @param session The session from which the query request originated.
+ * @return The logical result row
+ * @throws SQLException
+ * @throws HibernateException
+ */
+ public Object buildResultRow(
+ Object[] data,
+ ResultSet resultSet,
+ boolean hasTransformer,
+ SessionImplementor session) throws SQLException, HibernateException {
+ Object[] resultRow;
+ if ( !hasScalars ) {
+ resultRow = data;
+ }
+ else {
+ // build an array with indices equal to the total number
+ // of actual returns in the result Hibernate will return
+ // for this query (scalars + non-scalars)
+ resultRow = new Object[ columnProcessors.length ];
+ for ( int i = 0; i < columnProcessors.length; i++ ) {
+ resultRow[i] = columnProcessors[i].extract( data, resultSet, session );
+ }
+ }
+
+ return ( hasTransformer )
+ ? resultRow
+ : ( resultRow.length == 1 )
+ ? resultRow[0]
+ : resultRow;
+ }
+ }
+
+ private static interface ResultColumnProcessor {
+ public Object extract(Object[] data, ResultSet resultSet, SessionImplementor session) throws SQLException, HibernateException;
+ public void performDiscovery(Metadata metadata, List types, List aliases) throws SQLException, HibernateException;
+ }
+
+ public class NonScalarResultColumnProcessor implements ResultColumnProcessor {
+ private final int position;
+
+ public NonScalarResultColumnProcessor(int position) {
+ this.position = position;
+ }
+
+ public Object extract(
+ Object[] data,
+ ResultSet resultSet,
+ SessionImplementor session) throws SQLException, HibernateException {
+ return data[ position ];
+ }
+
+ public void performDiscovery(Metadata metadata, List types, List aliases) {
+ }
+
+ }
+
+ public class ScalarResultColumnProcessor implements ResultColumnProcessor {
+ private int position = -1;
+ private String alias;
+ private Type type;
+
+ public ScalarResultColumnProcessor(int position) {
+ this.position = position;
+ }
+
+ public ScalarResultColumnProcessor(String alias, Type type) {
+ this.alias = alias;
+ this.type = type;
+ }
+
+ public Object extract(
+ Object[] data,
+ ResultSet resultSet,
+ SessionImplementor session) throws SQLException, HibernateException {
+ return type.nullSafeGet( resultSet, alias, session, null );
+ }
+
+ public void performDiscovery(Metadata metadata, List types, List aliases) throws SQLException {
+ if ( alias == null ) {
+ alias = metadata.getColumnName( position );
+ }
+ else if ( position < 0 ) {
+ position = metadata.resolveColumnPosition( alias );
+ }
+ if ( type == null ) {
+ type = metadata.getHibernateType( position );
+ }
+ types.add( type );
+ aliases.add( alias );
+ }
+ }
+
+ protected void autoDiscoverTypes(ResultSet rs) {
+ try {
+ Metadata metadata = new Metadata( getFactory(), rs );
+ List aliases = new ArrayList();
+ List types = new ArrayList();
+
+ rowProcessor.prepareForAutoDiscovery( metadata );
+
+ for ( int i = 0; i < rowProcessor.columnProcessors.length; i++ ) {
+ rowProcessor.columnProcessors[i].performDiscovery( metadata, types, aliases );
+ }
+
+ resultTypes = ArrayHelper.toTypeArray( types );
+ transformerAliases = ArrayHelper.toStringArray( aliases );
+ }
+ catch ( SQLException e ) {
+ throw new HibernateException( "Exception while trying to autodiscover types.", e );
+ }
+ }
+
+ private static class Metadata {
+ private final SessionFactoryImplementor factory;
+ private final ResultSet resultSet;
+ private final ResultSetMetaData resultSetMetaData;
+
+ public Metadata(SessionFactoryImplementor factory, ResultSet resultSet) throws HibernateException {
+ try {
+ this.factory = factory;
+ this.resultSet = resultSet;
+ this.resultSetMetaData = resultSet.getMetaData();
+ }
+ catch( SQLException e ) {
+ throw new HibernateException( "Could not extract result set metadata", e );
+ }
+ }
+
+ public int getColumnCount() throws HibernateException {
+ try {
+ return resultSetMetaData.getColumnCount();
+ }
+ catch( SQLException e ) {
+ throw new HibernateException( "Could not determine result set column count", e );
+ }
+ }
+
+ public int resolveColumnPosition(String columnName) throws HibernateException {
+ try {
+ return resultSet.findColumn( columnName );
+ }
+ catch( SQLException e ) {
+ throw new HibernateException( "Could not resolve column name in result set [" + columnName + "]", e );
+ }
+ }
+
+ public String getColumnName(int position) throws HibernateException {
+ try {
+ return resultSetMetaData.getColumnName( position );
+ }
+ catch( SQLException e ) {
+ throw new HibernateException( "Could not resolve column name [" + position + "]", e );
+ }
+ }
+
+ public Type getHibernateType(int columnPos) throws SQLException {
+ int columnType = resultSetMetaData.getColumnType( columnPos );
+ int scale = resultSetMetaData.getScale( columnPos );
+ int precision = resultSetMetaData.getPrecision( columnPos );
+ return TypeFactory.heuristicType(
+ factory.getDialect().getHibernateTypeName(
+ columnType,
+ precision,
+ precision,
+ scale
+ )
+ );
+ }
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/CustomLoader.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/CustomQuery.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/CustomQuery.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/CustomQuery.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,55 @@
+//$Id: CustomQuery.java 10018 2006-06-15 05:21:06Z steve.ebersole at jboss.com $
+package org.hibernate.loader.custom;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.List;
+
+/**
+ * Extension point allowing any SQL query with named and positional parameters
+ * to be executed by Hibernate, returning managed entities, collections and
+ * simple scalar values.
+ *
+ * @author Gavin King
+ * @author Steve Ebersole
+ */
+public interface CustomQuery {
+ /**
+ * The SQL query string to be performed.
+ *
+ * @return The SQL statement string.
+ */
+ public String getSQL();
+
+ /**
+ * Any query spaces to apply to the query execution. Query spaces are
+ * used in Hibernate's auto-flushing mechanism to determine which
+ * entities need to be checked for pending changes.
+ *
+ * @return The query spaces
+ */
+ public Set getQuerySpaces();
+
+ /**
+ * A map representing positions within the supplied {@link #getSQL query} to
+ * which we need to bind named parameters.
+ * <p/>
+ * Optional, may return null if no named parameters.
+ * <p/>
+ * The structure of the returned map (if one) as follows:<ol>
+ * <li>The keys into the map are the named parameter names</li>
+ * <li>The corresponding value is either an {@link Integer} if the
+ * parameter occurs only once in the query; or a List of Integers if the
+ * parameter occurs more than once</li>
+ * </ol>
+ */
+ public Map getNamedParameterBindPoints();
+
+ /**
+ * A collection of {@link Return descriptors} describing the
+ * JDBC result set to be expected and how to map this result set.
+ *
+ * @return List of return descriptors.
+ */
+ public List getCustomQueryReturns();
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/CustomQuery.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/EntityFetchReturn.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/EntityFetchReturn.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/EntityFetchReturn.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,27 @@
+package org.hibernate.loader.custom;
+
+import org.hibernate.loader.EntityAliases;
+import org.hibernate.LockMode;
+
+/**
+ * Spefically a fetch return that refers to an entity association.
+ *
+ * @author Steve Ebersole
+ */
+public class EntityFetchReturn extends FetchReturn {
+ private final EntityAliases entityAliases;
+
+ public EntityFetchReturn(
+ String alias,
+ EntityAliases entityAliases,
+ NonScalarReturn owner,
+ String ownerProperty,
+ LockMode lockMode) {
+ super( owner, ownerProperty, alias, lockMode );
+ this.entityAliases = entityAliases;
+ }
+
+ public EntityAliases getEntityAliases() {
+ return entityAliases;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/FetchReturn.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/FetchReturn.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/FetchReturn.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,49 @@
+package org.hibernate.loader.custom;
+
+import org.hibernate.LockMode;
+
+/**
+ * Represents a return which names a fetched association.
+ *
+ * @author Steve Ebersole
+ */
+public abstract class FetchReturn extends NonScalarReturn {
+ private final NonScalarReturn owner;
+ private final String ownerProperty;
+
+ /**
+ * Creates a return descriptor for an association fetch.
+ *
+ * @param owner The return descriptor for the owner of the fetch
+ * @param ownerProperty The name of the property represernting the association being fetched
+ * @param alias The alias for the fetch
+ * @param lockMode The lock mode to apply to the fetched association.
+ */
+ public FetchReturn(
+ NonScalarReturn owner,
+ String ownerProperty,
+ String alias,
+ LockMode lockMode) {
+ super( alias, lockMode );
+ this.owner = owner;
+ this.ownerProperty = ownerProperty;
+ }
+
+ /**
+ * Retrieves the return descriptor for the owner of this fetch.
+ *
+ * @return The owner
+ */
+ public NonScalarReturn getOwner() {
+ return owner;
+ }
+
+ /**
+ * The name of the property on the owner which represents this association.
+ *
+ * @return The property name.
+ */
+ public String getOwnerProperty() {
+ return ownerProperty;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/NonScalarReturn.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/NonScalarReturn.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/NonScalarReturn.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,30 @@
+package org.hibernate.loader.custom;
+
+import org.hibernate.LockMode;
+import org.hibernate.HibernateException;
+
+/**
+ * Represents some non-scalar (entity/collection) return within the query result.
+ *
+ * @author Steve Ebersole
+ */
+public abstract class NonScalarReturn implements Return {
+ private final String alias;
+ private final LockMode lockMode;
+
+ public NonScalarReturn(String alias, LockMode lockMode) {
+ this.alias = alias;
+ if ( alias == null ) {
+ throw new HibernateException("alias must be specified");
+ }
+ this.lockMode = lockMode;
+ }
+
+ public String getAlias() {
+ return alias;
+ }
+
+ public LockMode getLockMode() {
+ return lockMode;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/Return.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/Return.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/Return.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,9 @@
+package org.hibernate.loader.custom;
+
+/**
+ * Represents a return in a custom query.
+ *
+ * @author Steve Ebersole
+ */
+public interface Return {
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/RootReturn.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/RootReturn.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/RootReturn.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,35 @@
+package org.hibernate.loader.custom;
+
+import org.hibernate.LockMode;
+import org.hibernate.loader.EntityAliases;
+
+/**
+ * Represents a return which names a "root" entity.
+ * <p/>
+ * A root entity means it is explicitly a "column" in the result, as opposed to
+ * a fetched association.
+ *
+ * @author Steve Ebersole
+ */
+public class RootReturn extends NonScalarReturn {
+ private final String entityName;
+ private final EntityAliases entityAliases;
+
+ public RootReturn(
+ String alias,
+ String entityName,
+ EntityAliases entityAliases,
+ LockMode lockMode) {
+ super( alias, lockMode );
+ this.entityName = entityName;
+ this.entityAliases = entityAliases;
+ }
+
+ public String getEntityName() {
+ return entityName;
+ }
+
+ public EntityAliases getEntityAliases() {
+ return entityAliases;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/ScalarReturn.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/ScalarReturn.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/ScalarReturn.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,26 @@
+package org.hibernate.loader.custom;
+
+import org.hibernate.type.Type;
+
+/**
+ * Represent a scalar (aka simple value) return within a query result.
+ *
+ * @author Steve Ebersole
+ */
+public class ScalarReturn implements Return {
+ private final Type type;
+ private final String columnAlias;
+
+ public ScalarReturn(Type type, String columnAlias) {
+ this.type = type;
+ this.columnAlias = columnAlias;
+ }
+
+ public Type getType() {
+ return type;
+ }
+
+ public String getColumnAlias() {
+ return columnAlias;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,9 @@
+<html>
+<head></head>
+<body>
+<p>
+ This package defines a framework for custom loaders that accept
+ handwritten SQL
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/sql/SQLCustomQuery.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/sql/SQLCustomQuery.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/sql/SQLCustomQuery.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,232 @@
+//$Id: SQLCustomQuery.java 10018 2006-06-15 05:21:06Z steve.ebersole at jboss.com $
+package org.hibernate.loader.custom.sql;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.HashMap;
+
+import org.hibernate.HibernateException;
+import org.hibernate.engine.query.sql.NativeSQLQueryReturn;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.loader.custom.CustomQuery;
+import org.hibernate.persister.collection.SQLLoadableCollection;
+import org.hibernate.persister.entity.SQLLoadable;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Implements Hibernate's built-in support for native SQL queries.
+ * <p/>
+ * This support is built on top of the notion of "custom queries"...
+ *
+ * @author Gavin King
+ * @author Max Andersen
+ * @author Steve Ebersole
+ */
+public class SQLCustomQuery implements CustomQuery {
+
+ public static final Log log = LogFactory.getLog( SQLCustomQuery.class );
+
+ private final String sql;
+ private final Set querySpaces = new HashSet();
+ private final Map namedParameterBindPoints = new HashMap();
+ private final List customQueryReturns = new ArrayList();
+
+
+ public String getSQL() {
+ return sql;
+ }
+
+ public Set getQuerySpaces() {
+ return querySpaces;
+ }
+
+ public Map getNamedParameterBindPoints() {
+ return namedParameterBindPoints;
+ }
+
+ public List getCustomQueryReturns() {
+ return customQueryReturns;
+ }
+
+ public SQLCustomQuery(
+ final String sqlQuery,
+ final NativeSQLQueryReturn[] queryReturns,
+ final Collection additionalQuerySpaces,
+ final SessionFactoryImplementor factory) throws HibernateException {
+
+ log.trace( "starting processing of sql query [" + sqlQuery + "]" );
+ SQLQueryReturnProcessor processor = new SQLQueryReturnProcessor(queryReturns, factory);
+ SQLQueryReturnProcessor.ResultAliasContext aliasContext = processor.process();
+
+
+// Map[] propertyResultMaps = (Map[]) processor.getPropertyResults().toArray( new Map[0] );
+// Map[] collectionResultMaps = (Map[]) processor.getCollectionPropertyResults().toArray( new Map[0] );
+//
+// List collectionSuffixes = new ArrayList();
+// List collectionOwnerAliases = processor.getCollectionOwnerAliases();
+// List collectionPersisters = processor.getCollectionPersisters();
+// int size = collectionPersisters.size();
+// if (size!=0) {
+// collectionOwners = new int[size];
+// collectionRoles = new String[size];
+// //collectionDescriptors = new CollectionAliases[size];
+// for ( int i=0; i<size; i++ ) {
+// CollectionPersister collectionPersister = (CollectionPersister) collectionPersisters.get(i);
+// collectionRoles[i] = ( collectionPersister ).getRole();
+// collectionOwners[i] = processor.getAliases().indexOf( collectionOwnerAliases.get(i) );
+// String suffix = i + "__";
+// collectionSuffixes.add(suffix);
+// //collectionDescriptors[i] = new GeneratedCollectionAliases( collectionResultMaps[i], collectionPersister, suffix );
+// }
+// }
+// else {
+// collectionRoles = null;
+// //collectionDescriptors = null;
+// collectionOwners = null;
+// }
+//
+// String[] aliases = ArrayHelper.toStringArray( processor.getAliases() );
+// String[] collAliases = ArrayHelper.toStringArray( processor.getCollectionAliases() );
+// String[] collSuffixes = ArrayHelper.toStringArray(collectionSuffixes);
+//
+// SQLLoadable[] entityPersisters = (SQLLoadable[]) processor.getPersisters().toArray( new SQLLoadable[0] );
+// SQLLoadableCollection[] collPersisters = (SQLLoadableCollection[]) collectionPersisters.toArray( new SQLLoadableCollection[0] );
+// lockModes = (LockMode[]) processor.getLockModes().toArray( new LockMode[0] );
+//
+// scalarColumnAliases = ArrayHelper.toStringArray( processor.getScalarColumnAliases() );
+// scalarTypes = ArrayHelper.toTypeArray( processor.getScalarTypes() );
+//
+// // need to match the "sequence" of what we return. scalar first, entity last.
+// returnAliases = ArrayHelper.join(scalarColumnAliases, aliases);
+//
+// String[] suffixes = BasicLoader.generateSuffixes(entityPersisters.length);
+
+ SQLQueryParser parser = new SQLQueryParser( sqlQuery, new ParserContext( aliasContext ) );
+ this.sql = parser.process();
+ this.namedParameterBindPoints.putAll( parser.getNamedParameters() );
+
+// SQLQueryParser parser = new SQLQueryParser(
+// sqlQuery,
+// processor.getAlias2Persister(),
+// processor.getAlias2Return(),
+// aliases,
+// collAliases,
+// collPersisters,
+// suffixes,
+// collSuffixes
+// );
+//
+// sql = parser.process();
+//
+// namedParameterBindPoints = parser.getNamedParameters();
+
+
+ customQueryReturns.addAll( processor.generateCustomReturns( parser.queryHasAliases() ) );
+
+// // Populate entityNames, entityDescrptors and querySpaces
+// entityNames = new String[entityPersisters.length];
+// entityDescriptors = new EntityAliases[entityPersisters.length];
+// for (int i = 0; i < entityPersisters.length; i++) {
+// SQLLoadable persister = entityPersisters[i];
+// //alias2Persister.put( aliases[i], persister );
+// //TODO: Does not consider any other tables referenced in the query
+// ArrayHelper.addAll( querySpaces, persister.getQuerySpaces() );
+// entityNames[i] = persister.getEntityName();
+// if ( parser.queryHasAliases() ) {
+// entityDescriptors[i] = new DefaultEntityAliases(
+// propertyResultMaps[i],
+// entityPersisters[i],
+// suffixes[i]
+// );
+// }
+// else {
+// entityDescriptors[i] = new ColumnEntityAliases(
+// propertyResultMaps[i],
+// entityPersisters[i],
+// suffixes[i]
+// );
+// }
+// }
+ if ( additionalQuerySpaces != null ) {
+ querySpaces.addAll( additionalQuerySpaces );
+ }
+
+// if (size!=0) {
+// collectionDescriptors = new CollectionAliases[size];
+// for ( int i=0; i<size; i++ ) {
+// CollectionPersister collectionPersister = (CollectionPersister) collectionPersisters.get(i);
+// String suffix = i + "__";
+// if( parser.queryHasAliases() ) {
+// collectionDescriptors[i] = new GeneratedCollectionAliases( collectionResultMaps[i], collectionPersister, suffix );
+// } else {
+// collectionDescriptors[i] = new ColumnCollectionAliases( collectionResultMaps[i], (SQLLoadableCollection) collectionPersister );
+// }
+// }
+// }
+// else {
+// collectionDescriptors = null;
+// }
+//
+//
+// // Resolve owners
+// Map alias2OwnerAlias = processor.getAlias2OwnerAlias();
+// int[] ownersArray = new int[entityPersisters.length];
+// for ( int j=0; j < aliases.length; j++ ) {
+// String ownerAlias = (String) alias2OwnerAlias.get( aliases[j] );
+// if ( StringHelper.isNotEmpty(ownerAlias) ) {
+// ownersArray[j] = processor.getAliases().indexOf( ownerAlias );
+// }
+// else {
+// ownersArray[j] = -1;
+// }
+// }
+// if ( ArrayHelper.isAllNegative(ownersArray) ) {
+// ownersArray = null;
+// }
+// this.entityOwners = ownersArray;
+
+ }
+
+
+ private static class ParserContext implements SQLQueryParser.ParserContext {
+
+ private final SQLQueryReturnProcessor.ResultAliasContext aliasContext;
+
+ public ParserContext(SQLQueryReturnProcessor.ResultAliasContext aliasContext) {
+ this.aliasContext = aliasContext;
+ }
+
+ public boolean isEntityAlias(String alias) {
+ return getEntityPersisterByAlias( alias ) != null;
+ }
+
+ public SQLLoadable getEntityPersisterByAlias(String alias) {
+ return aliasContext.getEntityPersister( alias );
+ }
+
+ public String getEntitySuffixByAlias(String alias) {
+ return aliasContext.getEntitySuffix( alias );
+ }
+
+ public boolean isCollectionAlias(String alias) {
+ return getCollectionPersisterByAlias( alias ) != null;
+ }
+
+ public SQLLoadableCollection getCollectionPersisterByAlias(String alias) {
+ return aliasContext.getCollectionPersister( alias );
+ }
+
+ public String getCollectionSuffixByAlias(String alias) {
+ return aliasContext.getCollectionSuffix( alias );
+ }
+
+ public Map getPropertyResultsMapByAlias(String alias) {
+ return aliasContext.getPropertyResultsMap( alias );
+ }
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/sql/SQLCustomQuery.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/sql/SQLQueryParser.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/sql/SQLQueryParser.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/sql/SQLQueryParser.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,274 @@
+//$Id: SQLQueryParser.java 10067 2006-06-28 13:24:59Z steve.ebersole at jboss.com $
+package org.hibernate.loader.custom.sql;
+
+import org.hibernate.QueryException;
+import org.hibernate.engine.query.ParameterParser;
+import org.hibernate.persister.collection.SQLLoadableCollection;
+import org.hibernate.persister.entity.SQLLoadable;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Gavin King
+ * @author Max Andersen
+ * @author Steve Ebersole
+ */
+public class SQLQueryParser {
+
+ private final String originalQueryString;
+ private final ParserContext context;
+
+ private final Map namedParameters = new HashMap();
+ private long aliasesFound = 0;
+
+ static interface ParserContext {
+ boolean isEntityAlias(String aliasName);
+ SQLLoadable getEntityPersisterByAlias(String alias);
+ String getEntitySuffixByAlias(String alias);
+ boolean isCollectionAlias(String aliasName);
+ SQLLoadableCollection getCollectionPersisterByAlias(String alias);
+ String getCollectionSuffixByAlias(String alias);
+ Map getPropertyResultsMapByAlias(String alias);
+ }
+
+ public SQLQueryParser(String queryString, ParserContext context) {
+ this.originalQueryString = queryString;
+ this.context = context;
+ }
+
+ public Map getNamedParameters() {
+ return namedParameters;
+ }
+
+ public boolean queryHasAliases() {
+ return aliasesFound>0;
+ }
+
+ public String process() {
+ return substituteParams( substituteBrackets( originalQueryString ) );
+ }
+
+ // TODO: should "record" how many properties we have reffered to - and if we
+ // don't get'em'all we throw an exception! Way better than trial and error ;)
+ private String substituteBrackets(String sqlQuery) throws QueryException {
+
+ StringBuffer result = new StringBuffer( sqlQuery.length() + 20 );
+ int left, right;
+
+ // replace {....} with corresponding column aliases
+ for ( int curr = 0; curr < sqlQuery.length(); curr = right + 1 ) {
+ if ( ( left = sqlQuery.indexOf( '{', curr ) ) < 0 ) {
+ // No additional open braces found in the string, append the
+ // rest of the string in its entirty and quit this loop
+ result.append( sqlQuery.substring( curr ) );
+ break;
+ }
+
+ // apend everything up until the next encountered open brace
+ result.append( sqlQuery.substring( curr, left ) );
+
+ if ( ( right = sqlQuery.indexOf( '}', left + 1 ) ) < 0 ) {
+ throw new QueryException( "Unmatched braces for alias path", sqlQuery );
+ }
+
+ String aliasPath = sqlQuery.substring( left + 1, right );
+ int firstDot = aliasPath.indexOf( '.' );
+ if ( firstDot == -1 ) {
+ if ( context.isEntityAlias( aliasPath ) ) {
+ // it is a simple table alias {foo}
+ result.append( aliasPath );
+ aliasesFound++;
+ }
+ else {
+ // passing through anything we do not know : to support jdbc escape sequences HB-898
+ result.append( '{' ).append(aliasPath).append( '}' );
+ }
+ }
+ else {
+ String aliasName = aliasPath.substring(0, firstDot);
+ boolean isCollection = context.isCollectionAlias( aliasName );
+ boolean isEntity = context.isEntityAlias( aliasName );
+
+ if ( isCollection ) {
+ // The current alias is referencing the collection to be eagerly fetched
+ String propertyName = aliasPath.substring( firstDot + 1 );
+ result.append( resolveCollectionProperties( aliasName, propertyName ) );
+ aliasesFound++;
+ }
+ else if ( isEntity ) {
+ // it is a property reference {foo.bar}
+ String propertyName = aliasPath.substring( firstDot + 1 );
+ result.append( resolveProperties( aliasName, propertyName ) );
+ aliasesFound++;
+ }
+ else {
+ // passing through anything we do not know : to support jdbc escape sequences HB-898
+ result.append( '{' ).append(aliasPath).append( '}' );
+ }
+
+ }
+ }
+
+ // Possibly handle :something parameters for the query ?
+
+ return result.toString();
+ }
+
+ private String resolveCollectionProperties(
+ String aliasName,
+ String propertyName) {
+
+ Map fieldResults = context.getPropertyResultsMapByAlias( aliasName );
+ SQLLoadableCollection collectionPersister = context.getCollectionPersisterByAlias( aliasName );
+ String collectionSuffix = context.getCollectionSuffixByAlias( aliasName );
+
+ if ( "*".equals( propertyName ) ) {
+ if( !fieldResults.isEmpty() ) {
+ throw new QueryException("Using return-propertys together with * syntax is not supported.");
+ }
+
+ String selectFragment = collectionPersister.selectFragment( aliasName, collectionSuffix );
+ aliasesFound++;
+ return selectFragment
+ + ", "
+ + resolveProperties( aliasName, propertyName );
+ }
+ else if ( "element.*".equals( propertyName ) ) {
+ return resolveProperties( aliasName, "*" );
+ }
+ else {
+ String[] columnAliases;
+
+ // Let return-propertys override whatever the persister has for aliases.
+ columnAliases = ( String[] ) fieldResults.get(propertyName);
+ if ( columnAliases==null ) {
+ columnAliases = collectionPersister.getCollectionPropertyColumnAliases( propertyName, collectionSuffix );
+ }
+
+ if ( columnAliases == null || columnAliases.length == 0 ) {
+ throw new QueryException(
+ "No column name found for property [" + propertyName + "] for alias [" + aliasName + "]",
+ originalQueryString
+ );
+ }
+ if ( columnAliases.length != 1 ) {
+ // TODO: better error message since we actually support composites if names are explicitly listed.
+ throw new QueryException(
+ "SQL queries only support properties mapped to a single column - property [" +
+ propertyName + "] is mapped to " + columnAliases.length + " columns.",
+ originalQueryString
+ );
+ }
+ aliasesFound++;
+ return columnAliases[0];
+
+ }
+ }
+ private String resolveProperties(
+ String aliasName,
+ String propertyName) {
+ Map fieldResults = context.getPropertyResultsMapByAlias( aliasName );
+ SQLLoadable persister = context.getEntityPersisterByAlias( aliasName );
+ String suffix = context.getEntitySuffixByAlias( aliasName );
+
+ if ( "*".equals( propertyName ) ) {
+ if( !fieldResults.isEmpty() ) {
+ throw new QueryException("Using return-propertys together with * syntax is not supported.");
+ }
+ aliasesFound++;
+ return persister.selectFragment( aliasName, suffix ) ;
+ }
+ else {
+
+ String[] columnAliases;
+
+ // Let return-propertys override whatever the persister has for aliases.
+ columnAliases = (String[]) fieldResults.get( propertyName );
+ if ( columnAliases == null ) {
+ columnAliases = persister.getSubclassPropertyColumnAliases( propertyName, suffix );
+ }
+
+ if ( columnAliases == null || columnAliases.length == 0 ) {
+ throw new QueryException(
+ "No column name found for property [" + propertyName + "] for alias [" + aliasName + "]",
+ originalQueryString
+ );
+ }
+ if ( columnAliases.length != 1 ) {
+ // TODO: better error message since we actually support composites if names are explicitly listed.
+ throw new QueryException(
+ "SQL queries only support properties mapped to a single column - property [" + propertyName + "] is mapped to " + columnAliases.length + " columns.",
+ originalQueryString
+ );
+ }
+ aliasesFound++;
+ return columnAliases[0];
+ }
+ }
+
+ /**
+ * Substitues JDBC parameter placeholders (?) for all encountered
+ * parameter specifications. It also tracks the positions of these
+ * parameter specifications within the query string. This accounts for
+ * ordinal-params, named-params, and ejb3-positional-params.
+ *
+ * @param sqlString The query string.
+ * @return The SQL query with parameter substitution complete.
+ */
+ private String substituteParams(String sqlString) {
+ ParameterSubstitutionRecognizer recognizer = new ParameterSubstitutionRecognizer();
+ ParameterParser.parse( sqlString, recognizer );
+
+ namedParameters.clear();
+ namedParameters.putAll( recognizer.namedParameterBindPoints );
+
+ return recognizer.result.toString();
+ }
+
+ public static class ParameterSubstitutionRecognizer implements ParameterParser.Recognizer {
+ StringBuffer result = new StringBuffer();
+ Map namedParameterBindPoints = new HashMap();
+ int parameterCount = 0;
+
+ public void outParameter(int position) {
+ result.append( '?' );
+ }
+
+ public void ordinalParameter(int position) {
+ result.append( '?' );
+ }
+
+ public void namedParameter(String name, int position) {
+ addNamedParameter( name );
+ result.append( '?' );
+ }
+
+ public void jpaPositionalParameter(String name, int position) {
+ namedParameter( name, position );
+ }
+
+ public void other(char character) {
+ result.append( character );
+ }
+
+ private void addNamedParameter(String name) {
+ Integer loc = new Integer( parameterCount++ );
+ Object o = namedParameterBindPoints.get( name );
+ if ( o == null ) {
+ namedParameterBindPoints.put( name, loc );
+ }
+ else if ( o instanceof Integer ) {
+ ArrayList list = new ArrayList( 4 );
+ list.add( o );
+ list.add( loc );
+ namedParameterBindPoints.put( name, list );
+ }
+ else {
+ ( ( List ) o ).add( loc );
+ }
+ }
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/sql/SQLQueryParser.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/sql/SQLQueryReturnProcessor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/sql/SQLQueryReturnProcessor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/custom/sql/SQLQueryReturnProcessor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,502 @@
+//$Id: SQLQueryReturnProcessor.java 7370 2005-07-04 11:17:33Z maxcsaucdk $
+package org.hibernate.loader.custom.sql;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.loader.custom.Return;
+import org.hibernate.loader.custom.ScalarReturn;
+import org.hibernate.loader.custom.RootReturn;
+import org.hibernate.loader.custom.CollectionReturn;
+import org.hibernate.loader.custom.ColumnCollectionAliases;
+import org.hibernate.loader.custom.FetchReturn;
+import org.hibernate.loader.custom.CollectionFetchReturn;
+import org.hibernate.loader.custom.NonScalarReturn;
+import org.hibernate.loader.custom.EntityFetchReturn;
+import org.hibernate.loader.BasicLoader;
+import org.hibernate.loader.EntityAliases;
+import org.hibernate.loader.DefaultEntityAliases;
+import org.hibernate.loader.ColumnEntityAliases;
+import org.hibernate.loader.CollectionAliases;
+import org.hibernate.loader.GeneratedCollectionAliases;
+import org.hibernate.engine.query.sql.NativeSQLQueryReturn;
+import org.hibernate.engine.query.sql.NativeSQLQueryCollectionReturn;
+import org.hibernate.engine.query.sql.NativeSQLQueryScalarReturn;
+import org.hibernate.engine.query.sql.NativeSQLQueryNonScalarReturn;
+import org.hibernate.engine.query.sql.NativeSQLQueryJoinReturn;
+import org.hibernate.engine.query.sql.NativeSQLQueryRootReturn;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.persister.collection.SQLLoadableCollection;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.persister.entity.SQLLoadable;
+import org.hibernate.type.EntityType;
+import org.hibernate.type.Type;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Responsible for processing the series of {@link org.hibernate.engine.query.sql.NativeSQLQueryReturn returns}
+ * defined by a {@link org.hibernate.engine.query.sql.NativeSQLQuerySpecification} and
+ * breaking them down into a series of {@link Return returns} for use within the
+ * {@link org.hibernate.loader.custom.CustomLoader}.
+ *
+ * @author Gavin King
+ * @author Max Andersen
+ * @author Steve Ebersole
+ */
+public class SQLQueryReturnProcessor {
+
+ public static final Log log = LogFactory.getLog( SQLQueryReturnProcessor.class );
+
+ private NativeSQLQueryReturn[] queryReturns;
+
+// private final List persisters = new ArrayList();
+
+ private final Map alias2Return = new HashMap();
+ private final Map alias2OwnerAlias = new HashMap();
+
+ private final Map alias2Persister = new HashMap();
+ private final Map alias2Suffix = new HashMap();
+
+ private final Map alias2CollectionPersister = new HashMap();
+ private final Map alias2CollectionSuffix = new HashMap();
+
+ private final Map entityPropertyResultMaps = new HashMap();
+ private final Map collectionPropertyResultMaps = new HashMap();
+
+// private final List scalarTypes = new ArrayList();
+// private final List scalarColumnAliases = new ArrayList();
+
+ private final SessionFactoryImplementor factory;
+
+// private List collectionOwnerAliases = new ArrayList();
+// private List collectionAliases = new ArrayList();
+// private List collectionPersisters = new ArrayList();
+// private List collectionResults = new ArrayList();
+
+ private int entitySuffixSeed = 0;
+ private int collectionSuffixSeed = 0;
+
+
+ public SQLQueryReturnProcessor(NativeSQLQueryReturn[] queryReturns, SessionFactoryImplementor factory) {
+ this.queryReturns = queryReturns;
+ this.factory = factory;
+ }
+
+ /*package*/ class ResultAliasContext {
+ public SQLLoadable getEntityPersister(String alias) {
+ return ( SQLLoadable ) alias2Persister.get( alias );
+ }
+
+ public SQLLoadableCollection getCollectionPersister(String alias) {
+ return ( SQLLoadableCollection ) alias2CollectionPersister.get( alias );
+ }
+
+ public String getEntitySuffix(String alias) {
+ return ( String ) alias2Suffix.get( alias );
+ }
+
+ public String getCollectionSuffix(String alias) {
+ return ( String ) alias2CollectionSuffix.get ( alias );
+ }
+
+ public String getOwnerAlias(String alias) {
+ return ( String ) alias2OwnerAlias.get( alias );
+ }
+
+ public Map getPropertyResultsMap(String alias) {
+ return internalGetPropertyResultsMap( alias );
+ }
+ }
+
+ private Map internalGetPropertyResultsMap(String alias) {
+ NativeSQLQueryReturn rtn = ( NativeSQLQueryReturn ) alias2Return.get( alias );
+ if ( rtn instanceof NativeSQLQueryNonScalarReturn ) {
+ return ( ( NativeSQLQueryNonScalarReturn ) rtn ).getPropertyResultsMap();
+ }
+ else {
+ return null;
+ }
+ }
+
+ private boolean hasPropertyResultMap(String alias) {
+ Map propertyMaps = internalGetPropertyResultsMap( alias );
+ return propertyMaps != null && ! propertyMaps.isEmpty();
+ }
+
+ public ResultAliasContext process() {
+ // first, break down the returns into maps keyed by alias
+ // so that role returns can be more easily resolved to their owners
+ for ( int i = 0; i < queryReturns.length; i++ ) {
+ if ( queryReturns[i] instanceof NativeSQLQueryNonScalarReturn ) {
+ NativeSQLQueryNonScalarReturn rtn = ( NativeSQLQueryNonScalarReturn ) queryReturns[i];
+ alias2Return.put( rtn.getAlias(), rtn );
+ if ( rtn instanceof NativeSQLQueryJoinReturn ) {
+ NativeSQLQueryJoinReturn fetchReturn = ( NativeSQLQueryJoinReturn ) rtn;
+ alias2OwnerAlias.put( fetchReturn.getAlias(), fetchReturn.getOwnerAlias() );
+ }
+ }
+ }
+
+ // Now, process the returns
+ for ( int i = 0; i < queryReturns.length; i++ ) {
+ processReturn( queryReturns[i] );
+ }
+
+ return new ResultAliasContext();
+ }
+
+ public List generateCustomReturns(boolean queryHadAliases) {
+ List customReturns = new ArrayList();
+ Map customReturnsByAlias = new HashMap();
+ for ( int i = 0; i < queryReturns.length; i++ ) {
+ if ( queryReturns[i] instanceof NativeSQLQueryScalarReturn ) {
+ NativeSQLQueryScalarReturn rtn = ( NativeSQLQueryScalarReturn ) queryReturns[i];
+ customReturns.add( new ScalarReturn( rtn.getType(), rtn.getColumnAlias() ) );
+ }
+ else if ( queryReturns[i] instanceof NativeSQLQueryRootReturn ) {
+ NativeSQLQueryRootReturn rtn = ( NativeSQLQueryRootReturn ) queryReturns[i];
+ String alias = rtn.getAlias();
+ EntityAliases entityAliases;
+ if ( queryHadAliases || hasPropertyResultMap( alias ) ) {
+ entityAliases = new DefaultEntityAliases(
+ ( Map ) entityPropertyResultMaps.get( alias ),
+ ( SQLLoadable ) alias2Persister.get( alias ),
+ ( String ) alias2Suffix.get( alias )
+ );
+ }
+ else {
+ entityAliases = new ColumnEntityAliases(
+ ( Map ) entityPropertyResultMaps.get( alias ),
+ ( SQLLoadable ) alias2Persister.get( alias ),
+ ( String ) alias2Suffix.get( alias )
+ );
+ }
+ RootReturn customReturn = new RootReturn(
+ alias,
+ rtn.getReturnEntityName(),
+ entityAliases,
+ rtn.getLockMode()
+ );
+ customReturns.add( customReturn );
+ customReturnsByAlias.put( rtn.getAlias(), customReturn );
+ }
+ else if ( queryReturns[i] instanceof NativeSQLQueryCollectionReturn ) {
+ NativeSQLQueryCollectionReturn rtn = ( NativeSQLQueryCollectionReturn ) queryReturns[i];
+ String alias = rtn.getAlias();
+ SQLLoadableCollection persister = ( SQLLoadableCollection ) alias2CollectionPersister.get( alias );
+ boolean isEntityElements = persister.getElementType().isEntityType();
+ CollectionAliases collectionAliases;
+ EntityAliases elementEntityAliases = null;
+ if ( queryHadAliases || hasPropertyResultMap( alias ) ) {
+ collectionAliases = new GeneratedCollectionAliases(
+ ( Map ) collectionPropertyResultMaps.get( alias ),
+ ( SQLLoadableCollection ) alias2CollectionPersister.get( alias ),
+ ( String ) alias2CollectionSuffix.get( alias )
+ );
+ if ( isEntityElements ) {
+ elementEntityAliases = new DefaultEntityAliases(
+ ( Map ) entityPropertyResultMaps.get( alias ),
+ ( SQLLoadable ) alias2Persister.get( alias ),
+ ( String ) alias2Suffix.get( alias )
+ );
+ }
+ }
+ else {
+ collectionAliases = new ColumnCollectionAliases(
+ ( Map ) collectionPropertyResultMaps.get( alias ),
+ ( SQLLoadableCollection ) alias2CollectionPersister.get( alias )
+ );
+ if ( isEntityElements ) {
+ elementEntityAliases = new ColumnEntityAliases(
+ ( Map ) entityPropertyResultMaps.get( alias ),
+ ( SQLLoadable ) alias2Persister.get( alias ),
+ ( String ) alias2Suffix.get( alias )
+ );
+ }
+ }
+ CollectionReturn customReturn = new CollectionReturn(
+ alias,
+ rtn.getOwnerEntityName(),
+ rtn.getOwnerProperty(),
+ collectionAliases,
+ elementEntityAliases,
+ rtn.getLockMode()
+ );
+ customReturns.add( customReturn );
+ customReturnsByAlias.put( rtn.getAlias(), customReturn );
+ }
+ else if ( queryReturns[i] instanceof NativeSQLQueryJoinReturn ) {
+ NativeSQLQueryJoinReturn rtn = ( NativeSQLQueryJoinReturn ) queryReturns[i];
+ String alias = rtn.getAlias();
+ FetchReturn customReturn;
+ NonScalarReturn ownerCustomReturn = ( NonScalarReturn ) customReturnsByAlias.get( rtn.getOwnerAlias() );
+ if ( alias2CollectionPersister.containsKey( alias ) ) {
+ SQLLoadableCollection persister = ( SQLLoadableCollection ) alias2CollectionPersister.get( alias );
+ boolean isEntityElements = persister.getElementType().isEntityType();
+ CollectionAliases collectionAliases;
+ EntityAliases elementEntityAliases = null;
+ if ( queryHadAliases || hasPropertyResultMap( alias ) ) {
+ collectionAliases = new GeneratedCollectionAliases(
+ ( Map ) collectionPropertyResultMaps.get( alias ),
+ persister,
+ ( String ) alias2CollectionSuffix.get( alias )
+ );
+ if ( isEntityElements ) {
+ elementEntityAliases = new DefaultEntityAliases(
+ ( Map ) entityPropertyResultMaps.get( alias ),
+ ( SQLLoadable ) alias2Persister.get( alias ),
+ ( String ) alias2Suffix.get( alias )
+ );
+ }
+ }
+ else {
+ collectionAliases = new ColumnCollectionAliases(
+ ( Map ) collectionPropertyResultMaps.get( alias ),
+ persister
+ );
+ if ( isEntityElements ) {
+ elementEntityAliases = new ColumnEntityAliases(
+ ( Map ) entityPropertyResultMaps.get( alias ),
+ ( SQLLoadable ) alias2Persister.get( alias ),
+ ( String ) alias2Suffix.get( alias )
+ );
+ }
+ }
+ customReturn = new CollectionFetchReturn(
+ alias,
+ ownerCustomReturn,
+ rtn.getOwnerProperty(),
+ collectionAliases,
+ elementEntityAliases,
+ rtn.getLockMode()
+ );
+ }
+ else {
+ EntityAliases entityAliases;
+ if ( queryHadAliases || hasPropertyResultMap( alias ) ) {
+ entityAliases = new DefaultEntityAliases(
+ ( Map ) entityPropertyResultMaps.get( alias ),
+ ( SQLLoadable ) alias2Persister.get( alias ),
+ ( String ) alias2Suffix.get( alias )
+ );
+ }
+ else {
+ entityAliases = new ColumnEntityAliases(
+ ( Map ) entityPropertyResultMaps.get( alias ),
+ ( SQLLoadable ) alias2Persister.get( alias ),
+ ( String ) alias2Suffix.get( alias )
+ );
+ }
+ customReturn = new EntityFetchReturn(
+ alias,
+ entityAliases,
+ ownerCustomReturn,
+ rtn.getOwnerProperty(),
+ rtn.getLockMode()
+ );
+ }
+ customReturns.add( customReturn );
+ customReturnsByAlias.put( alias, customReturn );
+ }
+ }
+ return customReturns;
+ }
+
+ private SQLLoadable getSQLLoadable(String entityName) throws MappingException {
+ EntityPersister persister = factory.getEntityPersister( entityName );
+ if ( !(persister instanceof SQLLoadable) ) {
+ throw new MappingException( "class persister is not SQLLoadable: " + entityName );
+ }
+ return (SQLLoadable) persister;
+ }
+
+ private String generateEntitySuffix() {
+ return BasicLoader.generateSuffixes( entitySuffixSeed++, 1 )[0];
+ }
+
+ private String generateCollectionSuffix() {
+ return collectionSuffixSeed++ + "__";
+ }
+
+ private void processReturn(NativeSQLQueryReturn rtn) {
+ if ( rtn instanceof NativeSQLQueryScalarReturn ) {
+ processScalarReturn( ( NativeSQLQueryScalarReturn ) rtn );
+ }
+ else if ( rtn instanceof NativeSQLQueryRootReturn ) {
+ processRootReturn( ( NativeSQLQueryRootReturn ) rtn );
+ }
+ else if ( rtn instanceof NativeSQLQueryCollectionReturn ) {
+ processCollectionReturn( ( NativeSQLQueryCollectionReturn ) rtn );
+ }
+ else {
+ processJoinReturn( ( NativeSQLQueryJoinReturn ) rtn );
+ }
+ }
+
+ private void processScalarReturn(NativeSQLQueryScalarReturn typeReturn) {
+// scalarColumnAliases.add( typeReturn.getColumnAlias() );
+// scalarTypes.add( typeReturn.getType() );
+ }
+
+ private void processRootReturn(NativeSQLQueryRootReturn rootReturn) {
+ if ( alias2Persister.containsKey( rootReturn.getAlias() ) ) {
+ // already been processed...
+ return;
+ }
+
+ SQLLoadable persister = getSQLLoadable( rootReturn.getReturnEntityName() );
+ addPersister( rootReturn.getAlias(), rootReturn.getPropertyResultsMap(), persister );
+ }
+
+ /**
+ * @param propertyResult
+ * @param persister
+ */
+ private void addPersister(String alias, Map propertyResult, SQLLoadable persister) {
+ alias2Persister.put( alias, persister );
+ String suffix = generateEntitySuffix();
+ log.trace( "mapping alias [" + alias + "] to entity-suffix [" + suffix + "]" );
+ alias2Suffix.put( alias, suffix );
+ entityPropertyResultMaps.put( alias, propertyResult );
+ }
+
+ private void addCollection(String role, String alias, Map propertyResults) {
+ SQLLoadableCollection collectionPersister = ( SQLLoadableCollection ) factory.getCollectionPersister( role );
+ alias2CollectionPersister.put( alias, collectionPersister );
+ String suffix = generateCollectionSuffix();
+ log.trace( "mapping alias [" + alias + "] to collection-suffix [" + suffix + "]" );
+ alias2CollectionSuffix.put( alias, suffix );
+ collectionPropertyResultMaps.put( alias, propertyResults );
+
+ if ( collectionPersister.isOneToMany() ) {
+ SQLLoadable persister = ( SQLLoadable ) collectionPersister.getElementPersister();
+ addPersister( alias, filter( propertyResults ), persister );
+ }
+ }
+
+ private Map filter(Map propertyResults) {
+ Map result = new HashMap( propertyResults.size() );
+
+ String keyPrefix = "element.";
+
+ Iterator iter = propertyResults.entrySet().iterator();
+ while ( iter.hasNext() ) {
+ Map.Entry element = ( Map.Entry ) iter.next();
+ String path = ( String ) element.getKey();
+ if ( path.startsWith( keyPrefix ) ) {
+ result.put( path.substring( keyPrefix.length() ), element.getValue() );
+ }
+ }
+
+ return result;
+ }
+
+ private void processCollectionReturn(NativeSQLQueryCollectionReturn collectionReturn) {
+ // we are initializing an owned collection
+ //collectionOwners.add( new Integer(-1) );
+// collectionOwnerAliases.add( null );
+ String role = collectionReturn.getOwnerEntityName() + '.' + collectionReturn.getOwnerProperty();
+ addCollection(
+ role,
+ collectionReturn.getAlias(),
+ collectionReturn.getPropertyResultsMap()
+ );
+ }
+
+ private void processJoinReturn(NativeSQLQueryJoinReturn fetchReturn) {
+ String alias = fetchReturn.getAlias();
+// if ( alias2Persister.containsKey( alias ) || collectionAliases.contains( alias ) ) {
+ if ( alias2Persister.containsKey( alias ) || alias2CollectionPersister.containsKey( alias ) ) {
+ // already been processed...
+ return;
+ }
+
+ String ownerAlias = fetchReturn.getOwnerAlias();
+
+ // Make sure the owner alias is known...
+ if ( !alias2Return.containsKey( ownerAlias ) ) {
+ throw new HibernateException( "Owner alias [" + ownerAlias + "] is unknown for alias [" + alias + "]" );
+ }
+
+ // If this return's alias has not been processed yet, do so b4 further processing of this return
+ if ( !alias2Persister.containsKey( ownerAlias ) ) {
+ NativeSQLQueryNonScalarReturn ownerReturn = ( NativeSQLQueryNonScalarReturn ) alias2Return.get(ownerAlias);
+ processReturn( ownerReturn );
+ }
+
+ SQLLoadable ownerPersister = ( SQLLoadable ) alias2Persister.get( ownerAlias );
+ Type returnType = ownerPersister.getPropertyType( fetchReturn.getOwnerProperty() );
+
+ if ( returnType.isCollectionType() ) {
+ String role = ownerPersister.getEntityName() + '.' + fetchReturn.getOwnerProperty();
+ addCollection( role, alias, fetchReturn.getPropertyResultsMap() );
+// collectionOwnerAliases.add( ownerAlias );
+ }
+ else if ( returnType.isEntityType() ) {
+ EntityType eType = ( EntityType ) returnType;
+ String returnEntityName = eType.getAssociatedEntityName();
+ SQLLoadable persister = getSQLLoadable( returnEntityName );
+ addPersister( alias, fetchReturn.getPropertyResultsMap(), persister );
+ }
+
+ }
+
+// public List getCollectionAliases() {
+// return collectionAliases;
+// }
+//
+// /*public List getCollectionOwners() {
+// return collectionOwners;
+// }*/
+//
+// public List getCollectionOwnerAliases() {
+// return collectionOwnerAliases;
+// }
+//
+// public List getCollectionPersisters() {
+// return collectionPersisters;
+// }
+//
+// public Map getAlias2Persister() {
+// return alias2Persister;
+// }
+//
+// /*public boolean isCollectionInitializer() {
+// return isCollectionInitializer;
+// }*/
+//
+//// public List getPersisters() {
+//// return persisters;
+//// }
+//
+// public Map getAlias2OwnerAlias() {
+// return alias2OwnerAlias;
+// }
+//
+// public List getScalarTypes() {
+// return scalarTypes;
+// }
+// public List getScalarColumnAliases() {
+// return scalarColumnAliases;
+// }
+//
+// public List getPropertyResults() {
+// return propertyResults;
+// }
+//
+// public List getCollectionPropertyResults() {
+// return collectionResults;
+// }
+//
+//
+// public Map getAlias2Return() {
+// return alias2Return;
+// }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/AbstractEntityLoader.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/AbstractEntityLoader.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/AbstractEntityLoader.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,89 @@
+//$Id: AbstractEntityLoader.java 9636 2006-03-16 14:14:48Z max.andersen at jboss.com $
+package org.hibernate.loader.entity;
+
+import java.io.Serializable;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.loader.OuterJoinLoader;
+import org.hibernate.persister.entity.OuterJoinLoadable;
+import org.hibernate.transform.ResultTransformer;
+import org.hibernate.type.Type;
+
+public abstract class AbstractEntityLoader extends OuterJoinLoader
+ implements UniqueEntityLoader {
+
+ protected static final Log log = LogFactory.getLog(EntityLoader.class);
+ protected final OuterJoinLoadable persister;
+ protected final Type uniqueKeyType;
+ protected final String entityName;
+
+ public AbstractEntityLoader(
+ OuterJoinLoadable persister,
+ Type uniqueKeyType,
+ SessionFactoryImplementor factory,
+ Map enabledFilters) {
+ super( factory, enabledFilters );
+ this.uniqueKeyType = uniqueKeyType;
+ this.entityName = persister.getEntityName();
+ this.persister = persister;
+
+ }
+
+ public Object load(Serializable id, Object optionalObject, SessionImplementor session)
+ throws HibernateException {
+ return load(session, id, optionalObject, id);
+ }
+
+ protected Object load(SessionImplementor session, Object id, Object optionalObject, Serializable optionalId)
+ throws HibernateException {
+
+ List list = loadEntity(
+ session,
+ id,
+ uniqueKeyType,
+ optionalObject,
+ entityName,
+ optionalId,
+ persister
+ );
+
+ if ( list.size()==1 ) {
+ return list.get(0);
+ }
+ else if ( list.size()==0 ) {
+ return null;
+ }
+ else {
+ if ( getCollectionOwners()!=null ) {
+ return list.get(0);
+ }
+ else {
+ throw new HibernateException(
+ "More than one row with the given identifier was found: " +
+ id +
+ ", for class: " +
+ persister.getEntityName()
+ );
+ }
+ }
+
+ }
+
+ protected Object getResultColumnOrRow(Object[] row, ResultTransformer transformer, ResultSet rs, SessionImplementor session)
+ throws SQLException, HibernateException {
+ return row[row.length-1];
+ }
+
+ protected boolean isSingleRowLoader() {
+ return true;
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/AbstractEntityLoader.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/BatchingEntityLoader.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/BatchingEntityLoader.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/BatchingEntityLoader.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,107 @@
+//$Id: BatchingEntityLoader.java 6929 2005-05-27 03:54:08Z oneovthafew $
+package org.hibernate.loader.entity;
+
+import java.io.Serializable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.hibernate.HibernateException;
+import org.hibernate.LockMode;
+import org.hibernate.MappingException;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.loader.Loader;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.persister.entity.OuterJoinLoadable;
+import org.hibernate.type.Type;
+import org.hibernate.util.ArrayHelper;
+
+/**
+ * "Batch" loads entities, using multiple primary key values in the
+ * SQL <tt>where</tt> clause.
+ *
+ * @see EntityLoader
+ * @author Gavin King
+ */
+public class BatchingEntityLoader implements UniqueEntityLoader {
+
+ private final Loader[] loaders;
+ private final int[] batchSizes;
+ private final EntityPersister persister;
+ private final Type idType;
+
+ public BatchingEntityLoader(EntityPersister persister, int[] batchSizes, Loader[] loaders) {
+ this.batchSizes = batchSizes;
+ this.loaders = loaders;
+ this.persister = persister;
+ idType = persister.getIdentifierType();
+ }
+
+ private Object getObjectFromList(List results, Serializable id, SessionImplementor session) {
+ // get the right object from the list ... would it be easier to just call getEntity() ??
+ Iterator iter = results.iterator();
+ while ( iter.hasNext() ) {
+ Object obj = iter.next();
+ final boolean equal = idType.isEqual(
+ id,
+ session.getContextEntityIdentifier(obj),
+ session.getEntityMode(),
+ session.getFactory()
+ );
+ if ( equal ) return obj;
+ }
+ return null;
+ }
+
+ public Object load(Serializable id, Object optionalObject, SessionImplementor session)
+ throws HibernateException {
+
+ Serializable[] batch = session.getPersistenceContext()
+ .getBatchFetchQueue()
+ .getEntityBatch( persister, id, batchSizes[0], session.getEntityMode() );
+
+ for ( int i=0; i<batchSizes.length-1; i++) {
+ final int smallBatchSize = batchSizes[i];
+ if ( batch[smallBatchSize-1]!=null ) {
+ Serializable[] smallBatch = new Serializable[smallBatchSize];
+ System.arraycopy(batch, 0, smallBatch, 0, smallBatchSize);
+ final List results = loaders[i].loadEntityBatch(
+ session,
+ smallBatch,
+ idType,
+ optionalObject,
+ persister.getEntityName(),
+ id,
+ persister
+ );
+ return getObjectFromList(results, id, session); //EARLY EXIT
+ }
+ }
+
+ return ( (UniqueEntityLoader) loaders[batchSizes.length-1] ).load(id, optionalObject, session);
+
+ }
+
+ public static UniqueEntityLoader createBatchingEntityLoader(
+ final OuterJoinLoadable persister,
+ final int maxBatchSize,
+ final LockMode lockMode,
+ final SessionFactoryImplementor factory,
+ final Map enabledFilters)
+ throws MappingException {
+
+ if ( maxBatchSize>1 ) {
+ int[] batchSizesToCreate = ArrayHelper.getBatchSizes(maxBatchSize);
+ Loader[] loadersToCreate = new Loader[ batchSizesToCreate.length ];
+ for ( int i=0; i<batchSizesToCreate.length; i++ ) {
+ loadersToCreate[i] = new EntityLoader(persister, batchSizesToCreate[i], lockMode, factory, enabledFilters);
+ }
+ return new BatchingEntityLoader(persister, batchSizesToCreate, loadersToCreate);
+ }
+ else {
+ return new EntityLoader(persister, lockMode, factory, enabledFilters);
+ }
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/CascadeEntityJoinWalker.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/CascadeEntityJoinWalker.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/CascadeEntityJoinWalker.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,43 @@
+//$Id: CascadeEntityJoinWalker.java 9889 2006-05-05 01:24:12Z steve.ebersole at jboss.com $
+package org.hibernate.loader.entity;
+
+import org.hibernate.FetchMode;
+import org.hibernate.LockMode;
+import org.hibernate.MappingException;
+import org.hibernate.engine.CascadeStyle;
+import org.hibernate.engine.CascadingAction;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.loader.AbstractEntityJoinWalker;
+import org.hibernate.persister.entity.OuterJoinLoadable;
+import org.hibernate.type.AssociationType;
+import org.hibernate.util.CollectionHelper;
+
+public class CascadeEntityJoinWalker extends AbstractEntityJoinWalker {
+
+ private final CascadingAction cascadeAction;
+
+ public CascadeEntityJoinWalker(OuterJoinLoadable persister, CascadingAction action, SessionFactoryImplementor factory)
+ throws MappingException {
+ super( persister, factory, CollectionHelper.EMPTY_MAP );
+ this.cascadeAction = action;
+ StringBuffer whereCondition = whereString( getAlias(), persister.getIdentifierColumnNames(), 1 )
+ //include the discriminator and class-level where, but not filters
+ .append( persister.filterFragment( getAlias(), CollectionHelper.EMPTY_MAP ) );
+
+ initAll( whereCondition.toString(), "", LockMode.READ );
+ }
+
+ protected boolean isJoinedFetchEnabled(AssociationType type, FetchMode config, CascadeStyle cascadeStyle) {
+ return ( type.isEntityType() || type.isCollectionType() ) &&
+ ( cascadeStyle==null || cascadeStyle.doCascade(cascadeAction) );
+ }
+
+ protected boolean isTooManyCollections() {
+ return countCollectionPersisters(associations)>0;
+ }
+
+ public String getComment() {
+ return "load " + getPersister().getEntityName();
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/CascadeEntityJoinWalker.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/CascadeEntityLoader.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/CascadeEntityLoader.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/CascadeEntityLoader.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,38 @@
+//$Id: CascadeEntityLoader.java 7652 2005-07-26 05:51:47Z oneovthafew $
+package org.hibernate.loader.entity;
+
+import org.hibernate.MappingException;
+import org.hibernate.engine.CascadingAction;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.loader.JoinWalker;
+import org.hibernate.persister.entity.OuterJoinLoadable;
+import org.hibernate.util.CollectionHelper;
+
+public class CascadeEntityLoader extends AbstractEntityLoader {
+
+ public CascadeEntityLoader(
+ OuterJoinLoadable persister,
+ CascadingAction action,
+ SessionFactoryImplementor factory)
+ throws MappingException {
+ super(
+ persister,
+ persister.getIdentifierType(),
+ factory,
+ CollectionHelper.EMPTY_MAP
+ );
+
+ JoinWalker walker = new CascadeEntityJoinWalker(
+ persister,
+ action,
+ factory
+ );
+ initFromWalker( walker );
+
+ postInstantiate();
+
+ log.debug( "Static select for action " + action + " on entity " + entityName + ": " + getSQLString() );
+
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/CascadeEntityLoader.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/CollectionElementLoader.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/CollectionElementLoader.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/CollectionElementLoader.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,111 @@
+//$Id: CollectionElementLoader.java 9636 2006-03-16 14:14:48Z max.andersen at jboss.com $
+package org.hibernate.loader.entity;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.HibernateException;
+import org.hibernate.LockMode;
+import org.hibernate.MappingException;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.loader.JoinWalker;
+import org.hibernate.loader.OuterJoinLoader;
+import org.hibernate.persister.collection.QueryableCollection;
+import org.hibernate.persister.entity.OuterJoinLoadable;
+import org.hibernate.transform.ResultTransformer;
+import org.hibernate.type.Type;
+import org.hibernate.util.ArrayHelper;
+
+/**
+ *
+ *
+ * @author Gavin King
+ */
+public class CollectionElementLoader extends OuterJoinLoader {
+
+ private static final Log log = LogFactory.getLog(CollectionElementLoader.class);
+
+ private final OuterJoinLoadable persister;
+ private final Type keyType;
+ private final Type indexType;
+ private final String entityName;
+
+ public CollectionElementLoader(
+ QueryableCollection collectionPersister,
+ SessionFactoryImplementor factory,
+ Map enabledFilters)
+ throws MappingException {
+ super(factory, enabledFilters);
+
+ this.keyType = collectionPersister.getKeyType();
+ this.indexType = collectionPersister.getIndexType();
+ this.persister = (OuterJoinLoadable) collectionPersister.getElementPersister();
+ this.entityName = persister.getEntityName();
+
+ JoinWalker walker = new EntityJoinWalker(
+ persister,
+ ArrayHelper.join(
+ collectionPersister.getKeyColumnNames(),
+ collectionPersister.getIndexColumnNames()
+ ),
+ 1,
+ LockMode.NONE,
+ factory,
+ enabledFilters
+ );
+ initFromWalker( walker );
+
+ postInstantiate();
+
+ log.debug( "Static select for entity " + entityName + ": " + getSQLString() );
+
+ }
+
+ public Object loadElement(SessionImplementor session, Object key, Object index)
+ throws HibernateException {
+
+ List list = loadEntity(
+ session,
+ key,
+ index,
+ keyType,
+ indexType,
+ persister
+ );
+
+ if ( list.size()==1 ) {
+ return list.get(0);
+ }
+ else if ( list.size()==0 ) {
+ return null;
+ }
+ else {
+ if ( getCollectionOwners()!=null ) {
+ return list.get(0);
+ }
+ else {
+ throw new HibernateException("More than one row was found");
+ }
+ }
+
+ }
+
+ protected Object getResultColumnOrRow(
+ Object[] row,
+ ResultTransformer transformer,
+ ResultSet rs, SessionImplementor session)
+ throws SQLException, HibernateException {
+ return row[row.length-1];
+ }
+
+ protected boolean isSingleRowLoader() {
+ return true;
+ }
+
+
+}
\ No newline at end of file
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/CollectionElementLoader.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/EntityJoinWalker.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/EntityJoinWalker.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/EntityJoinWalker.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,60 @@
+//$Id: EntityJoinWalker.java 7652 2005-07-26 05:51:47Z oneovthafew $
+package org.hibernate.loader.entity;
+
+import java.util.Collections;
+import java.util.Map;
+
+import org.hibernate.FetchMode;
+import org.hibernate.LockMode;
+import org.hibernate.MappingException;
+import org.hibernate.engine.CascadeStyle;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.loader.AbstractEntityJoinWalker;
+import org.hibernate.persister.entity.OuterJoinLoadable;
+import org.hibernate.type.AssociationType;
+
+/**
+ * A walker for loaders that fetch entities
+ *
+ * @see EntityLoader
+ * @author Gavin King
+ */
+public class EntityJoinWalker extends AbstractEntityJoinWalker {
+
+ private final LockMode lockMode;
+
+ public EntityJoinWalker(
+ OuterJoinLoadable persister,
+ String[] uniqueKey,
+ int batchSize,
+ LockMode lockMode,
+ SessionFactoryImplementor factory,
+ Map enabledFilters)
+ throws MappingException {
+ super(persister, factory, enabledFilters);
+
+ this.lockMode = lockMode;
+
+ StringBuffer whereCondition = whereString( getAlias(), uniqueKey, batchSize )
+ //include the discriminator and class-level where, but not filters
+ .append( persister.filterFragment( getAlias(), Collections.EMPTY_MAP ) );
+
+ initAll( whereCondition.toString(), "", lockMode );
+
+ }
+
+ /**
+ * Disable outer join fetching if this loader obtains an
+ * upgrade lock mode
+ */
+ protected boolean isJoinedFetchEnabled(AssociationType type, FetchMode config, CascadeStyle cascadeStyle) {
+ return lockMode.greaterThan(LockMode.READ) ?
+ false :
+ super.isJoinedFetchEnabled(type, config, cascadeStyle);
+ }
+
+ public String getComment() {
+ return "load " + getPersister().getEntityName();
+ }
+
+}
\ No newline at end of file
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/EntityJoinWalker.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/EntityLoader.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/EntityLoader.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/EntityLoader.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,92 @@
+//$Id: EntityLoader.java 7652 2005-07-26 05:51:47Z oneovthafew $
+package org.hibernate.loader.entity;
+
+import java.util.Map;
+
+import org.hibernate.HibernateException;
+import org.hibernate.LockMode;
+import org.hibernate.MappingException;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.loader.JoinWalker;
+import org.hibernate.persister.entity.OuterJoinLoadable;
+import org.hibernate.type.Type;
+
+/**
+ * Loads an entity instance using outerjoin fetching to fetch associated entities.
+ * <br>
+ * The <tt>EntityPersister</tt> must implement <tt>Loadable</tt>. For other entities,
+ * create a customized subclass of <tt>Loader</tt>.
+ *
+ * @author Gavin King
+ */
+public class EntityLoader extends AbstractEntityLoader {
+
+ private final boolean batchLoader;
+
+ public EntityLoader(
+ OuterJoinLoadable persister,
+ LockMode lockMode,
+ SessionFactoryImplementor factory,
+ Map enabledFilters)
+ throws MappingException {
+ this(persister, 1, lockMode, factory, enabledFilters);
+ }
+
+ public EntityLoader(
+ OuterJoinLoadable persister,
+ int batchSize,
+ LockMode lockMode,
+ SessionFactoryImplementor factory,
+ Map enabledFilters)
+ throws MappingException {
+ this(
+ persister,
+ persister.getIdentifierColumnNames(),
+ persister.getIdentifierType(),
+ batchSize,
+ lockMode,
+ factory,
+ enabledFilters
+ );
+ }
+
+ public EntityLoader(
+ OuterJoinLoadable persister,
+ String[] uniqueKey,
+ Type uniqueKeyType,
+ int batchSize,
+ LockMode lockMode,
+ SessionFactoryImplementor factory,
+ Map enabledFilters)
+ throws MappingException {
+ super(persister, uniqueKeyType, factory, enabledFilters);
+
+ JoinWalker walker = new EntityJoinWalker(
+ persister,
+ uniqueKey,
+ batchSize,
+ lockMode,
+ factory,
+ enabledFilters
+ );
+ initFromWalker( walker );
+
+ postInstantiate();
+
+ batchLoader = batchSize > 1;
+
+ log.debug( "Static select for entity " + entityName + ": " + getSQLString() );
+
+ }
+
+ public Object loadByUniqueKey(SessionImplementor session, Object key)
+ throws HibernateException {
+ return load(session, key, null, null);
+ }
+
+ protected boolean isSingleRowLoader() {
+ return !batchLoader;
+ }
+
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/UniqueEntityLoader.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/UniqueEntityLoader.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/UniqueEntityLoader.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,25 @@
+//$Id: UniqueEntityLoader.java 5699 2005-02-13 11:50:11Z oneovthafew $
+package org.hibernate.loader.entity;
+
+import java.io.Serializable;
+
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionImplementor;
+
+/**
+ * Loads entities for a <tt>EntityPersister</tt>
+ * @author Gavin King
+ */
+public interface UniqueEntityLoader {
+ /**
+ * Load an entity instance. If <tt>optionalObject</tt> is supplied,
+ * load the entity state into the given (uninitialized) object.
+ */
+ public Object load(Serializable id, Object optionalObject, SessionImplementor session) throws HibernateException;
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,8 @@
+<html>
+<head></head>
+<body>
+<p>
+ This package defines entity loaders
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/entity/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/hql/QueryLoader.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/hql/QueryLoader.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/hql/QueryLoader.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,520 @@
+// $Id: QueryLoader.java 11116 2007-01-30 14:30:55Z steve.ebersole at jboss.com $
+package org.hibernate.loader.hql;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.hibernate.HibernateException;
+import org.hibernate.LockMode;
+import org.hibernate.QueryException;
+import org.hibernate.ScrollableResults;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.event.EventSource;
+import org.hibernate.exception.JDBCExceptionHelper;
+import org.hibernate.hql.HolderInstantiator;
+import org.hibernate.hql.ast.QueryTranslatorImpl;
+import org.hibernate.hql.ast.tree.FromElement;
+import org.hibernate.hql.ast.tree.SelectClause;
+import org.hibernate.hql.ast.tree.QueryNode;
+import org.hibernate.impl.IteratorImpl;
+import org.hibernate.loader.BasicLoader;
+import org.hibernate.param.ParameterSpecification;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.persister.collection.QueryableCollection;
+import org.hibernate.persister.entity.Loadable;
+import org.hibernate.persister.entity.Queryable;
+import org.hibernate.persister.entity.Lockable;
+import org.hibernate.transform.ResultTransformer;
+import org.hibernate.type.EntityType;
+import org.hibernate.type.Type;
+import org.hibernate.util.ArrayHelper;
+
+/**
+ * A delegate that implements the Loader part of QueryTranslator.
+ *
+ * @author josh
+ */
+public class QueryLoader extends BasicLoader {
+
+ /**
+ * The query translator that is delegating to this object.
+ */
+ private QueryTranslatorImpl queryTranslator;
+
+ private Queryable[] entityPersisters;
+ private String[] entityAliases;
+ private String[] sqlAliases;
+ private String[] sqlAliasSuffixes;
+ private boolean[] includeInSelect;
+
+ private String[] collectionSuffixes;
+
+ private boolean hasScalars;
+ private String[][] scalarColumnNames;
+ //private Type[] sqlResultTypes;
+ private Type[] queryReturnTypes;
+
+ private final Map sqlAliasByEntityAlias = new HashMap(8);
+
+ private EntityType[] ownerAssociationTypes;
+ private int[] owners;
+ private boolean[] entityEagerPropertyFetches;
+
+ private int[] collectionOwners;
+ private QueryableCollection[] collectionPersisters;
+
+ private int selectLength;
+
+ private ResultTransformer selectNewTransformer;
+ private String[] queryReturnAliases;
+
+ private LockMode[] defaultLockModes;
+
+
+ /**
+ * Creates a new Loader implementation.
+ *
+ * @param queryTranslator The query translator that is the delegator.
+ * @param factory The factory from which this loader is being created.
+ * @param selectClause The AST representing the select clause for loading.
+ */
+ public QueryLoader(
+ final QueryTranslatorImpl queryTranslator,
+ final SessionFactoryImplementor factory,
+ final SelectClause selectClause) {
+ super( factory );
+ this.queryTranslator = queryTranslator;
+ initialize( selectClause );
+ postInstantiate();
+ }
+
+ private void initialize(SelectClause selectClause) {
+
+ List fromElementList = selectClause.getFromElementsForLoad();
+
+ hasScalars = selectClause.isScalarSelect();
+ scalarColumnNames = selectClause.getColumnNames();
+ //sqlResultTypes = selectClause.getSqlResultTypes();
+ queryReturnTypes = selectClause.getQueryReturnTypes();
+
+ selectNewTransformer = HolderInstantiator.createSelectNewTransformer(
+ selectClause.getConstructor(),
+ selectClause.isMap(),
+ selectClause.isList());
+ queryReturnAliases = selectClause.getQueryReturnAliases();
+
+ List collectionFromElements = selectClause.getCollectionFromElements();
+ if ( collectionFromElements != null && collectionFromElements.size()!=0 ) {
+ int length = collectionFromElements.size();
+ collectionPersisters = new QueryableCollection[length];
+ collectionOwners = new int[length];
+ collectionSuffixes = new String[length];
+ for ( int i=0; i<length; i++ ) {
+ FromElement collectionFromElement = (FromElement) collectionFromElements.get(i);
+ collectionPersisters[i] = collectionFromElement.getQueryableCollection();
+ collectionOwners[i] = fromElementList.indexOf( collectionFromElement.getOrigin() );
+// collectionSuffixes[i] = collectionFromElement.getColumnAliasSuffix();
+// collectionSuffixes[i] = Integer.toString( i ) + "_";
+ collectionSuffixes[i] = collectionFromElement.getCollectionSuffix();
+ }
+ }
+
+ int size = fromElementList.size();
+ entityPersisters = new Queryable[size];
+ entityEagerPropertyFetches = new boolean[size];
+ entityAliases = new String[size];
+ sqlAliases = new String[size];
+ sqlAliasSuffixes = new String[size];
+ includeInSelect = new boolean[size];
+ owners = new int[size];
+ ownerAssociationTypes = new EntityType[size];
+
+ for ( int i = 0; i < size; i++ ) {
+ final FromElement element = ( FromElement ) fromElementList.get( i );
+ entityPersisters[i] = ( Queryable ) element.getEntityPersister();
+
+ if ( entityPersisters[i] == null ) {
+ throw new IllegalStateException( "No entity persister for " + element.toString() );
+ }
+
+ entityEagerPropertyFetches[i] = element.isAllPropertyFetch();
+ sqlAliases[i] = element.getTableAlias();
+ entityAliases[i] = element.getClassAlias();
+ sqlAliasByEntityAlias.put( entityAliases[i], sqlAliases[i] );
+ // TODO should we just collect these like with the collections above?
+ sqlAliasSuffixes[i] = ( size == 1 ) ? "" : Integer.toString( i ) + "_";
+// sqlAliasSuffixes[i] = element.getColumnAliasSuffix();
+ includeInSelect[i] = !element.isFetch();
+ if ( includeInSelect[i] ) {
+ selectLength++;
+ }
+
+ owners[i] = -1; //by default
+ if ( element.isFetch() ) {
+ if ( element.isCollectionJoin() || element.getQueryableCollection() != null ) {
+ // This is now handled earlier in this method.
+ }
+ else if ( element.getDataType().isEntityType() ) {
+ EntityType entityType = ( EntityType ) element.getDataType();
+ if ( entityType.isOneToOne() ) {
+ owners[i] = fromElementList.indexOf( element.getOrigin() );
+ }
+ ownerAssociationTypes[i] = entityType;
+ }
+ }
+ }
+
+ //NONE, because its the requested lock mode, not the actual!
+ defaultLockModes = ArrayHelper.fillArray(LockMode.NONE, size);
+
+ }
+
+ // -- Loader implementation --
+
+ public final void validateScrollability() throws HibernateException {
+ queryTranslator.validateScrollability();
+ }
+
+ protected boolean needsFetchingScroll() {
+ return queryTranslator.containsCollectionFetches();
+ }
+
+ public Loadable[] getEntityPersisters() {
+ return entityPersisters;
+ }
+
+ public String[] getAliases() {
+ return sqlAliases;
+ }
+
+ public String[] getSqlAliasSuffixes() {
+ return sqlAliasSuffixes;
+ }
+
+ public String[] getSuffixes() {
+ return getSqlAliasSuffixes();
+ }
+
+ public String[] getCollectionSuffixes() {
+ return collectionSuffixes;
+ }
+
+ protected String getQueryIdentifier() {
+ return queryTranslator.getQueryIdentifier();
+ }
+
+ /**
+ * The SQL query string to be called.
+ */
+ protected String getSQLString() {
+ return queryTranslator.getSQLString();
+ }
+
+ /**
+ * An (optional) persister for a collection to be initialized; only collection loaders
+ * return a non-null value
+ */
+ protected CollectionPersister[] getCollectionPersisters() {
+ return collectionPersisters;
+ }
+
+ protected int[] getCollectionOwners() {
+ return collectionOwners;
+ }
+
+ protected boolean[] getEntityEagerPropertyFetches() {
+ return entityEagerPropertyFetches;
+ }
+
+ /**
+ * An array of indexes of the entity that owns a one-to-one association
+ * to the entity at the given index (-1 if there is no "owner")
+ */
+ protected int[] getOwners() {
+ return owners;
+ }
+
+ protected EntityType[] getOwnerAssociationTypes() {
+ return ownerAssociationTypes;
+ }
+
+ // -- Loader overrides --
+
+ protected boolean isSubselectLoadingEnabled() {
+ return hasSubselectLoadableCollections();
+ }
+
+ /**
+ * @param lockModes a collection of lock modes specified dynamically via the Query interface
+ */
+ protected LockMode[] getLockModes(Map lockModes) {
+
+ if ( lockModes==null || lockModes.size()==0 ) {
+ return defaultLockModes;
+ }
+ else {
+ // unfortunately this stuff can't be cached because
+ // it is per-invocation, not constant for the
+ // QueryTranslator instance
+
+ LockMode[] lockModeArray = new LockMode[entityAliases.length];
+ for ( int i = 0; i < entityAliases.length; i++ ) {
+ LockMode lockMode = (LockMode) lockModes.get( entityAliases[i] );
+ if ( lockMode == null ) {
+ //NONE, because its the requested lock mode, not the actual!
+ lockMode = LockMode.NONE;
+ }
+ lockModeArray[i] = lockMode;
+ }
+ return lockModeArray;
+ }
+ }
+
+ protected String applyLocks(String sql, Map lockModes, Dialect dialect) throws QueryException {
+ if ( lockModes == null || lockModes.size() == 0 ) {
+ return sql;
+ }
+
+ // can't cache this stuff either (per-invocation)
+ // we are given a map of user-alias -> lock mode
+ // create a new map of sql-alias -> lock mode
+ final Map aliasedLockModes = new HashMap();
+ final Map keyColumnNames = dialect.forUpdateOfColumns() ? new HashMap() : null;
+ final Iterator iter = lockModes.entrySet().iterator();
+ while ( iter.hasNext() ) {
+ Map.Entry me = ( Map.Entry ) iter.next();
+ final String userAlias = ( String ) me.getKey();
+ final String drivingSqlAlias = ( String ) sqlAliasByEntityAlias.get( userAlias );
+ if ( drivingSqlAlias == null ) {
+ throw new IllegalArgumentException( "could not locate alias to apply lock mode : " + userAlias );
+ }
+ // at this point we have (drivingSqlAlias) the SQL alias of the driving table
+ // corresponding to the given user alias. However, the driving table is not
+ // (necessarily) the table against which we want to apply locks. Mainly,
+ // the exception case here is joined-subclass hierarchies where we instead
+ // want to apply the lock against the root table (for all other strategies,
+ // it just happens that driving and root are the same).
+ final QueryNode select = ( QueryNode ) queryTranslator.getSqlAST();
+ final Lockable drivingPersister = ( Lockable ) select.getFromClause().getFromElement( userAlias ).getQueryable();
+ final String sqlAlias = drivingPersister.getRootTableAlias( drivingSqlAlias );
+ aliasedLockModes.put( sqlAlias, me.getValue() );
+ if ( keyColumnNames != null ) {
+ keyColumnNames.put( sqlAlias, drivingPersister.getRootTableIdentifierColumnNames() );
+ }
+ }
+ return dialect.applyLocksToSql( sql, aliasedLockModes, keyColumnNames );
+ }
+
+ protected boolean upgradeLocks() {
+ return true;
+ }
+
+ private boolean hasSelectNew() {
+ return selectNewTransformer!=null;
+ }
+
+ protected Object getResultColumnOrRow(Object[] row, ResultTransformer transformer, ResultSet rs, SessionImplementor session)
+ throws SQLException, HibernateException {
+
+ row = toResultRow( row );
+ boolean hasTransform = hasSelectNew() || transformer!=null;
+ if ( hasScalars ) {
+ String[][] scalarColumns = scalarColumnNames;
+ int queryCols = queryReturnTypes.length;
+ if ( !hasTransform && queryCols == 1 ) {
+ return queryReturnTypes[0].nullSafeGet( rs, scalarColumns[0], session, null );
+ }
+ else {
+ row = new Object[queryCols];
+ for ( int i = 0; i < queryCols; i++ ) {
+ row[i] = queryReturnTypes[i].nullSafeGet( rs, scalarColumns[i], session, null );
+ }
+ return row;
+ }
+ }
+ else if ( !hasTransform ) {
+ return row.length == 1 ? row[0] : row;
+ }
+ else {
+ return row;
+ }
+
+ }
+
+ protected List getResultList(List results, ResultTransformer resultTransformer) throws QueryException {
+ // meant to handle dynamic instantiation queries...
+ HolderInstantiator holderInstantiator = HolderInstantiator.getHolderInstantiator(selectNewTransformer, resultTransformer, queryReturnAliases);
+ if ( holderInstantiator.isRequired() ) {
+ for ( int i = 0; i < results.size(); i++ ) {
+ Object[] row = ( Object[] ) results.get( i );
+ Object result = holderInstantiator.instantiate(row);
+ results.set( i, result );
+ }
+
+ if(!hasSelectNew() && resultTransformer!=null) {
+ return resultTransformer.transformList(results);
+ } else {
+ return results;
+ }
+ } else {
+ return results;
+ }
+ }
+
+ // --- Query translator methods ---
+
+ public List list(
+ SessionImplementor session,
+ QueryParameters queryParameters) throws HibernateException {
+ checkQuery( queryParameters );
+ return list( session, queryParameters, queryTranslator.getQuerySpaces(), queryReturnTypes );
+ }
+
+ private void checkQuery(QueryParameters queryParameters) {
+ if ( hasSelectNew() && queryParameters.getResultTransformer() != null ) {
+ throw new QueryException( "ResultTransformer is not allowed for 'select new' queries." );
+ }
+ }
+
+ public Iterator iterate(
+ QueryParameters queryParameters,
+ EventSource session) throws HibernateException {
+ checkQuery( queryParameters );
+ final boolean stats = session.getFactory().getStatistics().isStatisticsEnabled();
+ long startTime = 0;
+ if ( stats ) {
+ startTime = System.currentTimeMillis();
+ }
+
+ try {
+
+ final PreparedStatement st = prepareQueryStatement( queryParameters, false, session );
+
+ if(queryParameters.isCallable()) {
+ throw new QueryException("iterate() not supported for callable statements");
+ }
+ final ResultSet rs = getResultSet(st, queryParameters.hasAutoDiscoverScalarTypes(), false, queryParameters.getRowSelection(), session);
+ final Iterator result = new IteratorImpl(
+ rs,
+ st,
+ session,
+ queryReturnTypes,
+ queryTranslator.getColumnNames(),
+ HolderInstantiator.getHolderInstantiator(selectNewTransformer, queryParameters.getResultTransformer(), queryReturnAliases)
+ );
+
+ if ( stats ) {
+ session.getFactory().getStatisticsImplementor().queryExecuted(
+// "HQL: " + queryTranslator.getQueryString(),
+ getQueryIdentifier(),
+ 0,
+ System.currentTimeMillis() - startTime
+ );
+ }
+
+ return result;
+
+ }
+ catch ( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ getFactory().getSQLExceptionConverter(),
+ sqle,
+ "could not execute query using iterate",
+ getSQLString()
+ );
+ }
+
+ }
+
+ public ScrollableResults scroll(
+ final QueryParameters queryParameters,
+ final SessionImplementor session) throws HibernateException {
+ checkQuery( queryParameters );
+ return scroll( queryParameters, queryReturnTypes, HolderInstantiator.getHolderInstantiator(selectNewTransformer, queryParameters.getResultTransformer(), queryReturnAliases), session );
+ }
+
+ // -- Implementation private methods --
+
+ private Object[] toResultRow(Object[] row) {
+ if ( selectLength == row.length ) {
+ return row;
+ }
+ else {
+ Object[] result = new Object[selectLength];
+ int j = 0;
+ for ( int i = 0; i < row.length; i++ ) {
+ if ( includeInSelect[i] ) {
+ result[j++] = row[i];
+ }
+ }
+ return result;
+ }
+ }
+
+ /**
+ * Returns the locations of all occurrences of the named parameter.
+ */
+ public int[] getNamedParameterLocs(String name) throws QueryException {
+ return queryTranslator.getParameterTranslations().getNamedParameterSqlLocations( name );
+ }
+
+ /**
+ * We specifically override this method here, because in general we know much more
+ * about the parameters and their appropriate bind positions here then we do in
+ * our super because we track them explciitly here through the ParameterSpecification
+ * interface.
+ *
+ * @param queryParameters The encapsulation of the parameter values to be bound.
+ * @param startIndex The position from which to start binding parameter values.
+ * @param session The originating session.
+ * @return The number of JDBC bind positions actually bound during this method execution.
+ * @throws SQLException Indicates problems performing the binding.
+ */
+ protected int bindParameterValues(
+ final PreparedStatement statement,
+ final QueryParameters queryParameters,
+ final int startIndex,
+ final SessionImplementor session) throws SQLException {
+ int position = bindFilterParameterValues( statement, queryParameters, startIndex, session );
+ List parameterSpecs = queryTranslator.getSqlAST().getWalker().getParameters();
+ Iterator itr = parameterSpecs.iterator();
+ while ( itr.hasNext() ) {
+ ParameterSpecification spec = ( ParameterSpecification ) itr.next();
+ position += spec.bind( statement, queryParameters, session, position );
+ }
+ return position - startIndex;
+ }
+
+ private int bindFilterParameterValues(
+ PreparedStatement st,
+ QueryParameters queryParameters,
+ int position,
+ SessionImplementor session) throws SQLException {
+ // todo : better to handle dynamic filters through implicit DynamicFilterParameterSpecification
+ // see the discussion there in DynamicFilterParameterSpecification's javadocs as to why
+ // it is currently not done that way.
+ int filteredParamCount = queryParameters.getFilteredPositionalParameterTypes() == null
+ ? 0
+ : queryParameters.getFilteredPositionalParameterTypes().length;
+ int nonfilteredParamCount = queryParameters.getPositionalParameterTypes() == null
+ ? 0
+ : queryParameters.getPositionalParameterTypes().length;
+ int filterParamCount = filteredParamCount - nonfilteredParamCount;
+ for ( int i = 0; i < filterParamCount; i++ ) {
+ Type type = queryParameters.getFilteredPositionalParameterTypes()[i];
+ Object value = queryParameters.getFilteredPositionalParameterValues()[i];
+ type.nullSafeSet( st, value, position, session );
+ position += type.getColumnSpan( getFactory() );
+ }
+
+ return position;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/hql/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/hql/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/hql/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,8 @@
+<html>
+<head></head>
+<body>
+<p>
+ This package defines a loader for the AST-based query parser
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/hql/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/loader/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/loader/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,14 @@
+<html>
+<head></head>
+<body>
+<p>
+ This package defines functionality for processing JDBC
+ result sets and returning complex graphs of persistent
+ objects.
+</p>
+<p>
+ Subclasses of <tt>Loader</tt> define a particular
+ query mechanism.
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/loader/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/lob/BlobImpl.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/lob/BlobImpl.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/lob/BlobImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,113 @@
+//$Id: BlobImpl.java 4997 2004-12-18 18:48:20Z oneovthafew $
+package org.hibernate.lob;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.sql.Blob;
+import java.sql.SQLException;
+
+/**
+ * A dummy implementation of <tt>java.sql.Blob</tt> that
+ * may be used to insert new data into a BLOB.
+ * @author Gavin King
+ */
+public class BlobImpl implements Blob {
+
+ private InputStream stream;
+ private int length;
+ private boolean needsReset = false;
+
+ public BlobImpl(byte[] bytes) {
+ this.stream = new ByteArrayInputStream(bytes);
+ this.length = bytes.length;
+ }
+
+ public BlobImpl(InputStream stream, int length) {
+ this.stream = stream;
+ this.length = length;
+ }
+
+ /**
+ * @see java.sql.Blob#length()
+ */
+ public long length() throws SQLException {
+ return length;
+ }
+
+ /**
+ * @see java.sql.Blob#truncate(long)
+ */
+ public void truncate(long pos) throws SQLException {
+ excep();
+ }
+
+ /**
+ * @see java.sql.Blob#getBytes(long, int)
+ */
+ public byte[] getBytes(long pos, int len) throws SQLException {
+ excep(); return null;
+ }
+
+ /**
+ * @see java.sql.Blob#setBytes(long, byte[])
+ */
+ public int setBytes(long pos, byte[] bytes) throws SQLException {
+ excep(); return 0;
+ }
+
+ /**
+ * @see java.sql.Blob#setBytes(long, byte[], int, int)
+ */
+ public int setBytes(long pos, byte[] bytes, int i, int j)
+ throws SQLException {
+ excep(); return 0;
+ }
+
+ /**
+ * @see java.sql.Blob#position(byte[], long)
+ */
+ public long position(byte[] bytes, long pos) throws SQLException {
+ excep(); return 0;
+ }
+
+ /**
+ * @see java.sql.Blob#getBinaryStream()
+ */
+ public InputStream getBinaryStream() throws SQLException {
+ try {
+ if (needsReset) stream.reset();
+ }
+ catch (IOException ioe) {
+ throw new SQLException("could not reset reader");
+ }
+ needsReset = true;
+ return stream;
+ }
+
+ /**
+ * @see java.sql.Blob#setBinaryStream(long)
+ */
+ public OutputStream setBinaryStream(long pos) throws SQLException {
+ excep(); return null;
+ }
+
+ /**
+ * @see java.sql.Blob#position(Blob, long)
+ */
+ public long position(Blob blob, long pos) throws SQLException {
+ excep(); return 0;
+ }
+
+ private static void excep() {
+ throw new UnsupportedOperationException("Blob may not be manipulated from creating session");
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/lob/ClobImpl.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/lob/ClobImpl.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/lob/ClobImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,137 @@
+//$Id: ClobImpl.java 5683 2005-02-12 03:09:22Z oneovthafew $
+package org.hibernate.lob;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.Writer;
+import java.sql.Clob;
+import java.sql.SQLException;
+
+/**
+ * A dummy implementation of <tt>java.sql.Clob</tt> that
+ * may be used to insert new data into a CLOB.
+ * @author Gavin King
+ */
+public class ClobImpl implements Clob {
+
+ private Reader reader;
+ private int length;
+ private boolean needsReset = false;
+
+ public ClobImpl(String string) {
+ reader = new StringReader(string);
+ length = string.length();
+ }
+
+ public ClobImpl(Reader reader, int length) {
+ this.reader = reader;
+ this.length = length;
+ }
+
+ /**
+ * @see java.sql.Clob#length()
+ */
+ public long length() throws SQLException {
+ return length;
+ }
+
+ /**
+ * @see java.sql.Clob#truncate(long)
+ */
+ public void truncate(long pos) throws SQLException {
+ excep();
+ }
+
+ /**
+ * @see java.sql.Clob#getAsciiStream()
+ */
+ public InputStream getAsciiStream() throws SQLException {
+ try {
+ if (needsReset) reader.reset();
+ }
+ catch (IOException ioe) {
+ throw new SQLException("could not reset reader");
+ }
+ needsReset = true;
+ return new ReaderInputStream(reader);
+ }
+
+ /**
+ * @see java.sql.Clob#setAsciiStream(long)
+ */
+ public OutputStream setAsciiStream(long pos) throws SQLException {
+ excep(); return null;
+ }
+
+ /**
+ * @see java.sql.Clob#getCharacterStream()
+ */
+ public Reader getCharacterStream() throws SQLException {
+ try {
+ if (needsReset) reader.reset();
+ }
+ catch (IOException ioe) {
+ throw new SQLException("could not reset reader");
+ }
+ needsReset = true;
+ return reader;
+ }
+
+ /**
+ * @see java.sql.Clob#setCharacterStream(long)
+ */
+ public Writer setCharacterStream(long pos) throws SQLException {
+ excep(); return null;
+ }
+
+ /**
+ * @see java.sql.Clob#getSubString(long, int)
+ */
+ public String getSubString(long pos, int len) throws SQLException {
+ excep(); return null;
+ }
+
+ /**
+ * @see java.sql.Clob#setString(long, String)
+ */
+ public int setString(long pos, String string) throws SQLException {
+ excep(); return 0;
+ }
+
+ /**
+ * @see java.sql.Clob#setString(long, String, int, int)
+ */
+ public int setString(long pos, String string, int i, int j)
+ throws SQLException {
+ excep(); return 0;
+ }
+
+ /**
+ * @see java.sql.Clob#position(String, long)
+ */
+ public long position(String string, long pos) throws SQLException {
+ excep(); return 0;
+ }
+
+ /**
+ * @see java.sql.Clob#position(Clob, long)
+ */
+ public long position(Clob colb, long pos) throws SQLException {
+ excep(); return 0;
+ }
+
+
+ private static void excep() {
+ throw new UnsupportedOperationException("Blob may not be manipulated from creating session");
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/lob/ReaderInputStream.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/lob/ReaderInputStream.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/lob/ReaderInputStream.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,24 @@
+//$Id: ReaderInputStream.java 5683 2005-02-12 03:09:22Z oneovthafew $
+package org.hibernate.lob;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+
+/**
+ * Exposes a <tt>Reader</tt> as an <tt>InputStream</tt>
+ * @author Gavin King
+ */
+public class ReaderInputStream extends InputStream {
+
+ private Reader reader;
+
+ public ReaderInputStream(Reader reader) {
+ this.reader = reader;
+ }
+
+ public int read() throws IOException {
+ return reader.read();
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/lob/ReaderInputStream.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/lob/SerializableBlob.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/lob/SerializableBlob.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/lob/SerializableBlob.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,66 @@
+//$Id: SerializableBlob.java 5986 2005-03-02 11:43:36Z oneovthafew $
+package org.hibernate.lob;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Serializable;
+import java.sql.Blob;
+import java.sql.SQLException;
+
+/**
+ * @author Gavin King
+ */
+public class SerializableBlob implements Serializable, Blob {
+
+ private transient final Blob blob;
+
+ public SerializableBlob(Blob blob) {
+ this.blob = blob;
+ }
+
+ public Blob getWrappedBlob() {
+ if ( blob==null ) {
+ throw new IllegalStateException("Blobs may not be accessed after serialization");
+ }
+ else {
+ return blob;
+ }
+ }
+
+ public long length() throws SQLException {
+ return getWrappedBlob().length();
+ }
+
+ public byte[] getBytes(long pos, int length) throws SQLException {
+ return getWrappedBlob().getBytes(pos, length);
+ }
+
+ public InputStream getBinaryStream() throws SQLException {
+ return getWrappedBlob().getBinaryStream();
+ }
+
+ public long position(byte[] pattern, long start) throws SQLException {
+ return getWrappedBlob().position(pattern, start);
+ }
+
+ public long position(Blob pattern, long start) throws SQLException {
+ return getWrappedBlob().position(pattern, start);
+ }
+
+ public int setBytes(long pos, byte[] bytes) throws SQLException {
+ return getWrappedBlob().setBytes(pos, bytes);
+ }
+
+ public int setBytes(long pos, byte[] bytes, int offset, int len) throws SQLException {
+ return getWrappedBlob().setBytes(pos, bytes, offset, len);
+ }
+
+ public OutputStream setBinaryStream(long pos) throws SQLException {
+ return getWrappedBlob().setBinaryStream(pos);
+ }
+
+ public void truncate(long len) throws SQLException {
+ getWrappedBlob().truncate(len);
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/lob/SerializableBlob.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/lob/SerializableClob.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/lob/SerializableClob.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/lob/SerializableClob.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,76 @@
+//$Id: SerializableClob.java 5986 2005-03-02 11:43:36Z oneovthafew $
+package org.hibernate.lob;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Serializable;
+import java.io.Writer;
+import java.sql.Clob;
+import java.sql.SQLException;
+
+/**
+ * @author Gavin King
+ */
+public class SerializableClob implements Serializable, Clob {
+
+ private transient final Clob clob;
+
+ public SerializableClob(Clob blob) {
+ this.clob = blob;
+ }
+
+ public Clob getWrappedClob() {
+ if ( clob==null ) {
+ throw new IllegalStateException("Clobs may not be accessed after serialization");
+ }
+ else {
+ return clob;
+ }
+ }
+
+ public long length() throws SQLException {
+ return getWrappedClob().length();
+ }
+
+ public String getSubString(long pos, int length) throws SQLException {
+ return getWrappedClob().getSubString(pos, length);
+ }
+
+ public Reader getCharacterStream() throws SQLException {
+ return getWrappedClob().getCharacterStream();
+ }
+
+ public InputStream getAsciiStream() throws SQLException {
+ return getWrappedClob().getAsciiStream();
+ }
+
+ public long position(String searchstr, long start) throws SQLException {
+ return getWrappedClob().position(searchstr, start);
+ }
+
+ public long position(Clob searchstr, long start) throws SQLException {
+ return getWrappedClob().position(searchstr, start);
+ }
+
+ public int setString(long pos, String str) throws SQLException {
+ return getWrappedClob().setString(pos, str);
+ }
+
+ public int setString(long pos, String str, int offset, int len) throws SQLException {
+ return getWrappedClob().setString(pos, str, offset, len);
+ }
+
+ public OutputStream setAsciiStream(long pos) throws SQLException {
+ return getWrappedClob().setAsciiStream(pos);
+ }
+
+ public Writer setCharacterStream(long pos) throws SQLException {
+ return getWrappedClob().setCharacterStream(pos);
+ }
+
+ public void truncate(long len) throws SQLException {
+ getWrappedClob().truncate(len);
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/lob/SerializableClob.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/lob/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/lob/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/lob/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,9 @@
+<html>
+<head></head>
+<body>
+<p>
+ This package defines dummy and wrapper implementations
+ of <tt>java.sql.Clob</tt> and <tt>java.sql.Blob</tt>.
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/lob/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/AbstractAuxiliaryDatabaseObject.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/AbstractAuxiliaryDatabaseObject.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/AbstractAuxiliaryDatabaseObject.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,41 @@
+// $Id: AbstractAuxiliaryDatabaseObject.java 7801 2005-08-10 12:16:55Z steveebersole $
+package org.hibernate.mapping;
+
+import java.util.HashSet;
+
+import org.hibernate.dialect.Dialect;
+
+/**
+ * Convenience base class for {@link AuxiliaryDatabaseObject}s.
+ * <p/>
+ * This implementation performs dialect scoping checks strictly based on
+ * dialect name comparisons. Custom implementations might want to do
+ * instanceof-type checks.
+ *
+ * @author Steve Ebersole
+ */
+public abstract class AbstractAuxiliaryDatabaseObject implements AuxiliaryDatabaseObject {
+
+ private final HashSet dialectScopes;
+
+ protected AbstractAuxiliaryDatabaseObject() {
+ this.dialectScopes = new HashSet();
+ }
+
+ protected AbstractAuxiliaryDatabaseObject(HashSet dialectScopes) {
+ this.dialectScopes = dialectScopes;
+ }
+
+ public void addDialectScope(String dialectName) {
+ dialectScopes.add( dialectName );
+ }
+
+ public HashSet getDialectScopes() {
+ return dialectScopes;
+ }
+
+ public boolean appliesToDialect(Dialect dialect) {
+ // empty means no scoping
+ return dialectScopes.isEmpty() || dialectScopes.contains( dialect.getClass().getName() );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Any.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Any.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Any.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,69 @@
+//$Id: Any.java 4905 2004-12-07 09:59:56Z maxcsaucdk $
+package org.hibernate.mapping;
+
+import java.util.Map;
+
+import org.hibernate.MappingException;
+import org.hibernate.type.MetaType;
+import org.hibernate.type.AnyType;
+import org.hibernate.type.Type;
+import org.hibernate.type.TypeFactory;
+
+/**
+ * A Hibernate "any" type (ie. polymorphic association to
+ * one-of-several tables).
+ * @author Gavin King
+ */
+public class Any extends SimpleValue {
+
+ private String identifierTypeName;
+ private String metaTypeName = "string";
+ private Map metaValues;
+
+ public Any(Table table) {
+ super(table);
+ }
+
+ public String getIdentifierType() {
+ return identifierTypeName;
+ }
+
+ public void setIdentifierType(String identifierType) {
+ this.identifierTypeName = identifierType;
+ }
+
+ public Type getType() throws MappingException {
+ return new AnyType(
+ metaValues==null ?
+ TypeFactory.heuristicType(metaTypeName) :
+ new MetaType( metaValues, TypeFactory.heuristicType(metaTypeName) ),
+ TypeFactory.heuristicType(identifierTypeName)
+ );
+ }
+
+ public void setTypeByReflection(String propertyClass, String propertyName) {}
+
+ public String getMetaType() {
+ return metaTypeName;
+ }
+
+ public void setMetaType(String type) {
+ metaTypeName = type;
+ }
+
+ public Map getMetaValues() {
+ return metaValues;
+ }
+
+ public void setMetaValues(Map metaValues) {
+ this.metaValues = metaValues;
+ }
+
+ public void setTypeUsingReflection(String className, String propertyName)
+ throws MappingException {
+ }
+
+ public Object accept(ValueVisitor visitor) {
+ return visitor.accept(this);
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Array.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Array.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Array.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,68 @@
+//$Id: Array.java 5793 2005-02-20 03:34:50Z oneovthafew $
+package org.hibernate.mapping;
+
+import org.hibernate.MappingException;
+import org.hibernate.type.CollectionType;
+import org.hibernate.type.PrimitiveType;
+import org.hibernate.type.TypeFactory;
+import org.hibernate.util.ReflectHelper;
+
+/**
+ * An array mapping has a primary key consisting of
+ * the key columns + index column.
+ * @author Gavin King
+ */
+public class Array extends List {
+
+ private String elementClassName;
+
+ /**
+ * Constructor for Array.
+ * @param owner
+ */
+ public Array(PersistentClass owner) {
+ super(owner);
+ }
+
+ public Class getElementClass() throws MappingException {
+ if (elementClassName==null) {
+ org.hibernate.type.Type elementType = getElement().getType();
+ return isPrimitiveArray() ?
+ ( (PrimitiveType) elementType ).getPrimitiveClass() :
+ elementType.getReturnedClass();
+ }
+ else {
+ try {
+ return ReflectHelper.classForName(elementClassName);
+ }
+ catch (ClassNotFoundException cnfe) {
+ throw new MappingException(cnfe);
+ }
+ }
+ }
+
+ public CollectionType getDefaultCollectionType() throws MappingException {
+ return TypeFactory.array( getRole(), getReferencedPropertyName(), isEmbedded(), getElementClass() );
+ }
+
+ public boolean isArray() {
+ return true;
+ }
+
+ /**
+ * @return Returns the elementClassName.
+ */
+ public String getElementClassName() {
+ return elementClassName;
+ }
+ /**
+ * @param elementClassName The elementClassName to set.
+ */
+ public void setElementClassName(String elementClassName) {
+ this.elementClassName = elementClassName;
+ }
+
+ public Object accept(ValueVisitor visitor) {
+ return visitor.accept(this);
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/AuxiliaryDatabaseObject.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/AuxiliaryDatabaseObject.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/AuxiliaryDatabaseObject.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,31 @@
+// $Id: AuxiliaryDatabaseObject.java 7800 2005-08-10 12:13:00Z steveebersole $
+package org.hibernate.mapping;
+
+import java.io.Serializable;
+
+import org.hibernate.dialect.Dialect;
+
+/**
+ * Auxiliary database objects (i.e., triggers, stored procedures, etc) defined
+ * in the mappings. Allows Hibernate to manage their lifecycle as part of
+ * creating/dropping the schema.
+ *
+ * @author Steve Ebersole
+ */
+public interface AuxiliaryDatabaseObject extends RelationalModel, Serializable {
+ /**
+ * Add the given dialect name to the scope of dialects to which
+ * this database object applies.
+ *
+ * @param dialectName The name of a dialect.
+ */
+ void addDialectScope(String dialectName);
+
+ /**
+ * Does this database object apply to the given dialect?
+ *
+ * @param dialect The dialect to check against.
+ * @return True if this database object does apply to the given dialect.
+ */
+ boolean appliesToDialect(Dialect dialect);
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Backref.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Backref.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Backref.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,38 @@
+//$Id: Backref.java 6924 2005-05-27 01:30:18Z oneovthafew $
+package org.hibernate.mapping;
+
+import org.hibernate.property.BackrefPropertyAccessor;
+import org.hibernate.property.PropertyAccessor;
+
+/**
+ * @author Gavin King
+ */
+public class Backref extends Property {
+ private String collectionRole;
+ private String entityName;
+
+ public boolean isBackRef() {
+ return true;
+ }
+ public String getCollectionRole() {
+ return collectionRole;
+ }
+ public void setCollectionRole(String collectionRole) {
+ this.collectionRole = collectionRole;
+ }
+
+ public boolean isBasicPropertyAccessor() {
+ return false;
+ }
+
+ public PropertyAccessor getPropertyAccessor(Class clazz) {
+ return new BackrefPropertyAccessor(collectionRole, entityName);
+ }
+
+ public String getEntityName() {
+ return entityName;
+ }
+ public void setEntityName(String entityName) {
+ this.entityName = entityName;
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Backref.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Bag.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Bag.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Bag.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,28 @@
+//$Id: Bag.java 5793 2005-02-20 03:34:50Z oneovthafew $
+package org.hibernate.mapping;
+
+import org.hibernate.type.CollectionType;
+import org.hibernate.type.TypeFactory;
+
+/**
+ * A bag permits duplicates, so it has no primary key
+ * @author Gavin King
+ */
+public class Bag extends Collection {
+
+ public Bag(PersistentClass owner) {
+ super(owner);
+ }
+
+ public CollectionType getDefaultCollectionType() {
+ return TypeFactory.bag( getRole(), getReferencedPropertyName(), isEmbedded() );
+ }
+
+ void createPrimaryKey() {
+ //create an index on the key columns??
+ }
+
+ public Object accept(ValueVisitor visitor) {
+ return visitor.accept(this);
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Collection.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Collection.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Collection.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,622 @@
+// $Id: Collection.java 11496 2007-05-09 03:54:06Z steve.ebersole at jboss.com $
+package org.hibernate.mapping;
+
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Properties;
+
+import org.hibernate.FetchMode;
+import org.hibernate.MappingException;
+import org.hibernate.engine.Mapping;
+import org.hibernate.engine.ExecuteUpdateResultCheckStyle;
+import org.hibernate.type.CollectionType;
+import org.hibernate.type.Type;
+import org.hibernate.type.TypeFactory;
+import org.hibernate.util.ArrayHelper;
+import org.hibernate.util.EmptyIterator;
+import org.hibernate.util.ReflectHelper;
+
+/**
+ * Mapping for a collection. Subclasses specialize to particular collection styles.
+ *
+ * @author Gavin King
+ */
+public abstract class Collection implements Fetchable, Value, Filterable {
+
+ public static final String DEFAULT_ELEMENT_COLUMN_NAME = "elt";
+ public static final String DEFAULT_KEY_COLUMN_NAME = "id";
+
+ private KeyValue key;
+ private Value element;
+ private Table collectionTable;
+ private String role;
+ private boolean lazy;
+ private boolean extraLazy;
+ private boolean inverse;
+ private boolean mutable = true;
+ private boolean subselectLoadable;
+ private String cacheConcurrencyStrategy;
+ private String cacheRegionName;
+ private String orderBy;
+ private String where;
+ private String manyToManyWhere;
+ private String manyToManyOrderBy;
+ private PersistentClass owner;
+ private String referencedPropertyName;
+ private String nodeName;
+ private String elementNodeName;
+ private boolean sorted;
+ private Comparator comparator;
+ private String comparatorClassName;
+ private boolean orphanDelete;
+ private int batchSize = -1;
+ private FetchMode fetchMode;
+ private boolean embedded = true;
+ private boolean optimisticLocked = true;
+ private Class collectionPersisterClass;
+ private String typeName;
+ private Properties typeParameters;
+ private final java.util.Map filters = new HashMap();
+ private final java.util.Map manyToManyFilters = new HashMap();
+ private final java.util.Set synchronizedTables = new HashSet();
+
+ private String customSQLInsert;
+ private boolean customInsertCallable;
+ private ExecuteUpdateResultCheckStyle insertCheckStyle;
+ private String customSQLUpdate;
+ private boolean customUpdateCallable;
+ private ExecuteUpdateResultCheckStyle updateCheckStyle;
+ private String customSQLDelete;
+ private boolean customDeleteCallable;
+ private ExecuteUpdateResultCheckStyle deleteCheckStyle;
+ private String customSQLDeleteAll;
+ private boolean customDeleteAllCallable;
+ private ExecuteUpdateResultCheckStyle deleteAllCheckStyle;
+
+ private String loaderName;
+
+ protected Collection(PersistentClass owner) {
+ this.owner = owner;
+ }
+
+ public boolean isSet() {
+ return false;
+ }
+
+ public KeyValue getKey() {
+ return key;
+ }
+
+ public Value getElement() {
+ return element;
+ }
+
+ public boolean isIndexed() {
+ return false;
+ }
+
+ public Table getCollectionTable() {
+ return collectionTable;
+ }
+
+ public void setCollectionTable(Table table) {
+ this.collectionTable = table;
+ }
+
+ public boolean isSorted() {
+ return sorted;
+ }
+
+ public Comparator getComparator() {
+ if ( comparator == null && comparatorClassName != null ) {
+ try {
+ setComparator( (Comparator) ReflectHelper.classForName( comparatorClassName ).newInstance() );
+ }
+ catch ( Exception e ) {
+ throw new MappingException(
+ "Could not instantiate comparator class [" + comparatorClassName
+ + "] for collection " + getRole()
+ );
+ }
+ }
+ return comparator;
+ }
+
+ public boolean isLazy() {
+ return lazy;
+ }
+
+ public void setLazy(boolean lazy) {
+ this.lazy = lazy;
+ }
+
+ public String getRole() {
+ return role;
+ }
+
+ public abstract CollectionType getDefaultCollectionType() throws MappingException;
+
+ public boolean isPrimitiveArray() {
+ return false;
+ }
+
+ public boolean isArray() {
+ return false;
+ }
+
+ public boolean hasFormula() {
+ return false;
+ }
+
+ public boolean isOneToMany() {
+ return element instanceof OneToMany;
+ }
+
+ public boolean isInverse() {
+ return inverse;
+ }
+
+ public String getOwnerEntityName() {
+ return owner.getEntityName();
+ }
+
+ public String getOrderBy() {
+ return orderBy;
+ }
+
+ public void setComparator(Comparator comparator) {
+ this.comparator = comparator;
+ }
+
+ public void setElement(Value element) {
+ this.element = element;
+ }
+
+ public void setKey(KeyValue key) {
+ this.key = key;
+ }
+
+ public void setOrderBy(String orderBy) {
+ this.orderBy = orderBy;
+ }
+
+ public void setRole(String role) {
+ this.role = role==null ? null : role.intern();
+ }
+
+ public void setSorted(boolean sorted) {
+ this.sorted = sorted;
+ }
+
+ public void setInverse(boolean inverse) {
+ this.inverse = inverse;
+ }
+
+ public PersistentClass getOwner() {
+ return owner;
+ }
+
+ public void setOwner(PersistentClass owner) {
+ this.owner = owner;
+ }
+
+ public String getWhere() {
+ return where;
+ }
+
+ public void setWhere(String where) {
+ this.where = where;
+ }
+
+ public String getManyToManyWhere() {
+ return manyToManyWhere;
+ }
+
+ public void setManyToManyWhere(String manyToManyWhere) {
+ this.manyToManyWhere = manyToManyWhere;
+ }
+
+ public String getManyToManyOrdering() {
+ return manyToManyOrderBy;
+ }
+
+ public void setManyToManyOrdering(String orderFragment) {
+ this.manyToManyOrderBy = orderFragment;
+ }
+
+ public boolean isIdentified() {
+ return false;
+ }
+
+ public boolean hasOrphanDelete() {
+ return orphanDelete;
+ }
+
+ public void setOrphanDelete(boolean orphanDelete) {
+ this.orphanDelete = orphanDelete;
+ }
+
+ public int getBatchSize() {
+ return batchSize;
+ }
+
+ public void setBatchSize(int i) {
+ batchSize = i;
+ }
+
+ public FetchMode getFetchMode() {
+ return fetchMode;
+ }
+
+ public void setFetchMode(FetchMode fetchMode) {
+ this.fetchMode = fetchMode;
+ }
+
+ public void setCollectionPersisterClass(Class persister) {
+ this.collectionPersisterClass = persister;
+ }
+
+ public Class getCollectionPersisterClass() {
+ return collectionPersisterClass;
+ }
+
+ public void validate(Mapping mapping) throws MappingException {
+ if ( getKey().isCascadeDeleteEnabled() && ( !isInverse() || !isOneToMany() ) ) {
+ throw new MappingException(
+ "only inverse one-to-many associations may use on-delete=\"cascade\": "
+ + getRole() );
+ }
+ if ( !getKey().isValid( mapping ) ) {
+ throw new MappingException(
+ "collection foreign key mapping has wrong number of columns: "
+ + getRole()
+ + " type: "
+ + getKey().getType().getName() );
+ }
+ if ( !getElement().isValid( mapping ) ) {
+ throw new MappingException(
+ "collection element mapping has wrong number of columns: "
+ + getRole()
+ + " type: "
+ + getElement().getType().getName() );
+ }
+
+ checkColumnDuplication();
+
+ if ( elementNodeName!=null && elementNodeName.startsWith("@") ) {
+ throw new MappingException("element node must not be an attribute: " + elementNodeName );
+ }
+ if ( elementNodeName!=null && elementNodeName.equals(".") ) {
+ throw new MappingException("element node must not be the parent: " + elementNodeName );
+ }
+ if ( nodeName!=null && nodeName.indexOf('@')>-1 ) {
+ throw new MappingException("collection node must not be an attribute: " + elementNodeName );
+ }
+ }
+
+ private void checkColumnDuplication(java.util.Set distinctColumns, Iterator columns)
+ throws MappingException {
+ while ( columns.hasNext() ) {
+ Selectable s = (Selectable) columns.next();
+ if ( !s.isFormula() ) {
+ Column col = (Column) s;
+ if ( !distinctColumns.add( col.getName() ) ) {
+ throw new MappingException( "Repeated column in mapping for collection: "
+ + getRole()
+ + " column: "
+ + col.getName() );
+ }
+ }
+ }
+ }
+
+ private void checkColumnDuplication() throws MappingException {
+ HashSet cols = new HashSet();
+ checkColumnDuplication( cols, getKey().getColumnIterator() );
+ if ( isIndexed() ) {
+ checkColumnDuplication( cols, ( (IndexedCollection) this )
+ .getIndex()
+ .getColumnIterator() );
+ }
+ if ( isIdentified() ) {
+ checkColumnDuplication( cols, ( (IdentifierCollection) this )
+ .getIdentifier()
+ .getColumnIterator() );
+ }
+ if ( !isOneToMany() ) {
+ checkColumnDuplication( cols, getElement().getColumnIterator() );
+ }
+ }
+
+ public Iterator getColumnIterator() {
+ return EmptyIterator.INSTANCE;
+ }
+
+ public int getColumnSpan() {
+ return 0;
+ }
+
+ public Type getType() throws MappingException {
+ return getCollectionType();
+ }
+
+ public CollectionType getCollectionType() {
+ if ( typeName == null ) {
+ return getDefaultCollectionType();
+ }
+ else {
+ return TypeFactory.customCollection( typeName, typeParameters, role, referencedPropertyName, isEmbedded() );
+ }
+ }
+
+ public boolean isNullable() {
+ return true;
+ }
+
+ public boolean isAlternateUniqueKey() {
+ return false;
+ }
+
+ public Table getTable() {
+ return owner.getTable();
+ }
+
+ public void createForeignKey() {
+ }
+
+ public boolean isSimpleValue() {
+ return false;
+ }
+
+ public boolean isValid(Mapping mapping) throws MappingException {
+ return true;
+ }
+
+ private void createForeignKeys() throws MappingException {
+ // if ( !isInverse() ) { // for inverse collections, let the "other end" handle it
+ if ( referencedPropertyName == null ) {
+ getElement().createForeignKey();
+ key.createForeignKeyOfEntity( getOwner().getEntityName() );
+ }
+ // }
+ }
+
+ abstract void createPrimaryKey();
+
+ public void createAllKeys() throws MappingException {
+ createForeignKeys();
+ if ( !isInverse() ) createPrimaryKey();
+ }
+
+ public String getCacheConcurrencyStrategy() {
+ return cacheConcurrencyStrategy;
+ }
+
+ public void setCacheConcurrencyStrategy(String cacheConcurrencyStrategy) {
+ this.cacheConcurrencyStrategy = cacheConcurrencyStrategy;
+ }
+
+ public void setTypeUsingReflection(String className, String propertyName) {
+ }
+
+ public String getCacheRegionName() {
+ return cacheRegionName == null ? role : cacheRegionName;
+ }
+
+ public void setCacheRegionName(String cacheRegionName) {
+ this.cacheRegionName = cacheRegionName;
+ }
+
+
+
+ public void setCustomSQLInsert(String customSQLInsert, boolean callable, ExecuteUpdateResultCheckStyle checkStyle) {
+ this.customSQLInsert = customSQLInsert;
+ this.customInsertCallable = callable;
+ this.insertCheckStyle = checkStyle;
+ }
+
+ public String getCustomSQLInsert() {
+ return customSQLInsert;
+ }
+
+ public boolean isCustomInsertCallable() {
+ return customInsertCallable;
+ }
+
+ public ExecuteUpdateResultCheckStyle getCustomSQLInsertCheckStyle() {
+ return insertCheckStyle;
+ }
+
+ public void setCustomSQLUpdate(String customSQLUpdate, boolean callable, ExecuteUpdateResultCheckStyle checkStyle) {
+ this.customSQLUpdate = customSQLUpdate;
+ this.customUpdateCallable = callable;
+ this.updateCheckStyle = checkStyle;
+ }
+
+ public String getCustomSQLUpdate() {
+ return customSQLUpdate;
+ }
+
+ public boolean isCustomUpdateCallable() {
+ return customUpdateCallable;
+ }
+
+ public ExecuteUpdateResultCheckStyle getCustomSQLUpdateCheckStyle() {
+ return updateCheckStyle;
+ }
+
+ public void setCustomSQLDelete(String customSQLDelete, boolean callable, ExecuteUpdateResultCheckStyle checkStyle) {
+ this.customSQLDelete = customSQLDelete;
+ this.customDeleteCallable = callable;
+ this.deleteCheckStyle = checkStyle;
+ }
+
+ public String getCustomSQLDelete() {
+ return customSQLDelete;
+ }
+
+ public boolean isCustomDeleteCallable() {
+ return customDeleteCallable;
+ }
+
+ public ExecuteUpdateResultCheckStyle getCustomSQLDeleteCheckStyle() {
+ return deleteCheckStyle;
+ }
+
+ public void setCustomSQLDeleteAll(String customSQLDeleteAll, boolean callable, ExecuteUpdateResultCheckStyle checkStyle) {
+ this.customSQLDeleteAll = customSQLDeleteAll;
+ this.customDeleteAllCallable = callable;
+ this.deleteAllCheckStyle = checkStyle;
+ }
+
+ public String getCustomSQLDeleteAll() {
+ return customSQLDeleteAll;
+ }
+
+ public boolean isCustomDeleteAllCallable() {
+ return customDeleteAllCallable;
+ }
+
+ public ExecuteUpdateResultCheckStyle getCustomSQLDeleteAllCheckStyle() {
+ return deleteAllCheckStyle;
+ }
+
+ public void addFilter(String name, String condition) {
+ filters.put( name, condition );
+ }
+
+ public java.util.Map getFilterMap() {
+ return filters;
+ }
+
+ public void addManyToManyFilter(String name, String condition) {
+ manyToManyFilters.put( name, condition );
+ }
+
+ public java.util.Map getManyToManyFilterMap() {
+ return manyToManyFilters;
+ }
+
+ public String toString() {
+ return getClass().getName() + '(' + getRole() + ')';
+ }
+
+ public java.util.Set getSynchronizedTables() {
+ return synchronizedTables;
+ }
+
+ public String getLoaderName() {
+ return loaderName;
+ }
+
+ public void setLoaderName(String name) {
+ this.loaderName = name==null ? null : name.intern();
+ }
+
+ public String getReferencedPropertyName() {
+ return referencedPropertyName;
+ }
+
+ public void setReferencedPropertyName(String propertyRef) {
+ this.referencedPropertyName = propertyRef==null ? null : propertyRef.intern();
+ }
+
+ public boolean isOptimisticLocked() {
+ return optimisticLocked;
+ }
+
+ public void setOptimisticLocked(boolean optimisticLocked) {
+ this.optimisticLocked = optimisticLocked;
+ }
+
+ public boolean isMap() {
+ return false;
+ }
+
+ public String getTypeName() {
+ return typeName;
+ }
+
+ public void setTypeName(String typeName) {
+ this.typeName = typeName;
+ }
+
+ public Properties getTypeParameters() {
+ return typeParameters;
+ }
+
+ public void setTypeParameters(Properties parameterMap) {
+ this.typeParameters = parameterMap;
+ }
+
+ public boolean[] getColumnInsertability() {
+ return ArrayHelper.EMPTY_BOOLEAN_ARRAY;
+ }
+
+ public boolean[] getColumnUpdateability() {
+ return ArrayHelper.EMPTY_BOOLEAN_ARRAY;
+ }
+
+ public String getNodeName() {
+ return nodeName;
+ }
+
+ public void setNodeName(String nodeName) {
+ this.nodeName = nodeName;
+ }
+
+ public String getElementNodeName() {
+ return elementNodeName;
+ }
+
+ public void setElementNodeName(String elementNodeName) {
+ this.elementNodeName = elementNodeName;
+ }
+
+ public boolean isEmbedded() {
+ return embedded;
+ }
+
+ public void setEmbedded(boolean embedded) {
+ this.embedded = embedded;
+ }
+
+ public boolean isSubselectLoadable() {
+ return subselectLoadable;
+ }
+
+
+ public void setSubselectLoadable(boolean subqueryLoadable) {
+ this.subselectLoadable = subqueryLoadable;
+ }
+
+ public boolean isMutable() {
+ return mutable;
+ }
+
+ public void setMutable(boolean mutable) {
+ this.mutable = mutable;
+ }
+
+ public boolean isExtraLazy() {
+ return extraLazy;
+ }
+
+ public void setExtraLazy(boolean extraLazy) {
+ this.extraLazy = extraLazy;
+ }
+
+ public boolean hasOrder() {
+ return orderBy!=null || manyToManyOrderBy!=null;
+ }
+
+ public void setComparatorClassName(String comparatorClassName) {
+ this.comparatorClassName = comparatorClassName;
+ }
+
+ public String getComparatorClassName() {
+ return comparatorClassName;
+ }
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Column.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Column.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Column.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,310 @@
+//$Id: Column.java 9908 2006-05-08 20:59:20Z max.andersen at jboss.com $
+package org.hibernate.mapping;
+
+import java.io.Serializable;
+
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.function.SQLFunctionRegistry;
+import org.hibernate.engine.Mapping;
+import org.hibernate.util.StringHelper;
+
+/**
+ * A column of a relational database table
+ * @author Gavin King
+ */
+public class Column implements Selectable, Serializable, Cloneable {
+
+ public static final int DEFAULT_LENGTH = 255;
+ public static final int DEFAULT_PRECISION = 19;
+ public static final int DEFAULT_SCALE = 2;
+
+ private int length=DEFAULT_LENGTH;
+ private int precision=DEFAULT_PRECISION;
+ private int scale=DEFAULT_SCALE;
+ private Value value;
+ private int typeIndex = 0;
+ private String name;
+ private boolean nullable=true;
+ private boolean unique=false;
+ private String sqlType;
+ private Integer sqlTypeCode;
+ private boolean quoted=false;
+ int uniqueInteger;
+ private String checkConstraint;
+ private String comment;
+ private String defaultValue;
+
+ public Column() { };
+
+ public Column(String columnName) {
+ setName(columnName);
+ }
+
+ public int getLength() {
+ return length;
+ }
+ public void setLength(int length) {
+ this.length = length;
+ }
+ public Value getValue() {
+ return value;
+ }
+ public void setValue(Value value) {
+ this.value= value;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ if (
+ name.charAt(0)=='`' ||
+ Dialect.QUOTE.indexOf( name.charAt(0) ) > -1 //TODO: deprecated, remove eventually
+ ) {
+ quoted=true;
+ this.name=name.substring( 1, name.length()-1 );
+ }
+ else {
+ this.name = name;
+ }
+ }
+
+ /** returns quoted name as it would be in the mapping file. */
+ public String getQuotedName() {
+ return quoted ?
+ "`" + name + "`" :
+ name;
+ }
+
+ public String getQuotedName(Dialect d) {
+ return quoted ?
+ d.openQuote() + name + d.closeQuote() :
+ name;
+ }
+
+ /**
+ * For any column name, generate an alias that is unique
+ * to that column name, and also 10 characters or less
+ * in length.
+ */
+ public String getAlias(Dialect dialect) {
+ String alias = name;
+ String unique = Integer.toString(uniqueInteger) + '_';
+ int lastLetter = StringHelper.lastIndexOfLetter(name);
+ if ( lastLetter == -1 ) {
+ alias = "column";
+ }
+ else if ( lastLetter < name.length()-1 ) {
+ alias = name.substring(0, lastLetter+1);
+ }
+ if ( alias.length() > dialect.getMaxAliasLength() ) {
+ alias = alias.substring( 0, dialect.getMaxAliasLength() - unique.length() );
+ }
+ boolean useRawName = name.equals(alias) &&
+ !quoted &&
+ !name.toLowerCase().equals("rowid");
+ if ( useRawName ) {
+ return alias;
+ }
+ else {
+ return alias + unique;
+ }
+ }
+
+ /**
+ * Generate a column alias that is unique across multiple tables
+ */
+ public String getAlias(Dialect dialect, Table table) {
+ return getAlias(dialect) + table.getUniqueInteger() + '_';
+ }
+
+ public boolean isNullable() {
+ return nullable;
+ }
+
+ public void setNullable(boolean nullable) {
+ this.nullable=nullable;
+ }
+
+ public int getTypeIndex() {
+ return typeIndex;
+ }
+ public void setTypeIndex(int typeIndex) {
+ this.typeIndex = typeIndex;
+ }
+
+ public int getSqlTypeCode(Mapping mapping) throws MappingException {
+ org.hibernate.type.Type type = getValue().getType();
+ try {
+ int sqlTypeCode = type.sqlTypes(mapping)[ getTypeIndex() ];
+ if(getSqlTypeCode()!=null && getSqlTypeCode().intValue()!=sqlTypeCode) {
+ throw new MappingException("SQLType code's does not match. mapped as " + sqlTypeCode + " but is " + getSqlTypeCode() );
+ }
+ return sqlTypeCode;
+ }
+ catch (Exception e) {
+ throw new MappingException(
+ "Could not determine type for column " +
+ name +
+ " of type " +
+ type.getClass().getName() +
+ ": " +
+ e.getClass().getName(),
+ e
+ );
+ }
+ }
+
+ /**
+ * Returns the underlying columns sqltypecode.
+ * If null, it is because the sqltype code is unknown.
+ *
+ * Use #getSqlTypeCode(Mapping) to retreive the sqltypecode used
+ * for the columns associated Value/Type.
+ *
+ * @return sqltypecode if it is set, otherwise null.
+ */
+ public Integer getSqlTypeCode() {
+ return sqlTypeCode;
+ }
+
+ public void setSqlTypeCode(Integer typecode) {
+ sqlTypeCode=typecode;
+ }
+
+ public boolean isUnique() {
+ return unique;
+ }
+
+
+ public String getSqlType(Dialect dialect, Mapping mapping) throws HibernateException {
+ return sqlType==null ?
+ dialect.getTypeName( getSqlTypeCode(mapping), getLength(), getPrecision(), getScale() ) :
+ sqlType;
+ }
+
+ public boolean equals(Object object) {
+ return object instanceof Column && equals( (Column) object );
+ }
+
+ public boolean equals(Column column) {
+ if (null == column) return false;
+ if (this == column) return true;
+
+ return isQuoted() ?
+ name.equals(column.name) :
+ name.equalsIgnoreCase(column.name);
+ }
+
+ //used also for generation of FK names!
+ public int hashCode() {
+ return isQuoted() ?
+ name.hashCode() :
+ name.toLowerCase().hashCode();
+ }
+
+ public String getSqlType() {
+ return sqlType;
+ }
+
+ public void setSqlType(String sqlType) {
+ this.sqlType = sqlType;
+ }
+
+ public void setUnique(boolean unique) {
+ this.unique = unique;
+ }
+
+ public boolean isQuoted() {
+ return quoted;
+ }
+
+ public String toString() {
+ return getClass().getName() + '(' + getName() + ')';
+ }
+
+ public String getCheckConstraint() {
+ return checkConstraint;
+ }
+
+ public void setCheckConstraint(String checkConstraint) {
+ this.checkConstraint = checkConstraint;
+ }
+
+ public boolean hasCheckConstraint() {
+ return checkConstraint!=null;
+ }
+
+ public String getTemplate(Dialect dialect, SQLFunctionRegistry functionRegistry) {
+ return getQuotedName(dialect);
+ }
+
+ public boolean isFormula() {
+ return false;
+ }
+
+ public String getText(Dialect d) {
+ return getQuotedName(d);
+ }
+ public String getText() {
+ return getName();
+ }
+
+ public int getPrecision() {
+ return precision;
+ }
+ public void setPrecision(int scale) {
+ this.precision = scale;
+ }
+
+ public int getScale() {
+ return scale;
+ }
+ public void setScale(int scale) {
+ this.scale = scale;
+ }
+
+ public String getComment() {
+ return comment;
+ }
+
+ public void setComment(String comment) {
+ this.comment = comment;
+ }
+
+ public String getDefaultValue() {
+ return defaultValue;
+ }
+
+ public void setDefaultValue(String defaultValue) {
+ this.defaultValue = defaultValue;
+ }
+
+ public String getCanonicalName() {
+ return quoted ? name : name.toLowerCase();
+ }
+
+ /**
+ * Shallow copy, the value is not copied
+ */
+ protected Object clone() {
+ Column copy = new Column();
+ copy.setLength( length );
+ copy.setScale( scale );
+ copy.setValue( value );
+ copy.setTypeIndex( typeIndex );
+ copy.setName( getQuotedName() );
+ copy.setNullable( nullable );
+ copy.setPrecision( precision );
+ copy.setUnique( unique );
+ copy.setSqlType( sqlType );
+ copy.setSqlTypeCode( sqlTypeCode );
+ copy.uniqueInteger = uniqueInteger; //usually useless
+ copy.setCheckConstraint( checkConstraint );
+ copy.setComment( comment );
+ copy.setDefaultValue( defaultValue );
+ return copy;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Component.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Component.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Component.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,275 @@
+//$Id: Component.java 10119 2006-07-14 00:09:19Z steve.ebersole at jboss.com $
+package org.hibernate.mapping;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.hibernate.EntityMode;
+import org.hibernate.MappingException;
+import org.hibernate.tuple.component.ComponentMetamodel;
+import org.hibernate.type.ComponentType;
+import org.hibernate.type.EmbeddedComponentType;
+import org.hibernate.type.Type;
+import org.hibernate.util.JoinedIterator;
+import org.hibernate.util.ReflectHelper;
+
+/**
+ * The mapping for a component, composite element,
+ * composite identifier, etc.
+ * @author Gavin King
+ */
+public class Component extends SimpleValue implements MetaAttributable {
+
+ private ArrayList properties = new ArrayList();
+ private String componentClassName;
+ private boolean embedded;
+ private String parentProperty;
+ private PersistentClass owner;
+ private boolean dynamic;
+ private Map metaAttributes;
+ private String nodeName;
+ private boolean isKey;
+ private String roleName;
+
+ private java.util.Map tuplizerImpls;
+
+ public Component(PersistentClass owner) throws MappingException {
+ super( owner.getTable() );
+ this.owner = owner;
+ }
+
+ public Component(Component component) throws MappingException {
+ super( component.getTable() );
+ this.owner = component.getOwner();
+ }
+
+ public Component(Join join) throws MappingException {
+ super( join.getTable() );
+ this.owner = join.getPersistentClass();
+ }
+
+ public Component(Collection collection) throws MappingException {
+ super( collection.getCollectionTable() );
+ this.owner = collection.getOwner();
+ }
+
+ public int getPropertySpan() {
+ return properties.size();
+ }
+ public Iterator getPropertyIterator() {
+ return properties.iterator();
+ }
+ public void addProperty(Property p) {
+ properties.add(p);
+ }
+ public void addColumn(Column column) {
+ throw new UnsupportedOperationException("Cant add a column to a component");
+ }
+ public int getColumnSpan() {
+ int n=0;
+ Iterator iter = getPropertyIterator();
+ while ( iter.hasNext() ) {
+ Property p = (Property) iter.next();
+ n+= p.getColumnSpan();
+ }
+ return n;
+ }
+ public Iterator getColumnIterator() {
+ Iterator[] iters = new Iterator[ getPropertySpan() ];
+ Iterator iter = getPropertyIterator();
+ int i=0;
+ while ( iter.hasNext() ) {
+ iters[i++] = ( (Property) iter.next() ).getColumnIterator();
+ }
+ return new JoinedIterator(iters);
+ }
+
+ public void setTypeByReflection(String propertyClass, String propertyName) {}
+
+ public boolean isEmbedded() {
+ return embedded;
+ }
+
+ public String getComponentClassName() {
+ return componentClassName;
+ }
+
+ public Class getComponentClass() throws MappingException {
+ try {
+ return ReflectHelper.classForName(componentClassName);
+ }
+ catch (ClassNotFoundException cnfe) {
+ throw new MappingException("component class not found: " + componentClassName, cnfe);
+ }
+ }
+
+ public PersistentClass getOwner() {
+ return owner;
+ }
+
+ public String getParentProperty() {
+ return parentProperty;
+ }
+
+ public void setComponentClassName(String componentClass) {
+ this.componentClassName = componentClass;
+ }
+
+ public void setEmbedded(boolean embedded) {
+ this.embedded = embedded;
+ }
+
+ public void setOwner(PersistentClass owner) {
+ this.owner = owner;
+ }
+
+ public void setParentProperty(String parentProperty) {
+ this.parentProperty = parentProperty;
+ }
+
+ public boolean isDynamic() {
+ return dynamic;
+ }
+
+ public void setDynamic(boolean dynamic) {
+ this.dynamic = dynamic;
+ }
+
+ private Type type;
+
+ public Type getType() throws MappingException {
+ // added this caching as I noticed that getType() is being called multiple times...
+ if ( type == null ) {
+ type = buildType();
+ }
+ return type;
+ }
+
+ private Type buildType() {
+ // TODO : temporary initial step towards HHH-1907
+ ComponentMetamodel metamodel = new ComponentMetamodel( this );
+ if ( isEmbedded() ) {
+ return new EmbeddedComponentType( metamodel );
+ }
+ else {
+ return new ComponentType( metamodel );
+ }
+ }
+
+ public void setTypeUsingReflection(String className, String propertyName)
+ throws MappingException {
+ }
+
+ public java.util.Map getMetaAttributes() {
+ return metaAttributes;
+ }
+ public MetaAttribute getMetaAttribute(String attributeName) {
+ return metaAttributes==null?null:(MetaAttribute) metaAttributes.get(attributeName);
+ }
+
+ public void setMetaAttributes(java.util.Map metas) {
+ this.metaAttributes = metas;
+ }
+
+ public Object accept(ValueVisitor visitor) {
+ return visitor.accept(this);
+ }
+
+ public boolean[] getColumnInsertability() {
+ boolean[] result = new boolean[ getColumnSpan() ];
+ Iterator iter = getPropertyIterator();
+ int i=0;
+ while ( iter.hasNext() ) {
+ Property prop = (Property) iter.next();
+ boolean[] chunk = prop.getValue().getColumnInsertability();
+ if ( prop.isInsertable() ) {
+ System.arraycopy(chunk, 0, result, i, chunk.length);
+ }
+ i+=chunk.length;
+ }
+ return result;
+ }
+
+ public boolean[] getColumnUpdateability() {
+ boolean[] result = new boolean[ getColumnSpan() ];
+ Iterator iter = getPropertyIterator();
+ int i=0;
+ while ( iter.hasNext() ) {
+ Property prop = (Property) iter.next();
+ boolean[] chunk = prop.getValue().getColumnUpdateability();
+ if ( prop.isUpdateable() ) {
+ System.arraycopy(chunk, 0, result, i, chunk.length);
+ }
+ i+=chunk.length;
+ }
+ return result;
+ }
+
+ public String getNodeName() {
+ return nodeName;
+ }
+
+ public void setNodeName(String nodeName) {
+ this.nodeName = nodeName;
+ }
+
+ public boolean isKey() {
+ return isKey;
+ }
+
+ public void setKey(boolean isKey) {
+ this.isKey = isKey;
+ }
+
+ public boolean hasPojoRepresentation() {
+ return componentClassName!=null;
+ }
+
+ public void addTuplizer(EntityMode entityMode, String implClassName) {
+ if ( tuplizerImpls == null ) {
+ tuplizerImpls = new HashMap();
+ }
+ tuplizerImpls.put( entityMode, implClassName );
+ }
+
+ public String getTuplizerImplClassName(EntityMode mode) {
+ // todo : remove this once ComponentMetamodel is complete and merged
+ if ( tuplizerImpls == null ) {
+ return null;
+ }
+ return ( String ) tuplizerImpls.get( mode );
+ }
+
+ public Map getTuplizerMap() {
+ if ( tuplizerImpls == null ) {
+ return null;
+ }
+ return java.util.Collections.unmodifiableMap( tuplizerImpls );
+ }
+
+ public Property getProperty(String propertyName) throws MappingException {
+ Iterator iter = getPropertyIterator();
+ while ( iter.hasNext() ) {
+ Property prop = (Property) iter.next();
+ if ( prop.getName().equals(propertyName) ) {
+ return prop;
+ }
+ }
+ throw new MappingException("component property not found: " + propertyName);
+ }
+
+ public String getRoleName() {
+ return roleName;
+ }
+
+ public void setRoleName(String roleName) {
+ this.roleName = roleName;
+ }
+
+ public String toString() {
+ return getClass().getName() + '(' + properties.toString() + ')';
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Constraint.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Constraint.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Constraint.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,111 @@
+//$Id: Constraint.java 10661 2006-10-31 02:19:13Z epbernard $
+package org.hibernate.mapping;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.Mapping;
+
+/**
+ * A relational constraint.
+ *
+ * @author Gavin King
+ */
+public abstract class Constraint implements RelationalModel, Serializable {
+
+ private String name;
+ private final List columns = new ArrayList();
+ private Table table;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Iterator getColumnIterator() {
+ return columns.iterator();
+ }
+
+ public void addColumn(Column column) {
+ if ( !columns.contains( column ) ) columns.add( column );
+ }
+
+ public void addColumns(Iterator columnIterator) {
+ while ( columnIterator.hasNext() ) {
+ Selectable col = (Selectable) columnIterator.next();
+ if ( !col.isFormula() ) addColumn( (Column) col );
+ }
+ }
+
+ /**
+ * @param column
+ * @return true if this constraint already contains a column with same name.
+ */
+ public boolean containsColumn(Column column) {
+ return columns.contains( column );
+ }
+
+ public int getColumnSpan() {
+ return columns.size();
+ }
+
+ public Column getColumn(int i) {
+ return (Column) columns.get( i );
+ }
+
+ public Iterator columnIterator() {
+ return columns.iterator();
+ }
+
+ public Table getTable() {
+ return table;
+ }
+
+ public void setTable(Table table) {
+ this.table = table;
+ }
+
+ public boolean isGenerated(Dialect dialect) {
+ return true;
+ }
+
+ public String sqlDropString(Dialect dialect, String defaultCatalog, String defaultSchema) {
+ if ( isGenerated( dialect ) ) {
+ return "alter table " + getTable()
+ .getQualifiedName( dialect, defaultCatalog, defaultSchema ) + " drop constraint " + getName();
+ }
+ else {
+ return null;
+ }
+ }
+
+ public String sqlCreateString(Dialect dialect, Mapping p, String defaultCatalog, String defaultSchema) {
+ if ( isGenerated( dialect ) ) {
+ String constraintString = sqlConstraintString( dialect, getName(), defaultCatalog, defaultSchema );
+ StringBuffer buf = new StringBuffer( "alter table " )
+ .append( getTable().getQualifiedName( dialect, defaultCatalog, defaultSchema ) )
+ .append( constraintString );
+ return buf.toString();
+ }
+ else {
+ return null;
+ }
+ }
+
+ public List getColumns() {
+ return columns;
+ }
+
+ public abstract String sqlConstraintString(Dialect d, String constraintName, String defaultCatalog,
+ String defaultSchema);
+
+ public String toString() {
+ return getClass().getName() + '(' + getTable().getName() + getColumns() + ") as " + name;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/DenormalizedTable.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/DenormalizedTable.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/DenormalizedTable.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,90 @@
+//$Id: DenormalizedTable.java 10880 2006-11-29 15:18:34Z epbernard $
+package org.hibernate.mapping;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.hibernate.util.JoinedIterator;
+
+/**
+ * @author Gavin King
+ */
+public class DenormalizedTable extends Table {
+
+ private final Table includedTable;
+
+ public DenormalizedTable(Table includedTable) {
+ this.includedTable = includedTable;
+ includedTable.setHasDenormalizedTables();
+ }
+
+ public void createForeignKeys() {
+ includedTable.createForeignKeys();
+ Iterator iter = includedTable.getForeignKeyIterator();
+ while ( iter.hasNext() ) {
+ ForeignKey fk = (ForeignKey) iter.next();
+ createForeignKey(
+ fk.getName() + Integer.toHexString( getName().hashCode() ),
+ fk.getColumns(),
+ fk.getReferencedEntityName()
+ );
+ }
+ }
+
+ public Column getColumn(Column column) {
+ Column superColumn = super.getColumn( column );
+ if (superColumn != null) {
+ return superColumn;
+ }
+ else {
+ return includedTable.getColumn( column );
+ }
+ }
+
+ public Iterator getColumnIterator() {
+ return new JoinedIterator(
+ includedTable.getColumnIterator(),
+ super.getColumnIterator()
+ );
+ }
+
+ public boolean containsColumn(Column column) {
+ return super.containsColumn(column) || includedTable.containsColumn(column);
+ }
+
+ public PrimaryKey getPrimaryKey() {
+ return includedTable.getPrimaryKey();
+ }
+
+ public Iterator getUniqueKeyIterator() {
+ //wierd implementation because of hacky behavior
+ //of Table.sqlCreateString() which modifies the
+ //list of unique keys by side-effect on some
+ //dialects
+ Map uks = new HashMap();
+ uks.putAll( getUniqueKeys() );
+ uks.putAll( includedTable.getUniqueKeys() );
+ return uks.values().iterator();
+ }
+
+ public Iterator getIndexIterator() {
+ List indexes = new ArrayList();
+ Iterator iter = includedTable.getIndexIterator();
+ while ( iter.hasNext() ) {
+ Index parentIndex = (Index) iter.next();
+ Index index = new Index();
+ index.setName( getName() + parentIndex.getName() );
+ index.setTable(this);
+ index.addColumns( parentIndex.getColumnIterator() );
+ indexes.add( index );
+ }
+ return new JoinedIterator(
+ indexes.iterator(),
+ super.getIndexIterator()
+ );
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/DependantValue.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/DependantValue.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/DependantValue.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,50 @@
+//$Id: DependantValue.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate.mapping;
+
+import org.hibernate.MappingException;
+import org.hibernate.type.Type;
+
+/**
+ * A value which is "typed" by reference to some other
+ * value (for example, a foreign key is typed by the
+ * referenced primary key).
+ *
+ * @author Gavin King
+ */
+public class DependantValue extends SimpleValue {
+ private KeyValue wrappedValue;
+ private boolean nullable;
+ private boolean updateable;
+
+ public DependantValue(Table table, KeyValue prototype) {
+ super(table);
+ this.wrappedValue = prototype;
+ }
+
+ public Type getType() throws MappingException {
+ return wrappedValue.getType();
+ }
+
+ public void setTypeUsingReflection(String className, String propertyName) {}
+
+ public Object accept(ValueVisitor visitor) {
+ return visitor.accept(this);
+ }
+
+ public boolean isNullable() {
+ return nullable;
+
+ }
+
+ public void setNullable(boolean nullable) {
+ this.nullable = nullable;
+ }
+
+ public boolean isUpdateable() {
+ return updateable;
+ }
+
+ public void setUpdateable(boolean updateable) {
+ this.updateable = updateable;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Fetchable.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Fetchable.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Fetchable.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,15 @@
+//$Id: Fetchable.java 6586 2005-04-28 08:35:10Z oneovthafew $
+package org.hibernate.mapping;
+
+import org.hibernate.FetchMode;
+
+/**
+ * Any mapping with an outer-join attribute
+ * @author Gavin King
+ */
+public interface Fetchable {
+ public FetchMode getFetchMode();
+ public void setFetchMode(FetchMode joinedFetch);
+ public boolean isLazy();
+ public void setLazy(boolean lazy);
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Filterable.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Filterable.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Filterable.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,13 @@
+// $Id: Filterable.java 4219 2004-08-10 05:11:47Z steveebersole $
+package org.hibernate.mapping;
+
+/**
+ * Defines mapping elements to which filters may be applied.
+ *
+ * @author Steve Ebersole
+ */
+public interface Filterable {
+ public void addFilter(String name, String condition);
+
+ public java.util.Map getFilterMap();
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/ForeignKey.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/ForeignKey.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/ForeignKey.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,163 @@
+//$Id: ForeignKey.java 7360 2005-07-01 16:38:03Z maxcsaucdk $
+package org.hibernate.mapping;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.hibernate.MappingException;
+import org.hibernate.dialect.Dialect;
+
+/**
+ * A foreign key constraint
+ * @author Gavin King
+ */
+public class ForeignKey extends Constraint {
+
+ private Table referencedTable;
+ private String referencedEntityName;
+ private boolean cascadeDeleteEnabled;
+ private List referencedColumns = new ArrayList();
+
+ public String sqlConstraintString(Dialect dialect, String constraintName, String defaultCatalog, String defaultSchema) {
+ String[] cols = new String[ getColumnSpan() ];
+ String[] refcols = new String[ getColumnSpan() ];
+ int i=0;
+ Iterator refiter = null;
+ if(isReferenceToPrimaryKey() ) {
+ refiter = referencedTable.getPrimaryKey().getColumnIterator();
+ }
+ else {
+ refiter = referencedColumns.iterator();
+ }
+
+ Iterator iter = getColumnIterator();
+ while ( iter.hasNext() ) {
+ cols[i] = ( (Column) iter.next() ).getQuotedName(dialect);
+ refcols[i] = ( (Column) refiter.next() ).getQuotedName(dialect);
+ i++;
+ }
+ String result = dialect.getAddForeignKeyConstraintString(
+ constraintName, cols, referencedTable.getQualifiedName(dialect, defaultCatalog, defaultSchema), refcols, isReferenceToPrimaryKey()
+ );
+ return cascadeDeleteEnabled && dialect.supportsCascadeDelete() ?
+ result + " on delete cascade" :
+ result;
+ }
+
+ public Table getReferencedTable() {
+ return referencedTable;
+ }
+
+ private void appendColumns(StringBuffer buf, Iterator columns) {
+ while( columns.hasNext() ) {
+ Column column = (Column) columns.next();
+ buf.append( column.getName() );
+ if ( columns.hasNext() ) buf.append(",");
+ }
+ }
+
+ public void setReferencedTable(Table referencedTable) throws MappingException {
+ //if( isReferenceToPrimaryKey() ) alignColumns(referencedTable); // TODO: possibly remove to allow more piecemal building of a foreignkey.
+
+ this.referencedTable = referencedTable;
+ }
+
+ /**
+ * Validates that columnspan of the foreignkey and the primarykey is the same.
+ *
+ * Furthermore it aligns the length of the underlying tables columns.
+ * @param referencedTable
+ */
+ public void alignColumns() {
+ if ( isReferenceToPrimaryKey() ) alignColumns(referencedTable);
+ }
+
+ private void alignColumns(Table referencedTable) {
+ if ( referencedTable.getPrimaryKey().getColumnSpan()!=getColumnSpan() ) {
+ StringBuffer sb = new StringBuffer();
+ sb.append("Foreign key (")
+ .append( getName() + ":")
+ .append( getTable().getName() )
+ .append(" [");
+ appendColumns( sb, getColumnIterator() );
+ sb.append("])")
+ .append(") must have same number of columns as the referenced primary key (")
+ .append( referencedTable.getName() )
+ .append(" [");
+ appendColumns( sb, referencedTable.getPrimaryKey().getColumnIterator() );
+ sb.append("])");
+ throw new MappingException( sb.toString() );
+ }
+
+ Iterator fkCols = getColumnIterator();
+ Iterator pkCols = referencedTable.getPrimaryKey().getColumnIterator();
+ while ( pkCols.hasNext() ) {
+ ( (Column) fkCols.next() ).setLength( ( (Column) pkCols.next() ).getLength() );
+ }
+
+ }
+
+ public String getReferencedEntityName() {
+ return referencedEntityName;
+ }
+
+ public void setReferencedEntityName(String referencedEntityName) {
+ this.referencedEntityName = referencedEntityName;
+ }
+
+ public String sqlDropString(Dialect dialect, String defaultCatalog, String defaultSchema) {
+ return "alter table " +
+ getTable().getQualifiedName(dialect, defaultCatalog, defaultSchema) +
+ dialect.getDropForeignKeyString() +
+ getName();
+ }
+
+ public boolean isCascadeDeleteEnabled() {
+ return cascadeDeleteEnabled;
+ }
+
+ public void setCascadeDeleteEnabled(boolean cascadeDeleteEnabled) {
+ this.cascadeDeleteEnabled = cascadeDeleteEnabled;
+ }
+
+ public boolean isPhysicalConstraint() {
+ return referencedTable.isPhysicalTable() &&
+ getTable().isPhysicalTable() &&
+ !referencedTable.hasDenormalizedTables();
+ }
+
+ /** Returns the referenced columns if the foreignkey does not refer to the primary key */
+ public List getReferencedColumns() {
+ return referencedColumns;
+ }
+
+ /** Does this foreignkey reference the primary key of the reference table */
+ public boolean isReferenceToPrimaryKey() {
+ return referencedColumns.isEmpty();
+ }
+
+ public void addReferencedColumns(Iterator referencedColumnsIterator) {
+ while ( referencedColumnsIterator.hasNext() ) {
+ Selectable col = (Selectable) referencedColumnsIterator.next();
+ if ( !col.isFormula() ) addReferencedColumn( (Column) col );
+ }
+ }
+
+ private void addReferencedColumn(Column column) {
+ if ( !referencedColumns.contains(column) ) referencedColumns.add(column);
+ }
+
+ public String toString() {
+ if(!isReferenceToPrimaryKey() ) {
+ StringBuffer result = new StringBuffer(getClass().getName() + '(' + getTable().getName() + getColumns() );
+ result.append( " ref-columns:" + '(' + getReferencedColumns() );
+ result.append( ") as " + getName() );
+ return result.toString();
+ }
+ else {
+ return super.toString();
+ }
+
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Formula.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Formula.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Formula.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,52 @@
+//$Id: Formula.java 9908 2006-05-08 20:59:20Z max.andersen at jboss.com $
+package org.hibernate.mapping;
+
+import java.io.Serializable;
+
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.function.SQLFunctionRegistry;
+import org.hibernate.sql.Template;
+
+/**
+ * A formula is a derived column value
+ * @author Gavin King
+ */
+public class Formula implements Selectable, Serializable {
+ private static int formulaUniqueInteger=0;
+
+ private String formula;
+ private int uniqueInteger;
+
+ public Formula() {
+ uniqueInteger = formulaUniqueInteger++;
+ }
+
+ public String getTemplate(Dialect dialect, SQLFunctionRegistry functionRegistry) {
+ return Template.renderWhereStringTemplate(formula, dialect, functionRegistry);
+ }
+ public String getText(Dialect dialect) {
+ return getFormula();
+ }
+ public String getText() {
+ return getFormula();
+ }
+ public String getAlias(Dialect dialect) {
+ return "formula" + Integer.toString(uniqueInteger) + '_';
+ }
+ public String getAlias(Dialect dialect, Table table) {
+ return getAlias(dialect);
+ }
+ public String getFormula() {
+ return formula;
+ }
+ public void setFormula(String string) {
+ formula = string;
+ }
+ public boolean isFormula() {
+ return true;
+ }
+
+ public String toString() {
+ return this.getClass().getName() + "( " + formula + " )";
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/IdentifierBag.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/IdentifierBag.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/IdentifierBag.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,24 @@
+//$Id: IdentifierBag.java 5793 2005-02-20 03:34:50Z oneovthafew $
+package org.hibernate.mapping;
+
+import org.hibernate.type.CollectionType;
+import org.hibernate.type.TypeFactory;
+
+/**
+ * An <tt>IdentifierBag</tt> has a primary key consisting of
+ * just the identifier column
+ */
+public class IdentifierBag extends IdentifierCollection {
+
+ public IdentifierBag(PersistentClass owner) {
+ super(owner);
+ }
+
+ public CollectionType getDefaultCollectionType() {
+ return TypeFactory.idbag( getRole(), getReferencedPropertyName(), isEmbedded() );
+ }
+
+ public Object accept(ValueVisitor visitor) {
+ return visitor.accept(this);
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/IdentifierCollection.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/IdentifierCollection.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/IdentifierCollection.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,64 @@
+//$Id: IdentifierCollection.java 4173 2004-08-08 04:56:01Z oneovthafew $
+package org.hibernate.mapping;
+
+import org.hibernate.MappingException;
+import org.hibernate.engine.Mapping;
+
+/**
+ * A collection with a synthetic "identifier" column
+ */
+public abstract class IdentifierCollection extends Collection {
+
+ public static final String DEFAULT_IDENTIFIER_COLUMN_NAME = "id";
+
+ private KeyValue identifier;
+
+ public IdentifierCollection(PersistentClass owner) {
+ super(owner);
+ }
+
+ public KeyValue getIdentifier() {
+ return identifier;
+ }
+ public void setIdentifier(KeyValue identifier) {
+ this.identifier = identifier;
+ }
+ public final boolean isIdentified() {
+ return true;
+ }
+
+ void createPrimaryKey() {
+ if ( !isOneToMany() ) {
+ PrimaryKey pk = new PrimaryKey();
+ pk.addColumns( getIdentifier().getColumnIterator() );
+ getCollectionTable().setPrimaryKey(pk);
+ }
+ else {
+ // don't create a unique key, 'cos some
+ // databases don't like a UK on nullable
+ // columns
+ //getCollectionTable().createUniqueKey( getIdentifier().getConstraintColumns() );
+ }
+ // create an index on the key columns??
+ }
+
+ public void validate(Mapping mapping) throws MappingException {
+ super.validate(mapping);
+ if ( !getIdentifier().isValid(mapping) ) {
+ throw new MappingException(
+ "collection id mapping has wrong number of columns: " +
+ getRole() +
+ " type: " +
+ getIdentifier().getType().getName()
+ );
+ }
+ }
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Index.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Index.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Index.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,145 @@
+//$Id: Index.java 10661 2006-10-31 02:19:13Z epbernard $
+package org.hibernate.mapping;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.hibernate.HibernateException;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.Mapping;
+import org.hibernate.util.StringHelper;
+
+/**
+ * A relational table index
+ *
+ * @author Gavin King
+ */
+public class Index implements RelationalModel, Serializable {
+
+ private Table table;
+ private List columns = new ArrayList();
+ private String name;
+
+ public String sqlCreateString(Dialect dialect, Mapping mapping, String defaultCatalog, String defaultSchema)
+ throws HibernateException {
+ return buildSqlCreateIndexString(
+ dialect,
+ getName(),
+ getTable(),
+ getColumnIterator(),
+ false,
+ defaultCatalog,
+ defaultSchema
+ );
+ }
+
+ public static String buildSqlDropIndexString(
+ Dialect dialect,
+ Table table,
+ String name,
+ String defaultCatalog,
+ String defaultSchema
+ ) {
+ return "drop index " +
+ StringHelper.qualify(
+ table.getQualifiedName( dialect, defaultCatalog, defaultSchema ),
+ name
+ );
+ }
+
+ public static String buildSqlCreateIndexString(
+ Dialect dialect,
+ String name,
+ Table table,
+ Iterator columns,
+ boolean unique,
+ String defaultCatalog,
+ String defaultSchema
+ ) {
+ //TODO handle supportsNotNullUnique=false, but such a case does not exist in the wild so far
+ StringBuffer buf = new StringBuffer( "create" )
+ .append( unique ?
+ " unique" :
+ "" )
+ .append( " index " )
+ .append( dialect.qualifyIndexName() ?
+ name :
+ StringHelper.unqualify( name ) )
+ .append( " on " )
+ .append( table.getQualifiedName( dialect, defaultCatalog, defaultSchema ) )
+ .append( " (" );
+ Iterator iter = columns;
+ while ( iter.hasNext() ) {
+ buf.append( ( (Column) iter.next() ).getQuotedName( dialect ) );
+ if ( iter.hasNext() ) buf.append( ", " );
+ }
+ buf.append( ")" );
+ return buf.toString();
+ }
+
+
+ // Used only in Table for sqlCreateString (but commented out at the moment)
+ public String sqlConstraintString(Dialect dialect) {
+ StringBuffer buf = new StringBuffer( " index (" );
+ Iterator iter = getColumnIterator();
+ while ( iter.hasNext() ) {
+ buf.append( ( (Column) iter.next() ).getQuotedName( dialect ) );
+ if ( iter.hasNext() ) buf.append( ", " );
+ }
+ return buf.append( ')' ).toString();
+ }
+
+ public String sqlDropString(Dialect dialect, String defaultCatalog, String defaultSchema) {
+ return "drop index " +
+ StringHelper.qualify(
+ table.getQualifiedName( dialect, defaultCatalog, defaultSchema ),
+ name
+ );
+ }
+
+ public Table getTable() {
+ return table;
+ }
+
+ public void setTable(Table table) {
+ this.table = table;
+ }
+
+ public int getColumnSpan() {
+ return columns.size();
+ }
+
+ public Iterator getColumnIterator() {
+ return columns.iterator();
+ }
+
+ public void addColumn(Column column) {
+ if ( !columns.contains( column ) ) columns.add( column );
+ }
+
+ public void addColumns(Iterator extraColumns) {
+ while ( extraColumns.hasNext() ) addColumn( (Column) extraColumns.next() );
+ }
+
+ /**
+ * @param column
+ * @return true if this constraint already contains a column with same name.
+ */
+ public boolean containsColumn(Column column) {
+ return columns.contains( column );
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String toString() {
+ return getClass().getName() + "(" + getName() + ")";
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/IndexBackref.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/IndexBackref.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/IndexBackref.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,38 @@
+//$Id: IndexBackref.java 6924 2005-05-27 01:30:18Z oneovthafew $
+package org.hibernate.mapping;
+
+import org.hibernate.property.IndexPropertyAccessor;
+import org.hibernate.property.PropertyAccessor;
+
+/**
+ * @author Gavin King
+ */
+public class IndexBackref extends Property {
+ private String collectionRole;
+ private String entityName;
+
+ public boolean isBackRef() {
+ return true;
+ }
+ public String getCollectionRole() {
+ return collectionRole;
+ }
+ public void setCollectionRole(String collectionRole) {
+ this.collectionRole = collectionRole;
+ }
+
+ public boolean isBasicPropertyAccessor() {
+ return false;
+ }
+
+ public PropertyAccessor getPropertyAccessor(Class clazz) {
+ return new IndexPropertyAccessor(collectionRole, entityName);
+ }
+
+ public String getEntityName() {
+ return entityName;
+ }
+ public void setEntityName(String entityName) {
+ this.entityName = entityName;
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/IndexBackref.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/IndexedCollection.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/IndexedCollection.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/IndexedCollection.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,94 @@
+//$Id: IndexedCollection.java 5797 2005-02-20 08:37:56Z oneovthafew $
+package org.hibernate.mapping;
+
+import java.util.Iterator;
+
+import org.hibernate.MappingException;
+import org.hibernate.engine.Mapping;
+
+/**
+ * Indexed collections include Lists, Maps, arrays and
+ * primitive arrays.
+ * @author Gavin King
+ */
+public abstract class IndexedCollection extends Collection {
+
+ public static final String DEFAULT_INDEX_COLUMN_NAME = "idx";
+
+ private Value index;
+ private String indexNodeName;
+
+ public IndexedCollection(PersistentClass owner) {
+ super(owner);
+ }
+
+ public Value getIndex() {
+ return index;
+ }
+ public void setIndex(Value index) {
+ this.index = index;
+ }
+ public final boolean isIndexed() {
+ return true;
+ }
+
+ void createPrimaryKey() {
+ if ( !isOneToMany() ) {
+ PrimaryKey pk = new PrimaryKey();
+ pk.addColumns( getKey().getColumnIterator() );
+
+ // index should be last column listed
+ boolean isFormula = false;
+ Iterator iter = getIndex().getColumnIterator();
+ while ( iter.hasNext() ) {
+ if ( ( (Selectable) iter.next() ).isFormula() ) isFormula=true;
+ }
+ if (isFormula) {
+ //if it is a formula index, use the element columns in the PK
+ pk.addColumns( getElement().getColumnIterator() );
+ }
+ else {
+ pk.addColumns( getIndex().getColumnIterator() );
+ }
+ getCollectionTable().setPrimaryKey(pk);
+ }
+ else {
+ // don't create a unique key, 'cos some
+ // databases don't like a UK on nullable
+ // columns
+ /*ArrayList list = new ArrayList();
+ list.addAll( getKey().getConstraintColumns() );
+ list.addAll( getIndex().getConstraintColumns() );
+ getCollectionTable().createUniqueKey(list);*/
+ }
+ }
+
+ public void validate(Mapping mapping) throws MappingException {
+ super.validate(mapping);
+ if ( !getIndex().isValid(mapping) ) {
+ throw new MappingException(
+ "collection index mapping has wrong number of columns: " +
+ getRole() +
+ " type: " +
+ getIndex().getType().getName()
+ );
+ }
+ if ( indexNodeName!=null && !indexNodeName.startsWith("@") ) {
+ throw new MappingException("index node must be an attribute: " + indexNodeName );
+ }
+ }
+
+ public boolean isList() {
+ return false;
+ }
+
+ public String getIndexNodeName() {
+ return indexNodeName;
+ }
+
+ public void setIndexNodeName(String indexNodeName) {
+ this.indexNodeName = indexNodeName;
+ }
+
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Join.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Join.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Join.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,176 @@
+//$Id: Join.java 10040 2006-06-22 19:51:43Z steve.ebersole at jboss.com $
+package org.hibernate.mapping;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.hibernate.sql.Alias;
+import org.hibernate.engine.ExecuteUpdateResultCheckStyle;
+
+/**
+ * @author Gavin King
+ */
+public class Join implements Serializable {
+
+ private static final Alias PK_ALIAS = new Alias(15, "PK");
+
+ private ArrayList properties = new ArrayList();
+ private Table table;
+ private KeyValue key;
+ private PersistentClass persistentClass;
+ private boolean sequentialSelect;
+ private boolean inverse;
+ private boolean optional;
+
+ // Custom SQL
+ private String customSQLInsert;
+ private boolean customInsertCallable;
+ private ExecuteUpdateResultCheckStyle insertCheckStyle;
+ private String customSQLUpdate;
+ private boolean customUpdateCallable;
+ private ExecuteUpdateResultCheckStyle updateCheckStyle;
+ private String customSQLDelete;
+ private boolean customDeleteCallable;
+ private ExecuteUpdateResultCheckStyle deleteCheckStyle;
+
+ public void addProperty(Property prop) {
+ properties.add(prop);
+ prop.setPersistentClass( getPersistentClass() );
+ }
+ public boolean containsProperty(Property prop) {
+ return properties.contains(prop);
+ }
+ public Iterator getPropertyIterator() {
+ return properties.iterator();
+ }
+
+ public Table getTable() {
+ return table;
+ }
+ public void setTable(Table table) {
+ this.table = table;
+ }
+
+ public KeyValue getKey() {
+ return key;
+ }
+ public void setKey(KeyValue key) {
+ this.key = key;
+ }
+
+ public PersistentClass getPersistentClass() {
+ return persistentClass;
+ }
+
+ public void setPersistentClass(PersistentClass persistentClass) {
+ this.persistentClass = persistentClass;
+ }
+
+ public void createForeignKey() {
+ getKey().createForeignKeyOfEntity( persistentClass.getEntityName() );
+ }
+
+ public void createPrimaryKey() {
+ //Primary key constraint
+ PrimaryKey pk = new PrimaryKey();
+ pk.setTable(table);
+ pk.setName( PK_ALIAS.toAliasString( table.getName() ) );
+ table.setPrimaryKey(pk);
+
+ pk.addColumns( getKey().getColumnIterator() );
+ }
+
+ public int getPropertySpan() {
+ return properties.size();
+ }
+
+ public void setCustomSQLInsert(String customSQLInsert, boolean callable, ExecuteUpdateResultCheckStyle checkStyle) {
+ this.customSQLInsert = customSQLInsert;
+ this.customInsertCallable = callable;
+ this.insertCheckStyle = checkStyle;
+ }
+
+ public String getCustomSQLInsert() {
+ return customSQLInsert;
+ }
+
+ public boolean isCustomInsertCallable() {
+ return customInsertCallable;
+ }
+
+ public ExecuteUpdateResultCheckStyle getCustomSQLInsertCheckStyle() {
+ return insertCheckStyle;
+ }
+
+ public void setCustomSQLUpdate(String customSQLUpdate, boolean callable, ExecuteUpdateResultCheckStyle checkStyle) {
+ this.customSQLUpdate = customSQLUpdate;
+ this.customUpdateCallable = callable;
+ this.updateCheckStyle = checkStyle;
+ }
+
+ public String getCustomSQLUpdate() {
+ return customSQLUpdate;
+ }
+
+ public boolean isCustomUpdateCallable() {
+ return customUpdateCallable;
+ }
+
+ public ExecuteUpdateResultCheckStyle getCustomSQLUpdateCheckStyle() {
+ return updateCheckStyle;
+ }
+
+ public void setCustomSQLDelete(String customSQLDelete, boolean callable, ExecuteUpdateResultCheckStyle checkStyle) {
+ this.customSQLDelete = customSQLDelete;
+ this.customDeleteCallable = callable;
+ this.deleteCheckStyle = checkStyle;
+ }
+
+ public String getCustomSQLDelete() {
+ return customSQLDelete;
+ }
+
+ public boolean isCustomDeleteCallable() {
+ return customDeleteCallable;
+ }
+
+ public ExecuteUpdateResultCheckStyle getCustomSQLDeleteCheckStyle() {
+ return deleteCheckStyle;
+ }
+
+ public boolean isSequentialSelect() {
+ return sequentialSelect;
+ }
+ public void setSequentialSelect(boolean deferred) {
+ this.sequentialSelect = deferred;
+ }
+
+ public boolean isInverse() {
+ return inverse;
+ }
+
+ public void setInverse(boolean leftJoin) {
+ this.inverse = leftJoin;
+ }
+
+ public String toString() {
+ return getClass().getName() + '(' + table.toString() + ')';
+ }
+
+ public boolean isLazy() {
+ Iterator iter = getPropertyIterator();
+ while ( iter.hasNext() ) {
+ Property prop = (Property) iter.next();
+ if ( !prop.isLazy() ) return false;
+ }
+ return true;
+ }
+
+ public boolean isOptional() {
+ return optional;
+ }
+ public void setOptional(boolean nullable) {
+ this.optional = nullable;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/JoinedSubclass.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/JoinedSubclass.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/JoinedSubclass.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,58 @@
+//$Id: JoinedSubclass.java 7586 2005-07-21 01:11:52Z oneovthafew $
+package org.hibernate.mapping;
+
+import java.util.Iterator;
+
+import org.hibernate.MappingException;
+import org.hibernate.engine.Mapping;
+
+/**
+ * A subclass in a table-per-subclass mapping
+ * @author Gavin King
+ */
+public class JoinedSubclass extends Subclass implements TableOwner {
+
+ private Table table;
+ private KeyValue key;
+
+ public JoinedSubclass(PersistentClass superclass) {
+ super(superclass);
+ }
+
+ public Table getTable() {
+ return table;
+ }
+
+ public void setTable(Table table) {
+ this.table=table;
+ getSuperclass().addSubclassTable(table);
+ }
+
+ public KeyValue getKey() {
+ return key;
+ }
+
+ public void setKey(KeyValue key) {
+ this.key = key;
+ }
+
+ public void validate(Mapping mapping) throws MappingException {
+ super.validate(mapping);
+ if ( key!=null && !key.isValid(mapping) ) {
+ throw new MappingException(
+ "subclass key mapping has wrong number of columns: " +
+ getEntityName() +
+ " type: " +
+ key.getType().getName()
+ );
+ }
+ }
+
+ public Iterator getReferenceablePropertyIterator() {
+ return getPropertyIterator();
+ }
+
+ public Object accept(PersistentClassVisitor mv) {
+ return mv.accept(this);
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/KeyValue.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/KeyValue.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/KeyValue.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,31 @@
+//$Id: KeyValue.java 6514 2005-04-26 06:37:54Z oneovthafew $
+package org.hibernate.mapping;
+
+import org.hibernate.MappingException;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.id.IdentifierGenerator;
+
+/**
+ * Represents an identifying key of a table: the value for primary key
+ * of an entity, or a foreign key of a collection or join table or
+ * joined subclass table.
+ * @author Gavin King
+ */
+public interface KeyValue extends Value {
+
+ public void createForeignKeyOfEntity(String entityName);
+
+ public boolean isCascadeDeleteEnabled();
+
+ public boolean isIdentityColumn(Dialect dialect);
+
+ public String getNullValue();
+
+ public boolean isUpdateable();
+
+ public IdentifierGenerator createIdentifierGenerator(
+ Dialect dialect,
+ String defaultCatalog,
+ String defaultSchema,
+ RootClass rootClass) throws MappingException;
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/KeyValue.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/List.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/List.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/List.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,40 @@
+//$Id: List.java 5793 2005-02-20 03:34:50Z oneovthafew $
+package org.hibernate.mapping;
+
+import org.hibernate.MappingException;
+import org.hibernate.type.CollectionType;
+import org.hibernate.type.TypeFactory;
+
+/**
+ * A list mapping has a primary key consisting of
+ * the key columns + index column.
+ * @author Gavin King
+ */
+public class List extends IndexedCollection {
+
+ private int baseIndex;
+
+ public boolean isList() {
+ return true;
+ }
+
+ public List(PersistentClass owner) {
+ super(owner);
+ }
+
+ public CollectionType getDefaultCollectionType() throws MappingException {
+ return TypeFactory.list( getRole(), getReferencedPropertyName(), isEmbedded() );
+ }
+
+ public Object accept(ValueVisitor visitor) {
+ return visitor.accept(this);
+ }
+
+ public int getBaseIndex() {
+ return baseIndex;
+ }
+
+ public void setBaseIndex(int baseIndex) {
+ this.baseIndex = baseIndex;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/ManyToOne.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/ManyToOne.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/ManyToOne.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,91 @@
+//$Id: ManyToOne.java 7586 2005-07-21 01:11:52Z oneovthafew $
+package org.hibernate.mapping;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.hibernate.MappingException;
+import org.hibernate.type.EntityType;
+import org.hibernate.type.Type;
+import org.hibernate.type.TypeFactory;
+
+/**
+ * A many-to-one association mapping
+ * @author Gavin King
+ */
+public class ManyToOne extends ToOne {
+
+ private boolean ignoreNotFound;
+
+ public ManyToOne(Table table) {
+ super(table);
+ }
+
+ public Type getType() throws MappingException {
+ return TypeFactory.manyToOne(
+ getReferencedEntityName(),
+ getReferencedPropertyName(),
+ isLazy(),
+ isUnwrapProxy(),
+ isEmbedded(),
+ isIgnoreNotFound()
+ );
+ }
+
+ public void createForeignKey() throws MappingException {
+ // the case of a foreign key to something other than the pk is handled in createPropertyRefConstraints
+ if (referencedPropertyName==null && !hasFormula() ) {
+ createForeignKeyOfEntity( ( (EntityType) getType() ).getAssociatedEntityName() );
+ }
+ }
+
+ public void createPropertyRefConstraints(Map persistentClasses) {
+ if (referencedPropertyName!=null) {
+ PersistentClass pc = (PersistentClass) persistentClasses.get(getReferencedEntityName() );
+
+ Property property = pc.getReferencedProperty( getReferencedPropertyName() );
+
+ if (property==null) {
+ throw new MappingException(
+ "Could not find property " +
+ getReferencedPropertyName() +
+ " on " +
+ getReferencedEntityName()
+ );
+ }
+ else {
+ if ( !hasFormula() && !"none".equals( getForeignKeyName() ) ) {
+ java.util.List refColumns = new ArrayList();
+ Iterator iter = property.getColumnIterator();
+ while ( iter.hasNext() ) {
+ Column col = (Column) iter.next();
+ refColumns.add( col );
+ }
+
+ ForeignKey fk = getTable().createForeignKey(
+ getForeignKeyName(),
+ getConstraintColumns(),
+ ( (EntityType) getType() ).getAssociatedEntityName(),
+ refColumns
+ );
+ fk.setCascadeDeleteEnabled(isCascadeDeleteEnabled() );
+ }
+ }
+ }
+ }
+
+ public Object accept(ValueVisitor visitor) {
+ return visitor.accept(this);
+ }
+
+ public boolean isIgnoreNotFound() {
+ return ignoreNotFound;
+ }
+
+ public void setIgnoreNotFound(boolean ignoreNotFound) {
+ this.ignoreNotFound = ignoreNotFound;
+ }
+
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Map.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Map.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Map.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,43 @@
+//$Id: Map.java 7714 2005-08-01 16:29:33Z oneovthafew $
+package org.hibernate.mapping;
+
+import org.hibernate.MappingException;
+import org.hibernate.type.CollectionType;
+import org.hibernate.type.TypeFactory;
+
+/**
+ * A map has a primary key consisting of
+ * the key columns + index columns.
+ */
+public class Map extends IndexedCollection {
+
+ public Map(PersistentClass owner) {
+ super(owner);
+ }
+
+ public boolean isMap() {
+ return true;
+ }
+
+ public CollectionType getDefaultCollectionType() {
+ if ( isSorted() ) {
+ return TypeFactory.sortedMap( getRole(), getReferencedPropertyName(), isEmbedded(), getComparator() );
+ }
+ else if ( hasOrder() ) {
+ return TypeFactory.orderedMap( getRole(), getReferencedPropertyName(), isEmbedded() );
+ }
+ else {
+ return TypeFactory.map( getRole(), getReferencedPropertyName(), isEmbedded() );
+ }
+ }
+
+
+ public void createAllKeys() throws MappingException {
+ super.createAllKeys();
+ if ( !isInverse() ) getIndex().createForeignKey();
+ }
+
+ public Object accept(ValueVisitor visitor) {
+ return visitor.accept(this);
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/MetaAttributable.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/MetaAttributable.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/MetaAttributable.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,16 @@
+package org.hibernate.mapping;
+
+/**
+ * Common interface for things that can handle meta attributes.
+ *
+ * @since 3.0.1
+ */
+public interface MetaAttributable {
+
+ public java.util.Map getMetaAttributes();
+
+ public void setMetaAttributes(java.util.Map metas);
+
+ public MetaAttribute getMetaAttribute(String name);
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/MetaAttribute.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/MetaAttribute.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/MetaAttribute.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,46 @@
+//$Id: MetaAttribute.java 10659 2006-10-30 16:19:10Z max.andersen at jboss.com $
+package org.hibernate.mapping;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * A meta attribute is a named value or values.
+ * @author Gavin King
+ */
+public class MetaAttribute implements Serializable {
+ private String name;
+ private java.util.List values = new ArrayList();
+
+ public MetaAttribute(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public java.util.List getValues() {
+ return Collections.unmodifiableList(values);
+ }
+
+ public void addValue(String value) {
+ values.add(value);
+ }
+
+ public String getValue() {
+ if ( values.size()!=1 ) {
+ throw new IllegalStateException("no unique value");
+ }
+ return (String) values.get(0);
+ }
+
+ public boolean isMultiValued() {
+ return values.size()>1;
+ }
+
+ public String toString() {
+ return "[" + name + "=" + values + "]";
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/OneToMany.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/OneToMany.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/OneToMany.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,145 @@
+//$Id: OneToMany.java 7246 2005-06-20 20:32:36Z oneovthafew $
+package org.hibernate.mapping;
+
+import java.util.Iterator;
+
+import org.hibernate.FetchMode;
+import org.hibernate.MappingException;
+import org.hibernate.engine.Mapping;
+import org.hibernate.type.EntityType;
+import org.hibernate.type.Type;
+import org.hibernate.type.TypeFactory;
+
+/**
+ * A mapping for a one-to-many association
+ * @author Gavin King
+ */
+public class OneToMany implements Value {
+
+ private String referencedEntityName;
+ private Table referencingTable;
+ private PersistentClass associatedClass;
+ private boolean embedded;
+ private boolean ignoreNotFound;
+
+ private EntityType getEntityType() {
+ return TypeFactory.manyToOne(
+ getReferencedEntityName(),
+ null,
+ false,
+ false,
+ isEmbedded(),
+ isIgnoreNotFound()
+ );
+ }
+
+ public OneToMany(PersistentClass owner) throws MappingException {
+ this.referencingTable = (owner==null) ? null : owner.getTable();
+ }
+
+ public PersistentClass getAssociatedClass() {
+ return associatedClass;
+ }
+
+ /**
+ * Associated entity on the many side
+ */
+ public void setAssociatedClass(PersistentClass associatedClass) {
+ this.associatedClass = associatedClass;
+ }
+
+ public void createForeignKey() {
+ // no foreign key element of for a one-to-many
+ }
+
+ public Iterator getColumnIterator() {
+ return associatedClass.getKey().getColumnIterator();
+ }
+
+ public int getColumnSpan() {
+ return associatedClass.getKey().getColumnSpan();
+ }
+
+ public FetchMode getFetchMode() {
+ return FetchMode.JOIN;
+ }
+
+ /**
+ * Table of the owner entity (the "one" side)
+ */
+ public Table getTable() {
+ return referencingTable;
+ }
+
+ public Type getType() {
+ return getEntityType();
+ }
+
+ public boolean isNullable() {
+ return false;
+ }
+
+ public boolean isSimpleValue() {
+ return false;
+ }
+
+ public boolean isAlternateUniqueKey() {
+ return false;
+ }
+
+ public boolean hasFormula() {
+ return false;
+ }
+
+ public boolean isValid(Mapping mapping) throws MappingException {
+ if (referencedEntityName==null) {
+ throw new MappingException("one to many association must specify the referenced entity");
+ }
+ return true;
+ }
+
+ public String getReferencedEntityName() {
+ return referencedEntityName;
+ }
+
+ /**
+ * Associated entity on the "many" side
+ */
+ public void setReferencedEntityName(String referencedEntityName) {
+ this.referencedEntityName = referencedEntityName==null ? null : referencedEntityName.intern();
+ }
+
+ public void setTypeUsingReflection(String className, String propertyName) {}
+
+ public Object accept(ValueVisitor visitor) {
+ return visitor.accept(this);
+ }
+
+
+ public boolean[] getColumnInsertability() {
+ //TODO: we could just return all false...
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean[] getColumnUpdateability() {
+ //TODO: we could just return all false...
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean isEmbedded() {
+ return embedded;
+ }
+
+ public void setEmbedded(boolean embedded) {
+ this.embedded = embedded;
+ }
+
+ public boolean isIgnoreNotFound() {
+ return ignoreNotFound;
+ }
+
+ public void setIgnoreNotFound(boolean ignoreNotFound) {
+ this.ignoreNotFound = ignoreNotFound;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/OneToOne.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/OneToOne.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/OneToOne.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,143 @@
+//$Id: OneToOne.java 7246 2005-06-20 20:32:36Z oneovthafew $
+package org.hibernate.mapping;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.hibernate.MappingException;
+import org.hibernate.type.EntityType;
+import org.hibernate.type.ForeignKeyDirection;
+import org.hibernate.type.SpecialOneToOneType;
+import org.hibernate.type.Type;
+import org.hibernate.type.TypeFactory;
+
+/**
+ * A one-to-one association mapping
+ * @author Gavin King
+ */
+public class OneToOne extends ToOne {
+
+ private boolean constrained;
+ private ForeignKeyDirection foreignKeyType;
+ private KeyValue identifier;
+ private String propertyName;
+ private String entityName;
+
+ public OneToOne(Table table, PersistentClass owner) throws MappingException {
+ super(table);
+ this.identifier = owner.getKey();
+ this.entityName = owner.getEntityName();
+ }
+
+ public String getPropertyName() {
+ return propertyName;
+ }
+
+ public void setPropertyName(String propertyName) {
+ this.propertyName = propertyName==null ? null : propertyName.intern();
+ }
+
+ public String getEntityName() {
+ return entityName;
+ }
+
+ public void setEntityName(String propertyName) {
+ this.entityName = entityName==null ? null : entityName.intern();
+ }
+
+ public Type getType() throws MappingException {
+ if ( getColumnIterator().hasNext() ) {
+ return new SpecialOneToOneType(
+ getReferencedEntityName(),
+ foreignKeyType,
+ referencedPropertyName,
+ isLazy(),
+ isUnwrapProxy(),
+ entityName,
+ propertyName
+ );
+ }
+ else {
+ return TypeFactory.oneToOne(
+ getReferencedEntityName(),
+ foreignKeyType,
+ referencedPropertyName,
+ isLazy(),
+ isUnwrapProxy(),
+ isEmbedded(),
+ entityName,
+ propertyName
+ );
+ }
+ }
+
+ public void createForeignKey() throws MappingException {
+ if ( constrained && referencedPropertyName==null) {
+ //TODO: handle the case of a foreign key to something other than the pk
+ createForeignKeyOfEntity( ( (EntityType) getType() ).getAssociatedEntityName() );
+ }
+ }
+
+ public java.util.List getConstraintColumns() {
+ ArrayList list = new ArrayList();
+ Iterator iter = identifier.getColumnIterator();
+ while ( iter.hasNext() ) list.add( iter.next() );
+ return list;
+ }
+ /**
+ * Returns the constrained.
+ * @return boolean
+ */
+ public boolean isConstrained() {
+ return constrained;
+ }
+
+ /**
+ * Returns the foreignKeyType.
+ * @return AssociationType.ForeignKeyType
+ */
+ public ForeignKeyDirection getForeignKeyType() {
+ return foreignKeyType;
+ }
+
+ /**
+ * Returns the identifier.
+ * @return Value
+ */
+ public KeyValue getIdentifier() {
+ return identifier;
+ }
+
+ /**
+ * Sets the constrained.
+ * @param constrained The constrained to set
+ */
+ public void setConstrained(boolean constrained) {
+ this.constrained = constrained;
+ }
+
+ /**
+ * Sets the foreignKeyType.
+ * @param foreignKeyType The foreignKeyType to set
+ */
+ public void setForeignKeyType(ForeignKeyDirection foreignKeyType) {
+ this.foreignKeyType = foreignKeyType;
+ }
+
+ /**
+ * Sets the identifier.
+ * @param identifier The identifier to set
+ */
+ public void setIdentifier(KeyValue identifier) {
+ this.identifier = identifier;
+ }
+
+ public boolean isNullable() {
+ return !constrained;
+ }
+
+ public Object accept(ValueVisitor visitor) {
+ return visitor.accept(this);
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/PersistentClass.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/PersistentClass.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/PersistentClass.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,791 @@
+//$Id: PersistentClass.java 10927 2006-12-05 18:48:50Z steve.ebersole at jboss.com $
+package org.hibernate.mapping;
+
+import java.io.Serializable;
+import java.util.*;
+import java.util.Set;
+
+import org.hibernate.MappingException;
+import org.hibernate.EntityMode;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.Mapping;
+import org.hibernate.engine.ExecuteUpdateResultCheckStyle;
+import org.hibernate.sql.Alias;
+import org.hibernate.util.EmptyIterator;
+import org.hibernate.util.JoinedIterator;
+import org.hibernate.util.ReflectHelper;
+import org.hibernate.util.SingletonIterator;
+import org.hibernate.util.StringHelper;
+
+/**
+ * Mapping for an entity.
+ *
+ * @author Gavin King
+ */
+public abstract class PersistentClass implements Serializable, Filterable, MetaAttributable {
+
+ private static final Alias PK_ALIAS = new Alias(15, "PK");
+
+ public static final String NULL_DISCRIMINATOR_MAPPING = "null";
+ public static final String NOT_NULL_DISCRIMINATOR_MAPPING = "not null";
+
+ private String entityName;
+
+ private String className;
+ private String proxyInterfaceName;
+
+ private String nodeName;
+
+ private String discriminatorValue;
+ private boolean lazy;
+ private ArrayList properties = new ArrayList();
+ private final ArrayList subclasses = new ArrayList();
+ private final ArrayList subclassProperties = new ArrayList();
+ private final ArrayList subclassTables = new ArrayList();
+ private boolean dynamicInsert;
+ private boolean dynamicUpdate;
+ private int batchSize=-1;
+ private boolean selectBeforeUpdate;
+ private java.util.Map metaAttributes;
+ private ArrayList joins = new ArrayList();
+ private final ArrayList subclassJoins = new ArrayList();
+ private final java.util.Map filters = new HashMap();
+ protected final java.util.Set synchronizedTables = new HashSet();
+ private String loaderName;
+ private Boolean isAbstract;
+ private boolean hasSubselectLoadableCollections;
+ private Component identifierMapper;
+
+ // Custom SQL
+ private String customSQLInsert;
+ private boolean customInsertCallable;
+ private ExecuteUpdateResultCheckStyle insertCheckStyle;
+ private String customSQLUpdate;
+ private boolean customUpdateCallable;
+ private ExecuteUpdateResultCheckStyle updateCheckStyle;
+ private String customSQLDelete;
+ private boolean customDeleteCallable;
+ private ExecuteUpdateResultCheckStyle deleteCheckStyle;
+
+ private String temporaryIdTableName;
+ private String temporaryIdTableDDL;
+
+ private java.util.Map tuplizerImpls;
+
+ protected int optimisticLockMode;
+
+ public String getClassName() {
+ return className;
+ }
+
+ public void setClassName(String className) {
+ this.className = className==null ? null : className.intern();
+ }
+
+ public String getProxyInterfaceName() {
+ return proxyInterfaceName;
+ }
+
+ public void setProxyInterfaceName(String proxyInterfaceName) {
+ this.proxyInterfaceName = proxyInterfaceName;
+ }
+
+ public Class getMappedClass() throws MappingException {
+ if (className==null) return null;
+ try {
+ return ReflectHelper.classForName(className);
+ }
+ catch (ClassNotFoundException cnfe) {
+ throw new MappingException("entity class not found: " + className, cnfe);
+ }
+ }
+
+ public Class getProxyInterface() {
+ if (proxyInterfaceName==null) return null;
+ try {
+ return ReflectHelper.classForName(proxyInterfaceName);
+ }
+ catch (ClassNotFoundException cnfe) {
+ throw new MappingException("proxy class not found: " + proxyInterfaceName, cnfe);
+ }
+ }
+ public boolean useDynamicInsert() {
+ return dynamicInsert;
+ }
+
+ abstract int nextSubclassId();
+ public abstract int getSubclassId();
+
+ public boolean useDynamicUpdate() {
+ return dynamicUpdate;
+ }
+
+ public void setDynamicInsert(boolean dynamicInsert) {
+ this.dynamicInsert = dynamicInsert;
+ }
+
+ public void setDynamicUpdate(boolean dynamicUpdate) {
+ this.dynamicUpdate = dynamicUpdate;
+ }
+
+
+ public String getDiscriminatorValue() {
+ return discriminatorValue;
+ }
+
+ public void addSubclass(Subclass subclass) throws MappingException {
+ // inheritance cycle detection (paranoid check)
+ PersistentClass superclass = getSuperclass();
+ while (superclass!=null) {
+ if( subclass.getEntityName().equals( superclass.getEntityName() ) ) {
+ throw new MappingException(
+ "Circular inheritance mapping detected: " +
+ subclass.getEntityName() +
+ " will have it self as superclass when extending " +
+ getEntityName()
+ );
+ }
+ superclass = superclass.getSuperclass();
+ }
+ subclasses.add(subclass);
+ }
+
+ public boolean hasSubclasses() {
+ return subclasses.size() > 0;
+ }
+
+ public int getSubclassSpan() {
+ int n = subclasses.size();
+ Iterator iter = subclasses.iterator();
+ while ( iter.hasNext() ) {
+ n += ( (Subclass) iter.next() ).getSubclassSpan();
+ }
+ return n;
+ }
+ /**
+ * Iterate over subclasses in a special 'order', most derived subclasses
+ * first.
+ */
+ public Iterator getSubclassIterator() {
+ Iterator[] iters = new Iterator[ subclasses.size() + 1 ];
+ Iterator iter = subclasses.iterator();
+ int i=0;
+ while ( iter.hasNext() ) {
+ iters[i++] = ( (Subclass) iter.next() ).getSubclassIterator();
+ }
+ iters[i] = subclasses.iterator();
+ return new JoinedIterator(iters);
+ }
+
+ public Iterator getSubclassClosureIterator() {
+ ArrayList iters = new ArrayList();
+ iters.add( new SingletonIterator(this) );
+ Iterator iter = getSubclassIterator();
+ while ( iter.hasNext() ) {
+ PersistentClass clazz = (PersistentClass) iter.next();
+ iters.add( clazz.getSubclassClosureIterator() );
+ }
+ return new JoinedIterator(iters);
+ }
+
+ public Table getIdentityTable() {
+ return getRootTable();
+ }
+
+ public Iterator getDirectSubclasses() {
+ return subclasses.iterator();
+ }
+
+ public void addProperty(Property p) {
+ properties.add(p);
+ p.setPersistentClass(this);
+ }
+
+ public abstract Table getTable();
+
+ public String getEntityName() {
+ return entityName;
+ }
+
+ public abstract boolean isMutable();
+ public abstract boolean hasIdentifierProperty();
+ public abstract Property getIdentifierProperty();
+ public abstract KeyValue getIdentifier();
+ public abstract Property getVersion();
+ public abstract Value getDiscriminator();
+ public abstract boolean isInherited();
+ public abstract boolean isPolymorphic();
+ public abstract boolean isVersioned();
+ public abstract String getCacheConcurrencyStrategy();
+ public abstract PersistentClass getSuperclass();
+ public abstract boolean isExplicitPolymorphism();
+ public abstract boolean isDiscriminatorInsertable();
+
+ public abstract Iterator getPropertyClosureIterator();
+ public abstract Iterator getTableClosureIterator();
+ public abstract Iterator getKeyClosureIterator();
+
+ protected void addSubclassProperty(Property prop) {
+ subclassProperties.add(prop);
+ }
+ protected void addSubclassJoin(Join join) {
+ subclassJoins.add(join);
+ }
+ protected void addSubclassTable(Table subclassTable) {
+ subclassTables.add(subclassTable);
+ }
+ public Iterator getSubclassPropertyClosureIterator() {
+ ArrayList iters = new ArrayList();
+ iters.add( getPropertyClosureIterator() );
+ iters.add( subclassProperties.iterator() );
+ for ( int i=0; i<subclassJoins.size(); i++ ) {
+ Join join = (Join) subclassJoins.get(i);
+ iters.add( join.getPropertyIterator() );
+ }
+ return new JoinedIterator(iters);
+ }
+ public Iterator getSubclassJoinClosureIterator() {
+ return new JoinedIterator( getJoinClosureIterator(), subclassJoins.iterator() );
+ }
+ public Iterator getSubclassTableClosureIterator() {
+ return new JoinedIterator( getTableClosureIterator(), subclassTables.iterator() );
+ }
+
+ public boolean isClassOrSuperclassJoin(Join join) {
+ return joins.contains(join);
+ }
+
+ public boolean isClassOrSuperclassTable(Table closureTable) {
+ return getTable()==closureTable;
+ }
+
+ public boolean isLazy() {
+ return lazy;
+ }
+
+ public void setLazy(boolean lazy) {
+ this.lazy = lazy;
+ }
+
+ public abstract boolean hasEmbeddedIdentifier();
+ public abstract Class getEntityPersisterClass();
+ public abstract void setEntityPersisterClass(Class classPersisterClass);
+ public abstract Table getRootTable();
+ public abstract RootClass getRootClass();
+ public abstract KeyValue getKey();
+
+ public void setDiscriminatorValue(String discriminatorValue) {
+ this.discriminatorValue = discriminatorValue;
+ }
+
+ public void setEntityName(String entityName) {
+ this.entityName = entityName==null ? null : entityName.intern();
+ }
+
+ public void createPrimaryKey() {
+ //Primary key constraint
+ PrimaryKey pk = new PrimaryKey();
+ Table table = getTable();
+ pk.setTable(table);
+ pk.setName( PK_ALIAS.toAliasString( table.getName() ) );
+ table.setPrimaryKey(pk);
+
+ pk.addColumns( getKey().getColumnIterator() );
+ }
+
+ public abstract String getWhere();
+
+ public int getBatchSize() {
+ return batchSize;
+ }
+
+ public void setBatchSize(int batchSize) {
+ this.batchSize = batchSize;
+ }
+
+ public boolean hasSelectBeforeUpdate() {
+ return selectBeforeUpdate;
+ }
+
+ public void setSelectBeforeUpdate(boolean selectBeforeUpdate) {
+ this.selectBeforeUpdate = selectBeforeUpdate;
+ }
+
+ /**
+ * Build an iterator of properties which are "referenceable".
+ *
+ * @see #getReferencedProperty for a discussion of "referenceable"
+ * @return The property iterator.
+ */
+ public Iterator getReferenceablePropertyIterator() {
+ return getPropertyClosureIterator();
+ }
+
+ /**
+ * Given a property path, locate the appropriate referenceable property reference.
+ * <p/>
+ * A referenceable property is a property which can be a target of a foreign-key
+ * mapping (an identifier or explcitly named in a property-ref).
+ *
+ * @param propertyPath The property path to resolve into a property reference.
+ * @return The property reference (never null).
+ * @throws MappingException If the property could not be found.
+ */
+ public Property getReferencedProperty(String propertyPath) throws MappingException {
+ try {
+ return getRecursiveProperty( propertyPath, getReferenceablePropertyIterator() );
+ }
+ catch ( MappingException e ) {
+ throw new MappingException(
+ "property-ref [" + propertyPath + "] not found on entity [" + getEntityName() + "]", e
+ );
+ }
+ }
+
+ public Property getRecursiveProperty(String propertyPath) throws MappingException {
+ try {
+ return getRecursiveProperty( propertyPath, getPropertyIterator() );
+ }
+ catch ( MappingException e ) {
+ throw new MappingException(
+ "property [" + propertyPath + "] not found on entity [" + getEntityName() + "]", e
+ );
+ }
+ }
+
+ private Property getRecursiveProperty(String propertyPath, Iterator iter) throws MappingException {
+ Property property = null;
+ StringTokenizer st = new StringTokenizer( propertyPath, ".", false );
+ try {
+ while ( st.hasMoreElements() ) {
+ final String element = ( String ) st.nextElement();
+ if ( property == null ) {
+ Property identifierProperty = getIdentifierProperty();
+ if ( identifierProperty != null && identifierProperty.getName().equals( element ) ) {
+ // we have a mapped identifier property and the root of
+ // the incoming property path matched that identifier
+ // property
+ property = identifierProperty;
+ }
+ else if ( identifierProperty == null && getIdentifierMapper() != null ) {
+ // we have an embedded composite identifier
+ try {
+ identifierProperty = getProperty( element, getIdentifierMapper().getPropertyIterator() );
+ if ( identifierProperty != null ) {
+ // the root of the incoming property path matched one
+ // of the embedded composite identifier properties
+ property = identifierProperty;
+ }
+ }
+ catch( MappingException ignore ) {
+ // ignore it...
+ }
+ }
+
+ if ( property == null ) {
+ property = getProperty( element, iter );
+ }
+ }
+ else {
+ //flat recursive algorithm
+ property = ( ( Component ) property.getValue() ).getProperty( element );
+ }
+ }
+ }
+ catch ( MappingException e ) {
+ throw new MappingException( "property [" + propertyPath + "] not found on entity [" + getEntityName() + "]" );
+ }
+
+ return property;
+ }
+
+ private Property getProperty(String propertyName, Iterator iterator) throws MappingException {
+ while ( iterator.hasNext() ) {
+ Property prop = (Property) iterator.next();
+ if ( prop.getName().equals( StringHelper.root(propertyName) ) ) {
+ return prop;
+ }
+ }
+ throw new MappingException( "property [" + propertyName + "] not found on entity [" + getEntityName() + "]" );
+ }
+
+ public Property getProperty(String propertyName) throws MappingException {
+ Iterator iter = getPropertyClosureIterator();
+ Property identifierProperty = getIdentifierProperty();
+ if ( identifierProperty != null
+ && identifierProperty.getName().equals( StringHelper.root(propertyName) )
+ ) {
+ return identifierProperty;
+ }
+ else {
+ return getProperty( propertyName, iter );
+ }
+ }
+
+ abstract public int getOptimisticLockMode();
+
+ public void setOptimisticLockMode(int optimisticLockMode) {
+ this.optimisticLockMode = optimisticLockMode;
+ }
+
+ public void validate(Mapping mapping) throws MappingException {
+ Iterator iter = getPropertyIterator();
+ while ( iter.hasNext() ) {
+ Property prop = (Property) iter.next();
+ if ( !prop.isValid(mapping) ) {
+ throw new MappingException(
+ "property mapping has wrong number of columns: " +
+ StringHelper.qualify( getEntityName(), prop.getName() ) +
+ " type: " +
+ prop.getType().getName()
+ );
+ }
+ }
+ checkPropertyDuplication();
+ checkColumnDuplication();
+ }
+
+ private void checkPropertyDuplication() throws MappingException {
+ HashSet names = new HashSet();
+ Iterator iter = getPropertyIterator();
+ while ( iter.hasNext() ) {
+ Property prop = (Property) iter.next();
+ if ( !names.add( prop.getName() ) ) {
+ throw new MappingException( "Duplicate property mapping of " + prop.getName() + " found in " + getEntityName());
+ }
+ }
+ }
+
+ public boolean isDiscriminatorValueNotNull() {
+ return NOT_NULL_DISCRIMINATOR_MAPPING.equals( getDiscriminatorValue() );
+ }
+ public boolean isDiscriminatorValueNull() {
+ return NULL_DISCRIMINATOR_MAPPING.equals( getDiscriminatorValue() );
+ }
+
+ public java.util.Map getMetaAttributes() {
+ return metaAttributes;
+ }
+
+ public void setMetaAttributes(java.util.Map metas) {
+ this.metaAttributes = metas;
+ }
+
+ public MetaAttribute getMetaAttribute(String name) {
+ return metaAttributes==null?null:(MetaAttribute) metaAttributes.get(name);
+ }
+
+ public String toString() {
+ return getClass().getName() + '(' + getEntityName() + ')';
+ }
+
+ public Iterator getJoinIterator() {
+ return joins.iterator();
+ }
+
+ public Iterator getJoinClosureIterator() {
+ return joins.iterator();
+ }
+
+ public void addJoin(Join join) {
+ joins.add(join);
+ join.setPersistentClass(this);
+ }
+
+ public int getJoinClosureSpan() {
+ return joins.size();
+ }
+
+ public int getPropertyClosureSpan() {
+ int span = properties.size();
+ for ( int i=0; i<joins.size(); i++ ) {
+ Join join = (Join) joins.get(i);
+ span += join.getPropertySpan();
+ }
+ return span;
+ }
+
+ public int getJoinNumber(Property prop) {
+ int result=1;
+ Iterator iter = getSubclassJoinClosureIterator();
+ while ( iter.hasNext() ) {
+ Join join = (Join) iter.next();
+ if ( join.containsProperty(prop) ) return result;
+ result++;
+ }
+ return 0;
+ }
+
+ /**
+ * Build an iterator over the properties defined on this class. The returned
+ * iterator only accounts for "normal" properties (i.e. non-identifier
+ * properties).
+ * <p/>
+ * Differs from {@link #getUnjoinedPropertyIterator} in that the iterator
+ * we return here will include properties defined as part of a join.
+ *
+ * @return An iterator over the "normal" properties.
+ */
+ public Iterator getPropertyIterator() {
+ ArrayList iterators = new ArrayList();
+ iterators.add( properties.iterator() );
+ for ( int i = 0; i < joins.size(); i++ ) {
+ Join join = ( Join ) joins.get( i );
+ iterators.add( join.getPropertyIterator() );
+ }
+ return new JoinedIterator( iterators );
+ }
+
+ /**
+ * Build an iterator over the properties defined on this class <b>which
+ * are not defined as part of a join</b>. As with {@link #getPropertyIterator},
+ * the returned iterator only accounts for non-identifier properties.
+ *
+ * @return An iterator over the non-joined "normal" properties.
+ */
+ public Iterator getUnjoinedPropertyIterator() {
+ return properties.iterator();
+ }
+
+ public void setCustomSQLInsert(String customSQLInsert, boolean callable, ExecuteUpdateResultCheckStyle checkStyle) {
+ this.customSQLInsert = customSQLInsert;
+ this.customInsertCallable = callable;
+ this.insertCheckStyle = checkStyle;
+ }
+
+ public String getCustomSQLInsert() {
+ return customSQLInsert;
+ }
+
+ public boolean isCustomInsertCallable() {
+ return customInsertCallable;
+ }
+
+ public ExecuteUpdateResultCheckStyle getCustomSQLInsertCheckStyle() {
+ return insertCheckStyle;
+ }
+
+ public void setCustomSQLUpdate(String customSQLUpdate, boolean callable, ExecuteUpdateResultCheckStyle checkStyle) {
+ this.customSQLUpdate = customSQLUpdate;
+ this.customUpdateCallable = callable;
+ this.updateCheckStyle = checkStyle;
+ }
+
+ public String getCustomSQLUpdate() {
+ return customSQLUpdate;
+ }
+
+ public boolean isCustomUpdateCallable() {
+ return customUpdateCallable;
+ }
+
+ public ExecuteUpdateResultCheckStyle getCustomSQLUpdateCheckStyle() {
+ return updateCheckStyle;
+ }
+
+ public void setCustomSQLDelete(String customSQLDelete, boolean callable, ExecuteUpdateResultCheckStyle checkStyle) {
+ this.customSQLDelete = customSQLDelete;
+ this.customDeleteCallable = callable;
+ this.deleteCheckStyle = checkStyle;
+ }
+
+ public String getCustomSQLDelete() {
+ return customSQLDelete;
+ }
+
+ public boolean isCustomDeleteCallable() {
+ return customDeleteCallable;
+ }
+
+ public ExecuteUpdateResultCheckStyle getCustomSQLDeleteCheckStyle() {
+ return deleteCheckStyle;
+ }
+
+ public void addFilter(String name, String condition) {
+ filters.put(name, condition);
+ }
+
+ public java.util.Map getFilterMap() {
+ return filters;
+ }
+
+ public boolean isForceDiscriminator() {
+ return false;
+ }
+
+ public abstract boolean isJoinedSubclass();
+
+ public String getLoaderName() {
+ return loaderName;
+ }
+
+ public void setLoaderName(String loaderName) {
+ this.loaderName = loaderName==null ? null : loaderName.intern();
+ }
+
+ public abstract java.util.Set getSynchronizedTables();
+
+ public void addSynchronizedTable(String table) {
+ synchronizedTables.add(table);
+ }
+
+ public Boolean isAbstract() {
+ return isAbstract;
+ }
+
+ public void setAbstract(Boolean isAbstract) {
+ this.isAbstract = isAbstract;
+ }
+
+ protected void checkColumnDuplication(Set distinctColumns, Iterator columns)
+ throws MappingException {
+ while ( columns.hasNext() ) {
+ Selectable columnOrFormula = (Selectable) columns.next();
+ if ( !columnOrFormula.isFormula() ) {
+ Column col = (Column) columnOrFormula;
+ if ( !distinctColumns.add( col.getName() ) ) {
+ throw new MappingException(
+ "Repeated column in mapping for entity: " +
+ getEntityName() +
+ " column: " +
+ col.getName() +
+ " (should be mapped with insert=\"false\" update=\"false\")"
+ );
+ }
+ }
+ }
+ }
+
+ protected void checkPropertyColumnDuplication(Set distinctColumns, Iterator properties)
+ throws MappingException {
+ while ( properties.hasNext() ) {
+ Property prop = (Property) properties.next();
+ if ( prop.getValue() instanceof Component ) { //TODO: remove use of instanceof!
+ Component component = (Component) prop.getValue();
+ checkPropertyColumnDuplication( distinctColumns, component.getPropertyIterator() );
+ }
+ else {
+ if ( prop.isUpdateable() || prop.isInsertable() ) {
+ checkColumnDuplication( distinctColumns, prop.getColumnIterator() );
+ }
+ }
+ }
+ }
+
+ protected Iterator getNonDuplicatedPropertyIterator() {
+ return getUnjoinedPropertyIterator();
+ }
+
+ protected Iterator getDiscriminatorColumnIterator() {
+ return EmptyIterator.INSTANCE;
+ }
+
+ protected void checkColumnDuplication() {
+ HashSet cols = new HashSet();
+ if (getIdentifierMapper() == null ) {
+ //an identifier mapper => getKey will be included in the getNonDuplicatedPropertyIterator()
+ //and checked later, so it needs to be excluded
+ checkColumnDuplication( cols, getKey().getColumnIterator() );
+ }
+ checkColumnDuplication( cols, getDiscriminatorColumnIterator() );
+ checkPropertyColumnDuplication( cols, getNonDuplicatedPropertyIterator() );
+ Iterator iter = getJoinIterator();
+ while ( iter.hasNext() ) {
+ cols.clear();
+ Join join = (Join) iter.next();
+ checkColumnDuplication( cols, join.getKey().getColumnIterator() );
+ checkPropertyColumnDuplication( cols, join.getPropertyIterator() );
+ }
+ }
+
+ public abstract Object accept(PersistentClassVisitor mv);
+
+ public String getNodeName() {
+ return nodeName;
+ }
+
+ public void setNodeName(String nodeName) {
+ this.nodeName = nodeName;
+ }
+
+ public boolean hasPojoRepresentation() {
+ return getClassName()!=null;
+ }
+
+ public boolean hasDom4jRepresentation() {
+ return getNodeName()!=null;
+ }
+
+ public boolean hasSubselectLoadableCollections() {
+ return hasSubselectLoadableCollections;
+ }
+
+ public void setSubselectLoadableCollections(boolean hasSubselectCollections) {
+ this.hasSubselectLoadableCollections = hasSubselectCollections;
+ }
+
+ public void prepareTemporaryTables(Mapping mapping, Dialect dialect) {
+ if ( dialect.supportsTemporaryTables() ) {
+ temporaryIdTableName = dialect.generateTemporaryTableName( getTable().getName() );
+ Table table = new Table();
+ table.setName( temporaryIdTableName );
+ Iterator itr = getTable().getPrimaryKey().getColumnIterator();
+ while( itr.hasNext() ) {
+ Column column = (Column) itr.next();
+ table.addColumn( (Column) column.clone() );
+ }
+ temporaryIdTableDDL = table.sqlTemporaryTableCreateString( dialect, mapping );
+ }
+ }
+
+ public String getTemporaryIdTableName() {
+ return temporaryIdTableName;
+ }
+
+ public String getTemporaryIdTableDDL() {
+ return temporaryIdTableDDL;
+ }
+
+ public Component getIdentifierMapper() {
+ return identifierMapper;
+ }
+
+ public boolean hasIdentifierMapper() {
+ return identifierMapper != null;
+ }
+
+ public void setIdentifierMapper(Component handle) {
+ this.identifierMapper = handle;
+ }
+
+ public void addTuplizer(EntityMode entityMode, String implClassName) {
+ if ( tuplizerImpls == null ) {
+ tuplizerImpls = new HashMap();
+ }
+ tuplizerImpls.put( entityMode, implClassName );
+ }
+
+ public String getTuplizerImplClassName(EntityMode mode) {
+ if ( tuplizerImpls == null ) return null;
+ return ( String ) tuplizerImpls.get( mode );
+ }
+
+ public java.util.Map getTuplizerMap() {
+ if ( tuplizerImpls == null ) {
+ return null;
+ }
+ return java.util.Collections.unmodifiableMap( tuplizerImpls );
+ }
+
+ public boolean hasNaturalId() {
+ Iterator props = getRootClass().getPropertyIterator();
+ while ( props.hasNext() ) {
+ if ( ( (Property) props.next() ).isNaturalIdentifier() ) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public abstract boolean isLazyPropertiesCacheable();
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/PersistentClassVisitor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/PersistentClassVisitor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/PersistentClassVisitor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,44 @@
+/*
+ * Created on 07-Dec-2004
+ *
+ */
+package org.hibernate.mapping;
+
+/**
+ * @author max
+ *
+ */
+public interface PersistentClassVisitor {
+
+ /**
+ * @param class1
+ * @return
+ */
+ Object accept(RootClass class1);
+
+ /**
+ * @param subclass
+ * @return
+ */
+ Object accept(UnionSubclass subclass);
+
+ /**
+ * @param subclass
+ * @return
+ */
+ Object accept(SingleTableSubclass subclass);
+
+ /**
+ * @param subclass
+ * @return
+ */
+ Object accept(JoinedSubclass subclass);
+
+ /**
+ * @param subclass
+ * @return
+ */
+ Object accept(Subclass subclass);
+
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/PrimaryKey.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/PrimaryKey.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/PrimaryKey.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,35 @@
+//$Id: PrimaryKey.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate.mapping;
+
+import java.util.Iterator;
+
+import org.hibernate.dialect.Dialect;
+
+/**
+ * A primary key constraint
+ * @author Gavin King
+ */
+public class PrimaryKey extends Constraint {
+
+ public String sqlConstraintString(Dialect dialect) {
+ StringBuffer buf = new StringBuffer("primary key (");
+ Iterator iter = getColumnIterator();
+ while ( iter.hasNext() ) {
+ buf.append( ( (Column) iter.next() ).getQuotedName(dialect) );
+ if ( iter.hasNext() ) buf.append(", ");
+ }
+ return buf.append(')').toString();
+ }
+
+ public String sqlConstraintString(Dialect dialect, String constraintName, String defaultCatalog, String defaultSchema) {
+ StringBuffer buf = new StringBuffer(
+ dialect.getAddPrimaryKeyConstraintString(constraintName)
+ ).append('(');
+ Iterator iter = getColumnIterator();
+ while ( iter.hasNext() ) {
+ buf.append( ( (Column) iter.next() ).getQuotedName(dialect) );
+ if ( iter.hasNext() ) buf.append(", ");
+ }
+ return buf.append(')').toString();
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/PrimitiveArray.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/PrimitiveArray.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/PrimitiveArray.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,28 @@
+//$Id: PrimitiveArray.java 4905 2004-12-07 09:59:56Z maxcsaucdk $
+package org.hibernate.mapping;
+
+/**
+ * A primitive array has a primary key consisting
+ * of the key columns + index column.
+ */
+public class PrimitiveArray extends Array {
+
+ public PrimitiveArray(PersistentClass owner) {
+ super(owner);
+ }
+
+ public boolean isPrimitiveArray() {
+ return true;
+ }
+
+ public Object accept(ValueVisitor visitor) {
+ return visitor.accept(this);
+ }
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Property.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Property.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Property.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,292 @@
+//$Id: Property.java 10245 2006-08-11 18:38:39Z steve.ebersole at jboss.com $
+package org.hibernate.mapping;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.StringTokenizer;
+
+import org.hibernate.MappingException;
+import org.hibernate.PropertyNotFoundException;
+import org.hibernate.EntityMode;
+import org.hibernate.engine.CascadeStyle;
+import org.hibernate.engine.Mapping;
+import org.hibernate.property.Getter;
+import org.hibernate.property.PropertyAccessor;
+import org.hibernate.property.PropertyAccessorFactory;
+import org.hibernate.property.Setter;
+import org.hibernate.type.AbstractComponentType;
+import org.hibernate.type.Type;
+import org.hibernate.util.ArrayHelper;
+
+/**
+ * Represents a property as part of an entity or a component.
+ *
+ * @author Gavin King
+ */
+public class Property implements Serializable, MetaAttributable {
+
+ private String name;
+ private Value value;
+ private String cascade;
+ private boolean updateable = true;
+ private boolean insertable = true;
+ private boolean selectable = true;
+ private boolean optimisticLocked = true;
+ private PropertyGeneration generation = PropertyGeneration.NEVER;
+ private String propertyAccessorName;
+ private boolean lazy;
+ private boolean optional;
+ private String nodeName;
+ private java.util.Map metaAttributes;
+ private PersistentClass persistentClass;
+ private boolean naturalIdentifier;
+
+ public boolean isBackRef() {
+ return false;
+ }
+
+ public Type getType() throws MappingException {
+ return value.getType();
+ }
+
+ public int getColumnSpan() {
+ return value.getColumnSpan();
+ }
+
+ public Iterator getColumnIterator() {
+ return value.getColumnIterator();
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public boolean isComposite() {
+ return value instanceof Component;
+ }
+
+ public Value getValue() {
+ return value;
+ }
+
+ public boolean isPrimitive(Class clazz) {
+ return getGetter(clazz).getReturnType().isPrimitive();
+ }
+
+ public CascadeStyle getCascadeStyle() throws MappingException {
+ Type type = value.getType();
+ if ( type.isComponentType() && !type.isAnyType() ) {
+ AbstractComponentType actype = (AbstractComponentType) type;
+ int length = actype.getSubtypes().length;
+ for ( int i=0; i<length; i++ ) {
+ if ( actype.getCascadeStyle(i)!=CascadeStyle.NONE ) return CascadeStyle.ALL;
+ }
+ return CascadeStyle.NONE;
+ }
+ else if ( cascade==null || cascade.equals("none") ) {
+ return CascadeStyle.NONE;
+ }
+ else {
+ StringTokenizer tokens = new StringTokenizer(cascade, ", ");
+ CascadeStyle[] styles = new CascadeStyle[ tokens.countTokens() ] ;
+ int i=0;
+ while ( tokens.hasMoreTokens() ) {
+ styles[i++] = CascadeStyle.getCascadeStyle( tokens.nextToken() );
+ }
+ return new CascadeStyle.MultipleCascadeStyle(styles);
+ }
+ }
+
+ public String getCascade() {
+ return cascade;
+ }
+
+ public void setCascade(String cascade) {
+ this.cascade = cascade;
+ }
+
+ public void setName(String name) {
+ this.name = name==null ? null : name.intern();
+ }
+
+ public void setValue(Value value) {
+ this.value = value;
+ }
+
+ public boolean isUpdateable() {
+ // if the property mapping consists of all formulas,
+ // make it non-updateable
+ final boolean[] columnUpdateability = value.getColumnUpdateability();
+ return updateable && (
+ //columnUpdateability.length==0 ||
+ !ArrayHelper.isAllFalse(columnUpdateability)
+ );
+ }
+
+ public boolean isInsertable() {
+ // if the property mapping consists of all formulas,
+ // make it insertable
+ final boolean[] columnInsertability = value.getColumnInsertability();
+ return insertable && (
+ columnInsertability.length==0 ||
+ !ArrayHelper.isAllFalse(columnInsertability)
+ );
+ }
+
+ public PropertyGeneration getGeneration() {
+ return generation;
+ }
+
+ public void setGeneration(PropertyGeneration generation) {
+ this.generation = generation;
+ }
+
+ public void setUpdateable(boolean mutable) {
+ this.updateable = mutable;
+ }
+
+ public void setInsertable(boolean insertable) {
+ this.insertable = insertable;
+ }
+
+ public String getPropertyAccessorName() {
+ return propertyAccessorName;
+ }
+
+ public void setPropertyAccessorName(String string) {
+ propertyAccessorName = string;
+ }
+
+ /**
+ * Approximate!
+ */
+ boolean isNullable() {
+ return value==null || value.isNullable();
+ }
+
+ public boolean isBasicPropertyAccessor() {
+ return propertyAccessorName==null || "property".equals(propertyAccessorName);
+ }
+
+ public java.util.Map getMetaAttributes() {
+ return metaAttributes;
+ }
+
+ public MetaAttribute getMetaAttribute(String attributeName) {
+ return metaAttributes==null?null:(MetaAttribute) metaAttributes.get(attributeName);
+ }
+
+ public void setMetaAttributes(java.util.Map metas) {
+ this.metaAttributes = metas;
+ }
+
+ public boolean isValid(Mapping mapping) throws MappingException {
+ return getValue().isValid(mapping);
+ }
+
+ public String toString() {
+ return getClass().getName() + '(' + name + ')';
+ }
+
+ public void setLazy(boolean lazy) {
+ this.lazy=lazy;
+ }
+
+ public boolean isLazy() {
+ if ( value instanceof ToOne ) {
+ // both many-to-one and one-to-one are represented as a
+ // Property. EntityPersister is relying on this value to
+ // determine "lazy fetch groups" in terms of field-level
+ // interception. So we need to make sure that we return
+ // true here for the case of many-to-one and one-to-one
+ // with lazy="no-proxy"
+ //
+ // * impl note - lazy="no-proxy" currently forces both
+ // lazy and unwrap to be set to true. The other case we
+ // are extremely interested in here is that of lazy="proxy"
+ // where lazy is set to true, but unwrap is set to false.
+ // thus we use both here under the assumption that this
+ // return is really only ever used during persister
+ // construction to determine the lazy property/field fetch
+ // groupings. If that assertion changes then this check
+ // needs to change as well. Partially, this is an issue with
+ // the overloading of the term "lazy" here...
+ ToOne toOneValue = ( ToOne ) value;
+ return toOneValue.isLazy() && toOneValue.isUnwrapProxy();
+ }
+ return lazy;
+ }
+
+ public boolean isOptimisticLocked() {
+ return optimisticLocked;
+ }
+
+ public void setOptimisticLocked(boolean optimisticLocked) {
+ this.optimisticLocked = optimisticLocked;
+ }
+
+ public boolean isOptional() {
+ return optional || isNullable();
+ }
+
+ public void setOptional(boolean optional) {
+ this.optional = optional;
+ }
+
+ public PersistentClass getPersistentClass() {
+ return persistentClass;
+ }
+
+ public void setPersistentClass(PersistentClass persistentClass) {
+ this.persistentClass = persistentClass;
+ }
+
+ public boolean isSelectable() {
+ return selectable;
+ }
+
+ public void setSelectable(boolean selectable) {
+ this.selectable = selectable;
+ }
+
+ public String getNodeName() {
+ return nodeName;
+ }
+
+ public void setNodeName(String nodeName) {
+ this.nodeName = nodeName;
+ }
+
+ public String getAccessorPropertyName( EntityMode mode ) {
+ if ( mode == EntityMode.DOM4J ) {
+ return nodeName;
+ }
+ else {
+ return getName();
+ }
+ }
+
+ // todo : remove
+ public Getter getGetter(Class clazz) throws PropertyNotFoundException, MappingException {
+ return getPropertyAccessor(clazz).getGetter(clazz, name);
+ }
+
+ // todo : remove
+ public Setter getSetter(Class clazz) throws PropertyNotFoundException, MappingException {
+ return getPropertyAccessor(clazz).getSetter(clazz, name);
+ }
+
+ // todo : remove
+ public PropertyAccessor getPropertyAccessor(Class clazz) throws MappingException {
+ return PropertyAccessorFactory.getPropertyAccessor( clazz, getPropertyAccessorName() );
+ }
+
+ public boolean isNaturalIdentifier() {
+ return naturalIdentifier;
+ }
+
+ public void setNaturalIdentifier(boolean naturalIdentifier) {
+ this.naturalIdentifier = naturalIdentifier;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/PropertyGeneration.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/PropertyGeneration.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/PropertyGeneration.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,55 @@
+package org.hibernate.mapping;
+
+import java.io.Serializable;
+
+/**
+ * Indicates whether given properties are generated by the database and, if
+ * so, at what time(s) they are generated.
+ *
+ * @author Steve Ebersole
+ */
+public class PropertyGeneration implements Serializable {
+
+ /**
+ * Values for this property are never generated by the database.
+ */
+ public static final PropertyGeneration NEVER = new PropertyGeneration( "never" );
+ /**
+ * Values for this property are generated by the database on insert.
+ */
+ public static final PropertyGeneration INSERT = new PropertyGeneration( "insert" );
+ /**
+ * Values for this property are generated by the database on both insert and update.
+ */
+ public static final PropertyGeneration ALWAYS = new PropertyGeneration( "always" );
+
+ private final String name;
+
+ private PropertyGeneration(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public static PropertyGeneration parse(String name) {
+ if ( "insert".equalsIgnoreCase( name ) ) {
+ return INSERT;
+ }
+ else if ( "always".equalsIgnoreCase( name ) ) {
+ return ALWAYS;
+ }
+ else {
+ return NEVER;
+ }
+ }
+
+ private Object readResolve() {
+ return parse( name );
+ }
+
+ public String toString() {
+ return getClass().getName() + "(" + getName() + ")";
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/RelationalModel.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/RelationalModel.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/RelationalModel.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,15 @@
+//$Id: RelationalModel.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate.mapping;
+
+import org.hibernate.engine.Mapping;
+import org.hibernate.HibernateException;
+import org.hibernate.dialect.Dialect;
+
+/**
+ * A relational object which may be created using DDL
+ * @author Gavin King
+ */
+public interface RelationalModel {
+ public String sqlCreateString(Dialect dialect, Mapping p, String defaultCatalog, String defaultSchema) throws HibernateException;
+ public String sqlDropString(Dialect dialect, String defaultCatalog, String defaultSchema);
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/RootClass.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/RootClass.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/RootClass.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,276 @@
+//$Id: RootClass.java 8698 2005-11-29 14:34:11Z steveebersole $
+package org.hibernate.mapping;
+
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.MappingException;
+import org.hibernate.engine.Mapping;
+import org.hibernate.util.ReflectHelper;
+import org.hibernate.util.SingletonIterator;
+
+/**
+ * The root class of an inheritance hierarchy
+ * @author Gavin King
+ */
+public class RootClass extends PersistentClass implements TableOwner {
+
+ public static final String DEFAULT_IDENTIFIER_COLUMN_NAME = "id";
+ public static final String DEFAULT_DISCRIMINATOR_COLUMN_NAME = "class";
+
+ private Property identifierProperty; //may be final
+ private KeyValue identifier; //may be final
+ private Property version; //may be final
+ private boolean polymorphic;
+ private String cacheConcurrencyStrategy;
+ private String cacheRegionName;
+ private boolean lazyPropertiesCacheable = true;
+ private Value discriminator; //may be final
+ private boolean mutable = true;
+ private boolean embeddedIdentifier = false; // may be final
+ private boolean explicitPolymorphism;
+ private Class entityPersisterClass;
+ private boolean forceDiscriminator = false;
+ private String where;
+ private Table table;
+ private boolean discriminatorInsertable = true;
+ private int nextSubclassId = 0;
+
+ int nextSubclassId() {
+ return ++nextSubclassId;
+ }
+
+ public int getSubclassId() {
+ return 0;
+ }
+
+ public void setTable(Table table) {
+ this.table=table;
+ }
+ public Table getTable() {
+ return table;
+ }
+
+ public Property getIdentifierProperty() {
+ return identifierProperty;
+ }
+ public KeyValue getIdentifier() {
+ return identifier;
+ }
+ public boolean hasIdentifierProperty() {
+ return identifierProperty!=null;
+ }
+
+ public Value getDiscriminator() {
+ return discriminator;
+ }
+
+ public boolean isInherited() {
+ return false;
+ }
+ public boolean isPolymorphic() {
+ return polymorphic;
+ }
+
+ public void setPolymorphic(boolean polymorphic) {
+ this.polymorphic = polymorphic;
+ }
+
+ public RootClass getRootClass() {
+ return this;
+ }
+
+ public Iterator getPropertyClosureIterator() {
+ return getPropertyIterator();
+ }
+ public Iterator getTableClosureIterator() {
+ return new SingletonIterator( getTable() );
+ }
+ public Iterator getKeyClosureIterator() {
+ return new SingletonIterator( getKey() );
+ }
+
+ public void addSubclass(Subclass subclass) throws MappingException {
+ super.addSubclass(subclass);
+ setPolymorphic(true);
+ }
+
+ public boolean isExplicitPolymorphism() {
+ return explicitPolymorphism;
+ }
+
+ public Property getVersion() {
+ return version;
+ }
+ public void setVersion(Property version) {
+ this.version = version;
+ }
+ public boolean isVersioned() {
+ return version!=null;
+ }
+
+ public boolean isMutable() {
+ return mutable;
+ }
+ public boolean hasEmbeddedIdentifier() {
+ return embeddedIdentifier;
+ }
+
+ public Class getEntityPersisterClass() {
+ return entityPersisterClass;
+ }
+
+ public Table getRootTable() {
+ return getTable();
+ }
+
+ public void setEntityPersisterClass(Class persister) {
+ this.entityPersisterClass = persister;
+ }
+
+ public PersistentClass getSuperclass() {
+ return null;
+ }
+
+ public KeyValue getKey() {
+ return getIdentifier();
+ }
+
+ public void setDiscriminator(Value discriminator) {
+ this.discriminator = discriminator;
+ }
+
+ public void setEmbeddedIdentifier(boolean embeddedIdentifier) {
+ this.embeddedIdentifier = embeddedIdentifier;
+ }
+
+ public void setExplicitPolymorphism(boolean explicitPolymorphism) {
+ this.explicitPolymorphism = explicitPolymorphism;
+ }
+
+ public void setIdentifier(KeyValue identifier) {
+ this.identifier = identifier;
+ }
+
+ public void setIdentifierProperty(Property identifierProperty) {
+ this.identifierProperty = identifierProperty;
+ identifierProperty.setPersistentClass(this);
+ }
+
+ public void setMutable(boolean mutable) {
+ this.mutable = mutable;
+ }
+
+ public boolean isDiscriminatorInsertable() {
+ return discriminatorInsertable;
+ }
+
+ public void setDiscriminatorInsertable(boolean insertable) {
+ this.discriminatorInsertable = insertable;
+ }
+
+ public boolean isForceDiscriminator() {
+ return forceDiscriminator;
+ }
+
+ public void setForceDiscriminator(boolean forceDiscriminator) {
+ this.forceDiscriminator = forceDiscriminator;
+ }
+
+ public String getWhere() {
+ return where;
+ }
+
+ public void setWhere(String string) {
+ where = string;
+ }
+
+ public void validate(Mapping mapping) throws MappingException {
+ super.validate(mapping);
+ if ( !getIdentifier().isValid(mapping) ) {
+ throw new MappingException(
+ "identifier mapping has wrong number of columns: " +
+ getEntityName() +
+ " type: " +
+ getIdentifier().getType().getName()
+ );
+ }
+ checkCompositeIdentifier();
+ }
+
+ private void checkCompositeIdentifier() {
+ if ( getIdentifier() instanceof Component ) {
+ Component id = (Component) getIdentifier();
+ if ( !id.isDynamic() ) {
+ Class idClass = id.getComponentClass();
+ if ( idClass != null && !ReflectHelper.overridesEquals( idClass ) ) {
+ LogFactory.getLog(RootClass.class)
+ .warn( "composite-id class does not override equals(): "
+ + id.getComponentClass().getName() );
+ }
+ if ( !ReflectHelper.overridesHashCode( idClass ) ) {
+ LogFactory.getLog(RootClass.class)
+ .warn( "composite-id class does not override hashCode(): "
+ + id.getComponentClass().getName() );
+ }
+ if ( !Serializable.class.isAssignableFrom( idClass ) ) {
+ throw new MappingException( "composite-id class must implement Serializable: "
+ + id.getComponentClass().getName() );
+ }
+ }
+ }
+ }
+
+ public String getCacheConcurrencyStrategy() {
+ return cacheConcurrencyStrategy;
+ }
+
+ public void setCacheConcurrencyStrategy(String cacheConcurrencyStrategy) {
+ this.cacheConcurrencyStrategy = cacheConcurrencyStrategy;
+ }
+
+ public String getCacheRegionName() {
+ return cacheRegionName==null ? getEntityName() : cacheRegionName;
+ }
+ public void setCacheRegionName(String cacheRegionName) {
+ this.cacheRegionName = cacheRegionName;
+ }
+
+ public boolean isLazyPropertiesCacheable() {
+ return lazyPropertiesCacheable;
+ }
+
+ public void setLazyPropertiesCacheable(boolean lazyPropertiesCacheable) {
+ this.lazyPropertiesCacheable = lazyPropertiesCacheable;
+ }
+
+ public boolean isJoinedSubclass() {
+ return false;
+ }
+
+ public java.util.Set getSynchronizedTables() {
+ return synchronizedTables;
+ }
+
+ public Set getIdentityTables() {
+ Set tables = new HashSet();
+ Iterator iter = getSubclassClosureIterator();
+ while ( iter.hasNext() ) {
+ PersistentClass clazz = (PersistentClass) iter.next();
+ if ( clazz.isAbstract() == null || !clazz.isAbstract().booleanValue() ) tables.add( clazz.getIdentityTable() );
+ }
+ return tables;
+ }
+
+ public Object accept(PersistentClassVisitor mv) {
+ return mv.accept(this);
+ }
+
+ public int getOptimisticLockMode() {
+ return optimisticLockMode;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Selectable.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Selectable.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Selectable.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,14 @@
+//$Id: Selectable.java 9908 2006-05-08 20:59:20Z max.andersen at jboss.com $
+package org.hibernate.mapping;
+
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.function.SQLFunctionRegistry;
+
+public interface Selectable {
+ public String getAlias(Dialect dialect);
+ public String getAlias(Dialect dialect, Table table);
+ public boolean isFormula();
+ public String getTemplate(Dialect dialect, SQLFunctionRegistry functionRegistry);
+ public String getText(Dialect dialect);
+ public String getText();
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Selectable.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Set.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Set.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Set.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,86 @@
+//$Id: Set.java 7714 2005-08-01 16:29:33Z oneovthafew $
+package org.hibernate.mapping;
+
+import java.util.Iterator;
+
+import org.hibernate.MappingException;
+import org.hibernate.engine.Mapping;
+import org.hibernate.type.CollectionType;
+import org.hibernate.type.TypeFactory;
+
+/**
+ * A set with no nullable element columns. It will have a primary key
+ * consisting of all table columns (ie. key columns + element columns).
+ * @author Gavin King
+ */
+public class Set extends Collection {
+
+ public void validate(Mapping mapping) throws MappingException {
+ super.validate( mapping );
+ //for backward compatibility, disable this:
+ /*Iterator iter = getElement().getColumnIterator();
+ while ( iter.hasNext() ) {
+ Column col = (Column) iter.next();
+ if ( !col.isNullable() ) {
+ return;
+ }
+ }
+ throw new MappingException("set element mappings must have at least one non-nullable column: " + getRole() );*/
+ }
+
+ /**
+ * Constructor for Set.
+ * @param owner
+ */
+ public Set(PersistentClass owner) {
+ super(owner);
+ }
+
+ public boolean isSet() {
+ return true;
+ }
+
+ public CollectionType getDefaultCollectionType() {
+ if ( isSorted() ) {
+ return TypeFactory.sortedSet( getRole(), getReferencedPropertyName(), isEmbedded(), getComparator() );
+ }
+ else if ( hasOrder() ) {
+ return TypeFactory.orderedSet( getRole(), getReferencedPropertyName(), isEmbedded() );
+ }
+ else {
+ return TypeFactory.set( getRole(), getReferencedPropertyName(), isEmbedded() );
+ }
+ }
+
+ void createPrimaryKey() {
+ if ( !isOneToMany() ) {
+ PrimaryKey pk = new PrimaryKey();
+ pk.addColumns( getKey().getColumnIterator() );
+ Iterator iter = getElement().getColumnIterator();
+ while ( iter.hasNext() ) {
+ Object selectable = iter.next();
+ if ( selectable instanceof Column ) {
+ Column col = (Column) selectable;
+ if ( !col.isNullable() ) {
+ pk.addColumn(col);
+ }
+ }
+ }
+ if ( pk.getColumnSpan()==getKey().getColumnSpan() ) {
+ //for backward compatibility, allow a set with no not-null
+ //element columns, using all columns in the row locater SQL
+ //TODO: create an implicit not null constraint on all cols?
+ }
+ else {
+ getCollectionTable().setPrimaryKey(pk);
+ }
+ }
+ else {
+ //create an index on the key columns??
+ }
+ }
+
+ public Object accept(ValueVisitor visitor) {
+ return visitor.accept(this);
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/SimpleAuxiliaryDatabaseObject.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/SimpleAuxiliaryDatabaseObject.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/SimpleAuxiliaryDatabaseObject.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,54 @@
+// $Id: SimpleAuxiliaryDatabaseObject.java 7800 2005-08-10 12:13:00Z steveebersole $
+package org.hibernate.mapping;
+
+import java.util.HashSet;
+
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.Mapping;
+import org.hibernate.HibernateException;
+import org.hibernate.util.StringHelper;
+
+/**
+ * A simple implementation of AbstractAuxiliaryDatabaseObject in which the CREATE and DROP strings are
+ * provided up front. Contains simple facilities for templating the catalog and schema
+ * names into the provided strings.
+ * <p/>
+ * This is the form created when the mapping documents use <create/> and
+ * <drop/>.
+ *
+ * @author Steve Ebersole
+ */
+public class SimpleAuxiliaryDatabaseObject extends AbstractAuxiliaryDatabaseObject {
+
+ private final String sqlCreateString;
+ private final String sqlDropString;
+
+ public SimpleAuxiliaryDatabaseObject(String sqlCreateString, String sqlDropString) {
+ this.sqlCreateString = sqlCreateString;
+ this.sqlDropString = sqlDropString;
+ }
+
+ public SimpleAuxiliaryDatabaseObject(String sqlCreateString, String sqlDropString, HashSet dialectScopes) {
+ super( dialectScopes );
+ this.sqlCreateString = sqlCreateString;
+ this.sqlDropString = sqlDropString;
+ }
+
+ public String sqlCreateString(
+ Dialect dialect,
+ Mapping p,
+ String defaultCatalog,
+ String defaultSchema) throws HibernateException {
+ return injectCatalogAndSchema( sqlCreateString, defaultCatalog, defaultSchema );
+ }
+
+ public String sqlDropString(Dialect dialect, String defaultCatalog, String defaultSchema) {
+ return injectCatalogAndSchema( sqlDropString, defaultCatalog, defaultSchema );
+ }
+
+ private String injectCatalogAndSchema(String ddlString, String defaultCatalog, String defaultSchema) {
+ String rtn = StringHelper.replace( ddlString, "${catalog}", defaultCatalog );
+ rtn = StringHelper.replace( rtn, "${schema}", defaultSchema );
+ return rtn;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/SimpleValue.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/SimpleValue.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/SimpleValue.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,314 @@
+//$Id: SimpleValue.java 8046 2005-08-30 20:11:43Z oneovthafew $
+package org.hibernate.mapping;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
+
+import org.hibernate.FetchMode;
+import org.hibernate.MappingException;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.Mapping;
+import org.hibernate.id.IdentifierGenerator;
+import org.hibernate.id.IdentifierGeneratorFactory;
+import org.hibernate.id.IdentityGenerator;
+import org.hibernate.id.PersistentIdentifierGenerator;
+import org.hibernate.type.Type;
+import org.hibernate.type.TypeFactory;
+import org.hibernate.util.ReflectHelper;
+
+/**
+ * Any value that maps to columns.
+ * @author Gavin King
+ */
+public class SimpleValue implements KeyValue {
+
+ private final List columns = new ArrayList();
+ private String typeName;
+ private Properties identifierGeneratorProperties;
+ private String identifierGeneratorStrategy = "assigned";
+ private String nullValue;
+ private Table table;
+ private String foreignKeyName;
+ private boolean alternateUniqueKey;
+ private Properties typeParameters;
+ private boolean cascadeDeleteEnabled;
+
+ public boolean isCascadeDeleteEnabled() {
+ return cascadeDeleteEnabled;
+ }
+
+ public void setCascadeDeleteEnabled(boolean cascadeDeleteEnabled) {
+ this.cascadeDeleteEnabled = cascadeDeleteEnabled;
+ }
+
+ public void addColumn(Column column) {
+ if ( !columns.contains(column) ) columns.add(column);
+ column.setValue(this);
+ column.setTypeIndex( columns.size()-1 );
+ }
+
+ public void addFormula(Formula formula) {
+ columns.add(formula);
+ }
+
+ public boolean hasFormula() {
+ Iterator iter = getColumnIterator();
+ while ( iter.hasNext() ) {
+ Object o = iter.next();
+ if (o instanceof Formula) return true;
+ }
+ return false;
+ }
+
+ public int getColumnSpan() {
+ return columns.size();
+ }
+ public Iterator getColumnIterator() {
+ return columns.iterator();
+ }
+ public List getConstraintColumns() {
+ return columns;
+ }
+ public String getTypeName() {
+ return typeName;
+ }
+ public void setTypeName(String type) {
+ this.typeName = type;
+ }
+ public void setTable(Table table) {
+ this.table = table;
+ }
+
+ public SimpleValue(Table table) {
+ this.table = table;
+ }
+
+ public SimpleValue() {}
+
+ public void createForeignKey() throws MappingException {}
+
+ public void createForeignKeyOfEntity(String entityName) {
+ if ( !hasFormula() && !"none".equals(getForeignKeyName())) {
+ ForeignKey fk = table.createForeignKey( getForeignKeyName(), getConstraintColumns(), entityName );
+ fk.setCascadeDeleteEnabled(cascadeDeleteEnabled);
+ }
+ }
+
+ public IdentifierGenerator createIdentifierGenerator(
+ Dialect dialect,
+ String defaultCatalog,
+ String defaultSchema,
+ RootClass rootClass)
+ throws MappingException {
+
+ Properties params = new Properties();
+
+ //if the hibernate-mapping did not specify a schema/catalog, use the defaults
+ //specified by properties - but note that if the schema/catalog were specified
+ //in hibernate-mapping, or as params, they will already be initialized and
+ //will override the values set here (they are in identifierGeneratorProperties)
+ if ( defaultSchema!=null ) {
+ params.setProperty(PersistentIdentifierGenerator.SCHEMA, defaultSchema);
+ }
+ if ( defaultCatalog!=null ) {
+ params.setProperty(PersistentIdentifierGenerator.CATALOG, defaultCatalog);
+ }
+
+ //pass the entity-name, if not a collection-id
+ if (rootClass!=null) {
+ params.setProperty( IdentifierGenerator.ENTITY_NAME, rootClass.getEntityName() );
+ }
+
+ //init the table here instead of earlier, so that we can get a quoted table name
+ //TODO: would it be better to simply pass the qualified table name, instead of
+ // splitting it up into schema/catalog/table names
+ String tableName = getTable().getQuotedName(dialect);
+ params.setProperty( PersistentIdentifierGenerator.TABLE, tableName );
+
+ //pass the column name (a generated id almost always has a single column)
+ String columnName = ( (Column) getColumnIterator().next() ).getQuotedName(dialect);
+ params.setProperty( PersistentIdentifierGenerator.PK, columnName );
+
+ if (rootClass!=null) {
+ StringBuffer tables = new StringBuffer();
+ Iterator iter = rootClass.getIdentityTables().iterator();
+ while ( iter.hasNext() ) {
+ Table table= (Table) iter.next();
+ tables.append( table.getQuotedName(dialect) );
+ if ( iter.hasNext() ) tables.append(", ");
+ }
+ params.setProperty( PersistentIdentifierGenerator.TABLES, tables.toString() );
+ }
+ else {
+ params.setProperty( PersistentIdentifierGenerator.TABLES, tableName );
+ }
+
+ if (identifierGeneratorProperties!=null) {
+ params.putAll(identifierGeneratorProperties);
+ }
+
+ return IdentifierGeneratorFactory.create(
+ identifierGeneratorStrategy,
+ getType(),
+ params,
+ dialect
+ );
+
+ }
+
+ public boolean isUpdateable() {
+ //needed to satisfy KeyValue
+ return true;
+ }
+
+ public FetchMode getFetchMode() {
+ return FetchMode.SELECT;
+ }
+
+ public Properties getIdentifierGeneratorProperties() {
+ return identifierGeneratorProperties;
+ }
+
+ public String getNullValue() {
+ return nullValue;
+ }
+
+ public Table getTable() {
+ return table;
+ }
+
+ /**
+ * Returns the identifierGeneratorStrategy.
+ * @return String
+ */
+ public String getIdentifierGeneratorStrategy() {
+ return identifierGeneratorStrategy;
+ }
+
+ public boolean isIdentityColumn(Dialect dialect) {
+ return IdentifierGeneratorFactory.getIdentifierGeneratorClass(identifierGeneratorStrategy, dialect)
+ .equals(IdentityGenerator.class);
+ }
+
+ /**
+ * Sets the identifierGeneratorProperties.
+ * @param identifierGeneratorProperties The identifierGeneratorProperties to set
+ */
+ public void setIdentifierGeneratorProperties(Properties identifierGeneratorProperties) {
+ this.identifierGeneratorProperties = identifierGeneratorProperties;
+ }
+
+ /**
+ * Sets the identifierGeneratorStrategy.
+ * @param identifierGeneratorStrategy The identifierGeneratorStrategy to set
+ */
+ public void setIdentifierGeneratorStrategy(String identifierGeneratorStrategy) {
+ this.identifierGeneratorStrategy = identifierGeneratorStrategy;
+ }
+
+ /**
+ * Sets the nullValue.
+ * @param nullValue The nullValue to set
+ */
+ public void setNullValue(String nullValue) {
+ this.nullValue = nullValue;
+ }
+
+ public String getForeignKeyName() {
+ return foreignKeyName;
+ }
+
+ public void setForeignKeyName(String foreignKeyName) {
+ this.foreignKeyName = foreignKeyName;
+ }
+
+ public boolean isAlternateUniqueKey() {
+ return alternateUniqueKey;
+ }
+
+ public void setAlternateUniqueKey(boolean unique) {
+ this.alternateUniqueKey = unique;
+ }
+
+ public boolean isNullable() {
+ if ( hasFormula() ) return true;
+ boolean nullable = true;
+ Iterator iter = getColumnIterator();
+ while ( iter.hasNext() ) {
+ if ( !( (Column) iter.next() ).isNullable() ) {
+ nullable = false;
+ return nullable; //shortcut
+ }
+ }
+ return nullable;
+ }
+
+ public boolean isSimpleValue() {
+ return true;
+ }
+
+ public boolean isValid(Mapping mapping) throws MappingException {
+ return getColumnSpan()==getType().getColumnSpan(mapping);
+ }
+
+ public Type getType() throws MappingException {
+ if (typeName==null) {
+ throw new MappingException("No type name");
+ }
+ Type result = TypeFactory.heuristicType(typeName, typeParameters);
+ if (result==null) {
+ String msg = "Could not determine type for: " + typeName;
+ if(columns!=null && columns.size()>0) {
+ msg += ", for columns: " + columns;
+ }
+ throw new MappingException(msg);
+ }
+ return result;
+ }
+
+ public void setTypeUsingReflection(String className, String propertyName) throws MappingException {
+ if (typeName==null) {
+ if (className==null) {
+ throw new MappingException("you must specify types for a dynamic entity: " + propertyName);
+ }
+ typeName = ReflectHelper.reflectedPropertyClass(className, propertyName).getName();
+ }
+ }
+
+ public boolean isTypeSpecified() {
+ return typeName!=null;
+ }
+
+ public void setTypeParameters(Properties parameterMap) {
+ this.typeParameters = parameterMap;
+ }
+
+ public Properties getTypeParameters() {
+ return typeParameters;
+ }
+
+ public String toString() {
+ return getClass().getName() + '(' + columns.toString() + ')';
+ }
+
+ public Object accept(ValueVisitor visitor) {
+ return visitor.accept(this);
+ }
+
+ public boolean[] getColumnInsertability() {
+ boolean[] result = new boolean[ getColumnSpan() ];
+ int i = 0;
+ Iterator iter = getColumnIterator();
+ while ( iter.hasNext() ) {
+ Selectable s = (Selectable) iter.next();
+ result[i++] = !s.isFormula();
+ }
+ return result;
+ }
+
+ public boolean[] getColumnUpdateability() {
+ return getColumnInsertability();
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/SingleTableSubclass.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/SingleTableSubclass.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/SingleTableSubclass.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,45 @@
+//$Id: SingleTableSubclass.java 7270 2005-06-22 17:02:26Z turin42 $
+package org.hibernate.mapping;
+
+import java.util.Iterator;
+
+import org.hibernate.MappingException;
+import org.hibernate.engine.Mapping;
+import org.hibernate.util.JoinedIterator;
+
+/**
+ * @author Gavin King
+ */
+public class SingleTableSubclass extends Subclass {
+
+ public SingleTableSubclass(PersistentClass superclass) {
+ super(superclass);
+ }
+
+ protected Iterator getNonDuplicatedPropertyIterator() {
+ return new JoinedIterator(
+ getSuperclass().getUnjoinedPropertyIterator(),
+ getUnjoinedPropertyIterator()
+ );
+ }
+
+ protected Iterator getDiscriminatorColumnIterator() {
+ if ( isDiscriminatorInsertable() && !getDiscriminator().hasFormula() ) {
+ return getDiscriminator().getColumnIterator();
+ }
+ else {
+ return super.getDiscriminatorColumnIterator();
+ }
+ }
+
+ public Object accept(PersistentClassVisitor mv) {
+ return mv.accept(this);
+ }
+
+ public void validate(Mapping mapping) throws MappingException {
+ if(getDiscriminator()==null) {
+ throw new MappingException("No discriminator found for " + getEntityName() + ". Discriminator is needed when 'single-table-per-hierarchy' is used and a class has subclasses");
+ }
+ super.validate(mapping);
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/SingleTableSubclass.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Subclass.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Subclass.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Subclass.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,257 @@
+//$Id: Subclass.java 10119 2006-07-14 00:09:19Z steve.ebersole at jboss.com $
+package org.hibernate.mapping;
+
+import java.util.*;
+import java.util.Map;
+
+import org.hibernate.AssertionFailure;
+import org.hibernate.EntityMode;
+import org.hibernate.util.JoinedIterator;
+import org.hibernate.util.SingletonIterator;
+
+/**
+ * A sublass in a table-per-class-hierarchy mapping
+ * @author Gavin King
+ */
+public class Subclass extends PersistentClass {
+
+ private PersistentClass superclass;
+ private Class classPersisterClass;
+ private final int subclassId;
+
+ public Subclass(PersistentClass superclass) {
+ this.superclass = superclass;
+ this.subclassId = superclass.nextSubclassId();
+ }
+
+ int nextSubclassId() {
+ return getSuperclass().nextSubclassId();
+ }
+
+ public int getSubclassId() {
+ return subclassId;
+ }
+
+ public String getCacheConcurrencyStrategy() {
+ return getSuperclass().getCacheConcurrencyStrategy();
+ }
+
+ public RootClass getRootClass() {
+ return getSuperclass().getRootClass();
+ }
+
+ public PersistentClass getSuperclass() {
+ return superclass;
+ }
+
+ public Property getIdentifierProperty() {
+ return getSuperclass().getIdentifierProperty();
+ }
+ public KeyValue getIdentifier() {
+ return getSuperclass().getIdentifier();
+ }
+ public boolean hasIdentifierProperty() {
+ return getSuperclass().hasIdentifierProperty();
+ }
+ public Value getDiscriminator() {
+ return getSuperclass().getDiscriminator();
+ }
+ public boolean isMutable() {
+ return getSuperclass().isMutable();
+ }
+ public boolean isInherited() {
+ return true;
+ }
+ public boolean isPolymorphic() {
+ return true;
+ }
+
+ public void addProperty(Property p) {
+ super.addProperty(p);
+ getSuperclass().addSubclassProperty(p);
+ }
+ public void addJoin(Join j) {
+ super.addJoin(j);
+ getSuperclass().addSubclassJoin(j);
+ }
+
+ public Iterator getPropertyClosureIterator() {
+ return new JoinedIterator(
+ getSuperclass().getPropertyClosureIterator(),
+ getPropertyIterator()
+ );
+ }
+ public Iterator getTableClosureIterator() {
+ return new JoinedIterator(
+ getSuperclass().getTableClosureIterator(),
+ new SingletonIterator( getTable() )
+ );
+ }
+ public Iterator getKeyClosureIterator() {
+ return new JoinedIterator(
+ getSuperclass().getKeyClosureIterator(),
+ new SingletonIterator( getKey() )
+ );
+ }
+ protected void addSubclassProperty(Property p) {
+ super.addSubclassProperty(p);
+ getSuperclass().addSubclassProperty(p);
+ }
+ protected void addSubclassJoin(Join j) {
+ super.addSubclassJoin(j);
+ getSuperclass().addSubclassJoin(j);
+ }
+
+ protected void addSubclassTable(Table table) {
+ super.addSubclassTable(table);
+ getSuperclass().addSubclassTable(table);
+ }
+
+ public boolean isVersioned() {
+ return getSuperclass().isVersioned();
+ }
+ public Property getVersion() {
+ return getSuperclass().getVersion();
+ }
+
+ public boolean hasEmbeddedIdentifier() {
+ return getSuperclass().hasEmbeddedIdentifier();
+ }
+ public Class getEntityPersisterClass() {
+ if (classPersisterClass==null) {
+ return getSuperclass().getEntityPersisterClass();
+ }
+ else {
+ return classPersisterClass;
+ }
+ }
+
+ public Table getRootTable() {
+ return getSuperclass().getRootTable();
+ }
+
+ public KeyValue getKey() {
+ return getSuperclass().getIdentifier();
+ }
+
+ public boolean isExplicitPolymorphism() {
+ return getSuperclass().isExplicitPolymorphism();
+ }
+
+ public void setSuperclass(PersistentClass superclass) {
+ this.superclass = superclass;
+ }
+
+ public String getWhere() {
+ return getSuperclass().getWhere();
+ }
+
+ public boolean isJoinedSubclass() {
+ return getTable()!=getRootTable();
+ }
+
+ public void createForeignKey() {
+ if ( !isJoinedSubclass() ) {
+ throw new AssertionFailure( "not a joined-subclass" );
+ }
+ getKey().createForeignKeyOfEntity( getSuperclass().getEntityName() );
+ }
+
+ public void setEntityPersisterClass(Class classPersisterClass) {
+ this.classPersisterClass = classPersisterClass;
+ }
+
+ public boolean isLazyPropertiesCacheable() {
+ return getSuperclass().isLazyPropertiesCacheable();
+ }
+
+ public int getJoinClosureSpan() {
+ return getSuperclass().getJoinClosureSpan() + super.getJoinClosureSpan();
+ }
+
+ public int getPropertyClosureSpan() {
+ return getSuperclass().getPropertyClosureSpan() + super.getPropertyClosureSpan();
+ }
+
+ public Iterator getJoinClosureIterator() {
+ return new JoinedIterator(
+ getSuperclass().getJoinClosureIterator(),
+ super.getJoinClosureIterator()
+ );
+ }
+
+ public boolean isClassOrSuperclassJoin(Join join) {
+ return super.isClassOrSuperclassJoin(join) || getSuperclass().isClassOrSuperclassJoin(join);
+ }
+
+ public boolean isClassOrSuperclassTable(Table table) {
+ return super.isClassOrSuperclassTable(table) || getSuperclass().isClassOrSuperclassTable(table);
+ }
+
+ public Table getTable() {
+ return getSuperclass().getTable();
+ }
+
+ public boolean isForceDiscriminator() {
+ return getSuperclass().isForceDiscriminator();
+ }
+
+ public boolean isDiscriminatorInsertable() {
+ return getSuperclass().isDiscriminatorInsertable();
+ }
+
+ public java.util.Set getSynchronizedTables() {
+ HashSet result = new HashSet();
+ result.addAll(synchronizedTables);
+ result.addAll( getSuperclass().getSynchronizedTables() );
+ return result;
+ }
+
+ public Object accept(PersistentClassVisitor mv) {
+ return mv.accept(this);
+ }
+
+ public Map getFilterMap() {
+ return getSuperclass().getFilterMap();
+ }
+
+ public boolean hasSubselectLoadableCollections() {
+ return super.hasSubselectLoadableCollections() ||
+ getSuperclass().hasSubselectLoadableCollections();
+ }
+
+ public String getTuplizerImplClassName(EntityMode mode) {
+ String impl = super.getTuplizerImplClassName( mode );
+ if ( impl == null ) {
+ impl = getSuperclass().getTuplizerImplClassName( mode );
+ }
+ return impl;
+ }
+
+ public Map getTuplizerMap() {
+ Map specificTuplizerDefs = super.getTuplizerMap();
+ Map superclassTuplizerDefs = getSuperclass().getTuplizerMap();
+ if ( specificTuplizerDefs == null && superclassTuplizerDefs == null ) {
+ return null;
+ }
+ else {
+ Map combined = new HashMap();
+ if ( superclassTuplizerDefs != null ) {
+ combined.putAll( superclassTuplizerDefs );
+ }
+ if ( specificTuplizerDefs != null ) {
+ combined.putAll( specificTuplizerDefs );
+ }
+ return java.util.Collections.unmodifiableMap( combined );
+ }
+ }
+
+ public Component getIdentifierMapper() {
+ return superclass.getIdentifierMapper();
+ }
+
+ public int getOptimisticLockMode() {
+ return superclass.getOptimisticLockMode();
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Table.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Table.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Table.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,768 @@
+//$Id: Table.java 11303 2007-03-19 22:06:14Z steve.ebersole at jboss.com $
+package org.hibernate.mapping;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.collections.SequencedHashMap;
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.Mapping;
+import org.hibernate.tool.hbm2ddl.ColumnMetadata;
+import org.hibernate.tool.hbm2ddl.TableMetadata;
+import org.hibernate.util.CollectionHelper;
+
+/**
+ * A relational table
+ *
+ * @author Gavin King
+ */
+public class Table implements RelationalModel, Serializable {
+
+ private String name;
+ private String schema;
+ private String catalog;
+ /**
+ * contains all columns, including the primary key
+ */
+ private Map columns = new SequencedHashMap();
+ private KeyValue idValue;
+ private PrimaryKey primaryKey;
+ private Map indexes = new HashMap();
+ private Map foreignKeys = new HashMap();
+ private Map uniqueKeys = new HashMap();
+ private final int uniqueInteger;
+ private boolean quoted;
+ private boolean schemaQuoted;
+ private static int tableCounter = 0;
+ private List checkConstraints = new ArrayList();
+ private String rowId;
+ private String subselect;
+ private boolean isAbstract;
+ private boolean hasDenormalizedTables = false;
+ private String comment;
+
+ static class ForeignKeyKey implements Serializable {
+ String referencedClassName;
+ List columns;
+ List referencedColumns;
+
+ ForeignKeyKey(List columns, String referencedClassName, List referencedColumns) {
+ this.referencedClassName = referencedClassName;
+ this.columns = new ArrayList();
+ this.columns.addAll( columns );
+ if ( referencedColumns != null ) {
+ this.referencedColumns = new ArrayList();
+ this.referencedColumns.addAll( referencedColumns );
+ }
+ else {
+ this.referencedColumns = CollectionHelper.EMPTY_LIST;
+ }
+ }
+
+ public int hashCode() {
+ return columns.hashCode() + referencedColumns.hashCode();
+ }
+
+ public boolean equals(Object other) {
+ ForeignKeyKey fkk = (ForeignKeyKey) other;
+ return fkk.columns.equals( columns ) &&
+ fkk.referencedClassName.equals( referencedClassName ) && fkk.referencedColumns
+ .equals( referencedColumns );
+ }
+ }
+
+ public Table() {
+ uniqueInteger = tableCounter++;
+ }
+
+ public Table(String name) {
+ this();
+ setName( name );
+ }
+
+ public String getQualifiedName(Dialect dialect, String defaultCatalog, String defaultSchema) {
+ if ( subselect != null ) {
+ return "( " + subselect + " )";
+ }
+ String quotedName = getQuotedName( dialect );
+ String usedSchema = schema == null ?
+ defaultSchema :
+ getQuotedSchema( dialect );
+ String usedCatalog = catalog == null ?
+ defaultCatalog :
+ catalog;
+ return qualify( usedCatalog, usedSchema, quotedName );
+ }
+
+ public static String qualify(String catalog, String schema, String table) {
+ StringBuffer qualifiedName = new StringBuffer();
+ if ( catalog != null ) {
+ qualifiedName.append( catalog ).append( '.' );
+ }
+ if ( schema != null ) {
+ qualifiedName.append( schema ).append( '.' );
+ }
+ return qualifiedName.append( table ).toString();
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * returns quoted name as it would be in the mapping file.
+ */
+ public String getQuotedName() {
+ return quoted ?
+ "`" + name + "`" :
+ name;
+ }
+
+ public String getQuotedName(Dialect dialect) {
+ return quoted ?
+ dialect.openQuote() + name + dialect.closeQuote() :
+ name;
+ }
+
+ /**
+ * returns quoted name as it is in the mapping file.
+ */
+ public String getQuotedSchema() {
+ return schemaQuoted ?
+ "`" + schema + "`" :
+ schema;
+ }
+
+ public String getQuotedSchema(Dialect dialect) {
+ return schemaQuoted ?
+ dialect.openQuote() + schema + dialect.closeQuote() :
+ schema;
+ }
+
+ public void setName(String name) {
+ if ( name.charAt( 0 ) == '`' ) {
+ quoted = true;
+ this.name = name.substring( 1, name.length() - 1 );
+ }
+ else {
+ this.name = name;
+ }
+ }
+
+ /**
+ * Return the column which is identified by column provided as argument.
+ *
+ * @param column column with atleast a name.
+ * @return the underlying column or null if not inside this table. Note: the instance *can* be different than the input parameter, but the name will be the same.
+ */
+ public Column getColumn(Column column) {
+ if ( column == null ) {
+ return null;
+ }
+
+ Column myColumn = (Column) columns.get( column.getCanonicalName() );
+
+ return column.equals( myColumn ) ?
+ myColumn :
+ null;
+ }
+
+ public Column getColumn(int n) {
+ Iterator iter = columns.values().iterator();
+ for ( int i = 0; i < n - 1; i++ ) {
+ iter.next();
+ }
+ return (Column) iter.next();
+ }
+
+ public void addColumn(Column column) {
+ Column old = (Column) getColumn( column );
+ if ( old == null ) {
+ columns.put( column.getCanonicalName(), column );
+ column.uniqueInteger = columns.size();
+ }
+ else {
+ column.uniqueInteger = old.uniqueInteger;
+ }
+ }
+
+ public int getColumnSpan() {
+ return columns.size();
+ }
+
+ public Iterator getColumnIterator() {
+ return columns.values().iterator();
+ }
+
+ public Iterator getIndexIterator() {
+ return indexes.values().iterator();
+ }
+
+ public Iterator getForeignKeyIterator() {
+ return foreignKeys.values().iterator();
+ }
+
+ public Iterator getUniqueKeyIterator() {
+ return getUniqueKeys().values().iterator();
+ }
+
+ Map getUniqueKeys() {
+ if ( uniqueKeys.size() > 1 ) {
+ //deduplicate unique constraints sharing the same columns
+ //this is needed by Hibernate Annotations since it creates automagically
+ // unique constraints for the user
+ Iterator it = uniqueKeys.entrySet().iterator();
+ Map finalUniqueKeys = new HashMap( uniqueKeys.size() );
+ while ( it.hasNext() ) {
+ Map.Entry entry = (Map.Entry) it.next();
+ UniqueKey uk = (UniqueKey) entry.getValue();
+ List columns = uk.getColumns();
+ int size = finalUniqueKeys.size();
+ boolean skip = false;
+ Iterator tempUks = finalUniqueKeys.entrySet().iterator();
+ while ( tempUks.hasNext() ) {
+ final UniqueKey currentUk = (UniqueKey) ( (Map.Entry) tempUks.next() ).getValue();
+ if ( currentUk.getColumns().containsAll( columns ) && columns
+ .containsAll( currentUk.getColumns() ) ) {
+ skip = true;
+ break;
+ }
+ }
+ if ( !skip ) finalUniqueKeys.put( entry.getKey(), uk );
+ }
+ return finalUniqueKeys;
+ }
+ else {
+ return uniqueKeys;
+ }
+ }
+
+ public void validateColumns(Dialect dialect, Mapping mapping, TableMetadata tableInfo) {
+ Iterator iter = getColumnIterator();
+ while ( iter.hasNext() ) {
+ Column col = (Column) iter.next();
+
+ ColumnMetadata columnInfo = tableInfo.getColumnMetadata( col.getName() );
+
+ if ( columnInfo == null ) {
+ throw new HibernateException( "Missing column: " + col.getName() + " in " + Table.qualify( tableInfo.getCatalog(), tableInfo.getSchema(), tableInfo.getName()));
+ }
+ else {
+ final boolean typesMatch = col.getSqlType( dialect, mapping )
+ .startsWith( columnInfo.getTypeName().toLowerCase() )
+ || columnInfo.getTypeCode() == col.getSqlTypeCode( mapping );
+ if ( !typesMatch ) {
+ throw new HibernateException(
+ "Wrong column type: " + col.getName() +
+ ", expected: " + col.getSqlType( dialect, mapping )
+ );
+ }
+ }
+ }
+
+ }
+
+ public Iterator sqlAlterStrings(Dialect dialect, Mapping p, TableMetadata tableInfo, String defaultCatalog,
+ String defaultSchema)
+ throws HibernateException {
+
+ StringBuffer root = new StringBuffer( "alter table " )
+ .append( getQualifiedName( dialect, defaultCatalog, defaultSchema ) )
+ .append( ' ' )
+ .append( dialect.getAddColumnString() );
+
+ Iterator iter = getColumnIterator();
+ List results = new ArrayList();
+ while ( iter.hasNext() ) {
+ Column column = (Column) iter.next();
+
+ ColumnMetadata columnInfo = tableInfo.getColumnMetadata( column.getName() );
+
+ if ( columnInfo == null ) {
+ // the column doesnt exist at all.
+ StringBuffer alter = new StringBuffer( root.toString() )
+ .append( ' ' )
+ .append( column.getQuotedName( dialect ) )
+ .append( ' ' )
+ .append( column.getSqlType( dialect, p ) );
+
+ String defaultValue = column.getDefaultValue();
+ if ( defaultValue != null ) {
+ alter.append( " default " ).append( defaultValue );
+
+ if ( column.isNullable() ) {
+ alter.append( dialect.getNullColumnString() );
+ }
+ else {
+ alter.append( " not null" );
+ }
+
+ }
+
+ boolean useUniqueConstraint = column.isUnique() &&
+ dialect.supportsUnique() &&
+ ( !column.isNullable() || dialect.supportsNotNullUnique() );
+ if ( useUniqueConstraint ) {
+ alter.append( " unique" );
+ }
+
+ if ( column.hasCheckConstraint() && dialect.supportsColumnCheck() ) {
+ alter.append( " check(" )
+ .append( column.getCheckConstraint() )
+ .append( ")" );
+ }
+
+ String columnComment = column.getComment();
+ if ( columnComment != null ) {
+ alter.append( dialect.getColumnComment( columnComment ) );
+ }
+
+ results.add( alter.toString() );
+ }
+
+ }
+
+ return results.iterator();
+ }
+
+ public boolean hasPrimaryKey() {
+ return getPrimaryKey() != null;
+ }
+
+ public String sqlTemporaryTableCreateString(Dialect dialect, Mapping mapping) throws HibernateException {
+ StringBuffer buffer = new StringBuffer( dialect.getCreateTemporaryTableString() )
+ .append( ' ' )
+ .append( name )
+ .append( " (" );
+ Iterator itr = getColumnIterator();
+ while ( itr.hasNext() ) {
+ final Column column = (Column) itr.next();
+ buffer.append( column.getQuotedName( dialect ) ).append( ' ' );
+ buffer.append( column.getSqlType( dialect, mapping ) );
+ if ( column.isNullable() ) {
+ buffer.append( dialect.getNullColumnString() );
+ }
+ else {
+ buffer.append( " not null" );
+ }
+ if ( itr.hasNext() ) {
+ buffer.append( ", " );
+ }
+ }
+ buffer.append( ") " );
+ buffer.append( dialect.getCreateTemporaryTablePostfix() );
+ return buffer.toString();
+ }
+
+ public String sqlCreateString(Dialect dialect, Mapping p, String defaultCatalog, String defaultSchema) {
+ StringBuffer buf = new StringBuffer( hasPrimaryKey() ? dialect.getCreateTableString() : dialect.getCreateMultisetTableString() )
+ .append( ' ' )
+ .append( getQualifiedName( dialect, defaultCatalog, defaultSchema ) )
+ .append( " (" );
+
+ boolean identityColumn = idValue != null && idValue.isIdentityColumn( dialect );
+
+ // Try to find out the name of the primary key to create it as identity if the IdentityGenerator is used
+ String pkname = null;
+ if ( hasPrimaryKey() && identityColumn ) {
+ pkname = ( (Column) getPrimaryKey().getColumnIterator().next() ).getQuotedName( dialect );
+ }
+
+ Iterator iter = getColumnIterator();
+ while ( iter.hasNext() ) {
+ Column col = (Column) iter.next();
+
+ buf.append( col.getQuotedName( dialect ) )
+ .append( ' ' );
+
+ if ( identityColumn && col.getQuotedName( dialect ).equals( pkname ) ) {
+ // to support dialects that have their own identity data type
+ if ( dialect.hasDataTypeInIdentityColumn() ) {
+ buf.append( col.getSqlType( dialect, p ) );
+ }
+ buf.append( ' ' )
+ .append( dialect.getIdentityColumnString( col.getSqlTypeCode( p ) ) );
+ }
+ else {
+
+ buf.append( col.getSqlType( dialect, p ) );
+
+ String defaultValue = col.getDefaultValue();
+ if ( defaultValue != null ) {
+ buf.append( " default " ).append( defaultValue );
+ }
+
+ if ( col.isNullable() ) {
+ buf.append( dialect.getNullColumnString() );
+ }
+ else {
+ buf.append( " not null" );
+ }
+
+ }
+
+ boolean useUniqueConstraint = col.isUnique() &&
+ ( !col.isNullable() || dialect.supportsNotNullUnique() );
+ if ( useUniqueConstraint ) {
+ if ( dialect.supportsUnique() ) {
+ buf.append( " unique" );
+ }
+ else {
+ UniqueKey uk = getOrCreateUniqueKey( col.getQuotedName( dialect ) + '_' );
+ uk.addColumn( col );
+ }
+ }
+
+ if ( col.hasCheckConstraint() && dialect.supportsColumnCheck() ) {
+ buf.append( " check (" )
+ .append( col.getCheckConstraint() )
+ .append( ")" );
+ }
+
+ String columnComment = col.getComment();
+ if ( columnComment != null ) {
+ buf.append( dialect.getColumnComment( columnComment ) );
+ }
+
+ if ( iter.hasNext() ) {
+ buf.append( ", " );
+ }
+
+ }
+ if ( hasPrimaryKey() ) {
+ buf.append( ", " )
+ .append( getPrimaryKey().sqlConstraintString( dialect ) );
+ }
+
+ if ( dialect.supportsUniqueConstraintInCreateAlterTable() ) {
+ Iterator ukiter = getUniqueKeyIterator();
+ while ( ukiter.hasNext() ) {
+ UniqueKey uk = (UniqueKey) ukiter.next();
+ String constraint = uk.sqlConstraintString( dialect );
+ if ( constraint != null ) {
+ buf.append( ", " ).append( constraint );
+ }
+ }
+ }
+ /*Iterator idxiter = getIndexIterator();
+ while ( idxiter.hasNext() ) {
+ Index idx = (Index) idxiter.next();
+ buf.append(',').append( idx.sqlConstraintString(dialect) );
+ }*/
+
+ if ( dialect.supportsTableCheck() ) {
+ Iterator chiter = checkConstraints.iterator();
+ while ( chiter.hasNext() ) {
+ buf.append( ", check (" )
+ .append( chiter.next() )
+ .append( ')' );
+ }
+ }
+
+ buf.append( ')' );
+
+ if ( comment != null ) {
+ buf.append( dialect.getTableComment( comment ) );
+ }
+
+ return buf.append( dialect.getTableTypeString() ).toString();
+ }
+
+ public String sqlDropString(Dialect dialect, String defaultCatalog, String defaultSchema) {
+ StringBuffer buf = new StringBuffer( "drop table " );
+ if ( dialect.supportsIfExistsBeforeTableName() ) {
+ buf.append( "if exists " );
+ }
+ buf.append( getQualifiedName( dialect, defaultCatalog, defaultSchema ) )
+ .append( dialect.getCascadeConstraintsString() );
+ if ( dialect.supportsIfExistsAfterTableName() ) {
+ buf.append( " if exists" );
+ }
+ return buf.toString();
+ }
+
+ public PrimaryKey getPrimaryKey() {
+ return primaryKey;
+ }
+
+ public void setPrimaryKey(PrimaryKey primaryKey) {
+ this.primaryKey = primaryKey;
+ }
+
+ public Index getOrCreateIndex(String indexName) {
+
+ Index index = (Index) indexes.get( indexName );
+
+ if ( index == null ) {
+ index = new Index();
+ index.setName( indexName );
+ index.setTable( this );
+ indexes.put( indexName, index );
+ }
+
+ return index;
+ }
+
+ public Index getIndex(String indexName) {
+ return (Index) indexes.get( indexName );
+ }
+
+ public Index addIndex(Index index) {
+ Index current = (Index) indexes.get( index.getName() );
+ if ( current != null ) {
+ throw new MappingException( "Index " + index.getName() + " already exists!" );
+ }
+ indexes.put( index.getName(), index );
+ return index;
+ }
+
+ public UniqueKey addUniqueKey(UniqueKey uniqueKey) {
+ UniqueKey current = (UniqueKey) uniqueKeys.get( uniqueKey.getName() );
+ if ( current != null ) {
+ throw new MappingException( "UniqueKey " + uniqueKey.getName() + " already exists!" );
+ }
+ uniqueKeys.put( uniqueKey.getName(), uniqueKey );
+ return uniqueKey;
+ }
+
+ public UniqueKey createUniqueKey(List keyColumns) {
+ String keyName = "UK" + uniqueColumnString( keyColumns.iterator() );
+ UniqueKey uk = getOrCreateUniqueKey( keyName );
+ uk.addColumns( keyColumns.iterator() );
+ return uk;
+ }
+
+ public UniqueKey getUniqueKey(String keyName) {
+ return (UniqueKey) uniqueKeys.get( keyName );
+ }
+
+ public UniqueKey getOrCreateUniqueKey(String keyName) {
+ UniqueKey uk = (UniqueKey) uniqueKeys.get( keyName );
+
+ if ( uk == null ) {
+ uk = new UniqueKey();
+ uk.setName( keyName );
+ uk.setTable( this );
+ uniqueKeys.put( keyName, uk );
+ }
+ return uk;
+ }
+
+ public void createForeignKeys() {
+ }
+
+ public ForeignKey createForeignKey(String keyName, List keyColumns, String referencedEntityName) {
+ return createForeignKey( keyName, keyColumns, referencedEntityName, null );
+ }
+
+ public ForeignKey createForeignKey(String keyName, List keyColumns, String referencedEntityName,
+ List referencedColumns) {
+ Object key = new ForeignKeyKey( keyColumns, referencedEntityName, referencedColumns );
+
+ ForeignKey fk = (ForeignKey) foreignKeys.get( key );
+ if ( fk == null ) {
+ fk = new ForeignKey();
+ if ( keyName != null ) {
+ fk.setName( keyName );
+ }
+ else {
+ fk.setName( "FK" + uniqueColumnString( keyColumns.iterator(), referencedEntityName ) );
+ //TODO: add referencedClass to disambiguate to FKs on the same
+ // columns, pointing to different tables
+ }
+ fk.setTable( this );
+ foreignKeys.put( key, fk );
+ fk.setReferencedEntityName( referencedEntityName );
+ fk.addColumns( keyColumns.iterator() );
+ if ( referencedColumns != null ) {
+ fk.addReferencedColumns( referencedColumns.iterator() );
+ }
+ }
+
+ if ( keyName != null ) {
+ fk.setName( keyName );
+ }
+
+ return fk;
+ }
+
+
+ public String uniqueColumnString(Iterator iterator) {
+ return uniqueColumnString( iterator, null );
+ }
+
+ public String uniqueColumnString(Iterator iterator, String referencedEntityName) {
+ int result = 0;
+ if ( referencedEntityName != null ) {
+ result += referencedEntityName.hashCode();
+ }
+ while ( iterator.hasNext() ) {
+ result += iterator.next().hashCode();
+ }
+ return ( Integer.toHexString( name.hashCode() ) + Integer.toHexString( result ) ).toUpperCase();
+ }
+
+
+ public String getSchema() {
+ return schema;
+ }
+
+ public void setSchema(String schema) {
+ if ( schema != null && schema.charAt( 0 ) == '`' ) {
+ schemaQuoted = true;
+ this.schema = schema.substring( 1, schema.length() - 1 );
+ }
+ else {
+ this.schema = schema;
+ }
+ }
+
+ public String getCatalog() {
+ return catalog;
+ }
+
+ public void setCatalog(String catalog) {
+ this.catalog = catalog;
+ }
+
+ public int getUniqueInteger() {
+ return uniqueInteger;
+ }
+
+ public void setIdentifierValue(KeyValue idValue) {
+ this.idValue = idValue;
+ }
+
+ public KeyValue getIdentifierValue() {
+ return idValue;
+ }
+
+ public boolean isSchemaQuoted() {
+ return schemaQuoted;
+ }
+
+ public boolean isQuoted() {
+ return quoted;
+ }
+
+ public void setQuoted(boolean quoted) {
+ this.quoted = quoted;
+ }
+
+ public void addCheckConstraint(String constraint) {
+ checkConstraints.add( constraint );
+ }
+
+ public boolean containsColumn(Column column) {
+ return columns.containsValue( column );
+ }
+
+ public String getRowId() {
+ return rowId;
+ }
+
+ public void setRowId(String rowId) {
+ this.rowId = rowId;
+ }
+
+ public String toString() {
+ StringBuffer buf = new StringBuffer().append( getClass().getName() )
+ .append( '(' );
+ if ( getCatalog() != null ) {
+ buf.append( getCatalog() + "." );
+ }
+ if ( getSchema() != null ) {
+ buf.append( getSchema() + "." );
+ }
+ buf.append( getName() ).append( ')' );
+ return buf.toString();
+ }
+
+ public String getSubselect() {
+ return subselect;
+ }
+
+ public void setSubselect(String subselect) {
+ this.subselect = subselect;
+ }
+
+ public boolean isSubselect() {
+ return subselect != null;
+ }
+
+ public boolean isAbstractUnionTable() {
+ return hasDenormalizedTables() && isAbstract;
+ }
+
+ public boolean hasDenormalizedTables() {
+ return hasDenormalizedTables;
+ }
+
+ void setHasDenormalizedTables() {
+ hasDenormalizedTables = true;
+ }
+
+ public void setAbstract(boolean isAbstract) {
+ this.isAbstract = isAbstract;
+ }
+
+ public boolean isAbstract() {
+ return isAbstract;
+ }
+
+ public boolean isPhysicalTable() {
+ return !isSubselect() && !isAbstractUnionTable();
+ }
+
+ public String getComment() {
+ return comment;
+ }
+
+ public void setComment(String comment) {
+ this.comment = comment;
+ }
+
+ public Iterator getCheckConstraintsIterator() {
+ return checkConstraints.iterator();
+ }
+
+ public Iterator sqlCommentStrings(Dialect dialect, String defaultCatalog, String defaultSchema) {
+ List comments = new ArrayList();
+ if ( dialect.supportsCommentOn() ) {
+ String tableName = getQualifiedName( dialect, defaultCatalog, defaultSchema );
+ if ( comment != null ) {
+ StringBuffer buf = new StringBuffer()
+ .append( "comment on table " )
+ .append( tableName )
+ .append( " is '" )
+ .append( comment )
+ .append( "'" );
+ comments.add( buf.toString() );
+ }
+ Iterator iter = getColumnIterator();
+ while ( iter.hasNext() ) {
+ Column column = (Column) iter.next();
+ String columnComment = column.getComment();
+ if ( columnComment != null ) {
+ StringBuffer buf = new StringBuffer()
+ .append( "comment on column " )
+ .append( tableName )
+ .append( '.' )
+ .append( column.getQuotedName( dialect ) )
+ .append( " is '" )
+ .append( columnComment )
+ .append( "'" );
+ comments.add( buf.toString() );
+ }
+ }
+ }
+ return comments.iterator();
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/TableOwner.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/TableOwner.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/TableOwner.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,13 @@
+//$Id: TableOwner.java 4646 2004-10-06 23:24:14Z epbernard $
+package org.hibernate.mapping;
+
+/**
+ * Interface allowing to differenciate SubClasses
+ * from Classes, JoinedSubClasses and UnionSubClasses
+ * The first one has not its own table while the others have
+ *
+ * @author Emmanuel Bernard
+ */
+public interface TableOwner {
+ void setTable(Table table);
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/ToOne.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/ToOne.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/ToOne.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,108 @@
+//$Id: ToOne.java 7246 2005-06-20 20:32:36Z oneovthafew $
+package org.hibernate.mapping;
+
+import org.hibernate.FetchMode;
+import org.hibernate.MappingException;
+import org.hibernate.engine.Mapping;
+import org.hibernate.type.Type;
+import org.hibernate.util.ReflectHelper;
+
+/**
+ * A simple-point association (ie. a reference to another entity).
+ * @author Gavin King
+ */
+public abstract class ToOne extends SimpleValue implements Fetchable {
+
+ private FetchMode fetchMode;
+ protected String referencedPropertyName;
+ private String referencedEntityName;
+ private boolean embedded;
+ private boolean lazy = true;
+ protected boolean unwrapProxy;
+
+ protected ToOne(Table table) {
+ super(table);
+ }
+
+ public FetchMode getFetchMode() {
+ return fetchMode;
+ }
+
+ public void setFetchMode(FetchMode fetchMode) {
+ this.fetchMode=fetchMode;
+ }
+
+ public abstract void createForeignKey() throws MappingException;
+ public abstract Type getType() throws MappingException;
+
+ public String getReferencedPropertyName() {
+ return referencedPropertyName;
+ }
+
+ public void setReferencedPropertyName(String name) {
+ referencedPropertyName = name==null ? null : name.intern();
+ }
+
+ public String getReferencedEntityName() {
+ return referencedEntityName;
+ }
+
+ public void setReferencedEntityName(String referencedEntityName) {
+ this.referencedEntityName = referencedEntityName==null ?
+ null : referencedEntityName.intern();
+ }
+
+ public void setTypeUsingReflection(String className, String propertyName)
+ throws MappingException {
+ if (referencedEntityName==null) {
+ referencedEntityName = ReflectHelper.reflectedPropertyClass(className, propertyName).getName();
+ }
+ }
+
+ public boolean isTypeSpecified() {
+ return referencedEntityName!=null;
+ }
+
+ public Object accept(ValueVisitor visitor) {
+ return visitor.accept(this);
+ }
+
+ public boolean isEmbedded() {
+ return embedded;
+ }
+
+ public void setEmbedded(boolean embedded) {
+ this.embedded = embedded;
+ }
+
+ public boolean isValid(Mapping mapping) throws MappingException {
+ if (referencedEntityName==null) {
+ throw new MappingException("association must specify the referenced entity");
+ }
+ return super.isValid( mapping );
+ }
+
+ public boolean isLazy() {
+ return lazy;
+ }
+
+ public void setLazy(boolean lazy) {
+ this.lazy = lazy;
+ }
+
+ public boolean isUnwrapProxy() {
+ return unwrapProxy;
+ }
+
+ public void setUnwrapProxy(boolean unwrapProxy) {
+ this.unwrapProxy = unwrapProxy;
+ }
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/TypeDef.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/TypeDef.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/TypeDef.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,26 @@
+package org.hibernate.mapping;
+
+import java.io.Serializable;
+import java.util.Properties;
+
+/**
+ * Placeholder for typedef information
+ */
+public class TypeDef implements Serializable {
+
+ private String typeClass;
+ private Properties parameters;
+
+ public TypeDef(String typeClass, Properties parameters) {
+ this.typeClass = typeClass;
+ this.parameters = parameters;
+ }
+
+ public Properties getParameters() {
+ return parameters;
+ }
+ public String getTypeClass() {
+ return typeClass;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/UnionSubclass.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/UnionSubclass.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/UnionSubclass.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,58 @@
+//$Id: UnionSubclass.java 6514 2005-04-26 06:37:54Z oneovthafew $
+package org.hibernate.mapping;
+
+import java.util.Iterator;
+
+import org.hibernate.MappingException;
+import org.hibernate.engine.Mapping;
+
+/**
+ * A subclass in a table-per-concrete-class mapping
+ * @author Gavin King
+ */
+public class UnionSubclass extends Subclass implements TableOwner {
+
+ private Table table;
+ private KeyValue key;
+
+ public UnionSubclass(PersistentClass superclass) {
+ super(superclass);
+ }
+
+ public Table getTable() {
+ return table;
+ }
+
+ public void setTable(Table table) {
+ this.table = table;
+ getSuperclass().addSubclassTable(table);
+ }
+
+ public java.util.Set getSynchronizedTables() {
+ return synchronizedTables;
+ }
+
+ protected Iterator getNonDuplicatedPropertyIterator() {
+ return getPropertyClosureIterator();
+ }
+
+ public void validate(Mapping mapping) throws MappingException {
+ super.validate(mapping);
+ if ( key!=null && !key.isValid(mapping) ) {
+ throw new MappingException(
+ "subclass key mapping has wrong number of columns: " +
+ getEntityName() +
+ " type: " +
+ key.getType().getName()
+ );
+ }
+ }
+
+ public Table getIdentityTable() {
+ return getTable();
+ }
+
+ public Object accept(PersistentClassVisitor mv) {
+ return mv.accept(this);
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/UniqueKey.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/UniqueKey.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/UniqueKey.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,82 @@
+//$Id: UniqueKey.java 10661 2006-10-31 02:19:13Z epbernard $
+package org.hibernate.mapping;
+
+import java.util.Iterator;
+
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.Mapping;
+import org.hibernate.util.StringHelper;
+
+/**
+ * A relational unique key constraint
+ *
+ * @author Gavin King
+ */
+public class UniqueKey extends Constraint {
+
+ public String sqlConstraintString(Dialect dialect) {
+ StringBuffer buf = new StringBuffer( "unique (" );
+ Iterator iter = getColumnIterator();
+ boolean nullable = false;
+ while ( iter.hasNext() ) {
+ Column column = (Column) iter.next();
+ if ( !nullable && column.isNullable() ) nullable = true;
+ buf.append( column.getQuotedName( dialect ) );
+ if ( iter.hasNext() ) buf.append( ", " );
+ }
+ //do not add unique constraint on DB not supporting unique and nullable columns
+ return !nullable || dialect.supportsNotNullUnique() ?
+ buf.append( ')' ).toString() :
+ null;
+ }
+
+ public String sqlConstraintString(Dialect dialect, String constraintName, String defaultCatalog,
+ String defaultSchema) {
+ StringBuffer buf = new StringBuffer(
+ dialect.getAddPrimaryKeyConstraintString( constraintName )
+ ).append( '(' );
+ Iterator iter = getColumnIterator();
+ boolean nullable = false;
+ while ( iter.hasNext() ) {
+ Column column = (Column) iter.next();
+ if ( !nullable && column.isNullable() ) nullable = true;
+ buf.append( column.getQuotedName( dialect ) );
+ if ( iter.hasNext() ) buf.append( ", " );
+ }
+ return !nullable || dialect.supportsNotNullUnique() ?
+ StringHelper.replace( buf.append( ')' ).toString(), "primary key", "unique" ) :
+ //TODO: improve this hack!
+ null;
+ }
+
+ public String sqlCreateString(Dialect dialect, Mapping p, String defaultCatalog, String defaultSchema) {
+ if ( dialect.supportsUniqueConstraintInCreateAlterTable() ) {
+ return super.sqlCreateString( dialect, p, defaultCatalog, defaultSchema );
+ }
+ else {
+ return Index.buildSqlCreateIndexString( dialect, getName(), getTable(), getColumnIterator(), true,
+ defaultCatalog, defaultSchema );
+ }
+ }
+
+ public String sqlDropString(Dialect dialect, String defaultCatalog, String defaultSchema) {
+ if ( dialect.supportsUniqueConstraintInCreateAlterTable() ) {
+ return super.sqlDropString( dialect, defaultCatalog, defaultSchema );
+ }
+ else {
+ return Index.buildSqlDropIndexString( dialect, getTable(), getName(), defaultCatalog, defaultSchema );
+ }
+ }
+
+ public boolean isGenerated(Dialect dialect) {
+ if ( dialect.supportsNotNullUnique() ) return true;
+ Iterator iter = getColumnIterator();
+ while ( iter.hasNext() ) {
+ if ( ( (Column) iter.next() ).isNullable() ) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Value.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Value.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/Value.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,37 @@
+//$Id: Value.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate.mapping;
+
+import java.io.Serializable;
+import java.util.Iterator;
+
+import org.hibernate.FetchMode;
+import org.hibernate.MappingException;
+import org.hibernate.engine.Mapping;
+import org.hibernate.type.Type;
+
+/**
+ * A value is anything that is persisted by value, instead of
+ * by reference. It is essentially a Hibernate Type, together
+ * with zero or more columns. Values are wrapped by things with
+ * higher level semantics, for example properties, collections,
+ * classes.
+ *
+ * @author Gavin King
+ */
+public interface Value extends Serializable {
+ public int getColumnSpan();
+ public Iterator getColumnIterator();
+ public Type getType() throws MappingException;
+ public FetchMode getFetchMode();
+ public Table getTable();
+ public boolean hasFormula();
+ public boolean isAlternateUniqueKey();
+ public boolean isNullable();
+ public boolean[] getColumnUpdateability();
+ public boolean[] getColumnInsertability();
+ public void createForeignKey() throws MappingException;
+ public boolean isSimpleValue();
+ public boolean isValid(Mapping mapping) throws MappingException;
+ public void setTypeUsingReflection(String className, String propertyName) throws MappingException;
+ public Object accept(ValueVisitor visitor);
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/ValueVisitor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/ValueVisitor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/ValueVisitor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,63 @@
+/*
+ * Created on 06-Dec-2004
+ *
+ */
+package org.hibernate.mapping;
+
+/**
+ * @author max
+ *
+ */
+public interface ValueVisitor {
+
+ /**
+ * @param bag
+ */
+ Object accept(Bag bag);
+
+ /**
+ * @param bag
+ */
+ Object accept(IdentifierBag bag);
+
+ /**
+ * @param list
+ */
+ Object accept(List list);
+
+ Object accept(PrimitiveArray primitiveArray);
+ Object accept(Array list);
+
+ /**
+ * @param map
+ */
+ Object accept(Map map);
+
+ /**
+ * @param many
+ */
+ Object accept(OneToMany many);
+
+ /**
+ * @param set
+ */
+ Object accept(Set set);
+
+ /**
+ * @param any
+ */
+ Object accept(Any any);
+
+ /**
+ * @param value
+ */
+ Object accept(SimpleValue value);
+ Object accept(DependantValue value);
+
+ Object accept(Component component);
+
+ Object accept(ManyToOne mto);
+ Object accept(OneToOne oto);
+
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,8 @@
+<html>
+<head></head>
+<body>
+<p>
+ This package defines the Hibernate configuration-time metamodel.
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/mapping/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/metadata/ClassMetadata.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/metadata/ClassMetadata.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/metadata/ClassMetadata.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,186 @@
+//$Id: ClassMetadata.java 7516 2005-07-16 22:20:48Z oneovthafew $
+package org.hibernate.metadata;
+
+import java.io.Serializable;
+import java.util.Map;
+
+import org.hibernate.HibernateException;
+import org.hibernate.EntityMode;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.type.Type;
+
+/**
+ * Exposes entity class metadata to the application
+ *
+ * @see org.hibernate.SessionFactory#getClassMetadata(Class)
+ * @author Gavin King
+ */
+public interface ClassMetadata {
+
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ // stuff that is persister-centric and/or EntityInfo-centric ~~~~~~~~~~~~~~
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * The name of the entity
+ */
+ public String getEntityName();
+
+ /**
+ * Get the name of the identifier property (or return null)
+ */
+ public String getIdentifierPropertyName();
+
+ /**
+ * Get the names of the class' persistent properties
+ */
+ public String[] getPropertyNames();
+
+ /**
+ * Get the identifier Hibernate type
+ */
+ public Type getIdentifierType();
+
+ /**
+ * Get the Hibernate types of the class properties
+ */
+ public Type[] getPropertyTypes();
+
+ /**
+ * Get the type of a particular (named) property
+ */
+ public Type getPropertyType(String propertyName) throws HibernateException;
+
+ /**
+ * Does this class support dynamic proxies?
+ */
+ public boolean hasProxy();
+
+ /**
+ * Are instances of this class mutable?
+ */
+ public boolean isMutable();
+
+ /**
+ * Are instances of this class versioned by a timestamp or version number column?
+ */
+ public boolean isVersioned();
+
+ /**
+ * Get the index of the version property
+ */
+ public int getVersionProperty();
+
+ /**
+ * Get the nullability of the class' persistent properties
+ */
+ public boolean[] getPropertyNullability();
+
+
+ /**
+ * Get the "laziness" of the properties of this class
+ */
+ public boolean[] getPropertyLaziness();
+
+ /**
+ * Does this class have an identifier property?
+ */
+ public boolean hasIdentifierProperty();
+
+ /**
+ * Does this entity declare a natural id?
+ */
+ public boolean hasNaturalIdentifier();
+
+ /**
+ * Which properties hold the natural id?
+ */
+ public int[] getNaturalIdentifierProperties();
+
+ /**
+ * Does this entity have mapped subclasses?
+ */
+ public boolean hasSubclasses();
+
+ /**
+ * Does this entity extend a mapped superclass?
+ */
+ public boolean isInherited();
+
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ // stuff that is tuplizer-centric, but is passed a session ~~~~~~~~~~~~~~~~
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Return the values of the mapped properties of the object
+ */
+ public Object[] getPropertyValuesToInsert(Object entity, Map mergeMap, SessionImplementor session)
+ throws HibernateException;
+
+
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ // stuff that is Tuplizer-centric ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * The persistent class, or null
+ */
+ public Class getMappedClass(EntityMode entityMode);
+
+ /**
+ * Create a class instance initialized with the given identifier
+ */
+ public Object instantiate(Serializable id, EntityMode entityMode) throws HibernateException;
+
+ /**
+ * Get the value of a particular (named) property
+ */
+ public Object getPropertyValue(Object object, String propertyName, EntityMode entityMode) throws HibernateException;
+
+ /**
+ * Extract the property values from the given entity.
+ *
+ * @param entity The entity from which to extract the property values.
+ * @param entityMode The entity-mode of the given entity
+ * @return The property values.
+ * @throws HibernateException
+ */
+ public Object[] getPropertyValues(Object entity, EntityMode entityMode) throws HibernateException;
+
+ /**
+ * Set the value of a particular (named) property
+ */
+ public void setPropertyValue(Object object, String propertyName, Object value, EntityMode entityMode) throws HibernateException;
+
+ /**
+ * Set the given values to the mapped properties of the given object
+ */
+ public void setPropertyValues(Object object, Object[] values, EntityMode entityMode) throws HibernateException;
+
+ /**
+ * Get the identifier of an instance (throw an exception if no identifier property)
+ */
+ public Serializable getIdentifier(Object entity, EntityMode entityMode) throws HibernateException;
+
+ /**
+ * Set the identifier of an instance (or do nothing if no identifier property)
+ */
+ public void setIdentifier(Object object, Serializable id, EntityMode entityMode) throws HibernateException;
+
+ /**
+ * Does the class implement the <tt>Lifecycle</tt> interface?
+ */
+ public boolean implementsLifecycle(EntityMode entityMode);
+
+ /**
+ * Does the class implement the <tt>Validatable</tt> interface?
+ */
+ public boolean implementsValidatable(EntityMode entityMode);
+
+ /**
+ * Get the version number (or timestamp) from the object's version property
+ * (or return null if not versioned)
+ */
+ public Object getVersion(Object object, EntityMode entityMode) throws HibernateException;
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/metadata/CollectionMetadata.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/metadata/CollectionMetadata.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/metadata/CollectionMetadata.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,50 @@
+//$Id: CollectionMetadata.java 3890 2004-06-03 16:31:32Z steveebersole $
+package org.hibernate.metadata;
+
+import org.hibernate.type.Type;
+
+/**
+ * Exposes collection metadata to the application
+ *
+ * @author Gavin King
+ */
+public interface CollectionMetadata {
+ /**
+ * The collection key type
+ */
+ public Type getKeyType();
+ /**
+ * The collection element type
+ */
+ public Type getElementType();
+ /**
+ * The collection index type (or null if the collection has no index)
+ */
+ public Type getIndexType();
+ /**
+ * Is this collection indexed?
+ */
+ public boolean hasIndex();
+ /**
+ * The name of this collection role
+ */
+ public String getRole();
+ /**
+ * Is the collection an array?
+ */
+ public boolean isArray();
+ /**
+ * Is the collection a primitive array?
+ */
+ public boolean isPrimitiveArray();
+ /**
+ * Is the collection lazily initialized?
+ */
+ public boolean isLazy();
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/metadata/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/metadata/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/metadata/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,9 @@
+<html>
+<head></head>
+<body>
+<p>
+ This package defines an API for accessing the Hibernate
+ runtime metamodel.
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/metadata/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,8 @@
+<html>
+<head></head>
+<body>
+<p>
+ This package defines the central Hibernate APIs.
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/param/AbstractExplicitParameterSpecification.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/param/AbstractExplicitParameterSpecification.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/param/AbstractExplicitParameterSpecification.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,36 @@
+package org.hibernate.param;
+
+import org.hibernate.type.Type;
+
+/**
+ * Convenience base class for explicitly defined query parameters.
+ *
+ * @author <a href="mailto:steve at hibernate.org">Steve Ebersole </a>
+ */
+public abstract class AbstractExplicitParameterSpecification implements ExplicitParameterSpecification {
+
+ private final int sourceLine;
+ private final int sourceColumn;
+ private Type expectedType;
+
+ protected AbstractExplicitParameterSpecification(int sourceLine, int sourceColumn) {
+ this.sourceLine = sourceLine;
+ this.sourceColumn = sourceColumn;
+ }
+
+ public int getSourceLine() {
+ return sourceLine;
+ }
+
+ public int getSourceColumn() {
+ return sourceColumn;
+ }
+
+ public Type getExpectedType() {
+ return expectedType;
+ }
+
+ public void setExpectedType(Type expectedType) {
+ this.expectedType = expectedType;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/param/CollectionFilterKeyParameterSpecification.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/param/CollectionFilterKeyParameterSpecification.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/param/CollectionFilterKeyParameterSpecification.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,57 @@
+package org.hibernate.param;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.type.Type;
+
+/**
+ * A specialized ParameterSpecification impl for dealing with a collection-key
+ * as part of a collection filter compilation.
+ *
+ * @author Steve Ebersole
+ */
+public class CollectionFilterKeyParameterSpecification implements ParameterSpecification {
+
+ private final String collectionRole;
+ private final Type keyType;
+ private final int queryParameterPosition;
+
+ /**
+ * Creates a specialized collection-filter collection-key parameter spec.
+ *
+ * @param collectionRole The collection role being filtered.
+ * @param keyType The mapped collection-key type.
+ * @param queryParameterPosition The position within {@link org.hibernate.engine.QueryParameters} where
+ * we can find the appropriate param value to bind.
+ */
+ public CollectionFilterKeyParameterSpecification(String collectionRole, Type keyType, int queryParameterPosition) {
+ this.collectionRole = collectionRole;
+ this.keyType = keyType;
+ this.queryParameterPosition = queryParameterPosition;
+ }
+
+ public int bind(
+ PreparedStatement statement,
+ QueryParameters qp,
+ SessionImplementor session,
+ int position) throws SQLException {
+ Object value = qp.getPositionalParameterValues()[queryParameterPosition];
+ keyType.nullSafeSet( statement, value, position, session );
+ return keyType.getColumnSpan( session.getFactory() );
+ }
+
+ public Type getExpectedType() {
+ return keyType;
+ }
+
+ public void setExpectedType(Type expectedType) {
+ // todo : throw exception?
+ }
+
+ public String renderDisplayInfo() {
+ return "collection-filter-key=" + collectionRole;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/param/DynamicFilterParameterSpecification.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/param/DynamicFilterParameterSpecification.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/param/DynamicFilterParameterSpecification.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,60 @@
+package org.hibernate.param;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.type.Type;
+
+/**
+ * A specialized ParameterSpecification impl for dealing with a dynamic filter
+ * parameters.
+ * <p/>
+ * Note: this class is currently not used. The ideal way to deal with dynamic filter
+ * parameters for HQL would be to track them just as we do with other parameters
+ * in the translator. However, the problem with that is that we currently do not
+ * know the filters which actually apply to the query; we know the active/enabled ones,
+ * but not the ones that actually "make it into" the resulting query.
+ *
+ * @author Steve Ebersole
+ */
+public class DynamicFilterParameterSpecification implements ParameterSpecification {
+ private final String filterName;
+ private final String parameterName;
+ private final Type definedParameterType;
+ private final int queryParameterPosition;
+
+ public DynamicFilterParameterSpecification(
+ String filterName,
+ String parameterName,
+ Type definedParameterType,
+ int queryParameterPosition) {
+ this.filterName = filterName;
+ this.parameterName = parameterName;
+ this.definedParameterType = definedParameterType;
+ this.queryParameterPosition = queryParameterPosition;
+ }
+
+ public int bind(
+ PreparedStatement statement,
+ QueryParameters qp,
+ SessionImplementor session,
+ int position) throws SQLException {
+ Object value = qp.getFilteredPositionalParameterValues()[queryParameterPosition];
+ definedParameterType.nullSafeSet( statement, value, position, session );
+ return definedParameterType.getColumnSpan( session.getFactory() );
+ }
+
+ public Type getExpectedType() {
+ return definedParameterType;
+ }
+
+ public void setExpectedType(Type expectedType) {
+ // todo : throw exception?
+ }
+
+ public String renderDisplayInfo() {
+ return "dynamic-filter={filterName=" + filterName + ",paramName=" + parameterName + "}";
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/param/ExplicitParameterSpecification.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/param/ExplicitParameterSpecification.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/param/ExplicitParameterSpecification.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,13 @@
+package org.hibernate.param;
+
+/**
+ * An additional contract for parameters which originate from
+ * parameters explicitly encountered in the source statement
+ * (HQL or native-SQL).
+ *
+ * @author <a href="mailto:steve at hibernate.org">Steve Ebersole </a>
+ */
+public interface ExplicitParameterSpecification extends ParameterSpecification {
+ public int getSourceLine();
+ public int getSourceColumn();
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/param/NamedParameterSpecification.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/param/NamedParameterSpecification.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/param/NamedParameterSpecification.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,49 @@
+// $Id: NamedParameterSpecification.java 8513 2005-11-02 18:47:40Z steveebersole $
+package org.hibernate.param;
+
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.TypedValue;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+/**
+ * Relates to an explicit query named-parameter.
+ *
+ * @author Steve Ebersole
+ */
+public class NamedParameterSpecification extends AbstractExplicitParameterSpecification implements ParameterSpecification {
+
+ private final String name;
+
+ public NamedParameterSpecification(int sourceLine, int sourceColumn, String name) {
+ super( sourceLine, sourceColumn );
+ this.name = name;
+ }
+
+ /**
+ * Bind the appropriate value into the given statement at the specified position.
+ *
+ * @param statement The statement into which the value should be bound.
+ * @param qp The defined values for the current query execution.
+ * @param session The session against which the current execution is occuring.
+ * @param position The position from which to start binding value(s).
+ *
+ * @return The number of sql bind positions "eaten" by this bind operation.
+ */
+ public int bind(PreparedStatement statement, QueryParameters qp, SessionImplementor session, int position)
+ throws SQLException {
+ TypedValue typedValue = ( TypedValue ) qp.getNamedParameters().get( name );
+ typedValue.getType().nullSafeSet( statement, typedValue.getValue(), position, session );
+ return typedValue.getType().getColumnSpan( session.getFactory() );
+ }
+
+ public String renderDisplayInfo() {
+ return "name=" + name + ", expectedType=" + getExpectedType();
+ }
+
+ public String getName() {
+ return name;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/param/ParameterSpecification.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/param/ParameterSpecification.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/param/ParameterSpecification.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,52 @@
+// $Id: ParameterSpecification.java 10765 2006-11-08 04:30:27Z steve.ebersole at jboss.com $
+package org.hibernate.param;
+
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.type.Type;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+/**
+ * Maintains information relating to parameters which need to get bound into a
+ * JDBC {@link PreparedStatement}.
+ *
+ * @author Steve Ebersole
+ */
+public interface ParameterSpecification {
+ /**
+ * Bind the appropriate value into the given statement at the specified position.
+ *
+ * @param statement The statement into which the value should be bound.
+ * @param qp The defined values for the current query execution.
+ * @param session The session against which the current execution is occuring.
+ * @param position The position from which to start binding value(s).
+ *
+ * @return The number of sql bind positions "eaten" by this bind operation.
+ * @throws java.sql.SQLException Indicates problems performing the JDBC biind operation.
+ */
+ public int bind(PreparedStatement statement, QueryParameters qp, SessionImplementor session, int position) throws SQLException;
+
+ /**
+ * Get the type which we are expeting for a bind into this parameter based
+ * on translated contextual information.
+ *
+ * @return The expected type.
+ */
+ public Type getExpectedType();
+
+ /**
+ * Injects the expected type. Called during translation.
+ *
+ * @param expectedType The type to expect.
+ */
+ public void setExpectedType(Type expectedType);
+
+ /**
+ * Render this parameter into displayable info (for logging, etc).
+ *
+ * @return The displayable info.
+ */
+ public String renderDisplayInfo();
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/param/PositionalParameterSpecification.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/param/PositionalParameterSpecification.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/param/PositionalParameterSpecification.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,50 @@
+// $Id: PositionalParameterSpecification.java 8513 2005-11-02 18:47:40Z steveebersole $
+package org.hibernate.param;
+
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.type.Type;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+/**
+ * Relates to an explicit query positional (or ordinal) parameter.
+ *
+ * @author Steve Ebersole
+ */
+public class PositionalParameterSpecification extends AbstractExplicitParameterSpecification implements ParameterSpecification {
+
+ private final int hqlPosition;
+
+ public PositionalParameterSpecification(int sourceLine, int sourceColumn, int hqlPosition) {
+ super( sourceLine, sourceColumn );
+ this.hqlPosition = hqlPosition;
+ }
+
+ /**
+ * Bind the appropriate value into the given statement at the specified position.
+ *
+ * @param statement The statement into which the value should be bound.
+ * @param qp The defined values for the current query execution.
+ * @param session The session against which the current execution is occuring.
+ * @param position The position from which to start binding value(s).
+ *
+ * @return The number of sql bind positions "eaten" by this bind operation.
+ */
+ public int bind(PreparedStatement statement, QueryParameters qp, SessionImplementor session, int position) throws SQLException {
+ Type type = qp.getPositionalParameterTypes()[hqlPosition];
+ Object value = qp.getPositionalParameterValues()[hqlPosition];
+
+ type.nullSafeSet( statement, value, position, session );
+ return type.getColumnSpan( session.getFactory() );
+ }
+
+ public String renderDisplayInfo() {
+ return "ordinal=" + hqlPosition + ", expectedType=" + getExpectedType();
+ }
+
+ public int getHqlPosition() {
+ return hqlPosition;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/param/VersionTypeSeedParameterSpecification.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/param/VersionTypeSeedParameterSpecification.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/param/VersionTypeSeedParameterSpecification.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,46 @@
+// $Id: VersionTypeSeedParameterSpecification.java 8513 2005-11-02 18:47:40Z steveebersole $
+package org.hibernate.param;
+
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.type.VersionType;
+import org.hibernate.type.Type;
+import org.hibernate.param.ParameterSpecification;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+/**
+ * Implementation of VersionTypeSeedParameterSpecification.
+ *
+ * @author Steve Ebersole
+ */
+public class VersionTypeSeedParameterSpecification implements ParameterSpecification {
+
+ private VersionType type;
+
+ public VersionTypeSeedParameterSpecification(VersionType type) {
+ this.type = type;
+ }
+
+ /**
+ * @see org.hibernate.param.ParameterSpecification#bind
+ */
+ public int bind(PreparedStatement statement, QueryParameters qp, SessionImplementor session, int position)
+ throws SQLException {
+ type.nullSafeSet( statement, type.seed( session ), position, session );
+ return 1;
+ }
+
+ public Type getExpectedType() {
+ return type;
+ }
+
+ public void setExpectedType(Type expectedType) {
+ // expected type is intrinsic here...
+ }
+
+ public String renderDisplayInfo() {
+ return "version-seed, type=" + type;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/persister/PersisterFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/persister/PersisterFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/persister/PersisterFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,154 @@
+//$Id: PersisterFactory.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.persister;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.cache.access.EntityRegionAccessStrategy;
+import org.hibernate.cache.access.CollectionRegionAccessStrategy;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.engine.Mapping;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.mapping.Collection;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.persister.collection.BasicCollectionPersister;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.persister.collection.OneToManyPersister;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.persister.entity.JoinedSubclassEntityPersister;
+import org.hibernate.persister.entity.SingleTableEntityPersister;
+import org.hibernate.persister.entity.UnionSubclassEntityPersister;
+
+/**
+ * Factory for <tt>EntityPersister</tt> and <tt>CollectionPersister</tt> instances
+ *
+ * @author Gavin King
+ */
+public final class PersisterFactory {
+
+ //TODO: make EntityPersister *not* depend on SessionFactoryImplementor
+ //interface, if possible
+
+ // TODO : still need to make CollectionPersisters EntityMode-aware
+
+ private PersisterFactory() {}
+
+ private static final Class[] PERSISTER_CONSTRUCTOR_ARGS = new Class[] {
+ PersistentClass.class, EntityRegionAccessStrategy.class, SessionFactoryImplementor.class, Mapping.class
+ };
+
+ // TODO: is it really neceassry to provide Configuration to CollectionPersisters ? Should it not be enough with associated class ?
+ // or why does EntityPersister's not get access to configuration ?
+ //
+ // The only reason I could see that Configuration gets passed to collection persisters
+ // is so that they can look up the dom4j node name of the entity element in case
+ // no explicit node name was applied at the collection element level. Are you kidding me?
+ // Trivial to fix then. Just store and expose the node name on the entity persister
+ // (which the collection persister looks up anyway via other means...).
+ private static final Class[] COLLECTION_PERSISTER_CONSTRUCTOR_ARGS = new Class[] {
+ Collection.class, CollectionRegionAccessStrategy.class, Configuration.class, SessionFactoryImplementor.class
+ };
+
+ public static EntityPersister createClassPersister(
+ PersistentClass model,
+ EntityRegionAccessStrategy cacheAccessStrategy,
+ SessionFactoryImplementor factory,
+ Mapping cfg) throws HibernateException {
+ Class persisterClass = model.getEntityPersisterClass();
+ if ( persisterClass == null || persisterClass == SingleTableEntityPersister.class ) {
+ return new SingleTableEntityPersister( model, cacheAccessStrategy, factory, cfg );
+ }
+ else if ( persisterClass == JoinedSubclassEntityPersister.class ) {
+ return new JoinedSubclassEntityPersister( model, cacheAccessStrategy, factory, cfg );
+ }
+ else if ( persisterClass == UnionSubclassEntityPersister.class ) {
+ return new UnionSubclassEntityPersister( model, cacheAccessStrategy, factory, cfg );
+ }
+ else {
+ return create( persisterClass, model, cacheAccessStrategy, factory, cfg );
+ }
+ }
+
+ public static CollectionPersister createCollectionPersister(
+ Configuration cfg,
+ Collection model,
+ CollectionRegionAccessStrategy cacheAccessStrategy,
+ SessionFactoryImplementor factory) throws HibernateException {
+ Class persisterClass = model.getCollectionPersisterClass();
+ if ( persisterClass == null ) {
+ return model.isOneToMany()
+ ? ( CollectionPersister ) new OneToManyPersister( model, cacheAccessStrategy, cfg, factory )
+ : ( CollectionPersister ) new BasicCollectionPersister( model, cacheAccessStrategy, cfg, factory );
+ }
+ else {
+ return create( persisterClass, cfg, model, cacheAccessStrategy, factory );
+ }
+
+ }
+
+ private static EntityPersister create(
+ Class persisterClass,
+ PersistentClass model,
+ EntityRegionAccessStrategy cacheAccessStrategy,
+ SessionFactoryImplementor factory,
+ Mapping cfg) throws HibernateException {
+ Constructor pc;
+ try {
+ pc = persisterClass.getConstructor( PERSISTER_CONSTRUCTOR_ARGS );
+ }
+ catch ( Exception e ) {
+ throw new MappingException( "Could not get constructor for " + persisterClass.getName(), e );
+ }
+
+ try {
+ return (EntityPersister) pc.newInstance( new Object[] { model, cacheAccessStrategy, factory, cfg } );
+ }
+ catch (InvocationTargetException ite) {
+ Throwable e = ite.getTargetException();
+ if (e instanceof HibernateException) {
+ throw (HibernateException) e;
+ }
+ else {
+ throw new MappingException( "Could not instantiate persister " + persisterClass.getName(), e );
+ }
+ }
+ catch (Exception e) {
+ throw new MappingException( "Could not instantiate persister " + persisterClass.getName(), e );
+ }
+ }
+
+ private static CollectionPersister create(
+ Class persisterClass,
+ Configuration cfg,
+ Collection model,
+ CollectionRegionAccessStrategy cacheAccessStrategy,
+ SessionFactoryImplementor factory) throws HibernateException {
+ Constructor pc;
+ try {
+ pc = persisterClass.getConstructor( COLLECTION_PERSISTER_CONSTRUCTOR_ARGS );
+ }
+ catch (Exception e) {
+ throw new MappingException( "Could not get constructor for " + persisterClass.getName(), e );
+ }
+
+ try {
+ return (CollectionPersister) pc.newInstance( new Object[] { model, cacheAccessStrategy, cfg, factory } );
+ }
+ catch (InvocationTargetException ite) {
+ Throwable e = ite.getTargetException();
+ if (e instanceof HibernateException) {
+ throw (HibernateException) e;
+ }
+ else {
+ throw new MappingException( "Could not instantiate collection persister " + persisterClass.getName(), e );
+ }
+ }
+ catch (Exception e) {
+ throw new MappingException( "Could not instantiate collection persister " + persisterClass.getName(), e );
+ }
+ }
+
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1784 @@
+//$Id: AbstractCollectionPersister.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.persister.collection;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.AssertionFailure;
+import org.hibernate.FetchMode;
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.QueryException;
+import org.hibernate.TransientObjectException;
+import org.hibernate.jdbc.Expectation;
+import org.hibernate.jdbc.Expectations;
+import org.hibernate.cache.CacheException;
+import org.hibernate.cache.access.CollectionRegionAccessStrategy;
+import org.hibernate.cache.entry.CacheEntryStructure;
+import org.hibernate.cache.entry.StructuredCollectionCacheEntry;
+import org.hibernate.cache.entry.StructuredMapCacheEntry;
+import org.hibernate.cache.entry.UnstructuredCacheEntry;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.EntityKey;
+import org.hibernate.engine.PersistenceContext;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.SubselectFetch;
+import org.hibernate.engine.ExecuteUpdateResultCheckStyle;
+import org.hibernate.exception.JDBCExceptionHelper;
+import org.hibernate.exception.SQLExceptionConverter;
+import org.hibernate.id.IdentifierGenerator;
+import org.hibernate.loader.collection.CollectionInitializer;
+import org.hibernate.mapping.Collection;
+import org.hibernate.mapping.Column;
+import org.hibernate.mapping.Formula;
+import org.hibernate.mapping.IdentifierCollection;
+import org.hibernate.mapping.IndexedCollection;
+import org.hibernate.mapping.List;
+import org.hibernate.mapping.Selectable;
+import org.hibernate.mapping.Table;
+import org.hibernate.metadata.CollectionMetadata;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.persister.entity.Loadable;
+import org.hibernate.persister.entity.PropertyMapping;
+import org.hibernate.pretty.MessageHelper;
+import org.hibernate.sql.Alias;
+import org.hibernate.sql.SelectFragment;
+import org.hibernate.sql.SimpleSelect;
+import org.hibernate.sql.Template;
+import org.hibernate.type.AbstractComponentType;
+import org.hibernate.type.CollectionType;
+import org.hibernate.type.EntityType;
+import org.hibernate.type.Type;
+import org.hibernate.util.ArrayHelper;
+import org.hibernate.util.CollectionHelper;
+import org.hibernate.util.FilterHelper;
+import org.hibernate.util.StringHelper;
+
+
+/**
+ * Base implementation of the <tt>QueryableCollection</tt> interface.
+ *
+ * @author Gavin King
+ * @see BasicCollectionPersister
+ * @see OneToManyPersister
+ */
+public abstract class AbstractCollectionPersister
+ implements CollectionMetadata, SQLLoadableCollection {
+ // TODO: encapsulate the protected instance variables!
+
+ private final String role;
+
+ //SQL statements
+ private final String sqlDeleteString;
+ private final String sqlInsertRowString;
+ private final String sqlUpdateRowString;
+ private final String sqlDeleteRowString;
+ private final String sqlSelectSizeString;
+ private final String sqlSelectRowByIndexString;
+ private final String sqlDetectRowByIndexString;
+ private final String sqlDetectRowByElementString;
+
+ private final String sqlOrderByString;
+ protected final String sqlWhereString;
+ private final String sqlOrderByStringTemplate;
+ private final String sqlWhereStringTemplate;
+ private final boolean hasOrder;
+ protected final boolean hasWhere;
+ private final int baseIndex;
+
+ private final String nodeName;
+ private final String elementNodeName;
+ private final String indexNodeName;
+
+ protected final boolean indexContainsFormula;
+ protected final boolean elementIsPureFormula;
+
+ //types
+ private final Type keyType;
+ private final Type indexType;
+ protected final Type elementType;
+ private final Type identifierType;
+
+ //columns
+ protected final String[] keyColumnNames;
+ protected final String[] indexColumnNames;
+ protected final String[] indexFormulaTemplates;
+ protected final String[] indexFormulas;
+ protected final boolean[] indexColumnIsSettable;
+ protected final String[] elementColumnNames;
+ protected final String[] elementFormulaTemplates;
+ protected final String[] elementFormulas;
+ protected final boolean[] elementColumnIsSettable;
+ protected final boolean[] elementColumnIsInPrimaryKey;
+ protected final String[] indexColumnAliases;
+ protected final String[] elementColumnAliases;
+ protected final String[] keyColumnAliases;
+
+ protected final String identifierColumnName;
+ private final String identifierColumnAlias;
+ //private final String unquotedIdentifierColumnName;
+
+ protected final String qualifiedTableName;
+
+ private final String queryLoaderName;
+
+ private final boolean isPrimitiveArray;
+ private final boolean isArray;
+ protected final boolean hasIndex;
+ protected final boolean hasIdentifier;
+ private final boolean isLazy;
+ private final boolean isExtraLazy;
+ private final boolean isInverse;
+ private final boolean isMutable;
+ private final boolean isVersioned;
+ protected final int batchSize;
+ private final FetchMode fetchMode;
+ private final boolean hasOrphanDelete;
+ private final boolean subselectLoadable;
+
+ //extra information about the element type
+ private final Class elementClass;
+ private final String entityName;
+
+ private final Dialect dialect;
+ private final SQLExceptionConverter sqlExceptionConverter;
+ private final SessionFactoryImplementor factory;
+ private final EntityPersister ownerPersister;
+ private final IdentifierGenerator identifierGenerator;
+ private final PropertyMapping elementPropertyMapping;
+ private final EntityPersister elementPersister;
+ private final CollectionRegionAccessStrategy cacheAccessStrategy;
+ private final CollectionType collectionType;
+ private CollectionInitializer initializer;
+
+ private final CacheEntryStructure cacheEntryStructure;
+
+ // dynamic filters for the collection
+ private final FilterHelper filterHelper;
+
+ // dynamic filters specifically for many-to-many inside the collection
+ private final FilterHelper manyToManyFilterHelper;
+
+ private final String manyToManyWhereString;
+ private final String manyToManyWhereTemplate;
+
+ private final String manyToManyOrderByString;
+ private final String manyToManyOrderByTemplate;
+
+ // custom sql
+ private final boolean insertCallable;
+ private final boolean updateCallable;
+ private final boolean deleteCallable;
+ private final boolean deleteAllCallable;
+ private ExecuteUpdateResultCheckStyle insertCheckStyle;
+ private ExecuteUpdateResultCheckStyle updateCheckStyle;
+ private ExecuteUpdateResultCheckStyle deleteCheckStyle;
+ private ExecuteUpdateResultCheckStyle deleteAllCheckStyle;
+
+ private final Serializable[] spaces;
+
+ private Map collectionPropertyColumnAliases = new HashMap();
+ private Map collectionPropertyColumnNames = new HashMap();
+
+ private static final Log log = LogFactory.getLog( AbstractCollectionPersister.class );
+
+ public AbstractCollectionPersister(
+ final Collection collection,
+ final CollectionRegionAccessStrategy cacheAccessStrategy,
+ final Configuration cfg,
+ final SessionFactoryImplementor factory) throws MappingException, CacheException {
+
+ this.factory = factory;
+ this.cacheAccessStrategy = cacheAccessStrategy;
+ if ( factory.getSettings().isStructuredCacheEntriesEnabled() ) {
+ cacheEntryStructure = collection.isMap() ?
+ ( CacheEntryStructure ) new StructuredMapCacheEntry() :
+ ( CacheEntryStructure ) new StructuredCollectionCacheEntry();
+ }
+ else {
+ cacheEntryStructure = new UnstructuredCacheEntry();
+ }
+
+ dialect = factory.getDialect();
+ sqlExceptionConverter = factory.getSQLExceptionConverter();
+ collectionType = collection.getCollectionType();
+ role = collection.getRole();
+ entityName = collection.getOwnerEntityName();
+ ownerPersister = factory.getEntityPersister(entityName);
+ queryLoaderName = collection.getLoaderName();
+ nodeName = collection.getNodeName();
+ isMutable = collection.isMutable();
+
+ Table table = collection.getCollectionTable();
+ fetchMode = collection.getElement().getFetchMode();
+ elementType = collection.getElement().getType();
+ //isSet = collection.isSet();
+ //isSorted = collection.isSorted();
+ isPrimitiveArray = collection.isPrimitiveArray();
+ isArray = collection.isArray();
+ subselectLoadable = collection.isSubselectLoadable();
+
+ qualifiedTableName = table.getQualifiedName(
+ dialect,
+ factory.getSettings().getDefaultCatalogName(),
+ factory.getSettings().getDefaultSchemaName()
+ );
+
+ int spacesSize = 1 + collection.getSynchronizedTables().size();
+ spaces = new String[spacesSize];
+ spaces[0] = qualifiedTableName;
+ Iterator iter = collection.getSynchronizedTables().iterator();
+ for ( int i = 1; i < spacesSize; i++ ) {
+ spaces[i] = (String) iter.next();
+ }
+
+ sqlOrderByString = collection.getOrderBy();
+ hasOrder = sqlOrderByString != null;
+ sqlOrderByStringTemplate = hasOrder ?
+ Template.renderOrderByStringTemplate(sqlOrderByString, dialect, factory.getSqlFunctionRegistry()) :
+ null;
+ sqlWhereString = StringHelper.isNotEmpty( collection.getWhere() ) ? "( " + collection.getWhere() + ") " : null;
+ hasWhere = sqlWhereString != null;
+ sqlWhereStringTemplate = hasWhere ?
+ Template.renderWhereStringTemplate(sqlWhereString, dialect, factory.getSqlFunctionRegistry()) :
+ null;
+
+ hasOrphanDelete = collection.hasOrphanDelete();
+
+ int batch = collection.getBatchSize();
+ if ( batch == -1 ) {
+ batch = factory.getSettings().getDefaultBatchFetchSize();
+ }
+ batchSize = batch;
+
+ isVersioned = collection.isOptimisticLocked();
+
+ // KEY
+
+ keyType = collection.getKey().getType();
+ iter = collection.getKey().getColumnIterator();
+ int keySpan = collection.getKey().getColumnSpan();
+ keyColumnNames = new String[keySpan];
+ keyColumnAliases = new String[keySpan];
+ int k = 0;
+ while ( iter.hasNext() ) {
+ // NativeSQL: collect key column and auto-aliases
+ Column col = ( (Column) iter.next() );
+ keyColumnNames[k] = col.getQuotedName(dialect);
+ keyColumnAliases[k] = col.getAlias(dialect);
+ k++;
+ }
+
+ //unquotedKeyColumnNames = StringHelper.unQuote(keyColumnAliases);
+
+ //ELEMENT
+
+ String elemNode = collection.getElementNodeName();
+ if ( elementType.isEntityType() ) {
+ String entityName = ( (EntityType) elementType ).getAssociatedEntityName();
+ elementPersister = factory.getEntityPersister(entityName);
+ if ( elemNode==null ) {
+ elemNode = cfg.getClassMapping(entityName).getNodeName();
+ }
+ // NativeSQL: collect element column and auto-aliases
+
+ }
+ else {
+ elementPersister = null;
+ }
+ elementNodeName = elemNode;
+
+ int elementSpan = collection.getElement().getColumnSpan();
+ elementColumnAliases = new String[elementSpan];
+ elementColumnNames = new String[elementSpan];
+ elementFormulaTemplates = new String[elementSpan];
+ elementFormulas = new String[elementSpan];
+ elementColumnIsSettable = new boolean[elementSpan];
+ elementColumnIsInPrimaryKey = new boolean[elementSpan];
+ boolean isPureFormula = true;
+ boolean hasNotNullableColumns = false;
+ int j = 0;
+ iter = collection.getElement().getColumnIterator();
+ while ( iter.hasNext() ) {
+ Selectable selectable = (Selectable) iter.next();
+ elementColumnAliases[j] = selectable.getAlias(dialect);
+ if ( selectable.isFormula() ) {
+ Formula form = (Formula) selectable;
+ elementFormulaTemplates[j] = form.getTemplate(dialect, factory.getSqlFunctionRegistry());
+ elementFormulas[j] = form.getFormula();
+ }
+ else {
+ Column col = (Column) selectable;
+ elementColumnNames[j] = col.getQuotedName(dialect);
+ elementColumnIsSettable[j] = true;
+ elementColumnIsInPrimaryKey[j] = !col.isNullable();
+ if ( !col.isNullable() ) {
+ hasNotNullableColumns = true;
+ }
+ isPureFormula = false;
+ }
+ j++;
+ }
+ elementIsPureFormula = isPureFormula;
+
+ //workaround, for backward compatibility of sets with no
+ //not-null columns, assume all columns are used in the
+ //row locator SQL
+ if ( !hasNotNullableColumns ) {
+ Arrays.fill( elementColumnIsInPrimaryKey, true );
+ }
+
+
+ // INDEX AND ROW SELECT
+
+ hasIndex = collection.isIndexed();
+ if (hasIndex) {
+ // NativeSQL: collect index column and auto-aliases
+ IndexedCollection indexedCollection = (IndexedCollection) collection;
+ indexType = indexedCollection.getIndex().getType();
+ int indexSpan = indexedCollection.getIndex().getColumnSpan();
+ iter = indexedCollection.getIndex().getColumnIterator();
+ indexColumnNames = new String[indexSpan];
+ indexFormulaTemplates = new String[indexSpan];
+ indexFormulas = new String[indexSpan];
+ indexColumnIsSettable = new boolean[indexSpan];
+ indexColumnAliases = new String[indexSpan];
+ int i = 0;
+ boolean hasFormula = false;
+ while ( iter.hasNext() ) {
+ Selectable s = (Selectable) iter.next();
+ indexColumnAliases[i] = s.getAlias(dialect);
+ if ( s.isFormula() ) {
+ Formula indexForm = (Formula) s;
+ indexFormulaTemplates[i] = indexForm.getTemplate(dialect, factory.getSqlFunctionRegistry());
+ indexFormulas[i] = indexForm.getFormula();
+ hasFormula = true;
+ }
+ else {
+ Column indexCol = (Column) s;
+ indexColumnNames[i] = indexCol.getQuotedName(dialect);
+ indexColumnIsSettable[i] = true;
+ }
+ i++;
+ }
+ indexContainsFormula = hasFormula;
+ baseIndex = indexedCollection.isList() ?
+ ( (List) indexedCollection ).getBaseIndex() : 0;
+
+ indexNodeName = indexedCollection.getIndexNodeName();
+
+ }
+ else {
+ indexContainsFormula = false;
+ indexColumnIsSettable = null;
+ indexFormulaTemplates = null;
+ indexFormulas = null;
+ indexType = null;
+ indexColumnNames = null;
+ indexColumnAliases = null;
+ baseIndex = 0;
+ indexNodeName = null;
+ }
+
+ hasIdentifier = collection.isIdentified();
+ if (hasIdentifier) {
+ if ( collection.isOneToMany() ) {
+ throw new MappingException( "one-to-many collections with identifiers are not supported" );
+ }
+ IdentifierCollection idColl = (IdentifierCollection) collection;
+ identifierType = idColl.getIdentifier().getType();
+ iter = idColl.getIdentifier().getColumnIterator();
+ Column col = ( Column ) iter.next();
+ identifierColumnName = col.getQuotedName(dialect);
+ identifierColumnAlias = col.getAlias(dialect);
+ //unquotedIdentifierColumnName = identifierColumnAlias;
+ identifierGenerator = idColl.getIdentifier().createIdentifierGenerator(
+ factory.getDialect(),
+ factory.getSettings().getDefaultCatalogName(),
+ factory.getSettings().getDefaultSchemaName(),
+ null
+ );
+ }
+ else {
+ identifierType = null;
+ identifierColumnName = null;
+ identifierColumnAlias = null;
+ //unquotedIdentifierColumnName = null;
+ identifierGenerator = null;
+ }
+
+ //GENERATE THE SQL:
+
+ //sqlSelectString = sqlSelectString();
+ //sqlSelectRowString = sqlSelectRowString();
+
+ if ( collection.getCustomSQLInsert() == null ) {
+ sqlInsertRowString = generateInsertRowString();
+ insertCallable = false;
+ insertCheckStyle = ExecuteUpdateResultCheckStyle.COUNT;
+ }
+ else {
+ sqlInsertRowString = collection.getCustomSQLInsert();
+ insertCallable = collection.isCustomInsertCallable();
+ insertCheckStyle = collection.getCustomSQLInsertCheckStyle() == null
+ ? ExecuteUpdateResultCheckStyle.determineDefault( collection.getCustomSQLInsert(), insertCallable )
+ : collection.getCustomSQLInsertCheckStyle();
+ }
+
+ if ( collection.getCustomSQLUpdate() == null ) {
+ sqlUpdateRowString = generateUpdateRowString();
+ updateCallable = false;
+ updateCheckStyle = ExecuteUpdateResultCheckStyle.COUNT;
+ }
+ else {
+ sqlUpdateRowString = collection.getCustomSQLUpdate();
+ updateCallable = collection.isCustomUpdateCallable();
+ updateCheckStyle = collection.getCustomSQLUpdateCheckStyle() == null
+ ? ExecuteUpdateResultCheckStyle.determineDefault( collection.getCustomSQLUpdate(), insertCallable )
+ : collection.getCustomSQLUpdateCheckStyle();
+ }
+
+ if ( collection.getCustomSQLDelete() == null ) {
+ sqlDeleteRowString = generateDeleteRowString();
+ deleteCallable = false;
+ deleteCheckStyle = ExecuteUpdateResultCheckStyle.NONE;
+ }
+ else {
+ sqlDeleteRowString = collection.getCustomSQLDelete();
+ deleteCallable = collection.isCustomDeleteCallable();
+ deleteCheckStyle = ExecuteUpdateResultCheckStyle.NONE;
+ }
+
+ if ( collection.getCustomSQLDeleteAll() == null ) {
+ sqlDeleteString = generateDeleteString();
+ deleteAllCallable = false;
+ deleteAllCheckStyle = ExecuteUpdateResultCheckStyle.NONE;
+ }
+ else {
+ sqlDeleteString = collection.getCustomSQLDeleteAll();
+ deleteAllCallable = collection.isCustomDeleteAllCallable();
+ deleteAllCheckStyle = ExecuteUpdateResultCheckStyle.NONE;
+ }
+
+ sqlSelectSizeString = generateSelectSizeString( collection.isIndexed() && !collection.isMap() );
+ sqlDetectRowByIndexString = generateDetectRowByIndexString();
+ sqlDetectRowByElementString = generateDetectRowByElementString();
+ sqlSelectRowByIndexString = generateSelectRowByIndexString();
+
+ logStaticSQL();
+
+ isLazy = collection.isLazy();
+ isExtraLazy = collection.isExtraLazy();
+
+ isInverse = collection.isInverse();
+
+ if ( collection.isArray() ) {
+ elementClass = ( (org.hibernate.mapping.Array) collection ).getElementClass();
+ }
+ else {
+ // for non-arrays, we don't need to know the element class
+ elementClass = null; //elementType.returnedClass();
+ }
+
+ if ( elementType.isComponentType() ) {
+ elementPropertyMapping = new CompositeElementPropertyMapping(
+ elementColumnNames,
+ elementFormulaTemplates,
+ (AbstractComponentType) elementType,
+ factory
+ );
+ }
+ else if ( !elementType.isEntityType() ) {
+ elementPropertyMapping = new ElementPropertyMapping(
+ elementColumnNames,
+ elementType
+ );
+ }
+ else {
+ if ( elementPersister instanceof PropertyMapping ) { //not all classpersisters implement PropertyMapping!
+ elementPropertyMapping = (PropertyMapping) elementPersister;
+ }
+ else {
+ elementPropertyMapping = new ElementPropertyMapping(
+ elementColumnNames,
+ elementType
+ );
+ }
+ }
+
+ // Handle any filters applied to this collection
+ filterHelper = new FilterHelper( collection.getFilterMap(), dialect, factory.getSqlFunctionRegistry() );
+
+ // Handle any filters applied to this collection for many-to-many
+ manyToManyFilterHelper = new FilterHelper( collection.getManyToManyFilterMap(), dialect, factory.getSqlFunctionRegistry() );
+ manyToManyWhereString = StringHelper.isNotEmpty( collection.getManyToManyWhere() ) ?
+ "( " + collection.getManyToManyWhere() + ")" :
+ null;
+ manyToManyWhereTemplate = manyToManyWhereString == null ?
+ null :
+ Template.renderWhereStringTemplate( manyToManyWhereString, factory.getDialect(), factory.getSqlFunctionRegistry() );
+ manyToManyOrderByString = collection.getManyToManyOrdering();
+ manyToManyOrderByTemplate = manyToManyOrderByString == null
+ ? null
+ : Template.renderOrderByStringTemplate( manyToManyOrderByString, factory.getDialect(), factory.getSqlFunctionRegistry() );
+
+ initCollectionPropertyMap();
+ }
+
+ public void postInstantiate() throws MappingException {
+ initializer = queryLoaderName == null ?
+ createCollectionInitializer( CollectionHelper.EMPTY_MAP ) :
+ new NamedQueryCollectionInitializer( queryLoaderName, this );
+ }
+
+ protected void logStaticSQL() {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "Static SQL for collection: " + getRole() );
+ if ( getSQLInsertRowString() != null ) {
+ log.debug( " Row insert: " + getSQLInsertRowString() );
+ }
+ if ( getSQLUpdateRowString() != null ) {
+ log.debug( " Row update: " + getSQLUpdateRowString() );
+ }
+ if ( getSQLDeleteRowString() != null ) {
+ log.debug( " Row delete: " + getSQLDeleteRowString() );
+ }
+ if ( getSQLDeleteString() != null ) {
+ log.debug( " One-shot delete: " + getSQLDeleteString() );
+ }
+ }
+ }
+
+ public void initialize(Serializable key, SessionImplementor session) throws HibernateException {
+ getAppropriateInitializer( key, session ).initialize( key, session );
+ }
+
+ protected CollectionInitializer getAppropriateInitializer(Serializable key, SessionImplementor session) {
+ if ( queryLoaderName != null ) {
+ //if there is a user-specified loader, return that
+ //TODO: filters!?
+ return initializer;
+ }
+ CollectionInitializer subselectInitializer = getSubselectInitializer( key, session );
+ if ( subselectInitializer != null ) {
+ return subselectInitializer;
+ }
+ else if ( session.getEnabledFilters().isEmpty() ) {
+ return initializer;
+ }
+ else {
+ return createCollectionInitializer( session.getEnabledFilters() );
+ }
+ }
+
+ private CollectionInitializer getSubselectInitializer(Serializable key, SessionImplementor session) {
+
+ if ( !isSubselectLoadable() ) {
+ return null;
+ }
+
+ final PersistenceContext persistenceContext = session.getPersistenceContext();
+
+ SubselectFetch subselect = persistenceContext.getBatchFetchQueue()
+ .getSubselect( new EntityKey( key, getOwnerEntityPersister(), session.getEntityMode() ) );
+
+ if (subselect == null) {
+ return null;
+ }
+ else {
+
+ // Take care of any entities that might have
+ // been evicted!
+ Iterator iter = subselect.getResult().iterator();
+ while ( iter.hasNext() ) {
+ if ( !persistenceContext.containsEntity( (EntityKey) iter.next() ) ) {
+ iter.remove();
+ }
+ }
+
+ // Run a subquery loader
+ return createSubselectInitializer( subselect, session );
+ }
+ }
+
+ protected abstract CollectionInitializer createSubselectInitializer(SubselectFetch subselect, SessionImplementor session);
+
+ protected abstract CollectionInitializer createCollectionInitializer(Map enabledFilters)
+ throws MappingException;
+
+ public CollectionRegionAccessStrategy getCacheAccessStrategy() {
+ return cacheAccessStrategy;
+ }
+
+ public boolean hasCache() {
+ return cacheAccessStrategy != null;
+ }
+
+ public CollectionType getCollectionType() {
+ return collectionType;
+ }
+
+ protected String getSQLWhereString(String alias) {
+ return StringHelper.replace( sqlWhereStringTemplate, Template.TEMPLATE, alias );
+ }
+
+ public String getSQLOrderByString(String alias) {
+ return hasOrdering() ?
+ StringHelper.replace( sqlOrderByStringTemplate, Template.TEMPLATE, alias ) : "";
+ }
+
+ public String getManyToManyOrderByString(String alias) {
+ if ( isManyToMany() && manyToManyOrderByString != null ) {
+ return StringHelper.replace( manyToManyOrderByTemplate, Template.TEMPLATE, alias );
+ }
+ else {
+ return "";
+ }
+ }
+ public FetchMode getFetchMode() {
+ return fetchMode;
+ }
+
+ public boolean hasOrdering() {
+ return hasOrder;
+ }
+
+ public boolean hasManyToManyOrdering() {
+ return isManyToMany() && manyToManyOrderByTemplate != null;
+ }
+
+ public boolean hasWhere() {
+ return hasWhere;
+ }
+
+ protected String getSQLDeleteString() {
+ return sqlDeleteString;
+ }
+
+ protected String getSQLInsertRowString() {
+ return sqlInsertRowString;
+ }
+
+ protected String getSQLUpdateRowString() {
+ return sqlUpdateRowString;
+ }
+
+ protected String getSQLDeleteRowString() {
+ return sqlDeleteRowString;
+ }
+
+ public Type getKeyType() {
+ return keyType;
+ }
+
+ public Type getIndexType() {
+ return indexType;
+ }
+
+ public Type getElementType() {
+ return elementType;
+ }
+
+ /**
+ * Return the element class of an array, or null otherwise
+ */
+ public Class getElementClass() { //needed by arrays
+ return elementClass;
+ }
+
+ public Object readElement(ResultSet rs, Object owner, String[] aliases, SessionImplementor session)
+ throws HibernateException, SQLException {
+ return getElementType().nullSafeGet( rs, aliases, session, owner );
+ }
+
+ public Object readIndex(ResultSet rs, String[] aliases, SessionImplementor session)
+ throws HibernateException, SQLException {
+ Object index = getIndexType().nullSafeGet( rs, aliases, session, null );
+ if ( index == null ) {
+ throw new HibernateException( "null index column for collection: " + role );
+ }
+ index = decrementIndexByBase( index );
+ return index;
+ }
+
+ protected Object decrementIndexByBase(Object index) {
+ if (baseIndex!=0) {
+ index = new Integer( ( (Integer) index ).intValue() - baseIndex );
+ }
+ return index;
+ }
+
+ public Object readIdentifier(ResultSet rs, String alias, SessionImplementor session)
+ throws HibernateException, SQLException {
+ Object id = getIdentifierType().nullSafeGet( rs, alias, session, null );
+ if ( id == null ) {
+ throw new HibernateException( "null identifier column for collection: " + role );
+ }
+ return id;
+ }
+
+ public Object readKey(ResultSet rs, String[] aliases, SessionImplementor session)
+ throws HibernateException, SQLException {
+ return getKeyType().nullSafeGet( rs, aliases, session, null );
+ }
+
+ /**
+ * Write the key to a JDBC <tt>PreparedStatement</tt>
+ */
+ protected int writeKey(PreparedStatement st, Serializable key, int i, SessionImplementor session)
+ throws HibernateException, SQLException {
+
+ if ( key == null ) {
+ throw new NullPointerException( "null key for collection: " + role ); //an assertion
+ }
+ getKeyType().nullSafeSet( st, key, i, session );
+ return i + keyColumnAliases.length;
+ }
+
+ /**
+ * Write the element to a JDBC <tt>PreparedStatement</tt>
+ */
+ protected int writeElement(PreparedStatement st, Object elt, int i, SessionImplementor session)
+ throws HibernateException, SQLException {
+ getElementType().nullSafeSet(st, elt, i, elementColumnIsSettable, session);
+ return i + ArrayHelper.countTrue(elementColumnIsSettable);
+
+ }
+
+ /**
+ * Write the index to a JDBC <tt>PreparedStatement</tt>
+ */
+ protected int writeIndex(PreparedStatement st, Object index, int i, SessionImplementor session)
+ throws HibernateException, SQLException {
+ getIndexType().nullSafeSet( st, incrementIndexByBase(index), i, indexColumnIsSettable, session );
+ return i + ArrayHelper.countTrue(indexColumnIsSettable);
+ }
+
+ protected Object incrementIndexByBase(Object index) {
+ if (baseIndex!=0) {
+ index = new Integer( ( (Integer) index ).intValue() + baseIndex );
+ }
+ return index;
+ }
+
+ /**
+ * Write the element to a JDBC <tt>PreparedStatement</tt>
+ */
+ protected int writeElementToWhere(PreparedStatement st, Object elt, int i, SessionImplementor session)
+ throws HibernateException, SQLException {
+ if (elementIsPureFormula) {
+ throw new AssertionFailure("cannot use a formula-based element in the where condition");
+ }
+ getElementType().nullSafeSet(st, elt, i, elementColumnIsInPrimaryKey, session);
+ return i + elementColumnAliases.length;
+
+ }
+
+ /**
+ * Write the index to a JDBC <tt>PreparedStatement</tt>
+ */
+ protected int writeIndexToWhere(PreparedStatement st, Object index, int i, SessionImplementor session)
+ throws HibernateException, SQLException {
+ if (indexContainsFormula) {
+ throw new AssertionFailure("cannot use a formula-based index in the where condition");
+ }
+ getIndexType().nullSafeSet( st, incrementIndexByBase(index), i, session );
+ return i + indexColumnAliases.length;
+ }
+
+ /**
+ * Write the identifier to a JDBC <tt>PreparedStatement</tt>
+ */
+ public int writeIdentifier(PreparedStatement st, Object id, int i, SessionImplementor session)
+ throws HibernateException, SQLException {
+
+ getIdentifierType().nullSafeSet( st, id, i, session );
+ return i + 1;
+ }
+
+ public boolean isPrimitiveArray() {
+ return isPrimitiveArray;
+ }
+
+ public boolean isArray() {
+ return isArray;
+ }
+
+ public String[] getKeyColumnAliases(String suffix) {
+ return new Alias( suffix ).toAliasStrings( keyColumnAliases );
+ }
+
+ public String[] getElementColumnAliases(String suffix) {
+ return new Alias( suffix ).toAliasStrings( elementColumnAliases );
+ }
+
+ public String[] getIndexColumnAliases(String suffix) {
+ if ( hasIndex ) {
+ return new Alias( suffix ).toAliasStrings( indexColumnAliases );
+ }
+ else {
+ return null;
+ }
+ }
+
+ public String getIdentifierColumnAlias(String suffix) {
+ if ( hasIdentifier ) {
+ return new Alias( suffix ).toAliasString( identifierColumnAlias );
+ }
+ else {
+ return null;
+ }
+ }
+
+ public String getIdentifierColumnName() {
+ if ( hasIdentifier ) {
+ return identifierColumnName;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Generate a list of collection index, key and element columns
+ */
+ public String selectFragment(String alias, String columnSuffix) {
+ SelectFragment frag = generateSelectFragment( alias, columnSuffix );
+ appendElementColumns( frag, alias );
+ appendIndexColumns( frag, alias );
+ appendIdentifierColumns( frag, alias );
+
+ return frag.toFragmentString()
+ .substring( 2 ); //strip leading ','
+ }
+
+ protected String generateSelectSizeString(boolean isIntegerIndexed) {
+ String selectValue = isIntegerIndexed ?
+ "max(" + getIndexColumnNames()[0] + ") + 1": //lists, arrays
+ "count(" + getElementColumnNames()[0] + ")"; //sets, maps, bags
+ return new SimpleSelect(dialect)
+ .setTableName( getTableName() )
+ .addCondition( getKeyColumnNames(), "=?" )
+ .addColumn(selectValue)
+ .toStatementString();
+ }
+
+ protected String generateDetectRowByIndexString() {
+ if ( !hasIndex() ) {
+ return null;
+ }
+ return new SimpleSelect(dialect)
+ .setTableName( getTableName() )
+ .addCondition( getKeyColumnNames(), "=?" )
+ .addCondition( getIndexColumnNames(), "=?" )
+ .addCondition( indexFormulas, "=?" )
+ .addColumn("1")
+ .toStatementString();
+ }
+
+ protected String generateSelectRowByIndexString() {
+ if ( !hasIndex() ) {
+ return null;
+ }
+ return new SimpleSelect(dialect)
+ .setTableName( getTableName() )
+ .addCondition( getKeyColumnNames(), "=?" )
+ .addCondition( getIndexColumnNames(), "=?" )
+ .addCondition( indexFormulas, "=?" )
+ .addColumns( getElementColumnNames(), elementColumnAliases )
+ .addColumns( indexFormulas, indexColumnAliases )
+ .toStatementString();
+ }
+
+ protected String generateDetectRowByElementString() {
+ return new SimpleSelect(dialect)
+ .setTableName( getTableName() )
+ .addCondition( getKeyColumnNames(), "=?" )
+ .addCondition( getElementColumnNames(), "=?" )
+ .addCondition( elementFormulas, "=?" )
+ .addColumn("1")
+ .toStatementString();
+ }
+
+ protected SelectFragment generateSelectFragment(String alias, String columnSuffix) {
+ return new SelectFragment()
+ .setSuffix( columnSuffix )
+ .addColumns( alias, keyColumnNames, keyColumnAliases );
+ }
+
+ protected void appendElementColumns(SelectFragment frag, String elemAlias) {
+ for ( int i=0; i<elementColumnIsSettable.length; i++ ) {
+ if ( elementColumnIsSettable[i] ) {
+ frag.addColumn( elemAlias, elementColumnNames[i], elementColumnAliases[i] );
+ }
+ else {
+ frag.addFormula( elemAlias, elementFormulaTemplates[i], elementColumnAliases[i] );
+ }
+ }
+ }
+
+ protected void appendIndexColumns(SelectFragment frag, String alias) {
+ if ( hasIndex ) {
+ for ( int i=0; i<indexColumnIsSettable.length; i++ ) {
+ if ( indexColumnIsSettable[i] ) {
+ frag.addColumn( alias, indexColumnNames[i], indexColumnAliases[i] );
+ }
+ else {
+ frag.addFormula( alias, indexFormulaTemplates[i], indexColumnAliases[i] );
+ }
+ }
+ }
+ }
+
+ protected void appendIdentifierColumns(SelectFragment frag, String alias) {
+ if ( hasIdentifier ) {
+ frag.addColumn( alias, identifierColumnName, identifierColumnAlias );
+ }
+ }
+
+ public String[] getIndexColumnNames() {
+ return indexColumnNames;
+ }
+
+ public String[] getIndexFormulas() {
+ return indexFormulas;
+ }
+
+ public String[] getIndexColumnNames(String alias) {
+ return qualify(alias, indexColumnNames, indexFormulaTemplates);
+
+ }
+
+ public String[] getElementColumnNames(String alias) {
+ return qualify(alias, elementColumnNames, elementFormulaTemplates);
+ }
+
+ private static String[] qualify(String alias, String[] columnNames, String[] formulaTemplates) {
+ int span = columnNames.length;
+ String[] result = new String[span];
+ for (int i=0; i<span; i++) {
+ if ( columnNames[i]==null ) {
+ result[i] = StringHelper.replace( formulaTemplates[i], Template.TEMPLATE, alias );
+ }
+ else {
+ result[i] = StringHelper.qualify( alias, columnNames[i] );
+ }
+ }
+ return result;
+ }
+
+ public String[] getElementColumnNames() {
+ return elementColumnNames; //TODO: something with formulas...
+ }
+
+ public String[] getKeyColumnNames() {
+ return keyColumnNames;
+ }
+
+ public boolean hasIndex() {
+ return hasIndex;
+ }
+
+ public boolean isLazy() {
+ return isLazy;
+ }
+
+ public boolean isInverse() {
+ return isInverse;
+ }
+
+ public String getTableName() {
+ return qualifiedTableName;
+ }
+
+ public void remove(Serializable id, SessionImplementor session) throws HibernateException {
+
+ if ( !isInverse && isRowDeleteEnabled() ) {
+
+ if ( log.isDebugEnabled() ) {
+ log.debug(
+ "Deleting collection: " +
+ MessageHelper.collectionInfoString( this, id, getFactory() )
+ );
+ }
+
+ // Remove all the old entries
+
+ try {
+ int offset = 1;
+ PreparedStatement st = null;
+ Expectation expectation = Expectations.appropriateExpectation( getDeleteAllCheckStyle() );
+ boolean callable = isDeleteAllCallable();
+ boolean useBatch = expectation.canBeBatched();
+ String sql = getSQLDeleteString();
+ if ( useBatch ) {
+ if ( callable ) {
+ st = session.getBatcher().prepareBatchCallableStatement( sql );
+ }
+ else {
+ st = session.getBatcher().prepareBatchStatement( sql );
+ }
+ }
+ else {
+ if ( callable ) {
+ st = session.getBatcher().prepareCallableStatement( sql );
+ }
+ else {
+ st = session.getBatcher().prepareStatement( sql );
+ }
+ }
+
+
+ try {
+ offset+= expectation.prepare( st );
+
+ writeKey( st, id, offset, session );
+ if ( useBatch ) {
+ session.getBatcher().addToBatch( expectation );
+ }
+ else {
+ expectation.verifyOutcome( st.executeUpdate(), st, -1 );
+ }
+ }
+ catch ( SQLException sqle ) {
+ if ( useBatch ) {
+ session.getBatcher().abortBatch( sqle );
+ }
+ throw sqle;
+ }
+ finally {
+ if ( !useBatch ) {
+ session.getBatcher().closeStatement( st );
+ }
+ }
+
+ if ( log.isDebugEnabled() ) {
+ log.debug( "done deleting collection" );
+ }
+ }
+ catch ( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ sqlExceptionConverter,
+ sqle,
+ "could not delete collection: " +
+ MessageHelper.collectionInfoString( this, id, getFactory() ),
+ getSQLDeleteString()
+ );
+ }
+
+ }
+
+ }
+
+ public void recreate(PersistentCollection collection, Serializable id, SessionImplementor session)
+ throws HibernateException {
+
+ if ( !isInverse && isRowInsertEnabled() ) {
+
+ if ( log.isDebugEnabled() ) {
+ log.debug(
+ "Inserting collection: " +
+ MessageHelper.collectionInfoString( this, id, getFactory() )
+ );
+ }
+
+ try {
+ //create all the new entries
+ Iterator entries = collection.entries(this);
+ if ( entries.hasNext() ) {
+ collection.preInsert( this );
+ int i = 0;
+ int count = 0;
+ while ( entries.hasNext() ) {
+
+ final Object entry = entries.next();
+ if ( collection.entryExists( entry, i ) ) {
+ int offset = 1;
+ PreparedStatement st = null;
+ Expectation expectation = Expectations.appropriateExpectation( getInsertCheckStyle() );
+ boolean callable = isInsertCallable();
+ boolean useBatch = expectation.canBeBatched();
+ String sql = getSQLInsertRowString();
+
+ if ( useBatch ) {
+ if ( callable ) {
+ st = session.getBatcher().prepareBatchCallableStatement( sql );
+ }
+ else {
+ st = session.getBatcher().prepareBatchStatement( sql );
+ }
+ }
+ else {
+ if ( callable ) {
+ st = session.getBatcher().prepareCallableStatement( sql );
+ }
+ else {
+ st = session.getBatcher().prepareStatement( sql );
+ }
+ }
+
+
+ try {
+ offset+= expectation.prepare( st );
+
+ //TODO: copy/paste from insertRows()
+ int loc = writeKey( st, id, offset, session );
+ if ( hasIdentifier ) {
+ loc = writeIdentifier( st, collection.getIdentifier(entry, i), loc, session );
+ }
+ if ( hasIndex /*&& !indexIsFormula*/ ) {
+ loc = writeIndex( st, collection.getIndex(entry, i, this), loc, session );
+ }
+ loc = writeElement(st, collection.getElement(entry), loc, session );
+
+ if ( useBatch ) {
+ session.getBatcher().addToBatch( expectation );
+ }
+ else {
+ expectation.verifyOutcome( st.executeUpdate(), st, -1 );
+ }
+
+ collection.afterRowInsert( this, entry, i );
+ count++;
+ }
+ catch ( SQLException sqle ) {
+ if ( useBatch ) {
+ session.getBatcher().abortBatch( sqle );
+ }
+ throw sqle;
+ }
+ finally {
+ if ( !useBatch ) {
+ session.getBatcher().closeStatement( st );
+ }
+ }
+
+ }
+ i++;
+ }
+
+ if ( log.isDebugEnabled() ) {
+ log.debug( "done inserting collection: " + count + " rows inserted" );
+ }
+
+ }
+ else {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "collection was empty" );
+ }
+ }
+ }
+ catch ( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ sqlExceptionConverter,
+ sqle,
+ "could not insert collection: " +
+ MessageHelper.collectionInfoString( this, id, getFactory() ),
+ getSQLInsertRowString()
+ );
+ }
+ }
+ }
+
+ protected boolean isRowDeleteEnabled() {
+ return true;
+ }
+
+ public void deleteRows(PersistentCollection collection, Serializable id, SessionImplementor session)
+ throws HibernateException {
+
+ if ( !isInverse && isRowDeleteEnabled() ) {
+
+ if ( log.isDebugEnabled() ) {
+ log.debug(
+ "Deleting rows of collection: " +
+ MessageHelper.collectionInfoString( this, id, getFactory() )
+ );
+ }
+
+ boolean deleteByIndex = !isOneToMany() && hasIndex && !indexContainsFormula;
+
+ try {
+ //delete all the deleted entries
+ Iterator deletes = collection.getDeletes( this, !deleteByIndex );
+ if ( deletes.hasNext() ) {
+ int offset = 1;
+ int count = 0;
+ while ( deletes.hasNext() ) {
+ PreparedStatement st = null;
+ Expectation expectation = Expectations.appropriateExpectation( getDeleteCheckStyle() );
+ boolean callable = isDeleteCallable();
+ boolean useBatch = expectation.canBeBatched();
+ String sql = getSQLDeleteRowString();
+
+ if ( useBatch ) {
+ if ( callable ) {
+ st = session.getBatcher().prepareBatchCallableStatement( sql );
+ }
+ else {
+ st = session.getBatcher().prepareBatchStatement( sql );
+ }
+ }
+ else {
+ if ( callable ) {
+ st = session.getBatcher().prepareCallableStatement( sql );
+ }
+ else {
+ st = session.getBatcher().prepareStatement( sql );
+ }
+ }
+
+ try {
+ expectation.prepare( st );
+
+ Object entry = deletes.next();
+ int loc = offset;
+ if ( hasIdentifier ) {
+ writeIdentifier( st, entry, loc, session );
+ }
+ else {
+ loc = writeKey( st, id, loc, session );
+ if ( deleteByIndex ) {
+ writeIndexToWhere( st, entry, loc, session );
+ }
+ else {
+ writeElementToWhere( st, entry, loc, session );
+ }
+ }
+
+ if ( useBatch ) {
+ session.getBatcher().addToBatch( expectation );
+ }
+ else {
+ expectation.verifyOutcome( st.executeUpdate(), st, -1 );
+ }
+ count++;
+ }
+ catch ( SQLException sqle ) {
+ if ( useBatch ) {
+ session.getBatcher().abortBatch( sqle );
+ }
+ throw sqle;
+ }
+ finally {
+ if ( !useBatch ) {
+ session.getBatcher().closeStatement( st );
+ }
+ }
+
+ if ( log.isDebugEnabled() ) {
+ log.debug( "done deleting collection rows: " + count + " deleted" );
+ }
+ }
+ }
+ else {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "no rows to delete" );
+ }
+ }
+ }
+ catch ( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ sqlExceptionConverter,
+ sqle,
+ "could not delete collection rows: " +
+ MessageHelper.collectionInfoString( this, id, getFactory() ),
+ getSQLDeleteRowString()
+ );
+ }
+ }
+ }
+
+ protected boolean isRowInsertEnabled() {
+ return true;
+ }
+
+ public void insertRows(PersistentCollection collection, Serializable id, SessionImplementor session)
+ throws HibernateException {
+
+ if ( !isInverse && isRowInsertEnabled() ) {
+
+ if ( log.isDebugEnabled() ) {
+ log.debug(
+ "Inserting rows of collection: " +
+ MessageHelper.collectionInfoString( this, id, getFactory() )
+ );
+ }
+
+ try {
+ //insert all the new entries
+ collection.preInsert( this );
+ Iterator entries = collection.entries( this );
+ Expectation expectation = Expectations.appropriateExpectation( getInsertCheckStyle() );
+ boolean callable = isInsertCallable();
+ boolean useBatch = expectation.canBeBatched();
+ String sql = getSQLInsertRowString();
+ int i = 0;
+ int count = 0;
+ while ( entries.hasNext() ) {
+ int offset = 1;
+ Object entry = entries.next();
+ PreparedStatement st = null;
+ if ( collection.needsInserting( entry, i, elementType ) ) {
+
+ if ( useBatch ) {
+ if ( st == null ) {
+ if ( callable ) {
+ st = session.getBatcher().prepareBatchCallableStatement( sql );
+ }
+ else {
+ st = session.getBatcher().prepareBatchStatement( sql );
+ }
+ }
+ }
+ else {
+ if ( callable ) {
+ st = session.getBatcher().prepareCallableStatement( sql );
+ }
+ else {
+ st = session.getBatcher().prepareStatement( sql );
+ }
+ }
+
+ try {
+ offset += expectation.prepare( st );
+ //TODO: copy/paste from recreate()
+ offset = writeKey( st, id, offset, session );
+ if ( hasIdentifier ) {
+ offset = writeIdentifier( st, collection.getIdentifier(entry, i), offset, session );
+ }
+ if ( hasIndex /*&& !indexIsFormula*/ ) {
+ offset = writeIndex( st, collection.getIndex(entry, i, this), offset, session );
+ }
+ writeElement(st, collection.getElement(entry), offset, session );
+
+ if ( useBatch ) {
+ session.getBatcher().addToBatch( expectation );
+ }
+ else {
+ expectation.verifyOutcome( st.executeUpdate(), st, -1 );
+ }
+ collection.afterRowInsert( this, entry, i );
+ count++;
+ }
+ catch ( SQLException sqle ) {
+ if ( useBatch ) {
+ session.getBatcher().abortBatch( sqle );
+ }
+ throw sqle;
+ }
+ finally {
+ if ( !useBatch ) {
+ session.getBatcher().closeStatement( st );
+ }
+ }
+ }
+ i++;
+ }
+ if ( log.isDebugEnabled() ) {
+ log.debug( "done inserting rows: " + count + " inserted" );
+ }
+ }
+ catch ( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ sqlExceptionConverter,
+ sqle,
+ "could not insert collection rows: " +
+ MessageHelper.collectionInfoString( this, id, getFactory() ),
+ getSQLInsertRowString()
+ );
+ }
+
+ }
+ }
+
+
+ public String getRole() {
+ return role;
+ }
+
+ public String getOwnerEntityName() {
+ return entityName;
+ }
+
+ public EntityPersister getOwnerEntityPersister() {
+ return ownerPersister;
+ }
+
+ public IdentifierGenerator getIdentifierGenerator() {
+ return identifierGenerator;
+ }
+
+ public Type getIdentifierType() {
+ return identifierType;
+ }
+
+ public boolean hasOrphanDelete() {
+ return hasOrphanDelete;
+ }
+
+ public Type toType(String propertyName) throws QueryException {
+ if ( "index".equals( propertyName ) ) {
+ return indexType;
+ }
+ return elementPropertyMapping.toType( propertyName );
+ }
+
+ public abstract boolean isManyToMany();
+
+ public String getManyToManyFilterFragment(String alias, Map enabledFilters) {
+ StringBuffer buffer = new StringBuffer();
+ manyToManyFilterHelper.render( buffer, alias, enabledFilters );
+
+ if ( manyToManyWhereString != null ) {
+ buffer.append( " and " )
+ .append( StringHelper.replace( manyToManyWhereTemplate, Template.TEMPLATE, alias ) );
+ }
+
+ return buffer.toString();
+ }
+
+ public String[] toColumns(String alias, String propertyName)
+ throws QueryException {
+
+ if ( "index".equals( propertyName ) ) {
+ if ( isManyToMany() ) {
+ throw new QueryException( "index() function not supported for many-to-many association" );
+ }
+ return StringHelper.qualify( alias, indexColumnNames );
+ }
+
+ return elementPropertyMapping.toColumns( alias, propertyName );
+ }
+
+ public String[] toColumns(String propertyName)
+ throws QueryException {
+
+ if ( "index".equals( propertyName ) ) {
+ if ( isManyToMany() ) {
+ throw new QueryException( "index() function not supported for many-to-many association" );
+ }
+ return indexColumnNames;
+ }
+
+ return elementPropertyMapping.toColumns( propertyName );
+ }
+
+ public Type getType() {
+ return elementPropertyMapping.getType(); //==elementType ??
+ }
+
+ public String getName() {
+ return getRole();
+ }
+
+ public EntityPersister getElementPersister() {
+ if ( elementPersister == null ) {
+ throw new AssertionFailure( "not an association" );
+ }
+ return ( Loadable ) elementPersister;
+ }
+
+ public boolean isCollection() {
+ return true;
+ }
+
+ public Serializable[] getCollectionSpaces() {
+ return spaces;
+ }
+
+ protected abstract String generateDeleteString();
+
+ protected abstract String generateDeleteRowString();
+
+ protected abstract String generateUpdateRowString();
+
+ protected abstract String generateInsertRowString();
+
+ public void updateRows(PersistentCollection collection, Serializable id, SessionImplementor session)
+ throws HibernateException {
+
+ if ( !isInverse && collection.isRowUpdatePossible() ) {
+
+ if ( log.isDebugEnabled() ) {
+ log.debug( "Updating rows of collection: " + role + "#" + id );
+ }
+
+ //update all the modified entries
+ int count = doUpdateRows( id, collection, session );
+
+ if ( log.isDebugEnabled() ) {
+ log.debug( "done updating rows: " + count + " updated" );
+ }
+ }
+ }
+
+ protected abstract int doUpdateRows(Serializable key, PersistentCollection collection, SessionImplementor session)
+ throws HibernateException;
+
+ public CollectionMetadata getCollectionMetadata() {
+ return this;
+ }
+
+ public SessionFactoryImplementor getFactory() {
+ return factory;
+ }
+
+ protected String filterFragment(String alias) throws MappingException {
+ return hasWhere() ? " and " + getSQLWhereString( alias ) : "";
+ }
+
+ public String filterFragment(String alias, Map enabledFilters) throws MappingException {
+
+ StringBuffer sessionFilterFragment = new StringBuffer();
+ filterHelper.render( sessionFilterFragment, alias, enabledFilters );
+
+ return sessionFilterFragment.append( filterFragment( alias ) ).toString();
+ }
+
+ public String oneToManyFilterFragment(String alias) throws MappingException {
+ return "";
+ }
+
+ protected boolean isInsertCallable() {
+ return insertCallable;
+ }
+
+ protected ExecuteUpdateResultCheckStyle getInsertCheckStyle() {
+ return insertCheckStyle;
+ }
+
+ protected boolean isUpdateCallable() {
+ return updateCallable;
+ }
+
+ protected ExecuteUpdateResultCheckStyle getUpdateCheckStyle() {
+ return updateCheckStyle;
+ }
+
+ protected boolean isDeleteCallable() {
+ return deleteCallable;
+ }
+
+ protected ExecuteUpdateResultCheckStyle getDeleteCheckStyle() {
+ return deleteCheckStyle;
+ }
+
+ protected boolean isDeleteAllCallable() {
+ return deleteAllCallable;
+ }
+
+ protected ExecuteUpdateResultCheckStyle getDeleteAllCheckStyle() {
+ return deleteAllCheckStyle;
+ }
+
+ public String toString() {
+ return StringHelper.unqualify( getClass().getName() ) + '(' + role + ')';
+ }
+
+ public boolean isVersioned() {
+ return isVersioned && getOwnerEntityPersister().isVersioned();
+ }
+
+ public String getNodeName() {
+ return nodeName;
+ }
+
+ public String getElementNodeName() {
+ return elementNodeName;
+ }
+
+ public String getIndexNodeName() {
+ return indexNodeName;
+ }
+
+ protected SQLExceptionConverter getSQLExceptionConverter() {
+ return sqlExceptionConverter;
+ }
+
+ public CacheEntryStructure getCacheEntryStructure() {
+ return cacheEntryStructure;
+ }
+
+ public boolean isAffectedByEnabledFilters(SessionImplementor session) {
+ return filterHelper.isAffectedBy( session.getEnabledFilters() ) ||
+ ( isManyToMany() && manyToManyFilterHelper.isAffectedBy( session.getEnabledFilters() ) );
+ }
+
+ public boolean isSubselectLoadable() {
+ return subselectLoadable;
+ }
+
+ public boolean isMutable() {
+ return isMutable;
+ }
+
+ public String[] getCollectionPropertyColumnAliases(String propertyName, String suffix) {
+ String rawAliases[] = (String[]) collectionPropertyColumnAliases.get(propertyName);
+
+ if ( rawAliases == null ) {
+ return null;
+ }
+
+ String result[] = new String[rawAliases.length];
+ for ( int i=0; i<rawAliases.length; i++ ) {
+ result[i] = new Alias(suffix).toUnquotedAliasString( rawAliases[i] );
+ }
+ return result;
+ }
+
+ //TODO: formulas ?
+ public void initCollectionPropertyMap() {
+
+ initCollectionPropertyMap( "key", keyType, keyColumnAliases, keyColumnNames );
+ initCollectionPropertyMap( "element", elementType, elementColumnAliases, elementColumnNames );
+ if (hasIndex) {
+ initCollectionPropertyMap( "index", indexType, indexColumnAliases, indexColumnNames );
+ }
+ if (hasIdentifier) {
+ initCollectionPropertyMap(
+ "id",
+ identifierType,
+ new String[] { identifierColumnAlias },
+ new String[] { identifierColumnName }
+ );
+ }
+ }
+
+ private void initCollectionPropertyMap(String aliasName, Type type, String[] columnAliases, String[] columnNames) {
+
+ collectionPropertyColumnAliases.put(aliasName, columnAliases);
+ collectionPropertyColumnNames.put(aliasName, columnNames);
+
+ if( type.isComponentType() ) {
+ AbstractComponentType ct = (AbstractComponentType) type;
+ String[] propertyNames = ct.getPropertyNames();
+ for (int i = 0; i < propertyNames.length; i++) {
+ String name = propertyNames[i];
+ collectionPropertyColumnAliases.put( aliasName + "." + name, columnAliases[i] );
+ collectionPropertyColumnNames.put( aliasName + "." + name, columnNames[i] );
+ }
+ }
+
+ }
+
+ public int getSize(Serializable key, SessionImplementor session) {
+ try {
+ PreparedStatement st = session.getBatcher().prepareSelectStatement(sqlSelectSizeString);
+ try {
+ getKeyType().nullSafeSet(st, key, 1, session);
+ ResultSet rs = st.executeQuery();
+ try {
+ return rs.next() ? rs.getInt(1) - baseIndex : 0;
+ }
+ finally {
+ rs.close();
+ }
+ }
+ finally {
+ session.getBatcher().closeStatement( st );
+ }
+ }
+ catch (SQLException sqle) {
+ throw JDBCExceptionHelper.convert(
+ getFactory().getSQLExceptionConverter(),
+ sqle,
+ "could not retrieve collection size: " +
+ MessageHelper.collectionInfoString( this, key, getFactory() ),
+ sqlSelectSizeString
+ );
+ }
+ }
+
+ public boolean indexExists(Serializable key, Object index, SessionImplementor session) {
+ return exists(key, incrementIndexByBase(index), getIndexType(), sqlDetectRowByIndexString, session);
+ }
+
+ public boolean elementExists(Serializable key, Object element, SessionImplementor session) {
+ return exists(key, element, getElementType(), sqlDetectRowByElementString, session);
+ }
+
+ private boolean exists(Serializable key, Object indexOrElement, Type indexOrElementType, String sql, SessionImplementor session) {
+ try {
+ PreparedStatement st = session.getBatcher().prepareSelectStatement(sql);
+ try {
+ getKeyType().nullSafeSet(st, key, 1, session);
+ indexOrElementType.nullSafeSet( st, indexOrElement, keyColumnNames.length + 1, session );
+ ResultSet rs = st.executeQuery();
+ try {
+ return rs.next();
+ }
+ finally {
+ rs.close();
+ }
+ }
+ catch( TransientObjectException e ) {
+ return false;
+ }
+ finally {
+ session.getBatcher().closeStatement( st );
+ }
+ }
+ catch (SQLException sqle) {
+ throw JDBCExceptionHelper.convert(
+ getFactory().getSQLExceptionConverter(),
+ sqle,
+ "could not check row existence: " +
+ MessageHelper.collectionInfoString( this, key, getFactory() ),
+ sqlSelectSizeString
+ );
+ }
+ }
+
+ public Object getElementByIndex(Serializable key, Object index, SessionImplementor session, Object owner) {
+ try {
+ PreparedStatement st = session.getBatcher().prepareSelectStatement(sqlSelectRowByIndexString);
+ try {
+ getKeyType().nullSafeSet(st, key, 1, session);
+ getIndexType().nullSafeSet( st, incrementIndexByBase(index), keyColumnNames.length + 1, session );
+ ResultSet rs = st.executeQuery();
+ try {
+ if ( rs.next() ) {
+ return getElementType().nullSafeGet(rs, elementColumnAliases, session, owner);
+ }
+ else {
+ return null;
+ }
+ }
+ finally {
+ rs.close();
+ }
+ }
+ finally {
+ session.getBatcher().closeStatement( st );
+ }
+ }
+ catch (SQLException sqle) {
+ throw JDBCExceptionHelper.convert(
+ getFactory().getSQLExceptionConverter(),
+ sqle,
+ "could not read row: " +
+ MessageHelper.collectionInfoString( this, key, getFactory() ),
+ sqlSelectSizeString
+ );
+ }
+ }
+
+ public boolean isExtraLazy() {
+ return isExtraLazy;
+ }
+
+ protected Dialect getDialect() {
+ return dialect;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/BasicCollectionPersister.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/BasicCollectionPersister.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/BasicCollectionPersister.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,320 @@
+//$Id: BasicCollectionPersister.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.persister.collection;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.Iterator;
+
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.jdbc.Expectations;
+import org.hibernate.jdbc.Expectation;
+import org.hibernate.type.AssociationType;
+import org.hibernate.persister.entity.Joinable;
+import org.hibernate.cache.CacheException;
+import org.hibernate.cache.access.CollectionRegionAccessStrategy;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.SubselectFetch;
+import org.hibernate.exception.JDBCExceptionHelper;
+import org.hibernate.loader.collection.BatchingCollectionInitializer;
+import org.hibernate.loader.collection.CollectionInitializer;
+import org.hibernate.loader.collection.SubselectCollectionLoader;
+import org.hibernate.mapping.Collection;
+import org.hibernate.pretty.MessageHelper;
+import org.hibernate.sql.Delete;
+import org.hibernate.sql.Insert;
+import org.hibernate.sql.Update;
+import org.hibernate.sql.SelectFragment;
+import org.hibernate.util.ArrayHelper;
+
+/**
+ * Collection persister for collections of values and many-to-many associations.
+ *
+ * @author Gavin King
+ */
+public class BasicCollectionPersister extends AbstractCollectionPersister {
+
+ public boolean isCascadeDeleteEnabled() {
+ return false;
+ }
+
+ public BasicCollectionPersister(
+ Collection collection,
+ CollectionRegionAccessStrategy cacheAccessStrategy,
+ Configuration cfg,
+ SessionFactoryImplementor factory) throws MappingException, CacheException {
+ super( collection, cacheAccessStrategy, cfg, factory );
+ }
+
+ /**
+ * Generate the SQL DELETE that deletes all rows
+ */
+ protected String generateDeleteString() {
+
+ Delete delete = new Delete()
+ .setTableName( qualifiedTableName )
+ .setPrimaryKeyColumnNames( keyColumnNames );
+
+ if ( hasWhere ) delete.setWhere( sqlWhereString );
+
+ if ( getFactory().getSettings().isCommentsEnabled() ) {
+ delete.setComment( "delete collection " + getRole() );
+ }
+
+ return delete.toStatementString();
+ }
+
+ /**
+ * Generate the SQL INSERT that creates a new row
+ */
+ protected String generateInsertRowString() {
+
+ Insert insert = new Insert( getDialect() )
+ .setTableName( qualifiedTableName )
+ .addColumns( keyColumnNames );
+
+ if ( hasIdentifier) insert.addColumn( identifierColumnName );
+
+ if ( hasIndex /*&& !indexIsFormula*/ ) {
+ insert.addColumns( indexColumnNames, indexColumnIsSettable );
+ }
+
+ if ( getFactory().getSettings().isCommentsEnabled() ) {
+ insert.setComment( "insert collection row " + getRole() );
+ }
+
+ //if ( !elementIsFormula ) {
+ insert.addColumns( elementColumnNames, elementColumnIsSettable );
+ //}
+
+ return insert.toStatementString();
+ }
+
+ /**
+ * Generate the SQL UPDATE that updates a row
+ */
+ protected String generateUpdateRowString() {
+
+ Update update = new Update( getDialect() )
+ .setTableName( qualifiedTableName );
+
+ //if ( !elementIsFormula ) {
+ update.addColumns( elementColumnNames, elementColumnIsSettable );
+ //}
+
+ if ( hasIdentifier ) {
+ update.setPrimaryKeyColumnNames( new String[]{ identifierColumnName } );
+ }
+ else if ( hasIndex && !indexContainsFormula ) {
+ update.setPrimaryKeyColumnNames( ArrayHelper.join( keyColumnNames, indexColumnNames ) );
+ }
+ else {
+ update.setPrimaryKeyColumnNames( ArrayHelper.join( keyColumnNames, elementColumnNames, elementColumnIsInPrimaryKey ) );
+ }
+
+ if ( getFactory().getSettings().isCommentsEnabled() ) {
+ update.setComment( "update collection row " + getRole() );
+ }
+
+ return update.toStatementString();
+ }
+
+ /**
+ * Generate the SQL DELETE that deletes a particular row
+ */
+ protected String generateDeleteRowString() {
+
+ Delete delete = new Delete()
+ .setTableName( qualifiedTableName );
+
+ if ( hasIdentifier ) {
+ delete.setPrimaryKeyColumnNames( new String[]{ identifierColumnName } );
+ }
+ else if ( hasIndex && !indexContainsFormula ) {
+ delete.setPrimaryKeyColumnNames( ArrayHelper.join( keyColumnNames, indexColumnNames ) );
+ }
+ else {
+ delete.setPrimaryKeyColumnNames( ArrayHelper.join( keyColumnNames, elementColumnNames, elementColumnIsInPrimaryKey ) );
+ }
+
+ if ( getFactory().getSettings().isCommentsEnabled() ) {
+ delete.setComment( "delete collection row " + getRole() );
+ }
+
+ return delete.toStatementString();
+ }
+
+ public boolean consumesEntityAlias() {
+ return false;
+ }
+
+ public boolean consumesCollectionAlias() {
+// return !isOneToMany();
+ return true;
+ }
+
+ public boolean isOneToMany() {
+ return false;
+ }
+
+ public boolean isManyToMany() {
+ return elementType.isEntityType(); //instanceof AssociationType;
+ }
+
+ protected int doUpdateRows(Serializable id, PersistentCollection collection, SessionImplementor session)
+ throws HibernateException {
+
+ if ( ArrayHelper.isAllFalse(elementColumnIsSettable) ) return 0;
+
+ try {
+ PreparedStatement st = null;
+ Expectation expectation = Expectations.appropriateExpectation( getUpdateCheckStyle() );
+ boolean callable = isUpdateCallable();
+ boolean useBatch = expectation.canBeBatched();
+ Iterator entries = collection.entries( this );
+ String sql = getSQLUpdateRowString();
+ int i = 0;
+ int count = 0;
+ while ( entries.hasNext() ) {
+ Object entry = entries.next();
+ if ( collection.needsUpdating( entry, i, elementType ) ) {
+ int offset = 1;
+
+ if ( useBatch ) {
+ if ( st == null ) {
+ if ( callable ) {
+ st = session.getBatcher().prepareBatchCallableStatement( sql );
+ }
+ else {
+ st = session.getBatcher().prepareBatchStatement( sql );
+ }
+ }
+ }
+ else {
+ if ( callable ) {
+ st = session.getBatcher().prepareCallableStatement( sql );
+ }
+ else {
+ st = session.getBatcher().prepareStatement( sql );
+ }
+ }
+
+ try {
+ offset+= expectation.prepare( st );
+ int loc = writeElement( st, collection.getElement( entry ), offset, session );
+ if ( hasIdentifier ) {
+ writeIdentifier( st, collection.getIdentifier( entry, i ), loc, session );
+ }
+ else {
+ loc = writeKey( st, id, loc, session );
+ if ( hasIndex && !indexContainsFormula ) {
+ writeIndexToWhere( st, collection.getIndex( entry, i, this ), loc, session );
+ }
+ else {
+ writeElementToWhere( st, collection.getSnapshotElement( entry, i ), loc, session );
+ }
+ }
+
+ if ( useBatch ) {
+ session.getBatcher().addToBatch( expectation );
+ }
+ else {
+ expectation.verifyOutcome( st.executeUpdate(), st, -1 );
+ }
+ }
+ catch ( SQLException sqle ) {
+ if ( useBatch ) {
+ session.getBatcher().abortBatch( sqle );
+ }
+ throw sqle;
+ }
+ finally {
+ if ( !useBatch ) {
+ session.getBatcher().closeStatement( st );
+ }
+ }
+ count++;
+ }
+ i++;
+ }
+ return count;
+ }
+ catch ( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ getSQLExceptionConverter(),
+ sqle,
+ "could not update collection rows: " + MessageHelper.collectionInfoString( this, id, getFactory() ),
+ getSQLUpdateRowString()
+ );
+ }
+ }
+
+ public String selectFragment(
+ Joinable rhs,
+ String rhsAlias,
+ String lhsAlias,
+ String entitySuffix,
+ String collectionSuffix,
+ boolean includeCollectionColumns) {
+ // we need to determine the best way to know that two joinables
+ // represent a single many-to-many...
+ if ( rhs != null && isManyToMany() && !rhs.isCollection() ) {
+ AssociationType elementType = ( ( AssociationType ) getElementType() );
+ if ( rhs.equals( elementType.getAssociatedJoinable( getFactory() ) ) ) {
+ return manyToManySelectFragment( rhs, rhsAlias, lhsAlias, collectionSuffix );
+ }
+ }
+ return includeCollectionColumns ? selectFragment( lhsAlias, collectionSuffix ) : "";
+ }
+
+ private String manyToManySelectFragment(
+ Joinable rhs,
+ String rhsAlias,
+ String lhsAlias,
+ String collectionSuffix) {
+ SelectFragment frag = generateSelectFragment( lhsAlias, collectionSuffix );
+
+ String[] elementColumnNames = rhs.getKeyColumnNames();
+ frag.addColumns( rhsAlias, elementColumnNames, elementColumnAliases );
+ appendIndexColumns( frag, lhsAlias );
+ appendIdentifierColumns( frag, lhsAlias );
+
+ return frag.toFragmentString()
+ .substring( 2 ); //strip leading ','
+ }
+
+ /**
+ * Create the <tt>CollectionLoader</tt>
+ *
+ * @see org.hibernate.loader.collection.BasicCollectionLoader
+ */
+ protected CollectionInitializer createCollectionInitializer(java.util.Map enabledFilters)
+ throws MappingException {
+ return BatchingCollectionInitializer.createBatchingCollectionInitializer( this, batchSize, getFactory(), enabledFilters );
+ }
+
+ public String fromJoinFragment(String alias, boolean innerJoin, boolean includeSubclasses) {
+ return "";
+ }
+
+ public String whereJoinFragment(String alias, boolean innerJoin, boolean includeSubclasses) {
+ return "";
+ }
+
+ protected CollectionInitializer createSubselectInitializer(SubselectFetch subselect, SessionImplementor session) {
+ return new SubselectCollectionLoader(
+ this,
+ subselect.toSubselectString( getCollectionType().getLHSPropertyName() ),
+ subselect.getResult(),
+ subselect.getQueryParameters(),
+ subselect.getNamedParameterLocMap(),
+ session.getFactory(),
+ session.getEnabledFilters()
+ );
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/CollectionPersister.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/CollectionPersister.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/CollectionPersister.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,288 @@
+//$Id: CollectionPersister.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.persister.collection;
+
+import java.io.Serializable;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Map;
+
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.cache.access.CollectionRegionAccessStrategy;
+import org.hibernate.cache.entry.CacheEntryStructure;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.id.IdentifierGenerator;
+import org.hibernate.metadata.CollectionMetadata;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.type.CollectionType;
+import org.hibernate.type.Type;
+
+/**
+ * A strategy for persisting a collection role. Defines a contract between
+ * the persistence strategy and the actual persistent collection framework
+ * and session. Does not define operations that are required for querying
+ * collections, or loading by outer join.<br>
+ * <br>
+ * Implements persistence of a collection instance while the instance is
+ * referenced in a particular role.<br>
+ * <br>
+ * This class is highly coupled to the <tt>PersistentCollection</tt>
+ * hierarchy, since double dispatch is used to load and update collection
+ * elements.<br>
+ * <br>
+ * May be considered an immutable view of the mapping object
+ *
+ * @see QueryableCollection
+ * @see PersistentCollection
+ * @author Gavin King
+ */
+public interface CollectionPersister {
+ /**
+ * Initialize the given collection with the given key
+ */
+ public void initialize(Serializable key, SessionImplementor session) //TODO: add owner argument!!
+ throws HibernateException;
+ /**
+ * Is this collection role cacheable
+ */
+ public boolean hasCache();
+ /**
+ * Get the cache
+ */
+ public CollectionRegionAccessStrategy getCacheAccessStrategy();
+ /**
+ * Get the cache structure
+ */
+ public CacheEntryStructure getCacheEntryStructure();
+ /**
+ * Get the associated <tt>Type</tt>
+ */
+ public CollectionType getCollectionType();
+ /**
+ * Get the "key" type (the type of the foreign key)
+ */
+ public Type getKeyType();
+ /**
+ * Get the "index" type for a list or map (optional operation)
+ */
+ public Type getIndexType();
+ /**
+ * Get the "element" type
+ */
+ public Type getElementType();
+ /**
+ * Return the element class of an array, or null otherwise
+ */
+ public Class getElementClass();
+ /**
+ * Read the key from a row of the JDBC <tt>ResultSet</tt>
+ */
+ public Object readKey(ResultSet rs, String[] keyAliases, SessionImplementor session)
+ throws HibernateException, SQLException;
+ /**
+ * Read the element from a row of the JDBC <tt>ResultSet</tt>
+ */
+ public Object readElement(
+ ResultSet rs,
+ Object owner,
+ String[] columnAliases,
+ SessionImplementor session)
+ throws HibernateException, SQLException;
+ /**
+ * Read the index from a row of the JDBC <tt>ResultSet</tt>
+ */
+ public Object readIndex(ResultSet rs, String[] columnAliases, SessionImplementor session)
+ throws HibernateException, SQLException;
+ /**
+ * Read the identifier from a row of the JDBC <tt>ResultSet</tt>
+ */
+ public Object readIdentifier(
+ ResultSet rs,
+ String columnAlias,
+ SessionImplementor session)
+ throws HibernateException, SQLException;
+ /**
+ * Is this an array or primitive values?
+ */
+ public boolean isPrimitiveArray();
+ /**
+ * Is this an array?
+ */
+ public boolean isArray();
+ /**
+ * Is this a one-to-many association?
+ */
+ public boolean isOneToMany();
+ /**
+ * Is this a many-to-many association? Note that this is mainly
+ * a convenience feature as the single persister does not
+ * conatin all the information needed to handle a many-to-many
+ * itself, as internally it is looked at as two many-to-ones.
+ */
+ public boolean isManyToMany();
+
+ public String getManyToManyFilterFragment(String alias, Map enabledFilters);
+
+ /**
+ * Is this an "indexed" collection? (list or map)
+ */
+ public boolean hasIndex();
+ /**
+ * Is this collection lazyily initialized?
+ */
+ public boolean isLazy();
+ /**
+ * Is this collection "inverse", so state changes are not
+ * propogated to the database.
+ */
+ public boolean isInverse();
+ /**
+ * Completely remove the persistent state of the collection
+ */
+ public void remove(Serializable id, SessionImplementor session)
+ throws HibernateException;
+ /**
+ * (Re)create the collection's persistent state
+ */
+ public void recreate(
+ PersistentCollection collection,
+ Serializable key,
+ SessionImplementor session)
+ throws HibernateException;
+ /**
+ * Delete the persistent state of any elements that were removed from
+ * the collection
+ */
+ public void deleteRows(
+ PersistentCollection collection,
+ Serializable key,
+ SessionImplementor session)
+ throws HibernateException;
+ /**
+ * Update the persistent state of any elements that were modified
+ */
+ public void updateRows(
+ PersistentCollection collection,
+ Serializable key,
+ SessionImplementor session)
+ throws HibernateException;
+ /**
+ * Insert the persistent state of any new collection elements
+ */
+ public void insertRows(
+ PersistentCollection collection,
+ Serializable key,
+ SessionImplementor session)
+ throws HibernateException;
+ /**
+ * Get the name of this collection role (the fully qualified class name,
+ * extended by a "property path")
+ */
+ public String getRole();
+ /**
+ * Get the persister of the entity that "owns" this collection
+ */
+ public EntityPersister getOwnerEntityPersister();
+ /**
+ * Get the surrogate key generation strategy (optional operation)
+ */
+ public IdentifierGenerator getIdentifierGenerator();
+ /**
+ * Get the type of the surrogate key
+ */
+ public Type getIdentifierType();
+ /**
+ * Does this collection implement "orphan delete"?
+ */
+ public boolean hasOrphanDelete();
+ /**
+ * Is this an ordered collection? (An ordered collection is
+ * ordered by the initialization operation, not by sorting
+ * that happens in memory, as in the case of a sorted collection.)
+ */
+ public boolean hasOrdering();
+
+ public boolean hasManyToManyOrdering();
+
+ /**
+ * Get the "space" that holds the persistent state
+ */
+ public Serializable[] getCollectionSpaces();
+
+ public CollectionMetadata getCollectionMetadata();
+
+ /**
+ * Is cascade delete handled by the database-level
+ * foreign key constraint definition?
+ */
+ public abstract boolean isCascadeDeleteEnabled();
+
+ /**
+ * Does this collection cause version increment of the
+ * owning entity?
+ */
+ public boolean isVersioned();
+
+ /**
+ * Can the elements of this collection change?
+ */
+ public boolean isMutable();
+
+ //public boolean isSubselectLoadable();
+
+ public String getNodeName();
+
+ public String getElementNodeName();
+
+ public String getIndexNodeName();
+
+ public void postInstantiate() throws MappingException;
+
+ public SessionFactoryImplementor getFactory();
+
+ public boolean isAffectedByEnabledFilters(SessionImplementor session);
+
+ /**
+ * Generates the collection's key column aliases, based on the given
+ * suffix.
+ *
+ * @param suffix The suffix to use in the key column alias generation.
+ * @return The key column aliases.
+ */
+ public String[] getKeyColumnAliases(String suffix);
+
+ /**
+ * Generates the collection's index column aliases, based on the given
+ * suffix.
+ *
+ * @param suffix The suffix to use in the index column alias generation.
+ * @return The key column aliases, or null if not indexed.
+ */
+ public String[] getIndexColumnAliases(String suffix);
+
+ /**
+ * Generates the collection's element column aliases, based on the given
+ * suffix.
+ *
+ * @param suffix The suffix to use in the element column alias generation.
+ * @return The key column aliases.
+ */
+ public String[] getElementColumnAliases(String suffix);
+
+ /**
+ * Generates the collection's identifier column aliases, based on the given
+ * suffix.
+ *
+ * @param suffix The suffix to use in the key column alias generation.
+ * @return The key column aliases.
+ */
+ public String getIdentifierColumnAlias(String suffix);
+
+ public boolean isExtraLazy();
+ public int getSize(Serializable key, SessionImplementor session);
+ public boolean indexExists(Serializable key, Object index, SessionImplementor session);
+ public boolean elementExists(Serializable key, Object element, SessionImplementor session);
+ public Object getElementByIndex(Serializable key, Object index, SessionImplementor session, Object owner);
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/CollectionPropertyMapping.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/CollectionPropertyMapping.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/CollectionPropertyMapping.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,100 @@
+package org.hibernate.persister.collection;
+
+import org.hibernate.Hibernate;
+import org.hibernate.QueryException;
+import org.hibernate.persister.entity.PropertyMapping;
+import org.hibernate.type.Type;
+
+/**
+ * @author Gavin King
+ */
+public class CollectionPropertyMapping implements PropertyMapping {
+
+ private final QueryableCollection memberPersister;
+
+ public CollectionPropertyMapping(QueryableCollection memberPersister) {
+ this.memberPersister = memberPersister;
+ }
+
+ public Type toType(String propertyName) throws QueryException {
+ if ( propertyName.equals(CollectionPropertyNames.COLLECTION_ELEMENTS) ) {
+ return memberPersister.getElementType();
+ }
+ else if ( propertyName.equals(CollectionPropertyNames.COLLECTION_INDICES) ) {
+ if ( !memberPersister.hasIndex() ) throw new QueryException("unindexed collection before indices()");
+ return memberPersister.getIndexType();
+ }
+ else if ( propertyName.equals(CollectionPropertyNames.COLLECTION_SIZE) ) {
+ return Hibernate.INTEGER;
+ }
+ else if ( propertyName.equals(CollectionPropertyNames.COLLECTION_MAX_INDEX) ) {
+ return memberPersister.getIndexType();
+ }
+ else if ( propertyName.equals(CollectionPropertyNames.COLLECTION_MIN_INDEX) ) {
+ return memberPersister.getIndexType();
+ }
+ else if ( propertyName.equals(CollectionPropertyNames.COLLECTION_MAX_ELEMENT) ) {
+ return memberPersister.getElementType();
+ }
+ else if ( propertyName.equals(CollectionPropertyNames.COLLECTION_MIN_ELEMENT) ) {
+ return memberPersister.getElementType();
+ }
+ else {
+ //return memberPersister.getPropertyType(propertyName);
+ throw new QueryException("illegal syntax near collection: " + propertyName);
+ }
+ }
+
+ public String[] toColumns(String alias, String propertyName) throws QueryException {
+ if ( propertyName.equals(CollectionPropertyNames.COLLECTION_ELEMENTS) ) {
+ return memberPersister.getElementColumnNames(alias);
+ }
+ else if ( propertyName.equals(CollectionPropertyNames.COLLECTION_INDICES) ) {
+ if ( !memberPersister.hasIndex() ) throw new QueryException("unindexed collection in indices()");
+ return memberPersister.getIndexColumnNames(alias);
+ }
+ else if ( propertyName.equals(CollectionPropertyNames.COLLECTION_SIZE) ) {
+ String[] cols = memberPersister.getKeyColumnNames();
+ return new String[] { "count(" + alias + '.' + cols[0] + ')' };
+ }
+ else if ( propertyName.equals(CollectionPropertyNames.COLLECTION_MAX_INDEX) ) {
+ if ( !memberPersister.hasIndex() ) throw new QueryException("unindexed collection in maxIndex()");
+ String[] cols = memberPersister.getIndexColumnNames(alias);
+ if ( cols.length!=1 ) throw new QueryException("composite collection index in maxIndex()");
+ return new String[] { "max(" + cols[0] + ')' };
+ }
+ else if ( propertyName.equals(CollectionPropertyNames.COLLECTION_MIN_INDEX) ) {
+ if ( !memberPersister.hasIndex() ) throw new QueryException("unindexed collection in minIndex()");
+ String[] cols = memberPersister.getIndexColumnNames(alias);
+ if ( cols.length!=1 ) throw new QueryException("composite collection index in minIndex()");
+ return new String[] { "min(" + cols[0] + ')' };
+ }
+ else if ( propertyName.equals(CollectionPropertyNames.COLLECTION_MAX_ELEMENT) ) {
+ String[] cols = memberPersister.getElementColumnNames(alias);
+ if ( cols.length!=1 ) throw new QueryException("composite collection element in maxElement()");
+ return new String[] { "max(" + cols[0] + ')' };
+ }
+ else if ( propertyName.equals(CollectionPropertyNames.COLLECTION_MIN_ELEMENT) ) {
+ String[] cols = memberPersister.getElementColumnNames(alias);
+ if ( cols.length!=1 ) throw new QueryException("composite collection element in minElement()");
+ return new String[] { "min(" + cols[0] + ')' };
+ }
+ else {
+ //return memberPersister.toColumns(alias, propertyName);
+ throw new QueryException("illegal syntax near collection: " + propertyName);
+ }
+ }
+
+ /**
+ * Given a property path, return the corresponding column name(s).
+ */
+ public String[] toColumns(String propertyName) throws QueryException, UnsupportedOperationException {
+ throw new UnsupportedOperationException( "References to collections must be define a SQL alias" );
+ }
+
+ public Type getType() {
+ //return memberPersister.getType();
+ return memberPersister.getCollectionType();
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/CollectionPropertyNames.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/CollectionPropertyNames.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/CollectionPropertyNames.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,19 @@
+// $Id: CollectionPropertyNames.java 5699 2005-02-13 11:50:11Z oneovthafew $
+package org.hibernate.persister.collection;
+
+/**
+ * The names of all the collection properties.
+ *
+ * @author josh Dec 23, 2004 7:35:02 AM
+ */
+public class CollectionPropertyNames {
+
+ public static final String COLLECTION_SIZE = "size";
+ public static final String COLLECTION_ELEMENTS = "elements";
+ public static final String COLLECTION_INDICES = "indices";
+ public static final String COLLECTION_MAX_INDEX = "maxIndex";
+ public static final String COLLECTION_MIN_INDEX = "minIndex";
+ public static final String COLLECTION_MAX_ELEMENT = "maxElement";
+ public static final String COLLECTION_MIN_ELEMENT = "minElement";
+ public static final String COLLECTION_INDEX = "index";
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/CompositeElementPropertyMapping.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/CompositeElementPropertyMapping.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/CompositeElementPropertyMapping.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,38 @@
+//$Id: CompositeElementPropertyMapping.java 6136 2005-03-21 18:15:29Z oneovthafew $
+package org.hibernate.persister.collection;
+
+import org.hibernate.MappingException;
+import org.hibernate.engine.Mapping;
+import org.hibernate.persister.entity.AbstractPropertyMapping;
+import org.hibernate.type.AbstractComponentType;
+import org.hibernate.type.Type;
+
+/**
+ * @author Gavin King
+ */
+public class CompositeElementPropertyMapping extends AbstractPropertyMapping {
+
+ private final AbstractComponentType compositeType;
+
+ public CompositeElementPropertyMapping(
+ String[] elementColumns,
+ String[] elementFormulaTemplates,
+ AbstractComponentType compositeType,
+ Mapping factory)
+ throws MappingException {
+
+ this.compositeType = compositeType;
+
+ initComponentPropertyPaths(null, compositeType, elementColumns, elementFormulaTemplates, factory);
+
+ }
+
+ public Type getType() {
+ return compositeType;
+ }
+
+ protected String getEntityName() {
+ return compositeType.getName();
+ }
+
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/ElementPropertyMapping.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/ElementPropertyMapping.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/ElementPropertyMapping.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,57 @@
+//$Id: ElementPropertyMapping.java 6179 2005-03-23 15:41:48Z steveebersole $
+package org.hibernate.persister.collection;
+
+import org.hibernate.MappingException;
+
+import org.hibernate.QueryException;
+
+import org.hibernate.persister.entity.PropertyMapping;
+
+import org.hibernate.type.Type;
+
+import org.hibernate.util.StringHelper;
+
+/**
+ * @author Gavin King
+ */
+public class ElementPropertyMapping implements PropertyMapping {
+
+ private final String[] elementColumns;
+ private final Type type;
+
+ public ElementPropertyMapping(String[] elementColumns, Type type)
+ throws MappingException {
+ this.elementColumns = elementColumns;
+ this.type = type;
+ }
+
+ public Type toType(String propertyName) throws QueryException {
+ if ( propertyName==null || "id".equals(propertyName) ) {
+ return type;
+ }
+ else {
+ throw new QueryException("cannot dereference scalar collection element: " + propertyName);
+ }
+ }
+
+ public String[] toColumns(String alias, String propertyName) throws QueryException {
+ if (propertyName==null || "id".equals(propertyName) ) {
+ return StringHelper.qualify(alias, elementColumns);
+ }
+ else {
+ throw new QueryException("cannot dereference scalar collection element: " + propertyName);
+ }
+ }
+
+ /**
+ * Given a property path, return the corresponding column name(s).
+ */
+ public String[] toColumns(String propertyName) throws QueryException, UnsupportedOperationException {
+ throw new UnsupportedOperationException( "References to collections must be define a SQL alias" );
+ }
+
+ public Type getType() {
+ return type;
+ }
+
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/NamedQueryCollectionInitializer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/NamedQueryCollectionInitializer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/NamedQueryCollectionInitializer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,59 @@
+//$Id: NamedQueryCollectionInitializer.java 10019 2006-06-15 07:50:12Z steve.ebersole at jboss.com $
+package org.hibernate.persister.collection;
+
+import java.io.Serializable;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.FlushMode;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.impl.AbstractQueryImpl;
+import org.hibernate.loader.collection.CollectionInitializer;
+
+/**
+ * A wrapper around a named query.
+ * @author Gavin King
+ */
+public final class NamedQueryCollectionInitializer implements CollectionInitializer {
+ private final String queryName;
+ private final CollectionPersister persister;
+
+ private static final Log log = LogFactory.getLog(NamedQueryCollectionInitializer.class);
+
+ public NamedQueryCollectionInitializer(String queryName, CollectionPersister persister) {
+ super();
+ this.queryName = queryName;
+ this.persister = persister;
+ }
+
+ public void initialize(Serializable key, SessionImplementor session)
+ throws HibernateException {
+
+ if ( log.isDebugEnabled() ) {
+ log.debug(
+ "initializing collection: " +
+ persister.getRole() +
+ " using named query: " +
+ queryName
+ );
+ }
+
+ //TODO: is there a more elegant way than downcasting?
+ AbstractQueryImpl query = (AbstractQueryImpl) session.getNamedSQLQuery(queryName);
+ if ( query.getNamedParameters().length>0 ) {
+ query.setParameter(
+ query.getNamedParameters()[0],
+ key,
+ persister.getKeyType()
+ );
+ }
+ else {
+ query.setParameter( 0, key, persister.getKeyType() );
+ }
+ query.setCollectionKey( key )
+ .setFlushMode( FlushMode.MANUAL )
+ .list();
+
+ }
+}
\ No newline at end of file
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/NamedQueryCollectionInitializer.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/OneToManyPersister.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/OneToManyPersister.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/OneToManyPersister.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,364 @@
+//$Id: OneToManyPersister.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.persister.collection;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.Iterator;
+
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.jdbc.Expectation;
+import org.hibernate.jdbc.Expectations;
+import org.hibernate.cache.CacheException;
+import org.hibernate.cache.access.CollectionRegionAccessStrategy;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.SubselectFetch;
+import org.hibernate.exception.JDBCExceptionHelper;
+import org.hibernate.loader.collection.BatchingCollectionInitializer;
+import org.hibernate.loader.collection.CollectionInitializer;
+import org.hibernate.loader.collection.SubselectOneToManyLoader;
+import org.hibernate.loader.entity.CollectionElementLoader;
+import org.hibernate.mapping.Collection;
+import org.hibernate.persister.entity.Joinable;
+import org.hibernate.persister.entity.OuterJoinLoadable;
+import org.hibernate.pretty.MessageHelper;
+import org.hibernate.sql.Update;
+import org.hibernate.util.ArrayHelper;
+
+/**
+ * Collection persister for one-to-many associations.
+ *
+ * @author Gavin King
+ */
+public class OneToManyPersister extends AbstractCollectionPersister {
+
+ private final boolean cascadeDeleteEnabled;
+ private final boolean keyIsNullable;
+ private final boolean keyIsUpdateable;
+
+ protected boolean isRowDeleteEnabled() {
+ return keyIsUpdateable && keyIsNullable;
+ }
+
+ protected boolean isRowInsertEnabled() {
+ return keyIsUpdateable;
+ }
+
+ public boolean isCascadeDeleteEnabled() {
+ return cascadeDeleteEnabled;
+ }
+
+ public OneToManyPersister(
+ Collection collection,
+ CollectionRegionAccessStrategy cacheAccessStrategy,
+ Configuration cfg,
+ SessionFactoryImplementor factory) throws MappingException, CacheException {
+ super( collection, cacheAccessStrategy, cfg, factory );
+ cascadeDeleteEnabled = collection.getKey().isCascadeDeleteEnabled() &&
+ factory.getDialect().supportsCascadeDelete();
+ keyIsNullable = collection.getKey().isNullable();
+ keyIsUpdateable = collection.getKey().isUpdateable();
+ }
+
+ /**
+ * Generate the SQL UPDATE that updates all the foreign keys to null
+ */
+ protected String generateDeleteString() {
+
+ Update update = new Update( getDialect() )
+ .setTableName( qualifiedTableName )
+ .addColumns( keyColumnNames, "null" )
+ .setPrimaryKeyColumnNames( keyColumnNames );
+
+ if ( hasIndex && !indexContainsFormula ) update.addColumns( indexColumnNames, "null" );
+
+ if ( hasWhere ) update.setWhere( sqlWhereString );
+
+ if ( getFactory().getSettings().isCommentsEnabled() ) {
+ update.setComment( "delete one-to-many " + getRole() );
+ }
+
+ return update.toStatementString();
+ }
+
+ /**
+ * Generate the SQL UPDATE that updates a foreign key to a value
+ */
+ protected String generateInsertRowString() {
+
+ Update update = new Update( getDialect() )
+ .setTableName( qualifiedTableName )
+ .addColumns( keyColumnNames );
+
+ if ( hasIndex && !indexContainsFormula ) update.addColumns( indexColumnNames );
+
+ //identifier collections not supported for 1-to-many
+ if ( getFactory().getSettings().isCommentsEnabled() ) {
+ update.setComment( "create one-to-many row " + getRole() );
+ }
+
+ return update.setPrimaryKeyColumnNames( elementColumnNames )
+ .toStatementString();
+ }
+
+ /**
+ * Not needed for one-to-many association
+ */
+ protected String generateUpdateRowString() {
+ return null;
+ }
+
+ /**
+ * Generate the SQL UPDATE that updates a particular row's foreign
+ * key to null
+ */
+ protected String generateDeleteRowString() {
+
+ Update update = new Update( getDialect() )
+ .setTableName( qualifiedTableName )
+ .addColumns( keyColumnNames, "null" );
+
+ if ( hasIndex && !indexContainsFormula ) update.addColumns( indexColumnNames, "null" );
+
+ if ( getFactory().getSettings().isCommentsEnabled() ) {
+ update.setComment( "delete one-to-many row " + getRole() );
+ }
+
+ //use a combination of foreign key columns and pk columns, since
+ //the ordering of removal and addition is not guaranteed when
+ //a child moves from one parent to another
+ String[] rowSelectColumnNames = ArrayHelper.join(keyColumnNames, elementColumnNames);
+ return update.setPrimaryKeyColumnNames( rowSelectColumnNames )
+ .toStatementString();
+ }
+
+ public boolean consumesEntityAlias() {
+ return true;
+ }
+ public boolean consumesCollectionAlias() {
+ return true;
+ }
+
+ public boolean isOneToMany() {
+ return true;
+ }
+
+ public boolean isManyToMany() {
+ return false;
+ }
+
+ protected int doUpdateRows(Serializable id, PersistentCollection collection, SessionImplementor session)
+ throws HibernateException {
+
+ // we finish all the "removes" first to take care of possible unique
+ // constraints and so that we can take better advantage of batching
+
+ try {
+ int count = 0;
+ if ( isRowDeleteEnabled() ) {
+ boolean useBatch = true;
+ PreparedStatement st = null;
+ // update removed rows fks to null
+ try {
+ int i = 0;
+
+ Iterator entries = collection.entries( this );
+ int offset = 1;
+ Expectation expectation = Expectations.NONE;
+ while ( entries.hasNext() ) {
+
+ Object entry = entries.next();
+ if ( collection.needsUpdating( entry, i, elementType ) ) { // will still be issued when it used to be null
+ if ( st == null ) {
+ String sql = getSQLDeleteRowString();
+ if ( isDeleteCallable() ) {
+ expectation = Expectations.appropriateExpectation( getDeleteCheckStyle() );
+ useBatch = expectation.canBeBatched();
+ st = useBatch
+ ? session.getBatcher().prepareBatchCallableStatement( sql )
+ : session.getBatcher().prepareCallableStatement( sql );
+ offset += expectation.prepare( st );
+ }
+ else {
+ st = session.getBatcher().prepareBatchStatement( getSQLDeleteRowString() );
+ }
+ }
+ int loc = writeKey( st, id, offset, session );
+ writeElementToWhere( st, collection.getSnapshotElement(entry, i), loc, session );
+ if ( useBatch ) {
+ session.getBatcher().addToBatch( expectation );
+ }
+ else {
+ expectation.verifyOutcome( st.executeUpdate(), st, -1 );
+ }
+ count++;
+ }
+ i++;
+ }
+ }
+ catch ( SQLException sqle ) {
+ if ( useBatch ) {
+ session.getBatcher().abortBatch( sqle );
+ }
+ throw sqle;
+ }
+ finally {
+ if ( !useBatch ) {
+ session.getBatcher().closeStatement( st );
+ }
+ }
+ }
+
+ if ( isRowInsertEnabled() ) {
+ Expectation expectation = Expectations.appropriateExpectation( getInsertCheckStyle() );
+ boolean callable = isInsertCallable();
+ boolean useBatch = expectation.canBeBatched();
+ String sql = getSQLInsertRowString();
+ PreparedStatement st = null;
+ // now update all changed or added rows fks
+ try {
+ int i = 0;
+ Iterator entries = collection.entries( this );
+ while ( entries.hasNext() ) {
+ Object entry = entries.next();
+ int offset = 1;
+ if ( collection.needsUpdating( entry, i, elementType ) ) {
+ if ( useBatch ) {
+ if ( st == null ) {
+ if ( callable ) {
+ st = session.getBatcher().prepareBatchCallableStatement( sql );
+ }
+ else {
+ st = session.getBatcher().prepareBatchStatement( sql );
+ }
+ }
+ }
+ else {
+ if ( callable ) {
+ st = session.getBatcher().prepareCallableStatement( sql );
+ }
+ else {
+ st = session.getBatcher().prepareStatement( sql );
+ }
+ }
+
+ offset += expectation.prepare( st );
+
+ int loc = writeKey( st, id, offset, session );
+ if ( hasIndex && !indexContainsFormula ) {
+ loc = writeIndexToWhere( st, collection.getIndex( entry, i, this ), loc, session );
+ }
+
+ writeElementToWhere( st, collection.getElement( entry ), loc, session );
+
+ if ( useBatch ) {
+ session.getBatcher().addToBatch( expectation );
+ }
+ else {
+ expectation.verifyOutcome( st.executeUpdate(), st, -1 );
+ }
+ count++;
+ }
+ i++;
+ }
+ }
+ catch ( SQLException sqle ) {
+ if ( useBatch ) {
+ session.getBatcher().abortBatch( sqle );
+ }
+ throw sqle;
+ }
+ finally {
+ if ( !useBatch ) {
+ session.getBatcher().closeStatement( st );
+ }
+ }
+ }
+
+ return count;
+ }
+ catch ( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ getSQLExceptionConverter(),
+ sqle,
+ "could not update collection rows: " +
+ MessageHelper.collectionInfoString( this, id, getFactory() ),
+ getSQLInsertRowString()
+ );
+ }
+ }
+
+ public String selectFragment(
+ Joinable rhs,
+ String rhsAlias,
+ String lhsAlias,
+ String entitySuffix,
+ String collectionSuffix,
+ boolean includeCollectionColumns) {
+ StringBuffer buf = new StringBuffer();
+ if ( includeCollectionColumns ) {
+// buf.append( selectFragment( lhsAlias, "" ) )//ignore suffix for collection columns!
+ buf.append( selectFragment( lhsAlias, collectionSuffix ) )
+ .append( ", " );
+ }
+ OuterJoinLoadable ojl = ( OuterJoinLoadable ) getElementPersister();
+ return buf.append( ojl.selectFragment( lhsAlias, entitySuffix ) )//use suffix for the entity columns
+ .toString();
+ }
+
+ /**
+ * Create the <tt>OneToManyLoader</tt>
+ *
+ * @see org.hibernate.loader.collection.OneToManyLoader
+ */
+ protected CollectionInitializer createCollectionInitializer(java.util.Map enabledFilters) throws MappingException {
+ return BatchingCollectionInitializer.createBatchingOneToManyInitializer( this, batchSize, getFactory(), enabledFilters );
+ }
+
+ public String fromJoinFragment(String alias,
+ boolean innerJoin,
+ boolean includeSubclasses) {
+ return ( ( Joinable ) getElementPersister() ).fromJoinFragment( alias, innerJoin, includeSubclasses );
+ }
+
+ public String whereJoinFragment(String alias,
+ boolean innerJoin,
+ boolean includeSubclasses) {
+ return ( ( Joinable ) getElementPersister() ).whereJoinFragment( alias, innerJoin, includeSubclasses );
+ }
+
+ public String getTableName() {
+ return ( ( Joinable ) getElementPersister() ).getTableName();
+ }
+
+ public String filterFragment(String alias) throws MappingException {
+ String result = super.filterFragment( alias );
+ if ( getElementPersister() instanceof Joinable ) {
+ result += ( ( Joinable ) getElementPersister() ).oneToManyFilterFragment( alias );
+ }
+ return result;
+
+ }
+
+ protected CollectionInitializer createSubselectInitializer(SubselectFetch subselect, SessionImplementor session) {
+ return new SubselectOneToManyLoader(
+ this,
+ subselect.toSubselectString( getCollectionType().getLHSPropertyName() ),
+ subselect.getResult(),
+ subselect.getQueryParameters(),
+ subselect.getNamedParameterLocMap(),
+ session.getFactory(),
+ session.getEnabledFilters()
+ );
+ }
+
+ public Object getElementByIndex(Serializable key, Object index, SessionImplementor session, Object owner) {
+ return new CollectionElementLoader( this, getFactory(), session.getEnabledFilters() )
+ .loadElement( session, key, incrementIndexByBase(index) );
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/QueryableCollection.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/QueryableCollection.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/QueryableCollection.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,74 @@
+//$Id: QueryableCollection.java 9875 2006-05-04 16:23:44Z steve.ebersole at jboss.com $
+package org.hibernate.persister.collection;
+
+import org.hibernate.FetchMode;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.persister.entity.Joinable;
+import org.hibernate.persister.entity.PropertyMapping;
+
+/**
+ * A collection role that may be queried or loaded by outer join.
+ * @author Gavin King
+ */
+public interface QueryableCollection extends PropertyMapping, Joinable, CollectionPersister {
+ /**
+ * Generate a list of collection index and element columns
+ */
+ public abstract String selectFragment(String alias, String columnSuffix);
+ /**
+ * Get the names of the collection index columns if
+ * this is an indexed collection (optional operation)
+ */
+ public abstract String[] getIndexColumnNames();
+ /**
+ * Get the index formulas if this is an indexed collection
+ * (optional operation)
+ */
+ public abstract String[] getIndexFormulas();
+ /**
+ * Get the names of the collection index columns if
+ * this is an indexed collection (optional operation),
+ * aliased by the given table alias
+ */
+ public abstract String[] getIndexColumnNames(String alias);
+ /**
+ * Get the names of the collection element columns (or the primary
+ * key columns in the case of a one-to-many association),
+ * aliased by the given table alias
+ */
+ public abstract String[] getElementColumnNames(String alias);
+ /**
+ * Get the names of the collection element columns (or the primary
+ * key columns in the case of a one-to-many association)
+ */
+ public abstract String[] getElementColumnNames();
+ /**
+ * Get the order by SQL
+ */
+ public abstract String getSQLOrderByString(String alias);
+
+ /**
+ * Get the order-by to be applied at the target table of a many to many
+ *
+ * @param alias The alias for the many-to-many target table
+ * @return appropriate order-by fragment or empty string.
+ */
+ public abstract String getManyToManyOrderByString(String alias);
+
+ /**
+ * Does this collection role have a where clause filter?
+ */
+ public abstract boolean hasWhere();
+ /**
+ * Get the persister of the element class, if this is a
+ * collection of entities (optional operation). Note that
+ * for a one-to-many association, the returned persister
+ * must be <tt>OuterJoinLoadable</tt>.
+ */
+ public abstract EntityPersister getElementPersister();
+ /**
+ * Should we load this collection role by outerjoining?
+ */
+ public abstract FetchMode getFetchMode();
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/SQLLoadableCollection.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/SQLLoadableCollection.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/SQLLoadableCollection.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,9 @@
+package org.hibernate.persister.collection;
+
+public interface SQLLoadableCollection extends QueryableCollection {
+
+ public abstract String[] getCollectionPropertyColumnAliases(String propertyName, String string);
+
+ public abstract String getIdentifierColumnName();
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,15 @@
+<html>
+<head></head>
+<body>
+<p>
+ This package abstracts the persistence mechanism for collections.
+</p>
+<p>
+ Concrete implementations of <tt>CollectionPersister</tt>
+ define strategies for persistence of particular collection
+ roles. Collection persisters may optionally implement
+ <tt>QueryableCollection</tt> if they should be queryable
+ using HQL or loaded using <tt>OuterJoinLoader</tt>.
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/persister/collection/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,3839 @@
+//$Id: AbstractEntityPersister.java 11527 2007-05-15 20:16:50Z epbernard $
+package org.hibernate.persister.entity;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.Comparator;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.AssertionFailure;
+import org.hibernate.EntityMode;
+import org.hibernate.FetchMode;
+import org.hibernate.HibernateException;
+import org.hibernate.LockMode;
+import org.hibernate.MappingException;
+import org.hibernate.QueryException;
+import org.hibernate.StaleObjectStateException;
+import org.hibernate.StaleStateException;
+import org.hibernate.jdbc.Expectation;
+import org.hibernate.jdbc.Expectations;
+import org.hibernate.jdbc.TooManyRowsAffectedException;
+import org.hibernate.dialect.lock.LockingStrategy;
+import org.hibernate.cache.CacheConcurrencyStrategy;
+import org.hibernate.cache.CacheKey;
+import org.hibernate.cache.access.EntityRegionAccessStrategy;
+import org.hibernate.cache.entry.CacheEntry;
+import org.hibernate.cache.entry.CacheEntryStructure;
+import org.hibernate.cache.entry.StructuredCacheEntry;
+import org.hibernate.cache.entry.UnstructuredCacheEntry;
+import org.hibernate.engine.CascadeStyle;
+import org.hibernate.engine.CascadingAction;
+import org.hibernate.engine.EntityEntry;
+import org.hibernate.engine.Mapping;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.Versioning;
+import org.hibernate.engine.ExecuteUpdateResultCheckStyle;
+import org.hibernate.engine.EntityKey;
+import org.hibernate.engine.ValueInclusion;
+import org.hibernate.exception.JDBCExceptionHelper;
+import org.hibernate.id.IdentifierGenerator;
+import org.hibernate.id.PostInsertIdentifierGenerator;
+import org.hibernate.id.PostInsertIdentityPersister;
+import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
+import org.hibernate.id.insert.Binder;
+import org.hibernate.intercept.LazyPropertyInitializer;
+import org.hibernate.intercept.FieldInterceptionHelper;
+import org.hibernate.intercept.FieldInterceptor;
+import org.hibernate.loader.entity.BatchingEntityLoader;
+import org.hibernate.loader.entity.CascadeEntityLoader;
+import org.hibernate.loader.entity.EntityLoader;
+import org.hibernate.loader.entity.UniqueEntityLoader;
+import org.hibernate.mapping.Column;
+import org.hibernate.mapping.Component;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Property;
+import org.hibernate.mapping.Selectable;
+import org.hibernate.metadata.ClassMetadata;
+import org.hibernate.pretty.MessageHelper;
+import org.hibernate.property.BackrefPropertyAccessor;
+import org.hibernate.sql.Alias;
+import org.hibernate.sql.Delete;
+import org.hibernate.sql.Insert;
+import org.hibernate.sql.JoinFragment;
+import org.hibernate.sql.Select;
+import org.hibernate.sql.SelectFragment;
+import org.hibernate.sql.SimpleSelect;
+import org.hibernate.sql.Template;
+import org.hibernate.sql.Update;
+import org.hibernate.tuple.entity.EntityMetamodel;
+import org.hibernate.tuple.entity.EntityTuplizer;
+import org.hibernate.tuple.Tuplizer;
+import org.hibernate.type.AbstractComponentType;
+import org.hibernate.type.AssociationType;
+import org.hibernate.type.EntityType;
+import org.hibernate.type.Type;
+import org.hibernate.type.TypeFactory;
+import org.hibernate.type.VersionType;
+import org.hibernate.util.ArrayHelper;
+import org.hibernate.util.CollectionHelper;
+import org.hibernate.util.FilterHelper;
+import org.hibernate.util.StringHelper;
+
+/**
+ * Basic functionality for persisting an entity via JDBC
+ * through either generated or custom SQL
+ *
+ * @author Gavin King
+ */
+public abstract class AbstractEntityPersister
+ implements OuterJoinLoadable, Queryable, ClassMetadata, UniqueKeyLoadable,
+ SQLLoadable, LazyPropertyInitializer, PostInsertIdentityPersister, Lockable {
+
+ private static final Log log = LogFactory.getLog( AbstractEntityPersister.class );
+
+ public static final String ENTITY_CLASS = "class";
+
+ // moved up from AbstractEntityPersister ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ private final SessionFactoryImplementor factory;
+ private final EntityRegionAccessStrategy cacheAccessStrategy;
+ private final boolean isLazyPropertiesCacheable;
+ private final CacheEntryStructure cacheEntryStructure;
+ private final EntityMetamodel entityMetamodel;
+ private final Map entityNameBySubclass = new HashMap();
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ private final String[] rootTableKeyColumnNames;
+ private final String[] identifierAliases;
+ private final int identifierColumnSpan;
+ private final String versionColumnName;
+ private final boolean hasFormulaProperties;
+ private final int batchSize;
+ private final boolean hasSubselectLoadableCollections;
+ protected final String rowIdName;
+
+ private final Set lazyProperties;
+
+ // The optional SQL string defined in the where attribute
+ private final String sqlWhereString;
+ private final String sqlWhereStringTemplate;
+
+ //information about properties of this class,
+ //including inherited properties
+ //(only really needed for updatable/insertable properties)
+ private final int[] propertyColumnSpans;
+ private final String[] propertySubclassNames;
+ private final String[][] propertyColumnAliases;
+ private final String[][] propertyColumnNames;
+ private final String[][] propertyColumnFormulaTemplates;
+ private final boolean[][] propertyColumnUpdateable;
+ private final boolean[][] propertyColumnInsertable;
+ private final boolean[] propertyUniqueness;
+ private final boolean[] propertySelectable;
+
+ //information about lazy properties of this class
+ private final String[] lazyPropertyNames;
+ private final int[] lazyPropertyNumbers;
+ private final Type[] lazyPropertyTypes;
+ private final String[][] lazyPropertyColumnAliases;
+
+ //information about all properties in class hierarchy
+ private final String[] subclassPropertyNameClosure;
+ private final String[] subclassPropertySubclassNameClosure;
+ private final Type[] subclassPropertyTypeClosure;
+ private final String[][] subclassPropertyFormulaTemplateClosure;
+ private final String[][] subclassPropertyColumnNameClosure;
+ private final FetchMode[] subclassPropertyFetchModeClosure;
+ private final boolean[] subclassPropertyNullabilityClosure;
+ private final boolean[] propertyDefinedOnSubclass;
+ private final int[][] subclassPropertyColumnNumberClosure;
+ private final int[][] subclassPropertyFormulaNumberClosure;
+ private final CascadeStyle[] subclassPropertyCascadeStyleClosure;
+
+ //information about all columns/formulas in class hierarchy
+ private final String[] subclassColumnClosure;
+ private final boolean[] subclassColumnLazyClosure;
+ private final String[] subclassColumnAliasClosure;
+ private final boolean[] subclassColumnSelectableClosure;
+ private final String[] subclassFormulaClosure;
+ private final String[] subclassFormulaTemplateClosure;
+ private final String[] subclassFormulaAliasClosure;
+ private final boolean[] subclassFormulaLazyClosure;
+
+ // dynamic filters attached to the class-level
+ private final FilterHelper filterHelper;
+
+ private final Map uniqueKeyLoaders = new HashMap();
+ private final Map lockers = new HashMap();
+ private final Map loaders = new HashMap();
+
+ // SQL strings
+ private String sqlVersionSelectString;
+ private String sqlSnapshotSelectString;
+ private String sqlLazySelectString;
+
+ private String sqlIdentityInsertString;
+ private String sqlUpdateByRowIdString;
+ private String sqlLazyUpdateByRowIdString;
+
+ private String[] sqlDeleteStrings;
+ private String[] sqlInsertStrings;
+ private String[] sqlUpdateStrings;
+ private String[] sqlLazyUpdateStrings;
+
+ private String sqlInsertGeneratedValuesSelectString;
+ private String sqlUpdateGeneratedValuesSelectString;
+
+ //Custom SQL (would be better if these were private)
+ protected boolean[] insertCallable;
+ protected boolean[] updateCallable;
+ protected boolean[] deleteCallable;
+ protected String[] customSQLInsert;
+ protected String[] customSQLUpdate;
+ protected String[] customSQLDelete;
+ protected ExecuteUpdateResultCheckStyle[] insertResultCheckStyles;
+ protected ExecuteUpdateResultCheckStyle[] updateResultCheckStyles;
+ protected ExecuteUpdateResultCheckStyle[] deleteResultCheckStyles;
+
+ private InsertGeneratedIdentifierDelegate identityDelegate;
+
+ private boolean[] tableHasColumns;
+
+ private final String loaderName;
+
+ private UniqueEntityLoader queryLoader;
+
+ private final String temporaryIdTableName;
+ private final String temporaryIdTableDDL;
+
+ private final Map subclassPropertyAliases = new HashMap();
+ private final Map subclassPropertyColumnNames = new HashMap();
+
+ protected final BasicEntityPropertyMapping propertyMapping;
+
+ protected void addDiscriminatorToInsert(Insert insert) {}
+
+ protected void addDiscriminatorToSelect(SelectFragment select, String name, String suffix) {}
+
+ protected abstract int[] getSubclassColumnTableNumberClosure();
+
+ protected abstract int[] getSubclassFormulaTableNumberClosure();
+
+ public abstract String getSubclassTableName(int j);
+
+ protected abstract String[] getSubclassTableKeyColumns(int j);
+
+ protected abstract boolean isClassOrSuperclassTable(int j);
+
+ protected abstract int getSubclassTableSpan();
+
+ protected abstract int getTableSpan();
+
+ protected abstract boolean isTableCascadeDeleteEnabled(int j);
+
+ protected abstract String getTableName(int j);
+
+ protected abstract String[] getKeyColumns(int j);
+
+ protected abstract boolean isPropertyOfTable(int property, int j);
+
+ protected abstract int[] getPropertyTableNumbersInSelect();
+
+ protected abstract int[] getPropertyTableNumbers();
+
+ protected abstract int getSubclassPropertyTableNumber(int i);
+
+ protected abstract String filterFragment(String alias) throws MappingException;
+
+ private static final String DISCRIMINATOR_ALIAS = "clazz_";
+
+ public String getDiscriminatorColumnName() {
+ return DISCRIMINATOR_ALIAS;
+ }
+
+ protected String getDiscriminatorAlias() {
+ return DISCRIMINATOR_ALIAS;
+ }
+
+ protected String getDiscriminatorFormulaTemplate() {
+ return null;
+ }
+
+ protected boolean isInverseTable(int j) {
+ return false;
+ }
+
+ protected boolean isNullableTable(int j) {
+ return false;
+ }
+
+ protected boolean isNullableSubclassTable(int j) {
+ return false;
+ }
+
+ protected boolean isInverseSubclassTable(int j) {
+ return false;
+ }
+
+ public boolean isSubclassEntityName(String entityName) {
+ return entityMetamodel.getSubclassEntityNames().contains(entityName);
+ }
+
+ private boolean[] getTableHasColumns() {
+ return tableHasColumns;
+ }
+
+ public String[] getRootTableKeyColumnNames() {
+ return rootTableKeyColumnNames;
+ }
+
+ protected String[] getSQLUpdateByRowIdStrings() {
+ if ( sqlUpdateByRowIdString == null ) {
+ throw new AssertionFailure( "no update by row id" );
+ }
+ String[] result = new String[getTableSpan() + 1];
+ result[0] = sqlUpdateByRowIdString;
+ System.arraycopy( sqlUpdateStrings, 0, result, 1, getTableSpan() );
+ return result;
+ }
+
+ protected String[] getSQLLazyUpdateByRowIdStrings() {
+ if ( sqlLazyUpdateByRowIdString == null ) {
+ throw new AssertionFailure( "no update by row id" );
+ }
+ String[] result = new String[getTableSpan()];
+ result[0] = sqlLazyUpdateByRowIdString;
+ for ( int i = 1; i < getTableSpan(); i++ ) {
+ result[i] = sqlLazyUpdateStrings[i];
+ }
+ return result;
+ }
+
+ protected String getSQLSnapshotSelectString() {
+ return sqlSnapshotSelectString;
+ }
+
+ protected String getSQLLazySelectString() {
+ return sqlLazySelectString;
+ }
+
+ protected String[] getSQLDeleteStrings() {
+ return sqlDeleteStrings;
+ }
+
+ protected String[] getSQLInsertStrings() {
+ return sqlInsertStrings;
+ }
+
+ protected String[] getSQLUpdateStrings() {
+ return sqlUpdateStrings;
+ }
+
+ protected String[] getSQLLazyUpdateStrings() {
+ return sqlLazyUpdateStrings;
+ }
+
+ /**
+ * The query that inserts a row, letting the database generate an id
+ *
+ * @return The IDENTITY-based insertion query.
+ */
+ protected String getSQLIdentityInsertString() {
+ return sqlIdentityInsertString;
+ }
+
+ protected String getVersionSelectString() {
+ return sqlVersionSelectString;
+ }
+
+ protected boolean isInsertCallable(int j) {
+ return insertCallable[j];
+ }
+
+ protected boolean isUpdateCallable(int j) {
+ return updateCallable[j];
+ }
+
+ protected boolean isDeleteCallable(int j) {
+ return deleteCallable[j];
+ }
+
+ protected boolean isSubclassPropertyDeferred(String propertyName, String entityName) {
+ return false;
+ }
+
+ protected boolean isSubclassTableSequentialSelect(int j) {
+ return false;
+ }
+
+ public boolean hasSequentialSelect() {
+ return false;
+ }
+
+ /**
+ * Decide which tables need to be updated.
+ * <p/>
+ * The return here is an array of boolean values with each index corresponding
+ * to a given table in the scope of this persister.
+ *
+ * @param dirtyProperties The indices of all the entity properties considered dirty.
+ * @param hasDirtyCollection Whether any collections owned by the entity which were considered dirty.
+ *
+ * @return Array of booleans indicating which table require updating.
+ */
+ protected boolean[] getTableUpdateNeeded(final int[] dirtyProperties, boolean hasDirtyCollection) {
+
+ if ( dirtyProperties == null ) {
+ return getTableHasColumns(); // for objects that came in via update()
+ }
+ else {
+ boolean[] updateability = getPropertyUpdateability();
+ int[] propertyTableNumbers = getPropertyTableNumbers();
+ boolean[] tableUpdateNeeded = new boolean[ getTableSpan() ];
+ for ( int i = 0; i < dirtyProperties.length; i++ ) {
+ int property = dirtyProperties[i];
+ int table = propertyTableNumbers[property];
+ tableUpdateNeeded[table] = tableUpdateNeeded[table] ||
+ ( getPropertyColumnSpan(property) > 0 && updateability[property] );
+ }
+ if ( isVersioned() ) {
+ tableUpdateNeeded[0] = tableUpdateNeeded[0] ||
+ Versioning.isVersionIncrementRequired( dirtyProperties, hasDirtyCollection, getPropertyVersionability() );
+ }
+ return tableUpdateNeeded;
+ }
+ }
+
+ public boolean hasRowId() {
+ return rowIdName != null;
+ }
+
+ public AbstractEntityPersister(
+ final PersistentClass persistentClass,
+ final EntityRegionAccessStrategy cacheAccessStrategy,
+ final SessionFactoryImplementor factory) throws HibernateException {
+
+ // moved up from AbstractEntityPersister ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ this.factory = factory;
+ this.cacheAccessStrategy = cacheAccessStrategy;
+ isLazyPropertiesCacheable = persistentClass.isLazyPropertiesCacheable();
+ this.cacheEntryStructure = factory.getSettings().isStructuredCacheEntriesEnabled() ?
+ (CacheEntryStructure) new StructuredCacheEntry(this) :
+ (CacheEntryStructure) new UnstructuredCacheEntry();
+
+ this.entityMetamodel = new EntityMetamodel( persistentClass, factory );
+
+ if ( persistentClass.hasPojoRepresentation() ) {
+ //TODO: this is currently specific to pojos, but need to be available for all entity-modes
+ Iterator iter = persistentClass.getSubclassIterator();
+ while ( iter.hasNext() ) {
+ PersistentClass pc = ( PersistentClass ) iter.next();
+ entityNameBySubclass.put( pc.getMappedClass(), pc.getEntityName() );
+ }
+ }
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ int batch = persistentClass.getBatchSize();
+ if ( batch == -1 ) {
+ batch = factory.getSettings().getDefaultBatchFetchSize();
+ }
+ batchSize = batch;
+ hasSubselectLoadableCollections = persistentClass.hasSubselectLoadableCollections();
+
+ propertyMapping = new BasicEntityPropertyMapping( this );
+
+ // IDENTIFIER
+
+ identifierColumnSpan = persistentClass.getIdentifier().getColumnSpan();
+ rootTableKeyColumnNames = new String[identifierColumnSpan];
+ identifierAliases = new String[identifierColumnSpan];
+
+ rowIdName = persistentClass.getRootTable().getRowId();
+
+ loaderName = persistentClass.getLoaderName();
+
+ Iterator iter = persistentClass.getIdentifier().getColumnIterator();
+ int i = 0;
+ while ( iter.hasNext() ) {
+ Column col = ( Column ) iter.next();
+ rootTableKeyColumnNames[i] = col.getQuotedName( factory.getDialect() );
+ identifierAliases[i] = col.getAlias( factory.getDialect(), persistentClass.getRootTable() );
+ i++;
+ }
+
+ // VERSION
+
+ if ( persistentClass.isVersioned() ) {
+ versionColumnName = ( ( Column ) persistentClass.getVersion().getColumnIterator().next() ).getQuotedName( factory.getDialect() );
+ }
+ else {
+ versionColumnName = null;
+ }
+
+ //WHERE STRING
+
+ sqlWhereString = StringHelper.isNotEmpty( persistentClass.getWhere() ) ? "( " + persistentClass.getWhere() + ") " : null;
+ sqlWhereStringTemplate = sqlWhereString == null ?
+ null :
+ Template.renderWhereStringTemplate( sqlWhereString, factory.getDialect(), factory.getSqlFunctionRegistry() );
+
+ // PROPERTIES
+
+ final boolean lazyAvailable = isInstrumented(EntityMode.POJO);
+
+ int hydrateSpan = entityMetamodel.getPropertySpan();
+ propertyColumnSpans = new int[hydrateSpan];
+ propertySubclassNames = new String[hydrateSpan];
+ propertyColumnAliases = new String[hydrateSpan][];
+ propertyColumnNames = new String[hydrateSpan][];
+ propertyColumnFormulaTemplates = new String[hydrateSpan][];
+ propertyUniqueness = new boolean[hydrateSpan];
+ propertySelectable = new boolean[hydrateSpan];
+ propertyColumnUpdateable = new boolean[hydrateSpan][];
+ propertyColumnInsertable = new boolean[hydrateSpan][];
+ HashSet thisClassProperties = new HashSet();
+
+ lazyProperties = new HashSet();
+ ArrayList lazyNames = new ArrayList();
+ ArrayList lazyNumbers = new ArrayList();
+ ArrayList lazyTypes = new ArrayList();
+ ArrayList lazyColAliases = new ArrayList();
+
+ iter = persistentClass.getPropertyClosureIterator();
+ i = 0;
+ boolean foundFormula = false;
+ while ( iter.hasNext() ) {
+ Property prop = ( Property ) iter.next();
+ thisClassProperties.add( prop );
+
+ int span = prop.getColumnSpan();
+ propertyColumnSpans[i] = span;
+ propertySubclassNames[i] = prop.getPersistentClass().getEntityName();
+ String[] colNames = new String[span];
+ String[] colAliases = new String[span];
+ String[] templates = new String[span];
+ Iterator colIter = prop.getColumnIterator();
+ int k = 0;
+ while ( colIter.hasNext() ) {
+ Selectable thing = ( Selectable ) colIter.next();
+ colAliases[k] = thing.getAlias( factory.getDialect() , prop.getValue().getTable() );
+ if ( thing.isFormula() ) {
+ foundFormula = true;
+ templates[k] = thing.getTemplate( factory.getDialect(), factory.getSqlFunctionRegistry() );
+ }
+ else {
+ colNames[k] = thing.getTemplate( factory.getDialect(), factory.getSqlFunctionRegistry() );
+ }
+ k++;
+ }
+ propertyColumnNames[i] = colNames;
+ propertyColumnFormulaTemplates[i] = templates;
+ propertyColumnAliases[i] = colAliases;
+
+ if ( lazyAvailable && prop.isLazy() ) {
+ lazyProperties.add( prop.getName() );
+ lazyNames.add( prop.getName() );
+ lazyNumbers.add( new Integer( i ) );
+ lazyTypes.add( prop.getValue().getType() );
+ lazyColAliases.add( colAliases );
+ }
+
+ propertyColumnUpdateable[i] = prop.getValue().getColumnUpdateability();
+ propertyColumnInsertable[i] = prop.getValue().getColumnInsertability();
+
+ propertySelectable[i] = prop.isSelectable();
+
+ propertyUniqueness[i] = prop.getValue().isAlternateUniqueKey();
+
+ i++;
+
+ }
+ hasFormulaProperties = foundFormula;
+ lazyPropertyColumnAliases = ArrayHelper.to2DStringArray( lazyColAliases );
+ lazyPropertyNames = ArrayHelper.toStringArray( lazyNames );
+ lazyPropertyNumbers = ArrayHelper.toIntArray( lazyNumbers );
+ lazyPropertyTypes = ArrayHelper.toTypeArray( lazyTypes );
+
+ // SUBCLASS PROPERTY CLOSURE
+
+ ArrayList columns = new ArrayList();
+ ArrayList columnsLazy = new ArrayList();
+ ArrayList aliases = new ArrayList();
+ ArrayList formulas = new ArrayList();
+ ArrayList formulaAliases = new ArrayList();
+ ArrayList formulaTemplates = new ArrayList();
+ ArrayList formulasLazy = new ArrayList();
+ ArrayList types = new ArrayList();
+ ArrayList names = new ArrayList();
+ ArrayList classes = new ArrayList();
+ ArrayList templates = new ArrayList();
+ ArrayList propColumns = new ArrayList();
+ ArrayList joinedFetchesList = new ArrayList();
+ ArrayList cascades = new ArrayList();
+ ArrayList definedBySubclass = new ArrayList();
+ ArrayList propColumnNumbers = new ArrayList();
+ ArrayList propFormulaNumbers = new ArrayList();
+ ArrayList columnSelectables = new ArrayList();
+ ArrayList propNullables = new ArrayList();
+
+ iter = persistentClass.getSubclassPropertyClosureIterator();
+ while ( iter.hasNext() ) {
+ Property prop = ( Property ) iter.next();
+ names.add( prop.getName() );
+ classes.add( prop.getPersistentClass().getEntityName() );
+ boolean isDefinedBySubclass = !thisClassProperties.contains( prop );
+ definedBySubclass.add( Boolean.valueOf( isDefinedBySubclass ) );
+ propNullables.add( Boolean.valueOf( prop.isOptional() || isDefinedBySubclass ) ); //TODO: is this completely correct?
+ types.add( prop.getType() );
+
+ Iterator colIter = prop.getColumnIterator();
+ String[] cols = new String[prop.getColumnSpan()];
+ String[] forms = new String[prop.getColumnSpan()];
+ int[] colnos = new int[prop.getColumnSpan()];
+ int[] formnos = new int[prop.getColumnSpan()];
+ int l = 0;
+ Boolean lazy = Boolean.valueOf( prop.isLazy() && lazyAvailable );
+ while ( colIter.hasNext() ) {
+ Selectable thing = ( Selectable ) colIter.next();
+ if ( thing.isFormula() ) {
+ String template = thing.getTemplate( factory.getDialect(), factory.getSqlFunctionRegistry() );
+ formnos[l] = formulaTemplates.size();
+ colnos[l] = -1;
+ formulaTemplates.add( template );
+ forms[l] = template;
+ formulas.add( thing.getText( factory.getDialect() ) );
+ formulaAliases.add( thing.getAlias( factory.getDialect() ) );
+ formulasLazy.add( lazy );
+ }
+ else {
+ String colName = thing.getTemplate( factory.getDialect(), factory.getSqlFunctionRegistry() );
+ colnos[l] = columns.size(); //before add :-)
+ formnos[l] = -1;
+ columns.add( colName );
+ cols[l] = colName;
+ aliases.add( thing.getAlias( factory.getDialect(), prop.getValue().getTable() ) );
+ columnsLazy.add( lazy );
+ columnSelectables.add( Boolean.valueOf( prop.isSelectable() ) );
+ }
+ l++;
+ }
+ propColumns.add( cols );
+ templates.add( forms );
+ propColumnNumbers.add( colnos );
+ propFormulaNumbers.add( formnos );
+
+ joinedFetchesList.add( prop.getValue().getFetchMode() );
+ cascades.add( prop.getCascadeStyle() );
+ }
+ subclassColumnClosure = ArrayHelper.toStringArray( columns );
+ subclassColumnAliasClosure = ArrayHelper.toStringArray( aliases );
+ subclassColumnLazyClosure = ArrayHelper.toBooleanArray( columnsLazy );
+ subclassColumnSelectableClosure = ArrayHelper.toBooleanArray( columnSelectables );
+
+ subclassFormulaClosure = ArrayHelper.toStringArray( formulas );
+ subclassFormulaTemplateClosure = ArrayHelper.toStringArray( formulaTemplates );
+ subclassFormulaAliasClosure = ArrayHelper.toStringArray( formulaAliases );
+ subclassFormulaLazyClosure = ArrayHelper.toBooleanArray( formulasLazy );
+
+ subclassPropertyNameClosure = ArrayHelper.toStringArray( names );
+ subclassPropertySubclassNameClosure = ArrayHelper.toStringArray( classes );
+ subclassPropertyTypeClosure = ArrayHelper.toTypeArray( types );
+ subclassPropertyNullabilityClosure = ArrayHelper.toBooleanArray( propNullables );
+ subclassPropertyFormulaTemplateClosure = ArrayHelper.to2DStringArray( templates );
+ subclassPropertyColumnNameClosure = ArrayHelper.to2DStringArray( propColumns );
+ subclassPropertyColumnNumberClosure = ArrayHelper.to2DIntArray( propColumnNumbers );
+ subclassPropertyFormulaNumberClosure = ArrayHelper.to2DIntArray( propFormulaNumbers );
+
+ subclassPropertyCascadeStyleClosure = new CascadeStyle[cascades.size()];
+ iter = cascades.iterator();
+ int j = 0;
+ while ( iter.hasNext() ) {
+ subclassPropertyCascadeStyleClosure[j++] = ( CascadeStyle ) iter.next();
+ }
+ subclassPropertyFetchModeClosure = new FetchMode[joinedFetchesList.size()];
+ iter = joinedFetchesList.iterator();
+ j = 0;
+ while ( iter.hasNext() ) {
+ subclassPropertyFetchModeClosure[j++] = ( FetchMode ) iter.next();
+ }
+
+ propertyDefinedOnSubclass = new boolean[definedBySubclass.size()];
+ iter = definedBySubclass.iterator();
+ j = 0;
+ while ( iter.hasNext() ) {
+ propertyDefinedOnSubclass[j++] = ( ( Boolean ) iter.next() ).booleanValue();
+ }
+
+ // Handle any filters applied to the class level
+ filterHelper = new FilterHelper( persistentClass.getFilterMap(), factory.getDialect(), factory.getSqlFunctionRegistry() );
+
+ temporaryIdTableName = persistentClass.getTemporaryIdTableName();
+ temporaryIdTableDDL = persistentClass.getTemporaryIdTableDDL();
+ }
+
+ protected String generateLazySelectString() {
+
+ if ( !entityMetamodel.hasLazyProperties() ) {
+ return null;
+ }
+
+ HashSet tableNumbers = new HashSet();
+ ArrayList columnNumbers = new ArrayList();
+ ArrayList formulaNumbers = new ArrayList();
+ for ( int i = 0; i < lazyPropertyNames.length; i++ ) {
+ // all this only really needs to consider properties
+ // of this class, not its subclasses, but since we
+ // are reusing code used for sequential selects, we
+ // use the subclass closure
+ int propertyNumber = getSubclassPropertyIndex( lazyPropertyNames[i] );
+
+ int tableNumber = getSubclassPropertyTableNumber( propertyNumber );
+ tableNumbers.add( new Integer( tableNumber ) );
+
+ int[] colNumbers = subclassPropertyColumnNumberClosure[propertyNumber];
+ for ( int j = 0; j < colNumbers.length; j++ ) {
+ if ( colNumbers[j]!=-1 ) {
+ columnNumbers.add( new Integer( colNumbers[j] ) );
+ }
+ }
+ int[] formNumbers = subclassPropertyFormulaNumberClosure[propertyNumber];
+ for ( int j = 0; j < formNumbers.length; j++ ) {
+ if ( formNumbers[j]!=-1 ) {
+ formulaNumbers.add( new Integer( formNumbers[j] ) );
+ }
+ }
+ }
+
+ if ( columnNumbers.size()==0 && formulaNumbers.size()==0 ) {
+ // only one-to-one is lazy fetched
+ return null;
+ }
+
+ return renderSelect( ArrayHelper.toIntArray( tableNumbers ),
+ ArrayHelper.toIntArray( columnNumbers ),
+ ArrayHelper.toIntArray( formulaNumbers ) );
+
+ }
+
+ public Object initializeLazyProperty(String fieldName, Object entity, SessionImplementor session)
+ throws HibernateException {
+
+ final Serializable id = session.getContextEntityIdentifier( entity );
+
+ final EntityEntry entry = session.getPersistenceContext().getEntry( entity );
+ if ( entry == null ) {
+ throw new HibernateException( "entity is not associated with the session: " + id );
+ }
+
+ if ( log.isTraceEnabled() ) {
+ log.trace(
+ "initializing lazy properties of: " +
+ MessageHelper.infoString( this, id, getFactory() ) +
+ ", field access: " + fieldName
+ );
+ }
+
+ if ( hasCache() ) {
+ CacheKey cacheKey = new CacheKey(id, getIdentifierType(), getEntityName(), session.getEntityMode(), getFactory() );
+ Object ce = getCacheAccessStrategy().get( cacheKey, session.getTimestamp() );
+ if (ce!=null) {
+ CacheEntry cacheEntry = (CacheEntry) getCacheEntryStructure().destructure(ce, factory);
+ if ( !cacheEntry.areLazyPropertiesUnfetched() ) {
+ //note early exit here:
+ return initializeLazyPropertiesFromCache( fieldName, entity, session, entry, cacheEntry );
+ }
+ }
+ }
+
+ return initializeLazyPropertiesFromDatastore( fieldName, entity, session, id, entry );
+
+ }
+
+ private Object initializeLazyPropertiesFromDatastore(
+ final String fieldName,
+ final Object entity,
+ final SessionImplementor session,
+ final Serializable id,
+ final EntityEntry entry) {
+
+ if ( !hasLazyProperties() ) {
+ throw new AssertionFailure("no lazy properties");
+ }
+
+ log.trace("initializing lazy properties from datastore");
+
+ try {
+
+ Object result = null;
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+ try {
+ final String lazySelect = getSQLLazySelectString();
+ if ( lazySelect != null ) {
+ // null sql means that the only lazy properties
+ // are shared PK one-to-one associations which are
+ // handled differently in the Type#nullSafeGet code...
+ ps = session.getBatcher().prepareSelectStatement(lazySelect);
+ getIdentifierType().nullSafeSet( ps, id, 1, session );
+ rs = session.getBatcher().getResultSet( ps );
+ rs.next();
+ }
+ final Object[] snapshot = entry.getLoadedState();
+ for ( int j = 0; j < lazyPropertyNames.length; j++ ) {
+ Object propValue = lazyPropertyTypes[j].nullSafeGet( rs, lazyPropertyColumnAliases[j], session, entity );
+ if ( initializeLazyProperty( fieldName, entity, session, snapshot, j, propValue ) ) {
+ result = propValue;
+ }
+ }
+ }
+ finally {
+ if ( rs != null ) {
+ session.getBatcher().closeQueryStatement( ps, rs );
+ }
+ else if ( ps != null ) {
+ session.getBatcher().closeStatement( ps );
+ }
+ }
+
+ log.trace( "done initializing lazy properties" );
+
+ return result;
+
+ }
+ catch ( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ getFactory().getSQLExceptionConverter(),
+ sqle,
+ "could not initialize lazy properties: " +
+ MessageHelper.infoString( this, id, getFactory() ),
+ getSQLLazySelectString()
+ );
+ }
+ }
+
+ private Object initializeLazyPropertiesFromCache(
+ final String fieldName,
+ final Object entity,
+ final SessionImplementor session,
+ final EntityEntry entry,
+ final CacheEntry cacheEntry
+ ) {
+
+ log.trace("initializing lazy properties from second-level cache");
+
+ Object result = null;
+ Serializable[] disassembledValues = cacheEntry.getDisassembledState();
+ final Object[] snapshot = entry.getLoadedState();
+ for ( int j = 0; j < lazyPropertyNames.length; j++ ) {
+ final Object propValue = lazyPropertyTypes[j].assemble(
+ disassembledValues[ lazyPropertyNumbers[j] ],
+ session,
+ entity
+ );
+ if ( initializeLazyProperty( fieldName, entity, session, snapshot, j, propValue ) ) {
+ result = propValue;
+ }
+ }
+
+ log.trace( "done initializing lazy properties" );
+
+ return result;
+ }
+
+ private boolean initializeLazyProperty(
+ final String fieldName,
+ final Object entity,
+ final SessionImplementor session,
+ final Object[] snapshot,
+ final int j,
+ final Object propValue) {
+ setPropertyValue( entity, lazyPropertyNumbers[j], propValue, session.getEntityMode() );
+ if (snapshot != null) {
+ // object have been loaded with setReadOnly(true); HHH-2236
+ snapshot[ lazyPropertyNumbers[j] ] = lazyPropertyTypes[j].deepCopy( propValue, session.getEntityMode(), factory );
+ }
+ return fieldName.equals( lazyPropertyNames[j] );
+ }
+
+ public boolean isBatchable() {
+ return optimisticLockMode()==Versioning.OPTIMISTIC_LOCK_NONE ||
+ ( !isVersioned() && optimisticLockMode()==Versioning.OPTIMISTIC_LOCK_VERSION ) ||
+ getFactory().getSettings().isJdbcBatchVersionedData();
+ }
+
+ public Serializable[] getQuerySpaces() {
+ return getPropertySpaces();
+ }
+
+ protected Set getLazyProperties() {
+ return lazyProperties;
+ }
+
+ public boolean isBatchLoadable() {
+ return batchSize > 1;
+ }
+
+ public String[] getIdentifierColumnNames() {
+ return rootTableKeyColumnNames;
+ }
+
+ protected int getIdentifierColumnSpan() {
+ return identifierColumnSpan;
+ }
+
+ protected String[] getIdentifierAliases() {
+ return identifierAliases;
+ }
+
+ public String getVersionColumnName() {
+ return versionColumnName;
+ }
+
+ protected String getVersionedTableName() {
+ return getTableName( 0 );
+ }
+
+ protected boolean[] getSubclassColumnLazyiness() {
+ return subclassColumnLazyClosure;
+ }
+
+ protected boolean[] getSubclassFormulaLazyiness() {
+ return subclassFormulaLazyClosure;
+ }
+
+ /**
+ * We can't immediately add to the cache if we have formulas
+ * which must be evaluated, or if we have the possibility of
+ * two concurrent updates to the same item being merged on
+ * the database. This can happen if (a) the item is not
+ * versioned and either (b) we have dynamic update enabled
+ * or (c) we have multiple tables holding the state of the
+ * item.
+ */
+ public boolean isCacheInvalidationRequired() {
+ return hasFormulaProperties() ||
+ ( !isVersioned() && ( entityMetamodel.isDynamicUpdate() || getTableSpan() > 1 ) );
+ }
+
+ public boolean isLazyPropertiesCacheable() {
+ return isLazyPropertiesCacheable;
+ }
+
+ public String selectFragment(String alias, String suffix) {
+ return identifierSelectFragment( alias, suffix ) +
+ propertySelectFragment( alias, suffix, false );
+ }
+
+ public String[] getIdentifierAliases(String suffix) {
+ // NOTE: this assumes something about how propertySelectFragment is implemented by the subclass!
+ // was toUnqotedAliasStrings( getIdentiferColumnNames() ) before - now tried
+ // to remove that unqoting and missing aliases..
+ return new Alias( suffix ).toAliasStrings( getIdentifierAliases() );
+ }
+
+ public String[] getPropertyAliases(String suffix, int i) {
+ // NOTE: this assumes something about how propertySelectFragment is implemented by the subclass!
+ return new Alias( suffix ).toUnquotedAliasStrings( propertyColumnAliases[i] );
+ }
+
+ public String getDiscriminatorAlias(String suffix) {
+ // NOTE: this assumes something about how propertySelectFragment is implemented by the subclass!
+ // was toUnqotedAliasStrings( getdiscriminatorColumnName() ) before - now tried
+ // to remove that unqoting and missing aliases..
+ return entityMetamodel.hasSubclasses() ?
+ new Alias( suffix ).toAliasString( getDiscriminatorAlias() ) :
+ null;
+ }
+
+ public String identifierSelectFragment(String name, String suffix) {
+ return new SelectFragment()
+ .setSuffix( suffix )
+ .addColumns( name, getIdentifierColumnNames(), getIdentifierAliases() )
+ .toFragmentString()
+ .substring( 2 ); //strip leading ", "
+ }
+
+
+ public String propertySelectFragment(String name, String suffix, boolean allProperties) {
+
+ SelectFragment select = new SelectFragment()
+ .setSuffix( suffix )
+ .setUsedAliases( getIdentifierAliases() );
+
+ int[] columnTableNumbers = getSubclassColumnTableNumberClosure();
+ String[] columnAliases = getSubclassColumnAliasClosure();
+ String[] columns = getSubclassColumnClosure();
+ for ( int i = 0; i < getSubclassColumnClosure().length; i++ ) {
+ boolean selectable = ( allProperties || !subclassColumnLazyClosure[i] ) &&
+ !isSubclassTableSequentialSelect( columnTableNumbers[i] ) &&
+ subclassColumnSelectableClosure[i];
+ if ( selectable ) {
+ String subalias = generateTableAlias( name, columnTableNumbers[i] );
+ select.addColumn( subalias, columns[i], columnAliases[i] );
+ }
+ }
+
+ int[] formulaTableNumbers = getSubclassFormulaTableNumberClosure();
+ String[] formulaTemplates = getSubclassFormulaTemplateClosure();
+ String[] formulaAliases = getSubclassFormulaAliasClosure();
+ for ( int i = 0; i < getSubclassFormulaTemplateClosure().length; i++ ) {
+ boolean selectable = ( allProperties || !subclassFormulaLazyClosure[i] )
+ && !isSubclassTableSequentialSelect( formulaTableNumbers[i] );
+ if ( selectable ) {
+ String subalias = generateTableAlias( name, formulaTableNumbers[i] );
+ select.addFormula( subalias, formulaTemplates[i], formulaAliases[i] );
+ }
+ }
+
+ if ( entityMetamodel.hasSubclasses() ) {
+ addDiscriminatorToSelect( select, name, suffix );
+ }
+
+ if ( hasRowId() ) {
+ select.addColumn( name, rowIdName, ROWID_ALIAS );
+ }
+
+ return select.toFragmentString();
+ }
+
+ public Object[] getDatabaseSnapshot(Serializable id, SessionImplementor session)
+ throws HibernateException {
+
+ if ( log.isTraceEnabled() ) {
+ log.trace( "Getting current persistent state for: " + MessageHelper.infoString( this, id, getFactory() ) );
+ }
+
+ try {
+ PreparedStatement ps = session.getBatcher().prepareSelectStatement( getSQLSnapshotSelectString() );
+ try {
+ getIdentifierType().nullSafeSet( ps, id, 1, session );
+ //if ( isVersioned() ) getVersionType().nullSafeSet( ps, version, getIdentifierColumnSpan()+1, session );
+ ResultSet rs = ps.executeQuery();
+ try {
+ //if there is no resulting row, return null
+ if ( !rs.next() ) {
+ return null;
+ }
+
+ //otherwise return the "hydrated" state (ie. associations are not resolved)
+ Type[] types = getPropertyTypes();
+ Object[] values = new Object[types.length];
+ boolean[] includeProperty = getPropertyUpdateability();
+ for ( int i = 0; i < types.length; i++ ) {
+ if ( includeProperty[i] ) {
+ values[i] = types[i].hydrate( rs, getPropertyAliases( "", i ), session, null ); //null owner ok??
+ }
+ }
+ return values;
+ }
+ finally {
+ rs.close();
+ }
+ }
+ finally {
+ session.getBatcher().closeStatement( ps );
+ }
+ }
+ catch ( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ getFactory().getSQLExceptionConverter(),
+ sqle,
+ "could not retrieve snapshot: " +
+ MessageHelper.infoString( this, id, getFactory() ),
+ getSQLSnapshotSelectString()
+ );
+ }
+
+ }
+
+ /**
+ * Generate the SQL that selects the version number by id
+ */
+ protected String generateSelectVersionString() {
+ SimpleSelect select = new SimpleSelect( getFactory().getDialect() )
+ .setTableName( getVersionedTableName() );
+ if ( isVersioned() ) {
+ select.addColumn( versionColumnName );
+ }
+ else {
+ select.addColumns( rootTableKeyColumnNames );
+ }
+ if ( getFactory().getSettings().isCommentsEnabled() ) {
+ select.setComment( "get version " + getEntityName() );
+ }
+ return select.addCondition( rootTableKeyColumnNames, "=?" ).toStatementString();
+ }
+
+ protected String generateInsertGeneratedValuesSelectString() {
+ return generateGeneratedValuesSelectString( getPropertyInsertGenerationInclusions() );
+ }
+
+ protected String generateUpdateGeneratedValuesSelectString() {
+ return generateGeneratedValuesSelectString( getPropertyUpdateGenerationInclusions() );
+ }
+
+ private String generateGeneratedValuesSelectString(ValueInclusion[] inclusions) {
+ Select select = new Select( getFactory().getDialect() );
+
+ if ( getFactory().getSettings().isCommentsEnabled() ) {
+ select.setComment( "get generated state " + getEntityName() );
+ }
+
+ String[] aliasedIdColumns = StringHelper.qualify( getRootAlias(), getIdentifierColumnNames() );
+
+ // Here we render the select column list based on the properties defined as being generated.
+ // For partial component generation, we currently just re-select the whole component
+ // rather than trying to handle the individual generated portions.
+ String selectClause = concretePropertySelectFragment( getRootAlias(), inclusions );
+ selectClause = selectClause.substring( 2 );
+
+ String fromClause = fromTableFragment( getRootAlias() ) +
+ fromJoinFragment( getRootAlias(), true, false );
+
+ String whereClause = new StringBuffer()
+ .append( StringHelper.join( "=? and ", aliasedIdColumns ) )
+ .append( "=?" )
+ .append( whereJoinFragment( getRootAlias(), true, false ) )
+ .toString();
+
+ return select.setSelectClause( selectClause )
+ .setFromClause( fromClause )
+ .setOuterJoins( "", "" )
+ .setWhereClause( whereClause )
+ .toStatementString();
+ }
+
+ protected static interface InclusionChecker {
+ public boolean includeProperty(int propertyNumber);
+ }
+
+ protected String concretePropertySelectFragment(String alias, final ValueInclusion[] inclusions) {
+ return concretePropertySelectFragment(
+ alias,
+ new InclusionChecker() {
+ // TODO : currently we really do not handle ValueInclusion.PARTIAL...
+ // ValueInclusion.PARTIAL would indicate parts of a component need to
+ // be included in the select; currently we then just render the entire
+ // component into the select clause in that case.
+ public boolean includeProperty(int propertyNumber) {
+ return inclusions[propertyNumber] != ValueInclusion.NONE;
+ }
+ }
+ );
+ }
+
+ protected String concretePropertySelectFragment(String alias, final boolean[] includeProperty) {
+ return concretePropertySelectFragment(
+ alias,
+ new InclusionChecker() {
+ public boolean includeProperty(int propertyNumber) {
+ return includeProperty[propertyNumber];
+ }
+ }
+ );
+ }
+
+ protected String concretePropertySelectFragment(String alias, InclusionChecker inclusionChecker) {
+ int propertyCount = getPropertyNames().length;
+ int[] propertyTableNumbers = getPropertyTableNumbersInSelect();
+ SelectFragment frag = new SelectFragment();
+ for ( int i = 0; i < propertyCount; i++ ) {
+ if ( inclusionChecker.includeProperty( i ) ) {
+ frag.addColumns(
+ generateTableAlias( alias, propertyTableNumbers[i] ),
+ propertyColumnNames[i],
+ propertyColumnAliases[i]
+ );
+ frag.addFormulas(
+ generateTableAlias( alias, propertyTableNumbers[i] ),
+ propertyColumnFormulaTemplates[i],
+ propertyColumnAliases[i]
+ );
+ }
+ }
+ return frag.toFragmentString();
+ }
+
+ protected String generateSnapshotSelectString() {
+
+ //TODO: should we use SELECT .. FOR UPDATE?
+
+ Select select = new Select( getFactory().getDialect() );
+
+ if ( getFactory().getSettings().isCommentsEnabled() ) {
+ select.setComment( "get current state " + getEntityName() );
+ }
+
+ String[] aliasedIdColumns = StringHelper.qualify( getRootAlias(), getIdentifierColumnNames() );
+ String selectClause = StringHelper.join( ", ", aliasedIdColumns ) +
+ concretePropertySelectFragment( getRootAlias(), getPropertyUpdateability() );
+
+ String fromClause = fromTableFragment( getRootAlias() ) +
+ fromJoinFragment( getRootAlias(), true, false );
+
+ String whereClause = new StringBuffer()
+ .append( StringHelper.join( "=? and ",
+ aliasedIdColumns ) )
+ .append( "=?" )
+ .append( whereJoinFragment( getRootAlias(), true, false ) )
+ .toString();
+
+ /*if ( isVersioned() ) {
+ where.append(" and ")
+ .append( getVersionColumnName() )
+ .append("=?");
+ }*/
+
+ return select.setSelectClause( selectClause )
+ .setFromClause( fromClause )
+ .setOuterJoins( "", "" )
+ .setWhereClause( whereClause )
+ .toStatementString();
+ }
+
+ public Object forceVersionIncrement(Serializable id, Object currentVersion, SessionImplementor session) {
+ if ( !isVersioned() ) {
+ throw new AssertionFailure( "cannot force version increment on non-versioned entity" );
+ }
+
+ if ( isVersionPropertyGenerated() ) {
+ // the difficulty here is exactly what do we update in order to
+ // force the version to be incremented in the db...
+ throw new HibernateException( "LockMode.FORCE is currently not supported for generated version properties" );
+ }
+
+ Object nextVersion = getVersionType().next( currentVersion, session );
+ if ( log.isTraceEnabled() ) {
+ log.trace(
+ "Forcing version increment [" + MessageHelper.infoString( this, id, getFactory() ) +
+ "; " + getVersionType().toLoggableString( currentVersion, getFactory() ) +
+ " -> " + getVersionType().toLoggableString( nextVersion, getFactory() ) + "]"
+ );
+ }
+
+ // todo : cache this sql...
+ String versionIncrementString = generateVersionIncrementUpdateString();
+ PreparedStatement st = null;
+ try {
+ try {
+ st = session.getBatcher().prepareStatement( versionIncrementString );
+ getVersionType().nullSafeSet( st, nextVersion, 1, session );
+ getIdentifierType().nullSafeSet( st, id, 2, session );
+ getVersionType().nullSafeSet( st, currentVersion, 2 + getIdentifierColumnSpan(), session );
+ int rows = st.executeUpdate();
+ if ( rows != 1 ) {
+ throw new StaleObjectStateException( getEntityName(), id );
+ }
+ }
+ finally {
+ session.getBatcher().closeStatement( st );
+ }
+ }
+ catch ( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ getFactory().getSQLExceptionConverter(),
+ sqle,
+ "could not retrieve version: " +
+ MessageHelper.infoString( this, id, getFactory() ),
+ getVersionSelectString()
+ );
+ }
+
+ return nextVersion;
+ }
+
+ private String generateVersionIncrementUpdateString() {
+ Update update = new Update( getFactory().getDialect() );
+ update.setTableName( getTableName( 0 ) );
+ if ( getFactory().getSettings().isCommentsEnabled() ) {
+ update.setComment( "forced version increment" );
+ }
+ update.addColumn( getVersionColumnName() );
+ update.setPrimaryKeyColumnNames( getIdentifierColumnNames() );
+ update.setVersionColumnName( getVersionColumnName() );
+ return update.toStatementString();
+ }
+
+ /**
+ * Retrieve the version number
+ */
+ public Object getCurrentVersion(Serializable id, SessionImplementor session) throws HibernateException {
+
+ if ( log.isTraceEnabled() ) {
+ log.trace( "Getting version: " + MessageHelper.infoString( this, id, getFactory() ) );
+ }
+
+ try {
+
+ PreparedStatement st = session.getBatcher().prepareSelectStatement( getVersionSelectString() );
+ try {
+ getIdentifierType().nullSafeSet( st, id, 1, session );
+
+ ResultSet rs = st.executeQuery();
+ try {
+ if ( !rs.next() ) {
+ return null;
+ }
+ if ( !isVersioned() ) {
+ return this;
+ }
+ return getVersionType().nullSafeGet( rs, getVersionColumnName(), session, null );
+ }
+ finally {
+ rs.close();
+ }
+ }
+ finally {
+ session.getBatcher().closeStatement( st );
+ }
+
+ }
+ catch ( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ getFactory().getSQLExceptionConverter(),
+ sqle,
+ "could not retrieve version: " +
+ MessageHelper.infoString( this, id, getFactory() ),
+ getVersionSelectString()
+ );
+ }
+
+ }
+
+ protected void initLockers() {
+ lockers.put( LockMode.READ, generateLocker( LockMode.READ ) );
+ lockers.put( LockMode.UPGRADE, generateLocker( LockMode.UPGRADE ) );
+ lockers.put( LockMode.UPGRADE_NOWAIT, generateLocker( LockMode.UPGRADE_NOWAIT ) );
+ lockers.put( LockMode.FORCE, generateLocker( LockMode.FORCE ) );
+ }
+
+ protected LockingStrategy generateLocker(LockMode lockMode) {
+ return factory.getDialect().getLockingStrategy( this, lockMode );
+ }
+
+ private LockingStrategy getLocker(LockMode lockMode) {
+ return ( LockingStrategy ) lockers.get( lockMode );
+ }
+
+ public void lock(
+ Serializable id,
+ Object version,
+ Object object,
+ LockMode lockMode,
+ SessionImplementor session) throws HibernateException {
+ getLocker( lockMode ).lock( id, version, object, session );
+ }
+
+ public String getRootTableName() {
+ return getSubclassTableName( 0 );
+ }
+
+ public String getRootTableAlias(String drivingAlias) {
+ return drivingAlias;
+ }
+
+ public String[] getRootTableIdentifierColumnNames() {
+ return getRootTableKeyColumnNames();
+ }
+
+ public String[] toColumns(String alias, String propertyName) throws QueryException {
+ return propertyMapping.toColumns( alias, propertyName );
+ }
+
+ public String[] toColumns(String propertyName) throws QueryException {
+ return propertyMapping.getColumnNames( propertyName );
+ }
+
+ public Type toType(String propertyName) throws QueryException {
+ return propertyMapping.toType( propertyName );
+ }
+
+ public String[] getPropertyColumnNames(String propertyName) {
+ return propertyMapping.getColumnNames( propertyName );
+ }
+
+ /**
+ * Warning:
+ * When there are duplicated property names in the subclasses
+ * of the class, this method may return the wrong table
+ * number for the duplicated subclass property (note that
+ * SingleTableEntityPersister defines an overloaded form
+ * which takes the entity name.
+ */
+ public int getSubclassPropertyTableNumber(String propertyPath) {
+ String rootPropertyName = StringHelper.root(propertyPath);
+ Type type = propertyMapping.toType(rootPropertyName);
+ if ( type.isAssociationType() ) {
+ AssociationType assocType = ( AssociationType ) type;
+ if ( assocType.useLHSPrimaryKey() ) {
+ // performance op to avoid the array search
+ return 0;
+ }
+ else if ( type.isCollectionType() ) {
+ // properly handle property-ref-based associations
+ rootPropertyName = assocType.getLHSPropertyName();
+ }
+ }
+ //Enable for HHH-440, which we don't like:
+ /*if ( type.isComponentType() && !propertyName.equals(rootPropertyName) ) {
+ String unrooted = StringHelper.unroot(propertyName);
+ int idx = ArrayHelper.indexOf( getSubclassColumnClosure(), unrooted );
+ if ( idx != -1 ) {
+ return getSubclassColumnTableNumberClosure()[idx];
+ }
+ }*/
+ int index = ArrayHelper.indexOf( getSubclassPropertyNameClosure(), rootPropertyName); //TODO: optimize this better!
+ return index==-1 ? 0 : getSubclassPropertyTableNumber(index);
+ }
+
+ public Declarer getSubclassPropertyDeclarer(String propertyPath) {
+ int tableIndex = getSubclassPropertyTableNumber( propertyPath );
+ if ( tableIndex == 0 ) {
+ return Declarer.CLASS;
+ }
+ else if ( isClassOrSuperclassTable( tableIndex ) ) {
+ return Declarer.SUPERCLASS;
+ }
+ else {
+ return Declarer.SUBCLASS;
+ }
+ }
+
+ protected String generateTableAlias(String rootAlias, int tableNumber) {
+ if ( tableNumber == 0 ) {
+ return rootAlias;
+ }
+ StringBuffer buf = new StringBuffer().append( rootAlias );
+ if ( !rootAlias.endsWith( "_" ) ) {
+ buf.append( '_' );
+ }
+ return buf.append( tableNumber ).append( '_' ).toString();
+ }
+
+ public String[] toColumns(String name, final int i) {
+ final String alias = generateTableAlias( name, getSubclassPropertyTableNumber( i ) );
+ String[] cols = getSubclassPropertyColumnNames( i );
+ String[] templates = getSubclassPropertyFormulaTemplateClosure()[i];
+ String[] result = new String[cols.length];
+ for ( int j = 0; j < cols.length; j++ ) {
+ if ( cols[j] == null ) {
+ result[j] = StringHelper.replace( templates[j], Template.TEMPLATE, alias );
+ }
+ else {
+ result[j] = StringHelper.qualify( alias, cols[j] );
+ }
+ }
+ return result;
+ }
+
+ private int getSubclassPropertyIndex(String propertyName) {
+ return ArrayHelper.indexOf(subclassPropertyNameClosure, propertyName);
+ }
+
+ protected String[] getPropertySubclassNames() {
+ return propertySubclassNames;
+ }
+
+ public String[] getPropertyColumnNames(int i) {
+ return propertyColumnNames[i];
+ }
+
+ protected int getPropertyColumnSpan(int i) {
+ return propertyColumnSpans[i];
+ }
+
+ protected boolean hasFormulaProperties() {
+ return hasFormulaProperties;
+ }
+
+ public FetchMode getFetchMode(int i) {
+ return subclassPropertyFetchModeClosure[i];
+ }
+
+ public CascadeStyle getCascadeStyle(int i) {
+ return subclassPropertyCascadeStyleClosure[i];
+ }
+
+ public Type getSubclassPropertyType(int i) {
+ return subclassPropertyTypeClosure[i];
+ }
+
+ public String getSubclassPropertyName(int i) {
+ return subclassPropertyNameClosure[i];
+ }
+
+ public int countSubclassProperties() {
+ return subclassPropertyTypeClosure.length;
+ }
+
+ public String[] getSubclassPropertyColumnNames(int i) {
+ return subclassPropertyColumnNameClosure[i];
+ }
+
+ public boolean isDefinedOnSubclass(int i) {
+ return propertyDefinedOnSubclass[i];
+ }
+
+ protected String[][] getSubclassPropertyFormulaTemplateClosure() {
+ return subclassPropertyFormulaTemplateClosure;
+ }
+
+ protected Type[] getSubclassPropertyTypeClosure() {
+ return subclassPropertyTypeClosure;
+ }
+
+ protected String[][] getSubclassPropertyColumnNameClosure() {
+ return subclassPropertyColumnNameClosure;
+ }
+
+ protected String[] getSubclassPropertyNameClosure() {
+ return subclassPropertyNameClosure;
+ }
+
+ protected String[] getSubclassPropertySubclassNameClosure() {
+ return subclassPropertySubclassNameClosure;
+ }
+
+ protected String[] getSubclassColumnClosure() {
+ return subclassColumnClosure;
+ }
+
+ protected String[] getSubclassColumnAliasClosure() {
+ return subclassColumnAliasClosure;
+ }
+
+ protected String[] getSubclassFormulaClosure() {
+ return subclassFormulaClosure;
+ }
+
+ protected String[] getSubclassFormulaTemplateClosure() {
+ return subclassFormulaTemplateClosure;
+ }
+
+ protected String[] getSubclassFormulaAliasClosure() {
+ return subclassFormulaAliasClosure;
+ }
+
+ public String[] getSubclassPropertyColumnAliases(String propertyName, String suffix) {
+ String rawAliases[] = ( String[] ) subclassPropertyAliases.get( propertyName );
+
+ if ( rawAliases == null ) {
+ return null;
+ }
+
+ String result[] = new String[rawAliases.length];
+ for ( int i = 0; i < rawAliases.length; i++ ) {
+ result[i] = new Alias( suffix ).toUnquotedAliasString( rawAliases[i] );
+ }
+ return result;
+ }
+
+ public String[] getSubclassPropertyColumnNames(String propertyName) {
+ //TODO: should we allow suffixes on these ?
+ return ( String[] ) subclassPropertyColumnNames.get( propertyName );
+ }
+
+
+
+ //This is really ugly, but necessary:
+ /**
+ * Must be called by subclasses, at the end of their constructors
+ */
+ protected void initSubclassPropertyAliasesMap(PersistentClass model) throws MappingException {
+
+ // ALIASES
+ internalInitSubclassPropertyAliasesMap( null, model.getSubclassPropertyClosureIterator() );
+
+ // aliases for identifier ( alias.id ); skip if the entity defines a non-id property named 'id'
+ if ( ! entityMetamodel.hasNonIdentifierPropertyNamedId() ) {
+ subclassPropertyAliases.put( ENTITY_ID, getIdentifierAliases() );
+ subclassPropertyColumnNames.put( ENTITY_ID, getIdentifierColumnNames() );
+ }
+
+ // aliases named identifier ( alias.idname )
+ if ( hasIdentifierProperty() ) {
+ subclassPropertyAliases.put( getIdentifierPropertyName(), getIdentifierAliases() );
+ subclassPropertyColumnNames.put( getIdentifierPropertyName(), getIdentifierColumnNames() );
+ }
+
+ // aliases for composite-id's
+ if ( getIdentifierType().isComponentType() ) {
+ // Fetch embedded identifiers propertynames from the "virtual" identifier component
+ AbstractComponentType componentId = ( AbstractComponentType ) getIdentifierType();
+ String[] idPropertyNames = componentId.getPropertyNames();
+ String[] idAliases = getIdentifierAliases();
+ String[] idColumnNames = getIdentifierColumnNames();
+
+ for ( int i = 0; i < idPropertyNames.length; i++ ) {
+ if ( entityMetamodel.hasNonIdentifierPropertyNamedId() ) {
+ subclassPropertyAliases.put(
+ ENTITY_ID + "." + idPropertyNames[i],
+ new String[] { idAliases[i] }
+ );
+ subclassPropertyColumnNames.put(
+ ENTITY_ID + "." + getIdentifierPropertyName() + "." + idPropertyNames[i],
+ new String[] { idColumnNames[i] }
+ );
+ }
+// if (hasIdentifierProperty() && !ENTITY_ID.equals( getIdentifierPropertyName() ) ) {
+ if ( hasIdentifierProperty() ) {
+ subclassPropertyAliases.put(
+ getIdentifierPropertyName() + "." + idPropertyNames[i],
+ new String[] { idAliases[i] }
+ );
+ subclassPropertyColumnNames.put(
+ getIdentifierPropertyName() + "." + idPropertyNames[i],
+ new String[] { idColumnNames[i] }
+ );
+ }
+ else {
+ // embedded composite ids ( alias.idname1, alias.idname2 )
+ subclassPropertyAliases.put( idPropertyNames[i], new String[] { idAliases[i] } );
+ subclassPropertyColumnNames.put( idPropertyNames[i], new String[] { idColumnNames[i] } );
+ }
+ }
+ }
+
+ if ( entityMetamodel.isPolymorphic() ) {
+ subclassPropertyAliases.put( ENTITY_CLASS, new String[] { getDiscriminatorAlias() } );
+ subclassPropertyColumnNames.put( ENTITY_CLASS, new String[] { getDiscriminatorColumnName() } );
+ }
+
+ }
+
+ private void internalInitSubclassPropertyAliasesMap(String path, Iterator propertyIterator) {
+ while ( propertyIterator.hasNext() ) {
+
+ Property prop = ( Property ) propertyIterator.next();
+ String propname = path == null ? prop.getName() : path + "." + prop.getName();
+ if ( prop.isComposite() ) {
+ Component component = ( Component ) prop.getValue();
+ Iterator compProps = component.getPropertyIterator();
+ internalInitSubclassPropertyAliasesMap( propname, compProps );
+ }
+ else {
+ String[] aliases = new String[prop.getColumnSpan()];
+ String[] cols = new String[prop.getColumnSpan()];
+ Iterator colIter = prop.getColumnIterator();
+ int l = 0;
+ while ( colIter.hasNext() ) {
+ Selectable thing = ( Selectable ) colIter.next();
+ aliases[l] = thing.getAlias( getFactory().getDialect(), prop.getValue().getTable() );
+ cols[l] = thing.getText( getFactory().getDialect() ); // TODO: skip formulas?
+ l++;
+ }
+
+ subclassPropertyAliases.put( propname, aliases );
+ subclassPropertyColumnNames.put( propname, cols );
+ }
+ }
+
+ }
+
+ public Object loadByUniqueKey(String propertyName, Object uniqueKey, SessionImplementor session)
+ throws HibernateException {
+ return getAppropriateUniqueKeyLoader( propertyName, session.getEnabledFilters() )
+ .loadByUniqueKey( session, uniqueKey );
+ }
+
+ private EntityLoader getAppropriateUniqueKeyLoader(String propertyName, Map enabledFilters) {
+
+ final boolean useStaticLoader = ( enabledFilters == null || enabledFilters.isEmpty() )
+ && propertyName.indexOf('.')<0; //ugly little workaround for fact that createUniqueKeyLoaders() does not handle component properties
+
+ if ( useStaticLoader ) {
+ return (EntityLoader) uniqueKeyLoaders.get( propertyName );
+ }
+ else {
+ return createUniqueKeyLoader(
+ propertyMapping.toType(propertyName),
+ propertyMapping.toColumns(propertyName),
+ enabledFilters
+ );
+ }
+ }
+
+ public int getPropertyIndex(String propertyName) {
+ return entityMetamodel.getPropertyIndex(propertyName);
+ }
+
+ protected void createUniqueKeyLoaders() throws MappingException {
+ Type[] propertyTypes = getPropertyTypes();
+ String[] propertyNames = getPropertyNames();
+ for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) {
+ if ( propertyUniqueness[i] ) {
+ //don't need filters for the static loaders
+ uniqueKeyLoaders.put(
+ propertyNames[i],
+ createUniqueKeyLoader(
+ propertyTypes[i],
+ getPropertyColumnNames( i ),
+ CollectionHelper.EMPTY_MAP
+ )
+ );
+ //TODO: create uk loaders for component properties
+ }
+ }
+ }
+
+ private EntityLoader createUniqueKeyLoader(Type uniqueKeyType, String[] columns, Map enabledFilters) {
+ if ( uniqueKeyType.isEntityType() ) {
+ String className = ( ( EntityType ) uniqueKeyType ).getAssociatedEntityName();
+ uniqueKeyType = getFactory().getEntityPersister( className ).getIdentifierType();
+ }
+
+ return new EntityLoader( this, columns, uniqueKeyType, 1, LockMode.NONE, getFactory(), enabledFilters );
+ }
+
+ protected String getSQLWhereString(String alias) {
+ return StringHelper.replace( sqlWhereStringTemplate, Template.TEMPLATE, alias );
+ }
+
+ protected boolean hasWhere() {
+ return sqlWhereString != null;
+ }
+
+ private void initOrdinaryPropertyPaths(Mapping mapping) throws MappingException {
+ for ( int i = 0; i < getSubclassPropertyNameClosure().length; i++ ) {
+ propertyMapping.initPropertyPaths( getSubclassPropertyNameClosure()[i],
+ getSubclassPropertyTypeClosure()[i],
+ getSubclassPropertyColumnNameClosure()[i],
+ getSubclassPropertyFormulaTemplateClosure()[i],
+ mapping );
+ }
+ }
+
+ private void initIdentifierPropertyPaths(Mapping mapping) throws MappingException {
+ String idProp = getIdentifierPropertyName();
+ if ( idProp != null ) {
+ propertyMapping.initPropertyPaths( idProp, getIdentifierType(), getIdentifierColumnNames(), null, mapping );
+ }
+ if ( entityMetamodel.getIdentifierProperty().isEmbedded() ) {
+ propertyMapping.initPropertyPaths( null, getIdentifierType(), getIdentifierColumnNames(), null, mapping );
+ }
+ if ( ! entityMetamodel.hasNonIdentifierPropertyNamedId() ) {
+ propertyMapping.initPropertyPaths( ENTITY_ID, getIdentifierType(), getIdentifierColumnNames(), null, mapping );
+ }
+ }
+
+ private void initDiscriminatorPropertyPath(Mapping mapping) throws MappingException {
+ propertyMapping.initPropertyPaths( ENTITY_CLASS,
+ getDiscriminatorType(),
+ new String[]{getDiscriminatorColumnName()},
+ new String[]{getDiscriminatorFormulaTemplate()},
+ getFactory() );
+ }
+
+ protected void initPropertyPaths(Mapping mapping) throws MappingException {
+ initOrdinaryPropertyPaths(mapping);
+ initOrdinaryPropertyPaths(mapping); //do two passes, for collection property-ref!
+ initIdentifierPropertyPaths(mapping);
+ if ( entityMetamodel.isPolymorphic() ) {
+ initDiscriminatorPropertyPath( mapping );
+ }
+ }
+
+ protected UniqueEntityLoader createEntityLoader(LockMode lockMode, Map enabledFilters) throws MappingException {
+ //TODO: disable batch loading if lockMode > READ?
+ return BatchingEntityLoader.createBatchingEntityLoader( this, batchSize, lockMode, getFactory(), enabledFilters );
+ }
+
+ protected UniqueEntityLoader createEntityLoader(LockMode lockMode) throws MappingException {
+ return createEntityLoader( lockMode, CollectionHelper.EMPTY_MAP );
+ }
+
+ protected boolean check(int rows, Serializable id, int tableNumber, Expectation expectation, PreparedStatement statement) throws HibernateException {
+ try {
+ expectation.verifyOutcome( rows, statement, -1 );
+ }
+ catch( StaleStateException e ) {
+ if ( !isNullableTable( tableNumber ) ) {
+ if ( getFactory().getStatistics().isStatisticsEnabled() ) {
+ getFactory().getStatisticsImplementor()
+ .optimisticFailure( getEntityName() );
+ }
+ throw new StaleObjectStateException( getEntityName(), id );
+ }
+ }
+ catch( TooManyRowsAffectedException e ) {
+ throw new HibernateException(
+ "Duplicate identifier in table for: " +
+ MessageHelper.infoString( this, id, getFactory() )
+ );
+ }
+ catch ( Throwable t ) {
+ return false;
+ }
+ return true;
+ }
+
+ protected String generateUpdateString(boolean[] includeProperty, int j, boolean useRowId) {
+ return generateUpdateString( includeProperty, j, null, useRowId );
+ }
+
+ /**
+ * Generate the SQL that updates a row by id (and version)
+ */
+ protected String generateUpdateString(final boolean[] includeProperty,
+ final int j,
+ final Object[] oldFields,
+ final boolean useRowId) {
+
+ Update update = new Update( getFactory().getDialect() ).setTableName( getTableName( j ) );
+
+ // select the correct row by either pk or rowid
+ if ( useRowId ) {
+ update.setPrimaryKeyColumnNames( new String[]{rowIdName} ); //TODO: eventually, rowIdName[j]
+ }
+ else {
+ update.setPrimaryKeyColumnNames( getKeyColumns( j ) );
+ }
+
+ boolean hasColumns = false;
+ for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) {
+ if ( includeProperty[i] && isPropertyOfTable( i, j ) ) {
+ // this is a property of the table, which we are updating
+ update.addColumns( getPropertyColumnNames(i), propertyColumnUpdateable[i] );
+ hasColumns = hasColumns || getPropertyColumnSpan( i ) > 0;
+ }
+ }
+
+ if ( j == 0 && isVersioned() && entityMetamodel.getOptimisticLockMode() == Versioning.OPTIMISTIC_LOCK_VERSION ) {
+ // this is the root (versioned) table, and we are using version-based
+ // optimistic locking; if we are not updating the version, also don't
+ // check it (unless this is a "generated" version column)!
+ if ( checkVersion( includeProperty ) ) {
+ update.setVersionColumnName( getVersionColumnName() );
+ hasColumns = true;
+ }
+ }
+ else if ( entityMetamodel.getOptimisticLockMode() > Versioning.OPTIMISTIC_LOCK_VERSION && oldFields != null ) {
+ // we are using "all" or "dirty" property-based optimistic locking
+
+ boolean[] includeInWhere = entityMetamodel.getOptimisticLockMode() == Versioning.OPTIMISTIC_LOCK_ALL ?
+ getPropertyUpdateability() : //optimistic-lock="all", include all updatable properties
+ includeProperty; //optimistic-lock="dirty", include all properties we are updating this time
+
+ boolean[] versionability = getPropertyVersionability();
+ Type[] types = getPropertyTypes();
+ for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) {
+ boolean include = includeInWhere[i] &&
+ isPropertyOfTable( i, j ) &&
+ versionability[i];
+ if ( include ) {
+ // this property belongs to the table, and it is not specifically
+ // excluded from optimistic locking by optimistic-lock="false"
+ String[] propertyColumnNames = getPropertyColumnNames( i );
+ boolean[] propertyNullness = types[i].toColumnNullness( oldFields[i], getFactory() );
+ for ( int k=0; k<propertyNullness.length; k++ ) {
+ if ( propertyNullness[k] ) {
+ update.addWhereColumn( propertyColumnNames[k] );
+ }
+ else {
+ update.addWhereColumn( propertyColumnNames[k], " is null" );
+ }
+ }
+ }
+ }
+
+ }
+
+ if ( getFactory().getSettings().isCommentsEnabled() ) {
+ update.setComment( "update " + getEntityName() );
+ }
+
+ return hasColumns ? update.toStatementString() : null;
+ }
+
+ private boolean checkVersion(final boolean[] includeProperty) {
+ return includeProperty[ getVersionProperty() ] ||
+ entityMetamodel.getPropertyUpdateGenerationInclusions()[ getVersionProperty() ] != ValueInclusion.NONE;
+ }
+
+ protected String generateInsertString(boolean[] includeProperty, int j) {
+ return generateInsertString( false, includeProperty, j );
+ }
+
+ protected String generateInsertString(boolean identityInsert, boolean[] includeProperty) {
+ return generateInsertString( identityInsert, includeProperty, 0 );
+ }
+
+ /**
+ * Generate the SQL that inserts a row
+ */
+ protected String generateInsertString(boolean identityInsert, boolean[] includeProperty, int j) {
+
+ // todo : remove the identityInsert param and variations;
+ // identity-insert strings are now generated from generateIdentityInsertString()
+
+ Insert insert = new Insert( getFactory().getDialect() )
+ .setTableName( getTableName( j ) );
+
+ // add normal properties
+ for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) {
+ if ( includeProperty[i] && isPropertyOfTable( i, j ) ) {
+ // this property belongs on the table and is to be inserted
+ insert.addColumns( getPropertyColumnNames(i), propertyColumnInsertable[i] );
+ }
+ }
+
+ // add the discriminator
+ if ( j == 0 ) {
+ addDiscriminatorToInsert( insert );
+ }
+
+ // add the primary key
+ if ( j == 0 && identityInsert ) {
+ insert.addIdentityColumn( getKeyColumns( 0 )[0] );
+ }
+ else {
+ insert.addColumns( getKeyColumns( j ) );
+ }
+
+ if ( getFactory().getSettings().isCommentsEnabled() ) {
+ insert.setComment( "insert " + getEntityName() );
+ }
+
+ String result = insert.toStatementString();
+
+ // append the SQL to return the generated identifier
+ if ( j == 0 && identityInsert && useInsertSelectIdentity() ) { //TODO: suck into Insert
+ result = getFactory().getDialect().appendIdentitySelectToInsert( result );
+ }
+
+ return result;
+ }
+
+ /**
+ * Used to generate an insery statement against the root table in the
+ * case of identifier generation strategies where the insert statement
+ * executions actually generates the identifier value.
+ *
+ * @param includeProperty indices of the properties to include in the
+ * insert statement.
+ * @return The insert SQL statement string
+ */
+ protected String generateIdentityInsertString(boolean[] includeProperty) {
+ Insert insert = identityDelegate.prepareIdentifierGeneratingInsert();
+ insert.setTableName( getTableName( 0 ) );
+
+ // add normal properties
+ for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) {
+ if ( includeProperty[i] && isPropertyOfTable( i, 0 ) ) {
+ // this property belongs on the table and is to be inserted
+ insert.addColumns( getPropertyColumnNames(i), propertyColumnInsertable[i] );
+ }
+ }
+
+ // add the discriminator
+ addDiscriminatorToInsert( insert );
+
+ // delegate already handles PK columns
+
+ if ( getFactory().getSettings().isCommentsEnabled() ) {
+ insert.setComment( "insert " + getEntityName() );
+ }
+
+ return insert.toStatementString();
+ }
+
+ /**
+ * Generate the SQL that deletes a row by id (and version)
+ */
+ protected String generateDeleteString(int j) {
+ Delete delete = new Delete()
+ .setTableName( getTableName( j ) )
+ .setPrimaryKeyColumnNames( getKeyColumns( j ) );
+ if ( j == 0 ) {
+ delete.setVersionColumnName( getVersionColumnName() );
+ }
+ if ( getFactory().getSettings().isCommentsEnabled() ) {
+ delete.setComment( "delete " + getEntityName() );
+ }
+ return delete.toStatementString();
+ }
+
+ protected int dehydrate(
+ Serializable id,
+ Object[] fields,
+ boolean[] includeProperty,
+ boolean[][] includeColumns,
+ int j,
+ PreparedStatement st,
+ SessionImplementor session) throws HibernateException, SQLException {
+ return dehydrate( id, fields, null, includeProperty, includeColumns, j, st, session, 1 );
+ }
+
+ /**
+ * Marshall the fields of a persistent instance to a prepared statement
+ */
+ protected int dehydrate(
+ final Serializable id,
+ final Object[] fields,
+ final Object rowId,
+ final boolean[] includeProperty,
+ final boolean[][] includeColumns,
+ final int j,
+ final PreparedStatement ps,
+ final SessionImplementor session,
+ int index) throws SQLException, HibernateException {
+
+ if ( log.isTraceEnabled() ) {
+ log.trace( "Dehydrating entity: " + MessageHelper.infoString( this, id, getFactory() ) );
+ }
+
+ for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) {
+ if ( includeProperty[i] && isPropertyOfTable( i, j ) ) {
+ getPropertyTypes()[i].nullSafeSet( ps, fields[i], index, includeColumns[i], session );
+ //index += getPropertyColumnSpan( i );
+ index += ArrayHelper.countTrue( includeColumns[i] ); //TODO: this is kinda slow...
+ }
+ }
+
+ if ( rowId != null ) {
+ ps.setObject( index, rowId );
+ index += 1;
+ }
+ else if ( id != null ) {
+ getIdentifierType().nullSafeSet( ps, id, index, session );
+ index += getIdentifierColumnSpan();
+ }
+
+ return index;
+
+ }
+
+ /**
+ * Unmarshall the fields of a persistent instance from a result set,
+ * without resolving associations or collections. Question: should
+ * this really be here, or should it be sent back to Loader?
+ */
+ public Object[] hydrate(
+ final ResultSet rs,
+ final Serializable id,
+ final Object object,
+ final Loadable rootLoadable,
+ final String[][] suffixedPropertyColumns,
+ final boolean allProperties,
+ final SessionImplementor session) throws SQLException, HibernateException {
+
+ if ( log.isTraceEnabled() ) {
+ log.trace( "Hydrating entity: " + MessageHelper.infoString( this, id, getFactory() ) );
+ }
+
+ final AbstractEntityPersister rootPersister = (AbstractEntityPersister) rootLoadable;
+
+ final boolean hasDeferred = rootPersister.hasSequentialSelect();
+ PreparedStatement sequentialSelect = null;
+ ResultSet sequentialResultSet = null;
+ boolean sequentialSelectEmpty = false;
+ try {
+
+ if ( hasDeferred ) {
+ final String sql = rootPersister.getSequentialSelect( getEntityName() );
+ if ( sql != null ) {
+ //TODO: I am not so sure about the exception handling in this bit!
+ sequentialSelect = session.getBatcher().prepareSelectStatement( sql );
+ rootPersister.getIdentifierType().nullSafeSet( sequentialSelect, id, 1, session );
+ sequentialResultSet = sequentialSelect.executeQuery();
+ if ( !sequentialResultSet.next() ) {
+ // TODO: Deal with the "optional" attribute in the <join> mapping;
+ // this code assumes that optional defaults to "true" because it
+ // doesn't actually seem to work in the fetch="join" code
+ //
+ // Note that actual proper handling of optional-ality here is actually
+ // more involved than this patch assumes. Remember that we might have
+ // multiple <join/> mappings associated with a single entity. Really
+ // a couple of things need to happen to properly handle optional here:
+ // 1) First and foremost, when handling multiple <join/>s, we really
+ // should be using the entity root table as the driving table;
+ // another option here would be to choose some non-optional joined
+ // table to use as the driving table. In all likelihood, just using
+ // the root table is much simplier
+ // 2) Need to add the FK columns corresponding to each joined table
+ // to the generated select list; these would then be used when
+ // iterating the result set to determine whether all non-optional
+ // data is present
+ // My initial thoughts on the best way to deal with this would be
+ // to introduce a new SequentialSelect abstraction that actually gets
+ // generated in the persisters (ok, SingleTable...) and utilized here.
+ // It would encapsulated all this required optional-ality checking...
+ sequentialSelectEmpty = true;
+ }
+ }
+ }
+
+ final String[] propNames = getPropertyNames();
+ final Type[] types = getPropertyTypes();
+ final Object[] values = new Object[types.length];
+ final boolean[] laziness = getPropertyLaziness();
+ final String[] propSubclassNames = getSubclassPropertySubclassNameClosure();
+
+ for ( int i = 0; i < types.length; i++ ) {
+ if ( !propertySelectable[i] ) {
+ values[i] = BackrefPropertyAccessor.UNKNOWN;
+ }
+ else if ( allProperties || !laziness[i] ) {
+ //decide which ResultSet to get the property value from:
+ final boolean propertyIsDeferred = hasDeferred &&
+ rootPersister.isSubclassPropertyDeferred( propNames[i], propSubclassNames[i] );
+ if ( propertyIsDeferred && sequentialSelectEmpty ) {
+ values[i] = null;
+ }
+ else {
+ final ResultSet propertyResultSet = propertyIsDeferred ? sequentialResultSet : rs;
+ final String[] cols = propertyIsDeferred ? propertyColumnAliases[i] : suffixedPropertyColumns[i];
+ values[i] = types[i].hydrate( propertyResultSet, cols, session, object );
+ }
+ }
+ else {
+ values[i] = LazyPropertyInitializer.UNFETCHED_PROPERTY;
+ }
+ }
+
+ if ( sequentialResultSet != null ) {
+ sequentialResultSet.close();
+ }
+
+ return values;
+
+ }
+ finally {
+ if ( sequentialSelect != null ) {
+ session.getBatcher().closeStatement( sequentialSelect );
+ }
+ }
+ }
+
+ protected boolean useInsertSelectIdentity() {
+ return !useGetGeneratedKeys() && getFactory().getDialect().supportsInsertSelectIdentity();
+ }
+
+ protected boolean useGetGeneratedKeys() {
+ return getFactory().getSettings().isGetGeneratedKeysEnabled();
+ }
+
+ protected String getSequentialSelect(String entityName) {
+ throw new UnsupportedOperationException("no sequential selects");
+ }
+
+ /**
+ * Perform an SQL INSERT, and then retrieve a generated identifier.
+ * <p/>
+ * This form is used for PostInsertIdentifierGenerator-style ids (IDENTITY,
+ * select, etc).
+ */
+ protected Serializable insert(
+ final Object[] fields,
+ final boolean[] notNull,
+ String sql,
+ final Object object,
+ final SessionImplementor session) throws HibernateException {
+
+ if ( log.isTraceEnabled() ) {
+ log.trace( "Inserting entity: " + getEntityName() + " (native id)" );
+ if ( isVersioned() ) {
+ log.trace( "Version: " + Versioning.getVersion( fields, this ) );
+ }
+ }
+
+ Binder binder = new Binder() {
+ public void bindValues(PreparedStatement ps) throws SQLException {
+ dehydrate( null, fields, notNull, propertyColumnInsertable, 0, ps, session );
+ }
+ public Object getEntity() {
+ return object;
+ }
+ };
+ return identityDelegate.performInsert( sql, session, binder );
+ }
+
+ public String getIdentitySelectString() {
+ //TODO: cache this in an instvar
+ return getFactory().getDialect().getIdentitySelectString(
+ getTableName(0),
+ getKeyColumns(0)[0],
+ getIdentifierType().sqlTypes( getFactory() )[0]
+ );
+ }
+
+ public String getSelectByUniqueKeyString(String propertyName) {
+ return new SimpleSelect( getFactory().getDialect() )
+ .setTableName( getTableName(0) )
+ .addColumns( getKeyColumns(0) )
+ .addCondition( getPropertyColumnNames(propertyName), "=?" )
+ .toStatementString();
+ }
+
+ /**
+ * Perform an SQL INSERT.
+ * <p/>
+ * This for is used for all non-root tables as well as the root table
+ * in cases where the identifier value is known before the insert occurs.
+ */
+ protected void insert(
+ final Serializable id,
+ final Object[] fields,
+ final boolean[] notNull,
+ final int j,
+ final String sql,
+ final Object object,
+ final SessionImplementor session) throws HibernateException {
+
+ if ( isInverseTable( j ) ) {
+ return;
+ }
+
+ //note: it is conceptually possible that a UserType could map null to
+ // a non-null value, so the following is arguable:
+ if ( isNullableTable( j ) && isAllNull( fields, j ) ) {
+ return;
+ }
+
+ if ( log.isTraceEnabled() ) {
+ log.trace( "Inserting entity: " + MessageHelper.infoString( this, id, getFactory() ) );
+ if ( j == 0 && isVersioned() ) {
+ log.trace( "Version: " + Versioning.getVersion( fields, this ) );
+ }
+ }
+
+ Expectation expectation = Expectations.appropriateExpectation( insertResultCheckStyles[j] );
+ boolean callable = isInsertCallable( j );
+ // we can't batch joined inserts, *especially* not if it is an identity insert;
+ // nor can we batch statements where the expectation is based on an output param
+ final boolean useBatch = j == 0 && expectation.canBeBatched();
+ try {
+
+ // Render the SQL query
+ final PreparedStatement insert;
+ if ( useBatch ) {
+ if ( callable ) {
+ insert = session.getBatcher().prepareBatchCallableStatement( sql );
+ }
+ else {
+ insert = session.getBatcher().prepareBatchStatement( sql );
+ }
+ }
+ else {
+ if ( callable ) {
+ insert = session.getBatcher().prepareCallableStatement( sql );
+ }
+ else {
+ insert = session.getBatcher().prepareStatement( sql );
+ }
+ }
+
+ try {
+ int index = 1;
+ index += expectation.prepare( insert );
+
+ // Write the values of fields onto the prepared statement - we MUST use the state at the time the
+ // insert was issued (cos of foreign key constraints). Not necessarily the object's current state
+
+ dehydrate( id, fields, null, notNull, propertyColumnInsertable, j, insert, session, index );
+
+ if ( useBatch ) {
+ // TODO : shouldnt inserts be Expectations.NONE?
+ session.getBatcher().addToBatch( expectation );
+ }
+ else {
+ expectation.verifyOutcome( insert.executeUpdate(), insert, -1 );
+ }
+
+ }
+ catch ( SQLException sqle ) {
+ if ( useBatch ) {
+ session.getBatcher().abortBatch( sqle );
+ }
+ throw sqle;
+ }
+ finally {
+ if ( !useBatch ) {
+ session.getBatcher().closeStatement( insert );
+ }
+ }
+ }
+ catch ( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ getFactory().getSQLExceptionConverter(),
+ sqle,
+ "could not insert: " + MessageHelper.infoString( this ),
+ sql
+ );
+ }
+
+ }
+
+ /**
+ * Perform an SQL UPDATE or SQL INSERT
+ */
+ protected void updateOrInsert(
+ final Serializable id,
+ final Object[] fields,
+ final Object[] oldFields,
+ final Object rowId,
+ final boolean[] includeProperty,
+ final int j,
+ final Object oldVersion,
+ final Object object,
+ final String sql,
+ final SessionImplementor session) throws HibernateException {
+
+ if ( !isInverseTable( j ) ) {
+
+ final boolean isRowToUpdate;
+ if ( isNullableTable( j ) && oldFields != null && isAllNull( oldFields, j ) ) {
+ //don't bother trying to update, we know there is no row there yet
+ isRowToUpdate = false;
+ }
+ else if ( isNullableTable( j ) && isAllNull( fields, j ) ) {
+ //if all fields are null, we might need to delete existing row
+ isRowToUpdate = true;
+ delete( id, oldVersion, j, object, getSQLDeleteStrings()[j], session, null );
+ }
+ else {
+ //there is probably a row there, so try to update
+ //if no rows were updated, we will find out
+ isRowToUpdate = update( id, fields, oldFields, rowId, includeProperty, j, oldVersion, object, sql, session );
+ }
+
+ if ( !isRowToUpdate && !isAllNull( fields, j ) ) {
+ // assume that the row was not there since it previously had only null
+ // values, so do an INSERT instead
+ //TODO: does not respect dynamic-insert
+ insert( id, fields, getPropertyInsertability(), j, getSQLInsertStrings()[j], object, session );
+ }
+
+ }
+
+ }
+
+ protected boolean update(
+ final Serializable id,
+ final Object[] fields,
+ final Object[] oldFields,
+ final Object rowId,
+ final boolean[] includeProperty,
+ final int j,
+ final Object oldVersion,
+ final Object object,
+ final String sql,
+ final SessionImplementor session) throws HibernateException {
+
+ final boolean useVersion = j == 0 && isVersioned();
+ final Expectation expectation = Expectations.appropriateExpectation( updateResultCheckStyles[j] );
+ final boolean callable = isUpdateCallable( j );
+ final boolean useBatch = j == 0 && expectation.canBeBatched() && isBatchable(); //note: updates to joined tables can't be batched...
+
+ if ( log.isTraceEnabled() ) {
+ log.trace( "Updating entity: " + MessageHelper.infoString( this, id, getFactory() ) );
+ if ( useVersion ) {
+ log.trace( "Existing version: " + oldVersion + " -> New version: " + fields[getVersionProperty()] );
+ }
+ }
+
+ try {
+
+ int index = 1; // starting index
+ final PreparedStatement update;
+ if ( useBatch ) {
+ if ( callable ) {
+ update = session.getBatcher().prepareBatchCallableStatement( sql );
+ }
+ else {
+ update = session.getBatcher().prepareBatchStatement( sql );
+ }
+ }
+ else {
+ if ( callable ) {
+ update = session.getBatcher().prepareCallableStatement( sql );
+ }
+ else {
+ update = session.getBatcher().prepareStatement( sql );
+ }
+ }
+
+ try {
+
+ index+= expectation.prepare( update );
+
+ //Now write the values of fields onto the prepared statement
+ index = dehydrate( id, fields, rowId, includeProperty, propertyColumnUpdateable, j, update, session, index );
+
+ // Write any appropriate versioning conditional parameters
+ if ( useVersion && Versioning.OPTIMISTIC_LOCK_VERSION == entityMetamodel.getOptimisticLockMode() ) {
+ if ( checkVersion( includeProperty ) ) {
+ getVersionType().nullSafeSet( update, oldVersion, index, session );
+ }
+ }
+ else if ( entityMetamodel.getOptimisticLockMode() > Versioning.OPTIMISTIC_LOCK_VERSION && oldFields != null ) {
+ boolean[] versionability = getPropertyVersionability(); //TODO: is this really necessary????
+ boolean[] includeOldField = entityMetamodel.getOptimisticLockMode() == Versioning.OPTIMISTIC_LOCK_ALL ?
+ getPropertyUpdateability() : includeProperty;
+ Type[] types = getPropertyTypes();
+ for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) {
+ boolean include = includeOldField[i] &&
+ isPropertyOfTable( i, j ) &&
+ versionability[i]; //TODO: is this really necessary????
+ if ( include ) {
+ boolean[] settable = types[i].toColumnNullness( oldFields[i], getFactory() );
+ types[i].nullSafeSet(
+ update,
+ oldFields[i],
+ index,
+ settable,
+ session
+ );
+ index += ArrayHelper.countTrue(settable);
+ }
+ }
+ }
+
+ if ( useBatch ) {
+ session.getBatcher().addToBatch( expectation );
+ return true;
+ }
+ else {
+ return check( update.executeUpdate(), id, j, expectation, update );
+ }
+
+ }
+ catch ( SQLException sqle ) {
+ if ( useBatch ) {
+ session.getBatcher().abortBatch( sqle );
+ }
+ throw sqle;
+ }
+ finally {
+ if ( !useBatch ) {
+ session.getBatcher().closeStatement( update );
+ }
+ }
+
+ }
+ catch ( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ getFactory().getSQLExceptionConverter(),
+ sqle,
+ "could not update: " + MessageHelper.infoString( this, id, getFactory() ),
+ sql
+ );
+ }
+ }
+
+ /**
+ * Perform an SQL DELETE
+ */
+ protected void delete(
+ final Serializable id,
+ final Object version,
+ final int j,
+ final Object object,
+ final String sql,
+ final SessionImplementor session,
+ final Object[] loadedState) throws HibernateException {
+
+ if ( isInverseTable( j ) ) {
+ return;
+ }
+
+ final boolean useVersion = j == 0 && isVersioned();
+ final boolean callable = isDeleteCallable( j );
+ final Expectation expectation = Expectations.appropriateExpectation( deleteResultCheckStyles[j] );
+ final boolean useBatch = j == 0 && isBatchable() && expectation.canBeBatched();
+
+ if ( log.isTraceEnabled() ) {
+ log.trace( "Deleting entity: " + MessageHelper.infoString( this, id, getFactory() ) );
+ if ( useVersion ) {
+ log.trace( "Version: " + version );
+ }
+ }
+
+ if ( isTableCascadeDeleteEnabled( j ) ) {
+ if ( log.isTraceEnabled() ) {
+ log.trace( "delete handled by foreign key constraint: " + getTableName( j ) );
+ }
+ return; //EARLY EXIT!
+ }
+
+ try {
+
+ //Render the SQL query
+ PreparedStatement delete;
+ int index = 1;
+ if ( useBatch ) {
+ if ( callable ) {
+ delete = session.getBatcher().prepareBatchCallableStatement( sql );
+ }
+ else {
+ delete = session.getBatcher().prepareBatchStatement( sql );
+ }
+ }
+ else {
+ if ( callable ) {
+ delete = session.getBatcher().prepareCallableStatement( sql );
+ }
+ else {
+ delete = session.getBatcher().prepareStatement( sql );
+ }
+ }
+
+ try {
+
+ index += expectation.prepare( delete );
+
+ // Do the key. The key is immutable so we can use the _current_ object state - not necessarily
+ // the state at the time the delete was issued
+ getIdentifierType().nullSafeSet( delete, id, index, session );
+ index += getIdentifierColumnSpan();
+
+ // We should use the _current_ object state (ie. after any updates that occurred during flush)
+
+ if ( useVersion ) {
+ getVersionType().nullSafeSet( delete, version, index, session );
+ }
+ else if ( entityMetamodel.getOptimisticLockMode() > Versioning.OPTIMISTIC_LOCK_VERSION && loadedState != null ) {
+ boolean[] versionability = getPropertyVersionability();
+ Type[] types = getPropertyTypes();
+ for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) {
+ if ( isPropertyOfTable( i, j ) && versionability[i] ) {
+ // this property belongs to the table and it is not specifically
+ // excluded from optimistic locking by optimistic-lock="false"
+ boolean[] settable = types[i].toColumnNullness( loadedState[i], getFactory() );
+ types[i].nullSafeSet( delete, loadedState[i], index, settable, session );
+ index += ArrayHelper.countTrue( settable );
+ }
+ }
+ }
+
+ if ( useBatch ) {
+ session.getBatcher().addToBatch( expectation );
+ }
+ else {
+ check( delete.executeUpdate(), id, j, expectation, delete );
+ }
+
+ }
+ catch ( SQLException sqle ) {
+ if ( useBatch ) {
+ session.getBatcher().abortBatch( sqle );
+ }
+ throw sqle;
+ }
+ finally {
+ if ( !useBatch ) {
+ session.getBatcher().closeStatement( delete );
+ }
+ }
+
+ }
+ catch ( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ getFactory().getSQLExceptionConverter(),
+ sqle,
+ "could not delete: " +
+ MessageHelper.infoString( this, id, getFactory() ),
+ sql
+ );
+
+ }
+
+ }
+
+ private String[] getUpdateStrings(boolean byRowId, boolean lazy) {
+ if ( byRowId ) {
+ return lazy ? getSQLLazyUpdateByRowIdStrings() : getSQLUpdateByRowIdStrings();
+ }
+ else {
+ return lazy ? getSQLLazyUpdateStrings() : getSQLUpdateStrings();
+ }
+ }
+
+ /**
+ * Update an object
+ */
+ public void update(
+ final Serializable id,
+ final Object[] fields,
+ final int[] dirtyFields,
+ final boolean hasDirtyCollection,
+ final Object[] oldFields,
+ final Object oldVersion,
+ final Object object,
+ final Object rowId,
+ final SessionImplementor session) throws HibernateException {
+
+ //note: dirtyFields==null means we had no snapshot, and we couldn't get one using select-before-update
+ // oldFields==null just means we had no snapshot to begin with (we might have used select-before-update to get the dirtyFields)
+
+ final boolean[] tableUpdateNeeded = getTableUpdateNeeded( dirtyFields, hasDirtyCollection );
+ final int span = getTableSpan();
+
+ final boolean[] propsToUpdate;
+ final String[] updateStrings;
+ if ( entityMetamodel.isDynamicUpdate() && dirtyFields != null ) {
+ // For the case of dynamic-update="true", we need to generate the UPDATE SQL
+ propsToUpdate = getPropertiesToUpdate( dirtyFields, hasDirtyCollection );
+ // don't need to check laziness (dirty checking algorithm handles that)
+ updateStrings = new String[span];
+ for ( int j = 0; j < span; j++ ) {
+ updateStrings[j] = tableUpdateNeeded[j] ?
+ generateUpdateString( propsToUpdate, j, oldFields, j == 0 && rowId != null ) :
+ null;
+ }
+ }
+ else {
+ // For the case of dynamic-update="false", or no snapshot, we use the static SQL
+ updateStrings = getUpdateStrings(
+ rowId != null,
+ hasUninitializedLazyProperties( object, session.getEntityMode() )
+ );
+ propsToUpdate = getPropertyUpdateability( object, session.getEntityMode() );
+ }
+
+ for ( int j = 0; j < span; j++ ) {
+ // Now update only the tables with dirty properties (and the table with the version number)
+ if ( tableUpdateNeeded[j] ) {
+ updateOrInsert(
+ id,
+ fields,
+ oldFields,
+ j == 0 ? rowId : null,
+ propsToUpdate,
+ j,
+ oldVersion,
+ object,
+ updateStrings[j],
+ session
+ );
+ }
+ }
+ }
+
+ public Serializable insert(Object[] fields, Object object, SessionImplementor session)
+ throws HibernateException {
+
+ final int span = getTableSpan();
+ final Serializable id;
+ if ( entityMetamodel.isDynamicInsert() ) {
+ // For the case of dynamic-insert="true", we need to generate the INSERT SQL
+ boolean[] notNull = getPropertiesToInsert( fields );
+ id = insert( fields, notNull, generateInsertString( true, notNull ), object, session );
+ for ( int j = 1; j < span; j++ ) {
+ insert( id, fields, notNull, j, generateInsertString( notNull, j ), object, session );
+ }
+ }
+ else {
+ // For the case of dynamic-insert="false", use the static SQL
+ id = insert( fields, getPropertyInsertability(), getSQLIdentityInsertString(), object, session );
+ for ( int j = 1; j < span; j++ ) {
+ insert( id, fields, getPropertyInsertability(), j, getSQLInsertStrings()[j], object, session );
+ }
+ }
+ return id;
+ }
+
+ public void insert(Serializable id, Object[] fields, Object object, SessionImplementor session)
+ throws HibernateException {
+
+ final int span = getTableSpan();
+ if ( entityMetamodel.isDynamicInsert() ) {
+ // For the case of dynamic-insert="true", we need to generate the INSERT SQL
+ boolean[] notNull = getPropertiesToInsert( fields );
+ for ( int j = 0; j < span; j++ ) {
+ insert( id, fields, notNull, j, generateInsertString( notNull, j ), object, session );
+ }
+ }
+ else {
+ // For the case of dynamic-insert="false", use the static SQL
+ for ( int j = 0; j < span; j++ ) {
+ insert( id, fields, getPropertyInsertability(), j, getSQLInsertStrings()[j], object, session );
+ }
+ }
+ }
+
+ /**
+ * Delete an object
+ */
+ public void delete(Serializable id, Object version, Object object, SessionImplementor session)
+ throws HibernateException {
+ final int span = getTableSpan();
+ boolean isImpliedOptimisticLocking = !entityMetamodel.isVersioned() && entityMetamodel.getOptimisticLockMode() > Versioning.OPTIMISTIC_LOCK_VERSION;
+ Object[] loadedState = null;
+ if ( isImpliedOptimisticLocking ) {
+ // need to treat this as if it where optimistic-lock="all" (dirty does *not* make sense);
+ // first we need to locate the "loaded" state
+ //
+ // Note, it potentially could be a proxy, so perform the location the safe way...
+ EntityKey key = new EntityKey( id, this, session.getEntityMode() );
+ Object entity = session.getPersistenceContext().getEntity( key );
+ if ( entity != null ) {
+ EntityEntry entry = session.getPersistenceContext().getEntry( entity );
+ loadedState = entry.getLoadedState();
+ }
+ }
+
+ final String[] deleteStrings;
+ if ( isImpliedOptimisticLocking && loadedState != null ) {
+ // we need to utilize dynamic delete statements
+ deleteStrings = generateSQLDeletStrings( loadedState );
+ }
+ else {
+ // otherwise, utilize the static delete statements
+ deleteStrings = getSQLDeleteStrings();
+ }
+
+ for ( int j = span - 1; j >= 0; j-- ) {
+ delete( id, version, j, object, deleteStrings[j], session, loadedState );
+ }
+
+ }
+
+ private String[] generateSQLDeletStrings(Object[] loadedState) {
+ int span = getTableSpan();
+ String[] deleteStrings = new String[span];
+ for ( int j = span - 1; j >= 0; j-- ) {
+ Delete delete = new Delete()
+ .setTableName( getTableName( j ) )
+ .setPrimaryKeyColumnNames( getKeyColumns( j ) );
+ if ( getFactory().getSettings().isCommentsEnabled() ) {
+ delete.setComment( "delete " + getEntityName() + " [" + j + "]" );
+ }
+
+ boolean[] versionability = getPropertyVersionability();
+ Type[] types = getPropertyTypes();
+ for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) {
+ if ( isPropertyOfTable( i, j ) && versionability[i] ) {
+ // this property belongs to the table and it is not specifically
+ // excluded from optimistic locking by optimistic-lock="false"
+ String[] propertyColumnNames = getPropertyColumnNames( i );
+ boolean[] propertyNullness = types[i].toColumnNullness( loadedState[i], getFactory() );
+ for ( int k = 0; k < propertyNullness.length; k++ ) {
+ if ( propertyNullness[k] ) {
+ delete.addWhereFragment( propertyColumnNames[k] + " = ?" );
+ }
+ else {
+ delete.addWhereFragment( propertyColumnNames[k] + " is null" );
+ }
+ }
+ }
+ }
+ deleteStrings[j] = delete.toStatementString();
+ }
+ return deleteStrings;
+ }
+
+ protected void logStaticSQL() {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "Static SQL for entity: " + getEntityName() );
+ if ( sqlLazySelectString != null ) {
+ log.debug( " Lazy select: " + sqlLazySelectString );
+ }
+ if ( sqlVersionSelectString != null ) {
+ log.debug( " Version select: " + sqlVersionSelectString );
+ }
+ if ( sqlSnapshotSelectString != null ) {
+ log.debug( " Snapshot select: " + sqlSnapshotSelectString );
+ }
+ for ( int j = 0; j < getTableSpan(); j++ ) {
+ log.debug( " Insert " + j + ": " + getSQLInsertStrings()[j] );
+ log.debug( " Update " + j + ": " + getSQLUpdateStrings()[j] );
+ log.debug( " Delete " + j + ": " + getSQLDeleteStrings()[j] );
+
+ }
+ if ( sqlIdentityInsertString != null ) {
+ log.debug( " Identity insert: " + sqlIdentityInsertString );
+ }
+ if ( sqlUpdateByRowIdString != null ) {
+ log.debug( " Update by row id (all fields): " + sqlUpdateByRowIdString );
+ }
+ if ( sqlLazyUpdateByRowIdString != null ) {
+ log.debug( " Update by row id (non-lazy fields): " + sqlLazyUpdateByRowIdString );
+ }
+ if ( sqlInsertGeneratedValuesSelectString != null ) {
+ log.debug( "Insert-generated property select: " + sqlInsertGeneratedValuesSelectString );
+ }
+ if ( sqlUpdateGeneratedValuesSelectString != null ) {
+ log.debug( "Update-generated property select: " + sqlUpdateGeneratedValuesSelectString );
+ }
+ }
+ }
+
+ public String filterFragment(String alias, Map enabledFilters) throws MappingException {
+ final StringBuffer sessionFilterFragment = new StringBuffer();
+ filterHelper.render( sessionFilterFragment, generateFilterConditionAlias( alias ), enabledFilters );
+
+ return sessionFilterFragment.append( filterFragment( alias ) ).toString();
+ }
+
+ public String generateFilterConditionAlias(String rootAlias) {
+ return rootAlias;
+ }
+
+ public String oneToManyFilterFragment(String alias) throws MappingException {
+ return "";
+ }
+
+ public String fromJoinFragment(String alias, boolean innerJoin, boolean includeSubclasses) {
+ return getSubclassTableSpan() == 1 ?
+ "" : //just a performance opt!
+ createJoin( alias, innerJoin, includeSubclasses ).toFromFragmentString();
+ }
+
+ public String whereJoinFragment(String alias, boolean innerJoin, boolean includeSubclasses) {
+ return getSubclassTableSpan() == 1 ?
+ "" : //just a performance opt!
+ createJoin( alias, innerJoin, includeSubclasses ).toWhereFragmentString();
+ }
+
+ protected boolean isSubclassTableLazy(int j) {
+ return false;
+ }
+
+ protected JoinFragment createJoin(String name, boolean innerJoin, boolean includeSubclasses) {
+ final String[] idCols = StringHelper.qualify( name, getIdentifierColumnNames() ); //all joins join to the pk of the driving table
+ final JoinFragment join = getFactory().getDialect().createOuterJoinFragment();
+ final int tableSpan = getSubclassTableSpan();
+ for ( int j = 1; j < tableSpan; j++ ) { //notice that we skip the first table; it is the driving table!
+ final boolean joinIsIncluded = isClassOrSuperclassTable( j ) ||
+ ( includeSubclasses && !isSubclassTableSequentialSelect( j ) && !isSubclassTableLazy( j ) );
+ if ( joinIsIncluded ) {
+ join.addJoin( getSubclassTableName( j ),
+ generateTableAlias( name, j ),
+ idCols,
+ getSubclassTableKeyColumns( j ),
+ innerJoin && isClassOrSuperclassTable( j ) && !isInverseTable( j ) && !isNullableTable( j ) ?
+ JoinFragment.INNER_JOIN : //we can inner join to superclass tables (the row MUST be there)
+ JoinFragment.LEFT_OUTER_JOIN //we can never inner join to subclass tables
+ );
+ }
+ }
+ return join;
+ }
+
+ protected JoinFragment createJoin(int[] tableNumbers, String drivingAlias) {
+ final String[] keyCols = StringHelper.qualify( drivingAlias, getSubclassTableKeyColumns( tableNumbers[0] ) );
+ final JoinFragment jf = getFactory().getDialect().createOuterJoinFragment();
+ for ( int i = 1; i < tableNumbers.length; i++ ) { //skip the driving table
+ final int j = tableNumbers[i];
+ jf.addJoin( getSubclassTableName( j ),
+ generateTableAlias( getRootAlias(), j ),
+ keyCols,
+ getSubclassTableKeyColumns( j ),
+ isInverseSubclassTable( j ) || isNullableSubclassTable( j ) ?
+ JoinFragment.LEFT_OUTER_JOIN :
+ JoinFragment.INNER_JOIN );
+ }
+ return jf;
+ }
+
+ protected SelectFragment createSelect(final int[] subclassColumnNumbers,
+ final int[] subclassFormulaNumbers) {
+
+ SelectFragment selectFragment = new SelectFragment();
+
+ int[] columnTableNumbers = getSubclassColumnTableNumberClosure();
+ String[] columnAliases = getSubclassColumnAliasClosure();
+ String[] columns = getSubclassColumnClosure();
+ for ( int i = 0; i < subclassColumnNumbers.length; i++ ) {
+ if ( subclassColumnSelectableClosure[i] ) {
+ int columnNumber = subclassColumnNumbers[i];
+ final String subalias = generateTableAlias( getRootAlias(), columnTableNumbers[columnNumber] );
+ selectFragment.addColumn( subalias, columns[columnNumber], columnAliases[columnNumber] );
+ }
+ }
+
+ int[] formulaTableNumbers = getSubclassFormulaTableNumberClosure();
+ String[] formulaTemplates = getSubclassFormulaTemplateClosure();
+ String[] formulaAliases = getSubclassFormulaAliasClosure();
+ for ( int i = 0; i < subclassFormulaNumbers.length; i++ ) {
+ int formulaNumber = subclassFormulaNumbers[i];
+ final String subalias = generateTableAlias( getRootAlias(), formulaTableNumbers[formulaNumber] );
+ selectFragment.addFormula( subalias, formulaTemplates[formulaNumber], formulaAliases[formulaNumber] );
+ }
+
+ return selectFragment;
+ }
+
+ protected String createFrom(int tableNumber, String alias) {
+ return getSubclassTableName( tableNumber ) + ' ' + alias;
+ }
+
+ protected String createWhereByKey(int tableNumber, String alias) {
+ //TODO: move to .sql package, and refactor with similar things!
+ return StringHelper.join( "=? and ",
+ StringHelper.qualify( alias, getSubclassTableKeyColumns( tableNumber ) ) ) + "=?";
+ }
+
+ protected String renderSelect(
+ final int[] tableNumbers,
+ final int[] columnNumbers,
+ final int[] formulaNumbers) {
+
+ Arrays.sort( tableNumbers ); //get 'em in the right order (not that it really matters)
+
+ //render the where and from parts
+ int drivingTable = tableNumbers[0];
+ final String drivingAlias = generateTableAlias( getRootAlias(), drivingTable ); //we *could* regerate this inside each called method!
+ final String where = createWhereByKey( drivingTable, drivingAlias );
+ final String from = createFrom( drivingTable, drivingAlias );
+
+ //now render the joins
+ JoinFragment jf = createJoin( tableNumbers, drivingAlias );
+
+ //now render the select clause
+ SelectFragment selectFragment = createSelect( columnNumbers, formulaNumbers );
+
+ //now tie it all together
+ Select select = new Select( getFactory().getDialect() );
+ select.setSelectClause( selectFragment.toFragmentString().substring( 2 ) );
+ select.setFromClause( from );
+ select.setWhereClause( where );
+ select.setOuterJoins( jf.toFromFragmentString(), jf.toWhereFragmentString() );
+ if ( getFactory().getSettings().isCommentsEnabled() ) {
+ select.setComment( "sequential select " + getEntityName() );
+ }
+ return select.toStatementString();
+ }
+
+ private String getRootAlias() {
+ return StringHelper.generateAlias( getEntityName() );
+ }
+
+ protected void postConstruct(Mapping mapping) throws MappingException {
+ initPropertyPaths(mapping);
+
+ //insert/update/delete SQL
+ final int joinSpan = getTableSpan();
+ sqlDeleteStrings = new String[joinSpan];
+ sqlInsertStrings = new String[joinSpan];
+ sqlUpdateStrings = new String[joinSpan];
+ sqlLazyUpdateStrings = new String[joinSpan];
+
+ sqlUpdateByRowIdString = rowIdName == null ?
+ null :
+ generateUpdateString( getPropertyUpdateability(), 0, true );
+ sqlLazyUpdateByRowIdString = rowIdName == null ?
+ null :
+ generateUpdateString( getNonLazyPropertyUpdateability(), 0, true );
+
+ for ( int j = 0; j < joinSpan; j++ ) {
+ sqlInsertStrings[j] = customSQLInsert[j] == null ?
+ generateInsertString( getPropertyInsertability(), j ) :
+ customSQLInsert[j];
+ sqlUpdateStrings[j] = customSQLUpdate[j] == null ?
+ generateUpdateString( getPropertyUpdateability(), j, false ) :
+ customSQLUpdate[j];
+ sqlLazyUpdateStrings[j] = customSQLUpdate[j] == null ?
+ generateUpdateString( getNonLazyPropertyUpdateability(), j, false ) :
+ customSQLUpdate[j];
+ sqlDeleteStrings[j] = customSQLDelete[j] == null ?
+ generateDeleteString( j ) :
+ customSQLDelete[j];
+ }
+
+ tableHasColumns = new boolean[joinSpan];
+ for ( int j = 0; j < joinSpan; j++ ) {
+ tableHasColumns[j] = sqlUpdateStrings[j] != null;
+ }
+
+ //select SQL
+ sqlSnapshotSelectString = generateSnapshotSelectString();
+ sqlLazySelectString = generateLazySelectString();
+ sqlVersionSelectString = generateSelectVersionString();
+ if ( hasInsertGeneratedProperties() ) {
+ sqlInsertGeneratedValuesSelectString = generateInsertGeneratedValuesSelectString();
+ }
+ if ( hasUpdateGeneratedProperties() ) {
+ sqlUpdateGeneratedValuesSelectString = generateUpdateGeneratedValuesSelectString();
+ }
+ if ( isIdentifierAssignedByInsert() ) {
+ identityDelegate = ( ( PostInsertIdentifierGenerator ) getIdentifierGenerator() )
+ .getInsertGeneratedIdentifierDelegate( this, getFactory().getDialect(), useGetGeneratedKeys() );
+ sqlIdentityInsertString = customSQLInsert[0] == null
+ ? generateIdentityInsertString( getPropertyInsertability() )
+ : customSQLInsert[0];
+ }
+ else {
+ sqlIdentityInsertString = null;
+ }
+
+ logStaticSQL();
+
+ }
+
+ public void postInstantiate() throws MappingException {
+
+ createLoaders();
+ createUniqueKeyLoaders();
+ createQueryLoader();
+
+ }
+
+ private void createLoaders() {
+ loaders.put( LockMode.NONE, createEntityLoader( LockMode.NONE ) );
+
+ UniqueEntityLoader readLoader = createEntityLoader( LockMode.READ );
+ loaders.put( LockMode.READ, readLoader );
+
+ //TODO: inexact, what we really need to know is: are any outer joins used?
+ boolean disableForUpdate = getSubclassTableSpan() > 1 &&
+ hasSubclasses() &&
+ !getFactory().getDialect().supportsOuterJoinForUpdate();
+
+ loaders.put(
+ LockMode.UPGRADE,
+ disableForUpdate ?
+ readLoader :
+ createEntityLoader( LockMode.UPGRADE )
+ );
+ loaders.put(
+ LockMode.UPGRADE_NOWAIT,
+ disableForUpdate ?
+ readLoader :
+ createEntityLoader( LockMode.UPGRADE_NOWAIT )
+ );
+ loaders.put(
+ LockMode.FORCE,
+ disableForUpdate ?
+ readLoader :
+ createEntityLoader( LockMode.FORCE )
+ );
+
+ loaders.put(
+ "merge",
+ new CascadeEntityLoader( this, CascadingAction.MERGE, getFactory() )
+ );
+ loaders.put(
+ "refresh",
+ new CascadeEntityLoader( this, CascadingAction.REFRESH, getFactory() )
+ );
+ }
+
+ protected void createQueryLoader() {
+ if ( loaderName != null ) {
+ queryLoader = new NamedQueryLoader( loaderName, this );
+ }
+ }
+
+ /**
+ * Load an instance using either the <tt>forUpdateLoader</tt> or the outer joining <tt>loader</tt>,
+ * depending upon the value of the <tt>lock</tt> parameter
+ */
+ public Object load(Serializable id, Object optionalObject, LockMode lockMode, SessionImplementor session)
+ throws HibernateException {
+
+ if ( log.isTraceEnabled() ) {
+ log.trace(
+ "Fetching entity: " +
+ MessageHelper.infoString( this, id, getFactory() )
+ );
+ }
+
+ final UniqueEntityLoader loader = getAppropriateLoader( lockMode, session );
+ return loader.load( id, optionalObject, session );
+ }
+
+ private UniqueEntityLoader getAppropriateLoader(LockMode lockMode, SessionImplementor session) {
+ final Map enabledFilters = session.getEnabledFilters();
+ if ( queryLoader != null ) {
+ return queryLoader;
+ }
+ else if ( enabledFilters == null || enabledFilters.isEmpty() ) {
+ if ( session.getFetchProfile()!=null && LockMode.UPGRADE.greaterThan(lockMode) ) {
+ return (UniqueEntityLoader) loaders.get( session.getFetchProfile() );
+ }
+ else {
+ return (UniqueEntityLoader) loaders.get( lockMode );
+ }
+ }
+ else {
+ return createEntityLoader( lockMode, enabledFilters );
+ }
+ }
+
+ private boolean isAllNull(Object[] array, int tableNumber) {
+ for ( int i = 0; i < array.length; i++ ) {
+ if ( isPropertyOfTable( i, tableNumber ) && array[i] != null ) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public boolean isSubclassPropertyNullable(int i) {
+ return subclassPropertyNullabilityClosure[i];
+ }
+
+ /**
+ * Transform the array of property indexes to an array of booleans,
+ * true when the property is dirty
+ */
+ protected final boolean[] getPropertiesToUpdate(final int[] dirtyProperties, final boolean hasDirtyCollection) {
+ final boolean[] propsToUpdate = new boolean[ entityMetamodel.getPropertySpan() ];
+ final boolean[] updateability = getPropertyUpdateability(); //no need to check laziness, dirty checking handles that
+ for ( int j = 0; j < dirtyProperties.length; j++ ) {
+ int property = dirtyProperties[j];
+ if ( updateability[property] ) {
+ propsToUpdate[property] = true;
+ }
+ }
+ if ( isVersioned() ) {
+ propsToUpdate[ getVersionProperty() ] =
+ Versioning.isVersionIncrementRequired( dirtyProperties, hasDirtyCollection, getPropertyVersionability() );
+ }
+ return propsToUpdate;
+ }
+
+ /**
+ * Transform the array of property indexes to an array of booleans,
+ * true when the property is insertable and non-null
+ */
+ protected boolean[] getPropertiesToInsert(Object[] fields) {
+ boolean[] notNull = new boolean[fields.length];
+ boolean[] insertable = getPropertyInsertability();
+ for ( int i = 0; i < fields.length; i++ ) {
+ notNull[i] = insertable[i] && fields[i] != null;
+ }
+ return notNull;
+ }
+
+ /**
+ * Locate the property-indices of all properties considered to be dirty.
+ *
+ * @param currentState The current state of the entity (the state to be checked).
+ * @param previousState The previous state of the entity (the state to be checked against).
+ * @param entity The entity for which we are checking state dirtiness.
+ * @param session The session in which the check is ccurring.
+ * @return <tt>null</tt> or the indices of the dirty properties
+ * @throws HibernateException
+ */
+ public int[] findDirty(Object[] currentState, Object[] previousState, Object entity, SessionImplementor session)
+ throws HibernateException {
+ int[] props = TypeFactory.findDirty(
+ entityMetamodel.getProperties(),
+ currentState,
+ previousState,
+ propertyColumnUpdateable,
+ hasUninitializedLazyProperties( entity, session.getEntityMode() ),
+ session
+ );
+ if ( props == null ) {
+ return null;
+ }
+ else {
+ logDirtyProperties( props );
+ return props;
+ }
+ }
+
+ /**
+ * Locate the property-indices of all properties considered to be dirty.
+ *
+ * @param old The old state of the entity.
+ * @param current The current state of the entity.
+ * @param entity The entity for which we are checking state modification.
+ * @param session The session in which the check is ccurring.
+ * @return <tt>null</tt> or the indices of the modified properties
+ * @throws HibernateException
+ */
+ public int[] findModified(Object[] old, Object[] current, Object entity, SessionImplementor session)
+ throws HibernateException {
+ int[] props = TypeFactory.findModified(
+ entityMetamodel.getProperties(),
+ current,
+ old,
+ propertyColumnUpdateable,
+ hasUninitializedLazyProperties( entity, session.getEntityMode() ),
+ session
+ );
+ if ( props == null ) {
+ return null;
+ }
+ else {
+ logDirtyProperties( props );
+ return props;
+ }
+ }
+
+ /**
+ * Which properties appear in the SQL update?
+ * (Initialized, updateable ones!)
+ */
+ protected boolean[] getPropertyUpdateability(Object entity, EntityMode entityMode) {
+ return hasUninitializedLazyProperties( entity, entityMode ) ?
+ getNonLazyPropertyUpdateability() :
+ getPropertyUpdateability();
+ }
+
+ private void logDirtyProperties(int[] props) {
+ if ( log.isTraceEnabled() ) {
+ for ( int i = 0; i < props.length; i++ ) {
+ String propertyName = entityMetamodel.getProperties()[ props[i] ].getName();
+ log.trace( StringHelper.qualify( getEntityName(), propertyName ) + " is dirty" );
+ }
+ }
+ }
+
+ protected EntityTuplizer getTuplizer(SessionImplementor session) {
+ return getTuplizer( session.getEntityMode() );
+ }
+
+ protected EntityTuplizer getTuplizer(EntityMode entityMode) {
+ return entityMetamodel.getTuplizer( entityMode );
+ }
+
+ public SessionFactoryImplementor getFactory() {
+ return factory;
+ }
+
+ public EntityMetamodel getEntityMetamodel() {
+ return entityMetamodel;
+ }
+
+ public boolean hasCache() {
+ return cacheAccessStrategy != null;
+ }
+
+ public EntityRegionAccessStrategy getCacheAccessStrategy() {
+ return cacheAccessStrategy;
+ }
+
+ public CacheEntryStructure getCacheEntryStructure() {
+ return cacheEntryStructure;
+ }
+
+ public Comparator getVersionComparator() {
+ return isVersioned() ? getVersionType().getComparator() : null;
+ }
+
+ // temporary ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ public final String getEntityName() {
+ return entityMetamodel.getName();
+ }
+
+ public EntityType getEntityType() {
+ return entityMetamodel.getEntityType();
+ }
+
+ private String getSubclassEntityName(Class clazz) {
+ return ( String ) entityNameBySubclass.get( clazz );
+ }
+
+ public boolean isPolymorphic() {
+ return entityMetamodel.isPolymorphic();
+ }
+
+ public boolean isInherited() {
+ return entityMetamodel.isInherited();
+ }
+
+ public boolean hasCascades() {
+ return entityMetamodel.hasCascades();
+ }
+
+ public boolean hasIdentifierProperty() {
+ return !entityMetamodel.getIdentifierProperty().isVirtual();
+ }
+
+ public VersionType getVersionType() {
+ return ( VersionType ) locateVersionType();
+ }
+
+ private Type locateVersionType() {
+ return entityMetamodel.getVersionProperty() == null ?
+ null :
+ entityMetamodel.getVersionProperty().getType();
+ }
+
+ public int getVersionProperty() {
+ return entityMetamodel.getVersionPropertyIndex();
+ }
+
+ public boolean isVersioned() {
+ return entityMetamodel.isVersioned();
+ }
+
+ public boolean isIdentifierAssignedByInsert() {
+ return entityMetamodel.getIdentifierProperty().isIdentifierAssignedByInsert();
+ }
+
+ public boolean hasLazyProperties() {
+ return entityMetamodel.hasLazyProperties();
+ }
+
+// public boolean hasUninitializedLazyProperties(Object entity) {
+// if ( hasLazyProperties() ) {
+// InterceptFieldCallback callback = ( ( InterceptFieldEnabled ) entity ).getInterceptFieldCallback();
+// return callback != null && !( ( FieldInterceptor ) callback ).isInitialized();
+// }
+// else {
+// return false;
+// }
+// }
+
+ public void afterReassociate(Object entity, SessionImplementor session) {
+ //if ( hasLazyProperties() ) {
+ if ( FieldInterceptionHelper.isInstrumented( entity ) ) {
+ FieldInterceptor interceptor = FieldInterceptionHelper.extractFieldInterceptor( entity );
+ if ( interceptor != null ) {
+ interceptor.setSession( session );
+ }
+ else {
+ FieldInterceptor fieldInterceptor = FieldInterceptionHelper.injectFieldInterceptor(
+ entity,
+ getEntityName(),
+ null,
+ session
+ );
+ fieldInterceptor.dirty();
+ }
+ }
+ }
+
+ public Boolean isTransient(Object entity, SessionImplementor session) throws HibernateException {
+ final Serializable id;
+ if ( canExtractIdOutOfEntity() ) {
+ id = getIdentifier( entity, session.getEntityMode() );
+ }
+ else {
+ id = null;
+ }
+ // we *always* assume an instance with a null
+ // identifier or no identifier property is unsaved!
+ if ( id == null ) {
+ return Boolean.TRUE;
+ }
+
+ // check the version unsaved-value, if appropriate
+ final Object version = getVersion( entity, session.getEntityMode() );
+ if ( isVersioned() ) {
+ // let this take precedence if defined, since it works for
+ // assigned identifiers
+ Boolean result = entityMetamodel.getVersionProperty()
+ .getUnsavedValue().isUnsaved( version );
+ if ( result != null ) {
+ return result;
+ }
+ }
+
+ // check the id unsaved-value
+ Boolean result = entityMetamodel.getIdentifierProperty()
+ .getUnsavedValue().isUnsaved( id );
+ if ( result != null ) {
+ return result;
+ }
+
+ // check to see if it is in the second-level cache
+ if ( hasCache() ) {
+ CacheKey ck = new CacheKey(
+ id,
+ getIdentifierType(),
+ getRootEntityName(),
+ session.getEntityMode(),
+ session.getFactory()
+ );
+ if ( getCacheAccessStrategy().get( ck, session.getTimestamp() ) != null ) {
+ return Boolean.FALSE;
+ }
+ }
+
+ return null;
+ }
+
+ public boolean hasCollections() {
+ return entityMetamodel.hasCollections();
+ }
+
+ public boolean hasMutableProperties() {
+ return entityMetamodel.hasMutableProperties();
+ }
+
+ public boolean isMutable() {
+ return entityMetamodel.isMutable();
+ }
+
+ public boolean isAbstract() {
+ return entityMetamodel.isAbstract();
+ }
+
+ public boolean hasSubclasses() {
+ return entityMetamodel.hasSubclasses();
+ }
+
+ public boolean hasProxy() {
+ return entityMetamodel.isLazy();
+ }
+
+ public IdentifierGenerator getIdentifierGenerator() throws HibernateException {
+ return entityMetamodel.getIdentifierProperty().getIdentifierGenerator();
+ }
+
+ public String getRootEntityName() {
+ return entityMetamodel.getRootName();
+ }
+
+ public ClassMetadata getClassMetadata() {
+ return this;
+ }
+
+ public String getMappedSuperclass() {
+ return entityMetamodel.getSuperclass();
+ }
+
+ public boolean isExplicitPolymorphism() {
+ return entityMetamodel.isExplicitPolymorphism();
+ }
+
+ protected boolean useDynamicUpdate() {
+ return entityMetamodel.isDynamicUpdate();
+ }
+
+ protected boolean useDynamicInsert() {
+ return entityMetamodel.isDynamicInsert();
+ }
+
+ protected boolean hasEmbeddedCompositeIdentifier() {
+ return entityMetamodel.getIdentifierProperty().isEmbedded();
+ }
+
+ public boolean canExtractIdOutOfEntity() {
+ return hasIdentifierProperty() || hasEmbeddedCompositeIdentifier() || hasIdentifierMapper();
+ }
+
+ private boolean hasIdentifierMapper() {
+ return entityMetamodel.getIdentifierProperty().hasIdentifierMapper();
+ }
+
+ public String[] getKeyColumnNames() {
+ return getIdentifierColumnNames();
+ }
+
+ public String getName() {
+ return getEntityName();
+ }
+
+ public boolean isCollection() {
+ return false;
+ }
+
+ public boolean consumesEntityAlias() {
+ return true;
+ }
+
+ public boolean consumesCollectionAlias() {
+ return false;
+ }
+
+ public Type getPropertyType(String propertyName) throws MappingException {
+ return propertyMapping.toType(propertyName);
+ }
+
+ public Type getType() {
+ return entityMetamodel.getEntityType();
+ }
+
+ public boolean isSelectBeforeUpdateRequired() {
+ return entityMetamodel.isSelectBeforeUpdate();
+ }
+
+ protected final int optimisticLockMode() {
+ return entityMetamodel.getOptimisticLockMode();
+ }
+
+ public Object createProxy(Serializable id, SessionImplementor session) throws HibernateException {
+ return entityMetamodel.getTuplizer( session.getEntityMode() )
+ .createProxy( id, session );
+ }
+
+ public String toString() {
+ return StringHelper.unqualify( getClass().getName() ) +
+ '(' + entityMetamodel.getName() + ')';
+ }
+
+ public final String selectFragment(
+ Joinable rhs,
+ String rhsAlias,
+ String lhsAlias,
+ String entitySuffix,
+ String collectionSuffix,
+ boolean includeCollectionColumns) {
+ return selectFragment( lhsAlias, entitySuffix );
+ }
+
+ public boolean isInstrumented(EntityMode entityMode) {
+ EntityTuplizer tuplizer = entityMetamodel.getTuplizerOrNull(entityMode);
+ return tuplizer!=null && tuplizer.isInstrumented();
+ }
+
+ public boolean hasInsertGeneratedProperties() {
+ return entityMetamodel.hasInsertGeneratedValues();
+ }
+
+ public boolean hasUpdateGeneratedProperties() {
+ return entityMetamodel.hasUpdateGeneratedValues();
+ }
+
+ public boolean isVersionPropertyGenerated() {
+ return isVersioned() && ( getPropertyUpdateGenerationInclusions() [ getVersionProperty() ] != ValueInclusion.NONE );
+ }
+
+ public boolean isVersionPropertyInsertable() {
+ return isVersioned() && getPropertyInsertability() [ getVersionProperty() ];
+ }
+
+ public void afterInitialize(Object entity, boolean lazyPropertiesAreUnfetched, SessionImplementor session) {
+ getTuplizer( session ).afterInitialize( entity, lazyPropertiesAreUnfetched, session );
+ }
+
+ public String[] getPropertyNames() {
+ return entityMetamodel.getPropertyNames();
+ }
+
+ public Type[] getPropertyTypes() {
+ return entityMetamodel.getPropertyTypes();
+ }
+
+ public boolean[] getPropertyLaziness() {
+ return entityMetamodel.getPropertyLaziness();
+ }
+
+ public boolean[] getPropertyUpdateability() {
+ return entityMetamodel.getPropertyUpdateability();
+ }
+
+ public boolean[] getPropertyCheckability() {
+ return entityMetamodel.getPropertyCheckability();
+ }
+
+ public boolean[] getNonLazyPropertyUpdateability() {
+ return entityMetamodel.getNonlazyPropertyUpdateability();
+ }
+
+ public boolean[] getPropertyInsertability() {
+ return entityMetamodel.getPropertyInsertability();
+ }
+
+ public ValueInclusion[] getPropertyInsertGenerationInclusions() {
+ return entityMetamodel.getPropertyInsertGenerationInclusions();
+ }
+
+ public ValueInclusion[] getPropertyUpdateGenerationInclusions() {
+ return entityMetamodel.getPropertyUpdateGenerationInclusions();
+ }
+
+ public boolean[] getPropertyNullability() {
+ return entityMetamodel.getPropertyNullability();
+ }
+
+ public boolean[] getPropertyVersionability() {
+ return entityMetamodel.getPropertyVersionability();
+ }
+
+ public CascadeStyle[] getPropertyCascadeStyles() {
+ return entityMetamodel.getCascadeStyles();
+ }
+
+ public final Class getMappedClass(EntityMode entityMode) {
+ Tuplizer tup = entityMetamodel.getTuplizerOrNull(entityMode);
+ return tup==null ? null : tup.getMappedClass();
+ }
+
+ public boolean implementsLifecycle(EntityMode entityMode) {
+ return getTuplizer( entityMode ).isLifecycleImplementor();
+ }
+
+ public boolean implementsValidatable(EntityMode entityMode) {
+ return getTuplizer( entityMode ).isValidatableImplementor();
+ }
+
+ public Class getConcreteProxyClass(EntityMode entityMode) {
+ return getTuplizer( entityMode ).getConcreteProxyClass();
+ }
+
+ public void setPropertyValues(Object object, Object[] values, EntityMode entityMode)
+ throws HibernateException {
+ getTuplizer( entityMode ).setPropertyValues( object, values );
+ }
+
+ public void setPropertyValue(Object object, int i, Object value, EntityMode entityMode)
+ throws HibernateException {
+ getTuplizer( entityMode ).setPropertyValue( object, i, value );
+ }
+
+ public Object[] getPropertyValues(Object object, EntityMode entityMode)
+ throws HibernateException {
+ return getTuplizer( entityMode ).getPropertyValues( object );
+ }
+
+ public Object getPropertyValue(Object object, int i, EntityMode entityMode)
+ throws HibernateException {
+ return getTuplizer( entityMode ).getPropertyValue( object , i );
+ }
+
+ public Object getPropertyValue(Object object, String propertyName, EntityMode entityMode)
+ throws HibernateException {
+ return getTuplizer( entityMode ).getPropertyValue( object, propertyName );
+ }
+
+ public Serializable getIdentifier(Object object, EntityMode entityMode)
+ throws HibernateException {
+ return getTuplizer( entityMode ).getIdentifier( object );
+ }
+
+ public void setIdentifier(Object object, Serializable id, EntityMode entityMode)
+ throws HibernateException {
+ getTuplizer( entityMode ).setIdentifier( object, id );
+ }
+
+ public Object getVersion(Object object, EntityMode entityMode)
+ throws HibernateException {
+ return getTuplizer( entityMode ).getVersion( object );
+ }
+
+ public Object instantiate(Serializable id, EntityMode entityMode)
+ throws HibernateException {
+ return getTuplizer( entityMode ).instantiate( id );
+ }
+
+ public boolean isInstance(Object object, EntityMode entityMode) {
+ return getTuplizer( entityMode ).isInstance( object );
+ }
+
+ public boolean hasUninitializedLazyProperties(Object object, EntityMode entityMode) {
+ return getTuplizer( entityMode ).hasUninitializedLazyProperties( object );
+ }
+
+ public void resetIdentifier(Object entity, Serializable currentId, Object currentVersion, EntityMode entityMode) {
+ getTuplizer( entityMode ).resetIdentifier( entity, currentId, currentVersion );
+ }
+
+ public EntityPersister getSubclassEntityPersister(Object instance, SessionFactoryImplementor factory, EntityMode entityMode) {
+ if ( !hasSubclasses() ) {
+ return this;
+ }
+ else {
+ // TODO : really need a way to do something like :
+ // getTuplizer(entityMode).determineConcreteSubclassEntityName(instance)
+ Class clazz = instance.getClass();
+ if ( clazz == getMappedClass( entityMode ) ) {
+ return this;
+ }
+ else {
+ String subclassEntityName = getSubclassEntityName( clazz );
+ if ( subclassEntityName == null ) {
+ throw new HibernateException(
+ "instance not of expected entity type: " + clazz.getName() +
+ " is not a: " + getEntityName()
+ );
+ }
+ else {
+ return factory.getEntityPersister( subclassEntityName );
+ }
+ }
+ }
+ }
+
+ public EntityMode guessEntityMode(Object object) {
+ return entityMetamodel.guessEntityMode(object);
+ }
+
+ public boolean isMultiTable() {
+ return false;
+ }
+
+ public String getTemporaryIdTableName() {
+ return temporaryIdTableName;
+ }
+
+ public String getTemporaryIdTableDDL() {
+ return temporaryIdTableDDL;
+ }
+
+ protected int getPropertySpan() {
+ return entityMetamodel.getPropertySpan();
+ }
+
+ public Object[] getPropertyValuesToInsert(Object object, Map mergeMap, SessionImplementor session) throws HibernateException {
+ return getTuplizer( session.getEntityMode() ).getPropertyValuesToInsert( object, mergeMap, session );
+ }
+
+ public void processInsertGeneratedProperties(Serializable id, Object entity, Object[] state, SessionImplementor session) {
+ if ( !hasInsertGeneratedProperties() ) {
+ throw new AssertionFailure("no insert-generated properties");
+ }
+ processGeneratedProperties( id, entity, state, session, sqlInsertGeneratedValuesSelectString, getPropertyInsertGenerationInclusions() );
+ }
+
+ public void processUpdateGeneratedProperties(Serializable id, Object entity, Object[] state, SessionImplementor session) {
+ if ( !hasUpdateGeneratedProperties() ) {
+ throw new AssertionFailure("no update-generated properties");
+ }
+ processGeneratedProperties( id, entity, state, session, sqlUpdateGeneratedValuesSelectString, getPropertyUpdateGenerationInclusions() );
+ }
+
+ private void processGeneratedProperties(
+ Serializable id,
+ Object entity,
+ Object[] state,
+ SessionImplementor session,
+ String selectionSQL,
+ ValueInclusion[] includeds) {
+
+ session.getBatcher().executeBatch(); //force immediate execution of the insert
+
+ try {
+ PreparedStatement ps = session.getBatcher().prepareSelectStatement( selectionSQL );
+ ResultSet rs = null;
+ try {
+ getIdentifierType().nullSafeSet( ps, id, 1, session );
+ rs = session.getBatcher().getResultSet( ps );
+ if ( !rs.next() ) {
+ throw new HibernateException(
+ "Unable to locate row for retrieval of generated properties: " +
+ MessageHelper.infoString( this, id, getFactory() )
+ );
+ }
+ for ( int i = 0; i < getPropertySpan(); i++ ) {
+ if ( includeds[i] != ValueInclusion.NONE ) {
+ Object hydratedState = getPropertyTypes()[i].hydrate( rs, getPropertyAliases( "", i ), session, entity );
+ state[i] = getPropertyTypes()[i].resolve( hydratedState, session, entity );
+ setPropertyValue( entity, i, state[i], session.getEntityMode() );
+ }
+ }
+ }
+ finally {
+ session.getBatcher().closeQueryStatement( ps, rs );
+ }
+ }
+ catch( SQLException sqle ) {
+ JDBCExceptionHelper.convert(
+ getFactory().getSQLExceptionConverter(),
+ sqle,
+ "unable to select generated column values",
+ selectionSQL
+ );
+ }
+
+ }
+
+ public String getIdentifierPropertyName() {
+ return entityMetamodel.getIdentifierProperty().getName();
+ }
+
+ public Type getIdentifierType() {
+ return entityMetamodel.getIdentifierProperty().getType();
+ }
+
+ public boolean hasSubselectLoadableCollections() {
+ return hasSubselectLoadableCollections;
+ }
+
+ public int[] getNaturalIdentifierProperties() {
+ return entityMetamodel.getNaturalIdentifierProperties();
+ }
+
+ public Object[] getNaturalIdentifierSnapshot(Serializable id, SessionImplementor session) throws HibernateException {
+ if ( !hasNaturalIdentifier() ) {
+ throw new MappingException( "persistent class did not define a natural-id : " + MessageHelper.infoString( this ) );
+ }
+ if ( log.isTraceEnabled() ) {
+ log.trace( "Getting current natural-id snapshot state for: " + MessageHelper.infoString( this, id, getFactory() ) );
+ }
+
+ int[] naturalIdPropertyIndexes = getNaturalIdentifierProperties();
+ int naturalIdPropertyCount = naturalIdPropertyIndexes.length;
+ boolean[] naturalIdMarkers = new boolean[ getPropertySpan() ];
+ Type[] extractionTypes = new Type[ naturalIdPropertyCount ];
+ for ( int i = 0; i < naturalIdPropertyCount; i++ ) {
+ extractionTypes[i] = getPropertyTypes()[ naturalIdPropertyIndexes[i] ];
+ naturalIdMarkers[ naturalIdPropertyIndexes[i] ] = true;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+ // TODO : look at perhaps caching this...
+ Select select = new Select( getFactory().getDialect() );
+ if ( getFactory().getSettings().isCommentsEnabled() ) {
+ select.setComment( "get current natural-id state " + getEntityName() );
+ }
+ select.setSelectClause( concretePropertySelectFragmentSansLeadingComma( getRootAlias(), naturalIdMarkers ) );
+ select.setFromClause( fromTableFragment( getRootAlias() ) + fromJoinFragment( getRootAlias(), true, false ) );
+
+ String[] aliasedIdColumns = StringHelper.qualify( getRootAlias(), getIdentifierColumnNames() );
+ String whereClause = new StringBuffer()
+ .append( StringHelper.join( "=? and ",
+ aliasedIdColumns ) )
+ .append( "=?" )
+ .append( whereJoinFragment( getRootAlias(), true, false ) )
+ .toString();
+
+ String sql = select.setOuterJoins( "", "" )
+ .setWhereClause( whereClause )
+ .toStatementString();
+ ///////////////////////////////////////////////////////////////////////
+
+ Object[] snapshot = new Object[ naturalIdPropertyCount ];
+ try {
+ PreparedStatement ps = session.getBatcher().prepareSelectStatement( sql );
+ try {
+ getIdentifierType().nullSafeSet( ps, id, 1, session );
+ ResultSet rs = ps.executeQuery();
+ try {
+ //if there is no resulting row, return null
+ if ( !rs.next() ) {
+ return null;
+ }
+
+ for ( int i = 0; i < naturalIdPropertyCount; i++ ) {
+ snapshot[i] = extractionTypes[i].hydrate( rs, getPropertyAliases( "", naturalIdPropertyIndexes[i] ), session, null );
+ }
+ return snapshot;
+ }
+ finally {
+ rs.close();
+ }
+ }
+ finally {
+ session.getBatcher().closeStatement( ps );
+ }
+ }
+ catch ( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ getFactory().getSQLExceptionConverter(),
+ sqle,
+ "could not retrieve snapshot: " +
+ MessageHelper.infoString( this, id, getFactory() ),
+ sql
+ );
+ }
+ }
+
+ protected String concretePropertySelectFragmentSansLeadingComma(String alias, boolean[] include) {
+ String concretePropertySelectFragment = concretePropertySelectFragment( alias, include );
+ int firstComma = concretePropertySelectFragment.indexOf( ", " );
+ if ( firstComma == 0 ) {
+ concretePropertySelectFragment = concretePropertySelectFragment.substring( 2 );
+ }
+ return concretePropertySelectFragment;
+ }
+ public boolean hasNaturalIdentifier() {
+ return entityMetamodel.hasNaturalIdentifier();
+ }
+
+ public void setPropertyValue(Object object, String propertyName, Object value, EntityMode entityMode)
+ throws HibernateException {
+ getTuplizer( entityMode ).setPropertyValue( object, propertyName, value );
+ }
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/AbstractPropertyMapping.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/AbstractPropertyMapping.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/AbstractPropertyMapping.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,234 @@
+//$Id: AbstractPropertyMapping.java 10852 2006-11-21 17:39:14Z steve.ebersole at jboss.com $
+package org.hibernate.persister.entity;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.hibernate.MappingException;
+import org.hibernate.QueryException;
+import org.hibernate.engine.Mapping;
+import org.hibernate.sql.Template;
+import org.hibernate.type.AbstractComponentType;
+import org.hibernate.type.AssociationType;
+import org.hibernate.type.EntityType;
+import org.hibernate.type.Type;
+import org.hibernate.util.ArrayHelper;
+import org.hibernate.util.StringHelper;
+
+/**
+ * Base implementation of a <tt>PropertyMapping</tt>
+ *
+ * @author Gavin King
+ */
+public abstract class AbstractPropertyMapping implements PropertyMapping {
+
+ private final Map typesByPropertyPath = new HashMap();
+ private final Map columnsByPropertyPath = new HashMap();
+ private final Map formulaTemplatesByPropertyPath = new HashMap();
+
+ public String[] getIdentifierColumnNames() {
+ throw new UnsupportedOperationException("one-to-one is not supported here");
+ }
+
+ protected abstract String getEntityName();
+
+ public Type toType(String propertyName) throws QueryException {
+ Type type = (Type) typesByPropertyPath.get(propertyName);
+ if ( type == null ) {
+ throw propertyException( propertyName );
+ }
+ return type;
+ }
+
+ protected final QueryException propertyException(String propertyName) throws QueryException {
+ return new QueryException( "could not resolve property: " + propertyName + " of: " + getEntityName() );
+ }
+
+ public String[] getColumnNames(String propertyName) {
+ String[] cols = (String[]) columnsByPropertyPath.get(propertyName);
+ if (cols==null) {
+ throw new MappingException("unknown property: " + propertyName);
+ }
+ return cols;
+ }
+
+ public String[] toColumns(String alias, String propertyName) throws QueryException {
+ //TODO: *two* hashmap lookups here is one too many...
+ String[] columns = (String[]) columnsByPropertyPath.get(propertyName);
+ if ( columns == null ) {
+ throw propertyException( propertyName );
+ }
+ String[] templates = (String[]) formulaTemplatesByPropertyPath.get(propertyName);
+ String[] result = new String[columns.length];
+ for ( int i=0; i<columns.length; i++ ) {
+ if ( columns[i]==null ) {
+ result[i] = StringHelper.replace( templates[i], Template.TEMPLATE, alias );
+ }
+ else {
+ result[i] = StringHelper.qualify( alias, columns[i] );
+ }
+ }
+ return result;
+ }
+
+ public String[] toColumns(String propertyName) throws QueryException {
+ String[] columns = (String[]) columnsByPropertyPath.get(propertyName);
+ if ( columns == null ) {
+ throw propertyException( propertyName );
+ }
+ String[] templates = (String[]) formulaTemplatesByPropertyPath.get(propertyName);
+ String[] result = new String[columns.length];
+ for ( int i=0; i<columns.length; i++ ) {
+ if ( columns[i]==null ) {
+ result[i] = StringHelper.replace( templates[i], Template.TEMPLATE, "" );
+ }
+ else {
+ result[i] = columns[i];
+ }
+ }
+ return result;
+ }
+
+ protected void addPropertyPath(String path, Type type, String[] columns, String[] formulaTemplates) {
+ typesByPropertyPath.put(path, type);
+ columnsByPropertyPath.put(path, columns);
+ if (formulaTemplates!=null) {
+ formulaTemplatesByPropertyPath.put(path, formulaTemplates);
+ }
+ }
+
+ /*protected void initPropertyPaths(
+ final String path,
+ final Type type,
+ final String[] columns,
+ final String[] formulaTemplates,
+ final Mapping factory)
+ throws MappingException {
+ //addFormulaPropertyPath(path, type, formulaTemplates);
+ initPropertyPaths(path, type, columns, formulaTemplates, factory);
+ }*/
+
+ protected void initPropertyPaths(
+ final String path,
+ final Type type,
+ String[] columns,
+ final String[] formulaTemplates,
+ final Mapping factory)
+ throws MappingException {
+
+ if ( columns.length!=type.getColumnSpan(factory) ) {
+ throw new MappingException(
+ "broken column mapping for: " + path +
+ " of: " + getEntityName()
+ );
+ }
+
+ if ( type.isAssociationType() ) {
+ AssociationType actype = (AssociationType) type;
+ if ( actype.useLHSPrimaryKey() ) {
+ columns = getIdentifierColumnNames();
+ }
+ else {
+ String foreignKeyProperty = actype.getLHSPropertyName();
+ if ( foreignKeyProperty!=null && !path.equals(foreignKeyProperty) ) {
+ //TODO: this requires that the collection is defined after the
+ // referenced property in the mapping file (ok?)
+ columns = (String[]) columnsByPropertyPath.get(foreignKeyProperty);
+ if (columns==null) return; //get em on the second pass!
+ }
+ }
+ }
+
+ if (path!=null) addPropertyPath(path, type, columns, formulaTemplates);
+
+ if ( type.isComponentType() ) {
+ AbstractComponentType actype = (AbstractComponentType) type;
+ initComponentPropertyPaths( path, actype, columns, formulaTemplates, factory );
+ if ( actype.isEmbedded() ) {
+ initComponentPropertyPaths(
+ path==null ? null : StringHelper.qualifier(path),
+ actype,
+ columns,
+ formulaTemplates,
+ factory
+ );
+ }
+ }
+ else if ( type.isEntityType() ) {
+ initIdentifierPropertyPaths( path, (EntityType) type, columns, factory );
+ }
+ }
+
+ protected void initIdentifierPropertyPaths(
+ final String path,
+ final EntityType etype,
+ final String[] columns,
+ final Mapping factory) throws MappingException {
+
+ Type idtype = etype.getIdentifierOrUniqueKeyType( factory );
+ String idPropName = etype.getIdentifierOrUniqueKeyPropertyName(factory);
+ boolean hasNonIdentifierPropertyNamedId = hasNonIdentifierPropertyNamedId( etype, factory );
+
+ if ( etype.isReferenceToPrimaryKey() ) {
+ if ( !hasNonIdentifierPropertyNamedId ) {
+ String idpath1 = extendPath(path, EntityPersister.ENTITY_ID);
+ addPropertyPath(idpath1, idtype, columns, null);
+ initPropertyPaths(idpath1, idtype, columns, null, factory);
+ }
+ }
+
+ if (idPropName!=null) {
+ String idpath2 = extendPath(path, idPropName);
+ addPropertyPath(idpath2, idtype, columns, null);
+ initPropertyPaths(idpath2, idtype, columns, null, factory);
+ }
+ }
+
+ private boolean hasNonIdentifierPropertyNamedId(final EntityType entityType, final Mapping factory) {
+ // TODO : would be great to have a Mapping#hasNonIdentifierPropertyNamedId method
+ // I don't believe that Mapping#getReferencedPropertyType accounts for the identifier property; so
+ // if it returns for a property named 'id', then we should have a non-id field named id
+ try {
+ return factory.getReferencedPropertyType( entityType.getAssociatedEntityName(), EntityPersister.ENTITY_ID ) != null;
+ }
+ catch( MappingException e ) {
+ return false;
+ }
+ }
+
+ protected void initComponentPropertyPaths(
+ final String path,
+ final AbstractComponentType type,
+ final String[] columns,
+ String[] formulaTemplates, final Mapping factory)
+ throws MappingException {
+
+ Type[] types = type.getSubtypes();
+ String[] properties = type.getPropertyNames();
+ int begin=0;
+ for ( int i=0; i<properties.length; i++ ) {
+ String subpath = extendPath( path, properties[i] );
+ try {
+ int length = types[i].getColumnSpan(factory);
+ String[] columnSlice = ArrayHelper.slice(columns, begin, length);
+ String[] formulaSlice = formulaTemplates==null ?
+ null : ArrayHelper.slice(formulaTemplates, begin, length);
+ initPropertyPaths(subpath, types[i], columnSlice, formulaSlice, factory);
+ begin+=length;
+ }
+ catch (Exception e) {
+ throw new MappingException("bug in initComponentPropertyPaths", e);
+ }
+ }
+ }
+
+ private static String extendPath(String path, String property) {
+ if ( path==null || "".equals(path) ) {
+ return property;
+ }
+ else {
+ return StringHelper.qualify(path, property);
+ }
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/BasicEntityPropertyMapping.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/BasicEntityPropertyMapping.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/BasicEntityPropertyMapping.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,38 @@
+//$Id: BasicEntityPropertyMapping.java 7636 2005-07-24 23:51:12Z oneovthafew $
+package org.hibernate.persister.entity;
+
+import org.hibernate.QueryException;
+import org.hibernate.type.Type;
+
+/**
+ * @author Gavin King
+ */
+public class BasicEntityPropertyMapping extends AbstractPropertyMapping {
+
+ private final AbstractEntityPersister persister;
+
+ public BasicEntityPropertyMapping(AbstractEntityPersister persister) {
+ this.persister = persister;
+ }
+
+ public String[] getIdentifierColumnNames() {
+ return persister.getIdentifierColumnNames();
+ }
+
+ protected String getEntityName() {
+ return persister.getEntityName();
+ }
+
+ public Type getType() {
+ return persister.getType();
+ }
+
+ public String[] toColumns(final String alias, final String propertyName) throws QueryException {
+ return super.toColumns(
+ persister.generateTableAlias( alias, persister.getSubclassPropertyTableNumber(propertyName) ),
+ propertyName
+ );
+ }
+
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/BasicEntityPropertyMapping.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/EntityPersister.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/EntityPersister.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/EntityPersister.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,660 @@
+//$Id: EntityPersister.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.persister.entity;
+
+import java.io.Serializable;
+import java.util.Map;
+
+import org.hibernate.HibernateException;
+import org.hibernate.LockMode;
+import org.hibernate.MappingException;
+import org.hibernate.EntityMode;
+import org.hibernate.tuple.entity.EntityMetamodel;
+import org.hibernate.cache.CacheConcurrencyStrategy;
+import org.hibernate.cache.OptimisticCacheSource;
+import org.hibernate.cache.access.EntityRegionAccessStrategy;
+import org.hibernate.cache.entry.CacheEntryStructure;
+import org.hibernate.engine.CascadeStyle;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.ValueInclusion;
+import org.hibernate.id.IdentifierGenerator;
+import org.hibernate.metadata.ClassMetadata;
+import org.hibernate.type.Type;
+import org.hibernate.type.VersionType;
+
+/**
+ * Implementors define mapping and persistence logic for a particular
+ * strategy of entity mapping. An instance of entity persisters corresponds
+ * to a given mapped entity.
+ * <p/>
+ * Implementors must be threadsafe (preferrably immutable) and must provide a constructor
+ * matching the signature of: {@link org.hibernate.mapping.PersistentClass}, {@link org.hibernate.engine.SessionFactoryImplementor}
+ *
+ * @author Gavin King
+ */
+public interface EntityPersister extends OptimisticCacheSource {
+
+ /**
+ * The property name of the "special" identifier property in HQL
+ */
+ public static final String ENTITY_ID = "id";
+
+ /**
+ * Finish the initialization of this object.
+ * <p/>
+ * Called only once per {@link org.hibernate.SessionFactory} lifecycle,
+ * after all entity persisters have been instantiated.
+ *
+ * @throws org.hibernate.MappingException Indicates an issue in the metdata.
+ */
+ public void postInstantiate() throws MappingException;
+
+ /**
+ * Return the SessionFactory to which this persister "belongs".
+ *
+ * @return The owning SessionFactory.
+ */
+ public SessionFactoryImplementor getFactory();
+
+
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ // stuff that is persister-centric and/or EntityInfo-centric ~~~~~~~~~~~~~~
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Returns an object that identifies the space in which identifiers of
+ * this entity hierarchy are unique. Might be a table name, a JNDI URL, etc.
+ *
+ * @return The root entity name.
+ */
+ public String getRootEntityName();
+
+ /**
+ * The entity name which this persister maps.
+ *
+ * @return The name of the entity which this persister maps.
+ */
+ public String getEntityName();
+
+ /**
+ * Retrieve the underlying entity metamodel instance...
+ *
+ *@return The metamodel
+ */
+ public EntityMetamodel getEntityMetamodel();
+
+ /**
+ * Determine whether the given name represents a subclass entity
+ * (or this entity itself) of the entity mapped by this persister.
+ *
+ * @param entityName The entity name to be checked.
+ * @return True if the given entity name represents either the entity
+ * mapped by this persister or one of its subclass entities; false
+ * otherwise.
+ */
+ public boolean isSubclassEntityName(String entityName);
+
+ /**
+ * Returns an array of objects that identify spaces in which properties of
+ * this entity are persisted, for instances of this class only.
+ * <p/>
+ * For most implementations, this returns the complete set of table names
+ * to which instances of the mapped entity are persisted (not accounting
+ * for superclass entity mappings).
+ *
+ * @return The property spaces.
+ */
+ public Serializable[] getPropertySpaces();
+
+ /**
+ * Returns an array of objects that identify spaces in which properties of
+ * this entity are persisted, for instances of this class and its subclasses.
+ * <p/>
+ * Much like {@link #getPropertySpaces()}, except that here we include subclass
+ * entity spaces.
+ *
+ * @return The query spaces.
+ */
+ public Serializable[] getQuerySpaces();
+
+ /**
+ * Determine whether this entity supports dynamic proxies.
+ *
+ * @return True if the entity has dynamic proxy support; false otherwise.
+ */
+ public boolean hasProxy();
+
+ /**
+ * Determine whether this entity contains references to persistent collections.
+ *
+ * @return True if the entity does contain persistent collections; false otherwise.
+ */
+ public boolean hasCollections();
+
+ /**
+ * Determine whether any properties of this entity are considered mutable.
+ *
+ * @return True if any properties of the entity are mutable; false otherwise (meaning none are).
+ */
+ public boolean hasMutableProperties();
+
+ /**
+ * Determine whether this entity contains references to persistent collections
+ * which are fetchable by subselect?
+ *
+ * @return True if the entity contains collections fetchable by subselect; false otherwise.
+ */
+ public boolean hasSubselectLoadableCollections();
+
+ /**
+ * Determine whether this entity has any non-none cascading.
+ *
+ * @return True if the entity has any properties with a cscade other than NONE;
+ * false otherwise (aka, no cascading).
+ */
+ public boolean hasCascades();
+
+ /**
+ * Determine whether instances of this entity are considered mutable.
+ *
+ * @return True if the entity is considered mutable; false otherwise.
+ */
+ public boolean isMutable();
+
+ /**
+ * Determine whether the entity is inherited one or more other entities.
+ * In other words, is this entity a subclass of other entities.
+ *
+ * @return True if other entities extend this entity; false otherwise.
+ */
+ public boolean isInherited();
+
+ /**
+ * Are identifiers of this entity assigned known before the insert execution?
+ * Or, are they generated (in the database) by the insert execution.
+ *
+ * @return True if identifiers for this entity are generated by the insert
+ * execution.
+ */
+ public boolean isIdentifierAssignedByInsert();
+
+ /**
+ * Get the type of a particular property by name.
+ *
+ * @param propertyName The name of the property for which to retrieve
+ * the typpe.
+ * @return The type.
+ * @throws org.hibernate.MappingException Typically indicates an unknown
+ * property name.
+ */
+ public Type getPropertyType(String propertyName) throws MappingException;
+
+ /**
+ * Compare the two snapshots to determine if they represent dirty state.
+ *
+ * @param currentState The current snapshot
+ * @param previousState The baseline snapshot
+ * @param owner The entity containing the state
+ * @param session The originating session
+ * @return The indices of all dirty properties, or null if no properties
+ * were dirty.
+ */
+ public int[] findDirty(Object[] currentState, Object[] previousState, Object owner, SessionImplementor session);
+
+ /**
+ * Compare the two snapshots to determine if they represent modified state.
+ *
+ * @param old The baseline snapshot
+ * @param current The current snapshot
+ * @param object The entity containing the state
+ * @param session The originating session
+ * @return The indices of all modified properties, or null if no properties
+ * were modified.
+ */
+ public int[] findModified(Object[] old, Object[] current, Object object, SessionImplementor session);
+
+ /**
+ * Determine whether the entity has a particular property holding
+ * the identifier value.
+ *
+ * @return True if the entity has a specific property holding identifier value.
+ */
+ public boolean hasIdentifierProperty();
+
+ /**
+ * Determine whether detahced instances of this entity carry their own
+ * identifier value.
+ * <p/>
+ * The other option is the deperecated feature where users could supply
+ * the id during session calls.
+ *
+ * @return True if either (1) {@link #hasIdentifierProperty()} or
+ * (2) the identifier is an embedded composite identifier; false otherwise.
+ */
+ public boolean canExtractIdOutOfEntity();
+
+ /**
+ * Determine whether optimistic locking by column is enabled for this
+ * entity.
+ *
+ * @return True if optimistic locking by column (i.e., <version/> or
+ * <timestamp/>) is enabled; false otherwise.
+ */
+ public boolean isVersioned();
+
+ /**
+ * If {@link #isVersioned()}, then what is the type of the property
+ * holding the locking value.
+ *
+ * @return The type of the version property; or null, if not versioned.
+ */
+ public VersionType getVersionType();
+
+ /**
+ * If {@link #isVersioned()}, then what is the index of the property
+ * holding the locking value.
+ *
+ * @return The type of the version property; or -66, if not versioned.
+ */
+ public int getVersionProperty();
+
+ /**
+ * Determine whether this entity defines a natural identifier.
+ *
+ * @return True if the entity defines a natural id; false otherwise.
+ */
+ public boolean hasNaturalIdentifier();
+
+ /**
+ * If the entity defines a natural id ({@link #hasNaturalIdentifier()}), which
+ * properties make up the natural id.
+ *
+ * @return The indices of the properties making of the natural id; or
+ * null, if no natural id is defined.
+ */
+ public int[] getNaturalIdentifierProperties();
+
+ /**
+ * Retrieve the current state of the natural-id properties from the database.
+ *
+ * @param id The identifier of the entity for which to retrieve the naturak-id values.
+ * @param session The session from which the request originated.
+ * @return The natural-id snapshot.
+ */
+ public Object[] getNaturalIdentifierSnapshot(Serializable id, SessionImplementor session);
+
+ /**
+ * Determine which identifier generation strategy is used for this entity.
+ *
+ * @return The identifier generation strategy.
+ */
+ public IdentifierGenerator getIdentifierGenerator();
+
+ /**
+ * Determine whether this entity defines any lazy properties (ala
+ * bytecode instrumentation).
+ *
+ * @return True if the entity has properties mapped as lazy; false otherwise.
+ */
+ public boolean hasLazyProperties();
+
+ /**
+ * Load an instance of the persistent class.
+ */
+ public Object load(Serializable id, Object optionalObject, LockMode lockMode, SessionImplementor session)
+ throws HibernateException;
+
+ /**
+ * Do a version check (optional operation)
+ */
+ public void lock(Serializable id, Object version, Object object, LockMode lockMode, SessionImplementor session)
+ throws HibernateException;
+
+ /**
+ * Persist an instance
+ */
+ public void insert(Serializable id, Object[] fields, Object object, SessionImplementor session)
+ throws HibernateException;
+
+ /**
+ * Persist an instance, using a natively generated identifier (optional operation)
+ */
+ public Serializable insert(Object[] fields, Object object, SessionImplementor session)
+ throws HibernateException;
+
+ /**
+ * Delete a persistent instance
+ */
+ public void delete(Serializable id, Object version, Object object, SessionImplementor session)
+ throws HibernateException;
+
+ /**
+ * Update a persistent instance
+ */
+ public void update(
+ Serializable id,
+ Object[] fields,
+ int[] dirtyFields,
+ boolean hasDirtyCollection,
+ Object[] oldFields,
+ Object oldVersion,
+ Object object,
+ Object rowId,
+ SessionImplementor session
+ ) throws HibernateException;
+
+ /**
+ * Get the Hibernate types of the class properties
+ */
+ public Type[] getPropertyTypes();
+
+ /**
+ * Get the names of the class properties - doesn't have to be the names of the
+ * actual Java properties (used for XML generation only)
+ */
+ public String[] getPropertyNames();
+
+ /**
+ * Get the "insertability" of the properties of this class
+ * (does the property appear in an SQL INSERT)
+ */
+ public boolean[] getPropertyInsertability();
+
+ /**
+ * Which of the properties of this class are database generated values on insert?
+ */
+ public ValueInclusion[] getPropertyInsertGenerationInclusions();
+
+ /**
+ * Which of the properties of this class are database generated values on update?
+ */
+ public ValueInclusion[] getPropertyUpdateGenerationInclusions();
+
+ /**
+ * Get the "updateability" of the properties of this class
+ * (does the property appear in an SQL UPDATE)
+ */
+ public boolean[] getPropertyUpdateability();
+
+ /**
+ * Get the "checkability" of the properties of this class
+ * (is the property dirty checked, does the cache need
+ * to be updated)
+ */
+ public boolean[] getPropertyCheckability();
+
+ /**
+ * Get the nullability of the properties of this class
+ */
+ public boolean[] getPropertyNullability();
+
+ /**
+ * Get the "versionability" of the properties of this class
+ * (is the property optimistic-locked)
+ */
+ public boolean[] getPropertyVersionability();
+ public boolean[] getPropertyLaziness();
+ /**
+ * Get the cascade styles of the propertes (optional operation)
+ */
+ public CascadeStyle[] getPropertyCascadeStyles();
+
+ /**
+ * Get the identifier type
+ */
+ public Type getIdentifierType();
+
+ /**
+ * Get the name of the identifier property (or return null) - need not return the
+ * name of an actual Java property
+ */
+ public String getIdentifierPropertyName();
+
+ /**
+ * Should we always invalidate the cache instead of
+ * recaching updated state
+ */
+ public boolean isCacheInvalidationRequired();
+ /**
+ * Should lazy properties of this entity be cached?
+ */
+ public boolean isLazyPropertiesCacheable();
+ /**
+ * Does this class have a cache.
+ */
+ public boolean hasCache();
+ /**
+ * Get the cache (optional operation)
+ */
+ public EntityRegionAccessStrategy getCacheAccessStrategy();
+ /**
+ * Get the cache structure
+ */
+ public CacheEntryStructure getCacheEntryStructure();
+
+ /**
+ * Get the user-visible metadata for the class (optional operation)
+ */
+ public ClassMetadata getClassMetadata();
+
+ /**
+ * Is batch loading enabled?
+ */
+ public boolean isBatchLoadable();
+
+ /**
+ * Is select snapshot before update enabled?
+ */
+ public boolean isSelectBeforeUpdateRequired();
+
+ /**
+ * Get the current database state of the object, in a "hydrated" form, without
+ * resolving identifiers
+ * @return null if there is no row in the database
+ */
+ public Object[] getDatabaseSnapshot(Serializable id, SessionImplementor session)
+ throws HibernateException;
+
+ /**
+ * Get the current version of the object, or return null if there is no row for
+ * the given identifier. In the case of unversioned data, return any object
+ * if the row exists.
+ */
+ public Object getCurrentVersion(Serializable id, SessionImplementor session)
+ throws HibernateException;
+
+ public Object forceVersionIncrement(Serializable id, Object currentVersion, SessionImplementor session)
+ throws HibernateException;
+
+ /**
+ * Try to discover the entity mode from the entity instance
+ */
+ public EntityMode guessEntityMode(Object object);
+
+ /**
+ * Has the class actually been bytecode instrumented?
+ */
+ public boolean isInstrumented(EntityMode entityMode);
+
+ /**
+ * Does this entity define any properties as being database generated on insert?
+ *
+ * @return True if this entity contains at least one property defined
+ * as generated (including version property, but not identifier).
+ */
+ public boolean hasInsertGeneratedProperties();
+
+ /**
+ * Does this entity define any properties as being database generated on update?
+ *
+ * @return True if this entity contains at least one property defined
+ * as generated (including version property, but not identifier).
+ */
+ public boolean hasUpdateGeneratedProperties();
+
+ /**
+ * Does this entity contain a version property that is defined
+ * to be database generated?
+ *
+ * @return true if this entity contains a version property and that
+ * property has been marked as generated.
+ */
+ public boolean isVersionPropertyGenerated();
+
+
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ // stuff that is tuplizer-centric, but is passed a session ~~~~~~~~~~~~~~~~
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Called just after the entities properties have been initialized
+ */
+ public void afterInitialize(Object entity, boolean lazyPropertiesAreUnfetched, SessionImplementor session);
+
+ /**
+ * Called just after the entity has been reassociated with the session
+ */
+ public void afterReassociate(Object entity, SessionImplementor session);
+
+ /**
+ * Create a new proxy instance
+ */
+ public Object createProxy(Serializable id, SessionImplementor session)
+ throws HibernateException;
+
+ /**
+ * Is this a new transient instance?
+ */
+ public Boolean isTransient(Object object, SessionImplementor session) throws HibernateException;
+
+ /**
+ * Return the values of the insertable properties of the object (including backrefs)
+ */
+ public Object[] getPropertyValuesToInsert(Object object, Map mergeMap, SessionImplementor session) throws HibernateException;
+
+ /**
+ * Perform a select to retrieve the values of any generated properties
+ * back from the database, injecting these generated values into the
+ * given entity as well as writing this state to the
+ * {@link org.hibernate.engine.PersistenceContext}.
+ * <p/>
+ * Note, that because we update the PersistenceContext here, callers
+ * need to take care that they have already written the initial snapshot
+ * to the PersistenceContext before calling this method.
+ *
+ * @param id The entity's id value.
+ * @param entity The entity for which to get the state.
+ * @param state
+ * @param session The session
+ */
+ public void processInsertGeneratedProperties(Serializable id, Object entity, Object[] state, SessionImplementor session);
+ /**
+ * Perform a select to retrieve the values of any generated properties
+ * back from the database, injecting these generated values into the
+ * given entity as well as writing this state to the
+ * {@link org.hibernate.engine.PersistenceContext}.
+ * <p/>
+ * Note, that because we update the PersistenceContext here, callers
+ * need to take care that they have already written the initial snapshot
+ * to the PersistenceContext before calling this method.
+ *
+ * @param id The entity's id value.
+ * @param entity The entity for which to get the state.
+ * @param state
+ * @param session The session
+ */
+ public void processUpdateGeneratedProperties(Serializable id, Object entity, Object[] state, SessionImplementor session);
+
+
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ // stuff that is Tuplizer-centric ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * The persistent class, or null
+ */
+ public Class getMappedClass(EntityMode entityMode);
+
+ /**
+ * Does the class implement the <tt>Lifecycle</tt> interface.
+ */
+ public boolean implementsLifecycle(EntityMode entityMode);
+
+ /**
+ * Does the class implement the <tt>Validatable</tt> interface.
+ */
+ public boolean implementsValidatable(EntityMode entityMode);
+ /**
+ * Get the proxy interface that instances of <em>this</em> concrete class will be
+ * cast to (optional operation).
+ */
+ public Class getConcreteProxyClass(EntityMode entityMode);
+
+ /**
+ * Set the given values to the mapped properties of the given object
+ */
+ public void setPropertyValues(Object object, Object[] values, EntityMode entityMode) throws HibernateException;
+
+ /**
+ * Set the value of a particular property
+ */
+ public void setPropertyValue(Object object, int i, Object value, EntityMode entityMode) throws HibernateException;
+
+ /**
+ * Return the (loaded) values of the mapped properties of the object (not including backrefs)
+ */
+ public Object[] getPropertyValues(Object object, EntityMode entityMode) throws HibernateException;
+
+ /**
+ * Get the value of a particular property
+ */
+ public Object getPropertyValue(Object object, int i, EntityMode entityMode) throws HibernateException;
+
+ /**
+ * Get the value of a particular property
+ */
+ public Object getPropertyValue(Object object, String propertyName, EntityMode entityMode) throws HibernateException;
+
+ /**
+ * Get the identifier of an instance (throw an exception if no identifier property)
+ */
+ public Serializable getIdentifier(Object object, EntityMode entityMode) throws HibernateException;
+
+ /**
+ * Set the identifier of an instance (or do nothing if no identifier property)
+ */
+ public void setIdentifier(Object object, Serializable id, EntityMode entityMode) throws HibernateException;
+
+ /**
+ * Get the version number (or timestamp) from the object's version property (or return null if not versioned)
+ */
+ public Object getVersion(Object object, EntityMode entityMode) throws HibernateException;
+
+ /**
+ * Create a class instance initialized with the given identifier
+ */
+ public Object instantiate(Serializable id, EntityMode entityMode) throws HibernateException;
+
+ /**
+ * Is the given object an instance of this entity?
+ */
+ public boolean isInstance(Object object, EntityMode entityMode);
+
+ /**
+ * Does the given instance have any uninitialized lazy properties?
+ */
+ public boolean hasUninitializedLazyProperties(Object object, EntityMode entityMode);
+
+ /**
+ * Set the identifier and version of the given instance back
+ * to its "unsaved" value, returning the id
+ * @param currentId TODO
+ * @param currentVersion TODO
+ */
+ public void resetIdentifier(Object entity, Serializable currentId, Object currentVersion, EntityMode entityMode);
+
+ /**
+ * Get the persister for an instance of this class or a subclass
+ */
+ public EntityPersister getSubclassEntityPersister(Object instance, SessionFactoryImplementor factory, EntityMode entityMode);
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/Joinable.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/Joinable.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/Joinable.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,71 @@
+//$Id: Joinable.java 6828 2005-05-19 07:28:59Z steveebersole $
+package org.hibernate.persister.entity;
+
+import org.hibernate.MappingException;
+
+import java.util.Map;
+
+/**
+ * Anything that can be loaded by outer join - namely
+ * persisters for classes or collections.
+ *
+ * @author Gavin King
+ */
+public interface Joinable {
+ //should this interface extend PropertyMapping?
+
+ /**
+ * An identifying name; a class name or collection role name.
+ */
+ public String getName();
+ /**
+ * The table to join to.
+ */
+ public String getTableName();
+
+ /**
+ * All columns to select, when loading.
+ */
+ public String selectFragment(Joinable rhs, String rhsAlias, String lhsAlias, String currentEntitySuffix, String currentCollectionSuffix, boolean includeCollectionColumns);
+
+ /**
+ * Get the where clause part of any joins
+ * (optional operation)
+ */
+ public String whereJoinFragment(String alias, boolean innerJoin, boolean includeSubclasses);
+ /**
+ * Get the from clause part of any joins
+ * (optional operation)
+ */
+ public String fromJoinFragment(String alias, boolean innerJoin, boolean includeSubclasses);
+ /**
+ * The columns to join on
+ */
+ public String[] getKeyColumnNames();
+ /**
+ * Get the where clause filter, given a query alias and considering enabled session filters
+ */
+ public String filterFragment(String alias, Map enabledFilters) throws MappingException;
+
+ public String oneToManyFilterFragment(String alias) throws MappingException;
+ /**
+ * Is this instance actually a CollectionPersister?
+ */
+ public boolean isCollection();
+
+ /**
+ * Very, very, very ugly...
+ *
+ * @return Does this persister "consume" entity column aliases in the result
+ * set?
+ */
+ public boolean consumesEntityAlias();
+
+ /**
+ * Very, very, very ugly...
+ *
+ * @return Does this persister "consume" collection column aliases in the result
+ * set?
+ */
+ public boolean consumesCollectionAlias();
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,586 @@
+//$Id: JoinedSubclassEntityPersister.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.persister.entity;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.hibernate.AssertionFailure;
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.QueryException;
+import org.hibernate.cache.access.EntityRegionAccessStrategy;
+import org.hibernate.engine.Mapping;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.Versioning;
+import org.hibernate.engine.ExecuteUpdateResultCheckStyle;
+import org.hibernate.mapping.Column;
+import org.hibernate.mapping.KeyValue;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Property;
+import org.hibernate.mapping.Selectable;
+import org.hibernate.mapping.Subclass;
+import org.hibernate.mapping.Table;
+import org.hibernate.sql.CaseFragment;
+import org.hibernate.sql.SelectFragment;
+import org.hibernate.type.Type;
+import org.hibernate.util.ArrayHelper;
+
+/**
+ * An <tt>EntityPersister</tt> implementing the normalized "table-per-subclass"
+ * mapping strategy
+ *
+ * @author Gavin King
+ */
+public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
+
+ // the class hierarchy structure
+ private final int tableSpan;
+ private final String[] tableNames;
+ private final String[] naturalOrderTableNames;
+ private final String[][] tableKeyColumns;
+ private final String[][] naturalOrderTableKeyColumns;
+ private final boolean[] naturalOrderCascadeDeleteEnabled;
+
+ private final String[] spaces;
+
+ private final String[] subclassClosure;
+
+ private final String[] subclassTableNameClosure;
+ private final String[][] subclassTableKeyColumnClosure;
+ private final boolean[] isClassOrSuperclassTable;
+
+ // properties of this class, including inherited properties
+ private final int[] naturalOrderPropertyTableNumbers;
+ private final int[] propertyTableNumbers;
+
+ // the closure of all properties in the entire hierarchy including
+ // subclasses and superclasses of this class
+ private final int[] subclassPropertyTableNumberClosure;
+
+ // the closure of all columns used by the entire hierarchy including
+ // subclasses and superclasses of this class
+ private final int[] subclassColumnTableNumberClosure;
+ private final int[] subclassFormulaTableNumberClosure;
+
+ // subclass discrimination works by assigning particular
+ // values to certain combinations of null primary key
+ // values in the outer join using an SQL CASE
+ private final Map subclassesByDiscriminatorValue = new HashMap();
+ private final String[] discriminatorValues;
+ private final String[] notNullColumnNames;
+ private final int[] notNullColumnTableNumbers;
+
+ private final String[] constraintOrderedTableNames;
+ private final String[][] constraintOrderedKeyColumnNames;
+
+ private final String discriminatorSQLString;
+
+ //INITIALIZATION:
+
+ public JoinedSubclassEntityPersister(
+ final PersistentClass persistentClass,
+ final EntityRegionAccessStrategy cacheAccessStrategy,
+ final SessionFactoryImplementor factory,
+ final Mapping mapping) throws HibernateException {
+
+ super( persistentClass, cacheAccessStrategy, factory );
+
+ // DISCRIMINATOR
+
+ final Object discriminatorValue;
+ if ( persistentClass.isPolymorphic() ) {
+ try {
+ discriminatorValue = new Integer( persistentClass.getSubclassId() );
+ discriminatorSQLString = discriminatorValue.toString();
+ }
+ catch (Exception e) {
+ throw new MappingException("Could not format discriminator value to SQL string", e );
+ }
+ }
+ else {
+ discriminatorValue = null;
+ discriminatorSQLString = null;
+ }
+
+ if ( optimisticLockMode() > Versioning.OPTIMISTIC_LOCK_VERSION ) {
+ throw new MappingException( "optimistic-lock=all|dirty not supported for joined-subclass mappings [" + getEntityName() + "]" );
+ }
+
+ //MULTITABLES
+
+ final int idColumnSpan = getIdentifierColumnSpan();
+
+ ArrayList tables = new ArrayList();
+ ArrayList keyColumns = new ArrayList();
+ ArrayList cascadeDeletes = new ArrayList();
+ Iterator titer = persistentClass.getTableClosureIterator();
+ Iterator kiter = persistentClass.getKeyClosureIterator();
+ while ( titer.hasNext() ) {
+ Table tab = (Table) titer.next();
+ KeyValue key = (KeyValue) kiter.next();
+ String tabname = tab.getQualifiedName(
+ factory.getDialect(),
+ factory.getSettings().getDefaultCatalogName(),
+ factory.getSettings().getDefaultSchemaName()
+ );
+ tables.add(tabname);
+ String[] keyCols = new String[idColumnSpan];
+ Iterator citer = key.getColumnIterator();
+ for ( int k=0; k<idColumnSpan; k++ ) {
+ keyCols[k] = ( (Column) citer.next() ).getQuotedName( factory.getDialect() );
+ }
+ keyColumns.add(keyCols);
+ cascadeDeletes.add( new Boolean( key.isCascadeDeleteEnabled() && factory.getDialect().supportsCascadeDelete() ) );
+ }
+ naturalOrderTableNames = ArrayHelper.toStringArray(tables);
+ naturalOrderTableKeyColumns = ArrayHelper.to2DStringArray(keyColumns);
+ naturalOrderCascadeDeleteEnabled = ArrayHelper.toBooleanArray(cascadeDeletes);
+
+ ArrayList subtables = new ArrayList();
+ ArrayList isConcretes = new ArrayList();
+ keyColumns = new ArrayList();
+ titer = persistentClass.getSubclassTableClosureIterator();
+ while ( titer.hasNext() ) {
+ Table tab = (Table) titer.next();
+ isConcretes.add( new Boolean( persistentClass.isClassOrSuperclassTable(tab) ) );
+ String tabname = tab.getQualifiedName(
+ factory.getDialect(),
+ factory.getSettings().getDefaultCatalogName(),
+ factory.getSettings().getDefaultSchemaName()
+ );
+ subtables.add(tabname);
+ String[] key = new String[idColumnSpan];
+ Iterator citer = tab.getPrimaryKey().getColumnIterator();
+ for ( int k=0; k<idColumnSpan; k++ ) {
+ key[k] = ( (Column) citer.next() ).getQuotedName( factory.getDialect() );
+ }
+ keyColumns.add(key);
+ }
+ subclassTableNameClosure = ArrayHelper.toStringArray(subtables);
+ subclassTableKeyColumnClosure = ArrayHelper.to2DStringArray(keyColumns);
+ isClassOrSuperclassTable = ArrayHelper.toBooleanArray(isConcretes);
+
+ constraintOrderedTableNames = new String[subclassTableNameClosure.length];
+ constraintOrderedKeyColumnNames = new String[subclassTableNameClosure.length][];
+ int currentPosition = 0;
+ for ( int i = subclassTableNameClosure.length - 1; i >= 0 ; i--, currentPosition++ ) {
+ constraintOrderedTableNames[currentPosition] = subclassTableNameClosure[i];
+ constraintOrderedKeyColumnNames[currentPosition] = subclassTableKeyColumnClosure[i];
+ }
+
+ tableSpan = naturalOrderTableNames.length;
+ tableNames = reverse(naturalOrderTableNames);
+ tableKeyColumns = reverse(naturalOrderTableKeyColumns);
+ reverse(subclassTableNameClosure, tableSpan);
+ reverse(subclassTableKeyColumnClosure, tableSpan);
+
+ spaces = ArrayHelper.join(
+ tableNames,
+ ArrayHelper.toStringArray( persistentClass.getSynchronizedTables() )
+ );
+
+ // Custom sql
+ customSQLInsert = new String[tableSpan];
+ customSQLUpdate = new String[tableSpan];
+ customSQLDelete = new String[tableSpan];
+ insertCallable = new boolean[tableSpan];
+ updateCallable = new boolean[tableSpan];
+ deleteCallable = new boolean[tableSpan];
+ insertResultCheckStyles = new ExecuteUpdateResultCheckStyle[tableSpan];
+ updateResultCheckStyles = new ExecuteUpdateResultCheckStyle[tableSpan];
+ deleteResultCheckStyles = new ExecuteUpdateResultCheckStyle[tableSpan];
+
+ PersistentClass pc = persistentClass;
+ int jk = tableSpan-1;
+ while (pc!=null) {
+ customSQLInsert[jk] = pc.getCustomSQLInsert();
+ insertCallable[jk] = customSQLInsert[jk] != null && pc.isCustomInsertCallable();
+ insertResultCheckStyles[jk] = pc.getCustomSQLInsertCheckStyle() == null
+ ? ExecuteUpdateResultCheckStyle.determineDefault( customSQLInsert[jk], insertCallable[jk] )
+ : pc.getCustomSQLInsertCheckStyle();
+ customSQLUpdate[jk] = pc.getCustomSQLUpdate();
+ updateCallable[jk] = customSQLUpdate[jk] != null && pc.isCustomUpdateCallable();
+ updateResultCheckStyles[jk] = pc.getCustomSQLUpdateCheckStyle() == null
+ ? ExecuteUpdateResultCheckStyle.determineDefault( customSQLUpdate[jk], updateCallable[jk] )
+ : pc.getCustomSQLUpdateCheckStyle();
+ customSQLDelete[jk] = pc.getCustomSQLDelete();
+ deleteCallable[jk] = customSQLDelete[jk] != null && pc.isCustomDeleteCallable();
+ deleteResultCheckStyles[jk] = pc.getCustomSQLDeleteCheckStyle() == null
+ ? ExecuteUpdateResultCheckStyle.determineDefault( customSQLDelete[jk], deleteCallable[jk] )
+ : pc.getCustomSQLDeleteCheckStyle();
+ jk--;
+ pc = pc.getSuperclass();
+ }
+ if ( jk != -1 ) {
+ throw new AssertionFailure( "Tablespan does not match height of joined-subclass hiearchy." );
+ }
+
+ // PROPERTIES
+
+ int hydrateSpan = getPropertySpan();
+ naturalOrderPropertyTableNumbers = new int[hydrateSpan];
+ propertyTableNumbers = new int[hydrateSpan];
+ Iterator iter = persistentClass.getPropertyClosureIterator();
+ int i=0;
+ while( iter.hasNext() ) {
+ Property prop = (Property) iter.next();
+ String tabname = prop.getValue().getTable().getQualifiedName(
+ factory.getDialect(),
+ factory.getSettings().getDefaultCatalogName(),
+ factory.getSettings().getDefaultSchemaName()
+ );
+ propertyTableNumbers[i] = getTableId(tabname, tableNames);
+ naturalOrderPropertyTableNumbers[i] = getTableId(tabname, naturalOrderTableNames);
+ i++;
+ }
+
+ // subclass closure properties
+
+ //TODO: code duplication with SingleTableEntityPersister
+
+ ArrayList columnTableNumbers = new ArrayList();
+ ArrayList formulaTableNumbers = new ArrayList();
+ ArrayList propTableNumbers = new ArrayList();
+
+ iter = persistentClass.getSubclassPropertyClosureIterator();
+ while ( iter.hasNext() ) {
+ Property prop = (Property) iter.next();
+ Table tab = prop.getValue().getTable();
+ String tabname = tab.getQualifiedName(
+ factory.getDialect(),
+ factory.getSettings().getDefaultCatalogName(),
+ factory.getSettings().getDefaultSchemaName()
+ );
+ Integer tabnum = new Integer( getTableId(tabname, subclassTableNameClosure) );
+ propTableNumbers.add(tabnum);
+
+ Iterator citer = prop.getColumnIterator();
+ while ( citer.hasNext() ) {
+ Selectable thing = (Selectable) citer.next();
+ if ( thing.isFormula() ) {
+ formulaTableNumbers.add(tabnum);
+ }
+ else {
+ columnTableNumbers.add(tabnum);
+ }
+ }
+
+ }
+
+ subclassColumnTableNumberClosure = ArrayHelper.toIntArray(columnTableNumbers);
+ subclassPropertyTableNumberClosure = ArrayHelper.toIntArray(propTableNumbers);
+ subclassFormulaTableNumberClosure = ArrayHelper.toIntArray(formulaTableNumbers);
+
+ // SUBCLASSES
+
+ int subclassSpan = persistentClass.getSubclassSpan() + 1;
+ subclassClosure = new String[subclassSpan];
+ subclassClosure[subclassSpan-1] = getEntityName();
+ if ( persistentClass.isPolymorphic() ) {
+ subclassesByDiscriminatorValue.put( discriminatorValue, getEntityName() );
+ discriminatorValues = new String[subclassSpan];
+ discriminatorValues[subclassSpan-1] = discriminatorSQLString;
+ notNullColumnTableNumbers = new int[subclassSpan];
+ final int id = getTableId(
+ persistentClass.getTable().getQualifiedName(
+ factory.getDialect(),
+ factory.getSettings().getDefaultCatalogName(),
+ factory.getSettings().getDefaultSchemaName()
+ ),
+ subclassTableNameClosure
+ );
+ notNullColumnTableNumbers[subclassSpan-1] = id;
+ notNullColumnNames = new String[subclassSpan];
+ notNullColumnNames[subclassSpan-1] = subclassTableKeyColumnClosure[id][0]; //( (Column) model.getTable().getPrimaryKey().getColumnIterator().next() ).getName();
+ }
+ else {
+ discriminatorValues = null;
+ notNullColumnTableNumbers = null;
+ notNullColumnNames = null;
+ }
+
+ iter = persistentClass.getSubclassIterator();
+ int k=0;
+ while ( iter.hasNext() ) {
+ Subclass sc = (Subclass) iter.next();
+ subclassClosure[k] = sc.getEntityName();
+ try {
+ if ( persistentClass.isPolymorphic() ) {
+ // we now use subclass ids that are consistent across all
+ // persisters for a class hierarchy, so that the use of
+ // "foo.class = Bar" works in HQL
+ Integer subclassId = new Integer( sc.getSubclassId() );//new Integer(k+1);
+ subclassesByDiscriminatorValue.put( subclassId, sc.getEntityName() );
+ discriminatorValues[k] = subclassId.toString();
+ int id = getTableId(
+ sc.getTable().getQualifiedName(
+ factory.getDialect(),
+ factory.getSettings().getDefaultCatalogName(),
+ factory.getSettings().getDefaultSchemaName()
+ ),
+ subclassTableNameClosure
+ );
+ notNullColumnTableNumbers[k] = id;
+ notNullColumnNames[k] = subclassTableKeyColumnClosure[id][0]; //( (Column) sc.getTable().getPrimaryKey().getColumnIterator().next() ).getName();
+ }
+ }
+ catch (Exception e) {
+ throw new MappingException("Error parsing discriminator value", e );
+ }
+ k++;
+ }
+
+ initLockers();
+
+ initSubclassPropertyAliasesMap(persistentClass);
+
+ postConstruct(mapping);
+
+ }
+
+ /*public void postInstantiate() throws MappingException {
+ super.postInstantiate();
+ //TODO: other lock modes?
+ loader = createEntityLoader(LockMode.NONE, CollectionHelper.EMPTY_MAP);
+ }*/
+
+ public String getSubclassPropertyTableName(int i) {
+ return subclassTableNameClosure[ subclassPropertyTableNumberClosure[i] ];
+ }
+
+ public Type getDiscriminatorType() {
+ return Hibernate.INTEGER;
+ }
+
+ public String getDiscriminatorSQLValue() {
+ return discriminatorSQLString;
+ }
+
+
+ public String getSubclassForDiscriminatorValue(Object value) {
+ return (String) subclassesByDiscriminatorValue.get(value);
+ }
+
+ public Serializable[] getPropertySpaces() {
+ return spaces; // don't need subclass tables, because they can't appear in conditions
+ }
+
+
+ protected String getTableName(int j) {
+ return naturalOrderTableNames[j];
+ }
+
+ protected String[] getKeyColumns(int j) {
+ return naturalOrderTableKeyColumns[j];
+ }
+
+ protected boolean isTableCascadeDeleteEnabled(int j) {
+ return naturalOrderCascadeDeleteEnabled[j];
+ }
+
+ protected boolean isPropertyOfTable(int property, int j) {
+ return naturalOrderPropertyTableNumbers[property]==j;
+ }
+
+ /**
+ * Load an instance using either the <tt>forUpdateLoader</tt> or the outer joining <tt>loader</tt>,
+ * depending upon the value of the <tt>lock</tt> parameter
+ */
+ /*public Object load(Serializable id, Object optionalObject, LockMode lockMode, SessionImplementor session)
+ throws HibernateException {
+
+ if ( log.isTraceEnabled() ) log.trace( "Materializing entity: " + MessageHelper.infoString(this, id) );
+
+ final UniqueEntityLoader loader = hasQueryLoader() ?
+ getQueryLoader() :
+ this.loader;
+ try {
+
+ final Object result = loader.load(id, optionalObject, session);
+
+ if (result!=null) lock(id, getVersion(result), result, lockMode, session);
+
+ return result;
+
+ }
+ catch (SQLException sqle) {
+ throw new JDBCException( "could not load by id: " + MessageHelper.infoString(this, id), sqle );
+ }
+ }*/
+
+ private static final void reverse(Object[] objects, int len) {
+ Object[] temp = new Object[len];
+ for (int i=0; i<len; i++) {
+ temp[i] = objects[len-i-1];
+ }
+ for (int i=0; i<len; i++) {
+ objects[i] = temp[i];
+ }
+ }
+
+ private static final String[] reverse(String[] objects) {
+ int len = objects.length;
+ String[] temp = new String[len];
+ for (int i=0; i<len; i++) {
+ temp[i] = objects[len-i-1];
+ }
+ return temp;
+ }
+
+ private static final String[][] reverse(String[][] objects) {
+ int len = objects.length;
+ String[][] temp = new String[len][];
+ for (int i=0; i<len; i++) {
+ temp[i] = objects[len-i-1];
+ }
+ return temp;
+ }
+
+ public String fromTableFragment(String alias) {
+ return getTableName() + ' ' + alias;
+ }
+
+ public String getTableName() {
+ return tableNames[0];
+ }
+
+ private static int getTableId(String tableName, String[] tables) {
+ for ( int j=0; j<tables.length; j++ ) {
+ if ( tableName.equals( tables[j] ) ) {
+ return j;
+ }
+ }
+ throw new AssertionFailure("Table " + tableName + " not found");
+ }
+
+ public void addDiscriminatorToSelect(SelectFragment select, String name, String suffix) {
+ if ( hasSubclasses() ) {
+ select.setExtraSelectList( discriminatorFragment(name), getDiscriminatorAlias() );
+ }
+ }
+
+ private CaseFragment discriminatorFragment(String alias) {
+ CaseFragment cases = getFactory().getDialect().createCaseFragment();
+
+ for ( int i=0; i<discriminatorValues.length; i++ ) {
+ cases.addWhenColumnNotNull(
+ generateTableAlias( alias, notNullColumnTableNumbers[i] ),
+ notNullColumnNames[i],
+ discriminatorValues[i]
+ );
+ }
+
+ return cases;
+ }
+
+ public String filterFragment(String alias) {
+ return hasWhere() ?
+ " and " + getSQLWhereString( generateFilterConditionAlias( alias ) ) :
+ "";
+ }
+
+ public String generateFilterConditionAlias(String rootAlias) {
+ return generateTableAlias( rootAlias, tableSpan-1 );
+ }
+
+ public String[] getIdentifierColumnNames() {
+ return tableKeyColumns[0];
+ }
+
+ public String[] toColumns(String alias, String propertyName) throws QueryException {
+
+ if ( ENTITY_CLASS.equals(propertyName) ) {
+ // This doesn't actually seem to work but it *might*
+ // work on some dbs. Also it doesn't work if there
+ // are multiple columns of results because it
+ // is not accounting for the suffix:
+ // return new String[] { getDiscriminatorColumnName() };
+
+ return new String[] { discriminatorFragment(alias).toFragmentString() };
+ }
+ else {
+ return super.toColumns(alias, propertyName);
+ }
+
+ }
+
+ protected int[] getPropertyTableNumbersInSelect() {
+ return propertyTableNumbers;
+ }
+
+ protected int getSubclassPropertyTableNumber(int i) {
+ return subclassPropertyTableNumberClosure[i];
+ }
+
+ public int getTableSpan() {
+ return tableSpan;
+ }
+
+ public boolean isMultiTable() {
+ return true;
+ }
+
+ protected int[] getSubclassColumnTableNumberClosure() {
+ return subclassColumnTableNumberClosure;
+ }
+
+ protected int[] getSubclassFormulaTableNumberClosure() {
+ return subclassFormulaTableNumberClosure;
+ }
+
+ protected int[] getPropertyTableNumbers() {
+ return naturalOrderPropertyTableNumbers;
+ }
+
+ protected String[] getSubclassTableKeyColumns(int j) {
+ return subclassTableKeyColumnClosure[j];
+ }
+
+ public String getSubclassTableName(int j) {
+ return subclassTableNameClosure[j];
+ }
+
+ public int getSubclassTableSpan() {
+ return subclassTableNameClosure.length;
+ }
+
+ protected boolean isClassOrSuperclassTable(int j) {
+ return isClassOrSuperclassTable[j];
+ }
+
+ public String getPropertyTableName(String propertyName) {
+ Integer index = getEntityMetamodel().getPropertyIndexOrNull(propertyName);
+ if ( index == null ) {
+ return null;
+ }
+ return tableNames[ propertyTableNumbers[ index.intValue() ] ];
+ }
+
+ public String[] getConstraintOrderedTableNameClosure() {
+ return constraintOrderedTableNames;
+ }
+
+ public String[][] getContraintOrderedTableKeyColumnClosure() {
+ return constraintOrderedKeyColumnNames;
+ }
+
+ public String getRootTableName() {
+ return naturalOrderTableNames[0];
+ }
+
+ public String getRootTableAlias(String drivingAlias) {
+ return generateTableAlias( drivingAlias, getTableId( getRootTableName(), tableNames ) );
+ }
+
+ public Declarer getSubclassPropertyDeclarer(String propertyPath) {
+ if ( "class".equals( propertyPath ) ) {
+ // special case where we need to force incloude all subclass joins
+ return Declarer.SUBCLASS;
+ }
+ return super.getSubclassPropertyDeclarer( propertyPath );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/Loadable.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/Loadable.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/Loadable.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,88 @@
+//$Id: Loadable.java 7458 2005-07-12 20:12:57Z oneovthafew $
+package org.hibernate.persister.entity;
+
+import java.io.Serializable;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.type.Type;
+
+/**
+ * Implemented by a <tt>EntityPersister</tt> that may be loaded
+ * using <tt>Loader</tt>.
+ *
+ * @see org.hibernate.loader.Loader
+ * @author Gavin King
+ */
+public interface Loadable extends EntityPersister {
+
+ public static final String ROWID_ALIAS = "rowid_";
+
+ /**
+ * Does this persistent class have subclasses?
+ */
+ public boolean hasSubclasses();
+
+ /**
+ * Get the discriminator type
+ */
+ public Type getDiscriminatorType();
+
+ /**
+ * Get the concrete subclass corresponding to the given discriminator
+ * value
+ */
+ public String getSubclassForDiscriminatorValue(Object value);
+
+ /**
+ * Get the names of columns used to persist the identifier
+ */
+ public String[] getIdentifierColumnNames();
+
+ /**
+ * Get the result set aliases used for the identifier columns, given a suffix
+ */
+ public String[] getIdentifierAliases(String suffix);
+ /**
+ * Get the result set aliases used for the property columns, given a suffix (properties of this class, only).
+ */
+ public String[] getPropertyAliases(String suffix, int i);
+
+ /**
+ * Get the result set column names mapped for this property (properties of this class, only).
+ */
+ public String[] getPropertyColumnNames(int i);
+
+ /**
+ * Get the result set aliases used for the identifier columns, given a suffix
+ */
+ public String getDiscriminatorAlias(String suffix);
+
+ /**
+ * @return the column name for the discriminator as specified in the mapping.
+ */
+ public String getDiscriminatorColumnName();
+
+ /**
+ * Does the result set contain rowids?
+ */
+ public boolean hasRowId();
+
+ /**
+ * Retrieve property values from one row of a result set
+ */
+ public Object[] hydrate(
+ ResultSet rs,
+ Serializable id,
+ Object object,
+ Loadable rootLoadable,
+ String[][] suffixedPropertyColumns,
+ boolean allProperties,
+ SessionImplementor session)
+ throws SQLException, HibernateException;
+
+ public boolean isAbstract();
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/Lockable.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/Lockable.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/Lockable.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,45 @@
+package org.hibernate.persister.entity;
+
+/**
+ * Contract for things that can be locked via a {@link org.hibernate.dialect.lock.LockingStrategy}.
+ * <p/>
+ * Currently only the root table gets locked, except for the case of HQL and Criteria queries
+ * against dialects which do not support either (1) FOR UPDATE OF or (2) support hint locking
+ * (in which case *all* queried tables would be locked).
+ *
+ * @author Steve Ebersole
+ * @since 3.2
+ */
+public interface Lockable extends EntityPersister {
+ /**
+ * Locks are always applied to the "root table".
+ *
+ * @return The root table name
+ */
+ public String getRootTableName();
+
+ /**
+ * Get the SQL alias this persister would use for the root table
+ * given the passed driving alias.
+ *
+ * @param drivingAlias The driving alias; or the alias for the table
+ * mapped by this persister in the hierarchy.
+ * @return The root table alias.
+ */
+ public String getRootTableAlias(String drivingAlias);
+
+ /**
+ * Get the names of columns on the root table used to persist the identifier.
+ *
+ * @return The root table identifier column names.
+ */
+ public String[] getRootTableIdentifierColumnNames();
+
+ /**
+ * For versioned entities, get the name of the column (again, expected on the
+ * root table) used to store the version values.
+ *
+ * @return The version column name.
+ */
+ public String getVersionColumnName();
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/NamedQueryLoader.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/NamedQueryLoader.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/NamedQueryLoader.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,66 @@
+//$Id: NamedQueryLoader.java 10019 2006-06-15 07:50:12Z steve.ebersole at jboss.com $
+package org.hibernate.persister.entity;
+
+import java.io.Serializable;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.FlushMode;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.EntityKey;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.impl.AbstractQueryImpl;
+import org.hibernate.loader.entity.UniqueEntityLoader;
+
+/**
+ * Not really a <tt>Loader</tt>, just a wrapper around a
+ * named query.
+ * @author Gavin King
+ */
+public final class NamedQueryLoader implements UniqueEntityLoader {
+ private final String queryName;
+ private final EntityPersister persister;
+
+ private static final Log log = LogFactory.getLog(NamedQueryLoader.class);
+
+ public NamedQueryLoader(String queryName, EntityPersister persister) {
+ super();
+ this.queryName = queryName;
+ this.persister = persister;
+ }
+
+ public Object load(Serializable id, Object optionalObject, SessionImplementor session)
+ throws HibernateException {
+
+ if ( log.isDebugEnabled() ) {
+ log.debug(
+ "loading entity: " + persister.getEntityName() +
+ " using named query: " + queryName
+ );
+ }
+
+ AbstractQueryImpl query = (AbstractQueryImpl) session.getNamedQuery(queryName);
+ if ( query.hasNamedParameters() ) {
+ query.setParameter(
+ query.getNamedParameters()[0],
+ id,
+ persister.getIdentifierType()
+ );
+ }
+ else {
+ query.setParameter( 0, id, persister.getIdentifierType() );
+ }
+ query.setOptionalId(id);
+ query.setOptionalEntityName( persister.getEntityName() );
+ query.setOptionalObject(optionalObject);
+ query.setFlushMode( FlushMode.MANUAL );
+ query.list();
+
+ // now look up the object we are really interested in!
+ // (this lets us correctly handle proxies and multi-row
+ // or multi-column queries)
+ return session.getPersistenceContext()
+ .getEntity( new EntityKey( id, persister, session.getEntityMode() ) );
+
+ }
+}
\ No newline at end of file
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/NamedQueryLoader.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/OuterJoinLoadable.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/OuterJoinLoadable.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/OuterJoinLoadable.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,90 @@
+//$Id: OuterJoinLoadable.java 7652 2005-07-26 05:51:47Z oneovthafew $
+package org.hibernate.persister.entity;
+
+import org.hibernate.FetchMode;
+import org.hibernate.engine.CascadeStyle;
+import org.hibernate.type.EntityType;
+import org.hibernate.type.Type;
+
+/**
+ * A <tt>EntityPersister</tt> that may be loaded by outer join using
+ * the <tt>OuterJoinLoader</tt> hierarchy and may be an element
+ * of a one-to-many association.
+ *
+ * @see org.hibernate.loader.OuterJoinLoader
+ * @author Gavin King
+ */
+public interface OuterJoinLoadable extends Loadable, Joinable {
+
+ /**
+ * Generate a list of collection index, key and element columns
+ */
+ public String selectFragment(String alias, String suffix);
+ /**
+ * How many properties are there, for this class and all subclasses?
+ */
+ public int countSubclassProperties();
+
+ /**
+ * May this (subclass closure) property be fetched using an SQL outerjoin?
+ */
+ public FetchMode getFetchMode(int i);
+ /**
+ * Get the cascade style of this (subclass closure) property
+ */
+ public CascadeStyle getCascadeStyle(int i);
+
+ /**
+ * Is this property defined on a subclass of the mapped class.
+ */
+ public boolean isDefinedOnSubclass(int i);
+
+ /**
+ * Get the type of the numbered property of the class or a subclass.
+ */
+ public Type getSubclassPropertyType(int i);
+
+ /**
+ * Get the name of the numbered property of the class or a subclass.
+ */
+ public String getSubclassPropertyName(int i);
+
+ /**
+ * Is the numbered property of the class of subclass nullable?
+ */
+ public boolean isSubclassPropertyNullable(int i);
+
+ /**
+ * Return the column names used to persist the numbered property of the
+ * class or a subclass.
+ */
+ public String[] getSubclassPropertyColumnNames(int i);
+
+ /**
+ * Return the table name used to persist the numbered property of the
+ * class or a subclass.
+ */
+ public String getSubclassPropertyTableName(int i);
+ /**
+ * Given the number of a property of a subclass, and a table alias,
+ * return the aliased column names.
+ */
+ public String[] toColumns(String name, int i);
+
+ /**
+ * Get the main from table fragment, given a query alias.
+ */
+ public String fromTableFragment(String alias);
+
+ /**
+ * Get the column names for the given property path
+ */
+ public String[] getPropertyColumnNames(String propertyPath);
+ /**
+ * Get the table name for the given property path
+ */
+ public String getPropertyTableName(String propertyName);
+
+ public EntityType getEntityType();
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/PropertyMapping.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/PropertyMapping.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/PropertyMapping.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,32 @@
+//$Id: PropertyMapping.java 6179 2005-03-23 15:41:48Z steveebersole $
+package org.hibernate.persister.entity;
+
+import org.hibernate.QueryException;
+import org.hibernate.type.Type;
+
+/**
+ * Abstraction of all mappings that define properties:
+ * entities, collection elements.
+ *
+ * @author Gavin King
+ */
+public interface PropertyMapping {
+ // TODO: It would be really, really nice to use this to also model components!
+ /**
+ * Given a component path expression, get the type of the property
+ */
+ public Type toType(String propertyName) throws QueryException;
+ /**
+ * Given a query alias and a property path, return the qualified
+ * column name
+ */
+ public String[] toColumns(String alias, String propertyName) throws QueryException;
+ /**
+ * Given a property path, return the corresponding column name(s).
+ */
+ public String[] toColumns(String propertyName) throws QueryException, UnsupportedOperationException;
+ /**
+ * Get the type of the thing containing the properties
+ */
+ public Type getType();
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/Queryable.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/Queryable.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/Queryable.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,154 @@
+//$Id: Queryable.java 10824 2006-11-16 19:32:48Z steve.ebersole at jboss.com $
+package org.hibernate.persister.entity;
+
+/**
+ * Extends the generic <tt>EntityPersister</tt> contract to add
+ * operations required by the Hibernate Query Language
+ *
+ * @author Gavin King
+ */
+public interface Queryable extends Loadable, PropertyMapping, Joinable {
+
+ /**
+ * Is this an abstract class?
+ */
+ public boolean isAbstract();
+ /**
+ * Is this class explicit polymorphism only?
+ */
+ public boolean isExplicitPolymorphism();
+ /**
+ * Get the class that this class is mapped as a subclass of -
+ * not necessarily the direct superclass
+ */
+ public String getMappedSuperclass();
+ /**
+ * Get the discriminator value for this particular concrete subclass,
+ * as a string that may be embedded in a select statement
+ */
+ public String getDiscriminatorSQLValue();
+
+ /**
+ * Given a query alias and an identifying suffix, render the intentifier select fragment.
+ */
+ public String identifierSelectFragment(String name, String suffix);
+ /**
+ * Given a query alias and an identifying suffix, render the property select fragment.
+ */
+ public String propertySelectFragment(String alias, String suffix, boolean allProperties);
+
+ /**
+ * Get the names of columns used to persist the identifier
+ */
+ public String[] getIdentifierColumnNames();
+
+ /**
+ * Is the inheritence hierarchy described by this persister contained across
+ * multiple tables?
+ *
+ * @return True if the inheritence hierarchy is spread across multiple tables; false otherwise.
+ */
+ public boolean isMultiTable();
+
+ /**
+ * Get the names of all tables used in the hierarchy (up and down) ordered such
+ * that deletes in the given order would not cause contraint violations.
+ *
+ * @return The ordered array of table names.
+ */
+ public String[] getConstraintOrderedTableNameClosure();
+
+ /**
+ * For each table specified in {@link #getConstraintOrderedTableNameClosure()}, get
+ * the columns that define the key between the various hierarchy classes.
+ * <p/>
+ * The first dimension here corresponds to the table indexes returned in
+ * {@link #getConstraintOrderedTableNameClosure()}.
+ * <p/>
+ * The second dimension should have the same length across all the elements in
+ * the first dimension. If not, that'd be a problem ;)
+ *
+ * @return
+ */
+ public String[][] getContraintOrderedTableKeyColumnClosure();
+
+ /**
+ * Get the name of the temporary table to be used to (potentially) store id values
+ * when performing bulk update/deletes.
+ *
+ * @return The appropriate temporary table name.
+ */
+ public String getTemporaryIdTableName();
+
+ /**
+ * Get the appropriate DDL command for generating the temporary table to
+ * be used to (potentially) store id values when performing bulk update/deletes.
+ *
+ * @return The appropriate temporary table creation command.
+ */
+ public String getTemporaryIdTableDDL();
+
+ /**
+ * Given a property name, determine the number of the table which contains the column
+ * to which this property is mapped.
+ * <p/>
+ * Note that this is <b>not</b> relative to the results from {@link #getConstraintOrderedTableNameClosure()}.
+ * It is relative to the subclass table name closure maintained internal to the persister (yick!).
+ * It is also relative to the indexing used to resolve {@link #getSubclassTableName}...
+ *
+ * @param propertyPath The name of the property.
+ * @return The nunber of the table to which the property is mapped.
+ */
+ public int getSubclassPropertyTableNumber(String propertyPath);
+
+ /**
+ * Determine whether the given property is declared by our
+ * mapped class, our super class, or one of our subclasses...
+ * <p/>
+ * Note: the method is called 'subclass property...' simply
+ * for consistency sake (e.g. {@link #getSubclassPropertyTableNumber}
+ *
+ * @param propertyPath The property name.
+ * @return The property declarer
+ */
+ public Declarer getSubclassPropertyDeclarer(String propertyPath);
+
+ /**
+ * Get the name of the table with the given index from the internal
+ * array.
+ *
+ * @param number The index into the internal array.
+ * @return
+ */
+ public String getSubclassTableName(int number);
+
+ /**
+ * Is the version property included in insert statements?
+ */
+ public boolean isVersionPropertyInsertable();
+
+ /**
+ * The alias used for any filter conditions (mapped where-fragments or
+ * enabled-filters).
+ * </p>
+ * This may or may not be different from the root alias depending upon the
+ * inheritence mapping strategy.
+ *
+ * @param rootAlias The root alias
+ * @return The alias used for "filter conditions" within the where clause.
+ */
+ public String generateFilterConditionAlias(String rootAlias);
+
+ public static class Declarer {
+ public static final Declarer CLASS = new Declarer( "class" );
+ public static final Declarer SUBCLASS = new Declarer( "subclass" );
+ public static final Declarer SUPERCLASS = new Declarer( "superclass" );
+ private final String name;
+ public Declarer(String name) {
+ this.name = name;
+ }
+ public String toString() {
+ return name;
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/SQLLoadable.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/SQLLoadable.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/SQLLoadable.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,36 @@
+//$Id: SQLLoadable.java 7363 2005-07-03 19:42:43Z maxcsaucdk $
+package org.hibernate.persister.entity;
+
+import org.hibernate.type.Type;
+
+/**
+ * A class persister that supports queries expressed in the
+ * platform native SQL dialect
+ *
+ * @author Gavin King, Max Andersen
+ */
+public interface SQLLoadable extends Loadable {
+
+ /**
+ * Return the column alias names used to persist/query the named property of the class or a subclass (optional operation).
+ */
+ public String[] getSubclassPropertyColumnAliases(String propertyName, String suffix);
+
+ /**
+ * Return the column names used to persist/query the named property of the class or a subclass (optional operation).
+ */
+ public String[] getSubclassPropertyColumnNames(String propertyName);
+
+ /**
+ * All columns to select, when loading.
+ */
+ public String selectFragment(String alias, String suffix);
+
+ /**
+ * Get the type
+ */
+ public Type getType();
+
+
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,713 @@
+//$Id: SingleTableEntityPersister.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.persister.entity;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.cache.access.EntityRegionAccessStrategy;
+import org.hibernate.engine.Mapping;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.ExecuteUpdateResultCheckStyle;
+import org.hibernate.mapping.Column;
+import org.hibernate.mapping.Formula;
+import org.hibernate.mapping.Join;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Property;
+import org.hibernate.mapping.Selectable;
+import org.hibernate.mapping.Subclass;
+import org.hibernate.mapping.Table;
+import org.hibernate.mapping.Value;
+import org.hibernate.sql.InFragment;
+import org.hibernate.sql.Insert;
+import org.hibernate.sql.SelectFragment;
+import org.hibernate.type.AssociationType;
+import org.hibernate.type.DiscriminatorType;
+import org.hibernate.type.Type;
+import org.hibernate.util.ArrayHelper;
+import org.hibernate.util.MarkerObject;
+
+/**
+ * The default implementation of the <tt>EntityPersister</tt> interface.
+ * Implements the "table-per-class-hierarchy" or "roll-up" mapping strategy
+ * for an entity class and its inheritence hierarchy. This is implemented
+ * as a single table holding all classes in the hierarchy with a discrimator
+ * column used to determine which concrete class is referenced.
+ *
+ * @author Gavin King
+ */
+public class SingleTableEntityPersister extends AbstractEntityPersister {
+
+ // the class hierarchy structure
+ private final int joinSpan;
+ private final String[] qualifiedTableNames;
+ private final boolean[] isInverseTable;
+ private final boolean[] isNullableTable;
+ private final String[][] keyColumnNames;
+ private final boolean[] cascadeDeleteEnabled;
+ private final boolean hasSequentialSelects;
+
+ private final String[] spaces;
+
+ private final String[] subclassClosure;
+
+ private final String[] subclassTableNameClosure;
+ private final boolean[] subclassTableIsLazyClosure;
+ private final boolean[] isInverseSubclassTable;
+ private final boolean[] isNullableSubclassTable;
+ private final boolean[] subclassTableSequentialSelect;
+ private final String[][] subclassTableKeyColumnClosure;
+ private final boolean[] isClassOrSuperclassTable;
+
+ // properties of this class, including inherited properties
+ private final int[] propertyTableNumbers;
+
+ // the closure of all columns used by the entire hierarchy including
+ // subclasses and superclasses of this class
+ private final int[] subclassPropertyTableNumberClosure;
+
+ private final int[] subclassColumnTableNumberClosure;
+ private final int[] subclassFormulaTableNumberClosure;
+
+ // discriminator column
+ private final Map subclassesByDiscriminatorValue = new HashMap();
+ private final boolean forceDiscriminator;
+ private final String discriminatorColumnName;
+ private final String discriminatorFormula;
+ private final String discriminatorFormulaTemplate;
+ private final String discriminatorAlias;
+ private final Type discriminatorType;
+ private final String discriminatorSQLValue;
+ private final boolean discriminatorInsertable;
+
+ private final String[] constraintOrderedTableNames;
+ private final String[][] constraintOrderedKeyColumnNames;
+
+ //private final Map propertyTableNumbersByName = new HashMap();
+ private final Map propertyTableNumbersByNameAndSubclass = new HashMap();
+
+ private final Map sequentialSelectStringsByEntityName = new HashMap();
+
+ private static final Object NULL_DISCRIMINATOR = new MarkerObject("<null discriminator>");
+ private static final Object NOT_NULL_DISCRIMINATOR = new MarkerObject("<not null discriminator>");
+
+ //INITIALIZATION:
+
+ public SingleTableEntityPersister(
+ final PersistentClass persistentClass,
+ final EntityRegionAccessStrategy cacheAccessStrategy,
+ final SessionFactoryImplementor factory,
+ final Mapping mapping) throws HibernateException {
+
+ super( persistentClass, cacheAccessStrategy, factory );
+
+ // CLASS + TABLE
+
+ joinSpan = persistentClass.getJoinClosureSpan()+1;
+ qualifiedTableNames = new String[joinSpan];
+ isInverseTable = new boolean[joinSpan];
+ isNullableTable = new boolean[joinSpan];
+ keyColumnNames = new String[joinSpan][];
+ final Table table = persistentClass.getRootTable();
+ qualifiedTableNames[0] = table.getQualifiedName(
+ factory.getDialect(),
+ factory.getSettings().getDefaultCatalogName(),
+ factory.getSettings().getDefaultSchemaName()
+ );
+ isInverseTable[0] = false;
+ isNullableTable[0] = false;
+ keyColumnNames[0] = getIdentifierColumnNames();
+ cascadeDeleteEnabled = new boolean[joinSpan];
+
+ // Custom sql
+ customSQLInsert = new String[joinSpan];
+ customSQLUpdate = new String[joinSpan];
+ customSQLDelete = new String[joinSpan];
+ insertCallable = new boolean[joinSpan];
+ updateCallable = new boolean[joinSpan];
+ deleteCallable = new boolean[joinSpan];
+ insertResultCheckStyles = new ExecuteUpdateResultCheckStyle[joinSpan];
+ updateResultCheckStyles = new ExecuteUpdateResultCheckStyle[joinSpan];
+ deleteResultCheckStyles = new ExecuteUpdateResultCheckStyle[joinSpan];
+
+ customSQLInsert[0] = persistentClass.getCustomSQLInsert();
+ insertCallable[0] = customSQLInsert[0] != null && persistentClass.isCustomInsertCallable();
+ insertResultCheckStyles[0] = persistentClass.getCustomSQLInsertCheckStyle() == null
+ ? ExecuteUpdateResultCheckStyle.determineDefault( customSQLInsert[0], insertCallable[0] )
+ : persistentClass.getCustomSQLInsertCheckStyle();
+ customSQLUpdate[0] = persistentClass.getCustomSQLUpdate();
+ updateCallable[0] = customSQLUpdate[0] != null && persistentClass.isCustomUpdateCallable();
+ updateResultCheckStyles[0] = persistentClass.getCustomSQLUpdateCheckStyle() == null
+ ? ExecuteUpdateResultCheckStyle.determineDefault( customSQLUpdate[0], updateCallable[0] )
+ : persistentClass.getCustomSQLUpdateCheckStyle();
+ customSQLDelete[0] = persistentClass.getCustomSQLDelete();
+ deleteCallable[0] = customSQLDelete[0] != null && persistentClass.isCustomDeleteCallable();
+ deleteResultCheckStyles[0] = persistentClass.getCustomSQLDeleteCheckStyle() == null
+ ? ExecuteUpdateResultCheckStyle.determineDefault( customSQLDelete[0], deleteCallable[0] )
+ : persistentClass.getCustomSQLDeleteCheckStyle();
+
+ // JOINS
+
+ Iterator joinIter = persistentClass.getJoinClosureIterator();
+ int j = 1;
+ while ( joinIter.hasNext() ) {
+ Join join = (Join) joinIter.next();
+ qualifiedTableNames[j] = join.getTable().getQualifiedName(
+ factory.getDialect(),
+ factory.getSettings().getDefaultCatalogName(),
+ factory.getSettings().getDefaultSchemaName()
+ );
+ isInverseTable[j] = join.isInverse();
+ isNullableTable[j] = join.isOptional();
+ cascadeDeleteEnabled[j] = join.getKey().isCascadeDeleteEnabled() &&
+ factory.getDialect().supportsCascadeDelete();
+
+ customSQLInsert[j] = join.getCustomSQLInsert();
+ insertCallable[j] = customSQLInsert[j] != null && join.isCustomInsertCallable();
+ insertResultCheckStyles[j] = join.getCustomSQLInsertCheckStyle() == null
+ ? ExecuteUpdateResultCheckStyle.determineDefault( customSQLInsert[j], insertCallable[j] )
+ : join.getCustomSQLInsertCheckStyle();
+ customSQLUpdate[j] = join.getCustomSQLUpdate();
+ updateCallable[j] = customSQLUpdate[j] != null && join.isCustomUpdateCallable();
+ updateResultCheckStyles[j] = join.getCustomSQLUpdateCheckStyle() == null
+ ? ExecuteUpdateResultCheckStyle.determineDefault( customSQLUpdate[j], updateCallable[j] )
+ : join.getCustomSQLUpdateCheckStyle();
+ customSQLDelete[j] = join.getCustomSQLDelete();
+ deleteCallable[j] = customSQLDelete[j] != null && join.isCustomDeleteCallable();
+ deleteResultCheckStyles[j] = join.getCustomSQLDeleteCheckStyle() == null
+ ? ExecuteUpdateResultCheckStyle.determineDefault( customSQLDelete[j], deleteCallable[j] )
+ : join.getCustomSQLDeleteCheckStyle();
+
+ Iterator iter = join.getKey().getColumnIterator();
+ keyColumnNames[j] = new String[ join.getKey().getColumnSpan() ];
+ int i = 0;
+ while ( iter.hasNext() ) {
+ Column col = (Column) iter.next();
+ keyColumnNames[j][i++] = col.getQuotedName( factory.getDialect() );
+ }
+
+ j++;
+ }
+
+ constraintOrderedTableNames = new String[qualifiedTableNames.length];
+ constraintOrderedKeyColumnNames = new String[qualifiedTableNames.length][];
+ for ( int i = qualifiedTableNames.length - 1, position = 0; i >= 0; i--, position++ ) {
+ constraintOrderedTableNames[position] = qualifiedTableNames[i];
+ constraintOrderedKeyColumnNames[position] = keyColumnNames[i];
+ }
+
+ spaces = ArrayHelper.join(
+ qualifiedTableNames,
+ ArrayHelper.toStringArray( persistentClass.getSynchronizedTables() )
+ );
+
+ final boolean lazyAvailable = isInstrumented(EntityMode.POJO);
+
+ boolean hasDeferred = false;
+ ArrayList subclassTables = new ArrayList();
+ ArrayList joinKeyColumns = new ArrayList();
+ ArrayList isConcretes = new ArrayList();
+ ArrayList isDeferreds = new ArrayList();
+ ArrayList isInverses = new ArrayList();
+ ArrayList isNullables = new ArrayList();
+ ArrayList isLazies = new ArrayList();
+ subclassTables.add( qualifiedTableNames[0] );
+ joinKeyColumns.add( getIdentifierColumnNames() );
+ isConcretes.add(Boolean.TRUE);
+ isDeferreds.add(Boolean.FALSE);
+ isInverses.add(Boolean.FALSE);
+ isNullables.add(Boolean.FALSE);
+ isLazies.add(Boolean.FALSE);
+ joinIter = persistentClass.getSubclassJoinClosureIterator();
+ while ( joinIter.hasNext() ) {
+ Join join = (Join) joinIter.next();
+ isConcretes.add( new Boolean( persistentClass.isClassOrSuperclassJoin(join) ) );
+ isDeferreds.add( new Boolean( join.isSequentialSelect() ) );
+ isInverses.add( new Boolean( join.isInverse() ) );
+ isNullables.add( new Boolean( join.isOptional() ) );
+ isLazies.add( new Boolean( lazyAvailable && join.isLazy() ) );
+ if ( join.isSequentialSelect() && !persistentClass.isClassOrSuperclassJoin(join) ) hasDeferred = true;
+ subclassTables.add( join.getTable().getQualifiedName(
+ factory.getDialect(),
+ factory.getSettings().getDefaultCatalogName(),
+ factory.getSettings().getDefaultSchemaName()
+ ) );
+ Iterator iter = join.getKey().getColumnIterator();
+ String[] keyCols = new String[ join.getKey().getColumnSpan() ];
+ int i = 0;
+ while ( iter.hasNext() ) {
+ Column col = (Column) iter.next();
+ keyCols[i++] = col.getQuotedName( factory.getDialect() );
+ }
+ joinKeyColumns.add(keyCols);
+ }
+
+ subclassTableSequentialSelect = ArrayHelper.toBooleanArray(isDeferreds);
+ subclassTableNameClosure = ArrayHelper.toStringArray(subclassTables);
+ subclassTableIsLazyClosure = ArrayHelper.toBooleanArray(isLazies);
+ subclassTableKeyColumnClosure = ArrayHelper.to2DStringArray(joinKeyColumns);
+ isClassOrSuperclassTable = ArrayHelper.toBooleanArray(isConcretes);
+ isInverseSubclassTable = ArrayHelper.toBooleanArray(isInverses);
+ isNullableSubclassTable = ArrayHelper.toBooleanArray(isNullables);
+ hasSequentialSelects = hasDeferred;
+
+ // DISCRIMINATOR
+
+ final Object discriminatorValue;
+ if ( persistentClass.isPolymorphic() ) {
+ Value discrimValue = persistentClass.getDiscriminator();
+ if (discrimValue==null) {
+ throw new MappingException("discriminator mapping required for single table polymorphic persistence");
+ }
+ forceDiscriminator = persistentClass.isForceDiscriminator();
+ Selectable selectable = (Selectable) discrimValue.getColumnIterator().next();
+ if ( discrimValue.hasFormula() ) {
+ Formula formula = (Formula) selectable;
+ discriminatorFormula = formula.getFormula();
+ discriminatorFormulaTemplate = formula.getTemplate( factory.getDialect(), factory.getSqlFunctionRegistry() );
+ discriminatorColumnName = null;
+ discriminatorAlias = "clazz_";
+ }
+ else {
+ Column column = (Column) selectable;
+ discriminatorColumnName = column.getQuotedName( factory.getDialect() );
+ discriminatorAlias = column.getAlias( factory.getDialect(), persistentClass.getRootTable() );
+ discriminatorFormula = null;
+ discriminatorFormulaTemplate = null;
+ }
+ discriminatorType = persistentClass.getDiscriminator().getType();
+ if ( persistentClass.isDiscriminatorValueNull() ) {
+ discriminatorValue = NULL_DISCRIMINATOR;
+ discriminatorSQLValue = InFragment.NULL;
+ discriminatorInsertable = false;
+ }
+ else if ( persistentClass.isDiscriminatorValueNotNull() ) {
+ discriminatorValue = NOT_NULL_DISCRIMINATOR;
+ discriminatorSQLValue = InFragment.NOT_NULL;
+ discriminatorInsertable = false;
+ }
+ else {
+ discriminatorInsertable = persistentClass.isDiscriminatorInsertable() && !discrimValue.hasFormula();
+ try {
+ DiscriminatorType dtype = (DiscriminatorType) discriminatorType;
+ discriminatorValue = dtype.stringToObject( persistentClass.getDiscriminatorValue() );
+ discriminatorSQLValue = dtype.objectToSQLString( discriminatorValue, factory.getDialect() );
+ }
+ catch (ClassCastException cce) {
+ throw new MappingException("Illegal discriminator type: " + discriminatorType.getName() );
+ }
+ catch (Exception e) {
+ throw new MappingException("Could not format discriminator value to SQL string", e);
+ }
+ }
+ }
+ else {
+ forceDiscriminator = false;
+ discriminatorInsertable = false;
+ discriminatorColumnName = null;
+ discriminatorAlias = null;
+ discriminatorType = null;
+ discriminatorValue = null;
+ discriminatorSQLValue = null;
+ discriminatorFormula = null;
+ discriminatorFormulaTemplate = null;
+ }
+
+ // PROPERTIES
+
+ propertyTableNumbers = new int[ getPropertySpan() ];
+ Iterator iter = persistentClass.getPropertyClosureIterator();
+ int i=0;
+ while( iter.hasNext() ) {
+ Property prop = (Property) iter.next();
+ propertyTableNumbers[i++] = persistentClass.getJoinNumber(prop);
+
+ }
+
+ //TODO: code duplication with JoinedSubclassEntityPersister
+
+ ArrayList columnJoinNumbers = new ArrayList();
+ ArrayList formulaJoinedNumbers = new ArrayList();
+ ArrayList propertyJoinNumbers = new ArrayList();
+
+ iter = persistentClass.getSubclassPropertyClosureIterator();
+ while ( iter.hasNext() ) {
+ Property prop = (Property) iter.next();
+ Integer join = new Integer( persistentClass.getJoinNumber(prop) );
+ propertyJoinNumbers.add(join);
+
+ //propertyTableNumbersByName.put( prop.getName(), join );
+ propertyTableNumbersByNameAndSubclass.put(
+ prop.getPersistentClass().getEntityName() + '.' + prop.getName(),
+ join
+ );
+
+ Iterator citer = prop.getColumnIterator();
+ while ( citer.hasNext() ) {
+ Selectable thing = (Selectable) citer.next();
+ if ( thing.isFormula() ) {
+ formulaJoinedNumbers.add(join);
+ }
+ else {
+ columnJoinNumbers.add(join);
+ }
+ }
+ }
+ subclassColumnTableNumberClosure = ArrayHelper.toIntArray(columnJoinNumbers);
+ subclassFormulaTableNumberClosure = ArrayHelper.toIntArray(formulaJoinedNumbers);
+ subclassPropertyTableNumberClosure = ArrayHelper.toIntArray(propertyJoinNumbers);
+
+ int subclassSpan = persistentClass.getSubclassSpan() + 1;
+ subclassClosure = new String[subclassSpan];
+ subclassClosure[0] = getEntityName();
+ if ( persistentClass.isPolymorphic() ) {
+ subclassesByDiscriminatorValue.put( discriminatorValue, getEntityName() );
+ }
+
+ // SUBCLASSES
+ if ( persistentClass.isPolymorphic() ) {
+ iter = persistentClass.getSubclassIterator();
+ int k=1;
+ while ( iter.hasNext() ) {
+ Subclass sc = (Subclass) iter.next();
+ subclassClosure[k++] = sc.getEntityName();
+ if ( sc.isDiscriminatorValueNull() ) {
+ subclassesByDiscriminatorValue.put( NULL_DISCRIMINATOR, sc.getEntityName() );
+ }
+ else if ( sc.isDiscriminatorValueNotNull() ) {
+ subclassesByDiscriminatorValue.put( NOT_NULL_DISCRIMINATOR, sc.getEntityName() );
+ }
+ else {
+ try {
+ DiscriminatorType dtype = (DiscriminatorType) discriminatorType;
+ subclassesByDiscriminatorValue.put(
+ dtype.stringToObject( sc.getDiscriminatorValue() ),
+ sc.getEntityName()
+ );
+ }
+ catch (ClassCastException cce) {
+ throw new MappingException("Illegal discriminator type: " + discriminatorType.getName() );
+ }
+ catch (Exception e) {
+ throw new MappingException("Error parsing discriminator value", e);
+ }
+ }
+ }
+ }
+
+ initLockers();
+
+ initSubclassPropertyAliasesMap(persistentClass);
+
+ postConstruct(mapping);
+
+ }
+
+ protected boolean isInverseTable(int j) {
+ return isInverseTable[j];
+ }
+
+ protected boolean isInverseSubclassTable(int j) {
+ return isInverseSubclassTable[j];
+ }
+
+ public String getDiscriminatorColumnName() {
+ return discriminatorColumnName;
+ }
+
+ protected String getDiscriminatorAlias() {
+ return discriminatorAlias;
+ }
+
+ protected String getDiscriminatorFormulaTemplate() {
+ return discriminatorFormulaTemplate;
+ }
+
+ public String getTableName() {
+ return qualifiedTableNames[0];
+ }
+
+ public Type getDiscriminatorType() {
+ return discriminatorType;
+ }
+
+ public String getDiscriminatorSQLValue() {
+ return discriminatorSQLValue;
+ }
+
+ public String[] getSubclassClosure() {
+ return subclassClosure;
+ }
+
+ public String getSubclassForDiscriminatorValue(Object value) {
+ if (value==null) {
+ return (String) subclassesByDiscriminatorValue.get(NULL_DISCRIMINATOR);
+ }
+ else {
+ String result = (String) subclassesByDiscriminatorValue.get(value);
+ if (result==null) result = (String) subclassesByDiscriminatorValue.get(NOT_NULL_DISCRIMINATOR);
+ return result;
+ }
+ }
+
+ public Serializable[] getPropertySpaces() {
+ return spaces;
+ }
+
+ //Access cached SQL
+
+ protected boolean isDiscriminatorFormula() {
+ return discriminatorColumnName==null;
+ }
+
+ protected String getDiscriminatorFormula() {
+ return discriminatorFormula;
+ }
+
+ protected String getTableName(int j) {
+ return qualifiedTableNames[j];
+ }
+
+ protected String[] getKeyColumns(int j) {
+ return keyColumnNames[j];
+ }
+
+ protected boolean isTableCascadeDeleteEnabled(int j) {
+ return cascadeDeleteEnabled[j];
+ }
+
+ protected boolean isPropertyOfTable(int property, int j) {
+ return propertyTableNumbers[property]==j;
+ }
+
+ protected boolean isSubclassTableSequentialSelect(int j) {
+ return subclassTableSequentialSelect[j] && !isClassOrSuperclassTable[j];
+ }
+
+ // Execute the SQL:
+
+ public String fromTableFragment(String name) {
+ return getTableName() + ' ' + name;
+ }
+
+ public String filterFragment(String alias) throws MappingException {
+ String result = discriminatorFilterFragment(alias);
+ if ( hasWhere() ) result += " and " + getSQLWhereString(alias);
+ return result;
+ }
+
+ public String oneToManyFilterFragment(String alias) throws MappingException {
+ return forceDiscriminator ?
+ discriminatorFilterFragment(alias) :
+ "";
+ }
+
+ private String discriminatorFilterFragment(String alias) throws MappingException {
+ if ( needsDiscriminator() ) {
+ InFragment frag = new InFragment();
+
+ if ( isDiscriminatorFormula() ) {
+ frag.setFormula( alias, getDiscriminatorFormulaTemplate() );
+ }
+ else {
+ frag.setColumn( alias, getDiscriminatorColumnName() );
+ }
+
+ String[] subclasses = getSubclassClosure();
+ for ( int i=0; i<subclasses.length; i++ ) {
+ final Queryable queryable = (Queryable) getFactory().getEntityPersister( subclasses[i] );
+ if ( !queryable.isAbstract() ) frag.addValue( queryable.getDiscriminatorSQLValue() );
+ }
+
+ StringBuffer buf = new StringBuffer(50)
+ .append(" and ")
+ .append( frag.toFragmentString() );
+
+ return buf.toString();
+ }
+ else {
+ return "";
+ }
+ }
+
+ private boolean needsDiscriminator() {
+ return forceDiscriminator || isInherited();
+ }
+
+ public String getSubclassPropertyTableName(int i) {
+ return subclassTableNameClosure[ subclassPropertyTableNumberClosure[i] ];
+ }
+
+ protected void addDiscriminatorToSelect(SelectFragment select, String name, String suffix) {
+ if ( isDiscriminatorFormula() ) {
+ select.addFormula( name, getDiscriminatorFormulaTemplate(), getDiscriminatorAlias() );
+ }
+ else {
+ select.addColumn( name, getDiscriminatorColumnName(), getDiscriminatorAlias() );
+ }
+ }
+
+ protected int[] getPropertyTableNumbersInSelect() {
+ return propertyTableNumbers;
+ }
+
+ protected int getSubclassPropertyTableNumber(int i) {
+ return subclassPropertyTableNumberClosure[i];
+ }
+
+ public int getTableSpan() {
+ return joinSpan;
+ }
+
+ protected void addDiscriminatorToInsert(Insert insert) {
+
+ if (discriminatorInsertable) {
+ insert.addColumn( getDiscriminatorColumnName(), discriminatorSQLValue );
+ }
+
+ }
+
+ protected int[] getSubclassColumnTableNumberClosure() {
+ return subclassColumnTableNumberClosure;
+ }
+
+ protected int[] getSubclassFormulaTableNumberClosure() {
+ return subclassFormulaTableNumberClosure;
+ }
+
+ protected int[] getPropertyTableNumbers() {
+ return propertyTableNumbers;
+ }
+
+ protected boolean isSubclassPropertyDeferred(String propertyName, String entityName) {
+ return hasSequentialSelects &&
+ isSubclassTableSequentialSelect( getSubclassPropertyTableNumber(propertyName, entityName) );
+ }
+
+ public boolean hasSequentialSelect() {
+ return hasSequentialSelects;
+ }
+
+ private int getSubclassPropertyTableNumber(String propertyName, String entityName) {
+ Type type = propertyMapping.toType(propertyName);
+ if ( type.isAssociationType() && ( (AssociationType) type ).useLHSPrimaryKey() ) return 0;
+ final Integer tabnum = (Integer) propertyTableNumbersByNameAndSubclass.get(entityName + '.' + propertyName);
+ return tabnum==null ? 0 : tabnum.intValue();
+ }
+
+ protected String getSequentialSelect(String entityName) {
+ return (String) sequentialSelectStringsByEntityName.get(entityName);
+ }
+
+ private String generateSequentialSelect(Loadable persister) {
+ //if ( this==persister || !hasSequentialSelects ) return null;
+
+ //note that this method could easily be moved up to BasicEntityPersister,
+ //if we ever needed to reuse it from other subclasses
+
+ //figure out which tables need to be fetched
+ AbstractEntityPersister subclassPersister = (AbstractEntityPersister) persister;
+ HashSet tableNumbers = new HashSet();
+ String[] props = subclassPersister.getPropertyNames();
+ String[] classes = subclassPersister.getPropertySubclassNames();
+ for ( int i=0; i<props.length; i++ ) {
+ int propTableNumber = getSubclassPropertyTableNumber( props[i], classes[i] );
+ if ( isSubclassTableSequentialSelect(propTableNumber) && !isSubclassTableLazy(propTableNumber) ) {
+ tableNumbers.add( new Integer(propTableNumber) );
+ }
+ }
+ if ( tableNumbers.isEmpty() ) return null;
+
+ //figure out which columns are needed
+ ArrayList columnNumbers = new ArrayList();
+ final int[] columnTableNumbers = getSubclassColumnTableNumberClosure();
+ for ( int i=0; i<getSubclassColumnClosure().length; i++ ) {
+ if ( tableNumbers.contains( new Integer( columnTableNumbers[i] ) ) ) {
+ columnNumbers.add( new Integer(i) );
+ }
+ }
+
+ //figure out which formulas are needed
+ ArrayList formulaNumbers = new ArrayList();
+ final int[] formulaTableNumbers = getSubclassColumnTableNumberClosure();
+ for ( int i=0; i<getSubclassFormulaTemplateClosure().length; i++ ) {
+ if ( tableNumbers.contains( new Integer( formulaTableNumbers[i] ) ) ) {
+ formulaNumbers.add( new Integer(i) );
+ }
+ }
+
+ //render the SQL
+ return renderSelect(
+ ArrayHelper.toIntArray(tableNumbers),
+ ArrayHelper.toIntArray(columnNumbers),
+ ArrayHelper.toIntArray(formulaNumbers)
+ );
+ }
+
+
+ protected String[] getSubclassTableKeyColumns(int j) {
+ return subclassTableKeyColumnClosure[j];
+ }
+
+ public String getSubclassTableName(int j) {
+ return subclassTableNameClosure[j];
+ }
+
+ public int getSubclassTableSpan() {
+ return subclassTableNameClosure.length;
+ }
+
+ protected boolean isClassOrSuperclassTable(int j) {
+ return isClassOrSuperclassTable[j];
+ }
+
+ protected boolean isSubclassTableLazy(int j) {
+ return subclassTableIsLazyClosure[j];
+ }
+
+ protected boolean isNullableTable(int j) {
+ return isNullableTable[j];
+ }
+
+ protected boolean isNullableSubclassTable(int j) {
+ return isNullableSubclassTable[j];
+ }
+
+ public String getPropertyTableName(String propertyName) {
+ Integer index = getEntityMetamodel().getPropertyIndexOrNull(propertyName);
+ if (index==null) return null;
+ return qualifiedTableNames[ propertyTableNumbers[ index.intValue() ] ];
+ }
+
+ public void postInstantiate() {
+ super.postInstantiate();
+ if (hasSequentialSelects) {
+ String[] entityNames = getSubclassClosure();
+ for ( int i=1; i<entityNames.length; i++ ) {
+ Loadable loadable = (Loadable) getFactory().getEntityPersister( entityNames[i] );
+ if ( !loadable.isAbstract() ) { //perhaps not really necessary...
+ String sequentialSelect = generateSequentialSelect(loadable);
+ sequentialSelectStringsByEntityName.put( entityNames[i], sequentialSelect );
+ }
+ }
+ }
+ }
+
+ public boolean isMultiTable() {
+ return getTableSpan() > 1;
+ }
+
+ public String[] getConstraintOrderedTableNameClosure() {
+ return constraintOrderedTableNames;
+ }
+
+ public String[][] getContraintOrderedTableKeyColumnClosure() {
+ return constraintOrderedKeyColumnNames;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,459 @@
+//$Id: UnionSubclassEntityPersister.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.persister.entity;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.ArrayList;
+
+import org.hibernate.AssertionFailure;
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.LockMode;
+import org.hibernate.MappingException;
+import org.hibernate.cache.access.EntityRegionAccessStrategy;
+import org.hibernate.cfg.Settings;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.Mapping;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.ExecuteUpdateResultCheckStyle;
+import org.hibernate.id.IdentityGenerator;
+import org.hibernate.mapping.Column;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Subclass;
+import org.hibernate.mapping.Table;
+import org.hibernate.sql.SelectFragment;
+import org.hibernate.sql.SimpleSelect;
+import org.hibernate.type.Type;
+import org.hibernate.util.ArrayHelper;
+import org.hibernate.util.JoinedIterator;
+import org.hibernate.util.SingletonIterator;
+
+/**
+ * Implementation of the "table-per-concrete-class" or "roll-down" mapping
+ * strategy for an entity and its inheritence hierarchy.
+ *
+ * @author Gavin King
+ */
+public class UnionSubclassEntityPersister extends AbstractEntityPersister {
+
+ // the class hierarchy structure
+ private final String subquery;
+ private final String tableName;
+ //private final String rootTableName;
+ private final String[] subclassClosure;
+ private final String[] spaces;
+ private final String[] subclassSpaces;
+ private final String discriminatorSQLValue;
+ private final Map subclassByDiscriminatorValue = new HashMap();
+
+ private final String[] constraintOrderedTableNames;
+ private final String[][] constraintOrderedKeyColumnNames;
+
+ //INITIALIZATION:
+
+ public UnionSubclassEntityPersister(
+ final PersistentClass persistentClass,
+ final EntityRegionAccessStrategy cacheAccessStrategy,
+ final SessionFactoryImplementor factory,
+ final Mapping mapping) throws HibernateException {
+
+ super( persistentClass, cacheAccessStrategy, factory );
+
+ if ( getIdentifierGenerator() instanceof IdentityGenerator ) {
+ throw new MappingException(
+ "Cannot use identity column key generation with <union-subclass> mapping for: " +
+ getEntityName()
+ );
+ }
+
+ // TABLE
+
+ tableName = persistentClass.getTable().getQualifiedName(
+ factory.getDialect(),
+ factory.getSettings().getDefaultCatalogName(),
+ factory.getSettings().getDefaultSchemaName()
+ );
+ /*rootTableName = persistentClass.getRootTable().getQualifiedName(
+ factory.getDialect(),
+ factory.getDefaultCatalog(),
+ factory.getDefaultSchema()
+ );*/
+
+ //Custom SQL
+
+ String sql;
+ boolean callable = false;
+ ExecuteUpdateResultCheckStyle checkStyle = null;
+ sql = persistentClass.getCustomSQLInsert();
+ callable = sql != null && persistentClass.isCustomInsertCallable();
+ checkStyle = sql == null
+ ? ExecuteUpdateResultCheckStyle.COUNT
+ : persistentClass.getCustomSQLInsertCheckStyle() == null
+ ? ExecuteUpdateResultCheckStyle.determineDefault( sql, callable )
+ : persistentClass.getCustomSQLInsertCheckStyle();
+ customSQLInsert = new String[] { sql };
+ insertCallable = new boolean[] { callable };
+ insertResultCheckStyles = new ExecuteUpdateResultCheckStyle[] { checkStyle };
+
+ sql = persistentClass.getCustomSQLUpdate();
+ callable = sql != null && persistentClass.isCustomUpdateCallable();
+ checkStyle = sql == null
+ ? ExecuteUpdateResultCheckStyle.COUNT
+ : persistentClass.getCustomSQLUpdateCheckStyle() == null
+ ? ExecuteUpdateResultCheckStyle.determineDefault( sql, callable )
+ : persistentClass.getCustomSQLUpdateCheckStyle();
+ customSQLUpdate = new String[] { sql };
+ updateCallable = new boolean[] { callable };
+ updateResultCheckStyles = new ExecuteUpdateResultCheckStyle[] { checkStyle };
+
+ sql = persistentClass.getCustomSQLDelete();
+ callable = sql != null && persistentClass.isCustomDeleteCallable();
+ checkStyle = sql == null
+ ? ExecuteUpdateResultCheckStyle.COUNT
+ : persistentClass.getCustomSQLDeleteCheckStyle() == null
+ ? ExecuteUpdateResultCheckStyle.determineDefault( sql, callable )
+ : persistentClass.getCustomSQLDeleteCheckStyle();
+ customSQLDelete = new String[] { sql };
+ deleteCallable = new boolean[] { callable };
+ deleteResultCheckStyles = new ExecuteUpdateResultCheckStyle[] { checkStyle };
+
+ discriminatorSQLValue = String.valueOf( persistentClass.getSubclassId() );
+
+ // PROPERTIES
+
+ int subclassSpan = persistentClass.getSubclassSpan() + 1;
+ subclassClosure = new String[subclassSpan];
+ subclassClosure[0] = getEntityName();
+
+ // SUBCLASSES
+ subclassByDiscriminatorValue.put(
+ new Integer( persistentClass.getSubclassId() ),
+ persistentClass.getEntityName()
+ );
+ if ( persistentClass.isPolymorphic() ) {
+ Iterator iter = persistentClass.getSubclassIterator();
+ int k=1;
+ while ( iter.hasNext() ) {
+ Subclass sc = (Subclass) iter.next();
+ subclassClosure[k++] = sc.getEntityName();
+ subclassByDiscriminatorValue.put( new Integer( sc.getSubclassId() ), sc.getEntityName() );
+ }
+ }
+
+ //SPACES
+ //TODO: i'm not sure, but perhaps we should exclude
+ // abstract denormalized tables?
+
+ int spacesSize = 1 + persistentClass.getSynchronizedTables().size();
+ spaces = new String[spacesSize];
+ spaces[0] = tableName;
+ Iterator iter = persistentClass.getSynchronizedTables().iterator();
+ for ( int i=1; i<spacesSize; i++ ) {
+ spaces[i] = (String) iter.next();
+ }
+
+ HashSet subclassTables = new HashSet();
+ iter = persistentClass.getSubclassTableClosureIterator();
+ while ( iter.hasNext() ) {
+ Table table = (Table) iter.next();
+ subclassTables.add( table.getQualifiedName(
+ factory.getDialect(),
+ factory.getSettings().getDefaultCatalogName(),
+ factory.getSettings().getDefaultSchemaName()
+ ) );
+ }
+ subclassSpaces = ArrayHelper.toStringArray(subclassTables);
+
+ subquery = generateSubquery(persistentClass, mapping);
+
+ if ( isMultiTable() ) {
+ int idColumnSpan = getIdentifierColumnSpan();
+ ArrayList tableNames = new ArrayList();
+ ArrayList keyColumns = new ArrayList();
+ if ( !isAbstract() ) {
+ tableNames.add( tableName );
+ keyColumns.add( getIdentifierColumnNames() );
+ }
+ iter = persistentClass.getSubclassTableClosureIterator();
+ while ( iter.hasNext() ) {
+ Table tab = ( Table ) iter.next();
+ if ( !tab.isAbstractUnionTable() ) {
+ String tableName = tab.getQualifiedName(
+ factory.getDialect(),
+ factory.getSettings().getDefaultCatalogName(),
+ factory.getSettings().getDefaultSchemaName()
+ );
+ tableNames.add( tableName );
+ String[] key = new String[idColumnSpan];
+ Iterator citer = tab.getPrimaryKey().getColumnIterator();
+ for ( int k=0; k<idColumnSpan; k++ ) {
+ key[k] = ( ( Column ) citer.next() ).getQuotedName( factory.getDialect() );
+ }
+ keyColumns.add( key );
+ }
+ }
+
+ constraintOrderedTableNames = ArrayHelper.toStringArray( tableNames );
+ constraintOrderedKeyColumnNames = ArrayHelper.to2DStringArray( keyColumns );
+ }
+ else {
+ constraintOrderedTableNames = new String[] { tableName };
+ constraintOrderedKeyColumnNames = new String[][] { getIdentifierColumnNames() };
+ }
+
+ initLockers();
+
+ initSubclassPropertyAliasesMap(persistentClass);
+
+ postConstruct(mapping);
+
+ }
+
+ public Serializable[] getQuerySpaces() {
+ return subclassSpaces;
+ }
+
+ public String getTableName() {
+ return subquery;
+ }
+
+ public Type getDiscriminatorType() {
+ return Hibernate.INTEGER;
+ }
+
+ public String getDiscriminatorSQLValue() {
+ return discriminatorSQLValue;
+ }
+
+ public String[] getSubclassClosure() {
+ return subclassClosure;
+ }
+
+ public String getSubclassForDiscriminatorValue(Object value) {
+ return (String) subclassByDiscriminatorValue.get(value);
+ }
+
+ public Serializable[] getPropertySpaces() {
+ return spaces;
+ }
+
+ protected boolean isDiscriminatorFormula() {
+ return false;
+ }
+
+ /**
+ * Generate the SQL that selects a row by id
+ */
+ protected String generateSelectString(LockMode lockMode) {
+ SimpleSelect select = new SimpleSelect( getFactory().getDialect() )
+ .setLockMode(lockMode)
+ .setTableName( getTableName() )
+ .addColumns( getIdentifierColumnNames() )
+ .addColumns(
+ getSubclassColumnClosure(),
+ getSubclassColumnAliasClosure(),
+ getSubclassColumnLazyiness()
+ )
+ .addColumns(
+ getSubclassFormulaClosure(),
+ getSubclassFormulaAliasClosure(),
+ getSubclassFormulaLazyiness()
+ );
+ //TODO: include the rowids!!!!
+ if ( hasSubclasses() ) {
+ if ( isDiscriminatorFormula() ) {
+ select.addColumn( getDiscriminatorFormula(), getDiscriminatorAlias() );
+ }
+ else {
+ select.addColumn( getDiscriminatorColumnName(), getDiscriminatorAlias() );
+ }
+ }
+ if ( getFactory().getSettings().isCommentsEnabled() ) {
+ select.setComment( "load " + getEntityName() );
+ }
+ return select.addCondition( getIdentifierColumnNames(), "=?" ).toStatementString();
+ }
+
+ protected String getDiscriminatorFormula() {
+ return null;
+ }
+
+ protected String getTableName(int j) {
+ return tableName;
+ }
+
+ protected String[] getKeyColumns(int j) {
+ return getIdentifierColumnNames();
+ }
+
+ protected boolean isTableCascadeDeleteEnabled(int j) {
+ return false;
+ }
+
+ protected boolean isPropertyOfTable(int property, int j) {
+ return true;
+ }
+
+ // Execute the SQL:
+
+ public String fromTableFragment(String name) {
+ return getTableName() + ' ' + name;
+ }
+
+ public String filterFragment(String name) {
+ return hasWhere() ?
+ " and " + getSQLWhereString(name) :
+ "";
+ }
+
+ public String getSubclassPropertyTableName(int i) {
+ return getTableName();//ie. the subquery! yuck!
+ }
+
+ protected void addDiscriminatorToSelect(SelectFragment select, String name, String suffix) {
+ select.addColumn( name, getDiscriminatorColumnName(), getDiscriminatorAlias() );
+ }
+
+ protected int[] getPropertyTableNumbersInSelect() {
+ return new int[ getPropertySpan() ];
+ }
+
+ protected int getSubclassPropertyTableNumber(int i) {
+ return 0;
+ }
+
+ public int getSubclassPropertyTableNumber(String propertyName) {
+ return 0;
+ }
+
+ public boolean isMultiTable() {
+ // This could also just be true all the time...
+ return isAbstract() || hasSubclasses();
+ }
+
+ public int getTableSpan() {
+ return 1;
+ }
+
+ protected int[] getSubclassColumnTableNumberClosure() {
+ return new int[ getSubclassColumnClosure().length ];
+ }
+
+ protected int[] getSubclassFormulaTableNumberClosure() {
+ return new int[ getSubclassFormulaClosure().length ];
+ }
+
+ protected boolean[] getTableHasColumns() {
+ return new boolean[] { true };
+ }
+
+ protected int[] getPropertyTableNumbers() {
+ return new int[ getPropertySpan() ];
+ }
+
+ protected String generateSubquery(PersistentClass model, Mapping mapping) {
+
+ Dialect dialect = getFactory().getDialect();
+ Settings settings = getFactory().getSettings();
+
+ if ( !model.hasSubclasses() ) {
+ return model.getTable().getQualifiedName(
+ dialect,
+ settings.getDefaultCatalogName(),
+ settings.getDefaultSchemaName()
+ );
+ }
+
+ HashSet columns = new HashSet();
+ Iterator titer = model.getSubclassTableClosureIterator();
+ while ( titer.hasNext() ) {
+ Table table = (Table) titer.next();
+ if ( !table.isAbstractUnionTable() ) {
+ Iterator citer = table.getColumnIterator();
+ while ( citer.hasNext() ) columns.add( citer.next() );
+ }
+ }
+
+ StringBuffer buf = new StringBuffer()
+ .append("( ");
+
+ Iterator siter = new JoinedIterator(
+ new SingletonIterator(model),
+ model.getSubclassIterator()
+ );
+
+ while ( siter.hasNext() ) {
+ PersistentClass clazz = (PersistentClass) siter.next();
+ Table table = clazz.getTable();
+ if ( !table.isAbstractUnionTable() ) {
+ //TODO: move to .sql package!!
+ buf.append("select ");
+ Iterator citer = columns.iterator();
+ while ( citer.hasNext() ) {
+ Column col = (Column) citer.next();
+ if ( !table.containsColumn(col) ) {
+ int sqlType = col.getSqlTypeCode(mapping);
+ buf.append( dialect.getSelectClauseNullString(sqlType) )
+ .append(" as ");
+ }
+ buf.append( col.getName() );
+ buf.append(", ");
+ }
+ buf.append( clazz.getSubclassId() )
+ .append(" as clazz_");
+ buf.append(" from ")
+ .append( table.getQualifiedName(
+ dialect,
+ settings.getDefaultCatalogName(),
+ settings.getDefaultSchemaName()
+ ) );
+ buf.append(" union ");
+ if ( dialect.supportsUnionAll() ) {
+ buf.append("all ");
+ }
+ }
+ }
+
+ if ( buf.length() > 2 ) {
+ //chop the last union (all)
+ buf.setLength( buf.length() - ( dialect.supportsUnionAll() ? 11 : 7 ) );
+ }
+
+ return buf.append(" )").toString();
+ }
+
+ protected String[] getSubclassTableKeyColumns(int j) {
+ if (j!=0) throw new AssertionFailure("only one table");
+ return getIdentifierColumnNames();
+ }
+
+ public String getSubclassTableName(int j) {
+ if (j!=0) throw new AssertionFailure("only one table");
+ return tableName;
+ }
+
+ public int getSubclassTableSpan() {
+ return 1;
+ }
+
+ protected boolean isClassOrSuperclassTable(int j) {
+ if (j!=0) throw new AssertionFailure("only one table");
+ return true;
+ }
+
+ public String getPropertyTableName(String propertyName) {
+ //TODO: check this....
+ return getTableName();
+ }
+
+ public String[] getConstraintOrderedTableNameClosure() {
+ return constraintOrderedTableNames;
+ }
+
+ public String[][] getContraintOrderedTableKeyColumnClosure() {
+ return constraintOrderedKeyColumnNames;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/UniqueKeyLoadable.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/UniqueKeyLoadable.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/UniqueKeyLoadable.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+//$Id: UniqueKeyLoadable.java 5732 2005-02-14 15:53:24Z oneovthafew $
+package org.hibernate.persister.entity;
+
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionImplementor;
+
+/**
+ * @author Gavin King
+ */
+public interface UniqueKeyLoadable extends Loadable {
+ /**
+ * Load an instance of the persistent class, by a unique key other
+ * than the primary key.
+ */
+ public Object loadByUniqueKey(String propertyName, Object uniqueKey, SessionImplementor session)
+ throws HibernateException;
+ /**
+ * Get the property number of the unique key property
+ */
+ public int getPropertyIndex(String propertyName);
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,18 @@
+<html>
+<head></head>
+<body>
+<p>
+ This package abstracts persistence mechanisms for
+ entities, and defines the Hibernate runtime
+ metamodel.
+</p>
+<p>
+ Strategies for persisting entities implement the
+ <tt>EntityPersister</tt> interface. Optionally,
+ they may implement certain additional interfaces
+ that define contracts with various loaders.
+ Concrete implementations in this package define
+ the built-in inheritance mapping strategies.
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/persister/entity/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/persister/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/persister/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/persister/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,9 @@
+<html>
+<head></head>
+<body>
+<p>
+ A persister defines a mapping strategy for a collection
+ or entity.
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/persister/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/pretty/DDLFormatter.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/pretty/DDLFormatter.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/pretty/DDLFormatter.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,126 @@
+//$Id: DDLFormatter.java 7664 2005-07-27 23:29:37Z oneovthafew $
+package org.hibernate.pretty;
+
+import java.util.StringTokenizer;
+
+public class DDLFormatter {
+
+ private String sql;
+
+ public DDLFormatter(String sql) {
+ this.sql = sql;
+ }
+
+ /**
+ * Format an SQL statement using simple rules:
+ * a) Insert newline after each comma;
+ * b) Indent three spaces after each inserted newline;
+ * If the statement contains single/double quotes return unchanged,
+ * it is too complex and could be broken by simple formatting.
+ */
+ public String format() {
+ if ( sql.toLowerCase().startsWith("create table") ) {
+ return formatCreateTable();
+ }
+ else if ( sql.toLowerCase().startsWith("alter table") ) {
+ return formatAlterTable();
+ }
+ else if ( sql.toLowerCase().startsWith("comment on") ) {
+ return formatCommentOn();
+ }
+ else {
+ return "\n " + sql;
+ }
+ }
+
+ private String formatCommentOn() {
+ StringBuffer result = new StringBuffer(60).append("\n ");
+ StringTokenizer tokens = new StringTokenizer( sql, " '[]\"", true );
+
+ boolean quoted = false;
+ while ( tokens.hasMoreTokens() ) {
+ String token = tokens.nextToken();
+ result.append(token);
+ if ( isQuote(token) ) {
+ quoted = !quoted;
+ }
+ else if (!quoted) {
+ if ( "is".equals(token) ) {
+ result.append("\n ");
+ }
+ }
+ }
+
+ return result.toString();
+ }
+
+ private String formatAlterTable() {
+ StringBuffer result = new StringBuffer(60).append("\n ");
+ StringTokenizer tokens = new StringTokenizer( sql, " (,)'[]\"", true );
+
+ boolean quoted = false;
+ while ( tokens.hasMoreTokens() ) {
+ String token = tokens.nextToken();
+ if ( isQuote(token) ) {
+ quoted = !quoted;
+ }
+ else if (!quoted) {
+ if ( isBreak(token) ) {
+ result.append("\n ");
+ }
+ }
+ result.append(token);
+ }
+
+ return result.toString();
+ }
+
+ private String formatCreateTable() {
+ StringBuffer result = new StringBuffer(60).append("\n ");
+ StringTokenizer tokens = new StringTokenizer( sql, "(,)'[]\"", true );
+
+ int depth = 0;
+ boolean quoted = false;
+ while ( tokens.hasMoreTokens() ) {
+ String token = tokens.nextToken();
+ if ( isQuote(token) ) {
+ quoted = !quoted;
+ result.append(token);
+ }
+ else if (quoted) {
+ result.append(token);
+ }
+ else {
+ if ( ")".equals(token) ) {
+ depth--;
+ if (depth==0) result.append("\n ");
+ }
+ result.append(token);
+ if ( ",".equals(token) && depth==1 ) result.append("\n ");
+ if ( "(".equals(token) ) {
+ depth++;
+ if (depth==1) result.append("\n ");
+ }
+ }
+ }
+
+ return result.toString();
+ }
+
+ private static boolean isBreak(String token) {
+ return "drop".equals(token) ||
+ "add".equals(token) ||
+ "references".equals(token) ||
+ "foreign".equals(token) ||
+ "on".equals(token);
+ }
+
+ private static boolean isQuote(String tok) {
+ return "\"".equals(tok) ||
+ "`".equals(tok) ||
+ "]".equals(tok) ||
+ "[".equals(tok) ||
+ "'".equals(tok);
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/pretty/DDLFormatter.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/pretty/Formatter.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/pretty/Formatter.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/pretty/Formatter.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,370 @@
+//$Id: Formatter.java 9961 2006-05-30 13:17:45Z max.andersen at jboss.com $
+package org.hibernate.pretty;
+
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import org.hibernate.util.StringHelper;
+
+public class Formatter {
+
+ private static final Set BEGIN_CLAUSES = new HashSet();
+ private static final Set END_CLAUSES = new HashSet();
+ private static final Set LOGICAL = new HashSet();
+ private static final Set QUANTIFIERS = new HashSet();
+ private static final Set DML = new HashSet();
+ private static final Set MISC = new HashSet();
+ static {
+
+ BEGIN_CLAUSES.add("left");
+ BEGIN_CLAUSES.add("right");
+ BEGIN_CLAUSES.add("inner");
+ BEGIN_CLAUSES.add("outer");
+ BEGIN_CLAUSES.add("group");
+ BEGIN_CLAUSES.add("order");
+
+ END_CLAUSES.add("where");
+ END_CLAUSES.add("set");
+ END_CLAUSES.add("having");
+ END_CLAUSES.add("join");
+ END_CLAUSES.add("from");
+ END_CLAUSES.add("by");
+ END_CLAUSES.add("join");
+ END_CLAUSES.add("into");
+ END_CLAUSES.add("union");
+
+ LOGICAL.add("and");
+ LOGICAL.add("or");
+ LOGICAL.add("when");
+ LOGICAL.add("else");
+ LOGICAL.add("end");
+
+ QUANTIFIERS.add("in");
+ QUANTIFIERS.add("all");
+ QUANTIFIERS.add("exists");
+ QUANTIFIERS.add("some");
+ QUANTIFIERS.add("any");
+
+ DML.add("insert");
+ DML.add("update");
+ DML.add("delete");
+
+ MISC.add("select");
+ MISC.add("on");
+ //MISC.add("values");
+
+ }
+
+ String indentString = " ";
+ String initial = "\n ";
+
+ boolean beginLine = true;
+ boolean afterBeginBeforeEnd = false;
+ boolean afterByOrSetOrFromOrSelect = false;
+ boolean afterValues = false;
+ boolean afterOn = false;
+ boolean afterBetween = false;
+ boolean afterInsert = false;
+ int inFunction = 0;
+ int parensSinceSelect = 0;
+ private LinkedList parenCounts = new LinkedList();
+ private LinkedList afterByOrFromOrSelects = new LinkedList();
+
+ int indent = 1;
+
+ StringBuffer result = new StringBuffer();
+ StringTokenizer tokens;
+ String lastToken;
+ String token;
+ String lcToken;
+
+ public Formatter(String sql) {
+ tokens = new StringTokenizer(
+ sql,
+ "()+*/-=<>'`\"[]," + StringHelper.WHITESPACE,
+ true
+ );
+ }
+
+ public Formatter setInitialString(String initial) {
+ this.initial = initial;
+ return this;
+ }
+
+ public Formatter setIndentString(String indent) {
+ this.indentString = indent;
+ return this;
+ }
+
+ public String format() {
+
+ result.append(initial);
+
+ while ( tokens.hasMoreTokens() ) {
+ token = tokens.nextToken();
+ lcToken = token.toLowerCase();
+
+ if ( "'".equals(token) ) {
+ String t;
+ do {
+ t = tokens.nextToken();
+ token += t;
+ }
+ while ( !"'".equals(t) && tokens.hasMoreTokens() ); // cannot handle single quotes
+ }
+ else if ( "\"".equals(token) ) {
+ String t;
+ do {
+ t = tokens.nextToken();
+ token += t;
+ }
+ while ( !"\"".equals(t) );
+ }
+
+ if ( afterByOrSetOrFromOrSelect && ",".equals(token) ) {
+ commaAfterByOrFromOrSelect();
+ }
+ else if ( afterOn && ",".equals(token) ) {
+ commaAfterOn();
+ }
+
+ else if ( "(".equals(token) ) {
+ openParen();
+ }
+ else if ( ")".equals(token) ) {
+ closeParen();
+ }
+
+ else if ( BEGIN_CLAUSES.contains(lcToken) ) {
+ beginNewClause();
+ }
+
+ else if ( END_CLAUSES.contains(lcToken) ) {
+ endNewClause();
+ }
+
+ else if ( "select".equals(lcToken) ) {
+ select();
+ }
+
+ else if ( DML.contains(lcToken) ) {
+ updateOrInsertOrDelete();
+ }
+
+ else if ( "values".equals(lcToken) ) {
+ values();
+ }
+
+ else if ( "on".equals(lcToken) ) {
+ on();
+ }
+
+ else if ( afterBetween && lcToken.equals("and") ) {
+ misc();
+ afterBetween = false;
+ }
+
+ else if ( LOGICAL.contains(lcToken) ) {
+ logical();
+ }
+
+ else if ( isWhitespace(token) ) {
+ white();
+ }
+
+ else {
+ misc();
+ }
+
+ if ( !isWhitespace( token ) ) lastToken = lcToken;
+
+ }
+ return result.toString();
+ }
+
+ private void commaAfterOn() {
+ out();
+ indent--;
+ newline();
+ afterOn = false;
+ afterByOrSetOrFromOrSelect = true;
+ }
+
+ private void commaAfterByOrFromOrSelect() {
+ out();
+ newline();
+ }
+
+ private void logical() {
+ if ( "end".equals(lcToken) ) indent--;
+ newline();
+ out();
+ beginLine = false;
+ }
+
+ private void on() {
+ indent++;
+ afterOn = true;
+ newline();
+ out();
+ beginLine = false;
+ }
+
+ private void misc() {
+ out();
+ if ( "between".equals(lcToken) ) {
+ afterBetween = true;
+ }
+ if (afterInsert) {
+ newline();
+ afterInsert = false;
+ }
+ else {
+ beginLine = false;
+ if ( "case".equals(lcToken) ) {
+ indent++;
+ }
+ }
+ }
+
+ private void white() {
+ if ( !beginLine ) {
+ result.append(" ");
+ }
+ }
+
+ private void updateOrInsertOrDelete() {
+ out();
+ indent++;
+ beginLine = false;
+ if ( "update".equals(lcToken) ) newline();
+ if ( "insert".equals(lcToken) ) afterInsert = true;
+ }
+
+ private void select() {
+ out();
+ indent++;
+ newline();
+ parenCounts.addLast( new Integer(parensSinceSelect) );
+ afterByOrFromOrSelects.addLast( new Boolean(afterByOrSetOrFromOrSelect) );
+ parensSinceSelect = 0;
+ afterByOrSetOrFromOrSelect = true;
+ }
+
+ private void out() {
+ result.append(token);
+ }
+
+ private void endNewClause() {
+ if (!afterBeginBeforeEnd) {
+ indent--;
+ if (afterOn) {
+ indent--;
+ afterOn=false;
+ }
+ newline();
+ }
+ out();
+ if ( !"union".equals(lcToken) ) indent++;
+ newline();
+ afterBeginBeforeEnd = false;
+ afterByOrSetOrFromOrSelect = "by".equals(lcToken)
+ || "set".equals(lcToken)
+ || "from".equals(lcToken);
+ }
+
+ private void beginNewClause() {
+ if (!afterBeginBeforeEnd) {
+ if (afterOn) {
+ indent--;
+ afterOn=false;
+ }
+ indent--;
+ newline();
+ }
+ out();
+ beginLine = false;
+ afterBeginBeforeEnd = true;
+ }
+
+ private void values() {
+ indent--;
+ newline();
+ out();
+ indent++;
+ newline();
+ afterValues = true;
+ }
+
+ private void closeParen() {
+ parensSinceSelect--;
+ if (parensSinceSelect<0) {
+ indent--;
+ parensSinceSelect = ( (Integer) parenCounts.removeLast() ).intValue();
+ afterByOrSetOrFromOrSelect = ( (Boolean) afterByOrFromOrSelects.removeLast() ).booleanValue();
+ }
+ if ( inFunction>0 ) {
+ inFunction--;
+ out();
+ }
+ else {
+ if (!afterByOrSetOrFromOrSelect) {
+ indent--;
+ newline();
+ }
+ out();
+ }
+ beginLine = false;
+ }
+
+ private void openParen() {
+ if ( isFunctionName( lastToken ) || inFunction>0 ) {
+ inFunction++;
+ }
+ beginLine = false;
+ if ( inFunction>0 ) {
+ out();
+ }
+ else {
+ out();
+ if (!afterByOrSetOrFromOrSelect) {
+ indent++;
+ newline();
+ beginLine = true;
+ }
+ }
+ parensSinceSelect++;
+ }
+
+ private static boolean isFunctionName(String tok) {
+ final char begin = tok.charAt(0);
+ final boolean isIdentifier = Character.isJavaIdentifierStart( begin ) || '"'==begin;
+ return isIdentifier &&
+ !LOGICAL.contains(tok) &&
+ !END_CLAUSES.contains(tok) &&
+ !QUANTIFIERS.contains(tok) &&
+ !DML.contains(tok) &&
+ !MISC.contains(tok);
+ }
+
+ private static boolean isWhitespace(String token) {
+ return StringHelper.WHITESPACE.indexOf(token)>=0;
+ }
+
+ private void newline() {
+ result.append("\n");
+ for ( int i=0; i<indent; i++ ) {
+ result.append(indentString);
+ }
+ beginLine = true;
+ }
+
+ public static void main(String[] args) {
+ if ( args.length>0 ) System.out.println(
+ new Formatter( StringHelper.join(" ", args) ).format()
+ );
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/pretty/Formatter.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/pretty/MessageHelper.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/pretty/MessageHelper.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/pretty/MessageHelper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,321 @@
+//$Id: MessageHelper.java 9561 2006-03-07 14:17:16Z steve.ebersole at jboss.com $
+package org.hibernate.pretty;
+
+import java.io.Serializable;
+
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.type.Type;
+
+/**
+ * MessageHelper methods for rendering log messages relating to managed
+ * entities and collections typically used in log statements and exception
+ * messages.
+ *
+ * @author Max Andersen, Gavin King
+ */
+public final class MessageHelper {
+
+ private MessageHelper() {
+ }
+
+
+ // entities ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Generate an info message string relating to a particular entity,
+ * based on the given entityName and id.
+ *
+ * @param entityName The defined entity name.
+ * @param id The entity id value.
+ * @return An info string, in the form [FooBar#1].
+ */
+ public static String infoString(String entityName, Serializable id) {
+ StringBuffer s = new StringBuffer();
+ s.append( '[' );
+ if( entityName == null ) {
+ s.append( "<null entity name>" );
+ }
+ else {
+ s.append( entityName );
+ }
+ s.append( '#' );
+
+ if ( id == null ) {
+ s.append( "<null>" );
+ }
+ else {
+ s.append( id );
+ }
+ s.append( ']' );
+
+ return s.toString();
+ }
+
+ /**
+ * Generate an info message string relating to a particular entity.
+ *
+ * @param persister The persister for the entity
+ * @param id The entity id value
+ * @param factory The session factory
+ * @return An info string, in the form [FooBar#1]
+ */
+ public static String infoString(
+ EntityPersister persister,
+ Object id,
+ SessionFactoryImplementor factory) {
+ StringBuffer s = new StringBuffer();
+ s.append( '[' );
+ Type idType;
+ if( persister == null ) {
+ s.append( "<null EntityPersister>" );
+ idType = null;
+ }
+ else {
+ s.append( persister.getEntityName() );
+ idType = persister.getIdentifierType();
+ }
+ s.append( '#' );
+
+ if ( id == null ) {
+ s.append( "<null>" );
+ }
+ else {
+ if ( idType == null ) {
+ s.append( id );
+ }
+ else {
+ s.append( idType.toLoggableString( id, factory ) );
+ }
+ }
+ s.append( ']' );
+
+ return s.toString();
+
+ }
+
+ /**
+ * Generate an info message string relating to a particular entity,.
+ *
+ * @param persister The persister for the entity
+ * @param id The entity id value
+ * @param identifierType The entity identifier type mapping
+ * @param factory The session factory
+ * @return An info string, in the form [FooBar#1]
+ */
+ public static String infoString(
+ EntityPersister persister,
+ Object id,
+ Type identifierType,
+ SessionFactoryImplementor factory) {
+ StringBuffer s = new StringBuffer();
+ s.append( '[' );
+ if( persister == null ) {
+ s.append( "<null EntityPersister>" );
+ }
+ else {
+ s.append( persister.getEntityName() );
+ }
+ s.append( '#' );
+
+ if ( id == null ) {
+ s.append( "<null>" );
+ }
+ else {
+ s.append( identifierType.toLoggableString( id, factory ) );
+ }
+ s.append( ']' );
+
+ return s.toString();
+ }
+
+ /**
+ * Generate an info message string relating to a series of entities.
+ *
+ * @param persister The persister for the entities
+ * @param ids The entity id values
+ * @param factory The session factory
+ * @return An info string, in the form [FooBar#<1,2,3>]
+ */
+ public static String infoString(
+ EntityPersister persister,
+ Serializable[] ids,
+ SessionFactoryImplementor factory) {
+ StringBuffer s = new StringBuffer();
+ s.append( '[' );
+ if( persister == null ) {
+ s.append( "<null EntityPersister>" );
+ }
+ else {
+ s.append( persister.getEntityName() );
+ s.append( "#<" );
+ for ( int i=0; i<ids.length; i++ ) {
+ s.append( persister.getIdentifierType().toLoggableString( ids[i], factory ) );
+ if ( i < ids.length-1 ) {
+ s.append( ", " );
+ }
+ }
+ s.append( '>' );
+ }
+ s.append( ']' );
+
+ return s.toString();
+
+ }
+
+ /**
+ * Generate an info message string relating to given entity persister.
+ *
+ * @param persister The persister.
+ * @return An info string, in the form [FooBar]
+ */
+ public static String infoString(EntityPersister persister) {
+ StringBuffer s = new StringBuffer();
+ s.append( '[' );
+ if ( persister == null ) {
+ s.append( "<null EntityPersister>" );
+ }
+ else {
+ s.append( persister.getEntityName() );
+ }
+ s.append( ']' );
+ return s.toString();
+ }
+
+ /**
+ * Generate an info message string relating to a given property value
+ * for an entity.
+ *
+ * @param entityName The entity name
+ * @param propertyName The name of the property
+ * @param key The property value.
+ * @return An info string, in the form [Foo.bars#1]
+ */
+ public static String infoString(String entityName, String propertyName, Object key) {
+ StringBuffer s = new StringBuffer()
+ .append( '[' )
+ .append( entityName )
+ .append( '.' )
+ .append( propertyName )
+ .append( '#' );
+
+ if ( key == null ) {
+ s.append( "<null>" );
+ }
+ else {
+ s.append( key );
+ }
+ s.append( ']' );
+ return s.toString();
+ }
+
+
+ // collections ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+ /**
+ * Generate an info message string relating to a series of managed
+ * collections.
+ *
+ * @param persister The persister for the collections
+ * @param ids The id values of the owners
+ * @param factory The session factory
+ * @return An info string, in the form [Foo.bars#<1,2,3>]
+ */
+ public static String collectionInfoString(
+ CollectionPersister persister,
+ Serializable[] ids,
+ SessionFactoryImplementor factory) {
+ StringBuffer s = new StringBuffer();
+ s.append( '[' );
+ if ( persister == null ) {
+ s.append( "<unreferenced>" );
+ }
+ else {
+ s.append( persister.getRole() );
+ s.append( "#<" );
+ for ( int i = 0; i < ids.length; i++ ) {
+ // Need to use the identifier type of the collection owner
+ // since the incoming is value is actually the owner's id.
+ // Using the collection's key type causes problems with
+ // property-ref keys...
+ s.append( persister.getOwnerEntityPersister().getIdentifierType().toLoggableString( ids[i], factory ) );
+ if ( i < ids.length-1 ) {
+ s.append( ", " );
+ }
+ }
+ s.append( '>' );
+ }
+ s.append( ']' );
+ return s.toString();
+ }
+
+ /**
+ * Generate an info message string relating to a particular managed
+ * collection.
+ *
+ * @param persister The persister for the collection
+ * @param id The id value of the owner
+ * @param factory The session factory
+ * @return An info string, in the form [Foo.bars#1]
+ */
+ public static String collectionInfoString(
+ CollectionPersister persister,
+ Serializable id,
+ SessionFactoryImplementor factory) {
+ StringBuffer s = new StringBuffer();
+ s.append( '[' );
+ if ( persister == null ) {
+ s.append( "<unreferenced>" );
+ }
+ else {
+ s.append( persister.getRole() );
+ s.append( '#' );
+
+ if ( id == null ) {
+ s.append( "<null>" );
+ }
+ else {
+ // Need to use the identifier type of the collection owner
+ // since the incoming is value is actually the owner's id.
+ // Using the collection's key type causes problems with
+ // property-ref keys...
+ s.append( persister.getOwnerEntityPersister().getIdentifierType().toLoggableString( id, factory ) );
+ }
+ }
+ s.append( ']' );
+
+ return s.toString();
+ }
+
+ /**
+ * Generate an info message string relating to a particular managed
+ * collection.
+ *
+ * @param role The role-name of the collection
+ * @param id The id value of the owner
+ * @return An info string, in the form [Foo.bars#1]
+ */
+ public static String collectionInfoString(String role, Serializable id) {
+ StringBuffer s = new StringBuffer();
+ s.append( '[' );
+ if( role == null ) {
+ s.append( "<unreferenced>" );
+ }
+ else {
+ s.append( role );
+ s.append( '#' );
+
+ if ( id == null ) {
+ s.append( "<null>" );
+ }
+ else {
+ s.append( id );
+ }
+ }
+ s.append( ']' );
+ return s.toString();
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/pretty/Printer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/pretty/Printer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/pretty/Printer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,98 @@
+//$Id: Printer.java 5785 2005-02-19 12:58:24Z oneovthafew $
+package org.hibernate.pretty;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.HibernateException;
+import org.hibernate.EntityMode;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.TypedValue;
+import org.hibernate.intercept.LazyPropertyInitializer;
+import org.hibernate.metadata.ClassMetadata;
+import org.hibernate.type.Type;
+
+/**
+ * Renders entities to a nicely readable string.
+ * @author Gavin King
+ */
+public final class Printer {
+
+ private SessionFactoryImplementor factory;
+ private static final Log log = LogFactory.getLog(Printer.class);
+
+ /**
+ * @param entity an actual entity object, not a proxy!
+ */
+ public String toString(Object entity, EntityMode entityMode) throws HibernateException {
+
+ // todo : this call will not work for anything other than pojos!
+ ClassMetadata cm = factory.getClassMetadata( entity.getClass() );
+
+ if ( cm==null ) return entity.getClass().getName();
+
+ Map result = new HashMap();
+
+ if ( cm.hasIdentifierProperty() ) {
+ result.put(
+ cm.getIdentifierPropertyName(),
+ cm.getIdentifierType().toLoggableString( cm.getIdentifier( entity, entityMode ), factory )
+ );
+ }
+
+ Type[] types = cm.getPropertyTypes();
+ String[] names = cm.getPropertyNames();
+ Object[] values = cm.getPropertyValues( entity, entityMode );
+ for ( int i=0; i<types.length; i++ ) {
+ if ( !names[i].startsWith("_") ) {
+ String strValue = values[i]==LazyPropertyInitializer.UNFETCHED_PROPERTY ?
+ values[i].toString() :
+ types[i].toLoggableString( values[i], factory );
+ result.put( names[i], strValue );
+ }
+ }
+ return cm.getEntityName() + result.toString();
+ }
+
+ public String toString(Type[] types, Object[] values) throws HibernateException {
+ List list = new ArrayList( types.length * 5 );
+ for ( int i=0; i<types.length; i++ ) {
+ if ( types[i]!=null ) list.add( types[i].toLoggableString( values[i], factory ) );
+ }
+ return list.toString();
+ }
+
+ public String toString(Map namedTypedValues) throws HibernateException {
+ Map result = new HashMap();
+ Iterator iter = namedTypedValues.entrySet().iterator();
+ while ( iter.hasNext() ) {
+ Map.Entry me = (Map.Entry) iter.next();
+ TypedValue tv = (TypedValue) me.getValue();
+ result.put( me.getKey(), tv.getType().toLoggableString( tv.getValue(), factory ) );
+ }
+ return result.toString();
+ }
+
+ public void toString(Iterator iter, EntityMode entityMode) throws HibernateException {
+ if ( !log.isDebugEnabled() || !iter.hasNext() ) return;
+ log.debug("listing entities:");
+ int i=0;
+ while ( iter.hasNext() ) {
+ if (i++>20) {
+ log.debug("more......");
+ break;
+ }
+ log.debug( toString( iter.next(), entityMode ) );
+ }
+ }
+
+ public Printer(SessionFactoryImplementor factory) {
+ this.factory = factory;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/pretty/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/pretty/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/pretty/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,9 @@
+<html>
+<head></head>
+<body>
+<p>
+ Classes for pretty printing things for exception
+ and log messages.
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/pretty/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/property/BackrefPropertyAccessor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/property/BackrefPropertyAccessor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/property/BackrefPropertyAccessor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,105 @@
+//$Id: BackrefPropertyAccessor.java 7516 2005-07-16 22:20:48Z oneovthafew $
+package org.hibernate.property;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.io.Serializable;
+
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.SessionFactoryImplementor;
+
+/**
+ * Represents a "back-reference" to the id of a collection owner.
+ *
+ * @author Gavin King
+ */
+public class BackrefPropertyAccessor implements PropertyAccessor {
+
+ private final String propertyName;
+ private final String entityName;
+
+ /**
+ * A placeholder for a property value, indicating that
+ * we don't know the value of the back reference
+ */
+ public static final Serializable UNKNOWN = new Serializable() {
+ public String toString() { return "<unknown>"; }
+ public Object readResolve() {
+ return UNKNOWN;
+ }
+ };
+
+ /**
+ * Constructs a new instance of BackrefPropertyAccessor.
+ *
+ * @param collectionRole The collection role which this back ref references.
+ */
+ public BackrefPropertyAccessor(String collectionRole, String entityName) {
+ this.propertyName = collectionRole.substring( entityName.length() + 1 );
+ this.entityName = entityName;
+ }
+
+ public Setter getSetter(Class theClass, String propertyName) {
+ return new BackrefSetter();
+ }
+
+ public Getter getGetter(Class theClass, String propertyName) {
+ return new BackrefGetter();
+ }
+
+
+ /**
+ * The Setter implementation for id backrefs.
+ */
+ public static final class BackrefSetter implements Setter {
+
+ public Method getMethod() {
+ return null;
+ }
+
+ public String getMethodName() {
+ return null;
+ }
+
+ public void set(Object target, Object value, SessionFactoryImplementor factory) {
+ // this page intentionally left blank :)
+ }
+
+ }
+
+
+ /**
+ * The Getter implementation for id backrefs.
+ */
+ public class BackrefGetter implements Getter {
+
+ public Object getForInsert(Object target, Map mergeMap, SessionImplementor session)
+ throws HibernateException {
+ if (session==null) {
+ return UNKNOWN;
+ }
+ else {
+ return session.getPersistenceContext()
+ .getOwnerId( entityName, propertyName, target, mergeMap );
+ }
+ }
+
+ public Object get(Object target) {
+ return UNKNOWN;
+ }
+
+ public Method getMethod() {
+ return null;
+ }
+
+ public String getMethodName() {
+ return null;
+ }
+
+ public Class getReturnType() {
+ return Object.class;
+ }
+ }
+}
+
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/property/BackrefPropertyAccessor.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/property/BasicPropertyAccessor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/property/BasicPropertyAccessor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/property/BasicPropertyAccessor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,348 @@
+//$Id: BasicPropertyAccessor.java 7516 2005-07-16 22:20:48Z oneovthafew $
+package org.hibernate.property;
+
+import java.beans.Introspector;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.hibernate.HibernateException;
+import org.hibernate.PropertyAccessException;
+import org.hibernate.PropertyNotFoundException;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.util.ReflectHelper;
+
+/**
+ * Accesses property values via a get/set pair, which may be nonpublic.
+ * The default (and recommended strategy).
+ * @author Gavin King
+ */
+public class BasicPropertyAccessor implements PropertyAccessor {
+
+ private static final Log log = LogFactory.getLog(BasicPropertyAccessor.class);
+
+ public static final class BasicSetter implements Setter {
+ private Class clazz;
+ private final transient Method method;
+ private final String propertyName;
+
+ private BasicSetter(Class clazz, Method method, String propertyName) {
+ this.clazz=clazz;
+ this.method=method;
+ this.propertyName=propertyName;
+ }
+
+ public void set(Object target, Object value, SessionFactoryImplementor factory)
+ throws HibernateException {
+ try {
+ method.invoke( target, new Object[] { value } );
+ }
+ catch (NullPointerException npe) {
+ if ( value==null && method.getParameterTypes()[0].isPrimitive() ) {
+ throw new PropertyAccessException(
+ npe,
+ "Null value was assigned to a property of primitive type",
+ true,
+ clazz,
+ propertyName
+ );
+ }
+ else {
+ throw new PropertyAccessException(
+ npe,
+ "NullPointerException occurred while calling",
+ true,
+ clazz,
+ propertyName
+ );
+ }
+ }
+ catch (InvocationTargetException ite) {
+ throw new PropertyAccessException(
+ ite,
+ "Exception occurred inside",
+ true,
+ clazz,
+ propertyName
+ );
+ }
+ catch (IllegalAccessException iae) {
+ throw new PropertyAccessException(
+ iae,
+ "IllegalAccessException occurred while calling",
+ true,
+ clazz,
+ propertyName
+ );
+ //cannot occur
+ }
+ catch (IllegalArgumentException iae) {
+ if ( value==null && method.getParameterTypes()[0].isPrimitive() ) {
+ throw new PropertyAccessException(
+ iae,
+ "Null value was assigned to a property of primitive type",
+ true,
+ clazz,
+ propertyName
+ );
+ }
+ else {
+ log.error(
+ "IllegalArgumentException in class: " + clazz.getName() +
+ ", setter method of property: " + propertyName
+ );
+ log.error(
+ "expected type: " +
+ method.getParameterTypes()[0].getName() +
+ ", actual value: " +
+ ( value==null ? null : value.getClass().getName() )
+ );
+ throw new PropertyAccessException(
+ iae,
+ "IllegalArgumentException occurred while calling",
+ true,
+ clazz,
+ propertyName
+ );
+ }
+ }
+ }
+
+ public Method getMethod() {
+ return method;
+ }
+
+ public String getMethodName() {
+ return method.getName();
+ }
+
+ Object readResolve() {
+ return createSetter(clazz, propertyName);
+ }
+
+ public String toString() {
+ return "BasicSetter(" + clazz.getName() + '.' + propertyName + ')';
+ }
+ }
+
+ public static final class BasicGetter implements Getter {
+ private Class clazz;
+ private final transient Method method;
+ private final String propertyName;
+
+ private BasicGetter(Class clazz, Method method, String propertyName) {
+ this.clazz=clazz;
+ this.method=method;
+ this.propertyName=propertyName;
+ }
+
+ public Object get(Object target) throws HibernateException {
+ try {
+ return method.invoke(target, null);
+ }
+ catch (InvocationTargetException ite) {
+ throw new PropertyAccessException(
+ ite,
+ "Exception occurred inside",
+ false,
+ clazz,
+ propertyName
+ );
+ }
+ catch (IllegalAccessException iae) {
+ throw new PropertyAccessException(
+ iae,
+ "IllegalAccessException occurred while calling",
+ false,
+ clazz,
+ propertyName
+ );
+ //cannot occur
+ }
+ catch (IllegalArgumentException iae) {
+ log.error(
+ "IllegalArgumentException in class: " + clazz.getName() +
+ ", getter method of property: " + propertyName
+ );
+ throw new PropertyAccessException(
+ iae,
+ "IllegalArgumentException occurred calling",
+ false,
+ clazz,
+ propertyName
+ );
+ }
+ }
+
+ public Object getForInsert(Object target, Map mergeMap, SessionImplementor session) {
+ return get( target );
+ }
+
+ public Class getReturnType() {
+ return method.getReturnType();
+ }
+
+ public Method getMethod() {
+ return method;
+ }
+
+ public String getMethodName() {
+ return method.getName();
+ }
+
+ public String toString() {
+ return "BasicGetter(" + clazz.getName() + '.' + propertyName + ')';
+ }
+
+ Object readResolve() {
+ return createGetter(clazz, propertyName);
+ }
+ }
+
+
+ public Setter getSetter(Class theClass, String propertyName)
+ throws PropertyNotFoundException {
+ return createSetter(theClass, propertyName);
+ }
+
+ private static Setter createSetter(Class theClass, String propertyName)
+ throws PropertyNotFoundException {
+ BasicSetter result = getSetterOrNull(theClass, propertyName);
+ if (result==null) {
+ throw new PropertyNotFoundException(
+ "Could not find a setter for property " +
+ propertyName +
+ " in class " +
+ theClass.getName()
+ );
+ }
+ return result;
+ }
+
+ private static BasicSetter getSetterOrNull(Class theClass, String propertyName) {
+
+ if (theClass==Object.class || theClass==null) return null;
+
+ Method method = setterMethod(theClass, propertyName);
+
+ if (method!=null) {
+ if ( !ReflectHelper.isPublic(theClass, method) ) method.setAccessible(true);
+ return new BasicSetter(theClass, method, propertyName);
+ }
+ else {
+ BasicSetter setter = getSetterOrNull( theClass.getSuperclass(), propertyName );
+ if (setter==null) {
+ Class[] interfaces = theClass.getInterfaces();
+ for ( int i=0; setter==null && i<interfaces.length; i++ ) {
+ setter=getSetterOrNull( interfaces[i], propertyName );
+ }
+ }
+ return setter;
+ }
+
+ }
+
+ private static Method setterMethod(Class theClass, String propertyName) {
+
+ BasicGetter getter = getGetterOrNull(theClass, propertyName);
+ Class returnType = (getter==null) ? null : getter.getReturnType();
+
+ Method[] methods = theClass.getDeclaredMethods();
+ Method potentialSetter = null;
+ for (int i=0; i<methods.length; i++) {
+ String methodName = methods[i].getName();
+
+ if ( methods[i].getParameterTypes().length==1 && methodName.startsWith("set") ) {
+ String testStdMethod = Introspector.decapitalize( methodName.substring(3) );
+ String testOldMethod = methodName.substring(3);
+ if ( testStdMethod.equals(propertyName) || testOldMethod.equals(propertyName) ) {
+ potentialSetter = methods[i];
+ if ( returnType==null || methods[i].getParameterTypes()[0].equals(returnType) ) {
+ return potentialSetter;
+ }
+ }
+ }
+ }
+ return potentialSetter;
+ }
+
+ public Getter getGetter(Class theClass, String propertyName)
+ throws PropertyNotFoundException {
+ return createGetter(theClass, propertyName);
+ }
+
+ public static Getter createGetter(Class theClass, String propertyName)
+ throws PropertyNotFoundException {
+ BasicGetter result = getGetterOrNull(theClass, propertyName);
+ if (result==null) {
+ throw new PropertyNotFoundException(
+ "Could not find a getter for " +
+ propertyName +
+ " in class " +
+ theClass.getName()
+ );
+ }
+ return result;
+
+ }
+
+ private static BasicGetter getGetterOrNull(Class theClass, String propertyName) {
+
+ if (theClass==Object.class || theClass==null) return null;
+
+ Method method = getterMethod(theClass, propertyName);
+
+ if (method!=null) {
+ if ( !ReflectHelper.isPublic(theClass, method) ) method.setAccessible(true);
+ return new BasicGetter(theClass, method, propertyName);
+ }
+ else {
+ BasicGetter getter = getGetterOrNull( theClass.getSuperclass(), propertyName );
+ if (getter==null) {
+ Class[] interfaces = theClass.getInterfaces();
+ for ( int i=0; getter==null && i<interfaces.length; i++ ) {
+ getter=getGetterOrNull( interfaces[i], propertyName );
+ }
+ }
+ return getter;
+ }
+ }
+
+ private static Method getterMethod(Class theClass, String propertyName) {
+
+ Method[] methods = theClass.getDeclaredMethods();
+ for (int i=0; i<methods.length; i++) {
+ // only carry on if the method has no parameters
+ if ( methods[i].getParameterTypes().length==0 ) {
+ String methodName = methods[i].getName();
+
+ // try "get"
+ if ( methodName.startsWith("get") ) {
+ String testStdMethod = Introspector.decapitalize( methodName.substring(3) );
+ String testOldMethod = methodName.substring(3);
+ if ( testStdMethod.equals(propertyName) || testOldMethod.equals(propertyName) ) {
+ return methods[i];
+ }
+
+ }
+
+ // if not "get" then try "is"
+ /*boolean isBoolean = methods[i].getReturnType().equals(Boolean.class) ||
+ methods[i].getReturnType().equals(boolean.class);*/
+ if ( methodName.startsWith("is") ) {
+ String testStdMethod = Introspector.decapitalize( methodName.substring(2) );
+ String testOldMethod = methodName.substring(2);
+ if ( testStdMethod.equals(propertyName) || testOldMethod.equals(propertyName) ) {
+ return methods[i];
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/property/ChainedPropertyAccessor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/property/ChainedPropertyAccessor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/property/ChainedPropertyAccessor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,47 @@
+package org.hibernate.property;
+
+import org.hibernate.PropertyNotFoundException;
+
+/**
+ * @author max
+ *
+ */
+public class ChainedPropertyAccessor implements PropertyAccessor {
+
+ final PropertyAccessor[] chain;
+
+ public ChainedPropertyAccessor(PropertyAccessor[] chain) {
+ this.chain = chain;
+ }
+
+ public Getter getGetter(Class theClass, String propertyName)
+ throws PropertyNotFoundException {
+ Getter result = null;
+ for (int i = 0; i < chain.length; i++) {
+ PropertyAccessor candidate = chain[i];
+ try {
+ result = candidate.getGetter(theClass, propertyName);
+ return result;
+ } catch (PropertyNotFoundException pnfe) {
+ // ignore
+ }
+ }
+ throw new PropertyNotFoundException("Could not find getter for " + propertyName + " on " + theClass);
+ }
+
+ public Setter getSetter(Class theClass, String propertyName)
+ throws PropertyNotFoundException {
+ Setter result = null;
+ for (int i = 0; i < chain.length; i++) {
+ PropertyAccessor candidate = chain[i];
+ try {
+ result = candidate.getSetter(theClass, propertyName);
+ return result;
+ } catch (PropertyNotFoundException pnfe) {
+ //
+ }
+ }
+ throw new PropertyNotFoundException("Could not find setter for " + propertyName + " on " + theClass);
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/property/DirectPropertyAccessor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/property/DirectPropertyAccessor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/property/DirectPropertyAccessor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,145 @@
+//$Id: DirectPropertyAccessor.java 11412 2007-04-17 14:38:51Z max.andersen at jboss.com $
+package org.hibernate.property;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Map;
+
+import org.hibernate.HibernateException;
+import org.hibernate.PropertyAccessException;
+import org.hibernate.PropertyNotFoundException;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.util.ReflectHelper;
+
+/**
+ * Accesses fields directly.
+ * @author Gavin King
+ */
+public class DirectPropertyAccessor implements PropertyAccessor {
+
+ public static final class DirectGetter implements Getter {
+ private final transient Field field;
+ private final Class clazz;
+ private final String name;
+ DirectGetter(Field field, Class clazz, String name) {
+ this.field = field;
+ this.clazz = clazz;
+ this.name = name;
+ }
+ public Object get(Object target) throws HibernateException {
+ try {
+ return field.get(target);
+ }
+ catch (Exception e) {
+ throw new PropertyAccessException(e, "could not get a field value by reflection", false, clazz, name);
+ }
+ }
+
+ public Object getForInsert(Object target, Map mergeMap, SessionImplementor session) {
+ return get( target );
+ }
+
+ public Method getMethod() {
+ return null;
+ }
+ public String getMethodName() {
+ return null;
+ }
+ public Class getReturnType() {
+ return field.getType();
+ }
+
+ Object readResolve() {
+ return new DirectGetter( getField(clazz, name), clazz, name );
+ }
+
+ public String toString() {
+ return "DirectGetter(" + clazz.getName() + '.' + name + ')';
+ }
+ }
+
+ public static final class DirectSetter implements Setter {
+ private final transient Field field;
+ private final Class clazz;
+ private final String name;
+ DirectSetter(Field field, Class clazz, String name) {
+ this.field = field;
+ this.clazz = clazz;
+ this.name = name;
+ }
+ public Method getMethod() {
+ return null;
+ }
+ public String getMethodName() {
+ return null;
+ }
+ public void set(Object target, Object value, SessionFactoryImplementor factory) throws HibernateException {
+ try {
+ field.set(target, value);
+ }
+ catch (Exception e) {
+ if(value == null && field.getType().isPrimitive()) {
+ throw new PropertyAccessException(
+ e,
+ "Null value was assigned to a property of primitive type",
+ true,
+ clazz,
+ name
+ );
+ } else {
+ throw new PropertyAccessException(e, "could not set a field value by reflection", true, clazz, name);
+ }
+ }
+ }
+
+ public String toString() {
+ return "DirectSetter(" + clazz.getName() + '.' + name + ')';
+ }
+
+ Object readResolve() {
+ return new DirectSetter( getField(clazz, name), clazz, name );
+ }
+ }
+
+ private static Field getField(Class clazz, String name) throws PropertyNotFoundException {
+ if ( clazz==null || clazz==Object.class ) {
+ throw new PropertyNotFoundException("field not found: " + name);
+ }
+ Field field;
+ try {
+ field = clazz.getDeclaredField(name);
+ }
+ catch (NoSuchFieldException nsfe) {
+ field = getField( clazz, clazz.getSuperclass(), name );
+ }
+ if ( !ReflectHelper.isPublic(clazz, field) ) field.setAccessible(true);
+ return field;
+ }
+
+ private static Field getField(Class root, Class clazz, String name) throws PropertyNotFoundException {
+ if ( clazz==null || clazz==Object.class ) {
+ throw new PropertyNotFoundException("field [" + name + "] not found on " + root.getName());
+ }
+ Field field;
+ try {
+ field = clazz.getDeclaredField(name);
+ }
+ catch (NoSuchFieldException nsfe) {
+ field = getField( root, clazz.getSuperclass(), name );
+ }
+ if ( !ReflectHelper.isPublic(clazz, field) ) field.setAccessible(true);
+ return field;
+ }
+
+ public Getter getGetter(Class theClass, String propertyName)
+ throws PropertyNotFoundException {
+ return new DirectGetter( getField(theClass, propertyName), theClass, propertyName );
+ }
+
+ public Setter getSetter(Class theClass, String propertyName)
+ throws PropertyNotFoundException {
+ return new DirectSetter( getField(theClass, propertyName), theClass, propertyName );
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/property/Dom4jAccessor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/property/Dom4jAccessor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/property/Dom4jAccessor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,359 @@
+// $Id: Dom4jAccessor.java 8063 2005-09-01 18:49:39Z oneovthafew $
+package org.hibernate.property;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+
+import org.dom4j.Attribute;
+import org.dom4j.Element;
+import org.dom4j.Node;
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.PropertyNotFoundException;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.type.CollectionType;
+import org.hibernate.type.Type;
+
+/**
+ * Responsible for accessing property values represented as a dom4j Element
+ * or Attribute.
+ *
+ * @author Steve Ebersole
+ */
+public class Dom4jAccessor implements PropertyAccessor {
+ private String nodeName;
+ private Type propertyType;
+ private final SessionFactoryImplementor factory;
+
+ public Dom4jAccessor(String nodeName, Type propertyType, SessionFactoryImplementor factory) {
+ this.factory = factory;
+ this.nodeName = nodeName;
+ this.propertyType = propertyType;
+
+ }
+
+ /**
+ * Create a "getter" for the named attribute
+ */
+ public Getter getGetter(Class theClass, String propertyName)
+ throws PropertyNotFoundException {
+ if (nodeName==null) {
+ throw new MappingException("no node name for property: " + propertyName);
+ }
+ if ( ".".equals(nodeName) ) {
+ return new TextGetter(propertyType, factory);
+ }
+ else if ( nodeName.indexOf('/')>-1 ) {
+ return new ElementAttributeGetter(nodeName, propertyType, factory);
+ }
+ else if ( nodeName.indexOf('@')>-1 ) {
+ return new AttributeGetter(nodeName, propertyType, factory);
+ }
+ else {
+ return new ElementGetter(nodeName, propertyType, factory);
+ }
+ }
+
+ /**
+ * Create a "setter" for the named attribute
+ */
+ public Setter getSetter(Class theClass, String propertyName)
+ throws PropertyNotFoundException {
+ if (nodeName==null) {
+ throw new MappingException("no node name for property: " + propertyName);
+ }
+ if ( ".".equals(nodeName) ) {
+ return new TextSetter(propertyType);
+ }
+ else if ( nodeName.indexOf('/')>-1 ) {
+ return new ElementAttributeSetter(nodeName, propertyType);
+ }
+ else if ( nodeName.indexOf('@')>-1 ) {
+ return new AttributeSetter(nodeName, propertyType);
+ }
+ else {
+ return new ElementSetter(nodeName, propertyType);
+ }
+ }
+
+ /**
+ * Defines the strategy for getting property values out of a dom4j Node.
+ */
+ public abstract static class Dom4jGetter implements Getter {
+ protected final Type propertyType;
+ protected final SessionFactoryImplementor factory;
+
+ Dom4jGetter(Type propertyType, SessionFactoryImplementor factory) {
+ this.propertyType = propertyType;
+ this.factory = factory;
+ }
+
+ public Object getForInsert(Object owner, Map mergeMap, SessionImplementor session)
+ throws HibernateException {
+ return get( owner );
+ }
+
+ /**
+ * Get the declared Java type
+ */
+ public Class getReturnType() {
+ return Object.class;
+ }
+
+ /**
+ * Optional operation (return null)
+ */
+ public String getMethodName() {
+ return null;
+ }
+
+ /**
+ * Optional operation (return null)
+ */
+ public Method getMethod() {
+ return null;
+ }
+ }
+
+ public abstract static class Dom4jSetter implements Setter {
+ protected final Type propertyType;
+
+ Dom4jSetter(Type propertyType) {
+ this.propertyType = propertyType;
+ }
+
+ /**
+ * Optional operation (return null)
+ */
+ public String getMethodName() {
+ return null;
+ }
+
+ /**
+ * Optional operation (return null)
+ */
+ public Method getMethod() {
+ return null;
+ }
+ }
+
+ /**
+ * For nodes like <tt>"."</tt>
+ * @author Gavin King
+ */
+ public static class TextGetter extends Dom4jGetter {
+
+ TextGetter(Type propertyType, SessionFactoryImplementor factory) {
+ super(propertyType, factory);
+ }
+
+ public Object get(Object owner) throws HibernateException {
+ Element ownerElement = (Element) owner;
+ return super.propertyType.fromXMLNode(ownerElement, super.factory);
+ }
+
+ }
+
+ /**
+ * For nodes like <tt>"@bar"</tt>
+ * @author Gavin King
+ */
+ public static class AttributeGetter extends Dom4jGetter {
+ private final String attributeName;
+
+ AttributeGetter(String name, Type propertyType, SessionFactoryImplementor factory) {
+ super(propertyType, factory);
+ attributeName = name.substring(1);
+ }
+
+ public Object get(Object owner) throws HibernateException {
+ Element ownerElement = (Element) owner;
+ Node attribute = ownerElement.attribute(attributeName);
+ return attribute==null ? null :
+ super.propertyType.fromXMLNode(attribute, super.factory);
+ }
+
+ }
+
+ /**
+ * For nodes like <tt>"foo"</tt>
+ * @author Gavin King
+ */
+ public static class ElementGetter extends Dom4jGetter {
+ private final String elementName;
+
+ ElementGetter(String name, Type propertyType, SessionFactoryImplementor factory) {
+ super(propertyType, factory);
+ elementName = name;
+ }
+
+ public Object get(Object owner) throws HibernateException {
+ Element ownerElement = (Element) owner;
+ Node element = ownerElement.element(elementName);
+ return element==null ?
+ null : super.propertyType.fromXMLNode(element, super.factory);
+ }
+
+ }
+
+ /**
+ * For nodes like <tt>"foo/@bar"</tt>
+ * @author Gavin King
+ */
+ public static class ElementAttributeGetter extends Dom4jGetter {
+ private final String elementName;
+ private final String attributeName;
+
+ ElementAttributeGetter(String name, Type propertyType, SessionFactoryImplementor factory) {
+ super(propertyType, factory);
+ elementName = name.substring( 0, name.indexOf('/') );
+ attributeName = name.substring( name.indexOf('/')+2 );
+ }
+
+ public Object get(Object owner) throws HibernateException {
+ Element ownerElement = (Element) owner;
+
+ Element element = ownerElement.element(elementName);
+
+ if ( element==null ) {
+ return null;
+ }
+ else {
+ Attribute attribute = element.attribute(attributeName);
+ if (attribute==null) {
+ return null;
+ }
+ else {
+ return super.propertyType.fromXMLNode(attribute, super.factory);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * For nodes like <tt>"."</tt>
+ * @author Gavin King
+ */
+ public static class TextSetter extends Dom4jSetter {
+
+ TextSetter(Type propertyType) {
+ super(propertyType);
+ }
+
+ public void set(Object target, Object value, SessionFactoryImplementor factory)
+ throws HibernateException {
+ Element owner = ( Element ) target;
+ if ( !super.propertyType.isXMLElement() ) { //kinda ugly, but needed for collections with a "." node mapping
+ if (value==null) {
+ owner.setText(null); //is this ok?
+ }
+ else {
+ super.propertyType.setToXMLNode(owner, value, factory);
+ }
+ }
+ }
+
+ }
+
+ /**
+ * For nodes like <tt>"@bar"</tt>
+ * @author Gavin King
+ */
+ public static class AttributeSetter extends Dom4jSetter {
+ private final String attributeName;
+
+ AttributeSetter(String name, Type propertyType) {
+ super(propertyType);
+ attributeName = name.substring(1);
+ }
+
+ public void set(Object target, Object value, SessionFactoryImplementor factory)
+ throws HibernateException {
+ Element owner = ( Element ) target;
+ Attribute attribute = owner.attribute(attributeName);
+ if (value==null) {
+ if (attribute!=null) attribute.detach();
+ }
+ else {
+ if (attribute==null) {
+ owner.addAttribute(attributeName, "null");
+ attribute = owner.attribute(attributeName);
+ }
+ super.propertyType.setToXMLNode(attribute, value, factory);
+ }
+ }
+
+ }
+
+ /**
+ * For nodes like <tt>"foo"</tt>
+ * @author Gavin King
+ */
+ public static class ElementSetter extends Dom4jSetter {
+ private final String elementName;
+
+ ElementSetter(String name, Type propertyType) {
+ super(propertyType);
+ elementName = name;
+ }
+
+ public void set(Object target, Object value, SessionFactoryImplementor factory)
+ throws HibernateException {
+ if (value!=CollectionType.UNFETCHED_COLLECTION) {
+ Element owner = ( Element ) target;
+ Element existing = owner.element(elementName);
+ if (existing!=null) existing.detach();
+ if (value!=null) {
+ Element element = owner.addElement(elementName);
+ super.propertyType.setToXMLNode(element, value, factory);
+ }
+ }
+ }
+
+ }
+
+ /**
+ * For nodes like <tt>"foo/@bar"</tt>
+ * @author Gavin King
+ */
+ public static class ElementAttributeSetter extends Dom4jSetter {
+ private final String elementName;
+ private final String attributeName;
+
+ ElementAttributeSetter(String name, Type propertyType) {
+ super(propertyType);
+ elementName = name.substring( 0, name.indexOf('/') );
+ attributeName = name.substring( name.indexOf('/')+2 );
+ }
+
+ public void set(Object target, Object value, SessionFactoryImplementor factory)
+ throws HibernateException {
+ Element owner = ( Element ) target;
+ Element element = owner.element(elementName);
+ if (value==null) {
+ if (element!=null) element.detach();
+ }
+ else {
+ Attribute attribute;
+ if (element==null) {
+ element = owner.addElement(elementName);
+ attribute = null;
+ }
+ else {
+ attribute = element.attribute(attributeName);
+ }
+
+ if (attribute==null) {
+ element.addAttribute(attributeName, "null");
+ attribute = element.attribute(attributeName);
+ }
+ super.propertyType.setToXMLNode(attribute, value, factory);
+ }
+ }
+
+ }
+
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/property/EmbeddedPropertyAccessor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/property/EmbeddedPropertyAccessor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/property/EmbeddedPropertyAccessor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,83 @@
+//$Id: EmbeddedPropertyAccessor.java 7516 2005-07-16 22:20:48Z oneovthafew $
+package org.hibernate.property;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+
+import org.hibernate.HibernateException;
+import org.hibernate.PropertyNotFoundException;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.SessionFactoryImplementor;
+
+/**
+ * @author Gavin King
+ */
+public class EmbeddedPropertyAccessor implements PropertyAccessor {
+
+ public static final class EmbeddedGetter implements Getter {
+
+ private final Class clazz;
+
+ EmbeddedGetter(Class clazz) {
+ this.clazz = clazz;
+ }
+
+ public Object get(Object target) throws HibernateException {
+ return target;
+ }
+
+ public Object getForInsert(Object target, Map mergeMap, SessionImplementor session) {
+ return get( target );
+ }
+
+ public Method getMethod() {
+ return null;
+ }
+
+ public String getMethodName() {
+ return null;
+ }
+
+ public Class getReturnType() {
+ return clazz;
+ }
+
+ public String toString() {
+ return "EmbeddedGetter(" + clazz.getName() + ')';
+ }
+ }
+
+ public static final class EmbeddedSetter implements Setter {
+
+ private final Class clazz;
+
+ EmbeddedSetter(Class clazz) {
+ this.clazz = clazz;
+ }
+
+ public Method getMethod() {
+ return null;
+ }
+
+ public String getMethodName() {
+ return null;
+ }
+
+ public void set(Object target, Object value, SessionFactoryImplementor factory) throws HibernateException {}
+
+ public String toString() {
+ return "EmbeddedSetter(" + clazz.getName() + ')';
+ }
+ }
+
+ public Getter getGetter(Class theClass, String propertyName)
+ throws PropertyNotFoundException {
+ return new EmbeddedGetter(theClass);
+ }
+
+ public Setter getSetter(Class theClass, String propertyName)
+ throws PropertyNotFoundException {
+ return new EmbeddedSetter(theClass);
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/property/EmbeddedPropertyAccessor.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/property/Getter.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/property/Getter.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/property/Getter.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,51 @@
+//$Id: Getter.java 7516 2005-07-16 22:20:48Z oneovthafew $
+package org.hibernate.property;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.Map;
+
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionImplementor;
+
+/**
+ * Gets values of a particular property
+ *
+ * @author Gavin King
+ */
+public interface Getter extends Serializable {
+ /**
+ * Get the property value from the given instance .
+ * @param owner The instance containing the value to be retreived.
+ * @return The extracted value.
+ * @throws HibernateException
+ */
+ public Object get(Object owner) throws HibernateException;
+
+ /**
+ * Get the property value from the given owner instance.
+ *
+ * @param owner The instance containing the value to be retreived.
+ * @param mergeMap a map of merged persistent instances to detached instances
+ * @param session The session from which this request originated.
+ * @return The extracted value.
+ * @throws HibernateException
+ */
+ public Object getForInsert(Object owner, Map mergeMap, SessionImplementor session)
+ throws HibernateException;
+
+ /**
+ * Get the declared Java type
+ */
+ public Class getReturnType();
+
+ /**
+ * Optional operation (return null)
+ */
+ public String getMethodName();
+
+ /**
+ * Optional operation (return null)
+ */
+ public Method getMethod();
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/property/IndexPropertyAccessor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/property/IndexPropertyAccessor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/property/IndexPropertyAccessor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,95 @@
+//$Id: IndexPropertyAccessor.java 7516 2005-07-16 22:20:48Z oneovthafew $
+package org.hibernate.property;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.SessionFactoryImplementor;
+
+/**
+ * Represents a "back-reference" to the index of a collection.
+ *
+ * @author Gavin King
+ */
+public class IndexPropertyAccessor implements PropertyAccessor {
+
+ private final String propertyName;
+ private final String entityName;
+
+ /**
+ * Constructs a new instance of IndexPropertyAccessor.
+ *
+ * @param collectionRole The collection role which this back ref references.
+ */
+ public IndexPropertyAccessor(String collectionRole, String entityName) {
+ this.propertyName = collectionRole.substring( entityName.length()+1 );
+ this.entityName = entityName;
+ }
+
+ public Setter getSetter(Class theClass, String propertyName) {
+ return new IndexSetter();
+ }
+
+ public Getter getGetter(Class theClass, String propertyName) {
+ return new IndexGetter();
+ }
+
+
+ /**
+ * The Setter implementation for index backrefs.
+ */
+ public static final class IndexSetter implements Setter {
+
+ public Method getMethod() {
+ return null;
+ }
+
+ public String getMethodName() {
+ return null;
+ }
+
+ public void set(Object target, Object value) {
+ // do nothing...
+ }
+
+ public void set(Object target, Object value, SessionFactoryImplementor factory) throws HibernateException {
+ // do nothing...
+ }
+
+ }
+
+
+ /**
+ * The Getter implementation for index backrefs.
+ */
+ public class IndexGetter implements Getter {
+
+ public Object getForInsert(Object target, Map mergeMap, SessionImplementor session) throws HibernateException {
+ if (session==null) {
+ return BackrefPropertyAccessor.UNKNOWN;
+ }
+ else {
+ return session.getPersistenceContext()
+ .getIndexInOwner(entityName, propertyName, target, mergeMap);
+ }
+ }
+
+ public Object get(Object target) {
+ return BackrefPropertyAccessor.UNKNOWN;
+ }
+
+ public Method getMethod() {
+ return null;
+ }
+
+ public String getMethodName() {
+ return null;
+ }
+
+ public Class getReturnType() {
+ return Object.class;
+ }
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/property/IndexPropertyAccessor.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/property/MapAccessor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/property/MapAccessor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/property/MapAccessor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,80 @@
+//$Id: MapAccessor.java 7516 2005-07-16 22:20:48Z oneovthafew $
+package org.hibernate.property;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+
+import org.hibernate.HibernateException;
+import org.hibernate.PropertyNotFoundException;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+
+/**
+ * @author Gavin King
+ */
+public class MapAccessor implements PropertyAccessor {
+
+ public Getter getGetter(Class theClass, String propertyName)
+ throws PropertyNotFoundException {
+ return new MapGetter(propertyName);
+ }
+
+ public Setter getSetter(Class theClass, String propertyName)
+ throws PropertyNotFoundException {
+ return new MapSetter(propertyName);
+ }
+
+ public static final class MapSetter implements Setter {
+
+ private String name;
+
+ MapSetter(String name) {
+ this.name = name;
+ }
+
+ public Method getMethod() {
+ return null;
+ }
+
+ public String getMethodName() {
+ return null;
+ }
+
+ public void set(Object target, Object value, SessionFactoryImplementor factory)
+ throws HibernateException {
+ ( (Map) target ).put(name, value);
+ }
+
+ }
+
+ public static final class MapGetter implements Getter {
+
+ private String name;
+
+ MapGetter(String name) {
+ this.name = name;
+ }
+
+ public Method getMethod() {
+ return null;
+ }
+
+ public String getMethodName() {
+ return null;
+ }
+
+ public Object get(Object target) throws HibernateException {
+ return ( (Map) target ).get(name);
+ }
+
+ public Object getForInsert(Object target, Map mergeMap, SessionImplementor session) {
+ return get( target );
+ }
+
+ public Class getReturnType() {
+ return Object.class;
+ }
+
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/property/NoopAccessor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/property/NoopAccessor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/property/NoopAccessor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,79 @@
+package org.hibernate.property;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+
+import org.hibernate.HibernateException;
+import org.hibernate.PropertyNotFoundException;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.property.Getter;
+import org.hibernate.property.PropertyAccessor;
+import org.hibernate.property.Setter;
+
+/**
+ * Used to declare properties not represented at the pojo level
+ *
+ * @author Michael Bartmann
+ */
+public class NoopAccessor implements PropertyAccessor {
+
+ public Getter getGetter(Class arg0, String arg1) throws PropertyNotFoundException {
+ return new NoopGetter();
+ }
+
+ public Setter getSetter(Class arg0, String arg1) throws PropertyNotFoundException {
+ return new NoopSetter();
+ }
+
+ /**
+ * A Getter which will always return null. It should not be called anyway.
+ */
+ private static class NoopGetter implements Getter {
+
+ /**
+ * @return always null
+ */
+ public Object get(Object target) throws HibernateException {
+ return null;
+ }
+
+ public Object getForInsert(Object target, Map map, SessionImplementor arg1)
+ throws HibernateException {
+ return null;
+ }
+
+ public Class getReturnType() {
+ return Object.class;
+ }
+
+ public String getMethodName() {
+ return null;
+ }
+
+ public Method getMethod() {
+ return null;
+ }
+
+ }
+
+ /**
+ * A Setter which will just do nothing.
+ */
+ private static class NoopSetter implements Setter {
+
+ public void set(Object target, Object value, SessionFactoryImplementor arg2)
+ throws HibernateException {
+ // do not do anything
+ }
+
+ public String getMethodName() {
+ return null;
+ }
+
+ public Method getMethod() {
+ return null;
+ }
+
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/property/NoopAccessor.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/property/PropertyAccessor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/property/PropertyAccessor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/property/PropertyAccessor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,20 @@
+//$Id: PropertyAccessor.java 3890 2004-06-03 16:31:32Z steveebersole $
+package org.hibernate.property;
+
+import org.hibernate.PropertyNotFoundException;
+
+/**
+ * Abstracts the notion of a "property". Defines a strategy for accessing the
+ * value of an attribute.
+ * @author Gavin King
+ */
+public interface PropertyAccessor {
+ /**
+ * Create a "getter" for the named attribute
+ */
+ public Getter getGetter(Class theClass, String propertyName) throws PropertyNotFoundException;
+ /**
+ * Create a "setter" for the named attribute
+ */
+ public Setter getSetter(Class theClass, String propertyName) throws PropertyNotFoundException;
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/property/PropertyAccessorFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/property/PropertyAccessorFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/property/PropertyAccessorFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,135 @@
+//$Id: PropertyAccessorFactory.java 8451 2005-10-24 08:45:37Z maxcsaucdk $
+package org.hibernate.property;
+
+import java.util.Map;
+
+import org.hibernate.MappingException;
+import org.hibernate.EntityMode;
+import org.hibernate.type.Type;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.mapping.Property;
+import org.hibernate.util.ReflectHelper;
+import org.hibernate.util.StringHelper;
+
+/**
+ * A factory for building/retrieving PropertyAccessor instances.
+ *
+ * @author Gavin King
+ * @author Steve Ebersole
+ */
+public final class PropertyAccessorFactory {
+
+ private static final PropertyAccessor BASIC_PROPERTY_ACCESSOR = new BasicPropertyAccessor();
+ private static final PropertyAccessor DIRECT_PROPERTY_ACCESSOR = new DirectPropertyAccessor();
+ private static final PropertyAccessor MAP_ACCESSOR = new MapAccessor();
+ private static final PropertyAccessor NOOP_ACCESSOR = new NoopAccessor();
+ private static final PropertyAccessor EMBEDDED_PROPERTY_ACCESSOR = new EmbeddedPropertyAccessor();
+
+ //TODO: ideally we need the construction of PropertyAccessor to take the following:
+ // 1) EntityMode
+ // 2) EntityMode-specific data (i.e., the classname for pojo entities)
+ // 3) Property-specific data based on the EntityMode (i.e., property-name or dom4j-node-name)
+ // The easiest way, with the introduction of the new runtime-metamodel classes, would be the
+ // the following predicates:
+ // 1) PropertyAccessorFactory.getPropertyAccessor() takes references to both a
+ // org.hibernate.metadata.EntityModeMetadata and org.hibernate.metadata.Property
+ // 2) What is now termed a "PropertyAccessor" stores any values needed from those two
+ // pieces of information
+ // 3) Code can then simply call PropertyAccess.getGetter() with no parameters; likewise with
+ // PropertyAccessor.getSetter()
+
+ /**
+ * Retrieves a PropertyAccessor instance based on the given property definition and
+ * entity mode.
+ *
+ * @param property The property for which to retrieve an accessor.
+ * @param mode The mode for the resulting entity.
+ * @return An appropriate accessor.
+ * @throws MappingException
+ */
+ public static PropertyAccessor getPropertyAccessor(Property property, EntityMode mode) throws MappingException {
+ //TODO: this is temporary in that the end result will probably not take a Property reference per-se.
+ if ( null == mode || EntityMode.POJO.equals( mode ) ) {
+ return getPojoPropertyAccessor( property.getPropertyAccessorName() );
+ }
+ else if ( EntityMode.MAP.equals( mode ) ) {
+ return getDynamicMapPropertyAccessor();
+ }
+ else if ( EntityMode.DOM4J.equals( mode ) ) {
+ //TODO: passing null here, because this method is not really used for DOM4J at the moment
+ // but it is still a bug, if we don't get rid of this!
+ return getDom4jPropertyAccessor( property.getAccessorPropertyName( mode ), property.getType(), null );
+ }
+ else {
+ throw new MappingException( "Unknown entity mode [" + mode + "]" );
+ }
+ } /**
+ * Retreives a PropertyAccessor specific for a PojoRepresentation with the given access strategy.
+ *
+ * @param pojoAccessorStrategy The access strategy.
+ * @return An appropriate accessor.
+ */
+ private static PropertyAccessor getPojoPropertyAccessor(String pojoAccessorStrategy) {
+ if ( StringHelper.isEmpty( pojoAccessorStrategy ) || "property".equals( pojoAccessorStrategy ) ) {
+ return BASIC_PROPERTY_ACCESSOR;
+ }
+ else if ( "field".equals( pojoAccessorStrategy ) ) {
+ return DIRECT_PROPERTY_ACCESSOR;
+ }
+ else if ( "embedded".equals( pojoAccessorStrategy ) ) {
+ return EMBEDDED_PROPERTY_ACCESSOR;
+ }
+ else if ( "noop".equals(pojoAccessorStrategy) ) {
+ return NOOP_ACCESSOR;
+ }
+ else {
+ return resolveCustomAccessor( pojoAccessorStrategy );
+ }
+ }
+
+ public static PropertyAccessor getDynamicMapPropertyAccessor() throws MappingException {
+ return MAP_ACCESSOR;
+ }
+
+ public static PropertyAccessor getDom4jPropertyAccessor(String nodeName, Type type, SessionFactoryImplementor factory)
+ throws MappingException {
+ //TODO: need some caching scheme? really comes down to decision
+ // regarding amount of state (if any) kept on PropertyAccessors
+ return new Dom4jAccessor( nodeName, type, factory );
+ }
+
+ private static PropertyAccessor resolveCustomAccessor(String accessorName) {
+ Class accessorClass;
+ try {
+ accessorClass = ReflectHelper.classForName(accessorName);
+ }
+ catch (ClassNotFoundException cnfe) {
+ throw new MappingException("could not find PropertyAccessor class: " + accessorName, cnfe);
+ }
+ try {
+ return (PropertyAccessor) accessorClass.newInstance();
+ }
+ catch (Exception e) {
+ throw new MappingException("could not instantiate PropertyAccessor class: " + accessorName, e);
+ }
+ }
+
+ private PropertyAccessorFactory() {}
+
+ // todo : this eventually needs to be removed
+ public static PropertyAccessor getPropertyAccessor(Class optionalClass, String type) throws MappingException {
+ if ( type==null ) type = optionalClass==null || optionalClass==Map.class ? "map" : "property";
+ return getPropertyAccessor(type);
+ }
+
+ // todo : this eventually needs to be removed
+ public static PropertyAccessor getPropertyAccessor(String type) throws MappingException {
+ if ( type==null || "property".equals(type) ) return BASIC_PROPERTY_ACCESSOR;
+ if ( "field".equals(type) ) return DIRECT_PROPERTY_ACCESSOR;
+ if ( "map".equals(type) ) return MAP_ACCESSOR;
+ if ( "embedded".equals(type) ) return EMBEDDED_PROPERTY_ACCESSOR;
+ if ( "noop".equals(type)) return NOOP_ACCESSOR;
+
+ return resolveCustomAccessor(type);
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/property/Setter.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/property/Setter.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/property/Setter.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,33 @@
+//$Id: Setter.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate.property;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionFactoryImplementor;
+
+/**
+ * Sets values to a particular property.
+ *
+ * @author Gavin King
+ */
+public interface Setter extends Serializable {
+ /**
+ * Set the property value from the given instance
+ *
+ * @param target The instance upon which to set the given value.
+ * @param value The value to be set on the target.
+ * @param factory The session factory from which this request originated.
+ * @throws HibernateException
+ */
+ public void set(Object target, Object value, SessionFactoryImplementor factory) throws HibernateException;
+ /**
+ * Optional operation (return null)
+ */
+ public String getMethodName();
+ /**
+ * Optional operation (return null)
+ */
+ public Method getMethod();
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/property/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/property/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/property/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,10 @@
+<html>
+<head></head>
+<body>
+<p>
+ This package abstracts the notion of a "property" of
+ an entity. Support for JavaBean properties and
+ <tt>Map</tt> elements is included.
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/property/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/AbstractLazyInitializer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/AbstractLazyInitializer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/AbstractLazyInitializer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,140 @@
+package org.hibernate.proxy;
+
+import java.io.Serializable;
+
+import org.hibernate.HibernateException;
+import org.hibernate.LazyInitializationException;
+import org.hibernate.ObjectNotFoundException;
+import org.hibernate.engine.EntityKey;
+import org.hibernate.engine.SessionImplementor;
+
+/**
+ * Convenience base class for lazy initialization handlers. Centralizes the
+ * basic plumbing of doing lazy initialization freeing subclasses to
+ * acts as essentially adapters to their intended entity mode and/or
+ * proxy generation strategy.
+ *
+ * @author Gavin King
+ */
+public abstract class AbstractLazyInitializer implements LazyInitializer {
+
+ private Object target;
+ private boolean initialized;
+ private String entityName;
+ private Serializable id;
+ private transient SessionImplementor session;
+ private boolean unwrap;
+
+ protected AbstractLazyInitializer(String entityName, Serializable id, SessionImplementor session) {
+ this.id = id;
+ this.session = session;
+ this.entityName = entityName;
+ }
+
+ public final Serializable getIdentifier() {
+ return id;
+ }
+
+ public final void setIdentifier(Serializable id) {
+ this.id = id;
+ }
+
+ public final String getEntityName() {
+ return entityName;
+ }
+
+ public final boolean isUninitialized() {
+ return !initialized;
+ }
+
+ public final SessionImplementor getSession() {
+ return session;
+ }
+
+ public final void initialize() throws HibernateException {
+ if (!initialized) {
+ if ( session==null ) {
+ throw new LazyInitializationException("could not initialize proxy - no Session");
+ }
+ else if ( !session.isOpen() ) {
+ throw new LazyInitializationException("could not initialize proxy - the owning Session was closed");
+ }
+ else if ( !session.isConnected() ) {
+ throw new LazyInitializationException("could not initialize proxy - the owning Session is disconnected");
+ }
+ else {
+ target = session.immediateLoad(entityName, id);
+ initialized = true;
+ checkTargetState();
+ }
+ }
+ else {
+ checkTargetState();
+ }
+ }
+
+ private void checkTargetState() {
+ if ( !unwrap ) {
+ if ( target == null ) {
+ getSession().getFactory().getEntityNotFoundDelegate().handleEntityNotFound( entityName, id );
+ }
+ }
+ }
+
+ public final void setSession(SessionImplementor s) throws HibernateException {
+ if (s!=session) {
+ if ( isConnectedToSession() ) {
+ //TODO: perhaps this should be some other RuntimeException...
+ throw new HibernateException("illegally attempted to associate a proxy with two open Sessions");
+ }
+ else {
+ session = s;
+ }
+ }
+ }
+
+ protected final boolean isConnectedToSession() {
+ return session!=null &&
+ session.isOpen() &&
+ session.getPersistenceContext().containsProxy(this);
+ }
+
+ public final void setImplementation(Object target) {
+ this.target = target;
+ initialized = true;
+ }
+
+ /**
+ * Return the underlying persistent object, initializing if necessary
+ */
+ public final Object getImplementation() {
+ initialize();
+ return target;
+ }
+
+ /**
+ * Return the underlying persistent object in the given <tt>Session</tt>, or null,
+ * do not initialize the proxy
+ */
+ public final Object getImplementation(SessionImplementor s) throws HibernateException {
+ final EntityKey entityKey = new EntityKey(
+ getIdentifier(),
+ s.getFactory().getEntityPersister( getEntityName() ),
+ s.getEntityMode()
+ );
+ return s.getPersistenceContext().getEntity( entityKey );
+ }
+
+ protected final Object getTarget() {
+ return target;
+ }
+
+ public boolean isUnwrap() {
+ return unwrap;
+ }
+
+ public void setUnwrap(boolean unwrap) {
+ this.unwrap = unwrap;
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/AbstractLazyInitializer.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/EntityNotFoundDelegate.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/EntityNotFoundDelegate.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/EntityNotFoundDelegate.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,12 @@
+package org.hibernate.proxy;
+
+import java.io.Serializable;
+
+/**
+ * Delegate to handle the scenario of an entity not found by a specified id.
+ *
+ * @author Steve Ebersole
+ */
+public interface EntityNotFoundDelegate {
+ public void handleEntityNotFound(String entityName, Serializable id);
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/HibernateProxy.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/HibernateProxy.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/HibernateProxy.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,31 @@
+package org.hibernate.proxy;
+
+import java.io.Serializable;
+
+/**
+ * Marker interface for entity proxies
+ *
+ * @author Gavin King
+ */
+public interface HibernateProxy extends Serializable {
+ /**
+ * Perform serialization-time write-replacement of this proxy.
+ *
+ * @return The serializable proxy replacement.
+ */
+ public Object writeReplace();
+
+ /**
+ * Get the underlying lazy initialization handler.
+ *
+ * @return The lazy initializer.
+ */
+ public LazyInitializer getHibernateLazyInitializer();
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/HibernateProxyHelper.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/HibernateProxyHelper.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/HibernateProxyHelper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,37 @@
+//$Id: HibernateProxyHelper.java 4453 2004-08-29 07:31:03Z oneovthafew $
+package org.hibernate.proxy;
+
+
+/**
+ * Utility methods for working with proxies. (this class is being phased out)
+ * @author Gavin King
+ */
+public final class HibernateProxyHelper {
+
+ /**
+ * Get the class of an instance or the underlying class
+ * of a proxy (without initializing the proxy!). It is
+ * almost always better to use the entity name!
+ */
+ public static Class getClassWithoutInitializingProxy(Object object) {
+ if (object instanceof HibernateProxy) {
+ HibernateProxy proxy = (HibernateProxy) object;
+ LazyInitializer li = proxy.getHibernateLazyInitializer();
+ return li.getPersistentClass();
+ }
+ else {
+ return object.getClass();
+ }
+ }
+
+ private HibernateProxyHelper() {
+ //cant instantiate
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/LazyInitializer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/LazyInitializer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/LazyInitializer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,75 @@
+//$Id: LazyInitializer.java 7246 2005-06-20 20:32:36Z oneovthafew $
+package org.hibernate.proxy;
+
+import java.io.Serializable;
+
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionImplementor;
+
+/**
+ * Handles fetching of the underlying entity for a proxy
+ * @author Gavin King
+ */
+public interface LazyInitializer {
+
+ /**
+ * Initialize the proxy, fetching the target
+ * entity if necessary
+ */
+ public abstract void initialize() throws HibernateException;
+
+ /**
+ * Get the identifier held by the proxy
+ */
+ public abstract Serializable getIdentifier();
+
+ /**
+ * Set the identifier property of the proxy
+ */
+ public abstract void setIdentifier(Serializable id);
+
+ /**
+ * Get the entity name
+ */
+ public abstract String getEntityName();
+
+ /**
+ * Get the actual class of the entity (don't
+ * use this, use the entityName)
+ */
+ public abstract Class getPersistentClass();
+
+ /**
+ * Is the proxy uninitialzed?
+ */
+ public abstract boolean isUninitialized();
+
+ /**
+ * Initialize the proxy manually
+ */
+ public abstract void setImplementation(Object target);
+
+ /**
+ * Get the session, if this proxy is attached
+ */
+ public abstract SessionImplementor getSession();
+
+ /**
+ * Attach the proxy to a session
+ */
+ public abstract void setSession(SessionImplementor s) throws HibernateException;
+
+ /**
+ * Return the underlying persistent object, initializing if necessary
+ */
+ public abstract Object getImplementation();
+
+ /**
+ * Return the underlying persistent object in the given <tt>Session</tt>, or null
+ */
+ public abstract Object getImplementation(SessionImplementor s)
+ throws HibernateException;
+
+ public void setUnwrap(boolean unwrap);
+ public boolean isUnwrap();
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/ProxyFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/ProxyFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/ProxyFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,63 @@
+//$Id: ProxyFactory.java 9210 2006-02-03 22:15:19Z steveebersole $
+package org.hibernate.proxy;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.Set;
+
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.type.AbstractComponentType;
+
+/**
+ * Contract for run-time, proxy-based lazy initialization proxies.
+ *
+ * @author Gavin King
+ */
+public interface ProxyFactory {
+
+ /**
+ * Called immediately after instantiation of this factory.
+ * <p/>
+ * Essentially equivalent to contructor injection, but contracted
+ * here via interface.
+ *
+ * @param entityName The name of the entity for which this factory should
+ * generate proxies.
+ * @param persistentClass The entity class for which to generate proxies;
+ * not always the same as the entityName.
+ * @param interfaces The interfaces to expose in the generated proxy;
+ * {@link HibernateProxy} is already included in this collection.
+ * @param getIdentifierMethod Reference to the identifier getter method;
+ * invocation on this method should not force initialization
+ * @param setIdentifierMethod Reference to the identifier setter method;
+ * invocation on this method should not force initialization
+ * @param componentIdType For composite identifier types, a reference to
+ * the {@link org.hibernate.type.ComponentType type} of the identifier
+ * property; again accessing the id should generally not cause
+ * initialization - but need to bear in mind <key-many-to-one/>
+ * mappings.
+ * @throws HibernateException Indicates a problem completing post
+ * instantiation initialization.
+ */
+ public void postInstantiate(
+ String entityName,
+ Class persistentClass,
+ Set interfaces,
+ Method getIdentifierMethod,
+ Method setIdentifierMethod,
+ AbstractComponentType componentIdType) throws HibernateException;
+
+ /**
+ * Create a new proxy instance
+ *
+ * @param id The id value for the proxy to be generated.
+ * @param session The session to which the generated proxy will be
+ * associated.
+ * @return The generated proxy.
+ * @throws HibernateException Indicates problems generating the requested
+ * proxy.
+ */
+ public HibernateProxy getProxy(Serializable id,SessionImplementor session) throws HibernateException;
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/dom4j/Dom4jLazyInitializer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/dom4j/Dom4jLazyInitializer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/dom4j/Dom4jLazyInitializer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,29 @@
+// $Id: Dom4jLazyInitializer.java 9210 2006-02-03 22:15:19Z steveebersole $
+package org.hibernate.proxy.dom4j;
+
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.proxy.AbstractLazyInitializer;
+import org.dom4j.Element;
+
+import java.io.Serializable;
+
+/**
+ * Lazy initializer for "dom4j" entity representations.
+ *
+ * @author Steve Ebersole
+ */
+public class Dom4jLazyInitializer extends AbstractLazyInitializer implements Serializable {
+
+ Dom4jLazyInitializer(String entityName, Serializable id, SessionImplementor session) {
+ super(entityName, id, session);
+ }
+
+ public Element getElement() {
+ return (Element) getImplementation();
+ }
+
+ public Class getPersistentClass() {
+ throw new UnsupportedOperationException("dom4j entity representation");
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/dom4j/Dom4jProxy.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/dom4j/Dom4jProxy.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/dom4j/Dom4jProxy.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,568 @@
+// $Id: Dom4jProxy.java 9210 2006-02-03 22:15:19Z steveebersole $
+package org.hibernate.proxy.dom4j;
+
+import org.dom4j.Element;
+import org.dom4j.QName;
+import org.dom4j.Namespace;
+import org.dom4j.Attribute;
+import org.dom4j.CDATA;
+import org.dom4j.Entity;
+import org.dom4j.Text;
+import org.dom4j.Node;
+import org.dom4j.Branch;
+import org.dom4j.ProcessingInstruction;
+import org.dom4j.Comment;
+import org.dom4j.Document;
+import org.dom4j.XPath;
+import org.dom4j.InvalidXPathException;
+import org.dom4j.Visitor;
+import org.hibernate.proxy.dom4j.Dom4jLazyInitializer;
+import org.hibernate.proxy.HibernateProxy;
+import org.hibernate.proxy.LazyInitializer;
+
+import java.io.Serializable;
+import java.io.Writer;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Iterator;
+
+/**
+ * Proxy for "dom4j" entity representations.
+ *
+ * @author Steve Ebersole
+ */
+public class Dom4jProxy implements HibernateProxy, Element, Serializable {
+
+ private Dom4jLazyInitializer li;
+
+ public Dom4jProxy(Dom4jLazyInitializer li) {
+ this.li = li;
+ }
+
+ public Object writeReplace() {
+ return this;
+ }
+
+ public LazyInitializer getHibernateLazyInitializer() {
+ return li;
+ }
+
+ public QName getQName() {
+ return target().getQName();
+ }
+
+ public QName getQName(String s) {
+ return target().getQName( s );
+ }
+
+ public void setQName(QName qName) {
+ target().setQName( qName );
+ }
+
+ public Namespace getNamespace() {
+ return target().getNamespace();
+ }
+
+ public Namespace getNamespaceForPrefix(String s) {
+ return target().getNamespaceForPrefix( s );
+ }
+
+ public Namespace getNamespaceForURI(String s) {
+ return target().getNamespaceForURI( s );
+ }
+
+ public List getNamespacesForURI(String s) {
+ return target().getNamespacesForURI( s );
+ }
+
+ public String getNamespacePrefix() {
+ return target().getNamespacePrefix();
+ }
+
+ public String getNamespaceURI() {
+ return target().getNamespaceURI();
+ }
+
+ public String getQualifiedName() {
+ return target().getQualifiedName();
+ }
+
+ public List additionalNamespaces() {
+ return target().additionalNamespaces();
+ }
+
+ public List declaredNamespaces() {
+ return target().declaredNamespaces();
+ }
+
+ public Element addAttribute(String attrName, String text) {
+ return target().addAttribute( attrName, text );
+ }
+
+ public Element addAttribute(QName attrName, String text) {
+ return target().addAttribute( attrName, text );
+ }
+
+ public Element addComment(String text) {
+ return target().addComment( text );
+ }
+
+ public Element addCDATA(String text) {
+ return target().addCDATA( text );
+ }
+
+ public Element addEntity(String name, String text) {
+ return target().addEntity( name, text );
+ }
+
+ public Element addNamespace(String prefix, String uri) {
+ return target().addNamespace( prefix, uri );
+ }
+
+ public Element addProcessingInstruction(String target, String text) {
+ return target().addProcessingInstruction( target, text );
+ }
+
+ public Element addProcessingInstruction(String target, Map data) {
+ return target().addProcessingInstruction( target, data );
+ }
+
+ public Element addText(String text) {
+ return target().addText( text );
+ }
+
+ public void add(Attribute attribute) {
+ target().add( attribute );
+ }
+
+ public void add(CDATA cdata) {
+ target().add( cdata );
+ }
+
+ public void add(Entity entity) {
+ target().add( entity );
+ }
+
+ public void add(Text text) {
+ target().add( text );
+ }
+
+ public void add(Namespace namespace) {
+ target().add( namespace );
+ }
+
+ public boolean remove(Attribute attribute) {
+ return target().remove( attribute );
+ }
+
+ public boolean remove(CDATA cdata) {
+ return target().remove( cdata );
+ }
+
+ public boolean remove(Entity entity) {
+ return target().remove( entity );
+ }
+
+ public boolean remove(Namespace namespace) {
+ return target().remove( namespace );
+ }
+
+ public boolean remove(Text text) {
+ return target().remove( text );
+ }
+
+ public boolean supportsParent() {
+ return target().supportsParent();
+ }
+
+ public Element getParent() {
+ return target().getParent();
+ }
+
+ public void setParent(Element element) {
+ target().setParent( element );
+ }
+
+ public Document getDocument() {
+ return target().getDocument();
+ }
+
+ public void setDocument(Document document) {
+ target().setDocument( document );
+ }
+
+ public boolean isReadOnly() {
+ return target().isReadOnly();
+ }
+
+ public boolean hasContent() {
+ return target().hasContent();
+ }
+
+ public String getName() {
+ return target().getName();
+ }
+
+ public void setName(String name) {
+ target().setName( name );
+ }
+
+ public String getText() {
+ return target().getText();
+ }
+
+ public void setText(String text) {
+ target().setText( text );
+ }
+
+ public String getTextTrim() {
+ return target().getTextTrim();
+ }
+
+ public String getStringValue() {
+ return target().getStringValue();
+ }
+
+ public String getPath() {
+ return target().getPath();
+ }
+
+ public String getPath(Element element) {
+ return target().getPath( element );
+ }
+
+ public String getUniquePath() {
+ return target().getUniquePath();
+ }
+
+ public String getUniquePath(Element element) {
+ return target().getUniquePath( element );
+ }
+
+ public String asXML() {
+ return target().asXML();
+ }
+
+ public void write(Writer writer) throws IOException {
+ target().write( writer );
+ }
+
+ public short getNodeType() {
+ return target().getNodeType();
+ }
+
+ public String getNodeTypeName() {
+ return target().getNodeTypeName();
+ }
+
+ public Node detach() {
+ Element parent = target().getParent();
+ if (parent!=null) parent.remove(this);
+ return target().detach();
+ }
+
+ public List selectNodes(String xpath) {
+ return target().selectNodes( xpath );
+ }
+
+ public Object selectObject(String xpath) {
+ return target().selectObject( xpath );
+ }
+
+ public List selectNodes(String xpath, String comparison) {
+ return target().selectNodes( xpath, comparison );
+ }
+
+ public List selectNodes(String xpath, String comparison, boolean removeDups) {
+ return target().selectNodes( xpath, comparison, removeDups );
+ }
+
+ public Node selectSingleNode(String xpath) {
+ return target().selectSingleNode( xpath );
+ }
+
+ public String valueOf(String xpath) {
+ return target().valueOf( xpath );
+ }
+
+ public Number numberValueOf(String xpath) {
+ return target().numberValueOf( xpath );
+ }
+
+ public boolean matches(String xpath) {
+ return target().matches( xpath );
+ }
+
+ public XPath createXPath(String xpath) throws InvalidXPathException {
+ return target().createXPath( xpath );
+ }
+
+ public Node asXPathResult(Element element) {
+ return target().asXPathResult( element );
+ }
+
+ public void accept(Visitor visitor) {
+ target().accept( visitor );
+ }
+
+ public Object clone() {
+ return target().clone();
+ }
+
+ public Object getData() {
+ return target().getData();
+ }
+
+ public void setData(Object data) {
+ target().setData( data );
+ }
+
+ public List attributes() {
+ return target().attributes();
+ }
+
+ public void setAttributes(List list) {
+ target().setAttributes( list );
+ }
+
+ public int attributeCount() {
+ return target().attributeCount();
+ }
+
+ public Iterator attributeIterator() {
+ return target().attributeIterator();
+ }
+
+ public Attribute attribute(int i) {
+ return target().attribute( i );
+ }
+
+ public Attribute attribute(String name) {
+ return target().attribute( name );
+ }
+
+ public Attribute attribute(QName qName) {
+ return target().attribute( qName );
+ }
+
+ public String attributeValue(String name) {
+ return target().attributeValue( name );
+ }
+
+ public String attributeValue(String name, String defaultValue) {
+ return target().attributeValue( name, defaultValue );
+ }
+
+ public String attributeValue(QName qName) {
+ return target().attributeValue( qName );
+ }
+
+ public String attributeValue(QName qName, String defaultValue) {
+ return target().attributeValue( qName, defaultValue );
+ }
+
+ /**
+ * @deprecated
+ */
+ public void setAttributeValue(String name, String value) {
+ target().setAttributeValue( name, value );
+ }
+
+ /**
+ * @deprecated
+ */
+ public void setAttributeValue(QName qName, String value) {
+ target().setAttributeValue( qName, value );
+ }
+
+ public Element element(String name) {
+ return target().element( name );
+ }
+
+ public Element element(QName qName) {
+ return target().element( qName );
+ }
+
+ public List elements() {
+ return target().elements();
+ }
+
+ public List elements(String name) {
+ return target().elements( name );
+ }
+
+ public List elements(QName qName) {
+ return target().elements( qName );
+ }
+
+ public Iterator elementIterator() {
+ return target().elementIterator();
+ }
+
+ public Iterator elementIterator(String name) {
+ return target().elementIterator( name );
+
+ }
+
+ public Iterator elementIterator(QName qName) {
+ return target().elementIterator( qName );
+ }
+
+ public boolean isRootElement() {
+ return target().isRootElement();
+ }
+
+ public boolean hasMixedContent() {
+ return target().hasMixedContent();
+ }
+
+ public boolean isTextOnly() {
+ return target().isTextOnly();
+ }
+
+ public void appendAttributes(Element element) {
+ target().appendAttributes( element );
+ }
+
+ public Element createCopy() {
+ return target().createCopy();
+ }
+
+ public Element createCopy(String name) {
+ return target().createCopy( name );
+ }
+
+ public Element createCopy(QName qName) {
+ return target().createCopy( qName );
+ }
+
+ public String elementText(String name) {
+ return target().elementText( name );
+ }
+
+ public String elementText(QName qName) {
+ return target().elementText( qName );
+ }
+
+ public String elementTextTrim(String name) {
+ return target().elementTextTrim( name );
+ }
+
+ public String elementTextTrim(QName qName) {
+ return target().elementTextTrim( qName );
+ }
+
+ public Node getXPathResult(int i) {
+ return target().getXPathResult( i );
+ }
+
+ public Node node(int i) {
+ return target().node( i );
+ }
+
+ public int indexOf(Node node) {
+ return target().indexOf( node );
+ }
+
+ public int nodeCount() {
+ return target().nodeCount();
+ }
+
+ public Element elementByID(String id) {
+ return target().elementByID( id );
+ }
+
+ public List content() {
+ return target().content();
+ }
+
+ public Iterator nodeIterator() {
+ return target().nodeIterator();
+ }
+
+ public void setContent(List list) {
+ target().setContent( list );
+ }
+
+ public void appendContent(Branch branch) {
+ target().appendContent( branch );
+ }
+
+ public void clearContent() {
+ target().clearContent();
+ }
+
+ public List processingInstructions() {
+ return target().processingInstructions();
+ }
+
+ public List processingInstructions(String name) {
+ return target().processingInstructions( name );
+ }
+
+ public ProcessingInstruction processingInstruction(String name) {
+ return target().processingInstruction( name );
+ }
+
+ public void setProcessingInstructions(List list) {
+ target().setProcessingInstructions( list );
+ }
+
+ public Element addElement(String name) {
+ return target().addElement( name );
+ }
+
+ public Element addElement(QName qName) {
+ return target().addElement( qName );
+ }
+
+ public Element addElement(String name, String text) {
+ return target().addElement( name, text );
+
+ }
+
+ public boolean removeProcessingInstruction(String name) {
+ return target().removeProcessingInstruction( name );
+ }
+
+ public void add(Node node) {
+ target().add( node );
+ }
+
+ public void add(Comment comment) {
+ target().add( comment );
+ }
+
+ public void add(Element element) {
+ target().add( element );
+ }
+
+ public void add(ProcessingInstruction processingInstruction) {
+ target().add( processingInstruction );
+ }
+
+ public boolean remove(Node node) {
+ return target().remove( node );
+ }
+
+ public boolean remove(Comment comment) {
+ return target().remove( comment );
+ }
+
+ public boolean remove(Element element) {
+ return target().remove( element );
+ }
+
+ public boolean remove(ProcessingInstruction processingInstruction) {
+ return target().remove( processingInstruction );
+ }
+
+ public void normalize() {
+ target().normalize();
+ }
+
+ private Element target() {
+ return li.getElement();
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/dom4j/Dom4jProxyFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/dom4j/Dom4jProxyFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/dom4j/Dom4jProxyFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,44 @@
+// $Id: Dom4jProxyFactory.java 9210 2006-02-03 22:15:19Z steveebersole $
+package org.hibernate.proxy.dom4j;
+
+import org.hibernate.HibernateException;
+import org.hibernate.proxy.dom4j.Dom4jLazyInitializer;
+import org.hibernate.proxy.dom4j.Dom4jProxy;
+import org.hibernate.proxy.ProxyFactory;
+import org.hibernate.proxy.HibernateProxy;
+import org.hibernate.type.AbstractComponentType;
+import org.hibernate.engine.SessionImplementor;
+
+import java.util.Set;
+import java.lang.reflect.Method;
+import java.io.Serializable;
+
+/**
+ * Builds proxies for "dom4j" entity representations.
+ *
+ * @author Steve Ebersole
+ */
+public class Dom4jProxyFactory implements ProxyFactory {
+
+ private String entityName;
+
+ /**
+ * Called immediately after instantiation
+ */
+ public void postInstantiate(
+ String entityName,
+ Class persistentClass,
+ Set interfaces,
+ Method getIdentifierMethod,
+ Method setIdentifierMethod,
+ AbstractComponentType componentIdType) throws HibernateException {
+ this.entityName = entityName;
+ }
+
+ /**
+ * Create a new proxy
+ */
+ public HibernateProxy getProxy(Serializable id, SessionImplementor session) throws HibernateException {
+ return new Dom4jProxy( new Dom4jLazyInitializer( entityName, id, session ) );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/map/MapLazyInitializer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/map/MapLazyInitializer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/map/MapLazyInitializer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,29 @@
+//$Id: MapLazyInitializer.java 9210 2006-02-03 22:15:19Z steveebersole $
+package org.hibernate.proxy.map;
+
+import java.io.Serializable;
+import java.util.Map;
+
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.proxy.AbstractLazyInitializer;
+
+/**
+ * Lazy initializer for "dynamic-map" entity representations.
+ *
+ * @author Gavin King
+ */
+public class MapLazyInitializer extends AbstractLazyInitializer implements Serializable {
+
+ MapLazyInitializer(String entityName, Serializable id, SessionImplementor session) {
+ super(entityName, id, session);
+ }
+
+ public Map getMap() {
+ return (Map) getImplementation();
+ }
+
+ public Class getPersistentClass() {
+ throw new UnsupportedOperationException("dynamic-map entity representation");
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/map/MapProxy.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/map/MapProxy.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/map/MapProxy.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,82 @@
+//$Id: MapProxy.java 9210 2006-02-03 22:15:19Z steveebersole $
+package org.hibernate.proxy.map;
+
+import org.hibernate.proxy.map.MapLazyInitializer;
+import org.hibernate.proxy.HibernateProxy;
+import org.hibernate.proxy.LazyInitializer;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Proxy for "dynamic-map" entity representations.
+ *
+ * @author Gavin King
+ */
+public class MapProxy implements HibernateProxy, Map, Serializable {
+
+ private MapLazyInitializer li;
+
+ MapProxy(MapLazyInitializer li) {
+ this.li = li;
+ }
+
+ public Object writeReplace() {
+ return this;
+ }
+
+ public LazyInitializer getHibernateLazyInitializer() {
+ return li;
+ }
+
+ public int size() {
+ return li.getMap().size();
+ }
+
+ public void clear() {
+ li.getMap().clear();
+ }
+
+ public boolean isEmpty() {
+ return li.getMap().isEmpty();
+ }
+
+ public boolean containsKey(Object key) {
+ return li.getMap().containsKey(key);
+ }
+
+ public boolean containsValue(Object value) {
+ return li.getMap().containsValue(value);
+ }
+
+ public Collection values() {
+ return li.getMap().values();
+ }
+
+ public void putAll(Map t) {
+ li.getMap().putAll(t);
+ }
+
+ public Set entrySet() {
+ return li.getMap().entrySet();
+ }
+
+ public Set keySet() {
+ return li.getMap().keySet();
+ }
+
+ public Object get(Object key) {
+ return li.getMap().get(key);
+ }
+
+ public Object remove(Object key) {
+ return li.getMap().remove(key);
+ }
+
+ public Object put(Object key, Object value) {
+ return li.getMap().put(key, value);
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/map/MapProxyFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/map/MapProxyFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/map/MapProxyFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,43 @@
+//$Id: MapProxyFactory.java 9210 2006-02-03 22:15:19Z steveebersole $
+package org.hibernate.proxy.map;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.Set;
+
+import org.hibernate.HibernateException;
+import org.hibernate.proxy.map.MapLazyInitializer;
+import org.hibernate.proxy.map.MapProxy;
+import org.hibernate.proxy.ProxyFactory;
+import org.hibernate.proxy.HibernateProxy;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.type.AbstractComponentType;
+
+/**
+ * @author Gavin King
+ */
+public class MapProxyFactory implements ProxyFactory {
+
+ private String entityName;
+
+ public void postInstantiate(
+ final String entityName,
+ final Class persistentClass,
+ final Set interfaces,
+ final Method getIdentifierMethod,
+ final Method setIdentifierMethod,
+ AbstractComponentType componentIdType)
+ throws HibernateException {
+
+ this.entityName = entityName;
+
+ }
+
+ public HibernateProxy getProxy(
+ final Serializable id,
+ final SessionImplementor session)
+ throws HibernateException {
+ return new MapProxy( new MapLazyInitializer(entityName, id, session) );
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,9 @@
+<html>
+<head></head>
+<body>
+<p>
+ This package defines a framework for lazy-initializing
+ entity proxies.
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/pojo/BasicLazyInitializer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/pojo/BasicLazyInitializer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/pojo/BasicLazyInitializer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,121 @@
+//$Id: BasicLazyInitializer.java 9210 2006-02-03 22:15:19Z steveebersole $
+package org.hibernate.proxy.pojo;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+
+import org.hibernate.engine.EntityKey;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.type.AbstractComponentType;
+import org.hibernate.util.MarkerObject;
+import org.hibernate.util.ReflectHelper;
+import org.hibernate.proxy.AbstractLazyInitializer;
+
+/**
+ * Lazy initializer for POJOs
+ * @author Gavin King
+ */
+public abstract class BasicLazyInitializer extends AbstractLazyInitializer {
+
+ protected static final Object INVOKE_IMPLEMENTATION = new MarkerObject("INVOKE_IMPLEMENTATION");
+
+ protected Class persistentClass;
+ protected Method getIdentifierMethod;
+ protected Method setIdentifierMethod;
+ protected boolean overridesEquals;
+ private Object replacement;
+ protected AbstractComponentType componentIdType;
+
+ protected BasicLazyInitializer(
+ String entityName,
+ Class persistentClass,
+ Serializable id,
+ Method getIdentifierMethod,
+ Method setIdentifierMethod,
+ AbstractComponentType componentIdType,
+ SessionImplementor session) {
+ super(entityName, id, session);
+ this.persistentClass = persistentClass;
+ this.getIdentifierMethod = getIdentifierMethod;
+ this.setIdentifierMethod = setIdentifierMethod;
+ this.componentIdType = componentIdType;
+ overridesEquals = ReflectHelper.overridesEquals(persistentClass);
+ }
+
+ protected abstract Object serializableProxy();
+
+ protected final Object invoke(Method method, Object[] args, Object proxy) throws Throwable {
+
+ String methodName = method.getName();
+ int params = args.length;
+
+ if ( params==0 ) {
+
+ if ( "writeReplace".equals(methodName) ) {
+ return getReplacement();
+ }
+ else if ( !overridesEquals && "hashCode".equals(methodName) ) {
+ return new Integer( System.identityHashCode(proxy) );
+ }
+ else if ( isUninitialized() && method.equals(getIdentifierMethod) ) {
+ return getIdentifier();
+ }
+
+ else if ( "getHibernateLazyInitializer".equals(methodName) ) {
+ return this;
+ }
+
+ }
+ else if ( params==1 ) {
+
+ if ( !overridesEquals && "equals".equals(methodName) ) {
+ return args[0]==proxy ? Boolean.TRUE : Boolean.FALSE;
+ }
+ else if ( method.equals(setIdentifierMethod) ) {
+ initialize();
+ setIdentifier( (Serializable) args[0] );
+ return INVOKE_IMPLEMENTATION;
+ }
+
+ }
+
+ //if it is a property of an embedded component, invoke on the "identifier"
+ if ( componentIdType!=null && componentIdType.isMethodOf(method) ) {
+ return method.invoke( getIdentifier(), args );
+ }
+
+ // otherwise:
+ return INVOKE_IMPLEMENTATION;
+
+ }
+
+ private Object getReplacement() {
+
+ final SessionImplementor session = getSession();
+ if ( isUninitialized() && session != null && session.isOpen()) {
+ final EntityKey key = new EntityKey(
+ getIdentifier(),
+ session.getFactory().getEntityPersister( getEntityName() ),
+ session.getEntityMode()
+ );
+ final Object entity = session.getPersistenceContext().getEntity(key);
+ if (entity!=null) setImplementation( entity );
+ }
+
+ if ( isUninitialized() ) {
+ if (replacement==null) {
+ replacement = serializableProxy();
+ }
+ return replacement;
+ }
+ else {
+ return getTarget();
+ }
+
+ }
+
+ public final Class getPersistentClass() {
+ return persistentClass;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/pojo/cglib/CGLIBLazyInitializer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/pojo/cglib/CGLIBLazyInitializer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/pojo/cglib/CGLIBLazyInitializer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,198 @@
+// $Id: CGLIBLazyInitializer.java 11295 2007-03-18 04:05:28Z scottmarlownovell $
+package org.hibernate.proxy.pojo.cglib;
+
+import java.io.Serializable;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import net.sf.cglib.proxy.Callback;
+import net.sf.cglib.proxy.CallbackFilter;
+import net.sf.cglib.proxy.Enhancer;
+import net.sf.cglib.proxy.InvocationHandler;
+import net.sf.cglib.proxy.NoOp;
+
+import org.hibernate.HibernateException;
+import org.hibernate.LazyInitializationException;
+import org.hibernate.proxy.pojo.BasicLazyInitializer;
+import org.hibernate.proxy.HibernateProxy;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.type.AbstractComponentType;
+import org.hibernate.util.ReflectHelper;
+
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * A <tt>LazyInitializer</tt> implemented using the CGLIB bytecode generation library
+ */
+public final class CGLIBLazyInitializer extends BasicLazyInitializer implements InvocationHandler {
+
+ private static final CallbackFilter FINALIZE_FILTER = new CallbackFilter() {
+ public int accept(Method method) {
+ if ( method.getParameterTypes().length == 0 && method.getName().equals("finalize") ){
+ return 1;
+ }
+ else {
+ return 0;
+ }
+ }
+ };
+
+ private Class[] interfaces;
+ private boolean constructed = false;
+
+ static HibernateProxy getProxy(final String entityName, final Class persistentClass,
+ final Class[] interfaces, final Method getIdentifierMethod,
+ final Method setIdentifierMethod, AbstractComponentType componentIdType,
+ final Serializable id, final SessionImplementor session) throws HibernateException {
+ // note: interfaces is assumed to already contain HibernateProxy.class
+
+ try {
+ final CGLIBLazyInitializer instance = new CGLIBLazyInitializer(
+ entityName,
+ persistentClass,
+ interfaces,
+ id,
+ getIdentifierMethod,
+ setIdentifierMethod,
+ componentIdType,
+ session
+ );
+
+ final HibernateProxy proxy;
+ Class factory = getProxyFactory(persistentClass, interfaces);
+ proxy = getProxyInstance(factory, instance);
+ instance.constructed = true;
+ return proxy;
+ }
+ catch (Throwable t) {
+ LogFactory.getLog( BasicLazyInitializer.class )
+ .error( "CGLIB Enhancement failed: " + entityName, t );
+ throw new HibernateException( "CGLIB Enhancement failed: " + entityName, t );
+ }
+ }
+
+ public static HibernateProxy getProxy(final Class factory, final String entityName,
+ final Class persistentClass, final Class[] interfaces,
+ final Method getIdentifierMethod, final Method setIdentifierMethod,
+ final AbstractComponentType componentIdType, final Serializable id,
+ final SessionImplementor session) throws HibernateException {
+
+ final CGLIBLazyInitializer instance = new CGLIBLazyInitializer(
+ entityName,
+ persistentClass,
+ interfaces,
+ id,
+ getIdentifierMethod,
+ setIdentifierMethod,
+ componentIdType,
+ session
+ );
+
+ final HibernateProxy proxy;
+ try {
+ proxy = getProxyInstance(factory, instance);
+ }
+ catch (Exception e) {
+ throw new HibernateException( "CGLIB Enhancement failed: " + persistentClass.getName(), e );
+ }
+ instance.constructed = true;
+
+ return proxy;
+ }
+
+ private static HibernateProxy getProxyInstance(Class factory, CGLIBLazyInitializer instance) throws InstantiationException, IllegalAccessException {
+ HibernateProxy proxy;
+ try {
+ Enhancer.registerCallbacks(factory, new Callback[]{ instance, null });
+ proxy = (HibernateProxy)factory.newInstance();
+ } finally {
+ // HHH-2481 make sure the callback gets cleared, otherwise the instance stays in a static thread local.
+ Enhancer.registerCallbacks(factory, null);
+ }
+ return proxy;
+ }
+
+ public static Class getProxyFactory(Class persistentClass, Class[] interfaces)
+ throws HibernateException {
+ Enhancer e = new Enhancer();
+ e.setSuperclass( interfaces.length == 1 ? persistentClass : null );
+ e.setInterfaces(interfaces);
+ e.setCallbackTypes(new Class[]{
+ InvocationHandler.class,
+ NoOp.class,
+ });
+ e.setCallbackFilter(FINALIZE_FILTER);
+ e.setUseFactory(false);
+ e.setInterceptDuringConstruction( false );
+ return e.createClass();
+ }
+
+ private CGLIBLazyInitializer(final String entityName, final Class persistentClass,
+ final Class[] interfaces, final Serializable id, final Method getIdentifierMethod,
+ final Method setIdentifierMethod, final AbstractComponentType componentIdType,
+ final SessionImplementor session) {
+ super(
+ entityName,
+ persistentClass,
+ id,
+ getIdentifierMethod,
+ setIdentifierMethod,
+ componentIdType,
+ session
+ );
+ this.interfaces = interfaces;
+ }
+
+ public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
+ if ( constructed ) {
+ Object result = invoke( method, args, proxy );
+ if ( result == INVOKE_IMPLEMENTATION ) {
+ Object target = getImplementation();
+ try {
+ final Object returnValue;
+ if ( ReflectHelper.isPublic( persistentClass, method ) ) {
+ if ( !method.getDeclaringClass().isInstance( target ) ) {
+ throw new ClassCastException( target.getClass().getName() );
+ }
+ returnValue = method.invoke( target, args );
+ }
+ else {
+ if ( !method.isAccessible() ) {
+ method.setAccessible( true );
+ }
+ returnValue = method.invoke( target, args );
+ }
+ return returnValue == target ? proxy : returnValue;
+ }
+ catch ( InvocationTargetException ite ) {
+ throw ite.getTargetException();
+ }
+ }
+ else {
+ return result;
+ }
+ }
+ else {
+ // while constructor is running
+ if ( method.getName().equals( "getHibernateLazyInitializer" ) ) {
+ return this;
+ }
+ else {
+ throw new LazyInitializationException( "unexpected case hit, method=" + method.getName() );
+ }
+ }
+ }
+
+ protected Object serializableProxy() {
+ return new SerializableProxy(
+ getEntityName(),
+ persistentClass,
+ interfaces,
+ getIdentifier(),
+ getIdentifierMethod,
+ setIdentifierMethod,
+ componentIdType
+ );
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/pojo/cglib/CGLIBProxyFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/pojo/cglib/CGLIBProxyFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/pojo/cglib/CGLIBProxyFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,62 @@
+//$Id: CGLIBProxyFactory.java 9210 2006-02-03 22:15:19Z steveebersole $
+package org.hibernate.proxy.pojo.cglib;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.Set;
+
+import org.hibernate.HibernateException;
+import org.hibernate.proxy.ProxyFactory;
+import org.hibernate.proxy.HibernateProxy;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.type.AbstractComponentType;
+
+/**
+ * @author Gavin King
+ */
+public class CGLIBProxyFactory implements ProxyFactory {
+
+ protected static final Class[] NO_CLASSES = new Class[0];
+
+ private Class persistentClass;
+ private String entityName;
+ private Class[] interfaces;
+ private Method getIdentifierMethod;
+ private Method setIdentifierMethod;
+ private AbstractComponentType componentIdType;
+ private Class factory;
+
+ public void postInstantiate(
+ final String entityName,
+ final Class persistentClass,
+ final Set interfaces,
+ final Method getIdentifierMethod,
+ final Method setIdentifierMethod,
+ AbstractComponentType componentIdType)
+ throws HibernateException {
+ this.entityName = entityName;
+ this.persistentClass = persistentClass;
+ this.interfaces = (Class[]) interfaces.toArray(NO_CLASSES);
+ this.getIdentifierMethod = getIdentifierMethod;
+ this.setIdentifierMethod = setIdentifierMethod;
+ this.componentIdType = componentIdType;
+ factory = CGLIBLazyInitializer.getProxyFactory(persistentClass, this.interfaces);
+ }
+
+ public HibernateProxy getProxy(Serializable id, SessionImplementor session)
+ throws HibernateException {
+
+ return CGLIBLazyInitializer.getProxy(
+ factory,
+ entityName,
+ persistentClass,
+ interfaces,
+ getIdentifierMethod,
+ setIdentifierMethod,
+ componentIdType,
+ id,
+ session
+ );
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/pojo/cglib/SerializableProxy.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/pojo/cglib/SerializableProxy.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/pojo/cglib/SerializableProxy.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,75 @@
+//$Id: SerializableProxy.java 9210 2006-02-03 22:15:19Z steveebersole $
+package org.hibernate.proxy.pojo.cglib;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+
+import org.hibernate.HibernateException;
+import org.hibernate.type.AbstractComponentType;
+
+/**
+ * Serializable placeholder for <tt>CGLIB</tt> proxies
+ */
+public final class SerializableProxy implements Serializable {
+
+ private String entityName;
+ private Class persistentClass;
+ private Class[] interfaces;
+ private Serializable id;
+ private Class getIdentifierMethodClass;
+ private Class setIdentifierMethodClass;
+ private String getIdentifierMethodName;
+ private String setIdentifierMethodName;
+ private Class[] setIdentifierMethodParams;
+ private AbstractComponentType componentIdType;
+
+ public SerializableProxy() {}
+
+ public SerializableProxy(
+ final String entityName,
+ final Class persistentClass,
+ final Class[] interfaces,
+ final Serializable id,
+ final Method getIdentifierMethod,
+ final Method setIdentifierMethod,
+ AbstractComponentType componentIdType
+ ) {
+ this.entityName = entityName;
+ this.persistentClass = persistentClass;
+ this.interfaces = interfaces;
+ this.id = id;
+ if (getIdentifierMethod!=null) {
+ getIdentifierMethodClass = getIdentifierMethod.getDeclaringClass();
+ getIdentifierMethodName = getIdentifierMethod.getName();
+ }
+ if (setIdentifierMethod!=null) {
+ setIdentifierMethodClass = setIdentifierMethod.getDeclaringClass();
+ setIdentifierMethodName = setIdentifierMethod.getName();
+ setIdentifierMethodParams = setIdentifierMethod.getParameterTypes();
+ }
+ this.componentIdType = componentIdType;
+ }
+
+ private Object readResolve() {
+ try {
+ return CGLIBLazyInitializer.getProxy(
+ entityName,
+ persistentClass,
+ interfaces,
+ getIdentifierMethodName==null ?
+ null :
+ getIdentifierMethodClass.getDeclaredMethod(getIdentifierMethodName, null),
+ setIdentifierMethodName==null ?
+ null :
+ setIdentifierMethodClass.getDeclaredMethod(setIdentifierMethodName, setIdentifierMethodParams),
+ componentIdType,
+ id,
+ null
+ );
+ }
+ catch (NoSuchMethodException nsme) {
+ throw new HibernateException("could not create proxy for entity: " + entityName, nsme);
+ }
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/pojo/javassist/JavassistLazyInitializer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/pojo/javassist/JavassistLazyInitializer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/pojo/javassist/JavassistLazyInitializer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,213 @@
+package org.hibernate.proxy.pojo.javassist;
+
+import java.io.Serializable;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import javassist.util.proxy.MethodFilter;
+import javassist.util.proxy.MethodHandler;
+import javassist.util.proxy.ProxyFactory;
+import javassist.util.proxy.ProxyObject;
+
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.proxy.pojo.BasicLazyInitializer;
+import org.hibernate.proxy.HibernateProxy;
+import org.hibernate.type.AbstractComponentType;
+import org.hibernate.util.ReflectHelper;
+
+/**
+ * A Javassist-based lazy initializer proxy.
+ *
+ * @author Muga Nishizawa
+ */
+public class JavassistLazyInitializer extends BasicLazyInitializer implements MethodHandler {
+
+ private static final MethodFilter FINALIZE_FILTER = new MethodFilter() {
+ public boolean isHandled(Method m) {
+ // skip finalize methods
+ return !( m.getParameterTypes().length == 0 && m.getName().equals( "finalize" ) );
+ }
+ };
+
+ private Class[] interfaces;
+ private boolean constructed = false;
+
+ private JavassistLazyInitializer(
+ final String entityName,
+ final Class persistentClass,
+ final Class[] interfaces,
+ final Serializable id,
+ final Method getIdentifierMethod,
+ final Method setIdentifierMethod,
+ final AbstractComponentType componentIdType,
+ final SessionImplementor session) {
+ super( entityName, persistentClass, id, getIdentifierMethod, setIdentifierMethod, componentIdType, session );
+ this.interfaces = interfaces;
+ }
+
+ public static HibernateProxy getProxy(
+ final String entityName,
+ final Class persistentClass,
+ final Class[] interfaces,
+ final Method getIdentifierMethod,
+ final Method setIdentifierMethod,
+ AbstractComponentType componentIdType,
+ final Serializable id,
+ final SessionImplementor session) throws HibernateException {
+ // note: interface is assumed to already contain HibernateProxy.class
+ try {
+ final JavassistLazyInitializer instance = new JavassistLazyInitializer(
+ entityName,
+ persistentClass,
+ interfaces,
+ id,
+ getIdentifierMethod,
+ setIdentifierMethod,
+ componentIdType,
+ session
+ );
+ ProxyFactory factory = new ProxyFactory();
+ factory.setSuperclass( interfaces.length == 1 ? persistentClass : null );
+ factory.setInterfaces( interfaces );
+ factory.setFilter( FINALIZE_FILTER );
+ Class cl = factory.createClass();
+ final HibernateProxy proxy = ( HibernateProxy ) cl.newInstance();
+ ( ( ProxyObject ) proxy ).setHandler( instance );
+ instance.constructed = true;
+ return proxy;
+ }
+ catch ( Throwable t ) {
+ LogFactory.getLog( BasicLazyInitializer.class ).error(
+ "Javassist Enhancement failed: " + entityName, t
+ );
+ throw new HibernateException(
+ "Javassist Enhancement failed: "
+ + entityName, t
+ );
+ }
+ }
+
+ public static HibernateProxy getProxy(
+ final Class factory,
+ final String entityName,
+ final Class persistentClass,
+ final Class[] interfaces,
+ final Method getIdentifierMethod,
+ final Method setIdentifierMethod,
+ final AbstractComponentType componentIdType,
+ final Serializable id,
+ final SessionImplementor session) throws HibernateException {
+
+ final JavassistLazyInitializer instance = new JavassistLazyInitializer(
+ entityName,
+ persistentClass,
+ interfaces, id,
+ getIdentifierMethod,
+ setIdentifierMethod,
+ componentIdType,
+ session
+ );
+
+ final HibernateProxy proxy;
+ try {
+ proxy = ( HibernateProxy ) factory.newInstance();
+ }
+ catch ( Exception e ) {
+ throw new HibernateException(
+ "Javassist Enhancement failed: "
+ + persistentClass.getName(), e
+ );
+ }
+ ( ( ProxyObject ) proxy ).setHandler( instance );
+ instance.constructed = true;
+ return proxy;
+ }
+
+ public static Class getProxyFactory(
+ Class persistentClass,
+ Class[] interfaces) throws HibernateException {
+ // note: interfaces is assumed to already contain HibernateProxy.class
+
+ try {
+ ProxyFactory factory = new ProxyFactory();
+ factory.setSuperclass( interfaces.length == 1 ? persistentClass : null );
+ factory.setInterfaces( interfaces );
+ factory.setFilter( FINALIZE_FILTER );
+ return factory.createClass();
+ }
+ catch ( Throwable t ) {
+ LogFactory.getLog( BasicLazyInitializer.class ).error(
+ "Javassist Enhancement failed: "
+ + persistentClass.getName(), t
+ );
+ throw new HibernateException(
+ "Javassist Enhancement failed: "
+ + persistentClass.getName(), t
+ );
+ }
+ }
+
+ public Object invoke(
+ final Object proxy,
+ final Method thisMethod,
+ final Method proceed,
+ final Object[] args) throws Throwable {
+ if ( this.constructed ) {
+ Object result;
+ try {
+ result = this.invoke( thisMethod, args, proxy );
+ }
+ catch ( Throwable t ) {
+ throw new Exception( t.getCause() );
+ }
+ if ( result == INVOKE_IMPLEMENTATION ) {
+ Object target = getImplementation();
+ final Object returnValue;
+ try {
+ if ( ReflectHelper.isPublic( persistentClass, thisMethod ) ) {
+ if ( !thisMethod.getDeclaringClass().isInstance( target ) ) {
+ throw new ClassCastException( target.getClass().getName() );
+ }
+ returnValue = thisMethod.invoke( target, args );
+ }
+ else {
+ if ( !thisMethod.isAccessible() ) {
+ thisMethod.setAccessible( true );
+ }
+ returnValue = thisMethod.invoke( target, args );
+ }
+ return returnValue == target ? proxy : returnValue;
+ }
+ catch ( InvocationTargetException ite ) {
+ throw ite.getTargetException();
+ }
+ }
+ else {
+ return result;
+ }
+ }
+ else {
+ // while constructor is running
+ if ( thisMethod.getName().equals( "getHibernateLazyInitializer" ) ) {
+ return this;
+ }
+ else {
+ return proceed.invoke( proxy, args );
+ }
+ }
+ }
+
+ protected Object serializableProxy() {
+ return new SerializableProxy(
+ getEntityName(),
+ persistentClass,
+ interfaces,
+ getIdentifier(),
+ getIdentifierMethod,
+ setIdentifierMethod,
+ componentIdType
+ );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/pojo/javassist/JavassistProxyFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/pojo/javassist/JavassistProxyFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/pojo/javassist/JavassistProxyFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,61 @@
+package org.hibernate.proxy.pojo.javassist;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.Set;
+
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.proxy.HibernateProxy;
+import org.hibernate.proxy.ProxyFactory;
+import org.hibernate.type.AbstractComponentType;
+
+/**
+ * A {@link ProxyFactory} implementation for producing Javassist-based proxies.
+ *
+ * @author Muga Nishizawa
+ */
+public class JavassistProxyFactory implements ProxyFactory, Serializable {
+
+ protected static final Class[] NO_CLASSES = new Class[0];
+ private Class persistentClass;
+ private String entityName;
+ private Class[] interfaces;
+ private Method getIdentifierMethod;
+ private Method setIdentifierMethod;
+ private AbstractComponentType componentIdType;
+ private Class factory;
+
+ public void postInstantiate(
+ final String entityName,
+ final Class persistentClass,
+ final Set interfaces,
+ final Method getIdentifierMethod,
+ final Method setIdentifierMethod,
+ AbstractComponentType componentIdType) throws HibernateException {
+ this.entityName = entityName;
+ this.persistentClass = persistentClass;
+ this.interfaces = (Class[]) interfaces.toArray(NO_CLASSES);
+ this.getIdentifierMethod = getIdentifierMethod;
+ this.setIdentifierMethod = setIdentifierMethod;
+ this.componentIdType = componentIdType;
+ factory = JavassistLazyInitializer.getProxyFactory( persistentClass, this.interfaces );
+ }
+
+ public HibernateProxy getProxy(
+ Serializable id,
+ SessionImplementor session) throws HibernateException {
+ return JavassistLazyInitializer.getProxy(
+ factory,
+ entityName,
+ persistentClass,
+ interfaces,
+ getIdentifierMethod,
+ setIdentifierMethod,
+ componentIdType,
+ id,
+ session
+ );
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/pojo/javassist/SerializableProxy.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/pojo/javassist/SerializableProxy.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/proxy/pojo/javassist/SerializableProxy.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,74 @@
+package org.hibernate.proxy.pojo.javassist;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+
+import org.hibernate.HibernateException;
+import org.hibernate.type.AbstractComponentType;
+
+/**
+ * Serializable placeholder for Javassist proxies
+ */
+public final class SerializableProxy implements Serializable {
+
+ private String entityName;
+ private Class persistentClass;
+ private Class[] interfaces;
+ private Serializable id;
+ private Class getIdentifierMethodClass;
+ private Class setIdentifierMethodClass;
+ private String getIdentifierMethodName;
+ private String setIdentifierMethodName;
+ private Class[] setIdentifierMethodParams;
+ private AbstractComponentType componentIdType;
+
+ public SerializableProxy() {}
+
+ public SerializableProxy(
+ final String entityName,
+ final Class persistentClass,
+ final Class[] interfaces,
+ final Serializable id,
+ final Method getIdentifierMethod,
+ final Method setIdentifierMethod,
+ AbstractComponentType componentIdType
+ ) {
+ this.entityName = entityName;
+ this.persistentClass = persistentClass;
+ this.interfaces = interfaces;
+ this.id = id;
+ if (getIdentifierMethod!=null) {
+ getIdentifierMethodClass = getIdentifierMethod.getDeclaringClass();
+ getIdentifierMethodName = getIdentifierMethod.getName();
+ }
+ if (setIdentifierMethod!=null) {
+ setIdentifierMethodClass = setIdentifierMethod.getDeclaringClass();
+ setIdentifierMethodName = setIdentifierMethod.getName();
+ setIdentifierMethodParams = setIdentifierMethod.getParameterTypes();
+ }
+ this.componentIdType = componentIdType;
+ }
+
+ private Object readResolve() {
+ try {
+ return JavassistLazyInitializer.getProxy(
+ entityName,
+ persistentClass,
+ interfaces,
+ getIdentifierMethodName==null ?
+ null :
+ getIdentifierMethodClass.getDeclaredMethod(getIdentifierMethodName, null),
+ setIdentifierMethodName==null ?
+ null :
+ setIdentifierMethodClass.getDeclaredMethod(setIdentifierMethodName, setIdentifierMethodParams),
+ componentIdType,
+ id,
+ null
+ );
+ }
+ catch (NoSuchMethodException nsme) {
+ throw new HibernateException("could not create proxy for entity: " + entityName, nsme);
+ }
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/secure/HibernatePermission.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/secure/HibernatePermission.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/secure/HibernatePermission.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,49 @@
+//$Id: HibernatePermission.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate.secure;
+
+import java.security.Permission;
+
+/**
+ * @author Gavin King
+ */
+public class HibernatePermission extends Permission {
+
+ public static final String INSERT = "insert";
+ public static final String UPDATE = "update";
+ public static final String DELETE = "delete";
+ public static final String READ = "read";
+ public static final String ANY = "*";
+
+ private final String actions;
+
+ public HibernatePermission(String entityName, String actions) {
+ super(entityName);
+ this.actions = actions;
+ }
+
+ public boolean implies(Permission permission) {
+ //TODO!
+ return ( "*".equals( getName() ) || getName().equals( permission.getName() ) ) &&
+ ( "*".equals(actions) || actions.indexOf( permission.getActions() ) >= 0 );
+ }
+
+ public boolean equals(Object obj) {
+ if ( !(obj instanceof HibernatePermission) ) return false;
+ HibernatePermission permission = (HibernatePermission) obj;
+ return permission.getName().equals( getName() ) &&
+ permission.getActions().equals(actions);
+ }
+
+ public int hashCode() {
+ return getName().hashCode() * 37 + actions.hashCode();
+ }
+
+ public String getActions() {
+ return actions;
+ }
+
+ public String toString() {
+ return "HibernatePermission(" + getName() + ':' + actions + ')';
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/secure/HibernatePermission.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/secure/JACCConfiguration.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/secure/JACCConfiguration.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/secure/JACCConfiguration.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,69 @@
+// $Id: JACCConfiguration.java 7592 2005-07-21 04:56:17Z oneovthafew $
+package org.hibernate.secure;
+
+import java.util.StringTokenizer;
+
+import javax.security.jacc.EJBMethodPermission;
+import javax.security.jacc.PolicyConfiguration;
+import javax.security.jacc.PolicyConfigurationFactory;
+import javax.security.jacc.PolicyContextException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.HibernateException;
+
+/**
+ * Adds Hibernate permissions to roles via JACC
+ *
+ * @author Gavin King
+ */
+public class JACCConfiguration {
+
+ private static final Log log = LogFactory.getLog( JACCConfiguration.class );
+
+ private final PolicyConfiguration policyConfiguration;
+
+ public JACCConfiguration(String contextId) throws HibernateException {
+ try {
+ policyConfiguration = PolicyConfigurationFactory
+ .getPolicyConfigurationFactory()
+ .getPolicyConfiguration( contextId, false );
+ }
+ catch (ClassNotFoundException cnfe) {
+ throw new HibernateException( "JACC provider class not found", cnfe );
+ }
+ catch (PolicyContextException pce) {
+ throw new HibernateException( "policy context exception occurred", pce );
+ }
+ }
+
+ public void addPermission(String role, String entityName, String action) {
+
+ if ( action.equals( "*" ) ) {
+ action = "insert,read,update,delete";
+ }
+
+ StringTokenizer tok = new StringTokenizer( action, "," );
+
+ while ( tok.hasMoreTokens() ) {
+ String methodName = tok.nextToken().trim();
+ EJBMethodPermission permission = new EJBMethodPermission(
+ entityName,
+ methodName,
+ null, // interfaces
+ null // arguments
+ );
+
+ if ( log.isDebugEnabled() ) {
+ log.debug( "adding permission to role " + role + ": " + permission );
+ }
+ try {
+ policyConfiguration.addToRole( role, permission );
+ }
+ catch (PolicyContextException pce) {
+ throw new HibernateException( "policy context exception occurred", pce );
+ }
+ }
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/secure/JACCConfiguration.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/secure/JACCPermissions.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/secure/JACCPermissions.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/secure/JACCPermissions.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,123 @@
+//$Id: JACCPermissions.java 8661 2005-11-25 12:00:22Z epbernard $
+package org.hibernate.secure;
+
+import java.lang.reflect.UndeclaredThrowableException;
+import java.security.AccessController;
+import java.security.CodeSource;
+import java.security.Policy;
+import java.security.Principal;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.security.ProtectionDomain;
+import java.util.Set;
+import javax.security.auth.Subject;
+import javax.security.jacc.EJBMethodPermission;
+import javax.security.jacc.PolicyContext;
+import javax.security.jacc.PolicyContextException;
+
+
+/**
+ * Copied from JBoss org.jboss.ejb3.security.JaccHelper and org.jboss.ejb3.security.SecurityActions
+ *
+ * @author <a href="mailto:kabir.khan at jboss.org">Kabir Khan</a>
+ */
+public class JACCPermissions {
+
+ public static void checkPermission(Class clazz, String contextID, EJBMethodPermission methodPerm)
+ throws SecurityException {
+ CodeSource ejbCS = clazz.getProtectionDomain().getCodeSource();
+
+ try {
+ setContextID( contextID );
+
+ Policy policy = Policy.getPolicy();
+ // Get the caller
+ Subject caller = getContextSubject();
+
+ Principal[] principals = null;
+ if ( caller != null ) {
+ // Get the caller principals
+ Set principalsSet = caller.getPrincipals();
+ principals = new Principal[ principalsSet.size() ];
+ principalsSet.toArray( principals );
+ }
+
+ ProtectionDomain pd = new ProtectionDomain( ejbCS, null, null, principals );
+ if ( policy.implies( pd, methodPerm ) == false ) {
+ String msg = "Denied: " + methodPerm + ", caller=" + caller;
+ SecurityException e = new SecurityException( msg );
+ throw e;
+ }
+ }
+ catch (PolicyContextException e) {
+ throw new RuntimeException( e );
+ }
+ }
+
+ interface PolicyContextActions {
+ /**
+ * The JACC PolicyContext key for the current Subject
+ */
+ static final String SUBJECT_CONTEXT_KEY = "javax.security.auth.Subject.container";
+ PolicyContextActions PRIVILEGED = new PolicyContextActions() {
+ private final PrivilegedExceptionAction exAction = new PrivilegedExceptionAction() {
+ public Object run() throws Exception {
+ return (Subject) PolicyContext.getContext( SUBJECT_CONTEXT_KEY );
+ }
+ };
+
+ public Subject getContextSubject() throws PolicyContextException {
+ try {
+ return (Subject) AccessController.doPrivileged( exAction );
+ }
+ catch (PrivilegedActionException e) {
+ Exception ex = e.getException();
+ if ( ex instanceof PolicyContextException ) {
+ throw (PolicyContextException) ex;
+ }
+ else {
+ throw new UndeclaredThrowableException( ex );
+ }
+ }
+ }
+ };
+
+ PolicyContextActions NON_PRIVILEGED = new PolicyContextActions() {
+ public Subject getContextSubject() throws PolicyContextException {
+ return (Subject) PolicyContext.getContext( SUBJECT_CONTEXT_KEY );
+ }
+ };
+
+ Subject getContextSubject() throws PolicyContextException;
+ }
+
+ static Subject getContextSubject() throws PolicyContextException {
+ if ( System.getSecurityManager() == null ) {
+ return PolicyContextActions.NON_PRIVILEGED.getContextSubject();
+ }
+ else {
+ return PolicyContextActions.PRIVILEGED.getContextSubject();
+ }
+ }
+
+ private static class SetContextID implements PrivilegedAction {
+ String contextID;
+
+ SetContextID(String contextID) {
+ this.contextID = contextID;
+ }
+
+ public Object run() {
+ String previousID = PolicyContext.getContextID();
+ PolicyContext.setContextID( contextID );
+ return previousID;
+ }
+ }
+
+ static String setContextID(String contextID) {
+ PrivilegedAction action = new SetContextID( contextID );
+ String previousID = (String) AccessController.doPrivileged( action );
+ return previousID;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/secure/JACCPreDeleteEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/secure/JACCPreDeleteEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/secure/JACCPreDeleteEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,37 @@
+//$Id: JACCPreDeleteEventListener.java 8702 2005-11-29 18:34:29Z kabkhan $
+package org.hibernate.secure;
+
+import javax.security.jacc.EJBMethodPermission;
+
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.event.Initializable;
+import org.hibernate.event.PreDeleteEvent;
+import org.hibernate.event.PreDeleteEventListener;
+
+/**
+ * Check security before any deletion
+ *
+ * @author <a href="mailto:kabir.khan at jboss.org">Kabir Khan</a>
+ */
+public class JACCPreDeleteEventListener implements PreDeleteEventListener, Initializable, JACCSecurityListener {
+ private String contextID;
+
+ public boolean onPreDelete(PreDeleteEvent event) {
+
+ EJBMethodPermission deletePermission = new EJBMethodPermission(
+ event.getPersister().getEntityName(),
+ HibernatePermission.DELETE,
+ null,
+ null
+ );
+
+ JACCPermissions.checkPermission( event.getEntity().getClass(), contextID, deletePermission );
+
+ return false;
+ }
+
+ public void initialize(Configuration cfg){
+ contextID = cfg.getProperty(Environment.JACC_CONTEXTID);
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/secure/JACCPreDeleteEventListener.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/secure/JACCPreInsertEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/secure/JACCPreInsertEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/secure/JACCPreInsertEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,38 @@
+//$Id: JACCPreInsertEventListener.java 8702 2005-11-29 18:34:29Z kabkhan $
+package org.hibernate.secure;
+
+import javax.security.jacc.EJBMethodPermission;
+
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.event.Initializable;
+import org.hibernate.event.PreInsertEvent;
+import org.hibernate.event.PreInsertEventListener;
+
+/**
+ * Check security before an insertion
+ *
+ * @author <a href="mailto:kabir.khan at jboss.org">Kabir Khan</a>
+ */
+public class JACCPreInsertEventListener implements PreInsertEventListener, Initializable, JACCSecurityListener {
+ private String contextID;
+
+ public boolean onPreInsert(PreInsertEvent event) {
+
+ EJBMethodPermission insertPermission = new EJBMethodPermission(
+ event.getPersister().getEntityName(),
+ HibernatePermission.INSERT,
+ null,
+ null
+ );
+
+ JACCPermissions.checkPermission( event.getEntity().getClass(), contextID, insertPermission );
+
+ return false;
+ }
+
+
+ public void initialize(Configuration cfg){
+ contextID = cfg.getProperty(Environment.JACC_CONTEXTID);
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/secure/JACCPreInsertEventListener.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/secure/JACCPreLoadEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/secure/JACCPreLoadEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/secure/JACCPreLoadEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,38 @@
+// $Id: JACCPreLoadEventListener.java 8702 2005-11-29 18:34:29Z kabkhan $
+package org.hibernate.secure;
+
+import javax.security.jacc.EJBMethodPermission;
+
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.event.Initializable;
+import org.hibernate.event.PreLoadEvent;
+import org.hibernate.event.PreLoadEventListener;
+
+/**
+ * Check security before any load
+ *
+ * @author <a href="mailto:kabir.khan at jboss.org">Kabir Khan</a>
+ * @version $Revision: 8702 $
+ */
+public class JACCPreLoadEventListener implements PreLoadEventListener, Initializable, JACCSecurityListener {
+ private String contextID;
+
+ public void onPreLoad(PreLoadEvent event) {
+
+ EJBMethodPermission loadPermission = new EJBMethodPermission(
+ event.getPersister().getEntityName(),
+ HibernatePermission.READ,
+ null,
+ null
+ );
+
+ JACCPermissions.checkPermission( event.getEntity().getClass(), contextID, loadPermission );
+
+ }
+
+
+ public void initialize(Configuration cfg){
+ contextID = cfg.getProperty(Environment.JACC_CONTEXTID);
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/secure/JACCPreLoadEventListener.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/secure/JACCPreUpdateEventListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/secure/JACCPreUpdateEventListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/secure/JACCPreUpdateEventListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,39 @@
+//$Id: JACCPreUpdateEventListener.java 8702 2005-11-29 18:34:29Z kabkhan $
+package org.hibernate.secure;
+
+import javax.security.jacc.EJBMethodPermission;
+
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.event.Initializable;
+import org.hibernate.event.PreUpdateEvent;
+import org.hibernate.event.PreUpdateEventListener;
+
+/**
+ * Check security before any update
+ *
+ * @author <a href="mailto:kabir.khan at jboss.org">Kabir Khan</a>
+ * @version $Revision: 8702 $
+ */
+public class JACCPreUpdateEventListener implements PreUpdateEventListener, Initializable, JACCSecurityListener {
+ private String contextID;
+
+ public boolean onPreUpdate(PreUpdateEvent event) {
+
+ EJBMethodPermission updatePermission = new EJBMethodPermission(
+ event.getPersister().getEntityName(),
+ HibernatePermission.UPDATE,
+ null,
+ null
+ );
+
+ JACCPermissions.checkPermission( event.getEntity().getClass(), contextID, updatePermission );
+
+ return false;
+ }
+
+
+ public void initialize(Configuration cfg){
+ contextID = cfg.getProperty(Environment.JACC_CONTEXTID);
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/secure/JACCPreUpdateEventListener.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/secure/JACCSecurityListener.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/secure/JACCSecurityListener.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/secure/JACCSecurityListener.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,11 @@
+package org.hibernate.secure;
+
+/**
+ * Marker interface for JACC event listeners
+ *
+ * @author <a href="kabir.khan at jboss.com">Kabir Khan</a>
+ * @version $Revision: 8702 $
+ */
+public interface JACCSecurityListener{
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/secure/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/secure/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/secure/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,8 @@
+<html>
+<head></head>
+<body>
+<p>
+ Declarative security for CRUD operations on entities.
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/secure/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/sql/ANSICaseFragment.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/sql/ANSICaseFragment.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/sql/ANSICaseFragment.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,40 @@
+//$Id: ANSICaseFragment.java 4851 2004-12-02 05:09:49Z oneovthafew $
+package org.hibernate.sql;
+
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ An ANSI SQL CASE expression.
+ <br>
+ <code>case when ... then ... end as ...</code>
+ <br>
+ @author Gavin King, Simon Harris
+ */
+public class ANSICaseFragment extends CaseFragment {
+
+ public String toFragmentString() {
+
+ StringBuffer buf = new StringBuffer( cases.size() * 15 + 10 )
+ .append("case");
+
+ Iterator iter = cases.entrySet().iterator();
+ while ( iter.hasNext() ) {
+ Map.Entry me = (Map.Entry) iter.next();
+ buf.append(" when ")
+ .append( me.getKey() )
+ .append(" is not null then ")
+ .append( me.getValue() );
+ }
+
+ buf.append(" end");
+
+ if (returnColumnName!=null) {
+ buf.append(" as ")
+ .append(returnColumnName);
+ }
+
+ return buf.toString();
+ }
+
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/sql/ANSIJoinFragment.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/sql/ANSIJoinFragment.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/sql/ANSIJoinFragment.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,117 @@
+//$Id: ANSIJoinFragment.java 4886 2004-12-05 15:04:21Z pgmjsd $
+package org.hibernate.sql;
+
+import org.hibernate.AssertionFailure;
+
+/**
+ * An ANSI-style join
+ *
+ * @author Gavin King
+ */
+public class ANSIJoinFragment extends JoinFragment {
+
+ private StringBuffer buffer = new StringBuffer();
+ private StringBuffer conditions = new StringBuffer();
+
+ public void addJoin(String tableName, String alias, String[] fkColumns, String[] pkColumns, int joinType) {
+ addJoin(tableName, alias, fkColumns, pkColumns, joinType, null);
+ }
+
+ public void addJoin(String tableName, String alias, String[] fkColumns, String[] pkColumns, int joinType, String on) {
+ String joinString;
+ switch (joinType) {
+ case INNER_JOIN:
+ joinString = " inner join ";
+ break;
+ case LEFT_OUTER_JOIN:
+ joinString = " left outer join ";
+ break;
+ case RIGHT_OUTER_JOIN:
+ joinString = " right outer join ";
+ break;
+ case FULL_JOIN:
+ joinString = " full outer join ";
+ break;
+ default:
+ throw new AssertionFailure("undefined join type");
+ }
+
+ buffer.append(joinString)
+ .append(tableName)
+ .append(' ')
+ .append(alias)
+ .append(" on ");
+
+
+ for ( int j=0; j<fkColumns.length; j++) {
+ /*if ( fkColumns[j].indexOf('.')<1 ) {
+ throw new AssertionFailure("missing alias");
+ }*/
+ buffer.append( fkColumns[j] )
+ .append('=')
+ .append(alias)
+ .append('.')
+ .append( pkColumns[j] );
+ if ( j<fkColumns.length-1 ) buffer.append(" and ");
+ }
+
+ addCondition(buffer, on);
+
+ }
+
+ public String toFromFragmentString() {
+ return buffer.toString();
+ }
+
+ public String toWhereFragmentString() {
+ return conditions.toString();
+ }
+
+ public void addJoins(String fromFragment, String whereFragment) {
+ buffer.append(fromFragment);
+ //where fragment must be empty!
+ }
+
+ public JoinFragment copy() {
+ ANSIJoinFragment copy = new ANSIJoinFragment();
+ copy.buffer = new StringBuffer( buffer.toString() );
+ return copy;
+ }
+
+ public void addCondition(String alias, String[] columns, String condition) {
+ for ( int i=0; i<columns.length; i++ ) {
+ conditions.append(" and ")
+ .append(alias)
+ .append('.')
+ .append( columns[i] )
+ .append(condition);
+ }
+ }
+
+ public void addCrossJoin(String tableName, String alias) {
+ buffer.append(", ")
+ .append(tableName)
+ .append(' ')
+ .append(alias);
+ }
+
+ public void addCondition(String alias, String[] fkColumns, String[] pkColumns) {
+ throw new UnsupportedOperationException();
+
+ }
+
+ public boolean addCondition(String condition) {
+ return addCondition(conditions, condition);
+ }
+
+ public void addFromFragmentString(String fromFragmentString) {
+ buffer.append(fromFragmentString);
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/sql/Alias.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/sql/Alias.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/sql/Alias.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,90 @@
+//$Id: Alias.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate.sql;
+
+import org.hibernate.dialect.Dialect;
+
+/**
+ * An alias generator for SQL identifiers
+ * @author Gavin King
+ */
+public final class Alias {
+
+ private final int length;
+ private final String suffix;
+
+ /**
+ * Constructor for Alias.
+ */
+ public Alias(int length, String suffix) {
+ super();
+ this.length = (suffix==null) ? length : length - suffix.length();
+ this.suffix = suffix;
+ }
+
+ /**
+ * Constructor for Alias.
+ */
+ public Alias(String suffix) {
+ super();
+ this.length = Integer.MAX_VALUE;
+ this.suffix = suffix;
+ }
+
+ public String toAliasString(String sqlIdentifier) {
+ char begin = sqlIdentifier.charAt(0);
+ int quoteType = Dialect.QUOTE.indexOf(begin);
+ String unquoted = getUnquotedAliasString(sqlIdentifier, quoteType);
+ if ( quoteType >= 0 ) {
+ char endQuote = Dialect.CLOSED_QUOTE.charAt(quoteType);
+ return begin + unquoted + endQuote;
+ }
+ else {
+ return unquoted;
+ }
+ }
+
+ public String toUnquotedAliasString(String sqlIdentifier) {
+ return getUnquotedAliasString(sqlIdentifier);
+ }
+
+ private String getUnquotedAliasString(String sqlIdentifier) {
+ char begin = sqlIdentifier.charAt(0);
+ int quoteType = Dialect.QUOTE.indexOf(begin);
+ return getUnquotedAliasString(sqlIdentifier, quoteType);
+ }
+
+ private String getUnquotedAliasString(String sqlIdentifier, int quoteType) {
+ String unquoted = sqlIdentifier;
+ if ( quoteType >= 0 ) {
+ //if the identifier is quoted, remove the quotes
+ unquoted = unquoted.substring( 1, unquoted.length()-1 );
+ }
+ if ( unquoted.length() > length ) {
+ //truncate the identifier to the max alias length, less the suffix length
+ unquoted = unquoted.substring(0, length);
+ }
+ if ( suffix == null ) {
+ return unquoted;
+ }
+ else {
+ return unquoted + suffix;
+ }
+ }
+
+ public String[] toUnquotedAliasStrings(String[] sqlIdentifiers) {
+ String[] aliases = new String[ sqlIdentifiers.length ];
+ for ( int i=0; i<sqlIdentifiers.length; i++ ) {
+ aliases[i] = toUnquotedAliasString(sqlIdentifiers[i]);
+ }
+ return aliases;
+ }
+
+ public String[] toAliasStrings(String[] sqlIdentifiers) {
+ String[] aliases = new String[ sqlIdentifiers.length ];
+ for ( int i=0; i<sqlIdentifiers.length; i++ ) {
+ aliases[i] = toAliasString(sqlIdentifiers[i]);
+ }
+ return aliases;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/sql/CacheJoinFragment.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/sql/CacheJoinFragment.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/sql/CacheJoinFragment.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+//$Id: $
+package org.hibernate.sql;
+
+import org.hibernate.AssertionFailure;
+
+/**
+ * A Caché dialect join. Differs from ANSI only in that full outer join
+ * is not supported.
+ *
+ * @author Jeff Miller
+ * @author Jonathan Levinson
+ */
+public class CacheJoinFragment extends ANSIJoinFragment {
+
+ public void addJoin(String tableName, String alias, String[] fkColumns, String[] pkColumns, int joinType, String on) {
+ if ( joinType == FULL_JOIN ) {
+ throw new AssertionFailure( "Cache does not support full outer joins" );
+ }
+ super.addJoin( tableName, alias, fkColumns, pkColumns, joinType, on );
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/sql/CaseFragment.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/sql/CaseFragment.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/sql/CaseFragment.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,35 @@
+//$Id: CaseFragment.java 4070 2004-07-23 05:30:26Z steveebersole $
+package org.hibernate.sql;
+
+import java.util.Map;
+
+import org.hibernate.util.StringHelper;
+
+import org.apache.commons.collections.SequencedHashMap;
+
+/**
+ * Abstract SQL case fragment renderer
+ *
+ * @author Gavin King, Simon Harris
+ */
+public abstract class CaseFragment {
+ public abstract String toFragmentString();
+
+ protected String returnColumnName;
+
+ protected Map cases = new SequencedHashMap();
+
+ public CaseFragment setReturnColumnName(String returnColumnName) {
+ this.returnColumnName = returnColumnName;
+ return this;
+ }
+
+ public CaseFragment setReturnColumnName(String returnColumnName, String suffix) {
+ return setReturnColumnName( new Alias(suffix).toAliasString(returnColumnName) );
+ }
+
+ public CaseFragment addWhenColumnNotNull(String alias, String columnName, String value) {
+ cases.put( StringHelper.qualify(alias, columnName), value );
+ return this;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/sql/ConditionFragment.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/sql/ConditionFragment.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/sql/ConditionFragment.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,58 @@
+//$Id: ConditionFragment.java 4218 2004-08-10 05:06:14Z oneovthafew $
+package org.hibernate.sql;
+
+import org.hibernate.util.ArrayHelper;
+
+/**
+ * @author Gavin King
+ */
+public class ConditionFragment {
+ private String tableAlias;
+ private String[] lhs;
+ private String[] rhs;
+ private String op = "=";
+
+ /**
+ * Sets the op.
+ * @param op The op to set
+ */
+ public ConditionFragment setOp(String op) {
+ this.op = op;
+ return this;
+ }
+
+ /**
+ * Sets the tableAlias.
+ * @param tableAlias The tableAlias to set
+ */
+ public ConditionFragment setTableAlias(String tableAlias) {
+ this.tableAlias = tableAlias;
+ return this;
+ }
+
+ public ConditionFragment setCondition(String[] lhs, String[] rhs) {
+ this.lhs = lhs;
+ this.rhs = rhs;
+ return this;
+ }
+
+ public ConditionFragment setCondition(String[] lhs, String rhs) {
+ this.lhs = lhs;
+ this.rhs = ArrayHelper.fillArray(rhs, lhs.length);
+ return this;
+ }
+
+ public String toFragmentString() {
+ StringBuffer buf = new StringBuffer( lhs.length * 10 );
+ for ( int i=0; i<lhs.length; i++ ) {
+ buf.append(tableAlias)
+ .append('.')
+ .append( lhs[i] )
+ .append(op)
+ .append( rhs[i] );
+ if (i<lhs.length-1) buf.append(" and ");
+ }
+ return buf.toString();
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/sql/DecodeCaseFragment.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/sql/DecodeCaseFragment.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/sql/DecodeCaseFragment.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,48 @@
+//$Id: DecodeCaseFragment.java 4851 2004-12-02 05:09:49Z oneovthafew $
+package org.hibernate.sql;
+
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ An Oracle-style DECODE function.
+ <br>
+ <code>decode(pkvalue, key1, 1, key2, 2, ..., 0)</code>
+ <br>
+
+ @author Simon Harris
+ */
+public class DecodeCaseFragment extends CaseFragment {
+
+ public String toFragmentString() {
+
+ StringBuffer buf = new StringBuffer( cases.size() * 15 + 10 )
+ .append("decode(");
+
+ Iterator iter = cases.entrySet().iterator();
+ while ( iter.hasNext() ) {
+ Map.Entry me = (Map.Entry) iter.next();
+
+ if ( iter.hasNext() ) {
+ buf.append(", ")
+ .append( me.getKey() )
+ .append(", ")
+ .append( me.getValue() );
+ }
+ else {
+ buf.insert( 7, me.getKey() )
+ .append(", ")
+ .append( me.getValue() );
+ }
+ }
+
+ buf.append(')');
+
+ if (returnColumnName!=null) {
+ buf.append(" as ")
+ .append(returnColumnName);
+ }
+
+ return buf.toString();
+ }
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/sql/Delete.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/sql/Delete.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/sql/Delete.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,84 @@
+//$Id: Delete.java 10226 2006-08-05 04:27:55Z steve.ebersole at jboss.com $
+package org.hibernate.sql;
+
+import org.hibernate.util.StringHelper;
+
+/**
+ * An SQL <tt>DELETE</tt> statement
+ *
+ * @author Gavin King
+ */
+public class Delete {
+
+ private String tableName;
+ private String[] primaryKeyColumnNames;
+ private String versionColumnName;
+ private String where;
+
+ private String comment;
+ public Delete setComment(String comment) {
+ this.comment = comment;
+ return this;
+ }
+
+ public Delete setTableName(String tableName) {
+ this.tableName = tableName;
+ return this;
+ }
+
+ public String toStatementString() {
+ StringBuffer buf = new StringBuffer( tableName.length() + 10 );
+ if ( comment!=null ) {
+ buf.append( "/* " ).append(comment).append( " */ " );
+ }
+ buf.append( "delete from " ).append(tableName);
+ if ( where != null || primaryKeyColumnNames != null || versionColumnName != null ) {
+ buf.append( " where " );
+ }
+ boolean conditionsAppended = false;
+ if ( primaryKeyColumnNames != null ) {
+ buf.append( StringHelper.join( "=? and ", primaryKeyColumnNames ) ).append( "=?" );
+ conditionsAppended = true;
+ }
+ if ( where!=null ) {
+ if ( conditionsAppended ) {
+ buf.append( " and " );
+ }
+ buf.append( where );
+ conditionsAppended = true;
+ }
+ if ( versionColumnName!=null ) {
+ if ( conditionsAppended ) {
+ buf.append( " and " );
+ }
+ buf.append( versionColumnName ).append( "=?" );
+ }
+ return buf.toString();
+ }
+
+ public Delete setWhere(String where) {
+ this.where=where;
+ return this;
+ }
+
+ public Delete addWhereFragment(String fragment) {
+ if ( where == null ) {
+ where = fragment;
+ }
+ else {
+ where += ( " and " + fragment );
+ }
+ return this;
+ }
+
+ public Delete setPrimaryKeyColumnNames(String[] primaryKeyColumnNames) {
+ this.primaryKeyColumnNames = primaryKeyColumnNames;
+ return this;
+ }
+
+ public Delete setVersionColumnName(String versionColumnName) {
+ this.versionColumnName = versionColumnName;
+ return this;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/sql/DerbyCaseFragment.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/sql/DerbyCaseFragment.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/sql/DerbyCaseFragment.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,39 @@
+//$Id: DerbyCaseFragment.java 4631 2004-09-29 12:29:05Z pgmjsd $
+package org.hibernate.sql;
+
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * @author Simon Johnston
+ * @see org.hibernate.dialect.DerbyDialect
+ */
+public class DerbyCaseFragment extends CaseFragment {
+
+ /**
+ * From http://www.jroller.com/comments/kenlars99/Weblog/cloudscape_soon_to_be_derby
+ * <p/>
+ * The problem we had, was when Hibernate does a select with a case statement, for joined subclasses.
+ * This seems to be because there was no else at the end of the case statement (other dbs seem to not mind).
+ */
+ public String toFragmentString() {
+ StringBuffer buf = new StringBuffer( cases.size() * 15 + 10 );
+ buf.append( "case" ); //$NON-NLS-1
+ Iterator iter = cases.entrySet().iterator();
+ while ( iter.hasNext() ) {
+ Map.Entry me = ( Map.Entry ) iter.next();
+ buf.append( " when " )//$NON-NLS-1
+ .append( me.getKey() )
+ .append( " is not null then " )//$NON-NLS-1
+ .append( me.getValue() );
+ }
+ // null is not considered the same type as Integer.
+ buf.append( " else -1" ); //$NON-NLS-1
+ buf.append( " end" ); //$NON-NLS-1
+ if ( returnColumnName != null ) {
+ buf.append( " as " )//$NON-NLS-1
+ .append( returnColumnName );
+ }
+ return buf.toString();
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/sql/DerbyCaseFragment.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/sql/DisjunctionFragment.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/sql/DisjunctionFragment.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/sql/DisjunctionFragment.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,23 @@
+//$Id: DisjunctionFragment.java 3890 2004-06-03 16:31:32Z steveebersole $
+package org.hibernate.sql;
+
+/**
+ * A disjunctive string of conditions
+ * @author Gavin King
+ */
+public class DisjunctionFragment {
+
+ private StringBuffer buffer = new StringBuffer();
+
+ public DisjunctionFragment addCondition(ConditionFragment fragment) {
+ if ( buffer.length()>0 ) buffer.append(" or ");
+ buffer.append("(")
+ .append( fragment.toFragmentString() )
+ .append(")");
+ return this;
+ }
+
+ public String toFragmentString() {
+ return buffer.toString();
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/sql/ForUpdateFragment.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/sql/ForUpdateFragment.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/sql/ForUpdateFragment.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,79 @@
+//$Id: ForUpdateFragment.java 11320 2007-03-20 11:50:53Z steve.ebersole at jboss.com $
+package org.hibernate.sql;
+
+import java.util.Iterator;
+import java.util.Map;
+
+import org.hibernate.LockMode;
+import org.hibernate.QueryException;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.util.StringHelper;
+
+/**
+ * @author Gavin King
+ */
+public class ForUpdateFragment {
+ private final StringBuffer aliases = new StringBuffer();
+ private boolean isNowaitEnabled;
+ private final Dialect dialect;
+
+ public ForUpdateFragment(Dialect dialect) {
+ this.dialect = dialect;
+ }
+
+ public ForUpdateFragment(Dialect dialect, Map lockModes, Map keyColumnNames) throws QueryException {
+ this( dialect );
+ LockMode upgradeType = null;
+ Iterator iter = lockModes.entrySet().iterator();
+ while ( iter.hasNext() ) {
+ final Map.Entry me = ( Map.Entry ) iter.next();
+ final LockMode lockMode = ( LockMode ) me.getValue();
+ if ( LockMode.READ.lessThan( lockMode ) ) {
+ final String tableAlias = ( String ) me.getKey();
+ if ( dialect.forUpdateOfColumns() ) {
+ String[] keyColumns = ( String[] ) keyColumnNames.get( tableAlias ); //use the id column alias
+ if ( keyColumns == null ) {
+ throw new IllegalArgumentException( "alias not found: " + tableAlias );
+ }
+ keyColumns = StringHelper.qualify( tableAlias, keyColumns );
+ for ( int i = 0; i < keyColumns.length; i++ ) {
+ addTableAlias( keyColumns[i] );
+ }
+ }
+ else {
+ addTableAlias( tableAlias );
+ }
+ if ( upgradeType != null && lockMode != upgradeType ) {
+ throw new QueryException( "mixed LockModes" );
+ }
+ upgradeType = lockMode;
+ }
+ }
+
+ if ( upgradeType == LockMode.UPGRADE_NOWAIT ) {
+ setNowaitEnabled( true );
+ }
+ }
+
+ public ForUpdateFragment addTableAlias(String alias) {
+ if ( aliases.length() > 0 ) {
+ aliases.append( ", " );
+ }
+ aliases.append( alias );
+ return this;
+ }
+
+ public String toFragmentString() {
+ if ( aliases.length() == 0 ) {
+ return "";
+ }
+ return isNowaitEnabled ?
+ dialect.getForUpdateNowaitString( aliases.toString() ) :
+ dialect.getForUpdateString( aliases.toString() );
+ }
+
+ public ForUpdateFragment setNowaitEnabled(boolean nowait) {
+ isNowaitEnabled = nowait;
+ return this;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/sql/HSQLCaseFragment.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/sql/HSQLCaseFragment.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/sql/HSQLCaseFragment.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,41 @@
+//$Id: HSQLCaseFragment.java 3890 2004-06-03 16:31:32Z steveebersole $
+package org.hibernate.sql;
+
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * The HSQL CASEWHEN function.
+ * <br>
+ * <code>casewhen(..., ..., ...) as ...</code>
+ * <br>
+ * @author Wolfgang Jung
+ */
+public class HSQLCaseFragment extends CaseFragment {
+
+ public String toFragmentString() {
+ StringBuffer buf = new StringBuffer( cases.size() * 15 + 10 );
+ StringBuffer buf2 = new StringBuffer( cases.size() );
+
+ Iterator iter = cases.entrySet().iterator();
+ while ( iter.hasNext() ) {
+ Map.Entry me = (Map.Entry) iter.next();
+ buf.append(" casewhen(")
+ .append( me.getKey() )
+ .append(" is not null")
+ .append(", ")
+ .append( me.getValue() )
+ .append(", ");
+ buf2.append(")");
+ }
+
+ buf.append("-1"); //null caused some problems
+ buf.append( buf2.toString() );
+ if ( returnColumnName!=null ) {
+ buf.append(" as ")
+ .append(returnColumnName);
+ }
+ return buf.toString();
+ }
+}
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/sql/InFragment.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/sql/InFragment.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/sql/InFragment.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,94 @@
+//$Id: InFragment.java 7022 2005-06-05 06:36:26Z oneovthafew $
+package org.hibernate.sql;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.hibernate.util.StringHelper;
+
+/**
+ * An SQL IN expression.
+ * <br>
+ * <code>... in(...)</code>
+ * <br>
+ * @author Gavin King
+ */
+public class InFragment {
+
+ public static final String NULL = "null";
+ public static final String NOT_NULL = "not null";
+
+ private String columnName;
+ private List values = new ArrayList();
+
+ /**
+ * @param value, an SQL literal, NULL, or NOT_NULL
+ */
+ public InFragment addValue(Object value) {
+ values.add(value);
+ return this;
+ }
+
+ public InFragment setColumn(String columnName) {
+ this.columnName = columnName;
+ return this;
+ }
+
+ public InFragment setColumn(String alias, String columnName) {
+ this.columnName = StringHelper.qualify(alias, columnName);
+ return setColumn(this.columnName);
+ }
+
+ public InFragment setFormula(String alias, String formulaTemplate) {
+ this.columnName = StringHelper.replace(formulaTemplate, Template.TEMPLATE, alias);
+ return setColumn(this.columnName);
+ }
+
+ public String toFragmentString() {
+ if ( values.size()==0 ) return "1=2";
+ StringBuffer buf = new StringBuffer( values.size() * 5 );
+ buf.append(columnName);
+ //following doesn't handle (null, not null) but unnecessary
+ //since this would mean all rows
+ if ( values.size()>1 ) {
+ boolean allowNull = false;
+ buf.append(" in (");
+ Iterator iter = values.iterator();
+ while ( iter.hasNext() ) {
+ Object value = iter.next();
+ if ( NULL.equals(value) ) {
+ allowNull = true;
+ }
+ else if ( NOT_NULL.equals(value) ) {
+ throw new IllegalArgumentException("not null makes no sense for in expression");
+ }
+ else {
+ buf.append(value);
+ buf.append(", ");
+ }
+ }
+ buf.setLength( buf.length()-2 );
+ buf.append(')');
+ if (allowNull) {
+ buf.insert(0, " is null or ")
+ .insert(0, columnName)
+ .insert(0, '(')
+ .append(')');
+ }
+ }
+ else {
+ Object value = values.iterator().next();
+ if ( NULL.equals(value) ) {
+ buf.append(" is null");
+ }
+ else if ( NOT_NULL.equals(value) ) {
+ buf.append(" is not null");
+ }
+ else {
+ buf.append("=").append(value);
+ }
+ }
+ return buf.toString();
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/sql/Insert.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/sql/Insert.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/sql/Insert.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,108 @@
+//$Id: Insert.java 9681 2006-03-24 18:10:04Z steve.ebersole at jboss.com $
+package org.hibernate.sql;
+
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.commons.collections.SequencedHashMap;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.type.LiteralType;
+
+/**
+ * An SQL <tt>INSERT</tt> statement
+ *
+ * @author Gavin King
+ */
+public class Insert {
+ private Dialect dialect;
+ private String tableName;
+ private String comment;
+ private Map columns = new SequencedHashMap();
+
+ public Insert(Dialect dialect) {
+ this.dialect = dialect;
+ }
+
+ protected Dialect getDialect() {
+ return dialect;
+ }
+
+ public Insert setComment(String comment) {
+ this.comment = comment;
+ return this;
+ }
+
+ public Insert addColumn(String columnName) {
+ return addColumn(columnName, "?");
+ }
+
+ public Insert addColumns(String[] columnNames) {
+ for ( int i=0; i<columnNames.length; i++ ) {
+ addColumn( columnNames[i] );
+ }
+ return this;
+ }
+
+ public Insert addColumns(String[] columnNames, boolean[] insertable) {
+ for ( int i=0; i<columnNames.length; i++ ) {
+ if ( insertable[i] ) {
+ addColumn( columnNames[i] );
+ }
+ }
+ return this;
+ }
+
+ public Insert addColumn(String columnName, String value) {
+ columns.put(columnName, value);
+ return this;
+ }
+
+ public Insert addColumn(String columnName, Object value, LiteralType type) throws Exception {
+ return addColumn( columnName, type.objectToSQLString(value, dialect) );
+ }
+
+ public Insert addIdentityColumn(String columnName) {
+ String value = dialect.getIdentityInsertString();
+ if ( value != null ) {
+ addColumn( columnName, value );
+ }
+ return this;
+ }
+
+ public Insert setTableName(String tableName) {
+ this.tableName = tableName;
+ return this;
+ }
+
+ public String toStatementString() {
+ StringBuffer buf = new StringBuffer( columns.size()*15 + tableName.length() + 10 );
+ if ( comment != null ) {
+ buf.append( "/* " ).append( comment ).append( " */ " );
+ }
+ buf.append("insert into ")
+ .append(tableName);
+ if ( columns.size()==0 ) {
+ buf.append(' ').append( dialect.getNoColumnsInsertString() );
+ }
+ else {
+ buf.append(" (");
+ Iterator iter = columns.keySet().iterator();
+ while ( iter.hasNext() ) {
+ buf.append( iter.next() );
+ if ( iter.hasNext() ) {
+ buf.append( ", " );
+ }
+ }
+ buf.append(") values (");
+ iter = columns.values().iterator();
+ while ( iter.hasNext() ) {
+ buf.append( iter.next() );
+ if ( iter.hasNext() ) {
+ buf.append( ", " );
+ }
+ }
+ buf.append(')');
+ }
+ return buf.toString();
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/sql/InsertSelect.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/sql/InsertSelect.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/sql/InsertSelect.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,78 @@
+// $Id: InsertSelect.java 7057 2005-06-07 20:06:10Z steveebersole $
+package org.hibernate.sql;
+
+import org.hibernate.dialect.Dialect;
+import org.hibernate.HibernateException;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * Implementation of InsertSelect.
+ *
+ * @author Steve Ebersole
+ */
+public class InsertSelect {
+
+ private Dialect dialect;
+ private String tableName;
+ private String comment;
+ private List columnNames = new ArrayList();
+ private Select select;
+
+ public InsertSelect(Dialect dialect) {
+ this.dialect = dialect;
+ }
+
+ public InsertSelect setTableName(String tableName) {
+ this.tableName = tableName;
+ return this;
+ }
+
+ public InsertSelect setComment(String comment) {
+ this.comment = comment;
+ return this;
+ }
+
+ public InsertSelect addColumn(String columnName) {
+ columnNames.add( columnName );
+ return this;
+ }
+
+ public InsertSelect addColumns(String[] columnNames) {
+ for ( int i = 0; i < columnNames.length; i++ ) {
+ this.columnNames.add( columnNames[i] );
+ }
+ return this;
+ }
+
+ public InsertSelect setSelect(Select select) {
+ this.select = select;
+ return this;
+ }
+
+ public String toStatementString() {
+ if ( tableName == null ) throw new HibernateException( "no table name defined for insert-select" );
+ if ( select == null ) throw new HibernateException( "no select defined for insert-select" );
+
+ StringBuffer buf = new StringBuffer( (columnNames.size() * 15) + tableName.length() + 10 );
+ if ( comment!=null ) {
+ buf.append( "/* " ).append( comment ).append( " */ " );
+ }
+ buf.append( "insert into " ).append( tableName );
+ if ( !columnNames.isEmpty() ) {
+ buf.append( " (" );
+ Iterator itr = columnNames.iterator();
+ while ( itr.hasNext() ) {
+ buf.append( itr.next() );
+ if ( itr.hasNext() ) {
+ buf.append( ", " );
+ }
+ }
+ buf.append( ")" );
+ }
+ buf.append( ' ' ).append( select.toStatementString() );
+ return buf.toString();
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/sql/JoinFragment.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/sql/JoinFragment.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/sql/JoinFragment.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,87 @@
+//$Id: JoinFragment.java 6034 2005-03-07 12:31:37Z pgmjsd $
+package org.hibernate.sql;
+
+import org.hibernate.util.StringHelper;
+
+/**
+ * An abstract SQL join fragment renderer
+ *
+ * @author Gavin King
+ */
+public abstract class JoinFragment {
+
+ public abstract void addJoin(String tableName, String alias, String[] fkColumns, String[] pkColumns, int joinType);
+
+ public abstract void addJoin(String tableName, String alias, String[] fkColumns, String[] pkColumns, int joinType, String on);
+
+ public abstract void addCrossJoin(String tableName, String alias);
+
+ public abstract void addJoins(String fromFragment, String whereFragment);
+
+ public abstract String toFromFragmentString();
+
+ public abstract String toWhereFragmentString();
+
+ // --Commented out by Inspection (12/4/04 9:10 AM): public abstract void addCondition(String alias, String[] columns, String condition);
+ public abstract void addCondition(String alias, String[] fkColumns, String[] pkColumns);
+
+ public abstract boolean addCondition(String condition);
+ // --Commented out by Inspection (12/4/04 9:10 AM): public abstract void addFromFragmentString(String fromFragmentString);
+
+ public abstract JoinFragment copy();
+
+ public static final int INNER_JOIN = 0;
+ public static final int FULL_JOIN = 4;
+ public static final int LEFT_OUTER_JOIN = 1;
+ public static final int RIGHT_OUTER_JOIN = 2;
+
+ private boolean hasFilterCondition = false;
+ private boolean hasThetaJoins = false;
+
+ public void addFragment(JoinFragment ojf) {
+ if ( ojf.hasThetaJoins() ) {
+ hasThetaJoins = true;
+ }
+ addJoins( ojf.toFromFragmentString(), ojf.toWhereFragmentString() );
+ }
+
+ /**
+ * Appends the 'on' condition to the buffer, returning true if the condition was added.
+ * Returns false if the 'on' condition was empty.
+ *
+ * @param buffer The buffer to append the 'on' condition to.
+ * @param on The 'on' condition.
+ * @return Returns true if the condition was added, false if the condition was already in 'on' string.
+ */
+ protected boolean addCondition(StringBuffer buffer, String on) {
+ if ( StringHelper.isNotEmpty( on ) ) {
+ if ( !on.startsWith( " and" ) ) buffer.append( " and " );
+ buffer.append( on );
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ /**
+ * True if the where fragment is from a filter condition.
+ *
+ * @return True if the where fragment is from a filter condition.
+ */
+ public boolean hasFilterCondition() {
+ return hasFilterCondition;
+ }
+
+ public void setHasFilterCondition(boolean b) {
+ this.hasFilterCondition = b;
+ }
+
+ public boolean hasThetaJoins() {
+ return hasThetaJoins;
+ }
+
+ public void setHasThetaJoins(boolean hasThetaJoins) {
+ this.hasThetaJoins = hasThetaJoins;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/sql/MckoiCaseFragment.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/sql/MckoiCaseFragment.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/sql/MckoiCaseFragment.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,42 @@
+//$Id: MckoiCaseFragment.java 3890 2004-06-03 16:31:32Z steveebersole $
+package org.hibernate.sql;
+
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * A Mckoi IF function.
+ * <br>
+ * <code>if(..., ..., ...) as ...</code>
+ * <br>
+ * @author Gavin King
+ */
+public class MckoiCaseFragment extends CaseFragment {
+
+ public String toFragmentString() {
+ StringBuffer buf = new StringBuffer( cases.size() * 15 + 10 );
+ StringBuffer buf2= new StringBuffer( cases.size() );
+
+ Iterator iter = cases.entrySet().iterator();
+ while ( iter.hasNext() ) {
+ Map.Entry me = (Map.Entry) iter.next();
+ buf.append(" if(")
+ .append( me.getKey() )
+ .append(" is not null")
+ .append(", ")
+ .append( me.getValue() )
+ .append(", ");
+ buf2.append(")");
+ }
+
+ buf.append("null");
+ buf.append(buf2);
+ if (returnColumnName!=null) {
+ buf.append(" as ")
+ .append(returnColumnName);
+ }
+
+ return buf.toString();
+ }
+}
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/sql/OracleJoinFragment.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/sql/OracleJoinFragment.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/sql/OracleJoinFragment.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,127 @@
+//$Id: OracleJoinFragment.java 6750 2005-05-11 15:26:04Z oneovthafew $
+package org.hibernate.sql;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * An Oracle-style (theta) join
+ *
+ * @author Jon Lipsky, Gavin King
+ */
+public class OracleJoinFragment extends JoinFragment {
+
+ private StringBuffer afterFrom = new StringBuffer();
+ private StringBuffer afterWhere = new StringBuffer();
+
+ public void addJoin(String tableName, String alias, String[] fkColumns, String[] pkColumns, int joinType) {
+
+ addCrossJoin( tableName, alias );
+
+ for ( int j = 0; j < fkColumns.length; j++ ) {
+ setHasThetaJoins( true );
+ afterWhere.append( " and " )
+ .append( fkColumns[j] );
+ if ( joinType == RIGHT_OUTER_JOIN || joinType == FULL_JOIN ) afterWhere.append( "(+)" );
+ afterWhere.append( '=' )
+ .append( alias )
+ .append( '.' )
+ .append( pkColumns[j] );
+ if ( joinType == LEFT_OUTER_JOIN || joinType == FULL_JOIN ) afterWhere.append( "(+)" );
+ }
+
+ }
+
+ public String toFromFragmentString() {
+ return afterFrom.toString();
+ }
+
+ public String toWhereFragmentString() {
+ return afterWhere.toString();
+ }
+
+ public void addJoins(String fromFragment, String whereFragment) {
+ afterFrom.append( fromFragment );
+ afterWhere.append( whereFragment );
+ }
+
+ public JoinFragment copy() {
+ OracleJoinFragment copy = new OracleJoinFragment();
+ copy.afterFrom = new StringBuffer( afterFrom.toString() );
+ copy.afterWhere = new StringBuffer( afterWhere.toString() );
+ return copy;
+ }
+
+ public void addCondition(String alias, String[] columns, String condition) {
+ for ( int i = 0; i < columns.length; i++ ) {
+ afterWhere.append( " and " )
+ .append( alias )
+ .append( '.' )
+ .append( columns[i] )
+ .append( condition );
+ }
+ }
+
+ public void addCrossJoin(String tableName, String alias) {
+ afterFrom.append( ", " )
+ .append( tableName )
+ .append( ' ' )
+ .append( alias );
+ }
+
+ public void addCondition(String alias, String[] fkColumns, String[] pkColumns) {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean addCondition(String condition) {
+ return addCondition( afterWhere, condition );
+ }
+
+ public void addFromFragmentString(String fromFragmentString) {
+ afterFrom.append( fromFragmentString );
+ }
+
+ public void addJoin(String tableName, String alias, String[] fkColumns, String[] pkColumns, int joinType, String on) {
+ //arbitrary on clause ignored!!
+ addJoin( tableName, alias, fkColumns, pkColumns, joinType );
+ if ( joinType == JoinFragment.INNER_JOIN ) {
+ addCondition( on );
+ }
+ else if ( joinType == JoinFragment.LEFT_OUTER_JOIN ) {
+ addLeftOuterJoinCondition( on );
+ }
+ else {
+ throw new UnsupportedOperationException( "join type not supported by OracleJoinFragment (use Oracle9Dialect)" );
+ }
+ }
+
+ /**
+ * This method is a bit of a hack, and assumes
+ * that the column on the "right" side of the
+ * join appears on the "left" side of the
+ * operator, which is extremely wierd if this
+ * was a normal join condition, but is natural
+ * for a filter.
+ */
+ private void addLeftOuterJoinCondition(String on) {
+ StringBuffer buf = new StringBuffer( on );
+ for ( int i = 0; i < buf.length(); i++ ) {
+ char character = buf.charAt( i );
+ boolean isInsertPoint = OPERATORS.contains( new Character( character ) ) ||
+ ( character == ' ' && buf.length() > i + 3 && "is ".equals( buf.substring( i + 1, i + 4 ) ) );
+ if ( isInsertPoint ) {
+ buf.insert( i, "(+)" );
+ i += 3;
+ }
+ }
+ addCondition( buf.toString() );
+ }
+
+ private static final Set OPERATORS = new HashSet();
+
+ static {
+ OPERATORS.add( new Character( '=' ) );
+ OPERATORS.add( new Character( '<' ) );
+ OPERATORS.add( new Character( '>' ) );
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/sql/QueryJoinFragment.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/sql/QueryJoinFragment.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/sql/QueryJoinFragment.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,129 @@
+//$Id: QueryJoinFragment.java 4889 2004-12-05 17:20:21Z pgmjsd $
+package org.hibernate.sql;
+
+import org.hibernate.dialect.Dialect;
+
+/**
+ * A join that appears in a translated HQL query
+ *
+ * @author Gavin King
+ */
+public class QueryJoinFragment extends JoinFragment {
+
+ private StringBuffer afterFrom = new StringBuffer();
+ private StringBuffer afterWhere = new StringBuffer();
+ private Dialect dialect;
+ private boolean useThetaStyleInnerJoins;
+
+ public QueryJoinFragment(Dialect dialect, boolean useThetaStyleInnerJoins) {
+ this.dialect = dialect;
+ this.useThetaStyleInnerJoins = useThetaStyleInnerJoins;
+ }
+
+ public void addJoin(String tableName, String alias, String[] fkColumns, String[] pkColumns, int joinType) {
+ addJoin( tableName, alias, alias, fkColumns, pkColumns, joinType, null );
+ }
+
+ public void addJoin(String tableName, String alias, String[] fkColumns, String[] pkColumns, int joinType, String on) {
+ addJoin( tableName, alias, alias, fkColumns, pkColumns, joinType, on );
+ }
+
+ private void addJoin(String tableName, String alias, String concreteAlias, String[] fkColumns, String[] pkColumns, int joinType, String on) {
+ if ( !useThetaStyleInnerJoins || joinType != INNER_JOIN ) {
+ JoinFragment jf = dialect.createOuterJoinFragment();
+ jf.addJoin( tableName, alias, fkColumns, pkColumns, joinType, on );
+ addFragment( jf );
+ }
+ else {
+ addCrossJoin( tableName, alias );
+ addCondition( concreteAlias, fkColumns, pkColumns );
+ addCondition( on );
+ }
+ }
+
+ public String toFromFragmentString() {
+ return afterFrom.toString();
+ }
+
+ public String toWhereFragmentString() {
+ return afterWhere.toString();
+ }
+
+ public void addJoins(String fromFragment, String whereFragment) {
+ afterFrom.append( fromFragment );
+ afterWhere.append( whereFragment );
+ }
+
+ public JoinFragment copy() {
+ QueryJoinFragment copy = new QueryJoinFragment( dialect, useThetaStyleInnerJoins );
+ copy.afterFrom = new StringBuffer( afterFrom.toString() );
+ copy.afterWhere = new StringBuffer( afterWhere.toString() );
+ return copy;
+ }
+
+ public void addCondition(String alias, String[] columns, String condition) {
+ for ( int i = 0; i < columns.length; i++ ) {
+ afterWhere.append( " and " )
+ .append( alias )
+ .append( '.' )
+ .append( columns[i] )
+ .append( condition );
+ }
+ }
+
+
+ public void addCrossJoin(String tableName, String alias) {
+ afterFrom.append( ", " )
+ .append( tableName )
+ .append( ' ' )
+ .append( alias );
+ }
+
+ public void addCondition(String alias, String[] fkColumns, String[] pkColumns) {
+ for ( int j = 0; j < fkColumns.length; j++ ) {
+ afterWhere.append( " and " )
+ .append( fkColumns[j] )
+ .append( '=' )
+ .append( alias )
+ .append( '.' )
+ .append( pkColumns[j] );
+ }
+ }
+
+ /**
+ * Add the condition string to the join fragment.
+ *
+ * @param condition
+ * @return true if the condition was added, false if it was already in the fragment.
+ */
+ public boolean addCondition(String condition) {
+ // if the condition is not already there...
+ if (
+ afterFrom.toString().indexOf( condition.trim() ) < 0 &&
+ afterWhere.toString().indexOf( condition.trim() ) < 0
+ ) {
+ if ( !condition.startsWith( " and " ) ) {
+ afterWhere.append( " and " );
+ }
+ afterWhere.append( condition );
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ public void addFromFragmentString(String fromFragmentString) {
+ afterFrom.append( fromFragmentString );
+ }
+
+ public void clearWherePart() {
+ afterWhere.setLength( 0 );
+ }
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/sql/QuerySelect.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/sql/QuerySelect.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/sql/QuerySelect.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,190 @@
+//$Id: QuerySelect.java 6999 2005-06-03 02:04:13Z oneovthafew $
+package org.hibernate.sql;
+
+import java.util.HashSet;
+import java.util.Iterator;
+
+import org.hibernate.dialect.Dialect;
+
+/**
+ * A translated HQL query
+ * @author Gavin King
+ */
+public class QuerySelect {
+ private Dialect dialect;
+ private JoinFragment joins;
+ private StringBuffer select = new StringBuffer();
+ private StringBuffer where = new StringBuffer();
+ private StringBuffer groupBy = new StringBuffer();
+ private StringBuffer orderBy = new StringBuffer();
+ private StringBuffer having = new StringBuffer();
+ private String comment;
+ private boolean distinct=false;
+
+ private static final HashSet DONT_SPACE_TOKENS = new HashSet();
+ static {
+ //dontSpace.add("'");
+ DONT_SPACE_TOKENS.add(".");
+ DONT_SPACE_TOKENS.add("+");
+ DONT_SPACE_TOKENS.add("-");
+ DONT_SPACE_TOKENS.add("/");
+ DONT_SPACE_TOKENS.add("*");
+ DONT_SPACE_TOKENS.add("<");
+ DONT_SPACE_TOKENS.add(">");
+ DONT_SPACE_TOKENS.add("=");
+ DONT_SPACE_TOKENS.add("#");
+ DONT_SPACE_TOKENS.add("~");
+ DONT_SPACE_TOKENS.add("|");
+ DONT_SPACE_TOKENS.add("&");
+ DONT_SPACE_TOKENS.add("<=");
+ DONT_SPACE_TOKENS.add(">=");
+ DONT_SPACE_TOKENS.add("=>");
+ DONT_SPACE_TOKENS.add("=<");
+ DONT_SPACE_TOKENS.add("!=");
+ DONT_SPACE_TOKENS.add("<>");
+ DONT_SPACE_TOKENS.add("!#");
+ DONT_SPACE_TOKENS.add("!~");
+ DONT_SPACE_TOKENS.add("!<");
+ DONT_SPACE_TOKENS.add("!>");
+ DONT_SPACE_TOKENS.add("("); //for MySQL
+ DONT_SPACE_TOKENS.add(")");
+ }
+
+ public QuerySelect(Dialect dialect) {
+ this.dialect = dialect;
+ joins = new QueryJoinFragment(dialect, false);
+ }
+
+ public JoinFragment getJoinFragment() {
+ return joins;
+ }
+
+ public void addSelectFragmentString(String fragment) {
+ if ( fragment.length()>0 && fragment.charAt(0)==',' ) fragment = fragment.substring(1);
+ fragment = fragment.trim();
+ if ( fragment.length()>0 ) {
+ if ( select.length()>0 ) select.append(", ");
+ select.append(fragment);
+ }
+ }
+
+ public void addSelectColumn(String columnName, String alias) {
+ addSelectFragmentString(columnName + ' ' + alias);
+ }
+
+ public void setDistinct(boolean distinct) {
+ this.distinct = distinct;
+ }
+
+ public void setWhereTokens(Iterator tokens) {
+ //if ( conjunctiveWhere.length()>0 ) conjunctiveWhere.append(" and ");
+ appendTokens(where, tokens);
+ }
+
+ public void prependWhereConditions(String conditions) {
+ if (where.length() > 0) {
+ where.insert(0, conditions + " and ");
+ }
+ else {
+ where.append(conditions);
+ }
+ }
+
+ public void setGroupByTokens(Iterator tokens) {
+ //if ( groupBy.length()>0 ) groupBy.append(" and ");
+ appendTokens(groupBy, tokens);
+ }
+
+ public void setOrderByTokens(Iterator tokens) {
+ //if ( orderBy.length()>0 ) orderBy.append(" and ");
+ appendTokens(orderBy, tokens);
+ }
+
+ public void setHavingTokens(Iterator tokens) {
+ //if ( having.length()>0 ) having.append(" and ");
+ appendTokens(having, tokens);
+ }
+
+ public void addOrderBy(String orderByString) {
+ if ( orderBy.length() > 0 ) orderBy.append(", ");
+ orderBy.append(orderByString);
+ }
+
+ public String toQueryString() {
+ StringBuffer buf = new StringBuffer(50);
+ if (comment!=null) buf.append("/* ").append(comment).append(" */ ");
+ buf.append("select ");
+ if (distinct) buf.append("distinct ");
+ String from = joins.toFromFragmentString();
+ if ( from.startsWith(",") ) {
+ from = from.substring(1);
+ }
+ else if ( from.startsWith(" inner join") ){
+ from = from.substring(11);
+ }
+
+ buf.append( select.toString() )
+ .append(" from")
+ .append(from);
+
+ String outerJoinsAfterWhere = joins.toWhereFragmentString().trim();
+ String whereConditions = where.toString().trim();
+ boolean hasOuterJoinsAfterWhere = outerJoinsAfterWhere.length() > 0;
+ boolean hasWhereConditions = whereConditions.length() > 0;
+ if (hasOuterJoinsAfterWhere || hasWhereConditions) {
+ buf.append(" where ");
+ if (hasOuterJoinsAfterWhere) {
+ buf.append( outerJoinsAfterWhere.substring(4) );
+ }
+ if (hasWhereConditions) {
+ if (hasOuterJoinsAfterWhere) {
+ buf.append(" and (");
+ }
+ buf.append(whereConditions);
+ if (hasOuterJoinsAfterWhere) {
+ buf.append(")");
+ }
+ }
+ }
+
+ if ( groupBy.length() > 0 ) buf.append(" group by ").append( groupBy.toString() );
+ if ( having.length() > 0 ) buf.append(" having ").append( having.toString() );
+ if ( orderBy.length() > 0 ) buf.append(" order by ").append( orderBy.toString() );
+
+ return dialect.transformSelectString( buf.toString() );
+ }
+
+ private static void appendTokens(StringBuffer buf, Iterator iter) {
+ boolean lastSpaceable=true;
+ boolean lastQuoted=false;
+ while ( iter.hasNext() ) {
+ String token = (String) iter.next();
+ boolean spaceable = !DONT_SPACE_TOKENS.contains(token);
+ boolean quoted = token.startsWith("'");
+ if (spaceable && lastSpaceable) {
+ if ( !quoted || !lastQuoted ) buf.append(' ');
+ }
+ lastSpaceable = spaceable;
+ buf.append(token);
+ lastQuoted = token.endsWith("'");
+ }
+ }
+
+ public void setComment(String comment) {
+ this.comment = comment;
+ }
+
+ public QuerySelect copy() {
+ QuerySelect copy = new QuerySelect(dialect);
+ copy.joins = this.joins.copy();
+ copy.select.append( this.select.toString() );
+ copy.where.append( this.where.toString() );
+ copy.groupBy.append( this.groupBy.toString() );
+ copy.orderBy.append( this.orderBy.toString() );
+ copy.having.append( this.having.toString() );
+ copy.comment = this.comment;
+ copy.distinct = this.distinct;
+ return copy;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/sql/Select.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/sql/Select.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/sql/Select.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,155 @@
+//$Id: Select.java 7143 2005-06-15 02:57:03Z oneovthafew $
+package org.hibernate.sql;
+
+import org.hibernate.LockMode;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.util.StringHelper;
+
+
+/**
+ * A simple SQL <tt>SELECT</tt> statement
+ * @author Gavin King
+ */
+public class Select {
+
+ private String selectClause;
+ private String fromClause;
+ private String outerJoinsAfterFrom;
+ private String whereClause;
+ private String outerJoinsAfterWhere;
+ private String orderByClause;
+ private String groupByClause;
+ private String comment;
+ private LockMode lockMode;
+ public final Dialect dialect;
+
+ private int guesstimatedBufferSize = 20;
+
+ public Select(Dialect dialect) {
+ this.dialect = dialect;
+ }
+
+ /**
+ * Construct an SQL <tt>SELECT</tt> statement from the given clauses
+ */
+ public String toStatementString() {
+ StringBuffer buf = new StringBuffer(guesstimatedBufferSize);
+ if ( StringHelper.isNotEmpty(comment) ) {
+ buf.append("/* ").append(comment).append(" */ ");
+ }
+
+ buf.append("select ").append(selectClause)
+ .append(" from ").append(fromClause);
+
+ if ( StringHelper.isNotEmpty(outerJoinsAfterFrom) ) {
+ buf.append(outerJoinsAfterFrom);
+ }
+
+ if ( StringHelper.isNotEmpty(whereClause) || StringHelper.isNotEmpty(outerJoinsAfterWhere) ) {
+ buf.append(" where " );
+ // the outerJoinsAfterWhere needs to come before where clause to properly
+ // handle dynamic filters
+ if ( StringHelper.isNotEmpty(outerJoinsAfterWhere) ) {
+ buf.append(outerJoinsAfterWhere);
+ if ( StringHelper.isNotEmpty(whereClause) ) {
+ buf.append( " and " );
+ }
+ }
+ if ( StringHelper.isNotEmpty(whereClause) ) {
+ buf.append(whereClause);
+ }
+ }
+
+ if ( StringHelper.isNotEmpty(groupByClause) ) {
+ buf.append(" group by ").append(groupByClause);
+ }
+
+ if ( StringHelper.isNotEmpty(orderByClause) ) {
+ buf.append(" order by ").append(orderByClause);
+ }
+
+ if (lockMode!=null) {
+ buf.append( dialect.getForUpdateString(lockMode) );
+ }
+
+ return dialect.transformSelectString( buf.toString() );
+ }
+
+ /**
+ * Sets the fromClause.
+ * @param fromClause The fromClause to set
+ */
+ public Select setFromClause(String fromClause) {
+ this.fromClause = fromClause;
+ this.guesstimatedBufferSize += fromClause.length();
+ return this;
+ }
+
+ public Select setFromClause(String tableName, String alias) {
+ this.fromClause = tableName + ' ' + alias;
+ this.guesstimatedBufferSize += fromClause.length();
+ return this;
+ }
+
+ public Select setOrderByClause(String orderByClause) {
+ this.orderByClause = orderByClause;
+ this.guesstimatedBufferSize += orderByClause.length();
+ return this;
+ }
+
+ public Select setGroupByClause(String groupByClause) {
+ this.groupByClause = groupByClause;
+ this.guesstimatedBufferSize += groupByClause.length();
+ return this;
+ }
+
+ public Select setOuterJoins(String outerJoinsAfterFrom, String outerJoinsAfterWhere) {
+ this.outerJoinsAfterFrom = outerJoinsAfterFrom;
+
+ // strip off any leading 'and' token
+ String tmpOuterJoinsAfterWhere = outerJoinsAfterWhere.trim();
+ if ( tmpOuterJoinsAfterWhere.startsWith("and") ) {
+ tmpOuterJoinsAfterWhere = tmpOuterJoinsAfterWhere.substring(4);
+ }
+ this.outerJoinsAfterWhere = tmpOuterJoinsAfterWhere;
+
+ this.guesstimatedBufferSize += outerJoinsAfterFrom.length() + outerJoinsAfterWhere.length();
+ return this;
+ }
+
+
+ /**
+ * Sets the selectClause.
+ * @param selectClause The selectClause to set
+ */
+ public Select setSelectClause(String selectClause) {
+ this.selectClause = selectClause;
+ this.guesstimatedBufferSize += selectClause.length();
+ return this;
+ }
+
+ /**
+ * Sets the whereClause.
+ * @param whereClause The whereClause to set
+ */
+ public Select setWhereClause(String whereClause) {
+ this.whereClause = whereClause;
+ this.guesstimatedBufferSize += whereClause.length();
+ return this;
+ }
+
+ public Select setComment(String comment) {
+ this.comment = comment;
+ this.guesstimatedBufferSize += comment.length();
+ return this;
+ }
+
+ public LockMode getLockMode() {
+ return lockMode;
+ }
+
+ public Select setLockMode(LockMode lockMode) {
+ this.lockMode = lockMode;
+ return this;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/sql/SelectFragment.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/sql/SelectFragment.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/sql/SelectFragment.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,128 @@
+//$Id: SelectFragment.java 7479 2005-07-14 23:56:53Z oneovthafew $
+package org.hibernate.sql;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+import org.hibernate.util.StringHelper;
+
+/**
+ * A fragment of an SQL <tt>SELECT</tt> clause
+ *
+ * @author Gavin King
+ */
+public class SelectFragment {
+ private String suffix;
+ private List columns = new ArrayList();
+ //private List aliases = new ArrayList();
+ private List columnAliases = new ArrayList();
+ private String extraSelectList;
+ private String[] usedAliases;
+
+ public SelectFragment() {}
+
+ public SelectFragment setUsedAliases(String[] aliases) {
+ usedAliases = aliases;
+ return this;
+ }
+
+ public SelectFragment setExtraSelectList(String extraSelectList) {
+ this.extraSelectList = extraSelectList;
+ return this;
+ }
+
+ public SelectFragment setExtraSelectList(CaseFragment caseFragment, String fragmentAlias) {
+ setExtraSelectList( caseFragment.setReturnColumnName(fragmentAlias, suffix).toFragmentString() );
+ return this;
+ }
+
+ public SelectFragment setSuffix(String suffix) {
+ this.suffix = suffix;
+ return this;
+ }
+
+ public SelectFragment addColumn(String columnName) {
+ addColumn(null, columnName);
+ return this;
+ }
+
+ public SelectFragment addColumns(String[] columnNames) {
+ for (int i=0; i<columnNames.length; i++) addColumn( columnNames[i] );
+ return this;
+ }
+
+ public SelectFragment addColumn(String tableAlias, String columnName) {
+ return addColumn(tableAlias, columnName, columnName);
+ }
+
+ public SelectFragment addColumn(String tableAlias, String columnName, String columnAlias) {
+ columns.add( StringHelper.qualify(tableAlias, columnName) );
+ //columns.add(columnName);
+ //aliases.add(tableAlias);
+ columnAliases.add(columnAlias);
+ return this;
+ }
+
+ public SelectFragment addColumns(String tableAlias, String[] columnNames) {
+ for (int i=0; i<columnNames.length; i++) addColumn( tableAlias, columnNames[i] );
+ return this;
+ }
+
+ public SelectFragment addColumns(String tableAlias, String[] columnNames, String[] columnAliases) {
+ for (int i=0; i<columnNames.length; i++) {
+ if ( columnNames[i]!=null ) addColumn( tableAlias, columnNames[i], columnAliases[i] );
+ }
+ return this;
+ }
+
+ public SelectFragment addFormulas(String tableAlias, String[] formulas, String[] formulaAliases) {
+ for ( int i=0; i<formulas.length; i++ ) {
+ if ( formulas[i]!=null ) addFormula( tableAlias, formulas[i], formulaAliases[i] );
+ }
+ return this;
+ }
+
+ public SelectFragment addFormula(String tableAlias, String formula, String formulaAlias) {
+ columns.add( StringHelper.replace(formula, Template.TEMPLATE, tableAlias) );
+ columnAliases.add(formulaAlias);
+ return this;
+ }
+
+ public String toFragmentString() {
+ StringBuffer buf = new StringBuffer( columns.size() * 10 );
+ Iterator iter = columns.iterator();
+ Iterator columnAliasIter = columnAliases.iterator();
+ //HashMap columnsUnique = new HashMap();
+ HashSet columnsUnique = new HashSet();
+ if (usedAliases!=null) columnsUnique.addAll( Arrays.asList(usedAliases) );
+ while ( iter.hasNext() ) {
+ String column = (String) iter.next();
+ String columnAlias = (String) columnAliasIter.next();
+ //TODO: eventually put this back in, once we think all is fixed
+ //Object otherAlias = columnsUnique.put(qualifiedColumn, columnAlias);
+ /*if ( otherAlias!=null && !columnAlias.equals(otherAlias) ) {
+ throw new AssertionFailure("bug in Hibernate SQL alias generation");
+ }*/
+ if ( columnsUnique.add(columnAlias) ) {
+ buf.append(", ")
+ .append(column)
+ .append(" as ");
+ if (suffix==null) {
+ buf.append(columnAlias);
+ }
+ else {
+ buf.append( new Alias(suffix).toAliasString(columnAlias) );
+ }
+ }
+ }
+ if (extraSelectList!=null) {
+ buf.append(", ")
+ .append(extraSelectList);
+ }
+ return buf.toString();
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/sql/SimpleSelect.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/sql/SimpleSelect.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/sql/SimpleSelect.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,188 @@
+//$Id: SimpleSelect.java 7627 2005-07-24 06:53:06Z oneovthafew $
+package org.hibernate.sql;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.hibernate.LockMode;
+import org.hibernate.dialect.Dialect;
+
+/**
+ * An SQL <tt>SELECT</tt> statement with no table joins
+ *
+ * @author Gavin King
+ */
+public class SimpleSelect {
+
+ public SimpleSelect(Dialect dialect) {
+ this.dialect = dialect;
+ }
+
+ //private static final Alias DEFAULT_ALIAS = new Alias(10, null);
+
+ private String tableName;
+ private String orderBy;
+ private Dialect dialect;
+ private LockMode lockMode = LockMode.READ;
+ private String comment;
+
+ private List columns = new ArrayList();
+ private Map aliases = new HashMap();
+ private List whereTokens = new ArrayList();
+
+ public SimpleSelect addColumns(String[] columnNames, String[] columnAliases) {
+ for ( int i=0; i<columnNames.length; i++ ) {
+ if ( columnNames[i]!=null ) {
+ addColumn( columnNames[i], columnAliases[i] );
+ }
+ }
+ return this;
+ }
+
+ public SimpleSelect addColumns(String[] columns, String[] aliases, boolean[] ignore) {
+ for ( int i=0; i<ignore.length; i++ ) {
+ if ( !ignore[i] && columns[i]!=null ) {
+ addColumn( columns[i], aliases[i] );
+ }
+ }
+ return this;
+ }
+
+ public SimpleSelect addColumns(String[] columnNames) {
+ for ( int i=0; i<columnNames.length; i++ ) {
+ if ( columnNames[i]!=null ) addColumn( columnNames[i] );
+ }
+ return this;
+ }
+ public SimpleSelect addColumn(String columnName) {
+ columns.add(columnName);
+ //aliases.put( columnName, DEFAULT_ALIAS.toAliasString(columnName) );
+ return this;
+ }
+
+ public SimpleSelect addColumn(String columnName, String alias) {
+ columns.add(columnName);
+ aliases.put(columnName, alias);
+ return this;
+ }
+
+ public SimpleSelect setTableName(String tableName) {
+ this.tableName = tableName;
+ return this;
+ }
+
+ public SimpleSelect setLockMode(LockMode lockMode) {
+ this.lockMode = lockMode;
+ return this;
+ }
+
+ public SimpleSelect addWhereToken(String token) {
+ whereTokens.add(token);
+ return this;
+ }
+
+ private void and() {
+ if ( whereTokens.size()>0 ) {
+ whereTokens.add("and");
+ }
+ }
+
+ public SimpleSelect addCondition(String lhs, String op, String rhs) {
+ and();
+ whereTokens.add( lhs + ' ' + op + ' ' + rhs );
+ return this;
+ }
+
+ public SimpleSelect addCondition(String lhs, String condition) {
+ and();
+ whereTokens.add( lhs + ' ' + condition );
+ return this;
+ }
+
+ public SimpleSelect addCondition(String[] lhs, String op, String[] rhs) {
+ for ( int i=0; i<lhs.length; i++ ) {
+ addCondition( lhs[i], op, rhs[i] );
+ }
+ return this;
+ }
+
+ public SimpleSelect addCondition(String[] lhs, String condition) {
+ for ( int i=0; i<lhs.length; i++ ) {
+ if ( lhs[i]!=null ) addCondition( lhs[i], condition );
+ }
+ return this;
+ }
+
+ public String toStatementString() {
+ StringBuffer buf = new StringBuffer(
+ columns.size()*10 +
+ tableName.length() +
+ whereTokens.size() * 10 +
+ 10
+ );
+
+ if ( comment!=null ) {
+ buf.append("/* ").append(comment).append(" */ ");
+ }
+
+ buf.append("select ");
+ Set uniqueColumns = new HashSet();
+ Iterator iter = columns.iterator();
+ boolean appendComma = false;
+ while ( iter.hasNext() ) {
+ String col = (String) iter.next();
+ String alias = (String) aliases.get(col);
+ if ( uniqueColumns.add(alias==null ? col : alias) ) {
+ if (appendComma) buf.append(", ");
+ buf.append(col);
+ if ( alias!=null && !alias.equals(col) ) {
+ buf.append(" as ")
+ .append(alias);
+ }
+ appendComma = true;
+ }
+ }
+
+ buf.append(" from ")
+ .append( dialect.appendLockHint(lockMode, tableName) );
+
+ if ( whereTokens.size() > 0 ) {
+ buf.append(" where ")
+ .append( toWhereClause() );
+ }
+
+ if (orderBy!=null) buf.append(orderBy);
+
+ if (lockMode!=null) {
+ buf.append( dialect.getForUpdateString(lockMode) );
+ }
+
+ return dialect.transformSelectString( buf.toString() );
+ }
+
+ public String toWhereClause() {
+ StringBuffer buf = new StringBuffer( whereTokens.size() * 5 );
+ Iterator iter = whereTokens.iterator();
+ while ( iter.hasNext() ) {
+ buf.append( iter.next() );
+ if ( iter.hasNext() ) buf.append(' ');
+ }
+ return buf.toString();
+ }
+
+ public SimpleSelect setOrderBy(String orderBy) {
+ this.orderBy = orderBy;
+ return this;
+ }
+
+ public SimpleSelect setComment(String comment) {
+ this.comment = comment;
+ return this;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/sql/Sybase11JoinFragment.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/sql/Sybase11JoinFragment.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/sql/Sybase11JoinFragment.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,98 @@
+//$Id: Sybase11JoinFragment.java 4886 2004-12-05 15:04:21Z pgmjsd $
+package org.hibernate.sql;
+
+
+/**
+ * An old Sybase-style join (before Sybase supported the ANSI style "inner join" etc syntax)
+ * This is needed for Sybase 11.9.2 and earlier, using the HQL 2.* syntax with Collections.
+ *
+ * @author Colm O' Flaherty
+ */
+public class Sybase11JoinFragment extends JoinFragment {
+
+ private StringBuffer afterFrom = new StringBuffer();
+ private StringBuffer afterWhere = new StringBuffer();
+
+ public void addJoin(String tableName, String alias, String[] fkColumns, String[] pkColumns, int joinType) {
+
+ addCrossJoin(tableName, alias);
+
+ for ( int j=0; j<fkColumns.length; j++) {
+ //full joins are not supported.. yet!
+ if (joinType==JoinFragment.FULL_JOIN ) throw new UnsupportedOperationException();
+
+ afterWhere.append(" and ")
+ .append( fkColumns[j] )
+ .append( " " );
+
+ if (joinType==LEFT_OUTER_JOIN ) afterWhere.append("*");
+ afterWhere.append('=');
+ if (joinType==RIGHT_OUTER_JOIN ) afterWhere.append("*");
+
+ afterWhere.append (" ")
+ .append(alias)
+ .append('.')
+ .append( pkColumns[j] );
+ }
+
+ }
+
+ public String toFromFragmentString() {
+ return afterFrom.toString();
+ }
+
+ public String toWhereFragmentString() {
+ return afterWhere.toString();
+ }
+
+ public void addJoins(String fromFragment, String whereFragment) {
+ afterFrom.append(fromFragment);
+ afterWhere.append(whereFragment);
+ }
+
+ public JoinFragment copy() {
+ Sybase11JoinFragment copy = new Sybase11JoinFragment();
+ copy.afterFrom = new StringBuffer( afterFrom.toString() );
+ copy.afterWhere = new StringBuffer( afterWhere.toString() );
+ return copy;
+ }
+
+ public void addCondition(String alias, String[] columns, String condition) {
+ for ( int i=0; i<columns.length; i++ ) {
+ afterWhere.append(" and ")
+ .append(alias)
+ .append('.')
+ .append( columns[i] )
+ .append(condition);
+ }
+ }
+
+ public void addCrossJoin(String tableName, String alias) {
+ afterFrom.append(", ")
+ .append(tableName)
+ .append(' ')
+ .append(alias);
+ }
+
+ public void addCondition(String alias, String[] fkColumns, String[] pkColumns) {
+ throw new UnsupportedOperationException();
+
+ }
+
+ public boolean addCondition(String condition) {
+ return addCondition(afterWhere, condition);
+ }
+
+
+ public void addFromFragmentString(String fromFragmentString) {
+ afterFrom.append(fromFragmentString);
+ }
+
+
+ public void addJoin(String tableName, String alias, String[] fkColumns, String[] pkColumns, int joinType, String on) {
+ addJoin(tableName, alias, fkColumns, pkColumns, joinType);
+ addCondition(on);
+ }
+}
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/sql/Template.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/sql/Template.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/sql/Template.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,318 @@
+//$Id: Template.java 9922 2006-05-10 16:58:09Z steve.ebersole at jboss.com $
+package org.hibernate.sql;
+
+import java.util.HashSet;
+import java.util.StringTokenizer;
+
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.function.SQLFunctionRegistry;
+import org.hibernate.util.StringHelper;
+
+/**
+ * Parses SQL fragments specified in mapping documents
+ *
+ * @author Gavin King
+ */
+public final class Template {
+
+ private static final java.util.Set KEYWORDS = new HashSet();
+ private static final java.util.Set BEFORE_TABLE_KEYWORDS = new HashSet();
+ private static final java.util.Set FUNCTION_KEYWORDS = new HashSet();
+ static {
+ KEYWORDS.add("and");
+ KEYWORDS.add("or");
+ KEYWORDS.add("not");
+ KEYWORDS.add("like");
+ KEYWORDS.add("is");
+ KEYWORDS.add("in");
+ KEYWORDS.add("between");
+ KEYWORDS.add("null");
+ KEYWORDS.add("select");
+ KEYWORDS.add("distinct");
+ KEYWORDS.add("from");
+ KEYWORDS.add("join");
+ KEYWORDS.add("inner");
+ KEYWORDS.add("outer");
+ KEYWORDS.add("left");
+ KEYWORDS.add("right");
+ KEYWORDS.add("on");
+ KEYWORDS.add("where");
+ KEYWORDS.add("having");
+ KEYWORDS.add("group");
+ KEYWORDS.add("order");
+ KEYWORDS.add("by");
+ KEYWORDS.add("desc");
+ KEYWORDS.add("asc");
+ KEYWORDS.add("limit");
+ KEYWORDS.add("any");
+ KEYWORDS.add("some");
+ KEYWORDS.add("exists");
+ KEYWORDS.add("all");
+
+ BEFORE_TABLE_KEYWORDS.add("from");
+ BEFORE_TABLE_KEYWORDS.add("join");
+
+ FUNCTION_KEYWORDS.add("as");
+ FUNCTION_KEYWORDS.add("leading");
+ FUNCTION_KEYWORDS.add("trailing");
+ FUNCTION_KEYWORDS.add("from");
+ FUNCTION_KEYWORDS.add("case");
+ FUNCTION_KEYWORDS.add("when");
+ FUNCTION_KEYWORDS.add("then");
+ FUNCTION_KEYWORDS.add("else");
+ FUNCTION_KEYWORDS.add("end");
+ }
+
+ public static final String TEMPLATE = "$PlaceHolder$";
+
+ private Template() {}
+
+ public static String renderWhereStringTemplate(String sqlWhereString, Dialect dialect, SQLFunctionRegistry functionRegistry) {
+ return renderWhereStringTemplate(sqlWhereString, TEMPLATE, dialect, functionRegistry);
+ }
+
+ /**
+ * Same functionality as {@link #renderWhereStringTemplate(String, String, Dialect, SQLFunctionRegistry)},
+ * except that a SQLFunctionRegistry is not provided (i.e., only the dialect-defined functions are
+ * considered). This is only intended for use by the annotations project until the
+ * many-to-many/map-key-from-target-table feature is pulled into core.
+ *
+ * @deprecated Only intended for annotations usage; use {@link #renderWhereStringTemplate(String, String, Dialect, SQLFunctionRegistry)} instead
+ */
+ public static String renderWhereStringTemplate(String sqlWhereString, String placeholder, Dialect dialect) {
+ return renderWhereStringTemplate( sqlWhereString, placeholder, dialect, new SQLFunctionRegistry( dialect, java.util.Collections.EMPTY_MAP ) );
+ }
+
+ /**
+ * Takes the where condition provided in the mapping attribute and interpolates the alias.
+ * Handles subselects, quoted identifiers, quoted strings, expressions, SQL functions,
+ * named parameters.
+ *
+ * @param sqlWhereString The string into which to interpolate the placeholder value
+ * @param placeholder The value to be interpolated into the the sqlWhereString
+ * @param dialect The dialect to apply
+ * @param functionRegistry The registry of all sql functions
+ * @return The rendered sql fragment
+ */
+ public static String renderWhereStringTemplate(String sqlWhereString, String placeholder, Dialect dialect, SQLFunctionRegistry functionRegistry ) {
+ //TODO: make this a bit nicer
+ String symbols = new StringBuffer()
+ .append("=><!+-*/()',|&`")
+ .append(StringHelper.WHITESPACE)
+ .append( dialect.openQuote() )
+ .append( dialect.closeQuote() )
+ .toString();
+ StringTokenizer tokens = new StringTokenizer(sqlWhereString, symbols, true);
+
+ StringBuffer result = new StringBuffer();
+ boolean quoted = false;
+ boolean quotedIdentifier = false;
+ boolean beforeTable = false;
+ boolean inFromClause = false;
+ boolean afterFromTable = false;
+
+ boolean hasMore = tokens.hasMoreTokens();
+ String nextToken = hasMore ? tokens.nextToken() : null;
+ while (hasMore) {
+ String token = nextToken;
+ String lcToken = token.toLowerCase();
+ hasMore = tokens.hasMoreTokens();
+ nextToken = hasMore ? tokens.nextToken() : null;
+
+ boolean isQuoteCharacter = false;
+
+ if ( !quotedIdentifier && "'".equals(token) ) {
+ quoted = !quoted;
+ isQuoteCharacter = true;
+ }
+
+ if ( !quoted ) {
+
+ boolean isOpenQuote;
+ if ( "`".equals(token) ) {
+ isOpenQuote = !quotedIdentifier;
+ token = lcToken = isOpenQuote ?
+ new Character( dialect.openQuote() ).toString() :
+ new Character( dialect.closeQuote() ).toString();
+ quotedIdentifier = isOpenQuote;
+ isQuoteCharacter = true;
+ }
+ else if ( !quotedIdentifier && ( dialect.openQuote()==token.charAt(0) ) ) {
+ isOpenQuote = true;
+ quotedIdentifier = true;
+ isQuoteCharacter = true;
+ }
+ else if ( quotedIdentifier && ( dialect.closeQuote()==token.charAt(0) ) ) {
+ quotedIdentifier = false;
+ isQuoteCharacter = true;
+ isOpenQuote = false;
+ }
+ else {
+ isOpenQuote = false;
+ }
+
+ if (isOpenQuote) {
+ result.append(placeholder).append('.');
+ }
+
+ }
+
+ boolean quotedOrWhitespace = quoted ||
+ quotedIdentifier ||
+ isQuoteCharacter ||
+ Character.isWhitespace( token.charAt(0) );
+
+ if (quotedOrWhitespace) {
+ result.append(token);
+ }
+ else if (beforeTable) {
+ result.append(token);
+ beforeTable = false;
+ afterFromTable = true;
+ }
+ else if (afterFromTable) {
+ if ( !"as".equals(lcToken) ) afterFromTable = false;
+ result.append(token);
+ }
+ else if ( isNamedParameter(token) ) {
+ result.append(token);
+ }
+ else if (
+ isIdentifier(token, dialect) &&
+ !isFunctionOrKeyword(lcToken, nextToken, dialect , functionRegistry)
+ ) {
+ result.append(placeholder)
+ .append('.')
+ .append( dialect.quote(token) );
+ }
+ else {
+ if ( BEFORE_TABLE_KEYWORDS.contains(lcToken) ) {
+ beforeTable = true;
+ inFromClause = true;
+ }
+ else if ( inFromClause && ",".equals(lcToken) ) {
+ beforeTable = true;
+ }
+ result.append(token);
+ }
+
+ if ( //Yuck:
+ inFromClause &&
+ KEYWORDS.contains(lcToken) && //"as" is not in KEYWORDS
+ !BEFORE_TABLE_KEYWORDS.contains(lcToken)
+ ) {
+ inFromClause = false;
+ }
+
+ }
+ return result.toString();
+ }
+
+ /**
+ * Takes order by clause provided in the mapping attribute and interpolates the alias.
+ * Handles asc, desc, SQL functions, quoted identifiers.
+ */
+ public static String renderOrderByStringTemplate(String sqlOrderByString, Dialect dialect, SQLFunctionRegistry functionRegistry) {
+ //TODO: make this a bit nicer
+ String symbols = new StringBuffer()
+ .append("=><!+-*/()',|&`")
+ .append(StringHelper.WHITESPACE)
+ .append( dialect.openQuote() )
+ .append( dialect.closeQuote() )
+ .toString();
+ StringTokenizer tokens = new StringTokenizer(sqlOrderByString, symbols, true);
+
+ StringBuffer result = new StringBuffer();
+ boolean quoted = false;
+ boolean quotedIdentifier = false;
+
+ boolean hasMore = tokens.hasMoreTokens();
+ String nextToken = hasMore ? tokens.nextToken() : null;
+ while (hasMore) {
+ String token = nextToken;
+ String lcToken = token.toLowerCase();
+ hasMore = tokens.hasMoreTokens();
+ nextToken = hasMore ? tokens.nextToken() : null;
+
+ boolean isQuoteCharacter = false;
+
+ if ( !quotedIdentifier && "'".equals(token) ) {
+ quoted = !quoted;
+ isQuoteCharacter = true;
+ }
+
+ if ( !quoted ) {
+
+ boolean isOpenQuote;
+ if ( "`".equals(token) ) {
+ isOpenQuote = !quotedIdentifier;
+ token = lcToken = isOpenQuote ?
+ new Character( dialect.openQuote() ).toString() :
+ new Character( dialect.closeQuote() ).toString();
+ quotedIdentifier = isOpenQuote;
+ isQuoteCharacter = true;
+ }
+ else if ( !quotedIdentifier && ( dialect.openQuote()==token.charAt(0) ) ) {
+ isOpenQuote = true;
+ quotedIdentifier = true;
+ isQuoteCharacter = true;
+ }
+ else if ( quotedIdentifier && ( dialect.closeQuote()==token.charAt(0) ) ) {
+ quotedIdentifier = false;
+ isQuoteCharacter = true;
+ isOpenQuote = false;
+ }
+ else {
+ isOpenQuote = false;
+ }
+
+ if (isOpenQuote) {
+ result.append(TEMPLATE).append('.');
+ }
+
+ }
+
+ boolean quotedOrWhitespace = quoted ||
+ quotedIdentifier ||
+ isQuoteCharacter ||
+ Character.isWhitespace( token.charAt(0) );
+
+ if (quotedOrWhitespace) {
+ result.append(token);
+ }
+ else if (
+ isIdentifier(token, dialect) &&
+ !isFunctionOrKeyword(lcToken, nextToken, dialect, functionRegistry)
+ ) {
+ result.append(TEMPLATE)
+ .append('.')
+ .append( dialect.quote(token) );
+ }
+ else {
+ result.append(token);
+ }
+ }
+ return result.toString();
+ }
+
+ private static boolean isNamedParameter(String token) {
+ return token.startsWith(":");
+ }
+
+ private static boolean isFunctionOrKeyword(String lcToken, String nextToken, Dialect dialect, SQLFunctionRegistry functionRegistry) {
+ return "(".equals(nextToken) ||
+ KEYWORDS.contains(lcToken) ||
+ functionRegistry.hasFunction(lcToken) ||
+ dialect.getKeywords().contains(lcToken) ||
+ FUNCTION_KEYWORDS.contains(lcToken);
+ }
+
+ private static boolean isIdentifier(String token, Dialect dialect) {
+ return token.charAt(0)=='`' || ( //allow any identifier quoted with backtick
+ Character.isLetter( token.charAt(0) ) && //only recognizes identifiers beginning with a letter
+ token.indexOf('.') < 0
+ );
+ }
+
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/sql/Update.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/sql/Update.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/sql/Update.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,189 @@
+//$Id: Update.java 7825 2005-08-10 20:23:55Z oneovthafew $
+package org.hibernate.sql;
+
+import java.util.Iterator;
+import java.util.Map;
+
+import org.hibernate.dialect.Dialect;
+import org.hibernate.type.LiteralType;
+import org.hibernate.util.StringHelper;
+
+import org.apache.commons.collections.SequencedHashMap;
+
+/**
+ * An SQL <tt>UPDATE</tt> statement
+ *
+ * @author Gavin King
+ */
+public class Update {
+
+ private String tableName;
+ private String[] primaryKeyColumnNames;
+ private String versionColumnName;
+ private String where;
+ private String assignments;
+ private String comment;
+
+ private Map columns = new SequencedHashMap();
+ private Map whereColumns = new SequencedHashMap();
+
+ private Dialect dialect;
+
+ public Update(Dialect dialect) {
+ this.dialect = dialect;
+ }
+
+ public String getTableName() {
+ return tableName;
+ }
+
+ public Update appendAssignmentFragment(String fragment) {
+ if ( assignments == null ) {
+ assignments = fragment;
+ }
+ else {
+ assignments += ", " + fragment;
+ }
+ return this;
+ }
+
+ public Update setTableName(String tableName) {
+ this.tableName = tableName;
+ return this;
+ }
+
+ public Update setPrimaryKeyColumnNames(String[] primaryKeyColumnNames) {
+ this.primaryKeyColumnNames = primaryKeyColumnNames;
+ return this;
+ }
+
+ public Update setVersionColumnName(String versionColumnName) {
+ this.versionColumnName = versionColumnName;
+ return this;
+ }
+
+
+ public Update setComment(String comment) {
+ this.comment = comment;
+ return this;
+ }
+
+ public Update addColumns(String[] columnNames) {
+ for ( int i=0; i<columnNames.length; i++ ) {
+ addColumn( columnNames[i] );
+ }
+ return this;
+ }
+
+ public Update addColumns(String[] columnNames, boolean[] updateable) {
+ for ( int i=0; i<columnNames.length; i++ ) {
+ if ( updateable[i] ) addColumn( columnNames[i] );
+ }
+ return this;
+ }
+
+ public Update addColumns(String[] columnNames, String value) {
+ for ( int i=0; i<columnNames.length; i++ ) {
+ addColumn( columnNames[i], value );
+ }
+ return this;
+ }
+
+ public Update addColumn(String columnName) {
+ return addColumn(columnName, "?");
+ }
+
+ public Update addColumn(String columnName, String value) {
+ columns.put(columnName, value);
+ return this;
+ }
+
+ public Update addColumn(String columnName, Object value, LiteralType type) throws Exception {
+ return addColumn( columnName, type.objectToSQLString(value, dialect) );
+ }
+
+ public Update addWhereColumns(String[] columnNames) {
+ for ( int i=0; i<columnNames.length; i++ ) {
+ addWhereColumn( columnNames[i] );
+ }
+ return this;
+ }
+
+ public Update addWhereColumns(String[] columnNames, String value) {
+ for ( int i=0; i<columnNames.length; i++ ) {
+ addWhereColumn( columnNames[i], value );
+ }
+ return this;
+ }
+
+ public Update addWhereColumn(String columnName) {
+ return addWhereColumn(columnName, "=?");
+ }
+
+ public Update addWhereColumn(String columnName, String value) {
+ whereColumns.put(columnName, value);
+ return this;
+ }
+
+ public Update setWhere(String where) {
+ this.where=where;
+ return this;
+ }
+
+ public String toStatementString() {
+ StringBuffer buf = new StringBuffer( (columns.size() * 15) + tableName.length() + 10 );
+ if ( comment!=null ) {
+ buf.append( "/* " ).append( comment ).append( " */ " );
+ }
+ buf.append( "update " ).append( tableName ).append( " set " );
+ boolean assignmentsAppended = false;
+ Iterator iter = columns.entrySet().iterator();
+ while ( iter.hasNext() ) {
+ Map.Entry e = (Map.Entry) iter.next();
+ buf.append( e.getKey() ).append( '=' ).append( e.getValue() );
+ if ( iter.hasNext() ) {
+ buf.append( ", " );
+ }
+ assignmentsAppended = true;
+ }
+ if ( assignments != null ) {
+ if ( assignmentsAppended ) {
+ buf.append( ", " );
+ }
+ buf.append( assignments );
+ }
+
+ boolean conditionsAppended = false;
+ if ( primaryKeyColumnNames != null || where != null || !whereColumns.isEmpty() || versionColumnName != null ) {
+ buf.append( " where " );
+ }
+ if ( primaryKeyColumnNames != null ) {
+ buf.append( StringHelper.join( "=? and ", primaryKeyColumnNames ) ).append( "=?" );
+ conditionsAppended = true;
+ }
+ if ( where != null ) {
+ if ( conditionsAppended ) {
+ buf.append( " and " );
+ }
+ buf.append( where );
+ conditionsAppended = true;
+ }
+ iter = whereColumns.entrySet().iterator();
+ while ( iter.hasNext() ) {
+ final Map.Entry e = (Map.Entry) iter.next();
+ if ( conditionsAppended ) {
+ buf.append( " and " );
+ }
+ buf.append( e.getKey() ).append( e.getValue() );
+ conditionsAppended = true;
+ }
+ if ( versionColumnName != null ) {
+ if ( conditionsAppended ) {
+ buf.append( " and " );
+ }
+ buf.append( versionColumnName ).append( "=?" );
+ }
+
+ return buf.toString();
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/sql/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/sql/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/sql/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,9 @@
+<html>
+<head></head>
+<body>
+<p>
+ This package defines helper classes for rendering SQL
+ fragments and SQL statements.
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/sql/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/stat/CategorizedStatistics.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/stat/CategorizedStatistics.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/stat/CategorizedStatistics.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,23 @@
+//$Id: CategorizedStatistics.java 7093 2005-06-09 06:33:06Z oneovthafew $
+package org.hibernate.stat;
+
+import java.io.Serializable;
+
+/**
+ * Statistics for a particular "category" (a named entity,
+ * collection role, second level cache region or query).
+ *
+ * @author Gavin King
+ */
+public class CategorizedStatistics implements Serializable {
+
+ private final String categoryName;
+
+ CategorizedStatistics(String categoryName) {
+ this.categoryName = categoryName;
+ }
+
+ public String getCategoryName() {
+ return categoryName;
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/stat/CategorizedStatistics.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/stat/CollectionStatistics.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/stat/CollectionStatistics.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/stat/CollectionStatistics.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,49 @@
+//$Id: CollectionStatistics.java 7093 2005-06-09 06:33:06Z oneovthafew $
+package org.hibernate.stat;
+
+
+/**
+ * Collection related statistics
+ *
+ * @author Gavin King
+ */
+public class CollectionStatistics extends CategorizedStatistics {
+
+ CollectionStatistics(String role) {
+ super(role);
+ }
+
+ long loadCount;
+ long fetchCount;
+ long updateCount;
+ long removeCount;
+ long recreateCount;
+
+ public long getLoadCount() {
+ return loadCount;
+ }
+ public long getFetchCount() {
+ return fetchCount;
+ }
+ public long getRecreateCount() {
+ return recreateCount;
+ }
+ public long getRemoveCount() {
+ return removeCount;
+ }
+ public long getUpdateCount() {
+ return updateCount;
+ }
+
+ public String toString() {
+ return new StringBuffer()
+ .append("CollectionStatistics")
+ .append("[loadCount=").append(this.loadCount)
+ .append(",fetchCount=").append(this.fetchCount)
+ .append(",recreateCount=").append(this.recreateCount)
+ .append(",removeCount=").append(this.removeCount)
+ .append(",updateCount=").append(this.updateCount)
+ .append(']')
+ .toString();
+ }
+}
\ No newline at end of file
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/stat/CollectionStatistics.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/stat/EntityStatistics.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/stat/EntityStatistics.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/stat/EntityStatistics.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,55 @@
+//$Id: EntityStatistics.java 7864 2005-08-11 23:22:52Z oneovthafew $
+package org.hibernate.stat;
+
+
+/**
+ * Entity related statistics
+ *
+ * @author Gavin King
+ */
+public class EntityStatistics extends CategorizedStatistics {
+
+ EntityStatistics(String name) {
+ super(name);
+ }
+
+ long loadCount;
+ long updateCount;
+ long insertCount;
+ long deleteCount;
+ long fetchCount;
+ long optimisticFailureCount;
+
+ public long getDeleteCount() {
+ return deleteCount;
+ }
+ public long getInsertCount() {
+ return insertCount;
+ }
+ public long getLoadCount() {
+ return loadCount;
+ }
+ public long getUpdateCount() {
+ return updateCount;
+ }
+ public long getFetchCount() {
+ return fetchCount;
+ }
+ public long getOptimisticFailureCount() {
+ return optimisticFailureCount;
+ }
+
+ public String toString() {
+ return new StringBuffer()
+ .append("EntityStatistics")
+ .append("[loadCount=").append(this.loadCount)
+ .append(",updateCount=").append(this.updateCount)
+ .append(",insertCount=").append(this.insertCount)
+ .append(",deleteCount=").append(this.deleteCount)
+ .append(",fetchCount=").append(this.fetchCount)
+ .append(",optimisticLockFailureCount=").append(this.optimisticFailureCount)
+ .append(']')
+ .toString();
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/stat/EntityStatistics.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/stat/QueryStatistics.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/stat/QueryStatistics.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/stat/QueryStatistics.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,112 @@
+//$Id: QueryStatistics.java 9162 2006-01-27 23:40:32Z steveebersole $
+package org.hibernate.stat;
+
+
+/**
+ * Query statistics (HQL and SQL)
+ *
+ * Note that for a cached query, the cache miss is equals to the db count
+ *
+ * @author Gavin King
+ */
+public class QueryStatistics extends CategorizedStatistics {
+
+ /*package*/ long cacheHitCount;
+ /*package*/ long cacheMissCount;
+ /*package*/ long cachePutCount;
+ private long executionCount;
+ private long executionRowCount;
+ private long executionAvgTime;
+ private long executionMaxTime;
+ private long executionMinTime;
+
+ QueryStatistics(String query) {
+ super(query);
+ }
+
+ /**
+ * queries executed to the DB
+ */
+ public long getExecutionCount() {
+ return executionCount;
+ }
+
+ /**
+ * Queries retrieved successfully from the cache
+ */
+ public long getCacheHitCount() {
+ return cacheHitCount;
+ }
+
+ public long getCachePutCount() {
+ return cachePutCount;
+ }
+
+ public long getCacheMissCount() {
+ return cacheMissCount;
+ }
+
+ /**
+ * Number of lines returned by all the executions of this query (from DB)
+ * For now, {@link org.hibernate.Query#iterate()}
+ * and {@link org.hibernate.Query#scroll()()} do not fill this statistic
+ *
+ * @return The number of rows cumulatively returned by the given query; iterate
+ * and scroll queries do not effect this total as their number of returned rows
+ * is not known at execution time.
+ */
+ public long getExecutionRowCount() {
+ return executionRowCount;
+ }
+
+ /**
+ * average time in ms taken by the excution of this query onto the DB
+ */
+ public long getExecutionAvgTime() {
+ return executionAvgTime;
+ }
+
+ /**
+ * max time in ms taken by the excution of this query onto the DB
+ */
+ public long getExecutionMaxTime() {
+ return executionMaxTime;
+ }
+
+ /**
+ * min time in ms taken by the excution of this query onto the DB
+ */
+ public long getExecutionMinTime() {
+ return executionMinTime;
+ }
+
+ /**
+ * add statistics report of a DB query
+ *
+ * @param rows rows count returned
+ * @param time time taken
+ */
+ void executed(long rows, long time) {
+ if (time < executionMinTime) executionMinTime = time;
+ if (time > executionMaxTime) executionMaxTime = time;
+ executionAvgTime = ( executionAvgTime * executionCount + time ) / ( executionCount + 1 );
+ executionCount++;
+ executionRowCount += rows;
+ }
+
+ public String toString() {
+ return new StringBuffer()
+ .append( "QueryStatistics" )
+ .append( "[cacheHitCount=" ).append( this.cacheHitCount )
+ .append( ",cacheMissCount=" ).append( this.cacheMissCount )
+ .append( ",cachePutCount=" ).append( this.cachePutCount )
+ .append( ",executionCount=" ).append( this.executionCount )
+ .append( ",executionRowCount=" ).append( this.executionRowCount )
+ .append( ",executionAvgTime=" ).append( this.executionAvgTime )
+ .append( ",executionMaxTime=" ).append( this.executionMaxTime )
+ .append( ",executionMinTime=" ).append( this.executionMinTime )
+ .append( ']' )
+ .toString();
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/stat/QueryStatistics.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/stat/SecondLevelCacheStatistics.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/stat/SecondLevelCacheStatistics.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/stat/SecondLevelCacheStatistics.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,71 @@
+//$Id: SecondLevelCacheStatistics.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.stat;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.hibernate.cache.CacheKey;
+import org.hibernate.cache.Region;
+
+/**
+ * Second level cache statistics of a specific region
+ *
+ * @author Gavin King
+ */
+public class SecondLevelCacheStatistics extends CategorizedStatistics {
+
+ private transient Region region;
+ long hitCount;
+ long missCount;
+ long putCount;
+
+ SecondLevelCacheStatistics(Region region) {
+ super( region.getName() );
+ this.region = region;
+ }
+ public long getHitCount() {
+ return hitCount;
+ }
+ public long getMissCount() {
+ return missCount;
+ }
+ public long getPutCount() {
+ return putCount;
+ }
+ public long getElementCountInMemory() {
+ return region.getElementCountInMemory();
+ }
+ public long getElementCountOnDisk() {
+ return region.getElementCountOnDisk();
+ }
+ public long getSizeInMemory() {
+ return region.getSizeInMemory();
+ }
+
+ public Map getEntries() {
+ Map map = new HashMap();
+ Iterator iter = region.toMap().entrySet().iterator();
+ while ( iter.hasNext() ) {
+ Map.Entry me = (Map.Entry) iter.next();
+ map.put( ( (CacheKey) me.getKey() ).getKey(), me.getValue() );
+ }
+ return map;
+ }
+
+ public String toString() {
+ StringBuffer buf = new StringBuffer()
+ .append("SecondLevelCacheStatistics")
+ .append("[hitCount=").append(this.hitCount)
+ .append(",missCount=").append(this.missCount)
+ .append(",putCount=").append(this.putCount);
+ //not sure if this would ever be null but wanted to be careful
+ if ( region != null ) {
+ buf.append(",elementCountInMemory=").append(this.getElementCountInMemory())
+ .append(",elementCountOnDisk=").append(this.getElementCountOnDisk())
+ .append(",sizeInMemory=").append(this.getSizeInMemory());
+ }
+ buf.append(']');
+ return buf.toString();
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/stat/SecondLevelCacheStatistics.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/stat/SessionStatistics.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/stat/SessionStatistics.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/stat/SessionStatistics.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,33 @@
+//$Id: SessionStatistics.java 6750 2005-05-11 15:26:04Z oneovthafew $
+package org.hibernate.stat;
+
+import java.util.Set;
+
+/**
+ * Information about the first-level (session) cache
+ * for a particular session instance
+ * @author Gavin King
+ */
+public interface SessionStatistics {
+
+ /**
+ * Get the number of entity instances associated with the session
+ */
+ public int getEntityCount();
+ /**
+ * Get the number of collection instances associated with the session
+ */
+ public int getCollectionCount();
+
+ /**
+ * Get the set of all <tt>EntityKey</tt>s
+ * @see org.hibernate.engine.EntityKey
+ */
+ public Set getEntityKeys();
+ /**
+ * Get the set of all <tt>CollectionKey</tt>s
+ * @see org.hibernate.engine.CollectionKey
+ */
+ public Set getCollectionKeys();
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/stat/SessionStatistics.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/stat/SessionStatisticsImpl.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/stat/SessionStatisticsImpl.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/stat/SessionStatisticsImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,45 @@
+//$Id: SessionStatisticsImpl.java 7688 2005-07-29 21:43:18Z epbernard $
+package org.hibernate.stat;
+
+import java.util.Collections;
+import java.util.Set;
+
+import org.hibernate.engine.SessionImplementor;
+
+/**
+ * @author Gavin King
+ */
+public class SessionStatisticsImpl implements SessionStatistics {
+
+ private final SessionImplementor session;
+
+ public SessionStatisticsImpl(SessionImplementor session) {
+ this.session = session;
+ }
+
+ public int getEntityCount() {
+ return session.getPersistenceContext().getEntityEntries().size();
+ }
+
+ public int getCollectionCount() {
+ return session.getPersistenceContext().getCollectionEntries().size();
+ }
+
+ public Set getEntityKeys() {
+ return Collections.unmodifiableSet( session.getPersistenceContext().getEntitiesByKey().keySet() );
+ }
+
+ public Set getCollectionKeys() {
+ return Collections.unmodifiableSet( session.getPersistenceContext().getCollectionsByKey().keySet() );
+ }
+
+ public String toString() {
+ return new StringBuffer()
+ .append("SessionStatistics[")
+ .append("entity count=").append( getEntityCount() )
+ .append("collection count=").append( getCollectionCount() )
+ .append(']')
+ .toString();
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/stat/SessionStatisticsImpl.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/stat/Statistics.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/stat/Statistics.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/stat/Statistics.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,211 @@
+//$Id: Statistics.java 8262 2005-09-30 07:48:53Z oneovthafew $
+package org.hibernate.stat;
+
+
+/**
+ * Statistics for a particular <tt>SessionFactory</tt>.
+ * Beware of milliseconds metrics, they are depdendent of the JVM precision:
+ * you may then encounter a 10 ms approximation dending on you OS platform.
+ * Please refer to the JVM documentation for more information.
+ *
+ * @author Emmanuel Bernard
+ */
+public interface Statistics {
+ /**
+ * reset all statistics
+ */
+ public void clear();
+
+ /**
+ * find entity statistics per name
+ *
+ * @param entityName entity name
+ * @return EntityStatistics object
+ */
+ public EntityStatistics getEntityStatistics(String entityName);
+ /**
+ * Get collection statistics per role
+ *
+ * @param role collection role
+ * @return CollectionStatistics
+ */
+ public CollectionStatistics getCollectionStatistics(String role);
+
+ /**
+ * Second level cache statistics per region
+ *
+ * @param regionName region name
+ * @return SecondLevelCacheStatistics
+ */
+ public SecondLevelCacheStatistics getSecondLevelCacheStatistics(String regionName);
+
+ /**
+ * Query statistics from query string (HQL or SQL)
+ *
+ * @param queryString query string
+ * @return QueryStatistics
+ */
+ public QueryStatistics getQueryStatistics(String queryString);
+
+ /**
+ * Get global number of entity deletes
+ * @return entity deletion count
+ */
+ public long getEntityDeleteCount();
+
+ /**
+ * Get global number of entity inserts
+ * @return entity insertion count
+ */
+ public long getEntityInsertCount();
+
+ /**
+ * Get global number of entity loads
+ * @return entity load (from DB)
+ */
+ public long getEntityLoadCount();
+ /**
+ * Get global number of entity fetchs
+ * @return entity fetch (from DB)
+ */
+ public long getEntityFetchCount();
+
+ /**
+ * Get global number of entity updates
+ * @return entity update
+ */
+ public long getEntityUpdateCount();
+
+ /**
+ * Get global number of executed queries
+ * @return query execution count
+ */
+ public long getQueryExecutionCount();
+
+ /**
+ * Get the time in milliseconds of the slowest query.
+ */
+ public long getQueryExecutionMaxTime();
+ /**
+ * Get the query string for the slowest query.
+ */
+ public String getQueryExecutionMaxTimeQueryString();
+
+ /**
+ * Get the global number of cached queries successfully retrieved from cache
+ */
+ public long getQueryCacheHitCount();
+ /**
+ * Get the global number of cached queries *not* found in cache
+ */
+ public long getQueryCacheMissCount();
+ /**
+ * Get the global number of cacheable queries put in cache
+ */
+ public long getQueryCachePutCount();
+ /**
+ * Get the global number of flush executed by sessions (either implicit or explicit)
+ */
+ public long getFlushCount();
+ /**
+ * Get the global number of connections asked by the sessions
+ * (the actual number of connections used may be much smaller depending
+ * whether you use a connection pool or not)
+ */
+ public long getConnectCount();
+ /**
+ * Global number of cacheable entities/collections successfully retrieved from the cache
+ */
+ public long getSecondLevelCacheHitCount();
+ /**
+ * Global number of cacheable entities/collections not found in the cache and loaded from the database.
+ */
+ public long getSecondLevelCacheMissCount();
+ /**
+ * Global number of cacheable entities/collections put in the cache
+ */
+ public long getSecondLevelCachePutCount();
+ /**
+ * Global number of sessions closed
+ */
+ public long getSessionCloseCount();
+ /**
+ * Global number of sessions opened
+ */
+ public long getSessionOpenCount();
+ /**
+ * Global number of collections loaded
+ */
+ public long getCollectionLoadCount();
+ /**
+ * Global number of collections fetched
+ */
+ public long getCollectionFetchCount();
+ /**
+ * Global number of collections updated
+ */
+ public long getCollectionUpdateCount();
+ /**
+ * Global number of collections removed
+ */
+ //even on inverse="true"
+ public long getCollectionRemoveCount();
+ /**
+ * Global number of collections recreated
+ */
+ public long getCollectionRecreateCount();
+ /**
+ * @return start time in ms (JVM standards {@link System#currentTimeMillis()})
+ */
+ public long getStartTime();
+ /**
+ * log in info level the main statistics
+ */
+ public void logSummary();
+ /**
+ * Are statistics logged
+ */
+ public boolean isStatisticsEnabled();
+ /**
+ * Enable statistics logs (this is a dynamic parameter)
+ */
+ public void setStatisticsEnabled(boolean b);
+
+ /**
+ * Get all executed query strings
+ */
+ public String[] getQueries();
+ /**
+ * Get the names of all entities
+ */
+ public String[] getEntityNames();
+ /**
+ * Get the names of all collection roles
+ */
+ public String[] getCollectionRoleNames();
+ /**
+ * Get all second-level cache region names
+ */
+ public String[] getSecondLevelCacheRegionNames();
+ /**
+ * The number of transactions we know to have been successful
+ */
+ public long getSuccessfulTransactionCount();
+ /**
+ * The number of transactions we know to have completed
+ */
+ public long getTransactionCount();
+ /**
+ * The number of prepared statements that were acquired
+ */
+ public long getPrepareStatementCount();
+ /**
+ * The number of prepared statements that were released
+ */
+ public long getCloseStatementCount();
+ /**
+ * The number of <tt>StaleObjectStateException</tt>s
+ * that occurred
+ */
+ public long getOptimisticFailureCount();
+}
\ No newline at end of file
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/stat/Statistics.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/stat/StatisticsImpl.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/stat/StatisticsImpl.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/stat/StatisticsImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,640 @@
+//$Id: StatisticsImpl.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.stat;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.cache.Cache;
+import org.hibernate.cache.Region;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.util.ArrayHelper;
+
+/**
+ * @see org.hibernate.stat.Statistics
+ *
+ * @author Gavin King
+ */
+public class StatisticsImpl implements Statistics, StatisticsImplementor {
+
+ //TODO: we should provide some way to get keys of collection of statistics to make it easier to retrieve from a GUI perspective
+
+ private static final Log log = LogFactory.getLog(StatisticsImpl.class);
+
+ private SessionFactoryImplementor sessionFactory;
+
+ private boolean isStatisticsEnabled;
+ private long startTime;
+ private long sessionOpenCount;
+ private long sessionCloseCount;
+ private long flushCount;
+ private long connectCount;
+
+ private long prepareStatementCount;
+ private long closeStatementCount;
+
+ private long entityLoadCount;
+ private long entityUpdateCount;
+ private long entityInsertCount;
+ private long entityDeleteCount;
+ private long entityFetchCount;
+ private long collectionLoadCount;
+ private long collectionUpdateCount;
+ private long collectionRemoveCount;
+ private long collectionRecreateCount;
+ private long collectionFetchCount;
+
+ private long secondLevelCacheHitCount;
+ private long secondLevelCacheMissCount;
+ private long secondLevelCachePutCount;
+
+ private long queryExecutionCount;
+ private long queryExecutionMaxTime;
+ private String queryExecutionMaxTimeQueryString;
+ private long queryCacheHitCount;
+ private long queryCacheMissCount;
+ private long queryCachePutCount;
+
+ private long commitedTransactionCount;
+ private long transactionCount;
+
+ private long optimisticFailureCount;
+
+ /** second level cache statistics per region */
+ private final Map secondLevelCacheStatistics = new HashMap();
+ /** entity statistics per name */
+ private final Map entityStatistics = new HashMap();
+ /** collection statistics per name */
+ private final Map collectionStatistics = new HashMap();
+ /** entity statistics per query string (HQL or SQL) */
+ private final Map queryStatistics = new HashMap();
+
+ public StatisticsImpl() {
+ clear();
+ }
+
+ public StatisticsImpl(SessionFactoryImplementor sessionFactory) {
+ clear();
+ this.sessionFactory = sessionFactory;
+ }
+
+ /**
+ * reset all statistics
+ */
+ public synchronized void clear() {
+ secondLevelCacheHitCount = 0;
+ secondLevelCacheMissCount = 0;
+ secondLevelCachePutCount = 0;
+
+ sessionCloseCount = 0;
+ sessionOpenCount = 0;
+ flushCount = 0;
+ connectCount = 0;
+
+ prepareStatementCount = 0;
+ closeStatementCount = 0;
+
+ entityDeleteCount = 0;
+ entityInsertCount = 0;
+ entityUpdateCount = 0;
+ entityLoadCount = 0;
+ entityFetchCount = 0;
+
+ collectionRemoveCount = 0;
+ collectionUpdateCount = 0;
+ collectionRecreateCount = 0;
+ collectionLoadCount = 0;
+ collectionFetchCount = 0;
+
+ queryExecutionCount = 0;
+ queryCacheHitCount = 0;
+ queryExecutionMaxTime = 0;
+ queryExecutionMaxTimeQueryString = null;
+ queryCacheMissCount = 0;
+ queryCachePutCount = 0;
+
+ transactionCount = 0;
+ commitedTransactionCount = 0;
+
+ optimisticFailureCount = 0;
+
+ secondLevelCacheStatistics.clear();
+ entityStatistics.clear();
+ collectionStatistics.clear();
+ queryStatistics.clear();
+
+ startTime = System.currentTimeMillis();
+ }
+
+ public synchronized void openSession() {
+ sessionOpenCount++;
+ }
+
+ public synchronized void closeSession() {
+ sessionCloseCount++;
+ }
+
+ public synchronized void flush() {
+ flushCount++;
+ }
+
+ public synchronized void connect() {
+ connectCount++;
+ }
+
+ public synchronized void loadEntity(String entityName) {
+ entityLoadCount++;
+ getEntityStatistics(entityName).loadCount++;
+ }
+
+ public synchronized void fetchEntity(String entityName) {
+ entityFetchCount++;
+ getEntityStatistics(entityName).fetchCount++;
+ }
+
+ /**
+ * find entity statistics per name
+ *
+ * @param entityName entity name
+ * @return EntityStatistics object
+ */
+ public synchronized EntityStatistics getEntityStatistics(String entityName) {
+ EntityStatistics es = (EntityStatistics) entityStatistics.get(entityName);
+ if (es==null) {
+ es = new EntityStatistics(entityName);
+ entityStatistics.put(entityName, es);
+ }
+ return es;
+ }
+
+ public synchronized void updateEntity(String entityName) {
+ entityUpdateCount++;
+ EntityStatistics es = getEntityStatistics(entityName);
+ es.updateCount++;
+ }
+
+ public synchronized void insertEntity(String entityName) {
+ entityInsertCount++;
+ EntityStatistics es = getEntityStatistics(entityName);
+ es.insertCount++;
+ }
+
+ public synchronized void deleteEntity(String entityName) {
+ entityDeleteCount++;
+ EntityStatistics es = getEntityStatistics(entityName);
+ es.deleteCount++;
+ }
+
+ /**
+ * Get collection statistics per role
+ *
+ * @param role collection role
+ * @return CollectionStatistics
+ */
+ public synchronized CollectionStatistics getCollectionStatistics(String role) {
+ CollectionStatistics cs = (CollectionStatistics) collectionStatistics.get(role);
+ if (cs==null) {
+ cs = new CollectionStatistics(role);
+ collectionStatistics.put(role, cs);
+ }
+ return cs;
+ }
+
+ public synchronized void loadCollection(String role) {
+ collectionLoadCount++;
+ getCollectionStatistics(role).loadCount++;
+ }
+
+ public synchronized void fetchCollection(String role) {
+ collectionFetchCount++;
+ getCollectionStatistics(role).fetchCount++;
+ }
+
+ public synchronized void updateCollection(String role) {
+ collectionUpdateCount++;
+ getCollectionStatistics(role).updateCount++;
+ }
+
+ public synchronized void recreateCollection(String role) {
+ collectionRecreateCount++;
+ getCollectionStatistics(role).recreateCount++;
+ }
+
+ public synchronized void removeCollection(String role) {
+ collectionRemoveCount++;
+ getCollectionStatistics(role).removeCount++;
+ }
+
+ /**
+ * Second level cache statistics per region
+ *
+ * @param regionName region name
+ * @return SecondLevelCacheStatistics
+ */
+ public synchronized SecondLevelCacheStatistics getSecondLevelCacheStatistics(String regionName) {
+ SecondLevelCacheStatistics slcs = ( SecondLevelCacheStatistics ) secondLevelCacheStatistics.get( regionName );
+ if ( slcs == null ) {
+ if ( sessionFactory == null ) {
+ return null;
+ }
+ Region region = sessionFactory.getSecondLevelCacheRegion( regionName );
+ if ( region == null ) {
+ return null;
+ }
+ slcs = new SecondLevelCacheStatistics( region );
+ secondLevelCacheStatistics.put( regionName, slcs );
+ }
+ return slcs;
+ }
+
+ public synchronized void secondLevelCachePut(String regionName) {
+ secondLevelCachePutCount++;
+ getSecondLevelCacheStatistics(regionName).putCount++;
+ }
+
+ public synchronized void secondLevelCacheHit(String regionName) {
+ secondLevelCacheHitCount++;
+ getSecondLevelCacheStatistics(regionName).hitCount++;
+ }
+
+ public synchronized void secondLevelCacheMiss(String regionName) {
+ secondLevelCacheMissCount++;
+ getSecondLevelCacheStatistics(regionName).missCount++;
+ }
+
+ public synchronized void queryExecuted(String hql, int rows, long time) {
+ queryExecutionCount++;
+ if (queryExecutionMaxTime<time) {
+ queryExecutionMaxTime=time;
+ queryExecutionMaxTimeQueryString = hql;
+ }
+ if (hql!=null) {
+ QueryStatistics qs = getQueryStatistics(hql);
+ qs.executed(rows, time);
+ }
+ }
+
+ public synchronized void queryCacheHit(String hql, String regionName) {
+ queryCacheHitCount++;
+ if (hql!=null) {
+ QueryStatistics qs = getQueryStatistics(hql);
+ qs.cacheHitCount++;
+ }
+ SecondLevelCacheStatistics slcs = getSecondLevelCacheStatistics(regionName);
+ slcs.hitCount++;
+ }
+
+ public synchronized void queryCacheMiss(String hql, String regionName) {
+ queryCacheMissCount++;
+ if (hql!=null) {
+ QueryStatistics qs = getQueryStatistics(hql);
+ qs.cacheMissCount++;
+ }
+ SecondLevelCacheStatistics slcs = getSecondLevelCacheStatistics(regionName);
+ slcs.missCount++;
+ }
+
+ public synchronized void queryCachePut(String hql, String regionName) {
+ queryCachePutCount++;
+ if (hql!=null) {
+ QueryStatistics qs = getQueryStatistics(hql);
+ qs.cachePutCount++;
+ }
+ SecondLevelCacheStatistics slcs = getSecondLevelCacheStatistics(regionName);
+ slcs.putCount++;
+ }
+
+ /**
+ * Query statistics from query string (HQL or SQL)
+ *
+ * @param queryString query string
+ * @return QueryStatistics
+ */
+ public synchronized QueryStatistics getQueryStatistics(String queryString) {
+ QueryStatistics qs = (QueryStatistics) queryStatistics.get(queryString);
+ if (qs==null) {
+ qs = new QueryStatistics(queryString);
+ queryStatistics.put(queryString, qs);
+ }
+ return qs;
+ }
+
+ /**
+ * @return entity deletion count
+ */
+ public long getEntityDeleteCount() {
+ return entityDeleteCount;
+ }
+
+ /**
+ * @return entity insertion count
+ */
+ public long getEntityInsertCount() {
+ return entityInsertCount;
+ }
+
+ /**
+ * @return entity load (from DB)
+ */
+ public long getEntityLoadCount() {
+ return entityLoadCount;
+ }
+
+ /**
+ * @return entity fetch (from DB)
+ */
+ public long getEntityFetchCount() {
+ return entityFetchCount;
+ }
+
+ /**
+ * @return entity update
+ */
+ public long getEntityUpdateCount() {
+ return entityUpdateCount;
+ }
+
+ public long getQueryExecutionCount() {
+ return queryExecutionCount;
+ }
+
+ public long getQueryCacheHitCount() {
+ return queryCacheHitCount;
+ }
+
+ public long getQueryCacheMissCount() {
+ return queryCacheMissCount;
+ }
+
+ public long getQueryCachePutCount() {
+ return queryCachePutCount;
+ }
+
+ /**
+ * @return flush
+ */
+ public long getFlushCount() {
+ return flushCount;
+ }
+
+ /**
+ * @return session connect
+ */
+ public long getConnectCount() {
+ return connectCount;
+ }
+
+ /**
+ * @return second level cache hit
+ */
+ public long getSecondLevelCacheHitCount() {
+ return secondLevelCacheHitCount;
+ }
+
+ /**
+ * @return second level cache miss
+ */
+ public long getSecondLevelCacheMissCount() {
+ return secondLevelCacheMissCount;
+ }
+
+ /**
+ * @return second level cache put
+ */
+ public long getSecondLevelCachePutCount() {
+ return secondLevelCachePutCount;
+ }
+
+ /**
+ * @return session closing
+ */
+ public long getSessionCloseCount() {
+ return sessionCloseCount;
+ }
+
+ /**
+ * @return session opening
+ */
+ public long getSessionOpenCount() {
+ return sessionOpenCount;
+ }
+
+ /**
+ * @return collection loading (from DB)
+ */
+ public long getCollectionLoadCount() {
+ return collectionLoadCount;
+ }
+
+ /**
+ * @return collection fetching (from DB)
+ */
+ public long getCollectionFetchCount() {
+ return collectionFetchCount;
+ }
+
+ /**
+ * @return collection update
+ */
+ public long getCollectionUpdateCount() {
+ return collectionUpdateCount;
+ }
+
+ /**
+ * @return collection removal
+ * FIXME: even if isInverse="true"?
+ */
+ public long getCollectionRemoveCount() {
+ return collectionRemoveCount;
+ }
+ /**
+ * @return collection recreation
+ */
+ public long getCollectionRecreateCount() {
+ return collectionRecreateCount;
+ }
+
+ /**
+ * @return start time in ms (JVM standards {@link System#currentTimeMillis()})
+ */
+ public long getStartTime() {
+ return startTime;
+ }
+
+ /**
+ * log in info level the main statistics
+ */
+ public void logSummary() {
+ log.info("Logging statistics....");
+ log.info("start time: " + startTime);
+ log.info("sessions opened: " + sessionOpenCount);
+ log.info("sessions closed: " + sessionCloseCount);
+ log.info("transactions: " + transactionCount);
+ log.info("successful transactions: " + commitedTransactionCount);
+ log.info("optimistic lock failures: " + optimisticFailureCount);
+ log.info("flushes: " + flushCount);
+ log.info("connections obtained: " + connectCount);
+ log.info("statements prepared: " + prepareStatementCount);
+ log.info("statements closed: " + closeStatementCount);
+ log.info("second level cache puts: " + secondLevelCachePutCount);
+ log.info("second level cache hits: " + secondLevelCacheHitCount);
+ log.info("second level cache misses: " + secondLevelCacheMissCount);
+ log.info("entities loaded: " + entityLoadCount);
+ log.info("entities updated: " + entityUpdateCount);
+ log.info("entities inserted: " + entityInsertCount);
+ log.info("entities deleted: " + entityDeleteCount);
+ log.info("entities fetched (minimize this): " + entityFetchCount);
+ log.info("collections loaded: " + collectionLoadCount);
+ log.info("collections updated: " + collectionUpdateCount);
+ log.info("collections removed: " + collectionRemoveCount);
+ log.info("collections recreated: " + collectionRecreateCount);
+ log.info("collections fetched (minimize this): " + collectionFetchCount);
+ log.info("queries executed to database: " + queryExecutionCount);
+ log.info("query cache puts: " + queryCachePutCount);
+ log.info("query cache hits: " + queryCacheHitCount);
+ log.info("query cache misses: " + queryCacheMissCount);
+ log.info("max query time: " + queryExecutionMaxTime + "ms");
+ }
+
+ /**
+ * Are statistics logged
+ */
+ public boolean isStatisticsEnabled() {
+ return isStatisticsEnabled;
+ }
+
+ /**
+ * Enable statistics logs (this is a dynamic parameter)
+ */
+ public void setStatisticsEnabled(boolean b) {
+ isStatisticsEnabled = b;
+ }
+
+ /**
+ * @return Returns the max query execution time,
+ * for all queries
+ */
+ public long getQueryExecutionMaxTime() {
+ return queryExecutionMaxTime;
+ }
+
+ /**
+ * Get all executed query strings
+ */
+ public String[] getQueries() {
+ return ArrayHelper.toStringArray( queryStatistics.keySet() );
+ }
+
+ /**
+ * Get the names of all entities
+ */
+ public String[] getEntityNames() {
+ if (sessionFactory==null) {
+ return ArrayHelper.toStringArray( entityStatistics.keySet() );
+ }
+ else {
+ return ArrayHelper.toStringArray( sessionFactory.getAllClassMetadata().keySet() );
+ }
+ }
+
+ /**
+ * Get the names of all collection roles
+ */
+ public String[] getCollectionRoleNames() {
+ if (sessionFactory==null) {
+ return ArrayHelper.toStringArray( collectionStatistics.keySet() );
+ }
+ else {
+ return ArrayHelper.toStringArray( sessionFactory.getAllCollectionMetadata().keySet() );
+ }
+ }
+
+ /**
+ * Get all second-level cache region names
+ */
+ public String[] getSecondLevelCacheRegionNames() {
+ if (sessionFactory==null) {
+ return ArrayHelper.toStringArray( secondLevelCacheStatistics.keySet() );
+ }
+ else {
+ return ArrayHelper.toStringArray( sessionFactory.getAllSecondLevelCacheRegions().keySet() );
+ }
+ }
+
+ public void endTransaction(boolean success) {
+ transactionCount++;
+ if (success) commitedTransactionCount++;
+ }
+
+ public long getSuccessfulTransactionCount() {
+ return commitedTransactionCount;
+ }
+
+ public long getTransactionCount() {
+ return transactionCount;
+ }
+
+ public void closeStatement() {
+ closeStatementCount++;
+ }
+
+ public void prepareStatement() {
+ prepareStatementCount++;
+ }
+
+ public long getCloseStatementCount() {
+ return closeStatementCount;
+ }
+
+ public long getPrepareStatementCount() {
+ return prepareStatementCount;
+ }
+
+ public void optimisticFailure(String entityName) {
+ optimisticFailureCount++;
+ getEntityStatistics(entityName).optimisticFailureCount++;
+ }
+
+ public long getOptimisticFailureCount() {
+ return optimisticFailureCount;
+ }
+ public String toString() {
+ return new StringBuffer()
+ .append("Statistics[")
+ .append("start time=").append(startTime)
+ .append(",sessions opened=").append(sessionOpenCount)
+ .append(",sessions closed=").append(sessionCloseCount)
+ .append(",transactions=").append(transactionCount)
+ .append(",successful transactions=").append(commitedTransactionCount)
+ .append(",optimistic lock failures=").append(optimisticFailureCount)
+ .append(",flushes=").append(flushCount)
+ .append(",connections obtained=").append(connectCount)
+ .append(",statements prepared=").append(prepareStatementCount)
+ .append(",statements closed=").append(closeStatementCount)
+ .append(",second level cache puts=").append(secondLevelCachePutCount)
+ .append(",second level cache hits=").append(secondLevelCacheHitCount)
+ .append(",second level cache misses=").append(secondLevelCacheMissCount)
+ .append(",entities loaded=").append(entityLoadCount)
+ .append(",entities updated=").append(entityUpdateCount)
+ .append(",entities inserted=").append(entityInsertCount)
+ .append(",entities deleted=").append(entityDeleteCount)
+ .append(",entities fetched=").append(entityFetchCount)
+ .append(",collections loaded=").append(collectionLoadCount)
+ .append(",collections updated=").append(collectionUpdateCount)
+ .append(",collections removed=").append(collectionRemoveCount)
+ .append(",collections recreated=").append(collectionRecreateCount)
+ .append(",collections fetched=").append(collectionFetchCount)
+ .append(",queries executed to database=").append(queryExecutionCount)
+ .append(",query cache puts=").append(queryCachePutCount)
+ .append(",query cache hits=").append(queryCacheHitCount)
+ .append(",query cache misses=").append(queryCacheMissCount)
+ .append(",max query time=").append(queryExecutionMaxTime)
+ .append(']')
+ .toString();
+ }
+
+ public String getQueryExecutionMaxTimeQueryString() {
+ return queryExecutionMaxTimeQueryString;
+ }
+
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/stat/StatisticsImplementor.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/stat/StatisticsImplementor.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/stat/StatisticsImplementor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,35 @@
+//$Id: StatisticsImplementor.java 7864 2005-08-11 23:22:52Z oneovthafew $
+package org.hibernate.stat;
+
+/**
+ * Statistics SPI for the Hibernate core
+ *
+ * @author Emmanuel Bernard
+ */
+public interface StatisticsImplementor {
+ public void openSession();
+ public void closeSession();
+ public void flush();
+ public void connect();
+ public void loadEntity(String entityName);
+ public void fetchEntity(String entityName);
+ public void updateEntity(String entityName);
+ public void insertEntity(String entityName);
+ public void deleteEntity(String entityName);
+ public void loadCollection(String role);
+ public void fetchCollection(String role);
+ public void updateCollection(String role);
+ public void recreateCollection(String role);
+ public void removeCollection(String role);
+ public void secondLevelCachePut(String regionName);
+ public void secondLevelCacheHit(String regionName);
+ public void secondLevelCacheMiss(String regionName);
+ public void queryExecuted(String hql, int rows, long time);
+ public void queryCacheHit(String hql, String regionName);
+ public void queryCacheMiss(String hql, String regionName);
+ public void queryCachePut(String hql, String regionName);
+ public void endTransaction(boolean success);
+ public void closeStatement();
+ public void prepareStatement();
+ public void optimisticFailure(String entityName);
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/stat/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/stat/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/stat/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,9 @@
+<html>
+<head></head>
+<body>
+<p>
+ This package exposes statistics about a running
+ Hibernate instance to the application.
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/stat/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/ColumnMetadata.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/ColumnMetadata.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/ColumnMetadata.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,63 @@
+//$Id: ColumnMetadata.java 7854 2005-08-11 20:41:21Z oneovthafew $
+package org.hibernate.tool.hbm2ddl;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.StringTokenizer;
+
+/**
+ * JDBC column metadata
+ * @author Christoph Sturm
+ */
+public class ColumnMetadata {
+ private final String name;
+ private final String typeName;
+ private final int columnSize;
+ private final int decimalDigits;
+ private final String isNullable;
+ private final int typeCode;
+
+ ColumnMetadata(ResultSet rs) throws SQLException {
+ name = rs.getString("COLUMN_NAME");
+ columnSize = rs.getInt("COLUMN_SIZE");
+ decimalDigits = rs.getInt("DECIMAL_DIGITS");
+ isNullable = rs.getString("IS_NULLABLE");
+ typeCode = rs.getInt("DATA_TYPE");
+ typeName = new StringTokenizer( rs.getString("TYPE_NAME"), "() " ).nextToken();
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getTypeName() {
+ return typeName;
+ }
+
+ public int getColumnSize() {
+ return columnSize;
+ }
+
+ public int getDecimalDigits() {
+ return decimalDigits;
+ }
+
+ public String getNullable() {
+ return isNullable;
+ }
+
+ public String toString() {
+ return "ColumnMetadata(" + name + ')';
+ }
+
+ public int getTypeCode() {
+ return typeCode;
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/ConnectionHelper.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/ConnectionHelper.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/ConnectionHelper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,36 @@
+package org.hibernate.tool.hbm2ddl;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+/**
+ * Contract for delegates responsible for managing connection used by the
+ * hbm2ddl tools.
+ *
+ * @author Steve Ebersole
+ */
+interface ConnectionHelper {
+ /**
+ * Prepare the helper for use.
+ *
+ * @param needsAutoCommit Should connection be forced to auto-commit
+ * if not already.
+ * @throws SQLException
+ */
+ public void prepare(boolean needsAutoCommit) throws SQLException;
+
+ /**
+ * Get a reference to the connection we are using.
+ *
+ * @return The JDBC connection.
+ * @throws SQLException
+ */
+ public Connection getConnection() throws SQLException;
+
+ /**
+ * Release any resources held by this helper.
+ *
+ * @throws SQLException
+ */
+ public void release() throws SQLException;
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/DatabaseMetadata.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/DatabaseMetadata.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/DatabaseMetadata.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,181 @@
+//$Id: DatabaseMetadata.java 10726 2006-11-06 14:50:05Z max.andersen at jboss.com $
+package org.hibernate.tool.hbm2ddl;
+
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.HibernateException;
+import org.hibernate.exception.JDBCExceptionHelper;
+import org.hibernate.exception.SQLExceptionConverter;
+import org.hibernate.mapping.Table;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.util.StringHelper;
+
+/**
+ * JDBC database metadata
+ * @author Christoph Sturm, Teodor Danciu
+ */
+public class DatabaseMetadata {
+
+ private static final Log log = LogFactory.getLog(DatabaseMetadata.class);
+
+ private final Map tables = new HashMap();
+ private final Set sequences = new HashSet();
+ private final boolean extras;
+
+ private DatabaseMetaData meta;
+ private SQLExceptionConverter sqlExceptionConverter;
+
+ public DatabaseMetadata(Connection connection, Dialect dialect) throws SQLException {
+ this(connection, dialect, true);
+ }
+
+ public DatabaseMetadata(Connection connection, Dialect dialect, boolean extras) throws SQLException {
+ sqlExceptionConverter = dialect.buildSQLExceptionConverter();
+ meta = connection.getMetaData();
+ this.extras = extras;
+ initSequences(connection, dialect);
+ }
+
+ private static final String[] TYPES = {"TABLE"};
+
+ public TableMetadata getTableMetadata(String name, String schema, String catalog, boolean isQuoted) throws HibernateException {
+
+ Object identifier = identifier(catalog, schema, name);
+ TableMetadata table = (TableMetadata) tables.get(identifier);
+ if (table!=null) {
+ return table;
+ }
+ else {
+
+ try {
+ ResultSet rs = null;
+ try {
+ if ( (isQuoted && meta.storesMixedCaseQuotedIdentifiers())) {
+ rs = meta.getTables(catalog, schema, name, TYPES);
+ } else if ( (isQuoted && meta.storesUpperCaseQuotedIdentifiers())
+ || (!isQuoted && meta.storesUpperCaseIdentifiers() )) {
+ rs = meta.getTables(
+ StringHelper.toUpperCase(catalog),
+ StringHelper.toUpperCase(schema),
+ StringHelper.toUpperCase(name),
+ TYPES
+ );
+ }
+ else if ( (isQuoted && meta.storesLowerCaseQuotedIdentifiers())
+ || (!isQuoted && meta.storesLowerCaseIdentifiers() )) {
+ rs = meta.getTables(
+ StringHelper.toLowerCase(catalog),
+ StringHelper.toLowerCase(schema),
+ StringHelper.toLowerCase(name),
+ TYPES
+ );
+ }
+ else {
+ rs = meta.getTables(catalog, schema, name, TYPES);
+ }
+
+ while ( rs.next() ) {
+ String tableName = rs.getString("TABLE_NAME");
+ if ( name.equalsIgnoreCase(tableName) ) {
+ table = new TableMetadata(rs, meta, extras);
+ tables.put(identifier, table);
+ return table;
+ }
+ }
+
+ log.info("table not found: " + name);
+ return null;
+
+ }
+ finally {
+ if (rs!=null) rs.close();
+ }
+ }
+ catch (SQLException sqle) {
+ throw JDBCExceptionHelper.convert(
+ sqlExceptionConverter,
+ sqle,
+ "could not get table metadata: " + name
+ );
+ }
+ }
+
+ }
+
+ private Object identifier(String catalog, String schema, String name) {
+ return Table.qualify(catalog,schema,name);
+ }
+
+ private void initSequences(Connection connection, Dialect dialect) throws SQLException {
+ if ( dialect.supportsSequences() ) {
+ String sql = dialect.getQuerySequencesString();
+ if (sql!=null) {
+
+ Statement statement = null;
+ ResultSet rs = null;
+ try {
+ statement = connection.createStatement();
+ rs = statement.executeQuery(sql);
+
+ while ( rs.next() ) {
+ sequences.add( rs.getString(1).toLowerCase().trim() );
+ }
+ }
+ finally {
+ if (rs!=null) rs.close();
+ if (statement!=null) statement.close();
+ }
+
+ }
+ }
+ }
+
+ public boolean isSequence(Object key) {
+ if (key instanceof String){
+ String[] strings = StringHelper.split(".", (String) key);
+ return sequences.contains( strings[strings.length-1].toLowerCase());
+ }
+ return false;
+ }
+
+ public boolean isTable(Object key) throws HibernateException {
+ if(key instanceof String) {
+ Table tbl = new Table((String)key);
+ if ( getTableMetadata( tbl.getName(), tbl.getSchema(), tbl.getCatalog(), tbl.isQuoted() ) != null ) {
+ return true;
+ } else {
+ String[] strings = StringHelper.split(".", (String) key);
+ if(strings.length==3) {
+ tbl = new Table(strings[2]);
+ tbl.setCatalog(strings[0]);
+ tbl.setSchema(strings[1]);
+ return getTableMetadata( tbl.getName(), tbl.getSchema(), tbl.getCatalog(), tbl.isQuoted() ) != null;
+ } else if (strings.length==2) {
+ tbl = new Table(strings[1]);
+ tbl.setSchema(strings[0]);
+ return getTableMetadata( tbl.getName(), tbl.getSchema(), tbl.getCatalog(), tbl.isQuoted() ) != null;
+ }
+ }
+ }
+ return false;
+ }
+
+ public String toString() {
+ return "DatabaseMetadata" + tables.keySet().toString() + sequences.toString();
+ }
+}
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/ForeignKeyMetadata.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/ForeignKeyMetadata.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/ForeignKeyMetadata.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,42 @@
+//$Id: ForeignKeyMetadata.java 4279 2004-08-13 08:05:47Z oneovthafew $
+package org.hibernate.tool.hbm2ddl;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * JDBC foreign key metadata
+ * @author Christoph Sturm
+ */
+public class ForeignKeyMetadata {
+ private final String name;
+ private final List columns = new ArrayList();
+
+ ForeignKeyMetadata(ResultSet rs) throws SQLException {
+ name = rs.getString("FK_NAME");
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ void addColumn(ColumnMetadata column) {
+ if (column != null) columns.add(column);
+ }
+
+ public ColumnMetadata[] getColumns() {
+ return (ColumnMetadata[]) columns.toArray(new ColumnMetadata[0]);
+ }
+
+ public String toString() {
+ return "ForeignKeyMetadata(" + name + ')';
+ }
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/IndexMetadata.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/IndexMetadata.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/IndexMetadata.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,42 @@
+//$Id: IndexMetadata.java 4279 2004-08-13 08:05:47Z oneovthafew $
+package org.hibernate.tool.hbm2ddl;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * JDBC index metadata
+ * @author Christoph Sturm
+ */
+public class IndexMetadata {
+ private final String name;
+ private final List columns = new ArrayList();
+
+ IndexMetadata(ResultSet rs) throws SQLException {
+ name = rs.getString("INDEX_NAME");
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ void addColumn(ColumnMetadata column) {
+ if (column != null) columns.add(column);
+ }
+
+ public ColumnMetadata[] getColumns() {
+ return (ColumnMetadata[]) columns.toArray(new ColumnMetadata[0]);
+ }
+
+ public String toString() {
+ return "IndexMatadata(" + name + ')';
+ }
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/ManagedProviderConnectionHelper.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/ManagedProviderConnectionHelper.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/ManagedProviderConnectionHelper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,51 @@
+package org.hibernate.tool.hbm2ddl;
+
+import org.hibernate.connection.ConnectionProvider;
+import org.hibernate.connection.ConnectionProviderFactory;
+import org.hibernate.util.JDBCExceptionReporter;
+
+import java.util.Properties;
+import java.sql.Connection;
+import java.sql.SQLException;
+
+/**
+ * A {@link ConnectionHelper} implementation based on an internally
+ * built and managed {@link ConnectionProvider}.
+ *
+ * @author Steve Ebersole
+ */
+class ManagedProviderConnectionHelper implements ConnectionHelper {
+ private Properties cfgProperties;
+ private ConnectionProvider connectionProvider;
+ private Connection connection;
+
+ public ManagedProviderConnectionHelper(Properties cfgProperties) {
+ this.cfgProperties = cfgProperties;
+ }
+
+ public void prepare(boolean needsAutoCommit) throws SQLException {
+ connectionProvider = ConnectionProviderFactory.newConnectionProvider( cfgProperties );
+ connection = connectionProvider.getConnection();
+ if ( needsAutoCommit && !connection.getAutoCommit() ) {
+ connection.commit();
+ connection.setAutoCommit( true );
+ }
+ }
+
+ public Connection getConnection() throws SQLException {
+ return connection;
+ }
+
+ public void release() throws SQLException {
+ if ( connection != null ) {
+ try {
+ JDBCExceptionReporter.logAndClearWarnings( connection );
+ connectionProvider.closeConnection( connection );
+ }
+ finally {
+ connectionProvider.close();
+ }
+ }
+ connection = null;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/SchemaExport.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/SchemaExport.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/SchemaExport.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,436 @@
+//$Id: SchemaExport.java 10765 2006-11-08 04:30:27Z steve.ebersole at jboss.com $
+package org.hibernate.tool.hbm2ddl;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.Writer;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.SQLWarning;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.HibernateException;
+import org.hibernate.JDBCException;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.cfg.NamingStrategy;
+import org.hibernate.cfg.Settings;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.pretty.DDLFormatter;
+import org.hibernate.util.ConfigHelper;
+import org.hibernate.util.PropertiesHelper;
+import org.hibernate.util.ReflectHelper;
+import org.hibernate.util.JDBCExceptionReporter;
+
+/**
+ * Commandline tool to export table schema to the database. This
+ * class may also be called from inside an application.
+ *
+ * @author Daniel Bradby, Gavin King
+ */
+public class SchemaExport {
+
+ private static final Log log = LogFactory.getLog( SchemaExport.class );
+
+ private ConnectionHelper connectionHelper;
+ private String[] dropSQL;
+ private String[] createSQL;
+ private String outputFile = null;
+ private String importFile = "/import.sql";
+ private Dialect dialect;
+ private String delimiter;
+ private final List exceptions = new ArrayList();
+ private boolean haltOnError = false;
+ private boolean format = true;
+
+ /**
+ * Create a schema exporter for the given Configuration
+ */
+ public SchemaExport(Configuration cfg) throws HibernateException {
+ this( cfg, cfg.getProperties() );
+ }
+
+ /**
+ * Create a schema exporter for the given Configuration
+ * and given settings
+ */
+ public SchemaExport(Configuration cfg, Settings settings) throws HibernateException {
+ dialect = settings.getDialect();
+ connectionHelper = new SuppliedConnectionProviderConnectionHelper(
+ settings.getConnectionProvider()
+ );
+ dropSQL = cfg.generateDropSchemaScript( dialect );
+ createSQL = cfg.generateSchemaCreationScript( dialect );
+ format = settings.isFormatSqlEnabled();
+ }
+
+ /**
+ * Create a schema exporter for the given Configuration, with the given
+ * database connection properties.
+ *
+ * @deprecated properties may be specified via the Configuration object
+ */
+ public SchemaExport(Configuration cfg, Properties properties)
+ throws HibernateException {
+ dialect = Dialect.getDialect( properties );
+
+ Properties props = new Properties();
+ props.putAll( dialect.getDefaultProperties() );
+ props.putAll( properties );
+
+ connectionHelper = new ManagedProviderConnectionHelper( props );
+ dropSQL = cfg.generateDropSchemaScript( dialect );
+ createSQL = cfg.generateSchemaCreationScript( dialect );
+ format = PropertiesHelper.getBoolean( Environment.FORMAT_SQL, props );
+ }
+
+ public SchemaExport(Configuration cfg, Connection connection) {
+ this.connectionHelper = new SuppliedConnectionHelper( connection );
+ dialect = Dialect.getDialect( cfg.getProperties() );
+ dropSQL = cfg.generateDropSchemaScript( dialect );
+ createSQL = cfg.generateSchemaCreationScript( dialect );
+ }
+
+ /**
+ * Set an output filename. The generated script will be written to this file.
+ */
+ public SchemaExport setOutputFile(String filename) {
+ outputFile = filename;
+ return this;
+ }
+
+ public SchemaExport setImportFile(String filename) {
+ importFile = filename;
+ return this;
+ }
+
+ /**
+ * Set the end of statement delimiter
+ */
+ public SchemaExport setDelimiter(String delimiter) {
+ this.delimiter = delimiter;
+ return this;
+ }
+
+ /**
+ * Run the schema creation script.
+ *
+ * @param script print the DDL to the console
+ * @param export export the script to the database
+ */
+ public void create(boolean script, boolean export) {
+ execute( script, export, false, false );
+ }
+
+ /**
+ * Run the drop schema script.
+ *
+ * @param script print the DDL to the console
+ * @param export export the script to the database
+ */
+ public void drop(boolean script, boolean export) {
+ execute( script, export, true, false );
+ }
+
+ private String format(String sql) {
+ return format ?
+ new DDLFormatter( sql ).format() :
+ sql;
+ }
+
+ public void execute(boolean script, boolean export, boolean justDrop, boolean justCreate) {
+
+ log.info( "Running hbm2ddl schema export" );
+
+ Connection connection = null;
+ Writer outputFileWriter = null;
+ Reader importFileReader = null;
+ Statement statement = null;
+
+ exceptions.clear();
+
+ try {
+
+ try {
+ InputStream stream = ConfigHelper.getResourceAsStream( importFile );
+ importFileReader = new InputStreamReader( stream );
+ }
+ catch ( HibernateException e ) {
+ log.debug( "import file not found: " + importFile );
+ }
+
+ if ( outputFile != null ) {
+ log.info( "writing generated schema to file: " + outputFile );
+ outputFileWriter = new FileWriter( outputFile );
+ }
+
+ if ( export ) {
+ log.info( "exporting generated schema to database" );
+ connectionHelper.prepare( true );
+ connection = connectionHelper.getConnection();
+ statement = connection.createStatement();
+ }
+
+ if ( !justCreate ) {
+ drop( script, export, outputFileWriter, statement );
+ }
+
+ if ( !justDrop ) {
+ create( script, export, outputFileWriter, statement );
+ if ( export && importFileReader != null ) {
+ importScript( importFileReader, statement );
+ }
+ }
+
+ log.info( "schema export complete" );
+
+ }
+
+ catch ( Exception e ) {
+ exceptions.add( e );
+ log.error( "schema export unsuccessful", e );
+ }
+
+ finally {
+
+ try {
+ if ( statement != null ) {
+ statement.close();
+ }
+ if ( connection != null ) {
+ connectionHelper.release();
+ }
+ }
+ catch ( Exception e ) {
+ exceptions.add( e );
+ log.error( "Could not close connection", e );
+ }
+
+ try {
+ if ( outputFileWriter != null ) {
+ outputFileWriter.close();
+ }
+ if ( importFileReader != null ) {
+ importFileReader.close();
+ }
+ }
+ catch ( IOException ioe ) {
+ exceptions.add( ioe );
+ log.error( "Error closing output file: " + outputFile, ioe );
+ }
+
+ }
+ }
+
+ private void importScript(Reader importFileReader, Statement statement)
+ throws IOException {
+ log.info( "Executing import script: " + importFile );
+ BufferedReader reader = new BufferedReader( importFileReader );
+ long lineNo = 0;
+ for ( String sql = reader.readLine(); sql != null; sql = reader.readLine() ) {
+ try {
+ lineNo++;
+ String trimmedSql = sql.trim();
+ if ( trimmedSql.length() == 0 ||
+ trimmedSql.startsWith( "--" ) ||
+ trimmedSql.startsWith( "//" ) ||
+ trimmedSql.startsWith( "/*" ) ) {
+ continue;
+ }
+ else {
+ if ( trimmedSql.endsWith( ";" ) ) {
+ trimmedSql = trimmedSql.substring( 0, trimmedSql.length() - 1 );
+ }
+ log.debug( trimmedSql );
+ statement.execute( trimmedSql );
+ }
+ }
+ catch ( SQLException e ) {
+ throw new JDBCException( "Error during import script execution at line " + lineNo, e );
+ }
+ }
+ }
+
+ private void create(boolean script, boolean export, Writer fileOutput, Statement statement)
+ throws IOException {
+ for ( int j = 0; j < createSQL.length; j++ ) {
+ try {
+ execute( script, export, fileOutput, statement, createSQL[j] );
+ }
+ catch ( SQLException e ) {
+ if ( haltOnError ) {
+ throw new JDBCException( "Error during DDL export", e );
+ }
+ exceptions.add( e );
+ log.error( "Unsuccessful: " + createSQL[j] );
+ log.error( e.getMessage() );
+ }
+ }
+ }
+
+ private void drop(boolean script, boolean export, Writer fileOutput, Statement statement)
+ throws IOException {
+ for ( int i = 0; i < dropSQL.length; i++ ) {
+ try {
+ execute( script, export, fileOutput, statement, dropSQL[i] );
+ }
+ catch ( SQLException e ) {
+ exceptions.add( e );
+ log.debug( "Unsuccessful: " + dropSQL[i] );
+ log.debug( e.getMessage() );
+ }
+ }
+ }
+
+ private void execute(boolean script, boolean export, Writer fileOutput, Statement statement, final String sql)
+ throws IOException, SQLException {
+ String formatted = format( sql );
+ if ( delimiter != null ) {
+ formatted += delimiter;
+ }
+ if ( script ) {
+ System.out.println( formatted );
+ }
+ log.debug( formatted );
+ if ( outputFile != null ) {
+ fileOutput.write( formatted + "\n" );
+ }
+ if ( export ) {
+ statement.executeUpdate( sql );
+ try {
+ SQLWarning warnings = statement.getWarnings();
+ if ( warnings != null) {
+ JDBCExceptionReporter.logAndClearWarnings( connectionHelper.getConnection() );
+ }
+ }
+ catch( SQLException sqle ) {
+ log.warn( "unable to log SQLWarnings : " + sqle );
+ }
+ }
+
+
+ }
+
+ public static void main(String[] args) {
+ try {
+ Configuration cfg = new Configuration();
+
+ boolean script = true;
+ boolean drop = false;
+ boolean create = false;
+ boolean halt = false;
+ boolean export = true;
+ String outFile = null;
+ String importFile = "/import.sql";
+ String propFile = null;
+ boolean format = false;
+ String delim = null;
+
+ for ( int i = 0; i < args.length; i++ ) {
+ if ( args[i].startsWith( "--" ) ) {
+ if ( args[i].equals( "--quiet" ) ) {
+ script = false;
+ }
+ else if ( args[i].equals( "--drop" ) ) {
+ drop = true;
+ }
+ else if ( args[i].equals( "--create" ) ) {
+ create = true;
+ }
+ else if ( args[i].equals( "--haltonerror" ) ) {
+ halt = true;
+ }
+ else if ( args[i].equals( "--text" ) ) {
+ export = false;
+ }
+ else if ( args[i].startsWith( "--output=" ) ) {
+ outFile = args[i].substring( 9 );
+ }
+ else if ( args[i].startsWith( "--import=" ) ) {
+ importFile = args[i].substring( 9 );
+ }
+ else if ( args[i].startsWith( "--properties=" ) ) {
+ propFile = args[i].substring( 13 );
+ }
+ else if ( args[i].equals( "--format" ) ) {
+ format = true;
+ }
+ else if ( args[i].startsWith( "--delimiter=" ) ) {
+ delim = args[i].substring( 12 );
+ }
+ else if ( args[i].startsWith( "--config=" ) ) {
+ cfg.configure( args[i].substring( 9 ) );
+ }
+ else if ( args[i].startsWith( "--naming=" ) ) {
+ cfg.setNamingStrategy(
+ ( NamingStrategy ) ReflectHelper.classForName( args[i].substring( 9 ) )
+ .newInstance()
+ );
+ }
+ }
+ else {
+ String filename = args[i];
+ if ( filename.endsWith( ".jar" ) ) {
+ cfg.addJar( new File( filename ) );
+ }
+ else {
+ cfg.addFile( filename );
+ }
+ }
+
+ }
+
+ if ( propFile != null ) {
+ Properties props = new Properties();
+ props.putAll( cfg.getProperties() );
+ props.load( new FileInputStream( propFile ) );
+ cfg.setProperties( props );
+ }
+
+ SchemaExport se = new SchemaExport( cfg )
+ .setHaltOnError( halt )
+ .setOutputFile( outFile )
+ .setImportFile( importFile )
+ .setDelimiter( delim );
+ if ( format ) {
+ se.setFormat( true );
+ }
+ se.execute( script, export, drop, create );
+
+ }
+ catch ( Exception e ) {
+ log.error( "Error creating schema ", e );
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Returns a List of all Exceptions which occured during the export.
+ *
+ * @return A List containig the Exceptions occured during the export
+ */
+ public List getExceptions() {
+ return exceptions;
+ }
+
+ public SchemaExport setFormat(boolean format) {
+ this.format = format;
+ return this;
+ }
+
+ public SchemaExport setHaltOnError(boolean haltOnError) {
+ this.haltOnError = haltOnError;
+ return this;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/SchemaExportTask.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/SchemaExportTask.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/SchemaExportTask.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,232 @@
+//$Id: SchemaExportTask.java 7863 2005-08-11 23:11:03Z oneovthafew $
+package org.hibernate.tool.hbm2ddl;
+
+import org.hibernate.HibernateException;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.NamingStrategy;
+import org.hibernate.util.ArrayHelper;
+import org.hibernate.util.ReflectHelper;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.tools.ant.types.FileSet;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * An Ant task for <tt>SchemaExport</tt>.
+ *
+ * <pre>
+ * <taskdef name="schemaexport"
+ * classname="org.hibernate.tool.hbm2ddl.SchemaExportTask"
+ * classpathref="class.path"/>
+ *
+ * <schemaexport
+ * properties="${build.classes.dir}/hibernate.properties"
+ * quiet="no"
+ * text="no"
+ * drop="no"
+ * delimiter=";"
+ * output="${build.dir}/schema-export.sql">
+ * <fileset dir="${build.classes.dir}">
+ * <include name="*.hbm.xml"/>
+ * </fileset>
+ * </schemaexport>
+ * </pre>
+ *
+ * @see SchemaExport
+ * @author Rong C Ou
+ */
+public class SchemaExportTask extends MatchingTask {
+
+ private List fileSets = new LinkedList();
+ private File propertiesFile = null;
+ private File configurationFile = null;
+ private File outputFile = null;
+ private boolean quiet = false;
+ private boolean text = false;
+ private boolean drop = false;
+ private boolean create = false;
+ private boolean haltOnError = false;
+ private String delimiter = null;
+ private String namingStrategy = null;
+
+ public void addFileset(FileSet set) {
+ fileSets.add(set);
+ }
+
+ /**
+ * Set a properties file
+ * @param propertiesFile the properties file name
+ */
+ public void setProperties(File propertiesFile) {
+ if ( !propertiesFile.exists() ) {
+ throw new BuildException("Properties file: " + propertiesFile + " does not exist.");
+ }
+
+ log("Using properties file " + propertiesFile, Project.MSG_DEBUG);
+ this.propertiesFile = propertiesFile;
+ }
+
+ /**
+ * Set a <literal>.cfg.xml</literal> file, which will be
+ * loaded as a resource, from the classpath
+ * @param configurationFile the path to the resource
+ */
+ public void setConfig(File configurationFile) {
+ this.configurationFile = configurationFile;
+ }
+
+ /**
+ * Enable "quiet" mode. The schema will not be
+ * written to standard out.
+ * @param quiet true to enable quiet mode
+ */
+ public void setQuiet(boolean quiet) {
+ this.quiet = quiet;
+ }
+
+ /**
+ * Enable "text-only" mode. The schema will not
+ * be exported to the database.
+ * @param text true to enable text-only mode
+ */
+ public void setText(boolean text) {
+ this.text = text;
+ }
+
+ /**
+ * Enable "drop" mode. Database objects will be
+ * dropped but not recreated.
+ * @param drop true to enable drop mode
+ */
+ public void setDrop(boolean drop) {
+ this.drop = drop;
+ }
+
+ /**
+ * Enable "create" mode. Database objects will be
+ * created but not first dropped.
+ * @param create true to enable create mode
+ */
+ public void setCreate(boolean create) {
+ this.create = create;
+ }
+
+ /**
+ * Set the end of statement delimiter for the generated script
+ * @param delimiter the delimiter
+ */
+ public void setDelimiter(String delimiter) {
+ this.delimiter = delimiter;
+ }
+
+ /**
+ * Set the script output file
+ * @param outputFile the file name
+ */
+ public void setOutput(File outputFile) {
+ this.outputFile = outputFile;
+ }
+
+ /**
+ * Execute the task
+ */
+ public void execute() throws BuildException {
+ try {
+ getSchemaExport( getConfiguration() ).execute(!quiet, !text, drop, create);
+ }
+ catch (HibernateException e) {
+ throw new BuildException("Schema text failed: " + e.getMessage(), e);
+ }
+ catch (FileNotFoundException e) {
+ throw new BuildException("File not found: " + e.getMessage(), e);
+ }
+ catch (IOException e) {
+ throw new BuildException("IOException : " + e.getMessage(), e);
+ }
+ catch (Exception e) {
+ throw new BuildException(e);
+ }
+ }
+
+ private String[] getFiles() {
+
+ List files = new LinkedList();
+ for ( Iterator i = fileSets.iterator(); i.hasNext(); ) {
+
+ FileSet fs = (FileSet) i.next();
+ DirectoryScanner ds = fs.getDirectoryScanner( getProject() );
+
+ String[] dsFiles = ds.getIncludedFiles();
+ for (int j = 0; j < dsFiles.length; j++) {
+ File f = new File(dsFiles[j]);
+ if ( !f.isFile() ) {
+ f = new File( ds.getBasedir(), dsFiles[j] );
+ }
+
+ files.add( f.getAbsolutePath() );
+ }
+ }
+
+ return ArrayHelper.toStringArray(files);
+ }
+
+ private Configuration getConfiguration() throws Exception {
+ Configuration cfg = new Configuration();
+ if (namingStrategy!=null) {
+ cfg.setNamingStrategy(
+ (NamingStrategy) ReflectHelper.classForName(namingStrategy).newInstance()
+ );
+ }
+ if (configurationFile != null) {
+ cfg.configure( configurationFile );
+ }
+
+ String[] files = getFiles();
+ for (int i = 0; i < files.length; i++) {
+ String filename = files[i];
+ if ( filename.endsWith(".jar") ) {
+ cfg.addJar( new File(filename) );
+ }
+ else {
+ cfg.addFile(filename);
+ }
+ }
+ return cfg;
+ }
+
+ private SchemaExport getSchemaExport(Configuration cfg) throws HibernateException, IOException {
+ Properties properties = new Properties();
+ properties.putAll( cfg.getProperties() );
+ if (propertiesFile == null) {
+ properties.putAll( getProject().getProperties() );
+ }
+ else {
+ properties.load( new FileInputStream(propertiesFile) );
+ }
+ cfg.setProperties(properties);
+ return new SchemaExport(cfg)
+ .setHaltOnError(haltOnError)
+ .setOutputFile( outputFile.getPath() )
+ .setDelimiter(delimiter);
+ }
+
+ public void setNamingStrategy(String namingStrategy) {
+ this.namingStrategy = namingStrategy;
+ }
+
+ public void setHaltonerror(boolean haltOnError) {
+ this.haltOnError = haltOnError;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/SchemaUpdate.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/SchemaUpdate.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/SchemaUpdate.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,191 @@
+//$Id: SchemaUpdate.java 9250 2006-02-10 03:48:37Z steveebersole $
+package org.hibernate.tool.hbm2ddl;
+
+import java.io.FileInputStream;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.HibernateException;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.NamingStrategy;
+import org.hibernate.cfg.Settings;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.util.ReflectHelper;
+
+/**
+ * A commandline tool to update a database schema. May also be called from
+ * inside an application.
+ *
+ * @author Christoph Sturm
+ */
+public class SchemaUpdate {
+
+ private static final Log log = LogFactory.getLog( SchemaUpdate.class );
+ private ConnectionHelper connectionHelper;
+ private Configuration configuration;
+ private Dialect dialect;
+ private List exceptions;
+
+ public SchemaUpdate(Configuration cfg) throws HibernateException {
+ this( cfg, cfg.getProperties() );
+ }
+
+ public SchemaUpdate(Configuration cfg, Properties connectionProperties) throws HibernateException {
+ this.configuration = cfg;
+ dialect = Dialect.getDialect( connectionProperties );
+ Properties props = new Properties();
+ props.putAll( dialect.getDefaultProperties() );
+ props.putAll( connectionProperties );
+ connectionHelper = new ManagedProviderConnectionHelper( props );
+ exceptions = new ArrayList();
+ }
+
+ public SchemaUpdate(Configuration cfg, Settings settings) throws HibernateException {
+ this.configuration = cfg;
+ dialect = settings.getDialect();
+ connectionHelper = new SuppliedConnectionProviderConnectionHelper(
+ settings.getConnectionProvider()
+ );
+ exceptions = new ArrayList();
+ }
+
+ public static void main(String[] args) {
+ try {
+ Configuration cfg = new Configuration();
+
+ boolean script = true;
+ // If true then execute db updates, otherwise just generate and display updates
+ boolean doUpdate = true;
+ String propFile = null;
+
+ for ( int i = 0; i < args.length; i++ ) {
+ if ( args[i].startsWith( "--" ) ) {
+ if ( args[i].equals( "--quiet" ) ) {
+ script = false;
+ }
+ else if ( args[i].startsWith( "--properties=" ) ) {
+ propFile = args[i].substring( 13 );
+ }
+ else if ( args[i].startsWith( "--config=" ) ) {
+ cfg.configure( args[i].substring( 9 ) );
+ }
+ else if ( args[i].startsWith( "--text" ) ) {
+ doUpdate = false;
+ }
+ else if ( args[i].startsWith( "--naming=" ) ) {
+ cfg.setNamingStrategy(
+ ( NamingStrategy ) ReflectHelper.classForName( args[i].substring( 9 ) ).newInstance()
+ );
+ }
+ }
+ else {
+ cfg.addFile( args[i] );
+ }
+
+ }
+
+ if ( propFile != null ) {
+ Properties props = new Properties();
+ props.putAll( cfg.getProperties() );
+ props.load( new FileInputStream( propFile ) );
+ cfg.setProperties( props );
+ }
+
+ new SchemaUpdate( cfg ).execute( script, doUpdate );
+ }
+ catch ( Exception e ) {
+ log.error( "Error running schema update", e );
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Execute the schema updates
+ *
+ * @param script print all DDL to the console
+ */
+ public void execute(boolean script, boolean doUpdate) {
+
+ log.info( "Running hbm2ddl schema update" );
+
+ Connection connection = null;
+ Statement stmt = null;
+
+ exceptions.clear();
+
+ try {
+
+ DatabaseMetadata meta;
+ try {
+ log.info( "fetching database metadata" );
+ connectionHelper.prepare( true );
+ connection = connectionHelper.getConnection();
+ meta = new DatabaseMetadata( connection, dialect );
+ stmt = connection.createStatement();
+ }
+ catch ( SQLException sqle ) {
+ exceptions.add( sqle );
+ log.error( "could not get database metadata", sqle );
+ throw sqle;
+ }
+
+ log.info( "updating schema" );
+
+ String[] createSQL = configuration.generateSchemaUpdateScript( dialect, meta );
+ for ( int j = 0; j < createSQL.length; j++ ) {
+
+ final String sql = createSQL[j];
+ try {
+ if ( script ) {
+ System.out.println( sql );
+ }
+ if ( doUpdate ) {
+ log.debug( sql );
+ stmt.executeUpdate( sql );
+ }
+ }
+ catch ( SQLException e ) {
+ exceptions.add( e );
+ log.error( "Unsuccessful: " + sql );
+ log.error( e.getMessage() );
+ }
+ }
+
+ log.info( "schema update complete" );
+
+ }
+ catch ( Exception e ) {
+ exceptions.add( e );
+ log.error( "could not complete schema update", e );
+ }
+ finally {
+
+ try {
+ if ( stmt != null ) {
+ stmt.close();
+ }
+ connectionHelper.release();
+ }
+ catch ( Exception e ) {
+ exceptions.add( e );
+ log.error( "Error closing connection", e );
+ }
+
+ }
+ }
+
+ /**
+ * Returns a List of all Exceptions which occured during the export.
+ *
+ * @return A List containig the Exceptions occured during the export
+ */
+ public List getExceptions() {
+ return exceptions;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/SchemaUpdateTask.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/SchemaUpdateTask.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/SchemaUpdateTask.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,182 @@
+//$Id: SchemaUpdateTask.java 7863 2005-08-11 23:11:03Z oneovthafew $
+package org.hibernate.tool.hbm2ddl;
+
+import org.hibernate.HibernateException;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.NamingStrategy;
+import org.hibernate.util.ArrayHelper;
+import org.hibernate.util.ReflectHelper;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.tools.ant.types.FileSet;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * An Ant task for <tt>SchemaUpdate</tt>.
+ *
+ * <pre>
+ * <taskdef name="schemaupdate"
+ * classname="org.hibernate.tool.hbm2ddl.SchemaUpdateTask"
+ * classpathref="class.path"/>
+ *
+ * <schemaupdate
+ * properties="${build.classes.dir}/hibernate.properties"
+ * quiet="no"
+ * <fileset dir="${build.classes.dir}">
+ * <include name="*.hbm.xml"/>
+ * </fileset>
+ * </schemaupdate>
+ * </pre>
+ *
+ * @see SchemaUpdate
+ * @author Rong C Ou, Gavin King
+ */
+public class SchemaUpdateTask extends MatchingTask {
+
+ private List fileSets = new LinkedList();
+ private File propertiesFile = null;
+ private File configurationFile = null;
+ private boolean quiet = false;
+ private boolean text = true;
+ private String namingStrategy = null;
+
+ public void addFileset(FileSet set) {
+ fileSets.add(set);
+ }
+
+ /**
+ * Set a properties file
+ * @param propertiesFile the properties file name
+ */
+ public void setProperties(File propertiesFile) {
+ if ( !propertiesFile.exists() ) {
+ throw new BuildException("Properties file: " + propertiesFile + " does not exist.");
+ }
+
+ log("Using properties file " + propertiesFile, Project.MSG_DEBUG);
+ this.propertiesFile = propertiesFile;
+ }
+
+ /**
+ * Set a <literal>.cfg.xml</literal> file
+ * @param configurationFile the file name
+ */
+ public void setConfig(File configurationFile) {
+ this.configurationFile = configurationFile;
+ }
+
+ /**
+ * Enable "text-only" mode. The schema will not
+ * be updated in the database.
+ * @param text true to enable text-only mode
+ */
+ public void setText(boolean text) {
+ this.text = text;
+ }
+
+ /**
+ * Enable "quiet" mode. The schema will not be
+ * written to standard out.
+ * @param quiet true to enable quiet mode
+ */
+ public void setQuiet(boolean quiet) {
+ this.quiet = quiet;
+ }
+
+ /**
+ * Execute the task
+ */
+ public void execute() throws BuildException {
+ try {
+ Configuration cfg = getConfiguration();
+ getSchemaUpdate(cfg).execute(!quiet, !text);
+ }
+ catch (HibernateException e) {
+ throw new BuildException("Schema text failed: " + e.getMessage(), e);
+ }
+ catch (FileNotFoundException e) {
+ throw new BuildException("File not found: " + e.getMessage(), e);
+ }
+ catch (IOException e) {
+ throw new BuildException("IOException : " + e.getMessage(), e);
+ }
+ catch (Exception e) {
+ throw new BuildException(e);
+ }
+ }
+
+ private String[] getFiles() {
+
+ List files = new LinkedList();
+ for ( Iterator i = fileSets.iterator(); i.hasNext(); ) {
+
+ FileSet fs = (FileSet) i.next();
+ DirectoryScanner ds = fs.getDirectoryScanner( getProject() );
+
+ String[] dsFiles = ds.getIncludedFiles();
+ for (int j = 0; j < dsFiles.length; j++) {
+ File f = new File(dsFiles[j]);
+ if ( !f.isFile() ) {
+ f = new File( ds.getBasedir(), dsFiles[j] );
+ }
+
+ files.add( f.getAbsolutePath() );
+ }
+ }
+
+ return ArrayHelper.toStringArray(files);
+ }
+
+ private Configuration getConfiguration() throws Exception {
+ Configuration cfg = new Configuration();
+ if (namingStrategy!=null) {
+ cfg.setNamingStrategy(
+ (NamingStrategy) ReflectHelper.classForName(namingStrategy).newInstance()
+ );
+ }
+ if (configurationFile!=null) {
+ cfg.configure( configurationFile );
+ }
+
+ String[] files = getFiles();
+ for (int i = 0; i < files.length; i++) {
+ String filename = files[i];
+ if ( filename.endsWith(".jar") ) {
+ cfg.addJar( new File(filename) );
+ }
+ else {
+ cfg.addFile(filename);
+ }
+ }
+ return cfg;
+ }
+
+ private SchemaUpdate getSchemaUpdate(Configuration cfg) throws HibernateException, IOException {
+ Properties properties = new Properties();
+ properties.putAll( cfg.getProperties() );
+ if (propertiesFile == null) {
+ properties.putAll( getProject().getProperties() );
+ }
+ else {
+ properties.load( new FileInputStream(propertiesFile) );
+ }
+ cfg.setProperties(properties);
+ return new SchemaUpdate(cfg);
+ }
+
+ public void setNamingStrategy(String namingStrategy) {
+ this.namingStrategy = namingStrategy;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/SchemaValidator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/SchemaValidator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/SchemaValidator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,134 @@
+//$Id: SchemaValidator.java 9249 2006-02-10 03:48:37Z steveebersole $
+package org.hibernate.tool.hbm2ddl;
+
+import java.io.FileInputStream;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.HibernateException;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.NamingStrategy;
+import org.hibernate.cfg.Settings;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.util.ReflectHelper;
+
+/**
+ * A commandline tool to update a database schema. May also be called from
+ * inside an application.
+ *
+ * @author Christoph Sturm
+ */
+public class SchemaValidator {
+
+ private static final Log log = LogFactory.getLog( SchemaValidator.class );
+ private ConnectionHelper connectionHelper;
+ private Configuration configuration;
+ private Dialect dialect;
+
+ public SchemaValidator(Configuration cfg) throws HibernateException {
+ this( cfg, cfg.getProperties() );
+ }
+
+ public SchemaValidator(Configuration cfg, Properties connectionProperties) throws HibernateException {
+ this.configuration = cfg;
+ dialect = Dialect.getDialect( connectionProperties );
+ Properties props = new Properties();
+ props.putAll( dialect.getDefaultProperties() );
+ props.putAll( connectionProperties );
+ connectionHelper = new ManagedProviderConnectionHelper( props );
+ }
+
+ public SchemaValidator(Configuration cfg, Settings settings) throws HibernateException {
+ this.configuration = cfg;
+ dialect = settings.getDialect();
+ connectionHelper = new SuppliedConnectionProviderConnectionHelper(
+ settings.getConnectionProvider()
+ );
+ }
+
+ public static void main(String[] args) {
+ try {
+ Configuration cfg = new Configuration();
+
+ String propFile = null;
+
+ for ( int i = 0; i < args.length; i++ ) {
+ if ( args[i].startsWith( "--" ) ) {
+ if ( args[i].startsWith( "--properties=" ) ) {
+ propFile = args[i].substring( 13 );
+ }
+ else if ( args[i].startsWith( "--config=" ) ) {
+ cfg.configure( args[i].substring( 9 ) );
+ }
+ else if ( args[i].startsWith( "--naming=" ) ) {
+ cfg.setNamingStrategy(
+ ( NamingStrategy ) ReflectHelper.classForName( args[i].substring( 9 ) ).newInstance()
+ );
+ }
+ }
+ else {
+ cfg.addFile( args[i] );
+ }
+
+ }
+
+ if ( propFile != null ) {
+ Properties props = new Properties();
+ props.putAll( cfg.getProperties() );
+ props.load( new FileInputStream( propFile ) );
+ cfg.setProperties( props );
+ }
+
+ new SchemaValidator( cfg ).validate();
+ }
+ catch ( Exception e ) {
+ log.error( "Error running schema update", e );
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Perform the validations.
+ */
+ public void validate() {
+
+ log.info( "Running schema validator" );
+
+ Connection connection = null;
+
+ try {
+
+ DatabaseMetadata meta;
+ try {
+ log.info( "fetching database metadata" );
+ connectionHelper.prepare( false );
+ connection = connectionHelper.getConnection();
+ meta = new DatabaseMetadata( connection, dialect, false );
+ }
+ catch ( SQLException sqle ) {
+ log.error( "could not get database metadata", sqle );
+ throw sqle;
+ }
+
+ configuration.validateSchema( dialect, meta );
+
+ }
+ catch ( SQLException e ) {
+ log.error( "could not complete schema validation", e );
+ }
+ finally {
+
+ try {
+ connectionHelper.release();
+ }
+ catch ( Exception e ) {
+ log.error( "Error closing connection", e );
+ }
+
+ }
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/SchemaValidator.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/SchemaValidatorTask.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/SchemaValidatorTask.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/SchemaValidatorTask.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,161 @@
+//$Id: SchemaValidatorTask.java 7863 2005-08-11 23:11:03Z oneovthafew $
+package org.hibernate.tool.hbm2ddl;
+
+import org.hibernate.HibernateException;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.NamingStrategy;
+import org.hibernate.util.ArrayHelper;
+import org.hibernate.util.ReflectHelper;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.tools.ant.types.FileSet;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * An Ant task for <tt>SchemaUpdate</tt>.
+ *
+ * <pre>
+ * <taskdef name="schemavalidator"
+ * classname="org.hibernate.tool.hbm2ddl.SchemaValidatorTask"
+ * classpathref="class.path"/>
+ *
+ * <schemaupdate
+ * properties="${build.classes.dir}/hibernate.properties"
+ * <fileset dir="${build.classes.dir}">
+ * <include name="*.hbm.xml"/>
+ * </fileset>
+ * </schemaupdate>
+ * </pre>
+ *
+ * @see SchemaValidator
+ * @author Gavin King
+ */
+public class SchemaValidatorTask extends MatchingTask {
+
+ private List fileSets = new LinkedList();
+ private File propertiesFile = null;
+ private File configurationFile = null;
+ private String namingStrategy = null;
+
+ public void addFileset(FileSet set) {
+ fileSets.add(set);
+ }
+
+ /**
+ * Set a properties file
+ * @param propertiesFile the properties file name
+ */
+ public void setProperties(File propertiesFile) {
+ if ( !propertiesFile.exists() ) {
+ throw new BuildException("Properties file: " + propertiesFile + " does not exist.");
+ }
+
+ log("Using properties file " + propertiesFile, Project.MSG_DEBUG);
+ this.propertiesFile = propertiesFile;
+ }
+
+ /**
+ * Set a <literal>.cfg.xml</literal> file
+ * @param configurationFile the file name
+ */
+ public void setConfig(File configurationFile) {
+ this.configurationFile = configurationFile;
+ }
+
+ /**
+ * Execute the task
+ */
+ public void execute() throws BuildException {
+ try {
+ Configuration cfg = getConfiguration();
+ getSchemaValidator(cfg).validate();
+ }
+ catch (HibernateException e) {
+ throw new BuildException("Schema text failed: " + e.getMessage(), e);
+ }
+ catch (FileNotFoundException e) {
+ throw new BuildException("File not found: " + e.getMessage(), e);
+ }
+ catch (IOException e) {
+ throw new BuildException("IOException : " + e.getMessage(), e);
+ }
+ catch (Exception e) {
+ throw new BuildException(e);
+ }
+ }
+
+ private String[] getFiles() {
+
+ List files = new LinkedList();
+ for ( Iterator i = fileSets.iterator(); i.hasNext(); ) {
+
+ FileSet fs = (FileSet) i.next();
+ DirectoryScanner ds = fs.getDirectoryScanner( getProject() );
+
+ String[] dsFiles = ds.getIncludedFiles();
+ for (int j = 0; j < dsFiles.length; j++) {
+ File f = new File(dsFiles[j]);
+ if ( !f.isFile() ) {
+ f = new File( ds.getBasedir(), dsFiles[j] );
+ }
+
+ files.add( f.getAbsolutePath() );
+ }
+ }
+
+ return ArrayHelper.toStringArray(files);
+ }
+
+ private Configuration getConfiguration() throws Exception {
+ Configuration cfg = new Configuration();
+ if (namingStrategy!=null) {
+ cfg.setNamingStrategy(
+ (NamingStrategy) ReflectHelper.classForName(namingStrategy).newInstance()
+ );
+ }
+ if (configurationFile!=null) {
+ cfg.configure( configurationFile );
+ }
+
+ String[] files = getFiles();
+ for (int i = 0; i < files.length; i++) {
+ String filename = files[i];
+ if ( filename.endsWith(".jar") ) {
+ cfg.addJar( new File(filename) );
+ }
+ else {
+ cfg.addFile(filename);
+ }
+ }
+ return cfg;
+ }
+
+ private SchemaValidator getSchemaValidator(Configuration cfg) throws HibernateException, IOException {
+ Properties properties = new Properties();
+ properties.putAll( cfg.getProperties() );
+ if (propertiesFile == null) {
+ properties.putAll( getProject().getProperties() );
+ }
+ else {
+ properties.load( new FileInputStream(propertiesFile) );
+ }
+ cfg.setProperties(properties);
+ return new SchemaValidator(cfg);
+ }
+
+ public void setNamingStrategy(String namingStrategy) {
+ this.namingStrategy = namingStrategy;
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/SchemaValidatorTask.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/SuppliedConnectionHelper.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/SuppliedConnectionHelper.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/SuppliedConnectionHelper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,46 @@
+package org.hibernate.tool.hbm2ddl;
+
+import org.hibernate.util.JDBCExceptionReporter;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+/**
+ * A {@link ConnectionHelper} implementation based on an explicitly supplied
+ * connection.
+ *
+ * @author Steve Ebersole
+ */
+class SuppliedConnectionHelper implements ConnectionHelper {
+ private Connection connection;
+ private boolean toggleAutoCommit;
+
+ public SuppliedConnectionHelper(Connection connection) {
+ this.connection = connection;
+ }
+
+ public void prepare(boolean needsAutoCommit) throws SQLException {
+ toggleAutoCommit = needsAutoCommit && !connection.getAutoCommit();
+ if ( toggleAutoCommit ) {
+ try {
+ connection.commit();
+ }
+ catch( Throwable ignore ) {
+ // might happen with a managed connection
+ }
+ connection.setAutoCommit( true );
+ }
+ }
+
+ public Connection getConnection() {
+ return connection;
+ }
+
+ public void release() throws SQLException {
+ JDBCExceptionReporter.logAndClearWarnings( connection );
+ if ( toggleAutoCommit ) {
+ connection.setAutoCommit( false );
+ }
+ connection = null;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/SuppliedConnectionProviderConnectionHelper.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/SuppliedConnectionProviderConnectionHelper.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/SuppliedConnectionProviderConnectionHelper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,55 @@
+package org.hibernate.tool.hbm2ddl;
+
+import org.hibernate.connection.ConnectionProvider;
+import org.hibernate.util.JDBCExceptionReporter;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+/**
+ * A {@link ConnectionHelper} implementation based on a provided
+ * {@link ConnectionProvider}. Essentially, ensures that the connection
+ * gets cleaned up, but that the provider itself remains usable since it
+ * was externally provided to us.
+ *
+ * @author Steve Ebersole
+ */
+class SuppliedConnectionProviderConnectionHelper implements ConnectionHelper {
+ private ConnectionProvider provider;
+ private Connection connection;
+ private boolean toggleAutoCommit;
+
+ public SuppliedConnectionProviderConnectionHelper(ConnectionProvider provider) {
+ this.provider = provider;
+ }
+
+ public void prepare(boolean needsAutoCommit) throws SQLException {
+ connection = provider.getConnection();
+ toggleAutoCommit = needsAutoCommit && !connection.getAutoCommit();
+ if ( toggleAutoCommit ) {
+ try {
+ connection.commit();
+ }
+ catch( Throwable ignore ) {
+ // might happen with a managed connection
+ }
+ connection.setAutoCommit( true );
+ }
+ }
+
+ public Connection getConnection() throws SQLException {
+ return connection;
+ }
+
+ public void release() throws SQLException {
+ // we only release the connection
+ if ( connection != null ) {
+ JDBCExceptionReporter.logAndClearWarnings( connection );
+ if ( toggleAutoCommit ) {
+ connection.setAutoCommit( false );
+ }
+ provider.closeConnection( connection );
+ connection = null;
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/TableMetadata.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/TableMetadata.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/TableMetadata.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,160 @@
+//$Id: TableMetadata.java 10726 2006-11-06 14:50:05Z max.andersen at jboss.com $
+package org.hibernate.tool.hbm2ddl;
+
+import java.sql.DatabaseMetaData;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * JDBC table metadata
+ * @author Christoph Sturm, Max Rydahl Andersen
+ */
+public class TableMetadata {
+
+ private static final Log log = LogFactory.getLog(TableMetadata.class);
+
+ private final String catalog;
+ private final String schema;
+ private final String name;
+ private final Map columns = new HashMap();
+ private final Map foreignKeys = new HashMap();
+ private final Map indexes = new HashMap();
+
+ TableMetadata(ResultSet rs, DatabaseMetaData meta, boolean extras) throws SQLException {
+ catalog = rs.getString("TABLE_CAT");
+ schema = rs.getString("TABLE_SCHEM");
+ name = rs.getString("TABLE_NAME");
+ initColumns(meta);
+ if (extras) {
+ initForeignKeys(meta);
+ initIndexes(meta);
+ }
+ String cat = catalog==null ? "" : catalog + '.';
+ String schem = schema==null ? "" : schema + '.';
+ log.info( "table found: " + cat + schem + name );
+ log.info( "columns: " + columns.keySet() );
+ if (extras) {
+ log.info( "foreign keys: " + foreignKeys.keySet() );
+ log.info( "indexes: " + indexes.keySet() );
+ }
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getCatalog() {
+ return catalog;
+ }
+
+ public String getSchema() {
+ return schema;
+ }
+
+ public String toString() {
+ return "TableMetadata(" + name + ')';
+ }
+
+ public ColumnMetadata getColumnMetadata(String columnName) {
+ return (ColumnMetadata) columns.get( columnName.toLowerCase() );
+ }
+
+ public ForeignKeyMetadata getForeignKeyMetadata(String keyName) {
+ return (ForeignKeyMetadata) foreignKeys.get( keyName.toLowerCase() );
+ }
+
+ public IndexMetadata getIndexMetadata(String indexName) {
+ return (IndexMetadata) indexes.get( indexName.toLowerCase() );
+ }
+
+ private void addForeignKey(ResultSet rs) throws SQLException {
+ String fk = rs.getString("FK_NAME");
+
+ if (fk == null) return;
+
+ ForeignKeyMetadata info = getForeignKeyMetadata(fk);
+ if (info == null) {
+ info = new ForeignKeyMetadata(rs);
+ foreignKeys.put( info.getName().toLowerCase(), info );
+ }
+
+ info.addColumn( getColumnMetadata( rs.getString("FKCOLUMN_NAME") ) );
+ }
+
+ private void addIndex(ResultSet rs) throws SQLException {
+ String index = rs.getString("INDEX_NAME");
+
+ if (index == null) return;
+
+ IndexMetadata info = getIndexMetadata(index);
+ if (info == null) {
+ info = new IndexMetadata(rs);
+ indexes.put( info.getName().toLowerCase(), info );
+ }
+
+ info.addColumn( getColumnMetadata( rs.getString("COLUMN_NAME") ) );
+ }
+
+ public void addColumn(ResultSet rs) throws SQLException {
+ String column = rs.getString("COLUMN_NAME");
+
+ if (column==null) return;
+
+ if ( getColumnMetadata(column) == null ) {
+ ColumnMetadata info = new ColumnMetadata(rs);
+ columns.put( info.getName().toLowerCase(), info );
+ }
+ }
+
+ private void initForeignKeys(DatabaseMetaData meta) throws SQLException {
+ ResultSet rs = null;
+
+ try {
+ rs = meta.getImportedKeys(catalog, schema, name);
+ while ( rs.next() ) addForeignKey(rs);
+ }
+ finally {
+ if (rs != null) rs.close();
+ }
+ }
+
+ private void initIndexes(DatabaseMetaData meta) throws SQLException {
+ ResultSet rs = null;
+
+ try {
+ rs = meta.getIndexInfo(catalog, schema, name, false, true);
+
+ while ( rs.next() ) {
+ if ( rs.getShort("TYPE") == DatabaseMetaData.tableIndexStatistic ) continue;
+ addIndex(rs);
+ }
+ }
+ finally {
+ if (rs != null) rs.close();
+ }
+ }
+
+ private void initColumns(DatabaseMetaData meta) throws SQLException {
+ ResultSet rs = null;
+
+ try {
+ rs = meta.getColumns(catalog, schema, name, "%");
+ while ( rs.next() ) addColumn(rs);
+ }
+ finally {
+ if (rs != null) rs.close();
+ }
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,8 @@
+<html>
+<head></head>
+<body>
+<p>
+ The <tt>hbm2ddl</tt> tool.
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/tool/hbm2ddl/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tool/instrument/BasicInstrumentationTask.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tool/instrument/BasicInstrumentationTask.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tool/instrument/BasicInstrumentationTask.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,383 @@
+package org.hibernate.tool.instrument;
+
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.types.FileSet;
+import org.hibernate.bytecode.util.ClassDescriptor;
+import org.hibernate.bytecode.util.ByteCodeHelper;
+import org.hibernate.bytecode.util.FieldFilter;
+import org.hibernate.bytecode.ClassTransformer;
+import org.hibernate.util.StringHelper;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.CRC32;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+import java.io.IOException;
+import java.io.FileInputStream;
+import java.io.DataInputStream;
+import java.io.ByteArrayInputStream;
+
+/**
+ * Super class for all Hibernate instrumentation tasks. Provides the basic
+ * templating of how instrumentation should occur.
+ *
+ * @author Steve Ebersole
+ */
+public abstract class BasicInstrumentationTask extends Task {
+
+ private static final int ZIP_MAGIC = 0x504B0304;
+ private static final int CLASS_MAGIC = 0xCAFEBABE;
+
+ protected final Logger logger = new Logger();
+ private List filesets = new ArrayList();
+ private Set classNames = new HashSet();
+ private boolean extended;
+ private boolean verbose;
+
+ public void addFileset(FileSet set) {
+ this.filesets.add( set );
+ }
+
+ protected final Iterator filesets() {
+ return filesets.iterator();
+ }
+
+ public boolean isExtended() {
+ return extended;
+ }
+
+ public void setExtended(boolean extended) {
+ this.extended = extended;
+ }
+
+ public boolean isVerbose() {
+ return verbose;
+ }
+
+ public void setVerbose(boolean verbose) {
+ this.verbose = verbose;
+ }
+
+ public void execute() throws BuildException {
+ if ( isExtended() ) {
+ collectClassNames();
+ }
+ logger.info( "starting instrumentation" );
+ Project project = getProject();
+ Iterator filesets = filesets();
+ while ( filesets.hasNext() ) {
+ FileSet fs = ( FileSet ) filesets.next();
+ DirectoryScanner ds = fs.getDirectoryScanner( project );
+ String[] includedFiles = ds.getIncludedFiles();
+ File d = fs.getDir( project );
+ for ( int i = 0; i < includedFiles.length; ++i ) {
+ File file = new File( d, includedFiles[i] );
+ try {
+ processFile( file );
+ }
+ catch ( Exception e ) {
+ throw new BuildException( e );
+ }
+ }
+ }
+ }
+
+ private void collectClassNames() {
+ logger.info( "collecting class names for extended instrumentation determination" );
+ Project project = getProject();
+ Iterator filesets = filesets();
+ while ( filesets.hasNext() ) {
+ FileSet fs = ( FileSet ) filesets.next();
+ DirectoryScanner ds = fs.getDirectoryScanner( project );
+ String[] includedFiles = ds.getIncludedFiles();
+ File d = fs.getDir( project );
+ for ( int i = 0; i < includedFiles.length; ++i ) {
+ File file = new File( d, includedFiles[i] );
+ try {
+ collectClassNames( file );
+ }
+ catch ( Exception e ) {
+ throw new BuildException( e );
+ }
+ }
+ }
+ logger.info( classNames.size() + " class(es) being checked" );
+ }
+
+ private void collectClassNames(File file) throws Exception {
+ if ( isClassFile( file ) ) {
+ byte[] bytes = ByteCodeHelper.readByteCode( file );
+ ClassDescriptor descriptor = getClassDescriptor( bytes );
+ classNames.add( descriptor.getName() );
+ }
+ else if ( isJarFile( file ) ) {
+ ZipEntryHandler collector = new ZipEntryHandler() {
+ public void handleEntry(ZipEntry entry, byte[] byteCode) throws Exception {
+ if ( !entry.isDirectory() ) {
+ // see if the entry represents a class file
+ DataInputStream din = new DataInputStream( new ByteArrayInputStream( byteCode ) );
+ if ( din.readInt() == CLASS_MAGIC ) {
+ classNames.add( getClassDescriptor( byteCode ).getName() );
+ }
+ }
+ }
+ };
+ ZipFileProcessor processor = new ZipFileProcessor( collector );
+ processor.process( file );
+ }
+ }
+
+ protected void processFile(File file) throws Exception {
+ logger.verbose( "processing file : " + file.toURL() );
+ if ( isClassFile( file ) ) {
+ processClassFile(file);
+ }
+ else if ( isJarFile( file ) ) {
+ processJarFile(file);
+ }
+ else {
+ logger.verbose( "ignoring " + file.toURL() );
+
+ }
+ }
+
+ protected final boolean isClassFile(File file) throws IOException {
+ return checkMagic( file, CLASS_MAGIC );
+ }
+
+ protected final boolean isJarFile(File file) throws IOException {
+ return checkMagic(file, ZIP_MAGIC);
+ }
+
+ protected final boolean checkMagic(File file, long magic) throws IOException {
+ DataInputStream in = new DataInputStream( new FileInputStream( file ) );
+ try {
+ int m = in.readInt();
+ return magic == m;
+ }
+ finally {
+ in.close();
+ }
+ }
+
+ protected void processClassFile(File file) throws Exception {
+ logger.verbose( "Starting class file : " + file.toURL() );
+ byte[] bytes = ByteCodeHelper.readByteCode( file );
+ ClassDescriptor descriptor = getClassDescriptor( bytes );
+ ClassTransformer transformer = getClassTransformer( descriptor );
+ if ( transformer == null ) {
+ logger.verbose( "skipping file : " + file.toURL() );
+ return;
+ }
+
+ logger.info( "processing class [" + descriptor.getName() + "]; file = " + file.toURL() );
+ byte[] transformedBytes = transformer.transform(
+ getClass().getClassLoader(),
+ descriptor.getName(),
+ null,
+ null,
+ descriptor.getBytes()
+ );
+
+ OutputStream out = new FileOutputStream( file );
+ try {
+ out.write( transformedBytes );
+ out.flush();
+ }
+ finally {
+ try {
+ out.close();
+ }
+ catch ( IOException ignore) {
+ // intentionally empty
+ }
+ }
+ }
+
+ protected void processJarFile(final File file) throws Exception {
+ logger.verbose( "starting jar file : " + file.toURL() );
+
+ File tempFile = File.createTempFile(
+ file.getName(),
+ null,
+ new File( file.getAbsoluteFile().getParent() )
+ );
+
+ try {
+ FileOutputStream fout = new FileOutputStream( tempFile, false );
+ try {
+ final ZipOutputStream out = new ZipOutputStream( fout );
+ ZipEntryHandler transformer = new ZipEntryHandler() {
+ public void handleEntry(ZipEntry entry, byte[] byteCode) throws Exception {
+ logger.verbose( "starting entry : " + entry.toString() );
+ if ( !entry.isDirectory() ) {
+ // see if the entry represents a class file
+ DataInputStream din = new DataInputStream( new ByteArrayInputStream( byteCode ) );
+ if ( din.readInt() == CLASS_MAGIC ) {
+ ClassDescriptor descriptor = getClassDescriptor( byteCode );
+ ClassTransformer transformer = getClassTransformer( descriptor );
+ if ( transformer == null ) {
+ logger.verbose( "skipping entry : " + entry.toString() );
+ }
+ else {
+ logger.info( "processing class [" + descriptor.getName() + "]; entry = " + file.toURL() );
+ byteCode = transformer.transform(
+ getClass().getClassLoader(),
+ descriptor.getName(),
+ null,
+ null,
+ descriptor.getBytes()
+ );
+ }
+ }
+ else {
+ logger.verbose( "ignoring zip entry : " + entry.toString() );
+ }
+ }
+
+ ZipEntry outEntry = new ZipEntry( entry.getName() );
+ outEntry.setMethod( entry.getMethod() );
+ outEntry.setComment( entry.getComment() );
+ outEntry.setSize( byteCode.length );
+
+ if ( outEntry.getMethod() == ZipEntry.STORED ){
+ CRC32 crc = new CRC32();
+ crc.update( byteCode );
+ outEntry.setCrc( crc.getValue() );
+ outEntry.setCompressedSize( byteCode.length );
+ }
+ out.putNextEntry( outEntry );
+ out.write( byteCode );
+ out.closeEntry();
+ }
+ };
+ ZipFileProcessor processor = new ZipFileProcessor( transformer );
+ processor.process( file );
+ out.close();
+ }
+ finally{
+ fout.close();
+ }
+
+ if ( file.delete() ) {
+ File newFile = new File( tempFile.getAbsolutePath() );
+ if( !newFile.renameTo( file ) ) {
+ throw new IOException( "can not rename " + tempFile + " to " + file );
+ }
+ }
+ else {
+ throw new IOException("can not delete " + file);
+ }
+ }
+ finally {
+ tempFile.delete();
+ }
+ }
+
+ protected boolean isBeingIntrumented(String className) {
+ logger.verbose( "checking to see if class [" + className + "] is set to be instrumented" );
+ return classNames.contains( className );
+ }
+
+ protected abstract ClassDescriptor getClassDescriptor(byte[] byecode) throws Exception;
+
+ protected abstract ClassTransformer getClassTransformer(ClassDescriptor descriptor);
+
+ protected class CustomFieldFilter implements FieldFilter {
+ private final ClassDescriptor descriptor;
+
+ public CustomFieldFilter(ClassDescriptor descriptor) {
+ this.descriptor = descriptor;
+ }
+
+ public boolean shouldInstrumentField(String className, String fieldName) {
+ if ( descriptor.getName().equals( className ) ) {
+ logger.verbose( "accepting transformation of field [" + className + "." + fieldName + "]" );
+ return true;
+ }
+ else {
+ logger.verbose( "rejecting transformation of field [" + className + "." + fieldName + "]" );
+ return false;
+ }
+ }
+
+ public boolean shouldTransformFieldAccess(
+ String transformingClassName,
+ String fieldOwnerClassName,
+ String fieldName) {
+ if ( descriptor.getName().equals( fieldOwnerClassName ) ) {
+ logger.verbose( "accepting transformation of field access [" + fieldOwnerClassName + "." + fieldName + "]" );
+ return true;
+ }
+ else if ( isExtended() && isBeingIntrumented( fieldOwnerClassName ) ) {
+ logger.verbose( "accepting extended transformation of field access [" + fieldOwnerClassName + "." + fieldName + "]" );
+ return true;
+ }
+ else {
+ logger.verbose( "rejecting transformation of field access [" + fieldOwnerClassName + "." + fieldName + "]; caller = " + transformingClassName );
+ return false;
+ }
+ }
+ }
+
+ protected class Logger {
+ public void verbose(String message) {
+ if ( verbose ) {
+ System.out.println( message );
+ }
+ log( message, Project.MSG_VERBOSE );
+ }
+
+ public void debug(String message) {
+ log( message, Project.MSG_DEBUG );
+ }
+
+ public void info(String message) {
+ log( message, Project.MSG_INFO );
+ }
+
+ public void warn(String message) {
+ log( message, Project.MSG_WARN );
+ }
+ }
+
+
+ private static interface ZipEntryHandler {
+ public void handleEntry(ZipEntry entry, byte[] byteCode) throws Exception;
+ }
+
+ private static class ZipFileProcessor {
+ private final ZipEntryHandler entryHandler;
+
+ public ZipFileProcessor(ZipEntryHandler entryHandler) {
+ this.entryHandler = entryHandler;
+ }
+
+ public void process(File file) throws Exception {
+ ZipInputStream zip = new ZipInputStream( new FileInputStream( file ) );
+
+ try {
+ ZipEntry entry;
+ while ( (entry = zip.getNextEntry()) != null ) {
+ byte bytes[] = ByteCodeHelper.readByteCode( zip );
+ entryHandler.handleEntry( entry, bytes );
+ zip.closeEntry();
+ }
+ }
+ finally {
+ zip.close();
+ }
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tool/instrument/cglib/InstrumentTask.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tool/instrument/cglib/InstrumentTask.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tool/instrument/cglib/InstrumentTask.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,106 @@
+//$Id: InstrumentTask.java 10206 2006-08-03 19:59:42Z steve.ebersole at jboss.com $
+package org.hibernate.tool.instrument.cglib;
+
+import org.hibernate.bytecode.util.BasicClassFilter;
+import org.hibernate.bytecode.util.ClassDescriptor;
+import org.hibernate.bytecode.cglib.BytecodeProviderImpl;
+import org.hibernate.bytecode.ClassTransformer;
+import org.hibernate.tool.instrument.BasicInstrumentationTask;
+import org.objectweb.asm.ClassReader;
+
+import java.io.ByteArrayInputStream;
+
+import net.sf.cglib.core.ClassNameReader;
+import net.sf.cglib.transform.impl.InterceptFieldEnabled;
+
+/**
+ * An Ant task for instrumenting persistent classes in order to enable
+ * field-level interception using CGLIB.
+ * <p/>
+ * In order to use this task, typically you would define a a taskdef
+ * similiar to:<pre>
+ * <taskdef name="instrument" classname="org.hibernate.tool.instrument.cglib.InstrumentTask">
+ * <classpath refid="lib.class.path"/>
+ * </taskdef>
+ * </pre>
+ * where <tt>lib.class.path</tt> is an ANT path reference containing all the
+ * required Hibernate and CGLIB libraries.
+ * <p/>
+ * And then use it like:<pre>
+ * <instrument verbose="true">
+ * <fileset dir="${testclasses.dir}/org/hibernate/test">
+ * <include name="yadda/yadda/**"/>
+ * ...
+ * </fileset>
+ * </instrument>
+ * </pre>
+ * where the nested ANT fileset includes the class you would like to have
+ * instrumented.
+ * <p/>
+ * Optionally you can chose to enable "Extended Instrumentation" if desired
+ * by specifying the extended attriubute on the task:<pre>
+ * <instrument verbose="true" extended="true">
+ * ...
+ * </instrument>
+ * </pre>
+ * See the Hibernate manual regarding this option.
+ *
+ * @author Gavin King
+ * @author Steve Ebersole
+ */
+public class InstrumentTask extends BasicInstrumentationTask {
+
+ private static final BasicClassFilter CLASS_FILTER = new BasicClassFilter();
+
+ private final BytecodeProviderImpl provider = new BytecodeProviderImpl();
+
+
+ protected ClassDescriptor getClassDescriptor(byte[] byecode) throws Exception {
+ return new CustomClassDescriptor( byecode );
+ }
+
+ protected ClassTransformer getClassTransformer(ClassDescriptor descriptor) {
+ if ( descriptor.isInstrumented() ) {
+ logger.verbose( "class [" + descriptor.getName() + "] already instrumented" );
+ return null;
+ }
+ else {
+ return provider.getTransformer( CLASS_FILTER, new CustomFieldFilter( descriptor ) );
+ }
+ }
+
+ private static class CustomClassDescriptor implements ClassDescriptor {
+ private final byte[] bytecode;
+ private final String name;
+ private final boolean isInstrumented;
+
+ public CustomClassDescriptor(byte[] bytecode) throws Exception {
+ this.bytecode = bytecode;
+ ClassReader reader = new ClassReader( new ByteArrayInputStream( bytecode ) );
+ String[] names = ClassNameReader.getClassInfo( reader );
+ this.name = names[0];
+ boolean instrumented = false;
+ for ( int i = 1; i < names.length; i++ ) {
+ if ( InterceptFieldEnabled.class.getName().equals( names[i] ) ) {
+ instrumented = true;
+ break;
+ }
+ }
+ this.isInstrumented = instrumented;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public boolean isInstrumented() {
+ return isInstrumented;
+ }
+
+ public byte[] getBytes() {
+ return bytecode;
+ }
+ }
+
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tool/instrument/javassist/InstrumentTask.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tool/instrument/javassist/InstrumentTask.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tool/instrument/javassist/InstrumentTask.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,100 @@
+package org.hibernate.tool.instrument.javassist;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.ByteArrayInputStream;
+
+import javassist.bytecode.ClassFile;
+
+import org.hibernate.tool.instrument.BasicInstrumentationTask;
+import org.hibernate.bytecode.util.ClassDescriptor;
+import org.hibernate.bytecode.util.BasicClassFilter;
+import org.hibernate.bytecode.util.FieldFilter;
+import org.hibernate.bytecode.ClassTransformer;
+import org.hibernate.bytecode.javassist.BytecodeProviderImpl;
+import org.hibernate.bytecode.javassist.FieldHandled;
+
+/**
+ * An Ant task for instrumenting persistent classes in order to enable
+ * field-level interception using Javassist.
+ * <p/>
+ * In order to use this task, typically you would define a a taskdef
+ * similiar to:<pre>
+ * <taskdef name="instrument" classname="org.hibernate.tool.instrument.javassist.InstrumentTask">
+ * <classpath refid="lib.class.path"/>
+ * </taskdef>
+ * </pre>
+ * where <tt>lib.class.path</tt> is an ANT path reference containing all the
+ * required Hibernate and Javassist libraries.
+ * <p/>
+ * And then use it like:<pre>
+ * <instrument verbose="true">
+ * <fileset dir="${testclasses.dir}/org/hibernate/test">
+ * <include name="yadda/yadda/**"/>
+ * ...
+ * </fileset>
+ * </instrument>
+ * </pre>
+ * where the nested ANT fileset includes the class you would like to have
+ * instrumented.
+ * <p/>
+ * Optionally you can chose to enable "Extended Instrumentation" if desired
+ * by specifying the extended attriubute on the task:<pre>
+ * <instrument verbose="true" extended="true">
+ * ...
+ * </instrument>
+ * </pre>
+ * See the Hibernate manual regarding this option.
+ *
+ * @author Muga Nishizawa
+ * @author Steve Ebersole
+ */
+public class InstrumentTask extends BasicInstrumentationTask {
+
+ private static final BasicClassFilter CLASS_FILTER = new BasicClassFilter();
+
+ private final BytecodeProviderImpl provider = new BytecodeProviderImpl();
+
+ protected ClassDescriptor getClassDescriptor(byte[] bytecode) throws IOException {
+ return new CustomClassDescriptor( bytecode );
+ }
+
+ protected ClassTransformer getClassTransformer(ClassDescriptor descriptor) {
+ if ( descriptor.isInstrumented() ) {
+ logger.verbose( "class [" + descriptor.getName() + "] already instrumented" );
+ return null;
+ }
+ else {
+ return provider.getTransformer( CLASS_FILTER, new CustomFieldFilter( descriptor ) );
+ }
+ }
+
+ private static class CustomClassDescriptor implements ClassDescriptor {
+ private final byte[] bytes;
+ private final ClassFile classFile;
+
+ public CustomClassDescriptor(byte[] bytes) throws IOException {
+ this.bytes = bytes;
+ this.classFile = new ClassFile( new DataInputStream( new ByteArrayInputStream( bytes ) ) );
+ }
+
+ public String getName() {
+ return classFile.getName();
+ }
+
+ public boolean isInstrumented() {
+ String[] intfs = classFile.getInterfaces();
+ for ( int i = 0; i < intfs.length; i++ ) {
+ if ( FieldHandled.class.getName().equals( intfs[i] ) ) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public byte[] getBytes() {
+ return bytes;
+ }
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tool/instrument/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tool/instrument/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tool/instrument/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,11 @@
+<html>
+<head></head>
+<body>
+<p>
+ The <tt>instrument</tt> tool for adding field-interception hooks
+ to persistent classes using built-time bytecode processing. Use
+ this tool only if you wish to take advantage of lazy property
+ fetching (the <tt><lazy></tt> mapping element).
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/tool/instrument/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/BESTransactionManagerLookup.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/BESTransactionManagerLookup.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/BESTransactionManagerLookup.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,18 @@
+//$Id: BESTransactionManagerLookup.java 4388 2004-08-20 07:44:37Z oneovthafew $
+package org.hibernate.transaction;
+
+/**
+ * A <tt>TransactionManager</tt> lookup strategy for Borland ES.
+ * @author Etienne Hardy
+ */
+public final class BESTransactionManagerLookup extends JNDITransactionManagerLookup {
+
+ protected String getName() {
+ return "java:pm/TransactionManager";
+ }
+
+ public String getUserTransactionName() {
+ return "java:comp/UserTransaction";
+ }
+
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/CMTTransaction.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/CMTTransaction.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/CMTTransaction.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,167 @@
+//$Id: CMTTransaction.java 9680 2006-03-22 23:47:31Z epbernard $
+package org.hibernate.transaction;
+
+import javax.transaction.Status;
+import javax.transaction.Synchronization;
+import javax.transaction.SystemException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.HibernateException;
+import org.hibernate.Transaction;
+import org.hibernate.TransactionException;
+import org.hibernate.jdbc.JDBCContext;
+import org.hibernate.util.JTAHelper;
+
+/**
+ * Implements a basic transaction strategy for CMT transactions. All work is done
+ * in the context of the container managed transaction.
+ * @author Gavin King
+ */
+public class CMTTransaction implements Transaction {
+
+ private static final Log log = LogFactory.getLog(CMTTransaction.class);
+
+ protected final JDBCContext jdbcContext;
+ protected final TransactionFactory.Context transactionContext;
+
+ private boolean begun;
+
+ public CMTTransaction(JDBCContext jdbcContext, TransactionFactory.Context transactionContext) {
+ this.jdbcContext = jdbcContext;
+ this.transactionContext = transactionContext;
+ }
+
+ public void begin() throws HibernateException {
+ if (begun) {
+ return;
+ }
+
+ log.debug("begin");
+
+ boolean synchronization = jdbcContext.registerSynchronizationIfPossible();
+
+ if ( !synchronization ) {
+ throw new TransactionException("Could not register synchronization for container transaction");
+ }
+
+ begun = true;
+
+ jdbcContext.afterTransactionBegin(this);
+ }
+
+ public void commit() throws HibernateException {
+ if (!begun) {
+ throw new TransactionException("Transaction not successfully started");
+ }
+
+ log.debug("commit");
+
+ boolean flush = !transactionContext.isFlushModeNever() &&
+ !transactionContext.isFlushBeforeCompletionEnabled();
+
+ if (flush) {
+ transactionContext.managedFlush(); //if an exception occurs during flush, user must call rollback()
+ }
+
+ begun = false;
+
+ }
+
+ public void rollback() throws HibernateException {
+ if (!begun) {
+ throw new TransactionException("Transaction not successfully started");
+ }
+
+ log.debug("rollback");
+
+ try {
+ getTransaction().setRollbackOnly();
+ }
+ catch (SystemException se) {
+ log.error("Could not set transaction to rollback only", se);
+ throw new TransactionException("Could not set transaction to rollback only", se);
+ }
+
+ begun = false;
+
+ }
+
+ public javax.transaction.Transaction getTransaction() throws SystemException {
+ return transactionContext.getFactory().getTransactionManager().getTransaction();
+ }
+
+ public boolean isActive() throws TransactionException {
+
+ if (!begun) return false;
+
+ final int status;
+ try {
+ status = getTransaction().getStatus();
+ }
+ catch (SystemException se) {
+ log.error("Could not determine transaction status", se);
+ throw new TransactionException("Could not determine transaction status: ", se);
+ }
+ if (status==Status.STATUS_UNKNOWN) {
+ throw new TransactionException("Could not determine transaction status");
+ }
+ else {
+ return status==Status.STATUS_ACTIVE;
+ }
+ }
+
+ public boolean wasRolledBack() throws TransactionException {
+
+ if (!begun) return false;
+
+ final int status;
+ try {
+ status = getTransaction().getStatus();
+ }
+ catch (SystemException se) {
+ log.error("Could not determine transaction status", se);
+ throw new TransactionException("Could not determine transaction status", se);
+ }
+ if (status==Status.STATUS_UNKNOWN) {
+ throw new TransactionException("Could not determine transaction status");
+ }
+ else {
+ return JTAHelper.isRollback(status);
+ }
+ }
+
+ public boolean wasCommitted() throws TransactionException {
+
+ if ( !begun ) return false;
+
+ final int status;
+ try {
+ status = getTransaction().getStatus();
+ }
+ catch (SystemException se) {
+ log.error("Could not determine transaction status", se);
+ throw new TransactionException("Could not determine transaction status: ", se);
+ }
+ if (status==Status.STATUS_UNKNOWN) {
+ throw new TransactionException("Could not determine transaction status");
+ }
+ else {
+ return status==Status.STATUS_COMMITTED;
+ }
+ }
+
+ public void registerSynchronization(Synchronization sync) throws HibernateException {
+ try {
+ getTransaction().registerSynchronization(sync);
+ }
+ catch (Exception e) {
+ throw new TransactionException("Could not register synchronization", e);
+ }
+ }
+
+ public void setTimeout(int seconds) {
+ throw new UnsupportedOperationException("cannot set transaction timeout in CMT");
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/CMTTransaction.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/CMTTransactionFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/CMTTransactionFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/CMTTransactionFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,57 @@
+//$Id: CMTTransactionFactory.java 9595 2006-03-10 18:14:21Z steve.ebersole at jboss.com $
+package org.hibernate.transaction;
+
+import java.util.Properties;
+
+import org.hibernate.ConnectionReleaseMode;
+import org.hibernate.HibernateException;
+import org.hibernate.Transaction;
+import org.hibernate.TransactionException;
+import org.hibernate.util.JTAHelper;
+import org.hibernate.jdbc.JDBCContext;
+
+import javax.transaction.SystemException;
+
+/**
+ * Factory for <tt>CMTTransaction</tt>.
+ *
+ * @see CMTTransaction
+ * @author Gavin King
+ */
+public class CMTTransactionFactory implements TransactionFactory {
+
+ public ConnectionReleaseMode getDefaultReleaseMode() {
+ return ConnectionReleaseMode.AFTER_STATEMENT;
+ }
+
+ public void configure(Properties props) throws HibernateException {}
+
+ public Transaction createTransaction(JDBCContext jdbcContext, Context transactionContext)
+ throws HibernateException {
+ return new CMTTransaction(jdbcContext, transactionContext);
+ }
+
+ public boolean isTransactionManagerRequired() {
+ return true;
+ }
+
+ public boolean areCallbacksLocalToHibernateTransactions() {
+ return false;
+ }
+
+ public boolean isTransactionInProgress(
+ JDBCContext jdbcContext,
+ Context transactionContext,
+ Transaction transaction) {
+ try {
+ return JTAHelper.isTransactionInProgress(
+ transactionContext.getFactory().getTransactionManager().getTransaction()
+ );
+ }
+ catch( SystemException se ) {
+ throw new TransactionException( "Unable to check transaction status", se );
+ }
+
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/CMTTransactionFactory.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/CacheSynchronization.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/CacheSynchronization.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/CacheSynchronization.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,99 @@
+//$Id: CacheSynchronization.java 9239 2006-02-08 22:34:34Z steveebersole $
+package org.hibernate.transaction;
+
+import javax.transaction.Status;
+import javax.transaction.Synchronization;
+import javax.transaction.SystemException;
+import javax.transaction.Transaction;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.TransactionException;
+import org.hibernate.jdbc.JDBCContext;
+import org.hibernate.util.JTAHelper;
+
+/**
+ * @author Gavin King
+ */
+public final class CacheSynchronization implements Synchronization {
+
+ private static final Log log = LogFactory.getLog(CacheSynchronization.class);
+
+ private final TransactionFactory.Context ctx;
+ private JDBCContext jdbcContext;
+ private final Transaction transaction;
+ private final org.hibernate.Transaction hibernateTransaction;
+
+ public CacheSynchronization(
+ TransactionFactory.Context ctx,
+ JDBCContext jdbcContext,
+ Transaction transaction,
+ org.hibernate.Transaction tx
+ ) {
+ this.ctx = ctx;
+ this.jdbcContext = jdbcContext;
+ this.transaction = transaction;
+ this.hibernateTransaction = tx;
+ }
+
+ public void beforeCompletion() {
+ log.trace("transaction before completion callback");
+
+ boolean flush;
+ try {
+ flush = !ctx.isFlushModeNever() &&
+ ctx.isFlushBeforeCompletionEnabled() &&
+ !JTAHelper.isRollback( transaction.getStatus() );
+ //actually, this last test is probably unnecessary, since
+ //beforeCompletion() doesn't get called during rollback
+ }
+ catch (SystemException se) {
+ log.error("could not determine transaction status", se);
+ setRollbackOnly();
+ throw new TransactionException("could not determine transaction status in beforeCompletion()", se);
+ }
+
+ try {
+ if (flush) {
+ log.trace("automatically flushing session");
+ ctx.managedFlush();
+ }
+ }
+ catch (RuntimeException re) {
+ setRollbackOnly();
+ throw re;
+ }
+ finally {
+ jdbcContext.beforeTransactionCompletion(hibernateTransaction);
+ }
+ }
+
+ private void setRollbackOnly() {
+ try {
+ transaction.setRollbackOnly();
+ }
+ catch (SystemException se) {
+ log.error("could not set transaction to rollback only", se);
+ }
+ }
+
+ public void afterCompletion(int status) {
+ if ( log.isTraceEnabled() ) {
+ log.trace("transaction after completion callback, status: " + status);
+ }
+ try {
+ jdbcContext.afterTransactionCompletion(status==Status.STATUS_COMMITTED, hibernateTransaction);
+ }
+ finally {
+ if ( ctx.shouldAutoClose() && !ctx.isClosed() ) {
+ log.trace("automatically closing session");
+ ctx.managedClose();
+ }
+ }
+ }
+
+ public String toString() {
+ return CacheSynchronization.class.getName();
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/JBossTransactionManagerLookup.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/JBossTransactionManagerLookup.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/JBossTransactionManagerLookup.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,18 @@
+//$Id: JBossTransactionManagerLookup.java 8435 2005-10-18 16:23:33Z steveebersole $
+package org.hibernate.transaction;
+
+/**
+ * A <tt>TransactionManager</tt> lookup strategy for JBoss
+ * @author Gavin King
+ */
+public final class JBossTransactionManagerLookup extends JNDITransactionManagerLookup {
+
+ protected String getName() {
+ return "java:/TransactionManager";
+ }
+
+ public String getUserTransactionName() {
+ return "UserTransaction";
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/JDBCTransaction.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/JDBCTransaction.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/JDBCTransaction.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,255 @@
+//$Id: JDBCTransaction.java 9595 2006-03-10 18:14:21Z steve.ebersole at jboss.com $
+package org.hibernate.transaction;
+
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.transaction.Status;
+import javax.transaction.Synchronization;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.hibernate.HibernateException;
+import org.hibernate.Transaction;
+import org.hibernate.TransactionException;
+import org.hibernate.jdbc.JDBCContext;
+
+/**
+ * Implements a basic transaction strategy for JDBC connections.This is the
+ * default <tt>Transaction</tt> implementation used if none is explicitly
+ * specified.
+ * @author Anton van Straaten, Gavin King
+ */
+public class JDBCTransaction implements Transaction {
+
+ private static final Log log = LogFactory.getLog(JDBCTransaction.class);
+
+ private final JDBCContext jdbcContext;
+ private final TransactionFactory.Context transactionContext;
+
+ private boolean toggleAutoCommit;
+ private boolean begun;
+ private boolean rolledBack;
+ private boolean committed;
+ private boolean commitFailed;
+ private List synchronizations;
+ private boolean callback;
+ private int timeout = -1;
+
+ public JDBCTransaction(JDBCContext jdbcContext, TransactionFactory.Context transactionContext) {
+ this.jdbcContext = jdbcContext;
+ this.transactionContext = transactionContext;
+ }
+
+ public void begin() throws HibernateException {
+ if (begun) {
+ return;
+ }
+ if (commitFailed) {
+ throw new TransactionException("cannot re-start transaction after failed commit");
+ }
+
+ log.debug("begin");
+
+ try {
+ toggleAutoCommit = jdbcContext.connection().getAutoCommit();
+ if ( log.isDebugEnabled() ) {
+ log.debug("current autocommit status: " + toggleAutoCommit);
+ }
+ if (toggleAutoCommit) {
+ log.debug("disabling autocommit");
+ jdbcContext.connection().setAutoCommit(false);
+ }
+ }
+ catch (SQLException e) {
+ log.error("JDBC begin failed", e);
+ throw new TransactionException("JDBC begin failed: ", e);
+ }
+
+ callback = jdbcContext.registerCallbackIfNecessary();
+
+ begun = true;
+ committed = false;
+ rolledBack = false;
+
+ if ( timeout>0 ) {
+ jdbcContext.getConnectionManager()
+ .getBatcher()
+ .setTransactionTimeout(timeout);
+ }
+
+ jdbcContext.afterTransactionBegin(this);
+ }
+
+ private void closeIfRequired() throws HibernateException {
+ if ( callback && transactionContext.shouldAutoClose() && !transactionContext.isClosed() ) {
+ try {
+ transactionContext.managedClose();
+ }
+ catch (HibernateException he) {
+ log.error("Could not close session", he);
+ //swallow, the transaction was finished
+ }
+ }
+ }
+
+ public void commit() throws HibernateException {
+ if (!begun) {
+ throw new TransactionException("Transaction not successfully started");
+ }
+
+ log.debug("commit");
+
+ if ( !transactionContext.isFlushModeNever() && callback ) {
+ transactionContext.managedFlush(); //if an exception occurs during flush, user must call rollback()
+ }
+
+ notifyLocalSynchsBeforeTransactionCompletion();
+ if ( callback ) {
+ jdbcContext.beforeTransactionCompletion( this );
+ }
+
+ try {
+ commitAndResetAutoCommit();
+ log.debug("committed JDBC Connection");
+ committed = true;
+ if ( callback ) {
+ jdbcContext.afterTransactionCompletion( true, this );
+ }
+ notifyLocalSynchsAfterTransactionCompletion( Status.STATUS_COMMITTED );
+ }
+ catch (SQLException e) {
+ log.error("JDBC commit failed", e);
+ commitFailed = true;
+ if ( callback ) {
+ jdbcContext.afterTransactionCompletion( false, this );
+ }
+ notifyLocalSynchsAfterTransactionCompletion( Status.STATUS_UNKNOWN );
+ throw new TransactionException("JDBC commit failed", e);
+ }
+ finally {
+ closeIfRequired();
+ }
+ }
+
+ private void commitAndResetAutoCommit() throws SQLException {
+ try {
+ jdbcContext.connection().commit();
+ }
+ finally {
+ toggleAutoCommit();
+ }
+ }
+
+ public void rollback() throws HibernateException {
+
+ if (!begun && !commitFailed) {
+ throw new TransactionException("Transaction not successfully started");
+ }
+
+ log.debug("rollback");
+
+ if (!commitFailed) {
+
+ /*notifyLocalSynchsBeforeTransactionCompletion();
+ if ( callback ) {
+ jdbcContext.notifyLocalSynchsBeforeTransactionCompletion( this );
+ }*/
+
+ try {
+ rollbackAndResetAutoCommit();
+ log.debug("rolled back JDBC Connection");
+ rolledBack = true;
+ notifyLocalSynchsAfterTransactionCompletion(Status.STATUS_ROLLEDBACK);
+ }
+ catch (SQLException e) {
+ log.error("JDBC rollback failed", e);
+ notifyLocalSynchsAfterTransactionCompletion(Status.STATUS_UNKNOWN);
+ throw new TransactionException("JDBC rollback failed", e);
+ }
+ finally {
+ if ( callback ) {
+ jdbcContext.afterTransactionCompletion( false, this );
+ }
+ closeIfRequired();
+ }
+ }
+ }
+
+ private void rollbackAndResetAutoCommit() throws SQLException {
+ try {
+ jdbcContext.connection().rollback();
+ }
+ finally {
+ toggleAutoCommit();
+ }
+ }
+
+ private void toggleAutoCommit() {
+ try {
+ if (toggleAutoCommit) {
+ log.debug("re-enabling autocommit");
+ jdbcContext.connection().setAutoCommit( true );
+ }
+ }
+ catch (Exception sqle) {
+ log.error("Could not toggle autocommit", sqle);
+ //swallow it (the transaction _was_ successful or successfully rolled back)
+ }
+ }
+
+ public boolean wasRolledBack() {
+ return rolledBack;
+ }
+
+ public boolean wasCommitted() {
+ return committed;
+ }
+
+ public boolean isActive() {
+ return begun && ! ( rolledBack || committed | commitFailed );
+ }
+
+ public void registerSynchronization(Synchronization sync) throws HibernateException {
+ if (sync==null) throw new NullPointerException("null Synchronization");
+ if (synchronizations==null) {
+ synchronizations = new ArrayList();
+ }
+ synchronizations.add(sync);
+ }
+
+ private void notifyLocalSynchsBeforeTransactionCompletion() {
+ if (synchronizations!=null) {
+ for ( int i=0; i<synchronizations.size(); i++ ) {
+ Synchronization sync = (Synchronization) synchronizations.get(i);
+ try {
+ sync.beforeCompletion();
+ }
+ catch (Throwable t) {
+ log.error("exception calling user Synchronization", t);
+ }
+ }
+ }
+ }
+
+ private void notifyLocalSynchsAfterTransactionCompletion(int status) {
+ begun = false;
+ if (synchronizations!=null) {
+ for ( int i=0; i<synchronizations.size(); i++ ) {
+ Synchronization sync = (Synchronization) synchronizations.get(i);
+ try {
+ sync.afterCompletion(status);
+ }
+ catch (Throwable t) {
+ log.error("exception calling user Synchronization", t);
+ }
+ }
+ }
+ }
+
+ public void setTimeout(int seconds) {
+ timeout = seconds;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/JDBCTransactionFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/JDBCTransactionFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/JDBCTransactionFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,55 @@
+//$Id: JDBCTransactionFactory.java 10013 2006-06-11 17:00:19Z gavin.king at jboss.com $
+package org.hibernate.transaction;
+
+import java.util.Properties;
+
+import org.hibernate.ConnectionReleaseMode;
+import org.hibernate.HibernateException;
+import org.hibernate.Transaction;
+import org.hibernate.jdbc.JDBCContext;
+
+/**
+ * Factory for <tt>JDBCTransaction</tt>.
+ * @see JDBCTransaction
+ * @author Anton van Straaten
+ */
+public final class JDBCTransactionFactory implements TransactionFactory {
+
+ public ConnectionReleaseMode getDefaultReleaseMode() {
+ return ConnectionReleaseMode.AFTER_TRANSACTION;
+ }
+
+ public Transaction createTransaction(JDBCContext jdbcContext, Context transactionContext)
+ throws HibernateException {
+ return new JDBCTransaction( jdbcContext, transactionContext );
+ }
+
+ public void configure(Properties props) throws HibernateException {}
+
+ public boolean isTransactionManagerRequired() {
+ return false;
+ }
+
+ public boolean areCallbacksLocalToHibernateTransactions() {
+ return true;
+ }
+
+ public boolean isTransactionInProgress(
+ JDBCContext jdbcContext,
+ Context transactionContext,
+ Transaction transaction) {
+// try {
+// // for JDBC-based transactions, we only want to return true
+// // here if we (this transaction) are managing the transaction
+// return transaction != null &&
+// transaction.isActive() &&
+// !jdbcContext.getConnectionManager().isAutoCommit();
+// }
+// catch ( SQLException e ) {
+// // assume we are in auto-commit!
+// return false;
+// }
+ return transaction != null && transaction.isActive();
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/JNDITransactionManagerLookup.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/JNDITransactionManagerLookup.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/JNDITransactionManagerLookup.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,38 @@
+//$Id: JNDITransactionManagerLookup.java 3890 2004-06-03 16:31:32Z steveebersole $
+package org.hibernate.transaction;
+
+import java.util.Properties;
+
+import javax.naming.NamingException;
+import javax.transaction.TransactionManager;
+
+import org.hibernate.HibernateException;
+import org.hibernate.util.NamingHelper;
+
+/**
+ * Locates a <tt>TransactionManager</tt> in JNDI.
+ * @author Gavin King
+ */
+public abstract class JNDITransactionManagerLookup implements TransactionManagerLookup {
+
+ /**
+ * @see org.hibernate.transaction.TransactionManagerLookup#getTransactionManager(java.util.Properties)
+ */
+ public TransactionManager getTransactionManager(Properties props) throws HibernateException {
+ try {
+ return (TransactionManager) NamingHelper.getInitialContext(props).lookup( getName() );
+ }
+ catch (NamingException ne) {
+ throw new HibernateException( "Could not locate TransactionManager", ne );
+ }
+ }
+
+ protected abstract String getName();
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/JOTMTransactionManagerLookup.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/JOTMTransactionManagerLookup.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/JOTMTransactionManagerLookup.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,38 @@
+package org.hibernate.transaction;
+
+import java.util.Properties;
+
+import javax.transaction.TransactionManager;
+
+import org.hibernate.HibernateException;
+
+/**
+ * TransactionManager lookup strategy for JOTM
+ * @author Low Heng Sin
+ */
+public class JOTMTransactionManagerLookup implements TransactionManagerLookup {
+
+ /**
+ * @see org.hibernate.transaction.TransactionManagerLookup#getTransactionManager(Properties)
+ */
+ public TransactionManager getTransactionManager(Properties props) throws HibernateException {
+ try {
+ Class clazz = Class.forName("org.objectweb.jotm.Current");
+ return (TransactionManager) clazz.getMethod("getTransactionManager", null).invoke(null, null);
+ }
+ catch (Exception e) {
+ throw new HibernateException( "Could not obtain JOTM transaction manager instance", e );
+ }
+ }
+
+ public String getUserTransactionName() {
+ return "java:comp/UserTransaction";
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/JOnASTransactionManagerLookup.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/JOnASTransactionManagerLookup.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/JOnASTransactionManagerLookup.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,33 @@
+package org.hibernate.transaction;
+
+import java.util.Properties;
+
+import javax.transaction.TransactionManager;
+
+import org.hibernate.HibernateException;
+
+/**
+ * TransactionManager lookup strategy for JOnAS
+ * @author ?
+ */
+public class JOnASTransactionManagerLookup implements TransactionManagerLookup {
+
+ /**
+ * @see org.hibernate.transaction.TransactionManagerLookup#getTransactionManager(Properties)
+ */
+ public TransactionManager getTransactionManager(Properties props) throws HibernateException {
+ try {
+ Class clazz = Class.forName("org.objectweb.jonas_tm.Current");
+ return (TransactionManager) clazz.getMethod("getTransactionManager", null).invoke(null, null);
+ }
+ catch (Exception e) {
+ throw new HibernateException( "Could not obtain JOnAS transaction manager instance", e );
+ }
+ }
+
+ public String getUserTransactionName() {
+ return "java:comp/UserTransaction";
+ }
+
+}
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/JRun4TransactionManagerLookup.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/JRun4TransactionManagerLookup.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/JRun4TransactionManagerLookup.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,16 @@
+package org.hibernate.transaction;
+
+/**
+ * TransactionManager lookup strategy for JRun4
+ * @author Joseph Bissen
+ */
+public class JRun4TransactionManagerLookup extends JNDITransactionManagerLookup {
+
+ protected String getName() {
+ return "java:/TransactionManager";
+ }
+
+ public String getUserTransactionName() {
+ return "java:comp/UserTransaction";
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/JTATransaction.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/JTATransaction.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/JTATransaction.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,339 @@
+//$Id: JTATransaction.java 9601 2006-03-11 18:17:43Z epbernard $
+package org.hibernate.transaction;
+
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+import javax.transaction.Status;
+import javax.transaction.Synchronization;
+import javax.transaction.SystemException;
+import javax.transaction.TransactionManager;
+import javax.transaction.UserTransaction;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.AssertionFailure;
+import org.hibernate.HibernateException;
+import org.hibernate.Transaction;
+import org.hibernate.TransactionException;
+import org.hibernate.jdbc.JDBCContext;
+import org.hibernate.util.JTAHelper;
+
+/**
+ * Implements a basic transaction strategy for JTA transactions. Instances check to
+ * see if there is an existing JTA transaction. If none exists, a new transaction
+ * is started. If one exists, all work is done in the existing context. The
+ * following properties are used to locate the underlying <tt>UserTransaction</tt>:
+ * <br><br>
+ * <table>
+ * <tr><td><tt>hibernate.jndi.url</tt></td><td>JNDI initial context URL</td></tr>
+ * <tr><td><tt>hibernate.jndi.class</tt></td><td>JNDI provider class</td></tr>
+ * <tr><td><tt>jta.UserTransaction</tt></td><td>JNDI name</td></tr>
+ * </table>
+ * @author Gavin King
+ */
+public class JTATransaction implements Transaction {
+
+ private static final Log log = LogFactory.getLog(JTATransaction.class);
+
+ private final JDBCContext jdbcContext;
+ private final TransactionFactory.Context transactionContext;
+
+ private UserTransaction ut;
+ private boolean newTransaction;
+ private boolean begun;
+ private boolean commitFailed;
+ private boolean commitSucceeded;
+ private boolean callback;
+
+ public JTATransaction(
+ InitialContext context,
+ String utName,
+ JDBCContext jdbcContext,
+ TransactionFactory.Context transactionContext
+ ) {
+ this.jdbcContext = jdbcContext;
+ this.transactionContext = transactionContext;
+
+ log.debug("Looking for UserTransaction under: " + utName);
+
+ try {
+ ut = (UserTransaction) context.lookup(utName);
+ }
+ catch (NamingException ne) {
+ log.error("Could not find UserTransaction in JNDI", ne);
+ throw new TransactionException("Could not find UserTransaction in JNDI: ", ne);
+ }
+ if (ut==null) {
+ throw new AssertionFailure("A naming service lookup returned null");
+ }
+
+ log.debug("Obtained UserTransaction");
+ }
+
+ public void begin() throws HibernateException {
+ if (begun) {
+ return;
+ }
+ if (commitFailed) {
+ throw new TransactionException("cannot re-start transaction after failed commit");
+ }
+
+ log.debug("begin");
+
+ try {
+ newTransaction = ut.getStatus() == Status.STATUS_NO_TRANSACTION;
+ if (newTransaction) {
+ ut.begin();
+ log.debug("Began a new JTA transaction");
+ }
+ }
+ catch (Exception e) {
+ log.error("JTA transaction begin failed", e);
+ throw new TransactionException("JTA transaction begin failed", e);
+ }
+
+ /*if (newTransaction) {
+ // don't need a synchronization since we are committing
+ // or rolling back the transaction ourselves - assuming
+ // that we do no work in beforeTransactionCompletion()
+ synchronization = false;
+ }*/
+
+ boolean synchronization = jdbcContext.registerSynchronizationIfPossible();
+
+ if ( !newTransaction && !synchronization ) {
+ log.warn("You should set hibernate.transaction.manager_lookup_class if cache is enabled");
+ }
+
+ if (!synchronization) {
+ //if we could not register a synchronization,
+ //do the before/after completion callbacks
+ //ourself (but we need to let jdbcContext
+ //know that this is what we are going to
+ //do, so it doesn't keep trying to register
+ //synchronizations)
+ callback = jdbcContext.registerCallbackIfNecessary();
+ }
+
+ begun = true;
+ commitSucceeded = false;
+
+ jdbcContext.afterTransactionBegin(this);
+ }
+
+ public void commit() throws HibernateException {
+ if (!begun) {
+ throw new TransactionException("Transaction not successfully started");
+ }
+
+ log.debug("commit");
+
+ boolean flush = !transactionContext.isFlushModeNever()
+ && ( callback || !transactionContext.isFlushBeforeCompletionEnabled() );
+
+ if (flush) {
+ transactionContext.managedFlush(); //if an exception occurs during flush, user must call rollback()
+ }
+
+ if (callback && newTransaction) {
+ jdbcContext.beforeTransactionCompletion(this);
+ }
+
+ closeIfRequired();
+
+ if (newTransaction) {
+ try {
+ ut.commit();
+ commitSucceeded = true;
+ log.debug("Committed JTA UserTransaction");
+ }
+ catch (Exception e) {
+ commitFailed = true; // so the transaction is already rolled back, by JTA spec
+ log.error("JTA commit failed", e);
+ throw new TransactionException("JTA commit failed: ", e);
+ }
+ finally {
+ afterCommitRollback();
+ }
+ }
+ else {
+ // this one only really needed for badly-behaved applications!
+ // (if the TransactionManager has a Sychronization registered,
+ // its a noop)
+ // (actually we do need it for downgrading locks)
+ afterCommitRollback();
+ }
+
+ }
+
+ public void rollback() throws HibernateException {
+ if (!begun && !commitFailed) {
+ throw new TransactionException("Transaction not successfully started");
+ }
+
+ log.debug("rollback");
+
+ /*if (!synchronization && newTransaction && !commitFailed) {
+ jdbcContext.beforeTransactionCompletion(this);
+ }*/
+
+ try {
+ closeIfRequired();
+ }
+ catch (Exception e) {
+ log.error("could not close session during rollback", e);
+ //swallow it, and continue to roll back JTA transaction
+ }
+
+ try {
+ if (newTransaction) {
+ if (!commitFailed) {
+ ut.rollback();
+ log.debug("Rolled back JTA UserTransaction");
+ }
+ }
+ else {
+ ut.setRollbackOnly();
+ log.debug("set JTA UserTransaction to rollback only");
+ }
+ }
+ catch (Exception e) {
+ log.error("JTA rollback failed", e);
+ throw new TransactionException("JTA rollback failed", e);
+ }
+ finally {
+ afterCommitRollback();
+ }
+ }
+
+ private static final int NULL = Integer.MIN_VALUE;
+
+ private void afterCommitRollback() throws TransactionException {
+
+ begun = false;
+
+ if (callback) { // this method is a noop if there is a Synchronization!
+
+ if (!newTransaction) {
+ log.warn("You should set hibernate.transaction.manager_lookup_class if cache is enabled");
+ }
+ int status=NULL;
+ try {
+ status = ut.getStatus();
+ }
+ catch (Exception e) {
+ log.error("Could not determine transaction status after commit", e);
+ throw new TransactionException("Could not determine transaction status after commit", e);
+ }
+ finally {
+ /*if (status!=Status.STATUS_COMMITTED && status!=Status.STATUS_ROLLEDBACK) {
+ log.warn("Transaction not complete - you should set hibernate.transaction.manager_lookup_class if cache is enabled");
+ //throw exception??
+ }*/
+ jdbcContext.afterTransactionCompletion(status==Status.STATUS_COMMITTED, this);
+ }
+
+ }
+ }
+
+ public boolean wasRolledBack() throws TransactionException {
+
+ //if (!begun) return false;
+ //if (commitFailed) return true;
+
+ final int status;
+ try {
+ status = ut.getStatus();
+ }
+ catch (SystemException se) {
+ log.error("Could not determine transaction status", se);
+ throw new TransactionException("Could not determine transaction status", se);
+ }
+ if (status==Status.STATUS_UNKNOWN) {
+ throw new TransactionException("Could not determine transaction status");
+ }
+ else {
+ return JTAHelper.isRollback(status);
+ }
+ }
+
+ public boolean wasCommitted() throws TransactionException {
+
+ //if (!begun || commitFailed) return false;
+
+ final int status;
+ try {
+ status = ut.getStatus();
+ }
+ catch (SystemException se) {
+ log.error("Could not determine transaction status", se);
+ throw new TransactionException("Could not determine transaction status: ", se);
+ }
+ if (status==Status.STATUS_UNKNOWN) {
+ throw new TransactionException("Could not determine transaction status");
+ }
+ else {
+ return status==Status.STATUS_COMMITTED;
+ }
+ }
+
+ public boolean isActive() throws TransactionException {
+
+ if (!begun || commitFailed || commitSucceeded) return false;
+
+ final int status;
+ try {
+ status = ut.getStatus();
+ }
+ catch (SystemException se) {
+ log.error("Could not determine transaction status", se);
+ throw new TransactionException("Could not determine transaction status: ", se);
+ }
+ if (status==Status.STATUS_UNKNOWN) {
+ throw new TransactionException("Could not determine transaction status");
+ }
+ else {
+ return status==Status.STATUS_ACTIVE;
+ }
+ }
+
+ public void registerSynchronization(Synchronization sync) throws HibernateException {
+ if (getTransactionManager()==null) {
+ throw new IllegalStateException("JTA TransactionManager not available");
+ }
+ else {
+ try {
+ getTransactionManager().getTransaction().registerSynchronization(sync);
+ }
+ catch (Exception e) {
+ throw new TransactionException("could not register synchronization", e);
+ }
+ }
+ }
+
+ private TransactionManager getTransactionManager() {
+ return transactionContext.getFactory().getTransactionManager();
+ }
+
+ private void closeIfRequired() throws HibernateException {
+ boolean close = callback &&
+ transactionContext.shouldAutoClose() &&
+ !transactionContext.isClosed();
+ if ( close ) {
+ transactionContext.managedClose();
+ }
+ }
+
+ public void setTimeout(int seconds) {
+ try {
+ ut.setTransactionTimeout(seconds);
+ }
+ catch (SystemException se) {
+ throw new TransactionException("could not set transaction timeout", se);
+ }
+ }
+
+ protected UserTransaction getUserTransaction() {
+ return ut;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/JTATransactionFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/JTATransactionFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/JTATransactionFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,111 @@
+//$Id: JTATransactionFactory.java 10347 2006-08-24 15:37:11Z steve.ebersole at jboss.com $
+package org.hibernate.transaction;
+
+import java.util.Properties;
+
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+import javax.transaction.SystemException;
+import javax.transaction.UserTransaction;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.ConnectionReleaseMode;
+import org.hibernate.HibernateException;
+import org.hibernate.Transaction;
+import org.hibernate.TransactionException;
+import org.hibernate.jdbc.JDBCContext;
+import org.hibernate.cfg.Environment;
+import org.hibernate.util.NamingHelper;
+import org.hibernate.util.JTAHelper;
+
+/**
+ * Factory for <tt>JTATransaction</tt>.
+ *
+ * @see JTATransaction
+ * @author Gavin King
+ */
+public class JTATransactionFactory implements TransactionFactory {
+
+ private static final Log log = LogFactory.getLog(JTATransactionFactory.class);
+ private static final String DEFAULT_USER_TRANSACTION_NAME = "java:comp/UserTransaction";
+
+ protected InitialContext context;
+ protected String utName;
+
+ public void configure(Properties props) throws HibernateException {
+ try {
+ context = NamingHelper.getInitialContext(props);
+ }
+ catch (NamingException ne) {
+ log.error("Could not obtain initial context", ne);
+ throw new HibernateException( "Could not obtain initial context", ne );
+ }
+
+ utName = props.getProperty(Environment.USER_TRANSACTION);
+
+ if (utName==null) {
+ TransactionManagerLookup lookup = TransactionManagerLookupFactory.getTransactionManagerLookup(props);
+ if (lookup!=null) utName = lookup.getUserTransactionName();
+ }
+
+ if (utName==null) utName = DEFAULT_USER_TRANSACTION_NAME;
+ }
+
+ public Transaction createTransaction(JDBCContext jdbcContext, Context transactionContext)
+ throws HibernateException {
+ return new JTATransaction(context, utName, jdbcContext, transactionContext);
+ }
+
+ public ConnectionReleaseMode getDefaultReleaseMode() {
+ return ConnectionReleaseMode.AFTER_STATEMENT;
+ }
+
+ public boolean isTransactionManagerRequired() {
+ return false;
+ }
+
+ public boolean areCallbacksLocalToHibernateTransactions() {
+ return false;
+ }
+
+ public boolean isTransactionInProgress(
+ JDBCContext jdbcContext,
+ Context transactionContext,
+ Transaction transaction) {
+ try {
+ // Essentially:
+ // 1) If we have a local (Hibernate) transaction in progress
+ // and it already has the UserTransaction cached, use that
+ // UserTransaction to determine the status.
+ // 2) If a transaction manager has been located, use
+ // that transaction manager to determine the status.
+ // 3) Finally, as the last resort, try to lookup the
+ // UserTransaction via JNDI and use that to determine the
+ // status.
+ if ( transaction != null ) {
+ UserTransaction ut = ( ( JTATransaction ) transaction ).getUserTransaction();
+ if ( ut != null ) {
+ return JTAHelper.isInProgress( ut.getStatus() );
+ }
+ }
+
+ if ( jdbcContext.getFactory().getTransactionManager() != null ) {
+ return JTAHelper.isInProgress( jdbcContext.getFactory().getTransactionManager().getStatus() );
+ }
+ else {
+ try {
+ UserTransaction ut = ( UserTransaction ) context.lookup( utName );
+ return ut != null && JTAHelper.isInProgress( ut.getStatus() );
+ }
+ catch ( NamingException ne ) {
+ throw new TransactionException( "Unable to locate UserTransaction to check status", ne );
+ }
+ }
+ }
+ catch( SystemException se ) {
+ throw new TransactionException( "Unable to check transaction status", se );
+ }
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/OC4JTransactionManagerLookup.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/OC4JTransactionManagerLookup.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/OC4JTransactionManagerLookup.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,16 @@
+package org.hibernate.transaction;
+
+/**
+ * TransactionManagerLookup for the OC4J (Oracle) app-server.
+ *
+ * @author Stijn Janssens
+ */
+public class OC4JTransactionManagerLookup extends JNDITransactionManagerLookup {
+ protected String getName() {
+ return "java:comp/pm/TransactionManager";
+ }
+
+ public String getUserTransactionName() {
+ return "java:comp/UserTransaction";
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/OrionTransactionManagerLookup.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/OrionTransactionManagerLookup.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/OrionTransactionManagerLookup.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,28 @@
+//$Id: OrionTransactionManagerLookup.java 3890 2004-06-03 16:31:32Z steveebersole $
+package org.hibernate.transaction;
+
+/**
+ * TransactionManager lookup strategy for Orion
+ * @author Gavin King
+ */
+public class OrionTransactionManagerLookup
+extends JNDITransactionManagerLookup {
+
+ /**
+ * @see org.hibernate.transaction.JNDITransactionManagerLookup#getName()
+ */
+ protected String getName() {
+ return "java:comp/UserTransaction";
+ }
+
+ public String getUserTransactionName() {
+ return "java:comp/UserTransaction";
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/ResinTransactionManagerLookup.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/ResinTransactionManagerLookup.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/ResinTransactionManagerLookup.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,28 @@
+//$Id: ResinTransactionManagerLookup.java 3890 2004-06-03 16:31:32Z steveebersole $
+package org.hibernate.transaction;
+
+/**
+ * TransactionManager lookup strategy for Resin
+ * @author Aapo Laakkonen
+ */
+public class ResinTransactionManagerLookup
+extends JNDITransactionManagerLookup {
+
+ /**
+ * @see org.hibernate.transaction.JNDITransactionManagerLookup#getName()
+ */
+ protected String getName() {
+ return "java:comp/TransactionManager";
+ }
+
+ public String getUserTransactionName() {
+ return "java:comp/UserTransaction";
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/SunONETransactionManagerLookup.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/SunONETransactionManagerLookup.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/SunONETransactionManagerLookup.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,20 @@
+//$Id: SunONETransactionManagerLookup.java 11274 2007-03-12 00:18:38Z epbernard $
+package org.hibernate.transaction;
+
+/**
+ * TransactionManager lookup strategy for Sun ONE Application Server 7 and above
+ *
+ * @author Robert Davidson, Sanjeev Krishnan
+ * @author Emmanuel Bernard
+ */
+public class SunONETransactionManagerLookup extends JNDITransactionManagerLookup {
+
+ protected String getName() {
+ return "java:appserver/TransactionManager";
+ }
+
+ public String getUserTransactionName() {
+ return "java:comp/UserTransaction";
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/TransactionFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/TransactionFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/TransactionFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,87 @@
+//$Id: TransactionFactory.java 9595 2006-03-10 18:14:21Z steve.ebersole at jboss.com $
+package org.hibernate.transaction;
+
+import java.util.Properties;
+
+import org.hibernate.ConnectionReleaseMode;
+import org.hibernate.HibernateException;
+import org.hibernate.Transaction;
+import org.hibernate.jdbc.JDBCContext;
+import org.hibernate.engine.SessionFactoryImplementor;
+
+/**
+ * An abstract factory for <tt>Transaction</tt> instances. Concrete implementations
+ * are specified by <tt>hibernate.transaction.factory_class</tt>.<br>
+ * <br>
+ * Implementors must be threadsafe and should declare a public default constructor.
+ * @see Transaction
+ *
+ * @author Anton van Straaten, Gavin King
+ */
+public interface TransactionFactory {
+
+ public static interface Context {
+ public SessionFactoryImplementor getFactory();
+// public boolean isOpen();
+ public boolean isClosed();
+
+ public boolean isFlushModeNever();
+ public boolean isFlushBeforeCompletionEnabled();
+ public void managedFlush();
+
+ public boolean shouldAutoClose();
+ public void managedClose();
+ }
+
+ /**
+ * Begin a transaction and return the associated <tt>Transaction</tt> instance.
+ *
+ * @param jdbcContext The jdbc context to which the transaction belongs
+ * @param context The contract regarding the context in which this transaction will operate.
+ * @return Transaction
+ * @throws HibernateException
+ */
+ public Transaction createTransaction(JDBCContext jdbcContext, Context context) throws HibernateException;
+
+ /**
+ * Configure from the given properties.
+ * @param props
+ * @throws HibernateException
+ */
+ public void configure(Properties props) throws HibernateException;
+
+ /**
+ * Get the default connection release mode
+ */
+ public ConnectionReleaseMode getDefaultReleaseMode();
+
+ /**
+ * Do we require access to the JTA TransactionManager for
+ * this strategy?
+ */
+ public boolean isTransactionManagerRequired();
+
+ /**
+ * Are all transaction callbacks local to Hibernate Transactions?
+ * Or can the callbacks originate from some other source (e.g.
+ * a JTA Synchronization).
+ *
+ * @return true if callbacks only ever originate from
+ * the Hibernate {@link Transaction}; false otherwise.
+ */
+ public boolean areCallbacksLocalToHibernateTransactions();
+
+ /**
+ * Determine whether an underlying transaction is in progress.
+ * <p/>
+ * Mainly this is used in determining whether to register a
+ * synchronization as well as whether or not to circumvent
+ * auto flushing outside transactions.
+ *
+ * @param jdbcContext
+ * @param transactionContext
+ * @param transaction
+ * @return true if an underlying transaction is know to be in effect.
+ */
+ public boolean isTransactionInProgress(JDBCContext jdbcContext, Context transactionContext, Transaction transaction);
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/TransactionFactoryFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/TransactionFactoryFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/TransactionFactoryFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,56 @@
+package org.hibernate.transaction;
+
+import java.util.Properties;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.hibernate.HibernateException;
+import org.hibernate.cfg.Environment;
+import org.hibernate.util.ReflectHelper;
+
+/**
+ * @author Gavin King
+ */
+public final class TransactionFactoryFactory {
+
+ private static final Log log = LogFactory.getLog(TransactionFactoryFactory.class);
+
+ /**
+ * Obtain a TransactionFactory with the transaction handling strategy
+ * specified by the given Properties.
+ *
+ * @param transactionProps transaction properties
+ * @return TransactionFactory
+ * @throws HibernateException
+ */
+ public static TransactionFactory buildTransactionFactory(Properties transactionProps) throws HibernateException {
+
+ String strategyClassName = transactionProps.getProperty(Environment.TRANSACTION_STRATEGY);
+ if (strategyClassName==null) {
+ log.info("Using default transaction strategy (direct JDBC transactions)");
+ return new JDBCTransactionFactory();
+ }
+ log.info("Transaction strategy: " + strategyClassName);
+ TransactionFactory factory;
+ try {
+ factory = (TransactionFactory) ReflectHelper.classForName(strategyClassName).newInstance();
+ }
+ catch (ClassNotFoundException e) {
+ log.error("TransactionFactory class not found", e);
+ throw new HibernateException("TransactionFactory class not found: " + strategyClassName);
+ }
+ catch (IllegalAccessException e) {
+ log.error("Failed to instantiate TransactionFactory", e);
+ throw new HibernateException("Failed to instantiate TransactionFactory: " + e);
+ }
+ catch (java.lang.InstantiationException e) {
+ log.error("Failed to instantiate TransactionFactory", e);
+ throw new HibernateException("Failed to instantiate TransactionFactory: " + e);
+ }
+ factory.configure(transactionProps);
+ return factory;
+ }
+
+ private TransactionFactoryFactory() {}
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/TransactionManagerLookup.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/TransactionManagerLookup.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/TransactionManagerLookup.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,33 @@
+//$Id: TransactionManagerLookup.java 3890 2004-06-03 16:31:32Z steveebersole $
+package org.hibernate.transaction;
+
+import java.util.Properties;
+import javax.transaction.TransactionManager;
+
+import org.hibernate.HibernateException;
+
+/**
+ * Concrete implementations locate and return the JTA
+ * <tt>TransactionManager</tt>.
+ * @author Gavin King
+ */
+public interface TransactionManagerLookup {
+
+ /**
+ * Obtain the JTA <tt>TransactionManager</tt>
+ */
+ public TransactionManager getTransactionManager(Properties props) throws HibernateException;
+
+ /**
+ * Return the JNDI name of the JTA <tt>UserTransaction</tt>
+ * or <tt>null</tt> (optional operation).
+ */
+ public String getUserTransactionName();
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/TransactionManagerLookupFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/TransactionManagerLookupFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/TransactionManagerLookupFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,51 @@
+//$Id: TransactionManagerLookupFactory.java 11412 2007-04-17 14:38:51Z max.andersen at jboss.com $
+package org.hibernate.transaction;
+
+import java.util.Properties;
+
+import javax.transaction.TransactionManager;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.hibernate.HibernateException;
+import org.hibernate.cfg.Environment;
+import org.hibernate.util.ReflectHelper;
+
+/**
+ * @author Gavin King
+ */
+public final class TransactionManagerLookupFactory {
+
+ private static final Log log = LogFactory.getLog(TransactionManagerLookupFactory.class);
+
+ private TransactionManagerLookupFactory() {}
+
+ public static final TransactionManager getTransactionManager(Properties props) throws HibernateException {
+ log.info("obtaining TransactionManager");
+ return getTransactionManagerLookup(props).getTransactionManager(props);
+ }
+
+ public static final TransactionManagerLookup getTransactionManagerLookup(Properties props) throws HibernateException {
+
+ String tmLookupClass = props.getProperty(Environment.TRANSACTION_MANAGER_STRATEGY);
+ if (tmLookupClass==null) {
+ log.info("No TransactionManagerLookup configured (in JTA environment, use of read-write or transactional second-level cache is not recommended)");
+ return null;
+ }
+ else {
+
+ log.info("instantiating TransactionManagerLookup: " + tmLookupClass);
+
+ try {
+ TransactionManagerLookup lookup = (TransactionManagerLookup) ReflectHelper.classForName(tmLookupClass).newInstance();
+ log.info("instantiated TransactionManagerLookup");
+ return lookup;
+ }
+ catch (Exception e) {
+ log.error("Could not instantiate TransactionManagerLookup", e);
+ throw new HibernateException("Could not instantiate TransactionManagerLookup '" + tmLookupClass + "'");
+ }
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/WebSphereExtendedJTATransactionLookup.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/WebSphereExtendedJTATransactionLookup.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/WebSphereExtendedJTATransactionLookup.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,217 @@
+//$Id: WebSphereExtendedJTATransactionLookup.java 11498 2007-05-09 14:55:16Z steve.ebersole at jboss.com $
+package org.hibernate.transaction;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Properties;
+
+import javax.naming.NamingException;
+import javax.transaction.NotSupportedException;
+import javax.transaction.RollbackException;
+import javax.transaction.Status;
+import javax.transaction.Synchronization;
+import javax.transaction.SystemException;
+import javax.transaction.Transaction;
+import javax.transaction.TransactionManager;
+import javax.transaction.xa.XAResource;
+
+import org.hibernate.HibernateException;
+import org.hibernate.util.NamingHelper;
+
+/**
+ * TransactionManagerLookup implementation intended for use with WebSphere
+ * Application Server (WAS).
+ * <p/>
+ * WAS, unlike every other app server on the planet, does not allow direct
+ * access to the JTS TransactionManager. Instead, for common transaction-
+ * related tasks users must utilize a proprietary API known as
+ * ExtendedJTATransaction.
+ * <p/>
+ * Even more unfortunate, the exact TransactionManagerLookup to use inside of
+ * WAS is highly dependent upon (1) WAS version as well as (2) the WAS
+ * container in which Hibernate will be utilized.
+ * <p/>
+ * WebSphereExtendedJTATransactionLookup is reported to work on WAS version 6
+ * in any of the standard J2EE/JEE component containers.
+ *
+ * @author Gavin King
+ * @author <a href="mailto:jesper at udby.com>Jesper Udby</a>
+ */
+public class WebSphereExtendedJTATransactionLookup implements TransactionManagerLookup {
+
+ public TransactionManager getTransactionManager(Properties props) {
+ return new TransactionManagerAdapter( props );
+ }
+
+ public String getUserTransactionName() {
+ return "java:comp/UserTransaction";
+ }
+
+ public static class TransactionManagerAdapter implements TransactionManager {
+ private final Properties properties;
+ private final Class synchronizationCallbackClass;
+ private final Method registerSynchronizationMethod;
+ private final Method getLocalIdMethod;
+ private Object extendedJTATransaction;
+
+ private TransactionManagerAdapter(Properties props) throws HibernateException {
+ this.properties = props;
+ try {
+ synchronizationCallbackClass = Class.forName( "com.ibm.websphere.jtaextensions.SynchronizationCallback" );
+ Class extendedJTATransactionClass = Class.forName( "com.ibm.websphere.jtaextensions.ExtendedJTATransaction" );
+ registerSynchronizationMethod = extendedJTATransactionClass.getMethod(
+ "registerSynchronizationCallbackForCurrentTran",
+ new Class[] { synchronizationCallbackClass }
+ );
+ getLocalIdMethod = extendedJTATransactionClass.getMethod( "getLocalId", null );
+
+ }
+ catch ( ClassNotFoundException cnfe ) {
+ throw new HibernateException( cnfe );
+ }
+ catch ( NoSuchMethodException nsme ) {
+ throw new HibernateException( nsme );
+ }
+ }
+
+ public void begin() throws NotSupportedException, SystemException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void commit() throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ public int getStatus() throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ public Transaction getTransaction() throws SystemException {
+ return new TransactionAdapter( properties );
+ }
+
+ public void resume(Transaction txn) throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void rollback() throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setRollbackOnly() throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setTransactionTimeout(int i) throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ public Transaction suspend() throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ public class TransactionAdapter implements Transaction {
+
+ private TransactionAdapter(Properties props) {
+ try {
+ if ( extendedJTATransaction == null ) {
+ extendedJTATransaction = NamingHelper.getInitialContext( props )
+ .lookup( "java:comp/websphere/ExtendedJTATransaction" );
+ }
+ }
+ catch (NamingException ne) {
+ throw new HibernateException(ne);
+ }
+ }
+
+ public void registerSynchronization(final Synchronization synchronization)
+ throws RollbackException, IllegalStateException,
+ SystemException {
+
+ final InvocationHandler ih = new InvocationHandler() {
+
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ if ( "afterCompletion".equals( method.getName() ) ) {
+ int status = args[2].equals(Boolean.TRUE) ?
+ Status.STATUS_COMMITTED :
+ Status.STATUS_UNKNOWN;
+ synchronization.afterCompletion(status);
+ }
+ else if ( "beforeCompletion".equals( method.getName() ) ) {
+ synchronization.beforeCompletion();
+ }
+ else if ( "toString".equals( method.getName() ) ) {
+ return synchronization.toString();
+ }
+ return null;
+ }
+
+ };
+
+ final Object synchronizationCallback = Proxy.newProxyInstance(
+ getClass().getClassLoader(),
+ new Class[] { synchronizationCallbackClass },
+ ih
+ );
+
+ try {
+ registerSynchronizationMethod.invoke(
+ extendedJTATransaction,
+ new Object[] { synchronizationCallback }
+ );
+ }
+ catch (Exception e) {
+ throw new HibernateException(e);
+ }
+
+ }
+
+ public int hashCode() {
+ return getLocalId().hashCode();
+ }
+
+ public boolean equals(Object other) {
+ if ( !(other instanceof TransactionAdapter) ) return false;
+ TransactionAdapter that = (TransactionAdapter) other;
+ return getLocalId().equals( that.getLocalId() );
+ }
+
+ private Object getLocalId() throws HibernateException {
+ try {
+ return getLocalIdMethod.invoke( extendedJTATransaction, null );
+ }
+ catch ( Exception e ) {
+ throw new HibernateException( e );
+ }
+ }
+
+ public void commit() throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean delistResource(XAResource resource, int i) throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean enlistResource(XAResource resource) throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ public int getStatus() {
+ return new Integer(0).equals( getLocalId() ) ?
+ Status.STATUS_NO_TRANSACTION : Status.STATUS_ACTIVE;
+ }
+
+ public void rollback() throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setRollbackOnly() throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/WebSphereExtendedJTATransactionLookup.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/WebSphereTransactionManagerLookup.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/WebSphereTransactionManagerLookup.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/WebSphereTransactionManagerLookup.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,68 @@
+//$Id: WebSphereTransactionManagerLookup.java 8469 2005-10-26 22:03:03Z oneovthafew $
+package org.hibernate.transaction;
+
+import java.util.Properties;
+
+import javax.transaction.TransactionManager;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.hibernate.HibernateException;
+
+/**
+ * TransactionManager lookup strategy for WebSphere (versions 4, 5.0 and 5.1)
+ * @author Gavin King
+ */
+public class WebSphereTransactionManagerLookup implements TransactionManagerLookup {
+
+ private static final Log log = LogFactory.getLog(WebSphereTransactionManagerLookup.class);
+ private final int wsVersion;
+ private final Class tmfClass;
+
+ public WebSphereTransactionManagerLookup() {
+ try {
+ Class clazz;
+ int version;
+ try {
+ clazz = Class.forName("com.ibm.ws.Transaction.TransactionManagerFactory");
+ version = 5;
+ log.info("WebSphere 5.1");
+ }
+ catch (Exception e) {
+ try {
+ clazz = Class.forName("com.ibm.ejs.jts.jta.TransactionManagerFactory");
+ version = 5;
+ log.info("WebSphere 5.0");
+ }
+ catch (Exception e2) {
+ clazz = Class.forName("com.ibm.ejs.jts.jta.JTSXA");
+ version = 4;
+ log.info("WebSphere 4");
+ }
+ }
+
+ tmfClass=clazz;
+ wsVersion=version;
+ }
+ catch (Exception e) {
+ throw new HibernateException( "Could not obtain WebSphere TransactionManagerFactory instance", e );
+ }
+ }
+
+ public TransactionManager getTransactionManager(Properties props) throws HibernateException {
+ try {
+ return (TransactionManager) tmfClass.getMethod("getTransactionManager", null).invoke(null, null);
+ }
+ catch (Exception e) {
+ throw new HibernateException( "Could not obtain WebSphere TransactionManager", e );
+ }
+ }
+
+ public String getUserTransactionName() {
+ return wsVersion==5 ?
+ "java:comp/UserTransaction":
+ "jta/usertransaction";
+ }
+
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/WeblogicTransactionManagerLookup.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/WeblogicTransactionManagerLookup.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/WeblogicTransactionManagerLookup.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,27 @@
+//$Id: WeblogicTransactionManagerLookup.java 3890 2004-06-03 16:31:32Z steveebersole $
+package org.hibernate.transaction;
+
+/**
+ * TransactionManager lookup strategy for WebLogic
+ * @author Gavin King
+ */
+public final class WeblogicTransactionManagerLookup extends JNDITransactionManagerLookup {
+
+ /**
+ * @see org.hibernate.transaction.JNDITransactionManagerLookup#getName()
+ */
+ protected String getName() {
+ return "javax.transaction.TransactionManager";
+ }
+
+ public String getUserTransactionName() {
+ return "javax.transaction.UserTransaction";
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,10 @@
+<html>
+<head></head>
+<body>
+<p>
+ This package abstracts the underlying transaction mechanism
+ (JTA or JDBC) and provides strategies for obtaining application
+ server <tt>TransactionManager</tt>s.
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/transaction/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/transform/AliasToBeanConstructorResultTransformer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/transform/AliasToBeanConstructorResultTransformer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/transform/AliasToBeanConstructorResultTransformer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,33 @@
+package org.hibernate.transform;
+
+import java.lang.reflect.Constructor;
+import java.util.List;
+
+import org.hibernate.QueryException;
+
+public class AliasToBeanConstructorResultTransformer implements ResultTransformer {
+
+ private Constructor constructor;
+
+ public AliasToBeanConstructorResultTransformer(Constructor constructor) {
+ this.constructor = constructor;
+ }
+
+ public Object transformTuple(Object[] tuple, String[] aliases) {
+ try {
+ return constructor.newInstance( tuple );
+ }
+ catch ( Exception e ) {
+ throw new QueryException(
+ "could not instantiate: " +
+ constructor.getDeclaringClass().getName(),
+ e );
+ }
+ }
+
+ public List transformList(List collection) {
+ return collection;
+ }
+
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/transform/AliasToBeanResultTransformer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/transform/AliasToBeanResultTransformer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/transform/AliasToBeanResultTransformer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,81 @@
+/*
+ * Created on 27-Jan-2005
+ *
+ */
+package org.hibernate.transform;
+
+import java.util.List;
+
+import org.hibernate.HibernateException;
+import org.hibernate.property.ChainedPropertyAccessor;
+import org.hibernate.property.PropertyAccessor;
+import org.hibernate.property.PropertyAccessorFactory;
+import org.hibernate.property.Setter;
+
+/**
+ * Result transformer that allows to transform a result to
+ * a user specified class which will be populated via setter
+ * methods or fields matching the alias names.
+ *
+ * <pre>
+ * List resultWithAliasedBean = s.createCriteria(Enrolment.class)
+ * .createAlias("student", "st")
+ * .createAlias("course", "co")
+ * .setProjection( Projections.projectionList()
+ * .add( Projections.property("co.description"), "courseDescription" )
+ * )
+ * .setResultTransformer( new AliasToBeanResultTransformer(StudentDTO.class) )
+ * .list();
+ *
+ * StudentDTO dto = (StudentDTO)resultWithAliasedBean.get(0);
+ * </pre>
+ *
+ * @author max
+ *
+ */
+public class AliasToBeanResultTransformer implements ResultTransformer {
+
+ private final Class resultClass;
+ private Setter[] setters;
+ private PropertyAccessor propertyAccessor;
+
+ public AliasToBeanResultTransformer(Class resultClass) {
+ if(resultClass==null) throw new IllegalArgumentException("resultClass cannot be null");
+ this.resultClass = resultClass;
+ propertyAccessor = new ChainedPropertyAccessor(new PropertyAccessor[] { PropertyAccessorFactory.getPropertyAccessor(resultClass,null), PropertyAccessorFactory.getPropertyAccessor("field")});
+ }
+
+ public Object transformTuple(Object[] tuple, String[] aliases) {
+ Object result;
+
+ try {
+ if(setters==null) {
+ setters = new Setter[aliases.length];
+ for (int i = 0; i < aliases.length; i++) {
+ String alias = aliases[i];
+ if(alias != null) {
+ setters[i] = propertyAccessor.getSetter(resultClass, alias);
+ }
+ }
+ }
+ result = resultClass.newInstance();
+
+ for (int i = 0; i < aliases.length; i++) {
+ if(setters[i]!=null) {
+ setters[i].set(result, tuple[i], null);
+ }
+ }
+ } catch (InstantiationException e) {
+ throw new HibernateException("Could not instantiate resultclass: " + resultClass.getName());
+ } catch (IllegalAccessException e) {
+ throw new HibernateException("Could not instantiate resultclass: " + resultClass.getName());
+ }
+
+ return result;
+ }
+
+ public List transformList(List collection) {
+ return collection;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/transform/AliasToEntityMapResultTransformer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/transform/AliasToEntityMapResultTransformer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/transform/AliasToEntityMapResultTransformer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,28 @@
+//$Id: AliasToEntityMapResultTransformer.java 9649 2006-03-17 11:25:05Z max.andersen at jboss.com $
+package org.hibernate.transform;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Gavin King
+ */
+public class AliasToEntityMapResultTransformer implements ResultTransformer {
+
+ public Object transformTuple(Object[] tuple, String[] aliases) {
+ Map result = new HashMap(tuple.length);
+ for ( int i=0; i<tuple.length; i++ ) {
+ String alias = aliases[i];
+ if ( alias!=null ) {
+ result.put( alias, tuple[i] );
+ }
+ }
+ return result;
+ }
+
+ public List transformList(List collection) {
+ return collection;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/transform/DistinctRootEntityResultTransformer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/transform/DistinctRootEntityResultTransformer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/transform/DistinctRootEntityResultTransformer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,54 @@
+//$Id: DistinctRootEntityResultTransformer.java 3890 2004-06-03 16:31:32Z steveebersole $
+package org.hibernate.transform;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * @author Gavin King
+ */
+public class DistinctRootEntityResultTransformer implements ResultTransformer {
+
+ private static final Log log = LogFactory.getLog(DistinctRootEntityResultTransformer.class);
+
+ static final class Identity {
+ final Object entity;
+ Identity(Object entity) {
+ this.entity = entity;
+ }
+ public boolean equals(Object other) {
+ Identity that = (Identity) other;
+ return entity==that.entity;
+ }
+ public int hashCode() {
+ return System.identityHashCode(entity);
+ }
+ }
+
+ public Object transformTuple(Object[] tuple, String[] aliases) {
+ return tuple[ tuple.length-1 ];
+ }
+
+ public List transformList(List list) {
+ List result = new ArrayList( list.size() );
+ Set distinct = new HashSet();
+ for ( int i=0; i<list.size(); i++ ) {
+ Object entity = list.get(i);
+ if ( distinct.add( new Identity(entity) ) ) {
+ result.add(entity);
+ }
+ }
+ if ( log.isDebugEnabled() ) log.debug(
+ "transformed: " +
+ list.size() + " rows to: " +
+ result.size() + " distinct results"
+ );
+ return result;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/transform/PassThroughResultTransformer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/transform/PassThroughResultTransformer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/transform/PassThroughResultTransformer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,19 @@
+//$Id: PassThroughResultTransformer.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate.transform;
+
+import java.util.List;
+
+/**
+ * @author max
+ */
+public class PassThroughResultTransformer implements ResultTransformer {
+
+ public Object transformTuple(Object[] tuple, String[] aliases) {
+ return tuple.length==1 ? tuple[0] : tuple;
+ }
+
+ public List transformList(List collection) {
+ return collection;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/transform/ResultTransformer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/transform/ResultTransformer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/transform/ResultTransformer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,16 @@
+//$Id: ResultTransformer.java 5685 2005-02-12 07:19:50Z steveebersole $
+package org.hibernate.transform;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * Implementors define a strategy for transforming criteria query
+ * results into the actual application-visible query result list.
+ * @see org.hibernate.Criteria#setResultTransformer(ResultTransformer)
+ * @author Gavin King
+ */
+public interface ResultTransformer extends Serializable {
+ public Object transformTuple(Object[] tuple, String[] aliases);
+ public List transformList(List collection);
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/transform/RootEntityResultTransformer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/transform/RootEntityResultTransformer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/transform/RootEntityResultTransformer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,19 @@
+//$Id: RootEntityResultTransformer.java 3890 2004-06-03 16:31:32Z steveebersole $
+package org.hibernate.transform;
+
+import java.util.List;
+
+/**
+ * @author Gavin King
+ */
+public class RootEntityResultTransformer implements ResultTransformer {
+
+ public Object transformTuple(Object[] tuple, String[] aliases) {
+ return tuple[ tuple.length-1 ];
+ }
+
+ public List transformList(List collection) {
+ return collection;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/transform/ToListResultTransformer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/transform/ToListResultTransformer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/transform/ToListResultTransformer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,20 @@
+package org.hibernate.transform;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class ToListResultTransformer implements ResultTransformer {
+
+ public static final ResultTransformer INSTANCE = new ToListResultTransformer();
+
+ private ToListResultTransformer() {}
+
+ public Object transformTuple(Object[] tuple, String[] aliases) {
+ return Arrays.asList(tuple);
+ }
+
+ public List transformList(List collection) {
+ return collection;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/transform/Transformers.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/transform/Transformers.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/transform/Transformers.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,25 @@
+package org.hibernate.transform;
+
+final public class Transformers {
+
+ private Transformers() {}
+
+ /**
+ * Each row of results is a <tt>Map</tt> from alias to values/entities
+ */
+ public static final ResultTransformer ALIAS_TO_ENTITY_MAP = new AliasToEntityMapResultTransformer();
+
+ /**
+ * Each row of results is a <tt>List</tt>
+ */
+ public static final ResultTransformer TO_LIST = ToListResultTransformer.INSTANCE;
+
+ /**
+ * Creates a resulttransformer that will inject aliased values into
+ * instances of Class via property methods or fields.
+ */
+ public static ResultTransformer aliasToBean(Class target) {
+ return new AliasToBeanResultTransformer(target);
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/transform/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/transform/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/transform/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,9 @@
+<html>
+<head></head>
+<body>
+<p>
+ Defines strategies for post-processing criteria query
+ result sets into a form convenient to the application.
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/transform/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/Dom4jInstantiator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/Dom4jInstantiator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/Dom4jInstantiator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,54 @@
+//$Id: Dom4jInstantiator.java 7449 2005-07-11 17:31:50Z steveebersole $
+package org.hibernate.tuple;
+
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Iterator;
+
+import org.dom4j.Element;
+import org.hibernate.util.XMLHelper;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Component;
+
+/**
+ * Performs "instantiation" based on DOM4J elements.
+ */
+public class Dom4jInstantiator implements Instantiator {
+ private final String nodeName;
+ private final HashSet isInstanceNodeNames = new HashSet();
+
+ public Dom4jInstantiator(Component component) {
+ this.nodeName = component.getNodeName();
+ isInstanceNodeNames.add( nodeName );
+ }
+
+ public Dom4jInstantiator(PersistentClass mappingInfo) {
+ this.nodeName = mappingInfo.getNodeName();
+ isInstanceNodeNames.add( nodeName );
+
+ if ( mappingInfo.hasSubclasses() ) {
+ Iterator itr = mappingInfo.getSubclassClosureIterator();
+ while ( itr.hasNext() ) {
+ final PersistentClass subclassInfo = ( PersistentClass ) itr.next();
+ isInstanceNodeNames.add( subclassInfo.getNodeName() );
+ }
+ }
+ }
+
+ public Object instantiate(Serializable id) {
+ return instantiate();
+ }
+
+ public Object instantiate() {
+ return XMLHelper.generateDom4jElement( nodeName );
+ }
+
+ public boolean isInstance(Object object) {
+ if ( object instanceof Element ) {
+ return isInstanceNodeNames.contains( ( ( Element ) object ).getName() );
+ }
+ else {
+ return false;
+ }
+ }
+}
\ No newline at end of file
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/Dom4jInstantiator.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/DynamicMapInstantiator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/DynamicMapInstantiator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/DynamicMapInstantiator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,64 @@
+//$Id: DynamicMapInstantiator.java 7674 2005-07-29 06:14:16Z oneovthafew $
+package org.hibernate.tuple;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.hibernate.mapping.PersistentClass;
+
+
+public class DynamicMapInstantiator implements Instantiator {
+ public static final String KEY = "$type$";
+
+ private String entityName;
+ private Set isInstanceEntityNames = new HashSet();
+
+ public DynamicMapInstantiator() {
+ this.entityName = null;
+ }
+
+ public DynamicMapInstantiator(PersistentClass mappingInfo) {
+ this.entityName = mappingInfo.getEntityName();
+ isInstanceEntityNames.add( entityName );
+ if ( mappingInfo.hasSubclasses() ) {
+ Iterator itr = mappingInfo.getSubclassClosureIterator();
+ while ( itr.hasNext() ) {
+ final PersistentClass subclassInfo = ( PersistentClass ) itr.next();
+ isInstanceEntityNames.add( subclassInfo.getEntityName() );
+ }
+ }
+ }
+
+ public final Object instantiate(Serializable id) {
+ return instantiate();
+ }
+
+ public final Object instantiate() {
+ Map map = generateMap();
+ if ( entityName!=null ) {
+ map.put( KEY, entityName );
+ }
+ return map;
+ }
+
+ public final boolean isInstance(Object object) {
+ if ( object instanceof Map ) {
+ if ( entityName == null ) {
+ return true;
+ }
+ String type = ( String ) ( ( Map ) object ).get( KEY );
+ return type == null || isInstanceEntityNames.contains( type );
+ }
+ else {
+ return false;
+ }
+ }
+
+ protected Map generateMap() {
+ return new HashMap();
+ }
+}
\ No newline at end of file
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/DynamicMapInstantiator.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/ElementWrapper.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/ElementWrapper.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/ElementWrapper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,575 @@
+// $Id: ElementWrapper.java 6601 2005-04-29 03:06:37Z oneovthafew $
+package org.hibernate.tuple;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.io.Writer;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.dom4j.Attribute;
+import org.dom4j.Branch;
+import org.dom4j.CDATA;
+import org.dom4j.Comment;
+import org.dom4j.Document;
+import org.dom4j.Element;
+import org.dom4j.Entity;
+import org.dom4j.InvalidXPathException;
+import org.dom4j.Namespace;
+import org.dom4j.Node;
+import org.dom4j.ProcessingInstruction;
+import org.dom4j.QName;
+import org.dom4j.Text;
+import org.dom4j.Visitor;
+import org.dom4j.XPath;
+
+/**
+ * Wraps dom4j elements, allowing them to exist in a
+ * non-hierarchical structure.
+ *
+ * @author Gavin King
+ */
+public class ElementWrapper implements Element, Serializable {
+
+ private Element element;
+ private Element parent;
+
+ public Element getElement() {
+ return element;
+ }
+
+ public ElementWrapper(Element element) {
+ this.element = element;
+ }
+
+ public QName getQName() {
+ return element.getQName();
+ }
+
+ public QName getQName(String s) {
+ return element.getQName( s );
+ }
+
+ public void setQName(QName qName) {
+ element.setQName( qName );
+ }
+
+ public Namespace getNamespace() {
+ return element.getNamespace();
+ }
+
+ public Namespace getNamespaceForPrefix(String s) {
+ return element.getNamespaceForPrefix( s );
+ }
+
+ public Namespace getNamespaceForURI(String s) {
+ return element.getNamespaceForURI( s );
+ }
+
+ public List getNamespacesForURI(String s) {
+ return element.getNamespacesForURI( s );
+ }
+
+ public String getNamespacePrefix() {
+ return element.getNamespacePrefix();
+ }
+
+ public String getNamespaceURI() {
+ return element.getNamespaceURI();
+ }
+
+ public String getQualifiedName() {
+ return element.getQualifiedName();
+ }
+
+ public List additionalNamespaces() {
+ return element.additionalNamespaces();
+ }
+
+ public List declaredNamespaces() {
+ return element.declaredNamespaces();
+ }
+
+ public Element addAttribute(String attrName, String text) {
+ return element.addAttribute( attrName, text );
+ }
+
+ public Element addAttribute(QName attrName, String text) {
+ return element.addAttribute( attrName, text );
+ }
+
+ public Element addComment(String text) {
+ return element.addComment( text );
+ }
+
+ public Element addCDATA(String text) {
+ return element.addCDATA( text );
+ }
+
+ public Element addEntity(String name, String text) {
+ return element.addEntity( name, text );
+ }
+
+ public Element addNamespace(String prefix, String uri) {
+ return element.addNamespace( prefix, uri );
+ }
+
+ public Element addProcessingInstruction(String target, String text) {
+ return element.addProcessingInstruction( target, text );
+ }
+
+ public Element addProcessingInstruction(String target, Map data) {
+ return element.addProcessingInstruction( target, data );
+ }
+
+ public Element addText(String text) {
+ return element.addText( text );
+ }
+
+ public void add(Attribute attribute) {
+ element.add( attribute );
+ }
+
+ public void add(CDATA cdata) {
+ element.add( cdata );
+ }
+
+ public void add(Entity entity) {
+ element.add( entity );
+ }
+
+ public void add(Text text) {
+ element.add( text );
+ }
+
+ public void add(Namespace namespace) {
+ element.add( namespace );
+ }
+
+ public boolean remove(Attribute attribute) {
+ return element.remove( attribute );
+ }
+
+ public boolean remove(CDATA cdata) {
+ return element.remove( cdata );
+ }
+
+ public boolean remove(Entity entity) {
+ return element.remove( entity );
+ }
+
+ public boolean remove(Namespace namespace) {
+ return element.remove( namespace );
+ }
+
+ public boolean remove(Text text) {
+ return element.remove( text );
+ }
+
+ public boolean supportsParent() {
+ return element.supportsParent();
+ }
+
+ public Element getParent() {
+ return parent==null ? element.getParent() : parent;
+ }
+
+ public void setParent(Element parent) {
+ element.setParent( parent );
+ this.parent = parent;
+ }
+
+ public Document getDocument() {
+ return element.getDocument();
+ }
+
+ public void setDocument(Document document) {
+ element.setDocument( document );
+ }
+
+ public boolean isReadOnly() {
+ return element.isReadOnly();
+ }
+
+ public boolean hasContent() {
+ return element.hasContent();
+ }
+
+ public String getName() {
+ return element.getName();
+ }
+
+ public void setName(String name) {
+ element.setName( name );
+ }
+
+ public String getText() {
+ return element.getText();
+ }
+
+ public void setText(String text) {
+ element.setText( text );
+ }
+
+ public String getTextTrim() {
+ return element.getTextTrim();
+ }
+
+ public String getStringValue() {
+ return element.getStringValue();
+ }
+
+ public String getPath() {
+ return element.getPath();
+ }
+
+ public String getPath(Element element) {
+ return element.getPath( element );
+ }
+
+ public String getUniquePath() {
+ return element.getUniquePath();
+ }
+
+ public String getUniquePath(Element element) {
+ return element.getUniquePath( element );
+ }
+
+ public String asXML() {
+ return element.asXML();
+ }
+
+ public void write(Writer writer) throws IOException {
+ element.write( writer );
+ }
+
+ public short getNodeType() {
+ return element.getNodeType();
+ }
+
+ public String getNodeTypeName() {
+ return element.getNodeTypeName();
+ }
+
+ public Node detach() {
+ if (parent!=null) {
+ parent.remove(this);
+ parent = null;
+ }
+ return element.detach();
+ }
+
+ public List selectNodes(String xpath) {
+ return element.selectNodes( xpath );
+ }
+
+ public Object selectObject(String xpath) {
+ return element.selectObject( xpath );
+ }
+
+ public List selectNodes(String xpath, String comparison) {
+ return element.selectNodes( xpath, comparison );
+ }
+
+ public List selectNodes(String xpath, String comparison, boolean removeDups) {
+ return element.selectNodes( xpath, comparison, removeDups );
+ }
+
+ public Node selectSingleNode(String xpath) {
+ return element.selectSingleNode( xpath );
+ }
+
+ public String valueOf(String xpath) {
+ return element.valueOf( xpath );
+ }
+
+ public Number numberValueOf(String xpath) {
+ return element.numberValueOf( xpath );
+ }
+
+ public boolean matches(String xpath) {
+ return element.matches( xpath );
+ }
+
+ public XPath createXPath(String xpath) throws InvalidXPathException {
+ return element.createXPath( xpath );
+ }
+
+ public Node asXPathResult(Element element) {
+ return element.asXPathResult( element );
+ }
+
+ public void accept(Visitor visitor) {
+ element.accept( visitor );
+ }
+
+ public Object clone() {
+ return element.clone();
+ }
+
+ public Object getData() {
+ return element.getData();
+ }
+
+ public void setData(Object data) {
+ element.setData( data );
+ }
+
+ public List attributes() {
+ return element.attributes();
+ }
+
+ public void setAttributes(List list) {
+ element.setAttributes( list );
+ }
+
+ public int attributeCount() {
+ return element.attributeCount();
+ }
+
+ public Iterator attributeIterator() {
+ return element.attributeIterator();
+ }
+
+ public Attribute attribute(int i) {
+ return element.attribute( i );
+ }
+
+ public Attribute attribute(String name) {
+ return element.attribute( name );
+ }
+
+ public Attribute attribute(QName qName) {
+ return element.attribute( qName );
+ }
+
+ public String attributeValue(String name) {
+ return element.attributeValue( name );
+ }
+
+ public String attributeValue(String name, String defaultValue) {
+ return element.attributeValue( name, defaultValue );
+ }
+
+ public String attributeValue(QName qName) {
+ return element.attributeValue( qName );
+ }
+
+ public String attributeValue(QName qName, String defaultValue) {
+ return element.attributeValue( qName, defaultValue );
+ }
+
+ /**
+ * @deprecated
+ */
+ public void setAttributeValue(String name, String value) {
+ element.setAttributeValue( name, value );
+ }
+
+ /**
+ * @deprecated
+ */
+ public void setAttributeValue(QName qName, String value) {
+ element.setAttributeValue( qName, value );
+ }
+
+ public Element element(String name) {
+ return element.element( name );
+ }
+
+ public Element element(QName qName) {
+ return element.element( qName );
+ }
+
+ public List elements() {
+ return element.elements();
+ }
+
+ public List elements(String name) {
+ return element.elements( name );
+ }
+
+ public List elements(QName qName) {
+ return element.elements( qName );
+ }
+
+ public Iterator elementIterator() {
+ return element.elementIterator();
+ }
+
+ public Iterator elementIterator(String name) {
+ return element.elementIterator( name );
+
+ }
+
+ public Iterator elementIterator(QName qName) {
+ return element.elementIterator( qName );
+ }
+
+ public boolean isRootElement() {
+ return element.isRootElement();
+ }
+
+ public boolean hasMixedContent() {
+ return element.hasMixedContent();
+ }
+
+ public boolean isTextOnly() {
+ return element.isTextOnly();
+ }
+
+ public void appendAttributes(Element element) {
+ element.appendAttributes( element );
+ }
+
+ public Element createCopy() {
+ return element.createCopy();
+ }
+
+ public Element createCopy(String name) {
+ return element.createCopy( name );
+ }
+
+ public Element createCopy(QName qName) {
+ return element.createCopy( qName );
+ }
+
+ public String elementText(String name) {
+ return element.elementText( name );
+ }
+
+ public String elementText(QName qName) {
+ return element.elementText( qName );
+ }
+
+ public String elementTextTrim(String name) {
+ return element.elementTextTrim( name );
+ }
+
+ public String elementTextTrim(QName qName) {
+ return element.elementTextTrim( qName );
+ }
+
+ public Node getXPathResult(int i) {
+ return element.getXPathResult( i );
+ }
+
+ public Node node(int i) {
+ return element.node( i );
+ }
+
+ public int indexOf(Node node) {
+ return element.indexOf( node );
+ }
+
+ public int nodeCount() {
+ return element.nodeCount();
+ }
+
+ public Element elementByID(String id) {
+ return element.elementByID( id );
+ }
+
+ public List content() {
+ return element.content();
+ }
+
+ public Iterator nodeIterator() {
+ return element.nodeIterator();
+ }
+
+ public void setContent(List list) {
+ element.setContent( list );
+ }
+
+ public void appendContent(Branch branch) {
+ element.appendContent( branch );
+ }
+
+ public void clearContent() {
+ element.clearContent();
+ }
+
+ public List processingInstructions() {
+ return element.processingInstructions();
+ }
+
+ public List processingInstructions(String name) {
+ return element.processingInstructions( name );
+ }
+
+ public ProcessingInstruction processingInstruction(String name) {
+ return element.processingInstruction( name );
+ }
+
+ public void setProcessingInstructions(List list) {
+ element.setProcessingInstructions( list );
+ }
+
+ public Element addElement(String name) {
+ return element.addElement( name );
+ }
+
+ public Element addElement(QName qName) {
+ return element.addElement( qName );
+ }
+
+ public Element addElement(String name, String text) {
+ return element.addElement( name, text );
+
+ }
+
+ public boolean removeProcessingInstruction(String name) {
+ return element.removeProcessingInstruction( name );
+ }
+
+ public void add(Node node) {
+ element.add( node );
+ }
+
+ public void add(Comment comment) {
+ element.add( comment );
+ }
+
+ public void add(Element element) {
+ element.add( element );
+ }
+
+ public void add(ProcessingInstruction processingInstruction) {
+ element.add( processingInstruction );
+ }
+
+ public boolean remove(Node node) {
+ return element.remove( node );
+ }
+
+ public boolean remove(Comment comment) {
+ return element.remove( comment );
+ }
+
+ public boolean remove(Element element) {
+ return element.remove( element );
+ }
+
+ public boolean remove(ProcessingInstruction processingInstruction) {
+ return element.remove( processingInstruction );
+ }
+
+ public void normalize() {
+ element.normalize();
+ }
+
+ public boolean equals(Object other) {
+ return element.equals(other);
+ }
+
+ public int hashCode() {
+ return element.hashCode();
+ }
+
+ public String toString() {
+ return element.toString();
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/ElementWrapper.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/EntityModeToTuplizerMapping.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/EntityModeToTuplizerMapping.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/EntityModeToTuplizerMapping.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,72 @@
+package org.hibernate.tuple;
+
+import org.apache.commons.collections.SequencedHashMap;
+
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+
+import java.util.Map;
+import java.util.Collections;
+import java.util.Iterator;
+import java.io.Serializable;
+
+/**
+ * Centralizes handling of {@link EntityMode} to {@link Tuplizer} mappings.
+ *
+ * @author Steve Ebersole
+ */
+public abstract class EntityModeToTuplizerMapping implements Serializable {
+
+ // map of EntityMode -> Tuplizer
+ private final Map tuplizers = Collections.synchronizedMap( new SequencedHashMap() );
+
+ protected void addTuplizer(EntityMode entityMode, Tuplizer tuplizer) {
+ tuplizers.put( entityMode, tuplizer );
+ }
+
+ /**
+ * Given a supposed instance of an entity/component, guess its entity mode.
+ *
+ * @param object The supposed instance of the entity/component.
+ * @return The guessed entity mode.
+ */
+ public EntityMode guessEntityMode(Object object) {
+ Iterator itr = tuplizers.entrySet().iterator();
+ while( itr.hasNext() ) {
+ Map.Entry entry = ( Map.Entry ) itr.next();
+ Tuplizer tuplizer = ( Tuplizer ) entry.getValue();
+ if ( tuplizer.isInstance( object ) ) {
+ return ( EntityMode ) entry.getKey();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Locate the contained tuplizer responsible for the given entity-mode. If
+ * no such tuplizer is defined on this mapping, then return null.
+ *
+ * @param entityMode The entity-mode for which the caller wants a tuplizer.
+ * @return The tuplizer, or null if not found.
+ */
+ public Tuplizer getTuplizerOrNull(EntityMode entityMode) {
+ return ( Tuplizer ) tuplizers.get( entityMode );
+ }
+
+ /**
+ * Locate the tuplizer contained within this mapping which is responsible
+ * for the given entity-mode. If no such tuplizer is defined on this
+ * mapping, then an exception is thrown.
+ *
+ * @param entityMode The entity-mode for which the caller wants a tuplizer.
+ * @return The tuplizer.
+ * @throws HibernateException Unable to locate the requested tuplizer.
+ */
+ public Tuplizer getTuplizer(EntityMode entityMode) {
+ Tuplizer tuplizer = getTuplizerOrNull( entityMode );
+ if ( tuplizer == null ) {
+ throw new HibernateException( "No tuplizer found for entity-mode [" + entityMode + "]");
+ }
+ return tuplizer;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/IdentifierProperty.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/IdentifierProperty.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/IdentifierProperty.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,100 @@
+// $Id: IdentifierProperty.java 9295 2006-02-15 22:28:15Z epbernard $
+package org.hibernate.tuple;
+
+import org.hibernate.engine.IdentifierValue;
+import org.hibernate.id.IdentifierGenerator;
+import org.hibernate.id.PostInsertIdentifierGenerator;
+import org.hibernate.type.Type;
+
+/**
+ * Represents a defined entity identifier property within the Hibernate
+ * runtime-metamodel.
+ *
+ * @author Steve Ebersole
+ */
+public class IdentifierProperty extends Property {
+
+ private boolean virtual;
+ private boolean embedded;
+ private IdentifierValue unsavedValue;
+ private IdentifierGenerator identifierGenerator;
+ private boolean identifierAssignedByInsert;
+ private boolean hasIdentifierMapper;
+
+ /**
+ * Construct a non-virtual identifier property.
+ *
+ * @param name The name of the property representing the identifier within
+ * its owning entity.
+ * @param node The node name to use for XML-based representation of this
+ * property.
+ * @param type The Hibernate Type for the identifier property.
+ * @param embedded Is this an embedded identifier.
+ * @param unsavedValue The value which, if found as the value on the identifier
+ * property, represents new (i.e., un-saved) instances of the owning entity.
+ * @param identifierGenerator The generator to use for id value generation.
+ */
+ public IdentifierProperty(
+ String name,
+ String node,
+ Type type,
+ boolean embedded,
+ IdentifierValue unsavedValue,
+ IdentifierGenerator identifierGenerator) {
+ super(name, node, type);
+ this.virtual = false;
+ this.embedded = embedded;
+ this.hasIdentifierMapper = false;
+ this.unsavedValue = unsavedValue;
+ this.identifierGenerator = identifierGenerator;
+ this.identifierAssignedByInsert = identifierGenerator instanceof PostInsertIdentifierGenerator;
+ }
+
+ /**
+ * Construct a virtual IdentifierProperty.
+ *
+ * @param type The Hibernate Type for the identifier property.
+ * @param embedded Is this an embedded identifier.
+ * @param unsavedValue The value which, if found as the value on the identifier
+ * property, represents new (i.e., un-saved) instances of the owning entity.
+ * @param identifierGenerator The generator to use for id value generation.
+ */
+ public IdentifierProperty(
+ Type type,
+ boolean embedded,
+ boolean hasIdentifierMapper,
+ IdentifierValue unsavedValue,
+ IdentifierGenerator identifierGenerator) {
+ super(null, null, type);
+ this.virtual = true;
+ this.embedded = embedded;
+ this.hasIdentifierMapper = hasIdentifierMapper;
+ this.unsavedValue = unsavedValue;
+ this.identifierGenerator = identifierGenerator;
+ this.identifierAssignedByInsert = identifierGenerator instanceof PostInsertIdentifierGenerator;
+ }
+
+ public boolean isVirtual() {
+ return virtual;
+ }
+
+ public boolean isEmbedded() {
+ return embedded;
+ }
+
+ public IdentifierValue getUnsavedValue() {
+ return unsavedValue;
+ }
+
+ public IdentifierGenerator getIdentifierGenerator() {
+ return identifierGenerator;
+ }
+
+ public boolean isIdentifierAssignedByInsert() {
+ return identifierAssignedByInsert;
+ }
+
+ public boolean hasIdentifierMapper() {
+ return hasIdentifierMapper;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/Instantiator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/Instantiator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/Instantiator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,40 @@
+// $Id: Instantiator.java 7449 2005-07-11 17:31:50Z steveebersole $
+package org.hibernate.tuple;
+
+
+import java.io.Serializable;
+
+/**
+ * Contract for implementors responsible for instantiating entity/component instances.
+ *
+ * @author Steve Ebersole
+ */
+public interface Instantiator extends Serializable {
+
+ /**
+ * Perform the requested entity instantiation.
+ * <p/>
+ * This form is never called for component instantiation, only entity instantiation.
+ *
+ * @param id The id of the entity to be instantiated.
+ * @return An appropriately instantiated entity.
+ */
+ public Object instantiate(Serializable id);
+
+ /**
+ * Perform the requested instantiation.
+ *
+ * @return The instantiated data structure.
+ */
+ public Object instantiate();
+
+ /**
+ * Performs check to see if the given object is an instance of the entity
+ * or component which this Instantiator instantiates.
+ *
+ * @param object The object to be checked.
+ * @return True is the object does respresent an instance of the underlying
+ * entity/component.
+ */
+ public boolean isInstance(Object object);
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/PojoInstantiator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/PojoInstantiator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/PojoInstantiator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,107 @@
+//$Id: PojoInstantiator.java 9210 2006-02-03 22:15:19Z steveebersole $
+package org.hibernate.tuple;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.lang.reflect.Constructor;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.InstantiationException;
+import org.hibernate.PropertyNotFoundException;
+import org.hibernate.bytecode.ReflectionOptimizer;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Component;
+import org.hibernate.util.ReflectHelper;
+
+/**
+ * Defines a POJO-based instantiator for use from the tuplizers.
+ */
+public class PojoInstantiator implements Instantiator, Serializable {
+
+ private static final Log log = LogFactory.getLog(PojoInstantiator.class);
+
+ private transient Constructor constructor;
+
+ private final Class mappedClass;
+ private final transient ReflectionOptimizer.InstantiationOptimizer optimizer;
+ private final boolean embeddedIdentifier;
+ private final Class proxyInterface;
+
+ public PojoInstantiator(Component component, ReflectionOptimizer.InstantiationOptimizer optimizer) {
+ this.mappedClass = component.getComponentClass();
+ this.optimizer = optimizer;
+
+ this.proxyInterface = null;
+ this.embeddedIdentifier = false;
+
+ try {
+ constructor = ReflectHelper.getDefaultConstructor(mappedClass);
+ }
+ catch ( PropertyNotFoundException pnfe ) {
+ log.info(
+ "no default (no-argument) constructor for class: " +
+ mappedClass.getName() +
+ " (class must be instantiated by Interceptor)"
+ );
+ constructor = null;
+ }
+ }
+
+ public PojoInstantiator(PersistentClass persistentClass, ReflectionOptimizer.InstantiationOptimizer optimizer) {
+ this.mappedClass = persistentClass.getMappedClass();
+ this.proxyInterface = persistentClass.getProxyInterface();
+ this.embeddedIdentifier = persistentClass.hasEmbeddedIdentifier();
+ this.optimizer = optimizer;
+
+ try {
+ constructor = ReflectHelper.getDefaultConstructor( mappedClass );
+ }
+ catch ( PropertyNotFoundException pnfe ) {
+ log.info(
+ "no default (no-argument) constructor for class: " +
+ mappedClass.getName() +
+ " (class must be instantiated by Interceptor)"
+ );
+ constructor = null;
+ }
+ }
+
+ private void readObject(java.io.ObjectInputStream stream)
+ throws ClassNotFoundException, IOException {
+ stream.defaultReadObject();
+ constructor = ReflectHelper.getDefaultConstructor(mappedClass);
+ }
+
+ public Object instantiate() {
+ if ( ReflectHelper.isAbstractClass(mappedClass) ) {
+ throw new InstantiationException( "Cannot instantiate abstract class or interface: ", mappedClass );
+ }
+ else if ( optimizer != null ) {
+ return optimizer.newInstance();
+ }
+ else if ( constructor == null ) {
+ throw new InstantiationException( "No default constructor for entity: ", mappedClass );
+ }
+ else {
+ try {
+ return constructor.newInstance( null );
+ }
+ catch ( Exception e ) {
+ throw new InstantiationException( "Could not instantiate entity: ", mappedClass, e );
+ }
+ }
+ }
+
+ public Object instantiate(Serializable id) {
+ final boolean useEmbeddedIdentifierInstanceAsEntity = embeddedIdentifier &&
+ id != null &&
+ id.getClass().equals(mappedClass);
+ return useEmbeddedIdentifierInstanceAsEntity ? id : instantiate();
+ }
+
+ public boolean isInstance(Object object) {
+ return mappedClass.isInstance(object) ||
+ ( proxyInterface!=null && proxyInterface.isInstance(object) ); //this one needed only for guessEntityMode()
+ }
+}
\ No newline at end of file
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/PojoInstantiator.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/Property.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/Property.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/Property.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,49 @@
+// $Id: Property.java 5814 2005-02-21 02:08:46Z oneovthafew $
+package org.hibernate.tuple;
+
+import org.hibernate.type.Type;
+
+import java.io.Serializable;
+
+/**
+ * Defines the basic contract of a Property within the runtime metamodel.
+ *
+ * @author Steve Ebersole
+ */
+public abstract class Property implements Serializable {
+ private String name;
+ private String node;
+ private Type type;
+
+ /**
+ * Constructor for Property instances.
+ *
+ * @param name The name by which the property can be referenced within
+ * its owner.
+ * @param node The node name to use for XML-based representation of this
+ * property.
+ * @param type The Hibernate Type of this property.
+ */
+ protected Property(String name, String node, Type type) {
+ this.name = name;
+ this.node = node;
+ this.type = type;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getNode() {
+ return node;
+ }
+
+ public Type getType() {
+ return type;
+ }
+
+ public String toString() {
+ return "Property(" + name + ':' + type.getName() + ')';
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/PropertyFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/PropertyFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/PropertyFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,171 @@
+// $Id: PropertyFactory.java 10119 2006-07-14 00:09:19Z steve.ebersole at jboss.com $
+package org.hibernate.tuple;
+
+import java.lang.reflect.Constructor;
+
+import org.hibernate.EntityMode;
+import org.hibernate.mapping.PropertyGeneration;
+import org.hibernate.engine.IdentifierValue;
+import org.hibernate.engine.UnsavedValueFactory;
+import org.hibernate.engine.VersionValue;
+import org.hibernate.id.IdentifierGenerator;
+import org.hibernate.mapping.KeyValue;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Property;
+import org.hibernate.property.Getter;
+import org.hibernate.property.PropertyAccessor;
+import org.hibernate.property.PropertyAccessorFactory;
+import org.hibernate.type.AssociationType;
+import org.hibernate.type.Type;
+import org.hibernate.type.VersionType;
+import org.hibernate.util.ReflectHelper;
+
+/**
+ * Responsible for generation of runtime metamodel {@link Property} representations.
+ * Makes distinction between identifier, version, and other (standard) properties.
+ *
+ * @author Steve Ebersole
+ */
+public class PropertyFactory {
+
+ /**
+ * Generates an IdentifierProperty representation of the for a given entity mapping.
+ *
+ * @param mappedEntity The mapping definition of the entity.
+ * @param generator The identifier value generator to use for this identifier.
+ * @return The appropriate IdentifierProperty definition.
+ */
+ public static IdentifierProperty buildIdentifierProperty(PersistentClass mappedEntity, IdentifierGenerator generator) {
+
+ String mappedUnsavedValue = mappedEntity.getIdentifier().getNullValue();
+ Type type = mappedEntity.getIdentifier().getType();
+ Property property = mappedEntity.getIdentifierProperty();
+
+ IdentifierValue unsavedValue = UnsavedValueFactory.getUnsavedIdentifierValue(
+ mappedUnsavedValue,
+ getGetter( property ),
+ type,
+ getConstructor(mappedEntity)
+ );
+
+ if ( property == null ) {
+ // this is a virtual id property...
+ return new IdentifierProperty(
+ type,
+ mappedEntity.hasEmbeddedIdentifier(),
+ mappedEntity.hasIdentifierMapper(),
+ unsavedValue,
+ generator
+ );
+ }
+ else {
+ return new IdentifierProperty(
+ property.getName(),
+ property.getNodeName(),
+ type,
+ mappedEntity.hasEmbeddedIdentifier(),
+ unsavedValue,
+ generator
+ );
+ }
+ }
+
+ /**
+ * Generates a VersionProperty representation for an entity mapping given its
+ * version mapping Property.
+ *
+ * @param property The version mapping Property.
+ * @param lazyAvailable Is property lazy loading currently available.
+ * @return The appropriate VersionProperty definition.
+ */
+ public static VersionProperty buildVersionProperty(Property property, boolean lazyAvailable) {
+ String mappedUnsavedValue = ( (KeyValue) property.getValue() ).getNullValue();
+
+ VersionValue unsavedValue = UnsavedValueFactory.getUnsavedVersionValue(
+ mappedUnsavedValue,
+ getGetter( property ),
+ (VersionType) property.getType(),
+ getConstructor( property.getPersistentClass() )
+ );
+
+ boolean lazy = lazyAvailable && property.isLazy();
+
+ return new VersionProperty(
+ property.getName(),
+ property.getNodeName(),
+ property.getValue().getType(),
+ lazy,
+ property.isInsertable(),
+ property.isUpdateable(),
+ property.getGeneration() == PropertyGeneration.INSERT || property.getGeneration() == PropertyGeneration.ALWAYS,
+ property.getGeneration() == PropertyGeneration.ALWAYS,
+ property.isOptional(),
+ property.isUpdateable() && !lazy,
+ property.isOptimisticLocked(),
+ property.getCascadeStyle(),
+ unsavedValue
+ );
+ }
+
+ /**
+ * Generate a "standard" (i.e., non-identifier and non-version) based on the given
+ * mapped property.
+ *
+ * @param property The mapped property.
+ * @param lazyAvailable Is property lazy loading currently available.
+ * @return The appropriate StandardProperty definition.
+ */
+ public static StandardProperty buildStandardProperty(Property property, boolean lazyAvailable) {
+
+ final Type type = property.getValue().getType();
+
+ // we need to dirty check collections, since they can cause an owner
+ // version number increment
+
+ // we need to dirty check many-to-ones with not-found="ignore" in order
+ // to update the cache (not the database), since in this case a null
+ // entity reference can lose information
+
+ boolean alwaysDirtyCheck = type.isAssociationType() &&
+ ( (AssociationType) type ).isAlwaysDirtyChecked();
+
+ return new StandardProperty(
+ property.getName(),
+ property.getNodeName(),
+ type,
+ lazyAvailable && property.isLazy(),
+ property.isInsertable(),
+ property.isUpdateable(),
+ property.getGeneration() == PropertyGeneration.INSERT || property.getGeneration() == PropertyGeneration.ALWAYS,
+ property.getGeneration() == PropertyGeneration.ALWAYS,
+ property.isOptional(),
+ alwaysDirtyCheck || property.isUpdateable(),
+ property.isOptimisticLocked(),
+ property.getCascadeStyle(),
+ property.getValue().getFetchMode()
+ );
+ }
+
+ private static Constructor getConstructor(PersistentClass persistentClass) {
+ if ( persistentClass == null || !persistentClass.hasPojoRepresentation() ) {
+ return null;
+ }
+
+ try {
+ return ReflectHelper.getDefaultConstructor( persistentClass.getMappedClass() );
+ }
+ catch( Throwable t ) {
+ return null;
+ }
+ }
+
+ private static Getter getGetter(Property mappingProperty) {
+ if ( mappingProperty == null || !mappingProperty.getPersistentClass().hasPojoRepresentation() ) {
+ return null;
+ }
+
+ PropertyAccessor pa = PropertyAccessorFactory.getPropertyAccessor( mappingProperty, EntityMode.POJO );
+ return pa.getGetter( mappingProperty.getPersistentClass().getMappedClass(), mappingProperty.getName() );
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/StandardProperty.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/StandardProperty.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/StandardProperty.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,115 @@
+// $Id: StandardProperty.java 10119 2006-07-14 00:09:19Z steve.ebersole at jboss.com $
+package org.hibernate.tuple;
+
+import org.hibernate.engine.CascadeStyle;
+import org.hibernate.type.Type;
+import org.hibernate.FetchMode;
+
+/**
+ * Represents a basic property within the Hibernate runtime-metamodel.
+ *
+ * @author Steve Ebersole
+ */
+public class StandardProperty extends Property {
+
+ private final boolean lazy;
+ private final boolean insertable;
+ private final boolean updateable;
+ private final boolean insertGenerated;
+ private final boolean updateGenerated;
+ private final boolean nullable;
+ private final boolean dirtyCheckable;
+ private final boolean versionable;
+ private final CascadeStyle cascadeStyle;
+ private final FetchMode fetchMode;
+
+ /**
+ * Constructs StandardProperty instances.
+ *
+ * @param name The name by which the property can be referenced within
+ * its owner.
+ * @param node The node name to use for XML-based representation of this
+ * property.
+ * @param type The Hibernate Type of this property.
+ * @param lazy Should this property be handled lazily?
+ * @param insertable Is this property an insertable value?
+ * @param updateable Is this property an updateable value?
+ * @param insertGenerated Is this property generated in the database on insert?
+ * @param updateGenerated Is this property generated in the database on update?
+ * @param nullable Is this property a nullable value?
+ * @param checkable Is this property a checkable value?
+ * @param versionable Is this property a versionable value?
+ * @param cascadeStyle The cascade style for this property's value.
+ * @param fetchMode Any fetch mode defined for this property
+ */
+ public StandardProperty(
+ String name,
+ String node,
+ Type type,
+ boolean lazy,
+ boolean insertable,
+ boolean updateable,
+ boolean insertGenerated,
+ boolean updateGenerated,
+ boolean nullable,
+ boolean checkable,
+ boolean versionable,
+ CascadeStyle cascadeStyle,
+ FetchMode fetchMode) {
+ super(name, node, type);
+ this.lazy = lazy;
+ this.insertable = insertable;
+ this.updateable = updateable;
+ this.insertGenerated = insertGenerated;
+ this.updateGenerated = updateGenerated;
+ this.nullable = nullable;
+ this.dirtyCheckable = checkable;
+ this.versionable = versionable;
+ this.cascadeStyle = cascadeStyle;
+ this.fetchMode = fetchMode;
+ }
+
+ public boolean isLazy() {
+ return lazy;
+ }
+
+ public boolean isInsertable() {
+ return insertable;
+ }
+
+ public boolean isUpdateable() {
+ return updateable;
+ }
+
+ public boolean isInsertGenerated() {
+ return insertGenerated;
+ }
+
+ public boolean isUpdateGenerated() {
+ return updateGenerated;
+ }
+
+ public boolean isNullable() {
+ return nullable;
+ }
+
+ public boolean isDirtyCheckable(boolean hasUninitializedProperties) {
+ return isDirtyCheckable() && ( !hasUninitializedProperties || !isLazy() );
+ }
+
+ public boolean isDirtyCheckable() {
+ return dirtyCheckable;
+ }
+
+ public boolean isVersionable() {
+ return versionable;
+ }
+
+ public CascadeStyle getCascadeStyle() {
+ return cascadeStyle;
+ }
+
+ public FetchMode getFetchMode() {
+ return fetchMode;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/Tuplizer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/Tuplizer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/Tuplizer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,95 @@
+// $Id: Tuplizer.java 10119 2006-07-14 00:09:19Z steve.ebersole at jboss.com $
+package org.hibernate.tuple;
+
+import org.hibernate.HibernateException;
+
+/**
+ * A tuplizer defines the contract for things which know how to manage
+ * a particular representation of a piece of data, given that
+ * representation's {@link org.hibernate.EntityMode} (the entity-mode
+ * essentially defining which representation).
+ * </p>
+ * If that given piece of data is thought of as a data structure, then a tuplizer
+ * is the thing which knows how to<ul>
+ * <li>create such a data structure appropriately
+ * <li>extract values from and inject values into such a data structure
+ * </ul>
+ * </p>
+ * For example, a given piece of data might be represented as a POJO class.
+ * Here, it's representation and entity-mode is POJO. Well a tuplizer for POJO
+ * entity-modes would know how to<ul>
+ * <li>create the data structure by calling the POJO's constructor
+ * <li>extract and inject values through getters/setter, or by direct field access, etc
+ * </ul>
+ * </p>
+ * That same piece of data might also be represented as a DOM structure, using
+ * the tuplizer associated with the DOM4J entity-mode, which would generate instances
+ * of {@link org.dom4j.Element} as the data structure and know how to access the
+ * values as either nested {@link org.dom4j.Element}s or as {@link org.dom4j.Attribute}s.
+ *
+ * @see org.hibernate.tuple.entity.EntityTuplizer
+ * @see org.hibernate.tuple.component.ComponentTuplizer
+ *
+ * @author Steve Ebersole
+ */
+public interface Tuplizer {
+
+ /**
+ * Extract the current values contained on the given entity.
+ *
+ * @param entity The entity from which to extract values.
+ * @return The current property values.
+ * @throws HibernateException
+ */
+ public Object[] getPropertyValues(Object entity) throws HibernateException;
+
+ /**
+ * Inject the given values into the given entity.
+ *
+ * @param entity The entity.
+ * @param values The values to be injected.
+ * @throws HibernateException
+ */
+ public void setPropertyValues(Object entity, Object[] values) throws HibernateException;
+
+ /**
+ * Extract the value of a particular property from the given entity.
+ *
+ * @param entity The entity from which to extract the property value.
+ * @param i The index of the property for which to extract the value.
+ * @return The current value of the given property on the given entity.
+ * @throws HibernateException
+ */
+ public Object getPropertyValue(Object entity, int i) throws HibernateException;
+
+ /**
+ * Generate a new, empty entity.
+ *
+ * @return The new, empty entity instance.
+ * @throws HibernateException
+ */
+ public Object instantiate() throws HibernateException;
+
+ /**
+ * Is the given object considered an instance of the the entity (acconting
+ * for entity-mode) managed by this tuplizer.
+ *
+ * @param object The object to be checked.
+ * @return True if the object is considered as an instance of this entity
+ * within the given mode.
+ */
+ public boolean isInstance(Object object);
+
+ /**
+ * Return the pojo class managed by this tuplizer.
+ * </p>
+ * Need to determine how to best handle this for the Tuplizers for EntityModes
+ * other than POJO.
+ * </p>
+ * todo : be really nice to not have this here since it is essentially pojo specific...
+ *
+ * @return The persistent class.
+ */
+ public Class getMappedClass();
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/VersionProperty.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/VersionProperty.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/VersionProperty.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,59 @@
+// $Id: VersionProperty.java 10119 2006-07-14 00:09:19Z steve.ebersole at jboss.com $
+package org.hibernate.tuple;
+
+import org.hibernate.engine.CascadeStyle;
+import org.hibernate.engine.VersionValue;
+import org.hibernate.type.Type;
+
+/**
+ * Represents a version property within the Hibernate runtime-metamodel.
+ *
+ * @author Steve Ebersole
+ */
+public class VersionProperty extends StandardProperty {
+
+ private final VersionValue unsavedValue;
+
+ /**
+ * Constructs VersionProperty instances.
+ *
+ * @param name The name by which the property can be referenced within
+ * its owner.
+ * @param node The node name to use for XML-based representation of this
+ * property.
+ * @param type The Hibernate Type of this property.
+ * @param lazy Should this property be handled lazily?
+ * @param insertable Is this property an insertable value?
+ * @param updateable Is this property an updateable value?
+ * @param insertGenerated Is this property generated in the database on insert?
+ * @param updateGenerated Is this property generated in the database on update?
+ * @param nullable Is this property a nullable value?
+ * @param checkable Is this property a checkable value?
+ * @param versionable Is this property a versionable value?
+ * @param cascadeStyle The cascade style for this property's value.
+ * @param unsavedValue The value which, if found as the value of
+ * this (i.e., the version) property, represents new (i.e., un-saved)
+ * instances of the owning entity.
+ */
+ public VersionProperty(
+ String name,
+ String node,
+ Type type,
+ boolean lazy,
+ boolean insertable,
+ boolean updateable,
+ boolean insertGenerated,
+ boolean updateGenerated,
+ boolean nullable,
+ boolean checkable,
+ boolean versionable,
+ CascadeStyle cascadeStyle,
+ VersionValue unsavedValue) {
+ super( name, node, type, lazy, insertable, updateable, insertGenerated, updateGenerated, nullable, checkable, versionable, cascadeStyle, null );
+ this.unsavedValue = unsavedValue;
+ }
+
+ public VersionValue getUnsavedValue() {
+ return unsavedValue;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/component/AbstractComponentTuplizer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/component/AbstractComponentTuplizer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/component/AbstractComponentTuplizer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,108 @@
+//$Id: AbstractComponentTuplizer.java 9210 2006-02-03 22:15:19Z steveebersole $
+package org.hibernate.tuple.component;
+
+import java.lang.reflect.Method;
+import java.util.Iterator;
+
+import org.hibernate.HibernateException;
+import org.hibernate.tuple.Instantiator;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.mapping.Component;
+import org.hibernate.mapping.Property;
+import org.hibernate.property.Getter;
+import org.hibernate.property.Setter;
+
+/**
+ * Support for tuplizers relating to components.
+ *
+ * @author Gavin King
+ * @author Steve Ebersole
+ */
+public abstract class AbstractComponentTuplizer implements ComponentTuplizer {
+
+ protected final Getter[] getters;
+ protected final Setter[] setters;
+ protected final int propertySpan;
+ protected final Instantiator instantiator;
+ protected final boolean hasCustomAccessors;
+
+ protected abstract Instantiator buildInstantiator(Component component);
+ protected abstract Getter buildGetter(Component component, Property prop);
+ protected abstract Setter buildSetter(Component component, Property prop);
+
+ protected AbstractComponentTuplizer(Component component) {
+ propertySpan = component.getPropertySpan();
+ getters = new Getter[propertySpan];
+ setters = new Setter[propertySpan];
+
+ Iterator iter = component.getPropertyIterator();
+ boolean foundCustomAccessor=false;
+ int i = 0;
+ while ( iter.hasNext() ) {
+ Property prop = ( Property ) iter.next();
+ getters[i] = buildGetter( component, prop );
+ setters[i] = buildSetter( component, prop );
+ if ( !prop.isBasicPropertyAccessor() ) {
+ foundCustomAccessor = true;
+ }
+ i++;
+ }
+ hasCustomAccessors = foundCustomAccessor;
+
+ String[] getterNames = new String[propertySpan];
+ String[] setterNames = new String[propertySpan];
+ Class[] propTypes = new Class[propertySpan];
+ for ( int j = 0; j < propertySpan; j++ ) {
+ getterNames[j] = getters[j].getMethodName();
+ setterNames[j] = setters[j].getMethodName();
+ propTypes[j] = getters[j].getReturnType();
+ }
+ instantiator = buildInstantiator( component );
+ }
+
+ public Object getPropertyValue(Object component, int i) throws HibernateException {
+ return getters[i].get( component );
+ }
+
+ public Object[] getPropertyValues(Object component) throws HibernateException {
+ Object[] values = new Object[propertySpan];
+ for ( int i = 0; i < propertySpan; i++ ) {
+ values[i] = getPropertyValue( component, i );
+ }
+ return values;
+ }
+
+ public boolean isInstance(Object object) {
+ return instantiator.isInstance(object);
+ }
+
+ public void setPropertyValues(Object component, Object[] values) throws HibernateException {
+ for ( int i = 0; i < propertySpan; i++ ) {
+ setters[i].set( component, values[i], null );
+ }
+ }
+
+ /**
+ * This method does not populate the component parent
+ */
+ public Object instantiate() throws HibernateException {
+ return instantiator.instantiate();
+ }
+
+ public Object getParent(Object component) {
+ return null;
+ }
+
+ public boolean hasParentProperty() {
+ return false;
+ }
+
+ public boolean isMethodOf(Method method) {
+ return false;
+ }
+
+ public void setParent(Object component, Object parent, SessionFactoryImplementor factory) {
+ throw new UnsupportedOperationException();
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/component/ComponentEntityModeToTuplizerMapping.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/component/ComponentEntityModeToTuplizerMapping.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/component/ComponentEntityModeToTuplizerMapping.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,109 @@
+package org.hibernate.tuple.component;
+
+import org.hibernate.tuple.EntityModeToTuplizerMapping;
+import org.hibernate.tuple.Tuplizer;
+import org.hibernate.mapping.Component;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.util.ReflectHelper;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.io.Serializable;
+
+/**
+ * Handles mapping {@link EntityMode}s to {@link ComponentTuplizer}s.
+ * <p/>
+ * Most of the handling is really in the super class; here we just create
+ * the tuplizers and add them to the superclass
+ *
+ * @author Steve Ebersole
+ */
+class ComponentEntityModeToTuplizerMapping extends EntityModeToTuplizerMapping implements Serializable {
+
+ private static final Class[] COMPONENT_TUP_CTOR_SIG = new Class[] { Component.class };
+
+ public ComponentEntityModeToTuplizerMapping(Component component) {
+ PersistentClass owner = component.getOwner();
+
+ // create our own copy of the user-supplied tuplizer impl map
+ Map userSuppliedTuplizerImpls = new HashMap();
+ if ( component.getTuplizerMap() != null ) {
+ userSuppliedTuplizerImpls.putAll( component.getTuplizerMap() );
+ }
+
+ // Build the dynamic-map tuplizer...
+ Tuplizer dynamicMapTuplizer = null;
+ String tuplizerImpl = ( String ) userSuppliedTuplizerImpls.remove( EntityMode.MAP );
+ if ( tuplizerImpl == null ) {
+ dynamicMapTuplizer = new DynamicMapComponentTuplizer( component );
+ }
+ else {
+ dynamicMapTuplizer = buildComponentTuplizer( tuplizerImpl, component );
+ }
+
+ // then the pojo tuplizer, using the dynamic-map tuplizer if no pojo representation is available
+ tuplizerImpl = ( String ) userSuppliedTuplizerImpls.remove( EntityMode.POJO );
+ Tuplizer pojoTuplizer = null;
+ if ( owner.hasPojoRepresentation() && component.hasPojoRepresentation() ) {
+ if ( tuplizerImpl == null ) {
+ pojoTuplizer = new PojoComponentTuplizer( component );
+ }
+ else {
+ pojoTuplizer = buildComponentTuplizer( tuplizerImpl, component );
+ }
+ }
+ else {
+ pojoTuplizer = dynamicMapTuplizer;
+ }
+
+ // then dom4j tuplizer, if dom4j representation is available
+ Tuplizer dom4jTuplizer = null;
+ tuplizerImpl = ( String ) userSuppliedTuplizerImpls.remove( EntityMode.DOM4J );
+ if ( owner.hasDom4jRepresentation() ) {
+ if ( tuplizerImpl == null ) {
+ dom4jTuplizer = new Dom4jComponentTuplizer( component );
+ }
+ else {
+ dom4jTuplizer = buildComponentTuplizer( tuplizerImpl, component );
+ }
+ }
+ else {
+ dom4jTuplizer = null;
+ }
+
+ // put the "standard" tuplizers into the tuplizer map first
+ if ( pojoTuplizer != null ) {
+ addTuplizer( EntityMode.POJO, pojoTuplizer );
+ }
+ if ( dynamicMapTuplizer != null ) {
+ addTuplizer( EntityMode.MAP, dynamicMapTuplizer );
+ }
+ if ( dom4jTuplizer != null ) {
+ addTuplizer( EntityMode.DOM4J, dom4jTuplizer );
+ }
+
+ // then handle any user-defined entity modes...
+ if ( !userSuppliedTuplizerImpls.isEmpty() ) {
+ Iterator itr = userSuppliedTuplizerImpls.entrySet().iterator();
+ while ( itr.hasNext() ) {
+ Map.Entry entry = ( Map.Entry ) itr.next();
+ EntityMode entityMode = ( EntityMode ) entry.getKey();
+ ComponentTuplizer tuplizer = buildComponentTuplizer( ( String ) entry.getValue(), component );
+ addTuplizer( entityMode, tuplizer );
+ }
+ }
+ }
+
+ private ComponentTuplizer buildComponentTuplizer(String tuplizerImpl, Component component) {
+ try {
+ Class implClass = ReflectHelper.classForName( tuplizerImpl );
+ return ( ComponentTuplizer ) implClass.getConstructor( COMPONENT_TUP_CTOR_SIG ).newInstance( new Object[] { component } );
+ }
+ catch( Throwable t ) {
+ throw new HibernateException( "Could not build tuplizer [" + tuplizerImpl + "]", t );
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/component/ComponentMetamodel.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/component/ComponentMetamodel.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/component/ComponentMetamodel.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,88 @@
+package org.hibernate.tuple.component;
+
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.mapping.Component;
+import org.hibernate.mapping.Property;
+import org.hibernate.HibernateException;
+import org.hibernate.tuple.StandardProperty;
+import org.hibernate.tuple.PropertyFactory;
+
+import java.io.Serializable;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * Centralizes metamodel information about a component.
+ *
+ * @author Steve Ebersole
+ */
+public class ComponentMetamodel implements Serializable {
+
+ // TODO : will need reference to session factory to fully complete HHH-1907
+
+// private final SessionFactoryImplementor sessionFactory;
+ private final String role;
+ private final boolean isKey;
+ private final StandardProperty[] properties;
+ private final ComponentEntityModeToTuplizerMapping tuplizerMapping;
+
+ // cached for efficiency...
+ private final int propertySpan;
+ private final Map propertyIndexes = new HashMap();
+
+// public ComponentMetamodel(Component component, SessionFactoryImplementor sessionFactory) {
+ public ComponentMetamodel(Component component) {
+// this.sessionFactory = sessionFactory;
+ this.role = component.getRoleName();
+ this.isKey = component.isKey();
+ propertySpan = component.getPropertySpan();
+ properties = new StandardProperty[propertySpan];
+ Iterator itr = component.getPropertyIterator();
+ int i = 0;
+ while ( itr.hasNext() ) {
+ Property property = ( Property ) itr.next();
+ properties[i] = PropertyFactory.buildStandardProperty( property, false );
+ propertyIndexes.put( property.getName(), new Integer( i ) );
+ i++;
+ }
+
+ tuplizerMapping = new ComponentEntityModeToTuplizerMapping( component );
+ }
+
+ public boolean isKey() {
+ return isKey;
+ }
+
+ public int getPropertySpan() {
+ return propertySpan;
+ }
+
+ public StandardProperty[] getProperties() {
+ return properties;
+ }
+
+ public StandardProperty getProperty(int index) {
+ if ( index < 0 || index >= propertySpan ) {
+ throw new IllegalArgumentException( "illegal index value for component property access [request=" + index + ", span=" + propertySpan + "]" );
+ }
+ return properties[index];
+ }
+
+ public int getPropertyIndex(String propertyName) {
+ Integer index = ( Integer ) propertyIndexes.get( propertyName );
+ if ( index == null ) {
+ throw new HibernateException( "component does not contain such a property [" + propertyName + "]" );
+ }
+ return index.intValue();
+ }
+
+ public StandardProperty getProperty(String propertyName) {
+ return getProperty( getPropertyIndex( propertyName ) );
+ }
+
+ public ComponentEntityModeToTuplizerMapping getTuplizerMapping() {
+ return tuplizerMapping;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/component/ComponentTuplizer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/component/ComponentTuplizer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/component/ComponentTuplizer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,53 @@
+//$Id: ComponentTuplizer.java 7451 2005-07-11 21:49:08Z steveebersole $
+package org.hibernate.tuple.component;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.tuple.Tuplizer;
+
+/**
+ * Defines further responsibilities reagarding tuplization based on
+ * a mapped components.
+ * </p>
+ * ComponentTuplizer implementations should have the following constructor signature:
+ * (org.hibernate.mapping.Component)
+ *
+ * @author Gavin King
+ * @author Steve Ebersole
+ */
+public interface ComponentTuplizer extends Tuplizer, Serializable {
+ /**
+ * Retreive the current value of the parent property.
+ *
+ * @param component The component instance from which to extract the parent
+ * property value.
+ * @return The current value of the parent property.
+ */
+ public Object getParent(Object component);
+
+ /**
+ * Set the value of the parent property.
+ *
+ * @param component The component instance on which to set the parent.
+ * @param parent The parent to be set on the comonent.
+ * @param factory The current session factory.
+ */
+ public void setParent(Object component, Object parent, SessionFactoryImplementor factory);
+
+ /**
+ * Does the component managed by this tuuplizer contain a parent property?
+ *
+ * @return True if the component does contain a parent property; false otherwise.
+ */
+ public boolean hasParentProperty();
+
+ /**
+ * Is the given method available via the managed component as a property getter?
+ *
+ * @param method The method which to check against the managed component.
+ * @return True if the managed component is available from the managed component; else false.
+ */
+ public boolean isMethodOf(Method method);
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/component/Dom4jComponentTuplizer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/component/Dom4jComponentTuplizer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/component/Dom4jComponentTuplizer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,49 @@
+//$Id: Dom4jComponentTuplizer.java 7449 2005-07-11 17:31:50Z steveebersole $
+package org.hibernate.tuple.component;
+
+import org.dom4j.Element;
+import org.hibernate.mapping.Component;
+import org.hibernate.mapping.Property;
+import org.hibernate.property.Getter;
+import org.hibernate.property.PropertyAccessor;
+import org.hibernate.property.PropertyAccessorFactory;
+import org.hibernate.property.Setter;
+import org.hibernate.tuple.Instantiator;
+import org.hibernate.tuple.Dom4jInstantiator;
+
+/**
+ * A {@link ComponentTuplizer} specific to the dom4j entity mode.
+ *
+ * @author Gavin King
+ * @author Steve Ebersole
+ */
+public class Dom4jComponentTuplizer extends AbstractComponentTuplizer {
+
+ public Class getMappedClass() {
+ return Element.class;
+ }
+
+ public Dom4jComponentTuplizer(Component component) {
+ super(component);
+ }
+
+ protected Instantiator buildInstantiator(Component component) {
+ return new Dom4jInstantiator( component );
+ }
+
+ private PropertyAccessor buildPropertyAccessor(Property property) {
+ //TODO: currently we don't know a SessionFactory reference when building the Tuplizer
+ // THIS IS A BUG (embedded-xml=false on component)
+ // TODO : fix this after HHH-1907 is complete
+ return PropertyAccessorFactory.getDom4jPropertyAccessor( property.getNodeName(), property.getType(), null );
+ }
+
+ protected Getter buildGetter(Component component, Property prop) {
+ return buildPropertyAccessor(prop).getGetter( null, prop.getName() );
+ }
+
+ protected Setter buildSetter(Component component, Property prop) {
+ return buildPropertyAccessor(prop).getSetter( null, prop.getName() );
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/component/DynamicMapComponentTuplizer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/component/DynamicMapComponentTuplizer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/component/DynamicMapComponentTuplizer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,47 @@
+//$Id: DynamicMapComponentTuplizer.java 7449 2005-07-11 17:31:50Z steveebersole $
+package org.hibernate.tuple.component;
+
+import java.util.Map;
+
+import org.hibernate.mapping.Component;
+import org.hibernate.mapping.Property;
+import org.hibernate.property.Getter;
+import org.hibernate.property.PropertyAccessor;
+import org.hibernate.property.PropertyAccessorFactory;
+import org.hibernate.property.Setter;
+import org.hibernate.tuple.Instantiator;
+import org.hibernate.tuple.DynamicMapInstantiator;
+
+/**
+ * A {@link ComponentTuplizer} specific to the dynamic-map entity mode.
+ *
+ * @author Gavin King
+ * @author Steve Ebersole
+ */
+public class DynamicMapComponentTuplizer extends AbstractComponentTuplizer {
+
+ public Class getMappedClass() {
+ return Map.class;
+ }
+
+ protected Instantiator buildInstantiator(Component component) {
+ return new DynamicMapInstantiator();
+ }
+
+ public DynamicMapComponentTuplizer(Component component) {
+ super(component);
+ }
+
+ private PropertyAccessor buildPropertyAccessor(Property property) {
+ return PropertyAccessorFactory.getDynamicMapPropertyAccessor();
+ }
+
+ protected Getter buildGetter(Component component, Property prop) {
+ return buildPropertyAccessor(prop).getGetter( null, prop.getName() );
+ }
+
+ protected Setter buildSetter(Component component, Property prop) {
+ return buildPropertyAccessor(prop).getSetter( null, prop.getName() );
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/component/PojoComponentTuplizer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/component/PojoComponentTuplizer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/component/PojoComponentTuplizer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,167 @@
+//$Id: PojoComponentTuplizer.java 9619 2006-03-15 00:12:47Z steve.ebersole at jboss.com $
+package org.hibernate.tuple.component;
+
+import java.lang.reflect.Method;
+import java.io.Serializable;
+
+import org.hibernate.HibernateException;
+import org.hibernate.AssertionFailure;
+import org.hibernate.tuple.component.AbstractComponentTuplizer;
+import org.hibernate.tuple.Instantiator;
+import org.hibernate.tuple.PojoInstantiator;
+import org.hibernate.util.ReflectHelper;
+import org.hibernate.bytecode.ReflectionOptimizer;
+import org.hibernate.bytecode.BasicProxyFactory;
+import org.hibernate.cfg.Environment;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.mapping.Component;
+import org.hibernate.mapping.Property;
+import org.hibernate.property.Getter;
+import org.hibernate.property.PropertyAccessor;
+import org.hibernate.property.PropertyAccessorFactory;
+import org.hibernate.property.Setter;
+
+/**
+ * A {@link ComponentTuplizer} specific to the pojo entity mode.
+ *
+ * @author Gavin King
+ * @author Steve Ebersole
+ */
+public class PojoComponentTuplizer extends AbstractComponentTuplizer {
+
+ private final Class componentClass;
+ private ReflectionOptimizer optimizer;
+ private final Getter parentGetter;
+ private final Setter parentSetter;
+
+ public PojoComponentTuplizer(Component component) {
+ super( component );
+
+ this.componentClass = component.getComponentClass();
+
+ String[] getterNames = new String[propertySpan];
+ String[] setterNames = new String[propertySpan];
+ Class[] propTypes = new Class[propertySpan];
+ for ( int i = 0; i < propertySpan; i++ ) {
+ getterNames[i] = getters[i].getMethodName();
+ setterNames[i] = setters[i].getMethodName();
+ propTypes[i] = getters[i].getReturnType();
+ }
+
+ final String parentPropertyName = component.getParentProperty();
+ if ( parentPropertyName == null ) {
+ parentSetter = null;
+ parentGetter = null;
+ }
+ else {
+ PropertyAccessor pa = PropertyAccessorFactory.getPropertyAccessor( null );
+ parentSetter = pa.getSetter( componentClass, parentPropertyName );
+ parentGetter = pa.getGetter( componentClass, parentPropertyName );
+ }
+
+ if ( hasCustomAccessors || !Environment.useReflectionOptimizer() ) {
+ optimizer = null;
+ }
+ else {
+ // TODO: here is why we need to make bytecode provider global :(
+ // TODO : again, fix this after HHH-1907 is complete
+ optimizer = Environment.getBytecodeProvider().getReflectionOptimizer(
+ componentClass, getterNames, setterNames, propTypes
+ );
+ }
+ }
+
+ public Class getMappedClass() {
+ return componentClass;
+ }
+
+ public Object[] getPropertyValues(Object component) throws HibernateException {
+ if ( optimizer != null && optimizer.getAccessOptimizer() != null ) {
+ return optimizer.getAccessOptimizer().getPropertyValues( component );
+ }
+ else {
+ return super.getPropertyValues(component);
+ }
+ }
+
+ public void setPropertyValues(Object component, Object[] values) throws HibernateException {
+ if ( optimizer != null && optimizer.getAccessOptimizer() != null ) {
+ optimizer.getAccessOptimizer().setPropertyValues( component, values );
+ }
+ else {
+ super.setPropertyValues(component, values);
+ }
+
+ }
+
+ public Object getParent(Object component) {
+ return parentGetter.get( component );
+ }
+
+ public boolean hasParentProperty() {
+ return parentGetter!=null;
+ }
+
+ public boolean isMethodOf(Method method) {
+ for ( int i=0; i<propertySpan; i++ ) {
+ final Method getterMethod = getters[i].getMethod();
+ if ( getterMethod!=null && getterMethod.equals(method) ) return true;
+ }
+ return false;
+ }
+
+ public void setParent(Object component, Object parent, SessionFactoryImplementor factory) {
+ parentSetter.set(component, parent, factory);
+ }
+
+ protected Instantiator buildInstantiator(Component component) {
+ if ( component.isEmbedded() && ReflectHelper.isAbstractClass( component.getComponentClass() ) ) {
+ return new ProxiedInstantiator( component );
+ }
+ if ( optimizer == null ) {
+ return new PojoInstantiator( component, null );
+ }
+ else {
+ return new PojoInstantiator( component, optimizer.getInstantiationOptimizer() );
+ }
+ }
+
+ protected Getter buildGetter(Component component, Property prop) {
+ return prop.getGetter( component.getComponentClass() );
+ }
+
+ protected Setter buildSetter(Component component, Property prop) {
+ return prop.getSetter( component.getComponentClass() );
+ }
+
+ private static class ProxiedInstantiator implements Instantiator {
+ private final Class proxiedClass;
+ private final BasicProxyFactory factory;
+
+ public ProxiedInstantiator(Component component) {
+ proxiedClass = component.getComponentClass();
+ if ( proxiedClass.isInterface() ) {
+ factory = Environment.getBytecodeProvider()
+ .getProxyFactoryFactory()
+ .buildBasicProxyFactory( null, new Class[] { proxiedClass } );
+ }
+ else {
+ factory = Environment.getBytecodeProvider()
+ .getProxyFactoryFactory()
+ .buildBasicProxyFactory( proxiedClass, null );
+ }
+ }
+
+ public Object instantiate(Serializable id) {
+ throw new AssertionFailure( "ProxiedInstantiator can only be used to instantiate component" );
+ }
+
+ public Object instantiate() {
+ return factory.getProxy();
+ }
+
+ public boolean isInstance(Object object) {
+ return proxiedClass.isInstance( object );
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/entity/AbstractEntityTuplizer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/entity/AbstractEntityTuplizer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/entity/AbstractEntityTuplizer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,410 @@
+// $Id: AbstractEntityTuplizer.java 7516 2005-07-16 22:20:48Z oneovthafew $
+package org.hibernate.tuple.entity;
+
+import java.io.Serializable;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.tuple.Instantiator;
+import org.hibernate.tuple.VersionProperty;
+import org.hibernate.tuple.StandardProperty;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.id.Assigned;
+import org.hibernate.intercept.LazyPropertyInitializer;
+import org.hibernate.mapping.Component;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Property;
+import org.hibernate.property.Getter;
+import org.hibernate.property.Setter;
+import org.hibernate.proxy.ProxyFactory;
+import org.hibernate.type.AbstractComponentType;
+import org.hibernate.type.ComponentType;
+
+
+/**
+ * Support for tuplizers relating to entities.
+ *
+ * @author Steve Ebersole
+ * @author Gavin King
+ */
+public abstract class AbstractEntityTuplizer implements EntityTuplizer {
+
+ //TODO: currently keeps Getters and Setters (instead of PropertyAccessors) because of the way getGetter() and getSetter() are implemented currently; yuck!
+
+ private final EntityMetamodel entityMetamodel;
+
+ private final Getter idGetter;
+ private final Setter idSetter;
+
+ protected final Getter[] getters;
+ protected final Setter[] setters;
+ protected final int propertySpan;
+ protected final boolean hasCustomAccessors;
+ private final Instantiator instantiator;
+ private final ProxyFactory proxyFactory;
+ private final AbstractComponentType identifierMapperType;
+
+
+ /**
+ * Return the entity-mode handled by this tuplizer instance.
+ *
+ * @return The entity-mode
+ */
+ protected abstract EntityMode getEntityMode();
+
+ /**
+ * Build an appropriate Getter for the given property.
+ *
+ * @param mappedProperty The property to be accessed via the built Getter.
+ * @param mappedEntity The entity information regarding the mapped entity owning this property.
+ * @return An appropriate Getter instance.
+ */
+ protected abstract Getter buildPropertyGetter(Property mappedProperty, PersistentClass mappedEntity);
+
+ /**
+ * Build an appropriate Setter for the given property.
+ *
+ * @param mappedProperty The property to be accessed via the built Setter.
+ * @param mappedEntity The entity information regarding the mapped entity owning this property.
+ * @return An appropriate Setter instance.
+ */
+ protected abstract Setter buildPropertySetter(Property mappedProperty, PersistentClass mappedEntity);
+
+ /**
+ * Build an appropriate Instantiator for the given mapped entity.
+ *
+ * @param mappingInfo The mapping information regarding the mapped entity.
+ * @return An appropriate Instantiator instance.
+ */
+ protected abstract Instantiator buildInstantiator(PersistentClass mappingInfo);
+
+ /**
+ * Build an appropriate ProxyFactory for the given mapped entity.
+ *
+ * @param mappingInfo The mapping information regarding the mapped entity.
+ * @param idGetter The constructed Getter relating to the entity's id property.
+ * @param idSetter The constructed Setter relating to the entity's id property.
+ * @return An appropriate ProxyFactory instance.
+ */
+ protected abstract ProxyFactory buildProxyFactory(PersistentClass mappingInfo, Getter idGetter, Setter idSetter);
+
+ /**
+ * Constructs a new AbstractEntityTuplizer instance.
+ *
+ * @param entityMetamodel The "interpreted" information relating to the mapped entity.
+ * @param mappingInfo The parsed "raw" mapping data relating to the given entity.
+ */
+ public AbstractEntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappingInfo) {
+ this.entityMetamodel = entityMetamodel;
+
+ if ( !entityMetamodel.getIdentifierProperty().isVirtual() ) {
+ idGetter = buildPropertyGetter( mappingInfo.getIdentifierProperty(), mappingInfo );
+ idSetter = buildPropertySetter( mappingInfo.getIdentifierProperty(), mappingInfo );
+ }
+ else {
+ idGetter = null;
+ idSetter = null;
+ }
+
+ propertySpan = entityMetamodel.getPropertySpan();
+
+ getters = new Getter[propertySpan];
+ setters = new Setter[propertySpan];
+
+ Iterator iter = mappingInfo.getPropertyClosureIterator();
+ boolean foundCustomAccessor=false;
+ int i=0;
+ while ( iter.hasNext() ) {
+ //TODO: redesign how PropertyAccessors are acquired...
+ Property property = (Property) iter.next();
+ getters[i] = buildPropertyGetter(property, mappingInfo);
+ setters[i] = buildPropertySetter(property, mappingInfo);
+ if ( !property.isBasicPropertyAccessor() ) foundCustomAccessor = true;
+ i++;
+ }
+ hasCustomAccessors = foundCustomAccessor;
+
+ instantiator = buildInstantiator( mappingInfo );
+
+ if ( entityMetamodel.isLazy() ) {
+ proxyFactory = buildProxyFactory( mappingInfo, idGetter, idSetter );
+ if (proxyFactory == null) {
+ entityMetamodel.setLazy( false );
+ }
+ }
+ else {
+ proxyFactory = null;
+ }
+
+ Component mapper = mappingInfo.getIdentifierMapper();
+ identifierMapperType = mapper==null ? null : (AbstractComponentType) mapper.getType();
+ }
+
+ /** Retreives the defined entity-name for the tuplized entity.
+ *
+ * @return The entity-name.
+ */
+ protected String getEntityName() {
+ return entityMetamodel.getName();
+ }
+
+ /**
+ * Retreives the defined entity-names for any subclasses defined for this
+ * entity.
+ *
+ * @return Any subclass entity-names.
+ */
+ protected Set getSubclassEntityNames() {
+ return entityMetamodel.getSubclassEntityNames();
+ }
+
+ public Serializable getIdentifier(Object entity) throws HibernateException {
+ final Object id;
+ if ( entityMetamodel.getIdentifierProperty().isEmbedded() ) {
+ id = entity;
+ }
+ else {
+ if ( idGetter == null ) {
+ if (identifierMapperType==null) {
+ throw new HibernateException( "The class has no identifier property: " + getEntityName() );
+ }
+ else {
+ ComponentType copier = (ComponentType) entityMetamodel.getIdentifierProperty().getType();
+ id = copier.instantiate( getEntityMode() );
+ copier.setPropertyValues( id, identifierMapperType.getPropertyValues( entity, getEntityMode() ), getEntityMode() );
+ }
+ }
+ else {
+ id = idGetter.get( entity );
+ }
+ }
+
+ try {
+ return (Serializable) id;
+ }
+ catch ( ClassCastException cce ) {
+ StringBuffer msg = new StringBuffer( "Identifier classes must be serializable. " );
+ if ( id != null ) {
+ msg.append( id.getClass().getName() + " is not serializable. " );
+ }
+ if ( cce.getMessage() != null ) {
+ msg.append( cce.getMessage() );
+ }
+ throw new ClassCastException( msg.toString() );
+ }
+ }
+
+
+ public void setIdentifier(Object entity, Serializable id) throws HibernateException {
+ if ( entityMetamodel.getIdentifierProperty().isEmbedded() ) {
+ if ( entity != id ) {
+ AbstractComponentType copier = (AbstractComponentType) entityMetamodel.getIdentifierProperty().getType();
+ copier.setPropertyValues( entity, copier.getPropertyValues( id, getEntityMode() ), getEntityMode() );
+ }
+ }
+ else if ( idSetter != null ) {
+ idSetter.set( entity, id, getFactory() );
+ }
+ }
+
+ public void resetIdentifier(Object entity, Serializable currentId, Object currentVersion) {
+ if ( entityMetamodel.getIdentifierProperty().getIdentifierGenerator() instanceof Assigned ) {
+ //return currentId;
+ }
+ else {
+ //reset the id
+ Serializable result = entityMetamodel.getIdentifierProperty()
+ .getUnsavedValue()
+ .getDefaultValue( currentId );
+ setIdentifier( entity, result );
+ //reset the version
+ VersionProperty versionProperty = entityMetamodel.getVersionProperty();
+ if ( entityMetamodel.isVersioned() ) {
+ setPropertyValue(
+ entity,
+ entityMetamodel.getVersionPropertyIndex(),
+ versionProperty.getUnsavedValue().getDefaultValue( currentVersion )
+ );
+ }
+ //return the id, so we can use it to reset the proxy id
+ //return result;
+ }
+ }
+
+ public Object getVersion(Object entity) throws HibernateException {
+ if ( !entityMetamodel.isVersioned() ) return null;
+ return getters[ entityMetamodel.getVersionPropertyIndex() ].get( entity );
+ }
+
+ protected boolean shouldGetAllProperties(Object entity) {
+ return !hasUninitializedLazyProperties( entity );
+ }
+
+ public Object[] getPropertyValues(Object entity) throws HibernateException {
+ boolean getAll = shouldGetAllProperties( entity );
+ final int span = entityMetamodel.getPropertySpan();
+ final Object[] result = new Object[span];
+
+ for ( int j = 0; j < span; j++ ) {
+ StandardProperty property = entityMetamodel.getProperties()[j];
+ if ( getAll || !property.isLazy() ) {
+ result[j] = getters[j].get( entity );
+ }
+ else {
+ result[j] = LazyPropertyInitializer.UNFETCHED_PROPERTY;
+ }
+ }
+ return result;
+ }
+
+ public Object[] getPropertyValuesToInsert(Object entity, Map mergeMap, SessionImplementor session)
+ throws HibernateException {
+ final int span = entityMetamodel.getPropertySpan();
+ final Object[] result = new Object[span];
+
+ for ( int j = 0; j < span; j++ ) {
+ result[j] = getters[j].getForInsert( entity, mergeMap, session );
+ }
+ return result;
+ }
+
+ public Object getPropertyValue(Object entity, int i) throws HibernateException {
+ return getters[i].get( entity );
+ }
+
+ public Object getPropertyValue(Object entity, String propertyPath) throws HibernateException {
+
+ int loc = propertyPath.indexOf('.');
+ String basePropertyName = loc>0 ?
+ propertyPath.substring(0, loc) : propertyPath;
+
+ int index = entityMetamodel.getPropertyIndex( basePropertyName );
+ Object baseValue = getPropertyValue( entity, index );
+ if ( loc>0 ) {
+ ComponentType type = (ComponentType) entityMetamodel.getPropertyTypes()[index];
+ return getComponentValue( type, baseValue, propertyPath.substring(loc+1) );
+ }
+ else {
+ return baseValue;
+ }
+ }
+
+ /**
+ * Extract a component property value.
+ *
+ * @param type The component property types.
+ * @param component The component instance itself.
+ * @param propertyPath The property path for the property to be extracted.
+ * @return The property value extracted.
+ */
+ protected Object getComponentValue(ComponentType type, Object component, String propertyPath) {
+
+ int loc = propertyPath.indexOf('.');
+ String basePropertyName = loc>0 ?
+ propertyPath.substring(0, loc) : propertyPath;
+
+ String[] propertyNames = type.getPropertyNames();
+ int index=0;
+ for ( ; index<propertyNames.length; index++ ) {
+ if ( basePropertyName.equals( propertyNames[index] ) ) break;
+ }
+ if (index==propertyNames.length) {
+ throw new MappingException( "component property not found: " + basePropertyName );
+ }
+
+ Object baseValue = type.getPropertyValue( component, index, getEntityMode() );
+
+ if ( loc>0 ) {
+ ComponentType subtype = (ComponentType) type.getSubtypes()[index];
+ return getComponentValue( subtype, baseValue, propertyPath.substring(loc+1) );
+ }
+ else {
+ return baseValue;
+ }
+
+ }
+
+ public void setPropertyValues(Object entity, Object[] values) throws HibernateException {
+ boolean setAll = !entityMetamodel.hasLazyProperties();
+
+ for ( int j = 0; j < entityMetamodel.getPropertySpan(); j++ ) {
+ if ( setAll || values[j] != LazyPropertyInitializer.UNFETCHED_PROPERTY ) {
+ setters[j].set( entity, values[j], getFactory() );
+ }
+ }
+ }
+
+ public void setPropertyValue(Object entity, int i, Object value) throws HibernateException {
+ setters[i].set( entity, value, getFactory() );
+ }
+
+ public void setPropertyValue(Object entity, String propertyName, Object value) throws HibernateException {
+ setters[ entityMetamodel.getPropertyIndex( propertyName ) ].set( entity, value, getFactory() );
+ }
+
+ public final Object instantiate(Serializable id) throws HibernateException {
+ Object result = getInstantiator().instantiate( id );
+ if ( id != null ) {
+ setIdentifier( result, id );
+ }
+ return result;
+ }
+
+ public final Object instantiate() throws HibernateException {
+ return instantiate( null );
+ }
+
+ public void afterInitialize(Object entity, boolean lazyPropertiesAreUnfetched, SessionImplementor session) {}
+
+ public boolean hasUninitializedLazyProperties(Object entity) {
+ // the default is to simply not lazy fetch properties for now...
+ return false;
+ }
+
+ public final boolean isInstance(Object object) {
+ return getInstantiator().isInstance( object );
+ }
+
+ public boolean hasProxy() {
+ return entityMetamodel.isLazy();
+ }
+
+ public final Object createProxy(Serializable id, SessionImplementor session)
+ throws HibernateException {
+ return getProxyFactory().getProxy( id, session );
+ }
+
+ public boolean isLifecycleImplementor() {
+ return false;
+ }
+
+ public boolean isValidatableImplementor() {
+ return false;
+ }
+
+ protected final EntityMetamodel getEntityMetamodel() {
+ return entityMetamodel;
+ }
+
+ protected final SessionFactoryImplementor getFactory() {
+ return entityMetamodel.getSessionFactory();
+ }
+
+ protected final Instantiator getInstantiator() {
+ return instantiator;
+ }
+
+ protected final ProxyFactory getProxyFactory() {
+ return proxyFactory;
+ }
+
+ public String toString() {
+ return getClass().getName() + '(' + getEntityMetamodel().getName() + ')';
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/entity/Dom4jEntityTuplizer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/entity/Dom4jEntityTuplizer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/entity/Dom4jEntityTuplizer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,123 @@
+// $Id: Dom4jEntityTuplizer.java 9210 2006-02-03 22:15:19Z steveebersole $
+package org.hibernate.tuple.entity;
+
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Property;
+import org.hibernate.proxy.ProxyFactory;
+import org.hibernate.proxy.HibernateProxy;
+import org.hibernate.proxy.dom4j.Dom4jProxyFactory;
+import org.hibernate.property.PropertyAccessor;
+import org.hibernate.property.PropertyAccessorFactory;
+import org.hibernate.property.Getter;
+import org.hibernate.property.Setter;
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.tuple.Instantiator;
+import org.hibernate.tuple.Dom4jInstantiator;
+import org.hibernate.type.AbstractComponentType;
+import org.dom4j.Element;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Iterator;
+
+/**
+ * An {@link EntityTuplizer} specific to the dom4j entity mode.
+ *
+ * @author Steve Ebersole
+ * @author Gavin King
+ */
+public class Dom4jEntityTuplizer extends AbstractEntityTuplizer {
+
+ static final Log log = LogFactory.getLog( Dom4jEntityTuplizer.class );
+
+ private Set subclassNodeNames = new HashSet();
+
+ Dom4jEntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappedEntity) {
+ super(entityMetamodel, mappedEntity);
+ Iterator itr = mappedEntity.getSubclassClosureIterator();
+ while( itr.hasNext() ) {
+ final PersistentClass mapping = ( PersistentClass ) itr.next();
+ subclassNodeNames.add( mapping.getNodeName() );
+ }
+ }
+
+ public EntityMode getEntityMode() {
+ return EntityMode.DOM4J;
+ }
+
+ private PropertyAccessor buildPropertyAccessor(Property mappedProperty) {
+ if ( mappedProperty.isBackRef() ) {
+ return mappedProperty.getPropertyAccessor(null);
+ }
+ else {
+ return PropertyAccessorFactory.getDom4jPropertyAccessor(
+ mappedProperty.getNodeName(),
+ mappedProperty.getType(),
+ getEntityMetamodel().getSessionFactory()
+ );
+ }
+ }
+
+ protected Getter buildPropertyGetter(Property mappedProperty, PersistentClass mappedEntity) {
+ return buildPropertyAccessor(mappedProperty).getGetter( null, mappedProperty.getName() );
+ }
+
+ protected Setter buildPropertySetter(Property mappedProperty, PersistentClass mappedEntity) {
+ return buildPropertyAccessor(mappedProperty).getSetter( null, mappedProperty.getName() );
+ }
+
+ protected Instantiator buildInstantiator(PersistentClass persistentClass) {
+ return new Dom4jInstantiator( persistentClass );
+ }
+
+ public Serializable getIdentifier(Object entityOrId) throws HibernateException {
+ if (entityOrId instanceof Element) {
+ return super.getIdentifier(entityOrId);
+ }
+ else {
+ //it was not embedded, so the argument is just an id
+ return (Serializable) entityOrId;
+ }
+ }
+
+ protected ProxyFactory buildProxyFactory(PersistentClass mappingInfo, Getter idGetter, Setter idSetter) {
+ HashSet proxyInterfaces = new HashSet();
+ proxyInterfaces.add( HibernateProxy.class );
+ proxyInterfaces.add( Element.class );
+
+ ProxyFactory pf = new Dom4jProxyFactory();
+ try {
+ pf.postInstantiate(
+ getEntityName(),
+ Element.class,
+ proxyInterfaces,
+ null,
+ null,
+ mappingInfo.hasEmbeddedIdentifier() ?
+ (AbstractComponentType) mappingInfo.getIdentifier().getType() :
+ null
+ );
+ }
+ catch ( HibernateException he ) {
+ log.warn( "could not create proxy factory for:" + getEntityName(), he );
+ pf = null;
+ }
+ return pf;
+ }
+
+ public Class getMappedClass() {
+ return Element.class;
+ }
+
+ public Class getConcreteProxyClass() {
+ return Element.class;
+ }
+
+ public boolean isInstrumented() {
+ return false;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/entity/DynamicMapEntityTuplizer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/entity/DynamicMapEntityTuplizer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/entity/DynamicMapEntityTuplizer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,93 @@
+// $Id: DynamicMapEntityTuplizer.java 9210 2006-02-03 22:15:19Z steveebersole $
+package org.hibernate.tuple.entity;
+
+import java.util.Map;
+
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.tuple.entity.AbstractEntityTuplizer;
+import org.hibernate.tuple.Instantiator;
+import org.hibernate.tuple.DynamicMapInstantiator;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Property;
+import org.hibernate.property.Getter;
+import org.hibernate.property.PropertyAccessor;
+import org.hibernate.property.PropertyAccessorFactory;
+import org.hibernate.property.Setter;
+import org.hibernate.proxy.map.MapProxyFactory;
+import org.hibernate.proxy.ProxyFactory;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * An {@link EntityTuplizer} specific to the dynamic-map entity mode.
+ *
+ * @author Steve Ebersole
+ * @author Gavin King
+ */
+public class DynamicMapEntityTuplizer extends AbstractEntityTuplizer {
+
+ static final Log log = LogFactory.getLog( DynamicMapEntityTuplizer.class );
+
+ DynamicMapEntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappedEntity) {
+ super(entityMetamodel, mappedEntity);
+ }
+
+ public EntityMode getEntityMode() {
+ return EntityMode.MAP;
+ }
+
+ private PropertyAccessor buildPropertyAccessor(Property mappedProperty) {
+ if ( mappedProperty.isBackRef() ) {
+ return mappedProperty.getPropertyAccessor(null);
+ }
+ else {
+ return PropertyAccessorFactory.getDynamicMapPropertyAccessor();
+ }
+ }
+
+ protected Getter buildPropertyGetter(Property mappedProperty, PersistentClass mappedEntity) {
+ return buildPropertyAccessor(mappedProperty).getGetter( null, mappedProperty.getName() );
+ }
+
+ protected Setter buildPropertySetter(Property mappedProperty, PersistentClass mappedEntity) {
+ return buildPropertyAccessor(mappedProperty).getSetter( null, mappedProperty.getName() );
+ }
+
+ protected Instantiator buildInstantiator(PersistentClass mappingInfo) {
+ return new DynamicMapInstantiator( mappingInfo );
+ }
+
+ protected ProxyFactory buildProxyFactory(PersistentClass mappingInfo, Getter idGetter, Setter idSetter) {
+
+ ProxyFactory pf = new MapProxyFactory();
+ try {
+ //TODO: design new lifecycle for ProxyFactory
+ pf.postInstantiate(
+ getEntityName(),
+ null,
+ null,
+ null,
+ null,
+ null
+ );
+ }
+ catch ( HibernateException he ) {
+ log.warn( "could not create proxy factory for:" + getEntityName(), he );
+ pf = null;
+ }
+ return pf;
+ }
+
+ public Class getMappedClass() {
+ return Map.class;
+ }
+
+ public Class getConcreteProxyClass() {
+ return Map.class;
+ }
+
+ public boolean isInstrumented() {
+ return false;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/entity/EntityEntityModeToTuplizerMapping.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/entity/EntityEntityModeToTuplizerMapping.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/entity/EntityEntityModeToTuplizerMapping.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,113 @@
+package org.hibernate.tuple.entity;
+
+import org.hibernate.tuple.EntityModeToTuplizerMapping;
+import org.hibernate.tuple.Tuplizer;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.util.ReflectHelper;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.io.Serializable;
+
+/**
+ * Handles mapping {@link EntityMode}s to {@link EntityTuplizer}s.
+ * <p/>
+ * Most of the handling is really in the super class; here we just create
+ * the tuplizers and add them to the superclass
+ *
+ * @author Steve Ebersole
+ */
+public class EntityEntityModeToTuplizerMapping extends EntityModeToTuplizerMapping implements Serializable {
+
+ private static final Class[] ENTITY_TUP_CTOR_SIG = new Class[] { EntityMetamodel.class, PersistentClass.class };
+
+ /**
+ * Instantiates a EntityEntityModeToTuplizerMapping based on the given
+ * entity mapping and metamodel definitions.
+ *
+ * @param mappedEntity The entity mapping definition.
+ * @param em The entity metamodel definition.
+ */
+ public EntityEntityModeToTuplizerMapping(PersistentClass mappedEntity, EntityMetamodel em) {
+ // create our own copy of the user-supplied tuplizer impl map
+ Map userSuppliedTuplizerImpls = new HashMap();
+ if ( mappedEntity.getTuplizerMap() != null ) {
+ userSuppliedTuplizerImpls.putAll( mappedEntity.getTuplizerMap() );
+ }
+
+ // Build the dynamic-map tuplizer...
+ Tuplizer dynamicMapTuplizer = null;
+ String tuplizerImpl = ( String ) userSuppliedTuplizerImpls.remove( EntityMode.MAP );
+ if ( tuplizerImpl == null ) {
+ dynamicMapTuplizer = new DynamicMapEntityTuplizer( em, mappedEntity );
+ }
+ else {
+ dynamicMapTuplizer = buildEntityTuplizer( tuplizerImpl, mappedEntity, em );
+ }
+
+ // then the pojo tuplizer, using the dynamic-map tuplizer if no pojo representation is available
+ Tuplizer pojoTuplizer = null;
+ tuplizerImpl = ( String ) userSuppliedTuplizerImpls.remove( EntityMode.POJO );
+ if ( mappedEntity.hasPojoRepresentation() ) {
+ if ( tuplizerImpl == null ) {
+ pojoTuplizer = new PojoEntityTuplizer( em, mappedEntity );
+ }
+ else {
+ pojoTuplizer = buildEntityTuplizer( tuplizerImpl, mappedEntity, em );
+ }
+ }
+ else {
+ pojoTuplizer = dynamicMapTuplizer;
+ }
+
+ // then dom4j tuplizer, if dom4j representation is available
+ Tuplizer dom4jTuplizer = null;
+ tuplizerImpl = ( String ) userSuppliedTuplizerImpls.remove( EntityMode.DOM4J );
+ if ( mappedEntity.hasDom4jRepresentation() ) {
+ if ( tuplizerImpl == null ) {
+ dom4jTuplizer = new Dom4jEntityTuplizer( em, mappedEntity );
+ }
+ else {
+ dom4jTuplizer = buildEntityTuplizer( tuplizerImpl, mappedEntity, em );
+ }
+ }
+ else {
+ dom4jTuplizer = null;
+ }
+
+ // put the "standard" tuplizers into the tuplizer map first
+ if ( pojoTuplizer != null ) {
+ addTuplizer( EntityMode.POJO, pojoTuplizer );
+ }
+ if ( dynamicMapTuplizer != null ) {
+ addTuplizer( EntityMode.MAP, dynamicMapTuplizer );
+ }
+ if ( dom4jTuplizer != null ) {
+ addTuplizer( EntityMode.DOM4J, dom4jTuplizer );
+ }
+
+ // then handle any user-defined entity modes...
+ if ( !userSuppliedTuplizerImpls.isEmpty() ) {
+ Iterator itr = userSuppliedTuplizerImpls.entrySet().iterator();
+ while ( itr.hasNext() ) {
+ Map.Entry entry = ( Map.Entry ) itr.next();
+ EntityMode entityMode = ( EntityMode ) entry.getKey();
+ EntityTuplizer tuplizer = buildEntityTuplizer( ( String ) entry.getValue(), mappedEntity, em );
+ addTuplizer( entityMode, tuplizer );
+ }
+ }
+ }
+
+ private static EntityTuplizer buildEntityTuplizer(String className, PersistentClass pc, EntityMetamodel em) {
+ try {
+ Class implClass = ReflectHelper.classForName( className );
+ return ( EntityTuplizer ) implClass.getConstructor( ENTITY_TUP_CTOR_SIG ).newInstance( new Object[] { em, pc } );
+ }
+ catch( Throwable t ) {
+ throw new HibernateException( "Could not build tuplizer [" + className + "]", t );
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,586 @@
+// $Id: EntityMetamodel.java 9210 2006-02-03 22:15:19Z steveebersole $
+package org.hibernate.tuple.entity;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.tuple.IdentifierProperty;
+import org.hibernate.tuple.StandardProperty;
+import org.hibernate.tuple.PropertyFactory;
+import org.hibernate.tuple.VersionProperty;
+import org.hibernate.intercept.FieldInterceptionHelper;
+import org.hibernate.engine.CascadeStyle;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.Versioning;
+import org.hibernate.engine.ValueInclusion;
+import org.hibernate.mapping.Component;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Property;
+import org.hibernate.mapping.PropertyGeneration;
+import org.hibernate.type.AbstractComponentType;
+import org.hibernate.type.AssociationType;
+import org.hibernate.type.EntityType;
+import org.hibernate.type.Type;
+import org.hibernate.type.TypeFactory;
+import org.hibernate.util.ArrayHelper;
+import org.hibernate.util.ReflectHelper;
+
+/**
+ * Centralizes metamodel information about an entity.
+ *
+ * @author Steve Ebersole
+ */
+public class EntityMetamodel implements Serializable {
+
+ private static final Log log = LogFactory.getLog(EntityMetamodel.class);
+
+ private static final int NO_VERSION_INDX = -66;
+
+ private final SessionFactoryImplementor sessionFactory;
+
+ private final String name;
+ private final String rootName;
+ private final EntityType entityType;
+
+ private final IdentifierProperty identifierProperty;
+ private final boolean versioned;
+
+ private final int propertySpan;
+ private final int versionPropertyIndex;
+ private final StandardProperty[] properties;
+ // temporary ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ private final String[] propertyNames;
+ private final Type[] propertyTypes;
+ private final boolean[] propertyLaziness;
+ private final boolean[] propertyUpdateability;
+ private final boolean[] nonlazyPropertyUpdateability;
+ private final boolean[] propertyCheckability;
+ private final boolean[] propertyInsertability;
+ private final ValueInclusion[] insertInclusions;
+ private final ValueInclusion[] updateInclusions;
+ private final boolean[] propertyNullability;
+ private final boolean[] propertyVersionability;
+ private final CascadeStyle[] cascadeStyles;
+ private final boolean hasInsertGeneratedValues;
+ private final boolean hasUpdateGeneratedValues;
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ private final Map propertyIndexes = new HashMap();
+ private final boolean hasCollections;
+ private final boolean hasMutableProperties;
+ private final boolean hasLazyProperties;
+ private final boolean hasNonIdentifierPropertyNamedId;
+
+ private final int[] naturalIdPropertyNumbers;
+
+ private boolean lazy; //not final because proxy factory creation can fail
+ private final boolean hasCascades;
+ private final boolean mutable;
+ private final boolean isAbstract;
+ private final boolean selectBeforeUpdate;
+ private final boolean dynamicUpdate;
+ private final boolean dynamicInsert;
+ private final int optimisticLockMode;
+
+ private final boolean polymorphic;
+ private final String superclass; // superclass entity-name
+ private final boolean explicitPolymorphism;
+ private final boolean inherited;
+ private final boolean hasSubclasses;
+ private final Set subclassEntityNames = new HashSet();
+
+ private final EntityEntityModeToTuplizerMapping tuplizerMapping;
+
+ public EntityTuplizer getTuplizer(EntityMode entityMode) {
+ return (EntityTuplizer) tuplizerMapping.getTuplizer( entityMode );
+ }
+
+ public EntityTuplizer getTuplizerOrNull(EntityMode entityMode) {
+ return ( EntityTuplizer ) tuplizerMapping.getTuplizerOrNull( entityMode );
+ }
+
+ public EntityMode guessEntityMode(Object object) {
+ return tuplizerMapping.guessEntityMode( object );
+ }
+
+ public EntityMetamodel(PersistentClass persistentClass, SessionFactoryImplementor sessionFactory) {
+ this.sessionFactory = sessionFactory;
+
+ name = persistentClass.getEntityName();
+ rootName = persistentClass.getRootClass().getEntityName();
+ entityType = TypeFactory.manyToOne( name );
+
+ identifierProperty = PropertyFactory.buildIdentifierProperty(
+ persistentClass,
+ sessionFactory.getIdentifierGenerator( rootName )
+ );
+
+ versioned = persistentClass.isVersioned();
+
+ boolean lazyAvailable = persistentClass.hasPojoRepresentation() &&
+ FieldInterceptionHelper.isInstrumented( persistentClass.getMappedClass() );
+ boolean hasLazy = false;
+
+ propertySpan = persistentClass.getPropertyClosureSpan();
+ properties = new StandardProperty[propertySpan];
+ List naturalIdNumbers = new ArrayList();
+ // temporary ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ propertyNames = new String[propertySpan];
+ propertyTypes = new Type[propertySpan];
+ propertyUpdateability = new boolean[propertySpan];
+ propertyInsertability = new boolean[propertySpan];
+ insertInclusions = new ValueInclusion[propertySpan];
+ updateInclusions = new ValueInclusion[propertySpan];
+ nonlazyPropertyUpdateability = new boolean[propertySpan];
+ propertyCheckability = new boolean[propertySpan];
+ propertyNullability = new boolean[propertySpan];
+ propertyVersionability = new boolean[propertySpan];
+ propertyLaziness = new boolean[propertySpan];
+ cascadeStyles = new CascadeStyle[propertySpan];
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+ Iterator iter = persistentClass.getPropertyClosureIterator();
+ int i = 0;
+ int tempVersionProperty = NO_VERSION_INDX;
+ boolean foundCascade = false;
+ boolean foundCollection = false;
+ boolean foundMutable = false;
+ boolean foundNonIdentifierPropertyNamedId = false;
+ boolean foundInsertGeneratedValue = false;
+ boolean foundUpdateGeneratedValue = false;
+
+ while ( iter.hasNext() ) {
+ Property prop = ( Property ) iter.next();
+
+ if ( prop == persistentClass.getVersion() ) {
+ tempVersionProperty = i;
+ properties[i] = PropertyFactory.buildVersionProperty( prop, lazyAvailable );
+ }
+ else {
+ properties[i] = PropertyFactory.buildStandardProperty( prop, lazyAvailable );
+ }
+
+ if ( prop.isNaturalIdentifier() ) {
+ naturalIdNumbers.add( new Integer(i) );
+ }
+
+ if ( "id".equals( prop.getName() ) ) {
+ foundNonIdentifierPropertyNamedId = true;
+ }
+
+ // temporary ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ boolean lazy = prop.isLazy() && lazyAvailable;
+ if ( lazy ) hasLazy = true;
+ propertyLaziness[i] = lazy;
+
+ propertyNames[i] = properties[i].getName();
+ propertyTypes[i] = properties[i].getType();
+ propertyNullability[i] = properties[i].isNullable();
+ propertyUpdateability[i] = properties[i].isUpdateable();
+ propertyInsertability[i] = properties[i].isInsertable();
+ insertInclusions[i] = determineInsertValueGenerationType( prop, properties[i] );
+ updateInclusions[i] = determineUpdateValueGenerationType( prop, properties[i] );
+ propertyVersionability[i] = properties[i].isVersionable();
+ nonlazyPropertyUpdateability[i] = properties[i].isUpdateable() && !lazy;
+ propertyCheckability[i] = propertyUpdateability[i] ||
+ ( propertyTypes[i].isAssociationType() && ( (AssociationType) propertyTypes[i] ).isAlwaysDirtyChecked() );
+
+ cascadeStyles[i] = properties[i].getCascadeStyle();
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ if ( properties[i].isLazy() ) {
+ hasLazy = true;
+ }
+
+ if ( properties[i].getCascadeStyle() != CascadeStyle.NONE ) {
+ foundCascade = true;
+ }
+
+ if ( indicatesCollection( properties[i].getType() ) ) {
+ foundCollection = true;
+ }
+
+ if ( propertyTypes[i].isMutable() && propertyCheckability[i] ) {
+ foundMutable = true;
+ }
+
+ if ( insertInclusions[i] != ValueInclusion.NONE ) {
+ foundInsertGeneratedValue = true;
+ }
+
+ if ( updateInclusions[i] != ValueInclusion.NONE ) {
+ foundUpdateGeneratedValue = true;
+ }
+
+ mapPropertyToIndex(prop, i);
+ i++;
+ }
+
+ if (naturalIdNumbers.size()==0) {
+ naturalIdPropertyNumbers = null;
+ }
+ else {
+ naturalIdPropertyNumbers = ArrayHelper.toIntArray(naturalIdNumbers);
+ }
+
+ hasInsertGeneratedValues = foundInsertGeneratedValue;
+ hasUpdateGeneratedValues = foundUpdateGeneratedValue;
+
+ hasCascades = foundCascade;
+ hasNonIdentifierPropertyNamedId = foundNonIdentifierPropertyNamedId;
+ versionPropertyIndex = tempVersionProperty;
+ hasLazyProperties = hasLazy;
+ if ( hasLazyProperties ) {
+ log.info( "lazy property fetching available for: " + name );
+ }
+
+ lazy = persistentClass.isLazy() && (
+ // TODO: this disables laziness even in non-pojo entity modes:
+ !persistentClass.hasPojoRepresentation() ||
+ !ReflectHelper.isFinalClass( persistentClass.getProxyInterface() )
+ );
+ mutable = persistentClass.isMutable();
+ if ( persistentClass.isAbstract() == null ) {
+ // legacy behavior (with no abstract attribute specified)
+ isAbstract = persistentClass.hasPojoRepresentation() &&
+ ReflectHelper.isAbstractClass( persistentClass.getMappedClass() );
+ }
+ else {
+ isAbstract = persistentClass.isAbstract().booleanValue();
+ if ( !isAbstract && persistentClass.hasPojoRepresentation() &&
+ ReflectHelper.isAbstractClass( persistentClass.getMappedClass() ) ) {
+ log.warn( "entity [" + name + "] is abstract-class/interface explicitly mapped as non-abstract; be sure to supply entity-names" );
+ }
+ }
+ selectBeforeUpdate = persistentClass.hasSelectBeforeUpdate();
+ dynamicUpdate = persistentClass.useDynamicUpdate();
+ dynamicInsert = persistentClass.useDynamicInsert();
+
+ polymorphic = persistentClass.isPolymorphic();
+ explicitPolymorphism = persistentClass.isExplicitPolymorphism();
+ inherited = persistentClass.isInherited();
+ superclass = inherited ?
+ persistentClass.getSuperclass().getEntityName() :
+ null;
+ hasSubclasses = persistentClass.hasSubclasses();
+
+ optimisticLockMode = persistentClass.getOptimisticLockMode();
+ if ( optimisticLockMode > Versioning.OPTIMISTIC_LOCK_VERSION && !dynamicUpdate ) {
+ throw new MappingException( "optimistic-lock=all|dirty requires dynamic-update=\"true\": " + name );
+ }
+ if ( versionPropertyIndex != NO_VERSION_INDX && optimisticLockMode > Versioning.OPTIMISTIC_LOCK_VERSION ) {
+ throw new MappingException( "version and optimistic-lock=all|dirty are not a valid combination : " + name );
+ }
+
+ hasCollections = foundCollection;
+ hasMutableProperties = foundMutable;
+
+ iter = persistentClass.getSubclassIterator();
+ while ( iter.hasNext() ) {
+ subclassEntityNames.add( ( (PersistentClass) iter.next() ).getEntityName() );
+ }
+ subclassEntityNames.add( name );
+
+ tuplizerMapping = new EntityEntityModeToTuplizerMapping( persistentClass, this );
+ }
+
+ private ValueInclusion determineInsertValueGenerationType(Property mappingProperty, StandardProperty runtimeProperty) {
+ if ( runtimeProperty.isInsertGenerated() ) {
+ return ValueInclusion.FULL;
+ }
+ else if ( mappingProperty.getValue() instanceof Component ) {
+ if ( hasPartialInsertComponentGeneration( ( Component ) mappingProperty.getValue() ) ) {
+ return ValueInclusion.PARTIAL;
+ }
+ }
+ return ValueInclusion.NONE;
+ }
+
+ private boolean hasPartialInsertComponentGeneration(Component component) {
+ Iterator subProperties = component.getPropertyIterator();
+ while ( subProperties.hasNext() ) {
+ Property prop = ( Property ) subProperties.next();
+ if ( prop.getGeneration() == PropertyGeneration.ALWAYS || prop.getGeneration() == PropertyGeneration.INSERT ) {
+ return true;
+ }
+ else if ( prop.getValue() instanceof Component ) {
+ if ( hasPartialInsertComponentGeneration( ( Component ) prop.getValue() ) ) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private ValueInclusion determineUpdateValueGenerationType(Property mappingProperty, StandardProperty runtimeProperty) {
+ if ( runtimeProperty.isUpdateGenerated() ) {
+ return ValueInclusion.FULL;
+ }
+ else if ( mappingProperty.getValue() instanceof Component ) {
+ if ( hasPartialUpdateComponentGeneration( ( Component ) mappingProperty.getValue() ) ) {
+ return ValueInclusion.PARTIAL;
+ }
+ }
+ return ValueInclusion.NONE;
+ }
+
+ private boolean hasPartialUpdateComponentGeneration(Component component) {
+ Iterator subProperties = component.getPropertyIterator();
+ while ( subProperties.hasNext() ) {
+ Property prop = ( Property ) subProperties.next();
+ if ( prop.getGeneration() == PropertyGeneration.ALWAYS ) {
+ return true;
+ }
+ else if ( prop.getValue() instanceof Component ) {
+ if ( hasPartialUpdateComponentGeneration( ( Component ) prop.getValue() ) ) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private void mapPropertyToIndex(Property prop, int i) {
+ propertyIndexes.put( prop.getName(), new Integer(i) );
+ if ( prop.getValue() instanceof Component ) {
+ Iterator iter = ( (Component) prop.getValue() ).getPropertyIterator();
+ while ( iter.hasNext() ) {
+ Property subprop = (Property) iter.next();
+ propertyIndexes.put(
+ prop.getName() + '.' + subprop.getName(),
+ new Integer(i)
+ );
+ }
+ }
+ }
+
+ public int[] getNaturalIdentifierProperties() {
+ return naturalIdPropertyNumbers;
+ }
+
+ public boolean hasNaturalIdentifier() {
+ return naturalIdPropertyNumbers!=null;
+ }
+
+ public Set getSubclassEntityNames() {
+ return subclassEntityNames;
+ }
+
+ private boolean indicatesCollection(Type type) {
+ if ( type.isCollectionType() ) {
+ return true;
+ }
+ else if ( type.isComponentType() ) {
+ Type[] subtypes = ( ( AbstractComponentType ) type ).getSubtypes();
+ for ( int i = 0; i < subtypes.length; i++ ) {
+ if ( indicatesCollection( subtypes[i] ) ) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public SessionFactoryImplementor getSessionFactory() {
+ return sessionFactory;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getRootName() {
+ return rootName;
+ }
+
+ public EntityType getEntityType() {
+ return entityType;
+ }
+
+ public IdentifierProperty getIdentifierProperty() {
+ return identifierProperty;
+ }
+
+ public int getPropertySpan() {
+ return propertySpan;
+ }
+
+ public int getVersionPropertyIndex() {
+ return versionPropertyIndex;
+ }
+
+ public VersionProperty getVersionProperty() {
+ if ( NO_VERSION_INDX == versionPropertyIndex ) {
+ return null;
+ }
+ else {
+ return ( VersionProperty ) properties[ versionPropertyIndex ];
+ }
+ }
+
+ public StandardProperty[] getProperties() {
+ return properties;
+ }
+
+ public int getPropertyIndex(String propertyName) {
+ Integer index = getPropertyIndexOrNull(propertyName);
+ if ( index == null ) {
+ throw new HibernateException("Unable to resolve property: " + propertyName);
+ }
+ return index.intValue();
+ }
+
+ public Integer getPropertyIndexOrNull(String propertyName) {
+ return (Integer) propertyIndexes.get( propertyName );
+ }
+
+ public boolean hasCollections() {
+ return hasCollections;
+ }
+
+ public boolean hasMutableProperties() {
+ return hasMutableProperties;
+ }
+
+ public boolean hasNonIdentifierPropertyNamedId() {
+ return hasNonIdentifierPropertyNamedId;
+ }
+
+ public boolean hasLazyProperties() {
+ return hasLazyProperties;
+ }
+
+ public boolean hasCascades() {
+ return hasCascades;
+ }
+
+ public boolean isMutable() {
+ return mutable;
+ }
+
+ public boolean isSelectBeforeUpdate() {
+ return selectBeforeUpdate;
+ }
+
+ public boolean isDynamicUpdate() {
+ return dynamicUpdate;
+ }
+
+ public boolean isDynamicInsert() {
+ return dynamicInsert;
+ }
+
+ public int getOptimisticLockMode() {
+ return optimisticLockMode;
+ }
+
+ public boolean isPolymorphic() {
+ return polymorphic;
+ }
+
+ public String getSuperclass() {
+ return superclass;
+ }
+
+ public boolean isExplicitPolymorphism() {
+ return explicitPolymorphism;
+ }
+
+ public boolean isInherited() {
+ return inherited;
+ }
+
+ public boolean hasSubclasses() {
+ return hasSubclasses;
+ }
+
+ public boolean isLazy() {
+ return lazy;
+ }
+
+ public void setLazy(boolean lazy) {
+ this.lazy = lazy;
+ }
+
+ public boolean isVersioned() {
+ return versioned;
+ }
+
+ public boolean isAbstract() {
+ return isAbstract;
+ }
+
+ public String toString() {
+ return "EntityMetamodel(" + name + ':' + ArrayHelper.toString(properties) + ')';
+ }
+
+ // temporary ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ public String[] getPropertyNames() {
+ return propertyNames;
+ }
+
+ public Type[] getPropertyTypes() {
+ return propertyTypes;
+ }
+
+ public boolean[] getPropertyLaziness() {
+ return propertyLaziness;
+ }
+
+ public boolean[] getPropertyUpdateability() {
+ return propertyUpdateability;
+ }
+
+ public boolean[] getPropertyCheckability() {
+ return propertyCheckability;
+ }
+
+ public boolean[] getNonlazyPropertyUpdateability() {
+ return nonlazyPropertyUpdateability;
+ }
+
+ public boolean[] getPropertyInsertability() {
+ return propertyInsertability;
+ }
+
+ public ValueInclusion[] getPropertyInsertGenerationInclusions() {
+ return insertInclusions;
+ }
+
+ public ValueInclusion[] getPropertyUpdateGenerationInclusions() {
+ return updateInclusions;
+ }
+
+ public boolean[] getPropertyNullability() {
+ return propertyNullability;
+ }
+
+ public boolean[] getPropertyVersionability() {
+ return propertyVersionability;
+ }
+
+ public CascadeStyle[] getCascadeStyles() {
+ return cascadeStyles;
+ }
+
+ public boolean hasInsertGeneratedValues() {
+ return hasInsertGeneratedValues;
+ }
+
+ public boolean hasUpdateGeneratedValues() {
+ return hasUpdateGeneratedValues;
+ }
+
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/entity/EntityTuplizer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/entity/EntityTuplizer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/entity/EntityTuplizer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,178 @@
+//$Id: EntityTuplizer.java 7516 2005-07-16 22:20:48Z oneovthafew $
+package org.hibernate.tuple.entity;
+
+import java.io.Serializable;
+import java.util.Map;
+
+import org.hibernate.HibernateException;
+import org.hibernate.tuple.Tuplizer;
+import org.hibernate.engine.SessionImplementor;
+
+/**
+ * Defines further responsibilities reagarding tuplization based on
+ * a mapped entity.
+ * <p/>
+ * EntityTuplizer implementations should have the following constructor signature:
+ * (org.hibernate.tuple.entity.EntityMetamodel, org.hibernate.mapping.PersistentClass)
+ *
+ * @author Gavin King
+ * @author Steve Ebersole
+ */
+public interface EntityTuplizer extends Tuplizer {
+
+ /**
+ * Create an entity instance initialized with the given identifier.
+ *
+ * @param id The identifier value for the entity to be instantiated.
+ * @return The instantiated entity.
+ * @throws HibernateException
+ */
+ public Object instantiate(Serializable id) throws HibernateException;
+
+ /**
+ * Extract the identifier value from the given entity.
+ *
+ * @param entity The entity from which to extract the identifier value.
+ * @return The identifier value.
+ * @throws HibernateException If the entity does not define an identifier property, or an
+ * error occurrs accessing its value.
+ */
+ public Serializable getIdentifier(Object entity) throws HibernateException;
+
+ /**
+ * Inject the identifier value into the given entity.
+ * </p>
+ * Has no effect if the entity does not define an identifier property
+ *
+ * @param entity The entity to inject with the identifier value.
+ * @param id The value to be injected as the identifier.
+ * @throws HibernateException
+ */
+ public void setIdentifier(Object entity, Serializable id) throws HibernateException;
+
+ /**
+ * Inject the given identifier and version into the entity, in order to
+ * "roll back" to their original values.
+ *
+ * @param currentId The identifier value to inject into the entity.
+ * @param currentVersion The version value to inject into the entity.
+ */
+ public void resetIdentifier(Object entity, Serializable currentId, Object currentVersion);
+
+ /**
+ * Extract the value of the version property from the given entity.
+ *
+ * @param entity The entity from which to extract the version value.
+ * @return The value of the version property, or null if not versioned.
+ * @throws HibernateException
+ */
+ public Object getVersion(Object entity) throws HibernateException;
+
+ /**
+ * Inject the value of a particular property.
+ *
+ * @param entity The entity into which to inject the value.
+ * @param i The property's index.
+ * @param value The property value to inject.
+ * @throws HibernateException
+ */
+ public void setPropertyValue(Object entity, int i, Object value) throws HibernateException;
+
+ /**
+ * Inject the value of a particular property.
+ *
+ * @param entity The entity into which to inject the value.
+ * @param propertyName The name of the property.
+ * @param value The property value to inject.
+ * @throws HibernateException
+ */
+ public void setPropertyValue(Object entity, String propertyName, Object value) throws HibernateException;
+
+ /**
+ * Extract the values of the insertable properties of the entity (including backrefs)
+ *
+ * @param entity The entity from which to extract.
+ * @param mergeMap a map of instances being merged to merged instances
+ * @param session The session in which the resuest is being made.
+ * @return The insertable property values.
+ * @throws HibernateException
+ */
+ public Object[] getPropertyValuesToInsert(Object entity, Map mergeMap, SessionImplementor session)
+ throws HibernateException;
+
+ /**
+ * Extract the value of a particular property from the given entity.
+ *
+ * @param entity The entity from which to extract the property value.
+ * @param propertyName The name of the property for which to extract the value.
+ * @return The current value of the given property on the given entity.
+ * @throws HibernateException
+ */
+ public Object getPropertyValue(Object entity, String propertyName) throws HibernateException;
+
+ /**
+ * Called just after the entities properties have been initialized.
+ *
+ * @param entity The entity being initialized.
+ * @param lazyPropertiesAreUnfetched Are defined lazy properties currently unfecthed
+ * @param session The session initializing this entity.
+ */
+ public void afterInitialize(Object entity, boolean lazyPropertiesAreUnfetched, SessionImplementor session);
+
+ /**
+ * Does this entity, for this mode, present a possibility for proxying?
+ *
+ * @return True if this tuplizer can generate proxies for this entity.
+ */
+ public boolean hasProxy();
+
+ /**
+ * Generates an appropriate proxy representation of this entity for this
+ * entity-mode.
+ *
+ * @param id The id of the instance for which to generate a proxy.
+ * @param session The session to which the proxy should be bound.
+ * @return The generate proxies.
+ * @throws HibernateException Indicates an error generating the proxy.
+ */
+ public Object createProxy(Serializable id, SessionImplementor session) throws HibernateException;
+
+ /**
+ * Does the {@link #getMappedClass() class} managed by this tuplizer implement
+ * the {@link org.hibernate.classic.Lifecycle} interface.
+ *
+ * @return True if the Lifecycle interface is implemented; false otherwise.
+ */
+ public boolean isLifecycleImplementor();
+
+ /**
+ * Does the {@link #getMappedClass() class} managed by this tuplizer implement
+ * the {@link org.hibernate.classic.Validatable} interface.
+ *
+ * @return True if the Validatable interface is implemented; false otherwise.
+ */
+ public boolean isValidatableImplementor();
+
+ // TODO: getConcreteProxyClass() is solely used (externally) to perform narrowProxy()
+ // would be great to fully encapsulate that narrowProxy() functionality within the
+ // Tuplizer, itself, with a Tuplizer.narrowProxy(..., PersistentContext) method
+ /**
+ * Returns the java class to which generated proxies will be typed.
+ *
+ * @return The java class to which generated proxies will be typed
+ */
+ public Class getConcreteProxyClass();
+
+ /**
+ * Does the given entity instance have any currently uninitialized lazy properties?
+ *
+ * @param entity The entity to be check for uninitialized lazy properties.
+ * @return True if uninitialized lazy properties were found; false otherwise.
+ */
+ public boolean hasUninitializedLazyProperties(Object entity);
+
+ /**
+ * Is it an instrumented POJO?
+ */
+ public boolean isInstrumented();
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/entity/PojoEntityTuplizer.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/entity/PojoEntityTuplizer.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/entity/PojoEntityTuplizer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,284 @@
+// $Id: PojoEntityTuplizer.java 9210 2006-02-03 22:15:19Z steveebersole $
+package org.hibernate.tuple.entity;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.tuple.entity.AbstractEntityTuplizer;
+import org.hibernate.tuple.entity.EntityMetamodel;
+import org.hibernate.tuple.Instantiator;
+import org.hibernate.tuple.PojoInstantiator;
+import org.hibernate.bytecode.ReflectionOptimizer;
+import org.hibernate.cfg.Environment;
+import org.hibernate.classic.Lifecycle;
+import org.hibernate.classic.Validatable;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.intercept.FieldInterceptor;
+import org.hibernate.intercept.FieldInterceptionHelper;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Property;
+import org.hibernate.mapping.Subclass;
+import org.hibernate.property.Getter;
+import org.hibernate.property.Setter;
+import org.hibernate.proxy.HibernateProxy;
+import org.hibernate.proxy.ProxyFactory;
+import org.hibernate.type.AbstractComponentType;
+import org.hibernate.util.ReflectHelper;
+
+/**
+ * An {@link EntityTuplizer} specific to the pojo entity mode.
+ *
+ * @author Steve Ebersole
+ * @author Gavin King
+ */
+public class PojoEntityTuplizer extends AbstractEntityTuplizer {
+
+ static final Log log = LogFactory.getLog( PojoEntityTuplizer.class );
+
+ private final Class mappedClass;
+ private final Class proxyInterface;
+ private final boolean lifecycleImplementor;
+ private final boolean validatableImplementor;
+ private final Set lazyPropertyNames = new HashSet();
+ private ReflectionOptimizer optimizer;
+
+ public PojoEntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappedEntity) {
+ super( entityMetamodel, mappedEntity );
+ this.mappedClass = mappedEntity.getMappedClass();
+ this.proxyInterface = mappedEntity.getProxyInterface();
+ this.lifecycleImplementor = Lifecycle.class.isAssignableFrom( mappedClass );
+ this.validatableImplementor = Validatable.class.isAssignableFrom( mappedClass );
+
+ Iterator iter = mappedEntity.getPropertyClosureIterator();
+ while ( iter.hasNext() ) {
+ Property property = (Property) iter.next();
+ if ( property.isLazy() ) {
+ lazyPropertyNames.add( property.getName() );
+ }
+ }
+
+ String[] getterNames = new String[propertySpan];
+ String[] setterNames = new String[propertySpan];
+ Class[] propTypes = new Class[propertySpan];
+ for ( int i = 0; i < propertySpan; i++ ) {
+ getterNames[i] = getters[i].getMethodName();
+ setterNames[i] = setters[i].getMethodName();
+ propTypes[i] = getters[i].getReturnType();
+ }
+
+ if ( hasCustomAccessors || !Environment.useReflectionOptimizer() ) {
+ optimizer = null;
+ }
+ else {
+ // todo : YUCK!!!
+ optimizer = Environment.getBytecodeProvider().getReflectionOptimizer( mappedClass, getterNames, setterNames, propTypes );
+// optimizer = getFactory().getSettings().getBytecodeProvider().getReflectionOptimizer(
+// mappedClass, getterNames, setterNames, propTypes
+// );
+ }
+
+ }
+
+ protected ProxyFactory buildProxyFactory(PersistentClass persistentClass, Getter idGetter, Setter idSetter) {
+ // determine the id getter and setter methods from the proxy interface (if any)
+ // determine all interfaces needed by the resulting proxy
+ HashSet proxyInterfaces = new HashSet();
+ proxyInterfaces.add( HibernateProxy.class );
+
+ Class mappedClass = persistentClass.getMappedClass();
+ Class proxyInterface = persistentClass.getProxyInterface();
+
+ if ( proxyInterface!=null && !mappedClass.equals( proxyInterface ) ) {
+ if ( !proxyInterface.isInterface() ) {
+ throw new MappingException(
+ "proxy must be either an interface, or the class itself: " +
+ getEntityName()
+ );
+ }
+ proxyInterfaces.add( proxyInterface );
+ }
+
+ if ( mappedClass.isInterface() ) {
+ proxyInterfaces.add( mappedClass );
+ }
+
+ Iterator iter = persistentClass.getSubclassIterator();
+ while ( iter.hasNext() ) {
+ Subclass subclass = ( Subclass ) iter.next();
+ Class subclassProxy = subclass.getProxyInterface();
+ Class subclassClass = subclass.getMappedClass();
+ if ( subclassProxy!=null && !subclassClass.equals( subclassProxy ) ) {
+ if ( !proxyInterface.isInterface() ) {
+ throw new MappingException(
+ "proxy must be either an interface, or the class itself: " +
+ subclass.getEntityName()
+ );
+ }
+ proxyInterfaces.add( subclassProxy );
+ }
+ }
+
+ Iterator properties = persistentClass.getPropertyIterator();
+ Class clazz = persistentClass.getMappedClass();
+ while ( properties.hasNext() ) {
+ Property property = (Property) properties.next();
+ Method method = property.getGetter(clazz).getMethod();
+ if ( method != null && Modifier.isFinal( method.getModifiers() ) ) {
+ log.error(
+ "Getters of lazy classes cannot be final: " + persistentClass.getEntityName() +
+ "." + property.getName()
+ );
+ }
+ method = property.getSetter(clazz).getMethod();
+ if ( method != null && Modifier.isFinal( method.getModifiers() ) ) {
+ log.error(
+ "Setters of lazy classes cannot be final: " + persistentClass.getEntityName() +
+ "." + property.getName()
+ );
+ }
+ }
+
+ Method idGetterMethod = idGetter==null ? null : idGetter.getMethod();
+ Method idSetterMethod = idSetter==null ? null : idSetter.getMethod();
+
+ Method proxyGetIdentifierMethod = idGetterMethod==null || proxyInterface==null ?
+ null :
+ ReflectHelper.getMethod(proxyInterface, idGetterMethod);
+ Method proxySetIdentifierMethod = idSetterMethod==null || proxyInterface==null ?
+ null :
+ ReflectHelper.getMethod(proxyInterface, idSetterMethod);
+
+ ProxyFactory pf = buildProxyFactoryInternal( persistentClass, idGetter, idSetter );
+ try {
+ pf.postInstantiate(
+ getEntityName(),
+ mappedClass,
+ proxyInterfaces,
+ proxyGetIdentifierMethod,
+ proxySetIdentifierMethod,
+ persistentClass.hasEmbeddedIdentifier() ?
+ (AbstractComponentType) persistentClass.getIdentifier().getType() :
+ null
+ );
+ }
+ catch ( HibernateException he ) {
+ log.warn( "could not create proxy factory for:" + getEntityName(), he );
+ pf = null;
+ }
+ return pf;
+ }
+
+ protected ProxyFactory buildProxyFactoryInternal(PersistentClass persistentClass, Getter idGetter, Setter idSetter) {
+ // TODO : YUCK!!! finx after HHH-1907 is complete
+ return Environment.getBytecodeProvider().getProxyFactoryFactory().buildProxyFactory();
+// return getFactory().getSettings().getBytecodeProvider().getProxyFactoryFactory().buildProxyFactory();
+ }
+
+ protected Instantiator buildInstantiator(PersistentClass persistentClass) {
+ if ( optimizer == null ) {
+ return new PojoInstantiator( persistentClass, null );
+ }
+ else {
+ return new PojoInstantiator( persistentClass, optimizer.getInstantiationOptimizer() );
+ }
+ }
+
+ public void setPropertyValues(Object entity, Object[] values) throws HibernateException {
+ if ( !getEntityMetamodel().hasLazyProperties() && optimizer != null && optimizer.getAccessOptimizer() != null ) {
+ setPropertyValuesWithOptimizer( entity, values );
+ }
+ else {
+ super.setPropertyValues( entity, values );
+ }
+ }
+
+ public Object[] getPropertyValues(Object entity) throws HibernateException {
+ if ( shouldGetAllProperties( entity ) && optimizer != null && optimizer.getAccessOptimizer() != null ) {
+ return getPropertyValuesWithOptimizer( entity );
+ }
+ else {
+ return super.getPropertyValues( entity );
+ }
+ }
+
+ public Object[] getPropertyValuesToInsert(Object entity, Map mergeMap, SessionImplementor session) throws HibernateException {
+ if ( shouldGetAllProperties( entity ) && optimizer != null && optimizer.getAccessOptimizer() != null ) {
+ return getPropertyValuesWithOptimizer( entity );
+ }
+ else {
+ return super.getPropertyValuesToInsert( entity, mergeMap, session );
+ }
+ }
+
+ protected void setPropertyValuesWithOptimizer(Object object, Object[] values) {
+ optimizer.getAccessOptimizer().setPropertyValues( object, values );
+ }
+
+ protected Object[] getPropertyValuesWithOptimizer(Object object) {
+ return optimizer.getAccessOptimizer().getPropertyValues( object );
+ }
+
+ public EntityMode getEntityMode() {
+ return EntityMode.POJO;
+ }
+
+ public Class getMappedClass() {
+ return mappedClass;
+ }
+
+ public boolean isLifecycleImplementor() {
+ return lifecycleImplementor;
+ }
+
+ public boolean isValidatableImplementor() {
+ return validatableImplementor;
+ }
+
+ protected Getter buildPropertyGetter(Property mappedProperty, PersistentClass mappedEntity) {
+ return mappedProperty.getGetter( mappedEntity.getMappedClass() );
+ }
+
+ protected Setter buildPropertySetter(Property mappedProperty, PersistentClass mappedEntity) {
+ return mappedProperty.getSetter( mappedEntity.getMappedClass() );
+ }
+
+ public Class getConcreteProxyClass() {
+ return proxyInterface;
+ }
+
+ //TODO: need to make the majority of this functionality into a top-level support class for custom impl support
+
+ public void afterInitialize(Object entity, boolean lazyPropertiesAreUnfetched, SessionImplementor session) {
+ if ( isInstrumented() ) {
+ Set lazyProps = lazyPropertiesAreUnfetched && getEntityMetamodel().hasLazyProperties() ?
+ lazyPropertyNames : null;
+ //TODO: if we support multiple fetch groups, we would need
+ // to clone the set of lazy properties!
+ FieldInterceptionHelper.injectFieldInterceptor( entity, getEntityName(), lazyProps, session );
+ }
+ }
+
+ public boolean hasUninitializedLazyProperties(Object entity) {
+ if ( getEntityMetamodel().hasLazyProperties() ) {
+ FieldInterceptor callback = FieldInterceptionHelper.extractFieldInterceptor( entity );
+ return callback != null && !callback.isInitialized();
+ }
+ else {
+ return false;
+ }
+ }
+
+ public boolean isInstrumented() {
+ return FieldInterceptionHelper.isInstrumented( getMappedClass() );
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,15 @@
+<html>
+<head></head>
+<body>
+<p>
+ This package defines a runtime metamodel for entities at
+ the object level and abstracts the differences between
+ the various entity modes. It is unaware of mappings to
+ the database.
+</p>
+<p>
+ (This package is still undergoing maturation and will change
+ over the next few months.)
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/tuple/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/AbstractBynaryType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/AbstractBynaryType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/AbstractBynaryType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,166 @@
+//$Id: $
+package org.hibernate.type;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.ResultSet;
+import java.sql.Types;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Comparator;
+
+import org.hibernate.HibernateException;
+import org.hibernate.EntityMode;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.cfg.Environment;
+
+/**
+ * Logic to bind stream of byte into a VARBINARY
+ *
+ * @author Gavin King
+ * @author Emmanuel Bernard
+ */
+public abstract class AbstractBynaryType extends MutableType implements VersionType, Comparator {
+
+ /**
+ * Convert the byte[] into the expected object type
+ */
+ abstract protected Object toExternalFormat(byte[] bytes);
+
+ /**
+ * Convert the object into the internal byte[] representation
+ */
+ abstract protected byte[] toInternalFormat(Object bytes);
+
+ public void set(PreparedStatement st, Object value, int index) throws HibernateException, SQLException {
+ byte[] internalValue = toInternalFormat( value );
+ if ( Environment.useStreamsForBinary() ) {
+ st.setBinaryStream( index, new ByteArrayInputStream( internalValue ), internalValue.length );
+ }
+ else {
+ st.setBytes( index, internalValue );
+ }
+ }
+
+ public Object get(ResultSet rs, String name) throws HibernateException, SQLException {
+
+ if ( Environment.useStreamsForBinary() ) {
+
+ InputStream inputStream = rs.getBinaryStream(name);
+
+ if (inputStream==null) return toExternalFormat( null ); // is this really necessary?
+
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream(2048);
+ byte[] buffer = new byte[2048];
+
+ try {
+ while (true) {
+ int amountRead = inputStream.read(buffer);
+ if (amountRead == -1) {
+ break;
+ }
+ outputStream.write(buffer, 0, amountRead);
+ }
+
+ inputStream.close();
+ outputStream.close();
+ }
+ catch (IOException ioe) {
+ throw new HibernateException( "IOException occurred reading a binary value", ioe );
+ }
+
+ return toExternalFormat( outputStream.toByteArray() );
+
+ }
+ else {
+ return toExternalFormat( rs.getBytes(name) );
+ }
+ }
+
+ public int sqlType() {
+ return Types.VARBINARY;
+ }
+
+ // VersionType impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ // Note : simply returns null for seed() and next() as the only known
+ // application of binary types for versioning is for use with the
+ // TIMESTAMP datatype supported by Sybase and SQL Server, which
+ // are completely db-generated values...
+ public Object seed(SessionImplementor session) {
+ return null;
+ }
+
+ public Object next(Object current, SessionImplementor session) {
+ return current;
+ }
+
+ public Comparator getComparator() {
+ return this;
+ }
+
+ public int compare(Object o1, Object o2) {
+ return compare( o1, o2, null );
+ }
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public boolean isEqual(Object x, Object y) {
+ return x==y || ( x!=null && y!=null && java.util.Arrays.equals( toInternalFormat(x), toInternalFormat(y) ) );
+ }
+
+ public int getHashCode(Object x, EntityMode entityMode) {
+ byte[] bytes = toInternalFormat(x);
+ int hashCode = 1;
+ for ( int j=0; j<bytes.length; j++ ) {
+ hashCode = 31 * hashCode + bytes[j];
+ }
+ return hashCode;
+ }
+
+ public int compare(Object x, Object y, EntityMode entityMode) {
+ byte[] xbytes = toInternalFormat(x);
+ byte[] ybytes = toInternalFormat(y);
+ if ( xbytes.length < ybytes.length ) return -1;
+ if ( xbytes.length > ybytes.length ) return 1;
+ for ( int i=0; i<xbytes.length; i++ ) {
+ if ( xbytes[i] < ybytes[i] ) return -1;
+ if ( xbytes[i] > ybytes[i] ) return 1;
+ }
+ return 0;
+ }
+
+ public abstract String getName();
+
+ public String toString(Object val) {
+ byte[] bytes = toInternalFormat(val);
+ StringBuffer buf = new StringBuffer();
+ for ( int i=0; i<bytes.length; i++ ) {
+ String hexStr = Integer.toHexString( bytes[i] - Byte.MIN_VALUE );
+ if ( hexStr.length()==1 ) buf.append('0');
+ buf.append(hexStr);
+ }
+ return buf.toString();
+ }
+
+ public Object deepCopyNotNull(Object value) {
+ byte[] bytes = toInternalFormat(value);
+ byte[] result = new byte[bytes.length];
+ System.arraycopy(bytes, 0, result, 0, bytes.length);
+ return toExternalFormat(result);
+ }
+
+ public Object fromStringValue(String xml) throws HibernateException {
+ if (xml == null)
+ return null;
+ if (xml.length() % 2 != 0)
+ throw new IllegalArgumentException("The string is not a valid xml representation of a binary content.");
+ byte[] bytes = new byte[xml.length() / 2];
+ for (int i = 0; i < bytes.length; i++) {
+ String hexStr = xml.substring(i * 2, (i + 1) * 2);
+ bytes[i] = (byte) (Integer.parseInt(hexStr, 16) + Byte.MIN_VALUE);
+ }
+ return toExternalFormat(bytes);
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/AbstractCharArrayType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/AbstractCharArrayType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/AbstractCharArrayType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,96 @@
+//$Id: $
+package org.hibernate.type;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.PreparedStatement;
+import java.sql.Types;
+import java.io.Reader;
+import java.io.CharArrayWriter;
+import java.io.IOException;
+import java.io.CharArrayReader;
+
+import org.hibernate.HibernateException;
+import org.hibernate.dialect.Dialect;
+
+/**
+ * Logic to bind stream of char into a VARCHAR
+ *
+ * @author Emmanuel Bernard
+ */
+public abstract class AbstractCharArrayType extends MutableType {
+
+ /**
+ * Convert the char[] into the expected object type
+ */
+ abstract protected Object toExternalFormat(char[] chars);
+
+ /**
+ * Convert the object into the internal char[] representation
+ */
+ abstract protected char[] toInternalFormat(Object chars);
+
+ public Object get(ResultSet rs, String name) throws SQLException {
+ Reader stream = rs.getCharacterStream(name);
+ if ( stream == null ) return toExternalFormat( null );
+ CharArrayWriter writer = new CharArrayWriter();
+ for(;;) {
+ try {
+ int c = stream.read();
+ if ( c == -1) return toExternalFormat( writer.toCharArray() );
+ writer.write( c );
+ }
+ catch (IOException e) {
+ throw new HibernateException("Unable to read character stream from rs");
+ }
+ }
+ }
+
+ public abstract Class getReturnedClass();
+
+ public void set(PreparedStatement st, Object value, int index) throws SQLException {
+ char[] chars = toInternalFormat( value );
+ st.setCharacterStream(index, new CharArrayReader(chars), chars.length);
+ }
+
+ public int sqlType() {
+ return Types.VARCHAR;
+ }
+
+ public String objectToSQLString(Object value, Dialect dialect) throws Exception {
+
+ return '\'' + new String( toInternalFormat( value ) ) + '\'';
+ }
+
+ public Object stringToObject(String xml) throws Exception {
+ if (xml == null) return toExternalFormat( null );
+ int length = xml.length();
+ char[] chars = new char[length];
+ for (int index = 0 ; index < length ; index++ ) {
+ chars[index] = xml.charAt( index );
+ }
+ return toExternalFormat( chars );
+ }
+
+ public String toString(Object value) {
+ if (value == null) return null;
+ return new String( toInternalFormat( value ) );
+ }
+
+ public Object fromStringValue(String xml) {
+ if (xml == null) return null;
+ int length = xml.length();
+ char[] chars = new char[length];
+ for (int index = 0 ; index < length ; index++ ) {
+ chars[index] = xml.charAt( index );
+ }
+ return toExternalFormat( chars );
+ }
+
+ protected Object deepCopyNotNull(Object value) throws HibernateException {
+ char[] chars = toInternalFormat(value);
+ char[] result = new char[chars.length];
+ System.arraycopy(chars, 0, result, 0, chars.length);
+ return toExternalFormat(result);
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/AbstractComponentType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/AbstractComponentType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/AbstractComponentType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,52 @@
+//$Id: AbstractComponentType.java 7670 2005-07-29 05:36:14Z oneovthafew $
+package org.hibernate.type;
+
+import java.lang.reflect.Method;
+
+import org.hibernate.EntityMode;
+import org.hibernate.FetchMode;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.CascadeStyle;
+import org.hibernate.engine.SessionImplementor;
+
+/**
+ * Enables other Component-like types to hold collections and have cascades, etc.
+ *
+ * @see ComponentType
+ * @see AnyType
+ * @author Gavin King
+ */
+public interface AbstractComponentType extends Type {
+ /**
+ * Get the types of the component properties
+ */
+ public Type[] getSubtypes();
+ /**
+ * Get the names of the component properties
+ */
+ public String[] getPropertyNames();
+ /**
+ * Optional operation
+ * @return nullability of component properties
+ */
+ public boolean[] getPropertyNullability();
+ /**
+ * Get the values of the component properties of
+ * a component instance
+ */
+ public Object[] getPropertyValues(Object component, SessionImplementor session) throws HibernateException;
+ /**
+ * Optional operation
+ */
+ public Object[] getPropertyValues(Object component, EntityMode entityMode) throws HibernateException;
+ /**
+ * Optional operation
+ */
+ public void setPropertyValues(Object component, Object[] values, EntityMode entityMode) throws HibernateException;
+ public Object getPropertyValue(Object component, int i, SessionImplementor session) throws HibernateException;
+ //public Object instantiate(Object parent, SessionImplementor session) throws HibernateException;
+ public CascadeStyle getCascadeStyle(int i);
+ public FetchMode getFetchMode(int i);
+ public boolean isMethodOf(Method method);
+ public boolean isEmbedded();
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/AbstractType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/AbstractType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/AbstractType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,164 @@
+//$Id: AbstractType.java 7793 2005-08-10 05:06:40Z oneovthafew $
+package org.hibernate.type;
+
+import java.io.Serializable;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Map;
+
+import org.dom4j.Element;
+import org.dom4j.Node;
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.util.EqualsHelper;
+
+/**
+ * Abstract superclass of the built in Type hierarchy.
+ * @author Gavin King
+ */
+public abstract class AbstractType implements Type {
+
+ public boolean isAssociationType() {
+ return false;
+ }
+
+ public boolean isCollectionType() {
+ return false;
+ }
+
+ public boolean isComponentType() {
+ return false;
+ }
+
+ public boolean isEntityType() {
+ return false;
+ }
+
+ public boolean isXMLElement() {
+ return false;
+ }
+
+ public int compare(Object x, Object y, EntityMode entityMode) {
+ return ( (Comparable) x ).compareTo(y);
+ }
+
+ public Serializable disassemble(Object value, SessionImplementor session, Object owner)
+ throws HibernateException {
+
+ if (value==null) {
+ return null;
+ }
+ else {
+ return (Serializable) deepCopy( value, session.getEntityMode(), session.getFactory() );
+ }
+ }
+
+ public Object assemble(Serializable cached, SessionImplementor session, Object owner)
+ throws HibernateException {
+ if ( cached==null ) {
+ return null;
+ }
+ else {
+ return deepCopy( cached, session.getEntityMode(), session.getFactory() );
+ }
+ }
+
+ public boolean isDirty(Object old, Object current, SessionImplementor session)
+ throws HibernateException {
+ return !isSame( old, current, session.getEntityMode() );
+ }
+
+ public Object hydrate(
+ ResultSet rs,
+ String[] names,
+ SessionImplementor session,
+ Object owner)
+ throws HibernateException, SQLException {
+ // TODO: this is very suboptimal for some subclasses (namely components),
+ // since it does not take advantage of two-phase-load
+ return nullSafeGet(rs, names, session, owner);
+ }
+
+ public Object resolve(Object value, SessionImplementor session, Object owner)
+ throws HibernateException {
+ return value;
+ }
+
+ public Object semiResolve(Object value, SessionImplementor session, Object owner)
+ throws HibernateException {
+ return value;
+ }
+
+ public boolean isAnyType() {
+ return false;
+ }
+
+ public boolean isModified(Object old, Object current, boolean[] checkable, SessionImplementor session)
+ throws HibernateException {
+ return isDirty(old, current, session);
+ }
+
+ public boolean isSame(Object x, Object y, EntityMode entityMode) throws HibernateException {
+ return isEqual(x, y, entityMode);
+ }
+
+ public boolean isEqual(Object x, Object y, EntityMode entityMode) {
+ return EqualsHelper.equals(x, y);
+ }
+
+ public int getHashCode(Object x, EntityMode entityMode) {
+ return x.hashCode();
+ }
+
+ public boolean isEqual(Object x, Object y, EntityMode entityMode, SessionFactoryImplementor factory) {
+ return isEqual(x, y, entityMode);
+ }
+
+ public int getHashCode(Object x, EntityMode entityMode, SessionFactoryImplementor factory) {
+ return getHashCode(x, entityMode);
+ }
+
+ protected static void replaceNode(Node container, Element value) {
+ if ( container!=value ) { //not really necessary, I guess...
+ Element parent = container.getParent();
+ container.detach();
+ value.setName( container.getName() );
+ value.detach();
+ parent.add(value);
+ }
+ }
+
+ public Type getSemiResolvedType(SessionFactoryImplementor factory) {
+ return this;
+ }
+
+ public Object replace(
+ Object original,
+ Object target,
+ SessionImplementor session,
+ Object owner,
+ Map copyCache,
+ ForeignKeyDirection foreignKeyDirection)
+ throws HibernateException {
+ boolean include;
+ if ( isAssociationType() ) {
+ AssociationType atype = (AssociationType) this;
+ include = atype.getForeignKeyDirection()==foreignKeyDirection;
+ }
+ else {
+ include = ForeignKeyDirection.FOREIGN_KEY_FROM_PARENT==foreignKeyDirection;
+ }
+ return include ? replace(original, target, session, owner, copyCache) : target;
+ }
+
+ public void beforeAssemble(Serializable cached, SessionImplementor session) {}
+
+ /*public Object copy(Object original, Object target, SessionImplementor session, Object owner, Map copyCache)
+ throws HibernateException {
+ if (original==null) return null;
+ return assemble( disassemble(original, session), session, owner );
+ }*/
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/AdaptedImmutableType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/AdaptedImmutableType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/AdaptedImmutableType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,65 @@
+//$Id: AdaptedImmutableType.java 7238 2005-06-20 09:17:00Z oneovthafew $
+package org.hibernate.type;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+
+/**
+ * Optimize a mutable type, if the user promises not to mutable the
+ * instances.
+ *
+ * @author Gavin King
+ */
+public class AdaptedImmutableType extends ImmutableType {
+
+ private final NullableType mutableType;
+
+ public AdaptedImmutableType(NullableType mutableType) {
+ this.mutableType = mutableType;
+ }
+
+ public Object get(ResultSet rs, String name) throws HibernateException, SQLException {
+ return mutableType.get(rs, name);
+ }
+
+ public void set(PreparedStatement st, Object value, int index) throws HibernateException,
+ SQLException {
+ mutableType.set(st, value, index);
+ }
+
+ public int sqlType() {
+ return mutableType.sqlType();
+ }
+
+ public String toString(Object value) throws HibernateException {
+ return mutableType.toString(value);
+ }
+
+ public Object fromStringValue(String xml) throws HibernateException {
+ return mutableType.fromStringValue(xml);
+ }
+
+ public Class getReturnedClass() {
+ return mutableType.getReturnedClass();
+ }
+
+ public String getName() {
+ return "imm_" + mutableType.getName();
+ }
+
+ public boolean isEqual(Object x, Object y) {
+ return mutableType.isEqual(x, y);
+ }
+
+ public int getHashCode(Object x, EntityMode entityMode) {
+ return mutableType.getHashCode(x, entityMode);
+ }
+
+ public int compare(Object x, Object y, EntityMode entityMode) {
+ return mutableType.compare(x, y, entityMode);
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/type/AdaptedImmutableType.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/AnyType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/AnyType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/AnyType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,369 @@
+//$Id: AnyType.java 7670 2005-07-29 05:36:14Z oneovthafew $
+package org.hibernate.type;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Arrays;
+import java.util.Map;
+
+import org.dom4j.Node;
+import org.hibernate.EntityMode;
+import org.hibernate.FetchMode;
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.TransientObjectException;
+import org.hibernate.engine.CascadeStyle;
+import org.hibernate.engine.ForeignKeys;
+import org.hibernate.engine.Mapping;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.entity.Joinable;
+import org.hibernate.proxy.HibernateProxyHelper;
+import org.hibernate.util.ArrayHelper;
+
+/**
+ * Handles "any" mappings and the old deprecated "object" type
+ * @author Gavin King
+ */
+public class AnyType extends AbstractType implements AbstractComponentType, AssociationType {
+
+ private final Type identifierType;
+ private final Type metaType;
+
+ public AnyType(Type metaType, Type identifierType) {
+ this.identifierType = identifierType;
+ this.metaType = metaType;
+ }
+
+ public AnyType() {
+ this(Hibernate.STRING, Hibernate.SERIALIZABLE);
+ }
+
+ public Object deepCopy(Object value, EntityMode entityMode, SessionFactoryImplementor factory)
+ throws HibernateException {
+ return value;
+ }
+
+ public boolean isMethodOf(Method method) {
+ return false;
+ }
+
+ public boolean isSame(Object x, Object y, EntityMode entityMode) throws HibernateException {
+ return x==y;
+ }
+
+ public int compare(Object x, Object y, EntityMode entityMode) {
+ return 0; //TODO: entities CAN be compared, by PK and entity name, fix this!
+ }
+
+ public int getColumnSpan(Mapping session)
+ throws MappingException {
+ return 2;
+ }
+
+ public String getName() {
+ return "object";
+ }
+
+ public boolean isMutable() {
+ return false;
+ }
+
+ public Object nullSafeGet(ResultSet rs, String name, SessionImplementor session, Object owner)
+ throws HibernateException, SQLException {
+
+ throw new UnsupportedOperationException("object is a multicolumn type");
+ }
+
+ public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner)
+ throws HibernateException, SQLException {
+ return resolveAny(
+ (String) metaType.nullSafeGet(rs, names[0], session, owner),
+ (Serializable) identifierType.nullSafeGet(rs, names[1], session, owner),
+ session
+ );
+ }
+
+ public Object hydrate(ResultSet rs, String[] names, SessionImplementor session, Object owner)
+ throws HibernateException, SQLException {
+ String entityName = (String) metaType.nullSafeGet(rs, names[0], session, owner);
+ Serializable id = (Serializable) identifierType.nullSafeGet(rs, names[1], session, owner);
+ return new ObjectTypeCacheEntry(entityName, id);
+ }
+
+ public Object resolve(Object value, SessionImplementor session, Object owner)
+ throws HibernateException {
+ ObjectTypeCacheEntry holder = (ObjectTypeCacheEntry) value;
+ return resolveAny(holder.entityName, holder.id, session);
+ }
+
+ public Object semiResolve(Object value, SessionImplementor session, Object owner)
+ throws HibernateException {
+ throw new UnsupportedOperationException("any mappings may not form part of a property-ref");
+ }
+
+ private Object resolveAny(String entityName, Serializable id, SessionImplementor session)
+ throws HibernateException {
+ return entityName==null || id==null ?
+ null : session.internalLoad( entityName, id, false, false );
+ }
+
+ public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session)
+ throws HibernateException, SQLException {
+ nullSafeSet(st, value, index, null, session);
+ }
+
+ public void nullSafeSet(PreparedStatement st, Object value, int index, boolean[] settable, SessionImplementor session)
+ throws HibernateException, SQLException {
+
+ Serializable id;
+ String entityName;
+ if (value==null) {
+ id=null;
+ entityName=null;
+ }
+ else {
+ entityName = session.bestGuessEntityName(value);
+ id = ForeignKeys.getEntityIdentifierIfNotUnsaved(entityName, value, session);
+ }
+
+ // metaType is assumed to be single-column type
+ if ( settable==null || settable[0] ) {
+ metaType.nullSafeSet(st, entityName, index, session);
+ }
+ if (settable==null) {
+ identifierType.nullSafeSet(st, id, index+1, session);
+ }
+ else {
+ boolean[] idsettable = new boolean[ settable.length-1 ];
+ System.arraycopy(settable, 1, idsettable, 0, idsettable.length);
+ identifierType.nullSafeSet(st, id, index+1, idsettable, session);
+ }
+ }
+
+ public Class getReturnedClass() {
+ return Object.class;
+ }
+
+ public int[] sqlTypes(Mapping mapping) throws MappingException {
+ return ArrayHelper.join(
+ metaType.sqlTypes(mapping),
+ identifierType.sqlTypes(mapping)
+ );
+ }
+
+ public void setToXMLNode(Node xml, Object value, SessionFactoryImplementor factory) {
+ throw new UnsupportedOperationException("any types cannot be stringified");
+ }
+
+ public String toLoggableString(Object value, SessionFactoryImplementor factory)
+ throws HibernateException {
+ //TODO: terrible implementation!
+ return value==null ?
+ "null" :
+ Hibernate.entity( HibernateProxyHelper.getClassWithoutInitializingProxy(value) )
+ .toLoggableString(value, factory);
+ }
+
+ public Object fromXMLNode(Node xml, Mapping factory) throws HibernateException {
+ throw new UnsupportedOperationException(); //TODO: is this right??
+ }
+
+ public static final class ObjectTypeCacheEntry implements Serializable {
+ String entityName;
+ Serializable id;
+ ObjectTypeCacheEntry(String entityName, Serializable id) {
+ this.entityName = entityName;
+ this.id = id;
+ }
+ }
+
+ public Object assemble(
+ Serializable cached,
+ SessionImplementor session,
+ Object owner)
+ throws HibernateException {
+
+ ObjectTypeCacheEntry e = (ObjectTypeCacheEntry) cached;
+ return e==null ? null : session.internalLoad(e.entityName, e.id, false, false);
+ }
+
+ public Serializable disassemble(Object value, SessionImplementor session, Object owner)
+ throws HibernateException {
+ return value==null ?
+ null :
+ new ObjectTypeCacheEntry(
+ session.bestGuessEntityName(value),
+ ForeignKeys.getEntityIdentifierIfNotUnsaved(
+ session.bestGuessEntityName(value), value, session
+ )
+ );
+ }
+
+ public boolean isAnyType() {
+ return true;
+ }
+
+ public Object replace(
+ Object original,
+ Object target,
+ SessionImplementor session,
+ Object owner,
+ Map copyCache)
+ throws HibernateException {
+ if (original==null) {
+ return null;
+ }
+ else {
+ String entityName = session.bestGuessEntityName(original);
+ Serializable id = ForeignKeys.getEntityIdentifierIfNotUnsaved(
+ entityName,
+ original,
+ session
+ );
+ return session.internalLoad(
+ entityName,
+ id,
+ false,
+ false
+ );
+ }
+ }
+ public CascadeStyle getCascadeStyle(int i) {
+ return CascadeStyle.NONE;
+ }
+
+ public FetchMode getFetchMode(int i) {
+ return FetchMode.SELECT;
+ }
+
+ private static final String[] PROPERTY_NAMES = new String[] { "class", "id" };
+
+ public String[] getPropertyNames() {
+ return PROPERTY_NAMES;
+ }
+
+ public Object getPropertyValue(Object component, int i, SessionImplementor session)
+ throws HibernateException {
+
+ return i==0 ?
+ session.bestGuessEntityName(component) :
+ getIdentifier(component, session);
+ }
+
+ public Object[] getPropertyValues(Object component, SessionImplementor session)
+ throws HibernateException {
+
+ return new Object[] { session.bestGuessEntityName(component), getIdentifier(component, session) };
+ }
+
+ private Serializable getIdentifier(Object value, SessionImplementor session) throws HibernateException {
+ try {
+ return ForeignKeys.getEntityIdentifierIfNotUnsaved( session.bestGuessEntityName(value), value, session );
+ }
+ catch (TransientObjectException toe) {
+ return null;
+ }
+ }
+
+ public Type[] getSubtypes() {
+ return new Type[] { metaType, identifierType };
+ }
+
+ public void setPropertyValues(Object component, Object[] values, EntityMode entityMode)
+ throws HibernateException {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ public Object[] getPropertyValues(Object component, EntityMode entityMode) {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean isComponentType() {
+ return true;
+ }
+
+ public ForeignKeyDirection getForeignKeyDirection() {
+ //return AssociationType.FOREIGN_KEY_TO_PARENT; //this is better but causes a transient object exception...
+ return ForeignKeyDirection.FOREIGN_KEY_FROM_PARENT;
+ }
+
+ public boolean isAssociationType() {
+ return true;
+ }
+
+ public boolean useLHSPrimaryKey() {
+ return false;
+ }
+
+ public Joinable getAssociatedJoinable(SessionFactoryImplementor factory) {
+ throw new UnsupportedOperationException("any types do not have a unique referenced persister");
+ }
+
+ public boolean isModified(Object old, Object current, boolean[] checkable, SessionImplementor session)
+ throws HibernateException {
+ if (current==null) return old!=null;
+ if (old==null) return current!=null;
+ ObjectTypeCacheEntry holder = (ObjectTypeCacheEntry) old;
+ boolean[] idcheckable = new boolean[checkable.length-1];
+ System.arraycopy(checkable, 1, idcheckable, 0, idcheckable.length);
+ return ( checkable[0] && !holder.entityName.equals( session.bestGuessEntityName(current) ) ) ||
+ identifierType.isModified(holder.id, getIdentifier(current, session), idcheckable, session);
+ }
+
+ public String getAssociatedEntityName(SessionFactoryImplementor factory)
+ throws MappingException {
+ throw new UnsupportedOperationException("any types do not have a unique referenced persister");
+ }
+
+ public boolean[] getPropertyNullability() {
+ return null;
+ }
+
+ public String getOnCondition(String alias, SessionFactoryImplementor factory, Map enabledFilters)
+ throws MappingException {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean isReferenceToPrimaryKey() {
+ return true;
+ }
+
+ public String getRHSUniqueKeyPropertyName() {
+ return null;
+ }
+
+ public String getLHSPropertyName() {
+ return null;
+ }
+
+ public boolean isAlwaysDirtyChecked() {
+ return false;
+ }
+
+ public boolean isEmbeddedInXML() {
+ return false;
+ }
+
+ public boolean[] toColumnNullness(Object value, Mapping mapping) {
+ boolean[] result = new boolean[ getColumnSpan(mapping) ];
+ if (value!=null) Arrays.fill(result, true);
+ return result;
+ }
+
+ public boolean isDirty(Object old, Object current, boolean[] checkable, SessionImplementor session)
+ throws HibernateException {
+ //TODO!!!
+ return isDirty(old, current, session);
+ }
+
+ public boolean isEmbedded() {
+ return false;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/ArrayType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/ArrayType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/ArrayType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,121 @@
+//$Id: ArrayType.java 10086 2006-07-05 18:17:27Z steve.ebersole at jboss.com $
+package org.hibernate.type;
+
+import java.io.Serializable;
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.collection.PersistentArrayHolder;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.collection.CollectionPersister;
+
+/**
+ * A type for persistent arrays.
+ * @author Gavin King
+ */
+public class ArrayType extends CollectionType {
+
+ private final Class elementClass;
+ private final Class arrayClass;
+
+ public ArrayType(String role, String propertyRef, Class elementClass, boolean isEmbeddedInXML) {
+ super(role, propertyRef, isEmbeddedInXML);
+ this.elementClass = elementClass;
+ arrayClass = Array.newInstance(elementClass, 0).getClass();
+ }
+
+ public Class getReturnedClass() {
+ return arrayClass;
+ }
+
+ public PersistentCollection instantiate(SessionImplementor session, CollectionPersister persister, Serializable key)
+ throws HibernateException {
+ return new PersistentArrayHolder(session, persister);
+ }
+
+ /**
+ * Not defined for collections of primitive type
+ */
+ public Iterator getElementsIterator(Object collection) {
+ return Arrays.asList( (Object[]) collection ).iterator();
+ }
+
+ public PersistentCollection wrap(SessionImplementor session, Object array) {
+ return new PersistentArrayHolder(session, array);
+ }
+
+ public boolean isArrayType() {
+ return true;
+ }
+
+ public String toLoggableString(Object value, SessionFactoryImplementor factory) throws HibernateException {
+ if ( value == null ) {
+ return "null";
+ }
+ int length = Array.getLength(value);
+ List list = new ArrayList(length);
+ Type elemType = getElementType(factory);
+ for ( int i=0; i<length; i++ ) {
+ list.add( elemType.toLoggableString( Array.get(value, i), factory ) );
+ }
+ return list.toString();
+ }
+
+ public Object instantiateResult(Object original) {
+ return Array.newInstance( elementClass, Array.getLength(original) );
+ }
+
+ public Object replaceElements(
+ Object original,
+ Object target,
+ Object owner,
+ Map copyCache,
+ SessionImplementor session)
+ throws HibernateException {
+
+ int length = Array.getLength(original);
+ if ( length!=Array.getLength(target) ) {
+ //note: this affects the return value!
+ target=instantiateResult(original);
+ }
+
+ Type elemType = getElementType( session.getFactory() );
+ for ( int i=0; i<length; i++ ) {
+ Array.set( target, i, elemType.replace( Array.get(original, i), null, session, owner, copyCache ) );
+ }
+
+ return target;
+
+ }
+
+ public Object instantiate(int anticipatedSize) {
+ throw new UnsupportedOperationException();
+ }
+
+ public Object indexOf(Object array, Object element) {
+ int length = Array.getLength(array);
+ for ( int i=0; i<length; i++ ) {
+ //TODO: proxies!
+ if ( Array.get(array, i)==element ) return new Integer(i);
+ }
+ return null;
+ }
+
+ protected boolean initializeImmediately(EntityMode entityMode) {
+ return true;
+ }
+
+ public boolean hasHolder(EntityMode entityMode) {
+ return true;
+ }
+
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/AssociationType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/AssociationType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/AssociationType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,74 @@
+//$Id: AssociationType.java 7017 2005-06-05 04:31:34Z oneovthafew $
+package org.hibernate.type;
+
+import org.hibernate.MappingException;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.persister.entity.Joinable;
+
+import java.util.Map;
+
+/**
+ * A type that represents some kind of association between entities.
+ * @see org.hibernate.engine.Cascade
+ * @author Gavin King
+ */
+public interface AssociationType extends Type {
+
+ /**
+ * Get the foreign key directionality of this association
+ */
+ public ForeignKeyDirection getForeignKeyDirection();
+
+ //TODO: move these to a new JoinableType abstract class,
+ //extended by EntityType and PersistentCollectionType:
+
+ /**
+ * Is the primary key of the owning entity table
+ * to be used in the join?
+ */
+ public boolean useLHSPrimaryKey();
+ /**
+ * Get the name of a property in the owning entity
+ * that provides the join key (null if the identifier)
+ */
+ public String getLHSPropertyName();
+
+ /**
+ * The name of a unique property of the associated entity
+ * that provides the join key (null if the identifier of
+ * an entity, or key of a collection)
+ */
+ public String getRHSUniqueKeyPropertyName();
+
+ /**
+ * Get the "persister" for this association - a class or
+ * collection persister
+ */
+ public Joinable getAssociatedJoinable(SessionFactoryImplementor factory) throws MappingException;
+
+ /**
+ * Get the entity name of the associated entity
+ */
+ public String getAssociatedEntityName(SessionFactoryImplementor factory) throws MappingException;
+
+ /**
+ * Get the "filtering" SQL fragment that is applied in the
+ * SQL on clause, in addition to the usual join condition
+ */
+ public String getOnCondition(String alias, SessionFactoryImplementor factory, Map enabledFilters)
+ throws MappingException;
+
+ /**
+ * Do we dirty check this association, even when there are
+ * no columns to be updated?
+ */
+ public abstract boolean isAlwaysDirtyChecked();
+
+ public boolean isEmbeddedInXML();
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/BagType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/BagType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/BagType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,50 @@
+//$Id: BagType.java 10086 2006-07-05 18:17:27Z steve.ebersole at jboss.com $
+package org.hibernate.type;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.dom4j.Element;
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.collection.PersistentBag;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.collection.PersistentElementHolder;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.collection.CollectionPersister;
+
+public class BagType extends CollectionType {
+
+ public BagType(String role, String propertyRef, boolean isEmbeddedInXML) {
+ super(role, propertyRef, isEmbeddedInXML);
+ }
+
+ public PersistentCollection instantiate(SessionImplementor session, CollectionPersister persister, Serializable key)
+ throws HibernateException {
+ if ( session.getEntityMode()==EntityMode.DOM4J ) {
+ return new PersistentElementHolder(session, persister, key);
+ }
+ else {
+ return new PersistentBag(session);
+ }
+ }
+
+ public Class getReturnedClass() {
+ return java.util.Collection.class;
+ }
+
+ public PersistentCollection wrap(SessionImplementor session, Object collection) {
+ if ( session.getEntityMode()==EntityMode.DOM4J ) {
+ return new PersistentElementHolder( session, (Element) collection );
+ }
+ else {
+ return new PersistentBag( session, (Collection) collection );
+ }
+ }
+
+ public Object instantiate(int anticipatedSize) {
+ return anticipatedSize <= 0 ? new ArrayList() : new ArrayList( anticipatedSize + 1 );
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/BigDecimalType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/BigDecimalType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/BigDecimalType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,66 @@
+//$Id: BigDecimalType.java 5744 2005-02-16 12:50:19Z oneovthafew $
+package org.hibernate.type;
+
+import java.math.BigDecimal;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+
+/**
+ * <tt>big_decimal</tt>: A type that maps an SQL NUMERIC to a
+ * <tt>java.math.BigDecimal</tt>
+ * @see java.math.BigDecimal
+ * @author Gavin King
+ */
+public class BigDecimalType extends ImmutableType {
+
+ public Object get(ResultSet rs, String name)
+ throws HibernateException, SQLException {
+ return rs.getBigDecimal(name);
+ }
+
+ public void set(PreparedStatement st, Object value, int index)
+ throws HibernateException, SQLException {
+ st.setBigDecimal(index, (BigDecimal) value);
+ }
+
+ public int sqlType() {
+ return Types.NUMERIC;
+ }
+
+ public String toString(Object value) throws HibernateException {
+ return value.toString();
+ }
+
+ public Class getReturnedClass() {
+ return BigDecimal.class;
+ }
+
+ public boolean isEqual(Object x, Object y) {
+ return x==y || ( x!=null && y!=null && ( (BigDecimal) x ).compareTo( (BigDecimal) y )==0 );
+ }
+
+ public int getHashCode(Object x, EntityMode entityMode) {
+ return ( (BigDecimal) x ).intValue();
+ }
+
+ public String getName() {
+ return "big_decimal";
+ }
+
+ public Object fromStringValue(String xml) {
+ return new BigDecimal(xml);
+ }
+
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/BigIntegerType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/BigIntegerType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/BigIntegerType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,79 @@
+//$Id: BigIntegerType.java 7825 2005-08-10 20:23:55Z oneovthafew $
+package org.hibernate.type;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.dialect.Dialect;
+
+/**
+ * <tt>big_integer</tt>: A type that maps an SQL NUMERIC to a
+ * <tt>java.math.BigInteger</tt>
+ * @see java.math.BigInteger
+ * @author Gavin King
+ */
+public class BigIntegerType extends ImmutableType implements DiscriminatorType {
+
+ public String objectToSQLString(Object value, Dialect dialect) throws Exception {
+ return value.toString();
+ }
+
+ public Object stringToObject(String xml) throws Exception {
+ return new BigInteger(xml);
+ }
+
+ public Object get(ResultSet rs, String name)
+ throws HibernateException, SQLException {
+ //return rs.getBigDecimal(name).toBigIntegerExact(); this 1.5 only.
+ BigDecimal bigDecimal = rs.getBigDecimal(name);
+ return bigDecimal==null ? null :
+ bigDecimal.setScale(0, BigDecimal.ROUND_UNNECESSARY).unscaledValue();
+ }
+
+ public void set(PreparedStatement st, Object value, int index)
+ throws HibernateException, SQLException {
+ st.setBigDecimal( index, new BigDecimal( (BigInteger) value ) );
+ }
+
+ public int sqlType() {
+ return Types.NUMERIC;
+ }
+
+ public String toString(Object value) throws HibernateException {
+ return value.toString();
+ }
+
+ public Class getReturnedClass() {
+ return BigInteger.class;
+ }
+
+ public boolean isEqual(Object x, Object y) {
+ return x==y || ( x!=null && y!=null && ( (BigInteger) x ).compareTo( (BigInteger) y )==0 );
+ }
+
+ public int getHashCode(Object x, EntityMode entityMode) {
+ return ( (BigInteger) x ).intValue();
+ }
+
+ public String getName() {
+ return "big_integer";
+ }
+
+ public Object fromStringValue(String xml) {
+ return new BigInteger(xml);
+ }
+
+
+}
+
+
+
+
+
+
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/type/BigIntegerType.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/BinaryType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/BinaryType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/BinaryType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,24 @@
+//$Id: BinaryType.java 10009 2006-06-10 03:24:05Z epbernard $
+package org.hibernate.type;
+
+/**
+ * <tt>binary</tt>: A type that maps an SQL VARBINARY to a Java byte[].
+ * @author Gavin King
+ */
+public class BinaryType extends AbstractBynaryType {
+
+ protected Object toExternalFormat(byte[] bytes) {
+ return bytes;
+ }
+
+ protected byte[] toInternalFormat(Object bytes) {
+ return (byte[]) bytes;
+ }
+
+ public Class getReturnedClass() {
+ return byte[].class;
+ }
+
+ public String getName() { return "binary"; }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/BlobType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/BlobType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/BlobType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,159 @@
+//$Id: BlobType.java 7644 2005-07-25 06:53:09Z oneovthafew $
+package org.hibernate.type;
+
+import java.io.Serializable;
+import java.sql.Blob;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.Map;
+
+import org.dom4j.Node;
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.engine.Mapping;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.lob.BlobImpl;
+import org.hibernate.lob.SerializableBlob;
+import org.hibernate.util.ArrayHelper;
+
+/**
+ * <tt>blob</tt>: A type that maps an SQL BLOB to a java.sql.Blob.
+ * @author Gavin King
+ */
+public class BlobType extends AbstractType {
+
+ public void set(PreparedStatement st, Object value, int index, SessionImplementor session)
+ throws HibernateException, SQLException {
+
+ if (value==null) {
+ st.setNull(index, Types.BLOB);
+ }
+ else {
+
+ if (value instanceof SerializableBlob) {
+ value = ( (SerializableBlob) value ).getWrappedBlob();
+ }
+
+ final boolean useInputStream = session.getFactory().getDialect().useInputStreamToInsertBlob() &&
+ (value instanceof BlobImpl);
+
+ if ( useInputStream ) {
+ BlobImpl blob = (BlobImpl) value;
+ st.setBinaryStream( index, blob.getBinaryStream(), (int) blob.length() );
+ }
+ else {
+ st.setBlob(index, (Blob) value);
+ }
+
+ }
+
+ }
+
+ public Object get(ResultSet rs, String name) throws HibernateException, SQLException {
+ Blob value = rs.getBlob(name);
+ return rs.wasNull() ? null : new SerializableBlob(value);
+ }
+
+ public Class getReturnedClass() {
+ return Blob.class;
+ }
+
+ public boolean isEqual(Object x, Object y, EntityMode entityMode) {
+ return x == y;
+ }
+
+ public int getHashCode(Object x, EntityMode entityMode) {
+ return System.identityHashCode(x);
+ }
+
+ public int compare(Object x, Object y, EntityMode entityMode) {
+ return 0; //lobs cannot be compared
+ }
+
+ public String getName() {
+ return "blob";
+ }
+
+ public Serializable disassemble(Object value, SessionImplementor session, Object owner)
+ throws HibernateException {
+ throw new UnsupportedOperationException("Blobs are not cacheable");
+ }
+
+ public Object deepCopy(Object value, EntityMode entityMode, SessionFactoryImplementor factory) {
+ return value;
+ }
+
+ public Object fromXMLNode(Node xml, Mapping factory) {
+ throw new UnsupportedOperationException("todo");
+ }
+
+ public int getColumnSpan(Mapping mapping) {
+ return 1;
+ }
+
+ public boolean isMutable() {
+ return false;
+ }
+
+ public Object nullSafeGet(ResultSet rs, String name,
+ SessionImplementor session, Object owner)
+ throws HibernateException, SQLException {
+ return get(rs, name);
+ }
+
+ public Object nullSafeGet(ResultSet rs, String[] names,
+ SessionImplementor session, Object owner)
+ throws HibernateException, SQLException {
+ return get( rs, names[0] );
+ }
+
+ public void nullSafeSet(PreparedStatement st, Object value, int index,
+ boolean[] settable, SessionImplementor session)
+ throws HibernateException, SQLException {
+ if ( settable[0] ) set(st, value, index, session);
+ }
+
+ public void nullSafeSet(PreparedStatement st, Object value, int index,
+ SessionImplementor session) throws HibernateException, SQLException {
+ set(st, value, index, session);
+ }
+
+ public Object replace(Object original, Object target,
+ SessionImplementor session, Object owner, Map copyCache)
+ throws HibernateException {
+ //Blobs are ignored by merge()
+ return target;
+ }
+
+ public int[] sqlTypes(Mapping mapping) throws MappingException {
+ return new int[] { Types.BLOB };
+ }
+
+ public void setToXMLNode(Node node, Object value, SessionFactoryImplementor factory) {
+ throw new UnsupportedOperationException("todo");
+ }
+
+ public String toLoggableString(Object value, SessionFactoryImplementor factory)
+ throws HibernateException {
+ return value==null ? "null" : value.toString();
+ }
+
+ public boolean[] toColumnNullness(Object value, Mapping mapping) {
+ return value==null ? ArrayHelper.FALSE : ArrayHelper.TRUE;
+ }
+
+ public boolean isDirty(Object old, Object current, boolean[] checkable, SessionImplementor session) throws HibernateException {
+ return checkable[0] && isDirty(old, current, session);
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/BooleanType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/BooleanType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/BooleanType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,62 @@
+//$Id: BooleanType.java 7825 2005-08-10 20:23:55Z oneovthafew $
+package org.hibernate.type;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+
+import org.hibernate.dialect.Dialect;
+
+/**
+ * <tt>boolean</tt>: A type that maps an SQL BIT to a Java Boolean.
+ * @author Gavin King
+ */
+public class BooleanType extends PrimitiveType implements DiscriminatorType {
+
+ public Serializable getDefaultValue() {
+ return Boolean.FALSE;
+ }
+
+ public Object get(ResultSet rs, String name) throws SQLException {
+ return rs.getBoolean(name) ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ public Class getPrimitiveClass() {
+ return boolean.class;
+ }
+
+ public Class getReturnedClass() {
+ return Boolean.class;
+ }
+
+ public void set(PreparedStatement st, Object value, int index)
+ throws SQLException {
+ st.setBoolean( index, ( (Boolean) value ).booleanValue() );
+ }
+
+ public int sqlType() {
+ return Types.BIT;
+ }
+
+ public String getName() { return "boolean"; }
+
+ public String objectToSQLString(Object value, Dialect dialect) throws Exception {
+ return dialect.toBooleanValueString( ( (Boolean) value ).booleanValue() );
+ }
+
+ public Object stringToObject(String xml) throws Exception {
+ return fromStringValue(xml);
+ }
+
+ public Object fromStringValue(String xml) {
+ return Boolean.valueOf(xml);
+ }
+
+}
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/ByteType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/ByteType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/ByteType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,73 @@
+//$Id: ByteType.java 7825 2005-08-10 20:23:55Z oneovthafew $
+package org.hibernate.type;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.Comparator;
+
+import org.hibernate.util.ComparableComparator;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.SessionImplementor;
+
+/**
+ * <tt>byte</tt>: A type that maps an SQL TINYINT to a Java Byte.
+ * @author Gavin King
+ */
+public class ByteType extends PrimitiveType implements DiscriminatorType, VersionType {
+
+ private static final Byte ZERO = new Byte( (byte) 0 );
+
+ public Serializable getDefaultValue() {
+ return ZERO;
+ }
+
+ public Object get(ResultSet rs, String name) throws SQLException {
+ return new Byte( rs.getByte(name) );
+ }
+
+ public Class getPrimitiveClass() {
+ return byte.class;
+ }
+
+ public Class getReturnedClass() {
+ return Byte.class;
+ }
+
+ public void set(PreparedStatement st, Object value, int index) throws SQLException {
+ st.setByte( index, ( (Byte) value ).byteValue() );
+ }
+
+ public int sqlType() {
+ return Types.TINYINT;
+ }
+
+ public String getName() { return "byte"; }
+
+ public String objectToSQLString(Object value, Dialect dialect) throws Exception {
+ return value.toString();
+ }
+
+ public Object stringToObject(String xml) throws Exception {
+ return new Byte(xml);
+ }
+
+ public Object fromStringValue(String xml) {
+ return new Byte(xml);
+ }
+
+ public Object next(Object current, SessionImplementor session) {
+ return new Byte( (byte) ( ( (Byte) current ).byteValue() + 1 ) );
+ }
+
+ public Object seed(SessionImplementor session) {
+ return ZERO;
+ }
+
+ public Comparator getComparator() {
+ return ComparableComparator.INSTANCE;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/CalendarDateType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/CalendarDateType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/CalendarDateType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,95 @@
+//$Id: CalendarDateType.java 8761 2005-12-06 04:34:23Z oneovthafew $
+package org.hibernate.type;
+
+import java.sql.Date;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+
+import org.hibernate.EntityMode;
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.util.CalendarComparator;
+
+/**
+ * <tt>calendar_date</tt>: A type mapping for a <tt>Calendar</tt>
+ * object that represents a date.
+ * @author Gavin King
+ */
+public class CalendarDateType extends MutableType {
+
+ public Object get(ResultSet rs, String name) throws HibernateException, SQLException {
+
+ Date date = rs.getDate(name);
+ if (date!=null) {
+ Calendar cal = new GregorianCalendar();
+ cal.setTime(date);
+ return cal;
+ }
+ else {
+ return null;
+ }
+
+ }
+
+ public void set(PreparedStatement st, Object value, int index) throws HibernateException, SQLException {
+ final Calendar cal = (Calendar) value;
+ //st.setDate( index, new Date( cal.getTimeInMillis() ), cal ); //JDK 1.5 only
+ st.setDate( index, new Date( cal.getTime().getTime() ), cal );
+ }
+
+ public int sqlType() {
+ return Types.DATE;
+ }
+
+ public String toString(Object value) throws HibernateException {
+ return Hibernate.DATE.toString( ( (Calendar) value ).getTime() );
+ }
+
+ public Object fromStringValue(String xml) throws HibernateException {
+ Calendar result = new GregorianCalendar();
+ result.setTime( ( (java.util.Date) Hibernate.DATE.fromStringValue(xml) ) );
+ return result;
+ }
+
+ public Object deepCopyNotNull(Object value) {
+ return ( (Calendar) value ).clone();
+ }
+
+ public Class getReturnedClass() {
+ return Calendar.class;
+ }
+
+ public int compare(Object x, Object y, EntityMode entityMode) {
+ return CalendarComparator.INSTANCE.compare(x, y);
+ }
+
+ public boolean isEqual(Object x, Object y) {
+ if (x==y) return true;
+ if (x==null || y==null) return false;
+
+ Calendar calendar1 = (Calendar) x;
+ Calendar calendar2 = (Calendar) y;
+
+ return calendar1.get(Calendar.DAY_OF_MONTH) == calendar2.get(Calendar.DAY_OF_MONTH)
+ && calendar1.get(Calendar.MONTH) == calendar2.get(Calendar.MONTH)
+ && calendar1.get(Calendar.YEAR) == calendar2.get(Calendar.YEAR);
+ }
+
+ public int getHashCode(Object x, EntityMode entityMode) {
+ Calendar calendar = (Calendar) x;
+ int hashCode = 1;
+ hashCode = 31 * hashCode + calendar.get(Calendar.DAY_OF_MONTH);
+ hashCode = 31 * hashCode + calendar.get(Calendar.MONTH);
+ hashCode = 31 * hashCode + calendar.get(Calendar.YEAR);
+ return hashCode;
+ }
+
+ public String getName() {
+ return "calendar_date";
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/CalendarType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/CalendarType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/CalendarType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,124 @@
+//$Id: CalendarType.java 7736 2005-08-03 20:03:34Z steveebersole $
+package org.hibernate.type;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Timestamp;
+import java.sql.Types;
+import java.util.Calendar;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.GregorianCalendar;
+
+import org.hibernate.EntityMode;
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.cfg.Environment;
+import org.hibernate.util.CalendarComparator;
+
+/**
+ * <tt>calendar</tt>: A type mapping for a <tt>Calendar</tt> object that
+ * represents a datetime.
+ * @author Gavin King
+ */
+public class CalendarType extends MutableType implements VersionType {
+
+ public Object get(ResultSet rs, String name) throws HibernateException, SQLException {
+
+ Timestamp ts = rs.getTimestamp(name);
+ if (ts!=null) {
+ Calendar cal = new GregorianCalendar();
+ if ( Environment.jvmHasTimestampBug() ) {
+ cal.setTime( new Date( ts.getTime() + ts.getNanos() / 1000000 ) );
+ }
+ else {
+ cal.setTime(ts);
+ }
+ return cal;
+ }
+ else {
+ return null;
+ }
+
+ }
+
+ public void set(PreparedStatement st, Object value, int index) throws HibernateException, SQLException {
+ final Calendar cal = (Calendar) value;
+ //st.setTimestamp( index, new Timestamp( cal.getTimeInMillis() ), cal ); //JDK 1.5 only
+ st.setTimestamp( index, new Timestamp( cal.getTime().getTime() ), cal );
+ }
+
+ public int sqlType() {
+ return Types.TIMESTAMP;
+ }
+
+ public String toString(Object value) throws HibernateException {
+ return Hibernate.TIMESTAMP.toString( ( (Calendar) value ).getTime() );
+ }
+
+ public Object fromStringValue(String xml) throws HibernateException {
+ Calendar result = new GregorianCalendar();
+ result.setTime( ( (Date) Hibernate.TIMESTAMP.fromStringValue(xml) ) );
+ return result;
+ }
+
+ public Object deepCopyNotNull(Object value) throws HibernateException {
+ return ( (Calendar) value ).clone();
+ }
+
+ public Class getReturnedClass() {
+ return Calendar.class;
+ }
+
+ public int compare(Object x, Object y, EntityMode entityMode) {
+ return CalendarComparator.INSTANCE.compare(x, y);
+ }
+
+ public boolean isEqual(Object x, Object y) {
+ if (x==y) return true;
+ if (x==null || y==null) return false;
+
+ Calendar calendar1 = (Calendar) x;
+ Calendar calendar2 = (Calendar) y;
+
+ return calendar1.get(Calendar.MILLISECOND) == calendar2.get(Calendar.MILLISECOND)
+ && calendar1.get(Calendar.SECOND) == calendar2.get(Calendar.SECOND)
+ && calendar1.get(Calendar.MINUTE) == calendar2.get(Calendar.MINUTE)
+ && calendar1.get(Calendar.HOUR_OF_DAY) == calendar2.get(Calendar.HOUR_OF_DAY)
+ && calendar1.get(Calendar.DAY_OF_MONTH) == calendar2.get(Calendar.DAY_OF_MONTH)
+ && calendar1.get(Calendar.MONTH) == calendar2.get(Calendar.MONTH)
+ && calendar1.get(Calendar.YEAR) == calendar2.get(Calendar.YEAR);
+ }
+
+ public int getHashCode(Object x, EntityMode entityMode) {
+ Calendar calendar = (Calendar) x;
+ int hashCode = 1;
+ hashCode = 31 * hashCode + calendar.get(Calendar.MILLISECOND);
+ hashCode = 31 * hashCode + calendar.get(Calendar.SECOND);
+ hashCode = 31 * hashCode + calendar.get(Calendar.MINUTE);
+ hashCode = 31 * hashCode + calendar.get(Calendar.HOUR_OF_DAY);
+ hashCode = 31 * hashCode + calendar.get(Calendar.DAY_OF_MONTH);
+ hashCode = 31 * hashCode + calendar.get(Calendar.MONTH);
+ hashCode = 31 * hashCode + calendar.get(Calendar.YEAR);
+ return hashCode;
+ }
+
+ public String getName() {
+ return "calendar";
+ }
+
+ public Object next(Object current, SessionImplementor session) {
+ return seed( session );
+ }
+
+ public Object seed(SessionImplementor session) {
+ return Calendar.getInstance();
+ }
+
+ public Comparator getComparator() {
+ return CalendarComparator.INSTANCE;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/CharArrayType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/CharArrayType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/CharArrayType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,23 @@
+//$Id: $
+package org.hibernate.type;
+
+/**
+ * put char[] into VARCHAR
+ * @author Emmanuel Bernard
+ */
+public class CharArrayType extends AbstractCharArrayType {
+
+ protected Object toExternalFormat(char[] chars) {
+ return chars;
+ }
+
+ protected char[] toInternalFormat(Object chars) {
+ return (char[]) chars;
+ }
+
+ public Class getReturnedClass() {
+ return char[].class;
+ }
+
+ public String getName() { return "characters"; }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/CharBooleanType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/CharBooleanType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/CharBooleanType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,70 @@
+//$Id: CharBooleanType.java 7825 2005-08-10 20:23:55Z oneovthafew $
+package org.hibernate.type;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+
+import org.hibernate.HibernateException;
+import org.hibernate.dialect.Dialect;
+
+
+/**
+ * Superclass for types that map Java boolean to SQL CHAR(1).
+ * @author Gavin King
+ */
+public abstract class CharBooleanType extends BooleanType {
+
+ protected abstract String getTrueString();
+ protected abstract String getFalseString();
+
+ public Object get(ResultSet rs, String name) throws SQLException {
+ String code = rs.getString(name);
+ if ( code==null || code.length()==0 ) {
+ return null;
+ }
+ else {
+ return getTrueString().equalsIgnoreCase( code.trim() ) ?
+ Boolean.TRUE : Boolean.FALSE;
+ }
+ }
+
+ public void set(PreparedStatement st, Object value, int index)
+ throws SQLException {
+ st.setString( index, toCharacter(value) );
+
+ }
+
+ public int sqlType() {
+ return Types.CHAR;
+ }
+
+ private String toCharacter(Object value) {
+ return ( (Boolean) value ).booleanValue() ? getTrueString() : getFalseString();
+ }
+
+ public String objectToSQLString(Object value, Dialect dialect) throws Exception {
+ return "'" + toCharacter(value) + "'";
+ }
+
+ public Object stringToObject(String xml) throws Exception {
+ if ( getTrueString().equalsIgnoreCase(xml) ) {
+ return Boolean.TRUE;
+ }
+ else if ( getFalseString().equalsIgnoreCase(xml) ) {
+ return Boolean.FALSE;
+ }
+ else {
+ throw new HibernateException("Could not interpret: " + xml);
+ }
+ }
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/CharacterArrayType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/CharacterArrayType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/CharacterArrayType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,37 @@
+//$Id: $
+package org.hibernate.type;
+
+import org.hibernate.HibernateException;
+
+/**
+ * Bridge Character[] and VARCHAR
+ * @author Emmanuel Bernard
+ */
+public class CharacterArrayType extends AbstractCharArrayType {
+ protected Object toExternalFormat(char[] chars) {
+ if (chars == null) return null;
+ Character[] characters = new Character[chars.length];
+ for (int i = 0 ; i < chars.length ; i++) {
+ characters[i] = new Character( chars[i] );
+ }
+ return characters;
+ }
+
+ protected char[] toInternalFormat(Object value) {
+ if (value == null) return null;
+ Character[] characters = (Character[]) value;
+ char[] chars = new char[characters.length];
+ for (int i = 0 ; i < characters.length ; i++) {
+ if (characters[i] == null)
+ throw new HibernateException("Unable to store an Character[] when one of its element is null");
+ chars[i] = characters[i].charValue();
+ }
+ return chars;
+ }
+
+ public Class getReturnedClass() {
+ return Character[].class;
+ }
+
+ public String getName() { return "wrapper-characters"; }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/CharacterType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/CharacterType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/CharacterType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,68 @@
+//$Id: CharacterType.java 7825 2005-08-10 20:23:55Z oneovthafew $
+package org.hibernate.type;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+
+import org.hibernate.MappingException;
+import org.hibernate.dialect.Dialect;
+
+/**
+ * <tt>character</tt>: A type that maps an SQL CHAR(1) to a Java Character.
+ * @author Gavin King
+ */
+public class CharacterType extends PrimitiveType implements DiscriminatorType {
+
+ public Serializable getDefaultValue() {
+ throw new UnsupportedOperationException("not a valid id type");
+ }
+
+ public Object get(ResultSet rs, String name) throws SQLException {
+ String str = rs.getString(name);
+ if (str==null) {
+ return null;
+ }
+ else {
+ return new Character( str.charAt(0) );
+ }
+ }
+
+ public Class getPrimitiveClass() {
+ return char.class;
+ }
+
+ public Class getReturnedClass() {
+ return Character.class;
+ }
+
+ public void set(PreparedStatement st, Object value, int index) throws SQLException {
+ st.setString( index, (value).toString() );
+ }
+
+ public int sqlType() {
+ return Types.CHAR;
+ }
+ public String getName() { return "character"; }
+
+ public String objectToSQLString(Object value, Dialect dialect) throws Exception {
+ return '\'' + value.toString() + '\'';
+ }
+
+ public Object stringToObject(String xml) throws Exception {
+ if ( xml.length() != 1 ) throw new MappingException("multiple or zero characters found parsing string");
+ return new Character( xml.charAt(0) );
+ }
+
+ public Object fromStringValue(String xml) {
+ return new Character( xml.charAt(0) );
+ }
+
+}
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/ClassType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/ClassType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/ClassType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,69 @@
+//$Id: ClassType.java 4582 2004-09-25 11:22:20Z oneovthafew $
+package org.hibernate.type;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.util.ReflectHelper;
+
+/**
+ * <tt>class</tt>: A type that maps an SQL VARCHAR to a Java Class.
+ * @author Gavin King
+ */
+public class ClassType extends ImmutableType {
+
+ public Object get(ResultSet rs, String name) throws HibernateException, SQLException {
+ String str = (String) Hibernate.STRING.get(rs, name);
+ if (str == null) {
+ return null;
+ }
+ else {
+ try {
+ return ReflectHelper.classForName(str);
+ }
+ catch (ClassNotFoundException cnfe) {
+ throw new HibernateException("Class not found: " + str);
+ }
+ }
+ }
+
+ public void set(PreparedStatement st, Object value, int index) throws HibernateException, SQLException {
+ //TODO: would be nice to handle proxy classes elegantly!
+ Hibernate.STRING.set(st, ( (Class) value ).getName(), index);
+ }
+
+ public int sqlType() {
+ return Hibernate.STRING.sqlType();
+ }
+
+ public String toString(Object value) throws HibernateException {
+ return ( (Class) value ).getName();
+ }
+
+ public Class getReturnedClass() {
+ return Class.class;
+ }
+
+ public String getName() {
+ return "class";
+ }
+
+ public Object fromStringValue(String xml) throws HibernateException {
+ try {
+ return ReflectHelper.classForName(xml);
+ }
+ catch (ClassNotFoundException cnfe) {
+ throw new HibernateException("could not parse xml", cnfe);
+ }
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/ClobType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/ClobType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/ClobType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,169 @@
+//$Id: ClobType.java 7644 2005-07-25 06:53:09Z oneovthafew $
+package org.hibernate.type;
+
+import java.io.Serializable;
+import java.sql.Clob;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.Map;
+
+import org.dom4j.Node;
+import org.hibernate.EntityMode;
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.engine.Mapping;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.lob.ClobImpl;
+import org.hibernate.lob.SerializableClob;
+import org.hibernate.util.ArrayHelper;
+
+/**
+ * <tt>clob</tt>: A type that maps an SQL CLOB to a java.sql.Clob.
+ * @author Gavin King
+ */
+public class ClobType extends AbstractType {
+
+ public void set(PreparedStatement st, Object value, int index, SessionImplementor session)
+ throws HibernateException, SQLException {
+
+ if (value==null) {
+ st.setNull(index, Types.CLOB);
+ }
+ else {
+
+ if (value instanceof SerializableClob) {
+ value = ( (SerializableClob) value ).getWrappedClob();
+ }
+
+ final boolean useReader = session.getFactory().getDialect().useInputStreamToInsertBlob() &&
+ (value instanceof ClobImpl);
+
+ if ( useReader ) {
+ ClobImpl clob = (ClobImpl) value;
+ st.setCharacterStream( index, clob.getCharacterStream(), (int) clob.length() );
+ }
+ else {
+ st.setClob(index, (Clob) value);
+ }
+
+ }
+
+ }
+
+ public Object get(ResultSet rs, String name) throws HibernateException, SQLException {
+ Clob value = rs.getClob(name);
+ return rs.wasNull() ? null : new SerializableClob(value);
+ }
+
+ public Class getReturnedClass() {
+ return Clob.class;
+ }
+
+ public boolean isEqual(Object x, Object y, EntityMode entityMode) {
+ return x == y;
+ }
+
+ public int getHashCode(Object x, EntityMode entityMode) {
+ return System.identityHashCode(x);
+ }
+
+ public int compare(Object x, Object y, EntityMode entityMode) {
+ return 0; //lobs cannot be compared
+ }
+
+ public String getName() {
+ return "clob";
+ }
+
+ public Serializable disassemble(Object value, SessionImplementor session, Object owner)
+ throws HibernateException {
+ throw new UnsupportedOperationException("Clobs are not cacheable");
+ }
+
+ public Object deepCopy(Object value, EntityMode entityMode, SessionFactoryImplementor factory) {
+ return value;
+ }
+
+ public Object fromXMLNode(Node xml, Mapping factory) {
+ return Hibernate.createClob( xml.getText() );
+ }
+
+ public int getColumnSpan(Mapping mapping) {
+ return 1;
+ }
+
+ public boolean isMutable() {
+ return false;
+ }
+
+ public Object nullSafeGet(ResultSet rs, String name,
+ SessionImplementor session, Object owner)
+ throws HibernateException, SQLException {
+ return get(rs, name);
+ }
+
+ public Object nullSafeGet(ResultSet rs, String[] names,
+ SessionImplementor session, Object owner)
+ throws HibernateException, SQLException {
+ return get( rs, names[0] );
+ }
+
+ public void nullSafeSet(PreparedStatement st, Object value, int index,
+ boolean[] settable, SessionImplementor session)
+ throws HibernateException, SQLException {
+ if ( settable[0] ) set(st, value, index, session);
+ }
+
+ public void nullSafeSet(PreparedStatement st, Object value, int index,
+ SessionImplementor session) throws HibernateException, SQLException {
+ set(st, value, index, session);
+ }
+
+ public Object replace(Object original, Object target,
+ SessionImplementor session, Object owner, Map copyCache)
+ throws HibernateException {
+ //Clobs are ignored by merge() operation
+ return target;
+ }
+
+ public int[] sqlTypes(Mapping mapping) throws MappingException {
+ return new int[] { Types.CLOB };
+ }
+
+ public void setToXMLNode(Node node, Object value, SessionFactoryImplementor factory) {
+ if (value!=null) {
+ Clob clob = (Clob) value;
+ try {
+ int len = (int) clob.length();
+ node.setText( clob.getSubString(0, len) );
+ }
+ catch (SQLException sqle) {
+ throw new HibernateException("could not read XML from Clob", sqle);
+ }
+ }
+ }
+
+ public String toLoggableString(Object value, SessionFactoryImplementor factory)
+ throws HibernateException {
+ return value==null ? "null" : value.toString();
+ }
+
+ public boolean[] toColumnNullness(Object value, Mapping mapping) {
+ return value==null ? ArrayHelper.FALSE : ArrayHelper.TRUE;
+ }
+
+ public boolean isDirty(Object old, Object current, boolean[] checkable, SessionImplementor session) throws HibernateException {
+ return checkable[0] && isDirty(old, current, session);
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/CollectionType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/CollectionType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/CollectionType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,639 @@
+// $Id: CollectionType.java 11302 2007-03-19 20:44:11Z steve.ebersole at jboss.com $
+package org.hibernate.type;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.dom4j.Element;
+import org.dom4j.Node;
+import org.hibernate.EntityMode;
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.engine.CollectionKey;
+import org.hibernate.engine.EntityEntry;
+import org.hibernate.engine.Mapping;
+import org.hibernate.engine.PersistenceContext;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.persister.collection.QueryableCollection;
+import org.hibernate.persister.entity.Joinable;
+import org.hibernate.proxy.HibernateProxy;
+import org.hibernate.proxy.LazyInitializer;
+import org.hibernate.util.ArrayHelper;
+import org.hibernate.util.MarkerObject;
+
+/**
+ * A type that handles Hibernate <tt>PersistentCollection</tt>s (including arrays).
+ *
+ * @author Gavin King
+ */
+public abstract class CollectionType extends AbstractType implements AssociationType {
+
+ private static final Object NOT_NULL_COLLECTION = new MarkerObject( "NOT NULL COLLECTION" );
+ public static final Object UNFETCHED_COLLECTION = new MarkerObject( "UNFETCHED COLLECTION" );
+
+ private final String role;
+ private final String foreignKeyPropertyName;
+ private final boolean isEmbeddedInXML;
+
+ public CollectionType(String role, String foreignKeyPropertyName, boolean isEmbeddedInXML) {
+ this.role = role;
+ this.foreignKeyPropertyName = foreignKeyPropertyName;
+ this.isEmbeddedInXML = isEmbeddedInXML;
+ }
+
+ public boolean isEmbeddedInXML() {
+ return isEmbeddedInXML;
+ }
+
+ public String getRole() {
+ return role;
+ }
+
+ public Object indexOf(Object collection, Object element) {
+ throw new UnsupportedOperationException( "generic collections don't have indexes" );
+ }
+
+ public boolean contains(Object collection, Object childObject, SessionImplementor session) {
+ // we do not have to worry about queued additions to uninitialized
+ // collections, since they can only occur for inverse collections!
+ Iterator elems = getElementsIterator( collection, session );
+ while ( elems.hasNext() ) {
+ Object element = elems.next();
+ // worrying about proxies is perhaps a little bit of overkill here...
+ if ( element instanceof HibernateProxy ) {
+ LazyInitializer li = ( (HibernateProxy) element ).getHibernateLazyInitializer();
+ if ( !li.isUninitialized() ) element = li.getImplementation();
+ }
+ if ( element == childObject ) return true;
+ }
+ return false;
+ }
+
+ public boolean isCollectionType() {
+ return true;
+ }
+
+ public final boolean isEqual(Object x, Object y, EntityMode entityMode) {
+ return x == y
+ || ( x instanceof PersistentCollection && ( (PersistentCollection) x ).isWrapper( y ) )
+ || ( y instanceof PersistentCollection && ( (PersistentCollection) y ).isWrapper( x ) );
+ }
+
+ public int compare(Object x, Object y, EntityMode entityMode) {
+ return 0; // collections cannot be compared
+ }
+
+ public int getHashCode(Object x, EntityMode entityMode) {
+ throw new UnsupportedOperationException( "cannot perform lookups on collections" );
+ }
+
+ /**
+ * Instantiate an uninitialized collection wrapper or holder. Callers MUST add the holder to the
+ * persistence context!
+ *
+ * @param session The session from which the request is originating.
+ * @param persister The underlying collection persister (metadata)
+ * @param key The owner key.
+ * @return The instantiated collection.
+ */
+ public abstract PersistentCollection instantiate(SessionImplementor session, CollectionPersister persister, Serializable key);
+
+ public Object nullSafeGet(ResultSet rs, String name, SessionImplementor session, Object owner) throws SQLException {
+ return nullSafeGet( rs, new String[] { name }, session, owner );
+ }
+
+ public Object nullSafeGet(ResultSet rs, String[] name, SessionImplementor session, Object owner)
+ throws HibernateException, SQLException {
+ return resolve( null, session, owner );
+ }
+
+ public final void nullSafeSet(PreparedStatement st, Object value, int index, boolean[] settable,
+ SessionImplementor session) throws HibernateException, SQLException {
+ //NOOP
+ }
+
+ public void nullSafeSet(PreparedStatement st, Object value, int index,
+ SessionImplementor session) throws HibernateException, SQLException {
+ }
+
+ public int[] sqlTypes(Mapping session) throws MappingException {
+ return ArrayHelper.EMPTY_INT_ARRAY;
+ }
+
+ public int getColumnSpan(Mapping session) throws MappingException {
+ return 0;
+ }
+
+ public String toLoggableString(Object value, SessionFactoryImplementor factory)
+ throws HibernateException {
+ if ( value == null ) {
+ return "null";
+ }
+ else if ( !Hibernate.isInitialized( value ) ) {
+ return "<uninitialized>";
+ }
+ else {
+ return renderLoggableString( value, factory );
+ }
+ }
+
+ protected String renderLoggableString(Object value, SessionFactoryImplementor factory)
+ throws HibernateException {
+ if ( Element.class.isInstance( value ) ) {
+ // for DOM4J "collections" only
+ // TODO: it would be better if this was done at the higher level by Printer
+ return ( ( Element ) value ).asXML();
+ }
+ else {
+ List list = new ArrayList();
+ Type elemType = getElementType( factory );
+ Iterator iter = getElementsIterator( value );
+ while ( iter.hasNext() ) {
+ list.add( elemType.toLoggableString( iter.next(), factory ) );
+ }
+ return list.toString();
+ }
+ }
+
+ public Object deepCopy(Object value, EntityMode entityMode, SessionFactoryImplementor factory)
+ throws HibernateException {
+ return value;
+ }
+
+ public String getName() {
+ return getReturnedClass().getName() + '(' + getRole() + ')';
+ }
+
+ /**
+ * Get an iterator over the element set of the collection, which may not yet be wrapped
+ *
+ * @param collection The collection to be iterated
+ * @param session The session from which the request is originating.
+ * @return The iterator.
+ */
+ public Iterator getElementsIterator(Object collection, SessionImplementor session) {
+ if ( session.getEntityMode()==EntityMode.DOM4J ) {
+ final SessionFactoryImplementor factory = session.getFactory();
+ final CollectionPersister persister = factory.getCollectionPersister( getRole() );
+ final Type elementType = persister.getElementType();
+
+ List elements = ( (Element) collection ).elements( persister.getElementNodeName() );
+ ArrayList results = new ArrayList();
+ for ( int i=0; i<elements.size(); i++ ) {
+ Element value = (Element) elements.get(i);
+ results.add( elementType.fromXMLNode( value, factory ) );
+ }
+ return results.iterator();
+ }
+ else {
+ return getElementsIterator(collection);
+ }
+ }
+
+ /**
+ * Get an iterator over the element set of the collection in POJO mode
+ *
+ * @param collection The collection to be iterated
+ * @return The iterator.
+ */
+ protected Iterator getElementsIterator(Object collection) {
+ return ( (Collection) collection ).iterator();
+ }
+
+ public boolean isMutable() {
+ return false;
+ }
+
+ public Serializable disassemble(Object value, SessionImplementor session, Object owner)
+ throws HibernateException {
+ //remember the uk value
+
+ //This solution would allow us to eliminate the owner arg to disassemble(), but
+ //what if the collection was null, and then later had elements added? seems unsafe
+ //session.getPersistenceContext().getCollectionEntry( (PersistentCollection) value ).getKey();
+
+ final Serializable key = getKeyOfOwner(owner, session);
+ if (key==null) {
+ return null;
+ }
+ else {
+ return getPersister(session)
+ .getKeyType()
+ .disassemble( key, session, owner );
+ }
+ }
+
+ public Object assemble(Serializable cached, SessionImplementor session, Object owner)
+ throws HibernateException {
+ //we must use the "remembered" uk value, since it is
+ //not available from the EntityEntry during assembly
+ if (cached==null) {
+ return null;
+ }
+ else {
+ final Serializable key = (Serializable) getPersister(session)
+ .getKeyType()
+ .assemble( cached, session, owner);
+ return resolveKey( key, session, owner );
+ }
+ }
+
+ /**
+ * Is the owning entity versioned?
+ *
+ * @param session The session from which the request is originating.
+ * @return True if the collection owner is versioned; false otherwise.
+ * @throws org.hibernate.MappingException Indicates our persister could not be located.
+ */
+ private boolean isOwnerVersioned(SessionImplementor session) throws MappingException {
+ return getPersister( session ).getOwnerEntityPersister().isVersioned();
+ }
+
+ private CollectionPersister getPersister(SessionImplementor session) {
+ return session.getFactory().getCollectionPersister( role );
+ }
+
+ public boolean isDirty(Object old, Object current, SessionImplementor session)
+ throws HibernateException {
+
+ // collections don't dirty an unversioned parent entity
+
+ // TODO: I don't really like this implementation; it would be better if
+ // this was handled by searchForDirtyCollections()
+ return isOwnerVersioned( session ) && super.isDirty( old, current, session );
+ // return false;
+
+ }
+
+ public boolean isDirty(Object old, Object current, boolean[] checkable, SessionImplementor session)
+ throws HibernateException {
+ return isDirty(old, current, session);
+ }
+
+ /**
+ * Wrap the naked collection instance in a wrapper, or instantiate a
+ * holder. Callers <b>MUST</b> add the holder to the persistence context!
+ *
+ * @param session The session from which the request is originating.
+ * @param collection The bare collection to be wrapped.
+ * @return The wrapped collection.
+ */
+ public abstract PersistentCollection wrap(SessionImplementor session, Object collection);
+
+ /**
+ * Note: return true because this type is castable to <tt>AssociationType</tt>. Not because
+ * all collections are associations.
+ */
+ public boolean isAssociationType() {
+ return true;
+ }
+
+ public ForeignKeyDirection getForeignKeyDirection() {
+ return ForeignKeyDirection.FOREIGN_KEY_TO_PARENT;
+ }
+
+ /**
+ * Get the key value from the owning entity instance, usually the identifier, but might be some
+ * other unique key, in the case of property-ref
+ *
+ * @param owner The collection owner
+ * @param session The session from which the request is originating.
+ * @return The collection owner's key
+ */
+ public Serializable getKeyOfOwner(Object owner, SessionImplementor session) {
+
+ EntityEntry entityEntry = session.getPersistenceContext().getEntry( owner );
+ if ( entityEntry == null ) return null; // This just handles a particular case of component
+ // projection, perhaps get rid of it and throw an exception
+
+ if ( foreignKeyPropertyName == null ) {
+ return entityEntry.getId();
+ }
+ else {
+ // TODO: at the point where we are resolving collection references, we don't
+ // know if the uk value has been resolved (depends if it was earlier or
+ // later in the mapping document) - now, we could try and use e.getStatus()
+ // to decide to semiResolve(), trouble is that initializeEntity() reuses
+ // the same array for resolved and hydrated values
+ Object id;
+ if ( entityEntry.getLoadedState() != null ) {
+ id = entityEntry.getLoadedValue( foreignKeyPropertyName );
+ }
+ else {
+ id = entityEntry.getPersister().getPropertyValue( owner, foreignKeyPropertyName, session.getEntityMode() );
+ }
+
+ // NOTE VERY HACKISH WORKAROUND!!
+ Type keyType = getPersister( session ).getKeyType();
+ if ( !keyType.getReturnedClass().isInstance( id ) ) {
+ id = (Serializable) keyType.semiResolve(
+ entityEntry.getLoadedValue( foreignKeyPropertyName ),
+ session,
+ owner
+ );
+ }
+
+ return (Serializable) id;
+ }
+ }
+
+ public Object hydrate(ResultSet rs, String[] name, SessionImplementor session, Object owner) {
+ // can't just return null here, since that would
+ // cause an owning component to become null
+ return NOT_NULL_COLLECTION;
+ }
+
+ public Object resolve(Object value, SessionImplementor session, Object owner)
+ throws HibernateException {
+
+ return resolveKey( getKeyOfOwner( owner, session ), session, owner );
+ }
+
+ private Object resolveKey(Serializable key, SessionImplementor session, Object owner) {
+ // if (key==null) throw new AssertionFailure("owner identifier unknown when re-assembling
+ // collection reference");
+ return key == null ? null : // TODO: can this case really occur??
+ getCollection( key, session, owner );
+ }
+
+ public Object semiResolve(Object value, SessionImplementor session, Object owner)
+ throws HibernateException {
+ throw new UnsupportedOperationException(
+ "collection mappings may not form part of a property-ref" );
+ }
+
+ public boolean isArrayType() {
+ return false;
+ }
+
+ public boolean useLHSPrimaryKey() {
+ return foreignKeyPropertyName == null;
+ }
+
+ public String getRHSUniqueKeyPropertyName() {
+ return null;
+ }
+
+ public Joinable getAssociatedJoinable(SessionFactoryImplementor factory)
+ throws MappingException {
+ return (Joinable) factory.getCollectionPersister( role );
+ }
+
+ public boolean isModified(Object old, Object current, boolean[] checkable, SessionImplementor session) throws HibernateException {
+ return false;
+ }
+
+ public String getAssociatedEntityName(SessionFactoryImplementor factory)
+ throws MappingException {
+ try {
+
+ QueryableCollection collectionPersister = (QueryableCollection) factory
+ .getCollectionPersister( role );
+
+ if ( !collectionPersister.getElementType().isEntityType() ) {
+ throw new MappingException(
+ "collection was not an association: " +
+ collectionPersister.getRole()
+ );
+ }
+
+ return collectionPersister.getElementPersister().getEntityName();
+
+ }
+ catch (ClassCastException cce) {
+ throw new MappingException( "collection role is not queryable " + role );
+ }
+ }
+
+ /**
+ * Replace the elements of a collection with the elements of another collection.
+ *
+ * @param original The 'source' of the replacement elements (where we copy from)
+ * @param target The target of the replacement elements (where we copy to)
+ * @param owner The owner of the collection being merged
+ * @param copyCache The map of elements already replaced.
+ * @param session The session from which the merge event originated.
+ * @return The merged collection.
+ */
+ public Object replaceElements(
+ Object original,
+ Object target,
+ Object owner,
+ Map copyCache,
+ SessionImplementor session) {
+ // TODO: does not work for EntityMode.DOM4J yet!
+ java.util.Collection result = ( java.util.Collection ) target;
+ result.clear();
+
+ // copy elements into newly empty target collection
+ Type elemType = getElementType( session.getFactory() );
+ Iterator iter = ( (java.util.Collection) original ).iterator();
+ while ( iter.hasNext() ) {
+ result.add( elemType.replace( iter.next(), null, session, owner, copyCache ) );
+ }
+
+ // if the original is a PersistentCollection, and that original
+ // was not flagged as dirty, then reset the target's dirty flag
+ // here after the copy operation.
+ // </p>
+ // One thing to be careful of here is a "bare" original collection
+ // in which case we should never ever ever reset the dirty flag
+ // on the target because we simply do not know...
+ if ( original instanceof PersistentCollection ) {
+ if ( result instanceof PersistentCollection ) {
+ if ( ! ( ( PersistentCollection ) original ).isDirty() ) {
+ ( ( PersistentCollection ) result ).clearDirty();
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Instantiate a new "underlying" collection exhibiting the same capacity
+ * charactersitcs and the passed "original".
+ *
+ * @param original The original collection.
+ * @return The newly instantiated collection.
+ */
+ protected Object instantiateResult(Object original) {
+ // by default just use an unanticipated capacity since we don't
+ // know how to extract the capacity to use from original here...
+ return instantiate( -1 );
+ }
+
+ /**
+ * Instantiate an empty instance of the "underlying" collection (not a wrapper),
+ * but with the given anticipated size (i.e. accounting for initial capacity
+ * and perhaps load factor).
+ *
+ * @param anticipatedSize The anticipated size of the instaniated collection
+ * after we are done populating it.
+ * @return A newly instantiated collection to be wrapped.
+ */
+ public abstract Object instantiate(int anticipatedSize);
+
+ /**
+ * {@inheritDoc}
+ */
+ public Object replace(
+ final Object original,
+ final Object target,
+ final SessionImplementor session,
+ final Object owner,
+ final Map copyCache) throws HibernateException {
+ if ( original == null ) {
+ return null;
+ }
+ if ( !Hibernate.isInitialized( original ) ) {
+ return target;
+ }
+
+ // for a null target, or a target which is the same as the original, we
+ // need to put the merged elements in a new collection
+ Object result = target == null || target == original ? instantiateResult( original ) : target;
+
+ //for arrays, replaceElements() may return a different reference, since
+ //the array length might not match
+ result = replaceElements( original, result, owner, copyCache, session );
+
+ if (original==target) {
+ //get the elements back into the target
+ //TODO: this is a little inefficient, don't need to do a whole
+ // deep replaceElements() call
+ replaceElements( result, target, owner, copyCache, session );
+ result = target;
+ }
+
+ return result;
+ }
+
+ /**
+ * Get the Hibernate type of the collection elements
+ *
+ * @param factory The session factory.
+ * @return The type of the collection elements
+ * @throws MappingException Indicates the underlying persister could not be located.
+ */
+ public final Type getElementType(SessionFactoryImplementor factory) throws MappingException {
+ return factory.getCollectionPersister( getRole() ).getElementType();
+ }
+
+ public String toString() {
+ return getClass().getName() + '(' + getRole() + ')';
+ }
+
+ public String getOnCondition(String alias, SessionFactoryImplementor factory, Map enabledFilters)
+ throws MappingException {
+ return getAssociatedJoinable( factory ).filterFragment( alias, enabledFilters );
+ }
+
+ /**
+ * instantiate a collection wrapper (called when loading an object)
+ *
+ * @param key The collection owner key
+ * @param session The session from which the request is originating.
+ * @param owner The collection owner
+ * @return The collection
+ */
+ public Object getCollection(Serializable key, SessionImplementor session, Object owner) {
+
+ CollectionPersister persister = getPersister( session );
+ final PersistenceContext persistenceContext = session.getPersistenceContext();
+ final EntityMode entityMode = session.getEntityMode();
+
+ if (entityMode==EntityMode.DOM4J && !isEmbeddedInXML) {
+ return UNFETCHED_COLLECTION;
+ }
+
+ // check if collection is currently being loaded
+ PersistentCollection collection = persistenceContext.getLoadContexts().locateLoadingCollection( persister, key );
+
+ if ( collection == null ) {
+
+ // check if it is already completely loaded, but unowned
+ collection = persistenceContext.useUnownedCollection( new CollectionKey(persister, key, entityMode) );
+
+ if ( collection == null ) {
+ // create a new collection wrapper, to be initialized later
+ collection = instantiate( session, persister, key );
+ collection.setOwner(owner);
+
+ persistenceContext.addUninitializedCollection( persister, collection, key );
+
+ // some collections are not lazy:
+ if ( initializeImmediately( entityMode ) ) {
+ session.initializeCollection( collection, false );
+ }
+ else if ( !persister.isLazy() ) {
+ persistenceContext.addNonLazyCollection( collection );
+ }
+
+ if ( hasHolder( entityMode ) ) {
+ session.getPersistenceContext().addCollectionHolder( collection );
+ }
+
+ }
+
+ }
+
+ collection.setOwner(owner);
+
+ return collection.getValue();
+ }
+
+ public boolean hasHolder(EntityMode entityMode) {
+ return entityMode == EntityMode.DOM4J;
+ }
+
+ protected boolean initializeImmediately(EntityMode entityMode) {
+ return entityMode == EntityMode.DOM4J;
+ }
+
+ public String getLHSPropertyName() {
+ return foreignKeyPropertyName;
+ }
+
+ public boolean isXMLElement() {
+ return true;
+ }
+
+ public Object fromXMLNode(Node xml, Mapping factory) throws HibernateException {
+ return xml;
+ }
+
+ public void setToXMLNode(Node node, Object value, SessionFactoryImplementor factory)
+ throws HibernateException {
+ if ( !isEmbeddedInXML ) {
+ node.detach();
+ }
+ else {
+ replaceNode( node, (Element) value );
+ }
+ }
+
+ /**
+ * We always need to dirty check the collection because we sometimes
+ * need to incremement version number of owner and also because of
+ * how assemble/disassemble is implemented for uks
+ */
+ public boolean isAlwaysDirtyChecked() {
+ return true;
+ }
+
+ public boolean[] toColumnNullness(Object value, Mapping mapping) {
+ return ArrayHelper.EMPTY_BOOLEAN_ARRAY;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/ComponentType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/ComponentType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/ComponentType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,636 @@
+//$Id: ComponentType.java 10119 2006-07-14 00:09:19Z steve.ebersole at jboss.com $
+package org.hibernate.type;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.dom4j.Element;
+import org.dom4j.Node;
+import org.hibernate.EntityMode;
+import org.hibernate.FetchMode;
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.engine.CascadeStyle;
+import org.hibernate.engine.Mapping;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.tuple.component.ComponentTuplizer;
+import org.hibernate.tuple.component.ComponentMetamodel;
+import org.hibernate.tuple.StandardProperty;
+import org.hibernate.tuple.EntityModeToTuplizerMapping;
+import org.hibernate.util.ArrayHelper;
+import org.hibernate.util.StringHelper;
+
+/**
+ * Handles "component" mappings
+ *
+ * @author Gavin King
+ */
+public class ComponentType extends AbstractType implements AbstractComponentType {
+
+ private final String[] propertyNames;
+ private final Type[] propertyTypes;
+ private final boolean[] propertyNullability;
+ protected final int propertySpan;
+ private final CascadeStyle[] cascade;
+ private final FetchMode[] joinedFetch;
+ private final boolean isKey;
+
+ protected final EntityModeToTuplizerMapping tuplizerMapping;
+
+ public ComponentType(ComponentMetamodel metamodel) {
+ // for now, just "re-flatten" the metamodel since this is temporary stuff anyway (HHH-1907)
+ this.isKey = metamodel.isKey();
+ this.propertySpan = metamodel.getPropertySpan();
+ this.propertyNames = new String[ propertySpan ];
+ this.propertyTypes = new Type[ propertySpan ];
+ this.propertyNullability = new boolean[ propertySpan ];
+ this.cascade = new CascadeStyle[ propertySpan ];
+ this.joinedFetch = new FetchMode[ propertySpan ];
+
+ for ( int i = 0; i < propertySpan; i++ ) {
+ StandardProperty prop = metamodel.getProperty( i );
+ this.propertyNames[i] = prop.getName();
+ this.propertyTypes[i] = prop.getType();
+ this.propertyNullability[i] = prop.isNullable();
+ this.cascade[i] = prop.getCascadeStyle();
+ this.joinedFetch[i] = prop.getFetchMode();
+ }
+
+ this.tuplizerMapping = metamodel.getTuplizerMapping();
+ }
+
+ public int[] sqlTypes(Mapping mapping) throws MappingException {
+ //Not called at runtime so doesn't matter if its slow :)
+ int[] sqlTypes = new int[getColumnSpan( mapping )];
+ int n = 0;
+ for ( int i = 0; i < propertySpan; i++ ) {
+ int[] subtypes = propertyTypes[i].sqlTypes( mapping );
+ for ( int j = 0; j < subtypes.length; j++ ) {
+ sqlTypes[n++] = subtypes[j];
+ }
+ }
+ return sqlTypes;
+ }
+
+ public int getColumnSpan(Mapping mapping) throws MappingException {
+ int span = 0;
+ for ( int i = 0; i < propertySpan; i++ ) {
+ span += propertyTypes[i].getColumnSpan( mapping );
+ }
+ return span;
+ }
+
+ public final boolean isComponentType() {
+ return true;
+ }
+
+ public Class getReturnedClass() {
+ return tuplizerMapping.getTuplizer( EntityMode.POJO ).getMappedClass(); //TODO
+ }
+
+ public boolean isSame(Object x, Object y, EntityMode entityMode) throws HibernateException {
+ if ( x == y ) {
+ return true;
+ }
+ if ( x == null || y == null ) {
+ return false;
+ }
+ Object[] xvalues = getPropertyValues( x, entityMode );
+ Object[] yvalues = getPropertyValues( y, entityMode );
+ for ( int i = 0; i < propertySpan; i++ ) {
+ if ( !propertyTypes[i].isSame( xvalues[i], yvalues[i], entityMode ) ) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public boolean isEqual(Object x, Object y, EntityMode entityMode)
+ throws HibernateException {
+ if ( x == y ) {
+ return true;
+ }
+ if ( x == null || y == null ) {
+ return false;
+ }
+ Object[] xvalues = getPropertyValues( x, entityMode );
+ Object[] yvalues = getPropertyValues( y, entityMode );
+ for ( int i = 0; i < propertySpan; i++ ) {
+ if ( !propertyTypes[i].isEqual( xvalues[i], yvalues[i], entityMode ) ) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public boolean isEqual(Object x, Object y, EntityMode entityMode, SessionFactoryImplementor factory)
+ throws HibernateException {
+ if ( x == y ) {
+ return true;
+ }
+ if ( x == null || y == null ) {
+ return false;
+ }
+ Object[] xvalues = getPropertyValues( x, entityMode );
+ Object[] yvalues = getPropertyValues( y, entityMode );
+ for ( int i = 0; i < propertySpan; i++ ) {
+ if ( !propertyTypes[i].isEqual( xvalues[i], yvalues[i], entityMode, factory ) ) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public int compare(Object x, Object y, EntityMode entityMode) {
+ if ( x == y ) {
+ return 0;
+ }
+ Object[] xvalues = getPropertyValues( x, entityMode );
+ Object[] yvalues = getPropertyValues( y, entityMode );
+ for ( int i = 0; i < propertySpan; i++ ) {
+ int propertyCompare = propertyTypes[i].compare( xvalues[i], yvalues[i], entityMode );
+ if ( propertyCompare != 0 ) {
+ return propertyCompare;
+ }
+ }
+ return 0;
+ }
+
+ public boolean isMethodOf(Method method) {
+ return false;
+ }
+
+ public int getHashCode(Object x, EntityMode entityMode) {
+ int result = 17;
+ Object[] values = getPropertyValues( x, entityMode );
+ for ( int i = 0; i < propertySpan; i++ ) {
+ Object y = values[i];
+ result *= 37;
+ if ( y != null ) {
+ result += propertyTypes[i].getHashCode( y, entityMode );
+ }
+ }
+ return result;
+ }
+
+ public int getHashCode(Object x, EntityMode entityMode, SessionFactoryImplementor factory) {
+ int result = 17;
+ Object[] values = getPropertyValues( x, entityMode );
+ for ( int i = 0; i < propertySpan; i++ ) {
+ Object y = values[i];
+ result *= 37;
+ if ( y != null ) {
+ result += propertyTypes[i].getHashCode( y, entityMode, factory );
+ }
+ }
+ return result;
+ }
+
+ public boolean isDirty(Object x, Object y, SessionImplementor session)
+ throws HibernateException {
+ if ( x == y ) {
+ return false;
+ }
+ if ( x == null || y == null ) {
+ return true;
+ }
+ EntityMode entityMode = session.getEntityMode();
+ Object[] xvalues = getPropertyValues( x, entityMode );
+ Object[] yvalues = getPropertyValues( y, entityMode );
+ for ( int i = 0; i < xvalues.length; i++ ) {
+ if ( propertyTypes[i].isDirty( xvalues[i], yvalues[i], session ) ) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean isDirty(Object x, Object y, boolean[] checkable, SessionImplementor session)
+ throws HibernateException {
+ if ( x == y ) {
+ return false;
+ }
+ if ( x == null || y == null ) {
+ return true;
+ }
+ EntityMode entityMode = session.getEntityMode();
+ Object[] xvalues = getPropertyValues( x, entityMode );
+ Object[] yvalues = getPropertyValues( y, entityMode );
+ int loc = 0;
+ for ( int i = 0; i < xvalues.length; i++ ) {
+ int len = propertyTypes[i].getColumnSpan( session.getFactory() );
+ if ( len <= 1 ) {
+ final boolean dirty = ( len == 0 || checkable[loc] ) &&
+ propertyTypes[i].isDirty( xvalues[i], yvalues[i], session );
+ if ( dirty ) {
+ return true;
+ }
+ }
+ else {
+ boolean[] subcheckable = new boolean[len];
+ System.arraycopy( checkable, loc, subcheckable, 0, len );
+ final boolean dirty = propertyTypes[i].isDirty( xvalues[i], yvalues[i], subcheckable, session );
+ if ( dirty ) {
+ return true;
+ }
+ }
+ loc += len;
+ }
+ return false;
+ }
+
+ public boolean isModified(Object old, Object current, boolean[] checkable, SessionImplementor session)
+ throws HibernateException {
+
+ if ( current == null ) {
+ return old != null;
+ }
+ if ( old == null ) {
+ return current != null;
+ }
+ Object[] currentValues = getPropertyValues( current, session );
+ Object[] oldValues = ( Object[] ) old;
+ int loc = 0;
+ for ( int i = 0; i < currentValues.length; i++ ) {
+ int len = propertyTypes[i].getColumnSpan( session.getFactory() );
+ boolean[] subcheckable = new boolean[len];
+ System.arraycopy( checkable, loc, subcheckable, 0, len );
+ if ( propertyTypes[i].isModified( oldValues[i], currentValues[i], subcheckable, session ) ) {
+ return true;
+ }
+ loc += len;
+ }
+ return false;
+
+ }
+
+ public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner)
+ throws HibernateException, SQLException {
+ return resolve( hydrate( rs, names, session, owner ), session, owner );
+ }
+
+ public void nullSafeSet(PreparedStatement st, Object value, int begin, SessionImplementor session)
+ throws HibernateException, SQLException {
+
+ Object[] subvalues = nullSafeGetValues( value, session.getEntityMode() );
+
+ for ( int i = 0; i < propertySpan; i++ ) {
+ propertyTypes[i].nullSafeSet( st, subvalues[i], begin, session );
+ begin += propertyTypes[i].getColumnSpan( session.getFactory() );
+ }
+ }
+
+ public void nullSafeSet(
+ PreparedStatement st,
+ Object value,
+ int begin,
+ boolean[] settable,
+ SessionImplementor session)
+ throws HibernateException, SQLException {
+
+ Object[] subvalues = nullSafeGetValues( value, session.getEntityMode() );
+
+ int loc = 0;
+ for ( int i = 0; i < propertySpan; i++ ) {
+ int len = propertyTypes[i].getColumnSpan( session.getFactory() );
+ if ( len == 0 ) {
+ //noop
+ }
+ else if ( len == 1 ) {
+ if ( settable[loc] ) {
+ propertyTypes[i].nullSafeSet( st, subvalues[i], begin, session );
+ begin++;
+ }
+ }
+ else {
+ boolean[] subsettable = new boolean[len];
+ System.arraycopy( settable, loc, subsettable, 0, len );
+ propertyTypes[i].nullSafeSet( st, subvalues[i], begin, subsettable, session );
+ begin += ArrayHelper.countTrue( subsettable );
+ }
+ loc += len;
+ }
+ }
+
+ private Object[] nullSafeGetValues(Object value, EntityMode entityMode) throws HibernateException {
+ if ( value == null ) {
+ return new Object[propertySpan];
+ }
+ else {
+ return getPropertyValues( value, entityMode );
+ }
+ }
+
+ public Object nullSafeGet(ResultSet rs, String name, SessionImplementor session, Object owner)
+ throws HibernateException, SQLException {
+
+ return nullSafeGet( rs, new String[] {name}, session, owner );
+ }
+
+ public Object getPropertyValue(Object component, int i, SessionImplementor session)
+ throws HibernateException {
+ return getPropertyValue( component, i, session.getEntityMode() );
+ }
+
+ public Object getPropertyValue(Object component, int i, EntityMode entityMode)
+ throws HibernateException {
+ return tuplizerMapping.getTuplizer( entityMode ).getPropertyValue( component, i );
+ }
+
+ public Object[] getPropertyValues(Object component, SessionImplementor session)
+ throws HibernateException {
+ return getPropertyValues( component, session.getEntityMode() );
+ }
+
+ public Object[] getPropertyValues(Object component, EntityMode entityMode)
+ throws HibernateException {
+ return tuplizerMapping.getTuplizer( entityMode ).getPropertyValues( component );
+ }
+
+ public void setPropertyValues(Object component, Object[] values, EntityMode entityMode)
+ throws HibernateException {
+ tuplizerMapping.getTuplizer( entityMode ).setPropertyValues( component, values );
+ }
+
+ public Type[] getSubtypes() {
+ return propertyTypes;
+ }
+
+ public String getName() {
+ return "component" + ArrayHelper.toString( propertyNames );
+ }
+
+ public String toLoggableString(Object value, SessionFactoryImplementor factory)
+ throws HibernateException {
+ if ( value == null ) {
+ return "null";
+ }
+ Map result = new HashMap();
+ EntityMode entityMode = tuplizerMapping.guessEntityMode( value );
+ if ( entityMode == null ) {
+ throw new ClassCastException( value.getClass().getName() );
+ }
+ Object[] values = getPropertyValues( value, entityMode );
+ for ( int i = 0; i < propertyTypes.length; i++ ) {
+ result.put( propertyNames[i], propertyTypes[i].toLoggableString( values[i], factory ) );
+ }
+ return StringHelper.unqualify( getName() ) + result.toString();
+ }
+
+ public String[] getPropertyNames() {
+ return propertyNames;
+ }
+
+ public Object deepCopy(Object component, EntityMode entityMode, SessionFactoryImplementor factory)
+ throws HibernateException {
+ if ( component == null ) {
+ return null;
+ }
+
+ Object[] values = getPropertyValues( component, entityMode );
+ for ( int i = 0; i < propertySpan; i++ ) {
+ values[i] = propertyTypes[i].deepCopy( values[i], entityMode, factory );
+ }
+
+ Object result = instantiate( entityMode );
+ setPropertyValues( result, values, entityMode );
+
+ //not absolutely necessary, but helps for some
+ //equals()/hashCode() implementations
+ ComponentTuplizer ct = ( ComponentTuplizer ) tuplizerMapping.getTuplizer( entityMode );
+ if ( ct.hasParentProperty() ) {
+ ct.setParent( result, ct.getParent( component ), factory );
+ }
+
+ return result;
+ }
+
+ public Object replace(
+ Object original,
+ Object target,
+ SessionImplementor session,
+ Object owner,
+ Map copyCache)
+ throws HibernateException {
+
+ if ( original == null ) {
+ return null;
+ }
+ //if ( original == target ) return target;
+
+ final Object result = target == null
+ ? instantiate( owner, session )
+ : target;
+
+ final EntityMode entityMode = session.getEntityMode();
+ Object[] values = TypeFactory.replace(
+ getPropertyValues( original, entityMode ),
+ getPropertyValues( result, entityMode ),
+ propertyTypes,
+ session,
+ owner,
+ copyCache
+ );
+
+ setPropertyValues( result, values, entityMode );
+ return result;
+ }
+
+ public Object replace(
+ Object original,
+ Object target,
+ SessionImplementor session,
+ Object owner,
+ Map copyCache,
+ ForeignKeyDirection foreignKeyDirection)
+ throws HibernateException {
+
+ if ( original == null ) {
+ return null;
+ }
+ //if ( original == target ) return target;
+
+ final Object result = target == null ?
+ instantiate( owner, session ) :
+ target;
+
+ final EntityMode entityMode = session.getEntityMode();
+ Object[] values = TypeFactory.replace(
+ getPropertyValues( original, entityMode ),
+ getPropertyValues( result, entityMode ),
+ propertyTypes,
+ session,
+ owner,
+ copyCache,
+ foreignKeyDirection
+ );
+
+ setPropertyValues( result, values, entityMode );
+ return result;
+ }
+
+ /**
+ * This method does not populate the component parent
+ */
+ public Object instantiate(EntityMode entityMode) throws HibernateException {
+ return tuplizerMapping.getTuplizer( entityMode ).instantiate();
+ }
+
+ public Object instantiate(Object parent, SessionImplementor session)
+ throws HibernateException {
+
+ Object result = instantiate( session.getEntityMode() );
+
+ ComponentTuplizer ct = ( ComponentTuplizer ) tuplizerMapping.getTuplizer( session.getEntityMode() );
+ if ( ct.hasParentProperty() && parent != null ) {
+ ct.setParent(
+ result,
+ session.getPersistenceContext().proxyFor( parent ),
+ session.getFactory()
+ );
+ }
+
+ return result;
+ }
+
+ public CascadeStyle getCascadeStyle(int i) {
+ return cascade[i];
+ }
+
+ public boolean isMutable() {
+ return true;
+ }
+
+ public Serializable disassemble(Object value, SessionImplementor session, Object owner)
+ throws HibernateException {
+
+ if ( value == null ) {
+ return null;
+ }
+ else {
+ Object[] values = getPropertyValues( value, session.getEntityMode() );
+ for ( int i = 0; i < propertyTypes.length; i++ ) {
+ values[i] = propertyTypes[i].disassemble( values[i], session, owner );
+ }
+ return values;
+ }
+ }
+
+ public Object assemble(Serializable object, SessionImplementor session, Object owner)
+ throws HibernateException {
+
+ if ( object == null ) {
+ return null;
+ }
+ else {
+ Object[] values = ( Object[] ) object;
+ Object[] assembled = new Object[values.length];
+ for ( int i = 0; i < propertyTypes.length; i++ ) {
+ assembled[i] = propertyTypes[i].assemble( ( Serializable ) values[i], session, owner );
+ }
+ Object result = instantiate( owner, session );
+ setPropertyValues( result, assembled, session.getEntityMode() );
+ return result;
+ }
+ }
+
+ public FetchMode getFetchMode(int i) {
+ return joinedFetch[i];
+ }
+
+ public Object hydrate(
+ final ResultSet rs,
+ final String[] names,
+ final SessionImplementor session,
+ final Object owner)
+ throws HibernateException, SQLException {
+
+ int begin = 0;
+ boolean notNull = false;
+ Object[] values = new Object[propertySpan];
+ for ( int i = 0; i < propertySpan; i++ ) {
+ int length = propertyTypes[i].getColumnSpan( session.getFactory() );
+ String[] range = ArrayHelper.slice( names, begin, length ); //cache this
+ Object val = propertyTypes[i].hydrate( rs, range, session, owner );
+ if ( val == null ) {
+ if ( isKey ) {
+ return null; //different nullability rules for pk/fk
+ }
+ }
+ else {
+ notNull = true;
+ }
+ values[i] = val;
+ begin += length;
+ }
+
+ return notNull ? values : null;
+ }
+
+ public Object resolve(Object value, SessionImplementor session, Object owner)
+ throws HibernateException {
+
+ if ( value != null ) {
+ Object result = instantiate( owner, session );
+ Object[] values = ( Object[] ) value;
+ Object[] resolvedValues = new Object[values.length]; //only really need new array during semiresolve!
+ for ( int i = 0; i < values.length; i++ ) {
+ resolvedValues[i] = propertyTypes[i].resolve( values[i], session, owner );
+ }
+ setPropertyValues( result, resolvedValues, session.getEntityMode() );
+ return result;
+ }
+ else {
+ return null;
+ }
+ }
+
+ public Object semiResolve(Object value, SessionImplementor session, Object owner)
+ throws HibernateException {
+ //note that this implementation is kinda broken
+ //for components with many-to-one associations
+ return resolve( value, session, owner );
+ }
+
+ public boolean[] getPropertyNullability() {
+ return propertyNullability;
+ }
+
+ public boolean isXMLElement() {
+ return true;
+ }
+
+ public Object fromXMLNode(Node xml, Mapping factory) throws HibernateException {
+ return xml;
+ }
+
+ public void setToXMLNode(Node node, Object value, SessionFactoryImplementor factory) throws HibernateException {
+ replaceNode( node, ( Element ) value );
+ }
+
+ public boolean[] toColumnNullness(Object value, Mapping mapping) {
+ boolean[] result = new boolean[ getColumnSpan( mapping ) ];
+ if ( value == null ) {
+ return result;
+ }
+ Object[] values = getPropertyValues( value, EntityMode.POJO ); //TODO!!!!!!!
+ int loc = 0;
+ for ( int i = 0; i < propertyTypes.length; i++ ) {
+ boolean[] propertyNullness = propertyTypes[i].toColumnNullness( values[i], mapping );
+ System.arraycopy( propertyNullness, 0, result, loc, propertyNullness.length );
+ loc += propertyNullness.length;
+ }
+ return result;
+ }
+
+ public boolean isEmbedded() {
+ return false;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/CompositeCustomType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/CompositeCustomType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/CompositeCustomType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,274 @@
+//$Id: CompositeCustomType.java 7670 2005-07-29 05:36:14Z oneovthafew $
+package org.hibernate.type;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Map;
+import java.util.Properties;
+
+import org.dom4j.Element;
+import org.dom4j.Node;
+import org.hibernate.EntityMode;
+import org.hibernate.FetchMode;
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.engine.CascadeStyle;
+import org.hibernate.engine.Mapping;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.usertype.CompositeUserType;
+
+/**
+ * Adapts <tt>CompositeUserType</tt> to <tt>Type</tt> interface
+ * @author Gavin King
+ */
+public class CompositeCustomType extends AbstractType
+ implements AbstractComponentType {
+
+ private final CompositeUserType userType;
+ private final String name;
+
+ public CompositeCustomType(Class userTypeClass, Properties parameters)
+ throws MappingException {
+ name = userTypeClass.getName();
+
+ if ( !CompositeUserType.class.isAssignableFrom(userTypeClass) ) {
+ throw new MappingException(
+ "Custom type does not implement CompositeUserType: " +
+ userTypeClass.getName()
+ );
+ }
+
+ try {
+ userType = (CompositeUserType) userTypeClass.newInstance();
+ }
+ catch (InstantiationException ie) {
+ throw new MappingException(
+ "Cannot instantiate custom type: " +
+ userTypeClass.getName()
+ );
+ }
+ catch (IllegalAccessException iae) {
+ throw new MappingException(
+ "IllegalAccessException trying to instantiate custom type: " +
+ userTypeClass.getName()
+ );
+ }
+ TypeFactory.injectParameters(userType, parameters);
+ }
+
+ public boolean isMethodOf(Method method) {
+ return false;
+ }
+
+ public Type[] getSubtypes() {
+ return userType.getPropertyTypes();
+ }
+
+ public String[] getPropertyNames() {
+ return userType.getPropertyNames();
+ }
+
+ public Object[] getPropertyValues(Object component, SessionImplementor session)
+ throws HibernateException {
+ return getPropertyValues( component, session.getEntityMode() );
+ }
+
+ public Object[] getPropertyValues(Object component, EntityMode entityMode)
+ throws HibernateException {
+
+ int len = getSubtypes().length;
+ Object[] result = new Object[len];
+ for ( int i=0; i<len; i++ ) {
+ result[i] = getPropertyValue(component, i);
+ }
+ return result;
+ }
+
+ public void setPropertyValues(Object component, Object[] values, EntityMode entityMode)
+ throws HibernateException {
+
+ for (int i=0; i<values.length; i++) {
+ userType.setPropertyValue( component, i, values[i] );
+ }
+ }
+
+ public Object getPropertyValue(Object component, int i, SessionImplementor session)
+ throws HibernateException {
+ return getPropertyValue(component, i);
+ }
+
+ public Object getPropertyValue(Object component, int i)
+ throws HibernateException {
+ return userType.getPropertyValue(component, i);
+ }
+
+ public CascadeStyle getCascadeStyle(int i) {
+ return CascadeStyle.NONE;
+ }
+
+ public FetchMode getFetchMode(int i) {
+ return FetchMode.DEFAULT;
+ }
+
+ public boolean isComponentType() {
+ return true;
+ }
+
+ public Object deepCopy(Object value, EntityMode entityMode, SessionFactoryImplementor factory)
+ throws HibernateException {
+ return userType.deepCopy(value);
+ }
+
+ public Object assemble(
+ Serializable cached,
+ SessionImplementor session,
+ Object owner)
+ throws HibernateException {
+
+ return userType.assemble(cached, session, owner);
+ }
+
+ public Serializable disassemble(Object value, SessionImplementor session, Object owner)
+ throws HibernateException {
+ return userType.disassemble(value, session);
+ }
+
+ public Object replace(
+ Object original,
+ Object target,
+ SessionImplementor session,
+ Object owner,
+ Map copyCache)
+ throws HibernateException {
+ return userType.replace(original, target, session, owner);
+ }
+
+ public boolean isEqual(Object x, Object y, EntityMode entityMode)
+ throws HibernateException {
+ return userType.equals(x, y);
+ }
+
+ public int getHashCode(Object x, EntityMode entityMode) {
+ return userType.hashCode(x);
+ }
+
+ public int getColumnSpan(Mapping mapping) throws MappingException {
+ Type[] types = userType.getPropertyTypes();
+ int n=0;
+ for (int i=0; i<types.length; i++) {
+ n+=types[i].getColumnSpan(mapping);
+ }
+ return n;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Class getReturnedClass() {
+ return userType.returnedClass();
+ }
+
+ public boolean isMutable() {
+ return userType.isMutable();
+ }
+
+ public Object nullSafeGet(
+ ResultSet rs,
+ String columnName,
+ SessionImplementor session,
+ Object owner)
+ throws HibernateException, SQLException {
+
+ return userType.nullSafeGet(rs, new String[] {columnName}, session, owner);
+ }
+
+ public Object nullSafeGet(
+ ResultSet rs,
+ String[] names,
+ SessionImplementor session,
+ Object owner)
+ throws HibernateException, SQLException {
+
+ return userType.nullSafeGet(rs, names, session, owner);
+ }
+
+ public void nullSafeSet(
+ PreparedStatement st,
+ Object value,
+ int index,
+ SessionImplementor session)
+ throws HibernateException, SQLException {
+
+ userType.nullSafeSet(st, value, index, session);
+
+ }
+
+ public void nullSafeSet(
+ PreparedStatement st,
+ Object value,
+ int index,
+ boolean[] settable,
+ SessionImplementor session)
+ throws HibernateException, SQLException {
+
+ userType.nullSafeSet(st, value, index, session);
+
+ }
+
+ public int[] sqlTypes(Mapping mapping) throws MappingException {
+ Type[] types = userType.getPropertyTypes();
+ int[] result = new int[ getColumnSpan(mapping) ];
+ int n=0;
+ for (int i=0; i<types.length; i++) {
+ int[] sqlTypes = types[i].sqlTypes(mapping);
+ for ( int k=0; k<sqlTypes.length; k++ ) result[n++] = sqlTypes[k];
+ }
+ return result;
+ }
+
+ public String toLoggableString(Object value, SessionFactoryImplementor factory)
+ throws HibernateException {
+
+ return value==null ? "null" : value.toString();
+ }
+
+ public boolean[] getPropertyNullability() {
+ return null;
+ }
+
+ public Object fromXMLNode(Node xml, Mapping factory) throws HibernateException {
+ return xml;
+ }
+
+ public void setToXMLNode(Node node, Object value, SessionFactoryImplementor factory)
+ throws HibernateException {
+ replaceNode( node, (Element) value );
+ }
+
+ public boolean[] toColumnNullness(Object value, Mapping mapping) {
+ boolean[] result = new boolean[ getColumnSpan(mapping) ];
+ if (value==null) return result;
+ Object[] values = getPropertyValues(value, EntityMode.POJO); //TODO!!!!!!!
+ int loc = 0;
+ Type[] propertyTypes = getSubtypes();
+ for ( int i=0; i<propertyTypes.length; i++ ) {
+ boolean[] propertyNullness = propertyTypes[i].toColumnNullness( values[i], mapping );
+ System.arraycopy(propertyNullness, 0, result, loc, propertyNullness.length);
+ loc += propertyNullness.length;
+ }
+ return result;
+ }
+
+ public boolean isDirty(Object old, Object current, boolean[] checkable, SessionImplementor session) throws HibernateException {
+ return isDirty(old, current, session);
+ }
+
+ public boolean isEmbedded() {
+ return false;
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/CurrencyType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/CurrencyType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/CurrencyType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,142 @@
+//$Id: CurrencyType.java 8173 2005-09-14 19:54:49Z oneovthafew $
+package org.hibernate.type;
+
+import java.lang.reflect.Method;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.hibernate.AssertionFailure;
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.dialect.Dialect;
+
+/**
+ * <tt>currency</tt>: A type that maps an SQL VARCHAR to a
+ * <tt>java.util.Currency</tt>
+ * @see java.util.Currency
+ * @author Gavin King
+ */
+public class CurrencyType extends ImmutableType implements LiteralType {
+
+ public static final Class CURRENCY_CLASS;
+ private static final Method CURRENCY_GET_INSTANCE;
+ private static final Method CURRENCY_GET_CODE;
+
+ static {
+ Class clazz;
+ try {
+ clazz = Class.forName("java.util.Currency");
+ }
+ catch (ClassNotFoundException cnfe) {
+ clazz = null;
+ }
+ if (clazz==null) {
+ CURRENCY_CLASS = null;
+ CURRENCY_GET_INSTANCE = null;
+ CURRENCY_GET_CODE = null;
+ }
+ else {
+ CURRENCY_CLASS = clazz;
+ try {
+ CURRENCY_GET_INSTANCE = clazz.getMethod("getInstance", new Class[] { String.class } );
+ CURRENCY_GET_CODE = clazz.getMethod("getCurrencyCode", new Class[0] );
+ }
+ catch (Exception e) {
+ throw new AssertionFailure("Exception in static initializer of CurrencyType", e);
+ }
+ }
+ }
+
+ /**
+ * @see org.hibernate.type.NullableType#get(ResultSet, String)
+ */
+ public Object get(ResultSet rs, String name)
+ throws HibernateException, SQLException {
+ String code = (String) Hibernate.STRING.nullSafeGet(rs, name);
+ try {
+ return code==null ? null :
+ CURRENCY_GET_INSTANCE.invoke(null, new Object[] { code } );
+ }
+ catch (Exception e) {
+ throw new HibernateException("Could not resolve currency code: " + code);
+ }
+ }
+
+ /**
+ * @see org.hibernate.type.NullableType#set(PreparedStatement, Object, int)
+ */
+ public void set(PreparedStatement st, Object value, int index)
+ throws HibernateException, SQLException {
+ Object code;
+ try {
+ code = CURRENCY_GET_CODE.invoke(value, null);
+ }
+ catch (Exception e) {
+ throw new HibernateException("Could not get Currency code", e);
+ }
+ Hibernate.STRING.set(st, code, index);
+ }
+
+ /**
+ * @see org.hibernate.type.NullableType#sqlType()
+ */
+ public int sqlType() {
+ return Hibernate.STRING.sqlType();
+ }
+
+ /**
+ */
+ public String toString(Object value) throws HibernateException {
+ try {
+ return (String) CURRENCY_GET_CODE.invoke(value, null);
+ }
+ catch (Exception e) {
+ throw new HibernateException("Could not get Currency code", e);
+ }
+ }
+
+ /**
+ * @see org.hibernate.type.Type#getReturnedClass()
+ */
+ public Class getReturnedClass() {
+ return CURRENCY_CLASS;
+ }
+
+ /**
+ * @see org.hibernate.type.Type#getName()
+ */
+ public String getName() {
+ return "currency";
+ }
+
+ /**
+ * @see org.hibernate.type.LiteralType#objectToSQLString(Object, Dialect)
+ */
+ public String objectToSQLString(Object value, Dialect dialect) throws Exception {
+ String code;
+ try {
+ code = (String) CURRENCY_GET_CODE.invoke(value, null);
+ }
+ catch (Exception e) {
+ throw new HibernateException("Could not get Currency code", e);
+ }
+ return ( (LiteralType) Hibernate.STRING ).objectToSQLString(code, dialect);
+ }
+
+ public Object fromStringValue(String xml) throws HibernateException {
+ try {
+ return CURRENCY_GET_INSTANCE.invoke( null, new Object[] {xml} );
+ }
+ catch (Exception e) {
+ throw new HibernateException("Could not resolve currency code: " + xml);
+ }
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/CustomCollectionType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/CustomCollectionType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/CustomCollectionType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,94 @@
+//$Id: CustomCollectionType.java 11496 2007-05-09 03:54:06Z steve.ebersole at jboss.com $
+package org.hibernate.type;
+
+import java.io.Serializable;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.usertype.UserCollectionType;
+import org.hibernate.usertype.LoggableUserType;
+
+/**
+ * A custom type for mapping user-written classes that implement <tt>PersistentCollection</tt>
+ *
+ * @see org.hibernate.collection.PersistentCollection
+ * @see org.hibernate.usertype.UserCollectionType
+ * @author Gavin King
+ */
+public class CustomCollectionType extends CollectionType {
+
+ private final UserCollectionType userType;
+ private final boolean customLogging;
+
+ public CustomCollectionType(Class userTypeClass, String role, String foreignKeyPropertyName, boolean isEmbeddedInXML) {
+ super(role, foreignKeyPropertyName, isEmbeddedInXML);
+
+ if ( !UserCollectionType.class.isAssignableFrom( userTypeClass ) ) {
+ throw new MappingException( "Custom type does not implement UserCollectionType: " + userTypeClass.getName() );
+ }
+
+ try {
+ userType = ( UserCollectionType ) userTypeClass.newInstance();
+ }
+ catch ( InstantiationException ie ) {
+ throw new MappingException( "Cannot instantiate custom type: " + userTypeClass.getName() );
+ }
+ catch ( IllegalAccessException iae ) {
+ throw new MappingException( "IllegalAccessException trying to instantiate custom type: " + userTypeClass.getName() );
+ }
+
+ customLogging = LoggableUserType.class.isAssignableFrom( userTypeClass );
+ }
+
+ public PersistentCollection instantiate(SessionImplementor session, CollectionPersister persister, Serializable key)
+ throws HibernateException {
+ return userType.instantiate(session, persister);
+ }
+
+ public PersistentCollection wrap(SessionImplementor session, Object collection) {
+ return userType.wrap(session, collection);
+ }
+
+ public Class getReturnedClass() {
+ return userType.instantiate( -1 ).getClass();
+ }
+
+ public Object instantiate(int anticipatedType) {
+ return userType.instantiate( anticipatedType );
+ }
+
+ public Iterator getElementsIterator(Object collection) {
+ return userType.getElementsIterator(collection);
+ }
+ public boolean contains(Object collection, Object entity, SessionImplementor session) {
+ return userType.contains(collection, entity);
+ }
+ public Object indexOf(Object collection, Object entity) {
+ return userType.indexOf(collection, entity);
+ }
+
+ public Object replaceElements(Object original, Object target, Object owner, Map copyCache, SessionImplementor session)
+ throws HibernateException {
+ CollectionPersister cp = session.getFactory().getCollectionPersister( getRole() );
+ return userType.replaceElements(original, target, cp, owner, copyCache, session);
+ }
+
+ protected String renderLoggableString(Object value, SessionFactoryImplementor factory) throws HibernateException {
+ if ( customLogging ) {
+ return ( ( LoggableUserType ) userType ).toLoggableString( value, factory );
+ }
+ else {
+ return super.renderLoggableString( value, factory );
+ }
+ }
+
+ public UserCollectionType getUserType() {
+ return userType;
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/type/CustomCollectionType.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/CustomType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/CustomType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/CustomType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,238 @@
+//$Id: CustomType.java 10084 2006-07-05 15:03:52Z steve.ebersole at jboss.com $
+package org.hibernate.type;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Map;
+import java.util.Properties;
+
+import org.dom4j.Node;
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.Mapping;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.usertype.EnhancedUserType;
+import org.hibernate.usertype.UserType;
+import org.hibernate.usertype.UserVersionType;
+import org.hibernate.usertype.LoggableUserType;
+
+/**
+ * Adapts {@link UserType} to the generic {@link Type} interface, in order
+ * to isolate user code from changes in the internal Type contracts.
+ *
+ * @see org.hibernate.usertype.UserType
+ * @author Gavin King
+ */
+public class CustomType extends AbstractType implements IdentifierType, DiscriminatorType, VersionType {
+
+ private final UserType userType;
+ private final String name;
+ private final int[] types;
+ private final boolean customLogging;
+
+ public CustomType(Class userTypeClass, Properties parameters) throws MappingException {
+
+ if ( !UserType.class.isAssignableFrom( userTypeClass ) ) {
+ throw new MappingException(
+ "Custom type does not implement UserType: " +
+ userTypeClass.getName()
+ );
+ }
+
+ name = userTypeClass.getName();
+
+ try {
+ userType = ( UserType ) userTypeClass.newInstance();
+ }
+ catch ( InstantiationException ie ) {
+ throw new MappingException(
+ "Cannot instantiate custom type: " +
+ userTypeClass.getName()
+ );
+ }
+ catch ( IllegalAccessException iae ) {
+ throw new MappingException(
+ "IllegalAccessException trying to instantiate custom type: " +
+ userTypeClass.getName()
+ );
+ }
+
+ TypeFactory.injectParameters( userType, parameters );
+ types = userType.sqlTypes();
+
+ customLogging = LoggableUserType.class.isAssignableFrom( userTypeClass );
+ }
+
+ public int[] sqlTypes(Mapping pi) {
+ return types;
+ }
+
+ public int getColumnSpan(Mapping session) {
+ return types.length;
+ }
+
+ public Class getReturnedClass() {
+ return userType.returnedClass();
+ }
+
+ public boolean isEqual(Object x, Object y) throws HibernateException {
+ return userType.equals(x, y);
+ }
+
+ public boolean isEqual(Object x, Object y, EntityMode entityMode)
+ throws HibernateException {
+ return isEqual(x, y);
+ }
+
+ public int getHashCode(Object x, EntityMode entityMode) {
+ return userType.hashCode(x);
+ }
+
+ public Object nullSafeGet(
+ ResultSet rs,
+ String[] names,
+ SessionImplementor session,
+ Object owner
+ ) throws HibernateException, SQLException {
+
+ return userType.nullSafeGet(rs, names, owner);
+ }
+
+ public Object nullSafeGet(
+ ResultSet rs,
+ String columnName,
+ SessionImplementor session,
+ Object owner
+ ) throws HibernateException, SQLException {
+ return nullSafeGet(rs, new String[] { columnName }, session, owner);
+ }
+
+
+ public Object assemble(Serializable cached, SessionImplementor session, Object owner)
+ throws HibernateException {
+ return userType.assemble(cached, owner);
+ }
+
+ public Serializable disassemble(Object value, SessionImplementor session, Object owner)
+ throws HibernateException {
+ return userType.disassemble(value);
+ }
+
+ public Object replace(
+ Object original,
+ Object target,
+ SessionImplementor session,
+ Object owner,
+ Map copyCache)
+ throws HibernateException {
+ return userType.replace(original, target, owner);
+ }
+
+ public void nullSafeSet(
+ PreparedStatement st,
+ Object value,
+ int index,
+ boolean[] settable,
+ SessionImplementor session
+ ) throws HibernateException, SQLException {
+
+ if ( settable[0] ) userType.nullSafeSet(st, value, index);
+ }
+
+ public void nullSafeSet(
+ PreparedStatement st,
+ Object value,
+ int index,
+ SessionImplementor session
+ ) throws HibernateException, SQLException {
+
+ userType.nullSafeSet(st, value, index);
+ }
+
+ public String toXMLString(Object value, SessionFactoryImplementor factory) {
+ if (value==null) return null;
+ if (userType instanceof EnhancedUserType) {
+ return ( (EnhancedUserType) userType ).toXMLString(value);
+ }
+ else {
+ return value.toString();
+ }
+ }
+
+ public Object fromXMLString(String xml, Mapping factory) {
+ return ( (EnhancedUserType) userType ).fromXMLString(xml);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Object deepCopy(Object value, EntityMode entityMode, SessionFactoryImplementor factory)
+ throws HibernateException {
+ return userType.deepCopy(value);
+ }
+
+ public boolean isMutable() {
+ return userType.isMutable();
+ }
+
+ public Object stringToObject(String xml) {
+ return ( (EnhancedUserType) userType ).fromXMLString(xml);
+ }
+
+ public String objectToSQLString(Object value, Dialect dialect) throws Exception {
+ return ( (EnhancedUserType) userType ).objectToSQLString(value);
+ }
+
+ public Comparator getComparator() {
+ return (Comparator) userType;
+ }
+
+ public Object next(Object current, SessionImplementor session) {
+ return ( (UserVersionType) userType ).next( current, session );
+ }
+
+ public Object seed(SessionImplementor session) {
+ return ( (UserVersionType) userType ).seed( session );
+ }
+
+ public Object fromXMLNode(Node xml, Mapping factory) throws HibernateException {
+ return fromXMLString( xml.getText(), factory );
+ }
+
+ public void setToXMLNode(Node node, Object value, SessionFactoryImplementor factory)
+ throws HibernateException {
+ node.setText( toXMLString(value, factory) );
+ }
+
+ public String toLoggableString(Object value, SessionFactoryImplementor factory)
+ throws HibernateException {
+ if ( value == null ) {
+ return "null";
+ }
+ else if ( customLogging ) {
+ return ( ( LoggableUserType ) userType ).toLoggableString( value, factory );
+ }
+ else {
+ return toXMLString( value, factory );
+ }
+ }
+
+ public boolean[] toColumnNullness(Object value, Mapping mapping) {
+ boolean[] result = new boolean[ getColumnSpan(mapping) ];
+ if (value!=null) Arrays.fill(result, true);
+ return result;
+ }
+
+ public boolean isDirty(Object old, Object current, boolean[] checkable, SessionImplementor session) throws HibernateException {
+ return checkable[0] && isDirty(old, current, session);
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/DateType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/DateType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/DateType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,107 @@
+//$Id: DateType.java 8891 2005-12-21 05:13:29Z oneovthafew $
+package org.hibernate.type;
+
+import java.sql.Date;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+
+import org.hibernate.EntityMode;
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.dialect.Dialect;
+
+/**
+ * <tt>date</tt>: A type that maps an SQL DATE to a Java Date.
+ * @author Gavin King
+ */
+public class DateType extends MutableType implements IdentifierType, LiteralType {
+
+ private static final String DATE_FORMAT = "dd MMMM yyyy";
+
+ public Object get(ResultSet rs, String name) throws SQLException {
+ return rs.getDate(name);
+ }
+
+ public Class getReturnedClass() {
+ return java.util.Date.class;
+ }
+
+ public void set(PreparedStatement st, Object value, int index) throws SQLException {
+
+ Date sqlDate;
+ if ( value instanceof Date) {
+ sqlDate = (Date) value;
+ }
+ else {
+ sqlDate = new Date( ( (java.util.Date) value ).getTime() );
+ }
+ st.setDate(index, sqlDate);
+ }
+
+ public int sqlType() {
+ return Types.DATE;
+ }
+
+ public boolean isEqual(Object x, Object y) {
+
+ if (x==y) return true;
+ if (x==null || y==null) return false;
+
+ java.util.Date xdate = (java.util.Date) x;
+ java.util.Date ydate = (java.util.Date) y;
+
+ if ( xdate.getTime()==ydate.getTime() ) return true;
+
+ Calendar calendar1 = java.util.Calendar.getInstance();
+ Calendar calendar2 = java.util.Calendar.getInstance();
+ calendar1.setTime( xdate );
+ calendar2.setTime( ydate );
+
+ return Hibernate.CALENDAR_DATE.isEqual(calendar1, calendar2);
+ }
+
+ public int getHashCode(Object x, EntityMode entityMode) {
+ Calendar calendar = java.util.Calendar.getInstance();
+ calendar.setTime( (java.util.Date) x );
+ return Hibernate.CALENDAR_DATE.getHashCode(calendar, entityMode);
+ }
+
+ public String getName() { return "date"; }
+
+ public String toString(Object val) {
+ return new SimpleDateFormat(DATE_FORMAT).format( (java.util.Date) val );
+ }
+
+ public Object deepCopyNotNull(Object value) {
+ return new Date( ( (java.util.Date) value ).getTime() );
+ }
+
+ public Object stringToObject(String xml) throws Exception {
+ return DateFormat.getDateInstance().parse(xml);
+ }
+
+ public String objectToSQLString(Object value, Dialect dialect) throws Exception {
+ return '\'' + new Date( ( (java.util.Date) value ).getTime() ).toString() + '\'';
+ }
+
+ public Object fromStringValue(String xml) throws HibernateException {
+ try {
+ return new SimpleDateFormat(DATE_FORMAT).parse(xml);
+ }
+ catch (ParseException pe) {
+ throw new HibernateException("could not parse XML", pe);
+ }
+ }
+
+}
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/DbTimestampType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/DbTimestampType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/DbTimestampType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,130 @@
+// $Id: DbTimestampType.java 7830 2005-08-11 00:10:26Z oneovthafew $
+package org.hibernate.type;
+
+import java.sql.Timestamp;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.ResultSet;
+import java.sql.CallableStatement;
+
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.exception.JDBCExceptionHelper;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * <tt>dbtimestamp</tt>: An extension of {@link TimestampType} which
+ * maps to the database's current timestamp, rather than the jvm's
+ * current timestamp.
+ * <p/>
+ * Note: May/may-not cause issues on dialects which do not properly support
+ * a true notion of timestamp (Oracle < 8, for example, where only its DATE
+ * datatype is supported). Depends on the frequency of DML operations...
+ *
+ * @author Steve Ebersole
+ */
+public class DbTimestampType extends TimestampType implements VersionType {
+
+ private static final Log log = LogFactory.getLog( DbTimestampType.class );
+
+ public String getName() { return "dbtimestamp"; }
+
+ public Object seed(SessionImplementor session) {
+ if ( session == null ) {
+ log.trace( "incoming session was null; using current jvm time" );
+ return super.seed( session );
+ }
+ else if ( !session.getFactory().getDialect().supportsCurrentTimestampSelection() ) {
+ log.debug( "falling back to vm-based timestamp, as dialect does not support current timestamp selection" );
+ return super.seed( session );
+ }
+ else {
+ return getCurrentTimestamp( session );
+ }
+ }
+
+ private Timestamp getCurrentTimestamp(SessionImplementor session) {
+ Dialect dialect = session.getFactory().getDialect();
+ String timestampSelectString = dialect.getCurrentTimestampSelectString();
+ if ( dialect.isCurrentTimestampSelectStringCallable() ) {
+ return useCallableStatement( timestampSelectString, session );
+ }
+ else {
+ return usePreparedStatement( timestampSelectString, session );
+ }
+ }
+
+ private Timestamp usePreparedStatement(String timestampSelectString, SessionImplementor session) {
+ PreparedStatement ps = null;
+ try {
+ ps = session.getBatcher().prepareStatement( timestampSelectString );
+ ResultSet rs = session.getBatcher().getResultSet( ps );
+ rs.next();
+ Timestamp ts = rs.getTimestamp( 1 );
+ if ( log.isTraceEnabled() ) {
+ log.trace(
+ "current timestamp retreived from db : " + ts +
+ " (nanos=" + ts.getNanos() +
+ ", time=" + ts.getTime() + ")"
+ );
+ }
+ return ts;
+ }
+ catch( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ session.getFactory().getSQLExceptionConverter(),
+ sqle,
+ "could not select current db timestamp",
+ timestampSelectString
+ );
+ }
+ finally {
+ if ( ps != null ) {
+ try {
+ session.getBatcher().closeStatement( ps );
+ }
+ catch( SQLException sqle ) {
+ log.warn( "unable to clean up prepared statement", sqle );
+ }
+ }
+ }
+ }
+
+ private Timestamp useCallableStatement(String callString, SessionImplementor session) {
+ CallableStatement cs = null;
+ try {
+ cs = session.getBatcher().prepareCallableStatement( callString );
+ cs.registerOutParameter( 1, java.sql.Types.TIMESTAMP );
+ cs.execute();
+ Timestamp ts = cs.getTimestamp( 1 );
+ if ( log.isTraceEnabled() ) {
+ log.trace(
+ "current timestamp retreived from db : " + ts +
+ " (nanos=" + ts.getNanos() +
+ ", time=" + ts.getTime() + ")"
+ );
+ }
+ return ts;
+ }
+ catch( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ session.getFactory().getSQLExceptionConverter(),
+ sqle,
+ "could not call current db timestamp function",
+ callString
+ );
+ }
+ finally {
+ if ( cs != null ) {
+ try {
+ session.getBatcher().closeStatement( cs );
+ }
+ catch( SQLException sqle ) {
+ log.warn( "unable to clean up callable statement", sqle );
+ }
+ }
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/DiscriminatorType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/DiscriminatorType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/DiscriminatorType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,15 @@
+//$Id: DiscriminatorType.java 3890 2004-06-03 16:31:32Z steveebersole $
+package org.hibernate.type;
+
+/**
+ * A <tt>Type</tt> that may be used for a discriminator column.
+ * @author Gavin King
+ */
+public interface DiscriminatorType extends IdentifierType, LiteralType {
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/DoubleType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/DoubleType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/DoubleType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,57 @@
+//$Id: DoubleType.java 7825 2005-08-10 20:23:55Z oneovthafew $
+package org.hibernate.type;
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+
+import org.hibernate.dialect.Dialect;
+
+/**
+ * <tt>double</tt>: A type that maps an SQL DOUBLE to a Java Double.
+ * @author Gavin King
+ */
+public class DoubleType extends PrimitiveType {
+
+ public Serializable getDefaultValue() {
+ return new Double(0.0);
+ }
+
+ public Object get(ResultSet rs, String name) throws SQLException {
+ return new Double( rs.getDouble(name) );
+ }
+
+ public Class getPrimitiveClass() {
+ return double.class;
+ }
+
+ public Class getReturnedClass() {
+ return Double.class;
+ }
+
+ public void set(PreparedStatement st, Object value, int index)
+ throws SQLException {
+
+ st.setDouble( index, ( (Double) value ).doubleValue() );
+ }
+
+ public int sqlType() {
+ return Types.DOUBLE;
+ }
+ public String getName() { return "double"; }
+
+ public String objectToSQLString(Object value, Dialect dialect) throws Exception {
+ return value.toString();
+ }
+
+ public Object fromStringValue(String xml) {
+ return new Double(xml);
+ }
+
+}
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/EmbeddedComponentType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/EmbeddedComponentType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/EmbeddedComponentType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,39 @@
+//$Id: EmbeddedComponentType.java 10119 2006-07-14 00:09:19Z steve.ebersole at jboss.com $
+package org.hibernate.type;
+
+import java.lang.reflect.Method;
+
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.tuple.component.ComponentTuplizer;
+import org.hibernate.tuple.component.ComponentMetamodel;
+
+/**
+ * @author Gavin King
+ */
+public class EmbeddedComponentType extends ComponentType {
+
+ public boolean isEmbedded() {
+ return true;
+ }
+
+ public EmbeddedComponentType(ComponentMetamodel metamodel) {
+ super( metamodel );
+ }
+
+ public boolean isMethodOf(Method method) {
+ return ( ( ComponentTuplizer ) tuplizerMapping.getTuplizer(EntityMode.POJO) ).isMethodOf(method);
+ }
+
+ public Object instantiate(Object parent, SessionImplementor session)
+ throws HibernateException {
+ final boolean useParent = parent!=null &&
+ //TODO: Yuck! This is not quite good enough, it's a quick
+ //hack around the problem of having a to-one association
+ //that refers to an embedded component:
+ super.getReturnedClass().isInstance(parent);
+
+ return useParent ? parent : super.instantiate(parent, session);
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/type/EmbeddedComponentType.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/EntityType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/EntityType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/EntityType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,613 @@
+//$Id: EntityType.java 10777 2006-11-08 22:02:28Z steve.ebersole at jboss.com $
+package org.hibernate.type;
+
+import java.io.Serializable;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Map;
+
+import org.dom4j.Element;
+import org.dom4j.Node;
+import org.hibernate.AssertionFailure;
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.engine.EntityUniqueKey;
+import org.hibernate.engine.ForeignKeys;
+import org.hibernate.engine.Mapping;
+import org.hibernate.engine.PersistenceContext;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.persister.entity.Joinable;
+import org.hibernate.persister.entity.UniqueKeyLoadable;
+import org.hibernate.proxy.HibernateProxy;
+import org.hibernate.proxy.LazyInitializer;
+import org.hibernate.tuple.ElementWrapper;
+import org.hibernate.util.ReflectHelper;
+
+/**
+ * Base for types which map associations to persistent entities.
+ *
+ * @author Gavin King
+ */
+public abstract class EntityType extends AbstractType implements AssociationType {
+
+ private final String associatedEntityName;
+ protected final String uniqueKeyPropertyName;
+ protected final boolean isEmbeddedInXML;
+ private final boolean eager;
+ private final boolean unwrapProxy;
+
+ private transient Class returnedClass;
+
+ /**
+ * Constructs the requested entity type mapping.
+ *
+ * @param entityName The name of the associated entity.
+ * @param uniqueKeyPropertyName The property-ref name, or null if we
+ * reference the PK of the associated entity.
+ * @param eager Is eager fetching enabled.
+ * @param isEmbeddedInXML Should values of this mapping be embedded in XML modes?
+ * @param unwrapProxy Is unwrapping of proxies allowed for this association; unwrapping
+ * says to return the "implementation target" of lazy prooxies; typically only possible
+ * with lazy="no-proxy".
+ */
+ protected EntityType(
+ String entityName,
+ String uniqueKeyPropertyName,
+ boolean eager,
+ boolean isEmbeddedInXML,
+ boolean unwrapProxy) {
+ this.associatedEntityName = entityName;
+ this.uniqueKeyPropertyName = uniqueKeyPropertyName;
+ this.isEmbeddedInXML = isEmbeddedInXML;
+ this.eager = eager;
+ this.unwrapProxy = unwrapProxy;
+ }
+
+ /**
+ * An entity type is a type of association type
+ *
+ * @return True.
+ */
+ public boolean isAssociationType() {
+ return true;
+ }
+
+ /**
+ * Explicitly, an entity type is an entity type ;)
+ *
+ * @return True.
+ */
+ public final boolean isEntityType() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isMutable() {
+ return false;
+ }
+
+ /**
+ * Generates a string representation of this type.
+ *
+ * @return string rep
+ */
+ public String toString() {
+ return getClass().getName() + '(' + getAssociatedEntityName() + ')';
+ }
+
+ /**
+ * For entity types, the name correlates to the associated entity name.
+ */
+ public String getName() {
+ return associatedEntityName;
+ }
+
+ /**
+ * Does this association foreign key reference the primary key of the other table?
+ * Otherwise, it references a property-ref.
+ *
+ * @return True if this association reference the PK of the associated entity.
+ */
+ public boolean isReferenceToPrimaryKey() {
+ return uniqueKeyPropertyName==null;
+ }
+
+ public String getRHSUniqueKeyPropertyName() {
+ return uniqueKeyPropertyName;
+ }
+
+ public String getLHSPropertyName() {
+ return null;
+ }
+
+ public String getPropertyName() {
+ return null;
+ }
+
+ /**
+ * The name of the associated entity.
+ *
+ * @return The associated entity name.
+ */
+ public final String getAssociatedEntityName() {
+ return associatedEntityName;
+ }
+
+ /**
+ * The name of the associated entity.
+ *
+ * @param factory The session factory, for resolution.
+ * @return The associated entity name.
+ */
+ public String getAssociatedEntityName(SessionFactoryImplementor factory) {
+ return getAssociatedEntityName();
+ }
+
+ /**
+ * Retrieves the {@link Joinable} defining the associated entity.
+ *
+ * @param factory The session factory.
+ * @return The associated joinable
+ * @throws MappingException Generally indicates an invalid entity name.
+ */
+ public Joinable getAssociatedJoinable(SessionFactoryImplementor factory) throws MappingException {
+ return ( Joinable ) factory.getEntityPersister( associatedEntityName );
+ }
+
+ /**
+ * This returns the wrong class for an entity with a proxy, or for a named
+ * entity. Theoretically it should return the proxy class, but it doesn't.
+ * <p/>
+ * The problem here is that we do not necessarily have a ref to the associated
+ * entity persister (nor to the session factory, to look it up) which is really
+ * needed to "do the right thing" here...
+ *
+ * @return The entiyt class.
+ */
+ public final Class getReturnedClass() {
+ if ( returnedClass == null ) {
+ returnedClass = determineAssociatedEntityClass();
+ }
+ return returnedClass;
+ }
+
+ private Class determineAssociatedEntityClass() {
+ try {
+ return ReflectHelper.classForName( getAssociatedEntityName() );
+ }
+ catch ( ClassNotFoundException cnfe ) {
+ return java.util.Map.class;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Object nullSafeGet(ResultSet rs, String name, SessionImplementor session, Object owner)
+ throws HibernateException, SQLException {
+ return nullSafeGet( rs, new String[] {name}, session, owner );
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public final Object nullSafeGet(
+ ResultSet rs,
+ String[] names,
+ SessionImplementor session,
+ Object owner) throws HibernateException, SQLException {
+ return resolve( hydrate(rs, names, session, owner), session, owner );
+ }
+
+ /**
+ * Two entities are considered the same when their instances are the same.
+ *
+ * @param x One entity instance
+ * @param y Another entity instance
+ * @param entityMode The entity mode.
+ * @return True if x == y; false otherwise.
+ */
+ public final boolean isSame(Object x, Object y, EntityMode entityMode) {
+ return x == y;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int compare(Object x, Object y, EntityMode entityMode) {
+ return 0; //TODO: entities CAN be compared, by PK, fix this! -> only if/when we can extract the id values....
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Object deepCopy(Object value, EntityMode entityMode, SessionFactoryImplementor factory) {
+ return value; //special case ... this is the leaf of the containment graph, even though not immutable
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Object replace(
+ Object original,
+ Object target,
+ SessionImplementor session,
+ Object owner,
+ Map copyCache) throws HibernateException {
+ if ( original == null ) {
+ return null;
+ }
+ Object cached = copyCache.get(original);
+ if ( cached != null ) {
+ return cached;
+ }
+ else {
+ if ( original == target ) {
+ return target;
+ }
+ Object id = getIdentifier( original, session );
+ if ( id == null ) {
+ throw new AssertionFailure("cannot copy a reference to an object with a null id");
+ }
+ id = getIdentifierOrUniqueKeyType( session.getFactory() )
+ .replace(id, null, session, owner, copyCache);
+ return resolve( id, session, owner );
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int getHashCode(Object x, EntityMode entityMode, SessionFactoryImplementor factory) {
+ EntityPersister persister = factory.getEntityPersister(associatedEntityName);
+ if ( !persister.canExtractIdOutOfEntity() ) {
+ return super.getHashCode(x, entityMode);
+ }
+
+ final Serializable id;
+ if (x instanceof HibernateProxy) {
+ id = ( (HibernateProxy) x ).getHibernateLazyInitializer().getIdentifier();
+ }
+ else {
+ id = persister.getIdentifier(x, entityMode);
+ }
+ return persister.getIdentifierType().getHashCode(id, entityMode, factory);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isEqual(Object x, Object y, EntityMode entityMode, SessionFactoryImplementor factory) {
+ EntityPersister persister = factory.getEntityPersister(associatedEntityName);
+ if ( !persister.canExtractIdOutOfEntity() ) {
+ return super.isEqual(x, y, entityMode);
+ }
+
+ Serializable xid;
+ if (x instanceof HibernateProxy) {
+ xid = ( (HibernateProxy) x ).getHibernateLazyInitializer()
+ .getIdentifier();
+ }
+ else {
+ xid = persister.getIdentifier(x, entityMode);
+ }
+
+ Serializable yid;
+ if (y instanceof HibernateProxy) {
+ yid = ( (HibernateProxy) y ).getHibernateLazyInitializer()
+ .getIdentifier();
+ }
+ else {
+ yid = persister.getIdentifier(y, entityMode);
+ }
+
+ return persister.getIdentifierType()
+ .isEqual(xid, yid, entityMode, factory);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isEmbeddedInXML() {
+ return isEmbeddedInXML;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isXMLElement() {
+ return isEmbeddedInXML;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Object fromXMLNode(Node xml, Mapping factory) throws HibernateException {
+ if ( !isEmbeddedInXML ) {
+ return getIdentifierType(factory).fromXMLNode(xml, factory);
+ }
+ else {
+ return xml;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setToXMLNode(Node node, Object value, SessionFactoryImplementor factory) throws HibernateException {
+ if ( !isEmbeddedInXML ) {
+ getIdentifierType(factory).setToXMLNode(node, value, factory);
+ }
+ else {
+ Element elt = (Element) value;
+ replaceNode( node, new ElementWrapper(elt) );
+ }
+ }
+
+ public String getOnCondition(String alias, SessionFactoryImplementor factory, Map enabledFilters)
+ throws MappingException {
+ if ( isReferenceToPrimaryKey() ) { //TODO: this is a bit arbitrary, expose a switch to the user?
+ return "";
+ }
+ else {
+ return getAssociatedJoinable( factory ).filterFragment( alias, enabledFilters );
+ }
+ }
+
+ /**
+ * Resolve an identifier or unique key value
+ */
+ public Object resolve(Object value, SessionImplementor session, Object owner) throws HibernateException {
+ if ( isNotEmbedded( session ) ) {
+ return value;
+ }
+
+ if ( value == null ) {
+ return null;
+ }
+ else {
+ if ( isNull( owner, session ) ) {
+ return null; //EARLY EXIT!
+ }
+
+ if ( isReferenceToPrimaryKey() ) {
+ return resolveIdentifier( (Serializable) value, session );
+ }
+ else {
+ return loadByUniqueKey( getAssociatedEntityName(), uniqueKeyPropertyName, value, session );
+ }
+ }
+ }
+
+ public Type getSemiResolvedType(SessionFactoryImplementor factory) {
+ return factory.getEntityPersister( associatedEntityName ).getIdentifierType();
+ }
+
+ protected final Object getIdentifier(Object value, SessionImplementor session) throws HibernateException {
+ if ( isNotEmbedded(session) ) {
+ return value;
+ }
+
+ if ( isReferenceToPrimaryKey() ) {
+ return ForeignKeys.getEntityIdentifierIfNotUnsaved( getAssociatedEntityName(), value, session ); //tolerates nulls
+ }
+ else if ( value == null ) {
+ return null;
+ }
+ else {
+ EntityPersister entityPersister = session.getFactory().getEntityPersister( getAssociatedEntityName() );
+ Object propertyValue = entityPersister.getPropertyValue( value, uniqueKeyPropertyName, session.getEntityMode() );
+ // We now have the value of the property-ref we reference. However,
+ // we need to dig a little deeper, as that property might also be
+ // an entity type, in which case we need to resolve its identitifier
+ Type type = entityPersister.getPropertyType( uniqueKeyPropertyName );
+ if ( type.isEntityType() ) {
+ propertyValue = ( ( EntityType ) type ).getIdentifier( propertyValue, session );
+ }
+
+ return propertyValue;
+ }
+ }
+
+ protected boolean isNotEmbedded(SessionImplementor session) {
+ return !isEmbeddedInXML && session.getEntityMode()==EntityMode.DOM4J;
+ }
+
+ /**
+ * Get the identifier value of an instance or proxy.
+ * <p/>
+ * Intended only for loggin purposes!!!
+ *
+ * @param object The object from which to extract the identifier.
+ * @param persister The entity persister
+ * @param entityMode The entity mode
+ * @return The extracted identifier.
+ */
+ private static Serializable getIdentifier(Object object, EntityPersister persister, EntityMode entityMode) {
+ if (object instanceof HibernateProxy) {
+ HibernateProxy proxy = (HibernateProxy) object;
+ LazyInitializer li = proxy.getHibernateLazyInitializer();
+ return li.getIdentifier();
+ }
+ else {
+ return persister.getIdentifier( object, entityMode );
+ }
+ }
+
+ /**
+ * Generate a loggable representation of an instance of the value mapped by this type.
+ *
+ * @param value The instance to be logged.
+ * @param factory The session factory.
+ * @return The loggable string.
+ * @throws HibernateException Generally some form of resolution problem.
+ */
+ public String toLoggableString(Object value, SessionFactoryImplementor factory) {
+ if ( value == null ) {
+ return "null";
+ }
+
+ EntityPersister persister = factory.getEntityPersister( associatedEntityName );
+ StringBuffer result = new StringBuffer().append( associatedEntityName );
+
+ if ( persister.hasIdentifierProperty() ) {
+ final EntityMode entityMode = persister.guessEntityMode( value );
+ final Serializable id;
+ if ( entityMode == null ) {
+ if ( isEmbeddedInXML ) {
+ throw new ClassCastException( value.getClass().getName() );
+ }
+ id = ( Serializable ) value;
+ }
+ else {
+ id = getIdentifier( value, persister, entityMode );
+ }
+
+ result.append( '#' )
+ .append( persister.getIdentifierType().toLoggableString( id, factory ) );
+ }
+
+ return result.toString();
+ }
+
+ public abstract boolean isOneToOne();
+
+ /**
+ * Convenience method to locate the identifier type of the associated entity.
+ *
+ * @param factory The mappings...
+ * @return The identifier type
+ */
+ Type getIdentifierType(Mapping factory) {
+ return factory.getIdentifierType( getAssociatedEntityName() );
+ }
+
+ /**
+ * Convenience method to locate the identifier type of the associated entity.
+ *
+ * @param session The originating session
+ * @return The identifier type
+ */
+ Type getIdentifierType(SessionImplementor session) {
+ return getIdentifierType( session.getFactory() );
+ }
+
+ /**
+ * Determine the type of either (1) the identifier if we reference the
+ * associated entity's PK or (2) the unique key to which we refer (i.e.
+ * the property-ref).
+ *
+ * @param factory The mappings...
+ * @return The appropriate type.
+ * @throws MappingException Generally, if unable to resolve the associated entity name
+ * or unique key property name.
+ */
+ public final Type getIdentifierOrUniqueKeyType(Mapping factory) throws MappingException {
+ if ( isReferenceToPrimaryKey() ) {
+ return getIdentifierType(factory);
+ }
+ else {
+ Type type = factory.getReferencedPropertyType( getAssociatedEntityName(), uniqueKeyPropertyName );
+ if ( type.isEntityType() ) {
+ type = ( ( EntityType ) type).getIdentifierOrUniqueKeyType( factory );
+ }
+ return type;
+ }
+ }
+
+ /**
+ * The name of the property on the associated entity to which our FK
+ * refers
+ *
+ * @param factory The mappings...
+ * @return The appropriate property name.
+ * @throws MappingException Generally, if unable to resolve the associated entity name
+ */
+ public final String getIdentifierOrUniqueKeyPropertyName(Mapping factory)
+ throws MappingException {
+ if ( isReferenceToPrimaryKey() ) {
+ return factory.getIdentifierPropertyName( getAssociatedEntityName() );
+ }
+ else {
+ return uniqueKeyPropertyName;
+ }
+ }
+
+ protected abstract boolean isNullable();
+
+ /**
+ * Resolve an identifier via a load.
+ *
+ * @param id The entity id to resolve
+ * @param session The orginating session.
+ * @return The resolved identifier (i.e., loaded entity).
+ * @throws org.hibernate.HibernateException Indicates problems performing the load.
+ */
+ protected final Object resolveIdentifier(Serializable id, SessionImplementor session) throws HibernateException {
+ boolean isProxyUnwrapEnabled = unwrapProxy &&
+ session.getFactory()
+ .getEntityPersister( getAssociatedEntityName() )
+ .isInstrumented( session.getEntityMode() );
+
+ Object proxyOrEntity = session.internalLoad(
+ getAssociatedEntityName(),
+ id,
+ eager,
+ isNullable() && !isProxyUnwrapEnabled
+ );
+
+ if ( proxyOrEntity instanceof HibernateProxy ) {
+ ( ( HibernateProxy ) proxyOrEntity ).getHibernateLazyInitializer()
+ .setUnwrap( isProxyUnwrapEnabled );
+ }
+
+ return proxyOrEntity;
+ }
+
+ protected boolean isNull(Object owner, SessionImplementor session) {
+ return false;
+ }
+
+ /**
+ * Load an instance by a unique key that is not the primary key.
+ *
+ * @param entityName The name of the entity to load
+ * @param uniqueKeyPropertyName The name of the property defining the uniqie key.
+ * @param key The unique key property value.
+ * @param session The originating session.
+ * @return The loaded entity
+ * @throws HibernateException generally indicates problems performing the load.
+ */
+ public Object loadByUniqueKey(
+ String entityName,
+ String uniqueKeyPropertyName,
+ Object key,
+ SessionImplementor session) throws HibernateException {
+ final SessionFactoryImplementor factory = session.getFactory();
+ UniqueKeyLoadable persister = ( UniqueKeyLoadable ) factory.getEntityPersister( entityName );
+
+ //TODO: implement caching?! proxies?!
+
+ EntityUniqueKey euk = new EntityUniqueKey(
+ entityName,
+ uniqueKeyPropertyName,
+ key,
+ getIdentifierOrUniqueKeyType( factory ),
+ session.getEntityMode(),
+ session.getFactory()
+ );
+
+ final PersistenceContext persistenceContext = session.getPersistenceContext();
+ Object result = persistenceContext.getEntity( euk );
+ if ( result == null ) {
+ result = persister.loadByUniqueKey( uniqueKeyPropertyName, key, session );
+ }
+ return result == null ? null : persistenceContext.proxyFor( result );
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/FloatType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/FloatType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/FloatType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,59 @@
+//$Id: FloatType.java 7825 2005-08-10 20:23:55Z oneovthafew $
+package org.hibernate.type;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+
+import org.hibernate.dialect.Dialect;
+
+/**
+ * <tt>float</tt>: A type that maps an SQL FLOAT to a Java Float.
+ * @author Gavin King
+ */
+public class FloatType extends PrimitiveType {
+
+ public Serializable getDefaultValue() {
+ return new Float(0.0);
+ }
+
+ public Object get(ResultSet rs, String name) throws SQLException {
+ return new Float( rs.getFloat(name) );
+ }
+
+ public Class getPrimitiveClass() {
+ return float.class;
+ }
+
+ public Class getReturnedClass() {
+ return Float.class;
+ }
+
+ public void set(PreparedStatement st, Object value, int index)
+ throws SQLException {
+
+ st.setFloat( index, ( (Float) value ).floatValue() );
+ }
+
+ public int sqlType() {
+ return Types.FLOAT;
+ }
+
+ public String getName() { return "float"; }
+
+ public String objectToSQLString(Object value, Dialect dialect) throws Exception {
+ return value.toString();
+ }
+
+ public Object fromStringValue(String xml) {
+ return new Float(xml);
+ }
+
+}
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/ForeignKeyDirection.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/ForeignKeyDirection.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/ForeignKeyDirection.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,52 @@
+//$Id: ForeignKeyDirection.java 7019 2005-06-05 05:09:58Z oneovthafew $
+package org.hibernate.type;
+
+import java.io.Serializable;
+
+import org.hibernate.engine.Cascade;
+
+/**
+ * Represents directionality of the foreign key constraint
+ * @author Gavin King
+ */
+public abstract class ForeignKeyDirection implements Serializable {
+ protected ForeignKeyDirection() {}
+ /**
+ * Should we cascade at this cascade point?
+ * @see org.hibernate.engine.Cascade
+ */
+ public abstract boolean cascadeNow(int cascadePoint);
+
+ /**
+ * A foreign key from child to parent
+ */
+ public static final ForeignKeyDirection FOREIGN_KEY_TO_PARENT = new ForeignKeyDirection() {
+ public boolean cascadeNow(int cascadePoint) {
+ return cascadePoint!=Cascade.BEFORE_INSERT_AFTER_DELETE;
+ }
+
+ public String toString() {
+ return "toParent";
+ }
+
+ Object readResolve() {
+ return FOREIGN_KEY_TO_PARENT;
+ }
+ };
+ /**
+ * A foreign key from parent to child
+ */
+ public static final ForeignKeyDirection FOREIGN_KEY_FROM_PARENT = new ForeignKeyDirection() {
+ public boolean cascadeNow(int cascadePoint) {
+ return cascadePoint!=Cascade.AFTER_INSERT_BEFORE_DELETE;
+ }
+
+ public String toString() {
+ return "fromParent";
+ }
+
+ Object readResolve() {
+ return FOREIGN_KEY_FROM_PARENT;
+ }
+ };
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/IdentifierBagType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/IdentifierBagType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/IdentifierBagType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,45 @@
+//$Id: IdentifierBagType.java 10086 2006-07-05 18:17:27Z steve.ebersole at jboss.com $
+package org.hibernate.type;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+
+import org.hibernate.HibernateException;
+import org.hibernate.collection.PersistentIdentifierBag;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.collection.CollectionPersister;
+
+public class IdentifierBagType extends CollectionType {
+
+ public IdentifierBagType(String role, String propertyRef, boolean isEmbeddedInXML) {
+ super(role, propertyRef, isEmbeddedInXML);
+ }
+
+ public PersistentCollection instantiate(
+ SessionImplementor session,
+ CollectionPersister persister, Serializable key)
+ throws HibernateException {
+
+ return new PersistentIdentifierBag(session);
+ }
+
+ public Object instantiate(int anticipatedSize) {
+ return anticipatedSize <= 0 ? new ArrayList() : new ArrayList( anticipatedSize + 1 );
+ }
+
+ public Class getReturnedClass() {
+ return java.util.Collection.class;
+ }
+
+ public PersistentCollection wrap(SessionImplementor session, Object collection) {
+ return new PersistentIdentifierBag( session, (java.util.Collection) collection );
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/IdentifierType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/IdentifierType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/IdentifierType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,24 @@
+//$Id: IdentifierType.java 3890 2004-06-03 16:31:32Z steveebersole $
+package org.hibernate.type;
+
+/**
+ * A <tt>Type</tt> that may be used as an identifier.
+ * @author Gavin King
+ */
+public interface IdentifierType extends Type {
+
+ /**
+ * Convert the value from the mapping file to a Java object.
+ * @param xml the value of <tt>discriminator-value</tt> or <tt>unsaved-value</tt> attribute
+ * @return Object
+ * @throws Exception
+ */
+ public Object stringToObject(String xml) throws Exception;
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/ImmutableType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/ImmutableType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/ImmutableType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,36 @@
+//$Id: ImmutableType.java 5744 2005-02-16 12:50:19Z oneovthafew $
+package org.hibernate.type;
+
+import java.util.Map;
+
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+
+/**
+ * Superclass of nullable immutable types.
+ * @author Gavin King
+ */
+public abstract class ImmutableType extends NullableType {
+
+ public final Object deepCopy(Object value, EntityMode entityMode, SessionFactoryImplementor factory) {
+ return value;
+ }
+
+ public final boolean isMutable() {
+ return false;
+ }
+
+ public Object replace(
+ Object original,
+ Object target,
+ SessionImplementor session,
+ Object owner,
+ Map copyCache)
+ throws HibernateException {
+ return original;
+ }
+
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/IntegerType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/IntegerType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/IntegerType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,74 @@
+//$Id: IntegerType.java 7825 2005-08-10 20:23:55Z oneovthafew $
+package org.hibernate.type;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.Comparator;
+
+import org.hibernate.util.ComparableComparator;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.SessionImplementor;
+
+/**
+ * <tt>integer</tt>: A type that maps an SQL INT to a Java Integer.
+ * @author Gavin King
+ */
+public class IntegerType extends PrimitiveType implements DiscriminatorType, VersionType {
+
+ private static final Integer ZERO = new Integer(0);
+
+ public Serializable getDefaultValue() {
+ return ZERO;
+ }
+
+ public Object get(ResultSet rs, String name) throws SQLException {
+ return new Integer( rs.getInt(name) );
+ }
+
+ public Class getPrimitiveClass() {
+ return int.class;
+ }
+
+ public Class getReturnedClass() {
+ return Integer.class;
+ }
+
+ public void set(PreparedStatement st, Object value, int index)
+ throws SQLException {
+ st.setInt( index, ( (Integer) value ).intValue() );
+ }
+
+ public int sqlType() {
+ return Types.INTEGER;
+ }
+
+ public String getName() { return "integer"; }
+
+ public String objectToSQLString(Object value, Dialect dialect) throws Exception {
+ return value.toString();
+ }
+
+ public Object stringToObject(String xml) throws Exception {
+ return new Integer(xml);
+ }
+
+ public Object next(Object current, SessionImplementor session) {
+ return new Integer( ( (Integer) current ).intValue() + 1 );
+ }
+
+ public Object seed(SessionImplementor session) {
+ return ZERO;
+ }
+
+ public Comparator getComparator() {
+ return ComparableComparator.INSTANCE;
+ }
+
+ public Object fromStringValue(String xml) {
+ return new Integer(xml);
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/ListType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/ListType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/ListType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,62 @@
+//$Id: ListType.java 10086 2006-07-05 18:17:27Z steve.ebersole at jboss.com $
+package org.hibernate.type;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.dom4j.Element;
+import org.hibernate.EntityMode;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.collection.PersistentList;
+import org.hibernate.collection.PersistentListElementHolder;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.collection.CollectionPersister;
+
+public class ListType extends CollectionType {
+
+ public ListType(String role, String propertyRef, boolean isEmbeddedInXML) {
+ super(role, propertyRef, isEmbeddedInXML);
+ }
+
+ public PersistentCollection instantiate(SessionImplementor session, CollectionPersister persister, Serializable key) {
+ if ( session.getEntityMode()==EntityMode.DOM4J ) {
+ return new PersistentListElementHolder(session, persister, key);
+ }
+ else {
+ return new PersistentList(session);
+ }
+ }
+
+ public Class getReturnedClass() {
+ return List.class;
+ }
+
+ public PersistentCollection wrap(SessionImplementor session, Object collection) {
+ if ( session.getEntityMode()==EntityMode.DOM4J ) {
+ return new PersistentListElementHolder( session, (Element) collection );
+ }
+ else {
+ return new PersistentList( session, (List) collection );
+ }
+ }
+
+ public Object instantiate(int anticipatedSize) {
+ return anticipatedSize <= 0 ? new ArrayList() : new ArrayList( anticipatedSize + 1 );
+ }
+
+ public Object indexOf(Object collection, Object element) {
+ List list = (List) collection;
+ for ( int i=0; i<list.size(); i++ ) {
+ //TODO: proxies!
+ if ( list.get(i)==element ) return new Integer(i);
+ }
+ return null;
+ }
+
+}
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/LiteralType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/LiteralType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/LiteralType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,27 @@
+//$Id: LiteralType.java 7825 2005-08-10 20:23:55Z oneovthafew $
+package org.hibernate.type;
+
+import org.hibernate.dialect.Dialect;
+
+/**
+ * A type that may appear as an SQL literal
+ * @author Gavin King
+ */
+public interface LiteralType {
+ /**
+ * String representation of the value, suitable for embedding in
+ * an SQL statement.
+ * @param value
+ * @param dialect
+ * @return String the value, as it appears in a SQL query
+ * @throws Exception
+ */
+ public String objectToSQLString(Object value, Dialect dialect) throws Exception;
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/LocaleType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/LocaleType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/LocaleType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,78 @@
+//$Id: LocaleType.java 7825 2005-08-10 20:23:55Z oneovthafew $
+package org.hibernate.type;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Locale;
+import java.util.StringTokenizer;
+
+import org.hibernate.EntityMode;
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.dialect.Dialect;
+
+/**
+ * <tt>locale</tt>: A type that maps an SQL VARCHAR to a Java Locale.
+ * @author Gavin King
+ */
+public class LocaleType extends ImmutableType implements LiteralType {
+
+ public Object get(ResultSet rs, String name) throws HibernateException, SQLException {
+ return fromStringValue( (String) Hibernate.STRING.get(rs, name) );
+ }
+
+ public void set(PreparedStatement st, Object value, int index) throws HibernateException, SQLException {
+ Hibernate.STRING.set(st, value.toString(), index);
+ }
+
+ public Object fromStringValue(String string) {
+ if (string == null) {
+ return null;
+ }
+ else {
+ StringTokenizer tokens = new StringTokenizer(string, "_");
+ String language = tokens.hasMoreTokens() ? tokens.nextToken() : "";
+ String country = tokens.hasMoreTokens() ? tokens.nextToken() : "";
+ // Need to account for allowable '_' within the variant
+ String variant = "";
+ String sep = "";
+ while ( tokens.hasMoreTokens() ) {
+ variant += sep + tokens.nextToken();
+ sep = "_";
+ }
+ return new Locale(language, country, variant);
+ }
+ }
+
+ public int compare(Object x, Object y, EntityMode entityMode) {
+ return x.toString().compareTo( y.toString() );
+ }
+
+ public int sqlType() {
+ return Hibernate.STRING.sqlType();
+ }
+
+ public String toString(Object value) throws HibernateException {
+ return value.toString();
+ }
+
+ public Class getReturnedClass() {
+ return Locale.class;
+ }
+
+ public String getName() {
+ return "locale";
+ }
+
+ public String objectToSQLString(Object value, Dialect dialect) throws Exception {
+ return ( (LiteralType) Hibernate.STRING ).objectToSQLString( value.toString(), dialect );
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/LongType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/LongType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/LongType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,76 @@
+//$Id: LongType.java 7825 2005-08-10 20:23:55Z oneovthafew $
+package org.hibernate.type;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.Comparator;
+
+import org.hibernate.util.ComparableComparator;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.SessionImplementor;
+
+/**
+ * <tt>long</tt>: A type that maps an SQL BIGINT to a Java Long.
+ * @author Gavin King
+ */
+public class LongType extends PrimitiveType implements DiscriminatorType, VersionType {
+
+ private static final Long ZERO = new Long(0);
+
+ public Serializable getDefaultValue() {
+ return ZERO;
+ }
+
+ public Object get(ResultSet rs, String name) throws SQLException {
+ return new Long( rs.getLong(name) );
+ }
+
+ public Class getPrimitiveClass() {
+ return long.class;
+ }
+
+ public Class getReturnedClass() {
+ return Long.class;
+ }
+
+ public void set(PreparedStatement st, Object value, int index)
+ throws SQLException {
+
+ st.setLong( index, ( (Long) value ).longValue() );
+ }
+
+ public int sqlType() {
+ return Types.BIGINT;
+ }
+
+ public String getName() { return "long"; }
+
+ public Object stringToObject(String xml) throws Exception {
+ return new Long(xml);
+ }
+
+ public Object next(Object current, SessionImplementor session) {
+ return new Long( ( (Long) current ).longValue() + 1 );
+ }
+
+ public Object seed(SessionImplementor session) {
+ return ZERO;
+ }
+
+ public Comparator getComparator() {
+ return ComparableComparator.INSTANCE;
+ }
+
+ public String objectToSQLString(Object value, Dialect dialect) throws Exception {
+ return value.toString();
+ }
+
+ public Object fromStringValue(String xml) {
+ return new Long(xml);
+ }
+
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/ManyToOneType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/ManyToOneType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/ManyToOneType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,248 @@
+//$Id: ManyToOneType.java 10020 2006-06-15 16:38:03Z steve.ebersole at jboss.com $
+package org.hibernate.type;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Arrays;
+
+import org.hibernate.AssertionFailure;
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.engine.EntityKey;
+import org.hibernate.engine.ForeignKeys;
+import org.hibernate.engine.Mapping;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.entity.EntityPersister;
+
+/**
+ * A many-to-one association to an entity.
+ *
+ * @author Gavin King
+ */
+public class ManyToOneType extends EntityType {
+
+ private final boolean ignoreNotFound;
+
+ public ManyToOneType(String className) {
+ this( className, false );
+ }
+
+ public ManyToOneType(String className, boolean lazy) {
+ super( className, null, !lazy, true, false );
+ this.ignoreNotFound = false;
+ }
+
+ public ManyToOneType(
+ String entityName,
+ String uniqueKeyPropertyName,
+ boolean lazy,
+ boolean unwrapProxy,
+ boolean isEmbeddedInXML,
+ boolean ignoreNotFound) {
+ super( entityName, uniqueKeyPropertyName, !lazy, isEmbeddedInXML, unwrapProxy );
+ this.ignoreNotFound = ignoreNotFound;
+ }
+
+ protected boolean isNullable() {
+ return ignoreNotFound;
+ }
+
+ public boolean isAlwaysDirtyChecked() {
+ // If we have <tt>not-found="ignore"</tt> association mapped to a
+ // formula, we always need to dirty check it, so we can update the
+ // second-level cache
+ return ignoreNotFound;
+ }
+
+ public boolean isOneToOne() {
+ return false;
+ }
+
+ public int getColumnSpan(Mapping mapping) throws MappingException {
+ // our column span is the number of columns in the PK
+ return getIdentifierOrUniqueKeyType( mapping ).getColumnSpan( mapping );
+ }
+
+ public int[] sqlTypes(Mapping mapping) throws MappingException {
+ return getIdentifierOrUniqueKeyType( mapping ).sqlTypes( mapping );
+ }
+
+ public void nullSafeSet(
+ PreparedStatement st,
+ Object value,
+ int index,
+ boolean[] settable,
+ SessionImplementor session) throws HibernateException, SQLException {
+ getIdentifierOrUniqueKeyType( session.getFactory() )
+ .nullSafeSet( st, getIdentifier( value, session ), index, settable, session );
+ }
+
+ public void nullSafeSet(
+ PreparedStatement st,
+ Object value,
+ int index,
+ SessionImplementor session) throws HibernateException, SQLException {
+ getIdentifierOrUniqueKeyType( session.getFactory() )
+ .nullSafeSet( st, getIdentifier( value, session ), index, session );
+ }
+
+ public ForeignKeyDirection getForeignKeyDirection() {
+ return ForeignKeyDirection.FOREIGN_KEY_FROM_PARENT;
+ }
+
+ public Object hydrate(
+ ResultSet rs,
+ String[] names,
+ SessionImplementor session,
+ Object owner) throws HibernateException, SQLException {
+ // return the (fully resolved) identifier value, but do not resolve
+ // to the actual referenced entity instance
+ // NOTE: the owner of the association is not really the owner of the id!
+ Serializable id = (Serializable) getIdentifierOrUniqueKeyType( session.getFactory() )
+ .nullSafeGet( rs, names, session, null );
+ scheduleBatchLoadIfNeeded( id, session );
+ return id;
+ }
+
+ /**
+ * Register the entity as batch loadable, if enabled
+ */
+ private void scheduleBatchLoadIfNeeded(
+ Serializable id,
+ SessionImplementor session) throws MappingException {
+ //cannot batch fetch by unique key (property-ref associations)
+ if ( uniqueKeyPropertyName == null && id != null ) {
+ EntityPersister persister = session.getFactory().getEntityPersister( getAssociatedEntityName() );
+ EntityKey entityKey = new EntityKey( id, persister, session.getEntityMode() );
+ if ( !session.getPersistenceContext().containsEntity( entityKey ) ) {
+ session.getPersistenceContext()
+ .getBatchFetchQueue()
+ .addBatchLoadableEntityKey( entityKey );
+ }
+ }
+ }
+
+ public boolean useLHSPrimaryKey() {
+ return false;
+ }
+
+ public boolean isModified(
+ Object old,
+ Object current,
+ boolean[] checkable,
+ SessionImplementor session) throws HibernateException {
+ if ( current == null ) {
+ return old!=null;
+ }
+ if ( old == null ) {
+ // we already know current is not null...
+ return true;
+ }
+ // the ids are fully resolved, so compare them with isDirty(), not isModified()
+ return getIdentifierOrUniqueKeyType( session.getFactory() )
+ .isDirty( old, getIdentifier( current, session ), session );
+ }
+
+ public Serializable disassemble(
+ Object value,
+ SessionImplementor session,
+ Object owner) throws HibernateException {
+
+ if ( isNotEmbedded( session ) ) {
+ return getIdentifierType( session ).disassemble( value, session, owner );
+ }
+
+ if ( value == null ) {
+ return null;
+ }
+ else {
+ // cache the actual id of the object, not the value of the
+ // property-ref, which might not be initialized
+ Object id = ForeignKeys.getEntityIdentifierIfNotUnsaved(
+ getAssociatedEntityName(),
+ value,
+ session
+ );
+ if ( id == null ) {
+ throw new AssertionFailure(
+ "cannot cache a reference to an object with a null id: " +
+ getAssociatedEntityName()
+ );
+ }
+ return getIdentifierType( session ).disassemble( id, session, owner );
+ }
+ }
+
+ public Object assemble(
+ Serializable oid,
+ SessionImplementor session,
+ Object owner) throws HibernateException {
+
+ //TODO: currently broken for unique-key references (does not detect
+ // change to unique key property of the associated object)
+
+ Serializable id = assembleId( oid, session );
+
+ if ( isNotEmbedded( session ) ) {
+ return id;
+ }
+
+ if ( id == null ) {
+ return null;
+ }
+ else {
+ return resolveIdentifier( id, session );
+ }
+ }
+
+ private Serializable assembleId(Serializable oid, SessionImplementor session) {
+ //the owner of the association is not the owner of the id
+ return ( Serializable ) getIdentifierType( session ).assemble( oid, session, null );
+ }
+
+ public void beforeAssemble(Serializable oid, SessionImplementor session) {
+ scheduleBatchLoadIfNeeded( assembleId( oid, session ), session );
+ }
+
+ public boolean[] toColumnNullness(Object value, Mapping mapping) {
+ boolean[] result = new boolean[ getColumnSpan( mapping ) ];
+ if ( value != null ) {
+ Arrays.fill( result, true );
+ }
+ return result;
+ }
+
+ public boolean isDirty(
+ Object old,
+ Object current,
+ SessionImplementor session) throws HibernateException {
+ if ( isSame( old, current, session.getEntityMode() ) ) {
+ return false;
+ }
+ Object oldid = getIdentifier( old, session );
+ Object newid = getIdentifier( current, session );
+ return getIdentifierType( session ).isDirty( oldid, newid, session );
+ }
+
+ public boolean isDirty(
+ Object old,
+ Object current,
+ boolean[] checkable,
+ SessionImplementor session) throws HibernateException {
+ if ( isAlwaysDirtyChecked() ) {
+ return isDirty( old, current, session );
+ }
+ else {
+ if ( isSame( old, current, session.getEntityMode() ) ) {
+ return false;
+ }
+ Object oldid = getIdentifier( old, session );
+ Object newid = getIdentifier( current, session );
+ return getIdentifierType( session ).isDirty( oldid, newid, checkable, session );
+ }
+
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/MapType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/MapType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/MapType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,92 @@
+//$Id: MapType.java 10086 2006-07-05 18:17:27Z steve.ebersole at jboss.com $
+package org.hibernate.type;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.dom4j.Element;
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.collection.PersistentMap;
+import org.hibernate.collection.PersistentMapElementHolder;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.collection.CollectionPersister;
+
+
+public class MapType extends CollectionType {
+
+ public MapType(String role, String propertyRef, boolean isEmbeddedInXML) {
+ super(role, propertyRef, isEmbeddedInXML);
+ }
+
+ public PersistentCollection instantiate(SessionImplementor session, CollectionPersister persister, Serializable key) {
+ if ( session.getEntityMode()==EntityMode.DOM4J ) {
+ return new PersistentMapElementHolder(session, persister, key);
+ }
+ else {
+ return new PersistentMap(session);
+ }
+ }
+
+ public Class getReturnedClass() {
+ return Map.class;
+ }
+
+ public Iterator getElementsIterator(Object collection) {
+ return ( (java.util.Map) collection ).values().iterator();
+ }
+
+ public PersistentCollection wrap(SessionImplementor session, Object collection) {
+ if ( session.getEntityMode()==EntityMode.DOM4J ) {
+ return new PersistentMapElementHolder( session, (Element) collection );
+ }
+ else {
+ return new PersistentMap( session, (java.util.Map) collection );
+ }
+ }
+
+ public Object instantiate(int anticipatedSize) {
+ return anticipatedSize <= 0
+ ? new HashMap()
+ : new HashMap( anticipatedSize + (int)( anticipatedSize * .75f ), .75f );
+ }
+
+ public Object replaceElements(
+ final Object original,
+ final Object target,
+ final Object owner,
+ final java.util.Map copyCache,
+ final SessionImplementor session)
+ throws HibernateException {
+
+ CollectionPersister cp = session.getFactory().getCollectionPersister( getRole() );
+
+ java.util.Map result = (java.util.Map) target;
+ result.clear();
+
+ Iterator iter = ( (java.util.Map) original ).entrySet().iterator();
+ while ( iter.hasNext() ) {
+ java.util.Map.Entry me = (java.util.Map.Entry) iter.next();
+ Object key = cp.getIndexType().replace( me.getKey(), null, session, owner, copyCache );
+ Object value = cp.getElementType().replace( me.getValue(), null, session, owner, copyCache );
+ result.put(key, value);
+ }
+
+ return result;
+
+ }
+
+ public Object indexOf(Object collection, Object element) {
+ Iterator iter = ( (Map) collection ).entrySet().iterator();
+ while ( iter.hasNext() ) {
+ Map.Entry me = (Map.Entry) iter.next();
+ //TODO: proxies!
+ if ( me.getValue()==element ) return me.getKey();
+ }
+ return null;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/MetaType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/MetaType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/MetaType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,139 @@
+//$Id: MetaType.java 7644 2005-07-25 06:53:09Z oneovthafew $
+package org.hibernate.type;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.dom4j.Node;
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.engine.Mapping;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+
+/**
+ * @author Gavin King
+ */
+public class MetaType extends AbstractType {
+
+ private final Map values;
+ private final Map keys;
+ private final Type baseType;
+
+ public MetaType(Map values, Type baseType) {
+ this.baseType = baseType;
+ this.values = values;
+ keys = new HashMap();
+ Iterator iter = values.entrySet().iterator();
+ while ( iter.hasNext() ) {
+ Map.Entry me = (Map.Entry) iter.next();
+ keys.put( me.getValue(), me.getKey() );
+ }
+ }
+
+ public int[] sqlTypes(Mapping mapping) throws MappingException {
+ return baseType.sqlTypes(mapping);
+ }
+
+ public int getColumnSpan(Mapping mapping) throws MappingException {
+ return baseType.getColumnSpan(mapping);
+ }
+
+ public Class getReturnedClass() {
+ return String.class;
+ }
+
+ public Object nullSafeGet(
+ ResultSet rs,
+ String[] names,
+ SessionImplementor session,
+ Object owner)
+ throws HibernateException, SQLException {
+ Object key = baseType.nullSafeGet(rs, names, session, owner);
+ return key==null ? null : values.get(key);
+ }
+
+ public Object nullSafeGet(
+ ResultSet rs,
+ String name,
+ SessionImplementor session,
+ Object owner)
+ throws HibernateException, SQLException {
+ Object key = baseType.nullSafeGet(rs, name, session, owner);
+ return key==null ? null : values.get(key);
+ }
+
+ public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session)
+ throws HibernateException, SQLException {
+ baseType.nullSafeSet(st, value==null ? null : keys.get(value), index, session);
+ }
+
+ public void nullSafeSet(
+ PreparedStatement st,
+ Object value,
+ int index,
+ boolean[] settable,
+ SessionImplementor session)
+ throws HibernateException, SQLException {
+ if ( settable[0] ) nullSafeSet(st, value, index, session);
+ }
+
+ public String toLoggableString(Object value, SessionFactoryImplementor factory) throws HibernateException {
+ return toXMLString(value, factory);
+ }
+
+ public String toXMLString(Object value, SessionFactoryImplementor factory)
+ throws HibernateException {
+ return (String) value; //value is the entity name
+ }
+
+ public Object fromXMLString(String xml, Mapping factory)
+ throws HibernateException {
+ return xml; //xml is the entity name
+ }
+
+ public String getName() {
+ return baseType.getName(); //TODO!
+ }
+
+ public Object deepCopy(Object value, EntityMode entityMode, SessionFactoryImplementor factory)
+ throws HibernateException {
+ return value;
+ }
+
+ public Object replace(
+ Object original,
+ Object target,
+ SessionImplementor session,
+ Object owner,
+ Map copyCache
+ ) {
+ return original;
+ }
+
+ public boolean isMutable() {
+ return false;
+ }
+
+ public Object fromXMLNode(Node xml, Mapping factory) throws HibernateException {
+ return fromXMLString( xml.getText(), factory );
+ }
+
+ public void setToXMLNode(Node node, Object value, SessionFactoryImplementor factory) throws HibernateException {
+ node.setText( toXMLString(value, factory) );
+ }
+
+ public boolean[] toColumnNullness(Object value, Mapping mapping) {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean isDirty(Object old, Object current, boolean[] checkable, SessionImplementor session) throws HibernateException {
+ return checkable[0] && isDirty(old, current, session);
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/MutableType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/MutableType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/MutableType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,39 @@
+//$Id: MutableType.java 6883 2005-05-24 16:22:15Z oneovthafew $
+package org.hibernate.type;
+
+import java.util.Map;
+
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+
+/**
+ * Superclass for mutable nullable types
+ * @author Gavin King
+ */
+public abstract class MutableType extends NullableType {
+
+ public final boolean isMutable() {
+ return true;
+ }
+
+ protected abstract Object deepCopyNotNull(Object value) throws HibernateException;
+
+ public final Object deepCopy(Object value, EntityMode entityMode, SessionFactoryImplementor factory)
+ throws HibernateException {
+ return (value==null) ? null : deepCopyNotNull(value);
+ }
+
+ public Object replace(
+ Object original,
+ Object target,
+ SessionImplementor session,
+ Object owner,
+ Map copyCache)
+ throws HibernateException {
+ if ( isEqual( original, target, session.getEntityMode() ) ) return original;
+ return deepCopy( original, session.getEntityMode(), session.getFactory() );
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/NullableType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/NullableType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/NullableType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,236 @@
+//$Id: NullableType.java 11047 2007-01-16 15:26:09Z steve.ebersole at jboss.com $
+package org.hibernate.type;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.logging.Log;
+import org.dom4j.Node;
+
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.Mapping;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.util.ArrayHelper;
+import org.hibernate.util.EqualsHelper;
+import org.hibernate.util.StringHelper;
+
+/**
+ * Superclass of single-column nullable types.
+ *
+ * @author Gavin King
+ */
+public abstract class NullableType extends AbstractType {
+
+ /**
+ * This is the old scheme where logging of parameter bindings and value extractions
+ * was controlled by the trace level enablement on the 'org.hibernate.type' package...
+ * <p/>
+ * Originally was cached such because of performance of looking up the logger each time
+ * in order to check the trace-enablement. Driving this via a central Log-specific class
+ * would alleviate that performance hit, and yet still allow more "normal" logging usage/config.
+ */
+ private static final boolean IS_VALUE_TRACING_ENABLED = LogFactory.getLog( StringHelper.qualifier( Type.class.getName() ) ).isTraceEnabled();
+ private transient Log log;
+
+ private Log log() {
+ if ( log == null ) {
+ log = LogFactory.getLog( getClass() );
+ }
+ return log;
+ }
+
+ /**
+ * Get a column value from a result set, without worrying about the
+ * possibility of null values. Called from {@link #nullSafeGet} after
+ * nullness checks have been performed.
+ *
+ * @param rs The result set from which to extract the value.
+ * @param name The name of the value to extract.
+ *
+ * @return The extracted value.
+ *
+ * @throws org.hibernate.HibernateException Generally some form of mismatch error.
+ * @throws java.sql.SQLException Indicates problem making the JDBC call(s).
+ */
+ public abstract Object get(ResultSet rs, String name) throws HibernateException, SQLException;
+
+ /**
+ * Set a parameter value without worrying about the possibility of null
+ * values. Called from {@link #nullSafeSet} after nullness checks have
+ * been performed.
+ *
+ * @param st The statement into which to bind the parameter value.
+ * @param value The parameter value to bind.
+ * @param index The position or index at which to bind the param value.
+ *
+ * @throws org.hibernate.HibernateException Generally some form of mismatch error.
+ * @throws java.sql.SQLException Indicates problem making the JDBC call(s).
+ */
+ public abstract void set(PreparedStatement st, Object value, int index) throws HibernateException, SQLException;
+
+ /**
+ * A convenience form of {@link #sqlTypes(org.hibernate.engine.Mapping)}, returning
+ * just a single type value since these are explicitly dealing with single column
+ * mappings.
+ *
+ * @return The {@link java.sql.Types} mapping value.
+ */
+ public abstract int sqlType();
+
+ /**
+ * A null-safe version of {@link #toString(Object)}. Specifically we are
+ * worried about null safeness in regards to the incoming value parameter,
+ * not the return.
+ *
+ * @param value The value to convert to a string representation; may be null.
+ * @return The string representation; may be null.
+ * @throws HibernateException Thrown by {@link #toString(Object)}, which this calls.
+ */
+ public String nullSafeToString(Object value) throws HibernateException {
+ return value == null ? null : toString( value );
+ }
+
+ public abstract String toString(Object value) throws HibernateException;
+
+ public abstract Object fromStringValue(String xml) throws HibernateException;
+
+ public final void nullSafeSet(
+ PreparedStatement st,
+ Object value,
+ int index,
+ boolean[] settable,
+ SessionImplementor session)
+ throws HibernateException, SQLException {
+ if ( settable[0] ) nullSafeSet(st, value, index);
+ }
+
+ public final void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session)
+ throws HibernateException, SQLException {
+ nullSafeSet(st, value, index);
+ }
+
+ public final void nullSafeSet(PreparedStatement st, Object value, int index)
+ throws HibernateException, SQLException {
+ try {
+ if ( value == null ) {
+ if ( IS_VALUE_TRACING_ENABLED ) {
+ log().trace( "binding null to parameter: " + index );
+ }
+
+ st.setNull( index, sqlType() );
+ }
+ else {
+ if ( IS_VALUE_TRACING_ENABLED ) {
+ log().trace( "binding '" + toString( value ) + "' to parameter: " + index );
+ }
+
+ set( st, value, index );
+ }
+ }
+ catch ( RuntimeException re ) {
+ log().info( "could not bind value '" + nullSafeToString( value ) + "' to parameter: " + index + "; " + re.getMessage() );
+ throw re;
+ }
+ catch ( SQLException se ) {
+ log().info( "could not bind value '" + nullSafeToString( value ) + "' to parameter: " + index + "; " + se.getMessage() );
+ throw se;
+ }
+ }
+
+ public final Object nullSafeGet(
+ ResultSet rs,
+ String[] names,
+ SessionImplementor session,
+ Object owner)
+ throws HibernateException, SQLException {
+ return nullSafeGet(rs, names[0]);
+ }
+
+ public final Object nullSafeGet(ResultSet rs, String[] names)
+ throws HibernateException, SQLException {
+ return nullSafeGet(rs, names[0]);
+ }
+
+ public final Object nullSafeGet(ResultSet rs, String name)
+ throws HibernateException, SQLException {
+ try {
+ Object value = get(rs, name);
+ if ( value == null || rs.wasNull() ) {
+ if ( IS_VALUE_TRACING_ENABLED ) {
+ log().trace( "returning null as column: " + name );
+ }
+ return null;
+ }
+ else {
+ if ( IS_VALUE_TRACING_ENABLED ) {
+ log().trace( "returning '" + toString( value ) + "' as column: " + name );
+ }
+ return value;
+ }
+ }
+ catch ( RuntimeException re ) {
+ log().info( "could not read column value from result set: " + name + "; " + re.getMessage() );
+ throw re;
+ }
+ catch ( SQLException se ) {
+ log().info( "could not read column value from result set: " + name + "; " + se.getMessage() );
+ throw se;
+ }
+ }
+
+ public final Object nullSafeGet(ResultSet rs, String name, SessionImplementor session, Object owner)
+ throws HibernateException, SQLException {
+ return nullSafeGet(rs, name);
+ }
+
+ public final String toXMLString(Object value, SessionFactoryImplementor pc)
+ throws HibernateException {
+ return toString(value);
+ }
+
+ public final Object fromXMLString(String xml, Mapping factory) throws HibernateException {
+ return xml==null || xml.length()==0 ? null : fromStringValue(xml);
+ }
+
+ public final int getColumnSpan(Mapping session) {
+ return 1;
+ }
+
+ public final int[] sqlTypes(Mapping session) {
+ return new int[] { sqlType() };
+ }
+
+ public final boolean isEqual(Object x, Object y, EntityMode entityMode) {
+ return isEqual(x, y);
+ }
+
+ public boolean isEqual(Object x, Object y) {
+ return EqualsHelper.equals(x, y);
+ }
+
+ public String toLoggableString(Object value, SessionFactoryImplementor factory) {
+ return value == null ? "null" : toString(value);
+ }
+
+ public Object fromXMLNode(Node xml, Mapping factory) throws HibernateException {
+ return fromXMLString( xml.getText(), factory );
+ }
+
+ public void setToXMLNode(Node xml, Object value, SessionFactoryImplementor factory)
+ throws HibernateException {
+ xml.setText( toXMLString(value, factory) );
+ }
+
+ public boolean[] toColumnNullness(Object value, Mapping mapping) {
+ return value==null ? ArrayHelper.FALSE : ArrayHelper.TRUE;
+ }
+
+ public boolean isDirty(Object old, Object current, boolean[] checkable, SessionImplementor session)
+ throws HibernateException {
+ return checkable[0] && isDirty(old, current, session);
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/OneToOneType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/OneToOneType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/OneToOneType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,155 @@
+//$Id: OneToOneType.java 7644 2005-07-25 06:53:09Z oneovthafew $
+package org.hibernate.type;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.engine.EntityKey;
+import org.hibernate.engine.Mapping;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.util.ArrayHelper;
+
+/**
+ * A one-to-one association to an entity
+ * @author Gavin King
+ */
+public class OneToOneType extends EntityType {
+
+ private final ForeignKeyDirection foreignKeyType;
+ private final String propertyName;
+ private final String entityName;
+
+ public String getPropertyName() {
+ return propertyName;
+ }
+
+ public boolean isNull(Object owner, SessionImplementor session) {
+
+ if ( propertyName != null ) {
+
+ EntityPersister ownerPersister = session.getFactory()
+ .getEntityPersister(entityName);
+ Serializable id = session.getContextEntityIdentifier(owner);
+
+ EntityKey entityKey = new EntityKey( id, ownerPersister, session.getEntityMode() );
+
+ return session.getPersistenceContext()
+ .isPropertyNull( entityKey, getPropertyName() );
+
+ }
+ else {
+ return false;
+ }
+
+ }
+
+ public int getColumnSpan(Mapping session) throws MappingException {
+ return 0;
+ }
+
+ public int[] sqlTypes(Mapping session) throws MappingException {
+ return ArrayHelper.EMPTY_INT_ARRAY;
+ }
+
+ public boolean[] toColumnNullness(Object value, Mapping mapping) {
+ return ArrayHelper.EMPTY_BOOLEAN_ARRAY;
+ }
+
+ public OneToOneType(
+ String referencedEntityName,
+ ForeignKeyDirection foreignKeyType,
+ String uniqueKeyPropertyName,
+ boolean lazy,
+ boolean unwrapProxy,
+ boolean isEmbeddedInXML,
+ String entityName,
+ String propertyName
+ ) {
+ super(
+ referencedEntityName,
+ uniqueKeyPropertyName,
+ !lazy,
+ isEmbeddedInXML,
+ unwrapProxy
+ );
+ this.foreignKeyType = foreignKeyType;
+ this.propertyName = propertyName;
+ this.entityName = entityName;
+ }
+
+ public void nullSafeSet(PreparedStatement st, Object value, int index, boolean[] settable, SessionImplementor session) {
+ //nothing to do
+ }
+
+ public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) {
+ //nothing to do
+ }
+
+ public boolean isOneToOne() {
+ return true;
+ }
+
+ public boolean isDirty(Object old, Object current, SessionImplementor session) {
+ return false;
+ }
+
+ public boolean isDirty(Object old, Object current, boolean[] checkable, SessionImplementor session) {
+ return false;
+ }
+
+ public boolean isModified(Object old, Object current, boolean[] checkable, SessionImplementor session) {
+ return false;
+ }
+
+ public ForeignKeyDirection getForeignKeyDirection() {
+ return foreignKeyType;
+ }
+
+ public Object hydrate(
+ ResultSet rs,
+ String[] names,
+ SessionImplementor session,
+ Object owner)
+ throws HibernateException, SQLException {
+
+ return session.getContextEntityIdentifier(owner);
+ }
+
+ protected boolean isNullable() {
+ return foreignKeyType==ForeignKeyDirection.FOREIGN_KEY_TO_PARENT;
+ }
+
+ public boolean useLHSPrimaryKey() {
+ return true;
+ }
+
+ public Serializable disassemble(Object value, SessionImplementor session, Object owner)
+ throws HibernateException {
+ return null;
+ }
+
+ public Object assemble(Serializable oid, SessionImplementor session, Object owner)
+ throws HibernateException {
+ //this should be a call to resolve(), not resolveIdentifier(),
+ //'cos it might be a property-ref, and we did not cache the
+ //referenced value
+ return resolve( session.getContextEntityIdentifier(owner), session, owner );
+ }
+
+ /**
+ * We don't need to dirty check one-to-one because of how
+ * assemble/disassemble is implemented and because a one-to-one
+ * association is never dirty
+ */
+ public boolean isAlwaysDirtyChecked() {
+ //TODO: this is kinda inconsistent with CollectionType
+ return false;
+ }
+
+}
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/OrderedMapType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/OrderedMapType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/OrderedMapType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,16 @@
+//$Id: OrderedMapType.java 10100 2006-07-10 16:31:09Z steve.ebersole at jboss.com $
+package org.hibernate.type;
+
+import org.hibernate.util.LinkedHashCollectionHelper;
+
+public class OrderedMapType extends MapType {
+
+ public OrderedMapType(String role, String propertyRef, boolean isEmbeddedInXML) {
+ super( role, propertyRef, isEmbeddedInXML );
+ }
+
+ public Object instantiate(int anticipatedSize) {
+ return LinkedHashCollectionHelper.createLinkedHashMap( anticipatedSize );
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/type/OrderedMapType.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/OrderedSetType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/OrderedSetType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/OrderedSetType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,16 @@
+//$Id: OrderedSetType.java 10100 2006-07-10 16:31:09Z steve.ebersole at jboss.com $
+package org.hibernate.type;
+
+import org.hibernate.util.LinkedHashCollectionHelper;
+
+public class OrderedSetType extends SetType {
+
+ public OrderedSetType(String role, String propertyRef, boolean isEmbeddedInXML) {
+ super( role, propertyRef, isEmbeddedInXML );
+ }
+
+ public Object instantiate(int anticipatedSize) {
+ return LinkedHashCollectionHelper.createLinkedHashSet( anticipatedSize );
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/type/OrderedSetType.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/PrimitiveType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/PrimitiveType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/PrimitiveType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,25 @@
+//$Id: PrimitiveType.java 4582 2004-09-25 11:22:20Z oneovthafew $
+package org.hibernate.type;
+
+import java.io.Serializable;
+
+/**
+ * Superclass of primitive / primitive wrapper types.
+ * @author Gavin King
+ */
+public abstract class PrimitiveType extends ImmutableType implements LiteralType {
+
+ public abstract Class getPrimitiveClass();
+
+ public String toString(Object value) {
+ return value.toString();
+ }
+
+ public abstract Serializable getDefaultValue();
+
+}
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/SerializableType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/SerializableType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/SerializableType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,106 @@
+//$Id: SerializableType.java 10818 2006-11-16 05:12:01Z steve.ebersole at jboss.com $
+package org.hibernate.type;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.hibernate.EntityMode;
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.util.SerializationHelper;
+
+/**
+ * <tt>serializable</tt>: A type that maps an SQL VARBINARY to a
+ * serializable Java object.
+ * @author Gavin King
+ */
+public class SerializableType extends MutableType {
+
+ private final Class serializableClass;
+
+ public SerializableType(Class serializableClass) {
+ this.serializableClass = serializableClass;
+ }
+
+ public void set(PreparedStatement st, Object value, int index) throws HibernateException, SQLException {
+ Hibernate.BINARY.set(st, toBytes(value), index);
+ }
+
+ public Object get(ResultSet rs, String name) throws HibernateException, SQLException {
+ byte[] bytes = (byte[]) Hibernate.BINARY.get(rs, name);
+ // Some JDBC drivers erroneously return an empty array here for a null DB value :/
+ if ( bytes == null || bytes.length == 0 ) {
+ return null;
+ }
+ else {
+ return fromBytes(bytes);
+ }
+ }
+
+ public Class getReturnedClass() {
+ return serializableClass;
+ }
+
+ public boolean isEqual(Object x, Object y) throws HibernateException {
+ if ( x == y ) {
+ return true;
+ }
+ if ( x == null || y == null ) {
+ return false;
+ }
+ return x.equals( y ) || Hibernate.BINARY.isEqual( toBytes( x ), toBytes( y ) );
+ }
+
+ public int getHashCode(Object x, EntityMode entityMode) {
+ return Hibernate.BINARY.getHashCode( toBytes(x), entityMode );
+ }
+
+ public String toString(Object value) throws HibernateException {
+ return Hibernate.BINARY.toString( toBytes(value) );
+ }
+
+ public Object fromStringValue(String xml) throws HibernateException {
+ return fromBytes( (byte[]) Hibernate.BINARY.fromStringValue(xml) );
+ }
+
+ public String getName() {
+ return (serializableClass==Serializable.class) ? "serializable" : serializableClass.getName();
+ }
+
+ public Object deepCopyNotNull(Object value) throws HibernateException {
+ return fromBytes( toBytes(value) );
+ }
+
+ private static byte[] toBytes(Object object) throws SerializationException {
+ return SerializationHelper.serialize( (Serializable) object );
+ }
+
+ private static Object fromBytes( byte[] bytes ) throws SerializationException {
+ return SerializationHelper.deserialize(bytes);
+ }
+
+ public int sqlType() {
+ return Hibernate.BINARY.sqlType();
+ }
+
+ public Object assemble(Serializable cached, SessionImplementor session, Object owner)
+ throws HibernateException {
+ return (cached==null) ? null : fromBytes( (byte[]) cached );
+ }
+
+ public Serializable disassemble(Object value, SessionImplementor session, Object owner)
+ throws HibernateException {
+ return (value==null) ? null : toBytes(value);
+ }
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/SerializationException.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/SerializationException.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/SerializationException.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+//$Id: SerializationException.java 3890 2004-06-03 16:31:32Z steveebersole $
+package org.hibernate.type;
+
+import org.hibernate.HibernateException;
+
+/**
+ * Thrown when a property cannot be serializaed/deserialized
+ * @author Gavin King
+ */
+public class SerializationException extends HibernateException {
+
+ public SerializationException(String message, Exception root) {
+ super(message, root);
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/SetType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/SetType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/SetType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,49 @@
+//$Id: SetType.java 10086 2006-07-05 18:17:27Z steve.ebersole at jboss.com $
+package org.hibernate.type;
+
+import java.io.Serializable;
+import java.util.HashSet;
+
+import org.dom4j.Element;
+import org.hibernate.EntityMode;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.collection.PersistentElementHolder;
+import org.hibernate.collection.PersistentSet;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.collection.CollectionPersister;
+
+public class SetType extends CollectionType {
+
+ public SetType(String role, String propertyRef, boolean isEmbeddedInXML) {
+ super(role, propertyRef, isEmbeddedInXML);
+ }
+
+ public PersistentCollection instantiate(SessionImplementor session, CollectionPersister persister, Serializable key) {
+ if ( session.getEntityMode()==EntityMode.DOM4J ) {
+ return new PersistentElementHolder(session, persister, key);
+ }
+ else {
+ return new PersistentSet(session);
+ }
+ }
+
+ public Class getReturnedClass() {
+ return java.util.Set.class;
+ }
+
+ public PersistentCollection wrap(SessionImplementor session, Object collection) {
+ if ( session.getEntityMode()==EntityMode.DOM4J ) {
+ return new PersistentElementHolder( session, (Element) collection );
+ }
+ else {
+ return new PersistentSet( session, (java.util.Set) collection );
+ }
+ }
+
+ public Object instantiate(int anticipatedSize) {
+ return anticipatedSize <= 0
+ ? new HashSet()
+ : new HashSet( anticipatedSize + (int)( anticipatedSize * .75f ), .75f );
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/ShortType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/ShortType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/ShortType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,78 @@
+//$Id: ShortType.java 7825 2005-08-10 20:23:55Z oneovthafew $
+package org.hibernate.type;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.Comparator;
+
+import org.hibernate.util.ComparableComparator;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.SessionImplementor;
+
+/**
+ * <tt>short</tt>: A type that maps an SQL SMALLINT to a Java Short.
+ * @author Gavin King
+ */
+public class ShortType extends PrimitiveType implements DiscriminatorType, VersionType {
+
+ private static final Short ZERO = new Short( (short) 0 );
+
+ public Serializable getDefaultValue() {
+ return ZERO;
+ }
+
+ public Object get(ResultSet rs, String name) throws SQLException {
+ return new Short( rs.getShort(name) );
+ }
+
+ public Class getPrimitiveClass() {
+ return short.class;
+ }
+
+ public Class getReturnedClass() {
+ return Short.class;
+ }
+
+ public void set(PreparedStatement st, Object value, int index) throws SQLException {
+ st.setShort( index, ( (Short) value ).shortValue() );
+ }
+
+ public int sqlType() {
+ return Types.SMALLINT;
+ }
+
+ public String getName() { return "short"; }
+
+ public String objectToSQLString(Object value, Dialect dialect) throws Exception {
+ return value.toString();
+ }
+
+ public Object stringToObject(String xml) throws Exception {
+ return new Short(xml);
+ }
+
+ public Object next(Object current, SessionImplementor session) {
+ return new Short( (short) ( ( (Short) current ).shortValue() + 1 ) );
+ }
+
+ public Object seed(SessionImplementor session) {
+ return ZERO;
+ }
+
+ public Comparator getComparator() {
+ return ComparableComparator.INSTANCE;
+ }
+
+ public Object fromStringValue(String xml) {
+ return new Short(xml);
+ }
+
+}
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/SortedMapType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/SortedMapType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/SortedMapType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,61 @@
+//$Id: SortedMapType.java 10100 2006-07-10 16:31:09Z steve.ebersole at jboss.com $
+package org.hibernate.type;
+
+import java.io.Serializable;
+import java.util.Comparator;
+import java.util.TreeMap;
+
+import org.dom4j.Element;
+import org.hibernate.EntityMode;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.collection.PersistentElementHolder;
+import org.hibernate.collection.PersistentMapElementHolder;
+import org.hibernate.collection.PersistentSortedMap;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.collection.CollectionPersister;
+
+
+public class SortedMapType extends MapType {
+
+ private final Comparator comparator;
+
+ public SortedMapType(String role, String propertyRef, Comparator comparator, boolean isEmbeddedInXML) {
+ super(role, propertyRef, isEmbeddedInXML);
+ this.comparator = comparator;
+ }
+
+ public PersistentCollection instantiate(SessionImplementor session, CollectionPersister persister, Serializable key) {
+ if ( session.getEntityMode()==EntityMode.DOM4J ) {
+ return new PersistentMapElementHolder(session, persister, key);
+ }
+ else {
+ PersistentSortedMap map = new PersistentSortedMap(session);
+ map.setComparator(comparator);
+ return map;
+ }
+ }
+
+ public Class getReturnedClass() {
+ return java.util.SortedMap.class;
+ }
+
+ public Object instantiate(int anticipatedSize) {
+ return new TreeMap(comparator);
+ }
+
+ public PersistentCollection wrap(SessionImplementor session, Object collection) {
+ if ( session.getEntityMode()==EntityMode.DOM4J ) {
+ return new PersistentElementHolder( session, (Element) collection );
+ }
+ else {
+ return new PersistentSortedMap( session, (java.util.SortedMap) collection );
+ }
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/SortedSetType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/SortedSetType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/SortedSetType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,58 @@
+//$Id: SortedSetType.java 10100 2006-07-10 16:31:09Z steve.ebersole at jboss.com $
+package org.hibernate.type;
+
+import java.io.Serializable;
+import java.util.Comparator;
+import java.util.TreeSet;
+
+import org.dom4j.Element;
+import org.hibernate.EntityMode;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.collection.PersistentElementHolder;
+import org.hibernate.collection.PersistentSortedSet;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.collection.CollectionPersister;
+
+public class SortedSetType extends SetType {
+
+ private final Comparator comparator;
+
+ public SortedSetType(String role, String propertyRef, Comparator comparator, boolean isEmbeddedInXML) {
+ super(role, propertyRef, isEmbeddedInXML);
+ this.comparator = comparator;
+ }
+
+ public PersistentCollection instantiate(SessionImplementor session, CollectionPersister persister, Serializable key) {
+ if ( session.getEntityMode()==EntityMode.DOM4J ) {
+ return new PersistentElementHolder(session, persister, key);
+ }
+ else {
+ PersistentSortedSet set = new PersistentSortedSet(session);
+ set.setComparator(comparator);
+ return set;
+ }
+ }
+
+ public Class getReturnedClass() {
+ return java.util.SortedSet.class;
+ }
+
+ public Object instantiate(int anticipatedSize) {
+ return new TreeSet(comparator);
+ }
+
+ public PersistentCollection wrap(SessionImplementor session, Object collection) {
+ if ( session.getEntityMode()==EntityMode.DOM4J ) {
+ return new PersistentElementHolder( session, (Element) collection );
+ }
+ else {
+ return new PersistentSortedSet( session, (java.util.SortedSet) collection );
+ }
+ }
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/SpecialOneToOneType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/SpecialOneToOneType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/SpecialOneToOneType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,106 @@
+//$Id: SpecialOneToOneType.java 7246 2005-06-20 20:32:36Z oneovthafew $
+package org.hibernate.type;
+
+import java.io.Serializable;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.hibernate.AssertionFailure;
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.engine.ForeignKeys;
+import org.hibernate.engine.Mapping;
+import org.hibernate.engine.SessionImplementor;
+
+/**
+ * A one-to-one association that maps to specific formula(s)
+ * instead of the primary key column of the owning entity.
+ *
+ * @author Gavin King
+ */
+public class SpecialOneToOneType extends OneToOneType {
+
+ public SpecialOneToOneType(
+ String referencedEntityName,
+ ForeignKeyDirection foreignKeyType,
+ String uniqueKeyPropertyName,
+ boolean lazy,
+ boolean unwrapProxy,
+ String entityName,
+ String propertyName
+ ) {
+ super(
+ referencedEntityName,
+ foreignKeyType,
+ uniqueKeyPropertyName,
+ lazy,
+ unwrapProxy,
+ true,
+ entityName,
+ propertyName
+ );
+ }
+
+ public int getColumnSpan(Mapping mapping) throws MappingException {
+ return super.getIdentifierOrUniqueKeyType(mapping).getColumnSpan(mapping);
+ }
+
+ public int[] sqlTypes(Mapping mapping) throws MappingException {
+ return super.getIdentifierOrUniqueKeyType(mapping).sqlTypes(mapping);
+ }
+
+ public boolean useLHSPrimaryKey() {
+ return false;
+ }
+
+ public Object hydrate(ResultSet rs, String[] names, SessionImplementor session, Object owner)
+ throws HibernateException, SQLException {
+ return super.getIdentifierOrUniqueKeyType( session.getFactory() )
+ .nullSafeGet(rs, names, session, owner);
+ }
+
+ // TODO: copy/paste from ManyToOneType
+
+ public Serializable disassemble(Object value, SessionImplementor session, Object owner)
+ throws HibernateException {
+
+ if ( isNotEmbedded(session) ) {
+ return getIdentifierType(session).disassemble(value, session, owner);
+ }
+
+ if (value==null) {
+ return null;
+ }
+ else {
+ // cache the actual id of the object, not the value of the
+ // property-ref, which might not be initialized
+ Object id = ForeignKeys.getEntityIdentifierIfNotUnsaved( getAssociatedEntityName(), value, session );
+ if (id==null) {
+ throw new AssertionFailure(
+ "cannot cache a reference to an object with a null id: " +
+ getAssociatedEntityName()
+ );
+ }
+ return getIdentifierType(session).disassemble(id, session, owner);
+ }
+ }
+
+ public Object assemble(Serializable oid, SessionImplementor session, Object owner)
+ throws HibernateException {
+ //TODO: currently broken for unique-key references (does not detect
+ // change to unique key property of the associated object)
+ Serializable id = (Serializable) getIdentifierType(session).assemble(oid, session, null); //the owner of the association is not the owner of the id
+
+ if ( isNotEmbedded(session) ) return id;
+
+ if (id==null) {
+ return null;
+ }
+ else {
+ return resolveIdentifier(id, session);
+ }
+ }
+
+
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/type/SpecialOneToOneType.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/StringType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/StringType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/StringType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,56 @@
+//$Id: StringType.java 7825 2005-08-10 20:23:55Z oneovthafew $
+package org.hibernate.type;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+
+import org.hibernate.dialect.Dialect;
+
+/**
+ * <tt>string</tt>: A type that maps an SQL VARCHAR to a Java String.
+ * @author Gavin King
+ */
+public class StringType extends ImmutableType implements DiscriminatorType {
+
+ public Object get(ResultSet rs, String name) throws SQLException {
+ return rs.getString(name);
+ }
+
+ public Class getReturnedClass() {
+ return String.class;
+ }
+
+ public void set(PreparedStatement st, Object value, int index) throws SQLException {
+ st.setString(index, (String) value);
+ }
+
+ public int sqlType() {
+ return Types.VARCHAR;
+ }
+
+ public String getName() { return "string"; }
+
+ public String objectToSQLString(Object value, Dialect dialect) throws Exception {
+ return '\'' + (String) value + '\'';
+ }
+
+ public Object stringToObject(String xml) throws Exception {
+ return xml;
+ }
+
+ public String toString(Object value) {
+ return (String) value;
+ }
+
+ public Object fromStringValue(String xml) {
+ return xml;
+ }
+
+}
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/TextType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/TextType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/TextType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,84 @@
+//$Id: TextType.java 4582 2004-09-25 11:22:20Z oneovthafew $
+package org.hibernate.type;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+
+import org.hibernate.HibernateException;
+
+/**
+ * <tt>text</tt>: A type that maps an SQL CLOB to a Java String.
+ * @author Gavin King, Bertrand Renuart
+ */
+public class TextType extends ImmutableType {
+
+ public void set(PreparedStatement st, Object value, int index) throws HibernateException, SQLException {
+ String str = (String) value;
+ st.setCharacterStream( index, new StringReader(str), str.length() );
+ }
+
+ public Object get(ResultSet rs, String name) throws HibernateException, SQLException {
+
+ // Retrieve the value of the designated column in the current row of this
+ // ResultSet object as a java.io.Reader object
+ Reader charReader = rs.getCharacterStream(name);
+
+ // if the corresponding SQL value is NULL, the reader we got is NULL as well
+ if (charReader==null) return null;
+
+ // Fetch Reader content up to the end - and put characters in a StringBuffer
+ StringBuffer sb = new StringBuffer();
+ try {
+ char[] buffer = new char[2048];
+ while (true) {
+ int amountRead = charReader.read(buffer, 0, buffer.length);
+ if ( amountRead == -1 ) break;
+ sb.append(buffer, 0, amountRead);
+ }
+ }
+ catch (IOException ioe) {
+ throw new HibernateException( "IOException occurred reading text", ioe );
+ }
+ finally {
+ try {
+ charReader.close();
+ }
+ catch (IOException e) {
+ throw new HibernateException( "IOException occurred closing stream", e );
+ }
+ }
+
+ // Return StringBuffer content as a large String
+ return sb.toString();
+ }
+
+ public int sqlType() {
+ return Types.CLOB; //or Types.LONGVARCHAR?
+ }
+
+ public Class getReturnedClass() {
+ return String.class;
+ }
+
+ public String getName() { return "text"; }
+
+ public String toString(Object val) {
+ return (String) val;
+ }
+ public Object fromStringValue(String xml) {
+ return xml;
+ }
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/TimeType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/TimeType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/TimeType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,107 @@
+//$Id: TimeType.java 8891 2005-12-21 05:13:29Z oneovthafew $
+package org.hibernate.type;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Time;
+import java.sql.Types;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.dialect.Dialect;
+
+/**
+ * <tt>time</tt>: A type that maps an SQL TIME to a Java
+ * java.util.Date or java.sql.Time.
+ * @author Gavin King
+ */
+public class TimeType extends MutableType implements LiteralType {
+
+ private static final String TIME_FORMAT = "HH:mm:ss";
+
+ public Object get(ResultSet rs, String name) throws SQLException {
+ return rs.getTime(name);
+ }
+ public Class getReturnedClass() {
+ return java.util.Date.class;
+ }
+ public void set(PreparedStatement st, Object value, int index) throws SQLException {
+
+ Time time;
+ if (value instanceof Time) {
+ time = (Time) value;
+ }
+ else {
+ time = new Time( ( (java.util.Date) value ).getTime() );
+ }
+ st.setTime(index, time);
+ }
+
+ public int sqlType() {
+ return Types.TIME;
+ }
+ public String getName() { return "time"; }
+
+ public String toString(Object val) {
+ return new SimpleDateFormat(TIME_FORMAT).format( (java.util.Date) val );
+ }
+ public boolean isEqual(Object x, Object y) {
+
+ if (x==y) return true;
+ if (x==null || y==null) return false;
+
+ Date xdate = (Date) x;
+ Date ydate = (Date) y;
+
+ if ( xdate.getTime()==ydate.getTime() ) return true;
+
+ Calendar calendar1 = java.util.Calendar.getInstance();
+ Calendar calendar2 = java.util.Calendar.getInstance();
+ calendar1.setTime( xdate );
+ calendar2.setTime( ydate );
+
+ return calendar1.get(Calendar.HOUR_OF_DAY) == calendar2.get(Calendar.HOUR_OF_DAY)
+ && calendar1.get(Calendar.MINUTE) == calendar2.get(Calendar.MINUTE)
+ && calendar1.get(Calendar.SECOND) == calendar2.get(Calendar.SECOND)
+ && calendar1.get(Calendar.MILLISECOND) == calendar2.get(Calendar.MILLISECOND);
+ }
+
+ public int getHashCode(Object x, EntityMode entityMode) {
+ Calendar calendar = java.util.Calendar.getInstance();
+ calendar.setTime( (java.util.Date) x );
+ int hashCode = 1;
+ hashCode = 31 * hashCode + calendar.get(Calendar.HOUR_OF_DAY);
+ hashCode = 31 * hashCode + calendar.get(Calendar.MINUTE);
+ hashCode = 31 * hashCode + calendar.get(Calendar.SECOND);
+ hashCode = 31 * hashCode + calendar.get(Calendar.MILLISECOND);
+ return hashCode;
+ }
+
+ public Object deepCopyNotNull(Object value) {
+ return new Time( ( (java.util.Date) value ).getTime() );
+ }
+
+ public String objectToSQLString(Object value, Dialect dialect) throws Exception {
+ return '\'' + new Time( ( (java.util.Date) value ).getTime() ).toString() + '\'';
+ }
+
+ public Object fromStringValue(String xml) throws HibernateException {
+ try {
+ return new SimpleDateFormat(TIME_FORMAT).parse(xml);
+ }
+ catch (ParseException pe) {
+ throw new HibernateException("could not parse XML", pe);
+ }
+ }
+
+}
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/TimeZoneType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/TimeZoneType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/TimeZoneType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,69 @@
+//$Id: TimeZoneType.java 7825 2005-08-10 20:23:55Z oneovthafew $
+package org.hibernate.type;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.TimeZone;
+
+import org.hibernate.EntityMode;
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.dialect.Dialect;
+
+/**
+ * <tt>timezone</tt>: A type that maps an SQL VARCHAR to a
+ * <tt>java.util.TimeZone</tt>
+ * @see java.util.TimeZone
+ * @author Gavin King
+ */
+public class TimeZoneType extends ImmutableType implements LiteralType {
+
+ public Object get(ResultSet rs, String name)
+ throws HibernateException, SQLException {
+ String id = (String) Hibernate.STRING.nullSafeGet(rs, name);
+ return (id==null) ? null : TimeZone.getTimeZone(id);
+ }
+
+
+ public void set(PreparedStatement st, Object value, int index) throws HibernateException, SQLException {
+ Hibernate.STRING.set(st, ( (TimeZone) value ).getID(), index);
+ }
+
+ public int sqlType() {
+ return Hibernate.STRING.sqlType();
+ }
+
+ public String toString(Object value) throws HibernateException {
+ return ( (TimeZone) value ).getID();
+ }
+
+ public int compare(Object x, Object y, EntityMode entityMode) {
+ return ( (TimeZone) x ).getID().compareTo( ( (TimeZone) y ).getID() );
+ }
+
+ public Object fromStringValue(String xml) throws HibernateException {
+ return TimeZone.getTimeZone(xml);
+ }
+
+ public Class getReturnedClass() {
+ return TimeZone.class;
+ }
+
+ public String getName() {
+ return "timezone";
+ }
+
+ public String objectToSQLString(Object value, Dialect dialect) throws Exception {
+ return ( (LiteralType) Hibernate.STRING ).objectToSQLString(
+ ( (TimeZone) value ).getID(), dialect
+ );
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/TimestampType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/TimestampType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/TimestampType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,135 @@
+//$Id: TimestampType.java 8891 2005-12-21 05:13:29Z oneovthafew $
+package org.hibernate.type;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Timestamp;
+import java.sql.Types;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Comparator;
+
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.cfg.Environment;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.util.ComparableComparator;
+
+/**
+ * <tt>timestamp</tt>: A type that maps an SQL TIMESTAMP to a Java
+ * java.util.Date or java.sql.Timestamp.
+ * @author Gavin King
+ */
+public class TimestampType extends MutableType implements VersionType, LiteralType {
+
+ private static final String TIMESTAMP_FORMAT = "yyyy-MM-dd HH:mm:ss";
+
+ public Object get(ResultSet rs, String name) throws SQLException {
+ return rs.getTimestamp(name);
+ }
+
+ public Class getReturnedClass() {
+ return java.util.Date.class;
+ }
+
+ public void set(PreparedStatement st, Object value, int index) throws SQLException {
+ Timestamp ts;
+ if (value instanceof Timestamp) {
+ ts = (Timestamp) value;
+ }
+ else {
+ ts = new Timestamp( ( (java.util.Date) value ).getTime() );
+ }
+ st.setTimestamp(index, ts);
+ }
+
+ public int sqlType() {
+ return Types.TIMESTAMP;
+ }
+
+ public String getName() { return "timestamp"; }
+
+ public String toString(Object val) {
+ return new SimpleDateFormat(TIMESTAMP_FORMAT).format( (java.util.Date) val );
+ }
+
+ public Object deepCopyNotNull(Object value) {
+ if ( value instanceof Timestamp ) {
+ Timestamp orig = (Timestamp) value;
+ Timestamp ts = new Timestamp( orig.getTime() );
+ ts.setNanos( orig.getNanos() );
+ return ts;
+ }
+ else {
+ java.util.Date orig = (java.util.Date) value;
+ return new java.util.Date( orig.getTime() );
+ }
+ }
+
+ public boolean isEqual(Object x, Object y) {
+
+ if (x==y) return true;
+ if (x==null || y==null) return false;
+
+ long xTime = ( (java.util.Date) x ).getTime();
+ long yTime = ( (java.util.Date) y ).getTime();
+ boolean xts = x instanceof Timestamp;
+ boolean yts = y instanceof Timestamp;
+ int xNanos = xts ? ( (Timestamp) x ).getNanos() : 0;
+ int yNanos = yts ? ( (Timestamp) y ).getNanos() : 0;
+ if ( !Environment.jvmHasJDK14Timestamp() ) {
+ xTime += xNanos / 1000000;
+ yTime += yNanos / 1000000;
+ }
+ if ( xTime!=yTime ) return false;
+ if (xts && yts) {
+ // both are Timestamps
+ int xn = xNanos % 1000000;
+ int yn = yNanos % 1000000;
+ return xn==yn;
+ }
+ else {
+ // at least one is a plain old Date
+ return true;
+ }
+
+ }
+
+ public int getHashCode(Object x, EntityMode entityMode) {
+ java.util.Date ts = (java.util.Date) x;
+ return new Long( ts.getTime() / 1000 ).hashCode();
+ }
+
+ public Object next(Object current, SessionImplementor session) {
+ return seed( session );
+ }
+
+ public Object seed(SessionImplementor session) {
+ return new Timestamp( System.currentTimeMillis() );
+ }
+
+ public Comparator getComparator() {
+ return ComparableComparator.INSTANCE;
+ }
+
+ public String objectToSQLString(Object value, Dialect dialect) throws Exception {
+ return '\'' + new Timestamp( ( (java.util.Date) value ).getTime() ).toString() + '\'';
+ }
+
+ public Object fromStringValue(String xml) throws HibernateException {
+ try {
+ return new Timestamp( new SimpleDateFormat(TIMESTAMP_FORMAT).parse(xml).getTime() );
+ }
+ catch (ParseException pe) {
+ throw new HibernateException("could not parse XML", pe);
+ }
+ }
+
+}
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/TrueFalseType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/TrueFalseType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/TrueFalseType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,25 @@
+//$Id: TrueFalseType.java 3890 2004-06-03 16:31:32Z steveebersole $
+package org.hibernate.type;
+
+/**
+ * <tt>true_false</tt>: A type that maps an SQL CHAR(1) to a Java Boolean.
+ * @author Gavin King
+ */
+public class TrueFalseType extends CharBooleanType {
+
+ protected final String getTrueString() {
+ return "T";
+ }
+ protected final String getFalseString() {
+ return "F";
+ }
+ public String getName() { return "true_false"; }
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/Type.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/Type.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/Type.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,427 @@
+//$Id: Type.java 7793 2005-08-10 05:06:40Z oneovthafew $
+package org.hibernate.type;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Map;
+
+import org.dom4j.Node;
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.engine.Mapping;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+
+/**
+ * Defines a mapping from a Java type to an JDBC datatype. This interface is intended to
+ * be implemented by applications that need custom types.<br>
+ * <br>
+ * Implementors should usually be immutable and <b>must</b> certainly be threadsafe.
+ *
+ * @author Gavin King
+ */
+public interface Type extends Serializable {
+
+ /**
+ * Return true if the implementation is castable to
+ * <tt>AssociationType</tt>. This does not necessarily imply that
+ * the type actually represents an association.
+ * @see AssociationType
+ * @return boolean
+ */
+ public boolean isAssociationType();
+ /**
+ * Is this type a collection type.
+ */
+ public boolean isCollectionType();
+
+ /**
+ * Is this type a component type. If so, the implementation
+ * must be castable to <tt>AbstractComponentType</tt>. A component
+ * type may own collections or associations and hence must provide
+ * certain extra functionality.
+ * @see AbstractComponentType
+ * @return boolean
+ */
+ public boolean isComponentType();
+
+ /**
+ * Is this type an entity type?
+ * @return boolean
+ */
+ public boolean isEntityType();
+
+ /**
+ * Is this an "any" type.
+ *
+ * i.e. a reference to a persistent entity
+ * that is not modelled as a (foreign key) association.
+ */
+ public boolean isAnyType();
+
+ public boolean isXMLElement();
+
+ /**
+ * Return the SQL type codes for the columns mapped by this type. The codes
+ * are defined on <tt>java.sql.Types</tt>.
+ * @see java.sql.Types
+ * @return the typecodes
+ * @throws MappingException
+ */
+ public int[] sqlTypes(Mapping mapping) throws MappingException;
+
+ /**
+ * How many columns are used to persist this type.
+ */
+ public int getColumnSpan(Mapping mapping) throws MappingException;
+
+ /**
+ * The class returned by <tt>nullSafeGet()</tt> methods. This is used to
+ * establish the class of an array of this type.
+ *
+ * @return Class
+ */
+ public Class getReturnedClass();
+
+ /**
+ * Compare two instances of the class mapped by this type for persistence
+ * "equality" - equality of persistent state - taking a shortcut for
+ * entity references.
+ * @param x
+ * @param y
+ * @param entityMode
+ *
+ * @return boolean
+ * @throws HibernateException
+ */
+ public boolean isSame(Object x, Object y, EntityMode entityMode) throws HibernateException;
+
+ /**
+ * Compare two instances of the class mapped by this type for persistence
+ * "equality" - equality of persistent state.
+ * @param x
+ * @param y
+ * @param entityMode
+ *
+ * @return boolean
+ * @throws HibernateException
+ */
+ public boolean isEqual(Object x, Object y, EntityMode entityMode) throws HibernateException;
+
+ /**
+ * Compare two instances of the class mapped by this type for persistence
+ * "equality" - equality of persistent state.
+ * @param x
+ * @param y
+ * @param entityMode
+ *
+ * @return boolean
+ * @throws HibernateException
+ */
+ public boolean isEqual(Object x, Object y, EntityMode entityMode, SessionFactoryImplementor factory)
+ throws HibernateException;
+
+ /**
+ * Get a hashcode, consistent with persistence "equality"
+ * @param x
+ * @param entityMode
+ */
+ public int getHashCode(Object x, EntityMode entityMode) throws HibernateException;
+
+ /**
+ * Get a hashcode, consistent with persistence "equality"
+ * @param x
+ * @param entityMode
+ * @param factory
+ */
+ public int getHashCode(Object x, EntityMode entityMode, SessionFactoryImplementor factory)
+ throws HibernateException;
+
+ /**
+ * compare two instances of the type
+ * @param entityMode
+ */
+ public int compare(Object x, Object y, EntityMode entityMode);
+
+ /**
+ * Should the parent be considered dirty, given both the old and current field or
+ * element value?
+ *
+ * @param old the old value
+ * @param current the current value
+ * @param session
+ * @return true if the field is dirty
+ */
+ public boolean isDirty(Object old, Object current, SessionImplementor session)
+ throws HibernateException;
+ /**
+ * Should the parent be considered dirty, given both the old and current field or
+ * element value?
+ *
+ * @param old the old value
+ * @param current the current value
+ * @param checkable which columns are actually updatable
+ * @param session
+ * @return true if the field is dirty
+ */
+ public boolean isDirty(Object old, Object current, boolean[] checkable, SessionImplementor session)
+ throws HibernateException;
+
+ /**
+ * Has the parent object been modified, compared to the current database state?
+ * @param oldHydratedState the database state, in a "hydrated" form, with identifiers unresolved
+ * @param currentState the current state of the object
+ * @param checkable which columns are actually updatable
+ * @param session
+ * @return true if the field has been modified
+ */
+ public boolean isModified(Object oldHydratedState, Object currentState, boolean[] checkable, SessionImplementor session)
+ throws HibernateException;
+
+ /**
+ * Retrieve an instance of the mapped class from a JDBC resultset. Implementors
+ * should handle possibility of null values.
+ *
+ * @see Type#hydrate(ResultSet, String[], SessionImplementor, Object) alternative, 2-phase property initialization
+ * @param rs
+ * @param names the column names
+ * @param session
+ * @param owner the parent entity
+ * @return Object
+ * @throws HibernateException
+ * @throws SQLException
+ */
+ public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner)
+ throws HibernateException, SQLException;
+
+ /**
+ * Retrieve an instance of the mapped class from a JDBC resultset. Implementations
+ * should handle possibility of null values. This method might be called if the
+ * type is known to be a single-column type.
+ *
+ * @param rs
+ * @param name the column name
+ * @param session
+ * @param owner the parent entity
+ * @return Object
+ * @throws HibernateException
+ * @throws SQLException
+ */
+ public Object nullSafeGet(ResultSet rs, String name, SessionImplementor session, Object owner)
+ throws HibernateException, SQLException;
+
+ /**
+ * Write an instance of the mapped class to a prepared statement, ignoring some columns.
+ * Implementors should handle possibility of null values. A multi-column type should be
+ * written to parameters starting from <tt>index</tt>.
+ * @param st
+ * @param value the object to write
+ * @param index statement parameter index
+ * @param settable an array indicating which columns to ignore
+ * @param session
+ *
+ * @throws HibernateException
+ * @throws SQLException
+ */
+ public void nullSafeSet(PreparedStatement st, Object value, int index, boolean[] settable, SessionImplementor session)
+ throws HibernateException, SQLException;
+
+ /**
+ * Write an instance of the mapped class to a prepared statement. Implementors
+ * should handle possibility of null values. A multi-column type should be written
+ * to parameters starting from <tt>index</tt>.
+ * @param st
+ * @param value the object to write
+ * @param index statement parameter index
+ * @param session
+ *
+ * @throws HibernateException
+ * @throws SQLException
+ */
+ public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session)
+ throws HibernateException, SQLException;
+
+ /**
+ * A representation of the value to be embedded in an XML element.
+ *
+ * @param value
+ * @param factory
+ * @return String
+ * @throws HibernateException
+ */
+ public void setToXMLNode(Node node, Object value, SessionFactoryImplementor factory)
+ throws HibernateException;
+
+ /**
+ * A representation of the value to be embedded in a log file.
+ *
+ * @param value
+ * @param factory
+ * @return String
+ * @throws HibernateException
+ */
+ public String toLoggableString(Object value, SessionFactoryImplementor factory)
+ throws HibernateException;
+
+ /**
+ * Parse the XML representation of an instance.
+ * @param xml
+ * @param factory
+ *
+ * @return an instance of the type
+ * @throws HibernateException
+ */
+ public Object fromXMLNode(Node xml, Mapping factory) throws HibernateException;
+
+ /**
+ * Returns the abbreviated name of the type.
+ *
+ * @return String the Hibernate type name
+ */
+ public String getName();
+
+ /**
+ * Return a deep copy of the persistent state, stopping at entities and at
+ * collections.
+ * @param value generally a collection element or entity field
+ * @param entityMode
+ * @param factory
+ * @return Object a copy
+ */
+ public Object deepCopy(Object value, EntityMode entityMode, SessionFactoryImplementor factory)
+ throws HibernateException;
+
+ /**
+ * Are objects of this type mutable. (With respect to the referencing object ...
+ * entities and collections are considered immutable because they manage their
+ * own internal state.)
+ *
+ * @return boolean
+ */
+ public boolean isMutable();
+
+ /**
+ * Return a cacheable "disassembled" representation of the object.
+ * @param value the value to cache
+ * @param session the session
+ * @param owner optional parent entity object (needed for collections)
+ * @return the disassembled, deep cloned state
+ */
+ public Serializable disassemble(Object value, SessionImplementor session, Object owner) throws HibernateException;
+
+ /**
+ * Reconstruct the object from its cached "disassembled" state.
+ * @param cached the disassembled state from the cache
+ * @param session the session
+ * @param owner the parent entity object
+ * @return the the object
+ */
+ public Object assemble(Serializable cached, SessionImplementor session, Object owner)
+ throws HibernateException;
+
+ /**
+ * Called before assembling a query result set from the query cache, to allow batch fetching
+ * of entities missing from the second-level cache.
+ */
+ public void beforeAssemble(Serializable cached, SessionImplementor session);
+
+ /**
+ * Retrieve an instance of the mapped class, or the identifier of an entity or collection,
+ * from a JDBC resultset. This is useful for 2-phase property initialization - the second
+ * phase is a call to <tt>resolveIdentifier()</tt>.
+ *
+ * @see Type#resolve(Object, SessionImplementor, Object)
+ * @param rs
+ * @param names the column names
+ * @param session the session
+ * @param owner the parent entity
+ * @return Object an identifier or actual value
+ * @throws HibernateException
+ * @throws SQLException
+ */
+ public Object hydrate(ResultSet rs, String[] names, SessionImplementor session, Object owner)
+ throws HibernateException, SQLException;
+
+ /**
+ * Map identifiers to entities or collections. This is the second phase of 2-phase property
+ * initialization.
+ *
+ * @see Type#hydrate(ResultSet, String[], SessionImplementor, Object)
+ * @param value an identifier or value returned by <tt>hydrate()</tt>
+ * @param owner the parent entity
+ * @param session the session
+ * @return the given value, or the value associated with the identifier
+ * @throws HibernateException
+ */
+ public Object resolve(Object value, SessionImplementor session, Object owner)
+ throws HibernateException;
+
+ /**
+ * Given a hydrated, but unresolved value, return a value that may be used to
+ * reconstruct property-ref associations.
+ */
+ public Object semiResolve(Object value, SessionImplementor session, Object owner)
+ throws HibernateException;
+
+ /**
+ * Get the type of a semi-resolved value.
+ */
+ public Type getSemiResolvedType(SessionFactoryImplementor factory);
+
+ /**
+ * During merge, replace the existing (target) value in the entity we are merging to
+ * with a new (original) value from the detached entity we are merging. For immutable
+ * objects, or null values, it is safe to simply return the first parameter. For
+ * mutable objects, it is safe to return a copy of the first parameter. For objects
+ * with component values, it might make sense to recursively replace component values.
+ *
+ * @param original the value from the detached entity being merged
+ * @param target the value in the managed entity
+ * @return the value to be merged
+ */
+ public Object replace(
+ Object original,
+ Object target,
+ SessionImplementor session,
+ Object owner,
+ Map copyCache)
+ throws HibernateException;
+
+ /**
+ * During merge, replace the existing (target) value in the entity we are merging to
+ * with a new (original) value from the detached entity we are merging. For immutable
+ * objects, or null values, it is safe to simply return the first parameter. For
+ * mutable objects, it is safe to return a copy of the first parameter. For objects
+ * with component values, it might make sense to recursively replace component values.
+ *
+ * @param original the value from the detached entity being merged
+ * @param target the value in the managed entity
+ * @return the value to be merged
+ */
+ public Object replace(
+ Object original,
+ Object target,
+ SessionImplementor session,
+ Object owner,
+ Map copyCache,
+ ForeignKeyDirection foreignKeyDirection)
+ throws HibernateException;
+
+ /**
+ * Given an instance of the type, return an array of boolean, indicating
+ * which mapped columns would be null.
+ *
+ * @param value an instance of the type
+ */
+ public boolean[] toColumnNullness(Object value, Mapping mapping);
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/TypeFactory.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/TypeFactory.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/TypeFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,665 @@
+// $Id: TypeFactory.java 11496 2007-05-09 03:54:06Z steve.ebersole at jboss.com $
+package org.hibernate.type;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.sql.Blob;
+import java.sql.Clob;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Properties;
+import java.util.TimeZone;
+
+import org.hibernate.Hibernate;
+import org.hibernate.MappingException;
+import org.hibernate.classic.Lifecycle;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.intercept.LazyPropertyInitializer;
+import org.hibernate.property.BackrefPropertyAccessor;
+import org.hibernate.tuple.StandardProperty;
+import org.hibernate.usertype.CompositeUserType;
+import org.hibernate.usertype.UserType;
+import org.hibernate.usertype.ParameterizedType;
+import org.hibernate.util.ReflectHelper;
+
+/**
+ * Used internally to obtain instances of <tt>Type</tt>. Applications should use static methods
+ * and constants on <tt>org.hibernate.Hibernate</tt>.
+ *
+ * @see org.hibernate.Hibernate
+ * @author Gavin King
+ */
+public final class TypeFactory {
+
+ private static final Map BASIC_TYPES;
+
+ static {
+ HashMap basics = new HashMap();
+ basics.put( boolean.class.getName(), Hibernate.BOOLEAN );
+ basics.put( long.class.getName(), Hibernate.LONG );
+ basics.put( short.class.getName(), Hibernate.SHORT );
+ basics.put( int.class.getName(), Hibernate.INTEGER );
+ basics.put( byte.class.getName(), Hibernate.BYTE );
+ basics.put( float.class.getName(), Hibernate.FLOAT );
+ basics.put( double.class.getName(), Hibernate.DOUBLE );
+ basics.put( char.class.getName(), Hibernate.CHARACTER );
+ basics.put( Hibernate.CHARACTER.getName(), Hibernate.CHARACTER );
+ basics.put( Hibernate.INTEGER.getName(), Hibernate.INTEGER );
+ basics.put( Hibernate.STRING.getName(), Hibernate.STRING );
+ basics.put( Hibernate.DATE.getName(), Hibernate.DATE );
+ basics.put( Hibernate.TIME.getName(), Hibernate.TIME );
+ basics.put( Hibernate.TIMESTAMP.getName(), Hibernate.TIMESTAMP );
+ basics.put( "dbtimestamp", new DbTimestampType() );
+ basics.put( Hibernate.LOCALE.getName(), Hibernate.LOCALE );
+ basics.put( Hibernate.CALENDAR.getName(), Hibernate.CALENDAR );
+ basics.put( Hibernate.CALENDAR_DATE.getName(), Hibernate.CALENDAR_DATE );
+ basics.put( Hibernate.CURRENCY.getName(), Hibernate.CURRENCY );
+ basics.put( Hibernate.TIMEZONE.getName(), Hibernate.TIMEZONE );
+ basics.put( Hibernate.CLASS.getName(), Hibernate.CLASS );
+ basics.put( Hibernate.TRUE_FALSE.getName(), Hibernate.TRUE_FALSE );
+ basics.put( Hibernate.YES_NO.getName(), Hibernate.YES_NO );
+ basics.put( Hibernate.BINARY.getName(), Hibernate.BINARY );
+ basics.put( Hibernate.TEXT.getName(), Hibernate.TEXT );
+ basics.put( Hibernate.BLOB.getName(), Hibernate.BLOB );
+ basics.put( Hibernate.CLOB.getName(), Hibernate.CLOB );
+ basics.put( Hibernate.BIG_DECIMAL.getName(), Hibernate.BIG_DECIMAL );
+ basics.put( Hibernate.BIG_INTEGER.getName(), Hibernate.BIG_INTEGER );
+ basics.put( Hibernate.SERIALIZABLE.getName(), Hibernate.SERIALIZABLE );
+ basics.put( Hibernate.OBJECT.getName(), Hibernate.OBJECT );
+ basics.put( Boolean.class.getName(), Hibernate.BOOLEAN );
+ basics.put( Long.class.getName(), Hibernate.LONG );
+ basics.put( Short.class.getName(), Hibernate.SHORT );
+ basics.put( Integer.class.getName(), Hibernate.INTEGER );
+ basics.put( Byte.class.getName(), Hibernate.BYTE );
+ basics.put( Float.class.getName(), Hibernate.FLOAT );
+ basics.put( Double.class.getName(), Hibernate.DOUBLE );
+ basics.put( Character.class.getName(), Hibernate.CHARACTER );
+ basics.put( String.class.getName(), Hibernate.STRING );
+ basics.put( java.util.Date.class.getName(), Hibernate.TIMESTAMP );
+ basics.put( Time.class.getName(), Hibernate.TIME );
+ basics.put( Timestamp.class.getName(), Hibernate.TIMESTAMP );
+ basics.put( java.sql.Date.class.getName(), Hibernate.DATE );
+ basics.put( BigDecimal.class.getName(), Hibernate.BIG_DECIMAL );
+ basics.put( BigInteger.class.getName(), Hibernate.BIG_INTEGER );
+ basics.put( Locale.class.getName(), Hibernate.LOCALE );
+ basics.put( Calendar.class.getName(), Hibernate.CALENDAR );
+ basics.put( GregorianCalendar.class.getName(), Hibernate.CALENDAR );
+ if ( CurrencyType.CURRENCY_CLASS != null ) {
+ basics.put( CurrencyType.CURRENCY_CLASS.getName(), Hibernate.CURRENCY );
+ }
+ basics.put( TimeZone.class.getName(), Hibernate.TIMEZONE );
+ basics.put( Object.class.getName(), Hibernate.OBJECT );
+ basics.put( Class.class.getName(), Hibernate.CLASS );
+ basics.put( byte[].class.getName(), Hibernate.BINARY );
+ basics.put( "byte[]", Hibernate.BINARY );
+ basics.put( Byte[].class.getName(), Hibernate.WRAPPER_BINARY );
+ basics.put( "Byte[]", Hibernate.WRAPPER_BINARY );
+ basics.put( char[].class.getName(), Hibernate.CHAR_ARRAY );
+ basics.put( "char[]", Hibernate.CHAR_ARRAY );
+ basics.put( Character[].class.getName(), Hibernate.CHARACTER_ARRAY );
+ basics.put( "Character[]", Hibernate.CHARACTER_ARRAY );
+ basics.put( Blob.class.getName(), Hibernate.BLOB );
+ basics.put( Clob.class.getName(), Hibernate.CLOB );
+ basics.put( Serializable.class.getName(), Hibernate.SERIALIZABLE );
+
+ Type type = new AdaptedImmutableType(Hibernate.DATE);
+ basics.put( type.getName(), type );
+ type = new AdaptedImmutableType(Hibernate.TIME);
+ basics.put( type.getName(), type );
+ type = new AdaptedImmutableType(Hibernate.TIMESTAMP);
+ basics.put( type.getName(), type );
+ type = new AdaptedImmutableType( new DbTimestampType() );
+ basics.put( type.getName(), type );
+ type = new AdaptedImmutableType(Hibernate.CALENDAR);
+ basics.put( type.getName(), type );
+ type = new AdaptedImmutableType(Hibernate.CALENDAR_DATE);
+ basics.put( type.getName(), type );
+ type = new AdaptedImmutableType(Hibernate.SERIALIZABLE);
+ basics.put( type.getName(), type );
+ type = new AdaptedImmutableType(Hibernate.BINARY);
+ basics.put( type.getName(), type );
+
+ BASIC_TYPES = Collections.unmodifiableMap( basics );
+ }
+
+ private TypeFactory() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * A one-to-one association type for the given class
+ */
+ public static EntityType oneToOne(
+ String persistentClass,
+ ForeignKeyDirection foreignKeyType,
+ String uniqueKeyPropertyName,
+ boolean lazy,
+ boolean unwrapProxy,
+ boolean isEmbeddedInXML,
+ String entityName,
+ String propertyName
+ ) {
+ return new OneToOneType(
+ persistentClass,
+ foreignKeyType,
+ uniqueKeyPropertyName,
+ lazy,
+ unwrapProxy,
+ isEmbeddedInXML,
+ entityName,
+ propertyName
+ );
+ }
+
+ /**
+ * A many-to-one association type for the given class
+ */
+ public static EntityType manyToOne(String persistentClass) {
+ return new ManyToOneType( persistentClass );
+ }
+
+ /**
+ * A many-to-one association type for the given class
+ */
+ public static EntityType manyToOne(String persistentClass, boolean lazy) {
+ return new ManyToOneType( persistentClass, lazy );
+ }
+
+ /**
+ * A many-to-one association type for the given class
+ */
+ public static EntityType manyToOne(
+ String persistentClass,
+ String uniqueKeyPropertyName,
+ boolean lazy,
+ boolean unwrapProxy,
+ boolean isEmbeddedInXML,
+ boolean ignoreNotFound
+ ) {
+ return new ManyToOneType(
+ persistentClass,
+ uniqueKeyPropertyName,
+ lazy,
+ unwrapProxy,
+ isEmbeddedInXML,
+ ignoreNotFound
+ );
+ }
+
+ /**
+ * Given the name of a Hibernate basic type, return an instance of
+ * <tt>org.hibernate.type.Type</tt>.
+ */
+ public static Type basic(String name) {
+ return (Type) BASIC_TYPES.get( name );
+ }
+
+ /**
+ * Uses heuristics to deduce a Hibernate type given a string naming the type or Java class.
+ * Return an instance of <tt>org.hibernate.type.Type</tt>.
+ */
+ public static Type heuristicType(String typeName) throws MappingException {
+ return heuristicType( typeName, null );
+ }
+
+ /**
+ * Uses heuristics to deduce a Hibernate type given a string naming the type or Java class.
+ * Return an instance of <tt>org.hibernate.type.Type</tt>.
+ */
+ public static Type heuristicType(String typeName, Properties parameters)
+ throws MappingException {
+ Type type = TypeFactory.basic( typeName );
+ if ( type == null ) {
+ Class typeClass;
+ try {
+ typeClass = ReflectHelper.classForName( typeName );
+ }
+ catch (ClassNotFoundException cnfe) {
+ typeClass = null;
+ }
+ if ( typeClass != null ) {
+ if ( Type.class.isAssignableFrom( typeClass ) ) {
+ try {
+ type = (Type) typeClass.newInstance();
+ }
+ catch (Exception e) {
+ throw new MappingException(
+ "Could not instantiate Type: " + typeClass.getName(),
+ e
+ );
+ }
+ injectParameters(type, parameters);
+ }
+ else if ( CompositeUserType.class.isAssignableFrom( typeClass ) ) {
+ type = new CompositeCustomType( typeClass, parameters );
+ }
+ else if ( UserType.class.isAssignableFrom( typeClass ) ) {
+ type = new CustomType( typeClass, parameters );
+ }
+ else if ( Lifecycle.class.isAssignableFrom( typeClass ) ) {
+ type = Hibernate.entity( typeClass );
+ }
+ else if ( Serializable.class.isAssignableFrom( typeClass ) ) {
+ type = Hibernate.serializable( typeClass );
+ }
+ }
+ }
+ return type;
+
+ }
+
+ /**
+ * The legacy contract.
+ *
+ * @deprecated Use {@link #customCollection(String, java.util.Properties, String, String, boolean)} instead
+ */
+ public static CollectionType customCollection(
+ String typeName,
+ String role,
+ String propertyRef,
+ boolean embedded) {
+ return customCollection( typeName, null, role, propertyRef, embedded );
+ }
+
+ public static CollectionType customCollection(
+ String typeName,
+ Properties typeParameters,
+ String role,
+ String propertyRef,
+ boolean embedded) {
+ Class typeClass;
+ try {
+ typeClass = ReflectHelper.classForName( typeName );
+ }
+ catch ( ClassNotFoundException cnfe ) {
+ throw new MappingException( "user collection type class not found: " + typeName, cnfe );
+ }
+ CustomCollectionType result = new CustomCollectionType( typeClass, role, propertyRef, embedded );
+ if ( typeParameters != null ) {
+ TypeFactory.injectParameters( result.getUserType(), typeParameters );
+ }
+ return result;
+ }
+
+ // Collection Types:
+
+ public static CollectionType array(String role, String propertyRef, boolean embedded,
+ Class elementClass) {
+ return new ArrayType( role, propertyRef, elementClass, embedded );
+ }
+
+ public static CollectionType list(String role, String propertyRef, boolean embedded) {
+ return new ListType( role, propertyRef, embedded );
+ }
+
+ public static CollectionType bag(String role, String propertyRef, boolean embedded) {
+ return new BagType( role, propertyRef, embedded );
+ }
+
+ public static CollectionType idbag(String role, String propertyRef, boolean embedded) {
+ return new IdentifierBagType( role, propertyRef, embedded );
+ }
+
+ public static CollectionType map(String role, String propertyRef, boolean embedded) {
+ return new MapType( role, propertyRef, embedded );
+ }
+
+ public static CollectionType orderedMap(String role, String propertyRef, boolean embedded) {
+ return new OrderedMapType( role, propertyRef, embedded );
+ }
+
+ public static CollectionType set(String role, String propertyRef, boolean embedded) {
+ return new SetType( role, propertyRef, embedded );
+ }
+
+ public static CollectionType orderedSet(String role, String propertyRef, boolean embedded) {
+ return new OrderedSetType( role, propertyRef, embedded );
+ }
+
+ public static CollectionType sortedMap(String role, String propertyRef, boolean embedded,
+ Comparator comparator) {
+ return new SortedMapType( role, propertyRef, comparator, embedded );
+ }
+
+ public static CollectionType sortedSet(String role, String propertyRef, boolean embedded,
+ Comparator comparator) {
+ return new SortedSetType( role, propertyRef, comparator, embedded );
+ }
+
+ public static void injectParameters(Object type, Properties parameters) {
+ if (type instanceof ParameterizedType) {
+ ( (ParameterizedType) type ).setParameterValues(parameters);
+ }
+ else if ( parameters!=null && !parameters.isEmpty() ) {
+ throw new MappingException(
+ "type is not parameterized: " +
+ type.getClass().getName()
+ );
+ }
+ }
+
+
+ // convenience methods relating to operations across arrays of types...
+
+ /**
+ * Deep copy a series of values from one array to another...
+ *
+ * @param values The values to copy (the source)
+ * @param types The value types
+ * @param copy an array indicating which values to include in the copy
+ * @param target The array into which to copy the values
+ * @param session The orginating session
+ */
+ public static void deepCopy(
+ final Object[] values,
+ final Type[] types,
+ final boolean[] copy,
+ final Object[] target,
+ final SessionImplementor session) {
+ for ( int i = 0; i < types.length; i++ ) {
+ if ( copy[i] ) {
+ if ( values[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY
+ || values[i] == BackrefPropertyAccessor.UNKNOWN ) {
+ target[i] = values[i];
+ }
+ else {
+ target[i] = types[i].deepCopy( values[i], session.getEntityMode(), session
+ .getFactory() );
+ }
+ }
+ }
+ }
+
+ /**
+ * Apply the {@link Type#beforeAssemble} operation across a series of values.
+ *
+ * @param row The values
+ * @param types The value types
+ * @param session The orginating session
+ */
+ public static void beforeAssemble(
+ final Serializable[] row,
+ final Type[] types,
+ final SessionImplementor session) {
+ for ( int i = 0; i < types.length; i++ ) {
+ if ( row[i] != LazyPropertyInitializer.UNFETCHED_PROPERTY
+ && row[i] != BackrefPropertyAccessor.UNKNOWN ) {
+ types[i].beforeAssemble( row[i], session );
+ }
+ }
+ }
+
+ /**
+ * Apply the {@link Type#assemble} operation across a series of values.
+ *
+ * @param row The values
+ * @param types The value types
+ * @param session The orginating session
+ * @param owner The entity "owning" the values
+ * @return The assembled state
+ */
+ public static Object[] assemble(
+ final Serializable[] row,
+ final Type[] types,
+ final SessionImplementor session,
+ final Object owner) {
+ Object[] assembled = new Object[row.length];
+ for ( int i = 0; i < types.length; i++ ) {
+ if ( row[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY || row[i] == BackrefPropertyAccessor.UNKNOWN ) {
+ assembled[i] = row[i];
+ }
+ else {
+ assembled[i] = types[i].assemble( row[i], session, owner );
+ }
+ }
+ return assembled;
+ }
+
+ /**
+ * Apply the {@link Type#disassemble} operation across a series of values.
+ *
+ * @param row The values
+ * @param types The value types
+ * @param nonCacheable An array indicating which values to include in the disassemled state
+ * @param session The orginating session
+ * @param owner The entity "owning" the values
+ * @return The disassembled state
+ */
+ public static Serializable[] disassemble(
+ final Object[] row,
+ final Type[] types,
+ final boolean[] nonCacheable,
+ final SessionImplementor session,
+ final Object owner) {
+ Serializable[] disassembled = new Serializable[row.length];
+ for ( int i = 0; i < row.length; i++ ) {
+ if ( nonCacheable!=null && nonCacheable[i] ) {
+ disassembled[i] = LazyPropertyInitializer.UNFETCHED_PROPERTY;
+ }
+ else if ( row[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY || row[i] == BackrefPropertyAccessor.UNKNOWN ) {
+ disassembled[i] = (Serializable) row[i];
+ }
+ else {
+ disassembled[i] = types[i].disassemble( row[i], session, owner );
+ }
+ }
+ return disassembled;
+ }
+
+ /**
+ * Apply the {@link Type#replace} operation across a series of values.
+ *
+ * @param original The source of the state
+ * @param target The target into which to replace the source values.
+ * @param types The value types
+ * @param session The orginating session
+ * @param owner The entity "owning" the values
+ * @param copyCache A map representing a cache of already replaced state
+ * @return The replaced state
+ */
+ public static Object[] replace(
+ final Object[] original,
+ final Object[] target,
+ final Type[] types,
+ final SessionImplementor session,
+ final Object owner,
+ final Map copyCache) {
+ Object[] copied = new Object[original.length];
+ for ( int i = 0; i < types.length; i++ ) {
+ if ( original[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY
+ || original[i] == BackrefPropertyAccessor.UNKNOWN ) {
+ copied[i] = target[i];
+ }
+ else {
+ copied[i] = types[i].replace( original[i], target[i], session, owner, copyCache );
+ }
+ }
+ return copied;
+ }
+
+ /**
+ * Apply the {@link Type#replace} operation across a series of values.
+ *
+ * @param original The source of the state
+ * @param target The target into which to replace the source values.
+ * @param types The value types
+ * @param session The orginating session
+ * @param owner The entity "owning" the values
+ * @param copyCache A map representing a cache of already replaced state
+ * @param foreignKeyDirection FK directionality to be applied to the replacement
+ * @return The replaced state
+ */
+ public static Object[] replace(
+ final Object[] original,
+ final Object[] target,
+ final Type[] types,
+ final SessionImplementor session,
+ final Object owner,
+ final Map copyCache,
+ final ForeignKeyDirection foreignKeyDirection) {
+ Object[] copied = new Object[original.length];
+ for ( int i = 0; i < types.length; i++ ) {
+ if ( original[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY
+ || original[i] == BackrefPropertyAccessor.UNKNOWN ) {
+ copied[i] = target[i];
+ }
+ else {
+ copied[i] = types[i].replace( original[i], target[i], session, owner, copyCache, foreignKeyDirection );
+ }
+ }
+ return copied;
+ }
+
+ /**
+ * Apply the {@link Type#replace} operation across a series of values, as
+ * long as the corresponding {@link Type} is an association.
+ * <p/>
+ * If the corresponding type is a component type, then apply {@link #replaceAssociations}
+ * accross the component subtypes but do not replace the component value itself.
+ *
+ * @param original The source of the state
+ * @param target The target into which to replace the source values.
+ * @param types The value types
+ * @param session The orginating session
+ * @param owner The entity "owning" the values
+ * @param copyCache A map representing a cache of already replaced state
+ * @param foreignKeyDirection FK directionality to be applied to the replacement
+ * @return The replaced state
+ */
+ public static Object[] replaceAssociations(
+ final Object[] original,
+ final Object[] target,
+ final Type[] types,
+ final SessionImplementor session,
+ final Object owner,
+ final Map copyCache,
+ final ForeignKeyDirection foreignKeyDirection) {
+ Object[] copied = new Object[original.length];
+ for ( int i = 0; i < types.length; i++ ) {
+ if ( original[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY
+ || original[i] == BackrefPropertyAccessor.UNKNOWN ) {
+ copied[i] = target[i];
+ }
+ else if ( types[i].isComponentType() ) {
+ // need to extract the component values and check for subtype replacements...
+ AbstractComponentType componentType = ( AbstractComponentType ) types[i];
+ Type[] subtypes = componentType.getSubtypes();
+ Object[] origComponentValues = original[i] == null ? new Object[subtypes.length] : componentType.getPropertyValues( original[i], session );
+ Object[] targetComponentValues = componentType.getPropertyValues( target[i], session );
+ replaceAssociations( origComponentValues, targetComponentValues, subtypes, session, null, copyCache, foreignKeyDirection );
+ copied[i] = target[i];
+ }
+ else if ( !types[i].isAssociationType() ) {
+ copied[i] = target[i];
+ }
+ else {
+ copied[i] = types[i].replace( original[i], target[i], session, owner, copyCache, foreignKeyDirection );
+ }
+ }
+ return copied;
+ }
+
+ /**
+ * Determine if any of the given field values are dirty, returning an array containing
+ * indices of the dirty fields.
+ * <p/>
+ * If it is determined that no fields are dirty, null is returned.
+ *
+ * @param properties The property definitions
+ * @param currentState The current state of the entity
+ * @param previousState The baseline state of the entity
+ * @param includeColumns Columns to be included in the dirty checking, per property
+ * @param anyUninitializedProperties Does the entity currently hold any uninitialized property values?
+ * @param session The session from which the dirty check request originated.
+ * @return Array containing indices of the dirty properties, or null if no properties considered dirty.
+ */
+ public static int[] findDirty(
+ final StandardProperty[] properties,
+ final Object[] currentState,
+ final Object[] previousState,
+ final boolean[][] includeColumns,
+ final boolean anyUninitializedProperties,
+ final SessionImplementor session) {
+ int[] results = null;
+ int count = 0;
+ int span = properties.length;
+
+ for ( int i = 0; i < span; i++ ) {
+ final boolean dirty = currentState[i] != LazyPropertyInitializer.UNFETCHED_PROPERTY
+ && properties[i].isDirtyCheckable( anyUninitializedProperties )
+ && properties[i].getType().isDirty( previousState[i], currentState[i], includeColumns[i], session );
+ if ( dirty ) {
+ if ( results == null ) {
+ results = new int[span];
+ }
+ results[count++] = i;
+ }
+ }
+
+ if ( count == 0 ) {
+ return null;
+ }
+ else {
+ int[] trimmed = new int[count];
+ System.arraycopy( results, 0, trimmed, 0, count );
+ return trimmed;
+ }
+ }
+
+ /**
+ * Determine if any of the given field values are modified, returning an array containing
+ * indices of the modified fields.
+ * <p/>
+ * If it is determined that no fields are dirty, null is returned.
+ *
+ * @param properties The property definitions
+ * @param currentState The current state of the entity
+ * @param previousState The baseline state of the entity
+ * @param includeColumns Columns to be included in the mod checking, per property
+ * @param anyUninitializedProperties Does the entity currently hold any uninitialized property values?
+ * @param session The session from which the dirty check request originated.
+ * @return Array containing indices of the modified properties, or null if no properties considered modified.
+ */
+ public static int[] findModified(
+ final StandardProperty[] properties,
+ final Object[] currentState,
+ final Object[] previousState,
+ final boolean[][] includeColumns,
+ final boolean anyUninitializedProperties,
+ final SessionImplementor session) {
+ int[] results = null;
+ int count = 0;
+ int span = properties.length;
+
+ for ( int i = 0; i < span; i++ ) {
+ final boolean modified = currentState[i]!=LazyPropertyInitializer.UNFETCHED_PROPERTY
+ && properties[i].isDirtyCheckable(anyUninitializedProperties)
+ && properties[i].getType().isModified( previousState[i], currentState[i], includeColumns[i], session );
+
+ if ( modified ) {
+ if ( results == null ) {
+ results = new int[span];
+ }
+ results[count++] = i;
+ }
+ }
+
+ if ( count == 0 ) {
+ return null;
+ }
+ else {
+ int[] trimmed = new int[count];
+ System.arraycopy( results, 0, trimmed, 0, count );
+ return trimmed;
+ }
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/VersionType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/VersionType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/VersionType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,51 @@
+//$Id: VersionType.java 7736 2005-08-03 20:03:34Z steveebersole $
+package org.hibernate.type;
+
+import java.util.Comparator;
+
+import org.hibernate.engine.SessionImplementor;
+
+/**
+ * A <tt>Type</tt> that may be used to version data.
+ * @author Gavin King
+ */
+public interface VersionType extends Type {
+ /**
+ * Generate an initial version.
+ *
+ * @param session The session from which this request originates.
+ * @return an instance of the type
+ */
+ public Object seed(SessionImplementor session);
+
+ /**
+ * Increment the version.
+ *
+ * @param session The session from which this request originates.
+ * @param current the current version
+ * @return an instance of the type
+ */
+ public Object next(Object current, SessionImplementor session);
+
+ /**
+ * Get a comparator for version values.
+ *
+ * @return The comparator to use to compare different version values.
+ */
+ public Comparator getComparator();
+
+ /**
+ * Are the two version values considered equal?
+ *
+ * @param x One value to check.
+ * @param y The other value to check.
+ * @return true if the values are equal, false otherwise.
+ */
+ public boolean isEqual(Object x, Object y);
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/WrapperBinaryType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/WrapperBinaryType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/WrapperBinaryType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,41 @@
+//$Id: $
+package org.hibernate.type;
+
+import org.hibernate.HibernateException;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class WrapperBinaryType extends AbstractBynaryType {
+ protected Object toExternalFormat(byte[] bytes) {
+ if (bytes == null) return null;
+ int length = bytes.length;
+ Byte[] result = new Byte[length];
+ for ( int index = 0; index < length ; index++ ) {
+ result[index] = new Byte( bytes[index] );
+ }
+ return result;
+ }
+
+ protected byte[] toInternalFormat(Object val) {
+ if (val == null) return null;
+ Byte[] bytes = (Byte[]) val;
+ int length = bytes.length;
+ byte[] result = new byte[length];
+ for ( int i = 0; i < length ; i++ ) {
+ if (bytes[i] == null)
+ throw new HibernateException("Unable to store an Byte[] when one of its element is null");
+ result[i] = bytes[i].byteValue();
+ }
+ return result;
+ }
+
+ public Class getReturnedClass() {
+ return Byte[].class;
+ }
+
+ public String getName() {
+ //TODO find a decent name before documenting
+ return "wrapper-binary";
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/YesNoType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/YesNoType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/YesNoType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,25 @@
+//$Id: YesNoType.java 3890 2004-06-03 16:31:32Z steveebersole $
+package org.hibernate.type;
+
+/**
+ * <tt>yes_no</tt>: A type that maps an SQL CHAR(1) to a Java Boolean.
+ * @author Gavin King
+ */
+public class YesNoType extends CharBooleanType {
+
+ protected final String getTrueString() {
+ return "Y";
+ }
+ protected final String getFalseString() {
+ return "N";
+ }
+ public String getName() { return "yes_no"; }
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/type/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/type/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/type/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,9 @@
+<html>
+<head></head>
+<body>
+<p>
+ A Hibernate <tt>Type</tt> is a strategy for mapping a
+ Java property type to a JDBC type or types.
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/type/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/usertype/CompositeUserType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/usertype/CompositeUserType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/usertype/CompositeUserType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,184 @@
+//$Id: CompositeUserType.java 6471 2005-04-19 20:36:59Z oneovthafew $
+package org.hibernate.usertype;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.type.Type;
+
+/**
+ * A <tt>UserType</tt> that may be dereferenced in a query.
+ * This interface allows a custom type to define "properties".
+ * These need not necessarily correspond to physical JavaBeans
+ * style properties.<br>
+ * <br>
+ * A <tt>CompositeUserType</tt> may be used in almost every way
+ * that a component may be used. It may even contain many-to-one
+ * associations.<br>
+ * <br>
+ * Implementors must be immutable and must declare a public
+ * default constructor.<br>
+ * <br>
+ * Unlike <tt>UserType</tt>, cacheability does not depend upon
+ * serializability. Instead, <tt>assemble()</tt> and
+ * <tt>disassemble</tt> provide conversion to/from a cacheable
+ * representation.
+ *
+ * @see UserType for more simple cases
+ * @see org.hibernate.type.Type
+ * @author Gavin King
+ */
+public interface CompositeUserType {
+
+ /**
+ * Get the "property names" that may be used in a
+ * query.
+ *
+ * @return an array of "property names"
+ */
+ public String[] getPropertyNames();
+
+ /**
+ * Get the corresponding "property types".
+ *
+ * @return an array of Hibernate types
+ */
+ public Type[] getPropertyTypes();
+
+ /**
+ * Get the value of a property.
+ *
+ * @param component an instance of class mapped by this "type"
+ * @param property
+ * @return the property value
+ * @throws HibernateException
+ */
+ public Object getPropertyValue(Object component, int property) throws HibernateException;
+
+ /**
+ * Set the value of a property.
+ *
+ * @param component an instance of class mapped by this "type"
+ * @param property
+ * @param value the value to set
+ * @throws HibernateException
+ */
+ public void setPropertyValue(Object component, int property, Object value) throws HibernateException;
+
+ /**
+ * The class returned by <tt>nullSafeGet()</tt>.
+ *
+ * @return Class
+ */
+ public Class returnedClass();
+
+ /**
+ * Compare two instances of the class mapped by this type for persistence "equality".
+ * Equality of the persistent state.
+ *
+ * @param x
+ * @param y
+ * @return boolean
+ * @throws HibernateException
+ */
+ public boolean equals(Object x, Object y) throws HibernateException;
+
+ /**
+ * Get a hashcode for the instance, consistent with persistence "equality"
+ */
+ public int hashCode(Object x) throws HibernateException;
+
+ /**
+ * Retrieve an instance of the mapped class from a JDBC resultset. Implementors
+ * should handle possibility of null values.
+ *
+ * @param rs a JDBC result set
+ * @param names the column names
+ * @param session
+ * @param owner the containing entity
+ * @return Object
+ * @throws HibernateException
+ * @throws SQLException
+ */
+ public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner)
+ throws HibernateException, SQLException;
+
+ /**
+ * Write an instance of the mapped class to a prepared statement. Implementors
+ * should handle possibility of null values. A multi-column type should be written
+ * to parameters starting from <tt>index</tt>.
+ *
+ * @param st a JDBC prepared statement
+ * @param value the object to write
+ * @param index statement parameter index
+ * @param session
+ * @throws HibernateException
+ * @throws SQLException
+ */
+ public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session)
+ throws HibernateException, SQLException;
+
+ /**
+ * Return a deep copy of the persistent state, stopping at entities and at collections.
+ *
+ * @param value generally a collection element or entity field
+ * @return Object a copy
+ * @throws HibernateException
+ */
+ public Object deepCopy(Object value) throws HibernateException;
+
+ /**
+ * Check if objects of this type mutable.
+ *
+ * @return boolean
+ */
+ public boolean isMutable();
+
+ /**
+ * Transform the object into its cacheable representation. At the very least this
+ * method should perform a deep copy. That may not be enough for some implementations,
+ * however; for example, associations must be cached as identifier values. (optional
+ * operation)
+ *
+ * @param value the object to be cached
+ * @param session
+ * @return a cachable representation of the object
+ * @throws HibernateException
+ */
+ public Serializable disassemble(Object value, SessionImplementor session) throws HibernateException;
+
+ /**
+ * Reconstruct an object from the cacheable representation. At the very least this
+ * method should perform a deep copy. (optional operation)
+ *
+ * @param cached the object to be cached
+ * @param session
+ * @param owner the owner of the cached object
+ * @return a reconstructed object from the cachable representation
+ * @throws HibernateException
+ */
+ public Object assemble(Serializable cached, SessionImplementor session, Object owner)
+ throws HibernateException;
+
+ /**
+ * During merge, replace the existing (target) value in the entity we are merging to
+ * with a new (original) value from the detached entity we are merging. For immutable
+ * objects, or null values, it is safe to simply return the first parameter. For
+ * mutable objects, it is safe to return a copy of the first parameter. However, since
+ * composite user types often define component values, it might make sense to recursively
+ * replace component values in the target object.
+ *
+ * @param original
+ * @param target
+ * @param session
+ * @param owner
+ * @return
+ * @throws HibernateException
+ */
+ public Object replace(Object original, Object target, SessionImplementor session, Object owner)
+ throws HibernateException;
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/usertype/EnhancedUserType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/usertype/EnhancedUserType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/usertype/EnhancedUserType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,27 @@
+//$Id: EnhancedUserType.java 5956 2005-02-28 06:07:06Z oneovthafew $
+package org.hibernate.usertype;
+
+/**
+ * A custom type that may function as an identifier or
+ * discriminator type, or may be marshalled to and from
+ * an XML document
+ *
+ * @author Gavin King
+ */
+public interface EnhancedUserType extends UserType {
+ /**
+ * Return an SQL literal representation of the value
+ */
+ public String objectToSQLString(Object value);
+
+ /**
+ * Return a string representation of this value, as it
+ * should appear in an XML document
+ */
+ public String toXMLString(Object value);
+ /**
+ * Parse a string representation of this value, as it
+ * appears in an XML document
+ */
+ public Object fromXMLString(String xmlValue);
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/usertype/EnhancedUserType.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/usertype/LoggableUserType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/usertype/LoggableUserType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/usertype/LoggableUserType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,20 @@
+package org.hibernate.usertype;
+
+import org.hibernate.engine.SessionFactoryImplementor;
+
+/**
+ * Marker interface for user types which want to perform custom
+ * logging of their corresponding values
+ *
+ * @author Steve Ebersole
+ */
+public interface LoggableUserType {
+ /**
+ * Generate a loggable string representation of the collection (value).
+ *
+ * @param value The collection to be logged; guarenteed to be non-null and initialized.
+ * @param factory The factory.
+ * @return The loggable string representation.
+ */
+ public String toLoggableString(Object value, SessionFactoryImplementor factory);
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/usertype/ParameterizedType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/usertype/ParameterizedType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/usertype/ParameterizedType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,21 @@
+//$Id: ParameterizedType.java 4499 2004-09-05 04:32:54Z oneovthafew $
+package org.hibernate.usertype;
+
+import java.util.Properties;
+
+/**
+ * Support for parameterizable types. A UserType or CustomUserType may be
+ * made parameterizable by implementing this interface. Parameters for a
+ * type may be set by using a nested type element for the property element
+ * in the mapping file, or by defining a typedef.
+ *
+ * @author Michael Gloegl
+ */
+public interface ParameterizedType {
+
+ /**
+ * Gets called by Hibernate to pass the configured type parameters to
+ * the implementation.
+ */
+ public void setParameterValues(Properties parameters);
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/usertype/UserCollectionType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/usertype/UserCollectionType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/usertype/UserCollectionType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,70 @@
+//$Id: UserCollectionType.java 10086 2006-07-05 18:17:27Z steve.ebersole at jboss.com $
+package org.hibernate.usertype;
+
+import java.util.Iterator;
+import java.util.Map;
+
+import org.hibernate.HibernateException;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.collection.CollectionPersister;
+
+/**
+ * A custom type for mapping user-written classes that implement <tt>PersistentCollection</tt>
+ *
+ * @see org.hibernate.collection.PersistentCollection
+ * @author Gavin King
+ */
+public interface UserCollectionType {
+
+ /**
+ * Instantiate an uninitialized instance of the collection wrapper
+ */
+ public PersistentCollection instantiate(SessionImplementor session, CollectionPersister persister)
+ throws HibernateException;
+
+ /**
+ * Wrap an instance of a collection
+ */
+ public PersistentCollection wrap(SessionImplementor session, Object collection);
+
+ /**
+ * Return an iterator over the elements of this collection - the passed collection
+ * instance may or may not be a wrapper
+ */
+ public Iterator getElementsIterator(Object collection);
+
+ /**
+ * Optional operation. Does the collection contain the entity instance?
+ */
+ public boolean contains(Object collection, Object entity);
+ /**
+ * Optional operation. Return the index of the entity in the collection.
+ */
+ public Object indexOf(Object collection, Object entity);
+
+ /**
+ * Replace the elements of a collection with the elements of another collection
+ */
+ public Object replaceElements(
+ Object original,
+ Object target,
+ CollectionPersister persister,
+ Object owner,
+ Map copyCache,
+ SessionImplementor session)
+ throws HibernateException;
+
+ /**
+ * Instantiate an empty instance of the "underlying" collection (not a wrapper),
+ * but with the given anticipated size (i.e. accounting for initial size
+ * and perhaps load factor).
+ *
+ * @param anticipatedSize The anticipated size of the instaniated collection
+ * after we are done populating it. Note, may be negative to indicate that
+ * we not yet know anything about the anticipated size (i.e., when initializing
+ * from a result set row by row).
+ */
+ public Object instantiate(int anticipatedSize);
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/usertype/UserCollectionType.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/usertype/UserType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/usertype/UserType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/usertype/UserType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,160 @@
+//$Id: UserType.java 6133 2005-03-21 16:53:58Z turin42 $
+package org.hibernate.usertype;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.hibernate.HibernateException;
+
+/**
+ * This interface should be implemented by user-defined "types".
+ * A "type" class is <em>not</em> the actual property type - it
+ * is a class that knows how to serialize instances of another
+ * class to and from JDBC.<br>
+ * <br>
+ * This interface
+ * <ul>
+ * <li>abstracts user code from future changes to the <tt>Type</tt>
+ * interface,</li>
+ * <li>simplifies the implementation of custom types and</li>
+ * <li>hides certain "internal" interfaces from user code.</li>
+ * </ul>
+ * <br>
+ * Implementors must be immutable and must declare a public
+ * default constructor.<br>
+ * <br>
+ * The actual class mapped by a <tt>UserType</tt> may be just
+ * about anything.<br>
+ * <br>
+ * <tt>CompositeUserType</tt> provides an extended version of
+ * this interface that is useful for more complex cases.<br>
+ * <br>
+ * Alternatively, custom types could implement <tt>Type</tt>
+ * directly or extend one of the abstract classes in
+ * <tt>org.hibernate.type</tt>. This approach risks future
+ * incompatible changes to classes or interfaces in that
+ * package.
+ *
+ * @see CompositeUserType for more complex cases
+ * @see org.hibernate.type.Type
+ * @author Gavin King
+ */
+public interface UserType {
+
+ /**
+ * Return the SQL type codes for the columns mapped by this type. The
+ * codes are defined on <tt>java.sql.Types</tt>.
+ * @see java.sql.Types
+ * @return int[] the typecodes
+ */
+ public int[] sqlTypes();
+
+ /**
+ * The class returned by <tt>nullSafeGet()</tt>.
+ *
+ * @return Class
+ */
+ public Class returnedClass();
+
+ /**
+ * Compare two instances of the class mapped by this type for persistence "equality".
+ * Equality of the persistent state.
+ *
+ * @param x
+ * @param y
+ * @return boolean
+ */
+ public boolean equals(Object x, Object y) throws HibernateException;
+
+ /**
+ * Get a hashcode for the instance, consistent with persistence "equality"
+ */
+ public int hashCode(Object x) throws HibernateException;
+
+ /**
+ * Retrieve an instance of the mapped class from a JDBC resultset. Implementors
+ * should handle possibility of null values.
+ *
+ * @param rs a JDBC result set
+ * @param names the column names
+ * @param owner the containing entity
+ * @return Object
+ * @throws HibernateException
+ * @throws SQLException
+ */
+ public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException;
+
+ /**
+ * Write an instance of the mapped class to a prepared statement. Implementors
+ * should handle possibility of null values. A multi-column type should be written
+ * to parameters starting from <tt>index</tt>.
+ *
+ * @param st a JDBC prepared statement
+ * @param value the object to write
+ * @param index statement parameter index
+ * @throws HibernateException
+ * @throws SQLException
+ */
+ public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException;
+
+ /**
+ * Return a deep copy of the persistent state, stopping at entities and at
+ * collections. It is not necessary to copy immutable objects, or null
+ * values, in which case it is safe to simply return the argument.
+ *
+ * @param value the object to be cloned, which may be null
+ * @return Object a copy
+ */
+ public Object deepCopy(Object value) throws HibernateException;
+
+ /**
+ * Are objects of this type mutable?
+ *
+ * @return boolean
+ */
+ public boolean isMutable();
+
+ /**
+ * Transform the object into its cacheable representation. At the very least this
+ * method should perform a deep copy if the type is mutable. That may not be enough
+ * for some implementations, however; for example, associations must be cached as
+ * identifier values. (optional operation)
+ *
+ * @param value the object to be cached
+ * @return a cachable representation of the object
+ * @throws HibernateException
+ */
+ public Serializable disassemble(Object value) throws HibernateException;
+
+ /**
+ * Reconstruct an object from the cacheable representation. At the very least this
+ * method should perform a deep copy if the type is mutable. (optional operation)
+ *
+ * @param cached the object to be cached
+ * @param owner the owner of the cached object
+ * @return a reconstructed object from the cachable representation
+ * @throws HibernateException
+ */
+ public Object assemble(Serializable cached, Object owner) throws HibernateException;
+
+ /**
+ * During merge, replace the existing (target) value in the entity we are merging to
+ * with a new (original) value from the detached entity we are merging. For immutable
+ * objects, or null values, it is safe to simply return the first parameter. For
+ * mutable objects, it is safe to return a copy of the first parameter. For objects
+ * with component values, it might make sense to recursively replace component values.
+ *
+ * @param original the value from the detached entity being merged
+ * @param target the value in the managed entity
+ * @return the value to be merged
+ */
+ public Object replace(Object original, Object target, Object owner) throws HibernateException;
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/usertype/UserVersionType.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/usertype/UserVersionType.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/usertype/UserVersionType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,32 @@
+//$Id: UserVersionType.java 7736 2005-08-03 20:03:34Z steveebersole $
+package org.hibernate.usertype;
+
+import java.util.Comparator;
+
+import org.hibernate.engine.SessionImplementor;
+
+/**
+ * A user type that may be used for a version property
+ *
+ * @author Gavin King
+ */
+public interface UserVersionType extends UserType, Comparator {
+ /**
+ * Generate an initial version.
+ *
+ * @param session The session from which this request originates. May be
+ * null; currently this only happens during startup when trying to determine
+ * the "unsaved value" of entities.
+ * @return an instance of the type
+ */
+ public Object seed(SessionImplementor session);
+ /**
+ * Increment the version.
+ *
+ * @param session The session from which this request originates.
+ * @param current the current version
+ * @return an instance of the type
+ */
+ public Object next(Object current, SessionImplementor session);
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/usertype/UserVersionType.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/usertype/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/usertype/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/usertype/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,8 @@
+<html>
+<head></head>
+<body>
+<p>
+ Interfaces for user-defined custom types.
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/usertype/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/util/ArrayHelper.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/util/ArrayHelper.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/util/ArrayHelper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,345 @@
+//$Id: ArrayHelper.java 7546 2005-07-19 18:17:15Z oneovthafew $
+package org.hibernate.util;
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.hibernate.LockMode;
+import org.hibernate.type.Type;
+
+public final class ArrayHelper {
+
+ /*public static boolean contains(Object[] array, Object object) {
+ for ( int i=0; i<array.length; i++ ) {
+ if ( array[i].equals(object) ) return true;
+ }
+ return false;
+ }*/
+
+ public static int indexOf(Object[] array, Object object) {
+ for ( int i=0; i<array.length; i++ ) {
+ if ( array[i].equals(object) ) return i;
+ }
+ return -1;
+ }
+
+ /*public static Object[] clone(Class elementClass, Object[] array) {
+ Object[] result = (Object[]) Array.newInstance( elementClass, array.length );
+ System.arraycopy(array, 0, result, 0, array.length);
+ return result;
+ }*/
+
+ public static String[] toStringArray(Object[] objects) {
+ int length=objects.length;
+ String[] result = new String[length];
+ for (int i=0; i<length; i++) {
+ result[i] = objects[i].toString();
+ }
+ return result;
+ }
+
+ public static String[] fillArray(String value, int length) {
+ String[] result = new String[length];
+ Arrays.fill(result, value);
+ return result;
+ }
+
+ public static int[] fillArray(int value, int length) {
+ int[] result = new int[length];
+ Arrays.fill(result, value);
+ return result;
+ }
+
+ public static LockMode[] fillArray(LockMode lockMode, int length) {
+ LockMode[] array = new LockMode[length];
+ Arrays.fill(array, lockMode);
+ return array;
+ }
+
+ public static String[] toStringArray(Collection coll) {
+ return (String[]) coll.toArray(EMPTY_STRING_ARRAY);
+ }
+
+ public static String[][] to2DStringArray(Collection coll) {
+ return (String[][]) coll.toArray( new String[ coll.size() ][] );
+ }
+
+ public static int[][] to2DIntArray(Collection coll) {
+ return (int[][]) coll.toArray( new int[ coll.size() ][] );
+ }
+
+ public static Type[] toTypeArray(Collection coll) {
+ return (Type[]) coll.toArray(EMPTY_TYPE_ARRAY);
+ }
+
+ public static int[] toIntArray(Collection coll) {
+ Iterator iter = coll.iterator();
+ int[] arr = new int[ coll.size() ];
+ int i=0;
+ while( iter.hasNext() ) {
+ arr[i++] = ( (Integer) iter.next() ).intValue();
+ }
+ return arr;
+ }
+
+ public static boolean[] toBooleanArray(Collection coll) {
+ Iterator iter = coll.iterator();
+ boolean[] arr = new boolean[ coll.size() ];
+ int i=0;
+ while( iter.hasNext() ) {
+ arr[i++] = ( (Boolean) iter.next() ).booleanValue();
+ }
+ return arr;
+ }
+
+ public static Object[] typecast(Object[] array, Object[] to) {
+ return java.util.Arrays.asList(array).toArray(to);
+ }
+
+ //Arrays.asList doesn't do primitive arrays
+ public static List toList(Object array) {
+ if ( array instanceof Object[] ) return Arrays.asList( (Object[]) array ); //faster?
+ int size = Array.getLength(array);
+ ArrayList list = new ArrayList(size);
+ for (int i=0; i<size; i++) {
+ list.add( Array.get(array, i) );
+ }
+ return list;
+ }
+
+ public static String[] slice(String[] strings, int begin, int length) {
+ String[] result = new String[length];
+ for ( int i=0; i<length; i++ ) {
+ result[i] = strings[begin+i];
+ }
+ return result;
+ }
+
+ public static Object[] slice(Object[] objects, int begin, int length) {
+ Object[] result = new Object[length];
+ for ( int i=0; i<length; i++ ) {
+ result[i] = objects[begin+i];
+ }
+ return result;
+ }
+
+ public static List toList(Iterator iter) {
+ List list = new ArrayList();
+ while ( iter.hasNext() ) {
+ list.add( iter.next() );
+ }
+ return list;
+ }
+
+ public static String[] join(String[] x, String[] y) {
+ String[] result = new String[ x.length + y.length ];
+ for ( int i=0; i<x.length; i++ ) result[i] = x[i];
+ for ( int i=0; i<y.length; i++ ) result[i+x.length] = y[i];
+ return result;
+ }
+
+ public static String[] join(String[] x, String[] y, boolean[] use) {
+ String[] result = new String[ x.length + countTrue(use) ];
+ for ( int i=0; i<x.length; i++ ) result[i] = x[i];
+ int k = x.length;
+ for ( int i=0; i<y.length; i++ ) {
+ if ( use[i] ) result[k++] = y[i];
+ }
+ return result;
+ }
+
+ public static int[] join(int[] x, int[] y) {
+ int[] result = new int[ x.length + y.length ];
+ for ( int i=0; i<x.length; i++ ) result[i] = x[i];
+ for ( int i=0; i<y.length; i++ ) result[i+x.length] = y[i];
+ return result;
+ }
+
+ public static final boolean[] TRUE = { true };
+ public static final boolean[] FALSE = { false };
+
+ private ArrayHelper() {}
+
+ public static String toString( Object[] array ) {
+ StringBuffer sb = new StringBuffer();
+ sb.append("[");
+ for (int i = 0; i < array.length; i++) {
+ sb.append( array[i] );
+ if( i<array.length-1 ) sb.append(",");
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+
+ public static boolean isAllNegative(int[] array) {
+ for ( int i=0; i<array.length; i++ ) {
+ if ( array[i] >=0 ) return false;
+ }
+ return true;
+ }
+
+ public static boolean isAllTrue(boolean[] array) {
+ for ( int i=0; i<array.length; i++ ) {
+ if ( !array[i] ) return false;
+ }
+ return true;
+ }
+
+ public static int countTrue(boolean[] array) {
+ int result=0;
+ for ( int i=0; i<array.length; i++ ) {
+ if ( array[i] ) result++;
+ }
+ return result;
+ }
+
+ /*public static int countFalse(boolean[] array) {
+ int result=0;
+ for ( int i=0; i<array.length; i++ ) {
+ if ( !array[i] ) result++;
+ }
+ return result;
+ }*/
+
+ public static boolean isAllFalse(boolean[] array) {
+ for ( int i=0; i<array.length; i++ ) {
+ if ( array[i] ) return false;
+ }
+ return true;
+ }
+
+ public static void addAll(Collection collection, Object[] array) {
+ for ( int i=0; i<array.length; i++ ) {
+ collection.add( array[i] );
+ }
+ }
+
+ public static final String[] EMPTY_STRING_ARRAY = {};
+ public static final int[] EMPTY_INT_ARRAY = {};
+ public static final boolean[] EMPTY_BOOLEAN_ARRAY = {};
+ public static final Class[] EMPTY_CLASS_ARRAY = {};
+ public static final Object[] EMPTY_OBJECT_ARRAY = {};
+ public static final Type[] EMPTY_TYPE_ARRAY = {};
+
+ public static int[] getBatchSizes(int maxBatchSize) {
+ int batchSize = maxBatchSize;
+ int n=1;
+ while ( batchSize>1 ) {
+ batchSize = getNextBatchSize(batchSize);
+ n++;
+ }
+ int[] result = new int[n];
+ batchSize = maxBatchSize;
+ for ( int i=0; i<n; i++ ) {
+ result[i] = batchSize;
+ batchSize = getNextBatchSize(batchSize);
+ }
+ return result;
+ }
+
+ private static int getNextBatchSize(int batchSize) {
+ if (batchSize<=10) {
+ return batchSize-1; //allow 9,8,7,6,5,4,3,2,1
+ }
+ else if (batchSize/2 < 10) {
+ return 10;
+ }
+ else {
+ return batchSize / 2;
+ }
+ }
+
+ private static int SEED = 23;
+ private static int PRIME_NUMER = 37;
+
+ /**
+ * calculate the array hash (only the first level)
+ */
+ public static int hash(Object[] array) {
+ int length = array.length;
+ int seed = SEED;
+ for (int index = 0 ; index < length ; index++) {
+ seed = hash( seed, array[index] == null ? 0 : array[index].hashCode() );
+ }
+ return seed;
+ }
+
+ /**
+ * calculate the array hash (only the first level)
+ */
+ public static int hash(char[] array) {
+ int length = array.length;
+ int seed = SEED;
+ for (int index = 0 ; index < length ; index++) {
+ seed = hash( seed, (int) array[index] ) ;
+ }
+ return seed;
+ }
+
+ /**
+ * calculate the array hash (only the first level)
+ */
+ public static int hash(byte[] bytes) {
+ int length = bytes.length;
+ int seed = SEED;
+ for (int index = 0 ; index < length ; index++) {
+ seed = hash( seed, (int) bytes[index] ) ;
+ }
+ return seed;
+ }
+
+ private static int hash(int seed, int i) {
+ return PRIME_NUMER * seed + i;
+ }
+
+ /**
+ * Compare 2 arrays only at the first level
+ */
+ public static boolean isEquals(Object[] o1, Object[] o2) {
+ if (o1 == o2) return true;
+ if (o1 == null || o2 == null) return false;
+ int length = o1.length;
+ if (length != o2.length) return false;
+ for (int index = 0 ; index < length ; index++) {
+ if ( ! o1[index].equals( o2[index] ) ) return false;
+ }
+ return true;
+ }
+
+ /**
+ * Compare 2 arrays only at the first level
+ */
+ public static boolean isEquals(char[] o1, char[] o2) {
+ if (o1 == o2) return true;
+ if (o1 == null || o2 == null) return false;
+ int length = o1.length;
+ if (length != o2.length) return false;
+ for (int index = 0 ; index < length ; index++) {
+ if ( ! ( o1[index] == o2[index] ) ) return false;
+ }
+ return true;
+ }
+
+ /**
+ * Compare 2 arrays only at the first level
+ */
+ public static boolean isEquals(byte[] b1, byte[] b2) {
+ if (b1 == b2) return true;
+ if (b1 == null || b2 == null) return false;
+ int length = b1.length;
+ if (length != b2.length) return false;
+ for (int index = 0 ; index < length ; index++) {
+ if ( ! ( b1[index] == b2[index] ) ) return false;
+ }
+ return true;
+ }
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/util/BytesHelper.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/util/BytesHelper.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/util/BytesHelper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+//$Id: BytesHelper.java 4597 2004-09-26 03:17:21Z oneovthafew $
+package org.hibernate.util;
+
+public final class BytesHelper {
+
+ private BytesHelper() {}
+
+ public static int toInt( byte[] bytes ) {
+ int result = 0;
+ for (int i=0; i<4; i++) {
+ result = ( result << 8 ) - Byte.MIN_VALUE + (int) bytes[i];
+ }
+ return result;
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/util/CalendarComparator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/util/CalendarComparator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/util/CalendarComparator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+//$Id: CalendarComparator.java 4496 2004-09-04 13:01:55Z oneovthafew $
+package org.hibernate.util;
+
+import java.util.Calendar;
+import java.util.Comparator;
+
+/**
+ * @author Gavin King
+ */
+public class CalendarComparator implements Comparator {
+
+ public int compare(Object x, Object y) {
+ Calendar xcal = (Calendar) x;
+ Calendar ycal = (Calendar) y;
+ if ( xcal.before(ycal) ) return -1;
+ if ( xcal.after(ycal) ) return 1;
+ return 0;
+ }
+
+ public static final Comparator INSTANCE = new CalendarComparator();
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/util/CalendarComparator.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/util/Cloneable.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/util/Cloneable.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/util/Cloneable.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,139 @@
+//$Id: Cloneable.java 8670 2005-11-25 17:36:29Z epbernard $
+package org.hibernate.util;
+
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import org.hibernate.HibernateException;
+
+/**
+ * An object that is shallow-coneable
+ *
+ * @author Steve Ebersole
+ */
+public class Cloneable {
+
+ private static final Object[] READER_METHOD_ARGS = new Object[0];
+
+ /**
+ * Essentially performs a shallow copy of this SessionEventListenerConfig
+ * instance; meaning the SessionEventListenerConfig itself is cloned, but
+ * the individual listeners are <b>not</b> cloned.
+ *
+ * @return The SessionEventListenerConfig shallow copy.
+ */
+ public Object shallowCopy() {
+ return AccessController.doPrivileged(
+ new PrivilegedAction() {
+ public Object run() {
+ return copyListeners();
+ }
+ }
+ );
+ }
+
+ /**
+ * Checks to ensure the SessionEventListenerConfig is fully
+ * configured (basically, that none of the listeners is null).
+ *
+ * @throws HibernateException If the SessionEventListenerConfig
+ * is not fully configured.
+ */
+ public void validate() throws HibernateException {
+ AccessController.doPrivileged(
+ new PrivilegedAction() {
+ public Object run() {
+ checkListeners();
+ return null;
+ }
+ }
+ );
+
+ }
+
+ private Object copyListeners() {
+ Object copy = null;
+ BeanInfo beanInfo = null;
+ try {
+ beanInfo = Introspector.getBeanInfo( getClass(), Object.class );
+ internalCheckListeners( beanInfo );
+ copy = getClass().newInstance();
+ PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
+ for ( int i = 0, max = pds.length; i < max; i++ ) {
+ try {
+ pds[i].getWriteMethod().invoke(
+ copy,
+ new Object[] {
+ pds[i].getReadMethod().invoke( this, READER_METHOD_ARGS )
+ }
+ );
+ }
+ catch( Throwable t ) {
+ throw new HibernateException( "Unable copy copy listener [" + pds[i].getName() + "]" );
+ }
+ }
+ }
+ catch( Exception t ) {
+ throw new HibernateException( "Unable to copy listeners", t );
+ }
+ finally {
+ if ( beanInfo != null ) {
+ // release the jdk internal caches everytime to ensure this
+ // plays nicely with destroyable class-loaders
+ Introspector.flushFromCaches( getClass() );
+ }
+ }
+
+ return copy;
+ }
+
+ private void checkListeners() {
+ BeanInfo beanInfo = null;
+ try {
+ beanInfo = Introspector.getBeanInfo( getClass(), Object.class );
+ internalCheckListeners( beanInfo );
+ }
+ catch( IntrospectionException t ) {
+ throw new HibernateException( "Unable to validate listener config", t );
+ }
+ finally {
+ if ( beanInfo != null ) {
+ // release the jdk internal caches everytime to ensure this
+ // plays nicely with destroyable class-loaders
+ Introspector.flushFromCaches( getClass() );
+ }
+ }
+ }
+
+ private void internalCheckListeners(BeanInfo beanInfo) {
+ PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
+ try {
+ for ( int i = 0, max = pds.length; i < max; i++ ) {
+ final Object listener = pds[i].getReadMethod().invoke( this, READER_METHOD_ARGS );
+ if ( listener == null ) {
+ throw new HibernateException( "Listener [" + pds[i].getName() + "] was null" );
+ }
+ if ( listener.getClass().isArray() ) {
+ Object[] listenerArray = (Object[]) listener;
+ int length = listenerArray.length;
+ for ( int index = 0 ; index < length ; index++ ) {
+ if ( listenerArray[index] == null ) {
+ throw new HibernateException( "Listener in [" + pds[i].getName() + "] was null" );
+ }
+ }
+ }
+ }
+ }
+ catch( HibernateException e ) {
+ throw e;
+ }
+ catch( Throwable t ) {
+ throw new HibernateException( "Unable to validate listener config" );
+ }
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/util/Cloneable.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/util/CollectionHelper.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/util/CollectionHelper.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/util/CollectionHelper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+//$Id: CollectionHelper.java 7516 2005-07-16 22:20:48Z oneovthafew $
+package org.hibernate.util;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Gavin King
+ */
+public final class CollectionHelper {
+
+ public static final List EMPTY_LIST = Collections.unmodifiableList( new ArrayList(0) );
+ public static final Collection EMPTY_COLLECTION = Collections.unmodifiableCollection( new ArrayList(0) );
+ public static final Map EMPTY_MAP = Collections.unmodifiableMap( new HashMap(0) );
+
+ private CollectionHelper() {}
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/util/CollectionHelper.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/util/ComparableComparator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/util/ComparableComparator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/util/ComparableComparator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,18 @@
+//$Id: ComparableComparator.java 4496 2004-09-04 13:01:55Z oneovthafew $
+package org.hibernate.util;
+
+import java.util.Comparator;
+
+/**
+ * Delegates to Comparable
+ * @author Gavin King
+ */
+public class ComparableComparator implements Comparator {
+
+ public int compare(Object x, Object y) {
+ return ( (Comparable) x ).compareTo(y);
+ }
+
+ public static final Comparator INSTANCE = new ComparableComparator();
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/util/ComparableComparator.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/util/ConfigHelper.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/util/ConfigHelper.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/util/ConfigHelper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,181 @@
+// $Id: ConfigHelper.java 9972 2006-05-31 16:59:05Z steve.ebersole at jboss.com $
+package org.hibernate.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Properties;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.HibernateException;
+import org.hibernate.cfg.Environment;
+
+/**
+ * A simple class to centralize logic needed to locate config files on the system.
+ *
+ * @author Steve
+ */
+public final class ConfigHelper {
+ private static final Log log = LogFactory.getLog(ConfigHelper.class);
+
+ /** Try to locate a local URL representing the incoming path. The first attempt
+ * assumes that the incoming path is an actual URL string (file://, etc). If this
+ * does not work, then the next attempts try to locate this UURL as a java system
+ * resource.
+ *
+ * @param path The path representing the config location.
+ * @return An appropriate URL or null.
+ */
+ public static final URL locateConfig(final String path) {
+ try {
+ return new URL(path);
+ }
+ catch(MalformedURLException e) {
+ return findAsResource(path);
+ }
+ }
+
+ /**
+ * Try to locate a local URL representing the incoming path.
+ * This method <b>only</b> attempts to locate this URL as a
+ * java system resource.
+ *
+ * @param path The path representing the config location.
+ * @return An appropriate URL or null.
+ */
+ public static final URL findAsResource(final String path) {
+ URL url = null;
+
+ // First, try to locate this resource through the current
+ // context classloader.
+ ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+ if (contextClassLoader!=null) {
+ url = contextClassLoader.getResource(path);
+ }
+ if (url != null)
+ return url;
+
+ // Next, try to locate this resource through this class's classloader
+ url = ConfigHelper.class.getClassLoader().getResource(path);
+ if (url != null)
+ return url;
+
+ // Next, try to locate this resource through the system classloader
+ url = ClassLoader.getSystemClassLoader().getResource(path);
+
+ // Anywhere else we should look?
+ return url;
+ }
+
+ /** Open an InputStream to the URL represented by the incoming path. First makes a call
+ * to {@link #locateConfig(java.lang.String)} in order to find an appropriate URL.
+ * {@link java.net.URL#openStream()} is then called to obtain the stream.
+ *
+ * @param path The path representing the config location.
+ * @return An input stream to the requested config resource.
+ * @throws HibernateException Unable to open stream to that resource.
+ */
+ public static final InputStream getConfigStream(final String path) throws HibernateException {
+ final URL url = ConfigHelper.locateConfig(path);
+
+ if (url == null) {
+ String msg = "Unable to locate config file: " + path;
+ log.fatal(msg);
+ throw new HibernateException(msg);
+ }
+
+ try {
+ return url.openStream();
+ }
+ catch(IOException e) {
+ throw new HibernateException("Unable to open config file: " + path, e);
+ }
+ }
+
+ /** Open an Reader to the URL represented by the incoming path. First makes a call
+ * to {@link #locateConfig(java.lang.String)} in order to find an appropriate URL.
+ * {@link java.net.URL#openStream()} is then called to obtain a stream, which is then
+ * wrapped in a Reader.
+ *
+ * @param path The path representing the config location.
+ * @return An input stream to the requested config resource.
+ * @throws HibernateException Unable to open reader to that resource.
+ */
+ public static final Reader getConfigStreamReader(final String path) throws HibernateException {
+ return new InputStreamReader( getConfigStream(path) );
+ }
+
+ /** Loads a properties instance based on the data at the incoming config location.
+ *
+ * @param path The path representing the config location.
+ * @return The loaded properties instance.
+ * @throws HibernateException Unable to load properties from that resource.
+ */
+ public static final Properties getConfigProperties(String path) throws HibernateException {
+ try {
+ Properties properties = new Properties();
+ properties.load( getConfigStream(path) );
+ return properties;
+ }
+ catch(IOException e) {
+ throw new HibernateException("Unable to load properties from specified config file: " + path, e);
+ }
+ }
+
+ private ConfigHelper() {}
+
+ public static InputStream getResourceAsStream(String resource) {
+ String stripped = resource.startsWith("/") ?
+ resource.substring(1) : resource;
+
+ InputStream stream = null;
+ ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+ if (classLoader!=null) {
+ stream = classLoader.getResourceAsStream( stripped );
+ }
+ if ( stream == null ) {
+ stream = Environment.class.getResourceAsStream( resource );
+ }
+ if ( stream == null ) {
+ stream = Environment.class.getClassLoader().getResourceAsStream( stripped );
+ }
+ if ( stream == null ) {
+ throw new HibernateException( resource + " not found" );
+ }
+ return stream;
+ }
+
+
+ public static InputStream getUserResourceAsStream(String resource) {
+ boolean hasLeadingSlash = resource.startsWith( "/" );
+ String stripped = hasLeadingSlash ? resource.substring(1) : resource;
+
+ InputStream stream = null;
+
+ ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+ if ( classLoader != null ) {
+ stream = classLoader.getResourceAsStream( resource );
+ if ( stream == null && hasLeadingSlash ) {
+ stream = classLoader.getResourceAsStream( stripped );
+ }
+ }
+
+ if ( stream == null ) {
+ stream = Environment.class.getClassLoader().getResourceAsStream( resource );
+ }
+ if ( stream == null && hasLeadingSlash ) {
+ stream = Environment.class.getClassLoader().getResourceAsStream( stripped );
+ }
+
+ if ( stream == null ) {
+ throw new HibernateException( resource + " not found" );
+ }
+
+ return stream;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/util/DTDEntityResolver.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/util/DTDEntityResolver.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/util/DTDEntityResolver.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,89 @@
+//$Id: DTDEntityResolver.java 10033 2006-06-21 06:23:30Z christian.bauer at jboss.com $
+//Contributed by Markus Meissner
+package org.hibernate.util;
+
+import java.io.InputStream;
+import java.io.Serializable;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+
+/**
+ * An {@link EntityResolver} implementation which attempts to resolve
+ * various systemId URLs to local classpath lookups<ol>
+ * <li>Any systemId URL beginning with <tt>http://hibernate.sourceforge.net/</tt> is
+ * searched for as a classpath resource in the classloader which loaded the
+ * Hibernate classes.</li>
+ * <li>Any systemId URL using <tt>classpath</tt> as the scheme (i.e. starting
+ * with <tt>classpath://</tt> is searched for as a classpath resource using first
+ * the current thread context classloader and then the classloader which loaded
+ * the Hibernate classes.
+ * </ol>
+ * <p/>
+ * Any entity references which cannot be resolved in relation to the above
+ * rules result in returning null, which should force the SAX reader to
+ * handle the entity reference in its default manner.
+ */
+public class DTDEntityResolver implements EntityResolver, Serializable {
+
+ private static final Log log = LogFactory.getLog( DTDEntityResolver.class );
+
+ private static final String HIBERNATE_NAMESPACE = "http://hibernate.sourceforge.net/";
+ private static final String USER_NAMESPACE = "classpath://";
+
+ public InputSource resolveEntity(String publicId, String systemId) {
+ if ( systemId != null ) {
+ log.debug( "trying to resolve system-id [" + systemId + "]" );
+ if ( systemId.startsWith( HIBERNATE_NAMESPACE ) ) {
+ log.debug( "recognized hibernate namespace; attempting to resolve on classpath under org/hibernate/" );
+ String path = "org/hibernate/" + systemId.substring( HIBERNATE_NAMESPACE.length() );
+ InputStream dtdStream = resolveInHibernateNamespace( path );
+ if ( dtdStream == null ) {
+ log.debug( "unable to locate [" + systemId + "] on classpath" );
+ if ( systemId.substring( HIBERNATE_NAMESPACE.length() ).indexOf( "2.0" ) > -1 ) {
+ log.error( "Don't use old DTDs, read the Hibernate 3.x Migration Guide!" );
+ }
+ }
+ else {
+ log.debug( "located [" + systemId + "] in classpath" );
+ InputSource source = new InputSource( dtdStream );
+ source.setPublicId( publicId );
+ source.setSystemId( systemId );
+ return source;
+ }
+ }
+ else if ( systemId.startsWith( USER_NAMESPACE ) ) {
+ log.debug( "recognized local namespace; attempting to resolve on classpath" );
+ String path = systemId.substring( USER_NAMESPACE.length() );
+ InputStream stream = resolveInLocalNamespace( path );
+ if ( stream == null ) {
+ log.debug( "unable to locate [" + systemId + "] on classpath" );
+ }
+ else {
+ log.debug( "located [" + systemId + "] in classpath" );
+ InputSource source = new InputSource( stream );
+ source.setPublicId( publicId );
+ source.setSystemId( systemId );
+ return source;
+ }
+ }
+ }
+ // use default behavior
+ return null;
+ }
+
+ protected InputStream resolveInHibernateNamespace(String path) {
+ return this.getClass().getClassLoader().getResourceAsStream( path );
+ }
+
+ protected InputStream resolveInLocalNamespace(String path) {
+ try {
+ return ConfigHelper.getUserResourceAsStream( path );
+ }
+ catch( Throwable t ) {
+ return null;
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/util/EmptyIterator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/util/EmptyIterator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/util/EmptyIterator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,27 @@
+//$Id: EmptyIterator.java 3890 2004-06-03 16:31:32Z steveebersole $
+package org.hibernate.util;
+
+import java.util.Iterator;
+
+/**
+ * @author Gavin King
+ */
+public final class EmptyIterator implements Iterator {
+
+ public static final Iterator INSTANCE = new EmptyIterator();
+
+ public boolean hasNext() {
+ return false;
+ }
+
+ public Object next() {
+ throw new UnsupportedOperationException();
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ private EmptyIterator() {}
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/util/EqualsHelper.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/util/EqualsHelper.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/util/EqualsHelper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,15 @@
+//$Id: EqualsHelper.java 4582 2004-09-25 11:22:20Z oneovthafew $
+package org.hibernate.util;
+
+/**
+ * @author Gavin King
+ */
+public final class EqualsHelper {
+
+ public static boolean equals(Object x, Object y) {
+ return x==y || ( x!=null && y!=null && x.equals(y) );
+ }
+
+ private EqualsHelper() {}
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/util/ExternalSessionFactoryConfig.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/util/ExternalSessionFactoryConfig.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/util/ExternalSessionFactoryConfig.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,374 @@
+// $Id: ExternalSessionFactoryConfig.java 10860 2006-11-22 00:02:55Z steve.ebersole at jboss.com $
+package org.hibernate.util;
+
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+
+import java.util.Properties;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.HashMap;
+import java.util.HashSet;
+
+/**
+ * Defines support for various externally configurable SessionFactory(s), for
+ * example, {@link org.hibernate.jmx.HibernateService JMX} or the JCA
+ * adapter.
+ *
+ * @author Steve Ebersole
+ */
+public abstract class ExternalSessionFactoryConfig {
+
+ private String mapResources;
+ private String dialect;
+ private String defaultSchema;
+ private String defaultCatalog;
+ private String maximumFetchDepth;
+ private String jdbcFetchSize;
+ private String jdbcBatchSize;
+ private String batchVersionedDataEnabled;
+ private String jdbcScrollableResultSetEnabled;
+ private String getGeneratedKeysEnabled;
+ private String streamsForBinaryEnabled;
+ private String reflectionOptimizationEnabled;
+ private String querySubstitutions;
+ private String showSqlEnabled;
+ private String commentsEnabled;
+ private String cacheProviderClass;
+ private String cacheProviderConfig;
+ private String cacheRegionPrefix;
+ private String secondLevelCacheEnabled;
+ private String minimalPutsEnabled;
+ private String queryCacheEnabled;
+
+ private Map additionalProperties;
+ private Set excludedPropertyNames = new HashSet();
+ private Map customListeners;
+
+
+ protected Set getExcludedPropertyNames() {
+ return excludedPropertyNames;
+ }
+
+ public final String getMapResources() {
+ return mapResources;
+ }
+
+ public final void setMapResources(String mapResources) {
+ this.mapResources = mapResources;
+ }
+
+ public void addMapResource(String mapResource) {
+ if ( mapResources==null || mapResources.length()==0 ) {
+ mapResources = mapResource.trim();
+ }
+ else {
+ mapResources += ", " + mapResource.trim();
+ }
+ }
+
+ public final String getDialect() {
+ return dialect;
+ }
+
+ public final void setDialect(String dialect) {
+ this.dialect = dialect;
+ }
+
+ public final String getDefaultSchema() {
+ return defaultSchema;
+ }
+
+ public final void setDefaultSchema(String defaultSchema) {
+ this.defaultSchema = defaultSchema;
+ }
+
+ public final String getDefaultCatalog() {
+ return defaultCatalog;
+ }
+
+ public final void setDefaultCatalog(String defaultCatalog) {
+ this.defaultCatalog = defaultCatalog;
+ }
+
+ public final String getMaximumFetchDepth() {
+ return maximumFetchDepth;
+ }
+
+ public final void setMaximumFetchDepth(String maximumFetchDepth) {
+ verifyInt( maximumFetchDepth );
+ this.maximumFetchDepth = maximumFetchDepth;
+ }
+
+ public final String getJdbcFetchSize() {
+ return jdbcFetchSize;
+ }
+
+ public final void setJdbcFetchSize(String jdbcFetchSize) {
+ verifyInt( jdbcFetchSize );
+ this.jdbcFetchSize = jdbcFetchSize;
+ }
+
+ public final String getJdbcBatchSize() {
+ return jdbcBatchSize;
+ }
+
+ public final void setJdbcBatchSize(String jdbcBatchSize) {
+ verifyInt( jdbcBatchSize );
+ this.jdbcBatchSize = jdbcBatchSize;
+ }
+
+ public final String getBatchVersionedDataEnabled() {
+ return batchVersionedDataEnabled;
+ }
+
+ public final void setBatchVersionedDataEnabled(String batchVersionedDataEnabled) {
+ this.batchVersionedDataEnabled = batchVersionedDataEnabled;
+ }
+
+ public final String getJdbcScrollableResultSetEnabled() {
+ return jdbcScrollableResultSetEnabled;
+ }
+
+ public final void setJdbcScrollableResultSetEnabled(String jdbcScrollableResultSetEnabled) {
+ this.jdbcScrollableResultSetEnabled = jdbcScrollableResultSetEnabled;
+ }
+
+ public final String getGetGeneratedKeysEnabled() {
+ return getGeneratedKeysEnabled;
+ }
+
+ public final void setGetGeneratedKeysEnabled(String getGeneratedKeysEnabled) {
+ this.getGeneratedKeysEnabled = getGeneratedKeysEnabled;
+ }
+
+ public final String getStreamsForBinaryEnabled() {
+ return streamsForBinaryEnabled;
+ }
+
+ public final void setStreamsForBinaryEnabled(String streamsForBinaryEnabled) {
+ this.streamsForBinaryEnabled = streamsForBinaryEnabled;
+ }
+
+ public final String getReflectionOptimizationEnabled() {
+ return reflectionOptimizationEnabled;
+ }
+
+ public final void setReflectionOptimizationEnabled(String reflectionOptimizationEnabled) {
+ this.reflectionOptimizationEnabled = reflectionOptimizationEnabled;
+ }
+
+ public final String getQuerySubstitutions() {
+ return querySubstitutions;
+ }
+
+ public final void setQuerySubstitutions(String querySubstitutions) {
+ this.querySubstitutions = querySubstitutions;
+ }
+
+ public final String getShowSqlEnabled() {
+ return showSqlEnabled;
+ }
+
+ public final void setShowSqlEnabled(String showSqlEnabled) {
+ this.showSqlEnabled = showSqlEnabled;
+ }
+
+ public final String getCommentsEnabled() {
+ return commentsEnabled;
+ }
+
+ public final void setCommentsEnabled(String commentsEnabled) {
+ this.commentsEnabled = commentsEnabled;
+ }
+
+ public final String getSecondLevelCacheEnabled() {
+ return secondLevelCacheEnabled;
+ }
+
+ public final void setSecondLevelCacheEnabled(String secondLevelCacheEnabled) {
+ this.secondLevelCacheEnabled = secondLevelCacheEnabled;
+ }
+
+ public final String getCacheProviderClass() {
+ return cacheProviderClass;
+ }
+
+ public final void setCacheProviderClass(String cacheProviderClass) {
+ this.cacheProviderClass = cacheProviderClass;
+ }
+
+ public String getCacheProviderConfig() {
+ return cacheProviderConfig;
+ }
+
+ public void setCacheProviderConfig(String cacheProviderConfig) {
+ this.cacheProviderConfig = cacheProviderConfig;
+ }
+
+ public final String getCacheRegionPrefix() {
+ return cacheRegionPrefix;
+ }
+
+ public final void setCacheRegionPrefix(String cacheRegionPrefix) {
+ this.cacheRegionPrefix = cacheRegionPrefix;
+ }
+
+ public final String getMinimalPutsEnabled() {
+ return minimalPutsEnabled;
+ }
+
+ public final void setMinimalPutsEnabled(String minimalPutsEnabled) {
+ this.minimalPutsEnabled = minimalPutsEnabled;
+ }
+
+ public final String getQueryCacheEnabled() {
+ return queryCacheEnabled;
+ }
+
+ public final void setQueryCacheEnabled(String queryCacheEnabled) {
+ this.queryCacheEnabled = queryCacheEnabled;
+ }
+
+ public final Map getCustomListeners() {
+ return customListeners;
+ }
+
+ public void setCustomListeners(Map customListeners) {
+ this.customListeners = customListeners;
+ }
+
+ public void setCustomListenersAsString(String customListenersString) {
+ // Note : expected in the syntax:
+ // type=listenerClass
+ // ({sep}type=listenerClass)*
+ // where {sep} is any whitespace or comma
+ if ( StringHelper.isNotEmpty( customListenersString) ) {
+ String[] listenerEntries = PropertiesHelper.toStringArray( customListenersString, " ,\n\t\r\f" );
+ for ( int i = 0; i < listenerEntries.length; i++ ) {
+ final int keyValueSepPosition = listenerEntries[i].indexOf( '=' );
+ final String type = listenerEntries[i].substring( 0, keyValueSepPosition );
+ final String listenerClass = listenerEntries[i].substring( keyValueSepPosition + 1 );
+ setCustomListener( type, listenerClass );
+ }
+ }
+ }
+
+ public void setCustomListener(String type, String listenerClass) {
+ if ( customListeners == null ) {
+ customListeners = new HashMap();
+ }
+ customListeners.put( type, listenerClass );
+ }
+
+ public final void addAdditionalProperty(String name, String value) {
+ if ( !getExcludedPropertyNames().contains( name ) ) {
+ if ( additionalProperties == null ) {
+ additionalProperties = new HashMap();
+ }
+ additionalProperties.put( name, value );
+ }
+ }
+
+ protected final Configuration buildConfiguration() {
+
+ Configuration cfg = new Configuration().setProperties( buildProperties() );
+
+
+ String[] mappingFiles = PropertiesHelper.toStringArray( mapResources, " ,\n\t\r\f" );
+ for ( int i = 0; i < mappingFiles.length; i++ ) {
+ cfg.addResource( mappingFiles[i] );
+ }
+
+ if ( customListeners != null && !customListeners.isEmpty() ) {
+ Iterator entries = customListeners.entrySet().iterator();
+ while ( entries.hasNext() ) {
+ final Map.Entry entry = ( Map.Entry ) entries.next();
+ final String type = ( String ) entry.getKey();
+ final Object value = entry.getValue();
+ if ( value != null ) {
+ if ( String.class.isAssignableFrom( value.getClass() ) ) {
+ // Its the listener class name
+ cfg.setListener( type, ( ( String ) value ) );
+ }
+ else {
+ // Its the listener instance (or better be)
+ cfg.setListener( type, value );
+ }
+ }
+ }
+ }
+
+ return cfg;
+ }
+
+ protected final Properties buildProperties() {
+ Properties props = new Properties();
+ setUnlessNull( props, Environment.DIALECT, dialect );
+ setUnlessNull( props, Environment.DEFAULT_SCHEMA, defaultSchema );
+ setUnlessNull( props, Environment.DEFAULT_CATALOG, defaultCatalog );
+ setUnlessNull( props, Environment.MAX_FETCH_DEPTH, maximumFetchDepth );
+ setUnlessNull( props, Environment.STATEMENT_FETCH_SIZE, jdbcFetchSize );
+ setUnlessNull( props, Environment.STATEMENT_BATCH_SIZE, jdbcBatchSize );
+ setUnlessNull( props, Environment.BATCH_VERSIONED_DATA, batchVersionedDataEnabled );
+ setUnlessNull( props, Environment.USE_SCROLLABLE_RESULTSET, jdbcScrollableResultSetEnabled );
+ setUnlessNull( props, Environment.USE_GET_GENERATED_KEYS, getGeneratedKeysEnabled );
+ setUnlessNull( props, Environment.USE_STREAMS_FOR_BINARY, streamsForBinaryEnabled );
+ setUnlessNull( props, Environment.USE_REFLECTION_OPTIMIZER, reflectionOptimizationEnabled );
+ setUnlessNull( props, Environment.QUERY_SUBSTITUTIONS, querySubstitutions );
+ setUnlessNull( props, Environment.SHOW_SQL, showSqlEnabled );
+ setUnlessNull( props, Environment.USE_SQL_COMMENTS, commentsEnabled );
+ setUnlessNull( props, Environment.CACHE_PROVIDER, cacheProviderClass );
+ setUnlessNull( props, Environment.CACHE_PROVIDER_CONFIG, cacheProviderConfig );
+ setUnlessNull( props, Environment.CACHE_REGION_PREFIX, cacheRegionPrefix );
+ setUnlessNull( props, Environment.USE_MINIMAL_PUTS, minimalPutsEnabled );
+ setUnlessNull( props, Environment.USE_SECOND_LEVEL_CACHE, secondLevelCacheEnabled );
+ setUnlessNull( props, Environment.USE_QUERY_CACHE, queryCacheEnabled );
+
+ Map extraProperties = getExtraProperties();
+ if ( extraProperties != null ) {
+ addAll( props, extraProperties );
+ }
+
+ if ( additionalProperties != null ) {
+ addAll( props, additionalProperties );
+ }
+
+ return props;
+ }
+
+ protected void addAll( Properties target, Map source ) {
+ Iterator itr = source.entrySet().iterator();
+ while ( itr.hasNext() ) {
+ final Map.Entry entry = ( Map.Entry ) itr.next();
+ final String propertyName = ( String ) entry.getKey();
+ final String propertyValue = ( String ) entry.getValue();
+ if ( propertyName != null && propertyValue != null ) {
+ // Make sure we don't override previous set values
+ if ( !target.keySet().contains( propertyName ) ) {
+ if ( !getExcludedPropertyNames().contains( propertyName) ) {
+ target.put( propertyName, propertyValue );
+ }
+ }
+ }
+ }
+ }
+
+ protected Map getExtraProperties() {
+ return null;
+ }
+
+ private void setUnlessNull(Properties props, String key, String value) {
+ if ( value != null ) {
+ props.setProperty( key, value );
+ }
+ }
+
+ private void verifyInt(String value)
+ {
+ if ( value != null ) {
+ Integer.parseInt( value );
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/util/FastHashMap.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/util/FastHashMap.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/util/FastHashMap.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,368 @@
+//$Id: FastHashMap.java 3890 2004-06-03 16:31:32Z steveebersole $
+/*
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2002 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ * any, must include the following acknowlegement:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowlegement may appear in the software itself,
+ * if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ * Foundation" must not be used to endorse or promote products derived
+ * from this software without prior written permission. For written
+ * permission, please contact apache at apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+// This class was taken from Apache commons-collections -
+// I made it final + removed the "slow" mode...
+
+package org.hibernate.util;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * <p>A customized implementation of <code>java.util.HashMap</code> designed
+ * to operate in a multithreaded environment where the large majority of
+ * method calls are read-only, instead of structural changes.
+ * Read calls are non-synchronized and write calls perform the
+ * following steps:</p>
+ * <ul>
+ * <li>Clone the existing collection
+ * <li>Perform the modification on the clone
+ * <li>Replace the existing collection with the (modified) clone
+ * </ul>
+ * <p><strong>NOTE</strong>: If you are creating and accessing a
+ * <code>HashMap</code> only within a single thread, you should use
+ * <code>java.util.HashMap</code> directly (with no synchronization), for
+ * maximum performance.</p>
+ *
+ */
+
+public final class FastHashMap implements Map, Serializable {
+
+ // ----------------------------------------------------------- Constructors
+
+ /**
+ * Construct a an empty map.
+ */
+ public FastHashMap() {
+
+ super();
+ this.map = new HashMap();
+
+ }
+
+ /**
+ * Construct an empty map with the specified capacity.
+ *
+ * @param capacity The initial capacity of the empty map
+ */
+ public FastHashMap(int capacity) {
+
+ super();
+ this.map = new HashMap(capacity);
+
+ }
+
+ /**
+ * Construct an empty map with the specified capacity and load factor.
+ *
+ * @param capacity The initial capacity of the empty map
+ * @param factor The load factor of the new map
+ */
+ public FastHashMap(int capacity, float factor) {
+
+ super();
+ this.map = new HashMap(capacity, factor);
+
+ }
+
+ /**
+ * Construct a new map with the same mappings as the specified map.
+ *
+ * @param map The map whose mappings are to be copied
+ */
+ public FastHashMap(Map map) {
+
+ super();
+ this.map = new HashMap(map);
+
+ }
+
+ // ----------------------------------------------------- Instance Variables
+
+ /**
+ * The underlying map we are managing.
+ */
+ private HashMap map = null;
+
+ // --------------------------------------------------------- Public Methods
+
+ /**
+ * Remove all mappings from this map.
+ */
+ public void clear() {
+
+ synchronized (this) {
+ HashMap temp = (HashMap) map.clone();
+ temp.clear();
+ map = temp;
+ }
+
+ }
+
+ /**
+ * Return a shallow copy of this <code>FastHashMap</code> instance.
+ * The keys and values themselves are not copied.
+ */
+ public Object clone() {
+
+ return new FastHashMap(map);
+
+ }
+
+ /**
+ * Return <code>true</code> if this map contains a mapping for the
+ * specified key.
+ *
+ * @param key Key to be searched for
+ */
+ public boolean containsKey(Object key) {
+
+ return map.containsKey(key);
+
+ }
+
+ /**
+ * Return <code>true</code> if this map contains one or more keys mapping
+ * to the specified value.
+ *
+ * @param value Value to be searched for
+ */
+ public boolean containsValue(Object value) {
+
+ return map.containsValue(value);
+
+ }
+
+ /**
+ * Return a collection view of the mappings contained in this map. Each
+ * element in the returned collection is a <code>Map.Entry</code>.
+ */
+ public Set entrySet() {
+
+ return map.entrySet();
+
+ }
+
+ /**
+ * Compare the specified object with this list for equality. This
+ * implementation uses exactly the code that is used to define the
+ * list equals function in the documentation for the
+ * <code>Map.equals</code> method.
+ *
+ * @param o Object to be compared to this list
+ */
+ public boolean equals(Object o) {
+
+ // Simple tests that require no synchronization
+ if (o == this)
+ return true;
+ else if ( !(o instanceof Map) )
+ return false;
+ Map mo = (Map) o;
+
+ // Compare the two maps for equality
+
+ if ( mo.size() != map.size() )
+ return false;
+ java.util.Iterator i = map.entrySet().iterator();
+ while ( i.hasNext() ) {
+ Map.Entry e = (Map.Entry) i.next();
+ Object key = e.getKey();
+ Object value = e.getValue();
+ if (value == null) {
+ if ( !( mo.get(key) == null && mo.containsKey(key) ) )
+ return false;
+ }
+ else {
+ if ( !value.equals( mo.get(key) ) )
+ return false;
+ }
+ }
+ return true;
+
+ }
+
+ /**
+ * Return the value to which this map maps the specified key. Returns
+ * <code>null</code> if the map contains no mapping for this key, or if
+ * there is a mapping with a value of <code>null</code>. Use the
+ * <code>containsKey()</code> method to disambiguate these cases.
+ *
+ * @param key Key whose value is to be returned
+ */
+ public Object get(Object key) {
+
+ return map.get(key);
+
+ }
+
+ /**
+ * Return the hash code value for this map. This implementation uses
+ * exactly the code that is used to define the list hash function in the
+ * documentation for the <code>Map.hashCode</code> method.
+ */
+ public int hashCode() {
+
+ int h = 0;
+ java.util.Iterator i = map.entrySet().iterator();
+ while ( i.hasNext() )
+ h += i.next().hashCode();
+ return h;
+
+ }
+
+ /**
+ * Return <code>true</code> if this map contains no mappings.
+ */
+ public boolean isEmpty() {
+
+ return map.isEmpty();
+
+ }
+
+ /**
+ * Return a set view of the keys contained in this map.
+ */
+ public Set keySet() {
+
+ return map.keySet();
+
+ }
+
+ /**
+ * Associate the specified value with the specified key in this map.
+ * If the map previously contained a mapping for this key, the old
+ * value is replaced and returned.
+ *
+ * @param key The key with which the value is to be associated
+ * @param value The value to be associated with this key
+ */
+ public Object put(Object key, Object value) {
+
+ synchronized (this) {
+ HashMap temp = (HashMap) map.clone();
+ Object result = temp.put(key, value);
+ map = temp;
+ return (result);
+ }
+
+ }
+
+ /**
+ * Copy all of the mappings from the specified map to this one, replacing
+ * any mappings with the same keys.
+ *
+ * @param in Map whose mappings are to be copied
+ */
+ public void putAll(Map in) {
+
+ synchronized (this) {
+ HashMap temp = (HashMap) map.clone();
+ temp.putAll(in);
+ map = temp;
+ }
+
+ }
+
+ /**
+ * Remove any mapping for this key, and return any previously
+ * mapped value.
+ *
+ * @param key Key whose mapping is to be removed
+ */
+ public Object remove(Object key) {
+
+ synchronized (this) {
+ HashMap temp = (HashMap) map.clone();
+ Object result = temp.remove(key);
+ map = temp;
+ return (result);
+ }
+
+ }
+
+ /**
+ * Return the number of key-value mappings in this map.
+ */
+ public int size() {
+
+ return map.size();
+
+ }
+
+ /**
+ * Return a collection view of the values contained in this map.
+ */
+ public Collection values() {
+
+ return map.values();
+
+ }
+
+ public String toString() { return map.toString(); }
+
+}
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/util/FilterHelper.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/util/FilterHelper.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/util/FilterHelper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,81 @@
+// $Id: FilterHelper.java 9908 2006-05-08 20:59:20Z max.andersen at jboss.com $
+package org.hibernate.util;
+
+import org.hibernate.sql.Template;
+import org.hibernate.impl.FilterImpl;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.function.SQLFunctionRegistry;
+
+import java.util.Map;
+import java.util.Iterator;
+
+/**
+ * Implementation of FilterHelper.
+ *
+ * @author Steve Ebersole
+ */
+public class FilterHelper {
+
+ private final String[] filterNames;
+ private final String[] filterConditions;
+
+ /**
+ * The map of defined filters. This is expected to be in format
+ * where the filter names are the map keys, and the defined
+ * conditions are the values.
+ *
+ * @param filters The map of defined filters.
+ * @param dialect The sql dialect
+ * @param functionRegistry The SQL function registry
+ */
+ public FilterHelper(Map filters, Dialect dialect, SQLFunctionRegistry functionRegistry) {
+ int filterCount = filters.size();
+ filterNames = new String[filterCount];
+ filterConditions = new String[filterCount];
+ Iterator iter = filters.entrySet().iterator();
+ filterCount = 0;
+ while ( iter.hasNext() ) {
+ final Map.Entry entry = (Map.Entry) iter.next();
+ filterNames[filterCount] = (String) entry.getKey();
+ filterConditions[filterCount] = Template.renderWhereStringTemplate(
+ (String) entry.getValue(),
+ FilterImpl.MARKER,
+ dialect,
+ functionRegistry
+ );
+ filterConditions[filterCount] = StringHelper.replace( filterConditions[filterCount],
+ ":",
+ ":" + filterNames[filterCount] + "." );
+ filterCount++;
+ }
+ }
+
+ public boolean isAffectedBy(Map enabledFilters) {
+ for ( int i = 0, max = filterNames.length; i < max; i++ ) {
+ if ( enabledFilters.containsKey( filterNames[i] ) ) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public String render(String alias, Map enabledFilters) {
+ StringBuffer buffer = new StringBuffer();
+ render( buffer, alias, enabledFilters );
+ return buffer.toString();
+ }
+
+ public void render(StringBuffer buffer, String alias, Map enabledFilters) {
+ if ( filterNames != null && filterNames.length > 0 ) {
+ for ( int i = 0, max = filterNames.length; i < max; i++ ) {
+ if ( enabledFilters.containsKey( filterNames[i] ) ) {
+ final String condition = filterConditions[i];
+ if ( StringHelper.isNotEmpty( condition ) ) {
+ buffer.append( " and " )
+ .append( StringHelper.replace( condition, FilterImpl.MARKER, alias ) );
+ }
+ }
+ }
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/util/GetGeneratedKeysHelper.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/util/GetGeneratedKeysHelper.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/util/GetGeneratedKeysHelper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,87 @@
+//$Id: GetGeneratedKeysHelper.java 9676 2006-03-22 17:38:55Z steve.ebersole at jboss.com $
+package org.hibernate.util;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import org.hibernate.AssertionFailure;
+
+/**
+ * @author Gavin King
+ */
+public final class GetGeneratedKeysHelper {
+
+ private GetGeneratedKeysHelper() {
+ }
+
+ private static final Integer RETURN_GENERATED_KEYS;
+ private static final Method PREPARE_STATEMENT_METHOD;
+ private static final Method GET_GENERATED_KEYS_METHOD;
+
+ static {
+ try {
+ int returnGeneratedKeysEnumValue = Statement.class
+ .getDeclaredField( "RETURN_GENERATED_KEYS" )
+ .getInt( PreparedStatement.class );
+ RETURN_GENERATED_KEYS = new Integer( returnGeneratedKeysEnumValue );
+ PREPARE_STATEMENT_METHOD = Connection.class.getMethod(
+ "prepareStatement",
+ new Class[] { String.class, Integer.TYPE }
+ );
+ GET_GENERATED_KEYS_METHOD = Statement.class.getDeclaredMethod(
+ "getGeneratedKeys",
+ null
+ );
+ }
+ catch ( Exception e ) {
+ throw new AssertionFailure( "could not initialize getGeneratedKeys() support", e );
+ }
+ }
+
+ public static PreparedStatement prepareStatement(Connection conn, String sql) throws SQLException {
+ Object[] args = new Object[] { sql, RETURN_GENERATED_KEYS } ;
+ try {
+ return ( PreparedStatement ) PREPARE_STATEMENT_METHOD.invoke( conn, args );
+ }
+ catch ( InvocationTargetException ite ) {
+ if ( ite.getTargetException() instanceof SQLException ) {
+ throw ( SQLException ) ite.getTargetException();
+ }
+ else if ( ite.getTargetException() instanceof RuntimeException ) {
+ throw ( RuntimeException ) ite.getTargetException();
+ }
+ else {
+ throw new AssertionFailure( "InvocationTargetException preparing statement capable of returning generated keys (JDBC3)", ite );
+ }
+ }
+ catch ( IllegalAccessException iae ) {
+ throw new AssertionFailure( "IllegalAccessException preparing statement capable of returning generated keys (JDBC3)", iae );
+ }
+ }
+
+ public static ResultSet getGeneratedKey(PreparedStatement ps) throws SQLException {
+ try {
+ return ( ResultSet ) GET_GENERATED_KEYS_METHOD.invoke( ps, null );
+ }
+ catch ( InvocationTargetException ite ) {
+ if ( ite.getTargetException() instanceof SQLException ) {
+ throw ( SQLException ) ite.getTargetException();
+ }
+ else if ( ite.getTargetException() instanceof RuntimeException ) {
+ throw ( RuntimeException ) ite.getTargetException();
+ }
+ else {
+ throw new AssertionFailure( "InvocationTargetException extracting generated keys (JDBC3)", ite );
+ }
+ }
+ catch ( IllegalAccessException iae ) {
+ throw new AssertionFailure( "IllegalAccessException extracting generated keys (JDBC3)", iae );
+ }
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/util/IdentityMap.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/util/IdentityMap.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/util/IdentityMap.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,264 @@
+//$Id: IdentityMap.java 9194 2006-02-01 19:59:07Z steveebersole $
+package org.hibernate.util;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.collections.SequencedHashMap;
+
+/**
+ * A <tt>Map</tt> where keys are compared by object identity,
+ * rather than <tt>equals()</tt>.
+ */
+
+public final class IdentityMap implements Map {
+
+ private final Map map;
+ private transient Map.Entry[] entryArray = new Map.Entry[0];
+ private transient boolean dirty = false;
+
+ /**
+ * Return a new instance of this class, with an undefined
+ * iteration order
+ *
+ * @return Map
+ */
+ public static Map instantiate(int size) {
+ return new IdentityMap( new HashMap(size) );
+ }
+
+ /**
+ * Return a new instance of this class, with iteration
+ * order defined by the order that entries were added
+ */
+ public static Map instantiateSequenced(int size) {
+ return new IdentityMap( new SequencedHashMap(size) );
+ }
+
+ private IdentityMap(Map underlyingMap) {
+ map = underlyingMap;
+ dirty = true;
+ }
+
+ /**
+ * Return the map entries (as instances of <tt>Map.Entry</tt> in a collection that
+ * is safe from concurrent modification). ie. we may safely add new instances to
+ * the underlying <tt>Map</tt> during iteration of the <tt>entries()</tt>.
+ *
+ * @param map
+ * @return Collection
+ */
+ public static Map.Entry[] concurrentEntries(Map map) {
+ return ( (IdentityMap) map ).entryArray();
+ }
+
+ public static List entries(Map map) {
+ return ( (IdentityMap) map ).entryList();
+ }
+
+ public static Iterator keyIterator(Map map) {
+ return ( (IdentityMap) map ).keyIterator();
+ }
+
+ public Iterator keyIterator() {
+ return new KeyIterator( map.keySet().iterator() );
+ }
+
+ public static final class IdentityMapEntry implements java.util.Map.Entry {
+ IdentityMapEntry(Object key, Object value) {
+ this.key=key;
+ this.value=value;
+ }
+ private Object key;
+ private Object value;
+ public Object getKey() {
+ return key;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ public Object setValue(Object value) {
+ Object result = this.value;
+ this.value = value;
+ return result;
+ }
+ }
+
+ public static final class IdentityKey implements Serializable {
+ private Object key;
+
+ IdentityKey(Object key) {
+ this.key=key;
+ }
+ public boolean equals(Object other) {
+ return key == ( (IdentityKey) other ).key;
+ }
+ public int hashCode() {
+ return System.identityHashCode(key);
+ }
+ public String toString() {
+ return key.toString();
+ }
+ public Object getRealKey() {
+ return key;
+ }
+ }
+
+ public int size() {
+ return map.size();
+ }
+
+ public boolean isEmpty() {
+ return map.isEmpty();
+ }
+
+ public boolean containsKey(Object key) {
+ IdentityKey k = new IdentityKey(key);
+ return map.containsKey(k);
+ }
+
+ public boolean containsValue(Object val) {
+ return map.containsValue(val);
+ }
+
+ public Object get(Object key) {
+ IdentityKey k = new IdentityKey(key);
+ return map.get(k);
+ }
+
+ public Object put(Object key, Object value) {
+ dirty = true;
+ return map.put( new IdentityKey(key), value );
+ }
+
+ public Object remove(Object key) {
+ dirty = true;
+ IdentityKey k = new IdentityKey(key);
+ return map.remove(k);
+ }
+
+ public void putAll(Map otherMap) {
+ Iterator iter = otherMap.entrySet().iterator();
+ while ( iter.hasNext() ) {
+ Map.Entry me = (Map.Entry) iter.next();
+ put( me.getKey(), me.getValue() );
+ }
+ }
+
+ public void clear() {
+ dirty = true;
+ entryArray = null;
+ map.clear();
+ }
+
+ public Set keySet() {
+ // would need an IdentitySet for this!
+ throw new UnsupportedOperationException();
+ }
+
+ public Collection values() {
+ return map.values();
+ }
+
+ public Set entrySet() {
+ Set set = new HashSet( map.size() );
+ Iterator iter = map.entrySet().iterator();
+ while ( iter.hasNext() ) {
+ Map.Entry me = (Map.Entry) iter.next();
+ set.add( new IdentityMapEntry( ( (IdentityKey) me.getKey() ).key, me.getValue() ) );
+ }
+ return set;
+ }
+
+ public List entryList() {
+ ArrayList list = new ArrayList( map.size() );
+ Iterator iter = map.entrySet().iterator();
+ while ( iter.hasNext() ) {
+ Map.Entry me = (Map.Entry) iter.next();
+ list.add( new IdentityMapEntry( ( (IdentityKey) me.getKey() ).key, me.getValue() ) );
+ }
+ return list;
+ }
+
+ public Map.Entry[] entryArray() {
+ if (dirty) {
+ entryArray = new Map.Entry[ map.size() ];
+ Iterator iter = map.entrySet().iterator();
+ int i=0;
+ while ( iter.hasNext() ) {
+ Map.Entry me = (Map.Entry) iter.next();
+ entryArray[i++] = new IdentityMapEntry( ( (IdentityKey) me.getKey() ).key, me.getValue() );
+ }
+ dirty = false;
+ }
+ return entryArray;
+ }
+
+ /**
+ * Workaround for a JDK 1.4.1 bug where <tt>IdentityHashMap</tt>s are not
+ * correctly deserialized.
+ *
+ * @param map
+ * @return Object
+ */
+ public static Object serialize(Map map) {
+ return ( (IdentityMap) map ).map;
+ }
+
+ /**
+ * Workaround for a JDK 1.4.1 bug where <tt>IdentityHashMap</tt>s are not
+ * correctly deserialized.
+ *
+ * @param o
+ * @return Map
+ */
+ public static Map deserialize(Object o) {
+ return new IdentityMap( (Map) o );
+ }
+
+ public String toString() {
+ return map.toString();
+ }
+
+ public static Map invert(Map map) {
+ Map result = instantiate( map.size() );
+ Iterator iter = map.entrySet().iterator();
+ while ( iter.hasNext() ) {
+ Map.Entry me = (Map.Entry) iter.next();
+ result.put( me.getValue(), me.getKey() );
+ }
+ return result;
+ }
+
+ static final class KeyIterator implements Iterator {
+
+ private KeyIterator(Iterator iter) {
+ identityKeyIterator = iter;
+ }
+
+ private final Iterator identityKeyIterator;
+
+ public boolean hasNext() {
+ return identityKeyIterator.hasNext();
+ }
+
+ public Object next() {
+ return ( (IdentityKey) identityKeyIterator.next() ).key;
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ }
+
+}
\ No newline at end of file
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/util/IdentitySet.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/util/IdentitySet.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/util/IdentitySet.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,89 @@
+//$Id: IdentitySet.java 8807 2005-12-09 15:27:05Z epbernard $
+package org.hibernate.util;
+
+import java.util.Set;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Set implementation that use == instead of equals() as its comparison mechanism
+ * that base its implementation of IdentityMap
+ *
+ * @author Emmanuel Bernard
+ */
+public class IdentitySet implements Set {
+ private Map map;
+ private static final Object DUMP_VALUE = new Object();
+
+ public IdentitySet() {
+ this.map = IdentityMap.instantiate( 10 );
+ }
+
+ public int size() {
+ return map.size();
+ }
+
+ public boolean isEmpty() {
+ return map.isEmpty();
+ }
+
+ public boolean contains(Object o) {
+ return map.get( o ) == DUMP_VALUE;
+ }
+
+ public Iterator iterator() {
+ return map.entrySet().iterator();
+ }
+
+ public Object[] toArray() {
+ return map.entrySet().toArray();
+ }
+
+ public Object[] toArray(Object[] a) {
+ return map.entrySet().toArray(a);
+ }
+
+ public boolean add(Object o) {
+ return map.put( o, DUMP_VALUE) == null;
+ }
+
+ public boolean remove(Object o) {
+ return map.remove( o ) == DUMP_VALUE;
+ }
+
+ public boolean containsAll(Collection c) {
+ Iterator it = c.iterator();
+ while ( it.hasNext() ) {
+ if ( ! map.containsKey( it.next() ) ) return false;
+ }
+ return true;
+ }
+
+ public boolean addAll(Collection c) {
+ Iterator it = c.iterator();
+ boolean changed = false;
+ while ( it.hasNext() ) {
+ if ( this.add( it.next() ) ) changed = true;
+ }
+ return changed;
+ }
+
+ public boolean retainAll(Collection c) {
+ //doable if needed
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean removeAll(Collection c) {
+ Iterator it = c.iterator();
+ boolean changed = false;
+ while ( it.hasNext() ) {
+ if ( this.remove( it.next() ) ) changed = true;
+ }
+ return changed;
+ }
+
+ public void clear() {
+ map.clear();
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/util/JDBCExceptionReporter.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/util/JDBCExceptionReporter.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/util/JDBCExceptionReporter.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,103 @@
+//$Id: JDBCExceptionReporter.java 10670 2006-10-31 21:30:15Z epbernard $
+package org.hibernate.util;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+public final class JDBCExceptionReporter {
+
+ public static final Log log = LogFactory.getLog(JDBCExceptionReporter.class);
+ public static final String DEFAULT_EXCEPTION_MSG = "SQL Exception";
+ public static final String DEFAULT_WARNING_MSG = "SQL Warning";
+
+ private JDBCExceptionReporter() {}
+
+ public static void logAndClearWarnings(Connection connection) {
+ if ( log.isWarnEnabled() ) {
+ try {
+ logWarnings( connection.getWarnings() );
+ }
+ catch (SQLException sqle) {
+ //workaround for WebLogic
+ log.debug("could not log warnings", sqle);
+ }
+ }
+ try {
+ //Sybase fail if we don't do that, sigh...
+ connection.clearWarnings();
+ }
+ catch (SQLException sqle) {
+ log.debug("could not clear warnings", sqle);
+ }
+ }
+
+ public static void logWarnings(SQLWarning warning) {
+ logWarnings(warning, null);
+ }
+
+ public static void logWarnings(SQLWarning warning, String message) {
+ if ( log.isWarnEnabled() ) {
+ if ( log.isDebugEnabled() && warning != null ) {
+ message = StringHelper.isNotEmpty(message) ? message : DEFAULT_WARNING_MSG;
+ log.debug( message, warning );
+ }
+ while (warning != null) {
+ StringBuffer buf = new StringBuffer(30)
+ .append( "SQL Warning: ")
+ .append( warning.getErrorCode() )
+ .append( ", SQLState: ")
+ .append( warning.getSQLState() );
+ log.warn( buf.toString() );
+ log.warn( warning.getMessage() );
+ warning = warning.getNextWarning();
+ }
+ }
+ }
+
+ public static void logExceptions(SQLException ex) {
+ logExceptions(ex, null);
+ }
+
+ public static void logExceptions(SQLException ex, String message) {
+ if ( log.isErrorEnabled() ) {
+ if ( log.isDebugEnabled() ) {
+ message = StringHelper.isNotEmpty(message) ? message : DEFAULT_EXCEPTION_MSG;
+ log.debug( message, ex );
+ }
+ while (ex != null) {
+ StringBuffer buf = new StringBuffer(30)
+ .append( "SQL Error: " )
+ .append( ex.getErrorCode() )
+ .append( ", SQLState: " )
+ .append( ex.getSQLState() );
+ log.warn( buf.toString() );
+ log.error( ex.getMessage() );
+ ex = ex.getNextException();
+ }
+ }
+ }
+
+// public static JDBCException newJDBCException(String string, SQLException root, String sql) {
+// string = string + " [" + sql + ']';
+// log.error(string, root);
+// logExceptions(root);
+// return new JDBCException(string, root, sql);
+// }
+//
+// public static JDBCException newJDBCException(String string, SQLException root) {
+// log.error(string, root);
+// logExceptions(root);
+// return new JDBCException(string, root);
+// }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/util/JTAHelper.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/util/JTAHelper.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/util/JTAHelper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,54 @@
+//$Id: JTAHelper.java 10068 2006-06-28 17:07:06Z steve.ebersole at jboss.com $
+package org.hibernate.util;
+
+import javax.transaction.Status;
+import javax.transaction.SystemException;
+import javax.transaction.TransactionManager;
+
+import org.hibernate.TransactionException;
+import org.hibernate.engine.SessionFactoryImplementor;
+
+/**
+ * @author Gavin King
+ */
+public final class JTAHelper {
+
+ private JTAHelper() {}
+
+ public static boolean isRollback(int status) {
+ return status==Status.STATUS_MARKED_ROLLBACK ||
+ status==Status.STATUS_ROLLING_BACK ||
+ status==Status.STATUS_ROLLEDBACK;
+ }
+
+ public static boolean isInProgress(int status) {
+ return status==Status.STATUS_ACTIVE ||
+ status==Status.STATUS_MARKED_ROLLBACK;
+ }
+
+ /**
+ * Return true if a JTA transaction is in progress
+ * and false in *every* other cases (including in a JDBC transaction).
+ */
+ public static boolean isTransactionInProgress(SessionFactoryImplementor factory) {
+ TransactionManager tm = factory.getTransactionManager();
+ try {
+ return tm != null && isTransactionInProgress( tm.getTransaction() );
+ }
+ catch (SystemException se) {
+ throw new TransactionException( "could not obtain JTA Transaction", se );
+ }
+ }
+
+ public static boolean isTransactionInProgress(javax.transaction.Transaction tx) throws SystemException {
+ return tx != null && JTAHelper.isInProgress( tx.getStatus() );
+ }
+
+ public static boolean isMarkedForRollback(int status) {
+ return status == Status.STATUS_MARKED_ROLLBACK;
+ }
+
+ public static boolean isMarkedForRollback(javax.transaction.Transaction tx) throws SystemException {
+ return isMarkedForRollback( tx.getStatus() );
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/util/JTAHelper.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/util/JoinedIterator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/util/JoinedIterator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/util/JoinedIterator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,84 @@
+//$Id: JoinedIterator.java 3973 2004-06-28 23:58:08Z epbernard $
+package org.hibernate.util;
+
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * An JoinedIterator is an Iterator that wraps a number of Iterators.
+ *
+ * This class makes multiple iterators look like one to the caller.
+ * When any method from the Iterator interface is called, the JoinedIterator
+ * will delegate to a single underlying Iterator. The JoinedIterator will
+ * invoke the Iterators in sequence until all Iterators are exhausted.
+ *
+ */
+public class JoinedIterator implements Iterator {
+
+ private static final Iterator[] ITERATORS = {};
+
+ // wrapped iterators
+ private Iterator[] iterators;
+
+ // index of current iterator in the wrapped iterators array
+ private int currentIteratorIndex;
+
+ // the current iterator
+ private Iterator currentIterator;
+
+ // the last used iterator
+ private Iterator lastUsedIterator;
+
+ public JoinedIterator(List iterators) {
+ this( (Iterator[]) iterators.toArray(ITERATORS) );
+ }
+
+ public JoinedIterator(Iterator[] iterators) {
+ if( iterators==null )
+ throw new NullPointerException("Unexpected NULL iterators argument");
+ this.iterators = iterators;
+ }
+
+ public JoinedIterator(Iterator first, Iterator second) {
+ this( new Iterator[] { first, second } );
+ }
+
+ public boolean hasNext() {
+ updateCurrentIterator();
+ return currentIterator.hasNext();
+ }
+
+ public Object next() {
+ updateCurrentIterator();
+ return currentIterator.next();
+ }
+
+ public void remove() {
+ updateCurrentIterator();
+ lastUsedIterator.remove();
+ }
+
+
+ // call this before any Iterator method to make sure that the current Iterator
+ // is not exhausted
+ protected void updateCurrentIterator() {
+
+ if (currentIterator == null) {
+ if( iterators.length==0 ) {
+ currentIterator = EmptyIterator.INSTANCE;
+ }
+ else {
+ currentIterator = iterators[0];
+ }
+ // set last used iterator here, in case the user calls remove
+ // before calling hasNext() or next() (although they shouldn't)
+ lastUsedIterator = currentIterator;
+ }
+
+ while (! currentIterator.hasNext() && currentIteratorIndex < iterators.length - 1) {
+ currentIteratorIndex++;
+ currentIterator = iterators[currentIteratorIndex];
+ }
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/util/LazyIterator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/util/LazyIterator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/util/LazyIterator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,35 @@
+//$Id: LazyIterator.java 7699 2005-07-30 04:56:09Z oneovthafew $
+package org.hibernate.util;
+
+import java.util.Iterator;
+import java.util.Map;
+
+public final class LazyIterator implements Iterator {
+
+ private final Map map;
+ private Iterator iterator;
+
+ private Iterator getIterator() {
+ if (iterator==null) {
+ iterator = map.values().iterator();
+ }
+ return iterator;
+ }
+
+ public LazyIterator(Map map) {
+ this.map = map;
+ }
+
+ public boolean hasNext() {
+ return getIterator().hasNext();
+ }
+
+ public Object next() {
+ return getIterator().next();
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/util/LazyIterator.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/util/LinkedHashCollectionHelper.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/util/LinkedHashCollectionHelper.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/util/LinkedHashCollectionHelper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,95 @@
+//$Id: LinkedHashCollectionHelper.java 10100 2006-07-10 16:31:09Z steve.ebersole at jboss.com $
+package org.hibernate.util;
+
+import java.util.Map;
+import java.util.Set;
+import java.lang.reflect.Constructor;
+
+import org.hibernate.AssertionFailure;
+
+public final class LinkedHashCollectionHelper {
+
+ private static final Class SET_CLASS;
+ private static final Class MAP_CLASS;
+ private static final Class[] CAPACITY_CTOR_SIG = new Class[] { int.class, float.class };
+ private static final Constructor SET_CAPACITY_CTOR;
+ private static final Constructor MAP_CAPACITY_CTOR;
+ private static final float LOAD_FACTOR_V = .75f;
+ private static final Float LOAD_FACTOR = new Float( LOAD_FACTOR_V );
+
+ static {
+ Class setClass;
+ Class mapClass;
+ Constructor setCtor;
+ Constructor mapCtor;
+ try {
+ setClass = Class.forName( "java.util.LinkedHashSet" );
+ mapClass = Class.forName( "java.util.LinkedHashMap" );
+ setCtor = setClass.getConstructor( CAPACITY_CTOR_SIG );
+ mapCtor = mapClass.getConstructor( CAPACITY_CTOR_SIG );
+ }
+ catch ( Throwable t ) {
+ setClass = null;
+ mapClass = null;
+ setCtor = null;
+ mapCtor = null;
+ }
+ SET_CLASS = setClass;
+ MAP_CLASS = mapClass;
+ SET_CAPACITY_CTOR = setCtor;
+ MAP_CAPACITY_CTOR = mapCtor;
+ }
+
+ public static Set createLinkedHashSet() {
+ try {
+ return (Set) SET_CLASS.newInstance();
+ }
+ catch (Exception e) {
+ throw new AssertionFailure("Could not instantiate LinkedHashSet", e);
+ }
+ }
+
+ public static Set createLinkedHashSet(int anticipatedSize) {
+ if ( anticipatedSize <= 0 ) {
+ return createLinkedHashSet();
+ }
+ int initialCapacity = anticipatedSize + (int)( anticipatedSize * LOAD_FACTOR_V );
+ try {
+ return ( Set ) SET_CAPACITY_CTOR.newInstance( new Object[] { new Integer( initialCapacity ), LOAD_FACTOR } );
+ }
+ catch (Exception e) {
+ throw new AssertionFailure("Could not instantiate LinkedHashSet", e);
+ }
+ }
+
+ public static Map createLinkedHashMap() {
+ try {
+ return (Map) MAP_CLASS.newInstance();
+ }
+ catch (Exception e) {
+ throw new AssertionFailure("Could not instantiate LinkedHashMap", e);
+ }
+ }
+
+ public static Map createLinkedHashMap(int anticipatedSize) {
+ if ( anticipatedSize <= 0 ) {
+ return createLinkedHashMap();
+ }
+ int initialCapacity = anticipatedSize + (int)( anticipatedSize * LOAD_FACTOR_V );
+ try {
+ return ( Map ) MAP_CAPACITY_CTOR.newInstance( new Object[] { new Integer( initialCapacity ), LOAD_FACTOR } );
+ }
+ catch (Exception e) {
+ throw new AssertionFailure("Could not instantiate LinkedHashMap", e);
+ }
+ }
+
+ private LinkedHashCollectionHelper() {}
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/util/MarkerObject.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/util/MarkerObject.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/util/MarkerObject.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,16 @@
+//$Id: MarkerObject.java 4649 2004-10-07 00:14:29Z oneovthafew $
+package org.hibernate.util;
+
+/**
+ * @author Gavin King
+ */
+public class MarkerObject {
+ private String name;
+
+ public MarkerObject(String name) {
+ this.name=name;
+ }
+ public String toString() {
+ return name;
+ }
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/util/MarkerObject.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/util/NamedGeneratedKeysHelper.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/util/NamedGeneratedKeysHelper.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/util/NamedGeneratedKeysHelper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,79 @@
+package org.hibernate.util;
+
+import org.hibernate.AssertionFailure;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+import java.sql.Connection;
+import java.sql.Statement;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.ResultSet;
+
+/**
+ * @author Steve Ebersole
+ */
+public class NamedGeneratedKeysHelper {
+ private NamedGeneratedKeysHelper() {
+ }
+
+ private static final Method PREPARE_STATEMENT_METHOD;
+ private static final Method GET_GENERATED_KEYS_METHOD;
+
+ static {
+ try {
+ PREPARE_STATEMENT_METHOD = Connection.class.getMethod(
+ "prepareStatement",
+ new Class[] { String.class, String[].class }
+ );
+ GET_GENERATED_KEYS_METHOD = Statement.class.getDeclaredMethod(
+ "getGeneratedKeys",
+ null
+ );
+ }
+ catch ( Exception e ) {
+ throw new AssertionFailure( "could not initialize getGeneratedKeys() support", e );
+ }
+ }
+
+ public static PreparedStatement prepareStatement(Connection conn, String sql, String[] columnNames) throws SQLException {
+ Object[] args = new Object[] { sql, columnNames } ;
+ try {
+ return ( PreparedStatement ) PREPARE_STATEMENT_METHOD.invoke( conn, args );
+ }
+ catch ( InvocationTargetException ite ) {
+ if ( ite.getTargetException() instanceof SQLException ) {
+ throw ( SQLException ) ite.getTargetException();
+ }
+ else if ( ite.getTargetException() instanceof RuntimeException ) {
+ throw ( RuntimeException ) ite.getTargetException();
+ }
+ else {
+ throw new AssertionFailure( "InvocationTargetException preparing statement capable of returning generated keys (JDBC3)", ite );
+ }
+ }
+ catch ( IllegalAccessException iae ) {
+ throw new AssertionFailure( "IllegalAccessException preparing statement capable of returning generated keys (JDBC3)", iae );
+ }
+ }
+
+ public static ResultSet getGeneratedKey(PreparedStatement ps) throws SQLException {
+ try {
+ return ( ResultSet ) GET_GENERATED_KEYS_METHOD.invoke( ps, null );
+ }
+ catch ( InvocationTargetException ite ) {
+ if ( ite.getTargetException() instanceof SQLException ) {
+ throw ( SQLException ) ite.getTargetException();
+ }
+ else if ( ite.getTargetException() instanceof RuntimeException ) {
+ throw ( RuntimeException ) ite.getTargetException();
+ }
+ else {
+ throw new AssertionFailure( "InvocationTargetException extracting generated keys (JDBC3)", ite );
+ }
+ }
+ catch ( IllegalAccessException iae ) {
+ throw new AssertionFailure( "IllegalAccessException extracting generated keys (JDBC3)", iae );
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/util/NamingHelper.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/util/NamingHelper.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/util/NamingHelper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,121 @@
+//$Id: NamingHelper.java 8149 2005-09-11 21:10:52Z oneovthafew $
+package org.hibernate.util;
+
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Properties;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.Name;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.hibernate.cfg.Environment;
+
+public final class NamingHelper {
+
+ private static final Log log = LogFactory.getLog(NamingHelper.class);
+
+ public static InitialContext getInitialContext(Properties props) throws NamingException {
+
+ Hashtable hash = getJndiProperties(props);
+ log.info("JNDI InitialContext properties:" + hash);
+ try {
+ return hash.size()==0 ?
+ new InitialContext() :
+ new InitialContext(hash);
+ }
+ catch (NamingException e) {
+ log.error("Could not obtain initial context", e);
+ throw e;
+ }
+ }
+
+ /**
+ * Bind val to name in ctx, and make sure that all intermediate contexts exist.
+ *
+ * @param ctx the root context
+ * @param name the name as a string
+ * @param val the object to be bound
+ * @throws NamingException
+ */
+ public static void bind(Context ctx, String name, Object val) throws NamingException {
+ try {
+ log.trace("binding: " + name);
+ ctx.rebind(name, val);
+ }
+ catch (Exception e) {
+ Name n = ctx.getNameParser("").parse(name);
+ while ( n.size() > 1 ) {
+ String ctxName = n.get(0);
+
+ Context subctx=null;
+ try {
+ log.trace("lookup: " + ctxName);
+ subctx = (Context) ctx.lookup(ctxName);
+ }
+ catch (NameNotFoundException nfe) {}
+
+ if (subctx!=null) {
+ log.debug("Found subcontext: " + ctxName);
+ ctx = subctx;
+ }
+ else {
+ log.info("Creating subcontext: " + ctxName);
+ ctx = ctx.createSubcontext(ctxName);
+ }
+ n = n.getSuffix(1);
+ }
+ log.trace("binding: " + n);
+ ctx.rebind(n, val);
+ }
+ log.debug("Bound name: " + name);
+ }
+
+ /**
+ * Transform JNDI properties passed in the form <tt>hibernate.jndi.*</tt> to the
+ * format accepted by <tt>InitialContext</tt> by triming the leading "<tt>hibernate.jndi</tt>".
+ */
+ public static Properties getJndiProperties(Properties properties) {
+
+ HashSet specialProps = new HashSet();
+ specialProps.add(Environment.JNDI_CLASS);
+ specialProps.add(Environment.JNDI_URL);
+
+ Iterator iter = properties.keySet().iterator();
+ Properties result = new Properties();
+ while ( iter.hasNext() ) {
+ String prop = (String) iter.next();
+ if ( prop.indexOf(Environment.JNDI_PREFIX) > -1 && !specialProps.contains(prop) ) {
+ result.setProperty(
+ prop.substring( Environment.JNDI_PREFIX.length()+1 ),
+ properties.getProperty(prop)
+ );
+ }
+ }
+
+ String jndiClass = properties.getProperty(Environment.JNDI_CLASS);
+ String jndiURL = properties.getProperty(Environment.JNDI_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);
+
+ return result;
+ }
+
+ private NamingHelper() {}
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/util/PropertiesHelper.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/util/PropertiesHelper.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/util/PropertiesHelper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,149 @@
+//$Id: PropertiesHelper.java 9712 2006-03-29 13:56:59Z steve.ebersole at jboss.com $
+package org.hibernate.util;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import java.util.Iterator;
+
+
+public final class PropertiesHelper {
+
+ private static final String PLACEHOLDER_START = "${";
+
+ public static boolean getBoolean(String property, Properties properties) {
+ String setting = properties.getProperty(property);
+ return setting != null && Boolean.valueOf( setting.trim() ).booleanValue();
+ }
+
+ public static boolean getBoolean(String property, Properties properties, boolean defaultValue) {
+ String setting = properties.getProperty(property);
+ return setting==null ? defaultValue : Boolean.valueOf( setting.trim() ).booleanValue();
+ }
+
+ public static int getInt(String property, Properties properties, int defaultValue) {
+ String propValue = properties.getProperty(property);
+ return propValue==null ? defaultValue : Integer.parseInt( propValue.trim() );
+ }
+
+ public static String getString(String property, Properties properties, String defaultValue) {
+ String propValue = properties.getProperty(property);
+ return propValue==null ? defaultValue : propValue;
+ }
+
+ public static Integer getInteger(String property, Properties properties) {
+ String propValue = properties.getProperty(property);
+ return propValue==null ? null : Integer.valueOf( propValue.trim() );
+ }
+
+ public static Map toMap(String property, String delim, Properties properties) {
+ Map map = new HashMap();
+ String propValue = properties.getProperty(property);
+ if (propValue!=null) {
+ StringTokenizer tokens = new StringTokenizer(propValue, delim);
+ while ( tokens.hasMoreTokens() ) {
+ map.put(
+ tokens.nextToken(),
+ tokens.hasMoreElements() ? tokens.nextToken() : ""
+ );
+ }
+ }
+ return map;
+ }
+
+ public static String[] toStringArray(String property, String delim, Properties properties) {
+ return toStringArray( properties.getProperty(property), delim );
+ }
+
+ public static String[] toStringArray(String propValue, String delim) {
+ if (propValue!=null) {
+ return StringHelper.split(delim, propValue);
+ }
+ else {
+ return ArrayHelper.EMPTY_STRING_ARRAY;
+ }
+ }
+
+ /**
+ * replace a property by a starred version
+ *
+ * @param props properties to check
+ * @param key proeprty to mask
+ * @return cloned and masked properties
+ */
+ public static Properties maskOut(Properties props, String key) {
+ Properties clone = (Properties) props.clone();
+ if (clone.get(key) != null) {
+ clone.setProperty(key, "****");
+ }
+ return clone;
+ }
+
+ public static void resolvePlaceHolders(Properties properties) {
+ Iterator itr = properties.entrySet().iterator();
+ while ( itr.hasNext() ) {
+ final Map.Entry entry = ( Map.Entry ) itr.next();
+ final Object value = entry.getValue();
+ if ( value != null && String.class.isInstance( value ) ) {
+ final String resolved = resolvePlaceHolder( ( String ) value );
+ if ( !value.equals( resolved ) ) {
+ if ( resolved == null ) {
+ itr.remove();
+ }
+ else {
+ entry.setValue( resolved );
+ }
+ }
+ }
+ }
+ }
+
+ public static String resolvePlaceHolder(String property) {
+ if ( property.indexOf( PLACEHOLDER_START ) < 0 ) {
+ return property;
+ }
+ StringBuffer buff = new StringBuffer();
+ char[] chars = property.toCharArray();
+ for ( int pos = 0; pos < chars.length; pos++ ) {
+ if ( chars[pos] == '$' ) {
+ // peek ahead
+ if ( chars[pos+1] == '{' ) {
+ // we have a placeholder, spin forward till we find the end
+ String systemPropertyName = "";
+ int x = pos + 2;
+ for ( ; x < chars.length && chars[x] != '}'; x++ ) {
+ systemPropertyName += chars[x];
+ // if we reach the end of the string w/o finding the
+ // matching end, that is an exception
+ if ( x == chars.length - 1 ) {
+ throw new IllegalArgumentException( "unmatched placeholder start [" + property + "]" );
+ }
+ }
+ String systemProperty = extractFromSystem( systemPropertyName );
+ buff.append( systemProperty == null ? "" : systemProperty );
+ pos = x + 1;
+ // make sure spinning forward did not put us past the end of the buffer...
+ if ( pos >= chars.length ) {
+ break;
+ }
+ }
+ }
+ buff.append( chars[pos] );
+ }
+ String rtn = buff.toString();
+ return StringHelper.isEmpty( rtn ) ? null : rtn;
+ }
+
+ private static String extractFromSystem(String systemPropertyName) {
+ try {
+ return System.getProperty( systemPropertyName );
+ }
+ catch( Throwable t ) {
+ return null;
+ }
+ }
+
+
+ private PropertiesHelper() {}
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/util/Range.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/util/Range.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/util/Range.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+//$Id: Range.java 3890 2004-06-03 16:31:32Z steveebersole $
+package org.hibernate.util;
+
+public final class Range {
+
+ public static int[] range(int begin, int length) {
+ int[] result = new int[length];
+ for ( int i=0; i<length; i++ ) {
+ result[i]=begin + i;
+ }
+ return result;
+ }
+
+ private Range() {}
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/util/ReflectHelper.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/util/ReflectHelper.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/util/ReflectHelper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,201 @@
+//$Id: ReflectHelper.java 10109 2006-07-12 15:39:59Z steve.ebersole at jboss.com $
+package org.hibernate.util;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import org.hibernate.AssertionFailure;
+import org.hibernate.MappingException;
+import org.hibernate.PropertyNotFoundException;
+import org.hibernate.property.BasicPropertyAccessor;
+import org.hibernate.property.DirectPropertyAccessor;
+import org.hibernate.property.Getter;
+import org.hibernate.property.PropertyAccessor;
+import org.hibernate.type.PrimitiveType;
+import org.hibernate.type.Type;
+
+
+public final class ReflectHelper {
+
+ //TODO: this dependency is kinda Bad
+ private static final PropertyAccessor BASIC_PROPERTY_ACCESSOR = new BasicPropertyAccessor();
+ private static final PropertyAccessor DIRECT_PROPERTY_ACCESSOR = new DirectPropertyAccessor();
+
+ private static final Class[] NO_CLASSES = new Class[0];
+ private static final Class[] OBJECT = new Class[] { Object.class };
+ private static final Method OBJECT_EQUALS;
+ private static final Class[] NO_PARAM = new Class[] { };
+
+ private static final Method OBJECT_HASHCODE;
+ static {
+ Method eq;
+ Method hash;
+ try {
+ eq = Object.class.getMethod("equals", OBJECT);
+ hash = Object.class.getMethod("hashCode", NO_PARAM);
+ }
+ catch (Exception e) {
+ throw new AssertionFailure("Could not find Object.equals() or Object.hashCode()", e);
+ }
+ OBJECT_EQUALS = eq;
+ OBJECT_HASHCODE = hash;
+ }
+
+ public static boolean overridesEquals(Class clazz) {
+ Method equals;
+ try {
+ equals = clazz.getMethod("equals", OBJECT);
+ }
+ catch (NoSuchMethodException nsme) {
+ return false; //its an interface so we can't really tell anything...
+ }
+ return !OBJECT_EQUALS.equals(equals);
+ }
+
+ public static boolean overridesHashCode(Class clazz) {
+ Method hashCode;
+ try {
+ hashCode = clazz.getMethod("hashCode", NO_PARAM);
+ }
+ catch (NoSuchMethodException nsme) {
+ return false; //its an interface so we can't really tell anything...
+ }
+ return !OBJECT_HASHCODE.equals(hashCode);
+ }
+
+ public static Class reflectedPropertyClass(String className, String name) throws MappingException {
+ try {
+ Class clazz = ReflectHelper.classForName(className);
+ return getter(clazz, name).getReturnType();
+ }
+ catch (ClassNotFoundException cnfe) {
+ throw new MappingException("class " + className + " not found while looking for property: " + name, cnfe);
+ }
+ }
+
+ private static Getter getter(Class clazz, String name) throws MappingException {
+ try {
+ return BASIC_PROPERTY_ACCESSOR.getGetter(clazz, name);
+ }
+ catch (PropertyNotFoundException pnfe) {
+ return DIRECT_PROPERTY_ACCESSOR.getGetter(clazz, name);
+ }
+ }
+
+ public static Getter getGetter(Class theClass, String name) throws MappingException {
+ return BASIC_PROPERTY_ACCESSOR.getGetter(theClass, name);
+ }
+
+ public static Class classForName(String name) throws ClassNotFoundException {
+ try {
+ ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+ if ( contextClassLoader != null ) {
+ return contextClassLoader.loadClass(name);
+ }
+ }
+ catch ( Throwable t ) {
+ }
+ return Class.forName( name );
+ }
+
+ public static Class classForName(String name, Class caller) throws ClassNotFoundException {
+ try {
+ ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+ if ( contextClassLoader != null ) {
+ return contextClassLoader.loadClass( name );
+ }
+ }
+ catch ( Throwable e ) {
+ }
+ return Class.forName( name, true, caller.getClassLoader() );
+ }
+
+ public static boolean isPublic(Class clazz, Member member) {
+ return Modifier.isPublic( member.getModifiers() ) && Modifier.isPublic( clazz.getModifiers() );
+ }
+
+ public static Object getConstantValue(String name) {
+ Class clazz;
+ try {
+ clazz = classForName( StringHelper.qualifier( name ) );
+ }
+ catch ( Throwable t ) {
+ return null;
+ }
+ try {
+ return clazz.getField( StringHelper.unqualify( name ) ).get(null);
+ }
+ catch ( Throwable t ) {
+ return null;
+ }
+ }
+
+ public static Constructor getDefaultConstructor(Class clazz) throws PropertyNotFoundException {
+
+ if ( isAbstractClass(clazz) ) return null;
+
+ try {
+ Constructor constructor = clazz.getDeclaredConstructor(NO_CLASSES);
+ if ( !isPublic(clazz, constructor) ) {
+ constructor.setAccessible(true);
+ }
+ return constructor;
+ }
+ catch (NoSuchMethodException nme) {
+ throw new PropertyNotFoundException(
+ "Object class " + clazz.getName() +
+ " must declare a default (no-argument) constructor"
+ );
+ }
+
+ }
+
+ public static boolean isAbstractClass(Class clazz) {
+ int modifier = clazz.getModifiers();
+ return Modifier.isAbstract(modifier) || Modifier.isInterface(modifier);
+ }
+
+ public static boolean isFinalClass(Class clazz) {
+ return Modifier.isFinal( clazz.getModifiers() );
+ }
+
+ public static Constructor getConstructor(Class clazz, Type[] types) throws PropertyNotFoundException {
+ final Constructor[] candidates = clazz.getConstructors();
+ for ( int i=0; i<candidates.length; i++ ) {
+ final Constructor constructor = candidates[i];
+ final Class[] params = constructor.getParameterTypes();
+ if ( params.length==types.length ) {
+ boolean found = true;
+ for ( int j=0; j<params.length; j++ ) {
+ final boolean ok = params[j].isAssignableFrom( types[j].getReturnedClass() ) || (
+ types[j] instanceof PrimitiveType &&
+ params[j] == ( (PrimitiveType) types[j] ).getPrimitiveClass()
+ );
+ if (!ok) {
+ found = false;
+ break;
+ }
+ }
+ if (found) {
+ if ( !isPublic(clazz, constructor) ) constructor.setAccessible(true);
+ return constructor;
+ }
+ }
+ }
+ throw new PropertyNotFoundException( "no appropriate constructor in class: " + clazz.getName() );
+ }
+
+ public static Method getMethod(Class clazz, Method method) {
+ try {
+ return clazz.getMethod( method.getName(), method.getParameterTypes() );
+ }
+ catch (Exception e) {
+ return null;
+ }
+ }
+
+ private ReflectHelper() {}
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/util/SerializationHelper.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/util/SerializationHelper.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/util/SerializationHelper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,274 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2002-2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ * any, must include the following acknowledgement:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgement may appear in the software itself,
+ * if and wherever such third-party acknowledgements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ * Foundation" must not be used to endorse or promote products derived
+ * from this software without prior written permission. For written
+ * permission, please contact apache at apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+package org.hibernate.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.io.Serializable;
+import java.io.ObjectStreamClass;
+import java.io.ObjectInputStream;
+
+import org.hibernate.type.SerializationException;
+import org.hibernate.Hibernate;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * <p>Assists with the serialization process and performs additional functionality based
+ * on serialization.</p>
+ * <p>
+ * <ul>
+ * <li>Deep clone using serialization
+ * <li>Serialize managing finally and IOException
+ * <li>Deserialize managing finally and IOException
+ * </ul>
+ *
+ * <p>This class throws exceptions for invalid <code>null</code> inputs.
+ * Each method documents its behaviour in more detail.</p>
+ *
+ * @author <a href="mailto:nissim at nksystems.com">Nissim Karpenstein</a>
+ * @author <a href="mailto:janekdb at yahoo.co.uk">Janek Bogucki</a>
+ * @author <a href="mailto:dlr at finemaltcoding.com">Daniel Rall</a>
+ * @author Stephen Colebourne
+ * @author Jeff Varszegi
+ * @author Gary Gregory
+ * @since 1.0
+ * @version $Id: SerializationHelper.java 9180 2006-01-30 23:51:27Z steveebersole $
+ */
+public final class SerializationHelper {
+
+ private static final Log log = LogFactory.getLog(SerializationHelper.class);
+
+ private SerializationHelper() {}
+
+ // Clone
+ //-----------------------------------------------------------------------
+ /**
+ * <p>Deep clone an <code>Object</code> using serialization.</p>
+ *
+ * <p>This is many times slower than writing clone methods by hand
+ * on all objects in your object graph. However, for complex object
+ * graphs, or for those that don't support deep cloning this can
+ * be a simple alternative implementation. Of course all the objects
+ * must be <code>Serializable</code>.</p>
+ *
+ * @param object the <code>Serializable</code> object to clone
+ * @return the cloned object
+ * @throws SerializationException (runtime) if the serialization fails
+ */
+ public static Object clone(Serializable object) throws SerializationException {
+ log.trace("Starting clone through serialization");
+ return deserialize( serialize(object) );
+ }
+
+ // Serialize
+ //-----------------------------------------------------------------------
+ /**
+ * <p>Serializes an <code>Object</code> to the specified stream.</p>
+ *
+ * <p>The stream will be closed once the object is written.
+ * This avoids the need for a finally clause, and maybe also exception
+ * handling, in the application code.</p>
+ *
+ * <p>The stream passed in is not buffered internally within this method.
+ * This is the responsibility of your application if desired.</p>
+ *
+ * @param obj the object to serialize to bytes, may be null
+ * @param outputStream the stream to write to, must not be null
+ * @throws IllegalArgumentException if <code>outputStream</code> is <code>null</code>
+ * @throws SerializationException (runtime) if the serialization fails
+ */
+ public static void serialize(Serializable obj, OutputStream outputStream) throws SerializationException {
+ if (outputStream == null) {
+ throw new IllegalArgumentException("The OutputStream must not be null");
+ }
+
+ if ( log.isTraceEnabled() ) {
+ if ( Hibernate.isInitialized( obj ) ) {
+ log.trace( "Starting serialization of object [" + obj + "]" );
+ }
+ else {
+ log.trace( "Starting serialization of [uninitialized proxy]" );
+ }
+ }
+
+ ObjectOutputStream out = null;
+ try {
+ // stream closed in the finally
+ out = new ObjectOutputStream(outputStream);
+ out.writeObject(obj);
+
+ }
+ catch (IOException ex) {
+ throw new SerializationException("could not serialize", ex);
+ }
+ finally {
+ try {
+ if (out != null) out.close();
+ }
+ catch (IOException ignored) {}
+ }
+ }
+
+ /**
+ * <p>Serializes an <code>Object</code> to a byte array for
+ * storage/serialization.</p>
+ *
+ * @param obj the object to serialize to bytes
+ * @return a byte[] with the converted Serializable
+ * @throws SerializationException (runtime) if the serialization fails
+ */
+ public static byte[] serialize(Serializable obj) throws SerializationException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
+ serialize(obj, baos);
+ return baos.toByteArray();
+ }
+
+ // Deserialize
+ //-----------------------------------------------------------------------
+ /**
+ * <p>Deserializes an <code>Object</code> from the specified stream.</p>
+ *
+ * <p>The stream will be closed once the object is written. This
+ * avoids the need for a finally clause, and maybe also exception
+ * handling, in the application code.</p>
+ *
+ * <p>The stream passed in is not buffered internally within this method.
+ * This is the responsibility of your application if desired.</p>
+ *
+ * @param inputStream the serialized object input stream, must not be null
+ * @return the deserialized object
+ * @throws IllegalArgumentException if <code>inputStream</code> is <code>null</code>
+ * @throws SerializationException (runtime) if the serialization fails
+ */
+ public static Object deserialize(InputStream inputStream) throws SerializationException {
+ if (inputStream == null) {
+ throw new IllegalArgumentException("The InputStream must not be null");
+ }
+
+ log.trace("Starting deserialization of object");
+
+ CustomObjectInputStream in = null;
+ try {
+ // stream closed in the finally
+ in = new CustomObjectInputStream(inputStream);
+ return in.readObject();
+
+ }
+ catch (ClassNotFoundException ex) {
+ throw new SerializationException("could not deserialize", ex);
+ }
+ catch (IOException ex) {
+ throw new SerializationException("could not deserialize", ex);
+ }
+ finally {
+ try {
+ if (in != null) in.close();
+ }
+ catch (IOException ex) {}
+ }
+ }
+
+ /**
+ * <p>Deserializes a single <code>Object</code> from an array of bytes.</p>
+ *
+ * @param objectData the serialized object, must not be null
+ * @return the deserialized object
+ * @throws IllegalArgumentException if <code>objectData</code> is <code>null</code>
+ * @throws SerializationException (runtime) if the serialization fails
+ */
+ public static Object deserialize(byte[] objectData) throws SerializationException {
+ if (objectData == null) {
+ throw new IllegalArgumentException("The byte[] must not be null");
+ }
+ ByteArrayInputStream bais = new ByteArrayInputStream(objectData);
+ return deserialize(bais);
+ }
+
+
+ /**
+ * Custom ObjectInputStream implementation to more appropriately handle classloading
+ * within app servers (mainly jboss - hence this class inspired by jboss's class of
+ * the same purpose).
+ */
+ private static final class CustomObjectInputStream extends ObjectInputStream {
+
+ public CustomObjectInputStream(InputStream in) throws IOException {
+ super(in);
+ }
+
+ protected Class resolveClass(ObjectStreamClass v) throws IOException, ClassNotFoundException {
+ String className = v.getName();
+ Class resolvedClass = null;
+
+ log.trace("Attempting to locate class [" + className + "]");
+
+ ClassLoader loader = Thread.currentThread().getContextClassLoader();
+ try {
+ resolvedClass = loader.loadClass(className);
+ log.trace("Class resolved through context class loader");
+ }
+ catch(ClassNotFoundException e) {
+ log.trace("Asking super to resolve");
+ resolvedClass = super.resolveClass(v);
+ }
+
+ return resolvedClass;
+ }
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/util/SimpleMRUCache.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/util/SimpleMRUCache.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/util/SimpleMRUCache.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,58 @@
+package org.hibernate.util;
+
+import org.apache.commons.collections.LRUMap;
+
+import java.io.Serializable;
+import java.io.IOException;
+
+/**
+ * Cache following a "Most Recently Used" (MRU) algorithm for maintaining a
+ * bounded in-memory size; the "Least Recently Used" (LRU) entry is the first
+ * available for removal from the cache.
+ * <p/>
+ * This implementation uses a bounded MRU Map to limit the in-memory size of
+ * the cache. Thus the size of this cache never grows beyond the stated size.
+ *
+ * @author Steve Ebersole
+ */
+public class SimpleMRUCache implements Serializable {
+
+ public static final int DEFAULT_STRONG_REF_COUNT = 128;
+
+ private final int strongReferenceCount;
+ private transient LRUMap cache;
+
+ public SimpleMRUCache() {
+ this( DEFAULT_STRONG_REF_COUNT );
+ }
+
+ public SimpleMRUCache(int strongReferenceCount) {
+ this.strongReferenceCount = strongReferenceCount;
+ init();
+ }
+
+ public synchronized Object get(Object key) {
+ return cache.get( key );
+ }
+
+ public synchronized Object put(Object key, Object value) {
+ return cache.put( key, value );
+ }
+
+ public synchronized int size() {
+ return cache.size();
+ }
+
+ private void init() {
+ cache = new LRUMap( strongReferenceCount );
+ }
+
+ private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
+ in.defaultReadObject();
+ init();
+ }
+
+ public synchronized void clear() {
+ cache.clear();
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/util/SingletonIterator.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/util/SingletonIterator.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/util/SingletonIterator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,36 @@
+//$Id: SingletonIterator.java 6514 2005-04-26 06:37:54Z oneovthafew $
+package org.hibernate.util;
+
+import java.util.Iterator;
+
+/**
+ * @author Gavin King
+ */
+public final class SingletonIterator implements Iterator {
+
+ private Object value;
+ private boolean hasNext = true;
+
+ public boolean hasNext() {
+ return hasNext;
+ }
+
+ public Object next() {
+ if (hasNext) {
+ hasNext = false;
+ return value;
+ }
+ else {
+ throw new IllegalStateException();
+ }
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ public SingletonIterator(Object value) {
+ this.value = value;
+ }
+
+}
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/util/SingletonIterator.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/util/SoftLimitMRUCache.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/util/SoftLimitMRUCache.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/util/SoftLimitMRUCache.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,83 @@
+package org.hibernate.util;
+
+import org.apache.commons.collections.ReferenceMap;
+import org.apache.commons.collections.LRUMap;
+
+import java.io.Serializable;
+import java.io.IOException;
+
+/**
+ * Cache following a "Most Recently Used" (MRY) algorithm for maintaining a
+ * bounded in-memory size; the "Least Recently Used" (LRU) entry is the first
+ * available for removal from the cache.
+ * <p/>
+ * This implementation uses a "soft limit" to the in-memory size of the cache,
+ * meaning that all cache entries are kept within a completely
+ * {@link java.lang.ref.SoftReference}-based map with the most recently utilized
+ * entries additionally kept in a hard-reference manner to prevent those cache
+ * entries soft references from becoming enqueued by the garbage collector.
+ * Thus the actual size of this cache impl can actually grow beyond the stated
+ * max size bound as long as GC is not actively seeking soft references for
+ * enqueuement.
+ *
+ * @author Steve Ebersole
+ */
+public class SoftLimitMRUCache implements Serializable {
+
+ public static final int DEFAULT_STRONG_REF_COUNT = 128;
+
+ private final int strongReferenceCount;
+
+ // actual cache of the entries. soft references are used for both the keys and the
+ // values here since the values pertaining to the MRU entries are kept in a
+ // seperate hard reference cache (to avoid their enqueuement/garbage-collection).
+ private transient ReferenceMap softReferenceCache = new ReferenceMap( ReferenceMap.SOFT, ReferenceMap.SOFT );
+ // the MRU cache used to keep hard references to the most recently used query plans;
+ // note : LRU here is a bit of a misnomer, it indicates that LRU entries are removed, the
+ // actual kept entries are the MRU entries
+ private transient LRUMap strongReferenceCache;
+
+ public SoftLimitMRUCache() {
+ this( DEFAULT_STRONG_REF_COUNT );
+ }
+
+ public SoftLimitMRUCache(int strongRefCount) {
+ this.strongReferenceCount = strongRefCount;
+ init();
+ }
+
+ public synchronized Object get(Object key) {
+ Object result = softReferenceCache.get( key );
+ if ( result != null ) {
+ strongReferenceCache.put( key, result );
+ }
+ return result;
+ }
+
+ public synchronized Object put(Object key, Object value) {
+ softReferenceCache.put( key, value );
+ return strongReferenceCache.put( key, value );
+ }
+
+ public synchronized int size() {
+ return strongReferenceCache.size();
+ }
+
+ public synchronized int softSize() {
+ return softReferenceCache.size();
+ }
+
+ private void init() {
+ strongReferenceCache = new LRUMap( strongReferenceCount );
+ }
+
+ private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
+ in.defaultReadObject();
+ init();
+ }
+
+ public synchronized void clear() {
+ strongReferenceCache.clear();
+ softReferenceCache.clear();
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/util/StringHelper.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/util/StringHelper.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/util/StringHelper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,393 @@
+//$Id: StringHelper.java 10315 2006-08-23 13:22:54Z steve.ebersole at jboss.com $
+package org.hibernate.util;
+
+import java.util.Iterator;
+import java.util.StringTokenizer;
+import java.util.ArrayList;
+
+public final class StringHelper {
+
+ private static final int ALIAS_TRUNCATE_LENGTH = 10;
+ public static final String WHITESPACE = " \n\r\f\t";
+
+ private StringHelper() { /* static methods only - hide constructor */
+ }
+
+ /*public static boolean containsDigits(String string) {
+ for ( int i=0; i<string.length(); i++ ) {
+ if ( Character.isDigit( string.charAt(i) ) ) return true;
+ }
+ return false;
+ }*/
+
+ public static int lastIndexOfLetter(String string) {
+ for ( int i=0; i<string.length(); i++ ) {
+ char character = string.charAt(i);
+ if ( !Character.isLetter(character) /*&& !('_'==character)*/ ) return i-1;
+ }
+ return string.length()-1;
+ }
+
+ public static String join(String seperator, String[] strings) {
+ int length = strings.length;
+ if ( length == 0 ) return "";
+ StringBuffer buf = new StringBuffer( length * strings[0].length() )
+ .append( strings[0] );
+ for ( int i = 1; i < length; i++ ) {
+ buf.append( seperator ).append( strings[i] );
+ }
+ return buf.toString();
+ }
+
+ public static String join(String seperator, Iterator objects) {
+ StringBuffer buf = new StringBuffer();
+ if ( objects.hasNext() ) buf.append( objects.next() );
+ while ( objects.hasNext() ) {
+ buf.append( seperator ).append( objects.next() );
+ }
+ return buf.toString();
+ }
+
+ public static String[] add(String[] x, String sep, String[] y) {
+ String[] result = new String[x.length];
+ for ( int i = 0; i < x.length; i++ ) {
+ result[i] = x[i] + sep + y[i];
+ }
+ return result;
+ }
+
+ public static String repeat(String string, int times) {
+ StringBuffer buf = new StringBuffer( string.length() * times );
+ for ( int i = 0; i < times; i++ ) buf.append( string );
+ return buf.toString();
+ }
+
+
+ public static String replace(String template, String placeholder, String replacement) {
+ return replace( template, placeholder, replacement, false );
+ }
+
+ public static String[] replace(String templates[], String placeholder, String replacement) {
+ String[] result = new String[templates.length];
+ for ( int i =0; i<templates.length; i++ ) {
+ result[i] = replace( templates[i], placeholder, replacement );;
+ }
+ return result;
+ }
+
+ public static String replace(String template, String placeholder, String replacement, boolean wholeWords) {
+ int loc = template == null ? -1 : template.indexOf( placeholder );
+ if ( loc < 0 ) {
+ return template;
+ }
+ else {
+ final boolean actuallyReplace = !wholeWords ||
+ loc + placeholder.length() == template.length() ||
+ !Character.isJavaIdentifierPart( template.charAt( loc + placeholder.length() ) );
+ String actualReplacement = actuallyReplace ? replacement : placeholder;
+ return new StringBuffer( template.substring( 0, loc ) )
+ .append( actualReplacement )
+ .append( replace( template.substring( loc + placeholder.length() ),
+ placeholder,
+ replacement,
+ wholeWords ) ).toString();
+ }
+ }
+
+
+ public static String replaceOnce(String template, String placeholder, String replacement) {
+ int loc = template == null ? -1 : template.indexOf( placeholder );
+ if ( loc < 0 ) {
+ return template;
+ }
+ else {
+ return new StringBuffer( template.substring( 0, loc ) )
+ .append( replacement )
+ .append( template.substring( loc + placeholder.length() ) )
+ .toString();
+ }
+ }
+
+
+ public static String[] split(String seperators, String list) {
+ return split( seperators, list, false );
+ }
+
+ public static String[] split(String seperators, String list, boolean include) {
+ StringTokenizer tokens = new StringTokenizer( list, seperators, include );
+ String[] result = new String[ tokens.countTokens() ];
+ int i = 0;
+ while ( tokens.hasMoreTokens() ) {
+ result[i++] = tokens.nextToken();
+ }
+ return result;
+ }
+
+ public static String unqualify(String qualifiedName) {
+ int loc = qualifiedName.lastIndexOf(".");
+ return ( loc < 0 ) ? qualifiedName : qualifiedName.substring( qualifiedName.lastIndexOf(".") + 1 );
+ }
+
+ public static String qualifier(String qualifiedName) {
+ int loc = qualifiedName.lastIndexOf(".");
+ return ( loc < 0 ) ? "" : qualifiedName.substring( 0, loc );
+ }
+
+ public static String[] suffix(String[] columns, String suffix) {
+ if ( suffix == null ) return columns;
+ String[] qualified = new String[columns.length];
+ for ( int i = 0; i < columns.length; i++ ) {
+ qualified[i] = suffix( columns[i], suffix );
+ }
+ return qualified;
+ }
+
+ private static String suffix(String name, String suffix) {
+ return ( suffix == null ) ? name : name + suffix;
+ }
+
+ public static String root(String qualifiedName) {
+ int loc = qualifiedName.indexOf( "." );
+ return ( loc < 0 ) ? qualifiedName : qualifiedName.substring( 0, loc );
+ }
+
+ public static String unroot(String qualifiedName) {
+ int loc = qualifiedName.indexOf( "." );
+ return ( loc < 0 ) ? qualifiedName : qualifiedName.substring( loc+1, qualifiedName.length() );
+ }
+
+ public static boolean booleanValue(String tfString) {
+ String trimmed = tfString.trim().toLowerCase();
+ return trimmed.equals( "true" ) || trimmed.equals( "t" );
+ }
+
+ public static String toString(Object[] array) {
+ int len = array.length;
+ if ( len == 0 ) return "";
+ StringBuffer buf = new StringBuffer( len * 12 );
+ for ( int i = 0; i < len - 1; i++ ) {
+ buf.append( array[i] ).append(", ");
+ }
+ return buf.append( array[len - 1] ).toString();
+ }
+
+ public static String[] multiply(String string, Iterator placeholders, Iterator replacements) {
+ String[] result = new String[]{string};
+ while ( placeholders.hasNext() ) {
+ result = multiply( result, ( String ) placeholders.next(), ( String[] ) replacements.next() );
+ }
+ return result;
+ }
+
+ private static String[] multiply(String[] strings, String placeholder, String[] replacements) {
+ String[] results = new String[replacements.length * strings.length];
+ int n = 0;
+ for ( int i = 0; i < replacements.length; i++ ) {
+ for ( int j = 0; j < strings.length; j++ ) {
+ results[n++] = replaceOnce( strings[j], placeholder, replacements[i] );
+ }
+ }
+ return results;
+ }
+
+ public static int countUnquoted(String string, char character) {
+ if ( '\'' == character ) {
+ throw new IllegalArgumentException( "Unquoted count of quotes is invalid" );
+ }
+ if (string == null)
+ return 0;
+ // Impl note: takes advantage of the fact that an escpaed single quote
+ // embedded within a quote-block can really be handled as two seperate
+ // quote-blocks for the purposes of this method...
+ int count = 0;
+ int stringLength = string.length();
+ boolean inQuote = false;
+ for ( int indx = 0; indx < stringLength; indx++ ) {
+ char c = string.charAt( indx );
+ if ( inQuote ) {
+ if ( '\'' == c ) {
+ inQuote = false;
+ }
+ }
+ else if ( '\'' == c ) {
+ inQuote = true;
+ }
+ else if ( c == character ) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ public static int[] locateUnquoted(String string, char character) {
+ if ( '\'' == character ) {
+ throw new IllegalArgumentException( "Unquoted count of quotes is invalid" );
+ }
+ if (string == null) {
+ return new int[0];
+ }
+
+ ArrayList locations = new ArrayList( 20 );
+
+ // Impl note: takes advantage of the fact that an escpaed single quote
+ // embedded within a quote-block can really be handled as two seperate
+ // quote-blocks for the purposes of this method...
+ int stringLength = string.length();
+ boolean inQuote = false;
+ for ( int indx = 0; indx < stringLength; indx++ ) {
+ char c = string.charAt( indx );
+ if ( inQuote ) {
+ if ( '\'' == c ) {
+ inQuote = false;
+ }
+ }
+ else if ( '\'' == c ) {
+ inQuote = true;
+ }
+ else if ( c == character ) {
+ locations.add( new Integer( indx ) );
+ }
+ }
+ return ArrayHelper.toIntArray( locations );
+ }
+
+ public static boolean isNotEmpty(String string) {
+ return string != null && string.length() > 0;
+ }
+
+ public static boolean isEmpty(String string) {
+ return string == null || string.length() == 0;
+ }
+
+ public static String qualify(String prefix, String name) {
+ if ( name == null || prefix == null ) {
+ throw new NullPointerException();
+ }
+ return new StringBuffer( prefix.length() + name.length() + 1 )
+ .append(prefix)
+ .append('.')
+ .append(name)
+ .toString();
+ }
+
+ public static String[] qualify(String prefix, String[] names) {
+ if ( prefix == null ) return names;
+ int len = names.length;
+ String[] qualified = new String[len];
+ for ( int i = 0; i < len; i++ ) {
+ qualified[i] = qualify( prefix, names[i] );
+ }
+ return qualified;
+ }
+
+ public static int firstIndexOfChar(String sqlString, String string, int startindex) {
+ int matchAt = -1;
+ for ( int i = 0; i < string.length(); i++ ) {
+ int curMatch = sqlString.indexOf( string.charAt( i ), startindex );
+ if ( curMatch >= 0 ) {
+ if ( matchAt == -1 ) { // first time we find match!
+ matchAt = curMatch;
+ }
+ else {
+ matchAt = Math.min( matchAt, curMatch );
+ }
+ }
+ }
+ return matchAt;
+ }
+
+ public static String truncate(String string, int length) {
+ if ( string.length() <= length ) {
+ return string;
+ }
+ else {
+ return string.substring( 0, length );
+ }
+ }
+
+ public static String generateAlias(String description) {
+ return generateAliasRoot(description) + '_';
+ }
+
+ /**
+ * Generate a nice alias for the given class name or collection role
+ * name and unique integer. Subclasses of Loader do <em>not</em> have
+ * to use aliases of this form.
+ * @return an alias of the form <tt>foo1_</tt>
+ */
+ public static String generateAlias(String description, int unique) {
+ return generateAliasRoot(description) +
+ Integer.toString(unique) +
+ '_';
+ }
+
+ /**
+ * Generates a root alias by truncating the "root name" defined by
+ * the incoming decription and removing/modifying any non-valid
+ * alias characters.
+ *
+ * @param description The root name from which to generate a root alias.
+ * @return The generated root alias.
+ */
+ private static String generateAliasRoot(String description) {
+ String result = truncate( unqualifyEntityName(description), ALIAS_TRUNCATE_LENGTH )
+ .toLowerCase()
+ .replace( '/', '_' ) // entityNames may now include slashes for the representations
+ .replace( '$', '_' ); //classname may be an inner class
+ result = cleanAlias( result );
+ if ( Character.isDigit( result.charAt(result.length()-1) ) ) {
+ return result + "x"; //ick!
+ }
+ else {
+ return result;
+ }
+ }
+
+ /**
+ * Clean the generated alias by removing any non-alpha characters from the
+ * beginning.
+ *
+ * @param alias The generated alias to be cleaned.
+ * @return The cleaned alias, stripped of any leading non-alpha characters.
+ */
+ private static String cleanAlias(String alias) {
+ char[] chars = alias.toCharArray();
+ // short cut check...
+ if ( !Character.isLetter( chars[0] ) ) {
+ for ( int i = 1; i < chars.length; i++ ) {
+ // as soon as we encounter our first letter, return the substring
+ // from that position
+ if ( Character.isLetter( chars[i] ) ) {
+ return alias.substring( i );
+ }
+ }
+ }
+ return alias;
+ }
+
+ public static String unqualifyEntityName(String entityName) {
+ String result = unqualify(entityName);
+ int slashPos = result.indexOf( '/' );
+ if ( slashPos > 0 ) {
+ result = result.substring( 0, slashPos - 1 );
+ }
+ return result;
+ }
+
+ public static String toUpperCase(String str) {
+ return str==null ? null : str.toUpperCase();
+ }
+
+ public static String toLowerCase(String str) {
+ return str==null ? null : str.toLowerCase();
+ }
+
+ public static String moveAndToBeginning(String filter) {
+ if ( filter.trim().length()>0 ){
+ filter += " and ";
+ if ( filter.startsWith(" and ") ) filter = filter.substring(4);
+ }
+ return filter;
+ }
+
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/util/XMLHelper.java
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/util/XMLHelper.java (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/util/XMLHelper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,91 @@
+//$Id: XMLHelper.java 8563 2005-11-10 15:58:59Z steveebersole $
+package org.hibernate.util;
+
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.dom4j.io.DOMReader;
+import org.dom4j.io.SAXReader;
+import org.dom4j.io.OutputFormat;
+import org.dom4j.io.XMLWriter;
+import org.dom4j.Element;
+import org.dom4j.DocumentFactory;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.SAXParseException;
+
+/**
+ * Small helper class that lazy loads DOM and SAX reader and keep them for fast use afterwards.
+ *
+ *
+ *
+ */
+public final class XMLHelper {
+
+ private static final Log log = LogFactory.getLog(XMLHelper.class);
+ public static final EntityResolver DEFAULT_DTD_RESOLVER = new DTDEntityResolver();
+
+ private DOMReader domReader;
+ private SAXReader saxReader;
+
+ /**
+ * Create a dom4j SAXReader which will append all validation errors
+ * to errorList
+ */
+ public SAXReader createSAXReader(String file, List errorsList, EntityResolver entityResolver) {
+ if (saxReader==null) saxReader = new SAXReader();
+ saxReader.setEntityResolver(entityResolver);
+ saxReader.setErrorHandler( new ErrorLogger(file, errorsList) );
+ saxReader.setMergeAdjacentText(true);
+ saxReader.setValidation(true);
+ return saxReader;
+ }
+
+ /**
+ * Create a dom4j DOMReader
+ */
+ public DOMReader createDOMReader() {
+ if (domReader==null) domReader = new DOMReader();
+ return domReader;
+ }
+
+ public static class ErrorLogger implements ErrorHandler {
+ private String file;
+ private List errors;
+ ErrorLogger(String file, List errors) {
+ this.file=file;
+ this.errors = errors;
+ }
+ public void error(SAXParseException error) {
+ log.error( "Error parsing XML: " + file + '(' + error.getLineNumber() + ") " + error.getMessage() );
+ errors.add(error);
+ }
+ public void fatalError(SAXParseException error) {
+ error(error);
+ }
+ public void warning(SAXParseException warn) {
+ log.warn( "Warning parsing XML: " + file + '(' + warn.getLineNumber() + ") " + warn.getMessage() );
+ }
+ }
+
+ public static Element generateDom4jElement(String elementName) {
+ return DocumentFactory.getInstance().createElement( elementName );
+ }
+
+ public static void dump(Element element) {
+ try {
+ // try to "pretty print" it
+ OutputFormat outformat = OutputFormat.createPrettyPrint();
+ XMLWriter writer = new XMLWriter( System.out, outformat );
+ writer.write( element );
+ writer.flush();
+ System.out.println( "" );
+ }
+ catch( Throwable t ) {
+ // otherwise, just dump it
+ System.out.println( element.asXML() );
+ }
+
+ }
+}
Added: trunk/Hibernate3/core/src/main/java/org/hibernate/util/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/java/org/hibernate/util/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/java/org/hibernate/util/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,8 @@
+<html>
+<head></head>
+<body>
+<p>
+ Utility classes.
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/core/src/main/java/org/hibernate/util/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/core/src/main/javadoc/package.html
===================================================================
--- trunk/Hibernate3/core/src/main/javadoc/package.html (rev 0)
+++ trunk/Hibernate3/core/src/main/javadoc/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,39 @@
+<body>
+
+<h2>Hibernate API</h2>
+
+This documentation concentrates upon the following <b>Core API</b> interfaces:
+<li><tt>org.hibernate.Hibernate</tt></li>
+<li><tt>org.hibernate.Session</tt></li>
+<li><tt>org.hibernate.SessionFactory</tt></li>
+<li><tt>org.hibernate.Transaction</tt></li>
+<li><tt>org.hibernate.Query</tt></li>
+<li><tt>org.hibernate.Criteria</tt></li>
+<li><tt>org.hibernate.ScrollableResults</tt></li>
+<li><tt>org.hibernate.cfg.Configuration</tt></li>
+<li><tt>org.hibernate.expression.Expression</tt></li>
+<li><tt>org.hibernate.expression.Order</tt></li>
+<li><tt>org.hibernate.expression.Example</tt></li>
+These interfaces are fully intended to be exposed to application code.<br>
+<br>
+The <b>Extension API</b> is intended to be used by application programmers
+to extend Hibernate functionality. None of these interfaces are intended
+to be called by the application - they are called internally by Hibernate.
+This API is less stable than the Core API. The safest way to extend
+functionality is to contribute extensions back to the project; that way
+extensions will be updated when the Extension API changes.<br>
+<br>
+Full Hibernate documentation may be found at
+<a href="http://hibernate.org">hibernate.org</a>.
+ at see org.hibernate.Hibernate
+ at see org.hibernate.Session
+ at see org.hibernate.SessionFactory
+ at see org.hibernate.Transaction
+ at see org.hibernate.Query
+ at see org.hibernate.Criteria
+ at see org.hibernate.ScrollableResults
+ at see org.hibernate.cfg.Configuration
+ at see org.hibernate.expression.Expression
+ at see org.hibernate.expression.Order
+ at see org.hibernate.expression.Example
+</body>
Added: trunk/Hibernate3/core/src/main/resources/org/hibernate/checkstyle_checks.xml
===================================================================
--- trunk/Hibernate3/core/src/main/resources/org/hibernate/checkstyle_checks.xml (rev 0)
+++ trunk/Hibernate3/core/src/main/resources/org/hibernate/checkstyle_checks.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.1//EN" "http://www.puppycrawl.com/dtds/configuration_1_1.dtd">
+<module name="Checker">
+ <module name="TreeWalker">
+ <module name="com.puppycrawl.tools.checkstyle.checks.blocks.AvoidNestedBlocksCheck">
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="com.puppycrawl.tools.checkstyle.checks.imports.AvoidStarImportCheck">
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="com.puppycrawl.tools.checkstyle.checks.naming.ConstantNameCheck">
+ <property name="format" value="^[A-Z](_?[A-Z0-9]+)*$|log"/>
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="com.puppycrawl.tools.checkstyle.checks.coding.EmptyStatementCheck">
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="com.puppycrawl.tools.checkstyle.checks.coding.EqualsHashCodeCheck">
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="com.puppycrawl.tools.checkstyle.checks.design.FinalClassCheck">
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="com.puppycrawl.tools.checkstyle.checks.GenericIllegalRegexpCheck">
+ <property name="format" value="\(\(|\)\)"/>
+ <property name="ignoreCase" value="false"/>
+ <property name="message" value="stacked parentheses"/>
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="com.puppycrawl.tools.checkstyle.checks.coding.HiddenFieldCheck">
+ <property name="severity" value="warning"/>
+ <property name="tokens" value="VARIABLE_DEF"/>
+ </module>
+ <module name="com.puppycrawl.tools.checkstyle.checks.design.HideUtilityClassConstructorCheck">
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="com.puppycrawl.tools.checkstyle.checks.coding.IllegalInstantiationCheck">
+ <property name="classes" value="{}"/>
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="com.puppycrawl.tools.checkstyle.checks.coding.InnerAssignmentCheck">
+ <property name="severity" value="warning"/>
+ <property name="tokens" value="ASSIGN, BAND_ASSIGN, BOR_ASSIGN, BSR_ASSIGN, BXOR_ASSIGN, DIV_ASSIGN, MINUS_ASSIGN, MOD_ASSIGN, PLUS_ASSIGN, SL_ASSIGN, SR_ASSIGN, STAR_ASSIGN"/>
+ </module>
+ <module name="com.puppycrawl.tools.checkstyle.checks.design.InterfaceIsTypeCheck">
+ <property name="allowMarkerInterfaces" value="true"/>
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="com.puppycrawl.tools.checkstyle.checks.blocks.LeftCurlyCheck">
+ <property name="maxLineLength" value="80"/>
+ <property name="option" value="eol"/>
+ <property name="severity" value="warning"/>
+ <property name="tokens" value="CLASS_DEF, CTOR_DEF, INTERFACE_DEF, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF, LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, METHOD_DEF"/>
+ </module>
+ <module name="com.puppycrawl.tools.checkstyle.checks.naming.LocalFinalVariableNameCheck">
+ <property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="com.puppycrawl.tools.checkstyle.checks.naming.LocalVariableNameCheck">
+ <property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="com.puppycrawl.tools.checkstyle.checks.naming.MemberNameCheck">
+ <property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="com.puppycrawl.tools.checkstyle.checks.naming.MethodNameCheck">
+ <property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="com.puppycrawl.tools.checkstyle.checks.ModifierOrderCheck">
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="com.puppycrawl.tools.checkstyle.checks.blocks.NeedBracesCheck">
+ <property name="severity" value="warning"/>
+ <property name="tokens" value="LITERAL_DO, LITERAL_ELSE"/>
+ </module>
+ <module name="com.puppycrawl.tools.checkstyle.checks.naming.PackageNameCheck">
+ <property name="format" value="^[a-z]+(\.[a-zA-Z_][a-zA-Z0-9_]*)*$"/>
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="com.puppycrawl.tools.checkstyle.checks.imports.RedundantImportCheck">
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="com.puppycrawl.tools.checkstyle.checks.blocks.RightCurlyCheck">
+ <property name="option" value="alone"/>
+ <property name="severity" value="warning"/>
+ <property name="tokens" value="LITERAL_CATCH, LITERAL_ELSE, LITERAL_TRY"/>
+ </module>
+ <module name="com.puppycrawl.tools.checkstyle.checks.coding.SimplifyBooleanExpressionCheck">
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="com.puppycrawl.tools.checkstyle.checks.coding.SimplifyBooleanReturnCheck">
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="com.puppycrawl.tools.checkstyle.checks.naming.StaticVariableNameCheck">
+ <property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="com.puppycrawl.tools.checkstyle.checks.naming.TypeNameCheck">
+ <property name="format" value="^[A-Z][a-zA-Z0-9]*$"/>
+ <property name="severity" value="warning"/>
+ <property name="tokens" value="CLASS_DEF, INTERFACE_DEF"/>
+ </module>
+ <module name="com.puppycrawl.tools.checkstyle.checks.imports.UnusedImportsCheck">
+ <property name="severity" value="warning"/>
+ </module>
+ <module name="com.puppycrawl.tools.checkstyle.checks.design.VisibilityModifierCheck">
+ <property name="packageAllowed" value="true"/>
+ <property name="protectedAllowed" value="true"/>
+ <property name="publicMemberPattern" value="^serialVersionUID"/>
+ <property name="severity" value="warning"/>
+ </module>
+ </module>
+</module>
Added: trunk/Hibernate3/core/src/main/resources/org/hibernate/hibernate-configuration-3.0.dtd
===================================================================
--- trunk/Hibernate3/core/src/main/resources/org/hibernate/hibernate-configuration-3.0.dtd (rev 0)
+++ trunk/Hibernate3/core/src/main/resources/org/hibernate/hibernate-configuration-3.0.dtd 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,52 @@
+<!-- Hibernate file-based configuration document.
+
+<!DOCTYPE hibernate-configuration PUBLIC
+ "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
+
+An instance of this document contains property settings and references
+to mapping files for a number of SessionFactory instances to be listed
+in JNDI.
+
+-->
+
+<!ELEMENT hibernate-configuration (session-factory,security?)>
+
+<!ELEMENT property (#PCDATA)>
+<!ATTLIST property name CDATA #REQUIRED>
+
+<!ELEMENT mapping EMPTY> <!-- reference to a mapping file -->
+<!ATTLIST mapping resource CDATA #IMPLIED>
+<!ATTLIST mapping file CDATA #IMPLIED>
+<!ATTLIST mapping jar CDATA #IMPLIED>
+<!ATTLIST mapping package CDATA #IMPLIED>
+<!ATTLIST mapping class CDATA #IMPLIED>
+
+<!ELEMENT class-cache EMPTY>
+<!ATTLIST class-cache class CDATA #REQUIRED>
+<!ATTLIST class-cache region CDATA #IMPLIED>
+<!ATTLIST class-cache usage (read-only|read-write|nonstrict-read-write|transactional) #REQUIRED>
+<!ATTLIST class-cache include (all|non-lazy) "all">
+
+<!ELEMENT collection-cache EMPTY>
+<!ATTLIST collection-cache collection CDATA #REQUIRED>
+<!ATTLIST collection-cache region CDATA #IMPLIED>
+<!ATTLIST collection-cache usage (read-only|read-write|nonstrict-read-write|transactional) #REQUIRED>
+
+<!ELEMENT event (listener*)>
+<!ATTLIST event type (auto-flush|merge|create|create-onflush|delete|dirty-check|evict|flush|flush-entity|load|load-collection|lock|refresh|replicate|save-update|save|update|pre-load|pre-update|pre-insert|pre-delete|post-load|post-update|post-insert|post-delete|post-commit-update|post-commit-insert|post-commit-delete) #REQUIRED>
+
+<!ELEMENT listener EMPTY>
+<!ATTLIST listener type (auto-flush|merge|create|create-onflush|delete|dirty-check|evict|flush|flush-entity|load|load-collection|lock|refresh|replicate|save-update|save|update|pre-load|pre-update|pre-insert|pre-delete|post-load|post-update|post-insert|post-delete|post-commit-update|post-commit-insert|post-commit-delete) #IMPLIED>
+<!ATTLIST listener class CDATA #REQUIRED>
+
+<!ELEMENT session-factory (property*, mapping*, (class-cache|collection-cache)*, event*, listener*)>
+<!ATTLIST session-factory name CDATA #IMPLIED> <!-- the JNDI name -->
+
+<!ELEMENT security (grant*)>
+<!ATTLIST security context CDATA #REQUIRED> <!--the JACC contextID-->
+
+<!ELEMENT grant EMPTY>
+<!ATTLIST grant role CDATA #REQUIRED>
+<!ATTLIST grant entity-name CDATA #REQUIRED>
+<!ATTLIST grant actions CDATA #REQUIRED>
Added: trunk/Hibernate3/core/src/main/resources/org/hibernate/hibernate-mapping-3.0.dtd
===================================================================
--- trunk/Hibernate3/core/src/main/resources/org/hibernate/hibernate-mapping-3.0.dtd (rev 0)
+++ trunk/Hibernate3/core/src/main/resources/org/hibernate/hibernate-mapping-3.0.dtd 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1036 @@
+<!-- Hibernate Mapping DTD.
+
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+An instance of this XML document may contain mappings for an arbitrary
+number of classes. The class mappings may contain associations to classes
+mapped in the same document or in another document. No class may be
+mapped more than once. Each document may also contain definitions of an
+arbitrary number of queries, and import declarations of arbitrary classes.
+
+-->
+
+<!--
+ The document root.
+ -->
+
+<!ELEMENT hibernate-mapping (
+ meta*,
+ typedef*,
+ import*,
+ (class|subclass|joined-subclass|union-subclass)*,
+ resultset*,
+ (query|sql-query)*,
+ filter-def*,
+ database-object*
+)>
+ <!ATTLIST hibernate-mapping schema CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST hibernate-mapping catalog CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST hibernate-mapping default-cascade CDATA "none">
+ <!ATTLIST hibernate-mapping default-access CDATA "property">
+ <!ATTLIST hibernate-mapping default-lazy (true|false) "true">
+ <!ATTLIST hibernate-mapping auto-import (true|false) "true">
+ <!ATTLIST hibernate-mapping package CDATA #IMPLIED> <!-- default: none -->
+
+<!--
+ META element definition; used to assign meta-level attributes to a class
+ or property. Is currently used by codegenerator as a placeholder for
+ values that is not directly related to OR mappings.
+-->
+<!ELEMENT meta (#PCDATA)>
+ <!ATTLIST meta attribute CDATA #REQUIRED>
+ <!ATTLIST meta inherit (true|false) "true">
+
+<!--
+ TYPEDEF element definition; defines a new name for a Hibernate type. May
+ contain parameters for parameterizable types.
+-->
+<!ELEMENT typedef (param*)>
+ <!ATTLIST typedef class CDATA #REQUIRED>
+ <!ATTLIST typedef name CDATA #REQUIRED>
+
+<!--
+ IMPORT element definition; an explicit query language "import"
+-->
+<!ELEMENT import EMPTY>
+ <!ATTLIST import class CDATA #REQUIRED>
+ <!ATTLIST import rename CDATA #IMPLIED> <!-- default: unqualified class name -->
+
+<!--
+ Root entity mapping. Poorly named as entities do not have to be represented by
+ classes at all. Mapped entities may be represented via different methodologies
+ (POJO, Map, Dom4j).
+-->
+<!ELEMENT class (
+ meta*,
+ subselect?,
+ cache?,
+ synchronize*,
+ comment?,
+ tuplizer*,
+ (id|composite-id),
+ discriminator?,
+ natural-id?,
+ (version|timestamp)?,
+ (property|many-to-one|one-to-one|component|dynamic-component|properties|any|map|set|list|bag|idbag|array|primitive-array)*,
+ ((join*,subclass*)|joined-subclass*|union-subclass*),
+ loader?,sql-insert?,sql-update?,sql-delete?,
+ filter*,
+ resultset*,
+ (query|sql-query)*
+)>
+ <!ATTLIST class entity-name CDATA #IMPLIED>
+ <!ATTLIST class name CDATA #IMPLIED> <!-- this is the class name -->
+ <!ATTLIST class proxy CDATA #IMPLIED> <!-- default: no proxy interface -->
+ <!ATTLIST class lazy (true|false) #IMPLIED>
+ <!ATTLIST class table CDATA #IMPLIED> <!-- default: unqualified classname -->
+ <!ATTLIST class schema CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST class catalog CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST class subselect CDATA #IMPLIED>
+ <!ATTLIST class discriminator-value CDATA #IMPLIED> <!-- default: unqualified class name | none -->
+ <!ATTLIST class mutable (true|false) "true">
+ <!ATTLIST class abstract (true|false) #IMPLIED>
+ <!ATTLIST class polymorphism (implicit|explicit) "implicit">
+ <!ATTLIST class where CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST class persister CDATA #IMPLIED>
+ <!ATTLIST class dynamic-update (true|false) "false">
+ <!ATTLIST class dynamic-insert (true|false) "false">
+ <!ATTLIST class batch-size CDATA #IMPLIED>
+ <!ATTLIST class select-before-update (true|false) "false">
+ <!ATTLIST class optimistic-lock (none|version|dirty|all) "version">
+ <!ATTLIST class check CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST class rowid CDATA #IMPLIED>
+ <!ATTLIST class node CDATA #IMPLIED>
+
+<!--
+ TUPLIZER element; defines tuplizer to use for a component/entity for a given entity-mode
+-->
+<!ELEMENT tuplizer EMPTY>
+ <!ATTLIST tuplizer entity-mode (pojo|dom4j|dynamic-map) #IMPLIED> <!-- entity mode for which tuplizer is in effect -->
+ <!ATTLIST tuplizer class CDATA #REQUIRED> <!-- the tuplizer class to use -->
+
+<!--
+ FILTER-DEF element; top-level filter definition.
+-->
+<!ELEMENT filter-def (#PCDATA|filter-param)*>
+ <!ATTLIST filter-def name CDATA #REQUIRED> <!-- The filter name -->
+ <!ATTLIST filter-def condition CDATA #IMPLIED>
+
+<!--
+ FILTER-PARAM element; qualifies parameters found within a FILTER-DEF
+ condition.
+-->
+<!ELEMENT filter-param EMPTY>
+ <!ATTLIST filter-param name CDATA #REQUIRED> <!-- The parameter name -->
+ <!ATTLIST filter-param type CDATA #REQUIRED> <!-- The parameter type -->
+
+<!--
+ FILTER element; used to apply a filter.
+-->
+<!ELEMENT filter (#PCDATA)>
+ <!ATTLIST filter name CDATA #REQUIRED>
+ <!ATTLIST filter condition CDATA #IMPLIED>
+
+
+<!-- A join allows some properties of a class to be persisted to a second table -->
+
+<!ELEMENT join (
+ subselect?,
+ comment?,
+ key,
+ (property|many-to-one|component|dynamic-component|any)*,
+ sql-insert?,sql-update?,sql-delete?
+)>
+ <!ATTLIST join table CDATA #REQUIRED>
+ <!ATTLIST join schema CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST join catalog CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST join subselect CDATA #IMPLIED>
+ <!ATTLIST join fetch (join|select) "join">
+ <!ATTLIST join inverse (true|false) "false">
+ <!ATTLIST join optional (true|false) "false">
+
+<!-- A natural-id element allows declaration of the unique business key -->
+
+<!ELEMENT natural-id ( (property|many-to-one|component|dynamic-component|any)* )>
+ <!ATTLIST natural-id mutable (true|false) "false">
+
+<!-- Declares the id type, column and generation algorithm for an entity class.
+If a name attribut is given, the id is exposed to the application through the
+named property of the class. If not, the id is only exposed to the application
+via Session.getIdentifier() -->
+
+<!ELEMENT id (meta*,column*,type?,generator?)>
+ <!ATTLIST id name CDATA #IMPLIED>
+ <!ATTLIST id node CDATA #IMPLIED>
+ <!ATTLIST id access CDATA #IMPLIED>
+ <!ATTLIST id column CDATA #IMPLIED>
+ <!ATTLIST id type CDATA #IMPLIED>
+ <!ATTLIST id length CDATA #IMPLIED>
+ <!ATTLIST id unsaved-value CDATA #IMPLIED> <!-- any|none|null|undefined|0|-1|... -->
+
+<!-- A composite key may be modelled by a java class with a property for each
+key column. The class must implement java.io.Serializable and reimplement equals()
+and hashCode(). -->
+
+<!ELEMENT composite-id ( meta*, (key-property|key-many-to-one)+ )>
+ <!ATTLIST composite-id class CDATA #IMPLIED>
+ <!ATTLIST composite-id mapped (true|false) "false">
+ <!ATTLIST composite-id name CDATA #IMPLIED>
+ <!ATTLIST composite-id node CDATA #IMPLIED>
+ <!ATTLIST composite-id access CDATA #IMPLIED>
+ <!ATTLIST composite-id unsaved-value (undefined|any|none) "undefined">
+
+<!-- Polymorphic data requires a column holding a class discriminator value. This
+value is not directly exposed to the application. -->
+
+<!ELEMENT discriminator ((column|formula)?)>
+ <!ATTLIST discriminator column CDATA #IMPLIED> <!-- default: "class"|none -->
+ <!ATTLIST discriminator formula CDATA #IMPLIED>
+ <!ATTLIST discriminator type CDATA "string">
+ <!ATTLIST discriminator not-null (true|false) "true">
+ <!ATTLIST discriminator length CDATA #IMPLIED>
+ <!ATTLIST discriminator force (true|false) "false">
+ <!ATTLIST discriminator insert (true|false) "true">
+
+<!-- Versioned data requires a column holding a version number. This is exposed to the
+application through a property of the Java class. -->
+
+<!ELEMENT version (meta*,column*)>
+ <!ATTLIST version name CDATA #REQUIRED>
+ <!ATTLIST version node CDATA #IMPLIED>
+ <!ATTLIST version access CDATA #IMPLIED>
+ <!ATTLIST version column CDATA #IMPLIED>
+ <!ATTLIST version type CDATA "integer">
+ <!ATTLIST version unsaved-value (null|negative|undefined) "undefined">
+ <!ATTLIST version generated (never|always) "never">
+ <!ATTLIST version insert (true|false) #IMPLIED>
+
+<!ELEMENT timestamp (meta*)>
+ <!ATTLIST timestamp name CDATA #REQUIRED>
+ <!ATTLIST timestamp node CDATA #IMPLIED>
+ <!ATTLIST timestamp column CDATA #IMPLIED>
+ <!ATTLIST timestamp access CDATA #IMPLIED>
+ <!ATTLIST timestamp unsaved-value (null|undefined) "null">
+ <!ATTLIST timestamp source (vm|db) "vm">
+ <!ATTLIST timestamp generated (never|always) "never">
+
+
+<!--
+ Subclass declarations are nested beneath the root class declaration to achieve
+ polymorphic persistence with the table-per-hierarchy mapping strategy.
+
+ See the note on the class element regarding <pojo/> vs. @name usage...
+-->
+<!ELEMENT subclass (
+ meta*,
+ tuplizer*,
+ synchronize*,
+ (property|many-to-one|one-to-one|component|dynamic-component|any|map|set|list|bag|idbag|array|primitive-array)*,
+ join*,
+ subclass*,
+ loader?,sql-insert?,sql-update?,sql-delete?,
+ resultset*,
+ (query|sql-query)*
+)>
+ <!ATTLIST subclass entity-name CDATA #IMPLIED>
+ <!ATTLIST subclass name CDATA #IMPLIED>
+ <!ATTLIST subclass proxy CDATA #IMPLIED> <!-- default: no proxy interface -->
+ <!ATTLIST subclass discriminator-value CDATA #IMPLIED> <!-- default: unqualified class name | none -->
+ <!ATTLIST subclass dynamic-update (true|false) "false">
+ <!ATTLIST subclass dynamic-insert (true|false) "false">
+ <!ATTLIST subclass select-before-update (true|false) "false">
+ <!ATTLIST subclass extends CDATA #IMPLIED> <!-- default: empty when a toplevel, otherwise the nearest class definition -->
+ <!ATTLIST subclass lazy (true|false) #IMPLIED>
+ <!ATTLIST subclass abstract (true|false) #IMPLIED>
+ <!ATTLIST subclass persister CDATA #IMPLIED>
+ <!ATTLIST subclass batch-size CDATA #IMPLIED>
+ <!ATTLIST subclass node CDATA #IMPLIED>
+
+<!--
+ Joined subclasses are used for the normalized table-per-subclass mapping strategy
+
+ See the note on the class element regarding <pojo/> vs. @name usage...
+-->
+<!ELEMENT joined-subclass (
+ meta*,
+ subselect?,
+ synchronize*,
+ comment?,
+ tuplizer*,
+ key,
+ (property|many-to-one|one-to-one|component|dynamic-component|properties|any|map|set|list|bag|idbag|array|primitive-array)*,
+ joined-subclass*,
+ loader?,sql-insert?,sql-update?,sql-delete?,
+ resultset*,
+ (query|sql-query)*
+)>
+ <!ATTLIST joined-subclass entity-name CDATA #IMPLIED>
+ <!ATTLIST joined-subclass name CDATA #IMPLIED>
+ <!ATTLIST joined-subclass proxy CDATA #IMPLIED> <!-- default: no proxy interface -->
+ <!ATTLIST joined-subclass table CDATA #IMPLIED> <!-- default: unqualified class name -->
+ <!ATTLIST joined-subclass schema CDATA #IMPLIED>
+ <!ATTLIST joined-subclass catalog CDATA #IMPLIED>
+ <!ATTLIST joined-subclass subselect CDATA #IMPLIED>
+ <!ATTLIST joined-subclass dynamic-update (true|false) "false">
+ <!ATTLIST joined-subclass dynamic-insert (true|false) "false">
+ <!ATTLIST joined-subclass select-before-update (true|false) "false">
+ <!ATTLIST joined-subclass extends CDATA #IMPLIED> <!-- default: none when toplevel, otherwise the nearest class definition -->
+ <!ATTLIST joined-subclass lazy (true|false) #IMPLIED>
+ <!ATTLIST joined-subclass abstract (true|false) #IMPLIED>
+ <!ATTLIST joined-subclass persister CDATA #IMPLIED>
+ <!ATTLIST joined-subclass check CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST joined-subclass batch-size CDATA #IMPLIED>
+ <!ATTLIST joined-subclass node CDATA #IMPLIED>
+
+<!--
+ Union subclasses are used for the table-per-concrete-class mapping strategy
+
+ See the note on the class element regarding <pojo/> vs. @name usage...
+-->
+<!ELEMENT union-subclass (
+ meta*,
+ subselect?,
+ synchronize*,
+ comment?,
+ tuplizer*,
+ (property|many-to-one|one-to-one|component|dynamic-component|properties|any|map|set|list|bag|idbag|array|primitive-array)*,
+ union-subclass*,
+ loader?,sql-insert?,sql-update?,sql-delete?,
+ resultset*,
+ (query|sql-query)*
+)>
+ <!ATTLIST union-subclass entity-name CDATA #IMPLIED>
+ <!ATTLIST union-subclass name CDATA #IMPLIED>
+ <!ATTLIST union-subclass proxy CDATA #IMPLIED> <!-- default: no proxy interface -->
+ <!ATTLIST union-subclass table CDATA #IMPLIED> <!-- default: unqualified class name -->
+ <!ATTLIST union-subclass schema CDATA #IMPLIED>
+ <!ATTLIST union-subclass catalog CDATA #IMPLIED>
+ <!ATTLIST union-subclass subselect CDATA #IMPLIED>
+ <!ATTLIST union-subclass dynamic-update (true|false) "false">
+ <!ATTLIST union-subclass dynamic-insert (true|false) "false">
+ <!ATTLIST union-subclass select-before-update (true|false) "false">
+ <!ATTLIST union-subclass extends CDATA #IMPLIED> <!-- default: none when toplevel, otherwise the nearest class definition -->
+ <!ATTLIST union-subclass lazy (true|false) #IMPLIED>
+ <!ATTLIST union-subclass abstract (true|false) #IMPLIED>
+ <!ATTLIST union-subclass persister CDATA #IMPLIED>
+ <!ATTLIST union-subclass check CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST union-subclass batch-size CDATA #IMPLIED>
+ <!ATTLIST union-subclass node CDATA #IMPLIED>
+
+<!-- Property of an entity class or component, component-element, composite-id, etc.
+JavaBeans style properties are mapped to table columns. -->
+
+<!ELEMENT property (meta*,(column|formula)*,type?)>
+ <!ATTLIST property name CDATA #REQUIRED>
+ <!ATTLIST property node CDATA #IMPLIED>
+ <!ATTLIST property access CDATA #IMPLIED>
+ <!ATTLIST property type CDATA #IMPLIED>
+ <!ATTLIST property column CDATA #IMPLIED>
+ <!ATTLIST property length CDATA #IMPLIED>
+ <!ATTLIST property precision CDATA #IMPLIED>
+ <!ATTLIST property scale CDATA #IMPLIED>
+ <!ATTLIST property not-null (true|false) #IMPLIED>
+ <!ATTLIST property unique (true|false) "false">
+ <!ATTLIST property unique-key CDATA #IMPLIED>
+ <!ATTLIST property index CDATA #IMPLIED> <!-- include the columns spanned by this property in an index -->
+ <!ATTLIST property update (true|false) #IMPLIED>
+ <!ATTLIST property insert (true|false) #IMPLIED>
+ <!ATTLIST property optimistic-lock (true|false) "true"> <!-- only supported for properties of a class (not component) -->
+ <!ATTLIST property formula CDATA #IMPLIED>
+ <!ATTLIST property lazy (true|false) "false">
+ <!ATTLIST property generated (never|insert|always) "never">
+
+<!-- Declares the type of the containing property (overrides an eventually existing type
+attribute of the property). May contain param elements to customize a ParametrizableType. -->
+<!ELEMENT type (param*)>
+ <!ATTLIST type name CDATA #REQUIRED>
+
+<!-- Declares an association between two entities (Or from a component, component element,
+etc. to an entity). -->
+
+<!ELEMENT many-to-one (meta*,(column|formula)*)>
+ <!ATTLIST many-to-one name CDATA #REQUIRED>
+ <!ATTLIST many-to-one access CDATA #IMPLIED>
+ <!ATTLIST many-to-one class CDATA #IMPLIED>
+ <!ATTLIST many-to-one entity-name CDATA #IMPLIED>
+ <!ATTLIST many-to-one column CDATA #IMPLIED>
+ <!ATTLIST many-to-one not-null (true|false) #IMPLIED>
+ <!ATTLIST many-to-one unique (true|false) "false">
+ <!ATTLIST many-to-one unique-key CDATA #IMPLIED>
+ <!ATTLIST many-to-one index CDATA #IMPLIED>
+ <!ATTLIST many-to-one cascade CDATA #IMPLIED>
+ <!ATTLIST many-to-one outer-join (true|false|auto) #IMPLIED>
+ <!ATTLIST many-to-one fetch (join|select) #IMPLIED>
+ <!ATTLIST many-to-one update (true|false) "true">
+ <!ATTLIST many-to-one insert (true|false) "true">
+ <!ATTLIST many-to-one optimistic-lock (true|false) "true"> <!-- only supported for properties of a class (not component) -->
+ <!ATTLIST many-to-one foreign-key CDATA #IMPLIED>
+ <!ATTLIST many-to-one property-ref CDATA #IMPLIED>
+ <!ATTLIST many-to-one formula CDATA #IMPLIED>
+ <!ATTLIST many-to-one lazy (false|proxy|no-proxy) #IMPLIED>
+ <!ATTLIST many-to-one not-found (exception|ignore) "exception">
+ <!ATTLIST many-to-one node CDATA #IMPLIED>
+ <!ATTLIST many-to-one embed-xml (true|false) "true">
+
+<!-- Declares a one-to-one association between two entities (Or from a component,
+component element, etc. to an entity). -->
+
+<!ELEMENT one-to-one (meta*,formula*)>
+ <!ATTLIST one-to-one name CDATA #REQUIRED>
+ <!ATTLIST one-to-one formula CDATA #IMPLIED>
+ <!ATTLIST one-to-one access CDATA #IMPLIED>
+ <!ATTLIST one-to-one class CDATA #IMPLIED>
+ <!ATTLIST one-to-one entity-name CDATA #IMPLIED>
+ <!ATTLIST one-to-one cascade CDATA #IMPLIED>
+ <!ATTLIST one-to-one outer-join (true|false|auto) #IMPLIED>
+ <!ATTLIST one-to-one fetch (join|select) #IMPLIED>
+ <!ATTLIST one-to-one constrained (true|false) "false">
+ <!ATTLIST one-to-one foreign-key CDATA #IMPLIED>
+ <!ATTLIST one-to-one property-ref CDATA #IMPLIED>
+ <!ATTLIST one-to-one lazy (false|proxy|no-proxy) #IMPLIED>
+ <!ATTLIST one-to-one node CDATA #IMPLIED>
+ <!ATTLIST one-to-one embed-xml (true|false) "true">
+
+<!-- A property embedded in a composite identifier or map index (always not-null). -->
+
+<!ELEMENT key-property (meta*,column*,type?)>
+ <!ATTLIST key-property name CDATA #REQUIRED>
+ <!ATTLIST key-property access CDATA #IMPLIED>
+ <!ATTLIST key-property type CDATA #IMPLIED>
+ <!ATTLIST key-property column CDATA #IMPLIED>
+ <!ATTLIST key-property length CDATA #IMPLIED>
+ <!ATTLIST key-property node CDATA #IMPLIED>
+
+<!-- A many-to-one association embedded in a composite identifier or map index
+(always not-null, never cascade). -->
+
+<!ELEMENT key-many-to-one (meta*,column*)>
+ <!ATTLIST key-many-to-one name CDATA #REQUIRED>
+ <!ATTLIST key-many-to-one access CDATA #IMPLIED>
+ <!ATTLIST key-many-to-one class CDATA #IMPLIED>
+ <!ATTLIST key-many-to-one entity-name CDATA #IMPLIED>
+ <!ATTLIST key-many-to-one column CDATA #IMPLIED>
+ <!ATTLIST key-many-to-one foreign-key CDATA #IMPLIED>
+ <!ATTLIST key-many-to-one lazy (false|proxy) #IMPLIED>
+
+<!-- An "any" association is a polymorphic association to any table with
+the given identifier type. The first listed column is a VARCHAR column
+holding the name of the class (for that row). -->
+
+<!ELEMENT any (meta*,meta-value*,column,column+)>
+ <!ATTLIST any id-type CDATA #REQUIRED>
+ <!ATTLIST any meta-type CDATA #IMPLIED> <!--- default: Hibernate.STRING -->
+ <!ATTLIST any name CDATA #REQUIRED>
+ <!ATTLIST any access CDATA #IMPLIED>
+ <!ATTLIST any insert (true|false) "true">
+ <!ATTLIST any update (true|false) "true">
+ <!ATTLIST any cascade CDATA #IMPLIED>
+ <!ATTLIST any index CDATA #IMPLIED> <!-- include the columns spanned by this association in an index -->
+ <!ATTLIST any optimistic-lock (true|false) "true"> <!-- only supported for properties of a class (not component) -->
+ <!ATTLIST any lazy (true|false) "false">
+ <!ATTLIST any node CDATA #IMPLIED>
+
+<!ELEMENT meta-value EMPTY>
+ <!ATTLIST meta-value value CDATA #REQUIRED>
+ <!ATTLIST meta-value class CDATA #REQUIRED>
+
+<!-- A component is a user-defined class, persisted along with its containing entity
+to the table of the entity class. JavaBeans style properties of the component are
+mapped to columns of the table of the containing entity. A null component reference
+is mapped to null values in all columns and vice versa. Components do not support
+shared reference semantics. -->
+
+<!ELEMENT component (
+ meta*,
+ tuplizer*,
+ parent?,
+ (property|many-to-one|one-to-one|component|dynamic-component|any|map|set|list|bag|array|primitive-array)*
+)>
+ <!ATTLIST component class CDATA #IMPLIED>
+ <!ATTLIST component name CDATA #REQUIRED>
+ <!ATTLIST component access CDATA #IMPLIED>
+ <!ATTLIST component unique (true|false) "false">
+ <!ATTLIST component update (true|false) "true">
+ <!ATTLIST component insert (true|false) "true">
+ <!ATTLIST component lazy (true|false) "false">
+ <!ATTLIST component optimistic-lock (true|false) "true">
+ <!ATTLIST component node CDATA #IMPLIED>
+
+<!-- A dynamic-component maps columns of the database entity to a java.util.Map
+at the Java level -->
+
+<!ELEMENT dynamic-component (
+ (property|many-to-one|one-to-one|component|dynamic-component|any|map|set|list|bag|array|primitive-array)*
+)>
+ <!ATTLIST dynamic-component name CDATA #REQUIRED>
+ <!ATTLIST dynamic-component access CDATA #IMPLIED>
+ <!ATTLIST dynamic-component unique (true|false) "false">
+ <!ATTLIST dynamic-component update (true|false) "true">
+ <!ATTLIST dynamic-component insert (true|false) "true">
+ <!ATTLIST dynamic-component optimistic-lock (true|false) "true">
+ <!ATTLIST dynamic-component node CDATA #IMPLIED>
+
+<!-- properties declares that the contained properties form an alternate key. The name
+attribute allows an alternate key to be used as the target of a property-ref. -->
+
+<!ELEMENT properties (
+ (property|many-to-one|component|dynamic-component)*
+)>
+ <!ATTLIST properties name CDATA #REQUIRED>
+ <!ATTLIST properties unique (true|false) "false">
+ <!ATTLIST properties insert (true|false) "true">
+ <!ATTLIST properties update (true|false) "true">
+ <!ATTLIST properties optimistic-lock (true|false) "true">
+ <!ATTLIST properties node CDATA #IMPLIED>
+
+<!-- The parent element maps a property of the component class as a pointer back to
+the owning entity. -->
+
+<!ELEMENT parent EMPTY>
+ <!ATTLIST parent name CDATA #REQUIRED>
+
+<!-- Collection declarations nested inside a class declaration indicate a foreign key
+relationship from the collection table to the enclosing class. -->
+
+<!ELEMENT map (
+ meta*,
+ subselect?,
+ cache?,
+ synchronize*,
+ comment?,
+ key,
+ (map-key|composite-map-key|map-key-many-to-many|index|composite-index|index-many-to-many|index-many-to-any),
+ (element|one-to-many|many-to-many|composite-element|many-to-any),
+ loader?,sql-insert?,sql-update?,sql-delete?,sql-delete-all?,
+ filter*
+)>
+ <!ATTLIST map name CDATA #REQUIRED>
+ <!ATTLIST map access CDATA #IMPLIED>
+ <!ATTLIST map table CDATA #IMPLIED> <!-- default: name -->
+ <!ATTLIST map schema CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST map subselect CDATA #IMPLIED>
+ <!ATTLIST map catalog CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST map lazy (true|false|extra) #IMPLIED>
+ <!ATTLIST map mutable (true|false) "true">
+ <!ATTLIST map inverse (true|false) "false">
+ <!ATTLIST map sort CDATA "unsorted"> <!-- unsorted|natural|"comparator class", default: unsorted -->
+ <!ATTLIST map cascade CDATA #IMPLIED>
+ <!ATTLIST map order-by CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST map where CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST map batch-size CDATA #IMPLIED>
+ <!ATTLIST map outer-join (true|false|auto) #IMPLIED>
+ <!ATTLIST map fetch (join|select|subselect) #IMPLIED>
+ <!ATTLIST map check CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST map persister CDATA #IMPLIED>
+ <!ATTLIST map collection-type CDATA #IMPLIED>
+ <!ATTLIST map optimistic-lock (true|false) "true"> <!-- only supported for properties of a class (not component) -->
+ <!ATTLIST map node CDATA #IMPLIED>
+ <!ATTLIST map embed-xml (true|false) "true">
+
+<!ELEMENT set (
+ meta*,
+ subselect?,
+ cache?,
+ synchronize*,
+ comment?,
+ key,
+ (element|one-to-many|many-to-many|composite-element|many-to-any),
+ loader?,sql-insert?,sql-update?,sql-delete?,sql-delete-all?,
+ filter*
+)>
+ <!ATTLIST set name CDATA #REQUIRED>
+ <!ATTLIST set access CDATA #IMPLIED>
+ <!ATTLIST set table CDATA #IMPLIED> <!-- default: name -->
+ <!ATTLIST set schema CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST set catalog CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST set subselect CDATA #IMPLIED>
+ <!ATTLIST set lazy (true|false|extra) #IMPLIED>
+ <!ATTLIST set sort CDATA "unsorted"> <!-- unsorted|natural|"comparator class" -->
+ <!ATTLIST set inverse (true|false) "false">
+ <!ATTLIST set mutable (true|false) "true">
+ <!ATTLIST set cascade CDATA #IMPLIED>
+ <!ATTLIST set order-by CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST set where CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST set batch-size CDATA #IMPLIED>
+ <!ATTLIST set outer-join (true|false|auto) #IMPLIED>
+ <!ATTLIST set fetch (join|select|subselect) #IMPLIED>
+ <!ATTLIST set persister CDATA #IMPLIED>
+ <!ATTLIST set collection-type CDATA #IMPLIED>
+ <!ATTLIST set check CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST set optimistic-lock (true|false) "true"> <!-- only supported for properties of a class (not component) -->
+ <!ATTLIST set node CDATA #IMPLIED>
+ <!ATTLIST set embed-xml (true|false) "true">
+
+<!ELEMENT bag (
+ meta*,
+ subselect?,
+ cache?,
+ synchronize*,
+ comment?,
+ key,
+ (element|one-to-many|many-to-many|composite-element|many-to-any),
+ loader?,sql-insert?,sql-update?,sql-delete?,sql-delete-all?,
+ filter*
+)>
+ <!ATTLIST bag name CDATA #REQUIRED>
+ <!ATTLIST bag access CDATA #IMPLIED>
+ <!ATTLIST bag table CDATA #IMPLIED> <!-- default: name -->
+ <!ATTLIST bag schema CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST bag catalog CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST bag subselect CDATA #IMPLIED>
+ <!ATTLIST bag lazy (true|false|extra) #IMPLIED>
+ <!ATTLIST bag inverse (true|false) "false">
+ <!ATTLIST bag mutable (true|false) "true">
+ <!ATTLIST bag cascade CDATA #IMPLIED>
+ <!ATTLIST bag order-by CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST bag where CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST bag batch-size CDATA #IMPLIED>
+ <!ATTLIST bag outer-join (true|false|auto) #IMPLIED>
+ <!ATTLIST bag fetch (join|select|subselect) #IMPLIED>
+ <!ATTLIST bag persister CDATA #IMPLIED>
+ <!ATTLIST bag collection-type CDATA #IMPLIED>
+ <!ATTLIST bag check CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST bag optimistic-lock (true|false) "true"> <!-- only supported for properties of a class (not component) -->
+ <!ATTLIST bag node CDATA #IMPLIED>
+ <!ATTLIST bag embed-xml (true|false) "true">
+
+<!ELEMENT idbag (
+ meta*,
+ subselect?,
+ cache?,
+ synchronize*,
+ comment?,
+ collection-id,
+ key,
+ (element|many-to-many|composite-element|many-to-any),
+ loader?,sql-insert?,sql-update?,sql-delete?,sql-delete-all?,
+ filter*
+)>
+ <!ATTLIST idbag name CDATA #REQUIRED>
+ <!ATTLIST idbag access CDATA #IMPLIED>
+ <!ATTLIST idbag table CDATA #IMPLIED> <!-- default: name -->
+ <!ATTLIST idbag schema CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST idbag catalog CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST idbag subselect CDATA #IMPLIED>
+ <!ATTLIST idbag lazy (true|false|extra) #IMPLIED>
+ <!ATTLIST idbag mutable (true|false) "true">
+ <!ATTLIST idbag cascade CDATA #IMPLIED>
+ <!ATTLIST idbag order-by CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST idbag where CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST idbag batch-size CDATA #IMPLIED>
+ <!ATTLIST idbag outer-join (true|false|auto) #IMPLIED>
+ <!ATTLIST idbag fetch (join|select|subselect) #IMPLIED>
+ <!ATTLIST idbag persister CDATA #IMPLIED>
+ <!ATTLIST idbag collection-type CDATA #IMPLIED>
+ <!ATTLIST idbag check CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST idbag optimistic-lock (true|false) "true"> <!-- only supported for properties of a class (not component) -->
+ <!ATTLIST idbag node CDATA #IMPLIED>
+ <!ATTLIST idbag embed-xml (true|false) "true">
+
+<!ELEMENT list (
+ meta*,
+ subselect?,
+ cache?,
+ synchronize*,
+ comment?,
+ key,
+ (index|list-index),
+ (element|one-to-many|many-to-many|composite-element|many-to-any),
+ loader?,sql-insert?,sql-update?,sql-delete?,sql-delete-all?,
+ filter*
+)>
+ <!ATTLIST list name CDATA #REQUIRED>
+ <!ATTLIST list access CDATA #IMPLIED>
+ <!ATTLIST list table CDATA #IMPLIED> <!-- default: name -->
+ <!ATTLIST list schema CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST list catalog CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST list subselect CDATA #IMPLIED>
+ <!ATTLIST list lazy (true|false|extra) #IMPLIED>
+ <!ATTLIST list inverse (true|false) "false">
+ <!ATTLIST list mutable (true|false) "true">
+ <!ATTLIST list cascade CDATA #IMPLIED>
+ <!ATTLIST list where CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST list batch-size CDATA #IMPLIED>
+ <!ATTLIST list outer-join (true|false|auto) #IMPLIED>
+ <!ATTLIST list fetch (join|select|subselect) #IMPLIED>
+ <!ATTLIST list persister CDATA #IMPLIED>
+ <!ATTLIST list collection-type CDATA #IMPLIED>
+ <!ATTLIST list check CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST list optimistic-lock (true|false) "true"> <!-- only supported for properties of a class (not component) -->
+ <!ATTLIST list node CDATA #IMPLIED>
+ <!ATTLIST list embed-xml (true|false) "true">
+
+<!ELEMENT array (
+ meta*,
+ subselect?,
+ cache?,
+ synchronize*,
+ comment?,
+ key,
+ (index|list-index),
+ (element|one-to-many|many-to-many|composite-element|many-to-any),
+ loader?,sql-insert?,sql-update?,sql-delete?,sql-delete-all?
+)>
+ <!ATTLIST array name CDATA #REQUIRED>
+ <!ATTLIST array access CDATA #IMPLIED>
+ <!ATTLIST array table CDATA #IMPLIED> <!-- default: name -->
+ <!ATTLIST array schema CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST array catalog CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST array subselect CDATA #IMPLIED>
+ <!ATTLIST array inverse (true|false) "false">
+ <!ATTLIST array mutable (true|false) "true">
+ <!ATTLIST array element-class CDATA #IMPLIED>
+ <!ATTLIST array cascade CDATA #IMPLIED>
+ <!ATTLIST array where CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST array batch-size CDATA #IMPLIED>
+ <!ATTLIST array outer-join (true|false|auto) #IMPLIED>
+ <!ATTLIST array fetch (join|select|subselect) #IMPLIED>
+ <!ATTLIST array persister CDATA #IMPLIED>
+ <!ATTLIST array collection-type CDATA #IMPLIED>
+ <!ATTLIST array check CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST array optimistic-lock (true|false) "true"> <!-- only supported for properties of a class (not component) -->
+ <!ATTLIST array node CDATA #IMPLIED>
+ <!ATTLIST array embed-xml (true|false) "true">
+
+<!ELEMENT primitive-array (
+ meta*,
+ subselect?,
+ cache?,
+ synchronize*,
+ comment?,
+ key,
+ (index|list-index),
+ element,
+ loader?,sql-insert?,sql-update?,sql-delete?,sql-delete-all?
+)>
+ <!ATTLIST primitive-array name CDATA #REQUIRED>
+ <!ATTLIST primitive-array access CDATA #IMPLIED>
+ <!ATTLIST primitive-array table CDATA #IMPLIED> <!-- default: name -->
+ <!ATTLIST primitive-array schema CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST primitive-array catalog CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST primitive-array subselect CDATA #IMPLIED>
+ <!ATTLIST primitive-array mutable (true|false) "true">
+ <!ATTLIST primitive-array where CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST primitive-array batch-size CDATA #IMPLIED>
+ <!ATTLIST primitive-array outer-join (true|false|auto) #IMPLIED>
+ <!ATTLIST primitive-array fetch (join|select|subselect) #IMPLIED>
+ <!ATTLIST primitive-array persister CDATA #IMPLIED>
+ <!ATTLIST primitive-array collection-type CDATA #IMPLIED>
+ <!ATTLIST primitive-array check CDATA #IMPLIED> <!-- default: none -->
+ <!ATTLIST primitive-array optimistic-lock (true|false) "true"> <!-- only supported for properties of a class (not component) -->
+ <!ATTLIST primitive-array node CDATA #IMPLIED>
+ <!ATTLIST primitive-array embed-xml (true|false) "true">
+
+<!-- Declares the element type of a collection of basic type -->
+
+<!ELEMENT element ( (column|formula)*, type? )>
+ <!ATTLIST element column CDATA #IMPLIED>
+ <!ATTLIST element node CDATA #IMPLIED>
+ <!ATTLIST element formula CDATA #IMPLIED>
+ <!ATTLIST element type CDATA #IMPLIED>
+ <!ATTLIST element length CDATA #IMPLIED>
+ <!ATTLIST element precision CDATA #IMPLIED>
+ <!ATTLIST element scale CDATA #IMPLIED>
+ <!ATTLIST element not-null (true|false) "false">
+ <!ATTLIST element unique (true|false) "false">
+
+<!-- One to many association. This tag declares the entity-class
+element type of a collection and specifies a one-to-many relational model -->
+
+<!ELEMENT one-to-many EMPTY>
+ <!ATTLIST one-to-many class CDATA #IMPLIED>
+ <!ATTLIST one-to-many not-found (exception|ignore) "exception">
+ <!ATTLIST one-to-many node CDATA #IMPLIED>
+ <!ATTLIST one-to-many embed-xml (true|false) "true">
+ <!ATTLIST one-to-many entity-name CDATA #IMPLIED>
+ <!-- No column declaration attributes required in this case. The primary
+ key column of the associated class is already mapped elsewhere.-->
+
+<!-- Many to many association. This tag declares the entity-class
+element type of a collection and specifies a many-to-many relational model -->
+
+<!ELEMENT many-to-many (meta*,(column|formula)*,filter*)>
+ <!ATTLIST many-to-many class CDATA #IMPLIED>
+ <!ATTLIST many-to-many node CDATA #IMPLIED>
+ <!ATTLIST many-to-many embed-xml (true|false) "true">
+ <!ATTLIST many-to-many entity-name CDATA #IMPLIED>
+ <!ATTLIST many-to-many column CDATA #IMPLIED>
+ <!ATTLIST many-to-many formula CDATA #IMPLIED>
+ <!ATTLIST many-to-many not-found (exception|ignore) "exception">
+ <!ATTLIST many-to-many outer-join (true|false|auto) #IMPLIED>
+ <!ATTLIST many-to-many fetch (join|select) #IMPLIED>
+ <!ATTLIST many-to-many lazy (false|proxy) #IMPLIED>
+ <!ATTLIST many-to-many foreign-key CDATA #IMPLIED>
+ <!ATTLIST many-to-many unique (true|false) "false">
+ <!ATTLIST many-to-many where CDATA #IMPLIED>
+ <!ATTLIST many-to-many order-by CDATA #IMPLIED>
+ <!ATTLIST many-to-many property-ref CDATA #IMPLIED>
+
+<!-- A composite element allows a collection to hold instances of an arbitrary
+class, without the requirement of joining to an entity table. Composite elements
+have component semantics - no shared references and ad hoc null value semantics.
+Composite elements may not hold nested collections. -->
+
+<!ELEMENT composite-element (
+ (meta*),
+ parent?,
+ (property|many-to-one|any|nested-composite-element)*
+)>
+ <!ATTLIST composite-element class CDATA #REQUIRED>
+ <!ATTLIST composite-element node CDATA #IMPLIED>
+
+<!ELEMENT nested-composite-element (
+ parent?,
+ (property|many-to-one|any|nested-composite-element)*
+)>
+ <!ATTLIST nested-composite-element class CDATA #REQUIRED>
+ <!ATTLIST nested-composite-element name CDATA #REQUIRED>
+ <!ATTLIST nested-composite-element access CDATA #IMPLIED>
+ <!ATTLIST nested-composite-element node CDATA #IMPLIED>
+
+<!-- Declares the column name of a foreign key. -->
+
+<!ELEMENT key (column*)>
+ <!ATTLIST key column CDATA #IMPLIED>
+ <!ATTLIST key property-ref CDATA #IMPLIED>
+ <!ATTLIST key foreign-key CDATA #IMPLIED>
+ <!ATTLIST key on-delete (cascade|noaction) "noaction">
+ <!ATTLIST key not-null (true|false) #IMPLIED>
+ <!ATTLIST key update (true|false) #IMPLIED>
+ <!ATTLIST key unique (true|false) #IMPLIED>
+
+<!-- Declares the type and column mapping for a collection index (array or
+list index, or key of a map). -->
+
+<!ELEMENT list-index (column?)>
+ <!ATTLIST list-index column CDATA #IMPLIED>
+ <!ATTLIST list-index base CDATA "0">
+
+<!ELEMENT map-key ((column|formula)*)>
+ <!ATTLIST map-key column CDATA #IMPLIED>
+ <!ATTLIST map-key formula CDATA #IMPLIED>
+ <!ATTLIST map-key type CDATA #REQUIRED>
+ <!ATTLIST map-key length CDATA #IMPLIED>
+ <!ATTLIST map-key node CDATA #IMPLIED>
+
+<!ELEMENT index (column*)>
+ <!ATTLIST index column CDATA #IMPLIED>
+ <!ATTLIST index type CDATA #IMPLIED> <!-- required for maps -->
+ <!ATTLIST index length CDATA #IMPLIED>
+
+<!-- Many to many association mapped to the key of a map. ie. a map keyed
+on entities. -->
+
+<!ELEMENT map-key-many-to-many ((column|formula)*)>
+ <!ATTLIST map-key-many-to-many class CDATA #IMPLIED>
+ <!ATTLIST map-key-many-to-many entity-name CDATA #IMPLIED>
+ <!ATTLIST map-key-many-to-many column CDATA #IMPLIED>
+ <!ATTLIST map-key-many-to-many formula CDATA #IMPLIED>
+ <!ATTLIST map-key-many-to-many foreign-key CDATA #IMPLIED>
+
+<!ELEMENT index-many-to-many (column*)>
+ <!ATTLIST index-many-to-many class CDATA #REQUIRED>
+ <!ATTLIST index-many-to-many entity-name CDATA #IMPLIED>
+ <!ATTLIST index-many-to-many column CDATA #IMPLIED>
+ <!ATTLIST index-many-to-many foreign-key CDATA #IMPLIED>
+
+<!-- Composite index of a map ie. a map keyed on components. -->
+
+<!ELEMENT composite-map-key ( (key-property|key-many-to-one)+ )>
+ <!ATTLIST composite-map-key class CDATA #REQUIRED>
+
+<!ELEMENT composite-index ( (key-property|key-many-to-one)+ )>
+ <!ATTLIST composite-index class CDATA #REQUIRED>
+
+<!-- A "many to any" defines a polymorphic association to any table
+with the given identifier type. The first listed column is a VARCHAR column
+holding the name of the class (for that row). -->
+
+<!ELEMENT many-to-any (meta-value*,column, column+)>
+ <!ATTLIST many-to-any id-type CDATA #REQUIRED>
+ <!ATTLIST many-to-any meta-type CDATA #IMPLIED> <!--- default: Hibernate.CLASS -->
+
+<!ELEMENT index-many-to-any (column, column+)>
+ <!ATTLIST index-many-to-any id-type CDATA #REQUIRED>
+ <!ATTLIST index-many-to-any meta-type CDATA #IMPLIED> <!--- default: Hibernate.CLASS -->
+
+<!ELEMENT collection-id (meta*, column*, generator)>
+ <!ATTLIST collection-id column CDATA #REQUIRED>
+ <!ATTLIST collection-id type CDATA #REQUIRED>
+ <!ATTLIST collection-id length CDATA #IMPLIED>
+
+<!-- Generators generate unique identifiers. The class attribute specifies a Java
+class implementing an id generation algorithm. -->
+
+<!ELEMENT generator (param*)>
+ <!ATTLIST generator class CDATA #REQUIRED>
+<!ELEMENT param (#PCDATA)>
+ <!ATTLIST param name CDATA #REQUIRED>
+
+<!-- The column element is an alternative to column attributes and required for
+mapping associations to classes with composite ids. -->
+
+<!ELEMENT column (comment?)>
+ <!ATTLIST column name CDATA #REQUIRED>
+ <!ATTLIST column length CDATA #IMPLIED> <!-- default: 255 -->
+ <!ATTLIST column precision CDATA #IMPLIED>
+ <!ATTLIST column scale CDATA #IMPLIED>
+ <!ATTLIST column not-null (true|false) #IMPLIED> <!-- default: false (except for id properties) -->
+ <!ATTLIST column unique (true|false) #IMPLIED> <!-- default: false (except for id properties) -->
+ <!ATTLIST column unique-key CDATA #IMPLIED> <!-- default: no unique key -->
+ <!ATTLIST column sql-type CDATA #IMPLIED> <!-- override default column type for hibernate type -->
+ <!ATTLIST column index CDATA #IMPLIED>
+ <!ATTLIST column check CDATA #IMPLIED> <!-- default: no check constraint -->
+ <!ATTLIST column default CDATA #IMPLIED> <!-- default: no default value -->
+
+<!-- The formula and subselect elements allow us to map derived properties and
+entities. -->
+
+<!ELEMENT formula (#PCDATA)>
+<!ELEMENT subselect (#PCDATA)>
+
+<!-- The cache element enables caching of an entity class. -->
+<!ELEMENT cache EMPTY>
+ <!ATTLIST cache usage (read-only|read-write|nonstrict-read-write|transactional) #REQUIRED>
+ <!ATTLIST cache region CDATA #IMPLIED> <!-- default: class or collection role name -->
+ <!ATTLIST cache include (all|non-lazy) "all">
+
+<!-- The comment element allows definition of a database table or column comment. -->
+
+<!ELEMENT comment (#PCDATA)>
+
+<!-- The loader element allows specification of a named query to be used for fetching
+an entity or collection -->
+
+<!ELEMENT loader EMPTY>
+ <!ATTLIST loader query-ref CDATA #REQUIRED>
+
+<!-- The query element declares a named Hibernate query string -->
+
+<!ELEMENT query (#PCDATA|query-param)*>
+ <!ATTLIST query name CDATA #REQUIRED>
+ <!ATTLIST query flush-mode (auto|never|always) #IMPLIED>
+ <!ATTLIST query cacheable (true|false) "false">
+ <!ATTLIST query cache-region CDATA #IMPLIED>
+ <!ATTLIST query fetch-size CDATA #IMPLIED>
+ <!ATTLIST query timeout CDATA #IMPLIED>
+ <!ATTLIST query cache-mode (get|ignore|normal|put|refresh) #IMPLIED>
+ <!ATTLIST query read-only (true|false) #IMPLIED>
+ <!ATTLIST query comment CDATA #IMPLIED>
+
+<!-- The sql-query element declares a named SQL query string -->
+
+<!ELEMENT sql-query (#PCDATA|return-scalar|return|return-join|load-collection|synchronize|query-param)*>
+ <!ATTLIST sql-query name CDATA #REQUIRED>
+ <!ATTLIST sql-query resultset-ref CDATA #IMPLIED>
+ <!ATTLIST sql-query flush-mode (auto|never|always) #IMPLIED>
+ <!ATTLIST sql-query cacheable (true|false) "false">
+ <!ATTLIST sql-query cache-region CDATA #IMPLIED>
+ <!ATTLIST sql-query fetch-size CDATA #IMPLIED>
+ <!ATTLIST sql-query timeout CDATA #IMPLIED>
+ <!ATTLIST sql-query cache-mode (get|ignore|normal|put|refresh) #IMPLIED>
+ <!ATTLIST sql-query read-only (true|false) #IMPLIED>
+ <!ATTLIST sql-query comment CDATA #IMPLIED>
+ <!ATTLIST sql-query callable (true|false) "false">
+
+<!-- The query-param element is used only by tools that generate
+finder methods for named queries -->
+
+<!ELEMENT query-param EMPTY>
+ <!ATTLIST query-param name CDATA #REQUIRED>
+ <!ATTLIST query-param type CDATA #REQUIRED>
+
+<!-- The resultset element declares a named resultset mapping definition for SQL queries -->
+<!ELEMENT resultset (return-scalar|return|return-join|load-collection)*>
+ <!ATTLIST resultset name CDATA #REQUIRED>
+
+<!--
+ Defines a return component for a sql-query. Alias refers to the alias
+ used in the actual sql query; lock-mode specifies the locking to be applied
+ when the query is executed. The class, collection, and role attributes are mutually exclusive;
+ class refers to the class name of a "root entity" in the object result; collection refers
+ to a collection of a given class and is used to define custom sql to load that owned collection
+ and takes the form "ClassName.propertyName"; role refers to the property path for an eager fetch
+ and takes the form "owningAlias.propertyName"
+-->
+<!ELEMENT return (return-discriminator?,return-property)*>
+ <!ATTLIST return alias CDATA #IMPLIED>
+ <!ATTLIST return entity-name CDATA #IMPLIED>
+ <!ATTLIST return class CDATA #IMPLIED>
+ <!ATTLIST return lock-mode (none|read|upgrade|upgrade-nowait|write) "read">
+
+<!ELEMENT return-property (return-column*)>
+ <!ATTLIST return-property name CDATA #REQUIRED>
+ <!ATTLIST return-property column CDATA #IMPLIED>
+
+<!ELEMENT return-column EMPTY>
+ <!ATTLIST return-column name CDATA #REQUIRED>
+
+<!ELEMENT return-discriminator EMPTY>
+ <!ATTLIST return-discriminator column CDATA #REQUIRED>
+
+<!ELEMENT return-join (return-property)*>
+ <!ATTLIST return-join alias CDATA #REQUIRED>
+ <!ATTLIST return-join property CDATA #REQUIRED>
+ <!ATTLIST return-join lock-mode (none|read|upgrade|upgrade-nowait|write) "read">
+
+<!ELEMENT load-collection (return-property)*>
+ <!ATTLIST load-collection alias CDATA #REQUIRED>
+ <!ATTLIST load-collection role CDATA #REQUIRED>
+ <!ATTLIST load-collection lock-mode (none|read|upgrade|upgrade-nowait|write) "read">
+
+<!ELEMENT return-scalar EMPTY>
+ <!ATTLIST return-scalar column CDATA #REQUIRED>
+ <!ATTLIST return-scalar type CDATA #IMPLIED>
+
+<!ELEMENT synchronize EMPTY>
+ <!ATTLIST synchronize table CDATA #REQUIRED>
+
+<!-- custom sql operations -->
+<!ELEMENT sql-insert (#PCDATA)>
+ <!ATTLIST sql-insert callable (true|false) "false">
+ <!ATTLIST sql-insert check (none|rowcount|param) #IMPLIED>
+
+<!ELEMENT sql-update (#PCDATA)>
+ <!ATTLIST sql-update callable (true|false) "false">
+ <!ATTLIST sql-update check (none|rowcount|param) #IMPLIED>
+
+<!ELEMENT sql-delete (#PCDATA)>
+ <!ATTLIST sql-delete callable (true|false) "false">
+ <!ATTLIST sql-delete check (none|rowcount|param) #IMPLIED>
+
+<!ELEMENT sql-delete-all (#PCDATA)>
+ <!ATTLIST sql-delete-all callable (true|false) "false">
+ <!ATTLIST sql-delete-all check (none|rowcount|param) #IMPLIED>
+
+<!--
+ Element for defining "auxiliary" database objects. Must be one of two forms:
+
+ #1 :
+ <database-object>
+ <definition class="CustomClassExtendingAuxiliaryObject"/>
+ </database-object>
+
+ #2 :
+ <database-object>
+ <create>CREATE OR REPLACE ....</create>
+ <drop>DROP ....</drop>
+ </database-object>
+-->
+<!ELEMENT database-object ( (definition|(create,drop)), dialect-scope* )>
+
+<!ELEMENT definition EMPTY>
+ <!ATTLIST definition class CDATA #REQUIRED>
+
+<!ELEMENT create (#PCDATA)>
+<!ELEMENT drop (#PCDATA)>
+
+<!--
+ dialect-scope element allows scoping auxiliary-objects to a particular
+ Hibernate dialect implementation.
+-->
+<!ELEMENT dialect-scope (#PCDATA)>
+ <!ATTLIST dialect-scope name CDATA #REQUIRED>
+
Added: trunk/Hibernate3/documentation/manual/README
===================================================================
--- trunk/Hibernate3/documentation/manual/README (rev 0)
+++ trunk/Hibernate3/documentation/manual/README 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,176 @@
+THE HIBERNATE DOCUMENTATION
+christian at hibernate.org
+
+COPYRIGHT NOTICE: This documentation system and all its source files are
+licensed under the GNU Lesser Public License (LGPL). Authors and translators
+retain the copyright of their work. All font and other build files (the DocBook
+system) are property of their respective copyright holders. Some of the files
+(especially font files) might require a license from the respective vendor; you
+are responsible to check and obtain these licenses as necessary before you use
+and/or distribute these files.
+
+
+Preface
+
+The Hibernate documentation is a modular documentation, it uses
+various XML files (written with the DocBook DTD) and a Java-based
+build process to generate HTML and PDF output. Use a simple text
+editor with XML support, such as JEdit, to edit the source files. You
+will need Java and Ant installed for the output generation. The toolset
+is Java only and should work on any operating system.
+
+Note: Always use 4 spaces to indent, no tabstops (code examples will
+be broken otherwise).
+
+
+1. How to get it
+
+Check out a copy of Hibernate from the repository. A regular
+Hibernate download will not contain the build process for the
+documentation, only the PDF/HTML output, use the repository!
+See http://www.hibernate.org/Download/DownloadOverview
+
+
+2. Working on the original language
+
+The original and master language is English, hence the "en" subdirectory
+in /doc/reference/ is authorative. We use "id" and "revision" attributes on
+XML elements to track changes. Here are the rules, they are mandatory:
+
+2a. Changing existing content involves an update of the "revision" of the XML
+element you are working on (e.g. a <sect1>, <sect2> or even a <para>).
+
+If a <sect1> has a revision="1", you update it to "2" after updating the
+content in that section.
+
+You can also add a revision attribute to an element if there is none,
+start with revision="1". You should not add a revision attribute to each
+paragraph, try to only add/use revision attributes to sections. You can'
+t add a revision attribute to elements without an "id" attribute!
+
+2b. Adding new content involves adding new elements (even new files), such
+as <sect1>, <para> and so on. Any new element (or its new parent element)
+needs an "id" attribute if the new content is to be included in the change
+tracking. If you add a section, give it a unique short text
+identifer, look at the parent element's identifier for the common prefix.
+
+2c. Deleting content involves removing old elements. Just remove them and
+make sure that the parent elements revision is updated, if the removed
+element did not itself have an identifer and a revision. If you remove an
+element with its own identifier, everything is fine and no other changes are
+necessary.
+
+
+3. Starting a new language
+
+If you start a translation for a new language, you have to copy
+the default language (English) and start an initial translation.
+
+3a. First, duplicate the default language "en" by duplicating the directory
+/doc/reference/en. For example, a new German translation
+will be a copy of that directory in /doc/reference/de. We use the ISO
+codes to name the language subdirectories.
+
+3b. You also have to add your new language to the language build file,
+/doc/reference/build.xml. Look for the lines that have a "TRANSLATOR"
+comment and duplicate them. Change the default "en" to your language
+code, every language listed here will be included in both the PDF/HTML
+generation and the revision diff change tracking reports (discussed later).
+
+
+4. The initial translation
+
+If you just copied the default language, start translating the DocBook
+XML modules and illustrations in the new language subdirectory. For
+example, all modules for German would be in /doc/reference/de/modules
+and all illustrations in /doc/reference/de/images, note that you also have
+to translate the master.xml in your language subdirectory.
+
+The initial translation is straightforward: Translate all modules and
+all illustrations, but don't add any files, don't add any new XML elements
+(like a section or a chapter, not even a paragraph). Simply translate
+sentence by sentence. This is very important.
+
+Note that every DocBook XML file needs an encoding, specific to a
+language. Add a line like this at the top of every file, if it doesn't exist:
+<?xml version='1.0' encoding="iso-8859-1"?>
+
+You can use UTF-8 or any other character set, please experiment with
+the builds to see what works for you.
+
+If you need a new section or paragraph, because your translation requires
+more explanation, you can add it if you also add an "id" and a "revision"
+to that new section or paragraph.
+
+For example, if you add a new <para> element to the existing document,
+give it an identifier, a short unique string that extends the identifier
+string of the parent element: <para id="queryhql-projection-specialnote">
+would be a special paragraph in the <sect1 id="queryhql-projection">
+section in the chapter <chapter id="queryhql">.
+
+Never add a new element in a translated version without also adding a new
+unique identifier value! Also, you have to mark this new element as "only
+relevant in the translated version". Simply set the "revision" attribute of
+your new element to "-1". For example, set the previously created
+paragraph to "only relevant in the translation" by declaring
+<para id="queryhql-projection-specialnote" revision="-1">.
+Changes to that paragraph will not be tracked, it is your responsibility to
+watch out for neccessary updates. Any element with revision="-1" will not be
+tracked.
+
+
+5. Updating translated documentation
+
+Translators get updates by updating their working directory from the
+repository. As a translator you will get an e-mail from us when translation
+is required, you can then update your copy. Or, subscribe to the commit
+mailing list to get all updates automatically.
+
+The documentation tools can generate a report after you updated
+from the repository and show you what needs to be translated and/or removed
+in your local translation copy. To generate that report, run "ant all.revdiff"
+in the doc/reference/ subdirectory. Click on the generated HTML report
+file for your language and you will see what has to be updated and/or
+removed.
+
+If the report indicates that content in the original has been removed,
+simply remove the identified XML element from your language modules.
+
+If the report detects a new revision, open the file that has been updated
+in your translation, find the identified XML element and update/translate
+its contents. Important: Make sure you also update the "revision"
+attribute of that XML element by setting it to the same version as in
+the original file, hence both the original XML file and your translated
+file should have the same revision number for all elements. If an
+XML element in your translation doesn't have a revision, but the original
+file has, add a new "revision" attribute to your XML element.
+The HTML report shows the identifiers and revisions for both the original
+and the translated files, use it to compare.
+
+Rerun the "ant all.revdiff" report generation as often as you like until
+no more differences are detected. You should always try to get your
+copy clean, with all updated revisions and all identified elements
+synchronzied.
+
+
+6. Committing a translation
+
+All translators will be asked to submit their translated versions from
+time to time. This will be a manual process, you will get an e-mail from
+the Hibernate team and simply send your language subdirectory as
+a ZIP file to us. It will then be integrated in the main Hibernate
+distribution and on the website. Or, you can contact us for commit access
+to the repository, where you can maintain a translation directly.
+
+
+7. Generating PDF and HTML output
+
+The documentation is generated with the target 'ant all.doc'.
+
+To build the reference docs for a particular language only, use
+"ant -Dlang=en", for example, and call either lang.all, lang.docpdf,
+lang.dochtml, or lang.dochtmlsingle for the target of your choice.
+
+You can also call lang.section-check to track down missing identifiers in
+a particular language, or you can call lang.revdiff to get a difference
+report for a particular language, compared with the English reference.
Added: trunk/Hibernate3/documentation/manual/en-US/pom.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/en-US/pom.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/en-US/pom.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-manual</artifactId>
+ <version>3.3.0.beta1</version>
+ <relativePath>../../../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>hibernate-manual-${translation}</artifactId>
+ <packaging>pom</packaging>
+ <name>Hibernate Manual (${translation})</name>
+
+ <properties>
+ <translation>en-US</translation>
+ </properties>
+
+</project>
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/master.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/master.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/master.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,183 @@
+<?xml version='1.0' encoding="iso-8859-1"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3CR3//EN"
+ "../support/docbook-dtd/docbookx.dtd"
+[
+<!ENTITY tutorial SYSTEM "modules/tutorial.xml">
+<!ENTITY architecture SYSTEM "modules/architecture.xml">
+<!ENTITY configuration SYSTEM "modules/configuration.xml">
+<!ENTITY persistent-classes SYSTEM "modules/persistent_classes.xml">
+<!ENTITY basic-mapping SYSTEM "modules/basic_mapping.xml">
+<!ENTITY collection-mapping SYSTEM "modules/collection_mapping.xml">
+<!ENTITY association-mapping SYSTEM "modules/association_mapping.xml">
+<!ENTITY component-mapping SYSTEM "modules/component_mapping.xml">
+<!ENTITY inheritance-mapping SYSTEM "modules/inheritance_mapping.xml">
+<!ENTITY session-api SYSTEM "modules/session_api.xml">
+<!ENTITY transactions SYSTEM "modules/transactions.xml">
+<!ENTITY events SYSTEM "modules/events.xml">
+<!ENTITY batch SYSTEM "modules/batch.xml">
+<!ENTITY query-hql SYSTEM "modules/query_hql.xml">
+<!ENTITY query-criteria SYSTEM "modules/query_criteria.xml">
+<!ENTITY query-sql SYSTEM "modules/query_sql.xml">
+<!ENTITY filters SYSTEM "modules/filters.xml">
+<!ENTITY xml SYSTEM "modules/xml.xml">
+<!ENTITY performance SYSTEM "modules/performance.xml">
+<!ENTITY toolset-guide SYSTEM "modules/toolset_guide.xml">
+<!ENTITY example-parentchild SYSTEM "modules/example_parentchild.xml">
+<!ENTITY example-weblog SYSTEM "modules/example_weblog.xml">
+<!ENTITY example-mappings SYSTEM "modules/example_mappings.xml">
+<!ENTITY best-practices SYSTEM "modules/best_practices.xml">
+]>
+
+<book lang="en">
+
+ <bookinfo>
+ <title>HIBERNATE - Relational Persistence for Idiomatic Java</title>
+ <subtitle>Hibernate Reference Documentation</subtitle>
+ <releaseinfo>3.2 cr3</releaseinfo>
+ </bookinfo>
+
+ <toc/>
+
+ <preface id="preface" revision="2">
+ <title>Preface</title>
+
+ <para>
+ Working with object-oriented software and a relational database can be cumbersome
+ and time consuming in today's enterprise environments. Hibernate is an object/relational
+ mapping tool for Java environments. The term object/relational mapping (ORM) refers to
+ the technique of mapping a data representation from an object model to a relational
+ data model with a SQL-based schema.
+ </para>
+
+ <para>
+ Hibernate not only takes care of the mapping from Java classes to
+ database tables (and from Java data types to SQL data types), but also provides data
+ query and retrieval facilities and can significantly reduce development time otherwise
+ spent with manual data handling in SQL and JDBC.
+ </para>
+
+ <para>
+ Hibernates goal is to relieve the developer from 95 percent of common data persistence
+ related programming tasks. Hibernate may not be the best solution for data-centric
+ applications that only use stored-procedures to implement the business logic in the
+ database, it is most useful with object-oriented domain models and business logic in
+ the Java-based middle-tier. However, Hibernate can certainly help you to remove or
+ encapsulate vendor-specific SQL code and will help with the common task of result set
+ translation from a tabular representation to a graph of objects.
+ </para>
+
+ <para>
+ If you are new to Hibernate and Object/Relational Mapping or even Java,
+ please follow these steps:
+ </para>
+
+ <orderedlist>
+ <listitem>
+ <para>
+ Read <xref linkend="tutorial"/> for a tutorial with step-by-step
+ instructions. The source code for the tutorial is included in the
+ distribution in the <literal>doc/reference/tutorial/</literal>
+ directory.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Read <xref linkend="architecture"/> to understand the environments where
+ Hibernate can be used.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Have a look at the <literal>eg/</literal> directory in the Hibernate
+ distribution, it contains a simple standalone application. Copy your
+ JDBC driver to the <literal>lib/</literal> directory and edit
+ <literal>etc/hibernate.properties</literal>, specifying correct values for
+ your database. From a command prompt in the distribution directory,
+ type <literal>ant eg</literal> (using Ant), or under Windows, type
+ <literal>build eg</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Use this reference documentation as your primary source of information.
+ Consider reading <emphasis>Hibernate in Action</emphasis>
+ (http://www.manning.com/bauer) if you need more help with application
+ design or if you prefer a step-by-step tutorial. Also visit
+ http://caveatemptor.hibernate.org and download the example application
+ for Hibernate in Action.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ FAQs are answered on the Hibernate website.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Third party demos, examples, and tutorials are linked on the Hibernate
+ website.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ The Community Area on the Hibernate website is a good resource for
+ design patterns and various integration solutions (Tomcat, JBoss AS,
+ Struts, EJB, etc.).
+ </para>
+ </listitem>
+ </orderedlist>
+
+ <para>
+ If you have questions, use the user forum linked on the Hibernate website. We also
+ provide a JIRA issue trackings system for bug reports and feature requests. If you
+ are interested in the development of Hibernate, join the developer mailing list. If
+ you are interested in translating this documentation into your language, contact us
+ on the developer mailing list.
+ </para>
+
+ <para>
+ Commercial development support, production support, and training for Hibernate is
+ available through JBoss Inc. (see http://www.hibernate.org/SupportTraining/).
+ Hibernate is a Professional Open Source project and a critical component of the
+ JBoss Enterprise Middleware System (JEMS) suite of products.
+ </para>
+
+ </preface>
+
+ &tutorial;
+
+ &architecture;
+
+ &configuration;
+
+ &persistent-classes;
+
+ &basic-mapping;
+ &collection-mapping;
+ &association-mapping;
+ &component-mapping;
+ &inheritance-mapping;
+
+ &session-api;
+ &transactions;
+ &events;
+ &batch;
+
+ &query-hql;
+ &query-criteria;
+ &query-sql;
+ &filters;
+ &xml;
+
+ &performance;
+
+ &toolset-guide;
+
+ &example-parentchild;
+ &example-weblog;
+ &example-mappings;
+
+ &best-practices;
+
+</book>
+
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/architecture.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/architecture.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/architecture.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,355 @@
+<chapter id="architecture">
+
+ <title>Architecture</title>
+
+ <sect1 id="architecture-overview" revision="1">
+ <title>Overview</title>
+
+ <para>
+ A (very) high-level view of the Hibernate architecture:
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/overview.svg" format="SVG" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/overview.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ This diagram shows Hibernate using the database and configuration data to
+ provide persistence services (and persistent objects) to the application.
+ </para>
+
+ <para>
+ We would like to show a more detailed view of the runtime architecture.
+ Unfortunately, Hibernate is flexible and supports several approaches. We will
+ show the two extremes. The "lite" architecture has the application
+ provide its own JDBC connections and manage its own transactions. This approach
+ uses a minimal subset of Hibernate's APIs:
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/lite.svg" format="SVG" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/lite.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ The "full cream" architecture abstracts the application away from the
+ underlying JDBC/JTA APIs and lets Hibernate take care of the details.
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/full_cream.svg" format="SVG" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/full_cream.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ Heres some definitions of the objects in the diagrams:
+
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term>SessionFactory (<literal>org.hibernate.SessionFactory</literal>)</term>
+ <listitem>
+ <para>
+ A threadsafe (immutable) cache of compiled mappings for a single database.
+ A factory for <literal>Session</literal> and a client of
+ <literal>ConnectionProvider</literal>. Might hold an optional (second-level)
+ cache of data that is reusable between transactions, at a
+ process- or cluster-level.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Session (<literal>org.hibernate.Session</literal>)</term>
+ <listitem>
+ <para>
+ A single-threaded, short-lived object representing a conversation between
+ the application and the persistent store. Wraps a JDBC connection. Factory
+ for <literal>Transaction</literal>. Holds a mandatory (first-level) cache
+ of persistent objects, used when navigating the object graph or looking up
+ objects by identifier.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Persistent objects and collections</term>
+ <listitem>
+ <para>
+ Short-lived, single threaded objects containing persistent state and business
+ function. These might be ordinary JavaBeans/POJOs, the only special thing about
+ them is that they are currently associated with (exactly one)
+ <literal>Session</literal>. As soon as the <literal>Session</literal> is closed,
+ they will be detached and free to use in any application layer (e.g. directly
+ as data transfer objects to and from presentation).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Transient and detached objects and collections</term>
+ <listitem>
+ <para>
+ Instances of persistent classes that are not currently associated with a
+ <literal>Session</literal>. They may have been instantiated by
+ the application and not (yet) persisted or they may have been instantiated by a
+ closed <literal>Session</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Transaction (<literal>org.hibernate.Transaction</literal>)</term>
+ <listitem>
+ <para>
+ (Optional) A single-threaded, short-lived object used by the application to
+ specify atomic units of work. Abstracts application from underlying JDBC,
+ JTA or CORBA transaction. A <literal>Session</literal> might span several
+ <literal>Transaction</literal>s in some cases. However, transaction demarcation,
+ either using the underlying API or <literal>Transaction</literal>, is never
+ optional!
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>ConnectionProvider (<literal>org.hibernate.connection.ConnectionProvider</literal>)</term>
+ <listitem>
+ <para>
+ (Optional) A factory for (and pool of) JDBC connections. Abstracts application from
+ underlying <literal>Datasource</literal> or <literal>DriverManager</literal>.
+ Not exposed to application, but can be extended/implemented by the developer.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>TransactionFactory (<literal>org.hibernate.TransactionFactory</literal>)</term>
+ <listitem>
+ <para>
+ (Optional) A factory for <literal>Transaction</literal> instances. Not exposed to the
+ application, but can be extended/implemented by the developer.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><emphasis>Extension Interfaces</emphasis></term>
+ <listitem>
+ <para>
+ Hibernate offers many optional extension interfaces you can implement to customize
+ the behavior of your persistence layer. See the API documentation for details.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ <para>
+ Given a "lite" architecture, the application bypasses the
+ <literal>Transaction</literal>/<literal>TransactionFactory</literal> and/or
+ <literal>ConnectionProvider</literal> APIs to talk to JTA or JDBC directly.
+ </para>
+ </sect1>
+
+ <sect1 id="architecture-states" revision="1">
+ <title>Instance states</title>
+ <para>
+ An instance of a persistent classes may be in one of three different states,
+ which are defined with respect to a <emphasis>persistence context</emphasis>.
+ The Hibernate <literal>Session</literal> object is the persistence context:
+ </para>
+
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term>transient</term>
+ <listitem>
+ <para>
+ The instance is not, and has never been associated with
+ any persistence context. It has no persistent identity
+ (primary key value).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>persistent</term>
+ <listitem>
+ <para>
+ The instance is currently associated with a persistence
+ context. It has a persistent identity (primary key value)
+ and, perhaps, a corresponding row in the database. For a
+ particular persistence context, Hibernate
+ <emphasis>guarantees</emphasis> that persistent identity
+ is equivalent to Java identity (in-memory location of the
+ object).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>detached</term>
+ <listitem>
+ <para>
+ The instance was once associated with a persistence
+ context, but that context was closed, or the instance
+ was serialized to another process. It has a persistent
+ identity and, perhaps, a corrsponding row in the database.
+ For detached instances, Hibernate makes no guarantees
+ about the relationship between persistent identity and
+ Java identity.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </sect1>
+
+ <sect1 id="architecture-jmx" revision="1">
+ <title>JMX Integration</title>
+
+ <para>
+ JMX is the J2EE standard for management of Java components. Hibernate may be managed via
+ a JMX standard service. We provide an MBean implementation in the distribution,
+ <literal>org.hibernate.jmx.HibernateService</literal>.
+ </para>
+
+ <para>
+ For an example how to deploy Hibernate as a JMX service on the JBoss Application Server,
+ please see the JBoss User Guide. On JBoss AS, you also get these benefits if you deploy
+ using JMX:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Session Management:</emphasis> The Hibernate <literal>Session</literal>'s lifecycle
+ can be automatically bound to the scope of a JTA transaction. This means you no
+ longer have to manually open and close the <literal>Session</literal>, this
+ becomes the job of a JBoss EJB interceptor. You also don't have to worry about
+ transaction demarcation in your code anymore (unless you'd like to write a portable
+ persistence layer of course, use the optional Hibernate <literal>Transaction</literal>
+ API for this). You call the <literal>HibernateContext</literal> to access a
+ <literal>Session</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>HAR deployment:</emphasis> Usually you deploy the Hibernate JMX service using a JBoss
+ service deployment descriptor (in an EAR and/or SAR file), it supports all the usual
+ configuration options of a Hibernate <literal>SessionFactory</literal>. However, you still
+ have to name all your mapping files in the deployment descriptor. If you decide to use
+ the optional HAR deployment, JBoss will automatically detect all mapping files in your
+ HAR file.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Consult the JBoss AS user guide for more information about these options.
+ </para>
+
+ <para>
+ Another feature available as a JMX service are runtime Hibernate statistics. See
+ <xref linkend="configuration-optional-statistics"/>.
+ </para>
+ </sect1>
+
+ <sect1 id="architecture-jca" revision="1">
+ <title>JCA Support</title>
+ <para>
+ Hibernate may also be configured as a JCA connector. Please see the website for more
+ details. Please note that Hibernate JCA support is still considered experimental.
+ </para>
+ </sect1>
+
+ <sect1 id="architecture-current-session" revision="2">
+ <title>Contextual Sessions</title>
+ <para>
+ Most applications using Hibernate need some form of "contextual" sessions, where a given
+ session is in effect throughout the scope of a given context. However, across applications
+ the definition of what constitutes a context is typically different; and different contexts
+ define different scopes to the notion of current. Applications using Hibernate prior
+ to version 3.0 tended to utilize either home-grown <literal>ThreadLocal</literal>-based
+ contextual sessions, helper classes such as <literal>HibernateUtil</literal>, or utilized
+ third-party frameworks (such as Spring or Pico) which provided proxy/interception-based contextual sessions.
+ </para>
+ <para>
+ Starting with version 3.0.1, Hibernate added the <literal>SessionFactory.getCurrentSession()</literal>
+ method. Initially, this assumed usage of <literal>JTA</literal> transactions, where the
+ <literal>JTA</literal> transaction defined both the scope and context of a current session.
+ The Hibernate team maintains that, given the maturity of the numerous stand-alone
+ <literal>JTA TransactionManager</literal> implementations out there, most (if not all)
+ applications should be using <literal>JTA</literal> transaction management whether or not
+ they are deployed into a <literal>J2EE</literal> container. Based on that, the
+ <literal>JTA</literal>-based contextual sessions is all you should ever need to use.
+ </para>
+ <para>
+ However, as of version 3.1, the processing behind
+ <literal>SessionFactory.getCurrentSession()</literal> is now pluggable. To that
+ end, a new extension interface (<literal>org.hibernate.context.CurrentSessionContext</literal>)
+ and a new configuration parameter (<literal>hibernate.current_session_context_class</literal>)
+ have been added to allow pluggability of the scope and context of defining current sessions.
+ </para>
+ <para>
+ See the Javadocs for the <literal>org.hibernate.context.CurrentSessionContext</literal>
+ interface for a detailed discussion of its contract. It defines a single method,
+ <literal>currentSession()</literal>, by which the implementation is responsible for
+ tracking the current contextual session. Out-of-the-box, Hibernate comes with three
+ implementations of this interface.
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>org.hibernate.context.JTASessionContext</literal> - current sessions
+ are tracked and scoped by a <literal>JTA</literal> transaction. The processing
+ here is exactly the same as in the older JTA-only approach. See the Javadocs
+ for details.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>org.hibernate.context.ThreadLocalSessionContext</literal> - current
+ sessions are tracked by thread of execution. Again, see the Javadocs for details.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>org.hibernate.context.ManagedSessionContext</literal> - current
+ sessions are tracked by thread of execution. However, you are responsible to
+ bind and unbind a <literal>Session</literal> instance with static methods
+ on this class, it does never open, flush, or close a <literal>Session</literal>.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ The first two implementations provide a "one session - one database transaction" programming
+ model, also known and used as <emphasis>session-per-request</emphasis>. The beginning
+ and end of a Hibernate session is defined by the duration of a database transaction.
+ If you use programatic transaction demarcation in plain JSE without JTA, you are adviced to
+ use the Hibernate <literal>Transaction</literal> API to hide the underlying transaction system
+ from your code. If you use JTA, use the JTA interfaces to demarcate transactions. If you
+ execute in an EJB container that supports CMT, transaction boundaries are defined declaratively
+ and you don't need any transaction or session demarcation operations in your code.
+ Refer to <xref linkend="transactions"/> for more information and code examples.
+ </para>
+
+ <para>
+ The <literal>hibernate.current_session_context_class</literal> configuration parameter
+ defines which <literal>org.hibernate.context.CurrentSessionContext</literal> implementation
+ should be used. Note that for backwards compatibility, if this config param is not set
+ but a <literal>org.hibernate.transaction.TransactionManagerLookup</literal> is configured,
+ Hibernate will use the <literal>org.hibernate.context.JTASessionContext</literal>.
+ Typically, the value of this parameter would just name the implementation class to
+ use; for the three out-of-the-box implementations, however, there are two corresponding
+ short names, "jta", "thread", and "managed".
+ </para>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/association_mapping.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/association_mapping.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/association_mapping.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,623 @@
+<chapter id="associations">
+
+ <title>Association Mappings</title>
+
+ <sect1 id="assoc-intro" revision="1">
+ <title>Introduction</title>
+
+ <para>
+ Association mappings are the often most difficult thing to get right. In
+ this section we'll go through the canonical cases one by one, starting
+ with unidirectional mappings, and then considering the bidirectional cases.
+ We'll use <literal>Person</literal> and <literal>Address</literal> in all
+ the examples.
+ </para>
+
+ <para>
+ We'll classify associations by whether or not they map to an intervening
+ join table, and by multiplicity.
+ </para>
+
+ <para>
+ Nullable foreign keys are not considered good practice in traditional data
+ modelling, so all our examples use not null foreign keys. This is not a
+ requirement of Hibernate, and the mappings will all work if you drop the
+ nullability constraints.
+ </para>
+
+ </sect1>
+
+ <sect1 id="assoc-unidirectional" revision="1">
+ <title>Unidirectional associations</title>
+
+ <sect2 id="assoc-unidirectional-m21">
+ <title>many to one</title>
+
+ <para>
+ A <emphasis>unidirectional many-to-one association</emphasis> is the most
+ common kind of unidirectional association.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key, addressId bigint not null )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-121">
+ <title>one to one</title>
+
+ <para>
+ A <emphasis>unidirectional one-to-one association on a foreign key</emphasis>
+ is almost identical. The only difference is the column unique constraint.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <many-to-one name="address"
+ column="addressId"
+ unique="true"
+ not-null="true"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key, addressId bigint not null unique )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ <para>
+ A <emphasis>unidirectional one-to-one association on a primary key</emphasis>
+ usually uses a special id generator. (Notice that we've reversed the direction
+ of the association in this example.)
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+</class>
+
+<class name="Address">
+ <id name="id" column="personId">
+ <generator class="foreign">
+ <param name="property">person</param>
+ </generator>
+ </id>
+ <one-to-one name="person" constrained="true"/>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table Address ( personId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-12m">
+ <title>one to many</title>
+
+ <para>
+ A <emphasis>unidirectional one-to-many association on a foreign key</emphasis>
+ is a very unusual case, and is not really recommended.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses">
+ <key column="personId"
+ not-null="true"/>
+ <one-to-many class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table Address ( addressId bigint not null primary key, personId bigint not null )
+ ]]></programlisting>
+
+ <para>
+ We think it's better to use a join table for this kind of association.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="assoc-unidirectional-join" revision="1">
+ <title>Unidirectional associations with join tables</title>
+
+ <sect2 id="assoc-unidirectional-join-12m">
+ <title>one to many</title>
+
+ <para>
+ A <emphasis>unidirectional one-to-many association on a join table</emphasis>
+ is much preferred. Notice that by specifying <literal>unique="true"</literal>,
+ we have changed the multiplicity from many-to-many to one-to-many.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses" table="PersonAddress">
+ <key column="personId"/>
+ <many-to-many column="addressId"
+ unique="true"
+ class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId not null, addressId bigint not null primary key )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-join-m21">
+ <title>many to one</title>
+
+ <para>
+ A <emphasis>unidirectional many-to-one association on a join table</emphasis>
+ is quite common when the association is optional.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ optional="true">
+ <key column="personId" unique="true"/>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"/>
+ </join>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null primary key, addressId bigint not null )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-join-121">
+ <title>one to one</title>
+
+ <para>
+ A <emphasis>unidirectional one-to-one association on a join table</emphasis>
+ is extremely unusual, but possible.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ optional="true">
+ <key column="personId"
+ unique="true"/>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"
+ unique="true"/>
+ </join>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-join-m2m">
+ <title>many to many</title>
+
+ <para>
+ Finally, we have a <emphasis>unidirectional many-to-many association</emphasis>.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses" table="PersonAddress">
+ <key column="personId"/>
+ <many-to-many column="addressId"
+ class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personId, addressId) )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="assoc-bidirectional" revision="1">
+ <title>Bidirectional associations</title>
+
+ <sect2 id="assoc-bidirectional-m21" revision="2">
+ <title>one to many / many to one</title>
+
+ <para>
+ A <emphasis>bidirectional many-to-one association</emphasis> is the
+ most common kind of association. (This is the standard parent/child
+ relationship.)
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <set name="people" inverse="true">
+ <key column="addressId"/>
+ <one-to-many class="Person"/>
+ </set>
+</class>]]></programlisting>
+
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key, addressId bigint not null )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ <para>
+ If you use a <literal>List</literal> (or other indexed collection) you need
+ to set the <literal>key</literal> column of the foreign key to <literal>not null</literal>,
+ and let Hibernate manage the association from the collections side to maintain the index
+ of each element (making the other side virtually inverse by setting
+ <literal>update="false"</literal> and <literal>insert="false"</literal>):
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id"/>
+ ...
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"
+ insert="false"
+ update="false"/>
+</class>
+
+<class name="Address">
+ <id name="id"/>
+ ...
+ <list name="people">
+ <key column="addressId" not-null="true"/>
+ <list-index column="peopleIdx"/>
+ <one-to-many class="Person"/>
+ </list>
+</class>]]></programlisting>
+
+ <para>
+ It is important that you define <literal>not-null="true"</literal> on the
+ <literal><key></literal> element of the collection mapping if the
+ underlying foreign key column is <literal>NOT NULL</literal>. Don't only
+ declare <literal>not-null="true"</literal> on a possible nested
+ <literal><column></literal> element, but on the <literal><key></literal>
+ element.
+ </para>
+
+ </sect2>
+
+ <sect2 id="assoc-bidirectional-121">
+ <title>one to one</title>
+
+ <para>
+ A <emphasis>bidirectional one-to-one association on a foreign key</emphasis>
+ is quite common.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <many-to-one name="address"
+ column="addressId"
+ unique="true"
+ not-null="true"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <one-to-one name="person"
+ property-ref="address"/>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key, addressId bigint not null unique )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ <para>
+ A <emphasis>bidirectional one-to-one association on a primary key</emphasis>
+ uses the special id generator.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <one-to-one name="address"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="personId">
+ <generator class="foreign">
+ <param name="property">person</param>
+ </generator>
+ </id>
+ <one-to-one name="person"
+ constrained="true"/>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table Address ( personId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="assoc-bidirectional-join" revision="1">
+ <title>Bidirectional associations with join tables</title>
+
+ <sect2 id="assoc-bidirectional-join-12m">
+ <title>one to many / many to one</title>
+
+ <para>
+ A <emphasis>bidirectional one-to-many association on a join table</emphasis>.
+ Note that the <literal>inverse="true"</literal> can go on either end of the
+ association, on the collection, or on the join.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses"
+ table="PersonAddress">
+ <key column="personId"/>
+ <many-to-many column="addressId"
+ unique="true"
+ class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ inverse="true"
+ optional="true">
+ <key column="addressId"/>
+ <many-to-one name="person"
+ column="personId"
+ not-null="true"/>
+ </join>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null, addressId bigint not null primary key )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-bidirectional-join-121">
+ <title>one to one</title>
+
+ <para>
+ A <emphasis>bidirectional one-to-one association on a join table</emphasis>
+ is extremely unusual, but possible.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ optional="true">
+ <key column="personId"
+ unique="true"/>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"
+ unique="true"/>
+ </join>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ optional="true"
+ inverse="true">
+ <key column="addressId"
+ unique="true"/>
+ <many-to-one name="person"
+ column="personId"
+ not-null="true"
+ unique="true"/>
+ </join>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-bidirectional-join-m2m" revision="1">
+ <title>many to many</title>
+
+ <para>
+ Finally, we have a <emphasis>bidirectional many-to-many association</emphasis>.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses" table="PersonAddress">
+ <key column="personId"/>
+ <many-to-many column="addressId"
+ class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <set name="people" inverse="true" table="PersonAddress">
+ <key column="addressId"/>
+ <many-to-many column="personId"
+ class="Person"/>
+ </set>
+</class>]]></programlisting>
+
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personId, addressId) )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="assoc-complex">
+ <title>More complex association mappings</title>
+
+ <para>
+ More complex association joins are <emphasis>extremely</emphasis> rare.
+ Hibernate makes it possible to handle more complex situations using
+ SQL fragments embedded in the mapping document. For example, if a table
+ with historical account information data defines
+ <literal>accountNumber</literal>, <literal>effectiveEndDate</literal>
+ and <literal>effectiveStartDate</literal>columns, mapped as follows:
+ </para>
+
+ <programlisting><![CDATA[<properties name="currentAccountKey">
+ <property name="accountNumber" type="string" not-null="true"/>
+ <property name="currentAccount" type="boolean">
+ <formula>case when effectiveEndDate is null then 1 else 0 end</formula>
+ </property>
+</properties>
+<property name="effectiveEndDate" type="date"/>
+<property name="effectiveStateDate" type="date" not-null="true"/>]]></programlisting>
+
+ <para>
+ Then we can map an association to the <emphasis>current</emphasis> instance
+ (the one with null <literal>effectiveEndDate</literal>) using:
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="currentAccountInfo"
+ property-ref="currentAccountKey"
+ class="AccountInfo">
+ <column name="accountNumber"/>
+ <formula>'1'</formula>
+</many-to-one>]]></programlisting>
+
+ <para>
+ In a more complex example, imagine that the association between
+ <literal>Employee</literal> and <literal>Organization</literal> is maintained
+ in an <literal>Employment</literal> table full of historical employment data.
+ Then an association to the employee's <emphasis>most recent</emphasis> employer
+ (the one with the most recent <literal>startDate</literal>) might be mapped this way:
+ </para>
+
+ <programlisting><![CDATA[<join>
+ <key column="employeeId"/>
+ <subselect>
+ select employeeId, orgId
+ from Employments
+ group by orgId
+ having startDate = max(startDate)
+ </subselect>
+ <many-to-one name="mostRecentEmployer"
+ class="Organization"
+ column="orgId"/>
+</join>]]></programlisting>
+
+ <para>
+ You can get quite creative with this functionality, but it is usually more practical
+ to handle these kinds of cases using HQL or a criteria query.
+ </para>
+
+ </sect1>
+
+
+</chapter>
+
Property changes on: trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/association_mapping.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/basic_mapping.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/basic_mapping.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/basic_mapping.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,3560 @@
+<chapter id="mapping">
+ <title>Basic O/R Mapping</title>
+
+ <sect1 id="mapping-declaration" revision="2">
+ <title>Mapping declaration</title>
+
+ <para>
+ Object/relational mappings are usually defined in an XML document. The mapping
+ document is designed to be readable and hand-editable. The mapping language is
+ Java-centric, meaning that mappings are constructed around persistent class
+ declarations, not table declarations.
+ </para>
+
+ <para>
+ Note that, even though many Hibernate users choose to write the XML by hand,
+ a number of tools exist to generate the mapping document, including XDoclet,
+ Middlegen and AndroMDA.
+ </para>
+
+ <para>
+ Lets kick off with an example mapping:
+ </para>
+
+ <programlisting id="mapping-declaration-ex1" revision="1"><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="eg">
+
+ <class name="Cat"
+ table="cats"
+ discriminator-value="C">
+
+ <id name="id">
+ <generator class="native"/>
+ </id>
+
+ <discriminator column="subclass"
+ type="character"/>
+
+ <property name="weight"/>
+
+ <property name="birthdate"
+ type="date"
+ not-null="true"
+ update="false"/>
+
+ <property name="color"
+ type="eg.types.ColorUserType"
+ not-null="true"
+ update="false"/>
+
+ <property name="sex"
+ not-null="true"
+ update="false"/>
+
+ <property name="litterId"
+ column="litterId"
+ update="false"/>
+
+ <many-to-one name="mother"
+ column="mother_id"
+ update="false"/>
+
+ <set name="kittens"
+ inverse="true"
+ order-by="litter_id">
+ <key column="mother_id"/>
+ <one-to-many class="Cat"/>
+ </set>
+
+ <subclass name="DomesticCat"
+ discriminator-value="D">
+
+ <property name="name"
+ type="string"/>
+
+ </subclass>
+
+ </class>
+
+ <class name="Dog">
+ <!-- mapping for Dog could go here -->
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ We will now discuss the content of the mapping document. We will only describe the
+ document elements and attributes that are used by Hibernate at runtime. The mapping
+ document also contains some extra optional attributes and elements that affect the
+ database schemas exported by the schema export tool. (For example the <literal>
+ not-null</literal> attribute.)
+ </para>
+
+
+
+ <sect2 id="mapping-declaration-doctype" revision="3">
+ <title>Doctype</title>
+
+ <para>
+ All XML mappings should declare the doctype shown. The actual DTD may be found
+ at the URL above, in the directory <literal>hibernate-x.x.x/src/org/hibernate
+ </literal> or in <literal>hibernate3.jar</literal>. Hibernate will always look for
+ the DTD in its classpath first. If you experience lookups of the DTD using an
+ Internet connection, check your DTD declaration against the contents of your
+ claspath.
+ </para>
+
+ <sect3 id="mapping-declaration-entity-resolution">
+ <title>EntityResolver</title>
+ <para>
+ As mentioned previously, Hibernate will first attempt to resolve DTDs in its classpath. The
+ manner in which it does this is by registering a custom <literal>org.xml.sax.EntityResolver</literal>
+ implementation with the SAXReader it uses to read in the xml files. This custom
+ <literal>EntityResolver</literal> recognizes two different systemId namespaces.
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ a <literal>hibernate namespace</literal> is recognized whenever the
+ resolver encounteres a systemId starting with
+ <literal>http://hibernate.sourceforge.net/</literal>; the resolver
+ attempts to resolve these entities via the classlaoder which loaded
+ the Hibernate classes.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ a <literal>user namespace</literal> is recognized whenever the
+ resolver encounteres a systemId using a <literal>classpath://</literal>
+ URL protocol; the resolver will attempt to resolve these entities
+ via (1) the current thread context classloader and (2) the
+ classloader which loaded the Hibernate classes.
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+ An example of utilizing user namespacing:
+ </para>
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" [
+ <!ENTITY types SYSTEM "classpath://your/domain/types.xml">
+]>
+
+<hibernate-mapping package="your.domain">
+ <class name="MyEntity">
+ <id name="id" type="my-custom-id-type">
+ ...
+ </id>
+ <class>
+ &types;
+</hibernate-mapping>]]></programlisting>
+ <para>
+ Where <literal>types.xml</literal> is a resource in the <literal>your.domain</literal>
+ package and contains a custom <xref linkend="mapping-types-custom">typedef</xref>.
+ </para>
+ </sect3>
+ </sect2>
+
+ <sect2 id="mapping-declaration-mapping" revision="3">
+ <title>hibernate-mapping</title>
+
+ <para>
+ This element has several optional attributes. The <literal>schema</literal> and
+ <literal>catalog</literal> attributes specify that tables referred to in this mapping
+ belong to the named schema and/or catalog. If specified, tablenames will be qualified
+ by the given schema and catalog names. If missing, tablenames will be unqualified.
+ The <literal>default-cascade</literal> attribute specifies what cascade style
+ should be assumed for properties and collections which do not specify a
+ <literal>cascade</literal> attribute. The <literal>auto-import</literal> attribute lets us
+ use unqualified class names in the query language, by default.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="hm1" coords="2 55"/>
+ <area id="hm2" coords="3 55"/>
+ <area id="hm3" coords="4 55"/>
+ <area id="hm4" coords="5 55"/>
+ <area id="hm5" coords="6 55"/>
+ <area id="hm6" coords="7 55"/>
+ <area id="hm7" coords="8 55"/>
+ </areaspec>
+ <programlisting><![CDATA[<hibernate-mapping
+ schema="schemaName"
+ catalog="catalogName"
+ default-cascade="cascade_style"
+ default-access="field|property|ClassName"
+ default-lazy="true|false"
+ auto-import="true|false"
+ package="package.name"
+ />]]></programlisting>
+ <calloutlist>
+ <callout arearefs="hm1">
+ <para>
+ <literal>schema</literal> (optional): The name of a database schema.
+ </para>
+ </callout>
+ <callout arearefs="hm2">
+ <para>
+ <literal>catalog</literal> (optional): The name of a database catalog.
+ </para>
+ </callout>
+ <callout arearefs="hm3">
+ <para>
+ <literal>default-cascade</literal> (optional - defaults to <literal>none</literal>):
+ A default cascade style.
+ </para>
+ </callout>
+ <callout arearefs="hm4">
+ <para>
+ <literal>default-access</literal> (optional - defaults to <literal>property</literal>):
+ The strategy Hibernate should use for accessing all properties. Can be a custom
+ implementation of <literal>PropertyAccessor</literal>.
+ </para>
+ </callout>
+ <callout arearefs="hm5">
+ <para>
+ <literal>default-lazy</literal> (optional - defaults to <literal>true</literal>):
+ The default value for unspecifed <literal>lazy</literal> attributes of class and
+ collection mappings.
+ </para>
+ </callout>
+ <callout arearefs="hm6">
+ <para>
+ <literal>auto-import</literal> (optional - defaults to <literal>true</literal>):
+ Specifies whether we can use unqualified class names (of classes in this mapping)
+ in the query language.
+ </para>
+ </callout>
+ <callout arearefs="hm7">
+ <para>
+ <literal>package</literal> (optional): Specifies a package prefix to assume for
+ unqualified class names in the mapping document.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ If you have two persistent classes with the same (unqualified) name, you should set
+ <literal>auto-import="false"</literal>. Hibernate will throw an exception if you attempt
+ to assign two classes to the same "imported" name.
+ </para>
+
+ <para>
+ Note that the <literal>hibernate-mapping</literal> element allows you to nest
+ several persistent <literal><class></literal> mappings, as shown above.
+ It is however good practice (and expected by some tools) to map only a single
+ persistent class (or a single class hierarchy) in one mapping file and name
+ it after the persistent superclass, e.g. <literal>Cat.hbm.xml</literal>,
+ <literal>Dog.hbm.xml</literal>, or if using inheritance,
+ <literal>Animal.hbm.xml</literal>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-class" revision="3">
+ <title>class</title>
+
+ <para>
+ You may declare a persistent class using the <literal>class</literal> element:
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="class1" coords="2 55"/>
+ <area id="class2" coords="3 55" />
+ <area id="class3" coords="4 55"/>
+ <area id="class4" coords="5 55" />
+ <area id="class5" coords="6 55"/>
+ <area id="class6" coords="7 55" />
+ <area id="class7" coords="8 55"/>
+ <area id="class8" coords="9 55" />
+ <area id="class9" coords="10 55" />
+ <area id="class10" coords="11 55"/>
+ <area id="class11" coords="12 55"/>
+ <area id="class12" coords="13 55"/>
+ <area id="class13" coords="14 55"/>
+ <area id="class14" coords="15 55"/>
+ <area id="class15" coords="16 55"/>
+ <area id="class16" coords="17 55"/>
+ <area id="class17" coords="18 55"/>
+ <area id="class18" coords="19 55"/>
+ <area id="class19" coords="20 55"/>
+ <area id="class20" coords="21 55"/>
+ <area id="class21" coords="22 55"/>
+ </areaspec>
+ <programlisting><![CDATA[<class
+ name="ClassName"
+ table="tableName"
+ discriminator-value="discriminator_value"
+ mutable="true|false"
+ schema="owner"
+ catalog="catalog"
+ proxy="ProxyInterface"
+ dynamic-update="true|false"
+ dynamic-insert="true|false"
+ select-before-update="true|false"
+ polymorphism="implicit|explicit"
+ where="arbitrary sql where condition"
+ persister="PersisterClass"
+ batch-size="N"
+ optimistic-lock="none|version|dirty|all"
+ lazy="true|false"
+ entity-name="EntityName"
+ check="arbitrary sql check condition"
+ rowid="rowid"
+ subselect="SQL expression"
+ abstract="true|false"
+ node="element-name"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="class1">
+ <para>
+ <literal>name</literal> (optional): The fully qualified Java class name of the
+ persistent class (or interface). If this attribute is missing, it is assumed
+ that the mapping is for a non-POJO entity.
+ </para>
+ </callout>
+ <callout arearefs="class2">
+ <para>
+ <literal>table</literal> (optional - defaults to the unqualified class name): The
+ name of its database table.
+ </para>
+ </callout>
+ <callout arearefs="class3">
+ <para>
+ <literal>discriminator-value</literal> (optional - defaults to the class name): A value
+ that distiguishes individual subclasses, used for polymorphic behaviour. Acceptable
+ values include <literal>null</literal> and <literal>not null</literal>.
+ </para>
+ </callout>
+ <callout arearefs="class4">
+ <para>
+ <literal>mutable</literal> (optional, defaults to <literal>true</literal>): Specifies
+ that instances of the class are (not) mutable.
+ </para>
+ </callout>
+ <callout arearefs="class5">
+ <para>
+ <literal>schema</literal> (optional): Override the schema name specified by
+ the root <literal><hibernate-mapping></literal> element.
+ </para>
+ </callout>
+ <callout arearefs="class6">
+ <para>
+ <literal>catalog</literal> (optional): Override the catalog name specified by
+ the root <literal><hibernate-mapping></literal> element.
+ </para>
+ </callout>
+ <callout arearefs="class7">
+ <para>
+ <literal>proxy</literal> (optional): Specifies an interface to use for lazy
+ initializing proxies. You may specify the name of the class itself.
+ </para>
+ </callout>
+ <callout arearefs="class8">
+ <para>
+ <literal>dynamic-update</literal> (optional, defaults to <literal>false</literal>):
+ Specifies that <literal>UPDATE</literal> SQL should be generated at runtime and
+ contain only those columns whose values have changed.
+ </para>
+ </callout>
+ <callout arearefs="class9">
+ <para>
+ <literal>dynamic-insert</literal> (optional, defaults to <literal>false</literal>):
+ Specifies that <literal>INSERT</literal> SQL should be generated at runtime and
+ contain only the columns whose values are not null.
+ </para>
+ </callout>
+ <callout arearefs="class10">
+ <para>
+ <literal>select-before-update</literal> (optional, defaults to <literal>false</literal>):
+ Specifies that Hibernate should <emphasis>never</emphasis> perform an SQL <literal>UPDATE</literal>
+ unless it is certain that an object is actually modified. In certain cases (actually, only
+ when a transient object has been associated with a new session using <literal>update()</literal>),
+ this means that Hibernate will perform an extra SQL <literal>SELECT</literal> to determine
+ if an <literal>UPDATE</literal> is actually required.
+ </para>
+ </callout>
+ <callout arearefs="class11">
+ <para>
+ <literal>polymorphism</literal> (optional, defaults to <literal>implicit</literal>):
+ Determines whether implicit or explicit query polymorphism is used.
+ </para>
+ </callout>
+ <callout arearefs="class12">
+ <para>
+ <literal>where</literal> (optional) specify an arbitrary SQL <literal>WHERE</literal>
+ condition to be used when retrieving objects of this class
+ </para>
+ </callout>
+ <callout arearefs="class13">
+ <para>
+ <literal>persister</literal> (optional): Specifies a custom <literal>ClassPersister</literal>.
+ </para>
+ </callout>
+ <callout arearefs="class14">
+ <para>
+ <literal>batch-size</literal> (optional, defaults to <literal>1</literal>) specify a "batch size"
+ for fetching instances of this class by identifier.
+ </para>
+ </callout>
+ <callout arearefs="class15">
+ <para>
+ <literal>optimistic-lock</literal> (optional, defaults to <literal>version</literal>):
+ Determines the optimistic locking strategy.
+ </para>
+ </callout>
+ <callout arearefs="class16">
+ <para>
+ <literal>lazy</literal> (optional): Lazy fetching may be completely disabled by setting
+ <literal>lazy="false"</literal>.
+ </para>
+ </callout>
+ <callout arearefs="class17">
+ <para>
+ <literal>entity-name</literal> (optional, defaults to the class name): Hibernate3
+ allows a class to be mapped multiple times (to different tables, potentially),
+ and allows entity mappings that are represented by Maps or XML at the Java level.
+ In these cases, you should provide an explicit arbitrary name for the entity. See
+ <xref linkend="persistent-classes-dynamicmodels"/> and <xref linkend="xml"/>
+ for more information.
+ </para>
+ </callout>
+ <callout arearefs="class18">
+ <para>
+ <literal>check</literal> (optional): A SQL expression used to generate a multi-row
+ <emphasis>check</emphasis> constraint for automatic schema generation.
+ </para>
+ </callout>
+ <callout arearefs="class19">
+ <para>
+ <literal>rowid</literal> (optional): Hibernate can use so called ROWIDs on databases
+ which support. E.g. on Oracle, Hibernate can use the <literal>rowid</literal> extra
+ column for fast updates if you set this option to <literal>rowid</literal>. A ROWID
+ is an implementation detail and represents the physical location of a stored tuple.
+ </para>
+ </callout>
+ <callout arearefs="class20">
+ <para>
+ <literal>subselect</literal> (optional): Maps an immutable and read-only entity
+ to a database subselect. Useful if you want to have a view instead of a base table,
+ but don't. See below for more information.
+ </para>
+ </callout>
+ <callout arearefs="class21">
+ <para>
+ <literal>abstract</literal> (optional): Used to mark abstract superclasses in
+ <literal><union-subclass></literal> hierarchies.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ It is perfectly acceptable for the named persistent class to be an interface. You would then
+ declare implementing classes of that interface using the <literal><subclass></literal>
+ element. You may persist any <emphasis>static</emphasis> inner class. You should specify the
+ class name using the standard form ie. <literal>eg.Foo$Bar</literal>.
+ </para>
+
+ <para>
+ Immutable classes, <literal>mutable="false"</literal>, may not be updated or deleted by the
+ application. This allows Hibernate to make some minor performance optimizations.
+ </para>
+
+ <para>
+ The optional <literal>proxy</literal> attribute enables lazy initialization of persistent
+ instances of the class. Hibernate will initially return CGLIB proxies which implement
+ the named interface. The actual persistent object will be loaded when a method of the
+ proxy is invoked. See "Proxies for Lazy Initialization" below.
+ </para>
+
+ <para><emphasis>Implicit</emphasis> polymorphism means that instances of the class will be returned
+ by a query that names any superclass or implemented interface or the class and that instances
+ of any subclass of the class will be returned by a query that names the class itself.
+ <emphasis>Explicit</emphasis> polymorphism means that class instances will be returned only
+ by queries that explicitly name that class and that queries that name the class will return
+ only instances of subclasses mapped inside this <literal><class></literal> declaration
+ as a <literal><subclass></literal> or <literal><joined-subclass></literal>. For
+ most purposes the default, <literal>polymorphism="implicit"</literal>, is appropriate.
+ Explicit polymorphism is useful when two different classes are mapped to the same table
+ (this allows a "lightweight" class that contains a subset of the table columns).
+ </para>
+
+ <para>
+ The <literal>persister</literal> attribute lets you customize the persistence strategy used for
+ the class. You may, for example, specify your own subclass of
+ <literal>org.hibernate.persister.EntityPersister</literal> or you might even provide a
+ completely new implementation of the interface
+ <literal>org.hibernate.persister.ClassPersister</literal> that implements persistence via,
+ for example, stored procedure calls, serialization to flat files or LDAP. See
+ <literal>org.hibernate.test.CustomPersister</literal> for a simple example (of "persistence"
+ to a <literal>Hashtable</literal>).
+ </para>
+
+ <para>
+ Note that the <literal>dynamic-update</literal> and <literal>dynamic-insert</literal>
+ settings are not inherited by subclasses and so may also be specified on the
+ <literal><subclass></literal> or <literal><joined-subclass></literal> elements.
+ These settings may increase performance in some cases, but might actually decrease
+ performance in others. Use judiciously.
+ </para>
+
+ <para>
+ Use of <literal>select-before-update</literal> will usually decrease performance. It is very
+ useful to prevent a database update trigger being called unnecessarily if you reattach a
+ graph of detached instances to a <literal>Session</literal>.
+ </para>
+
+ <para>
+ If you enable <literal>dynamic-update</literal>, you will have a choice of optimistic
+ locking strategies:
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>version</literal> check the version/timestamp columns
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>all</literal> check all columns
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>dirty</literal> check the changed columns, allowing some concurrent updates
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>none</literal> do not use optimistic locking
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+ We <emphasis>very</emphasis> strongly recommend that you use version/timestamp
+ columns for optimistic locking with Hibernate. This is the optimal strategy with
+ respect to performance and is the only strategy that correctly handles modifications
+ made to detached instances (ie. when <literal>Session.merge()</literal> is used).
+ </para>
+
+ <para>
+ There is no difference between a view and a base table for a Hibernate mapping, as
+ expected this is transparent at the database level (note that some DBMS don't support
+ views properly, especially with updates). Sometimes you want to use a view, but can't
+ create one in the database (ie. with a legacy schema). In this case, you can map an
+ immutable and read-only entity to a given SQL subselect expression:
+ </para>
+
+ <programlisting><![CDATA[<class name="Summary">
+ <subselect>
+ select item.name, max(bid.amount), count(*)
+ from item
+ join bid on bid.item_id = item.id
+ group by item.name
+ </subselect>
+ <synchronize table="item"/>
+ <synchronize table="bid"/>
+ <id name="name"/>
+ ...
+</class>]]></programlisting>
+
+ <para>
+ Declare the tables to synchronize this entity with, ensuring that auto-flush happens
+ correctly, and that queries against the derived entity do not return stale data.
+ The <literal><subselect></literal> is available as both as an attribute and
+ a nested mapping element.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-id" revision="4">
+ <title>id</title>
+
+ <para>
+ Mapped classes <emphasis>must</emphasis> declare the primary key column of the database
+ table. Most classes will also have a JavaBeans-style property holding the unique identifier
+ of an instance. The <literal><id></literal> element defines the mapping from that
+ property to the primary key column.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="id1" coords="2 70"/>
+ <area id="id2" coords="3 70" />
+ <area id="id3" coords="4 70"/>
+ <area id="id4" coords="5 70" />
+ <area id="id5" coords="6 70" />
+ </areaspec>
+ <programlisting><![CDATA[<id
+ name="propertyName"
+ type="typename"
+ column="column_name"
+ unsaved-value="null|any|none|undefined|id_value"
+ access="field|property|ClassName">
+ node="element-name|@attribute-name|element/@attribute|."
+
+ <generator class="generatorClass"/>
+</id>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="id1">
+ <para>
+ <literal>name</literal> (optional): The name of the identifier property.
+ </para>
+ </callout>
+ <callout arearefs="id2">
+ <para>
+ <literal>type</literal> (optional): A name that indicates the Hibernate type.
+ </para>
+ </callout>
+ <callout arearefs="id3">
+ <para>
+ <literal>column</literal> (optional - defaults to the property name): The
+ name of the primary key column.
+ </para>
+ </callout>
+ <callout arearefs="id4">
+ <para>
+ <literal>unsaved-value</literal> (optional - defaults to a "sensible" value):
+ An identifier property value that indicates that an instance is newly instantiated
+ (unsaved), distinguishing it from detached instances that were saved or loaded
+ in a previous session.
+ </para>
+ </callout>
+ <callout arearefs="id5">
+ <para>
+ <literal>access</literal> (optional - defaults to <literal>property</literal>): The
+ strategy Hibernate should use for accessing the property value.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ If the <literal>name</literal> attribute is missing, it is assumed that the class has no
+ identifier property.
+ </para>
+
+ <para>
+ The <literal>unsaved-value</literal> attribute is almost never needed in Hibernate3.
+ </para>
+
+ <para>
+ There is an alternative <literal><composite-id></literal> declaration to allow access to
+ legacy data with composite keys. We strongly discourage its use for anything else.
+ </para>
+
+ <sect3 id="mapping-declaration-id-generator" revision="2">
+ <title>Generator</title>
+
+ <para>
+ The optional <literal><generator></literal> child element names a Java class used
+ to generate unique identifiers for instances of the persistent class. If any parameters
+ are required to configure or initialize the generator instance, they are passed using the
+ <literal><param></literal> element.
+ </para>
+
+ <programlisting><![CDATA[<id name="id" type="long" column="cat_id">
+ <generator class="org.hibernate.id.TableHiLoGenerator">
+ <param name="table">uid_table</param>
+ <param name="column">next_hi_value_column</param>
+ </generator>
+</id>]]></programlisting>
+
+ <para>
+ All generators implement the interface <literal>org.hibernate.id.IdentifierGenerator</literal>.
+ This is a very simple interface; some applications may choose to provide their own specialized
+ implementations. However, Hibernate provides a range of built-in implementations. There are shortcut
+ names for the built-in generators:
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>increment</literal></term>
+ <listitem>
+ <para>
+ generates identifiers of type <literal>long</literal>, <literal>short</literal> or
+ <literal>int</literal> that are unique only when no other process is inserting data
+ into the same table.
+ <emphasis>Do not use in a cluster.</emphasis>
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>identity</literal></term>
+ <listitem>
+ <para>
+ supports identity columns in DB2, MySQL, MS SQL Server, Sybase and
+ HypersonicSQL. The returned identifier is of type <literal>long</literal>,
+ <literal>short</literal> or <literal>int</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>sequence</literal></term>
+ <listitem>
+ <para>
+ uses a sequence in DB2, PostgreSQL, Oracle, SAP DB, McKoi or a generator
+ in Interbase. The returned identifier is of type <literal>long</literal>,
+ <literal>short</literal> or <literal>int</literal>
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>hilo</literal></term>
+ <listitem>
+ <para id="mapping-declaration-id-hilodescription" revision="1">
+ uses a hi/lo algorithm to efficiently generate identifiers of
+ type <literal>long</literal>, <literal>short</literal> or <literal>int</literal>,
+ given a table and column (by default <literal>hibernate_unique_key</literal> and
+ <literal>next_hi</literal> respectively) as a source of hi values. The hi/lo
+ algorithm generates identifiers that are unique only for a particular database.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>seqhilo</literal></term>
+ <listitem>
+ <para>
+ uses a hi/lo algorithm to efficiently generate identifiers of type
+ <literal>long</literal>, <literal>short</literal> or <literal>int</literal>,
+ given a named database sequence.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>uuid</literal></term>
+ <listitem>
+ <para>
+ uses a 128-bit UUID algorithm to generate identifiers of type string,
+ unique within a network (the IP address is used). The UUID is encoded
+ as a string of hexadecimal digits of length 32.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>guid</literal></term>
+ <listitem>
+ <para>
+ uses a database-generated GUID string on MS SQL Server and MySQL.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>native</literal></term>
+ <listitem>
+ <para>
+ picks <literal>identity</literal>, <literal>sequence</literal> or
+ <literal>hilo</literal> depending upon the capabilities of the
+ underlying database.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>assigned</literal></term>
+ <listitem>
+ <para>
+ lets the application to assign an identifier to the object before
+ <literal>save()</literal> is called. This is the default strategy
+ if no <literal><generator></literal> element is specified.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>select</literal></term>
+ <listitem>
+ <para>
+ retrieves a primary key assigned by a database trigger by selecting
+ the row by some unique key and retrieving the primary key value.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>foreign</literal></term>
+ <listitem>
+ <para>
+ uses the identifier of another associated object. Usually used in conjunction
+ with a <literal><one-to-one></literal> primary key association.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>sequence-identity</literal></term>
+ <listitem>
+ <para>
+ a specialized sequence generation strategy which utilizes a
+ database sequence for the actual value generation, but combines
+ this with JDBC3 getGeneratedKeys to actually return the generated
+ identifier value as part of the insert statement execution. This
+ strategy is only known to be supported on Oracle 10g drivers
+ targetted for JDK 1.4. Note comments on these insert statements
+ are disabled due to a bug in the Oracle drivers.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ </para>
+ </sect3>
+
+ <sect3 id="mapping-declaration-id-hilo" revision="1">
+ <title>Hi/lo algorithm</title>
+ <para>
+ The <literal>hilo</literal> and <literal>seqhilo</literal> generators provide two alternate
+ implementations of the hi/lo algorithm, a favorite approach to identifier generation. The
+ first implementation requires a "special" database table to hold the next available "hi" value.
+ The second uses an Oracle-style sequence (where supported).
+ </para>
+
+ <programlisting><![CDATA[<id name="id" type="long" column="cat_id">
+ <generator class="hilo">
+ <param name="table">hi_value</param>
+ <param name="column">next_value</param>
+ <param name="max_lo">100</param>
+ </generator>
+</id>]]></programlisting>
+
+ <programlisting><![CDATA[<id name="id" type="long" column="cat_id">
+ <generator class="seqhilo">
+ <param name="sequence">hi_value</param>
+ <param name="max_lo">100</param>
+ </generator>
+</id>]]></programlisting>
+
+ <para>
+ Unfortunately, you can't use <literal>hilo</literal> when supplying your own
+ <literal>Connection</literal> to Hibernate. When Hibernate is using an application
+ server datasource to obtain connections enlisted with JTA, you must properly configure
+ the <literal>hibernate.transaction.manager_lookup_class</literal>.
+ </para>
+ </sect3>
+
+ <sect3 id="mapping-declaration-id-uuid">
+ <title>UUID algorithm</title>
+ <para>
+ The UUID contains: IP address, startup time of the JVM (accurate to a quarter
+ second), system time and a counter value (unique within the JVM). It's not
+ possible to obtain a MAC address or memory address from Java code, so this is
+ the best we can do without using JNI.
+ </para>
+ </sect3>
+
+ <sect3 id="mapping-declaration-id-sequences">
+ <title>Identity columns and sequences</title>
+ <para>
+ For databases which support identity columns (DB2, MySQL, Sybase, MS SQL), you
+ may use <literal>identity</literal> key generation. For databases that support
+ sequences (DB2, Oracle, PostgreSQL, Interbase, McKoi, SAP DB) you may use
+ <literal>sequence</literal> style key generation. Both these strategies require
+ two SQL queries to insert a new object.
+ </para>
+
+ <programlisting><![CDATA[<id name="id" type="long" column="person_id">
+ <generator class="sequence">
+ <param name="sequence">person_id_sequence</param>
+ </generator>
+</id>]]></programlisting>
+
+ <programlisting><![CDATA[<id name="id" type="long" column="person_id" unsaved-value="0">
+ <generator class="identity"/>
+</id>]]></programlisting>
+
+ <para>
+ For cross-platform development, the <literal>native</literal> strategy will
+ choose from the <literal>identity</literal>, <literal>sequence</literal> and
+ <literal>hilo</literal> strategies, dependant upon the capabilities of the
+ underlying database.
+ </para>
+ </sect3>
+
+ <sect3 id="mapping-declaration-id-assigned">
+ <title>Assigned identifiers</title>
+ <para>
+ If you want the application to assign identifiers (as opposed to having
+ Hibernate generate them), you may use the <literal>assigned</literal> generator.
+ This special generator will use the identifier value already assigned to the
+ object's identifier property. This generator is used when the primary key
+ is a natural key instead of a surrogate key. This is the default behavior
+ if you do no specify a <literal><generator></literal> element.
+ </para>
+
+ <para>
+ Choosing the <literal>assigned</literal> generator makes Hibernate use
+ <literal>unsaved-value="undefined"</literal>, forcing Hibernate to go to
+ the database to determine if an instance is transient or detached, unless
+ there is a version or timestamp property, or you define
+ <literal>Interceptor.isUnsaved()</literal>.
+ </para>
+ </sect3>
+
+ <sect3 id="mapping-declaration-id-select">
+ <title>Primary keys assigned by triggers</title>
+ <para>
+ For legacy schemas only (Hibernate does not generate DDL with triggers).
+ </para>
+
+ <programlisting><![CDATA[<id name="id" type="long" column="person_id">
+ <generator class="select">
+ <param name="key">socialSecurityNumber</param>
+ </generator>
+</id>]]></programlisting>
+
+ <para>
+ In the above example, there is a unique valued property named
+ <literal>socialSecurityNumber</literal> defined by the class, as a
+ natural key, and a surrogate key named <literal>person_id</literal>
+ whose value is generated by a trigger.
+ </para>
+
+ </sect3>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-id-enhanced">
+ <title>Enhanced identifier generators</title>
+
+ <para>
+ Starting with release 3.2.3, there are 2 new generators which represent a re-thinking of 2 different
+ aspects of identifier generation. The first aspect is database portability; the second is optimization
+ (not having to query the database for every request for a new identifier value). These two new
+ generators are intended to take the place of some of the named generators described above (starting
+ in 3.3.x); however, they are included in the current releases and can be referenced by FQN.
+ </para>
+
+ <para>
+ The first of these new generators is <literal>org.hibernate.id.enhanced.SequenceStyleGenerator</literal>
+ which is intended firstly as a replacement for the <literal>sequence</literal> generator and secondly as
+ a better portability generator than <literal>native</literal> (because <literal>native</literal>
+ (generally) chooses between <literal>identity</literal> and <literal>sequence</literal> which have
+ largely different semantics which can cause subtle isssues in applications eyeing portability).
+ <literal>org.hibernate.id.enhanced.SequenceStyleGenerator</literal> however achieves portability in
+ a different manner. It chooses between using a table or a sequence in the database to store its
+ incrementing values depending on the capabilities of the dialect being used. The difference between this
+ and <literal>native</literal> is that table-based and sequence-based storage have the same exact
+ semantic (in fact sequences are exactly what Hibernate tries to emmulate with its table-based
+ generators). This generator has a number of configuration parameters:
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>sequence_name</literal> (optional, defaults to <literal>hibernate_sequence</literal>):
+ The name of the sequence (or table) to be used.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>initial_value</literal> (optional, defaults to <literal>1</literal>): The initial
+ value to be retrieved from the sequence/table. In sequence creation terms, this is analogous
+ to the clause typical named "STARTS WITH".
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>increment_size</literal> (optional, defaults to <literal>1</literal>): The value by
+ which subsequent calls to the sequence/table should differ. In sequence creation terms, this
+ is analogous to the clause typical named "INCREMENT BY".
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>force_table_use</literal> (optional, defaults to <literal>false</literal>): Should
+ we force the use of a table as the backing structure even though the dialect might support
+ sequence?
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>value_column</literal> (optional, defaults to <literal>next_val</literal>): Only
+ relevant for table structures! The name of the column on the table which is used to
+ hold the value.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>optimizer</literal> (optional, defaults to <literal>none</literal>):
+ See <xref linkend="mapping-declaration-id-enhanced-optimizers"/>
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ <para>
+ The second of these new generators is <literal>org.hibernate.id.enhanced.TableGenerator</literal> which
+ is intended firstly as a replacement for the <literal>table</literal> generator (although it actually
+ functions much more like <literal>org.hibernate.id.MultipleHiLoPerTableGenerator</literal>) and secondly
+ as a re-implementation of <literal>org.hibernate.id.MultipleHiLoPerTableGenerator</literal> utilizing the
+ notion of pluggable optimiziers. Essentially this generator defines a table capable of holding
+ a number of different increment values simultaneously by using multiple distinctly keyed rows. This
+ generator has a number of configuration parameters:
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>table_name</literal> (optional, defaults to <literal>hibernate_sequences</literal>):
+ The name of the table to be used.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>value_column_name</literal> (optional, defaults to <literal>next_val</literal>):
+ The name of the column on the table which is used to hold the value.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>segment_column_name</literal> (optional, defaults to <literal>sequence_name</literal>):
+ The name of the column on the table which is used to hold the "segement key". This is the
+ value which distinctly identifies which increment value to use.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>segment_value</literal> (optional, defaults to <literal>default</literal>):
+ The "segment key" value for the segment from which we want to pull increment values for
+ this generator.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>segment_value_length</literal> (optional, defaults to <literal>255</literal>):
+ Used for schema generation; the column size to create this segment key column.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>initial_value</literal> (optional, defaults to <literal>1</literal>):
+ The initial value to be retrieved from the table.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>increment_size</literal> (optional, defaults to <literal>1</literal>):
+ The value by which subsequent calls to the table should differ.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>optimizer</literal> (optional, defaults to <literal></literal>):
+ See <xref linkend="mapping-declaration-id-enhanced-optimizers"/>
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </sect2>
+
+ <sect2 id="mapping-declaration-id-enhanced-optimizers">
+ <title>Identifier generator optimization</title>
+ <para>
+ For identifier generators which store values in the database, it is inefficient for them to hit the
+ database on each and every call to generate a new identifier value. Instead, you'd ideally want to
+ group a bunch of them in memory and only hit the database when you have exhausted your in-memory
+ value group. This is the role of the pluggable optimizers. Currently only the two enhanced generators
+ (<xref linkend="mapping-declaration-id-enhanced"/> support this notion.
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>none</literal> (generally this is the default if no optimizer was specified): This
+ says to not perform any optimizations, and hit the database each and every request.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>hilo</literal>: applies a hi/lo algorithm around the database retrieved values. The
+ values from the database for this optimizer are expected to be sequential. The values
+ retrieved from the database structure for this optimizer indicates the "group number"; the
+ <literal>increment_size</literal> is multiplied by that value in memory to define a group
+ "hi value".
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>pooled</literal>: like was discussed for <literal>hilo</literal>, this optimizers
+ attempts to minimize the number of hits to the database. Here, however, we simply store
+ the starting value for the "next group" into the database structure rather than a sequential
+ value in combination with an in-memory grouping algorithm. <literal>increment_size</literal>
+ here refers to the values coming from the database.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </sect2>
+
+ <sect2 id="mapping-declaration-compositeid" revision="3">
+ <title>composite-id</title>
+
+ <programlisting><![CDATA[<composite-id
+ name="propertyName"
+ class="ClassName"
+ mapped="true|false"
+ access="field|property|ClassName">
+ node="element-name|."
+
+ <key-property name="propertyName" type="typename" column="column_name"/>
+ <key-many-to-one name="propertyName class="ClassName" column="column_name"/>
+ ......
+</composite-id>]]></programlisting>
+
+ <para>
+ For a table with a composite key, you may map multiple properties of the class
+ as identifier properties. The <literal><composite-id></literal> element
+ accepts <literal><key-property></literal> property mappings and
+ <literal><key-many-to-one></literal> mappings as child elements.
+ </para>
+
+ <programlisting><![CDATA[<composite-id>
+ <key-property name="medicareNumber"/>
+ <key-property name="dependent"/>
+</composite-id>]]></programlisting>
+
+ <para>
+ Your persistent class <emphasis>must</emphasis> override <literal>equals()</literal>
+ and <literal>hashCode()</literal> to implement composite identifier equality. It must
+ also implements <literal>Serializable</literal>.
+ </para>
+
+ <para>
+ Unfortunately, this approach to composite identifiers means that a persistent object
+ is its own identifier. There is no convenient "handle" other than the object itself.
+ You must instantiate an instance of the persistent class itself and populate its
+ identifier properties before you can <literal>load()</literal> the persistent state
+ associated with a composite key. We call this approach an <emphasis>embedded</emphasis>
+ composite identifier, and discourage it for serious applications.
+ </para>
+
+ <para>
+ A second approach is what we call a <emphasis>mapped</emphasis> composite identifier,
+ where the identifier properties named inside the <literal><composite-id></literal>
+ element are duplicated on both the persistent class and a separate identifier class.
+ </para>
+
+ <programlisting><![CDATA[<composite-id class="MedicareId" mapped="true">
+ <key-property name="medicareNumber"/>
+ <key-property name="dependent"/>
+</composite-id>]]></programlisting>
+
+ <para>
+ In this example, both the composite identifier class, <literal>MedicareId</literal>,
+ and the entity class itself have properties named <literal>medicareNumber</literal>
+ and <literal>dependent</literal>. The identifier class must override
+ <literal>equals()</literal> and <literal>hashCode()</literal> and implement.
+ <literal>Serializable</literal>. The disadvantage of this approach is quite
+ obvious—code duplication.
+ </para>
+
+ <para>
+ The following attributes are used to specify a mapped composite identifier:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>mapped</literal> (optional, defaults to <literal>false</literal>):
+ indicates that a mapped composite identifier is used, and that the contained
+ property mappings refer to both the entity class and the composite identifier
+ class.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>class</literal> (optional, but required for a mapped composite identifier):
+ The class used as a composite identifier.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ We will describe a third, even more convenient approach where the composite identifier
+ is implemented as a component class in <xref linkend="components-compositeid"/>. The
+ attributes described below apply only to this alternative approach:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>name</literal> (optional, required for this approach): A property of
+ component type that holds the composite identifier (see chapter 9).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>access</literal> (optional - defaults to <literal>property</literal>):
+ The strategy Hibernate should use for accessing the property value.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>class</literal> (optional - defaults to the property type determined by
+ reflection): The component class used as a composite identifier (see next section).
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ This third approach, an <emphasis>identifier component</emphasis> is the one we recommend
+ for almost all applications.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-discriminator" revision="3">
+ <title>discriminator</title>
+
+ <para>
+ The <literal><discriminator></literal> element is required for polymorphic persistence
+ using the table-per-class-hierarchy mapping strategy and declares a discriminator column of the
+ table. The discriminator column contains marker values that tell the persistence layer what
+ subclass to instantiate for a particular row. A restricted set of types may be used:
+ <literal>string</literal>, <literal>character</literal>, <literal>integer</literal>,
+ <literal>byte</literal>, <literal>short</literal>, <literal>boolean</literal>,
+ <literal>yes_no</literal>, <literal>true_false</literal>.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="discriminator1" coords="2 60"/>
+ <area id="discriminator2" coords="3 60" />
+ <area id="discriminator3" coords="4 60" />
+ <area id="discriminator4" coords="5 60" />
+ <area id="discriminator5" coords="6 60" />
+ </areaspec>
+ <programlisting><![CDATA[<discriminator
+ column="discriminator_column"
+ type="discriminator_type"
+ force="true|false"
+ insert="true|false"
+ formula="arbitrary sql expression"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="discriminator1">
+ <para>
+ <literal>column</literal> (optional - defaults to <literal>class</literal>) the
+ name of the discriminator column.
+ </para>
+ </callout>
+ <callout arearefs="discriminator2">
+ <para>
+ <literal>type</literal> (optional - defaults to <literal>string</literal>) a
+ name that indicates the Hibernate type
+ </para>
+ </callout>
+ <callout arearefs="discriminator3">
+ <para>
+ <literal>force</literal> (optional - defaults to <literal>false</literal>)
+ "force" Hibernate to specify allowed discriminator values even when retrieving
+ all instances of the root class.
+ </para>
+ </callout>
+ <callout arearefs="discriminator4">
+ <para>
+ <literal>insert</literal> (optional - defaults to <literal>true</literal>)
+ set this to <literal>false</literal> if your discriminator column is also part
+ of a mapped composite identifier. (Tells Hibernate to not include the column
+ in SQL <literal>INSERT</literal>s.)
+ </para>
+ </callout>
+ <callout arearefs="discriminator5">
+ <para>
+ <literal>formula</literal> (optional) an arbitrary SQL expression that is
+ executed when a type has to be evaluated. Allows content-based discrimination.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Actual values of the discriminator column are specified by the
+ <literal>discriminator-value</literal> attribute of the <literal><class></literal> and
+ <literal><subclass></literal> elements.
+ </para>
+
+ <para>
+ The <literal>force</literal> attribute is (only) useful if the table contains rows with
+ "extra" discriminator values that are not mapped to a persistent class. This will not
+ usually be the case.
+ </para>
+
+ <para>
+ Using the <literal>formula</literal> attribute you can declare an arbitrary SQL expression
+ that will be used to evaluate the type of a row:
+ </para>
+
+ <programlisting><![CDATA[<discriminator
+ formula="case when CLASS_TYPE in ('a', 'b', 'c') then 0 else 1 end"
+ type="integer"/>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-version" revision="4">
+ <title>version (optional)</title>
+
+ <para>
+ The <literal><version></literal> element is optional and indicates that
+ the table contains versioned data. This is particularly useful if you plan to
+ use <emphasis>long transactions</emphasis> (see below).
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="version1" coords="2 70"/>
+ <area id="version2" coords="3 70"/>
+ <area id="version3" coords="4 70"/>
+ <area id="version4" coords="5 70"/>
+ <area id="version5" coords="6 70"/>
+ <area id="version6" coords="7 70"/>
+ <area id="version7" coords="8 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<version
+ column="version_column"
+ name="propertyName"
+ type="typename"
+ access="field|property|ClassName"
+ unsaved-value="null|negative|undefined"
+ generated="never|always"
+ insert="true|false"
+ node="element-name|@attribute-name|element/@attribute|."
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="version1">
+ <para>
+ <literal>column</literal> (optional - defaults to the property name): The name
+ of the column holding the version number.
+ </para>
+ </callout>
+ <callout arearefs="version2">
+ <para>
+ <literal>name</literal>: The name of a property of the persistent class.
+ </para>
+ </callout>
+ <callout arearefs="version3">
+ <para>
+ <literal>type</literal> (optional - defaults to <literal>integer</literal>):
+ The type of the version number.
+ </para>
+ </callout>
+ <callout arearefs="version4">
+ <para>
+ <literal>access</literal> (optional - defaults to <literal>property</literal>): The
+ strategy Hibernate should use for accessing the property value.
+ </para>
+ </callout>
+ <callout arearefs="version5">
+ <para>
+ <literal>unsaved-value</literal> (optional - defaults to <literal>undefined</literal>):
+ A version property value that indicates that an instance is newly instantiated
+ (unsaved), distinguishing it from detached instances that were saved or loaded
+ in a previous session. (<literal>undefined</literal> specifies that the identifier
+ property value should be used.)
+ </para>
+ </callout>
+ <callout arearefs="version6">
+ <para>
+ <literal>generated</literal> (optional - defaults to <literal>never</literal>):
+ Specifies that this version property value is actually generated by the database.
+ See the discussion of <xref linkend="mapping-generated">generated properties</xref>.
+ </para>
+ </callout>
+ <callout arearefs="version7">
+ <para>
+ <literal>insert</literal> (optional - defaults to <literal>true</literal>):
+ Specifies whether the version column should be included in SQL insert statements.
+ May be set to <literal>false</literal> if and only if the database column
+ is defined with a default value of <literal>0</literal>.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Version numbers may be of Hibernate type <literal>long</literal>, <literal>integer</literal>,
+ <literal>short</literal>, <literal>timestamp</literal> or <literal>calendar</literal>.
+ </para>
+
+ <para>
+ A version or timestamp property should never be null for a detached instance, so
+ Hibernate will detact any instance with a null version or timestamp as transient,
+ no matter what other <literal>unsaved-value</literal> strategies are specified.
+ <emphasis>Declaring a nullable version or timestamp property is an easy way to avoid
+ any problems with transitive reattachment in Hibernate, especially useful for people
+ using assigned identifiers or composite keys!</emphasis>
+ </para>
+ </sect2>
+
+ <sect2 id="mapping-declaration-timestamp" revision="4" >
+ <title>timestamp (optional)</title>
+
+ <para>
+ The optional <literal><timestamp></literal> element indicates that the table contains
+ timestamped data. This is intended as an alternative to versioning. Timestamps are by nature
+ a less safe implementation of optimistic locking. However, sometimes the application might
+ use the timestamps in other ways.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="timestamp1" coords="2 70"/>
+ <area id="timestamp2" coords="3 70" />
+ <area id="timestamp3" coords="4 70" />
+ <area id="timestamp4" coords="5 70" />
+ <area id="timestamp5" coords="6 70" />
+ <area id="timestamp6" coords="7 70" />
+ </areaspec>
+ <programlisting><![CDATA[<timestamp
+ column="timestamp_column"
+ name="propertyName"
+ access="field|property|ClassName"
+ unsaved-value="null|undefined"
+ source="vm|db"
+ generated="never|always"
+ node="element-name|@attribute-name|element/@attribute|."
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="timestamp1">
+ <para>
+ <literal>column</literal> (optional - defaults to the property name): The name
+ of a column holding the timestamp.
+ </para>
+ </callout>
+ <callout arearefs="timestamp2">
+ <para>
+ <literal>name</literal>: The name of a JavaBeans style property of
+ Java type <literal>Date</literal> or <literal>Timestamp</literal> of the
+ persistent class.
+ </para>
+ </callout>
+ <callout arearefs="timestamp3">
+ <para>
+ <literal>access</literal> (optional - defaults to <literal>property</literal>): The
+ strategy Hibernate should use for accessing the property value.
+ </para>
+ </callout>
+ <callout arearefs="timestamp4">
+ <para>
+ <literal>unsaved-value</literal> (optional - defaults to <literal>null</literal>):
+ A version property value that indicates that an instance is newly instantiated
+ (unsaved), distinguishing it from detached instances that were saved or loaded
+ in a previous session. (<literal>undefined</literal> specifies that the identifier
+ property value should be used.)
+ </para>
+ </callout>
+ <callout arearefs="timestamp5">
+ <para>
+ <literal>source</literal> (optional - defaults to <literal>vm</literal>):
+ From where should Hibernate retrieve the timestamp value? From the database,
+ or from the current JVM? Database-based timestamps incur an overhead because
+ Hibernate must hit the database in order to determine the "next value",
+ but will be safer for use in clustered environments. Note also, that not
+ all <literal>Dialect</literal>s are known to support retrieving of the
+ database's current timestamp, while others might be unsafe for usage
+ in locking due to lack of precision (Oracle 8 for example).
+ </para>
+ </callout>
+ <callout arearefs="timestamp6">
+ <para>
+ <literal>generated</literal> (optional - defaults to <literal>never</literal>):
+ Specifies that this timestamp property value is actually generated by the database.
+ See the discussion of <xref linkend="mapping-generated">generated properties</xref>.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Note that <literal><timestamp></literal> is equivalent to
+ <literal><version type="timestamp"></literal>. And
+ <literal><timestamp source="db"></literal> is equivalent to
+ <literal><version type="dbtimestamp"></literal>
+ </para>
+ </sect2>
+
+
+ <sect2 id="mapping-declaration-property" revision="4">
+ <title>property</title>
+
+ <para>
+ The <literal><property></literal> element declares a persistent, JavaBean style
+ property of the class.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="property1" coords="2 70"/>
+ <area id="property2" coords="3 70"/>
+ <area id="property3" coords="4 70"/>
+ <areaset id="property4-5" coords="">
+ <area id="property4" coords='5 70'/>
+ <area id="property5" coords='6 70'/>
+ </areaset>
+ <area id="property6" coords="7 70"/>
+ <area id="property7" coords="8 70"/>
+ <area id="property8" coords="9 70"/>
+ <area id="property9" coords="10 70"/>
+ <area id="property10" coords="11 70"/>
+ <area id="property11" coords="12 70"/>
+ <area id="property12" coords="13 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<property
+ name="propertyName"
+ column="column_name"
+ type="typename"
+ update="true|false"
+ insert="true|false"
+ formula="arbitrary SQL expression"
+ access="field|property|ClassName"
+ lazy="true|false"
+ unique="true|false"
+ not-null="true|false"
+ optimistic-lock="true|false"
+ generated="never|insert|always"
+ node="element-name|@attribute-name|element/@attribute|."
+ index="index_name"
+ unique_key="unique_key_id"
+ length="L"
+ precision="P"
+ scale="S"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="property1">
+ <para>
+ <literal>name</literal>: the name of the property, with an initial lowercase
+ letter.
+ </para>
+ </callout>
+ <callout arearefs="property2">
+ <para>
+ <literal>column</literal> (optional - defaults to the property name): the name
+ of the mapped database table column. This may also be specified by nested
+ <literal><column></literal> element(s).
+ </para>
+ </callout>
+ <callout arearefs="property3">
+ <para>
+ <literal>type</literal> (optional): a name that indicates the Hibernate type.
+ </para>
+ </callout>
+ <callout arearefs="property4-5">
+ <para>
+ <literal>update, insert</literal> (optional - defaults to <literal>true</literal>) :
+ specifies that the mapped columns should be included in SQL <literal>UPDATE</literal>
+ and/or <literal>INSERT</literal> statements. Setting both to <literal>false</literal>
+ allows a pure "derived" property whose value is initialized from some other
+ property that maps to the same colum(s) or by a trigger or other application.
+ </para>
+ </callout>
+ <callout arearefs="property6">
+ <para>
+ <literal>formula</literal> (optional): an SQL expression that defines the value for a
+ <emphasis>computed</emphasis> property. Computed properties do not have a column
+ mapping of their own.
+ </para>
+ </callout>
+ <callout arearefs="property7">
+ <para>
+ <literal>access</literal> (optional - defaults to <literal>property</literal>): The
+ strategy Hibernate should use for accessing the property value.
+ </para>
+ </callout>
+ <callout arearefs="property8">
+ <para>
+ <literal>lazy</literal> (optional - defaults to <literal>false</literal>): Specifies
+ that this property should be fetched lazily when the instance variable is first
+ accessed (requires build-time bytecode instrumentation).
+ </para>
+ </callout>
+ <callout arearefs="property9">
+ <para>
+ <literal>unique</literal> (optional): Enable the DDL generation of a unique
+ constraint for the columns. Also, allow this to be the target of
+ a <literal>property-ref</literal>.
+ </para>
+ </callout>
+ <callout arearefs="property10">
+ <para>
+ <literal>not-null</literal> (optional): Enable the DDL generation of a nullability
+ constraint for the columns.
+ </para>
+ </callout>
+ <callout arearefs="property11">
+ <para>
+ <literal>optimistic-lock</literal> (optional - defaults to <literal>true</literal>):
+ Specifies that updates to this property do or do not require acquisition of the
+ optimistic lock. In other words, determines if a version increment should occur when
+ this property is dirty.
+ </para>
+ </callout>
+ <callout arearefs="property12">
+ <para>
+ <literal>generated</literal> (optional - defaults to <literal>never</literal>):
+ Specifies that this property value is actually generated by the database.
+ See the discussion of <xref linkend="mapping-generated">generated properties</xref>.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ <emphasis>typename</emphasis> could be:
+ </para>
+
+ <orderedlist spacing="compact">
+ <listitem>
+ <para>
+ The name of a Hibernate basic type (eg. <literal>integer, string, character,
+ date, timestamp, float, binary, serializable, object, blob</literal>).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ The name of a Java class with a default basic type (eg. <literal>int, float,
+ char, java.lang.String, java.util.Date, java.lang.Integer, java.sql.Clob</literal>).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ The name of a serializable Java class.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ The class name of a custom type (eg. <literal>com.illflow.type.MyCustomType</literal>).
+ </para>
+ </listitem>
+ </orderedlist>
+
+ <para>
+ If you do not specify a type, Hibernate will use reflection upon the named
+ property to take a guess at the correct Hibernate type. Hibernate will try to
+ interpret the name of the return class of the property getter using rules 2, 3,
+ 4 in that order. However, this is not always enough.
+ In certain cases you will still need the <literal>type</literal>
+ attribute. (For example, to distinguish between <literal>Hibernate.DATE</literal> and
+ <literal>Hibernate.TIMESTAMP</literal>, or to specify a custom type.)
+ </para>
+
+ <para>
+ The <literal>access</literal> attribute lets you control how Hibernate will access
+ the property at runtime. By default, Hibernate will call the property get/set pair.
+ If you specify <literal>access="field"</literal>, Hibernate will bypass the get/set
+ pair and access the field directly, using reflection. You may specify your own
+ strategy for property access by naming a class that implements the interface
+ <literal>org.hibernate.property.PropertyAccessor</literal>.
+ </para>
+
+ <para>
+ An especially powerful feature are derived properties. These properties are by
+ definition read-only, the property value is computed at load time. You declare
+ the computation as a SQL expression, this translates to a <literal>SELECT</literal>
+ clause subquery in the SQL query that loads an instance:
+ </para>
+
+ <programlisting><![CDATA[
+<property name="totalPrice"
+ formula="( SELECT SUM (li.quantity*p.price) FROM LineItem li, Product p
+ WHERE li.productId = p.productId
+ AND li.customerId = customerId
+ AND li.orderNumber = orderNumber )"/>]]></programlisting>
+
+ <para>
+ Note that you can reference the entities own table by not declaring an alias on
+ a particular column (<literal>customerId</literal> in the given example). Also note
+ that you can use the nested <literal><formula></literal> mapping element
+ if you don't like to use the attribute.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-manytoone" revision="5">
+ <title>many-to-one</title>
+
+ <para>
+ An ordinary association to another persistent class is declared using a
+ <literal>many-to-one</literal> element. The relational model is a
+ many-to-one association: a foreign key in one table is referencing
+ the primary key column(s) of the target table.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="manytoone1" coords="2 70"/>
+ <area id="manytoone2" coords="3 70"/>
+ <area id="manytoone3" coords="4 70"/>
+ <area id="manytoone4" coords="5 70"/>
+ <area id="manytoone5" coords="6 70"/>
+ <areaset id="manytoone6-7" coords="">
+ <area id="manytoone6" coords='7 70'/>
+ <area id="manytoone7" coords='8 70'/>
+ </areaset>
+ <area id="manytoone8" coords="9 70"/>
+ <area id="manytoone9" coords="10 70"/>
+ <area id="manytoone10" coords="11 70"/>
+ <area id="manytoone11" coords="12 70"/>
+ <area id="manytoone12" coords="13 70"/>
+ <area id="manytoone13" coords="14 70"/>
+ <area id="manytoone14" coords="15 70"/>
+ <area id="manytoone15" coords="16 70"/>
+ <area id="manytoone16" coords="17 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<many-to-one
+ name="propertyName"
+ column="column_name"
+ class="ClassName"
+ cascade="cascade_style"
+ fetch="join|select"
+ update="true|false"
+ insert="true|false"
+ property-ref="propertyNameFromAssociatedClass"
+ access="field|property|ClassName"
+ unique="true|false"
+ not-null="true|false"
+ optimistic-lock="true|false"
+ lazy="proxy|no-proxy|false"
+ not-found="ignore|exception"
+ entity-name="EntityName"
+ formula="arbitrary SQL expression"
+ node="element-name|@attribute-name|element/@attribute|."
+ embed-xml="true|false"
+ index="index_name"
+ unique_key="unique_key_id"
+ foreign-key="foreign_key_name"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="manytoone1">
+ <para>
+ <literal>name</literal>: The name of the property.
+ </para>
+ </callout>
+ <callout arearefs="manytoone2">
+ <para>
+ <literal>column</literal> (optional): The name of the foreign key column.
+ This may also be specified by nested <literal><column></literal>
+ element(s).
+ </para>
+ </callout>
+ <callout arearefs="manytoone3">
+ <para>
+ <literal>class</literal> (optional - defaults to the property type
+ determined by reflection): The name of the associated class.
+ </para>
+ </callout>
+ <callout arearefs="manytoone4">
+ <para>
+ <literal>cascade</literal> (optional): Specifies which operations should
+ be cascaded from the parent object to the associated object.
+ </para>
+ </callout>
+ <callout arearefs="manytoone5">
+ <para>
+ <literal>fetch</literal> (optional - defaults to <literal>select</literal>):
+ Chooses between outer-join fetching or sequential select fetching.
+ </para>
+ </callout>
+ <callout arearefs="manytoone6-7">
+ <para>
+ <literal>update, insert</literal> (optional - defaults to <literal>true</literal>)
+ specifies that the mapped columns should be included in SQL <literal>UPDATE</literal>
+ and/or <literal>INSERT</literal> statements. Setting both to <literal>false</literal>
+ allows a pure "derived" association whose value is initialized from some other
+ property that maps to the same colum(s) or by a trigger or other application.
+ </para>
+ </callout>
+ <callout arearefs="manytoone8">
+ <para>
+ <literal>property-ref</literal>: (optional) The name of a property of the associated
+ class that is joined to this foreign key. If not specified, the primary key of
+ the associated class is used.
+ </para>
+ </callout>
+ <callout arearefs="manytoone9">
+ <para>
+ <literal>access</literal> (optional - defaults to <literal>property</literal>): The
+ strategy Hibernate should use for accessing the property value.
+ </para>
+ </callout>
+ <callout arearefs="manytoone10">
+ <para>
+ <literal>unique</literal> (optional): Enable the DDL generation of a unique
+ constraint for the foreign-key column. Also, allow this to be the target of
+ a <literal>property-ref</literal>. This makes the association multiplicity
+ effectively one to one.
+ </para>
+ </callout>
+ <callout arearefs="manytoone11">
+ <para>
+ <literal>not-null</literal> (optional): Enable the DDL generation of a nullability
+ constraint for the foreign key columns.
+ </para>
+ </callout>
+ <callout arearefs="manytoone12">
+ <para>
+ <literal>optimistic-lock</literal> (optional - defaults to <literal>true</literal>):
+ Specifies that updates to this property do or do not require acquisition of the
+ optimistic lock. In other words, dertermines if a version increment should occur when
+ this property is dirty.
+ </para>
+ </callout>
+ <callout arearefs="manytoone13">
+ <para>
+ <literal>lazy</literal> (optional - defaults to <literal>proxy</literal>):
+ By default, single point associations are proxied. <literal>lazy="no-proxy"</literal>
+ specifies that the property should be fetched lazily when the instance variable
+ is first accessed (requires build-time bytecode instrumentation).
+ <literal>lazy="false"</literal> specifies that the association will always
+ be eagerly fetched.
+ </para>
+ </callout>
+ <callout arearefs="manytoone14">
+ <para>
+ <literal>not-found</literal> (optional - defaults to <literal>exception</literal>):
+ Specifies how foreign keys that reference missing rows will be handled:
+ <literal>ignore</literal> will treat a missing row as a null association.
+ </para>
+ </callout>
+ <callout arearefs="manytoone15">
+ <para>
+ <literal>entity-name</literal> (optional): The entity name of the associated class.
+ </para>
+ </callout>
+ </calloutlist>
+ <callout arearefs="manytoone16">
+ <para>
+ <literal>formula</literal> (optional): an SQL expression that defines the value for a
+ <emphasis>computed</emphasis> foreign key.
+ </para>
+ </callout>
+ </programlistingco>
+
+ <para>
+ Setting a value of the <literal>cascade</literal> attribute to any meaningful
+ value other than <literal>none</literal> will propagate certain operations to the
+ associated object. The meaningful values are the names of Hibernate's basic
+ operations, <literal>persist, merge, delete, save-update, evict, replicate, lock,
+ refresh</literal>, as well as the special values <literal>delete-orphan</literal>
+ and <literal>all</literal> and comma-separated combinations of operation
+ names, for example, <literal>cascade="persist,merge,evict"</literal> or
+ <literal>cascade="all,delete-orphan"</literal>. See <xref linkend="objectstate-transitive"/>
+ for a full explanation. Note that single valued associations (many-to-one and
+ one-to-one associations) do not support orphan delete.
+ </para>
+
+ <para>
+ A typical <literal>many-to-one</literal> declaration looks as simple as this:
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="product" class="Product" column="PRODUCT_ID"/>]]></programlisting>
+
+ <para>
+ The <literal>property-ref</literal> attribute should only be used for mapping legacy
+ data where a foreign key refers to a unique key of the associated table other than
+ the primary key. This is an ugly relational model. For example, suppose the
+ <literal>Product</literal> class had a unique serial number, that is not the primary
+ key. (The <literal>unique</literal> attribute controls Hibernate's DDL generation with
+ the SchemaExport tool.)
+ </para>
+
+ <programlisting><![CDATA[<property name="serialNumber" unique="true" type="string" column="SERIAL_NUMBER"/>]]></programlisting>
+
+ <para>
+ Then the mapping for <literal>OrderItem</literal> might use:
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="product" property-ref="serialNumber" column="PRODUCT_SERIAL_NUMBER"/>]]></programlisting>
+
+ <para>
+ This is certainly not encouraged, however.
+ </para>
+
+ <para>
+ If the referenced unique key comprises multiple properties of the associated entity, you should
+ map the referenced properties inside a named <literal><properties></literal> element.
+ </para>
+
+ <para>
+ If the referenced unique key is the property of a component, you may specify a property path:
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="owner" property-ref="identity.ssn" column="OWNER_SSN"/>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-onetoone" revision="3">
+ <title>one-to-one</title>
+
+ <para>
+ A one-to-one association to another persistent class is declared using a
+ <literal>one-to-one</literal> element.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="onetoone1" coords="2 70"/>
+ <area id="onetoone2" coords="3 70"/>
+ <area id="onetoone3" coords="4 70"/>
+ <area id="onetoone4" coords="5 70"/>
+ <area id="onetoone5" coords="6 70"/>
+ <area id="onetoone6" coords="7 70"/>
+ <area id="onetoone7" coords="8 70"/>
+ <area id="onetoone8" coords="9 70"/>
+ <area id="onetoone9" coords="10 70"/>
+ <area id="onetoone10" coords="11 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<one-to-one
+ name="propertyName"
+ class="ClassName"
+ cascade="cascade_style"
+ constrained="true|false"
+ fetch="join|select"
+ property-ref="propertyNameFromAssociatedClass"
+ access="field|property|ClassName"
+ formula="any SQL expression"
+ lazy="proxy|no-proxy|false"
+ entity-name="EntityName"
+ node="element-name|@attribute-name|element/@attribute|."
+ embed-xml="true|false"
+ foreign-key="foreign_key_name"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="onetoone1">
+ <para>
+ <literal>name</literal>: The name of the property.
+ </para>
+ </callout>
+ <callout arearefs="onetoone2">
+ <para>
+ <literal>class</literal> (optional - defaults to the property type
+ determined by reflection): The name of the associated class.
+ </para>
+ </callout>
+ <callout arearefs="onetoone3">
+ <para>
+ <literal>cascade</literal> (optional) specifies which operations should
+ be cascaded from the parent object to the associated object.
+ </para>
+ </callout>
+ <callout arearefs="onetoone4">
+ <para>
+ <literal>constrained</literal> (optional) specifies that a foreign key constraint
+ on the primary key of the mapped table references the table of the associated
+ class. This option affects the order in which <literal>save()</literal> and
+ <literal>delete()</literal> are cascaded, and determines whether the association
+ may be proxied (it is also used by the schema export tool).
+ </para>
+ </callout>
+ <callout arearefs="onetoone5">
+ <para>
+ <literal>fetch</literal> (optional - defaults to <literal>select</literal>):
+ Chooses between outer-join fetching or sequential select fetching.
+ </para>
+ </callout>
+ <callout arearefs="onetoone6">
+ <para>
+ <literal>property-ref</literal>: (optional) The name of a property of the associated class
+ that is joined to the primary key of this class. If not specified, the primary key of
+ the associated class is used.
+ </para>
+ </callout>
+ <callout arearefs="onetoone7">
+ <para>
+ <literal>access</literal> (optional - defaults to <literal>property</literal>): The
+ strategy Hibernate should use for accessing the property value.
+ </para>
+ </callout>
+ <callout arearefs="onetoone8">
+ <para>
+ <literal>formula</literal> (optional): Almost all one to one associations map to the
+ primary key of the owning entity. In the rare case that this is not the case, you may
+ specify a some other column, columns or expression to join on using an SQL formula. (See
+ <literal>org.hibernate.test.onetooneformula</literal> for an example.)
+ </para>
+ </callout>
+ <callout arearefs="onetoone9">
+ <para>
+ <literal>lazy</literal> (optional - defaults to <literal>proxy</literal>):
+ By default, single point associations are proxied. <literal>lazy="no-proxy"</literal>
+ specifies that the property should be fetched lazily when the instance variable
+ is first accessed (requires build-time bytecode instrumentation).
+ <literal>lazy="false"</literal> specifies that the association will always
+ be eagerly fetched. <emphasis>Note that if <literal>constrained="false"</literal>,
+ proxying is impossible and Hibernate will eager fetch the association!</emphasis>
+ </para>
+ </callout>
+ <callout arearefs="onetoone10">
+ <para>
+ <literal>entity-name</literal> (optional): The entity name of the associated class.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ There are two varieties of one-to-one association:
+ </para>
+ <itemizedlist>
+ <listitem><para>
+ primary key associations
+ </para></listitem>
+ <listitem><para>
+ unique foreign key associations
+ </para></listitem>
+ </itemizedlist>
+
+ <para>
+ Primary key associations don't need an extra table column; if two rows are related by
+ the association then the two table rows share the same primary key value. So if you want
+ two objects to be related by a primary key association, you must make sure that they
+ are assigned the same identifier value!
+ </para>
+
+ <para>
+ For a primary key association, add the following mappings to <literal>Employee</literal> and
+ <literal>Person</literal>, respectively.
+ </para>
+
+ <programlisting><![CDATA[<one-to-one name="person" class="Person"/>]]></programlisting>
+ <programlisting><![CDATA[<one-to-one name="employee" class="Employee" constrained="true"/>]]></programlisting>
+
+ <para>
+ Now we must ensure that the primary keys of related rows in the PERSON and
+ EMPLOYEE tables are equal. We use a special Hibernate identifier generation strategy
+ called <literal>foreign</literal>:
+ </para>
+
+ <programlisting><![CDATA[<class name="person" table="PERSON">
+ <id name="id" column="PERSON_ID">
+ <generator class="foreign">
+ <param name="property">employee</param>
+ </generator>
+ </id>
+ ...
+ <one-to-one name="employee"
+ class="Employee"
+ constrained="true"/>
+</class>]]></programlisting>
+
+ <para>
+ A newly saved instance of <literal>Person</literal> is then assigned the same primary
+ key value as the <literal>Employee</literal> instance refered with the <literal>employee</literal>
+ property of that <literal>Person</literal>.
+ </para>
+
+ <para>
+ Alternatively, a foreign key with a unique constraint, from <literal>Employee</literal> to
+ <literal>Person</literal>, may be expressed as:
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="person" class="Person" column="PERSON_ID" unique="true"/>]]></programlisting>
+
+ <para>
+ And this association may be made bidirectional by adding the following to the
+ <literal>Person</literal> mapping:
+ </para>
+
+ <programlisting><![CDATA[<one-to-one name"employee" class="Employee" property-ref="person"/>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-naturalid">
+ <title>natural-id</title>
+
+ <programlisting><![CDATA[<natural-id mutable="true|false"/>
+ <property ... />
+ <many-to-one ... />
+ ......
+</natural-id>]]></programlisting>
+
+ <para>
+ Even though we recommend the use of surrogate keys as primary keys, you should still try
+ to identify natural keys for all entities. A natural key is a property or combination of
+ properties that is unique and non-null. If it is also immutable, even better. Map the
+ properties of the natural key inside the <literal><natural-id></literal> element.
+ Hibernate will generate the necessary unique key and nullability constraints, and your
+ mapping will be more self-documenting.
+ </para>
+
+ <para>
+ We strongly recommend that you implement <literal>equals()</literal> and
+ <literal>hashCode()</literal> to compare the natural key properties of the entity.
+ </para>
+
+ <para>
+ This mapping is not intended for use with entities with natural primary keys.
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>mutable</literal> (optional, defaults to <literal>false</literal>):
+ By default, natural identifier properties as assumed to be immutable (constant).
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-component" revision="2">
+ <title>component, dynamic-component</title>
+
+ <para>
+ The <literal><component></literal> element maps properties of a
+ child object to columns of the table of a parent class. Components may, in
+ turn, declare their own properties, components or collections. See
+ "Components" below.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="component1" coords="2 45"/>
+ <area id="component2" coords="3 45"/>
+ <area id="component3" coords="4 45"/>
+ <area id="component4" coords="5 45"/>
+ <area id="component5" coords="6 45"/>
+ <area id="component6" coords="7 45"/>
+ <area id="component7" coords="8 45"/>
+ <area id="component8" coords="9 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<component
+ name="propertyName"
+ class="className"
+ insert="true|false"
+ update="true|false"
+ access="field|property|ClassName"
+ lazy="true|false"
+ optimistic-lock="true|false"
+ unique="true|false"
+ node="element-name|."
+>
+
+ <property ...../>
+ <many-to-one .... />
+ ........
+</component>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="component1">
+ <para>
+ <literal>name</literal>: The name of the property.
+ </para>
+ </callout>
+ <callout arearefs="component2">
+ <para>
+ <literal>class</literal> (optional - defaults to the property type
+ determined by reflection): The name of the component (child) class.
+ </para>
+ </callout>
+ <callout arearefs="component3">
+ <para>
+ <literal>insert</literal>: Do the mapped columns appear in SQL
+ <literal>INSERT</literal>s?
+ </para>
+ </callout>
+ <callout arearefs="component4">
+ <para>
+ <literal>update</literal>: Do the mapped columns appear in SQL
+ <literal>UPDATE</literal>s?
+ </para>
+ </callout>
+ <callout arearefs="component5">
+ <para>
+ <literal>access</literal> (optional - defaults to <literal>property</literal>): The
+ strategy Hibernate should use for accessing the property value.
+ </para>
+ </callout>
+ <callout arearefs="component6">
+ <para>
+ <literal>lazy</literal> (optional - defaults to <literal>false</literal>): Specifies
+ that this component should be fetched lazily when the instance variable is first
+ accessed (requires build-time bytecode instrumentation).
+ </para>
+ </callout>
+ <callout arearefs="component7">
+ <para>
+ <literal>optimistic-lock</literal> (optional - defaults to <literal>true</literal>):
+ Specifies that updates to this component do or do not require acquisition of the
+ optimistic lock. In other words, determines if a version increment should occur when
+ this property is dirty.
+ </para>
+ </callout>
+ <callout arearefs="component8">
+ <para>
+ <literal>unique</literal> (optional - defaults to <literal>false</literal>):
+ Specifies that a unique constraint exists upon all mapped columns of the
+ component.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ The child <literal><property></literal> tags map properties of the
+ child class to table columns.
+ </para>
+
+ <para>
+ The <literal><component></literal> element allows a <literal><parent></literal>
+ subelement that maps a property of the component class as a reference back to the
+ containing entity.
+ </para>
+
+ <para>
+ The <literal><dynamic-component></literal> element allows a <literal>Map</literal>
+ to be mapped as a component, where the property names refer to keys of the map, see
+ <xref linkend="components-dynamic"/>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-properties" revision="2">
+ <title>properties</title>
+
+ <para>
+ The <literal><properties></literal> element allows the definition of a named,
+ logical grouping of properties of a class. The most important use of the construct
+ is that it allows a combination of properties to be the target of a
+ <literal>property-ref</literal>. It is also a convenient way to define a multi-column
+ unique constraint.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="properties1" coords="2 45"/>
+ <area id="properties2" coords="3 45"/>
+ <area id="properties3" coords="4 45"/>
+ <area id="properties4" coords="5 45"/>
+ <area id="properties5" coords="6 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<properties
+ name="logicalName"
+ insert="true|false"
+ update="true|false"
+ optimistic-lock="true|false"
+ unique="true|false"
+>
+
+ <property ...../>
+ <many-to-one .... />
+ ........
+</properties>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="properties1">
+ <para>
+ <literal>name</literal>: The logical name of the grouping -
+ <emphasis>not</emphasis> an actual property name.
+ </para>
+ </callout>
+ <callout arearefs="properties2">
+ <para>
+ <literal>insert</literal>: Do the mapped columns appear in SQL
+ <literal>INSERT</literal>s?
+ </para>
+ </callout>
+ <callout arearefs="properties3">
+ <para>
+ <literal>update</literal>: Do the mapped columns appear in SQL
+ <literal>UPDATE</literal>s?
+ </para>
+ </callout>
+ <callout arearefs="properties4">
+ <para>
+ <literal>optimistic-lock</literal> (optional - defaults to <literal>true</literal>):
+ Specifies that updates to these properties do or do not require acquisition of the
+ optimistic lock. In other words, determines if a version increment should occur when
+ these properties are dirty.
+ </para>
+ </callout>
+ <callout arearefs="properties5">
+ <para>
+ <literal>unique</literal> (optional - defaults to <literal>false</literal>):
+ Specifies that a unique constraint exists upon all mapped columns of the
+ component.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ For example, if we have the following <literal><properties></literal> mapping:
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="personNumber"/>
+ ...
+ <properties name="name"
+ unique="true" update="false">
+ <property name="firstName"/>
+ <property name="initial"/>
+ <property name="lastName"/>
+ </properties>
+</class>]]></programlisting>
+
+ <para>
+ Then we might have some legacy data association which refers to this unique key of
+ the <literal>Person</literal> table, instead of to the primary key:
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="person"
+ class="Person" property-ref="name">
+ <column name="firstName"/>
+ <column name="initial"/>
+ <column name="lastName"/>
+</many-to-one>]]></programlisting>
+
+ <para>
+ We don't recommend the use of this kind of thing outside the context of mapping
+ legacy data.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-subclass" revision="4">
+ <title>subclass</title>
+
+ <para>
+ Finally, polymorphic persistence requires the declaration of each subclass of
+ the root persistent class. For the table-per-class-hierarchy
+ mapping strategy, the <literal><subclass></literal> declaration is used.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="subclass1" coords="2 55"/>
+ <area id="subclass2" coords="3 55"/>
+ <area id="subclass3" coords="4 55"/>
+ <area id="subclass4" coords="5 55"/>
+ </areaspec>
+ <programlisting><![CDATA[<subclass
+ name="ClassName"
+ discriminator-value="discriminator_value"
+ proxy="ProxyInterface"
+ lazy="true|false"
+ dynamic-update="true|false"
+ dynamic-insert="true|false"
+ entity-name="EntityName"
+ node="element-name"
+ extends="SuperclassName">
+
+ <property .... />
+ .....
+</subclass>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="subclass1">
+ <para>
+ <literal>name</literal>: The fully qualified class name of the subclass.
+ </para>
+ </callout>
+ <callout arearefs="subclass2">
+ <para>
+ <literal>discriminator-value</literal> (optional - defaults to the class name): A
+ value that distiguishes individual subclasses.
+ </para>
+ </callout>
+ <callout arearefs="subclass3">
+ <para>
+ <literal>proxy</literal> (optional): Specifies a class or interface to use for
+ lazy initializing proxies.
+ </para>
+ </callout>
+ <callout arearefs="subclass4">
+ <para>
+ <literal>lazy</literal> (optional, defaults to <literal>true</literal>): Setting
+ <literal>lazy="false"</literal> disables the use of lazy fetching.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Each subclass should declare its own persistent properties and subclasses.
+ <literal><version></literal> and <literal><id></literal> properties
+ are assumed to be inherited from the root class. Each subclass in a heirarchy must
+ define a unique <literal>discriminator-value</literal>. If none is specified, the
+ fully qualified Java class name is used.
+ </para>
+
+ <para>
+ For information about inheritance mappings, see <xref linkend="inheritance"/>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-joinedsubclass" revision="3">
+ <title>joined-subclass</title>
+
+ <para>
+ Alternatively, each subclass may be mapped to its own table (table-per-subclass
+ mapping strategy). Inherited state is retrieved by joining with the table of the
+ superclass. We use the <literal><joined-subclass></literal> element.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="joinedsubclass1" coords="2 45"/>
+ <area id="joinedsubclass2" coords="3 45"/>
+ <area id="joinedsubclass3" coords="4 45"/>
+ <area id="joinedsubclass4" coords="5 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<joined-subclass
+ name="ClassName"
+ table="tablename"
+ proxy="ProxyInterface"
+ lazy="true|false"
+ dynamic-update="true|false"
+ dynamic-insert="true|false"
+ schema="schema"
+ catalog="catalog"
+ extends="SuperclassName"
+ persister="ClassName"
+ subselect="SQL expression"
+ entity-name="EntityName"
+ node="element-name">
+
+ <key .... >
+
+ <property .... />
+ .....
+</joined-subclass>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="joinedsubclass1">
+ <para>
+ <literal>name</literal>: The fully qualified class name of the subclass.
+ </para>
+ </callout>
+ <callout arearefs="joinedsubclass2">
+ <para>
+ <literal>table</literal>: The name of the subclass table.
+ </para>
+ </callout>
+ <callout arearefs="joinedsubclass3">
+ <para>
+ <literal>proxy</literal> (optional): Specifies a class or interface to use
+ for lazy initializing proxies.
+ </para>
+ </callout>
+ <callout arearefs="joinedsubclass4">
+ <para>
+ <literal>lazy</literal> (optional, defaults to <literal>true</literal>): Setting
+ <literal>lazy="false"</literal> disables the use of lazy fetching.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ No discriminator column is required for this mapping strategy. Each subclass must,
+ however, declare a table column holding the object identifier using the
+ <literal><key></literal> element. The mapping at the start of the chapter
+ would be re-written as:
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="eg">
+
+ <class name="Cat" table="CATS">
+ <id name="id" column="uid" type="long">
+ <generator class="hilo"/>
+ </id>
+ <property name="birthdate" type="date"/>
+ <property name="color" not-null="true"/>
+ <property name="sex" not-null="true"/>
+ <property name="weight"/>
+ <many-to-one name="mate"/>
+ <set name="kittens">
+ <key column="MOTHER"/>
+ <one-to-many class="Cat"/>
+ </set>
+ <joined-subclass name="DomesticCat" table="DOMESTIC_CATS">
+ <key column="CAT"/>
+ <property name="name" type="string"/>
+ </joined-subclass>
+ </class>
+
+ <class name="eg.Dog">
+ <!-- mapping for Dog could go here -->
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ For information about inheritance mappings, see <xref linkend="inheritance"/>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-unionsubclass" revision="2">
+ <title>union-subclass</title>
+
+ <para>
+ A third option is to map only the concrete classes of an inheritance hierarchy
+ to tables, (the table-per-concrete-class strategy) where each table defines all
+ persistent state of the class, including inherited state. In Hibernate, it is
+ not absolutely necessary to explicitly map such inheritance hierarchies. You
+ can simply map each class with a separate <literal><class></literal>
+ declaration. However, if you wish use polymorphic associations (e.g. an association
+ to the superclass of your hierarchy), you need to
+ use the <literal><union-subclass></literal> mapping.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="unionsubclass1" coords="2 45"/>
+ <area id="unionsubclass2" coords="3 45"/>
+ <area id="unionsubclass3" coords="4 45"/>
+ <area id="unionsubclass4" coords="5 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<union-subclass
+ name="ClassName"
+ table="tablename"
+ proxy="ProxyInterface"
+ lazy="true|false"
+ dynamic-update="true|false"
+ dynamic-insert="true|false"
+ schema="schema"
+ catalog="catalog"
+ extends="SuperclassName"
+ abstract="true|false"
+ persister="ClassName"
+ subselect="SQL expression"
+ entity-name="EntityName"
+ node="element-name">
+
+ <property .... />
+ .....
+</union-subclass>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="unionsubclass1">
+ <para>
+ <literal>name</literal>: The fully qualified class name of the subclass.
+ </para>
+ </callout>
+ <callout arearefs="unionsubclass2">
+ <para>
+ <literal>table</literal>: The name of the subclass table.
+ </para>
+ </callout>
+ <callout arearefs="unionsubclass3">
+ <para>
+ <literal>proxy</literal> (optional): Specifies a class or interface to use
+ for lazy initializing proxies.
+ </para>
+ </callout>
+ <callout arearefs="unionsubclass4">
+ <para>
+ <literal>lazy</literal> (optional, defaults to <literal>true</literal>): Setting
+ <literal>lazy="false"</literal> disables the use of lazy fetching.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ No discriminator column or key column is required for this mapping strategy.
+ </para>
+
+ <para>
+ For information about inheritance mappings, see <xref linkend="inheritance"/>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-join" revision="3">
+ <title>join</title>
+
+ <para>
+ Using the <literal><join></literal> element, it is possible to map
+ properties of one class to several tables, when there's a 1-to-1 relationship between the tables.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="join1" coords="2 50"/>
+ <area id="join2" coords="3 50"/>
+ <area id="join3" coords="4 50"/>
+ <area id="join4" coords="5 50"/>
+ <area id="join5" coords="6 50"/>
+ <area id="join6" coords="7 50"/>
+ </areaspec>
+ <programlisting><![CDATA[<join
+ table="tablename"
+ schema="owner"
+ catalog="catalog"
+ fetch="join|select"
+ inverse="true|false"
+ optional="true|false">
+
+ <key ... />
+
+ <property ... />
+ ...
+</join>]]></programlisting>
+
+ <calloutlist>
+ <callout arearefs="join1">
+ <para>
+ <literal>table</literal>: The name of the joined table.
+ </para>
+ </callout>
+ <callout arearefs="join2">
+ <para>
+ <literal>schema</literal> (optional): Override the schema name specified by
+ the root <literal><hibernate-mapping></literal> element.
+ </para>
+ </callout>
+ <callout arearefs="join3">
+ <para>
+ <literal>catalog</literal> (optional): Override the catalog name specified by
+ the root <literal><hibernate-mapping></literal> element.
+ </para>
+ </callout>
+ <callout arearefs="join4">
+ <para>
+ <literal>fetch</literal> (optional - defaults to <literal>join</literal>):
+ If set to <literal>join</literal>, the default, Hibernate will use an inner join
+ to retrieve a <literal><join></literal> defined by a class or its superclasses
+ and an outer join for a <literal><join></literal> defined by a subclass.
+ If set to <literal>select</literal> then Hibernate will use a sequential select for
+ a <literal><join></literal> defined on a subclass, which will be issued only
+ if a row turns out to represent an instance of the subclass. Inner joins will still
+ be used to retrieve a <literal><join></literal> defined by the class and its
+ superclasses.
+ </para>
+ </callout>
+ <callout arearefs="join5">
+ <para>
+ <literal>inverse</literal> (optional - defaults to <literal>false</literal>):
+ If enabled, Hibernate will not try to insert or update the properties defined
+ by this join.
+ </para>
+ </callout>
+ <callout arearefs="join6">
+ <para>
+ <literal>optional</literal> (optional - defaults to <literal>false</literal>):
+ If enabled, Hibernate will insert a row only if the properties defined by this
+ join are non-null and will always use an outer join to retrieve the properties.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ For example, the address information for a person can be mapped to a separate
+ table (while preserving value type semantics for all properties):
+ </para>
+
+ <programlisting><![CDATA[<class name="Person"
+ table="PERSON">
+
+ <id name="id" column="PERSON_ID">...</id>
+
+ <join table="ADDRESS">
+ <key column="ADDRESS_ID"/>
+ <property name="address"/>
+ <property name="zip"/>
+ <property name="country"/>
+ </join>
+ ...]]></programlisting>
+
+ <para>
+ This feature is often only useful for legacy data models, we recommend fewer
+ tables than classes and a fine-grained domain model. However, it is useful
+ for switching between inheritance mapping strategies in a single hierarchy, as
+ explained later.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-key">
+ <title>key</title>
+
+ <para>
+ We've seen the <literal><key></literal> element crop up a few times
+ now. It appears anywhere the parent mapping element defines a join to
+ a new table, and defines the foreign key in the joined table, that references
+ the primary key of the original table.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="key1" coords="2 50"/>
+ <area id="key2" coords="3 50"/>
+ <area id="key3" coords="4 50"/>
+ <area id="key4" coords="5 50"/>
+ <area id="key5" coords="6 50"/>
+ <area id="key6" coords="7 50"/>
+ </areaspec>
+ <programlisting><![CDATA[<key
+ column="columnname"
+ on-delete="noaction|cascade"
+ property-ref="propertyName"
+ not-null="true|false"
+ update="true|false"
+ unique="true|false"
+/>]]></programlisting>
+
+ <calloutlist>
+ <callout arearefs="key1">
+ <para>
+ <literal>column</literal> (optional): The name of the foreign key column.
+ This may also be specified by nested <literal><column></literal>
+ element(s).
+ </para>
+ </callout>
+ <callout arearefs="key2">
+ <para>
+ <literal>on-delete</literal> (optional, defaults to <literal>noaction</literal>):
+ Specifies whether the foreign key constraint has database-level cascade delete
+ enabled.
+ </para>
+ </callout>
+ <callout arearefs="key3">
+ <para>
+ <literal>property-ref</literal> (optional): Specifies that the foreign key refers
+ to columns that are not the primary key of the orginal table. (Provided for
+ legacy data.)
+ </para>
+ </callout>
+ <callout arearefs="key4">
+ <para>
+ <literal>not-null</literal> (optional): Specifies that the foreign key columns
+ are not nullable (this is implied whenever the foreign key is also part of the
+ primary key).
+ </para>
+ </callout>
+ <callout arearefs="key5">
+ <para>
+ <literal>update</literal> (optional): Specifies that the foreign key should never
+ be updated (this is implied whenever the foreign key is also part of the primary
+ key).
+ </para>
+ </callout>
+ <callout arearefs="key6">
+ <para>
+ <literal>unique</literal> (optional): Specifies that the foreign key should have
+ a unique constraint (this is implied whenever the foreign key is also the primary key).
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ We recommend that for systems where delete performance is important, all keys should be
+ defined <literal>on-delete="cascade"</literal>, and Hibernate will use a database-level
+ <literal>ON CASCADE DELETE</literal> constraint, instead of many individual
+ <literal>DELETE</literal> statements. Be aware that this feature bypasses Hibernate's
+ usual optimistic locking strategy for versioned data.
+ </para>
+
+ <para>
+ The <literal>not-null</literal> and <literal>update</literal> attributes are useful when
+ mapping a unidirectional one to many association. If you map a unidirectional one to many
+ to a non-nullable foreign key, you <emphasis>must</emphasis> declare the key column using
+ <literal><key not-null="true"></literal>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-column" revision="4">
+ <title>column and formula elements</title>
+ <para>
+ Any mapping element which accepts a <literal>column</literal> attribute will alternatively
+ accept a <literal><column></literal> subelement. Likewise, <literal><formula></literal>
+ is an alternative to the <literal>formula</literal> attribute.
+ </para>
+
+ <programlisting><![CDATA[<column
+ name="column_name"
+ length="N"
+ precision="N"
+ scale="N"
+ not-null="true|false"
+ unique="true|false"
+ unique-key="multicolumn_unique_key_name"
+ index="index_name"
+ sql-type="sql_type_name"
+ check="SQL expression"
+ default="SQL expression"/>]]></programlisting>
+
+ <programlisting><![CDATA[<formula>SQL expression</formula>]]></programlisting>
+
+ <para>
+ <literal>column</literal> and <literal>formula</literal> attributes may even be combined
+ within the same property or association mapping to express, for example, exotic join
+ conditions.
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="homeAddress" class="Address"
+ insert="false" update="false">
+ <column name="person_id" not-null="true" length="10"/>
+ <formula>'MAILING'</formula>
+</many-to-one>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-import">
+ <title>import</title>
+
+ <para>
+ Suppose your application has two persistent classes with the same name, and you don't want to
+ specify the fully qualified (package) name in Hibernate queries. Classes may be "imported"
+ explicitly, rather than relying upon <literal>auto-import="true"</literal>. You may even import
+ classes and interfaces that are not explicitly mapped.
+ </para>
+
+ <programlisting><![CDATA[<import class="java.lang.Object" rename="Universe"/>]]></programlisting>
+
+ <programlistingco>
+ <areaspec>
+ <area id="import1" coords="2 40"/>
+ <area id="import2" coords="3 40"/>
+ </areaspec>
+ <programlisting><![CDATA[<import
+ class="ClassName"
+ rename="ShortName"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="import1">
+ <para>
+ <literal>class</literal>: The fully qualified class name of of any Java class.
+ </para>
+ </callout>
+ <callout arearefs="import2">
+ <para>
+ <literal>rename</literal> (optional - defaults to the unqualified class name):
+ A name that may be used in the query language.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ </sect2>
+
+ <sect2 id="mapping-types-anymapping" revision="2">
+ <title>any</title>
+
+ <para>
+ There is one further type of property mapping. The <literal><any></literal> mapping element
+ defines a polymorphic association to classes from multiple tables. This type of mapping always
+ requires more than one column. The first column holds the type of the associated entity.
+ The remaining columns hold the identifier. It is impossible to specify a foreign key constraint
+ for this kind of association, so this is most certainly not meant as the usual way of mapping
+ (polymorphic) associations. You should use this only in very special cases (eg. audit logs,
+ user session data, etc).
+ </para>
+
+ <para>
+ The <literal>meta-type</literal> attribute lets the application specify a custom type that
+ maps database column values to persistent classes which have identifier properties of the
+ type specified by <literal>id-type</literal>. You must specify the mapping from values of
+ the meta-type to class names.
+ </para>
+
+ <programlisting><![CDATA[<any name="being" id-type="long" meta-type="string">
+ <meta-value value="TBL_ANIMAL" class="Animal"/>
+ <meta-value value="TBL_HUMAN" class="Human"/>
+ <meta-value value="TBL_ALIEN" class="Alien"/>
+ <column name="table_name"/>
+ <column name="id"/>
+</any>]]></programlisting>
+
+ <programlistingco>
+ <areaspec>
+ <area id="any1" coords="2 50"/>
+ <area id="any2" coords="3 50"/>
+ <area id="any3" coords="4 50"/>
+ <area id="any4" coords="5 50"/>
+ <area id="any5" coords="6 50"/>
+ <area id="any6" coords="7 50"/>
+ </areaspec>
+ <programlisting><![CDATA[<any
+ name="propertyName"
+ id-type="idtypename"
+ meta-type="metatypename"
+ cascade="cascade_style"
+ access="field|property|ClassName"
+ optimistic-lock="true|false"
+>
+ <meta-value ... />
+ <meta-value ... />
+ .....
+ <column .... />
+ <column .... />
+ .....
+</any>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="any1">
+ <para>
+ <literal>name</literal>: the property name.
+ </para>
+ </callout>
+ <callout arearefs="any2">
+ <para>
+ <literal>id-type</literal>: the identifier type.
+ </para>
+ </callout>
+ <callout arearefs="any3">
+ <para>
+ <literal>meta-type</literal> (optional - defaults to <literal>string</literal>):
+ Any type that is allowed for a discriminator mapping.
+ </para>
+ </callout>
+ <callout arearefs="any4">
+ <para>
+ <literal>cascade</literal> (optional- defaults to <literal>none</literal>):
+ the cascade style.
+ </para>
+ </callout>
+ <callout arearefs="any5">
+ <para>
+ <literal>access</literal> (optional - defaults to <literal>property</literal>): The
+ strategy Hibernate should use for accessing the property value.
+ </para>
+ </callout>
+ <callout arearefs="any6">
+ <para>
+ <literal>optimistic-lock</literal> (optional - defaults to <literal>true</literal>):
+ Specifies that updates to this property do or do not require acquisition of the
+ optimistic lock. In other words, define if a version increment should occur if this
+ property is dirty.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="mapping-types">
+ <title>Hibernate Types</title>
+
+ <sect2 id="mapping-types-entitiesvalues" revision="1">
+ <title>Entities and values</title>
+
+ <para>
+ To understand the behaviour of various Java language-level objects with respect
+ to the persistence service, we need to classify them into two groups:
+ </para>
+
+ <para>
+ An <emphasis>entity</emphasis> exists independently of any other objects holding
+ references to the entity. Contrast this with the usual Java model where an
+ unreferenced object is garbage collected. Entities must be explicitly saved and
+ deleted (except that saves and deletions may be <emphasis>cascaded</emphasis>
+ from a parent entity to its children). This is different from the ODMG model of
+ object persistence by reachablity - and corresponds more closely to how
+ application objects are usually used in large systems. Entities support
+ circular and shared references. They may also be versioned.
+ </para>
+
+ <para>
+ An entity's persistent state consists of references to other entities and
+ instances of <emphasis>value</emphasis> types. Values are primitives,
+ collections (not what's inside a collection), components and certain immutable
+ objects. Unlike entities, values (in particular collections and components)
+ <emphasis>are</emphasis> persisted and deleted by reachability. Since value
+ objects (and primitives) are persisted and deleted along with their containing
+ entity they may not be independently versioned. Values have no independent
+ identity, so they cannot be shared by two entities or collections.
+ </para>
+
+ <para>
+ Up until now, we've been using the term "persistent class" to refer to
+ entities. We will continue to do that. Strictly speaking, however, not all
+ user-defined classes with persistent state are entities. A
+ <emphasis>component</emphasis> is a user defined class with value semantics.
+ A Java property of type <literal>java.lang.String</literal> also has value
+ semantics. Given this definition, we can say that all types (classes) provided
+ by the JDK have value type semantics in Java, while user-defined types may
+ be mapped with entity or value type semantics. This decision is up to the
+ application developer. A good hint for an entity class in a domain model are
+ shared references to a single instance of that class, while composition or
+ aggregation usually translates to a value type.
+ </para>
+
+ <para>
+ We'll revisit both concepts throughout the documentation.
+ </para>
+
+ <para>
+ The challenge is to map the Java type system (and the developers' definition of
+ entities and value types) to the SQL/database type system. The bridge between
+ both systems is provided by Hibernate: for entities we use
+ <literal><class></literal>, <literal><subclass></literal> and so on.
+ For value types we use <literal><property></literal>,
+ <literal><component></literal>, etc, usually with a <literal>type</literal>
+ attribute. The value of this attribute is the name of a Hibernate
+ <emphasis>mapping type</emphasis>. Hibernate provides many mappings (for standard
+ JDK value types) out of the box. You can write your own mapping types and implement your
+ custom conversion strategies as well, as you'll see later.
+ </para>
+
+ <para>
+ All built-in Hibernate types except collections support null semantics.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-types-basictypes" revision="3">
+ <title>Basic value types</title>
+
+ <para>
+ The built-in <emphasis>basic mapping types</emphasis> may be roughly categorized into
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>integer, long, short, float, double, character, byte,
+ boolean, yes_no, true_false</literal></term>
+ <listitem>
+ <para>
+ Type mappings from Java primitives or wrapper classes to appropriate
+ (vendor-specific) SQL column types. <literal>boolean, yes_no</literal>
+ and <literal>true_false</literal> are all alternative encodings for
+ a Java <literal>boolean</literal> or <literal>java.lang.Boolean</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>string</literal></term>
+ <listitem>
+ <para>
+ A type mapping from <literal>java.lang.String</literal> to
+ <literal>VARCHAR</literal> (or Oracle <literal>VARCHAR2</literal>).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>date, time, timestamp</literal></term>
+ <listitem>
+ <para>
+ Type mappings from <literal>java.util.Date</literal> and its subclasses
+ to SQL types <literal>DATE</literal>, <literal>TIME</literal> and
+ <literal>TIMESTAMP</literal> (or equivalent).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>calendar, calendar_date</literal></term>
+ <listitem>
+ <para>
+ Type mappings from <literal>java.util.Calendar</literal> to
+ SQL types <literal>TIMESTAMP</literal> and <literal>DATE</literal>
+ (or equivalent).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>big_decimal, big_integer</literal></term>
+ <listitem>
+ <para>
+ Type mappings from <literal>java.math.BigDecimal</literal> and
+ <literal>java.math.BigInteger</literal> to <literal>NUMERIC</literal>
+ (or Oracle <literal>NUMBER</literal>).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>locale, timezone, currency</literal></term>
+ <listitem>
+ <para>
+ Type mappings from <literal>java.util.Locale</literal>,
+ <literal>java.util.TimeZone</literal> and
+ <literal>java.util.Currency</literal>
+ to <literal>VARCHAR</literal> (or Oracle <literal>VARCHAR2</literal>).
+ Instances of <literal>Locale</literal> and <literal>Currency</literal> are
+ mapped to their ISO codes. Instances of <literal>TimeZone</literal> are
+ mapped to their <literal>ID</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>class</literal></term>
+ <listitem>
+ <para>
+ A type mapping from <literal>java.lang.Class</literal> to
+ <literal>VARCHAR</literal> (or Oracle <literal>VARCHAR2</literal>).
+ A <literal>Class</literal> is mapped to its fully qualified name.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>binary</literal></term>
+ <listitem>
+ <para>
+ Maps byte arrays to an appropriate SQL binary type.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>text</literal></term>
+ <listitem>
+ <para>
+ Maps long Java strings to a SQL <literal>CLOB</literal> or
+ <literal>TEXT</literal> type.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>serializable</literal></term>
+ <listitem>
+ <para>
+ Maps serializable Java types to an appropriate SQL binary type. You
+ may also indicate the Hibernate type <literal>serializable</literal> with
+ the name of a serializable Java class or interface that does not default
+ to a basic type.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>clob, blob</literal></term>
+ <listitem>
+ <para>
+ Type mappings for the JDBC classes <literal>java.sql.Clob</literal> and
+ <literal>java.sql.Blob</literal>. These types may be inconvenient for some
+ applications, since the blob or clob object may not be reused outside of
+ a transaction. (Furthermore, driver support is patchy and inconsistent.)
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <literal>imm_date, imm_time, imm_timestamp, imm_calendar, imm_calendar_date,
+ imm_serializable, imm_binary</literal>
+ </term>
+ <listitem>
+ <para>
+ Type mappings for what are usually considered mutable Java types, where
+ Hibernate makes certain optimizations appropriate only for immutable
+ Java types, and the application treats the object as immutable. For
+ example, you should not call <literal>Date.setTime()</literal> for an
+ instance mapped as <literal>imm_timestamp</literal>. To change the
+ value of the property, and have that change made persistent, the
+ application must assign a new (nonidentical) object to the property.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ </para>
+
+ <para>
+ Unique identifiers of entities and collections may be of any basic type except
+ <literal>binary</literal>, <literal>blob</literal> and <literal>clob</literal>.
+ (Composite identifiers are also allowed, see below.)
+ </para>
+
+ <para>
+ The basic value types have corresponding <literal>Type</literal> constants defined on
+ <literal>org.hibernate.Hibernate</literal>. For example, <literal>Hibernate.STRING</literal>
+ represents the <literal>string</literal> type.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-types-custom" revision="2">
+ <title>Custom value types</title>
+
+ <para>
+ It is relatively easy for developers to create their own value types. For example,
+ you might want to persist properties of type <literal>java.lang.BigInteger</literal>
+ to <literal>VARCHAR</literal> columns. Hibernate does not provide a built-in type
+ for this. But custom types are not limited to mapping a property (or collection element)
+ to a single table column. So, for example, you might have a Java property
+ <literal>getName()</literal>/<literal>setName()</literal> of type
+ <literal>java.lang.String</literal> that is persisted to the columns
+ <literal>FIRST_NAME</literal>, <literal>INITIAL</literal>, <literal>SURNAME</literal>.
+ </para>
+
+ <para>
+ To implement a custom type, implement either <literal>org.hibernate.UserType</literal>
+ or <literal>org.hibernate.CompositeUserType</literal> and declare properties using the
+ fully qualified classname of the type. Check out
+ <literal>org.hibernate.test.DoubleStringType</literal> to see the kind of things that
+ are possible.
+ </para>
+
+ <programlisting><![CDATA[<property name="twoStrings" type="org.hibernate.test.DoubleStringType">
+ <column name="first_string"/>
+ <column name="second_string"/>
+</property>]]></programlisting>
+
+ <para>
+ Notice the use of <literal><column></literal> tags to map a property to multiple
+ columns.
+ </para>
+
+ <para>
+ The <literal>CompositeUserType</literal>, <literal>EnhancedUserType</literal>,
+ <literal>UserCollectionType</literal>, and <literal>UserVersionType</literal>
+ interfaces provide support for more specialized uses.
+ </para>
+
+ <para>
+ You may even supply parameters to a <literal>UserType</literal> in the mapping file. To
+ do this, your <literal>UserType</literal> must implement the
+ <literal>org.hibernate.usertype.ParameterizedType</literal> interface. To supply parameters
+ to your custom type, you can use the <literal><type></literal> element in your mapping
+ files.
+ </para>
+
+ <programlisting><![CDATA[<property name="priority">
+ <type name="com.mycompany.usertypes.DefaultValueIntegerType">
+ <param name="default">0</param>
+ </type>
+</property>]]></programlisting>
+
+ <para>
+ The <literal>UserType</literal> can now retrieve the value for the parameter named
+ <literal>default</literal> from the <literal>Properties</literal> object passed to it.
+ </para>
+
+ <para>
+ If you use a certain <literal>UserType</literal> very often, it may be useful to define a
+ shorter name for it. You can do this using the <literal><typedef></literal> element.
+ Typedefs assign a name to a custom type, and may also contain a list of default
+ parameter values if the type is parameterized.
+ </para>
+
+ <programlisting><![CDATA[<typedef class="com.mycompany.usertypes.DefaultValueIntegerType" name="default_zero">
+ <param name="default">0</param>
+</typedef>]]></programlisting>
+
+ <programlisting><![CDATA[<property name="priority" type="default_zero"/>]]></programlisting>
+
+ <para>
+ It is also possible to override the parameters supplied in a typedef on a case-by-case basis
+ by using type parameters on the property mapping.
+ </para>
+
+ <para>
+ Even though Hibernate's rich range of built-in types and support for components means you
+ will very rarely <emphasis>need</emphasis> to use a custom type, it is nevertheless
+ considered good form to use custom types for (non-entity) classes that occur frequently
+ in your application. For example, a <literal>MonetaryAmount</literal> class is a good
+ candidate for a <literal>CompositeUserType</literal>, even though it could easily be mapped
+ as a component. One motivation for this is abstraction. With a custom type, your mapping
+ documents would be future-proofed against possible changes in your way of representing
+ monetary values.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="mapping-entityname">
+ <title>Mapping a class more than once</title>
+ <para>
+ It is possible to provide more than one mapping for a particular persistent class. In this
+ case you must specify an <emphasis>entity name</emphasis> do disambiguate between instances
+ of the two mapped entities. (By default, the entity name is the same as the class name.)
+ Hibernate lets you specify the entity name when working with persistent objects, when writing
+ queries, or when mapping associations to the named entity.
+ </para>
+
+ <programlisting><![CDATA[<class name="Contract" table="Contracts"
+ entity-name="CurrentContract">
+ ...
+ <set name="history" inverse="true"
+ order-by="effectiveEndDate desc">
+ <key column="currentContractId"/>
+ <one-to-many entity-name="HistoricalContract"/>
+ </set>
+</class>
+
+<class name="Contract" table="ContractHistory"
+ entity-name="HistoricalContract">
+ ...
+ <many-to-one name="currentContract"
+ column="currentContractId"
+ entity-name="CurrentContract"/>
+</class>]]></programlisting>
+
+ <para>
+ Notice how associations are now specified using <literal>entity-name</literal> instead of
+ <literal>class</literal>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="mapping-quotedidentifiers">
+ <title>SQL quoted identifiers</title>
+ <para>
+ You may force Hibernate to quote an identifier in the generated SQL by enclosing the table or
+ column name in backticks in the mapping document. Hibernate will use the correct quotation
+ style for the SQL <literal>Dialect</literal> (usually double quotes, but brackets for SQL
+ Server and backticks for MySQL).
+ </para>
+
+ <programlisting><![CDATA[<class name="LineItem" table="`Line Item`">
+ <id name="id" column="`Item Id`"/><generator class="assigned"/></id>
+ <property name="itemNumber" column="`Item #`"/>
+ ...
+</class>]]></programlisting>
+
+ </sect1>
+
+
+ <sect1 id="mapping-alternatives">
+ <title>Metadata alternatives</title>
+
+ <para>
+ XML isn't for everyone, and so there are some alternative ways to define O/R mapping metadata in Hibernate.
+ </para>
+
+ <sect2 id="mapping-xdoclet">
+ <title>Using XDoclet markup</title>
+
+ <para>
+ Many Hibernate users prefer to embed mapping information directly in sourcecode using
+ XDoclet <literal>@hibernate.tags</literal>. We will not cover this approach in this
+ document, since strictly it is considered part of XDoclet. However, we include the
+ following example of the <literal>Cat</literal> class with XDoclet mappings.
+ </para>
+
+ <programlisting><![CDATA[package eg;
+import java.util.Set;
+import java.util.Date;
+
+/**
+ * @hibernate.class
+ * table="CATS"
+ */
+public class Cat {
+ private Long id; // identifier
+ private Date birthdate;
+ private Cat mother;
+ private Set kittens
+ private Color color;
+ private char sex;
+ private float weight;
+
+ /*
+ * @hibernate.id
+ * generator-class="native"
+ * column="CAT_ID"
+ */
+ public Long getId() {
+ return id;
+ }
+ private void setId(Long id) {
+ this.id=id;
+ }
+
+ /**
+ * @hibernate.many-to-one
+ * column="PARENT_ID"
+ */
+ public Cat getMother() {
+ return mother;
+ }
+ void setMother(Cat mother) {
+ this.mother = mother;
+ }
+
+ /**
+ * @hibernate.property
+ * column="BIRTH_DATE"
+ */
+ public Date getBirthdate() {
+ return birthdate;
+ }
+ void setBirthdate(Date date) {
+ birthdate = date;
+ }
+ /**
+ * @hibernate.property
+ * column="WEIGHT"
+ */
+ public float getWeight() {
+ return weight;
+ }
+ void setWeight(float weight) {
+ this.weight = weight;
+ }
+
+ /**
+ * @hibernate.property
+ * column="COLOR"
+ * not-null="true"
+ */
+ public Color getColor() {
+ return color;
+ }
+ void setColor(Color color) {
+ this.color = color;
+ }
+ /**
+ * @hibernate.set
+ * inverse="true"
+ * order-by="BIRTH_DATE"
+ * @hibernate.collection-key
+ * column="PARENT_ID"
+ * @hibernate.collection-one-to-many
+ */
+ public Set getKittens() {
+ return kittens;
+ }
+ void setKittens(Set kittens) {
+ this.kittens = kittens;
+ }
+ // addKitten not needed by Hibernate
+ public void addKitten(Cat kitten) {
+ kittens.add(kitten);
+ }
+
+ /**
+ * @hibernate.property
+ * column="SEX"
+ * not-null="true"
+ * update="false"
+ */
+ public char getSex() {
+ return sex;
+ }
+ void setSex(char sex) {
+ this.sex=sex;
+ }
+}]]></programlisting>
+
+ <para>
+ See the Hibernate web site for more examples of XDoclet and Hibernate.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-annotations" revision="2">
+ <title>Using JDK 5.0 Annotations</title>
+
+ <para>
+ JDK 5.0 introduced XDoclet-style annotations at the language level, type-safe and
+ checked at compile time. This mechnism is more powerful than XDoclet annotations and
+ better supported by tools and IDEs. IntelliJ IDEA, for example, supports auto-completion
+ and syntax highlighting of JDK 5.0 annotations. The new revision of the EJB specification
+ (JSR-220) uses JDK 5.0 annotations as the primary metadata mechanism for entity beans.
+ Hibernate3 implements the <literal>EntityManager</literal> of JSR-220 (the persistence API),
+ support for mapping metadata is available via the <emphasis>Hibernate Annotations</emphasis>
+ package, as a separate download. Both EJB3 (JSR-220) and Hibernate3 metadata is supported.
+ </para>
+
+ <para>
+ This is an example of a POJO class annotated as an EJB entity bean:
+ </para>
+
+ <programlisting><![CDATA[@Entity(access = AccessType.FIELD)
+public class Customer implements Serializable {
+
+ @Id;
+ Long id;
+
+ String firstName;
+ String lastName;
+ Date birthday;
+
+ @Transient
+ Integer age;
+
+ @Embedded
+ private Address homeAddress;
+
+ @OneToMany(cascade=CascadeType.ALL)
+ @JoinColumn(name="CUSTOMER_ID")
+ Set<Order> orders;
+
+ // Getter/setter and business methods
+}]]></programlisting>
+
+ <para>
+ Note that support for JDK 5.0 Annotations (and JSR-220) is still work in progress and
+ not completed. Please refer to the Hibernate Annotations module for more details.
+ </para>
+
+ </sect2>
+ </sect1>
+
+ <sect1 id="mapping-generated" revision="1">
+ <title>Generated Properties</title>
+ <para>
+ Generated properties are properties which have their values generated by the
+ database. Typically, Hibernate applications needed to <literal>refresh</literal>
+ objects which contain any properties for which the database was generating values.
+ Marking properties as generated, however, lets the application delegate this
+ responsibility to Hibernate. Essentially, whenever Hibernate issues an SQL INSERT
+ or UPDATE for an entity which has defined generated properties, it immediately
+ issues a select afterwards to retrieve the generated values.
+ </para>
+ <para>
+ Properties marked as generated must additionally be non-insertable and non-updateable.
+ Only <xref linkend="mapping-declaration-version">versions</xref>,
+ <xref linkend="mapping-declaration-timestamp">timestamps</xref>, and
+ <xref linkend="mapping-declaration-property">simple properties</xref> can be marked as
+ generated.
+ </para>
+ <para>
+ <literal>never</literal> (the default) - means that the given property value
+ is not generated within the database.
+ </para>
+ <para>
+ <literal>insert</literal> - states that the given property value is generated on
+ insert, but is not regenerated on subsequent updates. Things like created-date would
+ fall into this category. Note that even thought
+ <xref linkend="mapping-declaration-version">version</xref> and
+ <xref linkend="mapping-declaration-timestamp">timestamp</xref> properties can
+ be marked as generated, this option is not available there...
+ </para>
+ <para>
+ <literal>always</literal> - states that the property value is generated both
+ on insert and on update.
+ </para>
+ </sect1>
+
+ <sect1 id="mapping-database-object">
+ <title>Auxiliary Database Objects</title>
+ <para>
+ Allows CREATE and DROP of arbitrary database objects, in conjunction with
+ Hibernate's schema evolution tools, to provide the ability to fully define
+ a user schema within the Hibernate mapping files. Although designed specifically
+ for creating and dropping things like triggers or stored procedures, really any
+ SQL command that can be run via a <literal>java.sql.Statement.execute()</literal>
+ method is valid here (ALTERs, INSERTS, etc). There are essentially two modes for
+ defining auxiliary database objects...
+ </para>
+ <para>
+ The first mode is to explicitly list the CREATE and DROP commands out in the mapping
+ file:
+ </para>
+ <programlisting><![CDATA[<hibernate-mapping>
+ ...
+ <database-object>
+ <create>CREATE TRIGGER my_trigger ...</create>
+ <drop>DROP TRIGGER my_trigger</drop>
+ </database-object>
+</hibernate-mapping>]]></programlisting>
+ <para>
+ The second mode is to supply a custom class which knows how to construct the
+ CREATE and DROP commands. This custom class must implement the
+ <literal>org.hibernate.mapping.AuxiliaryDatabaseObject</literal> interface.
+ </para>
+ <programlisting><![CDATA[<hibernate-mapping>
+ ...
+ <database-object>
+ <definition class="MyTriggerDefinition"/>
+ </database-object>
+</hibernate-mapping>]]></programlisting>
+ <para>
+ Additionally, these database objects can be optionally scoped such that they only
+ apply when certain dialects are used.
+ </para>
+ <programlisting><![CDATA[<hibernate-mapping>
+ ...
+ <database-object>
+ <definition class="MyTriggerDefinition"/>
+ <dialect-scope name="org.hibernate.dialect.Oracle9Dialect"/>
+ <dialect-scope name="org.hibernate.dialect.OracleDialect"/>
+ </database-object>
+</hibernate-mapping>]]></programlisting>
+ </sect1>
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/batch.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/batch.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/batch.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,347 @@
+<chapter id="batch">
+ <title>Batch processing</title>
+
+ <para>
+ A naive approach to inserting 100 000 rows in the database using Hibernate might
+ look like this:
+ </para>
+
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+for ( int i=0; i<100000; i++ ) {
+ Customer customer = new Customer(.....);
+ session.save(customer);
+}
+tx.commit();
+session.close();]]></programlisting>
+
+ <para>
+ This would fall over with an <literal>OutOfMemoryException</literal> somewhere
+ around the 50 000th row. That's because Hibernate caches all the newly inserted
+ <literal>Customer</literal> instances in the session-level cache.
+ </para>
+
+ <para>
+ In this chapter we'll show you how to avoid this problem. First, however, if you
+ are doing batch processing, it is absolutely critical that you enable the use of
+ JDBC batching, if you intend to achieve reasonable performance. Set the JDBC batch
+ size to a reasonable number (say, 10-50):
+ </para>
+
+<programlisting><![CDATA[hibernate.jdbc.batch_size 20]]></programlisting>
+
+ <para id="disablebatching" revision="1">
+ Note that Hibernate disables insert batching at the JDBC level transparently if you
+ use an <literal>identiy</literal> identifier generator.
+ </para>
+
+ <para>
+ You also might like to do this kind of work in a process where interaction with
+ the second-level cache is completely disabled:
+ </para>
+
+<programlisting><![CDATA[hibernate.cache.use_second_level_cache false]]></programlisting>
+
+ <para>
+ However, this is not absolutely necessary, since we can explicitly set the
+ <literal>CacheMode</literal> to disable interaction with the second-level cache.
+ </para>
+
+ <sect1 id="batch-inserts">
+ <title>Batch inserts</title>
+
+ <para>
+ When making new objects persistent, you must <literal>flush()</literal> and
+ then <literal>clear()</literal> the session regularly, to control the size of
+ the first-level cache.
+ </para>
+
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+
+for ( int i=0; i<100000; i++ ) {
+ Customer customer = new Customer(.....);
+ session.save(customer);
+ if ( i % 20 == 0 ) { //20, same as the JDBC batch size
+ //flush a batch of inserts and release memory:
+ session.flush();
+ session.clear();
+ }
+}
+
+tx.commit();
+session.close();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="batch-update" >
+ <title>Batch updates</title>
+
+ <para>
+ For retrieving and updating data the same ideas apply. In addition, you need to
+ use <literal>scroll()</literal> to take advantage of server-side cursors for
+ queries that return many rows of data.
+ </para>
+
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+
+ScrollableResults customers = session.getNamedQuery("GetCustomers")
+ .setCacheMode(CacheMode.IGNORE)
+ .scroll(ScrollMode.FORWARD_ONLY);
+int count=0;
+while ( customers.next() ) {
+ Customer customer = (Customer) customers.get(0);
+ customer.updateStuff(...);
+ if ( ++count % 20 == 0 ) {
+ //flush a batch of updates and release memory:
+ session.flush();
+ session.clear();
+ }
+}
+
+tx.commit();
+session.close();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="batch-statelesssession">
+ <title>The StatelessSession interface</title>
+ <para>
+ Alternatively, Hibernate provides a command-oriented API that may be used for
+ streaming data to and from the database in the form of detached objects. A
+ <literal>StatelessSession</literal> has no persistence context associated
+ with it and does not provide many of the higher-level lifecycle semantics.
+ In particular, a stateless session does not implement a first-level cache nor
+ interact with any second-level or query cache. It does not implement
+ transactional write-behind or automatic dirty checking. Operations performed
+ using a stateless session do not ever cascade to associated instances. Collections
+ are ignored by a stateless session. Operations performed via a stateless session
+ bypass Hibernate's event model and interceptors. Stateless sessions are vulnerable
+ to data aliasing effects, due to the lack of a first-level cache. A stateless
+ session is a lower-level abstraction, much closer to the underlying JDBC.
+ </para>
+
+<programlisting><![CDATA[StatelessSession session = sessionFactory.openStatelessSession();
+Transaction tx = session.beginTransaction();
+
+ScrollableResults customers = session.getNamedQuery("GetCustomers")
+ .scroll(ScrollMode.FORWARD_ONLY);
+while ( customers.next() ) {
+ Customer customer = (Customer) customers.get(0);
+ customer.updateStuff(...);
+ session.update(customer);
+}
+
+tx.commit();
+session.close();]]></programlisting>
+
+ <para>
+ Note that in this code example, the <literal>Customer</literal> instances returned
+ by the query are immediately detached. They are never associated with any persistence
+ context.
+ </para>
+
+ <para>
+ The <literal>insert(), update()</literal> and <literal>delete()</literal> operations
+ defined by the <literal>StatelessSession</literal> interface are considered to be
+ direct database row-level operations, which result in immediate execution of a SQL
+ <literal>INSERT, UPDATE</literal> or <literal>DELETE</literal> respectively. Thus,
+ they have very different semantics to the <literal>save(), saveOrUpdate()</literal>
+ and <literal>delete()</literal> operations defined by the <literal>Session</literal>
+ interface.
+ </para>
+
+ </sect1>
+
+ <sect1 id="batch-direct" revision="3">
+ <title>DML-style operations</title>
+
+ <para>
+ As already discussed, automatic and transparent object/relational mapping is concerned
+ with the management of object state. This implies that the object state is available
+ in memory, hence manipulating (using the SQL <literal>Data Manipulation Language</literal>
+ (DML) statements: <literal>INSERT</literal>, <literal>UPDATE</literal>, <literal>DELETE</literal>)
+ data directly in the database will not affect in-memory state. However, Hibernate provides methods
+ for bulk SQL-style DML statement execution which are performed through the
+ Hibernate Query Language (<xref linkend="queryhql">HQL</xref>).
+ </para>
+
+ <para>
+ The pseudo-syntax for <literal>UPDATE</literal> and <literal>DELETE</literal> statements
+ is: <literal>( UPDATE | DELETE ) FROM? EntityName (WHERE where_conditions)?</literal>. Some
+ points to note:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ In the from-clause, the FROM keyword is optional
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ There can only be a single entity named in the from-clause; it can optionally be
+ aliased. If the entity name is aliased, then any property references must
+ be qualified using that alias; if the entity name is not aliased, then it is
+ illegal for any property references to be qualified.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ No <xref linkend="queryhql-joins-forms">joins</xref> (either implicit or explicit)
+ can be specified in a bulk HQL query. Sub-queries may be used in the where-clause;
+ the subqueries, themselves, may contain joins.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ The where-clause is also optional.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ As an example, to execute an HQL <literal>UPDATE</literal>, use the
+ <literal>Query.executeUpdate()</literal> method (the method is named for
+ those familiar with JDBC's <literal>PreparedStatement.executeUpdate()</literal>):
+ </para>
+
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+
+String hqlUpdate = "update Customer c set c.name = :newName where c.name = :oldName";
+// or String hqlUpdate = "update Customer set name = :newName where name = :oldName";
+int updatedEntities = s.createQuery( hqlUpdate )
+ .setString( "newName", newName )
+ .setString( "oldName", oldName )
+ .executeUpdate();
+tx.commit();
+session.close();]]></programlisting>
+
+ <para>
+ HQL <literal>UPDATE</literal> statements, by default do not effect the
+ <xref linkend="mapping-declaration-version">version</xref>
+ or the <xref linkend="mapping-declaration-timestamp">timestamp</xref> property values
+ for the affected entities; this is in keeping with the EJB3 specification. However,
+ you can force Hibernate to properly reset the <literal>version</literal> or
+ <literal>timestamp</literal> property values through the use of a <literal>versioned update</literal>.
+ This is achieved by adding the <literal>VERSIONED</literal> keyword after the <literal>UPDATE</literal>
+ keyword.
+ </para>
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+String hqlVersionedUpdate = "update versioned Customer set name = :newName where name = :oldName";
+int updatedEntities = s.createQuery( hqlUpdate )
+ .setString( "newName", newName )
+ .setString( "oldName", oldName )
+ .executeUpdate();
+tx.commit();
+session.close();]]></programlisting>
+
+ <para>
+ Note that custom version types (<literal>org.hibernate.usertype.UserVersionType</literal>)
+ are not allowed in conjunction with a <literal>update versioned</literal> statement.
+ </para>
+
+ <para>
+ To execute an HQL <literal>DELETE</literal>, use the same <literal>Query.executeUpdate()</literal>
+ method:
+ </para>
+
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+
+String hqlDelete = "delete Customer c where c.name = :oldName";
+// or String hqlDelete = "delete Customer where name = :oldName";
+int deletedEntities = s.createQuery( hqlDelete )
+ .setString( "oldName", oldName )
+ .executeUpdate();
+tx.commit();
+session.close();]]></programlisting>
+
+ <para>
+ The <literal>int</literal> value returned by the <literal>Query.executeUpdate()</literal>
+ method indicate the number of entities effected by the operation. Consider this may or may not
+ correlate to the number of rows effected in the database. An HQL bulk operation might result in
+ multiple actual SQL statements being executed, for joined-subclass, for example. The returned
+ number indicates the number of actual entities affected by the statement. Going back to the
+ example of joined-subclass, a delete against one of the subclasses may actually result
+ in deletes against not just the table to which that subclass is mapped, but also the "root"
+ table and potentially joined-subclass tables further down the inheritence hierarchy.
+ </para>
+
+ <para>
+ The pseudo-syntax for <literal>INSERT</literal> statements is:
+ <literal>INSERT INTO EntityName properties_list select_statement</literal>. Some
+ points to note:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ Only the INSERT INTO ... SELECT ... form is supported; not the INSERT INTO ... VALUES ... form.
+ </para>
+ <para>
+ The properties_list is analogous to the <literal>column speficiation</literal>
+ in the SQL <literal>INSERT</literal> statement. For entities involved in mapped
+ inheritence, only properties directly defined on that given class-level can be
+ used in the properties_list. Superclass properties are not allowed; and subclass
+ properties do not make sense. In other words, <literal>INSERT</literal>
+ statements are inherently non-polymorphic.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ select_statement can be any valid HQL select query, with the caveat that the return types
+ must match the types expected by the insert. Currently, this is checked during query
+ compilation rather than allowing the check to relegate to the database. Note however
+ that this might cause problems between Hibernate <literal>Type</literal>s which are
+ <emphasis>equivalent</emphasis> as opposed to <emphasis>equal</emphasis>. This might cause
+ issues with mismatches between a property defined as a <literal>org.hibernate.type.DateType</literal>
+ and a property defined as a <literal>org.hibernate.type.TimestampType</literal>, even though the
+ database might not make a distinction or might be able to handle the conversion.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ For the id property, the insert statement gives you two options. You can either
+ explicitly specify the id property in the properties_list (in which case its value
+ is taken from the corresponding select expression) or omit it from the properties_list
+ (in which case a generated value is used). This later option is only available when
+ using id generators that operate in the database; attempting to use this option with
+ any "in memory" type generators will cause an exception during parsing. Note that
+ for the purposes of this discussion, in-database generators are considered to be
+ <literal>org.hibernate.id.SequenceGenerator</literal> (and its subclasses) and
+ any implementors of <literal>org.hibernate.id.PostInsertIdentifierGenerator</literal>.
+ The most notable exception here is <literal>org.hibernate.id.TableHiLoGenerator</literal>,
+ which cannot be used because it does not expose a selectable way to get its values.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ For properties mapped as either <literal>version</literal> or <literal>timestamp</literal>,
+ the insert statement gives you two options. You can either specify the property in the
+ properties_list (in which case its value is taken from the corresponding select expressions)
+ or omit it from the properties_list (in which case the <literal>seed value</literal> defined
+ by the <literal>org.hibernate.type.VersionType</literal> is used).
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ An example HQL <literal>INSERT</literal> statement execution:
+ </para>
+
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+
+String hqlInsert = "insert into DelinquentAccount (id, name) select c.id, c.name from Customer c where ...";
+int createdEntities = s.createQuery( hqlInsert )
+ .executeUpdate();
+tx.commit();
+session.close();]]></programlisting>
+
+ </sect1>
+
+</chapter>
Property changes on: trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/batch.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/best_practices.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/best_practices.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/best_practices.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,223 @@
+<chapter id="best-practices" revision="3">
+ <title>Best Practices</title>
+
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term>Write fine-grained classes and map them using <literal><component></literal>.</term>
+ <listitem>
+ <para>
+ Use an <literal>Address</literal> class to encapsulate <literal>street</literal>,
+ <literal>suburb</literal>, <literal>state</literal>, <literal>postcode</literal>.
+ This encourages code reuse and simplifies refactoring.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Declare identifier properties on persistent classes.</term>
+ <listitem>
+ <para>
+ Hibernate makes identifier properties optional. There are all sorts of reasons why
+ you should use them. We recommend that identifiers be 'synthetic' (generated, with
+ no business meaning).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Identify natural keys.</term>
+ <listitem>
+ <para>
+ Identify natural keys for all entities, and map them using
+ <literal><natural-id></literal>. Implement <literal>equals()</literal> and
+ <literal>hashCode()</literal> to compare the properties that make up the natural key.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Place each class mapping in its own file.</term>
+ <listitem>
+ <para>
+ Don't use a single monolithic mapping document. Map <literal>com.eg.Foo</literal> in
+ the file <literal>com/eg/Foo.hbm.xml</literal>. This makes particularly good sense in
+ a team environment.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Load mappings as resources.</term>
+ <listitem>
+ <para>
+ Deploy the mappings along with the classes they map.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Consider externalising query strings.</term>
+ <listitem>
+ <para>
+ This is a good practice if your queries call non-ANSI-standard SQL functions.
+ Externalising the query strings to mapping files will make the application more
+ portable.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Use bind variables.</term>
+ <listitem>
+ <para>
+ As in JDBC, always replace non-constant values by "?". Never use string manipulation to
+ bind a non-constant value in a query! Even better, consider using named parameters in
+ queries.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Don't manage your own JDBC connections.</term>
+ <listitem>
+ <para>
+ Hibernate lets the application manage JDBC connections. This approach should be considered
+ a last-resort. If you can't use the built-in connections providers, consider providing your
+ own implementation of <literal>org.hibernate.connection.ConnectionProvider</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Consider using a custom type.</term>
+ <listitem>
+ <para>
+ Suppose you have a Java type, say from some library, that needs to be persisted but doesn't
+ provide the accessors needed to map it as a component. You should consider implementing
+ <literal>org.hibernate.UserType</literal>. This approach frees the application
+ code from implementing transformations to / from a Hibernate type.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Use hand-coded JDBC in bottlenecks.</term>
+ <listitem>
+ <para>
+ In performance-critical areas of the system, some kinds of operations might benefit from
+ direct JDBC. But please, wait until you <emphasis>know</emphasis> something is a bottleneck.
+ And don't assume that direct JDBC is necessarily faster. If you need to use direct JDBC, it might
+ be worth opening a Hibernate <literal>Session</literal> and using that JDBC connection. That
+ way you can still use the same transaction strategy and underlying connection provider.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Understand <literal>Session</literal> flushing.</term>
+ <listitem>
+ <para>
+ From time to time the Session synchronizes its persistent state with the database. Performance will
+ be affected if this process occurs too often. You may sometimes minimize unnecessary flushing by
+ disabling automatic flushing or even by changing the order of queries and other operations within a
+ particular transaction.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>In a three tiered architecture, consider using detached objects.</term>
+ <listitem>
+ <para>
+ When using a servlet / session bean architecture, you could pass persistent objects loaded in
+ the session bean to and from the servlet / JSP layer. Use a new session to service each request.
+ Use <literal>Session.merge()</literal> or <literal>Session.saveOrUpdate()</literal> to
+ synchronize objects with the database.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>In a two tiered architecture, consider using long persistence contexts.</term>
+ <listitem>
+ <para>
+ Database Transactions have to be as short as possible for best scalability. However, it is often
+ neccessary to implement long running <emphasis>application transactions</emphasis>, a single
+ unit-of-work from the point of view of a user. An application transaction might span several
+ client request/response cycles. It is common to use detached objects to implement application
+ transactions. An alternative, extremely appropriate in two tiered architecture, is to maintain
+ a single open persistence contact (session) for the whole lifecycle of the application transaction
+ and simply disconnect from the JDBC connection at the end of each request and reconnect at the
+ beginning of the subsequent request. Never share a single session across more than one application
+ transaction, or you will be working with stale data.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Don't treat exceptions as recoverable.</term>
+ <listitem>
+ <para>
+ This is more of a necessary practice than a "best" practice. When an exception occurs, roll back
+ the <literal>Transaction</literal> and close the <literal>Session</literal>. If you don't, Hibernate
+ can't guarantee that in-memory state accurately represents persistent state. As a special case of this,
+ do not use <literal>Session.load()</literal> to determine if an instance with the given identifier
+ exists on the database; use <literal>Session.get()</literal> or a query instead.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Prefer lazy fetching for associations.</term>
+ <listitem>
+ <para>
+ Use eager fetching sparingly. Use proxies and lazy collections for most associations to classes that
+ are not likely to be completely held in the second-level cache. For associations to cached classes,
+ where there is an a extremely high probability of a cache hit, explicitly disable eager fetching using
+ <literal>lazy="false"</literal>. When an join fetching is appropriate to a particular use
+ case, use a query with a <literal>left join fetch</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ Use the <emphasis>open session in view</emphasis> pattern, or a disciplined
+ <emphasis>assembly phase</emphasis> to avoid problems with unfetched data.
+ </term>
+ <listitem>
+ <para>
+ Hibernate frees the developer from writing tedious <emphasis>Data Transfer Objects</emphasis> (DTO).
+ In a traditional EJB architecture, DTOs serve dual purposes: first, they work around the problem
+ that entity beans are not serializable; second, they implicitly define an assembly phase where
+ all data to be used by the view is fetched and marshalled into the DTOs before returning control
+ to the presentation tier. Hibernate eliminates the first purpose. However, you will still need
+ an assembly phase (think of your business methods as having a strict contract with the presentation
+ tier about what data is available in the detached objects) unless you are prepared to hold the
+ persistence context (the session) open across the view rendering process. This is not a limitation
+ of Hibernate! It is a fundamental requirement of safe transactional data access.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Consider abstracting your business logic from Hibernate.</term>
+ <listitem>
+ <para>
+ Hide (Hibernate) data-access code behind an interface. Combine the <emphasis>DAO</emphasis> and
+ <emphasis>Thread Local Session</emphasis> patterns. You can even have some classes persisted by
+ handcoded JDBC, associated to Hibernate via a <literal>UserType</literal>. (This advice is
+ intended for "sufficiently large" applications; it is not appropriate for an application with
+ five tables!)
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Don't use exotic association mappings.</term>
+ <listitem>
+ <para>
+ Good usecases for a real many-to-many associations are rare. Most of the time you need
+ additional information stored in the "link table". In this case, it is much better to
+ use two one-to-many associations to an intermediate link class. In fact, we think that
+ most associations are one-to-many and many-to-one, you should be careful when using any
+ other association style and ask yourself if it is really neccessary.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Prefer bidirectional associations.</term>
+ <listitem>
+ <para>
+ Unidirectional associations are more difficult to query. In a large application, almost
+ all associations must be navigable in both directions in queries.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/collection_mapping.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/collection_mapping.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/collection_mapping.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1231 @@
+<chapter id="collections">
+ <title>Collection Mapping</title>
+
+ <sect1 id="collections-persistent" revision="3">
+ <title>Persistent collections</title>
+
+ <para>
+ Hibernate requires that persistent collection-valued fields be declared
+ as an interface type, for example:
+ </para>
+
+ <programlisting><![CDATA[public class Product {
+ private String serialNumber;
+ private Set parts = new HashSet();
+
+ public Set getParts() { return parts; }
+ void setParts(Set parts) { this.parts = parts; }
+ public String getSerialNumber() { return serialNumber; }
+ void setSerialNumber(String sn) { serialNumber = sn; }
+}]]></programlisting>
+
+ <para>
+ The actual interface might be <literal>java.util.Set</literal>,
+ <literal>java.util.Collection</literal>, <literal>java.util.List</literal>,
+ <literal>java.util.Map</literal>, <literal>java.util.SortedSet</literal>,
+ <literal>java.util.SortedMap</literal> or ... anything you like! (Where
+ "anything you like" means you will have to write an implementation of
+ <literal>org.hibernate.usertype.UserCollectionType</literal>.)
+ </para>
+
+ <para>
+ Notice how we initialized the instance variable with an instance of
+ <literal>HashSet</literal>. This is the best way to initialize collection
+ valued properties of newly instantiated (non-persistent) instances. When
+ you make the instance persistent - by calling <literal>persist()</literal>,
+ for example - Hibernate will actually replace the <literal>HashSet</literal>
+ with an instance of Hibernate's own implementation of <literal>Set</literal>.
+ Watch out for errors like this:
+ </para>
+
+ <programlisting><![CDATA[Cat cat = new DomesticCat();
+Cat kitten = new DomesticCat();
+....
+Set kittens = new HashSet();
+kittens.add(kitten);
+cat.setKittens(kittens);
+session.persist(cat);
+kittens = cat.getKittens(); // Okay, kittens collection is a Set
+(HashSet) cat.getKittens(); // Error!]]></programlisting>
+
+ <para>
+ The persistent collections injected by Hibernate behave like
+ <literal>HashMap</literal>, <literal>HashSet</literal>,
+ <literal>TreeMap</literal>, <literal>TreeSet</literal> or
+ <literal>ArrayList</literal>, depending upon the interface type.
+ </para>
+
+ <para>
+ Collections instances have the usual behavior of value types. They are
+ automatically persisted when referenced by a persistent object and
+ automatically deleted when unreferenced. If a collection is passed from one
+ persistent object to another, its elements might be moved from one table to
+ another. Two entities may not share a reference to the same collection
+ instance. Due to the underlying relational model, collection-valued properties
+ do not support null value semantics; Hibernate does not distinguish between
+ a null collection reference and an empty collection.
+ </para>
+
+ <para>
+ You shouldn't have to worry much about any of this. Use persistent collections
+ the same way you use ordinary Java collections. Just make sure you understand
+ the semantics of bidirectional associations (discussed later).
+ </para>
+
+ </sect1>
+
+ <sect1 id="collections-mapping" revision="4">
+ <title>Collection mappings</title>
+
+ <para>
+ The Hibernate mapping element used for mapping a collection depends upon
+ the type of the interface. For example, a <literal><set></literal>
+ element is used for mapping properties of type <literal>Set</literal>.
+ </para>
+
+ <programlisting><![CDATA[<class name="Product">
+ <id name="serialNumber" column="productSerialNumber"/>
+ <set name="parts">
+ <key column="productSerialNumber" not-null="true"/>
+ <one-to-many class="Part"/>
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ Apart from <literal><set></literal>, there is also
+ <literal><list></literal>, <literal><map></literal>,
+ <literal><bag></literal>, <literal><array></literal> and
+ <literal><primitive-array></literal> mapping elements. The
+ <literal><map></literal> element is representative:
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="mappingcollection1" coords="2 65"/>
+ <area id="mappingcollection2" coords="3 65"/>
+ <area id="mappingcollection3" coords="4 65"/>
+ <area id="mappingcollection4" coords="5 65"/>
+ <area id="mappingcollection5" coords="6 65"/>
+ <area id="mappingcollection6" coords="7 65"/>
+ <area id="mappingcollection7" coords="8 65"/>
+ <area id="mappingcollection8" coords="9 65"/>
+ <area id="mappingcollection9" coords="10 65"/>
+ <area id="mappingcollection10" coords="11 65"/>
+ <area id="mappingcollection11" coords="12 65"/>
+ <area id="mappingcollection12" coords="13 65"/>
+ <area id="mappingcollection13" coords="14 65"/>
+ <area id="mappingcollection14" coords="15 65"/>
+ </areaspec>
+ <programlisting><![CDATA[<map
+ name="propertyName"
+ table="table_name"
+ schema="schema_name"
+ lazy="true|extra|false"
+ inverse="true|false"
+ cascade="all|none|save-update|delete|all-delete-orphan|delete-orphan"
+ sort="unsorted|natural|comparatorClass"
+ order-by="column_name asc|desc"
+ where="arbitrary sql where condition"
+ fetch="join|select|subselect"
+ batch-size="N"
+ access="field|property|ClassName"
+ optimistic-lock="true|false"
+ mutable="true|false"
+ node="element-name|."
+ embed-xml="true|false"
+>
+
+ <key .... />
+ <map-key .... />
+ <element .... />
+</map>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="mappingcollection1">
+ <para>
+ <literal>name</literal> the collection property name
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection2">
+ <para>
+ <literal>table</literal> (optional - defaults to property name) the
+ name of the collection table (not used for one-to-many associations)
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection3">
+ <para>
+ <literal>schema</literal> (optional) the name of a table schema to
+ override the schema declared on the root element
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection4">
+ <para>
+ <literal>lazy</literal> (optional - defaults to <literal>true</literal>)
+ may be used to disable lazy fetching and specify that the association is
+ always eagerly fetched, or to enable "extra-lazy" fetching where most
+ operations do not initialize the collection (suitable for very large
+ collections)
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection5">
+ <para>
+ <literal>inverse</literal> (optional - defaults to <literal>false</literal>)
+ mark this collection as the "inverse" end of a bidirectional association
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection6">
+ <para>
+ <literal>cascade</literal> (optional - defaults to <literal>none</literal>)
+ enable operations to cascade to child entities
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection7">
+ <para>
+ <literal>sort</literal> (optional) specify a sorted collection with
+ <literal>natural</literal> sort order, or a given comparator class
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection8">
+ <para>
+ <literal>order-by</literal> (optional, JDK1.4 only) specify a table column (or columns)
+ that define the iteration order of the <literal>Map</literal>, <literal>Set</literal>
+ or bag, together with an optional <literal>asc</literal> or <literal>desc</literal>
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection9">
+ <para>
+ <literal>where</literal> (optional) specify an arbitrary SQL <literal>WHERE</literal>
+ condition to be used when retrieving or removing the collection (useful if the
+ collection should contain only a subset of the available data)
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection10">
+ <para>
+ <literal>fetch</literal> (optional, defaults to <literal>select</literal>) Choose
+ between outer-join fetching, fetching by sequential select, and fetching by sequential
+ subselect.
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection11">
+ <para>
+ <literal>batch-size</literal> (optional, defaults to <literal>1</literal>) specify a
+ "batch size" for lazily fetching instances of this collection.
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection12">
+ <para>
+ <literal>access</literal> (optional - defaults to <literal>property</literal>): The
+ strategy Hibernate should use for accessing the collection property value.
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection13">
+ <para>
+ <literal>optimistic-lock</literal> (optional - defaults to <literal>true</literal>):
+ Species that changes to the state of the collection results in increment of the
+ owning entity's version. (For one to many associations, it is often reasonable to
+ disable this setting.)
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection14">
+ <para>
+ <literal>mutable</literal> (optional - defaults to <literal>true</literal>):
+ A value of <literal>false</literal> specifies that the elements of the
+ collection never change (a minor performance optimization in some cases).
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <sect2 id="collections-foreignkeys" >
+ <title>Collection foreign keys</title>
+
+ <para>
+ Collection instances are distinguished in the database by the foreign key of
+ the entity that owns the collection. This foreign key is referred to as the
+ <emphasis>collection key column</emphasis> (or columns) of the collection
+ table. The collection key column is mapped by the <literal><key></literal>
+ element.
+ </para>
+
+ <para>
+ There may be a nullability constraint on the foreign key column. For most
+ collections, this is implied. For unidirectional one to many associations,
+ the foreign key column is nullable by default, so you might need to specify
+ <literal>not-null="true"</literal>.
+ </para>
+
+ <programlisting><![CDATA[<key column="productSerialNumber" not-null="true"/>]]></programlisting>
+
+ <para>
+ The foreign key constraint may use <literal>ON DELETE CASCADE</literal>.
+ </para>
+
+ <programlisting><![CDATA[<key column="productSerialNumber" on-delete="cascade"/>]]></programlisting>
+
+ <para>
+ See the previous chapter for a full definition of the <literal><key></literal>
+ element.
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-elements" >
+ <title>Collection elements</title>
+
+ <para>
+ Collections may contain almost any other Hibernate type, including all basic types,
+ custom types, components, and of course, references to other entities. This is an
+ important distinction: an object in a collection might be handled with "value"
+ semantics (its lifecycle fully depends on the collection owner) or it might be a
+ reference to another entity, with its own lifecycle. In the latter case, only the
+ "link" between the two objects is considered to be state held by the collection.
+ </para>
+
+ <para>
+ The contained type is referred to as the <emphasis>collection element type</emphasis>.
+ Collection elements are mapped by <literal><element></literal> or
+ <literal><composite-element></literal>, or in the case of entity references,
+ with <literal><one-to-many></literal> or <literal><many-to-many></literal>.
+ The first two map elements with value semantics, the next two are used to map entity
+ associations.
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-indexed">
+ <title>Indexed collections</title>
+
+ <para>
+ All collection mappings, except those with set and bag semantics, need an
+ <emphasis>index column</emphasis> in the collection table - a column that maps to an
+ array index, or <literal>List</literal> index, or <literal>Map</literal> key. The
+ index of a <literal>Map</literal> may be of any basic type, mapped with
+ <literal><map-key></literal>, it may be an entity reference mapped with
+ <literal><map-key-many-to-many></literal>, or it may be a composite type,
+ mapped with <literal><composite-map-key></literal>. The index of an array or
+ list is always of type <literal>integer</literal> and is mapped using the
+ <literal><list-index></literal> element. The mapped column contains
+ sequential integers (numbered from zero, by default).
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="index1" coords="2 45"/>
+ <area id="index2" coords="3 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<list-index
+ column="column_name"
+ base="0|1|..."/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="index1">
+ <para>
+ <literal>column_name</literal> (required): The name of the column holding the
+ collection index values.
+ </para>
+ </callout>
+ <callout arearefs="index1">
+ <para>
+ <literal>base</literal> (optional, defaults to <literal>0</literal>): The value
+ of the index column that corresponds to the first element of the list or array.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <programlistingco>
+ <areaspec>
+ <area id="mapkey1" coords="2 45"/>
+ <area id="mapkey2" coords="3 45"/>
+ <area id="mapkey3" coords="4 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<map-key
+ column="column_name"
+ formula="any SQL expression"
+ type="type_name"
+ node="@attribute-name"
+ length="N"/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="mapkey1">
+ <para>
+ <literal>column</literal> (optional): The name of the column holding the
+ collection index values.
+ </para>
+ </callout>
+ <callout arearefs="mapkey2">
+ <para>
+ <literal>formula</literal> (optional): A SQL formula used to evaluate the
+ key of the map.
+ </para>
+ </callout>
+ <callout arearefs="mapkey3">
+ <para>
+ <literal>type</literal> (reguired): The type of the map keys.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <programlistingco>
+ <areaspec>
+ <area id="indexmanytomany1" coords="2 45"/>
+ <area id="indexmanytomany2" coords="3 45"/>
+ <area id="indexmanytomany3" coords="3 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<map-key-many-to-many
+ column="column_name"
+ formula="any SQL expression"
+ class="ClassName"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="indexmanytomany1">
+ <para>
+ <literal>column</literal> (optional): The name of the foreign key
+ column for the collection index values.
+ </para>
+ </callout>
+ <callout arearefs="indexmanytomany2">
+ <para>
+ <literal>formula</literal> (optional): A SQL formula used to evaluate the
+ foreign key of the map key.
+ </para>
+ </callout>
+ <callout arearefs="indexmanytomany3">
+ <para>
+ <literal>class</literal> (required): The entity class used as the map key.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+
+ <para>
+ If your table doesn't have an index column, and you still wish to use <literal>List</literal>
+ as the property type, you should map the property as a Hibernate <emphasis><bag></emphasis>.
+ A bag does not retain its order when it is retrieved from the database, but it may be
+ optionally sorted or ordered.
+ </para>
+
+ </sect2>
+
+ <para>
+ There are quite a range of mappings that can be generated for collections, covering
+ many common relational models. We suggest you experiment with the schema generation tool
+ to get a feeling for how various mapping declarations translate to database tables.
+ </para>
+
+ <sect2 id="collections-ofvalues" revision="2">
+ <title>Collections of values and many-to-many associations</title>
+
+ <para>
+ Any collection of values or many-to-many association requires a dedicated
+ <emphasis>collection table</emphasis> with a foreign key column or columns,
+ <emphasis>collection element column</emphasis> or columns and possibly
+ an index column or columns.
+ </para>
+
+ <para>
+ For a collection of values, we use the <literal><element></literal> tag.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="element1b" coords="2 50"/>
+ <area id="element2b" coords="3 50"/>
+ <area id="element3b" coords="4 50"/>
+ </areaspec>
+ <programlisting><![CDATA[<element
+ column="column_name"
+ formula="any SQL expression"
+ type="typename"
+ length="L"
+ precision="P"
+ scale="S"
+ not-null="true|false"
+ unique="true|false"
+ node="element-name"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="element1b">
+ <para>
+ <literal>column</literal> (optional): The name of the column holding the
+ collection element values.
+ </para>
+ </callout>
+ <callout arearefs="element2b">
+ <para>
+ <literal>formula</literal> (optional): An SQL formula used to evaluate the
+ element.
+ </para>
+ </callout>
+ <callout arearefs="element3b">
+ <para>
+ <literal>type</literal> (required): The type of the collection element.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ A <emphasis>many-to-many association</emphasis> is specified using the
+ <literal><many-to-many></literal> element.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="manytomany1" coords="2 60"/>
+ <area id="manytomany2" coords="3 60"/>
+ <area id="manytomany3" coords="4 60"/>
+ <area id="manytomany4" coords="5 60"/>
+ <area id="manytomany5" coords="6 60"/>
+ <area id="manytomany6" coords="7 60"/>
+ <area id="manytomany7" coords="8 60"/>
+ <area id="manytomany8" coords="9 60"/>
+ </areaspec>
+ <programlisting><![CDATA[<many-to-many
+ column="column_name"
+ formula="any SQL expression"
+ class="ClassName"
+ fetch="select|join"
+ unique="true|false"
+ not-found="ignore|exception"
+ entity-name="EntityName"
+ property-ref="propertyNameFromAssociatedClass"
+ node="element-name"
+ embed-xml="true|false"
+ />]]></programlisting>
+ <calloutlist>
+ <callout arearefs="manytomany1">
+ <para>
+ <literal>column</literal> (optional): The name of the element foreign key column.
+ </para>
+ </callout>
+ <callout arearefs="manytomany2">
+ <para>
+ <literal>formula</literal> (optional): An SQL formula used to evaluate the element
+ foreign key value.
+ </para>
+ </callout>
+ <callout arearefs="manytomany3">
+ <para>
+ <literal>class</literal> (required): The name of the associated class.
+ </para>
+ </callout>
+ <callout arearefs="manytomany4">
+ <para>
+ <literal>fetch</literal> (optional - defaults to <literal>join</literal>):
+ enables outer-join or sequential select fetching for this association. This
+ is a special case; for full eager fetching (in a single <literal>SELECT</literal>)
+ of an entity and its many-to-many relationships to other entities, you would
+ enable <literal>join</literal> fetching not only of the collection itself,
+ but also with this attribute on the <literal><many-to-many></literal>
+ nested element.
+ </para>
+ </callout>
+ <callout arearefs="manytomany5">
+ <para>
+ <literal>unique</literal> (optional): Enable the DDL generation of a unique
+ constraint for the foreign-key column. This makes the association multiplicity
+ effectively one to many.
+ </para>
+ </callout>
+ <callout arearefs="manytomany6">
+ <para>
+ <literal>not-found</literal> (optional - defaults to <literal>exception</literal>):
+ Specifies how foreign keys that reference missing rows will be handled:
+ <literal>ignore</literal> will treat a missing row as a null association.
+ </para>
+ </callout>
+ <callout arearefs="manytomany7">
+ <para>
+ <literal>entity-name</literal> (optional): The entity name of the associated class,
+ as an alternative to <literal>class</literal>.
+ </para>
+ </callout>
+ <callout arearefs="manytomany8">
+ <para>
+ <literal>property-ref</literal>: (optional) The name of a property of the associated
+ class that is joined to this foreign key. If not specified, the primary key of
+ the associated class is used.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Some examples, first, a set of strings:
+ </para>
+
+ <programlisting><![CDATA[<set name="names" table="person_names">
+ <key column="person_id"/>
+ <element column="person_name" type="string"/>
+</set>]]></programlisting>
+
+ <para>
+ A bag containing integers (with an iteration order determined by the
+ <literal>order-by</literal> attribute):
+ </para>
+
+ <programlisting><![CDATA[<bag name="sizes"
+ table="item_sizes"
+ order-by="size asc">
+ <key column="item_id"/>
+ <element column="size" type="integer"/>
+</bag>]]></programlisting>
+
+ <para>
+ An array of entities - in this case, a many to many association:
+ </para>
+
+ <programlisting><![CDATA[<array name="addresses"
+ table="PersonAddress"
+ cascade="persist">
+ <key column="personId"/>
+ <list-index column="sortOrder"/>
+ <many-to-many column="addressId" class="Address"/>
+</array>]]></programlisting>
+
+ <para>
+ A map from string indices to dates:
+ </para>
+
+ <programlisting><![CDATA[<map name="holidays"
+ table="holidays"
+ schema="dbo"
+ order-by="hol_name asc">
+ <key column="id"/>
+ <map-key column="hol_name" type="string"/>
+ <element column="hol_date" type="date"/>
+</map>]]></programlisting>
+
+ <para>
+ A list of components (discussed in the next chapter):
+ </para>
+
+ <programlisting><![CDATA[<list name="carComponents"
+ table="CarComponents">
+ <key column="carId"/>
+ <list-index column="sortOrder"/>
+ <composite-element class="CarComponent">
+ <property name="price"/>
+ <property name="type"/>
+ <property name="serialNumber" column="serialNum"/>
+ </composite-element>
+</list>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="collections-onetomany">
+ <title>One-to-many associations</title>
+
+ <para>
+ A <emphasis>one to many association</emphasis> links the tables of two classes
+ via a foreign key, with no intervening collection table. This mapping loses
+ certain semantics of normal Java collections:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ An instance of the contained entity class may not belong to more than
+ one instance of the collection
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ An instance of the contained entity class may not appear at more than
+ one value of the collection index
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ An association from <literal>Product</literal> to <literal>Part</literal> requires
+ existence of a foreign key column and possibly an index column to the <literal>Part</literal>
+ table. A <literal><one-to-many></literal> tag indicates that this is a one to many
+ association.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="onetomany1" coords="2 60"/>
+ <area id="onetomany2" coords="3 60"/>
+ <area id="onetomany3" coords="4 60"/>
+ </areaspec>
+ <programlisting><![CDATA[<one-to-many
+ class="ClassName"
+ not-found="ignore|exception"
+ entity-name="EntityName"
+ node="element-name"
+ embed-xml="true|false"
+ />]]></programlisting>
+ <calloutlist>
+ <callout arearefs="onetomany1">
+ <para>
+ <literal>class</literal> (required): The name of the associated class.
+ </para>
+ </callout>
+ <callout arearefs="onetomany2">
+ <para>
+ <literal>not-found</literal> (optional - defaults to <literal>exception</literal>):
+ Specifies how cached identifiers that reference missing rows will be handled:
+ <literal>ignore</literal> will treat a missing row as a null association.
+ </para>
+ </callout>
+ <callout arearefs="onetomany3">
+ <para>
+ <literal>entity-name</literal> (optional): The entity name of the associated class,
+ as an alternative to <literal>class</literal>.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Notice that the <literal><one-to-many></literal> element does not need to
+ declare any columns. Nor is it necessary to specify the <literal>table</literal>
+ name anywhere.
+ </para>
+
+ <para>
+ <emphasis>Very important note:</emphasis> If the foreign key column of a
+ <literal><one-to-many></literal> association is declared <literal>NOT NULL</literal>,
+ you must declare the <literal><key></literal> mapping
+ <literal>not-null="true"</literal> or <emphasis>use a bidirectional association</emphasis>
+ with the collection mapping marked <literal>inverse="true"</literal>. See the discussion
+ of bidirectional associations later in this chapter.
+ </para>
+
+ <para>
+ This example shows a map of <literal>Part</literal> entities by name (where
+ <literal>partName</literal> is a persistent property of <literal>Part</literal>).
+ Notice the use of a formula-based index.
+ </para>
+
+ <programlisting><![CDATA[<map name="parts"
+ cascade="all">
+ <key column="productId" not-null="true"/>
+ <map-key formula="partName"/>
+ <one-to-many class="Part"/>
+</map>]]></programlisting>
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="collections-advancedmappings">
+ <title>Advanced collection mappings</title>
+
+ <sect2 id="collections-sorted" revision="2">
+ <title>Sorted collections</title>
+
+ <para>
+ Hibernate supports collections implementing <literal>java.util.SortedMap</literal> and
+ <literal>java.util.SortedSet</literal>. You must specify a comparator in the mapping file:
+ </para>
+
+ <programlisting><![CDATA[<set name="aliases"
+ table="person_aliases"
+ sort="natural">
+ <key column="person"/>
+ <element column="name" type="string"/>
+</set>
+
+<map name="holidays" sort="my.custom.HolidayComparator">
+ <key column="year_id"/>
+ <map-key column="hol_name" type="string"/>
+ <element column="hol_date" type="date"/>
+</map>]]></programlisting>
+
+ <para>
+ Allowed values of the <literal>sort</literal> attribute are <literal>unsorted</literal>,
+ <literal>natural</literal> and the name of a class implementing
+ <literal>java.util.Comparator</literal>.
+ </para>
+
+ <para>
+ Sorted collections actually behave like <literal>java.util.TreeSet</literal> or
+ <literal>java.util.TreeMap</literal>.
+ </para>
+
+ <para>
+ If you want the database itself to order the collection elements use the
+ <literal>order-by</literal> attribute of <literal>set</literal>, <literal>bag</literal>
+ or <literal>map</literal> mappings. This solution is only available under
+ JDK 1.4 or higher (it is implemented using <literal>LinkedHashSet</literal> or
+ <literal>LinkedHashMap</literal>). This performs the ordering in the SQL query,
+ not in memory.
+ </para>
+
+ <programlisting><![CDATA[<set name="aliases" table="person_aliases" order-by="lower(name) asc">
+ <key column="person"/>
+ <element column="name" type="string"/>
+</set>
+
+<map name="holidays" order-by="hol_date, hol_name">
+ <key column="year_id"/>
+ <map-key column="hol_name" type="string"/>
+ <element column="hol_date type="date"/>
+</map>]]></programlisting>
+
+ <para>
+ Note that the value of the <literal>order-by</literal> attribute is an SQL ordering, not
+ a HQL ordering!
+ </para>
+
+ <para>
+ Associations may even be sorted by some arbitrary criteria at runtime using a collection
+ <literal>filter()</literal>.
+ </para>
+
+ <programlisting><![CDATA[sortedUsers = s.createFilter( group.getUsers(), "order by this.name" ).list();]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="collections-bidirectional" revision="1">
+ <title>Bidirectional associations</title>
+
+ <para>
+ A <emphasis>bidirectional association</emphasis> allows navigation from both
+ "ends" of the association. Two kinds of bidirectional association are
+ supported:
+
+ <variablelist>
+ <varlistentry>
+ <term>one-to-many</term>
+ <listitem>
+ <para>
+ set or bag valued at one end, single-valued at the other
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>many-to-many</term>
+ <listitem>
+ <para>
+ set or bag valued at both ends
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ </para>
+
+ <para>
+ You may specify a bidirectional many-to-many association simply by mapping two
+ many-to-many associations to the same database table and declaring one end as
+ <emphasis>inverse</emphasis> (which one is your choice, but it can not be an
+ indexed collection).
+ </para>
+
+ <para>
+ Here's an example of a bidirectional many-to-many association; each category can
+ have many items and each item can be in many categories:
+ </para>
+
+ <programlisting><![CDATA[<class name="Category">
+ <id name="id" column="CATEGORY_ID"/>
+ ...
+ <bag name="items" table="CATEGORY_ITEM">
+ <key column="CATEGORY_ID"/>
+ <many-to-many class="Item" column="ITEM_ID"/>
+ </bag>
+</class>
+
+<class name="Item">
+ <id name="id" column="CATEGORY_ID"/>
+ ...
+
+ <!-- inverse end -->
+ <bag name="categories" table="CATEGORY_ITEM" inverse="true">
+ <key column="ITEM_ID"/>
+ <many-to-many class="Category" column="CATEGORY_ID"/>
+ </bag>
+</class>]]></programlisting>
+
+ <para>
+ Changes made only to the inverse end of the association are <emphasis>not</emphasis>
+ persisted. This means that Hibernate has two representations in memory for every
+ bidirectional association, one link from A to B and another link from B to A. This
+ is easier to understand if you think about the Java object model and how we create
+ a many-to-many relationship in Java:
+ </para>
+
+ <programlisting><![CDATA[
+category.getItems().add(item); // The category now "knows" about the relationship
+item.getCategories().add(category); // The item now "knows" about the relationship
+
+session.persist(item); // The relationship won't be saved!
+session.persist(category); // The relationship will be saved]]></programlisting>
+
+ <para>
+ The non-inverse side is used to save the in-memory representation to the database.
+ </para>
+
+ <para>
+ You may define a bidirectional one-to-many association by mapping a one-to-many association
+ to the same table column(s) as a many-to-one association and declaring the many-valued
+ end <literal>inverse="true"</literal>.
+ </para>
+
+ <programlisting><![CDATA[<class name="Parent">
+ <id name="id" column="parent_id"/>
+ ....
+ <set name="children" inverse="true">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+ </set>
+</class>
+
+<class name="Child">
+ <id name="id" column="child_id"/>
+ ....
+ <many-to-one name="parent"
+ class="Parent"
+ column="parent_id"
+ not-null="true"/>
+</class>]]></programlisting>
+
+ <para>
+ Mapping one end of an association with <literal>inverse="true"</literal> doesn't
+ affect the operation of cascades, these are orthogonal concepts!
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-indexedbidirectional">
+ <title>Bidirectional associations with indexed collections</title>
+ <para>
+ A bidirectional association where one end is represented as a <literal><list></literal>
+ or <literal><map></literal> requires special consideration. If there is a property of
+ the child class which maps to the index column, no problem, we can continue using
+ <literal>inverse="true"</literal> on the collection mapping:
+ </para>
+
+ <programlisting><![CDATA[<class name="Parent">
+ <id name="id" column="parent_id"/>
+ ....
+ <map name="children" inverse="true">
+ <key column="parent_id"/>
+ <map-key column="name"
+ type="string"/>
+ <one-to-many class="Child"/>
+ </map>
+</class>
+
+<class name="Child">
+ <id name="id" column="child_id"/>
+ ....
+ <property name="name"
+ not-null="true"/>
+ <many-to-one name="parent"
+ class="Parent"
+ column="parent_id"
+ not-null="true"/>
+</class>]]></programlisting>
+
+ <para>
+ But, if there is no such property on the child class, we can't think of the association as
+ truly bidirectional (there is information available at one end of the association that is
+ not available at the other end). In this case, we can't map the collection
+ <literal>inverse="true"</literal>. Instead, we could use the following mapping:
+ </para>
+
+ <programlisting><![CDATA[<class name="Parent">
+ <id name="id" column="parent_id"/>
+ ....
+ <map name="children">
+ <key column="parent_id"
+ not-null="true"/>
+ <map-key column="name"
+ type="string"/>
+ <one-to-many class="Child"/>
+ </map>
+</class>
+
+<class name="Child">
+ <id name="id" column="child_id"/>
+ ....
+ <many-to-one name="parent"
+ class="Parent"
+ column="parent_id"
+ insert="false"
+ update="false"
+ not-null="true"/>
+</class>]]></programlisting>
+
+ <para>
+ Note that in this mapping, the collection-valued end of the association is responsible for
+ updates to the foreign key. TODO: Does this really result in some unnecessary update statements?
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-ternary">
+ <title>Ternary associations</title>
+
+ <para>
+ There are three possible approaches to mapping a ternary association. One is to use a
+ <literal>Map</literal> with an association as its index:
+ </para>
+
+ <programlisting><![CDATA[<map name="contracts">
+ <key column="employer_id" not-null="true"/>
+ <map-key-many-to-many column="employee_id" class="Employee"/>
+ <one-to-many class="Contract"/>
+</map>]]></programlisting>
+
+ <programlisting><![CDATA[<map name="connections">
+ <key column="incoming_node_id"/>
+ <map-key-many-to-many column="outgoing_node_id" class="Node"/>
+ <many-to-many column="connection_id" class="Connection"/>
+</map>]]></programlisting>
+
+ <para>
+ A second approach is to simply remodel the association as an entity class. This
+ is the approach we use most commonly.
+ </para>
+
+ <para>
+ A final alternative is to use composite elements, which we will discuss later.
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-idbag" revision="1">
+ <title><literal>Using an <idbag></literal></title>
+
+ <para>
+ If you've fully embraced our view that composite keys are a bad thing and that
+ entities should have synthetic identifiers (surrogate keys), then you might
+ find it a bit odd that the many to many associations and collections of values
+ that we've shown so far all map to tables with composite keys! Now, this point
+ is quite arguable; a pure association table doesn't seem to benefit much from
+ a surrogate key (though a collection of composite values <emphasis>might</emphasis>).
+ Nevertheless, Hibernate provides a feature that allows you to map many to many
+ associations and collections of values to a table with a surrogate key.
+ </para>
+
+ <para>
+ The <literal><idbag></literal> element lets you map a <literal>List</literal>
+ (or <literal>Collection</literal>) with bag semantics.
+ </para>
+
+<programlisting><![CDATA[<idbag name="lovers" table="LOVERS">
+ <collection-id column="ID" type="long">
+ <generator class="sequence"/>
+ </collection-id>
+ <key column="PERSON1"/>
+ <many-to-many column="PERSON2" class="Person" fetch="join"/>
+</idbag>]]></programlisting>
+
+ <para>
+ As you can see, an <literal><idbag></literal> has a synthetic id generator,
+ just like an entity class! A different surrogate key is assigned to each collection
+ row. Hibernate does not provide any mechanism to discover the surrogate key value
+ of a particular row, however.
+ </para>
+
+ <para>
+ Note that the update performance of an <literal><idbag></literal> is
+ <emphasis>much</emphasis> better than a regular <literal><bag></literal>!
+ Hibernate can locate individual rows efficiently and update or delete them
+ individually, just like a list, map or set.
+ </para>
+
+ <para>
+ In the current implementation, the <literal>native</literal> identifier generation
+ strategy is not supported for <literal><idbag></literal> collection identifiers.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <!--undocumenting this stuff -->
+
+ <!--sect1 id="collections-heterogeneous">
+ <title>Heterogeneous Associations</title>
+
+ <para>
+ The <literal><many-to-any></literal> and <literal><index-many-to-any></literal>
+ elements provide for true heterogeneous associations. These mapping elements work in the
+ same way as the <literal><any></literal> element - and should also be used
+ rarely, if ever.
+ </para>
+
+ </sect1-->
+
+ <sect1 id="collections-example" revision="1">
+ <title>Collection examples</title>
+
+ <para>
+ The previous sections are pretty confusing. So lets look at an example. This
+ class:
+ </para>
+
+ <programlisting><![CDATA[package eg;
+import java.util.Set;
+
+public class Parent {
+ private long id;
+ private Set children;
+
+ public long getId() { return id; }
+ private void setId(long id) { this.id=id; }
+
+ private Set getChildren() { return children; }
+ private void setChildren(Set children) { this.children=children; }
+
+ ....
+ ....
+}]]></programlisting>
+
+ <para>
+ has a collection of <literal>Child</literal> instances. If each
+ child has at most one parent, the most natural mapping is a
+ one-to-many association:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Parent">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <set name="children">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+ </set>
+ </class>
+
+ <class name="Child">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ This maps to the following table definitions:
+ </para>
+
+ <programlisting><![CDATA[create table parent ( id bigint not null primary key )
+create table child ( id bigint not null primary key, name varchar(255), parent_id bigint )
+alter table child add constraint childfk0 (parent_id) references parent]]></programlisting>
+
+ <para>
+ If the parent is <emphasis>required</emphasis>, use a bidirectional one-to-many
+ association:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Parent">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <set name="children" inverse="true">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+ </set>
+ </class>
+
+ <class name="Child">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <property name="name"/>
+ <many-to-one name="parent" class="Parent" column="parent_id" not-null="true"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Notice the <literal>NOT NULL</literal> constraint:
+ </para>
+
+ <programlisting><![CDATA[create table parent ( id bigint not null primary key )
+create table child ( id bigint not null
+ primary key,
+ name varchar(255),
+ parent_id bigint not null )
+alter table child add constraint childfk0 (parent_id) references parent]]></programlisting>
+
+ <para>
+ Alternatively, if you absolutely insist that this association should be unidirectional,
+ you can declare the <literal>NOT NULL</literal> constraint on the <literal><key></literal>
+ mapping:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Parent">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <set name="children">
+ <key column="parent_id" not-null="true"/>
+ <one-to-many class="Child"/>
+ </set>
+ </class>
+
+ <class name="Child">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ On the other hand, if a child might have multiple parents, a many-to-many
+ association is appropriate:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Parent">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <set name="children" table="childset">
+ <key column="parent_id"/>
+ <many-to-many class="Child" column="child_id"/>
+ </set>
+ </class>
+
+ <class name="Child">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Table definitions:
+ </para>
+
+ <programlisting><![CDATA[create table parent ( id bigint not null primary key )
+create table child ( id bigint not null primary key, name varchar(255) )
+create table childset ( parent_id bigint not null,
+ child_id bigint not null,
+ primary key ( parent_id, child_id ) )
+alter table childset add constraint childsetfk0 (parent_id) references parent
+alter table childset add constraint childsetfk1 (child_id) references child]]></programlisting>
+
+ <para>
+ For more examples and a complete walk-through a parent/child relationship mapping,
+ see <xref linkend="example-parentchild"/>.
+ </para>
+
+ <para>
+ Even more exotic association mappings are possible, we will catalog all possibilities
+ in the next chapter.
+ </para>
+
+ </sect1>
+
+</chapter>
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/component_mapping.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/component_mapping.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/component_mapping.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,402 @@
+<chapter id="components">
+ <title>Component Mapping</title>
+
+ <para>
+ The notion of a <emphasis>component</emphasis> is re-used in several different contexts,
+ for different purposes, throughout Hibernate.
+ </para>
+
+ <sect1 id="components-dependentobjects" revision="2" >
+ <title>Dependent objects</title>
+
+ <para>
+ A component is a contained object that is persisted as a value type, not an entity
+ reference. The term "component" refers to the object-oriented notion of composition
+ (not to architecture-level components). For example, you might model a person like this:
+ </para>
+
+ <programlisting><![CDATA[public class Person {
+ private java.util.Date birthday;
+ private Name name;
+ private String key;
+ public String getKey() {
+ return key;
+ }
+ private void setKey(String key) {
+ this.key=key;
+ }
+ public java.util.Date getBirthday() {
+ return birthday;
+ }
+ public void setBirthday(java.util.Date birthday) {
+ this.birthday = birthday;
+ }
+ public Name getName() {
+ return name;
+ }
+ public void setName(Name name) {
+ this.name = name;
+ }
+ ......
+ ......
+}]]></programlisting>
+
+<programlisting><![CDATA[public class Name {
+ char initial;
+ String first;
+ String last;
+ public String getFirst() {
+ return first;
+ }
+ void setFirst(String first) {
+ this.first = first;
+ }
+ public String getLast() {
+ return last;
+ }
+ void setLast(String last) {
+ this.last = last;
+ }
+ public char getInitial() {
+ return initial;
+ }
+ void setInitial(char initial) {
+ this.initial = initial;
+ }
+}]]></programlisting>
+
+ <para>
+ Now <literal>Name</literal> may be persisted as a component of
+ <literal>Person</literal>. Notice that <literal>Name</literal> defines getter
+ and setter methods for its persistent properties, but doesn't need to declare
+ any interfaces or identifier properties.
+ </para>
+
+ <para>
+ Our Hibernate mapping would look like:
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Person" table="person">
+ <id name="Key" column="pid" type="string">
+ <generator class="uuid"/>
+ </id>
+ <property name="birthday" type="date"/>
+ <component name="Name" class="eg.Name"> <!-- class attribute optional -->
+ <property name="initial"/>
+ <property name="first"/>
+ <property name="last"/>
+ </component>
+</class>]]></programlisting>
+
+ <para>
+ The person table would have the columns <literal>pid</literal>,
+ <literal>birthday</literal>,
+ <literal>initial</literal>,
+ <literal>first</literal> and
+ <literal>last</literal>.
+ </para>
+
+ <para>
+ Like all value types, components do not support shared references. In other words, two
+ persons could have the same name, but the two person objects would contain two independent
+ name ojects, only "the same" by value. The null value semantics of a component are
+ <emphasis>ad hoc</emphasis>. When reloading the containing object, Hibernate will assume
+ that if all component columns are null, then the entire component is null. This should
+ be okay for most purposes.
+ </para>
+
+ <para>
+ The properties of a component may be of any Hibernate type (collections, many-to-one
+ associations, other components, etc). Nested components should <emphasis>not</emphasis>
+ be considered an exotic usage. Hibernate is intended to support a very fine-grained
+ object model.
+ </para>
+
+ <para>
+ The <literal><component></literal> element allows a <literal><parent></literal>
+ subelement that maps a property of the component class as a reference back to the
+ containing entity.
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Person" table="person">
+ <id name="Key" column="pid" type="string">
+ <generator class="uuid"/>
+ </id>
+ <property name="birthday" type="date"/>
+ <component name="Name" class="eg.Name" unique="true">
+ <parent name="namedPerson"/> <!-- reference back to the Person -->
+ <property name="initial"/>
+ <property name="first"/>
+ <property name="last"/>
+ </component>
+</class>]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="components-incollections" revision="1">
+ <title>Collections of dependent objects</title>
+
+ <para>
+ Collections of components are supported (eg. an array of type
+ <literal>Name</literal>). Declare your component collection by
+ replacing the <literal><element></literal> tag with a
+ <literal><composite-element></literal> tag.
+ </para>
+
+ <programlisting><![CDATA[<set name="someNames" table="some_names" lazy="true">
+ <key column="id"/>
+ <composite-element class="eg.Name"> <!-- class attribute required -->
+ <property name="initial"/>
+ <property name="first"/>
+ <property name="last"/>
+ </composite-element>
+</set>]]></programlisting>
+
+ <para>
+ Note: if you define a <literal>Set</literal> of composite elements, it is
+ very important to implement <literal>equals()</literal> and
+ <literal>hashCode()</literal> correctly.
+ </para>
+
+ <para>
+ Composite elements may contain components but not collections. If your
+ composite element itself contains
+ components, use the <literal><nested-composite-element></literal>
+ tag. This is a pretty exotic case - a collection of components which
+ themselves have components. By this stage you should be asking yourself
+ if a one-to-many association is more appropriate. Try remodelling the
+ composite element as an entity - but note that even though the Java model
+ is the same, the relational model and persistence semantics are still
+ slightly different.
+ </para>
+
+ <para>
+ Please note that a composite element mapping doesn't support null-able properties
+ if you're using a <literal><set></literal>. Hibernate
+ has to use each columns value to identify a record when deleting objects
+ (there is no separate primary key column in the composite element table),
+ which is not possible with null values. You have to either use only
+ not-null properties in a composite-element or choose a
+ <literal><list></literal>, <literal><map></literal>,
+ <literal><bag></literal> or <literal><idbag></literal>.
+ </para>
+
+ <para>
+ A special case of a composite element is a composite element with a nested
+ <literal><many-to-one></literal> element. A mapping like this allows
+ you to map extra columns of a many-to-many association table to the
+ composite element class. The following is a many-to-many association
+ from <literal>Order</literal> to <literal>Item</literal> where
+ <literal>purchaseDate</literal>, <literal>price</literal> and
+ <literal>quantity</literal> are properties of the association:
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Order" .... >
+ ....
+ <set name="purchasedItems" table="purchase_items" lazy="true">
+ <key column="order_id">
+ <composite-element class="eg.Purchase">
+ <property name="purchaseDate"/>
+ <property name="price"/>
+ <property name="quantity"/>
+ <many-to-one name="item" class="eg.Item"/> <!-- class attribute is optional -->
+ </composite-element>
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ Of course, there can't be a reference to the purchae on the other side, for
+ bidirectional association navigation. Remember that components are value types and
+ don't allow shared references. A single <literal>Purchase</literal> can be in the
+ set of an <literal>Order</literal>, but it can't be referenced by the <literal>Item</literal>
+ at the same time.
+ </para>
+
+ <para>Even ternary (or quaternary, etc) associations are possible:</para>
+
+ <programlisting><![CDATA[<class name="eg.Order" .... >
+ ....
+ <set name="purchasedItems" table="purchase_items" lazy="true">
+ <key column="order_id">
+ <composite-element class="eg.OrderLine">
+ <many-to-one name="purchaseDetails class="eg.Purchase"/>
+ <many-to-one name="item" class="eg.Item"/>
+ </composite-element>
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ Composite elements may appear in queries using the same syntax as
+ associations to other entities.
+ </para>
+
+ </sect1>
+
+ <sect1 id="components-asmapindex">
+ <title>Components as Map indices</title>
+
+ <para>
+ The <literal><composite-map-key></literal> element lets you map a
+ component class as the key of a <literal>Map</literal>. Make sure you override
+ <literal>hashCode()</literal> and <literal>equals()</literal> correctly on
+ the component class.
+ </para>
+ </sect1>
+
+ <sect1 id="components-compositeid" revision="1">
+ <title>Components as composite identifiers</title>
+
+ <para>
+ You may use a component as an identifier of an entity class. Your component
+ class must satisfy certain requirements:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ It must implement <literal>java.io.Serializable</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ It must re-implement <literal>equals()</literal> and
+ <literal>hashCode()</literal>, consistently with the database's
+ notion of composite key equality.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ <emphasis>Note: in Hibernate3, the second requirement is not an absolutely hard
+ requirement of Hibernate. But do it anyway.</emphasis>
+ </para>
+
+ <para>
+ You can't use an <literal>IdentifierGenerator</literal> to generate composite keys.
+ Instead the application must assign its own identifiers.
+ </para>
+
+ <para>
+ Use the <literal><composite-id></literal> tag (with nested
+ <literal><key-property></literal> elements) in place of the usual
+ <literal><id></literal> declaration. For example, the
+ <literal>OrderLine</literal> class has a primary key that depends upon
+ the (composite) primary key of <literal>Order</literal>.
+ </para>
+
+ <programlisting><![CDATA[<class name="OrderLine">
+
+ <composite-id name="id" class="OrderLineId">
+ <key-property name="lineId"/>
+ <key-property name="orderId"/>
+ <key-property name="customerId"/>
+ </composite-id>
+
+ <property name="name"/>
+
+ <many-to-one name="order" class="Order"
+ insert="false" update="false">
+ <column name="orderId"/>
+ <column name="customerId"/>
+ </many-to-one>
+ ....
+
+</class>]]></programlisting>
+
+ <para>
+ Now, any foreign keys referencing the <literal>OrderLine</literal> table are also
+ composite. You must declare this in your mappings for other classes. An association
+ to <literal>OrderLine</literal> would be mapped like this:
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="orderLine" class="OrderLine">
+<!-- the "class" attribute is optional, as usual -->
+ <column name="lineId"/>
+ <column name="orderId"/>
+ <column name="customerId"/>
+</many-to-one>]]></programlisting>
+
+ <para>
+ (Note that the <literal><column></literal> tag is an alternative to the
+ <literal>column</literal> attribute everywhere.)
+ </para>
+
+ <para>
+ A <literal>many-to-many</literal> association to <literal>OrderLine</literal> also
+ uses the composite foreign key:
+ </para>
+
+ <programlisting><![CDATA[<set name="undeliveredOrderLines">
+ <key column name="warehouseId"/>
+ <many-to-many class="OrderLine">
+ <column name="lineId"/>
+ <column name="orderId"/>
+ <column name="customerId"/>
+ </many-to-many>
+</set>]]></programlisting>
+
+ <para>
+ The collection of <literal>OrderLine</literal>s in <literal>Order</literal> would
+ use:
+ </para>
+
+ <programlisting><![CDATA[<set name="orderLines" inverse="true">
+ <key>
+ <column name="orderId"/>
+ <column name="customerId"/>
+ </key>
+ <one-to-many class="OrderLine"/>
+</set>]]></programlisting>
+
+ <para>
+ (The <literal><one-to-many></literal> element, as usual, declares no columns.)
+ </para>
+
+ <para>
+ If <literal>OrderLine</literal> itself owns a collection, it also has a composite
+ foreign key.
+ </para>
+
+ <programlisting><![CDATA[<class name="OrderLine">
+ ....
+ ....
+ <list name="deliveryAttempts">
+ <key> <!-- a collection inherits the composite key type -->
+ <column name="lineId"/>
+ <column name="orderId"/>
+ <column name="customerId"/>
+ </key>
+ <list-index column="attemptId" base="1"/>
+ <composite-element class="DeliveryAttempt">
+ ...
+ </composite-element>
+ </set>
+</class>]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="components-dynamic" revision="1">
+ <title>Dynamic components</title>
+
+ <para>
+ You may even map a property of type <literal>Map</literal>:
+ </para>
+
+ <programlisting><![CDATA[<dynamic-component name="userAttributes">
+ <property name="foo" column="FOO" type="string"/>
+ <property name="bar" column="BAR" type="integer"/>
+ <many-to-one name="baz" class="Baz" column="BAZ_ID"/>
+</dynamic-component>]]></programlisting>
+
+ <para>
+ The semantics of a <literal><dynamic-component></literal> mapping are identical
+ to <literal><component></literal>. The advantage of this kind of mapping is
+ the ability to determine the actual properties of the bean at deployment time, just
+ by editing the mapping document. Runtime manipulation of the mapping document is
+ also possible, using a DOM parser. Even better, you can access (and change) Hibernate's
+ configuration-time metamodel via the <literal>Configuration</literal> object.
+ </para>
+
+ </sect1>
+
+</chapter>
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/configuration.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/configuration.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/configuration.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1738 @@
+<chapter id="session-configuration" revision="1">
+
+ <title>Configuration</title>
+
+ <para>
+ Because Hibernate is designed to operate in many different environments, there
+ are a large number of configuration parameters. Fortunately, most have sensible
+ default values and Hibernate is distributed with an example
+ <literal>hibernate.properties</literal> file in <literal>etc/</literal> that shows
+ the various options. Just put the example file in your classpath and customize it.
+ </para>
+
+ <sect1 id="configuration-programmatic" revision="1">
+ <title>Programmatic configuration</title>
+
+ <para>
+ An instance of <literal>org.hibernate.cfg.Configuration</literal>
+ represents an entire set of mappings of an application's Java types to an
+ SQL database. The <literal>Configuration</literal> is used to build an
+ (immutable) <literal>SessionFactory</literal>. The mappings are compiled
+ from various XML mapping files.
+ </para>
+
+ <para>
+ You may obtain a <literal>Configuration</literal> instance by instantiating
+ it directly and specifying XML mapping documents. If the mapping files are
+ in the classpath, use <literal>addResource()</literal>:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = new Configuration()
+ .addResource("Item.hbm.xml")
+ .addResource("Bid.hbm.xml");]]></programlisting>
+
+ <para>
+ An alternative (sometimes better) way is to specify the mapped class, and
+ let Hibernate find the mapping document for you:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = new Configuration()
+ .addClass(org.hibernate.auction.Item.class)
+ .addClass(org.hibernate.auction.Bid.class);]]></programlisting>
+
+ <para>
+ Then Hibernate will look for mapping files named
+ <literal>/org/hibernate/auction/Item.hbm.xml</literal> and
+ <literal>/org/hibernate/auction/Bid.hbm.xml</literal> in the classpath.
+ This approach eliminates any hardcoded filenames.
+ </para>
+
+ <para>
+ A <literal>Configuration</literal> also allows you to specify configuration
+ properties:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = new Configuration()
+ .addClass(org.hibernate.auction.Item.class)
+ .addClass(org.hibernate.auction.Bid.class)
+ .setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLInnoDBDialect")
+ .setProperty("hibernate.connection.datasource", "java:comp/env/jdbc/test")
+ .setProperty("hibernate.order_updates", "true");]]></programlisting>
+
+ <para>
+ This is not the only way to pass configuration properties to Hibernate.
+ The various options include:
+ </para>
+
+ <orderedlist spacing="compact">
+ <listitem>
+ <para>
+ Pass an instance of <literal>java.util.Properties</literal> to
+ <literal>Configuration.setProperties()</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Place <literal>hibernate.properties</literal> in a root directory
+ of the classpath.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Set <literal>System</literal> properties using
+ <literal>java -Dproperty=value</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Include <literal><property></literal> elements in
+ <literal>hibernate.cfg.xml</literal> (discussed later).
+ </para>
+ </listitem>
+ </orderedlist>
+
+ <para>
+ <literal>hibernate.properties</literal> is the easiest approach if you
+ want to get started quickly.
+ </para>
+
+ <para>
+ The <literal>Configuration</literal> is intended as a startup-time object,
+ to be discarded once a <literal>SessionFactory</literal> is created.
+ </para>
+
+ </sect1>
+
+ <sect1 id="configuration-sessionfactory">
+ <title>Obtaining a SessionFactory</title>
+
+ <para>
+ When all mappings have been parsed by the <literal>Configuration</literal>,
+ the application must obtain a factory for <literal>Session</literal> instances.
+ This factory is intended to be shared by all application threads:
+ </para>
+
+ <programlisting><![CDATA[SessionFactory sessions = cfg.buildSessionFactory();]]></programlisting>
+
+ <para>
+ Hibernate does allow your application to instantiate more than one
+ <literal>SessionFactory</literal>. This is useful if you are using more than
+ one database.
+ </para>
+
+ </sect1>
+
+ <sect1 id="configuration-hibernatejdbc" revision="1">
+ <title>JDBC connections</title>
+
+ <para>
+ Usually, you want to have the <literal>SessionFactory</literal> create and pool JDBC
+ connections for you. If you take this approach, opening a <literal>Session</literal>
+ is as simple as:
+ </para>
+
+ <programlisting><![CDATA[Session session = sessions.openSession(); // open a new Session]]></programlisting>
+
+ <para>
+ As soon as you do something that requires access to the database, a JDBC connection
+ will be obtained from the pool.
+ </para>
+
+ <para>
+ For this to work, we need to pass some JDBC connection properties to Hibernate.
+ All Hibernate property names and semantics are defined on the class
+ <literal>org.hibernate.cfg.Environment</literal>. We will now describe the most
+ important settings for JDBC connection configuration.
+ </para>
+
+ <para>
+ Hibernate will obtain (and pool) connections using <literal>java.sql.DriverManager</literal>
+ if you set the following properties:
+ </para>
+
+ <table frame="topbot">
+ <title>Hibernate JDBC Properties</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Property name</entry>
+ <entry>Purpose</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.connection.driver_class</literal>
+ </entry>
+ <entry>
+ <emphasis>jdbc driver class</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.url</literal>
+ </entry>
+ <entry>
+ <emphasis>jdbc URL</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.username</literal>
+ </entry>
+ <entry>
+ <emphasis>database user</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.password</literal>
+ </entry>
+ <entry>
+ <emphasis>database user password</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.pool_size</literal>
+ </entry>
+ <entry>
+ <emphasis>maximum number of pooled connections</emphasis>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ Hibernate's own connection pooling algorithm is however quite rudimentary.
+ It is intended to help you get started and is <emphasis>not intended for use
+ in a production system</emphasis> or even for performance testing. You should
+ use a third party pool for best performance and stability. Just replace the
+ <literal>hibernate.connection.pool_size</literal> property with connection
+ pool specific settings. This will turn off Hibernate's internal pool. For
+ example, you might like to use C3P0.
+ </para>
+
+ <para>
+ C3P0 is an open source JDBC connection pool distributed along with
+ Hibernate in the <literal>lib</literal> directory. Hibernate will use its
+ <literal>C3P0ConnectionProvider</literal> for connection pooling if you set
+ <literal>hibernate.c3p0.*</literal> properties. If you'd like to use Proxool
+ refer to the packaged <literal>hibernate.properties</literal> and the Hibernate
+ web site for more information.
+ </para>
+
+ <para>
+ Here is an example <literal>hibernate.properties</literal> file for C3P0:
+ </para>
+
+ <programlisting id="c3p0-configuration" revision="1"><![CDATA[hibernate.connection.driver_class = org.postgresql.Driver
+hibernate.connection.url = jdbc:postgresql://localhost/mydatabase
+hibernate.connection.username = myuser
+hibernate.connection.password = secret
+hibernate.c3p0.min_size=5
+hibernate.c3p0.max_size=20
+hibernate.c3p0.timeout=1800
+hibernate.c3p0.max_statements=50
+hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect]]></programlisting>
+
+ <para>
+ For use inside an application server, you should almost always configure
+ Hibernate to obtain connections from an application server
+ <literal>Datasource</literal> registered in JNDI. You'll need to set at
+ least one of the following properties:
+ </para>
+
+ <table frame="topbot">
+ <title>Hibernate Datasource Properties</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Propery name</entry>
+ <entry>Purpose</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.connection.datasource</literal>
+ </entry>
+ <entry>
+ <emphasis>datasource JNDI name</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jndi.url</literal>
+ </entry>
+ <entry>
+ <emphasis>URL of the JNDI provider</emphasis> (optional)
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jndi.class</literal>
+ </entry>
+ <entry>
+ <emphasis>class of the JNDI <literal>InitialContextFactory</literal></emphasis> (optional)
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.username</literal>
+ </entry>
+ <entry>
+ <emphasis>database user</emphasis> (optional)
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.password</literal>
+ </entry>
+ <entry>
+ <emphasis>database user password</emphasis> (optional)
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ Here's an example <literal>hibernate.properties</literal> file for an
+ application server provided JNDI datasource:
+ </para>
+
+ <programlisting><![CDATA[hibernate.connection.datasource = java:/comp/env/jdbc/test
+hibernate.transaction.factory_class = \
+ org.hibernate.transaction.JTATransactionFactory
+hibernate.transaction.manager_lookup_class = \
+ org.hibernate.transaction.JBossTransactionManagerLookup
+hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect]]></programlisting>
+
+ <para>
+ JDBC connections obtained from a JNDI datasource will automatically participate
+ in the container-managed transactions of the application server.
+ </para>
+
+ <para>
+ Arbitrary connection properties may be given by prepending
+ "<literal>hibernate.connnection</literal>" to the property name. For example, you
+ may specify a <literal>charSet</literal> using <literal>hibernate.connection.charSet</literal>.
+ </para>
+
+ <para>
+ You may define your own plugin strategy for obtaining JDBC connections by implementing the
+ interface <literal>org.hibernate.connection.ConnectionProvider</literal>. You may select
+ a custom implementation by setting <literal>hibernate.connection.provider_class</literal>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="configuration-optional" revision="1">
+ <title>Optional configuration properties</title>
+
+ <para>
+ There are a number of other properties that control the behaviour of Hibernate
+ at runtime. All are optional and have reasonable default values.
+ </para>
+
+ <para>
+ <emphasis>Warning: some of these properties are "system-level" only.</emphasis>
+ System-level properties can be set only via <literal>java -Dproperty=value</literal> or
+ <literal>hibernate.properties</literal>. They may <emphasis>not</emphasis> be set by
+ the other techniques described above.
+ </para>
+
+ <table frame="topbot" id="configuration-optional-properties" revision="8">
+ <title>Hibernate Configuration Properties</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Property name</entry>
+ <entry>Purpose</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.dialect</literal>
+ </entry>
+ <entry>
+ The classname of a Hibernate <literal>Dialect</literal> which
+ allows Hibernate to generate SQL optimized for a particular
+ relational database.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>full.classname.of.Dialect</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.show_sql</literal>
+ </entry>
+ <entry>
+ Write all SQL statements to console. This is an alternative
+ to setting the log category <literal>org.hibernate.SQL</literal>
+ to <literal>debug</literal>.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.format_sql</literal>
+ </entry>
+ <entry>
+ Pretty print the SQL in the log and console.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.default_schema</literal>
+ </entry>
+ <entry>
+ Qualify unqualified tablenames with the given schema/tablespace
+ in generated SQL.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>SCHEMA_NAME</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.default_catalog</literal>
+ </entry>
+ <entry>
+ Qualify unqualified tablenames with the given catalog
+ in generated SQL.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>CATALOG_NAME</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.session_factory_name</literal>
+ </entry>
+ <entry>
+ The <literal>SessionFactory</literal> will be automatically
+ bound to this name in JNDI after it has been created.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>jndi/composite/name</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.max_fetch_depth</literal>
+ </entry>
+ <entry>
+ Set a maximum "depth" for the outer join fetch tree
+ for single-ended associations (one-to-one, many-to-one).
+ A <literal>0</literal> disables default outer join fetching.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ recommended values between <literal>0</literal> and
+ <literal>3</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.default_batch_fetch_size</literal>
+ </entry>
+ <entry>
+ Set a default size for Hibernate batch fetching of associations.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ recommended values <literal>4</literal>, <literal>8</literal>,
+ <literal>16</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.default_entity_mode</literal>
+ </entry>
+ <entry>
+ Set a default mode for entity representation for all sessions
+ opened from this <literal>SessionFactory</literal>
+ <para>
+ <literal>dynamic-map</literal>, <literal>dom4j</literal>,
+ <literal>pojo</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.order_updates</literal>
+ </entry>
+ <entry>
+ Force Hibernate to order SQL updates by the primary key value
+ of the items being updated. This will result in fewer transaction
+ deadlocks in highly concurrent systems.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.generate_statistics</literal>
+ </entry>
+ <entry>
+ If enabled, Hibernate will collect statistics useful for
+ performance tuning.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.use_identifer_rollback</literal>
+ </entry>
+ <entry>
+ If enabled, generated identifier properties will be
+ reset to default values when objects are deleted.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.use_sql_comments</literal>
+ </entry>
+ <entry>
+ If turned on, Hibernate will generate comments inside the SQL, for
+ easier debugging, defaults to <literal>false</literal>.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame="topbot" id="configuration-jdbc-properties" revision="8">
+ <title>Hibernate JDBC and Connection Properties</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Property name</entry>
+ <entry>Purpose</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.fetch_size</literal>
+ </entry>
+ <entry>
+ A non-zero value determines the JDBC fetch size (calls
+ <literal>Statement.setFetchSize()</literal>).
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.batch_size</literal>
+ </entry>
+ <entry>
+ A non-zero value enables use of JDBC2 batch updates by Hibernate.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ recommended values between <literal>5</literal> and <literal>30</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.batch_versioned_data</literal>
+ </entry>
+ <entry>
+ Set this property to <literal>true</literal> if your JDBC driver returns
+ correct row counts from <literal>executeBatch()</literal> (it is usually
+ safe to turn this option on). Hibernate will then use batched DML for
+ automatically versioned data. Defaults to <literal>false</literal>.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.factory_class</literal>
+ </entry>
+ <entry>
+ Select a custom <literal>Batcher</literal>. Most applications
+ will not need this configuration property.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>classname.of.Batcher</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.use_scrollable_resultset</literal>
+ </entry>
+ <entry>
+ Enables use of JDBC2 scrollable resultsets by Hibernate.
+ This property is only necessary when using user supplied
+ JDBC connections, Hibernate uses connection metadata otherwise.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.use_streams_for_binary</literal>
+ </entry>
+ <entry>
+ Use streams when writing/reading <literal>binary</literal>
+ or <literal>serializable</literal> types to/from JDBC
+ (system-level property).
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.use_get_generated_keys</literal>
+ </entry>
+ <entry>
+ Enable use of JDBC3 <literal>PreparedStatement.getGeneratedKeys()</literal>
+ to retrieve natively generated keys after insert. Requires JDBC3+ driver
+ and JRE1.4+, set to false if your driver has problems with the Hibernate
+ identifier generators. By default, tries to determine the driver capabilites
+ using connection metadata.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.provider_class</literal>
+ </entry>
+ <entry>
+ The classname of a custom <literal>ConnectionProvider</literal> which provides
+ JDBC connections to Hibernate.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>classname.of.ConnectionProvider</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.isolation</literal>
+ </entry>
+ <entry>
+ Set the JDBC transaction isolation level. Check
+ <literal>java.sql.Connection</literal> for meaningful values but
+ note that most databases do not support all isolation levels.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>1, 2, 4, 8</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.autocommit</literal>
+ </entry>
+ <entry>
+ Enables autocommit for JDBC pooled connections (not recommended).
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.release_mode</literal>
+ </entry>
+ <entry>
+ Specify when Hibernate should release JDBC connections. By default,
+ a JDBC connection is held until the session is explicitly closed or
+ disconnected. For an application server JTA datasource, you should use
+ <literal>after_statement</literal> to aggressively release connections
+ after every JDBC call. For a non-JTA connection, it often makes sense to
+ release the connection at the end of each transaction, by using
+ <literal>after_transaction</literal>. <literal>auto</literal> will
+ choose <literal>after_statement</literal> for the JTA and CMT transaction
+ strategies and <literal>after_transaction</literal> for the JDBC
+ transaction strategy.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>auto</literal> (default) | <literal>on_close</literal> |
+ <literal>after_transaction</literal> | <literal>after_statement</literal>
+ </para>
+ <para>
+ Note that this setting only affects <literal>Session</literal>s returned from
+ <literal>SessionFactory.openSession</literal>. For <literal>Session</literal>s
+ obtained through <literal>SessionFactory.getCurrentSession</literal>, the
+ <literal>CurrentSessionContext</literal> implementation configured for use
+ controls the connection release mode for those <literal>Session</literal>s.
+ See <xref linkend="architecture-current-session"/>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.<emphasis><propertyName></emphasis></literal>
+ </entry>
+ <entry>
+ Pass the JDBC property <literal>propertyName</literal>
+ to <literal>DriverManager.getConnection()</literal>.
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jndi.<emphasis><propertyName></emphasis></literal>
+ </entry>
+ <entry>
+ Pass the property <literal>propertyName</literal> to
+ the JNDI <literal>InitialContextFactory</literal>.
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame="topbot" id="configuration-cache-properties" revision="7">
+ <title>Hibernate Cache Properties</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Property name</entry>
+ <entry>Purpose</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.cache.provider_class</literal>
+ </entry>
+ <entry>
+ The classname of a custom <literal>CacheProvider</literal>.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>classname.of.CacheProvider</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.use_minimal_puts</literal>
+ </entry>
+ <entry>
+ Optimize second-level cache operation to minimize writes, at the
+ cost of more frequent reads. This setting is most useful for
+ clustered caches and, in Hibernate3, is enabled by default for
+ clustered cache implementations.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.use_query_cache</literal>
+ </entry>
+ <entry>
+ Enable the query cache, individual queries still have to be set cachable.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.use_second_level_cache</literal>
+ </entry>
+ <entry>
+ May be used to completely disable the second level cache, which is enabled
+ by default for classes which specify a <literal><cache></literal>
+ mapping.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.query_cache_factory</literal>
+ </entry>
+ <entry>
+ The classname of a custom <literal>QueryCache</literal> interface,
+ defaults to the built-in <literal>StandardQueryCache</literal>.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>classname.of.QueryCache</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.region_prefix</literal>
+ </entry>
+ <entry>
+ A prefix to use for second-level cache region names.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>prefix</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.use_structured_entries</literal>
+ </entry>
+ <entry>
+ Forces Hibernate to store data in the second-level cache
+ in a more human-friendly format.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame="topbot" id="configuration-transaction-properties" revision="9">
+ <title>Hibernate Transaction Properties</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Property name</entry>
+ <entry>Purpose</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.transaction.factory_class</literal>
+ </entry>
+ <entry>
+ The classname of a <literal>TransactionFactory</literal>
+ to use with Hibernate <literal>Transaction</literal> API
+ (defaults to <literal>JDBCTransactionFactory</literal>).
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>classname.of.TransactionFactory</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>jta.UserTransaction</literal>
+ </entry>
+ <entry>
+ A JNDI name used by <literal>JTATransactionFactory</literal> to
+ obtain the JTA <literal>UserTransaction</literal> from the
+ application server.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>jndi/composite/name</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.transaction.manager_lookup_class</literal>
+ </entry>
+ <entry>
+ The classname of a <literal>TransactionManagerLookup</literal>
+ - required when JVM-level caching is enabled or when using hilo
+ generator in a JTA environment.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>classname.of.TransactionManagerLookup</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.transaction.flush_before_completion</literal>
+ </entry>
+ <entry>
+ If enabled, the session will be automatically flushed during the
+ before completion phase of the transaction. Built-in and
+ automatic session context management is preferred, see
+ <xref linkend="architecture-current-session"/>.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.transaction.auto_close_session</literal>
+ </entry>
+ <entry>
+ If enabled, the session will be automatically closed during the
+ after completion phase of the transaction. Built-in and
+ utomatic session context management is preferred, see
+ <xref linkend="architecture-current-session"/>.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame="topbot" id="configuration-misc-properties" revision="10">
+ <title>Miscellaneous Properties</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Property name</entry>
+ <entry>Purpose</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.current_session_context_class</literal>
+ </entry>
+ <entry>
+ Supply a (custom) strategy for the scoping of the "current"
+ <literal>Session</literal>. See
+ <xref linkend="architecture-current-session"/> for more
+ information about the built-in strategies.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>jta</literal> | <literal>thread</literal> |
+ <literal>managed</literal> | <literal>custom.Class</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.query.factory_class</literal>
+ </entry>
+ <entry>
+ Chooses the HQL parser implementation.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>org.hibernate.hql.ast.ASTQueryTranslatorFactory</literal> or
+ <literal>org.hibernate.hql.classic.ClassicQueryTranslatorFactory</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.query.substitutions</literal>
+ </entry>
+ <entry>
+ Mapping from tokens in Hibernate queries to SQL tokens
+ (tokens might be function or literal names, for example).
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>hqlLiteral=SQL_LITERAL, hqlFunction=SQLFUNC</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.hbm2ddl.auto</literal>
+ </entry>
+ <entry>
+ Automatically validate or export schema DDL to the database
+ when the <literal>SessionFactory</literal> is created. With
+ <literal>create-drop</literal>, the database schema will be
+ dropped when the <literal>SessionFactory</literal> is closed
+ explicitly.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>validate</literal> | <literal>update</literal> |
+ <literal>create</literal> | <literal>create-drop</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cglib.use_reflection_optimizer</literal>
+ </entry>
+ <entry>
+ Enables use of CGLIB instead of runtime reflection (System-level
+ property). Reflection can sometimes be useful when troubleshooting,
+ note that Hibernate always requires CGLIB even if you turn off the
+ optimizer. You can not set this property in <literal>hibernate.cfg.xml</literal>.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <sect2 id="configuration-optional-dialects" revision="1">
+ <title>SQL Dialects</title>
+
+ <para>
+ You should always set the <literal>hibernate.dialect</literal> property to the correct
+ <literal>org.hibernate.dialect.Dialect</literal> subclass for your database. If you
+ specify a dialect, Hibernate will use sensible defaults for some of the
+ other properties listed above, saving you the effort of specifying them manually.
+ </para>
+
+ <table frame="topbot" id="sql-dialects" revision="2">
+ <title>Hibernate SQL Dialects (<literal>hibernate.dialect</literal>)</title>
+ <tgroup cols="2">
+ <colspec colwidth="1*"/>
+ <colspec colwidth="2.5*"/>
+ <thead>
+ <row>
+ <entry>RDBMS</entry>
+ <entry>Dialect</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>DB2</entry> <entry><literal>org.hibernate.dialect.DB2Dialect</literal></entry>
+ </row>
+ <row>
+ <entry>DB2 AS/400</entry> <entry><literal>org.hibernate.dialect.DB2400Dialect</literal></entry>
+ </row>
+ <row>
+ <entry>DB2 OS390</entry> <entry><literal>org.hibernate.dialect.DB2390Dialect</literal></entry>
+ </row>
+ <row>
+ <entry>PostgreSQL</entry> <entry><literal>org.hibernate.dialect.PostgreSQLDialect</literal></entry>
+ </row>
+ <row>
+ <entry>MySQL</entry> <entry><literal>org.hibernate.dialect.MySQLDialect</literal></entry>
+ </row>
+ <row>
+ <entry>MySQL with InnoDB</entry> <entry><literal>org.hibernate.dialect.MySQLInnoDBDialect</literal></entry>
+ </row>
+ <row>
+ <entry>MySQL with MyISAM</entry> <entry><literal>org.hibernate.dialect.MySQLMyISAMDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Oracle (any version)</entry> <entry><literal>org.hibernate.dialect.OracleDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Oracle 9i/10g</entry> <entry><literal>org.hibernate.dialect.Oracle9Dialect</literal></entry>
+ </row>
+ <row>
+ <entry>Sybase</entry> <entry><literal>org.hibernate.dialect.SybaseDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Sybase Anywhere</entry> <entry><literal>org.hibernate.dialect.SybaseAnywhereDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Microsoft SQL Server</entry> <entry><literal>org.hibernate.dialect.SQLServerDialect</literal></entry>
+ </row>
+ <row>
+ <entry>SAP DB</entry> <entry><literal>org.hibernate.dialect.SAPDBDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Informix</entry> <entry><literal>org.hibernate.dialect.InformixDialect</literal></entry>
+ </row>
+ <row>
+ <entry>HypersonicSQL</entry> <entry><literal>org.hibernate.dialect.HSQLDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Ingres</entry> <entry><literal>org.hibernate.dialect.IngresDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Progress</entry> <entry><literal>org.hibernate.dialect.ProgressDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Mckoi SQL</entry> <entry><literal>org.hibernate.dialect.MckoiDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Interbase</entry> <entry><literal>org.hibernate.dialect.InterbaseDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Pointbase</entry> <entry><literal>org.hibernate.dialect.PointbaseDialect</literal></entry>
+ </row>
+ <row>
+ <entry>FrontBase</entry> <entry><literal>org.hibernate.dialect.FrontbaseDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Firebird</entry> <entry><literal>org.hibernate.dialect.FirebirdDialect</literal></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-outerjoin" revision="4">
+ <title>Outer Join Fetching</title>
+
+ <para>
+ If your database supports ANSI, Oracle or Sybase style outer joins, <emphasis>outer join
+ fetching</emphasis> will often increase performance by limiting the number of round
+ trips to and from the database (at the cost of possibly more work performed by
+ the database itself). Outer join fetching allows a whole graph of objects connected
+ by many-to-one, one-to-many, many-to-many and one-to-one associations to be retrieved
+ in a single SQL <literal>SELECT</literal>.
+ </para>
+
+ <para>
+ Outer join fetching may be disabled <emphasis>globally</emphasis> by setting
+ the property <literal>hibernate.max_fetch_depth</literal> to <literal>0</literal>.
+ A setting of <literal>1</literal> or higher enables outer join fetching for
+ one-to-one and many-to-one associations which have been mapped with
+ <literal>fetch="join"</literal>.
+ </para>
+
+ <para>
+ See <xref linkend="performance-fetching"/> for more information.
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-binarystreams" revision="1">
+ <title>Binary Streams</title>
+
+ <para>
+ Oracle limits the size of <literal>byte</literal> arrays that may
+ be passed to/from its JDBC driver. If you wish to use large instances of
+ <literal>binary</literal> or <literal>serializable</literal> type, you should
+ enable <literal>hibernate.jdbc.use_streams_for_binary</literal>.
+ <emphasis>This is a system-level setting only.</emphasis>
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-cacheprovider" revision="2">
+ <title>Second-level and query cache</title>
+
+ <para>
+ The properties prefixed by <literal>hibernate.cache</literal>
+ allow you to use a process or cluster scoped second-level cache system
+ with Hibernate. See the <xref linkend="performance-cache"/> for
+ more details.
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-querysubstitution">
+ <title>Query Language Substitution</title>
+
+ <para>
+ You may define new Hibernate query tokens using <literal>hibernate.query.substitutions</literal>.
+ For example:
+ </para>
+
+ <programlisting>hibernate.query.substitutions true=1, false=0</programlisting>
+
+ <para>
+ would cause the tokens <literal>true</literal> and <literal>false</literal> to be translated to
+ integer literals in the generated SQL.
+ </para>
+
+ <programlisting>hibernate.query.substitutions toLowercase=LOWER</programlisting>
+
+ <para>
+ would allow you to rename the SQL <literal>LOWER</literal> function.
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-statistics" revision="2">
+ <title>Hibernate statistics</title>
+
+ <para>
+ If you enable <literal>hibernate.generate_statistics</literal>, Hibernate will
+ expose a number of metrics that are useful when tuning a running system via
+ <literal>SessionFactory.getStatistics()</literal>. Hibernate can even be configured
+ to expose these statistics via JMX. Read the Javadoc of the interfaces in
+ <literal>org.hibernate.stats</literal> for more information.
+ </para>
+
+ </sect2>
+ </sect1>
+
+ <sect1 id="configuration-logging">
+ <title>Logging</title>
+
+ <para>
+ Hibernate logs various events using Apache commons-logging.
+ </para>
+
+ <para>
+ The commons-logging service will direct output to either Apache Log4j
+ (if you include <literal>log4j.jar</literal> in your classpath) or
+ JDK1.4 logging (if running under JDK1.4 or above). You may download
+ Log4j from <literal>http://jakarta.apache.org</literal>.
+ To use Log4j you will need to place a <literal>log4j.properties</literal>
+ file in your classpath, an example properties file is distributed with
+ Hibernate in the <literal>src/</literal> directory.
+ </para>
+
+ <para>
+ We strongly recommend that you familiarize yourself with Hibernate's log
+ messages. A lot of work has been put into making the Hibernate log as
+ detailed as possible, without making it unreadable. It is an essential
+ troubleshooting device. The most interesting log categories are the
+ following:
+ </para>
+
+ <table frame="topbot" id="log-categories" revision="2">
+ <title>Hibernate Log Categories</title>
+ <tgroup cols="2">
+ <colspec colwidth="1*"/>
+ <colspec colwidth="2.5*"/>
+ <thead>
+ <row>
+ <entry>Category</entry>
+ <entry>Function</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>org.hibernate.SQL</literal></entry>
+ <entry>Log all SQL DML statements as they are executed</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.type</literal></entry>
+ <entry>Log all JDBC parameters</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.tool.hbm2ddl</literal></entry>
+ <entry>Log all SQL DDL statements as they are executed</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.pretty</literal></entry>
+ <entry>
+ Log the state of all entities (max 20 entities) associated
+ with the session at flush time
+ </entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.cache</literal></entry>
+ <entry>Log all second-level cache activity</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction</literal></entry>
+ <entry>Log transaction related activity</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.jdbc</literal></entry>
+ <entry>Log all JDBC resource acquisition</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.hql.ast.AST</literal></entry>
+ <entry>
+ Log HQL and SQL ASTs during query parsing
+ </entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.secure</literal></entry>
+ <entry>Log all JAAS authorization requests</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate</literal></entry>
+ <entry>
+ Log everything (a lot of information, but very useful for
+ troubleshooting)
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ When developing applications with Hibernate, you should almost always work with
+ <literal>debug</literal> enabled for the category <literal>org.hibernate.SQL</literal>,
+ or, alternatively, the property <literal>hibernate.show_sql</literal> enabled.
+ </para>
+
+
+ </sect1>
+
+ <sect1 id="configuration-namingstrategy">
+ <title>Implementing a <literal>NamingStrategy</literal></title>
+
+ <para>
+ The interface <literal>org.hibernate.cfg.NamingStrategy</literal> allows you
+ to specify a "naming standard" for database objects and schema elements.
+ </para>
+
+ <para>
+ You may provide rules for automatically generating database identifiers from
+ Java identifiers or for processing "logical" column and table names given in
+ the mapping file into "physical" table and column names. This feature helps
+ reduce the verbosity of the mapping document, eliminating repetitive noise
+ (<literal>TBL_</literal> prefixes, for example). The default strategy used by
+ Hibernate is quite minimal.
+ </para>
+
+ <para>
+ You may specify a different strategy by calling
+ <literal>Configuration.setNamingStrategy()</literal> before adding mappings:
+ </para>
+
+ <programlisting><![CDATA[SessionFactory sf = new Configuration()
+ .setNamingStrategy(ImprovedNamingStrategy.INSTANCE)
+ .addFile("Item.hbm.xml")
+ .addFile("Bid.hbm.xml")
+ .buildSessionFactory();]]></programlisting>
+
+ <para>
+ <literal>org.hibernate.cfg.ImprovedNamingStrategy</literal> is a built-in
+ strategy that might be a useful starting point for some applications.
+ </para>
+
+ </sect1>
+
+ <sect1 id="configuration-xmlconfig" revision="2">
+ <title>XML configuration file</title>
+
+ <para>
+ An alternative approach to configuration is to specify a full configuration in
+ a file named <literal>hibernate.cfg.xml</literal>. This file can be used as a
+ replacement for the <literal>hibernate.properties</literal> file or, if both
+ are present, to override properties.
+ </para>
+
+ <para>
+ The XML configuration file is by default expected to be in the root o
+ your <literal>CLASSPATH</literal>. Here is an example:
+ </para>
+
+ <programlisting><![CDATA[<?xml version='1.0' encoding='utf-8'?>
+<!DOCTYPE hibernate-configuration PUBLIC
+ "-//Hibernate/Hibernate Configuration DTD//EN"
+ "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
+
+<hibernate-configuration>
+
+ <!-- a SessionFactory instance listed as /jndi/name -->
+ <session-factory
+ name="java:hibernate/SessionFactory">
+
+ <!-- properties -->
+ <property name="connection.datasource">java:/comp/env/jdbc/MyDB</property>
+ <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
+ <property name="show_sql">false</property>
+ <property name="transaction.factory_class">
+ org.hibernate.transaction.JTATransactionFactory
+ </property>
+ <property name="jta.UserTransaction">java:comp/UserTransaction</property>
+
+ <!-- mapping files -->
+ <mapping resource="org/hibernate/auction/Item.hbm.xml"/>
+ <mapping resource="org/hibernate/auction/Bid.hbm.xml"/>
+
+ <!-- cache settings -->
+ <class-cache class="org.hibernate.auction.Item" usage="read-write"/>
+ <class-cache class="org.hibernate.auction.Bid" usage="read-only"/>
+ <collection-cache collection="org.hibernate.auction.Item.bids" usage="read-write"/>
+
+ </session-factory>
+
+</hibernate-configuration>]]></programlisting>
+
+ <para>
+ As you can see, the advantage of this approach is the externalization of the
+ mapping file names to configuration. The <literal>hibernate.cfg.xml</literal>
+ is also more convenient once you have to tune the Hibernate cache. Note that is
+ your choice to use either <literal>hibernate.properties</literal> or
+ <literal>hibernate.cfg.xml</literal>, both are equivalent, except for the above
+ mentioned benefits of using the XML syntax.
+ </para>
+
+ <para>
+ With the XML configuration, starting Hibernate is then as simple as
+ </para>
+
+ <programlisting><![CDATA[SessionFactory sf = new Configuration().configure().buildSessionFactory();]]></programlisting>
+
+ <para>
+ You can pick a different XML configuration file using
+ </para>
+
+ <programlisting><![CDATA[SessionFactory sf = new Configuration()
+ .configure("catdb.cfg.xml")
+ .buildSessionFactory();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="configuration-j2ee" revision="1">
+ <title>J2EE Application Server integration</title>
+
+ <para>
+ Hibernate has the following integration points for J2EE infrastructure:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Container-managed datasources</emphasis>: Hibernate can use
+ JDBC connections managed by the container and provided through JNDI. Usually,
+ a JTA compatible <literal>TransactionManager</literal> and a
+ <literal>ResourceManager</literal> take care of transaction management (CMT),
+ esp. distributed transaction handling across several datasources. You may
+ of course also demarcate transaction boundaries programatically (BMT) or
+ you might want to use the optional Hibernate <literal>Transaction</literal>
+ API for this to keep your code portable.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Automatic JNDI binding</emphasis>: Hibernate can bind its
+ <literal>SessionFactory</literal> to JNDI after startup.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>JTA Session binding:</emphasis> The Hibernate <literal>Session</literal>
+ may be automatically bound to the scope of JTA transactions. Simply
+ lookup the <literal>SessionFactory</literal> from JNDI and get the current
+ <literal>Session</literal>. Let Hibernate take care of flushing and closing the
+ <literal>Session</literal> when your JTA transaction completes. Transaction
+ demarcation is either declarative (CMT) or programmatic (BMT/UserTransaction).
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>JMX deployment:</emphasis> If you have a JMX capable application server
+ (e.g. JBoss AS), you can chose to deploy Hibernate as a managed MBean. This saves
+ you the one line startup code to build your <literal>SessionFactory</literal> from
+ a <literal>Configuration</literal>. The container will startup your
+ <literal>HibernateService</literal>, and ideally also take care of service
+ dependencies (Datasource has to be available before Hibernate starts, etc).
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Depending on your environment, you might have to set the configuration option
+ <literal>hibernate.connection.aggressive_release</literal> to true if your
+ application server shows "connection containment" exceptions.
+ </para>
+
+ <sect2 id="configuration-optional-transactionstrategy" revision="3">
+ <title>Transaction strategy configuration</title>
+
+ <para>
+ The Hibernate <literal>Session</literal> API is independent of any transaction
+ demarcation system in your architecture. If you let Hibernate use JDBC directly,
+ through a connection pool, you may begin and end your transactions by calling
+ the JDBC API. If you run in a J2EE application server, you might want to use bean-managed
+ transactions and call the JTA API and <literal>UserTransaction</literal> when needed.
+ </para>
+
+ <para>
+ To keep your code portable between these two (and other) environments we recommend the optional
+ Hibernate <literal>Transaction</literal> API, which wraps and hides the underlying system.
+ You have to specify a factory class for <literal>Transaction</literal> instances by setting the
+ Hibernate configuration property <literal>hibernate.transaction.factory_class</literal>.
+ </para>
+
+ <para>
+ There are three standard (built-in) choices:
+ </para>
+
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term><literal>org.hibernate.transaction.JDBCTransactionFactory</literal></term>
+ <listitem>
+ <para>delegates to database (JDBC) transactions (default)</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>org.hibernate.transaction.JTATransactionFactory</literal></term>
+ <listitem>
+ <para>
+ delegates to container-managed transaction if an existing transaction is
+ underway in this context (e.g. EJB session bean method), otherwise
+ a new transaction is started and bean-managed transaction are used.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>org.hibernate.transaction.CMTTransactionFactory</literal></term>
+ <listitem>
+ <para>delegates to container-managed JTA transactions</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>
+ You may also define your own transaction strategies (for a CORBA transaction service,
+ for example).
+ </para>
+
+ <para>
+ Some features in Hibernate (i.e. the second level cache, Contextual Sessions with JTA, etc.)
+ require access to the JTA <literal>TransactionManager</literal> in a managed environment.
+ In an application server you have to specify how Hibernate should obtain a reference to the
+ <literal>TransactionManager</literal>, since J2EE does not standardize a single mechanism:
+ </para>
+
+ <table frame="topbot" id="jtamanagerlookup" revision="1">
+ <title>JTA TransactionManagers</title>
+ <tgroup cols="2">
+ <colspec colwidth="2.5*"/>
+ <colspec colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Transaction Factory</entry>
+ <entry align="center">Application Server</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>org.hibernate.transaction.JBossTransactionManagerLookup</literal></entry>
+ <entry align="center">JBoss</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.WeblogicTransactionManagerLookup</literal></entry>
+ <entry align="center">Weblogic</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.WebSphereTransactionManagerLookup</literal></entry>
+ <entry align="center">WebSphere</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.WebSphereExtendedJTATransactionLookup</literal></entry>
+ <entry align="center">WebSphere 6</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.OrionTransactionManagerLookup</literal></entry>
+ <entry align="center">Orion</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.ResinTransactionManagerLookup</literal></entry>
+ <entry align="center">Resin</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.JOTMTransactionManagerLookup</literal></entry>
+ <entry align="center">JOTM</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.JOnASTransactionManagerLookup</literal></entry>
+ <entry align="center">JOnAS</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.JRun4TransactionManagerLookup</literal></entry>
+ <entry align="center">JRun4</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.BESTransactionManagerLookup</literal></entry>
+ <entry align="center">Borland ES</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-jndi" revision="3">
+ <title>JNDI-bound <literal>SessionFactory</literal></title>
+
+ <para>
+ A JNDI bound Hibernate <literal>SessionFactory</literal> can simplify the lookup
+ of the factory and the creation of new <literal>Session</literal>s. Note that this
+ is not related to a JNDI bound <literal>Datasource</literal>, both simply use the
+ same registry!
+ </para>
+
+ <para>
+ If you wish to have the <literal>SessionFactory</literal> bound to a JNDI namespace, specify
+ a name (eg. <literal>java:hibernate/SessionFactory</literal>) using the property
+ <literal>hibernate.session_factory_name</literal>. If this property is omitted, the
+ <literal>SessionFactory</literal> will not be bound to JNDI. (This is especially useful in
+ environments with a read-only JNDI default implementation, e.g. Tomcat.)
+ </para>
+
+ <para>
+ When binding the <literal>SessionFactory</literal> to JNDI, Hibernate will use the values of
+ <literal>hibernate.jndi.url</literal>, <literal>hibernate.jndi.class</literal> to instantiate
+ an initial context. If they are not specified, the default <literal>InitialContext</literal>
+ will be used.
+ </para>
+
+ <para>
+ Hibernate will automatically place the <literal>SessionFactory</literal> in JNDI after
+ you call <literal>cfg.buildSessionFactory()</literal>. This means you will at least have
+ this call in some startup code (or utility class) in your application, unless you use
+ JMX deployment with the <literal>HibernateService</literal> (discussed later).
+ </para>
+
+ <para>
+ If you use a JNDI <literal>SessionFactory</literal>, an EJB or any other class may
+ obtain the <literal>SessionFactory</literal> using a JNDI lookup.
+ </para>
+
+ <para>
+ We recommend that you bind the <literal>SessionFactory</literal> to JNDI in
+ a managend environment and use a <literal>static</literal> singleton otherwise.
+ To shield your application code from these details, we also recommend to hide the
+ actual lookup code for a <literal>SessionFactory</literal> in a helper class,
+ such as <literal>HibernateUtil.getSessionFactory()</literal>. Note that such a
+ class is also a convenient way to startup Hibernate—see chapter 1.
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-j2ee-currentsession" revision="4">
+ <title>Current Session context management with JTA</title>
+
+ <para>
+ The easiest way to handle <literal>Session</literal>s and transactions is
+ Hibernates automatic "current" <literal>Session</literal> management.
+ See the discussion of <xref linkend="architecture-current-session">current sessions</xref>.
+ Using the <literal>"jta"</literal> session context, if there is no Hibernate
+ <literal>Session</literal> associated with the current JTA transaction, one will
+ be started and associated with that JTA transaction the first time you call
+ <literal>sessionFactory.getCurrentSession()</literal>. The <literal>Session</literal>s
+ retrieved via <literal>getCurrentSession()</literal> in <literal>"jta"</literal> context
+ will be set to automatically flush before the transaction completes, close
+ after the transaction completes, and aggressively release JDBC connections
+ after each statement. This allows the <literal>Session</literal>s to
+ be managed by the lifecycle of the JTA transaction to which it is associated,
+ keeping user code clean of such management concerns. Your code can either use
+ JTA programmatically through <literal>UserTransaction</literal>, or (recommended
+ for portable code) use the Hibernate <literal>Transaction</literal> API to set
+ transaction boundaries. If you run in an EJB container, declarative transaction
+ demarcation with CMT is preferred.
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-j2ee-jmx" revision="1">
+ <title>JMX deployment</title>
+
+ <para>
+ The line <literal>cfg.buildSessionFactory()</literal> still has to be executed
+ somewhere to get a <literal>SessionFactory</literal> into JNDI. You can do this
+ either in a <literal>static</literal> initializer block (like the one in
+ <literal>HibernateUtil</literal>) or you deploy Hibernate as a <emphasis>managed
+ service</emphasis>.
+ </para>
+
+ <para>
+ Hibernate is distributed with <literal>org.hibernate.jmx.HibernateService</literal>
+ for deployment on an application server with JMX capabilities, such as JBoss AS.
+ The actual deployment and configuration is vendor specific. Here is an example
+ <literal>jboss-service.xml</literal> for JBoss 4.0.x:
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<server>
+
+<mbean code="org.hibernate.jmx.HibernateService"
+ name="jboss.jca:service=HibernateFactory,name=HibernateFactory">
+
+ <!-- Required services -->
+ <depends>jboss.jca:service=RARDeployer</depends>
+ <depends>jboss.jca:service=LocalTxCM,name=HsqlDS</depends>
+
+ <!-- Bind the Hibernate service to JNDI -->
+ <attribute name="JndiName">java:/hibernate/SessionFactory</attribute>
+
+ <!-- Datasource settings -->
+ <attribute name="Datasource">java:HsqlDS</attribute>
+ <attribute name="Dialect">org.hibernate.dialect.HSQLDialect</attribute>
+
+ <!-- Transaction integration -->
+ <attribute name="TransactionStrategy">
+ org.hibernate.transaction.JTATransactionFactory</attribute>
+ <attribute name="TransactionManagerLookupStrategy">
+ org.hibernate.transaction.JBossTransactionManagerLookup</attribute>
+ <attribute name="FlushBeforeCompletionEnabled">true</attribute>
+ <attribute name="AutoCloseSessionEnabled">true</attribute>
+
+ <!-- Fetching options -->
+ <attribute name="MaximumFetchDepth">5</attribute>
+
+ <!-- Second-level caching -->
+ <attribute name="SecondLevelCacheEnabled">true</attribute>
+ <attribute name="CacheProviderClass">org.hibernate.cache.EhCacheProvider</attribute>
+ <attribute name="QueryCacheEnabled">true</attribute>
+
+ <!-- Logging -->
+ <attribute name="ShowSqlEnabled">true</attribute>
+
+ <!-- Mapping files -->
+ <attribute name="MapResources">auction/Item.hbm.xml,auction/Category.hbm.xml</attribute>
+
+</mbean>
+
+</server>]]></programlisting>
+
+ <para>
+ This file is deployed in a directory called <literal>META-INF</literal> and packaged
+ in a JAR file with the extension <literal>.sar</literal> (service archive). You also need
+ to package Hibernate, its required third-party libraries, your compiled persistent classes,
+ as well as your mapping files in the same archive. Your enterprise beans (usually session
+ beans) may be kept in their own JAR file, but you may include this EJB JAR file in the
+ main service archive to get a single (hot-)deployable unit. Consult the JBoss AS
+ documentation for more information about JMX service and EJB deployment.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/events.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/events.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/events.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,265 @@
+<chapter id="events">
+ <title>Interceptors and events</title>
+
+ <para>
+ It is often useful for the application to react to certain events that occur
+ inside Hibernate. This allows implementation of certain kinds of generic
+ functionality, and extension of Hibernate functionality.
+ </para>
+
+ <sect1 id="objectstate-interceptors" revision="3">
+ <title>Interceptors</title>
+
+ <para>
+ The <literal>Interceptor</literal> interface provides callbacks from the session to the
+ application allowing the application to inspect and/or manipulate properties of a
+ persistent object before it is saved, updated, deleted or loaded. One
+ possible use for this is to track auditing information. For example, the following
+ <literal>Interceptor</literal> automatically sets the <literal>createTimestamp</literal>
+ when an <literal>Auditable</literal> is created and updates the
+ <literal>lastUpdateTimestamp</literal> property when an <literal>Auditable</literal> is
+ updated.
+ </para>
+
+ <para>
+ You may either implement <literal>Interceptor</literal> directly or (better) extend
+ <literal>EmptyInterceptor</literal>.
+ </para>
+
+ <programlisting><![CDATA[package org.hibernate.test;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.Iterator;
+
+import org.hibernate.EmptyInterceptor;
+import org.hibernate.Transaction;
+import org.hibernate.type.Type;
+
+public class AuditInterceptor extends EmptyInterceptor {
+
+ private int updates;
+ private int creates;
+ private int loads;
+
+ public void onDelete(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+ // do nothing
+ }
+
+ public boolean onFlushDirty(Object entity,
+ Serializable id,
+ Object[] currentState,
+ Object[] previousState,
+ String[] propertyNames,
+ Type[] types) {
+
+ if ( entity instanceof Auditable ) {
+ updates++;
+ for ( int i=0; i < propertyNames.length; i++ ) {
+ if ( "lastUpdateTimestamp".equals( propertyNames[i] ) ) {
+ currentState[i] = new Date();
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public boolean onLoad(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+ if ( entity instanceof Auditable ) {
+ loads++;
+ }
+ return false;
+ }
+
+ public boolean onSave(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+
+ if ( entity instanceof Auditable ) {
+ creates++;
+ for ( int i=0; i<propertyNames.length; i++ ) {
+ if ( "createTimestamp".equals( propertyNames[i] ) ) {
+ state[i] = new Date();
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public void afterTransactionCompletion(Transaction tx) {
+ if ( tx.wasCommitted() ) {
+ System.out.println("Creations: " + creates + ", Updates: " + updates, "Loads: " + loads);
+ }
+ updates=0;
+ creates=0;
+ loads=0;
+ }
+
+}]]></programlisting>
+
+ <para>
+ Interceptors come in two flavors: <literal>Session</literal>-scoped and
+ <literal>SessionFactory</literal>-scoped.
+ </para>
+
+ <para>
+ A <literal>Session</literal>-scoped interceptor is specified
+ when a session is opened using one of the overloaded SessionFactory.openSession()
+ methods accepting an <literal>Interceptor</literal>.
+ </para>
+
+ <programlisting><![CDATA[Session session = sf.openSession( new AuditInterceptor() );]]></programlisting>
+
+ <para>
+ A <literal>SessionFactory</literal>-scoped interceptor is registered with the <literal>Configuration</literal>
+ object prior to building the <literal>SessionFactory</literal>. In this case, the supplied interceptor
+ will be applied to all sessions opened from that <literal>SessionFactory</literal>; this is true unless
+ a session is opened explicitly specifying the interceptor to use. <literal>SessionFactory</literal>-scoped
+ interceptors must be thread safe, taking care to not store session-specific state since multiple
+ sessions will use this interceptor (potentially) concurrently.
+ </para>
+
+ <programlisting><![CDATA[new Configuration().setInterceptor( new AuditInterceptor() );]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="objectstate-events" revision="4">
+ <title>Event system</title>
+
+ <para>
+ If you have to react to particular events in your persistence layer, you may
+ also use the Hibernate3 <emphasis>event</emphasis> architecture. The event
+ system can be used in addition or as a replacement for interceptors.
+ </para>
+
+ <para>
+ Essentially all of the methods of the <literal>Session</literal> interface correlate
+ to an event. You have a <literal>LoadEvent</literal>, a <literal>FlushEvent</literal>, etc
+ (consult the XML configuration-file DTD or the <literal>org.hibernate.event</literal>
+ package for the full list of defined event types). When a request is made of one of
+ these methods, the Hibernate <literal>Session</literal> generates an appropriate
+ event and passes it to the configured event listeners for that type. Out-of-the-box,
+ these listeners implement the same processing in which those methods always resulted.
+ However, you are free to implement a customization of one of the listener interfaces
+ (i.e., the <literal>LoadEvent</literal> is processed by the registered implemenation
+ of the <literal>LoadEventListener</literal> interface), in which case their
+ implementation would be responsible for processing any <literal>load()</literal> requests
+ made of the <literal>Session</literal>.
+ </para>
+
+ <para>
+ The listeners should be considered effectively singletons; meaning, they are shared between
+ requests, and thus should not save any state as instance variables.
+ </para>
+
+ <para>
+ A custom listener should implement the appropriate interface for the event it wants to
+ process and/or extend one of the convenience base classes (or even the default event
+ listeners used by Hibernate out-of-the-box as these are declared non-final for this
+ purpose). Custom listeners can either be registered programmatically through the
+ <literal>Configuration</literal> object, or specified in the Hibernate configuration
+ XML (declarative configuration through the properties file is not supported). Here's an
+ example of a custom load event listener:
+ </para>
+
+ <programlisting><![CDATA[public class MyLoadListener implements LoadEventListener {
+ // this is the single method defined by the LoadEventListener interface
+ public void onLoad(LoadEvent event, LoadEventListener.LoadType loadType)
+ throws HibernateException {
+ if ( !MySecurity.isAuthorized( event.getEntityClassName(), event.getEntityId() ) ) {
+ throw MySecurityException("Unauthorized access");
+ }
+ }
+}]]></programlisting>
+
+ <para>
+ You also need a configuration entry telling Hibernate to use the listener in addition
+ to the default listener:
+ </para>
+
+<programlisting><![CDATA[<hibernate-configuration>
+ <session-factory>
+ ...
+ <event type="load">
+ <listener class="com.eg.MyLoadListener"/>
+ <listener class="org.hibernate.event.def.DefaultLoadEventListener"/>
+ </event>
+ </session-factory>
+</hibernate-configuration>]]></programlisting>
+
+ <para>
+ Instead, you may register it programmatically:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = new Configuration();
+LoadEventListener[] stack = { new MyLoadListener(), new DefaultLoadEventListener() };
+cfg.EventListeners().setLoadEventListeners(stack);]]></programlisting>
+
+ <para>
+ Listeners registered declaratively cannot share instances. If the same class name is
+ used in multiple <literal><listener/></literal> elements, each reference will
+ result in a separate instance of that class. If you need the capability to share
+ listener instances between listener types you must use the programmatic registration
+ approach.
+ </para>
+
+ <para>
+ Why implement an interface and define the specific type during configuration? Well, a
+ listener implementation could implement multiple event listener interfaces. Having the
+ type additionally defined during registration makes it easier to turn custom listeners on
+ or off during configuration.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-decl-security" revision="2">
+ <title>Hibernate declarative security</title>
+ <para>
+ Usually, declarative security in Hibernate applications is managed in a session facade
+ layer. Now, Hibernate3 allows certain actions to be permissioned via JACC, and authorized
+ via JAAS. This is optional functionality built on top of the event architecture.
+ </para>
+
+ <para>
+ First, you must configure the appropriate event listeners, to enable the use of JAAS
+ authorization.
+ </para>
+
+ <programlisting><![CDATA[<listener type="pre-delete" class="org.hibernate.secure.JACCPreDeleteEventListener"/>
+<listener type="pre-update" class="org.hibernate.secure.JACCPreUpdateEventListener"/>
+<listener type="pre-insert" class="org.hibernate.secure.JACCPreInsertEventListener"/>
+<listener type="pre-load" class="org.hibernate.secure.JACCPreLoadEventListener"/>]]></programlisting>
+
+ <para>
+ Note that <literal><listener type="..." class="..."/></literal> is just a shorthand
+ for <literal><event type="..."><listener class="..."/></event></literal>
+ when there is exactly one listener for a particular event type.
+ </para>
+
+ <para>
+ Next, still in <literal>hibernate.cfg.xml</literal>, bind the permissions to roles:
+ </para>
+
+ <programlisting><![CDATA[<grant role="admin" entity-name="User" actions="insert,update,read"/>
+<grant role="su" entity-name="User" actions="*"/>]]></programlisting>
+
+ <para>
+ The role names are the roles understood by your JACC provider.
+ </para>
+
+ </sect1>
+
+</chapter>
+
Property changes on: trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/events.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/example_mappings.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/example_mappings.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/example_mappings.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,658 @@
+<chapter id="example-mappings">
+ <title>Example: Various Mappings</title>
+
+ <para>
+ This chapters shows off some more complex association mappings.
+ </para>
+
+ <sect1 id="example-mappings-emp">
+ <title>Employer/Employee</title>
+
+ <para>
+ The following model of the relationship between <literal>Employer</literal> and
+ <literal>Employee</literal> uses an actual entity class (<literal>Employment</literal>)
+ to represent the association. This is done because there might be more than one
+ period of employment for the same two parties. Components are used to model monetary
+ values and employee names.
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/EmployerEmployee.gif" format="GIF" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/EmployerEmployee.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ Heres a possible mapping document:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Employer" table="employers">
+ <id name="id">
+ <generator class="sequence">
+ <param name="sequence">employer_id_seq</param>
+ </generator>
+ </id>
+ <property name="name"/>
+ </class>
+
+ <class name="Employment" table="employment_periods">
+
+ <id name="id">
+ <generator class="sequence">
+ <param name="sequence">employment_id_seq</param>
+ </generator>
+ </id>
+ <property name="startDate" column="start_date"/>
+ <property name="endDate" column="end_date"/>
+
+ <component name="hourlyRate" class="MonetaryAmount">
+ <property name="amount">
+ <column name="hourly_rate" sql-type="NUMERIC(12, 2)"/>
+ </property>
+ <property name="currency" length="12"/>
+ </component>
+
+ <many-to-one name="employer" column="employer_id" not-null="true"/>
+ <many-to-one name="employee" column="employee_id" not-null="true"/>
+
+ </class>
+
+ <class name="Employee" table="employees">
+ <id name="id">
+ <generator class="sequence">
+ <param name="sequence">employee_id_seq</param>
+ </generator>
+ </id>
+ <property name="taxfileNumber"/>
+ <component name="name" class="Name">
+ <property name="firstName"/>
+ <property name="initial"/>
+ <property name="lastName"/>
+ </component>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ And heres the table schema generated by <literal>SchemaExport</literal>.
+ </para>
+
+ <programlisting><![CDATA[create table employers (
+ id BIGINT not null,
+ name VARCHAR(255),
+ primary key (id)
+)
+
+create table employment_periods (
+ id BIGINT not null,
+ hourly_rate NUMERIC(12, 2),
+ currency VARCHAR(12),
+ employee_id BIGINT not null,
+ employer_id BIGINT not null,
+ end_date TIMESTAMP,
+ start_date TIMESTAMP,
+ primary key (id)
+)
+
+create table employees (
+ id BIGINT not null,
+ firstName VARCHAR(255),
+ initial CHAR(1),
+ lastName VARCHAR(255),
+ taxfileNumber VARCHAR(255),
+ primary key (id)
+)
+
+alter table employment_periods
+ add constraint employment_periodsFK0 foreign key (employer_id) references employers
+alter table employment_periods
+ add constraint employment_periodsFK1 foreign key (employee_id) references employees
+create sequence employee_id_seq
+create sequence employment_id_seq
+create sequence employer_id_seq]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-mappings-authorwork">
+ <title>Author/Work</title>
+
+ <para>
+ Consider the following model of the relationships between <literal>Work</literal>,
+ <literal>Author</literal> and <literal>Person</literal>. We represent the relationship
+ between <literal>Work</literal> and <literal>Author</literal> as a many-to-many
+ association. We choose to represent the relationship between <literal>Author</literal>
+ and <literal>Person</literal> as one-to-one association. Another possibility would be to
+ have <literal>Author</literal> extend <literal>Person</literal>.
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/AuthorWork.gif" format="GIF" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/AuthorWork.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ The following mapping document correctly represents these relationships:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Work" table="works" discriminator-value="W">
+
+ <id name="id" column="id">
+ <generator class="native"/>
+ </id>
+ <discriminator column="type" type="character"/>
+
+ <property name="title"/>
+ <set name="authors" table="author_work">
+ <key column name="work_id"/>
+ <many-to-many class="Author" column name="author_id"/>
+ </set>
+
+ <subclass name="Book" discriminator-value="B">
+ <property name="text"/>
+ </subclass>
+
+ <subclass name="Song" discriminator-value="S">
+ <property name="tempo"/>
+ <property name="genre"/>
+ </subclass>
+
+ </class>
+
+ <class name="Author" table="authors">
+
+ <id name="id" column="id">
+ <!-- The Author must have the same identifier as the Person -->
+ <generator class="assigned"/>
+ </id>
+
+ <property name="alias"/>
+ <one-to-one name="person" constrained="true"/>
+
+ <set name="works" table="author_work" inverse="true">
+ <key column="author_id"/>
+ <many-to-many class="Work" column="work_id"/>
+ </set>
+
+ </class>
+
+ <class name="Person" table="persons">
+ <id name="id" column="id">
+ <generator class="native"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ There are four tables in this mapping. <literal>works</literal>,
+ <literal>authors</literal> and <literal>persons</literal> hold work, author
+ and person data respectively. <literal>author_work</literal> is an association
+ table linking authors to works. Heres the table schema, as generated by
+ <literal>SchemaExport</literal>.
+ </para>
+
+ <programlisting><![CDATA[create table works (
+ id BIGINT not null generated by default as identity,
+ tempo FLOAT,
+ genre VARCHAR(255),
+ text INTEGER,
+ title VARCHAR(255),
+ type CHAR(1) not null,
+ primary key (id)
+)
+
+create table author_work (
+ author_id BIGINT not null,
+ work_id BIGINT not null,
+ primary key (work_id, author_id)
+)
+
+create table authors (
+ id BIGINT not null generated by default as identity,
+ alias VARCHAR(255),
+ primary key (id)
+)
+
+create table persons (
+ id BIGINT not null generated by default as identity,
+ name VARCHAR(255),
+ primary key (id)
+)
+
+alter table authors
+ add constraint authorsFK0 foreign key (id) references persons
+alter table author_work
+ add constraint author_workFK0 foreign key (author_id) references authors
+alter table author_work
+ add constraint author_workFK1 foreign key (work_id) references works]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-mappings-customerorderproduct">
+ <title>Customer/Order/Product</title>
+
+ <para>
+ Now consider a model of the relationships between <literal>Customer</literal>,
+ <literal>Order</literal> and <literal>LineItem</literal> and <literal>Product</literal>.
+ There is a one-to-many association between <literal>Customer</literal> and
+ <literal>Order</literal>, but how should we represent <literal>Order</literal> /
+ <literal>LineItem</literal> / <literal>Product</literal>? I've chosen to map
+ <literal>LineItem</literal> as an association class representing the many-to-many
+ association between <literal>Order</literal> and <literal>Product</literal>. In
+ Hibernate, this is called a composite element.
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/CustomerOrderProduct.gif" format="GIF" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/CustomerOrderProduct.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ The mapping document:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Customer" table="customers">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="name"/>
+ <set name="orders" inverse="true">
+ <key column="customer_id"/>
+ <one-to-many class="Order"/>
+ </set>
+ </class>
+
+ <class name="Order" table="orders">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="date"/>
+ <many-to-one name="customer" column="customer_id"/>
+ <list name="lineItems" table="line_items">
+ <key column="order_id"/>
+ <list-index column="line_number"/>
+ <composite-element class="LineItem">
+ <property name="quantity"/>
+ <many-to-one name="product" column="product_id"/>
+ </composite-element>
+ </list>
+ </class>
+
+ <class name="Product" table="products">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="serialNumber"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ <literal>customers</literal>, <literal>orders</literal>, <literal>line_items</literal> and
+ <literal>products</literal> hold customer, order, order line item and product data
+ respectively. <literal>line_items</literal> also acts as an association table linking
+ orders with products.
+ </para>
+
+ <programlisting><![CDATA[create table customers (
+ id BIGINT not null generated by default as identity,
+ name VARCHAR(255),
+ primary key (id)
+)
+
+create table orders (
+ id BIGINT not null generated by default as identity,
+ customer_id BIGINT,
+ date TIMESTAMP,
+ primary key (id)
+)
+
+create table line_items (
+ line_number INTEGER not null,
+ order_id BIGINT not null,
+ product_id BIGINT,
+ quantity INTEGER,
+ primary key (order_id, line_number)
+)
+
+create table products (
+ id BIGINT not null generated by default as identity,
+ serialNumber VARCHAR(255),
+ primary key (id)
+)
+
+alter table orders
+ add constraint ordersFK0 foreign key (customer_id) references customers
+alter table line_items
+ add constraint line_itemsFK0 foreign key (product_id) references products
+alter table line_items
+ add constraint line_itemsFK1 foreign key (order_id) references orders]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="misc">
+ <title>Miscellaneous example mappings</title>
+
+ <para>
+ These examples are all taken from the Hibernate test suite. You
+ will find many other useful example mappings there. Look in the
+ <literal>test</literal> folder of the Hibernate distribution.
+ </para>
+
+ <para>TODO: put words around this stuff</para>
+
+ <sect2 id="example-mappings-typed-onetone">
+ <title>"Typed" one-to-one association</title>
+<programlisting><![CDATA[<class name="Person">
+ <id name="name"/>
+ <one-to-one name="address"
+ cascade="all">
+ <formula>name</formula>
+ <formula>'HOME'</formula>
+ </one-to-one>
+ <one-to-one name="mailingAddress"
+ cascade="all">
+ <formula>name</formula>
+ <formula>'MAILING'</formula>
+ </one-to-one>
+</class>
+
+<class name="Address" batch-size="2"
+ check="addressType in ('MAILING', 'HOME', 'BUSINESS')">
+ <composite-id>
+ <key-many-to-one name="person"
+ column="personName"/>
+ <key-property name="type"
+ column="addressType"/>
+ </composite-id>
+ <property name="street" type="text"/>
+ <property name="state"/>
+ <property name="zip"/>
+</class>]]></programlisting>
+ </sect2>
+
+ <sect2 id="example-mappings-composite-key">
+ <title>Composite key example</title>
+<programlisting><![CDATA[<class name="Customer">
+
+ <id name="customerId"
+ length="10">
+ <generator class="assigned"/>
+ </id>
+
+ <property name="name" not-null="true" length="100"/>
+ <property name="address" not-null="true" length="200"/>
+
+ <list name="orders"
+ inverse="true"
+ cascade="save-update">
+ <key column="customerId"/>
+ <index column="orderNumber"/>
+ <one-to-many class="Order"/>
+ </list>
+
+</class>
+
+<class name="Order" table="CustomerOrder" lazy="true">
+ <synchronize table="LineItem"/>
+ <synchronize table="Product"/>
+
+ <composite-id name="id"
+ class="Order$Id">
+ <key-property name="customerId" length="10"/>
+ <key-property name="orderNumber"/>
+ </composite-id>
+
+ <property name="orderDate"
+ type="calendar_date"
+ not-null="true"/>
+
+ <property name="total">
+ <formula>
+ ( select sum(li.quantity*p.price)
+ from LineItem li, Product p
+ where li.productId = p.productId
+ and li.customerId = customerId
+ and li.orderNumber = orderNumber )
+ </formula>
+ </property>
+
+ <many-to-one name="customer"
+ column="customerId"
+ insert="false"
+ update="false"
+ not-null="true"/>
+
+ <bag name="lineItems"
+ fetch="join"
+ inverse="true"
+ cascade="save-update">
+ <key>
+ <column name="customerId"/>
+ <column name="orderNumber"/>
+ </key>
+ <one-to-many class="LineItem"/>
+ </bag>
+
+</class>
+
+<class name="LineItem">
+
+ <composite-id name="id"
+ class="LineItem$Id">
+ <key-property name="customerId" length="10"/>
+ <key-property name="orderNumber"/>
+ <key-property name="productId" length="10"/>
+ </composite-id>
+
+ <property name="quantity"/>
+
+ <many-to-one name="order"
+ insert="false"
+ update="false"
+ not-null="true">
+ <column name="customerId"/>
+ <column name="orderNumber"/>
+ </many-to-one>
+
+ <many-to-one name="product"
+ insert="false"
+ update="false"
+ not-null="true"
+ column="productId"/>
+
+</class>
+
+<class name="Product">
+ <synchronize table="LineItem"/>
+
+ <id name="productId"
+ length="10">
+ <generator class="assigned"/>
+ </id>
+
+ <property name="description"
+ not-null="true"
+ length="200"/>
+ <property name="price" length="3"/>
+ <property name="numberAvailable"/>
+
+ <property name="numberOrdered">
+ <formula>
+ ( select sum(li.quantity)
+ from LineItem li
+ where li.productId = productId )
+ </formula>
+ </property>
+
+</class>]]></programlisting>
+ </sect2>
+
+ <sect2 id="example-mappings-composite-key-manytomany">
+ <title>Many-to-many with shared composite key attribute</title>
+<programlisting><![CDATA[<class name="User" table="`User`">
+ <composite-id>
+ <key-property name="name"/>
+ <key-property name="org"/>
+ </composite-id>
+ <set name="groups" table="UserGroup">
+ <key>
+ <column name="userName"/>
+ <column name="org"/>
+ </key>
+ <many-to-many class="Group">
+ <column name="groupName"/>
+ <formula>org</formula>
+ </many-to-many>
+ </set>
+</class>
+
+<class name="Group" table="`Group`">
+ <composite-id>
+ <key-property name="name"/>
+ <key-property name="org"/>
+ </composite-id>
+ <property name="description"/>
+ <set name="users" table="UserGroup" inverse="true">
+ <key>
+ <column name="groupName"/>
+ <column name="org"/>
+ </key>
+ <many-to-many class="User">
+ <column name="userName"/>
+ <formula>org</formula>
+ </many-to-many>
+ </set>
+</class>
+]]></programlisting>
+ </sect2>
+
+ <sect2 id="example-mappings-content-discrimination">
+ <title>Content based discrimination</title>
+<programlisting><![CDATA[<class name="Person"
+ discriminator-value="P">
+
+ <id name="id"
+ column="person_id"
+ unsaved-value="0">
+ <generator class="native"/>
+ </id>
+
+
+ <discriminator
+ type="character">
+ <formula>
+ case
+ when title is not null then 'E'
+ when salesperson is not null then 'C'
+ else 'P'
+ end
+ </formula>
+ </discriminator>
+
+ <property name="name"
+ not-null="true"
+ length="80"/>
+
+ <property name="sex"
+ not-null="true"
+ update="false"/>
+
+ <component name="address">
+ <property name="address"/>
+ <property name="zip"/>
+ <property name="country"/>
+ </component>
+
+ <subclass name="Employee"
+ discriminator-value="E">
+ <property name="title"
+ length="20"/>
+ <property name="salary"/>
+ <many-to-one name="manager"/>
+ </subclass>
+
+ <subclass name="Customer"
+ discriminator-value="C">
+ <property name="comments"/>
+ <many-to-one name="salesperson"/>
+ </subclass>
+
+</class>]]></programlisting>
+ </sect2>
+
+ <sect2 id="example-mappings-association-alternatekeys" revision="2">
+ <title>Associations on alternate keys</title>
+<programlisting><![CDATA[<class name="Person">
+
+ <id name="id">
+ <generator class="hilo"/>
+ </id>
+
+ <property name="name" length="100"/>
+
+ <one-to-one name="address"
+ property-ref="person"
+ cascade="all"
+ fetch="join"/>
+
+ <set name="accounts"
+ inverse="true">
+ <key column="userId"
+ property-ref="userId"/>
+ <one-to-many class="Account"/>
+ </set>
+
+ <property name="userId" length="8"/>
+
+</class>
+
+<class name="Address">
+
+ <id name="id">
+ <generator class="hilo"/>
+ </id>
+
+ <property name="address" length="300"/>
+ <property name="zip" length="5"/>
+ <property name="country" length="25"/>
+ <many-to-one name="person" unique="true" not-null="true"/>
+
+</class>
+
+<class name="Account">
+ <id name="accountId" length="32">
+ <generator class="uuid"/>
+ </id>
+
+ <many-to-one name="user"
+ column="userId"
+ property-ref="userId"/>
+
+ <property name="type" not-null="true"/>
+
+</class>]]></programlisting>
+ </sect2>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/example_parentchild.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/example_parentchild.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/example_parentchild.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,361 @@
+<chapter id="example-parentchild">
+ <title>Example: Parent/Child</title>
+
+ <para>
+ One of the very first things that new users try to do with Hibernate is to model a parent / child type
+ relationship. There are two different approaches to this. For various reasons the most convenient
+ approach, especially for new users, is to model both <literal>Parent</literal> and <literal>Child</literal>
+ as entity classes with a <literal><one-to-many></literal> association from <literal>Parent</literal>
+ to <literal>Child</literal>. (The alternative approach is to declare the <literal>Child</literal> as a
+ <literal><composite-element></literal>.) Now, it turns out that default semantics of a one to many
+ association (in Hibernate) are much less close to the usual semantics of a parent / child relationship than
+ those of a composite element mapping. We will explain how to use a <emphasis>bidirectional one to many
+ association with cascades</emphasis> to model a parent / child relationship efficiently and elegantly.
+ It's not at all difficult!
+ </para>
+
+ <sect1 id="example-parentchild-collections">
+ <title>A note about collections</title>
+
+ <para>
+ Hibernate collections are considered to be a logical part of their owning entity; never of the
+ contained entities. This is a crucial distinction! It has the following consequences:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ When we remove / add an object from / to a collection, the version number of the collection owner
+ is incremented.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ If an object that was removed from a collection is an instance of a value type (eg, a composite
+ element), that object will cease to be persistent and its state will be completely removed from
+ the database. Likewise, adding a value type instance to the collection will cause its state to be
+ immediately persistent.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ On the other hand, if an entity is removed from a collection (a one-to-many or many-to-many
+ association), it will not be deleted, by default. This behaviour is completely consistent - a
+ change to the internal state of another entity should not cause the associated entity to vanish!
+ Likewise, adding an entity to a collection does not cause that entity to become persistent, by
+ default.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Instead, the default behaviour is that adding an entity to a collection merely creates a link between
+ the two entities, while removing it removes the link. This is very appropriate for all sorts of cases.
+ Where it is not appropriate at all is the case of a parent / child relationship, where the life of the
+ child is bound to the lifecycle of the parent.
+ </para>
+
+ </sect1>
+
+ <sect1 id="example-parentchild-bidir">
+ <title>Bidirectional one-to-many</title>
+
+ <para>
+ Suppose we start with a simple <literal><one-to-many></literal> association from
+ <literal>Parent</literal> to <literal>Child</literal>.
+ </para>
+
+ <programlisting><![CDATA[<set name="children">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ If we were to execute the following code
+ </para>
+
+ <programlisting><![CDATA[Parent p = .....;
+Child c = new Child();
+p.getChildren().add(c);
+session.save(c);
+session.flush();]]></programlisting>
+
+ <para>
+ Hibernate would issue two SQL statements:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>an <literal>INSERT</literal> to create the record for <literal>c</literal></para>
+ </listitem>
+ <listitem>
+ <para>
+ an <literal>UPDATE</literal> to create the link from <literal>p</literal> to
+ <literal>c</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ This is not only inefficient, but also violates any <literal>NOT NULL</literal> constraint on the
+ <literal>parent_id</literal> column. We can fix the nullability constraint violation by specifying
+ <literal>not-null="true"</literal> in the collection mapping:
+ </para>
+
+ <programlisting><![CDATA[<set name="children">
+ <key column="parent_id" not-null="true"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ However, this is not the recommended solution.
+ </para>
+ <para>
+ The underlying cause of this behaviour is that the link (the foreign key <literal>parent_id</literal>)
+ from <literal>p</literal> to <literal>c</literal> is not considered part of the state of the
+ <literal>Child</literal> object and is therefore not created in the <literal>INSERT</literal>. So the
+ solution is to make the link part of the <literal>Child</literal> mapping.
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="parent" column="parent_id" not-null="true"/>]]></programlisting>
+
+ <para>
+ (We also need to add the <literal>parent</literal> property to the <literal>Child</literal> class.)
+ </para>
+
+ <para>
+ Now that the <literal>Child</literal> entity is managing the state of the link, we tell the collection
+ not to update the link. We use the <literal>inverse</literal> attribute.
+ </para>
+
+ <programlisting><![CDATA[<set name="children" inverse="true">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ The following code would be used to add a new <literal>Child</literal>
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+Child c = new Child();
+c.setParent(p);
+p.getChildren().add(c);
+session.save(c);
+session.flush();]]></programlisting>
+
+ <para>
+ And now, only one SQL <literal>INSERT</literal> would be issued!
+ </para>
+
+ <para>
+ To tighten things up a bit, we could create an <literal>addChild()</literal> method of
+ <literal>Parent</literal>.
+ </para>
+
+ <programlisting><![CDATA[public void addChild(Child c) {
+ c.setParent(this);
+ children.add(c);
+}]]></programlisting>
+
+ <para>
+ Now, the code to add a <literal>Child</literal> looks like
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+Child c = new Child();
+p.addChild(c);
+session.save(c);
+session.flush();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-parentchild-cascades">
+ <title>Cascading lifecycle</title>
+
+ <para>
+ The explicit call to <literal>save()</literal> is still annoying. We will address this by
+ using cascades.
+ </para>
+
+ <programlisting><![CDATA[<set name="children" inverse="true" cascade="all">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ This simplifies the code above to
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+Child c = new Child();
+p.addChild(c);
+session.flush();]]></programlisting>
+
+ <para>
+ Similarly, we don't need to iterate over the children when saving or deleting a <literal>Parent</literal>.
+ The following removes <literal>p</literal> and all its children from the database.
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+session.delete(p);
+session.flush();]]></programlisting>
+
+ <para>
+ However, this code
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+Child c = (Child) p.getChildren().iterator().next();
+p.getChildren().remove(c);
+c.setParent(null);
+session.flush();]]></programlisting>
+
+ <para>
+ will not remove <literal>c</literal> from the database; it will ony remove the link to <literal>p</literal>
+ (and cause a <literal>NOT NULL</literal> constraint violation, in this case). You need to explicitly
+ <literal>delete()</literal> the <literal>Child</literal>.
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+Child c = (Child) p.getChildren().iterator().next();
+p.getChildren().remove(c);
+session.delete(c);
+session.flush();]]></programlisting>
+
+ <para>
+ Now, in our case, a <literal>Child</literal> can't really exist without its parent. So if we remove
+ a <literal>Child</literal> from the collection, we really do want it to be deleted. For this, we must
+ use <literal>cascade="all-delete-orphan"</literal>.
+ </para>
+
+ <programlisting><![CDATA[<set name="children" inverse="true" cascade="all-delete-orphan">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ Note: even though the collection mapping specifies <literal>inverse="true"</literal>, cascades are
+ still processed by iterating the collection elements. So if you require that an object be saved,
+ deleted or updated by cascade, you must add it to the collection. It is not enough to simply call
+ <literal>setParent()</literal>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="example-parentchild-update">
+ <title>Cascades and <literal>unsaved-value</literal></title>
+
+ <para>
+ Suppose we loaded up a <literal>Parent</literal> in one <literal>Session</literal>, made some changes
+ in a UI action and wish to persist these changes in a new session by calling <literal>update()</literal>.
+ The <literal>Parent</literal> will contain a collection of childen and, since cascading update is enabled,
+ Hibernate needs to know which children are newly instantiated and which represent existing rows in the
+ database. Lets assume that both <literal>Parent</literal> and <literal>Child</literal> have genenerated
+ identifier properties of type <literal>Long</literal>. Hibernate will use the identifier and
+ version/timestamp property value to determine which of the children are new. (See
+ <xref linkend="objectstate-saveorupdate"/>.) <emphasis>In Hibernate3, it is no longer necessary to specify
+ an <literal>unsaved-value</literal> explicitly.</emphasis>
+ </para>
+
+ <para>
+ The following code will update <literal>parent</literal> and <literal>child</literal> and insert
+ <literal>newChild</literal>.
+ </para>
+
+ <programlisting><![CDATA[//parent and child were both loaded in a previous session
+parent.addChild(child);
+Child newChild = new Child();
+parent.addChild(newChild);
+session.update(parent);
+session.flush();]]></programlisting>
+
+ <para>
+ Well, that's all very well for the case of a generated identifier, but what about assigned identifiers
+ and composite identifiers? This is more difficult, since Hibernate can't use the identifier property to
+ distinguish between a newly instantiated object (with an identifier assigned by the user) and an
+ object loaded in a previous session. In this case, Hibernate will either use the timestamp or version
+ property, or will actually query the second-level cache or, worst case, the database, to see if the
+ row exists.
+ </para>
+
+ <!-- undocumenting
+ <para>
+ There is one further possibility. The <literal>Interceptor</literal> method named
+ <literal>isUnsaved()</literal> lets the application implement its own strategy for distinguishing
+ newly instantiated objects. For example, you could define a base class for your persistent classes.
+ </para>
+
+ <programlisting><![CDATA[public class Persistent {
+ private boolean _saved = false;
+ public void onSave() {
+ _saved=true;
+ }
+ public void onLoad() {
+ _saved=true;
+ }
+ ......
+ public boolean isSaved() {
+ return _saved;
+ }
+}]]></programlisting>
+
+ <para>
+ (The <literal>saved</literal> property is non-persistent.)
+ Now implement <literal>isUnsaved()</literal>, along with <literal>onLoad()</literal>
+ and <literal>onSave()</literal> as follows.
+ </para>
+
+ <programlisting><![CDATA[public Boolean isUnsaved(Object entity) {
+ if (entity instanceof Persistent) {
+ return new Boolean( !( (Persistent) entity ).isSaved() );
+ }
+ else {
+ return null;
+ }
+}
+
+public boolean onLoad(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+
+ if (entity instanceof Persistent) ( (Persistent) entity ).onLoad();
+ return false;
+}
+
+public boolean onSave(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+
+ if (entity instanceof Persistent) ( (Persistent) entity ).onSave();
+ return false;
+}]]></programlisting>
+
+ <para>
+ Don't worry; in Hibernate3 you don't need to write any of this kind of code if you don't want to.
+ </para>
+ -->
+ </sect1>
+
+ <sect1 id="example-parentchild-conclusion">
+ <title>Conclusion</title>
+
+ <para>
+ There is quite a bit to digest here and it might look confusing first time around. However, in practice,
+ it all works out very nicely. Most Hibernate applications use the parent / child pattern in many places.
+ </para>
+
+ <para>
+ We mentioned an alternative in the first paragraph. None of the above issues exist in the case of
+ <literal><composite-element></literal> mappings, which have exactly the semantics of a parent / child
+ relationship. Unfortunately, there are two big limitations to composite element classes: composite elements
+ may not own collections, and they should not be the child of any entity other than the unique parent.
+ </para>
+
+ </sect1>
+
+</chapter>
\ No newline at end of file
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/example_weblog.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/example_weblog.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/example_weblog.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,430 @@
+<chapter id="example-weblog">
+ <title>Example: Weblog Application</title>
+
+ <sect1 id="example-weblog-classes">
+ <title>Persistent Classes</title>
+
+ <para>
+ The persistent classes represent a weblog, and an item posted
+ in a weblog. They are to be modelled as a standard parent/child
+ relationship, but we will use an ordered bag, instead of a set.
+ </para>
+
+ <programlisting><![CDATA[package eg;
+
+import java.util.List;
+
+public class Blog {
+ private Long _id;
+ private String _name;
+ private List _items;
+
+ public Long getId() {
+ return _id;
+ }
+ public List getItems() {
+ return _items;
+ }
+ public String getName() {
+ return _name;
+ }
+ public void setId(Long long1) {
+ _id = long1;
+ }
+ public void setItems(List list) {
+ _items = list;
+ }
+ public void setName(String string) {
+ _name = string;
+ }
+}]]></programlisting>
+
+ <programlisting><![CDATA[package eg;
+
+import java.text.DateFormat;
+import java.util.Calendar;
+
+public class BlogItem {
+ private Long _id;
+ private Calendar _datetime;
+ private String _text;
+ private String _title;
+ private Blog _blog;
+
+ public Blog getBlog() {
+ return _blog;
+ }
+ public Calendar getDatetime() {
+ return _datetime;
+ }
+ public Long getId() {
+ return _id;
+ }
+ public String getText() {
+ return _text;
+ }
+ public String getTitle() {
+ return _title;
+ }
+ public void setBlog(Blog blog) {
+ _blog = blog;
+ }
+ public void setDatetime(Calendar calendar) {
+ _datetime = calendar;
+ }
+ public void setId(Long long1) {
+ _id = long1;
+ }
+ public void setText(String string) {
+ _text = string;
+ }
+ public void setTitle(String string) {
+ _title = string;
+ }
+}]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-weblog-mappings">
+ <title>Hibernate Mappings</title>
+
+ <para>
+ The XML mappings should now be quite straightforward.
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="eg">
+
+ <class
+ name="Blog"
+ table="BLOGS">
+
+ <id
+ name="id"
+ column="BLOG_ID">
+
+ <generator class="native"/>
+
+ </id>
+
+ <property
+ name="name"
+ column="NAME"
+ not-null="true"
+ unique="true"/>
+
+ <bag
+ name="items"
+ inverse="true"
+ order-by="DATE_TIME"
+ cascade="all">
+
+ <key column="BLOG_ID"/>
+ <one-to-many class="BlogItem"/>
+
+ </bag>
+
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="eg">
+
+ <class
+ name="BlogItem"
+ table="BLOG_ITEMS"
+ dynamic-update="true">
+
+ <id
+ name="id"
+ column="BLOG_ITEM_ID">
+
+ <generator class="native"/>
+
+ </id>
+
+ <property
+ name="title"
+ column="TITLE"
+ not-null="true"/>
+
+ <property
+ name="text"
+ column="TEXT"
+ not-null="true"/>
+
+ <property
+ name="datetime"
+ column="DATE_TIME"
+ not-null="true"/>
+
+ <many-to-one
+ name="blog"
+ column="BLOG_ID"
+ not-null="true"/>
+
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-weblog-code">
+ <title>Hibernate Code</title>
+
+ <para>
+ The following class demonstrates some of the kinds of things
+ we can do with these classes, using Hibernate.
+ </para>
+
+ <programlisting><![CDATA[package eg;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Iterator;
+import java.util.List;
+
+import org.hibernate.HibernateException;
+import org.hibernate.Query;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.tool.hbm2ddl.SchemaExport;
+
+public class BlogMain {
+
+ private SessionFactory _sessions;
+
+ public void configure() throws HibernateException {
+ _sessions = new Configuration()
+ .addClass(Blog.class)
+ .addClass(BlogItem.class)
+ .buildSessionFactory();
+ }
+
+ public void exportTables() throws HibernateException {
+ Configuration cfg = new Configuration()
+ .addClass(Blog.class)
+ .addClass(BlogItem.class);
+ new SchemaExport(cfg).create(true, true);
+ }
+
+ public Blog createBlog(String name) throws HibernateException {
+
+ Blog blog = new Blog();
+ blog.setName(name);
+ blog.setItems( new ArrayList() );
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ session.persist(blog);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return blog;
+ }
+
+ public BlogItem createBlogItem(Blog blog, String title, String text)
+ throws HibernateException {
+
+ BlogItem item = new BlogItem();
+ item.setTitle(title);
+ item.setText(text);
+ item.setBlog(blog);
+ item.setDatetime( Calendar.getInstance() );
+ blog.getItems().add(item);
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ session.update(blog);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return item;
+ }
+
+ public BlogItem createBlogItem(Long blogid, String title, String text)
+ throws HibernateException {
+
+ BlogItem item = new BlogItem();
+ item.setTitle(title);
+ item.setText(text);
+ item.setDatetime( Calendar.getInstance() );
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ Blog blog = (Blog) session.load(Blog.class, blogid);
+ item.setBlog(blog);
+ blog.getItems().add(item);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return item;
+ }
+
+ public void updateBlogItem(BlogItem item, String text)
+ throws HibernateException {
+
+ item.setText(text);
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ session.update(item);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ }
+
+ public void updateBlogItem(Long itemid, String text)
+ throws HibernateException {
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ BlogItem item = (BlogItem) session.load(BlogItem.class, itemid);
+ item.setText(text);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ }
+
+ public List listAllBlogNamesAndItemCounts(int max)
+ throws HibernateException {
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ List result = null;
+ try {
+ tx = session.beginTransaction();
+ Query q = session.createQuery(
+ "select blog.id, blog.name, count(blogItem) " +
+ "from Blog as blog " +
+ "left outer join blog.items as blogItem " +
+ "group by blog.name, blog.id " +
+ "order by max(blogItem.datetime)"
+ );
+ q.setMaxResults(max);
+ result = q.list();
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return result;
+ }
+
+ public Blog getBlogAndAllItems(Long blogid)
+ throws HibernateException {
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ Blog blog = null;
+ try {
+ tx = session.beginTransaction();
+ Query q = session.createQuery(
+ "from Blog as blog " +
+ "left outer join fetch blog.items " +
+ "where blog.id = :blogid"
+ );
+ q.setParameter("blogid", blogid);
+ blog = (Blog) q.uniqueResult();
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return blog;
+ }
+
+ public List listBlogsAndRecentItems() throws HibernateException {
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ List result = null;
+ try {
+ tx = session.beginTransaction();
+ Query q = session.createQuery(
+ "from Blog as blog " +
+ "inner join blog.items as blogItem " +
+ "where blogItem.datetime > :minDate"
+ );
+
+ Calendar cal = Calendar.getInstance();
+ cal.roll(Calendar.MONTH, false);
+ q.setCalendar("minDate", cal);
+
+ result = q.list();
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return result;
+ }
+}]]></programlisting>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/filters.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/filters.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/filters.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,146 @@
+<chapter id="filters">
+ <title>Filtering data</title>
+
+ <para>
+ Hibernate3 provides an innovative new approach to handling data with "visibility" rules.
+ A <emphasis>Hibernate filter</emphasis> is a global, named, parameterized filter that may be
+ enabled or disabled for a particular Hibernate session.
+ </para>
+
+ <sect1 id="objectstate-filters" revision="1">
+ <title>Hibernate filters</title>
+
+ <para>
+ Hibernate3 adds the ability to pre-define filter criteria and attach those filters at both
+ a class and a collection level. A filter criteria is the ability to define a restriction clause
+ very similiar to the existing "where" attribute available on the class and various collection
+ elements. Except these filter conditions can be parameterized. The application can then make
+ the decision at runtime whether given filters should be enabled and what their parameter
+ values should be. Filters can be used like database views, but parameterized inside the
+ application.
+ </para>
+
+ <para>
+ In order to use filters, they must first be defined and then attached to the appropriate
+ mapping elements. To define a filter, use the <literal><filter-def/></literal> element
+ within a <literal><hibernate-mapping/></literal> element:
+ </para>
+
+ <programlisting><![CDATA[<filter-def name="myFilter">
+ <filter-param name="myFilterParam" type="string"/>
+</filter-def>]]></programlisting>
+
+ <para>
+ Then, this filter can be attached to a class:
+ </para>
+
+ <programlisting><![CDATA[<class name="myClass" ...>
+ ...
+ <filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
+</class>]]></programlisting>
+
+ <para>
+ or, to a collection:
+ </para>
+
+ <programlisting><![CDATA[<set ...>
+ <filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
+</set>]]></programlisting>
+
+ <para>
+ or, even to both (or multiples of each) at the same time.
+ </para>
+
+ <para>
+ The methods on <literal>Session</literal> are: <literal>enableFilter(String filterName)</literal>,
+ <literal>getEnabledFilter(String filterName)</literal>, and <literal>disableFilter(String filterName)</literal>.
+ By default, filters are <emphasis>not</emphasis> enabled for a given session; they must be explcitly
+ enabled through use of the <literal>Session.enabledFilter()</literal> method, which returns an
+ instance of the <literal>Filter</literal> interface. Using the simple filter defined above, this
+ would look like:
+ </para>
+
+ <programlisting><![CDATA[session.enableFilter("myFilter").setParameter("myFilterParam", "some-value");]]></programlisting>
+
+ <para>
+ Note that methods on the org.hibernate.Filter interface do allow the method-chaining common to much of Hibernate.
+ </para>
+
+ <para>
+ A full example, using temporal data with an effective record date pattern:
+ </para>
+
+ <programlisting><![CDATA[<filter-def name="effectiveDate">
+ <filter-param name="asOfDate" type="date"/>
+</filter-def>
+
+<class name="Employee" ...>
+...
+ <many-to-one name="department" column="dept_id" class="Department"/>
+ <property name="effectiveStartDate" type="date" column="eff_start_dt"/>
+ <property name="effectiveEndDate" type="date" column="eff_end_dt"/>
+...
+ <!--
+ Note that this assumes non-terminal records have an eff_end_dt set to
+ a max db date for simplicity-sake
+ -->
+ <filter name="effectiveDate"
+ condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
+</class>
+
+<class name="Department" ...>
+...
+ <set name="employees" lazy="true">
+ <key column="dept_id"/>
+ <one-to-many class="Employee"/>
+ <filter name="effectiveDate"
+ condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ Then, in order to ensure that you always get back currently effective records, simply
+ enable the filter on the session prior to retrieving employee data:
+ </para>
+
+<programlisting><![CDATA[Session session = ...;
+session.enabledFilter("effectiveDate").setParameter("asOfDate", new Date());
+List results = session.createQuery("from Employee as e where e.salary > :targetSalary")
+ .setLong("targetSalary", new Long(1000000))
+ .list();
+]]></programlisting>
+
+ <para>
+ In the HQL above, even though we only explicitly mentioned a salary constraint on the results,
+ because of the enabled filter the query will return only currently active employees who have
+ a salary greater than a million dollars.
+ </para>
+
+ <para>
+ Note: if you plan on using filters with outer joining (either through HQL or load fetching) be
+ careful of the direction of the condition expression. Its safest to set this up for left
+ outer joining; in general, place the parameter first followed by the column name(s) after
+ the operator.
+ </para>
+
+ <para>
+ After being defined a filter might be attached to multiple entities and/or
+ collections each with its own condition. That can be tedious when the
+ conditions are the same each time. Thus <literal><filter-def/></literal>
+ allows defining a default condition, either as an attribute or CDATA:
+ </para>
+
+ <programlisting><![CDATA[<filter-def name="myFilter" condition="abc > xyz">...</filter-def>
+<filter-def name="myOtherFilter">abc=xyz</filter-def>]]></programlisting>
+
+ <para>
+ This default condition will then be used whenever the filter is attached to something
+ without specifying a condition. Note that this means you can give a specific condition
+ as part of the attachment of the filter which overrides the default condition in that
+ particular case.
+ </para>
+
+ </sect1>
+
+</chapter>
+
Property changes on: trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/filters.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/inheritance_mapping.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/inheritance_mapping.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/inheritance_mapping.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,491 @@
+<chapter id="inheritance">
+ <title>Inheritance Mapping</title>
+
+ <sect1 id="inheritance-strategies" revision="3">
+ <title>The Three Strategies</title>
+
+ <para>
+ Hibernate supports the three basic inheritance mapping strategies:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ table per class hierarchy
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ table per subclass
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ table per concrete class
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ In addition, Hibernate supports a fourth, slightly different kind of
+ polymorphism:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ implicit polymorphism
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ It is possible to use different mapping strategies for different
+ branches of the same inheritance hierarchy, and then make use of implicit
+ polymorphism to achieve polymorphism across the whole hierarchy. However,
+ Hibernate does not support mixing <literal><subclass></literal>,
+ and <literal><joined-subclass></literal> and
+ <literal><union-subclass></literal> mappings under the same root
+ <literal><class></literal> element. It is possible to mix together
+ the table per hierarchy and table per subclass strategies, under the
+ the same <literal><class></literal> element, by combining the
+ <literal><subclass></literal> and <literal><join></literal>
+ elements (see below).
+ </para>
+
+ <para>
+ It is possible to define <literal>subclass</literal>, <literal>union-subclass</literal>,
+ and <literal>joined-subclass</literal> mappings in separate mapping documents, directly beneath
+ <literal>hibernate-mapping</literal>. This allows you to extend a class hierachy just by adding
+ a new mapping file. You must specify an <literal>extends</literal> attribute in the subclass mapping,
+ naming a previously mapped superclass. Note: Previously this feature made the ordering of the mapping
+ documents important. Since Hibernate3, the ordering of mapping files does not matter when using the
+ extends keyword. The ordering inside a single mapping file still needs to be defined as superclasses
+ before subclasses.
+ </para>
+
+ <programlisting><![CDATA[
+ <hibernate-mapping>
+ <subclass name="DomesticCat" extends="Cat" discriminator-value="D">
+ <property name="name" type="string"/>
+ </subclass>
+ </hibernate-mapping>]]></programlisting>
+
+
+ <sect2 id="inheritance-tableperclass" >
+ <title>Table per class hierarchy</title>
+
+ <para>
+ Suppose we have an interface <literal>Payment</literal>, with implementors
+ <literal>CreditCardPayment</literal>, <literal>CashPayment</literal>,
+ <literal>ChequePayment</literal>. The table per hierarchy mapping would
+ look like:
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment" table="PAYMENT">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <discriminator column="PAYMENT_TYPE" type="string"/>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <subclass name="CreditCardPayment" discriminator-value="CREDIT">
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </subclass>
+ <subclass name="CashPayment" discriminator-value="CASH">
+ ...
+ </subclass>
+ <subclass name="ChequePayment" discriminator-value="CHEQUE">
+ ...
+ </subclass>
+</class>]]></programlisting>
+
+ <para>
+ Exactly one table is required. There is one big limitation of this mapping
+ strategy: columns declared by the subclasses, such as <literal>CCTYPE</literal>,
+ may not have <literal>NOT NULL</literal> constraints.
+ </para>
+
+ </sect2>
+
+ <sect2 id="inheritance-tablepersubclass">
+ <title>Table per subclass</title>
+
+ <para>
+ A table per subclass mapping would look like:
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment" table="PAYMENT">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <joined-subclass name="CreditCardPayment" table="CREDIT_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </joined-subclass>
+ <joined-subclass name="CashPayment" table="CASH_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ ...
+ </joined-subclass>
+ <joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ ...
+ </joined-subclass>
+</class>]]></programlisting>
+
+ <para>
+ Four tables are required. The three subclass tables have primary
+ key associations to the superclass table (so the relational model
+ is actually a one-to-one association).
+ </para>
+
+ </sect2>
+
+ <sect2 id="inheritance-tablepersubclass-discriminator" revision="2">
+ <title>Table per subclass, using a discriminator</title>
+
+ <para>
+ Note that Hibernate's implementation of table per subclass requires
+ no discriminator column. Other object/relational mappers use a
+ different implementation of table per subclass which requires a type
+ discriminator column in the superclass table. The approach taken by
+ Hibernate is much more difficult to implement but arguably more
+ correct from a relational point of view. If you would like to use
+ a discriminator column with the table per subclass strategy, you
+ may combine the use of <literal><subclass></literal> and
+ <literal><join></literal>, as follow:
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment" table="PAYMENT">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <discriminator column="PAYMENT_TYPE" type="string"/>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <subclass name="CreditCardPayment" discriminator-value="CREDIT">
+ <join table="CREDIT_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </join>
+ </subclass>
+ <subclass name="CashPayment" discriminator-value="CASH">
+ <join table="CASH_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ ...
+ </join>
+ </subclass>
+ <subclass name="ChequePayment" discriminator-value="CHEQUE">
+ <join table="CHEQUE_PAYMENT" fetch="select">
+ <key column="PAYMENT_ID"/>
+ ...
+ </join>
+ </subclass>
+</class>]]></programlisting>
+
+ <para>
+ The optional <literal>fetch="select"</literal> declaration tells Hibernate
+ not to fetch the <literal>ChequePayment</literal> subclass data using an
+ outer join when querying the superclass.
+ </para>
+
+ </sect2>
+
+ <sect2 id="inheritance-mixing-tableperclass-tablepersubclass">
+ <title>Mixing table per class hierarchy with table per subclass</title>
+
+ <para>
+ You may even mix the table per hierarchy and table per subclass strategies
+ using this approach:
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment" table="PAYMENT">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <discriminator column="PAYMENT_TYPE" type="string"/>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <subclass name="CreditCardPayment" discriminator-value="CREDIT">
+ <join table="CREDIT_PAYMENT">
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </join>
+ </subclass>
+ <subclass name="CashPayment" discriminator-value="CASH">
+ ...
+ </subclass>
+ <subclass name="ChequePayment" discriminator-value="CHEQUE">
+ ...
+ </subclass>
+</class>]]></programlisting>
+
+ <para>
+ For any of these mapping strategies, a polymorphic association to the root
+ <literal>Payment</literal> class is mapped using
+ <literal><many-to-one></literal>.
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="payment" column="PAYMENT_ID" class="Payment"/>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="inheritance-tableperconcrete" revision="2">
+ <title>Table per concrete class</title>
+
+ <para>
+ There are two ways we could go about mapping the table per concrete class
+ strategy. The first is to use <literal><union-subclass></literal>.
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="sequence"/>
+ </id>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <union-subclass name="CreditCardPayment" table="CREDIT_PAYMENT">
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </union-subclass>
+ <union-subclass name="CashPayment" table="CASH_PAYMENT">
+ ...
+ </union-subclass>
+ <union-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
+ ...
+ </union-subclass>
+</class>]]></programlisting>
+
+ <para>
+ Three tables are involved for the subclasses. Each table defines columns for
+ all properties of the class, including inherited properties.
+ </para>
+
+ <para>
+ The limitation of this approach is that if a property is mapped on the
+ superclass, the column name must be the same on all subclass tables.
+ (We might relax this in a future release of Hibernate.) The identity
+ generator strategy is not allowed in union subclass inheritance, indeed
+ the primary key seed has to be shared accross all unioned subclasses
+ of a hierarchy.
+ </para>
+
+ <para>
+ If your superclass is abstract, map it with <literal>abstract="true"</literal>.
+ Of course, if it is not abstract, an additional table (defaults to
+ <literal>PAYMENT</literal> in the example above) is needed to hold instances
+ of the superclass.
+ </para>
+
+ </sect2>
+
+ <sect2 id="inheritance-tableperconcreate-polymorphism">
+ <title>Table per concrete class, using implicit polymorphism</title>
+
+ <para>
+ An alternative approach is to make use of implicit polymorphism:
+ </para>
+
+ <programlisting><![CDATA[<class name="CreditCardPayment" table="CREDIT_PAYMENT">
+ <id name="id" type="long" column="CREDIT_PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="amount" column="CREDIT_AMOUNT"/>
+ ...
+</class>
+
+<class name="CashPayment" table="CASH_PAYMENT">
+ <id name="id" type="long" column="CASH_PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="amount" column="CASH_AMOUNT"/>
+ ...
+</class>
+
+<class name="ChequePayment" table="CHEQUE_PAYMENT">
+ <id name="id" type="long" column="CHEQUE_PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="amount" column="CHEQUE_AMOUNT"/>
+ ...
+</class>]]></programlisting>
+
+ <para>
+ Notice that nowhere do we mention the <literal>Payment</literal> interface
+ explicitly. Also notice that properties of <literal>Payment</literal> are
+ mapped in each of the subclasses. If you want to avoid duplication, consider
+ using XML entities
+ (e.g. <literal>[ <!ENTITY allproperties SYSTEM "allproperties.xml"> ]</literal>
+ in the <literal>DOCTYPE</literal> declartion and
+ <literal>&allproperties;</literal> in the mapping).
+ </para>
+
+ <para>
+ The disadvantage of this approach is that Hibernate does not generate SQL
+ <literal>UNION</literal>s when performing polymorphic queries.
+ </para>
+
+ <para>
+ For this mapping strategy, a polymorphic association to <literal>Payment</literal>
+ is usually mapped using <literal><any></literal>.
+ </para>
+
+ <programlisting><![CDATA[<any name="payment" meta-type="string" id-type="long">
+ <meta-value value="CREDIT" class="CreditCardPayment"/>
+ <meta-value value="CASH" class="CashPayment"/>
+ <meta-value value="CHEQUE" class="ChequePayment"/>
+ <column name="PAYMENT_CLASS"/>
+ <column name="PAYMENT_ID"/>
+</any>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="inheritace-mixingpolymorphism">
+ <title>Mixing implicit polymorphism with other inheritance mappings</title>
+
+ <para>
+ There is one further thing to notice about this mapping. Since the subclasses
+ are each mapped in their own <literal><class></literal> element (and since
+ <literal>Payment</literal> is just an interface), each of the subclasses could
+ easily be part of another inheritance hierarchy! (And you can still use polymorphic
+ queries against the <literal>Payment</literal> interface.)
+ </para>
+
+ <programlisting><![CDATA[<class name="CreditCardPayment" table="CREDIT_PAYMENT">
+ <id name="id" type="long" column="CREDIT_PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <discriminator column="CREDIT_CARD" type="string"/>
+ <property name="amount" column="CREDIT_AMOUNT"/>
+ ...
+ <subclass name="MasterCardPayment" discriminator-value="MDC"/>
+ <subclass name="VisaPayment" discriminator-value="VISA"/>
+</class>
+
+<class name="NonelectronicTransaction" table="NONELECTRONIC_TXN">
+ <id name="id" type="long" column="TXN_ID">
+ <generator class="native"/>
+ </id>
+ ...
+ <joined-subclass name="CashPayment" table="CASH_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ <property name="amount" column="CASH_AMOUNT"/>
+ ...
+ </joined-subclass>
+ <joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ <property name="amount" column="CHEQUE_AMOUNT"/>
+ ...
+ </joined-subclass>
+</class>]]></programlisting>
+
+ <para>
+ Once again, we don't mention <literal>Payment</literal> explicitly. If we
+ execute a query against the <literal>Payment</literal> interface - for
+ example, <literal>from Payment</literal> - Hibernate
+ automatically returns instances of <literal>CreditCardPayment</literal>
+ (and its subclasses, since they also implement <literal>Payment</literal>),
+ <literal>CashPayment</literal> and <literal>ChequePayment</literal> but
+ not instances of <literal>NonelectronicTransaction</literal>.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="inheritance-limitations">
+ <title>Limitations</title>
+
+ <para>
+ There are certain limitations to the "implicit polymorphism" approach to
+ the table per concrete-class mapping strategy. There are somewhat less
+ restrictive limitations to <literal><union-subclass></literal>
+ mappings.
+ </para>
+
+ <para>
+ The following table shows the limitations of table per concrete-class
+ mappings, and of implicit polymorphism, in Hibernate.
+ </para>
+
+ <table frame="topbot">
+ <title>Features of inheritance mappings</title>
+ <tgroup cols='8' align='left' colsep='1' rowsep='1'>
+ <colspec colname='c1' colwidth="1*"/>
+ <colspec colname='c2' colwidth="1*"/>
+ <colspec colname='c3' colwidth="1*"/>
+ <colspec colname='c4' colwidth="1*"/>
+ <colspec colname='c5' colwidth="1*"/>
+ <colspec colname='c6' colwidth="1*"/>
+ <colspec colname='c7' colwidth="1*"/>
+ <colspec colname='c8' colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Inheritance strategy</entry>
+ <entry>Polymorphic many-to-one</entry>
+ <entry>Polymorphic one-to-one</entry>
+ <entry>Polymorphic one-to-many</entry>
+ <entry>Polymorphic many-to-many</entry>
+ <entry>Polymorphic <literal>load()/get()</literal></entry>
+ <entry>Polymorphic queries</entry>
+ <entry>Polymorphic joins</entry>
+ <entry>Outer join fetching</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>table per class-hierarchy</entry>
+ <entry><literal><many-to-one></literal></entry>
+ <entry><literal><one-to-one></literal></entry>
+ <entry><literal><one-to-many></literal></entry>
+ <entry><literal><many-to-many></literal></entry>
+ <entry><literal>s.get(Payment.class, id)</literal></entry>
+ <entry><literal>from Payment p</literal></entry>
+ <entry><literal>from Order o join o.payment p</literal></entry>
+ <entry><emphasis>supported</emphasis></entry>
+ </row>
+ <row>
+ <entry>table per subclass</entry>
+ <entry><literal><many-to-one></literal></entry>
+ <entry><literal><one-to-one></literal></entry>
+ <entry><literal><one-to-many></literal></entry>
+ <entry><literal><many-to-many></literal></entry>
+ <entry><literal>s.get(Payment.class, id)</literal></entry>
+ <entry><literal>from Payment p</literal></entry>
+ <entry><literal>from Order o join o.payment p</literal></entry>
+ <entry><emphasis>supported</emphasis></entry>
+ </row>
+ <row>
+ <entry>table per concrete-class (union-subclass)</entry>
+ <entry><literal><many-to-one></literal></entry>
+ <entry><literal><one-to-one></literal></entry>
+ <entry><literal><one-to-many></literal> (for <literal>inverse="true"</literal> only)</entry>
+ <entry><literal><many-to-many></literal></entry>
+ <entry><literal>s.get(Payment.class, id)</literal></entry>
+ <entry><literal>from Payment p</literal></entry>
+ <entry><literal>from Order o join o.payment p</literal></entry>
+ <entry><emphasis>supported</emphasis></entry>
+ </row>
+ <row>
+ <entry>table per concrete class (implicit polymorphism)</entry>
+ <entry><literal><any></literal></entry>
+ <entry><emphasis>not supported</emphasis></entry>
+ <entry><emphasis>not supported</emphasis></entry>
+ <entry><literal><many-to-any></literal></entry>
+ <entry><literal>s.createCriteria(Payment.class).add( Restrictions.idEq(id) ).uniqueResult()</literal></entry>
+ <entry><literal>from Payment p</literal></entry>
+ <entry><emphasis>not supported</emphasis></entry>
+ <entry><emphasis>not supported</emphasis></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect1>
+
+</chapter>
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/performance.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/performance.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/performance.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1388 @@
+<chapter id="performance">
+ <title>Improving performance</title>
+
+ <sect1 id="performance-fetching" revision="2">
+ <title>Fetching strategies</title>
+
+ <para>
+ A <emphasis>fetching strategy</emphasis> is the strategy Hibernate will use for
+ retrieving associated objects if the application needs to navigate the association.
+ Fetch strategies may be declared in the O/R mapping metadata, or over-ridden by a
+ particular HQL or <literal>Criteria</literal> query.
+ </para>
+
+ <para>
+ Hibernate3 defines the following fetching strategies:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Join fetching</emphasis> - Hibernate retrieves the
+ associated instance or collection in the same <literal>SELECT</literal>,
+ using an <literal>OUTER JOIN</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Select fetching</emphasis> - a second <literal>SELECT</literal>
+ is used to retrieve the associated entity or collection. Unless
+ you explicitly disable lazy fetching by specifying <literal>lazy="false"</literal>,
+ this second select will only be executed when you actually access the
+ association.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Subselect fetching</emphasis> - a second <literal>SELECT</literal>
+ is used to retrieve the associated collections for all entities retrieved in a
+ previous query or fetch. Unless you explicitly disable lazy fetching by specifying
+ <literal>lazy="false"</literal>, this second select will only be executed when you
+ actually access the association.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Batch fetching</emphasis> - an optimization strategy
+ for select fetching - Hibernate retrieves a batch of entity instances
+ or collections in a single <literal>SELECT</literal>, by specifying
+ a list of primary keys or foreign keys.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Hibernate also distinguishes between:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Immediate fetching</emphasis> - an association, collection or
+ attribute is fetched immediately, when the owner is loaded.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Lazy collection fetching</emphasis> - a collection is fetched
+ when the application invokes an operation upon that collection. (This
+ is the default for collections.)
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>"Extra-lazy" collection fetching</emphasis> - individual
+ elements of the collection are accessed from the database as needed.
+ Hibernate tries not to fetch the whole collection into memory unless
+ absolutely needed (suitable for very large collections)
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Proxy fetching</emphasis> - a single-valued association is
+ fetched when a method other than the identifier getter is invoked
+ upon the associated object.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>"No-proxy" fetching</emphasis> - a single-valued association is
+ fetched when the instance variable is accessed. Compared to proxy fetching,
+ this approach is less lazy (the association is fetched even when only the
+ identifier is accessed) but more transparent, since no proxy is visible to
+ the application. This approach requires buildtime bytecode instrumentation
+ and is rarely necessary.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Lazy attribute fetching</emphasis> - an attribute or single
+ valued association is fetched when the instance variable is accessed.
+ This approach requires buildtime bytecode instrumentation and is rarely
+ necessary.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ We have two orthogonal notions here: <emphasis>when</emphasis> is the association
+ fetched, and <emphasis>how</emphasis> is it fetched (what SQL is used). Don't
+ confuse them! We use <literal>fetch</literal> to tune performance. We may use
+ <literal>lazy</literal> to define a contract for what data is always available
+ in any detached instance of a particular class.
+ </para>
+
+ <sect2 id="performance-fetching-lazy">
+ <title>Working with lazy associations</title>
+
+ <para>
+ By default, Hibernate3 uses lazy select fetching for collections and lazy proxy
+ fetching for single-valued associations. These defaults make sense for almost
+ all associations in almost all applications.
+ </para>
+
+ <para>
+ <emphasis>Note:</emphasis> if you set
+ <literal>hibernate.default_batch_fetch_size</literal>, Hibernate will use the
+ batch fetch optimization for lazy fetching (this optimization may also be enabled
+ at a more granular level).
+ </para>
+
+ <para>
+ However, lazy fetching poses one problem that you must be aware of. Access to a
+ lazy association outside of the context of an open Hibernate session will result
+ in an exception. For example:
+ </para>
+
+ <programlisting><![CDATA[s = sessions.openSession();
+Transaction tx = s.beginTransaction();
+
+User u = (User) s.createQuery("from User u where u.name=:userName")
+ .setString("userName", userName).uniqueResult();
+Map permissions = u.getPermissions();
+
+tx.commit();
+s.close();
+
+Integer accessLevel = (Integer) permissions.get("accounts"); // Error!]]></programlisting>
+
+ <para>
+ Since the permissions collection was not initialized when the
+ <literal>Session</literal> was closed, the collection will not be able to
+ load its state. <emphasis>Hibernate does not support lazy initialization
+ for detached objects</emphasis>. The fix is to move the code that reads
+ from the collection to just before the transaction is committed.
+ </para>
+
+ <para>
+ Alternatively, we could use a non-lazy collection or association,
+ by specifying <literal>lazy="false"</literal> for the association mapping.
+ However, it is intended that lazy initialization be used for almost all
+ collections and associations. If you define too many non-lazy associations
+ in your object model, Hibernate will end up needing to fetch the entire
+ database into memory in every transaction!
+ </para>
+
+ <para>
+ On the other hand, we often want to choose join fetching (which is non-lazy by
+ nature) instead of select fetching in a particular transaction. We'll now see
+ how to customize the fetching strategy. In Hibernate3, the mechanisms for
+ choosing a fetch strategy are identical for single-valued associations and
+ collections.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-fetching-custom" revision="4">
+ <title>Tuning fetch strategies</title>
+
+ <para>
+ Select fetching (the default) is extremely vulnerable to N+1 selects problems,
+ so we might want to enable join fetching in the mapping document:
+ </para>
+
+ <programlisting><![CDATA[<set name="permissions"
+ fetch="join">
+ <key column="userId"/>
+ <one-to-many class="Permission"/>
+</set]]></programlisting>
+
+ <programlisting><![CDATA[<many-to-one name="mother" class="Cat" fetch="join"/>]]></programlisting>
+
+ <para>
+ The <literal>fetch</literal> strategy defined in the mapping document affects:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ retrieval via <literal>get()</literal> or <literal>load()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ retrieval that happens implicitly when an association is navigated
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>Criteria</literal> queries
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ HQL queries if <literal>subselect</literal> fetching is used
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ No matter what fetching strategy you use, the defined non-lazy graph is guaranteed
+ to be loaded into memory. Note that this might result in several immediate selects
+ being used to execute a particular HQL query.
+ </para>
+
+ <para>
+ Usually, we don't use the mapping document to customize fetching. Instead, we
+ keep the default behavior, and override it for a particular transaction, using
+ <literal>left join fetch</literal> in HQL. This tells Hibernate to fetch
+ the association eagerly in the first select, using an outer join. In the
+ <literal>Criteria</literal> query API, you would use
+ <literal>setFetchMode(FetchMode.JOIN)</literal>.
+ </para>
+
+ <para>
+ If you ever feel like you wish you could change the fetching strategy used by
+ <literal>get()</literal> or <literal>load()</literal>, simply use a
+ <literal>Criteria</literal> query, for example:
+ </para>
+
+ <programlisting><![CDATA[User user = (User) session.createCriteria(User.class)
+ .setFetchMode("permissions", FetchMode.JOIN)
+ .add( Restrictions.idEq(userId) )
+ .uniqueResult();]]></programlisting>
+
+ <para>
+ (This is Hibernate's equivalent of what some ORM solutions call a "fetch plan".)
+ </para>
+
+ <para>
+ A completely different way to avoid problems with N+1 selects is to use the
+ second-level cache.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-fetching-proxies" revision="2">
+ <title>Single-ended association proxies</title>
+
+ <para>
+ Lazy fetching for collections is implemented using Hibernate's own implementation
+ of persistent collections. However, a different mechanism is needed for lazy
+ behavior in single-ended associations. The target entity of the association must
+ be proxied. Hibernate implements lazy initializing proxies for persistent objects
+ using runtime bytecode enhancement (via the excellent CGLIB library).
+ </para>
+
+ <para>
+ By default, Hibernate3 generates proxies (at startup) for all persistent classes
+ and uses them to enable lazy fetching of <literal>many-to-one</literal> and
+ <literal>one-to-one</literal> associations.
+ </para>
+
+ <para>
+ The mapping file may declare an interface to use as the proxy interface for that
+ class, with the <literal>proxy</literal> attribute. By default, Hibernate uses a subclass
+ of the class. <emphasis>Note that the proxied class must implement a default constructor
+ with at least package visibility. We recommend this constructor for all persistent classes!</emphasis>
+ </para>
+
+ <para>
+ There are some gotchas to be aware of when extending this approach to polymorphic
+ classes, eg.
+ </para>
+
+ <programlisting><![CDATA[<class name="Cat" proxy="Cat">
+ ......
+ <subclass name="DomesticCat">
+ .....
+ </subclass>
+</class>]]></programlisting>
+
+ <para>
+ Firstly, instances of <literal>Cat</literal> will never be castable to
+ <literal>DomesticCat</literal>, even if the underlying instance is an
+ instance of <literal>DomesticCat</literal>:
+ </para>
+
+ <programlisting><![CDATA[Cat cat = (Cat) session.load(Cat.class, id); // instantiate a proxy (does not hit the db)
+if ( cat.isDomesticCat() ) { // hit the db to initialize the proxy
+ DomesticCat dc = (DomesticCat) cat; // Error!
+ ....
+}]]></programlisting>
+
+ <para>
+ Secondly, it is possible to break proxy <literal>==</literal>.
+ </para>
+
+ <programlisting><![CDATA[Cat cat = (Cat) session.load(Cat.class, id); // instantiate a Cat proxy
+DomesticCat dc =
+ (DomesticCat) session.load(DomesticCat.class, id); // acquire new DomesticCat proxy!
+System.out.println(cat==dc); // false]]></programlisting>
+
+ <para>
+ However, the situation is not quite as bad as it looks. Even though we now have two references
+ to different proxy objects, the underlying instance will still be the same object:
+ </para>
+
+ <programlisting><![CDATA[cat.setWeight(11.0); // hit the db to initialize the proxy
+System.out.println( dc.getWeight() ); // 11.0]]></programlisting>
+
+ <para>
+ Third, you may not use a CGLIB proxy for a <literal>final</literal> class or a class
+ with any <literal>final</literal> methods.
+ </para>
+
+ <para>
+ Finally, if your persistent object acquires any resources upon instantiation (eg. in
+ initializers or default constructor), then those resources will also be acquired by
+ the proxy. The proxy class is an actual subclass of the persistent class.
+ </para>
+
+ <para>
+ These problems are all due to fundamental limitations in Java's single inheritance model.
+ If you wish to avoid these problems your persistent classes must each implement an interface
+ that declares its business methods. You should specify these interfaces in the mapping file. eg.
+ </para>
+
+ <programlisting><![CDATA[<class name="CatImpl" proxy="Cat">
+ ......
+ <subclass name="DomesticCatImpl" proxy="DomesticCat">
+ .....
+ </subclass>
+</class>]]></programlisting>
+
+ <para>
+ where <literal>CatImpl</literal> implements the interface <literal>Cat</literal> and
+ <literal>DomesticCatImpl</literal> implements the interface <literal>DomesticCat</literal>. Then
+ proxies for instances of <literal>Cat</literal> and <literal>DomesticCat</literal> may be returned
+ by <literal>load()</literal> or <literal>iterate()</literal>. (Note that <literal>list()</literal>
+ does not usually return proxies.)
+ </para>
+
+ <programlisting><![CDATA[Cat cat = (Cat) session.load(CatImpl.class, catid);
+Iterator iter = session.iterate("from CatImpl as cat where cat.name='fritz'");
+Cat fritz = (Cat) iter.next();]]></programlisting>
+
+ <para>
+ Relationships are also lazily initialized. This means you must declare any properties to be of
+ type <literal>Cat</literal>, not <literal>CatImpl</literal>.
+ </para>
+
+ <para>
+ Certain operations do <emphasis>not</emphasis> require proxy initialization
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>equals()</literal>, if the persistent class does not override
+ <literal>equals()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>hashCode()</literal>, if the persistent class does not override
+ <literal>hashCode()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ The identifier getter method
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Hibernate will detect persistent classes that override <literal>equals()</literal> or
+ <literal>hashCode()</literal>.
+ </para>
+
+ <para>
+ By choosing <literal>lazy="no-proxy"</literal> instead of the default
+ <literal>lazy="proxy"</literal>, we can avoid the problems associated with typecasting.
+ However, we will require buildtime bytecode instrumentation, and all operations
+ will result in immediate proxy initialization.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-fetching-initialization" revision="1">
+ <title>Initializing collections and proxies</title>
+
+ <para>
+ A <literal>LazyInitializationException</literal> will be thrown by Hibernate if an uninitialized
+ collection or proxy is accessed outside of the scope of the <literal>Session</literal>, ie. when
+ the entity owning the collection or having the reference to the proxy is in the detached state.
+ </para>
+
+ <para>
+ Sometimes we need to ensure that a proxy or collection is initialized before closing the
+ <literal>Session</literal>. Of course, we can alway force initialization by calling
+ <literal>cat.getSex()</literal> or <literal>cat.getKittens().size()</literal>, for example.
+ But that is confusing to readers of the code and is not convenient for generic code.
+ </para>
+
+ <para>
+ The static methods <literal>Hibernate.initialize()</literal> and <literal>Hibernate.isInitialized()</literal>
+ provide the application with a convenient way of working with lazily initialized collections or
+ proxies. <literal>Hibernate.initialize(cat)</literal> will force the initialization of a proxy,
+ <literal>cat</literal>, as long as its <literal>Session</literal> is still open.
+ <literal>Hibernate.initialize( cat.getKittens() )</literal> has a similar effect for the collection
+ of kittens.
+ </para>
+
+ <para>
+ Another option is to keep the <literal>Session</literal> open until all needed
+ collections and proxies have been loaded. In some application architectures,
+ particularly where the code that accesses data using Hibernate, and the code that
+ uses it are in different application layers or different physical processes, it
+ can be a problem to ensure that the <literal>Session</literal> is open when a
+ collection is initialized. There are two basic ways to deal with this issue:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ In a web-based application, a servlet filter can be used to close the
+ <literal>Session</literal> only at the very end of a user request, once
+ the rendering of the view is complete (the <emphasis>Open Session in
+ View</emphasis> pattern). Of course, this places heavy demands on the
+ correctness of the exception handling of your application infrastructure.
+ It is vitally important that the <literal>Session</literal> is closed and the
+ transaction ended before returning to the user, even when an exception occurs
+ during rendering of the view. See the Hibernate Wiki for examples of this
+ "Open Session in View" pattern.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ In an application with a separate business tier, the business logic must
+ "prepare" all collections that will be needed by the web tier before
+ returning. This means that the business tier should load all the data and
+ return all the data already initialized to the presentation/web tier that
+ is required for a particular use case. Usually, the application calls
+ <literal>Hibernate.initialize()</literal> for each collection that will
+ be needed in the web tier (this call must occur before the session is closed)
+ or retrieves the collection eagerly using a Hibernate query with a
+ <literal>FETCH</literal> clause or a <literal>FetchMode.JOIN</literal> in
+ <literal>Criteria</literal>. This is usually easier if you adopt the
+ <emphasis>Command</emphasis> pattern instead of a <emphasis>Session Facade</emphasis>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ You may also attach a previously loaded object to a new <literal>Session</literal>
+ with <literal>merge()</literal> or <literal>lock()</literal> before
+ accessing uninitialized collections (or other proxies). No, Hibernate does not,
+ and certainly <emphasis>should</emphasis> not do this automatically, since it
+ would introduce ad hoc transaction semantics!
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Sometimes you don't want to initialize a large collection, but still need some
+ information about it (like its size) or a subset of the data.
+ </para>
+
+ <para>
+ You can use a collection filter to get the size of a collection without initializing it:
+ </para>
+
+ <programlisting><![CDATA[( (Integer) s.createFilter( collection, "select count(*)" ).list().get(0) ).intValue()]]></programlisting>
+
+ <para>
+ The <literal>createFilter()</literal> method is also used to efficiently retrieve subsets
+ of a collection without needing to initialize the whole collection:
+ </para>
+
+ <programlisting><![CDATA[s.createFilter( lazyCollection, "").setFirstResult(0).setMaxResults(10).list();]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="performance-fetching-batch">
+ <title>Using batch fetching</title>
+
+ <para>
+ Hibernate can make efficient use of batch fetching, that is, Hibernate can load several uninitialized
+ proxies if one proxy is accessed (or collections. Batch fetching is an optimization of the lazy select
+ fetching strategy. There are two ways you can tune batch fetching: on the class and the collection level.
+ </para>
+
+ <para>
+ Batch fetching for classes/entities is easier to understand. Imagine you have the following situation
+ at runtime: You have 25 <literal>Cat</literal> instances loaded in a <literal>Session</literal>, each
+ <literal>Cat</literal> has a reference to its <literal>owner</literal>, a <literal>Person</literal>.
+ The <literal>Person</literal> class is mapped with a proxy, <literal>lazy="true"</literal>. If you now
+ iterate through all cats and call <literal>getOwner()</literal> on each, Hibernate will by default
+ execute 25 <literal>SELECT</literal> statements, to retrieve the proxied owners. You can tune this
+ behavior by specifying a <literal>batch-size</literal> in the mapping of <literal>Person</literal>:
+ </para>
+
+ <programlisting><![CDATA[<class name="Person" batch-size="10">...</class>]]></programlisting>
+
+ <para>
+ Hibernate will now execute only three queries, the pattern is 10, 10, 5.
+ </para>
+
+ <para>
+ You may also enable batch fetching of collections. For example, if each <literal>Person</literal> has
+ a lazy collection of <literal>Cat</literal>s, and 10 persons are currently loaded in the
+ <literal>Sesssion</literal>, iterating through all persons will generate 10 <literal>SELECT</literal>s,
+ one for every call to <literal>getCats()</literal>. If you enable batch fetching for the
+ <literal>cats</literal> collection in the mapping of <literal>Person</literal>, Hibernate can pre-fetch
+ collections:
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <set name="cats" batch-size="3">
+ ...
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ With a <literal>batch-size</literal> of 8, Hibernate will load 3, 3, 3, 1 collections in four
+ <literal>SELECT</literal>s. Again, the value of the attribute depends on the expected number of
+ uninitialized collections in a particular <literal>Session</literal>.
+ </para>
+
+ <para>
+ Batch fetching of collections is particularly useful if you have a nested tree of items, ie.
+ the typical bill-of-materials pattern. (Although a <emphasis>nested set</emphasis> or a
+ <emphasis>materialized path</emphasis> might be a better option for read-mostly trees.)
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-fetching-subselect">
+ <title>Using subselect fetching</title>
+
+ <para>
+ If one lazy collection or single-valued proxy has to be fetched, Hibernate loads all of
+ them, re-running the original query in a subselect. This works in the same way as
+ batch-fetching, without the piecemeal loading.
+ </para>
+
+ <!-- TODO: Write more about this -->
+
+ </sect2>
+
+ <sect2 id="performance-fetching-lazyproperties">
+ <title>Using lazy property fetching</title>
+
+ <para>
+ Hibernate3 supports the lazy fetching of individual properties. This optimization technique
+ is also known as <emphasis>fetch groups</emphasis>. Please note that this is mostly a
+ marketing feature, as in practice, optimizing row reads is much more important than
+ optimization of column reads. However, only loading some properties of a class might
+ be useful in extreme cases, when legacy tables have hundreds of columns and the data model
+ can not be improved.
+ </para>
+
+ <para>
+ To enable lazy property loading, set the <literal>lazy</literal> attribute on your
+ particular property mappings:
+ </para>
+
+ <programlisting><![CDATA[<class name="Document">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="name" not-null="true" length="50"/>
+ <property name="summary" not-null="true" length="200" lazy="true"/>
+ <property name="text" not-null="true" length="2000" lazy="true"/>
+</class>]]></programlisting>
+
+ <para>
+ Lazy property loading requires buildtime bytecode instrumentation! If your persistent
+ classes are not enhanced, Hibernate will silently ignore lazy property settings and
+ fall back to immediate fetching.
+ </para>
+
+ <para>
+ For bytecode instrumentation, use the following Ant task:
+ </para>
+
+ <programlisting><![CDATA[<target name="instrument" depends="compile">
+ <taskdef name="instrument" classname="org.hibernate.tool.instrument.InstrumentTask">
+ <classpath path="${jar.path}"/>
+ <classpath path="${classes.dir}"/>
+ <classpath refid="lib.class.path"/>
+ </taskdef>
+
+ <instrument verbose="true">
+ <fileset dir="${testclasses.dir}/org/hibernate/auction/model">
+ <include name="*.class"/>
+ </fileset>
+ </instrument>
+</target>]]></programlisting>
+
+ <para>
+ A different (better?) way to avoid unnecessary column reads, at least for
+ read-only transactions is to use the projection features of HQL or Criteria
+ queries. This avoids the need for buildtime bytecode processing and is
+ certainly a prefered solution.
+ </para>
+
+ <para>
+ You may force the usual eager fetching of properties using <literal>fetch all
+ properties</literal> in HQL.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="performance-cache" revision="1">
+ <title>The Second Level Cache</title>
+
+ <para>
+ A Hibernate <literal>Session</literal> is a transaction-level cache of persistent data. It is
+ possible to configure a cluster or JVM-level (<literal>SessionFactory</literal>-level) cache on
+ a class-by-class and collection-by-collection basis. You may even plug in a clustered cache. Be
+ careful. Caches are never aware of changes made to the persistent store by another application
+ (though they may be configured to regularly expire cached data).
+ </para>
+
+ <para revision="1">
+ You have the option to tell Hibernate which caching implementation to use by
+ specifying the name of a class that implements <literal>org.hibernate.cache.CacheProvider</literal>
+ using the property <literal>hibernate.cache.provider_class</literal>. Hibernate
+ comes bundled with a number of built-in integrations with open-source cache providers
+ (listed below); additionally, you could implement your own and plug it in as
+ outlined above. Note that versions prior to 3.2 defaulted to use EhCache as the
+ default cache provider; that is no longer the case as of 3.2.
+ </para>
+
+ <table frame="topbot" id="cacheproviders" revision="1">
+ <title>Cache Providers</title>
+ <tgroup cols='5' align='left' colsep='1' rowsep='1'>
+ <colspec colname='c1' colwidth="1*"/>
+ <colspec colname='c2' colwidth="3*"/>
+ <colspec colname='c3' colwidth="1*"/>
+ <colspec colname='c4' colwidth="1*"/>
+ <colspec colname='c5' colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Cache</entry>
+ <entry>Provider class</entry>
+ <entry>Type</entry>
+ <entry>Cluster Safe</entry>
+ <entry>Query Cache Supported</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>Hashtable (not intended for production use)</entry>
+ <entry><literal>org.hibernate.cache.HashtableCacheProvider</literal></entry>
+ <entry>memory</entry>
+ <entry></entry>
+ <entry>yes</entry>
+ </row>
+ <row>
+ <entry>EHCache</entry>
+ <entry><literal>org.hibernate.cache.EhCacheProvider</literal></entry>
+ <entry>memory, disk</entry>
+ <entry></entry>
+ <entry>yes</entry>
+ </row>
+ <row>
+ <entry>OSCache</entry>
+ <entry><literal>org.hibernate.cache.OSCacheProvider</literal></entry>
+ <entry>memory, disk</entry>
+ <entry></entry>
+ <entry>yes</entry>
+ </row>
+ <row>
+ <entry>SwarmCache</entry>
+ <entry><literal>org.hibernate.cache.SwarmCacheProvider</literal></entry>
+ <entry>clustered (ip multicast)</entry>
+ <entry>yes (clustered invalidation)</entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>JBoss TreeCache</entry>
+ <entry><literal>org.hibernate.cache.TreeCacheProvider</literal></entry>
+ <entry>clustered (ip multicast), transactional</entry>
+ <entry>yes (replication)</entry>
+ <entry>yes (clock sync req.)</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <sect2 id="performance-cache-mapping" revision="2">
+ <title>Cache mappings</title>
+
+ <para>
+ The <literal><cache></literal> element of a class or collection mapping has the
+ following form:
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="cache1" coords="2 70"/>
+ <area id="cache2" coords="3 70"/>
+ <area id="cache3" coords="4 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<cache
+ usage="transactional|read-write|nonstrict-read-write|read-only"
+ region="RegionName"
+ include="all|non-lazy"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="cache1">
+ <para>
+ <literal>usage</literal> (required) specifies the caching strategy:
+ <literal>transactional</literal>,
+ <literal>read-write</literal>,
+ <literal>nonstrict-read-write</literal> or
+ <literal>read-only</literal>
+ </para>
+ </callout>
+ <callout arearefs="cache2">
+ <para>
+ <literal>region</literal> (optional, defaults to the class or
+ collection role name) specifies the name of the second level cache
+ region
+ </para>
+ </callout>
+ <callout arearefs="cache3">
+ <para>
+ <literal>include</literal> (optional, defaults to <literal>all</literal>)
+ <literal>non-lazy</literal> specifies that properties of the entity mapped
+ with <literal>lazy="true"</literal> may not be cached when attribute-level
+ lazy fetching is enabled
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Alternatively (preferrably?), you may specify <literal><class-cache></literal> and
+ <literal><collection-cache></literal> elements in <literal>hibernate.cfg.xml</literal>.
+ </para>
+
+ <para>
+ The <literal>usage</literal> attribute specifies a <emphasis>cache concurrency strategy</emphasis>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-cache-readonly">
+ <title>Strategy: read only</title>
+
+ <para>
+ If your application needs to read but never modify instances of a persistent class, a
+ <literal>read-only</literal> cache may be used. This is the simplest and best performing
+ strategy. It's even perfectly safe for use in a cluster.
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Immutable" mutable="false">
+ <cache usage="read-only"/>
+ ....
+</class>]]></programlisting>
+
+ </sect2>
+
+
+ <sect2 id="performance-cache-readwrite">
+ <title>Strategy: read/write</title>
+
+ <para>
+ If the application needs to update data, a <literal>read-write</literal> cache might be appropriate.
+ This cache strategy should never be used if serializable transaction isolation level is required.
+ If the cache is used in a JTA environment, you must specify the property
+ <literal>hibernate.transaction.manager_lookup_class</literal>, naming a strategy for obtaining the
+ JTA <literal>TransactionManager</literal>. In other environments, you should ensure that the transaction
+ is completed when <literal>Session.close()</literal> or <literal>Session.disconnect()</literal> is called.
+ If you wish to use this strategy in a cluster, you should ensure that the underlying cache implementation
+ supports locking. The built-in cache providers do <emphasis>not</emphasis>.
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Cat" .... >
+ <cache usage="read-write"/>
+ ....
+ <set name="kittens" ... >
+ <cache usage="read-write"/>
+ ....
+ </set>
+</class>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="performance-cache-nonstrict">
+ <title>Strategy: nonstrict read/write</title>
+
+ <para>
+ If the application only occasionally needs to update data (ie. if it is extremely unlikely that two
+ transactions would try to update the same item simultaneously) and strict transaction isolation is
+ not required, a <literal>nonstrict-read-write</literal> cache might be appropriate. If the cache is
+ used in a JTA environment, you must specify <literal>hibernate.transaction.manager_lookup_class</literal>.
+ In other environments, you should ensure that the transaction is completed when
+ <literal>Session.close()</literal> or <literal>Session.disconnect()</literal> is called.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-cache-transactional">
+ <title>Strategy: transactional</title>
+
+ <para>
+ The <literal>transactional</literal> cache strategy provides support for fully transactional cache
+ providers such as JBoss TreeCache. Such a cache may only be used in a JTA environment and you must
+ specify <literal>hibernate.transaction.manager_lookup_class</literal>.
+ </para>
+
+ </sect2>
+
+ <para>
+ None of the cache providers support all of the cache concurrency strategies. The following table shows
+ which providers are compatible with which concurrency strategies.
+ </para>
+
+ <table frame="topbot">
+ <title>Cache Concurrency Strategy Support</title>
+ <tgroup cols='5' align='left' colsep='1' rowsep='1'>
+ <colspec colname='c1' colwidth="1*"/>
+ <colspec colname='c2' colwidth="1*"/>
+ <colspec colname='c3' colwidth="1*"/>
+ <colspec colname='c4' colwidth="1*"/>
+ <colspec colname='c5' colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Cache</entry>
+ <entry>read-only</entry>
+ <entry>nonstrict-read-write</entry>
+ <entry>read-write</entry>
+ <entry>transactional</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>Hashtable (not intended for production use)</entry>
+ <entry>yes</entry>
+ <entry>yes</entry>
+ <entry>yes</entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>EHCache</entry>
+ <entry>yes</entry>
+ <entry>yes</entry>
+ <entry>yes</entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>OSCache</entry>
+ <entry>yes</entry>
+ <entry>yes</entry>
+ <entry>yes</entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>SwarmCache</entry>
+ <entry>yes</entry>
+ <entry>yes</entry>
+ <entry></entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>JBoss TreeCache</entry>
+ <entry>yes</entry>
+ <entry></entry>
+ <entry></entry>
+ <entry>yes</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect1>
+
+ <sect1 id="performance-sessioncache" revision="2">
+ <title>Managing the caches</title>
+
+ <para>
+ Whenever you pass an object to <literal>save()</literal>, <literal>update()</literal>
+ or <literal>saveOrUpdate()</literal> and whenever you retrieve an object using
+ <literal>load()</literal>, <literal>get()</literal>, <literal>list()</literal>,
+ <literal>iterate()</literal> or <literal>scroll()</literal>, that object is added
+ to the internal cache of the <literal>Session</literal>.
+ </para>
+ <para>
+ When <literal>flush()</literal> is subsequently called, the state of that object will
+ be synchronized with the database. If you do not want this synchronization to occur or
+ if you are processing a huge number of objects and need to manage memory efficiently,
+ the <literal>evict()</literal> method may be used to remove the object and its collections
+ from the first-level cache.
+ </para>
+
+ <programlisting><![CDATA[ScrollableResult cats = sess.createQuery("from Cat as cat").scroll(); //a huge result set
+while ( cats.next() ) {
+ Cat cat = (Cat) cats.get(0);
+ doSomethingWithACat(cat);
+ sess.evict(cat);
+}]]></programlisting>
+
+ <para>
+ The <literal>Session</literal> also provides a <literal>contains()</literal> method to determine
+ if an instance belongs to the session cache.
+ </para>
+
+ <para>
+ To completely evict all objects from the session cache, call <literal>Session.clear()</literal>
+ </para>
+
+ <para>
+ For the second-level cache, there are methods defined on <literal>SessionFactory</literal> for
+ evicting the cached state of an instance, entire class, collection instance or entire collection
+ role.
+ </para>
+
+ <programlisting><![CDATA[sessionFactory.evict(Cat.class, catId); //evict a particular Cat
+sessionFactory.evict(Cat.class); //evict all Cats
+sessionFactory.evictCollection("Cat.kittens", catId); //evict a particular collection of kittens
+sessionFactory.evictCollection("Cat.kittens"); //evict all kitten collections]]></programlisting>
+
+ <para>
+ The <literal>CacheMode</literal> controls how a particular session interacts with the second-level
+ cache.
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>CacheMode.NORMAL</literal> - read items from and write items to the second-level cache
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>CacheMode.GET</literal> - read items from the second-level cache, but don't write to
+ the second-level cache except when updating data
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>CacheMode.PUT</literal> - write items to the second-level cache, but don't read from
+ the second-level cache
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>CacheMode.REFRESH</literal> - write items to the second-level cache, but don't read from
+ the second-level cache, bypass the effect of <literal>hibernate.cache.use_minimal_puts</literal>, forcing
+ a refresh of the second-level cache for all items read from the database
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ To browse the contents of a second-level or query cache region, use the <literal>Statistics</literal>
+ API:
+ </para>
+
+ <programlisting><![CDATA[Map cacheEntries = sessionFactory.getStatistics()
+ .getSecondLevelCacheStatistics(regionName)
+ .getEntries();]]></programlisting>
+
+ <para>
+ You'll need to enable statistics, and, optionally, force Hibernate to keep the cache entries in a
+ more human-understandable format:
+ </para>
+
+ <programlisting><![CDATA[hibernate.generate_statistics true
+hibernate.cache.use_structured_entries true]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="performance-querycache" revision="1">
+ <title>The Query Cache</title>
+
+ <para>
+ Query result sets may also be cached. This is only useful for queries that are run
+ frequently with the same parameters. To use the query cache you must first enable it:
+ </para>
+
+ <programlisting><![CDATA[hibernate.cache.use_query_cache true]]></programlisting>
+
+ <para>
+ This setting causes the creation of two new cache regions - one holding cached query
+ result sets (<literal>org.hibernate.cache.StandardQueryCache</literal>), the other
+ holding timestamps of the most recent updates to queryable tables
+ (<literal>org.hibernate.cache.UpdateTimestampsCache</literal>). Note that the query
+ cache does not cache the state of the actual entities in the result set; it caches
+ only identifier values and results of value type. So the query cache should always be
+ used in conjunction with the second-level cache.
+ </para>
+
+ <para>
+ Most queries do not benefit from caching, so by default queries are not cached. To
+ enable caching, call <literal>Query.setCacheable(true)</literal>. This call allows
+ the query to look for existing cache results or add its results to the cache when
+ it is executed.
+ </para>
+
+ <para>
+ If you require fine-grained control over query cache expiration policies, you may
+ specify a named cache region for a particular query by calling
+ <literal>Query.setCacheRegion()</literal>.
+ </para>
+
+ <programlisting><![CDATA[List blogs = sess.createQuery("from Blog blog where blog.blogger = :blogger")
+ .setEntity("blogger", blogger)
+ .setMaxResults(15)
+ .setCacheable(true)
+ .setCacheRegion("frontpages")
+ .list();]]></programlisting>
+
+ <para>
+ If the query should force a refresh of its query cache region, you should call
+ <literal>Query.setCacheMode(CacheMode.REFRESH)</literal>. This is particularly useful
+ in cases where underlying data may have been updated via a separate process (i.e.,
+ not modified through Hibernate) and allows the application to selectively refresh
+ particular query result sets. This is a more efficient alternative to eviction of
+ a query cache region via <literal>SessionFactory.evictQueries()</literal>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="performance-collections">
+ <title>Understanding Collection performance</title>
+
+ <para>
+ We've already spent quite some time talking about collections.
+ In this section we will highlight a couple more issues about
+ how collections behave at runtime.
+ </para>
+
+ <sect2 id="performance-collections-taxonomy">
+ <title>Taxonomy</title>
+
+ <para>Hibernate defines three basic kinds of collections:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>collections of values</para>
+ </listitem>
+ <listitem>
+ <para>one to many associations</para>
+ </listitem>
+ <listitem>
+ <para>many to many associations</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ This classification distinguishes the various table and foreign key
+ relationships but does not tell us quite everything we need to know
+ about the relational model. To fully understand the relational structure
+ and performance characteristics, we must also consider the structure of
+ the primary key that is used by Hibernate to update or delete collection
+ rows. This suggests the following classification:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>indexed collections</para>
+ </listitem>
+ <listitem>
+ <para>sets</para>
+ </listitem>
+ <listitem>
+ <para>bags</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ All indexed collections (maps, lists, arrays) have a primary key consisting
+ of the <literal><key></literal> and <literal><index></literal>
+ columns. In this case collection updates are usually extremely efficient -
+ the primary key may be efficiently indexed and a particular row may be efficiently
+ located when Hibernate tries to update or delete it.
+ </para>
+
+ <para>
+ Sets have a primary key consisting of <literal><key></literal> and element
+ columns. This may be less efficient for some types of collection element, particularly
+ composite elements or large text or binary fields; the database may not be able to index
+ a complex primary key as efficently. On the other hand, for one to many or many to many
+ associations, particularly in the case of synthetic identifiers, it is likely to be just
+ as efficient. (Side-note: if you want <literal>SchemaExport</literal> to actually create
+ the primary key of a <literal><set></literal> for you, you must declare all columns
+ as <literal>not-null="true"</literal>.)
+ </para>
+
+ <para>
+ <literal><idbag></literal> mappings define a surrogate key, so they are
+ always very efficient to update. In fact, they are the best case.
+ </para>
+
+ <para>
+ Bags are the worst case. Since a bag permits duplicate element values and has no
+ index column, no primary key may be defined. Hibernate has no way of distinguishing
+ between duplicate rows. Hibernate resolves this problem by completely removing
+ (in a single <literal>DELETE</literal>) and recreating the collection whenever it
+ changes. This might be very inefficient.
+ </para>
+
+ <para>
+ Note that for a one-to-many association, the "primary key" may not be the physical
+ primary key of the database table - but even in this case, the above classification
+ is still useful. (It still reflects how Hibernate "locates" individual rows of the
+ collection.)
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-collections-mostefficientupdate">
+ <title>Lists, maps, idbags and sets are the most efficient collections to update</title>
+
+ <para>
+ From the discussion above, it should be clear that indexed collections
+ and (usually) sets allow the most efficient operation in terms of adding,
+ removing and updating elements.
+ </para>
+
+ <para>
+ There is, arguably, one more advantage that indexed collections have over sets for
+ many to many associations or collections of values. Because of the structure of a
+ <literal>Set</literal>, Hibernate doesn't ever <literal>UPDATE</literal> a row when
+ an element is "changed". Changes to a <literal>Set</literal> always work via
+ <literal>INSERT</literal> and <literal>DELETE</literal> (of individual rows). Once
+ again, this consideration does not apply to one to many associations.
+ </para>
+
+ <para>
+ After observing that arrays cannot be lazy, we would conclude that lists, maps and
+ idbags are the most performant (non-inverse) collection types, with sets not far
+ behind. Sets are expected to be the most common kind of collection in Hibernate
+ applications. This is because the "set" semantics are most natural in the relational
+ model.
+ </para>
+
+ <para>
+ However, in well-designed Hibernate domain models, we usually see that most collections
+ are in fact one-to-many associations with <literal>inverse="true"</literal>. For these
+ associations, the update is handled by the many-to-one end of the association, and so
+ considerations of collection update performance simply do not apply.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-collections-mostefficentinverse">
+ <title>Bags and lists are the most efficient inverse collections</title>
+
+ <para>
+ Just before you ditch bags forever, there is a particular case in which bags (and also lists)
+ are much more performant than sets. For a collection with <literal>inverse="true"</literal>
+ (the standard bidirectional one-to-many relationship idiom, for example) we can add elements
+ to a bag or list without needing to initialize (fetch) the bag elements! This is because
+ <literal>Collection.add()</literal> or <literal>Collection.addAll()</literal> must always
+ return true for a bag or <literal>List</literal> (unlike a <literal>Set</literal>). This can
+ make the following common code much faster.
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) sess.load(Parent.class, id);
+Child c = new Child();
+c.setParent(p);
+p.getChildren().add(c); //no need to fetch the collection!
+sess.flush();]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="performance-collections-oneshotdelete">
+ <title>One shot delete</title>
+
+ <para>
+ Occasionally, deleting collection elements one by one can be extremely inefficient. Hibernate
+ isn't completely stupid, so it knows not to do that in the case of an newly-empty collection
+ (if you called <literal>list.clear()</literal>, for example). In this case, Hibernate will
+ issue a single <literal>DELETE</literal> and we are done!
+ </para>
+
+ <para>
+ Suppose we add a single element to a collection of size twenty and then remove two elements.
+ Hibernate will issue one <literal>INSERT</literal> statement and two <literal>DELETE</literal>
+ statements (unless the collection is a bag). This is certainly desirable.
+ </para>
+
+ <para>
+ However, suppose that we remove eighteen elements, leaving two and then add thee new elements.
+ There are two possible ways to proceed
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>delete eighteen rows one by one and then insert three rows</para>
+ </listitem>
+ <listitem>
+ <para>remove the whole collection (in one SQL <literal>DELETE</literal>) and insert
+ all five current elements (one by one)</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Hibernate isn't smart enough to know that the second option is probably quicker in this case.
+ (And it would probably be undesirable for Hibernate to be that smart; such behaviour might
+ confuse database triggers, etc.)
+ </para>
+
+ <para>
+ Fortunately, you can force this behaviour (ie. the second strategy) at any time by discarding
+ (ie. dereferencing) the original collection and returning a newly instantiated collection with
+ all the current elements. This can be very useful and powerful from time to time.
+ </para>
+
+ <para>
+ Of course, one-shot-delete does not apply to collections mapped <literal>inverse="true"</literal>.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="performance-monitoring" revision="1">
+ <title>Monitoring performance</title>
+
+ <para>
+ Optimization is not much use without monitoring and access to performance numbers.
+ Hibernate provides a full range of figures about its internal operations.
+ Statistics in Hibernate are available per <literal>SessionFactory</literal>.
+ </para>
+
+ <sect2 id="performance-monitoring-sf" revision="2">
+ <title>Monitoring a SessionFactory</title>
+
+ <para>
+ You can access <literal>SessionFactory</literal> metrics in two ways.
+ Your first option is to call <literal>sessionFactory.getStatistics()</literal> and
+ read or display the <literal>Statistics</literal> yourself.
+ </para>
+
+ <para>
+ Hibernate can also use JMX to publish metrics if you enable the
+ <literal>StatisticsService</literal> MBean. You may enable a single MBean for all your
+ <literal>SessionFactory</literal> or one per factory. See the following code for
+ minimalistic configuration examples:
+ </para>
+
+ <programlisting><![CDATA[// MBean service registration for a specific SessionFactory
+Hashtable tb = new Hashtable();
+tb.put("type", "statistics");
+tb.put("sessionFactory", "myFinancialApp");
+ObjectName on = new ObjectName("hibernate", tb); // MBean object name
+
+StatisticsService stats = new StatisticsService(); // MBean implementation
+stats.setSessionFactory(sessionFactory); // Bind the stats to a SessionFactory
+server.registerMBean(stats, on); // Register the Mbean on the server]]></programlisting>
+
+
+<programlisting><![CDATA[// MBean service registration for all SessionFactory's
+Hashtable tb = new Hashtable();
+tb.put("type", "statistics");
+tb.put("sessionFactory", "all");
+ObjectName on = new ObjectName("hibernate", tb); // MBean object name
+
+StatisticsService stats = new StatisticsService(); // MBean implementation
+server.registerMBean(stats, on); // Register the MBean on the server]]></programlisting>
+
+ <para>
+ TODO: This doesn't make sense: In the first case, we retrieve and use the MBean directly. In the second one, we must give
+ the JNDI name in which the session factory is held before using it. Use
+ <literal>hibernateStatsBean.setSessionFactoryJNDIName("my/JNDI/Name")</literal>
+ </para>
+ <para>
+ You can (de)activate the monitoring for a <literal>SessionFactory</literal>
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ at configuration time, set <literal>hibernate.generate_statistics</literal> to <literal>false</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+ <itemizedlist>
+ <listitem>
+ <para>
+ at runtime: <literal>sf.getStatistics().setStatisticsEnabled(true)</literal>
+ or <literal>hibernateStatsBean.setStatisticsEnabled(true)</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Statistics can be reset programatically using the <literal>clear()</literal> method.
+ A summary can be sent to a logger (info level) using the <literal>logSummary()</literal>
+ method.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-monitoring-metrics" revision="1">
+ <title>Metrics</title>
+
+ <para>
+ Hibernate provides a number of metrics, from very basic to the specialized information
+ only relevant in certain scenarios. All available counters are described in the
+ <literal>Statistics</literal> interface API, in three categories:
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ Metrics related to the general <literal>Session</literal> usage, such as
+ number of open sessions, retrieved JDBC connections, etc.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Metrics related to he entities, collections, queries, and caches as a
+ whole (aka global metrics),
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Detailed metrics related to a particular entity, collection, query or
+ cache region.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ For exampl,e you can check the cache hit, miss, and put ratio of entities, collections
+ and queries, and the average time a query needs. Beware that the number of milliseconds
+ is subject to approximation in Java. Hibernate is tied to the JVM precision, on some
+ platforms this might even only be accurate to 10 seconds.
+ </para>
+
+ <para>
+ Simple getters are used to access the global metrics (i.e. not tied to a particular entity,
+ collection, cache region, etc.). You can access the metrics of a particular entity, collection
+ or cache region through its name, and through its HQL or SQL representation for queries. Please
+ refer to the <literal>Statistics</literal>, <literal>EntityStatistics</literal>,
+ <literal>CollectionStatistics</literal>, <literal>SecondLevelCacheStatistics</literal>,
+ and <literal>QueryStatistics</literal> API Javadoc for more information. The following
+ code shows a simple example:
+ </para>
+
+ <programlisting><![CDATA[Statistics stats = HibernateUtil.sessionFactory.getStatistics();
+
+double queryCacheHitCount = stats.getQueryCacheHitCount();
+double queryCacheMissCount = stats.getQueryCacheMissCount();
+double queryCacheHitRatio =
+ queryCacheHitCount / (queryCacheHitCount + queryCacheMissCount);
+
+log.info("Query Hit ratio:" + queryCacheHitRatio);
+
+EntityStatistics entityStats =
+ stats.getEntityStatistics( Cat.class.getName() );
+long changes =
+ entityStats.getInsertCount()
+ + entityStats.getUpdateCount()
+ + entityStats.getDeleteCount();
+log.info(Cat.class.getName() + " changed " + changes + "times" );]]></programlisting>
+
+ <para>
+ To work on all entities, collections, queries and region caches, you can retrieve
+ the list of names of entities, collections, queries and region caches with the
+ following methods: <literal>getQueries()</literal>, <literal>getEntityNames()</literal>,
+ <literal>getCollectionRoleNames()</literal>, and
+ <literal>getSecondLevelCacheRegionNames()</literal>.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+</chapter>
\ No newline at end of file
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/persistent_classes.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/persistent_classes.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/persistent_classes.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,531 @@
+<chapter id="persistent-classes" revision="2">
+ <title>Persistent Classes</title>
+
+ <para>
+ Persistent classes are classes in an application that implement the entities
+ of the business problem (e.g. Customer and Order in an E-commerce application).
+ Not all instances of a persistent class are considered to be in the persistent
+ state - an instance may instead be transient or detached.
+ </para>
+
+ <para>
+ Hibernate works best if these classes follow some simple rules, also known
+ as the Plain Old Java Object (POJO) programming model. However, none of these
+ rules are hard requirements. Indeed, Hibernate3 assumes very little about
+ the nature of your persistent objects. You may express a domain model in other
+ ways: using trees of <literal>Map</literal> instances, for example.
+ </para>
+
+ <sect1 id="persistent-classes-pojo">
+ <title>A simple POJO example</title>
+
+ <para>
+ Most Java applications require a persistent class representing felines.
+ </para>
+
+ <programlisting><![CDATA[package eg;
+import java.util.Set;
+import java.util.Date;
+
+public class Cat {
+ private Long id; // identifier
+
+ private Date birthdate;
+ private Color color;
+ private char sex;
+ private float weight;
+ private int litterId;
+
+ private Cat mother;
+ private Set kittens = new HashSet();
+
+ private void setId(Long id) {
+ this.id=id;
+ }
+ public Long getId() {
+ return id;
+ }
+
+ void setBirthdate(Date date) {
+ birthdate = date;
+ }
+ public Date getBirthdate() {
+ return birthdate;
+ }
+
+ void setWeight(float weight) {
+ this.weight = weight;
+ }
+ public float getWeight() {
+ return weight;
+ }
+
+ public Color getColor() {
+ return color;
+ }
+ void setColor(Color color) {
+ this.color = color;
+ }
+
+ void setSex(char sex) {
+ this.sex=sex;
+ }
+ public char getSex() {
+ return sex;
+ }
+
+ void setLitterId(int id) {
+ this.litterId = id;
+ }
+ public int getLitterId() {
+ return litterId;
+ }
+
+ void setMother(Cat mother) {
+ this.mother = mother;
+ }
+ public Cat getMother() {
+ return mother;
+ }
+ void setKittens(Set kittens) {
+ this.kittens = kittens;
+ }
+ public Set getKittens() {
+ return kittens;
+ }
+
+ // addKitten not needed by Hibernate
+ public void addKitten(Cat kitten) {
+ kitten.setMother(this);
+ kitten.setLitterId( kittens.size() );
+ kittens.add(kitten);
+ }
+}]]></programlisting>
+
+ <para>
+ There are four main rules to follow here:
+ </para>
+
+
+ <sect2 id="persistent-classes-pojo-constructor" revision="1">
+ <title>Implement a no-argument constructor</title>
+
+ <para>
+ <literal>Cat</literal> has a no-argument constructor. All persistent classes must
+ have a default constructor (which may be non-public) so that Hibernate can instantiate
+ them using <literal>Constructor.newInstance()</literal>. We strongly recommend having a
+ default constructor with at least <emphasis>package</emphasis> visibility for runtime proxy
+ generation in Hibernate.
+ </para>
+ </sect2>
+
+ <sect2 id="persistent-classes-pojo-identifier" revision="2">
+ <title>Provide an identifier property (optional)</title>
+
+ <para>
+ <literal>Cat</literal> has a property called <literal>id</literal>. This property
+ maps to the primary key column of a database table. The property might have been called
+ anything, and its type might have been any primitive type, any primitive "wrapper"
+ type, <literal>java.lang.String</literal> or <literal>java.util.Date</literal>. (If
+ your legacy database table has composite keys, you can even use a user-defined class
+ with properties of these types - see the section on composite identifiers later.)
+ </para>
+
+ <para>
+ The identifier property is strictly optional. You can leave them off and let Hibernate
+ keep track of object identifiers internally. We do not recommend this, however.
+ </para>
+
+ <para>
+ In fact, some functionality is available only to classes which declare an
+ identifier property:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ Transitive reattachment for detached objects (cascade update or cascade
+ merge) - see <xref linkend="objectstate-transitive"/>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>Session.saveOrUpdate()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>Session.merge()</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ We recommend you declare consistently-named identifier properties on persistent
+ classes. We further recommend that you use a nullable (ie. non-primitive) type.
+ </para>
+ </sect2>
+
+ <sect2 id="persistent-classes-pojo-final">
+ <title>Prefer non-final classes (optional)</title>
+ <para>
+ A central feature of Hibernate, <emphasis>proxies</emphasis>, depends upon the
+ persistent class being either non-final, or the implementation of an interface
+ that declares all public methods.
+ </para>
+ <para>
+ You can persist <literal>final</literal> classes that do not implement an interface
+ with Hibernate, but you won't be able to use proxies for lazy association fetching -
+ which will limit your options for performance tuning.
+ </para>
+ <para>
+ You should also avoid declaring <literal>public final</literal> methods on the
+ non-final classes. If you want to use a class with a <literal>public final</literal>
+ method, you must explicitly disable proxying by setting <literal>lazy="false"</literal>.
+ </para>
+ </sect2>
+
+ <sect2 id="persistent-classes-pojo-accessors" revision="2">
+ <title>Declare accessors and mutators for persistent fields (optional)</title>
+
+ <para>
+ <literal>Cat</literal> declares accessor methods for all its persistent fields.
+ Many other ORM tools directly persist instance variables. We believe it is
+ better to provide an indirection between the relational schema and internal
+ data structures of the class. By default, Hibernate persists JavaBeans style
+ properties, and recognizes method names of the form <literal>getFoo</literal>,
+ <literal>isFoo</literal> and <literal>setFoo</literal>. You may switch to direct
+ field access for particular properties, if needed.
+ </para>
+
+ <para>
+ Properties need <emphasis>not</emphasis> be declared public - Hibernate can
+ persist a property with a default, <literal>protected</literal> or
+ <literal>private</literal> get / set pair.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="persistent-classes-inheritance">
+ <title>Implementing inheritance</title>
+
+ <para>
+ A subclass must also observe the first and second rules. It inherits its
+ identifier property from the superclass, <literal>Cat</literal>.
+ </para>
+
+ <programlisting><![CDATA[package eg;
+
+public class DomesticCat extends Cat {
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+ protected void setName(String name) {
+ this.name=name;
+ }
+}]]></programlisting>
+ </sect1>
+
+ <sect1 id="persistent-classes-equalshashcode" revision="1">
+ <title>Implementing <literal>equals()</literal> and <literal>hashCode()</literal></title>
+
+ <para>
+ You have to override the <literal>equals()</literal> and <literal>hashCode()</literal>
+ methods if you
+ </para>
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ intend to put instances of persistent classes in a <literal>Set</literal>
+ (the recommended way to represent many-valued associations)
+ <emphasis>and</emphasis>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ intend to use reattachment of detached instances
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Hibernate guarantees equivalence of persistent identity (database row) and Java identity
+ only inside a particular session scope. So as soon as we mix instances retrieved in
+ different sessions, we must implement <literal>equals()</literal> and
+ <literal>hashCode()</literal> if we wish to have meaningful semantics for
+ <literal>Set</literal>s.
+ </para>
+
+ <para>
+ The most obvious way is to implement <literal>equals()</literal>/<literal>hashCode()</literal>
+ by comparing the identifier value of both objects. If the value is the same, both must
+ be the same database row, they are therefore equal (if both are added to a <literal>Set</literal>,
+ we will only have one element in the <literal>Set</literal>). Unfortunately, we can't use that
+ approach with generated identifiers! Hibernate will only assign identifier values to objects
+ that are persistent, a newly created instance will not have any identifier value! Furthermore,
+ if an instance is unsaved and currently in a <literal>Set</literal>, saving it will assign
+ an identifier value to the object. If <literal>equals()</literal> and <literal>hashCode()</literal>
+ are based on the identifier value, the hash code would change, breaking the contract of the
+ <literal>Set</literal>. See the Hibernate website for a full discussion of this problem. Note
+ that this is not a Hibernate issue, but normal Java semantics of object identity and equality.
+ </para>
+
+ <para>
+ We recommend implementing <literal>equals()</literal> and <literal>hashCode()</literal>
+ using <emphasis>Business key equality</emphasis>. Business key equality means that the
+ <literal>equals()</literal> method compares only the properties that form the business
+ key, a key that would identify our instance in the real world (a
+ <emphasis>natural</emphasis> candidate key):
+ </para>
+
+ <programlisting><![CDATA[public class Cat {
+
+ ...
+ public boolean equals(Object other) {
+ if (this == other) return true;
+ if ( !(other instanceof Cat) ) return false;
+
+ final Cat cat = (Cat) other;
+
+ if ( !cat.getLitterId().equals( getLitterId() ) ) return false;
+ if ( !cat.getMother().equals( getMother() ) ) return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ int result;
+ result = getMother().hashCode();
+ result = 29 * result + getLitterId();
+ return result;
+ }
+
+}]]></programlisting>
+
+ <para>
+ Note that a business key does not have to be as solid as a database
+ primary key candidate (see <xref linkend="transactions-basics-identity"/>).
+ Immutable or unique properties are usually good
+ candidates for a business key.
+ </para>
+
+ </sect1>
+
+ <sect1 id="persistent-classes-dynamicmodels">
+ <title>Dynamic models</title>
+
+ <para>
+ <emphasis>Note that the following features are currently considered
+ experimental and may change in the near future.</emphasis>
+ </para>
+
+ <para>
+ Persistent entities don't necessarily have to be represented as POJO classes
+ or as JavaBean objects at runtime. Hibernate also supports dynamic models
+ (using <literal>Map</literal>s of <literal>Map</literal>s at runtime) and the
+ representation of entities as DOM4J trees. With this approach, you don't
+ write persistent classes, only mapping files.
+ </para>
+
+ <para>
+ By default, Hibernate works in normal POJO mode. You may set a default entity
+ representation mode for a particular <literal>SessionFactory</literal> using the
+ <literal>default_entity_mode</literal> configuration option (see
+ <xref linkend="configuration-optional-properties"/>.
+ </para>
+
+ <para>
+ The following examples demonstrates the representation using <literal>Map</literal>s.
+ First, in the mapping file, an <literal>entity-name</literal> has to be declared
+ instead of (or in addition to) a class name:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class entity-name="Customer">
+
+ <id name="id"
+ type="long"
+ column="ID">
+ <generator class="sequence"/>
+ </id>
+
+ <property name="name"
+ column="NAME"
+ type="string"/>
+
+ <property name="address"
+ column="ADDRESS"
+ type="string"/>
+
+ <many-to-one name="organization"
+ column="ORGANIZATION_ID"
+ class="Organization"/>
+
+ <bag name="orders"
+ inverse="true"
+ lazy="false"
+ cascade="all">
+ <key column="CUSTOMER_ID"/>
+ <one-to-many class="Order"/>
+ </bag>
+
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+
+ Note that even though associations are declared using target class names,
+ the target type of an associations may also be a dynamic entity instead
+ of a POJO.
+ </para>
+
+ <para>
+ After setting the default entity mode to <literal>dynamic-map</literal>
+ for the <literal>SessionFactory</literal>, we can at runtime work with
+ <literal>Map</literal>s of <literal>Map</literal>s:
+ </para>
+
+ <programlisting><![CDATA[Session s = openSession();
+Transaction tx = s.beginTransaction();
+Session s = openSession();
+
+// Create a customer
+Map david = new HashMap();
+david.put("name", "David");
+
+// Create an organization
+Map foobar = new HashMap();
+foobar.put("name", "Foobar Inc.");
+
+// Link both
+david.put("organization", foobar);
+
+// Save both
+s.save("Customer", david);
+s.save("Organization", foobar);
+
+tx.commit();
+s.close();]]></programlisting>
+
+ <para>
+ The advantages of a dynamic mapping are quick turnaround time for prototyping
+ without the need for entity class implementation. However, you lose compile-time
+ type checking and will very likely deal with many exceptions at runtime. Thanks
+ to the Hibernate mapping, the database schema can easily be normalized and sound,
+ allowing to add a proper domain model implementation on top later on.
+ </para>
+
+ <para>
+ Entity representation modes can also be set on a per <literal>Session</literal>
+ basis:
+ </para>
+
+ <programlisting><![CDATA[Session dynamicSession = pojoSession.getSession(EntityMode.MAP);
+
+// Create a customer
+Map david = new HashMap();
+david.put("name", "David");
+dynamicSession.save("Customer", david);
+...
+dynamicSession.flush();
+dynamicSession.close()
+...
+// Continue on pojoSession
+]]></programlisting>
+
+
+ <para>
+ Please note that the call to <literal>getSession()</literal> using an
+ <literal>EntityMode</literal> is on the <literal>Session</literal> API, not the
+ <literal>SessionFactory</literal>. That way, the new <literal>Session</literal>
+ shares the underlying JDBC connection, transaction, and other context
+ information. This means you don't have tocall <literal>flush()</literal>
+ and <literal>close()</literal> on the secondary <literal>Session</literal>, and
+ also leave the transaction and connection handling to the primary unit of work.
+ </para>
+
+ <para>
+ More information about the XML representation capabilities can be found
+ in <xref linkend="xml"/>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="persistent-classes-tuplizers" revision="1">
+ <title>Tuplizers</title>
+
+ <para>
+ <literal>org.hibernate.tuple.Tuplizer</literal>, and its sub-interfaces, are responsible
+ for managing a particular representation of a piece of data, given that representation's
+ <literal>org.hibernate.EntityMode</literal>. If a given piece of data is thought of as
+ a data structure, then a tuplizer is the thing which knows how to create such a data structure
+ and how to extract values from and inject values into such a data structure. For example,
+ for the POJO entity mode, the correpsonding tuplizer knows how create the POJO through its
+ constructor and how to access the POJO properties using the defined property accessors.
+ There are two high-level types of Tuplizers, represented by the
+ <literal>org.hibernate.tuple.entity.EntityTuplizer</literal> and <literal>org.hibernate.tuple.component.ComponentTuplizer</literal>
+ interfaces. <literal>EntityTuplizer</literal>s are responsible for managing the above mentioned
+ contracts in regards to entities, while <literal>ComponentTuplizer</literal>s do the same for
+ components.
+ </para>
+
+ <para>
+ Users may also plug in their own tuplizers. Perhaps you require that a <literal>java.util.Map</literal>
+ implementation other than <literal>java.util.HashMap</literal> be used while in the
+ dynamic-map entity-mode; or perhaps you need to define a different proxy generation strategy
+ than the one used by default. Both would be achieved by defining a custom tuplizer
+ implementation. Tuplizers definitions are attached to the entity or component mapping they
+ are meant to manage. Going back to the example of our customer entity:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+ <class entity-name="Customer">
+ <!--
+ Override the dynamic-map entity-mode
+ tuplizer for the customer entity
+ -->
+ <tuplizer entity-mode="dynamic-map"
+ class="CustomMapTuplizerImpl"/>
+
+ <id name="id" type="long" column="ID">
+ <generator class="sequence"/>
+ </id>
+
+ <!-- other properties -->
+ ...
+ </class>
+</hibernate-mapping>
+
+
+public class CustomMapTuplizerImpl
+ extends org.hibernate.tuple.entity.DynamicMapEntityTuplizer {
+ // override the buildInstantiator() method to plug in our custom map...
+ protected final Instantiator buildInstantiator(
+ org.hibernate.mapping.PersistentClass mappingInfo) {
+ return new CustomMapInstantiator( mappingInfo );
+ }
+
+ private static final class CustomMapInstantiator
+ extends org.hibernate.tuple.DynamicMapInstantitor {
+ // override the generateMap() method to return our custom map...
+ protected final Map generateMap() {
+ return new CustomMap();
+ }
+ }
+}]]></programlisting>
+
+
+ </sect1>
+
+ <para>
+ TODO: Document user-extension framework in the property and proxy packages
+ </para>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/query_criteria.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/query_criteria.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/query_criteria.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,436 @@
+<chapter id="querycriteria">
+ <title>Criteria Queries</title>
+
+ <para>
+ Hibernate features an intuitive, extensible criteria query API.
+ </para>
+
+ <sect1 id="querycriteria-creating">
+ <title>Creating a <literal>Criteria</literal> instance</title>
+
+ <para>
+ The interface <literal>org.hibernate.Criteria</literal> represents a query against
+ a particular persistent class. The <literal>Session</literal> is a factory for
+ <literal>Criteria</literal> instances.
+ </para>
+
+ <programlisting><![CDATA[Criteria crit = sess.createCriteria(Cat.class);
+crit.setMaxResults(50);
+List cats = crit.list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-narrowing">
+ <title>Narrowing the result set</title>
+
+ <para>
+ An individual query criterion is an instance of the interface
+ <literal>org.hibernate.criterion.Criterion</literal>. The class
+ <literal>org.hibernate.criterion.Restrictions</literal> defines
+ factory methods for obtaining certain built-in
+ <literal>Criterion</literal> types.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "Fritz%") )
+ .add( Restrictions.between("weight", minWeight, maxWeight) )
+ .list();]]></programlisting>
+
+ <para>
+ Restrictions may be grouped logically.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "Fritz%") )
+ .add( Restrictions.or(
+ Restrictions.eq( "age", new Integer(0) ),
+ Restrictions.isNull("age")
+ ) )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.in( "name", new String[] { "Fritz", "Izi", "Pk" } ) )
+ .add( Restrictions.disjunction()
+ .add( Restrictions.isNull("age") )
+ .add( Restrictions.eq("age", new Integer(0) ) )
+ .add( Restrictions.eq("age", new Integer(1) ) )
+ .add( Restrictions.eq("age", new Integer(2) ) )
+ ) )
+ .list();]]></programlisting>
+
+ <para>
+ There are quite a range of built-in criterion types (<literal>Restrictions</literal>
+ subclasses), but one that is especially useful lets you specify SQL directly.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.sqlRestriction("lower({alias}.name) like lower(?)", "Fritz%", Hibernate.STRING) )
+ .list();]]></programlisting>
+
+ <para>
+ The <literal>{alias}</literal> placeholder with be replaced by the row alias
+ of the queried entity.
+ </para>
+
+ <para>
+ An alternative approach to obtaining a criterion is to get it from a
+ <literal>Property</literal> instance. You can create a <literal>Property</literal>
+ by calling <literal>Property.forName()</literal>.
+ </para>
+
+ <programlisting><![CDATA[
+Property age = Property.forName("age");
+List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.disjunction()
+ .add( age.isNull() )
+ .add( age.eq( new Integer(0) ) )
+ .add( age.eq( new Integer(1) ) )
+ .add( age.eq( new Integer(2) ) )
+ ) )
+ .add( Property.forName("name").in( new String[] { "Fritz", "Izi", "Pk" } ) )
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-ordering">
+ <title>Ordering the results</title>
+
+ <para>
+ You may order the results using <literal>org.hibernate.criterion.Order</literal>.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "F%")
+ .addOrder( Order.asc("name") )
+ .addOrder( Order.desc("age") )
+ .setMaxResults(50)
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Property.forName("name").like("F%") )
+ .addOrder( Property.forName("name").asc() )
+ .addOrder( Property.forName("age").desc() )
+ .setMaxResults(50)
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-associations" revision="2">
+ <title>Associations</title>
+
+ <para>
+ You may easily specify constraints upon related entities by navigating
+ associations using <literal>createCriteria()</literal>.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "F%") )
+ .createCriteria("kittens")
+ .add( Restrictions.like("name", "F%") )
+ .list();]]></programlisting>
+
+ <para>
+ note that the second <literal>createCriteria()</literal> returns a new
+ instance of <literal>Criteria</literal>, which refers to the elements of
+ the <literal>kittens</literal> collection.
+ </para>
+
+ <para>
+ The following, alternate form is useful in certain circumstances.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .createAlias("kittens", "kt")
+ .createAlias("mate", "mt")
+ .add( Restrictions.eqProperty("kt.name", "mt.name") )
+ .list();]]></programlisting>
+
+ <para>
+ (<literal>createAlias()</literal> does not create a new instance of
+ <literal>Criteria</literal>.)
+ </para>
+
+ <para>
+ Note that the kittens collections held by the <literal>Cat</literal> instances
+ returned by the previous two queries are <emphasis>not</emphasis> pre-filtered
+ by the criteria! If you wish to retrieve just the kittens that match the
+ criteria, you must use a <literal>ResultTransformer</literal>.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .createCriteria("kittens", "kt")
+ .add( Restrictions.eq("name", "F%") )
+ .setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP)
+ .list();
+Iterator iter = cats.iterator();
+while ( iter.hasNext() ) {
+ Map map = (Map) iter.next();
+ Cat cat = (Cat) map.get(Criteria.ROOT_ALIAS);
+ Cat kitten = (Cat) map.get("kt");
+}]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-dynamicfetching" revision="1">
+ <title>Dynamic association fetching</title>
+
+ <para>
+ You may specify association fetching semantics at runtime using
+ <literal>setFetchMode()</literal>.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "Fritz%") )
+ .setFetchMode("mate", FetchMode.EAGER)
+ .setFetchMode("kittens", FetchMode.EAGER)
+ .list();]]></programlisting>
+
+ <para>
+ This query will fetch both <literal>mate</literal> and <literal>kittens</literal>
+ by outer join. See <xref linkend="performance-fetching"/> for more information.
+ </para>
+
+ </sect1>
+
+ <sect1 id="querycriteria-examples">
+ <title>Example queries</title>
+
+ <para>
+ The class <literal>org.hibernate.criterion.Example</literal> allows
+ you to construct a query criterion from a given instance.
+ </para>
+
+ <programlisting><![CDATA[Cat cat = new Cat();
+cat.setSex('F');
+cat.setColor(Color.BLACK);
+List results = session.createCriteria(Cat.class)
+ .add( Example.create(cat) )
+ .list();]]></programlisting>
+
+ <para>
+ Version properties, identifiers and associations are ignored. By default,
+ null valued properties are excluded.
+ </para>
+
+ <para>
+ You can adjust how the <literal>Example</literal> is applied.
+ </para>
+
+ <programlisting><![CDATA[Example example = Example.create(cat)
+ .excludeZeroes() //exclude zero valued properties
+ .excludeProperty("color") //exclude the property named "color"
+ .ignoreCase() //perform case insensitive string comparisons
+ .enableLike(); //use like for string comparisons
+List results = session.createCriteria(Cat.class)
+ .add(example)
+ .list();]]></programlisting>
+
+ <para>
+ You can even use examples to place criteria upon associated objects.
+ </para>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .add( Example.create(cat) )
+ .createCriteria("mate")
+ .add( Example.create( cat.getMate() ) )
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-projection">
+ <title>Projections, aggregation and grouping</title>
+ <para>
+ The class <literal>org.hibernate.criterion.Projections</literal> is a
+ factory for <literal>Projection</literal> instances. We apply a
+ projection to a query by calling <literal>setProjection()</literal>.
+ </para>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.rowCount() )
+ .add( Restrictions.eq("color", Color.BLACK) )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.projectionList()
+ .add( Projections.rowCount() )
+ .add( Projections.avg("weight") )
+ .add( Projections.max("weight") )
+ .add( Projections.groupProperty("color") )
+ )
+ .list();]]></programlisting>
+
+ <para>
+ There is no explicit "group by" necessary in a criteria query. Certain
+ projection types are defined to be <emphasis>grouping projections</emphasis>,
+ which also appear in the SQL <literal>group by</literal> clause.
+ </para>
+
+ <para>
+ An alias may optionally be assigned to a projection, so that the projected value
+ may be referred to in restrictions or orderings. Here are two different ways to
+ do this:
+ </para>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.alias( Projections.groupProperty("color"), "colr" ) )
+ .addOrder( Order.asc("colr") )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.groupProperty("color").as("colr") )
+ .addOrder( Order.asc("colr") )
+ .list();]]></programlisting>
+
+ <para>
+ The <literal>alias()</literal> and <literal>as()</literal> methods simply wrap a
+ projection instance in another, aliased, instance of <literal>Projection</literal>.
+ As a shortcut, you can assign an alias when you add the projection to a
+ projection list:
+ </para>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.projectionList()
+ .add( Projections.rowCount(), "catCountByColor" )
+ .add( Projections.avg("weight"), "avgWeight" )
+ .add( Projections.max("weight"), "maxWeight" )
+ .add( Projections.groupProperty("color"), "color" )
+ )
+ .addOrder( Order.desc("catCountByColor") )
+ .addOrder( Order.desc("avgWeight") )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Domestic.class, "cat")
+ .createAlias("kittens", "kit")
+ .setProjection( Projections.projectionList()
+ .add( Projections.property("cat.name"), "catName" )
+ .add( Projections.property("kit.name"), "kitName" )
+ )
+ .addOrder( Order.asc("catName") )
+ .addOrder( Order.asc("kitName") )
+ .list();]]></programlisting>
+
+ <para>
+ You can also use <literal>Property.forName()</literal> to express projections:
+ </para>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Property.forName("name") )
+ .add( Property.forName("color").eq(Color.BLACK) )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.projectionList()
+ .add( Projections.rowCount().as("catCountByColor") )
+ .add( Property.forName("weight").avg().as("avgWeight") )
+ .add( Property.forName("weight").max().as("maxWeight") )
+ .add( Property.forName("color").group().as("color" )
+ )
+ .addOrder( Order.desc("catCountByColor") )
+ .addOrder( Order.desc("avgWeight") )
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-detachedqueries">
+ <title>Detached queries and subqueries</title>
+ <para>
+ The <literal>DetachedCriteria</literal> class lets you create a query outside the scope
+ of a session, and then later execute it using some arbitrary <literal>Session</literal>.
+ </para>
+
+ <programlisting><![CDATA[DetachedCriteria query = DetachedCriteria.forClass(Cat.class)
+ .add( Property.forName("sex").eq('F') );
+
+Session session = ....;
+Transaction txn = session.beginTransaction();
+List results = query.getExecutableCriteria(session).setMaxResults(100).list();
+txn.commit();
+session.close();]]></programlisting>
+
+ <para>
+ A <literal>DetachedCriteria</literal> may also be used to express a subquery. Criterion
+ instances involving subqueries may be obtained via <literal>Subqueries</literal> or
+ <literal>Property</literal>.
+ </para>
+
+ <programlisting><![CDATA[DetachedCriteria avgWeight = DetachedCriteria.forClass(Cat.class)
+ .setProjection( Property.forName("weight").avg() );
+session.createCriteria(Cat.class)
+ .add( Property.forName("weight).gt(avgWeight) )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[DetachedCriteria weights = DetachedCriteria.forClass(Cat.class)
+ .setProjection( Property.forName("weight") );
+session.createCriteria(Cat.class)
+ .add( Subqueries.geAll("weight", weights) )
+ .list();]]></programlisting>
+
+ <para>
+ Even correlated subqueries are possible:
+ </para>
+
+ <programlisting><![CDATA[DetachedCriteria avgWeightForSex = DetachedCriteria.forClass(Cat.class, "cat2")
+ .setProjection( Property.forName("weight").avg() )
+ .add( Property.forName("cat2.sex").eqProperty("cat.sex") );
+session.createCriteria(Cat.class, "cat")
+ .add( Property.forName("weight).gt(avgWeightForSex) )
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <!--TODO: ResultSetTransformer + aliasing. AliasToBeanTransformer allow returning arbitrary
+ user objects - similar to setResultClass in JDO2. General use of ResultTransformer
+ could also be explained. -->
+
+ <sect1 id="query-criteria-naturalid">
+ <title>Queries by natural identifier</title>
+
+ <para>
+ For most queries, including criteria queries, the query cache is not very efficient,
+ because query cache invalidation occurs too frequently. However, there is one special
+ kind of query where we can optimize the cache invalidation algorithm: lookups by a
+ constant natural key. In some applications, this kind of query occurs frequently.
+ The criteria API provides special provision for this use case.
+ </para>
+
+ <para>
+ First, you should map the natural key of your entity using
+ <literal><natural-id></literal>, and enable use of the second-level cache.
+ </para>
+
+ <programlisting><![CDATA[<class name="User">
+ <cache usage="read-write"/>
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <natural-id>
+ <property name="name"/>
+ <property name="org"/>
+ </natural-id>
+ <property name="password"/>
+</class>]]></programlisting>
+
+ <para>
+ Note that this functionality is not intended for use with entities with
+ <emphasis>mutable</emphasis> natural keys.
+ </para>
+
+ <para>
+ Next, enable the Hibernate query cache.
+ </para>
+
+ <para>
+ Now, <literal>Restrictions.naturalId()</literal> allows us to make use of
+ the more efficient cache algorithm.
+ </para>
+
+ <programlisting><![CDATA[session.createCriteria(User.class)
+ .add( Restrictions.naturalId()
+ .set("name", "gavin")
+ .set("org", "hb")
+ ).setCacheable(true)
+ .uniqueResult();]]></programlisting>
+
+ </sect1>
+
+</chapter>
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/query_hql.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/query_hql.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/query_hql.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1234 @@
+<chapter id="queryhql" revision="1">
+ <title>HQL: The Hibernate Query Language</title>
+
+ <para>
+ Hibernate is equipped with an extremely powerful query language that (quite intentionally)
+ looks very much like SQL. But don't be fooled by the syntax; HQL is fully object-oriented,
+ understanding notions like inheritence, polymorphism and association.
+ </para>
+
+ <sect1 id="queryhql-casesensitivity">
+ <title>Case Sensitivity</title>
+
+ <para>
+ Queries are case-insensitive, except for names of Java classes and properties.
+ So <literal>SeLeCT</literal> is the same as
+ <literal>sELEct</literal> is the same as
+ <literal>SELECT</literal> but
+ <literal>org.hibernate.eg.FOO</literal> is not
+ <literal>org.hibernate.eg.Foo</literal> and
+ <literal>foo.barSet</literal> is not
+ <literal>foo.BARSET</literal>.
+ </para>
+
+ <para>
+ This manual uses lowercase HQL keywords. Some users find queries with uppercase keywords
+ more readable, but we find this convention ugly when embedded in Java code.
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-from">
+ <title>The from clause</title>
+
+ <para>
+ The simplest possible Hibernate query is of the form:
+ </para>
+
+ <programlisting><![CDATA[from eg.Cat]]></programlisting>
+
+ <para>
+ which simply returns all instances of the class <literal>eg.Cat</literal>.
+ We don't usually need to qualify the class name, since <literal>auto-import</literal>
+ is the default. So we almost always just write:
+ </para>
+
+ <programlisting><![CDATA[from Cat]]></programlisting>
+
+ <para>
+ Most of the time, you will need to assign an <emphasis>alias</emphasis>, since
+ you will want to refer to the <literal>Cat</literal> in other parts of the
+ query.
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat]]></programlisting>
+
+ <para>
+ This query assigns the alias <literal>cat</literal> to <literal>Cat</literal>
+ instances, so we could use that alias later in the query. The <literal>as</literal>
+ keyword is optional; we could also write:
+ </para>
+
+ <programlisting><![CDATA[from Cat cat]]></programlisting>
+
+ <para>
+ Multiple classes may appear, resulting in a cartesian product or "cross" join.
+ </para>
+
+ <programlisting><![CDATA[from Formula, Parameter]]></programlisting>
+ <programlisting><![CDATA[from Formula as form, Parameter as param]]></programlisting>
+
+ <para>
+ It is considered good practice to name query aliases using an initial lowercase,
+ consistent with Java naming standards for local variables
+ (eg. <literal>domesticCat</literal>).
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-joins" revision="2">
+ <title>Associations and joins</title>
+
+ <para>
+ We may also assign aliases to associated entities, or even to elements of a
+ collection of values, using a <literal>join</literal>.
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+ inner join cat.mate as mate
+ left outer join cat.kittens as kitten]]></programlisting>
+
+ <programlisting><![CDATA[from Cat as cat left join cat.mate.kittens as kittens]]></programlisting>
+
+ <programlisting><![CDATA[from Formula form full join form.parameter param]]></programlisting>
+
+ <para>
+ The supported join types are borrowed from ANSI SQL
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>inner join</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>left outer join</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>right outer join</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>full join</literal> (not usually useful)
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ The <literal>inner join</literal>, <literal>left outer join</literal> and
+ <literal>right outer join</literal> constructs may be abbreviated.
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+ join cat.mate as mate
+ left join cat.kittens as kitten]]></programlisting>
+
+ <para>
+ You may supply extra join conditions using the HQL <literal>with</literal>
+ keyword.
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+ left join cat.kittens as kitten
+ with kitten.bodyWeight > 10.0]]></programlisting>
+
+ <para>
+ In addition, a "fetch" join allows associations or collections of values to be
+ initialized along with their parent objects, using a single select. This is particularly
+ useful in the case of a collection. It effectively overrides the outer join and
+ lazy declarations of the mapping file for associations and collections. See
+ <xref linkend="performance-fetching"/> for more information.
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+ inner join fetch cat.mate
+ left join fetch cat.kittens]]></programlisting>
+
+ <para>
+ A fetch join does not usually need to assign an alias, because the associated objects
+ should not be used in the <literal>where</literal> clause (or any other clause). Also,
+ the associated objects are not returned directly in the query results. Instead, they may
+ be accessed via the parent object. The only reason we might need an alias is if we are
+ recursively join fetching a further collection:
+ </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>
+ Note that the <literal>fetch</literal> construct may not be used in queries called using
+ <literal>iterate()</literal> (though <literal>scroll()</literal> can be used). Nor should
+ <literal>fetch</literal> be used together with <literal>setMaxResults()</literal> or
+ <literal>setFirstResult()</literal> as these operations are based on the result rows, which
+ usually contain duplicates for eager collection fetching, hence, the number of rows is not what
+ you'd expect.
+ Nor may <literal>fetch</literal> be used together with an ad hoc <literal>with</literal> condition.
+ It is possible to create a cartesian product by join fetching more than one collection in a
+ query, so take care in this case. Join fetching multiple collection roles also sometimes gives
+ unexpected results for bag mappings, so be careful about how you formulate your queries in this
+ case. Finally, note that <literal>full join fetch</literal> and <literal>right join fetch</literal>
+ are not meaningful.
+ </para>
+
+ <para>
+ If you are using property-level lazy fetching (with bytecode instrumentation), it is
+ possible to force Hibernate to fetch the lazy properties immediately (in the first
+ query) using <literal>fetch all properties</literal>.
+ </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>
+
+ </sect1>
+
+ <sect1 id="queryhql-joins-forms">
+ <title>Forms of join syntax</title>
+
+ <para>
+ HQL supports two forms of association joining: <literal>implicit</literal> and <literal>explicit</literal>.
+ </para>
+
+ <para>
+ The queries shown in the previous section all use the <literal>explicit</literal> form where
+ the join keyword is explicitly used in the from clause. This is the recommended form.
+ </para>
+
+ <para>
+ The <literal>implicit</literal> form does not use the join keyword. Instead, the
+ associations are "dereferenced" using dot-notation. <literal>implicit</literal> joins
+ can appear in any of the HQL clauses. <literal>implicit</literal> join result
+ in inner joins in the resulting SQL statement.
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat where cat.mate.name like '%s%']]></programlisting>
+ </sect1>
+
+ <sect1 id="queryhql-identifier-property">
+ <title>Refering to identifier property</title>
+
+ <para>
+ There are, generally speaking, 2 ways to refer to an entity's identifier property:
+ </para>
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ The special property (lowercase) <literal>id</literal> may be used to reference the identifier
+ property of an entity <emphasis>provided that entity does not define a non-identifier property
+ named id</emphasis>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ If the entity defines a named identifier property, you may use that property name.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ References to composite identifier properties follow the same naming rules. If the
+ entity has a non-identifier property named id, the composite identifier property can only
+ be referenced by its defined named; otherwise, the special <literal>id</literal> property
+ can be used to rerference the identifier property.
+ </para>
+
+ <para>
+ Note: this has changed significantly starting in version 3.2.2. In previous versions,
+ <literal>id</literal> <emphasis>always</emphasis> referred to the identifier property no
+ matter what its actual name. A ramification of that decision was that non-identifier
+ properties named <literal>id</literal> could never be referenced in Hibernate queries.
+ </para>
+ </sect1>
+
+ <sect1 id="queryhql-select">
+ <title>The select clause</title>
+
+ <para>
+ The <literal>select</literal> clause picks which objects and properties to return in
+ the query result set. Consider:
+ </para>
+
+ <programlisting><![CDATA[select mate
+from Cat as cat
+ inner join cat.mate as mate]]></programlisting>
+
+ <para>
+ The query will select <literal>mate</literal>s of other <literal>Cat</literal>s.
+ Actually, you may express this query more compactly as:
+ </para>
+
+ <programlisting><![CDATA[select cat.mate from Cat cat]]></programlisting>
+
+ <para>
+ Queries may return properties of any value type including properties of component type:
+ </para>
+
+ <programlisting><![CDATA[select cat.name from DomesticCat cat
+where cat.name like 'fri%']]></programlisting>
+
+ <programlisting><![CDATA[select cust.name.firstName from Customer as cust]]></programlisting>
+
+ <para>
+ Queries may return multiple objects and/or properties as an array of type
+ <literal>Object[]</literal>,
+ </para>
+
+ <programlisting><![CDATA[select mother, offspr, mate.name
+from DomesticCat as mother
+ inner join mother.mate as mate
+ left outer join mother.kittens as offspr]]></programlisting>
+
+ <para>
+ or as a <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>
+ or as an actual typesafe Java object,
+ </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>
+ assuming that the class <literal>Family</literal> has an appropriate constructor.
+ </para>
+
+ <para>
+ You may assign aliases to selected expressions using <literal>as</literal>:
+ </para>
+
+ <programlisting><![CDATA[select max(bodyWeight) as max, min(bodyWeight) as min, count(*) as n
+from Cat cat]]></programlisting>
+
+ <para>
+ This is most useful when used together with <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>
+ This query returns a <literal>Map</literal> from aliases to selected values.
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-aggregation">
+ <title>Aggregate functions</title>
+
+ <para>
+ HQL queries may even return the results of aggregate functions on properties:
+ </para>
+
+ <programlisting><![CDATA[select avg(cat.weight), sum(cat.weight), max(cat.weight), count(cat)
+from Cat cat]]></programlisting>
+
+<!-- NO LONGER SUPPORTED
+ <para>
+ Collections may also appear inside aggregate functions in the <literal>select</literal>
+ clause.
+ </para>
+
+ <programlisting><![CDATA[select cat, count( elements(cat.kittens) )
+from Cat cat group by cat]]></programlisting>
+-->
+
+ <para>
+ The supported aggregate functions are
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>avg(...), sum(...), min(...), max(...)</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>count(*)</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>count(...), count(distinct ...), count(all...)</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ You may use arithmetic operators, concatenation, and recognized SQL functions
+ in the select clause:
+ </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>
+ The <literal>distinct</literal> and <literal>all</literal> keywords may be used and
+ have the same semantics as in SQL.
+ </para>
+
+ <programlisting><![CDATA[select distinct cat.name from Cat cat
+
+select count(distinct cat.name), count(cat) from Cat cat]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="queryhql-polymorphism">
+ <title>Polymorphic queries</title>
+
+ <para>
+ A query like:
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat]]></programlisting>
+
+ <para>
+ returns instances not only of <literal>Cat</literal>, but also of subclasses like
+ <literal>DomesticCat</literal>. Hibernate queries may name <emphasis>any</emphasis> Java
+ class or interface in the <literal>from</literal> clause. The query will return instances
+ of all persistent classes that extend that class or implement the interface. The following
+ query would return all persistent objects:
+ </para>
+
+ <programlisting><![CDATA[from java.lang.Object o]]></programlisting>
+
+ <para>
+ The interface <literal>Named</literal> might be implemented by various persistent
+ classes:
+ </para>
+
+ <programlisting><![CDATA[from Named n, Named m where n.name = m.name]]></programlisting>
+
+ <para>
+ Note that these last two queries will require more than one SQL <literal>SELECT</literal>. This
+ means that the <literal>order by</literal> clause does not correctly order the whole result set.
+ (It also means you can't call these queries using <literal>Query.scroll()</literal>.)
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-where" revision="1">
+ <title>The where clause</title>
+
+ <para>
+ The <literal>where</literal> clause allows you to narrow the list of instances returned.
+ If no alias exists, you may refer to properties by name:
+ </para>
+
+ <programlisting><![CDATA[from Cat where name='Fritz']]></programlisting>
+
+ <para>
+ If there is an alias, use a qualified property name:
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat where cat.name='Fritz']]></programlisting>
+
+ <para>
+ returns instances of <literal>Cat</literal> named 'Fritz'.
+ </para>
+
+ <programlisting><![CDATA[select foo
+from Foo foo, Bar bar
+where foo.startDate = bar.date]]></programlisting>
+
+ <para>
+ will return all instances of <literal>Foo</literal> for which
+ there exists an instance of <literal>bar</literal> with a
+ <literal>date</literal> property equal to the
+ <literal>startDate</literal> property of the
+ <literal>Foo</literal>. Compound path expressions make the
+ <literal>where</literal> clause extremely powerful. Consider:
+ </para>
+
+ <programlisting><![CDATA[from Cat cat where cat.mate.name is not null]]></programlisting>
+
+ <para>
+ This query translates to an SQL query with a table (inner) join. If you were to write
+ something like
+ </para>
+
+ <programlisting><![CDATA[from Foo foo
+where foo.bar.baz.customer.address.city is not null]]></programlisting>
+
+ <para>
+ you would end up with a query that would require four table joins in SQL.
+ </para>
+
+ <para>
+ The <literal>=</literal> operator may be used to compare not only properties, but also
+ instances:
+ </para>
+
+ <programlisting><![CDATA[from Cat cat, Cat rival where cat.mate = rival.mate]]></programlisting>
+
+ <programlisting><![CDATA[select cat, mate
+from Cat cat, Cat mate
+where cat.mate = mate]]></programlisting>
+
+ <para>
+ The special property (lowercase) <literal>id</literal> may be used to reference the
+ unique identifier of an object. See <xref linkend="queryhql-identifier-property"/>
+ for more information.
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat where cat.id = 123
+
+from Cat as cat where cat.mate.id = 69]]></programlisting>
+
+ <para>
+ The second query is efficient. No table join is required!
+ </para>
+
+ <para>
+ Properties of composite identifiers may also be used. Suppose <literal>Person</literal>
+ has a composite identifier consisting of <literal>country</literal> and
+ <literal>medicareNumber</literal>. Again, see <xref linkend="queryhql-identifier-property"/>
+ for more information regarding referencing identifier properties.
+ </para>
+
+ <programlisting><![CDATA[from bank.Person person
+where person.id.country = 'AU'
+ and person.id.medicareNumber = 123456]]></programlisting>
+
+ <programlisting><![CDATA[from bank.Account account
+where account.owner.id.country = 'AU'
+ and account.owner.id.medicareNumber = 123456]]></programlisting>
+
+ <para>
+ Once again, the second query requires no table join.
+ </para>
+
+ <para>
+ Likewise, the special property <literal>class</literal> accesses the discriminator value
+ of an instance in the case of polymorphic persistence. A Java class name embedded in the
+ where clause will be translated to its discriminator value.
+ </para>
+
+ <programlisting><![CDATA[from Cat cat where cat.class = DomesticCat]]></programlisting>
+
+ <para>
+ You may also use components or composite user types, or properties of said
+ component types. See <xref linkend="queryhql-coomponents"/> for more details.
+ </para>
+
+ <para>
+ An "any" type has the special properties <literal>id</literal> and <literal>class</literal>,
+ allowing us to express a join in the following way (where <literal>AuditLog.item</literal>
+ is a property mapped with <literal><any></literal>).
+ </para>
+
+ <programlisting><![CDATA[from AuditLog log, Payment payment
+where log.item.class = 'Payment' and log.item.id = payment.id]]></programlisting>
+
+ <para>
+ Notice that <literal>log.item.class</literal> and <literal>payment.class</literal>
+ would refer to the values of completely different database columns in the above query.
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-expressions">
+ <title>Expressions</title>
+
+ <para>
+ Expressions allowed in the <literal>where</literal> clause include
+ most of the kind of things you could write in SQL:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ mathematical operators <literal>+, -, *, /</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ binary comparison operators <literal>=, >=, <=, <>, !=, like</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ logical operations <literal>and, or, not</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Parentheses <literal>( )</literal>, indicating grouping
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>in</literal>,
+ <literal>not in</literal>,
+ <literal>between</literal>,
+ <literal>is null</literal>,
+ <literal>is not null</literal>,
+ <literal>is empty</literal>,
+ <literal>is not empty</literal>,
+ <literal>member of</literal> and
+ <literal>not member of</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ "Simple" case, <literal>case ... when ... then ... else ... end</literal>, and
+ "searched" case, <literal>case when ... then ... else ... end</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ string concatenation <literal>...||...</literal> or <literal>concat(...,...)</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>current_date()</literal>, <literal>current_time()</literal>,
+ <literal>current_timestamp()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>second(...)</literal>, <literal>minute(...)</literal>,
+ <literal>hour(...)</literal>, <literal>day(...)</literal>,
+ <literal>month(...)</literal>, <literal>year(...)</literal>,
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Any function or operator defined by EJB-QL 3.0: <literal>substring(), trim(),
+ lower(), upper(), length(), locate(), abs(), sqrt(), bit_length(), mod()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>coalesce()</literal> and <literal>nullif()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>str()</literal> for converting numeric or temporal values to a
+ readable string
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>cast(... as ...)</literal>, where the second argument is the name of
+ a Hibernate type, and <literal>extract(... from ...)</literal> if ANSI
+ <literal>cast()</literal> and <literal>extract()</literal> is supported by
+ the underlying database
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ the HQL <literal>index()</literal> function, that applies to aliases of
+ a joined indexed collection
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ HQL functions that take collection-valued path expressions: <literal>size(),
+ minelement(), maxelement(), minindex(), maxindex()</literal>, along with the
+ special <literal>elements()</literal> and <literal>indices</literal> functions
+ which may be quantified using <literal>some, all, exists, any, in</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Any database-supported SQL scalar function like <literal>sign()</literal>,
+ <literal>trunc()</literal>, <literal>rtrim()</literal>, <literal>sin()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ JDBC-style positional parameters <literal>?</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ named parameters <literal>:name</literal>, <literal>:start_date</literal>, <literal>:x1</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ SQL literals <literal>'foo'</literal>, <literal>69</literal>, <literal>6.66E+2</literal>,
+ <literal>'1970-01-01 10:00:01.0'</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Java <literal>public static final</literal> constants <literal>eg.Color.TABBY</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ <literal>in</literal> and <literal>between</literal> may be used as follows:
+ </para>
+
+ <programlisting><![CDATA[from DomesticCat cat where cat.name between 'A' and 'B']]></programlisting>
+
+ <programlisting><![CDATA[from DomesticCat cat where cat.name in ( 'Foo', 'Bar', 'Baz' )]]></programlisting>
+
+ <para>
+ and the negated forms may be written
+ </para>
+
+ <programlisting><![CDATA[from DomesticCat cat where cat.name not between 'A' and 'B']]></programlisting>
+
+ <programlisting><![CDATA[from DomesticCat cat where cat.name not in ( 'Foo', 'Bar', 'Baz' )]]></programlisting>
+
+ <para>
+ Likewise, <literal>is null</literal> and <literal>is not null</literal> may be used to test
+ for null values.
+ </para>
+
+ <para>
+ Booleans may be easily used in expressions by declaring HQL query substitutions in Hibernate
+ configuration:
+ </para>
+
+ <programlisting><![CDATA[<property name="hibernate.query.substitutions">true 1, false 0</property>]]></programlisting>
+
+ <para>
+ This will replace the keywords <literal>true</literal> and <literal>false</literal> with the
+ literals <literal>1</literal> and <literal>0</literal> in the translated SQL from this HQL:
+ </para>
+
+ <programlisting><![CDATA[from Cat cat where cat.alive = true]]></programlisting>
+
+ <para>
+ You may test the size of a collection with the special property <literal>size</literal>, or
+ the special <literal>size()</literal> function.
+ </para>
+
+ <programlisting><![CDATA[from Cat cat where cat.kittens.size > 0]]></programlisting>
+
+ <programlisting><![CDATA[from Cat cat where size(cat.kittens) > 0]]></programlisting>
+
+ <para>
+ For indexed collections, you may refer to the minimum and maximum indices using
+ <literal>minindex</literal> and <literal>maxindex</literal> functions. Similarly,
+ you may refer to the minimum and maximum elements of a collection of basic type
+ using the <literal>minelement</literal> and <literal>maxelement</literal>
+ functions.
+ </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>
+ The SQL functions <literal>any, some, all, exists, in</literal> are supported when passed the element
+ or index set of a collection (<literal>elements</literal> and <literal>indices</literal> functions)
+ or the result of a subquery (see below).
+ </para>
+
+ <programlisting><![CDATA[select mother from Cat as mother, Cat as kit
+where kit in elements(foo.kittens)]]></programlisting>
+
+ <programlisting><![CDATA[select p from NameList list, Person p
+where p.name = some elements(list.names)]]></programlisting>
+
+ <programlisting><![CDATA[from Cat cat where exists elements(cat.kittens)]]></programlisting>
+
+ <programlisting><![CDATA[from Player p where 3 > all elements(p.scores)]]></programlisting>
+
+ <programlisting><![CDATA[from Show show where 'fizard' in indices(show.acts)]]></programlisting>
+
+ <para>
+ Note that these constructs - <literal>size</literal>, <literal>elements</literal>,
+ <literal>indices</literal>, <literal>minindex</literal>, <literal>maxindex</literal>,
+ <literal>minelement</literal>, <literal>maxelement</literal> - may only be used in
+ the where clause in Hibernate3.
+ </para>
+
+ <para>
+ Elements of indexed collections (arrays, lists, maps) may be referred to by
+ index (in a where clause only):
+ </para>
+
+ <programlisting><![CDATA[from Order order where order.items[0].id = 1234]]></programlisting>
+
+ <programlisting><![CDATA[select person from Person person, Calendar calendar
+where calendar.holidays['national day'] = person.birthDay
+ and person.nationality.calendar = calendar]]></programlisting>
+
+ <programlisting><![CDATA[select item from Item item, Order order
+where order.items[ order.deliveredItemIndices[0] ] = item and order.id = 11]]></programlisting>
+
+ <programlisting><![CDATA[select item from Item item, Order order
+where order.items[ maxindex(order.items) ] = item and order.id = 11]]></programlisting>
+
+ <para>
+ The expression inside <literal>[]</literal> may even be an arithmetic expression.
+ </para>
+
+ <programlisting><![CDATA[select item from Item item, Order order
+where order.items[ size(order.items) - 1 ] = item]]></programlisting>
+
+ <para>
+ HQL also provides the built-in <literal>index()</literal> function, for elements
+ of a one-to-many association or collection of values.
+ </para>
+
+ <programlisting><![CDATA[select item, index(item) from Order order
+ join order.items item
+where index(item) < 5]]></programlisting>
+
+ <para>
+ Scalar SQL functions supported by the underlying database may be used
+ </para>
+
+ <programlisting><![CDATA[from DomesticCat cat where upper(cat.name) like 'FRI%']]></programlisting>
+
+ <para>
+ If you are not yet convinced by all this, think how much longer and less readable the
+ following query would be in SQL:
+ </para>
+
+ <programlisting><![CDATA[select cust
+from Product prod,
+ Store store
+ inner join store.customers cust
+where prod.name = 'widget'
+ and store.location.name in ( 'Melbourne', 'Sydney' )
+ and prod = all elements(cust.currentOrder.lineItems)]]></programlisting>
+
+ <para>
+ <emphasis>Hint:</emphasis> something like
+ </para>
+
+ <programlisting><![CDATA[SELECT cust.name, cust.address, cust.phone, cust.id, cust.current_order
+FROM customers cust,
+ stores store,
+ locations loc,
+ store_customers sc,
+ product prod
+WHERE prod.name = 'widget'
+ AND store.loc_id = loc.id
+ AND loc.name IN ( 'Melbourne', 'Sydney' )
+ AND sc.store_id = store.id
+ AND sc.cust_id = cust.id
+ AND prod.id = ALL(
+ SELECT item.prod_id
+ FROM line_items item, orders o
+ WHERE item.order_id = o.id
+ AND cust.current_order = o.id
+ )]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="queryhql-ordering">
+ <title>The order by clause</title>
+
+ <para>
+ The list returned by a query may be ordered by any property of a returned class or components:
+ </para>
+
+ <programlisting><![CDATA[from DomesticCat cat
+order by cat.name asc, cat.weight desc, cat.birthdate]]></programlisting>
+
+ <para>
+ The optional <literal>asc</literal> or <literal>desc</literal> indicate ascending or descending order
+ respectively.
+ </para>
+ </sect1>
+
+ <sect1 id="queryhql-grouping" revision="1">
+ <title>The group by clause</title>
+
+ <para>
+ A query that returns aggregate values may be grouped by any property of a returned class or components:
+ </para>
+
+ <programlisting><![CDATA[select cat.color, sum(cat.weight), count(cat)
+from Cat cat
+group by cat.color]]></programlisting>
+
+ <programlisting><![CDATA[select foo.id, avg(name), max(name)
+from Foo foo join foo.names name
+group by foo.id]]></programlisting>
+
+ <para>
+ A <literal>having</literal> clause is also allowed.
+ </para>
+
+ <programlisting><![CDATA[select cat.color, sum(cat.weight), count(cat)
+from Cat cat
+group by cat.color
+having cat.color in (eg.Color.TABBY, eg.Color.BLACK)]]></programlisting>
+
+ <para>
+ SQL functions and aggregate functions are allowed in the <literal>having</literal>
+ and <literal>order by</literal> clauses, if supported by the underlying database
+ (eg. not in MySQL).
+ </para>
+
+ <programlisting><![CDATA[select cat
+from Cat cat
+ join cat.kittens kitten
+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>
+ Note that neither the <literal>group by</literal> clause nor the
+ <literal>order by</literal> clause may contain arithmetic expressions.
+ Also note that Hibernate currently does not expand a grouped entity,
+ so you can't write <literal>group by cat</literal> if all properties
+ of <literal>cat</literal> are non-aggregated. You have to list all
+ non-aggregated properties explicitly.
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-subqueries" revision="3">
+ <title>Subqueries</title>
+
+ <para>
+ For databases that support subselects, Hibernate supports subqueries within queries. A subquery must
+ be surrounded by parentheses (often by an SQL aggregate function call). Even correlated subqueries
+ (subqueries that refer to an alias in the outer query) are allowed.
+ </para>
+
+ <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>
+
+ <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>
+
+ <programlisting><![CDATA[select cat.id, (select max(kit.weight) from cat.kitten kit)
+from Cat as cat]]></programlisting>
+
+ <para>
+ Note that HQL subqueries may occur only in the select or where clauses.
+ </para>
+
+ <para>
+ Note that subqueries can also utilize <literal>row value constructor</literal> syntax. See
+ <xref linkend="queryhql-tuple"/> for more details.
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-examples">
+ <title>HQL examples</title>
+
+ <para>
+ Hibernate queries can be quite powerful and complex. In fact, the power of the query language
+ is one of Hibernate's main selling points. Here are some example queries very similar to queries
+ that I used on a recent project. Note that most queries you will write are much simpler than these!
+ </para>
+
+ <para>
+ The following query returns the order id, number of items and total value of the order for all
+ unpaid orders for a particular customer and given minimum total value, ordering the results by
+ total value. In determining the prices, it uses the current catalog. The resulting SQL query,
+ against the <literal>ORDER</literal>, <literal>ORDER_LINE</literal>, <literal>PRODUCT</literal>,
+ <literal>CATALOG</literal> and <literal>PRICE</literal> tables has four inner joins and an
+ (uncorrelated) subselect.
+ </para>
+
+ <programlisting><![CDATA[select order.id, sum(price.amount), count(item)
+from Order as order
+ join order.lineItems as item
+ join item.product as product,
+ Catalog as catalog
+ join catalog.prices as price
+where order.paid = false
+ and order.customer = :customer
+ and price.product = product
+ and catalog.effectiveDate < sysdate
+ and catalog.effectiveDate >= all (
+ select cat.effectiveDate
+ from Catalog as cat
+ where cat.effectiveDate < sysdate
+ )
+group by order
+having sum(price.amount) > :minAmount
+order by sum(price.amount) desc]]></programlisting>
+
+ <para>
+ What a monster! Actually, in real life, I'm not very keen on subqueries, so my query was
+ really more like this:
+ </para>
+
+ <programlisting><![CDATA[select order.id, sum(price.amount), count(item)
+from Order as order
+ join order.lineItems as item
+ join item.product as product,
+ Catalog as catalog
+ join catalog.prices as price
+where order.paid = false
+ and order.customer = :customer
+ and price.product = product
+ and catalog = :currentCatalog
+group by order
+having sum(price.amount) > :minAmount
+order by sum(price.amount) desc]]></programlisting>
+
+ <para>
+ The next query counts the number of payments in each status, excluding all payments in the
+ <literal>AWAITING_APPROVAL</literal> status where the most recent status change was made by the
+ current user. It translates to an SQL query with two inner joins and a correlated subselect
+ against the <literal>PAYMENT</literal>, <literal>PAYMENT_STATUS</literal> and
+ <literal>PAYMENT_STATUS_CHANGE</literal> tables.
+ </para>
+
+ <programlisting><![CDATA[select count(payment), status.name
+from Payment as payment
+ join payment.currentStatus as status
+ join payment.statusChanges as statusChange
+where payment.status.name <> PaymentStatus.AWAITING_APPROVAL
+ or (
+ statusChange.timeStamp = (
+ select max(change.timeStamp)
+ from PaymentStatusChange change
+ where change.payment = payment
+ )
+ and statusChange.user <> :currentUser
+ )
+group by status.name, status.sortOrder
+order by status.sortOrder]]></programlisting>
+
+ <para>
+ If I would have mapped the <literal>statusChanges</literal> collection as a list, instead of a set,
+ the query would have been much simpler to write.
+ </para>
+
+ <programlisting><![CDATA[select count(payment), status.name
+from Payment as payment
+ join payment.currentStatus as status
+where payment.status.name <> PaymentStatus.AWAITING_APPROVAL
+ or payment.statusChanges[ maxIndex(payment.statusChanges) ].user <> :currentUser
+group by status.name, status.sortOrder
+order by status.sortOrder]]></programlisting>
+
+ <para>
+ The next query uses the MS SQL Server <literal>isNull()</literal> function to return all
+ the accounts and unpaid payments for the organization to which the current user belongs.
+ It translates to an SQL query with three inner joins, an outer join and a subselect against
+ the <literal>ACCOUNT</literal>, <literal>PAYMENT</literal>, <literal>PAYMENT_STATUS</literal>,
+ <literal>ACCOUNT_TYPE</literal>, <literal>ORGANIZATION</literal> and
+ <literal>ORG_USER</literal> tables.
+ </para>
+
+ <programlisting><![CDATA[select account, payment
+from Account as account
+ left outer join account.payments as payment
+where :currentUser in elements(account.holder.users)
+ and PaymentStatus.UNPAID = isNull(payment.currentStatus.name, PaymentStatus.UNPAID)
+order by account.type.sortOrder, account.accountNumber, payment.dueDate]]></programlisting>
+
+ <para>
+ For some databases, we would need to do away with the (correlated) subselect.
+ </para>
+
+ <programlisting><![CDATA[select account, payment
+from Account as account
+ join account.holder.users as user
+ left outer join account.payments as payment
+where :currentUser = user
+ and PaymentStatus.UNPAID = isNull(payment.currentStatus.name, PaymentStatus.UNPAID)
+order by account.type.sortOrder, account.accountNumber, payment.dueDate]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="queryhql-bulk" revision="2">
+ <title>Bulk update and delete</title>
+
+ <para>
+ HQL now supports <literal>update</literal>, <literal>delete</literal> and
+ <literal>insert ... select ...</literal> statements.
+ See <xref linkend="batch-direct"/> for details.
+ </para>
+ </sect1>
+
+ <sect1 id="queryhql-tipstricks">
+ <title>Tips & Tricks</title>
+
+ <para>
+ You can count the number of query results without actually returning them:
+ </para>
+
+ <programlisting><![CDATA[( (Integer) session.iterate("select count(*) from ....").next() ).intValue()]]></programlisting>
+
+ <para>
+ To order a result by the size of a collection, use the following query:
+ </para>
+
+ <programlisting><![CDATA[select usr.id, usr.name
+from User as usr
+ left join usr.messages as msg
+group by usr.id, usr.name
+order by count(msg)]]></programlisting>
+
+ <para>
+ If your database supports subselects, you can place a condition upon selection
+ size in the where clause of your query:
+ </para>
+
+ <programlisting><![CDATA[from User usr where size(usr.messages) >= 1]]></programlisting>
+
+ <para>
+ If your database doesn't support subselects, use the following query:
+ </para>
+
+ <programlisting><![CDATA[select usr.id, usr.name
+from User usr.name
+ join usr.messages msg
+group by usr.id, usr.name
+having count(msg) >= 1]]></programlisting>
+
+ <para>
+ As this solution can't return a <literal>User</literal> with zero messages
+ because of the inner join, the following form is also useful:
+ </para>
+
+ <programlisting><![CDATA[select usr.id, usr.name
+from User as usr
+ left join usr.messages as msg
+group by usr.id, usr.name
+having count(msg) = 0]]></programlisting>
+
+ <para>
+ Properties of a JavaBean can be bound to named query parameters:
+ </para>
+
+ <programlisting><![CDATA[Query q = s.createQuery("from foo Foo as foo where foo.name=:name and foo.size=:size");
+q.setProperties(fooBean); // fooBean has getName() and getSize()
+List foos = q.list();]]></programlisting>
+
+ <para>
+ Collections are pageable by using the <literal>Query</literal> interface with a filter:
+ </para>
+
+ <programlisting><![CDATA[Query q = s.createFilter( collection, "" ); // the trivial filter
+q.setMaxResults(PAGE_SIZE);
+q.setFirstResult(PAGE_SIZE * pageNumber);
+List page = q.list();]]></programlisting>
+
+ <para>
+ Collection elements may be ordered or grouped using a query filter:
+ </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>
+
+ <para>
+ You can find the size of a collection without initializing it:
+ </para>
+
+ <programlisting><![CDATA[( (Integer) session.iterate("select count(*) from ....").next() ).intValue();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="queryhql-components">
+ <title>Components</title>
+
+ <para>
+ Components might be used in just about every way that simple value types can be used in HQL
+ queries. They can appear in the <literal>select</literal> clause:
+ </para>
+
+ <programlisting><![CDATA[select p.name from from Person p]]></programlisting>
+ <programlisting><![CDATA[select p.name.first from from Person p]]></programlisting>
+
+ <para>
+ where the Person's name property is a component. Components can also be used
+ in the <literal>where</literal> clause:
+ </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>
+ Components can also be used in the <literal>order by</literal> clause:
+ </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>
+ Another common use of components is in <xref linkend="queryhql-tuple">row value constructors</xref>.
+ </para>
+ </sect1>
+
+ <sect1 id="queryhql-tuple">
+ <title>Row value constructor syntax</title>
+
+ <para>
+ HQL supports the use of ANSI SQL <literal>row value constructor</literal> syntax (sometimes
+ called <literal>tuple</literal> syntax), even though the underlying database may not support
+ that notion. Here we are generally referring to multi-valued comparisons, typically associated
+ with components. Consider an entity Person which defines a name component:
+ </para>
+
+ <programlisting><![CDATA[from Person p where p.name.first='John' and p.name.last='Jingleheimer-Schmidt']]></programlisting>
+
+ <para>
+ That's valid syntax, although a little verbose. It be nice to make this a bit more concise and use
+ <literal>row value constructor</literal> syntax:
+ </para>
+
+ <programlisting><![CDATA[from Person p where p.name=('John', 'Jingleheimer-Schmidt')]]></programlisting>
+
+ <para>
+ It can also be useful to specify this in the <literal>select</literal> clause:
+ </para>
+
+ <programlisting><![CDATA[select p.name from from Person p]]></programlisting>
+
+ <para>
+ Another time using <literal>row value constructor</literal> syntax can be beneficial
+ is when using subqueries needing to compare against multiple values:
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+where not ( cat.name, cat.color ) in (
+ select cat.name, cat.color from DomesticCat cat
+)]]></programlisting>
+
+ <para>
+ One thing to consider when deciding if you want to use this syntax is that the query will
+ be dependent upon the ordering of the component sub-properties in the metadata.
+ </para>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/query_sql.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/query_sql.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/query_sql.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,758 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<chapter id="querysql" revision="2">
+ <title>Native SQL</title>
+
+ <para>You may also express queries in the native SQL dialect of your
+ database. This is useful if you want to utilize database specific features
+ such as query hints or the <literal>CONNECT</literal> keyword in Oracle. It
+ also provides a clean migration path from a direct SQL/JDBC based
+ application to Hibernate.</para>
+
+ <para>Hibernate3 allows you to specify handwritten SQL (including stored
+ procedures) for all create, update, delete, and load operations.</para>
+
+ <sect1 id="querysql-creating" revision="4">
+ <title>Using a <literal>SQLQuery</literal></title>
+
+ <para>Execution of native SQL queries is controlled via the
+ <literal>SQLQuery</literal> interface, which is obtained by calling
+ <literal>Session.createSQLQuery()</literal>. The following describes how
+ to use this API for querying.</para>
+
+ <sect2>
+ <title>Scalar queries</title>
+
+ <para>The most basic SQL query is to get a list of scalars
+ (values).</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT * FROM CATS").list();
+sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS").list();
+]]></programlisting>
+
+ <para>These will both return a List of Object arrays (Object[]) with
+ scalar values for each column in the CATS table. Hibernate will use
+ ResultSetMetadata to deduce the actual order and types of the returned
+ scalar values.</para>
+
+ <para>To avoid the overhead of using
+ <literal>ResultSetMetadata</literal> or simply to be more explicit in
+ what is returned one can use <literal>addScalar()</literal>.</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT * FROM CATS")
+ .addScalar("ID", Hibernate.LONG)
+ .addScalar("NAME", Hibernate.STRING)
+ .addScalar("BIRTHDATE", Hibernate.DATE)
+]]></programlisting>
+
+ <para>This query specified:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>the SQL query string</para>
+ </listitem>
+
+ <listitem>
+ <para>the columns and types to return</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>This will still return Object arrays, but now it will not use
+ <literal>ResultSetMetdata</literal> but will instead explicitly get the
+ ID, NAME and BIRTHDATE column as respectively a Long, String and a Short
+ from the underlying resultset. This also means that only these three
+ columns will be returned, even though the query is using
+ <literal>*</literal> and could return more than the three listed
+ columns.</para>
+
+ <para>It is possible to leave out the type information for all or some
+ of the scalars.</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT * FROM CATS")
+ .addScalar("ID", Hibernate.LONG)
+ .addScalar("NAME")
+ .addScalar("BIRTHDATE")
+]]></programlisting>
+
+ <para>This is essentially the same query as before, but now
+ <literal>ResultSetMetaData</literal> is used to decide the type of NAME
+ and BIRTHDATE where as the type of ID is explicitly specified.</para>
+
+ <para>How the java.sql.Types returned from ResultSetMetaData is mapped
+ to Hibernate types is controlled by the Dialect. If a specific type is
+ not mapped or does not result in the expected type it is possible to
+ customize it via calls to <literal>registerHibernateType</literal> in
+ the Dialect.</para>
+ </sect2>
+
+ <sect2>
+ <title>Entity queries</title>
+
+ <para>The above queries were all about returning scalar values,
+ basically returning the "raw" values from the resultset. The following
+ shows how to get entity objects from a native sql query via
+ <literal>addEntity()</literal>.</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT * FROM CATS").addEntity(Cat.class);
+sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS").addEntity(Cat.class);
+]]></programlisting>
+
+ <para>This query specified:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>the SQL query string</para>
+ </listitem>
+
+ <listitem>
+ <para>the entity returned by the query</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>Assuming that Cat is mapped as a class with the columns ID, NAME
+ and BIRTHDATE the above queries will both return a List where each
+ element is a Cat entity.</para>
+
+ <para>If the entity is mapped with a <literal>many-to-one</literal> to
+ another entity it is required to also return this when performing the
+ native query, otherwise a database specific "column not found" error
+ will occur. The additional columns will automatically be returned when
+ using the * notation, but we prefer to be explicit as in the following
+ example for a <literal>many-to-one</literal> to a
+ <literal>Dog</literal>:</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE, DOG_ID FROM CATS").addEntity(Cat.class);
+]]></programlisting>
+
+ <para>This will allow cat.getDog() to function properly.</para>
+ </sect2>
+
+ <sect2>
+ <title>Handling associations and collections</title>
+
+ <para>It is possible to eagerly join in the <literal>Dog</literal> to
+ avoid the possible extra roundtrip for initializing the proxy. This is
+ done via the <literal>addJoin()</literal> method, which allows you to
+ join in an association or collection.</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT c.ID, NAME, BIRTHDATE, DOG_ID, D_ID, D_NAME FROM CATS c, DOGS d WHERE c.DOG_ID = d.D_ID")
+ .addEntity("cat", Cat.class)
+ .addJoin("cat.dog");
+]]></programlisting>
+
+ <para>In this example the returned <literal>Cat</literal>'s will have
+ their <literal>dog</literal> property fully initialized without any
+ extra roundtrip to the database. Notice that we added a alias name
+ ("cat") to be able to specify the target property path of the join. It
+ is possible to do the same eager joining for collections, e.g. if the
+ <literal>Cat</literal> had a one-to-many to <literal>Dog</literal>
+ instead.</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE, D_ID, D_NAME, CAT_ID FROM CATS c, DOGS d WHERE c.ID = d.CAT_ID")
+ .addEntity("cat", Cat.class)
+ .addJoin("cat.dogs");
+]]></programlisting>
+
+ <p>At this stage we are reaching the limits of what is possible with
+ native queries without starting to enhance the sql queries to make them
+ usable in Hibernate; the problems starts to arise when returning
+ multiple entities of the same type or when the default alias/column
+ names are not enough.</p>
+ </sect2>
+
+ <sect2>
+ <title>Returning multiple entities</title>
+
+ <para>Until now the result set column names are assumed to be the same
+ as the column names specified in the mapping document. This can be
+ problematic for SQL queries which join multiple tables, since the same
+ column names may appear in more than one table.</para>
+
+ <para>Column alias injection is needed in the following query (which
+ most likely will fail):</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT c.*, m.* FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID")
+ .addEntity("cat", Cat.class)
+ .addEntity("mother", Cat.class)
+]]></programlisting>
+
+ <para>The intention for this query is to return two Cat instances per
+ row, a cat and its mother. This will fail since there is a conflict of
+ names since they are mapped to the same column names and on some
+ databases the returned column aliases will most likely be on the form
+ "c.ID", "c.NAME", etc. which are not equal to the columns specificed in
+ the mappings ("ID" and "NAME").</para>
+
+ <para>The following form is not vulnerable to column name
+ duplication:</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT {cat.*}, {mother.*} FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID")
+ .addEntity("cat", Cat.class)
+ .addEntity("mother", Cat.class)
+]]></programlisting>
+
+ <para>This query specified:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>the SQL query string, with placeholders for Hibernate to
+ inject column aliases</para>
+ </listitem>
+
+ <listitem>
+ <para>the entities returned by the query</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>The {cat.*} and {mother.*} notation used above is a shorthand for
+ "all properties". Alternatively, you may list the columns explicity, but
+ even in this case we let Hibernate inject the SQL column aliases for
+ each property. The placeholder for a column alias is just the property
+ name qualified by the table alias. In the following example, we retrieve
+ Cats and their mothers from a different table (cat_log) to the one
+ declared in the mapping metadata. Notice that we may even use the
+ property aliases in the where clause if we like.</para>
+
+ <programlisting><![CDATA[String sql = "SELECT ID as {c.id}, NAME as {c.name}, " +
+ "BIRTHDATE as {c.birthDate}, MOTHER_ID as {c.mother}, {mother.*} " +
+ "FROM CAT_LOG c, CAT_LOG m WHERE {c.mother} = c.ID";
+
+List loggedCats = sess.createSQLQuery(sql)
+ .addEntity("cat", Cat.class)
+ .addEntity("mother", Cat.class).list()
+]]></programlisting>
+
+ <sect3 id="querysql-aliasreferences" revision="2">
+ <title>Alias and property references</title>
+
+ <para>For most cases the above alias injection is needed, but for
+ queries relating to more complex mappings like composite properties,
+ inheritance discriminators, collections etc. there are some specific
+ aliases to use to allow Hibernate to inject the proper aliases.</para>
+
+ <para>The following table shows the different possibilities of using
+ the alias injection. Note: the alias names in the result are examples,
+ each alias will have a unique and probably different name when
+ used.</para>
+
+ <table frame="topbot" id="aliasinjection-summary">
+ <title>Alias injection names</title>
+
+ <tgroup cols="3">
+ <colspec colwidth="1*" />
+
+ <colspec colwidth="1*" />
+
+ <colspec colwidth="2.5*" />
+
+ <thead>
+ <row>
+ <entry>Description</entry>
+
+ <entry>Syntax</entry>
+
+ <entry>Example</entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry>A simple property</entry>
+
+ <entry><literal>{[aliasname].[propertyname]</literal></entry>
+
+ <entry><literal>A_NAME as {item.name}</literal></entry>
+ </row>
+
+ <row>
+ <entry>A composite property</entry>
+
+ <entry><literal>{[aliasname].[componentname].[propertyname]}</literal></entry>
+
+ <entry><literal>CURRENCY as {item.amount.currency}, VALUE as
+ {item.amount.value}</literal></entry>
+ </row>
+
+ <row>
+ <entry>Discriminator of an entity</entry>
+
+ <entry><literal>{[aliasname].class}</literal></entry>
+
+ <entry><literal>DISC as {item.class}</literal></entry>
+ </row>
+
+ <row>
+ <entry>All properties of an entity</entry>
+
+ <entry><literal>{[aliasname].*}</literal></entry>
+
+ <entry><literal>{item.*}</literal></entry>
+ </row>
+
+ <row>
+ <entry>A collection key</entry>
+
+ <entry><literal>{[aliasname].key}</literal></entry>
+
+ <entry><literal>ORGID as {coll.key}</literal></entry>
+ </row>
+
+ <row>
+ <entry>The id of an collection</entry>
+
+ <entry><literal>{[aliasname].id}</literal></entry>
+
+ <entry><literal>EMPID as {coll.id}</literal></entry>
+ </row>
+
+ <row>
+ <entry>The element of an collection</entry>
+
+ <entry><literal>{[aliasname].element}</literal></entry>
+
+ <entry><literal>XID as {coll.element}</literal></entry>
+ </row>
+
+ <row>
+ <entry>roperty of the element in the collection</entry>
+
+ <entry><literal>{[aliasname].element.[propertyname]}</literal></entry>
+
+ <entry><literal>NAME as {coll.element.name}</literal></entry>
+ </row>
+
+ <row>
+ <entry>All properties of the element in the collection</entry>
+
+ <entry><literal>{[aliasname].element.*}</literal></entry>
+
+ <entry><literal>{coll.element.*}</literal></entry>
+ </row>
+
+ <row>
+ <entry>All properties of the the collection</entry>
+
+ <entry><literal>{[aliasname].*}</literal></entry>
+
+ <entry><literal>{coll.*}</literal></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect3>
+ </sect2>
+
+ <sect2>
+ <title>Returning non-managed entities</title>
+
+ <para>It is possible to apply a ResultTransformer to native sql queries. Allowing it to e.g. return non-managed entities.</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT NAME, BIRTHDATE FROM CATS")
+ .setResultTransformer(Transformers.aliasToBean(CatDTO.class))]]></programlisting>
+
+ <para>This query specified:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>the SQL query string</para>
+ </listitem>
+
+ <listitem>
+ <para>a result transformer</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ The above query will return a list of <literal>CatDTO</literal> which has been instantiated and injected the values of NAME and BIRTHNAME into its corresponding
+ properties or fields.
+ </para>
+ </sect2>
+
+ <sect2>
+ <title>Handling inheritance</title>
+
+ <para>Native sql queries which query for entities that is mapped as part
+ of an inheritance must include all properties for the baseclass and all
+ it subclasses.</para>
+ </sect2>
+
+ <sect2>
+ <title>Parameters</title>
+
+ <para>Native sql queries support positional as well as named
+ parameters:</para>
+
+ <programlisting><![CDATA[Query query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME like ?").addEntity(Cat.class);
+List pusList = query.setString(0, "Pus%").list();
+
+query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME like :name").addEntity(Cat.class);
+List pusList = query.setString("name", "Pus%").list(); ]]></programlisting>
+ </sect2>
+
+
+
+ </sect1>
+
+ <sect1 id="querysql-namedqueries" revision="3">
+ <title>Named SQL queries</title>
+
+ <para>Named SQL queries may be defined in the mapping document and called
+ in exactly the same way as a named HQL query. In this case, we do
+ <emphasis>not</emphasis> need to call
+ <literal>addEntity()</literal>.</para>
+
+ <programlisting><![CDATA[<sql-query name="persons">
+ <return alias="person" class="eg.Person"/>
+ SELECT person.NAME AS {person.name},
+ person.AGE AS {person.age},
+ person.SEX AS {person.sex}
+ FROM PERSON person
+ WHERE person.NAME LIKE :namePattern
+</sql-query>]]></programlisting>
+
+ <programlisting><![CDATA[List people = sess.getNamedQuery("persons")
+ .setString("namePattern", namePattern)
+ .setMaxResults(50)
+ .list();]]></programlisting>
+
+ <para>The <literal><return-join></literal> and
+ <literal><load-collection></literal> elements are used to join
+ associations and define queries which initialize collections,
+ respectively.</para>
+
+ <programlisting><![CDATA[<sql-query name="personsWith">
+ <return alias="person" class="eg.Person"/>
+ <return-join alias="address" property="person.mailingAddress"/>
+ SELECT person.NAME AS {person.name},
+ person.AGE AS {person.age},
+ person.SEX AS {person.sex},
+ adddress.STREET AS {address.street},
+ adddress.CITY AS {address.city},
+ adddress.STATE AS {address.state},
+ adddress.ZIP AS {address.zip}
+ FROM PERSON person
+ JOIN ADDRESS adddress
+ ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'
+ WHERE person.NAME LIKE :namePattern
+</sql-query>]]></programlisting>
+
+ <para>A named SQL query may return a scalar value. You must declare the
+ column alias and Hibernate type using the
+ <literal><return-scalar></literal> element:</para>
+
+ <programlisting><![CDATA[<sql-query name="mySqlQuery">
+ <return-scalar column="name" type="string"/>
+ <return-scalar column="age" type="long"/>
+ SELECT p.NAME AS name,
+ p.AGE AS age,
+ FROM PERSON p WHERE p.NAME LIKE 'Hiber%'
+</sql-query>]]></programlisting>
+
+ <para>You can externalize the resultset mapping informations in a
+ <literal><resultset></literal> element to either reuse them accross
+ several named queries or through the
+ <literal>setResultSetMapping()</literal> API.</para>
+
+ <programlisting><![CDATA[<resultset name="personAddress">
+ <return alias="person" class="eg.Person"/>
+ <return-join alias="address" property="person.mailingAddress"/>
+</resultset>
+
+<sql-query name="personsWith" resultset-ref="personAddress">
+ SELECT person.NAME AS {person.name},
+ person.AGE AS {person.age},
+ person.SEX AS {person.sex},
+ adddress.STREET AS {address.street},
+ adddress.CITY AS {address.city},
+ adddress.STATE AS {address.state},
+ adddress.ZIP AS {address.zip}
+ FROM PERSON person
+ JOIN ADDRESS adddress
+ ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'
+ WHERE person.NAME LIKE :namePattern
+</sql-query>]]></programlisting>
+
+ <para>You can alternatively use the resultset mapping information in your
+ hbm files directly in java code.</para>
+
+ <programlisting><![CDATA[List cats = sess.createSQLQuery(
+ "select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id"
+ )
+ .setResultSetMapping("catAndKitten")
+ .list();]]></programlisting>
+
+ <sect2 id="propertyresults">
+ <title>Using return-property to explicitly specify column/alias
+ names</title>
+
+ <para>With <literal><return-property></literal> you can explicitly
+ tell Hibernate what column aliases to use, instead of using the
+ <literal>{}</literal>-syntax to let Hibernate inject its own
+ aliases.</para>
+
+ <programlisting><![CDATA[<sql-query name="mySqlQuery">
+ <return alias="person" class="eg.Person">
+ <return-property name="name" column="myName"/>
+ <return-property name="age" column="myAge"/>
+ <return-property name="sex" column="mySex"/>
+ </return>
+ SELECT person.NAME AS myName,
+ person.AGE AS myAge,
+ person.SEX AS mySex,
+ FROM PERSON person WHERE person.NAME LIKE :name
+</sql-query>
+]]></programlisting>
+
+ <para><literal><return-property></literal> also works with
+ multiple columns. This solves a limitation with the
+ <literal>{}</literal>-syntax which can not allow fine grained control of
+ multi-column properties.</para>
+
+ <programlisting><![CDATA[<sql-query name="organizationCurrentEmployments">
+ <return alias="emp" class="Employment">
+ <return-property name="salary">
+ <return-column name="VALUE"/>
+ <return-column name="CURRENCY"/>
+ </return-property>
+ <return-property name="endDate" column="myEndDate"/>
+ </return>
+ SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer},
+ STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},
+ REGIONCODE as {emp.regionCode}, EID AS {emp.id}, VALUE, CURRENCY
+ FROM EMPLOYMENT
+ WHERE EMPLOYER = :id AND ENDDATE IS NULL
+ ORDER BY STARTDATE ASC
+</sql-query>]]></programlisting>
+
+ <para>Notice that in this example we used
+ <literal><return-property></literal> in combination with the
+ <literal>{}</literal>-syntax for injection. Allowing users to choose how
+ they want to refer column and properties.</para>
+
+ <para>If your mapping has a discriminator you must use
+ <literal><return-discriminator></literal> to specify the
+ discriminator column.</para>
+ </sect2>
+
+ <sect2 id="sp_query" revision="1">
+ <title>Using stored procedures for querying</title>
+
+ <para>Hibernate 3 introduces support for queries via stored procedures
+ and functions. Most of the following documentation is equivalent for
+ both. The stored procedure/function must return a resultset as the first
+ out-parameter to be able to work with Hibernate. An example of such a
+ stored function in Oracle 9 and higher is as follows:</para>
+
+ <programlisting><![CDATA[CREATE OR REPLACE FUNCTION selectAllEmployments
+ RETURN SYS_REFCURSOR
+AS
+ st_cursor SYS_REFCURSOR;
+BEGIN
+ OPEN st_cursor FOR
+ SELECT EMPLOYEE, EMPLOYER,
+ STARTDATE, ENDDATE,
+ REGIONCODE, EID, VALUE, CURRENCY
+ FROM EMPLOYMENT;
+ RETURN st_cursor;
+ END;]]></programlisting>
+
+ <para>To use this query in Hibernate you need to map it via a named
+ query.</para>
+
+ <programlisting><![CDATA[<sql-query name="selectAllEmployees_SP" callable="true">
+ <return alias="emp" class="Employment">
+ <return-property name="employee" column="EMPLOYEE"/>
+ <return-property name="employer" column="EMPLOYER"/>
+ <return-property name="startDate" column="STARTDATE"/>
+ <return-property name="endDate" column="ENDDATE"/>
+ <return-property name="regionCode" column="REGIONCODE"/>
+ <return-property name="id" column="EID"/>
+ <return-property name="salary">
+ <return-column name="VALUE"/>
+ <return-column name="CURRENCY"/>
+ </return-property>
+ </return>
+ { ? = call selectAllEmployments() }
+</sql-query>]]></programlisting>
+
+ <para>Notice stored procedures currently only return scalars and
+ entities. <literal><return-join></literal> and
+ <literal><load-collection></literal> are not supported.</para>
+
+ <sect3 id="querysql-limits-storedprocedures" revision="1">
+ <title>Rules/limitations for using stored procedures</title>
+
+ <para>To use stored procedures with Hibernate the procedures/functions
+ have to follow some rules. If they do not follow those rules they are
+ not usable with Hibernate. If you still want to use these procedures
+ you have to execute them via <literal>session.connection()</literal>.
+ The rules are different for each database, since database vendors have
+ different stored procedure semantics/syntax.</para>
+
+ <para>Stored procedure queries can't be paged with
+ <literal>setFirstResult()/setMaxResults()</literal>.</para>
+
+ <para>Recommended call form is standard SQL92: <literal>{ ? = call
+ functionName(<parameters>) }</literal> or <literal>{ ? = call
+ procedureName(<parameters>}</literal>. Native call syntax is not
+ supported.</para>
+
+ <para>For Oracle the following rules apply:</para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>A function must return a result set. The first parameter of
+ a procedure must be an <literal>OUT</literal> that returns a
+ result set. This is done by using a
+ <literal>SYS_REFCURSOR</literal> type in Oracle 9 or 10. In Oracle
+ you need to define a <literal>REF CURSOR</literal> type, see
+ Oracle literature.</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>For Sybase or MS SQL server the following rules apply:</para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>The procedure must return a result set. Note that since
+ these servers can/will return multiple result sets and update
+ counts, Hibernate will iterate the results and take the first
+ result that is a result set as its return value. Everything else
+ will be discarded.</para>
+ </listitem>
+
+ <listitem>
+ <para>If you can enable <literal>SET NOCOUNT ON</literal> in your
+ procedure it will probably be more efficient, but this is not a
+ requirement.</para>
+ </listitem>
+ </itemizedlist>
+ </sect3>
+ </sect2>
+ </sect1>
+
+ <sect1 id="querysql-cud">
+ <title>Custom SQL for create, update and delete</title>
+
+ <para>Hibernate3 can use custom SQL statements for create, update, and
+ delete operations. The class and collection persisters in Hibernate
+ already contain a set of configuration time generated strings (insertsql,
+ deletesql, updatesql etc.). The mapping tags
+ <literal><sql-insert></literal>,
+ <literal><sql-delete></literal>, and
+ <literal><sql-update></literal> override these strings:</para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true"/>
+ <sql-insert>INSERT INTO PERSON (NAME, ID) VALUES ( UPPER(?), ? )</sql-insert>
+ <sql-update>UPDATE PERSON SET NAME=UPPER(?) WHERE ID=?</sql-update>
+ <sql-delete>DELETE FROM PERSON WHERE ID=?</sql-delete>
+</class>]]></programlisting>
+
+ <para>The SQL is directly executed in your database, so you are free to
+ use any dialect you like. This will of course reduce the portability of
+ your mapping if you use database specific SQL.</para>
+
+ <para>Stored procedures are supported if the <literal>callable</literal>
+ attribute is set:</para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true"/>
+ <sql-insert callable="true">{call createPerson (?, ?)}</sql-insert>
+ <sql-delete callable="true">{? = call deletePerson (?)}</sql-delete>
+ <sql-update callable="true">{? = call updatePerson (?, ?)}</sql-update>
+</class>]]></programlisting>
+
+ <para>The order of the positional parameters are currently vital, as they
+ must be in the same sequence as Hibernate expects them.</para>
+
+ <para>You can see the expected order by enabling debug logging for the
+ <literal>org.hibernate.persister.entity</literal> level. With this level
+ enabled Hibernate will print out the static SQL that is used to create,
+ update, delete etc. entities. (To see the expected sequence, remember to
+ not include your custom SQL in the mapping files as that will override the
+ Hibernate generated static sql.)</para>
+
+ <para>The stored procedures are in most cases (read: better do it than
+ not) required to return the number of rows inserted/updated/deleted, as
+ Hibernate has some runtime checks for the success of the statement.
+ Hibernate always registers the first statement parameter as a numeric
+ output parameter for the CUD operations:</para>
+
+ <programlisting><![CDATA[CREATE OR REPLACE FUNCTION updatePerson (uid IN NUMBER, uname IN VARCHAR2)
+ RETURN NUMBER IS
+BEGIN
+
+ update PERSON
+ set
+ NAME = uname,
+ where
+ ID = uid;
+
+ return SQL%ROWCOUNT;
+
+END updatePerson;]]></programlisting>
+ </sect1>
+
+ <sect1 id="querysql-load">
+ <title>Custom SQL for loading</title>
+
+ <para>You may also declare your own SQL (or HQL) queries for entity
+ loading:</para>
+
+ <programlisting><![CDATA[<sql-query name="person">
+ <return alias="pers" class="Person" lock-mode="upgrade"/>
+ SELECT NAME AS {pers.name}, ID AS {pers.id}
+ FROM PERSON
+ WHERE ID=?
+ FOR UPDATE
+</sql-query>]]></programlisting>
+
+ <para>This is just a named query declaration, as discussed earlier. You
+ may reference this named query in a class mapping:</para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true"/>
+ <loader query-ref="person"/>
+</class>]]></programlisting>
+
+ <para>This even works with stored procedures.</para>
+
+ <para>You may even define a query for collection loading:</para>
+
+ <programlisting><![CDATA[<set name="employments" inverse="true">
+ <key/>
+ <one-to-many class="Employment"/>
+ <loader query-ref="employments"/>
+</set>]]></programlisting>
+
+ <programlisting><![CDATA[<sql-query name="employments">
+ <load-collection alias="emp" role="Person.employments"/>
+ SELECT {emp.*}
+ FROM EMPLOYMENT emp
+ WHERE EMPLOYER = :id
+ ORDER BY STARTDATE ASC, EMPLOYEE ASC
+</sql-query>]]></programlisting>
+
+ <para>You could even define an entity loader that loads a collection by
+ join fetching:</para>
+
+ <programlisting><![CDATA[<sql-query name="person">
+ <return alias="pers" class="Person"/>
+ <return-join alias="emp" property="pers.employments"/>
+ SELECT NAME AS {pers.*}, {emp.*}
+ FROM PERSON pers
+ LEFT OUTER JOIN EMPLOYMENT emp
+ ON pers.ID = emp.PERSON_ID
+ WHERE ID=?
+</sql-query>]]></programlisting>
+ </sect1>
+</chapter>
\ No newline at end of file
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/session_api.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/session_api.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/session_api.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1240 @@
+<chapter id="objectstate">
+ <title>Working with objects</title>
+
+ <para>
+ Hibernate is a full object/relational mapping solution that not only shields
+ the developer from the details of the underlying database management
+ system, but also offers <emphasis>state management</emphasis> of objects. This is,
+ contrary to the management of SQL <literal>statements</literal> in common JDBC/SQL
+ persistence layers, a very natural object-oriented view of persistence in Java
+ applications.
+ </para>
+
+ <para>
+ In other words, Hibernate application developers should always think about the
+ <emphasis>state</emphasis> of their objects, and not necessarily about the
+ execution of SQL statements. This part is taken care of by Hibernate and is only
+ relevant for the application developer when tuning the performance of the system.
+ </para>
+
+ <sect1 id="objectstate-overview">
+ <title>Hibernate object states</title>
+
+ <para>
+ Hibernate defines and supports the following object states:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Transient</emphasis> - an object is transient if it has just
+ been instantiated using the <literal>new</literal> operator, and it
+ is not associated with a Hibernate <literal>Session</literal>. It has no
+ persistent representation in the database and no identifier value has been
+ assigned. Transient instances will be destroyed by the garbage collector if
+ the application doesn't hold a reference anymore. Use the Hibernate
+ <literal>Session</literal> to make an object persistent (and let Hibernate
+ take care of the SQL statements that need to be executed for this transition).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Persistent</emphasis> - a persistent instance has a representation
+ in the database and an identifier value. It might just have been saved or loaded,
+ however, it is by definition in the scope of a <literal>Session</literal>.
+ Hibernate will detect any changes made to an object in persistent state and
+ synchronize the state with the database when the unit of work completes.
+ Developers don't execute manual <literal>UPDATE</literal> statements, or
+ <literal>DELETE</literal> statements when an object should be made transient.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Detached</emphasis> - a detached instance is an object that has been
+ persistent, but its <literal>Session</literal> has been closed. The reference
+ to the object is still valid, of course, and the detached instance might even
+ be modified in this state. A detached instance can be reattached to a new
+ <literal>Session</literal> at a later point in time, making it (and all the
+ modifications) persistent again. This feature enables a programming model for
+ long running units of work that require user think-time. We call them
+ <emphasis>application transactions</emphasis>, i.e. a unit of work from the
+ point of view of the user.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ We'll now discuss the states and state transitions (and the Hibernate methods that
+ trigger a transition) in more detail.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-makingpersistent" revision="1">
+ <title>Making objects persistent</title>
+
+ <para>
+ Newly instantiated instances of a a persistent class are considered
+ <emphasis>transient</emphasis> by Hibernate. We can make a transient
+ instance <emphasis>persistent</emphasis> by associating it with a
+ session:
+ </para>
+
+ <programlisting><![CDATA[DomesticCat fritz = new DomesticCat();
+fritz.setColor(Color.GINGER);
+fritz.setSex('M');
+fritz.setName("Fritz");
+Long generatedId = (Long) sess.save(fritz);]]></programlisting>
+
+ <para>
+ If <literal>Cat</literal> has a generated identifier, the identifier is
+ generated and assigned to the <literal>cat</literal> when <literal>save()</literal>
+ is called. If <literal>Cat</literal> has an <literal>assigned</literal>
+ identifier, or a composite key, the identifier should be assigned to
+ the <literal>cat</literal> instance before calling <literal>save()</literal>.
+ You may also use <literal>persist()</literal> instead of <literal>save()</literal>,
+ with the semantics defined in the EJB3 early draft.
+ </para>
+
+ <para>
+ Alternatively, you may assign the identifier using an overloaded version
+ of <literal>save()</literal>.
+ </para>
+
+<programlisting><![CDATA[DomesticCat pk = new DomesticCat();
+pk.setColor(Color.TABBY);
+pk.setSex('F');
+pk.setName("PK");
+pk.setKittens( new HashSet() );
+pk.addKitten(fritz);
+sess.save( pk, new Long(1234) );]]></programlisting>
+
+ <para>
+ If the object you make persistent has associated objects (e.g. the
+ <literal>kittens</literal> collection in the previous example),
+ these objects may be made persistent in any order you like unless you
+ have a <literal>NOT NULL</literal> constraint upon a foreign key column.
+ There is never a risk of violating foreign key constraints. However, you
+ might violate a <literal>NOT NULL</literal> constraint if you
+ <literal>save()</literal> the objects in the wrong order.
+ </para>
+
+ <para>
+ Usually you don't bother with this detail, as you'll very likely use Hibernate's
+ <emphasis>transitive persistence</emphasis> feature to save the associated
+ objects automatically. Then, even <literal>NOT NULL</literal>
+ constraint violations don't occur - Hibernate will take care of everything.
+ Transitive persistence is discussed later in this chapter.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-loading">
+ <title>Loading an object</title>
+
+ <para>
+ The <literal>load()</literal> methods of <literal>Session</literal> gives you
+ a way to retrieve a persistent instance if you already know its identifier.
+ <literal>load()</literal> takes a class object and will load the state into
+ a newly instantiated instance of that class, in persistent state.
+ </para>
+
+ <programlisting><![CDATA[Cat fritz = (Cat) sess.load(Cat.class, generatedId);]]></programlisting>
+
+<programlisting><![CDATA[// you need to wrap primitive identifiers
+long id = 1234;
+DomesticCat pk = (DomesticCat) sess.load( DomesticCat.class, new Long(id) );]]></programlisting>
+
+ <para>
+ Alternatively, you can load state into a given instance:
+ </para>
+
+<programlisting><![CDATA[Cat cat = new DomesticCat();
+// load pk's state into cat
+sess.load( cat, new Long(pkId) );
+Set kittens = cat.getKittens();]]></programlisting>
+
+ <para>
+ Note that <literal>load()</literal> will throw an unrecoverable exception if
+ there is no matching database row. If the class is mapped with a proxy,
+ <literal>load()</literal> just returns an uninitialized proxy and does not
+ actually hit the database until you invoke a method of the proxy. This
+ behaviour is very useful if you wish to create an association to an object
+ without actually loading it from the database. It also allows multiple
+ instances to be loaded as a batch if <literal>batch-size</literal> is
+ defined for the class mapping.
+ </para>
+
+ <para>
+ If you are not certain that a matching row exists, you should use the
+ <literal>get()</literal> method, which hits the database immediately and
+ returns null if there is no matching row.
+ </para>
+
+ <programlisting><![CDATA[Cat cat = (Cat) sess.get(Cat.class, id);
+if (cat==null) {
+ cat = new Cat();
+ sess.save(cat, id);
+}
+return cat;]]></programlisting>
+
+ <para>
+ You may even load an object using an SQL <literal>SELECT ... FOR UPDATE</literal>,
+ using a <literal>LockMode</literal>. See the API documentation for more information.
+ </para>
+
+ <programlisting><![CDATA[Cat cat = (Cat) sess.get(Cat.class, id, LockMode.UPGRADE);]]></programlisting>
+
+ <para>
+ Note that any associated instances or contained collections are
+ <emphasis>not</emphasis> selected <literal>FOR UPDATE</literal>, unless you decide
+ to specify <literal>lock</literal> or <literal>all</literal> as a
+ cascade style for the association.
+ </para>
+
+ <para>
+ It is possible to re-load an object and all its collections at any time, using the
+ <literal>refresh()</literal> method. This is useful when database triggers are used to
+ initialize some of the properties of the object.
+ </para>
+
+ <programlisting><![CDATA[sess.save(cat);
+sess.flush(); //force the SQL INSERT
+sess.refresh(cat); //re-read the state (after the trigger executes)]]></programlisting>
+
+ <para>
+ An important question usually appears at this point: How much does Hibernate load
+ from the database and how many SQL <literal>SELECT</literal>s will it use? This
+ depends on the <emphasis>fetching strategy</emphasis> and is explained in
+ <xref linkend="performance-fetching"/>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-querying" revision="1">
+ <title>Querying</title>
+
+ <para>
+ If you don't know the identifiers of the objects you are looking for,
+ you need a query. Hibernate supports an easy-to-use but powerful object
+ oriented query language (HQL). For programmatic query creation, Hibernate
+ supports a sophisticated Criteria and Example query feature (QBC and QBE).
+ You may also express your query in the native SQL of your database, with
+ optional support from Hibernate for result set conversion into objects.
+ </para>
+
+ <sect2 id="objectstate-querying-executing" revision="1">
+ <title>Executing queries</title>
+
+ <para>
+ HQL and native SQL queries are represented with an instance of <literal>org.hibernate.Query</literal>.
+ This interface offers methods for parameter binding, result set handling, and for the execution
+ of the actual query. You always obtain a <literal>Query</literal> using the current
+ <literal>Session</literal>:
+ </para>
+
+ <programlisting><![CDATA[List cats = session.createQuery(
+ "from Cat as cat where cat.birthdate < ?")
+ .setDate(0, date)
+ .list();
+
+List mothers = session.createQuery(
+ "select mother from Cat as cat join cat.mother as mother where cat.name = ?")
+ .setString(0, name)
+ .list();
+
+List kittens = session.createQuery(
+ "from Cat as cat where cat.mother = ?")
+ .setEntity(0, pk)
+ .list();
+
+Cat mother = (Cat) session.createQuery(
+ "select cat.mother from Cat as cat where cat = ?")
+ .setEntity(0, izi)
+ .uniqueResult();]]
+
+Query mothersWithKittens = (Cat) session.createQuery(
+ "select mother from Cat as mother left join fetch mother.kittens");
+Set uniqueMothers = new HashSet(mothersWithKittens.list());]]></programlisting>
+
+ <para>
+ A query is usually executed by invoking <literal>list()</literal>, the
+ result of the query will be loaded completely into a collection in memory.
+ Entity instances retrieved by a query are in persistent state. The
+ <literal>uniqueResult()</literal> method offers a shortcut if you
+ know your query will only return a single object. Note that queries that
+ make use of eager fetching of collections usually return duplicates of
+ the root objects (but with their collections initialized). You can filter
+ these duplicates simply through a <literal>Set</literal>.
+ </para>
+
+ <sect3 id="objectstate-querying-executing-iterate">
+ <title>Iterating results</title>
+
+ <para>
+ Occasionally, you might be able to achieve better performance by
+ executing the query using the <literal>iterate()</literal> method.
+ This will only usually be the case if you expect that the actual
+ entity instances returned by the query will already be in the session
+ or second-level cache. If they are not already cached,
+ <literal>iterate()</literal> will be slower than <literal>list()</literal>
+ and might require many database hits for a simple query, usually
+ <emphasis>1</emphasis> for the initial select which only returns identifiers,
+ and <emphasis>n</emphasis> additional selects to initialize the actual instances.
+ </para>
+
+ <programlisting><![CDATA[// fetch ids
+Iterator iter = sess.createQuery("from eg.Qux q order by q.likeliness").iterate();
+while ( iter.hasNext() ) {
+ Qux qux = (Qux) iter.next(); // fetch the object
+ // something we couldnt express in the query
+ if ( qux.calculateComplicatedAlgorithm() ) {
+ // delete the current instance
+ iter.remove();
+ // dont need to process the rest
+ break;
+ }
+}]]></programlisting>
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-tuples">
+ <title>Queries that return tuples</title>
+
+ <para>
+ Hibernate queries sometimes return tuples of objects, in which case each tuple
+ is returned as an array:
+ </para>
+
+ <programlisting><![CDATA[Iterator kittensAndMothers = sess.createQuery(
+ "select kitten, mother from Cat kitten join kitten.mother mother")
+ .list()
+ .iterator();
+
+while ( kittensAndMothers.hasNext() ) {
+ Object[] tuple = (Object[]) kittensAndMothers.next();
+ Cat kitten = tuple[0];
+ Cat mother = tuple[1];
+ ....
+}]]></programlisting>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-scalar" revision="1">
+ <title>Scalar results</title>
+
+ <para>
+ Queries may specify a property of a class in the <literal>select</literal> clause.
+ They may even call SQL aggregate functions. Properties or aggregates are considered
+ "scalar" results (and not entities in persistent state).
+ </para>
+
+ <programlisting><![CDATA[Iterator results = sess.createQuery(
+ "select cat.color, min(cat.birthdate), count(cat) from Cat cat " +
+ "group by cat.color")
+ .list()
+ .iterator();
+
+while ( results.hasNext() ) {
+ Object[] row = (Object[]) results.next();
+ Color type = (Color) row[0];
+ Date oldest = (Date) row[1];
+ Integer count = (Integer) row[2];
+ .....
+}]]></programlisting>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-parameters">
+ <title>Bind parameters</title>
+
+ <para>
+ Methods on <literal>Query</literal> are provided for binding values to
+ named parameters or JDBC-style <literal>?</literal> parameters.
+ <emphasis>Contrary to JDBC, Hibernate numbers parameters from zero.</emphasis>
+ Named parameters are identifiers of the form <literal>:name</literal> in
+ the query string. The advantages of named parameters are:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ named parameters are insensitive to the order they occur in the
+ query string
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ they may occur multiple times in the same query
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ they are self-documenting
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <programlisting><![CDATA[//named parameter (preferred)
+Query q = sess.createQuery("from DomesticCat cat where cat.name = :name");
+q.setString("name", "Fritz");
+Iterator cats = q.iterate();]]></programlisting>
+
+ <programlisting><![CDATA[//positional parameter
+Query q = sess.createQuery("from DomesticCat cat where cat.name = ?");
+q.setString(0, "Izi");
+Iterator cats = q.iterate();]]></programlisting>
+
+ <programlisting><![CDATA[//named parameter list
+List names = new ArrayList();
+names.add("Izi");
+names.add("Fritz");
+Query q = sess.createQuery("from DomesticCat cat where cat.name in (:namesList)");
+q.setParameterList("namesList", names);
+List cats = q.list();]]></programlisting>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-pagination">
+ <title>Pagination</title>
+
+ <para>
+ If you need to specify bounds upon your result set (the maximum number of rows
+ you want to retrieve and / or the first row you want to retrieve) you should
+ use methods of the <literal>Query</literal> interface:
+ </para>
+
+ <programlisting><![CDATA[Query q = sess.createQuery("from DomesticCat cat");
+q.setFirstResult(20);
+q.setMaxResults(10);
+List cats = q.list();]]></programlisting>
+
+ <para>
+ Hibernate knows how to translate this limit query into the native
+ SQL of your DBMS.
+ </para>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-scrolling">
+ <title>Scrollable iteration</title>
+
+ <para>
+ If your JDBC driver supports scrollable <literal>ResultSet</literal>s, the
+ <literal>Query</literal> interface may be used to obtain a
+ <literal>ScrollableResults</literal> object, which allows flexible
+ navigation of the query results.
+ </para>
+
+ <programlisting><![CDATA[Query q = sess.createQuery("select cat.name, cat from DomesticCat cat " +
+ "order by cat.name");
+ScrollableResults cats = q.scroll();
+if ( cats.first() ) {
+
+ // find the first name on each page of an alphabetical list of cats by name
+ firstNamesOfPages = new ArrayList();
+ do {
+ String name = cats.getString(0);
+ firstNamesOfPages.add(name);
+ }
+ while ( cats.scroll(PAGE_SIZE) );
+
+ // Now get the first page of cats
+ pageOfCats = new ArrayList();
+ cats.beforeFirst();
+ int i=0;
+ while( ( PAGE_SIZE > i++ ) && cats.next() ) pageOfCats.add( cats.get(1) );
+
+}
+cats.close()]]></programlisting>
+
+ <para>
+ Note that an open database connection (and cursor) is required for this
+ functionality, use <literal>setMaxResult()</literal>/<literal>setFirstResult()</literal>
+ if you need offline pagination functionality.
+ </para>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-named" revision="1">
+ <title>Externalizing named queries</title>
+
+ <para>
+ You may also define named queries in the mapping document. (Remember to use a
+ <literal>CDATA</literal> section if your query contains characters that could
+ be interpreted as markup.)
+ </para>
+
+ <programlisting><![CDATA[<query name="ByNameAndMaximumWeight"><![CDATA[
+ from eg.DomesticCat as cat
+ where cat.name = ?
+ and cat.weight > ?
+] ]></query>]]></programlisting>
+
+ <para>
+ Parameter binding and executing is done programatically:
+ </para>
+
+ <programlisting><![CDATA[Query q = sess.getNamedQuery("ByNameAndMaximumWeight");
+q.setString(0, name);
+q.setInt(1, minWeight);
+List cats = q.list();]]></programlisting>
+
+ <para>
+ Note that the actual program code is independent of the query language that
+ is used, you may also define native SQL queries in metadata, or migrate
+ existing queries to Hibernate by placing them in mapping files.
+ </para>
+
+ <para>
+ Also note that a query declaration inside a <literal><hibernate-mapping></literal>
+ element requires a global unique name for the query, while a query declaration inside a
+ <literal><class></literal> element is made unique automatically by prepending the
+ fully qualified name of the class, for example
+ <literal>eg.Cat.ByNameAndMaximumWeight</literal>.
+ </para>
+
+ </sect3>
+
+ </sect2>
+
+ <sect2 id="objectstate-filtering" revision="1">
+ <title>Filtering collections</title>
+ <para>
+ A collection <emphasis>filter</emphasis> is a special type of query that may be applied to
+ a persistent collection or array. The query string may refer to <literal>this</literal>,
+ meaning the current collection element.
+ </para>
+
+ <programlisting><![CDATA[Collection blackKittens = session.createFilter(
+ pk.getKittens(),
+ "where this.color = ?")
+ .setParameter( Color.BLACK, Hibernate.custom(ColorUserType.class) )
+ .list()
+);]]></programlisting>
+
+ <para>
+ The returned collection is considered a bag, and it's a copy of the given
+ collection. The original collection is not modified (this is contrary to
+ the implication of the name "filter", but consistent with expected behavior).
+ </para>
+
+ <para>
+ Observe that filters do not require a <literal>from</literal> clause (though they may have
+ one if required). Filters are not limited to returning the collection elements themselves.
+ </para>
+
+ <programlisting><![CDATA[Collection blackKittenMates = session.createFilter(
+ pk.getKittens(),
+ "select this.mate where this.color = eg.Color.BLACK.intValue")
+ .list();]]></programlisting>
+
+ <para>
+ Even an empty filter query is useful, e.g. to load a subset of elements in a
+ huge collection:
+ </para>
+
+ <programlisting><![CDATA[Collection tenKittens = session.createFilter(
+ mother.getKittens(), "")
+ .setFirstResult(0).setMaxResults(10)
+ .list();]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="objecstate-querying-criteria" revision="1">
+ <title>Criteria queries</title>
+
+ <para>
+ HQL is extremely powerful but some developers prefer to build queries dynamically,
+ using an object-oriented API, rather than building query strings. Hibernate provides
+ an intuitive <literal>Criteria</literal> query API for these cases:
+ </para>
+
+ <programlisting><![CDATA[Criteria crit = session.createCriteria(Cat.class);
+crit.add( Expression.eq( "color", eg.Color.BLACK ) );
+crit.setMaxResults(10);
+List cats = crit.list();]]></programlisting>
+
+ <para>
+ The <literal>Criteria</literal> and the associated <literal>Example</literal>
+ API are discussed in more detail in <xref linkend="querycriteria"/>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="objectstate-querying-nativesql" revision="2">
+ <title>Queries in native SQL</title>
+
+ <para>
+ You may express a query in SQL, using <literal>createSQLQuery()</literal> and
+ let Hibernate take care of the mapping from result sets to objects. Note
+ that you may at any time call <literal>session.connection()</literal> and
+ use the JDBC <literal>Connection</literal> directly. If you chose to use the
+ Hibernate API, you must enclose SQL aliases in braces:
+ </para>
+
+ <programlisting><![CDATA[List cats = session.createSQLQuery(
+ "SELECT {cat.*} FROM CAT {cat} WHERE ROWNUM<10",
+ "cat",
+ Cat.class
+).list();]]></programlisting>
+
+ <programlisting><![CDATA[List cats = session.createSQLQuery(
+ "SELECT {cat}.ID AS {cat.id}, {cat}.SEX AS {cat.sex}, " +
+ "{cat}.MATE AS {cat.mate}, {cat}.SUBCLASS AS {cat.class}, ... " +
+ "FROM CAT {cat} WHERE ROWNUM<10",
+ "cat",
+ Cat.class
+).list()]]></programlisting>
+
+ <para>
+ SQL queries may contain named and positional parameters, just like Hibernate queries.
+ More information about native SQL queries in Hibernate can be found in
+ <xref linkend="querysql"/>.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="objectstate-modifying" revision="1">
+ <title>Modifying persistent objects</title>
+
+ <para>
+ <emphasis>Transactional persistent instances</emphasis> (ie. objects loaded, saved, created or
+ queried by the <literal>Session</literal>) may be manipulated by the application
+ and any changes to persistent state will be persisted when the <literal>Session</literal>
+ is <emphasis>flushed</emphasis> (discussed later in this chapter). There is no need
+ to call a particular method (like <literal>update()</literal>, which has a different
+ purpose) to make your modifications persistent. So the most straightforward way to update
+ the state of an object is to <literal>load()</literal> it,
+ and then manipulate it directly, while the <literal>Session</literal> is open:
+ </para>
+
+ <programlisting><![CDATA[DomesticCat cat = (DomesticCat) sess.load( Cat.class, new Long(69) );
+cat.setName("PK");
+sess.flush(); // changes to cat are automatically detected and persisted]]></programlisting>
+
+ <para>
+ Sometimes this programming model is inefficient since it would require both an SQL
+ <literal>SELECT</literal> (to load an object) and an SQL <literal>UPDATE</literal>
+ (to persist its updated state) in the same session. Therefore Hibernate offers an
+ alternate approach, using detached instances.
+ </para>
+
+ <para>
+ <emphasis>Note that Hibernate does not offer its own API for direct execution of
+ <literal>UPDATE</literal> or <literal>DELETE</literal> statements. Hibernate is a
+ <emphasis>state management</emphasis> service, you don't have to think in
+ <emphasis>statements</emphasis> to use it. JDBC is a perfect API for executing
+ SQL statements, you can get a JDBC <literal>Connection</literal> at any time
+ by calling <literal>session.connection()</literal>. Furthermore, the notion
+ of mass operations conflicts with object/relational mapping for online
+ transaction processing-oriented applications. Future versions of Hibernate
+ may however provide special mass operation functions. See <xref linkend="batch"/>
+ for some possible batch operation tricks.</emphasis>
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-detached" revision="2">
+ <title>Modifying detached objects</title>
+
+ <para>
+ Many applications need to retrieve an object in one transaction, send it to the
+ UI layer for manipulation, then save the changes in a new transaction.
+ Applications that use this kind of approach in a high-concurrency environment
+ usually use versioned data to ensure isolation for the "long" unit of work.
+ </para>
+
+ <para>
+ Hibernate supports this model by providing for reattachment of detached instances
+ using the <literal>Session.update()</literal> or <literal>Session.merge()</literal>
+ methods:
+ </para>
+
+ <programlisting><![CDATA[// in the first session
+Cat cat = (Cat) firstSession.load(Cat.class, catId);
+Cat potentialMate = new Cat();
+firstSession.save(potentialMate);
+
+// in a higher layer of the application
+cat.setMate(potentialMate);
+
+// later, in a new session
+secondSession.update(cat); // update cat
+secondSession.update(mate); // update mate]]></programlisting>
+
+ <para>
+ If the <literal>Cat</literal> with identifier <literal>catId</literal> had already
+ been loaded by <literal>secondSession</literal> when the application tried to
+ reattach it, an exception would have been thrown.
+ </para>
+
+ <para>
+ Use <literal>update()</literal> if you are sure that the session does
+ not contain an already persistent instance with the same identifier, and
+ <literal>merge()</literal> if you want to merge your modifications at any time
+ without consideration of the state of the session. In other words, <literal>update()</literal>
+ is usually the first method you would call in a fresh session, ensuring that
+ reattachment of your detached instances is the first operation that is executed.
+ </para>
+
+ <para>
+ The application should individually <literal>update()</literal> detached instances
+ reachable from the given detached instance if and <emphasis>only</emphasis> if it wants
+ their state also updated. This can be automated of course, using <emphasis>transitive
+ persistence</emphasis>, see <xref linkend="objectstate-transitive"/>.
+ </para>
+
+ <para>
+ The <literal>lock()</literal> method also allows an application to reassociate
+ an object with a new session. However, the detached instance has to be unmodified!
+ </para>
+
+ <programlisting><![CDATA[//just reassociate:
+sess.lock(fritz, LockMode.NONE);
+//do a version check, then reassociate:
+sess.lock(izi, LockMode.READ);
+//do a version check, using SELECT ... FOR UPDATE, then reassociate:
+sess.lock(pk, LockMode.UPGRADE);]]></programlisting>
+
+ <para>
+ Note that <literal>lock()</literal> can be used with various
+ <literal>LockMode</literal>s, see the API documentation and the
+ chapter on transaction handling for more information. Reattachment is not
+ the only usecase for <literal>lock()</literal>.
+ </para>
+
+ <para>
+ Other models for long units of work are discussed in <xref linkend="transactions-optimistic"/>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-saveorupdate">
+ <title>Automatic state detection</title>
+
+ <para>
+ Hibernate users have requested a general purpose method that either saves a
+ transient instance by generating a new identifier or updates/reattaches
+ the detached instances associated with its current identifier.
+ The <literal>saveOrUpdate()</literal> method implements this functionality.
+ </para>
+
+ <programlisting><![CDATA[// in the first session
+Cat cat = (Cat) firstSession.load(Cat.class, catID);
+
+// in a higher tier of the application
+Cat mate = new Cat();
+cat.setMate(mate);
+
+// later, in a new session
+secondSession.saveOrUpdate(cat); // update existing state (cat has a non-null id)
+secondSession.saveOrUpdate(mate); // save the new instance (mate has a null id)]]></programlisting>
+
+ <para>
+ The usage and semantics of <literal>saveOrUpdate()</literal> seems to be confusing
+ for new users. Firstly, so long as you are not trying to use instances from one session
+ in another new session, you should not need to use <literal>update()</literal>,
+ <literal>saveOrUpdate()</literal>, or <literal>merge()</literal>. Some whole
+ applications will never use either of these methods.
+ </para>
+
+ <para>
+ Usually <literal>update()</literal> or <literal>saveOrUpdate()</literal> are used in
+ the following scenario:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ the application loads an object in the first session
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ the object is passed up to the UI tier
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ some modifications are made to the object
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ the object is passed back down to the business logic tier
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ the application persists these modifications by calling
+ <literal>update()</literal> in a second session
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ <literal>saveOrUpdate()</literal> does the following:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ if the object is already persistent in this session, do nothing
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ if another object associated with the session has the same identifier,
+ throw an exception
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ if the object has no identifier property, <literal>save()</literal> it
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ if the object's identifier has the value assigned to a newly instantiated
+ object, <literal>save()</literal> it
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ if the object is versioned (by a <literal><version></literal> or
+ <literal><timestamp></literal>), and the version property value
+ is the same value assigned to a newly instantiated object,
+ <literal>save()</literal> it
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ otherwise <literal>update()</literal> the object
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ and <literal>merge()</literal> is very different:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ if there is a persistent instance with the same identifier currently
+ associated with the session, copy the state of the given object onto
+ the persistent instance
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ if there is no persistent instance currently associated with the session,
+ try to load it from the database, or create a new persistent instance
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ the persistent instance is returned
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ the given instance does not become associated with the session, it
+ remains detached
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect1>
+
+ <sect1 id="objectstate-deleting" revision="1">
+ <title>Deleting persistent objects</title>
+
+ <para>
+ <literal>Session.delete()</literal> will remove an object's state from the database.
+ Of course, your application might still hold a reference to a deleted object.
+ It's best to think of <literal>delete()</literal> as making a persistent instance
+ transient.
+ </para>
+
+ <programlisting><![CDATA[sess.delete(cat);]]></programlisting>
+
+ <para>
+ You may delete objects in any order you like, without risk of foreign key
+ constraint violations. It is still possible to violate a <literal>NOT
+ NULL</literal> constraint on a foreign key column by deleting objects in
+ the wrong order, e.g. if you delete the parent, but forget to delete the
+ children.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-replicating" revision="1">
+ <title>Replicating object between two different datastores</title>
+
+ <para>
+ It is occasionally useful to be able to take a graph of persistent instances
+ and make them persistent in a different datastore, without regenerating identifier
+ values.
+ </para>
+
+ <programlisting><![CDATA[//retrieve a cat from one database
+Session session1 = factory1.openSession();
+Transaction tx1 = session1.beginTransaction();
+Cat cat = session1.get(Cat.class, catId);
+tx1.commit();
+session1.close();
+
+//reconcile with a second database
+Session session2 = factory2.openSession();
+Transaction tx2 = session2.beginTransaction();
+session2.replicate(cat, ReplicationMode.LATEST_VERSION);
+tx2.commit();
+session2.close();]]></programlisting>
+
+ <para>
+ The <literal>ReplicationMode</literal> determines how <literal>replicate()</literal>
+ will deal with conflicts with existing rows in the database.
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>ReplicationMode.IGNORE</literal> - ignore the object when there is
+ an existing database row with the same identifier
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>ReplicationMode.OVERWRITE</literal> - overwrite any existing database
+ row with the same identifier
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>ReplicationMode.EXCEPTION</literal> - throw an exception if there is
+ an existing database row with the same identifier
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>ReplicationMode.LATEST_VERSION</literal> - overwrite the row if its
+ version number is earlier than the version number of the object, or ignore
+ the object otherwise
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Usecases for this feature include reconciling data entered into different database
+ instances, upgrading system configuration information during product upgrades,
+ rolling back changes made during non-ACID transactions and more.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-flushing">
+ <title>Flushing the Session</title>
+
+ <para>
+ From time to time the <literal>Session</literal> will execute the SQL statements
+ needed to synchronize the JDBC connection's state with the state of objects held in
+ memory. This process, <emphasis>flush</emphasis>, occurs by default at the following
+ points
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ before some query executions
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ from <literal>org.hibernate.Transaction.commit()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ from <literal>Session.flush()</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ The SQL statements are issued in the following order
+ </para>
+
+ <orderedlist spacing="compact">
+ <listitem>
+ <para>
+ all entity insertions, in the same order the corresponding objects
+ were saved using <literal>Session.save()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ all entity updates
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ all collection deletions
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ all collection element deletions, updates and insertions
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ all collection insertions
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ all entity deletions, in the same order the corresponding objects
+ were deleted using <literal>Session.delete()</literal>
+ </para>
+ </listitem>
+ </orderedlist>
+
+ <para>
+ (An exception is that objects using <literal>native</literal> ID generation are
+ inserted when they are saved.)
+ </para>
+
+ <para>
+ Except when you explicity <literal>flush()</literal>, there are absolutely no
+ guarantees about <emphasis>when</emphasis> the <literal>Session</literal> executes
+ the JDBC calls, only the <emphasis>order</emphasis> in which they are executed.
+ However, Hibernate does guarantee that the <literal>Query.list(..)</literal>
+ will never return stale data; nor will they return the wrong data.
+ </para>
+
+ <para>
+ It is possible to change the default behavior so that flush occurs less frequently.
+ The <literal>FlushMode</literal> class defines three different modes: only flush
+ at commit time (and only when the Hibernate <literal>Transaction</literal> API
+ is used), flush automatically using the explained routine, or never flush unless
+ <literal>flush()</literal> is called explicitly. The last mode is useful for long running
+ units of work, where a <literal>Session</literal> is kept open and disconnected for
+ a long time (see <xref linkend="transactions-optimistic-longsession"/>).
+ </para>
+
+ <programlisting><![CDATA[sess = sf.openSession();
+Transaction tx = sess.beginTransaction();
+sess.setFlushMode(FlushMode.COMMIT); // allow queries to return stale state
+
+Cat izi = (Cat) sess.load(Cat.class, id);
+izi.setName(iznizi);
+
+// might return stale data
+sess.find("from Cat as cat left outer join cat.kittens kitten");
+
+// change to izi is not flushed!
+...
+tx.commit(); // flush occurs
+sess.close();]]></programlisting>
+
+ <para>
+ During flush, an exception might occur (e.g. if a DML operation violates a constraint).
+ Since handling exceptions involves some understanding of Hibernate's transactional
+ behavior, we discuss it in <xref linkend="transactions"/>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-transitive" revision="1">
+ <title>Transitive persistence</title>
+
+ <para>
+ It is quite cumbersome to save, delete, or reattach individual objects,
+ especially if you deal with a graph of associated objects. A common case is
+ a parent/child relationship. Consider the following example:
+ </para>
+
+ <para>
+ If the children in a parent/child relationship would be value typed (e.g. a collection
+ of addresses or strings), their lifecycle would depend on the parent and no
+ further action would be required for convenient "cascading" of state changes.
+ When the parent is saved, the value-typed child objects are saved as
+ well, when the parent is deleted, the children will be deleted, etc. This
+ even works for operations such as the removal of a child from the collection;
+ Hibernate will detect this and, since value-typed objects can't have shared
+ references, delete the child from the database.
+ </para>
+
+ <para>
+ Now consider the same scenario with parent and child objects being entities,
+ not value-types (e.g. categories and items, or parent and child cats). Entities
+ have their own lifecycle, support shared references (so removing an entity from
+ the collection does not mean it can be deleted), and there is by default no
+ cascading of state from one entity to any other associated entities. Hibernate
+ does not implement <emphasis>persistence by reachability</emphasis> by default.
+ </para>
+
+ <para>
+ For each basic operation of the Hibernate session - including <literal>persist(), merge(),
+ saveOrUpdate(), delete(), lock(), refresh(), evict(), replicate()</literal> - there is a
+ corresponding cascade style. Respectively, the cascade styles are named <literal>create,
+ merge, save-update, delete, lock, refresh, evict, replicate</literal>. If you want an
+ operation to be cascaded along an association, you must indicate that in the mapping
+ document. For example:
+ </para>
+
+ <programlisting><![CDATA[<one-to-one name="person" cascade="persist"/>]]></programlisting>
+
+ <para>
+ Cascade styles my be combined:
+ </para>
+
+ <programlisting><![CDATA[<one-to-one name="person" cascade="persist,delete,lock"/>]]></programlisting>
+
+ <para>
+ You may even use <literal>cascade="all"</literal> to specify that <emphasis>all</emphasis>
+ operations should be cascaded along the association. The default <literal>cascade="none"</literal>
+ specifies that no operations are to be cascaded.
+ </para>
+
+ <para>
+ A special cascade style, <literal>delete-orphan</literal>, applies only to one-to-many
+ associations, and indicates that the <literal>delete()</literal> operation should
+ be applied to any child object that is removed from the association.
+ </para>
+
+
+ <para>
+ Recommendations:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ It doesn't usually make sense to enable cascade on a <literal><many-to-one></literal>
+ or <literal><many-to-many></literal> association. Cascade is often useful for
+ <literal><one-to-one></literal> and <literal><one-to-many></literal>
+ associations.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ If the child object's lifespan is bounded by the lifespan of the of the parent
+ object make it a <emphasis>lifecycle object</emphasis> by specifying
+ <literal>cascade="all,delete-orphan"</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Otherwise, you might not need cascade at all. But if you think that you will often be
+ working with the parent and children together in the same transaction, and you want to save
+ yourself some typing, consider using <literal>cascade="persist,merge,save-update"</literal>.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Mapping an association (either a single valued association, or a collection) with
+ <literal>cascade="all"</literal> marks the association as a
+ <emphasis>parent/child</emphasis> style relationship where save/update/delete of the
+ parent results in save/update/delete of the child or children.
+ </para>
+ <para>
+ Futhermore, a mere reference to a child from a persistent parent will result in
+ save/update of the child. This metaphor is incomplete, however. A child which becomes
+ unreferenced by its parent is <emphasis>not</emphasis> automatically deleted, except
+ in the case of a <literal><one-to-many></literal> association mapped with
+ <literal>cascade="delete-orphan"</literal>. The precise semantics of cascading
+ operations for a parent/child relationship are as follows:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ If a parent is passed to <literal>persist()</literal>, all children are passed to
+ <literal>persist()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ If a parent is passed to <literal>merge()</literal>, all children are passed to
+ <literal>merge()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ If a parent is passed to <literal>save()</literal>, <literal>update()</literal> or
+ <literal>saveOrUpdate()</literal>, all children are passed to <literal>saveOrUpdate()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ If a transient or detached child becomes referenced by a persistent parent,
+ it is passed to <literal>saveOrUpdate()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ If a parent is deleted, all children are passed to <literal>delete()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ If a child is dereferenced by a persistent parent, <emphasis>nothing
+ special happens</emphasis> - the application should explicitly delete
+ the child if necessary - unless <literal>cascade="delete-orphan"</literal>,
+ in which case the "orphaned" child is deleted.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Finally, note that cascading of operations can be applied to an object graph at
+ <emphasis>call time</emphasis> or at <emphasis>flush time</emphasis>. All operations,
+ if enabled, are cascaded to associated entities reachable when the operation is
+ executed. However, <literal>save-upate</literal> and <literal>delete-orphan</literal>
+ are transitive for all associated entities reachable during flush of the
+ <literal>Session</literal>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-metadata">
+ <title>Using metadata</title>
+
+ <para>
+ Hibernate requires a very rich meta-level model of all entity and value types. From time
+ to time, this model is very useful to the application itself. For example, the application
+ might use Hibernate's metadata to implement a "smart" deep-copy algorithm that understands
+ which objects should be copied (eg. mutable value types) and which should not (eg.
+ immutable value types and, possibly, associated entities).
+ </para>
+ <para>
+ Hibernate exposes metadata via the <literal>ClassMetadata</literal> and
+ <literal>CollectionMetadata</literal> interfaces and the <literal>Type</literal>
+ hierarchy. Instances of the metadata interfaces may be obtained from the
+ <literal>SessionFactory</literal>.
+ </para>
+
+ <programlisting><![CDATA[Cat fritz = ......;
+ClassMetadata catMeta = sessionfactory.getClassMetadata(Cat.class);
+
+Object[] propertyValues = catMeta.getPropertyValues(fritz);
+String[] propertyNames = catMeta.getPropertyNames();
+Type[] propertyTypes = catMeta.getPropertyTypes();
+
+// get a Map of all properties which are not collections or associations
+Map namedValues = new HashMap();
+for ( int i=0; i<propertyNames.length; i++ ) {
+ if ( !propertyTypes[i].isEntityType() && !propertyTypes[i].isCollectionType() ) {
+ namedValues.put( propertyNames[i], propertyValues[i] );
+ }
+}]]></programlisting>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/toolset_guide.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/toolset_guide.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/toolset_guide.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,604 @@
+<chapter id="toolsetguide" revision="2">
+ <title>Toolset Guide</title>
+
+ <para>
+ Roundtrip engineering with Hibernate is possible using a set of Eclipse plugins,
+ commandline tools, as well as Ant tasks.
+ </para>
+
+ <para>
+ The <emphasis>Hibernate Tools</emphasis> currently include plugins for the Eclipse
+ IDE as well as Ant tasks for reverse engineering of existing databases:
+ </para>
+
+ <itemizedlist>
+ <listitem><para>
+ <emphasis>Mapping Editor:</emphasis> An editor for Hibernate XML mapping files,
+ supporting auto-completion and syntax highlighting. It also supports semantic
+ auto-completion for class names and property/field names, making it much more versatile than a normal XML editor.
+ </para></listitem>
+ <listitem><para>
+ <emphasis>Console:</emphasis> The console is a new view in Eclipse. In addition to
+ a tree overview of your console configurations, you also get an interactive view
+ of your persistent classes and their relationships. The console allows you to
+ execute HQL queries against your database and browse the result directly in
+ Eclipse.
+ </para></listitem>
+ <listitem><para>
+ <emphasis>Development Wizards:</emphasis> Several wizards are provided with the
+ Hibernate Eclipse tools; you can use a wizard to quickly generate Hibernate configuration
+ (cfg.xml) files, or you may even completely reverse engineer an existing database schema
+ into POJO source files and Hibernate mapping files. The reverse engineering wizard
+ supports customizable templates.
+ </para></listitem>
+ <listitem><para>
+ <emphasis>Ant Tasks:</emphasis>
+ </para></listitem>
+
+ </itemizedlist>
+
+ <para>
+ Please refer to the <emphasis>Hibernate Tools</emphasis> package and it's documentation
+ for more information.
+ </para>
+
+ <para>
+ However, the Hibernate main package comes bundled with an integrated tool (it can even
+ be used from "inside" Hibernate on-the-fly): <emphasis>SchemaExport</emphasis> aka
+ <literal>hbm2ddl</literal>.
+ </para>
+
+ <sect1 id="toolsetguide-s1" revision="2">
+ <title>Automatic schema generation</title>
+
+ <para>
+ DDL may be generated from your mapping files by a Hibernate utility. The generated
+ schema includes referential integrity constraints (primary and foreign keys) for
+ entity and collection tables. Tables and sequences are also created for mapped
+ identifier generators.
+ </para>
+
+ <para>
+ You <emphasis>must</emphasis> specify a SQL <literal>Dialect</literal> via the
+ <literal>hibernate.dialect</literal> property when using this tool, as DDL
+ is highly vendor specific.
+ </para>
+
+ <para>
+ First, customize your mapping files to improve the generated schema.
+ </para>
+
+ <sect2 id="toolsetguide-s1-2" revision="3">
+ <title>Customizing the schema</title>
+
+ <para>
+ Many Hibernate mapping elements define optional attributes named <literal>length</literal>,
+ <literal>precision</literal> and <literal>scale</literal>. You may set the length, precision
+ and scale of a column with this attribute.
+
+ </para>
+
+ <programlisting><![CDATA[<property name="zip" length="5"/>]]></programlisting>
+ <programlisting><![CDATA[<property name="balance" precision="12" scale="2"/>]]></programlisting>
+
+ <para>
+ Some tags also accept a <literal>not-null</literal> attribute (for generating a
+ <literal>NOT NULL</literal> constraint on table columns) and a <literal>unique</literal>
+ attribute (for generating <literal>UNIQUE</literal> constraint on table columns).
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="bar" column="barId" not-null="true"/>]]></programlisting>
+
+ <programlisting><![CDATA[<element column="serialNumber" type="long" not-null="true" unique="true"/>]]></programlisting>
+
+ <para>
+ A <literal>unique-key</literal> attribute may be used to group columns in
+ a single unique key constraint. Currently, the specified value of the
+ <literal>unique-key</literal> attribute is <emphasis>not</emphasis> used
+ to name the constraint in the generated DDL, only to group the columns in
+ the mapping file.
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="org" column="orgId" unique-key="OrgEmployeeId"/>
+<property name="employeeId" unique-key="OrgEmployee"/>]]></programlisting>
+
+ <para>
+ An <literal>index</literal> attribute specifies the name of an index that
+ will be created using the mapped column or columns. Multiple columns may be
+ grouped into the same index, simply by specifying the same index name.
+ </para>
+
+ <programlisting><![CDATA[<property name="lastName" index="CustName"/>
+<property name="firstName" index="CustName"/>]]></programlisting>
+
+ <para>
+ A <literal>foreign-key</literal> attribute may be used to override the name
+ of any generated foreign key constraint.
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="bar" column="barId" foreign-key="FKFooBar"/>]]></programlisting>
+
+ <para>
+ Many mapping elements also accept a child <literal><column></literal> element.
+ This is particularly useful for mapping multi-column types:
+ </para>
+
+ <programlisting><![CDATA[<property name="name" type="my.customtypes.Name"/>
+ <column name="last" not-null="true" index="bar_idx" length="30"/>
+ <column name="first" not-null="true" index="bar_idx" length="20"/>
+ <column name="initial"/>
+</property>]]></programlisting>
+
+ <para>
+ The <literal>default</literal> attribute lets you specify a default value for
+ a column (you should assign the same value to the mapped property before
+ saving a new instance of the mapped class).
+ </para>
+
+ <programlisting><![CDATA[<property name="credits" type="integer" insert="false">
+ <column name="credits" default="10"/>
+</property>]]></programlisting>
+
+ <programlisting><![CDATA[<version name="version" type="integer" insert="false">
+ <column name="version" default="0"/>
+</property>]]></programlisting>
+
+ <para>
+ The <literal>sql-type</literal> attribute allows the user to override the default
+ mapping of a Hibernate type to SQL datatype.
+ </para>
+
+ <programlisting><![CDATA[<property name="balance" type="float">
+ <column name="balance" sql-type="decimal(13,3)"/>
+</property>]]></programlisting>
+
+ <para>
+ The <literal>check</literal> attribute allows you to specify a check constraint.
+ </para>
+
+ <programlisting><![CDATA[<property name="foo" type="integer">
+ <column name="foo" check="foo > 10"/>
+</property>]]></programlisting>
+
+ <programlisting><![CDATA[<class name="Foo" table="foos" check="bar < 100.0">
+ ...
+ <property name="bar" type="float"/>
+</class>]]></programlisting>
+
+
+ <table frame="topbot" id="schemattributes-summary" revision="2">
+ <title>Summary</title>
+ <tgroup cols="3">
+ <colspec colwidth="1*"/>
+ <colspec colwidth="1*"/>
+ <colspec colwidth="2.5*"/>
+ <thead>
+ <row>
+ <entry>Attribute</entry>
+ <entry>Values</entry>
+ <entry>Interpretation</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>length</literal></entry>
+ <entry>number</entry>
+ <entry>column length</entry>
+ </row>
+ <row>
+ <entry><literal>precision</literal></entry>
+ <entry>number</entry>
+ <entry>column decimal precision</entry>
+ </row>
+ <row>
+ <entry><literal>scale</literal></entry>
+ <entry>number</entry>
+ <entry>column decimal scale</entry>
+ </row>
+ <row>
+ <entry><literal>not-null</literal></entry>
+ <entry><literal>true|false</literal></entry>
+ <entry>specfies that the column should be non-nullable</entry>
+ </row>
+ <row>
+ <entry><literal>unique</literal></entry>
+ <entry><literal>true|false</literal></entry>
+ <entry>specifies that the column should have a unique constraint</entry>
+ </row>
+ <row>
+ <entry><literal>index</literal></entry>
+ <entry><literal>index_name</literal></entry>
+ <entry>specifies the name of a (multi-column) index</entry>
+ </row>
+ <row>
+ <entry><literal>unique-key</literal></entry>
+ <entry><literal>unique_key_name</literal></entry>
+ <entry>specifies the name of a multi-column unique constraint</entry>
+ </row>
+ <row>
+ <entry><literal>foreign-key</literal></entry>
+ <entry><literal>foreign_key_name</literal></entry>
+ <entry>
+ specifies the name of the foreign key constraint generated
+ for an association, for a <literal><one-to-one></literal>,
+ <literal><many-to-one></literal>, <literal><key></literal>,
+ or <literal><many-to-many></literal> mapping element. Note that
+ <literal>inverse="true"</literal> sides will not be considered
+ by <literal>SchemaExport</literal>.
+ </entry>
+ </row>
+ <row>
+ <entry><literal>sql-type</literal></entry>
+ <entry><literal>SQL column type</literal></entry>
+ <entry>
+ overrides the default column type (attribute of
+ <literal><column></literal> element only)
+ </entry>
+ </row>
+ <row>
+ <entry><literal>default</literal></entry>
+ <entry>SQL expression</entry>
+ <entry>
+ specify a default value for the column
+ </entry>
+ </row>
+ <row>
+ <entry><literal>check</literal></entry>
+ <entry>SQL expression</entry>
+ <entry>
+ create an SQL check constraint on either column or table
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ The <literal><comment></literal> element allows you to specify comments
+ for the generated schema.
+ </para>
+
+ <programlisting><![CDATA[<class name="Customer" table="CurCust">
+ <comment>Current customers only</comment>
+ ...
+</class>]]></programlisting>
+
+ <programlisting><![CDATA[<property name="balance">
+ <column name="bal">
+ <comment>Balance in USD</comment>
+ </column>
+</property>]]></programlisting>
+
+ <para>
+ This results in a <literal>comment on table</literal> or
+ <literal>comment on column</literal> statement in the generated
+ DDL (where supported).
+ </para>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-3" revision="2">
+ <title>Running the tool</title>
+
+ <para>
+ The <literal>SchemaExport</literal> tool writes a DDL script to standard out and/or
+ executes the DDL statements.
+ </para>
+
+ <para>
+ <literal>java -cp </literal><emphasis>hibernate_classpaths</emphasis>
+ <literal>org.hibernate.tool.hbm2ddl.SchemaExport</literal> <emphasis>options mapping_files</emphasis>
+ </para>
+
+ <table frame="topbot">
+ <title><literal>SchemaExport</literal> Command Line Options</title>
+ <tgroup cols="2">
+ <colspec colwidth="1.5*"/>
+ <colspec colwidth="2*"/>
+ <thead>
+ <row>
+ <entry>Option</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>--quiet</literal></entry>
+ <entry>don't output the script to stdout</entry>
+ </row>
+ <row>
+ <entry><literal>--drop</literal></entry>
+ <entry>only drop the tables</entry>
+ </row>
+ <row>
+ <entry><literal>--create</literal></entry>
+ <entry>only create the tables</entry>
+ </row>
+ <row>
+ <entry><literal>--text</literal></entry>
+ <entry>don't export to the database</entry>
+ </row>
+ <row>
+ <entry><literal>--output=my_schema.ddl</literal></entry>
+ <entry>output the ddl script to a file</entry>
+ </row>
+ <row>
+ <entry><literal>--naming=eg.MyNamingStrategy</literal></entry>
+ <entry>select a <literal>NamingStrategy</literal></entry>
+ </row>
+ <row>
+ <entry><literal>--config=hibernate.cfg.xml</literal></entry>
+ <entry>read Hibernate configuration from an XML file</entry>
+ </row>
+ <row>
+ <entry><literal>--properties=hibernate.properties</literal></entry>
+ <entry>read database properties from a file</entry>
+ </row>
+ <row>
+ <entry><literal>--format</literal></entry>
+ <entry>format the generated SQL nicely in the script</entry>
+ </row>
+ <row>
+ <entry><literal>--delimiter=;</literal></entry>
+ <entry>set an end of line delimiter for the script</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ You may even embed <literal>SchemaExport</literal> in your application:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = ....;
+new SchemaExport(cfg).create(false, true);]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-4">
+ <title>Properties</title>
+
+ <para>
+ Database properties may be specified
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>as system properties with <literal>-D</literal><emphasis><property></emphasis></para>
+ </listitem>
+ <listitem>
+ <para>in <literal>hibernate.properties</literal></para>
+ </listitem>
+ <listitem>
+ <para>in a named properties file with <literal>--properties</literal></para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ The needed properties are:
+ </para>
+
+ <table frame="topbot">
+ <title>SchemaExport Connection Properties</title>
+ <tgroup cols="2">
+ <colspec colwidth="1.5*"/>
+ <colspec colwidth="2*"/>
+ <thead>
+ <row>
+ <entry>Property Name</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>hibernate.connection.driver_class</literal></entry>
+ <entry>jdbc driver class</entry>
+ </row>
+ <row>
+ <entry><literal>hibernate.connection.url</literal></entry>
+ <entry>jdbc url</entry>
+ </row>
+ <row>
+ <entry><literal>hibernate.connection.username</literal></entry>
+ <entry>database user</entry>
+ </row>
+ <row>
+ <entry><literal>hibernate.connection.password</literal></entry>
+ <entry>user password</entry>
+ </row>
+ <row>
+ <entry><literal>hibernate.dialect</literal></entry>
+ <entry>dialect</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-5">
+ <title>Using Ant</title>
+
+ <para>
+ You can call <literal>SchemaExport</literal> from your Ant build script:
+ </para>
+
+ <programlisting><![CDATA[<target name="schemaexport">
+ <taskdef name="schemaexport"
+ classname="org.hibernate.tool.hbm2ddl.SchemaExportTask"
+ classpathref="class.path"/>
+
+ <schemaexport
+ properties="hibernate.properties"
+ quiet="no"
+ text="no"
+ drop="no"
+ delimiter=";"
+ output="schema-export.sql">
+ <fileset dir="src">
+ <include name="**/*.hbm.xml"/>
+ </fileset>
+ </schemaexport>
+</target>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-6" revision="2">
+ <title>Incremental schema updates</title>
+
+ <para>
+ The <literal>SchemaUpdate</literal> tool will update an existing schema with "incremental" changes.
+ Note that <literal>SchemaUpdate</literal> depends heavily upon the JDBC metadata API, so it will
+ not work with all JDBC drivers.
+ </para>
+
+ <para>
+ <literal>java -cp </literal><emphasis>hibernate_classpaths</emphasis>
+ <literal>org.hibernate.tool.hbm2ddl.SchemaUpdate</literal> <emphasis>options mapping_files</emphasis>
+ </para>
+
+ <table frame="topbot">
+ <title><literal>SchemaUpdate</literal> Command Line Options</title>
+ <tgroup cols="2">
+ <colspec colwidth="1.5*"/>
+ <colspec colwidth="2*"/>
+ <thead>
+ <row>
+ <entry>Option</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>--quiet</literal></entry>
+ <entry>don't output the script to stdout</entry>
+ </row>
+ <row>
+ <entry><literal>--text</literal></entry>
+ <entry>don't export the script to the database</entry>
+ </row>
+ <row>
+ <entry><literal>--naming=eg.MyNamingStrategy</literal></entry>
+ <entry>select a <literal>NamingStrategy</literal></entry>
+ </row>
+ <row>
+ <entry><literal>--properties=hibernate.properties</literal></entry>
+ <entry>read database properties from a file</entry>
+ </row>
+ <row>
+ <entry><literal>--config=hibernate.cfg.xml</literal></entry>
+ <entry>specify a <literal>.cfg.xml</literal> file</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ You may embed <literal>SchemaUpdate</literal> in your application:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = ....;
+new SchemaUpdate(cfg).execute(false);]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-7">
+ <title>Using Ant for incremental schema updates</title>
+
+ <para>
+ You can call <literal>SchemaUpdate</literal> from the Ant script:
+ </para>
+
+ <programlisting><![CDATA[<target name="schemaupdate">
+ <taskdef name="schemaupdate"
+ classname="org.hibernate.tool.hbm2ddl.SchemaUpdateTask"
+ classpathref="class.path"/>
+
+ <schemaupdate
+ properties="hibernate.properties"
+ quiet="no">
+ <fileset dir="src">
+ <include name="**/*.hbm.xml"/>
+ </fileset>
+ </schemaupdate>
+</target>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-8" revision="1">
+ <title>Schema validation</title>
+
+ <para>
+ The <literal>SchemaValidator</literal> tool will validate that the existing database schema "matches"
+ your mapping documents. Note that <literal>SchemaValidator</literal> depends heavily upon the JDBC
+ metadata API, so it will not work with all JDBC drivers. This tool is extremely useful for testing.
+ </para>
+
+ <para>
+ <literal>java -cp </literal><emphasis>hibernate_classpaths</emphasis>
+ <literal>org.hibernate.tool.hbm2ddl.SchemaValidator</literal> <emphasis>options mapping_files</emphasis>
+ </para>
+
+ <table frame="topbot">
+ <title><literal>SchemaValidator</literal> Command Line Options</title>
+ <tgroup cols="2">
+ <colspec colwidth="1.5*"/>
+ <colspec colwidth="2*"/>
+ <thead>
+ <row>
+ <entry>Option</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>--naming=eg.MyNamingStrategy</literal></entry>
+ <entry>select a <literal>NamingStrategy</literal></entry>
+ </row>
+ <row>
+ <entry><literal>--properties=hibernate.properties</literal></entry>
+ <entry>read database properties from a file</entry>
+ </row>
+ <row>
+ <entry><literal>--config=hibernate.cfg.xml</literal></entry>
+ <entry>specify a <literal>.cfg.xml</literal> file</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ You may embed <literal>SchemaValidator</literal> in your application:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = ....;
+new SchemaValidator(cfg).validate();]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-9">
+ <title>Using Ant for schema validation</title>
+
+ <para>
+ You can call <literal>SchemaValidator</literal> from the Ant script:
+ </para>
+
+ <programlisting><![CDATA[<target name="schemavalidate">
+ <taskdef name="schemavalidator"
+ classname="org.hibernate.tool.hbm2ddl.SchemaValidatorTask"
+ classpathref="class.path"/>
+
+ <schemavalidator
+ properties="hibernate.properties">
+ <fileset dir="src">
+ <include name="**/*.hbm.xml"/>
+ </fileset>
+ </schemaupdate>
+</target>]]></programlisting>
+
+ </sect2>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/transactions.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/transactions.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/transactions.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1109 @@
+<chapter id="transactions" revision="2">
+ <title>Transactions And Concurrency</title>
+
+ <para>
+ The most important point about Hibernate and concurrency control is that it is very
+ easy to understand. Hibernate directly uses JDBC connections and JTA resources without
+ adding any additional locking behavior. We highly recommend you spend some time with the
+ JDBC, ANSI, and transaction isolation specification of your database management system.
+ </para>
+
+ <para>
+ Hibernate does not lock objects in memory. Your application can expect the behavior as
+ defined by the isolation level of your database transactions. Note that thanks to the
+ <literal>Session</literal>, which is also a transaction-scoped cache, Hibernate
+ provides repeatable reads for lookup by identifier and entity queries (not
+ reporting queries that return scalar values).
+ </para>
+
+ <para>
+ In addition to versioning for automatic optimistic concurrency control, Hibernate also
+ offers a (minor) API for pessimistic locking of rows, using the
+ <literal>SELECT FOR UPDATE</literal> syntax. Optimistic concurrency control and
+ this API are discussed later in this chapter.
+ </para>
+
+ <para>
+ We start the discussion of concurrency control in Hibernate with the granularity of
+ <literal>Configuration</literal>, <literal>SessionFactory</literal>, and
+ <literal>Session</literal>, as well as database transactions and long conversations.
+ </para>
+
+ <sect1 id="transactions-basics" revision="1">
+ <title>Session and transaction scopes</title>
+
+ <para>
+ A <literal>SessionFactory</literal> is an expensive-to-create, threadsafe object
+ intended to be shared by all application threads. It is created once, usually on
+ application startup, from a <literal>Configuration</literal> instance.
+ </para>
+
+ <para>
+ A <literal>Session</literal> is an inexpensive, non-threadsafe object that should be
+ used once, for a single request, a conversation, single unit of work, and then discarded.
+ A <literal>Session</literal> will not obtain a JDBC <literal>Connection</literal>
+ (or a <literal>Datasource</literal>) unless it is needed, hence consume no
+ resources until used.
+ </para>
+
+ <para>
+ To complete this picture you also have to think about database transactions. A
+ database transaction has to be as short as possible, to reduce lock contention in
+ the database. Long database transactions will prevent your application from scaling
+ to highly concurrent load. Hence, it is almost never good design to hold a
+ database transaction open during user think time, until the unit of work is
+ complete.
+ </para>
+
+ <para>
+ What is the scope of a unit of work? Can a single Hibernate <literal>Session</literal>
+ span several database transactions or is this a one-to-one relationship of scopes? When
+ should you open and close a <literal>Session</literal> and how do you demarcate the
+ database transaction boundaries?
+ </para>
+
+ <sect2 id="transactions-basics-uow" revision="1">
+ <title>Unit of work</title>
+
+ <para>
+ First, don't use the <emphasis>session-per-operation</emphasis> antipattern, that is,
+ don't open and close a <literal>Session</literal> for every simple database call in
+ a single thread! Of course, the same is true for database transactions. Database calls
+ in an application are made using a planned sequence, they are grouped into atomic
+ units of work. (Note that this also means that auto-commit after every single
+ SQL statement is useless in an application, this mode is intended for ad-hoc SQL
+ console work. Hibernate disables, or expects the application server to do so,
+ auto-commit mode immediately.) Database transactions are never optional, all
+ communication with a database has to occur inside a transaction, no matter if
+ you read or write data. As explained, auto-commit behavior for reading data
+ should be avoided, as many small transactions are unlikely to perform better than
+ one clearly defined unit of work. The latter is also much more maintainable
+ and extensible.
+ </para>
+
+ <para>
+ The most common pattern in a multi-user client/server application is
+ <emphasis>session-per-request</emphasis>. In this model, a request from the client
+ is send to the server (where the Hibernate persistence layer runs), a new Hibernate
+ <literal>Session</literal> is opened, and all database operations are executed in this unit
+ of work. Once the work has been completed (and the response for the client has been prepared),
+ the session is flushed and closed. You would also use a single database transaction to
+ serve the clients request, starting and committing it when you open and close the
+ <literal>Session</literal>. The relationship between the two is one-to-one and this
+ model is a perfect fit for many applications.
+ </para>
+
+ <para>
+ The challenge lies in the implementation. Hibernate provides built-in management of
+ the "current session" to simplify this pattern. All you have to do is start a
+ transaction when a server request has to be processed, and end the transaction
+ before the response is send to the client. You can do this in any way you
+ like, common solutions are <literal>ServletFilter</literal>, AOP interceptor with a
+ pointcut on the service methods, or a proxy/interception container. An EJB container
+ is a standardized way to implement cross-cutting aspects such as transaction
+ demarcation on EJB session beans, declaratively with CMT. If you decide to
+ use programmatic transaction demarcation, prefer the Hibernate <literal>Transaction</literal>
+ API shown later in this chapter, for ease of use and code portability.
+ </para>
+
+ <para>
+ Your application code can access a "current session" to process the request
+ by simply calling <literal>sessionFactory.getCurrentSession()</literal> anywhere
+ and as often as needed. You will always get a <literal>Session</literal> scoped
+ to the current database transaction. This has to be configured for either
+ resource-local or JTA environments, see <xref linkend="architecture-current-session"/>.
+ </para>
+
+ <para>
+ Sometimes it is convenient to extend the scope of a <literal>Session</literal> and
+ database transaction until the "view has been rendered". This is especially useful
+ in servlet applications that utilize a separate rendering phase after the request
+ has been processed. Extending the database transaction until view rendering is
+ complete is easy to do if you implement your own interceptor. However, it is not
+ easily doable if you rely on EJBs with container-managed transactions, as a
+ transaction will be completed when an EJB method returns, before rendering of any
+ view can start. See the Hibernate website and forum for tips and examples around
+ this <emphasis>Open Session in View</emphasis> pattern.
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-basics-apptx" revision="1">
+ <title>Long conversations</title>
+
+ <para>
+ The session-per-request pattern is not the only useful concept you can use to design
+ units of work. Many business processes require a whole series of interactions with the user
+ interleaved with database accesses. In web and enterprise applications it is
+ not acceptable for a database transaction to span a user interaction. Consider the following
+ example:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ The first screen of a dialog opens, the data seen by the user has been loaded in
+ a particular <literal>Session</literal> and database transaction. The user is free to
+ modify the objects.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ The user clicks "Save" after 5 minutes and expects his modifications to be made
+ persistent; he also expects that he was the only person editing this information and
+ that no conflicting modification can occur.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ We call this unit of work, from the point of view of the user, a long running
+ <emphasis>conversation</emphasis> (or <emphasis>application transaction</emphasis>).
+ There are many ways how you can implement this in your application.
+ </para>
+
+ <para>
+ A first naive implementation might keep the <literal>Session</literal> and database
+ transaction open during user think time, with locks held in the database to prevent
+ concurrent modification, and to guarantee isolation and atomicity. This is of course
+ an anti-pattern, since lock contention would not allow the application to scale with
+ the number of concurrent users.
+ </para>
+
+ <para>
+ Clearly, we have to use several database transactions to implement the converastion.
+ In this case, maintaining isolation of business processes becomes the
+ partial responsibility of the application tier. A single conversation
+ usually spans several database transactions. It will be atomic if only one of
+ these database transactions (the last one) stores the updated data, all others
+ simply read data (e.g. in a wizard-style dialog spanning several request/response
+ cycles). This is easier to implement than it might sound, especially if
+ you use Hibernate's features:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Automatic Versioning</emphasis> - Hibernate can do automatic
+ optimistic concurrency control for you, it can automatically detect
+ if a concurrent modification occured during user think time. Usually
+ we only check at the end of the conversation.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Detached Objects</emphasis> - If you decide to use the already
+ discussed <emphasis>session-per-request</emphasis> pattern, all loaded instances
+ will be in detached state during user think time. Hibernate allows you to
+ reattach the objects and persist the modifications, the pattern is called
+ <emphasis>session-per-request-with-detached-objects</emphasis>. Automatic
+ versioning is used to isolate concurrent modifications.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Extended (or Long) Session</emphasis> - The Hibernate
+ <literal>Session</literal> may be disconnected from the underlying JDBC
+ connection after the database transaction has been committed, and reconnected
+ when a new client request occurs. This pattern is known as
+ <emphasis>session-per-conversation</emphasis> and makes
+ even reattachment unnecessary. Automatic versioning is used to isolate
+ concurrent modifications and the <literal>Session</literal> is usually
+ not allowed to be flushed automatically, but explicitely.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Both <emphasis>session-per-request-with-detached-objects</emphasis> and
+ <emphasis>session-per-conversation</emphasis> have advantages and disadvantages,
+ we discuss them later in this chapter in the context of optimistic concurrency control.
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-basics-identity">
+ <title>Considering object identity</title>
+
+ <para>
+ An application may concurrently access the same persistent state in two
+ different <literal>Session</literal>s. However, an instance of a persistent class
+ is never shared between two <literal>Session</literal> instances. Hence there are
+ two different notions of identity:
+ </para>
+
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term>Database Identity</term>
+ <listitem>
+ <para>
+ <literal>foo.getId().equals( bar.getId() )</literal>
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>JVM Identity</term>
+ <listitem>
+ <para>
+ <literal>foo==bar</literal>
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>
+ Then for objects attached to a <emphasis>particular</emphasis> <literal>Session</literal>
+ (i.e. in the scope of a <literal>Session</literal>) the two notions are equivalent, and
+ JVM identity for database identity is guaranteed by Hibernate. However, while the application
+ might concurrently access the "same" (persistent identity) business object in two different
+ sessions, the two instances will actually be "different" (JVM identity). Conflicts are
+ resolved using (automatic versioning) at flush/commit time, using an optimistic approach.
+ </para>
+
+ <para>
+ This approach leaves Hibernate and the database to worry about concurrency; it also provides
+ the best scalability, since guaranteeing identity in single-threaded units of work only doesn't
+ need expensive locking or other means of synchronization. The application never needs to
+ synchronize on any business object, as long as it sticks to a single thread per
+ <literal>Session</literal>. Within a <literal>Session</literal> the application may safely use
+ <literal>==</literal> to compare objects.
+ </para>
+
+ <para>
+ However, an application that uses <literal>==</literal> outside of a <literal>Session</literal>,
+ might see unexpected results. This might occur even in some unexpected places, for example,
+ if you put two detached instances into the same <literal>Set</literal>. Both might have the same
+ database identity (i.e. they represent the same row), but JVM identity is by definition not
+ guaranteed for instances in detached state. The developer has to override the <literal>equals()</literal>
+ and <literal>hashCode()</literal> methods in persistent classes and implement
+ his own notion of object equality. There is one caveat: Never use the database
+ identifier to implement equality, use a business key, a combination of unique, usually
+ immutable, attributes. The database identifier will change if a transient object is made
+ persistent. If the transient instance (usually together with detached instances) is held in a
+ <literal>Set</literal>, changing the hashcode breaks the contract of the <literal>Set</literal>.
+ Attributes for business keys don't have to be as stable as database primary keys, you only
+ have to guarantee stability as long as the objects are in the same <literal>Set</literal>. See
+ the Hibernate website for a more thorough discussion of this issue. Also note that this is not
+ a Hibernate issue, but simply how Java object identity and equality has to be implemented.
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-basics-issues">
+ <title>Common issues</title>
+
+ <para>
+ Never use the anti-patterns <emphasis>session-per-user-session</emphasis> or
+ <emphasis>session-per-application</emphasis> (of course, there are rare exceptions to
+ this rule). Note that some of the following issues might also appear with the recommended
+ patterns, make sure you understand the implications before making a design decision:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ A <literal>Session</literal> is not thread-safe. Things which are supposed to work
+ concurrently, like HTTP requests, session beans, or Swing workers, will cause race
+ conditions if a <literal>Session</literal> instance would be shared. If you keep your
+ Hibernate <literal>Session</literal> in your <literal>HttpSession</literal> (discussed
+ later), you should consider synchronizing access to your Http session. Otherwise,
+ a user that clicks reload fast enough may use the same <literal>Session</literal> in
+ two concurrently running threads.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ An exception thrown by Hibernate means you have to rollback your database transaction
+ and close the <literal>Session</literal> immediately (discussed later in more detail).
+ If your <literal>Session</literal> is bound to the application, you have to stop
+ the application. Rolling back the database transaction doesn't put your business
+ objects back into the state they were at the start of the transaction. This means the
+ database state and the business objects do get out of sync. Usually this is not a
+ problem, because exceptions are not recoverable and you have to start over after
+ rollback anyway.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ The <literal>Session</literal> caches every object that is in persistent state (watched
+ and checked for dirty state by Hibernate). This means it grows endlessly until you
+ get an OutOfMemoryException, if you keep it open for a long time or simply load too
+ much data. One solution for this is to call <literal>clear()</literal> and <literal>evict()</literal>
+ to manage the <literal>Session</literal> cache, but you most likely should consider a
+ Stored Procedure if you need mass data operations. Some solutions are shown in
+ <xref linkend="batch"/>. Keeping a <literal>Session</literal> open for the duration
+ of a user session also means a high probability of stale data.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="transactions-demarcation">
+ <title>Database transaction demarcation</title>
+
+ <para>
+ Datatabase (or system) transaction boundaries are always necessary. No communication with
+ the database can occur outside of a database transaction (this seems to confuse many developers
+ who are used to the auto-commit mode). Always use clear transaction boundaries, even for
+ read-only operations. Depending on your isolation level and database capabilities this might not
+ be required but there is no downside if you always demarcate transactions explicitly. Certainly,
+ a single database transaction is going to perform better than many small transactions, even
+ for reading data.
+ </para>
+
+ <para>
+ A Hibernate application can run in non-managed (i.e. standalone, simple Web- or Swing applications)
+ and managed J2EE environments. In a non-managed environment, Hibernate is usually responsible for
+ its own database connection pool. The application developer has to manually set transaction
+ boundaries, in other words, begin, commit, or rollback database transactions himself. A managed environment
+ usually provides container-managed transactions (CMT), with the transaction assembly defined declaratively
+ in deployment descriptors of EJB session beans, for example. Programmatic transaction demarcation is
+ then no longer necessary.
+ </para>
+
+ <para>
+ However, it is often desirable to keep your persistence layer portable between non-managed
+ resource-local environments, and systems that can rely on JTA but use BMT instead of CMT.
+ In both cases you'd use programmatic transaction demaracation. Hibernate offers a wrapper
+ API called <literal>Transaction</literal> that translates into the native transaction system of
+ your deployment environment. This API is actually optional, but we strongly encourage its use
+ unless you are in a CMT session bean.
+ </para>
+
+ <para>
+ Usually, ending a <literal>Session</literal> involves four distinct phases:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ flush the session
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ commit the transaction
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ close the session
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ handle exceptions
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Flushing the session has been discussed earlier, we'll now have a closer look at transaction
+ demarcation and exception handling in both managed- and non-managed environments.
+ </para>
+
+
+ <sect2 id="transactions-demarcation-nonmanaged" revision="2">
+ <title>Non-managed environment</title>
+
+ <para>
+ If a Hibernate persistence layer runs in a non-managed environment, database connections
+ are usually handled by simple (i.e. non-DataSource) connection pools from which
+ Hibernate obtains connections as needed. The session/transaction handling idiom looks
+ like this:
+ </para>
+
+ <programlisting><![CDATA[// Non-managed environment idiom
+Session sess = factory.openSession();
+Transaction tx = null;
+try {
+ tx = sess.beginTransaction();
+
+ // do some work
+ ...
+
+ tx.commit();
+}
+catch (RuntimeException e) {
+ if (tx != null) tx.rollback();
+ throw e; // or display error message
+}
+finally {
+ sess.close();
+}]]></programlisting>
+
+ <para>
+ You don't have to <literal>flush()</literal> the <literal>Session</literal> explicitly -
+ the call to <literal>commit()</literal> automatically triggers the synchronization (depending
+ upon the <xref linkend="objectstate-flushing">FlushMode</xref> for the session.
+ A call to <literal>close()</literal> marks the end of a session. The main implication
+ of <literal>close()</literal> is that the JDBC connection will be relinquished by the
+ session. This Java code is portable and runs in both non-managed and JTA environments.
+ </para>
+
+ <para>
+ A much more flexible solution is Hibernate's built-in "current session" context
+ management, as described earlier:
+ </para>
+
+ <programlisting><![CDATA[// Non-managed environment idiom with getCurrentSession()
+try {
+ factory.getCurrentSession().beginTransaction();
+
+ // do some work
+ ...
+
+ factory.getCurrentSession().getTransaction().commit();
+}
+catch (RuntimeException e) {
+ factory.getCurrentSession().getTransaction().rollback();
+ throw e; // or display error message
+}]]></programlisting>
+
+ <para>
+ You will very likely never see these code snippets in a regular application;
+ fatal (system) exceptions should always be caught at the "top". In other words, the
+ code that executes Hibernate calls (in the persistence layer) and the code that handles
+ <literal>RuntimeException</literal> (and usually can only clean up and exit) are in
+ different layers. The current context management by Hibernate can significantly
+ simplify this design, as all you need is access to a <literal>SessionFactory</literal>.
+ Exception handling is discussed later in this chapter.
+ </para>
+
+ <para>
+ Note that you should select <literal>org.hibernate.transaction.JDBCTransactionFactory</literal>
+ (which is the default), and for the second example <literal>"thread"</literal> as your
+ <literal>hibernate.current_session_context_class</literal>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-demarcation-jta" revision="3">
+ <title>Using JTA</title>
+
+ <para>
+ If your persistence layer runs in an application server (e.g. behind EJB session beans),
+ every datasource connection obtained by Hibernate will automatically be part of the global
+ JTA transaction. You can also install a standalone JTA implementation and use it without
+ EJB. Hibernate offers two strategies for JTA integration.
+ </para>
+
+ <para>
+ If you use bean-managed transactions (BMT) Hibernate will tell the application server to start
+ and end a BMT transaction if you use the <literal>Transaction</literal> API. So, the
+ transaction management code is identical to the non-managed environment.
+ </para>
+
+ <programlisting><![CDATA[// BMT idiom
+Session sess = factory.openSession();
+Transaction tx = null;
+try {
+ tx = sess.beginTransaction();
+
+ // do some work
+ ...
+
+ tx.commit();
+}
+catch (RuntimeException e) {
+ if (tx != null) tx.rollback();
+ throw e; // or display error message
+}
+finally {
+ sess.close();
+}]]></programlisting>
+
+ <para>
+ If you want to use a transaction-bound <literal>Session</literal>, that is, the
+ <literal>getCurrentSession()</literal> functionality for easy context propagation,
+ you will have to use the JTA <literal>UserTransaction</literal> API directly:
+ </para>
+
+ <programlisting><![CDATA[// BMT idiom with getCurrentSession()
+try {
+ UserTransaction tx = (UserTransaction)new InitialContext()
+ .lookup("java:comp/UserTransaction");
+
+ tx.begin();
+
+ // Do some work on Session bound to transaction
+ factory.getCurrentSession().load(...);
+ factory.getCurrentSession().persist(...);
+
+ tx.commit();
+}
+catch (RuntimeException e) {
+ tx.rollback();
+ throw e; // or display error message
+}]]></programlisting>
+
+ <para>
+ With CMT, transaction demarcation is done in session bean deployment descriptors, not programatically,
+ hence, the code is reduced to:
+ </para>
+
+ <programlisting><![CDATA[// CMT idiom
+ Session sess = factory.getCurrentSession();
+
+ // do some work
+ ...
+]]></programlisting>
+
+ <para>
+ In a CMT/EJB even rollback happens automatically, since an unhandled <literal>RuntimeException</literal>
+ thrown by a session bean method tells the container to set the global transaction to rollback.
+ <emphasis>This means you do not need to use the Hibernate <literal>Transaction</literal> API at
+ all with BMT or CMT, and you get automatic propagation of the "current" Session bound to the
+ transaction.</emphasis>
+ </para>
+
+ <para>
+ Note that you should choose <literal>org.hibernate.transaction.JTATransactionFactory</literal>
+ if you use JTA directly (BMT), and <literal>org.hibernate.transaction.CMTTransactionFactory</literal>
+ in a CMT session bean, when you configure Hibernate's transaction factory. Remember to also set
+ <literal>hibernate.transaction.manager_lookup_class</literal>. Furthermore, make sure
+ that your <literal>hibernate.current_session_context_class</literal> is either unset (backwards
+ compatiblity), or set to <literal>"jta"</literal>.
+ </para>
+
+ <para>
+ The <literal>getCurrentSession()</literal> operation has one downside in a JTA environment.
+ There is one caveat to the use of <literal>after_statement</literal> connection release
+ mode, which is then used by default. Due to a silly limitation of the JTA spec, it is not
+ possible for Hibernate to automatically clean up any unclosed <literal>ScrollableResults</literal> or
+ <literal>Iterator</literal> instances returned by <literal>scroll()</literal> or
+ <literal>iterate()</literal>. You <emphasis>must</emphasis> release the underlying database
+ cursor by calling <literal>ScrollableResults.close()</literal> or
+ <literal>Hibernate.close(Iterator)</literal> explicity from a <literal>finally</literal>
+ block. (Of course, most applications can easily avoid using <literal>scroll()</literal> or
+ <literal>iterate()</literal> at all from the JTA or CMT code.)
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-demarcation-exceptions">
+ <title>Exception handling</title>
+
+ <para>
+ If the <literal>Session</literal> throws an exception (including any
+ <literal>SQLException</literal>), you should immediately rollback the database
+ transaction, call <literal>Session.close()</literal> and discard the
+ <literal>Session</literal> instance. Certain methods of <literal>Session</literal>
+ will <emphasis>not</emphasis> leave the session in a consistent state. No
+ exception thrown by Hibernate can be treated as recoverable. Ensure that the
+ <literal>Session</literal> will be closed by calling <literal>close()</literal>
+ in a <literal>finally</literal> block.
+ </para>
+
+ <para>
+ The <literal>HibernateException</literal>, which wraps most of the errors that
+ can occur in a Hibernate persistence layer, is an unchecked exception (it wasn't
+ in older versions of Hibernate). In our opinion, we shouldn't force the application
+ developer to catch an unrecoverable exception at a low layer. In most systems, unchecked
+ and fatal exceptions are handled in one of the first frames of the method call
+ stack (i.e. in higher layers) and an error message is presented to the application
+ user (or some other appropriate action is taken). Note that Hibernate might also throw
+ other unchecked exceptions which are not a <literal>HibernateException</literal>. These
+ are, again, not recoverable and appropriate action should be taken.
+ </para>
+
+ <para>
+ Hibernate wraps <literal>SQLException</literal>s thrown while interacting with the database
+ in a <literal>JDBCException</literal>. In fact, Hibernate will attempt to convert the eexception
+ into a more meningful subclass of <literal>JDBCException</literal>. The underlying
+ <literal>SQLException</literal> is always available via <literal>JDBCException.getCause()</literal>.
+ Hibernate converts the <literal>SQLException</literal> into an appropriate
+ <literal>JDBCException</literal> subclass using the <literal>SQLExceptionConverter</literal>
+ attached to the <literal>SessionFactory</literal>. By default, the
+ <literal>SQLExceptionConverter</literal> is defined by the configured dialect; however, it is
+ also possible to plug in a custom implementation (see the javadocs for the
+ <literal>SQLExceptionConverterFactory</literal> class for details). The standard
+ <literal>JDBCException</literal> subtypes are:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>JDBCConnectionException</literal> - indicates an error
+ with the underlying JDBC communication.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>SQLGrammarException</literal> - indicates a grammar
+ or syntax problem with the issued SQL.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>ConstraintViolationException</literal> - indicates some
+ form of integrity constraint violation.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>LockAcquisitionException</literal> - indicates an error
+ acquiring a lock level necessary to perform the requested operation.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>GenericJDBCException</literal> - a generic exception
+ which did not fall into any of the other categories.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect2>
+
+ <sect2 id="transactions-demarcation-timeout">
+ <title>Transaction timeout</title>
+
+ <para>
+ One extremely important feature provided by a managed environment like EJB
+ that is never provided for non-managed code is transaction timeout. Transaction
+ timeouts ensure that no misbehaving transaction can indefinitely tie up
+ resources while returning no response to the user. Outside a managed (JTA)
+ environment, Hibernate cannot fully provide this functionality. However,
+ Hibernate can at least control data access operations, ensuring that database
+ level deadlocks and queries with huge result sets are limited by a defined
+ timeout. In a managed environment, Hibernate can delegate transaction timeout
+ to JTA. This functioanlity is abstracted by the Hibernate
+ <literal>Transaction</literal> object.
+ </para>
+
+ <programlisting><![CDATA[
+Session sess = factory.openSession();
+try {
+ //set transaction timeout to 3 seconds
+ sess.getTransaction().setTimeout(3);
+ sess.getTransaction().begin();
+
+ // do some work
+ ...
+
+ sess.getTransaction().commit()
+}
+catch (RuntimeException e) {
+ sess.getTransaction().rollback();
+ throw e; // or display error message
+}
+finally {
+ sess.close();
+}]]></programlisting>
+
+ <para>
+ Note that <literal>setTimeout()</literal> may not be called in a CMT bean,
+ where transaction timeouts must be defined declaratively.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="transactions-optimistic">
+ <title>Optimistic concurrency control</title>
+
+ <para>
+ The only approach that is consistent with high concurrency and high
+ scalability is optimistic concurrency control with versioning. Version
+ checking uses version numbers, or timestamps, to detect conflicting updates
+ (and to prevent lost updates). Hibernate provides for three possible approaches
+ to writing application code that uses optimistic concurrency. The use cases
+ we show are in the context of long conversations, but version checking
+ also has the benefit of preventing lost updates in single database transactions.
+ </para>
+
+ <sect2 id="transactions-optimistic-manual">
+ <title>Application version checking</title>
+
+ <para>
+ In an implementation without much help from Hibernate, each interaction with the
+ database occurs in a new <literal>Session</literal> and the developer is responsible
+ for reloading all persistent instances from the database before manipulating them.
+ This approach forces the application to carry out its own version checking to ensure
+ conversation transaction isolation. This approach is the least efficient in terms of
+ database access. It is the approach most similar to entity EJBs.
+ </para>
+
+ <programlisting><![CDATA[// foo is an instance loaded by a previous Session
+session = factory.openSession();
+Transaction t = session.beginTransaction();
+
+int oldVersion = foo.getVersion();
+session.load( foo, foo.getKey() ); // load the current state
+if ( oldVersion!=foo.getVersion ) throw new StaleObjectStateException();
+foo.setProperty("bar");
+
+t.commit();
+session.close();]]></programlisting>
+
+ <para>
+ The <literal>version</literal> property is mapped using <literal><version></literal>,
+ and Hibernate will automatically increment it during flush if the entity is
+ dirty.
+ </para>
+
+ <para>
+ Of course, if you are operating in a low-data-concurrency environment and don't
+ require version checking, you may use this approach and just skip the version
+ check. In that case, <emphasis>last commit wins</emphasis> will be the default
+ strategy for your long conversations. Keep in mind that this might
+ confuse the users of the application, as they might experience lost updates without
+ error messages or a chance to merge conflicting changes.
+ </para>
+
+ <para>
+ Clearly, manual version checking is only feasible in very trivial circumstances
+ and not practical for most applications. Often not only single instances, but
+ complete graphs of modified ojects have to be checked. Hibernate offers automatic
+ version checking with either an extended <literal>Session</literal> or detached instances
+ as the design paradigm.
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-optimistic-longsession">
+ <title>Extended session and automatic versioning</title>
+
+ <para>
+ A single <literal>Session</literal> instance and its persistent instances are
+ used for the whole conversation, known as <emphasis>session-per-conversation</emphasis>.
+ Hibernate checks instance versions at flush time, throwing an exception if concurrent
+ modification is detected. It's up to the developer to catch and handle this exception
+ (common options are the opportunity for the user to merge changes or to restart the
+ business conversation with non-stale data).
+ </para>
+
+ <para>
+ The <literal>Session</literal> is disconnected from any underlying JDBC connection
+ when waiting for user interaction. This approach is the most efficient in terms
+ of database access. The application need not concern itself with version checking or
+ with reattaching detached instances, nor does it have to reload instances in every
+ database transaction.
+ </para>
+
+ <programlisting><![CDATA[// foo is an instance loaded earlier by the old session
+Transaction t = session.beginTransaction(); // Obtain a new JDBC connection, start transaction
+
+foo.setProperty("bar");
+
+session.flush(); // Only for last transaction in conversation
+t.commit(); // Also return JDBC connection
+session.close(); // Only for last transaction in conversation]]></programlisting>
+ <para>
+ The <literal>foo</literal> object still knows which <literal>Session</literal> it was
+ loaded in. Beginning a new database transaction on an old session obtains a new connection
+ and resumes the session. Committing a database transaction disconnects a session
+ from the JDBC connection and returns the connection to the pool. After reconnection, to
+ force a version check on data you aren't updating, you may call <literal>Session.lock()</literal>
+ with <literal>LockMode.READ</literal> on any objects that might have been updated by another
+ transaction. You don't need to lock any data that you <emphasis>are</emphasis> updating.
+ Usually you would set <literal>FlushMode.NEVER</literal> on an extended <literal>Session</literal>,
+ so that only the last database transaction cycle is allowed to actually persist all
+ modifications made in this conversation. Hence, only this last database transaction
+ would include the <literal>flush()</literal> operation, and then also
+ <literal>close()</literal> the session to end the conversation.
+ </para>
+
+ <para>
+ This pattern is problematic if the <literal>Session</literal> is too big to
+ be stored during user think time, e.g. an <literal>HttpSession</literal> should
+ be kept as small as possible. As the <literal>Session</literal> is also the
+ (mandatory) first-level cache and contains all loaded objects, we can probably
+ use this strategy only for a few request/response cycles. You should use a
+ <literal>Session</literal> only for a single conversation, as it will soon also
+ have stale data.
+ </para>
+
+ <para>
+ (Note that earlier Hibernate versions required explicit disconnection and reconnection
+ of a <literal>Session</literal>. These methods are deprecated, as beginning and
+ ending a transaction has the same effect.)
+ </para>
+
+ <para>
+ Also note that you should keep the disconnected <literal>Session</literal> close
+ to the persistence layer. In other words, use an EJB stateful session bean to
+ hold the <literal>Session</literal> in a three-tier environment, and don't transfer
+ it to the web layer (or even serialize it to a separate tier) to store it in the
+ <literal>HttpSession</literal>.
+ </para>
+
+ <para>
+ The extended session pattern, or <emphasis>session-per-conversation</emphasis>, is
+ more difficult to implement with automatic current session context management.
+ You need to supply your own implementation of the <literal>CurrentSessionContext</literal>
+ for this, see the Hibernate Wiki for examples.
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-optimistic-detached">
+ <title>Detached objects and automatic versioning</title>
+
+ <para>
+ Each interaction with the persistent store occurs in a new <literal>Session</literal>.
+ However, the same persistent instances are reused for each interaction with the database.
+ The application manipulates the state of detached instances originally loaded in another
+ <literal>Session</literal> and then reattaches them using <literal>Session.update()</literal>,
+ <literal>Session.saveOrUpdate()</literal>, or <literal>Session.merge()</literal>.
+ </para>
+
+ <programlisting><![CDATA[// foo is an instance loaded by a previous Session
+foo.setProperty("bar");
+session = factory.openSession();
+Transaction t = session.beginTransaction();
+session.saveOrUpdate(foo); // Use merge() if "foo" might have been loaded already
+t.commit();
+session.close();]]></programlisting>
+
+ <para>
+ Again, Hibernate will check instance versions during flush, throwing an
+ exception if conflicting updates occured.
+ </para>
+
+ <para>
+ You may also call <literal>lock()</literal> instead of <literal>update()</literal>
+ and use <literal>LockMode.READ</literal> (performing a version check, bypassing all
+ caches) if you are sure that the object has not been modified.
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-optimistic-customizing">
+ <title>Customizing automatic versioning</title>
+
+ <para>
+ You may disable Hibernate's automatic version increment for particular properties and
+ collections by setting the <literal>optimistic-lock</literal> mapping attribute to
+ <literal>false</literal>. Hibernate will then no longer increment versions if the
+ property is dirty.
+ </para>
+
+ <para>
+ Legacy database schemas are often static and can't be modified. Or, other applications
+ might also access the same database and don't know how to handle version numbers or
+ even timestamps. In both cases, versioning can't rely on a particular column in a table.
+ To force a version check without a version or timestamp property mapping, with a
+ comparison of the state of all fields in a row, turn on <literal>optimistic-lock="all"</literal>
+ in the <literal><class></literal> mapping. Note that this concepetually only works
+ if Hibernate can compare the old and new state, i.e. if you use a single long
+ <literal>Session</literal> and not session-per-request-with-detached-objects.
+ </para>
+
+ <para>
+ Sometimes concurrent modification can be permitted as long as the changes that have been
+ made don't overlap. If you set <literal>optimistic-lock="dirty"</literal> when mapping the
+ <literal><class></literal>, Hibernate will only compare dirty fields during flush.
+ </para>
+
+ <para>
+ In both cases, with dedicated version/timestamp columns or with full/dirty field
+ comparison, Hibernate uses a single <literal>UPDATE</literal> statement (with an
+ appropriate <literal>WHERE</literal> clause) per entity to execute the version check
+ and update the information. If you use transitive persistence to cascade reattachment
+ to associated entities, Hibernate might execute uneccessary updates. This is usually
+ not a problem, but <emphasis>on update</emphasis> triggers in the database might be
+ executed even when no changes have been made to detached instances. You can customize
+ this behavior by setting <literal>select-before-update="true"</literal> in the
+ <literal><class></literal> mapping, forcing Hibernate to <literal>SELECT</literal>
+ the instance to ensure that changes did actually occur, before updating the row.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="transactions-locking">
+ <title>Pessimistic Locking</title>
+
+ <para>
+ It is not intended that users spend much time worring about locking strategies. Its usually
+ enough to specify an isolation level for the JDBC connections and then simply let the
+ database do all the work. However, advanced users may sometimes wish to obtain
+ exclusive pessimistic locks, or re-obtain locks at the start of a new transaction.
+ </para>
+
+ <para>
+ Hibernate will always use the locking mechanism of the database, never lock objects
+ in memory!
+ </para>
+
+ <para>
+ The <literal>LockMode</literal> class defines the different lock levels that may be acquired
+ by Hibernate. A lock is obtained by the following mechanisms:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>LockMode.WRITE</literal> is acquired automatically when Hibernate updates or inserts
+ a row.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>LockMode.UPGRADE</literal> may be acquired upon explicit user request using
+ <literal>SELECT ... FOR UPDATE</literal> on databases which support that syntax.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>LockMode.UPGRADE_NOWAIT</literal> may be acquired upon explicit user request using a
+ <literal>SELECT ... FOR UPDATE NOWAIT</literal> under Oracle.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>LockMode.READ</literal> is acquired automatically when Hibernate reads data
+ under Repeatable Read or Serializable isolation level. May be re-acquired by explicit user
+ request.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>LockMode.NONE</literal> represents the absence of a lock. All objects switch to this
+ lock mode at the end of a <literal>Transaction</literal>. Objects associated with the session
+ via a call to <literal>update()</literal> or <literal>saveOrUpdate()</literal> also start out
+ in this lock mode.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ The "explicit user request" is expressed in one of the following ways:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ A call to <literal>Session.load()</literal>, specifying a <literal>LockMode</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ A call to <literal>Session.lock()</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ A call to <literal>Query.setLockMode()</literal>.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ If <literal>Session.load()</literal> is called with <literal>UPGRADE</literal> or
+ <literal>UPGRADE_NOWAIT</literal>, and the requested object was not yet loaded by
+ the session, the object is loaded using <literal>SELECT ... FOR UPDATE</literal>.
+ If <literal>load()</literal> is called for an object that is already loaded with
+ a less restrictive lock than the one requested, Hibernate calls
+ <literal>lock()</literal> for that object.
+ </para>
+
+ <para>
+ <literal>Session.lock()</literal> performs a version number check if the specified lock
+ mode is <literal>READ</literal>, <literal>UPGRADE</literal> or
+ <literal>UPGRADE_NOWAIT</literal>. (In the case of <literal>UPGRADE</literal> or
+ <literal>UPGRADE_NOWAIT</literal>, <literal>SELECT ... FOR UPDATE</literal> is used.)
+ </para>
+
+ <para>
+ If the database does not support the requested lock mode, Hibernate will use an appropriate
+ alternate mode (instead of throwing an exception). This ensures that applications will
+ be portable.
+ </para>
+
+ </sect1>
+
+ <sect1 id="transactions-connection-release">
+ <title>Connection Release Modes</title>
+
+ <para>
+ The legacy (2.x) behavior of Hibernate in regards to JDBC connection management
+ was that a <literal>Session</literal> would obtain a connection when it was first
+ needed and then hold unto that connection until the session was closed.
+ Hibernate 3.x introduced the notion of connection release modes to tell a session
+ how to handle its JDBC connections. Note that the following discussion is pertinent
+ only to connections provided through a configured <literal>ConnectionProvider</literal>;
+ user-supplied connections are outside the breadth of this discussion. The different
+ release modes are identified by the enumerated values of
+ <literal>org.hibernate.ConnectionReleaseMode</literal>:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>ON_CLOSE</literal> - is essentially the legacy behavior described above. The
+ Hibernate session obatins a connection when it first needs to perform some JDBC access
+ and holds unto that connection until the session is closed.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>AFTER_TRANSACTION</literal> - says to release connections after a
+ <literal>org.hibernate.Transaction</literal> has completed.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>AFTER_STATEMENT</literal> (also referred to as aggressive release) - says to
+ release connections after each and every statement execution. This aggressive releasing
+ is skipped if that statement leaves open resources associated with the given session;
+ currently the only situation where this occurs is through the use of
+ <literal>org.hibernate.ScrollableResults</literal>.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ The configuration parameter <literal>hibernate.connection.release_mode</literal> is used
+ to specify which release mode to use. The possible values:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>auto</literal> (the default) - this choice delegates to the release mode
+ returned by the <literal>org.hibernate.transaction.TransactionFactory.getDefaultReleaseMode()</literal>
+ method. For JTATransactionFactory, this returns ConnectionReleaseMode.AFTER_STATEMENT; for
+ JDBCTransactionFactory, this returns ConnectionReleaseMode.AFTER_TRANSACTION. It is rarely
+ a good idea to change this default behavior as failures due to the value of this setting
+ tend to indicate bugs and/or invalid assumptions in user code.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>on_close</literal> - says to use ConnectionReleaseMode.ON_CLOSE. This setting
+ is left for backwards compatibility, but its use is highly discouraged.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>after_transaction</literal> - says to use ConnectionReleaseMode.AFTER_TRANSACTION.
+ This setting should not be used in JTA environments. Also note that with
+ ConnectionReleaseMode.AFTER_TRANSACTION, if a session is considered to be in auto-commit
+ mode connections will be released as if the release mode were AFTER_STATEMENT.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>after_statement</literal> - says to use ConnectionReleaseMode.AFTER_STATEMENT. Additionally,
+ the configured <literal>ConnectionProvider</literal> is consulted to see if it supports this
+ setting (<literal>supportsAggressiveRelease()</literal>). If not, the release mode is reset
+ to ConnectionReleaseMode.AFTER_TRANSACTION. This setting is only safe in environments where
+ we can either re-acquire the same underlying JDBC connection each time we make a call into
+ <literal>ConnectionProvider.getConnection()</literal> or in auto-commit environments where
+ it does not matter whether we get back the same connection.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/tutorial.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/tutorial.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/tutorial.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1554 @@
+<chapter id="tutorial">
+ <title>Introduction to Hibernate</title>
+
+ <sect1 id="tutorial-intro" revision="1">
+ <title>Preface</title>
+
+ <para>
+ This chapter is an introductory tutorial for new users of Hibernate. We start
+ with a simple command line application using an in-memory database and develop
+ it in easy to understand steps.
+ </para>
+
+ <para>
+ This tutorial is intended for new users of Hibernate but requires Java and
+ SQL knowledge. It is based on a tutorial by Michael Gloegl, the third-party
+ libraries we name are for JDK 1.4 and 5.0. You might need others for JDK 1.3.
+ </para>
+
+ <para>
+ The source code for the tutorial is included in the distribution in the
+ <literal>doc/reference/tutorial/</literal> directory.
+ </para>
+
+ </sect1>
+
+ <sect1 id="tutorial-firstapp" revision="2">
+ <title>Part 1 - The first Hibernate Application</title>
+
+ <para>
+ First, we'll create a simple console-based Hibernate application. We use an
+ Java database (HSQL DB), so we do not have to install any database server.
+ </para>
+
+ <para>
+ Let's assume we need a small database application that can store events we want to
+ attend, and information about the hosts of these events.
+ </para>
+
+ <para>
+ The first thing we do, is set up our development directory and put all the
+ Java libraries we need into it. Download the Hibernate distribution from the
+ Hibernate website. Extract the package and place all required libraries
+ found in <literal>/lib</literal> into into the <literal>/lib</literal> directory
+ of your new development working directory. It should look like this:
+ </para>
+
+ <programlisting><![CDATA[.
++lib
+ antlr.jar
+ cglib.jar
+ asm.jar
+ asm-attrs.jars
+ commons-collections.jar
+ commons-logging.jar
+ hibernate3.jar
+ jta.jar
+ dom4j.jar
+ log4j.jar ]]></programlisting>
+
+ <para>
+ This is the minimum set of required libraries (note that we also copied
+ hibernate3.jar, the main archive) for Hibernate <emphasis>at the time of writing</emphasis>.
+ The Hibernate release you are using might require more or less libraries. See the
+ <literal>README.txt</literal> file in the <literal>lib/</literal> directory of the
+ Hibernate distribution for more information about required and optional third-party
+ libraries. (Actually, Log4j is not required but preferred by many developers.)
+ </para>
+
+ <para>
+ Next we create a class that represents the event we want to store in database.
+ </para>
+
+ <sect2 id="tutorial-firstapp-firstclass" revision="1">
+ <title>The first class</title>
+
+ <para>
+ Our first persistent class is a simple JavaBean class with some properties:
+ </para>
+
+ <programlisting><![CDATA[package events;
+
+import java.util.Date;
+
+public class Event {
+ private Long id;
+
+ private String title;
+ private Date date;
+
+ public Event() {}
+
+ public Long getId() {
+ return id;
+ }
+
+ private void setId(Long id) {
+ this.id = id;
+ }
+
+ public Date getDate() {
+ return date;
+ }
+
+ public void setDate(Date date) {
+ this.date = date;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+}]]></programlisting>
+
+ <para>
+ You can see that this class uses standard JavaBean naming conventions for property
+ getter and setter methods, as well as private visibility for the fields. This is
+ a recommended design - but not required. Hibernate can also access fields directly,
+ the benefit of accessor methods is robustness for refactoring. The no-argument
+ constructor is required to instantiate an object of this class through reflection.
+ </para>
+
+ <para>
+ The <literal>id</literal> property holds a unique identifier value for a particular event.
+ All persistent entity classes (there are less important dependent classes as well) will need
+ such an identifier property if we want to use the full feature set of Hibernate. In fact,
+ most applications (esp. web applications) need to distinguish objects by identifier, so you
+ should consider this a feature rather than a limitation. However, we usually don't manipulate
+ the identity of an object, hence the setter method should be private. Only Hibernate will assign
+ identifiers when an object is saved. You can see that Hibernate can access public, private,
+ and protected accessor methods, as well as (public, private, protected) fields directly. The
+ choice is up to you and you can match it to fit your application design.
+ </para>
+
+ <para>
+ The no-argument constructor is a requirement for all persistent classes; Hibernate
+ has to create objects for you, using Java Reflection. The constructor can be
+ private, however, package visibility is required for runtime proxy generation and
+ efficient data retrieval without bytecode instrumentation.
+ </para>
+
+ <para>
+ Place this Java source file in a directory called <literal>src</literal> in the
+ development folder, and in its correct package. The directory should now look like this:
+ </para>
+
+ <programlisting><![CDATA[.
++lib
+ <Hibernate and third-party libraries>
++src
+ +events
+ Event.java]]></programlisting>
+
+ <para>
+ In the next step, we tell Hibernate about this persistent class.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-mapping" revision="1">
+ <title>The mapping file</title>
+
+ <para>
+ Hibernate needs to know how to load and store objects of the persistent class.
+ This is where the Hibernate mapping file comes into play. The mapping file
+ tells Hibernate what table in the database it has to access, and what columns
+ in that table it should use.
+ </para>
+
+ <para>
+ The basic structure of a mapping file looks like this:
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping>
+[...]
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Note that the Hibernate DTD is very sophisticated. You can use it for
+ auto-completion of XML mapping elements and attributes in your editor or
+ IDE. You also should open up the DTD file in your text editor - it's the
+ easiest way to get an overview of all elements and attributes and to see
+ the defaults, as well as some comments. Note that Hibernate will not
+ load the DTD file from the web, but first look it up from the classpath
+ of the application. The DTD file is included in <literal>hibernate3.jar</literal>
+ as well as in the <literal>src/</literal> directory of the Hibernate distribution.
+ </para>
+
+ <para>
+ We will omit the DTD declaration in future examples to shorten the code. It is
+ of course not optional.
+ </para>
+
+ <para>
+ Between the two <literal>hibernate-mapping</literal> tags, include a
+ <literal>class</literal> element. All persistent entity classes (again, there
+ might be dependent classes later on, which are not first-class entities) need
+ such a mapping, to a table in the SQL database:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="events.Event" table="EVENTS">
+
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ So far we told Hibernate how to persist and load object of class <literal>Event</literal>
+ to the table <literal>EVENTS</literal>, each instance represented by a row in that table.
+ Now we continue with a mapping of the unique identifier property to the tables primary key.
+ In addition, as we don't want to care about handling this identifier, we configure Hibernate's
+ identifier generation strategy for a surrogate primary key column:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="events.Event" table="EVENTS">
+ <id name="id" column="EVENT_ID">
+ <generator class="native"/>
+ </id>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ The <literal>id</literal> element is the declaration of the identifer property,
+ <literal>name="id"</literal> declares the name of the Java property -
+ Hibernate will use the getter and setter methods to access the property.
+ The column attribute tells Hibernate which column of the
+ <literal>EVENTS</literal> table we use for this primary key. The nested
+ <literal>generator</literal> element specifies the identifier generation strategy,
+ in this case we used <literal>native</literal>, which picks the best strategy depending
+ on the configured database (dialect). Hibernate supports database generated, globally
+ unique, as well as application assigned identifiers (or any strategy you have written
+ an extension for).
+ </para>
+
+ <para>
+ Finally we include declarations for the persistent properties of the class in
+ the mapping file. By default, no properties of the class are considered
+ persistent:
+ </para>
+
+ <programlisting><![CDATA[
+<hibernate-mapping>
+
+ <class name="events.Event" table="EVENTS">
+ <id name="id" column="EVENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="date" type="timestamp" column="EVENT_DATE"/>
+ <property name="title"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Just as with the <literal>id</literal> element, the <literal>name</literal>
+ attribute of the <literal>property</literal> element tells Hibernate which getter
+ and setter methods to use. So, in this case, Hibernate will look for
+ <literal>getDate()/setDate()</literal>, as well as <literal>getTitle()/setTitle()</literal>.
+ </para>
+
+ <para>
+ Why does the <literal>date</literal> property mapping include the
+ <literal>column</literal> attribute, but the <literal>title</literal>
+ doesn't? Without the <literal>column</literal> attribute Hibernate
+ by default uses the property name as the column name. This works fine for
+ <literal>title</literal>. However, <literal>date</literal> is a reserved
+ keyword in most database, so we better map it to a different name.
+ </para>
+
+ <para>
+ The next interesting thing is that the <literal>title</literal> mapping also lacks
+ a <literal>type</literal> attribute. The types we declare and use in the mapping
+ files are not, as you might expect, Java data types. They are also not SQL
+ database types. These types are so called <emphasis>Hibernate mapping types</emphasis>,
+ converters which can translate from Java to SQL data types and vice versa. Again,
+ Hibernate will try to determine the correct conversion and mapping type itself if
+ the <literal>type</literal> attribute is not present in the mapping. In some cases this
+ automatic detection (using Reflection on the Java class) might not have the default you
+ expect or need. This is the case with the <literal>date</literal> property. Hibernate can't
+ know if the property (which is of <literal>java.util.Date</literal>) should map to a
+ SQL <literal>date</literal>, <literal>timestamp</literal>, or <literal>time</literal> column.
+ We preserve full date and time information by mapping the property with a
+ <literal>timestamp</literal> converter.
+ </para>
+
+ <para>
+ This mapping file should be saved as <literal>Event.hbm.xml</literal>, right in
+ the directory next to the <literal>Event</literal> Java class source file.
+ The naming of mapping files can be arbitrary, however the <literal>hbm.xml</literal>
+ suffix is a convention in the Hibernate developer community. The directory structure
+ should now look like this:
+ </para>
+
+ <programlisting><![CDATA[.
++lib
+ <Hibernate and third-party libraries>
++src
+ +events
+ Event.java
+ Event.hbm.xml]]></programlisting>
+
+ <para>
+ We continue with the main configuration of Hibernate.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-configuration" revision="2">
+ <title>Hibernate configuration</title>
+
+ <para>
+ We now have a persistent class and its mapping file in place. It is time to configure
+ Hibernate. Before we do this, we will need a database. HSQL DB, a java-based SQL DBMS,
+ can be downloaded from the HSQL DB website. Actually, you only need the <literal>hsqldb.jar</literal>
+ from this download. Place this file in the <literal>lib/</literal> directory of the
+ development folder.
+ </para>
+
+ <para>
+ Create a directory called <literal>data</literal> in the root of the development directory -
+ this is where HSQL DB will store its data files. Now start the database by running
+ <literal>java -classpath ../lib/hsqldb.jar org.hsqldb.Server</literal> in this data directory.
+ You can see it start up and bind to a TCP/IP socket, this is where our application
+ will connect later. If you want to start with a fresh database during this tutorial,
+ shutdown HSQL DB (press <literal>CTRL + C</literal> in the window), delete all files in the
+ <literal>data/</literal> directory, and start HSQL DB again.
+ </para>
+
+ <para>
+ Hibernate is the layer in your application which connects to this database, so it needs
+ connection information. The connections are made through a JDBC connection pool, which we
+ also have to configure. The Hibernate distribution contains several open source JDBC connection
+ pooling tools, but will use the Hibernate built-in connection pool for this tutorial. Note that
+ you have to copy the required library into your classpath and use different
+ connection pooling settings if you want to use a production-quality third party
+ JDBC pooling software.
+ </para>
+
+ <para>
+ For Hibernate's configuration, we can use a simple <literal>hibernate.properties</literal> file, a
+ slightly more sophisticated <literal>hibernate.cfg.xml</literal> file, or even complete
+ programmatic setup. Most users prefer the XML configuration file:
+ </para>
+
+ <programlisting><![CDATA[<?xml version='1.0' encoding='utf-8'?>
+<!DOCTYPE hibernate-configuration PUBLIC
+ "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
+
+<hibernate-configuration>
+
+ <session-factory>
+
+ <!-- Database connection settings -->
+ <property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
+ <property name="connection.url">jdbc:hsqldb:hsql://localhost</property>
+ <property name="connection.username">sa</property>
+ <property name="connection.password"></property>
+
+ <!-- JDBC connection pool (use the built-in) -->
+ <property name="connection.pool_size">1</property>
+
+ <!-- SQL dialect -->
+ <property name="dialect">org.hibernate.dialect.HSQLDialect</property>
+
+ <!-- Enable Hibernate's automatic session context management -->
+ <property name="current_session_context_class">thread</property>
+
+ <!-- Disable the second-level cache -->
+ <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
+
+ <!-- Echo all executed SQL to stdout -->
+ <property name="show_sql">true</property>
+
+ <!-- Drop and re-create the database schema on startup -->
+ <property name="hbm2ddl.auto">create</property>
+
+ <mapping resource="events/Event.hbm.xml"/>
+
+ </session-factory>
+
+</hibernate-configuration>]]></programlisting>
+
+ <para>
+ Note that this XML configuration uses a different DTD. We configure
+ Hibernate's <literal>SessionFactory</literal> - a global factory responsible
+ for a particular database. If you have several databases, use several
+ <literal><session-factory></literal> configurations, usually in
+ several configuration files (for easier startup).
+ </para>
+
+ <para>
+ The first four <literal>property</literal> elements contain the necessary
+ configuration for the JDBC connection. The dialect <literal>property</literal>
+ element specifies the particular SQL variant Hibernate generates.
+ Hibernate's automatic session management for persistence contexts will
+ come in handy as you will soon see.
+ The <literal>hbm2ddl.auto</literal> option turns on automatic generation of
+ database schemas - directly into the database. This can of course also be turned
+ off (by removing the config option) or redirected to a file with the help of
+ the <literal>SchemaExport</literal> Ant task. Finally, we add the mapping file(s)
+ for persistent classes to the configuration.
+ </para>
+
+ <para>
+ Copy this file into the source directory, so it will end up in the
+ root of the classpath. Hibernate automatically looks for a file called
+ <literal>hibernate.cfg.xml</literal> in the root of the classpath, on startup.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-ant" revision="1">
+ <title>Building with Ant</title>
+
+ <para>
+ We'll now build the tutorial with Ant. You will need to have Ant installed - get
+ it from the <ulink url="http://ant.apache.org/bindownload.cgi">Ant download page</ulink>.
+ How to install Ant will not be covered here. Please refer to the
+ <ulink url="http://ant.apache.org/manual/index.html">Ant manual</ulink>. After you
+ have installed Ant, we can start to create the buildfile. It will be called
+ <literal>build.xml</literal> and placed directly in the development directory.
+ </para>
+
+ <para>
+ A basic build file looks like this:
+ </para>
+
+ <programlisting><![CDATA[<project name="hibernate-tutorial" default="compile">
+
+ <property name="sourcedir" value="${basedir}/src"/>
+ <property name="targetdir" value="${basedir}/bin"/>
+ <property name="librarydir" value="${basedir}/lib"/>
+
+ <path id="libraries">
+ <fileset dir="${librarydir}">
+ <include name="*.jar"/>
+ </fileset>
+ </path>
+
+ <target name="clean">
+ <delete dir="${targetdir}"/>
+ <mkdir dir="${targetdir}"/>
+ </target>
+
+ <target name="compile" depends="clean, copy-resources">
+ <javac srcdir="${sourcedir}"
+ destdir="${targetdir}"
+ classpathref="libraries"/>
+ </target>
+
+ <target name="copy-resources">
+ <copy todir="${targetdir}">
+ <fileset dir="${sourcedir}">
+ <exclude name="**/*.java"/>
+ </fileset>
+ </copy>
+ </target>
+
+</project>]]></programlisting>
+
+ <para>
+ This will tell Ant to add all files in the lib directory ending with <literal>.jar</literal>
+ to the classpath used for compilation. It will also copy all non-Java source files to the
+ target directory, e.g. configuration and Hibernate mapping files. If you now run Ant, you
+ should get this output:
+ </para>
+
+ <programlisting><![CDATA[C:\hibernateTutorial\>ant
+Buildfile: build.xml
+
+copy-resources:
+ [copy] Copying 2 files to C:\hibernateTutorial\bin
+
+compile:
+ [javac] Compiling 1 source file to C:\hibernateTutorial\bin
+
+BUILD SUCCESSFUL
+Total time: 1 second ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-helpers" revision="3">
+ <title>Startup and helpers</title>
+
+ <para>
+ It's time to load and store some <literal>Event</literal> objects, but first
+ we have to complete the setup with some infrastructure code. We have to startup
+ Hibernate. This startup includes building a global <literal>SessionFactory</literal>
+ object and to store it somewhere for easy access in application code.
+ A <literal>SessionFactory</literal> can open up new <literal>Session</literal>'s.
+ A <literal>Session</literal> represents a single-threaded unit of work, the
+ <literal>SessionFactory</literal> is a thread-safe global object, instantiated once.
+ </para>
+
+ <para>
+ We'll create a <literal>HibernateUtil</literal> helper class which takes care
+ of startup and makes accessing a <literal>SessionFactory</literal> convenient.
+ Let's have a look at the implementation:
+ </para>
+
+ <programlisting><![CDATA[package util;
+
+import org.hibernate.*;
+import org.hibernate.cfg.*;
+
+public class HibernateUtil {
+
+ private static final SessionFactory sessionFactory;
+
+ static {
+ try {
+ // Create the SessionFactory from hibernate.cfg.xml
+ sessionFactory = new Configuration().configure().buildSessionFactory();
+ } catch (Throwable ex) {
+ // Make sure you log the exception, as it might be swallowed
+ System.err.println("Initial SessionFactory creation failed." + ex);
+ throw new ExceptionInInitializerError(ex);
+ }
+ }
+
+ public static SessionFactory getSessionFactory() {
+ return sessionFactory;
+ }
+
+}]]></programlisting>
+
+ <para>
+ This class does not only produce the global <literal>SessionFactory</literal> in
+ its static initializer (called once by the JVM when the class is loaded), but also
+ hides the fact that it uses a static singleton. It might as well lookup the
+ <literal>SessionFactory</literal> from JNDI in an application server.
+ </para>
+
+ <para>
+ If you give the <literal>SessionFactory</literal> a name in your configuration
+ file, Hibernate will in fact try to bind it to JNDI after it has been built.
+ To avoid this code completely you could also use JMX deployment and let the
+ JMX-capable container instantiate and bind a <literal>HibernateService</literal>
+ to JNDI. These advanced options are discussed in the Hibernate reference
+ documentation.
+ </para>
+
+ <para>
+ Place <literal>HibernateUtil.java</literal> in the development source directory, in
+ a package next to <literal>events</literal>:
+ </para>
+
+ <programlisting><![CDATA[.
++lib
+ <Hibernate and third-party libraries>
++src
+ +events
+ Event.java
+ Event.hbm.xml
+ +util
+ HibernateUtil.java
+ hibernate.cfg.xml
++data
+build.xml]]></programlisting>
+
+ <para>
+ This should again compile without problems. We finally need to configure a logging
+ system - Hibernate uses commons logging and leaves you the choice between Log4j and
+ JDK 1.4 logging. Most developers prefer Log4j: copy <literal>log4j.properties</literal>
+ from the Hibernate distribution (it's in the <literal>etc/</literal> directory) to
+ your <literal>src</literal> directory, next to <literal>hibernate.cfg.xml</literal>.
+ Have a look at the example configuration and change the settings if you like to have
+ more verbose output. By default, only Hibernate startup message are shown on stdout.
+ </para>
+
+ <para>
+ The tutorial infrastructure is complete - and we are ready to do some real work with
+ Hibernate.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-workingpersistence" revision="5">
+ <title>Loading and storing objects</title>
+
+ <para>
+ Finally, we can use Hibernate to load and store objects. We write an
+ <literal>EventManager</literal> class with a <literal>main()</literal> method:
+ </para>
+
+ <programlisting><![CDATA[package events;
+import org.hibernate.Session;
+
+import java.util.Date;
+
+import util.HibernateUtil;
+
+public class EventManager {
+
+ public static void main(String[] args) {
+ EventManager mgr = new EventManager();
+
+ if (args[0].equals("store")) {
+ mgr.createAndStoreEvent("My Event", new Date());
+ }
+
+ HibernateUtil.getSessionFactory().close();
+ }
+
+ private void createAndStoreEvent(String title, Date theDate) {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+
+ session.beginTransaction();
+
+ Event theEvent = new Event();
+ theEvent.setTitle(title);
+ theEvent.setDate(theDate);
+
+ session.save(theEvent);
+
+ session.getTransaction().commit();
+ }
+
+}]]></programlisting>
+
+ <para>
+ We create a new <literal>Event</literal> object, and hand it over to Hibernate.
+ Hibernate now takes care of the SQL and executes <literal>INSERT</literal>s
+ on the database. Let's have a look at the <literal>Session</literal> and
+ <literal>Transaction</literal>-handling code before we run this.
+ </para>
+
+ <para>
+ A <literal>Session</literal> is a single unit of work. For now we'll keep things
+ simple and assume a one-to-one granularity between a Hibernate <literal>Session</literal>
+ and a database transaction. To shield our code from the actual underlying transaction
+ system (in this case plain JDBC, but it could also run with JTA) we use the
+ <literal>Transaction</literal> API that is available on the Hibernate <literal>Session</literal>.
+ </para>
+
+ <para>
+ What does <literal>sessionFactory.getCurrentSession()</literal> do? First, you can call it
+ as many times and anywhere you like, once you get hold of your <literal>SessionFactory</literal>
+ (easy thanks to <literal>HibernateUtil</literal>). The <literal>getCurrentSession()</literal>
+ method always returns the "current" unit of work. Remember that we switched the configuration
+ option for this mechanism to "thread" in <literal>hibernate.cfg.xml</literal>? Hence,
+ the current unit of work is bound to the current Java thread that executes our application.
+ However, this is not the full picture, you also have to consider scope, when a unit of work
+ begins and when it ends.
+ </para>
+
+ <para>
+ A <literal>Session</literal> begins when it is first needed, when the first call to
+ <literal>getCurrentSession()</literal> is made. It is then bound by Hibernate to the current
+ thread. When the transaction ends, either through commit or rollback, Hibernate automatically
+ unbinds the <literal>Session</literal> from the thread and closes it for you. If you call
+ <literal>getCurrentSession()</literal> again, you get a new <literal>Session</literal> and can
+ start a new unit of work. This <emphasis>thread-bound</emphasis> programming model is the most
+ popular way of using Hibernate, as it allows flexible layering of your code (transaction
+ demarcation code can be separated from data access code, we'll do this later in this tutorial).
+ </para>
+
+ <para>
+ Related to the unit of work scope, should the Hibernate <literal>Session</literal> be used to
+ execute one or several database operations? The above example uses one <literal>Session</literal>
+ for one operation. This is pure coincidence, the example is just not complex enough to show any
+ other approach. The scope of a Hibernate <literal>Session</literal> is flexible but you should
+ never design your application to use a new Hibernate <literal>Session</literal> for
+ <emphasis>every</emphasis> database operation. So even if you see it a few more times in
+ the following (very trivial) examples, consider <emphasis>session-per-operation</emphasis>
+ an anti-pattern. A real (web) application is shown later in this tutorial.
+ </para>
+
+ <para>
+ Have a look at <xref linkend="transactions"/> for more information
+ about transaction handling and demarcation. We also skipped any error handling and
+ rollback in the previous example.
+ </para>
+
+ <para>
+ To run this first routine we have to add a callable target to the Ant build file:
+ </para>
+
+ <programlisting><![CDATA[<target name="run" depends="compile">
+ <java fork="true" classname="events.EventManager" classpathref="libraries">
+ <classpath path="${targetdir}"/>
+ <arg value="${action}"/>
+ </java>
+</target>]]></programlisting>
+
+ <para>
+ The value of the <literal>action</literal> argument is set on the command line when
+ calling the target:
+ </para>
+
+ <programlisting><![CDATA[C:\hibernateTutorial\>ant run -Daction=store]]></programlisting>
+
+ <para>
+ You should see, after compilation, Hibernate starting up and, depending on your
+ configuration, lots of log output. At the end you will find the following line:
+ </para>
+
+ <programlisting><![CDATA[[java] Hibernate: insert into EVENTS (EVENT_DATE, title, EVENT_ID) values (?, ?, ?)]]></programlisting>
+
+ <para>
+ This is the <literal>INSERT</literal> executed by Hibernate, the question marks
+ represent JDBC bind parameters. To see the values bound as arguments, or to reduce
+ the verbosity of the log, check your <literal>log4j.properties</literal>.
+ </para>
+
+ <para>
+ Now we'd like to list stored events as well, so we add an option to the main method:
+ </para>
+
+ <programlisting><![CDATA[if (args[0].equals("store")) {
+ mgr.createAndStoreEvent("My Event", new Date());
+}
+else if (args[0].equals("list")) {
+ List events = mgr.listEvents();
+ for (int i = 0; i < events.size(); i++) {
+ Event theEvent = (Event) events.get(i);
+ System.out.println("Event: " + theEvent.getTitle() +
+ " Time: " + theEvent.getDate());
+ }
+}]]></programlisting>
+
+ <para>
+ We also add a new <literal>listEvents() method</literal>:
+ </para>
+
+ <programlisting><![CDATA[private List listEvents() {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+
+ session.beginTransaction();
+
+ List result = session.createQuery("from Event").list();
+
+ session.getTransaction().commit();
+
+ return result;
+}]]></programlisting>
+
+ <para>
+ What we do here is use an HQL (Hibernate Query Language) query to load all existing
+ <literal>Event</literal> objects from the database. Hibernate will generate the
+ appropriate SQL, send it to the database and populate <literal>Event</literal> objects
+ with the data. You can create more complex queries with HQL, of course.
+ </para>
+
+ <para>
+ Now, to execute and test all of this, follow these steps:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ Run <literal>ant run -Daction=store</literal> to store something into the database
+ and, of course, to generate the database schema before through hbm2ddl.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Now disable hbm2ddl by commenting out the property in your <literal>hibernate.cfg.xml</literal>
+ file. Usually you only leave it turned on in continous unit testing, but another
+ run of hbm2ddl would <emphasis>drop</emphasis> everything you have stored - the
+ <literal>create</literal> configuration setting actually translates into "drop all
+ tables from the schema, then re-create all tables, when the SessionFactory is build".
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ If you now call Ant with <literal>-Daction=list</literal>, you should see the events
+ you have stored so far. You can of course also call the <literal>store</literal> action a few
+ times more.
+ </para>
+
+ <para>
+ Note: Most new Hibernate users fail at this point and we see questions about
+ <emphasis>Table not found</emphasis> error messages regularly. However, if you follow the
+ steps outlined above you will not have this problem, as hbm2ddl creates the database
+ schema on the first run, and subsequent application restarts will use this schema. If
+ you change the mapping and/or database schema, you have to re-enable hbm2ddl once again.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="tutorial-associations">
+ <title>Part 2 - Mapping associations</title>
+
+ <para>
+ We mapped a persistent entity class to a table. Let's build on this and add some class associations.
+ First we'll add people to our application, and store a list of events they participate in.
+ </para>
+
+ <sect2 id="tutorial-associations-mappinguser" revision="1">
+ <title>Mapping the Person class</title>
+
+ <para>
+ The first cut of the <literal>Person</literal> class is simple:
+ </para>
+
+ <programlisting><![CDATA[package events;
+
+public class Person {
+
+ private Long id;
+ private int age;
+ private String firstname;
+ private String lastname;
+
+ public Person() {}
+
+ // Accessor methods for all properties, private setter for 'id'
+
+}]]></programlisting>
+
+ <para>
+ Create a new mapping file called <literal>Person.hbm.xml</literal> (don't forget the
+ DTD reference at the top):
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="events.Person" table="PERSON">
+ <id name="id" column="PERSON_ID">
+ <generator class="native"/>
+ </id>
+ <property name="age"/>
+ <property name="firstname"/>
+ <property name="lastname"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Finally, add the new mapping to Hibernate's configuration:
+ </para>
+
+ <programlisting><![CDATA[<mapping resource="events/Event.hbm.xml"/>
+<mapping resource="events/Person.hbm.xml"/>]]></programlisting>
+
+ <para>
+ We'll now create an association between these two entities. Obviously, persons
+ can participate in events, and events have participants. The design questions
+ we have to deal with are: directionality, multiplicity, and collection
+ behavior.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-unidirset" revision="3">
+ <title>A unidirectional Set-based association</title>
+
+ <para>
+ We'll add a collection of events to the <literal>Person</literal> class. That way we can
+ easily navigate to the events for a particular person, without executing an explicit query -
+ by calling <literal>aPerson.getEvents()</literal>. We use a Java collection, a <literal>Set</literal>,
+ because the collection will not contain duplicate elements and the ordering is not relevant for us.
+ </para>
+
+ <para>
+ We need a unidirectional, many-valued associations, implemented with a <literal>Set</literal>.
+ Let's write the code for this in the Java classes and then map it:
+ </para>
+
+ <programlisting><![CDATA[public class Person {
+
+ private Set events = new HashSet();
+
+ public Set getEvents() {
+ return events;
+ }
+
+ public void setEvents(Set events) {
+ this.events = events;
+ }
+}]]></programlisting>
+
+ <para>
+ Before we map this association, think about the other side. Clearly, we could just keep this
+ unidirectional. Or, we could create another collection on the <literal>Event</literal>, if we
+ want to be able to navigate it bi-directional, i.e. <literal>anEvent.getParticipants()</literal>.
+ This is not necessary, from a functional perspective. You could always execute an explicit query
+ to retrieve the participants for a particular event. This is a design choice left to you, but what
+ is clear from this discussion is the multiplicity of the association: "many" valued on both sides,
+ we call this a <emphasis>many-to-many</emphasis> association. Hence, we use Hibernate's
+ many-to-many mapping:
+ </para>
+
+ <programlisting><![CDATA[<class name="events.Person" table="PERSON">
+ <id name="id" column="PERSON_ID">
+ <generator class="native"/>
+ </id>
+ <property name="age"/>
+ <property name="firstname"/>
+ <property name="lastname"/>
+
+ <set name="events" table="PERSON_EVENT">
+ <key column="PERSON_ID"/>
+ <many-to-many column="EVENT_ID" class="events.Event"/>
+ </set>
+
+</class>]]></programlisting>
+
+ <para>
+ Hibernate supports all kinds of collection mappings, a <literal><set></literal> being most
+ common. For a many-to-many association (or <emphasis>n:m</emphasis> entity relationship), an
+ association table is needed. Each row in this table represents a link between a person and an event.
+ The table name is configured with the <literal>table</literal> attribute of the <literal>set</literal>
+ element. The identifier column name in the association, for the person's side, is defined with the
+ <literal><key></literal> element, the column name for the event's side with the
+ <literal>column</literal> attribute of the <literal><many-to-many></literal>. You also
+ have to tell Hibernate the class of the objects in your collection (correct: the class on the
+ other side of the collection of references).
+ </para>
+
+ <para>
+ The database schema for this mapping is therefore:
+ </para>
+
+ <programlisting><![CDATA[
+ _____________ __________________
+ | | | | _____________
+ | EVENTS | | PERSON_EVENT | | |
+ |_____________| |__________________| | PERSON |
+ | | | | |_____________|
+ | *EVENT_ID | <--> | *EVENT_ID | | |
+ | EVENT_DATE | | *PERSON_ID | <--> | *PERSON_ID |
+ | TITLE | |__________________| | AGE |
+ |_____________| | FIRSTNAME |
+ | LASTNAME |
+ |_____________|
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-working" revision="2">
+ <title>Working the association</title>
+
+ <para>
+ Let's bring some people and events together in a new method in <literal>EventManager</literal>:
+ </para>
+
+ <programlisting><![CDATA[private void addPersonToEvent(Long personId, Long eventId) {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+ session.beginTransaction();
+
+ Person aPerson = (Person) session.load(Person.class, personId);
+ Event anEvent = (Event) session.load(Event.class, eventId);
+
+ aPerson.getEvents().add(anEvent);
+
+ session.getTransaction().commit();
+}]]></programlisting>
+
+ <para>
+ After loading a <literal>Person</literal> and an <literal>Event</literal>, simply
+ modify the collection using the normal collection methods. As you can see, there is no explicit call
+ to <literal>update()</literal> or <literal>save()</literal>, Hibernate automatically
+ detects that the collection has been modified and needs to be updated. This is called <emphasis>automatic
+ dirty checking</emphasis>, and you can also try it by modifying the name or the date property of
+ any of your objects. As long as they are in <emphasis>persistent</emphasis> state, that is, bound
+ to a particular Hibernate <literal>Session</literal> (i.e. they have been just loaded or saved in
+ a unit of work), Hibernate monitors any changes and executes SQL in a write-behind fashion. The
+ process of synchronizing the memory state with the database, usually only at the end of a unit of
+ work, is called <emphasis>flushing</emphasis>. In our code, the unit of work ends with a commit
+ (or rollback) of the database transaction - as defined by the <literal>thread</literal> configuration
+ option for the <literal>CurrentSessionContext</literal> class.
+ </para>
+
+ <para>
+ You might of course load person and event in different units of work. Or you modify an object
+ outside of a <literal>Session</literal>, when it is not in persistent state (if it was persistent
+ before, we call this state <emphasis>detached</emphasis>). You can even modify a collection when
+ it is detached:
+ </para>
+
+ <programlisting><![CDATA[private void addPersonToEvent(Long personId, Long eventId) {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+ session.beginTransaction();
+
+ Person aPerson = (Person) session
+ .createQuery("select p from Person p left join fetch p.events where p.id = :pid")
+ .setParameter("pid", personId)
+ .uniqueResult(); // Eager fetch the collection so we can use it detached
+
+ Event anEvent = (Event) session.load(Event.class, eventId);
+
+ session.getTransaction().commit();
+
+ // End of first unit of work
+
+ aPerson.getEvents().add(anEvent); // aPerson (and its collection) is detached
+
+ // Begin second unit of work
+
+ Session session2 = HibernateUtil.getSessionFactory().getCurrentSession();
+ session2.beginTransaction();
+
+ session2.update(aPerson); // Reattachment of aPerson
+
+ session2.getTransaction().commit();
+}]]></programlisting>
+
+ <para>
+ The call to <literal>update</literal> makes a detached object persistent again, you could
+ say it binds it to a new unit of work, so any modifications you made to it while detached
+ can be saved to the database. This includes any modifications (additions/deletions) you
+ made to a collection of that entity object.
+ </para>
+
+ <para>
+ Well, this is not much use in our current situation, but it's an important concept you can
+ design into your own application. For now, complete this exercise by adding a new action
+ to the <literal>EventManager</literal>'s main method and call it from the command line. If
+ you need the identifiers of a person and an event - the <literal>save()</literal> method
+ returns it (you might have to modify some of the previous methods to return that identifier):
+ </para>
+
+ <programlisting><![CDATA[else if (args[0].equals("addpersontoevent")) {
+ Long eventId = mgr.createAndStoreEvent("My Event", new Date());
+ Long personId = mgr.createAndStorePerson("Foo", "Bar");
+ mgr.addPersonToEvent(personId, eventId);
+ System.out.println("Added person " + personId + " to event " + eventId);
+}]]></programlisting>
+
+ <para>
+ This was an example of an association between two equally important classes, two entities.
+ As mentioned earlier, there are other classes and types in a typical model, usually "less
+ important". Some you have already seen, like an <literal>int</literal> or a <literal>String</literal>.
+ We call these classes <emphasis>value types</emphasis>, and their instances <emphasis>depend</emphasis>
+ on a particular entity. Instances of these types don't have their own identity, nor are they
+ shared between entities (two persons don't reference the same <literal>firstname</literal>
+ object, even if they have the same first name). Of course, value types can not only be found in
+ the JDK (in fact, in a Hibernate application all JDK classes are considered value types), but
+ you can also write dependent classes yourself, <literal>Address</literal> or <literal>MonetaryAmount</literal>,
+ for example.
+ </para>
+
+ <para>
+ You can also design a collection of value types. This is conceptually very different from a
+ collection of references to other entities, but looks almost the same in Java.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-valuecollections">
+ <title>Collection of values</title>
+
+ <para>
+ We add a collection of value typed objects to the <literal>Person</literal> entity. We want to
+ store email addresses, so the type we use is <literal>String</literal>, and the collection is
+ again a <literal>Set</literal>:
+ </para>
+ <programlisting><![CDATA[private Set emailAddresses = new HashSet();
+
+public Set getEmailAddresses() {
+ return emailAddresses;
+}
+
+public void setEmailAddresses(Set emailAddresses) {
+ this.emailAddresses = emailAddresses;
+}]]></programlisting>
+
+ <para>
+ The mapping of this <literal>Set</literal>:
+ </para>
+
+ <programlisting><![CDATA[<set name="emailAddresses" table="PERSON_EMAIL_ADDR">
+ <key column="PERSON_ID"/>
+ <element type="string" column="EMAIL_ADDR"/>
+</set>]]></programlisting>
+
+ <para>
+ The difference compared with the earlier mapping is the <literal>element</literal> part, which tells Hibernate that the collection
+ does not contain references to another entity, but a collection of elements of type
+ <literal>String</literal> (the lowercase name tells you it's a Hibernate mapping type/converter).
+ Once again, the <literal>table</literal> attribute of the <literal>set</literal> element determines
+ the table name for the collection. The <literal>key</literal> element defines the foreign-key column
+ name in the collection table. The <literal>column</literal> attribute in the <literal>element</literal>
+ element defines the column name where the <literal>String</literal> values will actually be stored.
+ </para>
+
+ <para>
+ Have a look at the updated schema:
+ </para>
+
+ <programlisting><![CDATA[
+ _____________ __________________
+ | | | | _____________
+ | EVENTS | | PERSON_EVENT | | | ___________________
+ |_____________| |__________________| | PERSON | | |
+ | | | | |_____________| | PERSON_EMAIL_ADDR |
+ | *EVENT_ID | <--> | *EVENT_ID | | | |___________________|
+ | EVENT_DATE | | *PERSON_ID | <--> | *PERSON_ID | <--> | *PERSON_ID |
+ | TITLE | |__________________| | AGE | | *EMAIL_ADDR |
+ |_____________| | FIRSTNAME | |___________________|
+ | LASTNAME |
+ |_____________|
+ ]]></programlisting>
+
+ <para>
+ You can see that the primary key of the collection table is in fact a composite key,
+ using both columns. This also implies that there can't be duplicate email addresses
+ per person, which is exactly the semantics we need for a set in Java.
+ </para>
+
+ <para>
+ You can now try and add elements to this collection, just like we did before by
+ linking persons and events. It's the same code in Java:
+ </para>
+
+ <programlisting><![CDATA[private void addEmailToPerson(Long personId, String emailAddress) {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+ session.beginTransaction();
+
+ Person aPerson = (Person) session.load(Person.class, personId);
+
+ // The getEmailAddresses() might trigger a lazy load of the collection
+ aPerson.getEmailAddresses().add(emailAddress);
+
+ session.getTransaction().commit();
+}]]></programlisting>
+
+ <para>
+ This time we didnt' use a <emphasis>fetch</emphasis> query to initialize the collection.
+ Hence, the call to its getter method will trigger an additional select to initialize
+ it, so we can add an element to it. Monitor the SQL log and try to optimize this with
+ an eager fetch.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-bidirectional" revision="1">
+ <title>Bi-directional associations</title>
+
+ <para>
+ Next we are going to map a bi-directional association - making the association between
+ person and event work from both sides in Java. Of course, the database schema doesn't
+ change, we still have many-to-many multiplicity. A relational database is more flexible
+ than a network programming language, so it doesn't need anything like a navigation
+ direction - data can be viewed and retrieved in any possible way.
+ </para>
+
+ <para>
+ First, add a collection of participants to the <literal>Event</literal> Event class:
+ </para>
+
+ <programlisting><![CDATA[private Set participants = new HashSet();
+
+public Set getParticipants() {
+ return participants;
+}
+
+public void setParticipants(Set participants) {
+ this.participants = participants;
+}]]></programlisting>
+
+ <para>
+ Now map this side of the association too, in <literal>Event.hbm.xml</literal>.
+ </para>
+
+ <programlisting><![CDATA[<set name="participants" table="PERSON_EVENT" inverse="true">
+ <key column="EVENT_ID"/>
+ <many-to-many column="PERSON_ID" class="events.Person"/>
+</set>]]></programlisting>
+
+ <para>
+ As you see, these are normal <literal>set</literal> mappings in both mapping documents.
+ Notice that the column names in <literal>key</literal> and <literal>many-to-many</literal> are
+ swapped in both mapping documents. The most important addition here is the
+ <literal>inverse="true"</literal> attribute in the <literal>set</literal> element of the
+ <literal>Event</literal>'s collection mapping.
+ </para>
+
+ <para>
+ What this means is that Hibernate should take the other side - the <literal>Person</literal> class -
+ when it needs to find out information about the link between the two. This will be a lot easier to
+ understand once you see how the bi-directional link between our two entities is created .
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-usingbidir">
+ <title>Working bi-directional links</title>
+
+ <para>
+ First, keep in mind that Hibernate does not affect normal Java semantics. How did we create a
+ link between a <literal>Person</literal> and an <literal>Event</literal> in the unidirectional
+ example? We added an instance of <literal>Event</literal> to the collection of event references,
+ of an instance of <literal>Person</literal>. So, obviously, if we want to make this link working
+ bi-directional, we have to do the same on the other side - adding a <literal>Person</literal>
+ reference to the collection in an <literal>Event</literal>. This "setting the link on both sides"
+ is absolutely necessary and you should never forget doing it.
+ </para>
+
+ <para>
+ Many developers program defensive and create a link management methods to
+ correctly set both sides, e.g. in <literal>Person</literal>:
+ </para>
+
+ <programlisting><![CDATA[protected Set getEvents() {
+ return events;
+}
+
+protected void setEvents(Set events) {
+ this.events = events;
+}
+
+public void addToEvent(Event event) {
+ this.getEvents().add(event);
+ event.getParticipants().add(this);
+}
+
+public void removeFromEvent(Event event) {
+ this.getEvents().remove(event);
+ event.getParticipants().remove(this);
+}]]></programlisting>
+
+ <para>
+ Notice that the get and set methods for the collection are now protected - this allows classes in the
+ same package and subclasses to still access the methods, but prevents everybody else from messing
+ with the collections directly (well, almost). You should probably do the same with the collection
+ on the other side.
+ </para>
+
+ <para>
+ What about the <literal>inverse</literal> mapping attribute? For you, and for Java, a bi-directional
+ link is simply a matter of setting the references on both sides correctly. Hibernate however doesn't
+ have enough information to correctly arrange SQL <literal>INSERT</literal> and <literal>UPDATE</literal>
+ statements (to avoid constraint violations), and needs some help to handle bi-directional associations
+ properly. Making one side of the association <literal>inverse</literal> tells Hibernate to basically
+ ignore it, to consider it a <emphasis>mirror</emphasis> of the other side. That's all that is necessary
+ for Hibernate to work out all of the issues when transformation a directional navigation model to
+ a SQL database schema. The rules you have to remember are straightforward: All bi-directional associations
+ need one side as <literal>inverse</literal>. In a one-to-many association it has to be the many-side,
+ in many-to-many association you can pick either side, there is no difference.
+ </para>
+
+ </sect2>
+
+ <para>
+ Let's turn this into a small web application.
+ </para>
+
+ </sect1>
+
+ <sect1 id="tutorial-webapp">
+ <title>Part 3 - The EventManager web application</title>
+
+ <para>
+ A Hibernate web application uses <literal>Session</literal> and <literal>Transaction</literal>
+ almost like a standalone application. However, some common patterns are useful. We now write
+ an <literal>EventManagerServlet</literal>. This servlet can list all events stored in the
+ database, and it provides an HTML form to enter new events.
+ </para>
+
+ <sect2 id="tutorial-webapp-servlet" revision="2">
+ <title>Writing the basic servlet</title>
+
+ <para>
+ Create a new class in your source directory, in the <literal>events</literal>
+ package:
+ </para>
+
+ <programlisting><![CDATA[package events;
+
+// Imports
+
+public class EventManagerServlet extends HttpServlet {
+
+ // Servlet code
+}]]></programlisting>
+
+ <para>
+ The servlet handles HTTP <literal>GET</literal> requests only, hence, the method
+ we implement is <literal>doGet()</literal>:
+ </para>
+
+ <programlisting><![CDATA[protected void doGet(HttpServletRequest request,
+ HttpServletResponse response)
+ throws ServletException, IOException {
+
+ SimpleDateFormat dateFormatter = new SimpleDateFormat("dd.MM.yyyy");
+
+ try {
+ // Begin unit of work
+ HibernateUtil.getSessionFactory()
+ .getCurrentSession().beginTransaction();
+
+ // Process request and render page...
+
+ // End unit of work
+ HibernateUtil.getSessionFactory()
+ .getCurrentSession().getTransaction().commit();
+
+ } catch (Exception ex) {
+ HibernateUtil.getSessionFactory()
+ .getCurrentSession().getTransaction().rollback();
+ throw new ServletException(ex);
+ }
+
+}]]></programlisting>
+
+ <para>
+ The pattern we are applying here is called <emphasis>session-per-request</emphasis>.
+ When a request hits the servlet, a new Hibernate <literal>Session</literal> is
+ opened through the first call to <literal>getCurrentSession()</literal> on the
+ <literal>SessionFactory</literal>. Then a database transaction is started—all
+ data access as to occur inside a transaction, no matter if data is read or written
+ (we don't use the auto-commit mode in applications).
+ </para>
+
+ <para>
+ Do <emphasis>not</emphasis> use a new Hibernate <literal>Session</literal> for
+ every database operation. Use one Hibernate <literal>Session</literal> that is
+ scoped to the whole request. Use <literal>getCurrentSession()</literal>, so that
+ it is automatically bound to the current Java thread.
+ </para>
+
+ <para>
+ Next, the possible actions of the request are processed and the response HTML
+ is rendered. We'll get to that part soon.
+ </para>
+
+ <para>
+ Finally, the unit of work ends when processing and rendering is complete. If any
+ problem occured during processing or rendering, an exception will be thrown
+ and the database transaction rolled back. This completes the
+ <literal>session-per-request</literal> pattern. Instead of the transaction
+ demarcation code in every servlet you could also write a servlet filter.
+ See the Hibernate website and Wiki for more information about this pattern,
+ called <emphasis>Open Session in View</emphasis>—you'll need it as soon
+ as you consider rendering your view in JSP, not in a servlet.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-webapp-processing" revision="1">
+ <title>Processing and rendering</title>
+
+ <para>
+ Let's implement the processing of the request and rendering of the page.
+ </para>
+
+<programlisting><![CDATA[// Write HTML header
+PrintWriter out = response.getWriter();
+out.println("<html><head><title>Event Manager</title></head><body>");
+
+// Handle actions
+if ( "store".equals(request.getParameter("action")) ) {
+
+ String eventTitle = request.getParameter("eventTitle");
+ String eventDate = request.getParameter("eventDate");
+
+ if ( "".equals(eventTitle) || "".equals(eventDate) ) {
+ out.println("<b><i>Please enter event title and date.</i></b>");
+ } else {
+ createAndStoreEvent(eventTitle, dateFormatter.parse(eventDate));
+ out.println("<b><i>Added event.</i></b>");
+ }
+}
+
+// Print page
+printEventForm(out);
+listEvents(out, dateFormatter);
+
+// Write HTML footer
+out.println("</body></html>");
+out.flush();
+out.close();]]></programlisting>
+
+ <para>
+ Granted, this coding style with a mix of Java and HTML would not scale
+ in a more complex application—keep in mind that we are only illustrating
+ basic Hibernate concepts in this tutorial. The code prints an HTML
+ header and a footer. Inside this page, an HTML form for event entry and
+ a list of all events in the database are printed. The first method is
+ trivial and only outputs HTML:
+ </para>
+
+ <programlisting><![CDATA[private void printEventForm(PrintWriter out) {
+ out.println("<h2>Add new event:</h2>");
+ out.println("<form>");
+ out.println("Title: <input name='eventTitle' length='50'/><br/>");
+ out.println("Date (e.g. 24.12.2009): <input name='eventDate' length='10'/><br/>");
+ out.println("<input type='submit' name='action' value='store'/>");
+ out.println("</form>");
+}]]></programlisting>
+
+ <para>
+ The <literal>listEvents()</literal> method uses the Hibernate
+ <literal>Session</literal> bound to the current thread to execute
+ a query:
+ </para>
+
+ <programlisting><![CDATA[private void listEvents(PrintWriter out, SimpleDateFormat dateFormatter) {
+
+ List result = HibernateUtil.getSessionFactory()
+ .getCurrentSession().createCriteria(Event.class).list();
+ if (result.size() > 0) {
+ out.println("<h2>Events in database:</h2>");
+ out.println("<table border='1'>");
+ out.println("<tr>");
+ out.println("<th>Event title</th>");
+ out.println("<th>Event date</th>");
+ out.println("</tr>");
+ for (Iterator it = result.iterator(); it.hasNext();) {
+ Event event = (Event) it.next();
+ out.println("<tr>");
+ out.println("<td>" + event.getTitle() + "</td>");
+ out.println("<td>" + dateFormatter.format(event.getDate()) + "</td>");
+ out.println("</tr>");
+ }
+ out.println("</table>");
+ }
+}]]></programlisting>
+
+ <para>
+ Finally, the <literal>store</literal> action is dispatched to the
+ <literal>createAndStoreEvent()</literal> method, which also uses
+ the <literal>Session</literal> of the current thread:
+ </para>
+
+ <programlisting><![CDATA[protected void createAndStoreEvent(String title, Date theDate) {
+ Event theEvent = new Event();
+ theEvent.setTitle(title);
+ theEvent.setDate(theDate);
+
+ HibernateUtil.getSessionFactory()
+ .getCurrentSession().save(theEvent);
+}]]></programlisting>
+
+ <para>
+ That's it, the servlet is complete. A request to the servlet will be processed
+ in a single <literal>Session</literal> and <literal>Transaction</literal>. As
+ earlier in the standalone application, Hibernate can automatically bind these
+ ojects to the current thread of execution. This gives you the freedom to layer
+ your code and access the <literal>SessionFactory</literal> in any way you like.
+ Usually you'd use a more sophisticated design and move the data access code
+ into data access objects (the DAO pattern). See the Hibernate Wiki for more
+ examples.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-webapp-deploy">
+ <title>Deploying and testing</title>
+
+ <para>
+ To deploy this application you have to create a web archive, a WAR. Add the
+ following Ant target to your <literal>build.xml</literal>:
+ </para>
+
+<programlisting><![CDATA[<target name="war" depends="compile">
+ <war destfile="hibernate-tutorial.war" webxml="web.xml">
+ <lib dir="${librarydir}">
+ <exclude name="jsdk*.jar"/>
+ </lib>
+
+ <classes dir="${targetdir}"/>
+ </war>
+</target>]]></programlisting>
+
+ <para>
+ This target creates a file called <literal>hibernate-tutorial.war</literal>
+ in your project directory. It packages all libraries and the <literal>web.xml</literal>
+ descriptor, which is expected in the base directory of your project:
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
+<web-app version="2.4"
+ xmlns="http://java.sun.com/xml/ns/j2ee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
+
+ <servlet>
+ <servlet-name>Event Manager</servlet-name>
+ <servlet-class>events.EventManagerServlet</servlet-class>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>Event Manager</servlet-name>
+ <url-pattern>/eventmanager</url-pattern>
+ </servlet-mapping>
+</web-app>]]></programlisting>
+
+ <para>
+ Before you compile and deploy the web application, note that an additional library
+ is required: <literal>jsdk.jar</literal>. This is the Java servlet development kit,
+ if you don't have this library already, get it from the Sun website and copy it to
+ your library directory. However, it will be only used for compliation and excluded
+ from the WAR package.
+ </para>
+
+ <para>
+ To build and deploy call <literal>ant war</literal> in your project directory
+ and copy the <literal>hibernate-tutorial.war</literal> file into your Tomcat
+ <literal>webapp</literal> directory. If you don't have Tomcat installed, download
+ it and follow the installation instructions. You don't have to change any Tomcat
+ configuration to deploy this application though.
+ </para>
+
+ <para>
+ Once deployed and Tomcat is running, access the application at
+ <literal>http://localhost:8080/hibernate-tutorial/eventmanager</literal>. Make
+ sure you watch the Tomcat log to see Hibernate initialize when the first
+ request hits your servlet (the static initializer in <literal>HibernateUtil</literal>
+ is called) and to get the detailed output if any exceptions occurs.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="tutorial-summary" revision="1">
+ <title>Summary</title>
+
+ <para>
+ This tutorial covered the basics of writing a simple standalone Hibernate application
+ and a small web application.
+ </para>
+
+ <para>
+ If you already feel confident with Hibernate, continue browsing through the reference
+ documentation table of contents for topics you find interesting - most asked are
+ transactional processing (<xref linkend="transactions"/>), fetch
+ performance (<xref linkend="performance"/>), or the usage of the API (<xref linkend="objectstate"/>)
+ and the query features (<xref linkend="objectstate-querying"/>).
+ </para>
+
+ <para>
+ Don't forget to check the Hibernate website for more (specialized) tutorials.
+ </para>
+
+ </sect1>
+
+</chapter>
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/xml.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/xml.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/xml.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,286 @@
+<chapter id="xml">
+ <title>XML Mapping</title>
+
+ <para><emphasis>
+ Note that this is an experimental feature in Hibernate 3.0 and is under
+ extremely active development.
+ </emphasis></para>
+
+ <sect1 id="xml-intro" revision="1">
+ <title>Working with XML data</title>
+
+ <para>
+ Hibernate lets you work with persistent XML data in much the same way
+ you work with persistent POJOs. A parsed XML tree can be thought of
+ as just another way to represent the relational data at the object level,
+ instead of POJOs.
+ </para>
+
+ <para>
+ Hibernate supports dom4j as API for manipulating XML trees. You can write
+ queries that retrieve dom4j trees from the database and have any
+ modification you make to the tree automatically synchronized to the
+ database. You can even take an XML document, parse it using dom4j, and
+ write it to the database with any of Hibernate's basic operations:
+ <literal>persist(), saveOrUpdate(), merge(), delete(), replicate()</literal>
+ (merging is not yet supported).
+ </para>
+
+ <para>
+ This feature has many applications including data import/export,
+ externalization of entity data via JMS or SOAP and XSLT-based reporting.
+ </para>
+
+ <para>
+ A single mapping may be used to simultaneously map properties of a class
+ and nodes of an XML document to the database, or, if there is no class to map,
+ it may be used to map just the XML.
+ </para>
+
+ <sect2 id="xml-intro-mapping">
+ <title>Specifying XML and class mapping together</title>
+
+ <para>
+ Here is an example of mapping a POJO and XML simultaneously:
+ </para>
+
+ <programlisting><![CDATA[<class name="Account"
+ table="ACCOUNTS"
+ node="account">
+
+ <id name="accountId"
+ column="ACCOUNT_ID"
+ node="@id"/>
+
+ <many-to-one name="customer"
+ column="CUSTOMER_ID"
+ node="customer/@id"
+ embed-xml="false"/>
+
+ <property name="balance"
+ column="BALANCE"
+ node="balance"/>
+
+ ...
+
+</class>]]></programlisting>
+ </sect2>
+
+ <sect2 id="xml-onlyxml">
+ <title>Specifying only an XML mapping</title>
+
+ <para>
+ Here is an example where there is no POJO class:
+ </para>
+
+ <programlisting><![CDATA[<class entity-name="Account"
+ table="ACCOUNTS"
+ node="account">
+
+ <id name="id"
+ column="ACCOUNT_ID"
+ node="@id"
+ type="string"/>
+
+ <many-to-one name="customerId"
+ column="CUSTOMER_ID"
+ node="customer/@id"
+ embed-xml="false"
+ entity-name="Customer"/>
+
+ <property name="balance"
+ column="BALANCE"
+ node="balance"
+ type="big_decimal"/>
+
+ ...
+
+</class>]]></programlisting>
+
+ <para>
+ This mapping allows you to access the data as a dom4j tree, or as a graph of
+ property name/value pairs (java <literal>Map</literal>s). The property names
+ are purely logical constructs that may be referred to in HQL queries.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="xml-mapping" revision="1">
+ <title>XML mapping metadata</title>
+
+ <para>
+ Many Hibernate mapping elements accept the <literal>node</literal> attribute.
+ This let's you specify the name of an XML attribute or element that holds the
+ property or entity data. The format of the <literal>node</literal> attribute
+ must be one of the following:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para><literal>"element-name"</literal> - map to the named XML element</para>
+ </listitem>
+ <listitem>
+ <para><literal>"@attribute-name"</literal> - map to the named XML attribute</para>
+ </listitem>
+ <listitem>
+ <para><literal>"."</literal> - map to the parent element</para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>"element-name/@attribute-name"</literal> -
+ map to the named attribute of the named element
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ For collections and single valued associations, there is an additional
+ <literal>embed-xml</literal> attribute. If <literal>embed-xml="true"</literal>,
+ the default, the XML tree for the associated entity (or collection of value type)
+ will be embedded directly in the XML tree for the entity that owns the association.
+ Otherwise, if <literal>embed-xml="false"</literal>, then only the referenced
+ identifier value will appear in the XML for single point associations and
+ collections will simply not appear at all.
+ </para>
+
+ <para>
+ You should be careful not to leave <literal>embed-xml="true"</literal> for
+ too many associations, since XML does not deal well with circularity!
+ </para>
+
+ <programlisting><![CDATA[<class name="Customer"
+ table="CUSTOMER"
+ node="customer">
+
+ <id name="id"
+ column="CUST_ID"
+ node="@id"/>
+
+ <map name="accounts"
+ node="."
+ embed-xml="true">
+ <key column="CUSTOMER_ID"
+ not-null="true"/>
+ <map-key column="SHORT_DESC"
+ node="@short-desc"
+ type="string"/>
+ <one-to-many entity-name="Account"
+ embed-xml="false"
+ node="account"/>
+ </map>
+
+ <component name="name"
+ node="name">
+ <property name="firstName"
+ node="first-name"/>
+ <property name="initial"
+ node="initial"/>
+ <property name="lastName"
+ node="last-name"/>
+ </component>
+
+ ...
+
+</class>]]></programlisting>
+
+ <para>
+ in this case, we have decided to embed the collection of account ids, but not
+ the actual account data. The following HQL query:
+ </para>
+
+ <programlisting><![CDATA[from Customer c left join fetch c.accounts where c.lastName like :lastName]]></programlisting>
+
+ <para>
+ Would return datasets such as this:
+ </para>
+
+ <programlisting><![CDATA[<customer id="123456789">
+ <account short-desc="Savings">987632567</account>
+ <account short-desc="Credit Card">985612323</account>
+ <name>
+ <first-name>Gavin</first-name>
+ <initial>A</initial>
+ <last-name>King</last-name>
+ </name>
+ ...
+</customer>]]></programlisting>
+
+ <para>
+ If you set <literal>embed-xml="true"</literal> on the <literal><one-to-many></literal>
+ mapping, the data might look more like this:
+ </para>
+
+ <programlisting><![CDATA[<customer id="123456789">
+ <account id="987632567" short-desc="Savings">
+ <customer id="123456789"/>
+ <balance>100.29</balance>
+ </account>
+ <account id="985612323" short-desc="Credit Card">
+ <customer id="123456789"/>
+ <balance>-2370.34</balance>
+ </account>
+ <name>
+ <first-name>Gavin</first-name>
+ <initial>A</initial>
+ <last-name>King</last-name>
+ </name>
+ ...
+</customer>]]></programlisting>
+
+ </sect1>
+
+
+ <sect1 id="xml-manipulation" revision="1">
+ <title>Manipulating XML data</title>
+
+ <para>
+ Let's rearead and update XML documents in the application. We do this by
+ obtaining a dom4j session:
+ </para>
+
+ <programlisting><![CDATA[Document doc = ....;
+
+Session session = factory.openSession();
+Session dom4jSession = session.getSession(EntityMode.DOM4J);
+Transaction tx = session.beginTransaction();
+
+List results = dom4jSession
+ .createQuery("from Customer c left join fetch c.accounts where c.lastName like :lastName")
+ .list();
+for ( int i=0; i<results.size(); i++ ) {
+ //add the customer data to the XML document
+ Element customer = (Element) results.get(i);
+ doc.add(customer);
+}
+
+tx.commit();
+session.close();]]></programlisting>
+
+ <programlisting><![CDATA[Session session = factory.openSession();
+Session dom4jSession = session.getSession(EntityMode.DOM4J);
+Transaction tx = session.beginTransaction();
+
+Element cust = (Element) dom4jSession.get("Customer", customerId);
+for ( int i=0; i<results.size(); i++ ) {
+ Element customer = (Element) results.get(i);
+ //change the customer name in the XML and database
+ Element name = customer.element("name");
+ name.element("first-name").setText(firstName);
+ name.element("initial").setText(initial);
+ name.element("last-name").setText(lastName);
+}
+
+tx.commit();
+session.close();]]></programlisting>
+
+ <para>
+ It is extremely useful to combine this feature with Hibernate's <literal>replicate()</literal>
+ operation to implement XML-based data import/export.
+ </para>
+
+ </sect1>
+
+</chapter>
+
Property changes on: trunk/Hibernate3/documentation/manual/en-US/src/main/docbook/modules/xml.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/css/html.css
===================================================================
--- trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/css/html.css (rev 0)
+++ trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/css/html.css 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,97 @@
+A {
+ color: #003399;
+}
+
+A:active {
+ color: #003399;
+}
+
+A:visited {
+ color: #888888;
+}
+
+P, OL, UL, LI, DL, DT, DD, BLOCKQUOTE {
+ color: #000000;
+}
+
+TD, TH, SPAN {
+ color: #000000;
+}
+
+BLOCKQUOTE {
+ margin-right: 0px;
+}
+
+
+H1, H2, H3, H4, H5, H6 {
+ color: #000000;
+ font-weight:500;
+ margin-top:10px;
+ padding-top:15px;
+}
+
+TABLE {
+ border-collapse: collapse;
+ border-spacing:0;
+ border: 1px thin black;
+ empty-cells: hide;
+}
+
+TD {
+ padding: 4pt;
+}
+
+H1 { font-size: 150%; }
+H2 { font-size: 140%; }
+H3 { font-size: 110%; font-weight: bold; }
+H4 { font-size: 110%; font-weight: bold;}
+H5 { font-size: 100%; font-style: italic; }
+H6 { font-size: 100%; font-style: italic; }
+
+TT {
+font-size: 90%;
+ font-family: "Courier New", Courier, monospace;
+ color: #000000;
+}
+
+PRE {
+font-size: 100%;
+ padding: 5px;
+ border-style: solid;
+ border-width: 1px;
+ border-color: #CCCCCC;
+ background-color: #F4F4F4;
+}
+
+UL, OL, LI {
+ list-style: disc;
+}
+
+HR {
+ width: 100%;
+ height: 1px;
+ background-color: #CCCCCC;
+ border-width: 0px;
+ padding: 0px;
+ color: #CCCCCC;
+}
+
+.variablelist {
+ padding-top: 10;
+ padding-bottom:10;
+ margin:0;
+}
+
+.itemizedlist, UL {
+ padding-top: 0;
+ padding-bottom:0;
+ margin:0;
+}
+
+.term {
+ font-weight:bold;
+}
+
+
+
+
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/AuthorWork.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/AuthorWork.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/AuthorWork.zargo
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/AuthorWork.zargo
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/CustomerOrderProduct.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/CustomerOrderProduct.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/CustomerOrderProduct.zargo
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/CustomerOrderProduct.zargo
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/EmployerEmployee.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/EmployerEmployee.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/EmployerEmployee.zargo
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/EmployerEmployee.zargo
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/full_cream.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/full_cream.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/full_cream.svg
===================================================================
--- trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/full_cream.svg (rev 0)
+++ trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/full_cream.svg 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,429 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"
+[
+ <!ATTLIST svg
+ xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink">
+]>
+<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
+<svg
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="354.331"
+ height="336.614"
+ id="svg1">
+ <defs
+ id="defs3">
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop128" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="1"
+ id="stop129" />
+ </linearGradient>
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient130"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ <radialGradient
+ cx="0.5"
+ cy="0.5"
+ fx="0.5"
+ fy="0.5"
+ r="0.5"
+ id="radialGradient131"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ </defs>
+ <g
+ transform="matrix(0.823795,0,0,0.823795,0.120302,5.25349)"
+ style="font-size:12;"
+ id="g659">
+ <rect
+ width="212.257"
+ height="57.2441"
+ x="17.9576"
+ y="100.132"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect137" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ transform="matrix(0.743454,0,0,0.482981,6.46949,52.2178)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect132" />
+ </g>
+ <rect
+ width="325.86"
+ height="63.6537"
+ x="17.4083"
+ y="15.194"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect136" />
+ <rect
+ width="325.86"
+ height="63.6537"
+ x="13.6713"
+ y="12.4966"
+ style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect126" />
+ <g
+ transform="matrix(1.14345,0,0,0.729078,-1.67818,105.325)"
+ style="font-size:12;"
+ id="g164">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="222.966"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect138" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="221.002"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect133" />
+ </g>
+ <text
+ x="170.824753"
+ y="58.402939"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text183">
+ <tspan
+ x="170.824997"
+ y="58.402901"
+ id="tspan360">
+Application</tspan>
+ </text>
+ <text
+ x="178.076340"
+ y="364.281433"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text197">
+ <tspan
+ x="178.076004"
+ y="364.281006"
+ id="tspan421">
+Database</tspan>
+ </text>
+ <text
+ x="68.605331"
+ y="138.524582"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text216">
+ <tspan
+ x="68.605301"
+ y="138.524994"
+ id="tspan384">
+SessionFactory</tspan>
+ </text>
+ <rect
+ width="67.0014"
+ height="101.35"
+ x="196.927"
+ y="89.2389"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect387" />
+ <rect
+ width="67.0014"
+ height="101.35"
+ x="194.633"
+ y="86.4389"
+ style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect388" />
+ <text
+ x="249.108841"
+ y="173.885559"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text389">
+ <tspan
+ x="249.108994"
+ y="173.886002"
+ id="tspan392">
+Session</tspan>
+ </text>
+ <rect
+ width="73.0355"
+ height="101.35"
+ x="270.995"
+ y="90.0018"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect395" />
+ <rect
+ width="73.0355"
+ height="101.35"
+ x="267.869"
+ y="87.2018"
+ style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect396" />
+ <text
+ x="328.593658"
+ y="174.715622"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text397">
+ <tspan
+ x="328.593994"
+ y="174.716003"
+ id="tspan563">
+Transaction</tspan>
+ </text>
+ <g
+ transform="matrix(0.29544,0,0,0.397877,9.70533,103.96)"
+ style="font-size:12;"
+ id="g565">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect566" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect567" />
+ </g>
+ <text
+ x="25.592752"
+ y="204.497803"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:10;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text568">
+ <tspan
+ x="25.592800"
+ y="204.498001"
+ id="tspan662">
+TransactionFactory</tspan>
+ </text>
+ <g
+ transform="matrix(0.298082,0,0,0.397877,99.6898,103.96)"
+ style="font-size:12;"
+ id="g573">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect574" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect575" />
+ </g>
+ <text
+ x="134.030670"
+ y="205.532791"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:10;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text576">
+ <tspan
+ x="134.031006"
+ y="205.533005"
+ id="tspan664">
+ConnectionProvider</tspan>
+ </text>
+ <g
+ transform="matrix(1.14345,0,0,0.729078,-1.67818,38.9539)"
+ style="font-size:12;"
+ id="g587">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="222.966"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect588" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="221.002"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect589" />
+ </g>
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="25.6196"
+ y="206.028"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect594" />
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="24.4229"
+ y="204.135"
+ style="font-size:12;fill:#b3b3b3;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect595" />
+ <text
+ x="85.575645"
+ y="282.300354"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text596">
+ <tspan
+ x="85.575600"
+ y="282.299988"
+ id="tspan607">
+JNDI</tspan>
+ </text>
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="236.937"
+ y="206.791"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect610" />
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="235.741"
+ y="204.898"
+ style="font-size:12;fill:#b3b3b3;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect611" />
+ <text
+ x="342.093201"
+ y="283.226410"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text612">
+ <tspan
+ x="342.092987"
+ y="283.226013"
+ id="tspan621">
+JTA</tspan>
+ </text>
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="130.134"
+ y="206.791"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect616" />
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="128.937"
+ y="204.898"
+ style="font-size:12;fill:#b3b3b3;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect617" />
+ <text
+ x="212.445343"
+ y="283.226410"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text618">
+ <tspan
+ x="212.445007"
+ y="283.226013"
+ id="tspan623">
+JDBC</tspan>
+ </text>
+ <g
+ transform="matrix(0.823795,0,0,0.823795,0.120302,6.19341)"
+ style="font-size:12;"
+ id="g637">
+ <g
+ transform="matrix(0.499515,0,0,0.415467,-0.237339,5.61339)"
+ id="g167">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect134" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect135" />
+ </g>
+ <text
+ x="33.749969"
+ y="50.589706"
+ style="font-size:11;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text188">
+ <tspan
+ x="33.750000"
+ y="50.589699"
+ id="tspan635">
+Transient Objects</tspan>
+ </text>
+ </g>
+ <g
+ transform="matrix(0.823795,0,0,0.823795,0.120302,5.25349)"
+ style="font-size:12;"
+ id="g644">
+ <g
+ transform="matrix(0.297486,0,0,0.516482,230.251,36.9178)"
+ id="g364">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect365" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect366" />
+ </g>
+ <text
+ x="277.123230"
+ y="85.155571"
+ style="font-size:11;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text367">
+ <tspan
+ x="277.122986"
+ y="85.155602"
+ id="tspan631">
+Persistent</tspan>
+ <tspan
+ x="277.122986"
+ y="96.155602"
+ id="tspan633">
+Objects</tspan>
+ </text>
+ </g>
+</svg>
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/hibernate_logo_a.png
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/hibernate_logo_a.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/lite.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/lite.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/lite.svg
===================================================================
--- trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/lite.svg (rev 0)
+++ trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/lite.svg 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,334 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"
+[
+ <!ATTLIST svg
+ xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink">
+]>
+<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
+<svg
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="318.898"
+ height="248.031"
+ id="svg1">
+ <defs
+ id="defs3">
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop128" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="1"
+ id="stop129" />
+ </linearGradient>
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient130"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ <radialGradient
+ cx="0.5"
+ cy="0.5"
+ fx="0.5"
+ fy="0.5"
+ r="0.5"
+ id="radialGradient131"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ </defs>
+ <rect
+ width="291.837"
+ height="57.0074"
+ x="17.3169"
+ y="18.646"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect136" />
+ <rect
+ width="291.837"
+ height="57.0074"
+ x="13.9703"
+ y="16.2302"
+ style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect126" />
+ <g
+ transform="matrix(0.326107,0,0,0.765831,9.59261,8.98517)"
+ style="font-size:12;"
+ id="g161">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect137" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect132" />
+ </g>
+ <g
+ transform="matrix(1.02406,0,0,0.652953,0.223384,39.9254)"
+ style="font-size:12;"
+ id="g164">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="222.966"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect138" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="221.002"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect133" />
+ </g>
+ <g
+ transform="matrix(0.449834,0,0,0.338463,-3.15909,9.73319)"
+ style="font-size:12;"
+ id="g167">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect134" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect135" />
+ </g>
+ <text
+ x="302.277679"
+ y="65.943230"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text183">
+ <tspan
+ x="302.277954"
+ y="65.943184"
+ id="tspan360">
+Application</tspan>
+ </text>
+ <text
+ x="36.235924"
+ y="63.796055"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:14;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text188">
+ <tspan
+ x="36.235950"
+ y="63.796051"
+ id="tspan427">
+Transient Objects</tspan>
+ </text>
+ <text
+ x="180.416245"
+ y="290.543701"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text197">
+ <tspan
+ x="180.415939"
+ y="290.543549"
+ id="tspan421">
+Database</tspan>
+ </text>
+ <text
+ x="25.037701"
+ y="179.154755"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text216">
+ <tspan
+ x="25.037655"
+ y="179.154648"
+ id="tspan384">
+SessionFactory</tspan>
+ </text>
+ <g
+ transform="matrix(0.252763,0,0,0.765831,109.104,8.98517)"
+ style="font-size:12;"
+ id="g386">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect387" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect388" />
+ </g>
+ <g
+ transform="matrix(0.297394,0,0,0.572692,101.502,21.6359)"
+ style="font-size:12;"
+ id="g364">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect365" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect366" />
+ </g>
+ <text
+ x="202.746506"
+ y="102.992203"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:14;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text367">
+ <tspan
+ x="202.746948"
+ y="102.992249"
+ id="tspan423">
+Persistent</tspan>
+ <tspan
+ x="202.746948"
+ y="116.992355"
+ id="tspan425">
+Objects</tspan>
+ </text>
+ <text
+ x="174.458496"
+ y="180.080795"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text389">
+ <tspan
+ x="174.458618"
+ y="180.080338"
+ id="tspan392">
+Session</tspan>
+ </text>
+ <g
+ transform="matrix(0.127369,0,0,0.765831,188.675,8.98517)"
+ style="font-size:12;"
+ id="g394">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect395" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect396" />
+ </g>
+ <text
+ x="260.413269"
+ y="179.154739"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text397">
+ <tspan
+ x="260.412964"
+ y="179.154343"
+ id="tspan400">
+JDBC</tspan>
+ </text>
+ <g
+ transform="matrix(0.127369,0,0,0.765831,229.156,8.98517)"
+ style="font-size:12;"
+ id="g405">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect406" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect407" />
+ </g>
+ <text
+ x="320.606903"
+ y="179.154739"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text408">
+ <tspan
+ x="320.606964"
+ y="179.154343"
+ id="tspan417">
+JNDI</tspan>
+ </text>
+ <g
+ transform="matrix(0.127369,0,0,0.765831,269.281,8.98517)"
+ style="font-size:12;"
+ id="g411">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect412" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect413" />
+ </g>
+ <text
+ x="377.096313"
+ y="179.154739"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text414">
+ <tspan
+ x="377.096008"
+ y="179.154999"
+ id="tspan145">
+JTA</tspan>
+ </text>
+</svg>
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/overview.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/overview.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/overview.svg
===================================================================
--- trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/overview.svg (rev 0)
+++ trunk/Hibernate3/documentation/manual/en-US/src/main/resources/shared/images/overview.svg 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,250 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"
+[
+ <!ATTLIST svg
+ xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink">
+]>
+<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
+<svg
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="248.031"
+ height="248.031"
+ id="svg1">
+ <defs
+ id="defs3">
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop128" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="1"
+ id="stop129" />
+ </linearGradient>
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient130"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ <radialGradient
+ cx="0.5"
+ cy="0.5"
+ fx="0.5"
+ fy="0.5"
+ r="0.5"
+ id="radialGradient131"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ </defs>
+ <g
+ transform="matrix(0.771934,0,0,0.771934,4.36019,-3.02123)"
+ style="font-size:12;"
+ id="g158">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="17.3527"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect136" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="15.3883"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect126" />
+ </g>
+ <g
+ transform="matrix(0.771934,0,0,0.771934,4.36019,3.04452)"
+ style="font-size:12;"
+ id="g161">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect137" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect132" />
+ </g>
+ <g
+ transform="matrix(0.771934,0,0,0.771934,4.36019,8.0993)"
+ style="font-size:12;"
+ id="g164">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="222.966"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect138" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="221.002"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect133" />
+ </g>
+ <g
+ transform="matrix(0.771934,0,0,0.543505,2.59104,21.1103)"
+ style="font-size:12;"
+ id="g167">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect134" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect135" />
+ </g>
+ <text
+ x="105.392174"
+ y="56.568123"
+ transform="scale(0.771934,0.771934)"
+ style="font-size:24;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text183">
+ <tspan
+ x="105.392273"
+ y="56.568146"
+ id="tspan186">
+Application</tspan>
+ </text>
+ <text
+ x="81.820183"
+ y="103.149330"
+ transform="scale(0.771934,0.771934)"
+ style="font-size:20;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text188">
+ <tspan
+ x="81.820213"
+ y="103.149727"
+ id="tspan206">
+Persistent Objects</tspan>
+ </text>
+ <text
+ x="111.548180"
+ y="278.927887"
+ transform="scale(0.771934,0.771934)"
+ style="font-size:24;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text197">
+ <tspan
+ x="111.547874"
+ y="278.927551"
+ id="tspan200">
+Database</tspan>
+ </text>
+ <text
+ x="94.436180"
+ y="153.805740"
+ transform="scale(0.771934,0.771934)"
+ style="font-size:24;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text216">
+ <tspan
+ x="94.436180"
+ y="153.805740"
+ id="tspan221">
+HIBERNATE</tspan>
+ </text>
+ <g
+ transform="matrix(0.771934,0,0,0.771934,2.59083,1.02261)"
+ style="font-size:12;"
+ id="g254">
+ <g
+ transform="translate(4.58374,2.61928)"
+ id="g176">
+ <g
+ transform="matrix(0.571429,0,0,0.67347,-10.6174,117.093)"
+ id="g170">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect171" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect172" />
+ </g>
+ <g
+ transform="matrix(0.571429,0,0,0.67347,138.682,117.093)"
+ id="g173">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect174" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect175" />
+ </g>
+ </g>
+ <text
+ x="47.259438"
+ y="182.367538"
+ style="font-weight:bold;stroke-width:1pt;font-family:Courier;"
+ id="text191">
+ <tspan
+ x="47.259399"
+ y="182.367996"
+ id="tspan212">
+hibernate.</tspan>
+ <tspan
+ x="47.259399"
+ y="194.367996"
+ id="tspan214">
+properties</tspan>
+ </text>
+ <text
+ x="198.523010"
+ y="188.260941"
+ style="font-weight:normal;stroke-width:1pt;font-family:helvetica;"
+ id="text194">
+ <tspan
+ id="tspan195">
+XML Mapping</tspan>
+ </text>
+ </g>
+</svg>
Added: trunk/Hibernate3/documentation/manual/es-ES/pom.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/es-ES/pom.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/es-ES/pom.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-manual</artifactId>
+ <version>3.3.0.beta1</version>
+ <relativePath>../../../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>hibernate-manual-${translation}</artifactId>
+ <packaging>pom</packaging>
+ <name>Hibernate Manual (${translation})</name>
+
+ <properties>
+ <translation>es-ES</translation>
+ </properties>
+
+</project>
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/master.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/master.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/master.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,203 @@
+<?xml version='1.0' encoding="iso-8859-1"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3CR3//EN"
+ "../support/docbook-dtd/docbookx.dtd"
+[
+<!ENTITY quickstart SYSTEM "modules/quickstart.xml">
+<!ENTITY tutorial SYSTEM "modules/tutorial.xml">
+<!ENTITY architecture SYSTEM "modules/architecture.xml">
+<!ENTITY configuration SYSTEM "modules/configuration.xml">
+<!ENTITY persistent-classes SYSTEM "modules/persistent_classes.xml">
+<!ENTITY basic-mapping SYSTEM "modules/basic_mapping.xml">
+<!ENTITY collection-mapping SYSTEM "modules/collection_mapping.xml">
+<!ENTITY association-mapping SYSTEM "modules/association_mapping.xml">
+<!ENTITY component-mapping SYSTEM "modules/component_mapping.xml">
+<!ENTITY inheritance-mapping SYSTEM "modules/inheritance_mapping.xml">
+<!ENTITY session-api SYSTEM "modules/session_api.xml">
+<!ENTITY transactions SYSTEM "modules/transactions.xml">
+<!ENTITY events SYSTEM "modules/events.xml">
+<!ENTITY batch SYSTEM "modules/batch.xml">
+<!ENTITY query-hql SYSTEM "modules/query_hql.xml">
+<!ENTITY query-criteria SYSTEM "modules/query_criteria.xml">
+<!ENTITY query-sql SYSTEM "modules/query_sql.xml">
+<!ENTITY filters SYSTEM "modules/filters.xml">
+<!ENTITY xml SYSTEM "modules/xml.xml">
+<!ENTITY performance SYSTEM "modules/performance.xml">
+<!ENTITY toolset-guide SYSTEM "modules/toolset_guide.xml">
+<!ENTITY example-parentchild SYSTEM "modules/example_parentchild.xml">
+<!ENTITY example-weblog SYSTEM "modules/example_weblog.xml">
+<!ENTITY example-mappings SYSTEM "modules/example_mappings.xml">
+<!ENTITY best-practices SYSTEM "modules/best_practices.xml">
+]>
+
+<book lang="es">
+
+ <bookinfo>
+ <title>HIBERNATE - Persistencia Relacional para Java Idiomático</title>
+ <subtitle>Documentación de Referencia de Hibernate</subtitle>
+ <releaseinfo>3.0.5</releaseinfo>
+ </bookinfo>
+
+ <toc/>
+
+ <preface id="preface" revision="2">
+ <title>Prefacio</title>
+
+ <para>
+ Advertencia! Esta es una versión traducida del inglés de
+ la documentacién de referencia de Hibernate. La versión
+ traducida puede no estar actualizada! Sin embargo, las diferencias
+ deberían ser sólo menores. Consulta la documentación
+ de referencia en inglés si estás perdiendo información
+ o encuentras algún error de traducción. Si quieres colaborar con
+ una traducción en particular, contáctanos en la lista de correo
+ de desarrolladores de Hibernate.
+ </para>
+
+ <para>
+ Traductor(es): Bernardo Antonio Buffa Colomé <kreimer at bbs.frc.utn.edu.ar>
+<!--,
+ Antonio López Gota <antoniogota at gmail.com> -->
+ </para>
+
+ <para>
+ Trabajar con software orientado a objetos y una base de datos relacional puede ser
+ incómodo y consumir tiempo en los entornos de empresa de hoy. Hibernate es una
+ herramienta de mapeo objeto/relacional para entornos Java. El término mapeo
+ objeto/relacional (MOR) hace referencia a la técnica de mapear una
+ representación de datos desde un modelo de objetos a un modelo de datos relacional
+ con un esquema basado en SQL.
+ </para>
+
+ <para>
+ Hibernate no sólo se encarga de mapear de clases Java a tablas de base de datos
+ (y de tipos de datos de Java a tipos de datos SQL), sino que también provee
+ facilidades de consulta y recuperación de datos y puede reducir significativamente
+ el tiempo de desarrollo que de otra forma se gasta en el manejo de los datos en SQL y JDBC.
+ </para>
+
+ <para>
+ La meta de Hibernate es relevar al desarrollador del 95 por ciento de las tareas comunes
+ relacionadas a la programación de la persistencia de los datos.
+ Hibernate puede no ser la mejor solución para aplicaciones que usan solamente
+ procedimientos almacenados para implementar la lógica de negocio en la base de
+ datos, es mas útil con modelos de dominio orientados a objetos y lógica de
+ negocio en middle-tier basada en Java. Sin embargo, Hibernate ciertamente puede ayudarte
+ a quitar o encapsular código SQL específico de vendedor y ayudará
+ con la tarea común de traducción de resultados desde una representación
+ tabular a un grafo de objetos.
+ </para>
+
+ <para>
+ Si eres nuevo en Hibernate y lo del Mapeo Objeto/Relacional o incluso en Java,
+ sigue por favor estos pasos:
+ </para>
+
+ <orderedlist>
+ <listitem>
+ <para>
+ Lee <xref linkend="quickstart"/> para un tutorial de 30 minutos, usando Tomcat.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Lee <xref linkend="architecture"/> para entender los entornos en los que
+ puede ser usado Hibernate.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Dale una mirada al directorio <literal>eg/</literal> en la distribución
+ de Hibernate, contiene una aplicación independiente simple.
+ Copia tu driver JDBC al directorio <literal>lib/</literal> y edita
+ <literal>etc/hibernate.properties</literal>, especificando los valores
+ correctos para tu base de datos. Desde línea de comandos en el
+ directorio de la distribución, tipea <literal>ant eg</literal>
+ (usando Ant), o bajo Windows, tipea <literal>build eg</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Usa esta documentación de referencia como tu fuente de información
+ primaria. Ten en consideración leer <emphasis>Hibernate in Action</emphasis>
+ (http://www.manning.com/bauer) si necesitas mas ayuda con el diseño
+ de aplicaciones o si prefieres un tutorial paso a paso.
+ Visita también http://caveatemptor.hibernate.org y descarga la aplicación
+ de ejemplo para Hibernate in Action.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Los FAQs son respondidos en el sitio web de Hibernate.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ En el sitio web de Hibernate hay enlaces a demos de terceros, ejemplos
+ y tutoriales.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ El Area de Comunidad en el sitio web de Hibernate es una buena fuente
+ de patrones de diseño y varias soluciones de integración
+ (Tomcat, JBoss, Struts, EJB, etc.).
+ </para>
+ </listitem>
+ </orderedlist>
+
+ <para>
+ Si tienes preguntas, usa el foro de usuarios enlazado en el sitio web de Hibernate.
+ También proveemos un sistema de seguimiento JIRA para reportes de defectos y
+ peticiones de nuevas características.
+ Si estas interesado en el desarrollo de Hibernate, únete a la lista de correo
+ de desarrolladores. Si estas interesado en traducir esta documentación a tu
+ lenguaje, contáctanos en la lista de correo de desarrolladores.
+ </para>
+
+ <para>
+ A través de JBoss Inc. (see http://www.hibernate.org/SupportTraining/) hay
+ disponibilidad de soporte comercial de desarrollo, soporte de producción y
+ entrenamiento en Hibernate.
+ Hibernate es un proyecto de la suite de productos de código abierto
+ JBoss Professional.
+ </para>
+
+ </preface>
+
+ &quickstart;
+ &tutorial;
+ &architecture;
+
+ &configuration;
+
+ &persistent-classes;
+
+ &basic-mapping;
+ &collection-mapping;
+ &association-mapping;
+ &component-mapping;
+ &inheritance-mapping;
+
+ &session-api;
+ &transactions;
+ &events;
+ &batch;
+
+ &query-hql;
+ &query-criteria;
+ &query-sql;
+ &filters;
+ &xml;
+
+ &performance;
+
+ &toolset-guide;
+
+ &example-parentchild;
+ &example-weblog;
+ &example-mappings;
+
+ &best-practices;
+
+</book>
+
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/architecture.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/architecture.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/architecture.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,279 @@
+<chapter id="architecture">
+
+ <title>Arquitectura</title>
+
+ <sect1 id="architecture-overview" revision="1">
+ <title>Visión General</title>
+
+ <para>
+ Una visión a (muy) alto nivel de la arquitectura de Hibernate:
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/overview.svg" format="SVG" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/overview.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ Este diagrama muestra a Hibernate usando la base de datos y los datos de
+ configuración para proveer servicios de persistencia (y objetos
+ persistentes) a la aplicación.
+ </para>
+
+ <para>
+ Nos gustaría mostrar una vista más detallada de la arquitectura de tiempo
+ de ejecución. Desafortunadamente, Hibernate es flexible y soporta diferentes
+ enfoques. Mostraremos los dos extremos. En la arquitectura "sencilla", es la
+ aplicación la que provee su propias conexiones JDBC y gestiona sus propias
+ transacciones. Este enfoque usa un mínimo subconjunto de la API de Hibernate:
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/lite.svg" format="SVG" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/lite.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ La arquitectura "full cream" abstrae a la aplicación de las APIs
+ de JDBC/JTA y deja que Hibernate se encargue de los detalles.
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/full_cream.svg" format="SVG" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/full_cream.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ He aquí algunas definiciones de los objetos en los diagramas:
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term>SessionFactory (<literal>org.hibernate.SessionFactory</literal>)</term>
+ <listitem>
+ <para>
+ Caché threadsafe (inmutable) de mapeos compilados para
+ una sola base de datos. Es una fábrica de <literal>Session</literal>
+ y un cliente de <literal>ConnectionProvider</literal>. Opcionalmente,
+ puede mantener una caché (de segundo nivel) de datos reusables
+ entre transacciones, a un nivel de proceso o de cluster.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Session (<literal>org.hibernate.Session</literal>)</term>
+ <listitem>
+ <para>
+ Objeto mono-hebra, de corta vida que representa una conversación
+ entre la aplicación y el almacenamiento persistente. Envuelve una
+ conexión JDBC. Es una fábrica de <literal>Transaction</literal>.
+ Mantiene una caché requerida (de primer nivel) de objetos persistentes,
+ usada mientras se navega el grafo de objetos o se recuperen objetos por
+ identificador.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Objetos y colecciones persistentes</term>
+ <listitem>
+ <para>
+ Objetos de corta vida, mono-hebra conteniendo estado persistente y
+ funciónalidad de negocio. Estos pueden ser JavaBeans/POJOs
+ (Plain Old Java Objects, o sea, cualquier objeto Java), la única
+ cosa especial en ellos es que estan asociados actualmente con una
+ (y sólo una) <literal>Session</literal>. Tan pronto como la
+ <literal>Session</literal> sea cerrada, serán separados y
+ estarán libres para ser usados en cualquier capa de aplicación.
+ (por ejemplo, directamente como objetos de transferencia de datos hacia
+ y desde la capa de presentación).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Objetos y colecciones transitorios y separados</term>
+ <listitem>
+ <para>
+ Instancias de clases persistentes que no estan acutualmente asociadas
+ con una <literal>Session</literal>. Pueden haber sido instanciadas por
+ la aplicación y (aún) no haber sido hechas persistentes,
+ o pueden haber sido instanciadas por una <literal>Session</literal> cerrada.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Transaction (<literal>org.hibernate.Transaction</literal>)</term>
+ <listitem>
+ <para>
+ (Opcional) Un objeto de corta vida, mono-hebra, usado por la aplicación
+ para especificar unidades atómicas de trabajo. Abstrae a la aplicación
+ de las subyacentes transacciones JDBC, JTA o CORBA. En algunos casos, una
+ <literal>Session</literal> puede extenderse sobre varias <literal>Transaction</literal>s.
+ Sin embargo, la demarcación de la transacción, ya sea usando la API
+ subyacente o <literal>Transaction</literal>, nunca es opcional!
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>ConnectionProvider (<literal>org.hibernate.connection.ConnectionProvider</literal>)</term>
+ <listitem>
+ <para>
+ (Opcional) Una fábrica (y pool) de conexiones JDBC. Abstrae a la aplicación
+ del <literal>Datasource</literal> o <literal>DriverManager</literal> subyacente.
+ No se expone a la aplicación, pero puede ser extendido/implementado por
+ el desarrollador.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>TransactionFactory (<literal>org.hibernate.TransactionFactory</literal>)</term>
+ <listitem>
+ <para>
+ (Opcional) Una fábrica de instancias de <literal>Transaction</literal>.
+ No se expone a la aplicación, pero puede ser extendido/implementado por
+ el desarrollador.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><emphasis>Interfaces de Extensión</emphasis></term>
+ <listitem>
+ <para>
+ Hibernate ofrece muchas interfaces de extensión opcional que puedes
+ implementar para modificar a medida el comportamiento de tu capa de persistencia.
+ Para más detalles, mira la documentación de la API.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ <para>
+ Dada una arquitectura "sencilla", la aplicación pasa por alto las APIs
+ de <literal>Transaction</literal>/<literal>TransactionFactory</literal> y/o
+ <literal>ConnectionProvider</literal>, para hablar directamente a JTA o JDBC.
+ </para>
+ </sect1>
+
+ <sect1 id="architecture-states" revision="1">
+ <title>Estados de instancia</title>
+ <para>
+ Una instancia de una clase persistente puede estar en uno de tres estados
+ diferentes, definidos respecto de su <emphasis>contexto de persistencia</emphasis>.
+ El objeto <literal>Session</literal> de Hibernate es el contexto de persistencia:
+ </para>
+
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term>transitorio</term>
+ <listitem>
+ <para>
+ La instancia no está y nunca estuvo asociada con
+ un contexto de persistencia. No tiene identidad persistente
+ (valor de clave primaria).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>persistente</term>
+ <listitem>
+ <para>
+ La instancia está actualmente asociada con un
+ contexto de persistencia. Tiene una identidad persistente
+ (valor de clave primaria) y, quizás, una fila
+ correspondiente en la base de datos. Para un contexto de
+ persistencia en particular, Hibernate <emphasis>garantiza</emphasis>
+ que la identidad persistente es equivalente a la identidad
+ Java (localización en memoria del objeto).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>separado</term>
+ <listitem>
+ <para>
+ La instancia estuvo una vez asociada con un contexto
+ de persistencia, pero ese contexto fue cerrado, o la
+ instancia fue serializada a otro proceso. Tiene una
+ identidad persistente y, quizás, una fila correspondiente
+ en la base de datos. Para las instancias separadas,
+ Hibernate no establece ninguna garantía sobre
+ la relación entre identidad persistente e identidad Java.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </sect1>
+
+ <sect1 id="architecture-jmx" revision="1">
+ <title>Integración JMX</title>
+
+ <para>
+ JMX es el estándar J2EE para la gestión de componentes Java. Hibernate
+ puede ser gestionado por medio de un servicio estándar JMX.
+ Proveemos una implementación de MBean en la distribución,
+ <literal>org.hibernate.jmx.HibernateService</literal>.
+ </para>
+
+ <para>
+ Para ejemplo de cómo desplegar Hibernate como un servicio JMX en un Servidor
+ de Aplicaciones JBoss, por favor, mira la Guía del Usuario de JBoss.
+ En JBoss AS, tienes además estos beneficios si despliegas usando JMX:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Gestión de Sesión:</emphasis> El ciclo de vida de la <literal>Session</literal>
+ de Hibernate puede estar automáticamente ligado al ámbito de una transacción
+ JTA. Esto significa que ya no tienes que abrir ni cerrar la <literal>Session</literal> manualmente,
+ esto pasa a ser trabajo de un interceptor EJB de JBoss. Además tampoco tienes
+ que preocuparte más de la demarcación de la transacción (a menos que
+ que quieras escribir una capa de persitencia portable, por supuesto, usa la API de
+ <literal>Transaction</literal> de Hibernate para esto). Para acceder a una
+ <literal>Session</literal> llama al <literal>HibernateContext</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Despliegue de HAR:</emphasis> Usualmente despliegas el servicio JMX de Hibernate
+ usando un descriptor de despliegue de servicio de JBoss (en un fichero EAR y/o SAR), que soporta
+ todas las opciones de configuración usuales de una <literal>SessionFactory</literal> de
+ Hibernate. Sin embargo, todavía tienes que nombrar todos tus ficheros de mapeo en el
+ descriptor de despliegue. Si decides usar el depliegue de HAR opcional, JBoss detectará
+ automáticamente todos los ficheros de mapeo en tu fichero HAR.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Para más información sobre estas opciones, consulta la
+ Guía de Usuario del JBoss AS.
+ </para>
+
+ <para>
+ Otra funcionalidad disponible como un servicio JMX son las estadísticas en
+ tiempo de ejecución de Hibernate. Mira <xref linkend="configuration-optional-statistics"/>.
+ </para>
+ </sect1>
+
+ <sect1 id="architecture-jca" revision="1">
+ <title>Soporte JCA:</title>
+ <para>
+ Hiberate puede además ser configurado como un conector JCA. Por favor mira el
+ sitio web para más detalles. Por favor ten en cuenta que el soporte de JCA
+ de Hibernate está aún considerado experimental.
+ </para>
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/association_mapping.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/association_mapping.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/association_mapping.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,527 @@
+<chapter id="associations">
+
+ <title>Mapeos de Asociación</title>
+
+ <sect1 id="assoc-intro" revision="1">
+ <title>Introducción</title>
+
+ <para>
+ Los mapeos de asociación son frecuentemente las cosas mas difíciles
+ de hacer correctamente. En esta sección iremos a través de los casos
+ canónicos uno a uno, comenzando con los mapeos unidireccionales, y considerando
+ luego los casos bidireccionales. Usaremos <literal>Person</literal> y <literal>Address</literal>
+ en todos los ejemplos.
+ </para>
+
+ <para>
+ Clasificaremos las asociaciones por cuanto mapeen o no a una tabla
+ de unión interviniente, y por su multiplicidad.
+ </para>
+
+ <para>
+ Las claves foráneas que aceptan valores nulos (en adelante, nullables)
+ no son consideradas una buena práctica en el modelado tradicional de datos,
+ así que todos nuestros ejemplos usan claves foráneas no nullables.
+ Esto no es un requerimiento de Hibernate, y todos los mapeos funcionarán
+ si quitas las restricciones de nulabilidad.
+ </para>
+
+ </sect1>
+
+ <sect1 id="assoc-unidirectional" revision="1">
+ <title>Asociaciones Unidireccionales</title>
+
+ <sect2 id="assoc-unidirectional-m21">
+ <title>muchos a uno</title>
+
+ <para>
+ Una <emphasis>asociación unidireccional muchos-a-uno</emphasis> es el tipo
+ más común de asociaciones unidireccionales.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key, addressId bigint not null )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-121">
+ <title>uno a uno</title>
+
+ <para>
+ Una <emphasis>asociación unidireccional uno-a-uno en una clave primaria</emphasis>
+ es casi idéntica. La única diferencia es la restricción de unicidad
+ de la columna.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <many-to-one name="address"
+ column="addressId"
+ unique="true"
+ not-null="true"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key, addressId bigint not null unique )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ <para>
+ Usualmente, una <emphasis>asociación unidireccional uno-a-uno en una
+ clave primaria</emphasis> usa un generador de id especial. (Observa que hemos
+ invertido el sentido de la asociación en este ejemplo).
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+</class>
+
+<class name="Address">
+ <id name="id" column="personId">
+ <generator class="foreign">
+ <param name="property">person</param>
+ </generator>
+ </id>
+ <one-to-one name="person" constrained="true"/>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table Address ( personId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-12m">
+ <title>uno a muchos</title>
+
+ <para>
+ Una <emphasis>asociación unidireccional uno-a-muchos en una clave foránea</emphasis>
+ es un caso muy inusual, y realmente no está recomendada.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses">
+ <key column="personId"
+ not-null="true"/>
+ <one-to-many class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table Address ( addressId bigint not null primary key, personId bigint not null )
+ ]]></programlisting>
+
+ <para>
+ Creemos que es mejor usar una tabla de unión para este tipo de asociación.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="assoc-unidirectional-join" revision="1">
+ <title>Asociaciones unidireccionales con tablas de unión</title>
+
+ <sect2 id="assoc-unidirectional-join-12m">
+ <title>uno a muchos</title>
+
+ <para>
+ Una <emphasis>asociación unidireccional uno-a-muchos en una tabla de unión</emphasis>
+ es más preferible. Observa que especificando <literal>unique="true"</literal>, hemos
+ cambiado la multiplicidad de muchos-a-muchos a uno-a-muchos.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses" table="PersonAddress">
+ <key column="personId"/>
+ <many-to-many column="addressId"
+ unique="true"
+ class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId not null, addressId bigint not null primary key )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-join-m21">
+ <title>muchos a uno</title>
+
+ <para>
+ Una <emphasis>asociación unidireccional muchos-a-uno en una tabla de unión</emphasis>
+ es bastante común cuando la asociación es opcional.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ optional="true">
+ <key column="personId" unique="true"/>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"/>
+ </join>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null primary key, addressId bigint not null )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-join-121">
+ <title>uno a uno</title>
+
+ <para>
+ Una <emphasis>asociación unidireccional uno-a-uno en una tabla de unión</emphasis>
+ es inusual en extremo, pero posible.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ optional="true">
+ <key column="personId"
+ unique="true"/>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"
+ unique="true"/>
+ </join>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-join-m2m">
+ <title>muchos a muchos</title>
+
+ <para>
+ Finalmente, tenemos una <emphasis>asociación unidireccional muchos-a-muchos</emphasis>
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses" table="PersonAddress">
+ <key column="personId"/>
+ <many-to-many column="addressId"
+ class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personId, addressId) )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="assoc-bidirectional" revision="1">
+ <title>Asociaciones Bidireccionales</title>
+
+ <sect2 id="assoc-bidirectional-m21">
+ <title>uno a muchos / muchos a uno</title>
+
+ <para>
+ Una <emphasis>asociación bidireccional muchos-a-uno</emphasis> es
+ el tipo más común de asociación. (Esta es la relación
+ estándar padre/hijo.)
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <set name="people" inverse="true">
+ <key column="addressId"/>
+ <one-to-many class="Person"/>
+ </set>
+</class>]]></programlisting>
+
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key, addressId bigint not null )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-bidirectional-121">
+ <title>uno a uno</title>
+
+ <para>
+ Una <emphasis>asociación bidireccional uno-a-uno en una clave foránea</emphasis>
+ es bastante común.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <many-to-one name="address"
+ column="addressId"
+ unique="true"
+ not-null="true"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <one-to-one name="person"
+ property-ref="address"/>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key, addressId bigint not null unique )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ <para>
+ Una <emphasis>asociación bidireccional uno-a-uno en una clave primaria</emphasis>
+ usa el generador de id especial.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <one-to-one name="address"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="personId">
+ <generator class="foreign">
+ <param name="property">person</param>
+ </generator>
+ </id>
+ <one-to-one name="person"
+ constrained="true"/>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table Address ( personId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="assoc-bidirectional-join" revision="1">
+ <title>Asociaciones bidireccionales con tablas de unión</title>
+
+ <sect2 id="assoc-bidirectional-join-12m">
+ <title>uno a muchos / muchos a uno</title>
+
+ <para>
+ Una <emphasis>asociación bidireccional uno-a-muchos en una tabla de unión</emphasis>.
+ Observa que el <literal>inverse="true"</literal> puede ir a cualquier lado de la asociación,
+ en la colección, o en la unión.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses"
+ table="PersonAddress">
+ <key column="personId"/>
+ <many-to-many column="addressId"
+ unique="true"
+ class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ inverse="true"
+ optional="true">
+ <key column="addressId"/>
+ <many-to-one name="person"
+ column="personId"
+ not-null="true"/>
+ </join>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null, addressId bigint not null primary key )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-bidirectional-join-121">
+ <title>uno a uno</title>
+
+ <para>
+ Una <emphasis>asociación bidireccional uno-a-uno en una tabla de unión</emphasis>
+ es inusual en extremo, pero posible.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ optional="true">
+ <key column="personId"
+ unique="true"/>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"
+ unique="true"/>
+ </join>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ optional="true"
+ inverse="true">
+ <key column="addressId"
+ unique="true"/>
+ <many-to-one name="address"
+ column="personId"
+ not-null="true"
+ unique="true"/>
+ </join>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-bidirectional-join-m2m">
+ <title>muchos a muchos</title>
+
+ <para>
+ Finalmente, tenemos una <emphasis>asociación bidireccional muchos-a-muchos</emphasis>.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses">
+ <key column="personId"/>
+ <many-to-many column="addressId"
+ class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <set name="people" inverse="true">
+ <key column="addressId"/>
+ <many-to-many column="personId"
+ class="Person"/>
+ </set>
+</class>]]></programlisting>
+
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personId, addressId) )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ </sect1>
+
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/basic_mapping.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/basic_mapping.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/basic_mapping.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,3222 @@
+<chapter id="mapping">
+ <title>Mapeo O/R Básico</title>
+
+ <sect1 id="mapping-declaration" revision="1">
+ <title>Declaración de mapeo</title>
+
+ <para>
+ Los mapeos objeto/relacional se definen usualmente en un documento XML.
+ El documento de mapeo está diseñado para ser leíble y
+ editable a mano. El lenguaje de mapeo es Java-céntrico, o sea que los
+ mapeos se construyen alrededor de declaraciones de clases persistentes,
+ no declaraciones de tablas.
+ </para>
+
+ <para>
+ Observa que, incluso aunque muchos usuarios de Hibernate eligen escribir el
+ XML a mano, existe una cantidad de herramientas para generar el documento de
+ mapeo, incluyendo XDoclet, Middlegen y AndroMDA.
+ </para>
+
+ <para>
+ Comencemos por un mapeo de ejemplo:
+ </para>
+
+ <programlisting id="mapping-declaration-ex1" revision="1"><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="eg">
+
+ <class name="Cat"
+ table="cats"
+ discriminator-value="C">
+
+ <id name="id">
+ <generator class="native"/>
+ </id>
+
+ <discriminator column="subclass"
+ type="character"/>
+
+ <property name="weight"/>
+
+ <property name="birthdate"
+ type="date"
+ not-null="true"
+ update="false"/>
+
+ <property name="color"
+ type="eg.types.ColorUserType"
+ not-null="true"
+ update="false"/>
+
+ <property name="sex"
+ not-null="true"
+ update="false"/>
+
+ <property name="litterId"
+ column="litterId"
+ update="false"/>
+
+ <many-to-one name="mother"
+ column="mother_id"
+ update="false"/>
+
+ <set name="kittens"
+ inverse="true"
+ order-by="litter_id">
+ <key column="mother_id"/>
+ <one-to-many class="Cat"/>
+ </set>
+
+ <subclass name="DomesticCat"
+ discriminator-value="D">
+
+ <property name="name"
+ type="string"/>
+
+ </subclass>
+
+ </class>
+
+ <class name="Dog">
+ <!-- mapping for Dog could go here -->
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Discutiremos ahora el contenido del documento de mapeo. Describiremos sólo los
+ elementos y atributos que son usados por Hibernate en tiempo de ejecución. El
+ documento de mapeo contiene además algunos atributos y elementos extra opcionales
+ que afectan los esquemas de base de datos exportados por la herramienta de exportación
+ de esquemas. (Por ejemplo, el atributo <literal>not-null</literal>.)
+ </para>
+
+
+
+ <sect2 id="mapping-declaration-doctype" revision="2">
+ <title>Doctype</title>
+
+ <para>
+ Todos los mapeos XML deben declarar el doctype mostrado. El DTD actual puede
+ ser encontrado en el URL mencionado arriba, en el directorio
+ <literal>hibernate-x.x.x/src/org/hibernate</literal>, o en <literal>hibernate3.jar</literal>.
+ Hibernate siempre buscará el DTD primero en el classpath. Si experimentas
+ búsquedas del DTD usando una conexión de Internet, chequea tu declaración
+ de DTD contra la contenida en el classpath.
+ </para>
+ </sect2>
+
+ <sect2 id="mapping-declaration-mapping" revision="3">
+ <title>hibernate-mapping</title>
+
+ <para>
+ Este elemento tiene muchos atributos opcionales. Los atributos <literal>schema</literal>
+ y <literal>catalog</literal> especifican que las tablas a las que se refiere en el mapeo
+ pertenecen al esquema y/o catálogo mencionado(s). De especificarse, los nombres de
+ tablas serán cualificados por el nombre de esquema y catálogo dados.
+ De omitirse, los nombres de tablas no serán cualificados. El atributo
+ <literal>default-cascade</literal> especifica qué estilo de cascada debe asumirse
+ para las propiedades y colecciones que no especifican un atributo <literal>cascade</literal>.
+ El atributo <literal>auto-import</literal> nos permite usar nombres de clase sin cualificar
+ en el lenguaje de consulta, por defecto.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="hm1" coords="2 55"/>
+ <area id="hm2" coords="3 55"/>
+ <area id="hm3" coords="4 55"/>
+ <area id="hm4" coords="5 55"/>
+ <area id="hm5" coords="6 55"/>
+ <area id="hm6" coords="7 55"/>
+ <area id="hm7" coords="8 55"/>
+ </areaspec>
+ <programlisting><![CDATA[<hibernate-mapping
+ schema="schemaName"
+ catalog="catalogName"
+ default-cascade="cascade_style"
+ default-access="field|property|ClassName"
+ default-lazy="true|false"
+ auto-import="true|false"
+ package="package.name"
+ />]]></programlisting>
+ <calloutlist>
+ <callout arearefs="hm1">
+ <para>
+ <literal>schema</literal> (opcional): El nombre de un esquema de la base de datos.
+ </para>
+ </callout>
+ <callout arearefs="hm2">
+ <para>
+ <literal>catalog</literal> (opcional): El nombre de un catálogo de la base de datos.
+ </para>
+ </callout>
+ <callout arearefs="hm3">
+ <para>
+ <literal>default-cascade</literal> (opcional - por defecto a <literal>none</literal>):
+ Un estilo de cascada por defecto.
+ </para>
+ </callout>
+ <callout arearefs="hm4">
+ <para>
+ <literal>default-access</literal> (opcional - por defecto a <literal>property</literal>):
+ La estrategia que Hibernate debe usar para acceder a todas las propiedades.
+ Puede ser una implementación personalizada de <literal>PropertyAccessor</literal>.
+ </para>
+ </callout>
+ <callout arearefs="hm5">
+ <para>
+ <literal>default-lazy</literal> (opcional - por defecto a <literal>true</literal>):
+ El valor por defecto para los atributos <literal>lazy</literal> de mapeos de clase
+ y colleción no especificados.
+ </para>
+ </callout>
+ <callout arearefs="hm6">
+ <para>
+ <literal>auto-import</literal> (opcional - por defecto a <literal>true</literal>):
+ Especifica si podemos usar nombres de clases no cualificados (de clases en este mapeo)
+ en el lenguaje de consulta.
+ </para>
+ </callout>
+ <callout arearefs="hm7">
+ <para>
+ <literal>package</literal> (opcional): Especifica un prefijo de paquete a asumir
+ para los nombres no cualificados de clase en el documento de mapeo.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Si tienes dos clases persistentes con el mismo nombre (sin cualificar), debes establecer
+ <literal>auto-import="false"</literal>. Hibernate lanzará una excepción si
+ intentas asignar dos clases al mismo nombre "importado".
+ </para>
+
+ <para>
+ Observa que el elemento <literal>hibernate-mapping</literal> te permite anidar
+ muchos mapeos <literal><class></literal> persistentes, como se muestra arriba.
+ Sin embargo, es una buena práctica (y se espera de algunas herramientas) mapear
+ sólo a una sola clase persistente (o a una sola jerarquía de clases) en
+ un fichero de mapeo y nombrarlo después de la superclase persistente;
+ por ejemplo, <literal>Cat.hbm.xml</literal>, <literal>Dog.hbm.xml</literal>,
+ o, si se usa herencia, <literal>Animal.hbm.xml</literal>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-class" revision="3">
+ <title>class</title>
+
+ <para>
+ Puedes declarar una clase persistente usando el elemento <literal>class</literal>:
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="class1" coords="2 55"/>
+ <area id="class2" coords="3 55" />
+ <area id="class3" coords="4 55"/>
+ <area id="class4" coords="5 55" />
+ <area id="class5" coords="6 55"/>
+ <area id="class6" coords="7 55" />
+ <area id="class7" coords="8 55"/>
+ <area id="class8" coords="9 55" />
+ <area id="class9" coords="10 55" />
+ <area id="class10" coords="11 55"/>
+ <area id="class11" coords="12 55"/>
+ <area id="class12" coords="13 55"/>
+ <area id="class13" coords="14 55"/>
+ <area id="class14" coords="15 55"/>
+ <area id="class15" coords="16 55"/>
+ <area id="class16" coords="17 55"/>
+ <area id="class17" coords="18 55"/>
+ <area id="class18" coords="19 55"/>
+ <area id="class19" coords="20 55"/>
+ <area id="class20" coords="21 55"/>
+ <area id="class21" coords="22 55"/>
+ </areaspec>
+ <programlisting><![CDATA[<class
+ name="ClassName"
+ table="tableName"
+ discriminator-value="discriminator_value"
+ mutable="true|false"
+ schema="owner"
+ catalog="catalog"
+ proxy="ProxyInterface"
+ dynamic-update="true|false"
+ dynamic-insert="true|false"
+ select-before-update="true|false"
+ polymorphism="implicit|explicit"
+ where="arbitrary sql where condition"
+ persister="PersisterClass"
+ batch-size="N"
+ optimistic-lock="none|version|dirty|all"
+ lazy="true|false"
+ entity-name="EntityName"
+ check="arbitrary sql check condition"
+ rowid="rowid"
+ subselect="SQL expression"
+ abstract="true|false"
+ node="element-name"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="class1">
+ <para>
+ <literal>name</literal> (opcional): El nombre completamente cualificado de la clase
+ Java persistente (o interface). Si este atributo es omitido, se asume que el mapeo
+ es para una entidad non-POJO.
+ </para>
+ </callout>
+ <callout arearefs="class2">
+ <para>
+ <literal>table</literal> (opcional - por defecto al nombre no cualificado de la clase):
+ El nombre de su tabla en base de datos.
+ </para>
+ </callout>
+ <callout arearefs="class3">
+ <para>
+ <literal>discriminator-value</literal> (opcional - por defecto al nombre de la clase):
+ Un valor que distingue subclases individuales, usado para el comportamiento
+ polimórfico. Los valores aceptables incluyen <literal>null</literal>
+ y <literal>not null</literal>.
+ </para>
+ </callout>
+ <callout arearefs="class4">
+ <para>
+ <literal>mutable</literal> (opcional, por defecto a <literal>true</literal>):
+ Especifica que las instancias de la clase (no) son mutables.
+ </para>
+ </callout>
+ <callout arearefs="class5">
+ <para>
+ <literal>schema</literal> (opcional): Sobreescribe el nombre de esquema especificado
+ por el elemento raíz <literal><hibernate-mapping></literal>.
+ </para>
+ </callout>
+ <callout arearefs="class6">
+ <para>
+ <literal>catalog</literal> (opcional): Sobreescribe el nombre de catálogo
+ especificado por el elemento raíz <literal><hibernate-mapping></literal>.
+ </para>
+ </callout>
+ <callout arearefs="class7">
+ <para>
+ <literal>proxy</literal> (opcional): Especifica una interface a usar para proxies
+ de inicialización perezosa. Puedes especificar el nombre mismo de la clase.
+ </para>
+ </callout>
+ <callout arearefs="class8">
+ <para>
+ <literal>dynamic-update</literal> (opcional, por defecto a <literal>false</literal>):
+ Especifica que el SQL <literal>UPDATE</literal> debe ser generado en tiempo de
+ ejecución y contener solamente aquellas columnas cuyo valor haya cambiado.
+ </para>
+ </callout>
+ <callout arearefs="class9">
+ <para>
+ <literal>dynamic-insert</literal> (opcional, por defecto a <literal>false</literal>):
+ Especifica que el SQL <literal>INSERT</literal> debe ser generado en tiempo de
+ ejecución y contener solamente aquellas columnas cuyo valores no son nulos.
+ </para>
+ </callout>
+ <callout arearefs="class10">
+ <para>
+ <literal>select-before-update</literal> (opcional, por defecto a <literal>false</literal>):
+ Especifica que Hibernate <emphasis>nunca</emphasis> debe realizar un SQL <literal>UPDATE</literal>
+ a menos que se tenga certeza que un objeto haya sido modificado realmente.
+ En ciertos casos, (realmente, sólo cuando un objeto transitorio ha sido asociado
+ con una sesión nueva usando <literal>update()</literal>), esto significa que Hibernate
+ realizará una SQL <literal>SELECT</literal> extra para determinar si un
+ <literal>UPDATE</literal> es realmente requerido.
+ </para>
+ </callout>
+ <callout arearefs="class11">
+ <para>
+ <literal>polymorphism</literal> (opcional, por defecto a <literal>implicit</literal>):
+ Determina si se usa polimorfismo de consulta implícito o explícito.
+ </para>
+ </callout>
+ <callout arearefs="class12">
+ <para>
+ <literal>where</literal> (opcional) especifica una condición SQL <literal>WHERE</literal>
+ arbitraria paraa ser usada al recuperar objetos de esta clase.
+ </para>
+ </callout>
+ <callout arearefs="class13">
+ <para>
+ <literal>persister</literal> (opcional): Especifica un <literal>ClassPersister</literal>
+ personalizado.
+ </para>
+ </callout>
+ <callout arearefs="class14">
+ <para>
+ <literal>batch-size</literal> (opcional, por defecto a <literal>1</literal>)
+ especifica un "tamaño de lote" para traer instancias de esta clase por
+ identificador.
+ </para>
+ </callout>
+ <callout arearefs="class15">
+ <para>
+ <literal>optimistic-lock</literal> (opcional, por defecto a <literal>version</literal>):
+ Determina la estrategia optimista de bloqueo.
+ </para>
+ </callout>
+ <callout arearefs="class16">
+ <para>
+ <literal>lazy</literal> (opcional):
+ La recuperación perezosa puede ser deshabilitada por completo estableciendo
+ <literal>lazy="false"</literal>.
+ </para>
+ </callout>
+ <callout arearefs="class17">
+ <para>
+ <literal>entity-name</literal> (opcional): Hibernate3 permite que una clase sea
+ mapeada varias veces (potencialmente a tablas diferentes), y permite que los mapeos
+ de entidad sean representados por Maps o XML al nivel de Java. En estos casos,
+ debes proveer un nombre explícito arbitrario para la entidad.
+ Para más información, mira
+ <xref linkend="persistent-classes-dynamicmodels"/> y <xref linkend="xml"/>.
+ </para>
+ </callout>
+ <callout arearefs="class18">
+ <para>
+ <literal>check</literal> (opcional): Una expresión SQL usada para generar
+ una restricción <emphasis>check</emphasis> multi-fila para la generación
+ automática de esquema.
+ </para>
+ </callout>
+ <callout arearefs="class19">
+ <para>
+ <literal>rowid</literal> (opcional): Hibernate puede usar los llamados ROWIDs en las
+ bases de datos que los soporten. Por ejemplo, en Oracle, Hibernate puede usar la columna
+ extra <literal>rowid</literal> para actualizaciones rápidas si estableces esta
+ opción a <literal>rowid</literal>. Un ROWID es un detalle de implementación
+ y representa la posición física de la tupla almacenada.
+ </para>
+ </callout>
+ <callout arearefs="class20">
+ <para>
+ <literal>subselect</literal> (opcional): Mapea una entidad inmutable y de sólo
+ lectura a una subselect de base de datos. Es útil si quieres tener una vista
+ en vez de una tabla base, pero no tienes vistas. Mira debajo para más información.
+ </para>
+ </callout>
+ <callout arearefs="class21">
+ <para>
+ <literal>abstract</literal> (opcional): Usado para marcar superclases abstractas en
+ jerarquías <literal><union-subclass></literal>.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Es perfectamente aceptable que la clase persistente mencionada sea una interface.
+ Entonces declararías clases que implementan esa interface usando el elemento
+ <literal><subclass></literal>. Puedes persistir cualquier clase interna
+ <emphasis>estática</emphasis>. Debes especificar el nombre de la clase usando la forma
+ estándar. Por ejemplo, <literal>eg.Foo$Bar</literal>.
+ </para>
+
+ <para>
+ Las clases inmutables, <literal>mutable="false"</literal>, no pueden ser actualizadas o
+ borradas por la aplicación. Esto permite a Hibernate hacer ciertas optimizaciones
+ menores de rendimiento.
+ </para>
+
+ <para>
+ El atributo opcional <literal>proxy</literal> habilita la inicialización postergada
+ de instancias persistentes de la clase. Hibernate inicialmente retornará proxies
+ CGLIB que implementan la interface mencionada. El objeto persistente real será
+ cargado cuando se invoque un método del proxy. Mira "Proxies para Inicialización
+ Postergada" debajo.
+ </para>
+
+ <para>
+ Por polimorfismo <emphasis>implícito</emphasis> se entiende que las instancias de la clase
+ serán devueltas por una consulta que mencione cualquier superclase, o interface implementada,
+ o la clase misma; y que las instancias de cualquier subclase de la clase serán devueltas
+ por una clase que mencione a la clase en sí.
+ Por polimorfismo <emphasis>explícito</emphasis> se entiende que instancias de la clase
+ serán devueltas sólo por consultas que mencionen explícitamente la clase;
+ y que las consultas que mencionen la clase devolverán sólo instancias de subclases
+ mapeadas dentro de esta declaración <literal><class></literal> como una
+ <literal><subclass></literal> o <literal><joined-subclass></literal>.
+ Para la mayoría de los propósitos el defecto,
+ <literal>polymorphism="implicit"</literal>, resulta apropiado.
+ El polimorfismo explícito es útil cuando dos clases diferentes están
+ mapeadas a la misma tabla (esto permite tener una clase "liviana" que contenga un subconjunto
+ de columnas de la tabla).
+ </para>
+
+ <para>
+ El atributo <literal>persister</literal> te permite personalizar la estrategia de persistencia
+ para la clase. Puedes, por ejemplo, especificar tu propia subclase de
+ <literal>org.hibernate.persister.EntityPersister</literal> o incluso puedes proveer una implementación
+ completamente nueva de la interface <literal>org.hibernate.persister.ClassPersister</literal> que implemente
+ la persistencia por medio, por ejemplo, de llamadas a procedimientos almacenados, serialización a
+ ficheros planos o LDAP. Para un ejemplo simple (de persistencia a una <literal>Hashtable</literal>) mira
+ <literal>org.hibernate.test.CustomPersister</literal>.
+ </para>
+
+ <para>
+ Observa que los valores de <literal>dynamic-update</literal> y <literal>dynamic-insert</literal>
+ no son heredados por las subclases y por lo tanto deben especificarse en los elementos
+ <literal><subclass></literal> o <literal><joined-subclass></literal>.
+ Estos ajustes pueden incrementar el rendimiento en algunos casos, pero podrían mermarlo en otros.
+ Ten juicio en su uso.
+ </para>
+
+ <para>
+ Generalmente el uso de <literal>select-before-update</literal> disminuirá el rendimiento.
+ Es muy útil prevenir que se llame innecesariamente a un disparador de actualización de
+ base de datos al volver a unir un grafo de instancias separadas a una <literal>Session</literal>.
+ </para>
+
+ <para>
+ Si habilitas <literal>dynamic-update</literal>, tendrás opción de estrategias
+ de bloqueo optimistas:
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>version</literal> chequea las columnas de versión/timestamp
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>all</literal> chequea todas las columnas
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>dirty</literal> chequea las columnas modificadas, permitiendo algunas
+ actualizaciones concurrentes
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>none</literal> no usa bloqueo optimista
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+ Recomendamos <emphasis>muy</emphasis> fuertemente que uses columnas de
+ versión/timestamp para bloqueo optimista con Hibernate. Esta es la estrategia
+ óptima con respecto al rendimiento y es la única estrategia que maneja
+ correctamente las modificaciones hechas a las instancias separadas.
+ (por ejemplo, cuando se usa <literal>Session.merge()</literal>).
+ </para>
+
+ <para>
+ Para un mapeo de Hibernate, no hay diferencia entre una vista y una tabla base.
+ Como se supone esto es transparente a nivel de base de datos (observa que algunos
+ DBMS no soportan correctamente las vistas, especialmente con las actualizaciones).
+ A veces quieres usar una vista, pero no puedes crear una en la base de datos
+ (por ejemplo, con un esquema heredado). En este caso, puedes mapear una entidad inmutable
+ de sólo lectura a una expresión de subconsulta SQL dada.
+ </para>
+
+ <programlisting><![CDATA[<class name="Summary">
+ <subselect>
+ select item.name, max(bid.amount), count(*)
+ from item
+ join bid on bid.item_id = item.id
+ group by item.name
+ </subselect>
+ <synchronize table="item"/>
+ <synchronize table="bid"/>
+ <id name="name"/>
+ ...
+</class>]]></programlisting>
+
+ <para>
+ Declara las tablas con las que sincronizar esta entidad, asegurando que el auto-flush
+ ocurre correctamente, y que las consultas contra la entidad derivada no devuelven datos
+ desactualizados. El <literal><subselect></literal> está disponible tanto
+ como un atributo o como un elemento anidado de mapeo.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-id" revision="3">
+ <title>id</title>
+
+ <para>
+ Las clases mapeadas <emphasis>deben</emphasis> declarar la columna de clave primaria de la tabla
+ de la base de datos. En la mayoría de los casos tendrá también una propiedad
+ estilo Javabeans que tenga el identificador único de una instancia. El elemento
+ <literal><id></literal> define el mapeo de esa propiedad a la columna de clave primaria.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="id1" coords="2 70"/>
+ <area id="id2" coords="3 70" />
+ <area id="id3" coords="4 70"/>
+ <area id="id4" coords="5 70" />
+ <area id="id5" coords="6 70" />
+ </areaspec>
+ <programlisting><![CDATA[<id
+ name="propertyName"
+ type="typename"
+ column="column_name"
+ unsaved-value="null|any|none|undefined|id_value"
+ access="field|property|ClassName"
+ node="element-name|@attribute-name|element/@attribute|.">
+
+ <generator class="generatorClass"/>
+</id>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="id1">
+ <para>
+ <literal>name</literal> (opcional): El nombre de la propiedad del indentificador.
+ </para>
+ </callout>
+ <callout arearefs="id2">
+ <para>
+ <literal>type</literal> (opcional): Un nombre que indica el tipo Hibernate.
+ </para>
+ </callout>
+ <callout arearefs="id3">
+ <para>
+ <literal>column</literal> (opcional - por defecto al nombre de la propiedad):
+ El nombre de la columna de clave primaria.
+ </para>
+ </callout>
+ <callout arearefs="id4">
+ <para>
+ <literal>unsaved-value</literal> (opcional - por defecto al valor "sensible"):
+ Una valor de la propiedad identificadora que indica que una instancia está
+ recién instanciada (sin salvar), distinguiéndola de instancias separadas
+ que fueran salvadas o cargadas en una sesión previa.
+ </para>
+ </callout>
+ <callout arearefs="id5">
+ <para>
+ <literal>access</literal> (opcional - por defecto a <literal>property</literal>):
+ La estrategia que Hibernate debe usar para acceder al valor de la propiedad.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Si se omite el atributo <literal>name</literal>, se asume que la clase no tiene propiedad
+ identificadora.
+ </para>
+
+ <para>
+ El atributo <literal>unsaved-value</literal> es importante! Si la propiedad identificadora de tu
+ clase no tiene por defecto el valor por defecto normal de Java (null o cero), entonces debes especificar
+ el valor por defecto real.
+ </para>
+
+ <para>
+ Hay una declaración <literal><composite-id></literal> alternativa para permitir acceso
+ a datos heredados con claves compuestas. Desalentamos fuertemente su uso para cualquier otra cosa.
+ </para>
+
+ <sect3 id="mapping-declaration-id-generator" revision="2">
+ <title>Generator</title>
+
+ <para>
+ El elemento hijo opcional <literal><generator></literal> nombra una clase Java
+ usada en generar identificadores únicos para instancias de la clase persistente.
+ De requerirse algún parámetro para configurar o inicializar la instancia del generador,
+ se pasa usando el elemento <literal><param></literal>.
+ </para>
+
+ <programlisting><![CDATA[<id name="id" type="long" column="cat_id">
+ <generator class="org.hibernate.id.TableHiLoGenerator">
+ <param name="table">uid_table</param>
+ <param name="column">next_hi_value_column</param>
+ </generator>
+</id>]]></programlisting>
+
+ <para>
+ Todos los generadores implementan la interface <literal>org.hibernate.id.IdentifierGenerator</literal>.
+ Esta es una interface muy simple; algunas aplicaciones pueden escoger proveer sus propias
+ implementaciones especializadas. Sin embargo, Hibernate provee un rango de implementaciones
+ prefabricadas. Hay nombres alias de atajo para los generadores prefabricados:
+ <variablelist>
+ <varlistentry>
+ <term><literal>increment</literal></term>
+ <listitem>
+ <para>
+ genera indentificadores de tipo <literal>long</literal>, <literal>short</literal> o
+ <literal>int</literal> que sólo son únicos cuando ningún otro proceso
+ está insertando datos en la misma tabla. <emphasis>No usar en un cluster.</emphasis>
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>identity</literal></term>
+ <listitem>
+ <para>
+ soporta columnas de identidad en DB2, MySQL, MS SQL Server, Sybase y
+ HypersonicSQL. El identificador devuelto es de tipo <literal>long</literal>,
+ <literal>short</literal> o <literal>int</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>sequence</literal></term>
+ <listitem>
+ <para>
+ usa una secuencia en DB2, PostgreSQL, Oracle, SAP DB, McKoi o un generador
+ en Interbase. El identificador devuelto es de tipo <literal>long</literal>,
+ <literal>short</literal> o <literal>int</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>hilo</literal></term>
+ <listitem>
+ <para id="mapping-declaration-id-hilodescription" revision="1">
+ usa un algoritmo alto/bajo para generar eficientemente identificadores de tipo
+ <literal>long</literal>, <literal>short</literal> o <literal>int</literal>,
+ dada una tabla y columna como fuente de valores altos (por defecto
+ <literal>hibernate_unique_key</literal> y <literal>next_hi</literal> respectivamente).
+ El algoritmo alto/bajo genera identificadores que son únicos sólo para una
+ base de datos particular.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>seqhilo</literal></term>
+ <listitem>
+ <para>
+ usa un algoritmo alto/bajo para generar eficientemente identificadores de tipo
+ <literal>long</literal>, <literal>short</literal> o <literal>int</literal>,
+ dada una secuencia de base de datos.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>uuid</literal></term>
+ <listitem>
+ <para>
+ usa un algoritmo UUID de 128 bits para generar identificadore de tipo
+ cadena, únicos en una ref (se usa la direccón IP). El UUID
+ se codifica como una cadena hexadecimal de 32 dígitos de largo.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>guid</literal></term>
+ <listitem>
+ <para>
+ usa una cadena GUID generada por base de datos en MS SQL Server y MySQL.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>native</literal></term>
+ <listitem>
+ <para>
+ selecciona <literal>identity</literal>, <literal>sequence</literal> o
+ <literal>hilo</literal> dependiendo de las capacidades de la base de datos
+ subyacente.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>assigned</literal></term>
+ <listitem>
+ <para>
+ deja a la aplicación asignar un identificador al objeto antes
+ de que se llame a <literal>save()</literal>. Esta es la estrategia
+ por defecto si no se especifica un elemento <literal><generator></literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>select</literal></term>
+ <listitem>
+ <para>
+ recupera una clave primaria asignada por un disparador de base de datos
+ seleccionando la fila por alguna clave única y recuperando el valor de
+ la clave primaria.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>foreign</literal></term>
+ <listitem>
+ <para>
+ usa el identificador de otro objeto asociado. Generalmente usado en conjuncón
+ a una asociacón de clave primaria <literal><uno-a-uno></literal>
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ </para>
+ </sect3>
+
+ <sect3 id="mapping-declaration-id-hilo" revision="1">
+ <title>Algoritmo alto/bajo</title>
+ <para>
+ Los generadores <literal>hilo</literal> y <literal>seqhilo</literal> proveen dos implementaciones
+ alternativas del algoritmo alto/bajo, un enfoque favorito en generación de identificadores.
+ La primera implementación requiere de una tabla "especial" de base de datos para tener el siguiente
+ valor "alto" disponible.
+ La segunda usa una secuencia del estilo de Oracle (donde se soporte).
+ </para>
+
+ <programlisting><![CDATA[<id name="id" type="long" column="cat_id">
+ <generator class="hilo">
+ <param name="table">hi_value</param>
+ <param name="column">next_value</param>
+ <param name="max_lo">100</param>
+ </generator>
+</id>]]></programlisting>
+
+ <programlisting><![CDATA[<id name="id" type="long" column="cat_id">
+ <generator class="seqhilo">
+ <param name="sequence">hi_value</param>
+ <param name="max_lo">100</param>
+ </generator>
+</id>]]></programlisting>
+
+ <para>
+ Desafortunadamente, no puedes usar <literal>hilo</literal> cuando le proveas tu propia
+ <literal>Connection</literal> a Hibernate. Cuando Hibernate está usando un datasource
+ del servidor de aplicaciones para obtener conexiones alistadas con JTA, debes configurar
+ correctamente el <literal>hibernate.transaction.manager_lookup_class</literal>.
+ </para>
+ </sect3>
+
+ <sect3 id="mapping-declaration-id-uuid">
+ <title>Algoritmo UUID</title>
+ <para>
+ El UUID contiene: la dirección IP, el instante de arranque de la JVM
+ (con una precisión de un cuarto de segundo), el tiempo de sistema y un valor
+ de contador (único en la JVM). No es posible obtener una dirección MAC o
+ una dirección de memoria desde código Java, así que esto es lo mejor
+ que podemos hacer sin usar JNI.
+ </para>
+ </sect3>
+
+ <sect3 id="mapping-declaration-id-sequences">
+ <title>Columnas de identidad y secuencias</title>
+ <para>
+ Para las bases de datos que soportan columnas de identidad (DB2, MySQL, Sybase, MS SQL),
+ puedes usar generación de claves <literal>identity</literal>. Para las bases de datos
+ que soportan secuencias (DB2, Oracle, PostgreSQL, Interbase, McKoi, SAP DB) puedes usar la generación
+ de claves del estilo <literal>sequence</literal>. Ambas estrategias requieren dos consultas SQL
+ para insertar un nuevo objeto.
+ </para>
+
+ <programlisting><![CDATA[<id name="id" type="long" column="person_id">
+ <generator class="sequence">
+ <param name="sequence">person_id_sequence</param>
+ </generator>
+</id>]]></programlisting>
+
+ <programlisting><![CDATA[<id name="id" type="long" column="person_id" unsaved-value="0">
+ <generator class="identity"/>
+</id>]]></programlisting>
+
+ <para>
+ Para desarrollos multiplataforma, la estrategia <literal>native</literal>
+ eiligirá de entre las estrategias <literal>identity</literal>,
+ <literal>sequence</literal> y <literal>hilo</literal>, dependiendo de las capacidades
+ de la base de datos subyacentes.
+ </para>
+ </sect3>
+
+ <sect3 id="mapping-declaration-id-assigned">
+ <title>Identificadores asignados</title>
+ <para>
+ Si quieres que la aplicación asigne los identificadores (en contraposición
+ a que los genere Hibernate), puedes usar el generador <literal>assigned</literal>.
+ Este generador especial usará el valor identificador ya asignado a la
+ propiedad identificadora del objeto. Este generador se usa cuandola clave primaria es
+ una clave natural en vez de una clave sustituta. Este es el comportamiento por defecto si
+ no especificas un elemento <literal><generator></literal>.
+ </para>
+
+ <para>
+ Elegir el generador <literal>assigned</literal> hace que Hibernate use
+ <literal>unsaved-value="undefined"</literal>, forzando a Hibernate a ir
+ a la base de datos para determinar si una instancia es transitoria o separada,
+ a menos que haya una propiedad de versión o timestamp, o que tu definas
+ <literal>Interceptor.isUnsaved()</literal>.
+ </para>
+ </sect3>
+
+ <sect3 id="mapping-declaration-id-select">
+ <title>Claves primarias asignadas por disparadores</title>
+ <para>
+ Para esquemas heredados solamente (Hibernate no genera DDL con disparadores).
+ </para>
+
+ <programlisting><![CDATA[<id name="id" type="long" column="person_id">
+ <generator class="select">
+ <param name="key">socialSecurityNumber</param>
+ </generator>
+</id>]]></programlisting>
+
+ <para>
+ En el ejemplo de arriba, hay una propiedad ánica llamada
+ <literal>socialSecurityNumber</literal> definida por la clase, como
+ una clave natural, y una clave sustituta llamada <literal>person_id</literal>
+ cuyo valor es generado por un disparador.
+ </para>
+
+ </sect3>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-compositeid" revision="2">
+ <title>composite-id</title>
+
+ <programlisting><![CDATA[<composite-id
+ name="propertyName"
+ class="ClassName"
+ unsaved-value="undefined|any|none"
+ access="field|property|ClassName"
+ node="element-name|.">
+
+ <key-property name="propertyName" type="typename" column="column_name"/>
+ <key-many-to-one name="propertyName class="ClassName" column="column_name"/>
+ ......
+</composite-id>]]></programlisting>
+
+ <para>
+ Para una tabla con clave compuesta, puedes mapear múltiples propiedades
+ de la clase como propiedades identificadoras. El elemento <literal><composite-id></literal>
+ acepta los mapeos de propiedad <literal><key-property></literal> y
+ los mapeos <literal><key-many-to-one></literal> como elementos hijo.
+ </para>
+
+ <programlisting><![CDATA[<composite-id>
+ <key-property name="medicareNumber"/>
+ <key-property name="dependent"/>
+</composite-id>]]></programlisting>
+
+ <para>
+ Tu clase persistente <emphasis>debe</emphasis> sobreescribir <literal>equals()</literal>
+ y <literal>hashCode()</literal> para implementar igualdad de identificador compuesto.
+ Debe también implementar <literal>Serializable</literal>.
+ </para>
+
+ <para>
+ Desafortunadamente, este enfoque de identificadores compuestos significa que un objeto
+ persistente es su propio identificador. No existe otra "asa" conveniente más que el
+ objeto mismo. Debes instanciar una instancia de la clase misma y poblar sus propiedades
+ identificadoras antes que puedas <literal>load()</literal> el estado persistente asociado
+ a una clave compuesta. Describiremos un enfoque mucho más conveniente donde el identificador
+ compuesto está implementado como una clase separada en <xref linkend="components-compositeid"/>.
+ Los atributos descriptos debajo solamente se aplican a este enfoque alternativo:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>name</literal> (opcional): Una propiedad de tipo componente que tiene el identificador
+ compuesto (ver siguiente sección).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>class</literal> (opcional - por defecto al tipo de la propiedad determinado
+ por reflección): La clase del componente usado como identificador compuesto (ver siguiente sección).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>unsaved-value</literal> (opcional - por defecto a <literal>undefined</literal>):
+ Indica que las instancias transitorias deben ser consideradas como recién instanciadas,
+ si se establece a <literal>any</literal>, o separadas, si se establece a <literal>none</literal>.
+ Lo mejor
+
+ Indicates that transient instances should be considered newly instantiated, if set
+ to <literal>any</literal>, or detached, if set to <literal>none</literal>.
+ Lo mejor en todos los casos es dejar el valor por defecto.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-discriminator" revision="3">
+ <title>discriminator</title>
+
+ <para>
+ El elemento <literal><discriminator></literal> es requerido para la persistencia
+ polimórfica usando la estrategia de mapeo de tabla-por-jerarquía-de-clases y
+ declara una columna discriminadora de la tabla. La columna discriminidora contiene valores
+ de marca que le dicen a la capa de persistencia qué subclase instanciar para una fila
+ en particular. Un conjunto restringido de tipos puede ser usado:
+ <literal>string</literal>, <literal>character</literal>, <literal>integer</literal>,
+ <literal>byte</literal>, <literal>short</literal>, <literal>boolean</literal>,
+ <literal>yes_no</literal>, <literal>true_false</literal>.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="discriminator1" coords="2 60"/>
+ <area id="discriminator2" coords="3 60" />
+ <area id="discriminator3" coords="4 60" />
+ <area id="discriminator4" coords="5 60" />
+ <area id="discriminator5" coords="6 60" />
+ </areaspec>
+ <programlisting><![CDATA[<discriminator
+ column="discriminator_column"
+ type="discriminator_type"
+ force="true|false"
+ insert="true|false"
+ formula="arbitrary sql expression"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="discriminator1">
+ <para>
+ <literal>column</literal> (opcional - por defecto a <literal>class</literal>) el
+ nombre de la columna discriminadora.
+ </para>
+ </callout>
+ <callout arearefs="discriminator2">
+ <para>
+ <literal>type</literal> (opcional - por defecto a <literal>string</literal>) un
+ nombre que indique el tipo Hibernate
+ </para>
+ </callout>
+ <callout arearefs="discriminator3">
+ <para>
+ <literal>force</literal> (optconal - por defecto a <literal>false</literal>)
+ "fuerza" a Hibernate a especificar valores discriminadores permitidos incluso
+ cuando se recuperan todas las instancias de la clase raíz.
+ </para>
+ </callout>
+ <callout arearefs="discriminator4">
+ <para>
+ <literal>insert</literal> (opcional - por defecto a <literal>true</literal>)
+ establezca este a <literal>false</literal> si tu columna discriminadora es
+ también parte de un identificador mapeado compuesto. (Le dice a Hibernate
+ que no incluya la columna en los SQL <literal>INSERT</literal>s.)
+ </para>
+ </callout>
+ <callout arearefs="discriminator5">
+ <para>
+ <literal>formula</literal> (opcional) una expresión SQL arbitraria que
+ es ejecutada cuando un tipo tenga que ser evaluado. Permite dicriminación
+ basada en el contenido.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Los valores reales de la columna discriminadora están especificados por
+ el atributo <literal>discriminator-value</literal> de los elementos
+ <literal><class></literal> y <literal><subclass></literal>.
+ </para>
+
+ <para>
+ El atributo <literal>force</literal> es (sólo) útil si la tabla contiene
+ filas con valores discriminadores "extra" que no están mapeados a la clase
+ persistente. Generalmente este no es el caso.
+ </para>
+
+ <para>
+ Usando el atributo <literal>formula</literal> puedes declarar una expresión SQL
+ arbitraria que será usada para evaluar el tipo de una fila:
+ </para>
+
+ <programlisting><![CDATA[<discriminator
+ formula="case when CLASS_TYPE in ('a', 'b', 'c') then 0 else 1 end"
+ type="integer"/>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-version" revision="1">
+ <title>version (opcional)</title>
+
+ <para>
+ El elemento <literal><version></literal> es opcional e indica que la
+ tabla contiene datos versionados. Esto es particularmente útil si planeas
+ usar <emphasis>transacciones largas</emphasis> (ver debajo).
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="version1" coords="2 70"/>
+ <area id="version2" coords="3 70"/>
+ <area id="version3" coords="4 70"/>
+ <area id="version4" coords="5 70"/>
+ <area id="version5" coords="6 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<version
+ column="version_column"
+ name="propertyName"
+ type="typename"
+ access="field|property|ClassName"
+ unsaved-value="null|negative|undefined"
+ node="element-name|@attribute-name|element/@attribute|."
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="version1">
+ <para>
+ <literal>column</literal> (opcional - por defecto al nombre de la propiedad): El nombre
+ de la columna que tiene el número de versión.
+ </para>
+ </callout>
+ <callout arearefs="version2">
+ <para>
+ <literal>name</literal>: El nombre de una propiedad de la clase persistente.
+ </para>
+ </callout>
+ <callout arearefs="version3">
+ <para>
+ <literal>type</literal> (opcional - por defecto a <literal>integer</literal>):
+ El tipo del nú.mero de vesión.
+ </para>
+ </callout>
+ <callout arearefs="version4">
+ <para>
+ <literal>access</literal> (opcional - por defecto a <literal>property</literal>): La
+ estrategia que Hibernate debe usar para acceder al valor de la propiedad.
+ </para>
+ </callout>
+ <callout arearefs="version5">
+ <para>
+ <literal>unsaved-value</literal> (opcional - por defecto a <literal>undefined</literal>):
+ Un valor de la propiedad de versión que indica que una instancia está
+ recién instanciada (sin guardar), distinguiéndola de instancias
+ separadas que fueran guardadas o cargadas en una sesión previa.
+ (<literal>undefined</literal> especifica que debe usarse el valor de la
+ propiedad identificadora.)
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Los números de versión deben ser de tipo <literal>long</literal>,
+ <literal>integer</literal>, <literal>short</literal>, <literal>timestamp</literal> o
+ <literal>calendar</literal> de Hibernate.
+ </para>
+
+ <para>
+ Una propiedad de versión o timestamp nunca debe ser nula para una instancia
+ separada, de modo que Hibernate detectará cualquier instancia con una versión
+ o timestamp nulo como transitoria, sin importar qué otras estrategias
+ <literal>unsaved-value</literal> se hayan especificado.
+ <emphasis>Declarar una propiedad de versón o timestamp nulable es una forma
+ fácil de evitar cualquier problema con la re-unión transitiva en Hibernate,
+ especialmente útil para que usa identificadores asignados o claves compuestas!</emphasis>
+ </para>
+ </sect2>
+
+ <sect2 id="mapping-declaration-timestamp">
+ <title>timestamp (opcional)</title>
+
+ <para>
+ El elemento opcional <literal><timestamp></literal> indica que la tabla contiene
+ datos con sellos de tiempo. Esto esta concebido como una alternativa al versionado.
+ Los timestamps (sellos de tiempo) son por su naturaleza una implementación menos
+ segura de bloqueo optimista. Sin embrago, a veces la aplicación puede usar los
+ timestamps en otras formas.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="timestamp1" coords="2 70"/>
+ <area id="timestamp2" coords="3 70" />
+ <area id="timestamp3" coords="4 70" />
+ <area id="timestamp4" coords="5 70" />
+ </areaspec>
+ <programlisting><![CDATA[<timestamp
+ column="timestamp_column"
+ name="propertyName"
+ access="field|property|ClassName"
+ unsaved-value="null|undefined"
+ node="element-name|@attribute-name|element/@attribute|."
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="timestamp1">
+ <para>
+ <literal>column</literal> (opcional - por defecto al nombre de la propiedad): El nombre
+ de una columna que tiene el timestamp.
+ </para>
+ </callout>
+ <callout arearefs="timestamp2">
+ <para>
+ <literal>name</literal>: El nombre de una propiedad del estilo JavaBeans de tipo
+ Java <literal>Date</literal> o <literal>Timestamp</literal> de la clase persistente.
+ </para>
+ </callout>
+ <callout arearefs="timestamp3">
+ <para>
+ <literal>access</literal> (opcional - por defecto a <literal>property</literal>): La
+ estrategia que Hibernate debe usar para acceder al valor de la propiedad.
+ </para>
+ </callout>
+ <callout arearefs="timestamp4">
+ <para>
+ <literal>unsaved-value</literal> (opcional - por defecto a <literal>null</literal>):
+ Un valor de propiedad de versión que indica que una instancia está
+ recién instanciada (sin guardar), distinguiéndola de instancias separadas
+ que hayan sido guardadas o cargadas en una sesión previa.
+ (<literal>undefined</literal> especifica que debe usarse el valor de la propiedad
+ identificadora.)
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Note that <literal><timestamp></literal> is equivalent to
+ <literal><version type="timestamp"></literal>.
+ </para>
+ </sect2>
+
+
+ <sect2 id="mapping-declaration-property" revision="2">
+ <title>property</title>
+
+ <para>
+ El elemento <literal><property></literal> declara una propiedad persistente estilo
+ JavaBean de la clase.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="property1" coords="2 70"/>
+ <area id="property2" coords="3 70"/>
+ <area id="property3" coords="4 70"/>
+ <areaset id="property4-5" coords="">
+ <area id="property4" coords='5 70'/>
+ <area id="property5" coords='6 70'/>
+ </areaset>
+ <area id="property6" coords="7 70"/>
+ <area id="property7" coords="8 70"/>
+ <area id="property8" coords="9 70"/>
+ <area id="property9" coords="10 70"/>
+ <area id="property10" coords="11 70"/>
+ <area id="property11" coords="12 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<property
+ name="propertyName"
+ column="column_name"
+ type="typename"
+ update="true|false"
+ insert="true|false"
+ formula="arbitrary SQL expression"
+ access="field|property|ClassName"
+ lazy="true|false"
+ unique="true|false"
+ not-null="true|false"
+ optimistic-lock="true|false"
+ node="element-name|@attribute-name|element/@attribute|."
+ index="index_name"
+ unique_key="unique_key_id"
+ length="L"
+ precision="P"
+ scale="S"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="property1">
+ <para>
+ <literal>name</literal>: el nombre de la propiedad, con la letra inicial
+ en minúsculas.
+ </para>
+ </callout>
+ <callout arearefs="property2">
+ <para>
+ <literal>column</literal> (opcional - por defecto al nombre de la propiedad): el nombre
+ de la columna de tabla de base de datos mapeada. Esto puede también ser especificado
+ con elemento(s) <literal><column></literal> anidado(s).
+ </para>
+ </callout>
+ <callout arearefs="property3">
+ <para>
+ <literal>type</literal> (opcional): un nombre que indica el nobre Hibernate.
+ </para>
+ </callout>
+ <callout arearefs="property4-5">
+ <para>
+ <literal>update, insert</literal> (opcional - por defecto a <literal>true</literal>) :
+ especifica que las columnas mapeadas deben ser incluídas en las sentencias SQL
+ <literal>UPDATE</literal> y/o <literal>INSERT</literal> . Especificando ambas a
+ <literal>false</literal> permite una propiedad "derivada" cuyo valor es inicializado desde
+ alguna otra propiedad que mapee a la misma columna (o columnas) o por un disparador u otra
+ aplicación.
+ </para>
+ </callout>
+ <callout arearefs="property6">
+ <para>
+ <literal>formula</literal> (opcional): una expresión SQL que define el valor
+ para una propiedad <emphasis>computada</emphasis>. Las propiedades computadas no tienen
+ una columna mapeada propia.
+ </para>
+ </callout>
+ <callout arearefs="property7">
+ <para>
+ <literal>access</literal> (opcional - por defecto a <literal>property</literal>): La
+ estrategia que Hibernate debe usar para acceder al valor de la propiedad.
+ </para>
+ </callout>
+ <callout arearefs="property8">
+ <para>
+ <literal>lazy</literal> (opcional - por defecto a <literal>false</literal>): Especifica
+ que esta propiedad debe ser traída perezosamente cuando la variable de instancia
+ sea accedida por primera vez (requiere instrumentación en tiempo de compilación).
+ </para>
+ </callout>
+ <callout arearefs="property9">
+ <para>
+ <literal>unique</literal> (opcional): Habilita la generació DDL de una restricción
+ de unicidad para las columnas. Además, permite que ésta sea un blanco objetivo de
+ una <literal>property-ref</literal>.
+ </para>
+ </callout>
+ <callout arearefs="property10">
+ <para>
+ <literal>not-null</literal> (opcional): Habilita la generació DDL de una restricción
+ de nulabilidad para las columnas.
+ </para>
+ </callout>
+ <callout arearefs="property11">
+ <para>
+ <literal>optimistic-lock</literal> (opcional - por defecto a <literal>true</literal>):
+ Especifica que las actualizaciones a esta propiedad requieran o no de la obtención
+ de un bloqueo optimista. En otras palabras, determina si debe ocurrir un incremento de versión
+ cuando la propiedad este sucia (desactualizada).
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ <emphasis>typename</emphasis> puede ser:
+ </para>
+
+ <orderedlist spacing="compact">
+ <listitem>
+ <para>
+ El nombre de un tipo básico Hibernate (por ejemplo, <literal>integer, string, character,
+ date, timestamp, float, binary, serializable, object, blob</literal>).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ El nombre de una clase Java de tipo básico (por ejemplo, <literal>int, float,
+ char, java.lang.String, java.util.Date, java.lang.Integer, java.sql.Clob</literal>).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ El nombre de una clase Java serializable.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ El nombre de un tipo personalizado (por ejemplo, <literal>com.illflow.type.MyCustomType</literal>).
+ </para>
+ </listitem>
+ </orderedlist>
+
+ <para>
+ Si no especificas un tipo, Hibernate usará reflección sobre la
+ propiedad mencionada para deducir el tipo Hibernate correcto. Hibernate intentará
+ interpretar el nombre de la clase de retorno del getter de la propiedad usando las reglas
+ 2, 3 y 4 en ese orden. Sin embargo, esto no siempre suficiente. En ciertos casos, necesitarás
+ aún el atributo <literal>type</literal>. (Por ejemplo, para distinguir entre
+ <literal>Hibernate.DATE</literal> y <literal>Hibernate.TIMESTAMP</literal>,
+ o especificar un tipo personalizado.)
+ </para>
+
+ <para>
+ El atributo <literal>access</literal> te deja controlar cómo Hibernate
+ accederá a la propiedad en tiempo de ejecución. Por defecto, Hibernate
+ llamará al par de getter/setter de la propiedad. Si especificas
+ <literal>access="field"</literal>, Hibernate se saltará el par get/set y
+ accederá al campo directamente usando reflección. Puedes especificar tu
+ propia estrategia de acceso a la propiedad mencionando una clase que implemente
+ la interface <literal>org.hibernate.property.PropertyAccessor</literal>.
+ </para>
+
+ <para>
+ Una aspecto especialmente poderoso son las propiedades derivadas. Estas propiedades
+ son por definición de sólo lectura, el valor de la propiedad es computado
+ en tiempo de carga. Tu declaras la computación como una expresión SQL,
+ y ésta se traduce a cláusula de subconsulta <literal>SELECT</literal>
+ en la consulta SQL que cargue una instancia:
+ </para>
+
+ <programlisting><![CDATA[
+<property name="totalPrice"
+ formula="( SELECT SUM (li.quantity*p.price) FROM LineItem li, Product p
+ WHERE li.productId = p.productId
+ AND li.customerId = customerId
+ AND li.orderNumber = orderNumber )"/>]]></programlisting>
+
+ <para>
+ Observa que puedes referenciar la propia tabla de las entidades sin declarar un alias
+ o una columna particular (<literal>customerId</literal> en el ejemplo dado). Observa
+ además que puedes usar el elemento anidado de mapeo <literal><formula></literal>
+ si no te gusta usar el atributo.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-manytoone" revision="3">
+ <title>many-to-one</title>
+
+ <para>
+ Una asociación ordinaria a otra clase persistente se declara usando
+ el elemento <literal>many-to-one</literal>. El modelo relacional es una
+ asociación muchos-a-uno: una clave foránea en una tabla está
+ referenciando la columna (o columnas) de la clave primaria de la tabla objetivo.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="manytoone1" coords="2 70"/>
+ <area id="manytoone2" coords="3 70"/>
+ <area id="manytoone3" coords="4 70"/>
+ <area id="manytoone4" coords="5 70"/>
+ <area id="manytoone5" coords="6 70"/>
+ <areaset id="manytoone6-7" coords="">
+ <area id="manytoone6" coords='7 70'/>
+ <area id="manytoone7" coords='8 70'/>
+ </areaset>
+ <area id="manytoone8" coords="9 70"/>
+ <area id="manytoone9" coords="10 70"/>
+ <area id="manytoone10" coords="11 70"/>
+ <area id="manytoone11" coords="12 70"/>
+ <area id="manytoone12" coords="13 70"/>
+ <area id="manytoone13" coords="14 70"/>
+ <area id="manytoone14" coords="15 70"/>
+ <area id="manytoone15" coords="16 70"/>
+ <area id="manytoone16" coords="17 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<many-to-one
+ name="propertyName"
+ column="column_name"
+ class="ClassName"
+ cascade="cascade_style"
+ fetch="join|select"
+ update="true|false"
+ insert="true|false"
+ property-ref="propertyNameFromAssociatedClass"
+ access="field|property|ClassName"
+ unique="true|false"
+ not-null="true|false"
+ optimistic-lock="true|false"
+ lazy="true|proxy|false"
+ not-found="ignore|exception"
+ entity-name="EntityName"
+ formula="cualquier expresión SQL"
+ node="element-name|@attribute-name|element/@attribute|."
+ embed-xml="true|false"
+ index="index_name"
+ unique_key="unique_key_id"
+ foreign-key="foreign_key_name"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="manytoone1">
+ <para>
+ <literal>name</literal>: El nombre de la propiedad.
+ </para>
+ </callout>
+ <callout arearefs="manytoone2">
+ <para>
+ <literal>column</literal> (opcional): El nombre de la columna clave foránea.
+ También puede ser especificado por uno o varios elementos anidados
+ <literal><column></literal>.
+ </para>
+ </callout>
+ <callout arearefs="manytoone3">
+ <para>
+ <literal>class</literal> (opcional - por defecto al tipo de la propiedad
+ determinado por reflección): El nombre de la clase asociada.
+ </para>
+ </callout>
+ <callout arearefs="manytoone4">
+ <para>
+ <literal>cascade</literal> (opcional): Especifica qué operaciones deben
+ ir en cascada desde el objeto padre al objeto asociado
+ </para>
+ </callout>
+ <callout arearefs="manytoone5">
+ <para>
+ <literal>fetch</literal> (opcional - por defecto a 1<literal>select</literal>):
+ Escoge entre recuperación outer-join o por selección secuencial.
+ </para>
+ </callout>
+ <callout arearefs="manytoone6-7">
+ <para>
+ <literal>update, insert</literal> (opcional - por defecto a <literal>true</literal>)
+ especifica que las columnas mapeadas deben ser incluídas en las sentencias SQL
+ <literal>UPDATE</literal> y/o <literal>INSERT</literal>. Establecer ambas a
+ <literal>false</literal> permite una asociación puramente "derivada" cuyo
+ valor es inicializado desde alguna otra propiedad que mapea a las misma columna (o columnas),
+ o por un disparador, o por otra aplicación.
+ </para>
+ </callout>
+ <callout arearefs="manytoone8">
+ <para>
+ <literal>property-ref</literal>: (opcional) El nombre de la propiedad de la clase
+ asociada que está unida a la clave foránea. Si no se especifica, se usa
+ la clave primaria de la clase asociada.
+ </para>
+ </callout>
+ <callout arearefs="manytoone9">
+ <para>
+ <literal>access</literal> (opcional - por defecto a <literal>property</literal>): La
+ estrategia que Hibernate debe usar para acceder al valor de la propiedad.
+ </para>
+ </callout>
+ <callout arearefs="manytoone10">
+ <para>
+ <literal>unique</literal> (opcional): Habilita la generación DDL de una
+ restricción de unicidad para la columna de clave foránea. Además,
+ permite que ésta sea el objetivo de una <literal>property-ref</literal>.
+ Esto hace efectivamente la multiplicidad de la asociación uno a uno.
+ </para>
+ </callout>
+ <callout arearefs="manytoone11">
+ <para>
+ <literal>not-null</literal> (opcional): Habilita la generación DDL de una
+ restricción de nulabilidad para las columnas de clave foránea.
+ </para>
+ </callout>
+ <callout arearefs="manytoone12">
+ <para>
+ <literal>optimistic-lock</literal> (opcional - por defecto a <literal>true</literal>):
+ Especifica que las actualizaciones a esta propiedad requieran o no la obtención
+ del bloqueo optimista. En otras palabras, determina si debe darse un incremento de versión
+ cuando esta propiedad esté desactualizada.
+ </para>
+ </callout>
+ <callout arearefs="manytoone13">
+ <para>
+ <literal>lazy</literal> (opcional - por defecto a <literal>proxy</literal>):
+ Por defecto, las asociaciones de punto único van con proxies.
+ <literal>lazy="true"</literal> especifica que esta propiedad debe ser
+ traída perezosamente cuando la variable de instancia sea accedida por primera
+ vez (requiere instrumentación del bytecode en tiempo de compilación).
+ <literal>lazy="false"</literal> especifica que la asociación siempre será
+ recuperada tempranamente.
+ </para>
+ </callout>
+ <callout arearefs="manytoone14">
+ <para>
+ <literal>not-found</literal> (opcional - por defecto a <literal>exception</literal>):
+ Especifica cómo deben manejarse las claves foráneas que referencien
+ filas inexistentes: <literal>ignore</literal> tratará una fila perdida como
+ una asociación nula.
+ </para>
+ </callout>
+ <callout arearefs="manytoone15">
+ <para>
+ <literal>entity-name</literal> (opcional): El nombre de entidad de la clase
+ asociada.
+ </para>
+ </callout>
+ <callout arearefs="manytoone16">
+ <para>
+ <literal>formula</literal> (opcional): una expresión SQL que define el valor
+ para una clave foránea <emphasis>computada</emphasis>.
+ </para>
+ </callout>
+
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Establecer el valor del atributo <literal>cascade</literal> a cualquier valor
+ significativo distinto de <literal>none</literal> propagará ciertas
+ operaciones al objeto asociado. Los valores significativos son los nombres de las
+ operaciones básicas de Hibernate, <literal>persist, merge, delete, save-update,
+ evict, replicate, lock, refresh</literal>, así como los valores especiales
+ <literal>delete-orphan</literal> y <literal>all</literal> y combinaciones de operaciones
+ separadas por coma, por ejemplo, <literal>cascade="persist,merge,evict"</literal> o
+ <literal>cascade="all,delete-orphan"</literal>. Para una explicación completa,
+ ver <xref linkend="objectstate-transitive"/>.
+ </para>
+
+ <para>
+ Una declaración típica <literal>muchos-a-uno</literal> se parece a esto:
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="product" class="Product" column="PRODUCT_ID"/>]]></programlisting>
+
+ <para>
+ El atributo <literal>property-ref</literal> debe ser usado solamente para el mapeo
+ de datos heredados donde una clave foránea referencia una clave única de
+ la tabla asociada, distinta de la clave primaria. Este es un modelo relacional feo.
+ Por ejemplo, supón que la clase <literal>Product</literal> tuviera un número
+ único serial que no es la clave primaria. (El atributo <literal>unique</literal>
+ controla la generación de DDL con la herramienta SchemaExport.)
+ </para>
+
+ <programlisting><![CDATA[<property name="serialNumber" unique="true" type="string" column="SERIAL_NUMBER"/>]]></programlisting>
+
+ <para>
+ Entonces el mapeo para <literal>OrderItem</literal> debería usar:
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="product" property-ref="serialNumber" column="PRODUCT_SERIAL_NUMBER"/>]]></programlisting>
+
+ <para>
+ Sin embargo, esto no esta ciertamente alentado.
+ </para>
+
+ <para>
+ Si la clave única referenciada abarca múltiples propiedades de la entidad asociada,
+ debes mapear las propiedades dentro de un elemento <literal><properties></literal>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-onetoone" revision="2">
+ <title>one-to-one</title>
+
+ <para>
+ Una asociación uno-a-uno a otra clase persistente se declara usando
+ un elemento <literal>one-to-one</literal>.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="onetoone1" coords="2 70"/>
+ <area id="onetoone2" coords="3 70"/>
+ <area id="onetoone3" coords="4 70"/>
+ <area id="onetoone4" coords="5 70"/>
+ <area id="onetoone5" coords="6 70"/>
+ <area id="onetoone6" coords="7 70"/>
+ <area id="onetoone7" coords="8 70"/>
+ <area id="onetoone8" coords="9 70"/>
+ <area id="onetoone9" coords="10 70"/>
+ <area id="onetoone10" coords="11 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<one-to-one
+ name="propertyName"
+ class="ClassName"
+ cascade="cascade_style"
+ constrained="true|false"
+ fetch="join|select"
+ property-ref="propertyNameFromAssociatedClass"
+ access="field|property|ClassName"
+ formula="cualquier expresión SQL"
+ lazy="true|proxy|false"
+ entity-name="EntityName"
+ node="element-name|@attribute-name|element/@attribute|."
+ embed-xml="true|false"
+ foreign-key="foreign_key_name"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="onetoone1">
+ <para>
+ <literal>name</literal>: El nombre de la propiedad.
+ </para>
+ </callout>
+ <callout arearefs="onetoone2">
+ <para>
+ <literal>class</literal> (opcional - por defecto al tipo de la propiedad
+ determinado por reflección): El nombre de la clase asociada.
+ </para>
+ </callout>
+ <callout arearefs="onetoone3">
+ <para>
+ <literal>cascade</literal> (opcional) especifica qué operaciones deben
+ ir en cascada desde el objeto padre al objeto asociado.
+ </para>
+ </callout>
+ <callout arearefs="onetoone4">
+ <para>
+ <literal>constrained</literal> (opcional) especifica que una restricción
+ de clave foránea de la tabla mapeada referencia a la tabla de la clase
+ asociada. Esta opción afecta el orden en que van en cascada
+ <literal>save()</literal> y <literal>delete()</literal>, y determina cuándo
+ la asociación pueden ser virtualizados por proxies (es también usado por
+ la herramienta de exportación de esquemas).
+ </para>
+ </callout>
+ <callout arearefs="onetoone5">
+ <para>
+ <literal>fetch</literal> (opcional - por defecto <literal>select</literal>):
+ Elige entre recuperación outer-join o recuperación por consulta secuencial.
+ </para>
+ </callout>
+ <callout arearefs="onetoone6">
+ <para>
+ <literal>property-ref</literal>: (opcional) El nombre de una propiedad de la clase
+ asociada que esté unida a la clave primaria de esta clase. Si no se especifica,
+ se usa la clave primaria de la clase asociada.
+ </para>
+ </callout>
+ <callout arearefs="onetoone7">
+ <para>
+ <literal>access</literal> (opcional - por defecto a <literal>property</literal>): La
+ estrategia que Hibernate debe usar para acceder al valor de la propiedad.
+ </para>
+ </callout>
+ <callout arearefs="onetoone8">
+ <para>
+ <literal>formula</literal> (opcional): Casi todas las asociaciones uno-a-uno mapean
+ a la clave primaria de la entidad propietaria. En el raro caso en que este no sea el caso,
+ puedes especificar alguna otra columna, o columnas, o expresión para unir usando una
+ fórmula SQL. (Para un ejemplo ver <literal>org.hibernate.test.onetooneformula</literal>).
+ </para>
+ </callout>
+ <callout arearefs="onetoone9">
+ <para>
+ <literal>lazy</literal> (opcional - por defecto a <literal>proxy</literal>):
+ Por defecto, las asociaciones de punto único van con proxies.
+ <literal>lazy="true"</literal> especifica que esta propiedad debe ser
+ traída perezosamente cuando la variable de instancia sea accedida por primera
+ vez (requiere instrumentación del bytecode en tiempo de compilación).
+ <literal>lazy="false"</literal> especifica que la asociación siempre será
+ recuperada tempranamente. <emphasis>Observa que si <literal>constrained="false"</literal>,
+ la aplicación de proxies es imposible e Hibernate traerá temprano la
+ asociación!</emphasis>
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Hay dos variedades de asociaciones uno-a-uno:
+ </para>
+ <itemizedlist>
+ <listitem><para>
+ asociaciones de clave primaria
+ </para></listitem>
+ <listitem><para>
+ asociaciones de clave foráneas única
+ </para></listitem>
+ </itemizedlist>
+
+ <para>
+ Las asociaciones de clave primaria no necesitan una columna de tabla extra; si dos filas
+ están relacionadas por la asociación entonces las dos filas de tablas comparten
+ el mismo valor de clave primaria. Por lo tanto, si quieres que dos objetos estén relacionados
+ por una asociación de clave primaria, debes asegurarte que se les asigne el mismo valor de
+ identificador!
+ </para>
+
+ <para>
+ Para una asociación de clave primaria, añade los siguientes mapeos a
+ <literal>Employee</literal> y <literal>Person</literal>, respectivamente.
+ </para>
+
+ <programlisting><![CDATA[<one-to-one name="person" class="Person"/>]]></programlisting>
+ <programlisting><![CDATA[<one-to-one name="employee" class="Employee" constrained="true"/>]]></programlisting>
+
+ <para>
+ Ahora debemos asegurarnos que las claves primarias de las filas relacionadas en las tablas
+ PERSON y EMPLOYEE sean iguales. Usamos una estrategia especial de generación de identificador
+ de Hibernate llamada <literal>foreign</literal>:
+ </para>
+
+ <programlisting><![CDATA[<class name="person" table="PERSON">
+ <id name="id" column="PERSON_ID">
+ <generator class="foreign">
+ <param name="property">employee</param>
+ </generator>
+ </id>
+ ...
+ <one-to-one name="employee"
+ class="Employee"
+ constrained="true"/>
+</class>]]></programlisting>
+
+ <para>
+ A una instancia recién salvada de <literal>Person</literal> se le asigna entonces
+ el mismo valor de clave primaria con que la instancia <literal>Employee</literal>
+ referida por la propiedad <literal>employee</literal> de esta <literal>Person</literal>.
+ </para>
+
+ <para>
+ Alternativamente, una clave foránea con una restricción de unicidad, desde
+ <literal>Employee</literal> a <literal>Person</literal>, puede ser expresada como:
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="person" class="Person" column="PERSON_ID" unique="true"/>]]></programlisting>
+
+ <para>
+ Y esta asociación puede hacerse bidireccional agregando lo siguiente al mapeo de
+ <literal>Person</literal> :
+ </para>
+
+ <programlisting><![CDATA[<one-to-one name"employee" class="Employee" property-ref="person"/>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-naturalid">
+ <title>natural-id</title>
+
+ <programlisting><![CDATA[<natural-id mutable="true|false"/>
+ <property ... />
+ <many-to-one ... />
+ ......
+</natural-id>]]></programlisting>
+
+ <para>
+ Aunque recomendamos el uso de claves delegadas como claves primarias, todavía debes
+ intentar identificar claves naturales para todas las entidades. Una clave natural es una
+ propiedad o combinación de propiedades que es única y no nula. Si además
+ es inmutable, mejor aún. Mapea las propiedades de la clave natural dentro del elemento
+ <literal><natural-id></literal>. Hibernate generará las restricciones de clave
+ única y nulabilidad necesarias, y tu mapeo será más auto-documentado.
+ </para>
+
+ <para>
+ Recomendamos fuertemente que implementes <literal>equals()</literal> y
+ <literal>hashCode()</literal> para comparar las propiedades de clave natural
+ de la entidad.
+ </para>
+
+ <para>
+ Este mapeo no está concebido para usar con entidades con claves
+ primarias naturales.
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>mutable</literal> (opcional, por defecto a <literal>false</literal>):
+ Por defecto, se asume que las propiedades de identificadores naturales son
+ inmutables (constantes).
+ </para>
+ </listitem>
+ </itemizedlist>
+ </sect2>
+
+
+
+ <sect2 id="mapping-declaration-component" revision="2">
+ <title>component, dynamic-component</title>
+
+ <para>
+ El elemento <literal><component></literal> mapea propiedades de un objeto
+ hijo a columnas de la tabla de la clase padre. Los componentes pueden a su vez
+ declarar sus propias propiedades, componentes o colecciones. Ver debajo "Componentes".
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="component1" coords="2 45"/>
+ <area id="component2" coords="3 45"/>
+ <area id="component3" coords="4 45"/>
+ <area id="component4" coords="5 45"/>
+ <area id="component5" coords="6 45"/>
+ <area id="component6" coords="7 45"/>
+ <area id="component7" coords="8 45"/>
+ <area id="component8" coords="9 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<component
+ name="propertyName"
+ class="className"
+ insert="true|false"
+ update="true|false"
+ access="field|property|ClassName"
+ lazy="true|false"
+ optimistic-lock="true|false"
+ unique="true|false"
+ node="element-name|."
+>
+
+ <property ...../>
+ <many-to-one .... />
+ ........
+</component>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="component1">
+ <para>
+ <literal>name</literal>: El nombre de la propiedad.
+ </para>
+ </callout>
+ <callout arearefs="component2">
+ <para>
+ <literal>class</literal> (opcional - por defecto al tipo de la propiedad
+ determinado por reflección): El nombre de la clase del componente (hijo).
+ </para>
+ </callout>
+ <callout arearefs="component3">
+ <para>
+ <literal>insert</literal>: Aparecen las columnas mapeadas en
+ <literal>INSERT</literal>s SQL?
+ </para>
+ </callout>
+ <callout arearefs="component4">
+ <para>
+ <literal>update</literal>: Aparecen las columnas mapeadas en
+ <literal>UPDATE</literal>s SQL?
+ </para>
+ </callout>
+ <callout arearefs="component5">
+ <para>
+ <literal>access</literal> (opcional - por defecto a <literal>property</literal>): La
+ estrategia que Hibernate debe usar para acceder al valor de la propiedad.
+ </para>
+ </callout>
+ <callout arearefs="component6">
+ <para>
+ <literal>lazy</literal> (opcional - por defecto a <literal>false</literal>): Especifica
+ que este componente debe ser recuperado perezosamente cuando la variable de instancia
+ sea accedida por primera vez (requiere instrumentación de bytecode en tiempo de
+ compilación).
+ </para>
+ </callout>
+ <callout arearefs="component7">
+ <para>
+ <literal>optimistic-lock</literal> (opcional - por defecto a <literal>true</literal>):
+ Especifica si las actualizaciones de este componente requieren o no la
+ adquisición de un bloqueo optimista. En otras palabras, determina si
+ debe ocurrir un incremento de versión cuando esta propiedad está
+ desactualizada.
+ </para>
+ </callout>
+ <callout arearefs="component8">
+ <para>
+ <literal>unique</literal> (opcional - por defecto a <literal>false</literal>):
+ Especifica que existe una restricción de unicidad sobre todas las
+ columnas mapeadas del componente.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Las etiquetas hijas <literal><property></literal> mapean propiedades de la
+ clase hija columnas de la tabla.
+ </para>
+
+ <para>
+ El elemento <literal><component></literal> permite un subelemento
+ <literal><parent></literal> que mapea una propiedad de la clase del componente
+ como una referencia de regreso a la entidad contenedora.
+ </para>
+
+ <para>
+ El elemento <literal><dynamic-component></literal> permite que un
+ <literal>Map</literal> sea mapeado como un componente, donde los nombres de
+ las propiedades se corresponden a las claves del mapa, ver <xref linkend="components-dynamic"/>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-properties" revision="2">
+ <title>properties</title>
+
+ <para>
+ El elemento <literal><properties></literal> permite la definición de
+ un grupo de propiedades lógico con nombre de una clase. El uso más
+ importante de la contrucción es que permite que una combinación
+ de propiedades sea objetivo de una <literal>property-ref</literal>. Es también
+ una forma conveniente de definir una restricción de unicidad multicolumna.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="properties1" coords="2 45"/>
+ <area id="properties2" coords="3 45"/>
+ <area id="properties3" coords="4 45"/>
+ <area id="properties4" coords="5 45"/>
+ <area id="properties5" coords="6 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<properties
+ name="logicalName"
+ insert="true|false"
+ update="true|false"
+ optimistic-lock="true|false"
+ unique="true|false"
+>
+
+ <property ...../>
+ <many-to-one .... />
+ ........
+</properties>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="properties1">
+ <para>
+ <literal>name</literal>: El nombre lógico del agrupamiento -
+ <emphasis>no</emphasis> un nombre de propiedad real.
+ </para>
+ </callout>
+ <callout arearefs="properties2">
+ <para>
+ <literal>insert</literal>: Aparecen las columnas mapeadas en
+ <literal>INSERT</literal>s SQL?
+ </para>
+ </callout>
+ <callout arearefs="properties3">
+ <para>
+ <literal>update</literal>: Aparecen las columnas mapeadas en
+ <literal>UPDATE</literal>s SQL?
+ </para>
+ </callout>
+ <callout arearefs="properties4">
+ <para>
+ <literal>optimistic-lock</literal> (opcional - por defecto a <literal>true</literal>):
+ Especifica si las actualizaciones de estas propiedades requieren o no de la
+ adquisición de un bloqueo optimista. En otras palabras, determina si
+ debe ocurrir un incremento de versión cuando estas propiedades están
+ desactualizadas.
+ </para>
+ </callout>
+ <callout arearefs="properties5">
+ <para>
+ <literal>unique</literal> (opcional - por defecto a <literal>false</literal>):
+ Especifica que existe una restricción de unicidad sobre todas las
+ columnas mapeadas del componente.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Por ejemplo, si tenemos el siguiente mapeo de <literal><properties></literal>:
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="personNumber"/>
+ ...
+ <properties name="name"
+ unique="true" update="false">
+ <property name="firstName"/>
+ <property name="initial"/>
+ <property name="lastName"/>
+ </properties>
+</class>]]></programlisting>
+
+ <para>
+ Entonces puede que tengamos alguna asociación de datos heredados que se refiera
+ a esta clave única de la tabla de <literal>Person</literal>, en vez de la clave
+ primaria:
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="person"
+ class="Person" property-ref="name">
+ <column name="firstName"/>
+ <column name="initial"/>
+ <column name="lastName"/>
+</many-to-one>]]></programlisting>
+
+ <para>
+ No recomendamos el uso de este tipo de cosas fuera del contexto del mapeo de
+ datos heredados.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-subclass" revision="3">
+ <title>subclass</title>
+
+ <para>
+ Finalmente, la persistencia polimórfica requiere la declaración
+ de la clase persistente raíz. Para la estrategia de mapeo
+ tabla-por-jerarquía-de-clases, se usa la declaración de
+ <literal><subclass></literal>.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="subclass1" coords="2 55"/>
+ <area id="subclass2" coords="3 55"/>
+ <area id="subclass3" coords="4 55"/>
+ <area id="subclass4" coords="5 55"/>
+ </areaspec>
+ <programlisting><![CDATA[<subclass
+ name="ClassName"
+ discriminator-value="discriminator_value"
+ proxy="ProxyInterface"
+ lazy="true|false"
+ dynamic-update="true|false"
+ dynamic-insert="true|false"
+ entity-name="EntityName"
+ node="element-name">
+
+ <property .... />
+ .....
+</subclass>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="subclass1">
+ <para>
+ <literal>name</literal>: El nombre de clase cualificado completamente
+ de la subclase.
+ </para>
+ </callout>
+ <callout arearefs="subclass2">
+ <para>
+ <literal>discriminator-value</literal> (opcional - por defecto al nombre de la clase):
+ Un valor que distingue a subclases individuales.
+ </para>
+ </callout>
+ <callout arearefs="subclass3">
+ <para>
+ <literal>proxy</literal> (opcional): Especifica una clase o interface a usar para
+ proxies de inicialización perezosa.
+ </para>
+ </callout>
+ <callout arearefs="subclass4">
+ <para>
+ <literal>lazy</literal> (opcional, por defecto a <literal>true</literal>):
+ Establecer <literal>lazy="false"</literal> deshabilita el uso de recuperación
+ perezosa.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Cada subclase debe declarar sus propias propiedades persistentes y subclases.
+ Se asume que las propiedades <literal><version></literal> y <literal><id></literal>
+ son heredadas de la clase raíz. Cada subclase en una jerarquía debe
+ definir un <literal>discriminator-value</literal> único. Si no se especifica ninguno,
+ se usa el nombre completamente cualificado de clase Java.
+ </para>
+
+ <para>
+ Es posible definir mapeos <literal>subclass</literal>, <literal>union-subclass</literal>,
+ y <literal>joined-subclass</literal> en documentos de mapeo separados, directamente debajo
+ de <literal>hibernate-mapping</literal>. Esto te permite extender una jerarquía de clases
+ con sólo agregar un nuevo fichero de mapeo. Debes especificar un atributo
+ <literal>extends</literal> en el mapeo de la subclase, mencionando una superclase previamente mapeada.
+ Nota: Previamente esta funcionalidad hacía importante el orden de los documentos de mapeo.
+ Desde Hibernate3, el orden de los ficheros de mapeo no importa cuando se usa la palabra reservada
+ extends. El orden dentro de un mismo fichero de mapeo todavía necesita ser definido como
+ superclases antes de subclases.
+ </para>
+
+ <programlisting><![CDATA[
+<hibernate-mapping>
+ <subclass name="DomesticCat" extends="Cat" discriminator-value="D">
+ <property name="name" type="string"/>
+ </subclass>
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Para información acerca de mapeos de herencia, ver <xref linkend="inheritance"/>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-joinedsubclass" revision="3">
+ <title>joined-subclass</title>
+
+ <para>
+ Alternativamente, cada subclase puede ser mapeada a su propia tabla
+ (estrategia de mapeo tabla-por-subclase). El estado heredado se recupera
+ uniendo con la tabla de la superclase.
+ Usamos el elemento <literal><joined-subclass></literal>.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="joinedsubclass1" coords="2 45"/>
+ <area id="joinedsubclass2" coords="3 45"/>
+ <area id="joinedsubclass3" coords="4 45"/>
+ <area id="joinedsubclass4" coords="5 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<joined-subclass
+ name="ClassName"
+ table="tablename"
+ proxy="ProxyInterface"
+ lazy="true|false"
+ dynamic-update="true|false"
+ dynamic-insert="true|false"
+ schema="schema"
+ catalog="catalog"
+ extends="SuperclassName"
+ persister="ClassName"
+ subselect="SQL expression"
+ entity-name="EntityName"
+ node="element-name">
+
+ <key .... >
+
+ <property .... />
+ .....
+</joined-subclass>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="joinedsubclass1">
+ <para>
+ <literal>name</literal>: El nombre de clase completamente cualificado de
+ la subclase.
+ </para>
+ </callout>
+ <callout arearefs="joinedsubclass2">
+ <para>
+ <literal>table</literal>: El nombre de tabla de la subclase.
+ </para>
+ </callout>
+ <callout arearefs="joinedsubclass3">
+ <para>
+ <literal>proxy</literal> (opcional): Especifica una clase o interface a
+ usar para proxies de inicializacón perezosa.
+ </para>
+ </callout>
+ <callout arearefs="joinedsubclass4">
+ <para>
+ <literal>lazy</literal> (opcional, por defecto a <literal>true</literal>):
+ Establecer <literal>lazy="false"</literal> deshabilita el uso de recuperación
+ perezosa.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ No se requiere de una columna discriminadora para esta estrategia de mapeo. Cada subclase debe,
+ sin embargo, declarar una columna de tabla que tenga el identificador del objeto usando el
+ elemento <literal><key></literal>. El mapeo del comienzo del capítulo
+ debería ser reescrito como:
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="eg">
+
+ <class name="Cat" table="CATS">
+ <id name="id" column="uid" type="long">
+ <generator class="hilo"/>
+ </id>
+ <property name="birthdate" type="date"/>
+ <property name="color" not-null="true"/>
+ <property name="sex" not-null="true"/>
+ <property name="weight"/>
+ <many-to-one name="mate"/>
+ <set name="kittens">
+ <key column="MOTHER"/>
+ <one-to-many class="Cat"/>
+ </set>
+ <joined-subclass name="DomesticCat" table="DOMESTIC_CATS">
+ <key column="CAT"/>
+ <property name="name" type="string"/>
+ </joined-subclass>
+ </class>
+
+ <class name="eg.Dog">
+ <!-- mapping for Dog could go here -->
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Para información acerca de mapeos de herencia, ver <xref linkend="inheritance"/>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-unionsubclass" revision="2">
+ <title>union-subclass</title>
+
+ <para>
+ Una tercera opción es mapear sólo las clases concretas de una
+ jerarquía de clases a tablas, (la estrategia tabla-por-clase-concreta)
+ donde cada tabla define todo el estado persistente de la clase, incluyendo el
+ estado heredado. En Hibernate, no es absolutamente necesario mapear dichas
+ jerarquías de herencia. Puedes simplemente mapear cada clase con una
+ declaración <literal><class></literal> separada. Sin embargo,
+ si deseas usar asociaciones polimórficas (por ejemplo, una asociación
+ a la superclase de tu jerarquía), debes usar el mapeo
+ <literal><union-subclass></literal>.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="unionsubclass1" coords="2 45"/>
+ <area id="unionsubclass2" coords="3 45"/>
+ <area id="unionsubclass3" coords="4 45"/>
+ <area id="unionsubclass4" coords="5 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<union-subclass
+ name="ClassName"
+ table="tablename"
+ proxy="ProxyInterface"
+ lazy="true|false"
+ dynamic-update="true|false"
+ dynamic-insert="true|false"
+ schema="schema"
+ catalog="catalog"
+ extends="SuperclassName"
+ abstract="true|false"
+ persister="ClassName"
+ subselect="SQL expression"
+ entity-name="EntityName"
+ node="element-name">
+
+ <property .... />
+ .....
+</union-subclass>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="unionsubclass1">
+ <para>
+ <literal>name</literal>: El nombre de clase completamente cualificado de la subclase.
+ </para>
+ </callout>
+ <callout arearefs="unionsubclass2">
+ <para>
+ <literal>table</literal>: El nombre de tabla de la subclase.
+ </para>
+ </callout>
+ <callout arearefs="unionsubclass3">
+ <para>
+ <literal>proxy</literal> (optional): Especifica una clase o interface a
+ usar para proxies de inicializacón perezosa.
+ </para>
+ </callout>
+ <callout arearefs="unionsubclass4">
+ <para>
+ <literal>lazy</literal> (opcional, por defecto a <literal>true</literal>):
+ Establecer <literal>lazy="false"</literal> deshabilita el uso de recuperación
+ perezosa.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ No se requiere columna o columna clave discriminadora para esta estrategia de mapeo.
+ </para>
+
+ <para>
+ Para información acerca de mapeos de herencia, ver <xref linkend="inheritance"/>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-join" revision="3">
+ <title>join</title>
+
+ <para>
+ Usando el elemento <literal><join></literal>, es posible mapear
+ propiedades de una clase a varias tablas.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="join1" coords="2 50"/>
+ <area id="join2" coords="3 50"/>
+ <area id="join3" coords="4 50"/>
+ <area id="join4" coords="5 50"/>
+ <area id="join5" coords="6 50"/>
+ <area id="join6" coords="7 50"/>
+ </areaspec>
+ <programlisting><![CDATA[<join
+ table="tablename"
+ schema="owner"
+ catalog="catalog"
+ fetch="join|select"
+ inverse="true|false"
+ optional="true|false">
+
+ <key ... />
+
+ <property ... />
+ ...
+</join>]]></programlisting>
+
+ <calloutlist>
+ <callout arearefs="join1">
+ <para>
+ <literal>table</literal>: El nombre de la clase unida.
+ </para>
+ </callout>
+ <callout arearefs="join2">
+ <para>
+ <literal>schema</literal> (opcional): Sobrescribe el nombre de esquema
+ especificado por el elemento raíz <literal><hibernate-mapping></literal>.
+ </para>
+ </callout>
+ <callout arearefs="join3">
+ <para>
+ <literal>catalog</literal> (opcional): Sobrescribe el nombre de catálogo
+ especificado por el elemento raíz <literal><hibernate-mapping></literal>.
+ </para>
+ </callout>
+ <callout arearefs="join4">
+ <para>
+ <literal>fetch</literal> (opcional - por defecto a <literal>join</literal>):
+ Si se establece a <literal>join</literal>, por defecto, Hibernate usará
+ una unión interior (inner join) para recuperar un <literal><join></literal>
+ definido por una clase o sus superclases y una unión externa (outer join)
+ para un <literal><join></literal> definido por una subclase.
+ Si se establece a <literal>select</literal>, entonces Hibernate usará una
+ select secuencial para un <literal><join></literal> definido en una subclase,
+ que será publicada sólo si una fila resulta representar una instancia
+ de la subclase. Las uniones interiores todavía serán usados para
+ recuperar un <literal><join></literal> definido por la clase y sus superclases.
+ </para>
+ </callout>
+ <callout arearefs="join5">
+ <para>
+ <literal>inverse</literal> (opcional - por defecto a <literal>false</literal>):
+ De habilitarse, Hibernate no intentará insertar o actualizar las propiedades
+ definidas por esta unión.
+ </para>
+ </callout>
+ <callout arearefs="join6">
+ <para>
+ <literal>optional</literal> (opcional - por defecto a <literal>false</literal>):
+ De habilitarse, Hibernate insertará una fila sólo si las propiedades
+ definidas por esta unión son no nulas y siempre usará una unión
+ externa para recuperar las propiedades.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Por ejemplo, la información domiciliaria de una persona puede ser mapeada
+ a una tabla separada (preservando a la vez la semántica de tipo de valor para
+ todas las propiedades):
+ </para>
+
+ <programlisting><![CDATA[<class name="Person"
+ table="PERSON">
+
+ <id name="id" column="PERSON_ID">...</id>
+
+ <join table="ADDRESS">
+ <key column="ADDRESS_ID"/>
+ <property name="address"/>
+ <property name="zip"/>
+ <property name="country"/>
+ </join>
+ ...]]></programlisting>
+
+ <para>
+ Esta funcionalidad es a menudo solamente útil para modelos de datos
+ heredados; recomendamos menos tablas que clases un modelo de dominio más
+ granularizado. Sin embargo, es útil para cambiar entre estrategias de mapeo
+ de herencias en una misma jerarquía, como se explica luego.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-key">
+ <title>key</title>
+
+ <para>
+ Hasta ahora hemos visto el elemento <literal><key></literal> pocas veces.
+ Aparece en cualquier sitio en que el elemento padre de mapeo defina una unión
+ a una nueva tabla, y define la clave foránea en la tabla unida,
+ que referencia la clave primaria de la tabla original.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="key1" coords="2 50"/>
+ <area id="key2" coords="3 50"/>
+ <area id="key3" coords="4 50"/>
+ <area id="key4" coords="5 50"/>
+ <area id="key5" coords="6 50"/>
+ <area id="key6" coords="7 50"/>
+ </areaspec>
+ <programlisting><![CDATA[<key
+ column="columnname"
+ on-delete="noaction|cascade"
+ property-ref="propertyName"
+ not-null="true|false"
+ update="true|false"
+ unique="true|false"
+/>]]></programlisting>
+
+ <calloutlist>
+ <callout arearefs="key1">
+ <para>
+ <literal>column</literal> (opcional): El nombre de columna de la clave foránea.
+ Puede ser también especificado por elemento(s) anidado(s)
+ <literal><column></literal>.
+ </para>
+ </callout>
+ <callout arearefs="key2">
+ <para>
+ <literal>on-delete</literal> (opcional, por defecto a <literal>noaction</literal>):
+ Especifica si la restricción de clave foránea tiene el borrado en cascada
+ habilitado a nivel de base de datos.
+ </para>
+ </callout>
+ <callout arearefs="key3">
+ <para>
+ <literal>property-ref</literal> (opcional): Especifica que la clave foránea
+ referencia columnas que no son del la clave primaria de la tabla original.
+ (Provisto para datos heredados.)
+ </para>
+ </callout>
+ <callout arearefs="key4">
+ <para>
+ <literal>not-null</literal> (opcional): Especifica que las columnas de la clave
+ foránea son no nulables (esto está implicado si la clave foránea
+ es también parte de la clave primaria).
+ </para>
+ </callout>
+ <callout arearefs="key5">
+ <para>
+ <literal>update</literal> (opcional): Especifica que la clave foránea nunca
+ debe ser actualizada (esto está implicado si la clave foránea
+ es también parte de la clave primaria).
+ </para>
+ </callout>
+ <callout arearefs="key6">
+ <para>
+ <literal>unique</literal> (opcional): Especifica que la clave foránea
+ debe tener una restricción de unicidad (esto está implicado si
+ la clave foránea es también la clave primaria).
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Recomendamos que, para los sistemas en donde el rendimiento sea importante, todas las
+ claves deben ser definidas <literal>on-delete="cascade"</literal>, e Hibernate usará
+ una restricción <literal>ON CASCADE DELETE</literal> a nivel de base de datos,
+ en vez de muchas sentencias <literal>DELETE</literal> individuales. Ten en cuenta que
+ esta funcionalidad se salta la habitual estrategia de bloqueo optimista de Hibernate para
+ datos versionados.
+ </para>
+
+ <para>
+ Los atributos <literal>not-null</literal> y <literal>update</literal> son útiles
+ al mapear una asociación uno a muchos unidireccional. Si mapeas una uno a muchos
+ unidireccional a una clave foránea no nulable, <emphasis>debes</emphasis> declarar
+ la columna clave usando <literal><key not-null="true"></literal>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-column" revision="3">
+ <title>los elementos column y formula</title>
+ <para>
+ Cualquier elemento de mapeo que acepte un atributo <literal>column</literal> aceptará
+ alternativamente un subelemento <literal><column></literal>. De forma similar,
+ <literal><formula></literal> es una alternativa al atributo <literal>formula</literal>.
+ </para>
+
+ <programlisting><![CDATA[<column
+ name="column_name"
+ length="N"
+ precision="N"
+ scale="N"
+ not-null="true|false"
+ unique="true|false"
+ unique-key="multicolumn_unique_key_name"
+ index="index_name"
+ sql-type="sql_type_name"
+ check="SQL expression"/>]]></programlisting>
+
+ <programlisting><![CDATA[<formula>expresión SQL</formula>]]></programlisting>
+
+ <para>
+ Los atributos <literal>column</literal> y <literal>formula</literal> pueden
+ incluso ser combinados dentro del mismo mapeo de propiedad o asociación para
+ expresar, por ejemplo, condiciones de unión exóticas.
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="homeAddress" class="Address"
+ insert="false" update="false">
+ <column name="person_id" not-null="true" length="10"/>
+ <formula>'MAILING'</formula>
+</many-to-one>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-import">
+ <title>import</title>
+
+ <para>
+ Supón que tu aplicación tiene dos clases persistentes con el mismo nombre,
+ y no quieres especificar el nombre completamenta cualificado (paquete) en las consultas
+ Hibernate. Las clases pueden ser "importadas" explícitamente, en vez de confiar en
+ <literal>auto-import="true"</literal>. Puedes incluso importar clases e interfaces que
+ no estén mapeadas explícitamente.
+ </para>
+
+ <programlisting><![CDATA[<import class="java.lang.Object" rename="Universe"/>]]></programlisting>
+
+ <programlistingco>
+ <areaspec>
+ <area id="import1" coords="2 40"/>
+ <area id="import2" coords="3 40"/>
+ </areaspec>
+ <programlisting><![CDATA[<import
+ class="ClassName"
+ rename="ShortName"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="import1">
+ <para>
+ <literal>class</literal>: El nombre de clase completamente cualificado de
+ cualquier clase Java.
+ </para>
+ </callout>
+ <callout arearefs="import2">
+ <para>
+ <literal>rename</literal> (opcional - por defecto al nombre de clase sin cualificar):
+ Un nombre que será usado en el leguaje de consulta.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ </sect2>
+
+ <sect2 id="mapping-types-anymapping" revision="2">
+ <title>any</title>
+
+ <para>
+ Hay un tipo más de mapeo de propiedad. El elemento de mapeo <literal><any></literal>
+ define una asociacián polimórfica a clases desde múltiples tablas. Este tipo
+ de mapeo siempre requiere más de una columna. La primera columna contiene el tipo de la
+ entidad asociada. Las columnas restantes contienen el identificador. Es imposible especificar una
+ restricción de clave foránea para este tipo de asociación, por lo que
+ esto ciertamente no está concebido como la forma habitual de mapear asociaciones
+ (polimórficas). Sólo debes usar esto en casos muy especiales (por ejemplo,
+ trazas de auditoréa, datos de sesión de usuario, etc).
+ </para>
+
+ <para>
+ El atributo <literal>meta-type</literal> permite a la aplicación especificar un tipo
+ personalizado que mapee columnas de base de datos a clases persistentes que tengan propiedades
+ identificadoras del tipo especificado por <literal>id-type</literal>. Debes especificar el
+ mapeo de valores del meta-type a nombres de clase.
+ </para>
+
+ <programlisting><![CDATA[<any name="being" id-type="long" meta-type="string">
+ <meta-value value="TBL_ANIMAL" class="Animal"/>
+ <meta-value value="TBL_HUMAN" class="Human"/>
+ <meta-value value="TBL_ALIEN" class="Alien"/>
+ <column name="table_name"/>
+ <column name="id"/>
+</any>]]></programlisting>
+
+ <programlistingco>
+ <areaspec>
+ <area id="any1" coords="2 50"/>
+ <area id="any2" coords="3 50"/>
+ <area id="any3" coords="4 50"/>
+ <area id="any4" coords="5 50"/>
+ <area id="any5" coords="6 50"/>
+ <area id="any6" coords="7 50"/>
+ </areaspec>
+ <programlisting><![CDATA[<any
+ name="propertyName"
+ id-type="idtypename"
+ meta-type="metatypename"
+ cascade="cascade_style"
+ access="field|property|ClassName"
+ optimistic-lock="true|false"
+>
+ <meta-value ... />
+ <meta-value ... />
+ .....
+ <column .... />
+ <column .... />
+ .....
+</any>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="any1">
+ <para>
+ <literal>name</literal>: el nombre de la propiedad.
+ </para>
+ </callout>
+ <callout arearefs="any2">
+ <para>
+ <literal>id-type</literal>: el tipo del identificador.
+ </para>
+ </callout>
+ <callout arearefs="any3">
+ <para>
+ <literal>meta-type</literal> (opcional - por defecto a <literal>string</literal>):
+ Cualquier tipo que sea permitido para un mapeo de discriminador.
+ </para>
+ </callout>
+ <callout arearefs="any4">
+ <para>
+ <literal>cascade</literal> (opcional- por defecto a <literal>none</literal>):
+ el estilo de cascada.
+ </para>
+ </callout>
+ <callout arearefs="any5">
+ <para>
+ <literal>access</literal> (opcional - por defecto a <literal>property</literal>): La
+ estrategia que Hibernate debe usar para acceder al valor de la propiedad.
+ </para>
+ </callout>
+ <callout arearefs="any6">
+ <para>
+ <literal>optimistic-lock</literal> (opcional - por defecto a <literal>true</literal>):
+ Especifica si las actualizaciones de esta propiedad requieren o no de la
+ adquisición del bloqueo optimista. En otras palabras, determina si debe ocurrir
+ un incremento de versión cuando esta propiedad está desactualizada.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="mapping-types">
+ <title>Tipos de Hibernate</title>
+
+ <sect2 id="mapping-types-entitiesvalues" revision="1">
+ <title>Entidades y Valores</title>
+
+ <para>
+ Para entender el comportamiento de varios objetos a nivel de lenguaje Java
+ con respecto al servicio de persistencia, necesitamos clasificarlos en dos grupos:
+ </para>
+
+ <para>
+ Una <emphasis>entidad</emphasis> existe independientemente de cualquier otros
+ objetos que referencien a la entidad. Contrasta esto con el model habitual de Java
+ donde un objeto desreferenciado es recolectado como basura. Las entidades deben ser
+ salvadas y borradas explícitamente (excepto que las grabaciones y borrados
+ puedan ser <emphasis>tratados en cascada</emphasis> desde una entidad padre a sus hijos).
+ Esto es diferente al modelo de persistencia de objetos por alcance - y se corresponde
+ más de cerca a cómo los objetos de aplicación son usados
+ habitualmente en grandes sistemas. Las entidades soportan referencias circulares y
+ compartidas, que tambié pueden ser versionadas.
+ </para>
+
+ <para>
+ El estado persistente de una entidad consiste en referencias a otras entidades
+ e instancias de tipo <emphasis>valor</emphasis>. Los valores son primitivos,
+ colecciones (no lo que está dentro de la colección), componentes
+ y ciertos objetos inmutables. A diferencia de las entidades, los valores
+ (en particular las colecciones y los componentes) <emphasis>son</emphasis>
+ hechos persitentes y borrados por alcance. Como los objetos valor (y primitivos)
+ son persistidos y borrados junto a sus entidades contenedoras, no pueden ser
+ versionados independientemente. Los valores no tienen identidad independiente,
+ por los que no pueden ser compartidos por dos entidades o colleciones.
+ </para>
+
+ <para>
+ Hasta ahora, hemos estado usando el término "clase persistente"
+ para referirnos a entidades. Continuaremos haciéndolo. Hablando
+ estrictamente, sin embargo, no todas la clases con estado persistente
+ definidas por el usuario son entidades. Un <emphasis>componente</emphasis>
+ es una clase definida por el usuario con semántica de valor.
+ Una propiedad Java de tipo <literal>java.lang.String</literal> también
+ tiene semántica de valor. Dada esta definición, podemos decir
+ que todos los tipo (clases) provistos por el JDK tienen una semántica
+ de tipo valor en Java, mientras que los tipos definidos por el usuario
+ pueden ser mapeados con semántica de tipo valor o de entidad.
+ La desición corre por cuenta del desarrollador de la aplicación.
+ Un buen consejo para una clase entidad en un modelo de dominio son las referencias
+ compartidas a una sola instancia de esa clase, mientras que la composición
+ o agregación usualmente se traducen a un tipo de valor.
+ </para>
+
+ <para>
+ Volveremos a visitar ambos conceptos a lo largo de la documentación.
+ </para>
+
+ <para>
+ EL desafío es mapear el sistema de tipos de Java (y la definición
+ de entidades y tipos de valor de los desarrolladores) al sistema de tipos de
+ SQL/base de datos. EL puente entre ambos sistemas es provisto por Hibernate:
+ para las entidades usamos <literal><class></literal>,
+ <literal><subclass></literal>, etc. Para los tipos de valor usamos
+ <literal><property></literal>, <literal><component></literal>, etc,
+ usualmente con un atributo <literal>type</literal>. El valor de este atributo
+ es el nombre de un <emphasis>tipo de mapeo</emphasis> de Hibernate. Hibernate
+ provee de fábrica muchos mapeos (para tipos de valores del JDK
+ estándar). Puedes escribir tus propios mapeos de tipo, así como
+ implementar tus estrategias de conversión personalizadas, como veremos luego.
+ </para>
+
+ <para>
+ Todos los tipos prefabricados de Hibernate soportan semántica de nulos
+ excepto las colecciones.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-types-basictypes" revision="2">
+ <title>Tipos de valores básicos</title>
+
+ <para>
+ Los <emphasis>tipos de mapeo básicos</emphasis> prefabricados pueden ser
+ categorizado a grandes rasgos en:
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>integer, long, short, float, double, character, byte,
+ boolean, yes_no, true_false</literal></term>
+ <listitem>
+ <para>
+ Mapeos de tipos primitivos de Java o clases de envoltura a
+ la tipos de columna SQL (especícifica del vendedor).
+ <literal>boolean, yes_no</literal> y <literal>true_false</literal>
+ son codificaciones alternativas a <literal>boolean</literal> de
+ Java o <literal>java.lang.Boolean</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>string</literal></term>
+ <listitem>
+ <para>
+ Un mapeo del tipo <literal>java.lang.String</literal> a
+ <literal>VARCHAR</literal> (u Oracle <literal>VAARCHAR2</literal>).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>date, time, timestamp</literal></term>
+ <listitem>
+ <para>
+ Mapeos de tipo desde <literal>java.util.Date</literal> y sus subclases
+ a tipos SQL <literal>DATE</literal>, <literal>TIME</literal> y
+ <literal>TIMESTAMP</literal> (o equivalente).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>calendar, calendar_date</literal></term>
+ <listitem>
+ <para>
+ Mapeos de tipo desde <literal>java.util.Date</literal> y sus subclases
+ a tipos SQL <literal>TIMESTAMP</literal> y <literal>DATE</literal>
+ (o equivalente).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>big_decimal, big_integer</literal></term>
+ <listitem>
+ <para>
+ Mapeos de tipo desde <literal>java.math.BigDecimal</literal> y
+ <literal>java.math.BigInteger</literal> a <literal>NUMERIC</literal>
+ (o <literal>NUMBER</literal> de Oracle).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>locale, timezone, currency</literal></term>
+ <listitem>
+ <para>
+ Mapeos de tipo desde <literal>java.util.Locale</literal>,
+ <literal>java.util.TimeZone</literal> y
+ <literal>java.util.Currency</literal> a
+ <literal>VARCHAR</literal> (o <literal>VARCHAR2</literal> de Oracle).
+ Las instancias de <literal>Locale</literal> y <literal>Currency</literal>
+ son mapeadas a sus códigos ISO. Las instancias de
+ <literal>TimeZone</literal> son mapeadas a sus <literal>ID</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>class</literal></term>
+ <listitem>
+ <para>
+ Un mapeo de tipo <literal>java.lang.Class</literal> a
+ <literal>VARCHAR</literal> (o <literal>VARCHAR2</literal> de Oracle).
+ Una <literal>Class</literal> es mapeara a su nombre completamente
+ cualificado.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>binary</literal></term>
+ <listitem>
+ <para>
+ Mapea arreglos de bytes a un tipo binario SQL apropiado.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>text</literal></term>
+ <listitem>
+ <para>
+ Mapea cadenas largas Java al tipo SQL <literal>CLOB</literal> o
+ <literal>TEXT</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>serializable</literal></term>
+ <listitem>
+ <para>
+ Mapea tipos serializables Java a un tipo binario SQL apropiado.
+ Puedes además indicar el tipo <literal>serializable</literal>
+ de Hibernate con el nombre de una clase o interface serializable Java
+ que no sea por defecto un tipo básico.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>clob, blob</literal></term>
+ <listitem>
+ <para>
+ Mapeos de tipo para las clases JDBC <literal>java.sql.Clob</literal> y
+ <literal>java.sql.Blob</literal>. Estos tipos pueden ser inconvenientes
+ para algunas aplicaciones, pues el objeto blob o clob no puede ser reusado
+ fuera de una transacción (Además, el soporte del driver suele
+ ser malo e inconsistente).
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ </para>
+
+ <para>
+ Los identificadores únicos de entidades y collecciones pueden ser de cualquier
+ tipo básico excepto <literal>binary</literal>, <literal>blob</literal>
+ y <literal>clob</literal>.
+ (Los identificadores compuestos están también permitidos, ver debajo.)
+ </para>
+
+ <para>
+ Los tipos de valor básicos tienen sus constantes <literal>Type</literal>
+ correspondientes definidas en <literal>org.hibernate.Hibernate</literal>. Por ejemplo,
+ <literal>Hibernate.STRING</literal> representa el tipo <literal>string</literal>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-types-custom" revision="2">
+ <title>Tipos de valor personalizados</title>
+
+ <para>
+ Es relativamente fácil para los desarrolladores crear sus propios tipos de valor.
+ Por ejemplo, podrías querer persistir propiedades del tipo <literal>java.lang.BigInteger</literal>
+ a columnas <literal>VARCHAR</literal>. Hibernate no provee un tipo de fábrica para esto.
+ Pero los tipos personalizados no están limitados a mapear una propiedad (o elemento de colección)
+ a una sola columna de tabla. Así, por ejemplo, podrías tener una propiedad Java
+ <literal>getName()</literal>/<literal>setName()</literal> de tipo <literal>java.lang.String</literal>
+ que fuera persistida a las columnas <literal>FIRST_NAME</literal>, <literal>INITIAL</literal>,
+ <literal>SURNAME</literal>.
+ </para>
+
+ <para>
+ Para implementar un tipo personalizado, implementa bien <literal>org.hibernate.UserType</literal>
+ o <literal>org.hibernate.CompositeUserType</literal> y declara las propiedades usando el nombre
+ de clase completamente cualificado del tipo. Revisa <literal>org.hibernate.test.DoubleStringType</literal>
+ para ver qué tipo de cosas son posibles.
+ </para>
+
+ <programlisting><![CDATA[<property name="twoStrings" type="org.hibernate.test.DoubleStringType">
+ <column name="first_string"/>
+ <column name="second_string"/>
+</property>]]></programlisting>
+
+ <para>
+ Observa el uso de etiquetas <literal><column></literal> para mapear una propiedad
+ a múltiples columnas.
+ </para>
+
+ <para>
+ Las interfaces <literal>CompositeUserType</literal>, <literal>EnhancedUserType</literal>,
+ <literal>UserCollectionType</literal>, y <literal>UserVersionType</literal> proveen
+ soporte a usos más especializados.
+ </para>
+
+ <para>
+ Puedes incluso proveer de parámetros a un <literal>UserType</literal> en el
+ fichero de mapeo. Para hacer esto, tu <literal>UserType</literal> debe implementar
+ la interface <literal>org.hibernate.usertype.ParameterizedType</literal>. Para
+ proveer de parámetros a tu tipo personalizado, puedes usar el elemento
+ <literal><type></literal> en tus ficheros de mapeo.
+ </para>
+
+ <programlisting><![CDATA[<property name="priority">
+ <type name="com.mycompany.usertypes.DefaultValueIntegerType">
+ <param name="default">0</param>
+ </type>
+</property>]]></programlisting>
+
+ <para>
+ Ahora el <literal>UserType</literal> puede recuperar el valor del parámetro
+ llamado <literal>default</literal> del objeto <literal>Properties</literal>
+ que se le pasa.
+ </para>
+
+ <para>
+ Si usas cierto <literal>UserType</literal> muy frecuentemente, puede ser útil
+ definir un nombre corto para é. Puedes hacer esto usando el elemento
+ <literal><typedef></literal>. Los typedefs asignan un nombre a un tipo
+ personalizado, y pueden también contener una lista de valores por defecto
+ de parámetros si el tipo fuese parametrizado.
+ </para>
+
+ <programlisting><![CDATA[<typedef class="com.mycompany.usertypes.DefaultValueIntegerType" name="default_zero">
+ <param name="default">0</param>
+</typedef>]]></programlisting>
+
+ <programlisting><![CDATA[<property name="priority" type="default_zero"/>]]></programlisting>
+
+ <para>
+ también es posible sobrescribir los parámetros provistos en un typedef sobre
+ una base caso por caso usando parámetros de tipo en el mapeo de la propiedad.
+ </para>
+
+ <para>
+ Aunque el rico espectro de tipos prefabricados y soporte de componentes de Hibernate
+ significa que raramente <emphasis>necesites</emphasis> usar un tipo personalizado;
+ sin embargo se considera una buena forma usar tipos personalizados para clases (no-entidades)
+ que aparezcan frecuentemente en tu aplicación. Por ejemplo, una clase
+ <literal>MonetaryAmount</literal> es una buena candidata para un
+ <literal>CompositeUserType</literal>, incluso cuando puede ser facilmente mapeada como un
+ componente. Un motivo para esto es la abstracción. Con un tipo personalizado,
+ tus documentos de mapeo estará impermeabilizados contra posibles cambios futuros en la
+ forma de representar valores monetarios.
+ </para>
+ </sect2>
+
+ </sect1>
+
+
+ <sect1 id="mapping-entityname">
+ <title>Mapeando una clase más de una vez</title>
+ <para>
+ Es posible proveer más de un mapeo para una clase persistente en particular. En este caso debes
+ especificar un <emphasis>nombre de entidad</emphasis> para desambiguar entr las instancias de las
+ dos entidades mapeadas. (Por defectom, el nombre de la entidad es el mismo que el nombre de la clase.)
+ Hibernate te deja especificar el nombre de entidad al trabajar con objetos persistentes, al escribir
+ consultas, o al mapear asociaciones a la entidad mencionada.
+ </para>
+
+ <programlisting><![CDATA[<class name="Contract" table="Contracts"
+ entity-name="CurrentContract">
+ ...
+ <set name="history" inverse="true"
+ order-by="effectiveEndDate desc">
+ <key column="currentContractId"/>
+ <one-to-many entity-name="HistoricalContract"/>
+ </set>
+</class>
+
+<class name="Contract" table="ContractHistory"
+ entity-name="HistoricalContract">
+ ...
+ <many-to-one name="currentContract"
+ column="currentContractId"
+ entity-name="CurrentContract"/>
+</class>]]></programlisting>
+
+ <para>
+ Observa cómo las asociaciones ahora se especifican usando <literal>entity-name</literal> en vez de
+ <literal>class</literal>.
+ </para>
+
+ </sect1>
+
+
+ <sect1 id="mapping-quotedidentifiers">
+ <title>identificadores SQL encomillados</title>
+ <para>
+ Puedes forzar a Hibernate a encomillar un identificador en el SQL generado encerrando el nombre
+ de tabla o columna entre backticks en el documento de mapeo. Hibernate usará el estilo de
+ encomillado para el <literal>Dialect</literal> SQL (usualmente comillas dobles, excepto corchetes
+ para SQL Server y backsticks para MySQL).
+ </para>
+
+ <programlisting><![CDATA[<class name="LineItem" table="`Line Item`">
+ <id name="id" column="`Item Id`"/><generator class="assigned"/></id>
+ <property name="itemNumber" column="`Item #`"/>
+ ...
+</class>]]></programlisting>
+
+ </sect1>
+
+
+ <sect1 id="mapping-alternatives">
+ <title>Alternativas de metadatos</title>
+
+ <para>
+ XML no es para todos, asá que hay algunas formas alternativas de definir metadatos de mapeo O/R
+ en Hibernate.
+ </para>
+
+ <sect2 id="mapping-xdoclet">
+ <title>Usando marcado de XDoclet</title>
+
+ <para>
+ Muchos usuarios de Hibernate prefieren embeber la información de mapeo directamente
+ en el código fuente usando las <literal>@hibernate.etiquetas</literal> XDoclet.
+ No cubriremos este enfoque en este documento, pues estrictamente es considerado parte
+ de XDoclet. Sin embargo, incluímos el siguiente ejemplo de la clase
+ <literal>Cat</literal> con mapeos XDoclet.
+ </para>
+
+ <programlisting><![CDATA[package eg;
+import java.util.Set;
+import java.util.Date;
+
+/**
+ * @hibernate.class
+ * table="CATS"
+ */
+public class Cat {
+ private Long id; // identifier
+ private Date birthdate;
+ private Cat mother;
+ private Set kittens
+ private Color color;
+ private char sex;
+ private float weight;
+
+ /*
+ * @hibernate.id
+ * generator-class="native"
+ * column="CAT_ID"
+ */
+ public Long getId() {
+ return id;
+ }
+ private void setId(Long id) {
+ this.id=id;
+ }
+
+ /**
+ * @hibernate.many-to-one
+ * column="PARENT_ID"
+ */
+ public Cat getMother() {
+ return mother;
+ }
+ void setMother(Cat mother) {
+ this.mother = mother;
+ }
+
+ /**
+ * @hibernate.property
+ * column="BIRTH_DATE"
+ */
+ public Date getBirthdate() {
+ return birthdate;
+ }
+ void setBirthdate(Date date) {
+ birthdate = date;
+ }
+ /**
+ * @hibernate.property
+ * column="WEIGHT"
+ */
+ public float getWeight() {
+ return weight;
+ }
+ void setWeight(float weight) {
+ this.weight = weight;
+ }
+
+ /**
+ * @hibernate.property
+ * column="COLOR"
+ * not-null="true"
+ */
+ public Color getColor() {
+ return color;
+ }
+ void setColor(Color color) {
+ this.color = color;
+ }
+ /**
+ * @hibernate.set
+ * inverse="true"
+ * order-by="BIRTH_DATE"
+ * @hibernate.collection-key
+ * column="PARENT_ID"
+ * @hibernate.collection-one-to-many
+ */
+ public Set getKittens() {
+ return kittens;
+ }
+ void setKittens(Set kittens) {
+ this.kittens = kittens;
+ }
+ // addKitten not needed by Hibernate
+ public void addKitten(Cat kitten) {
+ kittens.add(kitten);
+ }
+
+ /**
+ * @hibernate.property
+ * column="SEX"
+ * not-null="true"
+ * update="false"
+ */
+ public char getSex() {
+ return sex;
+ }
+ void setSex(char sex) {
+ this.sex=sex;
+ }
+}]]></programlisting>
+
+ <para>
+ Para más ejemplos de XDoclet e Hibernate ver en el sitio web de Hibernate.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-annotations" revision="2">
+ <title>Usando anotaciones JDK 5.0</title>
+ <para>
+ El JDK 5.0 introdujo anotaciones del estilo XDoclet a nivel del lenguaje,
+ con chequeo seguro de tipos en tiempo de compilación. Este mecanismo es más
+ potente y que las anotaciones XDoclet, y mejor soportado por herramientas e IDEs.
+ IntelliJ IDEA, por ejemplo, soporta auto-compleción y resaltado de sintaxis de anotaciones
+ JDK 5.0. La nueva revisión de la especificación de EJB (JSR-220) usa anotaciones
+ JDK 5.0 como el mecanismo primario de metadatos para beans de entidad. Hibernate3 implementa
+ el <literal>EntityManager</literal> del JSR-220 (la API de persistencia), y el soporte para
+ metadatos de mapeo está disponible vía el paquete <emphasis>Hibernate Annotations</emphasis>,
+ como una descarga por separado. Tanto metadatos de EJB3 (JSR-220) como de Hibernate3 están soportados.
+ </para>
+
+ <para>
+ Este es un ejemplo de una clase POJO anotada como un bean de entidad EJB:
+ </para>
+
+ <programlisting><![CDATA[@Entity(access = AccessType.FIELD)
+public class Customer implements Serializable {
+
+ @Id;
+ Long id;
+
+ String firstName;
+ String lastName;
+ Date birthday;
+
+ @Transient
+ Integer age;
+
+ @Embedded
+ private Address homeAddress;
+
+ @OneToMany(cascade=CascadeType.ALL)
+
+ @JoinColumn(name="CUSTOMER_ID")
+ Set<Order> orders;
+
+ // Getter/setter and business methods
+}]]></programlisting>
+
+ <para>
+ Ten en cuenta que el soporte a anotaciones JDK 5.0 (y JSR-220) es todavía un
+ trabajo en progreso y no completado. Por favor, para más detalles refiérete al modulo
+ de Anotaciones de Hibernate.
+ </para>
+
+ </sect2>
+ </sect1>
+
+</chapter>
+
+
+
+
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/batch.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/batch.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/batch.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,192 @@
+<chapter id="batch">
+ <title>Procesamiento por lotes</title>
+
+ <para>
+ Un enfoque ingenuo para insertar 100.000 filas en la base de datos usando Hibernate podrÃa verse asÃ:
+ </para>
+
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+for ( int i=0; i<100000; i++ ) {
+ Customer customer = new Customer(.....);
+ session.save(customer);
+}
+tx.commit();
+session.close();]]></programlisting>
+
+ <para>
+ Esto podrÃa caer sobre una <literal>OutOfMemoryException</literal> en algún sitio
+ cerca de la fila 50.000. Esto es porque Hibernate tiene en caché todas las instancias
+ de <literal>Customer</literal> recién instanciadas en el caché de nivel de sesión.
+ </para>
+
+ <para>
+ En este capÃtulo te mostraremos cómo evitar este problema. Primero, sin embargo,
+ si estás haciendo procesamiento por lotes (batch processing), es absolutamente crÃtico
+ que habilites el uso de loteo JDBC, si pretendes lograr un rendimiento razonable.
+ Establece el tamaño de lote JDBC a un número razonable (digamos 10-50):
+ </para>
+
+<programlisting><![CDATA[hibernate.jdbc.batch_size 20]]></programlisting>
+
+ <para>
+ PodrÃas además querer hacer este tipo de trabajo en un proceso donde la interacción con el caché de
+ segundo nivel esté completamente deshabilitado:
+ </para>
+
+<programlisting><![CDATA[hibernate.cache.use_second_level_cache false]]></programlisting>
+
+ <sect1 id="batch-inserts">
+ <title>Inserciones en lote</title>
+
+ <para>
+ Al hacer persistentes objetos nuevos, debes limpiar con <literal>flush()</literal> y
+ llamar a <literal>clear()</literal> en la sesión regularmente, para controlar el tamaño
+ del caché de primer nivel.
+ </para>
+
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+
+for ( int i=0; i<100000; i++ ) {
+ Customer customer = new Customer(.....);
+ session.save(customer);
+ if ( i % 20 == 0 ) { //20, same as the JDBC batch size
+ //flush a batch of inserts and release memory:
+ session.flush();
+ session.clear();
+ }
+}
+
+tx.commit();
+session.close();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="batch-update" >
+ <title>Actualizaciones en lote</title>
+
+ <para>
+ Para recuperar y actualizar datos se aplican las mismas ideas. Adicionalmente, necesitas usar
+ <literal>scroll()</literal> para sacar ventaja de los cursores del lado del servidor en consultas
+ que devuelvan muchas filas de datos.
+ </para>
+
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+
+ScrollableResults customers = session.getNamedQuery("GetCustomers")
+ .setCacheMode(CacheMode.IGNORE)
+ .scroll(ScrollMode.FORWARD_ONLY);
+int count=0;
+while ( customers.next() ) {
+ Customer customer = (Customer) customers.get(0);
+ customer.updateStuff(...);
+ if ( ++count % 20 == 0 ) {
+ //flush a batch of updates and release memory:
+ session.flush();
+ session.clear();
+ }
+}
+
+tx.commit();
+session.close();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="batch-direct">
+ <title>update/delete en masa</title>
+
+ <para>
+ Como ya se ha discutido, el mapeo objeto/relacional automático y transparente se refiere
+ al manejo de estado de objetos. Esto implica que el estado del objeto está disponible
+ en memoria, por lo tanto actualizar o borrar (usando <literal>UPDATE</literal> y
+ <literal>DELETE</literal> de SQL) datos directamente en la base de datos no afectará el
+ estado en memoria. Sin embargo, Hibernate provee métodos para la ejecución de sentencias
+ del estilo de <literal>UPDATE</literal> y <literal>DELETE</literal> de SQL que se realizan
+ a través del Lenguaje de Consulta de Hibernate (Hibernate Query Language o
+ <xref linkend="queryhql">HQL</xref>).
+ </para>
+
+ <para>
+ La pseudo-sintáxis para sentencias <literal>UPDATE</literal> y <literal>DELETE</literal> es:
+ <literal>( UPDATE | DELETE ) FROM? ClassName (WHERE WHERE_CONDITIONS)?</literal>. Algunos puntos
+ a tener en cuenta:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ En la cláusula-from, la palabra clave FROM es opcional
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Puede haber sólo una clase mencionada en la cláusula-from, y <emphasis>no puede</emphasis>
+ tener un alias.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ No puede especificarse ningún join (bien implÃcito o explÃcito) en una consulta masiva de HQL.
+ Pueden usarse subconsultas en la cláusula-where.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ La cláusula-where es también opcional.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Como un ejemplo, para ejecutar un <literal>UPDATE</literal> HQL, usa el
+ método <literal>Query.executeUpdate()</literal>:
+ </para>
+
+ <programlisting><![CDATA[Session session = sessionFactory.openSession();
+ Transaction tx = session.beginTransaction();
+
+ String hqlUpdate = "update Customer set name = :newName where name = :oldName";
+ int updatedEntities = s.createQuery( hqlUpdate )
+ .setString( "newName", newName )
+ .setString( "oldName", oldName )
+ .executeUpdate();
+ tx.commit();
+ session.close();]]></programlisting>
+
+ <para>
+ Para ejecutar un <literal>DELETE</literal> HQL, usa el mismo método <literal>Query.executeUpdate()</literal>
+ (el método está nombrado para aquellos familiarizados con
+ <literal>PreparedStatement.executeUpdate()</literal> de JDBC):
+ </para>
+
+ <programlisting><![CDATA[Session session = sessionFactory.openSession();
+ Transaction tx = session.beginTransaction();
+
+ String hqlDelete = "delete Customer where name = :oldName";
+ int deletedEntities = s.createQuery( hqlDelete )
+ .setString( "oldName", oldName )
+ .executeUpdate();
+ tx.commit();
+ session.close();]]></programlisting>
+
+ <para>
+ El valor <literal>int</literal> devuelto por el método <literal>Query.executeUpdate()</literal>
+ indica el número de entidades afectadas por la operación. Considera que esto puede o no
+ correlacionarse al número de filas afectadas en la base de datos. Una operación masiva HQL podrÃa
+ resultar en que se ejecuten múltiples sentencias de SQL reales, para joined-subclass, por ejemplo.
+ El número devuelto indica el número de entidades reales afectadas por la sentencia. Volviendo al
+ ejemplo de joined-subclass, un borrado contra una de las subclases puede resultar realmente en
+ borrados contra no sólo la tabla a la que está mapeada esa subclase, sino también la tabla "raÃz"
+ y potencialmente tablas de joined-subclass más debajo en la jerarquÃa de herencia.
+ </para>
+
+ <para>
+ Ten en cuenta que existen actualmente unas pocas limitaciones con las operaciones HQL masivas,
+ que serán atendidas en lanzamientos futuros; consulta la hoja de ruta de JIRA para más detalles.
+ </para>
+
+ </sect1>
+
+</chapter>
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/best_practices.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/best_practices.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/best_practices.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,229 @@
+<chapter id="best-practices" revision="3">
+ <title>Mejores Prácticas</title>
+
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term>Escribe clase finamente granularizadas y mapealas usando <literal><component></literal>.</term>
+ <listitem>
+ <para>
+ Usa una clase <literal>Dirección</literal> para encapsular <literal>calle</literal>,
+ <literal>distrito</literal>, <literal>estado</literal>, <literal>código postal</literal>.
+ Esto alienta la reutilización de código y simplifica el refactoring.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Declara las propiedades identificadoras en clases persistentes.</term>
+ <listitem>
+ <para>
+ Hibernate hace opcionales las propiedades identificadoras. Existen todo tipo de razones
+ por las que debes usarlas. Recomendamos que los identificadores sean 'sintéticos'
+ (generados, sin ningún significado de negocio).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Identifica las claves naturales.</term>
+ <listitem>
+ <para>
+ Identifica las claves naturales de todas las entidades, y mapealas usando
+ <literal><natural-id></literal>. Implementa <literal>equals()</literal> y
+ <literal>hashCode()</literal> para comparar las propiedades que componen la clave natural.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Coloca cada mapeo de clase en su propio fichero.</term>
+ <listitem>
+ <para>
+ No uses un solo documento monolítico de mapeo. Mapea <literal>com.eg.Foo</literal> en
+ el fichero <literal>com/eg/Foo.hbm.xml</literal>. Esto tiene sentido particularmente en un
+ ambiente de equipo.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Carga los mapeos como recursos.</term>
+ <listitem>
+ <para>
+ Despliega los mapeos junto a las clases que mapean.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Considera externalizar las cadenas de consulta.</term>
+ <listitem>
+ <para>
+ Esta es una buena práctica si tus consultas llaman a funciones SQL que no son del
+ estándar ANSI. Externalizar las cadenas de consulta a ficheros de mapeo hará la
+ aplicación más portable.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Usa variables de ligado.</term>
+ <listitem>
+ <para>
+ Igual que en JDBC, siempre remplaza valores no constantes con "?". ¡Nunca uses manipulación
+ de cadenas para ligar un valor no constante en una consulta! Incluso mejor, considera usar
+ parámetros con nombre en las consultas.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>No manejes tus propias conexiones JDBC.</term>
+ <listitem>
+ <para>
+ Hibernate deja a la aplicación administre las conexiones JDBC. Este enfoque debe considerarse
+ como último recurso. Si no puedes usar los provedores de conexión prefabricados, considera
+ prover tu propia implementación de <literal>org.hibernate.connection.ConnectionProvider</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Considera usar un tipo personalizado.</term>
+ <listitem>
+ <para>
+ Supón que tienes un tipo Java, digamos de alguna biblioteca, que necesita hacerse persistente
+ pero no provee los métodos de acceso necesarios para mapearlo como un componente. Debes considerar
+ implementar <literal>org.hibernate.UserType</literal>. Este enfoque libera al código de aplicación
+ de implementar transformaciones a / desde un tipo Hibernate.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Usa JDBC codificado a mano en cuellos de botella.</term>
+ <listitem>
+ <para>
+ En áreas del sistema de rendimiento crítico, algunos tipos de operaciones podrían beneficiarse
+ del JDBC directo. Pero por favor, espero hasta que <emphasis>sepas</emphasis> que algo es
+ un cuello de botella. Y no asumas que el JDBC directo es necesariamente más rápido. Si necesitas
+ usar JDBC directo, podría ser valioso abrir una <literal>Session</literal> de Hibernate y usar esa
+ conexión JDBC. De esta forma puedes usar aún la misma estrategia de transacción y el mismo
+ proveedor de conexiones subyacente.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Comprende la limpieza (flushing) de <literal>Session</literal>.</term>
+ <listitem>
+ <para>
+ De vez en cuando la sesión sincroniza su estado persistente con la base de datos. El rendimiento
+ se verá afectado si este proceso ocurre demasiado frecuentemente. A veces puedes minimizar
+ limpieza innecesaria deshabilitando la limpieza automática o incluso cambiando el orden de las
+ consultas u otras operaciones en una transacción en particular.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>En una aplicación en tres gradas, considera usar objetos separados.</term>
+ <listitem>
+ <para>
+ Al usar una arquitectura de servlet / sesión, puedes pasar objetos persistentes en el bean de
+ sesión hacia y desde la capa de servlet / JSP. Usa una sesión nueva para atender el servicio de cada
+ petición. Usa <literal>Session.merge()</literal> o <literal>Session.saveOrUpdate()</literal> para
+ sincronizar los objetos con la base de datos.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>En una arquitectura en dos gradas, considera usar contexto de persistencia largos.</term>
+ <listitem>
+ <para>
+ Las transacciones de base de datos tienen que ser tan cortas como sea posible. Sin embargo,
+ frecuentemente es necesario implementar <emphasis>transacciones de aplicación</emphasis>
+ ejecutándose en largo, una sola unidad de trabajo desde el punto de vista de un usuario.
+ Una transacción de aplicación puede abarcar muchos ciclos petición/respuesta del cliente.
+ Es común usar objetos separados para implementar transacciones de aplicación. Una alternativa,
+ extremadamente apropiada en arquitecturas en dos gradas, es mantener un solo contacto de persistencia
+ abierto (sesión) para todo el ciclo de vida de la transacción de aplicación y simplemente
+ desconectar de la conexión JDBC al final de cada petición, y reconectar al comienzo de la
+ petición subsecuente. Nunca compartas una única sesión a través de más de una transacción
+ de aplicación, o estarás trabajando con datos añejos.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>No trates la excepciones como recuperables.</term>
+ <listitem>
+ <para>
+ Esto es más una práctica necesaria que una "mejor" práctica. Cuando ocurra una excepción,
+ deshaz (rollback) la <literal>Transaction</literal> y cierra la <literal>Session</literal>.
+ Si no lo haces, Hibernate no puede garantizar que el estado en memoria representa con exactitud
+ el estado persistente. Como un caso especial de esto, no uses <literal>Session.load()</literal>
+ para determinar si una instancia con el identificador dado existe en la base de datos. En cambio,
+ usa <literal>Session.get()</literal> o una consulta.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Prefiere la recuperación perezosa para las asociaciones.</term>
+ <listitem>
+ <para>
+ Usa escasamente la recuperación temprana. Usa proxies y colecciones perezosas para la mayoría
+ de asociaciones a clases probablemente no estén mantenidas en el caché de segundo nivel. Para
+ las asociaciones a clases en caché, donde hay una probabilidad de acceso a caché extremadamente
+ alta, deshabilita explícitamente la recuperación temprana usando <literal>lazy="false"</literal>.
+ Cuando sea apropiada la recuperación por unión (join fetching) para un caso de uso en particular,
+ usa una consulta con un <literal>left join fetch</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ Usa el patrón <emphasis>sesión abierta en vista</emphasis>, o una <emphasis>fase de ensamblado</emphasis>
+ disciplinada para evitar problemas con datos no recuperados.
+ </term>
+ <listitem>
+ <para>
+ Hibernate liberal al desarrollador de escribir <emphasis>Objetos de Transferencia de Datos
+ (Data Transfer Objects)</emphasis> (DTO). En una arquitectura tradicional de EJB, los DTOs tienen
+ un propósito doble: primero, atacan el problema que los beans de entidad no son serializables.
+ Segundo, definen implícitamente una fase de ensamblado cuando se recuperan y se forman (marshalling)
+ todos los datos a usar por la vista en los DTOs antes de devolver el control a la grada de
+ presentación. Hibernate elimina el primer propósito. Sin embargo, aún necesitas una fase
+ de ensamblado (piensa en tus métodos de negocio como si tuviesen un contrato estricto con la grada
+ de presentación sobre qué datos están disponibles en los objetos separados) a menos que estés
+ preparado para tener el contexto de persistencia (la sesión) abierto a través del proceso
+ de renderización de la vista. ¡Esta no es una limitación de Hibernate! Es un requerimiento
+ fundamental de acceso seguro a datos transaccionales.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Considera abstraer tu lógica de negocio de Hibernate</term>
+ <listitem>
+ <para>
+ Oculta el código de acceso a datos (Hibernate) detrás de una interface. Combina los patrones
+ <emphasis>DAO</emphasis> y <emphasis>Sesión de Hebra Local</emphasis>. Incluso puedes tener
+ algunas clases hechas persistentes por JDBC escrito a mano, asociadas a Hibernate por medio
+ de un <literal>UserType</literal>. (Este consejo está pensado para aplicaciones "suficientemente
+ grandes"; ¡no es apropiado para una aplicación con cinco tablas!)
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>No uses mapeos de asociación exóticos.</term>
+ <listitem>
+ <para>
+ Son raros los casos de uso de asociaciones reales muchos-a-muchos. La mayor parte del tiempo
+ necesitas información adicional almacenada en una "tabla de enlace". En este caso, es mucho
+ mejor usar dos asociaciones uno-a-muchos a una clase de enlace intermedia. De hecho, pensamos
+ que la mayoría de asociaciones son uno-a-muchos y muchos-a-uno, debes ser cuidadoso al usr
+ cualquier otro estilo de asociación y preguntarte si es realmente necesario.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Prefiere las asociaciones bidireccionales.</term>
+ <listitem>
+ <para>
+ Las asociaciones unidireccionales son más difíciles de consultar. En una aplicación grande,
+ casi todas las asociaciones deben ser navegables en ambas direcciones en consultas.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/collection_mapping.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/collection_mapping.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/collection_mapping.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1241 @@
+<chapter id="collections">
+ <title>Mapeo de Colecciones</title>
+
+ <sect1 id="collections-persistent" revision="3">
+ <title>Colecciones persistentes</title>
+
+ <para>
+ Hibernate requiere que los campos valuados en colección
+ persistentes sean declarados como un tipo de interface, por ejemplo:
+ </para>
+
+ <programlisting><![CDATA[public class Product {
+ private String serialNumber;
+ private Set parts = new HashSet();
+
+ public Set getParts() { return parts; }
+ void setParts(Set parts) { this.parts = parts; }
+ public String getSerialNumber() { return serialNumber; }
+ void setSerialNumber(String sn) { serialNumber = sn; }
+}]]></programlisting>
+
+ <para>
+ La interface real podría ser <literal>java.util.Set</literal>,
+ <literal>java.util.Collection</literal>, <literal>java.util.List</literal>,
+ <literal>java.util.Map</literal>, <literal>java.util.SortedSet</literal>,
+ <literal>java.util.SortedMap</literal> o ... lo que te guste!
+ (Donde "lo que te guste" significa que tendrás que escribir una
+ implementación de <literal>org.hibernate.usertype.UserCollectionType</literal>.)
+ </para>
+
+ <para>
+ Nota cómo hemos inicializado la variable de instancia de
+ <literal>HashSet</literal>. Esta es la mejor forma de inicializar
+ propiedades valuadas en colección de instancias recién
+ instanciadas (no persistentes). Cuando haces persistente la instancia
+ - llamando a <literal>persist()</literal>, por ejemplo - Hibernate realmente
+ remplazará el <literal>HashSet</literal> con una instancia de una
+ implementación de <literal>Set</literal> propia de Hibernate.
+ Observa errores como este:
+ </para>
+
+ <programlisting><![CDATA[Cat cat = new DomesticCat();
+Cat kitten = new DomesticCat();
+....
+Set kittens = new HashSet();
+kittens.add(kitten);
+cat.setKittens(kittens);
+session.persist(cat);
+kittens = cat.getKittens(); // Okay, kittens collection is a Set
+(HashSet) cat.getKittens(); // Error!]]></programlisting>
+
+ <para>
+ Las colecciones persistentes inyectadas por Hibernate se comportan
+ como <literal>HashMap</literal>, <literal>HashSet</literal>,
+ <literal>TreeMap</literal>, <literal>TreeSet</literal> o
+ <literal>ArrayList</literal>, dependiendo del tipo de interface.
+ </para>
+
+ <para>
+ Las instancias de colecciones tienen el comportamiento usual de tipos de valor.
+ Son automáticamente persistidas al ser referenciadas por un objeto
+ persistente y automáticamente borradas al desreferenciarse. Si una
+ colección es pasada de un objeto persistente a otro, sus elementos
+ serían movidos de una tabla a otra. Dos entidades pueden no
+ compartir una referencia a la misma instancia de colección.
+ Debido al modelo relacional subyacente, las propiedades valuadas en
+ colección no soportan la semántica de valor nulo. Hibernate no
+ distingue entre una referencia de colección nula y una colección
+ vacía.
+ </para>
+
+ <para>
+ No debes tener que preocuparte demasiado por esto. Usa las colecciones
+ persistentes de la misma forma en que usas colecciones de Java ordinarias.
+ Sólo asegúrate que entiendes la semántica de las asociaciones
+ bidireccionales (discutida luego).
+ </para>
+
+ </sect1>
+
+ <sect1 id="collections-mapping" revision="2">
+ <title>Mapeos de colección</title>
+
+ <para>
+ El elemento de mapeo de Hibernate usado para mapear una colección
+ depende del tipo de la interface. Por ejemplom un elemento
+ <literal><set></literal> se usa para mapear propiedades de tipo
+ <literal>Set</literal>.
+ </para>
+
+ <programlisting><![CDATA[<class name="Product">
+ <id name="serialNumber" column="productSerialNumber"/>
+ <set name="parts">
+ <key column="productSerialNumber" not-null="true"/>
+ <one-to-many class="Part"/>
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ Aparte de <literal><set></literal>, existen además
+ los elementos de mapeo <literal><list></literal>,
+ <literal><map></literal>, <literal><bag></literal>,
+ <literal><array></literal> y <literal><primitive-array></literal>.
+ El elemento <literal><map></literal> es representativo:
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="mappingcollection1" coords="2 65"/>
+ <area id="mappingcollection2" coords="3 65"/>
+ <area id="mappingcollection3" coords="4 65"/>
+ <area id="mappingcollection4" coords="5 65"/>
+ <area id="mappingcollection5" coords="6 65"/>
+ <area id="mappingcollection6" coords="7 65"/>
+ <area id="mappingcollection7" coords="8 65"/>
+ <area id="mappingcollection8" coords="9 65"/>
+ <area id="mappingcollection9" coords="10 65"/>
+ <area id="mappingcollection10" coords="11 65"/>
+ <area id="mappingcollection11" coords="12 65"/>
+ <area id="mappingcollection12" coords="13 65"/>
+ <area id="mappingcollection13" coords="14 65"/>
+ </areaspec>
+ <programlisting><![CDATA[<map
+ name="propertyName"
+ table="table_name"
+ schema="schema_name"
+ lazy="true|false"
+ inverse="true|false"
+ cascade="all|none|save-update|delete|all-delete-orphan"
+ sort="unsorted|natural|comparatorClass"
+ order-by="column_name asc|desc"
+ where="arbitrary sql where condition"
+ fetch="join|select|subselect"
+ batch-size="N"
+ access="field|property|ClassName"
+ optimistic-lock="true|false"
+ node="element-name|."
+ embed-xml="true|false"
+>
+
+ <key .... />
+ <map-key .... />
+ <element .... />
+</map>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="mappingcollection1">
+ <para>
+ <literal>name</literal> el nombre de la propiedad de colección
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection2">
+ <para>
+ <literal>table</literal> (opcional - por defecto al nombre de la propiedad)
+ el nombre de la tabla de coleciión (no usado para asociaciones
+ uno-a-muchos)
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection3">
+ <para>
+ <literal>schema</literal> (opcional) el nombre de un esquema de tablas
+ para sobrescribir el esquema declarado en el elemento raíz
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection4">
+ <para>
+ <literal>lazy</literal> (opcional - por defecto a <literal>true</literal>)
+ puede ser usado para deshabilitar la recuperación perezosa y
+ especificar que la asociación es siempre recuperada tempranamente
+ (no disponible para arrays)
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection5">
+ <para>
+ <literal>inverse</literal> (opcional - por defecto a <literal>false</literal>)
+ marca esta colección como el extremo "inverso" de una asociación
+ bidireccional.
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection6">
+ <para>
+ <literal>cascade</literal> (opcional - por defecto a <literal>none</literal>)
+ habilita operaciones en cascada a entidades hijas
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection7">
+ <para>
+ <literal>sort</literal> (opcional) especifica una colección
+ con ordenamiento <literal>natural</literal>, o una clase comparadora
+ dada
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection8">
+ <para>
+ <literal>order-by</literal> (opcional, sólo JDK1.4) especifica una columna
+ de tabla (o columnas) que definen el orden de iteración del
+ <literal>Map</literal>, <literal>Set</literal> o bag, junto a un
+ <literal>asc</literal> o <literal>desc</literal> opcional.
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection9">
+ <para>
+ <literal>where</literal> (opcional) especifica una condición
+ <literal>WHERE</literal> de SQL arbitrario para ser usada al recuperar o
+ quitar la colección (útil si la colección debe
+ contener sólo un subconjunto de los datos disponibles)
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection10">
+ <para>
+ <literal>fetch</literal> (opcional, por defecto a <literal>select</literal>)
+ Elige entre recuperación por unión externa (outer-join),
+ recuperar por selección secuencial, y recuperación por
+ subselección secuencial.
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection11">
+ <para>
+ <literal>batch-size</literal> (opcional, por defecto a <literal>1</literal>)
+ especifica un "tamaño de lote" para la recuperar
+ perezosamente instancias de esta colección.
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection12">
+ <para>
+ <literal>access</literal> (opcional - por defecto a <literal>property</literal>):
+ La estrategia que debe usar Hibernate para acceder al valor de la
+ propiedad.
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection12">
+ <para>
+ <literal>optimistic-lock</literal> (opcional - por defecto a <literal>true</literal>):
+ Especifica que los cambios de estado de la colección resultan en
+ incrementos de versión de la entidad dueña. (Para asociaciones
+ uno a muchos, frecuentemente es razonable deshabilitar esta opción.)
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <sect2 id="collections-foreignkeys" >
+ <title>Claves foráneas de collección</title>
+
+ <para>
+ Las instancias de colección se distinguen en la base de datos por la
+ clave foránea de la entidad que posee la colección. Se hace
+ referencia a esta clave foránea como la <emphasis>columna clave de
+ colección</emphasis> (o columnas) de la tabla de colección.
+ La columna clave de la colección es mapeada por el elemento
+ <literal><key></literal>.
+ </para>
+
+ <para>
+ Puede haber una restricción de nulabilidad sobre la columna de clave
+ foránea. Para la mayoría de colecciones, esto está implicado.
+ Para asociaciones unidireccionales uno a muchos, la columna de clave
+ foránea es nulable por defecto, de modo que podrías necesitar
+ especificar <literal>not-null="true"</literal>.
+ </para>
+
+ <programlisting><![CDATA[<key column="productSerialNumber" not-null="true"/>]]></programlisting>
+
+ <para>
+ La restricción de clave foránea puede usar
+ <literal>ON DELETE CASCADE</literal>.
+ </para>
+
+ <programlisting><![CDATA[<key column="productSerialNumber" on-delete="cascade"/>]]></programlisting>
+
+ <para>
+ Mira el capítulo anterior por una definición completa del
+ elemento <literal><key></literal>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-elements" >
+ <title>Elementos de collección</title>
+
+ <para>
+ Las colecciones pueden contener casi cualquier tipo de Hibernate, incluyendo
+ todos los tipos básicos, componentes, y por supuesto, referencias a otras
+ entidades. Esta es una distinción importante: un objeto en una colección
+ puede ser manejado con una semántica de "valor" (su ciclo de vida depende
+ completamente del propietario de la colección) o podría ser una referencia
+ a otra entidad, con su propio ciclo de vida. En el último caso, sólo
+ el estado del "enlace" entre los dos objetos se considera mantenido por la
+ colección.
+ </para>
+
+ <para>
+ Se hace referencia al tipo contenido como el <emphasis>tipo de elemento
+ de la colección</emphasis>. Los elementos de colección son
+ mapeados por <literal><element></literal> o
+ <literal><composite-element></literal>, o en el caso de referencias
+ de entidades, con <literal><one-to-many></literal> o
+ <literal><many-to-many></literal>. Las dos primeras mapean elementos
+ con semántica de valor, los dos siguientes son usados para mapear
+ asociaciones de entidades.
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-indexed">
+ <title>Colecciones indexadas</title>
+
+ <para>
+ Todos los mapeos de colección, excepto aquellos con semántica de
+ set o bag, necesitan una <emphasis>columna índice</emphasis> en la tabla
+ de colección, una columna que mapea a un índice de array, o
+ índice de <literal>List</literal>, o clave de <literal>Map</literal>.
+ El índice de un <literal>Map</literal> puede ser de cualquier tipo
+ básico, mapeado con <literal><map-key></literal>, o puede ser
+ una referencia de entidad, mapeada con <literal><map-key-many-to-many></literal>,
+ o puede ser un tipo compuesto, mapeado con <literal><composite-map-key></literal>.
+ El índice de un array o lista es siempre de tipo <literal>integer</literal>
+ y se mapea usando el elemento <literal><list-index></literal>. La columna
+ mapeada contiene enteros secuenciales (numerados desde cero, por defecto).
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="index1" coords="2 45"/>
+ <area id="index2" coords="3 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<list-index
+ column="column_name"
+ base="0|1|..."/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="index1">
+ <para>
+ <literal>column_name</literal> (requerido): El nombre de la columna que tiene
+ los valores índice de la colección.
+ </para>
+ </callout>
+ <callout arearefs="index1">
+ <para>
+ <literal>base</literal> (opcional, por defecto a <literal>0</literal>): El valor
+ de la columna índice que corresponde al primer elemento de la lista o
+ array.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <programlistingco>
+ <areaspec>
+ <area id="mapkey1" coords="2 45"/>
+ <area id="mapkey2" coords="3 45"/>
+ <area id="mapkey3" coords="4 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<map-key
+ column="column_name"
+ formula="any SQL expression"
+ type="type_name"
+ node="@attribute-name"
+ length="N"/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="mapkey1">
+ <para>
+ <literal>column</literal> (opcional): El nombre de la columna que tiene
+ los valores índice de la colección.
+ </para>
+ </callout>
+ <callout arearefs="mapkey2">
+ <para>
+ <literal>formula</literal> (opcional): Una fórmula SQL usada para
+ evaluar la clave del mapa.
+ </para>
+ </callout>
+ <callout arearefs="mapkey3">
+ <para>
+ <literal>type</literal> (requerido): el tipo de las claves del mapa.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <programlistingco>
+ <areaspec>
+ <area id="indexmanytomany1" coords="2 45"/>
+ <area id="indexmanytomany2" coords="3 45"/>
+ <area id="indexmanytomany3" coords="3 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<map-key-many-to-many
+ column="column_name"
+ formula="any SQL expression"
+ class="ClassName"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="indexmanytomany1">
+ <para>
+ <literal>column</literal> (opcional): El nombre de la columna clave
+ foránea para los valores índice de la colección.
+ </para>
+ </callout>
+ <callout arearefs="indexmanytomany2">
+ <para>
+ <literal>formula</literal> (opcional): Una fórmula SQL usada para
+ evaluar la clave foránea de la clave del mapa.
+ </para>
+ </callout>
+ <callout arearefs="indexmanytomany3">
+ <para>
+ <literal>class</literal> (requerido): La clase de entidad usada como clave del mapa.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+
+ <para>
+ Si tu tabla no tiene una columna índice, y deseas aún usar <literal>List</literal> como
+ tipo de propiedad, debes mapear la propiedad como un <emphasis><bag></emphasis>
+ de Hibernate. Un bag (bolsa) no retiene su orden al ser recuperado de la base de datos,
+ pero puede ser ordenado o clasificado opcionalmente.
+ </para>
+
+ </sect2>
+
+ <para>
+ Hay absolutamente un rango de mapeos que pueden ser generados para colecciones,
+ cubriendo muchos modelos relacionales comunes. Te sugerimos que experimentes con
+ la herramienta de generación de esquemas para obtener una idea de cómo varias
+ declaraciones de mapeo se traducen a tablas de base de datos.
+ </para>
+
+ <sect2 id="collections-ofvalues" revision="1">
+ <title>Colecciones de valores y asociaciones muchos-a-muchos</title>
+
+ <para>
+ Cualquier colección de valores o asociación muchos a muchos requiere una
+ <emphasis>tabla de colección</emphasis> dedicada con una columna o columnas
+ de clave foránea, <emphasis>columna de elemento de colección</emphasis> o
+ columnas y posiblemente una columna o columnas índice.
+ </para>
+
+ <para>
+ Para una colección de valores, usamos la etiqueta <literal><element></literal>.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="element1b" coords="2 50"/>
+ <area id="element2b" coords="3 50"/>
+ <area id="element3b" coords="4 50"/>
+ </areaspec>
+ <programlisting><![CDATA[<element
+ column="column_name"
+ formula="any SQL expression"
+ type="typename"
+ length="L"
+ precision="P"
+ scale="S"
+ not-null="true|false"
+ unique="true|false"
+ node="element-name"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="element1b">
+ <para>
+ <literal>column</literal> (opcional): El nombre de la columna que tiene
+ los valores de los elementos de la colección.
+ </para>
+ </callout>
+ <callout arearefs="element2b">
+ <para>
+ <literal>formula</literal> (opcional): Una fórmula SQL usada para evaluar
+ el elemento.
+ </para>
+ </callout>
+ <callout arearefs="element3b">
+ <para>
+ <literal>type</literal> (requerido): El tipo del elemento de colección.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Una <emphasis>asociación muchos-a-muchos</emphasis> se especifica usando
+ el elemento <literal><many-to-many></literal>.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="manytomany1" coords="2 60"/>
+ <area id="manytomany2" coords="3 60"/>
+ <area id="manytomany3" coords="4 60"/>
+ <area id="manytomany4" coords="5 60"/>
+ <area id="manytomany5" coords="6 60"/>
+ <area id="manytomany6" coords="7 60"/>
+ <area id="manytomany7" coords="8 60"/>
+ </areaspec>
+ <programlisting><![CDATA[<many-to-many
+ column="column_name"
+ formula="any SQL expression"
+ class="ClassName"
+ fetch="select|join"
+ unique="true|false"
+ not-found="ignore|exception"
+ entity-name="EntityName"
+ node="element-name"
+ embed-xml="true|false"
+ />]]></programlisting>
+ <calloutlist>
+ <callout arearefs="manytomany1">
+ <para>
+ <literal>column</literal> (opcional): El nombre de la columna de clave
+ foránea del elemento.
+ </para>
+ </callout>
+ <callout arearefs="manytomany2">
+ <para>
+ <literal>formula</literal> (opcional): Una fórmula SQL opcional usada
+ para evaluar el valor de clave foránea del elemento.
+ </para>
+ </callout>
+ <callout arearefs="manytomany3">
+ <para>
+ <literal>class</literal> (requerido): El nombre de la clase asociada.
+ </para>
+ </callout>
+ <callout arearefs="manytomany4">
+ <para>
+ <literal>fetch</literal> (opcional - por defecto a <literal>join</literal>):
+ habilita la recuperación por unión externa o selección secuencial para esta
+ asociación. Este es un caso especial; para una recuperación completamente
+ temprana (en un solo <literal>SELECT</literal>) de una entidad y sus relaciones
+ muchos-a-muchos a otras entidades, deberías habilitar la recuperación
+ <literal>join</literal> no sólo de la colección misma, sino también con este
+ atributo en el elemento anidado <literal><many-to-many></literal>.
+ </para>
+ </callout>
+ <callout arearefs="manytomany5">
+ <para>
+ <literal>unique</literal> (opcional): Habilita la generación DDL de una
+ restricción de unicidad para la columna clave foránea. Esto hace la
+ multiplicidad de la asociación efectivamente uno a muchos.
+ </para>
+ </callout>
+ <callout arearefs="manytomany6">
+ <para>
+ <literal>not-found</literal> (opcional - por defecto a <literal>exception</literal>):
+ Especifica cómo serán manejadas las claves foráneas que referencian
+ filas perdidas: <literal>ignore</literal> tratará una fila perdida como
+ una asociación nula.
+ </para>
+ </callout>
+ <callout arearefs="manytomany7">
+ <para>
+ <literal>entity-name</literal> (opcional): El nombre de entidad de la clase
+ asociada, como una alternativa a <literal>class</literal>.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Algunos ejemplos, primero, un conjunto de cadenas:
+ </para>
+
+ <programlisting><![CDATA[<set name="names" table="person_names">
+ <key column="person_id"/>
+ <element column="person_name" type="string"/>
+</set>]]></programlisting>
+
+ <para>
+ Un bag conteniendo enteros (con un orden de iteración determinado por el
+ atributo <literal>order-by</literal>):
+ </para>
+
+ <programlisting><![CDATA[<bag name="sizes"
+ table="item_sizes"
+ order-by="size asc">
+ <key column="item_id"/>
+ <element column="size" type="integer"/>
+</bag>]]></programlisting>
+
+ <para>
+ Un array de entidades - en este caso, una asociación muchos a muchos:
+ </para>
+
+ <programlisting><![CDATA[<array name="addresses"
+ table="PersonAddress"
+ cascade="persist">
+ <key column="personId"/>
+ <list-index column="sortOrder"/>
+ <many-to-many column="addressId" class="Address"/>
+</array>]]></programlisting>
+
+ <para>
+ Un mapa de índices de cadenas a fechas:
+ </para>
+
+ <programlisting><![CDATA[<map name="holidays"
+ table="holidays"
+ schema="dbo"
+ order-by="hol_name asc">
+ <key column="id"/>
+ <map-key column="hol_name" type="string"/>
+ <element column="hol_date" type="date"/>
+</map>]]></programlisting>
+
+ <para>
+ Una lista de componentes (discutidos en el próximo capítulo):
+ </para>
+
+ <programlisting><![CDATA[<list name="carComponents"
+ table="CarComponents">
+ <key column="carId"/>
+ <list-index column="sortOrder"/>
+ <composite-element class="CarComponent">
+ <property name="price"/>
+ <property name="type"/>
+ <property name="serialNumber" column="serialNum"/>
+ </composite-element>
+</list>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="collections-onetomany">
+ <title>Asociaciones uno-a-muchos</title>
+
+ <para>
+ Una <emphasis>asociación uno a muchos</emphasis> enlaza las tablas de dos clases
+ por medio de una clave foránea, sin intervención de tabla de colección alguna.
+ Este mapeo pierde cierta semántica de colecciones Java normales:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ Una instancia de la clase entidad contenida no puede pertenecer
+ a más de una instancia de la colección.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Una instancia de la clase entidad contenida no puede aparecer en más
+ de un valor del índice de colección.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Una asociación de <literal>Product</literal> a <literal>Part</literal> requiere la
+ existencia de una columna clave foránea y posiblemente una columna índice a la tabla
+ <literal>Part</literal>. Una etiqueta <literal><one-to-many></literal> indica
+ que ésta es una asociación uno a muchos.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="onetomany1" coords="2 60"/>
+ <area id="onetomany2" coords="3 60"/>
+ <area id="onetomany3" coords="4 60"/>
+ </areaspec>
+ <programlisting><![CDATA[<one-to-many
+ class="ClassName"
+ not-found="ignore|exception"
+ entity-name="EntityName"
+ node="element-name"
+ embed-xml="true|false"
+ />]]></programlisting>
+ <calloutlist>
+ <callout arearefs="onetomany1">
+ <para>
+ <literal>class</literal> (requerido): El nombre de la clase asociada.
+ </para>
+ </callout>
+ <callout arearefs="onetomany2">
+ <para>
+ <literal>not-found</literal> (opcional - por defecto a <literal>exception</literal>):
+ Especifica cómo serán manejados los identificadores en caché que referencien
+ filas perdidas: <literal>ignore</literal> tratará una fila perdida como una
+ asociación nula.
+ </para>
+ </callout>
+ <callout arearefs="onetomany3">
+ <para>
+ <literal>entity-name</literal> (opcional): El nombre de entidad de la clase
+ asociada, como una alternativa a <literal>class</literal>.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Observa que el elemento <literal><one-to-many></literal> no necesita
+ declarar ninguna columna. Ni es necesario especificar el nombre de <literal>table</literal>
+ en ningún sitio.
+ </para>
+
+ <para>
+ <emphasis>Nota muy importante:</emphasis> Si la columna clave foránea de una asociación
+ <literal><one-to-many></literal> es declarada <literal>NOT NULL</literal>, debes
+ declarar el mapeo de <literal><key></literal> <literal>not-null="true"</literal>
+ o <emphasis>usar una asociación bidireccional</emphasis> con el mapeo de colección
+ marcado <literal>inverse="true"</literal>. Ver la discusión sobre asociaciones
+ bidireccionales más adelante en este capítulo.
+ </para>
+
+ <para>
+ Este ejemplo muestra un mapa de entidades <literal>Part</literal> por nombre
+ (donde <literal>partName</literal> es una propiedad persistente de <literal>Part</literal>).
+ Observa el uso de un índice basado en fórmula.
+ </para>
+
+ <programlisting><![CDATA[<map name="parts"
+ cascade="all">
+ <key column="productId" not-null="true"/>
+ <map-key formula="partName"/>
+ <one-to-many class="Part"/>
+</map>]]></programlisting>
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="collections-advancedmappings">
+ <title>Mapeos de colección avanzados</title>
+
+ <sect2 id="collections-sorted" revision="2">
+ <title>Colecciones ordenadas</title>
+
+ <para>
+ Hibernate soporta colecciones implementando <literal>java.util.SortedMap</literal> y
+ <literal>java.util.SortedSet</literal>. Debes especificar un comparador en el fichero de
+ mapeo:
+ </para>
+
+ <programlisting><![CDATA[<set name="aliases"
+ table="person_aliases"
+ sort="natural">
+ <key column="person"/>
+ <element column="name" type="string"/>
+</set>
+
+<map name="holidays" sort="my.custom.HolidayComparator">
+ <key column="year_id"/>
+ <map-key column="hol_name" type="string"/>
+ <element column="hol_date" type="date"/>
+</map>]]></programlisting>
+
+ <para>
+ Los valores permitidos del atributo <literal>sort</literal> son <literal>unsorted</literal>,
+ <literal>natural</literal> y el nombre de una clase que implemente
+ <literal>java.util.Comparator</literal>.
+ </para>
+
+ <para>
+ Las colecciones ordenadas realmente se comportan como <literal>java.util.TreeSet</literal> o
+ <literal>java.util.TreeMap</literal>.
+ </para>
+
+ <para>
+ Si quieres que la misma base de datos ordene los elementos de colección usa el
+ atributo <literal>order-by</literal> de los mapeos <literal>set</literal>,
+ <literal>bag</literal> o <literal>map</literal>. Esta solución está disponible sólo
+ bajo el JDK 1.4 o superior (está implementado usando <literal>LinkedHashSet</literal> o
+ <literal>LinkedHashMap</literal>). Esto realiza la ordenación en la consulta SQL,
+ no en memoria.
+ </para>
+
+ <programlisting><![CDATA[<set name="aliases" table="person_aliases" order-by="lower(name) asc">
+ <key column="person"/>
+ <element column="name" type="string"/>
+</set>
+
+<map name="holidays" order-by="hol_date, hol_name">
+ <key column="year_id"/>
+ <map-key column="hol_name" type="string"/>
+ <element column="hol_date type="date"/>
+</map>]]></programlisting>
+
+ <para>
+ Observa que el valor del atributo <literal>order-by</literal> es una ordenación SQL, no
+ una ordenación HQL!
+ </para>
+
+ <para>
+ Las asociaciones pueden incluso ser ordenadas por algún criterio arbitrario en tiempo de
+ ejecución usando un <literal>filter()</literal> de colección.
+ </para>
+
+ <programlisting><![CDATA[sortedUsers = s.createFilter( group.getUsers(), "order by this.name" ).list();]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="collections-bidirectional" revision="1">
+ <title>Asociaciones bidireccionales</title>
+
+ <para>
+ Una <emphasis>asociación bidireccional</emphasis> permite la nevegación desde
+ ambos "extremos" de la asociación. Son soportados dos tipos de asociación
+ bidireccional:
+
+ <variablelist>
+ <varlistentry>
+ <term>uno-a-muchos</term>
+ <listitem>
+ <para>
+ set o bag valorados en un extremo, monovaluados al otro
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>muchos-a-muchos</term>
+ <listitem>
+ <para>
+ set o bag valorados a ambos extremos
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ </para>
+
+ <para>
+ Puedes especificar una asociación bidireccional muchos-a-muchos simplemente
+ mapeando dos asociaciones muchos-a-muchos a la misma tabla de base de datos
+ y declarando un extremo como <emphasis>inverse</emphasis> (cuál de ellos es tu
+ elección, pero no puede ser una colección indexada).
+ </para>
+
+ <para>
+ He aquí un ejemplo de una asociación bidireccional muchos-a-muchos; cada categoría
+ puede tener muchos ítems y cada ítem puede estar en muchas categorías:
+ </para>
+
+ <programlisting><![CDATA[<class name="Category">
+ <id name="id" column="CATEGORY_ID"/>
+ ...
+ <bag name="items" table="CATEGORY_ITEM">
+ <key column="CATEGORY_ID"/>
+ <many-to-many class="Item" column="ITEM_ID"/>
+ </bag>
+</class>
+
+<class name="Item">
+ <id name="id" column="CATEGORY_ID"/>
+ ...
+
+ <!-- inverse end -->
+ <bag name="categories" table="CATEGORY_ITEM" inverse="true">
+ <key column="ITEM_ID"/>
+ <many-to-many class="Category" column="CATEGORY_ID"/>
+ </bag>
+</class>]]></programlisting>
+
+ <para>
+ Los cambios hechos sólo al extremo inverso de la asociación <emphasis>no</emphasis>
+ son persistidos. Esto significa que Hibernate tiene dos representaciones en memoria
+ para cada asociación bidireccional, una enlaza de A a B y otra enlaza de B a A.
+ Esto es más fácil de entender si piensas en el modelo de objetos de Java y cómo
+ creamos una relación muchos-a-muchos en Java:
+ </para>
+
+ <programlisting><![CDATA[
+category.getItems().add(item); // The category now "knows" about the relationship
+item.getCategories().add(category); // The item now "knows" about the relationship
+
+session.persist(item); // The relationship won't be saved!
+session.persist(category); // The relationship will be saved]]></programlisting>
+
+ <para>
+ El lado no-inverso se usa para salvar la representación en memoria a la base de datos.
+ </para>
+
+ <para>
+ Puedes definir una asociación bidireccional uno-a-muchos mapeando una asociación uno-a-muchos
+ a la misma columna (o columnas) de tabla como una asociación muchos-a-uno y declarando el
+ extremo multivaluado <literal>inverse="true"</literal>.
+ </para>
+
+ <programlisting><![CDATA[<class name="Parent">
+ <id name="id" column="parent_id"/>
+ ....
+ <set name="children" inverse="true">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+ </set>
+</class>
+
+<class name="eg.Child">
+ <id name="id" column="id"/>
+ ....
+ <many-to-one name="parent"
+ class="Parent"
+ column="parent_id"
+ not-null="true"/>
+</class>]]></programlisting>
+
+ <para>
+ Mapear un extremo de una asociación con <literal>inverse="true"</literal> no afecta
+ la operación de cascadas; éstos son conceptos ortogonales!
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-indexedbidirectional">
+ <title>Asociaciones bidireccionales con colecciones indexadas</title>
+ <para>
+ Requiere especial consideración una asociación bidireccional donde un extremo esté representado
+ como una <literal><list></literal> o <literal><map></literal>. Si hay una propiedad
+ de la clase hija que mapee a la columna índice, no hay problema, podemos seguir usando
+ <literal>inverse="true"</literal> en el mapeo de la colección:
+ </para>
+
+ <programlisting><![CDATA[<class name="Parent">
+ <id name="id" column="parent_id"/>
+ ....
+ <map name="children" inverse="true">
+ <key column="parent_id"/>
+ <map-key column="name"
+ type="string"/>
+ <one-to-many class="Child"/>
+ </map>
+</class>
+
+<class name="Child">
+ <id name="id" column="child_id"/>
+ ....
+ <property name="name"
+ not-null="true"/>
+ <many-to-one name="parent"
+ class="Parent"
+ column="parent_id"
+ not-null="true"/>
+</class>]]></programlisting>
+
+ <para>
+ Pero, si no existe tal proiedad en la clase hija, no podemos pensar en la asociación como
+ verdaderamente bidireccional (hay información en un extremo de la asociación que no está
+ disponible en el otro extremo). En este caso, no podemos mapear la colección con
+ <literal>inverse="true"</literal>. En cambio, podríamos usar el siguiente mapeo:
+ </para>
+
+ <programlisting><![CDATA[<class name="Parent">
+ <id name="id" column="parent_id"/>
+ ....
+ <map name="children">
+ <key column="parent_id"
+ not-null="true"/>
+ <map-key column="name"
+ type="string"/>
+ <one-to-many class="Child"/>
+ </map>
+</class>
+
+<class name="Child">
+ <id name="id" column="child_id"/>
+ ....
+ <many-to-one name="parent"
+ class="Parent"
+ column="parent_id"
+ insert="false"
+ update="false"
+ not-null="true"/>
+</class>]]></programlisting>
+
+ <para>
+ Nota que, en este mapeo, el extremo de la asociación valuado en colección es responsable de las
+ actualizaciones a la clave foránea.
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-ternary">
+ <title>Asociaciones ternarias</title>
+
+ <para>
+ Hay tres enfoques posibles para mapear una asociación ternaria.
+ Una es usar un <literal>Map</literal> con una asociación como su índice:
+ </para>
+
+ <programlisting><![CDATA[<map name="contracts">
+ <key column="employer_id" not-null="true"/>
+ <map-key-many-to-many column="employee_id" class="Employee"/>
+ <one-to-many class="Contract"/>
+</map>]]></programlisting>
+
+ <programlisting><![CDATA[<map name="connections">
+ <key column="incoming_node_id"/>
+ <map-key-many-to-many column="outgoing_node_id" class="Node"/>
+ <many-to-many column="connection_id" class="Connection"/>
+</map>]]></programlisting>
+
+ <para>
+ Un segundo enfoque es simplemente remodelar la asociación como una clase de entidad.
+ Este es el enfoque que usamos más comunmente.
+ </para>
+
+ <para>
+ Una alternativa final es usar elementos compuestos, que discutiremos más adelante.
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-idbag" revision="1">
+ <title><literal>Usando un <idbag></literal></title>
+
+ <para>
+ Si has adoptado completamente nuestra visión de que las claves compuestas son una
+ cosa mala y que las entidades deben tener identificadores sitéticos (claves delegadas),
+ entonces podrías encontrar un poco raro que todas las asociaciones muchos a muchos y
+ las colecciones de valores que hemos mostrado hasta ahora mapeen a tablas con claves
+ compuestas! Ahora, este punto es discutible; una tabla de pura asociación no parece
+ beneficiarse demasiado de una clave delegada (aunque sí <emphasis>podría</emphasis> una
+ colección de valores compuestos). Sin embargo, Hibernate provee una funcionalidad que
+ te permite mapear asociaciones muchos a muchos y colecciones de valores a una tabla con
+ una clave delegada.
+ </para>
+
+ <para>
+ El elemento <literal><idbag></literal> te permite mapear una <literal>List</literal>
+ (o <literal>Collection</literal>) con semántica de bag.
+ </para>
+
+<programlisting><![CDATA[<idbag name="lovers" table="LOVERS">
+ <collection-id column="ID" type="long">
+ <generator class="sequence"/>
+ </collection-id>
+ <key column="PERSON1"/>
+ <many-to-many column="PERSON2" class="Person" fetch="join"/>
+</idbag>]]></programlisting>
+
+ <para>
+ Como puedes ver, un <literal><idbag></literal> tiene un generador de id sintético,
+ igual que una clase de entidad! Una clave delegada diferente se asigna a cada fila
+ de la colección. Hibernate no provee ningún mecanismo para descubrir el valor de clave
+ delegada de una fila en particular, sin embargo.
+ </para>
+
+ <para>
+ Observa que el rendimiento de actualización de un <literal><idbag></literal> es
+ <emphasis>mucho</emphasis> mejor que el de un <literal><bag></literal> regular!
+ Hibernate puede localizar filas individuales eficientemente y actualizarlas o borrarlas
+ individualmente, igual que si fuese una lista, mapa o conjunto.
+ </para>
+
+ <para>
+ En la implementación actual, la estrategia de generación de identificador
+ <literal>native</literal> no está soportada para identificadores de colecciones
+ <literal><idbag></literal>.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <!--undocumenting this stuff -->
+
+ <!--sect1 id="collections-heterogeneous">
+ <title>Heterogeneous Associations</title>
+
+ <para>
+ The <literal><many-to-any></literal> and <literal><index-many-to-any></literal>
+ elements provide for true heterogeneous associations. These mapping elements work in the
+ same way as the <literal><any></literal> element - and should also be used
+ rarely, if ever.
+ </para>
+
+ </sect1-->
+
+ <sect1 id="collections-example" revision="1">
+ <title>Ejemplos de colección</title>
+
+ <para>
+ Las secciones previas son bastantes confusas. Así que miremos un ejemplo.
+ Esta clase:
+ </para>
+
+ <programlisting><![CDATA[package eg;
+import java.util.Set;
+
+public class Parent {
+ private long id;
+ private Set children;
+
+ public long getId() { return id; }
+ private void setId(long id) { this.id=id; }
+
+ private Set getChildren() { return children; }
+ private void setChildren(Set children) { this.children=children; }
+
+ ....
+ ....
+}]]></programlisting>
+
+ <para>
+ tiene una colección de instancias de <literal>Child</literal>.
+ Si cada hijo tiene como mucho un padre, el mapeo más natural es
+ una asociación uno-a-muchos:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Parent">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <set name="children">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+ </set>
+ </class>
+
+ <class name="Child">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Esto mapea a las siguientes definiciones de tablas:
+ </para>
+
+ <programlisting><![CDATA[create table parent ( id bigint not null primary key )
+create table child ( id bigint not null primary key, name varchar(255), parent_id bigint )
+alter table child add constraint childfk0 (parent_id) references parent]]></programlisting>
+
+ <para>
+ Si el padre es <emphasis>requerido</emphasis>, usa una asociación bidireccional
+ uno-a-muchos:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Parent">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <set name="children" inverse="true">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+ </set>
+ </class>
+
+ <class name="Child">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <property name="name"/>
+ <many-to-one name="parent" class="Parent" column="parent_id" not-null="true"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Observa la restricción <literal>NOT NULL</literal>:
+ </para>
+
+ <programlisting><![CDATA[create table parent ( id bigint not null primary key )
+create table child ( id bigint not null
+ primary key,
+ name varchar(255),
+ parent_id bigint not null )
+alter table child add constraint childfk0 (parent_id) references parent]]></programlisting>
+
+ <para>
+ Alternativamente, si absolutamente insistes que esta asociación debe ser unidireccional,
+ puedes declarar la restricción <literal>NOT NULL</literal> en el mapeo de
+ <literal><key></literal>:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Parent">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <set name="children">
+ <key column="parent_id" not-null="true"/>
+ <one-to-many class="Child"/>
+ </set>
+ </class>
+
+ <class name="Child">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ En la otra mano, si un hijo pudiera tener múltiples padres, sería apropiada
+ una asociación muchos-a-muchos:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Parent">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <set name="children" table="childset">
+ <key column="parent_id"/>
+ <many-to-many class="Child" column="child_id"/>
+ </set>
+ </class>
+
+ <class name="Child">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Definiciones de tabla:
+ </para>
+
+ <programlisting><![CDATA[create table parent ( id bigint not null primary key )
+create table child ( id bigint not null primary key, name varchar(255) )
+create table childset ( parent_id bigint not null,
+ child_id bigint not null,
+ primary key ( parent_id, child_id ) )
+alter table childset add constraint childsetfk0 (parent_id) references parent
+alter table childset add constraint childsetfk1 (child_id) references child]]></programlisting>
+
+ <para>
+ Para más ejemplos y un paseo completo a través del mapeo de relaciones padre/hijo,
+ ver <xref linkend="example-parentchild"/>.
+ </para>
+
+ <para>
+ Son posibles mapeos de asociación aún más complejos. Catalogaremos todas las posibilidades
+ en el próximo capítulo.
+ </para>
+
+ </sect1>
+
+</chapter>
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/component_mapping.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/component_mapping.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/component_mapping.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,403 @@
+<chapter id="components">
+ <title>Mapeo de Componentes</title>
+
+ <para>
+ La noción de un <emphasis>componente</emphasis> es reusada en muchos contextos diferentes,
+ para propósitos diferentes, a través de Hibernate.
+ </para>
+
+ <sect1 id="components-dependentobjects">
+ <title>Objetos dependientes</title>
+
+ <para>
+ Un componente es un objeto contenido que es persistido como un tipo de valor, no una
+ referencia de entidad. El término "componente" hace referencia a la noción orientada a
+ objetos de composición (no a componentes a nivel de arquitectura). Por ejemplo, podrías
+ modelar una persona como:
+ </para>
+
+ <programlisting><![CDATA[public class Person {
+ private java.util.Date birthday;
+ private Name name;
+ private String key;
+ public String getKey() {
+ return key;
+ }
+ private void setKey(String key) {
+ this.key=key;
+ }
+ public java.util.Date getBirthday() {
+ return birthday;
+ }
+ public void setBirthday(java.util.Date birthday) {
+ this.birthday = birthday;
+ }
+ public Name getName() {
+ return name;
+ }
+ public void setName(Name name) {
+ this.name = name;
+ }
+ ......
+ ......
+}]]></programlisting>
+
+<programlisting><![CDATA[public class Name {
+ char initial;
+ String first;
+ String last;
+ public String getFirst() {
+ return first;
+ }
+ void setFirst(String first) {
+ this.first = first;
+ }
+ public String getLast() {
+ return last;
+ }
+ void setLast(String last) {
+ this.last = last;
+ }
+ public char getInitial() {
+ return initial;
+ }
+ void setInitial(char initial) {
+ this.initial = initial;
+ }
+}]]></programlisting>
+
+ <para>
+ Ahora <literal>Name</literal> puede ser persistido como un componente de
+ <literal>Person</literal>. Observa que <literal>Name</literal> define métodos
+ getter y setter para sus propiedades persistentes, pero no necesita declarar
+ ninguna interface ni propiedades identificadoras.
+ </para>
+
+ <para>
+ Nuestro mapeo de Hibernate se vería así:
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Person" table="person">
+ <id name="Key" column="pid" type="string">
+ <generator class="uuid.hex"/>
+ </id>
+ <property name="birthday" type="date"/>
+ <component name="Name" class="eg.Name"> <!-- class attribute optional -->
+ <property name="initial"/>
+ <property name="first"/>
+ <property name="last"/>
+ </component>
+</class>]]></programlisting>
+
+ <para>
+ La tabla person tendría las columnas <literal>pid</literal>,
+ <literal>birthday</literal>,
+ <literal>initial</literal>,
+ <literal>first</literal> y
+ <literal>last</literal>.
+ </para>
+
+ <para>
+ Como todos los tipos de valor, los componentes no soportan referencias compartidas.
+ En otras palabras, dos personas pueden tener el mismo nombre, pero los dos objetos
+ persona contendrían dos objetos nombre independientes, sólo "iguales" en valor.
+ La semántica de valor nulo de un componente es <emphasis>ad hoc</emphasis>.
+ Cuando se recargue el objeto contenedor, Hibernate asumirá que si todas las columnas del
+ componente son nulas, el componente entero es nulo. Esto debe estar bien para la mayoría
+ de propósitos.
+ </para>
+
+ <para>
+ Las propiedades de un componentes pueden ser de cualquier tipo de Hibernate
+ (colecciones, muchos-a-uno, asociaciones, otros componentes, etc). Los componentes
+ anidados <emphasis>no</emphasis> deben ser considerados un uso exótico. Hibernate está
+ concebido para soportar un modelo de objetos granularizado en fino.
+ </para>
+
+ <para>
+ El elemento <literal><component></literal> permite un subelemento
+ <literal><parent></literal> que mapee una propiedad de la clase del componente
+ como una referencia de regreso a la entidad contenedora.
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Person" table="person">
+ <id name="Key" column="pid" type="string">
+ <generator class="uuid.hex"/>
+ </id>
+ <property name="birthday" type="date"/>
+ <component name="Name" class="eg.Name" unique="true">
+ <parent name="namedPerson"/> <!-- reference back to the Person -->
+ <property name="initial"/>
+ <property name="first"/>
+ <property name="last"/>
+ </component>
+</class>]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="components-incollections" revision="1">
+ <title>Colecciones de objetos dependientes</title>
+
+ <para>
+ Las colecciones de componentes están soportadas (por ejemplo,
+ un array de tipo <literal>Name</literal>). Declara tu colección
+ de componentes remplazando la etiqueta <literal><element></literal>
+ por una etiqueta <literal><composite-element></literal>.
+ </para>
+
+ <programlisting><![CDATA[<set name="someNames" table="some_names" lazy="true">
+ <key column="id"/>
+ <composite-element class="eg.Name"> <!-- class attribute required -->
+ <property name="initial"/>
+ <property name="first"/>
+ <property name="last"/>
+ </composite-element>
+</set>]]></programlisting>
+
+ <para>
+ Nota: si defines un <literal>Set</literal> de elementos compuestos, es muy
+ importante implementar <literal>equals()</literal> y <literal>hashCode()</literal>
+ correctamente.
+ </para>
+
+ <para>
+ Los elementos compuestos pueden contener componentes pero no colecciones.
+ Si tu elemento compuesto contiene a su vez componentes, usa la etiqueta
+ <literal><nested-composite-element></literal>. Este es un caso bastante
+ exótico - una colección de componentes que a su vez tienen componentes. A esta
+ altura debes estar preguntándote si una asociación uno-a-muchos es más
+ apropiada. Intenta remodelar el elemento compuesto como una entidad - pero
+ observa que aunque el modelo Java es el mismo, el modelo relacional y la
+ semántica de persistencia siguen siendo ligeramente diferentes.
+ </para>
+
+ <para>
+ Por favor observa que un mapeo de elemento compuesto no soporta
+ propiedades nulables si estás usando un <literal><set></literal>.
+ Hibernate tiene que usar cada columna para identificar un registro
+ al borrar objetos (no hay una columna clave primaria separada en la tabla del
+ elemento compuesto), lo que es imposible con valores nulos. Tienes que, o bien usar
+ sólo propiedades no nulas en un elemento compuesto o elegir un
+ <literal><list></literal>, <literal><map></literal>,
+ <literal><bag></literal> o <literal><idbag></literal>.
+ </para>
+
+ <para>
+ Un caso especial de un elemento compuesto es un elemento compuesto con un
+ elemento anidado <literal><many-to-one></literal>. Un mapeo como este
+ te permite mapear columnas extra de una tabla de asociación muchos-a-muchos
+ a la clase del elemento compuesto. La siguiente es una asociación muchos-a-muchos
+ de <literal>Order</literal> a <literal>Item</literal> donde
+ <literal>purchaseDate</literal>, <literal>price</literal> y
+ <literal>quantity</literal> son propiedades de la asociación:
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Order" .... >
+ ....
+ <set name="purchasedItems" table="purchase_items" lazy="true">
+ <key column="order_id">
+ <composite-element class="eg.Purchase">
+ <property name="purchaseDate"/>
+ <property name="price"/>
+ <property name="quantity"/>
+ <many-to-one name="item" class="eg.Item"/> <!-- class attribute is optional -->
+ </composite-element>
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ Por supuesto, no puede haber una referencia a la compra del otro lado para la
+ navegación bidireccional de la asociación. Recuerda que los componentes son tipos de
+ valor no permiten referencias compartidas. Una sola <literal>Purchase</literal> puede
+ estar en el conjunto de una <literal>Order</literal>, pero no puede ser referenciada
+ por el <literal>Item</literal> al mismo tiempo.
+ </para>
+
+ <para>Incluso son posibles las asociaciones ternarias (o cuaternarias, etc):</para>
+
+ <programlisting><![CDATA[<class name="eg.Order" .... >
+ ....
+ <set name="purchasedItems" table="purchase_items" lazy="true">
+ <key column="order_id">
+ <composite-element class="eg.OrderLine">
+ <many-to-one name="purchaseDetails class="eg.Purchase"/>
+ <many-to-one name="item" class="eg.Item"/>
+ </composite-element>
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ Los elementos compuestos pueden aparecer en consultas usando la misma
+ sintáxis que las asociaciones a otras entidades.
+ </para>
+
+ </sect1>
+
+ <sect1 id="components-asmapindex">
+ <title>Componentes como índices de Map</title>
+
+ <para>
+ El elemento <literal><composite-map-key></literal> te permite mapear
+ una clase componente como la clave de un <literal>Map</literal>. Asegúrate que
+ sobrescribes <literal>hashCode()</literal> y <literal>equals()</literal>
+ correctamente en la clase componente.
+ </para>
+ </sect1>
+
+ <sect1 id="components-compositeid" revision="1">
+ <title>Componentes como identificadores compuestos</title>
+
+ <para>
+ Puedes usar un componente como un identidicador de una clase entidad. Tu clase
+ componente debe satisfacer ciertos requerimientos:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ Debe implementar <literal>java.io.Serializable</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Debe re-implementar <literal>equals()</literal> y
+ <literal>hashCode()</literal>, consistentemente con la
+ noción de base de datos de igualdad de clave compuesta.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ <emphasis>Nota: en Hibernat3, el segundo requerimiento no es absolutamente un
+ requerimiento rígido de Hibernate. Pero de todas formas, házlo.</emphasis>
+ </para>
+
+ <para>
+ No puedes usar un <literal>IdentifierGenerator</literal> para generar claves
+ compuestas. La aplicación debe, en cambio, asignar sus propios identificadores.
+ </para>
+
+ <para>
+ Usa la etiqueta <literal><composite-id></literal> (con elementos
+ anidados <literal><key-property></literal>) en lugar de la usual
+ declaración <literal><id></literal>. Por ejemplo, la clase
+ <literal>OrderLine</literal> tiene una clave primaria que depende de
+ la clave primaria (compuesta) de <literal>Order</literal>.
+ </para>
+
+ <programlisting><![CDATA[<class name="OrderLine">
+
+ <composite-id name="id" class="OrderLineId">
+ <key-property name="lineId"/>
+ <key-property name="orderId"/>
+ <key-property name="customerId"/>
+ </composite-id>
+
+ <property name="name"/>
+
+ <many-to-one name="order" class="Order"
+ insert="false" update="false">
+ <column name="orderId"/>
+ <column name="customerId"/>
+ </many-to-one>
+ ....
+
+</class>]]></programlisting>
+
+ <para>
+ Ahora, cualquier clave foránea que referencie la tabla de <literal>OrderLine</literal>
+ es también compuesta. Debes declarar esto en tus mapeos de otras clases. Una asociación
+ a <literal>OrderLine</literal> sería mapeado así:
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="orderLine" class="OrderLine">
+<!-- the "class" attribute is optional, as usual -->
+ <column name="lineId"/>
+ <column name="orderId"/>
+ <column name="customerId"/>
+</many-to-one>]]></programlisting>
+
+ <para>
+ (Nota que la etiqueta <literal><column></literal> es una alternativa al
+ atributo <literal>column</literal> en cualquier sitio.)
+ </para>
+
+ <para>
+ Una asociación <literal>muchos-a-muchos</literal> a <literal>OrderLine</literal>
+ también usa la clave foránea compuesta:
+ </para>
+
+ <programlisting><![CDATA[<set name="undeliveredOrderLines">
+ <key column name="warehouseId"/>
+ <many-to-many class="OrderLine">
+ <column name="lineId"/>
+ <column name="orderId"/>
+ <column name="customerId"/>
+ </many-to-many>
+</set>]]></programlisting>
+
+ <para>
+ La colección de <literal>OrderLine</literal>s en <literal>Order</literal> usaría:
+ </para>
+
+ <programlisting><![CDATA[<set name="orderLines" inverse="true">
+ <key>
+ <column name="orderId"/>
+ <column name="customerId"/>
+ </key>
+ <one-to-many class="OrderLine"/>
+</set>]]></programlisting>
+
+ <para>
+ (El elemento <literal><one-to-many></literal>, como es usual, no declara columnas.)
+ </para>
+
+ <para>
+ Si <literal>OrderLine</literal> posee una colección por sí misma, tiene también
+ una clave foránea compuesta.
+ </para>
+
+ <programlisting><![CDATA[<class name="OrderLine">
+ ....
+ ....
+ <list name="deliveryAttempts">
+ <key> <!-- a collection inherits the composite key type -->
+ <column name="lineId"/>
+ <column name="orderId"/>
+ <column name="customerId"/>
+ </key>
+ <list-index column="attemptId" base="1"/>
+ <composite-element class="DeliveryAttempt">
+ ...
+ </composite-element>
+ </set>
+</class>]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="components-dynamic" revision="1">
+ <title>Componentes dinámicos</title>
+
+ <para>
+ Puedes incluso mapear una propiedad de tipo <literal>Map</literal>:
+ </para>
+
+ <programlisting><![CDATA[<dynamic-component name="userAttributes">
+ <property name="foo" column="FOO" type="string"/>
+ <property name="bar" column="BAR" type="integer"/>
+ <many-to-one name="baz" class="Baz" column="BAZ_ID"/>
+</dynamic-component>]]></programlisting>
+
+ <para>
+ La semántica de un mapeo <literal><dynamic-component></literal> es ídentica
+ a la de <literal><component></literal>. La ventaja de este tipo de mapeos es
+ la habilidad para determinar las propiedades reales del bean en tiempo de despliegue,
+ sólo con editar el documento de mapeo. La manipulación del documento de mapeo en tiempo
+ de ejecución es también posible, usando un analizador DOM. Incluso mejor, puedes acceder
+ (y cambiar) el metamodelo de tiempo de configuración de Hibernate por medio del objeto
+ <literal>Configuration</literal>.
+ </para>
+
+ </sect1>
+
+</chapter>
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/configuration.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/configuration.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/configuration.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1761 @@
+<chapter id="session-configuration" revision="1">
+
+ <title>Configuración</title>
+
+ <para>
+ Debido a que Hibernate está diseñado para operar en muchos entornos
+ diferentes, hay un gran número de parámetros de configuración.
+ Afortunadamente, la mayoría tiene valores por defecto sensibles e Hibernate
+ se distribuye con un fichero <literal>hibernate.properties</literal> de ejemplo en
+ <literal>etc/</literal> que muestra las diversas opciones. Tan sólo pon el
+ fichero de ejemplo en tu classpath y personalízalo.
+ </para>
+
+ <sect1 id="configuration-programmatic" revision="1">
+ <title>Configuración programática</title>
+
+ <para>
+ Una instancia de <literal>org.hibernate.cfg.Configuration</literal>
+ representa un conjunto entero de mapeos de los tipos Java de una aplicación
+ a una base de datos SQL. La <literal>Configuration</literal> es usada para
+ construir una <literal>SessionFactory</literal> (inmutable). Los mapeos se
+ compilan de varios ficheros de mapeo XML.
+ </para>
+
+ <para>
+ Puedes obtener una instancia de <literal>Configuration</literal> instanciándola
+ directamente y especificando documentos de mapeo XML. Si los ficheros de mapeo
+ están en el classpath, usa <literal>addResource()</literal>:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = new Configuration()
+ .addResource("Item.hbm.xml")
+ .addResource("Bid.hbm.xml");]]></programlisting>
+
+ <para>
+ Una forma alternativa (a veces mejor) es especificar la clase mapeada,
+ y dejar que Hibernate encuentre el documento de mapeo por ti:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = new Configuration()
+ .addClass(org.hibernate.auction.Item.class)
+ .addClass(org.hibernate.auction.Bid.class);]]></programlisting>
+
+ <para>
+ Entonces Hibernate buscará ficheros de mapeo llamados
+ <literal>/org/hibernate/auction/Item.hbm.xml</literal> y
+ <literal>/org/hibernate/auction/Bid.hbm.xml</literal> en el classpath.
+ Este enfoque elimina cualquier nombre de fichero en el código.
+ </para>
+
+ <para>
+ Una <literal>Configuration</literal> también te permite especificar
+ propiedades de configuración:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = new Configuration()
+ .addClass(org.hibernate.auction.Item.class)
+ .addClass(org.hibernate.auction.Bid.class)
+ .setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLInnoDBDialect")
+ .setProperty("hibernate.connection.datasource", "java:comp/env/jdbc/test")
+ .setProperty("hibernate.order_updates", "true");]]></programlisting>
+
+ <para>
+ Esta no es la única forma de pasar propiedades de configuración
+ a Hibernate. La diversas opciones incluyen:
+ </para>
+
+ <orderedlist spacing="compact">
+ <listitem>
+ <para>
+ Pasar una instancia de <literal>java.util.Properties</literal> a
+ <literal>Configuration.setProperties()</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Colocar <literal>hibernate.properties</literal> en un directorio
+ raíz del classpath.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Establecer propiedades <literal>System</literal>
+ usando <literal>java -Dproperty=value</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Incluir elementos <literal><property></literal>
+ en <literal>hibernate.cfg.xml</literal> (discutido luego).
+ </para>
+ </listitem>
+ </orderedlist>
+
+ <para>
+ <literal>hibernate.properties</literal> es el enfoque más fácil
+ si quieres comenzar rápido.
+ </para>
+
+ <para>
+ La <literal>Configuration</literal> está concebida como un objeto
+ de tiempo de arranque, para ser descartado una vez que una
+ <literal>SessionFactory</literal> es creada.
+ </para>
+
+ </sect1>
+
+ <sect1 id="configuration-sessionfactory">
+ <title>Obteniendo una SessionFactory</title>
+
+ <para>
+ Cuando todos los mapeos han sido parseados por la <literal>Configuration</literal>,
+ la aplicación debe obtener una fábrica de instancias de <literal>Session</literal>.
+ Esta fábrica está concebida para ser compartida por todas las hebras de
+ aplicación:
+ </para>
+
+ <programlisting><![CDATA[SessionFactory sessions = cfg.buildSessionFactory();]]></programlisting>
+
+ <para>
+ Hibernate permite que tu aplicación instancie más de una
+ <literal>SessionFactory</literal>. Esto es útil si estás usando
+ más de una base de datos.
+ </para>
+
+ </sect1>
+
+ <sect1 id="configuration-hibernatejdbc" revision="1">
+ <title>Conexiones JDBC</title>
+
+ <para>
+ Usualmente, quieres que la <literal>SessionFactory</literal> cree y almacene
+ en pool conexiones JDBC para ti. Si adoptas este enfoque, abrir una <literal>Session</literal>
+ es tan simple como:
+ </para>
+
+ <programlisting><![CDATA[Session session = sessions.openSession(); // open a new Session]]></programlisting>
+
+ <para>
+ En cuanto hagas algo que requiera acceso a la base de datos, se obtendrá una
+ conexión JDBC del pool.
+ </para>
+
+ <para>
+ Para que esto funcione, necesitamos pasar algunas propiedades de conexión
+ JDBC a Hibernate. Todos los nombres de propiedades y su semántica
+ están definidas en la clase <literal>org.hibernate.cfg.Environment</literal>.
+ Describiremos ahora las configuraciones más importantes para la conexión
+ JDBC.
+ </para>
+
+ <para>
+ Hibernate obtendrá (y tendrá en pool) conexiones usando
+ <literal>java.sql.DriverManager</literal> si configuras las siguientes propiedades:
+ </para>
+
+ <table frame="topbot">
+ <title>Propiedades JDBC de Hibernate</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Nombre de propiedad</entry>
+ <entry>Propósito</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.connection.driver_class</literal>
+ </entry>
+ <entry>
+ <emphasis>clase del driver jdbc</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.url</literal>
+ </entry>
+ <entry>
+ <emphasis>URL de jdbc</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.username</literal>
+ </entry>
+ <entry>
+ <emphasis>usuario de base de datos</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.password</literal>
+ </entry>
+ <entry>
+ <emphasis>contraseña del usuario de base de datos</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.pool_size</literal>
+ </entry>
+ <entry>
+ <emphasis>número máximo de conexiones manejadas por pooling</emphasis>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ El algoritmo de pooling de conexiones propio de Hibernate es sin embargo
+ algo rudimentario. Está concebido para ayudarte a comenzar y
+ <emphasis>no está concebido para usar en un sistema de producción</emphasis>
+ ni siquiera para pruebas de rendimiento. Debes usar un pool de terceros para
+ un mejor rendimiento y estabilidad. Sólo remplaza la propiedad
+ <literal>hibernate.connection.pool_size</literal> con configuraciones
+ específicas del pool de conexiones. Esto desactivará el pool
+ interno de Hibernate. Por ejemplo, podrías querer usar C3P0.
+ </para>
+
+ <para>
+ C3P0 es un pool de conexiones JDBC de código abierto distribuido
+ junto a Hibernate en el directorio <literal>lib</literal>.
+ Hibernate usará su <literal>C3P0ConnectionProvider</literal>
+ para pooling de conexiones si estableces propiedades <literal>hibernate.c3p0.*</literal>.
+ Si quieres usar Proxool refiérete al <literal>hibernate.properties</literal>
+ empaquetado y al sitio web de Hibernate para más información.
+ </para>
+
+ <para>
+ Aquí hay un fichero <literal>hibernate.properties</literal> de ejemplo para C3P0:
+ </para>
+
+ <programlisting id="c3p0-configuration" revision="1"><![CDATA[hibernate.connection.driver_class = org.postgresql.Driver
+hibernate.connection.url = jdbc:postgresql://localhost/mydatabase
+hibernate.connection.username = myuser
+hibernate.connection.password = secret
+hibernate.c3p0.min_size=5
+hibernate.c3p0.max_size=20
+hibernate.c3p0.timeout=1800
+hibernate.c3p0.max_statements=50
+hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect]]></programlisting>
+
+ <para>
+ Para su uso en un servidor de aplicaciones, casi siempre debes configurar
+ Hibernate para que obtenga conexiones de un <literal>Datasource</literal>
+ del servidor de aplicaciones registrado en JNDI. Necesitarás establecer
+ al menos una de las siguientes propiedades:
+ </para>
+
+ <table frame="topbot">
+ <title>Propiedades de Datasource de Hibernate</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Nombre de propiedad</entry>
+ <entry>Propósito</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.connection.datasource</literal>
+ </entry>
+ <entry>
+ <emphasis>nombre del datasource JNDI</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jndi.url</literal>
+ </entry>
+ <entry>
+ <emphasis>URL del provedor JNDI</emphasis> (optional)
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jndi.class</literal>
+ </entry>
+ <entry>
+ <emphasis>clase de la <literal>InitialContextFactory</literal> de JNDI</emphasis> (opcional)
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.username</literal>
+ </entry>
+ <entry>
+ <emphasis>usuario de base de datos</emphasis> (opcional)
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.password</literal>
+ </entry>
+ <entry>
+ <emphasis>contraseña del usuario de base de datos</emphasis> (opcional)
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ He aquí un fichero <literal>hibernate.properties</literal> de ejemplo
+ para un un datasource JNDI provisto por un servidor de aplicaciones.
+ </para>
+
+ <programlisting><![CDATA[hibernate.connection.datasource = java:/comp/env/jdbc/test
+hibernate.transaction.factory_class = \
+ org.hibernate.transaction.JTATransactionFactory
+hibernate.transaction.manager_lookup_class = \
+ org.hibernate.transaction.JBossTransactionManagerLookup
+hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect]]></programlisting>
+
+ <para>
+ Las conexiones JDBC obtenidas de un datasource JNDI participarán automáticamente
+ en las transacciones del servidor de aplicaciones manejadas por contenedor.
+ </para>
+
+ <para>
+ Pueden darse propiedades de conexión arbitrarias anteponiendo
+ "<literal>hibernate.connnection</literal>" al nombre de propiedad.
+ Por ejemplo, puedes especificar un <literal>charSet</literal> usando
+ <literal>hibernate.connection.charSet</literal>.
+ </para>
+
+ <para>
+ Puedes definir tu propia estrategia de plugin para obtener conexiones JDBC implementando
+ la interface <literal>org.hibernate.connection.ConnectionProvider</literal>. Puedes
+ seleccionar una implementación personalizada estableciendo
+ <literal>hibernate.connection.provider_class</literal>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="configuration-optional" revision="1">
+ <title>Parámetros de configuración opcionales</title>
+
+ <para>
+ Hay un número de otras propiedades que controlan el comportamiento
+ de Hibernate en tiempo de ejecución. Todas son opcionales y tienen
+ valores por defecto razonables.
+ </para>
+
+ <para>
+ <emphasis>Advertencia: algunas de estas propiedades son de "nivel-de-sistema"
+ solamente.</emphasis>. Las propiedades a nivel de sistema sólo pueden ser
+ establecidas por medio de <literal>java -Dproperty=value</literal> o
+ <literal>hibernate.properties</literal>. <emphasis>No</emphasis> pueden
+ establecerse por medio de las otras técnicas arriba descritas.
+ </para>
+
+ <table frame="topbot" id="configuration-optional-properties" revision="8">
+ <title>Propiedades de Configuración de Hibernate</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Nombre de propiedad</entry>
+ <entry>Propósito</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.dialect</literal>
+ </entry>
+ <entry>
+ El nombre de clase de un <literal>Dialect</literal>
+ de Hibernate que permite a Hibernate generar SQL optimizado
+ para una base de datos relacional en particular.
+ <para>
+ <emphasis role="strong">ej.</emphasis>
+ <literal>full.classname.of.Dialect</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.show_sql</literal>
+ </entry>
+ <entry>
+ Escribe todas las sentencias SQL a la consola.
+ <para>
+ <emphasis role="strong">ej.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.default_schema</literal>
+ </entry>
+ <entry>
+ Cualifica, en el SQL generado, los nombres de tabla sin cualificar
+ con el esquema/tablespace dado.
+ <para>
+ <emphasis role="strong">ej.</emphasis>
+ <literal>SCHEMA_NAME</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.default_catalog</literal>
+ </entry>
+ <entry>
+ Cualifica, en el SQL generado, los nombres de tabla sin cualificar
+ con el catálogo dado.
+ <para>
+ <emphasis role="strong">ej.</emphasis>
+ <literal>CATALOG_NAME</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.session_factory_name</literal>
+ </entry>
+ <entry>
+ La <literal>SessionFactory</literal> será
+ ligada a este nombre en JNDI automáticamente
+ después de ser creada.
+ <para>
+ <emphasis role="strong">ej.</emphasis>
+ <literal>jndi/composite/name</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.max_fetch_depth</literal>
+ </entry>
+ <entry>
+ Establece una "profundidad" máxima del
+ árbol de recuperación por outer join
+ para asociaciones de un extremo solo (uno-a-uno, muchos-a-uno).
+ Un <literal>0</literal> deshabilita la recuperación
+ por outer join por defecto.
+ <para>
+ <emphasis role="strong">ej.</emphasis>
+ valores recomendados entre <literal>0</literal> y
+ <literal>3</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.default_batch_fetch_size</literal>
+ </entry>
+ <entry>
+ Establece un tamaño por defecto para la recuperación
+ en lote de asociaciones de Hibernate.
+ <para>
+ <emphasis role="strong">ej.</emphasis>
+ valores recomendados <literal>4</literal>, <literal>8</literal>,
+ <literal>16</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.default_entity_mode</literal>
+ </entry>
+ <entry>
+ Establece un modo por defecto de representación de
+ entidades para todas las sesiones abiertas por esta
+ <literal>SessionFactory</literal>
+ <para>
+ <literal>dynamic-map</literal>, <literal>dom4j</literal>,
+ <literal>pojo</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.order_updates</literal>
+ </entry>
+ <entry>
+ Fuerza a Hibernate a ordenar las actualizaciones SQL
+ por el valor de la clave primaria de los items a actualizar.
+ Esto resultará en menos bloqueos muertos de transacción
+ en sistemas altamente concurrentes.
+ <para>
+ <emphasis role="strong">ej.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.generate_statistics</literal>
+ </entry>
+ <entry>
+ De habilitarse, Hibernate colectará estadísticas
+ útiles para la afinación de rendimiento.
+ <para>
+ <emphasis role="strong">ej.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.use_identifer_rollback</literal>
+ </entry>
+ <entry>
+ De habilitarse, las propiedades identificadoras
+ generadas serán reseteadas a valores por
+ defecto cuando los objetos sean borrados.
+ <para>
+ <emphasis role="strong">ej.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.use_sql_comments</literal>
+ </entry>
+ <entry>
+ De activarse, Hibernate generará comentarios dentro del SQL,
+ para una más fácil depuración, por defecto a
+ <literal>false</literal>.
+ <para>
+ <emphasis role="strong">ej.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame="topbot" id="configuration-jdbc-properties" revision="8">
+ <title>Propiedades de JDBC y Conexiones de Hibernate</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Nombre de propiedad</entry>
+ <entry>Propoósito</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.fetch_size</literal>
+ </entry>
+ <entry>
+ Un valor distinto de cero que determina el tamaño
+ de recuperación de JDBC (llama a
+ <literal>Statement.setFetchSize()</literal>).
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.batch_size</literal>
+ </entry>
+ <entry>
+ Un valor distinto de cero habilita el uso de actualizaciones
+ en lote de JDBC2 por Hibernate.
+ <para>
+ <emphasis role="strong">ej.</emphasis>
+ valores recomendados entre <literal>5</literal> y <literal>30</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.batch_versioned_data</literal>
+ </entry>
+ <entry>
+ Establece esta propiedad a <literal>true</literal> si tu driver JDBC
+ devuelve cuentas correctas de filas desde <literal>executeBatch()</literal>
+ (usualmente es seguro activar esta opción). Hibernate usará
+ DML en lote para versionar automáticamente los datos.
+ Por defecto a <literal>false</literal>.
+ <para>
+ <emphasis role="strong">ej.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.factory_class</literal>
+ </entry>
+ <entry>
+ Selecciona un <literal>Batcher</literal> personalizado.
+ La mayoría de las aplicaciones no necesitarán
+ esta propiedad de configuración.
+ <para>
+ <emphasis role="strong">ej.</emphasis>
+ <literal>classname.of.Batcher</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.use_scrollable_resultset</literal>
+ </entry>
+ <entry>
+ Habilita el uso de resultados scrollables de JDBC2 por Hibernate.
+ Esta propiedad sólo es necesaria cuando se usan conexiones
+ JDBC provistas por el usuario, en caso contrario Hibernate usa los
+ metadatos de conexión.
+ <para>
+ <emphasis role="strong">ej.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.use_streams_for_binary</literal>
+ </entry>
+ <entry>
+ Usa flujos (streams) al escribir/leer tipos
+ <literal>binary</literal> o <literal>serializable</literal>
+ a/desde JDBC (propiedad a nivel de sistema).
+ <para>
+ <emphasis role="strong">ej.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.use_get_generated_keys</literal>
+ </entry>
+ <entry>
+ Habilita el uso de <literal>PreparedStatement.getGeneratedKeys()</literal>
+ de JDBC3 para traer claves generadas nativamente después de insertar.
+ Requiere un driver JDBC3+ y un JRE1.4+. Establécela a false si tu
+ driver tiene problemas con los generadores de identificador de Hibernate.
+ Por defecto, se intenta determinar las capacidades del driver usando los
+ metadatos de conexión.
+ <para>
+ <emphasis role="strong">ej.</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.provider_class</literal>
+ </entry>
+ <entry>
+ EL nombre de clase de un <literal>ConnectionProvider</literal> personalizado
+ que provea conexiones JDBC a Hibernate.
+ <para>
+ <emphasis role="strong">ej.</emphasis>
+ <literal>classname.of.ConnectionProvider</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.isolation</literal>
+ </entry>
+ <entry>
+ Establece el nivel de aislamiento de transacción JDBC.
+ Comprueba <literal>java.sql.Connection</literal> para valores
+ significativos pero observa que la mayoría de las bases de
+ datos no soportan todos los niveles de aislamiento.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>1, 2, 4, 8</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.autocommit</literal>
+ </entry>
+ <entry>
+ Habilita compromiso automático (autocommit) para
+ las conexiones JDBC en pool (no recomendado).
+ <para>
+ <emphasis role="strong">ej.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.release_mode</literal>
+ </entry>
+ <entry>
+ Especifica cuándo Hibernate debe liberar las conexiones JDBC.
+ Por defecto, una conexión JDBC es retenida hasta que la sesión
+ es cerrada explícitamente o desconectada. Para un datasource JTA
+ del servidor de aplicaciones, debes usar <literal>after_statement</literal>
+ para liberar agresivamente las conexiones después de cada llamada
+ JDBC. Para una conexión no JTA, frecuentemente tiene sentido liberar
+ la conexión al final de cada transacción, usando
+ <literal>after_transaction</literal>. <literal>auto</literal> eligirá
+ <literal>after_statement</literal> para las estrategias JTA o CMT
+ de transacción y <literal>after_transaction</literal> para la
+ estrategia JDBC de transacción.
+ <para>
+ <emphasis role="strong">ej.</emphasis>
+ <literal>on_close</literal> (por defecto)| <literal>after_transaction</literal> |
+ <literal>after_statement</literal> | <literal>auto</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.<emphasis><propertyName></emphasis></literal>
+ </entry>
+ <entry>
+ Pasa la propiedad JDBC <literal>propertyName</literal>
+ a <literal>DriverManager.getConnection()</literal>.
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jndi.<emphasis><propertyName></emphasis></literal>
+ </entry>
+ <entry>
+ Pasa la propiedad <literal>propertyName</literal>
+ a <literal>InitialContextFactory</literal> de JNDI.
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame="topbot" id="configuration-cache-properties" revision="7">
+ <title>Propiedades de Caché de Hibernate</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Nombre de propiedad</entry>
+ <entry>Propósito</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.cache.provider_class</literal>
+ </entry>
+ <entry>
+ El nombre de clase de un <literal>CacheProvider</literal> personalizado.
+ <para>
+ <emphasis role="strong">ej.</emphasis>
+ <literal>classname.of.CacheProvider</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.use_minimal_puts</literal>
+ </entry>
+ <entry>
+ Optimiza la operación del caché de segundo nivel
+ para minimizar escrituras, al costo de lecturas más frecuentes.
+ Esto es más útil para cachés en cluster y,
+ en Hibernate3, está habilitado por defecto para implementaciones
+ de caché en cluster.
+ <para>
+ <emphasis role="strong">ej.</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.use_query_cache</literal>
+ </entry>
+ <entry>
+ Habilita el caché de lectura, consultas individuales todavía
+ tienen que ponerse cachables.
+ <para>
+ <emphasis role="strong">ej.</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.use_second_level_cache</literal>
+ </entry>
+ <entry>
+ Puede ser usado para deshabilitar completamente el caché
+ de segundo nivel, que está habilitado por defecto para clases
+ que especifican un mapeo <literal><cache></literal>.
+ <para>
+ <emphasis role="strong">ej.</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.query_cache_factory</literal>
+ </entry>
+ <entry>
+ El nombre de clase de una interface <literal>QueryCache</literal>
+ personalizada, por defecto al <literal>StandardQueryCache</literal>
+ prefabricado.
+ <para>
+ <emphasis role="strong">ej.</emphasis>
+ <literal>classname.of.QueryCache</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.region_prefix</literal>
+ </entry>
+ <entry>
+ Un prefijo a usar para los nombres de región
+ del caché de segundo nivel.
+ <para>
+ <emphasis role="strong">ej.</emphasis>
+ <literal>prefix</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.use_structured_entries</literal>
+ </entry>
+ <entry>
+ Fuerza a Hibernate a almacenar los datos en el caché
+ de segundo nivel en un formato más amigable al humano.
+ <para>
+ <emphasis role="strong">ej.</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame="topbot" id="configuration-transaction-properties" revision="8">
+ <title>Propiedades de Transacción de Hibernate</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Nombre de propiedad</entry>
+ <entry>Propósito</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.transaction.factory_class</literal>
+ </entry>
+ <entry>
+ El nombre de clase de un <literal>TransactionFactory</literal>
+ a usar con la API de <literal>Transaction</literal> de Hibernate
+ (por defectoa <literal>JDBCTransactionFactory</literal>).
+ <para>
+ <emphasis role="strong">ej.</emphasis>
+ <literal>classname.of.TransactionFactory</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>jta.UserTransaction</literal>
+ </entry>
+ <entry>
+ Un nombre JNDI usado por <literal>JTATransactionFactory</literal> para
+ obtener la <literal>UserTransaction</literal> JTA del servidor
+ de aplicaciones.
+ <para>
+ <emphasis role="strong">ej.</emphasis>
+ <literal>jndi/composite/name</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.transaction.manager_lookup_class</literal>
+ </entry>
+ <entry>
+ El nombre de clase de un <literal>TransactionManagerLookup</literal>
+ requerido cuando el chaché a nivel de JVM está
+ habilitado o cuando se usa un generador alto/bajo en un
+ entorno JTA.
+ <para>
+ <emphasis role="strong">ej.</emphasis>
+ <literal>classname.of.TransactionManagerLookup</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.transaction.flush_before_completion</literal>
+ </entry>
+ <entry>
+ De habilitarse, la sesión se limpiará (flushed)
+ automáticamente durante la fase previa a la compleción
+ de la transacción. (Muy útil cuando se usa Hibernate
+ con CMT).
+ <para>
+ <emphasis role="strong">ej.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.transaction.auto_close_session</literal>
+ </entry>
+ <entry>
+ De habilitarse, la sesión será cerrada automáticamente
+ durante la fase posterior a la compleción de la transacción.
+ (Muy útil cuando se usa Hibernate con CMT).
+ <para>
+ <emphasis role="strong">ej.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame="topbot" id="configuration-misc-properties" revision="7">
+ <title>Propiedades Misceláneas</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Nombre de propiedad</entry>
+ <entry>Propósito</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.query.factory_class</literal>
+ </entry>
+ <entry>
+ Elige la implementación de parser HQL.
+ <para>
+ <emphasis role="strong">ej.</emphasis>
+ <literal>org.hibernate.hql.ast.ASTQueryTranslatorFactory</literal> or
+ <literal>org.hibernate.hql.classic.ClassicQueryTranslatorFactory</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.query.substitutions</literal>
+ </entry>
+ <entry>
+ Mapeos de símbolos en consultas Hibernate a
+ símbolos SQL. (los símbolos puedem ser
+ nombres de función o literales, por ejemplo).
+ <para>
+ <emphasis role="strong">ej.</emphasis>
+ <literal>hqlLiteral=SQL_LITERAL, hqlFunction=SQLFUNC</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.hbm2ddl.auto</literal>
+ </entry>
+ <entry>
+ Exporta automáticamente DDL de esquema cuando
+ al crear la <literal>SessionFactory</literal>.
+ Con <literal>create-drop</literal>, el esquema de
+ base de datos será desechado cuando la
+ <literal>SessionFactory</literal> se cierre explícitamente.
+ <para>
+ <emphasis role="strong">ej.</emphasis>
+ <literal>update</literal> | <literal>create</literal> | <literal>create-drop</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cglib.use_reflection_optimizer</literal>
+ </entry>
+ <entry>
+ Habilita el uso de CGLIB en vez de refleccón en tiempo de
+ ejecución (propiedad a nivel de sistema). La reflección
+ a veces puede ser útil ante la aparición de problemas.
+ Observa que Hibernate siempre requiere CGLIB incluso si desactivas
+ el optimizador. No puedes establecer esta propiedad en
+ <literal>hibernate.cfg.xml</literal>.
+ <para>
+ <emphasis role="strong">ej.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <sect2 id="configuration-optional-dialects" revision="1">
+ <title>SQL Dialects</title>
+
+ <para>
+ You should always set the <literal>hibernate.dialect</literal> property to the correct
+ <literal>org.hibernate.dialect.Dialect</literal> subclass for your database. If you
+ specify a dialect, Hibernate will use sensible defaults for some of the
+ other properties listed above, saving you the effort of specifying them manually.
+ </para>
+
+ <table frame="topbot" id="sql-dialects" revision="2">
+ <title>Dialectos SQL de Hibernate(<literal>hibernate.dialect</literal>)</title>
+ <tgroup cols="2">
+ <colspec colwidth="1*"/>
+ <colspec colwidth="2.5*"/>
+ <thead>
+ <row>
+ <entry>RDBMS</entry>
+ <entry>Dialecto</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>DB2</entry> <entry><literal>org.hibernate.dialect.DB2Dialect</literal></entry>
+ </row>
+ <row>
+ <entry>DB2 AS/400</entry> <entry><literal>org.hibernate.dialect.DB2400Dialect</literal></entry>
+ </row>
+ <row>
+ <entry>DB2 OS390</entry> <entry><literal>org.hibernate.dialect.DB2390Dialect</literal></entry>
+ </row>
+ <row>
+ <entry>PostgreSQL</entry> <entry><literal>org.hibernate.dialect.PostgreSQLDialect</literal></entry>
+ </row>
+ <row>
+ <entry>MySQL</entry> <entry><literal>org.hibernate.dialect.MySQLDialect</literal></entry>
+ </row>
+ <row>
+ <entry>MySQL con InnoDB</entry> <entry><literal>org.hibernate.dialect.MySQLInnoDBDialect</literal></entry>
+ </row>
+ <row>
+ <entry>MySQL con MyISAM</entry> <entry><literal>org.hibernate.dialect.MySQLMyISAMDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Oracle (cualquier versión)</entry> <entry><literal>org.hibernate.dialect.OracleDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Oracle 9i/10g</entry> <entry><literal>org.hibernate.dialect.Oracle9Dialect</literal></entry>
+ </row>
+ <row>
+ <entry>Sybase</entry> <entry><literal>org.hibernate.dialect.SybaseDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Sybase Anywhere</entry> <entry><literal>org.hibernate.dialect.SybaseAnywhereDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Microsoft SQL Server</entry> <entry><literal>org.hibernate.dialect.SQLServerDialect</literal></entry>
+ </row>
+ <row>
+ <entry>SAP DB</entry> <entry><literal>org.hibernate.dialect.SAPDBDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Informix</entry> <entry><literal>org.hibernate.dialect.InformixDialect</literal></entry>
+ </row>
+ <row>
+ <entry>HypersonicSQL</entry> <entry><literal>org.hibernate.dialect.HSQLDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Ingres</entry> <entry><literal>org.hibernate.dialect.IngresDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Progress</entry> <entry><literal>org.hibernate.dialect.ProgressDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Mckoi SQL</entry> <entry><literal>org.hibernate.dialect.MckoiDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Interbase</entry> <entry><literal>org.hibernate.dialect.InterbaseDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Pointbase</entry> <entry><literal>org.hibernate.dialect.PointbaseDialect</literal></entry>
+ </row>
+ <row>
+ <entry>FrontBase</entry> <entry><literal>org.hibernate.dialect.FrontbaseDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Firebird</entry> <entry><literal>org.hibernate.dialect.FirebirdDialect</literal></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-outerjoin" revision="4">
+ <title>Recuperación por Unión Externa (Outer Join Fetching)</title>
+
+ <para>
+ Si tu base de datos soporta uniones externas del estilo ANSI, Oracle o Sybase, la
+ <emphasis>recuperación por unión externa</emphasis> aumentará
+ frecuentemente el rendimiento limitando el número de llamadas a la base de datos
+ (al costo de más trabajo posiblemente realizado por la base de datos misma).
+ La recuperación por unión externa permite que un grafo completo de objetos
+ conectados por asociaciones muchos-a-uno, uno-a-muchos, muchos-a-muchos y uno-a-uno sea
+ traído en una sola <literal>SELECT</literal> SQL.
+ </para>
+
+ <para>
+ La recuperación por unión externa puede ser deshabilitada
+ <emphasis>globalmente</emphasis> estableciendo la propiedad
+ <literal>hibernate.max_fetch_depth</literal> a <literal>0</literal>.
+ Un valor de <literal>1</literal> o mayor habilita la recuperación
+ por unión externa para asociaciones uno-a-uno y muchos-a-uno que
+ hayan sido mapeadas con <literal>fetch="join"</literal>.
+ </para>
+
+ <para>
+ Ver <xref linkend="performance-fetching"/> para más información.
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-binarystreams" revision="1">
+ <title>Flujos Binarios</title>
+
+ <para>
+ Oracle limita el tamaño de arrays de <literal>byte</literal>
+ que puedan ser pasados a/desde su driver JDBC. Si deseas usar instancias
+ grandes de tipo <literal>binary</literal> o <literal>serializable</literal>,
+ debes habilitar <literal>hibernate.jdbc.use_streams_for_binary</literal>.
+ <emphasis>Esta es una propiedad a nivel de sistema solamente.</emphasis>
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-cacheprovider" revision="2">
+ <title>Caché de segundo nivel y de lectura</title>
+
+ <para>
+ Las propiedades prefijadas por <literal>hibernate.cache</literal>
+ te permiten usar un sistema de caché de segundo nivel
+ en el ámbito de un proceso o cluster con Hibernate.
+ Ver <xref linkend="performance-cache"/> para más detalles.
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-querysubstitution">
+ <title>Sustitución de Lenguaje de Consulta</title>
+
+ <para>
+ Puedes definir nuevos símbolos de consulta de Hibernate usando
+ <literal>hibernate.query.substitutions</literal>. Por ejemplo:
+ </para>
+
+ <programlisting>hibernate.query.substitutions true=1, false=0</programlisting>
+
+ <para>
+ causaría que los símbolos <literal>true</literal> y <literal>false</literal> sean
+ traducidos a literales enteros en el SQL generado.
+ </para>
+
+ <programlisting>hibernate.query.substitutions toLowercase=LOWER</programlisting>
+
+ <para>
+ te permitiría renombrar la función <literal>LOWER</literal> de SQL.
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-statistics" revision="2">
+ <title>Hibernate statistics</title>
+
+ <para>
+ Si habilitas <literal>hibernate.generate_statistics</literal>, Hibernate
+ expondrá un número de métricas que son útiles
+ al afinar un sistema en ejecución vía
+ <literal>SessionFactory.getStatistics()</literal>. Hibernate puede incluso ser
+ configurado para exponer estas estadísticas vía JMX. Lee el
+ Javadoc de las interfaces en <literal>org.hibernate.stats</literal>
+ para más información.
+ </para>
+
+ </sect2>
+ </sect1>
+
+ <sect1 id="configuration-logging">
+ <title>Registros de mensajes (Logging)</title>
+
+ <para>
+ Hibernate registra varios eventos usando commons-logging
+ de Apache.
+ </para>
+
+ <para>
+ El servicio de commons-logging saldrá directamente ya sea a
+ Log4J (si incluyes <literal>log4j.jar</literal> in your classpath) o
+ JDK1.4 logging (al ejecutar bajo JDK1.4 o superior). Puedes descargar
+ Log4J desde <literal>http://logging.apache.org</literal>. Para usar
+ Log4J necesitarás colocar un fichero <literal>log4j.properties</literal>
+ en tu classpath. Un fichero de propiedades de ejemplo se distribuye con
+ Hibernate en el directorio <literal>src/</literal>.
+ </para>
+
+ <para>
+ Recomendamos fuertemente que te familiarices con los registros de mensajes
+ de Hibernate. Se ha puesto un gran trabajo en hacer los registros de Hibernate
+ tan detallados como se puede, sin hacerlos ilegibles. Es un dispositivo esencial
+ en la resolución de problemas. Las categorías de registro
+ más interesantes son las siguientes:
+ </para>
+
+ <table frame="topbot" id="log-categories" revision="2">
+ <title>Categorías de Registro de Hibernate</title>
+ <tgroup cols="2">
+ <colspec colwidth="1*"/>
+ <colspec colwidth="2.5*"/>
+ <thead>
+ <row>
+ <entry>Categoría</entry>
+ <entry>Función</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>org.hibernate.SQL</literal></entry>
+ <entry>Registra todas las sentencias DML de SQL a
+ medida que se ejecutan</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.type</literal></entry>
+ <entry>Registra todos los parámetros JDBC</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.tool.hbm2ddl</literal></entry>
+ <entry>Registra todas las sentencias DDL de SQL a
+ medida que se ejecutan</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.pretty</literal></entry>
+ <entry>
+ Registra el estado de todas las entidades (máximo
+ de 20 entidades) asociadas con la sesión en tiempo
+ de limpieza (flush)
+ </entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.cache</literal></entry>
+ <entry>Registra toda la actividad del caché de segundo nivel</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction</literal></entry>
+ <entry>Registra la actividad relacionada con la transacción</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.jdbc</literal></entry>
+ <entry>Registra toda adquisición de recursos JDBC</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.hql.ast</literal></entry>
+ <entry>Regista los ASTs de HQL y SQL, así como
+ otra información sobre análisis de consultas.</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.secure</literal></entry>
+ <entry>Registra todas las peticiones de autorización JAAS</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate</literal></entry>
+ <entry>
+ Registra todo (mucha información, pero muy útil
+ para la resolución de problemas)
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ Al desarrollar aplicacinoes con Hibernate, casi siempre debes trabajar con
+ <literal>debug</literal> habilitado para la categoría <literal>org.hibernate.SQL</literal>
+ o, alternativamente, la propiedad <literal>hibernate.show_sql</literal> habilitada.
+ </para>
+
+
+ </sect1>
+
+ <sect1 id="configuration-namingstrategy">
+ <title>Implementando una <literal>NamingStrategy</literal></title>
+
+ <para>
+ La interface <literal>org.hibernate.cfg.NamingStrategy</literal> te permite
+ especificar un "estándar de nombrado" para objetos de la base de datos
+ y elementos de esquema.
+ </para>
+
+ <para>
+ Puedes proveer reglas para generar automáticamente identificadores de
+ base de datos a partir de identificadores JDBC o para procesar nombres
+ "lógicos" de columnas y tablas dados en el fichero de mapeo en nombres
+ "físicos" de columnas y tablas. Esta funcionalidad ayuda a reducir
+ la verborragia del documento de mapeo, eliminando ruido repetitivo
+ (prefijos <literal>TBL_</literal>, por ejemplo). La estrategia por defecto
+ usada por Hibernate mínima en absoluto.
+ </para>
+
+ <para>
+ Puedes especificar una estrategia diferente llamando a
+ <literal>Configuration.setNamingStrategy()</literal> antes de agregar los mapeos:
+ </para>
+
+ <programlisting><![CDATA[SessionFactory sf = new Configuration()
+ .setNamingStrategy(ImprovedNamingStrategy.INSTANCE)
+ .addFile("Item.hbm.xml")
+ .addFile("Bid.hbm.xml")
+ .buildSessionFactory();]]></programlisting>
+
+ <para>
+ <literal>org.hibernate.cfg.ImprovedNamingStrategy</literal>
+ es una estrategia prefabricada que puede ser un punto de
+ partida útil para algunas aplicaciones.
+ </para>
+
+ </sect1>
+
+ <sect1 id="configuration-xmlconfig" revision="2">
+ <title>Fichero de configuración XML</title>
+
+ <para>
+ Un enfoque alternativo de configuración es especificar una
+ configuración completa en un fichero llamado <literal>hibernate.cfg.xml</literal>.
+ Este fichero puede ser usado como un remplazo del fichero <literal>hibernate.properties</literal> o,
+ si ambos están presentes, para sobrescribir propiedades.
+ </para>
+
+ <para>
+ El fichero de configuración XML se espera por defecto en la
+ raíz o tu <literal>CLASSPATH</literal>. He aquí un ejemplo:
+ </para>
+
+ <programlisting><![CDATA[<?xml version='1.0' encoding='utf-8'?>
+<!DOCTYPE hibernate-configuration PUBLIC
+ "-//Hibernate/Hibernate Configuration DTD//EN"
+ "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
+
+<hibernate-configuration>
+
+ <!-- a SessionFactory instance listed as /jndi/name -->
+ <session-factory
+ name="java:hibernate/SessionFactory">
+
+ <!-- properties -->
+ <property name="connection.datasource">java:/comp/env/jdbc/MyDB</property>
+ <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
+ <property name="show_sql">false</property>
+ <property name="transaction.factory_class">
+ org.hibernate.transaction.JTATransactionFactory
+ </property>
+ <property name="jta.UserTransaction">java:comp/UserTransaction</property>
+
+ <!-- mapping files -->
+ <mapping resource="org/hibernate/auction/Item.hbm.xml"/>
+ <mapping resource="org/hibernate/auction/Bid.hbm.xml"/>
+
+ <!-- cache settings -->
+ <class-cache class="org.hibernate.auction.Item" usage="read-write"/>
+ <class-cache class="org.hibernate.auction.Bid" usage="read-only"/>
+ <collection-cache collection="org.hibernate.auction.Item.bids" usage="read-write"/>
+
+ </session-factory>
+
+</hibernate-configuration>]]></programlisting>
+
+ <para>
+ Como puedes ver, la ventaja de este enfoque es la externalización de los
+ nombres de los fichero de mapeo a configuración. El
+ <literal>hibernate.cfg.xml</literal> es también más conveniente
+ una vez que hayas afinado el caché de Hibernate. Observa que elección
+ tuya usar ya sea <literal>hibernate.properties</literal> o
+ <literal>hibernate.cfg.xml</literal>, ambos son equivalentes, excepto por los
+ beneficios de usar la sintaxis XML arriba mencionados.
+ </para>
+
+ <para>
+ Con la configuración XML, arrancar Hibernate es tan simple como
+ </para>
+
+ <programlisting><![CDATA[SessionFactory sf = new Configuration().configure().buildSessionFactory();]]></programlisting>
+
+ <para>
+ Puedes tomar un fichero XML diferente usando
+ </para>
+
+ <programlisting><![CDATA[SessionFactory sf = new Configuration()
+ .configure("catdb.cfg.xml")
+ .buildSessionFactory();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="configuration-j2ee" revision="1">
+ <title>Integració con Servidores de Aplicaciones J2EE</title>
+
+ <para>
+ Hibernate tiene los siguientes puntos de integración con la
+ infraestructura J2EE:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Datasources manejados por contenedor</emphasis>: Hibernate puede usar
+ conexiones JDBC manejadas por el contenedor y provistas a través de JNDI.
+ Usualmente, un <literal>TransactionManager</literal> compatible con JTA y un
+ <literal>ResourceManager</literal> cuidan del manejo de transacciones (CMT),
+ esp. manejo de transacciones distribuídas a través de varios
+ datasources. Puedes también, por supuesto, demarcar los límites
+ de las transacciones programáticamente (BMT) o podrías querer usar
+ para esto la API opcional de <literal>Transaction</literal> de Hibernate para
+ mantener tu código portable.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Ligamento Automático JNDI</emphasis>: Hibernate puede ligar sus
+ <literal>SessionFactory</literal> a JNDI después del arranque.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Ligamento de Sesión JTA:</emphasis>
+ La <literal>Session</literal> de Hibernate puede ser ligada automáticamente
+ al ámbito de transacciones JTA si usas EJBs. Simplemente busca la
+ <literal>SessionFactory</literal> de JNDI y obtén la <literal>Session</literal>
+ actual. Deja que Hibernate cuide de limpiar y cerrar la <literal>Session</literal>
+ cuando se complete tu transacción JTA. La demarcación de transacción
+ es declarativa, en descriptores de despliegue de EJB.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Despliegue JMX:</emphasis> Si tienes un servidor de aplicaciones capaz
+ de JMX (por ejemplo, JBoss AS), puedes optar por desplegar Hibernate como un MBean
+ manejado. Esto te ahorra el código de una línea de arranque para
+ construir tu <literal>SessionFactory</literal> desde una <literal>Configuration</literal>.
+ El contenedor arrancará tu <literal>HibernateService</literal>, e idealmente también
+ cuidará de las dependencias entre servicios (El datasource debe estar
+ disponible antes que arranque Hibernate, etc).
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Dependiendo de tu entorno, podrías tener que establecer la opción
+ de configuración <literal>hibernate.connection.aggressive_release</literal>
+ a true si tu servidor de aplicaciones muestra excepciones "connection containment".
+ </para>
+
+ <sect2 id="configuration-optional-transactionstrategy" revision="3">
+ <title>Configuración de la estrategia de transacción</title>
+
+ <para>
+ La API de <literal>Session</literal> de Hibernate es independiente de cualquier
+ demarcación de transacción en tu arquitectura. Si dejas que Hibernate
+ use JDBC directamente, a través de un pool de conexiones. puedes comenzar y
+ acabar tus transacciones llamando la API de JDBC. Si ejecutas en un servidor de
+ aplicaciones J2EE, podréas querer usar transacciones manejadas por bean y
+ llamar la API de JTA y <literal>UserTransaction</literal> cuando sea necesario.
+ </para>
+
+ <para>
+ Para mantener tu código portable entre estos dos (y otros) entornos recomendamos la API
+ de <literal>Transaction</literal> de Hibernate, que envuelve y oculta el sistema subyacente.
+ Tienes que especificar una clase fábrica para las instancias de <literal>Transaction</literal>
+ estableciendo la propiedad de configuración <literal>hibernate.transaction.factory_class</literal>
+ de Hibernate.
+ </para>
+
+ <para>
+ Hay tres elecciones estándar (prefabricadas):
+ </para>
+
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term><literal>org.hibernate.transaction.JDBCTransactionFactory</literal></term>
+ <listitem>
+ <para>delega a transacciones de base de datos (JDBC) (por defecto)</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>org.hibernate.transaction.JTATransactionFactory</literal></term>
+ <listitem>
+ <para>
+ delega a transacciones manejadas por contenedor si una transacción
+ existente estó por debajo en este contexto (ej. método de un
+ bean de sesión EJB), en otro caso una nueva transacción es
+ comenzada y se usan transacciones manejadas por bean.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>org.hibernate.transaction.CMTTransactionFactory</literal></term>
+ <listitem>
+ <para>delega a transacciones JTA manejadas por contenedor</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>
+ Puedes definir también tus propias estrategias de transacción
+ (para un servicio de transacción CORBA, por ejemplo).
+ </para>
+
+ <para>
+ Algunas funcionalidades en Hibernate (ej, el caché de segundo nivel, ligamento
+ automático de JTA y Session, etc.) requieren acceso al <literal>TransactionManager</literal>
+ de JTA en un entorno manejado. En un servidor de aplicaciones tienes que especificar
+ cómo Hibernate debe obtener una referencia al <literal>TransactionManager</literal>,
+ pues J2EE no estandariza un solo mecanismo:
+ </para>
+
+ <table frame="topbot" id="jtamanagerlookup" revision="1">
+ <title>TransactionManagers de JTA</title>
+ <tgroup cols="2">
+ <colspec colwidth="2.5*"/>
+ <colspec colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Transaction Factory</entry>
+ <entry align="center">Servidor de Aplicaciones</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>org.hibernate.transaction.JBossTransactionManagerLookup</literal></entry>
+ <entry align="center">JBoss</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.WeblogicTransactionManagerLookup</literal></entry>
+ <entry align="center">Weblogic</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.WebSphereTransactionManagerLookup</literal></entry>
+ <entry align="center">WebSphere</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.WebSphereExtendedJTATransactionLookup</literal></entry>
+ <entry align="center">WebSphere 6</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.OrionTransactionManagerLookup</literal></entry>
+ <entry align="center">Orion</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.ResinTransactionManagerLookup</literal></entry>
+ <entry align="center">Resin</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.JOTMTransactionManagerLookup</literal></entry>
+ <entry align="center">JOTM</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.JOnASTransactionManagerLookup</literal></entry>
+ <entry align="center">JOnAS</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.JRun4TransactionManagerLookup</literal></entry>
+ <entry align="center">JRun4</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.BESTransactionManagerLookup</literal></entry>
+ <entry align="center">Borland ES</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-jndi" revision="2">
+ <title><literal>SessionFactory</literal> ligada a JNDI</title>
+
+ <para>
+ Una <literal>SessionFactory</literal> de Hibernate ligada a JNDI puede simplificar
+ la obtención de la fábrica y la creación de nuevas
+ <literal>Session</literal>s. Observa que esto no está relacionado a un
+ <literal>Datasource</literal> ligado a JNDI, simplemente ambos usan el mismo
+ registro!
+ </para>
+
+ <para>
+ Si deseas tener la <literal>SessionFactory</literal> ligada a un espacio de nombres de JNDI,
+ especifica un nombre (ej. <literal>java:hibernate/SessionFactory</literal>) usando la
+ propiedad <literal>hibernate.session_factory_name</literal>. Si esta propiedad es omitida,
+ la <literal>SessionFactory</literal> no será ligada a JNDI (Esto es especialmente
+ útil en entornos con una implementació JNDI de sólo lectura por defecto,
+ ej. Tomcat.)
+ </para>
+
+ <para>
+ Al ligar la <literal>SessionFactory</literal> a JNDI, Hibernate usará los valores de
+ <literal>hibernate.jndi.url</literal>, <literal>hibernate.jndi.class</literal> para instanciar
+ un contexto inicial. Si étos no se especifican, se usará el
+ <literal>InitialContext</literal> por defecto.
+ </para>
+
+ <para>
+ Hibernate colocará automáticamente la <literal>SessionFactory</literal>
+ en JNDI después que llames a <literal>cfg.buildSessionFactory()</literal>.
+ Esto significa que tendrás al menos esta llamada en algún código
+ de arranque (o clase de utilidad) en tu aplicación, a menos qie uses el despliegue
+ JMX con el <literal>HibernateService</literal> (discutido luego).
+ </para>
+
+ <para>
+ Si usas una <literal>SessionFactory</literal> de JNDI, un EJB o cualquier otra
+ clase puede obtener la <literal>SessionFactory</literal> usando una búsqueda
+ JNDI. Observa que esta configuración no es necesaria si usas la clase de ayuda
+ <literal>HibernateUtil</literal> introducida en el capítulo uno, que actúa
+ como un registro Singleton. Sin embargo, <literal>HibernateUtil</literal> es más
+ común en un entorno no manejado.
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-j2ee-currentsession" revision="1">
+ <title>Ligado automático de JTA y Session</title>
+
+ <para>
+ Para entornos no manejados hemos sugerido <literal>HibernateUtil</literal> con una
+ <literal>SessionFactory</literal> estática, y administración de la
+ <literal>Session</literal> de Hibernate. Este enfoque no es fácil de usar
+ en un entorno EJB, al poder ejecutarse muchos EJBs dentro de la misma transacción
+ pero no en la misma hebra. Recomendados que ligues la <literal>SessionFactory</literal>
+ a JNDI en un entorno manejado.
+ </para>
+
+ <para>
+ En vez de rodar tu propia utilidad de <literal>ThreadLocal</literal>,
+ usa el método <literal>getCurrentSession()</literal> en la
+ <literal>SessionFactory</literal> para obtener una <literal>Session</literal>
+ de Hibernate. Si no hubiese una <literal>Session</literal> de Hibernate en la
+ transacción JTA actual, se arrancará y asignará una.
+ Ambas opciones de configuración <literal>hibernate.transaction.flush_before_completion</literal>
+ y <literal>hibernate.transaction.auto_close_session</literal>, serán establecidas
+ automáticamente para cada <literal>Session</literal> que obtengas con
+ <literal>getCurrentSession()</literal>, de modo que éstas serán
+ limpiadas (flushed) y cerradas automáticamente cuando el contenedor complete
+ las transacciones JTA.
+ </para>
+
+ <para>
+ Si tu, por ejemplo, usas el patrón de diseño DAO para escribir tu
+ capa de persistencia, todos los DAO's buscan la <literal>SessionFactory</literal>
+ cuando se necesite y abren la sesión "actual". No hay necesidad de pasar
+ las instancias de <literal>SessionFactory</literal> o <literal>Session</literal>
+ alrededor entre el código de control y el código DAO.
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-j2ee-jmx" revision="1">
+ <title>Despliegue JMX</title>
+
+ <para>
+ La línea <literal>cfg.buildSessionFactory()</literal> todavía tiene
+ que ser ejecutada en algun sitio para obtener una <literal>SessionFactory</literal>
+ en JNDI. Puedes hacer esto bien en un bloque inicializador <literal>static</literal>
+ (como aquel en <literal>HibernateUtil</literal>) o bien despliegas Hibernate como un
+ <emphasis>servicio manejado</emphasis>.
+ </para>
+
+ <para>
+ Hibernate se distribuye con <literal>org.hibernate.jmx.HibernateService</literal>
+ para despliegue en un servidor de aplicaciones con capacidades JMX, como JBoss AS.
+ El despliegue y la configuracón reales son específicos del vendedor.
+ He aquí un <literal>jboss-service.xml</literal> de ejemplo para JBoss 4.0.x:
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<server>
+
+<mbean code="org.hibernate.jmx.HibernateService"
+ name="jboss.jca:service=HibernateFactory,name=HibernateFactory">
+
+ <!-- Required services -->
+ <depends>jboss.jca:service=RARDeployer</depends>
+ <depends>jboss.jca:service=LocalTxCM,name=HsqlDS</depends>
+
+ <!-- Bind the Hibernate service to JNDI -->
+ <attribute name="JndiName">java:/hibernate/SessionFactory</attribute>
+
+ <!-- Datasource settings -->
+ <attribute name="Datasource">java:HsqlDS</attribute>
+ <attribute name="Dialect">org.hibernate.dialect.HSQLDialect</attribute>
+
+ <!-- Transaction integration -->
+ <attribute name="TransactionStrategy">
+ org.hibernate.transaction.JTATransactionFactory</attribute>
+ <attribute name="TransactionManagerLookupStrategy">
+ org.hibernate.transaction.JBossTransactionManagerLookup</attribute>
+ <attribute name="FlushBeforeCompletionEnabled">true</attribute>
+ <attribute name="AutoCloseSessionEnabled">true</attribute>
+
+ <!-- Fetching options -->
+ <attribute name="MaximumFetchDepth">5</attribute>
+
+ <!-- Second-level caching -->
+ <attribute name="SecondLevelCacheEnabled">true</attribute>
+ <attribute name="CacheProviderClass">org.hibernate.cache.EhCacheProvider</attribute>
+ <attribute name="QueryCacheEnabled">true</attribute>
+
+ <!-- Logging -->
+ <attribute name="ShowSqlEnabled">true</attribute>
+
+ <!-- Mapping files -->
+ <attribute name="MapResources">auction/Item.hbm.xml,auction/Category.hbm.xml</attribute>
+
+</mbean>
+
+</server>]]></programlisting>
+
+ <para>
+ Este fichero es desplegado en un directorio llamado <literal>META-INF</literal> y
+ empaquetado en un fichero JAR con la extensión <literal>.sar</literal>
+ (fichero de servicio). También necesitas empaquetar Hibernate, sus bibliotecas
+ de terceros requeridas, tus clases persistentes compiladas, así como tus ficheros de mapeo
+ en el mismo fichero. Tus beans de empresa (usualmente beans de sesión) pueden ser
+ mantenidos en su propio fichero JAR, pero debes incluir este fichero EJB JAR en el fichero
+ de servicio principal para obtener una unidad desplegable (en caliente). Consulta la documentación
+ de JBoss AS para más información sobre el servicio JMX y despliegue de EJB.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/events.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/events.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/events.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,233 @@
+<chapter id="events">
+ <title>Interceptores y eventos</title>
+
+ <para>
+ Frecuentemente es útil para la aplicación reaccionar a ciertos eventos que ocurran dentro de Hibernate.
+ Esto permite la implementación de ciertos tipos de funcionalidade genérica, y extensión de la
+ funcionalidad de Hibernate.
+ </para>
+
+ <sect1 id="objectstate-interceptors" revision="1">
+ <title>Interceptores</title>
+
+ <para>
+ La interface <literal>Interceptor</literal> provee callbacks desde la sesión a la aplicación
+ permitiendo a ésta última inspeccionar y/o manipular las propiedades de un objeto persistente
+ antes que sea salvado, actualizado, borrado o cargado. Un uso posible de esto es seguir la pista
+ de información de auditoría. Por ejemplo, el siguiente <literal>Interceptor</literal> establece
+ automáticamente el <literal>createTimestamp</literal> cuando un <literal>Auditable</literal> es
+ creado y actualiza la propiedad <literal>lastUpdateTimestamp</literal> cuando un
+ <literal>Auditable</literal> es acutalizado.
+ </para>
+
+ <programlisting><![CDATA[package org.hibernate.test;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.Iterator;
+
+import org.hibernate.Interceptor;
+import org.hibernate.type.Type;
+
+public class AuditInterceptor implements Interceptor, Serializable {
+
+ private int updates;
+ private int creates;
+
+ public void onDelete(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+ // do nothing
+ }
+
+ public boolean onFlushDirty(Object entity,
+ Serializable id,
+ Object[] currentState,
+ Object[] previousState,
+ String[] propertyNames,
+ Type[] types) {
+
+ if ( entity instanceof Auditable ) {
+ updates++;
+ for ( int i=0; i < propertyNames.length; i++ ) {
+ if ( "lastUpdateTimestamp".equals( propertyNames[i] ) ) {
+ currentState[i] = new Date();
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public boolean onLoad(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+ return false;
+ }
+
+ public boolean onSave(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+
+ if ( entity instanceof Auditable ) {
+ creates++;
+ for ( int i=0; i<propertyNames.length; i++ ) {
+ if ( "createTimestamp".equals( propertyNames[i] ) ) {
+ state[i] = new Date();
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public void postFlush(Iterator entities) {
+ System.out.println("Creations: " + creates + ", Updates: " + updates);
+ }
+
+ public void preFlush(Iterator entities) {
+ updates=0;
+ creates=0;
+ }
+
+ ...
+
+}]]></programlisting>
+
+ <para>
+ El interceptor podría ser especificado cuando se crea la sesión:
+ </para>
+
+ <programlisting><![CDATA[Session session = sf.openSession( new AuditInterceptor() );]]></programlisting>
+
+ <para>
+ Puedes además establecer un interceptor a un nivel global, usando la <literal>Configuration</literal>:
+ </para>
+
+ <programlisting><![CDATA[new Configuration().setInterceptor( new AuditInterceptor() );]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="objectstate-events" revision="2">
+ <title>Sistema de eventos</title>
+
+ <para>
+ Si tienes que reaccionar a eventos particulares en tu capa de persistencia, puedes también la
+ arquitectura de <emphasis>eventos</emphasis> de Hibernate3. El sistema de eventos puede ser usado
+ en adición o como un remplazo a los interceptores.
+ </para>
+
+ <para>
+ Esencialmente todos los métodos de la interface <literal>Session</literal> se correlacionan
+ con un evento. Tienes un <literal>LoadEvent</literal>, un <literal>FlushEvent</literal>, etc
+ (consulta el DTD del fichero de configuración XML o el paquete <literal>org.hibernate.event</literal>
+ para la lista completa de tipos de evento definidos). Cuando se hace una petición de uno de estos
+ métodos, la <literal>Session</literal> de Hibernate genera un evento apropiado y se lo pasa
+ al oyente (listener) de eventos configurado para ese tipo. De fábrica, estos oyentes implementan
+ el mismo procesamiento en los que siempre resultan aquellos métodos. Sin embargo, eres libre de
+ implementar una personalización de una de las interfaces oyentes (es decir, el
+ <literal>LoadEvent</literal> es procesado por la implementación registrada de la interface
+ <literal>LoadEventListener</literal>), en cuyo caso su implementación sería responsable
+ de procesar cualquier petición <literal>load()</literal> hecha a la <literal>Session</literal>.
+ </para>
+
+ <para>
+ Los oyentes deben ser considerados efectivamente singletons; quiere decir, que son compartidos
+ entre las peticiones, y por lo tanto no guardan ningún estado en variables de instancia.
+ </para>
+
+ <para>
+ Un oyente personalizado debe implementar la interface apropiada para el evento que quiere procesar y/o
+ extender una de las clases base de conveniencia (o incluso los oyentes de eventos por defecto
+ usados por Hibernate de fábrica al ser éstos declarados non-final para este propósito). Los
+ oyentes personalizados pueden ser registrados programáticamente a través del objeto
+ <literal>Configuration</literal>, o especificados en el XML de configuración de Hibernate
+ (la declaración declarativa a través del fichero de propiedades no está soportada).
+ He aquí un ejemplo de un oyente personalizado de eventos load:
+ </para>
+
+ <programlisting><![CDATA[public class MyLoadListener extends DefaultLoadEventListener {
+ // this is the single method defined by the LoadEventListener interface
+ public Object onLoad(LoadEvent event, LoadEventListener.LoadType loadType)
+ throws HibernateException {
+ if ( !MySecurity.isAuthorized( event.getEntityClassName(), event.getEntityId() ) ) {
+ throw MySecurityException("Unauthorized access");
+ }
+ return super.onLoad(event, loadType);
+ }
+}]]></programlisting>
+
+ <para>
+ Necesitas además una entrada de configuración diciéndole a Hibernate que use el
+ oyente en vez del oyente por defecto:
+ </para>
+
+<programlisting><![CDATA[<hibernate-configuration>
+ <session-factory>
+ ...
+ <listener type="load" class="MyLoadListener"/>
+ </session-factory>
+</hibernate-configuration>]]></programlisting>
+
+ <para>
+ En cambio, puedes registrarlo programáticamente:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = new Configuration();
+cfg.getSessionEventListenerConfig().setLoadEventListener( new MyLoadListener() );]]></programlisting>
+
+ <para>
+ Los oyentes registrados declarativamente no pueden compartir instancias. Si el mismo nombre de clase es
+ usado en múltiples elementos <literal><listener/></literal>, cada referencia resultará en una instancia
+ separada de esa clase. Si necesitas la capacidad de compartir instancias de oyentes entre tipos de oyente
+ debes usar el enfoque de registración programática.
+ </para>
+
+ <para>
+ ¿Por qué implementar una interface y definir el tipo espcífico durante la configuración?
+ Bueno, una implementación de oyente podría implementar múltiples interfaces de oyente
+ de eventos. Teniendo el tipo definido adicionalmente durante la registración lo hace más
+ fácil para activar o desactivar oyentes personalizados durante la configuración.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-decl-security">
+ <title>Seguridad declarativa de Hibernate</title>
+ <para>
+ Usualmente, la seguridad declarativa en aplicaciones Hibernate es manejada en una capa de fachada
+ de sesión. Ahora, Hibernate3 permite que ciertas acciones sean permitidas vía JACC, y autorizadas vía
+ JAAS. Esta en una funcionalidad opcional construída encima de la arquitectura de eventos.
+ </para>
+
+ <para>
+ Primero, debes configurar los oyentes de eventos apropiados, para habilitar el uso de
+ autorización JAAS.
+ </para>
+
+ <programlisting><![CDATA[<listener type="pre-delete" class="org.hibernate.secure.JACCPreDeleteEventListener"/>
+<listener type="pre-update" class="org.hibernate.secure.JACCPreUpdateEventListener"/>
+<listener type="pre-insert" class="org.hibernate.secure.JACCPreInsertEventListener"/>
+<listener type="pre-load" class="org.hibernate.secure.JACCPreLoadEventListener"/>]]></programlisting>
+
+ <para>
+ Seguido, aún en <literal>hibernate.cfg.xml</literal>, liga los permisos a roles:
+ </para>
+
+ <programlisting><![CDATA[<grant role="admin" entity-name="User" actions="insert,update,read"/>
+<grant role="su" entity-name="User" actions="*"/>]]></programlisting>
+
+ <para>
+ Los nombres de role son los roles entendidos por tu proveedor de JACC.
+ </para>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/example_mappings.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/example_mappings.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/example_mappings.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,654 @@
+<chapter id="example-mappings">
+ <title>Ejemplo: Varios Mapeos</title>
+
+ <para>
+ Este capítulo muestra mapeos de asociaciones más complejos.
+ </para>
+
+ <sect1 id="example-mappings-emp">
+ <title>Empleador/Empleado</title>
+
+ <para>
+ El siguiente modelo de la relación entre <literal>Employer</literal> y <literal>Employee</literal>
+ usa una clase de entidad real (<literal>Employment</literal>) para representar la asociación.
+ Esto se ha hecho esto porque podría haber más de un período de empleo para los mismos dos participantes.
+ Se usan componentes para modelar valores monetarios y nombres de empleado.
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/EmployerEmployee.gif" format="GIF" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/EmployerEmployee.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ He aquí un documento de mapeo posible:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Employer" table="employers">
+ <id name="id">
+ <generator class="sequence">
+ <param name="sequence">employer_id_seq</param>
+ </generator>
+ </id>
+ <property name="name"/>
+ </class>
+
+ <class name="Employment" table="employment_periods">
+
+ <id name="id">
+ <generator class="sequence">
+ <param name="sequence">employment_id_seq</param>
+ </generator>
+ </id>
+ <property name="startDate" column="start_date"/>
+ <property name="endDate" column="end_date"/>
+
+ <component name="hourlyRate" class="MonetaryAmount">
+ <property name="amount">
+ <column name="hourly_rate" sql-type="NUMERIC(12, 2)"/>
+ </property>
+ <property name="currency" length="12"/>
+ </component>
+
+ <many-to-one name="employer" column="employer_id" not-null="true"/>
+ <many-to-one name="employee" column="employee_id" not-null="true"/>
+
+ </class>
+
+ <class name="Employee" table="employees">
+ <id name="id">
+ <generator class="sequence">
+ <param name="sequence">employee_id_seq</param>
+ </generator>
+ </id>
+ <property name="taxfileNumber"/>
+ <component name="name" class="Name">
+ <property name="firstName"/>
+ <property name="initial"/>
+ <property name="lastName"/>
+ </component>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Y he aquí el esquema de tablas generado por <literal>SchemaExport</literal>.
+ </para>
+
+ <programlisting><![CDATA[create table employers (
+ id BIGINT not null,
+ name VARCHAR(255),
+ primary key (id)
+)
+
+create table employment_periods (
+ id BIGINT not null,
+ hourly_rate NUMERIC(12, 2),
+ currency VARCHAR(12),
+ employee_id BIGINT not null,
+ employer_id BIGINT not null,
+ end_date TIMESTAMP,
+ start_date TIMESTAMP,
+ primary key (id)
+)
+
+create table employees (
+ id BIGINT not null,
+ firstName VARCHAR(255),
+ initial CHAR(1),
+ lastName VARCHAR(255),
+ taxfileNumber VARCHAR(255),
+ primary key (id)
+)
+
+alter table employment_periods
+ add constraint employment_periodsFK0 foreign key (employer_id) references employers
+alter table employment_periods
+ add constraint employment_periodsFK1 foreign key (employee_id) references employees
+create sequence employee_id_seq
+create sequence employment_id_seq
+create sequence employer_id_seq]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-mappings-authorwork">
+ <title>Autor/Obra</title>
+
+ <para>
+ Considera el siguiente modelo de las relaciones entre <literal>Work</literal>,
+ <literal>Author</literal> y <literal>Person</literal>. Representamos la relación entre <literal>Work</literal>
+ y <literal>Author</literal> como una asociación muchos-a-muchos. Elegimos representar la relación entre
+ <literal>Author</literal> y <literal>Person</literal> como una asociación uno-a-uno. Otra posibilidad
+ hubiese sido que <literal>Author</literal> extendiera <literal>Person</literal>.
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/AuthorWork.gif" format="GIF" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/AuthorWork.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ El siguiente documento de mapeo representa estas relaciones correctamente:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Work" table="works" discriminator-value="W">
+
+ <id name="id" column="id">
+ <generator class="native"/>
+ </id>
+ <discriminator column="type" type="character"/>
+
+ <property name="title"/>
+ <set name="authors" table="author_work">
+ <key column name="work_id"/>
+ <many-to-many class="Author" column name="author_id"/>
+ </set>
+
+ <subclass name="Book" discriminator-value="B">
+ <property name="text"/>
+ </subclass>
+
+ <subclass name="Song" discriminator-value="S">
+ <property name="tempo"/>
+ <property name="genre"/>
+ </subclass>
+
+ </class>
+
+ <class name="Author" table="authors">
+
+ <id name="id" column="id">
+ <!-- The Author must have the same identifier as the Person -->
+ <generator class="assigned"/>
+ </id>
+
+ <property name="alias"/>
+ <one-to-one name="person" constrained="true"/>
+
+ <set name="works" table="author_work" inverse="true">
+ <key column="author_id"/>
+ <many-to-many class="Work" column="work_id"/>
+ </set>
+
+ </class>
+
+ <class name="Person" table="persons">
+ <id name="id" column="id">
+ <generator class="native"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Hay cuatro tablas en este mapeo. <literal>works</literal>, <literal>authors</literal> y <literal>persons</literal>
+ tienen los datos de obra, autor y persona respectivamente. <literal>author_work</literal> es una tabla de
+ asociación enlazando autores a obras. He aquí el esquema de tablas, tal como fue generado por
+ <literal>SchemaExport</literal>.
+ </para>
+
+ <programlisting><![CDATA[create table works (
+ id BIGINT not null generated by default as identity,
+ tempo FLOAT,
+ genre VARCHAR(255),
+ text INTEGER,
+ title VARCHAR(255),
+ type CHAR(1) not null,
+ primary key (id)
+)
+
+create table author_work (
+ author_id BIGINT not null,
+ work_id BIGINT not null,
+ primary key (work_id, author_id)
+)
+
+create table authors (
+ id BIGINT not null generated by default as identity,
+ alias VARCHAR(255),
+ primary key (id)
+)
+
+create table persons (
+ id BIGINT not null generated by default as identity,
+ name VARCHAR(255),
+ primary key (id)
+)
+
+alter table authors
+ add constraint authorsFK0 foreign key (id) references persons
+alter table author_work
+ add constraint author_workFK0 foreign key (author_id) references authors
+alter table author_work
+ add constraint author_workFK1 foreign key (work_id) references works]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-mappings-customerorderproduct">
+ <title>Cliente/Orden/Producto</title>
+
+ <para>
+ Ahora considera un modelo de las relaciones entre <literal>Customer</literal>,
+ <literal>Order</literal> y <literal>LineItem</literal> y <literal>Product</literal>.
+ Hay una asociación uno-a-muchos entre <literal>Customer</literal> y <literal>Order</literal>,
+ pero, ¿cómo deberíamos representar <literal>Order</literal> / <literal>LineItem</literal> / <literal>Product</literal>?
+ He elegido mapear <literal>LineItem</literal> como una clase de asociación representando la
+ asociación muchos-a-muchos entre <literal>Order</literal> y <literal>Product</literal>. En Hibernate,
+ esto se llama un elemento compuesto.
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/CustomerOrderProduct.gif" format="GIF" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/CustomerOrderProduct.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ El documento de mapeo:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Customer" table="customers">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="name"/>
+ <set name="orders" inverse="true">
+ <key column="customer_id"/>
+ <one-to-many class="Order"/>
+ </set>
+ </class>
+
+ <class name="Order" table="orders">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="date"/>
+ <many-to-one name="customer" column="customer_id"/>
+ <list name="lineItems" table="line_items">
+ <key column="order_id"/>
+ <list-index column="line_number"/>
+ <composite-element class="LineItem">
+ <property name="quantity"/>
+ <many-to-one name="product" column="product_id"/>
+ </composite-element>
+ </list>
+ </class>
+
+ <class name="Product" table="products">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="serialNumber"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ <literal>customers</literal>, <literal>orders</literal>, <literal>line_items</literal> y
+ <literal>products</literal> tienen los datos de cliente, orden, ítem de línea de orden y producto
+ respectivamente. Además <literal>line_items</literal> actúa como una tabla de asociación enlazando
+ órdenes con productos.
+ </para>
+
+ <programlisting><![CDATA[create table customers (
+ id BIGINT not null generated by default as identity,
+ name VARCHAR(255),
+ primary key (id)
+)
+
+create table orders (
+ id BIGINT not null generated by default as identity,
+ customer_id BIGINT,
+ date TIMESTAMP,
+ primary key (id)
+)
+
+create table line_items (
+ line_number INTEGER not null,
+ order_id BIGINT not null,
+ product_id BIGINT,
+ quantity INTEGER,
+ primary key (order_id, line_number)
+)
+
+create table products (
+ id BIGINT not null generated by default as identity,
+ serialNumber VARCHAR(255),
+ primary key (id)
+)
+
+alter table orders
+ add constraint ordersFK0 foreign key (customer_id) references customers
+alter table line_items
+ add constraint line_itemsFK0 foreign key (product_id) references products
+alter table line_items
+ add constraint line_itemsFK1 foreign key (order_id) references orders]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="misc">
+ <title>Mapeos misceláneos de ejemplo</title>
+
+ <para>
+ Todos estos ejemplos están tomados de la batería de pruebas de Hibernate.
+ Encontrarás muchos otros mapeos de ejemplo útiles allí. Mira en la carpeta
+ <literal>test</literal> de la distribución de Hibernate.
+ </para>
+
+ <para>POR HACER: poner palabras alrededor de este material</para>
+
+ <sect2 id="example-mappings-typed-onetone">
+ <title>Asociación uno-a-uno "Tipificada"</title>
+<programlisting><![CDATA[<class name="Person">
+ <id name="name"/>
+ <one-to-one name="address"
+ cascade="all">
+ <formula>name</formula>
+ <formula>'HOME'</formula>
+ </one-to-one>
+ <one-to-one name="mailingAddress"
+ cascade="all">
+ <formula>name</formula>
+ <formula>'MAILING'</formula>
+ </one-to-one>
+</class>
+
+<class name="Address" batch-size="2"
+ check="addressType in ('MAILING', 'HOME', 'BUSINESS')">
+ <composite-id>
+ <key-many-to-one name="person"
+ column="personName"/>
+ <key-property name="type"
+ column="addressType"/>
+ </composite-id>
+ <property name="street" type="text"/>
+ <property name="state"/>
+ <property name="zip"/>
+</class>]]></programlisting>
+ </sect2>
+
+ <sect2 id="example-mappings-composite-key">
+ <title>Ejemplo de clave compuesta</title>
+<programlisting><![CDATA[<class name="Customer">
+
+ <id name="customerId"
+ length="10">
+ <generator class="assigned"/>
+ </id>
+
+ <property name="name" not-null="true" length="100"/>
+ <property name="address" not-null="true" length="200"/>
+
+ <list name="orders"
+ inverse="true"
+ cascade="save-update">
+ <key column="customerId"/>
+ <index column="orderNumber"/>
+ <one-to-many class="Order"/>
+ </list>
+
+</class>
+
+<class name="Order" table="CustomerOrder" lazy="true">
+ <synchronize table="LineItem"/>
+ <synchronize table="Product"/>
+
+ <composite-id name="id"
+ class="Order$Id">
+ <key-property name="customerId" length="10"/>
+ <key-property name="orderNumber"/>
+ </composite-id>
+
+ <property name="orderDate"
+ type="calendar_date"
+ not-null="true"/>
+
+ <property name="total">
+ <formula>
+ ( select sum(li.quantity*p.price)
+ from LineItem li, Product p
+ where li.productId = p.productId
+ and li.customerId = customerId
+ and li.orderNumber = orderNumber )
+ </formula>
+ </property>
+
+ <many-to-one name="customer"
+ column="customerId"
+ insert="false"
+ update="false"
+ not-null="true"/>
+
+ <bag name="lineItems"
+ fetch="join"
+ inverse="true"
+ cascade="save-update">
+ <key>
+ <column name="customerId"/>
+ <column name="orderNumber"/>
+ </key>
+ <one-to-many class="LineItem"/>
+ </bag>
+
+</class>
+
+<class name="LineItem">
+
+ <composite-id name="id"
+ class="LineItem$Id">
+ <key-property name="customerId" length="10"/>
+ <key-property name="orderNumber"/>
+ <key-property name="productId" length="10"/>
+ </composite-id>
+
+ <property name="quantity"/>
+
+ <many-to-one name="order"
+ insert="false"
+ update="false"
+ not-null="true">
+ <column name="customerId"/>
+ <column name="orderNumber"/>
+ </many-to-one>
+
+ <many-to-one name="product"
+ insert="false"
+ update="false"
+ not-null="true"
+ column="productId"/>
+
+</class>
+
+<class name="Product">
+ <synchronize table="LineItem"/>
+
+ <id name="productId"
+ length="10">
+ <generator class="assigned"/>
+ </id>
+
+ <property name="description"
+ not-null="true"
+ length="200"/>
+ <property name="price" length="3"/>
+ <property name="numberAvailable"/>
+
+ <property name="numberOrdered">
+ <formula>
+ ( select sum(li.quantity)
+ from LineItem li
+ where li.productId = productId )
+ </formula>
+ </property>
+
+</class>]]></programlisting>
+ </sect2>
+
+ <sect2 id="example-mappings-composite-key-manytomany">
+ <title>Muchos-a-muchos con atributo de clave compuesta compartido</title>
+<programlisting><![CDATA[<class name="User" table="`User`">
+ <composite-id>
+ <key-property name="name"/>
+ <key-property name="org"/>
+ </composite-id>
+ <set name="groups" table="UserGroup">
+ <key>
+ <column name="userName"/>
+ <column name="org"/>
+ </key>
+ <many-to-many class="Group">
+ <column name="groupName"/>
+ <formula>org</formula>
+ </many-to-many>
+ </set>
+</class>
+
+<class name="Group" table="`Group`">
+ <composite-id>
+ <key-property name="name"/>
+ <key-property name="org"/>
+ </composite-id>
+ <property name="description"/>
+ <set name="users" table="UserGroup" inverse="true">
+ <key>
+ <column name="groupName"/>
+ <column name="org"/>
+ </key>
+ <many-to-many class="User">
+ <column name="userName"/>
+ <formula>org</formula>
+ </many-to-many>
+ </set>
+</class>
+]]></programlisting>
+ </sect2>
+
+ <sect2 id="example-mappings-content-discrimination">
+ <title>Discriminación basada en contenido</title>
+<programlisting><![CDATA[<class name="Person"
+ discriminator-value="P">
+
+ <id name="id"
+ column="person_id"
+ unsaved-value="0">
+ <generator class="native"/>
+ </id>
+
+
+ <discriminator
+ type="character">
+ <formula>
+ case
+ when title is not null then 'E'
+ when salesperson is not null then 'C'
+ else 'P'
+ end
+ </formula>
+ </discriminator>
+
+ <property name="name"
+ not-null="true"
+ length="80"/>
+
+ <property name="sex"
+ not-null="true"
+ update="false"/>
+
+ <component name="address">
+ <property name="address"/>
+ <property name="zip"/>
+ <property name="country"/>
+ </component>
+
+ <subclass name="Employee"
+ discriminator-value="E">
+ <property name="title"
+ length="20"/>
+ <property name="salary"/>
+ <many-to-one name="manager"/>
+ </subclass>
+
+ <subclass name="Customer"
+ discriminator-value="C">
+ <property name="comments"/>
+ <many-to-one name="salesperson"/>
+ </subclass>
+
+</class>]]></programlisting>
+ </sect2>
+
+ <sect2 id="example-mappings-association-alternatekeys" >
+ <title>Asociaciones sobre claves alternativas</title>
+<programlisting><![CDATA[<class name="Person">
+
+ <id name="id">
+ <generator class="hilo"/>
+ </id>
+
+ <property name="name" length="100"/>
+
+ <one-to-one name="address"
+ property-ref="person"
+ cascade="all"
+ fetch="join"/>
+
+ <set name="accounts"
+ inverse="true">
+ <key column="userId"
+ property-ref="userId"/>
+ <one-to-many class="Account"/>
+ </set>
+
+ <property name="userId" length="8"/>
+
+</class>
+
+<class name="Address">
+
+ <id name="id">
+ <generator class="hilo"/>
+ </id>
+
+ <property name="address" length="300"/>
+ <property name="zip" length="5"/>
+ <property name="country" length="25"/>
+ <many-to-one name="person" unique="true" not-null="true"/>
+
+</class>
+
+<class name="Account">
+ <id name="accountId" length="32">
+ <generator class="uuid.hex"/>
+ </id>
+
+ <many-to-one name="user"
+ column="userId"
+ property-ref="userId"/>
+
+ <property name="type" not-null="true"/>
+
+</class>]]></programlisting>
+ </sect2>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/example_parentchild.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/example_parentchild.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/example_parentchild.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,362 @@
+<chapter id="example-parentchild">
+ <title>Ejemplo: Padre/Hijo</title>
+
+ <para>
+ Una de las primerísimas cosas que los usuarios nuevos intentan hacer con Hibernate es modelar una relación de
+ tipo padre / hijo. Para esto hay dos enfoques diferentes. Por varias razones, el enfoque más conveniente,
+ especialmente para usuarios nuevos, es modelar tanto <literal>Parent</literal> como <literal>Child</literal>
+ como clases de entidad con una asociación <literal><one-to-many></literal> desde <literal>Parent</literal>
+ a <literal>Child</literal>. (El enfoque alternativo es declarar el <literal>Child</literal> como un
+ <literal><composite-element></literal>.) Ahora, resulta que la semántica por defecto de una asociación
+ uno a muchos (en Hibernate) es mucho menos cercana a la semántica usual de una relación padre / hijo que aquellas
+ de un mapeo de elementos compuestos. Explicaremos cómo usar una <emphasis>asociación uno a muchos bidireccional
+ con tratamiento en cascada</emphasis> para modelar una relación padre / hijo eficiente y elegantemente.
+ ¡No es para nada difícil!
+ </para>
+
+ <sect1 id="example-parentchild-collections">
+ <title>Una nota sobre las colecciones</title>
+
+ <para>
+ Se considera que las colecciones de Hibernate son una parte lógica de la entidad que las posee; nunca de
+ las entidades contenidas. ¡Esta es una distinción crucial! Esto tiene las siguientes consecuencias:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ Cuando se quita / añade un objeto desde / a una colección, se incrementa el número de versión del
+ dueño de la colección.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Si un objeto que fue quitado de una colección es una instancia de un tipo de valor (por ejemplo, un
+ elemento compuesto), ese objeta cesará de ser persistente y su estado será completamente quitado de la
+ base de datos. Asimismo, añadir una instancia de tipo de valor a la colección causará que su estado
+ sea inmediatamente persistente.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Por otro lado, si se quita una entidad de una colección (una asociación uno-a-muchos o muchos-a-muchos),
+ no será borrado, por defecto. Este comportamiento es completamente consistente. ¡Un cambio en el
+ estado interno de otra entidad no hace desaparecer la entidad asociada! Asimismo, añadir una entidad a
+ una colección no causa que la entidad se vuelva persistente, por defecto.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ En cambio, el comportamiento por defecto es que al añadir una entidad a una colección se crea meramente
+ un enlace entre las dos entidades, mientras que al quitarla se quita el enlace. Esto es muy apropiado para
+ todos los tipos de casos. Donde no es para nada apropiado es en el caso de una relación padre / hijo. donde
+ la vida del hijo está ligada al ciclo de vida del padre.
+ </para>
+
+ </sect1>
+
+ <sect1 id="example-parentchild-bidir">
+ <title>Uno-a-muchos bidirectional</title>
+
+ <para>
+ Supón que empezamos con una asociación simple <literal><one-to-many></literal> desde
+ <literal>Parent</literal> a <literal>Child</literal>.
+ </para>
+
+ <programlisting><![CDATA[<set name="children">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ Si ejecutásemos el siguiente código
+ </para>
+
+ <programlisting><![CDATA[Parent p = .....;
+Child c = new Child();
+p.getChildren().add(c);
+session.save(c);
+session.flush();]]></programlisting>
+
+ <para>
+ Hibernate publicaría dos sentencias SQL:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>un <literal>INSERT</literal> para crear el registro de <literal>c</literal></para>
+ </listitem>
+ <listitem>
+ <para>
+ un <literal>UPDATE</literal> para crear el enlace desde <literal>p</literal> a
+ <literal>c</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Esto no es sólo ineficiente, sino que además viola cualquier restricción <literal>NOT NULL</literal> en la
+ columna <literal>parent_id</literal>. Podemos reparar la violación de restricción de nulabilidad
+ especificando <literal>not-null="true"</literal> en el mapeo de la colección:
+ </para>
+
+ <programlisting><![CDATA[<set name="children">
+ <key column="parent_id" not-null="true"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ Sin embargo, esta no es la solución recomendada.
+ </para>
+ <para>
+ El caso subyacente de este comportamiento es que el enlace (la clave foránea <literal>parent_id</literal>)
+ de <literal>p</literal> a <literal>c</literal> no es considerado parte del estado del objeto
+ <literal>Child</literal> y por lo tanto no es creada en el <literal>INSERT</literal>. De modo que la
+ solución es hacer el enlace parte del mapeo del <literal>Child</literal>.
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="parent" column="parent_id" not-null="true"/>]]></programlisting>
+
+ <para>
+ (Necesitamos además añadir la propiedad <literal>parent</literal> a la clase <literal>Child</literal>.)
+ </para>
+
+ <para>
+ Ahora que la entidad <literal>Child</literal> está gestionando el estado del enlace, le decimos a la
+ colección que no actualice el enlace. Usamos el atributo <literal>inverse</literal>.
+ </para>
+
+ <programlisting><![CDATA[<set name="children" inverse="true">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ El siguiente código podría ser usado para añadir un nuevo <literal>Child</literal>
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+Child c = new Child();
+c.setParent(p);
+p.getChildren().add(c);
+session.save(c);
+session.flush();]]></programlisting>
+
+ <para>
+ Y ahora, ¡Sólo se publicaría un <literal>INSERT</literal> de SQL!
+ </para>
+
+ <para>
+ Para ajustar un poco más las cosas, podríamos crear un método <literal>addChild()</literal> en
+ <literal>Parent</literal>.
+ </para>
+
+ <programlisting><![CDATA[public void addChild(Child c) {
+ c.setParent(this);
+ children.add(c);
+}]]></programlisting>
+
+ <para>
+ Ahora, el código para añadir un <literal>Child</literal> se ve así
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+Child c = new Child();
+p.addChild(c);
+session.save(c);
+session.flush();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-parentchild-cascades">
+ <title>Ciclo de vida en cascada</title>
+
+ <para>
+ La llamada explícita a <literal>save()</literal> es aún molesta. Apuntaremos a esto usando tratamientos
+ en cascada.
+ </para>
+
+ <programlisting><![CDATA[<set name="children" inverse="true" cascade="all">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ Esto simplifica el código anterior a
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+Child c = new Child();
+p.addChild(c);
+session.flush();]]></programlisting>
+
+ <para>
+ Similarmente, no necesitamos iterar los hijos al salvar o borrar un <literal>Parent</literal>.
+ Lo siguiente quita <literal>p</literal> y todos sus hijos de la base de datos.
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+session.delete(p);
+session.flush();]]></programlisting>
+
+ <para>
+ Sin embargo, este código
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+Child c = (Child) p.getChildren().iterator().next();
+p.getChildren().remove(c);
+c.setParent(null);
+session.flush();]]></programlisting>
+
+ <para>
+ no quitará <literal>c</literal> de la base de datos; sólo quitará el enlace a <literal>p</literal>
+ (y causará una violación a una restricción <literal>NOT NULL</literal>). Necesitas borrar el hijo
+ explícitamente llamando a <literal>delete()</literal>.
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+Child c = (Child) p.getChildren().iterator().next();
+p.getChildren().remove(c);
+session.delete(c);
+session.flush();]]></programlisting>
+
+ <para>
+ Ahora, en nuestro caso, un <literal>Child</literal> no puede existir realmente sin su padre. De modo que
+ si quitamos un <literal>Child</literal> de la colección, realmente queremos que sea borrado. Para esto,
+ debemos usar <literal>cascade="all-delete-orphan"</literal>.
+ </para>
+
+ <programlisting><![CDATA[<set name="children" inverse="true" cascade="all-delete-orphan">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ Nota: aunque el mapeo de la colección especifique <literal>inverse="true"</literal>, el tratamiento en
+ cascada se procesa aún al iterar los elementos de colección. De modo que si requieres que un objeto sea
+ salvado, borrado o actualizado en cascada, debes añadirlo a la colección. No es suficiente con simplemente
+ llamar a <literal>setParent()</literal>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="example-parentchild-update">
+ <title>Tratamiento en cascada y <literal>unsaved-value</literal></title>
+
+ <para>
+ Supón que hemos cargado un <literal>Parent</literal> en una <literal>Session</literal>, hemos hecho algunos
+ cambios en una acción de UI y deseamos hacer persistentes estos cambios en una nueva sesión llamando a
+ <literal>update()</literal>. El <literal>Parent</literal> contendrá una colección de hijos y, ya que
+ está habilitado el tratamiento en cascada, Hibernate necesita saber qué hijos están recién instanciados
+ y cuáles representan filas existentes en la base de datos. Asumamos que tanto <literal>Parent</literal> como
+ <literal>Child</literal> tienen propiedades identificadoras generadas de tipo <literal>Long</literal>.
+ Hibernate usará el identificador y el valor de la propiedad de versión/timestamp para determinar cuáles de
+ los hijos son nuevos. (Ver <xref linkend="objectstate-saveorupdate"/>.) <emphasis>En Hibernate3, no es
+ más necesario especificar un <literal>unsaved-value</literal> explícitamente.</emphasis>
+ </para>
+
+ <para>
+ The following code will update <literal>parent</literal> and <literal>child</literal> and insert
+ <literal>newChild</literal>.
+ </para>
+
+ <programlisting><![CDATA[//parent and child were both loaded in a previous session
+parent.addChild(child);
+Child newChild = new Child();
+parent.addChild(newChild);
+session.update(parent);
+session.flush();]]></programlisting>
+
+ <para>
+ Bueno, todo eso está muy bien para el caso de un identificador generado, pero ¿qué de los
+ identificadores asignados y de los identificadores compuestos? Esto es más difícil, ya que Hibernate
+ no puede usar la propiedad identificadora para distinguir entre un objeto recién instanciado (con un
+ identificador asignado por el usuario) y un objeto cargado en una sesión previa. En este caso, Hibernate
+ bien usará la propiedad de versión o timestamp, o bien consultará realmente el caché de segundo nivel,
+ o bien, en el peor de los casos, la base de datos, para ver si existe la fila.
+ </para>
+
+ <!-- undocumenting
+ <para>
+ There is one further possibility. The <literal>Interceptor</literal> method named
+ <literal>isUnsaved()</literal> lets the application implement its own strategy for distinguishing
+ newly instantiated objects. For example, you could define a base class for your persistent classes.
+ </para>
+
+ <programlisting><![CDATA[public class Persistent {
+ private boolean _saved = false;
+ public void onSave() {
+ _saved=true;
+ }
+ public void onLoad() {
+ _saved=true;
+ }
+ ......
+ public boolean isSaved() {
+ return _saved;
+ }
+}]]></programlisting>
+
+ <para>
+ (The <literal>saved</literal> property is non-persistent.)
+ Now implement <literal>isUnsaved()</literal>, along with <literal>onLoad()</literal>
+ and <literal>onSave()</literal> as follows.
+ </para>
+
+ <programlisting><![CDATA[public Boolean isUnsaved(Object entity) {
+ if (entity instanceof Persistent) {
+ return new Boolean( !( (Persistent) entity ).isSaved() );
+ }
+ else {
+ return null;
+ }
+}
+
+public boolean onLoad(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+
+ if (entity instanceof Persistent) ( (Persistent) entity ).onLoad();
+ return false;
+}
+
+public boolean onSave(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+
+ if (entity instanceof Persistent) ( (Persistent) entity ).onSave();
+ return false;
+}]]></programlisting>
+
+ <para>
+ Don't worry; in Hibernate3 you don't need to write any of this kind of code if you don't want to.
+ </para>
+ -->
+ </sect1>
+
+ <sect1 id="example-parentchild-conclusion">
+ <title>Conclusión</title>
+
+ <para>
+ Hay que resumir un poco aquí y podría parecer confuso a la primera vez. Sin embargo, en la práctica,
+ todo funciona muy agradablemente. La mayoría de las aplicaciones de Hibernate usan el patrón
+ padre / hijo en muchos sitios.
+ </para>
+
+ <para>
+ Hemos mencionado una alternativa en el primer párrafo. Ninguno de los temas anteriores existe en el caso
+ de los mapeos <literal><composite-element></literal>, que tienen exactamente la semántica de una
+ relación padre / hijo. Desafortunadamente, hay dos grandes limitaciones para las clases de elementos
+ compuestos: los elementos compuestos no pueden poseer sus propias colecciones, y no deben ser el hijo
+ de cualquier otra entidad que no sea su padre único.
+ </para>
+
+ </sect1>
+
+</chapter>
\ No newline at end of file
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/example_weblog.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/example_weblog.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/example_weblog.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,429 @@
+<chapter id="example-weblog">
+ <title>Ejemplo: Aplicación de Weblog</title>
+
+ <sect1 id="example-weblog-classes">
+ <title>Clases Persistentes</title>
+
+ <para>
+ Las clases persistentes representan un weblog, y un ítem enviado a un weblog. Van a ser modelados como una
+ relación padre/hijo estñndar, pero usaremos un bag ordenado, en vez de un conjunto (set).
+ </para>
+
+ <programlisting><![CDATA[package eg;
+
+import java.util.List;
+
+public class Blog {
+ private Long _id;
+ private String _name;
+ private List _items;
+
+ public Long getId() {
+ return _id;
+ }
+ public List getItems() {
+ return _items;
+ }
+ public String getName() {
+ return _name;
+ }
+ public void setId(Long long1) {
+ _id = long1;
+ }
+ public void setItems(List list) {
+ _items = list;
+ }
+ public void setName(String string) {
+ _name = string;
+ }
+}]]></programlisting>
+
+ <programlisting><![CDATA[package eg;
+
+import java.text.DateFormat;
+import java.util.Calendar;
+
+public class BlogItem {
+ private Long _id;
+ private Calendar _datetime;
+ private String _text;
+ private String _title;
+ private Blog _blog;
+
+ public Blog getBlog() {
+ return _blog;
+ }
+ public Calendar getDatetime() {
+ return _datetime;
+ }
+ public Long getId() {
+ return _id;
+ }
+ public String getText() {
+ return _text;
+ }
+ public String getTitle() {
+ return _title;
+ }
+ public void setBlog(Blog blog) {
+ _blog = blog;
+ }
+ public void setDatetime(Calendar calendar) {
+ _datetime = calendar;
+ }
+ public void setId(Long long1) {
+ _id = long1;
+ }
+ public void setText(String string) {
+ _text = string;
+ }
+ public void setTitle(String string) {
+ _title = string;
+ }
+}]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-weblog-mappings">
+ <title>Mapeos de Hibernate</title>
+
+ <para>
+ Los mapeos XML ahora deben ser absolutamente directos.
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="eg">
+
+ <class
+ name="Blog"
+ table="BLOGS">
+
+ <id
+ name="id"
+ column="BLOG_ID">
+
+ <generator class="native"/>
+
+ </id>
+
+ <property
+ name="name"
+ column="NAME"
+ not-null="true"
+ unique="true"/>
+
+ <bag
+ name="items"
+ inverse="true"
+ order-by="DATE_TIME"
+ cascade="all">
+
+ <key column="BLOG_ID"/>
+ <one-to-many class="BlogItem"/>
+
+ </bag>
+
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="eg">
+
+ <class
+ name="BlogItem"
+ table="BLOG_ITEMS"
+ dynamic-update="true">
+
+ <id
+ name="id"
+ column="BLOG_ITEM_ID">
+
+ <generator class="native"/>
+
+ </id>
+
+ <property
+ name="title"
+ column="TITLE"
+ not-null="true"/>
+
+ <property
+ name="text"
+ column="TEXT"
+ not-null="true"/>
+
+ <property
+ name="datetime"
+ column="DATE_TIME"
+ not-null="true"/>
+
+ <many-to-one
+ name="blog"
+ column="BLOG_ID"
+ not-null="true"/>
+
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-weblog-code">
+ <title>Código Hibernate</title>
+
+ <para>
+ La siguiente clase demuestra algunos de los tipos de cosas que podemos haces con estas clases,
+ usando Hibernate.
+ </para>
+
+ <programlisting><![CDATA[package eg;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Iterator;
+import java.util.List;
+
+import org.hibernate.HibernateException;
+import org.hibernate.Query;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.tool.hbm2ddl.SchemaExport;
+
+public class BlogMain {
+
+ private SessionFactory _sessions;
+
+ public void configure() throws HibernateException {
+ _sessions = new Configuration()
+ .addClass(Blog.class)
+ .addClass(BlogItem.class)
+ .buildSessionFactory();
+ }
+
+ public void exportTables() throws HibernateException {
+ Configuration cfg = new Configuration()
+ .addClass(Blog.class)
+ .addClass(BlogItem.class);
+ new SchemaExport(cfg).create(true, true);
+ }
+
+ public Blog createBlog(String name) throws HibernateException {
+
+ Blog blog = new Blog();
+ blog.setName(name);
+ blog.setItems( new ArrayList() );
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ session.persist(blog);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return blog;
+ }
+
+ public BlogItem createBlogItem(Blog blog, String title, String text)
+ throws HibernateException {
+
+ BlogItem item = new BlogItem();
+ item.setTitle(title);
+ item.setText(text);
+ item.setBlog(blog);
+ item.setDatetime( Calendar.getInstance() );
+ blog.getItems().add(item);
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ session.update(blog);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return item;
+ }
+
+ public BlogItem createBlogItem(Long blogid, String title, String text)
+ throws HibernateException {
+
+ BlogItem item = new BlogItem();
+ item.setTitle(title);
+ item.setText(text);
+ item.setDatetime( Calendar.getInstance() );
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ Blog blog = (Blog) session.load(Blog.class, blogid);
+ item.setBlog(blog);
+ blog.getItems().add(item);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return item;
+ }
+
+ public void updateBlogItem(BlogItem item, String text)
+ throws HibernateException {
+
+ item.setText(text);
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ session.update(item);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ }
+
+ public void updateBlogItem(Long itemid, String text)
+ throws HibernateException {
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ BlogItem item = (BlogItem) session.load(BlogItem.class, itemid);
+ item.setText(text);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ }
+
+ public List listAllBlogNamesAndItemCounts(int max)
+ throws HibernateException {
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ List result = null;
+ try {
+ tx = session.beginTransaction();
+ Query q = session.createQuery(
+ "select blog.id, blog.name, count(blogItem) " +
+ "from Blog as blog " +
+ "left outer join blog.items as blogItem " +
+ "group by blog.name, blog.id " +
+ "order by max(blogItem.datetime)"
+ );
+ q.setMaxResults(max);
+ result = q.list();
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return result;
+ }
+
+ public Blog getBlogAndAllItems(Long blogid)
+ throws HibernateException {
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ Blog blog = null;
+ try {
+ tx = session.beginTransaction();
+ Query q = session.createQuery(
+ "from Blog as blog " +
+ "left outer join fetch blog.items " +
+ "where blog.id = :blogid"
+ );
+ q.setParameter("blogid", blogid);
+ blog = (Blog) q.uniqueResult();
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return blog;
+ }
+
+ public List listBlogsAndRecentItems() throws HibernateException {
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ List result = null;
+ try {
+ tx = session.beginTransaction();
+ Query q = session.createQuery(
+ "from Blog as blog " +
+ "inner join blog.items as blogItem " +
+ "where blogItem.datetime > :minDate"
+ );
+
+ Calendar cal = Calendar.getInstance();
+ cal.roll(Calendar.MONTH, false);
+ q.setCalendar("minDate", cal);
+
+ result = q.list();
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return result;
+ }
+}]]></programlisting>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/filters.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/filters.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/filters.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,130 @@
+<chapter id="filters">
+ <title>Filtrando datos</title>
+
+ <para>
+ Hibernate3 provee un nuevo enfoque innovador para manejar datos con reglas de "visibilidad".
+ Un <emphasis>filtro de Hibernate</emphasis> es un filtro global, con nombre y parametrizado
+ que puede ser habilitado o deshabilitado para una sesión de Hibernate en particular.
+ </para>
+
+ <sect1 id="objectstate-filters">
+ <title>Filtros de Hibernate</title>
+
+ <para>
+ Hibernate3 añade la habilidad de predefinir criterios de filtros y unir esos filtros tanto a
+ nivel de una clase como de una colección. Un criterio de filtro es la habilidad de definir una
+ cláusula de restricción muy similar al atributo existente "where" disponible en el elemento
+ class y varios elementos de colección. Excepto en que estos filtros pueden ser parametrizados.
+ La aplicación puede tomar la decisión en tiempo de ejecución de qué filtros deben estar
+ habilitados y cuáles deben ser sus parámetros. Los filtros pueden ser usados como vistas de
+ base de datos, pero parametrizados dentro de la aplicación.
+ </para>
+
+ <para>
+ Para usar los filtros, éstos deben primero ser definidos y luego unidos a los elementos de mapeo
+ apropiados. Para definir un filtro, usa el elemento <literal><filter-def/></literal> dentro
+ de un elemento <literal><hibernate-mapping/></literal>:
+ </para>
+
+ <programlisting><![CDATA[<filter-def name="myFilter">
+ <filter-param name="myFilterParam" type="string"/>
+</filter-def>]]></programlisting>
+
+ <para>
+ Entonces este filtro puede ser unido a una clase:
+ </para>
+
+ <programlisting><![CDATA[<class name="myClass" ...>
+ ...
+ <filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
+</class>]]></programlisting>
+
+ <para>
+ o a una colección:
+ </para>
+
+ <programlisting><![CDATA[<set ...>
+ <filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
+</set>]]></programlisting>
+
+ <para>
+ o incluso a ambos (o muchos de cada uno) al mismo tiempo.
+ </para>
+
+ <para>
+ Los métodos en <literal>Session</literal> son: <literal>enableFilter(String filterName)</literal>,
+ <literal>getEnabledFilter(String filterName)</literal>, y <literal>disableFilter(String filterName)</literal>.
+ Por defecto, los filtros <emphasis>no</emphasis> están habilitados para una sesión dada; deben ser
+ habilitados explÃcitamente por medio del uso del método <literal>Session.enableFilter()</literal>,
+ que devuelve una instancia de la interface <literal>Filter</literal>. Usando el filtro simple definido
+ arriba, esto se verÃa asÃ:
+ </para>
+
+ <programlisting><![CDATA[session.enableFilter("myFilter").setParameter("myFilterParam", "some-value");]]></programlisting>
+
+ <para>
+ Nota que los métodos en la interface org.hibernate.Filter permiten el encadenamiento de métodos
+ común en gran parte de Hibernate.
+ </para>
+
+ <para>
+ Un ejemplo completo, usando datos temporales con un patrón efectivo de fechas de registro:
+ </para>
+
+ <programlisting><![CDATA[<filter-def name="effectiveDate">
+ <filter-param name="asOfDate" type="date"/>
+</filter-def>
+
+<class name="Employee" ...>
+...
+ <many-to-one name="department" column="dept_id" class="Department"/>
+ <property name="effectiveStartDate" type="date" column="eff_start_dt"/>
+ <property name="effectiveEndDate" type="date" column="eff_end_dt"/>
+...
+ <!--
+ Note that this assumes non-terminal records have an eff_end_dt set to
+ a max db date for simplicity-sake
+ -->
+ <filter name="effectiveDate"
+ condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
+</class>
+
+<class name="Department" ...>
+...
+ <set name="employees" lazy="true">
+ <key column="dept_id"/>
+ <one-to-many class="Employee"/>
+ <filter name="effectiveDate"
+ condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ Entonces, en orden de asegurar que siempre tendrás de vuelta registros actualmente efectivos,
+ simplemente habilita el filtro en la sesión previo a recuperar los datos de empleados:
+ </para>
+
+<programlisting><![CDATA[Session session = ...;
+session.enabledFilter("effectiveDate").setParameter("asOfDate", new Date());
+List results = session.createQuery("from Employee as e where e.salary > :targetSalary")
+ .setLong("targetSalary", new Long(1000000))
+ .list();
+]]></programlisting>
+
+ <para>
+ En el HQL de arriba, aunque sólo hemos mencionado explÃcitamente una restricción de salario en
+ los resultados, debido al filtro habilitado la consulta sólo devolverá empleados actualmente activos
+ que tengan un salario mayor que un millón de dólares.
+ </para>
+
+ <para>
+ Nota: si planeas usar filtros con unión externa (outer joining) (bien a través de HQL, o bien
+ de recuperación de carga) sé cuidadoso en la dirección de expresión de la condición. Lo más seguro
+ es establecer esto para unión externa izquierda (left outer joining). En general, coloca el primer
+ parámetro seguido del nombre(s) de columna(s) después del operador.
+ </para>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/inheritance_mapping.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/inheritance_mapping.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/inheritance_mapping.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,464 @@
+<chapter id="inheritance">
+ <title>Mapeo de Herencia</title>
+
+ <sect1 id="inheritance-strategies" revision="2">
+ <title>Las Tres Estrategias</title>
+
+ <para>
+ Hibernate soporta las tres estrategias básicas de mapeo de herencia:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ tabla por jerarquía de clases
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ tabla por subclase
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ tabla por clase concreta
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ En adición, Hibernate soporta un cuarto, ligeramente diferente tipo
+ de polimorfismo:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ polimorfismo implícito
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Es posible usar estrategias de mapeo diferentes para diferentes
+ ramificaciones de la misma jerarquía de herencia, y entonces usar
+ polimorfismo implícito para conseguir polimorfismo a través de
+ toda la jerarquía. Sin embargo, Hibernate no soporta la mezcla de
+ mapeos <literal><subclass></literal>,
+ y <literal><joined-subclass></literal>
+ y <literal><union-subclass></literal> bajo el mismo elemento
+ <literal><class></literal> raíz. Es posible mezclar juntas las
+ estrategias de tabla por jerarquía y tabla por subclase, bajo el mismo
+ elemento <literal><class></literal>, combinando los elementos
+ <literal><subclass></literal> y <literal><join></literal>
+ (ver debajo).
+ </para>
+
+ <sect2 id="inheritance-tableperclass" >
+ <title>Tabla por jerarquía de clases</title>
+
+ <para>
+ Supón que tenemos una interface <literal>Payment</literal>, con
+ los implementadores <literal>CreditCardPayment</literal>,
+ <literal>CashPayment</literal>, <literal>ChequePayment</literal>.
+ El mapeo de tabla por jerarquía se vería así:
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment" table="PAYMENT">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <discriminator column="PAYMENT_TYPE" type="string"/>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <subclass name="CreditCardPayment" discriminator-value="CREDIT">
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </subclass>
+ <subclass name="CashPayment" discriminator-value="CASH">
+ ...
+ </subclass>
+ <subclass name="ChequePayment" discriminator-value="CHEQUE">
+ ...
+ </subclass>
+</class>]]></programlisting>
+
+ <para>
+ Se requiere exactamente una tabla. Hay una gran limitación de esta estrategia de mapeo:
+ las columnas declaradas por las subclases, como <literal>CCTYPE</literal>, no pueden
+ tener restricciones <literal>NOT NULL</literal>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="inheritance-tablepersubclass">
+ <title>Tabla por subclase</title>
+
+ <para>
+ Un mapeo de tabla por sublclase se vería así:
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment" table="PAYMENT">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <joined-subclass name="CreditCardPayment" table="CREDIT_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </joined-subclass>
+ <joined-subclass name="CashPayment" table="CASH_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ ...
+ </joined-subclass>
+ <joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ ...
+ </joined-subclass>
+</class>]]></programlisting>
+
+ <para>
+ Se requieren cuatro tablas. Las tres tablas de subclase tienen
+ asociaciones de clave primaria a la tabla de superclase (de modo
+ que en el modelo relacional es realmente una asociación uno-a-uno).
+ </para>
+
+ </sect2>
+
+ <sect2 id="inheritance-tablepersubclass-discriminator" revision="2">
+ <title>Tabla por subclase, usando un discriminador</title>
+
+ <para>
+ Observa que la implementación de Hibernate de tabla por subclase
+ no requiere ninguna columna discriminadora. Otros mapeadores
+ objeto/relacional usan una implementación diferente de tabla por
+ subclase que requiere una columna discriminadora de tipo en la tabla
+ de superclase. Este enfoque es mucho más difícil de implementar
+ pero discutiblemente más correcto desde un punto de vista relacional.
+ Si quisieras usar una columna discriminadora con la estrategia de
+ tabla por subclase, puedes combinar el uso de <literal><subclass></literal>
+ y <literal><join></literal>, como sigue:
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment" table="PAYMENT">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <discriminator column="PAYMENT_TYPE" type="string"/>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <subclass name="CreditCardPayment" discriminator-value="CREDIT">
+ <join table="CREDIT_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </join>
+ </subclass>
+ <subclass name="CashPayment" discriminator-value="CASH">
+ <join table="CASH_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ ...
+ </join>
+ </subclass>
+ <subclass name="ChequePayment" discriminator-value="CHEQUE">
+ <join table="CHEQUE_PAYMENT" fetch="select">
+ <key column="PAYMENT_ID"/>
+ ...
+ </join>
+ </subclass>
+</class>]]></programlisting>
+
+ <para>
+ la declaración opcional <literal>fetch="select"</literal> dice a Hibernate
+ que no recupere los datos de la subclase <literal>ChequePayment</literal>
+ usando una unión externa (outer join) al consultar la superclase.
+ </para>
+
+ </sect2>
+
+ <sect2 id="inheritance-mixing-tableperclass-tablepersubclass">
+ <title>Mezclando tabla por jerarquía de clases con tabla por subclase</title>
+
+ <para>
+ Puedes incluso mezclar las estrategias de tabla po jerarquía y tabla por
+ subclase usando este enfoque:
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment" table="PAYMENT">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <discriminator column="PAYMENT_TYPE" type="string"/>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <subclass name="CreditCardPayment" discriminator-value="CREDIT">
+ <join table="CREDIT_PAYMENT">
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </join>
+ </subclass>
+ <subclass name="CashPayment" discriminator-value="CASH">
+ ...
+ </subclass>
+ <subclass name="ChequePayment" discriminator-value="CHEQUE">
+ ...
+ </subclass>
+</class>]]></programlisting>
+
+ <para>
+ Para cualquiera de estas estrategias de mapeo, una asociación polimórfica
+ a la clase raíz <literal>Payment</literal> es mapeada usando <literal><many-to-one></literal>.
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="payment" column="PAYMENT_ID" class="Payment"/>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="inheritance-tableperconcrete" revision="1">
+ <title>Tabla por clase concreta</title>
+
+ <para>
+ Podríamos ir de dos maneras a la estrategia de mapeo de tabla por clase
+ concreta. La primera es usar <literal><union-subclass></literal>.
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="sequence"/>
+ </id>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <union-subclass name="CreditCardPayment" table="CREDIT_PAYMENT">
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </union-subclass>
+ <union-subclass name="CashPayment" table="CASH_PAYMENT">
+ ...
+ </union-subclass>
+ <union-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
+ ...
+ </union-subclass>
+</class>]]></programlisting>
+
+ <para>
+ Están implicadas tres tablas. Cada tabla define columnas para todas las
+ propiedades de la clase, inccluyendo las propiedades heredadas.
+ </para>
+
+ <para>
+ La limitación de este enfoque es que si una propiedad es mapeada en la
+ superclase, el nombre de columna debe ser el mismo en todas las tablas
+ de subclase. (Podríamos relajar esto en un lanzamiento futuro de Hibernate.)
+ La estrategia de generador de indentidad no está permitida en la herencia
+ de unión de subclase, de hecho la semilla de clave primaria tiene que ser
+ compartida a través de todas las subclases unidas de una jerarquía.
+ </para>
+
+ </sect2>
+
+ <sect2 id="inheritance-tableperconcreate-polymorphism">
+ <title>Tabla por clase concreta, usando polimorfismo implícito</title>
+
+ <para>
+ Un enfoque alternativo es hacer uso de polimorfismo implícito:
+ </para>
+
+ <programlisting><![CDATA[<class name="CreditCardPayment" table="CREDIT_PAYMENT">
+ <id name="id" type="long" column="CREDIT_PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="amount" column="CREDIT_AMOUNT"/>
+ ...
+</class>
+
+<class name="CashPayment" table="CASH_PAYMENT">
+ <id name="id" type="long" column="CASH_PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="amount" column="CASH_AMOUNT"/>
+ ...
+</class>
+
+<class name="ChequePayment" table="CHEQUE_PAYMENT">
+ <id name="id" type="long" column="CHEQUE_PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="amount" column="CHEQUE_AMOUNT"/>
+ ...
+</class>]]></programlisting>
+
+ <para>
+ Nota que en ningún sitio mencionamos la interface <literal>Payment</literal>
+ explícitamente. Nota además que las propiedades de <literal>Payment</literal>
+ son mapeadas en cada una de las subclases. Si quieres evitar duplicación,
+ considera usar entidades XML. (por ejemplo,
+ <literal>[ <!ENTITY allproperties SYSTEM "allproperties.xml"> ]</literal>
+ en la declaración <literal>DOCTYPE</literal> y <literal>&allproperties;</literal>
+ en el mapeo).
+ </para>
+
+ <para>
+ La desventaja de este enfoque es que Hibernate no genera <literal>UNION</literal>s
+ de SQL al realizar consultas polimórficas.
+ </para>
+
+ <para>
+ Para esta estrategia de mapeo, una asociación polimórfica a <literal>Payment</literal>
+ es mapeada generalmente usando <literal><any></literal>.
+ </para>
+
+ <programlisting><![CDATA[<any name="payment" meta-type="string" id-type="long">
+ <meta-value value="CREDIT" class="CreditCardPayment"/>
+ <meta-value value="CASH" class="CashPayment"/>
+ <meta-value value="CHEQUE" class="ChequePayment"/>
+ <column name="PAYMENT_CLASS"/>
+ <column name="PAYMENT_ID"/>
+</any>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="inheritace-mixingpolymorphism">
+ <title>Mezclando polimorfismo implícito con otros mapeos de herencia</title>
+
+ <para>
+ Hay una cosa más por notar acerca de este mapeo. Ya que las subclases se mapean
+ cada una en su propio elemento <literal><class></literal> (y ya que
+ <literal>Payment</literal> es sólo una interface), cada una de las subclases
+ podría ser parte de otra jerarquía de herencia! (Y todavía puedes seguir usando
+ consultas polimórficas contra la interface <literal>Payment</literal>.)
+ </para>
+
+ <programlisting><![CDATA[<class name="CreditCardPayment" table="CREDIT_PAYMENT">
+ <id name="id" type="long" column="CREDIT_PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <discriminator column="CREDIT_CARD" type="string"/>
+ <property name="amount" column="CREDIT_AMOUNT"/>
+ ...
+ <subclass name="MasterCardPayment" discriminator-value="MDC"/>
+ <subclass name="VisaPayment" discriminator-value="VISA"/>
+</class>
+
+<class name="NonelectronicTransaction" table="NONELECTRONIC_TXN">
+ <id name="id" type="long" column="TXN_ID">
+ <generator class="native"/>
+ </id>
+ ...
+ <joined-subclass name="CashPayment" table="CASH_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ <property name="amount" column="CASH_AMOUNT"/>
+ ...
+ </joined-subclass>
+ <joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ <property name="amount" column="CHEQUE_AMOUNT"/>
+ ...
+ </joined-subclass>
+</class>]]></programlisting>
+
+ <para>
+ Una vez más, no mencionamos a <literal>Payment</literal> explícitamente.
+ Si ejecutamos una consulta contra la interface <literal>Payment</literal>
+ - por ejemplo, <literal>from Payment</literal> - Hibernate devuelve
+ automáticamente instancias de <literal>CreditCardPayment</literal>
+ (y sus subclases, ya que ellas también implementan <literal>Payment</literal>),
+ <literal>CashPayment</literal> y <literal>ChequePayment</literal> pero
+ no instancias de <literal>NonelectronicTransaction</literal>.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="inheritance-limitations">
+ <title>Limitaciones</title>
+
+ <para>
+ Existen ciertas limitaciones al enfoque de "polimorfismo implícito" en
+ la estrategia de mapeo de tabla por clase concreta. Existen limitaciones
+ algo menos restrictivas a los mapeos <literal><union-subclass></literal>.
+ </para>
+
+ <para>
+ La siguiente tabla muestra las limitaciones de mapeos de tabla por
+ clase concreta, y de polmorfismo implícito, en Hibernate.
+ </para>
+
+ <table frame="topbot">
+ <title>Funcionalidades de mapeo de herencia</title>
+ <tgroup cols='8' align='left' colsep='1' rowsep='1'>
+ <colspec colname='c1' colwidth="1*"/>
+ <colspec colname='c2' colwidth="1*"/>
+ <colspec colname='c3' colwidth="1*"/>
+ <colspec colname='c4' colwidth="1*"/>
+ <colspec colname='c5' colwidth="1*"/>
+ <colspec colname='c6' colwidth="1*"/>
+ <colspec colname='c7' colwidth="1*"/>
+ <colspec colname='c8' colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Estrategia de herencia</entry>
+ <entry>muchos-a-uno polimórfica</entry>
+ <entry>uno-a-uno polimórfica</entry>
+ <entry>uno-a-muchos polimórfica</entry>
+ <entry>mushos-a-muchos polimórfica</entry>
+ <entry><literal>load()/get()</literal> polimórficos</entry>
+ <entry>Consultas polimórficas</entry>
+ <entry>Uniones polimórficas</entry>
+ <entry>Recuperación por unión externa (outer join)</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>tabla por jerarquía de clases</entry>
+ <entry><literal><many-to-one></literal></entry>
+ <entry><literal><one-to-one></literal></entry>
+ <entry><literal><one-to-many></literal></entry>
+ <entry><literal><many-to-many></literal></entry>
+ <entry><literal>s.get(Payment.class, id)</literal></entry>
+ <entry><literal>from Payment p</literal></entry>
+ <entry><literal>from Order o join o.payment p</literal></entry>
+ <entry><emphasis>soportada</emphasis></entry>
+ </row>
+ <row>
+ <entry>tabla por subclase</entry>
+ <entry><literal><many-to-one></literal></entry>
+ <entry><literal><one-to-one></literal></entry>
+ <entry><literal><one-to-many></literal></entry>
+ <entry><literal><many-to-many></literal></entry>
+ <entry><literal>s.get(Payment.class, id)</literal></entry>
+ <entry><literal>from Payment p</literal></entry>
+ <entry><literal>from Order o join o.payment p</literal></entry>
+ <entry><emphasis>soportada</emphasis></entry>
+ </row>
+ <row>
+ <entry>tabla por clase concreta (union-subclass)</entry>
+ <entry><literal><many-to-one></literal></entry>
+ <entry><literal><one-to-one></literal></entry>
+ <entry><literal><one-to-many></literal> (para <literal>inverse="true"</literal> solamente)</entry>
+ <entry><literal><many-to-many></literal></entry>
+ <entry><literal>s.get(Payment.class, id)</literal></entry>
+ <entry><literal>from Payment p</literal></entry>
+ <entry><literal>from Order o join o.payment p</literal></entry>
+ <entry><emphasis>soportada</emphasis></entry>
+ </row>
+ <row>
+ <entry>tabla por clase concreta (polimorfismo implícito)</entry>
+ <entry><literal><any></literal></entry>
+ <entry><emphasis>no soportada</emphasis></entry>
+ <entry><emphasis>no soportada</emphasis></entry>
+ <entry><literal><many-to-any></literal></entry>
+ <entry><literal>s.createCriteria(Payment.class).add( Restrictions.idEq(id) ).uniqueResult()</literal></entry>
+ <entry><literal>from Payment p</literal></entry>
+ <entry><emphasis>no suportadas</emphasis></entry>
+ <entry><emphasis>no soportada</emphasis></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect1>
+
+</chapter>
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/performance.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/performance.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/performance.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1334 @@
+<chapter id="performance">
+ <title>Mejorando el rendimiento</title>
+
+ <sect1 id="performance-fetching">
+ <title>Estrategias de recuperación</title>
+
+ <para>
+ Una <emphasis>estrategia de recuperación</emphasis> es la estrategia que usará Hibernate para recuperar
+ los objetos asociados cuando la aplicación necesite navegar la asociación. Las estrategias de recuperación
+ pueden ser declaradas en los metadatos de mapeo O/R, o sobrescritas por una consulta HQL o
+ <literal>Criteria</literal> en particular.
+ </para>
+
+ <para>
+ Hibernate3 define las siguientes estrategias de recuperación:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Recuperación por unión (join fetching)</emphasis> - Hibernate recupera la
+ instancia asociada o colección en la misma <literal>SELECT</literal>, usando una
+ <literal>OUTER JOIN</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Recuperación por selección (select fetching)</emphasis> - se usa una segunda
+ <literal>SELECT</literal> para recuperar la entidad asociada o colección. A menos que
+ deshabilites explícitamente la recuperación perezosa especificando <literal>lazy="false"</literal>,
+ la segunda selección sólo será ejecutada cuando realmente accedas a la asociación.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Recuperación por subselección (subselect fetching)</emphasis> - se usa una segunda
+ <literal>SELECT</literal> para recuperar las colecciones asociadas de todas las entidades
+ recuperadas en una consulta o recuperación previa. A menos que deshabilites explícitamente la
+ recuperación perezosa especificando <literal>lazy="false"</literal>, esta segunda selección sólo
+ será ejecutada cuando realmente accedas a la asociación.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Recuperación en lote</emphasis> - una estrategia de optimización para la recuperación
+ por selección - Hibernate recupera un lote de instancias de entidad o colecciones en una sola
+ <literal>SELECT</literal>, especificando una lista de claves primarias o de claves foráneas.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Hibernate también distingue entre:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Recuperación inmediata</emphasis> - una asociación, colección o atributo es recuperado
+ inmediatamente, cuando el dueño es cargado.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Recuperación perezosa de colecciones</emphasis> - se recupera una colección cuando la
+ aplicación invoca una operación sobre la colección. (Esto es por defecto para las colecciones.)
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Recuperación por proxy</emphasis> - se recupera una asociación monovaluada cuando se
+ invoca un método que no sea el getter del identificador sobre el objeto asociado.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Recuperación perezosa de atributos</emphasis> - se recupera un atributo o una asociación
+ monovaluada cuando se accede a la variable de instancia (requiere instrumentación del bytecode en
+ tiempo de ejecución). Este enfoque es raramente necesario.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Aquí tenemos dos nociones ortogonales: <emphasis>cuándo</emphasis> se recupera la aplicación,
+ y <emphasis>cómo</emphasis> es recuperada (qué SQL es usado). ¡No las confundas! Usamos
+ <literal>fetch</literal> para afinar el rendimiento. Podemos usar <literal>lazy</literal> para
+ definir un contrato sobre qué datos están siempre disponibles en cualquier instancia separada de
+ una clase en particular.
+ </para>
+
+ <sect2 id="performance-fetching-lazy">
+ <title>Trabajando con asociaciones perezosas</title>
+
+ <para>
+ Por defecto, Hibernate3 usa una recuperación perezosa por selección para colecciones
+ y una recuperación por proxy perezosa para asociaciones monovaluadas. Estas políticas por
+ defecto tienen sentido para casi todas las asociaciones en casi todas las aplicaciones.
+ </para>
+
+ <para>
+ <emphasis>Nota:</emphasis> si estableces <literal>hibernate.default_batch_fetch_size</literal>, Hibernate
+ usará la optimización de recuperación en lotes para recuperación perezosa (esta optimización también puede
+ ser habilitada a un nivel más granularizado).
+ </para>
+
+ <para>
+ Sin embargo, la recuperación perezosa plantea un problema del que tienes que estar al tanto. Acceder
+ a una asociación perezosa fuera del contexto de una sesión de Hibernate abierta resultará en una
+ excepción. Por ejemplo:
+ </para>
+
+ <programlisting><![CDATA[s = sessions.openSession();
+Transaction tx = s.beginTransaction();
+
+User u = (User) s.createQuery("from User u where u.name=:userName")
+ .setString("userName", userName).uniqueResult();
+Map permissions = u.getPermissions();
+
+tx.commit();
+s.close();
+
+Integer accessLevel = (Integer) permissions.get("accounts"); // Error!]]></programlisting>
+
+ <para>
+ Ya que la colección de permisos no fue inicializada cuando se cerró la <literal>Session</literal>,
+ la colección no será capaz de cargar su estado. <emphasis>Hibernate no soporta la inicialización
+ perezosa de objetos separados</emphasis>. La solución es mover el código que lee de la colección
+ a justo antes que la transacción sea comprometida.
+ </para>
+
+ <para>
+ Alternativamente, podríamos usar una colección no perezosa o asociación, especificando
+ <literal>lazy="false"</literal> para el mapeo de asociación. Sin embargo, está pensado
+ que la inicialización perezosa sea usada para casi todas las colecciones y asociaciones.
+ ¡Si defines demasiadas asociaciones no perezosas en tu modelo de objetos, Hibernate terminará
+ necesitando recuperar la base de datos entera en cada transacción!
+ </para>
+
+ <para>
+ Por otro lado, frecuentemente necesitamos elegir la recuperación por unión (que es no perezosa
+ por naturaleza) en vez de la recuperación por selección en una transacción en particular. Veremos
+ ahora cómo personalizar la estrategia de recuperación. En Hibernate3, los mecanismos para elegir una
+ estrategia de recuperación son idénticas a las de las asociaciones monovaluadas y colecciones.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-fetching-custom" revision="3">
+ <title>Afinando las estrategias de recuperación</title>
+
+ <para>
+ La recuperación por selección (la preestablecida) es extremadamente vulnerable a problemas de
+ selección N+1, de modo querríamos habilitar la recuperación por unión (join fetching) en el
+ documento de mapeo:
+ </para>
+
+ <programlisting><![CDATA[<set name="permissions"
+ fetch="join">
+ <key column="userId"/>
+ <one-to-many class="Permission"/>
+</set]]></programlisting>
+
+ <programlisting><![CDATA[<many-to-one name="mother" class="Cat" fetch="join"/>]]></programlisting>
+
+ <para>
+ La estrategia de recuperación definida en el documento de mapeo afecta a:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ las recuperaciones vía <literal>get()</literal> o <literal>load()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ las recuperaciones que ocurren implícitamente cuando se navega una asociación
+ (recuperación perezosa)
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ las consultas de <literal>Criteria</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Usualmente, no usamos el documento de mapeo para personalizar la recuperación. En cambio, mantenemos el
+ comportamiento por defecto, y lo sobrescribimos para una transacción en particular, usando
+ <literal>left join fetch</literal> en HQL. Esto le dice a Hibernate que recupere la asociación
+ tempranamente en la primera selección, usando una unión externa. En la API de consulta de
+ <literal>Criteria</literal>, usarías <literal>setFetchMode(FetchMode.JOIN)</literal>.
+ </para>
+
+ <para>
+ Si acaso lo deseases, podrías cambiar la estrategia de recuperación usada por
+ <literal>get()</literal> or <literal>load()</literal>; simplemente usa una consulta
+ <literal>Criteria</literal>, por ejemplo:
+ </para>
+
+ <programlisting><![CDATA[User user = (User) session.createCriteria(User.class)
+ .setFetchMode("permissions", FetchMode.JOIN)
+ .add( Restrictions.idEq(userId) )
+ .uniqueResult();]]></programlisting>
+
+ <para>
+ (Esto es el equivalente de Hibernate de lo que otras soluciones ORM llaman un "plan de recuperación".)
+ </para>
+
+ <para>
+ Una forma completamente diferente de evitar problemas con selecciones N+1 es usar el caché de
+ segundo nivel.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-fetching-proxies" revision="2">
+ <title>Proxies de asociaciones de un solo extremo</title>
+
+ <para>
+ La recuperación perezosa de colecciones está implementada usando la implementación de colecciones
+ persistentes propia de Hibernate. Sin embargo, se necesita un mecanismo diferente para un comportamiento
+ perezoso en las asociaciones de un solo extremo. La entidad objetivo de la asociación debe ser tratada
+ con proxies. Hibernate implementa proxies de inicialización perezosa para objetos persistentes usando
+ mejora del bytecode en tiempo de ejecución (por medio de la excelente biblioteca CGLIB).
+ </para>
+
+ <para>
+ Por defecto, Hibernate3 genera proxies (en el arranque) para todas las clases persistentes y los usa
+ para habilitar la recuperación perezosa de asociaciones <literal>muchos-a-uno</literal> y
+ <literal>uno-a-uno</literal>.
+ </para>
+
+ <para>
+ El fichero de mapeo puede declarar una interface a usar como interface de proxy para esa clase,
+ con el atributo <literal>proxy</literal>. Por defecto, Hibernate usa una subclase de la clase.
+ <emphasis>Nota que la clase tratada con proxies debe implementar un constructor por defecto con al
+ menos visibilidad de paquete. ¡Recomendamos este constructor para todas las clases persistentes!</emphasis>
+ </para>
+
+ <para>
+ Hay algunos puntos a tener en cuenta al extender este enfoque a clases polimórficas, por ejemplo.
+ </para>
+
+ <programlisting><![CDATA[<class name="Cat" proxy="Cat">
+ ......
+ <subclass name="DomesticCat">
+ .....
+ </subclass>
+</class>]]></programlisting>
+
+ <para>
+ Primero, las instancias de <literal>Cat</literal> nunca serán objeto de un cast a
+ <literal>DomesticCat</literal>, incluso aunque la instancia subyacente sea instancia de
+ <literal>DomesticCat</literal>:
+ </para>
+
+ <programlisting><![CDATA[Cat cat = (Cat) session.load(Cat.class, id); // instantiate a proxy (does not hit the db)
+if ( cat.isDomesticCat() ) { // hit the db to initialize the proxy
+ DomesticCat dc = (DomesticCat) cat; // Error!
+ ....
+}]]></programlisting>
+
+ <para>
+ Segundo, es posible romper con el operador <literal>==</literal> de un proxy.
+ </para>
+
+ <programlisting><![CDATA[Cat cat = (Cat) session.load(Cat.class, id); // instantiate a Cat proxy
+DomesticCat dc =
+ (DomesticCat) session.load(DomesticCat.class, id); // acquire new DomesticCat proxy!
+System.out.println(cat==dc); // false]]></programlisting>
+
+ <para>
+ Sin embargo, la situación no en absoluta tan mala como parece. Aunque tenemos ahora dos referencias
+ a objetos proxy diferentes, la instancia subyacente será aún el mismo objeto:
+ </para>
+
+ <programlisting><![CDATA[cat.setWeight(11.0); // hit the db to initialize the proxy
+System.out.println( dc.getWeight() ); // 11.0]]></programlisting>
+
+ <para>
+ Tercero, no debes usar un proxy CGLIB para una clase <literal>final</literal> o una clase
+ con algún método <literal>final</literal>.
+ </para>
+
+ <para>
+ Finalmente, si tu objeto persistente adquiere cualquier recurso bajo instanciación
+ (por ejemplo, en inicializadores o constructores por defecto), entonces esos recursos
+ serán adquiridos también por el proxy. La clase del proxy es una subclase real de la clase
+ persistente.
+ </para>
+
+ <para>
+ Estos problemas se deben a limitaciones fundamentales en el modelo de herencia única de Java.
+ Si deseas evitar estos problemas cada una de tus clases persistentes deben implementar una
+ interface que declare sus métodos de negocio. Debes especificar estas interfaces en el fichero
+ de mapeo. Por ejemplo:
+ </para>
+
+ <programlisting><![CDATA[<class name="CatImpl" proxy="Cat">
+ ......
+ <subclass name="DomesticCatImpl" proxy="DomesticCat">
+ .....
+ </subclass>
+</class>]]></programlisting>
+
+ <para>
+ donde <literal>CatImpl</literal> implementa la interface <literal>Cat</literal> y
+ <literal>DomesticCatImpl</literal> implementa la interface <literal>DomesticCat</literal>.
+ Entonces <literal>load()</literal> o <literal>iterate()</literal> pueden devolver instancias de
+ <literal>Cat</literal> y <literal>DomesticCat</literal>. (Nota que <literal>list()</literal>
+ usualmente no devuelve proxies.)
+ </para>
+
+ <programlisting><![CDATA[Cat cat = (Cat) session.load(CatImpl.class, catid);
+Iterator iter = session.iterate("from CatImpl as cat where cat.name='fritz'");
+Cat fritz = (Cat) iter.next();]]></programlisting>
+
+ <para>
+ Las relaciones también son inicializadas perezosamente. Esto significa que debes declarar
+ cualquier propiedad como de tipo <literal>Cat</literal>, no <literal>CatImpl</literal>.
+ </para>
+
+ <para>
+ Ciertas operaciones <emphasis>no</emphasis> requieren inicialización de proxies.
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>equals()</literal>, si la clase persistente no sobrescribe <literal>equals()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>hashCode()</literal>, si la clase persistente no sobrescribe
+ <literal>hashCode()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ El método getter del identificador
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Hibernate detectará las clase persistentes que sobrescriban <literal>equals()</literal> o
+ <literal>hashCode()</literal>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-fetching-initialization">
+ <title>Inicializando colecciones y proxies</title>
+
+ <para>
+ Una <literal>LazyInitializationException</literal> será lanzada por Hibernate si una colección
+ o proxy sin inicializar es accedido fuera del ámbito de la <literal>Session</literal>, es decir,
+ cuando la entidad que posee la colección o que tiene la referencia al proxy esté en el estado
+ separada.
+ </para>
+
+ <para>
+ A veces necesitamos asegurarnos que un proxy o colección esté inicializado antes de cerrar la
+ <literal>Session</literal>. Por supuesto, siempre podemos forzar la inicialización llamando a
+ <literal>cat.getSex()</literal> o <literal>cat.getKittens().size()</literal>, por ejemplo.
+ Pero esto es confuso a lectores del código y no es conveniente para código genérico.
+ </para>
+
+ <para>
+ Los métodos estáticos <literal>Hibernate.initialize()</literal> y
+ <literal>Hibernate.isInitialized()</literal> proveen a la aplicación de una forma conveniente de
+ trabajar con colecciones o proxies inicializados perezosamente.
+ <literal>Hibernate.initialize(cat)</literal> forzará la inicialización de un proxy,
+ <literal>cat</literal>, en tanto su <literal>Session</literal> esté todavía abierta.
+ <literal>Hibernate.initialize( cat.getKittens() )</literal> tiene un efecto similar para la colección
+ de gatitos.
+ </para>
+
+ <para>
+ Otra opción es mantener la <literal>Session</literal> abierta hasta que todas las colecciones
+ y proxies necesarios hayan sido cargados. En algunas arquitecturas de aplicación, particularmente
+ en aquellas donde el código que accede a los datos usando Hibernate, y el código que los usa están
+ en capas de aplicación diferentes o procesos físicos diferentes, puede ser un problema asegurar que
+ la <literal>Session</literal> esté abierta cuando se inicializa una colección. Existen dos formas
+ básicas de tratar este tema:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ En una aplicación basada web, puede usarse un filtro de servlets para cerrar la
+ <literal>Session</literal> sólo bien al final de una petición de usuario, una
+ vez que el rendering de la vista esté completa (el patrón <emphasis>Sesión Abierta en
+ Vista (Open Session in View)</emphasis>). Por supuesto, estos sitios requieren una
+ fuerte demanda de corrección del manejo de excepciones de tu infraestructura de
+ aplicación. Es de una importancia vital que la <literal>Session</literal> esté
+ cerrada y la transacción terminada antes de volver al usuario, incluso cuando ocurra
+ una excepción durante el rendering de la página. Para este enfoque, el filtro de servlet tiene
+ que ser capaz de accceder la <literal>Session</literal>. Recomendamos que se use una variable
+ <literal>ThreadLocal</literal> para tener la <literal>Session</literal> actual (ver
+ el capítulo 1, <xref linkend="quickstart-playingwithcats"/>, para una implementación de ejemplo).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ En una aplciación con una grada de negocios separada, la lógica de negocio debe "preparar"
+ todas las colecciones que se vayan a necesitar por la grada web antes de volver.
+ Esto significa que la grada de negocios debe cargar todos los datos y devolver a la grada de
+ presentación web todos los datos que se requieran para un caso de uso en particular
+ ya inicializados. Usualmente, la aplicación llama a <literal>Hibernate.initialize()</literal>
+ para cada colección que se necesitará en la grada web (esta llamada debe ocurrir antes que la
+ sesión sea cerrada) o recupera la colección tempranamente usando una consulta de Hibernate con una
+ cláusula <literal>FETCH</literal> o una <literal>FetchMode.JOIN</literal> en
+ <literal>Criteria</literal>. Esto es usualmente más fácil si adoptas el patrón
+ <emphasis>Comando</emphasis> en vez de un <emphasis>Fachada de Sesión</emphasis>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Puedes también adjuntar un objeto cargado previamente a una nueva <literal>Session</literal>
+ con <literal>merge()</literal> o <literal>lock()</literal> antes de acceder a colecciones
+ no inicializadas (u otros proxies). ¡No, Hibernate no, y ciertamente <emphasis>no
+ debe</emphasis> hacer esto automáticamente, ya que introduciría semánticas de transacción ad hoc!
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ A veces no quieres inicializar una colección grande, pero necesitas aún alguna informacion sobre
+ ella (como su tamaño) o un subconjunto de los datos.
+ </para>
+
+ <para>
+ Puedes usar un filtro de colecciones para obtener el tamaño de una colección sin inicializarla:
+ </para>
+
+ <programlisting><![CDATA[( (Integer) s.createFilter( collection, "select count(*)" ).list().get(0) ).intValue()]]></programlisting>
+
+ <para>
+ El método <literal>createFilter()</literal> se usa también para recuperar eficientemente subconjuntos
+ de una colección sin necesidad de inicializar toda la colección:
+ </para>
+
+ <programlisting><![CDATA[s.createFilter( lazyCollection, "").setFirstResult(0).setMaxResults(10).list();]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="performance-fetching-batch">
+ <title>Usando recuperación en lotes</title>
+
+ <para>
+ Hibernate puede hacer un uso eficiente de la recuperación en lotes, esto es, Hibernate puede cargar
+ muchos proxies sin inicializar si se accede a un proxy (o colecciones). La recuperación en lotes es una
+ optimización de la estrategia de recuperación por selección perezosa. Hay dos formas en que puedes afinar
+ la recuperación en lotes: a nivel de la clase o de la colección.
+ </para>
+
+ <para>
+ La recuperación en lotes para clases/entidades es más fácil de entender. Imagina que tienes la siguiente
+ situación en tiempo de ejecución: Tienes 25 instancias de <literal>Cat</literal> cargadas en una
+ <literal>Session</literal>, cada <literal>Cat</literal> tiene una referencia a su <literal>owner</literal>,
+ una <literal>Person</literal>. La clase <literal>Person</literal> está mapeada con un proxy,
+ <literal>lazy="true"</literal>. Si ahora iteras a través de todos los gatos y llamas a
+ <literal>getOwner()</literal> para cada uno, Hibernate por defecto ejecutará 25 sentencias
+ <literal>SELECT</literal> para traer los dueños tratados con proxies. Puedes afinar este comportamiento
+ especificando un <literal>batch-size</literal> en el mapeo de <literal>Person</literal>:
+ </para>
+
+ <programlisting><![CDATA[<class name="Person" batch-size="10">...</class>]]></programlisting>
+
+ <para>
+ Hibernate ahora ejecutará sólo tres consultas, el patrón es 10, 10, 5.
+ </para>
+
+ <para>
+ También puedes habilitar la recuperación en lotes para colecciones. Por ejemplo, si cada
+ <literal>Person</literal> tiene una colección perezosa de <literal>Cat</literal>s, y hay 10
+ personas actualmente cargadas en la <literal>Session</literal>, iterar a través de las 10 personas
+ generará 10 <literal>SELECT</literal>s, una para cada llamada a <literal>getCats()</literal>.
+ Si habilitas la recuperación en lotes para la colección de <literal>cats</literal> en el mapeo de
+ <literal>Person</literal>, Hibernate puede recuperar por adelantado las colecciones:
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <set name="cats" batch-size="3">
+ ...
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ Con un <literal>batch-size</literal> de 3, Hibernate cargará 3, 3, 3, 1 colecciones en cuatro
+ <literal>SELECT</literal>s. Una vez más, el valor del atributo depende del número esperado de
+ colecciones sin inicializar en una <literal>Session</literal> en particular.
+ </para>
+
+ <para>
+ La recuperación de coleccione en lotes es particularmente útil si tienes un árbol anidado de
+ ítems, es decir, el típico patrón de cuenta de materiales. (Aunque un <emphasis>conjunto
+ anidado</emphasis> o una <emphasis>ruta materializada</emphasis> podría ser una mejor opción
+ para árboles que sean de lectura en la mayoría de los casos.)
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-fetching-subselect">
+ <title>Usando recuperación por subselección</title>
+
+ <para>
+ Si una colección perezosa o proxy monovaluado tiene que ser recuperado, Hibernate los carga a todos,
+ volviendo a ejecutar la consulta original en una subselección. Esto funciona de la misma forma que
+ la recuperación en lotes, sin carga fragmentaria.
+ </para>
+
+ <!-- TODO: Write more about this -->
+
+ </sect2>
+
+ <sect2 id="performance-fetching-lazyproperties">
+ <title>Usando recuperación perezosa de propiedades</title>
+
+ <para>
+ Hibernate3 soporta la recuperación perezosa de propiedades individuales. Esta técnica de optimización
+ es también conocida como <emphasis>grupos de recuperación (fetch groups)</emphasis>. Por favor, nota
+ que éste es mayormente un aspecto de marketing, ya que en la práctica, optimizar lecturas de filas es
+ mucho más importante que la optimización de lectura de columnas. Sin embargo, cargar sólo algunas
+ propiedades de una clase podría ser útil en casos extremos, cuando tablas heredadas tienen cientos de
+ columnas y el modelo de datos no puede ser mejorado.
+ </para>
+
+ <para>
+ Para habilitar la carga perezosa de propiedades, establece el atributo <literal>lazy</literal> en tus
+ mapeos de propiedades:
+ </para>
+
+ <programlisting><![CDATA[<class name="Document">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="name" not-null="true" length="50"/>
+ <property name="summary" not-null="true" length="200" lazy="true"/>
+ <property name="text" not-null="true" length="2000" lazy="true"/>
+</class>]]></programlisting>
+
+ <para>
+ ¡La carga perezosa de propiedades requiere la instrumentación del bytecode en tiempo
+ de construcción! Si tus clases persistentes no son mejoradas, Hibernate ignorará silenciosamente
+ la configuración perezosa de propiedades y caerá en recuperación inmediata.
+ </para>
+
+ <para>
+ Para la instrumentación del bytecode, usa la siguiente tarea Ant:
+ </para>
+
+ <programlisting><![CDATA[<target name="instrument" depends="compile">
+ <taskdef name="instrument" classname="org.hibernate.tool.instrument.InstrumentTask">
+ <classpath path="${jar.path}"/>
+ <classpath path="${classes.dir}"/>
+ <classpath refid="lib.class.path"/>
+ </taskdef>
+
+ <instrument verbose="true">
+ <fileset dir="${testclasses.dir}/org/hibernate/auction/model">
+ <include name="*.class"/>
+ </fileset>
+ </instrument>
+</target>]]></programlisting>
+
+ <para>
+ Una forma diferente (¿mejor?) de evitar lecturas innecesarias de columnas, al menos para
+ transacciones de sólo lectura es usar las funcionalidades de proyección de consultas HQL o Criteria.
+ Esto evita la necesidad de procesar el bytecode en tiempo de construcción y ciertamente es una solución
+ preferida.
+ </para>
+
+ <para>
+ Puedes forzar la usual recuperación temprana de propiedades usando <literal>fetch all properties</literal>
+ en HQL.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="performance-cache" revision="1">
+ <title>El Caché de Segundo Nivel</title>
+
+ <para>
+ Una <literal>Session</literal> de Hibernate es una caché de datos persistentes a nivel de transacción.
+ Es posible configurar un cluster o caché a nivel de JVM (a nivel de <literal>SessionFactory</literal>)
+ sobre una base de clase-a-clase o colección-a-colección. Puedes incluso enchufar una caché en cluster.
+ Sé cuidadoso. Las cachés nunca están al tanto de los cambios hechos por otra aplicación al almacén persistente
+ (aunque pueden ser configurados para expirar regularmente los datos en caché).
+ </para>
+
+ <para>
+ Por defecto, Hibernate usa EHCache para caching a nivel de JVM. (El soporte a JCS ahora está despreciado
+ y será quitado en una futura versión de Hibernate.) Puedes elegir una implementación diferente estableciendo
+ el nombre de una clase que implemente <literal>org.hibernate.cache.CacheProvider</literal> usando la propiedad
+ <literal>hibernate.cache.provider_class</literal>.
+ </para>
+
+ <table frame="topbot" id="cacheproviders" revision="1">
+ <title>Proveedores de Caché</title>
+ <tgroup cols='5' align='left' colsep='1' rowsep='1'>
+ <colspec colname='c1' colwidth="1*"/>
+ <colspec colname='c2' colwidth="3*"/>
+ <colspec colname='c3' colwidth="1*"/>
+ <colspec colname='c4' colwidth="1*"/>
+ <colspec colname='c5' colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Caché</entry>
+ <entry>clase del Provedor</entry>
+ <entry>Tipo</entry>
+ <entry>Cluster Seguro</entry>
+ <entry>Caché de Consultas Soportado</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>Hashtable (no pensado para uso en producción)</entry>
+ <entry><literal>org.hibernate.cache.HashtableCacheProvider</literal></entry>
+ <entry>memoria</entry>
+ <entry></entry>
+ <entry>sí</entry>
+ </row>
+ <row>
+ <entry>EHCache</entry>
+ <entry><literal>org.hibernate.cache.EhCacheProvider</literal></entry>
+ <entry>memoria, disco</entry>
+ <entry></entry>
+ <entry>sí</entry>
+ </row>
+ <row>
+ <entry>OSCache</entry>
+ <entry><literal>org.hibernate.cache.OSCacheProvider</literal></entry>
+ <entry>memoria, disco</entry>
+ <entry></entry>
+ <entry>sí</entry>
+ </row>
+ <row>
+ <entry>SwarmCache</entry>
+ <entry><literal>org.hibernate.cache.SwarmCacheProvider</literal></entry>
+ <entry>clusterizado (ip multicast)</entry>
+ <entry>sí (invalidación en cluster)</entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>TreeCache de JBoss</entry>
+ <entry><literal>org.hibernate.cache.TreeCacheProvider</literal></entry>
+ <entry>clusterizado (ip multicast), transaccional</entry>
+ <entry>sí (replicación)</entry>
+ <entry>sí (requiere sincronización de reloj)</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <sect2 id="performance-cache-mapping">
+ <title>Mapeos de caché</title>
+
+ <para>
+ El elemento <literal><cache></literal> de una mapeo de clase o colección tiene la siguiente
+ forma:
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="cache1" coords="2 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<cache
+ usage="transactional|read-write|nonstrict-read-write|read-only"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="cache1">
+ <para>
+ <literal>usage</literal> especifica la estrategia de caching:
+ <literal>transactional</literal>,
+ <literal>read-write</literal>,
+ <literal>nonstrict-read-write</literal> o
+ <literal>read-only</literal>
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Alternativamente (¿preferiblemente?), puedes especificar los elementos
+ <literal><class-cache></literal> y <literal><collection-cache></literal> en
+ <literal>hibernate.cfg.xml</literal>.
+ </para>
+
+ <para>
+ El atributo <literal>usage</literal> especifica una <emphasis>estrategia de concurrencia al
+ caché</emphasis>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-cache-readonly">
+ <title>Estrategia: sólo lectura (read only)</title>
+
+ <para>
+ Si tu aplicación necesita leer pero nunca modificar las instancias de una clase persistente,
+ puede usarse un caché <literal>read-only</literal>. Esta es la mejor y más simple estrategia.
+ Es incluso perfectamente segura de usar en un cluster.
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Immutable" mutable="false">
+ <cache usage="read-only"/>
+ ....
+</class>]]></programlisting>
+
+ </sect2>
+
+
+ <sect2 id="performance-cache-readwrite">
+ <title>Estrategia: lectura/escritura (read/write)</title>
+
+ <para>
+ Si la aplicación necesita actualizar datos, un caché <literal>read-write</literal> podría ser apropiado.
+ Esta estrategia de caché nunca debe ser usada si se requiere nivel de aislamiento serializable de
+ transacciones. Si el caché es usado en un entorno JTA, debes especificar la propiedad
+ <literal>hibernate.transaction.manager_lookup_class</literal>, mencionando una estrategia para obtener
+ el <literal>TransactionManager</literal> de JTA. En otros entornos, debes asegurarte que la transacción
+ esté completada cuando se llame a <literal>Session.close()</literal> o
+ <literal>Session.disconnect()</literal>. Si deseas usar esta estrategia en un cluster, debes asegurarte
+ que la implementación de caché subyacente soporta bloqueos. Los provedores de caché internos
+ predeterminados <emphasis>no</emphasis> no lo soportan.
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Cat" .... >
+ <cache usage="read-write"/>
+ ....
+ <set name="kittens" ... >
+ <cache usage="read-write"/>
+ ....
+ </set>
+</class>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="performance-cache-nonstrict">
+ <title>Estrategia: lectura/escritura no estricta (nonstrict read/write)</title>
+
+ <para>
+ Si la aplicación necesita sólo ocasionalmente actualizar datos (es decir, es extremadamente inprobable
+ que dos transacciones intenten actualizar el mismo ítem simultáneamente) y no se requiere de un
+ aislamiento de transacciones estricto, un caché <literal>nonstrict-read-write</literal> podría ser
+ apropiado. Si se usa el caché en un entorno JTA, debes especificar
+ <literal>hibernate.transaction.manager_lookup_class</literal>. En otros entornos, debes asegurarte que la
+ transacción se haya completado cuando se llame a <literal>Session.close()</literal> o
+ <literal>Session.disconnect()</literal>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-cache-transactional">
+ <title>Estrategia: transaccional</title>
+
+ <para>
+ La estrategia de caché <literal>transactional</literal> brinda soporte a provedores de cachés
+ completamente transaccionales como TreeCache de JBoss. Un caché así, puede sólo ser usado en un
+ entorno JTA y debes especificar <literal>hibernate.transaction.manager_lookup_class</literal>.
+ </para>
+
+ </sect2>
+
+ <para>
+ Ninguno de los provedores de caché soporta todas las estrategias de concurrencia al caché. La siguiente
+ tabla muestra qué provedores son compatibles con qué estrategias de concurrencia.
+ </para>
+
+ <table frame="topbot">
+ <title>Soporte a Estrategia de Concurrencia a Caché</title>
+ <tgroup cols='5' align='left' colsep='1' rowsep='1'>
+ <colspec colname='c1' colwidth="1*"/>
+ <colspec colname='c2' colwidth="1*"/>
+ <colspec colname='c3' colwidth="1*"/>
+ <colspec colname='c4' colwidth="1*"/>
+ <colspec colname='c5' colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Caché</entry>
+ <entry>read-only</entry>
+ <entry>nonstrict-read-write</entry>
+ <entry>read-write</entry>
+ <entry>transactional</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>Hashtable (no pensado para uso en producción)</entry>
+ <entry>sí</entry>
+ <entry>sí</entry>
+ <entry>sí</entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>EHCache</entry>
+ <entry>sí</entry>
+ <entry>sí</entry>
+ <entry>sí</entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>OSCache</entry>
+ <entry>sí</entry>
+ <entry>sí</entry>
+ <entry>sí</entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>SwarmCache</entry>
+ <entry>sí</entry>
+ <entry>sí</entry>
+ <entry></entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>JBoss TreeCache</entry>
+ <entry>sí</entry>
+ <entry></entry>
+ <entry></entry>
+ <entry>sí</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect1>
+
+ <sect1 id="performance-sessioncache" revision="2">
+ <title>Gestionando los cachés</title>
+
+ <para>
+ Siempre que pases un objeto a <literal>save()</literal>, <literal>update()</literal>
+ o <literal>saveOrUpdate()</literal> y siempre que recuperes un objeto usando
+ <literal>load()</literal>, <literal>get()</literal>, <literal>list()</literal>,
+ <literal>iterate()</literal> o <literal>scroll()</literal>, ese objeto es agregado
+ al caché interno de la <literal>Session</literal>.
+ </para>
+ <para>
+ Cuando subsecuentemente se llame a <literal>flush()</literal>, el estado de ese objeto será
+ sincronizado con la base de datos. Si no quieres que ocurra esta sincronización o si estás
+ procesando un número enorme de objetos y necesitas gestionar la memoria eficientemente,
+ puede usarse el método <literal>evict()</literal> para quitar el objeto y sus colecciones
+ del caché de primer nivel.
+ </para>
+
+ <programlisting><![CDATA[ScrollableResult cats = sess.createQuery("from Cat as cat").scroll(); //a huge result set
+while ( cats.next() ) {
+ Cat cat = (Cat) cats.get(0);
+ doSomethingWithACat(cat);
+ sess.evict(cat);
+}]]></programlisting>
+
+ <para>
+ La <literal>Session</literal> también provee un método <literal>contains()</literal> para determinar
+ si una instancia pertenece al caché de la sesión.
+ </para>
+
+ <para>
+ Para desahuciar (evict) todos los objetos del caché de sesión, llama a <literal>Session.clear()</literal>.
+ </para>
+
+ <para>
+ Para el caché de segundo nivel, hay métodos definidos en <literal>SessionFactory</literal> para
+ desahuciar el estado en caché de una instancia, clase entera, instancia de colección o rol
+ enter de colección.
+ </para>
+
+ <programlisting><![CDATA[sessionFactory.evict(Cat.class, catId); //evict a particular Cat
+sessionFactory.evict(Cat.class); //evict all Cats
+sessionFactory.evictCollection("Cat.kittens", catId); //evict a particular collection of kittens
+sessionFactory.evictCollection("Cat.kittens"); //evict all kitten collections]]></programlisting>
+
+ <para>
+ El <literal>CacheMode</literal> controla cómo una sesión en particular interactúa con el caché de segundo
+ nivel.
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>CacheMode.NORMAL</literal> - lee ítems desde y escribe ítems hacia el caché de segundo nivel
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>CacheMode.GET</literal> - lee ítems del caché de segundo nivel, pero no escribe al caché de
+ segundo nivel excepto al actualizar datos
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>CacheMode.PUT</literal> - escribe ítems al caché de segundo nivel, pero no lee del caché de segundo
+ nivel
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>CacheMode.REFRESH</literal> - escribe ítems al caché de segundo nivel, pero no lee del caché de
+ segundo nivel, saltándose el efecto de <literal>hibernate.cache.use_minimal_puts</literal>, forzando
+ un refresco del caché de segundo nivel para todos los ítems leídos de la base de datos
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Para navegar por los contenidos de una región de caché de segundo nivel o de consultas, usa la API de
+ <literal>Statistics</literal>:
+ </para>
+
+ <programlisting><![CDATA[Map cacheEntries = sessionFactory.getStatistics()
+ .getSecondLevelCacheStatistics(regionName)
+ .getEntries();]]></programlisting>
+
+ <para>
+ Necesitarás habilitar las estadísticas y, opcionalmente, forzar a Hibernate para que guarde las
+ entradas del caché en un formato más entendible por humanos:
+ </para>
+
+ <programlisting><![CDATA[hibernate.generate_statistics true
+hibernate.cache.use_structured_entries true]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="performance-querycache" revision="1">
+ <title>El Caché de Consultas</title>
+
+ <para>
+ Los conjuntos resultado de consultas también pueden tratarse en caché. Esto sólo es útil para
+ consultas que se ejecutan frecuentemente con los mismos parámetros. Para usar el caché de consultas
+ primero debes habilitarlo:
+ </para>
+
+ <programlisting><![CDATA[hibernate.cache.use_query_cache true]]></programlisting>
+
+ <para>
+ Esta configuración causa la creación de dos nuevas regiones de caché - una teniendo en caché
+ conjuntos resultado de consulta (<literal>org.hibernate.cache.StandardQueryCache</literal>),
+ el otro teniendo timestamps de las actualizaciones más recientes a tablas consultables
+ (<literal>org.hibernate.cache.UpdateTimestampsCache</literal>). Nota que el caché de consultas
+ no pone en caché el estado de las entidades reales en el conjunto resultado; sólo tiene en caché
+ valores indentificadores y resultados de tipo de valor. De modo que el caché de consultas siempre
+ debe ser usado en conjunción con el caché de segundo nivel.
+ </para>
+
+ <para>
+ La mayoría de consultas no se benefician del tratamiento en caché, de modo que por defecto las
+ consultas no son tratadas en caché. Para habilitar el tratamiento en caché, llama a
+ <literal>Query.setCacheable(true)</literal>. Esta llamada permite a la consulta buscar
+ resultados existentes en caché o agregar sus resultados al caché cuando se ejecuta.
+ </para>
+
+ <para>
+ Si requieres un control finamente granularizado sobre las políticas de expiración del caché de
+ consultas, puedes especificar una región de caché con nombre para una consulta en particular
+ llamando a <literal>Query.setCacheRegion()</literal>.
+ </para>
+
+ <programlisting><![CDATA[List blogs = sess.createQuery("from Blog blog where blog.blogger = :blogger")
+ .setEntity("blogger", blogger)
+ .setMaxResults(15)
+ .setCacheable(true)
+ .setCacheRegion("frontpages")
+ .list();]]></programlisting>
+
+ <para>
+ Si la consulta debe forzar un refresco de si región del caché de consultas, debes llamar a
+ <literal>Query.setCacheMode(CacheMode.REFRESH)</literal>. Esto es particularmente útil en casos donde
+ los datos subyacentes pueden haber sido actualizados por medio de un proceso separado (es decir,
+ no modificados a través de Hibernate) y permite a la aplicación refrescar selectivamente conjuntos
+ resultado de consultas en particular. Esto es una alternativa más eficient al desahuciamiento de una
+ región del caché de consultas vía <literal>SessionFactory.evictQueries()</literal>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="performance-collections">
+ <title>Entendiendo el rendimiento de Colecciones</title>
+
+ <para>
+ Ya hemos llevado un buen tiempo hablando sobre colecciones.
+ En esta sección resaltaremos un par de temas más sobre cómo las colecciones
+ se comportan en tiempo de ejecución.
+ </para>
+
+ <sect2 id="performance-collections-taxonomy">
+ <title>Taxonomia</title>
+
+ <para>Hibernate define tres tipos básicos de colecciones:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>colecciones de valores</para>
+ </listitem>
+ <listitem>
+ <para>asociaciones uno a muchos</para>
+ </listitem>
+ <listitem>
+ <para>asociaciones muchos a muchos</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Esta clasificación distingue las varias tablas y relaciones de clave foránea pero no nos
+ dice absolutamente todo lo que necesitamos saber sobre el modelo relacional. Para entender
+ completamente la estructura relacional y las características de rendimiento, debemos considerar
+ la estructura de la clave primaria que es usada por Hibernate para actualizar o borrar filas de
+ colección. Esto sugiere la siguiente clasificación:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>colecciones indexadas</para>
+ </listitem>
+ <listitem>
+ <para>conjuntos (sets)</para>
+ </listitem>
+ <listitem>
+ <para>bolsas (bags)</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Todas las colecciones indexadas (mapas, listas, arrays) tienen una clave primaria
+ consistente de las columnas <literal><key></literal> y <literal><index></literal>.
+ En este caso las actualizaciones de colecciones son usualmente extremadamente eficientes.
+ La clave primaria puede ser indexada fácilmente y una fila en particular puede ser localizada
+ cuando Hibernate intenta actualizarla o borrarla.
+ </para>
+
+ <para>
+ Los conjuntos (sets) tienen una clave primaria consistente en <literal><key></literal>
+ y columnas de elemento. Esto puede ser menos eficiente para algunos tipos de elemento de
+ colección, particularmente elementos compuestos o texto largo, o campos binarios. La base de datos
+ puede no ser capaz de indexar una clave primaria compleja eficientemente. Por otra parte,
+ para asociaciones uno a muchos o muchos a muchos, particularmente en el caso de identificadores
+ sintéticos, es probable que sólo sea tan eficiente. (Nota al márgen: si quieres que
+ <literal>SchemaExport</literal> realmente cree la clave primaria de un <literal><set></literal>
+ por ti, debes declarar todas las columnas como <literal>not-null="true"</literal>.)
+ </para>
+
+ <para>
+ Los mapeos de <literal><idbag></literal> definen una clave delegada, de modo que siempre
+ resulten eficientes de actualizar. De hecho, son el mejor caso.
+ </para>
+
+ <para>
+ Los bags son el peor caso. Ya que un bag permite valores de elementos duplicados y no tiene
+ ninguna columna índice, no puede definirse ninguna clave primaria. Hibernate no tiene forma de
+ distinguir entre filas duplicadas. Hibernate resuelve este problema quitando completamente
+ (en un solo <literal>DELETE</literal>) y recreando la colección siempre que cambia.
+ Esto podría ser muy ineficiente.
+ </para>
+
+ <para>
+ Nota que para una asociación uno-a-muchos, la "clave primaria" puede no ser la clave
+ primaria física de la tabla de base de datos; pero incluso en este caso, la clasificación
+ anterior es útil todavía. (Aún refleja cómo Hibernate "localiza" filas individuales de la
+ colección.)
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-collections-mostefficientupdate">
+ <title>Las listas, mapas, idbags y conjuntos son las colecciones más eficientes de actualizar</title>
+
+ <para>
+ Desde la discusión anterior, debe quedar claro que las colecciones indexadas y
+ (usualmente) los conjuntos permiten la operación más eficiente en términos de añadir,
+ quitar y actualizar elementos.
+ </para>
+
+ <para>
+ Hay, discutiblemente, una ventaja más que las colecciones indexadas tienen sobre otros
+ conjuntos para las asociaciones muchos a muchos o colecciones de valores. Debido a la
+ estructura de un <literal>Set</literal>, Hibernate ni siquiera actualiza una fila con
+ <literal>UPDATE</literal> cuando se "cambia" un elemento. Los cambios a un <literal>Set</literal>
+ siempre funcionan por medio de <literal>INSERT</literal> y <literal>DELETE</literal>
+ (de filas individuales). Una vez más, esta consideración no se aplica a las asociaciones
+ uno a muchos.
+ </para>
+
+ <para>
+ Después de observar que los arrays no pueden ser perezosos, podríamos concluir que las
+ listas, mapas e idbags son los tipos más eficientes de colecciones (no inversas), con los
+ conjuntos (sets) no muy por detrás. Se espera que los sets sean el tipo más común de colección
+ en las aplicaciones de Hibernate. Esto es debido a que la semántica de los sets es la más
+ natural en el modelo relacional.
+ </para>
+
+ <para>
+ Sin embargo, en modelos de dominio de Hibernate bien dieñados, usualmente vemos que la mayoría
+ de las colecciones son de hecho asociaciones uno-a-muchos con <literal>inverse="true"</literal>.
+ Para estas asociaciones, la actualización es manejada por el extremo muchos-a-uno de la asociación,
+ y las consideraciones de este tipo sobre el rendimiento de actualización de colecciones simplemente
+ no se aplican.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-collections-mostefficentinverse">
+ <title>Los Bags y las listas son las colecciones inversas más eficientes</title>
+
+ <para>
+ Justo antes que tires a la zanja los bags para siempre, hay un caso en particular en el que
+ los bags son muchos más eficientes que los conjuntos. Para una colección con
+ <literal>inverse="true"</literal> (el idioma estándar de relaciones uno-a-muchos bidireccionales,
+ por ejemplo) ¡podemos añadir elementos a un bag o lista sin necesidad de inicializar (fetch)
+ los elementos del bag! Esto se debe a que <literal>Collection.add()</literal> o
+ <literal>Collection.addAll()</literal> siempre deben devolver true para un bag o <literal>List</literal>
+ (no como un <literal>Set</literal>). Esto puede hacer el siguiente código común mucho más rápido.
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) sess.load(Parent.class, id);
+ Child c = new Child();
+ c.setParent(p);
+ p.getChildren().add(c); //no need to fetch the collection!
+ sess.flush();]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="performance-collections-oneshotdelete">
+ <title>Borrado de un solo tiro</title>
+
+ <para>
+ Ocasionalmente, borrar los elementos de una colección uno a uno puede ser extremadamente ineficiente.
+ Hibernate no es completamente estúpido, de modo que sabe no hacer eso, en el caso de una colección
+ nueva-vacía (si has llamado a <literal>list.clear()</literal>, por ejemplo). En este caso, Hibernate
+ publicará una sola <literal>DELETE</literal>, ¡y listo!
+ </para>
+
+ <para>
+ Supón que añadimos un solo elemento a una colección de tamaño veinte y luego quitamos dos elementos.
+ Hibernate publicará una sentencia <literal>INSERT</literal> y dos sentencias <literal>DELETE</literal>
+ (a menos que la colección sea un bag). Esto es ciertamente deseable.
+ </para>
+
+ <para>
+ Sin embargo, supón que quitamos dieciocho elementos, dejando dos y luego añadimos tres nuevos elementos.
+ Hay dos formas posibles de proceder
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>borrar dieciocho filas una a una y luego insertar tres filas</para>
+ </listitem>
+ <listitem>
+ <para>quitar toda la colección (en un solo <literal>DELETE</literal> de SQL) e insertar todos los
+ cinco elementos actuales (uno a uno)</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Hibernate no es lo suficientemente inteligente para saber que la segunda opción es probablemente más
+ rápida en este caso. (Y que sería probablemente indeseable para Hibernate ser tan inteligente;
+ este comportamiento podría confundir a disparadores de base de datos, etc.)
+ </para>
+
+ <para>
+ Afortunadamente, puedes forzar este comportamiento (es decir, la segunda estrategia) en cualquier
+ momento descartando (es decir, desreferenciando) la colección original y devolviendo una colección
+ nuevamente instanciada con todos los elementos actuales. Esto puede ser muy útil y potente de vez en
+ cuando.
+ </para>
+
+ <para>
+ Por supuesto, el borrado-de-un-solo-tiro no se aplica a colecciones mapeadas
+ <literal>inverse="true"</literal>.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="performance-monitoring" revision="1">
+ <title>Monitoreando el rendimiento</title>
+
+ <para>
+ La optimización no es de mucho uso sin el monitoreo y el acceso a números de rendimiento. Hibernate provee
+ un rango completo de figuras sobre sus operaciones internas. Las estadísticas en Hibernate están disponibles
+ por <literal>SessionFactory</literal>.
+ </para>
+
+ <sect2 id="performance-monitoring-sf" revision="2">
+ <title>Monitoreando una SessionFactory</title>
+
+ <para>
+ Puedes acceder a las métricas de <literal>SessionFactory</literal> de dos formas.
+ Tu primera opción es llamar a <literal>sessionFactory.getStatistics()</literal> y
+ leer o mostrar por pantalla la <literal>Statistics</literal> por ti mismo.
+ </para>
+
+ <para>
+ Hibernate puede también usar JMX para publicar las métricas si habilitas el MBean
+ <literal>StatisticsService</literal>. Puede habilitar un solo MBean para todas tus
+ <literal>SessionFactory</literal> o una por fábrica. Mira el siguiente código para
+ ejemplos de configuración minimalistas:
+ </para>
+
+ <programlisting><![CDATA[// MBean service registration for a specific SessionFactory
+Hashtable tb = new Hashtable();
+tb.put("type", "statistics");
+tb.put("sessionFactory", "myFinancialApp");
+ObjectName on = new ObjectName("hibernate", tb); // MBean object name
+
+StatisticsService stats = new StatisticsService(); // MBean implementation
+stats.setSessionFactory(sessionFactory); // Bind the stats to a SessionFactory
+server.registerMBean(stats, on); // Register the Mbean on the server]]></programlisting>
+
+
+<programlisting><![CDATA[// MBean service registration for all SessionFactory's
+Hashtable tb = new Hashtable();
+tb.put("type", "statistics");
+tb.put("sessionFactory", "all");
+ObjectName on = new ObjectName("hibernate", tb); // MBean object name
+
+StatisticsService stats = new StatisticsService(); // MBean implementation
+server.registerMBean(stats, on); // Register the MBean on the server]]></programlisting>
+
+ <para>
+ POR HACER: Esto no tiene sentido: En el primer caso, recuperamos y usamos el MBean directamente.
+ En el segundo, debemos proporcionar el nombre JNDI en el que se guarda la fábrica de sesiones antes
+ de usarlo. Usa <literal>hibernateStatsBean.setSessionFactoryJNDIName("my/JNDI/Name")</literal>
+ </para>
+ <para>
+ Puedes (des)activar el monitoreo de una <literal>SessionFactory</literal>
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ en tiempo de configuración, establece <literal>hibernate.generate_statistics</literal> a
+ <literal>false</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+ <itemizedlist>
+ <listitem>
+ <para>
+ en tiempo de ejecución: <literal>sf.getStatistics().setStatisticsEnabled(true)</literal>
+ o <literal>hibernateStatsBean.setStatisticsEnabled(true)</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Las estadísticas pueden ser reajustadas programáticamente usando el método <literal>clear()</literal>.
+ Puede enviarse un resumen a un logger (nivel info) usando el método <literal>logSummary()</literal>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-monitoring-metrics" revision="1">
+ <title>Métricas</title>
+
+ <para>
+ Hibernate provee un número de métricas, desde información muy básica a la especializada
+ sólo relevante en ciertos escenarios. Todos los contadores disponibles se describen en la
+ API de la interface <literal>Statistics</literal>, en tres categorías:
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ Métricas relacionadas al uso general de <literal>Session</literal> usage, tales como
+ número de sesiones abiertas, conexiones JDBC recuperadas, etc,
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Métricas relacionadas a las entidades, colecciones, consultas, y cachés como un todo.
+ (también conocidas como métricas globales).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Métricas detalladas relacionadas a una entidad, colección, consulta o región de caché
+ en particular.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Por ejemplo, puedes comprobar el acceso, pérdida, y radio de colocación de entidades, colecciones
+ y consultas en el caché, y el tiempo promedio que necesita una consulta. Ten en cuenta que el número
+ de milisegundos está sujeto a aproximación en Java. Hibernate está pegado a la precisión de la JVM,
+ en algunas plataformas esto podría incuso ser tener sólo una exactitud de 10 segundos.
+ </para>
+
+ <para>
+ Se usan getters simples para acceder a las métricas globales (es decir, no pegadas a una entidad,
+ colección, región de caché, etc, en particular). Puedes acceder a las métricas de una entidad,
+ colección, región de caché en particular a través de su nombre, y a través de su representación HQL
+ o SQL para las consultas. Por favor refiérete al Javadoc de la API de <literal>Statistics</literal>,
+ <literal>EntityStatistics</literal>, <literal>CollectionStatistics</literal>,
+ <literal>SecondLevelCacheStatistics</literal>, y <literal>QueryStatistics</literal> para más información.
+ El siguiente código muestra un ejemplo sencillo:
+ </para>
+
+ <programlisting><![CDATA[Statistics stats = HibernateUtil.sessionFactory.getStatistics();
+
+double queryCacheHitCount = stats.getQueryCacheHitCount();
+double queryCacheMissCount = stats.getQueryCacheMissCount();
+double queryCacheHitRatio =
+ queryCacheHitCount / (queryCacheHitCount + queryCacheMissCount);
+
+log.info("Query Hit ratio:" + queryCacheHitRatio);
+
+EntityStatistics entityStats =
+ stats.getEntityStatistics( Cat.class.getName() );
+long changes =
+ entityStats.getInsertCount()
+ + entityStats.getUpdateCount()
+ + entityStats.getDeleteCount();
+log.info(Cat.class.getName() + " changed " + changes + "times" );]]></programlisting>
+
+ <para>
+ Para trabajar sobre todas las entidades, colecciones, consultas y regiones de cachés, puedes recuperar la
+ lista de nombres de entidades, colecciones, consultas y regiones de cachés con los siguientes métodos:
+ <literal>getQueries()</literal>, <literal>getEntityNames()</literal>,
+ <literal>getCollectionRoleNames()</literal>, y <literal>getSecondLevelCacheRegionNames()</literal>.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+</chapter>
\ No newline at end of file
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/persistent_classes.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/persistent_classes.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/persistent_classes.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,478 @@
+<chapter id="persistent-classes" revision="2">
+ <title>Clases Persistentes</title>
+
+ <para>
+ Clases presistentes son clases en una aplicación que implementan las
+ entidades del problema de negocio (por ejemplo, Customer y Order en una
+ aplicación de comercio electrónico). No todas las instancias de una
+ clase persistente se considera que estén en el estado persistente,
+ una instancia puede en cambio ser transitoria o estar separada.
+ </para>
+
+ <para>
+ Hibernate funciona mejor si las clases siguen algunas simples reglas, también
+ conocidas como el modelo de programación de Viejas Clases Java Planas
+ (Plain Old Java Object o POJO). Sin embargo, ninguna de estas reglas son
+ requerimientos rígidos. En cambio, Hibernate3 asume muy poco acerca de
+ la naturaleza de tus objetos persistentes. Puedes expresar un modelo de dominio en
+ otras formas: usando árboles de instancias de <literal>Map</literal>,
+ por ejemplo.
+ </para>
+
+ <sect1 id="persistent-classes-pojo">
+ <title>Un ejemplo simple de POJO</title>
+
+ <para>
+ La mayoría de aplicaciones Java requieren una clase
+ representando felinos.
+ </para>
+
+ <programlisting><![CDATA[package eg;
+import java.util.Set;
+import java.util.Date;
+
+public class Cat {
+ private Long id; // identifier
+
+ private Date birthdate;
+ private Color color;
+ private char sex;
+ private float weight;
+ private int litterId;
+
+ private Cat mother;
+ private Set kittens = new HashSet();
+
+ private void setId(Long id) {
+ this.id=id;
+ }
+ public Long getId() {
+ return id;
+ }
+
+ void setBirthdate(Date date) {
+ birthdate = date;
+ }
+ public Date getBirthdate() {
+ return birthdate;
+ }
+
+ void setWeight(float weight) {
+ this.weight = weight;
+ }
+ public float getWeight() {
+ return weight;
+ }
+
+ public Color getColor() {
+ return color;
+ }
+ void setColor(Color color) {
+ this.color = color;
+ }
+
+ void setSex(char sex) {
+ this.sex=sex;
+ }
+ public char getSex() {
+ return sex;
+ }
+
+ void setLitterId(int id) {
+ this.litterId = id;
+ }
+ public int getLitterId() {
+ return litterId;
+ }
+
+ void setMother(Cat mother) {
+ this.mother = mother;
+ }
+ public Cat getMother() {
+ return mother;
+ }
+ void setKittens(Set kittens) {
+ this.kittens = kittens;
+ }
+ public Set getKittens() {
+ return kittens;
+ }
+
+ // addKitten not needed by Hibernate
+ public void addKitten(Cat kitten) {
+ kitten.setMother(this);
+ kitten.setLitterId( kittens.size() );
+ kittens.add(kitten);
+ }
+}]]></programlisting>
+
+ <para>
+ Aquí hay cuatro reglas principales a seguir:
+ </para>
+
+ <sect2 id="persistent-classes-pojo-constructor" revision="1">
+ <title>Implementa un constructor sin argumentos</title>
+
+ <para>
+ <literal>Cat</literal> tiene un contructor sin argumentos. Todas las clases persistentes
+ deben tener un constructor por defecto (que puede no ser público) de modo que Hibernate
+ pueda instanciarlas usando <literal>Constructor.newInstance()</literal>. Recomendamos fuertemente tener
+ un constructor por defecto con al menos visibilidad de <emphasis>package</emphasis> para la
+ generación de proxies en tiempo de ejecución en Hibernate.
+ </para>
+ </sect2>
+
+ <sect2 id="persistent-classes-pojo-identifier" revision="2">
+ <title>Provee una propiedad identificadora (opcional)</title>
+
+ <para>
+ <literal>Cat</literal> tiene una propiedad llamada <literal>id</literal>. Esta
+ propiedad mapea a la columna clave primaria de la tabla de base de datos. La propiedad
+ podría llamarse cualquierCosa, y su tipo podría haber sido cualquier tipo
+ primitivo, cualquier tipo de "envoltura" primitivo, <literal>java.lang.String</literal>
+ o <literal>java.util.Date</literal>. (Si tu tabla de base de datos heredada tiene claves
+ compuestas, puedes incluso usar una clase definida por el usuario con propiedades de
+ estos tipos, ver la sección sobre identificadores compuestos luego.)
+ </para>
+
+ <para>
+ La propiedad identificadora es estrictamente opcional. Puedes olvidarla y dejar que Hibernate
+ siga internamente la pista de los identificadores del objeto. Sin embargo, no recomendamos esto.
+ </para>
+
+ <para>
+ De hecho, alguna funcionalidad está disponible sólo para clases que
+ declaran una propiedad identificadora:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ Reasociación transitiva de objetos separados (actualizaciones o
+ fusiones en cascada) - ver <xref linkend="objectstate-transitive"/>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>Session.saveOrUpdate()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>Session.merge()</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Recomendamos que declares propiedades identificadoras nombradas-consistentemente
+ en clases persistentes. Mas aún, recomendamos que uses un tipo nulable
+ (es decir, no primitivo).
+ </para>
+ </sect2>
+
+ <sect2 id="persistent-classes-pojo-final">
+ <title>Prefiere las clases no finales (opcional)</title>
+ <para>
+ Un aspecto central de Hibernate, <emphasis>proxies</emphasis>, depende de que
+ las clases persistentes sean ya no finales, o sean ya la implementación
+ de una interface que declare todos los métodos públicos.
+ </para>
+ <para>
+ Puedes persistir con Hibernate clases <literal>final</literal> que no implementen una
+ interface, pero no serás capaz de usar proxies para recuperación perezosa
+ de asociaciones, lo que limitará tus opciones para afinar el rendimiento.
+ </para>
+ <para>
+ Debes también evitar declarar métodos <literal>public final</literal>
+ en clases non-final. Si quieres usar una clase con un método <literal>public
+ final</literal>, debes deshabilitar explícitamente el uso de proxies estableciendo
+ <literal>lazy="false"</literal>.
+ </para>
+ </sect2>
+
+ <sect2 id="persistent-classes-pojo-accessors" revision="2">
+ <title>Declara métodos de acceso y modificación para los campos persistentes (opcional)</title>
+ <para>
+ <literal>Cat</literal> declara métodos de acceso para todos sus campos persistente.
+ Muchas otras herramientas ORM persisten directamente variables de instancia. Creemos que
+ es mejor proveer una indirección entre el esquema relacional y las estructuras internas de la clase.
+ Por defecto, Hibernate persiste propiedades del estilo JavaBeans, y reconoce nombres de método
+ de la forma <literal>getFoo</literal>, <literal>isFoo</literal> y <literal>setFoo</literal>.
+ Puedes cambiar a acceso directo a campos para propiedades en particular, de ser necesario.
+ </para>
+
+ <para>
+ Las propiedades <emphasis>no</emphasis> necesitan ser declaradas públicas. Hibernate puede
+ persistir una propiedad con un par get / set <literal>protected</literal> o <literal>private</literal>.
+ </para>
+ </sect2>
+ </sect1>
+
+ <sect1 id="persistent-classes-inheritance">
+ <title>Implementando herencia</title>
+
+ <para>
+ Una subclase puede a su vez observar la primera y segunda regla. Hereda su
+ propiedad identificadora de la superclase, <literal>Cat</literal>.
+ </para>
+
+ <programlisting><![CDATA[package eg;
+
+public class DomesticCat extends Cat {
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+ protected void setName(String name) {
+ this.name=name;
+ }
+}]]></programlisting>
+ </sect1>
+
+ <sect1 id="persistent-classes-equalshashcode" revision="1">
+ <title>Implementando <literal>equals()</literal> y <literal>hashCode()</literal></title>
+
+
+ <para>
+ Tienes que sobrescribir los métodos <literal>equals()</literal> y <literal>hashCode()</literal>
+ si :
+ </para>
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ piensas poner instancias de clases persistentes en un <literal>Set</literal>
+ (la forma recomendada de representar asociaciones multivaluadas)
+ <emphasis>y</emphasis>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ piensas usar reasociación de instancias separadas.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Hibernate garantiza la equivalencia de identidad persistente (fila de base de datos) y
+ identidad Java sólo dentro del ámbito de una sesión en particular.
+ De modo que en el momento que mezclamos instancias recuperadas en sesiones diferentes,
+ debemos implementar <literal>equals()</literal> y <literal>hashCode()</literal> si
+ deseamos tener una semántica significativa de <literal>Set</literal>s.
+ </para>
+
+ <para>
+ La forma más obvia es implementar <literal>equals()</literal>/<literal>hashCode()</literal>
+ comparando el valor identificador de ambos objetos. Si el valor es el mismo, ambos deben ser
+ la misma fila de base de datos, por lo tanto son iguales (si ambos son agregados a un
+ <literal>Set</literal>, sólo tendremos un elemento en el <literal>Set</literal>).
+ Desafortunadamente, no podemos usar este enfoque con identificadores generados! Hibernate sólo
+ asignará valores identificadores a objetos que son persistentes, una instancia recién
+ creada no tendrá ningún valor identificador! Además, si una instancia no está
+ salvada y está actualmente en un <literal>Set</literal>, salvarla asignará un
+ valor identificador al objeto. Si <literal>equals()</literal> and <literal>hashCode()</literal>
+ están basados en el valor identificador, el código hash podría cambiar,
+ rompiendo el contrato de <literal>Set</literal>. Ver el sitio web de Hibernate para una
+ discusión completa de este problema. Observa que esto no es una incidencia de Hibernate,
+ sino la semántica normal de Java de identidad de objeto e igualdad.
+ </para>
+
+ <para>
+ Recomendamos implementar <literal>equals()</literal> y <literal>hashCode()</literal>
+ usando <emphasis>igualdad de clave de negocio (Business key equality)</emphasis>.
+ Igualdad de clave de negocio significa que el método <literal>equals()</literal>
+ compara sólo las propiedades que forman la clave de negocio, una clave que podría
+ identificar nuestra instancia en el mundo real (una clave candidata
+ <emphasis>natural</emphasis>):
+ </para>
+
+ <programlisting><![CDATA[public class Cat {
+
+ ...
+ public boolean equals(Object other) {
+ if (this == other) return true;
+ if ( !(other instanceof Cat) ) return false;
+
+ final Cat cat = (Cat) other;
+
+ if ( !cat.getLitterId().equals( getLitterId() ) ) return false;
+ if ( !cat.getMother().equals( getMother() ) ) return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ int result;
+ result = getMother().hashCode();
+ result = 29 * result + getLitterId();
+ return result;
+ }
+
+}]]></programlisting>
+
+ <para>
+ Nota que una clave de negocio no tiene que ser tan sólida como
+ una clave primaria candidata de base de datos (ver
+ <xref linkend="transactions-basics-identity"/>). Las propiedades inmutables o
+ únicas son usualmente buenas candidatas para una clave de negocio.
+ </para>
+
+ </sect1>
+
+ <sect1 id="persistent-classes-dynamicmodels">
+ <title>Modelos dinámicos</title>
+
+ <para>
+ <emphasis>Ten en cuenta que las siguientes funcionalidades están
+ consideradas actualmente experimentales y pueden cambiar en el futuro
+ cercano.</emphasis>
+ </para>
+
+ <para>
+ Las entidades persistentes no necesariamente tienen que estar representadas
+ como clases POJO o como objetos JavaBean en tiempo de ejecución. Hibernate
+ soporta además modelos dinámicos (usando <literal>Map</literal>s de
+ <literal>Map</literal>s en tiempo de ejecución) y la representación
+ de entidades como árboles de DOM4J. Con este enfoque no escribes clases
+ persistentes, sólo ficheros de mapeo.
+ </para>
+
+ <para>
+ Por defecto, Hibernate funciona en modo POJO normal. Puedes establecer una
+ representación de entidad por defecto para una <literal>SessionFactory</literal>
+ en particular usando la opción de configuración
+ <literal>default_entity_mode</literal>
+ (ver <xref linkend="configuration-optional-properties"/>).
+ </para>
+
+ <para>
+ Los siguientes ejemplos demuestran la representación usando
+ <literal>Map</literal>s. Primero, en el fichero de mapeo,
+ tiene que declararse un <literal>entity-name</literal> en vez de
+ (o como agregado a) un nombre de clase:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class entity-name="Customer">
+
+ <id name="id"
+ type="long"
+ column="ID">
+ <generator class="sequence"/>
+ </id>
+
+ <property name="name"
+ column="NAME"
+ type="string"/>
+
+ <property name="address"
+ column="ADDRESS"
+ type="string"/>
+
+ <many-to-one name="organization"
+ column="ORGANIZATION_ID"
+ class="Organization"/>
+
+ <bag name="orders"
+ inverse="true"
+ lazy="false"
+ cascade="all">
+ <key column="CUSTOMER_ID"/>
+ <one-to-many class="Order"/>
+ </bag>
+
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Ten en cuenta que aunque las asociaciones se declaran usando nombres
+ de clase objetivo, el tipo objetivo de una asociación puede
+ ser además una entidad dinámica en vez de un POJO.
+ </para>
+
+ <para>
+ Después de establecer el modo de entidad por defecto a
+ <literal>dynamic-map</literal> para la <literal>SessionFactory</literal>,
+ podemos trabajar en tiempo de ejecución con <literal>Map</literal>s
+ de <literal>Map</literal>s:
+ </para>
+
+ <programlisting><![CDATA[Session s = openSession();
+Transaction tx = s.beginTransaction();
+Session s = openSession();
+
+// Create a customer
+Map david = new HashMap();
+david.put("name", "David");
+
+// Create an organization
+Map foobar = new HashMap();
+foobar.put("name", "Foobar Inc.");
+
+// Link both
+david.put("organization", foobar);
+
+// Save both
+s.save("Customer", david);
+s.save("Organization", foobar);
+
+tx.commit();
+s.close();]]></programlisting>
+
+ <para>
+ Las ventajas de un mapeo dinámico es rápido tiempo de ciclo
+ de prototipado sin la necesidad de implementación de clases de entidad.
+ Sin embargo, pierdes chequeo de tipos en tiempo de compilación y
+ muy probablemente tratarás con muchas excepciones en tiempo de ejecución.
+ Gracias al mapeo de Hibernate, el esquema de base de datos puede estar facilmente
+ sano y normalizado, permitiendo agregar una implementación apropiada del
+ modelo de dominio más tarde.
+ </para>
+
+ <para>
+ Los modos de representación de entidad pueden ser establecidos
+ por <literal>Session</literal>:
+ </para>
+
+ <programlisting><![CDATA[Session dynamicSession = pojoSession.getSession(EntityMode.MAP);
+
+// Create a customer
+Map david = new HashMap();
+david.put("name", "David");
+dynamicSession.save("Customer", david);
+...
+dynamicSession.flush();
+dynamicSession.close()
+...
+// Continue on pojoSession
+]]></programlisting>
+
+
+ <para>
+ Por favor, ten en cuenta que la llamada a <literal>getSession()</literal>
+ usando un <literal>EntityMode</literal> está en la API de
+ <literal>Session</literal>, no en la de <literal>SessionFactory</literal>.
+ De esta forma, la nueva <literal>Session</literal> comparte la conexión
+ JDBC, transacción y otra información de contexto. Esto significa
+ que no tienes que llamar a <literal>flush()</literal> ni a <literal>close()</literal>
+ en la <literal>Session</literal> secundaria, y tembién dejar el manejo
+ de la transacción y de la conexión a la unidad de trabajo primaria.
+ </para>
+
+ <para>
+ Puede encontrarse más información sobre las capacidades de
+ representación XML en <xref linkend="xml"/>.
+ </para>
+
+ </sect1>
+
+ <para>
+ PORHACER: Documentar el framework de extensiones del usuario en los paquetes
+ de propiedad y proxies.
+ </para>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/query_criteria.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/query_criteria.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/query_criteria.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,431 @@
+<chapter id="querycriteria">
+ <title>Consultas por Criterios</title>
+
+ <para>
+ Acompaña a Hibernate una API de consultas por criterios intuitiva y extensible.
+ </para>
+
+ <sect1 id="querycriteria-creating">
+ <title>Creando una instancia de <literal>Criteria</literal></title>
+
+ <para>
+ La interface <literal>org.hibernate.Criteria</literal> representa una consulta contra
+ una clase persistente en particular. La <literal>Session</literal> es una fábrica de instancias
+ de <literal>Criteria</literal>.
+ </para>
+
+ <programlisting><![CDATA[Criteria crit = sess.createCriteria(Cat.class);
+crit.setMaxResults(50);
+List cats = crit.list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-narrowing">
+ <title>Estrechando el conjunto resultado</title>
+
+ <para>
+ Un criterio individual de consulta es una instancia de la interface
+ <literal>org.hibernate.criterion.Criterion</literal>. La clase
+ <literal>org.hibernate.criterion.Restrictions</literal> define métodos de fábrica para obtener ciertos tipos
+ prefabricados de <literal>Criterion</literal>.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "Fritz%") )
+ .add( Restrictions.between("weight", minWeight, maxWeight) )
+ .list();]]></programlisting>
+
+ <para>
+ Las restricciones pueden ser agrupadas lógicamente.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "Fritz%") )
+ .add( Restrictions.or(
+ Restrictions.eq( "age", new Integer(0) ),
+ Restrictions.isNull("age")
+ ) )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.in( "name", new String[] { "Fritz", "Izi", "Pk" } ) )
+ .add( Restrictions.disjunction()
+ .add( Restrictions.isNull("age") )
+ .add( Restrictions.eq("age", new Integer(0) ) )
+ .add( Restrictions.eq("age", new Integer(1) ) )
+ .add( Restrictions.eq("age", new Integer(2) ) )
+ ) )
+ .list();]]></programlisting>
+
+ <para>
+ Hay un gran rango de tipos de criterio prefabricados (subclases de <literal>Restrictions</literal>),
+ pero uno que es especialmente útil te deja especificar SQL directamente.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.sql("lower({alias}.name) like lower(?)", "Fritz%", Hibernate.STRING) )
+ .list();]]></programlisting>
+
+ <para>
+ El sitio <literal>{alias}</literal> será remplazado por el alias de fila de la entidad consultada.
+ </para>
+
+ <para>
+ Un enfoque alternativo para obtener un criterio es tomarlo de una instancia de
+ <literal>Property</literal>. Puedes crear una <literal>Property</literal> llamando a
+ <literal>Property.forName()</literal>.
+ </para>
+
+ <programlisting><![CDATA[
+Property age = Property.forName("age");
+List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.disjunction()
+ .add( age.isNull() )
+ .add( age.eq( new Integer(0) ) )
+ .add( age.eq( new Integer(1) ) )
+ .add( age.eq( new Integer(2) ) )
+ ) )
+ .add( Property.forName("name").in( new String[] { "Fritz", "Izi", "Pk" } ) )
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-ordering">
+ <title>Ordenando los resultados</title>
+
+ <para>
+ Puedes ordenar los resultados usando <literal>org.hibernate.criterion.Order</literal>.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "F%")
+ .addOrder( Order.asc("name") )
+ .addOrder( Order.desc("age") )
+ .setMaxResults(50)
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Property.forName("name").like("F%") )
+ .addOrder( Property.forName("name").asc() )
+ .addOrder( Property.forName("age").desc() )
+ .setMaxResults(50)
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-associations">
+ <title>Asociaciones</title>
+
+ <para>
+ Puedes especificar fácilmente restricciones sobre las entidades relacionadas al navegar asociaciones
+ usando <literal>createCriteria()</literal>.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "F%")
+ .createCriteria("kittens")
+ .add( Restrictions.like("name", "F%")
+ .list();]]></programlisting>
+
+ <para>
+ nota que el segundo <literal>createCriteria()</literal> devuelve una nueva instancia de
+ <literal>Criteria</literal>, que hace referencia a los elementos de la colección
+ <literal>kittens</literal>.
+ </para>
+
+ <para>
+ La siguiente forma alternativa es útil en ciertas circunstancias.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .createAlias("kittens", "kt")
+ .createAlias("mate", "mt")
+ .add( Restrictions.eqProperty("kt.name", "mt.name") )
+ .list();]]></programlisting>
+
+ <para>
+ (<literal>createAlias()</literal> no crea una nueva instancia de
+ <literal>Criteria</literal>.)
+ </para>
+
+ <para>
+ ¡Observa que las colecciones de gatitos tenidas por las instancias de <literal>Cat</literal> devueltas
+ por las dos consultas previas <emphasis>no</emphasis> están prefiltradas por los criterios! Si deseas
+ recuperar sólo los gatitos que emparejen los criterios, debes usar <literal>returnMaps()</literal>.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .createCriteria("kittens", "kt")
+ .add( Restrictions.eq("name", "F%") )
+ .returnMaps()
+ .list();
+Iterator iter = cats.iterator();
+while ( iter.hasNext() ) {
+ Map map = (Map) iter.next();
+ Cat cat = (Cat) map.get(Criteria.ROOT_ALIAS);
+ Cat kitten = (Cat) map.get("kt");
+}]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-dynamicfetching" revision="1">
+ <title>Recuperación dinámica de asociaciones</title>
+
+ <para>
+ Puedes especificar la semántica de recuperación de asociaciones en tiempo de ejecución usando
+ <literal>setFetchMode()</literal>.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "Fritz%") )
+ .setFetchMode("mate", FetchMode.EAGER)
+ .setFetchMode("kittens", FetchMode.EAGER)
+ .list();]]></programlisting>
+
+ <para>
+ Esta consulta recuperará tanto <literal>mate</literal> como <literal>kittens</literal> por
+ unión exterior (outer join). Ver <xref linkend="performance-fetching"/> para más información.
+ </para>
+
+ </sect1>
+
+ <sect1 id="querycriteria-examples">
+ <title>Consultas por ejemplos</title>
+
+ <para>
+ La clase <literal>org.hibernate.criterion.Example</literal> te permite construir un criterio de consulta
+ a partir de una instancia dada.
+ </para>
+
+ <programlisting><![CDATA[Cat cat = new Cat();
+cat.setSex('F');
+cat.setColor(Color.BLACK);
+List results = session.createCriteria(Cat.class)
+ .add( Example.create(cat) )
+ .list();]]></programlisting>
+
+ <para>
+ Las propiedades de versión, los identificadores y las asociaciones son ignorados. Por defecto,
+ las propiedades valuadas a nulo son excluídas.
+ </para>
+
+ <para>
+ Puedes ajustar cómo se aplica el <literal>Example</literal>.
+ </para>
+
+ <programlisting><![CDATA[Example example = Example.create(cat)
+ .excludeZeroes() //exclude zero valued properties
+ .excludeProperty("color") //exclude the property named "color"
+ .ignoreCase() //perform case insensitive string comparisons
+ .enableLike(); //use like for string comparisons
+List results = session.createCriteria(Cat.class)
+ .add(example)
+ .list();]]></programlisting>
+
+ <para>
+ Puedes incluso usar ejemplos para colocar criterios sobre objetos asociados.
+ </para>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .add( Example.create(cat) )
+ .createCriteria("mate")
+ .add( Example.create( cat.getMate() ) )
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-projection">
+ <title>Proyecciones, agregación y agrupamiento</title>
+ <para>
+ La clase <literal>org.hibernate.criterion.Projections</literal> es una fábrica de instancias de
+ <literal>Projection</literal>. Aplicamos una proyección a una consulta llamando a
+ <literal>setProjection()</literal>.
+ </para>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.rowCount() )
+ .add( Restrictions.eq("color", Color.BLACK) )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.projectionList()
+ .add( Projections.rowCount() )
+ .add( Projections.avg("weight") )
+ .add( Projections.max("weight") )
+ .add( Projections.groupProperty("color") )
+ )
+ .list();]]></programlisting>
+
+ <para>
+ No es necesario ningún "group by" explícito en una consulta por criterios.
+ Ciertos tipos de proyecciones son definidos para ser <emphasis>proyecciones agrupadas</emphasis>,
+ que además aparecen en la cláusula SQL <literal>group by</literal>.
+ </para>
+
+ <para>
+ Puede opcionalmente asignarse un alias a una proyección, de modo que el valor proyectado pueda
+ ser referido en restricciones u ordenamientos. Aquí hay dos formas diferentes de hacer esto:
+ </para>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.alias( Projections.groupProperty("color"), "colr" ) )
+ .addOrder( Order.asc("colr") )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.groupProperty("color").as("colr") )
+ .addOrder( Order.asc("colr") )
+ .list();]]></programlisting>
+
+ <para>
+ Los métodos <literal>alias()</literal> y <literal>as()</literal> simplemente envuelven una instancia
+ de proyección en otra instancia de <literal>Projection</literal> con alias. Como un atajo, puedes asignar
+ un alias cuando agregas la proyección a una lista de proyecciones:
+ </para>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.projectionList()
+ .add( Projections.rowCount(), "catCountByColor" )
+ .add( Projections.avg("weight"), "avgWeight" )
+ .add( Projections.max("weight"), "maxWeight" )
+ .add( Projections.groupProperty("color"), "color" )
+ )
+ .addOrder( Order.desc("catCountByColor") )
+ .addOrder( Order.desc("avgWeight") )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Domestic.class, "cat")
+ .createAlias("kittens", "kit")
+ .setProjection( Projections.projectionList()
+ .add( Projections.property("cat.name"), "catName" )
+ .add( Projections.property("kit.name"), "kitName" )
+ )
+ .addOrder( Order.asc("catName") )
+ .addOrder( Order.asc("kitName") )
+ .list();]]></programlisting>
+
+ <para>
+ Puedes también usar <literal>Property.forName()</literal> para expresar proyecciones:
+ </para>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Property.forName("name") )
+ .add( Property.forName("color").eq(Color.BLACK) )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.projectionList()
+ .add( Projections.rowCount().as("catCountByColor") )
+ .add( Property.forName("weight").avg().as("avgWeight") )
+ .add( Property.forName("weight").max().as("maxWeight") )
+ .add( Property.forName("color").group().as("color" )
+ )
+ .addOrder( Order.desc("catCountByColor") )
+ .addOrder( Order.desc("avgWeight") )
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-detachedqueries">
+ <title>Consultas y subconsultas separadas</title>
+ <para>
+ La clase <literal>DetachedCriteria</literal> te deja crear una consulta fuera del ámbito de una sesión,
+ y entonces ejecutarla luego usando alguna <literal>Session</literal> arbitraria.
+ </para>
+
+ <programlisting><![CDATA[DetachedCriteria query = DetachedCriteria.forClass(Cat.class)
+ .add( Property.forName("sex").eq('F') );
+
+Session session = ....;
+Transaction txn = session.beginTransaction();
+List results = query.getExecutableCriteria(session).setMaxResults(100).list();
+txn.commit();
+session.close();]]></programlisting>
+
+ <para>
+ También una <literal>DetachedCriteria</literal> puede usarse para expresar una subconsulta.
+ Las instancias de Criterion implicando subconsultas pueden obtenerse vía <literal>Subqueries</literal> o
+ <literal>Property</literal>.
+ </para>
+
+ <programlisting><![CDATA[DetachedCriteria avgWeight = DetachedCriteria.forClass(Cat.class)
+ .setProjection( Property.forName("weight").avg() );
+session.createCriteria(Cat.class)
+ .add( Property.forName("weight).gt(avgWeight) )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[DetachedCriteria weights = DetachedCriteria.forClass(Cat.class)
+ .setProjection( Property.forName("weight") );
+session.createCriteria(Cat.class)
+ .add( Subqueries.geAll("weight", weights) )
+ .list();]]></programlisting>
+
+ <para>
+ Incluso son posibles las subconsultas correlacionadas:
+ </para>
+
+ <programlisting><![CDATA[DetachedCriteria avgWeightForSex = DetachedCriteria.forClass(Cat.class, "cat2")
+ .setProjection( Property.forName("weight").avg() )
+ .add( Property.forName("cat2.sex").eqProperty("cat.sex") );
+session.createCriteria(Cat.class, "cat")
+ .add( Property.forName("weight).gt(avgWeightForSex) )
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <!--TODO: ResultSetTransformer + aliasing. AliasToBeanTransformer allow returning arbitrary
+ user objects - similar to setResultClass in JDO2. General use of ResultTransformer
+ could also be explained. -->
+
+ <sect1 id="query-criteria-naturalid">
+ <title>Consultas por identificador natural</title>
+
+ <para>
+ Para la mayoría de consultas, incluyendo las consultas por criterios, el caché de consulta no es
+ muy eficiente, debido a que la invalidación del caché de consulta ocurre demasiado frecuentemente.
+ Sin embargo, hay un tipo especial de consulta donde podemos optimizar el algoritmo de invalidación
+ de caché: búsquedas por una clave natural constante. En algunas aplicaciones, este tipo de consulta,
+ ocurre frecuentemente. La API de criterios brinda especial provisión para este caso de uso.
+ </para>
+
+ <para>
+ Primero, debes mapear la clave natural de tu entidad usando
+ <literal><natural-id></literal>, y habilitar el uso del caché de segundo nivel.
+ </para>
+
+ <programlisting><![CDATA[<class name="User">
+ <cache usage="read-write"/>
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <natural-id>
+ <property name="name"/>
+ <property name="org"/>
+ </natural-id>
+ <property name="password"/>
+</class>]]></programlisting>
+
+ <para>
+ Nota que esta funcionalidad no está pensada para uso con entidades con claves naturales
+ <emphasis>mutable</emphasis>.
+ </para>
+
+ <para>
+ Seguido, habilita el caché de consulta de Hibernate.
+ </para>
+
+ <para>
+ Ahora, <literal>Restrictions.naturalId()</literal> nos permite hacer uso de el algoritmo de caché
+ más eficiente.
+ </para>
+
+ <programlisting><![CDATA[session.createCriteria(User.class)
+ .add( Restrictions.naturalId()
+ .set("name", "gavin")
+ .set("org", "hb")
+ ).setCacheable(true)
+ .uniqueResult();]]></programlisting>
+
+ </sect1>
+
+</chapter>
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/query_hql.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/query_hql.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/query_hql.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1107 @@
+<chapter id="queryhql">
+ <title>HQL: El Lenguaje de Consulta de Hibernate</title>
+
+ <para>
+ Hibernate está equipado con un lenguaje de consulta extremadamente potente que
+ (intencionalmente en absoluto) se parece muchísimo a SQL. Pero no te engañes por la sintaxis;
+ HQL es completamente orientado a objetos, entendiendo nociones como herencia, polimorfismo
+ y asociación.
+ </para>
+
+ <sect1 id="queryhql-casesensitivity">
+ <title>Sensibilidad a Mayúsculas</title>
+
+ <para>
+ Las consultas son insensibles a mayúsculas, excepto para nombres de clases Java y propiedades. De modo que
+ <literal>SeLeCT</literal> es lo mismo que <literal>sELEct</literal> e igual a <literal>SELECT</literal>,
+ pero <literal>org.hibernate.eg.FOO</literal> no lo es a <literal>org.hibernate.eg.Foo</literal> y
+ <literal>foo.barSet</literal> no es igual a <literal>foo.BARSET</literal>.
+ </para>
+
+ <para>
+ Este manual usa palabras clave HQL en minúsculas. Algunos usuarios encuentran las consultas con
+ palabras clave en mayúsculas más leíbles, pero encontramos esta convención fea cuando se encaja
+ en código Java.
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-from">
+ <title>La cláusula from</title>
+
+ <para>
+ La consulta más simple posible de Hibernate es de la forma:
+ </para>
+
+ <programlisting><![CDATA[from eg.Cat]]></programlisting>
+
+ <para>
+ que simplemente devuelve todas las instancias de la clase <literal>eg.Cat</literal>.
+ Usualmente no necesitamos cualificar el nombre de la clase, ya que <literal>auto-import</literal>
+ está por defecto. De modo que casi siempre escribimos solamente:
+ </para>
+
+ <programlisting><![CDATA[from Cat]]></programlisting>
+
+ <para>
+ La mayoría del tiempo, necesitarás asignar un <emphasis>alias</emphasis>, ya que querrás referirte al
+ <literal>Cat</literal> en otras partes de la consulta.
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat]]></programlisting>
+
+ <para>
+ Esta consulta asigna el alias <literal>cat</literal> a las instancias de <literal>Cat</literal>,
+ de modo que podríamos usar ese alias luego en la consulta. La palabra clave <literal>as</literal>
+ es opcional; también podríamos escribir:
+ </para>
+
+ <programlisting><![CDATA[from Cat cat]]></programlisting>
+
+ <para>
+ Pueden aparecer múltiples clases, resultando en un producto cartesiano o unión "cruzada" (cross join).
+ </para>
+
+ <programlisting><![CDATA[from Formula, Parameter]]></programlisting>
+ <programlisting><![CDATA[from Formula as form, Parameter as param]]></programlisting>
+
+ <para>
+ Se considera buena práctica el nombrar los alias de consulta usando una inicial en minúsculas,
+ consistente con los estándares de nombrado de Java para variables locales
+ (por ejemplo, <literal>domesticCat</literal>).
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-joins" revision="1">
+ <title>Asociaciones y uniones (joins)</title>
+
+ <para>
+ Podemos también asignar aliases a entidades asociadas, e incluso a elementos de una colección de valores,
+ usando una <literal>join</literal>.
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+ inner join cat.mate as mate
+ left outer join cat.kittens as kitten]]></programlisting>
+
+ <programlisting><![CDATA[from Cat as cat left join cat.mate.kittens as kittens]]></programlisting>
+
+ <programlisting><![CDATA[from Formula form full join form.parameter param]]></programlisting>
+
+ <para>
+ Los tipos de join soportados son prestados de ANSI SQL
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>inner join</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>left outer join</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>right outer join</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>full join</literal> (no útil usualmente)
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Las construcciones <literal>inner join</literal>, <literal>left outer join</literal> y
+ <literal>right outer join</literal> pueden ser abreviadas.
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+ join cat.mate as mate
+ left join cat.kittens as kitten]]></programlisting>
+
+ <para>
+ Puedes proveer condiciones de unión extra usando la palabra clave <literal>with</literal> de HQL.
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+ left join cat.kittens as kitten
+ with kitten.bodyWeight > 10.0]]></programlisting>
+
+ <para>
+ En adición, un "fetch" join permite a las asociaciones o colecciones de valores ser inicializadas
+ junto a sus objetos padres, usando una sola selección. Esto es particularmente útil en el case de una
+ colección. Efectivamente sobrescribe el outer join y las declaraciones perezosas (lazy) del fichero
+ de mapeo para asociaciones y colecciones. Ver <xref linkend="performance-fetching"/> para más
+ información.
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+ inner join fetch cat.mate
+ left join fetch cat.kittens]]></programlisting>
+
+ <para>
+ Usualmente a un fetch join no se necesita asignársele un alias, porque los objetos asociados no deben
+ ser usados en la cláusula <literal>where</literal> (ni en cualquier otra cláusula). Además, los objetos
+ asociados no son devueltos directamente en los resultados de consulta. En cambio, pueden ser accedidos
+ vía el objeto padre. La única razón por la que necesitaríamos un alias es estamos uniendo recursivamente
+ otra colección:
+ </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>
+ Nota que la construcción <literal>fetch</literal> no puede usarse en consultas llamadas usando
+ <literal>scroll()</literal> o <literal>iterate()</literal>. Ni debe usarse <literal>fetch</literal>
+ junto con <literal>setMaxResults()</literal> o <literal>setFirstResult()</literal>. Tampoco puede usarse
+ <literal>fetch</literal> junto a una condición <literal>with</literal> ad hoc. Es posible crear
+ un producto cartesiano trayendo por join más de una colección en una colección, así que ten cuidado en
+ este caso. Traer por join múltiples roles de colección también da a veces resultados inesperados para mapeos
+ de bag, así que sé cuidadoso sobre cómo formular tus consultas en este caso. Finalmente, nota que
+ <literal>full join fetch</literal> y <literal>right join fetch</literal> no son significativos.
+ </para>
+
+ <para>
+ Si estás usando recuperación perezosa a nivel de propiedad (con instrumentación de bytecode), es posible
+ forzar a Hibernate a traer las propiedades perezosas inmediatamente (en la primera consulta) usando
+ <literal>fetch all properties</literal>.
+ </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>
+
+ </sect1>
+
+ <sect1 id="queryhql-select">
+ <title>La cláusula select</title>
+
+ <para>
+ La cláusula <literal>select</literal> escoge qué objetos y propiedades devolver in el conjunto resultado
+ de la consulta. Considera:
+ </para>
+
+ <programlisting><![CDATA[select mate
+from Cat as cat
+ inner join cat.mate as mate]]></programlisting>
+
+ <para>
+ La consulta seleccionará <literal>mate</literal>s de otros <literal>Cat</literal>s.
+ Realmente, puedes expresar esta consulta en un forma más compacta como:
+ </para>
+
+ <programlisting><![CDATA[select cat.mate from Cat cat]]></programlisting>
+
+ <para>
+ Las consultas pueden devolver propiedades de cualquier tipo de valor incluyendo propiedades de
+ tipo componente:
+ </para>
+
+ <programlisting><![CDATA[select cat.name from DomesticCat cat
+where cat.name like 'fri%']]></programlisting>
+
+ <programlisting><![CDATA[select cust.name.firstName from Customer as cust]]></programlisting>
+
+ <para>
+ Las consultas pueden devolver múltiples objetos y/o propiedades como un array de tipo
+ <literal>Object[]</literal>,
+ </para>
+
+ <programlisting><![CDATA[select mother, offspr, mate.name
+from DomesticCat as mother
+ inner join mother.mate as mate
+ left outer join mother.kittens as offspr]]></programlisting>
+
+ <para>
+ o como una <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>
+ o como un objeto real Java de tipo seguro,
+ </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>
+ asumiendo que la clase <literal>Family</literal> tiene un constructor apropiado.
+ </para>
+
+ <para>
+ Puedes asignar aliases para seleccionar expresiones usando <literal>as</literal>:
+ </para>
+
+ <programlisting><![CDATA[select max(bodyWeight) as max, min(bodyWeight) as min, count(*) as n
+from Cat cat]]></programlisting>
+
+ <para>
+ Esto es lo más útil cuando se usa junto con <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>
+ Esta consulta devuelve un <literal>Map</literal> de aliases a valores seleccionados.
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-aggregation">
+ <title>Funciones de agregación</title>
+
+ <para>
+ Las consultas HQL pueden incluso devolver resultados de funciones de agregación sobre propiedades:
+ </para>
+
+ <programlisting><![CDATA[select avg(cat.weight), sum(cat.weight), max(cat.weight), count(cat)
+from Cat cat]]></programlisting>
+
+<!-- NO LONGER SUPPORTED
+ <para>
+ Collections may also appear inside aggregate functions in the <literal>select</literal>
+ clause.
+ </para>
+
+ <programlisting><![CDATA[select cat, count( elements(cat.kittens) )
+from Cat cat group by cat]]></programlisting>
+-->
+
+ <para>
+ Las funciones de agregación soportadas son
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>avg(...), sum(...), min(...), max(...)</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>count(*)</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>count(...), count(distinct ...), count(all...)</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Puedes usar operadores aritméticos, concatenación, y funciones SQL reconocidas en la cláusula select:
+ </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>
+ Las palabras clave <literal>distinct</literal> y <literal>all</literal> pueden ser usadas y tienen las misma
+ semántica que en SQL.
+ </para>
+
+ <programlisting><![CDATA[select distinct cat.name from Cat cat
+
+select count(distinct cat.name), count(cat) from Cat cat]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="queryhql-polymorphism">
+ <title>Consultas polimórficas</title>
+
+ <para>
+ Una consulta como:
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat]]></programlisting>
+
+ <para>
+ devuelve instancias no sólo de <literal>Cat</literal>, sino también de subclases como
+ <literal>DomesticCat</literal>. Las consultas de Hibernate pueden mencionar <emphasis>cualquier</emphasis>
+ clase o interface Java en la cláusula <literal>from</literal>. La consulta devolverá instancias de todas
+ las clases persistentes que extiendan esa clase o implementen la interface. La siguiente consulta devolvería
+ todos los objetos persistentes.
+ </para>
+
+ <programlisting><![CDATA[from java.lang.Object o]]></programlisting>
+
+ <para>
+ La interface <literal>Named</literal> podría ser implementada por varias clases persistentes:
+ </para>
+
+ <programlisting><![CDATA[from Named n, Named m where n.name = m.name]]></programlisting>
+
+ <para>
+ Nota que estas dos últimas consultas requerirán más de un <literal>SELECT</literal> SQL. Esto significa
+ que la cláusula <literal>order by</literal> no ordenará correctamente todo el conjunto resultado.
+ (Significa además que no puedes llamar estas consulta usando <literal>Query.scroll()</literal>.)
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-where">
+ <title>La cláusula where</title>
+
+ <para>
+ La cláusula where te permite estrechar la lista de instancias devueltas. Si no existe ningún alias.
+ puedes referirte a las propiedades por nombre:
+ </para>
+
+ <programlisting><![CDATA[from Cat where name='Fritz']]></programlisting>
+
+ <para>
+ Si existe un alias, usan un nombre cualificado de propiedad:
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat where cat.name='Fritz']]></programlisting>
+
+ <para>
+ devuelve las instancias de <literal>Cat</literal> llamadas 'Fritz'.
+ </para>
+
+ <programlisting><![CDATA[select foo
+from Foo foo, Bar bar
+where foo.startDate = bar.date]]></programlisting>
+
+ <para>
+ devolverá todas las instancias de <literal>Foo</literal> para las cuales exista una instancia
+ de <literal>bar</literal> con una propiedad <literal>date</literal> igual a la propiedad
+ <literal>startDate</literal> del <literal>Foo</literal>. Las expresiones de ruta compuestas hacen
+ la cláusula <literal>where</literal> extremadamente potente. Considera:
+ </para>
+
+ <programlisting><![CDATA[from Cat cat where cat.mate.name is not null]]></programlisting>
+
+ <para>
+ Esta consulta se traduce en una consulta SQL con una unión de tabla (interna). Si fueses a escribir algo como
+ </para>
+
+ <programlisting><![CDATA[from Foo foo
+where foo.bar.baz.customer.address.city is not null]]></programlisting>
+
+ <para>
+ terminarías con una consulta que requeriría cuatro uniones de tablas en SQL.
+ </para>
+
+ <para>
+ El operador <literal>=</literal> puede ser usado para comparar no sólo propiedades, sino también instancias:
+ </para>
+
+ <programlisting><![CDATA[from Cat cat, Cat rival where cat.mate = rival.mate]]></programlisting>
+
+ <programlisting><![CDATA[select cat, mate
+from Cat cat, Cat mate
+where cat.mate = mate]]></programlisting>
+
+ <para>
+ La propiedad especial (en minúsculas) <literal>id</literal> puede ser usada para referenciar el identificador
+ único de un objeto. (También puedes usar su nombre de propiedad.)
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat where cat.id = 123
+
+from Cat as cat where cat.mate.id = 69]]></programlisting>
+
+ <para>
+ La segunda consulta es eficiente. ¡No se requiere ninguna unión de tablas!
+ </para>
+
+ <para>
+ También pueden ser usadas las propiedades de identificadores compuestos. Supón que <literal>Person</literal>
+ tiene un identificador compuesto consistente en <literal>country</literal> y <literal>medicareNumber</literal>.
+ </para>
+
+ <programlisting><![CDATA[from bank.Person person
+where person.id.country = 'AU'
+ and person.id.medicareNumber = 123456]]></programlisting>
+
+ <programlisting><![CDATA[from bank.Account account
+where account.owner.id.country = 'AU'
+ and account.owner.id.medicareNumber = 123456]]></programlisting>
+
+ <para>
+ Una vez más, la segunda consulta no requiere ninguna unión de tablas.
+ </para>
+
+ <para>
+ Asimismo, la propiedad especial <literal>class</literal> acccede al valor discriminador de una instancia en
+ el caso de persistencia polimórfica. Un nombre de clase Java embebido en la cláusula where será
+ traducido a su valor discriminador.
+ </para>
+
+ <programlisting><![CDATA[from Cat cat where cat.class = DomesticCat]]></programlisting>
+
+ <para>
+ Puedes también especificar propiedades de componentes o tipos compuestos de usuario (y de componentes
+ de componentes, etc). Nunca intentes usar una expresión de ruta que termine en una propiedad de tipo
+ componente (al contrario de una propiedad de un componente). Por ejemplo, si <literal>store.owner</literal>
+ es una entidad con un componente <literal>address</literal>
+ </para>
+
+ <programlisting><![CDATA[store.owner.address.city // okay
+store.owner.address // error!]]></programlisting>
+
+ <para>
+ Un tipo "any" tiene las propiedades especiales <literal>id</literal> y <literal>class</literal>,
+ permiténdonos expresar un join en la siguiente forma (donde <literal>AuditLog.item</literal> es una
+ propiedad mapeada con <literal><any></literal>).
+ </para>
+
+ <programlisting><![CDATA[from AuditLog log, Payment payment
+where log.item.class = 'Payment' and log.item.id = payment.id]]></programlisting>
+
+ <para>
+ Nota que <literal>log.item.class</literal> y <literal>payment.class</literal> harían referencia a
+ los valores de columnas de base de datos completamente diferentes en la consulta anterior.
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-expressions">
+ <title>Expresiones</title>
+
+ <para>
+ Las expresiones permitidas en la cláusula <literal>where</literal> incluyen la mayoría del tipo de cosas
+ que podrías escribir en SQL:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ operadores matemáticos <literal>+, -, *, /</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ operadores de comparación binarios <literal>=, >=, <=, <>, !=, like</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ operadores lógicos <literal>and, or, not</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Paréntesis <literal>( )</literal>, indicando agrupación
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>in</literal>,
+ <literal>not in</literal>,
+ <literal>between</literal>,
+ <literal>is null</literal>,
+ <literal>is not null</literal>,
+ <literal>is empty</literal>,
+ <literal>is not empty</literal>,
+ <literal>member of</literal> y
+ <literal>not member of</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Caso "simple", <literal>case ... when ... then ... else ... end</literal>,
+ y caso "buscado", <literal>case when ... then ... else ... end</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ concatenación de cadenas <literal>...||...</literal> o <literal>concat(...,...)</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>current_date()</literal>, <literal>current_time()</literal>,
+ <literal>current_timestamp()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>second(...)</literal>, <literal>minute(...)</literal>,
+ <literal>hour(...)</literal>, <literal>day(...)</literal>,
+ <literal>month(...)</literal>, <literal>year(...)</literal>,
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Cualquier función u operador definido por EJB-QL 3.0: <literal>substring(), trim(),
+ lower(), upper(), length(), locate(), abs(), sqrt(), bit_length(), mod()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>coalesce()</literal> y <literal>nullif()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>str()</literal> para convertir valores numéricos o temporales a una cadena legible.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>cast(... as ...)</literal>, donde el segundo argumento es el nombre de un tipo Hibernate
+ , y <literal>extract(... from ...)</literal> si <literal>cast()</literal> y
+ <literal>extract()</literal> fuesen soportados por la base de datos subyacente.
+ </para>
+ </listitem>
+
+
+ <listitem>
+ <para>
+ la función <literal>index()</literal> de HQL, que se aplica a alias de una colección
+ indexada unida.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ funciones de HQL que tomen expresiones de ruta valuadas en colecciones: <literal>size(),
+ minelement(), maxelement(), minindex(), maxindex()</literal>, junto a las funciones especiales
+ <literal>elements()</literal> and <literal>indices</literal> que pueden ser cuantificadas usando
+ <literal>some, all, exists, any, in</literal>.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Cualquier función escalar SQL soportada por la base de datos como <literal>sign()</literal>,
+ <literal>trunc()</literal>, <literal>rtrim()</literal>, <literal>sin()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ parámetros posicionales JDBC <literal>?</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ parámetros con nombre <literal>:name</literal>, <literal>:start_date</literal>, <literal>:x1</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ literales SQL <literal>'foo'</literal>, <literal>69</literal>, <literal>6.66E+2</literal>,
+ <literal>'1970-01-01 10:00:01.0'</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ constantes Java <literal>public static final</literal> <literal>eg.Color.TABBY</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ <literal>in</literal> y <literal>between</literal> pueden usarse como sigue:
+ </para>
+
+ <programlisting><![CDATA[from DomesticCat cat where cat.name between 'A' and 'B']]></programlisting>
+
+ <programlisting><![CDATA[from DomesticCat cat where cat.name in ( 'Foo', 'Bar', 'Baz' )]]></programlisting>
+
+ <para>
+ y pueden escribirse las formas negadas
+ </para>
+
+ <programlisting><![CDATA[from DomesticCat cat where cat.name not between 'A' and 'B']]></programlisting>
+
+ <programlisting><![CDATA[from DomesticCat cat where cat.name not in ( 'Foo', 'Bar', 'Baz' )]]></programlisting>
+
+ <para>
+ Asimismo, <literal>is null</literal> y <literal>is not null</literal> pueden ser usadas para comprobar
+ valores nulos.
+ </para>
+
+ <para>
+ Los booleanos pueden ser fácilmente usados en expresiones declarando substituciones de consulta HQL
+ en la configuración de Hibernate:
+ </para>
+
+ <programlisting><![CDATA[<property name="hibernate.query.substitutions">true 1, false 0</property>]]></programlisting>
+
+ <para>
+ Esto remplazará las palabras clave <literal>true</literal> y <literal>false</literal> con los literales
+ <literal>1</literal> y <literal>0</literal> en el SQL traducido de este HQL:
+ </para>
+
+ <programlisting><![CDATA[from Cat cat where cat.alive = true]]></programlisting>
+
+ <para>
+ Puedes comprobar el tamaño de una colección con la propiedad especial <literal>size</literal>, o la función
+ especial <literal>size()</literal>.
+ </para>
+
+ <programlisting><![CDATA[from Cat cat where cat.kittens.size > 0]]></programlisting>
+
+ <programlisting><![CDATA[from Cat cat where size(cat.kittens) > 0]]></programlisting>
+
+ <para>
+ Para colecciones indexadas, puedes referirte a los índices máximo y mínimo usando las funciones
+ <literal>minindex</literal> y <literal>maxindex</literal>. Similarmente, puedes referirte a los elementos
+ máximo y mínimo de una colección de tipo básico usando las funciones
+ <literal>minelement</literal> y <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>
+ Las funciones SQL <literal>any, some, all, exists, in</literal> están soportadas cuando se les pasa
+ el conjunto de elementos o índices de una colección (funciones <literal>elements</literal> y
+ <literal>indices</literal>) o el resultado de una subconsulta (ver debajo).
+ </para>
+
+ <programlisting><![CDATA[select mother from Cat as mother, Cat as kit
+where kit in elements(foo.kittens)]]></programlisting>
+
+ <programlisting><![CDATA[select p from NameList list, Person p
+where p.name = some elements(list.names)]]></programlisting>
+
+ <programlisting><![CDATA[from Cat cat where exists elements(cat.kittens)]]></programlisting>
+
+ <programlisting><![CDATA[from Player p where 3 > all elements(p.scores)]]></programlisting>
+
+ <programlisting><![CDATA[from Show show where 'fizard' in indices(show.acts)]]></programlisting>
+
+ <para>
+ Nota que estas construcciones - <literal>size</literal>, <literal>elements</literal>,
+ <literal>indices</literal>, <literal>minindex</literal>, <literal>maxindex</literal>,
+ <literal>minelement</literal>, <literal>maxelement</literal> - pueden ser usadas solamente
+ en la cláusula where en Hibernate3.
+ </para>
+
+ <para>
+ Los elementos de colecciones indexadas (arrays, listas, mapas) pueden ser referidos por índice
+ (en una cláusula where solamente):
+ </para>
+
+ <programlisting><![CDATA[from Order order where order.items[0].id = 1234]]></programlisting>
+
+ <programlisting><![CDATA[select person from Person person, Calendar calendar
+where calendar.holidays['national day'] = person.birthDay
+ and person.nationality.calendar = calendar]]></programlisting>
+
+ <programlisting><![CDATA[select item from Item item, Order order
+where order.items[ order.deliveredItemIndices[0] ] = item and order.id = 11]]></programlisting>
+
+ <programlisting><![CDATA[select item from Item item, Order order
+where order.items[ maxindex(order.items) ] = item and order.id = 11]]></programlisting>
+
+ <para>
+ La expresión dentro de <literal>[]</literal> puede incluso ser una expresión aritmética.
+ </para>
+
+ <programlisting><![CDATA[select item from Item item, Order order
+where order.items[ size(order.items) - 1 ] = item]]></programlisting>
+
+ <para>
+ HQL provee además el función prefabricada <literal>index()</literal>, para elementos de una
+ asociación uno-a-muchos o colección de valores.
+ </para>
+
+ <programlisting><![CDATA[select item, index(item) from Order order
+ join order.items item
+where index(item) < 5]]></programlisting>
+
+ <para>
+ Pueden usarse las funciones SQL escalares soportadas por la base de datos subyacente
+ </para>
+
+ <programlisting><![CDATA[from DomesticCat cat where upper(cat.name) like 'FRI%']]></programlisting>
+
+ <para>
+ Si aún no estás convencido de todo esto, piensa cuánto más largo y menos leíble sería la siguiente
+ consulta en SQL:
+ </para>
+
+ <programlisting><![CDATA[select cust
+from Product prod,
+ Store store
+ inner join store.customers cust
+where prod.name = 'widget'
+ and store.location.name in ( 'Melbourne', 'Sydney' )
+ and prod = all elements(cust.currentOrder.lineItems)]]></programlisting>
+
+ <para>
+ <emphasis>Ayuda:</emphasis> algo como
+ </para>
+
+ <programlisting><![CDATA[SELECT cust.name, cust.address, cust.phone, cust.id, cust.current_order
+FROM customers cust,
+ stores store,
+ locations loc,
+ store_customers sc,
+ product prod
+WHERE prod.name = 'widget'
+ AND store.loc_id = loc.id
+ AND loc.name IN ( 'Melbourne', 'Sydney' )
+ AND sc.store_id = store.id
+ AND sc.cust_id = cust.id
+ AND prod.id = ALL(
+ SELECT item.prod_id
+ FROM line_items item, orders o
+ WHERE item.order_id = o.id
+ AND cust.current_order = o.id
+ )]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="queryhql-ordering">
+ <title>La cláusula order by</title>
+
+ <para>
+ La lista devuelta por una consulta puede ser ordenada por cualquier propiedad de una clase devuelta
+ o componentes:
+ </para>
+
+ <programlisting><![CDATA[from DomesticCat cat
+order by cat.name asc, cat.weight desc, cat.birthdate]]></programlisting>
+
+ <para>
+ Los <literal>asc</literal> o <literal>desc</literal> opcionales indican ordenamiento ascendente o
+ descendente respectivamente.
+ </para>
+ </sect1>
+
+ <sect1 id="queryhql-grouping">
+ <title>La cláusula group by</title>
+
+ <para>
+ Una consulta que devuelve valores agregados puede ser agrupada por cualquier propiedad de una clase
+ devuelta o componentes:
+ </para>
+
+ <programlisting><![CDATA[select cat.color, sum(cat.weight), count(cat)
+from Cat cat
+group by cat.color]]></programlisting>
+
+ <programlisting><![CDATA[select foo.id, avg(name), max(name)
+from Foo foo join foo.names name
+group by foo.id]]></programlisting>
+
+ <para>
+ Se permite también una cláusula <literal>having</literal>.
+ </para>
+
+ <programlisting><![CDATA[select cat.color, sum(cat.weight), count(cat)
+from Cat cat
+group by cat.color
+having cat.color in (eg.Color.TABBY, eg.Color.BLACK)]]></programlisting>
+
+ <para>
+ Las funciones y funciones de agregación SQL están permitidas en las cláusulas
+ <literal>having</literal> y <literal>order by</literal>, si están soportadas por la base de datos
+ subyacente (por ejemplo, no en MySQL).
+ </para>
+
+ <programlisting><![CDATA[select cat
+from Cat cat
+ join cat.kittens kitten
+group by cat
+having avg(kitten.weight) > 100
+order by count(kitten) asc, sum(kitten.weight) desc]]></programlisting>
+
+ <para>
+ Nota que ni la cláusula <literal>group by</literal> ni la cláusula <literal>order by</literal> pueden
+ contener expresiones aritméticas.
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-subqueries">
+ <title>Subconsultas</title>
+
+ <para>
+ Para bases de datos que soportan subconsultas, Hibernate soporta subconsultas dentro de consultas. Una
+ subconsulta debe ser encerrada entre paréntesis (frecuentemente por una llamada a una función de agregación
+ SQL). Incluso se permiten subconsultas correlacionadas (subconsultas que hacen referencia a un alias en la
+ consulta exterior).
+ </para>
+
+ <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>
+
+ <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>
+
+ <para>
+ Para las subconsultas con más de una expresión en la lista de selección, puedes usar un constructor
+ de tuplas:
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+where not ( cat.name, cat.color ) in (
+ select cat.name, cat.color from DomesticCat cat
+)]]></programlisting>
+
+ <para>
+ Nota que en algunas bases de datos (pero no en Oracle o HSQL), puedes usar constructores de tuplar en
+ otros contextos, por ejemplo al consultar componentes o tipos de usuario compuestos:
+ </para>
+
+ <programlisting><![CDATA[from Person where name = ('Gavin', 'A', 'King')]]></programlisting>
+
+ <para>
+ Que es equivalente a la más verborrágica:
+ </para>
+
+ <programlisting><![CDATA[from Person where name.first = 'Gavin' and name.initial = 'A' and name.last = 'King')]]></programlisting>
+
+ <para>
+ Existen dos buenas razones por las cuales podrías no querer hacer este tipo de cosa: primero, no es
+ completamente portable entre plataformas de base de datos; segundo, la consulta ahora es dependiente
+ del orden de propiedades en el documento de mapeo.
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-examples">
+ <title>Ejemplos de HQL</title>
+
+ <para>
+ Las consultas de Hibernate pueden ser abolutamente potentes y complejas, De hecho, el poder del lenguaje
+ de consulta es uno de los puntos principales de venta de Hibernate. He aquí algunos consultas de ejemplo
+ muy similares a consultas que he usado en un proyecto reciente. ¡Nota que la mayoría de las consultas
+ que escribirás som mucho más simples que estas!
+ </para>
+
+ <para>
+ La siguiente consulta devuelve el order id, número de items y valor total de la orden para todas
+ las ordenes inpagas de un cliente en particular y valor total mínimo dados, ordenando los resultados
+ por valor total. Al determinar los precios, usa el catálogo actual. La consulta SQL resultante,
+ contra las tablas <literal>ORDER</literal>, <literal>ORDER_LINE</literal>, <literal>PRODUCT</literal>,
+ <literal>CATALOG</literal> and <literal>PRICE</literal> tiene cuatro joins interiores y una subselect
+ (no correlacionada).
+ </para>
+
+ <programlisting><![CDATA[select order.id, sum(price.amount), count(item)
+from Order as order
+ join order.lineItems as item
+ join item.product as product,
+ Catalog as catalog
+ join catalog.prices as price
+where order.paid = false
+ and order.customer = :customer
+ and price.product = product
+ and catalog.effectiveDate < sysdate
+ and catalog.effectiveDate >= all (
+ select cat.effectiveDate
+ from Catalog as cat
+ where cat.effectiveDate < sysdate
+ )
+group by order
+having sum(price.amount) > :minAmount
+order by sum(price.amount) desc]]></programlisting>
+
+ <para>
+ ¡Qué monstruo! Realmente, en la vida real, no estoy muy afilado en subconsultas, de modo que mi
+ consulta fue realmente algo como esto:
+ </para>
+
+ <programlisting><![CDATA[select order.id, sum(price.amount), count(item)
+from Order as order
+ join order.lineItems as item
+ join item.product as product,
+ Catalog as catalog
+ join catalog.prices as price
+where order.paid = false
+ and order.customer = :customer
+ and price.product = product
+ and catalog = :currentCatalog
+group by order
+having sum(price.amount) > :minAmount
+order by sum(price.amount) desc]]></programlisting>
+
+ <para>
+ La próxima consulta cuenta el número de pagos en cada estado, excluyendo todos los pagos
+ en el estado <literal>AWAITING_APPROVAL</literal> donde el estado más reciente fue hecho por el
+ usuario actual. Se traduce en una consulta SQL con dos joins interiores y una subselect
+ correlacionada contra las tablas <literal>PAYMENT</literal>, <literal>PAYMENT_STATUS</literal> y
+ <literal>PAYMENT_STATUS_CHANGE</literal>.
+ </para>
+
+ <programlisting><![CDATA[select count(payment), status.name
+from Payment as payment
+ join payment.currentStatus as status
+ join payment.statusChanges as statusChange
+where payment.status.name <> PaymentStatus.AWAITING_APPROVAL
+ or (
+ statusChange.timeStamp = (
+ select max(change.timeStamp)
+ from PaymentStatusChange change
+ where change.payment = payment
+ )
+ and statusChange.user <> :currentUser
+ )
+group by status.name, status.sortOrder
+order by status.sortOrder]]></programlisting>
+
+ <para>
+ Si hubiese mapeado la colección <literal>statusChanges</literal> como una lista, en vez de un conjunto,
+ la consulta habría sido mucho más simple de escribir.
+ </para>
+
+ <programlisting><![CDATA[select count(payment), status.name
+from Payment as payment
+ join payment.currentStatus as status
+where payment.status.name <> PaymentStatus.AWAITING_APPROVAL
+ or payment.statusChanges[ maxIndex(payment.statusChanges) ].user <> :currentUser
+group by status.name, status.sortOrder
+order by status.sortOrder]]></programlisting>
+
+ <para>
+ La próxima consulta usa la función <literal>isNull()</literal> de MS SQL Server para devolver
+ todas las cuentas y pagos inpagos de la organización a la que pertenece el usuario actual.
+ Se traduce en una consulta SQL con tres joins interiores, un join exterior y una subconsulta
+ contra las tablas <literal>ACCOUNT</literal>, <literal>PAYMENT</literal>, <literal>PAYMENT_STATUS</literal>,
+ <literal>ACCOUNT_TYPE</literal>, <literal>ORGANIZATION</literal> y <literal>ORG_USER</literal>.
+ </para>
+
+ <programlisting><![CDATA[select account, payment
+from Account as account
+ left outer join account.payments as payment
+where :currentUser in elements(account.holder.users)
+ and PaymentStatus.UNPAID = isNull(payment.currentStatus.name, PaymentStatus.UNPAID)
+order by account.type.sortOrder, account.accountNumber, payment.dueDate]]></programlisting>
+
+ <para>
+ Para algunas bases de datos, necesitaríamos eliminar la subselect (correlacionada).
+ </para>
+
+ <programlisting><![CDATA[select account, payment
+from Account as account
+ join account.holder.users as user
+ left outer join account.payments as payment
+where :currentUser = user
+ and PaymentStatus.UNPAID = isNull(payment.currentStatus.name, PaymentStatus.UNPAID)
+order by account.type.sortOrder, account.accountNumber, payment.dueDate]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="queryhql-bulk">
+ <title>Sentencias UPDATE y DELETE masivas</title>
+
+ <para>
+ HQL soporta ahora sentencias UPDATE y DELETE en HQL.
+ Ver <xref linkend="batch-direct"/> para detalles.
+ </para>
+ </sect1>
+
+ <sect1 id="queryhql-tipstricks">
+ <title>Consejos y Trucos</title>
+
+ <para>
+ Puedes contar el número de resultados de una consulta sin devolverlos realmente:
+ </para>
+
+ <programlisting><![CDATA[( (Integer) session.iterate("select count(*) from ....").next() ).intValue()]]></programlisting>
+
+ <para>
+ Para ordenar un resultado por el tamaño de una colección, usa la siguiente consulta:
+ </para>
+
+ <programlisting><![CDATA[select usr.id, usr.name
+from User as usr
+ left join usr.messages as msg
+group by usr.id, usr.name
+order by count(msg)]]></programlisting>
+
+ <para>
+ Si tu base de datos soporta subselects, puedes colocar una condición sobre el tamaño de selección
+ en la cláusula where de tu consulta:
+ </para>
+
+ <programlisting><![CDATA[from User usr where size(usr.messages) >= 1]]></programlisting>
+
+ <para>
+ Si tu base de datos no soporta subselects, usa la siguiente consulta:
+ </para>
+
+ <programlisting><![CDATA[select usr.id, usr.name
+from User usr.name
+ join usr.messages msg
+group by usr.id, usr.name
+having count(msg) >= 1]]></programlisting>
+
+ <para>
+ Como esta solución no puede devolver un <literal>User</literal> con cero mensajes debido a la unión interior,
+ la siguiente forma es también útil:
+ </para>
+
+ <programlisting><![CDATA[select usr.id, usr.name
+from User as usr
+ left join usr.messages as msg
+group by usr.id, usr.name
+having count(msg) = 0]]></programlisting>
+
+ <para>
+ Las propiedades de un JavaBean pueden ser ligadas al parámetros de consulta con nombre:
+ </para>
+
+ <programlisting><![CDATA[Query q = s.createQuery("from foo Foo as foo where foo.name=:name and foo.size=:size");
+q.setProperties(fooBean); // fooBean has getName() and getSize()
+List foos = q.list();]]></programlisting>
+
+ <para>
+ Las colecciones son paginables usando la interface <literal>Query</literal> con un filtro:
+ </para>
+
+ <programlisting><![CDATA[Query q = s.createFilter( collection, "" ); // the trivial filter
+q.setMaxResults(PAGE_SIZE);
+q.setFirstResult(PAGE_SIZE * pageNumber);
+List page = q.list();]]></programlisting>
+
+ <para>
+ Los elementos de colección pueden ser ordenados o agrupados usando un filtro de consulta:
+ </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>
+
+ <para>
+ Puedes hallar el tamaño de una colección sin inicializarla:
+ </para>
+
+ <programlisting><![CDATA[( (Integer) session.iterate("select count(*) from ....").next() ).intValue();]]></programlisting>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/query_sql.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/query_sql.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/query_sql.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,477 @@
+<chapter id="querysql" revision="2">
+ <title>SQL Nativo</title>
+
+ <para>
+ Puedes también expresar consultas en el dialecto SQL nativo de tu base de datos. Esto es útil si quieres
+ utilizar aspectos específicos de base de datos tal como consejos (hints) de consulta o la palabra clave
+ <literal>CONNECT</literal> en Oracle. Provee además una clara ruta de migración desde una aplicación
+ basada en SQL/JDBC directo a Hibernate.
+ </para>
+
+ <para>
+ Hibernate3 te permite especificar SQL escrito a mano (incluyendo procedimientos almacenados) para todas
+ las operaciones de creación, actualización, borrado y carga.
+ </para>
+
+ <sect1 id="querysql-creating">
+ <title>Creando una <literal>Query</literal> de SQL nativo</title>
+
+ <para>
+ Las consultas SQL se controlan por medio de la interface <literal>SQLQuery</literal>, que se obtiene
+ llamando a <literal>Session.createSQLQuery()</literal>.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createSQLQuery("select {cat.*} from cats cat")
+ .addEntity("cat", Cat.class)
+ .setMaxResults(50)
+ .list();]]></programlisting>
+
+ <para>
+ Esta consulta especificada:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ la cadena de consulta SQL, con un lugar para que Hibernate inyecte los alias de columnas
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ la entidad devuelta por la consulta, y sus alias de tablas SQL
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ El método <literal>addEntity()</literal> asocia alias de tablas SQL con clases de entidad,
+ y determina la forma del conjunto resultado de la consulta.
+ </para>
+
+ <para>
+ El método <literal>addJoin()</literal> puede ser usado para cargar asociaciones a otras entidades y
+ colecciones.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createSQLQuery(
+ "select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id"
+ )
+ .addEntity("cat", Cat.class)
+ .addJoin("kitten", "cat.kittens")
+ .list();]]></programlisting>
+
+ <para>
+ Una consulta SQL nativa podría devolver un valor escalar simple o una combinación de escalares y entidades.
+ </para>
+
+ <programlisting><![CDATA[Double max = (Double) sess.createSQLQuery("select max(cat.weight) as maxWeight from cats cat")
+ .addScalar("maxWeight", Hibernate.DOUBLE);
+ .uniqueResult();]]></programlisting>
+
+
+ </sect1>
+
+ <sect1 id="querysql-aliasreferences">
+ <title>Alias y referencias de propiedad</title>
+
+ <para>
+ La notación <literal>{cat.*}</literal> usada arriba es un atajo para "todas las propiedades".
+ Alternativamente, puedes listar las columnas explícitamente, pero incluso en este caso dejamos
+ que Hibernate inyecte los alias de columnas SQL para cada propiedad. El lugar para un alias de columna
+ es sólo el nombre de propiedad cualificado por el alias de la tabla. En el siguiente ejemplo,
+ recuperamos <literal>Cat</literal>s de una tabla diferente (<literal>cat_log</literal>) a una
+ declarada en los metadatos de mapeo. Nota que podríamos incluso usar los alias de propiedad en la
+ cláusula where si quisieramos.
+ </para>
+ <para>
+ La sintáxis <literal>{}</literal> <emphasis>no</emphasis> es requerida para consultas con nombre.
+ Ver <xref linkend="querysql-namedqueries"/>
+ </para>
+
+ <programlisting><![CDATA[String sql = "select cat.originalId as {cat.id}, " +
+ "cat.mateid as {cat.mate}, cat.sex as {cat.sex}, " +
+ "cat.weight*10 as {cat.weight}, cat.name as {cat.name} " +
+ "from cat_log cat where {cat.mate} = :catId"
+
+List loggedCats = sess.createSQLQuery(sql)
+ .addEntity("cat", Cat.class)
+ .setLong("catId", catId)
+ .list();]]></programlisting>
+
+ <para>
+ <emphasis>Nota:</emphasis> si listas cada propiedad explícitamente, ¡debes incluir todas las
+ propiedades de la clase <emphasis>y sus subclases</emphasis>!
+ </para>
+
+ </sect1>
+
+ <sect1 id="querysql-namedqueries" revision="2">
+ <title>Consultas SQL con nombre</title>
+
+ <para>
+ Las consultas SQL con nombre pueden definirse en el documento de mapeo y llamadas exactamente
+ en la misma forma en que a una consulta HQL con nombre. En este caso, <emphasis>no</emphasis>
+ necesitamos llamar a <literal>addEntity()</literal>.
+ </para>
+
+ <programlisting><![CDATA[<sql-query name="persons">
+ <return alias="person" class="eg.Person"/>
+ SELECT person.NAME AS {person.name},
+ person.AGE AS {person.age},
+ person.SEX AS {person.sex}
+ FROM PERSON person
+ WHERE person.NAME LIKE :namePattern
+</sql-query>]]></programlisting>
+
+ <programlisting><![CDATA[List people = sess.getNamedQuery("persons")
+ .setString("namePattern", namePattern)
+ .setMaxResults(50)
+ .list();]]></programlisting>
+
+ <para>
+ Los elementos <literal><return-join></literal> y <literal><load-collection></literal>
+ se usan para unir asociaciones y definir consultas que inicialicen colecciones, respectivamente.
+ </para>
+
+ <programlisting><![CDATA[<sql-query name="personsWith">
+ <return alias="person" class="eg.Person"/>
+ <return-join alias="address" property="person.mailingAddress"/>
+ SELECT person.NAME AS {person.name},
+ person.AGE AS {person.age},
+ person.SEX AS {person.sex},
+ adddress.STREET AS {address.street},
+ adddress.CITY AS {address.city},
+ adddress.STATE AS {address.state},
+ adddress.ZIP AS {address.zip}
+ FROM PERSON person
+ JOIN ADDRESS adddress
+ ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'
+ WHERE person.NAME LIKE :namePattern
+</sql-query>]]></programlisting>
+
+ <para>
+ Una consulta SQL con nombre puede devolver un valor escalar. Debes especificar el alias de columna y
+ tipo Hibernate usando el elementp <literal><return-scalar></literal>:
+ </para>
+
+ <programlisting><![CDATA[<sql-query name="mySqlQuery">
+ <return-scalar column="name" type="string"/>
+ <return-scalar column="age" type="long"/>
+ SELECT p.NAME AS name,
+ p.AGE AS age,
+ FROM PERSON p WHERE p.NAME LIKE 'Hiber%'
+</sql-query>]]></programlisting>
+
+ <sect2 id="propertyresults">
+ <title>Usando return-property para especificar explícitamente nombres de columna/alias</title>
+
+ <para>
+ Con <literal><return-property></literal> puedes decirle explícitamente a Hibernate qué
+ alias de columna usar, en vez de usar la sintáxis <literal>{}</literal> para dejar que Hibernate
+ inyecte sus propios alias.
+ </para>
+
+ <programlisting><![CDATA[<sql-query name="mySqlQuery">
+ <return alias="person" class="eg.Person">
+ <return-property name="name" column="myName"/>
+ <return-property name="age" column="myAge"/>
+ <return-property name="sex" column="mySex"/>
+ </return>
+ SELECT person.NAME AS myName,
+ person.AGE AS myAge,
+ person.SEX AS mySex,
+ FROM PERSON person WHERE person.NAME LIKE :name
+</sql-query>
+]]></programlisting>
+
+ <para>
+ <literal><return-property></literal> también trabaja con múltiples columnas. Esto resuelve una
+ limitación de la sintáxis <literal>{}</literal>, la cual no puede permitir un control fino de propiedades
+ multi-columna.
+ </para>
+
+ <programlisting><![CDATA[<sql-query name="organizationCurrentEmployments">
+ <return alias="emp" class="Employment">
+ <return-property name="salary">
+ <return-column name="VALUE"/>
+ <return-column name="CURRENCY"/>
+ </return-property>
+ <return-property name="endDate" column="myEndDate"/>
+ </return>
+ SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer},
+ STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},
+ REGIONCODE as {emp.regionCode}, EID AS {emp.id}, VALUE, CURRENCY
+ FROM EMPLOYMENT
+ WHERE EMPLOYER = :id AND ENDDATE IS NULL
+ ORDER BY STARTDATE ASC
+</sql-query>]]></programlisting>
+
+ <para>
+ Nota que en este ejemplo hemos usado <literal><return-property></literal> en combinación con
+ la sintáxis <literal>{}</literal> para inyección, permitiendo a los usuarios elejir cómo quieren
+ referirse a las columnas y propiedades.
+ </para>
+
+ <para>
+ Si tu mapeo tiene un discriminador debes usar <literal><return-discriminator></literal>
+ para especificar la columna discriminadora.
+ </para>
+ </sect2>
+
+ <sect2 id="sp_query">
+ <title>Usando procedimientos almacenados para consultar</title>
+
+ <para>
+ Hibernate3 introduce soporte para consultas vía procedimientos almacenados. Los procedimientos
+ almacenados deben devolver un conjunto resultado como el primer parámetro de salida para ser
+ capaces de funcionar con Hibernate. Un ejemplo de uno procedimiento almacenado en Oracle 9
+ o superior es así:
+ </para>
+
+ <programlisting><![CDATA[CREATE OR REPLACE FUNCTION selectAllEmployments
+ RETURN SYS_REFCURSOR
+AS
+ st_cursor SYS_REFCURSOR;
+BEGIN
+ OPEN st_cursor FOR
+ SELECT EMPLOYEE, EMPLOYER,
+ STARTDATE, ENDDATE,
+ REGIONCODE, EID, VALUE, CURRENCY
+ FROM EMPLOYMENT;
+ RETURN st_cursor;
+ END;]]></programlisting>
+
+ <para>
+ Para usar esta consulta en Hibernate necesitas mapearla por medio de una consulta con nombre.
+ </para>
+
+ <programlisting><![CDATA[<sql-query name="selectAllEmployees_SP" callable="true">
+ <return alias="emp" class="Employment">
+ <return-property name="employee" column="EMPLOYEE"/>
+ <return-property name="employer" column="EMPLOYER"/>
+ <return-property name="startDate" column="STARTDATE"/>
+ <return-property name="endDate" column="ENDDATE"/>
+ <return-property name="regionCode" column="REGIONCODE"/>
+ <return-property name="id" column="EID"/>
+ <return-property name="salary">
+ <return-column name="VALUE"/>
+ <return-column name="CURRENCY"/>
+ </return-property>
+ </return>
+ { ? = call selectAllEmployments() }
+</sql-query>]]></programlisting>
+
+ <para>
+ Nota que los procedimientos almacenados sólo devuelven escalares y entidades.
+ No están soportados <literal><return-join></literal> y <literal><load-collection></literal>.
+ </para>
+
+ <sect3 id="querysql-limits-storedprocedures">
+ <title>Reglas/limitaciones para usar procedimientos almacenados</title>
+
+ <para>
+ Para usar procedimientos almacenados con Hibernate los procedimientos tienen que seguir algunas reglas.
+ Si no siguen esas reglas no son usables por Hibernate. Si aún quisieras usar estos procedimientos
+ tendrías que ejecutarlos por medio de <literal>session.connection()</literal>. Las reglas son
+ diferentes para cada base de datos, ya que los vendedores de base de datos tienen diferentes
+ semánticas/sintáxis de procedimientos almacenados.
+ </para>
+
+ <para>
+ Las consultas de procedimientos almacenados no pueden ser paginadas con
+ <literal>setFirstResult()/setMaxResults()</literal>.
+ </para>
+
+ <para>
+ Para Oracle se aplican las siguientes reglas:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ El procedimiento debe devolver un conjunto resultado. Esto se hace devolviendo un
+ <literal>SYS_REFCURSOR</literal> en Oracle 9 o 10. En Oracle necesitas definir un
+ tipo <literal>REF CURSOR</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ La forma recomendada es <literal>{ ? = call procName(<parameters>) }</literal> o
+ <literal>{ ? = call procName }</literal> (esto es más una regla de Oracle que una regla de Hibernate).
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Para Sybase o MS SQL server se aplican las siguientes reglas:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ El procedimiento debe devolver un conjunto resultado. Nota que ya que estos servidores pueden
+ y devolverán múltiples conjuntos resultados y cuentas de actualización, Hibernate iterará
+ los resultados y tomará el primer resultado que sea un conjunto resultado como su valor
+ a devolver. Todo lo demás será descartado.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Si habilitas <literal>SET NOCOUNT ON</literal> en tu procedimiento será probablemente más
+ eficiente, pero esto no es un requerimiento.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </sect3>
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="querysql-cud">
+ <title>SQL personalizado para crear, actualizar y borrar</title>
+
+ <para>
+ Hibernate3 puede usar sentencias SQL personalizadas para las operaciones de
+ crear, actualizar y borrar. Los persistidores de clases y colecciones en Hibernate
+ ya contienen un conjunto de cadenas generadas en tiempo de configuración (insertsql,
+ deletesql, updatesql, etc.). Las etiquetas de mapeo <literal><sql-insert></literal>,
+ <literal><sql-delete></literal>, y <literal><sql-update></literal> sobrescriben
+ estas cadenas:
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true"/>
+ <sql-insert>INSERT INTO PERSON (NAME, ID) VALUES ( UPPER(?), ? )</sql-insert>
+ <sql-update>UPDATE PERSON SET NAME=UPPER(?) WHERE ID=?</sql-update>
+ <sql-delete>DELETE FROM PERSON WHERE ID=?</sql-delete>
+</class>]]></programlisting>
+
+ <para>
+ El SQL se ejecuta directamente en tu base de datos, de modo que eres libre de usar cualquier
+ dialecto que quieras. Esto reducirá, por supuesto, la portabilidad de tu mapeo si usas SQL
+ específico de la base de datos.
+ </para>
+
+ <para>
+ Los procedimientos almacenados son soportados si está establecido el atributo
+ <literal>callable</literal>:
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true"/>
+ <sql-insert callable="true">{call createPerson (?, ?)}</sql-insert>
+ <sql-delete callable="true">{? = call deletePerson (?)}</sql-delete>
+ <sql-update callable="true">{? = call updatePerson (?, ?)}</sql-update>
+</class>]]></programlisting>
+
+ <para>
+ El orden de los parámetros posicionales son actualmente vitales, ya que deben estar en la
+ misma secuencia en que las espera Hibernate.
+ </para>
+
+ <para>
+ Puedes ver el orden esperado habilitando el registro de depuración para el nivel
+ <literal>org.hibernate.persister.entity</literal>. Con este nivel habilitado, Hibernate
+ imprimirá el SQL estático que se usa para crear, actualizar, borrar, etc. las entidades.
+ (Para ver la secuencia esperada, recuerda no incluir tu SQL personalizado en los ficheros
+ de mapeo ya que sobrescribirán el sql estático generado por Hibernate.)
+ </para>
+
+ <para>
+ Los procedimientos almacenados son, en la mayoría de los casos (léase, mejor hacerlo que no hacerlo),
+ obligados a devolver el número de filas insertadas/actualizadas/borradas, ya que Hibernate tiene algunas
+ comprobaciones en tiempo de ejecución del éxito de la sentencia. Hibernate siempre registra el primer
+ parámetro de la sentencia como un parámetro de salida numérico para las operaciones CUD:
+ </para>
+
+ <programlisting><![CDATA[CREATE OR REPLACE FUNCTION updatePerson (uid IN NUMBER, uname IN VARCHAR2)
+ RETURN NUMBER IS
+BEGIN
+
+ update PERSON
+ set
+ NAME = uname,
+ where
+ ID = uid;
+
+ return SQL%ROWCOUNT;
+
+END updatePerson;]]></programlisting>
+
+
+ </sect1>
+
+ <sect1 id="querysql-load">
+ <title>SQL personalizado para carga</title>
+
+ <para>
+ Puedes también declarar tu propias consultas SQL (o HQL) para cargar entidades:
+ </para>
+
+ <programlisting><![CDATA[<sql-query name="person">
+ <return alias="pers" class="Person" lock-mode="upgrade"/>
+ SELECT NAME AS {pers.name}, ID AS {pers.id}
+ FROM PERSON
+ WHERE ID=?
+ FOR UPDATE
+</sql-query>]]></programlisting>
+
+ <para>
+ Esto es sólo una declaración de consulta con nombrem como se ha discutido anteriormente.
+ Puedes hacer referencia a esta consulta con nombre en un mapeo de clase:
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true"/>
+ <loader query-ref="person"/>
+</class>]]></programlisting>
+
+ <para>
+ Esto incluso funciona con procedimientos almacenados.
+ </para>
+
+ <para>
+ Puedes incluso definit una consulta para la carga de colecciones:
+ </para>
+
+<programlisting><![CDATA[<set name="employments" inverse="true">
+ <key/>
+ <one-to-many class="Employment"/>
+ <loader query-ref="employments"/>
+</set>]]></programlisting>
+
+ <programlisting><![CDATA[<sql-query name="employments">
+ <load-collection alias="emp" role="Person.employments"/>
+ SELECT {emp.*}
+ FROM EMPLOYMENT emp
+ WHERE EMPLOYER = :id
+ ORDER BY STARTDATE ASC, EMPLOYEE ASC
+</sql-query>]]></programlisting>
+
+ <para>
+ Podrías incluso definir un cargador de entidades que cargue una colección por
+ recuperación por unión (join fetching):
+ </para>
+
+ <programlisting><![CDATA[<sql-query name="person">
+ <return alias="pers" class="Person"/>
+ <return-join alias="emp" property="pers.employments"/>
+ SELECT NAME AS {pers.*}, {emp.*}
+ FROM PERSON pers
+ LEFT OUTER JOIN EMPLOYMENT emp
+ ON pers.ID = emp.PERSON_ID
+ WHERE ID=?
+</sql-query>]]></programlisting>
+
+ </sect1>
+
+</chapter>
\ No newline at end of file
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/quickstart.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/quickstart.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/quickstart.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,666 @@
+<chapter id="quickstart">
+ <title>Comienzo rápido con Tomcat</title>
+
+ <sect1 id="quickstart-intro" revision="2">
+ <title>Empezando con Hibernate</title>
+
+ <para>
+ Este tutorial explica una instalación de Hibernate con el
+ contenedor de servlets Apache Tomcat (hemos usado la versión 4.1,
+ las diferencias con la 5.0 deben ser mínimas) para una aplicación
+ basada en web. Hibernate trabaja bien en un entorno manejado con
+ todos los servidores de aplicaciones J2EE importantes, o incluso en aplicaciones
+ Java independientes. El sistema de base de datos es sólo una cuestión
+ de cambiar la configuración del dialecto SQL de Hibernate y las
+ propiedades de conexión.
+ </para>
+
+ <para>
+ Primero, tenemos que copiar todas las bibliotecas requeridas a la instalación
+ de Tomcat. Usamos un contexto web separado (<literal>webapps/quickstart</literal>)
+ para este tutorial, de modo que tenemos que considerar tanto la ruta de búsqueda
+ de bibliotecas global (<literal>TOMCAT/common/lib</literal>) como también
+ el cargador de clases a nivel de contexto en <literal>webapps/quickstart/WEB-INF/lib</literal>
+ (para ficheros JAR) y <literal>webapps/quickstart/WEB-INF/classes</literal>.
+ Nos referiremos a ambos niveles de cargador de clases como el classpath global y el classpath
+ de contexto, respectivamente.
+ </para>
+
+ <para>
+ Ahora, copia las bibliotecas a los dos classpaths:
+ </para>
+
+ <orderedlist>
+ <listitem>
+ <para>
+ Copia el driver JDBC para la base de datos al classpath global. Esto se
+ requiere para el software de pool de conexiones DBCP que se distribuye
+ con Tomcat. Hibernate usa conexiones JDBC para ejecutar SQL sobre la base de
+ datos, de modo que, o bien tienes que proveer conexiones JDBC en pool,
+ o bien configurar Hibernate para que use uno de los pools soportados
+ directamente (C3P0, Proxool). Para este tutorial, copia la biblioteca
+ <literal>pg74jdbc3.jar</literal> (para PostgreSQL 7.4 y JDK 1.4) al
+ classpath del cargador global. Si quisieras usar una base de datos diferente,
+ simplemente copia su apropiado driver JDBC.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Nunca copies nada más dentro de la ruta del cargador de clases global
+ en Tomcat, o tendrás problemas con varias herramientas, incluyendo
+ Log4J, commons-logging y otras. Siempre usa el classpath de contexto para
+ cada aplicación web, esto es, copia las bibliotecas a
+ <literal>WEB-INF/lib</literal> y tus propias clases y ficheros de
+ configuración/propiedades a <literal>WEB-INF/classes</literal>.
+ Ambos directorios están a nivel del classpath de contexto por defecto.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Hibernate está empaquetado como una biblioteca JAR. El fichero
+ <literal>hibernate3.jar</literal> debe ser copiado en el classpath de contexto
+ junto a las otras clases de la aplicación. Hibernate requiere algunas
+ bibliotecas de terceros en tiempo de ejecución; éstas vienen
+ incluídas con la distribución de Hibernate en el directorio
+ <literal>lib/</literal>. Ver <xref linkend="3rdpartylibs"/>. Copia las
+ bibliotecas de terceros requeridas al classpath de contexto.
+ </para>
+ </listitem>
+ </orderedlist>
+
+ <table frame="topbot" id="3rdpartylibs">
+ <title>
+ Bibliotecas de terceros de Hibernate
+ </title>
+ <tgroup cols="2" rowsep="1" colsep="1">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="2*"/>
+ <thead>
+ <row>
+ <entry align="center">
+ Biblioteca
+ </entry>
+ <entry align="center">
+ Descripción
+ </entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ antlr (requerida)
+ </entry>
+ <entry>
+ Hibernate usa ANTLR para producir analizadores de consultas,
+ esta biblioteca también se necesita en tiempo de ejecución.
+ </entry>
+ </row>
+ <row>
+ <entry>
+ dom4j (requerida)
+ </entry>
+ <entry>
+ Hibernate usa dom4j para analizar ficheros de configuración
+ XML y ficheros de metadatos de mapeo XML.
+ </entry>
+ </row>
+ <row>
+ <entry>
+ CGLIB, asm (requerida)
+ </entry>
+ <entry>
+ Hibernate usa la biblioteca de generación de código
+ para aumentar las clases en tiempo de ejecución
+ (en combinación con reflección Java).
+ </entry>
+ </row>
+ <row>
+ <entry>
+ Commons Collections, Commons Logging (requeridas)
+ </entry>
+ <entry>
+ Hibernate usa varias bibliotecas de utilidad del proyecto
+ Jakarta Commons de Apache.
+ </entry>
+ </row>
+ <row>
+ <entry>
+ EHCache (requerida)
+ </entry>
+ <entry>
+ Hibernate puede usar varios provedores de caché para
+ el caché de segundo nivel. EHCache es el provedor de
+ caché por defecto si no se cambia en la configuración.
+ </entry>
+ </row>
+ <row>
+ <entry>
+ Log4j (opcional)
+ </entry>
+ <entry>
+ Hibernate usa la API de Commons Logging, que a su vez puede
+ usar Log4J como el mecanismo de logging subyacente. Si la
+ biblioteca Log4J está disponible en el directorio de
+ bibliotecas del contexto, Commons Logging usará Log4J
+ y la configuración <literal>log4j.properties</literal>
+ en el classpath de contexto. Un fichero de propiedades de ejemplo
+ para Log4J se incluye con la distribución de Hibernate.
+ Así que copia log4j.jar y el fichero de configuración
+ (de <literal>src/</literal>) a tu classpath de contexto si quieres
+ ver que ocurre tras escénas.
+ </entry>
+ </row>
+ <row>
+ <entry>
+ ¿Requerida o no?
+ </entry>
+ <entry>
+ Echa una mirada al fichero <literal>lib/README.txt</literal> en la
+ distribución de Hibernate. Esta es una lista actualizada
+ de bibliotecas de terceros distribuídas con Hibernate.
+ Encontrarás listadas ahí todas las bibliotecas
+ requeridas y opcionales (Observa que "buildtame required" significa
+ aquí para la construcción de Hibernate, no de tu
+ aplicación).
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ Ahora instalamos el pooling y modo compartido de conexiones de base de datos
+ tanto en Tomcat como Hibernate. Esto significa que Tomcat proveerá
+ conexiones JDBC en pool (usando su funcionalidad prefabricada de pooling DBCP).
+ Hibernate pide esas conexiones a través de JNDI. Alternativamente,
+ puedes dejar que Hibernate maneje el pool de conexiones. Tomcat liga su pool
+ de conexiones a JNDI; agregamos una declaración de recurso al fichero
+ de configuración principal de Tomcat, <literal>TOMCAT/conf/server.xml</literal>:
+ </para>
+
+ <programlisting><![CDATA[<Context path="/quickstart" docBase="quickstart">
+ <Resource name="jdbc/quickstart" scope="Shareable" type="javax.sql.DataSource"/>
+ <ResourceParams name="jdbc/quickstart">
+ <parameter>
+ <name>factory</name>
+ <value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
+ </parameter>
+
+ <!-- DBCP database connection settings -->
+ <parameter>
+ <name>url</name>
+ <value>jdbc:postgresql://localhost/quickstart</value>
+ </parameter>
+ <parameter>
+ <name>driverClassName</name><value>org.postgresql.Driver</value>
+ </parameter>
+ <parameter>
+ <name>username</name>
+ <value>quickstart</value>
+ </parameter>
+ <parameter>
+ <name>password</name>
+ <value>secret</value>
+ </parameter>
+
+ <!-- DBCP connection pooling options -->
+ <parameter>
+ <name>maxWait</name>
+ <value>3000</value>
+ </parameter>
+ <parameter>
+ <name>maxIdle</name>
+ <value>100</value>
+ </parameter>
+ <parameter>
+ <name>maxActive</name>
+ <value>10</value>
+ </parameter>
+ </ResourceParams>
+</Context>]]></programlisting>
+
+ <para>
+ El contexto que configuramos en este ejemplo se llama <literal>quickstart</literal>,
+ su base es el directorio <literal>TOMCAT/webapp/quickstart</literal>. Para acceder
+ a cualquier servlet, llama a la ruta <literal>http://localhost:8080/quickstart</literal>
+ en tu navegador (por supuesto, agregando el nombre del servlet como se mapee en tu
+ <literal>web.xml</literal>). Puedes también ir más allá y crear
+ ahora un servlet simple que tenga un método <literal>process()</literal>
+ vacío.
+ </para>
+
+ <para>
+ Tomcat provee ahora conexiones a través de JNDI en
+ <literal>java:comp/env/jdbc/quickstart</literal>. Si tienes problemas obteniendo
+ el pool de conexiones en ejecución, refiérete a la documentación
+ de Tomcat. Si obtienes mensajes de excepción del driver JDBC, intenta instalar
+ primero el pool de conexiones JDBC sin Hibernate. Hay disponibles en la Web
+ tutoriales de Tomcat y JDBC.
+ </para>
+
+ <para>
+ Tu próximo paso es configurar Hibernate. Hibernate tiene que saber cómo
+ debe obtener conexiones JDBC. Usamos la configuración de Hibernate basada en XML.
+ El otro enfoque, usando un ficheros de propiedad, es casi equivalente pero pierde unas
+ pocas funcionalidades que sí permite la sintaxis XML. El fichero de configuración
+ XML se ubica en el classpath de contexto (<literal>WEB-INF/classes</literal>), como
+ <literal>hibernate.cfg.xml</literal>:
+ </para>
+
+ <programlisting><![CDATA[<?xml version='1.0' encoding='utf-8'?>
+<!DOCTYPE hibernate-configuration PUBLIC
+ "-//Hibernate/Hibernate Configuration DTD//EN"
+ "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
+
+<hibernate-configuration>
+
+ <session-factory>
+
+ <property name="connection.datasource">java:comp/env/jdbc/quickstart</property>
+ <property name="show_sql">false</property>
+ <property name="dialect">org.hibernate.dialect.PostgreSQLDialect</property>
+
+ <!-- Mapping files -->
+ <mapping resource="Cat.hbm.xml"/>
+
+ </session-factory>
+
+</hibernate-configuration>]]></programlisting>
+
+ <para>
+ Desactivamos el registro (logging) de comandos SQL y decimos a Hibernate
+ qué dialecto SQL de base de datos se usa y dónde obtener
+ conexiones JDBC (declarando la dirección JNDI del pool ligado a
+ Tomcat). El dialecto es una configuración requerida, las bases de
+ datos difieren en su interpretación del "estándar" de SQL.
+ Hibernate cuidará de las diferencias y viene con dialectos incluídos
+ para todas las principales bases de datos comerciales y de código
+ abierto.
+ </para>
+
+ <para>
+ Una <literal>SessionFactory</literal> es el concepto de Hibernate
+ de un almacén de datos solo. Pueden usarse múltiples
+ bases de datos creando múltiples ficheros de configuración
+ XML y creando múltiples objetos <literal>Configuration</literal>
+ y <literal>SessionFactory</literal> en tu aplicación.
+ </para>
+
+ <para>
+ El último elemento del <literal>hibernate.cfg.xml</literal>
+ declara <literal>Cat.hbm.xml</literal> como el nombre de un fichero
+ de mapeo XML para la clase persistente <literal>Cat</literal>. Este
+ fichero contiene los metadatos para el mapeo de la clase POJO
+ <literal>Cat</literal> a una tabla (o tablas) de base de datos.
+ Volveremos a este fichero pronto. Escribamos primero la clase POJO
+ y luego declaremos los metadatos de mapeo para ella.
+ </para>
+
+ </sect1>
+
+ <sect1 id="quickstart-persistentclass" revision="1">
+ <title>Primera clase persistente</title>
+
+ <para>
+ Hibernate trabaja mejor con el modelo de programación de los
+ Viejos Objetos Planos de Java (POJOs, a veces llamados Ordinarios Objetos Planos de Java)
+ para clases persistentes. Un POJO es como un JavaBean, con las propiedades
+ de la clase accesible vía métodos getter y setter,
+ encapsulando la representación interna de la interfaz publicamente
+ visible (Hibernate puede también acceder a los campos directamente, si se
+ necesita):
+ </para>
+
+ <programlisting><![CDATA[package org.hibernate.examples.quickstart;
+
+public class Cat {
+
+ private String id;
+ private String name;
+ private char sex;
+ private float weight;
+
+ public Cat() {
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ private void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public char getSex() {
+ return sex;
+ }
+
+ public void setSex(char sex) {
+ this.sex = sex;
+ }
+
+ public float getWeight() {
+ return weight;
+ }
+
+ public void setWeight(float weight) {
+ this.weight = weight;
+ }
+
+}]]></programlisting>
+
+ <para>
+ Hibernate no está restringido en su uso de tipos de propiedad, todos
+ los tipos y tipos primitivos del JDK de Java (como <literal>String</literal>,
+ <literal>char</literal> y <literal>Date</literal>) pueden ser mapeados, incluyendo
+ clases del framework de colecciones de Java. Puedes mapearlos como valores,
+ colecciones de valores, o asociaciones a otras entidades. El <literal>id</literal>
+ es una propiedad especial que representa el identificador de base de datos (clave
+ primaria) de la clase. Es altamente recomendado para entidades como un
+ <literal>Cat</literal>. Hibernate puede usar identificadores sólo
+ internamente, pero perderíamos algo de la flexibilidad en nuestra
+ arquitectura de aplicación.
+ </para>
+
+ <para>
+ No tiene que implementarse ninguna interface especial para las clases persistentes
+ ni tienes que subclasear de una clase persistente raíz en especial. Hibernate
+ tampoco requiere ningún procesamiento en tiempo de construcción,
+ como manipulación del byte-code. Se basa solamente en reflección de Java
+ y aumentación de clases en tiempo de ejecución (a través de CGLIB).
+ De modo que, sin ninguna dependencia de la clase POJO en Hibernate, podemos mapearla
+ a una tabla de base de datos.
+ </para>
+
+ </sect1>
+
+ <sect1 id="quickstart-mapping" revision="1">
+ <title>Mapeando el gato</title>
+
+ <para>
+ El fichero de mapeo <literal>Cat.hbm.xml</literal> contiene los
+ metadatos requeridos para el mapeo objeto/relacional. Los metadatos
+ incluyen la declaración de clases persistentes y el mapeo de
+ propiedades (a columnas y relaciones de claves foráneas a otras
+ entidades) a tablas de base de datos.
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping>
+
+ <class name="org.hibernate.examples.quickstart.Cat" table="CAT">
+
+ <!-- A 32 hex character is our surrogate key. It's automatically
+ generated by Hibernate with the UUID pattern. -->
+ <id name="id" type="string" unsaved-value="null" >
+ <column name="CAT_ID" sql-type="char(32)" not-null="true"/>
+ <generator class="uuid.hex"/>
+ </id>
+
+ <!-- A cat has to have a name, but it shouldn' be too long. -->
+ <property name="name">
+ <column name="NAME" length="16" not-null="true"/>
+ </property>
+
+ <property name="sex"/>
+
+ <property name="weight"/>
+
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Cada clase persistente debe tener un atributo identificador (realmente,
+ sólo las clases que representen entidades, no las clases dependientes
+ de tipo-valor, que son mapeadas como componentes de una entidad). Esta propiedad
+ es usada para distinguir los objetos persistentes: Dos gatos son iguales si
+ <literal>catA.getId().equals(catB.getId())</literal> es verdadero. Este concepto
+ se llama <emphasis>identidad de base de datos (database identity)</emphasis>.
+ Hibernate viene empaquetado con varios generadores de identificador para diferentes
+ escenarios (incluyendo generadores nativos para secuencias de base de datos, tablas
+ de identificadores alto/bajo, e identificadores asignados por aplicación).
+ Usamos el generador UUID (recomendado sólo para pruebas, pues deben
+ preferirse las claves enteras delegadas generadas por la base de datos) y
+ también especificamos la columna <literal>CAT_ID</literal> de la tabla
+ <literal>CAT</literal> para el valor identificador generado por Hibernate
+ (como una clave primaria de la tabla).
+ </para>
+
+ <para>
+ Todas las demás propiedades de <literal>Cat</literal> son mapeadas a la
+ misma tabla. En el caso de la propiedad <literal>name</literal>, la hemos mapeado
+ con una declaración explícita de columna de base de datos. Esto es
+ especialmente útil cuando el esquema de base de datos es generado
+ automáticamente (como sentencias DDL de SQL) desde la declaración
+ de mapeo con la herramienta <emphasis>SchemaExport</emphasis> de Hibernate.
+ Todas las demás propiedades son mapeadas usando la configuración
+ por defecto de Hibernate, que es lo que necesitas la mayoría del tiempo.
+ La tabla <literal>CAT</literal> en la base de datos se ve así como:
+ </para>
+
+ <programlisting><![CDATA[ Columna | Tipo | Modificadores
+--------+-----------------------+-----------
+ cat_id | character(32) | not null
+ name | character varying(16) | not null
+ sex | character(1) |
+ weight | real |
+Indexes: cat_pkey primary key btree (cat_id)]]></programlisting>
+
+ <para>
+ Ahora debes crear esta tabla manualmente en tu base de datos, y luego leer el
+ <xref linkend="toolsetguide"/> si quieres automatizar este paso con la
+ herramienta <literal>hbm2ddl</literal>. Esta herramienta puede crear un
+ DDL SQL completo, incluyendo definición de tablas, restricciones
+ personalizadas de tipo de columnas, restricciones de unicidad e índices.
+ </para>
+
+ </sect1>
+
+ <sect1 id="quickstart-playingwithcats" revision="2">
+ <title>Jugando con gatos</title>
+
+ <para>
+ Ahora estamos listos para comenzar la <literal>Session</literal> de Hibernate.
+ Es el <emphasis>manejador de persistencia</emphasis> que usamos para almacenar
+ y traer <literal>Cat</literal>s hacia y desde la base de datos. Pero primero,
+ tenemos que obtener una <literal>Session</literal> (unidad de trabajo de Hibernate)
+ de la <literal>SessionFactory</literal>:
+ </para>
+
+ <programlisting><![CDATA[SessionFactory sessionFactory =
+ new Configuration().configure().buildSessionFactory();]]></programlisting>
+
+ <para>
+ La llamada a <literal>configure()</literal> carga el fichero de
+ configuración <literal>hibernate.cfg.xml</literal> e
+ inicializa la instancia de <literal>Configuration</literal>.
+ Puedes establecer otras propiedades (e incluso cambiar los metadatos de mapeo)
+ accediendo a la <literal>Configuration</literal> <emphasis>antes</emphasis>
+ que construyas la <literal>SessionFactory</literal> (que es inmutable).
+ ¿Dónde creamos la <literal>SessionFactory</literal> y cómo
+ accedemos a ella en nuestra aplicación?
+ </para>
+
+ <para>
+ Una <literal>SessionFactory</literal> usualmente se construye una vez,
+ por ejemplo, al arrancar con un servlet <emphasis>load-on-startup</emphasis>.
+ Esto significa también que no debes mantenerla en una variable de instancia
+ en tus servlets, sino en alguna otro sitio. Además, necesitamos algún
+ tipo de <emphasis>Singleton</emphasis>, de modo que podamos acceder a la
+ <literal>SessionFactory</literal> fácilmente en el código de
+ aplicación. El siguiente enfoque mostrado resuelve ambos problemas:
+ configuración de arranque y fácil acceso a una
+ <literal>SessionFactory</literal>.
+ </para>
+
+ <para>
+ Implementamos una clase de ayuda <literal>HibernateUtil</literal>:
+ </para>
+
+ <programlisting><![CDATA[import org.hibernate.*;
+import org.hibernate.cfg.*;
+
+public class HibernateUtil {
+
+ private static Log log = LogFactory.getLog(HibernateUtil.class);
+
+ private static final SessionFactory sessionFactory;
+
+ static {
+ try {
+ // Create the SessionFactory
+ sessionFactory = new Configuration().configure().buildSessionFactory();
+ } catch (Throwable ex) {
+ // Make sure you log the exception, as it might be swallowed
+ log.error("Initial SessionFactory creation failed.", ex);
+ throw new ExceptionInInitializerError(ex);
+ }
+ }
+
+ public static final ThreadLocal session = new ThreadLocal();
+
+ public static Session currentSession() {
+ Session s = (Session) session.get();
+ // Open a new Session, if this Thread has none yet
+ if (s == null) {
+ s = sessionFactory.openSession();
+ session.set(s);
+ }
+ return s;
+ }
+
+ public static void closeSession() {
+ Session s = (Session) session.get();
+ if (s != null)
+ s.close();
+ session.set(null);
+ }
+}]]></programlisting>
+
+ <para>
+ Esta clase no sólo cuida de la <literal>SessionFactory</literal>
+ con su inicializador static, sino que además tiene una variable
+ <literal>ThreadLocal</literal> que tiene la <literal>Session</literal>
+ para la hebra actual. Asegúrate de entender el concepto Java de una
+ variable local a una hebra antes de intentar usar esta ayuda. Una clase
+ <literal>HibernateUtil</literal> más compleja y potente puede
+ encontrarse en <literal>CaveatEmptor</literal>, http://caveatemptor.hibernate.org/
+ </para>
+
+ <para>
+ Una <literal>SessionFactory</literal> es segura entre hebras, muchas hebras pueden
+ acceder a ella concurrentemente y pedirle <literal>Session</literal>s. Una
+ <literal>Session</literal> no es un objeto seguro entre hebras que representa
+ una sola unidad-de-trabajo con la base de datos. Las <literal>Session</literal>s
+ se abren desde una <literal>SessionFactory</literal> y son cerradas cuando
+ todo el trabajo está completo. Un ejemplo en el método
+ <literal>process()</literal> de tu servlet podría parecerse a esto
+ (sin manejo de excepciones):
+ </para>
+
+ <programlisting><![CDATA[Session session = HibernateUtil.currentSession();
+Transaction tx = session.beginTransaction();
+
+Cat princess = new Cat();
+princess.setName("Princess");
+princess.setSex('F');
+princess.setWeight(7.4f);
+
+session.save(princess);
+
+tx.commit();
+HibernateUtil.closeSession();]]></programlisting>
+
+ <para>
+ En una <literal>Session</literal>, cada operación de base de datos
+ ocurre dentro de una transacción que aísla las operaciones
+ de base de datos (incluso operaciones de sólo lectura).
+ Usamos la API de <literal>Transaction</literal> de Hibernate para
+ abstraer de la estrategia de transacciones subyacente (en nuestro caso,
+ transacciones JDBC). Esto permite que nuestro código sea desplegado
+ con transacciones manejadas por contenedor (usando JTA) sin cambio alguno.
+ </para>
+
+ <para>
+ Observa que puedes llamar <literal>HibernateUtil.currentSession();</literal>
+ tantas veces como quieras, siempre obtendrás la <literal>Session</literal>
+ actual de esta hebra. Tienes que asegurarte que la <literal>Session</literal>
+ sea cerrada después que se complete tu unidad-de-trabajo, ya sea en
+ código de tu servlet o en un filtro de servlet antes que la respuesta HTTP
+ sea enviada. El bonito efecto colateral de la segunda opción es la
+ fácil inicialización perezosa: la <literal>Session</literal> todavía
+ está abierta cuando se dibuja la vista, de modo que Hibernate puede cargar
+ objetos no inicializados mientras navegas tu actual grafo de objetos.
+ </para>
+
+ <para>
+ Hibernate tiene varios métodos que pueden ser usados para traer
+ objetos desde la base de datos. La forma más flexible es usando
+ el Lenguaje de Consulta de Hibernate (Hibernate Query Language o HQL),
+ que es una extensión orientada a objetos de SQL fácil de
+ aprender:
+ </para>
+
+ <programlisting><![CDATA[Transaction tx = session.beginTransaction();
+
+Query query = session.createQuery("select c from Cat as c where c.sex = :sex");
+query.setCharacter("sex", 'F');
+for (Iterator it = query.iterate(); it.hasNext();) {
+ Cat cat = (Cat) it.next();
+ out.println("Female Cat: " + cat.getName() );
+}
+
+tx.commit();]]></programlisting>
+
+ <para>
+ Hibernate también ofrece una API <emphasis>consulta por criterios</emphasis>
+ orientada a objetos que puede ser usada para formular consultas de tipo seguro.
+ Por supuesto, Hibernate usa <literal>PreparedStatement</literal>s y ligado de
+ parámetros para toda la comunicación SQL con la base de datos.
+ También puedes usar la funcionalidad de consulta SQL directa de Hibernate
+ u obtener una conexión plana de JDBC de una <literal>Session</literal>
+ en casos raros.
+ </para>
+
+ </sect1>
+
+ <sect1 id="quickstart-summary" revision="1">
+ <title>Finalmente</title>
+
+ <para>
+ Rasguñamos solamente la superficie de Hibernate en este pequeño
+ tutorial. Por favor, observa que no incluimos ningún código
+ específico de servlet en nuestros ejemplos. Tienes que crear un servlet
+ por tí mismo e insertar el código de Hibernate como lo veas
+ ubicado.
+ </para>
+
+ <para>
+ Ten en mente que Hibernate, como capa de acceso a datos, está firmemente
+ integrado dentro de tu aplicación. Usualmente, todas las otras capas dependen
+ del mecanismo de persistencia. Asegúrate de entender las implicaciones
+ de este diseño.
+ </para>
+
+ <para>
+ Para un ejemplo de aplicación más compleja, ver
+ http://caveatemptor.hibernate.org/ y echa una mirada a los
+ otros tutoriales con links en http://www.hibernate.org/Documentation
+ </para>
+
+ </sect1>
+
+</chapter>
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/session_api.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/session_api.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/session_api.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1227 @@
+<chapter id="objectstate">
+ <title>Trabajando con objetos</title>
+
+ <para>
+ Hibernate es una solución completa de mapeo objeto/relacional que no sólo
+ abstrae al desarrollador de los detalles del sistema de manejo de base datos
+ subyacente, sino que además ofrece <emphasis>manejo de estado</emphasis> de
+ objetos. Esto es, al contrario del manejo de <literal>sentencias</literal>
+ SQL en capas comunes de persistencia JDBC/SQL, una vista de la persistencia
+ en aplicaciones Java muy natural y orientada a objetos.
+ </para>
+
+ <para>
+ En otras palabras, los desarroladores de aplicaciones Hibernate deben siempre
+ pensar en el <emphasis>estado</emphasis> de sus objetos, y no necesariamente
+ en la ejecución de sentencias SQL. Esta parte es cuidada por Hibernate y es
+ sólo relevante para el desarrollador de la aplicación al afinar el rendimiento
+ del sistema.
+ </para>
+
+ <sect1 id="objectstate-overview">
+ <title>Estados de objeto de Hibernate</title>
+
+ <para>
+ Hibernate define y soporta los siguientes estados de objeto:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Transitorio</emphasis> - un objeto es transitorio si ha sido
+ recién instanciado usando el operador <literal>new</literal>, y no está
+ asociado a una <literal>Session</literal> de Hibernate. No tiene una
+ representación persistente en la base de datos y no se le ha asignado un
+ valor identificador. Las instancias transitorias serán destruídas por el
+ recolector de basura si la aplicación no mantiene más una referencia.
+ Usa la <literal>Session</literal> de Hibernate para hacer un objeto
+ persistente (y deja que Hibernate cuide de las sentencias SQL que necesitan
+ ejecutarse para esta transición).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Persistente</emphasis> - una instancia persistente tiene una
+ representación en la base de datos y un valor identificador. Puede haber
+ sido salvado o cargado, sin embargo, está por definición en el ámbito de
+ una <literal>Session</literal>. Hibernate detectará cualquier cambio hecho
+ a un objeto en estado persistentey sincronizará el estado con la base de
+ datos cuando se complete la unidad de trabajo. Los desarrolladores no ejecutan
+ sentencias <literal>UPDATE</literal> manuales, o sentencias <literal>DELETE</literal>
+ cuando un objeto debe ser hecho transitorio.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Separado (detached)</emphasis> - una instancia separada es un objeto
+ que ha sido hecho persistente, pero su <literal>Session</literal> ha sido cerrada.
+ La referencia al objeto todavía es válida, por supuesto, y la instancia separada
+ podría incluso ser modificada en este estado. Una instancia separada puede ser
+ re-unida a una nueva <literal>Session</literal> en un punto posterior en el tiempo,
+ haciéndola persistente de nuevo (con todas las modificaciones). Este aspecto
+ habilita un modelo de programación para unidades de trabajo de ejecución larga
+ que requieren tiempo-para-pensar del usuario. Las llamamos <emphasis>transaccciones
+ de aplicación</emphasis>, es decir, una unidad de trabajo desde el punto de vista
+ del usuario.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Discutiremos ahora los estados y transiciones de estados (y los métodos de Hibernate que
+ disparan una transición) en más detalle:
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-makingpersistent" revision="1">
+ <title>Haciendo los objetos persistentes</title>
+
+ <para>
+ Las instancias recién instanciadas de una clase persistente son consideradas
+ <emphasis>transitorias</emphasis> por Hibernate. Podemos hacer una instancia
+ transitoria <emphasis>persistente</emphasis> asociándola con una sesión:
+ </para>
+
+ <programlisting><![CDATA[DomesticCat fritz = new DomesticCat();
+fritz.setColor(Color.GINGER);
+fritz.setSex('M');
+fritz.setName("Fritz");
+Long generatedId = (Long) sess.save(fritz);]]></programlisting>
+
+ <para>
+ Si <literal>Cat</literal> tiene un identificador generado, el identificador es
+ generado y asignado al <literal>cat</literal> cuando se llama a <literal>save()</literal>.
+ Si <literal>Cat</literal> tiene un identificador <literal>assigned</literal>,
+ o una clave compuesta, el identificador debe ser asignado a la instancia de
+ <literal>cat</literal> antes de llamar a <literal>save()</literal>. Puedes también
+ usar <literal>persist()</literal> en vez de <literal>save()</literal>, con la semántica
+ definida en el temprano borrador de EJB3.
+ </para>
+
+ <para>
+ Alternativamente, puedes asignar el identificador usando una versión sobrecargada
+ de <literal>save()</literal>.
+ </para>
+
+<programlisting><![CDATA[DomesticCat pk = new DomesticCat();
+pk.setColor(Color.TABBY);
+pk.setSex('F');
+pk.setName("PK");
+pk.setKittens( new HashSet() );
+pk.addKitten(fritz);
+sess.save( pk, new Long(1234) );]]></programlisting>
+
+ <para>
+ Si el objeto que haces persistente tiene objetos asociados (por ejemplo,
+ la colección <literal>kittens</literal> en el ejemplo anterior), estos
+ objetos pueden ser hechos persistentes en cualquier orden que quieras
+ a menos que tengas una restricción <literal>NOT NULL</literal> sobre una
+ columna clave foránea. Nunca hay riesgo de violar restricciones de clave
+ foránea. Sin embargo, podrías violar una restricción <literal>NOT NULL</literal>
+ si llamas a <literal>save()</literal> sobre objetos en orden erróneo.
+ </para>
+
+ <para>
+ Usualmente no te preocupas con este detalle, pues muy probablemente usarás
+ la funcionalidad de <emphasis>persistencia transitiva</emphasis> de Hibernate
+ para salvar los objetos asociados automáticamente. Entonces, ni siquiera ocurren
+ violaciones de restricciones <literal>NOT NULL</literal> - Hibernate cuidará de todo.
+ La persistencia transitiva se discute más adelante en este capítulo.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-loading">
+ <title>Cargando un objeto</title>
+
+ <para>
+ Los métodos <literal>load()</literal> de <literal>Session</literal> te brindan
+ una forma de traer una instancia persistente si ya saves su identificador.
+ <literal>load()</literal> toma un objeto clase y cargará el estado dentro de
+ una instancia recién instanciada de esta clase, en estado persistente.
+ </para>
+
+ <programlisting><![CDATA[Cat fritz = (Cat) sess.load(Cat.class, generatedId);]]></programlisting>
+
+<programlisting><![CDATA[// you need to wrap primitive identifiers
+long id = 1234;
+DomesticCat pk = (DomesticCat) sess.load( DomesticCat.class, new Long(id) );]]></programlisting>
+
+ <para>
+ Alternativamente, puedes cargar estado dentro de una instancia dada:
+ </para>
+
+<programlisting><![CDATA[Cat cat = new DomesticCat();
+// load pk's state into cat
+sess.load( cat, new Long(pkId) );
+Set kittens = cat.getKittens();]]></programlisting>
+
+ <para>
+ Nota que <literal>load()</literal> lanzará una excepción irrecuperable si no
+ hay una fila correspondiente en base de datos. Si la clase es mapeada con un
+ proxy, <literal>load()</literal> sólo devuelve un proxy no inicializado y no
+ llamará realmente a la base de datos hasta que invoques un método del proxy.
+ Este comportamiento es muy útil si deseas crear una asociación a un objeto
+ sin cargarlo realmente de la base de datos. Permite además que múltiples
+ instancias sean cargadas como un lote si se define <literal>batch-size</literal>
+ para el mapeo de la clase.
+ </para>
+
+ <para>
+ Si no tienes certeza que exista una fila correspondiente, debes usar el
+ método <literal>get()</literal>, que llama a la base de datos inmediatamente
+ y devuelve nulo si no existe una fila correspondiente.
+ </para>
+
+ <programlisting><![CDATA[Cat cat = (Cat) sess.get(Cat.class, id);
+if (cat==null) {
+ cat = new Cat();
+ sess.save(cat, id);
+}
+return cat;]]></programlisting>
+
+ <para>
+ Puedes incluso cargar un objeto usando un <literal>SELECT ... FOR UPDATE</literal> de SQL,
+ usando un <literal>LockMode</literal>. Ver la documentación de la API para más
+ información.
+ </para>
+
+ <programlisting><![CDATA[Cat cat = (Cat) sess.get(Cat.class, id, LockMode.UPGRADE);]]></programlisting>
+
+ <para>
+ Ten en cuenta que <emphasis>ninguna</emphasis> instancia asociada o colección contenida es
+ selecciona <literal>FOR UPDATE</literal>, a menos que decidas especificar
+ <literal>lock</literal> o <literal>all</literal> como un estilo de cascada para la
+ asociación.
+ </para>
+
+ <para>
+ Es posible volver a cargar un objeto y todas sus colecciones en cualquier momento,
+ usando el método <literal>refresh()</literal>. Esto es útil cuando se usan disparadores de
+ base de datos para inicializar algunas de las propiedades del objeto.
+ </para>
+
+ <programlisting><![CDATA[sess.save(cat);
+sess.flush(); //force the SQL INSERT
+sess.refresh(cat); //re-read the state (after the trigger executes)]]></programlisting>
+
+ <para>
+ Una cuestión importante aparece usualmente en este punto: ¿Cuánto carga Hibernate de
+ la base de datos y cuántos <literal>SELECT</literal>s de SQL usará? Esto depende de la
+ <emphasis>estrategia de recuperación</emphasis> y se explica en <xref linkend="performance-fetching"/>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-querying" revision="1">
+ <title>Consultando</title>
+
+ <para>
+ Si no sabes los identificadores de los objetos que estás buscando,
+ necesitas una consulta. Hibernate soporta un lenguaje de consulta
+ orientado a objetos (HQL) fácil de usar pero potente. Para la creación
+ de consultas programáticas, Hibernate soporta una funcionalidad sofisticada
+ de consulta de Criteria y Example (QBC and QBE). También puedes expresar tu
+ consulta en el SQL nativo de tu base de datos, con soporte opcional de Hibernate
+ para la conversión del conjunto resultado en objetos.
+ </para>
+
+ <sect2 id="objectstate-querying-executing">
+ <title>Ejecutando consultas</title>
+
+ <para>
+ Las consultas HQL y SQL nativas son representadas con una instancia de
+ <literal>org.hibernate.Query</literal>. Esta interface ofrece métodos para
+ la ligación de parámetros, manejo del conjunto resultado, y para la
+ ejecución de la consulta real. Siempre obtienes una <literal>Query</literal>
+ usando la <literal>Session</literal> actual:
+ </para>
+
+ <programlisting><![CDATA[List cats = session.createQuery(
+ "from Cat as cat where cat.birthdate < ?")
+ .setDate(0, date)
+ .list();
+
+List mothers = session.createQuery(
+ "select mother from Cat as cat join cat.mother as mother where cat.name = ?")
+ .setString(0, name)
+ .list();
+
+List kittens = session.createQuery(
+ "from Cat as cat where cat.mother = ?")
+ .setEntity(0, pk)
+ .list();
+
+Cat mother = (Cat) session.createQuery(
+ "select cat.mother from Cat as cat where cat = ?")
+ .setEntity(0, izi)
+ .uniqueResult();]]></programlisting>
+
+ <para>
+ Una consulta se ejecuta usualmente invocando a <literal>list()</literal>,
+ el resultado de la consulta será cargado completamente dentro de una
+ colección en memoria. Las instancias de entidad traídas por una consulta
+ están en estado persistente. El método <literal>uniqueResult()</literal>
+ ofrece un atajo si sabes que tu consulta devolverá sólo un objeto.
+ </para>
+
+ <sect3 id="objectstate-querying-executing-iterate">
+ <title>Iterando los resultados</title>
+
+ <para>
+ Ocasionalmente, podrías ser capaz de lograr mejor rendimiento al ejecutar la consulta
+ usando el método <literal>iterate()</literal>. Esto sólo será en el caso que esperes
+ que las instancias reales de entidad devueltas por la consulta estén ya en la sesión
+ o caché de segundo nivel. Si todavía no están en caché, <literal>iterate()</literal>
+ será más lento que <literal>list()</literal> y podría requerir muchas llamadas a la
+ base de datos para una consulta simple, usualmente <emphasis>1</emphasis> para la
+ selección inicial que solamente devuelve identificadores, y <emphasis>n</emphasis>
+ selecciones adicionales para inicializar las instancias reales.
+ </para>
+
+ <programlisting><![CDATA[// fetch ids
+Iterator iter = sess.createQuery("from eg.Qux q order by q.likeliness").iterate();
+while ( iter.hasNext() ) {
+ Qux qux = (Qux) iter.next(); // fetch the object
+ // something we couldnt express in the query
+ if ( qux.calculateComplicatedAlgorithm() ) {
+ // delete the current instance
+ iter.remove();
+ // dont need to process the rest
+ break;
+ }
+}]]></programlisting>
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-tuples">
+ <title>Consultas que devuelven tuplas</title>
+
+ <para>
+ Las consultas de Hibernate a veces devuelven tuplas de objetos, en cuyo caso
+ cada tupla se devuelve como un array:
+ </para>
+
+ <programlisting><![CDATA[Iterator kittensAndMothers = sess.createQuery(
+ "select kitten, mother from Cat kitten join kitten.mother mother")
+ .list()
+ .iterator();
+
+while ( kittensAndMothers.hasNext() ) {
+ Object[] tuple = (Object[]) kittensAndMothers.next();
+ Cat kitten = tuple[0];
+ Cat mother = tuple[1];
+ ....
+}]]></programlisting>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-scalar">
+ <title>Resultados escalares</title>
+
+ <para>
+ Las consultas pueden especificar una propiedad de una clase en la cláusula
+ <literal>select</literal>. Pueden incluso llamar a funciones de agregación SQL.
+ Las propiedades o agregaciones son considerados resultados "escalares"
+ (y no entidades en estado persistente).
+ </para>
+
+ <programlisting><![CDATA[Iterator results = sess.createQuery(
+ "select cat.color, min(cat.birthdate), count(cat) from Cat cat " +
+ "group by cat.color")
+ .list()
+ .iterator();
+
+while ( results.hasNext() ) {
+ Object[] row = results.next();
+ Color type = (Color) row[0];
+ Date oldest = (Date) row[1];
+ Integer count = (Integer) row[2];
+ .....
+}]]></programlisting>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-parameters">
+ <title>Ligación de parámetros</title>
+
+ <para>
+ Se proveen métodos en <literal>Query</literal> para ligar valores a
+ parámetros con nombre o parámetros <literal>?</literal> de estilo JDBC.
+ <emphasis>Al contrario de JDBC, Hibernate numera los parámetros desde cero.</emphasis>
+ Los parámetros con nombre son identificadores de la forma <literal>:name</literal>
+ en la cadena de la consulta. Las ventajas de los parámetros con nombre son:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ los parámetros con nombre son insensibles al orden en que aparecen
+ en la cadena de consulta
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ pueden aparecer múltiples veces en la misma consulta
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ son auto-documentados
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <programlisting><![CDATA[//named parameter (preferred)
+Query q = sess.createQuery("from DomesticCat cat where cat.name = :name");
+q.setString("name", "Fritz");
+Iterator cats = q.iterate();]]></programlisting>
+
+ <programlisting><![CDATA[//positional parameter
+Query q = sess.createQuery("from DomesticCat cat where cat.name = ?");
+q.setString(0, "Izi");
+Iterator cats = q.iterate();]]></programlisting>
+
+ <programlisting><![CDATA[//named parameter list
+List names = new ArrayList();
+names.add("Izi");
+names.add("Fritz");
+Query q = sess.createQuery("from DomesticCat cat where cat.name in (:namesList)");
+q.setParameterList("namesList", names);
+List cats = q.list();]]></programlisting>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-pagination">
+ <title>Paginación</title>
+
+ <para>
+ Si necesitas especificar límites sobre tu conjunto resultado (el número máximo de filas
+ que quieras traer y/o la primera fila que quieras traer) debes usar los métodos de la
+ interface <literal>Query</literal>:
+ </para>
+
+ <programlisting><![CDATA[Query q = sess.createQuery("from DomesticCat cat");
+q.setFirstResult(20);
+q.setMaxResults(10);
+List cats = q.list();]]></programlisting>
+
+ <para>
+ Hibernate sabe cómo traducir este límite de consulta al SQL nativo de tu
+ DBMS.
+ </para>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-scrolling">
+ <title>Iteración scrollable</title>
+
+ <para>
+ Si tu driver JDBC soporta <literal>ResultSet</literal>s scrollables, la
+ interface <literal>Query</literal> puede ser usada para obtener un objeto
+ <literal>ScrollableResults</literal>, que permite una navegación flexible
+ de los resultados de consulta.
+ </para>
+
+ <programlisting><![CDATA[Query q = sess.createQuery("select cat.name, cat from DomesticCat cat " +
+ "order by cat.name");
+ScrollableResults cats = q.scroll();
+if ( cats.first() ) {
+
+ // find the first name on each page of an alphabetical list of cats by name
+ firstNamesOfPages = new ArrayList();
+ do {
+ String name = cats.getString(0);
+ firstNamesOfPages.add(name);
+ }
+ while ( cats.scroll(PAGE_SIZE) );
+
+ // Now get the first page of cats
+ pageOfCats = new ArrayList();
+ cats.beforeFirst();
+ int i=0;
+ while( ( PAGE_SIZE > i++ ) && cats.next() ) pageOfCats.add( cats.get(1) );
+
+}
+cats.close()]]></programlisting>
+
+ <para>
+ Nota que se requiere una conexión de base de datos abierta (y cursor) para esta
+ funcionalidad, usa <literal>setMaxResult()</literal>/<literal>setFirstResult()</literal>
+ si necesitas la funcionalidad de paginación fuera de línea.
+ </para>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-named">
+ <title>Externalizando consultas con nombre</title>
+
+ <para>
+ Puedes además definir consultas con nombre en el documento de mapeo.
+ (Recuerda usar una sección <literal>CDATA</literal> si tu consulta
+ contiene caracteres que puedan ser interpretados como etiquetado.)
+ </para>
+
+ <programlisting><![CDATA[<query name="eg.DomesticCat.by.name.and.minimum.weight"><![CDATA[
+ from eg.DomesticCat as cat
+ where cat.name = ?
+ and cat.weight > ?
+] ]></query>]]></programlisting>
+
+ <para>
+ La ligación de parámetros y ejecución se hace programáticamente:
+ </para>
+
+ <programlisting><![CDATA[Query q = sess.getNamedQuery("eg.DomesticCat.by.name.and.minimum.weight");
+q.setString(0, name);
+q.setInt(1, minWeight);
+List cats = q.list();]]></programlisting>
+
+ <para>
+ Nota que el código real del programa es independiente del lenguaje de consulta
+ usado; puedes además definir consultas SQL nativas en metadatos, o migrar
+ consultas existentes a Hibernate colocándolas en ficheros de mapeo.
+ </para>
+
+ </sect3>
+
+ </sect2>
+
+ <sect2 id="objectstate-filtering" revision="1">
+ <title>Filtrando colecciones</title>
+ <para>
+ Un <emphasis>filtro</emphasis> de colección es un tipo especial de consulta que puede ser
+ aplicado a una colección persistente o array. La cadena de consulta puede referirse a
+ <literal>this</literal>, significando el elemento de colección actual.
+ </para>
+
+ <programlisting><![CDATA[Collection blackKittens = session.createFilter(
+ pk.getKittens(),
+ "where this.color = ?")
+ .setParameter( Color.BLACK, Hibernate.custom(ColorUserType.class) )
+ .list()
+);]]></programlisting>
+
+ <para>
+ La colección devuelta es considerada un bag, y es una copia de la colección
+ dada. La colección original no es modificada (esto es contrario a la implicación
+ del nombre "filtro", pero consistente con el comportamiento esperado).
+ </para>
+
+ <para>
+ Observa que los filtros no requieren una cláusula <literal>from</literal> (aunque pueden
+ tener uno si se requiere). Los filtros no están limitados a devolver los elementos de
+ colección por sí mismos.
+ </para>
+
+ <programlisting><![CDATA[Collection blackKittenMates = session.createFilter(
+ pk.getKittens(),
+ "select this.mate where this.color = eg.Color.BLACK.intValue")
+ .list();]]></programlisting>
+
+ <para>
+ Incluso una consulta de filtro vacío es útil, por ejemplo, para cargar un
+ subconjunto de elementos en una colección enorme:
+ </para>
+
+ <programlisting><![CDATA[Collection tenKittens = session.createFilter(
+ mother.getKittens(), "")
+ .setFirstResult(0).setMaxResults(10)
+ .list();]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="objecstate-querying-criteria" revision="1">
+ <title>Consultas de criterios</title>
+
+ <para>
+ HQL es extremadamente potente pero algunos desarrolladores prefieren construir
+ consultas dinámicamente usando una API orientada a objetos, en vez construir
+ cadenas de consulta. Hibernate provee una API intuitiva de consulta <literal>Criteria</literal>
+ para estos casos:
+ </para>
+
+ <programlisting><![CDATA[Criteria crit = session.createCriteria(Cat.class);
+crit.add( Expression.eq( "color", eg.Color.BLACK ) );
+crit.setMaxResults(10);
+List cats = crit.list();]]></programlisting>
+
+ <para>
+ Las APIs de <literal>Criteria</literal> y la asociada <literal>Example</literal>
+ son discutidas en más detalle en <xref linkend="querycriteria"/>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="objectstate-querying-nativesql" revision="2">
+ <title>Consultas en SQL nativo</title>
+
+ <para>
+ Puedes expresar una consulta en SQL, usando <literal>createSQLQuery()</literal> y
+ dejando que Hibernate cuide del mapeo de los conjuntos resultado a objetos.
+ Nota que puedes llamar en cualquier momento a <literal>session.connection()</literal> y
+ usar la <literal>Connection</literal> JDBC directamente. Si eliges usar la API de
+ Hibernate, debes encerrar los alias de SQL entre llaves:
+ </para>
+
+ <programlisting><![CDATA[List cats = session.createSQLQuery(
+ "SELECT {cat.*} FROM CAT {cat} WHERE ROWNUM<10",
+ "cat",
+ Cat.class
+).list();]]></programlisting>
+
+ <programlisting><![CDATA[List cats = session.createSQLQuery(
+ "SELECT {cat}.ID AS {cat.id}, {cat}.SEX AS {cat.sex}, " +
+ "{cat}.MATE AS {cat.mate}, {cat}.SUBCLASS AS {cat.class}, ... " +
+ "FROM CAT {cat} WHERE ROWNUM<10",
+ "cat",
+ Cat.class
+).list()]]></programlisting>
+
+ <para>
+ Las consultas SQL pueden contener parámetros con nombre y posicionales, al igual que
+ las consultas de Hibernate. Puede encontrarse más información sobre consultas en SQL
+ nativo en <xref linkend="querysql"/>.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="objectstate-modifying" revision="1">
+ <title>Modificando objetos persistentes</title>
+
+ <para>
+ Las <emphasis>instancias persistentes transaccionales</emphasis> (es decir, objetos cargados,
+ creados o consultados por la <literal>Session</literal>) pueden ser manipulados por la
+ aplicación y cualquier cambio al estado persistente será persistido cuando la <literal>Session</literal>
+ sea <emphasis>limpiada (flushed)</emphasis> (discutido más adelante en este capítulo). No hay
+ necesidad de llamar un método en particular (como <literal>update()</literal>, que tiene un
+ propósito diferente) para hacer persistentes tus modificaciones. De modo que la forma más
+ directa de actualizar el estado de un objeto es cargarlo con <literal>load()</literal>,
+ y entonces manipularlo directamente, mientras la <literal>Session</literal> está abierta:
+ </para>
+
+ <programlisting><![CDATA[DomesticCat cat = (DomesticCat) sess.load( Cat.class, new Long(69) );
+cat.setName("PK");
+sess.flush(); // changes to cat are automatically detected and persisted]]></programlisting>
+
+ <para>
+ A veces este modelo de programación es ineficiente pues podría requerir una
+ <literal>SELECT</literal> de SQL (para cargar un objeto) y un <literal>UPDATE</literal>
+ de SQL (para hacer persistentes sus datos actualizados) en la misma sesión. Por lo tanto,
+ Hibernate ofrece un enfoque alternativo, usando instancias separadas (detached).
+ </para>
+
+ <para>
+ <emphasis>Nota que Hibernate no ofreve su propia API para ejecución directa de
+ sentencias <literal>UPDATE</literal> o <literal>DELETE</literal>. Hibernate es un
+ servicio de <emphasis>gestión de estado</emphasis>, no tienes que pensar en
+ <literal>sentencias</literal> para usarlo. JDBC es una API perfecta para ejecutar
+ sentencias SQL; puedes obtener una <literal>Connection</literal> JDBC en cualquier
+ momento llamando a <literal>session.connection()</literal>. Además, la noción de
+ operaciones masivas entra en conflicto con el mapeo objeto/relacional en aplicaciones
+ en línea orientadas al procesamiento de transacciones. Versiones futuras de Hibernate
+ pueden, sin embargo, proveer funciones de operación masiva especiales. Ver
+ <xref linkend="batch"/> por algunos trucos de operación en lote (batch) posibles.
+ </emphasis>
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-detached" revision="2">
+ <title>Modificando objetos separados</title>
+
+ <para>
+ Muchas aplicaciones necesitan recuperar un objeto en una transacción, enviarla
+ a la capa de UI para su manipulación, y entonces salvar los cambios en una nueva
+ transacción. Las aplicaciones que usan este tipo de enfoque en un entorno de
+ alta concurrencia usualmente usan datos versionados para asegurar el aislamiento
+ de la unidad de trabajo "larga".
+ </para>
+
+ <para>
+ Hibernate soporta este modelo al proveer re-unión de instancias separadas usando
+ los métodos <literal>Session.update()</literal> o <literal>Session.merge()</literal>:
+ </para>
+
+ <programlisting><![CDATA[// in the first session
+Cat cat = (Cat) firstSession.load(Cat.class, catId);
+Cat potentialMate = new Cat();
+firstSession.save(potentialMate);
+
+// in a higher layer of the application
+cat.setMate(potentialMate);
+
+// later, in a new session
+secondSession.update(cat); // update cat
+secondSession.update(mate); // update mate]]></programlisting>
+
+ <para>
+ Si el <literal>Cat</literal> con identificador <literal>catId</literal> ya hubiera
+ sido cargado por <literal>secondSession</literal> cuando la aplicación intentó
+ volver a unirlo, se habría lanzado una excepción.
+ </para>
+
+ <para>
+ Usa <literal>update()</literal> si no estás seguro que la sesión tenga
+ una instancia ya persistente con el mismo identificador, y <literal>merge()</literal>
+ si quieres fusionar tus modificaciones en cualquier momento sin consideración del
+ estado de la sesión. En otras palabras, <literal>update()</literal> es usualmente
+ el primer método que llamarías en una sesión fresca, asegurando que la re-unión de
+ tus instancias separadas es la primera operación que se ejecuta.
+ </para>
+
+ <para>
+ La aplicación debe actualizar individualmente las instancias separadas alcanzables
+ por la instancia separada dada llamando a <literal>update()</literal>, si y
+ <emphasis>sólo</emphasis> si quiere que sus estados sean también actualizados.
+ Esto puede, por supuesto, ser automatizado usando <emphasis>persistencia transitiva</emphasis>,
+ ver <xref linkend="objectstate-transitive"/>.
+ </para>
+
+ <para>
+ El método <literal>lock()</literal> también permite a una aplicación reasociar
+ un objeto con una sesión nueva. Sin embargo, la instancia separada no puede
+ haber sido modificada!
+ </para>
+
+ <programlisting><![CDATA[//just reassociate:
+sess.lock(fritz, LockMode.NONE);
+//do a version check, then reassociate:
+sess.lock(izi, LockMode.READ);
+//do a version check, using SELECT ... FOR UPDATE, then reassociate:
+sess.lock(pk, LockMode.UPGRADE);]]></programlisting>
+
+ <para>
+ Nota que <literal>lock()</literal> puede ser usado con varios <literal>LockMode</literal>s,
+ ver la documentación de la API y el capítulo sobre manejo de transacciones para más
+ información. La re-unión no es el único caso de uso para <literal>lock()</literal>.
+ </para>
+
+ <para>
+ Se discuten otros modelos para unidades de trabajo largas en <xref linkend="transactions-optimistic"/>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-saveorupdate">
+ <title>Detección automática de estado</title>
+
+ <para>
+ Los usuarios de Hibernate han pedido un método de propósito general que bien
+ salve una instancia transitoria generando un identificador nuevo, o bien
+ actualice/reúna las instancias separadas asociadas con su identificador actual.
+ El método <literal>saveOrUpdate()</literal> implementa esta funcionalidad.
+ </para>
+
+ <programlisting><![CDATA[// in the first session
+Cat cat = (Cat) firstSession.load(Cat.class, catID);
+
+// in a higher tier of the application
+Cat mate = new Cat();
+cat.setMate(mate);
+
+// later, in a new session
+secondSession.saveOrUpdate(cat); // update existing state (cat has a non-null id)
+secondSession.saveOrUpdate(mate); // save the new instance (mate has a null id)]]></programlisting>
+
+ <para>
+ El uso y semántica de <literal>saveOrUpdate()</literal> parece ser confuso para
+ usuarios nuevos. Primeramente, en tanto no estés intentando usar instancias de una
+ sesión en otra sesión nueva, no debes necesitar usar <literal>update()</literal>,
+ <literal>saveOrUpdate()</literal>, o <literal>merge()</literal>. Algunas aplicaciones
+ enteras nunca usarán ninguno de estos métodos.
+ </para>
+
+ <para>
+ Usualmente <literal>update()</literal> o <literal>saveOrUpdate()</literal> se usan en
+ el siguiente escenario:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ la aplicación carga un objeto en la primera sesión
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ el objeto es pasado a la capa de UI
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ se hacen algunas modificaciones al objeto
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ el objeto se pasa abajo de regreso a la capa de negocio
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ la aplicación hace estas modificaciones persistentes llamando
+ a <literal>update()</literal> en una segunda sesión
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ <literal>saveOrUpdate()</literal> hace lo siguiente:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ si el objeto ya es persistente en esta sesión, no hace nada
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ si otro objeto asociado con la sesión tiene el mismo identificador,
+ lanza una excepción
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ si el objeto no tiene ninguna propiedad identificadora, lo salva llamando a
+ <literal>save()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ si el identificador del objeto tiene el valor asignado a un objeto recién
+ instanciado, lo salva llamando a <literal>save()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ si el objeto está versionado (por un <literal><version></literal> o
+ <literal><timestamp></literal>), y el valor de la propiedad de versión
+ es el mismo valor asignado a una objeto recién instanciado, lo salva llamando
+ a <literal>save()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ en cualquier otro caso se actualiza el objeto llamando a <literal>update()</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ y <literal>merge()</literal> es muy diferente:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ si existe una instancia persistente con el mismo identificador asignado actualmente con la
+ sesión, copia el estado del objeto dado en la instancia persistente
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ si no existe ninguna instancia persistente actualmente asociada a la sesión,
+ intente cargarla de la base de datos, o crear una nueva instancia persistente
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ la instancia persistente es devuelta
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ la instancia dada no resulta ser asociada a la sesión, permanece separada
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect1>
+
+ <sect1 id="objectstate-deleting" revision="1">
+ <title>Borrando objetos persistentes</title>
+
+ <para>
+ <literal>Session.delete()</literal> quitará el estado de un objeto de la base de datos.
+ Por supuesto, tu aplicación podría tener aún una referencia a un objeto borrado. Lo mejor
+ es pensar en <literal>delete()</literal> como hacer transitoria una instancia persistente.
+ </para>
+
+ <programlisting><![CDATA[sess.delete(cat);]]></programlisting>
+
+ <para>
+ Puedes borrar los objetos en el orden que gustes, sin riesgo de violaciones
+ de restricción de clave foránea. Aún es posible violar una restricción
+ <literal>NOT NULL</literal> sobre una columna clave foránea borrando objetos
+ en un orden erróneo, por ejemplo, si borras el padre, pero olvidas borrar los
+ hijos.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-replicating" revision="1">
+ <title>Replicando objetos entre dos almacénes de datos diferentes</title>
+
+ <para>
+ Es ocasionalmente útil ser capaz de tomar un grafo de instancias persistentes
+ y hacerlas persistentes en un almacén de datos diferente, sin regenerar los valores
+ identificadores.
+ </para>
+
+ <programlisting><![CDATA[//retrieve a cat from one database
+Session session1 = factory1.openSession();
+Transaction tx1 = session1.beginTransaction();
+Cat cat = session1.get(Cat.class, catId);
+tx1.commit();
+session1.close();
+
+//reconcile with a second database
+Session session2 = factory2.openSession();
+Transaction tx2 = session2.beginTransaction();
+session2.replicate(cat, ReplicationMode.LATEST_VERSION);
+tx2.commit();
+session2.close();]]></programlisting>
+
+ <para>
+ El <literal>ReplicationMode</literal> determina cómo <literal>replicate()</literal>
+ tratará los conflictos con filas existentes en la base de datos.
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>ReplicationMode.IGNORE</literal> - ignora el objeto cuando existe una fila
+ de base de datos con el mismo identificador
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>ReplicationMode.OVERWRITE</literal> - sobrescribe cualquier fila de base de
+ datos existente con el mismo identificador
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>ReplicationMode.EXCEPTION</literal> - lanza una excepción si existe una fila
+ de base de datos con el mismo identificador
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>ReplicationMode.LATEST_VERSION</literal> - sobrescribe la fila si su número
+ de versión es anterior al número de versión del objeto, o en caso contrario ignora el objeto
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Los casos de uso para esta funcionalidad incluyen reconciliar datos ingresados en
+ instancias diferentes de bases de datos, actualizar información de configuración de
+ sistema durante actualizaciones de producto, deshacer cambios producidos durante
+ transacciones no-ACID y más.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-flushing">
+ <title>Limpiando (flushing) la sesión</title>
+
+ <para>
+ Cada tanto, la <literal>Session</literal> ejecutará las sentencias SQL necesarias para
+ sincronizar el estado de la conexión JDBC con el estado de los objetos mantenidos en menoria.
+ Este proceso, <emphasis>limpieza (flush)</emphasis>, ocurre por defecto en los siguientes
+ puntos
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ antes de algunas ejecuciones de consulta
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ desde <literal>org.hibernate.Transaction.commit()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ desde <literal>Session.flush()</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Las sentencias SQL son liberadas en el siguiente orden
+ </para>
+
+ <orderedlist spacing="compact">
+ <listitem>
+ <para>
+ todas las inserciones de entidades, en el mismo orden que los objetos
+ correspondientes fueron salvados usando <literal>Session.save()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ todas las actualizaciones de entidades
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ todas los borrados de colecciones
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ todos los borrados, actualizaciones e inserciones de elementos de colección
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ todas las inserciones de colecciones
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ todos los borrados de entidades, en el mismo orden que los objetos
+ correspondientes fueron borrados usando <literal>Session.delete()</literal>
+ </para>
+ </listitem>
+ </orderedlist>
+
+ <para>
+ (Una excepción es que los objetos que usan generación de ID <literal>native</literal>
+ se insertan cuando son salvados.)
+ </para>
+
+ <para>
+ Excepto cuando llamas explícitamente a <literal>flush()</literal>, no hay en absoluto
+ garantías sobre <emphasis>cuándo</emphasis> la <literal>Session</literal> ejecuta las
+ llamadas JDBC. sólo sobre el <emphasis>orden</emphasis> en que son ejecutadas. Sin embargo,
+ Hibernate garantiza que los métodos <literal>Query.list(..)</literal> nunca devolverán datos
+ añejos o erróneos.
+ </para>
+
+ <para>
+ Es posible cambiar el comportamiento por defecto de modo que la limpieza (flush)
+ ocurra menos frecuentemente. La clase <literal>FlushMode</literal> tres modos
+ diferentes: sólo en tiempo de compromiso (y sólo cuando se use la API de
+ <literal>Transaction</literal> de Hibernate), limpieza automática usando la rutina
+ explicada, o nunca limpiar a menos que se llame a <literal>flush()</literal>
+ explícitamente. El último modo es útil para unidades de trabajo largas, donde una
+ <literal>Session</literal> se mantiene abierta y desconectada por largo tiempo
+ (ver <xref linkend="transactions-optimistic-longsession"/>).
+ </para>
+
+ <programlisting><![CDATA[sess = sf.openSession();
+Transaction tx = sess.beginTransaction();
+sess.setFlushMode(FlushMode.COMMIT); // allow queries to return stale state
+
+Cat izi = (Cat) sess.load(Cat.class, id);
+izi.setName(iznizi);
+
+// might return stale data
+sess.find("from Cat as cat left outer join cat.kittens kitten");
+
+// change to izi is not flushed!
+...
+tx.commit(); // flush occurs]]></programlisting>
+
+ <para>
+ Durante la limpieza, puede ocurrir una excepción (por ejemplo, si una operación DML
+ violase una restricción). Ya que el manejo de excepciones implica alguna comprensión
+ del comportamiento transaccional de Hibernate, lo discutimos en <xref linkend="transactions"/>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-transitive">
+ <title>Persistencia transitiva</title>
+
+ <para>
+ Es absolutamente incómodo dalvar, borrar, o reunir objetos individuales,
+ especialmente si tratas con un grafo de objetos asociados. Un caso común es
+ una relación padre/hijo. Considera el siguiente ejemplo:
+ </para>
+
+ <para>
+ Si los hijos en una relación padre/hijo pudieran ser tipificados en valor
+ (por ejemplo, una colección de direcciones o cadenas), sus ciclos de vida
+ dependerían del padre y se requeriría ninguna otra acción para el tratamiento
+ en "cascada" de cambios de estado. Cuando el padre es salvado, los objetos hijo
+ tipificados en valor son salvados también, cuando se borra el padre, se borran
+ los hijos, etc. Esto funciona incluso para operaciones como el retiro de un
+ hijo de la colección. Hibernate detectará esto y, ya que los objetos tipificados
+ en valor no pueden tener referencias compartidas, borrará el hijo de la base
+ de datos.
+ </para>
+
+ <para>
+ Ahora considera el mismo escenario con los objetos padre e hijos siendo entidades,
+ no tipos de valor (por ejemplo, categorías e ítems, o gatos padre e hijos).
+ Las entidades tienen su propio ciclo de vida, soportan referencias compartidas
+ (de modo que quitar una entidad de una colección no significa que sea borrada),
+ y no hay por defecto ningún tratamiento en "cascada" de estado de una entidad
+ a otras entidades asociadas. Hibernate no implementa <emphasis>persistencia por
+ alcance</emphasis>.
+ </para>
+
+ <para>
+ Para cada operación básica de la sesión de Hibernate - incluyendo <literal>persist(), merge(),
+ saveOrUpdate(), delete(), lock(), refresh(), evict(), replicate()</literal> - hay un estilo
+ de cascada correspondiente. Respectivamente, los estilos de cascada se llaman <literal>create,
+ merge, save-update, delete, lock, refresh, evict, replicate</literal>. Si quieres que una
+ operación sea tratada en cascada a lo largo de una asociación, debes indicar eso en el
+ documento de mapeo. Por ejemplo:
+ </para>
+
+ <programlisting><![CDATA[<one-to-one name="person" cascade="persist"/>]]></programlisting>
+
+ <para>
+ Los estilos de cascada pueden combinarse:
+ </para>
+
+ <programlisting><![CDATA[<one-to-one name="person" cascade="persist,delete,lock"/>]]></programlisting>
+
+ <para>
+ Puedes incluso usar <literal>cascade="all"</literal> para especificar que <emphasis>todas</emphasis>
+ las operaciones deben ser tratadas en cascada a lo largo de la asociación. El por defecto
+ <literal>cascade="none"</literal> especifica que ninguna operación será tratada en cascada.
+ </para>
+
+ <para>
+ Un estilo de cascada especial, <literal>delete-orphan</literal>, se aplica sólo a
+ asociaciones uno-a-muchos, e indica que la operación <literal>delete()</literal> debe
+ aplicarse a cualquier objeto hijo que sea quitado de la asociación.
+ </para>
+
+
+ <para>
+ Recomendaciones:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ Usualmente no tiene sentido habilitar el tratamiento en cascada a una asociación
+ <literal><many-to-one></literal> o <literal><many-to-many></literal>.
+ El tratamiento en cascada es frecuentemente útil para las asociaciones
+ <literal><one-to-one></literal> y <literal><one-to-many></literal>.
+ associations.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Si la esperanza de vida de los objetos hijos está ligada a la eesperanza de
+ vida del objeto padre, házlo un <emphasis>objeto de ciclo de vida</emphasis>
+ especificando <literal>cascade="all,delete-orphan"</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ En otro caso, puede que no necesites tratamiento en cascada en absoluto. Pero
+ si piensas que estarás trabajando frecuentemente con padre e hijos juntos en la
+ misma transacción, y quieres ahorrarte algo de tipeo, considera usar
+ <literal>cascade="persist,merge,save-update"</literal>.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Mapear una asociación (ya sea una asociación monovaluada, o una colección) con
+ <literal>cascade="all"</literal> marca la asociación como una relación del estilo
+ <emphasis>padre/hijo</emphasis> donde save/update/delete en el padre resulta
+ en save/update/delete del hijo o hijos.
+ </para>
+ <para>
+ Además, una mera referencia a un hijo desde un padre persistente resultará en
+ un save/update del hijo. Esta metáfora está incompleta, sin embargo. Un hijo
+ que deje de ser referenciado por su padre <emphasis>no</emphasis> es borrado
+ automáticamente, excepto en el caso de una asociación <literal><one-to-many></literal>
+ mapeada con <literal>cascade="delete-orphan"</literal>. La semántica precisa de
+ las operaciones en cascada para una relación padre/hijo es:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ Si un padre le es pasado a <literal>persist()</literal>, todos los hijos le son
+ pasados a <literal>persist()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Si un padre le es pasado a <literal>merge()</literal>, todos los hijos le son
+ pasados a <literal>merge()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Si un padre le es pasado a <literal>save()</literal>, <literal>update()</literal> o
+ <literal>saveOrUpdate()</literal>, todos los hijos le son pasados a <literal>saveOrUpdate()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Si un hijo transitorio o separado se vuelve referenciado por un padre
+ persistente, le es pasado a <literal>saveOrUpdate()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Si un padre es borrado, todos los hijos le son pasados a <literal>delete()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Si un hijo deja de ser referenciado por un padre persistente,
+ <emphasis>no ocurre nada especial</emphasis> - la aplicación debe
+ borrar explícitamente el hijo de ser necesario - a menos que
+ <literal>cascade="delete-orphan"</literal>, en cuyo caso el hijo
+ "huérfano" es borrado.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect1>
+
+ <sect1 id="objectstate-metadata">
+ <title>Usando metadatos</title>
+
+ <para>
+ Hibernate requiere de un modelo de meta-nivel muy rico de todas las entidades y tipos de valor.
+ De vez en cuando, este modelo es muy útil para la aplicación misma. Por ejemplo, la aplicación
+ podría usar los metadatos de Hibernate para implementar un algoritmo "inteligente" de copia
+ en profundidad que entienda qué objetos deben ser copiados (por ejemplo, tipo de valor mutables)
+ y cuáles no (por ejemplo, tipos de valor inmutables y, posiblemente, entidades asociadas).
+ </para>
+ <para>
+ Hibernate expone los metadatos vía las interfaces <literal>ClassMetadata</literal> y
+ <literal>CollectionMetadata</literal> y la jerarquía <literal>Type</literal>. Las instancias
+ de las interfaces de metadatos pueden obtenerse de <literal>SessionFactory</literal>.
+ </para>
+
+ <programlisting><![CDATA[Cat fritz = ......;
+ClassMetadata catMeta = sessionfactory.getClassMetadata(Cat.class);
+
+Object[] propertyValues = catMeta.getPropertyValues(fritz);
+String[] propertyNames = catMeta.getPropertyNames();
+Type[] propertyTypes = catMeta.getPropertyTypes();
+
+// get a Map of all properties which are not collections or associations
+Map namedValues = new HashMap();
+for ( int i=0; i<propertyNames.length; i++ ) {
+ if ( !propertyTypes[i].isEntityType() && !propertyTypes[i].isCollectionType() ) {
+ namedValues.put( propertyNames[i], propertyValues[i] );
+ }
+}]]></programlisting>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/toolset_guide.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/toolset_guide.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/toolset_guide.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,459 @@
+<chapter id="toolsetguide" revision="2">
+ <title>Guía del Conjunto de Herramientas</title>
+
+ <para>
+ La ingeniería de ida y vuelta con Hibernate es posible usando un conjunto de plugins de Eclipse,
+ herramientas de línea de comandos, así como tareas de Ant.
+ </para>
+
+ <para>
+ Las <emphasis>Herramientas de Hibernate</emphasis> actualmente incluyen plugins para la IDE de
+ Eclipse así como tareas de Ant para la ingeniería inversa de bases de datos existentes:
+ </para>
+
+ <itemizedlist>
+ <listitem><para>
+ <emphasis>Editor de Mapeo:</emphasis> Un editor de ficheros de mapeo XML, que soporta autocompleción
+ y resaltado de sintáxis. Soporta también autocompleción semántica de nombres de clases y nombres de
+ campos/propiedades, haciéndolo mucho más versátil que un editor de XML normal.
+ </para></listitem>
+ <listitem><para>
+ <emphasis>Consola:</emphasis> La consola es una nueva vista en Eclipse. Además de la vista de
+ árbol de tus configuraciones de consola, tienes también una vista interactiva de tus clases
+ persistentes y sus relaciones. La console te permite ejecutar consultas HQL contra tu base de datos y
+ navegar el resultado directamente en Eclipse.
+ </para></listitem>
+ <listitem><para>
+ <emphasis>Asistentes de Desarrollo:</emphasis> Se proveen muchos asistentes con las herramientas
+ de Eclipse. Puedes usar un asistente para generar rápidamente ficheros de configuración de Hibernate
+ (cfg.xml), o incluso puedes haceruna ingeniería inversa completa de un esquema de base de datos existente
+ en ficheros de código de POJO y ficheros de mapeo de Hibernate. El asistente de ingeniería inversa soporta
+ plantillas personalizables.
+ </para></listitem>
+ <listitem><para>
+ <emphasis>Tareas de Ant:</emphasis>
+ </para></listitem>
+
+ </itemizedlist>
+
+ <para>
+ Por favor refiérete al paquete <emphasis>Herramientas de Hibernate</emphasis> y su documentación para
+ más información.
+ </para>
+
+ <para>
+ Sin embargo, el paquete principal de Hibernate viene incluyendo una herramienta integrada
+ (puede ser usada incluso "dentro" de Hibernate on-the-fly): <emphasis>SchemaExport</emphasis>
+ también conocido como <literal>hbm2ddl</literal>.
+ </para>
+
+ <sect1 id="toolsetguide-s1" revision="2">
+ <title>Generación automática de esquemas</title>
+
+ <para>
+ Una utilidad de Hibernate puede generar DDL desde tus ficheros de mapeo. El esquema generado incluye
+ restricciones de integridad referencial (claves primarias y foráneas) para las tablas de entidades y
+ colecciones. Las tablas y secuencias también son creadas para los generadores de identificadores mapeados.
+ </para>
+
+ <para>
+ <emphasis>Debes</emphasis> especificar un <literal>Dialecto</literal> SQL vía la propiedad
+ <literal>hibernate.dialect</literal> al usar esta herramienta, ya que el DDL es altamente específico del
+ vendedor.
+ </para>
+
+ <para>
+ First, customize your mapping files to improve the generated schema.
+ </para>
+
+ <sect2 id="toolsetguide-s1-2" revision="1">
+ <title>Personalizando el esquema</title>
+
+ <para>
+ Muchos elementos de mapeo de Hibernate definen un atributo opcional llamado <literal>length</literal>.
+ Con este atributo puedes establecer el tamaño de una columna. (O, para tipos de datos
+ numéricos/decimales, la precisión.)
+ </para>
+
+ <para>
+ Algunas etiquetas también aceptan un atributo <literal>not-null</literal> (para generar una restricción
+ <literal>NOT NULL</literal> en columnas de tablas) y y un atributo <literal>unique</literal> (para generar
+ restricciones <literal>UNIQUE</literal> en columnas de tablas).
+ </para>
+
+ <para>
+ Algunas etiquetas aceptan un atributo <literal>index</literal> para especificar el nombre de un índice
+ para esa columna. Se puede usar un atributo <literal>unique-key</literal> para agrupar columnas en una
+ restricción de clave de una sola unidad. Actualmente, el valor especificado del atributo
+ <literal>unique-key</literal> <emphasis>no</emphasis> es usado para nombrar la restricción, sólo para
+ agrupar las columnas en el fichero de mapeo.
+ </para>
+
+ <para>
+ Ejemplos:
+ </para>
+
+ <programlisting><![CDATA[<property name="foo" type="string" length="64" not-null="true"/>
+
+<many-to-one name="bar" foreign-key="fk_foo_bar" not-null="true"/>
+
+<element column="serial_number" type="long" not-null="true" unique="true"/>]]></programlisting>
+
+ <para>
+ Alternativamente, estos elementos aceptan tambíen un elemento hijo <literal><column></literal>.
+ Esto es particularmente útil para tipos multicolumnas:
+ </para>
+
+ <programlisting><![CDATA[<property name="foo" type="string">
+ <column name="foo" length="64" not-null="true" sql-type="text"/>
+</property>]]></programlisting>
+
+ <programlisting><![CDATA[<property name="bar" type="my.customtypes.MultiColumnType"/>
+ <column name="fee" not-null="true" index="bar_idx"/>
+ <column name="fi" not-null="true" index="bar_idx"/>
+ <column name="fo" not-null="true" index="bar_idx"/>
+</property>]]></programlisting>
+
+ <para>
+ El atributo <literal>sql-type</literal> permite al usuario sobrescribir el mapeo por defecto de
+ tipo Hibernate a tipo de datos SQL.
+ </para>
+
+ <para>
+ El atributo <literal>check</literal> te permite especificar una comprobación de restricción.
+ </para>
+
+ <programlisting><![CDATA[<property name="foo" type="integer">
+ <column name="foo" check="foo > 10"/>
+</property>]]></programlisting>
+
+ <programlisting><![CDATA[<class name="Foo" table="foos" check="bar < 100.0">
+ ...
+ <property name="bar" type="float"/>
+</class>]]></programlisting>
+
+
+ <table frame="topbot" id="schemattributes-summary" revision="2">
+ <title>Resumen</title>
+ <tgroup cols="3">
+ <colspec colwidth="1*"/>
+ <colspec colwidth="1*"/>
+ <colspec colwidth="2.5*"/>
+ <thead>
+ <row>
+ <entry>Atributo</entry>
+ <entry>Valores</entry>
+ <entry>Interpretación</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>length</literal></entry>
+ <entry>number</entry>
+ <entry>largo de columna/precisión decimal</entry>
+ </row>
+ <row>
+ <entry><literal>not-null</literal></entry>
+ <entry><literal>true|false</literal></entry>
+ <entry>especifica que la columna debe ser no nulable</entry>
+ </row>
+ <row>
+ <entry><literal>unique</literal></entry>
+ <entry><literal>true|false</literal></entry>
+ <entry>especifica que la columna debe tener una restricción de unicidad</entry>
+ </row>
+ <row>
+ <entry><literal>index</literal></entry>
+ <entry><literal>index_name</literal></entry>
+ <entry>especifica el nombre de un índice (multicolumna)</entry>
+ </row>
+ <row>
+ <entry><literal>unique-key</literal></entry>
+ <entry><literal>unique_key_name</literal></entry>
+ <entry>especifica el nombre de una restricción de unicidad multicolumna</entry>
+ </row>
+ <row>
+ <entry><literal>foreign-key</literal></entry>
+ <entry><literal>foreign_key_name</literal></entry>
+ <entry>
+ especifica el nombre de la restricción de clave foránea generada por una
+ asociación, úsalo en los elementos de mapeo <one-to-one>, <many-to-one>,
+ <key>, y <many-to-many>. Nota que los lados
+ <literal>inverse="true"</literal> no serán considerados por
+ <literal>SchemaExport</literal>.
+ </entry>
+ </row>
+ <row>
+ <entry><literal>sql-type</literal></entry>
+ <entry><literal>column_type</literal></entry>
+ <entry>
+ sobrescribe el tipo de columna por defecto (sólo atributo del elemento
+ <literal><column></literal>)
+ </entry>
+ </row>
+ <row>
+ <entry><literal>check</literal></entry>
+ <entry>expresión SQL</entry>
+ <entry>
+ crea una restricción de comprobación SQL en columna o tabla
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ El elemento <literal><comment></literal> te permite especificar un comentario para el esquema
+ generado.
+ </para>
+
+ <programlisting><![CDATA[<class name="Customer" table="CurCust">
+ <comment>Current customers only</comment>
+ ...
+</class>]]></programlisting>
+
+ <programlisting><![CDATA[<property name="balance">
+ <column name="bal">
+ <comment>Balance in USD</comment>
+ </column>
+</property>]]></programlisting>
+
+ <para>
+ Esto resulta en una sentencia <literal>comment on table</literal> o <literal>comment on column</literal>
+ en el DDL generado (donde esté soportado).
+ </para>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-3">
+ <title>Ejecutando la herramienta</title>
+
+ <para>
+ La herramienta <literal>SchemaExport</literal> escribe un guión DDL a la salida estándar y/o
+ ejecuta las sentencias DDL.
+ </para>
+
+ <para>
+ <literal>java -cp </literal><emphasis>classpaths_de_hibernate</emphasis>
+ <literal>org.hibernate.tool.hbm2ddl.SchemaExport</literal> <emphasis>opciones ficheros_de_mapeo</emphasis>
+ </para>
+
+ <table frame="topbot">
+ <title>Opciones de Línea de Comandos de <literal>SchemaExport</literal></title>
+ <tgroup cols="2">
+ <colspec colwidth="1.5*"/>
+ <colspec colwidth="2*"/>
+ <thead>
+ <row>
+ <entry>Opción</entry>
+ <entry>Descripción</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>--quiet</literal></entry>
+ <entry>no enviar a salida estándar el guión</entry>
+ </row>
+ <row>
+ <entry><literal>--drop</literal></entry>
+ <entry>sólo desechar las tablas</entry>
+ </row>
+ <row>
+ <entry><literal>--text</literal></entry>
+ <entry>no exportar a la base de datos</entry>
+ </row>
+ <row>
+ <entry><literal>--output=my_schema.ddl</literal></entry>
+ <entry>enviar la salida del guión ddl a un fichero</entry>
+ </row>
+ <row>
+ <entry><literal>--config=hibernate.cfg.xml</literal></entry>
+ <entry>lee la configuración de Hibernate de un fichero XML</entry>
+ </row>
+ <row>
+ <entry><literal>--properties=hibernate.properties</literal></entry>
+ <entry>lee las propiedades de base de datos de un fichero</entry>
+ </row>
+ <row>
+ <entry><literal>--format</literal></entry>
+ <entry>formatea agradablemente el SQL generado en el guión</entry>
+ </row>
+ <row>
+ <entry><literal>--delimiter=x</literal></entry>
+ <entry>establece un delimitador de fin de línea para el guión</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ Puedes incluso encajar <literal>SchemaExport</literal> en tu aplicación:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = ....;
+new SchemaExport(cfg).create(false, true);]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-4">
+ <title>Propiedades</title>
+
+ <para>
+ Las propiedades de base de datos pueden especificarse
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>como propiedades de sistema con <literal>-D</literal><emphasis><property></emphasis></para>
+ </listitem>
+ <listitem>
+ <para>en <literal>hibernate.properties</literal></para>
+ </listitem>
+ <listitem>
+ <para>en un fichero de propiedades mencionado con <literal>--properties</literal></para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Las propiedades necesarias son:
+ </para>
+
+ <table frame="topbot">
+ <title>Propiedades de Conexión de SchemaExport</title>
+ <tgroup cols="2">
+ <colspec colwidth="1.5*"/>
+ <colspec colwidth="2*"/>
+ <thead>
+ <row>
+ <entry>Nombre de Propiedad</entry>
+ <entry>Descripción</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>hibernate.connection.driver_class</literal></entry>
+ <entry>clase del driver jdbc</entry>
+ </row>
+ <row>
+ <entry><literal>hibernate.connection.url</literal></entry>
+ <entry>url de jdbc</entry>
+ </row>
+ <row>
+ <entry><literal>hibernate.connection.username</literal></entry>
+ <entry>usuario de base de datos</entry>
+ </row>
+ <row>
+ <entry><literal>hibernate.connection.password</literal></entry>
+ <entry>contraseña de usuario</entry>
+ </row>
+ <row>
+ <entry><literal>hibernate.dialect</literal></entry>
+ <entry>dialecto</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-5">
+ <title>Usando Ant</title>
+
+ <para>
+ Puedes llamar a <literal>SchemaExport</literal> desde tu guión de construcción de Ant:
+ </para>
+
+ <programlisting><![CDATA[<target name="schemaexport">
+ <taskdef name="schemaexport"
+ classname="org.hibernate.tool.hbm2ddl.SchemaExportTask"
+ classpathref="class.path"/>
+
+ <schemaexport
+ properties="hibernate.properties"
+ quiet="no"
+ text="no"
+ drop="no"
+ delimiter=";"
+ output="schema-export.sql">
+ <fileset dir="src">
+ <include name="**/*.hbm.xml"/>
+ </fileset>
+ </schemaexport>
+</target>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-6">
+ <title>Actualizaciones incrementales de esquema</title>
+
+ <para>
+ La herramienta <literal>SchemaUpdate</literal> actualizará un esquema existente con cambios
+ "incrementales". Nota que <literal>SchemaUpdate</literal> depende fuertemente de la API de metadatos
+ de JDBC, de modo que no funcionará con todos los drivers JDBC.
+ </para>
+
+ <para>
+ <literal>java -cp </literal><emphasis>classpaths_de_hibernate</emphasis>
+ <literal>org.hibernate.tool.hbm2ddl.SchemaUpdate</literal> <emphasis>opciones ficheros_de_mapeo</emphasis>
+ </para>
+
+ <table frame="topbot">
+ <title>Opciones de Línea de Comandos de <literal>SchemaUpdate</literal></title>
+ <tgroup cols="2">
+ <colspec colwidth="1.5*"/>
+ <colspec colwidth="2*"/>
+ <thead>
+ <row>
+ <entry>Opción</entry>
+ <entry>Descripción</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>--quiet</literal></entry>
+ <entry>no enviar a salida estándar el guión</entry>
+ </row>
+ <row>
+ <entry><literal>--properties=hibernate.properties</literal></entry>
+ <entry>lee las propiedades de base de datos de un fichero</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ Puedes encajar <literal>SchemaUpdate</literal> en tu aplicación:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = ....;
+new SchemaUpdate(cfg).execute(false);]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-7">
+ <title>Usando Ant para actualizaciones incrementales de esquema</title>
+
+ <para>
+ Puedes llamar a <literal>SchemaUpdate</literal> desde el guión de Ant:
+ </para>
+
+ <programlisting><![CDATA[<target name="schemaupdate">
+ <taskdef name="schemaupdate"
+ classname="org.hibernate.tool.hbm2ddl.SchemaUpdateTask"
+ classpathref="class.path"/>
+
+ <schemaupdate
+ properties="hibernate.properties"
+ quiet="no">
+ <fileset dir="src">
+ <include name="**/*.hbm.xml"/>
+ </fileset>
+ </schemaupdate>
+</target>]]></programlisting>
+
+ </sect2>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/transactions.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/transactions.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/transactions.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,925 @@
+<chapter id="transactions" revision="1">
+ <title>Transacciones y Concurrencia</title>
+
+ <para>
+ El punto más importante sobre Hibernate y el control de concurrencia es que muy fácil
+ de comprender. Hibernate usa directamente conexiones JDBC y recursos JTA sin agregar
+ ningún comportamiento de bloqueo adicional. Recomendamos altamente que gastes algo de
+ tiempo con la especificación de JDBC, ANSI, y el aislamiento de transacciones de tu sistema
+ de gestión de base de datos. Hibernate sólo añade versionado automático pero no bloquea
+ objetos en memoria ni cambia el nivel de aislamiento de tus transacciones de base de datos.
+ Básicamente, usa Hibernate como usarías JDBC directo (o JTA/CMT) con tus recursos de base de
+ datos.
+ </para>
+
+ <para>
+ Sin embargo, además del versionado automático, Hibernate ofrece una API (menor) para
+ bloqueo pesimista de filas, usando la sintáxis <literal>SELECT FOR UPDATE</literal>.
+ Esta API se discute más adelante en este capítulo:
+ </para>
+
+ <para>
+ Comenzamos la discusión del control de concurrencia en Hibernate con la granularidad
+ de <literal>Configuration</literal>, <literal>SessionFactory</literal>, y
+ <literal>Session</literal>, así como la base de datos y las transacciones de aplicación
+ largas.
+ </para>
+
+ <sect1 id="transactions-basics">
+ <title>Ámbitos de sesión y de transacción</title>
+
+ <para>
+ Una <literal>SessionFactory</literal> es un objeto seguro entre hebras caro-de-crear
+ pensado para ser compartido por todas las hebras de la aplicación. Es creado una sola vez,
+ usualmente en el arranque de la aplicación, a partir de una instancia de <literal>Configuration</literal>.
+ </para>
+
+ <para>
+ Una <literal>Session</literal> es un objeto barato, inseguro entre hebras que debe
+ ser usado una sola vez, para un solo proceso de negocio, una sola unidad de trabajo,
+ y luego descartado. Una <literal>Session</literal> no obtendrá una <literal>Connection</literal>
+ JDBC (o un <literal>Datasource</literal>) a menos que sea necesario, de modo que puedas
+ abrir y cerrar seguramente una <literal>Session</literal> incluso si no estás seguro
+ que se necesitará acceso a los datos para servir una petición en particular. (Esto se
+ vuelve importante en cuanto estés implementando alguno de los siguientes patrones usando
+ intercepción de peticiones).
+ </para>
+
+ <para>
+ Para completar este cuadro tienes que pensar también en las transacciones de base de
+ datos. Una transacción de base de datos tiene que ser tan corta como sea posible, para
+ reducir la contención de bloqueos en la base de datos. Las transacciones largas de base de
+ datos prevendrán a tu aplicación de escalar a una carga altamente concurrente.
+ </para>
+
+ <para>
+ ¿Qué es el ámbito de una unidad de trabajo? ¿Puede una sola <literal>Session</literal> de Hibernate
+ extenderse a través de varias transacciones de base de datos o es ésta una relación uno-a-uno
+ de ámbitos? ¿Cuándo debes abrir y cerrar una <literal>Session</literal> y cómo demarcas los
+ límites de la transacción de base de datos?
+ </para>
+
+ <sect2 id="transactions-basics-uow">
+ <title>Unidad de trabajo</title>
+
+ <para>
+ Primero, no uses el antipatrón <emphasis>sesión-por-operación</emphasis>, esto es,
+ ¡no abras y cierres una <literal>Session</literal> para cada simple llamada a la base
+ de datos en una sola hebra! Por supuesto, lo mismo es verdad para transacciones de base de
+ datos. Las llamadas a base de datos en una aplicación se hacen usando una secuencia
+ prevista, que están agrupadas dentro de unidades de trabajo atómicas. (Nota que esto
+ también significa que el auto-commit después de cada una de las sentencias SQL es inútil
+ en una aplicación, este modo está pensado para trabajo ad-hoc de consola SQL.
+ Hibernate deshabilita, o espera que el servidor de aplicaciones lo haga, el modo
+ auto-commit inmediatamente.)
+ </para>
+
+ <para>
+ El patrón más común en una aplicación mutiusuario cliente/servidor es
+ <emphasis>sesión-por-petición</emphasis>. En este modelo, una petición del cliente
+ es enviada al servidor (en donde se ejecuta la capa de persistencia de Hibernate),
+ se abre una nueva <literal>Session</literal> de Hibernate, y todas las operaciones
+ de base de datos se ejecutan en esta unidad de trabajo. Una vez completado el trabajo
+ (y se ha preparado la respuesta para el cliente) la sesión es limpiada y cerrada.
+ Podrías usar una sola transacción de base de datos para servir a petición del cliente,
+ comenzándola y comprometiéndola cuando abres y cierras la <literal>Session</literal>.
+ La relación entre las dos es uno-a-uno y este modelo es a la medida perfecta de muchas
+ aplicaciones.
+ </para>
+
+ <para>
+ El desafío yace en la implementación: no sólo tienen que comenzarse y terminarse correctamente
+ la <literal>Session</literal> y la transacción, sino que además tienen que estar accesibles
+ para las operaciones de acceso a datos. La demarcación de una unidad de trabajo se implementa
+ idealmente usando un interceptor que se ejecuta cuando una petición llama al servidor y anter que
+ la respuesta sea enviada (es decir, un <literal>ServletFilter</literal>). Recomendamos ligar la
+ <literal>Session</literal> a la hebra que atiende la petición, usando una variable
+ <literal>ThreadLocal</literal>. Esto permite un fácil acceso (como acceder a una variable static)
+ en tódo el código que se ejecuta en esta hebra. Dependiendo del mecanismo de demarcación de
+ transacciones de base de datos que elijas, podrías mantener también el contexto de la transacción
+ en una variable <literal>ThreadLocal</literal>. Los patrones de implementación para esto son
+ conocidos como <emphasis>Sesión Local de Hebra (ThreadLocal Session)</emphasis> y
+ <emphasis>Sesión Abierta en Vista (Open Session in View)</emphasis>. Puedes extender fácilmente
+ la clase de ayuda <literal>HibernateUtil</literal> mostrada anteriormente para encontrar
+ una forma de implementar un interceptor e instalarlo en tu entorno. Ver el sitio web de Hibernate
+ para consejos y ejemplos.
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-basics-apptx">
+ <title>Transacciones de aplicación</title>
+
+ <para>
+ El patrón sesión-por-petición no es el único concepto útil que puedes usar para diseñar unidades
+ de trabajo. Muchos procesos de negocio requiere una serie completa de interacciones con el
+ usuario intercaladas con accesos a base de datos. En aplicaciones web y de empresa no es aceptable
+ que una transacción de base de datos se extienda a través de la interacción de un usuario.
+ Considera el siguiente ejemplo:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ Se abre la primera pantalla de un diálogo, los datos vistos por el usuario han sido
+ cargados en una <literal>Session</literal> y transacción de base de datos particular.
+ El usuario es libre de modificar los objetos.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ El usuario hace click en "Salvar" después de 5 minutos y espera que sus modificaciones
+ sean hechas persistentes. También espera que él sea la única persona editando esta
+ información y que no puede ocurrir ninguna modificación en conflicto.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Llamamos a esto unidad de trabajo, desde el punto de vista del usuario, una larga
+ <emphasis>transacción de aplicación</emphasis> ejecutándose. Hay muchas formas en
+ que puedes implementar esto en tu aplicación.
+ </para>
+
+ <para>
+ Una primera implementación ingenua podría mantener abierta la <literal>Session</literal>
+ y la transacción de base de datos durante el tiempo de pensar del usuario, con bloqueos
+ tomados en la base de datos para prevenir la modificación concurrente, y para garantizar
+ aislamiento y atomicidad. Esto es, por supuesto, un antipatrón, ya que la contención de
+ bloqueo no permitiría a la aplicación escalar con el número de usuarios concurrentes.
+ </para>
+
+ <para>
+ Claramente, tenemos que usar muchas transacciones de base de datos para implementar la transacción
+ de aplicación. En este caso, mantener el aislamiento de los procesos de negocio se vuelve una
+ responsabilidad parcial de la capa de aplicación. Una sola transacción de aplicación usualmente
+ abarca varias transacciones de base de datos. Será atómica si sólo una de estas transacciones de
+ base de datos (la última) almacena los datos actualizados, todas las otras simplemente leen datos
+ (por ejemplo, en un diálogo estilo-asistente abarcando muchos ciclos petición/respuesta).
+ Esto es más fácil de implementar de lo que suena, especialmente si usas las funcionalidades de
+ Hibernate:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Versionado Automático</emphasis> - Hibernate puede llevar un control automático de
+ concurrencia optimista por ti, puede detectar automáticamente si una modificación concurrente
+ ha ocurrido durante el tiempo de pensar del usuario.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Objetos Separados</emphasis> - Si decides usar el ya discutido patrón
+ de <emphasis>sesión-por-petición</emphasis>, todas las instancias cargadas estarán
+ en estado separado durante el tiempo de pensar del usuario. Hibernate te permite
+ volver a unir los objetos y hacer persistentes las modificaciones. El patrón se
+ llama <emphasis>sesión-por-petición-con-objetos-separados</emphasis>. Se usa
+ versionado automático para aislar las modificaciones concurrentes.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Sesión Larga</emphasis> - La <literal>Session</literal> de Hibernate puede ser
+ desconectada de la conexión JDBC subyacente después que se haya sido comprometida la
+ transacción de base de datos, y reconectada cuando ocurra una nueva petición del cliente.
+ Este patrón es conocido como <emphasis>sesión-por-transacción-de-aplicación</emphasis>
+ y hace la re-unión innecesaria. Para aislar las modificaciones concurrentes se usa el
+ versionado automático.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Tanto <emphasis>sesión-por-petición-con-objetos-separados</emphasis> como
+ <emphasis>sesión-por-transacción-de-aplicación</emphasis>, ambas tienen
+ ventajas y desventajas, las discutimos más adelante en este capítulo en el contexto
+ del control optimista de concurrencia.
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-basics-identity">
+ <title>Considerando la identidad del objeto</title>
+
+ <para>
+ Una aplicación puede acceder concurrentemente a el mismo estado persistente en dos
+ <literal>Session</literal>s diferentes. Sin embargo, una instancia de una clase
+ persistente nunca se comparte entre dos instancias de <literal>Session</literal>.
+ Por lo tanto existen dos nociones diferentes de identidad:
+ </para>
+
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term>Identidad de Base de Datos</term>
+ <listitem>
+ <para>
+ <literal>foo.getId().equals( bar.getId() )</literal>
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Identidad JVM</term>
+ <listitem>
+ <para>
+ <literal>foo==bar</literal>
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>
+ Entonces para objetos unidos a una <literal>Session</literal> <emphasis>en particular</emphasis>
+ (es decir en el ámbito de una <literal>Session</literal>) las dos nociones son equivalentes, y
+ la identidad JVM para la identidad de base de datos está garantizada por Hibernate. Sin embargo,
+ mientras la aplicación acceda concurrentemente al "mismo" (identidad persistente) objeto de negocio
+ en dos sesiones diferentes, las dos instancias serán realmente "diferentes" (identidad JVM).
+ Los conflictos se resuelven (con versionado automático) en tiempo de limpieza (flush) usando un
+ enfoque optimista.
+ </para>
+
+ <para>
+ Este enfoque deja que Hibernate y la base de datos se preocupen sobre la concurrencia. Además
+ provee la mejor escalabilidad, ya que garantizando la identidad un unidades de trabajo monohebra
+ no se necesitan bloqueos caros u otros medios de sincronización. La aplicación nunca necesita
+ sincronizar sobre ningún objeto de negocio, siempre que se apegue a una sola hebra por
+ <literal>Session</literal>. Dentro de una <literal>Session</literal> la aplicación puede usar
+ con seguridad <literal>==</literal> para comparar objetos.
+ </para>
+
+ <para>
+ Sin embargo, una aplicación que usa <literal>==</literal> fuera de una <literal>Session</literal>,
+ podría ver resultados inesperados. Esto podría ocurrir incluso en sitios algo inesperados,
+ por ejemplo, si pones dos instancias separadas dentro del mismo <literal>Set</literal>.
+ Ambas podrían tener la misma identidad de base de datos (es decir, representar la misma fila),
+ pero la identidad JVM, por definición, no está garantizada para las instancias en estado separado.
+ El desarrollador tiene que sobrescribir los métodos <literal>equals()</literal> y
+ <literal>hashCode()</literal> en las clases persistentes e implementar su propia noción de igualdad
+ de objetos. Hay una advertencia: Nunca uses el identificador de base de datos para implementar
+ la igualdad, usa una clave de negocio, una combinación de atributos únicos, usualmente inmutables.
+ El identificador de base de datos cambiará si un objeto transitorio es hecho persistente.
+ Si la instancia transitoria (usualmente junta a instancias separadas) es mantenida en un
+ <literal>Set</literal>, cambiar el código hash rompe el contrato del <literal>Set</literal>.
+ Los atributos para las claves de negocio no tienen que ser tan estables como las claves primarias
+ de base de datos, sólo tienes que garantizar estabilidad en tanto los objetos estén en el mismo
+ <literal>Set</literal>. Mira el sitio web de Hibernate para una discusión más cuidadosa de este
+ tema. Nota también que éste no es un tema de Hibernate, sino simplemente cómo la identidad y la igualdad
+ de los objetos Java tiene que ser implementada.
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-basics-issues">
+ <title>Temas comunes</title>
+
+ <para>
+ Nunca uses los antipatrones <emphasis>sesión-por-sesión-de-usuario</emphasis> o
+ <emphasis>sesión-por-aplicación</emphasis> (por supuesto, hay raras excepciones a esta
+ regla). Nota que algunis de los siguientes temas podrían también aparecer con los patrones
+ recomendados. Asegúrate que entiendes las implicaciones antes de tomar una decisión de
+ diseño:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ Una <literal>Session</literal> no es segura entre hebras. Las cosas que se suponen
+ que funcionan concurrentemente, como peticiones HTTP, beans de sesión, o workers de
+ Swing, provocarán condiciones de competencia si una instancia de <literal>Session</literal>
+ fuese compartida. Si guardas tu <literal>Session</literal> de Hibernate en tu
+ <literal>HttpSession</literal> (discutido más adelante), debes considerar sincronizar
+ el acceso a tu sesión HTTP. De otro modo, un usuario que hace click lo suficientemente
+ rápido puede llegar a usar la misma <literal>Session</literal> en dos hebras ejecutándose
+ concurrentemente.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Una excepción lanzada por Hibernate significa que tienes que deshacer (rollback) tu
+ transacción de base de datos y cerrar la <literal>Session</literal> inmediatamente
+ (discutido en más detalle luego). Si tu <literal>Session</literal> está ligada a la
+ aplicación, tienes que parar la aplicación. Deshacer (rollback) la transacción de base
+ de datos no pone a tus objetos de vuelta al estado en que estaban al comienzo de la
+ transacción. Esto significa que el estado de la base de datos y los objetos de negocio
+ quedan fuera de sincronía. Usualmente esto no es un problema, pues las excepciones no
+ son recuperables y tienes que volver a comenzar después del rollback de todos modos.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ La <literal>Session</literal> pone en caché todo objeto que esté en estado persistente
+ (vigilado y chequeado por estado sucio por Hibernate). Esto significa que crece sin
+ fin hasta que obtienes una OutOfMemoryException, si la mantienes abierta por un largo
+ tiempo o simplemente cargas demasiados datos. Una solución para esto es llamar a
+ <literal>clear()</literal> y <literal>evict()</literal> para gestionar el caché de la
+ <literal>Session</literal>, pero probalemente debas considerar un procedimiento almacenado
+ si necesitas operaciones de datos masivas. Se muestran algunas soluciones en
+ <xref linkend="batch"/>. Mantener una <literal>Session</literal> abierta por la duración
+ de una sesión de usuario significa también una alta probabilidad de datos añejos.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="transactions-demarcation">
+ <title>Demarcación de la transacción de base de datos</title>
+
+ <para>
+ Los límites de las transacciones de base de datos (o sistema) son siempre necesarios. Ninguna comunicación
+ con la base de datos puede darse fuera de una transacción de base de datos (esto parece confundir muchos
+ desarrolladores acostumbrados al modo auto-commit). Siempre usa límites de transacción claros, incluso
+ para las operaciones de sólo lectura. Dependiendo del nivel de aislamiento y las capacidades de base de
+ datos, esto podría o no ser requerido, pero no hay un merma si siempre demarcas explícitamente
+ las transacciones.
+ </para>
+
+ <para>
+ Una aplicación Hibernate puede ejecutarse en entornos no manejados (es decir, como independiente,
+ Web simple, o aplicaciones Swing) y entornos manejados J2EE. En un entorno no manejado, Hibernate es
+ usualmente responsable de su propio pool de conexiones de base de datos. El desarrollador de aplicaciones
+ tiene que establecer manualmente los límites de transacción, en otras palabras, hacer begin, commit, o
+ rollback las transacciones de base de datos por sí mismo. Un entorno manejado usualmente provee transacciones
+ gestionadas por contenedor, con el ensamble de transacción definido declarativamente en descriptores de
+ despliegue de beans de sesión EJB, por ejemplo. La demarcación programática de transacciones no es más
+ necesario, incluso limpiar (flush) la <literal>Session</literal> es hecho automáticamente.
+ </para>
+
+ <para>
+ Sin embargo, frecuentemente es deseable mantener portable tu capa de persistencia. Hibernate ofrece
+ una API de envoltura llamada <literal>Transaction</literal> que se traduce al sistema de transacciones
+ nativo de tu entorno de despliegue. Esta API es realmente opcional, pero recomendamos fuertemente su uso
+ salvo que estés en un bean de sesión CMT.
+ </para>
+
+ <para>
+ Usualmente, finalizar una <literal>Session</literal> implica cuatro fases distintas:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ limpiar (flush) la sesión
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ comprometer la transacción
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ cerrar la sesión
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ manejar excepciones
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Limpiar la sesión ha sido discutido anteriormente, tendremos ahora una mirada más de cerca
+ a la demarcación de transacciones y manejo de excepciones en sendos entornos manejado y no manejados.
+ </para>
+
+
+ <sect2 id="transactions-demarcation-nonmanaged">
+ <title>Entorno no manejado</title>
+
+ <para>
+ Si una capa de persistencia Hibernate se ejecuta en un entorno no manejado, las conexiones
+ de base de datos son manejadas usualmente por el mecanismo de pooling de Hibernate. El idioma
+ manejo de sesión/transacción se ve así:
+ </para>
+
+ <programlisting><![CDATA[//Non-managed environment idiom
+Session sess = factory.openSession();
+Transaction tx = null;
+try {
+ tx = sess.beginTransaction();
+
+ // do some work
+ ...
+
+ tx.commit();
+}
+catch (RuntimeException e) {
+ if (tx != null) tx.rollback();
+ throw e; // or display error message
+}
+finally {
+ sess.close();
+}]]></programlisting>
+
+ <para>
+ No tienes que limpiar con <literal>flush()</literal> la <literal>Session</literal> explícitamente -
+ la llamada a <literal>commit()</literal> automáticamente dispara la sincronización.
+ </para>
+
+ <para>
+ Una llamada a <literal>close()</literal> marca el fin de una sesión. La principal implicación
+ de <literal>close()</literal> es que la conexión JDBC será abandonada por la sesión.
+ </para>
+
+ <para>
+ Este código Java es portable y se ejecuta tanto en entornos no manejados como en entornos JTA.
+ </para>
+
+ <para>
+ Muy probablemente nunca veas este idioma en código de negocio en una aplicación normal;
+ las excepciones fatales (sistema) deben siempre ser capturadas en la "cima". En otras palabras,
+ el código que ejecuta las llamadas de Hibernate (en la capa de persistencia) y el código que
+ maneja <literal>RuntimeException</literal> (y usualmente sólo puede limpiar y salir) están en
+ capas diferentes. Esto puede ser un desafío de diseñarlo tú mismo y debes usar los servicios
+ de contenedor J2EE/EJB en cuanto estuviesen disponibles. El manejo de excepciones se dicute
+ más adelante en este capítulo.
+ </para>
+
+ <para>
+ Nota que debes seleccionar <literal>org.hibernate.transaction.JDBCTransactionFactory</literal>
+ (que es el por defecto).
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-demarcation-jta">
+ <title>Usando JTA</title>
+
+ <para>
+ Si tu capa de persistencia se ejecuta en un servidor de aplicaciones (por ejemplo, detrás
+ de beans de sesión EJB), cada conexión de datasource obtenida por Hibernate será parte
+ automáticamente de la transacción JTA global. Hibernate ofrece dos estrategias para esta
+ integración.
+ </para>
+
+ <para>
+ Si usas transacciones gestionadas-por-bean (BMT) Hibernate le dirá al servidor de aplicaciones
+ que comience y finalice una transacción BMT si usas la API de <literal>Transaction</literal>.
+ De modo que, el código de gestión de la transacción es idéntico al de un entorno no manejado.
+ </para>
+
+ <programlisting><![CDATA[// BMT idiom
+Session sess = factory.openSession();
+Transaction tx = null;
+try {
+ tx = sess.beginTransaction();
+
+ // do some work
+ ...
+
+ tx.commit();
+}
+catch (RuntimeException e) {
+ if (tx != null) tx.rollback();
+ throw e; // or display error message
+}
+finally {
+ sess.close();
+}]]></programlisting>
+
+ <para>
+ Con CMT, la demarcación de la transacción se hace en descriptores de despliegue de beans de sesión,
+ no programáticamente. Si no quieres limpiar (flush) y cerrar manualmente la <literal>Session</literal>
+ por ti mismo, solamente establece <literal>hibernate.transaction.flush_before_completion</literal> a
+ <literal>true</literal>, <literal>hibernate.connection.release_mode</literal> a
+ <literal>after_statement</literal> o <literal>auto</literal> y
+ <literal>hibernate.transaction.auto_close_session</literal> a <literal>true</literal>. Hibernate
+ limpiará y cerrará entonces automáticamente la <literal>Session</literal> para ti. Lo único que resta
+ es deshacer (rollback) la transacción cuando ocurra una excepción. Afortunadamente, en un bean CMT,
+ incluso esto ocurre automáticamente, ya que una <literal>RuntimeException</literal> no manejada
+ disparada por un método de un bean de sesión le dice al contenedor que ponga a deshacer la transacción
+ global. <emphasis>Esto significa que, en CMT, no necesitas usar en absoluto la API de
+ <literal>Transaction</literal> de Hibernate.</emphasis>
+ </para>
+
+ <para>
+ Nota que debes elegir <literal>org.hibernate.transaction.JTATransactionFactory</literal> en un
+ bean de sesión BMT, y <literal>org.hibernate.transaction.CMTTransactionFactory</literal> en un
+ bean de sesión CMT, cuando configures la fábrica de transacciones de Hibernate. Recuerda además
+ establecer <literal>org.hibernate.transaction.manager_lookup_class</literal>.
+ </para>
+
+ <para>
+ Si trabajas en un entorno CMT, y usas limpieza (flushing) y cierre automáticos de la sesión,
+ podrías querer también usar la misma sesión en diferentes partes de tu código. Típicamente,
+ en un entorno no manejado, usarías una variable <literal>ThreadLocal</literal> para tener la sesión,
+ pero una sola petición de EJB puede ejecutarse en diferentes hebras (por ejemplo, un bean de sesión
+ llamando a otro bean de sesión). Si no quieres molestarte en pasar tu <literal>Session</literal>
+ por alrededor, la <literal>SessionFactory</literal> provee el método
+ <literal>getCurrentSession()</literal>, que devuelve una sesión que está pegada al contexto de
+ transacción JTA. ¡Esta es la forma más fácil de integrar Hibernate en una aplicación!
+ La sesión "actual" siempre tiene habilitados limpieza, cierre y liberación de conexión automáticos
+ (sin importar la configuración de las propiedades anteriores). Nuestra idioma de gestión de
+ sesión/transacción se reduce a:
+ </para>
+
+ <programlisting><![CDATA[// CMT idiom
+Session sess = factory.getCurrentSession();
+
+// do some work
+...
+
+]]></programlisting>
+
+ <para>
+ En otras palabras, todo lo que tienes que hacer en un entorno manejado, es llamar a
+ <literal>SessionFactory.getCurrentSession()</literal>, hacer tu trabajo de acceso a datos,
+ y dejar el resto al contenedor. Los límites de transacción se establecen declarativamente
+ en los descriptores de despliegue de tu bean de sesión. El ciclo de vida de la sesión es
+ manejado completamente por Hibernate.
+ </para>
+
+ <para>
+ Existe una advertencia al uso del modo de liberación de conexión <literal>after_statement</literal>.
+ Debido a una limitación tonta de la especificación de JTA, no es posible para Hibernate
+ limpiar automáticamente ningún <literal>ScrollableResults</literal> no cerrado ni
+ instancias de <literal>Iterator</literal> devueltas por <literal>scroll()</literal> o
+ <literal>iterate()</literal>. <emphasis>Debes</emphasis> liberar el cursor de base de datos
+ subyacente llamando a <literal>ScrollableResults.close()</literal> o
+ <literal>Hibernate.close(Iterator)</literal> explícitamente desde un bloque <literal>finally</literal>.
+ (Por supuesto, la mayoría de las aplicaciones pueden evitarlo fácilmente no usando en absoluto ningún
+ <literal>scroll()</literal> o <literal>iterate()</literal> desde el código CMT.)
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-demarcation-exceptions">
+ <title>Manejo de excepciones</title>
+
+ <para>
+ Si la <literal>Session</literal> lanza una excepción (incluyendo cualquier
+ <literal>SQLException</literal>), debes inmediatamente deshacer (rollback) la
+ transacción de base de datos, llamar a <literal>Session.close()</literal> y
+ descartar la instancia de <literal>Session</literal>. Ciertos métodos de
+ <literal>Session</literal> <emphasis>no</emphasis> dejarán la sesión en un
+ estado consistente. Ninguna excepción lanzada por Hibernate puede ser tratada
+ como recuperable. Asegúrate que la <literal>Session</literal> sea cerrada llamando
+ a <literal>close()</literal> en un bloque <literal>finally</literal>.
+ </para>
+
+ <para>
+ La <literal>HibernateException</literal>, que envuelve la mayoría de los errores que
+ pueden ocurrir en la capa de persistencia de Hibernate, en una excepción no chequeada
+ (no lo era en versiones anteriores de Hibernate). En nuestra opinión, no debemos forzar
+ al desarrollador de aplicaciones a capturar una excepción irrecuperable en una capa baja.
+ En la mayoría de los sistemas, las excepciones no chequeadas y fatales son manejadas
+ en uno de los primeros cuadros de la pila de llamadas a métodos (es decir, en las capas
+ más altas) y se presenta un mensaje de error al usuario de la aplicación (o se toma alguna
+ otra acción apropiada). Nota que Hibernate podría también lanzar otras excepciones no chequeadas
+ que no sean una <literal>HibernateException</literal>. Una vez más, no son recuperables y debe
+ tomarse una acción apropiada.
+ </para>
+
+ <para>
+ Hibernate envuelve <literal>SQLException</literal>s lanzadas mientras se interactúa con la base
+ de datos en una <literal>JDBCException</literal>. De hecho, Hibernate intentará convertir la excepción
+ en una subclase de <literal>JDBCException</literal> más significativa. La <literal>SQLException</literal>
+ está siempre disponible vía <literal>JDBCException.getCause()</literal>. Hibernate convierte la
+ <literal>SQLException</literal> en una subclase de <literal>JDBCException</literal> apropiada usando
+ el <literal>SQLExceptionConverter</literal> adjunto a la <literal>SessionFactory</literal>. Por defecto,
+ el <literal>SQLExceptionConverter</literal> está definido para el dialecto configurado; sin embargo,
+ es también posible enchufar una implementación personalizada (ver los javadocs de la clase
+ <literal>SQLExceptionConverterFactory</literal> para los detalles). Los subtipos estándar de
+ <literal>JDBCException</literal> son:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>JDBCConnectionException</literal> - indica un error con la comunicación JDBC subyacente.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>SQLGrammarException</literal> - indica un problema de gramática o sintáxis con el
+ SQL publicado.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>ConstraintViolationException</literal> - indica alguna forma de violación de restricción
+ de integridad.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>LockAcquisitionException</literal> - indica un error adquiriendo un nivel de bloqueo
+ necesario para realizar una operación solicitada.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>GenericJDBCException</literal> - una excepción genérica que no cayó en ninguna de las
+ otras categorías.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="transactions-optimistic">
+ <title>Control optimista de concurrencia</title>
+
+ <para>
+ El único enfoque que es consistente con alta concurrencia y alta escalabilidad es el control
+ optimista de concurrencia con versionamiento. El chuequeo de versión usa números de versión,
+ o timestamps, para detectar actualizaciones en conflicto (y para prevenir actualizaciones perdidas).
+ Hibernate provee para tres enfoques posibles de escribir código de aplicación que use concurrencia
+ optimista. Los casos de uso que hemos mostrado están en el contexto de transacciones de aplicación
+ largas pero el chequeo de versiones tiene además el beneficio de prevenir actualizaciones perdidas
+ en transacciones de base de datos solas.
+ </para>
+
+ <sect2 id="transactions-optimistic-manual">
+ <title>Chequeo de versiones de aplicación</title>
+
+ <para>
+ En una implementación sin mucha ayuda de Hibernate, cada interacción con la base de datos ocurre en una
+ nueva <literal>Session</literal> y el desarrollador es responsable de recargar todas las intancias
+ persistentes desde la base de datos antes de manipularlas. Este enfoque fuerza a la aplicación a
+ realizar su propio chequeo de versiones para asegurar el aislamiento de transacciones de base de datos.
+ Es el enfoque más similar a los EJBs de entidad.
+ </para>
+
+ <programlisting><![CDATA[// foo is an instance loaded by a previous Session
+session = factory.openSession();
+Transaction t = session.beginTransaction();
+int oldVersion = foo.getVersion();
+session.load( foo, foo.getKey() ); // load the current state
+if ( oldVersion!=foo.getVersion ) throw new StaleObjectStateException();
+foo.setProperty("bar");
+t.commit();
+session.close();]]></programlisting>
+
+ <para>
+ La propiedad <literal>version</literal> se mapea usando <literal><version></literal>,
+ e Hibernate la incrementará automáticamente durante la limpieza si la entidad está sucia.
+ </para>
+
+ <para>
+ Por supuesto, si estás operando un entorno de baja-concurrencia-de-datos y no requieres
+ chequeo de versiones, puedes usar este enfoque y simplemente saltar el chequeo de versiones.
+ En ese caso, <emphasis>el último compromiso (commit) gana</emphasis> será la estrategia por
+ defecto para tus transacciones de aplicación largas. Ten en mente que esto podría confundir
+ a los usuarios de la aplicación, pues podrían experimentar actualizaciones perdidas sin
+ mensajes de error ni chance de fusionar los cambios conflictivos.
+ </para>
+
+ <para>
+ Claramente, el chequeo manual de versiones es factible solamente en circunstancias muy triviales,
+ y no es práctico para la mayoría de aplicaciones. Frecuentemente, no sólo intancias solas, sino grafos
+ completos de objetos modificados tienen que ser chequeados. Hibernate ofrece chequeo de versiones
+ automático con el paradigma de diseño de <literal>Session</literal> larga o de instancias separadas.
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-optimistic-longsession">
+ <title>Sesión larga y versionado automático</title>
+
+ <para>
+ Una sola instancia de <literal>Session</literal> y sus instancias persistentes
+ son usadas para toda la transacción de aplicación. Hibernate chequea las versiones
+ de instancia en el momento de limpieza (flush), lanzando una excepción si se detecta
+ una modificación concurrente. Concierne al desarrollador capturar y manejar esta excepción
+ (las opciones comunes son la oportunidad del usuario de fusionar los cambios, o recomenzar el
+ proceso de negocio sin datos añejos).
+ </para>
+
+ <para>
+ La <literal>Session</literal> se desconecta de cualquier conexión JDBC subyacente
+ al esperar por una interacción del usuario. Este enfoque es el más eficiente en términos
+ de acceso a base de datos. La aplicación no necesita tratar por sí misma con el chequeo de
+ versiones, ni re-uniendo instancias separadas, ni tiene que recargar instancias en cada
+ transacción de base de datos.
+ </para>
+
+ <programlisting><![CDATA[// foo is an instance loaded earlier by the Session
+session.reconnect(); // Obtain a new JDBC connection
+Transaction t = session.beginTransaction();
+foo.setProperty("bar");
+t.commit(); // End database transaction, flushing the change and checking the version
+session.disconnect(); // Return JDBC connection ]]></programlisting>
+
+ <para>
+ El objeto <literal>foo</literal> todavía conoce en qué <literal>Session</literal> fue cargado.
+ <literal>Session.reconnect()</literal> obtiene una nueva conexión (o puedes proveer una) y
+ reasume la sesión. El método <literal>Session.disconnect()</literal> desconectará la sesión
+ de la conexión JDBC y la devolverá la conexión al pool (a menos que hayas provisto la conexión).
+ Después de la reconexión, para forzar un chequeo de versión en datos que no estés actualizando,
+ puedes llamar a <literal>Session.lock()</literal> con <literal>LockMode.READ</literal> sobre
+ cualquier objeto que pudiese haber sido actualizado por otra transacción. No necesitas bloquear
+ ningún dato que <emphasis>sí estés</emphasis> actualizando.
+ </para>
+
+ <para>
+ Si las llamadas explícitas a <literal>disconnect()</literal> y <literal>reconnect()</literal>
+ son muy onerosas, puedes usar en cambio <literal>hibernate.connection.release_mode</literal>.
+ </para>
+
+ <para>
+ Este patrón es problemático si la <literal>Session</literal> es demasiado grande para ser almacenada
+ durante el tiempo de pensar del usuario, por ejemplo, una <literal>HttpSession</literal> debe
+ mantenerse tan pequeña como sea posible. Ya que la <literal>Session</literal> es también el caché
+ (obligatorio) de primer nivel y contiene todos los objetos cargados, podemos probablemente cargar
+ esta estrategia sólo para unos pocos ciclos petición/respuesta. Esto está de hecho recomendado, ya que
+ la <literal>Session</literal> tendrá pronto también datos añejos.
+ </para>
+
+ <para>
+ Nota también que debes mantener la <literal>Session</literal> desconectada próxima a la capa
+ de persistencia. En otras palabras, usa una sesión de EJB con estado para tener la
+ <literal>Session</literal> y no transferirla a la capa web para almacenarla en la
+ <literal>HttpSession</literal> (ni incluso serializarla a una capa separada).
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-optimistic-detached">
+ <title>Objetos separados y versionado automático</title>
+
+ <para>
+ Cada interacción con el almacén persistente ocurre en una nueva <literal>Session</literal>.
+ Sin embargo, las mismas instancias persistentes son reusadas para cada interacción con la base de
+ datos. La aplicación manipula el estado de las instancias separadas originalmente cargadas en otra
+ <literal>Session</literal> y luego las readjunta usando <literal>Session.update()</literal>,
+ <literal>Session.saveOrUpdate()</literal>, o <literal>Session.merge()</literal>.
+ </para>
+
+ <programlisting><![CDATA[// foo is an instance loaded by a previous Session
+foo.setProperty("bar");
+session = factory.openSession();
+Transaction t = session.beginTransaction();
+session.saveOrUpdate(foo); // Use merge() if "foo" might have been loaded already
+t.commit();
+session.close();]]></programlisting>
+
+ <para>
+ De nuevo, Hibernate chequeará las versiones de instancia durante la limpieza (flush),
+ lanzando una excepción si ocurrieron actualizaciones en conflicto.
+ </para>
+
+ <para>
+ Puedes también llamar a <literal>lock()</literal> en vez de <literal>update()</literal>
+ y usar <literal>LockMode.READ</literal> (realizando un chequeo de versión, puenteando
+ todos los cachés) si estás seguro que el objeto no ha sido modificado.
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-optimistic-customizing">
+ <title>Personalizando el versionado automático</title>
+
+ <para>
+ Puedes deshabilitar el incremento de versión automático de Hibernate para propiedades en particular
+ y colecciones estableciendo el atributo de mapeo <literal>optimistic-lock</literal> a
+ <literal>false</literal>. Hibernate entonces no incrementará ya más las versiones si la propiedad está
+ sucia.
+ </para>
+
+ <para>
+ Los esquemas de base de datos heredados son frecuentemente estáticos y no pueden ser modificados.
+ U otras aplicaciones podrían también acceder la misma base de datos y no saber cómo manejar los números
+ de versión ni incluso timestamps. En ambos casos, el versionado no puede confiarse a una columna en
+ particular en una tabla. Para forzar un chequeo de versiones sin un mapeo de propiedad de versión o
+ timestamp, con una comparación del estado de todos los campos en una fila, activa
+ <literal>optimistic-lock="all"</literal> en el mapeo de <literal><class></literal>.
+ Nota que esto conceptualmente funciona solamente si Hibernate puede comparar el estado viejo y nuevo,
+ es decir, si usas una sola <literal>Session</literal> larga y no
+ sesión-por-petición-con-instancias-separadas.
+ </para>
+
+ <para>
+ A veces las modificaciones concurrentes pueden permitirse, en cuanto los cambios que hayan sido
+ hechos no se traslapen. Si estableces <literal>optimistic-lock="dirty"</literal> al mapear la
+ <literal><class></literal>, Hibernate sólo comparará los campos sucios durante la limpieza.
+ </para>
+
+ <para>
+ En ambos casos, con columnas de versión/timestamp dedicadas o con comparación de campos
+ completa/sucios, Hibernate usa una sola sentencia <literal>UPDATE</literal>
+ (con una cláusula <literal>WHERE</literal> apropiada) por entidad para ejecutar el chequeo
+ de versiones y actualizar la información. Si usas persistencia transitiva para la re-unión
+ en cascada de entidades asociadas, Hibernate podría ejecutar actualizaciones innecesarias.
+ Esto usualmente no es un problema, pero podrían ejecutarse disparadores (triggers)
+ <emphasis>on update</emphasis> en la base de datos incluso cuando no se haya hecho ningún cambio
+ a las instancias separadas. Puedes personalizar este comportamiento estableciendo
+ <literal>select-before-update="true"</literal> en el mapeo de <literal><class></literal>,
+ forzando a Hibernate a <literal>SELECT</literal> la instancia para asegurar que las actualizaciones
+ realmente ocurran, antes de actualizar la fila.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="transactions-locking">
+ <title>Bloqueo pesimista</title>
+
+ <para>
+ No se pretende que los usuarios gasten mucho tiempo preocupándose de las estrategias de bloqueo.
+ Usualmente es suficiente con especificar un nivel de aislamiento para las conexiones JDBC y entonces
+ simplemente dejar que la base de datos haga todo el trabajo. Sin embargo, los usuarios avanzados pueden
+ a veces obtener bloqueos exclusivos pesimistas, o reobtener bloqueos al comienzo de una nueva
+ transacción.
+ </para>
+
+ <para>
+ ¡Hibernate siempre usará el mecanismo de bloqueo de la base de datos, nunca bloqueo
+ de objetos en memoria!
+ </para>
+
+ <para>
+ La clase <literal>LockMode</literal> define los diferentes niveles de bloqueo que pueden ser adquiridos
+ por Hibernate. Un bloqueo se obtiene por los siguientes mecanismos:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>LockMode.WRITE</literal> se adquiere automáticamente cuando Hibernate actualiza o
+ inserta una fila.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>LockMode.UPGRADE</literal> puede ser adquirido bajo petición explícita del usuario
+ usando <literal>SELECT ... FOR UPDATE</literal> en base de datos que soporten esa sintáxis.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>LockMode.UPGRADE_NOWAIT</literal> puede ser adquirido bajo petición explícita del usuario
+ usando un <literal>SELECT ... FOR UPDATE NOWAIT</literal> bajo Oracle.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>LockMode.READ</literal> es adquirido automáticamente cuando Hibernate lee datos
+ bajo los niveles de aislamiento Repeatable Read o Serializable. Puede ser readquirido por
+ pedido explícito del usuario.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>LockMode.NONE</literal> representa la ausencia de un bloqueo. Todos los objetos se pasan
+ a este modo de bloqueo al final de una <literal>Transaction</literal>. Los objetos asociados con una
+ sesión vía una llamada a <literal>update()</literal> o <literal>saveOrUpdate()</literal> también
+ comienzan en este modo de bloqueo.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ La "petición explícita del usuario" se expresa en una de las siguientes formas:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ Una llamada a <literal>Session.load()</literal>, especificando un <literal>LockMode</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Una llamada a <literal>Session.lock()</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Una llamada a <literal>Query.setLockMode()</literal>.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Si se llama a <literal>Session.load()</literal> con <literal>UPGRADE</literal> o
+ <literal>UPGRADE_NOWAIT</literal>, y el objeto pedido no ha sido aún cargado por la sesión, el objeto es
+ cargado usando <literal>SELECT ... FOR UPDATE</literal>. Si se llama a <literal>load()</literal> para
+ un objeto que ya esté cargado con un bloqueo menos restrictivo que el pedido, Hibernate llama a
+ <literal>lock()</literal> para ese objeto.
+ </para>
+
+ <para>
+ <literal>Session.lock()</literal> realiza un chequeo de número de versión si el modo de bloqueo especificado
+ es <literal>READ</literal>, <literal>UPGRADE</literal> o <literal>UPGRADE_NOWAIT</literal>. (En el caso de
+ <literal>UPGRADE</literal> o <literal>UPGRADE_NOWAIT</literal>, se usa
+ <literal>SELECT ... FOR UPDATE</literal>.)
+ </para>
+
+ <para>
+ Si la base de datos no soporta el modo de bloqueo solicitado, Hibernate usará un modo alternativo
+ apropiado (en vez de lanzar una excepción). Esto asegura que las aplicaciones serán portables.
+ </para>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/tutorial.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/tutorial.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/tutorial.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1270 @@
+<chapter id="tutorial">
+ <title>Introducción a Hibernate</title>
+
+ <sect1 id="tutorial-intro">
+ <title>Prefacio</title>
+
+ <para>
+ Este capítulo es un tutorial introductorio de Hibernate. Comenzamos con
+ una aplicación simple de línea de comandos usando un base de datos
+ en-memoria y desarrollándola en fácil para entender los pasos.
+ </para>
+
+ <para>
+ Este tutorial está concebido para usuarios nuevos de Hibernate pero
+ requiere conocimiento en Java y SQL. Está basado en un tutorial de
+ Michael Gloegl. Las bibliotecas de terceros que mencionamos son para JDK 1.4
+ y 5.0. Podrías necesitar otras para JDK 1.3.
+ </para>
+
+ </sect1>
+
+ <sect1 id="tutorial-firstapp">
+ <title>Parte 1 - La primera Aplicación Hibernate</title>
+
+ <para>
+ Primero, crearemos una aplicación simple de Hibenate basada en consola.
+ Usamos usamos una base de datos en-memoria (HSQL DB), de modo que no necesitamos
+ instalar ningún servidor de base de datos.
+ </para>
+
+ <para>
+ Asumamos que necesitamos una aplicación pequeña de base de datos que
+ pueda almacenar eventos que queremos atender, e información acerca de los
+ hostales de estos eventos.
+ </para>
+
+ <para>
+ La primera cosa que hacemos, es armar nuestro directorio de desarrollo y poner
+ en él todas las bibliotecas Java que necesitamos. Descarga la distribución
+ de Hibernate del sitio web de Hibernate. Extrae el paquete y coloca todas las
+ bibliotecas requeridas encontradas en <literal>/lib</literal> dentro del directorio
+ <literal>/lib</literal> de nuestro nuevo directorio de desarrollo de trabajo.
+ Debe asemejarse a esto:
+ </para>
+
+ <programlisting><![CDATA[.
++lib
+ antlr.jar
+ cglib-full.jar
+ asm.jar
+ asm-attrs.jars
+ commons-collections.jar
+ commons-logging.jar
+ ehcache.jar
+ hibernate3.jar
+ jta.jar
+ dom4j.jar
+ log4j.jar ]]></programlisting>
+
+ <para>
+ Este es el conjunto mínimo de bibliotecas requeridas para Hibernate (observa que
+ también hemos copiado hibernate3.jar, el fichero principal). Ver el fichero
+ <literal>README.txt</literal> en el directorio <literal>lib/</literal> de la distribución
+ de Hibernate para más información sobre bibliotecas de terceros requeridas y
+ opcionales. (Realmente, Log4J no es requerida aunque preferida por muchos desarrolladores).
+ </para>
+
+ <para>
+ Por siguiente, creamos una clase que represente el evento que queremos
+ almacenar en base de datos.
+ </para>
+
+ <sect2 id="tutorial-firstapp-firstclass">
+ <title>La primera clase</title>
+
+ <para>
+ Nuestra primera clase persistente es un JavaBean simple con algunas propiedades:
+ </para>
+
+ <programlisting><![CDATA[import java.util.Date;
+
+public class Event {
+ private Long id;
+
+ private String title;
+ private Date date;
+
+ Event() {}
+
+ public Long getId() {
+ return id;
+ }
+
+ private void setId(Long id) {
+ this.id = id;
+ }
+
+ public Date getDate() {
+ return date;
+ }
+
+ public void setDate(Date date) {
+ this.date = date;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+}]]></programlisting>
+
+ <para>
+ Puedes ver que esta clase usa las convenciones de nombrado estándar de JavaBean
+ para métodos getter y setter de propiedad, así como visibilidad privada
+ para los campos. Esto es un diseño recomendado, aunque no requerido. Hibernate
+ también puede acceder a los campos directamente; el beneficio de los métodos
+ de acceso es la robustez para la refactorización.
+ </para>
+
+ <para>
+ La propiedad <literal>id</literal> tiene un valor único de identificador para
+ un evento en particular. Todas las clase de entidad persistentes ( también hay
+ clases dependientes menos importantes) necesitarán una propiedad identificadora
+ similar si queremos usar el conjunto completo de funcionalidades de Hibernate. De hecho,
+ la mayoría de las aplicaciones (esp. aplicaciones web) necesitan distinguir
+ objetos por identificador, de modo que debes considerar esto como un aspecto en vez de una
+ limitación. Sin embargo, usualmente no manipulamos la identidad de un objeto, por
+ lo tanto el método setter debe ser privado. Sólo Hibernate asignará
+ identificadores cuando un objeto sea salvado. Puedes ver que Hibernate puede acceder a
+ métodos de acceso públicos, privados y protegidos, tanto como directamente a
+ campos (públicos, privados y protegidos). La elección está en ti,
+ y puedes ajustarla a tu diseño de aplicación.
+ </para>
+
+ <para>
+ El constructor sin argumentos es un requerimiento para todas las clases persistentes.
+ Hibernate tiene que crear objetos para ti, usando reflección Java. El constructor
+ puede ser privado, sin embargo, la visibilidad de paquete es requerida para la generación
+ de proxies en tiempo de ejecución y la recuperación de datos sin
+ instrumentación del bytecode.
+ </para>
+
+ <para>
+ Coloca este fichero de código Java en un directorio llamado <literal>src</literal>
+ en la carpeta de desarrollo. El directorio ahora debe verse como esto:
+ </para>
+
+ <programlisting><![CDATA[.
++lib
+ <Hibernate and third-party libraries>
++src
+ Event.java]]></programlisting>
+
+ <para>
+ En el próximo paso, le decimos a Hibernate sobre esta clase persistente.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-mapping">
+ <title>El fichero de mapeo</title>
+
+ <para>
+ Hibernate necesita saber cómo cargar y almacenar objetos de la
+ clase persistente. Aquí es donde el fichero de mapeo de Hibernate
+ entra en juego. El fichero de mapeo le dice a Hibernate a qué tabla en
+ la base de datos tiene que acceder, y qué columnas en esta tabla debe usar.
+ </para>
+
+ <para>
+ La estructura básica de un fichero de mapeo se parece a esto:
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping>
+[...]
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Observa que el DTD de Hibernate es muy sofisticado. Puedes usarlo para
+ autocompleción de los elementos y atributos XML de mapeo en tu
+ editor o IDE. Debes también abrir el fichero DTD en tu editor de
+ texto. Es la forma más fácil para tener un panorama de todos
+ los elementos y atributos y ver los valores por defectos, así como
+ algunos comentarios. Nota que Hibernate no cargará el fichero DTD de
+ la web, sino que primero buscará en el classpath de la aplicación.
+ El fichero DTD está incluído en <literal>hibernate3.jar</literal>
+ así como también en el directorio <literal>src/</literal> de la
+ distribución de Hibernate.
+ </para>
+
+ <para>
+ Omitiremos la declaración de DTD en futuros ejemplos para acortar
+ el código. Por supuesto, no es opcional.
+ </para>
+
+ <para>
+ Entre las dos etiquetas <literal>hibernate-mapping</literal>, incluye
+ un elemento <literal>class</literal>. Todas las clases de entidad
+ persistentes (de nuevo, podría haber más adelante clases
+ dependientes, que no sean entidades de-primera-clase) necesitan dicho mapeo
+ a una tabla en la base de datos SQL:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Event" table="EVENTS">
+
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Hasta ahora dijimos a Hibernate cómo persistir y cargar el objeto
+ de clase <literal>Event</literal> a la tabla <literal>EVENTS</literal>,
+ cada instancia representada por una fila en esta tabla. Ahora continuamos con
+ un mapeo de la propiedad de identificado único a la clave primaria
+ de la tabla. Además, como no queremos cuidar del manejo de este identificador,
+ configuramos la estrategia de generación de identificadores para una columna
+ clave primaria delegada:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Event" table="EVENTS">
+ <id name="id" column="EVENT_ID">
+ <generator class="increment"/>
+ </id>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ El elemento <literal>id</literal> el la declaración de la propiedad
+ identificadora, <literal>name="id"</literal> declara el nombre de la
+ propiedad Java. Hibernate usará los métodos getter y setter
+ para acceder a la propiedad. El attributo de columna dice a Hibernate cuál
+ columna de la tabla <literal>EVENTS</literal> usamos para esta clave primaria.
+ El elemento anidado <literal>generator</literal> especifica la estrategia de
+ generación de identificadores, en este caso usamos <literal>increment</literal>,
+ que es un método muy simple de incremento de número en-memoria
+ útil mayormente para testeo (y tutoriales). Hibernate también
+ soporta identificadores generados por base de datos, globalmente únicos,
+ así como también asignados por aplicación (o cualquier
+ estrategia para la que hayas escrito una extensión).
+ </para>
+
+ <para>
+ Finalmente incluímos declaraciones para las propiedades persistentes
+ de la clases en el fichero de mapeo. Por defecto, ninguna propiedad de la clase
+ se considera persistente:
+ </para>
+
+ <programlisting><![CDATA[
+<hibernate-mapping>
+
+ <class name="Event" table="EVENTS">
+ <id name="id" column="EVENT_ID">
+ <generator class="increment"/>
+ </id>
+ <property name="date" type="timestamp" column="EVENT_DATE"/>
+ <property name="title"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Al igual que con el elemento <literal>id</literal>, el atributo <literal>name</literal>
+ del elemento <literal>property</literal> dice a Hibernate cáles métodos
+ getter y setter usar.
+ </para>
+
+ <para>
+ ¿Por qué el mapeo de la propiedad <literal>date</literal>
+ incluye el atributo <literal>column</literal>, pero el de la de
+ <literal>title</literal> no? Sin el atributo <literal>column</literal>
+ Hibernate usa por defecto el nombre de propiedad como nombre de columna.
+ Esto funciona bien para <literal>title</literal>. Sin embargo,
+ However, <literal>date</literal> es una palabra reservada en la
+ mayoría de las bases de datos, así que mejor la mapeamos
+ a un nombre diferente.
+ </para>
+
+ <para>
+ La próxima cosa interesante es que el mapeo de <literal>title</literal>
+ carece de un atributo <literal>type</literal>. Los tipos que declaramos y usamos
+ en el fichero de mapeo no son, como podrías esperar, tipos de datos Java.
+ Tampoco son tipos de base de datos SQL. Estos tipos son los llamados así
+ <emphasis>Tipos de mapeo de Hibernate</emphasis>, convertidores que pueden
+ traducir de tipos Java a SQL y vice versa. De nuevo, Hibernate intentará
+ determinar la conversión y el mapeo mismo de tipo correctos si el atributo
+ <literal>type</literal> no estuviese presente en el mapeo. En algunos casos esta
+ detección automática (usando reflección en la clase Java)
+ puede no tener lo que esperas o necesitas. Este es el caso de la propiedad
+ <literal>date</literal>. Hibernate no puede saber is la propiedad mapeará
+ a una columna <literal>date</literal>, <literal>timestamp</literal> o
+ <literal>time</literal>. Declaramos que queremos preservar la información
+ completa de fecha y hora mapeando la propiedad con un <literal>timestamp</literal>.
+ </para>
+
+ <para>
+ Este fichero de mapeo debe ser salvado como <literal>Event.hbm.xml</literal>,
+ justo en el directorio próximo al fichero de código fuente de
+ la clase Java <literal>Event</literal>. El nombrado de los ficheros de mapeo
+ puede ser arbitrario, sin embargo, el sufijo <literal>hbm.xml</literal> se ha
+ vuelto una convención el la comunidad de desarrolladores de Hibernate.
+ La estructura de directorio debe ahora verse como esto:
+ </para>
+
+ <programlisting><![CDATA[.
++lib
+ <Hibernate and third-party libraries>
++src
+ Event.java
+ Event.hbm.xml]]></programlisting>
+
+ <para>
+ Continuamos con la configuración principal de Hibernate.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-configuration">
+ <title>Configuración de Hibernate</title>
+
+ <para>
+ Tenemos ahora una clase persistente y su fichero de mapeo en su sitio. Es momento de
+ configurar Hibernate. Antes que hagamos esto, necesitaremos una base de datos.
+ HSQL DB, un DBMS SQL en-memoria basado en Java, puede ser descargado del sitio web
+ de HSQL DB. Realmente, de esta descarga sólo necesitas el <literal>hsqldb.jar</literal>.
+ Coloca este fichero en el directorio <literal>lib/</literal> de la carpeta de desarrollo.
+ </para>
+
+ <para>
+ Crea un directorio llamado <literal>data</literal> en la raíz del directorio de
+ desarrollo. Allí es donde HSQL DB almacenará sus ficheros de datos.
+ </para>
+
+ <para>
+ Hibernate es la capa en tu aplicación que se conecta a esta base de datos,
+ de modo que necesita información de conexión. Las conexiones se hacen
+ a través de un pool de conexiones JDBC, que tambén tenemos que configurar.
+ La distribución de Hibernate contiene muchas herramientas de pooling de conexiones
+ JDBC de código abierto, pero para este tutorial usaremos el pool de conexiones
+ prefabricado dentro de Hibernate. Observa que tienes que copiar la biblioteca requerida
+ en tu classpath y usar diferentes configuraciones de pooling de conexiones si quieres
+ usar un software de pooling JDBC de terceros de calidad de producción.
+ </para>
+
+ <para>
+ Para la configuración de Hibernate, podemos usar un fichero
+ <literal>hibernate.properties</literal> simple, un fichero <literal>hibernate.cfg.xml</literal>
+ ligeramente más sofisticado, o incluso una configuración completamente
+ programática. La mayoría de los usuarios prefieren el fichero de
+ configuración XML:
+ </para>
+
+ <programlisting><![CDATA[<?xml version='1.0' encoding='utf-8'?>
+<!DOCTYPE hibernate-configuration PUBLIC
+ "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
+
+<hibernate-configuration>
+
+ <session-factory>
+
+ <!-- Database connection settings -->
+ <property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
+ <property name="connection.url">jdbc:hsqldb:data/tutorial</property>
+ <property name="connection.username">sa</property>
+ <property name="connection.password"></property>
+
+ <!-- JDBC connection pool (use the built-in) -->
+ <property name="connection.pool_size">1</property>
+
+ <!-- SQL dialect -->
+ <property name="dialect">org.hibernate.dialect.HSQLDialect</property>
+
+ <!-- Echo all executed SQL to stdout -->
+ <property name="show_sql">true</property>
+
+ <!-- Drop and re-create the database schema on startup -->
+ <property name="hbm2ddl.auto">create</property>
+
+ <mapping resource="Event.hbm.xml"/>
+
+ </session-factory>
+
+</hibernate-configuration>]]></programlisting>
+
+ <para>
+ Observa que esta configuración XML usa un DTD diferente.
+ Configuramos la <literal>SessionFactory</literal> de Hibernate, una
+ fábrica global responsable de una base de datos en particular.
+ Si tienes varias bases de datos, usa varias configuraciones
+ <literal><session-factory></literal> , usualmente en varios
+ ficheros de configuración (para un arranque más fácil).
+ </para>
+
+ <para>
+ Los primeros cuatro elementos <literal>property</literal> contienen la configuración
+ necesaria para la conexión JDBC. El elemento de dialecto <literal>property</literal>
+ especifica la variante de SQL en particular que genera Hibernate. La opción
+ <literal>hbm2ddl.auto</literal> activa la generación automática de esquemas
+ de base de datos, directamente en la base de datos. Esto, por supuesto, puede desactivarse
+ (quitando la opción config) o redirigido a un fichero con la ayuda de la tarea
+ de Ant <literal>SchemaExport</literal>. Finalmente, agregamos el(los) fichero(s) de mapeo
+ para clases persistentes.
+ </para>
+
+ <para>
+ Copia este fichero dentro del directorio de código fuente, de modo que
+ termine ubicado en la raiíz del classpath. Hibernate busca automáticamente
+ un fichero llamado <literal>hibernate.cfg.xml</literal> en la raíz del classpath
+ al arrancar.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-ant">
+ <title>Construyendo con Ant</title>
+
+ <para>
+ Construiremos ahora el tutorial con Ant. Necesitarás tener Ant instalado.
+ Obténlo de <ulink url="http://ant.apache.org/bindownload.cgi">Página
+ de descarga de Ant</ulink>. No se cubrirá aquí cómo instalar Ant.
+ Por favor refiérete al <ulink url="http://ant.apache.org/manual/index.html">
+ Manual de Ant</ulink>. Después que hayas instalado Ant, podemos comenzar a
+ crear el buildfile. Será llamado <literal>build.xml</literal> y colocado
+ directamente en el directorio de desarrollo.
+ </para>
+
+ <note>
+ <title>Reparar Ant</title>
+ <para>
+ Observa que la distribución de Ant está por defecto rota
+ (como se describe en el FAQ de Ant) y tiene que ser reparado por ti,
+ por ejemplo, si quisieras usar JUnit desde dentro de tu fichero de construcción.
+ Para hacer que funcione la tarea de JUnit (no lo necesitaremos en este tutorial),
+ copia junit.jar a <literal>ANT_HOME/lib</literal> o quita el trozo de plugin
+ <literal>ANT_HOME/lib/ant-junit.jar</literal>.
+ </para>
+ </note>
+
+ <para>
+ Un fichero de construcción básico se ve como esto:
+ </para>
+
+ <programlisting><![CDATA[<project name="hibernate-tutorial" default="compile">
+
+ <property name="sourcedir" value="${basedir}/src"/>
+ <property name="targetdir" value="${basedir}/bin"/>
+ <property name="librarydir" value="${basedir}/lib"/>
+
+ <path id="libraries">
+ <fileset dir="${librarydir}">
+ <include name="*.jar"/>
+ </fileset>
+ </path>
+
+ <target name="clean">
+ <delete dir="${targetdir}"/>
+ <mkdir dir="${targetdir}"/>
+ </target>
+
+ <target name="compile" depends="clean, copy-resources">
+ <javac srcdir="${sourcedir}"
+ destdir="${targetdir}"
+ classpathref="libraries"/>
+ </target>
+
+ <target name="copy-resources">
+ <copy todir="${targetdir}">
+ <fileset dir="${sourcedir}">
+ <exclude name="**/*.java"/>
+ </fileset>
+ </copy>
+ </target>
+
+</project>]]></programlisting>
+
+ <para>
+ Esto dirá a Ant que agregue todos los ficheros en el directorio lib que terminen con
+ <literal>.jar</literal> al classpath usado para la compilación. También copiará
+ todos los ficheros que no sean código Java al directorio objetivo, por ejemplo,
+ ficheros de configuración y mapeos de Hibernate. Si ahora corres Ant, debes obtener
+ esta salida:
+ </para>
+
+ <programlisting><![CDATA[C:\hibernateTutorial\>ant
+Buildfile: build.xml
+
+copy-resources:
+ [copy] Copying 2 files to C:\hibernateTutorial\bin
+
+compile:
+ [javac] Compiling 1 source file to C:\hibernateTutorial\bin
+
+BUILD SUCCESSFUL
+Total time: 1 second ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-helpers">
+ <title>Arranque y ayudantes</title>
+
+ <para>
+ Es momento de cargar y almacenar algunos objetos <literal>Event</literal>,
+ pero primero tenemos que completar la configuración de algún
+ código de infraestructura. Tenemos que arrancar Hibernate. Este
+ arranque incluye construir un objeto <literal>SessionFactory</literal> global
+ y almacenarlo en algún sitio de fácil acceso en el código
+ de aplicación. Una <literal>SessionFactory</literal> puede abrir nuevas
+ <literal>Session</literal>'s. Una <literal>Session</literal> representa un unidad
+ de trabajo mono-hebra. La <literal>SessionFactory</literal> es un objeto global
+ seguro entre hebras, instanciado una sola vez.
+ </para>
+
+ <para>
+ Crearemos una clase de ayuda <literal>HibernateUtil</literal> que cuide del
+ arranque y haga conveniente el manejo de <literal>Session</literal>.
+ El así llamado patrón <emphasis>Sesión de Hebra Local
+ (ThreadLocal Session)</emphasis> es útil aquí; mantenemos la unidad
+ de trabajo actual asociada a la hebra actual. Echemos una mirada a la implementación:
+ </para>
+
+ <programlisting><![CDATA[import org.hibernate.*;
+import org.hibernate.cfg.*;
+
+public class HibernateUtil {
+
+ public static final SessionFactory sessionFactory;
+
+ static {
+ try {
+ // Create the SessionFactory from hibernate.cfg.xml
+ sessionFactory = new Configuration().configure().buildSessionFactory();
+ } catch (Throwable ex) {
+ // Make sure you log the exception, as it might be swallowed
+ System.err.println("Initial SessionFactory creation failed." + ex);
+ throw new ExceptionInInitializerError(ex);
+ }
+ }
+
+ public static final ThreadLocal session = new ThreadLocal();
+
+ public static Session currentSession() throws HibernateException {
+ Session s = (Session) session.get();
+ // Open a new Session, if this thread has none yet
+ if (s == null) {
+ s = sessionFactory.openSession();
+ // Store it in the ThreadLocal variable
+ session.set(s);
+ }
+ return s;
+ }
+
+ public static void closeSession() throws HibernateException {
+ Session s = (Session) session.get();
+ if (s != null)
+ s.close();
+ session.set(null);
+ }
+}]]></programlisting>
+
+ <para>
+ Esta clase no ólo produce la <literal>SessionFactory</literal> global en
+ su inicializador static (llamado sólo una vez por la JVM al cargar la clase),
+ sino que también tiene una variable <literal>ThreadLocal</literal> para
+ tener la <literal>Session</literal> para la hebra actual. No importa cuándo
+ llames a <literal>HibernateUtil.currentSession()</literal>, siempre devolverá
+ la misma unidad de trabajo de Hibernate en la misma hebra. Una llamada a
+ <literal>HibernateUtil.closeSession()</literal> termina la unidad de trabajo actualmente
+ asociada a la hebra.
+ </para>
+
+ <para>
+ Asegúrate de entender el concepto Java de una variable local a una hebra antes
+ de usar esta ayuda. Una clase <literal>HibernateUtil</literal> más potente puede
+ encontrarse en <literal>CaveatEmptor</literal>, http://caveatemptor.hibernate.org/,
+ así como en el libro "Hibernate in Action". Observa que esta clase no es necesaria
+ si despliegas Hibernate en un servidor de aplicaciones J2EE: una <literal>Session</literal>
+ será automáticamente ligada a la transacción JTA actual, y puedes
+ buscar la <literal>SessionFactory</literal> a través de JNDI. Si usas JBoss AS,
+ Hibernate puede ser desplegado como un servicio de sistema manejado y automáticamente
+ ligará la <literal>SessionFactory</literal> a un nombre JNDI.
+ </para>
+
+ <para>
+ Coloca <literal>HibernateUtil.java</literal> en el directorio de fuentes de desarrollo,
+ junto a <literal>Event.java</literal>:
+ </para>
+
+ <programlisting><![CDATA[.
++lib
+ <Hibernate and third-party libraries>
++src
+ Event.java
+ Event.hbm.xml
+ HibernateUtil.java
+ hibernate.cfg.xml
++data
+build.xml]]></programlisting>
+
+ <para>
+ Esto también debe compilar sin problemas. Finalmente necesitamos configurar
+ un sistema de logging (registro). Hibernate usa commons logging y te deja la elección
+ entre Log4J y logging de JDK 1.4. La mayoría de los desarrolladores prefieren
+ Log4J: copia <literal>log4j.properties</literal> de la distribución de Hibernate
+ (está en el directorio <literal>etc/</literal>) a tu directorio <literal>src</literal>,
+ junto a <literal>hibernate.cfg.xml</literal>. Echa una mirada a la configuración de
+ ejemplo y cambia los ajustes si te gusta tener una salida más verborrágica.
+ Por defecto, sólo se muestra el mensaje de arranque de Hibernate en la salida.
+ </para>
+
+ <para>
+ La infraestructura del tutorial está completa, y estamos listos para hacer
+ algún trabajo real con Hibernate.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-workingpersistence">
+ <title>Cargando y almacenando objetos</title>
+
+ <para>
+ Finalmente, podemos usar Hibernate para cargar y almacenar objetos.
+ Escribimos una clase <literal>EventManager</literal> con un método
+ <literal>main()</literal>:
+ </para>
+
+ <programlisting><![CDATA[import org.hibernate.Transaction;
+import org.hibernate.Session;
+
+import java.util.Date;
+
+public class EventManager {
+
+ public static void main(String[] args) {
+ EventManager mgr = new EventManager();
+
+ if (args[0].equals("store")) {
+ mgr.createAndStoreEvent("My Event", new Date());
+ }
+
+ HibernateUtil.sessionFactory.close();
+ }
+
+}]]></programlisting>
+
+ <para>
+ Leemos algunos argumentos de la línea de comandos, y si el primer
+ argumento es "store", creamos y almacenamos un nuevo Event:
+ </para>
+
+ <programlisting><![CDATA[private void createAndStoreEvent(String title, Date theDate) {
+ Session session = HibernateUtil.currentSession();
+ Transaction tx = session.beginTransaction();
+
+ Event theEvent = new Event();
+ theEvent.setTitle(title);
+ theEvent.setDate(theDate);
+
+ session.save(theEvent);
+
+ tx.commit();
+ HibernateUtil.closeSession();
+}]]></programlisting>
+
+ <para>
+ Creamos un nuevo objeto <literal>Event</literal>, y se lo damos a Hibernate.
+ Hibernate cuida ahora del SQL y ejecuta <literal>INSERT</literal>s en la base
+ de datos. Echemos una mirada al código de manejo de <literal>Session</literal>
+ y <literal>Transaction</literal> antes de ejecutar esto.
+ </para>
+
+ <para>
+ Una <literal>Session</literal> es una sola unidad de trabajo. Podría sorprenderte
+ que tengamos una API adicional, <literal>Transaction</literal>. Esto implica que una unidad
+ de trabajo puede ser "más larga" que una sola transacción de base de datos;
+ imagina una unidad de trabajo que se abarca varios ciclos petición/respuesta HTTP
+ (por ejemplo, un diálogo asistente) en una aplicación web. Separar las
+ transacciones de base de datos de "las unidades de trabajo de la aplicación desde
+ el punto de vista del usuario" es uno de los conceptos básicos de diseño de
+ Hibernate. Llamamos una unidad de trabajo larga <emphasis>Transacción de
+ Aplicación</emphasis>, usualmente encapsulando varias transacciones de base de
+ datos más cortas. Por ahora mantendremos las cosas simples y asumiremos una
+ granularidad uno-a-uno entre una <literal>Session</literal> y una <literal>Transaction</literal>.
+ </para>
+
+ <para>
+ ¿Qué es lo que hacen <literal>Transaction.begin()</literal> y <literal>commit()</literal>?
+ ¿Dónde está el rollback en caso que algo vaya mal? La API de <literal>Transaction</literal>
+ de Hibernate es opcional realmente, pero la usamos por conveniencia y portabilidad. Si manejases
+ la transacción de base de datos por ti mismo (por ejemplo, llamando a
+ <literal>session.connection.commit()</literal>), ligarías el código a un entorno
+ de despliegue particular, en este JDBC directo no manejado. Estableciendo la fábrica
+ de <literal>Transaction</literal> en tu configuración de Hibernate puedes desplegar
+ tu capa de persistencia en cualquier sitio. Echa una mirada al <xref linkend="transactions"/>
+ para más información sobre manejo y demarcación de transacciones.
+ Hemos saltado también cualquier manejo de excepciones y rollback en este ejemplo.
+ </para>
+
+ <para>
+ Para ejecutar la primera rutina tenemos que agregar un objetivo llamable al fichero
+ de construcción de Ant:
+ </para>
+
+ <programlisting><![CDATA[<target name="run" depends="compile">
+ <java fork="true" classname="EventManager" classpathref="libraries">
+ <classpath path="${targetdir}"/>
+ <arg value="${action}"/>
+ </java>
+</target>]]></programlisting>
+
+ <para>
+ El valor del argumento <literal>action</literal> es establecido por línea de
+ comandos al llamar al objetivo:
+ </para>
+
+ <programlisting><![CDATA[C:\hibernateTutorial\>ant run -Daction=store]]></programlisting>
+
+ <para>
+ Debes ver, después de la compilación, a Hibernate arrancando y, dependiendo
+ de tu configuración mucha salida de registro (log). Al final encontrarás
+ la siguiente línea:
+ </para>
+
+ <programlisting><![CDATA[[java] Hibernate: insert into EVENTS (EVENT_DATE, title, EVENT_ID) values (?, ?, ?)]]></programlisting>
+
+ <para>
+ Esta es la <literal>INSERT</literal> ejecutada por Hibernate, los signos de preguntas
+ representan parámetros de ligado JDBC. Para ver los valores ligados como
+ argumentos, o para reducir la verborragia del registro, chequea tu
+ <literal>log4j.properties</literal>.
+ </para>
+
+ <para>
+ Ahora quisiéramos listar acontecimientos almacenados también,
+ así que agregamos una opción al método principal:
+ </para>
+
+ <programlisting><![CDATA[if (args[0].equals("store")) {
+ mgr.createAndStoreEvent("My Event", new Date());
+}
+else if (args[0].equals("list")) {
+ List events = mgr.listEvents();
+ for (int i = 0; i < events.size(); i++) {
+ Event theEvent = (Event) events.get(i);
+ System.out.println("Event: " + theEvent.getTitle() +
+ " Time: " + theEvent.getDate());
+ }
+}]]></programlisting>
+
+ <para>
+ Agregamos también un nuevo método <literal>listEvents()</literal>:
+ </para>
+
+ <programlisting><![CDATA[private List listEvents() {
+ Session session = HibernateUtil.currentSession();
+ Transaction tx = session.beginTransaction();
+
+ List result = session.createQuery("from Event").list();
+
+ tx.commit();
+ session.close();
+
+ return result;
+}]]></programlisting>
+
+ <para>
+ Lo que hacemos aquí es usar una consulta HQL (Lenguaje de Consulta de Hibernate
+ o Hibernate Query Language) para cargar todos los objetos <literal>Event</literal>
+ existentes de la base de datos. Hibernate generará el SQL apropiado, lo enviará
+ a la base de datosy poblará los objetos <literal>Event</literal> con datos.
+ Puedes, por supuesto, crear consultas más complejas con HQL.
+ </para>
+
+ <para>
+ Si ahora llamas a Ant con <literal>-Daction=list</literal>, debes ver los eventos
+ que has almacenado hasta ahora. Puede sorprenderte que esto no funcione, al menos
+ si has seguido este tutorial paso por paso; el resultado siempre estará
+ vacío. La razon de esto es la opción <literal>hbm2ddl.auto</literal>
+ en la configuración de Hibernate: Hibernate recreará la base de datos
+ en cada ejecución. Deshabilítala quitando la opción, y verás
+ resultados en tu listado después que llames a la acción <literal>store</literal>
+ unas cuantas veces. La generación y exportación de esquema es útil
+ mayormente en testeo unitario.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="tutorial-associations">
+ <title>Part 2 - Mapeando asociaciones</title>
+
+ <para>
+ Hemos mapeado un clase de entidad persistente a una tabla. Construyamos sobre esto y agreguemos
+ algunas asociaciones de clase. Primero agregaremos personas a nuestra aplicación,
+ y almacenaremos una lista de eventos en las que participan.
+ </para>
+
+ <sect2 id="tutorial-associations-mappinguser">
+ <title>Mapeando la clase Person</title>
+
+ <para>
+ El primer corte de la clase <literal>Person</literal> es simple:
+ </para>
+
+ <programlisting><![CDATA[public class Person {
+
+ private Long id;
+ private int age;
+ private String firstname;
+ private String lastname;
+
+ Person() {}
+
+ // Accessor methods for all properties, private setter for 'id'
+
+}]]></programlisting>
+
+ <para>
+ Crea un fichero de mapeo llamado <literal>Person.hbm.xml</literal>:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Person" table="PERSON">
+ <id name="id" column="PERSON_ID">
+ <generator class="increment"/>
+ </id>
+ <property name="age"/>
+ <property name="firstname"/>
+ <property name="lastname"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Finalmente, agrega el nuevo mapeo a la configuración de Hibernate:
+ </para>
+
+ <programlisting><![CDATA[ <mapping resource="Event.hbm.xml"/>
+ <mapping resource="Person.hbm.xml"/>
+]]></programlisting>
+
+ <para>
+ Crearemos ahora una asociación entre estas dos entidades. Obviamente,
+ las personas pueden participar en eventos, y los eventos tienen participantes.
+ Las cuestiones de diseño con que tenemos que tratar son: direccionalidad,
+ multiplicidad y comportamiento de colección.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-unidirset">
+ <title>Una asociación unidireccional basada en Set</title>
+
+ <para>
+ Agregaremos una colección de eventos a la clase <literal>Person</literal>.
+ De esta forma podemos navegar facilmente a los eventos de una persona en particular,
+ sin ejecutar una consulta explícita, llamando a <literal>aPerson.getEvents()</literal>.
+ Usamos una colección Java, un <literal>Set</literal>, porque la colección no
+ contendrá elementos duplicados y el ordenamiento no nos es relevante.
+ </para>
+
+ <para>
+ Hasta ahora hemos diseñado asociaciones unidireccionales multivaluadas, implementadas con un
+ <literal>Set</literal>. Escribamos el código para esto en las clases Java y luego lo
+ mapeemos:
+ </para>
+
+ <programlisting><![CDATA[public class Person {
+
+ private Set events = new HashSet();
+
+ public Set getEvents() {
+ return events;
+ }
+
+ public void setEvents(Set events) {
+ this.events = events;
+ }
+}]]></programlisting>
+
+ <para>
+ Antes que mapeemos esta asociación, piensa sobre el otro lado. Claramente, podemos
+ mantener esto solamente unidireccional. O podemos crear otra colección en el
+ <literal>Event</literal>, si queremos ser capaces de navegarlos bidireccionalmente;
+ por ejemplo, <literal>anEvent.getParticipants()</literal>. Esta es una elección
+ de diseño que recae en ti, pero lo que está claro de esta discusión
+ es la multiplicidad de la asociación: "multi" valuada a ambos lados, llamamos a esto
+ una asociación <emphasis>muchos-a-muchos</emphasis>. Por lo tanto, usamos un mapeo
+ many-to-many de Hibernate:
+ </para>
+
+ <programlisting><![CDATA[<class name="Person" table="PERSON">
+ <id name="id" column="PERSON_ID">
+ <generator class="increment"/>
+ </id>
+ <property name="age"/>
+ <property name="firstname"/>
+ <property name="lastname"/>
+
+ <set name="events" table="PERSON_EVENT">
+ <key column="PERSON_ID"/>
+ <many-to-many column="EVENT_ID" class="Event"/>
+ </set>
+
+</class>]]></programlisting>
+
+ <para>
+ Hibernate soporta todo tipo de mapeos de colección, siendo el más común
+ un <literal><set></literal>. Para una asociación muchos-a-muchos (o relación
+ de entidad <emphasis>n:m</emphasis>), se necesita una tabla de asociación. Cada fila en esta
+ tabla representa un enlace entre una persona y un evento. Esta tabla se configura con el atributo
+ <literal>table</literal> del elemento <literal>set</literal>. El nombre de la columna identificadora
+ en la asociación, para el lado de la persona, se define con el elemento
+ <literal><key></literal>. El nombre de columna para el lado del evento se define con el atributo
+ <literal>column</literal> del <literal><many-to-many></literal>. También tienes que decirle
+ a Hibernate la clase de los objetos en tu colección (correcto: la clase del otro lado de la
+ colección de referencias).
+ </para>
+
+ <para>
+ El esquema de base de datos para este mapeo es, por lo tanto:
+ </para>
+
+ <programlisting><![CDATA[
+ _____________ __________________
+ | | | | _____________
+ | EVENTS | | PERSON_EVENT | | |
+ |_____________| |__________________| | PERSON |
+ | | | | |_____________|
+ | *EVENT_ID | <--> | *EVENT_ID | | |
+ | EVENT_DATE | | *PERSON_ID | <--> | *PERSON_ID |
+ | TITLE | |__________________| | AGE |
+ |_____________| | FIRSTNAME |
+ | LASTNAME |
+ |_____________|
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-working">
+ <title>Trabajando la asociación</title>
+
+ <para>
+ Traigamos alguna gente y eventos juntos en un nuevo método en
+ <literal>EventManager</literal>:
+ </para>
+
+ <programlisting><![CDATA[private void addPersonToEvent(Long personId, Long eventId) {
+ Session session = HibernateUtil.currentSession();
+ Transaction tx = session.beginTransaction();
+
+ Person aPerson = (Person) session.load(Person.class, personId);
+ Event anEvent = (Event) session.load(Event.class, eventId);
+
+ aPerson.getEvents().add(anEvent);
+
+ tx.commit();
+ HibernateUtil.closeSession();
+}]]></programlisting>
+
+ <para>
+ Después de cargar una <literal>Person</literal> y un <literal>Event</literal>, simplemente
+ modifica la colección usando sus métodos normales. Como puedes ver, no hay una llamada
+ explícita a <literal>update()</literal> o <literal>save()</literal>. Hibernate detecta
+ automáticamente que la colección ha sido modificada y necesita ser salvada. Esto
+ es llamado <emphasis>chequeo sucio automótico (automatic dirty checking)</emphasis>, y
+ también puedes intentarlo modificando el nombre de la propiedad de fecha de cualquiera de tus
+ objetos. Mientras estén en estado <emphasis>persistente</emphasis>, esto es, ligados a una
+ <literal>Session</literal> de Hibernate particular (es decir, justo han sido cargados o almacenados
+ en una unidad de trabajo), Hibernate monitoriza cualquier cambio y ejecuta SQL en estilo
+ escribe-por-detrás. El proceso de sincronización del estado de memoria con la base
+ de datos, usualmente sólo al final de una unidad de trabajo,
+ es llamado <emphasis>limpieza (flushing)</emphasis>.
+ </para>
+
+ <para>
+ Podrías, por supuesto, cargar persona y evento en unidades de trabajo diferentes. O
+ modificas un objeto fuera de una <literal>Session</literal>, cuando no está en estado
+ persistente (si antes era persistente llamamos a este estado <emphasis>separado (detached)
+ </emphasis>). En código (no muy realista), esto se vería como sigue:
+ </para>
+
+ <programlisting><![CDATA[ private void addPersonToEvent(Long personId, Long eventId) {
+
+ Session session = HibernateUtil.currentSession();
+ Transaction tx = session.beginTransaction();
+
+ Person aPerson = (Person) session.load(Person.class, personId);
+ Event anEvent = (Event) session.load(Event.class, eventId);
+
+ tx.commit();
+ HibernateUtil.closeSession();
+
+ aPerson.getEvents().add(anEvent); // aPerson is detached
+
+ Session session2 = HibernateUtil.currentSession();
+ Transaction tx2 = session.beginTransaction();
+
+ session2.update(aPerson); // Reattachment of aPerson
+
+ tx2.commit();
+ HibernateUtil.closeSession();
+ }
+]]></programlisting>
+
+ <para>
+ La llamada a <literal>update</literal> hace a un objeto persistente de nuevo, podrías
+ decir que la liga a una nueva unidad de trabajo, de modo que cualquier modificación que
+ le hagas mientras esté separado puede ser salvada a base de datos.
+ </para>
+
+ <para>
+ Bueno, esto no es muy usado en nuestra situación actual, pero es un concepto
+ importante que puedes diseñar en tu propia aplicación. Por ahora, completa
+ este ejercicio agregando una nueva acción al método main de
+ <literal>EventManager</literal> y llámala desde la línea de comandos.
+ Si necesitas los identificadores de una persona o evento, el método
+ <literal>save()</literal> los devuelve.
+ </para>
+
+ <para>
+ Esto fue un ejemplo de una asociación entre dos clases igualmente importantes, dos entidades.
+ Como se ha mencionado anteriormente, hay otras clases y tipos en un modelo típico,
+ usualmente "menos importantes". Algunos ya los habrás visto, como un <literal>int</literal>
+ o un <literal>String</literal>. Llamamos a estas clases <emphasis>tipos de valor (value types)</emphasis>,
+ y sus instancias <emphasis>dependen</emphasis> de una entidad en particular. Las instancias de estos
+ tipos no tienen su propia identidad, ni son compartidas entre entidades (dos personas no referencian
+ el mismo objeto <literal>firstname</literal>, incluso si tuvieran el mismo primer nombre). Por supuesto,
+ los tipos de valor no sólo pueden encontrarse en el JDK (de hecho, en una aplicación
+ Hibernate todas las clases del JDK son consideradas tipos de valor), sino que además puedes
+ escribir por ti mismo clases dependientes, por ejemplo, <literal>Address</literal> o
+ <literal>MonetaryAmount</literal>.
+ </para>
+
+ <para>
+ También puedes diseñar una colección de tipos de valor. Esto es conceptualmente
+ muy diferente de una colección de referencias a otras entidades, pero se ve casi lo mismo en
+ Java.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-valuecollections">
+ <title>Colección de valores</title>
+
+ <para>
+ Agregamos una colección de objetos tipificados en valor a la entidad <literal>Person</literal>.
+ Queremos almacenar direcciones de email, de modo que el tipo que usamos es <literal>String</literal>,
+ y la colección es nuevamente un <literal>Set</literal>:
+ </para>
+ <programlisting><![CDATA[private Set emailAddresses = new HashSet();
+
+public Set getEmailAddresses() {
+ return emailAddresses;
+}
+
+public void setEmailAddresses(Set emailAddresses) {
+ this.emailAddresses = emailAddresses;
+}]]></programlisting>
+
+ <para>
+ El mapeo de este <literal>Set</literal>:
+ </para>
+
+ <programlisting><![CDATA[<set name="emailAddresses" table="PERSON_EMAIL_ADDR">
+ <key column="PERSON_ID"/>
+ <element type="string" column="EMAIL_ADDR"/>
+</set>]]></programlisting>
+
+ <para>
+ La diferencia comparada con el mapeo anterior es la parte <literal>element</literal>, que le dice
+ a Hibernate que la colección no contiene referencias a otra entidad, sino una colección
+ de elementos de tipo <literal>String</literal> (el nombre en minúsculas te dice que es un
+ tipo/conversor de mapeo de Hibernate). Una vez más, el atributo <literal>table</literal> del
+ elemento <literal>set</literal> determina el nombre de la tabla para la colección. El elemento
+ <literal>key</literal> define el nombre de la columna clave foránea en la tabla de colección.
+ El atributo <literal>column</literal> en el elemento <literal>element</literal> define el nombre de
+ columna donde realmente serán almacenados los valores <literal>String</literal>.
+ </para>
+
+ <para>
+ Echa una mirada al esquema actualizado:
+ </para>
+
+ <programlisting><![CDATA[
+ _____________ __________________
+ | | | | _____________
+ | EVENTS | | PERSON_EVENT | | | ___________________
+ |_____________| |__________________| | PERSON | | |
+ | | | | |_____________| | PERSON_EMAIL_ADDR |
+ | *EVENT_ID | <--> | *EVENT_ID | | | |___________________|
+ | EVENT_DATE | | *PERSON_ID | <--> | *PERSON_ID | <--> | *PERSON_ID |
+ | TITLE | |__________________| | AGE | | *EMAIL_ADDR |
+ |_____________| | FIRSTNAME | |___________________|
+ | LASTNAME |
+ |_____________|
+ ]]></programlisting>
+
+ <para>
+ Puedes ver que la clave primaria de la tabla de colección es de hecho una clave
+ compuesta, usando ambas columnas. Esto implica también que no pueden haber
+ direcciones de email duplicadas por persona, que es exactamente la semántica
+ que necesitamos para un conjunto en Java.
+ </para>
+
+ <para>
+ Puedes ahora intentar y agregar elementos a esta colección, al igual que
+ hicimos antes enlazando personas y eventos. Es el mismo código en Java.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-bidirectional">
+ <title>Asociaciones bidireccionales</title>
+
+ <para>
+ A continuacion vamos a mapear una asociación bidireccional, haciendo que la
+ asociación entre persona y evento funcione desde ambos lados en Java. Por supuesto,
+ el esquema de base de datos no cambia; todavía necesitamos multiplicidad muchos-a-muchos.
+ Una base de datos relacional es más flexible que un lenguaje de programación
+ de red, así que no necesita nada parecido a una dirección de navegación;
+ los datos pueden ser vistos y recuperados en cualquier forma posible.
+ </para>
+
+ <para>
+ Primero agrega una colección de participantes a la clase de eventos
+ <literal>Event</literal>:
+ </para>
+
+ <programlisting><![CDATA[private Set participants = new HashSet();
+
+public Set getParticipants() {
+ return participants;
+}
+
+public void setParticipants(Set participants) {
+ this.participants = participants;
+}]]></programlisting>
+
+ <para>
+ Ahora mapea este lado de la asociación también, en
+ <literal>Event.hbm.xml</literal>.
+ </para>
+
+ <programlisting><![CDATA[<set name="participants" table="PERSON_EVENT" inverse="true">
+ <key column="EVENT_ID"/>
+ <many-to-many column="PERSON_ID" class="Person"/>
+</set>]]></programlisting>
+
+ <para>
+ Como ves, estos son mapeos normales de <literal>set</literal> en ambos documentos de
+ mapeo. Nota que los nombres de columnas en <literal>key</literal> y
+ <literal>many-to-many</literal> fueron permutados en ambos documentos de mapeo. Aquí la
+ adición más importante es el atributo <literal>inverse="true"</literal> en el
+ elemento <literal>set</literal> del mapeo de colección de <literal>Event</literal>.
+ </para>
+
+ <para>
+ Lo que esto significa es que Hibernate debe tomar el otro lado - la clase <literal>Person</literal> -
+ cuando necesite descubrir información sobre el enlace entre las dos. Esto será mucho
+ más fácil de entender una vez que veas cómo se crea el enlace bidireccional
+ entre nuestras dos entidades.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-usingbidir">
+ <title>Trabajando enlaces bidireccionales</title>
+
+ <para>
+ Primero, ten en mente que Hhibernate no afecta la semántica normal de Java. ¿Cómo
+ hemos creado un enlace entre una <literal>Person</literal> y un <literal>Event</literal> en el
+ ejemplo unidireccional? Hemos agregado una instancia de <literal>Event</literal> a la colección
+ de referencias de eventos de una instancia de <literal>Person</literal>. De modo que, obviamente,
+ si queremos que este enlace funcione bidireccionalmente, tenemos que hacer lo mismo del otro lado,
+ agregando una referencia a <literal>Person</literal> a la colección en un <literal>Event</literal>.
+ Este "establecer el enlace a ambos lados" es absolutamente necesario y nunca debes olvidar hacerlo.
+ </para>
+
+ <para>
+ Muchos desarrolladores programan a la defensiva y crean métodos de
+ manejo de un enlace para establecer correctamente ambos lados, por ejemplo
+ en <literal>Person</literal>:
+ </para>
+
+ <programlisting><![CDATA[protected Set getEvents() {
+ return events;
+}
+
+protected void setEvents(Set events) {
+ this.events = events;
+}
+
+public void addToEvent(Event event) {
+ this.getEvents().add(event);
+ event.getParticipants().add(this);
+}
+
+public void removeFromEvent(Event event) {
+ this.getEvents().remove(event);
+ event.getParticipants().remove(this);
+}]]></programlisting>
+
+ <para>
+ Nota que los métodos get y set para esta colección son ahora protegidos. Esto le
+ permite a clases en el mismo paquete y a subclases acceder aún a los métodos, pero
+ previene a cualquier otro de ensuciarse con la colección directamente (bueno, casi).
+ Probablemente debas hacer lo mismo con la colección al otro lado.
+ </para>
+
+ <para>
+ Y ¿qué del atributo de mapeo <literal>inverse</literal>? Para ti, y para Java, un enlace
+ bidireccional es simplemente cuestión de establecer correctamente las referencias a ambos
+ lados. Hibernate, sin embargo, no tiene suficiente información para arreglar correctamente
+ sentencias <literal>INSERT</literal> y <literal>UPDATE</literal> de SQL (para evitar violación
+ de restricciones), y necesita alguna ayuda para manejar asociaciones bidireccionales apropiadamente.
+ El hacer un lado de la asociación <literal>inverse</literal> le dice a Hibernate que basicamente
+ lo ignore, que lo considere un <emphasis>espejo</emphasis> del otro lado. Esto es todo lo necesario para
+ que Hibernate resuelva todas las incidencias al transformar un modelo de navegación direccional a
+ un esquema SQL de base de datos. Las reglas que tienes que recordar son directas: Todas las asociaciones
+ bidireccionales necesitan uno de los lados como <literal>inverse</literal>. En una asociación
+ uno-a-muchos debe ser el lado-de-muchos. En una asociación muchos-a-muchos, puedes tomar cualquier
+ lado, no hay diferencia.
+ </para>
+<!--
+ <para>
+ In the next section we integrate Hibernate with Tomcat and WebWork - the <literal>EventManager</literal>
+ doesn't scale anymore with our growing application.
+ </para>
+-->
+ </sect2>
+ </sect1>
+
+ <sect1 id="tutorial-summary">
+ <title>Summary</title>
+
+ <para>
+ Este tutorial cubrió los fundamentos de escribir una simple aplicación independiente
+ de Hibernate.
+ </para>
+
+ <para>
+ Si ya te sientes confidente con Hibernate, continúa navegando a través de la
+ tabla de contenidos de la documentación de referencia para los temas que encuentres
+ interesantes. Los más consultados son procesamiento transaccional (<xref linkend="transactions"/>),
+ rendimiento de recuperación (<xref linkend="performance"/>), o el uso de la API
+ (<xref linkend="objectstate"/>) y las funcionalidades de consulta (<xref linkend="objectstate-querying"/>).
+ </para>
+
+ <para>
+ No olvides chequear el sitio web de Hibernate por más (especializados) tutoriales.
+ </para>
+
+ </sect1>
+
+</chapter>
\ No newline at end of file
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/xml.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/xml.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/es-ES/src/main/docbook/modules/xml.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,282 @@
+<chapter id="xml">
+ <title>Mapeo XML</title>
+
+ <para><emphasis>
+ Nota que esta es una funcionalidad experimental en Hibernate 3.0 y está
+ bajo un desarrollo extremadamente activo.
+ </emphasis></para>
+
+ <sect1 id="xml-intro" revision="1">
+ <title>Trabajando con datos XML</title>
+
+ <para>
+ Hibernate te permite trabajar con datos XML persistentes en casi la misma forma
+ que trabajas con POJOs persistentes. Un árbol XML analizado (parsed) puede ser
+ pensado como sólo otra forma de representar los datos relacionales a nivel de objetos,
+ en vez de POJOs.
+ </para>
+
+ <para>
+ Hibernate soporta dom4j como API para manipular árboles XML. Puedes escribir
+ consultas que traigan árboles dom4j de la base de datos y tener cualquier modificación
+ que hagas al árbol sincronizada automáticamente a la base de datos. Puedes incluso tomar
+ un documento XML, analizarlo usando dom4j, y escribirlo a la base de datos con cualquiera
+ de las operaciones básicas de Hibernate: <literal>persist(), saveOrUpdate(), merge(),
+ delete(), replicate()</literal> (la fusión no está aún soportada).
+ </para>
+
+ <para>
+ Esta funcionalidad tiene muchas aplicaciones incluyendo la importación/exportación de datos,
+ externalización de datos de entidad vÃa JMS o SOAP y reportes basados en XSLT.
+ </para>
+
+ <para>
+ Un solo mapeo puede ser usado para mapear simultáneamente las propiedades de una clase y los nodos de un
+ documento XML a la base de datos, o, si no hay ninguna clase a mapear, puede ser usado para mapear sólo
+ el XML.
+ </para>
+
+ <sect2 id="xml-intro-mapping">
+ <title>Especificando los mapeos de XML y de clase juntos</title>
+
+ <para>
+ He aquà un ejemplo de mapear un POJO y XML simultáneamente:
+ </para>
+
+ <programlisting><![CDATA[<class name="Account"
+ table="ACCOUNTS"
+ node="account">
+
+ <id name="accountId"
+ column="ACCOUNT_ID"
+ node="@id"/>
+
+ <many-to-one name="customer"
+ column="CUSTOMER_ID"
+ node="customer/@id"
+ embed-xml="false"/>
+
+ <property name="balance"
+ column="BALANCE"
+ node="balance"/>
+
+ ...
+
+</class>]]></programlisting>
+ </sect2>
+
+ <sect2 id="xml-onlyxml">
+ <title>Especificando sólo un mapeo XML</title>
+
+ <para>
+ He aquà un ejemplo donde no hay ninguna clase POJO:
+ </para>
+
+ <programlisting><![CDATA[<class entity-name="Account"
+ table="ACCOUNTS"
+ node="account">
+
+ <id name="id"
+ column="ACCOUNT_ID"
+ node="@id"
+ type="string"/>
+
+ <many-to-one name="customerId"
+ column="CUSTOMER_ID"
+ node="customer/@id"
+ embed-xml="false"
+ entity-name="Customer"/>
+
+ <property name="balance"
+ column="BALANCE"
+ node="balance"
+ type="big_decimal"/>
+
+ ...
+
+</class>]]></programlisting>
+
+ <para>
+ Este mapeo te permite acceder a los datos como un árbol dom4j, o como un grafo de pares nombre/valor de
+ propiedad (<literal>Map</literal>s de Java). Los nombres de propiedades son construcciones puramente
+ lógicas a las que se puede hacer referencia en consultas HQL.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="xml-mapping" revision="1">
+ <title>Mapeo de metadatos XML</title>
+
+ <para>
+ Muchos elementos de mapeo de Hibernate aceptan el atributo <literal>node</literal>. Esto te permite espcificar
+ el nombre de un atributo o elemento XML que contenga los datos de la propiedad o entidad. El formato del
+ atributo <literal>node</literal> debe ser uno de los siguientes:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para><literal>"element-name"</literal> - mapea al elemento XML mencionado</para>
+ </listitem>
+ <listitem>
+ <para><literal>"@attribute-name"</literal> - mapea al atributo XML mencionado</para>
+ </listitem>
+ <listitem>
+ <para><literal>"."</literal> - mapea al elemento padre</para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>"element-name/@attribute-name"</literal> -
+ mapea al atributo mencionado del elemento mencionado
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Para las colecciones y asociaciones monovaluadas, existe un atributo adicional <literal>embed-xml</literal>.
+ Si <literal>embed-xml="true"</literal>, que es el valor por defecto, el árbol XML para la entidad
+ asociada (o colección de tipo de valor) será embebida directamente en el árbol XML para la entidad que
+ posee la asociación. En otro caso, si <literal>embed-xml="false"</literal>, sólo el valor identificador
+ referenciado aparecerá en el XML para asociaciones de punto único y para las colecciones simplemente
+ no aparecerá en absoluto.
+ </para>
+
+ <para>
+ ¡Debes ser cuidadoso de no dejar <literal>embed-xml="true"</literal> para demasiadas asociaciones,
+ ya que XML no trata bien la circularidad!
+ </para>
+
+ <programlisting><![CDATA[<class name="Customer"
+ table="CUSTOMER"
+ node="customer">
+
+ <id name="id"
+ column="CUST_ID"
+ node="@id"/>
+
+ <map name="accounts"
+ node="."
+ embed-xml="true">
+ <key column="CUSTOMER_ID"
+ not-null="true"/>
+ <map-key column="SHORT_DESC"
+ node="@short-desc"
+ type="string"/>
+ <one-to-many entity-name="Account"
+ embed-xml="false"
+ node="account"/>
+ </map>
+
+ <component name="name"
+ node="name">
+ <property name="firstName"
+ node="first-name"/>
+ <property name="initial"
+ node="initial"/>
+ <property name="lastName"
+ node="last-name"/>
+ </component>
+
+ ...
+
+</class>]]></programlisting>
+
+ <para>
+ en este caso, hemos decidido embeber la colección de ids de cuenta, pero no los datos reales de cuenta.
+ La siguiente consulta HQL:
+ </para>
+
+ <programlisting><![CDATA[from Customer c left join fetch c.accounts where c.lastName like :lastName]]></programlisting>
+
+ <para>
+ devolverÃa conjuntos de datos como estos:
+ </para>
+
+ <programlisting><![CDATA[<customer id="123456789">
+ <account short-desc="Savings">987632567</account>
+ <account short-desc="Credit Card">985612323</account>
+ <name>
+ <first-name>Gavin</first-name>
+ <initial>A</initial>
+ <last-name>King</last-name>
+ </name>
+ ...
+</customer>]]></programlisting>
+
+ <para>
+ Si estableces <literal>embed-xml="true"</literal> en el mapeo <literal><one-to-many></literal>, los datos
+ podrÃan verse asÃ:
+ </para>
+
+ <programlisting><![CDATA[<customer id="123456789">
+ <account id="987632567" short-desc="Savings">
+ <customer id="123456789"/>
+ <balance>100.29</balance>
+ </account>
+ <account id="985612323" short-desc="Credit Card">
+ <customer id="123456789"/>
+ <balance>-2370.34</balance>
+ </account>
+ <name>
+ <first-name>Gavin</first-name>
+ <initial>A</initial>
+ <last-name>King</last-name>
+ </name>
+ ...
+</customer>]]></programlisting>
+
+ </sect1>
+
+
+ <sect1 id="xml-manipulation" revision="1">
+ <title>Manipulando datos XML</title>
+
+ <para>
+ Vamos a releer y actualizar documentos XML en la aplicación. Hacemos esto obteniendo una sesión dom4j:
+ </para>
+
+ <programlisting><![CDATA[Document doc = ....;
+
+Session session = factory.openSession();
+Session dom4jSession = session.getSession(EntityMode.DOM4J);
+Transaction tx = session.beginTransaction();
+
+List results = dom4jSession
+ .createQuery("from Customer c left join fetch c.accounts where c.lastName like :lastName")
+ .list();
+for ( int i=0; i<results.size(); i++ ) {
+ //add the customer data to the XML document
+ Element customer = (Element) results.get(i);
+ doc.add(customer);
+}
+
+tx.commit();
+session.close();]]></programlisting>
+
+ <programlisting><![CDATA[Session session = factory.openSession();
+Session dom4jSession = session.getSession(EntityMode.DOM4J);
+Transaction tx = session.beginTransaction();
+
+Element cust = (Element) dom4jSession.get("Customer", customerId);
+for ( int i=0; i<results.size(); i++ ) {
+ Element customer = (Element) results.get(i);
+ //change the customer name in the XML and database
+ Element name = customer.element("name");
+ name.element("first-name").setText(firstName);
+ name.element("initial").setText(initial);
+ name.element("last-name").setText(lastName);
+}
+
+tx.commit();
+session.close();]]></programlisting>
+
+ <para>
+ Es extremadamente útil combinar esta funcionalidad con la operación <literal>replicate()</literal>
+ de Hibernate para implementar la importación/exportación de datos basada en XML.
+ </para>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/css/html.css
===================================================================
--- trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/css/html.css (rev 0)
+++ trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/css/html.css 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,97 @@
+A {
+ color: #003399;
+}
+
+A:active {
+ color: #003399;
+}
+
+A:visited {
+ color: #888888;
+}
+
+P, OL, UL, LI, DL, DT, DD, BLOCKQUOTE {
+ color: #000000;
+}
+
+TD, TH, SPAN {
+ color: #000000;
+}
+
+BLOCKQUOTE {
+ margin-right: 0px;
+}
+
+
+H1, H2, H3, H4, H5, H6 {
+ color: #000000;
+ font-weight:500;
+ margin-top:10px;
+ padding-top:15px;
+}
+
+TABLE {
+ border-collapse: collapse;
+ border-spacing:0;
+ border: 1px thin black;
+ empty-cells: hide;
+}
+
+TD {
+ padding: 4pt;
+}
+
+H1 { font-size: 150%; }
+H2 { font-size: 140%; }
+H3 { font-size: 110%; font-weight: bold; }
+H4 { font-size: 110%; font-weight: bold;}
+H5 { font-size: 100%; font-style: italic; }
+H6 { font-size: 100%; font-style: italic; }
+
+TT {
+font-size: 90%;
+ font-family: "Courier New", Courier, monospace;
+ color: #000000;
+}
+
+PRE {
+font-size: 100%;
+ padding: 5px;
+ border-style: solid;
+ border-width: 1px;
+ border-color: #CCCCCC;
+ background-color: #F4F4F4;
+}
+
+UL, OL, LI {
+ list-style: disc;
+}
+
+HR {
+ width: 100%;
+ height: 1px;
+ background-color: #CCCCCC;
+ border-width: 0px;
+ padding: 0px;
+ color: #CCCCCC;
+}
+
+.variablelist {
+ padding-top: 10;
+ padding-bottom:10;
+ margin:0;
+}
+
+.itemizedlist, UL {
+ padding-top: 0;
+ padding-bottom:0;
+ margin:0;
+}
+
+.term {
+ font-weight:bold;
+}
+
+
+
+
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/AuthorWork.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/AuthorWork.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/AuthorWork.zargo
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/AuthorWork.zargo
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/CustomerOrderProduct.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/CustomerOrderProduct.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/CustomerOrderProduct.zargo
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/CustomerOrderProduct.zargo
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/EmployerEmployee.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/EmployerEmployee.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/EmployerEmployee.zargo
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/EmployerEmployee.zargo
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/full_cream.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/full_cream.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/full_cream.svg
===================================================================
--- trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/full_cream.svg (rev 0)
+++ trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/full_cream.svg 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,429 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"
+[
+ <!ATTLIST svg
+ xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink">
+]>
+<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
+<svg
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="354.331"
+ height="336.614"
+ id="svg1">
+ <defs
+ id="defs3">
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop128" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="1"
+ id="stop129" />
+ </linearGradient>
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient130"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ <radialGradient
+ cx="0.5"
+ cy="0.5"
+ fx="0.5"
+ fy="0.5"
+ r="0.5"
+ id="radialGradient131"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ </defs>
+ <g
+ transform="matrix(0.823795,0,0,0.823795,0.120302,5.25349)"
+ style="font-size:12;"
+ id="g659">
+ <rect
+ width="212.257"
+ height="57.2441"
+ x="17.9576"
+ y="100.132"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect137" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ transform="matrix(0.743454,0,0,0.482981,6.46949,52.2178)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect132" />
+ </g>
+ <rect
+ width="325.86"
+ height="63.6537"
+ x="17.4083"
+ y="15.194"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect136" />
+ <rect
+ width="325.86"
+ height="63.6537"
+ x="13.6713"
+ y="12.4966"
+ style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect126" />
+ <g
+ transform="matrix(1.14345,0,0,0.729078,-1.67818,105.325)"
+ style="font-size:12;"
+ id="g164">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="222.966"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect138" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="221.002"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect133" />
+ </g>
+ <text
+ x="170.824753"
+ y="58.402939"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text183">
+ <tspan
+ x="170.824997"
+ y="58.402901"
+ id="tspan360">
+Application</tspan>
+ </text>
+ <text
+ x="178.076340"
+ y="364.281433"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text197">
+ <tspan
+ x="178.076004"
+ y="364.281006"
+ id="tspan421">
+Database</tspan>
+ </text>
+ <text
+ x="68.605331"
+ y="138.524582"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text216">
+ <tspan
+ x="68.605301"
+ y="138.524994"
+ id="tspan384">
+SessionFactory</tspan>
+ </text>
+ <rect
+ width="67.0014"
+ height="101.35"
+ x="196.927"
+ y="89.2389"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect387" />
+ <rect
+ width="67.0014"
+ height="101.35"
+ x="194.633"
+ y="86.4389"
+ style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect388" />
+ <text
+ x="249.108841"
+ y="173.885559"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text389">
+ <tspan
+ x="249.108994"
+ y="173.886002"
+ id="tspan392">
+Session</tspan>
+ </text>
+ <rect
+ width="73.0355"
+ height="101.35"
+ x="270.995"
+ y="90.0018"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect395" />
+ <rect
+ width="73.0355"
+ height="101.35"
+ x="267.869"
+ y="87.2018"
+ style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect396" />
+ <text
+ x="328.593658"
+ y="174.715622"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text397">
+ <tspan
+ x="328.593994"
+ y="174.716003"
+ id="tspan563">
+Transaction</tspan>
+ </text>
+ <g
+ transform="matrix(0.29544,0,0,0.397877,9.70533,103.96)"
+ style="font-size:12;"
+ id="g565">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect566" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect567" />
+ </g>
+ <text
+ x="25.592752"
+ y="204.497803"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:10;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text568">
+ <tspan
+ x="25.592800"
+ y="204.498001"
+ id="tspan662">
+TransactionFactory</tspan>
+ </text>
+ <g
+ transform="matrix(0.298082,0,0,0.397877,99.6898,103.96)"
+ style="font-size:12;"
+ id="g573">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect574" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect575" />
+ </g>
+ <text
+ x="134.030670"
+ y="205.532791"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:10;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text576">
+ <tspan
+ x="134.031006"
+ y="205.533005"
+ id="tspan664">
+ConnectionProvider</tspan>
+ </text>
+ <g
+ transform="matrix(1.14345,0,0,0.729078,-1.67818,38.9539)"
+ style="font-size:12;"
+ id="g587">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="222.966"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect588" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="221.002"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect589" />
+ </g>
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="25.6196"
+ y="206.028"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect594" />
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="24.4229"
+ y="204.135"
+ style="font-size:12;fill:#b3b3b3;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect595" />
+ <text
+ x="85.575645"
+ y="282.300354"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text596">
+ <tspan
+ x="85.575600"
+ y="282.299988"
+ id="tspan607">
+JNDI</tspan>
+ </text>
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="236.937"
+ y="206.791"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect610" />
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="235.741"
+ y="204.898"
+ style="font-size:12;fill:#b3b3b3;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect611" />
+ <text
+ x="342.093201"
+ y="283.226410"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text612">
+ <tspan
+ x="342.092987"
+ y="283.226013"
+ id="tspan621">
+JTA</tspan>
+ </text>
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="130.134"
+ y="206.791"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect616" />
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="128.937"
+ y="204.898"
+ style="font-size:12;fill:#b3b3b3;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect617" />
+ <text
+ x="212.445343"
+ y="283.226410"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text618">
+ <tspan
+ x="212.445007"
+ y="283.226013"
+ id="tspan623">
+JDBC</tspan>
+ </text>
+ <g
+ transform="matrix(0.823795,0,0,0.823795,0.120302,6.19341)"
+ style="font-size:12;"
+ id="g637">
+ <g
+ transform="matrix(0.499515,0,0,0.415467,-0.237339,5.61339)"
+ id="g167">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect134" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect135" />
+ </g>
+ <text
+ x="33.749969"
+ y="50.589706"
+ style="font-size:11;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text188">
+ <tspan
+ x="33.750000"
+ y="50.589699"
+ id="tspan635">
+Transient Objects</tspan>
+ </text>
+ </g>
+ <g
+ transform="matrix(0.823795,0,0,0.823795,0.120302,5.25349)"
+ style="font-size:12;"
+ id="g644">
+ <g
+ transform="matrix(0.297486,0,0,0.516482,230.251,36.9178)"
+ id="g364">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect365" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect366" />
+ </g>
+ <text
+ x="277.123230"
+ y="85.155571"
+ style="font-size:11;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text367">
+ <tspan
+ x="277.122986"
+ y="85.155602"
+ id="tspan631">
+Persistent</tspan>
+ <tspan
+ x="277.122986"
+ y="96.155602"
+ id="tspan633">
+Objects</tspan>
+ </text>
+ </g>
+</svg>
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/hibernate_logo_a.png
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/hibernate_logo_a.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/lite.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/lite.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/lite.svg
===================================================================
--- trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/lite.svg (rev 0)
+++ trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/lite.svg 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,334 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"
+[
+ <!ATTLIST svg
+ xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink">
+]>
+<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
+<svg
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="318.898"
+ height="248.031"
+ id="svg1">
+ <defs
+ id="defs3">
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop128" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="1"
+ id="stop129" />
+ </linearGradient>
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient130"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ <radialGradient
+ cx="0.5"
+ cy="0.5"
+ fx="0.5"
+ fy="0.5"
+ r="0.5"
+ id="radialGradient131"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ </defs>
+ <rect
+ width="291.837"
+ height="57.0074"
+ x="17.3169"
+ y="18.646"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect136" />
+ <rect
+ width="291.837"
+ height="57.0074"
+ x="13.9703"
+ y="16.2302"
+ style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect126" />
+ <g
+ transform="matrix(0.326107,0,0,0.765831,9.59261,8.98517)"
+ style="font-size:12;"
+ id="g161">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect137" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect132" />
+ </g>
+ <g
+ transform="matrix(1.02406,0,0,0.652953,0.223384,39.9254)"
+ style="font-size:12;"
+ id="g164">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="222.966"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect138" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="221.002"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect133" />
+ </g>
+ <g
+ transform="matrix(0.449834,0,0,0.338463,-3.15909,9.73319)"
+ style="font-size:12;"
+ id="g167">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect134" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect135" />
+ </g>
+ <text
+ x="302.277679"
+ y="65.943230"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text183">
+ <tspan
+ x="302.277954"
+ y="65.943184"
+ id="tspan360">
+Application</tspan>
+ </text>
+ <text
+ x="36.235924"
+ y="63.796055"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:14;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text188">
+ <tspan
+ x="36.235950"
+ y="63.796051"
+ id="tspan427">
+Transient Objects</tspan>
+ </text>
+ <text
+ x="180.416245"
+ y="290.543701"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text197">
+ <tspan
+ x="180.415939"
+ y="290.543549"
+ id="tspan421">
+Database</tspan>
+ </text>
+ <text
+ x="25.037701"
+ y="179.154755"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text216">
+ <tspan
+ x="25.037655"
+ y="179.154648"
+ id="tspan384">
+SessionFactory</tspan>
+ </text>
+ <g
+ transform="matrix(0.252763,0,0,0.765831,109.104,8.98517)"
+ style="font-size:12;"
+ id="g386">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect387" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect388" />
+ </g>
+ <g
+ transform="matrix(0.297394,0,0,0.572692,101.502,21.6359)"
+ style="font-size:12;"
+ id="g364">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect365" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect366" />
+ </g>
+ <text
+ x="202.746506"
+ y="102.992203"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:14;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text367">
+ <tspan
+ x="202.746948"
+ y="102.992249"
+ id="tspan423">
+Persistent</tspan>
+ <tspan
+ x="202.746948"
+ y="116.992355"
+ id="tspan425">
+Objects</tspan>
+ </text>
+ <text
+ x="174.458496"
+ y="180.080795"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text389">
+ <tspan
+ x="174.458618"
+ y="180.080338"
+ id="tspan392">
+Session</tspan>
+ </text>
+ <g
+ transform="matrix(0.127369,0,0,0.765831,188.675,8.98517)"
+ style="font-size:12;"
+ id="g394">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect395" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect396" />
+ </g>
+ <text
+ x="260.413269"
+ y="179.154739"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text397">
+ <tspan
+ x="260.412964"
+ y="179.154343"
+ id="tspan400">
+JDBC</tspan>
+ </text>
+ <g
+ transform="matrix(0.127369,0,0,0.765831,229.156,8.98517)"
+ style="font-size:12;"
+ id="g405">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect406" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect407" />
+ </g>
+ <text
+ x="320.606903"
+ y="179.154739"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text408">
+ <tspan
+ x="320.606964"
+ y="179.154343"
+ id="tspan417">
+JNDI</tspan>
+ </text>
+ <g
+ transform="matrix(0.127369,0,0,0.765831,269.281,8.98517)"
+ style="font-size:12;"
+ id="g411">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect412" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect413" />
+ </g>
+ <text
+ x="377.096313"
+ y="179.154739"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text414">
+ <tspan
+ x="377.096008"
+ y="179.154999"
+ id="tspan145">
+JTA</tspan>
+ </text>
+</svg>
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/overview.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/overview.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/overview.svg
===================================================================
--- trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/overview.svg (rev 0)
+++ trunk/Hibernate3/documentation/manual/es-ES/src/main/resources/shared/images/overview.svg 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,250 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"
+[
+ <!ATTLIST svg
+ xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink">
+]>
+<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
+<svg
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="248.031"
+ height="248.031"
+ id="svg1">
+ <defs
+ id="defs3">
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop128" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="1"
+ id="stop129" />
+ </linearGradient>
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient130"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ <radialGradient
+ cx="0.5"
+ cy="0.5"
+ fx="0.5"
+ fy="0.5"
+ r="0.5"
+ id="radialGradient131"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ </defs>
+ <g
+ transform="matrix(0.771934,0,0,0.771934,4.36019,-3.02123)"
+ style="font-size:12;"
+ id="g158">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="17.3527"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect136" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="15.3883"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect126" />
+ </g>
+ <g
+ transform="matrix(0.771934,0,0,0.771934,4.36019,3.04452)"
+ style="font-size:12;"
+ id="g161">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect137" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect132" />
+ </g>
+ <g
+ transform="matrix(0.771934,0,0,0.771934,4.36019,8.0993)"
+ style="font-size:12;"
+ id="g164">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="222.966"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect138" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="221.002"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect133" />
+ </g>
+ <g
+ transform="matrix(0.771934,0,0,0.543505,2.59104,21.1103)"
+ style="font-size:12;"
+ id="g167">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect134" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect135" />
+ </g>
+ <text
+ x="105.392174"
+ y="56.568123"
+ transform="scale(0.771934,0.771934)"
+ style="font-size:24;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text183">
+ <tspan
+ x="105.392273"
+ y="56.568146"
+ id="tspan186">
+Application</tspan>
+ </text>
+ <text
+ x="81.820183"
+ y="103.149330"
+ transform="scale(0.771934,0.771934)"
+ style="font-size:20;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text188">
+ <tspan
+ x="81.820213"
+ y="103.149727"
+ id="tspan206">
+Persistent Objects</tspan>
+ </text>
+ <text
+ x="111.548180"
+ y="278.927887"
+ transform="scale(0.771934,0.771934)"
+ style="font-size:24;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text197">
+ <tspan
+ x="111.547874"
+ y="278.927551"
+ id="tspan200">
+Database</tspan>
+ </text>
+ <text
+ x="94.436180"
+ y="153.805740"
+ transform="scale(0.771934,0.771934)"
+ style="font-size:24;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text216">
+ <tspan
+ x="94.436180"
+ y="153.805740"
+ id="tspan221">
+HIBERNATE</tspan>
+ </text>
+ <g
+ transform="matrix(0.771934,0,0,0.771934,2.59083,1.02261)"
+ style="font-size:12;"
+ id="g254">
+ <g
+ transform="translate(4.58374,2.61928)"
+ id="g176">
+ <g
+ transform="matrix(0.571429,0,0,0.67347,-10.6174,117.093)"
+ id="g170">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect171" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect172" />
+ </g>
+ <g
+ transform="matrix(0.571429,0,0,0.67347,138.682,117.093)"
+ id="g173">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect174" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect175" />
+ </g>
+ </g>
+ <text
+ x="47.259438"
+ y="182.367538"
+ style="font-weight:bold;stroke-width:1pt;font-family:Courier;"
+ id="text191">
+ <tspan
+ x="47.259399"
+ y="182.367996"
+ id="tspan212">
+hibernate.</tspan>
+ <tspan
+ x="47.259399"
+ y="194.367996"
+ id="tspan214">
+properties</tspan>
+ </text>
+ <text
+ x="198.523010"
+ y="188.260941"
+ style="font-weight:normal;stroke-width:1pt;font-family:helvetica;"
+ id="text194">
+ <tspan
+ id="tspan195">
+XML Mapping</tspan>
+ </text>
+ </g>
+</svg>
Added: trunk/Hibernate3/documentation/manual/fr-FR/pom.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/fr-FR/pom.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/fr-FR/pom.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,21 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-manual</artifactId>
+ <version>3.3.0.beta1</version>
+ <relativePath>../../../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>hibernate-manual-${translation}</artifactId>
+ <packaging>pom</packaging>
+ <name>Hibernate Manual (${translation})</name>
+
+ <properties>
+ <translation>fr-FR</translation>
+ </properties>
+</project>
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/master.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/master.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/master.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,188 @@
+<?xml version='1.0' encoding="iso-8859-1"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3CR3//EN"
+ "../support/docbook-dtd/docbookx.dtd"
+[
+<!ENTITY tutorial SYSTEM "modules/tutorial.xml">
+<!ENTITY architecture SYSTEM "modules/architecture.xml">
+<!ENTITY configuration SYSTEM "modules/configuration.xml">
+<!ENTITY persistent-classes SYSTEM "modules/persistent_classes.xml">
+<!ENTITY basic-mapping SYSTEM "modules/basic_mapping.xml">
+<!ENTITY collection-mapping SYSTEM "modules/collection_mapping.xml">
+<!ENTITY association-mapping SYSTEM "modules/association_mapping.xml">
+<!ENTITY component-mapping SYSTEM "modules/component_mapping.xml">
+<!ENTITY inheritance-mapping SYSTEM "modules/inheritance_mapping.xml">
+<!ENTITY session-api SYSTEM "modules/session_api.xml">
+<!ENTITY transactions SYSTEM "modules/transactions.xml">
+<!ENTITY events SYSTEM "modules/events.xml">
+<!ENTITY batch SYSTEM "modules/batch.xml">
+<!ENTITY query-hql SYSTEM "modules/query_hql.xml">
+<!ENTITY query-criteria SYSTEM "modules/query_criteria.xml">
+<!ENTITY query-sql SYSTEM "modules/query_sql.xml">
+<!ENTITY filters SYSTEM "modules/filters.xml">
+<!ENTITY xml SYSTEM "modules/xml.xml">
+<!ENTITY performance SYSTEM "modules/performance.xml">
+<!ENTITY toolset-guide SYSTEM "modules/toolset_guide.xml">
+<!ENTITY example-parentchild SYSTEM "modules/example_parentchild.xml">
+<!ENTITY example-weblog SYSTEM "modules/example_weblog.xml">
+<!ENTITY example-mappings SYSTEM "modules/example_mappings.xml">
+<!ENTITY best-practices SYSTEM "modules/best_practices.xml">
+]>
+
+<book lang="fr">
+
+ <bookinfo>
+ <title>HIBERNATE - Persistance relationnelle en Java standard</title>
+ <subtitle>Documentation de référence d'Hibernate</subtitle>
+ <releaseinfo>3.3.0.beta1</releaseinfo>
+ </bookinfo>
+
+ <toc/>
+
+ <preface id="preface" revision="2">
+ <title>Préface</title>
+ <para>
+ Traducteur(s): Vincent Ricard, Sebastien Cesbron, Michael Courcy, Vincent Giguère, Baptiste Mathus, Emmanuel Bernard, Anthony Patricio
+ </para>
+ <para>
+ Travailler dans les deux univers que sont l'orienté objet et la base de données
+ relationnelle peut être lourd et consommateur en temps dans le monde de
+ l'entreprise d'aujourd'hui. Hibernate est un outil de mapping objet/relationnel
+ pour le monde Java. Le terme mapping objet/relationnel (ORM) décrit la technique
+ consistant à faire le lien entre la représentation objet des données
+ et sa représentation relationnelle basée sur un schéma SQL.
+ </para>
+
+ <para>
+ Non seulement, Hibernate s'occupe du transfert des classes Java dans les tables
+ de la base de données (et des types de données Java dans les types de données SQL),
+ mais il permet de requêter les données et propose des moyens de les récupérer.
+ Il peut donc réduire de manière significative le temps de développement qui
+ aurait été autrement perdu dans une manipulation manuelle des données via SQL
+ et JDBC.
+ </para>
+
+ <para>
+ Le but d'Hibernate est de libérer le développeur de 95 pourcent des tâches de
+ programmation liées à la persistance des données communes. Hibernate n'est
+ probablement pas la meilleure solution pour les applications centrées sur les
+ données qui n'utilisent que les procédures stockées pour implémenter la logique
+ métier dans la base de données, il est le plus utile dans les modèles métier orientés
+ objets dont la logique métier est implémentée dans la couche Java dite intermédiaire.
+ Cependant, Hibernate vous aidera à supprimer ou à encapsuler le code SQL
+ spécifique à votre base de données et vous aidera sur la tâche commune qu'est
+ la transformation des données d'une représentation tabulaire à une
+ représentation sous forme de graphe d'objets.
+ </para>
+
+ <para>
+ Si vous êtes nouveau dans Hibernate et le mapping Objet/Relationnel voire même en Java,
+ suivez ces quelques étapes :
+ </para>
+
+ <orderedlist>
+ <listitem>
+ <para>
+ Lisez <xref linkend="tutorial"/> pour un didacticiel plus long avec plus d'instructions étape par étape.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Lisez <xref linkend="architecture"/> pour comprendre les environnements dans lesquels
+ Hibernate peut être utilisé.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Regardez le répertoire <literal>eg</literal> de la distribution Hibernate, il contient
+ une application simple et autonome. Copiez votre pilote JDBC dans le répertoire
+ <literal>lib/</literal> et éditez <literal>src/hibernate.properties</literal>, en
+ positionnant correctement les valeurs pour votre base de données. A partir d'une
+ invite de commande dans le répertoire de la distribution, tapez <literal>ant eg</literal>
+ (cela utilise Ant), ou sous Windows tapez <literal>build eg</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Faîtes de cette documentation de référence votre principale source d'information.
+ Pensez à lire <emphasis>Hibernate in Action</emphasis>
+ (http://www.manning.com/bauer) si vous avez besoin de plus d'aide avec le design
+ d'applications ou si vous préférez un tutoriel pas à pas. Visitez aussi
+ http://caveatemptor.hibernate.org et téléchargez l'application exemple
+ pour Hibernate in Action.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Les questions les plus fréquemment posées (FAQs) trouvent leur réponse sur le
+ site web Hibernate.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Des démos, exemples et tutoriaux de tierces personnes sont référencés sur
+ le site web Hibernate.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ La zone communautaire (Community Area) du site web Hibernate est une
+ bonne source d'information sur les design patterns et sur différentes
+ solutions d'intégration d'Hibernate (Tomcat, JBoss, Spring Framework, Struts,
+ EJB, etc).
+ </para>
+ </listitem>
+ </orderedlist>
+
+ <para>
+ Si vous avez des questions, utilisez le forum utilisateurs du site web Hibernate.
+ Nous utilisons également l'outil de gestion des incidents JIRA pour tout ce qui
+ est rapports de bogue et demandes d'évolution. Si vous êtes intéressé par le
+ développement d'Hibernate, joignez-vous à la liste de diffusion de développement.
+ </para>
+
+ <para>
+ Le développement commercial, le support de production et les formations à Hibernate
+ sont proposés par JBoss Inc (voir http://www.hibernate.org/SupportTraining/). Hibernate
+ est un projet Open Source professionnel et un composant critique de la suite de produits
+ JBoss Enterprise Middleware System (JEMS).
+ </para>
+
+ </preface>
+
+ &tutorial;
+
+ &architecture;
+
+ &configuration;
+
+ &persistent-classes;
+
+ &basic-mapping;
+ &collection-mapping;
+ &association-mapping;
+ &component-mapping;
+ &inheritance-mapping;
+
+ &session-api;
+ &transactions;
+ &events;
+ &batch;
+
+ &query-hql;
+ &query-criteria;
+ &query-sql;
+ &filters;
+ &xml;
+
+ &performance;
+
+ &toolset-guide;
+
+ &example-parentchild;
+ &example-weblog;
+ &example-mappings;
+
+ &best-practices;
+
+</book>
+
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/architecture.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/architecture.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/architecture.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,351 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<chapter id="architecture">
+
+ <title>Architecture</title>
+
+ <sect1 id="architecture-overview" revision="1">
+ <title>Généralités</title>
+
+ <para>
+ Voici une vue (très) haut niveau de l'architecture d'Hibernate :
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/overview.svg" format="SVG" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/overview.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ Ce diagramme montre Hibernate utilisant une base de données et des données
+ de configuration pour fournir un service de persistance (et des objets
+ persistants) à l'application.
+ </para>
+
+ <para>
+ Nous aimerions décrire une vue plus détaillée de l'architecture. Malheureusement,
+ Hibernate est flexible et supporte différentes approches. Nous allons en
+ montrer les deux extrêmes. L'architecture légère laisse l'application fournir
+ ses propres connexions JDBC et gérer ses propres transactions. Cette approche
+ utilise le minimum des APIs Hibernate :
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/lite.svg" format="SVG" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/lite.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ L'architecture la plus complète abstrait l'application des APIs JDBC/JTA
+ sous-jacentes et laisse Hibernate s'occuper des détails.
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/full_cream.svg" format="SVG" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/full_cream.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ Voici quelques définitions des objets des diagrammes :
+
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term>SessionFactory (<literal>org.hibernate.SessionFactory</literal>)</term>
+ <listitem>
+ <para>
+ Un cache threadsafe (immuable) des mappings vers une (et une seule) base
+ de données. Une factory (fabrique) de <literal>Session</literal> et un client
+ de <literal>ConnectionProvider</literal>. Peut contenir un cache optionnel de
+ données (de second niveau) qui est réutilisable entre les différentes transactions
+ que cela soit au sein du même processus (JVLM) ou par plusieurs n½uds d'un cluster.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Session (<literal>org.hibernate.Session</literal>)</term>
+ <listitem>
+ <para>
+ Un objet mono-threadé, à durée de vie courte, qui représente une conversation
+ entre l'application et l'entrepôt de persistance. Encapsule une connexion JDBC.
+ Factory (fabrique) des objets <literal>Transaction</literal>. Contient un cache
+ (de premier niveau) des objets persistants, ce cache est obligatoire. Il est
+ utilisé lors de la navigation dans le graphe d'objets ou lors de la récupération
+ d'objets par leur identifiant.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Objets et Collections persistants</term>
+ <listitem>
+ <para>
+ Objets mono-threadés à vie courte contenant l'état de persistance
+ et la fonction métier. Ceux-ci sont en général les objets de type JavaBean
+ (ou POJOs) ; la seule particularité est qu'ils sont associés avec une (et
+ une seule) <literal>Session</literal>. Dès que la <literal>Session</literal>
+ est fermée, ils seront détachés et libres d'être utilisés par n'importe laquelle
+ des couches de l'application (ie. de et vers la présentation en tant que Data
+ Transfer Objects - DTO : objet de transfert de données).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Objets et collections transients</term>
+ <listitem>
+ <para>
+ Instances de classes persistantes qui ne sont actuellement pas associées à
+ une <literal>Session</literal>. Elles ont pu être instanciées par l'application
+ et ne pas avoir (encore) été persistées ou elle ont pu être instanciées par
+ une <literal>Session</literal> fermée.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Transaction (<literal>org.hibernate.Transaction</literal>)</term>
+ <listitem>
+ <para>
+ (Optionnel) Un objet mono-threadé à vie courte utilisé par l'application
+ pour définir une unité de travail atomique. Abstrait l'application des
+ transactions sous-jacentes qu'elles soient JDBC, JTA ou CORBA. Une
+ <literal>Session</literal> peut fournir plusieurs <literal>Transaction</literal>s
+ dans certains cas. Toutefois, la délimitation des transactions, via l'API d'Hibernate
+ ou par la <literal>Transaction</literal> sous-jacente, n'est jamais optionnelle!
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>ConnectionProvider (<literal>org.hibernate.connection.ConnectionProvider</literal>)</term>
+ <listitem>
+ <para>
+ (Optionnel) Une fabrique de (pool de) connexions JDBC. Abstrait l'application
+ de la <literal>Datasource</literal> ou du <literal>DriverManager</literal> sous-jacent.
+ Non exposé à l'application, mais peut être étendu/implémenté par le développeur.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>TransactionFactory (<literal>org.hibernate.TransactionFactory</literal>)</term>
+ <listitem>
+ <para>
+ (Optionnel) Une fabrique d'instances de <literal>Transaction</literal>. Non
+ exposé à l'application, mais peut être étendu/implémenté par le développeur.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><emphasis>Interfaces d'extension</emphasis></term>
+ <listitem>
+ <para>
+ Hibernate fournit de nombreuses interfaces d'extensions optionnelles que
+ vous pouvez implémenter pour personnaliser le comportement de votre couche de persistance.
+ Reportez vous à la documentation de l'API pour plus de détails.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ <para>
+ Dans une architecture légère, l'application n'aura pas à utiliser les APIs
+ <literal>Transaction</literal>/<literal>TransactionFactory</literal>
+ et/ou n'utilisera pas les APIs <literal>ConnectionProvider</literal>
+ pour utiliser JTA ou JDBC.
+ </para>
+ </sect1>
+
+ <sect1 id="architecture-states" revision="1">
+ <title>Etats des instances</title>
+ <para>
+ Une instance d'une classe persistante peut être dans l'un des trois états suivants,
+ définis par rapport à un <emphasis>contexte de persistance</emphasis>.
+ L'objet <literal>Session</literal> d'hibernate correspond à ce concept de
+ contexte de persistance :
+ </para>
+
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term>passager (transient)</term>
+ <listitem>
+ <para>
+ L'instance n'est pas et n'a jamais été associée à un contexte
+ de persistance. Elle ne possède pas d'identité persistante (valeur de clé primaire)
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>persistant</term>
+ <listitem>
+ <para>
+ L'instance est associée au contexte de persistance.
+ Elle possède une identité persistante (valeur de clé primaire)
+ et, peut-être, un enregistrement correspondant dans la base.
+ Pour un contexte de persistance particulier, Hibernate
+ <emphasis>garantit</emphasis> que l'identité persistante
+ est équivalente à l'identité Java (emplacement mémoire de l'objet)
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>détaché</term>
+ <listitem>
+ <para>
+ L'instance a été associée au contexte de persistance mais ce
+ contexte a été fermé, ou l'instance a été sérialisée vers un
+ autre processus. Elle possède une identité persistante et
+ peut-être un enregistrement correspondant dans la base.
+ Pour des instances détachées, Hibernate ne donne aucune
+ garantie sur la relation entre l'identité persistante et
+ l'identité Java.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </sect1>
+
+ <sect1 id="architecture-jmx" revision="1">
+ <title>Intégration JMX</title>
+ <para>
+ JMX est le standard J2EE de gestion des composants Java.
+ Hibernate peut être géré via un service JMX standard. Nous fournissons une implémentation
+ d'un MBean dans la distribution : <literal>org.hibernate.jmx.HibernateService</literal>.
+ </para>
+
+ <para>
+ Pour avoir un exemple sur la manière de déployer Hibernate en tant que service JMX dans le
+ serveur d'application JBoss Application Server, référez vous au guide utilisateur JBoss (JBoss User Guide).
+ Si vous déployez Hibernate via JMX sur JBoss AS, vous aurez également les bénéfices suivants :
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Gestion de la session :</emphasis> Le cycle de vie de la <literal>Session</literal>
+ Hibernate peut être automatiquement limitée à la portée d'une transaction JTA.
+ Cela signifie que vous n'avez plus besoin d'ouvrir et de fermer la <literal>Session</literal>
+ manuellement, cela devient le travail de l'intercepteur EJB de JBoss. Vous n'avez
+ pas non plus à vous occuper des démarcations des transactions dans votre code (sauf
+ si vous voulez écrire une couche de persistance qui soit portable, dans ce cas vous
+ pouvez utiliser l'API optionnelle <literal>Transaction</literal> d'Hibernate).
+ Vous appelez l'<literal>HibernateContext</literal> pour accéder à la <literal>Session</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Déploiement HAR :</emphasis> Habituellement vous déployez le service JMX
+ Hibernate en utilisant le descripteur de déploiement de JBoss (dans un fichier EAR et/ou un SAR),
+ il supporte toutes les options de configuration usuelles d'une <literal>SessionFactory</literal>
+ Hibernate. Cependant, vous devez toujours nommer tous vos fichiers de mapping dans le
+ descripteur de déploiement. Si vous décidez d'utiliser le déploiement optionnel sous forme
+ de HAR, JBoss détectera automatiquement tous vos fichiers de mapping dans votre fichier HAR.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Consultez le guide d'utilisation de JBoss AS pour plus d'informations sur ces options.
+ </para>
+
+ <para>
+ Les statistiques pendant l'exécution d'Hibernate (au runtime) sont une
+ autre fonctionnalité disponible en tant que service JMX. Voyez pour cela
+ <xref linkend="configuration-optional-statistics"/>.
+ </para>
+ </sect1>
+
+ <sect1 id="architecture-jca" revision="1">
+ <title>Support JCA</title>
+ <para>
+ Hibernate peut aussi être configuré en tant que connecteur JCA. Référez-vous au site
+ web pour de plus amples détails. Il est important de noter que le support JCA d'Hibernate
+ est encore considéré comme expérimental.
+ </para>
+ </sect1>
+
+ <sect1 id="architecture-current-session" revision="1">
+ <title>Sessions Contextuelles</title>
+ <para>
+ Certaines applications utilisant Hibernate ont besoin d'une sorte de session "contextuelle", où
+ une session est liée à la portée d'un contexte particulier. Cependant, les applications ne définissent
+ pas toutes la notion de contexte de la même manière, et différents contextes définissent différentes
+ portées à la notion de "courant". Les applications à base d'Hibernate, versions précédentes à la 3.0
+ utilisaient généralement un principe maison de sessions contextuelles basées sur le <literal>ThreadLocal</literal>,
+ ainsi que sur des classes utilitaires comme <literal>HibernateUtil</literal>, ou utilisaient des
+ framework tiers (comme Spring ou Pico) qui fournissaient des sessions contextuelles basées sur
+ l'utilisation de proxy/interception.
+ </para>
+ <para>
+ A partir de la version 3.0.1, Hibernate a ajouté la méthode <literal>SessionFactory.getCurrentSession()</literal>.
+ Initialement, cela demandait l'usage de transactions <literal>JTA</literal>, où la
+ transaction <literal>JTA</literal> définissait la portée et le contexte de la session courante.
+ L'équipe Hibernate pense que, étant donnée la maturité des implémentations de <literal>JTA TransactionManager</literal> ,
+ la plupart (sinon toutes) des applications devraient utiliser la gestion des transactions par <literal>JTA</literal>
+ qu'elles soient ou non déployées dans un conteneur <literal>J2EE</literal>. Par conséquent,
+ vous devriez toujours contextualiser vos sessions, si vous en avez besoin, via la méthode basée sur JTA.
+ </para>
+ <para>
+ Cependant, depuis la version 3.1, la logique derrière
+ <literal>SessionFactory.getCurrentSession()</literal> est désormais branchable.
+ A cette fin, une nouvelle interface d'extension (<literal>org.hibernate.context.CurrentSessionContext</literal>)
+ et un nouveau paramètre de configuration (<literal>hibernate.current_session_context_class</literal>)
+ ont été ajoutés pour permettre de configurer d'autres moyens de définir la portée et le contexte des
+ sessions courantes.
+ </para>
+ <para>
+ Allez voir les Javadocs de l'interface <literal>org.hibernate.context.CurrentSessionContext</literal>
+ pour une description détaillée de son contrat. Elle définit une seule méthode,
+ <literal>currentSession()</literal>, depuis laquelle l'implémentation est responsable
+ de traquer la session courante du contexte. Hibernate fournit deux implémentation
+ de cette interface.
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>org.hibernate.context.JTASessionContext</literal> - les sessions courantes sont
+ associées à une transaction <literal>JTA</literal>. La logique est la même que
+ l'ancienne approche basée sur JTA. Voir les javadocs pour les détails.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>org.hibernate.context.ThreadLocalSessionContext</literal> - les sessions
+ courantes sont associées au thread d'exécution. Voir les javadocs pour les détails.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Les deux implémentations fournissent un modèle de programmation de type "une session - une transaction
+ à la base de données", aussi connu sous le nom de <emphasis>session-per-request</emphasis>.
+ Le début et la fin d'une session Hibernate sont définis par la durée d'une transaction de base de données.
+ Si vous utilisez une démarcation programmatique de la transaction (par exemple sous J2SE ou JTA/UserTransaction/BMT),
+ nous vous conseillons d'utiliser l'API Hibernate <literal>Transaction</literal> pour masquer le système
+ de transaction utilisé. Si vous exécutez sous un conteneur EJB qui supporte CMT, vous n'avez besoin d'aucune
+ opérations de démarcations de session ou transaction dans votre code puisque tout
+ est géré de manière déclarative. Référez vous à <xref linkend="transactions"/> pour plus d'informations
+ et des exemples de code.
+ </para>
+
+ <para>
+ Le paramètre de configuration <literal>hibernate.current_session_context_class</literal>
+ définit quelle implémentation de <literal>org.hibernate.context.CurrentSessionContext</literal>
+ doit être utilisée. Notez que pour assurer la compatibilité avec les versions précédentes, si
+ ce paramètre n'est pas défini mais qu'un <literal>org.hibernate.transaction.TransactionManagerLookup</literal>
+ est configuré, Hibernate utilisera le <literal>org.hibernate.context.JTASessionContext</literal>.
+ La valeur de ce paramètre devrait juste nommer la classe d'implémentation à utiliser,
+ pour les deux implémentations fournies, il y a cependant deux alias correspondant: "jta" et "thread".
+ </para>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/association_mapping.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/association_mapping.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/association_mapping.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,623 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<chapter id="associations">
+
+ <title>Mapper les associations</title>
+
+ <sect1 id="assoc-intro" revision="1">
+ <title>Introduction</title>
+
+ <para>
+ Correctement mapper les associations est souvent la tâche la plus difficile.
+ Dans cette section nous traiterons les cas classiques les uns après les autres.
+ Nous commencerons d'abbord par les mappings unidirectionnels, puis nous aborderons
+ la question des mappings bidirectionnels. Nous illustrerons tous nos exemples
+ avec les classes <literal>Person</literal> et <literal>Address</literal>.
+ </para>
+
+ <para>
+ Nous utiliserons deux critères pour classer les associations : le premier
+ sera de savoir si l'association est bâti sur une table supplémentaire d'association
+ et le deuxieme sera basé sur la multiplicité de cette association.
+ </para>
+
+ <para>
+ Autoriser une clé étrangère nulle est considéré comme un mauvais choix dans
+ la construction d'un modèle de données. Nous supposerons donc que dans tous
+ les exemples qui vont suivre on aura interdit la valeur nulle pour les clés
+ étrangères. Attention, ceci ne veut pas dire que Hibernate ne supporte pas
+ les clés étrangères pouvant prendre des valeurs nulles, les exemples qui suivent
+ continueront de fonctionner si vous décidiez ne plus imposer la contrainte
+ de non-nullité sur les clés étrangères.
+ </para>
+
+ </sect1>
+
+ <sect1 id="assoc-unidirectional" revision="1">
+ <title>Association unidirectionnelle</title>
+
+ <sect2 id="assoc-unidirectional-m21" >
+ <title>plusieurs à un</title>
+
+ <para>
+ Une <emphasis>association plusieurs-à-un (many-to-one) unidirectionnelle </emphasis>
+ est le type que l'on rencontre le plus souvent dans les associations unidirectionnelles.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key, addressId bigint not null )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-121">
+ <title>un à un</title>
+
+ <para>
+ une <emphasis>association un-à-un (one-to-one) sur une clé étrangère</emphasis>
+ est presque identique. La seule différence est sur la contrainte d'unicité que
+ l'on impose à cette colonne.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <many-to-one name="address"
+ column="addressId"
+ unique="true"
+ not-null="true"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key, addressId bigint not null unique )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ <para>
+ Une <emphasis>association un-à-un (one-to-one) unidirectionnelle sur une clé primaire</emphasis>
+ utilise un générateur d'identifiant particulier. (Remarquez que nous avons inversé le sens de cette
+ association dans cet exemple.)
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+</class>
+
+<class name="Address">
+ <id name="id" column="personId">
+ <generator class="foreign">
+ <param name="property">person</param>
+ </generator>
+ </id>
+ <one-to-one name="person" constrained="true"/>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table Address ( personId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-12m">
+ <title>un à plusieurs</title>
+
+ <para>
+ Une <emphasis>association un-à-plusieurs (one-to-many) unidirectionnelle sur une
+ clé étrangère</emphasis> est vraiment inhabituelle, et n'est pas vraiment recommandée.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses">
+ <key column="personId"
+ not-null="true"/>
+ <one-to-many class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table Address ( addressId bigint not null primary key, personId bigint not null )
+ ]]></programlisting>
+
+ <para>
+ Nous pensons qu'il est préférable d'utiliser une table de jointure pour ce type d'association.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="assoc-unidirectional-join" revision="1">
+ <title>Associations unidirectionnelles avec tables de jointure</title>
+
+ <sect2 id="assoc-unidirectional-join-12m">
+ <title>un à plusieurs</title>
+
+ <para>
+ Une <emphasis>association unidirectionnelle un-à-plusieurs (one-to-many) avec
+ une table de jointure</emphasis> est un bien meilleur choix.
+ Remarquez qu'en spécifiant <literal>unique="true"</literal>,
+ on a changé la multiplicité plusieurs-à-plusieurs (many-to-many) pour
+ un-à-plusieurs (one-to-many).
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses" table="PersonAddress">
+ <key column="personId"/>
+ <many-to-many column="addressId"
+ unique="true"
+ class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId not null, addressId bigint not null primary key )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-join-m21">
+ <title>plusieurs à un</title>
+
+ <para>
+ Une <emphasis>assiociation plusieurs-à-un (many-to-one) unidirectionnelle sur
+ une table de jointure</emphasis> est très fréquente quand l'association est optionnelle.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ optional="true">
+ <key column="personId" unique="true"/>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"/>
+ </join>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null primary key, addressId bigint not null )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-join-121">
+ <title>un à un</title>
+
+ <para>
+ Une <emphasis>association unidirectionnelle un-à-un (one-to-one) sur une table
+ de jointure</emphasis> est extrèmement rare mais envisageable.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ optional="true">
+ <key column="personId"
+ unique="true"/>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"
+ unique="true"/>
+ </join>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-join-m2m">
+ <title>plusieurs à plusieurs</title>
+
+ <para>
+ Finallement, nous avons <emphasis>l'association unidirectionnelle plusieurs-à-plusieurs (many-to-many)</emphasis>.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses" table="PersonAddress">
+ <key column="personId"/>
+ <many-to-many column="addressId"
+ class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personId, addressId) )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="assoc-bidirectional" revision="1">
+ <title>Associations bidirectionnelles</title>
+
+ <sect2 id="assoc-bidirectional-m21" revision="2">
+ <title>un à plusieurs / plusieurs à un</title>
+
+ <para>
+ Une <emphasis>association bidirectionnelle plusieurs à un (many-to-one)</emphasis>
+ est le type d'association que l'on rencontre le plus souvent. (c'est la façon standard de créer
+ des relations parents/enfants.)
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <set name="people" inverse="true">
+ <key column="addressId"/>
+ <one-to-many class="Person"/>
+ </set>
+</class>]]></programlisting>
+
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key, addressId bigint not null )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ <para>
+ Si vous utilisez une <literal>List</literal> (ou toute autre collection indexée) vous devez
+ paramétrer la colonne <literal>key</literal> de la clé étrangère à <literal>not null</literal>,
+ et laisser Hibernate gérer l'association depuis l'extrémité collection pour maintenir l'index
+ de chaque élément (rendant l'autre extrémité virtuellement inverse en paramétrant
+ <literal>update="false"</literal> et <literal>insert="false"</literal>):
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id"/>
+ ...
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"
+ insert="false"
+ update="false"/>
+</class>
+
+<class name="Address">
+ <id name="id"/>
+ ...
+ <list name="people">
+ <key column="addressId" not-null="true"/>
+ <list-index column="peopleIdx"/>
+ <one-to-many class="Person"/>
+ </list>
+</class>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-bidirectional-121">
+ <title>Un à un</title>
+
+ <para>
+ Une <emphasis>association bidirectionnelle un à un (one-to-one) sur une clé étrangère</emphasis>
+ est aussi très fréquente.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <many-to-one name="address"
+ column="addressId"
+ unique="true"
+ not-null="true"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <one-to-one name="person"
+ property-ref="address"/>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key, addressId bigint not null unique )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ <para>
+ Une <emphasis>association bidirectionnelle un-à-un (one-to-one) sur une clé primaire</emphasis>
+ utilise un générateur particulier d'id.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <one-to-one name="address"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="personId">
+ <generator class="foreign">
+ <param name="property">person</param>
+ </generator>
+ </id>
+ <one-to-one name="person"
+ constrained="true"/>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table Address ( personId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="assoc-bidirectional-join" revision="1">
+ <title>Associations bidirectionnelles avec table de jointure</title>
+
+ <sect2 id="assoc-bidirectional-join-12m">
+ <title>un à plusieurs / plusieurs à un</title>
+
+ <para>
+ Une <emphasis>association bidirectionnelle un-à-plusieurs (one-to-many) sur une table de jointure </emphasis>.
+ Remarquez que <literal>inverse="true"</literal> peut s'appliquer sur les deux extrémités de l'
+ association, sur la collection, ou sur la jointure.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses"
+ table="PersonAddress">
+ <key column="personId"/>
+ <many-to-many column="addressId"
+ unique="true"
+ class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ inverse="true"
+ optional="true">
+ <key column="addressId"/>
+ <many-to-one name="person"
+ column="personId"
+ not-null="true"/>
+ </join>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null, addressId bigint not null primary key )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-bidirectional-join-121">
+ <title>Un à un</title>
+
+ <para>
+ Une <emphasis>association bidirectionnelle un-à-un (one-to-one) sur une table de jointure</emphasis>
+ est extrèmement rare mais envisageable.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ optional="true">
+ <key column="personId"
+ unique="true"/>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"
+ unique="true"/>
+ </join>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ optional="true"
+ inverse="true">
+ <key column="addressId"
+ unique="true"/>
+ <many-to-one name="person"
+ column="personId"
+ not-null="true"
+ unique="true"/>
+ </join>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-bidirectional-join-m2m" revision="1">
+ <title>plusieurs à plusieurs</title>
+
+ <para>
+ Finallement nous avons <emphasis>l'association bidirectionnelle plusieurs à plusieurs</emphasis>.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses" table="PersonAddress">
+ <key column="personId"/>
+ <many-to-many column="addressId"
+ class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <set name="people" inverse="true" table="PersonAddress">
+ <key column="addressId"/>
+ <many-to-many column="personId"
+ class="Person"/>
+ </set>
+</class>]]></programlisting>
+
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personId, addressId) )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="assoc-complex">
+ <title>Des mappings plus complexes</title>
+
+ <para>
+ Des associations encore plus complexes sont <emphasis>extrêmement</emphasis> rares.
+ Hibernate permet de gérer des situations plus complexes en utilisant des
+ parties SQL dans les fichiers de mapping. Par exemple, si une table
+ avec l'historiques des informations d'un compte définit les colonnes
+ <literal>accountNumber</literal>, <literal>effectiveEndDate</literal>
+ et <literal>effectiveStartDate</literal>, mappées de telle sorte:
+ </para>
+
+ <programlisting><![CDATA[<properties name="currentAccountKey">
+ <property name="accountNumber" type="string" not-null="true"/>
+ <property name="currentAccount" type="boolean">
+ <formula>case when effectiveEndDate is null then 1 else 0 end</formula>
+ </property>
+</properties>
+<property name="effectiveEndDate" type="date"/>
+<property name="effectiveStateDate" type="date" not-null="true"/>]]></programlisting>
+
+ <para>
+ alors nous pouvons mapper une association à l'instance <emphasis>courante</emphasis>
+ (celle avec une <literal>effectiveEndDate</literal>) nulle en utilisant:
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="currentAccountInfo"
+ property-ref="currentAccountKey"
+ class="AccountInfo">
+ <column name="accountNumber"/>
+ <formula>'1'</formula>
+</many-to-one>]]></programlisting>
+
+ <para>
+ Dans un exemple plus complexe, imaginez qu'une association entre
+ <literal>Employee</literal> et <literal>Organization</literal> est gérée
+ dans une table <literal>Employment</literal> pleines de données historiques.
+ Dans ce cas, une association vers l'employeur <emphasis>le plus récent</emphasis>
+ (celui avec la <literal>startDate</literal> la plus récente) pourrait être mappée comme cela:
+ </para>
+
+ <programlisting><![CDATA[<join>
+ <key column="employeeId"/>
+ <subselect>
+ select employeeId, orgId
+ from Employments
+ group by orgId
+ having startDate = max(startDate)
+ </subselect>
+ <many-to-one name="mostRecentEmployer"
+ class="Organization"
+ column="orgId"/>
+</join>]]></programlisting>
+
+ <para>
+ Vous pouvez être créatif grace à ces possibilités, mais il est généralement plus pratique
+ d'utiliser des requêtes HQL ou criteria dans ce genre de situation.
+ </para>
+
+ </sect1>
+
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/basic_mapping.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/basic_mapping.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/basic_mapping.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,3117 @@
+<?xml version='1.0' encoding="iso-8859-1"?>
+<chapter id="mapping">
+ <title>Mapping O/R basique</title>
+ <sect1 id="mapping-declaration" revision="1">
+ <title>Déclaration de Mapping</title>
+ <para>
+ Les mappings Objet/relationnel sont généralement définis dans un document XML.
+ Le document de mapping est conçu pour être lisible et éditable à la main.
+ Le langage de mapping est Java-centrique, c'est à dire que les mappings sont construits
+ à partir des déclarations des classes persistantes et non des déclarations des tables.
+ </para>
+ <para>
+ Remarquez que même si beaucoup d'utilisateurs de Hibernate préfèrent écrire les
+ fichiers de mappings à la main, plusieurs outils existent pour générer ce document,
+ notamment XDoclet, Middlegen et AndroMDA.
+ </para>
+ <para>Démarrons avec un exemple de mapping :</para>
+ <programlisting id="mapping-declaration-ex1" revision="1"><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="eg">
+
+ <class name="Cat"
+ table="cats"
+ discriminator-value="C">
+
+ <id name="id">
+ <generator class="native"/>
+ </id>
+
+ <discriminator column="subclass"
+ type="character"/>
+
+ <property name="weight"/>
+
+ <property name="birthdate"
+ type="date"
+ not-null="true"
+ update="false"/>
+
+ <property name="color"
+ type="eg.types.ColorUserType"
+ not-null="true"
+ update="false"/>
+
+ <property name="sex"
+ not-null="true"
+ update="false"/>
+
+ <property name="litterId"
+ column="litterId"
+ update="false"/>
+
+ <many-to-one name="mother"
+ column="mother_id"
+ update="false"/>
+
+ <set name="kittens"
+ inverse="true"
+ order-by="litter_id">
+ <key column="mother_id"/>
+ <one-to-many class="Cat"/>
+ </set>
+
+ <subclass name="DomesticCat"
+ discriminator-value="D">
+
+ <property name="name"
+ type="string"/>
+
+ </subclass>
+
+ </class>
+
+ <class name="Dog">
+ <!-- mapping for Dog could go here -->
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+ <para>
+ Etudions le contenu du document de mapping. Nous décrirons uniquement
+ les éléments et attributs du document utilisés par Hibernate à l'exécution.
+ Le document de mapping contient aussi des attributs et éléments optionnels
+ qui agissent sur le schéma de base de données exporté par l'outil de
+ génération de schéma. (Par exemple l'attribut <literal>not-null</literal>.)
+ </para>
+ <sect2 id="mapping-declaration-doctype" revision="2">
+ <title>Doctype</title>
+ <para>
+ Tous les mappings XML devraient utiliser le doctype indiqué.
+ Ce fichier est présent à l'URL ci-dessus, dans le répertoire
+ <literal>hibernate-x.x.x/src/org/hibernate</literal> ou dans <literal>hibernate3.jar</literal>.
+ Hibernate va toujours chercher la DTD dans son classpath en premier lieu. Si vous constatez
+ des recherches de la DTD sur Internet, vérifiez votre déclaration de DTD par rapport
+ au contenu de votre classpath.
+ </para>
+ </sect2>
+ <sect2 id="mapping-declaration-mapping" revision="3">
+ <title>hibernate-mapping</title>
+ <para>
+ Cet élément a plusieurs attributs optionnels. Les attributs <literal>schema</literal> et <literal>catalog</literal>
+ indiquent que les tables référencées par ce mapping appartiennent au schéma nommé et/ou au catalogue.
+ S'ils sont spécifiés, les noms de tables seront qualifiés par les noms de schéma et catalogue.
+ L'attribut <literal>default-cascade</literal> indique quel type de cascade sera utlisé par défaut
+ pour les propriétés et collections qui ne précisent pas l'attribut <literal>cascade</literal>.
+ L'attribut <literal>auto-import</literal> nous permet d'utiliser par défaut des noms de classes
+ non qualifiés dans le langage de requête.
+ </para>
+ <programlistingco>
+ <areaspec>
+ <area id="hm1" coords="2 55"/>
+ <area id="hm2" coords="3 55"/>
+ <area id="hm3" coords="4 55"/>
+ <area id="hm4" coords="5 55"/>
+ <area id="hm5" coords="6 55"/>
+ <area id="hm6" coords="7 55"/>
+ <area id="hm7" coords="8 55"/>
+ </areaspec>
+ <programlisting><![CDATA[<hibernate-mapping
+ schema="schemaName"
+ catalog="catalogName"
+ default-cascade="cascade_style"
+ default-access="field|property|ClassName"
+ default-lazy="true|false"
+ auto-import="true|false"
+ package="package.name"
+ />]]></programlisting>
+ <calloutlist>
+ <callout arearefs="hm1">
+ <para>
+ <literal>schema</literal> (optionnel) : Le nom d'un schéma de base de données.
+ </para>
+ </callout>
+ <callout arearefs="hm2">
+ <para>
+ <literal>catalog</literal> (optionnel) : Le nom d'un catalogue de base de données.
+ </para>
+ </callout>
+ <callout arearefs="hm3">
+ <para>
+ <literal>default-cascade</literal> (optionnel - par défaut vaut : <literal>none</literal>) :
+ Un type de cascade par défaut.
+ </para>
+ </callout>
+ <callout arearefs="hm4">
+ <para>
+ <literal>default-access</literal> (optionnel - par défaut vaut : <literal>property</literal>) :
+ Comment hibernate accèdera aux propriétés. On peut aussi
+ redéfinir sa propre implémentation de <literal>PropertyAccessor</literal>.
+ </para>
+ </callout>
+ <callout arearefs="hm5">
+ <para>
+ <literal>default-lazy</literal> (optionnel - par défaut vaut : <literal>true</literal>) :
+ Valeur par défaut pour un attribut <literal>lazy</literal>
+ non spécifié : celui des mappings de classes et de collection.
+ </para>
+ </callout>
+ <callout arearefs="hm6">
+ <para>
+ <literal>auto-import</literal> (optionnel - par défaut vaut : <literal>true</literal>) :
+ Spécifie si l'on peut utiliser des noms de classes
+ non qualifiés (des classes de ce mapping) dans le langage de requête.
+ </para>
+ </callout>
+ <callout arearefs="hm7">
+ <para>
+ <literal>package</literal> (optionnel) : Préfixe de package par défaut pour
+ les noms de classe non qualifiés du document de mapping.
+
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+ <para>
+ Si deux classes possèdent le même nom de classe (non qualifié), vous devez indiquer
+ <literal>auto-import="false"</literal>. Hibernate lancera une exception
+ si vous essayez d'assigner à deux classes le même nom importé.
+ </para>
+ <para>
+ Notez que l'élément <literal>hibernate-mapping</literal> vous permet d'imbriquer plusieurs mappings de
+ <literal><class></literal> persistantes, comme dans l'exemple ci-dessus.
+ Cependant la bonne pratique (ce qui est attendu par certains outils) est
+ de mapper une seule classe (ou une seule hiérarchie de classes)
+ par fichier de mapping et de nommer ce fichier d'après le nom de la superclasse, par exemple
+ <literal>Cat.hbm.xml</literal>, <literal>Dog.hbm.xml</literal>, ou en cas d'héritage,
+ <literal>Animal.hbm.xml</literal>.
+ </para>
+ </sect2>
+ <sect2 id="mapping-declaration-class" revision="3">
+ <title>class</title>
+ <para>
+ Déclarez une classe persistante avec l'élément <literal>class</literal> :
+ </para>
+ <programlistingco>
+ <areaspec>
+ <area id="class1" coords="2 55"/>
+ <area id="class2" coords="3 55"/>
+ <area id="class3" coords="4 55"/>
+ <area id="class4" coords="5 55"/>
+ <area id="class5" coords="6 55"/>
+ <area id="class6" coords="7 55"/>
+ <area id="class7" coords="8 55"/>
+ <area id="class8" coords="9 55"/>
+ <area id="class9" coords="10 55"/>
+ <area id="class10" coords="11 55"/>
+ <area id="class11" coords="12 55"/>
+ <area id="class12" coords="13 55"/>
+ <area id="class13" coords="14 55"/>
+ <area id="class14" coords="15 55"/>
+ <area id="class15" coords="16 55"/>
+ <area id="class16" coords="17 55"/>
+ <area id="class17" coords="18 55"/>
+ <area id="class18" coords="19 55"/>
+ <area id="class19" coords="20 55"/>
+ <area id="class20" coords="21 55"/>
+ <area id="class21" coords="22 55"/>
+ </areaspec>
+ <programlisting><![CDATA[<class
+ name="ClassName"
+ table="tableName"
+ discriminator-value="discriminator_value"
+ mutable="true|false"
+ schema="owner"
+ catalog="catalog"
+ proxy="ProxyInterface"
+ dynamic-update="true|false"
+ dynamic-insert="true|false"
+ select-before-update="true|false"
+ polymorphism="implicit|explicit"
+ where="arbitrary sql where condition"
+ persister="PersisterClass"
+ batch-size="N"
+ optimistic-lock="none|version|dirty|all"
+ lazy="true|false"
+ entity-name="EntityName"
+ catalog="catalog"
+ check="arbitrary sql check condition"
+ rowid="rowid"
+ subselect="SQL expression"
+ abstract="true|false"
+ entity-name="EntityName"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="class1">
+ <para>
+ <literal>name</literal> (optionnel) : Le nom Java complet de la classe (ou interface) persistante.
+ Si cet attribut est absent, il est supposé que ce mapping ne se rapporte pas à une entité POJO.
+ </para>
+ </callout>
+ <callout arearefs="class2">
+ <para>
+ <literal>table</literal> (optionnel - par défaut le nom (non-qualifié) de la classe) : Le
+ nom de sa table en base de données.
+ </para>
+ </callout>
+ <callout arearefs="class3">
+ <para>
+ <literal>discriminator-value</literal> (optionnel - par défaut le nom de la classe) :
+ Une valeur permettant de distinguer les sous-classes dans le cas de l'utilisation du polymorphisme.
+ Les valeurs <literal>null</literal> et <literal>not null</literal> sont autorisées.
+ </para>
+ </callout>
+ <callout arearefs="class4">
+ <para>
+ <literal>mutable</literal> (optionnel, vaut <literal>true</literal> par défaut) : Spécifie
+ que des instances de la classe sont (ou non) immuables.
+ </para>
+ </callout>
+ <callout arearefs="class5">
+ <para>
+ <literal>schema</literal> (optionnel) : Surcharge le nom de schéma spécifié par
+ l'élément racine <literal><hibernate-mapping></literal>.
+ </para>
+ </callout>
+ <callout arearefs="class6">
+ <para>
+ <literal>catalog</literal> (optionnel) : Surcharge le nom du catalogue spécifié par
+ l'élément racine <literal><hibernate-mapping></literal>.
+ </para>
+ </callout>
+ <callout arearefs="class7">
+ <para>
+ <literal>proxy</literal> (optionnel) : Spécifie une interface à utiliser pour l'initialisation différée (lazy loading)
+ des proxies. Vous pouvez indiquer le nom de la classe elle-même.
+ </para>
+ </callout>
+ <callout arearefs="class8">
+ <para>
+ <literal>dynamic-update</literal> (optionnel, par défaut à <literal>false</literal>) :
+ Spécifie que les <literal>UPDATE</literal> SQL doivent être générés à l'exécution et contenir
+ uniquement les colonnes dont les valeurs ont été modifiées.
+ </para>
+ </callout>
+ <callout arearefs="class9">
+ <para>
+ <literal>dynamic-insert</literal> (optionnel, par défaut à <literal>false</literal>):
+ Spécifie que les <literal>INSERT</literal> SQL doivent être générés à l'exécution et ne contenir
+ que les colonnes dont les valeurs sont non nulles.
+ </para>
+ </callout>
+ <callout arearefs="class10">
+ <para>
+ <literal>select-before-update</literal> (optionnel, par défaut à <literal>false</literal>):
+ Spécifie que Hibernate ne doit <emphasis>jamais</emphasis> exécuter un <literal>UPDATE</literal> SQL
+ sans être certain qu'un objet a été réellement modifié. Dans certains cas, (en réalité, seulement
+ quand un objet transient a été associé à une nouvelle session par <literal>update()</literal>),
+ cela signifie que Hibernate exécutera un <literal>SELECT</literal> SQL pour s'assurer qu'un
+ <literal>UPDATE</literal> SQL est véritablement nécessaire.
+ </para>
+ </callout>
+ <callout arearefs="class11">
+ <para>
+ <literal>polymorphism</literal> (optionnel, vaut <literal>implicit</literal> par défaut) :
+ Détermine si, pour cette classe, une requête polymorphique implicite ou explicite est utilisée.
+ </para>
+ </callout>
+ <callout arearefs="class12">
+ <para>
+ <literal>where</literal> (optionnel) spécifie une clause SQL <literal>WHERE</literal>
+ à utiliser lorsque l'on récupère des objets de cette classe.
+ </para>
+ </callout>
+ <callout arearefs="class13">
+ <para>
+ <literal>persister</literal> (optionnel) : Spécifie un <literal>ClassPersister</literal> particulier.
+ </para>
+ </callout>
+ <callout arearefs="class14">
+ <para>
+ <literal>batch-size</literal> (optionnel, par défaut = <literal>1</literal>) : spécifie une taille de batch
+ pour remplir les instances de cette classe par identifiant en une seule requête.
+ </para>
+ </callout>
+ <callout arearefs="class15">
+ <para>
+ <literal>optimistic-lock</literal> (optionnel, par défaut = <literal>version</literal>) :
+ Détermine la stratégie de verrou optimiste.
+ </para>
+ </callout>
+ <callout arearefs="class16">
+ <para>
+ <literal>lazy</literal> (optionnel) : Déclarer <literal>lazy="true"</literal> est un raccourci
+ pour spécifier le nom de la classe comme étant l'interface <literal>proxy</literal>.
+ </para>
+ </callout>
+ <callout arearefs="class17">
+ <para>
+ <literal>entity-name</literal> (optionnel) : Hibernate3 permet à une classe d'être
+ mappée plusieurs fois (potentiellement à plusieurs tables), et permet aux mappings d'entité d'être
+ représentés par des Maps ou du XML au niveau Java. Dans ces cas, vous devez indiquer un nom explicite arbitraire pour
+ les entités. Voir <xref linkend="persistent-classes-dynamicmodels"/> et <xref linkend="xml"/>
+ pour plus d'informations.
+ </para>
+ </callout>
+ <callout arearefs="class18">
+ <para>
+ <literal>catalog</literal> (optionnel) : The name of a database catalog used for this
+ class and its table.
+ </para>
+ </callout>
+ <callout arearefs="class19">
+ <para>
+ <literal>check</literal> (optionnel) : expression SQL utilisée pour générer une contrainte
+ de vérification multi-lignes pour la génération automatique de schéma.
+ </para>
+ </callout>
+ <callout arearefs="class20">
+ <para>
+ <literal>rowid</literal> (optionnel) : Hibernate peut utiliser des ROWID sur les bases de
+ données qui utilisent ce mécanisme. Par exemple avec Oracle, Hibernate peut utiliser la colonne additionnelle
+ <literal>rowid</literal> pour des mises à jour rapides si cette option vaut <literal>rowid</literal>. Un ROWID représente
+ la localisation physique d'un tuple enregistré.
+ </para>
+ </callout>
+ <callout arearefs="class21">
+ <para>
+ <literal>subselect</literal> (optionnel) : Permet de mapper une entité immuable en lecture-seule
+ sur un sous-select de base de données. Utile pour avoir une vue au lieu d'une table en base, mais à éviter. Voir plus bas
+ pour plus d'information.
+ </para>
+ </callout>
+ <callout arearefs="class22">
+ <para>
+ <literal>abstract</literal> (optionnel) : Utilisé pour marquer des superclasses abstraites dans
+ des hiérarchies de <literal><union-subclass></literal>.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+ <para>
+ Il est tout à fait possible d'utiliser une interface comme nom
+ de classe persistante. Vous devez alors déclarer les classes implémentant
+ cette interface en utilisant l'élément <literal><subclass></literal>.
+ Vous pouvez faire persister toute classe interne <emphasis>static</emphasis>.
+ Vous devez alors spécifier le nom de la classe par la notation habituelle
+ des classes internes c'est à dire <literal>eg.Foo$Bar</literal>.
+ </para>
+ <para>
+ Les classes immuables, <literal>mutable="false"</literal>, ne peuvent
+ pas être modifiées ou supprimées par l'application. Cela permet
+ à Hibernate de faire quelques optimisations mineures sur les performances.
+ </para>
+ <para>
+ L'attribut optionnnel <literal>proxy</literal> permet les intialisations
+ différées des instances persistantes de la classe. Hibernate retournera
+ initialement des proxies CGLIB qui implémentent l'interface nommée.
+ Le véritable objet persistant ne sera chargé que lorsque une méthode du proxy
+ sera appelée. Voir plus bas le paragraphe abordant les proxies et le chargement différé (lazy initialization).
+ </para>
+ <para>
+ Le polymorphisme <emphasis>implicite</emphasis>
+ signifie que les instances de la classe seront retournées par une
+ requête qui utilise les noms de la classe ou de chacune de ses
+ superclasses ou encore des interfaces implémentées par cette classe
+ ou ses superclasses. Les instances des classes filles seront retournées
+ par une requête qui utilise le nom de la classe elle même. Le polymorphisme
+ <emphasis>explicite</emphasis> signifie que les instances de la classe
+ ne seront retournées que par une requête qui utilise explicitement
+ son nom et que seules les instances des classes filles déclarées dans les éléments
+ <literal><subclass></literal> ou <literal><joined-subclass></literal>
+ seront retournées. Dans la majorités des cas la valeur par défaut,
+ <literal>polymorphism="implicit"</literal>,
+ est appropriée. Le polymorphisme explicite est utile lorsque deux
+ classes différentes sont mappées à la même table (ceci permet d'écrire
+ une classe "légère" qui ne contient qu'une partie des colonnes de
+ la table - voir la partie design pattern du site communautaire).
+ </para>
+ <para>
+ L'attribut <literal>persister</literal> vous permet de customiser
+ la stratégie utilisée pour la classe. Vous pouvez, par exemple, spécifier
+ votre propre sous-classe de <literal>org.hibernate.persister.EntityPersister</literal> ou
+ vous pourriez aussi créer une nouvelle implémentation de l'interface <literal>org.hibernate.persister.ClassPersister</literal>
+ qui proposerait une persistance via, par exemple, des appels de procédures
+ stockées, de la sérialisation vers des fichiers plats ou un annuaire LDAP.
+ Voir <literal>org.hibernate.test.CustomPersister</literal> pour un exemple simple (d'une "persistance"
+ vers une <literal>Hashtable</literal>).
+ </para>
+ <para>
+ Notez que les paramètres <literal>dynamic-update</literal> et <literal>dynamic-insert</literal>
+ ne sont pas hérités par les sous-classes et peuvent donc être spécifiés
+ pour les éléments <literal><subclass></literal> ou <literal><joined-subclass></literal>
+ Ces paramètres peuvent améliorer les performances dans certains cas,
+ mais peuvent aussi les amoindrir. A utiliser en connaissance de causes.
+ </para>
+ <para>
+ L'utilisation de <literal>select-before-update</literal> va généralement
+ faire baisser les performances. Ce paramètre est pratique
+ pour prévenir l'appel inutile d'un trigger sur modification quand on
+ réattache un graphe d'instances à une <literal>Session</literal>.
+ </para>
+ <para>
+ Si vous utilisez le <literal>dynamic-update</literal>, les différentes
+ stratégies de verrouillage optimiste (optimistic locking) sont les suivantes:
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>version</literal> vérifie les colonnes version/timestamp
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>all</literal> vérifie toutes les colonnes
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>dirty</literal> vérifie les colonnes modifiées, permettant des updates concurrents
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>none</literal> pas de verrouillage optimiste
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+ Nous encourageons <emphasis>très</emphasis> fortement l'utilisation
+ de colonnes de version/timestamp pour le verrouillage optimiste
+ avec Hibernate. C'est la meilleure stratégie en regard des performances
+ et la seule qui gère correctement les modifications sur les objets détachés
+ (c'est à dire lorsqu'on utilise <literal>Session.merge()</literal>).
+ </para>
+ <para>
+ Il n'y a pas de différence entre table et vue pour le mapping Hibernate,
+ tant que c'est transparent au niveau base de données (remarquez
+ que certaines BDD ne supportent pas les vues correctement, notamment
+ pour les updates). Vous rencontrerez peut-être des cas où vous
+ souhaitez utiliser une vue mais ne pouvez pas en créer sur votre BDD
+ (par exemple à cause de schémas anciens et figés). Dans ces cas,
+ vous pouvez mapper une entité immuable en lecture seule sur un sous-select SQL donné:
+ </para>
+ <programlisting><![CDATA[<class name="Summary">
+ <subselect>
+ select item.name, max(bid.amount), count(*)
+ from item
+ join bid on bid.item_id = item.id
+ group by item.name
+ </subselect>
+ <synchronize table="item"/>
+ <synchronize table="bid"/>
+ <id name="name"/>
+ ...
+</class>]]></programlisting>
+ <para>
+ Déclarez les tables à synchroniser avec cette entité pour assurer
+ que le flush automatique se produise correctement,
+ et pour que les requêtes sur l'entité dérivée ne renvoient pas des données périmées.
+ Le litéral <literal><subselect></literal> est disponible
+ comme attribut ou comme élément de mapping.
+ </para>
+ </sect2>
+ <sect2 id="mapping-declaration-id" revision="4">
+ <title>id</title>
+ <para>
+ Les classes mappées <emphasis>doivent</emphasis> déclarer la
+ clef primaire de la table en base de données.
+ La plupart des classes auront aussi une propriété de type
+ javabean présentant l'identifiant unique d'une instance.
+ L'élément <literal><id></literal> sert à définir le
+ mapping entre cette propriété et la clef primaire en base.
+ </para>
+ <programlistingco>
+ <areaspec>
+ <area id="id1" coords="2 65"/>
+ <area id="id2" coords="3 65"/>
+ <area id="id3" coords="4 65"/>
+ <area id="id4" coords="5 65"/>
+ <area id="id5" coords="6 65"/>
+ </areaspec>
+ <programlisting><![CDATA[<id
+ name="propertyName"
+ type="typename"
+ column="column_name"
+ unsaved-value="null|any|none|undefined|id_value"
+ access="field|property|ClassName">
+
+ <generator class="generatorClass"/>
+</id>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="id1">
+ <para>
+ <literal>name</literal> (optionnel) : Nom de la propriété qui sert d'identifiant.
+ </para>
+ </callout>
+ <callout arearefs="id2">
+ <para>
+ <literal>type</literal> (optionnel) : Nom indiquant le type Hibernate.
+ </para>
+ </callout>
+ <callout arearefs="id3">
+ <para>
+ <literal>column</literal> (optionnel - le nom de la propriété est pris par défaut) : Nom de la clef primaire.
+ </para>
+ </callout>
+ <callout arearefs="id4">
+ <para>
+ <literal>unsaved-value</literal> (optionnel - par défaut une valeur "bien choisie") :
+ Une valeur de la propriété d'identifiant qui indique que l'instance est nouvellement
+ instanciée (non sauvegardée), et qui la distingue des instances <literal>transient</literal>s qui ont
+ été sauvegardées ou chargées dans une session précédente.
+ </para>
+ </callout>
+ <callout arearefs="id5">
+ <para>
+ <literal>access</literal> (optionnel - par défaut <literal>property</literal>) : La stratégie que doit utiliser Hibernate
+ pour accéder aux valeurs des propriétés.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+ <para>
+ Si l'attribut <literal>name</literal> est absent, Hibernate considère que la classe ne possède pas de propriété identifiant.
+ </para>
+ <para>
+ L'attribut <literal>unsaved-value</literal> est important ! Si
+ l'identifiant de votre classe n'a pas une valeur par défaut compatible avec
+ le comportement standard de Java (zéro ou null), vous devez alors préciser la valeur par défaut.
+ </para>
+ <para>
+ La déclaration alternative <literal><composite-id></literal>
+ permet l'acccès aux données d'anciens systèmes qui utilisent des
+ clefs composées. Son utilisation est fortement déconseillée pour d'autres cas.
+ </para>
+ <sect3 id="mapping-declaration-id-generator" revision="2">
+ <title>Generator</title>
+ <para>
+ L'élément fils <literal><generator></literal> nomme une classe Java utilisée pour générer
+ les identifiants uniques pour les instances des classes persistantes. Si des paramètres sont requis
+ pour configurer ou initialiser l'instance du générateur, ils sont passés en utilisant l'élément <literal><param></literal>.
+ </para>
+ <programlisting><![CDATA[<id name="id" type="long" column="cat_id">
+ <generator class="org.hibernate.id.TableHiLoGenerator">
+ <param name="table">uid_table</param>
+ <param name="column">next_hi_value_column</param>
+ </generator>
+</id>]]></programlisting>
+ <para>
+ Tous les générateurs doivent implémenter l'interface <literal>org.hibernate.id.IdentifierGenerator</literal>.
+ C'est une interface très simple ; certaines applications peuvent proposer leur propre implémentations spécialisées.
+ Cependant, Hibernate propose une série d'implémentations intégrées. Il existe des noms raccourcis pour les générateurs intégrés :
+ <variablelist>
+ <varlistentry>
+ <term>
+ <literal>increment</literal>
+ </term>
+ <listitem>
+ <para>
+ Génère des identifiants de type <literal>long</literal>, <literal>short</literal> ou
+ <literal>int</literal> qui ne sont uniques que si aucun autre processus n'insère de données dans la même table.
+ <emphasis>Ne pas utiliser en environnement clusterisé.</emphasis>
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <literal>identity</literal>
+ </term>
+ <listitem>
+ <para>
+ Utilisation de la colonne identity de DB2, MySQL, MS SQL Server, Sybase et
+ HypersonicSQL. L'identifiant renvoyé est de type <literal>long</literal>,
+ <literal>short</literal> ou <literal>int</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <literal>sequence</literal>
+ </term>
+ <listitem>
+ <para>
+ Utilisation des séquences dans DB2, PostgreSQL, Oracle, SAP DB, McKoi ou d'un générateur dans Interbase.
+ L'identifiant renvoyé est de type <literal>long</literal>,
+ <literal>short</literal> ou <literal>int</literal>
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <literal>hilo</literal>
+ </term>
+ <listitem>
+ <para id="mapping-declaration-id-hilodescription" revision="1">
+ Utilise un algorithme hi/lo pour générer de façon efficace des identifiants de type
+ <literal>long</literal>, <literal>short</literal> ou <literal>int</literal>,
+ en prenant comme source de valeur "hi" une table et une colonne (par défaut
+ <literal>hibernate_unique_key</literal> et
+ <literal>next_hi</literal> respectivement). L'algorithme hi/lo génère des identifiants uniques
+ pour une base de données particulière seulement.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <literal>seqhilo</literal>
+ </term>
+ <listitem>
+ <para>
+ Utilise un algorithme hi/lo pour générer efficacement des identifiants de type <literal>long</literal>, <literal>short</literal> ou <literal>int</literal>,
+ étant donné un nom de séquence en base.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <literal>uuid</literal>
+ </term>
+ <listitem>
+ <para>
+ Utilise un algorithme de type UUID 128 bits pour générer des identifiants de
+ type string, unique au sein d'un réseau (l'adresse IP est utilisée).
+ Le UUID en codé en une chaîne de nombre héxadécimaux de longueur 32.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <literal>guid</literal>
+ </term>
+ <listitem>
+ <para>
+ Utilise une chaîne GUID générée par la base pour MS SQL Server et MySQL.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <literal>native</literal>
+ </term>
+ <listitem>
+ <para>
+ Choisit <literal>identity</literal>, <literal>sequence</literal> ou
+ <literal>hilo</literal> selon les possibilités offertes par la base de données sous-jacente.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <literal>assigned</literal>
+ </term>
+ <listitem>
+ <para>
+ Laisse l'application affecter un identifiant à l'objet avant que la métode
+ <literal>save()</literal> soit appelée. Il s'agit de la stratégie par défaut
+ si aucun <literal><generator></literal> n'est spécifié.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <literal>select</literal>
+ </term>
+ <listitem>
+ <para>
+ Récupère une clef primaire assignée par un trigger en sélectionnant
+ la ligne par une clef unique quelconque.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <literal>foreign</literal>
+ </term>
+ <listitem>
+ <para>
+ Utilise l'identifiant d'un objet associé. Habituellement utilisé en conjonction
+ avec une association <literal><one-to-one></literal> sur la clef primaire.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </sect3>
+ <sect3 id="mapping-declaration-id-hilo" revision="1">
+ <title>algorithme Hi/lo</title>
+ <para>
+ Les générateurs <literal>hilo</literal> et <literal>seqhilo</literal> proposent deux implémentations
+ alternatives de l'algorithme hi/lo, une approche largement utilisée pour générer des identifiants. La
+ première implémentation nécessite une table "spéciale" en base pour héberger la prochaine valeur "hi" disponible.
+ La seconde utilise une séquence de type Oracle (quand la base sous-jacente le propose).
+ </para>
+ <programlisting><![CDATA[<id name="id" type="long" column="cat_id">
+ <generator class="hilo">
+ <param name="table">hi_value</param>
+ <param name="column">next_value</param>
+ <param name="max_lo">100</param>
+ </generator>
+</id>]]></programlisting>
+ <programlisting><![CDATA[<id name="id" type="long" column="cat_id">
+ <generator class="seqhilo">
+ <param name="sequence">hi_value</param>
+ <param name="max_lo">100</param>
+ </generator>
+</id>]]></programlisting>
+ <para>
+ Malheureusement, vous ne pouvez pas utilisez <literal>hilo</literal> quand vous apportez
+ votre propre <literal>Connection</literal> à Hibernate. Quand Hibernate utilise une datasource du serveur
+ d'application pour obtenir des connexions inscrites avec JTA, vous devez correctement configurer
+ <literal>hibernate.transaction.manager_lookup_class</literal>.
+ </para>
+ </sect3>
+ <sect3 id="mapping-declaration-id-uuid">
+ <title>UUID algorithm</title>
+ <para>
+ Le contenu du UUID est : adresse IP, date de démarrage de la JVM (précis au quart de seconde),
+ l'heure système et un compteur (unique au sein de la JVM). Il n'est pas possible d'obtenir l'adresse
+ MAC ou une adresse mémoire à partir de Java, c'est donc le mieux que l'on puisse faire sans utiliser JNI.
+ </para>
+ </sect3>
+ <sect3 id="mapping-declaration-id-sequences">
+ <title>Colonnes identifiantes et séquences</title>
+ <para>
+ Pour les bases qui implémentent les colonnes "identité" (DB2, MySQL, Sybase, MS SQL),
+ vous pouvez utiliser la génération de clef par <literal>identity</literal>.
+ Pour les bases qui implémentent les séquences (DB2, Oracle, PostgreSQL, Interbase, McKoi, SAP DB)
+ vous pouvez utiliser la génération de clef par <literal>sequence</literal>. Ces deux méthodes nécessitent
+ deux requêtes SQL pour insérer un objet.
+ </para>
+ <programlisting><![CDATA[<id name="id" type="long" column="person_id">
+ <generator class="sequence">
+ <param name="sequence">person_id_sequence</param>
+ </generator>
+</id>]]></programlisting>
+ <programlisting><![CDATA[<id name="id" type="long" column="person_id" unsaved-value="0">
+ <generator class="identity"/>
+</id>]]></programlisting>
+ <para>Pour le développement multi-plateformes, la stratégie <literal>native</literal> choisira
+ entre les méthodes <literal>identity</literal>, <literal>sequence</literal> et
+ <literal>hilo</literal>, selon les possibilités offertes par la base sous-jacente.
+ </para>
+ </sect3>
+ <sect3 id="mapping-declaration-id-assigned">
+ <title>Identifiants assignés</title>
+ <para>
+ Si vous souhaitez que l'application assigne des identifiants (par opposition
+ à la génération par Hibernate), vous pouvez utiliser le générateur <literal>assigned</literal>.
+ Ce générateur spécial utilisera une valeur d'identifiant déjà utilisé par la propriété identifiant l'objet.
+ Ce générateur est utilisé quand la clef primaire est une clef naturelle plutôt qu'une clef secondaire.
+ C'est le comportement par défaut si vous ne précisez pas d'élément <literal><generator></literal>.
+ </para>
+ <para>
+ Choisir le générateur <literal>assigned</literal> fait utiliser
+ <literal>unsaved-value="undefined"</literal> par Hibernate, le forçant à interroger
+ la base pour déterminer si l'instance est transiente ou détachée, à moins d'utiliser
+ une propriété version ou timestamp, ou alors de définir
+ <literal>Interceptor.isUnsaved()</literal>.
+ </para>
+ </sect3>
+ <sect3 id="mapping-declaration-id-select">
+ <title>Clefs primaires assignées par trigger</title>
+ <para>
+ Pour les schémas de base hérités d'anciens systèmes uniquement (Hibernate ne génère pas de DDL avec des triggers)
+ </para>
+ <programlisting><![CDATA[<id name="id" type="long" column="person_id">
+ <generator class="select">
+ <param name="key">socialSecurityNumber</param>
+ </generator>
+</id>]]></programlisting>
+ <para>
+ Dans l'exemple ci-dessus, <literal>socialSecurityNumber</literal> a une
+ valeur unique définie par la classe en tant que clef naturelle et <literal>person_id</literal>
+ est une clef secondaire dont la valeur est générée par trigger.
+ </para>
+ </sect3>
+ </sect2>
+ <sect2 id="mapping-declaration-compositeid" revision="3">
+ <title>composite-id</title>
+ <programlisting><![CDATA[<composite-id
+ name="propertyName"
+ class="ClassName"
+ mapped="true|false"
+ access="field|property|ClassName">
+ node="element-name|."
+
+ <key-property name="propertyName" type="typename" column="column_name"/>
+ <key-many-to-one name="propertyName class="ClassName" column="column_name"/>
+ ......
+</composite-id>]]></programlisting>
+ <para>
+ Pour une table avec clef composée, vous pouvez mapper plusieurs attributs de la classe
+ comme propriétés identifiantes. L'élement <literal><composite-id></literal> accepte
+ les mappings de propriétés <literal><key-property></literal> et les mappings
+ <literal><key-many-to-one></literal> comme fils.
+ </para>
+ <programlisting><![CDATA[<composite-id>
+ <key-property name="medicareNumber"/>
+ <key-property name="dependent"/>
+</composite-id>]]></programlisting>
+ <para>
+ Vos classes persistantes <emphasis>doivent</emphasis> surcharger les méthodes <literal>equals()</literal>
+ et <literal>hashCode()</literal> pour implémenter l'égalité d'identifiant composé. Elles doivent aussi
+ implenter l'interface <literal>Serializable</literal>.
+ </para>
+ <para>
+ Malheureusement cette approche sur les identifiants composés signifie qu'un objet persistant
+ est son propre identifiant. Il n'y a pas d'autre moyen pratique de manipuler l'objet que par l'objet lui-même.
+ Vous devez instancier une instance de la classe persistante elle-même et peupler ses attributs identifiants
+ avant de pouvoir appeler la méthode <literal>load()</literal> pour charger son état persistant associé
+ à une clef composée. Nous appelons cette approche "identifiant composé <emphasis>embarqué</emphasis>"
+ et ne la recommandons pas pour des applications complexes.
+ </para>
+
+ <para>
+ Une seconde approche, appelée identifiant composé <emphasis>mappé</emphasis>,
+ consiste à encapsuler les propriétés identifiantes (celles contenues dans <literal><composite-id></literal>)
+ dans une classe particulière.
+ </para>
+
+ <programlisting><![CDATA[<composite-id class="MedicareId" mapped="true">
+ <key-property name="medicareNumber"/>
+ <key-property name="dependent"/>
+</composite-id>]]></programlisting>
+
+ <para>
+ Dans cet exemple, la classe d'identifiant composée,<literal>MedicareId</literal> et la classe mappée elle-même,
+ possèdent les propriétés <literal>medicareNumber</literal>
+ et <literal>dependent</literal>. La classe identifiante doit redéfinir
+ <literal>equals()</literal> et <literal>hashCode()</literal> et implémenter
+ <literal>Serializable</literal>. Le désavantage de cette approche est la
+ duplication du code.
+ </para>
+
+ <para>
+ Les attributs suivants servent à configurer un identifiant composé mappé :
+ </para>
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>mapped</literal> (optionnel, défaut à <literal>false</literal>) :
+ indique qu'un identifiant composé mappé est utilisé, et que les propriétés
+ contenues font référence aux deux classes (celle mappée et la classe identifiante).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>class</literal> (optionnel, mais requis pour un identifiant composé mappé) :
+ La classe composant utilisée comme identifiant composé.
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+ Nous décrirons une troisième approche beaucoup plus efficace ou l'identifiant composé
+ est implémenté comme une classe composant dans <xref linkend="components-compositeid"/>.
+ Les attributs décrits ci dessous, ne s'appliquent que pour cette dernière approche :
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>name</literal> (optionnel, requis pour cette approche) : une propriété de type
+ composant qui contient l'identifiant composé (voir chapitre 9).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>access</literal> (optionnel - défaut à <literal>property</literal>) :
+ La stratégie qu'Hibernate utilisera pour accéder à la valeur de la propriété.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>class</literal> (optionnel - défaut au type de la propriété déterminé par réflexion) :
+ La classe composant utilisée comme identifiant (voir prochaine section).
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Cette dernière approche est celle que nous recommandons pour toutes vos applications.
+ </para>
+ </sect2>
+ <sect2 id="mapping-declaration-discriminator" revision="3">
+ <title>discriminator</title>
+ <para>
+ L'élément <literal><discriminator></literal> est nécessaire pour la persistance polymorphique
+ qui utilise la stratégie de mapping de table par hiérarchie de classe. La colonne discriminante contient
+ une valeur marqueur qui permet à la couche de persistance de savoir quelle
+ sous-classe instancier pour une ligne particulière de table en base. Un nombre restreint de types
+ peuvent être utilisés :
+ <literal>string</literal>, <literal>character</literal>, <literal>integer</literal>,
+ <literal>byte</literal>, <literal>short</literal>, <literal>boolean</literal>,
+ <literal>yes_no</literal>, <literal>true_false</literal>.
+ </para>
+ <programlistingco>
+ <areaspec>
+ <area id="discriminator1" coords="2 60"/>
+ <area id="discriminator2" coords="3 60"/>
+ <area id="discriminator3" coords="4 60"/>
+ <area id="discriminator4" coords="5 60"/>
+ <area id="discriminator5" coords="6 60"/>
+ </areaspec>
+ <programlisting><![CDATA[<discriminator
+ column="discriminator_column"
+ type="discriminator_type"
+ force="true|false"
+ insert="true|false"
+ formula="arbitrary sql expression"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="discriminator1">
+ <para>
+ <literal>column</literal> (optionnel - par défaut à <literal>class</literal>) le
+ nom de la colonne discriminante.
+ </para>
+ </callout>
+ <callout arearefs="discriminator2">
+ <para>
+ <literal>type</literal> (optionnel - par défaut à <literal>string</literal>) un nom
+ indiquant le type Hibernate.
+ </para>
+ </callout>
+ <callout arearefs="discriminator3">
+ <para>
+ <literal>force</literal> (optionnel - par défaut à <literal>false</literal>)
+ "oblige" Hibernate à spécifier une valeur discriminante autorisée même quand on récupère
+ toutes les instances de la classe de base.
+ </para>
+ </callout>
+ <callout arearefs="discriminator4">
+ <para>
+ <literal>insert</literal> (optionnel - par défaut à <literal>true</literal>)
+ à passer à <literal>false</literal> si la colonne discriminante fait aussi partie
+ d'un identifiant composé mappé (Indique à Hibernate de ne pas inclure la colonne
+ dans les <literal>INSERT</literal> SQL).
+ </para>
+ </callout>
+ <callout arearefs="discriminator5">
+ <para>
+ <literal>formula</literal> (optionnel) une expression SQL arbitraire qui est exécutée
+ quand un type doit être évalué. Permet la discrimination basée sur le contenu.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+ <para>
+ Les véritables valeurs de la colonne discriminante sont spécifiées par l'attribut
+ <literal>discriminator-value</literal> des éléments <literal><class></literal> et
+ <literal><subclass></literal>.
+ </para>
+ <para>
+ L'attribut <literal>force</literal> n'est utile que si la table contient des lignes avec des
+ valeurs "extra" discriminantes qui ne sont pas mappées à une classe persistante. Ce ne sera généralement pas le cas.
+ </para>
+ <para>
+ En utilisant l'attribut <literal>formula</literal> vous pouvez déclarer une expression SQL arbitraire
+ qui sera utilisée pour évaluer le type d'une ligne :
+ </para>
+ <programlisting><![CDATA[<discriminator
+ formula="case when CLASS_TYPE in ('a', 'b', 'c') then 0 else 1 end"
+ type="integer"/>]]></programlisting>
+ </sect2>
+ <sect2 id="mapping-declaration-version" revision="4">
+ <title>version (optionnel)</title>
+ <para>
+ L'élément <literal><version></literal> est optionnel et indique que la table contient
+ des données versionnées. C'est particulièrement utile si vous avez l'intention d'utiliser
+ des <emphasis>transactions longues</emphasis> (voir plus-bas).
+ </para>
+ <programlistingco>
+ <areaspec>
+ <area id="version1" coords="2 60"/>
+ <area id="version2" coords="3 60"/>
+ <area id="version3" coords="4 60"/>
+ <area id="version4" coords="5 60"/>
+ <area id="version5" coords="6 60"/>
+ <area id="version6" coords="7 70"/>
+ <area id="version7" coords="8 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<version
+ column="version_column"
+ name="propertyName"
+ type="typename"
+ access="field|property|ClassName"
+ unsaved-value="null|negative|undefined"
+ generated="never|always"
+ insert="true|false"
+ node="element-name|@attribute-name|element/@attribute|."
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="version1">
+ <para>
+ <literal>column</literal> (optionnel - par défaut égal au nom de la propriété) : Le nom de la colonne
+ contenant le numéro de version.
+ </para>
+ </callout>
+ <callout arearefs="version2">
+ <para>
+ <literal>name</literal> : Le nom d'un attribut de la classe persistante.
+ </para>
+ </callout>
+ <callout arearefs="version3">
+ <para>
+ <literal>type</literal> (optionnel - par défaut à <literal>integer</literal>) :
+ Le type du numéro de version.
+ </para>
+ </callout>
+ <callout arearefs="version4">
+ <para>
+ <literal>access</literal> (optionnel - par défaut à <literal>property</literal>) : La stratégie
+ à utiliser par Hibernate pour accéder à la valeur de la propriété.
+ </para>
+ </callout>
+ <callout arearefs="version5">
+ <para>
+ <literal>unsaved-value</literal> (optionnel - par défaut à <literal>undefined</literal>) :
+ Une valeur de la propriété d'identifiant qui indique que l'instance est nouvellement
+ instanciée (non sauvegardée), et qui la distingue des instances détachées qui ont
+ été sauvegardées ou chargées dans une session précédente (<literal>undefined</literal> indique
+ que la valeur de l'atribut identifiant devrait être utilisé).
+ </para>
+ </callout>
+ <callout arearefs="version6">
+ <para>
+ <literal>generated</literal> (optional - défaut à <literal>never</literal>) :
+ Indique que la valeur de la propriété version est générée par la base de données
+ cf. <xref linkend="mapping-generated">generated properties</xref>.
+ </para>
+ </callout>
+ <callout arearefs="version7">
+ <para>
+ <literal>insert</literal> (optionnel - défaut à <literal>true</literal>) :
+ Indique si la colonne de version doit être incluse dans les ordres insert.
+ Peut être à <literal>false</literal> si et seulement si la colonne de la
+ base de données est définie avec une valeur par défaut à <literal>0</literal>.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+ <para>
+ Les numéros de version doivent avoir les types Hibernate <literal>long</literal>, <literal>integer</literal>,
+ <literal>short</literal>, <literal>timestamp</literal> ou <literal>calendar</literal>.
+ </para>
+ <para>
+ Une propriété de version ou un timestamp ne doit jamais être null pour une instance
+ détachée, ainsi Hibernate pourra détecter toute instance ayant une version ou un timestamp null
+ comme transient, quelles que soient les stratégies <literal>unsaved-value</literal> spécifiées.
+ <emphasis>Déclarer un numéro de version ou un timestamp "nullable" est un moyen pratique d'éviter
+ tout problème avec les réattachements transitifs dans Hibernate, particulièrement utile pour ceux qui
+ utilisent des identifiants assignés ou des clefs composées !</emphasis>
+ </para>
+ </sect2>
+ <sect2 id="mapping-declaration-timestamp" revision="3" >
+ <title>timestamp (optionnel)</title>
+ <para>
+ L'élément optionnel <literal><timestamp></literal> indique que la table contient des données
+ horodatées (timestamp). Cela sert d'alternative à l'utilisation de numéros de version. Les timestamps (ou horodatage)
+ sont par nature une implémentation moins fiable pour l'optimistic locking. Cependant, l'application
+ peut parfois utiliser l'horodatage à d'autres fins.
+ </para>
+ <programlistingco>
+ <areaspec>
+ <area id="timestamp1" coords="2 45"/>
+ <area id="timestamp2" coords="3 45"/>
+ <area id="timestamp3" coords="4 45"/>
+ <area id="timestamp4" coords="5 45"/>
+ <area id="timestamp5" coords="6 70" />
+ <area id="timestamp6" coords="7 70" />
+ </areaspec>
+ <programlisting><![CDATA[<timestamp
+ column="timestamp_column"
+ name="propertyName"
+ access="field|property|ClassName"
+ unsaved-value="null|undefined"
+ source="vm|db"
+ generated="never|always"
+ node="element-name|@attribute-name|element/@attribute|."
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="timestamp1">
+ <para>
+ <literal>column</literal> (optionnel - par défaut à le nom de la propriété) : Le nom d'une colonne
+ contenant le timestamp.
+ </para>
+ </callout>
+ <callout arearefs="timestamp2">
+ <para>
+ <literal>name</literal> : Le nom d'une propriété au sens JavaBean de type
+ <literal>Date</literal> ou <literal>Timestamp</literal> de la classe persistante.
+ </para>
+ </callout>
+ <callout arearefs="timestamp3">
+ <para>
+ <literal>access</literal> (optionnel - par défaut à <literal>property</literal>) : La stratégie
+ à utiliser par Hibernate pour accéder à la valeur de la propriété.
+ </para>
+ </callout>
+ <callout arearefs="timestamp4">
+ <para>
+ <literal>unsaved-value</literal> (optionnel - par défaut à <literal>null</literal>) :
+ Propriété dont la valeur est un numéro de version qui indique que l'instance est nouvellement
+ instanciée (non sauvegardée), et qui la distingue des instances détachées qui ont
+ été sauvegardées ou chargées dans une session précédente (<literal>undefined</literal> indique
+ que la valeur de l'attribut identifiant devrait être utilisée).
+ </para>
+ </callout>
+ <callout arearefs="timestamp5">
+ <para>
+ <literal>source</literal> (optionnel - par défaut à <literal>vm</literal>) :
+ D'où Hibernate doit-il récupérer la valeur du timestamp? Depuis la base de données
+ ou depuis la JVM d'exécution? Les valeurs de timestamp de la base de données provoquent
+ une surcharge puisque Hibernate doit interroger la base pour déterminer la prochaine valeur
+ mais cela est plus sûr lorsque vous fonctionnez dans un cluster. Remarquez aussi que
+ certains des dialectes ne supportent pas cette fonction, et que d'autres l'implémentent
+ mal, provoquant des erreurs de précision (Oracle 8 par exemple).
+ </para>
+ </callout>
+ <callout arearefs="timestamp6">
+ <para>
+ <literal>generated</literal> (optional - défaut à <literal>never</literal>) :
+ Indique que la valeur de ce timestamp est générée par la base de données
+ cf. <xref linkend="mapping-generated">generated properties</xref>.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+ <para>
+ Notez que <literal><timestamp></literal> est équivalent à
+ <literal><version type="timestamp"></literal>.
+ </para>
+ </sect2>
+ <sect2 id="mapping-declaration-property" revision="4">
+ <title>property</title>
+ <para>
+ L'élément <literal><property></literal> déclare une propriété de la classe au sens JavaBean.
+ </para>
+ <programlistingco>
+ <areaspec>
+ <area id="property1" coords="2 45"/>
+ <area id="property2" coords="3 45"/>
+ <area id="property3" coords="4 45"/>
+ <areaset id="property4-5" coords="">
+ <area id="property4" coords="5 45"/>
+ <area id="property5" coords="6 45"/>
+ </areaset>
+ <area id="property6" coords="7 45"/>
+ <area id="property7" coords="8 45"/>
+ <area id="property8" coords="9 45"/>
+ <area id="property9" coords="10 45"/>
+ <area id="property10" coords="11 45"/>
+ <area id="property11" coords="12 45"/>
+ <area id="property12" coords="13 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<property
+ name="propertyName"
+ column="column_name"
+ type="typename"
+ update="true|false"
+ insert="true|false"
+ formula="arbitrary SQL expression"
+ access="field|property|ClassName"
+ lazy="true|false"
+ unique="true|false"
+ not-null="true|false"
+ optimistic-lock="true|false"
+ generated="never|insert|always"
+ node="element-name|@attribute-name|element/@attribute|."
+ index="index_name"
+ unique_key="unique_key_id"
+ length="L"
+ precision="P"
+ scale="S"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="property1">
+ <para>
+ <literal>name</literal> : nom de la propriété, avec une lettre initiale en minuscule.
+ </para>
+ </callout>
+ <callout arearefs="property2">
+ <para>
+ <literal>column</literal> (optionnel - par défaut au nom de la propriété) : le nom
+ de la colonne mappée. Cela peut aussi être indiqué dans le(s) sous-élément(s)
+ <literal><column></literal>.
+ </para>
+ </callout>
+ <callout arearefs="property3">
+ <para>
+ <literal>type</literal> (optionnel) : nom indiquant le type Hibernate.
+ </para>
+ </callout>
+ <callout arearefs="property4-5">
+ <para>
+ <literal>update, insert</literal> (optionnel - par défaut à <literal>true</literal>) :
+ indique que les colonnes mappées devraient être incluses dans des <literal>UPDATE</literal> SQL
+ et/ou des <literal>INSERT</literal>. Mettre les deux à <literal>false</literal>
+ empêche la propagation en base de données (utile si vous savez qu'un trigger affectera la valeur à la colonne).
+ </para>
+ </callout>
+ <callout arearefs="property6">
+ <para>
+ <literal>formula</literal> (optionnel) : une expression SQL qui définit la valeur pour une propriété
+ <emphasis>calculée</emphasis>. Les propriétés calculées ne possède pas leur propre mapping.
+ </para>
+ </callout>
+ <callout arearefs="property7">
+ <para>
+ <literal>access</literal> (optionnel - par défaut à <literal>property</literal>): Stratégie que Hibernate
+ doit utiliser pour accéder à cette valeur.
+ </para>
+ </callout>
+ <callout arearefs="property8">
+ <para>
+ <literal>lazy</literal> (optionnel - par défaut à <literal>false</literal>): Indique
+ que cette propriété devrait être chargée en différé (lazy loading) quand on accède à la variable
+ d'instance pour la première fois.
+ </para>
+ </callout>
+ <callout arearefs="property9">
+ <para>
+ <literal>unique</literal> (optionnel): Génère le DDL d'une contrainte d'unicité pour les colonnes.
+ Permet aussi d'en faire la cible d'un <literal>property-ref</literal>.
+ </para>
+ </callout>
+ <callout arearefs="property10">
+ <para>
+ <literal>not-null</literal> (optionnel): Génère le DDL d'une contrainte de non nullité pour les colonnes.
+ </para>
+ </callout>
+ <callout arearefs="property11">
+ <para>
+ <literal>optimistic-lock</literal> (optionnel - par défaut à <literal>true</literal>):
+ Indique que les mises à jour de cette propriété peuvent ou non nécessiter l'acquisition
+ d'un verrou optimiste. En d'autres termes, cela détermine s'il est nécessaire d'incrémenter
+ un numéro de version quand cette propriété est marquée obsolète (dirty).
+ </para>
+ </callout>
+ <callout arearefs="property12">
+ <para>
+ <literal>generated</literal> (optional - défaut à<literal>never</literal>):
+ Indique que la valeur de ce timestamp est générée par la base de données
+ cf. <xref linkend="mapping-generated">generated properties</xref>.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+ <para>
+ <emphasis>typename</emphasis> peut être:
+ </para>
+ <orderedlist spacing="compact">
+ <listitem>
+ <para>
+ Nom d'un type basique Hibernate (ex: <literal>integer, string, character,
+ date, timestamp, float, binary, serializable, object, blob</literal>).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Nom d'une classe Java avec un type basique par défaut (ex: <literal>int, float,
+ char, java.lang.String, java.util.Date, java.lang.Integer, java.sql.Clob</literal>).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Nom d'une classe Java sérialisable.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Nom d'une classe ayant un type spécifique (ex: <literal>com.illflow.type.MyCustomType</literal>).
+ </para>
+ </listitem>
+ </orderedlist>
+ <para>
+ Si vous n'indiquez pas un type, Hibernate utlisera la réflexion sur le nom de la propriété
+ pour tenter de trouver le type Hibernate correct. Hibernate essayera d'interprêter
+ le nom de la classe retournée par le getter de la propriété en utilisant les régles 2, 3,
+ 4 dans cet ordre. Cependant, ce n'est pas toujours suffisant. Dans certains cas vous aurez
+ encore besoin de l'attribut <literal>type</literal> (Par exemple, pour distinguer
+ <literal>Hibernate.DATE</literal> et <literal>Hibernate.TIMESTAMP</literal>, ou pour préciser
+ un type spécifique).
+ </para>
+ <para>
+ L'attribut <literal>access</literal> permet de contrôler comment Hibernate accèdera
+ à la propriété à l'exécution. Par défaut, Hibernate utilisera les méthodes set/get.
+ Si vous indiquez <literal>access="field"</literal>, Hibernate ignorera les getter/setter
+ et accèdera à la propriété directement en utilisant la réflexion. Vous pouvez spécifier
+ votre propre stratégie d'accès aux propriété en donnant une classe qui implémente l'interface
+ <literal>org.hibernate.property.PropertyAccessor</literal>.
+ </para>
+ <para>
+ Une fonctionnalité particulièrement intéressante est les propriétés dérivées.
+ Ces propriétés sont par définition en lecture seule, la valeur de la propriété est calculée au chargement.
+ Le calcul est déclaré comme une expression SQL, qui se traduit par une sous-requête <literal>SELECT</literal>
+ dans la requête SQL qui charge une instance :
+ </para>
+ <programlisting><![CDATA[
+<property name="totalPrice"
+ formula="( SELECT SUM (li.quantity*p.price) FROM LineItem li, Product p
+ WHERE li.productId = p.productId
+ AND li.customerId = customerId
+ AND li.orderNumber = orderNumber )"/>]]></programlisting>
+ <para>
+ Remarquez que vous pouvez référencer la propre table des entités en ne déclarant pas un
+ alias sur une colonne particulière (<literal>customerId</literal> dans l'exemple donné).
+ Notez aussi que vous pouvez utiliser le sous-élément de mapping <literal><formula></literal>
+ plutôt que d'utiliser l'attribut si vous le souhaitez.
+ </para>
+ </sect2>
+ <sect2 id="mapping-declaration-manytoone" revision="5">
+ <title>many-to-one</title>
+ <para> Une association ordinaire vers une autre classe persistante est déclarée en utilisant
+ un élément <literal>many-to-one</literal>. Le modèle relationnel est une association
+ de type many-to-one : une clef étrangère dans une table référence la ou les clef(s) primaire(s) dans la table cible.
+ </para>
+ <programlistingco>
+ <areaspec>
+ <area id="manytoone1" coords="2 60"/>
+ <area id="manytoone2" coords="3 60"/>
+ <area id="manytoone3" coords="4 60"/>
+ <area id="manytoone4" coords="5 60"/>
+ <area id="manytoone5" coords="6 60"/>
+ <areaset id="manytoone6-7" coords="">
+ <area id="manytoone6" coords="7 60"/>
+ <area id="manytoone7" coords="8 60"/>
+ </areaset>
+ <area id="manytoone8" coords="9 60"/>
+ <area id="manytoone9" coords="10 60"/>
+ <area id="manytoone10" coords="11 60"/>
+ <area id="manytoone11" coords="12 60"/>
+ <area id="manytoone12" coords="13 60"/>
+ <area id="manytoone13" coords="14 60"/>
+ <area id="manytoone14" coords="15 60"/>
+ <area id="manytoone14" coords="15 70"/>
+ <area id="manytoone15" coords="16 70"/>
+ <area id="manytoone16" coords="17 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<many-to-one
+ name="propertyName"
+ column="column_name"
+ class="ClassName"
+ cascade="cascade_style"
+ fetch="join|select"
+ update="true|false"
+ insert="true|false"
+ property-ref="propertyNameFromAssociatedClass"
+ access="field|property|ClassName"
+ unique="true|false"
+ not-null="true|false"
+ optimistic-lock="true|false"
+ lazy="proxy|no-proxy|false"
+ not-found="ignore|exception"
+ entity-name="EntityName"
+ formula="arbitrary SQL expression"
+ node="element-name|@attribute-name|element/@attribute|."
+ embed-xml="true|false"
+ index="index_name"
+ unique_key="unique_key_id"
+ foreign-key="foreign_key_name"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="manytoone1">
+ <para>
+ <literal>name</literal> : Nom de la propriété.
+ </para>
+ </callout>
+ <callout arearefs="manytoone2">
+ <para>
+ <literal>column</literal> (optionnel) : Le nom de la clef étrangère. Cela peut être
+ aussi indiqué avec le sous-élément <literal><column></literal>.
+ </para>
+ </callout>
+ <callout arearefs="manytoone3">
+ <para>
+ <literal>class</literal> (optionnel - par défaut le type de la propriété déterminé
+ par réflexion) : Le nom de la classe associée.
+ </para>
+ </callout>
+ <callout arearefs="manytoone4">
+ <para>
+ <literal>cascade</literal> (optionnel) : Indique quelles opérations doivent
+ être propagées de l'objet père vers les objets associés.
+ </para>
+ </callout>
+ <callout arearefs="manytoone5">
+ <para>
+ <literal>fetch</literal> (optionnel - par défaut à <literal>select</literal>) :
+ Choisit entre le chargement de type outer-join ou le chargement par select successifs.
+ </para>
+ </callout>
+ <callout arearefs="manytoone6-7">
+ <para>
+ <literal>update, insert</literal> (optionnel - par défaut à <literal>true</literal>) :
+ indique que les colonnes mappées devraient être incluses dans des <literal>UPDATE</literal> SQL
+ et/ou des <literal>INSERT</literal>. Mettre les deux à <literal>false</literal>
+ empêche la propagation en base de données (utile si vous savez qu'un trigger affectera la valeur à la colonne).
+ </para>
+ </callout>
+ <callout arearefs="manytoone8">
+ <para>
+ <literal>property-ref</literal> : (optionnel) Le nom d'une propriété de la classe
+ associée qui est liée à cette clef étrangère. Si ce n'est pas spécifié, la clef primaire de la
+ classe associée est utilisée.
+ </para>
+ </callout>
+ <callout arearefs="manytoone9">
+ <para>
+ <literal>access</literal> (optionnel - par défaut à <literal>property</literal>) : La
+ stratégie à utiliser par Hibernate pour accéder à la valeur de cette propriété.
+ </para>
+ </callout>
+ <callout arearefs="manytoone10">
+ <para>
+ <literal>unique</literal> (optionnel) : Génère le DDL d'une contrainte d'unicité pour la clef étrangère.
+ Permet aussi d'en faire la cible d'un <literal>property-ref</literal>. Cela permet de créer une véritable
+ association one-to-one.
+ </para>
+ </callout>
+ <callout arearefs="manytoone11">
+ <para>
+ <literal>not-null</literal> (optionnel) : Génère le DDL pour une contrainte de non nullité pour la clef étrangère.
+ </para>
+ </callout>
+ <callout arearefs="manytoone12">
+ <para>
+ <literal>optimistic-lock</literal> (optionnel - par défaut à <literal>true</literal>) :
+ Indique que les mises à jour de cette propriété requièrent ou non l'acquisition
+ d'un verrou optimiste. En d'autres termes, détermine si un incrément de version doit
+ avoir lieu quand la propriété est marquée obsolète (dirty).
+ </para>
+ </callout>
+ <callout arearefs="manytoone13">
+ <para>
+ <literal>lazy</literal> (optionnel - par défaut à <literal>false</literal>) : Indique
+ que cette propriété doit être chargée en différé (lazy loading) au premier accès
+ à la variable d'instance (nécessite une instrumentation du bytecode lors de la phase
+ de construction). Remarquez que cela n'influence pas le comportement du proxy
+ Hibernate - comme l'attribut <literal>lazy</literal> sur des classes ou des mappings
+ de collections, mais utilise l'interception pour le chargement différé.
+ <literal>lazy="false"</literal> indique que l'association sera toujours chargée.
+ </para>
+ </callout>
+ <callout arearefs="manytoone14">
+ <para>
+ <literal>not-found</literal> (optionnel - par défaut à <literal>exception</literal>) :
+ Indique comment les clefs étrangères qui référencent des lignes manquantes doivent être manipulées :
+ <literal>ignore</literal> traitera une ligne manquante comme une association nulle.
+ </para>
+ </callout>
+ <callout arearefs="manytoone15">
+ <para>
+ <literal>entity-name</literal> (optionnel) : Le nom de l'entité de la classe associée.
+ </para>
+ </callout>
+ <callout arearefs="manytoone16">
+ <para>
+ <literal>formula</literal> (optionnel) : une expression SQL qui définit la valeur
+ pour une clé étrangère calculée.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+ <para>
+ Donner une valeur significative à l'attribut <literal>cascade</literal> autre que
+ <literal>none</literal> propagera certaines opérations à l'objet associé. Les valeurs
+ significatives sont les noms des opérations Hibernate basiques,
+ <literal>persist, merge, delete, save-update, evict, replicate, lock,
+ refresh</literal>, ainsi que les valeurs spéciales <literal>delete-orphan</literal>
+ et <literal>all</literal> et des combinaisons de noms d'opérations séparées par des virgules,
+ comme par exemple <literal>cascade="persist,merge,evict"</literal> ou
+ <literal>cascade="all,delete-orphan"</literal>. Voir <xref linkend="objectstate-transitive"/>
+ pour une explication complète.
+ Notez que les assocations many-to-one et one-to-one ne supportent pas orphan delete.
+ </para>
+
+ <para>
+ Une déclaration <literal>many-to-one</literal> typique est aussi simple que :
+ </para>
+ <programlisting><![CDATA[<many-to-one name="product" class="Product" column="PRODUCT_ID"/>]]></programlisting>
+ <para>
+ L'attribut <literal>property-ref</literal> devrait être utilisé pour mapper seulement des données
+ provenant d'un ancien système où les clefs étrangères font référence à une clef unique de la table associée
+ et qui n'est pas la clef primaire. C'est un cas de mauvaise conception relationnelle.
+ Par exemple, supposez que la classe <literal>Product</literal> a un numéro de série unique qui n'est pas
+ la clef primaire. (L'attribut <literal>unique</literal> contrôle la génération DDL par Hibernate avec
+ l'outil SchemaExport.)
+ </para>
+ <programlisting><![CDATA[<property name="serialNumber" unique="true" type="string" column="SERIAL_NUMBER"/>]]></programlisting>
+ <para>
+ Ainsi le mapping pour <literal>OrderItem</literal> peut utiliser :
+ </para>
+ <programlisting><![CDATA[<many-to-one name="product" property-ref="serialNumber" column="PRODUCT_SERIAL_NUMBER"/>]]></programlisting>
+ <para>
+ bien que ce ne soit certainement pas encouragé.
+ </para>
+ <para>
+ Si la clef unique référencée comprend des propriétés multiples de l'entité associée, vous devez mapper
+ ces propriétés à l'intérieur d'un élément <literal><properties></literal>.
+ </para>
+ </sect2>
+ <sect2 id="mapping-declaration-onetoone" revision="3">
+ <title>one-to-one</title>
+ <para>
+ Une association one-to-one vers une autre classe persistante est déclarée avec l'élément
+ <literal>one-to-one</literal>.
+ </para>
+ <programlistingco>
+ <areaspec>
+ <area id="onetoone1" coords="2 60"/>
+ <area id="onetoone2" coords="3 60"/>
+ <area id="onetoone3" coords="4 60"/>
+ <area id="onetoone4" coords="5 60"/>
+ <area id="onetoone5" coords="6 60"/>
+ <area id="onetoone6" coords="7 60"/>
+ <area id="onetoone7" coords="8 60"/>
+ <area id="onetoone8" coords="9 60"/>
+ <area id="onetoone9" coords="10 70"/>
+ <area id="onetoone10" coords="11 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<one-to-one
+ name="propertyName"
+ class="ClassName"
+ cascade="cascade_style"
+ constrained="true|false"
+ fetch="join|select"
+ property-ref="propertyNameFromAssociatedClass"
+ access="field|property|ClassName"
+ formula="any SQL expression"
+ entity-name="EntityName"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="onetoone1">
+ <para>
+ <literal>name</literal> : Le nom de la propriété.
+ </para>
+ </callout>
+ <callout arearefs="onetoone2">
+ <para>
+ <literal>class</literal> (optionnel - par défaut du type de la propriété
+ déterminé par réflexion) : Le nom de la classe associée.
+ </para>
+ </callout>
+ <callout arearefs="onetoone3">
+ <para>
+ <literal>cascade</literal> (optionnel) : Indique quelles opérations doivent
+ être cascadées de l'objet père vers l'objet associé.
+ </para>
+ </callout>
+ <callout arearefs="onetoone4">
+ <para>
+ <literal>constrained</literal> (optionnel) : Indique qu'une contrainte de clef étrangère
+ sur la clef primaire de la table mappée référence la table de la classe associée.
+ Cette option affecte l'ordre dans lequel chaque <literal>save()</literal> et chaque
+ <literal>delete()</literal> sont cascadés et détermine si l'association peut utiliser un proxy
+ (aussi utilisé par l'outil d'export de schéma).
+ </para>
+ </callout>
+ <callout arearefs="onetoone5">
+ <para>
+ <literal>fetch</literal> (optionnel - par défaut à <literal>select</literal>) :
+ Choisit entre récupération par jointure externe ou select séquentiel.
+ </para>
+ </callout>
+ <callout arearefs="onetoone6">
+ <para>
+ <literal>property-ref</literal> (optionnel) : Le nom de la propriété de la classe associée qui est jointe à la clef
+ primaire de cette classe. Si ce n'est pas spécifié, la clef primaire de la classe associée est utilisée.
+ </para>
+ </callout>
+ <callout arearefs="onetoone7">
+ <para>
+ <literal>access</literal> (optionnel - par défaut à <literal>property</literal>) :
+ La stratégie à utiliser par Hibernate pour accéder à la valeur de la propriété.
+ </para>
+ </callout>
+ <callout arearefs="onetoone8">
+ <para>
+ <literal>formula</literal> (optionnel) : Presque toutes les associations one-to-one pointent sur la clef primaire
+ de l'entité propriétaire. Dans les rares cas différents, vous devez donner une ou plusieurs
+ autres colonnes ou expression à joindre par une formule SQL (voir <literal>org.hibernate.test.onetooneformula</literal> pour un exemple).
+ </para>
+ </callout>
+ <callout arearefs="onetoone9">
+ <para>
+ <literal>lazy</literal> (optionnel - par défaut <literal>proxy</literal>) :
+ Par défaut, les associations simples sont soumise à proxy. <literal>lazy="no-proxy"</literal>
+ spécifie que la propriété doit être chargée à la demande au premier accès à l'instance.
+ (nécessite l'intrumentation du bytecode à la construction).
+ <literal>lazy="false"</literal> indique que l'association sera toujours chargée
+ agressivement. <emphasis>Notez que si <literal>constrained="false"</literal>,
+ l'utilisation de proxy est impossible et Hibernate chargera automatiquement l'association !</emphasis>
+ </para>
+ </callout>
+ <callout arearefs="onetoone10">
+ <para>
+ <literal>entity-name</literal> (optional) : The entity name of the associated class.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+ <para>
+ Il existe deux types d'associations one-to-one :
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ associations par clef primaire
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ association par clef étrangère unique
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+ Les associations par clef primaire ne nécessitent pas une colonne supplémentaire en table ; si deux lignes sont
+ liés par l'association alors les deux lignes de la table partagent la même valeur de clef primaire. Donc si vous
+ voulez que deux objets soient liés par une association par clef primaire, vous devez faire en sorte qu'on leur
+ assigne la même valeur d'identifiant !
+ </para>
+ <para>
+ Pour une association par clef primaire, ajoutez les mappings suivants à <literal>Employee</literal> et
+ <literal>Person</literal>, respectivement.
+ </para>
+ <programlisting><![CDATA[<one-to-one name="person" class="Person"/>]]></programlisting>
+ <programlisting><![CDATA[<one-to-one name="employee" class="Employee" constrained="true"/>]]></programlisting>
+ <para>
+ Maintenant, vous devez faire en sorte que les clefs primaires des lignes liées dans les tables PERSON et EMPLOYEE
+ sont égales. On utilise une stratégie Hibernate spéciale de génération d'identifiants appelée
+ <literal>foreign</literal> :
+ </para>
+ <programlisting><![CDATA[<class name="person" table="PERSON">
+ <id name="id" column="PERSON_ID">
+ <generator class="foreign">
+ <param name="property">employee</param>
+ </generator>
+ </id>
+ ...
+ <one-to-one name="employee"
+ class="Employee"
+ constrained="true"/>
+</class>]]></programlisting>
+ <para>
+ Une instance fraîchement enregistrée de <literal>Person</literal> se voit alors assignée la même valeur
+ de clef primaire que l'instance de <literal>Employee</literal> référencée par la propriété <literal>employee</literal>
+ de cette <literal>Person</literal>.
+ </para>
+ <para>
+ Alternativement, une clef étrangère avec contrainte d'unicité de <literal>Employee</literal> vers
+ <literal>Person</literal> peut être indiquée ainsi :
+ </para>
+ <programlisting><![CDATA[<many-to-one name="person" class="Person" column="PERSON_ID" unique="true"/>]]></programlisting>
+ <para>
+ Et cette association peut être rendue bidirectionnelle en ajoutant ceci au mapping de <literal>Person</literal> :
+ </para>
+ <programlisting><![CDATA[<one-to-one name"employee" class="Employee" property-ref="person"/>]]></programlisting>
+ </sect2>
+
+ <sect2 id="mapping-declaration-naturalid">
+ <title>natural-id</title>
+
+ <programlisting><![CDATA[<natural-id mutable="true|false"/>
+ <property ... />
+ <many-to-one ... />
+ ......
+</natural-id>]]></programlisting>
+
+ <para>
+ Bien que nous recommandions l'utilisation de clé primaire générée, vous devriez toujours
+ essayer d'identifier des clé métier (naturelles) pour toutes vos entités. Une clé naturelle
+ est une propriété ou une combinaison de propriétés uniques et non nulles. Si elle est aussi
+ immuable, c'est encore mieux. Mappez les propriétés de la clé naturelle dans l'élément
+ <literal><natural-id></literal>. Hibernate générera la clé unique nécessaire et les contraintes
+ de non-nullité, et votre mapping s'auto-documentera.
+ </para>
+
+ <para>
+ Nous vous recommandons fortement d'implémenter <literal>equals()</literal> et
+ <literal>hashCode()</literal> pour comparer les clés naturelles de l'entité.
+ </para>
+
+ <para>
+ Ce mapping n'est pas destiné à être utilisé avec des entités qui ont des clés naturelles.
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>mutable</literal> (optionel, par défaut à <literal>false</literal>) :
+ Par défaut, les identifiants naturels sont supposés être immuable (constants).
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect2>
+ <sect2 id="mapping-declaration-component" revision="2">
+ <title>component, dynamic-component</title>
+ <para>
+ L'élément <literal><component></literal> mappe les propriétés d'un objet fils
+ aux colonnes d'une classe parente. Les composants peuvent en retour déclarer leurs propres
+ propriétés, composants ou collections. Voir "Components" plus bas.
+ </para>
+ <programlistingco>
+ <areaspec>
+ <area id="component1" coords="2 45"/>
+ <area id="component2" coords="3 45"/>
+ <area id="component3" coords="4 45"/>
+ <area id="component4" coords="5 45"/>
+ <area id="component5" coords="6 45"/>
+ <area id="component6" coords="7 45"/>
+ <area id="component7" coords="8 45"/>
+ <area id="component8" coords="9 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<component
+ name="propertyName"
+ class="className"
+ insert="true|false"
+ update="true|false"
+ access="field|property|ClassName"
+ lazy="true|false"
+ optimistic-lock="true|false"
+ unique="true|false"
+>
+
+ <property ...../>
+ <many-to-one .... />
+ ........
+</component>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="component1">
+ <para>
+ <literal>name</literal> : Nom de la propriété
+ </para>
+ </callout>
+ <callout arearefs="component2">
+ <para>
+ <literal>class</literal> (optionnel - par défaut au type de la propriété déterminé par réflexion) :
+ le nom de la classe (fille) du composant.
+ </para>
+ </callout>
+ <callout arearefs="component3">
+ <para>
+ <literal>insert</literal> : Est ce que les colonnes mappées apparaissent dans les
+ <literal>INSERT</literal>s ?
+ </para>
+ </callout>
+ <callout arearefs="component4">
+ <para>
+ <literal>update</literal>: Est ce que les colonnes mappées apparaissent dans les
+ <literal>UPDATE</literal>s ?
+ </para>
+ </callout>
+ <callout arearefs="component5">
+ <para>
+ <literal>access</literal> (optionnel - par défaut à <literal>property</literal>) :
+ La stratégie que Hibernate doit utiliser pour accéder à la valeur de cette propriété.
+ </para>
+ </callout>
+ <callout arearefs="component6">
+ <para>
+ <literal>lazy</literal> (optionnel - par défaut à <literal>false</literal>) :
+ Indique que ce composant doit être chargé au premier accès
+ à la variable d'instance (nécessite une instrumentation du bytecode au moment du build).
+ </para>
+ </callout>
+ <callout arearefs="component7">
+ <para>
+ <literal>optimistic-lock</literal> (optionnel - par défaut à <literal>true</literal>) :
+ Indique que les mises à jour sur ce composant nécessitent ou non l'acquisition d'un
+ verrou optimiste. En d'autres termes, cela détermine si une incrémentation de version
+ doit avoir lieu quand la propriété est marquée obsolète (dirty).
+ </para>
+ </callout>
+ <callout arearefs="component8">
+ <para>
+ <literal>unique</literal> (optionnel - par défaut à <literal>false</literal>) :
+ Indique qu'une contrainte d'unicité existe sur toutes les colonnes mappées de ce composant.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+ <para>
+ Les tags fils <literal><property></literal> mappent les propriétés
+ de la classe fille sur les colonnes de la table.
+ </para>
+ <para>
+ L'élément <literal><component></literal> permet de déclarer sous-élément <literal><parent></literal> qui associe une propriété
+ de la classe composant comme une référence arrière vers l'entité contenante.
+ </para>
+ <para>
+ L'élément <literal><dynamic-component></literal> permet à une <literal>Map</literal> d'être mappée
+ comme un composant, quand les noms de la propriété font référence aux clefs de cette Map, voir
+ <xref linkend="components-dynamic"/>.
+ </para>
+ </sect2>
+ <sect2 id="mapping-declaration-properties" revision="2">
+ <title>properties</title>
+ <para>
+ L'élément <literal><properties></literal> permet la définition d'un groupement logique nommé
+ des propriétés d'une classe. L'utilisation la plus importante de cette construction est la possibilité
+ pour une combinaison de propriétés d'être la cible d'un <literal>property-ref</literal>. C'est aussi
+ un moyen pratique de définir une contrainte d'unicité multi-colonnes.
+ </para>
+ <programlistingco>
+ <areaspec>
+ <area id="properties1" coords="2 45"/>
+ <area id="properties2" coords="3 45"/>
+ <area id="properties3" coords="4 45"/>
+ <area id="properties4" coords="5 45"/>
+ <area id="properties5" coords="6 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<properties
+ name="logicalName"
+ insert="true|false"
+ update="true|false"
+ optimistic-lock="true|false"
+ unique="true|false"
+>
+
+ <property ...../>
+ <many-to-one .... />
+ ........
+</properties>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="properties1">
+ <para>
+ <literal>name</literal> : Le nom logique d'un regroupement et
+ <emphasis>non</emphasis> le véritable nom d'une propriété.
+ </para>
+ </callout>
+ <callout arearefs="properties2">
+ <para>
+ <literal>insert</literal> : Est-ce que les colonnes mappées apparaissent dans les
+ <literal>INSERT</literal>s ?
+ </para>
+ </callout>
+ <callout arearefs="properties3">
+ <para>
+ <literal>update</literal> : Est-ce que les colonnes mappées apparaissent dans les
+ <literal>UPDATE</literal>s ?
+ </para>
+ </callout>
+ <callout arearefs="properties4">
+ <para>
+ <literal>optimistic-lock</literal> (optionnel - par défaut à <literal>true</literal>) :
+ Indique que les mises à jour sur ce composant nécessitent ou non l'acquisition d'un
+ verrou optimiste. En d'autres termes, cela détermine si une incrémentation
+ de version doit avoir lieu quand la propriété est marquée obsolète (dirty).
+ </para>
+ </callout>
+ <callout arearefs="properties5">
+ <para>
+ <literal>unique</literal> (optionnel - par défaut à <literal>false</literal>) :
+ Indique qu'une contrainte d'unicité existe sur toutes les colonnes mappées de ce composant.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+ <para>
+ Par exemple, si nous avons le mapping de <literal><properties></literal> suivant :
+ </para>
+ <programlisting><![CDATA[<class name="Person">
+ <id name="personNumber"/>
+ ...
+ <properties name="name"
+ unique="true" update="false">
+ <property name="firstName"/>
+ <property name="initial"/>
+ <property name="lastName"/>
+ </properties>
+</class>]]></programlisting>
+ <para>
+ Alors nous pourrions avoir une association sur des données d'un ancien système (legacy) qui font référence
+ à cette clef unique de la table <literal>Person</literal> au lieu de la clef primaire :
+ </para>
+ <programlisting><![CDATA[<many-to-one name="person"
+ class="Person" property-ref="name">
+ <column name="firstName"/>
+ <column name="initial"/>
+ <column name="lastName"/>
+</many-to-one>]]></programlisting>
+ <para>
+ Nous ne recommandons pas l'utilisation de ce genre de chose en dehors du contexte de mapping de données héritées
+ d'anciens systèmes.
+ </para>
+ </sect2>
+ <sect2 id="mapping-declaration-subclass" revision="4">
+ <title>subclass</title>
+ <para>
+ Pour finir, la persistance polymorphique nécessite la déclaration de chaque sous-classe de la classe persistante de base.
+ pour la stratégie de mapping de type table-per-class-hierarchy, on utilise la déclaration
+ <literal><subclass></literal>.
+ </para>
+ <programlistingco>
+ <areaspec>
+ <area id="subclass1" coords="2 55"/>
+ <area id="subclass2" coords="3 55"/>
+ <area id="subclass3" coords="4 55"/>
+ <area id="subclass4" coords="5 55"/>
+ </areaspec>
+ <programlisting><![CDATA[<subclass
+ name="ClassName"
+ discriminator-value="discriminator_value"
+ proxy="ProxyInterface"
+ lazy="true|false"
+ dynamic-update="true|false"
+ dynamic-insert="true|false"
+ entity-name="EntityName"
+ node="element-name"
+ extends="SuperclassName">
+
+ <property .... />
+ .....
+</subclass>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="subclass1">
+ <para>
+ <literal>name</literal> : Le nom complet de la sous-classe.
+ </para>
+ </callout>
+ <callout arearefs="subclass2">
+ <para>
+ <literal>discriminator-value</literal> (optionnel - par défaut le nom de la classe) :
+ une valeur qui distingue les différentes sous-classes.
+ </para>
+ </callout>
+ <callout arearefs="subclass3">
+ <para>
+ <literal>proxy</literal> (optionnel) : Indique une classe ou interface à utiliser pour les chargements
+ à la demande des proxies (lazy).
+ </para>
+ </callout>
+ <callout arearefs="subclass4">
+ <para>
+ <literal>lazy</literal> (optionnel, par défaut à <literal>true</literal>) : Spécifier
+ <literal>lazy="false"</literal> désactive l'utilisation du chargement à la demande (lazy).
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+ <para>
+ Chaque sous-classe devrait déclarer ses propres propriétés persistantes et sous-classes.
+ Les propriétés <literal><version></literal> et <literal><id></literal>
+ sont implicitement hérités de la classe de base. Chaque sous-classe dans une hiérarchie doit
+ définir une unique <literal>discriminator-value</literal>. Si aucune n'est spécifiée,
+ le nom complet de la classe Java est utilisé.
+ </para>
+ <para>
+ Pour plus d'infos sur le mapping d'héritage, voir <xref linkend="inheritance"/>.
+ </para>
+ <programlisting><![CDATA[
+<hibernate-mapping>
+ <subclass name="DomesticCat" extends="Cat" discriminator-value="D">
+ <property name="name" type="string"/>
+ </subclass>
+</hibernate-mapping>]]></programlisting>
+ <para>
+ Pour des informations sur les mappings d'héritage, voir <xref linkend="inheritance"/>.
+ </para>
+ </sect2>
+ <sect2 id="mapping-declaration-joinedsubclass" revision="3">
+ <title>joined-subclass</title>
+ <para>
+ Une autre façon possible de faire est la suivante, chaque sous-classe peut être mappée vers sa propre table (stratégie
+ de mapping de type table-per-subclass). L'état hérité est récupéré en joignant la table de la super-classe.
+ L'élément <literal><joined-subclass></literal> est utilisé.
+ </para>
+ <programlistingco>
+ <areaspec>
+ <area id="joinedsubclass1" coords="2 45"/>
+ <area id="joinedsubclass2" coords="3 45"/>
+ <area id="joinedsubclass3" coords="4 45"/>
+ <area id="joinedsubclass4" coords="5 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<joined-subclass
+ name="ClassName"
+ table="tablename"
+ proxy="ProxyInterface"
+ lazy="true|false"
+ dynamic-update="true|false"
+ dynamic-insert="true|false"
+ schema="schema"
+ catalog="catalog"
+ extends="SuperclassName"
+ persister="ClassName"
+ subselect="SQL expression"
+ entity-name="EntityName">
+
+ <key .... >
+
+ <property .... />
+ .....
+</joined-subclass>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="joinedsubclass1">
+ <para>
+ <literal>name</literal> : Le nom Java complet de la sous-classe.
+ </para>
+ </callout>
+ <callout arearefs="joinedsubclass2">
+ <para>
+ <literal>table</literal> : Le nom de la table de la sous-classe.
+ </para>
+ </callout>
+ <callout arearefs="joinedsubclass3">
+ <para>
+ <literal>proxy</literal> (optionnel) : Indique une classe ou interface pour le chargement différé des proxies.
+ </para>
+ </callout>
+ <callout arearefs="joinedsubclass4">
+ <para>
+ <literal>lazy</literal> (optionnel, par défaut à <literal>true</literal>) : Indiquer
+ <literal>lazy="false"</literal> désactive l'utilisation du chargement à la demande.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+ <para>
+ Aucune colonne discriminante n'est nécessaire pour cette stratégie de mapping. Cependant,
+ chaque sous-classe doit déclarer une colonne de table contenant l'objet identifiant qui utilise l'élément
+ <literal><key></literal>. Le mapping au début de ce chapitre serait ré-écrit ainsi :
+ </para>
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="eg">
+
+ <class name="Cat" table="CATS">
+ <id name="id" column="uid" type="long">
+ <generator class="hilo"/>
+ </id>
+ <property name="birthdate" type="date"/>
+ <property name="color" not-null="true"/>
+ <property name="sex" not-null="true"/>
+ <property name="weight"/>
+ <many-to-one name="mate"/>
+ <set name="kittens">
+ <key column="MOTHER"/>
+ <one-to-many class="Cat"/>
+ </set>
+ <joined-subclass name="DomesticCat" table="DOMESTIC_CATS">
+ <key column="CAT"/>
+ <property name="name" type="string"/>
+ </joined-subclass>
+ </class>
+
+ <class name="eg.Dog">
+ <!-- mapping for Dog could go here -->
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+ <para>
+ Pour des informations sur les mappings d'héritage, voir <xref linkend="inheritance"/>.
+ </para>
+ </sect2>
+ <sect2 id="mapping-declaration-unionsubclass" revision="2">
+ <title>union-subclass</title>
+ <para>
+ Une troisième option est de seulement mapper vers des tables les classes concrètes
+ d'une hiérarchie d'héritage, (stratégie de type table-per-concrete-class) où
+ chaque table définit tous les états persistants de la classe, y compris les états hérités.
+ Dans Hibernate il n'est absolument pas nécessaire de mapper explicitement de telles hiérarchies
+ d'héritage. Vous pouvez simplement mapper chaque classe avec une déclaration <literal><class></literal>
+ différente. Cependant, si vous souhaitez utiliser des associations polymorphiques (càd une association
+ vers la superclasse de la hiérarchie), vous devez utiliser le mapping <literal><union-subclass></literal>.
+ </para>
+ <programlistingco>
+ <areaspec>
+ <area id="unionsubclass1" coords="2 45"/>
+ <area id="unionsubclass2" coords="3 45"/>
+ <area id="unionsubclass3" coords="4 45"/>
+ <area id="unionsubclass4" coords="5 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<union-subclass
+ name="ClassName"
+ table="tablename"
+ proxy="ProxyInterface"
+ lazy="true|false"
+ dynamic-update="true|false"
+ dynamic-insert="true|false"
+ schema="schema"
+ catalog="catalog"
+ extends="SuperclassName"
+ abstract="true|false"
+ persister="ClassName"
+ subselect="SQL expression"
+ entity-name="EntityName">
+
+ <property .... />
+ .....
+</union-subclass>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="unionsubclass1">
+ <para>
+ <literal>name</literal> : Le nom Java complet de la sous-classe.
+ </para>
+ </callout>
+ <callout arearefs="unionsubclass2">
+ <para>
+ <literal>table</literal> : nom de la table de la sous-classe.
+ </para>
+ </callout>
+ <callout arearefs="unionsubclass3">
+ <para>
+ <literal>proxy</literal> (optionnel) : Indique une classe ou interface pour le chargement différé des proxies.
+ </para>
+ </callout>
+ <callout arearefs="unionsubclass4">
+ <para>
+ <literal>lazy</literal> (optionnel, par défaut à <literal>true</literal>) : Indiquer
+ <literal>lazy="false"</literal> désactive l'utilisation du chargement à la demande.
+ </para>
+
+ </callout>
+ </calloutlist>
+ </programlistingco>
+ <para>
+ Aucune colonne discriminante ou colonne clef n'est requise pour cette stratégie de mapping.
+ </para>
+ <para>
+ Pour des informations sur les mappings d'héritage, voir <xref linkend="inheritance"/>.
+ </para>
+ </sect2>
+ <sect2 id="mapping-declaration-join" revision="3">
+ <title>join</title>
+ <para>
+ En utilisant l'élément <literal><join></literal>, il est possible de mapper
+ des propriétés d'une classe sur plusieurs tables.
+ </para>
+ <programlistingco>
+ <areaspec>
+ <area id="join1" coords="2 50"/>
+ <area id="join2" coords="3 50"/>
+ <area id="join3" coords="4 50"/>
+ <area id="join4" coords="5 50"/>
+ <area id="join5" coords="6 50"/>
+ <area id="join6" coords="7 50"/>
+ </areaspec>
+ <programlisting><![CDATA[<join
+ table="tablename"
+ schema="owner"
+ catalog="catalog"
+ fetch="join|select"
+ inverse="true|false"
+ optionnel="true|false">
+
+ <key ... />
+
+ <property ... />
+ ...
+</join>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="join1">
+ <para>
+ <literal>table</literal> : Le nom de la table jointe.
+ </para>
+ </callout>
+ <callout arearefs="join2">
+ <para>
+ <literal>schema</literal> (optionnel) : court-circuite le nom de schéma spécifié par l'élément de base
+ <literal><hibernate-mapping></literal>.
+ </para>
+ </callout>
+ <callout arearefs="join3">
+ <para>
+ <literal>catalog</literal> (optionnel) : court-circuite le nom de catalogue spécifié par l'élément de base
+ <literal><hibernate-mapping></literal>.
+ </para>
+ </callout>
+ <callout arearefs="join4">
+ <para>
+ <literal>fetch</literal> (optionnel - par défaut à <literal>join</literal>) :
+ Si positionné à <literal>join</literal>, Hibernate utilisera une jointure interne pour charger
+ une <literal>jointure</literal> définie par une classe ou ses super-classes et une jointure externe
+ pour une <literal><jointure></literal> définie par une sous-classe.
+ Si positionné à <literal>select</literal> alors Hibernate utilisera un select séquentiel
+ pour une <literal><jointure></literal> définie sur une sous-classe, qui ne sera délivrée que
+ si une ligne se représente une instance de la sous-classe. Les jointures internes seront quand même
+ utilisées pour charger une <literal><jointure></literal> définie par une classe et ses super-classes.
+ </para>
+ </callout>
+ <callout arearefs="join5">
+ <para>
+ <literal>inverse</literal> (optionnel - par défaut à <literal>false</literal>) :
+ Si positionné à true, Hibernate n'essaiera pas d'insérer ou de mettre à jour les
+ propriétés définies par cette jointure.
+ </para>
+ </callout>
+ <callout arearefs="join6">
+ <para>
+ <literal>optionnel</literal> (optionnel - par défaut à <literal>false</literal>) :
+ Si positionné à true, Hibernate insèrera une ligne seulement si les propriétés définies
+ par cette jointure sont non-nulles et utilisera toujours une jointure externe pour charger les propriétés.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+ <para>
+ Par exemple, les informations d'adresse pour une personne peuvent être mappées vers une table
+ séparée (tout en préservant des sémantiques de type valeur pour toutes ses propriétés) :
+ </para>
+ <programlisting><![CDATA[<class name="Person"
+ table="PERSON">
+
+ <id name="id" column="PERSON_ID">...</id>
+
+ <join table="ADDRESS">
+ <key column="ADDRESS_ID"/>
+ <property name="address"/>
+ <property name="zip"/>
+ <property name="country"/>
+ </join>
+ ...]]></programlisting>
+ <para>
+ Cette fonctionnalité est souvent seulement utile pour les modèles de données
+ hérités d'anciens systèmes (legacy), nous recommandons d'utiliser moins de tables que de classes
+ et un modèle de domaine à granularité fine. Cependant, c'est utile
+ pour passer d'une stratégie de mapping d'héritage à une autre dans une hiérarchie simple ainsi qu'il est
+ expliqué plus tard.
+ </para>
+ </sect2>
+ <sect2 id="mapping-declaration-key">
+ <title>key</title>
+ <para>
+ Nous avons rencontré l'élément <literal><key></literal> à plusieurs reprises maintenant.
+ Il apparaît partout que l'élément de mapping parent définit une jointure sur une nouvele table, et
+ définit la clef étrangère dans la table jointe, ce qui référence la clef primaire de la table d'origine.
+ </para>
+ <programlistingco>
+ <areaspec>
+ <area id="key1" coords="2 50"/>
+ <area id="key2" coords="3 50"/>
+ <area id="key3" coords="4 50"/>
+ <area id="key4" coords="5 50"/>
+ <area id="key5" coords="6 50"/>
+ <area id="key6" coords="7 50"/>
+ </areaspec>
+ <programlisting><![CDATA[<key
+ column="columnname"
+ on-delete="noaction|cascade"
+ property-ref="propertyName"
+ not-null="true|false"
+ update="true|false"
+ unique="true|false"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="key1">
+ <para>
+ <literal>column</literal> (optionnel) : Le nom de la colonne de la clef étrangère
+ Cela peut aussi être spécifié par l'élément(s) intégré(s) <literal><column></literal>.
+ </para>
+ </callout>
+ <callout arearefs="key2">
+ <para>
+ <literal>on-delete</literal> (optionnel, par défaut à <literal>noaction</literal>) :
+ Indique si la contrainte de clef étrangère possède la possibilité au niveau base de données
+ de suppression en cascade.
+ </para>
+ </callout>
+ <callout arearefs="key3">
+ <para>
+ <literal>property-ref</literal> (optionnel) : Indique que la clef étrangère fait
+ référence à des colonnes qui ne sont pas la clef primaire de la table d'origine
+ (Pour les données de systèmes legacy).
+ </para>
+ </callout>
+ <callout arearefs="key4">
+ <para>
+ <literal>not-null</literal> (optionnel) : Indique que les colonnes des clefs étrangères ne
+ peuvent pas être nulles (c'est implicite si la clef étrangère fait partie de la clef primaire).
+ </para>
+ </callout>
+ <callout arearefs="key5">
+ <para>
+ <literal>update</literal> (optionnel) : Indique que la clef étrangère ne devrait jamais être mise à jour
+ (implicite si celle-ci fait partie de la clef primaire).
+ </para>
+ </callout>
+ <callout arearefs="key6">
+ <para>
+ <literal>unique</literal> (optionnel) : Indique que la clef étrangère doit posséder une contrainte
+ d'unicité (implicite si la clef étrangère est aussi la clef primaire).
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+ <para>
+ Nous recommandons pour les systèmes où les suppressions doivent être performantes de définir toutes
+ les clefs <literal>on-delete="cascade"</literal>, ainsi Hibernate utilisera une contrainte
+ <literal>ON CASCADE DELETE</literal> au niveau base de données, plutôt que de nombreux
+ <literal>DELETE</literal> individuels. Attention, cette fonctionnalité court-circuite la stratégie
+ habituelle de verrou optimiste pour les données versionnées.
+ </para>
+ <para>
+ Les attributs <literal>not-null</literal> et <literal>update</literal> sont utiles pour
+ mapper une association one-to-many unidirectionnelle. Si vous mappez un one-to-many unidirectionnel
+ vers une clef étrangère non nulle, vous <emphasis>devez</emphasis> déclarer la colonne de la clef
+ en utilisant <literal><key not-null="true"></literal>.
+ </para>
+ </sect2>
+ <sect2 id="mapping-column" revision="4">
+ <title>éléments column et formula</title>
+ <para>
+ Tout élément de mapping qui accepte un attribut <literal>column</literal> acceptera alternativement
+ un sous-élément <literal><column></literal>. De façon identique, <literal><formula></literal>
+ est une alternative à l'attribut <literal>formula</literal>.
+ </para>
+ <programlisting><![CDATA[<column
+ name="column_name"
+ length="N"
+ precision="N"
+ scale="N"
+ not-null="true|false"
+ unique="true|false"
+ unique-key="multicolumn_unique_key_name"
+ index="index_name"
+ sql-type="sql_type_name"
+ check="SQL expression"/>]]></programlisting>
+ <programlisting><![CDATA[<formula>SQL expression</formula>]]></programlisting>
+ <para>
+ Les attributs <literal>column</literal> et <literal>formula</literal> peuvent même être combinés
+ au sein d'une même propriété ou mapping d'association pour exprimer, par exemple, des conditions
+ de jointure exotiques.
+ </para>
+ <programlisting><![CDATA[<many-to-one name="homeAddress" class="Address"
+ insert="false" update="false">
+ <column name="person_id" not-null="true" length="10"/>
+ <formula>'MAILING'</formula>
+</many-to-one>]]></programlisting>
+ </sect2>
+ <sect2 id="mapping-declaration-import">
+ <title>import</title>
+ <para>
+ Supposez que votre application possède deux classes persistantes du même nom, et vous ne voulez pas préciser
+ le nom Java complet (packages inclus) dans les queries Hibernate. Les classes peuvent alors être "importées"
+ explicitement plutôt que de compter sur <literal>auto-import="true"</literal>.Vous pouvez même importer
+ des classes et interfaces qui ne sont pas mappées explicitement.
+ </para>
+ <programlisting><![CDATA[<import class="java.lang.Object" rename="Universe"/>]]></programlisting>
+ <programlistingco>
+ <areaspec>
+ <area id="import1" coords="2 40"/>
+ <area id="import2" coords="3 40"/>
+ </areaspec>
+ <programlisting><![CDATA[<import
+ class="ClassName"
+ rename="ShortName"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="import1">
+ <para>
+ <literal>class</literal> : Nom Java complet de la classe.
+ </para>
+ </callout>
+ <callout arearefs="import2">
+ <para>
+ <literal>rename</literal> (optionnel - par défaut vaut le nom de la classe Java (sans package)) :
+ Nom pouvant être utilisé dans le langage de requête.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+ </sect2>
+ <sect2 id="mapping-types-anymapping" revision="2">
+ <title>any</title>
+ <para>
+ Il existe encore un type de mapping de propriété. L'élément de mapping <literal><any></literal>
+ définit une association polymorphique vers des classes de tables multiples. Ce type de mapping requiert
+ toujours plus d'une colonne. La première colonne contient le type de l'entité associée. Les colonnes
+ restantes contiennent l'identifiant. il est impossible de spécifier une contrainte de clef étrangère
+ pour ce type d'association, donc ce n'est certainement pas considéré comme le moyen habituel de mapper
+ des associations (polymorphiques). Vous devriez utiliser cela uniquement dans des cas particuliers
+ (par exemple des logs d'audit, des données de session utilisateur, etc...).
+ </para>
+ <para>
+ L'attribut <literal>meta-type</literal> permet à l'application de spécifier un type personnalisé qui mappe
+ des valeurs de colonnes de le base de données sur des classes persistantes qui ont un attribut identifiant
+ du type spécifié par <literal>id-type</literal>. Vous devez spécifier le mapping à partir de valeurs du
+ méta-type sur les noms des classes.
+ </para>
+ <programlisting><![CDATA[<any name="being" id-type="long" meta-type="string">
+ <meta-value value="TBL_ANIMAL" class="Animal"/>
+ <meta-value value="TBL_HUMAN" class="Human"/>
+ <meta-value value="TBL_ALIEN" class="Alien"/>
+ <column name="table_name"/>
+ <column name="id"/>
+</any>]]></programlisting>
+ <programlistingco>
+ <areaspec>
+ <area id="any1" coords="2 50"/>
+ <area id="any2" coords="3 50"/>
+ <area id="any3" coords="4 50"/>
+ <area id="any4" coords="5 50"/>
+ <area id="any5" coords="6 50"/>
+ <area id="any6" coords="7 50"/>
+ </areaspec>
+ <programlisting><![CDATA[<any
+ name="propertyName"
+ id-type="idtypename"
+ meta-type="metatypename"
+ cascade="cascade_style"
+ access="field|property|ClassName"
+ optimistic-lock="true|false"
+>
+ <meta-value ... />
+ <meta-value ... />
+ .....
+ <column .... />
+ <column .... />
+ .....
+</any>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="any1">
+ <para>
+ <literal>name</literal> : le nom de la propriété.
+ </para>
+ </callout>
+ <callout arearefs="any2">
+ <para>
+ <literal>id-type</literal> : le type identifiant.
+ </para>
+ </callout>
+ <callout arearefs="any3">
+ <para>
+ <literal>meta-type</literal> (optionnel - par défaut à <literal>string</literal>) :
+ Tout type permis pour un mapping par discriminateur.
+ </para>
+ </callout>
+ <callout arearefs="any4">
+ <para>
+ <literal>cascade</literal> (optionnel - par défaut à <literal>none</literal>) :
+ le style de cascade.
+ </para>
+ </callout>
+ <callout arearefs="any5">
+ <para>
+ <literal>access</literal> (optionnel - par défaut à <literal>property</literal>) : La stratégie
+ à utiliser par Hibernate pour accéder à cette propriété.
+ </para>
+ </callout>
+ <callout arearefs="any6">
+ <para>
+ <literal>optimistic-lock</literal> (optionnel - par défaut à <literal>true</literal>) :
+ Indique que les mises à jour sur cette propriété nécessitent ou non l'acquisition d'un
+ verrou optimiste. En d'autres termes, définit si un incrément de version doit avoir lieu
+ quand cette propriété est marquée dirty.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+ </sect2>
+ </sect1>
+ <sect1 id="mapping-types">
+ <title>Hibernate Types</title>
+ <sect2 id="mapping-types-entitiesvalues" revision="1">
+ <title>Entités et valeurs</title>
+ <para>
+ Pour comprendre le comportement des différents objets Java par rapport au service
+ de persistance, nous avons besoin de les classer en deux groupes :
+ </para>
+ <para>
+ Une <emphasis>entité</emphasis> existe indépendamment de tout autre objet possédant
+ une référence vers l'entité. Comparez cela avec le modèle Java habituel où un objet
+ est supprimé par le garbage collector dès qu'il n'est plus référencé. Les entités
+ doivent être explicitement enregistrées et supprimées (sauf dans les cas où
+ sauvegardes et suppressions sont <emphasis>cascadées</emphasis> d'une entité mère
+ vers ses enfants). C'est différent du modèle ODMG de persistance par
+ atteignabilité - et correspond mieux à la façon dont les objets sont
+ habituellement utilisés dans des grands systèmes. Les entités permettent les références
+ circulaires et partagées. Elles peuvent aussi être versionnées.
+ </para>
+ <para>
+ L'état persistant d'une entité consiste en des références vers d'autres entités et
+ instances de types <emphasis>valeurs</emphasis>. Ces valeurs sont des types primitifs,
+ des collections (et non le contenu d'une collection), des composants de certains objets
+ immuables. Contrairement aux entités, les valeurs (et en particulier les collections et
+ composants) <emphasis>sont</emphasis> persistés par atteignabiliité. Comme les
+ valeurs (et types primitifs) sont persistés et supprimés avec l'entité qui les contient,
+ ils ne peuvent pas posséder leurs propres versions. Les valeurs n'ont pas d'identité
+ indépendantes, ainsi elles ne peuvent pas être partagées par deux entités ou collections.
+ </para>
+ <para>
+ Jusqu'à présent nous avons utilisé le terme "classe persistante" pour parler d'entités.
+ Nous allons continuer à faire ainsi. Cependant, au sens strict, toutes
+ les classes définies par un utilisateur possédant un état persistant ne sont pas des
+ entités. Un <emphasis>composant</emphasis> est une classe définie par un utilisateur
+ avec les caractéristiques d'une valeur. Une propriété Java de type <literal>java.lang.String</literal>
+ a aussi les caractéristiques d'une valeur.
+
+ <!-- FIXME Baptiste MATHUS : J'ai remis le texte anglais pour que si la version est publiée
+ comme ça, au moins le lecteur puisse essayer lui aussi de comprendre la version anglaise... -->
+
+ Given this definition, we can say that all types (classes) provided
+ by the JDK have value type semantics in Java, while user-defined types may
+ be mapped with entity or value type semantics. This decision is up to the
+ application developer. A good hint for an entity class in a domain model are
+ shared references to a single instance of that class, while composition or
+ aggregation usually translates to a value type.
+ </para>
+ <para>
+ Nous nous pencherons sur ces deux concepts tout au long de la documentation.
+ </para>
+ <para>
+ Le défi est de mapper les type Javas (et la définition des développeurs des
+ entités et valeurs types) sur les types du SQL ou des bases de données. Le pont
+ entre les deux systèmes est proposé par Hibernate : pour les entités nous utilisons
+ <literal><class></literal>, <literal><subclass></literal> et ainsi de suite.
+ Pour les types valeurs nous utilisons <literal><property></literal>,
+ <literal><component></literal>, etc., habituellement avec un attribut <literal>type</literal>.
+ La valeur de cet attribut est le nom d'un <emphasis>type de mapping</emphasis> Hibernate.
+ Hibernate propose de base de nombreux mappings (pour les types de valeurs standards du JDK).
+ Vous pouvez écrire vos propres types de mappings et implémenter aussi vos propres stratégies
+ de conversion, nous le verrons plus tard.
+ </para>
+ <para>
+ Tous les types proposés de base par Hibernate à part les collections autorisent la valeur null.
+ </para>
+ </sect2>
+ <sect2 id="mapping-types-basictypes" revision="3">
+ <title>Basic value types</title>
+ <para>
+ Les <emphasis>types basiques de mapping</emphasis> proposés de base peuvent grossièrement être
+ rangés dans les catégories suivantes :
+ <variablelist>
+ <varlistentry>
+ <term>
+ <literal>integer, long, short, float, double, character, byte,
+ boolean, yes_no, true_false</literal>
+ </term>
+ <listitem>
+ <para>
+ Les mappings de type des primitives Java ou leurs classes wrappers (ex: Integer
+ pour int) vers les types SQL (propriétaires) appropriés. <literal>boolean,
+ yes_no</literal>et <literal>true_false</literal> sont tous des alternatives
+ pour les types Java <literal>boolean</literal> ou <literal>java.lang.Boolean</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <literal>string</literal>
+ </term>
+ <listitem>
+ <para>
+ Mapping de type de <literal>java.lang.String</literal> vers
+ <literal>VARCHAR</literal> (ou le <literal>VARCHAR2</literal> Oracle).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <literal>date, time, timestamp</literal>
+ </term>
+ <listitem>
+ <para>
+ Mappings de type pour <literal>java.util.Date</literal> et ses sous-classes
+ vers les types SQL <literal>DATE</literal>, <literal>TIME</literal> et
+ <literal>TIMESTAMP</literal> (ou équivalent).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <literal>calendar, calendar_date</literal>
+ </term>
+ <listitem>
+ <para>
+ Mappings de type pour <literal>java.util.Calendar</literal> vers les types SQL
+ <literal>TIMESTAMP</literal> et <literal>DATE</literal>
+ (ou équivalent).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <literal>big_decimal, big_integer</literal>
+ </term>
+ <listitem>
+ <para>
+ Mappings de type pour <literal>java.math.BigDecimal</literal> et
+ <literal>java.math.BigInteger</literal> vers <literal>NUMERIC</literal>
+ (ou le <literal>NUMBER</literal> Oracle).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <literal>locale, timezone, currency</literal>
+ </term>
+ <listitem>
+ <para>
+ Mappings de type pour <literal>java.util.Locale</literal>,
+ <literal>java.util.TimeZone</literal> et
+ <literal>java.util.Currency</literal>
+ vers <literal>VARCHAR</literal> (ou le <literal>VARCHAR2</literal> Oracle).
+ Les instances de <literal>Locale</literal> et <literal>Currency</literal> sont
+ mappées sur leurs codes ISO. Les instances de <literal>TimeZone</literal> sont
+ mappées sur leur <literal>ID</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <literal>class</literal>
+ </term>
+ <listitem>
+ <para>
+ Un type de mapping pour <literal>java.lang.Class</literal> vers
+ <literal>VARCHAR</literal> (ou le <literal>VARCHAR2</literal> Oracle).
+ Un objet <literal>Class</literal> est mappé sur son nom Java complet.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <literal>binary</literal>
+ </term>
+ <listitem>
+ <para>
+ Mappe les tableaux de bytes vers le type binaire SQL approprié.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <literal>text</literal>
+ </term>
+ <listitem>
+ <para>
+ Mappe les longues chaînes de caractères Java vers les types SQL
+ <literal>CLOB</literal> ou <literal>TEXT</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <literal>serializable</literal>
+ </term>
+ <listitem>
+ <para>
+ Mappe les types Java sérialisables vers le type SQL binaire approprié. Vous pouvez
+ aussi indiquer le type Hibernate <literal>serializable</literal> avec le nom
+ d'une classe Java sérialisable ou une interface qui ne soit pas par défaut un type de base.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <literal>clob, blob</literal>
+ </term>
+ <listitem>
+ <para>
+ Mappings de type pour les classes JDBC <literal>java.sql.Clob</literal> and
+ <literal>java.sql.Blob</literal>. Ces types peuvent ne pas convenir pour certaines
+ applications car un objet blob ou clob peut ne pas être réutilisable en dehors
+ d'une transaction (de plus l'implémentation par les pilotes est moyennement bonne).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <literal>imm_date, imm_time, imm_timestamp, imm_calendar, imm_calendar_date,
+ imm_serializable, imm_binary</literal>
+ </term>
+ <listitem>
+ <para>
+ Mappings de type pour ceux qui sont habituellement modifiable, pour lesquels
+ Hibernate effectue certains optimisations convenant seulement aux types Java immuables,
+ et l'application les traite comme immuable.
+ Par exemple, vous ne devriez pas appeler <literal>Date.setTime()</literal> sur une instance
+ mappée sur un <literal>imm_timestamp</literal>. Pour changer la valeur
+ de la propriété, et faire que cette modification soit persistée, l'application
+ doit assigner un nouvel (non identique) objet à la propriété.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ <para>
+ Les identifiants uniques des entités et collections peuvent être de n'importe quel type de base excepté
+ <literal>binary</literal>, <literal>blob</literal> et <literal>clob</literal> (les identifiants
+ composites sont aussi permis, voir plus bas).
+ </para>
+ <para>
+ Les types de base des valeurs ont des <literal>Type</literal> constants correspondants définis
+ dans <literal>org.hibernate.Hibernate</literal>. Par exemple, <literal>Hibernate.STRING</literal>
+ représenté le type <literal>string</literal>.
+ </para>
+ </sect2>
+ <sect2 id="mapping-types-custom" revision="2">
+ <title>Types de valeur définis par l'utilisateur</title>
+ <para>
+ Il est assez facile pour les développeurs de créer leurs propres types de valeurs. Par exemple,
+ vous pourriez vouloir persister des propriétés du type <literal>java.lang.BigInteger</literal>
+ dans des colonnnes <literal>VARCHAR</literal>. Hibernate ne procure pas par défaut un type pour cela.
+ Mais les types que vous pouvez créer ne se limitent pas à mapper des propriétés (ou élément collection)
+ à une simple colonne d'une table. Donc, par exemple, vous pourriez avoir une propriété Java
+ <literal>getName()</literal>/<literal>setName()</literal> de type
+ <literal>java.lang.String</literal> persistée dans les colonnes
+ <literal>FIRST_NAME</literal>, <literal>INITIAL</literal>, <literal>SURNAME</literal>.
+ </para>
+ <para>
+ Pour implémenter votre propre type, vous pouvez soit implémenter <literal>org.hibernate.UserType</literal>
+ soit <literal>org.hibernate.CompositeUserType</literal> et déclarer des propriétés utilisant des
+ noms de classes complets du type. Regardez <literal>org.hibernate.test.DoubleStringType</literal>
+ pour voir ce qu'il est possible de faire.
+ </para>
+ <programlisting><![CDATA[<property name="twoStrings" type="org.hibernate.test.DoubleStringType">
+ <column name="first_string"/>
+ <column name="second_string"/>
+</property>]]></programlisting>
+ <para>
+ Remarquez l'utilisation des tags <literal><column></literal> pour mapper une propriété sur des colonnes
+ multiples.
+ </para>
+ <para>
+ Les interfaces <literal>CompositeUserType</literal>, <literal>EnhancedUserType</literal>,
+ <literal>UserCollectionType</literal>, et <literal>UserVersionType</literal> permettent des utilisations
+ plus spécialisées.
+ </para>
+ <para>
+ Vous pouvez même donner des paramètres en indiquant <literal>UserType</literal> dans le fichier
+ de mapping ; Pour cela, votre <literal>UserType</literal> doit implémenter l'interface
+ <literal>org.hibernate.usertype.ParameterizedType</literal>. Pour spécifier des paramètres dans
+ votre type propre, vous pouvez utiliser l'élément <literal><type></literal> dans vos fichiers de mapping.
+ </para>
+ <programlisting><![CDATA[<property name="priority">
+ <type name="com.mycompany.usertypes.DefaultValueIntegerType">
+ <param name="default">0</param>
+ </type>
+</property>]]></programlisting>
+ <para>
+ Le <literal>UserType</literal> permet maintenant de récupérer la valeur pour le paramètre nommé
+ <literal>default</literal> à partir de l'objet <literal>Properties</literal> qui lui est passé.
+ </para>
+ <para>
+ Si vous utilisez fréquemment un <literal>UserType</literal>, cela peut être utile de lui définir un
+ nom plus court. Vous pouvez faire cela en utilisant l'élément <literal><typedef></literal>.
+ Les typedefs permettent d'assigner un nom à votre type propre et peuvent aussi contenir une liste de
+ valeurs de paramètres par défaut si ce type est paramétré.
+ </para>
+ <programlisting><![CDATA[<typedef class="com.mycompany.usertypes.DefaultValueIntegerType" name="default_zero">
+ <param name="default">0</param>
+</typedef>]]></programlisting>
+ <programlisting><![CDATA[<property name="priority" type="default_zero"/>]]></programlisting>
+ <para>
+ Il est aussi possible de redéfinir les paramètres par défaut du typedef au cas par cas en
+ utilisant des paramètres type sur le mapping de la propriété.
+ </para>
+ <para>
+ Bien que le fait que Hibernate propose de base une riche variété de types, et qu'il supporte les composants
+ signifie que vous aurez très rarement <emphasis>besoin</emphasis> d'utiliser un nouveau type propre,
+ il est néanmoins de bonne pratique d'utiliser des types propres pour les classes (non entités) qui
+ apparaissent fréquemment dans votre application. Par exemple une classe <literal>MonetaryAmount</literal>
+ est un bon candidat pour un <literal>CompositeUserType</literal> même s'il pourrait facilement
+ être mappé comme un composant. Une motivation pour cela est l'abstraction. Avec un type propre
+ vos documents de mapping sont à l'abri des changements futurs dans votre façon de représenter des
+ valeurs monétaires.
+ </para>
+ </sect2>
+ </sect1>
+
+ <sect1 id="mapping-entityname">
+ <title>Mapper une classe plus d'une fois</title>
+ <para>
+ Il est possible de proposer plus d'un mapping par classe persistante. Dans ce cas, vous
+ devez spécifier un <emphasis>nom d'entité</emphasis> pour lever l'ambiguité entre les instances
+ des entités mappées (par défaut, le nom de l'entité est celui de la classe). Hibernate
+ vous permet de spécifier le nom de l'entité lorsque vous utilisez des objets persistants, lorsque
+ vous écrivez des requêtes ou quand vous mappez des associations vers les entités nommées.
+ </para>
+
+ <programlisting><![CDATA[<class name="Contract" table="Contracts"
+ entity-name="CurrentContract">
+ ...
+ <set name="history" inverse="true"
+ order-by="effectiveEndDate desc">
+ <key column="currentContractId"/>
+ <one-to-many entity-name="HistoricalContract"/>
+ </set>
+</class>
+
+<class name="Contract" table="ContractHistory"
+ entity-name="HistoricalContract">
+ ...
+ <many-to-one name="currentContract"
+ column="currentContractId"
+ entity-name="CurrentContract"/>
+</class>]]></programlisting>
+
+ <para>
+ Remarquez comment les associations sont désormais spécifiées en utilisant
+ <literal>entity-name</literal> au lieu de <literal>class</literal>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="mapping-quotedidentifiers">
+ <title>SQL quoted identifiers</title>
+ <para>
+ Vous pouvez forcer Hibernate à mettre un identifiant entre quotes dans le SQL généré en mettant le nom
+ de la table ou de la colonne entre backticks dans le
+ document de mapping. Hibernate utilisera les bons styles de quotes pour le <literal>Dialect</literal> SQL
+ (habituellement des doubles quotes, mais des parenthèses pour SQL server et des backticks pour MySQL).
+ </para>
+ <programlisting><![CDATA[<class name="LineItem" table="`Line Item`">
+ <id name="id" column="`Item Id`"/><generator class="assigned"/></id>
+ <property name="itemNumber" column="`Item #`"/>
+ ...
+</class>]]></programlisting>
+ </sect1>
+ <sect1 id="mapping-alternatives">
+ <title>alternatives Metadata</title>
+ <para>
+ XML ne convient pas à tout le monde, il y a donc des moyens alternatifs pour définir des metatda
+ de mappings O/R dans Hibernate.
+ </para>
+ <sect2 id="mapping-xdoclet">
+ <title>utilisation de XDoclet</title>
+ <para>
+ De nombreux utilisateurs de Hibernate préfèrent embarquer les informations de mappings
+ directement au sein du code source en utilisant les tags XDoclet <literal>@hibernate.tags</literal>.
+ Nous ne couvrons pas cette approche dans ce document cependant, puisque c'est considéré comme faisant partie
+ de XDoclet. Cependant, nous présentons l'exemple suivant de la classe <literal>Cat</literal> avec
+ des mappings XDoclet.
+ </para>
+ <programlisting><![CDATA[package eg;
+import java.util.Set;
+import java.util.Date;
+
+/**
+ * @hibernate.class
+ * table="CATS"
+ */
+public class Cat {
+ private Long id; // identifier
+ private Date birthdate;
+ private Cat mother;
+ private Set kittens
+ private Color color;
+ private char sex;
+ private float weight;
+
+ /*
+ * @hibernate.id
+ * generator-class="native"
+ * column="CAT_ID"
+ */
+ public Long getId() {
+ return id;
+ }
+ private void setId(Long id) {
+ this.id=id;
+ }
+
+ /**
+ * @hibernate.many-to-one
+ * column="PARENT_ID"
+ */
+ public Cat getMother() {
+ return mother;
+ }
+ void setMother(Cat mother) {
+ this.mother = mother;
+ }
+
+ /**
+ * @hibernate.property
+ * column="BIRTH_DATE"
+ */
+ public Date getBirthdate() {
+ return birthdate;
+ }
+ void setBirthdate(Date date) {
+ birthdate = date;
+ }
+ /**
+ * @hibernate.property
+ * column="WEIGHT"
+ */
+ public float getWeight() {
+ return weight;
+ }
+ void setWeight(float weight) {
+ this.weight = weight;
+ }
+
+ /**
+ * @hibernate.property
+ * column="COLOR"
+ * not-null="true"
+ */
+ public Color getColor() {
+ return color;
+ }
+ void setColor(Color color) {
+ this.color = color;
+ }
+ /**
+ * @hibernate.set
+ * inverse="true"
+ * order-by="BIRTH_DATE"
+ * @hibernate.collection-key
+ * column="PARENT_ID"
+ * @hibernate.collection-one-to-many
+ */
+ public Set getKittens() {
+ return kittens;
+ }
+ void setKittens(Set kittens) {
+ this.kittens = kittens;
+ }
+ // addKitten not needed by Hibernate
+ public void addKitten(Cat kitten) {
+ kittens.add(kitten);
+ }
+
+ /**
+ * @hibernate.property
+ * column="SEX"
+ * not-null="true"
+ * update="false"
+ */
+ public char getSex() {
+ return sex;
+ }
+ void setSex(char sex) {
+ this.sex=sex;
+ }
+}]]></programlisting>
+ <para>
+ Voyez le site web de Hibernate pour plus d'exemples sur XDoclet et Hibernate.
+ </para>
+ </sect2>
+ <sect2 id="mapping-annotations" revision="2">
+ <title>Utilisation des annotations JDK 5.0</title>
+ <para>
+ Le JDK 5.0 introduit des annotations proches de celles de XDoclet au niveau java, qui sont
+ type-safe et vérifiées à la compilation. Ce mécanisme est plus puissant que XDoclet et mieux
+ supporté par les outils et IDE. IntelliJ IDEA, par exemple, supporte l'auto-complétion et le
+ surlignement syntaxique des annotations JDK 5.0. La nouvelle révision des spécifications des EJB
+ (JSR-220) utilise les annotations JDK 5.0 comme mécanisme primaire pour les meta-données des beans entités.
+ Hibernate3 implémente l'<literal>EntityManager</literal> de la JSR-220 (API de persistance),
+ le support du mapping de meta-données est disponible via le package <emphasis>Hibernate Annotations</emphasis>,
+ en tant que module séparé à télécharger. EJB3 (JSR-220) et les métadata Hibernate3 sont supportés.
+ </para>
+ <para>
+ Ceci est un exemple d'une classe POJO annotée comme un EJB entité :
+ </para>
+ <programlisting><![CDATA[@Entity(access = AccessType.FIELD)
+public class Customer implements Serializable {
+
+ @Id;
+ Long id;
+
+ String firstName;
+ String lastName;
+ Date birthday;
+
+ @Transient
+ Integer age;
+
+ @Embedded
+ private Address homeAddress;
+
+ @OneToMany(cascade=CascadeType.ALL)
+ @JoinColumn(name="CUSTOMER_ID")
+ Set<Order> orders;
+
+ // Getter/setter and business methods
+}]]></programlisting>
+ <para>
+ Notez que le support des annotations JDK 5.0 (et de la JSR-220) est encore en cours et n'est pas terminé.
+ Référez vous au module Hibernate Annotation pour plus de détails.
+ </para>
+ </sect2>
+ </sect1>
+
+ <sect1 id="mapping-generated" revision="1">
+ <title>Propriétés générées</title>
+ <para>
+ Les propriétés générées sont des propriétés dont les valeurs sont générées par
+ la base de données. Typiquement, les applications Hibernate avaient besoin d'invoquer
+ <literal>refresh</literal> sur les instances qui contenaient des propriétés pour lesquelles
+ la base de données générait des valeurs. Marquer les propriétés comme générées permet à
+ l'application de déléguer cette responsabilité à Hibernate. Principalement, à chaque fois
+ qu'Hibernate réalise une insertion ou une mise à jour en base de données pour une entité
+ marquée comme telle, cela provoque immédiatement un select pour récupérer les valeurs générées.
+ </para>
+ <para>
+ Les propriétés marquées comme générées doivent de plus ne pas être insérables et modifiables
+ Seuls <xref linkend="mapping-declaration-version">versions</xref>,
+ <xref linkend="mapping-declaration-timestamp">timestamps</xref>, et
+ <xref linkend="mapping-declaration-property">simple properties</xref> peuvent être marqués comme
+ générées.
+ </para>
+ <para>
+ <literal>never</literal> (par défaut) - indique la valeur de la propriété n'est pas générée
+ dans la base de données.
+ </para>
+ <para>
+ <literal>insert</literal> - indique que la valeur de la propriété donnée est
+ générée à l'insertion mais pas lors des futures mises à jour de l'enregistrement.
+ Les colonnes de type "date de création" sont le cas d'utilisation typique de cette option.
+ Notez que même les propriétés <xref linkend="mapping-declaration-version">version</xref> et
+ <xref linkend="mapping-declaration-timestamp">timestamp</xref> peuvent être
+ déclarées comme générées, cette option n'est pas disponible à cet endroit...
+ </para>
+ <para>
+ <literal>always</literal> - indique que la valeur de la propriété est générée à l'insert
+ comme aux updates.
+ </para>
+
+ </sect1>
+
+ <sect1 id="mapping-database-object">
+ <title>Objets auxiliaires de la base de données</title>
+ <para>
+ Permettent les ordres CREATE et DROP d'objets arbitraire de la base de donnéées, en conjonction avec
+ les outils Hibernate d'évolutions de schéma, pour permettre de définir complètement
+ un schéma utilisateur au sein des fichiers de mapping Hibernate. Bien que conçu spécifiquement
+ pour créer et supprimer des objets tels que des triggers et des procédures stockées,
+ ou toute commande pouvant être exécutée via une méthode de <literal>java.sql.Statement.execute()</literal>
+ (ALTERs, INSERTS, etc). Il y a principalement deux modes pour définir les objets auxiliaires de base de données...
+ </para>
+ <para>
+ Le premier mode est de lister explicitement les commandes CREATE et DROP dans le fichier
+ de mapping:
+ </para>
+ <programlisting><![CDATA[<hibernate-mapping>
+ ...
+ <database-object>
+ <create>CREATE TRIGGER my_trigger ...</create>
+ <drop>DROP TRIGGER my_trigger</drop>
+ </database-object>
+</hibernate-mapping>]]></programlisting>
+ <para>
+ Le second mode est de fournir une classe particulière qui connait comment construire
+ les commandes CREATE et DROP. Cette classe particulière doit implémenter l'interface
+ <literal>org.hibernate.mapping.AuxiliaryDatabaseObject</literal>.
+ </para>
+ <programlisting><![CDATA[<hibernate-mapping>
+ ...
+ <database-object>
+ <definition class="MyTriggerDefinition"/>
+ </database-object>
+</hibernate-mapping>]]></programlisting>
+ <para>
+ Additionnellement, ces objets de base de données peuvent être optionnellement traités
+ selon l'utilisation de dialectes particuliers..
+ </para>
+ <programlisting><![CDATA[<hibernate-mapping>
+ ...
+ <database-object>
+ <definition class="MyTriggerDefinition"/>
+ <dialect-scope name="org.hibernate.dialect.Oracle9Dialect"/>
+ <dialect-scope name="org.hibernate.dialect.OracleDialect"/>
+ </database-object>
+</hibernate-mapping>]]></programlisting>
+ </sect1>
+</chapter>
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/batch.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/batch.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/batch.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,329 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<chapter id="batch">
+ <title>Traitement par paquet</title>
+
+ <para>
+ Une approche naïve pour insérer 100 000 lignes dans la base de données en utilisant
+ Hibernate pourrait ressembler à ça :
+ </para>
+
+ <programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+for ( int i=0; i<100000; i++ ) {
+ Customer customer = new Customer(.....);
+ session.save(customer);
+}
+tx.commit();
+session.close();]]></programlisting>
+
+ <para>
+ Ceci devrait s'écrouler avec une <literal>OutOfMemoryException</literal> quelque
+ part aux alentours de la 50 000ème ligne. C'est parce qu'Hibernate cache toutes
+ les instances de <literal>Customer</literal> nouvellement insérées dans le cache
+ de second niveau.
+ </para>
+
+ <para>
+ Dans ce chapitre nous montrerons comment éviter ce problème. D'abord, cependant,
+ si vous faites des traitements par batch, il est absolument critique que vous
+ activiez l'utilisation ds paquet JDBC (NdT : JDBC batching), si vous avez l'intention
+ d'obtenir des performances raisonnables. Configurez la taille du paquet JDBC avec un
+ nombre raisonnable (disons, 10-50) :
+ </para>
+
+ <programlisting><![CDATA[hibernate.jdbc.batch_size 20]]></programlisting>
+
+ <para>
+ Vous pourriez aussi vouloir faire cette sorte de travail dans un traitement où
+ l'interaction avec le cache de second niveau est complètement désactivé :
+ </para>
+
+ <programlisting><![CDATA[hibernate.cache.use_second_level_cache false]]></programlisting>
+
+ <sect1 id="batch-inserts">
+ <title>Insertions en paquet</title>
+
+ <para>
+ Lorsque vous rendez des nouveaux objets persistants, vous devez régulièrement appeler
+ <literal>flush()</literal> et puis <literal>clear()</literal> sur la session,
+ pour contrôler la taille du cache de premier niveau.
+ </para>
+
+ <programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+
+for ( int i=0; i<100000; i++ ) {
+ Customer customer = new Customer(.....);
+ session.save(customer);
+ if ( i % 20 == 0 ) { //20, même taille que la taille du paquet JDBC
+ //flush un paquet d'insertions et libère la mémoire :
+ session.flush();
+ session.clear();
+ }
+}
+
+tx.commit();
+session.close();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="batch-update" >
+ <title>Paquet de mises à jour</title>
+
+ <para>
+ Pour récupérer et mettre à jour des données les mêmes idées s'appliquent. En plus,
+ vous avez besoin d'utiliser <literal>scroll()</literal> pour tirer partie des
+ curseurs côté serveur pour les requêtes qui retournent beaucoup de lignes de données.
+ </para>
+
+ <programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+
+ScrollableResults customers = session.getNamedQuery("GetCustomers")
+ .setCacheMode(CacheMode.IGNORE)
+ .scroll(ScrollMode.FORWARD_ONLY);
+int count=0;
+while ( customers.next() ) {
+ Customer customer = (Customer) customers.get(0);
+ customer.updateStuff(...);
+ if ( ++count % 20 == 0 ) {
+ //flush un paquet de mises à jour et libère la mémoire :
+ session.flush();
+ session.clear();
+ }
+}
+
+tx.commit();
+session.close();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="batch-statelesssession">
+ <title>L'interface StatelessSession</title>
+ <para>
+ Alternativement, Hibernate fournit une API orientée commande qui peut être
+ utilisée avec des flux de données pour et en provenance de la base de données
+ sous la forme d'objets détachés. Une <literal>StatelessSession</literal> n'a pas
+ de contexte de persistance associé et ne fournit pas beaucoup de sémantique de
+ durée de vie de haut niveau. En particulier, une session sans état n'implémente
+ pas de cache de premier niveau et n'interagit pas non plus avec un cache de
+ seconde niveau ou un cache de requêtes. Elle n'implémente pas les transactions
+ ou la vérification sale automatique (NdT : automatic dirty checking). Les
+ opérations réalisées avec une session sans état ne sont jamais répercutées
+ en cascade sur les instances associées. Les collections sont ignorées par une
+ session sans état. Les opérations exécutées via une session sans état outrepasse
+ le modèle d'événements d'Hibernate et les intercepteurs. Les sessions sans état sont
+ vulnérables aux effets de modification des données, ceci est dû au manque de cache
+ de premier niveau. Une session sans état est une abstraction bas niveau, plus
+ proche de la couche JDBC sous-jacente.
+ </para>
+
+ <programlisting><![CDATA[StatelessSession session = sessionFactory.openStatelessSession();
+Transaction tx = session.beginTransaction();
+
+ScrollableResults customers = session.getNamedQuery("GetCustomers")
+ .scroll(ScrollMode.FORWARD_ONLY);
+while ( customers.next() ) {
+ Customer customer = (Customer) customers.get(0);
+ customer.updateStuff(...);
+ session.update(customer);
+}
+
+tx.commit();
+session.close();]]></programlisting>
+
+ <para>
+ Notez que dans le code de l'exemple, les intances de <literal>Customer</literal>
+ retournées par la requête sont immédiatement détachées. Elles ne sont jamais
+ associées à un contexte de persistance.
+ </para>
+
+ <para>
+ Les opérations <literal>insert()</literal>, <literal>update()</literal> et
+ <literal>delete()</literal> définies par l'interface <literal>StatelessSession</literal>
+ sont considérées comme des opérations d'accès direct aux lignes de la base de données,
+ ce qui résulte en une exécution immédiate du SQL <literal>INSERT</literal>, <literal>UPDATE</literal>
+ ou <literal>DELETE</literal> respectif. De là, elles ont des sémantiques tres différentes des
+ opérations <literal>save()</literal>, <literal>saveOrUpdate()</literal>
+ et <literal>delete()</literal> définies par l'interface <literal>Session</literal>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="batch-direct" revision="2">
+ <title>Opérations de style DML</title>
+
+ <para>
+ Comme déjà discuté avant, le mapping objet/relationnel automatique et transparent
+ est intéressé par la gestion de l'état de l'objet. Ceci implique que l'état de l'objet
+ est disponible en mémoire, d'où manipuler (en utilisant des expressions du langage de
+ manipulation de données - <literal>Data Manipulation Language</literal> (DML) - SQL)
+ les données directement dans la base n'affectera pas l'état en mémoire. Pourtant, Hibernate
+ fournit des méthodes pour l'exécution d'expression DML de style SQL lesquelles sont
+ réalisées à travers le langage de requête d'Hibernate (<xref linkend="queryhql">HQL</xref>).
+ </para>
+
+ <para>
+ La pseudo-syntaxe pour les expressions <literal>UPDATE</literal> et <literal>DELETE</literal>
+ est : <literal>( UPDATE | DELETE ) FROM? EntityName (WHERE where_conditions)?</literal>.
+ Certains points sont à noter :
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ Dans la clause from, le mot-clef FROM est optionnel
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Il ne peut y avoir qu'une seule entité nommée dans la clause from ; elle peut
+ optionnellement avoir un alias. Si le nom de l'entité a un alias, alors
+ n'importe quelle référence de propriété doit être qualifiée en ayant un alias ;
+ si le nom de l'entité n'a pas d'alias, alors il est illégal pour n'importe quelle
+ référence de propriété d'être qualifiée.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Aucune jointure (implicite ou explicite) ne peut être spécifiée dans une requête HQL.
+ Les sous-requêtes peuvent être utilisées dans la clause where ; les sous-requêtes,
+ elles-mêmes, peuvent contenir des jointures.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ La clause where est aussi optionnelle.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Par exemple, pour exécuter un <literal>UPDATE</literal> HQL, utilisez la méthode
+ <literal>Query.executeUpdate()</literal> (la méthode est données pour ceux
+ qui sont familiers avec <literal>PreparedStatement.executeUpdate()</literal> de
+ JDBC) :
+ </para>
+
+ <programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+
+String hqlUpdate = "update Customer c set c.name = :newName where c.name = :oldName";
+// ou String hqlUpdate = "update Customer set name = :newName where name = :oldName";
+int updatedEntities = s.createQuery( hqlUpdate )
+ .setString( "newName", newName )
+ .setString( "oldName", oldName )
+ .executeUpdate();
+tx.commit();
+session.close();]]></programlisting>
+
+ <para>
+ Pour exécuter un <literal>DELETE</literal> HQL, utilisez la même méthode
+ <literal>Query.executeUpdate()</literal> :
+ </para>
+
+ <programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+
+String hqlDelete = "delete Customer c where c.name = :oldName";
+// or String hqlDelete = "delete Customer where name = :oldName";
+int deletedEntities = s.createQuery( hqlDelete )
+ .setString( "oldName", oldName )
+ .executeUpdate();
+tx.commit();
+session.close();]]></programlisting>
+
+ <para>
+ La valeur du <literal>int</literal> retourné par la méthode <literal>Query.executeUpdate()</literal>
+ indique le nombre d'entités affectées par l'opération. Considérez que cela peut ou pas
+ corréler le nombre de lignes affectés dans la base de données. Une opération HQL
+ pourrait entraîner l'exécution de multiples expressions SQL réelles, pour des classes
+ filles mappées par jointure (NdT: join-subclass), par exemple. Le nombre retourné
+ indique le nombre d'entités réelles affectées par l'expression. Retour à l'exemple de la
+ classe fille mappée par jointure, un effacement d'une des classes filles peut réellement
+ entraîner des suppressions pas seulement dans la table qui mappe la classe fille, mais
+ aussi dans la table "racine" et potentillement dans les tables des classes filles plus bas
+ dans la hiérarchie d'héritage.
+ </para>
+
+ <para>
+ La pseudo-syntaxe pour l'expression <literal>INSERT</literal> est :
+ <literal>INSERT INTO EntityName properties_list select_statement</literal>. Quelques
+ points sont à noter :
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ Seule la forme INSERT INTO ... SELECT ... est supportée ; pas la forme INSERT INTO ... VALUES ... .
+ </para>
+ <para>
+ La properties_list est analogue à la <literal>spécification de la colonne</literal>
+
+ The properties_list is analogous to the <literal>column speficiation</literal> dans
+ l'expression SQL <literal>INSERT</literal>. Pour les entités impliquées dans
+ un héritage mappé, seules les propriétés directement définies à ce niveau de classe
+ donné peuvent être utilisées dans properties_list. Les propriétés de la classe mère
+ ne sont pas permises ; et les propriétés des classes filles n'ont pas de sens. En
+ d'autres mots, les expressions <literal>INSERT</literal> par nature non polymorphiques.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ select_statement peut être n'importe quelle requête de sélection HQl valide, avec
+ l'avertissement que les types de retour doivent correspondre aux types attendus par
+ l'insertion. Actuellement, c'est vérifié durant la compilation de la requête plutôt
+ que la vérification soit reléguée à la base de données. Notez cependant que cela
+ pourrait poser des problèmes entre les <literal>Type</literal>s d'Hibernate qui
+ sont <emphasis>équivalents</emphasis> opposé à <emphasis>égaux</emphasis>. Cela
+ pourrait poser des problèmes avec des disparités entre une propriété définie
+ comme un <literal>org.hibernate.type.DateType</literal> et une propriété définie
+ comme un <literal>org.hibernate.type.TimestampType</literal>, même si la base de données
+ ne ferait pas de distinction ou ne serait pas capable de gérer la conversion.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Pour la propriéte id, l'expression d'insertion vous donne deux options. Vous
+ pouvez soit spécifier explicitement la propriété id dans properties_list
+ (auquel cas sa valeur est extraite de l'expression de sélection correspondante),
+ soit l'omettre de properties_list (auquel cas une valeur générée est utilisée).
+ Cette dernière option est seulement disponible en utilisant le générateur d'identifiant
+ qui opère dans la base de données ; tenter d'utiliser cette option avec n'importe quel
+ type de générateur "en mémoire" causera une exception durant l'analyse. Notez
+ que pour les buts de cette discussion, les générateurs "en base" sont considérés
+ être <literal>org.hibernate.id.SequenceGenerator</literal> (et ses classes filles)
+ et n'importe quelles implémentations de
+ <literal>org.hibernate.id.PostInsertIdentifierGenerator</literal>.
+ L'exception la plus notable ici est <literal>org.hibernate.id.TableHiLoGenerator</literal>,
+ qu ne peut pas être utilisée parce qu'il ne propose pas un moyen de d'exposer ses valeurs
+ par un select.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Pour des propriétés mappées comme <literal>version</literal> ou <literal>timestamp</literal>,
+ l'expression d'insertion vous donne deux options. Vous pouvez soit spécifier la propriété dans
+ properties_list (auquel cas sa valeur est extraite des expressions select correspondantes),
+ soit l'omettre de properties_list (auquel cas la <literal>valeur de graine</literal>
+ (NdT : seed value) définie par le <literal>org.hibernate.type.VersionType</literal> est utilisée).
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Un exemple d'exécution d'une expression <literal>INSERT</literal> HQL :
+ </para>
+
+ <programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+
+String hqlInsert = "insert into DelinquentAccount (id, name) select c.id, c.name from Customer c where ...";
+int createdEntities = s.createQuery( hqlInsert )
+ .executeUpdate();
+tx.commit();
+session.close();]]></programlisting>
+
+ </sect1>
+
+</chapter>
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/best_practices.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/best_practices.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/best_practices.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,240 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<chapter id="best-practices" revision="3">
+ <title>Meilleures pratiques</title>
+
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term>Découpez finement vos classes et mappez les en utilisant <literal><component></literal>.</term>
+ <listitem>
+ <para>
+ Utilisez une classe <literal>Adresse</literal> pour encapsuler <literal>Rue</literal>,
+ <literal>Region</literal>, <literal>CodePostal</literal>.
+ Ceci permet la réutilisation du code et simplifie la maintenance.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Déclarez des propriétés d'identifiants dans les classes persistantes.</term>
+ <listitem>
+ <para>
+ Hibernate rend les propriétés d'identifiants optionnelles. Il existe beaucoup de raisons
+ pour lesquelles vous devriez les utiliser. Nous recommandons que vous utilisiez des identifiants
+ techniques (générés, et sans connotation métier).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Identifiez les clefs naturelles.</term>
+ <listitem>
+ <para>
+ Identifiez les clefs naturelles pour toutes les entités, et mappez les avec
+ <literal><natural-id></literal>. Implémentez <literal>equals()</literal> et
+ <literal>hashCode()</literal> pour comparer les propriétés qui composent la clef naturelle.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Placez chaque mapping de classe dans son propre fichier.</term>
+ <listitem>
+ <para>
+ N'utilisez pas un unique document de mapping. Mappez <literal>com.eg.Foo</literal> dans
+ le fichier <literal>com/eg/Foo.hbm.xml</literal>. Cela prend tout son sens lors
+ d'un travail en équipe.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Chargez les mappings comme des ressources.</term>
+ <listitem>
+ <para>
+ Déployez les mappings en même temps que les classes qu'ils mappent.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Pensez à externaliser les chaînes de caractères.</term>
+ <listitem>
+ <para>
+ Ceci est une bonne habitude si vos requêtes appellent des fonctions SQL qui ne sont
+ pas au standard ANSI. Cette externalisation dans les fichiers de mapping rendra votre
+ application plus portable.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Utilisez les variables "bindées".</term>
+ <listitem>
+ <para>
+ Comme en JDBC, remplacez toujours les valeurs non constantes par "?". N'utilisez jamais
+ la manipulation des chaînes de caractères pour remplacer des valeurs non constantes dans
+ une requête ! Encore mieux, utilisez les paramètres nommés dans les requêtes.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Ne gérez pas vous même les connexions JDBC.</term>
+ <listitem>
+ <para>
+ Hibernate laisse l'application gérer les connexions JDBC. Vous ne devriez gérer vos connexions
+ qu'en dernier recours. Si vous ne pouvez pas utiliser les systèmes de connexions livrés,
+ réfléchissez à l'idée de fournir votre propre implémentation de <literal>org.hibernate.connection.ConnectionProvider</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Pensez à utiliser les types utilisateurs.</term>
+ <listitem>
+ <para>
+ Supposez que vous ayez une type Java, de telle bibliothèque, qui a besoin d'être persisté mais
+ qui ne fournit pas les accesseurs nécessaires pour le mapper comme composant. Vous devriez
+ implémenter
+ <literal>org.hibernate.UserType</literal>.Cette approche libère le code de l'application
+ de l'implémentation des transformations vers / depuis les types Hibernate.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Utilisez du JDBC pur dans les goulets d'étranglement.</term>
+ <listitem>
+ <para>
+ Dans certaines parties critiques de votre système d'un point de vue performance, quelques opérations
+ peuvent tirer partie d'un appel JDBC natif.
+ Mais attendez de <emphasis>savoir</emphasis>
+ que c'est un goulet d'étranglement. Ne supposez jamais qu'un appel JDBC sera forcément plus
+ rapide. Si vous avez besoin d'utiliser JDBC directement, ouvrez une <literal>Session</literal>
+ Hibernate et utilisez la connexion SQL sous-jacente. Ainsi vous pourrez utiliser la même stratégie
+ de transation et la même gestion des connexions.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Comprendre le flush de <literal>Session</literal>.</term>
+ <listitem>
+ <para>
+ De temps en temps la Session synchronise ses états persistants avec la base de données.
+ Les performances seront affectées si ce processus arrive trop souvent. Vous pouvez parfois
+ minimiser les flush non nécessaires en désactivant le flush automatique ou même en changeant
+ l'ordre des opérations menées dans une transaction particulière.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Dans une architecture à trois couches, pensez à utiliser <literal>saveOrUpdate()</literal>.</term>
+ <listitem>
+ <para>
+ Quand vous utilisez une architecture à base de servlet / session bean, vous pourriez passer
+ des objets chargés dans le bean session vers et depuis la couche servlet / JSP. Utilisez
+ une nouvelle session pour traiter chaque requête.
+ Utilisez <literal>Session.merge()</literal> ou <literal>Session.saveOrUpdate()</literal> pour
+ synchroniser les objets avec la base de données.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Dans une architecture à deux couches, pensez à utiliser la déconnexion de session.</term>
+ <listitem>
+ <para>
+ Les transactions de bases de données doivent être aussi courtes que possible
+ pour une meilleure montée en charge.Cependant, il est souvent nécessaire d'implémenter
+ de longues <emphasis>transactions applicatives</emphasis>, une simple unité de travail du point de vue de
+ l'utilisateur. Une transaction applicative
+ peut s'étaler sur plusieurs cycles de requêtes/réponses du client.
+ Il est commun d'utiliser des objets détachés pour implémenter des transactions applicatives.
+ Une alternative, extrêmement appropriée dans une architecture à 2 couches, est de
+ maintenir un seul contact de persistance ouvert (session) pour toute la durée de vie
+ de la transaction applicative et simplement se déconnecter de la connexion JDBC à la fin de chaque requête,
+ et se reconnecter au début de la requête suivante. Ne partagez jamais une seule
+ session avec plus d'une transaction applicative, ou vous travaillerez avec des
+ données périmées.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Considérez que les exceptions ne sont pas rattrapables.</term>
+ <listitem>
+ <para>
+ Il s'agit plus d'une pratique obligatoire que d'une "meilleure pratique". Quand une exception
+ intervient, il faut faire un rollback de la <literal>Transaction</literal> et
+ fermer la <literal>Session</literal>.
+ Sinon, Hibernate ne peut garantir l'intégrité des états persistants en mémoire. En particulier,
+ n'utilisez pas <literal>Session.load()</literal> pour déterminer si une instance avec un identifiant
+ donné existe en base de données, utilisez <literal>Session.get()</literal> ou un requête.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Préférez le chargement tardif des associations.</term>
+ <listitem>
+ <para>
+ Utilisez le chargement complet avec modération.
+ Utilisez les proxies et les collections chargées tardivement
+ pour la plupart des associations vers des classes qui ne sont pas susceptibles
+ d'être complètement retenues dans le cache de second niveau.
+ Pour les assocations de classes en cache, où il y a une extrêmement
+ forte probabilité que l'élément soit en cache, désactivez explicitement le chargement
+ par jointures ouvertes en utilisant <literal>outer-join="false"</literal>.
+ Lorsqu'un chargement par jointure ouverte est approprié pour un cas d'utilisation
+ particulier, utilisez une requête avec un <literal>left join fetch</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ Utilisez le pattern <emphasis>d'une ouverture de session dans une vue</emphasis>,
+ ou une <emphasis>phase d'assemblage</emphasis> disciplinée pour éviter des problèmes
+ avec des données non rapatriées.
+ </term>
+ <listitem>
+ <para>
+ Hibernate libère les développeurs de l'écriture fastidieuse des <emphasis>objets de transfert
+ de données (NdT : Data Transfer Objects)</emphasis> (DTO). Dans une architecture EJB traditionnelle,
+ les DTOs ont deux buts : premièrement, ils contournent le problème des "entity bean" qui ne sont pas
+ sérialisables ; deuxièmement, ils définissent implicitement une phase d'assemblage où toutes les
+ données utilisées par la vue sont rapatriées et organisées dans les DTOs avant de retourner sous le
+ contrôle de la couche de présentation. Hibernate élimine le premier but. Pourtant, vous aurez encore
+ besoin d'une phase d'assemblage (pensez vos méthodes métier comme ayant un contrat strict avec la
+ couche de présentation à propos de quelles données sont disponibles dans les objets détachés)
+ à moins que vous soyez préparés à garder le contexte de
+ persistance (la session) ouvert à travers tout le processus de rendu de la vue.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Pensez à abstraite votre logique métier d'Hibernate.</term>
+ <listitem>
+ <para>
+ Cachez le mécanisme d'accès aux données (Hibernate) derrière une interface. Combinez les patterns
+ <emphasis>DAO</emphasis> et <emphasis>Thread Local Session</emphasis>. Vous pouvez même avoir quelques
+ classes persistées par du JDBC pur, associées à Hibernate via un <literal>UserType</literal> (ce conseil est
+ valable pour des applications de taille respectables ; il n'est pas valable pour une application
+ avec cinq tables).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>N'utilisez pas d'associations de mapping exotiques.</term>
+ <listitem>
+ <para>
+ De bons cas d'utilisation pour de vraies associations plusieurs-vers-plusieurs
+ sont rares. La plupart du temps vous avez besoin d'informations additionnelles
+ stockées dans la table d'association.
+ Dans ce cas, il est préférable d'utiliser deux associations un-vers-plusieurs vers une classe
+ de liaisons intermédiaire. En fait, nous pensons que la plupart des associations sont
+ de type un-vers-plusieurs ou plusieurs-vers-un, vous devez être très attentifs lorsque
+ vous utilisez autre chose et vous demander si c'est vraiment nécessaire.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Préférez les associations bidirectionnelles.</term>
+ <listitem>
+ <para>
+ Les associations unidirectionnelles sont plus difficiles à questionner.
+ Dans une grande application, la plupart des associations devraient être navigables dans les deux directions dans les requêtes.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/collection_mapping.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/collection_mapping.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/collection_mapping.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1216 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<chapter id="collections">
+ <title>Mapping des collections</title>
+
+ <sect1 id="collections-persistent" revision="3">
+ <title>Collections persistantes</title>
+
+ <para>
+ Hibernate requiert que les champs contenant des collections persistantes soient déclarés
+ comme des types d'interface, par exemple :
+ </para>
+
+ <programlisting><![CDATA[public class Product {
+ private String serialNumber;
+ private Set parts = new HashSet();
+
+ public Set getParts() { return parts; }
+ void setParts(Set parts) { this.parts = parts; }
+ public String getSerialNumber() { return serialNumber; }
+ void setSerialNumber(String sn) { serialNumber = sn; }
+}]]></programlisting>
+
+ <para>
+ L'interface réelle devrait être <literal>java.util.Set</literal>,
+ <literal>java.util.Collection</literal>, <literal>java.util.List</literal>,
+ <literal>java.util.Map</literal>, <literal>java.util.SortedSet</literal>,
+ <literal>java.util.SortedMap</literal> ou ... n'importe quoi d'autre ! (Où
+ "n'importe quoi d'autre" signifie que vous devrez écrire une implémentation de
+ <literal>org.hibernate.usertype.UserCollectionType</literal>.)
+ </para>
+
+ <para>
+ Notez comment nous avons initialisé les variables d'instance avec une instance de
+ <literal>HashSet</literal>. C'est le meilleur moyen pour initialiser les
+ collections d'instances nouvellement créées (non persistantes). Quand
+ nous fabriquons l'instance persistante - en appelant <literal>persist()</literal>,
+ par exemple - Hibernate remplacera réellement le <literal>HashSet</literal>
+ avec une instance d'une implémentation propre à Hibernate de <literal>Set</literal>.
+ Prenez garde aux erreurs :
+ </para>
+
+ <programlisting><![CDATA[Cat cat = new DomesticCat();
+Cat kitten = new DomesticCat();
+....
+Set kittens = new HashSet();
+kittens.add(kitten);
+cat.setKittens(kittens);
+session.persist(cat);
+kittens = cat.getKittens(); // Ok, la collection kittens est un Set
+(HashSet) cat.getKittens(); // Erreur !]]></programlisting>
+
+ <para>
+ Les collections persistantes injectées par Hibernate se comportent de la même manière que
+ <literal>HashMap</literal>, <literal>HashSet</literal>,
+ <literal>TreeMap</literal>, <literal>TreeSet</literal> ou
+ <literal>ArrayList</literal>, selon le type de l'interface.
+ </para>
+
+ <para>
+ Les instances des collections ont le comportement habituel des types des valeurs.
+ Elles sont automatiquement persistées quand elles sont référencées par un objet persistant et
+ automatiquement effacées quand elles sont déréférencées. Si une collection est passée
+ d'un objet persistant à un autre, ses éléments pourraient être déplacés d'une table
+ à une autre. Deux entités ne peuvent pas partager une référence vers une même instance
+ d'une collection. Dû au modèle relationnel sous-jacent, les propriétés contenant des
+ collections ne supportent pas la sémantique de la valeur null ; Hibernate ne distingue pas
+ une référence vers une collection nulle d'une collection vide.
+ </para>
+
+ <para>
+ Vous ne devriez pas vous préoccuper trop de ça. Utilisez les collections persistantes de
+ la même manière que vous utilisez des collections Java ordinaires. Assurez-vous
+ de comprendre la sémantique des associations bidirectionnelles (traitée plus loin).
+ </para>
+
+ </sect1>
+
+ <sect1 id="collections-mapping" revision="4">
+ <title>Mapper une collection</title>
+
+ <para>
+ L'élément de mapping d'Hibernate utilisé pour mapper une collection dépend du type de
+ l'interface. Par exemple, un élément <literal><set></literal> est utilisé
+ pour mapper des propriétés de type <literal>Set</literal>.
+ </para>
+
+ <programlisting><![CDATA[<class name="Product">
+ <id name="serialNumber" column="productSerialNumber"/>
+ <set name="parts">
+ <key column="productSerialNumber" not-null="true"/>
+ <one-to-many class="Part"/>
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ À part <literal><set></literal>, il y aussi les éléments de mapping
+ <literal><list></literal>, <literal><map></literal>,
+ <literal><bag></literal>, <literal><array></literal> et
+ <literal><primitive-array></literal>.
+ L'élément <literal><map></literal> est représentatif :
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="mappingcollection1" coords="2 65"/>
+ <area id="mappingcollection2" coords="3 65"/>
+ <area id="mappingcollection3" coords="4 65"/>
+ <area id="mappingcollection4" coords="5 65"/>
+ <area id="mappingcollection5" coords="6 65"/>
+ <area id="mappingcollection6" coords="7 65"/>
+ <area id="mappingcollection7" coords="8 65"/>
+ <area id="mappingcollection8" coords="9 65"/>
+ <area id="mappingcollection9" coords="10 65"/>
+ <area id="mappingcollection10" coords="11 65"/>
+ <area id="mappingcollection11" coords="12 65"/>
+ <area id="mappingcollection12" coords="13 65"/>
+ <area id="mappingcollection13" coords="14 65"/>
+ <area id="mappingcollection14" coords="15 65"/>
+ </areaspec>
+ <programlisting><![CDATA[<map
+ name="nomDePropriete"
+ table="nom_de_table"
+ schema="nom_du_schema"
+ lazy="true|extra|false"
+ inverse="true|false"
+ cascade="all|none|save-update|delete|all-delete-orphan"
+ sort="unsorted|natural|ClasseDeComparateur"
+ order-by="nom_de_column asc|desc"
+ where="condition sql where quelcconque"
+ fetch="join|select|subselect"
+ batch-size="N"
+ access="field|property|NomDeClasse"
+ optimistic-lock="true|false"
+ mutable="true|false"
+ node="nom-d-element|."
+ embed-xml="true|false"
+>
+
+ <key .... />
+ <map-key .... />
+ <element .... />
+</map>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="mappingcollection1">
+ <para>
+ <literal>name</literal> : le nom de la propriété contenant la collection
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection2">
+ <para>
+ <literal>table</literal> (optionnel - par défaut = nom de la propriété) : le
+ nom de la table de la collection (non utilisé pour les associations one-to-many)
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection3">
+ <para>
+ <literal>schema</literal> (optionnel) : le nom du schéma pour surcharger le
+ schéma déclaré dans l'élément racine
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection4">
+ <para>
+ <literal>lazy</literal> (optionnel - par défaut = <literal>true</literal>) :
+ peut être utilisé pour désactiver l'initialisation tardive et spécifier
+ que l'association est toujours rapportée, ou pour activer la
+ récupération extra-paresseuse (NdT : extra-lazy) où la plupart des
+ opérations n'initialisent pas la collection (approprié pour de très
+ grosses collections)
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection5">
+ <para>
+ <literal>inverse</literal> (optionnel - par défaut = <literal>false</literal>) :
+ définit cette collection comme l'extrêmité "inverse" de l'association
+ bidirectionnelle
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection6">
+ <para>
+ <literal>cascade</literal> (optionnel - par défaut = <literal>none</literal>) :
+ active les opérations de cascade vers les entités filles
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection7">
+ <para>
+ <literal>sort</literal> (optionnel) : spécifie une collection triée via un ordre
+ de tri <literal>naturel</literal>, ou via une classe comparateur donnée (implémentant Comparator)
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection8">
+ <para>
+ <literal>order-by</literal> (optionnel, seulement à partir du JDK1.4) :
+ spécifie une colonne de table
+ (ou des colonnes) qui définit l'ordre d'itération de <literal>Map</literal>, <literal>Set</literal>
+ ou Bag, avec en option <literal>asc</literal> ou <literal>desc</literal>
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection9">
+ <para>
+ <literal>where</literal> (optionnel) : spécifie une condition SQL arbitraire <literal>WHERE</literal>
+ à utiliser au chargement ou à la suppression d'une collection (utile si la collection
+ ne doit contenir qu'un sous ensemble des données disponibles)
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection10">
+ <para>
+ <literal>fetch</literal> (optionnel, par défaut = <literal>select</literal>) :
+ à choisir entre récupération par jointures externes, récupération par
+ selects séquentiels, et récupération par sous-selects séquentiels
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection11">
+ <para>
+ <literal>batch-size</literal> (optionnel, par défaut = <literal>1</literal>) : une taille
+ de batch (batch size) utilisée pour charger plusieurs instances de cette collection en
+ initialisation tardive
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection12">
+ <para>
+ <literal>access</literal> (optionnel - par défaut = <literal>property</literal>) : La
+ stratégie qu'Hibernate doit utiliser pour accéder à la valeur de la propriété
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection13">
+ <para>
+ <literal>optimistic-lock</literal> (optionnel - par défaut = <literal>true</literal>) :
+ spécifie que changer l'état de la collection entraîne l'incrémentation
+ de la version appartenant à l'entité (Pour une association un vers plusieurs,
+ il est souvent raisonnable de désactiver ce paramètre)
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection14">
+ <para>
+ <literal>mutable</literal> (optionnel - par défaut = <literal>true</literal>) :
+ une valeur à <literal>false</literal> spécifie que les éléments de la
+ collection ne changent jamais (une optimisation mineure dans certains cas)
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <sect2 id="collections-foreignkeys" >
+ <title>Les clefs étrangères d'une collection</title>
+
+ <para>
+ Les instances d'une collection sont distinguées dans la base par la clef étrangère
+ de l'entité qui possède la collection. Cette clef étrangère est référencée comme la(es)
+ <emphasis>colonne(s) de la clef de la collection</emphasis> de la table de la collection.
+ La colonne de la clef de la collection est mappée par l'élément <literal><key></literal>.
+ </para>
+
+ <para>
+ Il peut y avoir une contrainte de nullité sur la colonne de la clef étrangère. Pour les
+ associations unidirectionnelles un vers plusieurs, la colonne de la clef étrangère
+ peut être nulle par défaut, donc vous pourriez avoir besoin de spécifier
+ <literal>not-null="true"</literal>.
+ </para>
+
+ <programlisting><![CDATA[<key column="productSerialNumber" not-null="true"/>]]></programlisting>
+
+ <para>
+ La contraite de la clef étrangère peut utiliser <literal>ON DELETE CASCADE</literal>.
+ </para>
+
+ <programlisting><![CDATA[<key column="productSerialNumber" on-delete="cascade"/>]]></programlisting>
+
+ <para>
+ Voir le chapitre précédent pour une définition complète de l'élément <literal><key></literal>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-elements" >
+ <title>Les éléments d'une collection</title>
+
+ <para>
+ Les collections peuvent contenir la plupart des autres types Hibernate, dont tous les types
+ basiques, les types utilisateur, les composants, et bien sûr, les références vers
+ d'autres entités. C'est une distinction importante : un objet dans une collection
+ pourrait être géré avec une sémantique de "valeur" (sa durée de vie dépend complètement
+ du propriétaire de la collection) ou il pourrait avoir une référence vers une autre
+ entité, avec sa propre durée de vie. Dans le dernier cas, seul le "lien" entre les 2 objets
+ est considéré être l'état retenu par la collection.
+ </para>
+
+ <para>
+ Le type contenu est référencé comme le <emphasis>type de l'élément de la collection</emphasis>.
+ Les éléments de la collections sont mappés par <literal><element></literal> ou
+ <literal><composite-element></literal>, ou dans le cas des références d'entité, avec
+ <literal><one-to-many></literal> ou <literal><many-to-many></literal>.
+ Les deux premiers mappent des éléments avec un sémantique de valeur, les deux suivants sont
+ utilisés pour mapper des associations d'entité.
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-indexed">
+ <title>Collections indexées</title>
+
+ <para>
+ Tous les mappings de collection, exceptés ceux avec les sémantiques d'ensemble (NdT : set) et
+ de sac (NdT : bag), ont besoin d'une <emphasis>colonne d'index</emphasis> dans la
+ table de la collection - une colonne qui mappe un index de tableau, ou un index de
+ <literal>List</literal>, ou une clef de <literal>Map</literal>. L'index d'une
+ <literal>Map</literal> peut être n'importe quel type basique, mappé avec
+ <literal><map-key></literal>, ça peut être une référence d'entité mappée avec
+ <literal><map-key-many-to-many></literal>, ou ça peut être un type composé, mappé avec
+ <literal><composite-map-key></literal>. L'index d'un tableau ou d'une liste est toujours
+ de type <literal>integer</literal> et est mappé en utilisant l'élément <literal><list-index></literal>.
+ Les colonnes mappées contiennent des entiers séquentiels (numérotés à partir de zéro par défaut).
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="index1" coords="2 45"/>
+ <area id="index2" coords="3 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<list-index
+ column="nom_de_colonne"
+ base="0|1|..."/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="index1">
+ <para>
+ <literal>nom_de_colonne</literal> (requis) : le nom de la colonne contenant les valeurs de l'index de la collection
+ </para>
+ </callout>
+ <callout arearefs="index1">
+ <para>
+ <literal>base</literal> (optionnel, par défaut = <literal>0</literal>) : la valeur
+ de la colonne de l'index qui correspond au premier élément de la liste ou du tableau
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <programlistingco>
+ <areaspec>
+ <area id="mapkey1" coords="2 45"/>
+ <area id="mapkey2" coords="3 45"/>
+ <area id="mapkey3" coords="4 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<map-key
+ column="nom_de_colonne"
+ formula="n'importe quelle expression SQL"
+ type="nom_du_type"
+ node="@nom-d-attribut"
+ length="N"/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="mapkey1">
+ <para>
+ <literal>column</literal> (optionnel) :
+ le nom de la colonne contenant les valeurs de l'index de la collection
+ </para>
+ </callout>
+ <callout arearefs="mapkey2">
+ <para>
+ <literal>formula</literal> (optionnel) :
+ une formule SQL utilisée pour évaluer la clef de la map
+ </para>
+ </callout>
+ <callout arearefs="mapkey3">
+ <para>
+ <literal>type</literal> (reguis): le type des clefs de la map
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <programlistingco>
+ <areaspec>
+ <area id="indexmanytomany1" coords="2 45"/>
+ <area id="indexmanytomany2" coords="3 45"/>
+ <area id="indexmanytomany3" coords="3 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<map-key-many-to-many
+ column="nom_de_colonne"
+ formula="n'importe quelle expression SQL"
+ class="NomDeClasse"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="indexmanytomany1">
+ <para>
+ <literal>column</literal> (optionnel) :
+ le nom de la colonne de la clef étrangère pour les valeurs de l'index de la collection
+ </para>
+ </callout>
+ <callout arearefs="indexmanytomany2">
+ <para>
+ <literal>formula</literal> (optionnel) :
+ une formulre SQL utilisée pour évaluer la clef étrangère de la clef de la map
+ </para>
+ </callout>
+ <callout arearefs="indexmanytomany3">
+ <para>
+ <literal>class</literal> (requis): la classe de l'entité utilisée comme clef de la map
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+ <para>
+ Si votre table n'a pas de colonne d'index, et que vous souhaitez tout de même utiliser
+ <literal>List</literal> comme type de propriété, vous devriez mapper la propriété comme un
+ <emphasis><bag></emphasis> Hibernate. Un sac (NdT : bag) ne garde pas son ordre quand
+ il est récupéré de la base de données, mais il peut être optionnellement trié ou ordonné.
+ </para>
+
+ </sect2>
+
+ <para>
+ Il y a pas mal de variétés de mappings qui peuvent être générés pour les collections,
+ couvrant beaucoup des modèles relationnels communs. Nous vous suggérons d'expérimenter avec l'outil de
+ génération de schéma pour avoir une idée de comment traduire les différentes déclarations de mapping vers des table de la base de données.
+ </para>
+
+ <sect2 id="collections-ofvalues" revision="2">
+ <title>Collections de valeurs et associations plusieurs-vers-plusieurs</title>
+
+ <para>
+ N'importe quelle collection de valeurs ou association plusieurs-vers-plusieurs requiert une
+ <emphasis>table de collection</emphasis> avec une(des) colonne(s) de clef étrangère, une(des)
+ <emphasis>colonne(s) d'élément de la collection</emphasis> ou des colonnes et possiblement
+ une(des) colonne(s) d'index.
+ </para>
+
+ <para>
+ Pour une collection de valeurs, nous utilisons la balise <literal><element></literal>.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="element1b" coords="2 50"/>
+ <area id="element2b" coords="3 50"/>
+ <area id="element3b" coords="4 50"/>
+ </areaspec>
+ <programlisting><![CDATA[<element
+ column="nom_de_colonne"
+ formula="n'importe quelle expression SQL"
+ type="nomDeType"
+ length="L"
+ precision="P"
+ scale="S"
+ not-null="true|false"
+ unique="true|false"
+ node="nom-d-element"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="element1b">
+ <para>
+ <literal>column</literal> (optionnel) : le nom de la colonne contenant les valeurs de l'élément de la collection
+ </para>
+ </callout>
+ <callout arearefs="element2b">
+ <para>
+ <literal>formula</literal> (optionnel) : une formule SQL utilisée pour évaluer l'élément
+ </para>
+ </callout>
+ <callout arearefs="element3b">
+ <para>
+ <literal>type</literal> (requis) : le type de l'élément de la collection
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Une <emphasis>association plusieurs-vers-plusieurs</emphasis> est spécifiée en
+ utilisant l'élément <literal><many-to-many></literal>.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="manytomany1" coords="2 60"/>
+ <area id="manytomany2" coords="3 60"/>
+ <area id="manytomany3" coords="4 60"/>
+ <area id="manytomany4" coords="5 60"/>
+ <area id="manytomany5" coords="6 60"/>
+ <area id="manytomany6" coords="7 60"/>
+ <area id="manytomany7" coords="8 60"/>
+ <area id="manytomany8" coords="9 60"/>
+ </areaspec>
+ <programlisting><![CDATA[<many-to-many
+ column="nom_de_colonne"
+ formula="n'importe quelle expression SQL"
+ class="NomDeClasse"
+ fetch="select|join"
+ unique="true|false"
+ not-found="ignore|exception"
+ entity-name="NomDEntite"
+ property-ref="nomDeProprieteDeLaClasseAssociee"
+ node="nom-d-element"
+ embed-xml="true|false"
+ />]]></programlisting>
+ <calloutlist>
+ <callout arearefs="manytomany1">
+ <para>
+ <literal>column</literal> (optionnel) : le nom de la colonne de la clef étrangère de l'élément
+ </para>
+ </callout>
+ <callout arearefs="manytomany2">
+ <para>
+ <literal>formula</literal> (optionnel) :
+ une formule SQL utilisée pour évaluer la valeur de la clef étrangère de l'élément
+ </para>
+ </callout>
+ <callout arearefs="manytomany3">
+ <para>
+ <literal>class</literal> (requis) : le nom de la classe associée
+ </para>
+ </callout>
+ <callout arearefs="manytomany4">
+ <para>
+ <literal>fetch</literal> (optionnel - par défaut <literal>join</literal>) :
+ active les récupérations par jointures externes ou par selects séquentiels pour cette association.
+ C'est un cas spécial ; pour une récupération complète sans attente (dans un seul <literal>SELECT</literal>) d'une
+ entité et de ses relations plusieurs-vers-plusieurs vers d'autres entités,
+ vous devriez activer la récupération <literal>join</literal> non seulement sur
+ la collection elle-même, mais aussi avec cet attribut sur l'élément imbriqué
+ <literal><many-to-many></literal>.
+ </para>
+ </callout>
+ <callout arearefs="manytomany5">
+ <para>
+ <literal>unique</literal> (optionnel) : activer la génération DDL d'une
+ contrainte d'unicité pour la colonne de la clef étrangère. Ça rend la pluralité
+ de l'association effectivement un-vers-plusieurs.
+ </para>
+ </callout>
+ <callout arearefs="manytomany6">
+ <para>
+ <literal>not-found</literal> (optionnel - par défaut <literal>exception</literal>) :
+ spécifie comment les clefs étrangères qui référencent la lignes
+ manquantes seront gérées : <literal>ignore</literal> traitera
+ une ligne manquante comme une association nulle.
+ </para>
+ </callout>
+ <callout arearefs="manytomany7">
+ <para>
+ <literal>entity-name</literal> (optionnel) : le nom de l'entité de la classe associée, comme une alternative à <literal>class</literal>
+ </para>
+ </callout>
+ <callout arearefs="manytomany8">
+ <para>
+ <literal>property-ref</literal> (optionnel) : le nom d'une propriété de
+ la classe associée qui est jointe à cette clef étrangère. Si non spécifiée,
+ la clef primaire de la classe associée est utilisée.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Quelques exemples, d'abord, un ensemble de chaînes de caractères :
+ </para>
+
+ <programlisting><![CDATA[<set name="names" table="person_names">
+ <key column="person_id"/>
+ <element column="person_name" type="string"/>
+</set>]]></programlisting>
+
+ <para>
+ Un bag contenant des entiers (avec un ordre d'itération déterminé par l'attribut <literal>order-by</literal>) :
+ </para>
+
+ <programlisting><![CDATA[<bag name="sizes"
+ table="item_sizes"
+ order-by="size asc">
+ <key column="item_id"/>
+ <element column="size" type="integer"/>
+</bag>]]></programlisting>
+
+ <para>
+ Un tableau d'entités - dans ce cas, une association plusieurs-vers-plusieurs :
+ </para>
+
+ <programlisting><![CDATA[<array name="addresses"
+ table="PersonAddress"
+ cascade="persist">
+ <key column="personId"/>
+ <list-index column="sortOrder"/>
+ <many-to-many column="addressId" class="Address"/>
+</array>]]></programlisting>
+
+ <para>
+ Une map de chaînes de caractères vers des dates :
+ </para>
+
+ <programlisting><![CDATA[<map name="holidays"
+ table="holidays"
+ schema="dbo"
+ order-by="hol_name asc">
+ <key column="id"/>
+ <map-key column="hol_name" type="string"/>
+ <element column="hol_date" type="date"/>
+</map>]]></programlisting>
+
+ <para>
+ Une liste de composants (discute dans le prochain chapitre) :
+ </para>
+
+ <programlisting><![CDATA[<list name="carComponents"
+ table="CarComponents">
+ <key column="carId"/>
+ <list-index column="sortOrder"/>
+ <composite-element class="CarComponent">
+ <property name="price"/>
+ <property name="type"/>
+ <property name="serialNumber" column="serialNum"/>
+ </composite-element>
+</list>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="collections-onetomany">
+ <title>Association un-vers-plusieurs</title>
+
+ <para>
+ Une <emphasis>association un vers plusieurs</emphasis> lie les tables de deux classes
+ par une clef étrangère, sans l'intervention d'une table de collection. Ce mapping perd certaines sémantiques des collections Java normales :
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ Une instance de la classe de l'entité contenue ne peut pas appartenir à plus d'une
+ instance de la collection
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Une instance de la classe de l'entité contenue ne peut pas apparaître plus plus d'une valeur d'index de la collection
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Une association de <literal>Product</literal> vers <literal>Part</literal> requiert l'existence d'une
+ clef étrangère et possiblement une colonne d'index pour la table <literal>Part</literal>. Une balise
+ <literal><one-to-many></literal> indique que c'est une association un vers plusieurs.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="onetomany1" coords="2 60"/>
+ <area id="onetomany2" coords="3 60"/>
+ <area id="onetomany3" coords="4 60"/>
+ </areaspec>
+ <programlisting><![CDATA[<one-to-many
+ class="NomDeClasse"
+ not-found="ignore|exception"
+ entity-name="NomDEntite"
+ node="nom-d-element"
+ embed-xml="true|false"
+ />]]></programlisting>
+ <calloutlist>
+ <callout arearefs="onetomany1">
+ <para>
+ <literal>class</literal> (requis) : le nom de la classe associée
+ </para>
+ </callout>
+ <callout arearefs="onetomany2">
+ <para>
+ <literal>not-found</literal> (optionnel - par défaut <literal>exception</literal>) :
+ spécifie comment les identifiants cachés qui référencent des lignes manquantes seront gérés :
+ <literal>ignore</literal> traitera une ligne manquante comme une association nulle
+ </para>
+ </callout>
+ <callout arearefs="onetomany3">
+ <para>
+ <literal>entity-name</literal> (optionnel) : le nom de l'entité de la
+ classe associée, comme une alternative à <literal>class</literal>.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Notez que l'élément <literal><one-to-many></literal> n'a pas besoin de déclarer de colonnes. Il n'est pas non plus nécessaire de spécifier le nom de la table nulle part.
+ </para>
+
+ <para>
+ <emphasis>Note très importante :</emphasis> si la colonne de la clef d'une association
+ <literal><one-to-many></literal> est déclarée <literal>NOT NULL</literal>, vous devez déclarer le
+ mapping de <literal><key></literal> avec <literal>not-null="true"</literal> ou
+ <emphasis>utiliser une association bidirectionnelle</emphasis> avec le mapping de la
+ collection marqué <literal>inverse="true"</literal>. Voir la discussion sur les associations bidirectionnelles plus tard dans ce chapitre.
+ </para>
+
+ <para>
+ Cet exemple montre une map d'entités <literal>Part</literal> par nom (où
+ <literal>partName</literal> est une propriété persistante de <literal>Part</literal>).
+ Notez l'utilisation d'un index basé sur une formule.
+ </para>
+
+ <programlisting><![CDATA[<map name="parts"
+ cascade="all">
+ <key column="productId" not-null="true"/>
+ <map-key formula="partName"/>
+ <one-to-many class="Part"/>
+</map>]]></programlisting>
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="collections-advancedmappings">
+ <title>Mappings de collection avancés</title>
+
+ <sect2 id="collections-sorted" revision="2">
+ <title>Collections triées</title>
+
+ <para>
+ Hibernate supporte des collections implémentant <literal>java.util.SortedMap</literal> et
+ <literal>java.util.SortedSet</literal>. Vous devez spécifier un comparateur dans le fichier de mapping :
+ </para>
+
+ <programlisting><![CDATA[<set name="aliases"
+ table="person_aliases"
+ sort="natural">
+ <key column="person"/>
+ <element column="name" type="string"/>
+</set>
+
+<map name="holidays" sort="my.custom.HolidayComparator">
+ <key column="year_id"/>
+ <map-key column="hol_name" type="string"/>
+ <element column="hol_date" type="date"/>
+</map>]]></programlisting>
+
+ <para>
+ Les valeurs permises pour l'attribut <literal>sort</literal> sont <literal>unsorted</literal>,
+ <literal>natural</literal> et le nom d'une classe implémentant
+ <literal>java.util.Comparator</literal>.
+ </para>
+
+ <para>
+ Les collections triées se comportent réellement comme <literal>java.util.TreeSet</literal> ou
+ <literal>java.util.TreeMap</literal>.
+ </para>
+
+ <para>
+ Si vous voulez que la base de données elle-même ordonne les éléments de la collection, utilisez l'attribut
+ <literal>order-by</literal> des mappings <literal>set</literal>, <literal>bag</literal>
+ ou <literal>map</literal>. Cette solution est seulement disponible à partir du JDK 1.4 (c'est
+ implémenté en utilisant <literal>LinkedHashSet</literal> ou
+ <literal>LinkedHashMap</literal>). Ceci exécute le tri dans la requête SQL, pas en mémoire.
+ </para>
+
+ <programlisting><![CDATA[<set name="aliases" table="person_aliases" order-by="lower(name) asc">
+ <key column="person"/>
+ <element column="name" type="string"/>
+</set>
+
+<map name="holidays" order-by="hol_date, hol_name">
+ <key column="year_id"/>
+ <map-key column="hol_name" type="string"/>
+ <element column="hol_date type="date"/>
+</map>]]></programlisting>
+
+ <para>
+ Notez que la valeur de l'attribut <literal>order-by</literal> est un ordre SQL, pas un ordre HQL !
+ </para>
+
+ <para>
+ Les associations peuvent même être triées sur des critères arbitraires à l'exécution en utilisant un <literal>filter()</literal> de collection.
+ </para>
+
+ <programlisting><![CDATA[sortedUsers = s.createFilter( group.getUsers(), "order by this.name" ).list();]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="collections-bidirectional" revision="1">
+ <title>Associations bidirectionnelles</title>
+
+ <para>
+ Une <emphasis>association bidirectionnelle</emphasis> permet une navigation à
+ partir de la "fin" de l'association. Deux sortes d'associations bidirectionnelles sont supportées :
+ <variablelist>
+ <varlistentry>
+ <term>un-vers-plusieurs (NdT : one-to-many)</term>
+ <listitem>
+ <para>
+ ensemble ou sac à une extrémité, une seule valeur à l'autre
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>plusieurs-vers-plusieurs (NdT : many-to-many)</term>
+ <listitem>
+ <para>
+ ensemble ou sac aux deux extrémités
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ <para>
+ Vous pouvez spécifier une association plusieurs-vers-plusieurs bidirectionnelle simplement
+ en mappant deux associations plusieurs-vers-plusieurs vers la même table de base de données et en déclarant une extrémité comme <emphasis>inverse</emphasis> (celle de votre choix, mais ça ne peut pas être une collection indexée).
+ </para>
+
+ <para>
+ Voici un exemple d'association bidirectionnelle plusieurs-vers-plusieurs ; chaque catégorie peut
+ avoir plusieurs objets et chaque objet peut être dans plusieurs catégories :
+ </para>
+
+ <programlisting><![CDATA[<class name="Category">
+ <id name="id" column="CATEGORY_ID"/>
+ ...
+ <bag name="items" table="CATEGORY_ITEM">
+ <key column="CATEGORY_ID"/>
+ <many-to-many class="Item" column="ITEM_ID"/>
+ </bag>
+</class>
+
+<class name="Item">
+ <id name="id" column="CATEGORY_ID"/>
+ ...
+
+ <!-- inverse end -->
+ <bag name="categories" table="CATEGORY_ITEM" inverse="true">
+ <key column="ITEM_ID"/>
+ <many-to-many class="Category" column="CATEGORY_ID"/>
+ </bag>
+</class>]]></programlisting>
+
+ <para>
+ Les changements faits uniquement sur l'extréminté inverse de l'association <emphasis>ne sont pas</emphasis>
+ persistés. Ceci signifie qu'Hibernate a deux représentations en mémoire pour chaque
+ association bidirectionnelles, un lien de A vers B et un autre de B vers A. C'est
+ plus facile à comprendre si vous pensez au modèle objet de Java et comment nous
+ créons une relation plusieurs-vers-plusieurs en Java :
+ </para>
+
+ <programlisting><![CDATA[
+category.getItems().add(item); // La catégorie est maintenant "au courant" de la relation
+item.getCategories().add(category); // L'objet est maintenant "au courant" de la relation
+
+session.persist(item); // La relation ne sera pas sauvegardée !
+session.persist(category); // La relation sera sauvegardée]]></programlisting>
+
+ <para>
+ La partie non-inverse est utilisée pour sauvegarder la représentation en mémoire dans la base de données.
+ </para>
+
+ <para>
+ Vous pouvez définir une association un-vers-plusieurs bidirectionnelle en mappant une
+ association un-vers-plusieurs vers la(es) même(s) colonne(s) de table qu'une association
+ plusieurs-vers-un et en déclarant l'extrémité pluri-valuée <literal>inverse="true"</literal>.
+ </para>
+
+ <programlisting><![CDATA[<class name="Parent">
+ <id name="id" column="parent_id"/>
+ ....
+ <set name="children" inverse="true">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+ </set>
+</class>
+
+<class name="Child">
+ <id name="id" column="child_id"/>
+ ....
+ <many-to-one name="parent"
+ class="Parent"
+ column="parent_id"
+ not-null="true"/>
+</class>]]></programlisting>
+
+ <para>
+ Mapper une extrémité d'une association avec <literal>inverse="true"</literal> n'affecte
+ pas l'opération de cascades, ce sont des concepts orthogonaux !
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-indexedbidirectional">
+ <title>Associations bidirectionnelles avec des collections indexées</title>
+ <para>
+ Une association bidirectionnelle où une extrémité est représentée comme une <literal><list></literal>
+ ou une <literal><map></literal> requiert une considération spéciale. Si il y a une
+ propriété de la classe enfant qui mappe la colonne de l'index, pas de problème, nous pouvons
+ continuer à utiliser <literal>inverse="true"</literal> sur le mapping de la collection :
+ </para>
+
+ <programlisting><![CDATA[<class name="Parent">
+ <id name="id" column="parent_id"/>
+ ....
+ <map name="children" inverse="true">
+ <key column="parent_id"/>
+ <map-key column="name"
+ type="string"/>
+ <one-to-many class="Child"/>
+ </map>
+</class>
+
+<class name="Child">
+ <id name="id" column="child_id"/>
+ ....
+ <property name="name"
+ not-null="true"/>
+ <many-to-one name="parent"
+ class="Parent"
+ column="parent_id"
+ not-null="true"/>
+</class>]]></programlisting>
+
+ <para>
+ Mais, si il n'y a pas de telle prorpriété sur la classe enfant, nous ne pouvons pas penser
+ à l'association comme vraiment bidirectionnelle (il y a des informations disponibles à une
+ extrémité de l'association qui ne sont pas disponibles à l'autre extrémité). Dans ce cas,
+ nous ne pouvons pas mapper la collection <literal>inverse="true"</literal>. À la place, nous
+ pourrions utiliser le mapping suivant :
+ </para>
+
+ <programlisting><![CDATA[<class name="Parent">
+ <id name="id" column="parent_id"/>
+ ....
+ <map name="children">
+ <key column="parent_id"
+ not-null="true"/>
+ <map-key column="name"
+ type="string"/>
+ <one-to-many class="Child"/>
+ </map>
+</class>
+
+<class name="Child">
+ <id name="id" column="child_id"/>
+ ....
+ <many-to-one name="parent"
+ class="Parent"
+ column="parent_id"
+ insert="false"
+ update="false"
+ not-null="true"/>
+</class>]]></programlisting>
+
+ <para>
+ Notez que dans ce mapping, l'extrémité de l'association contenant la collection est responsable
+ des mises à jour de la clef étrangère. À faire : cela entraîne-t-il réellement des expressions
+ updates inutiles ?
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-ternary">
+ <title>Associations ternaires</title>
+
+ <para>
+ Il y a trois approches possibles pour mapper une association ternaire. L'une est d'utiliser
+ une <literal>Map</literal> avec une association en tant qu'index :
+ </para>
+
+ <programlisting><![CDATA[<map name="contracts">
+ <key column="employer_id" not-null="true"/>
+ <map-key-many-to-many column="employee_id" class="Employee"/>
+ <one-to-many class="Contract"/>
+</map>]]></programlisting>
+
+ <programlisting><![CDATA[<map name="connections">
+ <key column="incoming_node_id"/>
+ <map-key-many-to-many column="outgoing_node_id" class="Node"/>
+ <many-to-many column="connection_id" class="Connection"/>
+</map>]]></programlisting>
+
+ <para>
+ Une seconde approche est simplement de remodeler l'association comme une classe d'entité. C'est
+ l'approche la plus commune.
+ </para>
+
+ <para>
+ Une alternative finale est d'utiliser des éléments composites, dont nous discuterons plus tard.
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-idbag" revision="1">
+ <title>Utiliser un <literal><idbag></literal></title>
+
+ <para>
+ Si vous embrassez pleinement notre vue que les clefs composées sont une mauvaise
+ chose et que des entités devraient avoir des identifiants artificiels (des clefs
+ subrogées), alors vous pourriez trouver un peu curieux que les associations
+ plusieurs-vers-plusieurs et les collections de valeurs que nous avons montré jusqu'ici
+ mappent toutes des tables avec des clefs composées ! Maintenant, ce point est assez
+ discutable ; une table d'association pure ne semble pas beaucoup bénéficier d'une clef
+ subrogée (bien qu'une collection de valeur composées le <emphasis>pourrait</emphasis>).
+ Néanmoins, Hibernate fournit une foncionnalité qui vous permet de mapper
+ des associations plusieurs-vers-plusieurs et des collections de valeurs vers une
+ table avec une clef subrogée.
+ </para>
+
+ <para>
+ L'élément <literal><idbag></literal> vous laisse mapper une <literal>List</literal>
+ (ou une <literal>Collection</literal>) avec une sémantique de sac.
+ </para>
+
+<programlisting><![CDATA[<idbag name="lovers" table="LOVERS">
+ <collection-id column="ID" type="long">
+ <generator class="sequence"/>
+ </collection-id>
+ <key column="PERSON1"/>
+ <many-to-many column="PERSON2" class="Person" fetch="join"/>
+</idbag>]]></programlisting>
+
+ <para>
+ Comme vous pouvez voir, un <literal><idbag></literal> a un généréteur d'id
+ artificiel, comme une classe d'entité ! Une clef subrogée différente est assignée
+ à chaque ligne de la collection. Cependant, Hibernate ne fournit pas de mécanisme pour
+ découvrir la valeur d'une clef subrogée d'une ligne particulière.
+ </para>
+
+ <para>
+ Notez que les performances de la mise à jour d'un <literal><idbag></literal>
+ sont <emphasis>bien</emphasis> meilleures qu'un <literal><bag></literal> ordinaire !
+ Hibernate peut localiser des lignes individuelles efficacement et les mettre à jour ou
+ les effacer individuellement, comme une liste, une map ou un ensemble.
+ </para>
+
+ <para>
+ Dans l'implémentation actuelle, la stratégie de la génération de l'identifiant <literal>native</literal>
+ n'est pas supportée pour les identifiants de collection <literal><idbag></literal>.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <!--undocumenting this stuff -->
+
+ <!--sect1 id="collections-heterogeneous">
+ <title>Heterogeneous Associations</title>
+
+ <para>
+ The <literal><many-to-any></literal> and <literal><index-many-to-any></literal>
+ elements provide for true heterogeneous associations. These mapping elements work in the
+ same way as the <literal><any></literal> element - and should also be used
+ rarely, if ever.
+ </para>
+
+ </sect1-->
+
+ <sect1 id="collections-example" revision="1">
+ <title>Exemples de collections</title>
+
+ <para>
+ Les sections précédentes sont assez confuses. Donc prenons un exemple. Cette classe :
+ </para>
+
+ <programlisting><![CDATA[package eg;
+import java.util.Set;
+
+public class Parent {
+ private long id;
+ private Set children;
+
+ public long getId() { return id; }
+ private void setId(long id) { this.id=id; }
+
+ private Set getChildren() { return children; }
+ private void setChildren(Set children) { this.children=children; }
+
+ ....
+ ....
+}]]></programlisting>
+
+ <para>
+ a une collection d'instances de <literal>Child</literal>. Si chaque enfant
+ a au plus un parent, le mapping le plus naturel est une association
+ un-vers-plusieurs :
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Parent">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <set name="children">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+ </set>
+ </class>
+
+ <class name="Child">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Ceci mappe les définitions de tables suivantes :
+ </para>
+
+ <programlisting><![CDATA[create table parent ( id bigint not null primary key )
+create table child ( id bigint not null primary key, name varchar(255), parent_id bigint )
+alter table child add constraint childfk0 (parent_id) references parent]]></programlisting>
+
+ <para>
+ Si le parent est <emphasis>requis</emphasis>, utilisez une association un-vers-plusieurs unidirectionnelle :
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Parent">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <set name="children" inverse="true">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+ </set>
+ </class>
+
+ <class name="Child">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <property name="name"/>
+ <many-to-one name="parent" class="Parent" column="parent_id" not-null="true"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Notez la contrainte <literal>NOT NULL</literal> :
+ </para>
+
+ <programlisting><![CDATA[create table parent ( id bigint not null primary key )
+create table child ( id bigint not null
+ primary key,
+ name varchar(255),
+ parent_id bigint not null )
+alter table child add constraint childfk0 (parent_id) references parent]]></programlisting>
+
+ <para>
+ Alternativement, si vous insistez absolument pour que cette association soit unidirectionnelle,
+ vous pouvez déclarer la contrainte <literal>NOT NULL</literal> sur le mapping <literal><key></literal> :
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Parent">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <set name="children">
+ <key column="parent_id" not-null="true"/>
+ <one-to-many class="Child"/>
+ </set>
+ </class>
+
+ <class name="Child">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ D'un autre côté, si un enfant pouvait avoir plusieurs parent, une association
+ plusieurs-vers-plusieurs est plus appropriée :
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Parent">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <set name="children" table="childset">
+ <key column="parent_id"/>
+ <many-to-many class="Child" column="child_id"/>
+ </set>
+ </class>
+
+ <class name="Child">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Définitions des tables :
+ </para>
+
+ <programlisting><![CDATA[create table parent ( id bigint not null primary key )
+create table child ( id bigint not null primary key, name varchar(255) )
+create table childset ( parent_id bigint not null,
+ child_id bigint not null,
+ primary key ( parent_id, child_id ) )
+alter table childset add constraint childsetfk0 (parent_id) references parent
+alter table childset add constraint childsetfk1 (child_id) references child]]></programlisting>
+
+ <para>
+ Pour plus d'exemples et une revue complète du mapping de la relation parent/enfant, voir
+ see <xref linkend="example-parentchild"/>.
+ </para>
+
+ <para>
+ Des mappings d'association plus exotiques sont possibles, nous cataloguerons toutes les possibilités
+ dans le prochain chapitre.
+ </para>
+
+ </sect1>
+
+</chapter>
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/component_mapping.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/component_mapping.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/component_mapping.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,402 @@
+<?xml version='1.0' encoding="iso-8859-1"?>
+<chapter id="components">
+ <title>Mapping de composants</title>
+
+ <para>
+ La notion de <emphasis>composants</emphasis> est réutilisé dans différents contextes,
+ avec différents objectifs, à travers Hibernate.
+ </para>
+
+ <sect1 id="components-dependentobjects" revision="2" >
+ <title>Objects dépendants</title>
+
+ <para>
+ Le composant est un objet inclu dans un autre qui est sauvegardé comme une valeur, et
+ non pas comme une entité.
+ Le composant fait référence à la notion (au sens objet) de composition
+ (et non pas de composant au sens d'architecture de composants).
+ Par exemple on pourrait modélisé l'objet personne de cette façon:
+ </para>
+
+ <programlisting><![CDATA[public class Person {
+ private java.util.Date birthday;
+ private Name name;
+ private String key;
+ public String getKey() {
+ return key;
+ }
+ private void setKey(String key) {
+ this.key=key;
+ }
+ public java.util.Date getBirthday() {
+ return birthday;
+ }
+ public void setBirthday(java.util.Date birthday) {
+ this.birthday = birthday;
+ }
+ public Name getName() {
+ return name;
+ }
+ public void setName(Name name) {
+ this.name = name;
+ }
+ ......
+ ......
+}]]></programlisting>
+
+<programlisting><![CDATA[public class Name {
+ char initial;
+ String first;
+ String last;
+ public String getFirst() {
+ return first;
+ }
+ void setFirst(String first) {
+ this.first = first;
+ }
+ public String getLast() {
+ return last;
+ }
+ void setLast(String last) {
+ this.last = last;
+ }
+ public char getInitial() {
+ return initial;
+ }
+ void setInitial(char initial) {
+ this.initial = initial;
+ }
+}]]></programlisting>
+
+ <para>
+ Maintenant <literal>Name</literal> peut-être sauvegardé comme un composant de
+ <literal>Person</literal>. Remarquer que <literal>Name</literal> définit des methodes
+ d'accès et de modification pour ses propriétés persistantes, mais il n'a pas besoin
+ des interfaces ou des propriétés d'identification ( par exemple getId() ) qui sont propres aux entités.
+ </para>
+
+ <para>
+ Nous serions alors amené à mapper ce composant de cette façon:
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Person" table="person">
+ <id name="Key" column="pid" type="string">
+ <generator class="uuid"/>
+ </id>
+ <property name="birthday" type="date"/>
+ <component name="Name" class="eg.Name"> <!-- class attribute optional -->
+ <property name="initial"/>
+ <property name="first"/>
+ <property name="last"/>
+ </component>
+</class>]]></programlisting>
+
+ <para>
+ La table person aurai les colonnes <literal>pid</literal>,
+ <literal>birthday</literal>,
+ <literal>initial</literal>,
+ <literal>first</literal> and
+ <literal>last</literal>.
+ </para>
+
+ <para>
+ Comme tous les types valeurs, les composants ne supportent pas les références partagés.
+ En d'autres mots, deux instances de person peuvent avoir un même nom, mais ces noms sont
+ indépendants, ils peuvent être identiques si on les compare par valeur mais ils représentent
+ deux objets distincts en mémoire. La notion de nullité pour un composant est
+ <emphasis>ad hoc</emphasis>. Quand il recharge l'objet qui contient le composant, Hibernate
+ supposera que si tous les champs du composants sont nuls alors le composant sera positionné
+ à la valeur null. Ce choix programmatif devrait être satisfaisant dans la plupart des cas.
+ </para>
+
+ <para>
+ Les propriétés d'un composant peuvent être de tous les types qu'Hibernate supporte habituellement
+ (collections, many-to-one associations, autres composants, etc). Les composants inclus ne doivent <emphasis>pas</emphasis>
+ être vus comme quelque chose d'exotique. Hibernate a été conçu pour supporter un modèle objet très granulaire.
+ </para>
+
+ <para>
+ Le <literal><component></literal> peut inclure dans la liste de ses propriétés
+ une référence au <literal><parent></literal> conteneur.
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Person" table="person">
+ <id name="Key" column="pid" type="string">
+ <generator class="uuid"/>
+ </id>
+ <property name="birthday" type="date"/>
+ <component name="Name" class="eg.Name" unique="true">
+ <parent name="namedPerson"/> <!-- référence arrière à Person -->
+ <property name="initial"/>
+ <property name="first"/>
+ <property name="last"/>
+ </component>
+</class>]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="components-incollections" revision="1">
+ <title>Collection d'objets dépendants</title>
+
+ <para>
+ Les collections d'objets dépendants sont supportés (exemple: un tableau de type
+ <literal>Name</literal>). Déclarer la collection de composants en remplaçant le tag <literal><element></literal>
+ par le tag <literal><composite-element></literal>.
+ </para>
+
+ <programlisting><![CDATA[<set name="someNames" table="some_names" lazy="true">
+ <key column="id"/>
+ <composite-element class="eg.Name"> <!-- class attribute required -->
+ <property name="initial"/>
+ <property name="first"/>
+ <property name="last"/>
+ </composite-element>
+</set>]]></programlisting>
+
+ <para>
+ Remarque: Si vous définissez un <literal>Set</literal> d'élément composite,
+ il est très important d'implémenter la méthode <literal>equals()</literal> et
+ <literal>hashCode()</literal> correctement.
+ </para>
+
+ <para>
+ Les élements composite peuvent aussi contenir des composants mais pas des collections.
+ Si votre élément composite contient aussi des composants, utilisez l'élément <literal><nested-composite-element></literal>
+ . Une collections de composants qui ccontiennent eux-mêmes des composants est un cas très exotique.
+ A ce stade demandez-vous si une association un-à-plusieurs ne serait pas plus approprié.
+ Essayez de re remodeler votre élément composite comme une entité ( Dans ce cas même si le modèle
+ Java est le même la logique de persitence et de relation sont tout de même différentes)
+ </para>
+
+ <para>
+ Remarque, le mapping d'éléments composites ne supporte pas la nullité des
+ propriétés lorsqu'on utilise un <literal><set></literal>. Hibernate
+ lorsqu'il supprime un objet utilise chaque colonne pour identifier un objet
+ (on ne peut pas utiliser des clés primaires distinctes dans une table d'éléments composites),
+ ce qui n'est pas possible avec des valeurs nulles. Vous devez donc choisir d'interdire la nullité
+ des propriétés d'un élément composite ou choisir un autre type de collection comme :
+ <literal><list></literal>, <literal><map></literal>,
+ <literal><bag></literal> ou <literal><idbag></literal>.
+ </para>
+
+ <para>
+ Un cas particulier d'élément composite est un élément composite qui inclut un élément
+ <literal><many-to-one></literal>. Un mapping comme celui-ci
+ vous permet d'associer les colonnes d'une table d'association plusieurs à plusieurs (many-to-many)
+ à la classse de l'élément composite. L'exemple suivant est une association plusieurs à plusieurs
+ de <literal>Order</literal> à <literal>Item</literal> à
+ <literal>purchaseDate</literal>, <literal>price</literal> et
+ <literal>quantity</literal> sont des propriétés de l'association.
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Order" .... >
+ ....
+ <set name="purchasedItems" table="purchase_items" lazy="true">
+ <key column="order_id">
+ <composite-element class="eg.Purchase">
+ <property name="purchaseDate"/>
+ <property name="price"/>
+ <property name="quantity"/>
+ <many-to-one name="item" class="eg.Item"/> <!-- class attribute is optional -->
+ </composite-element>
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ Bien sûr, il ne peut pas y avoir de référence à l'achat (purchase) depuis l'article (item), pour
+ pouvoir naviguer de façon bidirectionnelle dans l'association. N'oubliez pas que les composants
+ sont de type valeurs et n'autorise pas les références partagées.
+ </para>
+
+ <para>Même les associations ternaires ou quaternaires sont possibles:</para>
+
+ <programlisting><![CDATA[<class name="eg.Order" .... >
+ ....
+ <set name="purchasedItems" table="purchase_items" lazy="true">
+ <key column="order_id">
+ <composite-element class="eg.OrderLine">
+ <many-to-one name="purchaseDetails class="eg.Purchase"/>
+ <many-to-one name="item" class="eg.Item"/>
+ </composite-element>
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ Les éléments composites peuvent apparaître dans les requêtes en utilisant
+ la même syntaxe que associations
+ </para>
+
+ </sect1>
+
+ <sect1 id="components-asmapindex">
+ <title>Utiliser les composants comme index de map</title>
+
+ <para>
+ l'élément <literal><composite-map-key></literal>
+ vous permet d'utiliser une classe de composant comme indice de
+ <literal>Map</literal>. Assurez-vous d'avoir surdéfini
+ <literal>hashCode()</literal> et <literal>equals()</literal> dans la
+ classe du composant.
+ </para>
+ </sect1>
+
+ <sect1 id="components-compositeid" revision="1">
+ <title>Utiliser un composant comme identifiant</title>
+
+ <para>
+ Vous pouvez utiliser un composant comme identifiant d'une entité.
+ Mais pour cela la classe du composant doit respecter certaines règles.
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ Elle doit implémenter <literal>java.io.Serializable</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Elle doit redéfinir <literal>equals()</literal> et
+ <literal>hashCode()</literal>, de façon cohérente avec le
+ fait qu'elle définit une clé composite dans la base de
+ données.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ <emphasis>
+ Remarque: avec hibernate3, la seconde règle n'est plus absolument
+ necessaire mais faîtes le quand même.</emphasis>
+ </para>
+
+ <para>
+ Vous ne pouvez pas utiliser de <literal>IdentifierGenerator</literal> pour générer
+ une clé composite, l'application devra définir elle même ses propres identifiants.
+ </para>
+
+ <para>
+ Utiliser l'élément <literal><composite-id></literal> (en incluant l'élément
+ <literal><key-property></literal>) à la place de l'habituel déclaration
+ <literal><id></literal>. Par exemple la classe
+ <literal>OrderLine</literal> qui dépend de la clé primaire
+ (composite) de <literal>Order</literal>.
+ </para>
+
+ <programlisting><![CDATA[<class name="OrderLine">
+
+ <composite-id name="id" class="OrderLineId">
+ <key-property name="lineId"/>
+ <key-property name="orderId"/>
+ <key-property name="customerId"/>
+ </composite-id>
+
+ <property name="name"/>
+
+ <many-to-one name="order" class="Order"
+ insert="false" update="false">
+ <column name="orderId"/>
+ <column name="customerId"/>
+ </many-to-one>
+ ....
+
+</class>]]></programlisting>
+
+ <para>
+ Maintenant toutes clés étrangères référençant la table <literal>OrderLine</literal>
+ devra aussi être composite. Vous devez en tenir compte lorsque vous écrivez vos mapping d'association pour les autres classes.
+ Une association à <literal>OrderLine</literal> devrait être mappé de la façon suivante :
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="orderLine" class="OrderLine">
+<!-- the "class" attribute is optional, as usual -->
+ <column name="lineId"/>
+ <column name="orderId"/>
+ <column name="customerId"/>
+</many-to-one>]]></programlisting>
+
+ <para>
+ (Remarque: l'élément <literal><column></literal> est une alternative à l'attribut
+ <literal>column</literal> que l'on utilise partout.)
+ </para>
+
+ <para>
+ Une association <literal>plusieurs-à-plusieurs</literal> (many-to-many) à <literal>OrderLine</literal>
+ utilisera aussi une clé étrangère composite:
+ </para>
+
+ <programlisting><![CDATA[<set name="undeliveredOrderLines">
+ <key column name="warehouseId"/>
+ <many-to-many class="OrderLine">
+ <column name="lineId"/>
+ <column name="orderId"/>
+ <column name="customerId"/>
+ </many-to-many>
+</set>]]></programlisting>
+
+ <para>
+ La collection des <literal>OrderLine</literal>s dans <literal>Order</literal>
+ utilisera:
+ </para>
+
+ <programlisting><![CDATA[<set name="orderLines" inverse="true">
+ <key>
+ <column name="orderId"/>
+ <column name="customerId"/>
+ </key>
+ <one-to-many class="OrderLine"/>
+</set>]]></programlisting>
+
+ <para>
+ (L'élément <literal><one-to-many></literal>, comme d'habitude, ne déclare pas de colonne.)
+ </para>
+
+ <para>
+ Si <literal>OrderLine</literal> lui-même possède une collection, celle-ci aura aussi
+ une clé composite étrangère.
+ </para>
+
+ <programlisting><![CDATA[<class name="OrderLine">
+ ....
+ ....
+ <list name="deliveryAttempts">
+ <key> <!-- a collection inherits the composite key type -->
+ <column name="lineId"/>
+ <column name="orderId"/>
+ <column name="customerId"/>
+ </key>
+ <list-index column="attemptId" base="1"/>
+ <composite-element class="DeliveryAttempt">
+ ...
+ </composite-element>
+ </set>
+</class>]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="components-dynamic" revision="1">
+ <title>Composant Dynamique</title>
+
+ <para>
+ Vous pouvez même mapper une propriété de type <literal>Map</literal>:
+ </para>
+
+ <programlisting><![CDATA[<dynamic-component name="userAttributes">
+ <property name="foo" column="FOO"/>
+ <property name="bar" column="BAR"/>
+ <many-to-one name="baz" class="Baz" column="BAZ_ID"/>
+</dynamic-component>]]></programlisting>
+
+ <para>
+ La sémantique de l'association à un <literal><dynamic-component></literal>
+ est identique à celle que l'on utilise pour les composants.
+ L'avantage de ce type de mapping est qu'il pemet de déterminer les véritables propriétés
+ du bean au moment su déploiement en éditant simplement le document de mapping.
+ La manipulation du document de mapping pendant l'execution de l'application est aussi
+ possible en utilisant un parser DOM. Il ya même mieux, vous pouvez accéder (et changer)
+ le metamodel de configuration d'hibernate en utilisant l'objet <literal>Configuration</literal>
+ </para>
+
+ </sect1>
+
+</chapter>
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/configuration.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/configuration.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/configuration.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1759 @@
+<?xml version='1.0' encoding="iso-8859-1"?>
+<chapter id="session-configuration" revision="1">
+
+ <title>Configuration</title>
+
+ <para>
+ Parce qu'Hibernate est conçu pour fonctionner dans différents environnements,
+ il existe beaucoup de paramètres de configuration. Heureusement, la plupart
+ ont des valeurs par défaut appropriées et la distribution d'Hibernate contient
+ un exemple de fichier <literal>hibernate.properties</literal> dans le répertoire
+ <literal>etc/</literal> qui montre les différentes options. Vous n'avez qu'à
+ placer ce fichier dans votre classpath et à l'adapter.
+ </para>
+
+ <sect1 id="configuration-programmatic" revision="1">
+ <title>Configuration par programmation</title>
+
+ <para>
+ Une instance de <literal>org.hibernate.cfg.Configuration</literal>
+ représente un ensemble de mappings des classes Java d'une application vers
+ la base de données SQL. La <literal>Configuration</literal> est utilisée
+ pour construire un objet (immuable) <literal>SessionFactory</literal>.
+ Les mappings sont constitués d'un ensemble de fichiers de mapping XML.
+ </para>
+
+ <para>
+ Vous pouvez obtenir une instance de <literal>Configuration</literal>
+ en l'instanciant directement et en spécifiant la liste des documents
+ XML de mapping. Si les fichiers de mapping sont dans le classpath, vous
+ pouvez le faire à l'aide de la méthode <literal>addResource()</literal> :
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = new Configuration()
+ .addResource("Item.hbm.xml")
+ .addResource("Bid.hbm.xml");]]></programlisting>
+
+ <para>
+ Une alternative (parfois meilleure) est de spécifier les classes mappées
+ et de laisser Hibernate trouver les documents de mapping pour vous :
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = new Configuration()
+ .addClass(org.hibernate.auction.Item.class)
+ .addClass(org.hibernate.auction.Bid.class);]]></programlisting>
+
+ <para>
+ Hibernate va rechercher les fichiers de mappings
+ <literal>/org/hibernate/auction/Item.hbm.xml</literal> et
+ <literal>/org/hibernate/auction/Bid.hbm.xml</literal> dans le classpath.
+ Cette approche élimine les noms de fichiers en dur.
+ </para>
+
+ <para>
+ Une <literal>Configuration</literal> vous permet également de préciser des
+ propriétés de configuration :
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = new Configuration()
+ .addClass(org.hibernate.auction.Item.class)
+ .addClass(org.hibernate.auction.Bid.class)
+ .setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLInnoDBDialect")
+ .setProperty("hibernate.connection.datasource", "java:comp/env/jdbc/test")
+ .setProperty("hibernate.order_updates", "true");]]></programlisting>
+
+ <para>
+ Ce n'est pas le seul moyen de passer des propriétés de configuration à Hibernate.
+ Les différentes options sont :
+ </para>
+
+ <orderedlist spacing="compact">
+ <listitem>
+ <para>
+ Passer une instance de <literal>java.util.Properties</literal>
+ à <literal>Configuration.setProperties()</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Placer <literal>hibernate.properties</literal> dans un répertoire racine
+ du classpath
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Positionner les propriétés <literal>System</literal> en utilisant
+ <literal>java -Dproperty=value</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Inclure des éléments <literal><property></literal> dans le
+ fichier <literal>hibernate.cfg.xml</literal> (voir plus loin).
+ </para>
+ </listitem>
+ </orderedlist>
+
+ <para>
+ L'utilisation d'<literal>hibernate.properties</literal> est l'approche la plus
+ simple si vous voulez démarrer rapidement
+ </para>
+ <para>
+ La <literal>Configuration</literal> est un objet de démarrage qui sera supprimé
+ une fois qu'une <literal>SessionFactory</literal> aura été créée.
+ </para>
+
+ </sect1>
+
+ <sect1 id="configuration-sessionfactory">
+ <title>Obtenir une SessionFactory</title>
+
+ <para>
+ Une fois que tous les mappings ont été parsés par la <literal>Configuration</literal>,
+ l'application doit obtenir une fabrique d'instances de <literal>Session</literal>.
+ Cette fabrique sera partagée entre tous les threads de l'application :
+ </para>
+
+ <programlisting><![CDATA[SessionFactory sessions = cfg.buildSessionFactory();]]></programlisting>
+
+ <para>
+ Hibernate permet à votre application d'instancier plus d'une <literal>SessionFactory</literal>.
+ Cela est pratique lorsque vous utilisez plus d'une base de données.
+ </para>
+
+ </sect1>
+
+ <sect1 id="configuration-hibernatejdbc" revision="1">
+ <title>Connexions JDBC</title>
+
+ <para>
+ Habituellement, vous voulez que la <literal>SessionFactory</literal> crée les connexions JDBC et
+ les mette dans un pool pour vous. Si vous suivez cette approche, ouvrir une <literal>Session</literal>
+ est aussi simple que :
+ </para>
+
+ <programlisting><![CDATA[Session session = sessions.openSession(); // open a new Session]]></programlisting>
+
+ <para>
+ Dès que vous ferez quelquechose qui requiert un accès à la base de données, une connexion
+ JDBC sera récupérée dans le pool.
+ </para>
+
+ <para>
+ Pour faire cela, il faut passer les propriétés de la connexion JDBC à Hibernate.
+ Tous les noms des propriétés Hibernate et leur signification sont définies dans
+ la classe <literal>org.hibernate.cfg.Environment</literal>. Nous allons maintenant
+ décrire les paramètres de configuration des connexions JDBC les plus importants.
+ </para>
+
+ <para>
+ Hibernate obtiendra des connexions (et les mettra dans un pool) en utilisant
+ <literal>java.sql.DriverManager</literal> si vous positionnez les paramètres de la manière
+ suivante :
+ </para>
+
+ <table frame="topbot">
+ <title>Propriétés JDBC d'Hibernate</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Nom de la propriété</entry>
+ <entry>Fonction</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.connection.driver_class</literal>
+ </entry>
+ <entry>
+ <emphasis>Classe du driver jdbc</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.url</literal>
+ </entry>
+ <entry>
+ <emphasis>URL jdbc</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.username</literal>
+ </entry>
+ <entry>
+ <emphasis>utilisateur de la base de données</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.password</literal>
+ </entry>
+ <entry>
+ <emphasis>mot de passe de la base de données</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.pool_size</literal>
+ </entry>
+ <entry>
+ <emphasis>nombre maximum de connexions dans le pool</emphasis>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ L'algorithme natif de pool de connexions d'Hibernate est plutôt rudimentaire. Il a été fait
+ dans le but de vous aider à démarrer et <emphasis>n'est pas prévu pour un système en production</emphasis>
+ ou même pour un test de peformance. Utilisez plutôt un pool tiers pour de meilleures performances et une
+ meilleure stabilité : pour cela, remplacez la propriété <literal>hibernate.connection.pool_size</literal> avec les propriétés
+ spécifique au pool de connexions que vous avez choisi. Cela désactivera le pool de connexions interne
+ d'Hibernate. Vous pouvez par exemple utiliser C3P0.
+ </para>
+
+ <para>
+ C3P0 est un pool de connexions JDBC open source distribué avec Hibernate dans le répertoire
+ <literal>lib</literal>. Hibernate utilisera son provider <literal>C3P0ConnectionProvider</literal>
+ pour le pool de connexions si vous positionnez les propriétés <literal>hibernate.c3p0.*</literal>.
+ Si vous voulez utiliser Proxool, référez vous au groupe de propriétés d'<literal>hibernate.properties</literal>
+ correspondant et regardez sur le site web d'Hibernate pour plus d'informations.
+ </para>
+
+ <para>
+ Voici un exemple de fichier <literal>hibernate.properties</literal> pour C3P0:
+ </para>
+
+ <programlisting id="c3p0-configuration" revision="1"><![CDATA[hibernate.connection.driver_class = org.postgresql.Driver
+hibernate.connection.url = jdbc:postgresql://localhost/mydatabase
+hibernate.connection.username = myuser
+hibernate.connection.password = secret
+hibernate.c3p0.min_size=5
+hibernate.c3p0.max_size=20
+hibernate.c3p0.timeout=1800
+hibernate.c3p0.max_statement=50
+hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect]]></programlisting>
+
+ <para>
+ Dans le cadre de l'utilisation au sein d'un serveur d'applications,
+ vous devriez quasiment toujours configurer Hibernate pour qu'il obtienne
+ ses connexions de la <literal>DataSource</literal> du serveur d'application
+ enregistrée dans le JNDI. Pour cela vous devrez définir au moins une des
+ propriétés suivantes :
+ </para>
+
+ <table frame="topbot">
+ <title>Propriété d'une Datasource Hibernate</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Nom d'une propriété</entry>
+ <entry>fonction</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.connection.datasource</literal>
+ </entry>
+ <entry>
+ <emphasis>Nom JNDI de la datasource</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jndi.url</literal>
+ </entry>
+ <entry>
+ <emphasis>URL du fournisseur JNDI</emphasis> (optionnelle)
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jndi.class</literal>
+ </entry>
+ <entry>
+ <emphasis>Classe de l'<literal>InitialContextFactory</literal> du JNDI</emphasis> (optionnelle)
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.username</literal>
+ </entry>
+ <entry>
+ <emphasis>utilisateur de la base de données</emphasis> (optionnelle)
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.password</literal>
+ </entry>
+ <entry>
+ <emphasis>mot de passe de la base de données</emphasis> (optionnelle)
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ Voici un exemple de fichier <literal>hibernate.properties</literal>
+ pour l'utilisation d'une datasource JNDI fournie par un serveur d'applications :
+ </para>
+
+ <programlisting><![CDATA[hibernate.connection.datasource = java:/comp/env/jdbc/test
+hibernate.transaction.factory_class = \
+ org.hibernate.transaction.JTATransactionFactory
+hibernate.transaction.manager_lookup_class = \
+ org.hibernate.transaction.JBossTransactionManagerLookup
+hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect]]></programlisting>
+
+ <para>
+ Les connexions JDBC obtenues à partir d'une datasource JNDI participeront automatiquement
+ aux transactions gérées par le conteneur du serveur d'applications.
+ </para>
+
+ <para>
+ Des propriétés supplémentaires de connexion peuvent être passées en préfixant
+ le nom de la propriété par "<literal>hibernate.connnection</literal>". Par exemple,
+ vous pouvez spécifier un jeu de caractères en utilisant
+ <literal>hibernate.connection.charSet</literal>.
+ </para>
+
+ <para>
+ Vous pouvez fournir votre propre stratégie d'obtention des connexions JDBC en implémentant l'interface
+ <literal>org.hibernate.connection.ConnectionProvider</literal>. Vous pouvez sélectionner
+ une implémentation spécifique en positionnant <literal>hibernate.connection.provider_class</literal>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="configuration-optional" revision="1">
+ <title>Propriétés de configuration optionnelles</title>
+
+ <para>
+ Il y a un certain nombre d'autres propriétés qui contrôlent le fonctionnement
+ d'Hibernate à l'exécution. Toutes sont optionnelles et ont comme valeurs par défaut
+ des valeurs "raisonnables" pour un fonctionnement nominal.
+ </para>
+
+ <para>
+ <emphasis>Attention : Certaines de ces propriétés sont uniquement de niveau System.</emphasis>
+ Les propriétés de niveau System ne peuvent être positionnées que via la ligne de commande
+ (<literal>java -Dproperty=value</literal>) ou être définies dans <literal>hibernate.properties</literal>.
+ Elle <emphasis>ne peuvent pas</emphasis> l'être via une des autres techniques décrites ci-dessus.
+ </para>
+
+ <table frame="topbot" id="configuration-optional-properties" revision="8">
+ <title>Propriétés de configuration d'Hibernate</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Nom de la propriété</entry>
+ <entry>Fonction</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.dialect</literal>
+ </entry>
+ <entry>
+ Le nom de la classe du <literal>Dialect</literal> Hibernate.
+ qui permet à Hibernate de générer du SQL optimisé pour une
+ base de données relationnelle particulière.
+ <para>
+ <emphasis role="strong">ex.</emphasis>
+ <literal>nom.complet.de.ma.classe.de.Dialect</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.show_sql</literal>
+ </entry>
+ <entry>
+ Ecrit toutes les requêtes SQL sur la console. Il s'agit d'une
+ alternative au positionnement de la catégorie de log
+ <literal>org.hibernate.SQL</literal> au niveau <literal>debug</literal>.
+ <para>
+ <emphasis role="strong">ex.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.format_sql</literal>
+ </entry>
+ <entry>
+ Formate et indente le sql dans la console et dans le log
+ <para>
+ <emphasis role="strong">ex.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.default_schema</literal>
+ </entry>
+ <entry>
+ Positionne dans le SQL généré un schéma/tablespace par défaut pour les noms de
+ table ne l'ayant pas surchargé.
+ <para>
+ <emphasis role="strong">ex.</emphasis>
+ <literal>MON_SCHEMA</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.default_catalog</literal>
+ </entry>
+ <entry>
+ Qualifie les noms de tables non qualifiées avec ce catalogue
+ dans le SQL généré.
+ <para>
+ <emphasis role="strong">ex.</emphasis>
+ <literal>CATALOG_NAME</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.session_factory_name</literal>
+ </entry>
+ <entry>
+ La <literal>SessionFactory</literal> sera automatiquement
+ liée à ce nom dans le JNDI après sa création.
+ <para>
+ <emphasis role="strong">ex.</emphasis>
+ <literal>jndi/nom/hierarchique</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.max_fetch_depth</literal>
+ </entry>
+ <entry>
+ Définit la profondeur maximale d'un arbre de chargement par
+ jointures ouvertes pour les associations à cardinalité unitaire
+ (un-à-un, plusieurs-à-un).
+ Un <literal>0</literal> désactive le chargement par jointure
+ ouverte.
+ <para>
+ <emphasis role="strong">ex.</emphasis>
+ valeurs recommandées entre <literal>0</literal> et <literal>3</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.default_batch_fetch_size</literal>
+ </entry>
+ <entry>
+ Définit une taille par défaut pour le chargement par lot des associations
+ <para>
+ <emphasis role="strong">ex.</emphasis>
+ Valeurs recommandées : <literal>4</literal>, <literal>8</literal>,
+ <literal>16</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.default_entity_mode</literal>
+ </entry>
+ <entry>
+ Définit un mode de représentation par défaut des entités pour
+ toutes les sessions ouvertes depuis cette <literal>SessionFactory</literal>
+ <para>
+ <literal>dynamic-map</literal>, <literal>dom4j</literal>,
+ <literal>pojo</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.order_updates</literal>
+ </entry>
+ <entry>
+ Force Hibernate à trier les updates SQL par la valeur de la clé
+ primaire des éléments qui sont mis à jour. Cela permet de limiter
+ les deadlocks de transaction dans les systèmes hautement concurents.
+ <para>
+ <emphasis role="strong">ex.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.generate_statistics</literal>
+ </entry>
+ <entry>
+ Si activé, Hibernate va collecter des statistiques utiles
+ pour le réglage des performances.
+ <para>
+ <emphasis role="strong">ex.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.use_identifer_rollback</literal>
+ </entry>
+ <entry>
+ Si activé, les propriétés correspondant à l'identifiant
+ des objets vont être remises aux valeurs par défaut lorsque
+ les objets seront supprimés.
+ <para>
+ <emphasis role="strong">ex.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.use_sql_comments</literal>
+ </entry>
+ <entry>
+ Si activé, Hibernate va générer des commentaires à l'intérieur
+ des requêtes SQL pour faciliter le debogage., par défaut à <literal>false</literal>.
+ <para>
+ <emphasis role="strong">ex.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame="topbot" id="configuration-jdbc-properties" revision="8">
+ <title>Propriétés Hibernate liées à JDBC et aux connexions</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Nom de la propriété</entry>
+ <entry>Fonction</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.fetch_size</literal>
+ </entry>
+ <entry>
+ Une valeur non nulle détermine la taille de chargement
+ des statements JDBC (appelle
+ <literal>Statement.setFetchSize()</literal>).
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.batch_size</literal>
+ </entry>
+ <entry>
+ Une valeur non nulle active l'utilisation par Hibernate des mises
+ à jour par batch de JDBC2.
+ <para>
+ <emphasis role="strong">ex.</emphasis>
+ les valeurs recommandées entre <literal>5</literal> et <literal>30</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.batch_versioned_data</literal>
+ </entry>
+ <entry>
+ Paramétrez cette propriété à <literal>true</literal> si votre pilote JDBC
+ retourne des row counts corrects depuis <literal>executeBatch()</literal> (il est
+ souvent approprié d'activer cette option). Hibernate utilisera alors le "batched DML" pour
+ versionner automatiquement les données. Par défaut = <literal>false</literal>.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.factory_class</literal>
+ </entry>
+ <entry>
+ Sélectionne un <literal>Batcher</literal> personnalisé. La
+ plupart des applications n'auront pas besoin de cette propriété
+ de configuration
+ <para>
+ <emphasis role="strong">ex.</emphasis>
+ <literal>classname.of.Batcher</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.use_scrollable_resultset</literal>
+ </entry>
+ <entry>
+ Active l'utilisation par Hibernate des resultsets scrollables
+ de JDBC2. Cette propriété est seulement nécessaire lorsque l'on
+ utilise une connexion JDBC fournie par l'utilisateur. Autrement,
+ Hibernate utilise les métadonnées de la connexion.
+ <para>
+ <emphasis role="strong">ex.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.use_streams_for_binary</literal>
+ </entry>
+ <entry>
+ Utilise des flux lorsque l'on écrit/lit des types
+ <literal>binary</literal> ou <literal>serializable</literal>
+ vers et à partir de JDBC (propriété de niveau système).
+ <para>
+ <emphasis role="strong">ex.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.use_get_generated_keys</literal>
+ </entry>
+ <entry>
+ Active l'utilisation de <literal>PreparedStatement.getGeneratedKeys()</literal> de JDBC3
+ pour récupérer nativement les clés générées après insertion. Nécessite un pilote
+ JDBC3+, le mettre à false si votre pilote a des problèmes avec les générateurs
+ d'identifiant Hibernate. Par défaut, essaie de déterminer les possibilités du
+ pilote en utilisant les meta données de connexion.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.provider_class</literal>
+ </entry>
+ <entry>
+ Le nom de la classe d'un <literal>ConnectionProvider</literal> personnalisé
+ qui fournit des connexions JDBC à Hibernate
+ <para>
+ <emphasis role="strong">ex.</emphasis>
+ <literal>classname.of.ConnectionProvider</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.isolation</literal>
+ </entry>
+ <entry>
+ Définit le niveau d'isolation des transactions JDBC. Regardez
+ <literal>java.sql.Connection</literal> pour connaître le
+ sens des différentes valeurs mais notez également que la plupart
+ des bases de données ne supportent pas tous les niveaux d'isolation.
+ <para>
+ <emphasis role="strong">ex.</emphasis>
+ <literal>1, 2, 4, 8</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.autocommit</literal>
+ </entry>
+ <entry>
+ Active le mode de commit automatique (autocommit) pour les connexions
+ JDBC du pool (non recommandé).
+ <para>
+ <emphasis role="strong">ex.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.release_mode</literal>
+ </entry>
+ <entry>
+ Spécifie à quel moment Hibernate doit relacher les connexion JDBC.
+ Par défaut une connexion JDBC est conservée jusqu'à ce que la session
+ soit explicitement fermée ou déconnectée. Pour une source de données
+ JTA d'un serveur d'application, vous devriez utiliser <literal>after_statement</literal>
+ pour libérer les connexions de manière plus agressive après chaque appel
+ JDBC. Pour une connexion non JTA, il est souvent préférable de libérer
+ la connexion à la fin de chaque transaction en utilisant <literal>after_transaction</literal>.
+ <literal>auto</literal> choisira <literal>after_statement</literal> pour
+ des transactions JTA et CMT et <literal>after_transaction</literal> pour
+ des transactions JDBC.
+ <para>
+ <emphasis role="strong">ex.</emphasis>
+ <literal>on_close</literal> (default) | <literal>after_transaction</literal> |
+ <literal>after_statement</literal> | <literal>auto</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.<emphasis><propertyName></emphasis></literal>
+ </entry>
+ <entry>
+ Passe la propriété JDBC<literal>propertyName</literal>
+ à <literal>DriverManager.getConnection()</literal>.
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jndi.<emphasis><propertyName></emphasis></literal>
+ </entry>
+ <entry>
+ Passe la propriété <literal>propertyName</literal> à l'<literal>InitialContextFactory</literal>
+ de JNDI.
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame="topbot" id="configuration-cache-properties" revision="7">
+ <title>Propriétés du Cache d'Hibernate</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Nom de la propriété</entry>
+ <entry>Fonction</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.cache.provider_class</literal>
+ </entry>
+ <entry>
+ Le nom de classe d'un <literal>CacheProvider</literal>
+ spécifique.
+ <para>
+ <emphasis role="strong">ex.</emphasis>
+ <literal>nom.de.classe.du.CacheProvider</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.use_minimal_puts</literal>
+ </entry>
+ <entry>
+ Optimise le cache de second niveau en minimisant les écritures,
+ au prix de plus de lectures. Ce paramètre est surtout utile pour
+ les caches en cluster et est activé par défaut dans hibernate3
+ pour les implémentations de cache en cluster.
+ <para>
+ <emphasis role="strong">ex.</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.use_query_cache</literal>
+ </entry>
+ <entry>
+ Activer le cache de requête, les requêtes individuelles doivent tout
+ de même être déclarées comme pouvant être mise en cache.
+ <para>
+ <emphasis role="strong">ex.</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.use_second_level_cache</literal>
+ </entry>
+ <entry>
+ Peut être utilisé pour désactiver complètement le cache de second niveau
+ qui est activé par défaut pour les classes qui spécifient un élément
+ <literal><cache></literal> dans leur mapping.
+ <para>
+ <emphasis role="strong">ex.</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.query_cache_factory</literal>
+ </entry>
+ <entry>
+ Le nom de classe d'une interface <literal>QueryCacheFactory</literal> ,
+ par défaut = built-in <literal>StandardQueryCacheFactory</literal>.
+ <para>
+ <emphasis role="strong">ex.</emphasis>
+ <literal>nom.de.la.classe.de.QueryCacheFactory</literal>
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <literal>hibernate.cache.region_prefix</literal>
+ </entry>
+ <entry>
+ Un préfixe à utiliser pour le nom des régions du
+ cache de second niveau.
+ <para>
+ <emphasis role="strong">ex.</emphasis>
+ <literal>prefix</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.use_structured_entries</literal>
+ </entry>
+ <entry>
+ Force Hibernate à stocker les données dans le cache de
+ second niveau dans un format plus adapté à la visualisation
+ par un humain.
+ <para>
+ <emphasis role="strong">ex.</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame="topbot" id="configuration-transaction-properties" revision="9">
+ <title>Propriétés des transactions Hibernate</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Nom de la propriété</entry>
+ <entry>Fonction</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.transaction.factory_class</literal>
+ </entry>
+ <entry>
+ Le nom de classe d'une <literal>TransactionFactory</literal>
+ qui sera utilisée par l'API <literal>Transaction</literal>
+ d'Hibernate (la valeur par défaut est
+ <literal>JDBCTransactionFactory</literal>).
+ <para>
+ <emphasis role="strong">ex.</emphasis>
+ <literal>nom.de.classe.d.une.TransactionFactory</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>jta.UserTransaction</literal>
+ </entry>
+ <entry>
+ Le nom JNDI utilisé par la <literal>JTATransactionFactory</literal>
+ pour obtenir la <literal>UserTransaction</literal> JTA du serveur
+ d'applications.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>jndi/nom/compose</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.transaction.manager_lookup_class</literal>
+ </entry>
+ <entry>
+ Le nom de la classe du <literal>TransactionManagerLookup</literal>
+ - requis lorsque le cache de niveau JVM est activé ou lorsque l'on
+ utilise un générateur hilo dans un environnement JTA.
+ <para>
+ <emphasis role="strong">ex.</emphasis>
+ <literal>nom.de.classe.du.TransactionManagerLookup</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.transaction.flush_before_completion</literal>
+ </entry>
+ <entry>
+ Si activé, la session sera automatiquement vidée durant la phase
+ qui précède la fin de la transaction (before completion).
+ La gestion automatique de contexte fourni par Hibernate est
+ recommandée, voir
+ <xref linkend="architecture-current-session"/>.
+ <para>
+ <emphasis role="strong">ex.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.transaction.auto_close_session</literal>
+ </entry>
+ <entry>
+ Si activé, la session sera automatiquement fermé pendant la phase
+ qui suit la fin de la transaction (after completion).
+ La gestion automatique de contexte fourni par Hibernate est
+ recommandée, voir
+ <para>
+ <emphasis role="strong">ex.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame="topbot" id="configuration-misc-properties" revision="9">
+ <title>Propriétés diverses</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Nom de la propriété</entry>
+ <entry>Fonction</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.current_session_context_class</literal>
+ </entry>
+ <entry>
+ Fournit une stratégie particulière pour contextualiser
+ la <literal>Session</literal> courante. Voir
+ <xref linkend="architecture-current-session"/> pour plus
+ d'informations sur les stratégies fournies.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>jta</literal> | <literal>thread</literal> |
+ <literal>custom.Class</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.query.factory_class</literal>
+ </entry>
+ <entry>
+ Choisi l'implémentation du parseur de requête
+ <para>
+ <emphasis role="strong">ex.</emphasis>
+ <literal>org.hibernate.hql.ast.ASTQueryTranslatorFactory</literal> ou
+ <literal>org.hibernate.hql.classic.ClassicQueryTranslatorFactory</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.query.substitutions</literal>
+ </entry>
+ <entry>
+ Lien entre les tokens de requêtes Hibernate et les
+ tokens SQL (les tokens peuvent être des fonctions ou des
+ noms littéraux par exemple).
+ <para>
+ <emphasis role="strong">ex.</emphasis>
+ <literal>hqlLiteral=SQL_LITERAL, hqlFunction=SQLFUNC</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.hbm2ddl.auto</literal>
+ </entry>
+ <entry>
+ Valide ou exporte automatiquement le schéma DDL vers la base de données
+ lorsque la <literal>SessionFactory</literal> est créée.
+ La valeur <literal>create-drop</literal> permet de supprimer
+ le schéma de base de données lorsque la <literal>SessionFactory</literal>
+ est fermée explicitement.
+ <para>
+ <emphasis role="strong">ex.</emphasis>
+ <literal>validate</literal> | <literal>update</literal> |
+ <literal>create</literal> | <literal>create-drop</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cglib.use_reflection_optimizer</literal>
+ </entry>
+ <entry>
+ Active l'utilisation de CGLIB à la place de la réflexion à l'exécution
+ (Propriété de niveau système). La réflexion peut parfois être utile pour
+ résoudre des problèmes. Notez qu'Hibernate a tout de même toujours besoin
+ de CGLIB même si l'optimiseur est désactivé. Cette optimisation ne peut être
+ définie que dans le fichier <literal>hibernate.cfg.xml</literal>.
+ <para>
+ <emphasis role="strong">ex.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <sect2 id="configuration-optional-dialects" revision="1">
+ <title>Dialectes SQL</title>
+
+ <para>
+ Vous devriez toujours positionner la propriété <literal>hibernate.dialect</literal> à
+ la sous-classe de <literal>org.hibernate.dialect.Dialect</literal> appropriée à
+ votre base de données. Si vous spécifiez un dialecte,
+ Hibernate utilisera des valeurs adaptées pour certaines autres
+ propriétés listées ci-dessus, vous évitant l'effort de le faire à la main.
+ </para>
+
+ <table frame="topbot" id="sql-dialects" revision="2">
+ <title>Dialectes SQL d'Hibernate (<literal>hibernate.dialect</literal>)</title>
+ <tgroup cols="2">
+ <colspec colwidth="1*"/>
+ <colspec colwidth="2.5*"/>
+ <thead>
+ <row>
+ <entry>SGBD</entry>
+ <entry>Dialecte</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>DB2</entry> <entry><literal>org.hibernate.dialect.DB2Dialect</literal></entry>
+ </row>
+ <row>
+ <entry>DB2 AS/400</entry> <entry><literal>org.hibernate.dialect.DB2400Dialect</literal></entry>
+ </row>
+ <row>
+ <entry>DB2 OS390</entry> <entry><literal>org.hibernate.dialect.DB2390Dialect</literal></entry>
+ </row>
+ <row>
+ <entry>PostgreSQL</entry> <entry><literal>org.hibernate.dialect.PostgreSQLDialect</literal></entry>
+ </row>
+ <row>
+ <entry>MySQL</entry> <entry><literal>org.hibernate.dialect.MySQLDialect</literal></entry>
+ </row>
+ <row>
+ <entry>MySQL with InnoDB</entry> <entry><literal>org.hibernate.dialect.MySQLInnoDBDialect</literal></entry>
+ </row>
+ <row>
+ <entry>MySQL with MyISAM</entry> <entry><literal>org.hibernate.dialect.MySQLMyISAMDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Oracle (any version)</entry> <entry><literal>org.hibernate.dialect.OracleDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Oracle 9i/10g</entry> <entry><literal>org.hibernate.dialect.Oracle9Dialect</literal></entry>
+ </row>
+ <row>
+ <entry>Sybase</entry> <entry><literal>org.hibernate.dialect.SybaseDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Sybase Anywhere</entry> <entry><literal>org.hibernate.dialect.SybaseAnywhereDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Microsoft SQL Server</entry> <entry><literal>org.hibernate.dialect.SQLServerDialect</literal></entry>
+ </row>
+ <row>
+ <entry>SAP DB</entry> <entry><literal>org.hibernate.dialect.SAPDBDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Informix</entry> <entry><literal>org.hibernate.dialect.InformixDialect</literal></entry>
+ </row>
+ <row>
+ <entry>HypersonicSQL</entry> <entry><literal>org.hibernate.dialect.HSQLDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Ingres</entry> <entry><literal>org.hibernate.dialect.IngresDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Progress</entry> <entry><literal>org.hibernate.dialect.ProgressDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Mckoi SQL</entry> <entry><literal>org.hibernate.dialect.MckoiDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Interbase</entry> <entry><literal>org.hibernate.dialect.InterbaseDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Pointbase</entry> <entry><literal>org.hibernate.dialect.PointbaseDialect</literal></entry>
+ </row>
+ <row>
+ <entry>FrontBase</entry> <entry><literal>org.hibernate.dialect.FrontbaseDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Firebird</entry> <entry><literal>org.hibernate.dialect.FirebirdDialect</literal></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-outerjoin" revision="4">
+ <title>Chargement par Jointure Ouverte</title>
+
+ <para>
+ Si votre base de données supporte les outer joins de type ANSI, Oracle ou Sybase,
+ <emphasis>le chargement par jointure ouverte</emphasis> devrait améliorer les
+ performances en limitant le nombre d'aller-retour avec la base de données (la
+ base de données effectuant donc potentiellement plus de travail). Le chargement par
+ jointure ouverte permet à un graphe entier d'objets connectés par une relation plusieurs-à-un,
+ un-à-plusieurs ou un-à-un d'être chargé en un seul <literal>SELECT</literal> SQL.
+ </para>
+
+ <para>
+ Le chargement par jointure ouverte peut être désactiver <emphasis>globalement</emphasis>
+ en mettant la propriété <literal>hibernate.max_fetch_depth</literal> à <literal>0</literal>.
+ Une valeur de <literal>1</literal> ou plus active le chargement par jointure ouverte
+ pour les associatiosn un-à-un et plusieurs-à-un qui ont été mappée avec
+ <literal>fetch="join"</literal>.
+ </para>
+
+ <para>
+ Reportez vous à <xref linkend="performance-fetching"/> pour plus d'information.
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-binarystreams" revision="1">
+ <title>Flux binaires</title>
+
+ <para>
+ Oracle limite la taille d'un tableau de <literal>byte</literal> qui peuvent être
+ passées à et vers son pilote JDBC. Si vous souhaitez utiliser des instances larges
+ de type <literal>binary</literal> ou <literal>serializable</literal>, vous devez activer
+ la propriété <literal>hibernate.jdbc.use_streams_for_binary</literal>. <emphasis>C'est une
+ fonctionalité de niveau système uniquement.</emphasis>
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-cacheprovider" revision="2">
+ <title>Cache de second niveau et cache de requêtes</title>
+
+ <para>
+ Les propriétés préfixées par <literal>hibernate.cache</literal>
+ vous permettent d'utiliser un système de cache de second niveau. Ce cache
+ peut avoir une portée dans le processus ou même être utilisable dans un
+ système distribué. Référez vous au chapitre <xref linkend="performance-cache"/>
+ pour plus de détails.
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-querysubstitution">
+ <title>Substitution dans le langage de requêtage</title>
+
+ <para>
+ Vous pouvez définir de nouveaux tokens dans les requêtes Hibernate en utilisant la propriété
+ <literal>hibernate.query.substitutions</literal>. Par exemple :
+ </para>
+
+ <programlisting>hibernate.query.substitutions vrai=1, faux=0</programlisting>
+
+ <para>
+ remplacerait les tokens <literal>vrai</literal> et <literal>faux</literal> par
+ des entiers dans le SQL généré.
+ </para>
+
+ <programlisting>hibernate.query.substitutions toLowercase=LOWER</programlisting>
+
+ <para>
+ permettrait de renommer la fonction SQL <literal>LOWER</literal> en <literal>toLowercase</literal>
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-statistics" revision="2">
+ <title>Statistiques Hibernate</title>
+
+ <para>
+ Si vous activez <literal>hibernate.generate_statistics</literal>, Hibernate va
+ fournir un certains nombre de métriques utiles pour régler les performances
+ d'une application qui tourne via <literal>SessionFactory.getStatistics()</literal>.
+ Hibernate peut aussi être configuré pour exposer ces statistiques via JMX.
+ Lisez les Javadoc des interfaces dans le package
+ <literal>org.hibernate.stats</literal> pour plus d'informations.
+ </para>
+
+ </sect2>
+ </sect1>
+
+ <sect1 id="configuration-logging">
+ <title>Tracer</title>
+
+ <para>
+ Hibernate trace divers évènements en utilisant Apache commons-logging.
+ </para>
+
+ <para>
+ Le service commons-logging délèguera directement à Apache Log4j
+ (si vous incluez <literal>log4j.jar</literal> dans votre classpath)
+ ou le système de trace du JDK 1.4 (si vous tournez sous le JDK 1.4
+ et supérieur). Vous pouvez télécharger Log4j à partir de
+ <literal>http://jakarta.apache.org</literal>. Pour utiliser Log4j,
+ vous devrez placer dans votre classpath un fichier
+ <literal>log4j.properties</literal>. Un exemple de fichier est distribué
+ avec Hibernate dans le répertoire <literal>src/</literal>.
+ </para>
+
+ <para>
+ Nous vous recommandons fortement de vous familiariser avec les messages des traces
+ d'Hibernate. Beaucoup de soins a été apporté pour donner le plus de détails
+ possible sans les rendre illisibles. C'est un outil essentiel en cas de soucis.
+ Les catégories de trace les plus intéressantes sont les suivantes :
+ </para>
+
+ <table frame="topbot" id="log-categories" revision="2">
+ <title>Catégories de trace d'Hibernate</title>
+ <tgroup cols="2">
+ <colspec colwidth="1*"/>
+ <colspec colwidth="2.5*"/>
+ <thead>
+ <row>
+ <entry>Catégorie</entry>
+ <entry>Fonction</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>org.hibernate.SQL</literal></entry>
+ <entry>Trace toutes les requêts SQL de type DML (gestion des données) qui sont exécutées</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.type</literal></entry>
+ <entry>Trace tous les paramètres JDBC</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.tool.hbm2ddl</literal></entry>
+ <entry>Trace toutes les requêts SQL de type DDL (gestion de la structure de la base) qui sont exécutées</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.pretty</literal></entry>
+ <entry>
+ Trace l'état de toutes les entités (20 entités maximum) qui
+ sont associées avec la session hibernate au moment du flush
+ </entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.cache</literal></entry>
+ <entry>Trace toute l'activité du cache de second niveau</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction</literal></entry>
+ <entry>Trace toute l'activité relative aux transactions</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.jdbc</literal></entry>
+ <entry>Trace toute acquisition de ressource JDBC</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.hql.ast.AST</literal></entry>
+ <entry>
+ Trace l'arbre syntaxique des requêtes HQL et SQL durant l'analyse syntaxique des requêtes
+ </entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.secure</literal></entry>
+ <entry>Trace toutes les demandes d'autorisation JAAS</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate</literal></entry>
+ <entry>
+ Trace tout (beaucoupe d'informations, mais très utile pour résoudre les problèmes).
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ Lorsque vous développez des applications avec Hibernate, vous devriez quasiment toujours
+ travailler avec le niveau <literal>debug</literal> activé pour la catégorie
+ <literal>org.hibernate.SQL</literal>, ou sinon avec la propriété
+ <literal>hibernate.show_sql</literal> activée.
+ </para>
+
+
+ </sect1>
+
+ <sect1 id="configuration-namingstrategy">
+ <title>Implémenter une <literal>NamingStrategy</literal></title>
+
+ <para>
+ L'interface <literal>org.hibernate.cfg.NamingStrategy</literal> vous permet de
+ spécifier une "stratégie de nommage" des objets et éléments de la base de données.
+ </para>
+
+ <para>
+ Vous pouvez fournir des règles pour automatiquement générer les identifiants
+ de base de données à partir des identifiants Java, ou transformer une colonne
+ ou table "logique" donnée dans le fichier de mapping en une colonne ou table
+ "physique". Cette fonctionnalité aide à réduire la verbosité de documents
+ de mapping, en éliminant le bruit répétitif (les préfixes <literal>TBL_</literal>
+ par exemple). La stratégie par défaut utilisée par Hibernate est minimale.
+ </para>
+
+ <para>
+ Vous pouvez définir une stratégie différente en appelant
+ <literal>Configuration.setNamingStrategy()</literal> avant d'ajouter des
+ mappings :
+ </para>
+
+ <programlisting><![CDATA[SessionFactory sf = new Configuration()
+ .setNamingStrategy(ImprovedNamingStrategy.INSTANCE)
+ .addFile("Item.hbm.xml")
+ .addFile("Bid.hbm.xml")
+ .buildSessionFactory();]]></programlisting>
+
+ <para>
+ <literal>net.sf.hibernate.cfg.ImprovedNamingStrategy</literal> est une
+ stratégie fournie qui peut être utile comme point de départ de quelques
+ applications.
+ </para>
+
+ </sect1>
+
+ <sect1 id="configuration-xmlconfig" revision="2">
+ <title>Fichier de configuration XML</title>
+
+ <para>
+ Une approche alternative est de spécifier toute la configuration dans un
+ fichier nommé <literal>hibernate.cfg.xml</literal>. Ce fichier peut être
+ utilisé à la place du fichier <literal>hibernate.properties</literal>, voire
+ même peut servir à surcharger les propriétés si les deux fichiers sont présents.
+ </para>
+
+ <para>
+ Le fichier de configuration XML doit par défaut se placer à la racine
+ du <literal>CLASSPATH</literal>. En voici un exemple :
+ </para>
+
+ <programlisting><![CDATA[<?xml version='1.0' encoding='utf-8'?>
+<!DOCTYPE hibernate-configuration PUBLIC
+ "-//Hibernate/Hibernate Configuration DTD//EN"
+ "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
+
+<hibernate-configuration>
+
+ <!-- a SessionFactory instance listed as /jndi/name -->
+ <session-factory
+ name="java:hibernate/SessionFactory">
+
+ <!-- properties -->
+ <property name="connection.datasource">java:/comp/env/jdbc/MyDB</property>
+ <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
+ <property name="show_sql">false</property>
+ <property name="transaction.factory_class">
+ org.hibernate.transaction.JTATransactionFactory
+ </property>
+ <property name="jta.UserTransaction">java:comp/UserTransaction</property>
+
+ <!-- mapping files -->
+ <mapping resource="org/hibernate/auction/Item.hbm.xml"/>
+ <mapping resource="org/hibernate/auction/Bid.hbm.xml"/>
+
+ <!-- cache settings -->
+ <class-cache class="org.hibernate.auction.Item" usage="read-write"/>
+ <class-cache class="org.hibernate.auction.Bid" usage="read-only"/>
+ <collection-cache collection="org.hibernate.auction.Item.bids" usage="read-write"/>
+
+ </session-factory>
+
+</hibernate-configuration>]]></programlisting>
+
+ <para>
+ Commme vous pouvez le voir, l'avantage de cette approche est l'externalisation
+ des noms des fichiers de mapping de la configuration. Le fichier <literal>hibernate.cfg.xml</literal>
+ est également plus pratique quand on commence à régler le cache d'Hibernate. Notez
+ que vous pouvez choisir entre utiliser <literal>hibernate.properties</literal> ou
+ <literal>hibernate.cfg.xml</literal>, les deux sont équivalents, sauf en ce qui
+ concerne les bénéfices de l'utilisation de la syntaxe XML mentionnés ci-dessus.
+ </para>
+
+ <para>
+ Avec la configuration XML, démarrer Hibernate devient donc aussi simple que ceci :
+ </para>
+
+ <programlisting><![CDATA[SessionFactory sf = new Configuration().configure().buildSessionFactory();]]></programlisting>
+
+
+
+ </sect1>
+
+ <sect1 id="configuration-j2ee" revision="1">
+ <title>Intégration à un serveur d'application J2EE</title>
+
+ <para>
+ Hibernate possède les points suivants d'intégration à l'infrastructure J2EE :
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Source de données gérée par le conteneur</emphasis> : Hibernate peut
+ utiliser des connexions JDBC gérées par le conteneur et fournie par l'intermédiaire
+ de JNDI. Souvent, un <literal>TransactionManager</literal> compatible JTA
+ et un <literal>ResourceManager</literal> s'occupent de la gestion des transactions (CMT).
+ Ils sont particulièrement prévus pour pouvoir gérer des transactions distribuées
+ sur plusieurs sources de données. Vous pouvez bien sûr également définir vos
+ limites de transaction dans votre programme (BMT) ou vous pouvez sinon aussi
+ utiliser l'API optionnelle <literal>Transaction</literal> d'Hibernate qui vous garantira
+ la portabilité de votre code entre plusieurs serveurs d'application.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Association JNDI automatique</emphasis>: Hibernate peut associer sa
+ <literal>SessionFactory</literal> à JNDI après le démarrage.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Association de la Session à JTA:</emphasis> La <literal>Session</literal> Hibernate
+ peut être associée automatiquement à une transaction JTA si vous utilisez les EJBs.
+ Vous avez juste à récupérer la <literal>SessionFactory</literal> depuis JNDI et
+ à récupérer la <literal>Session</literal> courante. Hibernate s'occupe de vider et
+ fermer la <literal>Session</literal> lorsque le transaction JTA se termine. La
+ démarcation des transactions se fait de manière déclarative dans les descripteurs de déploiement.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Déploiement JMX :</emphasis>Si vous avez un serveur d'application compatible JMX
+ (JBoss AS par exemple), vous pouvez choisir de déployer Hibernate en temps que MBean géré par
+ le serveur. Cela vous évite de coder la ligne de démarrage qui permet de construire
+ la <literal>SessionFactory</literal> depuis la <literal>Configuration</literal>.
+ Le conteneur va démarrer votre <literal>HibernateService</literal>, et va idéalement
+ s'occuper des dépendances entre les services (la source de données doit être disponible
+ avant qu'Hibernate ne démarre, etc).
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ En fonction de votre environnement, vous devrez peut être mettre l'option de
+ configuration <literal>hibernate.connection.aggressive_release</literal> à vrai si
+ le serveur d'application affiche des exceptions de type "connection containment".
+ </para>
+
+ <sect2 id="configuration-optional-transactionstrategy" revision="3">
+ <title>Configuration de la stratégie transactionnelle</title>
+
+ <para>
+ L'API de la <literal>Session</literal> Hibernate est indépendante de tout système
+ de démarcation des transactions qui peut être présent dans votre architecture. Si
+ vous laissez Hibernate utiliser l'API JDBC directement via un pool de connexion, vous
+ devrez commencer et terminer vos transactions en utilisant l'API JDBC. Si votre
+ application tourne à l'intérieur d'un serveur d'application J2EE, vous voudrez peut être
+ utiliser les transactions gérées par les beans (BMT) et appeller l'API JTA et
+ <literal>UserTransaction</literal> lorsque cela est nécessaire.
+ </para>
+ <para>
+ Pour conserver votre code portable entre ces deux environnements (et d'autres éventuels)
+ nous vous recommandons d'utiliser l'API optionnelle <literal>Transaction</literal> d'Hibernate,
+ qui va encapsuler et masquer le système de transaction sous-jacent.
+ Pour cela, vous devez préciser une classe de fabrique d'instances de <literal>Transaction</literal>
+ en positionnant la propriété
+ <literal>hibernate.transaction.factory_class</literal>.
+ </para>
+
+ <para>
+ Il existe trois choix standards (fournis) :
+ </para>
+
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term><literal>net.sf.hibernate.transaction.JDBCTransactionFactory</literal></term>
+ <listitem>
+ <para>délègue aux transactions de la base de données (JDBC). Valeur par défaut.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>org.hibernate.transaction.JTATransactionFactory</literal></term>
+ <listitem>
+ <para>
+ délègue à CMT si une transaction existante est sous ce contexte (ex: méthode
+ d'un EJB session), sinon une nouvelle transaction est entamée et
+ une transaction gérée par le bean est utilisée.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>org.hibernate.transaction.CMTTransactionFactory</literal></term>
+ <listitem>
+ <para>délègue à aux transactions JTA gérées par le conteneur</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>
+ Vous pouvez également définir votre propre stratégie transactionnelle
+ (pour un service de transaction CORBA par exemple).
+ </para>
+
+ <para>
+ Certaines fonctionnalités d'Hibernate (i.e. le cache de second niveau, l'association
+ automatique des Session à JTA, etc.) nécessitent l'accès au <literal>TransactionManager</literal>
+ JTA dans un environnement "managé". Dans un serveur d'application, vous devez indiquer
+ comment Hibernate peut obtenir une référence vers le <literal>TransactionManager</literal>,
+ car J2EE ne fournit pas un seul mécanisme standard.
+ </para>
+
+ <table frame="topbot" id="jtamanagerlookup" revision="1">
+ <title>TransactionManagers JTA</title>
+ <tgroup cols="2">
+ <colspec colwidth="2.5*"/>
+ <colspec colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Fabrique de Transaction</entry>
+ <entry align="center">Serveur d'application</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>org.hibernate.transaction.JBossTransactionManagerLookup</literal></entry>
+ <entry align="center">JBoss</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.WeblogicTransactionManagerLookup</literal></entry>
+ <entry align="center">Weblogic</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.WebSphereTransactionManagerLookup</literal></entry>
+ <entry align="center">WebSphere</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.WebSphereExtendedJTATransactionLookup</literal></entry>
+ <entry align="center">WebSphere 6</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.OrionTransactionManagerLookup</literal></entry>
+ <entry align="center">Orion</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.ResinTransactionManagerLookup</literal></entry>
+ <entry align="center">Resin</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.JOTMTransactionManagerLookup</literal></entry>
+ <entry align="center">JOTM</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.JOnASTransactionManagerLookup</literal></entry>
+ <entry align="center">JOnAS</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.JRun4TransactionManagerLookup</literal></entry>
+ <entry align="center">JRun4</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.BESTransactionManagerLookup</literal></entry>
+ <entry align="center">Borland ES</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-jndi" revision="3">
+ <title><literal>SessionFactory</literal> associée au JNDI</title>
+
+ <para>
+ Une <literal>SessionFactory</literal> Hibernate associée au JNDI peut
+ simplifier l'accès à la fabrique et donc la création de nouvelles
+ <literal>Session</literal>s. Notez que cela n'est pas lié avec les <literal>Datasource</literal>
+ associées au JNDI, elles utilisent juste le même registre.
+ </para>
+
+ <para>
+ Si vous désirez associer la <literal>SessionFactory</literal> à un nom JNDI,
+ spécifiez un nom (ex. <literal>java:hibernate/SessionFactory</literal>) en
+ utilisant la propriété <literal>hibernate.session_factory_name</literal>.
+ Si cette propriété est omise, la <literal>SessionFactory</literal> ne sera pas
+ associée au JNDI (c'est particulièrement pratique dans les environnements ayant une
+ implémentation de JNDI en lecture seule, comme c'est le cas pour Tomcat).
+ </para>
+
+ <para>
+ Lorsqu'il associe la <literal>SessionFactory</literal> au JNDI, Hibernate utilisera
+ les valeurs de <literal>hibernate.jndi.url</literal>, <literal>hibernate.jndi.class</literal>
+ pour instancier un contexte d'initialisation. S'ils ne sont pas spécifiés,
+ l'<literal>InitialContext</literal> par défaut sera utilisé.
+ </para>
+
+ <para>
+ Hibernate va automatiquement placer la <literal>SessionFactory</literal> dans JNDI
+ après avoir appelé <literal>cfg.buildSessionFactory()</literal>. Cela signifie que vous
+ devez avoir cet appel dans un code de démarrage (ou dans une classe utilitaire) dans
+ votre application sauf si vous utilisez le déploiement JMX avec le service
+ <literal>HibernateService</literal> présenté plus tard dans ce document.
+ </para>
+ <para>
+ Si vous utilisez <literal>SessionFactory</literal> JNDI, un EJB ou n'importe quelle autre classe
+ peut obtenir la <literal>SessionFactory</literal> en utilisant un lookup JNDI.
+ </para>
+
+ <para>
+ Nous recommandons que vous liiez la <literal>SessionFactory</literal> à JNDI dans les
+ environnements managés et que vous utilisiez un singleton <literal>static</literal> si ce n'est pas le cas.
+ Pour isoler votre application de ces détails, nous vous recommandons aussi de masquer
+ le code de lookup actuel pour une <literal>SessionFactory</literal> dans une classe helper,
+ comme <literal>HibernateUtil.getSessionFactory()</literal>. Notez qu'une telle classe
+ est aussi un moyen efficace de démarrer Hibernate—voir chapitre 1.
+ </para>
+ </sect2>
+
+ <sect2 id="configuration-j2ee-currentsession" revision="4">
+ <title>Association automatique de la Session à JTA</title>
+
+ <para>
+ Le moyen le plus simple de gérer les <literal>Session</literal>s et transactions est
+ la gestion automatique de session "courante" offerte par Hibernate.
+ Voir détail à <xref linkend="architecture-current-session">current sessions</xref>.
+ En utilisant le contexte de session <literal>"jta"</literal> session context, s'il n'y a pas
+ de <literal>Session</literal> associée à la transaction JTA courante, une session sera
+ démarrée et associée à la transaction JTA courante la première fois que vous appelez
+ <literal>sessionFactory.getCurrentSession()</literal>. Les <literal>Session</literal>s
+ obtenue via <literal>getCurrentSession()</literal> dans une contexte <literal>"jta"</literal>
+ seront automatiquement flushées avant la validation de la transaction, fermées une fois
+ la transaction complétée, et libéreront les connexions JDBC de manière aggressive
+ après chaque statement. Ceci permet aux <literal>Session</literal>s d'être
+ gérées par le cycle de vie de la transaction JTA à la quelle est sont associées,
+ laissant le code de l'utilisateur propre de ce type de gestion. Votre code peut
+ soit utiliser JTA de manière programmatique via <literal>UserTransaction</literal>, ou (ce qui est recommandé
+ pour la portabilité du code) utiliser l'API <literal>Transaction</literal> API pour marquer
+ les limites. Si vous exécutez sous un conteneur EJB, la démarcation déclarative des transactions
+ avec CMT est recommandée.
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-j2ee-jmx" revision="1">
+ <title>Déploiement JMX</title>
+
+ <para>
+ La ligne <literal>cfg.buildSessionFactory()</literal> doit toujours être exécutée
+ quelque part pour avoir une <literal>SessionFactory</literal> dans JNDI. Vous pouvez
+ faire cela dans un bloc d'initialisation <literal>static</literal> (comme
+ celui qui se trouve dans la classe <literal>HibernateUtil</literal>) ou vous pouvez
+ déployer Hibernate en temps que <emphasis>service managé</emphasis>.
+ </para>
+
+ <para>
+ Hibernate est distribué avec <literal>org.hibernate.jmx.HibernateService</literal>
+ pour le déploiement sur un serveur d'application avec le support de JMX comme JBoss AS.
+ Le déploiement et la configuration sont spécifiques à chaque vendeur. Voici un fichier
+ <literal>jboss-service.xml</literal> d'exemple pour JBoss 4.0.x:
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<server>
+
+<mbean code="org.hibernate.jmx.HibernateService"
+ name="jboss.jca:service=HibernateFactory,name=HibernateFactory">
+
+ <!-- Required services -->
+ <depends>jboss.jca:service=RARDeployer</depends>
+ <depends>jboss.jca:service=LocalTxCM,name=HsqlDS</depends>
+
+ <!-- Bind the Hibernate service to JNDI -->
+ <attribute name="JndiName">java:/hibernate/SessionFactory</attribute>
+
+ <!-- Datasource settings -->
+ <attribute name="Datasource">java:HsqlDS</attribute>
+ <attribute name="Dialect">org.hibernate.dialect.HSQLDialect</attribute>
+
+ <!-- Transaction integration -->
+ <attribute name="TransactionStrategy">
+ org.hibernate.transaction.JTATransactionFactory</attribute>
+ <attribute name="TransactionManagerLookupStrategy">
+ org.hibernate.transaction.JBossTransactionManagerLookup</attribute>
+ <attribute name="FlushBeforeCompletionEnabled">true</attribute>
+ <attribute name="AutoCloseSessionEnabled">true</attribute>
+
+ <!-- Fetching options -->
+ <attribute name="MaximumFetchDepth">5</attribute>
+
+ <!-- Second-level caching -->
+ <attribute name="SecondLevelCacheEnabled">true</attribute>
+ <attribute name="CacheProviderClass">org.hibernate.cache.EhCacheProvider</attribute>
+ <attribute name="QueryCacheEnabled">true</attribute>
+
+ <!-- Logging -->
+ <attribute name="ShowSqlEnabled">true</attribute>
+
+ <!-- Mapping files -->
+ <attribute name="MapResources">auction/Item.hbm.xml,auction/Category.hbm.xml</attribute>
+
+</mbean>
+
+</server>]]></programlisting>
+
+ <para>
+ Ce fichier est déployé dans un répertoire <literal>META-INF</literal> et est packagé
+ dans un fichier JAR avec l'extension <literal>.sar</literal> (service archive).
+ Vous devez également packager Hibernate, les librairies tierces requises, vos classes
+ persistantes compilées et vos fichiers de mapping dans la même archive. Vos beans
+ entreprise (souvent des EJBs session) peuvent rester dans leur propre fichier JAR mais
+ vous pouvez inclure ce fichier JAR dans le jar principal du service pour avoir une seule unité
+ déployable à chaud. Vous pouvez consulter la documentation de JBoss AS pour plus d'information
+ sur les services JMX et le déploiement des EJBs.
+ </para>
+
+ </sect2>
+
+
+ </sect1>
+
+
+
+
+</chapter>
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/events.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/events.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/events.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,263 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<chapter id="events">
+ <title>Les intercepteurs et les événements</title>
+
+ <para>
+ Il est souvent utile pour l'application de réagir à certains événements
+ qui surviennent dans Hibernate. Cela autorise l'implémentation de certaines sortes de
+ fonctionnalités génériques, et d'extensions de fonctionnalités d'Hibernate.
+ </para>
+
+ <sect1 id="objectstate-interceptors" revision="2">
+ <title>Intercepteurs</title>
+
+ <para>
+ L'interface <literal>Interceptor</literal> fournit des "callbacks" de la session vers l'application
+ et permettent à l'application de consulter et/ou de manipuler des propriétés
+ d'un objet persistant avant qu'il soit sauvegardé, mis à jour, supprimé ou chargé.
+ Une utilisation possible de cette fonctionnalité est de tracer l'accès à l'information.
+ Par exemple, l'<literal>Interceptor</literal> suivant positionne
+ <literal>createTimestamp</literal> quand un <literal>Auditable</literal> est créé
+ et met à jour la propriété <literal>lastUpdateTimestamp</literal> quand un
+ <literal>Auditable</literal> est mis à jour.
+ </para>
+
+ <para>
+ Vous pouvez soit implémenter <literal>Interceptor</literal> directement ou (mieux)
+ étendre <literal>EmptyInterceptor</literal>.
+ </para>
+
+ <programlisting><![CDATA[package org.hibernate.test;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.Iterator;
+
+import org.hibernate.EmptyInterceptor;
+import org.hibernate.Transaction;
+import org.hibernate.type.Type;
+
+public class AuditInterceptor extends EmptyInterceptor {
+
+ private int updates;
+ private int creates;
+ private int loads;
+
+ public void onDelete(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+ // ne fait rien
+ }
+
+ public boolean onFlushDirty(Object entity,
+ Serializable id,
+ Object[] currentState,
+ Object[] previousState,
+ String[] propertyNames,
+ Type[] types) {
+
+ if ( entity instanceof Auditable ) {
+ updates++;
+ for ( int i=0; i < propertyNames.length; i++ ) {
+ if ( "lastUpdateTimestamp".equals( propertyNames[i] ) ) {
+ currentState[i] = new Date();
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public boolean onLoad(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+ if ( entity instanceof Auditable ) {
+ loads++;
+ }
+ return false;
+ }
+
+ public boolean onSave(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+
+ if ( entity instanceof Auditable ) {
+ creates++;
+ for ( int i=0; i<propertyNames.length; i++ ) {
+ if ( "createTimestamp".equals( propertyNames[i] ) ) {
+ state[i] = new Date();
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public void postFlush(Iterator entities) {
+ System.out.println("Creations: " + creates + ", Updates: " + updates);
+ }
+
+ public void afterTransactionCompletion(Transaction tx) {
+ if ( tx.wasCommitted() ) {
+ System.out.println("Creations: " + creates + ", Updates: " + updates, "Loads: " + loads);
+ }
+ updates=0;
+ creates=0;
+ loads=0;
+ }
+
+}]]></programlisting>
+
+ <para>
+ L'intercepteur doit être spécifié quand une session est créée.
+ </para>
+
+ <programlisting><![CDATA[Session session = sf.openSession( new AuditInterceptor() );]]></programlisting>
+
+ <para>
+ Vous pouvez aussi mettre un intercepteur au niveau global, en utilisant l'objet <literal>Configuration</literal>.
+ Dans ce cas, l'intercepteur doit être "threadsafe".
+ </para>
+
+ <programlisting><![CDATA[new Configuration().setInterceptor( new AuditInterceptor() );]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="objectstate-events" revision="3">
+ <title>Système d'événements</title>
+
+ <para>
+ Si vous devez réagir à des événements particuliers dans votre couche de persistance,
+ vous pouvez aussi utiliser l'architecture d'<emphasis>événements</emphasis> d'Hibernate3.
+ Le système d'événements peut être utilisé en supplément ou en remplacement des interceptors.
+ </para>
+
+ <para>
+ Essentiellement toutes les méthodes de l'interface <literal>Session</literal> sont corrélées à
+ un événement. Vous avez un <literal>LoadEvent</literal>, un <literal>FlushEvent</literal>, etc
+ (consultez la DTD du fichier de configuration XML ou le paquet <literal>org.hibernate.event</literal>
+ pour avoir la liste complète des types d'événement définis).
+ Quand une requête est faite à partir d'une de ces méthodes, la
+ <literal>Session</literal> Hibernate génère un événement approprié et le passe
+ au listener configuré pour ce type.
+ Par défaut, ces listeners implémentent le même traitement dans lequel ces méthodes
+ aboutissent toujours.
+ Cependant, vous êtes libre d'implémenter une version personnalisée d'une de ces
+ interfaces de listener (c'est-à-dire, le <literal>LoadEvent</literal> est traité par
+ l'implémentation de l'interface <literal>LoadEventListener</literal> déclarée), dans
+ quel cas leur implémentation devrait être responsable du traitement des
+ requêtes <literal>load()</literal> faites par la <literal>Session</literal>.
+ </para>
+
+ <para>
+ Les listeners devraient effectivement être considérés comme des singletons ; dans le sens
+ où ils sont partagés entre des requêtes, et donc ne devraient pas sauvegarder des états
+ de variables d'instance.
+ </para>
+
+ <para>
+ Un listener personnalisé devrait implémenter l'interface appropriée pour l'événement
+ qu'il veut traiter et/ou étendre une des classes de base (ou même l'événement prêt à
+ l'emploi utilisé par Hibernate comme ceux déclarés non-finaux à cette intention). Les
+ listeners personnalisés peuvent être soit inscrits par programmation à travers l'objet
+ <literal>Configuration</literal>, ou spécifiés la configuration XML d'Hibernate
+ (la configuration déclarative à travers le fichier de propriétés n'est pas supportée).
+ Voici un exemple de listener personnalisé pour l'événement de chargement :
+ </para>
+
+ <programlisting><![CDATA[public class MyLoadListener implements LoadEventListener {
+ // C'est une simple méthode définie par l'interface LoadEventListener
+ public void onLoad(LoadEvent event, LoadEventListener.LoadType loadType)
+ throws HibernateException {
+ if ( !MySecurity.isAuthorized( event.getEntityClassName(), event.getEntityId() ) ) {
+ throw MySecurityException("Unauthorized access");
+ }
+ }
+}]]></programlisting>
+
+ <para>
+ Vous avez aussi besoin d'une entrée de configuration disant à Hibernate d'utiliser
+ ce listener en plus du listener par défaut :
+ </para>
+
+<programlisting><![CDATA[<hibernate-configuration>
+ <session-factory>
+ ...
+ <event type="load">
+ <listener class="com.eg.MyLoadListener"/>
+ <listener class="org.hibernate.event.def.DefaultLoadEventListener"/>
+ </event>
+ </session-factory>
+</hibernate-configuration>]]></programlisting>
+
+ <para>
+ Vous pouvez aussi l'inscrire par programmation :
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = new Configuration();
+LoadEventListener[] stack = { new MyLoadListener(), new DefaultLoadEventListener() };
+cfg.EventListeners().setLoadEventListeners(stack);]]></programlisting>
+
+ <para>
+ Les listeners inscrits déclarativement ne peuvent pas partager d'instances. Si le même
+ nom de classe est utilisée dans plusieurs éléments <literal><listener/></literal>,
+ chaque référence sera une instance distincte de cette classe. Si vous avez besoin de la
+ faculté de partager des instances de listener entre plusieurs types de listener, vous devez
+ utiliser l'approche d'inscription par programmation.
+ </para>
+
+ <para>
+ Pourquoi implémenter une interface et définir le type spécifique durant la configuration ?
+ Une implémentation de listener pourrait implémenter plusieurs interfaces de listener
+ d'événements. Avoir en plus le type défini durant l'inscription rend plus facile
+ l'activation ou la désactivation pendant la configuration.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-decl-security" revision="2">
+ <title>Sécurité déclarative d'Hibernate</title>
+ <para>
+ Généralement, la sécurité déclarative dans les applications Hibernate est gérée dans la
+ couche de session. Maintenant, Hibernate3 permet à certaines actions d'être approuvées
+ via JACC, et autorisées via JAAS. Cette fonctionnalité optionnelle est construite
+ au dessus de l'architecture d'événements.
+ </para>
+
+ <para>
+ D'abord, vous devez configurer les listeners d'événements appropriés pour permettre
+ l'utilisation d'autorisations JAAS.
+ </para>
+
+ <programlisting><![CDATA[<listener type="pre-delete" class="org.hibernate.secure.JACCPreDeleteEventListener"/>
+<listener type="pre-update" class="org.hibernate.secure.JACCPreUpdateEventListener"/>
+<listener type="pre-insert" class="org.hibernate.secure.JACCPreInsertEventListener"/>
+<listener type="pre-load" class="org.hibernate.secure.JACCPreLoadEventListener"/>]]></programlisting>
+
+ <para>
+ Notez que <literal><listener type="..." class="..."/></literal> est juste un raccourci
+ pour <literal><event type="..."><listener class="..."/></event></literal>
+ quand il y a exactement un listener pour un type d'événement particulier.
+ </para>
+
+ <para>
+ Ensuite, toujours dans <literal>hibernate.cfg.xml</literal>, lier les permissions aux rôles :
+ </para>
+
+ <programlisting><![CDATA[<grant role="admin" entity-name="User" actions="insert,update,read"/>
+<grant role="su" entity-name="User" actions="*"/>]]></programlisting>
+
+ <para>
+ Les noms de rôle sont les rôles compris par votre fournisseur JAAC.
+ </para>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/example_mappings.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/example_mappings.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/example_mappings.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,657 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<chapter id="example-mappings">
+ <title>Exemple : quelques mappings</title>
+
+ <para>
+ Ce chapitre montre quelques mappings plus complexes.
+ </para>
+
+ <sect1 id="example-mappings-emp">
+ <title>Employeur/Employé (Employer/Employee)</title>
+
+ <para>
+ Le modèle suivant de relation entre <literal>Employer</literal> et
+ <literal>Employee</literal> utilise une vraie classe entité (<literal>Employment</literal>)
+ pour représenter l'association. On a fait cela parce qu'il peut y avoir plus d'une période
+ d'emploi pour les deux mêmes parties. Des composants sont utilisés pour modéliser les
+ valeurs monétaires et les noms des employés.
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/EmployerEmployee.gif" format="GIF" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/EmployerEmployee.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ Voici un document de mapping possible :
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Employer" table="employers">
+ <id name="id">
+ <generator class="sequence">
+ <param name="sequence">employer_id_seq</param>
+ </generator>
+ </id>
+ <property name="name"/>
+ </class>
+
+ <class name="Employment" table="employment_periods">
+
+ <id name="id">
+ <generator class="sequence">
+ <param name="sequence">employment_id_seq</param>
+ </generator>
+ </id>
+ <property name="startDate" column="start_date"/>
+ <property name="endDate" column="end_date"/>
+
+ <component name="hourlyRate" class="MonetaryAmount">
+ <property name="amount">
+ <column name="hourly_rate" sql-type="NUMERIC(12, 2)"/>
+ </property>
+ <property name="currency" length="12"/>
+ </component>
+
+ <many-to-one name="employer" column="employer_id" not-null="true"/>
+ <many-to-one name="employee" column="employee_id" not-null="true"/>
+
+ </class>
+
+ <class name="Employee" table="employees">
+ <id name="id">
+ <generator class="sequence">
+ <param name="sequence">employee_id_seq</param>
+ </generator>
+ </id>
+ <property name="taxfileNumber"/>
+ <component name="name" class="Name">
+ <property name="firstName"/>
+ <property name="initial"/>
+ <property name="lastName"/>
+ </component>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Et voici le schéma des tables générées par <literal>SchemaExport</literal>.
+ </para>
+
+ <programlisting><![CDATA[create table employers (
+ id BIGINT not null,
+ name VARCHAR(255),
+ primary key (id)
+)
+
+create table employment_periods (
+ id BIGINT not null,
+ hourly_rate NUMERIC(12, 2),
+ currency VARCHAR(12),
+ employee_id BIGINT not null,
+ employer_id BIGINT not null,
+ end_date TIMESTAMP,
+ start_date TIMESTAMP,
+ primary key (id)
+)
+
+create table employees (
+ id BIGINT not null,
+ firstName VARCHAR(255),
+ initial CHAR(1),
+ lastName VARCHAR(255),
+ taxfileNumber VARCHAR(255),
+ primary key (id)
+)
+
+alter table employment_periods
+ add constraint employment_periodsFK0 foreign key (employer_id) references employers
+alter table employment_periods
+ add constraint employment_periodsFK1 foreign key (employee_id) references employees
+create sequence employee_id_seq
+create sequence employment_id_seq
+create sequence employer_id_seq]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-mappings-authorwork">
+ <title>Auteur/Travail (Author/Work)</title>
+
+ <para>
+ Soit le modèle de la relation entre <literal>Work</literal>, <literal>Author</literal>
+ et <literal>Person</literal>. Nous représentons la relation entre <literal>Work</literal>
+ et <literal>Author</literal> comme une association plusieurs-vers-plusieurs. Nous avons choisi de
+ représenter la relation entre <literal>Author</literal> et <literal>Person</literal>
+ comme une association un-vers-un. Une autre possibilité aurait été que
+ <literal>Author</literal> hérite de <literal>Person</literal>.
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/AuthorWork.gif" format="GIF" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/AuthorWork.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ Le mapping suivant représente exactement ces relations :
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Work" table="works" discriminator-value="W">
+
+ <id name="id" column="id">
+ <generator class="native"/>
+ </id>
+ <discriminator column="type" type="character"/>
+
+ <property name="title"/>
+ <set name="authors" table="author_work">
+ <key column name="work_id"/>
+ <many-to-many class="Author" column name="author_id"/>
+ </set>
+
+ <subclass name="Book" discriminator-value="B">
+ <property name="text"/>
+ </subclass>
+
+ <subclass name="Song" discriminator-value="S">
+ <property name="tempo"/>
+ <property name="genre"/>
+ </subclass>
+
+ </class>
+
+ <class name="Author" table="authors">
+
+ <id name="id" column="id">
+ <!-- The Author must have the same identifier as the Person -->
+ <generator class="assigned"/>
+ </id>
+
+ <property name="alias"/>
+ <one-to-one name="person" constrained="true"/>
+
+ <set name="works" table="author_work" inverse="true">
+ <key column="author_id"/>
+ <many-to-many class="Work" column="work_id"/>
+ </set>
+
+ </class>
+
+ <class name="Person" table="persons">
+ <id name="id" column="id">
+ <generator class="native"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Il y a quatre tables dans ce mapping. <literal>works</literal>,
+ <literal>authors</literal> et <literal>persons</literal> qui contiennent
+ respectivement les données de work, author et person.
+ <literal>author_work</literal> est une table d'association qui lie authors
+ à works. Voici le schéma de tables, généré par <literal>SchemaExport</literal>.
+ </para>
+
+ <programlisting><![CDATA[create table works (
+ id BIGINT not null generated by default as identity,
+ tempo FLOAT,
+ genre VARCHAR(255),
+ text INTEGER,
+ title VARCHAR(255),
+ type CHAR(1) not null,
+ primary key (id)
+)
+
+create table author_work (
+ author_id BIGINT not null,
+ work_id BIGINT not null,
+ primary key (work_id, author_id)
+)
+
+create table authors (
+ id BIGINT not null generated by default as identity,
+ alias VARCHAR(255),
+ primary key (id)
+)
+
+create table persons (
+ id BIGINT not null generated by default as identity,
+ name VARCHAR(255),
+ primary key (id)
+)
+
+alter table authors
+ add constraint authorsFK0 foreign key (id) references persons
+alter table author_work
+ add constraint author_workFK0 foreign key (author_id) references authors
+alter table author_work
+ add constraint author_workFK1 foreign key (work_id) references works]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-mappings-customerorderproduct">
+ <title>Client/Commande/Produit (Customer/Order/Product)</title>
+
+ <para>
+ Imaginons maintenant le modèle de relation entre <literal>Customer</literal>,
+ <literal>Order</literal>, <literal>LineItem</literal> et <literal>Product</literal>.
+ Il y a une association un-vers-plusieurs entre <literal>Customer</literal> et
+ <literal>Order</literal>, mais comment devrions nous représenter <literal>Order</literal> /
+ <literal>LineItem</literal> / <literal>Product</literal>? J'ai choisi de mapper
+ <literal>LineItem</literal> comme une classe d'association représentant l'association
+ plusieurs-vers-plusieurs entre <literal>Order</literal> et <literal>Product</literal>. Dans
+ Hibernate, on appelle cela un élément composite.
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/CustomerOrderProduct.gif" format="GIF" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/CustomerOrderProduct.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ Le document de mapping :
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Customer" table="customers">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="name"/>
+ <set name="orders" inverse="true">
+ <key column="customer_id"/>
+ <one-to-many class="Order"/>
+ </set>
+ </class>
+
+ <class name="Order" table="orders">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="date"/>
+ <many-to-one name="customer" column="customer_id"/>
+ <list name="lineItems" table="line_items">
+ <key column="order_id"/>
+ <list-index column="line_number"/>
+ <composite-element class="LineItem">
+ <property name="quantity"/>
+ <many-to-one name="product" column="product_id"/>
+ </composite-element>
+ </list>
+ </class>
+
+ <class name="Product" table="products">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="serialNumber"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ <literal>customers</literal>, <literal>orders</literal>, <literal>line_items</literal> et
+ <literal>products</literal> contiennent les données de customer, order, order line item et product.
+ <literal>line_items</literal> est aussi la table d'association liant orders à products.
+ </para>
+
+ <programlisting><![CDATA[create table customers (
+ id BIGINT not null generated by default as identity,
+ name VARCHAR(255),
+ primary key (id)
+)
+
+create table orders (
+ id BIGINT not null generated by default as identity,
+ customer_id BIGINT,
+ date TIMESTAMP,
+ primary key (id)
+)
+
+create table line_items (
+ line_number INTEGER not null,
+ order_id BIGINT not null,
+ product_id BIGINT,
+ quantity INTEGER,
+ primary key (order_id, line_number)
+)
+
+create table products (
+ id BIGINT not null generated by default as identity,
+ serialNumber VARCHAR(255),
+ primary key (id)
+)
+
+alter table orders
+ add constraint ordersFK0 foreign key (customer_id) references customers
+alter table line_items
+ add constraint line_itemsFK0 foreign key (product_id) references products
+alter table line_items
+ add constraint line_itemsFK1 foreign key (order_id) references orders]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="misc">
+ <title>Divers mappings d'exemple</title>
+
+ <para>
+ Ces exemples sont tous pris de la suite de tests d'Hibernate. Vous en trouverez beaucoup d'autres.
+ Regardez dans le dossier <literal>test</literal> de la distribution d'Hibernate.
+ </para>
+
+ <para>TODO: put words around this stuff</para>
+
+ <sect2 id="example-mappings-typed-onetone">
+ <title>"Typed" one-to-one association</title>
+<programlisting><![CDATA[<class name="Person">
+ <id name="name"/>
+ <one-to-one name="address"
+ cascade="all">
+ <formula>name</formula>
+ <formula>'HOME'</formula>
+ </one-to-one>
+ <one-to-one name="mailingAddress"
+ cascade="all">
+ <formula>name</formula>
+ <formula>'MAILING'</formula>
+ </one-to-one>
+</class>
+
+<class name="Address" batch-size="2"
+ check="addressType in ('MAILING', 'HOME', 'BUSINESS')">
+ <composite-id>
+ <key-many-to-one name="person"
+ column="personName"/>
+ <key-property name="type"
+ column="addressType"/>
+ </composite-id>
+ <property name="street" type="text"/>
+ <property name="state"/>
+ <property name="zip"/>
+</class>]]></programlisting>
+ </sect2>
+
+ <sect2 id="example-mappings-composite-key">
+ <title>Exemple de clef composée</title>
+<programlisting><![CDATA[<class name="Customer">
+
+ <id name="customerId"
+ length="10">
+ <generator class="assigned"/>
+ </id>
+
+ <property name="name" not-null="true" length="100"/>
+ <property name="address" not-null="true" length="200"/>
+
+ <list name="orders"
+ inverse="true"
+ cascade="save-update">
+ <key column="customerId"/>
+ <index column="orderNumber"/>
+ <one-to-many class="Order"/>
+ </list>
+
+</class>
+
+<class name="Order" table="CustomerOrder" lazy="true">
+ <synchronize table="LineItem"/>
+ <synchronize table="Product"/>
+
+ <composite-id name="id"
+ class="Order$Id">
+ <key-property name="customerId" length="10"/>
+ <key-property name="orderNumber"/>
+ </composite-id>
+
+ <property name="orderDate"
+ type="calendar_date"
+ not-null="true"/>
+
+ <property name="total">
+ <formula>
+ ( select sum(li.quantity*p.price)
+ from LineItem li, Product p
+ where li.productId = p.productId
+ and li.customerId = customerId
+ and li.orderNumber = orderNumber )
+ </formula>
+ </property>
+
+ <many-to-one name="customer"
+ column="customerId"
+ insert="false"
+ update="false"
+ not-null="true"/>
+
+ <bag name="lineItems"
+ fetch="join"
+ inverse="true"
+ cascade="save-update">
+ <key>
+ <column name="customerId"/>
+ <column name="orderNumber"/>
+ </key>
+ <one-to-many class="LineItem"/>
+ </bag>
+
+</class>
+
+<class name="LineItem">
+
+ <composite-id name="id"
+ class="LineItem$Id">
+ <key-property name="customerId" length="10"/>
+ <key-property name="orderNumber"/>
+ <key-property name="productId" length="10"/>
+ </composite-id>
+
+ <property name="quantity"/>
+
+ <many-to-one name="order"
+ insert="false"
+ update="false"
+ not-null="true">
+ <column name="customerId"/>
+ <column name="orderNumber"/>
+ </many-to-one>
+
+ <many-to-one name="product"
+ insert="false"
+ update="false"
+ not-null="true"
+ column="productId"/>
+
+</class>
+
+<class name="Product">
+ <synchronize table="LineItem"/>
+
+ <id name="productId"
+ length="10">
+ <generator class="assigned"/>
+ </id>
+
+ <property name="description"
+ not-null="true"
+ length="200"/>
+ <property name="price" length="3"/>
+ <property name="numberAvailable"/>
+
+ <property name="numberOrdered">
+ <formula>
+ ( select sum(li.quantity)
+ from LineItem li
+ where li.productId = productId )
+ </formula>
+ </property>
+
+</class>]]></programlisting>
+ </sect2>
+
+ <sect2 id="example-mappings-composite-key-manytomany">
+ <title>Many-to-many avec une clef composée partagée</title>
+<programlisting><![CDATA[<class name="User" table="`User`">
+ <composite-id>
+ <key-property name="name"/>
+ <key-property name="org"/>
+ </composite-id>
+ <set name="groups" table="UserGroup">
+ <key>
+ <column name="userName"/>
+ <column name="org"/>
+ </key>
+ <many-to-many class="Group">
+ <column name="groupName"/>
+ <formula>org</formula>
+ </many-to-many>
+ </set>
+</class>
+
+<class name="Group" table="`Group`">
+ <composite-id>
+ <key-property name="name"/>
+ <key-property name="org"/>
+ </composite-id>
+ <property name="description"/>
+ <set name="users" table="UserGroup" inverse="true">
+ <key>
+ <column name="groupName"/>
+ <column name="org"/>
+ </key>
+ <many-to-many class="User">
+ <column name="userName"/>
+ <formula>org</formula>
+ </many-to-many>
+ </set>
+</class>
+]]></programlisting>
+ </sect2>
+
+ <sect2 id="example-mappings-content-discrimination">
+ <title>Contenu basé sur une discrimination</title>
+<programlisting><![CDATA[<class name="Person"
+ discriminator-value="P">
+
+ <id name="id"
+ column="person_id"
+ unsaved-value="0">
+ <generator class="native"/>
+ </id>
+
+
+ <discriminator
+ type="character">
+ <formula>
+ case
+ when title is not null then 'E'
+ when salesperson is not null then 'C'
+ else 'P'
+ end
+ </formula>
+ </discriminator>
+
+ <property name="name"
+ not-null="true"
+ length="80"/>
+
+ <property name="sex"
+ not-null="true"
+ update="false"/>
+
+ <component name="address">
+ <property name="address"/>
+ <property name="zip"/>
+ <property name="country"/>
+ </component>
+
+ <subclass name="Employee"
+ discriminator-value="E">
+ <property name="title"
+ length="20"/>
+ <property name="salary"/>
+ <many-to-one name="manager"/>
+ </subclass>
+
+ <subclass name="Customer"
+ discriminator-value="C">
+ <property name="comments"/>
+ <many-to-one name="salesperson"/>
+ </subclass>
+
+</class>]]></programlisting>
+ </sect2>
+
+ <sect2 id="example-mappings-association-alternatekeys" revision="2">
+ <title>Associations sur des clefs alternées</title>
+<programlisting><![CDATA[<class name="Person">
+
+ <id name="id">
+ <generator class="hilo"/>
+ </id>
+
+ <property name="name" length="100"/>
+
+ <one-to-one name="address"
+ property-ref="person"
+ cascade="all"
+ fetch="join"/>
+
+ <set name="accounts"
+ inverse="true">
+ <key column="userId"
+ property-ref="userId"/>
+ <one-to-many class="Account"/>
+ </set>
+
+ <property name="userId" length="8"/>
+
+</class>
+
+<class name="Address">
+
+ <id name="id">
+ <generator class="hilo"/>
+ </id>
+
+ <property name="address" length="300"/>
+ <property name="zip" length="5"/>
+ <property name="country" length="25"/>
+ <many-to-one name="person" unique="true" not-null="true"/>
+
+</class>
+
+<class name="Account">
+ <id name="accountId" length="32">
+ <generator class="uuid"/>
+ </id>
+
+ <many-to-one name="user"
+ column="userId"
+ property-ref="userId"/>
+
+ <property name="type" not-null="true"/>
+
+</class>]]></programlisting>
+ </sect2>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/example_parentchild.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/example_parentchild.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/example_parentchild.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,372 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<chapter id="example-parentchild">
+ <title>Exemple : Père/Fils</title>
+
+ <para>
+ L'une des premières choses que les nouveaux utilisateurs essaient de faire avec Hibernate est de modéliser
+ une relation père/fils. Il y a deux approches différentes pour cela. Pour un certain nombre de raisons, la méthode la
+ plus courante, en particulier pour les nouveaux utilisateurs, est de modéliser les deux relations <literal>Père</literal>
+ et <literal>Fils</literal> comme des classes entités liées par une association <literal><one-to-many></literal> du
+ <literal>Père</literal> vers le <literal>Fils</literal> (l'autre approche est de déclarer le <literal>Fils</literal>
+ comme un <literal><composite-element></literal>). Il est évident que le sens de l'association un vers plusieurs
+ (dans Hibernate) est bien moins proche du sens habituel d'une relation père/fils que ne l'est celui d'un
+ élément cmposite. Nous allons vous expliquer comment utiliser une association <emphasis>un vers plusieurs bidirectionnelle
+ avec cascade</emphasis> afin de modéliser efficacement et élégamment une relation père/fils, ce n'est vraiment
+ pas difficile !
+ </para>
+
+ <sect1 id="example-parentchild-collections">
+ <title>Une note à propos des collections</title>
+
+ <para>
+ Les collections Hibernate sont considérées comme étant une partie logique
+ de l'entité dans laquelle elles sont contenues ; jamais des entités qu'elle
+ contient. C'est une distinction crutiale ! Les conséquences sont les suivantes :
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ Quand nous ajoutons / retirons un objet d'une collection, le numéro de version du
+ propriétaire de la collection est incrémenté.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Si un objet qui a été enlevé d'une collection est une instance de type valeur (ex :
+ élément composite), cet objet cessera d'être persistant et son état sera complètement effacé
+ de la base de données. Par ailleurs, ajouter une instance de type valeur dans une collection
+ aura pour conséquence que son état sera immédiatement persistant.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Si une entité est enlevée d'une collection (association un-vers-plusieurs
+ ou plusieurs-vers-plusieurs), par défaut, elle ne sera pas effacée. Ce comportement
+ est complètement logique - une modification de l'un des états internes d'une entité
+ ne doit pas causer la disparition de l'entité associée !
+ De même, l'ajout d'une entité dans une collection n'engendre pas,
+ par défaut, la persistance de cette entité.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Le comportement par défaut est donc que l'ajout d'une entité dans une collection créé
+ simplement le lien entre les deux entités, et qu'effacer une entité supprime ce lien.
+ C'est le comportement le plus approprié dans la plupart des cas. Ce comportement n'est
+ cependant pas approprié lorsque la vie du fils est liée au cycle de vie du père.
+ </para>
+
+ </sect1>
+
+ <sect1 id="example-parentchild-bidir">
+ <title>un-vers-plusieurs bidirectionnel</title>
+
+ <para>
+ Supposons que nous ayons une simple association <literal><one-to-many></literal>
+ de <literal>Parent</literal> vers <literal>Child</literal>.
+ </para>
+
+ <programlisting><![CDATA[<set name="children">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+ </set>]]></programlisting>
+
+ <para>
+ Si nous executions le code suivant
+ </para>
+
+ <programlisting><![CDATA[Parent p = .....;
+Child c = new Child();
+p.getChildren().add(c);
+session.save(c);
+session.flush();]]></programlisting>
+
+ <para>
+ Hibernate exécuterait deux ordres SQL:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>un <literal>INSERT</literal> pour créer l'enregistrement pour <literal>c</literal></para>
+ </listitem>
+ <listitem>
+ <para>
+ un <literal>UPDATE</literal> pour créer le lien de <literal>p</literal> vers
+ <literal>c</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Ceci est non seuleument inefficace, mais viole aussi toute contrainte <literal>NOT NULL</literal> sur
+ la colonne <literal>parent_id</literal>. Nous pouvons réparer la contrainte de nullité
+ en spécifiant <literal>not-null="true"</literal> dans le mapping de la collection :
+ </para>
+
+ <programlisting><![CDATA[<set name="children">
+ <key column="parent_id" not-null="true"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ Cependant ce n'est pas la solution recommandée.
+ </para>
+
+ <para>
+ La cause sous jacente à ce comportement est que le lien (la clé étrangère <literal>parent_id</literal>) de
+ <literal>p</literal> vers <literal>c</literal> n'est pas considérée comme faisant partie de l'état
+ de l'objet <literal>Child</literal> et n'est donc pas créé par l'<literal>INSERT</literal>.
+ La solution est donc que ce lien fasse partie du mapping de <literal>Child</literal>.
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="parent" column="parent_id" not-null="true"/>]]></programlisting>
+
+ <para>
+ (Nous avons aussi besoin d'ajouter la propriété <literal>parent</literal> dans la classe <literal>Child</literal>).
+ </para>
+
+ <para>
+ Maintenant que l'état du lien est géré par l'entité <literal>Child</literal>, nous spécifions à la
+ collection de ne pas mettre à jour le lien. Nous utilisons l'attribut <literal>inverse</literal>.
+ </para>
+
+ <programlisting><![CDATA[<set name="children" inverse="true">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ Le code suivant serait utilisé pour ajouter un nouveau <literal>Child</literal>
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+Child c = new Child();
+c.setParent(p);
+p.getChildren().add(c);
+session.save(c);
+session.flush();]]></programlisting>
+
+ <para>
+ Maintenant, seul un <literal>INSERT</literal> SQL est nécessaire !
+ </para>
+
+ <para>
+ Pour alléger encore un peu les choses, nous devrions créer une méthode <literal>addChild()</literal>
+ dans <literal>Parent</literal>.
+ </para>
+
+ <programlisting><![CDATA[public void addChild(Child c) {
+ c.setParent(this);
+ children.add(c);
+}]]></programlisting>
+
+ <para>
+ Le code d'ajout d'un <literal>Child</literal> serait alors
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+Child c = new Child();
+p.addChild(c);
+session.save(c);
+session.flush();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-parentchild-cascades">
+ <title>Cycle de vie en cascade</title>
+
+ <para>
+ L'appel explicite de <literal>save()</literal> est un peu fastidieux. Nous pouvons
+ simplifier cela en utilisant les cascades.
+ </para>
+
+ <programlisting><![CDATA[<set name="children" inverse="true" cascade="all">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ Simplifie le code précédent en
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+Child c = new Child();
+p.addChild(c);
+session.flush();]]></programlisting>
+
+ <para>
+ De la même manière, nous n'avons pas à itérer sur les fils lorsque nous sauvons
+ ou effacons un <literal>Parent</literal>. Le code suivant efface <literal>p</literal>
+ et tous ses fils de la base de données.
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+session.delete(p);
+session.flush();]]></programlisting>
+
+ <para>
+ Par contre, ce code
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+Child c = (Child) p.getChildren().iterator().next();
+p.getChildren().remove(c);
+c.setParent(null);
+session.flush();]]></programlisting>
+
+ <para>
+ n'effacera pas <literal>c</literal> de la base de données, il enlèvera seulement
+ le lien vers <literal>p</literal> (et causera une violation de contrainte
+ <literal>NOT NULL</literal>, dans ce cas).
+ Vous devez explicitement utiliser <literal>delete()</literal> sur <literal>Child</literal>.
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+Child c = (Child) p.getChildren().iterator().next();
+p.getChildren().remove(c);
+session.delete(c);
+session.flush();]]></programlisting>
+
+ <para>
+ Dans notre cas, un <literal>Child</literal> ne peut pas vraiment exister sans son père. Si nous
+ effacons un <literal>Child</literal> de la collection, nous voulons vraiment qu'il soit effacé.
+ Pour cela, nous devons utiliser <literal>cascade="all-delete-orphan"</literal>.
+ </para>
+
+ <programlisting><![CDATA[<set name="children" inverse="true" cascade="all-delete-orphan">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ A noter : même si le mapping de la collection spécifie <literal>inverse="true"</literal>, les cascades
+ sont toujours assurées par l'itération sur les éléments de la collection. Donc, si vous avez besoin
+ qu'un objet soit enregistré, effacé ou mis à jour par cascade, vous devez l'ajouter dans la colleciton.
+ Il ne suffit pas d'appeler explicitement <literal>setParent()</literal>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="example-parentchild-update">
+ <title>Cascades et <literal>unsaved-value</literal></title>
+
+ <para>
+ Supposons que nous ayons chargé un <literal>Parent</literal> dans une <literal>Session</literal>,
+ que nous l'ayons ensuite modifié et que voulions persiter ces modifications dans une nouvelle session
+ en appelant <literal>update()</literal>.
+ Le <literal>Parent</literal> contiendra une collection de fils et, puisque la cascade est activée,
+ Hibernate a besoin de savoir quels fils viennent d'être instanciés et quels fils proviennent de la base
+ de données. Supposons aussi que <literal>Parent</literal> et <literal>Child</literal> ont tous deux
+ des identifiants du type <literal>Long</literal>.
+ Hibernate utilisera la propriété de l'identifiant et la propriété de la version/horodatage pour déterminer quels fils sont nouveaux
+ (vous pouvez aussi utiliser la propriété version ou timestamp, voir
+ <xref linkend="manipulatingdata-updating-detached"/>).
+ <emphasis>Dans Hibernate3, il n'est plus nécessaire de spécifier
+ une <literal>unsaved-value</literal> explicitement.</emphasis>
+ </para>
+
+ <para>
+ Le code suivant mettra à jour <literal>parent</literal> et <literal>child</literal>
+ et insérera <literal>newChild</literal>.
+ </para>
+
+ <programlisting><![CDATA[//parent et child ont été chargés dans une session précédente
+parent.addChild(child);
+Child newChild = new Child();
+parent.addChild(newChild);
+session.update(parent);
+session.flush();]]></programlisting>
+
+ <para>
+ Ceci est très bien pour des identifiants générés, mais qu'en est-il des identifiants assignés et des
+ identifiants composés ? C'est plus difficile,
+ puisqu'Hibernate ne peut pas utiliser la propriété de l'identifiant pour distinguer un objet
+ nouvellement instancié (avec un identifiant assigné par l'utilisateur) d'un objet chargé dans une session précédente.
+ Dans ce cas, Hibernate utilisera soit la propriété de version ou d'horodatage, soit effectuera vraiment une requête au cache
+ de second niveau, soit, dans le pire des cas, à la base de données, pour voir si la ligne existe.
+ </para>
+
+ <!-- undocumenting
+ <para>
+ There is one further possibility. The <literal>Interceptor</literal> method named
+ <literal>isUnsaved()</literal> lets the application implement its own strategy for distinguishing
+ newly instantiated objects. For example, you could define a base class for your persistent classes.
+ </para>
+
+ <programlisting><![CDATA[public class Persistent {
+ private boolean _saved = false;
+ public void onSave() {
+ _saved=true;
+ }
+ public void onLoad() {
+ _saved=true;
+ }
+ ......
+ public boolean isSaved() {
+ return _saved;
+ }
+}]]></programlisting>
+
+ <para>
+ (The <literal>saved</literal> property is non-persistent.)
+ Now implement <literal>isUnsaved()</literal>, along with <literal>onLoad()</literal>
+ and <literal>onSave()</literal> as follows.
+ </para>
+
+ <programlisting><![CDATA[public Boolean isUnsaved(Object entity) {
+ if (entity instanceof Persistent) {
+ return new Boolean( !( (Persistent) entity ).isSaved() );
+ }
+ else {
+ return null;
+ }
+}
+
+public boolean onLoad(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+
+ if (entity instanceof Persistent) ( (Persistent) entity ).onLoad();
+ return false;
+}
+
+public boolean onSave(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+
+ if (entity instanceof Persistent) ( (Persistent) entity ).onSave();
+ return false;
+}]]></programlisting>
+
+ <para>
+ Don't worry; in Hibernate3 you don't need to write any of this kind of code if you don't want to.
+ </para>
+ -->
+ </sect1>
+
+ <sect1 id="example-parentchild-conclusion">
+ <title>Conclusion</title>
+
+ <para>
+ Il y a quelques principes à maîtriser dans ce chapitre et tout cela peut paraître déroutant la première fois.
+ Cependant, dans la pratique, tout fonctionne parfaitement. La plupart des applications Hibernate utilisent
+ le pattern père / fils.
+ </para>
+
+ <para>
+ Nous avons évoqué une alternative dans le premier paragraphe. Aucun des points traités précédemment n'existe
+ dans le cas d'un mapping <literal><composite-element></literal> qui possède exactement la sémantique
+ d'une relation père / fils. Malheureusement, il y a deux grandes limitations pour les classes éléments
+ composites : les éléments composites ne peuvent contenir de collections, et ils ne peuvent être les fils
+ d'entités autres que l'unique parent.
+ </para>
+
+ </sect1>
+
+</chapter>
\ No newline at end of file
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/example_weblog.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/example_weblog.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/example_weblog.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,432 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<chapter id="example-weblog">
+ <title>Exemple : application Weblog</title>
+
+ <sect1 id="example-weblog-classes">
+ <title>Classes persistantes</title>
+
+ <para>
+ Les classes persistantes representent un weblog, et un article posté
+ dans un weblog. Il seront modélisés comme une relation père/fils
+ standard, mais nous allons utiliser un "bag" trié au lieu d'un set.
+
+ </para>
+
+ <programlisting><![CDATA[package eg;
+
+import java.util.List;
+
+public class Blog {
+ private Long _id;
+ private String _name;
+ private List _items;
+
+ public Long getId() {
+ return _id;
+ }
+ public List getItems() {
+ return _items;
+ }
+ public String getName() {
+ return _name;
+ }
+ public void setId(Long long1) {
+ _id = long1;
+ }
+ public void setItems(List list) {
+ _items = list;
+ }
+ public void setName(String string) {
+ _name = string;
+ }
+}]]></programlisting>
+
+ <programlisting><![CDATA[package eg;
+
+import java.text.DateFormat;
+import java.util.Calendar;
+
+public class BlogItem {
+ private Long _id;
+ private Calendar _datetime;
+ private String _text;
+ private String _title;
+ private Blog _blog;
+
+ public Blog getBlog() {
+ return _blog;
+ }
+ public Calendar getDatetime() {
+ return _datetime;
+ }
+ public Long getId() {
+ return _id;
+ }
+ public String getText() {
+ return _text;
+ }
+ public String getTitle() {
+ return _title;
+ }
+ public void setBlog(Blog blog) {
+ _blog = blog;
+ }
+ public void setDatetime(Calendar calendar) {
+ _datetime = calendar;
+ }
+ public void setId(Long long1) {
+ _id = long1;
+ }
+ public void setText(String string) {
+ _text = string;
+ }
+ public void setTitle(String string) {
+ _title = string;
+ }
+}]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-weblog-mappings">
+ <title>Mappings Hibernate</title>
+
+ <para>
+ Le mapping XML doit maintenant être relativement simple à vos yeux.
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="eg">
+
+ <class
+ name="Blog"
+ table="BLOGS">
+
+ <id
+ name="id"
+ column="BLOG_ID">
+
+ <generator class="native"/>
+
+ </id>
+
+ <property
+ name="name"
+ column="NAME"
+ not-null="true"
+ unique="true"/>
+
+ <bag
+ name="items"
+ inverse="true"
+ order-by="DATE_TIME"
+ cascade="all">
+
+ <key column="BLOG_ID"/>
+ <one-to-many class="BlogItem"/>
+
+ </bag>
+
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="eg">
+
+ <class
+ name="BlogItem"
+ table="BLOG_ITEMS"
+ dynamic-update="true">
+
+ <id
+ name="id"
+ column="BLOG_ITEM_ID">
+
+ <generator class="native"/>
+
+ </id>
+
+ <property
+ name="title"
+ column="TITLE"
+ not-null="true"/>
+
+ <property
+ name="text"
+ column="TEXT"
+ not-null="true"/>
+
+ <property
+ name="datetime"
+ column="DATE_TIME"
+ not-null="true"/>
+
+ <many-to-one
+ name="blog"
+ column="BLOG_ID"
+ not-null="true"/>
+
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-weblog-code">
+ <title>Code Hibernate</title>
+
+ <para>
+ La classe suivante montre quelques utilisations que nous pouvons faire
+ de ces classes.
+ </para>
+
+ <programlisting><![CDATA[package eg;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Iterator;
+import java.util.List;
+
+import org.hibernate.HibernateException;
+import org.hibernate.Query;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.tool.hbm2ddl.SchemaExport;
+
+public class BlogMain {
+
+ private SessionFactory _sessions;
+
+ public void configure() throws HibernateException {
+ _sessions = new Configuration()
+ .addClass(Blog.class)
+ .addClass(BlogItem.class)
+ .buildSessionFactory();
+ }
+
+ public void exportTables() throws HibernateException {
+ Configuration cfg = new Configuration()
+ .addClass(Blog.class)
+ .addClass(BlogItem.class);
+ new SchemaExport(cfg).create(true, true);
+ }
+
+ public Blog createBlog(String name) throws HibernateException {
+
+ Blog blog = new Blog();
+ blog.setName(name);
+ blog.setItems( new ArrayList() );
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ session.persist(blog);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return blog;
+ }
+
+ public BlogItem createBlogItem(Blog blog, String title, String text)
+ throws HibernateException {
+
+ BlogItem item = new BlogItem();
+ item.setTitle(title);
+ item.setText(text);
+ item.setBlog(blog);
+ item.setDatetime( Calendar.getInstance() );
+ blog.getItems().add(item);
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ session.update(blog);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return item;
+ }
+
+ public BlogItem createBlogItem(Long blogid, String title, String text)
+ throws HibernateException {
+
+ BlogItem item = new BlogItem();
+ item.setTitle(title);
+ item.setText(text);
+ item.setDatetime( Calendar.getInstance() );
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ Blog blog = (Blog) session.load(Blog.class, blogid);
+ item.setBlog(blog);
+ blog.getItems().add(item);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return item;
+ }
+
+ public void updateBlogItem(BlogItem item, String text)
+ throws HibernateException {
+
+ item.setText(text);
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ session.update(item);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ }
+
+ public void updateBlogItem(Long itemid, String text)
+ throws HibernateException {
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ BlogItem item = (BlogItem) session.load(BlogItem.class, itemid);
+ item.setText(text);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ }
+
+ public List listAllBlogNamesAndItemCounts(int max)
+ throws HibernateException {
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ List result = null;
+ try {
+ tx = session.beginTransaction();
+ Query q = session.createQuery(
+ "select blog.id, blog.name, count(blogItem) " +
+ "from Blog as blog " +
+ "left outer join blog.items as blogItem " +
+ "group by blog.name, blog.id " +
+ "order by max(blogItem.datetime)"
+ );
+ q.setMaxResults(max);
+ result = q.list();
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return result;
+ }
+
+ public Blog getBlogAndAllItems(Long blogid)
+ throws HibernateException {
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ Blog blog = null;
+ try {
+ tx = session.beginTransaction();
+ Query q = session.createQuery(
+ "from Blog as blog " +
+ "left outer join fetch blog.items " +
+ "where blog.id = :blogid"
+ );
+ q.setParameter("blogid", blogid);
+ blog = (Blog) q.uniqueResult();
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return blog;
+ }
+
+ public List listBlogsAndRecentItems() throws HibernateException {
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ List result = null;
+ try {
+ tx = session.beginTransaction();
+ Query q = session.createQuery(
+ "from Blog as blog " +
+ "inner join blog.items as blogItem " +
+ "where blogItem.datetime > :minDate"
+ );
+
+ Calendar cal = Calendar.getInstance();
+ cal.roll(Calendar.MONTH, false);
+ q.setCalendar("minDate", cal);
+
+ result = q.list();
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return result;
+ }
+}]]></programlisting>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/filters.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/filters.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/filters.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<chapter id="filters">
+ <title>Filtrer les données</title>
+
+ <para>
+ Hibernate3 fournit une nouvelle approche innovatrice pour gérer des données
+ avec des règles de "visibilité". Un <emphasis>filtre Hibernate</emphasis> est un filtre
+ global, nommé, paramétré qui peut être activé ou désactivé pour une session Hibernate
+ particulière.
+ </para>
+
+ <sect1 id="objectstate-filters">
+ <title>Filtres Hibernate</title>
+
+ <para>
+ Hibernate3 ajoute la capacité de prédéfinir des critères de filtre et d'attacher ces
+ filtres à une classe ou à une collection. Un critère de filtre est la faculté de définir
+ une clause de restriction très similaire à l'attribut "where" existant disponible sur
+ une classe et divers éléments d'une collection. Mis à part que ces conditions de filtre
+ peuvent être paramétrées. L'application peut alors prendre la décision à l'exécution
+ si des filtres donnés devraient être activés et quels devraient être leurs paramètres.
+ Des filtres peuvent être utilisés comme des vues de base de données, mais paramétrées
+ dans l'application.
+ </para>
+
+ <para>
+ Afin d'utiliser des filtres, ils doivent d'abord être définis, puis attachés aux éléments
+ de mapping appropriés. Pour définir un filtre, utilisez l'élément <literal><filter-def/></literal>
+ dans un élément <literal><hibernate-mapping/></literal> :
+ </para>
+
+ <programlisting><![CDATA[<filter-def name="myFilter">
+ <filter-param name="myFilterParam" type="string"/>
+</filter-def>]]></programlisting>
+
+ <para>
+ Puis, ce filtre peut être attaché à une classe :
+ </para>
+
+ <programlisting><![CDATA[<class name="myClass" ...>
+ ...
+ <filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
+</class>]]></programlisting>
+
+ <para>
+ ou à une collection :
+ </para>
+
+ <programlisting><![CDATA[<set ...>
+ <filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
+</set>]]></programlisting>
+
+ <para>
+ ou même aux deux (ou à plusieurs de chaque) en même temps.
+ </para>
+
+ <para>
+ Les méthodes sur <literal>Session</literal> sont : <literal>enableFilter(String filterName)</literal>,
+ <literal>getEnabledFilter(String filterName)</literal>, et <literal>disableFilter(String filterName)</literal>.
+ Par défaut, les filtres <emphasis>ne sont pas</emphasis> activés pour une session donnée ;
+ ils doivent être explicitement activés en appelant la méthode
+ <literal>Session.enabledFilter()</literal>, laquelle retourne une instance de l'interface
+ <literal>Filter</literal>. Utiliser le simple filtre défini au-dessus ressemblerait à :
+ </para>
+
+ <programlisting><![CDATA[session.enableFilter("myFilter").setParameter("myFilterParam", "some-value");]]></programlisting>
+
+ <para>
+ Notez que des méthodes sur l'interface org.hibernate.Filter autorisent le chaînage de beaucoup
+ de méthodes communes d'Hibernate.
+ </para>
+
+ <para>
+ Un exemple complet, utilisant des données temporelles avec une structure de date
+ d'enregistrement effectif :
+ </para>
+
+ <programlisting><![CDATA[<filter-def name="effectiveDate">
+ <filter-param name="asOfDate" type="date"/>
+</filter-def>
+
+<class name="Employee" ...>
+...
+ <many-to-one name="department" column="dept_id" class="Department"/>
+ <property name="effectiveStartDate" type="date" column="eff_start_dt"/>
+ <property name="effectiveEndDate" type="date" column="eff_end_dt"/>
+...
+ <!--
+ Note that this assumes non-terminal records have an eff_end_dt set to
+ a max db date for simplicity-sake
+ -->
+ <filter name="effectiveDate"
+ condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
+</class>
+
+<class name="Department" ...>
+...
+ <set name="employees" lazy="true">
+ <key column="dept_id"/>
+ <one-to-many class="Employee"/>
+ <filter name="effectiveDate"
+ condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ Puis, afin de s'assurer que vous pouvez toujours récupérer les enregistrements actuellement
+ effectifs, activez simplement le filtre sur la session avant de récupérer des données des
+ employés :
+ </para>
+
+<programlisting><![CDATA[Session session = ...;
+session.enabledFilter("effectiveDate").setParameter("asOfDate", new Date());
+List results = session.createQuery("from Employee as e where e.salary > :targetSalary")
+ .setLong("targetSalary", new Long(1000000))
+ .list();
+]]></programlisting>
+
+ <para>
+ Dans le HQL ci-dessus, bien que nous ayons seulement mentionné une contrainte de
+ salaire sur les resultats, à cause du filtre activé, la requête retournera seulement
+ les employés actuellement actifs qui ont un salaire supérieur à un million de dollars.
+ </para>
+
+ <para>
+ A noter : si vous prévoyez d'utiliser des filtres avec des jointures externes (soit
+ à travers HQL, soit par le chargement) faites attention à la direction de l'expression
+ de condition. Il est plus sûr de la positionner pour les jointures externes à gauche ;
+ en général, placez le paramètre d'abord, suivi du(des) nom(s) de colonne après l'opérateur.
+ </para>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/inheritance_mapping.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/inheritance_mapping.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/inheritance_mapping.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,483 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<chapter id="inheritance">
+ <title>Mapping d'héritage de classe</title>
+
+ <sect1 id="inheritance-strategies" revision="3">
+ <title>Les trois stratégies</title>
+
+ <para>
+ Hibernate supporte les trois stratégies d'héritage de base :
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ une table par hiérarchie de classe (table per class hierarchy)
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ une table par classe fille (table per subclass)
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ une table par classe concrète (table per concrete class)
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Hibernate supporte en plus une quatrièmestratégie, légèrement différente, qui supporte le polymorphisme :
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ le polymorphisme implicite
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Il est possible d'utiliser différentes stratégies de mapping pour différentes branches d'une même
+ hiérarchie d'héritage, et alors d'employer le polymorphisme implicite pour réaliser le
+ polymorphisme à travers toute la hiérarchie. Pourtant, Hibernate ne supporte pas de mélanger
+ des mappings <literal><subclass></literal> et
+ <literal><joined-subclass></literal> et <literal><union-subclass></literal>
+ pour le même élément <literal><class></literal> racine.
+ Il est possible de mélanger ensemble les stratégies d'une table par hiérarchie et d'une
+ table par sous-classe, pour le même élément <literal><class></literal>, en combinant
+ les éléments <literal><subclass></literal> et <literal><join></literal> (voir dessous).
+ </para>
+
+ <para>
+ Il est possible de définir des mappings de <literal>subclass</literal>, <literal>union-subclass</literal>,
+ et <literal>joined-subclass</literal> dans des documents de mapping séparés, directement sous
+ <literal>hibernate-mapping</literal>. Ceci vous permet d'étendre une hiérarchie de classe juste en
+ ajoutant un nouveau fichier de mapping. Vous devez spécifier un attribut <literal>extends</literal>
+ dans le mapping de la sous-classe, en nommant une super-classe précédemment mappée. Note :
+ précédemment cette foncionnalité rendait l'ordre des documents de mapping important. Depuis
+ Hibernate3, l'ordre des fichier de mapping n'importe plus lors de l'utilisation du mot-clef "extends".
+ L'ordre à l'intérieur d'un simple fichier de mapping impose encore de définir les classes mères
+ avant les classes filles.
+ </para>
+
+ <programlisting><![CDATA[
+ <hibernate-mapping>
+ <subclass name="DomesticCat" extends="Cat" discriminator-value="D">
+ <property name="name" type="string"/>
+ </subclass>
+ </hibernate-mapping>]]></programlisting>
+
+
+ <sect2 id="inheritance-tableperclass" >
+ <title>Une table par hiérarchie de classe</title>
+
+ <para>
+ Supposons que nous ayons une interface <literal>Payment</literal>, implémentée
+ par <literal>CreditCardPayment</literal>, <literal>CashPayment</literal>,
+ <literal>ChequePayment</literal>. La stratégie une table par hiérarchie serait :
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment" table="PAYMENT">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <discriminator column="PAYMENT_TYPE" type="string"/>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <subclass name="CreditCardPayment" discriminator-value="CREDIT">
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </subclass>
+ <subclass name="CashPayment" discriminator-value="CASH">
+ ...
+ </subclass>
+ <subclass name="ChequePayment" discriminator-value="CHEQUE">
+ ...
+ </subclass>
+</class>]]></programlisting>
+
+ <para>
+ Une seule table est requise. Une grande limitation de cette
+ stratégie est que les colonnes déclarées par les classes filles, telles que <literal>CCTYPE</literal>,
+ ne peuvent avoir de contrainte <literal>NOT NULL</literal>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="inheritance-tablepersubclass">
+ <title>Une table par classe fille</title>
+
+ <para>
+ La stratégie une table par classe fille serait :
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment" table="PAYMENT">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <joined-subclass name="CreditCardPayment" table="CREDIT_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </joined-subclass>
+ <joined-subclass name="CashPayment" table="CASH_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ ...
+ </joined-subclass>
+ <joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ ...
+ </joined-subclass>
+</class>]]></programlisting>
+
+ <para>
+ Quatre tables sont requises. Les trois tables des classes filles ont
+ une clé primaire associée à la table classe mère (le modèle relationnel
+ est une association un-vers-un).
+ </para>
+
+ </sect2>
+
+ <sect2 id="inheritance-tablepersubclass-discriminator" revision="2">
+ <title>Une table par classe fille, en utilisant un discriminant</title>
+
+ <para>
+ Notez que l'implémentation Hibernate de la stratégie un table par
+ classe fille ne nécessite pas de colonne discriminante dans la table
+ classe mère. D'autres implémentations de mappers Objet/Relationnel utilisent
+ une autre implémentation de la stratégie une table par classe fille qui nécessite
+ une colonne de type discriminant dans la table de la classe mère. L'approche
+ prise par Hibernate est plus difficile à implémenter mais plus correcte
+ d'une point de vue relationnel. Si vous aimeriez utiliser
+ une colonne discriminante avec la stratégie d'une table par classe fille, vous pourriez combiner
+ l'utilisation de <literal><subclass></literal> et
+ <literal><join></literal>, comme suit :
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment" table="PAYMENT">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <discriminator column="PAYMENT_TYPE" type="string"/>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <subclass name="CreditCardPayment" discriminator-value="CREDIT">
+ <join table="CREDIT_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </join>
+ </subclass>
+ <subclass name="CashPayment" discriminator-value="CASH">
+ <join table="CASH_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ ...
+ </join>
+ </subclass>
+ <subclass name="ChequePayment" discriminator-value="CHEQUE">
+ <join table="CHEQUE_PAYMENT" fetch="select">
+ <key column="PAYMENT_ID"/>
+ ...
+ </join>
+ </subclass>
+</class>]]></programlisting>
+
+ <para>
+ La déclaration optionnelle <literal>fetch="select"</literal> indique à Hibernate
+ de ne pas récupérer les données de la classe fille <literal>ChequePayment</literal> par une jointure externe lors des requêtes sur la classe mère.
+ </para>
+
+ </sect2>
+
+ <sect2 id="inheritance-mixing-tableperclass-tablepersubclass">
+ <title>Mélange d'une table par hiérarchie de classe avec une table par classe fille</title>
+
+ <para>
+ Vous pouvez même mélanger les stratégies d'une table par hiérarchie de classe et d'une table par classe fille en utilisant cette approche :
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment" table="PAYMENT">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <discriminator column="PAYMENT_TYPE" type="string"/>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <subclass name="CreditCardPayment" discriminator-value="CREDIT">
+ <join table="CREDIT_PAYMENT">
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </join>
+ </subclass>
+ <subclass name="CashPayment" discriminator-value="CASH">
+ ...
+ </subclass>
+ <subclass name="ChequePayment" discriminator-value="CHEQUE">
+ ...
+ </subclass>
+</class>]]></programlisting>
+
+ <para>
+ Pour importe laquelle de ces stratégies, une association polymorphique vers la classe racine
+ <literal>Payment</literal> est mappée en utilisant <literal><many-to-one></literal>.
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="payment" column="PAYMENT_ID" class="Payment"/>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="inheritance-tableperconcrete" revision="2">
+ <title>Une table par classe concrète</title>
+
+ <para>
+ Il y a deux manières d'utiliser la stratégie d'une table par classe concrète. La première
+ est d'employer <literal><union-subclass></literal>.
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="sequence"/>
+ </id>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <union-subclass name="CreditCardPayment" table="CREDIT_PAYMENT">
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </union-subclass>
+ <union-subclass name="CashPayment" table="CASH_PAYMENT">
+ ...
+ </union-subclass>
+ <union-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
+ ...
+ </union-subclass>
+</class>]]></programlisting>
+
+ <para>
+ Trois tables sont nécessaires pour les classes filles. Chaque table définit des colonnes
+ pour toutes les propriétés de la classe, incluant les propriétés héritéés.
+ </para>
+
+ <para>
+ La limitation de cette approche est que si une propriété est mappée sur la classe mère, le nom
+ de la colonne doit être le même pour toutes les classes filles. (Nous pourrions être plus souple
+ dans une future version d'Hibernate).
+ La stratégie du générateur d'identifiant n'est pas permise dans l'héritage de classes filles par
+ union, en effet la valeur (NdT : seed) de la clef primaire
+ doit être partagée par toutes les classes filles "union" d'une hiérarchie.
+ </para>
+
+ <para>
+ Si votre classe mère est abstraite, mappez la avec <literal>abstract="true"</literal>.
+ Bien sûr, si elle n'est pas abstraite, une table supplémentaire (par défaut,
+ <literal>PAYMENT</literal> dans l'exemple ci-dessus) est requise pour contenir des instances
+ de la classe mère.
+ </para>
+
+ </sect2>
+
+ <sect2 id="inheritance-tableperconcreate-polymorphism">
+ <title>Une table par classe concrète, en utilisant le polymorphisme implicite</title>
+
+ <para>
+ Une approche alternative est l'emploi du polymorphisme implicite :
+ </para>
+
+ <programlisting><![CDATA[<class name="CreditCardPayment" table="CREDIT_PAYMENT">
+ <id name="id" type="long" column="CREDIT_PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="amount" column="CREDIT_AMOUNT"/>
+ ...
+</class>
+
+<class name="CashPayment" table="CASH_PAYMENT">
+ <id name="id" type="long" column="CASH_PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="amount" column="CASH_AMOUNT"/>
+ ...
+</class>
+
+<class name="ChequePayment" table="CHEQUE_PAYMENT">
+ <id name="id" type="long" column="CHEQUE_PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="amount" column="CHEQUE_AMOUNT"/>
+ ...
+</class>]]></programlisting>
+
+ <para>
+ Notez que nulle part nous ne mentionnons l'interface <literal>Payment</literal> explicitement.
+ Notez aussi que des propriétés de <literal>Payment</literal> sont mappées dans
+ chaque classe fille. Si vous voulez éviter des duplications, considérez l'utilisation des
+ entités XML (cf. <literal>[ <!ENTITY allproperties SYSTEM "allproperties.xml"> ]</literal>
+ dans la déclaration du <literal>DOCTYPE</literal> et <literal>&allproperties;</literal> dans le mapping).
+ </para>
+
+ <para>
+ L'inconvénient de cette approche est qu'Hibernate ne génère pas d'<literal>UNION</literal>s SQL
+ lors de l'exécution des requêtes polymorphiques.
+ </para>
+
+ <para>
+ Pour cette stratégie de mapping, une association polymorphique pour <literal>Payment</literal>
+ est habituellement mappée en utilisant <literal><any></literal>.
+ </para>
+
+ <programlisting><![CDATA[<any name="payment" meta-type="string" id-type="long">
+ <meta-value value="CREDIT" class="CreditCardPayment"/>
+ <meta-value value="CASH" class="CashPayment"/>
+ <meta-value value="CHEQUE" class="ChequePayment"/>
+ <column name="PAYMENT_CLASS"/>
+ <column name="PAYMENT_ID"/>
+</any>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="inheritace-mixingpolymorphism">
+ <title>Mélange du polymorphisme implicite avec d'autres mappings d'héritage</title>
+
+ <para>
+ Il y a une chose supplémentaire à noter à propos de ce mapping. Puisque les classes filles sont
+ chacune mappées avec leur propre élément <literal><class></literal> (et puisque
+ <literal>Payment</literal> est juste une interface), chaque classe fille pourrait
+ facilement faire partie d'une autre hiérarchie
+ d'héritage ! (Et vous pouvez encore faire des requêtes polymorphiques pour l'interface <literal>Payment</literal>).
+ </para>
+
+ <programlisting><![CDATA[<class name="CreditCardPayment" table="CREDIT_PAYMENT">
+ <id name="id" type="long" column="CREDIT_PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <discriminator column="CREDIT_CARD" type="string"/>
+ <property name="amount" column="CREDIT_AMOUNT"/>
+ ...
+ <subclass name="MasterCardPayment" discriminator-value="MDC"/>
+ <subclass name="VisaPayment" discriminator-value="VISA"/>
+</class>
+
+<class name="NonelectronicTransaction" table="NONELECTRONIC_TXN">
+ <id name="id" type="long" column="TXN_ID">
+ <generator class="native"/>
+ </id>
+ ...
+ <joined-subclass name="CashPayment" table="CASH_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ <property name="amount" column="CASH_AMOUNT"/>
+ ...
+ </joined-subclass>
+ <joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ <property name="amount" column="CHEQUE_AMOUNT"/>
+ ...
+ </joined-subclass>
+</class>]]></programlisting>
+
+ <para>
+ Encore une fois, nous ne mentionnons pas explicitement <literal>Payment</literal>.
+ Si nous exécutons une requête sur l'interface <literal>Payment</literal> - par
+ exemple, <literal>from Payment</literal> - Hibernate retournera
+ automatiquement les instances de <literal>CreditCardPayment</literal>
+ (et ses classes filles puisqu'elles implémentent aussi <literal>Payment</literal>),
+ <literal>CashPayment</literal> et <literal>ChequePayment</literal> mais pas
+ les instances de <literal>NonelectronicTransaction</literal>.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="inheritance-limitations">
+ <title>Limitations</title>
+
+ <para>
+ Il y a certaines limitations à l'approche du "polymorphisme implicite"
+ pour la stratégie de mapping d'une table par classe concrète.
+ Il y a plutôt moins de limitations restrictives aux mappings <literal><union-subclass></literal>.
+ </para>
+
+ <para>
+ La table suivante montre les limitations des mappings d'une table par classe concrète, et du polymorphisme implicite, dans Hibernate.
+ </para>
+
+ <table frame="topbot">
+ <title>Caractéristiques du mapping d'héritage</title>
+ <tgroup cols='8' align='left' colsep='1' rowsep='1'>
+ <colspec colname='c1' colwidth="1*"/>
+ <colspec colname='c2' colwidth="1*"/>
+ <colspec colname='c3' colwidth="1*"/>
+ <colspec colname='c4' colwidth="1*"/>
+ <colspec colname='c5' colwidth="1*"/>
+ <colspec colname='c6' colwidth="1*"/>
+ <colspec colname='c7' colwidth="1*"/>
+ <colspec colname='c8' colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Stratégie d'héritage</entry>
+ <entry>many-to-one polymorphique</entry>
+ <entry>one-to-one polymorphique</entry>
+ <entry>one-to-many polymorphique</entry>
+ <entry>many-to-many polymorphique</entry>
+ <entry><literal>load()/get()</literal> polymorphique</entry>
+ <entry>Requêtes polymorphiques</entry>
+ <entry>Jointures polymorphiques</entry>
+ <entry>Récupération par jointure externe</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>une table par hiérarchie de classe</entry>
+ <entry><literal><many-to-one></literal></entry>
+ <entry><literal><one-to-one></literal></entry>
+ <entry><literal><one-to-many></literal></entry>
+ <entry><literal><many-to-many></literal></entry>
+ <entry><literal>s.get(Payment.class, id)</literal></entry>
+ <entry><literal>from Payment p</literal></entry>
+ <entry><literal>from Order o join o.payment p</literal></entry>
+ <entry><emphasis>supportée</emphasis></entry>
+ </row>
+ <row>
+ <entry>une table par classe fille</entry>
+ <entry><literal><many-to-one></literal></entry>
+ <entry><literal><one-to-one></literal></entry>
+ <entry><literal><one-to-many></literal></entry>
+ <entry><literal><many-to-many></literal></entry>
+ <entry><literal>s.get(Payment.class, id)</literal></entry>
+ <entry><literal>from Payment p</literal></entry>
+ <entry><literal>from Order o join o.payment p</literal></entry>
+ <entry><emphasis>supportée</emphasis></entry>
+ </row>
+ <row>
+ <entry>une table par classe concrète (union-subclass)</entry>
+ <entry><literal><many-to-one></literal></entry>
+ <entry><literal><one-to-one></literal></entry>
+ <entry><literal><one-to-many></literal> (pour <literal>inverse="true"</literal> seulement)</entry>
+ <entry><literal><many-to-many></literal></entry>
+ <entry><literal>s.get(Payment.class, id)</literal></entry>
+ <entry><literal>from Payment p</literal></entry>
+ <entry><literal>from Order o join o.payment p</literal></entry>
+ <entry><emphasis>supportée</emphasis></entry>
+ </row>
+ <row>
+ <entry>une table par classe concrète (polymorphisme implicite)</entry>
+ <entry><literal><any></literal></entry>
+ <entry><emphasis>non supporté</emphasis></entry>
+ <entry><emphasis>non supporté</emphasis></entry>
+ <entry><literal><many-to-any></literal></entry>
+ <entry><literal>s.createCriteria(Payment.class).add( Restrictions.idEq(id) ).uniqueResult()</literal></entry>
+ <entry><literal>from Payment p</literal></entry>
+ <entry><emphasis>non supportées</emphasis></entry>
+ <entry><emphasis>non supportée</emphasis></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect1>
+
+</chapter>
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/performance.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/performance.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/performance.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1451 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<chapter id="performance">
+ <title>Améliorer les performances</title>
+
+ <sect1 id="performance-fetching" revision="2">
+ <title>Stratégies de chargement</title>
+
+ <para>
+ Une <emphasis>stratégie de chargement</emphasis> est une stratégie qu'Hibernate va
+ utiliser pour récupérer des objets associés si l'application à besoin de naviguer à
+ travers une association.
+ Les stratégies de chargement peuvent être déclarées dans les méta-données de l'outil
+ de mapping objet relationnel ou surchargées par une requête de type HQL ou <literal>Criteria</literal>
+ particulière.
+ </para>
+
+ <para>
+ Hibernate3 définit les stratégies de chargement suivantes :
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Chargement par jointure</emphasis> - Hibernate récupère
+ l'instance associée ou la collection dans un même <literal>SELECT</literal>,
+ en utilisant un <literal>OUTER JOIN</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Chargement par select</emphasis> - Un second <literal>SELECT</literal>
+ est utilisé pour récupérer l'instance associée ou la collection. A moins
+ que vous ne désactiviez explicitement le chargement tardif en spécifiant
+ <literal>lazy="false"</literal>, ce second select ne sera exécuté que lorsque
+ vous accéderez réellement à l'association.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Chargement par sous-select</emphasis> - Un second <literal>SELECT</literal>
+ est utilisé pour récupérer les associations pour toutes les entités récupérées dans
+ une requête ou un chargement préalable. A moins
+ que vous ne désactiviez explicitement le chargement tardif en spécifiant
+ <literal>lazy="false"</literal>, ce second select ne sera exécuté que lorsque
+ vous accéderez réellement à l'association.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Chargement par lot</emphasis> - Il s'agit d'une stratégie d'optimisation
+ pour le chargement par select - Hibernate récupère un lot
+ d'instances ou de collections en un seul <literal>SELECT</literal> en spécifiant
+ une liste de clé primaire ou de clé étrangère.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Hibernate fait également la distinction entre :
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Chargement immédiat</emphasis> - Une association, une collection ou
+ un attribut est chargé immédiatement lorsque l'objet auquel appartient cet
+ élément est chargé.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Chargement tardif d'une collection</emphasis> - Une collection est
+ chargée lorque l'application invoque une méthode sur cette collection (il s'agit
+ du mode de chargement par défaut pour les collections).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Chargement "super tardif" d'une collection</emphasis> - les
+ éléments de la collection sont récupérés individuellement depuis la base de données
+ lorsque nécessaire.
+ Hibernate essaie de ne pas charger toute la collection en mémoire sauf si cela est
+ absolument nécessaire (bien adapté aux très grandes collections).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Chargement par proxy</emphasis> - une association vers un seul
+ objet est chargée lorsqu'une méthode autre que le getter sur l'identifiant est
+ appelée sur l'objet associé.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Chargement "sans proxy"</emphasis> - une association vers un seul objet
+ est chargée lorsque l'on accède à cet objet. Par rapport au chargement par proxy,
+ cette approche est moins tardif (l'association est quand même chargée même
+ si on n'accède qu'à l'identifiant) mais plus transparente car il n'y a pas de proxy
+ visible dans l'application. Cette approche requiert une instrumentation du bytecode
+ à la compilation et est rarement nécessaire.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Chargement tardif des attributs</emphasis> - Un attribut ou un
+ objet associé seul est chargé lorsque l'on y accède. Cette approche requiert
+ une instrumentation du bytecode à la compilation et est rarement nécessaire.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Nous avons ici deux notions orthogonales : <emphasis>quand</emphasis> l'association est
+ chargée et <emphasis>comment</emphasis> (quelle requête SQL est utilisée). Il ne faut
+ pas confondre les deux. Le mode de chargement est utilisé pour améliorer les performances.
+ On peut utiliser le mode tardif pour définir un contrat sur quelles données sont toujours
+ accessibles sur une instance détachée d'une classe particulière.
+ </para>
+
+ <sect2 id="performance-fetching-lazy">
+ <title>Travailler avec des associations chargées tardivement</title>
+
+ <para>
+ Par défaut, Hibernate3 utilise le chargement tardif par select pour les collections
+ et le chargement tardif par proxy pour les associations vers un seul objet.
+ Ces valeurs par défaut sont valables pour la plupart des associations dans la
+ plupart des applications.
+ </para>
+
+ <para>
+ <emphasis>Note :</emphasis> si vous définissez
+ <literal>hibernate.default_batch_fetch_size</literal>, Hibernate va utiliser l'optimisation
+ du chargement par lot pour le chargement tardif (cette optimisation peut aussi
+ être activée à un niveau de granularité plus fin).
+ </para>
+
+ <para>
+ Cependant, le chargement tardif pose un problème qu'il faut connaitre. L'accès à
+ une association définie comme "tardive", hors du contexte d'une session hibernate
+ ouverte, va conduire à une exception. Par exemple :
+ </para>
+
+ <programlisting><![CDATA[s = sessions.openSession();
+Transaction tx = s.beginTransaction();
+
+User u = (User) s.createQuery("from User u where u.name=:userName")
+ .setString("userName", userName).uniqueResult();
+Map permissions = u.getPermissions();
+
+tx.commit();
+s.close();
+
+Integer accessLevel = (Integer) permissions.get("accounts"); // Error!]]></programlisting>
+
+ <para>
+ Etant donné que la collection des permissions n'a pas été initialisée
+ avant que la <literal>Session</literal> soit fermée, la collection n'est
+ pas capable de se charger. <emphasis>Hibernate ne supporte pas le chargement
+ tardif pour des objets détachés</emphasis>. La solution à ce problème est de
+ déplacer le code qui lit la collection avant le "commit" de la transaction.
+ </para>
+
+ <para>
+ Une autre alternative est d'utiliser une collection ou une association non
+ "tardive" en spécifiant <literal>lazy="false"</literal> dans le mapping de
+ l'association.
+ Cependant il est prévu que le chargement tardif soit utilisé pour quasiment
+ toutes les collections ou associations. Si vous définissez trop d'associtions
+ non "tardives" dans votre modèle objet, Hibernate va finir par devoir charger
+ toute la base de données en mémoire à chaque transaction !
+ </para>
+
+ <para>
+ D'un autre côté, on veut souvent choisir un chargement par jointure (qui est par
+ défaut non tardif) à la place du chargement par select dans une transaction particulière.
+ Nous allons maintenant voir comment adapter les stratégies de chargement. Dans Hibernate3
+ les mécanismes pour choisir une stratégie de chargement sont identiques que
+ l'on ait une association vers un objet simple ou vers une collection.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-fetching-custom" revision="4">
+ <title>Personnalisation des stratégies de chargement</title>
+
+ <para>
+ Le chargement par select (mode par défaut) est très vulnérable au problème du
+ N+1 selects, du coup vous pouvez avoir envie d'activer le chargement par jointure
+ dans les fichiers de mapping :
+ </para>
+
+ <programlisting><![CDATA[<set name="permissions"
+ fetch="join">
+ <key column="userId"/>
+ <one-to-many class="Permission"/>
+</set]]></programlisting>
+
+ <programlisting><![CDATA[<many-to-one name="mother" class="Cat" fetch="join"/>]]></programlisting>
+
+ <para>
+ La stratégie de chargement définie à l'aide du mot <literal>fetch</literal> dans les fichiers
+ de mapping affecte :
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ La récupération via <literal>get()</literal> ou <literal>load()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ La récupération implicite lorsque l'on navigue à travers une association
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Les requêtes de type <literal>Criteria</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Les requêtes HQL si l'on utilise le chargement par <literal>subselect</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Quelle que soit la stratégie de chargement que vous utilisez, la partie du graphe
+ d'objets qui est définie comme non "tardive" sera chargée en mémoire. Cela peut
+ mener à l'exécution de plusieurs selects successifs pour une seule requête HQL.
+ </para>
+
+ <para>
+ On n'utilise pas souvent les documents de mapping pour adapter le chargement.
+ Au lieu de cela, on conserve le comportement par défaut et on le surcharge pour
+ une transaction particulière en utilisant <literal>left join fetch</literal>
+ dans les requêtes HQL. Cela indique à hibernate à Hibernate de charger l'association
+ de manière agressive lors du premier select en utilisant une jointure externe.
+ Dans l'API Criteria vous pouvez utiliser la méthode
+ <literal>setFetchMode(FetchMode.JOIN)</literal>
+ </para>
+
+ <para>
+ Si vous ne vous sentez pas prêt à modifier la stratégie de chargement utilisé
+ par <literal>get()</literal> ou <literal>load()</literal>, vous pouvez juste
+ utiliser une requête de type <literal>Criteria</literal> comme par exemple :
+ </para>
+
+ <programlisting><![CDATA[User user = (User) session.createCriteria(User.class)
+ .setFetchMode("permissions", FetchMode.JOIN)
+ .add( Restrictions.idEq(userId) )
+ .uniqueResult();]]></programlisting>
+
+ <para>
+ (Il s'agit de l'équivalent pour Hibernate de ce que d'autres outils de mapping
+ appellent un "fetch plan" ou "plan de chargement")
+ </para>
+
+ <para>
+ Une autre manière complètement différente d'éviter le problème des N+1 selects
+ est d'utiliser le cache de second niveau.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-fetching-proxies" revision="2">
+ <title>Proxys pour des associations vers un seul objet</title>
+
+ <para>
+ Le chargement tardif des collections est implémenté par Hibernate en utilisant
+ ses propres implémentations pour des collections persistantes. Si l'on veut un
+ chargement tardif pour des associations vers un seul objet métier il faut utiliser
+ un autre mécanisme. L'entité qui est pointée par l'association doit être masquée
+ derrière un proxy. Hibernate implémente l'initialisation tardive des proxys sur des
+ objets persistents via une mise à jour à chaud du bytecode (à l'aide de l'excellente
+ librairie CGLIB).
+ </para>
+
+ <para>
+ Par défaut, Hibernate génère des proxys (au démarrage) pour toutes les classes
+ persistantes et les utilise pour activer le chargement tardif des associations
+ <literal>many-to-one</literal> et <literal>one-to-one</literal>.
+ </para>
+
+ <para>
+ Le fichier de mapping peut déclarer une interface qui sera utilisée par le proxy
+ d'interfaçage pour cette classe à l'aide de l'attribut <literal>proxy</literal>.
+ Par défaut Hibernate utilises une sous classe de la classe persistante.
+ <emphasis>Il faut que les classes pour lesquelles on ajoute un proxy implémentent
+ un constructeur par défaut de visibilité au moins package. Ce constructeur est
+ recommandé pour toutes les classes persistantes !</emphasis>
+ </para>
+
+
+
+ <para>
+ Il y a quelques précautions à prendre lorsque l'on étend cette approche à des classes
+ polymorphiques, exemple :
+ </para>
+
+ <programlisting><![CDATA[<class name="Cat" proxy="Cat">
+ ......
+ <subclass name="DomesticCat" proxy="DomesticCat">
+ .....
+ </subclass>
+ </class>]]></programlisting>
+
+ <para>
+ Tout d'abord, les instances de <literal>Cat</literal> ne pourront jamais être "castées"
+ en <literal>DomesticCat</literal>, même si l'instance sous jacente est une instance
+ de <literal>DomesticCat</literal> :
+ </para>
+
+ <programlisting><![CDATA[Cat cat = (Cat) session.load(Cat.class, id); // instancie un proxy (n'interroge pas la base de données)
+if ( cat.isDomesticCat() ) { // interroge la base de données pour initialiser le proxy
+ DomesticCat dc = (DomesticCat) cat; // Erreur !
+ ....
+}]]></programlisting>
+
+ <para>
+ Deuxièmement, il est possible de casser la notion d'<literal>==</literal> des proxy.
+ </para>
+
+ <programlisting><![CDATA[Cat cat = (Cat) session.load(Cat.class, id); // instancie un proxy Cat
+DomesticCat dc =
+ (DomesticCat) session.load(DomesticCat.class, id); // acquiert un nouveau proxy DomesticCat
+System.out.println(cat==dc); // faux]]></programlisting>
+
+ <para>
+ Cette situation n'est pas si mauvaise qu'il n'y parait. Même si nous avons deux
+ références à deux objets proxys différents, l'instance de base sera quand même le même objet :
+ </para>
+
+ <programlisting><![CDATA[cat.setWeight(11.0); // interroge la base de données pour initialiser le proxy
+System.out.println( dc.getWeight() ); // 11.0]]></programlisting>
+
+ <para>
+ Troisièmement, vous ne pourrez pas utiliser un proxy CGLIB pour une classe <literal>final</literal>
+ ou pour une classe contenant la moindre méthode <literal>final</literal>.
+ </para>
+
+ <para>
+ Enfin, si votre objet persistant obtient une ressource à l'instanciation (par
+ example dans les initialiseurs ou dans le contructeur par défaut), alors ces ressources
+ seront aussi obtenues par le proxy. La classe proxy est vraiment une sous classe de la classe
+ persistante.
+ </para>
+
+ <para>
+ Ces problèmes sont tous dus aux limitations fondamentales du modèle d'héritage unique de Java.
+ Si vous souhaitez éviter ces problèmes, vos classes persistantes doivent chacune implémenter
+ une interface qui déclare ses méthodes métier. Vous devriez alors spécifier ces interfaces
+ dans le fichier de mapping :
+ </para>
+
+ <programlisting><![CDATA[<class name="CatImpl" proxy="Cat">
+ ......
+ <subclass name="DomesticCatImpl" proxy="DomesticCat">
+ .....
+ </subclass>
+</class>]]></programlisting>
+
+ <para>
+ où <literal>CatImpl</literal> implémente l'interface <literal>Cat</literal> et <literal>DomesticCatImpl</literal>
+ implémente l'interface <literal>DomesticCat</literal>. Ainsi, des proxys pour les instances de
+ <literal>Cat</literal> et <literal>DomesticCat</literal> pourraient être retournées par <literal>load()</literal>
+ ou <literal>iterate()</literal> (Notez que <literal>list()</literal> ne retourne généralement pas de proxy).
+ </para>
+
+ <programlisting><![CDATA[Cat cat = (Cat) session.load(CatImpl.class, catid);
+Iterator iter = session.iterate("from CatImpl as cat where cat.name='fritz'");
+Cat fritz = (Cat) iter.next();]]></programlisting>
+
+ <para>
+ Les relations sont aussi initialisées tardivement. Ceci signifie que vous
+ devez déclarer chaque propriété comme étant de type <literal>Cat</literal>,
+ et non <literal>CatImpl</literal>.
+ </para>
+
+ <para>
+ Certaines opérations ne nécessitent pas l'initialisation du proxy
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>equals()</literal>, si la classe persistante ne surcharge pas
+ <literal>equals()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>hashCode()</literal>, si la classe persistante ne surcharge pas
+ <literal>hashCode()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Le getter de l'identifiant
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Hibernate détectera les classes qui surchargent <literal>equals()</literal> ou
+ <literal>hashCode()</literal>.
+ </para>
+
+ <para>
+ Eh choisissant <literal>lazy="no-proxy"</literal> au lieu de <literal>lazy="proxy"</literal>
+ qui est la valeur par défaut, il est possible d'éviter les problèmes liés au transtypage.
+ Il faudra alors une instrumentation du bytecode à la compilation et toutes les opérations
+ résulterons immédiatement en une initialisation du proxy.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-fetching-initialization" revision="1">
+ <title>Initialisation des collections et des proxys</title>
+
+ <para>
+ Une exception de type <literal>LazyInitializationException</literal> sera renvoyée par hibernate
+ si une collection ou un proxy non initialisé est accédé en dehors de la portée de la <literal>Session</literal>,
+ e.g. lorsque l'entité à laquelle appartient la collection ou qui a une référence vers le proxy est
+ dans l'état "détachée".
+ </para>
+
+ <para>
+ Parfois, nous devons nous assurer qu'un proxy ou une collection est initialisée avant de
+ fermer la <literal>Session</literal>. Bien sûr, nous pouvons toujours forcer l'initialisation
+ en appelant par exemple <literal>cat.getSex()</literal> ou <literal>cat.getKittens().size()</literal>.
+ Mais ceci n'est pas très lisible pour les personnes parcourant le code et n'est pas très générique.
+ </para>
+
+ <para>
+ Les méthodes statiques <literal>Hibernate.initialize()</literal> et <literal>Hibernate.isInitialized()</literal>
+ fournissent à l'application un moyen de travailler avec des proxys ou des collections initialisés.
+ <literal>Hibernate.initialize(cat)</literal> forcera l'initialisation d'un proxy de <literal>cat</literal>,
+ si tant est que sa <literal>Session</literal> est ouverte. <literal>Hibernate.initialize( cat.getKittens() )</literal>
+ a le même effet sur la collection kittens.
+ </para>
+
+ <para>
+ Une autre option est de conserver la <literal>Session</literal> ouverte jusqu'à
+ ce que toutes les collections et tous les proxys aient été chargés. Dans certaines
+ architectures applicatives, particulièrement celles ou le code d'accès aux données
+ via hiberante et le code qui utilise ces données sont dans des couches applicatives
+ différentes ou des processus physiques différents, il peut devenir problématique
+ de garantir que la <literal>Session</literal> est ouverte lorsqu'une collection
+ est initialisée. Il y a deux moyens de traiter ce problème :
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ Dans une application web, un filtre de servlet peut être utilisé pour
+ fermer la <literal>Session</literal> uniquement lorsque la requête
+ a été entièrement traitée, lorsque le rendu de la vue est fini
+ (il s'agit du pattern <emphasis>Open Session in View</emphasis>).
+ Bien sûr, cela demande plus d'attention à la bonne gestion des exceptions
+ de l'application. Il est d'une importance vitale que la <literal>Session</literal>
+ soit fermée et la transaction terminée avant que l'on rende la main à l'utilisateur
+ même si une exception survient durant le traitement de la vue.
+ Voir le wiki Hibernate pour des exemples sur le pattern
+ "Open Session in View".
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Dans une application avec une couche métier séparée, la couche contenant
+ la logique métier doit "préparer" toutes les collections qui seront
+ nécessaires à la couche web avant de retourner les données. Cela signifie
+ que la couche métier doit charger toutes les données et retourner toutes
+ les données déjà initialisées à la couche de présentation/web pour un
+ cas d'utilisation donné. En général l'application appelle la méthode
+ <literal>Hibernate.initialize()</literal> pour chaque collection nécessaire
+ dans la couche web (cet appel doit être fait avant la fermeture de la session)
+ ou bien récupère les collections de manière agressive à l'aide d'une requête
+ HQL avec une clause <literal>FETCH</literal> ou à l'aide du mode
+ <literal>FetchMode.JOIN</literal> pour une requête de type <literal>Criteria</literal>.
+ Cela est en général plus facile si vous utilisez le pattern <emphasis>Command</emphasis>
+ plutôt que <emphasis>Session Facade</emphasis>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Vous pouvez également attacher à une <literal>Session</literal> un objet chargé
+ au préalable à l'aide des méthodes <literal>merge()</literal> ou <literal>lock()</literal>
+ avant d'accéder aux collections (ou aux proxys) non initialisés. Non, Hibernate ne
+ fait pas, et ne doit pas faire, cela automatiquement car cela pourrait introduire
+ une sémantique transactionnelle ad hoc.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Parfois, vous ne voulez pas initialiser une grande collection mais vous avez quand même
+ besoin d'informations sur elle (comme sa taille) ou un sous ensemble de ses données
+ </para>
+
+ <para>
+ Vous pouvez utiliser un filtre de collection pour récupérer sa taille sans l'initialiser :
+ </para>
+
+ <programlisting><![CDATA[( (Integer) s.createFilter( collection, "select count(*)" ).list().get(0) ).intValue()]]></programlisting>
+
+ <para>
+ La méthode <literal>createFilter()</literal> est également utilisée pour récupérer
+ de manière efficace des sous ensembles d'une collection sans avoir besoin de l'initialiser
+ dans son ensemble.
+ </para>
+
+ <programlisting><![CDATA[s.createFilter( lazyCollection, "").setFirstResult(0).setMaxResults(10).list();]]></programlisting>
+
+ </sect2>
+
+
+ <sect2 id="performance-fetching-batch">
+ <title>Utiliser le chargement par lot</title>
+
+ <para>
+ Pour améliorer les performances, Hibernate peut utiliser le chargement par lot
+ ce qui veut dire qu'Hibernate peut charger plusieurs proxys (ou collections) non initialisés en une seule
+ requête lorsque l'on accède à l'un de ces proxys. Le chargement par lot est une optimisation
+ intimement liée à la stratégie de chargement tardif par select. Il y a deux moyens d'activer le
+ chargement par lot : au niveau de la classe et au niveau de la collection.
+ </para>
+
+ <para>
+ Le chargement par lot pour les classes/entités est plus simple à comprendre. Imaginez que vous ayez la
+ situation suivante à l'exécution : vous avez 25 instances de <literal>Cat</literal>
+ chargées dans une <literal>Session</literal>, chaque <literal>Cat</literal> a une référence
+ à son <literal>owner</literal>, une <literal>Person</literal>.
+ La classe <literal>Person</literal> est mappée avec un proxy, <literal>lazy="true"</literal>.
+ Si vous itérez sur tous les cats et appelez <literal>getOwner()</literal> sur chacun d'eux,
+ Hibernate exécutera par défaut 25 <literal>SELECT</literal>, pour charger les owners
+ (initialiser le proxy). Vous pouvez paramétrer ce comportement en spécifiant une
+ <literal>batch-size</literal> (taille du lot) dans le mapping de <literal>Person</literal> :
+ </para>
+
+ <programlisting><![CDATA[<class name="Person" batch-size="10">...</class>]]></programlisting>
+
+ <para>
+ Hibernate exécutera désormais trois requêtes, en chargeant respectivement 10,
+ 10, et 5 entités.
+ </para>
+
+ <para>
+ Vous pouvez aussi activer le chargement par lot pour les collections. Par exemple,
+ si chaque <literal>Person</literal> a une collection chargée tardivement de
+ <literal>Cat</literal>s, et que 10 personnes sont actuellement chargées dans la
+ <literal>Session</literal>, itérer sur toutes les persons générera 10 <literal>SELECT</literal>s,
+ un pour chaque appel de <literal>getCats()</literal>. Si vous activez le chargement par lot pour la
+ collection <literal>cats</literal> dans le mapping de <literal>Person</literal>, Hibernate pourra
+ précharger les collections :
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <set name="cats" batch-size="3">
+ ...
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ Avec une taille de lot (<literal>batch-size</literal>) de 8, Hibernate chargera
+ respectivement 3, 3, 3, et 1 collections en quatre <literal>SELECT</literal>s.
+ Encore une fois, la valeur de l'attribut dépend du nombre de collections
+ non initialisées dans une <literal>Session</literal> particulière.
+ </para>
+
+ <para>
+ Le chargement par lot de collections est particulièrement utile si vous avez des
+ arborescenses récursives d'éléments (typiquement, le schéma facture de
+ matériels). (Bien qu'un <emphasis>sous ensemble</emphasis> ou un
+ <emphasis>chemin matérialisé</emphasis> est sans doute une meilleure option pour
+ des arbres principalement en lecture.)
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-fetching-subselect">
+ <title>Utilisation du chargement par sous select</title>
+
+ <para>
+ Si une collection ou un proxy vers un objet doit être chargé, Hibernate va tous les
+ charger en ré-exécutant la requête orignial dans un sous select. Cela fonctionne de la
+ même manière que le chargement par lot sans la possibilité de fragmenter le chargement.
+ </para>
+
+ <!-- TODO: Write more about this -->
+
+ </sect2>
+
+ <sect2 id="performance-fetching-lazyproperties">
+ <title>Utiliser le chargement tardif des propriétés</title>
+
+ <para>
+ Hibernate3 supporte le chargement tardif de propriétés individuelles. La technique
+ d'optimisation est également connue sous le nom de <emphasis>fetch groups</emphasis> (groupes
+ de chargement). Il faut noter qu'il s'agit principalement d'une fonctionnalité marketing
+ car en pratique l'optimisation de la lecture d'un enregistrement est beaucoup plus importante
+ que l'optimisation de la lecture d'une colonne. Cependant, la restriction du chargement à
+ certaines colonnes peut être pratique dans des cas extrèmes, lorsque des tables "legacy"
+ possèdent des centaines de colonnes et que le modèle de données ne peut pas être amélioré.
+ </para>
+
+ <para>
+ Pour activer le chargement tardif d'une propriété, il faut mettre l'attribut <literal>lazy</literal>
+ sur une propriété particulière du mapping :
+ </para>
+
+ <programlisting><![CDATA[<class name="Document">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="name" not-null="true" length="50"/>
+ <property name="summary" not-null="true" length="200" lazy="true"/>
+ <property name="text" not-null="true" length="2000" lazy="true"/>
+</class>]]></programlisting>
+
+ <para>
+ Le chargement tardif des propriétés requiert une instrumentation du bytecode lors de la
+ compilation ! Si les classes persistantes ne sont pas instrumentées, Hibernate ignorera de
+ manière silencieuse le mode tardif et retombera dans le mode de chargement immédiat.
+ </para>
+
+ <para>
+ Pour l'instrumentation du bytecode vous pouvez utiliser la tâche Ant suivante :
+ </para>
+
+ <programlisting><![CDATA[<target name="instrument" depends="compile">
+ <taskdef name="instrument" classname="org.hibernate.tool.instrument.InstrumentTask">
+ <classpath path="${jar.path}"/>
+ <classpath path="${classes.dir}"/>
+ <classpath refid="lib.class.path"/>
+ </taskdef>
+
+ <instrument verbose="true">
+ <fileset dir="${testclasses.dir}/org/hibernate/auction/model">
+ <include name="*.class"/>
+ </fileset>
+ </instrument>
+</target>]]></programlisting>
+
+ <para>
+ Une autre façon (meilleure ?) pour éviter de lire plus de colonnes que
+ nécessaire au moins pour des transactions en lecture seule est d'utiliser
+ les fonctionnalités de projection des requêtes HQL ou Criteria. Cela évite
+ de devoir instrumenter le bytecode à la compilation et est certainement une
+ solution préférable.
+ </para>
+
+ <para>
+ Vous pouvez forcer le mode de chargement agressif des propriétés en utilisant
+ <literal>fetch all properties</literal> dans les requêts HQL.
+ </para>
+
+ </sect2>
+ </sect1>
+
+ <sect1 id="performance-cache" revision="1">
+ <title>Le cache de second niveau</title>
+
+ <para>
+ Une <literal>Session</literal> Hibernate est un cache de niveau transactionnel
+ des données persistantes. Il est possible de configurer un cache de cluster ou de JVM
+ (de niveau <literal>SessionFactory</literal> pour être exact) défini classe par classe
+ et collection par collection. Vous pouvez même utiliser votr choix de cache
+ en implémentant le pourvoyeur (provider) associé.
+ Faites attention, les caches ne sont jamais avertis des modifications faites
+ dans la base de données par d'autres applications (ils peuvent cependant être
+ configurés pour régulièrement expirer les données en cache).
+ </para>
+
+ <para>
+ Par défaut, Hibernate utilise EHCache comme cache de niveau JVM (le support
+ de JCS est désormais déprécié et sera enlevé des futures versions d'Hibernate).
+ Vous pouvez choisir une autre implémentation en spécifiant le nom de la classe qui
+ implémente <literal>org.hibernate.cache.CacheProvider</literal> en utilisant
+ la propriété <literal>hibernate.cache.provider_class</literal>.
+ </para>
+
+ <table frame="topbot" id="cacheproviders" revision="1">
+ <title>Fournisseur de cache</title>
+ <tgroup cols='5' align='left' colsep='1' rowsep='1'>
+ <colspec colname='c1' colwidth="1*"/>
+ <colspec colname='c2' colwidth="3*"/>
+ <colspec colname='c3' colwidth="1*"/>
+ <colspec colname='c4' colwidth="1*"/>
+ <colspec colname='c5' colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Cache</entry>
+ <entry>Classe pourvoyeuse</entry>
+ <entry>Type</entry>
+ <entry>Support en Cluster</entry>
+ <entry>Cache de requêtes supporté</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>Hashtable (ne pas utiliser en production)</entry>
+ <entry><literal>org.hibernate.cache.HashtableCacheProvider</literal></entry>
+ <entry>mémoire</entry>
+ <entry></entry>
+ <entry>oui</entry>
+ </row>
+ <row>
+ <entry>EHCache</entry>
+ <entry><literal>org.hibernate.cache.EhCacheProvider</literal></entry>
+ <entry>mémoire, disque</entry>
+ <entry></entry>
+ <entry>oui</entry>
+ </row>
+ <row>
+ <entry>OSCache</entry>
+ <entry><literal>org.hibernate.cache.OSCacheProvider</literal></entry>
+ <entry>mémoire, disque</entry>
+ <entry></entry>
+ <entry>oui</entry>
+ </row>
+ <row>
+ <entry>SwarmCache</entry>
+ <entry><literal>org.hibernate.cache.SwarmCacheProvider</literal></entry>
+ <entry>en cluster (multicast ip)</entry>
+ <entry>oui (invalidation de cluster)</entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>JBoss TreeCache</entry>
+ <entry><literal>org.hibernate.cache.TreeCacheProvider</literal></entry>
+ <entry>en cluster (multicast ip), transactionnel</entry>
+ <entry>oui (replication)</entry>
+ <entry>oui (horloge sync. nécessaire)</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <sect2 id="performance-cache-mapping" revision="2">
+ <title>Mapping de Cache</title>
+
+ <para>
+ L'élément <literal><cache></literal> d'une classe ou d'une collection à
+ la forme suivante :
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="cache1" coords="2 70"/>
+ <area id="cache2" coords="3 70"/>
+ <area id="cache3" coords="4 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<cache
+ usage="transactional|read-write|nonstrict-read-write|read-only"
+ region="RegionName"
+ include="all|non-lazy"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="cache1">
+ <para>
+ <literal>usage</literal> (requis) spécifie la stratégie de cache :
+ <literal>transactionel</literal>,
+ <literal>lecture-écriture</literal>,
+ <literal>lecture-écriture non stricte</literal> ou
+ <literal>lecture seule</literal>
+ </para>
+ </callout>
+ <callout arearefs="cache2">
+ <para>
+ <literal>region</literal> (optionnel, par défaut il s'agit du nom
+ de la classe ou du nom de role de la collection) spécifie le nom de la
+ région du cache de second niveau
+ </para>
+ </callout>
+ <callout arearefs="cache3">
+ <para>
+ <literal>include</literal> (optionnel, par défaut <literal>all</literal>)
+ <literal>non-lazy</literal> spécifie que les propriétés des entités mappées avec
+ <literal>lazy="true"</literal> ne doivent pas être mises en cache lorsque
+ le chargement tardif des attributs est activé.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Alternativement (voir préférentiellement), vous pouvez spécifier les éléments
+ <literal><class-cache></literal> et <literal><collection-cache></literal>
+ dans <literal>hibernate.cfg.xml</literal>.
+ </para>
+
+ <para>
+ L'attribut <literal>usage</literal> spécifie une <emphasis>stratégie de concurrence d'accès au cache</emphasis>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-cache-readonly">
+ <title>Strategie : lecture seule</title>
+
+ <para>
+ Si votre application a besoin de lire mais ne modifie jamais les instances d'une classe,
+ un cache <literal>read-only</literal> peut être utilisé. C'est la stratégie la plus simple
+ et la plus performante. Elle est même parfaitement sûre dans un cluster.
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Immutable" mutable="false">
+ <cache usage="read-only"/>
+ ....
+</class>]]></programlisting>
+
+ </sect2>
+
+
+ <sect2 id="performance-cache-readwrite">
+ <title>Stratégie : lecture/écriture</title>
+
+ <para>
+ Si l'application a besoin de mettre à jour des données, un cache <literal>read-write</literal> peut
+ être approprié. Cette stratégie ne devrait jamais être utilisée si votre application
+ nécessite un niveau d'isolation transactionnelle sérialisable. Si le cache est utilisé
+ dans un environnement JTA, vous devez spécifier
+ <literal>hibernate.transaction.manager_lookup_class</literal>, fournissant une stratégie pour obtenir
+ le <literal>TransactionManager</literal> JTA. Dans d'autres environnements, vous devriez vous assurer
+ que la transation est terminée à l'appel de <literal>Session.close()</literal>
+ ou <literal>Session.disconnect()</literal>. Si vous souhaitez utiliser cette stratégie
+ dans un cluster, vous devriez vous assurer que l'implémentation de cache utilisée supporte
+ le vérrouillage. Ce que ne font <emphasis>pas</emphasis> les pourvoyeurs caches fournis.
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Cat" .... >
+ <cache usage="read-write"/>
+ ....
+ <set name="kittens" ... >
+ <cache usage="read-write"/>
+ ....
+ </set>
+</class>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="performance-cache-nonstrict">
+ <title>Stratégie : lecture/écriture non stricte</title>
+
+ <para>
+ Si l'application besoin de mettre à jour les données de manière occasionnelle
+ (qu'il est très peu probable que deux transactions essaient de mettre à jour le même
+ élément simultanément) et qu'une isolation transactionnelle stricte n'est pas nécessaire,
+ un cache <literal>nonstrict-read-write</literal> peut être approprié. Si le cache est
+ utilisé dans un environnement JTA, vous devez spécifier
+ <literal>hibernate.transaction.manager_lookup_class</literal>. Dans d'autres
+ environnements, vous devriez vous assurer que la transation est terminée à l'appel
+ de <literal>Session.close()</literal> ou <literal>Session.disconnect()</literal>
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-cache-transactional">
+ <title>Stratégie : transactionelle</title>
+
+ <para>
+ La stratégie de cache <literal>transactional</literal> supporte un cache
+ complètement transactionnel comme, par exemple, JBoss TreeCache. Un tel cache ne
+ peut être utilisé que dans un environnement JTA et vous devez spécifier
+ <literal>hibernate.transaction.manager_lookup_class</literal>.
+ </para>
+
+ </sect2>
+
+ <para>
+ Aucun des caches livrés ne supporte toutes les stratégies de concurrence. Le tableau suivant montre
+ quels caches sont compatibles avec quelles stratégies de concurrence.
+ </para>
+
+ <table frame="topbot">
+ <title>Stratégie de concurrence du cache</title>
+ <tgroup cols='5' align='left' colsep='1' rowsep='1'>
+ <colspec colname='c1' colwidth="1*"/>
+ <colspec colname='c2' colwidth="1*"/>
+ <colspec colname='c3' colwidth="1*"/>
+ <colspec colname='c4' colwidth="1*"/>
+ <colspec colname='c5' colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Cache</entry>
+ <entry>read-only (lecture seule)</entry>
+ <entry>nonstrict-read-write (lecture-écriture non stricte)</entry>
+ <entry>read-write (lecture-ériture)</entry>
+ <entry>transactional (transactionnel)</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>Hashtable (ne pas utilser en production)</entry>
+ <entry>oui</entry>
+ <entry>oui</entry>
+ <entry>oui</entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>EHCache</entry>
+ <entry>oui</entry>
+ <entry>oui</entry>
+ <entry>oui</entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>OSCache</entry>
+ <entry>oui</entry>
+ <entry>oui</entry>
+ <entry>oui</entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>SwarmCache</entry>
+ <entry>oui</entry>
+ <entry>oui</entry>
+ <entry></entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>JBoss TreeCache</entry>
+ <entry>oui</entry>
+ <entry></entry>
+ <entry></entry>
+ <entry>oui</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect1>
+
+ <sect1 id="performance-sessioncache" revision="2">
+ <title>Gérer les caches</title>
+
+ <para>
+ A chaque fois que vous passez un objet à la méthode <literal>save()</literal>,
+ <literal>update()</literal> ou <literal>saveOrUpdate()</literal> et à chaque fois
+ que vous récupérez un objet avec <literal>load()</literal>, <literal>get()</literal>,
+ <literal>list()</literal>, <literal>iterate()</literal> or <literal>scroll()</literal>,
+ cet objet est ajouté au cache interne de la <literal>Session</literal>.
+ </para>
+ <para>
+ Lorsqu'il y a un appel à la méthode <literal>flush()</literal>, l'état de cet objet
+ va être synchronisé avec la base de données. Si vous ne voulez pas que cette synchronisation
+ ait lieu ou si vous traitez un grand nombre d'objets et que vous avez besoin de gérer
+ la mémoire de manière efficace, vous pouvez utiliser la méthode <literal>evict()</literal>
+ pour supprimer l'objet et ses collections dépendantes du cache de la session
+ </para>
+
+ <programlisting><![CDATA[ScrollableResult cats = sess.createQuery("from Cat as cat").scroll(); //a huge result set
+while ( cats.next() ) {
+ Cat cat = (Cat) cats.get(0);
+ doSomethingWithACat(cat);
+ sess.evict(cat);
+}]]></programlisting>
+
+ <para>
+ La <literal>Session</literal> dispose aussi de la méthode <literal>contains()</literal> pour déterminer
+ si une instance appartient au cache de la session.
+ </para>
+
+ <para>
+ Pour retirer tous les objets du cache session, appelez <literal>Session.clear()</literal>
+ </para>
+
+ <para>
+ Pour le cache de second niveau, il existe des méthodes définies dans
+ <literal>SessionFactory</literal> pour retirer des instances du cache,
+ la classe entière, une instance de collection ou
+ le rôle entier d'une collection.
+ </para>
+
+ <programlisting><![CDATA[sessionFactory.evict(Cat.class, catId); //evict a particular Cat
+sessionFactory.evict(Cat.class); //evict all Cats
+sessionFactory.evictCollection("Cat.kittens", catId); //evict a particular collection of kittens
+sessionFactory.evictCollection("Cat.kittens"); //evict all kitten collections]]></programlisting>
+
+ <para>
+ Le <literal>CacheMode</literal> contrôle comme une session particulière interragit avec le
+ cache de second niveau
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>CacheMode.NORMAL</literal> - lit et écrit les items dans le cache de second niveau
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>CacheMode.GET</literal> - lit les items dans le cache de second niveau mais ne
+ les écrit pas sauf dans le cache d'une mise à jour d'une donnée
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>CacheMode.PUT</literal> - écrit les items dans le cache de second niveau mais ne les
+ lit pas dans le cache de second niveau
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>CacheMode.REFRESH</literal> - écrit les items dans le cache de second niveau mais ne les
+ lit pas dans le cache de second niveau, outrepasse l'effet de<literal>hibernate.cache.use_minimal_puts</literal>,
+ en forçant un rafraîchissement du cache de second niveau pour chaque item lu dans la base
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Pour parcourir le contenu du cache de second niveau ou la région du cache dédiée au requêtes, vous
+ pouvez utiliser l'API <literal>Statistics</literal>
+ API:
+ </para>
+
+ <programlisting><![CDATA[Map cacheEntries = sessionFactory.getStatistics()
+ .getSecondLevelCacheStatistics(regionName)
+ .getEntries();]]></programlisting>
+
+ <para>
+ Vous devez pour cela activer les statistiques et optionnellement forcer Hibernate à conserver les entrées dans le
+ cache sous un format plus compréhensible pour l'utilisateur :
+ </para>
+
+ <programlisting><![CDATA[hibernate.generate_statistics true
+hibernate.cache.use_structured_entries true]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="performance-querycache" revision="1">
+ <title>Le cache de requêtes</title>
+
+ <para>
+ Les résultats d'une requête peuvent aussi être placés en cache. Ceci n'est utile
+ que pour les requêtes qui sont exécutées avec les mêmes paramètres. Pour utiliser
+ le cache de requêtes, vous devez d'abord l'activer :
+ </para>
+
+ <programlisting><![CDATA[hibernate.cache.use_query_cache true]]></programlisting>
+
+ <para>
+ Ce paramètre amène la création de deux nouvelles régions dans le cache, une qui va conserver
+ le résultat des requêtes mises en cache (<literal>org.hibernate.cache.StandardQueryCache</literal>)
+ et l'autre qui va conserver l'horodatage des mises à jour les plus récentes effectuées sur les
+ tables requêtables (<literal>org.hibernate.cache.UpdateTimestampsCache</literal>).
+ Il faut noter que le cache de requête ne conserve pas l'état des entités, il met en cache
+ uniquement les valeurs de l'identifiant et les valeurs de types de base (?). Le cache
+ de requête doit toujours être utilisé avec le cache de second niveau pour être efficace.
+ </para>
+
+ <para>
+ La plupart des requêtes ne retirent pas de bénéfice pas du cache,
+ donc par défaut les requêtes ne sont pas mises en cache. Pour activer le cache,
+ appelez <literal>Query.setCacheable(true)</literal>.
+ Cet appel permet de vérifier si les résultats sont en cache ou non, voire
+ d'ajouter ces résultats si la requête est exécutée.
+ </para>
+
+ <para>
+ Si vous avez besoin de contrôler finement les délais d'expiration du cache, vous
+ pouvez spécifier une région de cache nommée pour une requête particulière en
+ appelant <literal>Query.setCacheRegion()</literal>.
+ </para>
+
+ <programlisting><![CDATA[List blogs = sess.createQuery("from Blog blog where blog.blogger = :blogger")
+ .setEntity("blogger", blogger)
+ .setMaxResults(15)
+ .setCacheable(true)
+ .setCacheRegion("frontpages")
+ .list();]]></programlisting>
+
+ <para>
+ Si une requête doit forcer le rafraîchissement de sa région de cache, vous devez
+ appeler <literal>Query.setCacheMode(CacheMode.REFRESH)</literal>. C'est particulièrement
+ utile lorsque les données peuvent avoir été mises à jour par un processus séparé (e.g. elles
+ n'ont pas été modifiées par Hibernate). Cela permet à l'application de rafraîchir de
+ manière sélective les résultats d'une requête particulière. Il s'agit d'une alternative plus
+ efficace à l'éviction d'une région du cache à l'aide de la méthode
+ <literal>SessionFactory.evictQueries()</literal>.
+ </para>
+
+ </sect1>
+ <sect1 id="performance-collections">
+ <title>Comprendre les performances des Collections</title>
+
+ <para>
+ Nous avons déjà passé du temps à discuter des collections.
+ Dans cette section, nous allons traiter du comportement des
+ collections à l'exécution.
+ </para>
+
+ <sect2 id="performance-collections-taxonomy">
+ <title>Classification</title>
+
+ <para>Hibernate définit trois types de collections :</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>les collections de valeurs</para>
+ </listitem>
+ <listitem>
+ <para>les associations un-vers-plusieurs</para>
+ </listitem>
+ <listitem>
+ <para>les associations plusieurs-vers-plusieurs</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Cette classification distingue les différentes relations entre les tables
+ et les clés étrangères mais ne nous apprend rien de ce que nous devons savoir
+ sur le modèle relationnel. Pour comprendre parfaitement la structure relationnelle
+ et les caractéristiques des performances, nous devons considérer la structure
+ de la clé primaire qui est utilisée par Hibernate pour mettre à jour ou supprimer
+ les éléments des collections. Celà nous amène aux classifications suivantes :
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>collections indexées</para>
+ </listitem>
+ <listitem>
+ <para>sets</para>
+ </listitem>
+ <listitem>
+ <para>bags</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Toutes les collections indexées (maps, lists, arrays) ont une clé primaire constituée
+ des colonnes clé (<literal><key></literal>) et <literal><index></literal>.
+ Avec ce type de clé primaire, la mise à jour de collection est en général très performante - la clé
+ primaire peut être indexées efficacement et un élément particulier peut être
+ localisé efficacement lorsqu'Hibernate essaie de le mettre à jour ou de le supprimer.
+ </para>
+
+ <para>
+ Les Sets ont une clé primaire composée de <literal><key></literal> et des
+ colonnes représentant l'élément. Elle est donc moins efficace pour certains
+ types de collections d'éléments, en particulier les éléments composites,
+ les textes volumineux ou les champs binaires ; la base de données
+ peut ne pas être capable d'indexer aussi efficacement une clé primaire
+ aussi complexe. Cependant, pour les associations un-vers-plusieurs
+ ou plusieurs-vers-plusieurs, spécialement lorsque l'on utilise des entités
+ ayant des identifiants techniques, il est probable que cela soit aussi efficace
+ (note : si vous voulez que <literal>SchemaExport</literal> créé effectivement
+ la clé primaire d'un <literal><set></literal> pour vous, vous devez
+ déclarer toutes les colonnes avec <literal>not-null="true"</literal>).
+ </para>
+
+ <para>
+ Le mapping à l'aide d'<literal><idbag></literal> définit une clé
+ de substitution ce qui leur permet d'être très efficaces lors de la
+ mise à jour. En fait il s'agit du meilleur cas de mise à jour d'une collection
+ </para>
+
+ <para>
+ Le pire cas intervient pour les Bags. Dans la mesure où un bag permet
+ la duplications des éléments et n'a pas de colonne d'index, aucune clé primaire
+ ne peut être définie. Hibernate n'a aucun moyen de distinguer des enregistrements
+ dupliqués. Hibernate résout ce problème en supprimant complètement les
+ enregistrements (via un simple <literal>DELETE</literal>), puis en recréant
+ la collection chaque fois qu'elle change. Ce qui peut être très inefficace.
+ </para>
+
+ <para>
+ Notez que pour une relation un-vers-plusieurs, la "clé primaire"
+ peut ne pas être la clé primaire de la table en base de données -
+ mais même dans ce cas, la classification ci-dessus reste utile
+ (Elle explique comment Hibernate "localise" chaque enregistrement
+ de la collection).
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-collections-mostefficientupdate">
+ <title>Les lists, les maps, les idbags et les sets sont les collections les plus efficaces pour la mise à jour</title>
+
+ <para>
+ La discussion précédente montre clairement que les collections indexées
+ et (la plupart du temps) les sets, permettent de réaliser le plus efficacement
+ les opérations d'ajout, de suppression ou de modification d'éléments.
+ </para>
+
+ <para>
+ Il existe un autre avantage qu'ont les collections indexées sur les Sets
+ dans le cadre d'une association plusieurs vers plusieurs ou d'une collection de valeurs.
+ A cause de la structure inhérente d'un <literal>Set</literal>, Hibernate n'effectue jamais
+ d'<literal>UPDATE</literal> quand un enregistrement est modifié. Les modifications
+ apportées à un <literal>Set</literal> se font via un <literal>INSERT</literal> et <literal>DELETE</literal>
+ (de chaque enregistrement). Une fois de plus, ce cas ne s'applique pas aux associations
+ un vers plusieurs.
+ </para>
+
+ <para>
+ Après s'être rappelé que les tableaux ne peuvent pas être chargés tardivement,
+ nous pouvons conclure que les lists, les maps et les idbags sont les types de collections
+ (non inversées) les plus performants, avec les sets pas loin derrières.
+ Les sets son le type de collection le plus courant dans les applications Hibernate. Cela
+ est du au fait que la sémantique des "set" est la plus naturelle dans le modèle
+ relationnel.
+ </para>
+
+ <para>
+ Cependant, dans des modèles objet bien conçus avec Hibernate, on voit souvent que
+ la plupart des collections sont en fait des associations "un-vers-plusieurs" avec
+ <literal>inverse="true"</literal>. Pour ces associations, les mises à jour sont gérées
+ au niveau de l'association "plusieurs-vers-un" et les considérations de performance de
+ mise à jour des collections ne s'appliquent tout simplement pas dans ces cas là.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-collections-mostefficentinverse">
+ <title>Les Bags et les lists sont les plus efficaces pour les collections inverse</title>
+
+ <para>
+ Avant que vous n'oubliez les bags pour toujours, il y a un cas précis où les bags
+ (et les lists) sont bien plus performants que les sets. Pour une collection marquée
+ comme <literal>inverse="true"</literal> (le choix le plus courant pour un relation
+ un vers plusieurs bidirectionnelle), nous pouvons ajouter des éléments à un bag
+ ou une list sans avoir besoin de l'initialiser (fetch) les éléments du sac!
+ Ceci parce que <literal>Collection.add()</literal> ou <literal>Collection.addAll()</literal>
+ doit toujours retourner vrai pour un bag ou une <literal>List</literal>
+ (contrairement au <literal>Set</literal>).
+ Cela peut rendre le code suivant beaucoup plus rapide.
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) sess.load(Parent.class, id);
+ Child c = new Child();
+ c.setParent(p);
+ p.getChildren().add(c); //pas besoin de charger la collection !
+ sess.flush();]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="performance-collections-oneshotdelete">
+ <title>Suppression en un coup</title>
+
+ <para>
+ Parfois, effacer les éléments d'une collection un par un peut être extrêmement inefficace.
+ Hibernate n'est pas totalement stupide, il sait qu'il ne faut pas le faire dans le cas d'une
+ collection complètement vidée (lorsque vous appellez <literal>list.clear()</literal>, par exemple).
+ Dans ce cas, Hibernate fera un simple <literal>DELETE</literal> et le travail est fait !
+ </para>
+
+ <para>
+ Supposons que nous ajoutions un élément dans une collection de taille vingt et que nous
+ enlevions ensuite deux éléments. Hibernate effectuera un <literal>INSERT</literal> puis
+ deux <literal>DELETE</literal> (à moins que la collection ne soit un bag). Ce qui est
+ souhaitable.
+ </para>
+
+ <para>
+ Cependant, supposons que nous enlevions dix huit éléments, laissant ainsi deux éléments, puis
+ que nous ajoutions trois nouveaux éléments. Il y a deux moyens de procéder.
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>effacer dix huit enregistrements un à un puis en insérer trois</para>
+ </listitem>
+ <listitem>
+ <para>effacer la totalité de la collection (en un <literal>DELETE</literal> SQL) puis insérer
+ les cinq éléments restant un à un</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Hibernate n'est pas assez intelligent pour savoir que, dans ce cas, la seconde méthode est plus
+ rapide (Il plutôt heureux qu'Hibernate ne soit pas trop intelligent ; un tel comportement
+ pourrait rendre l'utilisation de triggers de bases de données plutôt aléatoire, etc...).
+ </para>
+
+ <para>
+ Heureusement, vous pouvez forcer ce comportement lorsque vous le souhaitez, en liberant
+ (c'est-à-dire en déréférençant) la collection initiale et en retournant une collection
+ nouvellement instanciée avec les éléments restants. Ceci peut être très pratique et
+ très puissant de temps en temps.
+ </para>
+
+ <para>
+ Bien sûr, la suppression en un coup ne s'applique pas pour les collections qui sont mappées
+ avec <literal>inverse="true"</literal>.
+ </para>
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="performance-monitoring" revision="1">
+ <title>Moniteur de performance</title>
+
+ <para>
+ L'optimisation n'est pas d'un grand intérêt sans le suivi et l'accès aux données de
+ performance. Hibernate fournit toute une panoplie de rapport sur ses opérations internes.
+ Les statistiques dans Hibernate sont fournies par <literal>SessionFactory</literal>.
+ </para>
+
+ <sect2 id="performance-monitoring-sf" revision="2">
+ <title>Suivi d'une SessionFactory</title>
+
+ <para>
+ Vous pouvez accéder au métriques d'une <literal>SessionFactory</literal> de deux
+ manières. La première option est d'appeler <literal>sessionFactory.getStatistics()</literal>
+ et de lire ou d'afficher les <literal>Statistics</literal> vous même.
+ </para>
+
+ <para>
+ Hibernate peut également utiliser JMX pour publier les métriques si vous activez
+ le MBean <literal>StatisticsService</literal>. Vous pouvez activer un seul MBean
+ pour toutes vos <literal>SessionFactory</literal> ou un par factory. Voici un code
+ qui montre un exemple de configuration minimaliste :
+ </para>
+
+ <programlisting><![CDATA[// MBean service registration for a specific SessionFactory
+Hashtable tb = new Hashtable();
+tb.put("type", "statistics");
+tb.put("sessionFactory", "myFinancialApp");
+ObjectName on = new ObjectName("hibernate", tb); // MBean object name
+
+StatisticsService stats = new StatisticsService(); // MBean implementation
+stats.setSessionFactory(sessionFactory); // Bind the stats to a SessionFactory
+server.registerMBean(stats, on); // Register the Mbean on the server]]></programlisting>
+
+
+<programlisting><![CDATA[// MBean service registration for all SessionFactory's
+Hashtable tb = new Hashtable();
+tb.put("type", "statistics");
+tb.put("sessionFactory", "all");
+ObjectName on = new ObjectName("hibernate", tb); // MBean object name
+
+StatisticsService stats = new StatisticsService(); // MBean implementation
+server.registerMBean(stats, on); // Register the MBean on the server]]></programlisting>
+
+ <para>
+ TODO: Cela n'a pas de sens : dans le premier cs on récupère et on utilise le MBean directement.
+ Dans le second, on doit fournir le nom JNDI sous lequel est retenu la fabrique de session avant de
+ l'utiliser. Pour cela il faut utiliser
+ <literal>hibernateStatsBean.setSessionFactoryJNDIName("my/JNDI/Name")</literal>
+ </para>
+ <para>
+ Vous pouvez (dés)activer le suivi pour une <literal>SessionFactory</literal>
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ au moment de la configuration en mettant <literal>hibernate.generate_statistics</literal> à <literal>false</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+ <itemizedlist>
+ <listitem>
+ <para>
+ à chaud avec <literal>sf.getStatistics().setStatisticsEnabled(true)</literal>
+ ou <literal>hibernateStatsBean.setStatisticsEnabled(true)</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Les statistiques peuvent être remises à zéro de manière programmatique à l'aide de la méthode
+ <literal>clear()</literal>
+ Un résumé peut être envoyé à un logger (niveau info) à l'aide de la méthode <literal>logSummary()</literal>
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-monitoring-metrics" revision="1">
+ <title>Métriques</title>
+
+ <para>
+ Hibernate fournit un certain nombre de métriques, qui vont des informations très basiques
+ aux informations très spécialisées qui ne sont appropriées que dans certains scenarii.
+ Tous les compteurs accessibles sont décrits dans l'API de l'interface
+ <literal>Statistics</literal> dans trois catégories :
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ Les métriques relatives à l'usage général de la <literal>Session</literal>
+ comme le nombre de sessions ouvertes, le nombre de connexions JDBC récupérées, etc...
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Les métriques relatives aux entités, collections, requêtes et caches dans
+ leur ensemble (métriques globales),
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Les métriques détaillées relatives à une entité, une collection, une requête
+ ou une région de cache particulière.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Par exemple, vous pouvez vérifier l'accès au cache ainsi que le taux d'éléments manquants et
+ de mise à jour des entités, collections et requêtes et le temps moyen que met une requête.
+ Il faut faire attention au fait que le nombre de millisecondes est sujet à approximation en
+ Java. Hibernate est lié à la précision de la machine virtuelle, sur certaines plateformes,
+ cela n'offre qu'une précision de l'ordre de 10 secondes.
+ </para>
+
+ <para>
+ Des accesseurs simples sont utilisés pour accéder aux métriques globales (e.g. celles qui ne
+ sont pas liées à une entité, collection ou région de cache particulière). Vous pouvez accéder
+ aux métriques d'une entité, collection, région de cache particulière à l'aide de son nom et à l'aide
+ de sa représentation HQL ou SQL pour une requête. Référez vous à la javadoc des APIS
+ <literal>Statistics</literal>, <literal>EntityStatistics</literal>,
+ <literal>CollectionStatistics</literal>, <literal>SecondLevelCacheStatistics</literal>,
+ and <literal>QueryStatistics</literal> pour plus d'informations. Le code ci-dessous montre
+ un exemple simple :
+ </para>
+
+ <programlisting><![CDATA[Statistics stats = HibernateUtil.sessionFactory.getStatistics();
+
+double queryCacheHitCount = stats.getQueryCacheHitCount();
+double queryCacheMissCount = stats.getQueryCacheMissCount();
+double queryCacheHitRatio =
+ queryCacheHitCount / (queryCacheHitCount + queryCacheMissCount);
+
+log.info("Query Hit ratio:" + queryCacheHitRatio);
+
+EntityStatistics entityStats =
+ stats.getEntityStatistics( Cat.class.getName() );
+long changes =
+ entityStats.getInsertCount()
+ + entityStats.getUpdateCount()
+ + entityStats.getDeleteCount();
+log.info(Cat.class.getName() + " changed " + changes + "times" );]]></programlisting>
+
+ <para>
+ Pour travailler sur toutes les entités, collections, requêtes et régions de cache, vous pouvez
+ récupérer la liste des noms des entités, collections, requêtes et régions de cache avec les
+ méthodes : <literal>getQueries()</literal>, <literal>getEntityNames()</literal>,
+ <literal>getCollectionRoleNames()</literal>, et
+ <literal>getSecondLevelCacheRegionNames()</literal>.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+</chapter>
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/persistent_classes.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/persistent_classes.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/persistent_classes.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,536 @@
+<?xml version='1.0' encoding="iso-8859-1"?>
+<chapter id="persistent-classes" revision="2">
+ <title>Classes persistantes</title>
+
+ <para>
+ Les classes persistantes sont les classes d'une application qui implémentent
+ les entités d'un problème métier (ex. Client et Commande dans une application
+ de commerce électronique).
+ Toutes les instances d'une classe persistante ne sont pas forcément
+ dans l'état persistant - au lieu de cela, une instance peut être éphémère (NdT : transient) ou détachée.
+ </para>
+
+ <para>
+ Hibernate fonctionne de manière optimale lorsque ces classes suivent quelques règles
+ simples, aussi connues comme le modèle de programmation Plain Old Java Object
+ (POJO). Cependant, aucune de ces règles ne sont des besoins absolus. En effet, Hibernate3 suppose très peu de choses à propos
+ de la nature de vos objets persistants. Vous pouvez exprimer un modèle de domaine par d'autres moyens : utiliser des arbres
+ d'instances de <literal>Map</literal>, par exemple.
+ </para>
+
+ <sect1 id="persistent-classes-pojo">
+ <title>Un exemple simple de POJO</title>
+
+ <para>
+ Toute bonne application Java nécessite une classe persistante
+ représentant les félins.
+ </para>
+
+ <programlisting><![CDATA[package eg;
+import java.util.Set;
+import java.util.Date;
+
+public class Cat {
+ private Long id; // identifier
+
+ private Date birthdate;
+ private Color color;
+ private char sex;
+ private float weight;
+ private int litterId;
+
+ private Cat mother;
+ private Set kittens = new HashSet();
+
+ private void setId(Long id) {
+ this.id=id;
+ }
+ public Long getId() {
+ return id;
+ }
+
+ void setBirthdate(Date date) {
+ birthdate = date;
+ }
+ public Date getBirthdate() {
+ return birthdate;
+ }
+
+ void setWeight(float weight) {
+ this.weight = weight;
+ }
+ public float getWeight() {
+ return weight;
+ }
+
+ public Color getColor() {
+ return color;
+ }
+ void setColor(Color color) {
+ this.color = color;
+ }
+
+ void setSex(char sex) {
+ this.sex=sex;
+ }
+ public char getSex() {
+ return sex;
+ }
+
+ void setLitterId(int id) {
+ this.litterId = id;
+ }
+ public int getLitterId() {
+ return litterId;
+ }
+
+ void setMother(Cat mother) {
+ this.mother = mother;
+ }
+ public Cat getMother() {
+ return mother;
+ }
+ void setKittens(Set kittens) {
+ this.kittens = kittens;
+ }
+ public Set getKittens() {
+ return kittens;
+ }
+
+ // addKitten not needed by Hibernate
+ public void addKitten(Cat kitten) {
+ kitten.setMother(this);
+ kitten.setLitterId( kittens.size() );
+ kittens.add(kitten);
+ }
+}]]></programlisting>
+
+ <para>
+ Il y a quatre règles à suivre ici :
+ </para>
+
+
+ <sect2 id="persistent-classes-pojo-constructor" revision="1">
+ <title>Implémenter un constructeur sans argument</title>
+
+ <para>
+ <literal>Cat</literal> a un constructeur sans argument. Toutes les classes persistantes doivent avoir un
+ constructeur par défaut (lequel peut ne pas être public) pour qu'Hibernate puissent les instancier en utilisant
+ <literal>Constructor.newInstance()</literal>. Nous recommandons fortement d'avoir un constructeur par défaut avec
+ au moins une visibilité <emphasis>paquet</emphasis> pour la génération du proxy à l'exécution dans Hibernate.
+ </para>
+ </sect2>
+
+ <sect2 id="persistent-classes-pojo-identifier" revision="2">
+ <title>Fournir une propriété d'indentifiant (optionnel)</title>
+
+ <para>
+ <literal>Cat</literal> possède une propriété appelée <literal>id</literal>.
+ Cette propriété mappe la valeur de la colonne de clé primaire de la table
+ d'une base de données.La propriété aurait pu s'appeler complètement autrement,
+ et son type aurait pu être n'importe quel type primitif, n'importe quel "encapsuleur"
+ de type primitif, <literal>java.lang.String</literal> ou <literal>java.util.Date</literal>.
+ (Si votre base de données héritée possède des clés composites, elles peuvent être mappées
+ en utilisant une classe définie par l'utilisateur et possédant les propriétés associées aux
+ types de la clé composite - voir la section concernant les identifiants composites plus tard).
+ </para>
+
+ <para>
+ La propriété d'identifiant est strictement optionnelle. Vous pouver l'oublier et laisser Hibernate
+ s'occuper des identifiants de l'objet en interne. Toutefois, nous ne le recommandons pas.
+ </para>
+
+ <para>
+ En fait, quelques fonctionnalités ne sont disponibles que pour les classes
+ déclarant un identifiant de propriété :
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ Les réattachements transitifs pour les objets détachés (mise à jour en cascade ou fusion en cascade) -
+ voir <xref linkend="objectstate-transitive"/>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>Session.saveOrUpdate()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>Session.merge()</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Nous recommandons que vous déclariez les propriétés d'identifiant de manière
+ uniforme. Nous recommandons également que vous utilisiez un type nullable
+ (ie. non primitif).
+ </para>
+ </sect2>
+
+ <sect2 id="persistent-classes-pojo-final">
+ <title>Favoriser les classes non finales (optionnel)</title>
+ <para>
+ Une fonctionnalité clef d'Hibernate, les <emphasis>proxies</emphasis>, nécessitent
+ que la classe persistente soit non finale ou qu'elle soit l'implémentation d'une
+ interface qui déclare toutes les méthodes publiques.
+ </para>
+ <para>
+ Vous pouvez persister, grâce à Hibernate, les classes <literal>final</literal>
+ qui n'implémentent pas d'interface, mais vous ne pourrez pas utiliser les proxies pour les chargements d'associations paresseuses
+ - ce qui limitera vos possibilités d'ajustement des performances.
+ </para>
+ <para>
+ Vous devriez aussi éviter de déclarer des méthodes <literal>public final</literal> sur des classes
+ non-finales. Si vous voulez utiliser une classe avec une méthode <literal>public final</literal>, vous devez
+ explicitement désactiver les proxies en paramétrant
+ <literal>lazy="false"</literal>.
+ </para>
+ </sect2>
+
+ <sect2 id="persistent-classes-pojo-accessors" revision="2">
+ <title>Déclarer les accesseurs et mutateurs des attributs persistants (optionnel)</title>
+
+ <para>
+ <literal>Cat</literal> déclare des mutateurs pour toutes ses champs persistants. Beaucoup d'autres
+ solutions de mapping Objet/relationnel persistent directement les variables d'instance. Nous pensons
+ qu'il est bien mieux de fournir une indirection entre le schéma relationnel et les structures de données internes de la classe.
+ Par défaut, Hibernate persiste les propriétés suivant le style JavaBean, et reconnaît les noms de méthodes de la forme <literal>
+ getFoo</literal>, <literal>isFoo</literal> et
+ <literal>setFoo</literal>. Nous pouvons changer pour un accès direct aux champs pour des propriétés particulières, si besoin est.
+ </para>
+
+ <para>
+ Les propriétés <emphasis>n'ont pas</emphasis> à être déclarées publiques -
+ Hibernate peut persister une propriété avec un paire de getter/setter de
+ visibilité par défault, <literal>protected</literal> ou <literal>
+ private</literal>.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="persistent-classes-inheritance">
+ <title>Implémenter l'héritage</title>
+
+ <para>
+ Une sous-classe doit également suivre la première et la seconde règle.
+ Elle hérite sa propriété d'identifiant de <literal>Cat</literal>.
+ </para>
+
+ <programlisting><![CDATA[package eg;
+
+public class DomesticCat extends Cat {
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+ protected void setName(String name) {
+ this.name=name;
+ }
+}]]></programlisting>
+ </sect1>
+
+ <sect1 id="persistent-classes-equalshashcode" revision="1">
+ <title>Implémenter <literal>equals()</literal> et <literal>hashCode()</literal></title>
+
+ <para>
+ Vous devez surcharger les méthodes <literal>equals()</literal> et
+ <literal>hashCode()</literal> si vous
+ </para>
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ avez l'intention de mettre des instances de classes persistantes dans un <literal>Set</literal>
+ (la manière recommandée pour représenter des associations pluri-valuées)
+ <emphasis>et</emphasis>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ avez l'intention d'utiliser le réattachement d'instances détachées
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Hibernate garantit l'équivalence de l'identité persistante (ligne de base de données) et l'identité Java seulement
+ à l'intérieur de la portée d'une session particulière. Donc dès que nous mélangeons des instances venant de différentes
+ sessions, nous devons implémenter <literal>equals()</literal> et
+ <literal>hashCode()</literal> si nous souhaitons avoir une sémantique correcte pour les <literal>Set</literal>s.
+ </para>
+
+ <para>
+ La manière la plus évidente est d'implémenter <literal>equals()</literal>/<literal>hashCode()</literal>
+ en comparant la valeur de l'identifiant des deux objets. Si cette valeur est identique, les deux
+ doivent représenter la même ligne de base de données, ils sont donc égaux (si les deux sont
+ ajoutés à un <literal>Set</literal>, nous n'aurons qu'un seul élément dans le
+ <literal>Set</literal>). Malheureusement, nous ne pouvons pas utiliser cette approche avec
+ des identifiants générés ! Hibernate n'assignera de
+ valeur d'identifiant qu'aux objets qui sont persistants, une instance nouvellement créée n'aura
+ donc pas de valeur d'identifiant ! De plus, si une instance est non sauvegardée et actuellement dans un <literal>Set</literal>,
+ le sauvegarder assignera une valeur d'identifiant à l'objet. Si <literal>equals()</literal> et <literal>hashCode()</literal>
+ sont basées sur la valeur de l'identifiant, le code de hachage devrait changer, rompant le contrat du <literal>Set</literal>.
+ Regardez sur le site web d'Hibernate pour une discussion complète de ce problème.
+ Notez que ceci n'est pas un problème d'Hibernate, mais la sémantique normale de Java pour l'identité d'un objet et l'égalité.
+ </para>
+
+ <para>
+ Nous recommandons donc d'implémenter
+ <literal>equals()</literal> et <literal>hashCode()</literal> en utilisant <emphasis>
+ l'égalité par clé métier</emphasis>.L'égalité par clé métier signifie que la méthode <literal>equals()</literal>
+ compare uniquement les propriétés qui forment une clé métier, une clé qui
+ identifierait notre instance dans le monde réel (une clé candidate
+ <emphasis>naturelle</emphasis>) :
+ </para>
+
+ <programlisting><![CDATA[public class Cat {
+
+ ...
+ public boolean equals(Object other) {
+ if (this == other) return true;
+ if ( !(other instanceof Cat) ) return false;
+
+ final Cat cat = (Cat) other;
+
+ if ( !cat.getLitterId().equals( getLitterId() ) ) return false;
+ if ( !cat.getMother().equals( getMother() ) ) return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ int result;
+ result = getMother().hashCode();
+ result = 29 * result + getLitterId();
+ return result;
+ }
+
+}]]></programlisting>
+
+ <para>
+ Notez qu'une clef métier ne doit pas être solide comme une clef primaire de base de données
+ (voir <xref linkend="transactions-basics-identity"/>). Les propriétés
+ immuables ou uniques sont généralement de bonnes candidates pour une clef métier.
+ </para>
+
+ </sect1>
+
+ <sect1 id="persistent-classes-dynamicmodels">
+ <title>Modèles dynamiques</title>
+
+ <para>
+ <emphasis>Notez que la fonctionnalités suivantes sont actuellement considérées
+ comme expérimentales et peuvent changer dans un futur proche.</emphasis>
+ </para>
+
+ <para>
+ Les entités persistantes ne doivent pas nécessairement être représentées comme
+ des classes POJO ou des objets JavaBean à l'exécution. Hibernate supporte aussi les
+ modèles dynamiques (en utilisant des <literal>Map</literal>s de <literal>Map</literal>s
+ à l'exécution) et la représentation des entités comme des arbres DOM4J. Avec cette
+ approche, vous n'écrivez pas de classes persistantes, seulement des fichiers de mapping.
+ </para>
+
+ <para>
+ Par défaut, Hibernate fonctionne en mode POJO normal. Vous pouvez paramétrer
+ un mode de représentation d'entité par défaut pour une <literal>SessionFactory</literal>
+ particulière en utilisant l'option de configuration <literal>default_entity_mode</literal>
+ (voir <xref linkend="configuration-optional-properties"/>).
+ </para>
+
+ <para>
+ Les exemples suivants démontrent la représentation utilisant des <literal>Map</literal>s.
+ D'abord, dans le fichier de mapping, un <literal>entity-name</literal> doit être déclaré
+ au lieu (ou en plus) d'un nom de classe :
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class entity-name="Customer">
+
+ <id name="id"
+ type="long"
+ column="ID">
+ <generator class="sequence"/>
+ </id>
+
+ <property name="name"
+ column="NAME"
+ type="string"/>
+
+ <property name="address"
+ column="ADDRESS"
+ type="string"/>
+
+ <many-to-one name="organization"
+ column="ORGANIZATION_ID"
+ class="Organization"/>
+
+ <bag name="orders"
+ inverse="true"
+ lazy="false"
+ cascade="all">
+ <key column="CUSTOMER_ID"/>
+ <one-to-many class="Order"/>
+ </bag>
+
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Notez que même si des associations sont déclarées en utilisant des noms de classe cible,
+ le type de cible d'une association peut aussi être une entité dynamique au lieu d'un POJO.
+ </para>
+
+ <para>
+ Après avoir configuré le mode d'entité par défaut à <literal>dynamic-map</literal>
+ pour la <literal>SessionFactory</literal>, nous pouvons lors de l'exécution fonctionner
+ avec des <literal>Map</literal>s de <literal>Map</literal>s :
+ </para>
+
+ <programlisting><![CDATA[Session s = openSession();
+Transaction tx = s.beginTransaction();
+Session s = openSession();
+
+// Create a customer
+Map david = new HashMap();
+david.put("name", "David");
+
+// Create an organization
+Map foobar = new HashMap();
+foobar.put("name", "Foobar Inc.");
+
+// Link both
+david.put("organization", foobar);
+
+// Save both
+s.save("Customer", david);
+s.save("Organization", foobar);
+
+tx.commit();
+s.close();]]></programlisting>
+
+ <para>
+ Les avantages d'un mapping dynamique sont un gain de temps pour le prototypage
+ sans la nécessité d'implémenter les classes d'entité. Pourtant, vous perdez la
+ vérification du typage au moment de la compilation et aurez plus d'exceptions à
+ gérer lors de l'exécution. Grâce au mapping d'Hibernate, le schéma de la base de
+ données peut facilement être normalisé et solidifié, permettant de rajouter une
+ implémentation propre du modèle de domaine plus tard.
+ </para>
+
+ <para>
+ Les modes de représentation d'une entité peut aussi être configuré par <literal>Session</literal> :
+ </para>
+
+ <programlisting><![CDATA[Session dynamicSession = pojoSession.getSession(EntityMode.MAP);
+
+// Create a customer
+Map david = new HashMap();
+david.put("name", "David");
+dynamicSession.save("Customer", david);
+...
+dynamicSession.flush();
+dynamicSession.close()
+...
+// Continue on pojoSession
+]]></programlisting>
+
+
+ <para>
+ Veuillez noter que l'appel à <literal>getSession()</literal> en utilisant un
+ <literal>EntityMode</literal> se fait sur l'API <literal>Session</literal>, pas
+ <literal>SessionFactory</literal>. De cette manière, la nouvelle <literal>Session</literal>
+ partage les connexions JDBC, transactions et autres informations de contexte sous-jacentes.
+ Cela signifie que vous n'avez pas à appeler <literal>flush()</literal> et <literal>close()</literal>
+ sur la <literal>Session</literal> secondaire, et laissez aussi la gestion de la transaction
+ et de la connexion à l'unité de travail primaire.
+ </para>
+
+ <para>
+ Plus d'informations à propos de la représentation XML peuvent être trouvées dans
+ <xref linkend="xml"/>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="persistent-classes-tuplizers" revision="0">
+ <title>Tuplizers</title>
+
+ <para>
+ <literal>org.hibernate.tuple.Tuplizer</literal>, et ses sous-interfaces, sont responsables
+ de la gestion d'une représentation particulière d'un morceau de données, en fonction du
+ <literal>org.hibernate.EntityMode</literal> de réprésentation. Si un morceau donné de données
+ est pensé comme une structure de données, alors un tuplizer est la chose qui sait comment
+ créer une telle structure de données, comment extraire des valeurs et injecter des valeurs dans
+ une telle structure de données. Par exemple, pour le mode d'entité POJO, le tuplizer correspondant
+ sait comment créer le POJO à travers son constructeur et comment accéder aux propriétés du POJO
+ utilisant les accesseurs de la propriété définie. Il y a deux types de Tuplizers haut niveau,
+ représenté par les interfaces <literal>org.hibernate.tuple.EntityTuplizer</literal> et
+ <literal>org.hibernate.tuple.ComponentTuplizer</literal>. Les <literal>EntityTuplizer</literal>s
+ sont responsables de la gestion des contrats mentionnés ci-dessus pour les entités, alors que
+ les <literal>ComponentTuplizer</literal>s s'occupent des composants.
+ </para>
+
+ <para>
+ Les utilisateurs peuvent aussi brancher leurs propres tuplizers. Peut-être vous est-il nécessaire qu'une
+ implémentation de <literal>java.util.Map</literal> autre que <literal>java.util.HashMap</literal>
+ soit utilisée dans le mode d'entité dynamic-map ; ou peut-être avez-vous besoin de définir une
+ statégie de génération de proxy différente de celle utilisée par défaut. Les deux devraient être
+ effectuées en définissant une implémentation de tuplizer utilisateur. Les définitions de tuplizers
+ sont attachées au mapping de l'entité ou du composant qu'ils sont censés gérer. Retour à l'exemple de
+ notre entité utilisateur :
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+ <class entity-name="Customer">
+ <!--
+ Override the dynamic-map entity-mode
+ tuplizer for the customer entity
+ -->
+ <tuplizer entity-mode="dynamic-map"
+ class="CustomMapTuplizerImpl"/>
+
+ <id name="id" type="long" column="ID">
+ <generator class="sequence"/>
+ </id>
+
+ <!-- other properties -->
+ ...
+ </class>
+</hibernate-mapping>
+
+
+public class CustomMapTuplizerImpl
+ extends org.hibernate.tuple.DynamicMapEntityTuplizer {
+ // override the buildInstantiator() method to plug in our custom map...
+ protected final Instantiator buildInstantiator(
+ org.hibernate.mapping.PersistentClass mappingInfo) {
+ return new CustomMapInstantiator( mappingInfo );
+ }
+
+ private static final class CustomMapInstantiator
+ extends org.hibernate.tuple.DynamicMapInstantitor {
+ // override the generateMap() method to return our custom map...
+ protected final Map generateMap() {
+ return new CustomMap();
+ }
+ }
+}]]></programlisting>
+
+
+ </sect1>
+
+ <para>
+ TODO: Document user-extension framework in the property and proxy packages
+ </para>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/query_criteria.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/query_criteria.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/query_criteria.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,443 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<chapter id="querycriteria">
+ <title>Requêtes par critères</title>
+
+ <para>
+ Hibernate offre une API d'interrogation par critères intuitive et extensible.
+ </para>
+
+ <sect1 id="querycriteria-creating">
+ <title>Créer une instance de <literal>Criteria</literal></title>
+
+ <para>
+ L'interface <literal>net.sf.hibernate.Criteria</literal> représente une requête sur une
+ classe persistente donnée. La <literal>Session</literal> fournit les instances de
+ <literal>Criteria</literal>.
+ </para>
+
+ <programlisting><![CDATA[Criteria crit = sess.createCriteria(Cat.class);
+crit.setMaxResults(50);
+List cats = crit.list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-narrowing">
+ <title>Restriction du résultat</title>
+
+ <para>
+ Un criterion (critère de recherche) est une instance de l'interface
+ <literal>org.hibernate.criterion.Criterion</literal>. La classe
+ <literal>org.hibernate.criterion.Restrictions</literal> définit
+ des méthodes pour obtenir des types de <literal>Criterion</literal>
+ pré-définis.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "Fritz%") )
+ .add( Restrictions.between("weight", minWeight, maxWeight) )
+ .list();]]></programlisting>
+
+ <para>
+ Les restrictions peuvent être goupées de manière logique.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "Fritz%") )
+ .add( Restrictions.or(
+ Restrictions.eq( "age", new Integer(0) ),
+ Restrictions.isNull("age")
+ ) )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.in( "name", new String[] { "Fritz", "Izi", "Pk" } ) )
+ .add( Restrictions.disjunction()
+ .add( Restrictions.isNull("age") )
+ .add( Restrictions.eq("age", new Integer(0) ) )
+ .add( Restrictions.eq("age", new Integer(1) ) )
+ .add( Restrictions.eq("age", new Integer(2) ) )
+ ) )
+ .list();]]></programlisting>
+
+ <para>
+ Il y a plusieurs types de criterion pré-définis (sous classes de <literal>Restriction</literal>),
+ mais l'une d'entre elle particulièrement utile vous permet de spécifier directement
+ du SQL.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.sql("lower({alias}.name) like lower(?)", "Fritz%", Hibernate.STRING) )
+ .list();]]></programlisting>
+
+ <para>
+ La zone <literal>{alias}</literal> sera remplacée par l'alias de colonne de l'entité
+ que l'on souhaite intérroger.
+ </para>
+
+ <para>
+ Une autre approche pour obtenir un criterion est de le récupérer d'une instance de <literal>Property</literal>.
+ Vous pouvez créer une <literal>Property</literal> en appelant <literal>Property.forName()</literal>.
+ </para>
+
+ <programlisting><![CDATA[
+Property age = Property.forName("age");
+List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.disjunction()
+ .add( age.isNull() )
+ .add( age.eq( new Integer(0) ) )
+ .add( age.eq( new Integer(1) ) )
+ .add( age.eq( new Integer(2) ) )
+ ) )
+ .add( Property.forName("name").in( new String[] { "Fritz", "Izi", "Pk" } ) )
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-ordering">
+ <title>Trier les résultats</title>
+
+ <para>
+ Vous pouvez trier les résultats en utilisant <literal>org.hibernate.criterion.Order</literal>.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "F%")
+ .addOrder( Order.asc("name") )
+ .addOrder( Order.desc("age") )
+ .setMaxResults(50)
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Property.forName("name").like("F%") )
+ .addOrder( Property.forName("name").asc() )
+ .addOrder( Property.forName("age").desc() )
+ .setMaxResults(50)
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-associations" revision="2">
+ <title>Associations</title>
+
+ <para>
+ Vous pouvez facilement spécifier des contraintes sur des entités liées,
+ par des associations en utilisant <literal>createCriteria()</literal>.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "F%")
+ .createCriteria("kittens")
+ .add( Restrictions.like("name", "F%")
+ .list();]]></programlisting>
+
+ <para>
+ Notez que la seconde <literal>createCriteria()</literal> retourne une nouvelle
+ instance de <literal>Criteria</literal>, qui se rapporte aux éléments de la
+ collection <literal>kittens</literal>.
+ </para>
+
+ <para>
+ La forme alternative suivante est utile dans certains cas.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .createAlias("kittens", "kt")
+ .createAlias("mate", "mt")
+ .add( Restrictions.eqProperty("kt.name", "mt.name") )
+ .list();]]></programlisting>
+
+ <para>
+ (<literal>createAlias()</literal> ne crée pas de nouvelle instance de
+ <literal>Criteria</literal>.)
+ </para>
+
+ <para>
+ Notez que les collections kittens contenues dans les instances de <literal>Cat</literal>
+ retournées par les deux précédentes requêtes ne sont <emphasis>pas</emphasis> pré-filtrées
+ par les critères ! Si vous souhaitez récupérer uniquement les kittens qui correspondent à la
+ criteria, vous devez utiliser <literal>ResultTransformer</literal>.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .createCriteria("kittens", "kt")
+ .add( Restrictions.eq("name", "F%") )
+ .setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP)
+ .list();
+Iterator iter = cats.iterator();
+while ( iter.hasNext() ) {
+ Map map = (Map) iter.next();
+ Cat cat = (Cat) map.get(Criteria.ROOT_ALIAS);
+ Cat kitten = (Cat) map.get("kt");
+}]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-dynamicfetching" revision="1">
+ <title>Peuplement d'associations de manière dynamique</title>
+
+ <para>
+ Vous pouvez spéficier au moment de l'exécution le peuplement d'une association en utilisant
+ <literal>setFetchMode()</literal> (c'est-à-dire le chargement de celle-ci).
+ Cela permet de surcharger les valeurs
+ "lazy" et "outer-join" du mapping.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "Fritz%") )
+ .setFetchMode("mate", FetchMode.EAGER)
+ .setFetchMode("kittens", FetchMode.EAGER)
+ .list();]]></programlisting>
+
+ <para>
+ Cette requête recherchera <literal>mate</literal> et <literal>kittens</literal>
+ via les jointures externes. Voir <xref linkend="performance-fetching"/> pour plus d'informations.
+ </para>
+
+ </sect1>
+
+ <sect1 id="querycriteria-examples">
+ <title>Requêtes par l'exemple</title>
+
+ <para>
+ La classe <literal>org.hibernate.criterion.Example</literal> vous permet de
+ construire un critère suivant une instance d'objet donnée.
+ </para>
+
+ <programlisting><![CDATA[Cat cat = new Cat();
+cat.setSex('F');
+cat.setColor(Color.BLACK);
+List results = session.createCriteria(Cat.class)
+ .add( Example.create(cat) )
+ .list();]]></programlisting>
+
+ <para>
+ Les propriétés de type version, identifiant et association sont ignorées.
+ Par défaut, les valeurs null sont exclues.
+ </para>
+
+ <para>
+ Vous pouvez ajuster la stratégie d'utilisation de valeurs de
+ l'<literal>Exemple</literal>.
+ </para>
+
+ <programlisting><![CDATA[Example example = Example.create(cat)
+ .excludeZeroes() //exclude zero valued properties
+ .excludeProperty("color") //exclude the property named "color"
+ .ignoreCase() //perform case insensitive string comparisons
+ .enableLike(); //use like for string comparisons
+List results = session.createCriteria(Cat.class)
+ .add(example)
+ .list();]]></programlisting>
+
+ <para>
+ Vous pouvez utiliser les "exemples" pour des critères sur les objets associés.
+ </para>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .add( Example.create(cat) )
+ .createCriteria("mate")
+ .add( Example.create( cat.getMate() ) )
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-projection">
+ <title>Projections, agrégation et regroupement</title>
+ <para>
+ La classe <literal>org.hibernate.criterion.Projections</literal> est une
+ fabrique d'instances de <literal>Projection</literal>. Nous appliquons une
+ projection sur une requête en appelant <literal>setProjection()</literal>.
+ </para>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.rowCount() )
+ .add( Restrictions.eq("color", Color.BLACK) )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.projectionList()
+ .add( Projections.rowCount() )
+ .add( Projections.avg("weight") )
+ .add( Projections.max("weight") )
+ .add( Projections.groupProperty("color") )
+ )
+ .list();]]></programlisting>
+
+ <para>
+ Il n'y a pas besoin de "group by" explicite dans une requête par critère.
+ Certains types de projection sont définis pour être des <emphasis>projections
+ de regroupement</emphasis>, lesquels apparaissent aussi dans la clause
+ <literal>group by</literal> SQL.
+ </para>
+
+ <para>
+ Un alias peut optionnellement être assigné à une projection, ainsi la valeur
+ projetée peut être référencée dans des restrictions ou des tris. Voici deux façons
+ différentes de faire ça :
+ </para>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.alias( Projections.groupProperty("color"), "colr" ) )
+ .addOrder( Order.asc("colr") )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.groupProperty("color").as("colr") )
+ .addOrder( Order.asc("colr") )
+ .list();]]></programlisting>
+
+ <para>
+ Les méthodes <literal>alias()</literal> et <literal>as()</literal> enveloppe simplement
+ une instance de projection dans une autre instance (aliasée) de <literal>Projection</literal>.
+ Comme un raccourci, vous pouvez assignez un alias lorsque vous ajoutez la projection à la
+ liste de projections :
+ </para>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.projectionList()
+ .add( Projections.rowCount(), "catCountByColor" )
+ .add( Projections.avg("weight"), "avgWeight" )
+ .add( Projections.max("weight"), "maxWeight" )
+ .add( Projections.groupProperty("color"), "color" )
+ )
+ .addOrder( Order.desc("catCountByColor") )
+ .addOrder( Order.desc("avgWeight") )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Domestic.class, "cat")
+ .createAlias("kittens", "kit")
+ .setProjection( Projections.projectionList()
+ .add( Projections.property("cat.name"), "catName" )
+ .add( Projections.property("kit.name"), "kitName" )
+ )
+ .addOrder( Order.asc("catName") )
+ .addOrder( Order.asc("kitName") )
+ .list();]]></programlisting>
+
+ <para>
+ Vous pouvez aussi utiliser <literal>Property.forName()</literal> pour formuler des projections :
+ </para>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Property.forName("name") )
+ .add( Property.forName("color").eq(Color.BLACK) )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.projectionList()
+ .add( Projections.rowCount().as("catCountByColor") )
+ .add( Property.forName("weight").avg().as("avgWeight") )
+ .add( Property.forName("weight").max().as("maxWeight") )
+ .add( Property.forName("color").group().as("color" )
+ )
+ .addOrder( Order.desc("catCountByColor") )
+ .addOrder( Order.desc("avgWeight") )
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-detachedqueries">
+ <title>Requêtes et sous-requêtes détachées</title>
+ <para>
+ La classe <literal>DetachedCriteria</literal> vous laisse créer une requête en dehors de la
+ portée de la session, et puis l'exécuter plus tard en utilisant n'importe quelle <literal>Session</literal>
+ arbitraire.
+ </para>
+
+ <programlisting><![CDATA[DetachedCriteria query = DetachedCriteria.forClass(Cat.class)
+ .add( Property.forName("sex").eq('F') );
+
+Session session = ....;
+Transaction txn = session.beginTransaction();
+List results = query.getExecutableCriteria(session).setMaxResults(100).list();
+txn.commit();
+session.close();]]></programlisting>
+
+ <para>
+ Une <literal>DetachedCriteria</literal> peut aussi être utilisée pour exprimer une
+ sous-requête. Des instances de criterion impliquant des sous-requêtes peuvent être
+ obtenues via <literal>Subqueries</literal> ou <literal>Property</literal>.
+ </para>
+
+ <programlisting><![CDATA[DetachedCriteria avgWeight = DetachedCriteria.forClass(Cat.class)
+ .setProjection( Property.forName("weight").avg() );
+session.createCriteria(Cat.class)
+ .add( Property.forName("weight).gt(avgWeight) )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[DetachedCriteria weights = DetachedCriteria.forClass(Cat.class)
+ .setProjection( Property.forName("weight") );
+session.createCriteria(Cat.class)
+ .add( Subqueries.geAll("weight", weights) )
+ .list();]]></programlisting>
+
+ <para>
+ Même des requêtes corrélées sont possibles :
+ </para>
+
+ <programlisting><![CDATA[DetachedCriteria avgWeightForSex = DetachedCriteria.forClass(Cat.class, "cat2")
+ .setProjection( Property.forName("weight").avg() )
+ .add( Property.forName("cat2.sex").eqProperty("cat.sex") );
+session.createCriteria(Cat.class, "cat")
+ .add( Property.forName("weight).gt(avgWeightForSex) )
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <!--TODO: ResultSetTransformer + aliasing. AliasToBeanTransformer allow returning arbitrary
+ user objects - similar to setResultClass in JDO2. General use of ResultTransformer
+ could also be explained. -->
+
+ <sect1 id="query-criteria-naturalid">
+ <title>Requêtes par identifiant naturel</title>
+
+ <para>
+ Pour la plupart des requêtes, incluant les requêtes par critère, le cache de requêtes
+ n'est pas très efficace, parce que l'invalidation du cache de requêtes arrive trop
+ souvent. Cependant, il y a une sorte spéciale de requête où nous pouvons optimiser
+ l'algorithme d'invalidation du cache : les recherches sur une clef naturelle constante.
+ Dans certaines applications, cette sorte de requête se produit fréquemment. L'API de
+ critère fournit une provision spéciale pour ce cas d'utilisation.
+ </para>
+
+ <para>
+ D'abord vous devriez mapper la clef naturelle de votre entité en utilisant
+ <literal><natural-id></literal>, et activer l'utilisation du cache de second niveau.
+ </para>
+
+ <programlisting><![CDATA[<class name="User">
+ <cache usage="read-write"/>
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <natural-id>
+ <property name="name"/>
+ <property name="org"/>
+ </natural-id>
+ <property name="password"/>
+</class>]]></programlisting>
+
+ <para>
+ Notez que cette fonctionnalité n'est pas prévue pour l'utilisation avec des
+ entités avec des clefs naturelles <emphasis>mutables</emphasis>.
+ </para>
+
+ <para>
+ Ensuite, activez le cache de requête d'Hibernate.
+ </para>
+
+ <para>
+ Maintenant <literal>Restrictions.naturalId()</literal> nous permet de rendre
+ l'utilisation de l'algorithme de cache plus efficace.
+ </para>
+
+ <programlisting><![CDATA[session.createCriteria(User.class)
+ .add( Restrictions.naturalId()
+ .set("name", "gavin")
+ .set("org", "hb")
+ ).setCacheable(true)
+ .uniqueResult();]]></programlisting>
+
+ </sect1>
+
+</chapter>
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/query_hql.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/query_hql.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/query_hql.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1149 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<chapter id="queryhql">
+ <title>HQL: Langage de requêtage d'Hibernate</title>
+
+ <para>
+ Hibernate fourni un langage d'interrogation extrêmement puissant qui
+ ressemble (et c'est voulu) au SQL. Mais ne soyez pas distraits par la syntaxe ;
+ HQL est totalement orienté objet, comprenant des notions d'héritage, de
+ polymorphisme et d'association.
+ </para>
+
+ <sect1 id="queryhql-casesensitivity">
+ <title>Sensibilité à la casse</title>
+
+ <para>
+ Les requêtes sont insensibles à la casse, à l'exception des noms des classes Java
+ et des propriétés.
+ Ainsi, <literal>SeLeCT</literal> est identique à
+ <literal>sELEct</literal> et à
+ <literal>SELECT</literal> mais
+ <literal>net.sf.hibernate.eg.FOO</literal> n'est pas identique
+ <literal>net.sf.hibernate.eg.Foo</literal> et
+ <literal>foo.barSet</literal> n'est pas identique à
+ <literal>foo.BARSET</literal>.
+ </para>
+
+ <para>
+ Ce guide utilise les mots clés HQL en minuscule. Certains utilisateurs trouvent les
+ requêtes écrites avec les mots clés en majuscule plus lisibles, mais nous trouvons
+ cette convention pénible lorsqu'elle est lue dans du code Java.
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-from">
+ <title>La clause from</title>
+
+ <para>
+ La requête Hibernate la plus simple est de la forme :
+ </para>
+
+ <programlisting><![CDATA[from eg.Cat]]></programlisting>
+
+ <para>
+ qui retourne simplement toutes les instances de la classe <literal>eg.Cat</literal>.
+ Nous n'avons pas besoin d'habitude de qualifier le nom de la classe,
+ puisque <literal>auto-import</literal> est la valeur par défaut. Donc nous écrivons presque toujours :
+ </para>
+
+ <programlisting><![CDATA[from Cat]]></programlisting>
+
+ <para>
+ La plupart du temps, vous devrez assigner un <emphasis>alias</emphasis> puisque vous
+ voudrez faire référence à <literal>Cat</literal> dans d'autres parties de la requête.
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat]]></programlisting>
+
+ <para>
+ Cette requête assigne l'alias <literal>cat</literal> à l'instance <literal>Cat</literal>,
+ nous pouvons donc utiliser cet alias ailleurs dans la requête. Le mot clé <literal>as</literal>
+ est optionnel ; nous aurions pu écrire :
+ </para>
+
+ <programlisting><![CDATA[from Cat cat]]></programlisting>
+
+ <para>
+ Plusieurs classes peuvent apparaître, ce qui conduira à un produit
+ cartésien (encore appelé jointures croisées).
+ </para>
+
+ <programlisting><![CDATA[from Formula, Parameter]]></programlisting>
+ <programlisting><![CDATA[from Formula as form, Parameter as param]]></programlisting>
+
+ <para>
+ C'est une bonne pratique que de nommer les alias dans les requêtes en utilisant l'initiale
+ en miniscule, ce qui a le mérite d'être en phase avec les standards de
+ nommage Java pour les variables locales (<literal>domesticCat</literal>).
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-joins" revision="1">
+ <title>Associations et jointures</title>
+
+ <para>
+ On peut aussi assigner des alias à des entités associées, ou même aux éléments d'une collection
+ de valeurs, en utilisant un <literal>join</literal> (jointure).
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+ inner join cat.mate as mate
+ left outer join cat.kittens as kitten]]></programlisting>
+
+ <programlisting><![CDATA[from Cat as cat left join cat.mate.kittens as kittens]]></programlisting>
+
+ <programlisting><![CDATA[from Formula form full join form.parameter param]]></programlisting>
+
+ <para>
+ Les types de jointures supportées sont celles de ANSI SQL
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>inner join</literal> (jointure fermée)
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>left outer join</literal> (jointure ouverte par la gauche)
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>right outer join</literal> (jointure ouverte par la droite)
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>full join</literal> (jointure ouverte totalement - généralement inutile)
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Les constructions des jointures <literal>inner join</literal>, <literal>left outer join</literal>
+ et <literal>right outer join</literal> peuvent être abbrégées.
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+ join cat.mate as mate
+ left join cat.kittens as kitten]]></programlisting>
+
+ <para>
+ Nous pouvons soumettre des conditions de jointure supplémentaires en utilisant le mot-clef HQL <literal>with</literal>.
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+ left join cat.kittens as kitten
+ with kitten.bodyWeight > 10.0]]></programlisting>
+
+ <para>
+ Par ailleurs, une jointure "fetchée" (rapportée) permet d'initialiser
+ les associations ou collections de valeurs en même temps que leur objet parent,
+ le tout n'utilisant qu'un seul Select.
+ Ceci est particulièrement utile dans le cas des collections. Ce système permet de surcharger
+ les déclarations "lazy" et "outer-join" des fichiers de mapping pour les associations et
+ collections. Voir
+ <xref linkend="performance-fetching"/> pour plus d'informations.
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+ inner join fetch cat.mate
+ left join fetch cat.kittens]]></programlisting>
+
+ <para>
+ Une jointure "fetchée" (rapportée) n'a généralement pas besoin de se voir assigner
+ un alias puisque les objets associés n'ont pas à être utilisés dans les autres clauses.
+ Notez aussi que les objets associés ne sont pas retournés directement dans le résultat de
+ la requête mais l'on peut y accéder via l'objet parent. La seule raison pour laquelle nous
+ pourrions avoir besoin d'un alias est si nous récupérions récursivement une collection supplémentaire :
+ </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>
+ Notez que la construction de <literal>fetch</literal> ne peut pas être utilisée dans les requêtes appelées par
+ <literal>scroll()</literal> ou <literal>iterate()</literal>.
+ <literal>fetch</literal> ne devrait pas non plus être utilisé avec <literal>setMaxResults()</literal> ou
+ <literal>setFirstResult()</literal>. <literal>fetch</literal> ne peut pas non plus être utilisé avec une
+ condition <literal>with</literal> ad hoc. Il est
+ possible de créer un produit cartésien par jointure en récupérant plus d'une collection dans une requête,
+ donc faites attention dans ce cas. Récupérer par jointure de multiples collections donne aussi parfois
+ des résultats inattendus pour des mappings de bag, donc soyez prudent lorsque vous formulez vos requêtes dans de tels cas.
+ Finalement, notez que <literal>full join fetch</literal> et <literal>right join fetch</literal> ne sont pas utiles en général.
+ </para>
+
+ <para>
+ Si vous utilisez un chargement retardé pour les propriétés (avec une instrumentation par bytecode), il est possible
+ de forcer Hibernate à récupérer les propriétés non encore chargées immédiatement (dans la première requête)
+ en utilisant <literal>fetch all properties</literal>.
+ </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>
+
+ </sect1>
+
+ <sect1 id="queryhql-joins-forms">
+ <title>Formes de syntaxes pour les jointures</title>
+
+ <para>
+ HQL supporte deux formes pour joindre les associations: <literal>implicite</literal> et <literal>explicite</literal>.
+ </para>
+
+ <para>
+ Les requêtes présentes dans la section précédente utilisent la forme <literal>explicite</literal>
+ où le mode clé join est explicitement utilisé dans la clause from. C'est la forme recommandée.
+ </para>
+
+ <para>
+ La forme <literal>implicite</literal> n'utilise pas le mot clé join.
+ A la place, les associations sont "déréférencées" en utilisant le notation '.'. Ces
+ jointures peuvent apparaitre dans toutes les clauses. Les jointures <literal>implicites</literal>
+ résultent en des inner join dans le SQL généré.
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat where cat.mate.name like '%s%']]></programlisting>
+ </sect1>
+
+ <sect1 id="queryhql-select">
+ <title>La clause select</title>
+
+ <para>
+ La clause <literal>select</literal> sélectionne les objets et propriétés
+ qui doivent être retournés dans le résultat de la requête.
+ Soit :
+ </para>
+
+ <programlisting><![CDATA[select mate
+from Cat as cat
+ inner join cat.mate as mate]]></programlisting>
+
+ <para>
+ La requête recherchera les <literal>mate</literal>s liés aux <literal>Cat</literal>s.
+ Vous pouvez explimer la requête d'une manière plus compacte :
+ </para>
+
+ <programlisting><![CDATA[select cat.mate from Cat cat]]></programlisting>
+
+ <para>
+ Les requêtes peuvent retourner des propriétés de n'importe quel type, même celles de type
+ composant (component) :
+ </para>
+
+ <programlisting><![CDATA[select cat.name from DomesticCat cat
+where cat.name like 'fri%']]></programlisting>
+
+ <programlisting><![CDATA[select cust.name.firstName from Customer as cust]]></programlisting>
+
+ <para>
+ Les requêtes peuvent retourner plusieurs objets et/ou propriétés sous la forme
+ d'un tableau du type <literal>Object[]</literal>,
+ </para>
+
+ <programlisting><![CDATA[select mother, offspr, mate.name
+from DomesticCat as mother
+ inner join mother.mate as mate
+ left outer join mother.kittens as offspr]]></programlisting>
+
+ <para>
+ ou sous la forme d'une <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>
+ ou sous la forme d'un objet Java typé,
+ </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>
+ à condition que la classe <literal>Family</literal> possède le constructeur approprié.
+ </para>
+
+ <para>
+ Vous pouvez assigner des alias aux expressions sélectionnées en utilisant <literal>as</literal> :
+ </para>
+
+ <programlisting><![CDATA[select max(bodyWeight) as max, min(bodyWeight) as min, count(*) as n
+from Cat cat]]></programlisting>
+
+ <para>
+ C'est surtout utile lorsque c'est utilisé avec
+ <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>
+ Cette requête retourne une <literal>Map</literal> à partir des alias vers les valeurs sélectionnées.
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-aggregation">
+ <title>Fonctions d'aggrégation</title>
+
+ <para>
+ Les requêtes HQL peuvent aussi retourner le résultat de fonctions d'aggrégation
+ sur les propriétés :
+ </para>
+
+ <programlisting><![CDATA[select avg(cat.weight), sum(cat.weight), max(cat.weight), count(cat)
+from Cat cat]]></programlisting>
+
+<!-- NO LONGER SUPPORTED
+ <para>
+ Collections may also appear inside aggregate functions in the <literal>select</literal>
+ clause.
+ </para>
+
+ <programlisting><![CDATA[select cat, count( elements(cat.kittens) )
+from Cat cat group by cat]]></programlisting>
+-->
+
+ <para>
+ Les fonctions supportées sont
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>avg(...), sum(...), min(...), max(...)</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>count(*)</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>count(...), count(distinct ...), count(all...)</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Vous pouvez utiliser des opérateurs arithmétiques, la concaténation, et des fonctions SQL reconnues dans la clause select :
+ </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>
+ Les mots clé <literal>distinct</literal> et <literal>all</literal> peuvent être utilisés et ont
+ la même signification qu'en SQL.
+ </para>
+
+ <programlisting><![CDATA[select distinct cat.name from Cat cat
+
+select count(distinct cat.name), count(cat) from Cat cat]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="queryhql-polymorphism">
+ <title>Requêtes polymorphiques</title>
+
+ <para>
+ Une requête comme:
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat]]></programlisting>
+
+ <para>
+ retourne non seuleument les instances de <literal>Cat</literal>, mais aussi celles des
+ sous classes comme <literal>DomesticCat</literal>. Les requêtes Hibernate peuvent nommer n'importe
+ quelle classe ou interface Java dans la clause <literal>from</literal>. La requête retournera les
+ instances de toutes les classes persistantes qui étendent cette classe ou implémente cette interface.
+ La requête suivante retournera tous les objets persistants :
+ </para>
+
+ <programlisting><![CDATA[from java.lang.Object o]]></programlisting>
+
+ <para>
+ L'interface <literal>Named</literal> peut être implémentée par plusieurs classes persistantes :
+ </para>
+
+ <programlisting><![CDATA[from Named n, Named m where n.name = m.name]]></programlisting>
+
+ <para>
+ Notez que ces deux dernières requêtes nécessitent plus d'un <literal>SELECT</literal> SQL.
+ Ce qui signifie que la clause <literal>order by</literal> ne trie pas correctement la totalité
+ des résultats (cela signifie aussi que vous ne pouvez exécuter ces requêtes en appelant
+ <literal>Query.scroll()</literal>).
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-where">
+ <title>La clause where</title>
+
+ <para>
+ La clause <literal>where</literal> vous permet de réduire la liste des instances retournées.
+ Si aucun alias n'existe, vous pouvez vous référer aux propriétés par leur nom :
+ </para>
+
+ <programlisting><![CDATA[from Cat where name='Fritz']]></programlisting>
+
+ <para>
+ S'il y a un alias, utilisez un nom de propriété qualifié :
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat where cat.name='Fritz']]></programlisting>
+
+ <para>
+ retourne les instances de <literal>Cat</literal> dont name est égale à 'Fritz'.
+ </para>
+
+ <programlisting><![CDATA[select foo
+from Foo foo, Bar bar
+where foo.startDate = bar.date]]></programlisting>
+
+ <para>
+ retournera les instances de <literal>Foo</literal> pour lesquelles
+ il existe une instance de <literal>bar</literal> avec la
+ propriété <literal>date</literal> est égale à la
+ propriété <literal>startDate</literal> de <literal>Foo</literal>.
+ Les expressions utilisant la navigation rendent la clause <literal>where</literal>
+ extrêmement puissante. Soit :
+ </para>
+
+ <programlisting><![CDATA[from Cat cat where cat.mate.name is not null]]></programlisting>
+
+ <para>
+ Cette requête se traduit en SQL par une jointure interne à une table.
+ Si vous souhaitez écrire quelque chose comme :
+ </para>
+
+ <programlisting><![CDATA[from Foo foo
+where foo.bar.baz.customer.address.city is not null]]></programlisting>
+
+ <para>
+ vous finiriez avec une requête qui nécessiterait quatre jointures en SQL.
+ </para>
+
+ <para>
+ L'opérateur <literal>=</literal> peut être utilisé pour comparer aussi bien des propriétés que des instances :
+ </para>
+
+ <programlisting><![CDATA[from Cat cat, Cat rival where cat.mate = rival.mate]]></programlisting>
+
+ <programlisting><![CDATA[select cat, mate
+from Cat cat, Cat mate
+where cat.mate = mate]]></programlisting>
+
+ <para>
+ La propriété spéciale (en minuscule) <literal>id</literal> peut être utilisée
+ pour faire référence à l'identifiant d'un objet (vous pouvez aussi utiliser
+ le nom de cette propriété).
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat where cat.id = 123
+
+from Cat as cat where cat.mate.id = 69]]></programlisting>
+
+ <para>
+ La seconde requête est particulièrement efficace. Aucune jointure n'est nécessaire !
+ </para>
+
+ <para>
+ Les propriétés d'un identifiant composé peuvent aussi être utilisées. Supposez que
+ <literal>Person</literal> ait un identifiant composé de <literal>country</literal> et
+ <literal>medicareNumber</literal>.
+ </para>
+
+ <programlisting><![CDATA[from bank.Person person
+where person.id.country = 'AU'
+ and person.id.medicareNumber = 123456]]></programlisting>
+
+ <programlisting><![CDATA[from bank.Account account
+where account.owner.id.country = 'AU'
+ and account.owner.id.medicareNumber = 123456]]></programlisting>
+
+ <para>
+ Une fois de plus, la seconde requête ne nécessite pas de jointure.
+ </para>
+
+ <para>
+ De même, la propriété spéciale <literal>class</literal> interroge la valeur discriminante
+ d'une instance dans le cas d'une persistance polymorphique. Le nom d'une classe Java incorporée
+ dans la clause where sera traduite par sa valeur discriminante.
+ </para>
+
+ <programlisting><![CDATA[from Cat cat where cat.class = DomesticCat]]></programlisting>
+
+ <para>
+ Vous pouvez aussi spécifier les propriétés des composants ou types utilisateurs composés
+ (components, composite user types etc). N'essayez jamais d'utiliser un expression de navigation
+ qui se terminerait par une propriété de type composant (qui est différent d'une propriété d'un
+ composant). Par exemple, si <literal>store.owner</literal> est une entité avec un composant
+ <literal>address</literal>
+ </para>
+
+ <programlisting><![CDATA[store.owner.address.city // okay
+store.owner.address // error!]]></programlisting>
+
+ <para>
+ Un type "any" possède les propriétés spéciales <literal>id</literal> et <literal>class</literal>,
+ qui nous permettent d'exprimer une jointure de la manière suivante (où <literal>AuditLog.item</literal>
+ est une propriété mappée avec <literal><any></literal>).
+ </para>
+
+ <programlisting><![CDATA[from AuditLog log, Payment payment
+where log.item.class = 'Payment' and log.item.id = payment.id]]></programlisting>
+
+ <para>
+ Dans la requête précédente, notez que <literal>log.item.class</literal> et <literal>payment.class</literal>
+ feraient référence à des valeurs de colonnes de la base de données complètement différentes.
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-expressions">
+ <title>Expressions</title>
+
+ <para>
+ Les expressions permises dans la clause <literal>where</literal> incluent
+ la plupart des choses que vous pouvez utiliser en SQL :
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ opérateurs mathématiques <literal>+, -, *, /</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ opérateur de comparaison binaire <literal>=, >=, <=, <>, !=, like</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ opérateurs logiques <literal>and, or, not</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Parenthèses <literal>( )</literal>, indiquant un regroupement
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>in</literal>,
+ <literal>not in</literal>,
+ <literal>between</literal>,
+ <literal>is null</literal>,
+ <literal>is not null</literal>,
+ <literal>is empty</literal>,
+ <literal>is not empty</literal>,
+ <literal>member of</literal> and
+ <literal>not member of</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ "Simple" case, <literal>case ... when ... then ... else ... end</literal>, and
+ "searched" case, <literal>case when ... then ... else ... end</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ concatenation de chaîne de caractères <literal>...||...</literal> ou <literal>concat(...,...)</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>current_date()</literal>, <literal>current_time()</literal>,
+ <literal>current_timestamp()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>second(...)</literal>, <literal>minute(...)</literal>,
+ <literal>hour(...)</literal>, <literal>day(...)</literal>,
+ <literal>month(...)</literal>, <literal>year(...)</literal>,
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ N'importe quel fonction ou opérateur défini par EJB-QL 3.0 : <literal>substring(), trim(),
+ lower(), upper(), length(), locate(), abs(), sqrt(), bit_length(), mod()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>coalesce()</literal> et <literal>nullif()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>str()</literal> pour convertir des valeurs numériques ou temporelles vers une chaîne de caractères lisible
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>cast(... as ...)</literal>, où le second argument est le nom d'un type Hibernate, et <literal>extract(... from ...)</literal> si le
+ <literal>cast()</literal> ANSI et <literal>extract()</literal> sont supportés par la base de données sous-jacente
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ La fonction HQL <literal>index()</literal>, qui s'applique aux alias d'une collection indexée jointe
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Les fonctions HQL qui s'appliquent expressions représentant des collections : <literal>size(),
+ minelement(), maxelement(), minindex(), maxindex()</literal>, ainsi que les fonctions spéciales <literal>elements()</literal>
+ et <literal>indices</literal> qui peuvent être quantifiées en utilisant <literal>some, all, exists, any, in</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ N'importe quelle fonction scalaire supportée par la base de données comme
+ <literal>sign()</literal>,
+ <literal>trunc()</literal>, <literal>rtrim()</literal>, <literal>sin()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Les paramètres positionnels de JDBC
+ <literal>?</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ paramètres nommés <literal>:name</literal>, <literal>:start_date</literal>, <literal>:x1</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ littéral SQL <literal>'foo'</literal>, <literal>69</literal>, <literal>'1970-01-01 10:00:01.0'</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Constantes Java <literal>public static final</literal> <literal>eg.Color.TABBY</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ <literal>in</literal> et <literal>between</literal> peuvent être utilisés comme suit :
+ </para>
+
+ <programlisting><![CDATA[from DomesticCat cat where cat.name between 'A' and 'B']]></programlisting>
+
+ <programlisting><![CDATA[from DomesticCat cat where cat.name in ( 'Foo', 'Bar', 'Baz' )]]></programlisting>
+
+ <para>
+ et la forme négative peut être écrite
+ </para>
+
+ <programlisting><![CDATA[from DomesticCat cat where cat.name not between 'A' and 'B']]></programlisting>
+
+ <programlisting><![CDATA[from DomesticCat cat where cat.name not in ( 'Foo', 'Bar', 'Baz' )]]></programlisting>
+
+ <para>
+ De même, <literal>is null</literal> et <literal>is not null</literal> peuvent être utilisés pour tester
+ les valeurs nulle.
+ </para>
+
+ <para>
+ Les booléens peuvent être facilement utilisés en déclarant les substitutions de requêtes dans la
+ configuration Hibernate :
+ </para>
+
+ <programlisting><![CDATA[<property name="hibernate.query.substitutions">true 1, false 0</property>]]></programlisting>
+
+ <para>
+ Ce qui remplacera les mots clés <literal>true</literal> et <literal>false</literal> par
+ <literal>1</literal> et <literal>0</literal> dans la traduction SQL du HQL suivant :
+ </para>
+
+ <programlisting><![CDATA[from Cat cat where cat.alive = true]]></programlisting>
+
+ <para>
+ Vous pouvez tester la taille d'une collection par la propriété spéciale <literal>size</literal>, ou
+ la fonction spéciale <literal>size()</literal>.
+ </para>
+
+ <programlisting><![CDATA[from Cat cat where cat.kittens.size > 0]]></programlisting>
+
+ <programlisting><![CDATA[from Cat cat where size(cat.kittens) > 0]]></programlisting>
+
+ <para>
+ Pour les collections indexées, vous pouvez faire référence aux indices minimum et maximum en
+ utilisant les fonctions <literal>minindex</literal> and <literal>maxindex</literal>. De manière similaire,
+ vous pouvez faire référence aux éléments minimum et maximum d'une collection de type basiques
+ en utilisant les fonctions <literal>minelement</literal> et <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>
+ Les fonctions SQL <literal>any, some, all, exists, in</literal> supportent que leur soient passées
+ l'élément, l'index d'une collection (fonctions <literal>elements</literal> et <literal>indices</literal>)
+ ou le résultat d'une sous requête (voir ci dessous).
+ </para>
+
+ <programlisting><![CDATA[select mother from Cat as mother, Cat as kit
+where kit in elements(foo.kittens)]]></programlisting>
+
+ <programlisting><![CDATA[select p from NameList list, Person p
+where p.name = some elements(list.names)]]></programlisting>
+
+ <programlisting><![CDATA[from Cat cat where exists elements(cat.kittens)]]></programlisting>
+
+ <programlisting><![CDATA[from Player p where 3 > all elements(p.scores)]]></programlisting>
+
+ <programlisting><![CDATA[from Show show where 'fizard' in indices(show.acts)]]></programlisting>
+
+ <para>
+ Notez que l'écriture de - <literal>size</literal>, <literal>elements</literal>,
+ <literal>indices</literal>, <literal>minindex</literal>, <literal>maxindex</literal>,
+ <literal>minelement</literal>, <literal>maxelement</literal> - peuvent seulement être utilisée dans la clause where dans Hibernate3.
+ </para>
+
+ <para>
+ Les éléments de collections indexées (arrays, lists, maps) peuvent être référencés via index
+ (dans une clause where seulement) :
+ </para>
+
+ <programlisting><![CDATA[from Order order where order.items[0].id = 1234]]></programlisting>
+
+ <programlisting><![CDATA[select person from Person person, Calendar calendar
+where calendar.holidays['national day'] = person.birthDay
+ and person.nationality.calendar = calendar]]></programlisting>
+
+ <programlisting><![CDATA[select item from Item item, Order order
+where order.items[ order.deliveredItemIndices[0] ] = item and order.id = 11]]></programlisting>
+
+ <programlisting><![CDATA[select item from Item item, Order order
+where order.items[ maxindex(order.items) ] = item and order.id = 11]]></programlisting>
+
+ <para>
+ L'expression entre <literal>[]</literal> peut même être une expression arithmétique.
+ </para>
+
+ <programlisting><![CDATA[select item from Item item, Order order
+where order.items[ size(order.items) - 1 ] = item]]></programlisting>
+
+ <para>
+ HQL propose aussi une fonction <literal>index()</literal> interne, pour les éléments
+ d'une association one-to-many ou d'une collections de valeurs.
+ </para>
+
+ <programlisting><![CDATA[select item, index(item) from Order order
+ join order.items item
+where index(item) < 5]]></programlisting>
+
+ <para>
+ Les fonctions SQL scalaires supportées par la base de données utilisée peuvent être utilisées
+ </para>
+
+ <programlisting><![CDATA[from DomesticCat cat where upper(cat.name) like 'FRI%']]></programlisting>
+
+ <para>
+ Si vous n'êtes pas encore convaincu par tout cela, imaginez la taille et l'illisibilité qui caractériseraient
+ la transformation SQL de la requête HQL suivante :
+ </para>
+
+ <programlisting><![CDATA[select cust
+from Product prod,
+ Store store
+ inner join store.customers cust
+where prod.name = 'widget'
+ and store.location.name in ( 'Melbourne', 'Sydney' )
+ and prod = all elements(cust.currentOrder.lineItems)]]></programlisting>
+
+ <para>
+ <emphasis>Un indice :</emphasis> cela donnerait quelque chose comme
+ </para>
+
+ <programlisting><![CDATA[SELECT cust.name, cust.address, cust.phone, cust.id, cust.current_order
+FROM customers cust,
+ stores store,
+ locations loc,
+ store_customers sc,
+ product prod
+WHERE prod.name = 'widget'
+ AND store.loc_id = loc.id
+ AND loc.name IN ( 'Melbourne', 'Sydney' )
+ AND sc.store_id = store.id
+ AND sc.cust_id = cust.id
+ AND prod.id = ALL(
+ SELECT item.prod_id
+ FROM line_items item, orders o
+ WHERE item.order_id = o.id
+ AND cust.current_order = o.id
+ )]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="queryhql-ordering">
+ <title>La clause order by</title>
+
+ <para>
+ La liste retounée par la requête peut être triée par n'importe quelle propriété de la classe ou
+ du composant retourné :
+ </para>
+
+ <programlisting><![CDATA[from DomesticCat cat
+order by cat.name asc, cat.weight desc, cat.birthdate]]></programlisting>
+
+ <para>
+ Le mot optionnel <literal>asc</literal> ou <literal>desc</literal> indique respectivement si le tri
+ doit être croissant ou décroissant.
+ </para>
+ </sect1>
+
+ <sect1 id="queryhql-grouping">
+ <title>La clause group by</title>
+
+ <para>
+ Si la requête retourne des valeurs aggrégées, celles ci peuvent être groupées par propriété ou composant :
+ </para>
+
+ <programlisting><![CDATA[select cat.color, sum(cat.weight), count(cat)
+from Cat cat
+group by cat.color]]></programlisting>
+
+ <programlisting><![CDATA[select foo.id, avg(name), max(name)
+from Foo foo join foo.names name
+group by foo.id]]></programlisting>
+
+ <para>
+ Une clause <literal>having</literal> est aussi permise.
+ </para>
+
+ <programlisting><![CDATA[select cat.color, sum(cat.weight), count(cat)
+from Cat cat
+group by cat.color
+having cat.color in (eg.Color.TABBY, eg.Color.BLACK)]]></programlisting>
+
+ <para>
+ Les fonctions SQL et les fonctions d'aggrégations sont permises dans les clauses <literal>having</literal>
+ et <literal>order by</literal>, si elles sont supportées par la base de données (ce que ne fait pas MySQL par exemple).
+ </para>
+
+ <programlisting><![CDATA[select cat
+from Cat cat
+ join cat.kittens kitten
+group by cat
+having avg(kitten.weight) > 100
+order by count(kitten) asc, sum(kitten.weight) desc]]></programlisting>
+
+ <para>
+ Notez que ni la clause <literal>group by</literal> ni la clause
+ <literal>order by</literal> ne peuvent contenir d'expressions arithmétiques.
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-subqueries" revision="2">
+ <title>Sous-requêtes</title>
+
+ <para>
+ Pour les bases de données le supportant, Hibernate supporte les sous requêtes dans les requêtes.
+ Une sous requête doit être entre parenthèses (souvent pour un appel à une fonction d'agrégation SQL)
+ Même les sous requêtes corrélées (celles qui font référence à un alias de la requête principale) sont
+ supportées.
+ </para>
+
+ <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>
+
+ <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>
+
+ <programlisting><![CDATA[select cat.id, (select max(kit.weight) from cat.kitten kit)
+from Cat as cat]]></programlisting>
+
+ <para>
+ Notez que les sous-requêtes HQL peuvent arriver seulememnt dans les clauses select ou where.
+ </para>
+
+ <para>
+ Pour des sous-requêtes avec plus d'une expression dans le select, vous pouvez utiliser un constructeur de tuples :
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+where not ( cat.name, cat.color ) in (
+ select cat.name, cat.color from DomesticCat cat
+)]]></programlisting>
+
+ <para>
+ Notez que sur certaines bases de données (mais par Oracle ou HSQL), vous pouvez utiliser des constructeurs de tuples
+ dans d'autres contextes, par exemple lors du requêtage de composants ou de types utilisateur composites :
+ </para>
+
+ <programlisting><![CDATA[from Person where name = ('Gavin', 'A', 'King')]]></programlisting>
+
+ <para>
+ Ce qui est équivalent à la forme plus verbeuse suivante :
+ </para>
+
+ <programlisting><![CDATA[from Person where name.first = 'Gavin' and name.initial = 'A' and name.last = 'King')]]></programlisting>
+
+ <para>
+ Il y a deux bonnes raisons que vous ne puissiez ne pas vouloir faire cette sorte de choses : d'abord, ce n'est
+ pas complètement portable entre les plateformes de base de données ; deuxièmement, la requête est maintenant
+ dépendante de l'ordre des propriétés dans le document de mapping.
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-examples">
+ <title>Exemples HQL</title>
+
+ <para>
+ Les requêtes Hibernate peuvent être relativement puissantes et complexes. En fait, la puissance
+ du langage de requêtage est l'un des avantages principaux d'Hibernate. Voici quelques exemples
+ très similaires aux requêtes que nous avons utilisées lors d'un récent projet. Notez que la plupart
+ des requêtes que vous écrirez seront plus simples que les exemples suivantes !
+ </para>
+
+ <para>
+ La requête suivante retourne l'id de commande (order), le nombre d'articles (items) et la valeur
+ totale de la commande (order) pour toutes les commandes non payées d'un client (customer) particulier
+ pour un total minimum donné, le tout trié par la valeur totale. La requête SQL générée sur les tables
+ <literal>ORDER</literal>, <literal>ORDER_LINE</literal>, <literal>PRODUCT</literal>,
+ <literal>CATALOG</literal> et <literal>PRICE</literal> est composée de quatre jointures interne ainsi que
+ d'une sous-requête (non corrélée).
+ </para>
+
+ <programlisting><![CDATA[select order.id, sum(price.amount), count(item)
+from Order as order
+ join order.lineItems as item
+ join item.product as product,
+ Catalog as catalog
+ join catalog.prices as price
+where order.paid = false
+ and order.customer = :customer
+ and price.product = product
+ and catalog.effectiveDate < sysdate
+ and catalog.effectiveDate >= all (
+ select cat.effectiveDate
+ from Catalog as cat
+ where cat.effectiveDate < sysdate
+ )
+group by order
+having sum(price.amount) > :minAmount
+order by sum(price.amount) desc]]></programlisting>
+
+ <para>
+ Quel monstre !
+ En principe, nous ne sommes pas très fan des sous-requêtes, la requête ressemblait donc plutôt
+ à cela :
+ </para>
+
+ <programlisting><![CDATA[select order.id, sum(price.amount), count(item)
+from Order as order
+ join order.lineItems as item
+ join item.product as product,
+ Catalog as catalog
+ join catalog.prices as price
+where order.paid = false
+ and order.customer = :customer
+ and price.product = product
+ and catalog = :currentCatalog
+group by order
+having sum(price.amount) > :minAmount
+order by sum(price.amount) desc]]></programlisting>
+
+ <para>
+ La requête suivante compte le nombre de paiements (payments) pour chaque status, en excluant
+ les paiements dans le status <literal>AWAITING_APPROVAL</literal> où le changement de status
+ le plus récent à été fait par l'utilisateur courant. En SQL, cette requête effectue deux
+ jointures internes et des sous requêtes corrélées sur les tables <literal>PAYMENT</literal>,
+ <literal>PAYMENT_STATUS</literal> et <literal>PAYMENT_STATUS_CHANGE</literal>.
+ </para>
+
+ <programlisting><![CDATA[select count(payment), status.name
+from Payment as payment
+ join payment.currentStatus as status
+ join payment.statusChanges as statusChange
+where payment.status.name <> PaymentStatus.AWAITING_APPROVAL
+ or (
+ statusChange.timeStamp = (
+ select max(change.timeStamp)
+ from PaymentStatusChange change
+ where change.payment = payment
+ )
+ and statusChange.user <> :currentUser
+ )
+group by status.name, status.sortOrder
+order by status.sortOrder]]></programlisting>
+
+ <para>
+ Si nous avions mappé la collection <literal>statusChanges</literal> comme une liste, au lieu d'un ensemble,
+ la requête aurait été plus facile à écrire.
+ </para>
+
+ <programlisting><![CDATA[select count(payment), status.name
+from Payment as payment
+ join payment.currentStatus as status
+where payment.status.name <> PaymentStatus.AWAITING_APPROVAL
+ or payment.statusChanges[ maxIndex(payment.statusChanges) ].user <> :currentUser
+group by status.name, status.sortOrder
+order by status.sortOrder]]></programlisting>
+
+ <para>
+ La requête qui suit utilise la fonction de MS SQL <literal>isNull()</literal> pour retourner
+ tous les comptes (accounts) et paiements (payments) impayés pour l'organisation à laquelle
+ l'uilisateur (user) courant appartient. Elle est traduite en SQL par trois jointures internes,
+ une jointure externe ainsi qu'une sous requête sur les tables <literal>ACCOUNT</literal>, <literal>PAYMENT</literal>,
+ <literal>PAYMENT_STATUS</literal>, <literal>ACCOUNT_TYPE</literal>, <literal>ORGANIZATION</literal> et
+ <literal>ORG_USER</literal>.
+ </para>
+
+ <programlisting><![CDATA[select account, payment
+from Account as account
+ left outer join account.payments as payment
+where :currentUser in elements(account.holder.users)
+ and PaymentStatus.UNPAID = isNull(payment.currentStatus.name, PaymentStatus.UNPAID)
+order by account.type.sortOrder, account.accountNumber, payment.dueDate]]></programlisting>
+
+ <para>
+ Pour d'autres base de données, nous aurions dû faire sans la sous-requête (corrélée).
+ </para>
+
+ <programlisting><![CDATA[select account, payment
+from Account as account
+ join account.holder.users as user
+ left outer join account.payments as payment
+where :currentUser = user
+ and PaymentStatus.UNPAID = isNull(payment.currentStatus.name, PaymentStatus.UNPAID)
+order by account.type.sortOrder, account.accountNumber, payment.dueDate]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="queryhql-bulk" revision="2">
+ <title>Mise à jour et suppression</title>
+
+ <para>
+ HQL supporte maintenant les expressions <literal>update</literal>, <literal>delete</literal> et
+ <literal>insert ... select ...</literal>.
+ Voir <xref linkend="batch-direct"/> pour les détails.
+ </para>
+ </sect1>
+
+ <sect1 id="queryhql-tipstricks">
+ <title>Trucs & Astuces</title>
+
+ <para>
+ Vous pouvez compter le nombre de résultats d'une requête sans les retourner :
+ </para>
+
+ <programlisting><![CDATA[( (Integer) session.iterate("select count(*) from ....").next() ).intValue()]]></programlisting>
+
+ <para>
+ Pour trier les résultats par la taille d'une collection, utilisez la requête suivante :
+ </para>
+
+ <programlisting><![CDATA[select usr.id, usr.name
+from User as usr
+ left join usr.messages as msg
+group by usr.id, usr.name
+order by count(msg)]]></programlisting>
+
+ <para>
+ Si votre base de données supporte les sous-requêtes, vous pouvez placer des
+ conditions sur la taille de la sélection dans la clause where de votre requête:
+ </para>
+
+ <programlisting><![CDATA[from User usr where size(usr.messages) >= 1]]></programlisting>
+
+ <para>
+ Si votre base de données ne supporte pas les sous-requêtes, utilisez la requête suivante :
+ </para>
+
+ <programlisting><![CDATA[select usr.id, usr.name
+from User usr.name
+ join usr.messages msg
+group by usr.id, usr.name
+having count(msg) >= 1]]></programlisting>
+
+ <para>
+ Cette solution ne peut pas retourner un <literal>User</literal> avec zéro message
+ à cause de la jointure interne, la forme suivante peut donc être utile :
+ </para>
+
+ <programlisting><![CDATA[select usr.id, usr.name
+from User as usr
+ left join usr.messages as msg
+group by usr.id, usr.name
+having count(msg) = 0]]></programlisting>
+
+ <para>
+ Les propriétés d'un JavaBean peuvent être injectées dans les paramètres nommés d'un requête :
+ </para>
+
+ <programlisting><![CDATA[Query q = s.createQuery("from foo Foo as foo where foo.name=:name and foo.size=:size");
+q.setProperties(fooBean); // fooBean has getName() and getSize()
+List foos = q.list();]]></programlisting>
+
+ <para>
+ Les collections sont paginables via l'utilisation de l'interface <literal>Query</literal> avec un filtre :
+ </para>
+
+ <programlisting><![CDATA[Query q = s.createFilter( collection, "" ); // the trivial filter
+q.setMaxResults(PAGE_SIZE);
+q.setFirstResult(PAGE_SIZE * pageNumber);
+List page = q.list();]]></programlisting>
+
+ <para>
+ Les éléments d'une collection peuvent être triés ou groupés en utilisant un filtre de requête :
+ </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>
+
+ <para>
+ Vous pouvez récupérer la taille d'une collection sans l'initialiser :
+ </para>
+
+ <programlisting><![CDATA[( (Integer) session.iterate("select count(*) from ....").next() ).intValue();]]></programlisting>
+
+ </sect1>
+
+</chapter>
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/query_sql.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/query_sql.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/query_sql.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,606 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<chapter id="querysql" revision="2">
+ <title>SQL natif</title>
+
+ <para>
+ Vous pouvez aussi écrire vos requêtes dans le dialecte SQL natif de votre base de données.
+ Ceci est utile si vous souhaitez utiliser les fonctionnalités spécifiques de votre base de
+ données comme le mot clé <literal>CONNECT</literal> d'Oracle. Cette fonctionnalité offre par ailleurs un moyen
+ de migration plus propre et doux d'une application basée sur SQL/JDBC vers
+ une application Hibernate.
+ </para>
+
+ <para>Hibernate3 vous permet de spécifier du SQL écrit à la main (incluant les procédures stockées)
+ pour toutes les opérations de création, mise à jour, suppression et chargement.</para>
+
+ <sect1 id="querysql-creating" revision="3">
+ <title>Utiliser une <literal>SQLQuery</literal></title>
+
+ <para>L'exécution des requêtes en SQL natif est contrôlée par l'interface <literal>SQLQuery</literal>,
+ laquelle est obtenue en appelant <literal>Session.createSQLQuery()</literal>.
+ Dans des cas extrêmement simples, nous pouvons utiliser la forme suivante :
+ </para>
+
+ <programlisting>List cats = sess.createSQLQuery("select * from cats")
+ .addEntity(Cat.class)
+ .list();</programlisting>
+
+ <para>Cette requête a spécifié :</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>la requête SQL</para>
+ </listitem>
+
+ <listitem>
+ <para>l'entité retournée par la requête</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Ici, les noms de colonne des résultats sont supposés être les mêmes que les noms de colonne spécifiés dans le
+ document de mapping. Cela peut être problématique pour des requêtes SQL qui joignent de multiple tables, puisque
+ les mêmes noms de colonne peuvent apparaître dans plus d'une table. La forme suivante n'est pas vulnérable à la
+ duplication des noms de colonne :
+ </para>
+
+ <programlisting>List cats = sess.createSQLQuery("select {cat.*} from cats cat")
+ .addEntity("cat", Cat.class)
+ .list();</programlisting>
+
+ <para>Cette requête a spécifié :</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>la requête SQL, avec un paramètre fictif pour Hibernate pour injecter les alias de colonne</para>
+ </listitem>
+
+ <listitem>
+ <para>l'entité retournée par la requête, et son alias de table SQL</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ La méthode <literal>addEntity()</literal> associe l'alias de la table SQL
+ avec la classe de l'entité retournée, et détermine la forme de l'ensemble des résultats de la requête.
+ </para>
+
+ <para>
+ La méthode <literal>addJoin()</literal> peut être utilisée pour charger des associations vers d'autres
+ entités et collections.
+ </para>
+
+ <programlisting>List cats = sess.createSQLQuery(
+ "select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id"
+ )
+ .addEntity("cat", Cat.class)
+ .addJoin("kitten", "cat.kittens")
+ .list();</programlisting>
+
+ <para>
+ Une requête SQL native pourrait retourner une simple valeur scalaire ou une combinaison de scalaires et d'entités.
+ </para>
+
+ <programlisting>Double max = (Double) sess.createSQLQuery("select max(cat.weight) as maxWeight from cats cat")
+ .addScalar("maxWeight", Hibernate.DOUBLE);
+ .uniqueResult();</programlisting>
+
+ <para>Vous pouvez alternativement décrire les informations de mapping des résultats dans vos fichiers hbm
+ et les utiliser pour vos requêtes.</para>
+
+ <programlisting>List cats = sess.createSQLQuery(
+ "select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id"
+ )
+ .setResultSetMapping("catAndKitten")
+ .list();</programlisting>
+ </sect1>
+
+ <sect1 id="querysql-aliasreferences">
+ <title>Alias et références de propriété</title>
+
+ <para>
+ La notation <literal>{cat.*}</literal> utilisée au-dessus est un raccourci pour "toutes les propriétés".
+ Alternativement, vous pouvez lister explicitement les colonnes, mais même ce cas que nous laissons à Hibernate
+ injecte des alias de colonne SQL pour chaque propriété. Le remplaçant pour un alias de colonne
+ est juste le nom de la propriété qualifié par l'alias de la table.
+ Dans l'exemple suivant, nous récupérons des <literal>Cat</literal>s à partir d'une table différente
+ (<literal>cat_log</literal>) de celle déclarée dans les méta-données de mapping.
+ Notez que nous pouvons même utiliser les alias de propriété dans la clause "where" si nous le souhaitons.
+ </para>
+
+ <para>
+ La syntaxe <literal>{}</literal> <emphasis>n'est pas</emphasis> requise pour le requêtes nommées. Voir
+ <xref linkend="querysql-namedqueries" />.
+ </para>
+
+ <programlisting>String sql = "select cat.originalId as {cat.id}, " +
+ "cat.mateid as {cat.mate}, cat.sex as {cat.sex}, " +
+ "cat.weight*10 as {cat.weight}, cat.name as {cat.name} " +
+ "from cat_log cat where {cat.mate} = :catId"
+
+List loggedCats = sess.createSQLQuery(sql)
+ .addEntity("cat", Cat.class)
+ .setLong("catId", catId)
+ .list();</programlisting>
+
+ <para>
+ <emphasis>À noter :</emphasis> si vous listez chaque propriété explicitement, vous devez inclure
+ toutes les propriétés de la classe <emphasis>et ses sous-classes</emphasis> !
+ </para>
+
+ <para>
+ La table suivante montre les différentes possibilités d'utilisation de l'injection d'alias. À noter : les noms
+ des alias dans le résultat sont des exemples, chaque alias aura un nom unique et probablement différent lors de l'utilisation.
+ </para>
+
+ <table frame="topbot" id="aliasinjection-summary">
+ <title>Noms d'injection d'alias</title>
+
+ <tgroup cols="4">
+ <colspec colwidth="1*" />
+
+ <colspec colwidth="1*" />
+
+ <colspec colwidth="2.5*" />
+
+ <thead>
+ <row>
+ <entry>Description</entry>
+
+ <entry>Syntaxe</entry>
+
+ <entry>Exemple</entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry>Une simple propriété</entry>
+
+ <entry><literal>{[aliasname].[propertyname]}</literal></entry>
+
+ <entry><literal>A_NAME as {item.name}</literal></entry>
+ </row>
+
+ <row>
+ <entry>Une propriété composée</entry>
+
+ <entry><literal>{[aliasname].[componentname].[propertyname]}</literal></entry>
+
+ <entry><literal>CURRENCY as {item.amount.currency}, VALUE as
+ {item.amount.value}</literal></entry>
+ </row>
+
+ <row>
+ <entry>Discriminant d'une entité</entry>
+
+ <entry><literal>{[aliasname].class}</literal></entry>
+
+ <entry><literal>DISC as {item.class}</literal></entry>
+ </row>
+
+ <row>
+ <entry>Toutes les propriétés d'une entité</entry>
+
+ <entry><literal>{[aliasname].*}</literal></entry>
+
+ <entry><literal>{item.*}</literal></entry>
+ </row>
+
+ <row>
+ <entry>Une clef de collection</entry>
+
+ <entry><literal>{[aliasname].key}</literal></entry>
+
+ <entry><literal>ORGID as {coll.key}</literal></entry>
+ </row>
+
+ <row>
+ <entry>L'identifiant d'une collection</entry>
+
+ <entry><literal>{[aliasname].id}</literal></entry>
+
+ <entry><literal>EMPID as {coll.id}</literal></entry>
+ </row>
+
+ <row>
+ <entry>L'élément d'une collection</entry>
+
+ <entry><literal>{[aliasname].element}</literal></entry>
+
+ <entry><literal>XID as {coll.element}</literal></entry>
+
+ <entry></entry>
+ </row>
+
+ <row>
+ <entry>Propriété de l'élément dans la collection</entry>
+
+ <entry><literal>{[aliasname].element.[propertyname]}</literal></entry>
+
+ <entry><literal>NAME as {coll.element.name}</literal></entry>
+ </row>
+
+ <row>
+ <entry>Toutes les propriétés de l'élément dans la collection</entry>
+
+ <entry><literal>{[aliasname].element.*}</literal></entry>
+
+ <entry><literal>{coll.element.*}</literal></entry>
+ </row>
+
+ <row>
+ <entry>Toutes les propriétés de la collection</entry>
+
+ <entry><literal>{[aliasname].*}</literal></entry>
+
+ <entry><literal>{coll.*}</literal></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+
+ <sect1 id="querysql-namedqueries" revision="3">
+ <title>Requêtes SQL nommées</title>
+
+ <para>
+ Les requêtes SQL nommées peuvent être définies dans le document de mapping
+ et appelées exactement de la même manière qu'un requête HQL nommée. Dans ce
+ cas, nous <emphasis>n'avons pas besoin</emphasis> d'appeler <literal>addEntity()</literal>.
+ </para>
+
+ <programlisting><sql-query name="persons">
+ <return alias="person" class="eg.Person"/>
+ SELECT person.NAME AS {person.name},
+ person.AGE AS {person.age},
+ person.SEX AS {person.sex}
+ FROM PERSON person
+ WHERE person.NAME LIKE :namePattern
+</sql-query></programlisting>
+
+ <programlisting>List people = sess.getNamedQuery("persons")
+ .setString("namePattern", namePattern)
+ .setMaxResults(50)
+ .list();</programlisting>
+
+ <para>
+ Les éléments <literal><return-join></literal> et
+ <literal><load-collection></literal> sont respectivement utilisés pour lier
+ des associations et définir des requêtes qui initialisent des collections.
+ </para>
+
+ <programlisting><sql-query name="personsWith">
+ <return alias="person" class="eg.Person"/>
+ <return-join alias="address" property="person.mailingAddress"/>
+ SELECT person.NAME AS {person.name},
+ person.AGE AS {person.age},
+ person.SEX AS {person.sex},
+ adddress.STREET AS {address.street},
+ adddress.CITY AS {address.city},
+ adddress.STATE AS {address.state},
+ adddress.ZIP AS {address.zip}
+ FROM PERSON person
+ JOIN ADDRESS adddress
+ ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'
+ WHERE person.NAME LIKE :namePattern
+</sql-query></programlisting>
+
+ <para>
+ Une requête SQL nommée peut retourner une valeur scalaire. Vous devez
+ spécifier l'alias de colonne et le type Hibernate utilisant l'élément
+ <literal><return-scalar></literal> :</para>
+
+ <programlisting><sql-query name="mySqlQuery">
+ <return-scalar column="name" type="string"/>
+ <return-scalar column="age" type="long"/>
+ SELECT p.NAME AS name,
+ p.AGE AS age,
+ FROM PERSON p WHERE p.NAME LIKE 'Hiber%'
+</sql-query></programlisting>
+
+ <para>
+ Vous pouvez externaliser les informations de mapping des résultats dans un
+ élément <literal><resultset></literal> pour soit les réutiliser
+ dans différentes requêtes nommées, soit à travers l'API
+ <literal>setResultSetMapping()</literal>.
+ </para>
+
+ <programlisting><resultset name="personAddress">
+ <return alias="person" class="eg.Person"/>
+ <return-join alias="address" property="person.mailingAddress"/>
+</resultset>
+
+<sql-query name="personsWith" resultset-ref="personAddress">
+ SELECT person.NAME AS {person.name},
+ person.AGE AS {person.age},
+ person.SEX AS {person.sex},
+ adddress.STREET AS {address.street},
+ adddress.CITY AS {address.city},
+ adddress.STATE AS {address.state},
+ adddress.ZIP AS {address.zip}
+ FROM PERSON person
+ JOIN ADDRESS adddress
+ ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'
+ WHERE person.NAME LIKE :namePattern
+</sql-query></programlisting>
+
+ <sect2 id="propertyresults">
+ <title>Utilisation de return-property pour spécifier explicitement les noms des colonnes/alias</title>
+
+ <para>
+ Avec <literal><return-property></literal> vous pouvez explicitement dire
+ à Hibernate quels alias de colonne utiliser, plutot que d'employer la syntaxe
+ <literal>{}</literal> pour laisser Hibernate injecter ses propres alias.
+ </para>
+
+ <programlisting><sql-query name="mySqlQuery">
+ <return alias="person" class="eg.Person">
+ <return-property name="name" column="myName"/>
+ <return-property name="age" column="myAge"/>
+ <return-property name="sex" column="mySex"/>
+ </return>
+ SELECT person.NAME AS myName,
+ person.AGE AS myAge,
+ person.SEX AS mySex,
+ FROM PERSON person WHERE person.NAME LIKE :name
+</sql-query>
+</programlisting>
+
+ <para>
+ <literal><return-property></literal> fonctionne aussi avec de
+ multiple colonnes. Cela résout une limitation de la syntaxe <literal>{}</literal>
+ qui ne peut pas permettre une bonne granularité des propriétés multi-colonnes.
+ </para>
+
+ <programlisting><sql-query name="organizationCurrentEmployments">
+ <return alias="emp" class="Employment">
+ <return-property name="salary">
+ <return-column name="VALUE"/>
+ <return-column name="CURRENCY"/>
+ </return-property>
+ <return-property name="endDate" column="myEndDate"/>
+ </return>
+ SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer},
+ STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},
+ REGIONCODE as {emp.regionCode}, EID AS {emp.id}, VALUE, CURRENCY
+ FROM EMPLOYMENT
+ WHERE EMPLOYER = :id AND ENDDATE IS NULL
+ ORDER BY STARTDATE ASC
+</sql-query></programlisting>
+
+ <para>
+ Notez que dans cet exemple nous avons utilisé <literal><return-property></literal>
+ en combinaison avec la syntaxe <literal>{}</literal> pour l'injection. Cela autorise les
+ utilisateurs à choisir comment ils veulent référencer les colonnes et les propriétés.
+ </para>
+
+ <para>
+ Si votre mapping a un discriminant vous devez utiliser
+ <literal><return-discriminator></literal> pour spécifier la colonne
+ discriminante.
+ </para>
+ </sect2>
+
+ <sect2 id="sp_query" revision="1">
+ <title>Utilisation de procédures stockées pour les requêtes</title>
+
+ <para>
+ Hibernate 3 introduit le support des requêtes via procédures stockées et les fonctions.
+ La documentation suivante est valable pour les deux.
+ Les procédures stockées/fonctions doivent retourner l'ensemble de résultats en tant que
+ premier paramètre sortant (NdT: "out-parameter") pour être capable de fonctionner
+ avec Hibernate. Un exemple d'une telle procédure stockée en Oracle 9 et
+ version supérieure :
+ </para>
+
+ <programlisting>CREATE OR REPLACE FUNCTION selectAllEmployments
+ RETURN SYS_REFCURSOR
+AS
+ st_cursor SYS_REFCURSOR;
+BEGIN
+ OPEN st_cursor FOR
+ SELECT EMPLOYEE, EMPLOYER,
+ STARTDATE, ENDDATE,
+ REGIONCODE, EID, VALUE, CURRENCY
+ FROM EMPLOYMENT;
+ RETURN st_cursor;
+ END;</programlisting>
+
+ <para>Pour utiliser cette requête dans Hibernate vous avez besoin de la mapper via une requête nommée.</para>
+
+ <programlisting><sql-query name="selectAllEmployees_SP" callable="true">
+ <return alias="emp" class="Employment">
+ <return-property name="employee" column="EMPLOYEE"/>
+ <return-property name="employer" column="EMPLOYER"/>
+ <return-property name="startDate" column="STARTDATE"/>
+ <return-property name="endDate" column="ENDDATE"/>
+ <return-property name="regionCode" column="REGIONCODE"/>
+ <return-property name="id" column="EID"/>
+ <return-property name="salary">
+ <return-column name="VALUE"/>
+ <return-column name="CURRENCY"/>
+ </return-property>
+ </return>
+ { ? = call selectAllEmployments() }
+</sql-query></programlisting>
+
+ <para>
+ Notez que les procédures stockées retournent, pour le moment, seulement des
+ scalaires et des entités. <literal><return-join></literal> et
+ <literal><load-collection></literal> ne sont pas supportés.
+ </para>
+
+ <sect3 id="querysql-limits-storedprocedures" revision="1">
+ <title>Règles/limitations lors de l'utilisation des procédures stockées</title>
+
+ <para>
+ Pur utiliser des procédures stockées avec Hibernate, les procédures doivent
+ suivre certaines règles. Si elles ne suivent pas ces règles, elles ne sont pas
+ utilisables avec Hibernate. Si vous voulez encore utiliser ces procédures vous
+ devez les exécuter via <literal>session.connection()</literal>. Les règles
+ sont différentes pour chaque base de données, puisque les vendeurs de base
+ de données ont des sémantiques/syntaxes différentes pour les procédures stockées.
+ </para>
+
+ <para>Les requêtes de procédures stockées ne peuvent pas être paginées avec
+ <literal>setFirstResult()/setMaxResults()</literal>.</para>
+
+ <para>Pour Oracle les règles suivantes s'appliquent :</para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ La procédure doit retourner un ensemble de résultats. Le
+ prmeier paramètre d'une procédure doit être un <literal>OUT</literal>
+ qui retourne un ensemble de résultats. Ceci est fait en
+ retournant un <literal>SYS_REFCURSOR</literal> dans Oracle 9 ou 10. Dans
+ Oracle vous avez besoin de définir un type <literal>REF CURSOR</literal>.</para>
+ </listitem>
+
+ </itemizedlist>
+
+ <para>Pour Sybase ou MS SQL server les règles suivantes s'appliquent :</para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>La procédure doit retourner un ensemble de résultats. Notez que comme
+ ces serveurs peuvent retourner de multiples ensembles de résultats et mettre à jour
+ des compteurs, Hibernate itérera les résultats et prendra le premier résultat qui est
+ un ensemble de résultat comme valeur de retour. Tout le reste sera ignoré.</para>
+ </listitem>
+
+ <listitem>
+ <para>Si vous pouvez activer <literal>SET NOCOUNT ON</literal> dans votre procédure,
+ elle sera probablement plus efficace, mais ce n'est pas une obligation.</para>
+ </listitem>
+ </itemizedlist>
+ </sect3>
+ </sect2>
+ </sect1>
+
+ <sect1 id="querysql-cud">
+ <title>SQL personnalisé pour créer, mettre à jour et effacer</title>
+
+ <para>
+ Hibernate3 peut utiliser des expression SQL personnalisées pour des opérations de création,
+ de mise à jour, et de suppression. Les objets persistants les classes et les collections
+ dans Hibernate contiennent déjà un ensemble de chaînes de caractères générées lors de la
+ configuration (insertsql, deletesql, updatesql, etc). Les tages de mapping
+ <literal><sql-insert></literal>,
+ <literal><sql-delete></literal>, et
+ <literal><sql-update></literal> surchargent ces chaînes de caractères :</para>
+
+ <programlisting><class name="Person">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true"/>
+ <sql-insert>INSERT INTO PERSON (NAME, ID) VALUES ( UPPER(?), ? )</sql-insert>
+ <sql-update>UPDATE PERSON SET NAME=UPPER(?) WHERE ID=?</sql-update>
+ <sql-delete>DELETE FROM PERSON WHERE ID=?</sql-delete>
+</class></programlisting>
+
+ <para>Le SQL est directement exécuté dans votre base de données, donc vous êtes libre d'utiliser
+ le dialecte que vous souhaitez. Cela réduira bien sûr la portabilité de votre mapping si vous
+ utilisez du SQL spécifique à votre base de données.</para>
+
+ <para>Les procédures stockées sont supportées si l'attribut <literal>callable</literal> est paramétré :</para>
+
+ <programlisting><class name="Person">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true"/>
+ <sql-insert callable="true">{call createPerson (?, ?)}</sql-insert>
+ <sql-delete callable="true">{? = call deletePerson (?)}</sql-delete>
+ <sql-update callable="true">{? = call updatePerson (?, ?)}</sql-update>
+</class></programlisting>
+
+ <para>L'ordre des paramètres positionnels est actuellement vital, car ils doivent être dans la
+ même séquence qu'Hibernate les attend.</para>
+
+ <para>
+ Vous pouvez voir l'ordre attendu en activant les journaux de debug pour le
+ niveau <literal>org.hibernate.persister.entity</literal> level. Avec ce niveau activé,
+ Hibernate imprimera le SQL statique qui est utilisé pour créer, mettre à jour,
+ supprimer, etc. des entités. (Pour voir la séquence attendue, rappelez-vous de ne pas
+ inclure votre SQL personnalisé dans les fichiers de mapping de manière à surcharger le
+ SQL statique généré par Hibernate.)</para>
+
+ <para>Les procédures stockées sont dans la plupart des cas (lire : il vaut mieux le faire)
+ requises pour retourner le nombre de lignes insérées/mises à jour/supprimées, puisque
+ Hibernate fait quelques vérifications de succès lors de l'exécution de l'expression.
+ Hibernate inscrit toujours la première expression comme un paramètre de sortie numérique pour les
+ opérations CUD :</para>
+
+ <programlisting>CREATE OR REPLACE FUNCTION updatePerson (uid IN NUMBER, uname IN VARCHAR2)
+ RETURN NUMBER IS
+BEGIN
+
+ update PERSON
+ set
+ NAME = uname,
+ where
+ ID = uid;
+
+ return SQL%ROWCOUNT;
+
+END updatePerson;</programlisting>
+ </sect1>
+
+ <sect1 id="querysql-load">
+ <title>SQL personnalisé pour le chargement</title>
+
+ <para>Vous pouvez aussi déclarer vos propres requêtes SQL (ou HQL) pour le chargement d'entité :</para>
+
+ <programlisting><sql-query name="person">
+ <return alias="pers" class="Person" lock-mode="upgrade"/>
+ SELECT NAME AS {pers.name}, ID AS {pers.id}
+ FROM PERSON
+ WHERE ID=?
+ FOR UPDATE
+</sql-query></programlisting>
+
+ <para>Ceci est juste une déclaration de requête nommée, comme vu plus tôt. Vous pouvez référencer
+ cette requête nommée dans un mapping de classe :</para>
+
+ <programlisting><class name="Person">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true"/>
+ <loader query-ref="person"/>
+</class></programlisting>
+
+ <para>Ceci fonctionne même avec des procédures stockées.</para>
+
+ <para>Vous pouvez même définir une requête pour le chargement d'une collection :</para>
+
+ <programlisting><set name="employments" inverse="true">
+ <key/>
+ <one-to-many class="Employment"/>
+ <loader query-ref="employments"/>
+</set></programlisting>
+
+ <programlisting><sql-query name="employments">
+ <load-collection alias="emp" role="Person.employments"/>
+ SELECT {emp.*}
+ FROM EMPLOYMENT emp
+ WHERE EMPLOYER = :id
+ ORDER BY STARTDATE ASC, EMPLOYEE ASC
+</sql-query></programlisting>
+
+ <para>Vous pourriez même définir un chargeur d'entité qui charge une collection par jointure :</para>
+
+ <programlisting><sql-query name="person">
+ <return alias="pers" class="Person"/>
+ <return-join alias="emp" property="pers.employments"/>
+ SELECT NAME AS {pers.*}, {emp.*}
+ FROM PERSON pers
+ LEFT OUTER JOIN EMPLOYMENT emp
+ ON pers.ID = emp.PERSON_ID
+ WHERE ID=?
+</sql-query></programlisting>
+ </sect1>
+</chapter>
\ No newline at end of file
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/session_api.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/session_api.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/session_api.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1229 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<chapter id="objectstate">
+ <title>Travailler avec des objets</title>
+ <para>
+ Hibernate est une solution de mapping objet/relationnel complète qui ne masque pas
+ seulement au développpeur les détails du système de gestion de base de données sous-jacent,
+ mais offre aussi <emphasis>la gestion d'état</emphasis> des objets. C'est, contrairement
+ à la gestion de <literal>statements</literal> SQL dans les couches de persistance
+ habituelles JDBC/SQL, une vue orientée objet très naturelle de la persistance dans les
+ applications Java.
+ </para>
+
+ <para>
+ En d'autres mots, les développeurs d'applications Hibernate devrait toujours
+ réfléchir à <emphasis>l'état</emphasis> de leurs objets, et pas nécessairement à
+ l'exécution des expressions SQL. Cette part est prise en charge pas Hibernate et
+ seulement importante pour les développeurs d'applications lors du réglage de la
+ performance de leur système.
+ </para>
+
+ <sect1 id="objectstate-overview">
+ <title>États des objets Hibernate</title>
+
+ <para>
+ Hibernate définit et comprend les états suivants :
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Éphémère</emphasis> (NdT : transient) - un objet est éphémère s'il a juste
+ été instancié en utilisant l'opérateur <literal>new</literal>. Il n'a aucune
+ représentation persistante dans la base de données et aucune valeur d'identifiant
+ n'a été assignée. Les instances éphémères seront détruites par le ramasse-miettes
+ si l'application n'en conserve aucune référence. Utilisez la <literal>Session</literal>
+ d'Hibernate pour rendre un objet persistant (et laisser Hibernate s'occuper des
+ expressions SQL qui ont besoin d'être exécutées pour cette transistion).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Persistant</emphasis> - une instance persistante a une représentation dans la
+ base de données et une valeur d'identifiant. Elle pourrait avoir juste été sauvegardée
+ ou chargée, pourtant, elle est par définition dans la portée d'une <literal>Session</literal>.
+ Hibernate détectera n'importe quels changements effectués sur un objet dans l'état
+ persistant et synchronisera l'état avec la base de données lors de la fin l'unité de travail.
+ Les développeurs n'exécutent pas d'expressions <literal>UPDATE</literal> ou
+ <literal>DELETE</literal> manuelles lorsqu'un objet devrait être rendu éphémère.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Détaché</emphasis> - une instance détachée est un objet qui a été persistant,
+ mais dont sa <literal>Session</literal> a été fermée. La référence à l'objet est
+ encore valide, bien sûr, et l'instance détachée pourrait même être modifiée dans cet
+ état. Une instance détachée peut être réattachée à une nouvelle <literal>Session</literal>
+ plus tard dans le temps, la rendant (et toutes les modifications avec) de nouveau persistante.
+ Cette fonctionnalité rend possible un modèle de programmation pour de longues unités de travail
+ qui requièrent un temps de réflexion de l'utilisateur. Nous les appelons des <emphasis>conversations</emphasis>,
+ c'est-à-dire une unité de travail du point de vue de l'utilisateur.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Nous alons maintenant dicuster des états et des transitions d'état (et des méthodes
+ d'Hibernate qui déclenchent une transition) plus en détails.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-makingpersistent" revision="1">
+ <title>Rendre des objets persistants</title>
+
+ <para>
+ Les instances nouvellement instanciées d'une classe persistante sont considérées
+ <emphasis>éphémères</emphasis> par Hibernate. Nous pouvons rendre une instance
+ éphémère <emphasis>persistante</emphasis> en l'associant avec une session :
+ </para>
+
+ <programlisting><![CDATA[DomesticCat fritz = new DomesticCat();
+fritz.setColor(Color.GINGER);
+fritz.setSex('M');
+fritz.setName("Fritz");
+Long generatedId = (Long) sess.save(fritz);]]></programlisting>
+
+ <para>
+ Si <literal>Cat</literal> a un identifiant généré, l'identifiant est généré et assigné
+ au <literal>cat</literal> lorsque <literal>save()</literal> est appelée. Si <literal>Cat</literal>
+ a un identifiant <literal>assigned</literal>, ou une clef composée, l'identifiant
+ devrait être assigné à l'instance de <literal>cat</literal> avant d'appeler <literal>save()</literal>.
+ Vous pouvez aussi utiliser <literal>persist()</literal> à la place de<literal>save()</literal>,
+ avec la sémantique définie plus tôt dans le brouillon d'EJB3.
+ </para>
+
+ <para>
+ Alternativement, vous pouvez assigner l'identifiant en utilisant une version
+ surchargée de <literal>save()</literal>.
+ </para>
+
+<programlisting><![CDATA[DomesticCat pk = new DomesticCat();
+pk.setColor(Color.TABBY);
+pk.setSex('F');
+pk.setName("PK");
+pk.setKittens( new HashSet() );
+pk.addKitten(fritz);
+sess.save( pk, new Long(1234) );]]></programlisting>
+
+ <para>
+ Si l'objet que vous rendez persistant a des objets associés (par exemple,
+ la collection <literal>kittens</literal> dans l'exemple précédent), ces objets
+ peuvent être rendus persistants dans n'importe quel ordre que vous souhaitez
+ à moins que vous ayez une contrainte <literal>NOT NULL</literal> sur la
+ colonne de la clef étrangère. Il n'y a jamais de risque de violer une
+ contrainte de clef étrangère. Cependant, vous pourriez violer une contrainte
+ <literal>NOT NULL</literal> si vous appeliez <literal>save()</literal> sur
+ les objets dans le mauvais ordre.
+ </para>
+
+ <para>
+ Habituellement, vous ne vous préoccupez pas de ce détail, puisque vous
+ utiliserez très probablement la fonctionnalité de <emphasis>persistance
+ transitive</emphasis> d'Hibernate pour sauvegarder les objets associés
+ automatiquement. Alors, même les violations de contrainte <literal>NOT NULL</literal>
+ n'ont plus lieu - Hibernate prendra soin de tout. La persistance transitive est
+ traitée plus loin dans ce chapitre.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-loading">
+ <title>Chargement d'un objet</title>
+
+ <para>
+ Les méthodes <literal>load()</literal> de <literal>Session</literal> vous donnent
+ un moyen de récupérer une instance persistante si vous connaissez déjà son identifiant.
+ <literal>load()</literal> prend un objet de classe et chargera l'état dans une instance
+ nouvellement instanciée de cette classe, dans un état persistant.
+ </para>
+
+ <programlisting><![CDATA[Cat fritz = (Cat) sess.load(Cat.class, generatedId);]]></programlisting>
+
+<programlisting><![CDATA[// vous avez besoin d'envelopper les identiants primitifs
+long pkId = 1234;
+DomesticCat pk = (DomesticCat) sess.load( Cat.class, new Long(pkId) );]]></programlisting>
+
+ <para>
+ Alternativement, vous pouvez charger un état dans une instance donnée :
+ </para>
+
+<programlisting><![CDATA[Cat cat = new DomesticCat();
+// load pk's state into cat
+sess.load( cat, new Long(pkId) );
+Set kittens = cat.getKittens();]]></programlisting>
+
+ <para>
+ Notez que <literal>load()</literal> lèvera une exception irrécupérable s'il
+ n'y a pas de ligne correspondante dans la base de données. Si la classe est mappée
+ avec un proxy, <literal>load()</literal> retourne juste un proxy non initialisé et
+ n'accède en fait pas à la base de données jusqu'à ce que vous invoquiez une
+ méthode du proxy. Ce comportement est très utile si vous souhaitez créer
+ une association vers un objet sans réellement le charger à partir de la base de
+ données. Cela permet aussi à de multiples instances d'être chargées comme un lot
+ si <literal>batch-size</literal> est défini pour le mapping de la classe.
+ </para>
+
+ <para>
+ Si vous n'êtes pas certain qu'une ligne correspondante existe, vous devriez
+ utiliser la méthode <literal>get()</literal>, laquelle accède à la base de
+ données immédiatement et retourne null s'il n'y a pas de ligne correspondante.
+ </para>
+
+ <programlisting><![CDATA[Cat cat = (Cat) sess.get(Cat.class, id);
+if (cat==null) {
+ cat = new Cat();
+ sess.save(cat, id);
+}
+return cat;]]></programlisting>
+
+ <para>
+ Vous pouvez même charger un objet en employant un <literal>SELECT ... FOR UPDATE</literal> SQL,
+ en utilisant un <literal>LockMode</literal>. Voir la documentation de l'API pour plus d'informations.
+ </para>
+
+ <programlisting><![CDATA[Cat cat = (Cat) sess.get(Cat.class, id, LockMode.UPGRADE);]]></programlisting>
+
+ <para>
+ Notez que n'importe quelles instances associées ou collections contenues
+ <emphasis>ne sont pas</emphasis> sélectionnées par <literal>FOR UPDATE</literal>,
+ à moins que vous ne décidiez de spécifier <literal>lock</literal> ou <literal>all</literal>
+ en tant que style de cascade pour l'association.
+ </para>
+
+ <para>
+ Il est possible de re-charger un objet et toutes ses collections à n'importe quel moment,
+ en utilisant la méthode <literal>refresh()</literal>. C'est utile lorsque des "triggers" de
+ base de données sont utilisés pour initiliser certains propriétés de l'objet.
+ </para>
+
+ <programlisting><![CDATA[sess.save(cat);
+sess.flush(); //force the SQL INSERT
+sess.refresh(cat); //re-read the state (after the trigger executes)]]></programlisting>
+
+ <para>
+ Une question importante apparaît généralement à ce point : combien (NdT : de données) Hibernate
+ charge-t-il de la base de données et combient de <literal>SELECT</literal>s utilisera-t-il ?
+ Cela dépent de la <emphasis>stratégie de récupération</emphasis> et cela est expliqué dans
+ <xref linkend="performance-fetching"/>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-querying" revision="1">
+ <title>Requêtage</title>
+
+ <para>
+ Si vous ne connaissez par les identifiants des objets que vous recherchez, vous
+ avez besoin d'une requête. Hibernate supporte un langage de requêtes orientées objet
+ facile à utiliser mais puissant. Pour la création de requêtes par programmation,
+ Hibernate supporte une fonction de requêtage sophistiqué Criteria et Example (QBC et QBE).
+ Vous pouvez aussi exprimez votre requête dans le SQL natif de votre base de données,
+ avec un support optionnel d'Hibernate pour la conversion des ensembles de résultats en
+ objets.
+ </para>
+
+ <sect2 id="objectstate-querying-executing">
+ <title>Exécution de requêtes</title>
+
+ <para>
+ Les requêtes HQL et SQL natives sont représentées avec une instance de <literal>org.hibernate.Query</literal>.
+ L'interface offre des méthodes pour la liaison des paramètres, la gestion des ensembles de resultats, et pour
+ l'exécution de la requête réelle. Vous obtenez toujours une <literal>Query</literal> en utilisant la
+ <literal>Session</literal> courante :
+ </para>
+
+ <programlisting><![CDATA[List cats = session.createQuery(
+ "from Cat as cat where cat.birthdate < ?")
+ .setDate(0, date)
+ .list();
+
+List mothers = session.createQuery(
+ "select mother from Cat as cat join cat.mother as mother where cat.name = ?")
+ .setString(0, name)
+ .list();
+
+List kittens = session.createQuery(
+ "from Cat as cat where cat.mother = ?")
+ .setEntity(0, pk)
+ .list();
+
+Cat mother = (Cat) session.createQuery(
+ "select cat.mother from Cat as cat where cat = ?")
+ .setEntity(0, izi)
+ .uniqueResult();]]></programlisting>
+
+ <para>
+ Une requête est généralement exécutée en invoquant <literal>list()</literal>,
+ le résultat de la requête sera chargée complètement dans une collection en mémoire.
+ Les intances d'entités recupérées par une requête sont dans un état persistant.
+ La méthode <literal>uniqueResult()</literal> offre un raccourci si vous
+ savez que votre requête retournera seulement un seul objet.
+ </para>
+
+ <sect3 id="objectstate-querying-executing-iterate">
+ <title>Itération de résultats</title>
+
+ <para>
+ Occasionnellement, vous pourriez être capable d'obtenir de meilleures
+ performances en exécutant la requête avec la méthode <literal>iterate()</literal>.
+ Ce sera généralement seulement le cas si vous espérez que les intances réelles
+ d'entité retournées par la requête soient déjà chargées dans la session ou le
+ cache de second niveau. Si elles ne sont pas cachées, <literal>iterate()</literal>
+ sera plus lent que <literal>list()</literal> et pourrait nécessiter plusieurs
+ accès à la base de données pour une simple requête, généralement <emphasis>1</emphasis>
+ pour le select initial qui retourne seulement les identifiants, et <emphasis>n</emphasis>
+ selects supplémentaires pour initialiser les instances réelles.
+ </para>
+
+ <programlisting><![CDATA[// fetch ids
+Iterator iter = sess.createQuery("from eg.Qux q order by q.likeliness").iterate();
+while ( iter.hasNext() ) {
+ Qux qux = (Qux) iter.next(); // fetch the object
+ // something we couldnt express in the query
+ if ( qux.calculateComplicatedAlgorithm() ) {
+ // delete the current instance
+ iter.remove();
+ // dont need to process the rest
+ break;
+ }
+}]]></programlisting>
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-tuples">
+ <title>Requêtes qui retournent des tuples</title>
+
+ <para>
+ Les requêtes d'Hibernate retournent parfois des tuples d'objets, auquel cas chaque tuple
+ est retourné comme un tableau :
+ </para>
+
+ <programlisting><![CDATA[Iterator kittensAndMothers = sess.createQuery(
+ "select kitten, mother from Cat kitten join kitten.mother mother")
+ .list()
+ .iterator();
+
+while ( kittensAndMothers.hasNext() ) {
+ Object[] tuple = (Object[]) kittensAndMothers.next();
+ Cat kitten = tuple[0];
+ Cat mother = tuple[1];
+ ....
+}]]></programlisting>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-scalar" revision="1">
+ <title>Résultats scalaires</title>
+
+ <para>
+ Des requêtes peuvent spécifier une propriété d'une classe dans la clause <literal>select</literal>.
+ Elles peuvent même appeler des fonctions d'aggrégat SQL. Les propriétés ou les aggrégats sont
+ considérés comme des résultats "scalaires" (et pas des entités dans un état persistant).
+ </para>
+
+ <programlisting><![CDATA[Iterator results = sess.createQuery(
+ "select cat.color, min(cat.birthdate), count(cat) from Cat cat " +
+ "group by cat.color")
+ .list()
+ .iterator();
+
+while ( results.hasNext() ) {
+ Object[] row = (Object[]) results.next();
+ Color type = (Color) row[0];
+ Date oldest = (Date) row[1];
+ Integer count = (Integer) row[2];
+ .....
+}]]></programlisting>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-parameters">
+ <title>Lier des paramètres</title>
+
+ <para>
+ Des méthodes de <literal>Query</literal> sont fournies pour lier des
+ valeurs à des paramètres nommés ou à des paramètres de style JDBC <literal>?</literal>.
+ <emphasis>Contrairement à JDBC, les numéros des paramètres d'Hibernate commencent à zéro.</emphasis>
+ Les paramètres nommés sont des identifiants de la forme <literal>:nom</literal> dans la chaîne de
+ caractères de la requête. Les avantages des paramètres nommés sont :
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ les paramètres nommés sont insensibles à l'ordre de leur place dans la chaîne
+ de la requête
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ils peuvent apparaître plusieurs fois dans la même requête
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ils sont auto-documentés
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <programlisting><![CDATA[//paramètre nomme (préféré)
+Query q = sess.createQuery("from DomesticCat cat where cat.name = :name");
+q.setString("name", "Fritz");
+Iterator cats = q.iterate();]]></programlisting>
+
+ <programlisting><![CDATA[//paramètre positionnel
+Query q = sess.createQuery("from DomesticCat cat where cat.name = ?");
+q.setString(0, "Izi");
+Iterator cats = q.iterate();]]></programlisting>
+
+ <programlisting><![CDATA[//liste de paramètres nommés
+List names = new ArrayList();
+names.add("Izi");
+names.add("Fritz");
+Query q = sess.createQuery("from DomesticCat cat where cat.name in (:namesList)");
+q.setParameterList("namesList", names);
+List cats = q.list();]]></programlisting>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-pagination">
+ <title>Pagination</title>
+
+ <para>
+ Si vous avez besoin de spécifier des liens sur votre ensemble de résultats (le nombre
+ maximum de lignes que vous voulez récupérez et/ou la première ligne que vous voulez récupérer)
+ vous devriez utiliser des méthodes de l'interface <literal>Query</literal> :
+ </para>
+
+ <programlisting><![CDATA[Query q = sess.createQuery("from DomesticCat cat");
+q.setFirstResult(20);
+q.setMaxResults(10);
+List cats = q.list();]]></programlisting>
+
+ <para>
+ Hibernate sait comment traduite cette requête de limite en SQL natif pour votre SGBD.
+ </para>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-scrolling">
+ <title>Itération "scrollable"</title>
+
+ <para>
+ Si votre connecteur JDBC supporte les <literal>ResultSet</literal>s "scrollables",
+ l'interface <literal>Query</literal> peut être utilisée pour obtenir un objet
+ <literal>ScrollableResults</literal>, lequel permet une navigation flexible dans les
+ résultats de la requête.
+ </para>
+
+ <programlisting><![CDATA[Query q = sess.createQuery("select cat.name, cat from DomesticCat cat " +
+ "order by cat.name");
+ScrollableResults cats = q.scroll();
+if ( cats.first() ) {
+
+ // trouve le premier nom sur chaque page d'une liste alphabétique de noms de chats
+ firstNamesOfPages = new ArrayList();
+ do {
+ String name = cats.getString(0);
+ firstNamesOfPages.add(name);
+ }
+ while ( cats.scroll(PAGE_SIZE) );
+
+ // Maintenant, obtiens la première page de chats
+ pageOfCats = new ArrayList();
+ cats.beforeFirst();
+ int i=0;
+ while( ( PAGE_SIZE > i++ ) && cats.next() ) pageOfCats.add( cats.get(1) );
+
+}
+cats.close()]]></programlisting>
+
+ <para>
+ Notez qu'une connexion ouverte (et un curseur) est requise pour cette fonctionnalité,
+ utilisez <literal>setMaxResult()</literal>/<literal>setFirstResult()</literal> si vous
+ avez besoin d'une fonctionnalité de pagination hors ligne.
+ </para>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-named">
+ <title>Externaliser des requêtes nommées</title>
+
+ <para>
+ Vous pouvez aussi définir des requêtes nommées dans le document de mapping.
+ (Souvenez-vous d'utiliser une section <literal>CDATA</literal> si votre requête
+ contient des caractères qui pourraient être interprétés comme des éléments XML.)
+ </para>
+
+ <programlisting><![CDATA[<query name="eg.DomesticCat.by.name.and.minimum.weight"><![CDATA[
+ from eg.DomesticCat as cat
+ where cat.name = ?
+ and cat.weight > ?
+] ]></query>]]></programlisting>
+
+ <para>
+ La liaison de paramètres et l'exécution sont fait par programmation :
+ </para>
+
+ <programlisting><![CDATA[Query q = sess.getNamedQuery("eg.DomesticCat.by.name.and.minimum.weight");
+q.setString(0, name);
+q.setInt(1, minWeight);
+List cats = q.list();]]></programlisting>
+
+ <para>
+ Notez que le code réel du programme est indépendant du langage de requête qui est
+ utilisé, vous pouvez aussi définir des requêtes SQL nativez dans les méta-données, ou
+ migrer des requêtes existantes vers Hibernate en les plaçant dans les fichiers de mapping.
+ </para>
+
+ </sect3>
+
+ </sect2>
+
+ <sect2 id="objectstate-filtering" revision="1">
+ <title>Filtrer des collections</title>
+ <para>
+ Un <emphasis>filtre</emphasis> de collection est un type spécial de requête qui peut être
+ appliqué à une collection persistante ou à un tableau. La chaîne de requête peut se référer à
+ <literal>this</literal>, correspondant à l'élément de la collection courant.
+ </para>
+
+ <programlisting><![CDATA[Collection blackKittens = session.createFilter(
+ pk.getKittens(),
+ "where this.color = ?")
+ .setParameter( Color.BLACK, Hibernate.custom(ColorUserType.class) )
+ .list()
+);]]></programlisting>
+
+ <para>
+ La collection retournée est considérée comme un bag, et c'est une copie de la
+ collection donnée. La collection originale n'est pas modifiée (c'est contraire
+ à l'implication du nom "filtre"; mais cohérent avec le comportement attendu).
+ </para>
+
+ <para>
+ Observez que les filtres ne nécessitent pas une clause <literal>from</literal> (bien qu'ils
+ puissent en avoir une si besoin est). Les filtres ne sont pas limités à retourner des
+ éléments de la collection eux-mêmes.
+ </para>
+
+ <programlisting><![CDATA[Collection blackKittenMates = session.createFilter(
+ pk.getKittens(),
+ "select this.mate where this.color = eg.Color.BLACK.intValue")
+ .list();]]></programlisting>
+
+ <para>
+ Même une requête de filtre vide est utile, par exemple pour charger un sous-ensemble
+ d'éléments dans une énorme collection :
+ </para>
+
+ <programlisting><![CDATA[Collection tenKittens = session.createFilter(
+ mother.getKittens(), "")
+ .setFirstResult(0).setMaxResults(10)
+ .list();]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="objecstate-querying-criteria" revision="1">
+ <title>Requêtes Criteria</title>
+
+ <para>
+ HQL est extrêmement puissant mais certains développeurs préfèrent construire des
+ requêtes dynamiquement, en utilisant l'API orientée objet, plutôt que construire
+ des chaînes de requêtes. Hibernate fournit une API intuitive de requête <literal>Criteria</literal>
+ pour ces cas :
+ </para>
+
+ <programlisting><![CDATA[Criteria crit = session.createCriteria(Cat.class);
+crit.add( Expression.eq( "color", eg.Color.BLACK ) );
+crit.setMaxResults(10);
+List cats = crit.list();]]></programlisting>
+
+ <para>
+ Les APIs <literal>Criteria</literal> et <literal>Example</literal> associé sont
+ traitées plus en détail dans <xref linkend="querycriteria"/>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="objectstate-querying-nativesql" revision="2">
+ <title>Requêtes en SQL natif</title>
+
+ <para>
+ Vous pouvez exprimer une requête en SQL, en utilisant <literal>createSQLQuery()</literal>
+ et laisser Hibernate s'occuper du mapping des résultats vers des objets. Notez que vous
+ pouvez n'importe quand appeler <literal>session.connection()</literal> et utiliser
+ directement la <literal>Connection</literal> JDBC. Si vous choisissez d'utiliser
+ l'API Hibernate, vous devez mettre les alias SQL entre accolades :
+ </para>
+
+ <programlisting><![CDATA[List cats = session.createSQLQuery(
+ "SELECT {cat.*} FROM CAT {cat} WHERE ROWNUM<10",
+ "cat",
+ Cat.class
+).list();]]></programlisting>
+
+ <programlisting><![CDATA[List cats = session.createSQLQuery(
+ "SELECT {cat}.ID AS {cat.id}, {cat}.SEX AS {cat.sex}, " +
+ "{cat}.MATE AS {cat.mate}, {cat}.SUBCLASS AS {cat.class}, ... " +
+ "FROM CAT {cat} WHERE ROWNUM<10",
+ "cat",
+ Cat.class
+).list()]]></programlisting>
+
+ <para>
+ Les requêtes SQL peuvent contenir des paramètres nommés et positionnels, comme des
+ requêtes Hibernate. Plus d'informations à propos des requêtes SQL natives dans Hibernate
+ peuvent être trouvées dans <xref linkend="querysql"/>.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="objectstate-modifying" revision="1">
+ <title>Modifier des objets persistants</title>
+
+ <para>
+ Les <emphasis>instances persistantes transactionnelles</emphasis> (c'est-à-dire des objets
+ chargés, sauvegardés, créés ou requêtés par la <literal>Session</literal>) peuvent être
+ manipulées par l'application et n'importe quel changement vers l'état persistant sera
+ persisté lorsque la <literal>Session</literal> est <emphasis>"flushée"</emphasis> (traité
+ plus tard dans ce chapitre). Il n'y a pas besoin d'appeler une méthode particulière
+ (comme <literal>update()</literal>, qui a un but différent) pour rendre vos modifications
+ persistantes. Donc la manière la plus directe de mettre à jour l'état d'un objet est de
+ le charger avec <literal>load()</literal>, et puis le manipuler directement, tant que la
+ <literal>Session</literal> est ouverte :
+ </para>
+
+ <programlisting><![CDATA[DomesticCat cat = (DomesticCat) sess.load( Cat.class, new Long(69) );
+cat.setName("PK");
+sess.flush(); // changes to cat are automatically detected and persisted]]></programlisting>
+
+ <para>
+ Parfois ce modèle de programmation est inefficace puisqu'il nécessiterait un
+ <literal>SELECT</literal> SQL (pour charger l'objet) et un <literal>UPDATE</literal>
+ SQL (pour persister son état mis à jour) dans la même session. Aussi Hibernate offre
+ une autre approche, en utilisant des instances détachées.
+ </para>
+
+ <para>
+ <emphasis>Notez que Hibernate n'offre par sa propre API pour l'exécution directe
+ d'expressions <literal>UPDATE</literal> ou <literal>DELETE</literal>. Hibernate
+ est un service de <emphasis>gestion d'état</emphasis>, vous n'avez pas à penser
+ aux <emphasis>expressions</emphasis> pour l'utiliser. JDBC est une API parfaite
+ pour exécuter des expressions SQL, vous pouvez obtenir une <literal>Connection</literal>
+ JDBC n'importe quand en appelant <literal>session.connection()</literal>. En outre,
+ la notion d'opérations de masse entre en conflit avec le mapping objet/relationnel
+ pour les applications orientées processus de transactions en ligne. Les futures
+ versions d'Hibernate peuvent cependant fournir des fonctions d'opération de masse.
+ Voir <xref linkend="batch"/> pour les astuces possibles d'opérations groupées.</emphasis>
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-detached" revision="2">
+ <title>Modifier des objets détachés</title>
+
+ <para>
+ Beaucoup d'applications ont besoin de récupérer un objet dans une transaction,
+ l'envoyer à la couche interfacée avec l'utilisateur pour les manipulations, puis
+ sauvegarder les changements dans une nouvelle transaction. Les applications
+ qui utilisent cette approche dans un environnement à haute concurrence utilisent
+ généralement des données versionnées pour assurer l'isolation pour les "longues"
+ unités de travail.
+ </para>
+
+ <para>
+ Hibernate supporte ce modèle en permettant pour le réattachement d'instances détachées
+ l'utilisation des méthodes <literal>Session.update()</literal> ou <literal>Session.merge()</literal> :
+ </para>
+
+ <programlisting><![CDATA[// dans la première session
+Cat cat = (Cat) firstSession.load(Cat.class, catId);
+Cat potentialMate = new Cat();
+firstSession.save(potentialMate);
+
+// dans une couche plus haute de l'application
+cat.setMate(potentialMate);
+
+// plus tard, dans une nouvelle session
+secondSession.update(cat); // update cat
+secondSession.update(mate); // update mate]]></programlisting>
+
+ <para>
+ Si le <literal>Cat</literal> avec l'identifiant <literal>catId</literal> avait déjà
+ été chargé par <literal>secondSession</literal> lorsque l'application a essayé de le
+ réattacher, une exception aurait été levée.
+ </para>
+
+ <para>
+ Utilisez <literal>update()</literal> si vous êtes sure que la session ne contient pas
+ déjà une instance persistante avec le même identifiant, et <literal>merge()</literal>
+ si vous voulez fusionner vos modifications n'importe quand sans considérer l'état de
+ la session. En d'autres mots, <literal>update()</literal> est généralement la première méthode
+ que vous devriez appeler dans une session fraîche, pour s'assurer que le réattachement
+ de vos instances détachées est la première opération qui est exécutée.
+ </para>
+
+ <para>
+ L'application devrait individuellement <literal>update()</literal> (NdT : mettre à jour)
+ les instances détachées accessibles depuis l'instance détachée donnée si et
+ <emphasis>seulement</emphasis> si elle veut que leur état soit aussi mis à jour. Ceci
+ peut être automatisé bien sûr, en utilisant la <emphasis>persistance transitive</emphasis>,
+ voir <xref linkend="objectstate-transitive"/>.
+ </para>
+
+ <para>
+ La méthode <literal>lock()</literal> permet aussi à une application de réassocier un
+ objet avec une nouvelle session. Pourtant, l'instance détachée doit être non modifiée !
+ </para>
+
+ <programlisting><![CDATA[//réassocie :
+sess.lock(fritz, LockMode.NONE);
+//fait une vérification de version, puis réassocie :
+sess.lock(izi, LockMode.READ);
+//fait une vérification de version, en utilisant SELECT ... FOR UPDATE, puis réassocie :
+sess.lock(pk, LockMode.UPGRADE);]]></programlisting>
+
+ <para>
+ Notez que <literal>lock()</literal> peut être utilisé avec différents
+ <literal>LockMode</literal>s, voir la documentation de l'API documentation et le chapitre
+ sur la gestion des transactions pour plus d'informations. Le réattachement n'est pas le seul
+ cas d'utilisation pour <literal>lock()</literal>.
+ </para>
+
+ <para>
+ D'autres modèles pour de longues unités de travail sont traités dans <xref linkend="transactions-optimistic"/>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-saveorupdate">
+ <title>Détection automatique d'un état</title>
+
+ <para>
+ Les utilisateurs d'Hibernate ont demandé une méthode dont l'intention générale
+ serait soit de sauvegarder une instance éphémère en générant un nouvel identifiant,
+ soit mettre à jour/réattacher les instances détachées associées à l'identifiant courant.
+ La méthode <literal>saveOrUpdate()</literal> implémente cette fonctionnalité.
+ </para>
+
+ <programlisting><![CDATA[// dans la première session
+Cat cat = (Cat) firstSession.load(Cat.class, catID);
+
+// dans une partie plus haute de l'application
+Cat mate = new Cat();
+cat.setMate(mate);
+
+// plus tard, dans une nouvelle session
+secondSession.saveOrUpdate(cat); // met à jour un état existant (cat a un identifiant non-null)
+secondSession.saveOrUpdate(mate); // sauvegarde les nouvelles instances (mate a un identiant null)]]></programlisting>
+
+ <para>
+ L'usage et la sémantique de <literal>saveOrUpdate()</literal> semble être confuse pour les
+ nouveaux utilisateurs. Premièrement, aussi longtemps que vous n'essayez pas d'utiliser des
+ instances d'une session dans une autre, vous ne devriez pas avoir besoin d'utiliser <literal>update()</literal>,
+ <literal>saveOrUpdate()</literal>, ou <literal>merge()</literal>. Certaines applications
+ n'utiliseront jamais ces méthodes.
+ </para>
+
+ <para>
+ Généralement <literal>update()</literal> ou <literal>saveOrUpdate()</literal> sont utilisées dans
+ le scénario suivant :
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ l'application charge un objet dans la première session
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ l'objet est passé à la couche utilisateur
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ certaines modifications sont effectuées sur l'objet
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ l'objet est retourné à la couche logique métier
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ l'application persiste ces modifications en appelant
+ <literal>update()</literal> dans une seconde sessin
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ <literal>saveOrUpdate()</literal> s'utilise dans le cas suivant :
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ si l'objet est déjà persistant dans cette session, ne rien faire
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ si un autre objet associé à la session a le même identifiant, lever une exception
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ si l'objet n'a pas de propriété d'identifiant, appeler <literal>save()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ si l'identifiant de l'objet a une valeur assignée à un objet nouvellement instancié,
+ appeler <literal>save()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ si l'objet est versionné (par <literal><version></literal> ou
+ <literal><timestamp></literal>), et la valeur de la propriété de version
+ est la même valeur que celle assignée à un objet nouvellement instancié, appeler
+ <literal>save()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ sinon mettre à jour l'objet avec <literal>update()</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ et <literal>merge()</literal> est très différent :
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ s'il y a une instance persistante avec le même identifiant couramment
+ associée à la session, copier l'état de l'objet donné dans l'instance persistante
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ s'il n'y a pas d'instance persistante associée à cette session, essayer de le charger
+ à partir de la base de données, ou créer une nouvelle instance persistante
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ l'instance persistante est retournée
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ l'instance donnée ne devient pas associée à la session, elle reste détachée
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect1>
+
+ <sect1 id="objectstate-deleting" revision="1">
+ <title>Suppression d'objets persistants</title>
+
+ <para>
+ <literal>Session.delete()</literal> supprimera l'état d'un objet de la base de données.
+ Bien sûr, votre application pourrait encore conserver une référence vers un objet effacé.
+ Il est mieux de penser à <literal>delete()</literal> comme rendant une instance persistante
+ éphémère.
+ </para>
+
+ <programlisting><![CDATA[sess.delete(cat);]]></programlisting>
+
+ <para>
+ Vous pouvez effacer des objets dans l'ordre que vous voulez, sans risque de violations
+ de contrainte de clef étrangère. Il est encore possible de violer une contrainte <literal>NOT
+ NULL</literal> sur une colonne de clef étrangère en effaçant des objets dans le
+ mauvais ordre, par exemple si vous effacer le parent, mais oubliez d'effacer les enfants.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-replicating" revision="1">
+ <title>Réplication d'objets entre deux entrepôts de données</title>
+
+ <para>
+ Il est occasionnellement utile de pouvoir prendre un graphe d'instances persistantes
+ et de les rendre persistantes dans un entrepôt différent, sans regénérer les valeurs
+ des identifiants.
+ </para>
+
+ <programlisting><![CDATA[//récupère un cat de la base de données
+Session session1 = factory1.openSession();
+Transaction tx1 = session1.beginTransaction();
+Cat cat = session1.get(Cat.class, catId);
+tx1.commit();
+session1.close();
+
+// réconcilie la seconde base de données
+Session session2 = factory2.openSession();
+Transaction tx2 = session2.beginTransaction();
+session2.replicate(cat, ReplicationMode.LATEST_VERSION);
+tx2.commit();
+session2.close();]]></programlisting>
+
+ <para>
+ Le <literal>ReplicationMode</literal> détermine comment <literal>replicate()</literal>
+ traitera les conflits avec les lignes existantes dans la base de données.
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>ReplicationMode.IGNORE</literal> - ignore l'objet s'il y a une ligne
+ existante dans la base de données avec le même identifiant
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>ReplicationMode.OVERWRITE</literal> - écrase n'importe quelle ligne existante
+ dans la base de données avec le même identifiant
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>ReplicationMode.EXCEPTION</literal> - lève une exception s'il y une ligne dans
+ la base de données avec le même identifiant
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>ReplicationMode.LATEST_VERSION</literal> - écrase la ligne si son numéro de version
+ est plus petit que le numéro de version de l'objet, ou ignore l'objet sinon
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Les cas d'utilisation de cette fonctionnalité incluent la réconciliation de données
+ entrées dans différentes base de données, l'extension des informations de configuration
+ du système durant une mise à jour du produit, retour en arrière sur les changements effectués
+ durant des transactions non-ACID, et plus.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-flushing">
+ <title>Flush de la session</title>
+
+ <para>
+ De temps en temps la <literal>Session</literal> exécutera les expressions SQL
+ requises pour syncrhoniser l'état de la connexion JDBC avec l'état des objets
+ retenus en mémoire. Ce processus, <emphasis>flush</emphasis>, arrive par défaut aux
+ points suivants :
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ lors de certaines exécutions de requête
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ lors d'un appel à <literal>org.hibernate.Transaction.commit()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ lors d'un appel à <literal>Session.flush()</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Les expressions SQL sont effectuées dans l'ordre suivant :
+ </para>
+
+ <orderedlist spacing="compact">
+ <listitem>
+ <para>
+ insertion des entités, dans le même ordre que celui des
+ objets correspondants sauvegardés par l'appel à <literal>Session.save()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ mise à jours des entités
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ suppression des collections
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ suppression, mise à jour et insertion des éléments des collections
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ insertion des collections
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ suppression des entités, dans le même ordre que celui des objets
+ correspondants qui ont été supprimés par l'appel à <literal>Session.delete()</literal>
+ </para>
+ </listitem>
+ </orderedlist>
+
+ <para>
+ (Une exception est que des objets utilisant la génération <literal>native</literal>
+ d'identifiants sont insérés lorsqu'ils sont sauvegardés.)
+ </para>
+
+ <para>
+ Excepté lorsque vous appelez <literal>flush()</literal> explicitement, il n'y
+ absolument aucune garantie à propos de <emphasis>quand</emphasis> la <literal>Session</literal>
+ exécute les appels JDBC, seulement sur l'<emphasis>ordre</emphasis> dans lequel ils sont
+ exécutés. Cependant, Hibernate garantit que <literal>Query.list(..)</literal> ne
+ retournera jamais de données périmées, ni des données fausses.
+ </para>
+
+ <para>
+ Il est possible de changer le comportement par défaut, donc que le flush se produise
+ moins fréquemment. La classe <literal>FlushMode</literal> définit trois modes différents :
+ flush seulement lors du commit (et seulement quand l'API <literal>Transaction</literal>
+ d'Hibernate est utilisée), flush automatiquement en utilisant la procédure expliquée, ou
+ jamais de flush à moins que <literal>flush()</literal> soit appelée explicitement.
+ Le dernier mode est utile pour l'exécution de longues unités de travail, où une
+ <literal>Session</literal> est gardée ouverte et déconnectée pour un long moment
+ (voir <xref linkend="transactions-optimistic-longsession"/>).
+ </para>
+
+ <programlisting><![CDATA[sess = sf.openSession();
+Transaction tx = sess.beginTransaction();
+sess.setFlushMode(FlushMode.COMMIT); // permet aux requêtes de retourner un état périmé
+
+Cat izi = (Cat) sess.load(Cat.class, id);
+izi.setName(iznizi);
+
+// pourrait retourner des données périmées
+sess.find("from Cat as cat left outer join cat.kittens kitten");
+
+// le changement pour izi n'est pas flushé !
+...
+tx.commit(); // le flush se produit]]></programlisting>
+
+ <para>
+ Durant le flush, une exception peut se produire (par exemple, si une opération de la
+ DML viole une contrainte). Puisque les exceptions de gestion impliquent une certaine
+ compréhension du comportement transactionnel d'Hibernate, nous le traitons dans
+ <xref linkend="transactions"/>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-transitive" revision="1">
+ <title>Persistance transitive</title>
+
+ <para>
+ Il est assez pénible de sauvegarder, supprimer, ou réattacher des objets
+ un par un, surtout si vous traitez un graphe d'objets associés. Un cas habituel
+ est une relation parent/enfant. Considérez l'exemple suivant :
+ </para>
+
+ <para>
+ Si les enfants de la relation parent/enfant étaient des types de valeur (par exemple,
+ une collection d'adresses ou de chaînes de caractères), leur cycle de vie dépendraient
+ du parent et aucune action ne serait requise pour "cascader" facilement les
+ changements d'état. Si le parent est sauvegardé, les objets enfants de type de valeur sont
+ sauvegardés également, si le parent est supprimé, les enfants sont supprimés, etc. Ceci
+ fonctionne même pour des opérations telles que la suppression d'un enfant de la collection ;
+ Hibernate détectera cela et, puisque les objets de type de valeur ne peuvent pas avoir
+ des références partagées, supprimera l'enfant de la base de données.
+ </para>
+
+ <para>
+ Maintenant considérez le même scénario avec un parent et dont les objets enfants
+ sont des entités, et non des types de valeur (par exemple, des catégories et des
+ objets, ou un parent et des chatons). Les entités ont leur propre cycle de vie,
+ supportent les références partagées (donc supprimer une entité de la collection
+ ne signifie pas qu'elle peut être supprimée), et il n'y a par défaut pas de
+ cascade d'état d'une entité vers n'importe quelle entité associée. Hibernate
+ n'implémente pas la <emphasis>persistance par accessibilité</emphasis> par défaut.
+ </para>
+
+ <para>
+ Pour chaque opération basique de la session d'Hibernate - incluant <literal>persist(), merge(),
+ saveOrUpdate(), delete(), lock(), refresh(), evict(), replicate()</literal> - il y a un
+ style de cascade correspondant. Respectivement, les styles de cascade s'appellent <literal>persist,
+ merge, save-update, delete, lock, refresh, evict, replicate</literal>. Si vous voulez qu'une
+ opération soit cascadée le long d'une association, vous devez l'indiquer dans le document de
+ mapping. Par exemple :
+ </para>
+
+ <programlisting><![CDATA[<one-to-one name="person" cascade="persist"/>]]></programlisting>
+
+ <para>
+ Les styles de cascade peuvent être combinés :
+ </para>
+
+ <programlisting><![CDATA[<one-to-one name="person" cascade="persist,delete,lock"/>]]></programlisting>
+
+ <para>
+ Vous pouvez même utiliser <literal>cascade="all"</literal> pour spécifier que <emphasis>toutes</emphasis>
+ les opérations devraient être cascadées le long de l'association. La valeur par défaut
+ <literal>cascade="none"</literal> spécifie qu'aucune opération ne sera cascadée.
+ </para>
+
+ <para>
+ Une style de cascade spécial, <literal>delete-orphan</literal>, s'applique seulement
+ aux associations un-vers-plusieurs, et indique que l'opération <literal>delete()</literal>
+ devrait être appliquée à n'importe quel enfant qui est supprimé de l'association.
+ </para>
+
+
+ <para>
+ Recommandations :
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ Cela n'a généralement aucun sens d'activer la cascade sur une association
+ <literal><many-to-one></literal> ou <literal><many-to-many></literal>. Les
+ cascades sont souvent utiles pour des associations
+ <literal><one-to-one></literal> et <literal><one-to-many></literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Si la durée de vie de l'objet enfant est liée à la durée de vie de l'objet parent,
+ faites en un <emphasis>objet du cycle de vie</emphasis> en spécifiant
+ <literal>cascade="all,delete-orphan"</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Sinon, vous pourriez ne pas avoir besoin de cascade du tout. Mais si vous pensez que vous
+ travaillerez souvent avec le parent et les enfants ensemble dans la même transaction, et
+ que vous voulez vous éviter quelques frappes, considérez l'utilisation de
+ <literal>cascade="persist,merge,save-update"</literal>.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Mapper une association (soit une simple association valuée, soit une collection) avec
+ <literal>cascade="all"</literal> marque l'association comme une relation de style
+ <emphasis>parent/enfant</emphasis> où la sauvegarde/mise à jour/suppression du parent
+ entraîne la sauvegarde/mise à jour/suppression de l'enfant ou des enfants.
+ </para>
+ <para>
+ En outre, une simple référence à un enfant d'un parent persistant aura pour conséquence
+ la sauvegarde/mise à jour de l'enfant. Cette métaphore est cependant incomplète. Un enfant
+ qui devient non référencé par son parent <emphasis>n'est pas</emphasis> automatiquement
+ supprimée, excepté dans le cas d'une association <literal><one-to-many></literal>
+ mappée avec <literal>cascade="delete-orphan"</literal>. La sémantique précise des opérations
+ de cascade pour une relation parent/enfant est la suivante :
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ Si un parent est passé à <literal>persist()</literal>, tous les enfant sont passés à
+ <literal>persist()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Si un parent est passé à <literal>merge()</literal>, tous les enfants sont passés à
+ <literal>merge()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Si un parent est passé à <literal>save()</literal>, <literal>update()</literal> ou
+ <literal>saveOrUpdate()</literal>, tous les enfants sont passés à <literal>saveOrUpdate()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Si un enfant détaché ou éphémère devient référencé par un parent persistant,
+ il est passé à <literal>saveOrUpdate()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Si un parent est supprimé, tous les enfants sont passés à <literal>delete()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Si un enfant est déréférencé par un parent persistant, <emphasis>rien de spécial
+ n'arrive</emphasis> - l'application devrait explicitement supprimer l'enfant si nécessaire -
+ à moins que <literal>cascade="delete-orphan"</literal> soit paramétré,
+ au quel cas l'enfant "orphelin" est supprimé.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Enfin, la cascade des opérations peut être effectuée sur un graphe donné lors
+ de l'<emphasis>appel de l'opération</emphasis> or lors du <emphasis>flush</emphasis>
+ suivant. Toutes les opérations, lorsque cascadées, le sont sur toutes les entités
+ associées atteignables lorsque l'opétation est exécutée. Cependant
+ <literal>save-upate</literal> et <literal>delete-orphan</literal> sont cascadées
+ à toutes les entités associées atteignables lors du flush de la
+ <literal>Session</literal>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-metadata">
+ <title>Utilisation des méta-données</title>
+
+ <para>
+ Hibernate requiert un modèle de méta-niveau très riche de toutes les entités et types valués.
+ De temps en temps, ce modèle est très utile à l'application elle même. Par exemple,
+ l'application pourrait utiliser les méta-données d'Hibernate pour implémenter un algorithme
+ de copie en profondeur "intelligent" qui comprendrait quels objets devraient copiés
+ (par exemple les types de valeur mutables) et lesquels ne devraient pas l'être (par exemple
+ les types de valeurs immutables et, possiblement, les entités associées).
+ </para>
+ <para>
+ Hibernate expose les méta-données via les interfaces <literal>ClassMetadata</literal>
+ et <literal>CollectionMetadata</literal> et la hiérarchie <literal>Type</literal>.
+ Les instances des interfaces de méta-données peuvent être obtenues à partir de la
+ <literal>SessionFactory</literal>.
+ </para>
+
+ <programlisting><![CDATA[Cat fritz = ......;
+ClassMetadata catMeta = sessionfactory.getClassMetadata(Cat.class);
+
+Object[] propertyValues = catMeta.getPropertyValues(fritz);
+String[] propertyNames = catMeta.getPropertyNames();
+Type[] propertyTypes = catMeta.getPropertyTypes();
+
+// récupère une Map de toutes les propriétés qui ne sont pas des collections ou des associations
+Map namedValues = new HashMap();
+for ( int i=0; i<propertyNames.length; i++ ) {
+ if ( !propertyTypes[i].isEntityType() && !propertyTypes[i].isCollectionType() ) {
+ namedValues.put( propertyNames[i], propertyValues[i] );
+ }
+}]]></programlisting>
+
+ </sect1>
+
+</chapter>
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/toolset_guide.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/toolset_guide.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/toolset_guide.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,594 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<chapter id="toolsetguide" revision="2">
+ <title>Guide des outils</title>
+
+ <para>
+ Des outils en ligne de commande, des plugins Eclipse ainsu que des tâches Ant permettent de gérer de cycles de développement complet
+ de projets utilisant Hibernate.
+ </para>
+
+ <para>
+ Les <emphasis>outils Hibernate</emphasis> actuels incluent des plugins pour l'IDE Eclipse ainsi que des tâches Ant pour l'ingénierie
+ inverse de bases de données existantes :
+ </para>
+
+ <itemizedlist>
+ <listitem><para>
+ <emphasis>Mapping Editor :</emphasis> un éditeur pour les fichiers de mapping XML Hibernate, supportant l'auto-complétion et la mise en valeur de la syntaxe.
+ Il supporte aussi l'auto-complétion automatique pour les noms de classes et les noms de propriété/champ,
+ le rendant beaucoup plus polyvalent qu'un éditeurXML normal.
+ </para></listitem>
+ <listitem><para>
+ <emphasis>Console :</emphasis> la console est une nouvelle vue d'Eclipse. En plus de la vue d'ensemble
+ arborescente de vos configurations de console, vous obtenez aussi une vue interactive de vos classes persistantes et de leurs relations.
+ La console vous permet d'exécuter des requête HQL dans votre base de données et de parcourir les résultats directement dans Eclipse.
+ </para></listitem>
+ <listitem><para>
+ <emphasis>Development Wizards :</emphasis> plusieurs assistants sont fournis avec les outils d'Hibernate
+ pour Eclipse ; vous pouvez utiliser un assistant pour générer rapidement les fichiers de configuration d'Hibernate (cfg.xml),
+ ou vous pouvez même complètement générer les fichiers de mapping Hibernate et les sources des POJOs à partir d'un schéma de base de données existant.
+ L'assistant d'ingénierie inverse supporte les modèles utilisateur.
+ </para></listitem>
+ <listitem><para>
+ <emphasis>Tâches Ant :</emphasis>
+ </para></listitem>
+
+ </itemizedlist>
+
+ <para>
+ Veuillez-vous référer au paquet <emphasis>outils Hibernate</emphasis> et sa documentation pour plus d'informations.
+ </para>
+
+ <para>
+ Pourtant, le paquet principal d'Hibernate arrive avec un lot d'outils intégrés (il peut même être utilisé de "l'intérieur" d'Hibernate à la volée) :
+ <emphasis>SchemaExport</emphasis> aussi connu comme
+ <literal>hbm2ddl</literal>.
+ </para>
+
+ <sect1 id="toolsetguide-s1" revision="2">
+ <title>Génération automatique du schéma</title>
+
+ <para>
+ La DDL peut être générée à partir de vos fichiers de mapping par un utilitaire d'Hibernate. Le schéma généré
+ inclut les contraintes d'intégrité référentielle (clefs primaires et étrangères) pour les tables d'entités
+ et de collections. Les tables et les séquences sont aussi créées pour les générateurs d'identifiant mappés.
+ </para>
+
+ <para>
+ Vous <emphasis>devez</emphasis> spécifier un <literal>Dialect</literal> SQL via la propriété
+ <literal>hibernate.dialect</literal> lors de l'utilisation de cet outils, puisque la DDL est
+ fortement dépendante de la base de données.
+ </para>
+
+ <para>
+ D'abord, personnalisez vos fichiers de mapping pour améliorer le schéma généré.
+ </para>
+
+ <sect2 id="toolsetguide-s1-2" revision="3">
+ <title>Personnaliser le schéma</title>
+
+ <para>
+ Plusieurs éléments du mapping hibernate définissent des attributs optionnels
+ nommés <literal>length</literal>, <literal>precision</literal> et <literal>scale</literal>.
+ Vous pouvez paramétrer la longueur, la précision,... d'une colonne avec ces attributs.
+ </para>
+
+ <programlisting><![CDATA[<property name="zip" length="5"/>]]></programlisting>
+ <programlisting><![CDATA[<property name="balance" precision="12" scale="2"/>]]></programlisting>
+
+ <para>
+ Certains éléments acceptent aussi un attribut <literal>not-null</literal>
+ (utilisé pour générer les contraintes de colonnes <literal>NOT NULL</literal>) et
+ un attribut <literal>unique</literal> (pour générer une contrainte de colonne
+ <literal>UNIQUE</literal>).
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="bar" column="barId" not-null="true"/>]]></programlisting>
+ <programlisting><![CDATA[<element column="serialNumber" type="long" not-null="true" unique="true"/>]]></programlisting>
+
+ <para>
+ Un attribut <literal>unique-key</literal> peut être utilisé pour grouper les colonnes
+ en une seule contrainte d'unicité. Actuellement, la valeur spécifiée par
+ l'attribut <literal>unique-key</literal> n'est <emphasis>pas</emphasis> utilisée pour
+ nommer la contrainte dans le DDL généré, elle sert juste à grouper les colonnes
+ dans le fichier de mapping.
+ </para>
+ <programlisting><![CDATA[<many-to-one name="org" column="orgId" unique-key="OrgEmployeeId"/>
+ <property name="employeeId" unique-key="OrgEmployee"/>]]></programlisting>
+
+ <para>
+ Un attribut <literal>index</literal> indique le nom d'un index qui sera
+ créé en utilisant la ou les colonnes mappées. Plusieurs colonnes
+ peuvent être groupées dans un même index, en spécifiant le même
+ nom d'index.
+ </para>
+
+ <programlisting><![CDATA[<property name="lastName" index="CustName"/>
+<property name="firstName" index="CustName"/>]]></programlisting>
+
+ <para>
+ Un attribut <literal>foreign-key</literal> peut être utilisé pour surcharger le nom
+ des clés étrangères générées.
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="bar" column="barId" foreign-key="FKFooBar"/>]]></programlisting>
+
+ <para>
+ Plusieurs éléments de mapping acceptent aussi un élément fils <literal><column></literal>.
+ Ceci est utile pour les type multi-colonnes:
+ </para>
+
+ <programlisting><![CDATA[<property name="name" type="my.customtypes.Name"/>
+ <column name="last" not-null="true" index="bar_idx" length="30"/>
+ <column name="first" not-null="true" index="bar_idx" length="20"/>
+ <column name="initial"/>
+</property>]]></programlisting>
+
+ <para>
+ L'attribut <literal>default</literal> vous laisse spécifier une valeur par défaut pour
+ une colonnes (vous devriez assigner la même valeur à la propriété mappée avant de sauvegarder une nouvelle instance
+ de la classe mappée).
+ </para>
+
+ <programlisting><![CDATA[<property name="credits" type="integer" insert="false">
+ <column name="credits" default="10"/>
+</property>]]></programlisting>
+
+ <programlisting><![CDATA[<version name="version" type="integer" insert="false">
+ <column name="version" default="0"/>
+</property>]]></programlisting>
+
+ <para>
+ L'attribut <literal>sql-type</literal> laisse l'utilisateur surcharger le mapping
+ par défaut du type Hibernate vers un type SQL.
+ </para>
+
+ <programlisting><![CDATA[<property name="balance" type="float">
+ <column name="balance" sql-type="decimal(13,3)"/>
+</property>]]></programlisting>
+
+
+ <para>
+ L'attribut <literal>check</literal> permet de spécifier une contrainte de vérification.
+ </para>
+
+ <programlisting><![CDATA[<property name="foo" type="integer">
+ <column name="foo" check="foo > 10"/>
+</property>]]></programlisting>
+
+ <programlisting><![CDATA[<class name="Foo" table="foos" check="bar < 100.0">
+ ...
+ <property name="bar" type="float"/>
+</class>]]></programlisting>
+
+
+ <table frame="topbot" id="schemattributes-summary" revision="2">
+ <title>Summary</title>
+ <tgroup cols="3">
+ <colspec colwidth="1*"/>
+ <colspec colwidth="1*"/>
+ <colspec colwidth="2.5*"/>
+ <thead>
+ <row>
+ <entry>Attribut</entry>
+ <entry>Valeur</entry>
+ <entry>Interprétation</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>length</literal></entry>
+ <entry>numérique</entry>
+ <entry>taille d'une colonne</entry>
+ </row>
+ <row>
+ <entry><literal>precision</literal></entry>
+ <entry>numérique</entry>
+ <entry>précision décimale de la colonne</entry>
+ </row>
+ <row>
+ <entry><literal>scale</literal></entry>
+ <entry>numérique</entry>
+ <entry>scale décimale de la colonne</entry>
+ </row>
+ <row>
+ <entry><literal>not-null</literal></entry>
+ <entry><literal>true|false</literal></entry>
+ <entry>spécifie que la colonne doit être non-nulle</entry>
+ </row>
+ <row>
+ <entry><literal>unique</literal></entry>
+ <entry><literal>true|false</literal></entry>
+ <entry>spécifie que la colonne doit avoir une contrainte d'unicité</entry>
+ </row>
+ <row>
+ <entry><literal>index</literal></entry>
+ <entry><literal>index_name</literal></entry>
+ <entry>spécifie le nom d'un index (multi-colonnes)</entry>
+ </row>
+ <row>
+ <entry><literal>unique-key</literal></entry>
+ <entry><literal>unique_key_name</literal></entry>
+ <entry>spécifie le nom d'une contrainte d'unicité multi-colonnes</entry>
+ </row>
+ <row>
+ <entry><literal>foreign-key</literal></entry>
+ <entry><literal>foreign_key_name</literal></entry>
+ <entry>
+ spécifie le nom d'une contrainte de clé étrangère générée pour
+ une association, utilisez-la avec les éléments de mapping
+ <one-to-one>, <many-to-one>, <key>, et <many-to-many>
+ Notez que les extrêmités <literal>inverse="true"</literal>
+ se seront pas prises en compte par <literal>SchemaExport</literal>.
+ </entry>
+ </row>
+ <row>
+ <entry><literal>sql-type</literal></entry>
+ <entry><literal>SQL column_type</literal></entry>
+ <entry>
+ surcharge le type par défaut (attribut de
+ l'élément <literal><column></literal> uniquement)
+ </entry>
+ </row>
+ <row>
+ <entry><literal>default</literal></entry>
+ <entry>expression SQL</entry>
+ <entry>
+ spécifie une valeur par défaut pour la colonne
+ </entry>
+ </row>
+ <row>
+ <entry><literal>check</literal></entry>
+ <entry>SQL expression</entry>
+ <entry>
+ crée une contrainte de vérification sur la table ou la colonne
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ L'élément <literal><comment></literal> vous permet de spécifier un commentaire pour le schéma généré.
+ </para>
+
+ <programlisting><![CDATA[<class name="Customer" table="CurCust">
+ <comment>Current customers only</comment>
+ ...
+</class>]]></programlisting>
+
+ <programlisting><![CDATA[<property name="balance">
+ <column name="bal">
+ <comment>Balance in USD</comment>
+ </column>
+</property>]]></programlisting>
+
+ <para>
+ Ceci a pour résultat une expression
+ <literal>comment on table</literal> ou
+ <literal>comment on column</literal> dans la DDL générée (où supportée).
+ </para>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-3" revision="2">
+ <title>Exécuter l'outil</title>
+
+ <para>
+ L'outil <literal>SchemaExport</literal> génère un script DDL vers
+ la sortie standard et/ou exécute les ordres DDL.
+ </para>
+
+ <para>
+ <literal>java -cp </literal><emphasis>classpath_hibernate</emphasis>
+ <literal>net.sf.hibernate.tool.hbm2ddl.SchemaExport</literal> <emphasis>options fichiers_de_mapping</emphasis>
+ </para>
+
+ <table frame="topbot">
+ <title><literal>SchemaExport</literal> Options de la ligne de commande</title>
+ <tgroup cols="2">
+ <colspec colwidth="1.5*"/>
+ <colspec colwidth="2*"/>
+ <thead>
+ <row>
+ <entry>Option</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>--quiet</literal></entry>
+ <entry>ne pas écrire le script vers la sortie standard</entry>
+ </row>
+ <row>
+ <entry><literal>--drop</literal></entry>
+ <entry>supprime seuleument les tables</entry>
+ </row>
+ <row>
+ <entry><literal>--create</literal></entry>
+ <entry>ne créé que les tables</entry>
+ </row>
+ <row>
+ <entry><literal>--text</literal></entry>
+ <entry>ne pas exécuter sur la base de données</entry>
+ </row>
+ <row>
+ <entry><literal>--output=my_schema.ddl</literal></entry>
+ <entry>écrit le script ddl vers un fichier</entry>
+ </row>
+ <row>
+ <entry><literal>--naming=eg.MyNamingStrategy</literal></entry>
+ <entry>sélectionne une <literal>NamingStrategy</literal></entry>
+ </row>
+ <row>
+ <entry><literal>--config=hibernate.cfg.xml</literal></entry>
+ <entry>lit la configuration Hibernate à partir d'un fichier XML</entry>
+ </row>
+ <row>
+ <entry><literal>--properties=hibernate.properties</literal></entry>
+ <entry>lit les propriétés de la base de données à partir d'un fichier</entry>
+ </row>
+ <row>
+ <entry><literal>--format</literal></entry>
+ <entry>formatte proprement le SQL généré dans le script</entry>
+ </row>
+ <row>
+ <entry><literal>--delimiter=x</literal></entry>
+ <entry>paramètre un délimiteur de fin de ligne pour le script</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ Vous pouvez même intégrer <literal>SchemaExport</literal> dans votre application :
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = ....;
+new SchemaExport(cfg).create(false, true);]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-4">
+ <title>Propriétés</title>
+
+ <para>
+ Les propriétés de la base de données peuvent être spécifiées
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>comme propriétés système avec <literal>-D</literal><emphasis><property></emphasis></para>
+ </listitem>
+ <listitem>
+ <para>dans <literal>hibernate.properties</literal></para>
+ </listitem>
+ <listitem>
+ <para>dans un fichier de propriétés déclaré avec <literal>--properties</literal></para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Les propriétés nécessaires sont :
+ </para>
+
+ <table frame="topbot">
+ <title>SchemaExport Connection Properties</title>
+ <tgroup cols="2">
+ <colspec colwidth="1.5*"/>
+ <colspec colwidth="2*"/>
+ <thead>
+ <row>
+ <entry>Nom de la propriété</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>hibernate.connection.driver_class</literal></entry>
+ <entry>classe du driver JDBC</entry>
+ </row>
+ <row>
+ <entry><literal>hibernate.connection.url</literal></entry>
+ <entry>URL JDBC</entry>
+ </row>
+ <row>
+ <entry><literal>hibernate.connection.username</literal></entry>
+ <entry>utilisateur de la base de données</entry>
+ </row>
+ <row>
+ <entry><literal>hibernate.connection.password</literal></entry>
+ <entry>mot de passe de l'utilisateur</entry>
+ </row>
+ <row>
+ <entry><literal>hibernate.dialect</literal></entry>
+ <entry>dialecte</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-5">
+ <title>Utiliser Ant</title>
+
+ <para>
+ Vous pouvez appeler <literal>SchemaExport</literal> depuis votre script
+ de construction Ant :
+ </para>
+
+ <programlisting><![CDATA[<target name="schemaexport">
+ <taskdef name="schemaexport"
+ classname="org.hibernate.tool.hbm2ddl.SchemaExportTask"
+ classpathref="class.path"/>
+
+ <schemaexport
+ properties="hibernate.properties"
+ quiet="no"
+ text="no"
+ drop="no"
+ delimiter=";"
+ output="schema-export.sql">
+ <fileset dir="src">
+ <include name="**/*.hbm.xml"/>
+ </fileset>
+ </schemaexport>
+</target>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-6" revision="2">
+ <title>Mises à jour incrémentales du schéma</title>
+
+ <para>
+ L'outil <literal>SchemaUpdate</literal> mettra à jour un schéma existant
+ en effectuant les changement par "incrément".
+ Notez que <literal>SchemaUpdate</literal> dépends beaucoup de l'API JDBC
+ metadata, il ne fonctionnera donc pas avec tous les drivers JDBC.
+ </para>
+
+ <para>
+ <literal>java -cp </literal><emphasis>classpath_hibernate</emphasis>
+ <literal>net.sf.hibernate.tool.hbm2ddl.SchemaUpdate</literal> <emphasis>options fichiers_de_mapping</emphasis>
+ </para>
+
+ <table frame="topbot">
+ <title><literal>SchemaUpdate</literal> Options de ligne de commande</title>
+ <tgroup cols="2">
+ <colspec colwidth="1.5*"/>
+ <colspec colwidth="2*"/>
+ <thead>
+ <row>
+ <entry>Option</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>--quiet</literal></entry>
+ <entry>ne pas écrire vers la sortie standard</entry>
+ </row>
+ <row>
+ <entry><literal>--text</literal></entry>
+ <entry>ne pas exporter vers la base de données</entry>
+ </row>
+ <row>
+ <entry><literal>--naming=eg.MyNamingStrategy</literal></entry>
+ <entry>choisit une <literal>NamingStrategy</literal></entry>
+ </row>
+ <row>
+ <entry><literal>--properties=hibernate.properties</literal></entry>
+ <entry>lire les propriétés de la base de données à partir d'un fichier</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ Vous pouvez intégrer <literal>SchemaUpdate</literal> dans votre application :
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = ....;
+new SchemaUpdate(cfg).execute(false);]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-7">
+ <title>Utiliser Ant pour des mises à jour de schéma par incrément</title>
+
+ <para>
+ Vous pouvez appeler <literal>SchemaUpdate</literal> depuis le script Ant :
+ </para>
+
+ <programlisting><![CDATA[<target name="schemaupdate">
+ <taskdef name="schemaupdate"
+ classname="org.hibernate.tool.hbm2ddl.SchemaUpdateTask"
+ classpathref="class.path"/>
+
+ <schemaupdate
+ properties="hibernate.properties"
+ quiet="no">
+ <fileset dir="src">
+ <include name="**/*.hbm.xml"/>
+ </fileset>
+ </schemaupdate>
+</target>]]></programlisting>
+
+ <sect2 id="toolsetguide-s1-8" revision="1">
+ <title>Validation du schéma</title>
+
+ <para>
+ L'outil <literal>SchemaValidator</literal> validera que le schéma existant correspond à vos documents de mapping.
+ Notez que le <literal>SchemaValidator</literal> dépends de l'API metadata de JDBC, il ne fonctionnera
+ donc pas avec tous les drivers JDBC. Cet outil est extrêmement utile pour tester.
+ </para>
+
+ <para>
+ <literal>java -cp </literal><emphasis>hibernate_classpaths</emphasis>
+ <literal>org.hibernate.tool.hbm2ddl.SchemaValidator</literal> <emphasis>options mapping_files</emphasis>
+ </para>
+
+ <table frame="topbot">
+ <title><literal>SchemaValidator</literal> Options de ligne de commande</title>
+ <tgroup cols="2">
+ <colspec colwidth="1.5*"/>
+ <colspec colwidth="2*"/>
+ <thead>
+ <row>
+ <entry>Option</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>--naming=eg.MyNamingStrategy</literal></entry>
+ <entry>Indique une <literal>NamingStrategy</literal></entry>
+ </row>
+ <row>
+ <entry><literal>--properties=hibernate.properties</literal></entry>
+ <entry>lit les propriétés dela base de données depuis un fichier de propriétés</entry>
+ </row>
+ <row>
+ <entry><literal>--config=hibernate.cfg.xml</literal></entry>
+ <entry>indique un fichier <literal>.cfg.xml</literal></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ Vous pouvez inclure <literal>SchemaValidator</literal> dans votre application:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = ....;
+new SchemaValidator(cfg).validate();]]></programlisting>
+
+ </sect2>
+
+ </sect2>
+ <sect2 id="toolsetguide-s1-9">
+ <title>Utiliser Ant pour la validation du Schéma</title>
+
+ <para>
+ Vous pouvez appeler <literal>SchemaValidator</literal> depuis le script Ant:
+ </para>
+
+ <programlisting><![CDATA[<target name="schemavalidate">
+ <taskdef name="schemavalidator"
+ classname="org.hibernate.tool.hbm2ddl.SchemaValidatorTask"
+ classpathref="class.path"/>
+
+ <schemavalidator
+ properties="hibernate.properties">
+ <fileset dir="src">
+ <include name="**/*.hbm.xml"/>
+ </fileset>
+ </schemaupdate>
+</target>]]></programlisting>
+
+ </sect2>
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/transactions.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/transactions.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/transactions.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1074 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<chapter id="transactions" revision="2">
+ <title>Transactions et accès concurrents</title>
+
+ <para>
+ L'un des principaux avantages du mécanisme de contrôle des accès concurrents d'Hibernate est qu'il est très
+ facile à comprendre. Hibernate utilise directement les connexions JDBC ainsi que les ressources JTA sans y
+ ajouter davantage de mécanisme de blocage. Nous vous recommandons de vous familiariser avec les spécifications
+ JDBC, ANSI et d'isolement de transaction de la base de données que vous utilisez.
+ </para>
+
+ <para>
+ Hibernate ne vérouille pas vos objets en mémoire. Votre application peut suivre le
+ comportement défini par le niveau d'isolation de vos transactions de base de données.
+ Notez que grâce à la <literal>Session</literal>, qui est aussi un cache de scope transaction, Hibernate
+ fournit des lectures répétées pour les récupération par identifiants et les requêtes
+ d'entités (pas celle de valeurs scalaires).
+ </para>
+
+ <para>
+ En addition au versionning pour le controle automatique de concurrence, Hibernate fournit
+ une API (mineure) pour le verrouillage perssimiste des enregistrements, en générant
+ une syntaxe <literal>SELECT FOR UPDATE</literal>. Le controle de concurrence optimiste
+ et cette API seront détaillés plus tard dans ce chapitre.
+ </para>
+
+ <para>
+ Nous aborderons la gestion des accès concurrents en discutant de la granularité des objets <literal>Configuration</literal>,
+ <literal>SessionFactory</literal>, et <literal>Session</literal>, ainsi que de certains concepts relatifs à la base de données
+ et aux longues transactions applicatives.
+ </para>
+
+ <sect1 id="transactions-basics" revision="1">
+ <title>Gestion de session et délimitation de transactions</title>
+
+ <para>Il est important de savoir qu'un objet <literal>SessionFactory</literal> est un objet complexe et optimisé pour
+ fonctionner avec les threads(thread- safe). Il est coûteux à créer et est ainsi prévu pour n'être instancié qu?une
+ seule fois via un objet <literal>Configuration</literal> au démarrage de l'application,
+ et être partagé par tous les threads d'une application.
+ </para>
+
+ <para>Un objet <literal>Session</literal> est relativement simple et n'est threadsafe. Il est également peu
+ coûteux à créer. Il devrait n'être utilisé qu'une seule fois, pour un processus d'affaire ou une unité de
+ travail ou une conversation et ensuite être relâché. Un objet <literal>Session</literal> ne tentera pas
+ d'obtenir de connexion ( <literal>Connection</literal> )
+ JDBC (ou de <literal>Datasource</literal> ) si ce n'est pas nécessaire.
+ </para>
+
+ <para>Afin de compléter ce tableau, vous devez également penser aux transactions de base de données. Une
+ transaction de base de données se doit d'être la plus courte possible afin de réduire les risques de
+ collision sur des enregistrements verrouillés. De longues transactions à la base de données nuiront à
+ l'extensibilité de vos applications lorsque confrontées à de hauts niveaux de charge. Par conséquent,
+ il n'est jamais bon de maintenir une transaction ouverte pendant la durée de reflexion de l'utilisateur,
+ jusqu'a ce que l'unité de travail soit achevée.
+ </para>
+
+ <para>Maintenant, comment délimiter une unité de travail? Est-ce qu'une instance de <literal>Session</literal> peut avoir une durée
+ de vie dépassant plusieurs transactions à la base de données, ou bien est-ce que celles-ci doivent être liées une à une?
+ Quand faut-il ouvrir et fermer une <literal>Session</literal> ? Comment définir la démarcation de vos transactions à la base de données?
+ </para>
+
+ <sect2 id="transactions-basics-uow" revision="1">
+ <title>Unité de travail</title>
+
+ <para>
+ Il est important de mentionner que d'utiliser un paradigme <emphasis>session-par-operation</emphasis>
+ est un anti-pattern. Autrement dit: n'ouvrez et ne fermez pas la
+ <literal>Session</literal> à chacun de vos accès simples à la base de données dans un même thread! Bien sûr, le même raisonnement
+ s'applique sur la gestion des transactions à la base de données. Les appels à la base de données
+ devraient être faits en ordre et selon une séquence définie. Ils devraient également être regroupés en
+ des unités de travail atomiques. (Notez que l?utilisation d?une connexion auto-commit constitue le même
+ anti-pattern. Ce mode de fonctionnement existe pour les applications émettant des commandes SQL à partir
+ d?une console. Hibernate désengage le mode auto-commit et s'attend à ce qu'un serveur d'applications le
+ fasse également.)
+ Les transactions avec la base de données ne sont jamais optionnelles, toute communication
+ avec une base de données doit se dérouler dans une transaction, peu importe si vous lisez
+ ou écrivez des données. Comme évoqué, le comportement auto-commit pour lire les
+ données devrait être évité, puisque plusieurs petites transactions ne seront jamais
+ aussi efficaces qu'une seule plus grosse clairement définie comme unité de travail.
+ Ce dernier choix et en plus beaucoup plus facile a maintenir et à faire évoluer.
+ </para>
+
+ <para>
+ Le pattern d'utilisation le plus fréquemment rencontré dans des applications clients serveur
+ multi-usagers est le <emphasis>session-per-request</emphasis>
+ (littéralement : Session par requête). Dans ce modèle, la requête d'un client est envoyée à un serveur
+ (Où la couche de persistance est implémentée via Hibernate), une nouvelle
+ <literal>Session</literal> est ouverte et toutes les opérations d'accès à la base de données sont exécutées à l'intérieur de
+ celle-ci. Lorsque le travail est terminé (et que les réponses à envoyer au client ont été préparées), la
+ session est flushée et fermée. Une seule transaction à la base de données peut être utilisée pour répondre
+ à la requête du client. La transaction est démarrée et validée au même moment où la Session est ouverte
+ et fermée. La relation entre la <literal>Session</literal> et la <literal>Transaction</literal> est donc one-to-one.
+ Ce modèle permet de répondre parfaitement aux attentes de la grande majorité des
+ applications.
+ </para>
+
+ <para>
+ Le défi réside dans l'implémentation. Hibernate fournit une fonction de gestion de
+ la "session courante" pour simplifier ce pattern. Tout ce que vous devez faire
+ est démarrer une transaction lorsqu'une requête est traitée par le serveur, et
+ la terminer avant que la réponse ne soit envoyée au client. Vous pouvez le faire
+ de la manière que vous voulez, les solutions communes sont un <literal>ServletFilter</literal>,
+ l'interception via AOP avec une pointcut sur les méthodes de type "service", ou un conteneur
+ avec interception/proxy. Un conteneur EJB est un moyen standard d'implémenter ce genre d'acpect
+ tranverse comme la démarcation des transactions sur les EJBs session, de manière déclarative
+ avec CMT. Si vous décidez d'utiliser la démarcation programmatique des transactions, préferrez
+ l'API Hibernate <literal>Transaction</literal> détaillée plus tard dans ce chapitre, afin de
+ facilité l'utilisation et la portabilité du code.
+ </para>
+
+ <para>
+ Votre application peut accéder la "session courante" pour exécuter une requête
+ en invoquant simplement <literal>sessionFactory.getCurrentSession()</literal> n'importe où
+ et autant de fois que souhaité. Vous obtiendrez toujours une <literal>Session</literal>
+ dont le scope est la transaction courante avec la base de données. Ceci doit être configuré
+ soit dans les ressources local ou dans l'environnement JTA, voir <xref linkend="architecture-current-session"/>.
+ </para>
+
+ <para>
+ Il est parfois utile d'étendre le scope d'une <literal>Session</literal> et d'une transaction
+ à la base de données jusqu'à ce que "la vue soit rendue". Ceci est particulièrement
+ utile dans des applications à base de servlet qui utilisent une phase de rendue séparée une fois
+ que la réponse a été préparée. Etendre la transaction avec la base de données jusqu'à la fin du
+ rendering de la vue est aisé si vous implémentez votre propre intercepteur. Cependant,
+ ce n'est pas facile si vous vous appuyez sur les EJBs avec CMT, puisqu'une transaction sera
+ achevée au retour de la méthode EJB, avant le rendu de la vue. Rendez vous sur le site
+ Hibernate et sur le forum pour des astuces et des exemples sur le pattern
+ <emphasis>Open Session in View</emphasis> pattern..
+ </para>
+ </sect2>
+
+ <sect2 id="transactions-basics-apptx" revision="1">
+ <title>Longue conversation</title>
+
+ <para>Le paradigme
+ <emphasis>session-per-request</emphasis>
+ n'est pas le seul élément à utiliser dans le design de vos unités de travail. Plusieurs processus
+ d'affaire requièrent toute une série d'interactions avec l'utilisateur, entrelacées d'accès à la base de
+ donnée. Dans une application Web ou une application d'entreprise, il serait inacceptable que la durée de
+ vie d'une transaction s'étale sur plusieurs interactions avec l'usager. Considérez l'exemple suivant:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>Un écran s'affiche. Les données vues par l'usager ont été chargées dans l'instance d'un objet
+ <literal>Session</literal> , dans le cadre d'une transaction de base de données. L'usager est libre de modifier ces objets.
+ </para>
+ </listitem>
+ <listitem>
+
+ <para>L'usager clique "Sauvegarder" après 5 minutes et souhaite persister les modifications qu'il a
+ apportées. Il s'attend à être la seule personne a avoir modifié ces données et qu'aucune
+ modification conflictuelle ne se soit produite durant ce laps de temps.</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>Ceci s'appelle une unité de travail. Du point de vue de l'utilisateur: une
+ <emphasis>conversation</emphasis> (ou <emphasis>transaction d'application</emphasis>).
+ Il y a plusieurs façon de mettre ceci en place dans votre application.
+ </para>
+
+ <para>Une première implémentation naïve pourrait consister à garder la
+ <literal>Session</literal> et la transaction à la base de données ouvertes durant le temps de travail de l'usager, à maintenir les
+ enregistrements verrouillés dans la base de données afin d'éviter des modifications concurrentes et de
+ maintenir l'isolation et l'atomicité de la transaction de l'usager. Ceci est un anti-pattern à éviter,
+ puisque le verrouillage des enregistrements dans la base de données ne permettrait pas à l'application
+ de gérer un grand nombre d'usagers concurrents.
+ </para>
+
+ <para>Il apparaît donc évident qu'il faille utiliser plusieurs transactions BDD afin d'implémenter la
+ conversation. Dans ce cas, maintenir l'isolation des processus d'affaire devient
+ partiellement la responsabilité de la couche applicative. Ainsi, la durée de vie d'une conversation
+ devrait englober celle d'une ou de plusieurs transactions de base de données. Celle-ci sera
+ atomique seulement si l'écriture des données mises à jour est faite exclusivement par la dernière
+ transaction BDD la composant. Toutes les autres sous transactions BD ne doivent faire que la lecture de
+ données. Ceci est relativement facile à mettre en place, surtout avec l'utilisation de certaines
+ fonctionnalités d'Hibernate:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Versionnage Automatique</emphasis>
+ - Hibernate peut gérer automatiquement les accès concurrents de manière optimiste et détecter si
+ une modification concurrente s'est produite durant le temps de réflexion d'un usager.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Objets Détachés</emphasis>
+ - Si vous décidez d'utiliser le paradigme
+ <emphasis>session-par-requête</emphasis>
+ discuté plus haut, toutes les entités chargées en mémoire deviendront des objets détachés durant
+ le temps de réflexion de l'usager. Hibernate vous permet de rattacher ces objets et de persister
+ les modifications y ayant été apportées. Ce pattern est appelé:
+ <emphasis>session-per- request-with-detached-objects</emphasis>
+ (littéralement: session- par-requête-avec-objets-détachés). Le versionnage automatique est
+ utilisé afin d'isoler les modifications concurrentes.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Session Longues (conversation)</emphasis>
+ - Une
+ <literal>Session</literal> Hibernate peut être déconnectée de la couche JDBC sous-jacente après que commit() ait été appelé
+ sur une transaction à la base de données et reconnectée lors d'une nouvelle requête-client. Ce
+ pattern s'appelle:
+ <emphasis>session-per-conversation</emphasis>
+ (Littéralement: session-par- conversation) et rend superflu le rattachement des
+ objets. Le versionnage automatique est utilisé afin d'isoler les modifications concurrentes.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>Les deux patterns
+ <emphasis>session-per-request-with- detached- objects</emphasis>
+ (session-par-requête-avec-objets- détachés) et
+ <emphasis>session-per-conversation</emphasis>
+ (session-par-conversation) ont chacun leurs avantages et désavantages qui seront exposés
+ dans ce même chapitre, dans la section au sujet du contrôle optimiste de concurrence.
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-basics-identity">
+ <title>L'identité des objets</title>
+
+ <para>Une application peut accéder à la même entité persistante de manière concurrente dans deux
+ <literal>Session</literal> s différentes. Toutefois, une instance d'une classe persistante n'est jamais partagée par deux instances
+ distinctes de la classe
+ <literal>Session</literal> . Il existe donc deux notions de l'identité d'un objet:
+ </para>
+
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term>Identité BD</term>
+ <listitem>
+ <para>
+ <literal>foo.getId().equals( bar.getId() )</literal> </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Identité JVM</term>
+ <listitem>
+ <para>
+ <literal>foo==bar</literal> </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>Ainsi, pour des objets attachés à une
+ <literal>Session</literal> <emphasis>précise</emphasis>
+ (dans la cadre d'exécution (scope) d'une instance de
+ <literal>Session</literal> ), ces deux notions d'identité sont équivalentes et garanties par Hibernate. Par contre, si une
+ application peut accéder de manière concurrente à la même entité persistante dans deux sessions
+ différentes, les deux instances seront en fait différentes (en ce qui a trait à l'identité JVM). Les
+ conflits sont résolus automatiquement par approche optimiste grâce au système de versionnage automatique
+ lorsque
+ <literal>Session.flush()</literal> ou
+ <literal>Transaction.commit()</literal> est appelé.
+ </para>
+
+ <para>Cette approche permet de reléguer à Hibernate et à la base de données sous-jacente le soin de gérer
+ les problèmes d'accès concurrents. Cette manière de faire assure également une meilleure extensibilité
+ de l'application puisque assurer l'identité JVM dans un thread ne nécessite pas de mécanismes de
+ verrouillage coûteux ou d'autres dispositifs de synchronisation. Une application n'aura jamais le besoin
+ de synchroniser des objets d'affaire tant qu'elle peut garantir qu'un seul thread aura accès à une
+ instance de
+ <literal>Session</literal> . Dans le cadre d'exécution d'un objet
+ <literal>Session</literal> , l'application peut utiliser en toute sécurité <literal>==
+ </literal> pour comparer des objets.
+ </para>
+
+ <para>
+ Une application qui utiliserait <literal>==</literal> à l'extérieur du cadre d'exécution d'une <literal>Session</literal>
+ pourrait obtenir des résultats inattendus et causer certains effets de bords. Par exemple, si vous mettez 2
+ objets dans le même <literal>Set</literal> , ceux-ci pourraient avoir la même identité BD (i.e. ils représentent le même enregistrement), mais leur
+ identité JVM pourrait être différente (elle ne peut, par définition, pas être garantie sur deux objets
+ détachés). Le développeur doit donc redéfinir l'implémentation des méthodes <literal>equals()</literal> et <literal>hashcode()</literal>
+ dans les classes persistantes et y adjoindre sa propre notion d'identité. Il existe toutefois une
+ restriction: Il ne faut jamais utiliser uniquement l'identifiant de la base de données dans l'implémentation
+ de l'égalité; Il faut utiliser une clé d'affaire, généralement une combinaison de plusieurs attributs
+ uniques, si possible immuables. Les identifiants de base de données vont changer si un objet transitoire
+ (transient) devient persistant. Si une instance transitoire est contenue dans un <literal>Set</literal> ,
+ changer le hashcode brisera le contrat du <literal>Set</literal> . Les attributs pour les clés d'affaire
+ n'ont pas à être aussi stables que des clés primaires de bases de
+ données. Il suffit simplement qu'elles soient stables tant et aussi longtemps que les objets sont dans le
+ même <literal>Set</literal> . Veuillez consulter le site web Hibernate pour des discussions plus pointues à ce sujet. Notez que ce
+ concept n'est pas propre à Hibernate mais bien général à l'implémentation de l'identité et de l'égalité en
+ Java.
+ </para>
+ </sect2>
+
+
+ <sect2 id="transactions-basics-issues">
+ <title>Problèmes communs</title>
+
+ <para>Bien qu'il puisse y avoir quelques rares exceptions à cette règle, il est recommandé de ne jamais utiliser
+ les anti-patterns
+ <emphasis>session-per- user-session</emphasis>
+ et
+ <emphasis>session-per-application</emphasis>
+ . Vous trouverez ici- bas quelques problèmes que vous risquez de rencontrer si vous en faite l?utilisation.
+ (Ces problèmes pourraient quand même survenir avec des patterns recommandés) Assurez-vous de bien comprendre
+ les implications de chacun des patterns avant de prendre votre décision.
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>L'objet
+ <literal>Session</literal> n?est pas conçu pour être utilisé par de multiples threads. En conséquence, les objets
+ potentiellement multi-thread comme les requêtes HTTP, les EJB Session et Swing Worker, risquent de
+ provoquer des conditions de course dans la
+ <literal>Session</literal> si celle-ci est partagée. Dans un environnement web classique, il serait préférable de synchroniser
+ les accès à la session http afin d?éviter qu?un usager ne recharge une page assez rapidement pour
+ que deux requêtes s?exécutant dans des threads concurrents n?utilisent la même
+ <literal>Session</literal> .
+ </para>
+ </listitem>
+ <listitem>
+ <para>Lorsque Hibernate lance une exception, le roll back de la transaction en cours doit être effectué
+ et la
+ <literal>Session</literal> doit être immédiatement fermée. (Ceci sera exploré plus tard dans le chapitre.) Si la
+ <literal>Session</literal> est directement associée à une application, il faut arrêter l?application. Le roll back de la
+ transaction ne remettra pas les objets dans leur état du début de la transaction. Ainsi, ceux-ci
+ pourraient être désynchronisés d?avec les enregistrements. (Généralement, cela ne cause pas de réels
+ problèmes puisque la plupart des exceptions sont non traitables et requièrent la reprise du
+ processus d?affaire ayant échoué.)
+ </para>
+ </listitem>
+ <listitem>
+ <para>La
+ <literal>Session</literal> met en mémoire cache tous les objets persistants (les objets surveillés et dont l'état est géré par
+ Hibernate.) Si la
+ <literal>Session</literal> est ouverte indéfiniment ou si une trop grande quantité d'objets y est chargée, l?utilisation de la
+ mémoire peut potentiellement croître jusqu?à atteindre le maximum allouable à l?application
+ (java.lang.OutOfMemoryError.) Une solution à ce problème est d?appeler les méthodes
+ <literal>Session.clear()</literal> et
+ <literal>Session.evict()</literal> pour gérer la mémoire cache de la
+ <literal>Session</literal> . Vous pouvez également utiliser des stored procedures si vous devez lancer des traitements sur de
+ grandes quantités d?informations. Certaines solutions sont décrites ici :
+ <xref linkend="batch"/>
+ . Garder une
+ <literal>Session</literal> ouverte pour toute la durée d?une session usager augmente également considérablement le risque de
+ travailler avec de l?information périmée.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect2>
+</sect1>
+
+<sect1 id="transactions-demarcation">
+ <title>Démarcation des transactions</title>
+
+ <para>La démarcation des transactions est importante dans le design d?une application. Aucune communication avec la
+ base de données ne peut être effectuée à l?extérieur du cadre d?une transaction. (Il semble que ce concept soit
+ mal compris par plusieurs développeurs trop habitués à utiliser le mode auto-commit.) Même si certains niveaux
+ d'isolation et certaines possibilités offertes par les bases de données permettent de l?éviter, il n'est jamais
+ désavantageux de toujours explicitement indiquer les bornes de transaction pour les opérations complexes comme
+ pour les opérations simples de lecture.</para>
+
+ <para>Une application utilisant Hibernate peut s'exécuter dans un environnement léger n?offrant pas la gestion
+ automatique des transactions (application autonome, application web simple ou applications Swing) ou dans un
+ environnement J2EE offrant des services de gestion automatique des transactions JTA. Dans un environnement
+ simple, Hibernate a généralement la responsabilité de la gestion de son propre pool de connexions à la base de
+ données. Le développeur de l'application doit manuellement délimiter les transactions. En d'autres mots, il
+ appartient au développeur de gérer les appels à
+ <literal>Transaction.begin()</literal>
+ ,
+ <literal>Transaction.commit()</literal>
+ et
+ <literal>Transaction.rollback()</literal>
+ . Un environnement transactionnel J2EE (serveur d'application J2EE) doit offrir la gestion des transactions au
+ niveau du container J2EE. Les bornes de transaction peuvent normalement être définies de manière déclarative
+ dans les descripteurs de déploiement d'EJB Session, par exemple. La gestion programmatique des transactions n'y
+ est donc pas nécessaire. Même les appels à
+ <literal>Session.flush()</literal>
+ sont faits automatiquement.
+ </para>
+
+ <para>Il peut être requis d'avoir une couche de persistance portable. Hibernate offre donc une API appelée
+ <literal>Transaction</literal>
+ qui sert d'enveloppe pour le système de transaction natif de l'environnement de déploiement. Il n'est pas
+ obligatoire d'utiliser cette API mais il est fortement conseillé de le faire, sauf lors de l'utilisation de CMT
+ Session Bean (EJB avec transactions gérées automatiquement par le container EJB).
+ </para>
+
+ <para>Il existe quatre étapes disctinctes lors de la fermeture d'une
+ <literal>Session</literal>
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>flush de la session</para>
+ </listitem>
+ <listitem>
+ <para>commit de la transaction</para>
+ </listitem>
+ <listitem>
+ <para>Fermeture de la session (Close)</para>
+ </listitem>
+ <listitem>
+ <para>Gestion des exceptions</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>La synchronisation de bdd depuis la session (flush) a déjà été expliqué, nous nous attarderons maintenant à la démarcation des
+ transactions et à la gestion des exceptions dans les environnements légers et les environnements J2EE.</para>
+
+
+ <sect2 id="transactions-demarcation-nonmanaged" revision="2">
+ <title>Environnement non managé</title>
+
+ <para>
+ Si la couche de persistance Hibernate s'exécute dans un environnement non managé, les connexions à la base de
+ données seront généralement prises en charge par le mécanisme de pool d'Hibernate. La gestion de la session
+ et de la transaction se fera donc de la manière suivante:</para>
+
+ <programlisting><![CDATA[// Non-managed environment idiom
+Session sess = factory.openSession();
+Transaction tx = null;
+try {
+ tx = sess.beginTransaction();
+
+ // do some work
+ ...
+
+ tx.commit();
+}
+catch (RuntimeException e) {
+ if (tx != null) tx.rollback();
+ throw e; // or display error message
+}
+finally {
+ sess.close();
+}]]></programlisting>
+
+ <para>
+ Vous n'avez pas à invoquer <literal>flush()</literal> explicitement sur la <literal>Session</literal> -
+ l'appel de <literal>commit()</literal> déclenchera automatiquement la synchronisation (selon le <xref linkend="objectstate-flushing">FlushMode</xref>
+ de la session. Un appel à <literal>close()</literal> marque la fin de la session.
+ La conséquence directe est que la connexion à la base de données sera relachée par la session.
+ Ce code est portable est fonctionne dans les environnements non managé ET les environnements JTA.
+ </para>
+
+ <para>
+ Une solution plus flexible est la gestion par contexte fourni par Hibernate que nous avons
+ déjà rencontré:
+ </para>
+
+ <programlisting><![CDATA[// Non-managed environment idiom with getCurrentSession()
+try {
+ factory.getCurrentSession().beginTransaction();
+
+ // do some work
+ ...
+
+ factory.getCurrentSession().getTransaction().commit();
+}
+catch (RuntimeException e) {
+ factory.getCurrentSession().getTransaction().rollback();
+ throw e; // or display error message
+}]]></programlisting>
+
+ <para>
+ Vous ne verrez probablement jamais ces exemples de code dans les applications;
+ les exceptions fatales (exceptions du système) ne devraient être traitées que
+ dans la couche la plus "haute". En d'autres termes, le code qui exécute les appels
+ à Hibernate (à la couche de persistance) et le code qui gère les
+ <literal>RuntimeException</literal> (qui ne peut généralement effectuer qu'un nettoyage et une sortie)
+ sont dans des couches différentes. La gestion du contexte courant par Hibernate peut
+ simplifier notablement ce design, puisque vous devez accéder à la gestion des exceptions
+ de la <literal>SessionFactory</literal>, ce qui est décrit plus tard dans ce chapitre.
+ </para>
+
+ <para>
+ Notez que vous devriez sélectionner <literal>org.hibernate.transaction.JDBCTransactionFactory</literal>
+ (le défaut), pour le second exemple <literal>"thread"</literal> comme
+ <literal>hibernate.current_session_context_class</literal>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-demarcation-jta" revision="2">
+ <title>Utilisation de JTA</title>
+
+ <para>Si votre couche de persistance s'exécute dans un serveur d'application (par exemple, derrière un EJB
+ Session Bean), toutes les datasource utilisées par Hibernate feront automatiquement partie de transactions
+ JTA globales. Hibernate propose deux stratégies pour réussir cette intégration.</para>
+
+ <para>
+ Si vous utilisez des transactions gérées par un EJB (bean managed transactions - BMT), Hibernate informera
+ le serveur d'application du début et de la fin des transactions si vous utilisez l'API <literal>Transaction</literal> .
+ Ainsi, le code de gestion des transactions sera identique dans les deux types d'environnements.
+ </para>
+
+ <programlisting><![CDATA[// BMT idiom
+Session sess = factory.openSession();
+Transaction tx = null;
+try {
+ tx = sess.beginTransaction();
+
+ // do some work
+ ...
+
+ tx.commit();
+}
+catch (RuntimeException e) {
+ if (tx != null) tx.rollback();
+ throw e; // or display error message
+}
+finally {
+ sess.close();
+}]]></programlisting>
+
+ <para>Ou encore, avec la gestion automatique de contexte:</para>
+
+ <programlisting><![CDATA[// BMT idiom with getCurrentSession()
+try {
+ factory.getCurrentSession().beginTransaction();
+
+ // do some work
+ ...
+
+ factory.getCurrentSession().getTransaction().commit();
+}
+catch (RuntimeException e) {
+ factory.getCurrentSession().getTransaction().rollback();
+ throw e; // or display error message
+}]]></programlisting>
+
+ <para>
+ Avec CMT, la démarcation des transactions est faite dans les descripteurs de déploiement des Beans Sessions et non
+ de manière programmmatique, ceci réduit le code:
+ </para>
+
+ <programlisting><![CDATA[// CMT idiom
+ Session sess = factory.getCurrentSession();
+
+ // do some work
+ ...
+]]></programlisting>
+
+ <para>
+ Dans un EJB CMT même le rollback intervient automatiquement, puisqu'une <literal>RuntimeException</literal>
+ non traitée et soulevée par une méthode d'un bean session indique au conteneur d'annuler la transaction
+ globale. <emphasis>Ceci veut donc dire que vous n'avez pas à utiliser l'API <literal>Transaction</literal> d'Hibernate
+ dans CMT.</emphasis>
+ </para>
+
+ <para>
+ Notez que le fichier de configuration Hibernate devrait contenir les valeurs
+ <literal>org.hibernate.transaction.JTATransactionFactory</literal> dans un environnement BMT ou
+ <literal>org.hibernate.transaction.CMTTransactionFactory</literal> dans un environnement CMT là où vous
+ configurez votre transaction factory Hibernate.
+ N'oubliez pas non plus de spécifier le paramètre <literal>org.hibernate.transaction.manager_lookup_class</literal> .
+ De plus, assurez vous de fixez votre <literal>hibernate.current_session_context_class</literal> soit à <literal>"jta"</literal>
+ ou de ne pas le configurer (compatibilité avec les versions précédentes).
+ </para>
+
+ <para>
+ La méthode <literal>getCurrentSession()</literal> a un inconvénient dans les environnement JTA.
+ Il y a une astuce qui est d'utiliser un mode de libération de connexion <literal>after_statement</literal> ,
+ qui est alors utilisé par défaut. Du à une étrange limitation de la spec JTA, il n'est pas possible
+ pour Hibernate de nettoyer et ferme automatiquement un <literal>ScrollableResults</literal> ouvert
+ ou une instance d'<literal>Iterator</literal> retournés <literal>scroll()</literal> ou
+ <literal>iterate()</literal>. Vous <emphasis>devez</emphasis> libérer le curseur base de données
+ sous jacent ou invoquer <literal>Hibernate.close(Iterator)</literal> explicitement depuis un
+ bloc <literal>finally</literal>. (Bien sur, la plupart des applications peuvent éviter
+ d'uiliser <literal>scroll()</literal> ou <literal>iterate()</literal> dans un code CMT.)
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-demarcation-exceptions">
+ <title>Gestion des exceptions</title>
+
+ <para>
+ Si une <literal>Session</literal> lance une exception (incluant les exceptions du type <literal>SQLException</literal>
+ ou d'un sous-type), vous devez immédiatement faire le rollback de la transaction, appeler <literal>Session.close()</literal>
+ et relâcher les références sur l'objet <literal>Session</literal> . La <literal>Session</literal> contient des méthodes
+ pouvant la mettre dans un état inutilisable. Vous devez considérer qu'<emphasis>aucune</emphasis>
+ exception lancée par Hibernate n'est traitable. Assurez-vous de fermer la session en faisant l'appel à
+ <literal>close()</literal> dans un bloc <literal>finally</literal> .
+ </para>
+
+ <para>
+ L'exception <literal>HibernateException</literal> , qui englobe la plupart des exceptions pouvant survenir dans la
+ couche de persistance Hibernate, est une exception non vérifiée (Ceci n'était pas le cas dans certaines versions antérieures de Hibernate.) Il est de
+ notre avis que nous ne devrions pas forcer un développeur à gérer une exception qu'il ne peut de toute façon
+ pas traiter dans une couche technique. Dans la plupart des applications, les exceptions non vérifiées et les
+ exceptions fatales sont gérées en amont du processus (dans les couches hautes) et un message d'erreur est
+ alors affiché à l'usager (ou un traitement alternatif est invoqué.) Veuillez noter qu'Hibernate peut
+ également lancer des exceptions non vérifiées d'un autre type que <literal>HibernateException</literal> . Celles-ci sont
+ également non traitables et vous devez les traiter comme telles.
+ </para>
+
+ <para>
+ Hibernate englobe les <literal>SQLException</literal> s lancées lors des interactions directes avec la base de données
+ dans des exceptions de type: <literal>JDBCException</literal> . En fait, Hibernate essaiera de convertir l'exception dans
+ un sous-type plus significatif de <literal>JDBCException</literal> . L'exception <literal>SQLException</literal> sous-jacente
+ est toujours disponible via la méthode <literal>JDBCException.getCause()</literal> . Cette conversion est faite par un objet
+ de type <literal>SQLExceptionConverter</literal> , qui est rattaché à l'objet <literal>SessionFactory</literal> .
+ Par défaut, le <literal>SQLExceptionConverter</literal> est associé au dialecte de BD configuré dans Hibernate. Toutefois,
+ il est possible de fournir sa propre implémentation de l'interface. (Veuillez vous référer à la javadoc sur la classe
+ <literal>SQLExceptionConverterFactory</literal> pour plus de détails. Les sous-types standard de <literal>JDBCException</literal> sont:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>JDBCConnectionException</literal> - Indique une erreur de communication avec la couche JDBC sous-jacente.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>SQLGrammarException</literal> - Indique un problème de grammaire ou de syntaxe avec la requête SQL envoyée.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>ConstraintViolationException</literal> - Indique une violation de contrainte d'intégrité.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>LockAcquisitionException</literal> - Indique une erreur de verrouillage lors de l'éxécution de la requête.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>GenericJDBCException</literal> - Indique une erreur générique JDBC d'une autre catégorie.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect2>
+
+ <sect2 id="transactions-demarcation-timeout">
+ <title>Timeout de transaction</title>
+
+ <para>L'un des avantages fournis par les environnements transactionnels JTA (tels les containers EJB) est la
+ gestion du timeout de transaction. La gestion des dépassements de temps de transaction vise à s'assurer
+ qu'une transaction agissant incorrectement ne viendra pas bloquer indéfiniment les ressources de
+ l'application. Hibernate ne peut fournir cette fonctionnalité dans un environnement transactionnel non-JTA.
+ Par contre, Hibernate gère les opérations d'accès aux données en allouant un temps maximal aux requêtes pour
+ s'exécuter. Ainsi, une requête créant de l'inter blocage ou retournant de très grandes quantités
+ d'information pourrait être interrompue. Dans un environnement transactionnel JTA, Hibernate peut déléguer
+ au gestionnaire de transaction le soin de gérer les dépassements de temps. Cette fonctionnalité est
+ abstraite par l'objet <literal>Transaction</literal> .
+ </para>
+
+ <programlisting><![CDATA[
+ Session sess = factory.openSession();
+ try {
+ //mettre le timeout à 3 secondes.
+ sess.getTransaction().setTimeout(3);
+ sess.getTransaction().begin();
+
+ // Effectuer le travail ...
+
+ sess.getTransaction().commit()
+ }
+ catch (RuntimeException e) {
+ if ( sess.getTransaction().isActive() ) {
+ sess.getTransaction().rollback();
+ }
+ throw e;
+ // ou afficher le message d'erreur.
+ }
+ finally {
+ sess.close();
+ }]]></programlisting>
+
+
+ <para>
+ Notez que <literal>setTimeout()</literal> ne peut pas être appelé d'un EJB CMT, puisque le timeout
+ des transaction doit être spécifié de manière déclarative.
+ </para>
+
+ </sect2>
+
+</sect1>
+
+<sect1 id="transactions-optimistic">
+ <title>Contrôle de consurrence optimiste</title>
+
+ <para>La gestion optimiste des accès concurrents avec versionnage est la seule approche pouvant garantir
+ l'extensibilité des applications à haut niveau de charge. Le système de versionnage utilise des numéros de
+ version ou l'horodatage pour détecter les mises à jour causant des conflits avec d'autres actualisations
+ antérieures. Hibernate propose trois approches pour l'écriture de code applicatif utilisant la gestion optimiste
+ d'accès concurrents. Le cas d'utilisation décrit plus bas fait mention de conversation,
+ mais le versionnage peut également améliorer la qualité d'une application en prévenant la perte de mises à
+ jour.</para>
+
+ <sect2 id="transactions-optimistic-manual">
+ <title>Gestion du versionnage au niveau applicatif</title>
+
+ <para>Dans cet exemple d'implémentation utilisant peu les fonctionnalités d'Hibernate, chaque interaction avec
+ la base de données se fait en utilisant une nouvelle <literal>Session</literal> et le développeur doit recharger
+ les données persistantes à partir de la BD avant de les manipuler. Cette
+ implémentation force l'application à vérifier la version des objets afin de maintenir l'isolation
+ transactionnelle. Cette approche, semblable à celle retrouvée pour les EJB, est la moins efficace de celles
+ présentées dans ce chapitre.
+ </para>
+
+ <programlisting>
+ <![CDATA[// foo est une instance chargée antérieurement par une autre
+ Session session = factory.openSession();
+ Transaction t = session.beginTransaction();
+
+ int oldVersion = foo.getVersion();
+ session.load( foo, foo.getKey() ); // Charger l'état courant
+
+ if ( oldVersion!=foo.getVersion )
+ throw new StaleObjectStateException();
+
+ foo.setProperty("bar");
+ t.commit();
+ session.close();]]></programlisting>
+
+ <para>Le mapping de la propriété <literal>version</literal> est fait via <literal><version></literal> et
+ Hibernate l'incrémentera automatiquement à chaque flush() si l'entité doit être mise à jour.
+ </para>
+
+
+ <para>Bien sûr, si votre application ne fait pas face à beaucoup d'accès concurrents et ne nécessite pas
+ l'utilisation du versionnage, cette approche peut également être utilisée, il n'y a qu'à ignorer le code
+ relié au versionnage. Dans ce cas, la stratégie du
+ <emphasis>last commit wins</emphasis>
+ (littéralement: le dernier commit l'emporte) sera utilisée pour les conversations (longues transactions applicatives).
+ Gardez à l'esprit que cette approche pourrait rendre perplexe les utilisateurs de l'application car ils
+ pourraient perdre des données mises à jour sans qu'aucun message d'erreur ne leur soit présenté et sans
+ avoir la possibilité de fusionner les données.
+ </para>
+
+ <para>Il est clair que la gestion manuelle de la vérification du versionnage des objets ne peut être effectuée
+ que dans certains cas triviaux et que cette approche n'est pas valable pour la plupart des applications. De
+ manière générale, les applications ne cherchent pas à actualiser de simples objets sans relations, elles le
+ font généralement pour de larges graphes d'objets. Pour toute application utilisant le paradigme des conversations
+ ou des objets détachés, Hibernate peut gérer automatiquement la vérification des versions
+ d'objets.</para>
+
+ </sect2>
+
+ <sect2 id="transactions-optimistic-longsession">
+ <title>Les sessions longues et le versionnage automatique.</title>
+
+ <para>Dans ce scénario, une seule instance de <literal>Session</literal> et des objets persistants est utilisée
+ pour toute l'application. Hibernate vérifie la version des objets
+ persistants avant d'effectuer le flush() et lance une exception si une modification concurrente est
+ détectée. Il appartient alors au développeur de gérer l'exception. Les traitements alternatifs généralement
+ proposés sont alors de permettre à l'usager de faire la fusion des données ou de lui offrir de recommencer
+ son travail à partie des données les plus récentes dans la BD.
+ </para>
+
+ <para>Il est à noter que lorsqu'une application est en attente d'une action de la part de l?usager, La <literal>Session</literal>
+ n'est pas connectée à la couche JDBC sous-jacente. C'est la manière la plus efficace de gérer les accès à la
+ base de données. L'application ne devrait pas se préoccuper du versionnage des objets, de la réassociation
+ des objets détachés, ni du rechargement de tous les objets à chaque transaction.
+ </para>
+
+ <programlisting>
+ <![CDATA[// foo est une instance chargée antérieurement par une autre session
+
+ session.reconnect();// Obtention d'une nouvelle connexion JDBC
+ Transaction t = session.beginTransaction();
+ foo.setProperty("bar");
+ t.commit(); //Terminer la transaction, propager les changements et vérifier les versions.
+ session.disconnect(); // Retourner la connexion JDBC
+ ]]></programlisting>
+
+
+ <para>L'objet <literal>foo</literal> sait quel objet <literal>Session</literal> l'a chargé. <literal>Session.reconnect()</literal>
+ obtient une nouvelle connexion (celle-ci peut être également fournie) et permet à la session de continuer
+ son travail. La méthode <literal>Session.disconnect()</literal> déconnecte la session de la connexion JDBC et
+ retourne celle-ci au pool de connexion (à moins que vous ne
+ lui ayez fourni vous même la connexion.) Après la reconnexion, afin de forcer la vérification du versionnage
+ de certaines entités que vous ne cherchez pas à actualiser, vous pouvez faire un appel à <literal>Session.lock()</literal>
+ en mode <literal>LockMode.READ</literal> pour tout objet ayant pu être modifié par une autre transaction. Il n'est pas nécessaire de verrouiller les
+ données que vous désirez mettre à jour.
+ </para>
+
+ <para>Si des appels implicites aux méthodes <literal>disconnect()</literal> et <literal>reconnect()</literal> sont trop
+ coûteux, vous pouvez les éviter en utilisant <literal>hibernate.connection.release_mode</literal> .
+ </para>
+
+ <para>Ce pattern peut présenter des problèmes si la <literal>Session</literal> est trop volumineuse pour être
+ stockée entre les actions de l'usager. Plus spécifiquement, une session <literal>HttpSession</literal> se doit
+ d'être la plus petite possible. Puisque la <literal>Session</literal> joue obligatoirement le rôle de mémoire
+ cache de premier niveau et contient à ce titre tous les objets
+ chargés, il est préférable de n'utiliser cette stratégie que pour quelques cycles de requêtes car les objets
+ risquent d'y être rapidement périmés.
+ </para>
+
+ <para>Notez que la <literal>Session</literal> déconnectée devrait être conservée près de la couche de persistance. Autrement dit, utilisez un EJB stateful
+ pour conserver la <literal>Session</literal> et évitez de la sérialiser et de la transférer à la couche de présentation (i.e. Il est préférable de ne pas
+ la conserver dans la session <literal>HttpSession</literal> .)
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-optimistic-detached">
+ <title>Les objets détachés et le versionnage automatique</title>
+
+ <para>Chaque interaction avec le système de persistance se fait via une nouvelle <literal>Session</literal> .
+ Toutefois, les mêmes instances d'objets persistants sont réutilisées pour chacune de ces interactions.
+ L'application doit pouvoir manipuler l'état des instances détachées ayant été chargées antérieurement via
+ une autre session. Pour ce faire, ces objets persistants doivent être rattachés à la <literal>Session</literal>
+ courante en utilisant <literal>Session.update()</literal> , <literal>Session.saveOrUpdate()</literal> , ou <literal>Session.merge()</literal> .
+ </para>
+
+ <programlisting>
+ <![CDATA[// foo est une instance chargée antérieurement par une autre session
+
+ foo.setProperty("bar");
+ session = factory.openSession();
+ Transaction t = session.beginTransaction();
+ session.saveOrUpdate(foo); //Utiliser merge() si "foo" pourrait avoir été chargé précédement
+ t.commit();
+ session.close();]]> </programlisting>
+
+
+ <para>Encore une fois, Hibernate vérifiera la version des instances devant être actualisées durant le flush().
+ Une exception sera lancée si des conflits sont détectés.</para>
+
+ <para>Vous pouvez également utiliser <literal>lock()</literal> au lieu de <literal>update()</literal> et
+ utiliser le mode <literal>LockMode.READ</literal> (qui lancera une vérification de version, en ignorant tous les niveaux de mémoire cache) si vous êtes
+ certain que l'objet n'a pas été modifié.
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-optimistic-customizing">
+ <title>Personnaliser le versionnage automatique</title>
+
+ <para>Vous pouvez désactiver l'incrémentation automatique du numéro de version de certains attributs et
+ collections en mettant la valeur du paramètre de mapping <literal>optimistic-lock</literal> à
+ false. Hibernate cessera ainsi d'incrémenter leur numéro de version s'ils sont mis à jour.
+ </para>
+
+ <para>Certaines entreprises possèdent de vieux systèmes dont les schémas de bases de données sont statiques et
+ ne peuvent être modifiés. Il existe aussi des cas où plusieurs applications doivent accéder à la même base
+ de données, mais certaines d'entre elles ne peuvent gérer les numéros de version ou les champs horodatés.
+ Dans les deux cas, le versionnage ne peut être implanté par le rajout d'une colonne dans la base de données.
+ Afin de forcer la vérification de version dans un système sans en faire le mapping, mais en forçant une
+ comparaison des états de tous les attributs d'une entité, vous pouvez utiliser l'attribut <literal>optimistic- lock="all"</literal>
+ sous l'élément <literal><class></literal> . Veuillez noter que cette manière de gérer le versionnage ne peut être utilisée que si l'application
+ utilises de longues sessions, lui permettant de comparer l'ancien état et le nouvel état d'une entité.
+ L'utilisation d'un pattern <literal>session-per-request-with-detached- objects</literal> devient alors impossible.
+ </para>
+
+ <para>Il peut être souhaitable de permettre les modifications concurrentes lorsque des champs distincts sont
+ modifiés. En mettant la propriété <literal>optimistic-lock="dirty"</literal> dans l'élément <literal><class></literal> ,
+ Hibernate ne fera la comparaison que des champs devant être actualisés lors du flush().
+ </para>
+
+ <para>Dans les deux cas: en utilisant une colonne de version/horodatée ou via la comparaison de l'état complet
+ de l'objet ou de ses champs modifiés, Hibernate ne créera qu'une seule commande d'UPDATE par entité avec la
+ clause WHERE appropriée pour mettre à jour l'entité
+ <emphasis>ET</emphasis>
+ en vérifier la version. Si vous utilisez la persistance transitive pour propager l'évènement de rattachement
+ à des entités associées, il est possible qu'Hibernate génère des commandes d'UPDATE inutiles. Ceci n'est
+ généralement pas un problème, mais certains déclencheurs
+ <emphasis>on update</emphasis>
+ dans la base de données pourraient être activés même si aucun changement n'était réellement persisté sur des
+ objets associés. Vous pouvez personnaliser ce comportement en indiquant <literal>select-before- update="true"</literal>
+ dans l'élément de mapping <literal><class></literal> . Ceci forcera Hibernate à faire le SELECT de l'instance
+ afin de s'assurer que l'entité doit réellement être
+ actualisée avant de lancer la commande d'UPDATE.
+ </para>
+
+ </sect2>
+
+</sect1>
+
+<sect1 id="transactions-locking">
+ <title>Verouillage pessimiste</title>
+
+ <para>Il n'est nécessaire de s'attarder à la stratégie de verrouillage des entités dans une application utilisant
+ Hibernate. Il est généralement suffisant de définir le niveau d'isolation pour les connexions JDBC et de laisser
+ ensuite la base de donnée effectuer son travail. Toutefois, certains utilisateurs avancés peuvent vouloir
+ obtenir un verrouillage pessimiste exclusif sur un enregistrement et le réobtenir au lancement d'une nouvelle
+ transaction.</para>
+
+ <para>Hibernate utilisera toujours le mécanisme de verrouillage de la base de données et ne verrouillera jamais les
+ objets en mémoire!</para>
+
+ <para>La classe
+ <literal>LockMode</literal>
+ définit les différents niveaux de verrouillage pouvant être obtenus par Hibernate. Le verrouillage est obtenu
+ par les mécanismes suivants:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>LockMode.WRITE</literal> est obtenu automatiquement quand Hibernate actualise ou insert un enregistrement.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>LockMode.UPGRADE</literal> peut être obtenu de manière explicite via la requête en utilisant
+ <literal>SELECT ... FOR UPDATE</literal> sur une base de données supportant cette syntaxe.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>LockMode.UPGRADE_NOWAIT</literal> peut être obtenu de manière explicite en utilisant
+ <literal>SELECT ... FOR UPDATE NOWAIT</literal> sur Oracle.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>LockMode.READ</literal> est obtenu automatiquement quand Hibernate lit des données dans un contexte d'isolation
+ <literal>Repeatable Read</literal> ou
+ <literal>Serializable</literal> . Peut être réobtenu explicitement via une requête.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>LockMode.NONE</literal> représente l'absence de verouillage. Tous les objets migrent vers ce mode a la fin d'une
+ <literal>Transaction</literal> . Les objets associés à une session via un appel à
+ <literal>saveOrUpdate()</literal> commencent également leur cycle de vie dans cet état.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>Les niveaux de verrouillage peuvent être explicitement obtenus de l'une des manières suivantes:</para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>Un appel à
+ <literal>Session.load()</literal> , en spécifiant un niveau verrouillage
+ <literal>LockMode</literal> .
+ </para>
+ </listitem>
+ <listitem>
+ <para>Un appel à
+ <literal>Session.lock()</literal> .
+ </para>
+ </listitem>
+ <listitem>
+ <para>Une appel à
+ <literal>Query.setLockMode()</literal> .
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>Si
+ <literal>Session.load()</literal>
+ est appelé avec le paramètre de niveau de verouillage
+ <literal>UPGRADE</literal>
+ ou
+ <literal>UPGRADE_NOWAIT</literal>
+ et que l'objet demandé n'est pas présent dans la session, celui-ci sera chargé à l'aide d'une requête
+ <literal>SELECT ... FOR UPDATE</literal>
+ . Si la méthode
+ <literal>load()</literal>
+ est appelée pour un objet déjà en session avec un verrouillage moindre que celui demandé, Hibernate appellera la
+ méthode
+ <literal>lock()</literal>
+ pour cet objet.
+ </para>
+
+ <para>
+ <literal>Session.lock()</literal>
+ effectue une vérification de version si le niveau de verrouillage est
+ <literal>READ</literal>
+ ,
+ <literal>UPGRADE</literal>
+ ou
+ <literal>UPGRADE_NOWAIT</literal>
+ . (Dans le cas des niveaux
+ <literal>UPGRADE</literal>
+ ou
+ <literal>UPGRADE_NOWAIT</literal>
+ , une requête
+ <literal>SELECT ... FOR UPDATE</literal>
+ sera utilisée.)
+ </para>
+
+ <para>Si une base de données ne supporte pas le niveau de verrouillage demandé, Hibernate utilisera un niveau
+ alternatif convenable au lieux de lancer une exception. Ceci assurera la portabilité de votre
+ application.</para>
+</sect1>
+
+ <sect1 id="transactions-connection-release">
+ <title>Mode de libération de Connection</title>
+
+ <para>
+ Le comportement original (2.x) d'Hibernate pour la gestion des connexions JDBC
+ était que la <literal>Session</literal> obtenait une connexion dès qu'elle en avait
+ besoin et la libérait une fois la session fermée.
+ Hibernate 3 a introduit les modes de libération de connexion pour indiquer à la session
+ comment gérer les transactions JDBC. Notez que la discussion suivante n'est pertinente
+ que pour des connexions fournies par un <literal>ConnectionProvider</literal>, celles gérées
+ par l'utilisateur sont en dehors du scope de cette discussion. Les différents modes
+ sont définies par <literal>org.hibernate.ConnectionReleaseMode</literal>:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>ON_CLOSE</literal> - est essentiellement le comportement passé.
+ La session Hibernate obtient une connexion lorsqu'elle en a besoin et la garde
+ jusqu'à ce que la session se ferme.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>AFTER_TRANSACTION</literal> - indique de relacher la connexion après qu'une
+ <literal>org.hibernate.Transaction</literal> se soit achevée.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>AFTER_STATEMENT</literal> (aussi appelé libération brutale) - indique de relacher
+ les connexions après chaque exécution d'un statement. Ce relachement aggressif est annulé
+ si ce statement laisse des ressources associées à une session donnée ouvertes, actuellement
+ ceci n'arrive que lors de l'utilisation de <literal>org.hibernate.ScrollableResults</literal>.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Le paramètre de configuration <literal>hibernate.connection.release_mode</literal> est utilisé
+ pour spécifier quel mode de libération doit être utiliser. Les valeurs possibles sont:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>auto</literal> (valeur par défaut) - ce choix délègue le choix de libération
+ à la méthode <literal>org.hibernate.transaction.TransactionFactory.getDefaultReleaseMode()</literal>
+ Pour la JTATransactionFactory, elle retourne ConnectionReleaseMode.AFTER_STATEMENT; pour
+ JDBCTransactionFactory, elle retourne ConnectionReleaseMode.AFTER_TRANSACTION. C'est rarement
+ une bonne idée de changer ce comportement par défaut puisque les erreurs soulevées par ce
+ paramétrage tend à prouver une erreur dans le code de l'utilisateur.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>on_close</literal> - indique d'utiliser ConnectionReleaseMode.ON_CLOSE. Ce paramétrage
+ existe pour garantir la compatibilité avec les versions précédentes, mais ne devrait plus être utilisé.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>after_transaction</literal> - indique d'utiliser ConnectionReleaseMode.AFTER_TRANSACTION.
+ Ne devrait pas être utilisé dans les environnements JTA. Notez aussi qu'avec
+ ConnectionReleaseMode.AFTER_TRANSACTION, si une session est considérée comme étant en mode auto-commit
+ les connexions seront relachées comme si le mode était AFTER_STATEMENT.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>after_statement</literal> - indique d'utiliser ConnectionReleaseMode.AFTER_STATEMENT.
+ Additonnellement, le <literal>ConnectionProvider</literal> utilisé est consulté pour savoir s'il supporte
+ ce paramétrage (<literal>supportsAggressiveRelease()</literal>). Si ce n'est pas le cas, le mode de
+ libération est ré initialisé à ConnectionReleaseMode.AFTER_TRANSACTION.
+ Ce paramétrage n'est sûr que dans les environnements où il est possible d'obtenir à nouveau
+ la même connexion JDBC à chaque fois que l'on fait un appel de <literal>ConnectionProvider.getConnection()</literal>
+ ou dans les envrionnements auto-commit où il n'est pas important d'obtenir plusieurs fois la
+ même connexion.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/tutorial.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/tutorial.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/tutorial.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1575 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<chapter id="tutorial">
+ <title>Introduction à Hibernate</title>
+
+ <sect1 id="tutorial-intro" revision="1">
+ <title>Préface</title>
+
+ <para>
+ Ce chapitre est un didacticiel introductif destiné aux nouveaux utilisateurs
+ d'Hibernate. Nous commençons avec une simple application en ligne de commande
+ utilisant une base de données en mémoire, et la développons en étapes faciles
+ à comprendre.
+ </para>
+
+ <para>
+ Ce didacticiel est destiné aux nouveaux utilisateurs d'Hibernate mais requiert
+ des connaissances Java et SQL. Il est basé sur un didacticiel de Michael Gloegl,
+ les bibliothèques tierces que nous nommons sont pour les JDK 1.4 et 5.0. Vous
+ pourriez avoir besoin d'autres bibliothèques pour le JDK 1.3.
+ </para>
+
+ <para>
+ Le code source de ce tutoriel est inclus dans la distribution dans le répertoire
+ <literal>doc/reference/tutorial/</literal>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="tutorial-firstapp" revision="1">
+ <title>Partie 1 - Première application Hibernate</title>
+
+ <para>
+ D'abord, nous créerons une simple application Hibernate en console. Nous utilisons
+ une base de données en mémoire (HSQL DB), donc nous n'avons pas à installer de
+ serveur de base de données.
+ </para>
+
+ <para>
+ Supposons que nous ayons besoin d'une petite application de base de données qui
+ puisse stocker des événements que nous voulons suivre, et des informations à propos
+ des hôtes de ces événements.
+ </para>
+
+ <para>
+ La première chose que nous faisons est de configurer notre répertoire de
+ développement et de mettre toutes les bibliothèques dont nous avons besoin dedans.
+ Téléchargez la distribution Hibernate à partir du site web d'Hibernate.
+ Extrayez le paquet et placez toutes les bibliothèques requises trouvées dans
+ <literal>/lib</literal> dans le répertoire <literal>/lib</literal> de votre
+ nouveau répertoire de travail. Il devrait ressembler à ça :
+ </para>
+
+ <programlisting><![CDATA[.
++lib
+ antlr.jar
+ cglib-full.jar
+ asm.jar
+ asm-attrs.jars
+ commons-collections.jar
+ commons-logging.jar
+ ehcache.jar
+ hibernate3.jar
+ jta.jar
+ dom4j.jar
+ log4j.jar ]]></programlisting>
+
+ <para>
+ Ceci est l'ensemble minimum de bibliothèques requises (notez que nous avons aussi
+ copié hibernate3.jar, l'archive principale) pour Hibernate. Lisez le fichier
+ <literal>README.txt</literal> dans le répertoire <literal>lib/</literal> de la
+ distribution Hibernate pour plus d'informations à propos des biliothèques tierces
+ requises et optionnelles. (En fait, log4j n'est pas requis mais préféré par beaucoup
+ de développeurs.)
+ </para>
+
+ <para>
+ Ensuite, nous créons une classe qui réprésente l'événement que nous voulons
+ stocker dans notre base de données.
+ </para>
+
+ <sect2 id="tutorial-firstapp-firstclass" revision="1">
+ <title>La première classe</title>
+
+ <para>
+ Notre première classe persistante est une simple classe JavaBean avec
+ quelques propriétés :
+ </para>
+
+ <programlisting><![CDATA[package events;
+
+import java.util.Date;
+
+public class Event {
+ private Long id;
+
+ private String title;
+ private Date date;
+
+ public Event() {}
+
+ public Long getId() {
+ return id;
+ }
+
+ private void setId(Long id) {
+ this.id = id;
+ }
+
+ public Date getDate() {
+ return date;
+ }
+
+ public void setDate(Date date) {
+ this.date = date;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+}]]></programlisting>
+
+ <para>
+ Vous pouvez voir que cette classe utilise les conventions de nommage standard JavaBean
+ pour les méthodes getter/setter des propriétés, ainsi qu'une visibilité privée
+ pour les champs. Ceci est la conception recommandée - mais pas obligatoire. Hibernate peut
+ aussi accéder aux champs directement, le bénéfice des méthodes d'accès est la robustesse
+ pour la refonte de code. Le constructeur sans argument est requis pour instancier
+ un objet de cette classe via reflexion.
+ </para>
+
+ <para>
+ La propriété <literal>id</literal> contient la valeur d'un identifiant unique pour un
+ événement particulier. Toutes les classes d'entités persistantes (ainsi que les classes
+ dépendantes de moindre importance) auront besoin d'une telle propriété identifiante si nous
+ voulons utiliser l'ensemble complet des fonctionnalités d'Hibernate. En fait, la plupart des
+ applications (surtout les applications web) ont besoin de distinguer des objets par des
+ identifiants, donc vous devriez considérer ça comme une fonctionnalité plutôt que comme une
+ limitation. Cependant, nous ne manipulons généralement pas l'identité d'un objet, dorénavant
+ la méthode setter devrait être privée. Seul Hibernate assignera les identifiants lorsqu'un
+ objet est sauvegardé. Vous pouvez voir qu'Hibernate peut accéder aux méthodes publiques,
+ privées et protégées, ainsi qu'aux champs (publics, privés, protégés) directement. Le choix
+ vous est laissé, et vous pouvez l'ajuster à la conception de votre application.
+ </para>
+
+ <para>
+ Le constructeur sans argument est requis pour toutes les classes persistantes ;
+ Hibernate doit créer des objets pour vous en utilisant la réflexion Java. Le
+ constructeur peut être privé, cependant, la visibilité du paquet est requise
+ pour la génération de proxy à l'exécution et une récupération des données efficaces
+ sans instrumentation du bytecode.
+ </para>
+
+ <para>
+ Placez ce fichier source Java dans un répertoire appelé <literal>src</literal>
+ dans le dossier de développement. Ce répertoire devrait maintenant ressembler
+ à ça :
+ </para>
+
+ <programlisting><![CDATA[.
++lib
+ <Hibernate et bibliothèques tierces>
++src
+ +events
+ Event.java]]></programlisting>
+
+
+ <para>
+ Dans la prochaine étape, nous informons Hibernate de cette classe persistante.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-mapping" revision="1">
+ <title>Le fichier de mapping</title>
+
+ <para>
+ Hibernate a besoin de savoir comment charger et stocker des objets d'une classe
+ persistante. C'est là qu'intervient le fichier de mapping Hibernate. Le fichier
+ de mapping indique à Hibernate à quelle table dans la base de données il doit
+ accéder, et quelles colonnes de cette table il devra utiliser.
+ </para>
+
+ <para>
+ La structure basique de ce fichier de mapping ressemble à ça :
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping>
+[...]
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Notez que la DTD Hibernate est très sophistiquée. Vous pouvez l'utiliser
+ pour l'auto-complétement des éléments et des attributs de mapping XML dans
+ votre éditeur ou votre IDE. Vous devriez aussi ouvrir le fichier DTD dans
+ votre éditeur de texte - c'est le moyen le plus facile d'obtenir une vue
+ d'ensemble de tous les éléments et attributs, et de voir les valeurs par
+ défaut, ainsi que quelques commentaires. Notez qu'Hibernate ne chargera
+ pas le fichier DTD à partir du web, mais regardera d'abord dans le classpath
+ de l'application. Le fichier DTD est inclus dans <literal>hibernate3.jar</literal>
+ ainsi que dans le répertoire <literal>src</literal> de la distribution
+ Hibernate.
+ </para>
+
+ <para>
+ Nous omettrons la déclaration de la DTD dans les exemples futurs pour
+ raccourcir le code. Bien sûr il n'est pas optionnel.
+ </para>
+
+ <para>
+ Entre les deux balises <literal>hibernate-mapping</literal>, incluez un
+ élément <literal>class</literal>. Toutes les classes d'entités persistantes
+ (encore une fois, il pourrait y avoir des classes dépendantes plus tard,
+ qui ne sont pas des entités mère) ont besoin d'un mapping vers une table
+ de la base de données SQL :
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Event" table="EVENTS">
+
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Plus loin, nous disons à Hibernate comment persister et charger un objet de la classe
+ <literal>Event</literal> dans la table <literal>EVENTS</literal>, chaque instance est
+ représentée par une ligne dans cette table. Maintenant nous continuons avec le mapping de
+ la propriété de l'identifiant unique vers la clef primaire de la table. De plus, comme
+ nous ne voulons pas nous occuper de la gestion de cet identifiant, nous utilisons une
+ stratégie de génération d'identifiant d'Hibernate pour la colonne de la clef primaire
+ subrogée :
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Event" table="EVENTS">
+ <id name="id" column="EVENT_ID">
+ <generator class="increment"/>
+ </id>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ L'élément <literal>id</literal> est la déclaration de la propriété de l'identifiant,
+ <literal>name="id"</literal> déclare le nom de la propriété Java - Hibernate
+ utilisera les méthodes getter et setter pour accéder à la propriété. L'attribut
+ <literal>column</literal> indique à Hibernate quelle colonne de la table
+ <literal>EVENTS</literal> nous utilisons pour cette clef primaire. L'élément
+ <literal>generator</literal> imbriqué spécifie la stratégie de génération de
+ l'identifiant, dans ce cas nous avons utilisé <literal>increment</literal>,
+ laquelle est une méthode très simple utile surtout pour les tests
+ (et didacticiels). Hibernate supporte aussi les identifiants générés par les
+ bases de données, globalement uniques, ainsi que les identifiants assignés par
+ l'application (ou n'importe quelle stratégie que vous avez écrit en extension).
+ </para>
+
+ <para>
+ Finalement nous incluons des déclarations pour les propriétés persistantes de la classe
+ dans le fichier de mapping. Par défaut, aucune propriété de la classe n'est considérée
+ comme persistante :
+ </para>
+
+ <programlisting><![CDATA[
+<hibernate-mapping>
+
+ <class name="Event" table="EVENTS">
+ <id name="id" column="EVENT_ID">
+ <generator class="increment"/>
+ </id>
+ <property name="date" type="timestamp" column="EVENT_DATE"/>
+ <property name="title"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Comme avec l'élément <literal>id</literal>, l'attribut <literal>name</literal>
+ de l'élément <literal>property</literal> indique à Hibernate quels getters/setters
+ utiliser.
+ </para>
+
+ <para>
+ Pourquoi le mapping de la propriété <literal>date</literal> inclut
+ l'attribut <literal>column</literal>, mais pas <literal>title</literal> ?
+ Sans l'attribut <literal>column</literal> Hibernate utilise par défaut
+ le nom de la propriété comme nom de colonne. Ca fonctionne bien pour
+ <literal>title</literal>. Cependant, <literal>date</literal> est un mot clef
+ réservé dans la plupart des bases de données, donc nous utilisons un nom
+ différent pour le mapping.
+ </para>
+
+ <para>
+ La prochaine chose intéressante est que le mapping de <literal>title</literal>
+ manque aussi d'un attribut <literal>type</literal>. Les types que nous déclarons
+ et utilisons dans les fichiers de mapping ne sont pas, comme vous pourriez vous
+ y attendre, des types de données Java. Ce ne sont pas, non plus, des types de
+ base de données SQL. Ces types sont donc appelés des <emphasis>types de mapping
+ Hibernate</emphasis>, des convertisseurs qui peuvent traduire des types Java en
+ types SQL et vice versa. De plus, Hibernate tentera de déterminer la bonne conversion
+ et le type de mapping lui-même si l'attribut <literal>type</literal> n'est pas
+ présent dans le mapping. Dans certains cas, cette détection automatique (utilisant
+ la réflexion sur la classe Java) pourrait ne pas donner la valeur attendue ou
+ dont vous avez besoin. C'est le cas avec la propriété <literal>date</literal>.
+ Hibernate ne peut pas savoir si la propriété "mappera" une colonne SQL de type
+ <literal>date</literal>, <literal>timestamp</literal> ou <literal>time</literal>.
+ Nous déclarons que nous voulons conserver des informations avec une date complète
+ et l'heure en mappant la propriété avec un <literal>timestamp</literal>.
+ </para>
+
+ <para>
+ Ce fichier de mapping devrait être sauvegardé en tant que <literal>Event.hbm.xml</literal>,
+ juste dans le répertoire à côté du fichier source de la classe Java <literal>Event</literal>.
+ Le nommage des fichiers de mapping peut être arbitraire, cependant le suffixe
+ <literal>hbm.xml</literal> est devenu une convention dans la communauté des
+ développeurs Hibernate. La structure du répertoire devrait ressembler à ça :
+ </para>
+
+ <programlisting><![CDATA[.
++lib
+ <Hibernate et bibliothèques tierces>
++src
+ Event.java
+ Event.hbm.xml]]></programlisting>
+
+ <para>
+ Nous poursuivons avec la configuration principale d'Hibernate.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-configuration" revision="1">
+ <title>Configuration d'Hibernate</title>
+
+ <para>
+ Nous avons maintenant une classe persistante et son fichier de mapping. Il est temps de
+ configurer Hibernate. Avant ça, nous avons besoin d'une base de données. HSQL DB, un
+ SGBD SQL basé sur Java et travaillant en mémoire, peut être téléchargé à partir du site
+ web de HSQL. En fait, vous avez seulement besoin de <literal>hsqldb.jar</literal>. Placez
+ ce fichier dans le répertoire <literal>lib/</literal> du dossier de développement.
+ </para>
+
+ <para>
+ Créez un répertoire appelé <literal>data</literal> à la racine du répertoire de développement -
+ c'est là que HSQL DB stockera ses fichiers de données. Démarrez maintenant votre base de données
+ en exécutant <literal>java -classpath lib/hsqldb.jar org.hsqldb.Server</literal> dans votre répertoire de travail.
+ Vous observez qu'elle démarre et ouvre une socket TCP/IP, c'est là que notre application
+ se connectera plus tard. Si vous souhaitez démarrez à partir d'une nouvelle base de données
+ pour ce tutoriel (faites <literal>CTRL + C</literal> dans la fenêtre the window), effacez
+ le répertoire <literal>data/</literal> et redémarrez HSQL DB à nouveau.
+
+ </para>
+
+ <para>
+ Hibernate est la couche de votre application qui se connecte à cette base de données,
+ donc il a besoin des informations de connexion. Les connexions sont établies à travers
+ un pool de connexions JDBC, que nous devons aussi configurer. La distribution Hibernate
+ contient différents outils de gestion de pools de connexions JDBC open source, mais
+ pour ce didacticiel nous utiliserons le pool de connexions intégré à Hibernate. Notez
+ que vous devez copier les bibliothèques requises dans votre classpath et utiliser
+ une configuration de pool de connexions différente si vous voulez utiliser
+ un logiciel de gestion de pools JDBC tiers avec une qualité de production.
+ </para>
+
+ <para>
+ Pour la configuration d'Hibernate, nous pouvons utiliser un simple fichier
+ <literal>hibernate.properties</literal>, un fichier <literal>hibernate.cfg.xml</literal>
+ légèrement plus sophistiqué, ou même une configuration complète par programmation. La
+ plupart des utilisateurs préfèrent le fichier de configuration XML :
+ </para>
+
+ <programlisting><![CDATA[<?xml version='1.0' encoding='utf-8'?>
+<!DOCTYPE hibernate-configuration PUBLIC
+ "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
+
+<hibernate-configuration>
+
+ <session-factory>
+
+ <!-- Database connection settings -->
+ <property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
+ <property name="connection.url">jdbc:hsqldb:hsql://localhost</property>
+ <property name="connection.username">sa</property>
+ <property name="connection.password"></property>
+
+ <!-- JDBC connection pool (use the built-in) -->
+ <property name="connection.pool_size">1</property>
+
+ <!-- SQL dialect -->
+ <property name="dialect">org.hibernate.dialect.HSQLDialect</property>
+
+ <!-- Enable Hibernate's automatic session context management -->
+ <property name="current_session_context_class">thread</property>
+
+ <!-- Disable the second-level cache -->
+ <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
+
+ <!-- Echo all executed SQL to stdout -->
+ <property name="show_sql">true</property>
+
+ <!-- Drop and re-create the database schema on startup -->
+ <property name="hbm2ddl.auto">create</property>
+
+ <mapping resource="events/Event.hbm.xml"/>
+
+ </session-factory>
+
+</hibernate-configuration>]]></programlisting>
+ <para>
+ Notez que cette configuration XML utilise une DTD différente. Nous configurons
+ une <literal>SessionFactory</literal> d'Hibernate - une fabrique globale responsable
+ d'une base de données particulière. Si vous avez plusieurs base de données, utilisez
+ plusieurs configurations <literal><session-factory></literal>, généralement
+ dans des fichiers de configuration différents (pour un démarrage plus facile).
+ </para>
+
+ <para>
+ Les quatre premiers éléments <literal>property</literal> contiennent la configuration
+ nécessaire pour la connexion JDBC. L'élément <literal>property</literal> du dialecte
+ spécifie quelle variante du SQL Hibernate va générer. La gestion automatique des sessions
+ d'Hibernate pour les contextes de persistance sera détaillée très vite.
+ L'option <literal>hbm2ddl.auto</literal> active la génération automatique des schémas de
+ base de données - directement dans la base de données. Cela peut bien sûr aussi être
+ désactivé (en supprimant l'option de configuration) ou redirigé vers un fichier avec
+ l'aide de la tâche Ant <literal>SchemaExport</literal>. Finalement, nous ajoutons
+ le(s) fichier(s) de mapping pour les classes persistantes.
+ </para>
+
+ <para>
+ Copiez ce fichier dans le répertoire source, il terminera dans la racine
+ du classpath. Hibernate cherchera automatiquement, au démarrage, un fichier appelé
+ <literal>hibernate.cfg.xml</literal> dans la racine du classpath.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-ant" revision="1">
+ <title>Construction avec Ant</title>
+
+ <para>
+ Nous allons maintenant construire le didacticiel avec Ant. Vous aurez besoin d'avoir Ant
+ d'installé - récupérez-le à partir de <ulink url="http://ant.apache.org/bindownload.cgi"> la page
+ de téléchargement de Ant</ulink>. Comment installer Ant ne sera pas couvert ici. Référez-vous
+ au <ulink url="http://ant.apache.org/manual/index.html">manuel d'Ant</ulink>. Après que
+ vous aurez installé Ant, nous pourrons commencer à créer le fichier de construction. Il
+ s'appellera <literal>build.xml</literal> et sera placé directement dans le répertoire de
+ développement.
+ </para>
+
+ <para>
+ Un fichier de construction basique ressemble à ça :
+ </para>
+
+ <programlisting><![CDATA[<project name="hibernate-tutorial" default="compile">
+
+ <property name="sourcedir" value="${basedir}/src"/>
+ <property name="targetdir" value="${basedir}/bin"/>
+ <property name="librarydir" value="${basedir}/lib"/>
+
+ <path id="libraries">
+ <fileset dir="${librarydir}">
+ <include name="*.jar"/>
+ </fileset>
+ </path>
+
+ <target name="clean">
+ <delete dir="${targetdir}"/>
+ <mkdir dir="${targetdir}"/>
+ </target>
+
+ <target name="compile" depends="clean, copy-resources">
+ <javac srcdir="${sourcedir}"
+ destdir="${targetdir}"
+ classpathref="libraries"/>
+ </target>
+
+ <target name="copy-resources">
+ <copy todir="${targetdir}">
+ <fileset dir="${sourcedir}">
+ <exclude name="**/*.java"/>
+ </fileset>
+ </copy>
+ </target>
+
+</project>]]></programlisting>
+
+ <para>
+ Cela dira à Ant d'ajouter tous les fichiers du répertoire lib finissant par
+ <literal>.jar</literal> dans le classpath utilisé pour la compilation. Cela copiera aussi
+ tous les fichiers source non Java dans le répertoire cible, par exemple les fichiers de
+ configuration et de mapping d'Hibernate. Si vous lancez Ant maintenant, vous devriez
+ obtenir cette sortie :
+ </para>
+
+ <programlisting><![CDATA[C:\hibernateTutorial\>ant
+Buildfile: build.xml
+
+copy-resources:
+ [copy] Copying 2 files to C:\hibernateTutorial\bin
+
+compile:
+ [javac] Compiling 1 source file to C:\hibernateTutorial\bin
+
+BUILD SUCCESSFUL
+Total time: 1 second ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-helpers" revision="3">
+ <title>Démarrage et aides</title>
+
+ <para>
+ Il est temps de charger et de stocker quelques objets <literal>Event</literal>,
+ mais d'abord nous devons compléter la configuration avec du code
+ d'infrastructure. Nous devons démarrer Hibernate. Ce démarrage inclut la construction
+ d'un objet <literal>SessionFactory</literal> global et le stocker quelque part
+ facile d'accès dans le code de l'application. Une <literal>SessionFactory</literal>
+ peut ouvrir des nouvelles <literal>Session</literal>s. Une <literal>Session</literal>
+ représente une unité de travail simplement "threadée", la <literal>SessionFactory</literal>
+ est un objet global "thread-safe", instancié une seule fois.
+ </para>
+
+ <para>
+ Nous créerons une classe d'aide <literal>HibernateUtil</literal> qui s'occupe du
+ démarrage et rend la gestion des <literal>Session</literal>s plus facile.
+ Regardons l'implémentation :
+ </para>
+
+ <programlisting><![CDATA[package util;
+
+import org.hibernate.*;
+import org.hibernate.cfg.*;
+
+public class HibernateUtil {
+ public static final SessionFactory sessionFactory;
+
+ static {
+ try {
+ // Création de la SessionFactory à partir de hibernate.cfg.xml
+ sessionFactory = new Configuration().configure().buildSessionFactory();
+ } catch (Throwable ex) {
+ // Make sure you log the exception, as it might be swallowed
+ System.err.println("Initial SessionFactory creation failed." + ex);
+ throw new ExceptionInInitializerError(ex);
+ }
+ }
+
+ public static final ThreadLocal session = new ThreadLocal();
+
+ public static SessionFactory getSessionFactory() {
+ return sessionFactory;
+ }
+}]]></programlisting>
+
+ <para>
+ Cette classe ne produit pas seulement la <literal>SessionFactory</literal> globale
+ dans un initialiseur statique (appelé une seule fois par la JVM lorsque la classe
+ est chargée), elle masque le fait qu'elle exploite un singleton. Elle pourrait aussi
+ obtenir la <literal>SessionFactory</literal> depuis JNDI dans un serveur d'applications.
+ </para>
+
+ <para>
+ Si vous nommez la <literal>SessionFactory</literal> dans votre fichier de configuration,
+ Hibernate tentera la récupération depuis JNDI. Pour éviter ce code, vous pouvez aussi
+ utiliser un déploiement JMX et laisser le conteneur (compatible JMX) instancier et lier
+ un <literal>HibernateService</literal> à JNDI. Ces options avancées sont détaillées dans
+ la documentation de référence Hibernate.
+ </para>
+
+ <para>
+ Placez <literal>HibernateUtil.java</literal> dans le répertoire source de développement,
+ et ensuite <literal>Event.java</literal> :
+ </para>
+
+ <programlisting><![CDATA[.
++lib
+ <Hibernate and third-party libraries>
++src
+ +events
+ Event.java
+ Event.hbm.xml
+ +util
+ HibernateUtil.java
+ hibernate.cfg.xml
++data
+build.xml]]></programlisting>
+
+ <para>
+ Cela devrait encore compiler sans problème. Nous avons finalement besoin de configurer
+ le système de "logs" - Hibernate utilise commons-logging et vous laisse le choix entre
+ log4j et le système de logs du JDK 1.4. La plupart des développeurs préfèrent log4j :
+ copiez <literal>log4j.properties</literal> de la distribution d'Hibernate (il est dans
+ le répertoire <literal>etc/</literal>) dans votre répertoire <literal>src</literal>,
+ puis faites de même avec <literal>hibernate.cfg.xml</literal>. Regardez la configuration
+ d'exemple et changez les paramètres si vous voulez une sortie plus verbeuse. Par défaut,
+ seul le message de démarrage d'Hibernate est affiché sur la sortie standard.
+ </para>
+
+ <para>
+ L'infrastructure de ce didacticiel est complète - et nous sommes prêts à effectuer un
+ travail réel avec Hibernate.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-workingpersistence" revision="4">
+ <title>Charger et stocker des objets</title>
+
+ <para>
+ Finalement nous pouvons utiliser Hibernate pour charger et stocker des objets.
+ Nous écrivons une classe <literal>EventManager</literal> avec une méthode
+ <literal>main()</literal> :
+ </para>
+
+ <programlisting><![CDATA[package events;
+import org.hibernate.Session;
+
+import java.util.Date;
+
+import util.HibernateUtil;
+
+public class EventManager {
+
+ public static void main(String[] args) {
+ EventManager mgr = new EventManager();
+
+ if (args[0].equals("store")) {
+ mgr.createAndStoreEvent("My Event", new Date());
+ }
+
+ HibernateUtil.getSessionFactory().close();
+ }
+
+ private void createAndStoreEvent(String title, Date theDate) {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+
+ session.beginTransaction();
+
+ Event theEvent = new Event();
+ theEvent.setTitle(title);
+ theEvent.setDate(theDate);
+
+ session.save(theEvent);
+
+ session.getTransaction().commit();
+ }]]></programlisting>
+
+ <para>
+ Nous créons un nouvel objet <literal>Event</literal>, et le remettons à Hibernate.
+ Hibernate s'occupe maintenant du SQL et exécute les <literal>INSERT</literal>s
+ dans la base de données. Regardons le code de gestion de la <literal>Session</literal>
+ et de la <literal>Transaction</literal> avant de lancer ça.
+ </para>
+
+ <para>
+ Une <literal>Session</literal> est une unité de travail. Pour le moment, nous allons faire
+ les choses simplement et assumer une granularité un-un entre une <literal>Session</literal>
+ hibernate et une transaction à la base de données. Pour isoler notre code du système de transaction
+ sous-jacent (dans notre cas, du pure JDBC, mais cela pourrait être JTA), nous utilisons l'API
+ <literal>Transaction</literal> qui est disponible depuis la <literal>Session</literal> Hibernate.
+ </para>
+
+ <para>
+ Que fait <literal>sessionFactory.getCurrentSession()</literal> ? Premièrement, vous pouvez
+ l'invoquer autant de fois que vous le voulez et n'importe où du moment que vous avez votre
+ <literal>SessionFactory</literal> (facile grâce à <literal>HibernateUtil</literal>).
+ La méthode <literal>getCurrentSession()</literal> renvoie toujours l'unité de travail courante.
+ Souvenez vous que nous avons basculé notre option de configuration au mécanisme basé sur le "thread"
+ dans <literal>hibernate.cfg.xml</literal>. Par conséquent, le scope de l'unité de travail
+ courante est le thread java courant d'exécution. Ceci n'est pas totalement vrai. Une
+ <literal>Session</literal> commence lorsqu'elle est vraiment utilisée la première fois,
+ Lorsque nous appelons pour la première fois <literal>getCurrentSession()</literal>.
+ Ensuite, elle est liée, par Hibernate, au thread courant. Lorsque la transaction s'achève
+ (commit ou rollback), Hibernate délie la <literal>Session</literal> du thread et la ferme
+ pour vous. Si vous invoquez <literal>getCurrentSession()</literal> une autre fois, vous obtenez
+ une nouvelle <literal>Session</literal> et pouvez entamer une nouvelle unité de travail.
+ Ce modèle de programmation "<emphasis>thread-bound</emphasis>" est le moyen le plus
+ populaire d'utiliser Hibernate.
+ </para>
+
+ <para>
+ Lisez <xref linkend="transactions"/> pour plus d'informations sur la gestion des transactions et leur démarcations.
+ Nous n'avons pas géré les erreurs et rollback sur l'exemple précédent.
+ </para>
+
+ <para>
+ Pour lancer cette première routine, nous devons ajouter une cible appelable dans
+ le fichier de construction de Ant :
+ </para>
+
+ <programlisting><![CDATA[<target name="run" depends="compile">
+ <java fork="true" classname="events.EventManager" classpathref="libraries">
+ <classpath path="${targetdir}"/>
+ <arg value="${action}"/>
+ </java>
+</target>]]></programlisting>
+
+ <para>
+ La valeur de l'argument <literal>action</literal> correspond à la ligne de commande
+ qui appelle la cible :
+ </para>
+
+ <programlisting><![CDATA[C:\hibernateTutorial\>ant run -Daction=store]]></programlisting>
+
+ <para>
+ Vous devriez voir, après la compilation, Hibernate démarrer et, en fonction de votre
+ configuration, beaucoup de traces sur la sortie. À la fin vous trouverez la ligne suivante :
+ </para>
+
+ <programlisting><![CDATA[[java] Hibernate: insert into EVENTS (EVENT_DATE, title, EVENT_ID) values (?, ?, ?)]]></programlisting>
+
+ <para>
+ C'est l'<literal>INSERT</literal> exécuté par Hibernate, les points d'interrogation
+ représentent les paramètres JDBC liés. Pour voir les valeurs liées aux arguments, ou
+ pour réduire la verbosité des traces, vérifier votre <literal>log4j.properties</literal>.
+ </para>
+
+ <para>
+ Maintenant nous aimerions aussi lister les événements stockés, donc nous ajoutons une
+ option à la méthode principale :
+ </para>
+
+ <programlisting><![CDATA[if (args[0].equals("store")) {
+ mgr.createAndStoreEvent("My Event", new Date());
+}
+else if (args[0].equals("list")) {
+ List events = mgr.listEvents();
+ for (int i = 0; i < events.size(); i++) {
+ Event theEvent = (Event) events.get(i);
+ System.out.println("Event: " + theEvent.getTitle() +
+ " Time: " + theEvent.getDate());
+ }
+}]]></programlisting>
+
+ <para>
+ Nous ajoutons aussi une nouvelle méthode <literal>listEvents()</literal> :
+ </para>
+
+ <programlisting><![CDATA[private List listEvents() {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+
+ session.beginTransaction();
+
+ List result = session.createQuery("from Event").list();
+
+ session.getTransaction().commit();
+
+ return result;
+}]]></programlisting>
+
+ <para>
+ Ce que nous faisons ici c'est utiliser une requête HQL (Hibernate Query Language) pour
+ charger tous les objets <literal>Event</literal> existants de la base de données.
+ Hibernate générera le SQL approprié, l'enverra à la base de données et peuplera des
+ objets <literal>Event</literal> avec les données. Vous pouvez créer des requêtes plus
+ complexes avec HQL, bien sûr.
+ </para>
+
+ <para>
+ Maintenant, pour exécuter et tester tout ça, suivez ces étapes :
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ Exécutez <literal>ant run -Daction=store</literal> pour stocker quelque
+ chose dans la base de données et, bien sûr, pour générer, avant, le schéma
+ de la base de données grâce à hbm2ddl.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Maintenant désactivez hbm2ddl en commentant la propriété dans votre fichier
+ <literal>hibernate.cfg.xml</literal>. Généralement vous la laissez seulement
+ activée dans des tests unitaires en continu, mais une autre exécution de hbm2ddl
+ <emphasis>effacerait</emphasis> tout ce que vous avez stocké - le paramètre de
+ configuration <literal>create</literal> se traduit en fait par "supprimer toutes les
+ tables du schéma, puis re-créer toutes les tables, lorsque la SessionFactory est
+ construite".
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Si maintenant vous appelez Ant avec <literal>-Daction=list</literal>, vous devriez voir
+ les événements que vous avez stockés jusque là. Vous pouvez bien sûr aussi appeler l'action
+ <literal>store</literal> plusieurs fois.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="tutorial-associations">
+ <title>Partie 2 - Mapper des associations</title>
+
+ <para>
+ Nous avons mappé une classe d'une entité persistante vers une table. Partons de là et
+ ajoutons quelques associations de classe. D'abord nous ajouterons des gens à notre
+ application, et stockerons une liste d'événements auxquels ils participent.
+ </para>
+
+ <sect2 id="tutorial-associations-mappinguser" revision="1">
+ <title>Mapper la classe Person</title>
+
+ <para>
+ La première version de la classe <literal>Person</literal> est simple :
+ </para>
+
+ <programlisting><![CDATA[package events;
+
+public class Person {
+
+ private Long id;
+ private int age;
+ private String firstname;
+ private String lastname;
+
+ public Person() {}
+
+ // Accessor methods for all properties, private setter for 'id'
+
+}]]></programlisting>
+
+ <para>
+ Créez un nouveau fichier de mapping appelé <literal>Person.hbm.xml</literal>
+ (n'oubliez pas la référence à la DTD)
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="events.Person" table="PERSON">
+ <id name="id" column="PERSON_ID">
+ <generator class="native"/>
+ </id>
+ <property name="age"/>
+ <property name="firstname"/>
+ <property name="lastname"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Finalement, ajoutez la nouveau mapping à la configuration d'Hibernate :
+ </para>
+
+ <programlisting><![CDATA[<mapping resource="events/Event.hbm.xml"/>
+<mapping resource="events/Person.hbm.xml"/>]]></programlisting>
+ <para>
+ Nous allons maintenant créer une association entre ces deux entités. Évidemment,
+ des personnes peuvent participer aux événements, et des événements ont des participants.
+ Les questions de conception que nous devons traiter sont : direction, cardinalité et comportement
+ de la collection.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-unidirset" revision="2">
+ <title>Une association unidirectionnelle basée sur Set</title>
+
+ <para>
+ Nous allons ajouter une collection d'événements à la classe <literal>Person</literal>. De
+ cette manière nous pouvons facilement naviguer dans les événements d'une personne
+ particulière, sans exécuter une requête explicite - en appelant
+ <literal>aPerson.getEvents()</literal>. Nous utilisons une collection Java, un
+ <literal>Set</literal>, parce que la collection ne contiendra pas d'éléments dupliqués et
+ l'ordre ne nous importe pas.
+ </para>
+
+ <para>
+ Nous avons besoin d'une association unidirectionnelle, pluri-valuée, implémentée avec
+ un <literal>Set</literal>. Écrivons le code pour ça dans les classes Java et mappons les :
+ </para>
+
+ <programlisting><![CDATA[public class Person {
+
+ private Set events = new HashSet();
+
+ public Set getEvents() {
+ return events;
+ }
+
+ public void setEvents(Set events) {
+ this.events = events;
+ }
+}]]></programlisting>
+
+ <para>
+ D'abord nous mappons cette association, mais pensez à l'autre côté. Clairement, nous pouvons
+ la laisser unidirectionnelle. Ou alors, nous pourrions créer une autre collection sur
+ <literal>Event</literal>, si nous voulons être capable de la parcourir de manière
+ bidirectionnelle, c'est-à-dire avoir <literal>anEvent.getParticipants()</literal>.
+ Ce n'est pas nécessaire d'un point de vue fonctionnel. Vous pourrez toujours exécuter une requête
+ explicite pour récupérer les participants d'un "event" particulier. Ce choix de conception
+ vous est laissé, mais ce qui reste certains est la cardinalité de l'association: "plusieurs"
+ des deux côtés, nous appelons cela une association <emphasis>many-to-many</emphasis>.
+ Par conséquent nous utilisons un mapping Hibernate many-to-many:
+ </para>
+
+ <programlisting><![CDATA[<class name="events.Person" table="PERSON">
+ <id name="id" column="PERSON_ID">
+ <generator class="native"/>
+ </id>
+ <property name="age"/>
+ <property name="firstname"/>
+ <property name="lastname"/>
+
+ <set name="events" table="PERSON_EVENT">
+ <key column="PERSON_ID"/>
+ <many-to-many column="EVENT_ID" class="Event"/>
+ </set>
+
+</class>]]></programlisting>
+
+ <para>
+ Hibernate supporte toutes sortes de mapping de collection, un <literal><set></literal>
+ étant le plus commun. Pour une association many-to-many (ou une relation
+ d'entité <emphasis>n:m</emphasis>), une table d'association est requise. Chaque ligne dans cette table représente un lien entre une personne et un événement. Le nom de la table est
+ configuré avec l'attribut <literal>table</literal> de l'élément <literal>set</literal>. Le
+ nom de la colonne identifiant dans l'association, du côté de la personne, est défini avec
+ l'élément <literal><key></literal>, et le nom de la colonne pour l'événement dans
+ l'attribut <literal>column</literal> de <literal><many-to-many></literal>. Vous
+ devez aussi donner à Hibernate la classe des objets de votre collection (c'est-à-dire : la
+ classe de l'autre côté de la collection).
+ </para>
+
+ <para>
+ Le schéma de base de données pour ce mapping est donc :
+ </para>
+
+ <programlisting><![CDATA[
+ _____________ __________________
+ | | | | _____________
+ | EVENTS | | PERSON_EVENT | | |
+ |_____________| |__________________| | PERSON |
+ | | | | |_____________|
+ | *EVENT_ID | <--> | *EVENT_ID | | |
+ | EVENT_DATE | | *PERSON_ID | <--> | *PERSON_ID |
+ | TITLE | |__________________| | AGE |
+ |_____________| | FIRSTNAME |
+ | LASTNAME |
+ |_____________|
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-working" revision="1">
+ <title>Travailler avec l'association</title>
+
+ <para>
+ Réunissons quelques personnes et quelques événements dans une nouvelle méthode dans
+ <literal>EventManager</literal> :
+ </para>
+
+ <programlisting><![CDATA[private void addPersonToEvent(Long personId, Long eventId) {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+ session.beginTransaction();
+
+ Person aPerson = (Person) session.load(Person.class, personId);
+ Event anEvent = (Event) session.load(Event.class, eventId);
+
+ aPerson.getEvents().add(anEvent);
+
+ session.getTransaction().commit();
+}]]></programlisting>
+
+ <para>
+ Après le chargement d'une <literal>Person</literal> et d'un <literal>Event</literal>, modifiez
+ simplement la collection en utilisant les méthodes normales de la collection. Comme vous
+ pouvez le voir, il n'y a pas d'appel explicite à <literal>update()</literal> ou
+ <literal>save()</literal>, Hibernate détecte automatiquement que la collection a été
+ modifiée et a besoin d'être mise à jour. Ceci est appelé <emphasis>la vérification sale
+ automatique</emphasis> (NdT : "automatic dirty checking"), et vous pouvez aussi l'essayer en
+ modifiant le nom ou la propriété date de n'importe lequel de vos objets. Tant qu'ils sont dans
+ un état <emphasis>persistant</emphasis>, c'est-à-dire, liés à une <literal>Session</literal> Hibernate
+ particulière (c-à-d qu'ils ont juste été chargés ou sauvegardés dans une unité de travail),
+ Hibernate surveille les changements et exécute le SQL correspondant. Le processus de
+ synchronisation de l'état de la mémoire avec la base de données, généralement seulement à la fin
+ d'une unité de travail, est appelé <emphasis>flushing</emphasis>. Dans notre code, l'unité de travail
+ s'achève par un commit (ou rollback) de la transaction avec la base de données - comme défini
+ par notre option <literal>thread</literal> de configuration pour la classe <literal>CurrentSessionContext</literal>.
+ </para>
+
+ <para>
+ Vous pourriez bien sûr charger une personne et un événement dans différentes unités de travail. Ou
+ vous modifiez un objet à l'extérieur d'une <literal>Session</literal>, s'il n'est pas dans un état
+ persistant (s'il était persistant avant, nous appelons cet état <emphasis>détaché</emphasis>).
+ Vous pouvez même modifier une collection lorsqu'elle est détachée:
+ </para>
+
+ <programlisting><![CDATA[private void addPersonToEvent(Long personId, Long eventId) {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+ session.beginTransaction();
+
+ Person aPerson = (Person) session
+ .createQuery("select p from Person p left join fetch p.events where p.id = :pid")
+ .setParameter("pid", personId)
+ .uniqueResult(); // Eager fetch the collection so we can use it detached
+
+ Event anEvent = (Event) session.load(Event.class, eventId);
+
+ session.getTransaction().commit();
+
+ // End of first unit of work
+
+ aPerson.getEvents().add(anEvent); // aPerson (and its collection) is detached
+
+ // Begin second unit of work
+
+ Session session2 = HibernateUtil.getSessionFactory().getCurrentSession();
+ session2.beginTransaction();
+
+ session2.update(aPerson); // Reattachment of aPerson
+
+ session2.getTransaction().commit();
+}]]></programlisting>
+
+ <para>
+ L'appel à <literal>update</literal> rend un objet détaché à nouveau persistant, vous pourriez
+ dire qu'il le lie à une unité de travail, ainsi toutes les modifications (ajout, suppression) que vous avez faites
+ pendant qu'il était détaché peuvent être sauvegardées dans la base de données
+ (il se peut que vous ayez besoin de modifier quelques unes des méthodes précédentes
+ pour retourner cet identifiant).
+ </para>
+
+ <programlisting><![CDATA[else if (args[0].equals("addpersontoevent")) {
+ Long eventId = mgr.createAndStoreEvent("My Event", new Date());
+ Long personId = mgr.createAndStorePerson("Foo", "Bar");
+ mgr.addPersonToEvent(personId, eventId);
+ System.out.println("Added person " + personId + " to event " + eventId);]]></programlisting>
+
+ <para>
+ Ce n'est pas très utile dans notre situation actuelle, mais c'est un concept important
+ que vous pouvez mettre dans votre propre application.
+ Pour le moment, complétez cet exercice en ajoutant une nouvelle action à la méthode
+ principale des <literal>EventManager</literal>s et appelez la à partir de la ligne de
+ commande. Si vous avez besoin des identifiants d'une personne et d'un événement - la
+ méthode <literal>save()</literal> les retourne.
+ </para>
+
+ <para>
+ C'était un exemple d'une association entre deux classes de même importance, deux entités.
+ Comme mentionné plus tôt, il y a d'autres classes et d'autres types dans un modèle typique,
+ généralement "moins importants". Vous en avez déjà vu certains, comme un <literal>int</literal>
+ ou une <literal>String</literal>. Nous appelons ces classes des <emphasis>types de valeur</emphasis>,
+ et leurs instances <emphasis>dépendent</emphasis> d'une entité particulière. Des instances de ces
+ types n'ont pas leur propre identité, elles ne sont pas non plus partagées entre des entités (deux
+ personnes ne référencent pas le même objet <literal>firstname</literal>, même si elles ont le
+ même prénom). Bien sûr, des types de valeur ne peuvent pas seulement être trouvés dans
+ le JDK (en fait, dans une application Hibernate toutes les classes du JDK sont considérées
+ comme des types de valeur), vous pouvez aussi écrire vous-même des classes dépendantes,
+ <literal>Address</literal> ou <literal>MonetaryAmount</literal>, par exemple.
+ </para>
+
+ <para>
+ Vous pouvez aussi concevoir une collection de types de valeur. C'est conceptuellement très
+ différent d'une collection de références vers d'autres entités, mais très ressemblant en Java.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-valuecollections">
+ <title>Collection de valeurs</title>
+
+ <para>
+ Nous ajoutons une collection d'objets de type de valeur à l'entité <literal>Person</literal>.
+ Nous voulons stocker des adresses email, donc le type que nous utilisons est <literal>String</literal>,
+ et la collection est encore un <literal>Set</literal> :
+ </para>
+ <programlisting><![CDATA[private Set emailAddresses = new HashSet();
+
+public Set getEmailAddresses() {
+ return emailAddresses;
+}
+
+public void setEmailAddresses(Set emailAddresses) {
+ this.emailAddresses = emailAddresses;
+}]]></programlisting>
+
+ <para>
+ Le mapping de ce <literal>Set</literal> :
+ </para>
+
+ <programlisting><![CDATA[<set name="emailAddresses" table="PERSON_EMAIL_ADDR">
+ <key column="PERSON_ID"/>
+ <element type="string" column="EMAIL_ADDR"/>
+</set>]]></programlisting>
+
+ <para>
+ La différence comparée au mapping vu plus tôt est la partie <literal>element</literal>,
+ laquelle dit à Hibernate que la collection ne contient pas de références vers une autre entité,
+ mais une collection d'éléments de type <literal>String</literal> (le nom en minuscule vous
+ indique que c'est un type/convertisseur du mapping Hibernate). Une fois encore, l'attribut
+ <literal>table</literal> de l'élément <literal>set</literal> détermine le nom de la table pour la
+ collection. L'élément <literal>key</literal> définit le nom de la colonne de la clef étrangère
+ dans la table de la collection. L'attribut <literal>column</literal> dans l'élément
+ <literal>element</literal> définit le nom de la colonne où les valeurs de <literal>String</literal>
+ seront réellement stockées.
+ </para>
+
+ <para>
+ Regardons le schéma mis à jour :
+ </para>
+
+ <programlisting><![CDATA[
+ _____________ __________________
+ | | | | _____________
+ | EVENTS | | PERSON_EVENT | | | ___________________
+ |_____________| |__________________| | PERSON | | |
+ | | | | |_____________| | PERSON_EMAIL_ADDR |
+ | *EVENT_ID | <--> | *EVENT_ID | | | |___________________|
+ | EVENT_DATE | | *PERSON_ID | <--> | *PERSON_ID | <--> | *PERSON_ID |
+ | TITLE | |__________________| | AGE | | *EMAIL_ADDR |
+ |_____________| | FIRSTNAME | |___________________|
+ | LASTNAME |
+ |_____________|
+ ]]></programlisting>
+
+ <para>
+ Vous pouvez voir que la clef primaire de la table de la collection est en fait une
+ clef composée, utilisant deux colonnes. Ceci implique aussi qu'il ne peut pas y avoir
+ d'adresses email dupliquées par personne, ce qui est exactement la sémantique dont
+ nous avons besoin pour un ensemble en Java.
+ </para>
+
+ <para>
+ Vous pouvez maintenant tester et ajouter des éléments à cette collection, juste comme
+ nous l'avons fait avant en liant des personnes et des événements. C'est le même code
+ en Java.
+ </para>
+
+ <programlisting><![CDATA[private void addEmailToPerson(Long personId, String emailAddress) {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+ session.beginTransaction();
+
+ Person aPerson = (Person) session.load(Person.class, personId);
+
+ // The getEmailAddresses() might trigger a lazy load of the collection
+ aPerson.getEmailAddresses().add(emailAddress);
+
+ session.getTransaction().commit();
+}]]></programlisting>
+
+ <para>
+ Cette fois ci, nous n'avons pas utilisé une requête de chargement agressif (<emphasis>fetch</emphasis>)
+ pour initialiser la collection. Par conséquent, l'invocation du getter déclenchera un
+ select supplémentaire pour l'initialiser. Traquez les logs SQL et tentez d'optimiser
+ ce cas avec un chargement aggressif.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-bidirectional" revision="1">
+ <title>Associations bidirectionnelles</title>
+
+ <para>
+ Ensuite nous allons mapper une association bidirectionnelle - faire fonctionner
+ l'association entre une personne et un événement à partir des deux côtés en Java.
+ Bien sûr, le schéma de la base de données ne change pas, nous avons toujours une pluralité
+ many-to-many. Une base de données relationnelle est plus flexible qu'un langage de
+ programmation réseau, donc elle n'a pas besoin de direction de navigation - les données
+ peuvent être vues et récupérées de toutes les manières possibles.
+ </para>
+
+ <para>
+ D'abord, ajouter une collection de participants à la classe <literal>Event</literal> :
+ </para>
+
+ <programlisting><![CDATA[private Set participants = new HashSet();
+
+public Set getParticipants() {
+ return participants;
+}
+
+public void setParticipants(Set participants) {
+ this.participants = participants;
+}]]></programlisting>
+
+ <para>
+ Maintenant mapper ce côté de l'association aussi, dans <literal>Event.hbm.xml</literal>.
+ </para>
+
+ <programlisting><![CDATA[<set name="participants" table="PERSON_EVENT" inverse="true">
+ <key column="EVENT_ID"/>
+ <many-to-many column="PERSON_ID" class="events.Person"/>
+</set>]]></programlisting>
+
+ <para>
+ Comme vous le voyez, ce sont des mappings de <literal>set</literal>s normaux dans les
+ deux documents de mapping. Notez que les noms de colonne dans <literal>key</literal> et
+ <literal>many-to-many</literal> sont inversés dans les 2 documents de mapping. L'ajout
+ le plus important ici est l'attribut <literal>inverse="true"</literal> dans l'élément
+ <literal>set</literal> du mapping de la collection des <literal>Event</literal>s.
+ </para>
+
+ <para>
+ Ce que signifie qu'Hibernate devrait prendre l'autre côté - la classe <literal>Person</literal> -
+ s'il a besoin de renseigner des informations à propos du lien entre les deux. Ce sera
+ beaucoup plus facile à comprendre une fois que vous verrez comment le lien bidirectionnel
+ entre les deux entités est créé.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-usingbidir">
+ <title>Travailler avec des liens bidirectionnels</title>
+
+ <para>
+ Premièrement, gardez à l'esprit qu'Hibernate n'affecte pas la sémantique normale de Java.
+ Comment avons-nous créé un lien entre une <literal>Person</literal> et un <literal>Event</literal>
+ dans l'exemple unidirectionnel ? Nous avons ajouté une instance de <literal>Event</literal>
+ à la collection des références d'événement d'une instance de <literal>Person</literal>. Donc,
+ évidemment, si vous voulons rendre ce lien bidirectionnel, nous devons faire la même chose de
+ l'autre côté - ajouter une référence de <literal>Person</literal> à la collection d'un
+ <literal>Event</literal>. Cette "configuration du lien des deux côtés" est absolument
+ nécessaire et vous ne devriez jamais oublier de le faire.
+ </para>
+
+ <para>
+ Beaucoup de développeurs programment de manière défensive et créent des
+ méthodes de gestion de lien pour affecter correctement les deux côtés,
+ par exemple dans <literal>Person</literal> :
+ </para>
+
+ <programlisting><![CDATA[protected Set getEvents() {
+ return events;
+}
+
+protected void setEvents(Set events) {
+ this.events = events;
+}
+
+public void addToEvent(Event event) {
+ this.getEvents().add(event);
+ event.getParticipants().add(this);
+}
+
+public void removeFromEvent(Event event) {
+ this.getEvents().remove(event);
+ event.getParticipants().remove(this);
+}]]></programlisting>
+
+ <para>
+ Notez que les méthodes get et set pour la collection sont maintenant protégées - ceci permet à des
+ classes du même paquet et aux sous-classes d'accéder encore aux méthodes, mais empêche n'importe qui
+ d'autre de mettre le désordre directement dans les collections (enfin, presque). Vous devriez
+ probablement faire de même avec la collection de l'autre côté.
+ </para>
+
+ <para>
+ Et à propos de l'attribut de mapping <literal>inverse</literal> ? Pour vous, et pour Java, un lien
+ bidirectionnel est simplement une manière de configurer correctement les références des deux côtés.
+ Hibernate n'a cependant pas assez d'informations pour ordonner correctement les expressions SQL
+ <literal>INSERT</literal> et <literal>UPDATE</literal> (pour éviter les violations de contrainte), et
+ a besoin d'aide pour gérer proprement les associations bidirectionnelles. Rendre
+ <literal>inverse</literal> un côté d'une assocation dit à Hibernate de l'ignorer essentiellement, pour
+ le considérer comme un <emphasis>miroir</emphasis> de l'autre côté. C'est tout ce qui est nécessaire à
+ Hibernate pour découvrir tout des problèmes de transformation d'un modèle de navigation
+ directionnelle vers un schéma SQL de base de données. Les règles dont vous devez vous souvenir sont :
+ toutes les associations bidirectionnelles ont besoin d'un côté marqué <literal>inverse</literal>.
+ Dans une association un-vers-plusieurs vous pouvez choisir n'importe quel côté, il n'y a pas de
+ différence.
+ </para>
+<!--
+ <para>
+ In the next section we integrate Hibernate with Tomcat and WebWork - the <literal>EventManager</literal>
+ doesn't scale anymore with our growing application.
+ </para>
+-->
+ </sect2>
+ </sect1>
+
+ <sect1 id="tutorial-webapp">
+ <title>Part 3 - L'application web EventManager</title>
+
+ <para>
+ Une application web Hibernate utilise la <literal>Session</literal> et <literal>Transaction</literal>
+ comme une application standalone. Cependant, quelques patterns sont utiles. Nous allons coder une
+ <literal>EventManagerServlet</literal>. Cette servlet peut lister tous les évènements stockés dans
+ la base de données, et fournir une formulaire HTML pour saisir d'autres évènements.
+ </para>
+
+ <sect2 id="tutorial-webapp-servlet">
+ <title>Ecrire la servlet de base</title>
+
+ <para>
+ Créons une nouvelle classe dans notre répertoire source, dans le package <literal>events</literal>:
+ </para>
+
+ <programlisting><![CDATA[package events;
+
+// Imports
+
+public class EventManagerServlet extends HttpServlet {
+
+ private final SimpleDateFormat dateFormatter =
+ new SimpleDateFormat("dd.MM.yyyy");
+
+ // Servlet code
+}]]></programlisting>
+
+ <para>
+ Le <literal>dateFormatter</literal> est un outil que nous utiliserons plus tard pour convertir les objets
+ <literal>Date</literal> depuis et vers des chaines de caractères. Il est propice de n'avoir qu'un
+ formatter comme membre de la servlet.
+ </para>
+
+ <para>
+ La servlet n'accepte que les requêtes HTTP <literal>GET</literal>, la méthode à implémenter est donc
+ <literal>doGet()</literal>:
+ </para>
+
+ <programlisting><![CDATA[protected void doGet(HttpServletRequest request,
+ HttpServletResponse response)
+ throws ServletException, IOException {
+
+ try {
+ // Begin unit of work
+ HibernateUtil.getSessionFactory()
+ .getCurrentSession().beginTransaction();
+
+ // Process request and render page...
+
+ // End unit of work
+ HibernateUtil.getSessionFactory()
+ .getCurrentSession().getTransaction().commit();
+
+ } catch (Exception ex) {
+ HibernateUtil.getSessionFactory()
+ .getCurrentSession().getTransaction().rollback();
+ throw new ServletException(ex);
+ }
+
+}]]></programlisting>
+
+ <para>
+ La pattern que nous utilisons ici est appelé <emphasis>session-per-request</emphasis>.
+ Lorsqu'une requête touche la servlet, une nouvelle <literal>Session</literal> hibernate est
+ ouverte à l'invocationde <literal>getCurrentSession()</literal> sur la
+ <literal>SessionFactory</literal>. Ensuite, une transaction avec la base de données est démarrée—
+ tous les accès à la base de données interviennent au sein de la transactiton, peu importe que les données
+ soient lues ou écrites (nous n'utilisons pas le mode auto-commit dans les applications).
+ </para>
+
+ <para>
+ Ensuite, les actions possibles de la requêtes sont exécutées et la réponse HTML
+ est rendue. Nous en parlerons plus tard.
+ </para>
+
+ <para>
+ Enfin, l'unité de travail s'achève lorsque l'exécution et le rendu sont achevés.
+ Si un problème survient lors de ces deux phases, une exception est soulevée et la
+ transaction avec la base de données subit un rollback. Voila pour le pattern
+ <literal>session-per-request</literal>. Au lieu d'un code de démarcation de transaction
+ au sein de chaque servlet, vous pouvez écrire un filtre de servlet.
+ Voir le site Hibernate et le Wiki pour plus d'information sur ce pattern, appelé
+ <emphasis>Open Session in View</emphasis>— vous en aurez besoin dès que vous
+ utiliserez des JSPs et non plus des servlets pour le rendu de vos vues.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-webapp-processing">
+ <title>Procéder et rendre</title>
+
+ <para>
+ Implémentons l'exécution de la requête et le rendu de la page.
+ </para>
+
+<programlisting><![CDATA[// Write HTML header
+PrintWriter out = response.getWriter();
+out.println("<html><head><title>Event Manager</title></head><body>");
+
+// Handle actions
+if ( "store".equals(request.getParameter("action")) ) {
+
+ String eventTitle = request.getParameter("eventTitle");
+ String eventDate = request.getParameter("eventDate");
+
+ if ( "".equals(eventTitle) || "".equals(eventDate) ) {
+ out.println("<b><i>Please enter event title and date.</i></b>");
+ } else {
+ createAndStoreEvent(eventTitle, dateFormatter.parse(eventDate));
+ out.println("<b><i>Added event.</i></b>");
+ }
+}
+
+// Print page
+printEventForm(out);
+listEvents(out);
+
+// Write HTML footer
+out.println("</body></html>");
+out.flush();
+out.close();]]></programlisting>
+
+ <para>
+ Ce style de code avec un mix de Java et d'HTML ne serait pas scalable
+ dans une application plus complexe—gardez à l'esprit que nous ne faisons qu'illustrer
+ les concepts basiques d'Hibernate dans ce tutoriel. Ce code affiche une en tête et un pied de page
+ HTML. Dans cette page, sont affichés un formulaire pour la saisie d'évènements ainsi
+ qu'une liste de tous les évènements de la base de données. La première méthode
+ est triviale est ne fait que sortir de l'HTML:
+ </para>
+
+ <programlisting><![CDATA[private void printEventForm(PrintWriter out) {
+ out.println("<h2>Add new event:</h2>");
+ out.println("<form>");
+ out.println("Title: <input name='eventTitle' length='50'/><br/>");
+ out.println("Date (e.g. 24.12.2009): <input name='eventDate' length='10'/><br/>");
+ out.println("<input type='submit' name='action' value='store'/>");
+ out.println("</form>");
+}]]></programlisting>
+
+ <para>
+ La méthode <literal>listEvents()</literal> utilise la
+ <literal>Session</literal> Hibernate liée au thread courant pour exécuter la
+ requête:
+ </para>
+
+ <programlisting><![CDATA[private void listEvents(PrintWriter out) {
+ List result = HibernateUtil.getSessionFactory()
+ .getCurrentSession().createCriteria(Event.class).list();
+ if (result.size() > 0) {
+ out.println("<h2>Events in database:</h2>");
+ out.println("<table border='1'>");
+ out.println("<tr>");
+ out.println("<th>Event title</th>");
+ out.println("<th>Event date</th>");
+ out.println("</tr>");
+ for (Iterator it = result.iterator(); it.hasNext();) {
+ Event event = (Event) it.next();
+ out.println("<tr>");
+ out.println("<td>" + event.getTitle() + "</td>");
+ out.println("<td>" + dateFormatter.format(event.getDate()) + "</td>");
+ out.println("</tr>");
+ }
+ out.println("</table>");
+ }
+}]]></programlisting>
+
+ <para>
+ FEnfin, l'action <literal>store</literal> renvoie à la méthode
+ <literal>createAndStoreEvent()</literal>, qui utilise aussi la
+ <literal>Session</literal> du thread courant:
+ </para>
+
+ <programlisting><![CDATA[protected void createAndStoreEvent(String title, Date theDate) {
+ Event theEvent = new Event();
+ theEvent.setTitle(title);
+ theEvent.setDate(theDate);
+
+ HibernateUtil.getSessionFactory()
+ .getCurrentSession().save(theEvent);
+}]]></programlisting>
+
+ <para>
+ La servlet est faite. Une requête à la servlet sera exécutée par une seule
+ <literal>Session</literal> et <literal>Transaction</literal>. Comme pour une application
+ standalone, Hibernate peut automatiquement lier ces objets au thread courant d'exécution.
+ Cela vous laisse la liberté de séparer votre code en couches et d'accéder à la
+ <literal>SessionFactory</literal> par le moyen que vous voulez.
+ Généralement, vous utiliserez des conceptions plus sophistiquées et déplacerez
+ le code d'accès aux données dans une couche DAO. Voir le wiki Hibernate pour plus
+ d'exemples.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-webapp-deploy">
+ <title>Déployer et tester</title>
+
+ <para>
+ Pour déployer cette application, vous devez créer une archive Web, un War. Ajoutez
+ la cible Ant suivante dans votre <literal>build.xml</literal>:
+ </para>
+
+<programlisting><![CDATA[<target name="war" depends="compile">
+ <war destfile="hibernate-tutorial.war" webxml="web.xml">
+ <lib dir="${librarydir}">
+ <exclude name="jsdk*.jar"/>
+ </lib>
+
+ <classes dir="${targetdir}"/>
+ </war>
+</target>]]></programlisting>
+
+ <para>
+ Cette cible créé un fichier nommé <literal>hibernate-tutorial.war</literal>
+ dans le répertoire de votre projet. Elle package les bibliothèques et le descripteur <literal>web.xml</literal>
+ qui est attendu dans le répertoire racine de votre projet:
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
+<web-app version="2.4"
+ xmlns="http://java.sun.com/xml/ns/j2ee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
+
+ <servlet>
+ <servlet-name>Event Manager</servlet-name>
+ <servlet-class>events.EventManagerServlet</servlet-class>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>Event Manager</servlet-name>
+ <url-pattern>/eventmanager</url-pattern>
+ </servlet-mapping>
+</web-app>]]></programlisting>
+
+ <para>
+ Avant de compiler et déployer l'application web, notez qu'une bibliothèque supplémentaire
+ est requise: <literal>jsdk.jar</literal>. C'est le kit de développement de Servlet Java,
+ si vous ne disposez pas de cette bibliothèque, prenez la sur le site de Sun et copiez la
+ dans votre répertoire des bibliothèques. Cependant, elle ne sera utilisée uniquement pour la
+ compilation et sera exclue du paackage WAR.
+ </para>
+
+ <para>
+ Pour construire et déployer, appelez <literal>ant war</literal> dans votre projet et
+ copier le fichier <literal>hibernate-tutorial.war</literal> dans le répertoire <literal>webapp</literal> de tomcat
+ Si vous n'avez pas installé Tomcat, téléchargez le et suivez la notice d'installation.
+ Vous n'avez pas à modifier la configuration Tomcat pour déployer cette application.
+ </para>
+
+ <para>
+ Une fois l'application déployée et Tomcat lancé, accédez à l'application via
+ <literal>http://localhost:8080/hibernate-tutorial/eventmanager</literal>.
+ Assurez vous de consulter les traces tomcat pour observer l'initialisation
+ d'Hibernate à la première requête touchant votre servlet (l'initialisation statique dans <literal>HibernateUtil</literal>
+ est invoquée) et pour vérifier qu'aucune exception ne survienne.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="tutorial-summary" revision="1">
+ <title>Résumé</title>
+
+ <para>
+ Ce didacticiel a couvert les bases de l'écriture d'une simple application Hibernate ainsi qu'une petite application web.
+ </para>
+
+ <para>
+ Si vous êtes déjà confiants avec Hibernate, continuez à parcourir les sujets que vous trouvez
+ intéressants à travers la table des matières de la documentation de référence - les plus
+ demandés sont le traitement transactionnel (<xref linkend="transactions"/>), la performance
+ des récupérations d'information (<xref linkend="performance"/>), ou l'utilisation de l'API
+ (<xref linkend="objectstate"/>) et les fonctionnalités des requêtes (<xref linkend="objectstate-querying"/>).
+ </para>
+
+ <para>
+ N'oubliez pas de vérifier le site web d'Hibernate pour d'autres didacticiels (plus spécialisés).
+ </para>
+
+ </sect1>
+
+</chapter>
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/xml.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/xml.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/fr-FR/src/main/docbook/modules/xml.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,292 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<chapter id="xml">
+ <title>Mapping XML</title>
+
+ <para><emphasis>
+ Notez que cette fonctionnalité est expérimentale dans Hibernate 3.0 et
+ est en développement extrêmement actif.
+ </emphasis></para>
+
+ <sect1 id="xml-intro" revision="1">
+ <title>Travailler avec des données XML</title>
+
+ <para>
+ Hibernate vous laisse travailler avec des données XML persistantes de la
+ même manière que vous travaillez avec des POJOs persistants. Un arbre XML
+ peut être vu comme une autre manière de représenter les données relationnelles
+ au niveau objet, à la place des POJOs.
+ </para>
+
+ <para>
+ Hibernate supporte dom4j en tant qu'API pour la manipulation des arbres XML.
+ Vous pouvez écrire des requêtes qui récupèrent des arbres dom4j à partie de la
+ base de données, et avoir toutes les modifications que vous faites sur l'arbre
+ automatiquement synchronisées dans la base de données. Vous pouvez même prendre
+ un document XML, l'analyser en utilisant dom4j, et l'écrire dans la base de
+ données via les opérations basiques d'Hibernate :
+ <literal>persist(), saveOrUpdate(), merge(), delete(), replicate()</literal>
+ (merge() n'est pas encore supporté).
+ </para>
+
+ <para>
+ Cette fonctionnalité a plusieurs applications dont l'import/export de données,
+ l'externalisation d'entités via JMS ou SOAP et les rapports XSLT.
+ </para>
+
+ <para>
+ Un simple mapping peut être utilisé pour simultanément mapper les propriétés
+ d'une classe et les noeuds d'un document XML vers la base de données, ou,
+ si il n'y a pas de classe à mapper, il peut être utilisé juste pour mapper
+ le XML.
+ </para>
+
+ <sect2 id="xml-intro-mapping">
+ <title>Spécifier le mapping XML et le mapping d'une classe ensemble</title>
+
+ <para>
+ Voici un exemple de mapping d'un POJO et du XML simultanément :
+ </para>
+
+ <programlisting><![CDATA[<class name="Account"
+ table="ACCOUNTS"
+ node="account">
+
+ <id name="accountId"
+ column="ACCOUNT_ID"
+ node="@id"/>
+
+ <many-to-one name="customer"
+ column="CUSTOMER_ID"
+ node="customer/@id"
+ embed-xml="false"/>
+
+ <property name="balance"
+ column="BALANCE"
+ node="balance"/>
+
+ ...
+
+</class>]]></programlisting>
+ </sect2>
+
+ <sect2 id="xml-onlyxml">
+ <title>Spécifier seulement un mapping XML</title>
+
+ <para>
+ Voici un exemple dans lequel il n'y a pas de class POJO :
+ </para>
+
+ <programlisting><![CDATA[<class entity-name="Account"
+ table="ACCOUNTS"
+ node="account">
+
+ <id name="id"
+ column="ACCOUNT_ID"
+ node="@id"
+ type="string"/>
+
+ <many-to-one name="customerId"
+ column="CUSTOMER_ID"
+ node="customer/@id"
+ embed-xml="false"
+ entity-name="Customer"/>
+
+ <property name="balance"
+ column="BALANCE"
+ node="balance"
+ type="big_decimal"/>
+
+ ...
+
+</class>]]></programlisting>
+
+ <para>
+ Ce mapping vous permet d'accéder aux données comme un arbre dom4j, ou comme
+ un graphe de paire nom de propriété/valeur (<literal>Map</literal>s java). Les
+ noms des propriétés sont des constructions purement logiques qui peuvent être
+ référées des dans requêtes HQL.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="xml-mapping" revision="1">
+ <title>Métadonnées du mapping XML</title>
+
+ <para>
+ Plusieurs éléments du mapping Hibernate acceptent l'attribut <literal>node</literal>.
+ Ceci vous permet de spécifier le nom d'un attribut XML ou d'un élément qui
+ contient la propriété ou les données de l'entité. Le format de l'attribut
+ <literal>node</literal> doit être un des suivants :
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para><literal>"element-name"</literal> - mappe vers l'élément XML nommé</para>
+ </listitem>
+ <listitem>
+ <para><literal>"@attribute-name"</literal> - mappe vers l'attribut XML nommé</para>
+ </listitem>
+ <listitem>
+ <para><literal>"."</literal> - mappe vers le parent de l'élément</para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>"element-name/@attribute-name"</literal> -
+ mappe vers l'élément nommé de l'attribut nommé
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Pour des collections et de simples associations valuées, il y a un attribut
+ <literal>embed-xml</literal> supplémentaire. Si <literal>embed-xml="true"</literal>,
+ qui est la valeur par défaut, l'arbre XML pour l'entité associée (ou la collection
+ des types de valeurs) sera embarquée directement dans l'arbre XML pour l'entité qui
+ possède l'association. Sinon, si <literal>embed-xml="false"</literal>, alors
+ seule la valeur de l'identifiant référencé apparaîtra dans le XML pour de simples
+ associations de points, et les collections n'appraîtront simplement pas.
+ </para>
+
+ <para>
+ Vous devriez faire attention à ne pas laisser <literal>embed-xml="true"</literal>
+ pour trop d'associations, puisque XML ne traite pas bien les liens circurlaires.
+ </para>
+
+ <programlisting><![CDATA[<class name="Customer"
+ table="CUSTOMER"
+ node="customer">
+
+ <id name="id"
+ column="CUST_ID"
+ node="@id"/>
+
+ <map name="accounts"
+ node="."
+ embed-xml="true">
+ <key column="CUSTOMER_ID"
+ not-null="true"/>
+ <map-key column="SHORT_DESC"
+ node="@short-desc"
+ type="string"/>
+ <one-to-many entity-name="Account"
+ embed-xml="false"
+ node="account"/>
+ </map>
+
+ <component name="name"
+ node="name">
+ <property name="firstName"
+ node="first-name"/>
+ <property name="initial"
+ node="initial"/>
+ <property name="lastName"
+ node="last-name"/>
+ </component>
+
+ ...
+
+</class>]]></programlisting>
+
+ <para>
+ dans ce cas, nous avons décidé d'embarquer la collection d'identifiants de compte,
+ mais pas les données actuelles du compte. La requête HQL suivante :
+ </para>
+
+ <programlisting><![CDATA[from Customer c left join fetch c.accounts where c.lastName like :lastName]]></programlisting>
+
+ <para>
+ devrait retourner l'ensemble de données suivant :
+ </para>
+
+ <programlisting><![CDATA[<customer id="123456789">
+ <account short-desc="Savings">987632567</account>
+ <account short-desc="Credit Card">985612323</account>
+ <name>
+ <first-name>Gavin</first-name>
+ <initial>A</initial>
+ <last-name>King</last-name>
+ </name>
+ ...
+</customer>]]></programlisting>
+
+ <para>
+ Si vous positionnez <literal>embed-xml="true"</literal> sur le mapping
+ <literal><one-to-many></literal>, les données pourraient
+ ressembler plus à ça :
+ </para>
+
+ <programlisting><![CDATA[<customer id="123456789">
+ <account id="987632567" short-desc="Savings">
+ <customer id="123456789"/>
+ <balance>100.29</balance>
+ </account>
+ <account id="985612323" short-desc="Credit Card">
+ <customer id="123456789"/>
+ <balance>-2370.34</balance>
+ </account>
+ <name>
+ <first-name>Gavin</first-name>
+ <initial>A</initial>
+ <last-name>King</last-name>
+ </name>
+ ...
+</customer>]]></programlisting>
+
+ </sect1>
+
+
+ <sect1 id="xml-manipulation" revision="1">
+ <title>Manipuler des données XML</title>
+
+ <para>
+ Relisons et mettons à jour des documents XML dans l'application. Nous faisons
+ ça en obtenant une session dom4j :
+ </para>
+
+ <programlisting><![CDATA[Document doc = ....;
+
+Session session = factory.openSession();
+Session dom4jSession = session.getSession(EntityMode.DOM4J);
+Transaction tx = session.beginTransaction();
+
+List results = dom4jSession
+ .createQuery("from Customer c left join fetch c.accounts where c.lastName like :lastName")
+ .list();
+for ( int i=0; i<results.size(); i++ ) {
+ //add the customer data to the XML document
+ Element customer = (Element) results.get(i);
+ doc.add(customer);
+}
+
+tx.commit();
+session.close();]]></programlisting>
+
+ <programlisting><![CDATA[Session session = factory.openSession();
+Session dom4jSession = session.getSession(EntityMode.DOM4J);
+Transaction tx = session.beginTransaction();
+
+Element cust = (Element) dom4jSession.get("Customer", customerId);
+for ( int i=0; i<results.size(); i++ ) {
+ Element customer = (Element) results.get(i);
+ //change the customer name in the XML and database
+ Element name = customer.element("name");
+ name.element("first-name").setText(firstName);
+ name.element("initial").setText(initial);
+ name.element("last-name").setText(lastName);
+}
+
+tx.commit();
+session.close();]]></programlisting>
+
+ <para>
+ Il est extrêmement utile de combiner cette fonctionnalité avec l'opération
+ <literal>replicate()</literal> d'Hibernate pour implémenter des imports/exports
+ de données XML.
+ </para>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/css/html.css
===================================================================
--- trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/css/html.css (rev 0)
+++ trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/css/html.css 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,97 @@
+A {
+ color: #003399;
+}
+
+A:active {
+ color: #003399;
+}
+
+A:visited {
+ color: #888888;
+}
+
+P, OL, UL, LI, DL, DT, DD, BLOCKQUOTE {
+ color: #000000;
+}
+
+TD, TH, SPAN {
+ color: #000000;
+}
+
+BLOCKQUOTE {
+ margin-right: 0px;
+}
+
+
+H1, H2, H3, H4, H5, H6 {
+ color: #000000;
+ font-weight:500;
+ margin-top:10px;
+ padding-top:15px;
+}
+
+TABLE {
+ border-collapse: collapse;
+ border-spacing:0;
+ border: 1px thin black;
+ empty-cells: hide;
+}
+
+TD {
+ padding: 4pt;
+}
+
+H1 { font-size: 150%; }
+H2 { font-size: 140%; }
+H3 { font-size: 110%; font-weight: bold; }
+H4 { font-size: 110%; font-weight: bold;}
+H5 { font-size: 100%; font-style: italic; }
+H6 { font-size: 100%; font-style: italic; }
+
+TT {
+font-size: 90%;
+ font-family: "Courier New", Courier, monospace;
+ color: #000000;
+}
+
+PRE {
+font-size: 100%;
+ padding: 5px;
+ border-style: solid;
+ border-width: 1px;
+ border-color: #CCCCCC;
+ background-color: #F4F4F4;
+}
+
+UL, OL, LI {
+ list-style: disc;
+}
+
+HR {
+ width: 100%;
+ height: 1px;
+ background-color: #CCCCCC;
+ border-width: 0px;
+ padding: 0px;
+ color: #CCCCCC;
+}
+
+.variablelist {
+ padding-top: 10;
+ padding-bottom:10;
+ margin:0;
+}
+
+.itemizedlist, UL {
+ padding-top: 0;
+ padding-bottom:0;
+ margin:0;
+}
+
+.term {
+ font-weight:bold;
+}
+
+
+
+
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/AuthorWork.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/AuthorWork.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/AuthorWork.zargo
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/AuthorWork.zargo
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/CustomerOrderProduct.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/CustomerOrderProduct.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/CustomerOrderProduct.zargo
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/CustomerOrderProduct.zargo
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/EmployerEmployee.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/EmployerEmployee.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/EmployerEmployee.zargo
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/EmployerEmployee.zargo
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/full_cream.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/full_cream.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/full_cream.svg
===================================================================
--- trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/full_cream.svg (rev 0)
+++ trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/full_cream.svg 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,429 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"
+[
+ <!ATTLIST svg
+ xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink">
+]>
+<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
+<svg
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="354.331"
+ height="336.614"
+ id="svg1">
+ <defs
+ id="defs3">
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop128" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="1"
+ id="stop129" />
+ </linearGradient>
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient130"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ <radialGradient
+ cx="0.5"
+ cy="0.5"
+ fx="0.5"
+ fy="0.5"
+ r="0.5"
+ id="radialGradient131"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ </defs>
+ <g
+ transform="matrix(0.823795,0,0,0.823795,0.120302,5.25349)"
+ style="font-size:12;"
+ id="g659">
+ <rect
+ width="212.257"
+ height="57.2441"
+ x="17.9576"
+ y="100.132"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect137" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ transform="matrix(0.743454,0,0,0.482981,6.46949,52.2178)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect132" />
+ </g>
+ <rect
+ width="325.86"
+ height="63.6537"
+ x="17.4083"
+ y="15.194"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect136" />
+ <rect
+ width="325.86"
+ height="63.6537"
+ x="13.6713"
+ y="12.4966"
+ style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect126" />
+ <g
+ transform="matrix(1.14345,0,0,0.729078,-1.67818,105.325)"
+ style="font-size:12;"
+ id="g164">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="222.966"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect138" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="221.002"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect133" />
+ </g>
+ <text
+ x="170.824753"
+ y="58.402939"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text183">
+ <tspan
+ x="170.824997"
+ y="58.402901"
+ id="tspan360">
+Application</tspan>
+ </text>
+ <text
+ x="178.076340"
+ y="364.281433"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text197">
+ <tspan
+ x="178.076004"
+ y="364.281006"
+ id="tspan421">
+Database</tspan>
+ </text>
+ <text
+ x="68.605331"
+ y="138.524582"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text216">
+ <tspan
+ x="68.605301"
+ y="138.524994"
+ id="tspan384">
+SessionFactory</tspan>
+ </text>
+ <rect
+ width="67.0014"
+ height="101.35"
+ x="196.927"
+ y="89.2389"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect387" />
+ <rect
+ width="67.0014"
+ height="101.35"
+ x="194.633"
+ y="86.4389"
+ style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect388" />
+ <text
+ x="249.108841"
+ y="173.885559"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text389">
+ <tspan
+ x="249.108994"
+ y="173.886002"
+ id="tspan392">
+Session</tspan>
+ </text>
+ <rect
+ width="73.0355"
+ height="101.35"
+ x="270.995"
+ y="90.0018"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect395" />
+ <rect
+ width="73.0355"
+ height="101.35"
+ x="267.869"
+ y="87.2018"
+ style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect396" />
+ <text
+ x="328.593658"
+ y="174.715622"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text397">
+ <tspan
+ x="328.593994"
+ y="174.716003"
+ id="tspan563">
+Transaction</tspan>
+ </text>
+ <g
+ transform="matrix(0.29544,0,0,0.397877,9.70533,103.96)"
+ style="font-size:12;"
+ id="g565">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect566" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect567" />
+ </g>
+ <text
+ x="25.592752"
+ y="204.497803"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:10;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text568">
+ <tspan
+ x="25.592800"
+ y="204.498001"
+ id="tspan662">
+TransactionFactory</tspan>
+ </text>
+ <g
+ transform="matrix(0.298082,0,0,0.397877,99.6898,103.96)"
+ style="font-size:12;"
+ id="g573">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect574" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect575" />
+ </g>
+ <text
+ x="134.030670"
+ y="205.532791"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:10;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text576">
+ <tspan
+ x="134.031006"
+ y="205.533005"
+ id="tspan664">
+ConnectionProvider</tspan>
+ </text>
+ <g
+ transform="matrix(1.14345,0,0,0.729078,-1.67818,38.9539)"
+ style="font-size:12;"
+ id="g587">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="222.966"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect588" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="221.002"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect589" />
+ </g>
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="25.6196"
+ y="206.028"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect594" />
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="24.4229"
+ y="204.135"
+ style="font-size:12;fill:#b3b3b3;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect595" />
+ <text
+ x="85.575645"
+ y="282.300354"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text596">
+ <tspan
+ x="85.575600"
+ y="282.299988"
+ id="tspan607">
+JNDI</tspan>
+ </text>
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="236.937"
+ y="206.791"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect610" />
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="235.741"
+ y="204.898"
+ style="font-size:12;fill:#b3b3b3;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect611" />
+ <text
+ x="342.093201"
+ y="283.226410"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text612">
+ <tspan
+ x="342.092987"
+ y="283.226013"
+ id="tspan621">
+JTA</tspan>
+ </text>
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="130.134"
+ y="206.791"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect616" />
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="128.937"
+ y="204.898"
+ style="font-size:12;fill:#b3b3b3;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect617" />
+ <text
+ x="212.445343"
+ y="283.226410"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text618">
+ <tspan
+ x="212.445007"
+ y="283.226013"
+ id="tspan623">
+JDBC</tspan>
+ </text>
+ <g
+ transform="matrix(0.823795,0,0,0.823795,0.120302,6.19341)"
+ style="font-size:12;"
+ id="g637">
+ <g
+ transform="matrix(0.499515,0,0,0.415467,-0.237339,5.61339)"
+ id="g167">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect134" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect135" />
+ </g>
+ <text
+ x="33.749969"
+ y="50.589706"
+ style="font-size:11;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text188">
+ <tspan
+ x="33.750000"
+ y="50.589699"
+ id="tspan635">
+Transient Objects</tspan>
+ </text>
+ </g>
+ <g
+ transform="matrix(0.823795,0,0,0.823795,0.120302,5.25349)"
+ style="font-size:12;"
+ id="g644">
+ <g
+ transform="matrix(0.297486,0,0,0.516482,230.251,36.9178)"
+ id="g364">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect365" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect366" />
+ </g>
+ <text
+ x="277.123230"
+ y="85.155571"
+ style="font-size:11;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text367">
+ <tspan
+ x="277.122986"
+ y="85.155602"
+ id="tspan631">
+Persistent</tspan>
+ <tspan
+ x="277.122986"
+ y="96.155602"
+ id="tspan633">
+Objects</tspan>
+ </text>
+ </g>
+</svg>
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/hibernate_logo_a.png
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/hibernate_logo_a.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/lite.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/lite.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/lite.svg
===================================================================
--- trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/lite.svg (rev 0)
+++ trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/lite.svg 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,334 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"
+[
+ <!ATTLIST svg
+ xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink">
+]>
+<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
+<svg
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="318.898"
+ height="248.031"
+ id="svg1">
+ <defs
+ id="defs3">
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop128" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="1"
+ id="stop129" />
+ </linearGradient>
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient130"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ <radialGradient
+ cx="0.5"
+ cy="0.5"
+ fx="0.5"
+ fy="0.5"
+ r="0.5"
+ id="radialGradient131"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ </defs>
+ <rect
+ width="291.837"
+ height="57.0074"
+ x="17.3169"
+ y="18.646"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect136" />
+ <rect
+ width="291.837"
+ height="57.0074"
+ x="13.9703"
+ y="16.2302"
+ style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect126" />
+ <g
+ transform="matrix(0.326107,0,0,0.765831,9.59261,8.98517)"
+ style="font-size:12;"
+ id="g161">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect137" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect132" />
+ </g>
+ <g
+ transform="matrix(1.02406,0,0,0.652953,0.223384,39.9254)"
+ style="font-size:12;"
+ id="g164">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="222.966"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect138" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="221.002"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect133" />
+ </g>
+ <g
+ transform="matrix(0.449834,0,0,0.338463,-3.15909,9.73319)"
+ style="font-size:12;"
+ id="g167">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect134" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect135" />
+ </g>
+ <text
+ x="302.277679"
+ y="65.943230"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text183">
+ <tspan
+ x="302.277954"
+ y="65.943184"
+ id="tspan360">
+Application</tspan>
+ </text>
+ <text
+ x="36.235924"
+ y="63.796055"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:14;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text188">
+ <tspan
+ x="36.235950"
+ y="63.796051"
+ id="tspan427">
+Transient Objects</tspan>
+ </text>
+ <text
+ x="180.416245"
+ y="290.543701"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text197">
+ <tspan
+ x="180.415939"
+ y="290.543549"
+ id="tspan421">
+Database</tspan>
+ </text>
+ <text
+ x="25.037701"
+ y="179.154755"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text216">
+ <tspan
+ x="25.037655"
+ y="179.154648"
+ id="tspan384">
+SessionFactory</tspan>
+ </text>
+ <g
+ transform="matrix(0.252763,0,0,0.765831,109.104,8.98517)"
+ style="font-size:12;"
+ id="g386">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect387" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect388" />
+ </g>
+ <g
+ transform="matrix(0.297394,0,0,0.572692,101.502,21.6359)"
+ style="font-size:12;"
+ id="g364">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect365" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect366" />
+ </g>
+ <text
+ x="202.746506"
+ y="102.992203"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:14;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text367">
+ <tspan
+ x="202.746948"
+ y="102.992249"
+ id="tspan423">
+Persistent</tspan>
+ <tspan
+ x="202.746948"
+ y="116.992355"
+ id="tspan425">
+Objects</tspan>
+ </text>
+ <text
+ x="174.458496"
+ y="180.080795"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text389">
+ <tspan
+ x="174.458618"
+ y="180.080338"
+ id="tspan392">
+Session</tspan>
+ </text>
+ <g
+ transform="matrix(0.127369,0,0,0.765831,188.675,8.98517)"
+ style="font-size:12;"
+ id="g394">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect395" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect396" />
+ </g>
+ <text
+ x="260.413269"
+ y="179.154739"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text397">
+ <tspan
+ x="260.412964"
+ y="179.154343"
+ id="tspan400">
+JDBC</tspan>
+ </text>
+ <g
+ transform="matrix(0.127369,0,0,0.765831,229.156,8.98517)"
+ style="font-size:12;"
+ id="g405">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect406" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect407" />
+ </g>
+ <text
+ x="320.606903"
+ y="179.154739"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text408">
+ <tspan
+ x="320.606964"
+ y="179.154343"
+ id="tspan417">
+JNDI</tspan>
+ </text>
+ <g
+ transform="matrix(0.127369,0,0,0.765831,269.281,8.98517)"
+ style="font-size:12;"
+ id="g411">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect412" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect413" />
+ </g>
+ <text
+ x="377.096313"
+ y="179.154739"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text414">
+ <tspan
+ x="377.096008"
+ y="179.154999"
+ id="tspan145">
+JTA</tspan>
+ </text>
+</svg>
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/overview.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/overview.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/overview.svg
===================================================================
--- trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/overview.svg (rev 0)
+++ trunk/Hibernate3/documentation/manual/fr-FR/src/main/resources/shared/images/overview.svg 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,250 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"
+[
+ <!ATTLIST svg
+ xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink">
+]>
+<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
+<svg
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="248.031"
+ height="248.031"
+ id="svg1">
+ <defs
+ id="defs3">
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop128" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="1"
+ id="stop129" />
+ </linearGradient>
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient130"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ <radialGradient
+ cx="0.5"
+ cy="0.5"
+ fx="0.5"
+ fy="0.5"
+ r="0.5"
+ id="radialGradient131"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ </defs>
+ <g
+ transform="matrix(0.771934,0,0,0.771934,4.36019,-3.02123)"
+ style="font-size:12;"
+ id="g158">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="17.3527"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect136" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="15.3883"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect126" />
+ </g>
+ <g
+ transform="matrix(0.771934,0,0,0.771934,4.36019,3.04452)"
+ style="font-size:12;"
+ id="g161">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect137" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect132" />
+ </g>
+ <g
+ transform="matrix(0.771934,0,0,0.771934,4.36019,8.0993)"
+ style="font-size:12;"
+ id="g164">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="222.966"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect138" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="221.002"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect133" />
+ </g>
+ <g
+ transform="matrix(0.771934,0,0,0.543505,2.59104,21.1103)"
+ style="font-size:12;"
+ id="g167">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect134" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect135" />
+ </g>
+ <text
+ x="105.392174"
+ y="56.568123"
+ transform="scale(0.771934,0.771934)"
+ style="font-size:24;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text183">
+ <tspan
+ x="105.392273"
+ y="56.568146"
+ id="tspan186">
+Application</tspan>
+ </text>
+ <text
+ x="81.820183"
+ y="103.149330"
+ transform="scale(0.771934,0.771934)"
+ style="font-size:20;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text188">
+ <tspan
+ x="81.820213"
+ y="103.149727"
+ id="tspan206">
+Persistent Objects</tspan>
+ </text>
+ <text
+ x="111.548180"
+ y="278.927887"
+ transform="scale(0.771934,0.771934)"
+ style="font-size:24;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text197">
+ <tspan
+ x="111.547874"
+ y="278.927551"
+ id="tspan200">
+Database</tspan>
+ </text>
+ <text
+ x="94.436180"
+ y="153.805740"
+ transform="scale(0.771934,0.771934)"
+ style="font-size:24;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text216">
+ <tspan
+ x="94.436180"
+ y="153.805740"
+ id="tspan221">
+HIBERNATE</tspan>
+ </text>
+ <g
+ transform="matrix(0.771934,0,0,0.771934,2.59083,1.02261)"
+ style="font-size:12;"
+ id="g254">
+ <g
+ transform="translate(4.58374,2.61928)"
+ id="g176">
+ <g
+ transform="matrix(0.571429,0,0,0.67347,-10.6174,117.093)"
+ id="g170">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect171" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect172" />
+ </g>
+ <g
+ transform="matrix(0.571429,0,0,0.67347,138.682,117.093)"
+ id="g173">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect174" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect175" />
+ </g>
+ </g>
+ <text
+ x="47.259438"
+ y="182.367538"
+ style="font-weight:bold;stroke-width:1pt;font-family:Courier;"
+ id="text191">
+ <tspan
+ x="47.259399"
+ y="182.367996"
+ id="tspan212">
+hibernate.</tspan>
+ <tspan
+ x="47.259399"
+ y="194.367996"
+ id="tspan214">
+properties</tspan>
+ </text>
+ <text
+ x="198.523010"
+ y="188.260941"
+ style="font-weight:normal;stroke-width:1pt;font-family:helvetica;"
+ id="text194">
+ <tspan
+ id="tspan195">
+XML Mapping</tspan>
+ </text>
+ </g>
+</svg>
Added: trunk/Hibernate3/documentation/manual/ja-JP/pom.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ja-JP/pom.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ja-JP/pom.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-manual</artifactId>
+ <version>3.3.0.beta1</version>
+ <relativePath>../../../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>hibernate-manual-${translation}</artifactId>
+ <packaging>pom</packaging>
+ <name>Hibernate Manual (${translation})</name>
+
+ <properties>
+ <translation>ja-JP</translation>
+ </properties>
+
+</project>
Added: trunk/Hibernate3/documentation/manual/ja-JP/readme_ja.txt
===================================================================
--- trunk/Hibernate3/documentation/manual/ja-JP/readme_ja.txt (rev 0)
+++ trunk/Hibernate3/documentation/manual/ja-JP/readme_ja.txt 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1 @@
+This is v3.2.0
\ No newline at end of file
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/master.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/master.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/master.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,231 @@
+<?xml version="1.0" encoding="Shift_JIS"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3CR3//EN"
+ "../support/docbook-dtd/docbookx.dtd"
+[
+<!ENTITY tutorial SYSTEM "modules/tutorial.xml">
+<!ENTITY architecture SYSTEM "modules/architecture.xml">
+<!ENTITY configuration SYSTEM "modules/configuration.xml">
+<!ENTITY persistent-classes SYSTEM "modules/persistent_classes.xml">
+<!ENTITY basic-mapping SYSTEM "modules/basic_mapping.xml">
+<!ENTITY collection-mapping SYSTEM "modules/collection_mapping.xml">
+<!ENTITY association-mapping SYSTEM "modules/association_mapping.xml">
+<!ENTITY component-mapping SYSTEM "modules/component_mapping.xml">
+<!ENTITY inheritance-mapping SYSTEM "modules/inheritance_mapping.xml">
+<!ENTITY session-api SYSTEM "modules/session_api.xml">
+<!ENTITY transactions SYSTEM "modules/transactions.xml">
+<!ENTITY events SYSTEM "modules/events.xml">
+<!ENTITY batch SYSTEM "modules/batch.xml">
+<!ENTITY query-hql SYSTEM "modules/query_hql.xml">
+<!ENTITY query-criteria SYSTEM "modules/query_criteria.xml">
+<!ENTITY query-sql SYSTEM "modules/query_sql.xml">
+<!ENTITY filters SYSTEM "modules/filters.xml">
+<!ENTITY xml SYSTEM "modules/xml.xml">
+<!ENTITY performance SYSTEM "modules/performance.xml">
+<!ENTITY toolset-guide SYSTEM "modules/toolset_guide.xml">
+<!ENTITY example-parentchild SYSTEM "modules/example_parentchild.xml">
+<!ENTITY example-weblog SYSTEM "modules/example_weblog.xml">
+<!ENTITY example-mappings SYSTEM "modules/example_mappings.xml">
+<!ENTITY best-practices SYSTEM "modules/best_practices.xml">
+]>
+
+<book lang="ja">
+
+ <bookinfo>
+ <title>HIBERNATE - Relational Persistence for Idiomatic Java</title>
+ <subtitle>Hibernate Reference Documentation</subtitle>
+ <releaseinfo>3.2 cr3</releaseinfo>
+ </bookinfo>
+
+ <toc/>
+
+ <preface id="preface" revision="2">
+ <title>O«</title>
+
+ <para>
+ ¡úÌéÆ«ɨ¢ÄAIuWFNgwü\tgEFAÆ[Vif[^x[XÉÖíéìÆÍ
+ ÏGÅcåÈÔðKvƵܷB
+ HibernateÍJava«ÌIuWFNg/[Vi}bsOc[Å·B
+ IuWFNg/[Vi}bsOiORMjÆÍA
+ IuWFNgf©çSQLx[XXL[}Ì[Vif[^fÖÆA
+ f[^\»ð}bsO·éiÎt¯éjZp̱ÆÅ·B
+ </para>
+
+ <para>
+ HibernateÍJavaNX©çf[^x[Xe[uÖi»µÄJavaf[^^©çSQLf[^^Öj
+ Ì}bsOð·é¾¯ÅÍÈAf[^ÌNGƳÌdgÝàñµÜ·B
+ ±Ì¨©°ÅSQLÆJDBCðgÁ½èìÆÅÌf[^Éïâ³êÄ¢½JÔðåÉí¸Å«Ü·B
+ </para>
+
+ <para>
+ HibernateÌÅIÚWÍAf[^Ìi±»ÉÖíéêÊIÈvO~OìÆÌ95©çJÒððú·é±ÆÅ·B
+ HibernateÍrWlXWbNÌÀÉXgAhvV[Wðg¤
+ f[^SAvP[VÉεÄÌxXg\
[VÅ éɯÜèܹñB
+ IuWFNgwühCfÆJavax[XÌÔwÅÌrWlXWbNÉεÄÅàðɧ¿Ü·B
+ µ©µHibernateÍx_ÅLÌSQLR[hðí¸Ü½ÍJvZ»µ½èA
+ \`®©çIuWFNgÌOtÖÆUgZbgðÏ··éÈÇÌA
+ êÊIÈ^XNÉàð§Âŵå¤B
+ </para>
+
+ <para>
+ HibernateâIuWFNg/[Vi}bsOA
+ ³çÉÍJavaªßÄÌûÍAȺÌXebvÉ]Áľ³¢F
+ </para>
+
+ <orderedlist>
+ <listitem>
+ <para>
+ <xref linkend="tutorial"/> ðÇñž³¢B
+ iKIÉðàµÄ¢«Ü·B
+ `
[gAÌ\[XR[hÍfBXgr
[VÌ
+ <literal>doc/reference/tutorial</literal> fBNgÉÜÜêĢܷB
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <xref linkend="architecture"/> ðÇñÅAHibernateªpÂ\È«ððµÄ¾³¢B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ HibernatefBXgr
[VÌ <literal>eg/</literal> fBNgð©Ä¾³¢B
+ ÈPÈX^hA[ÌAvP[Vª èÜ·B
+ JDBChCoð <literal>lib/</literal> fBNgÉRs[µÄA
+ ©ªÌf[^x[Xɤ³µ¢lðwè·éæ¤É <literal>etc/hibernate.properties</literal>
+ ðÒWµÄ¾³¢B
+ fBXgr
[VfBNg©çAR}hvvgÅ
+ <literal>ant eg</literal> Æ^CvµÄ¾³¢iAntðg¢Ü·jB
+ ܽWindows«ÅÍ <literal>build eg</literal> Æ^CvµÄ¾³¢B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ±Ìt@XhL
gðæêÌîñ¹ÆµÄpµÄ¾³¢B
+ AvP[VÝvÉ¢Ä̳çÈéîñâiKIÈ`
[gAðTµÄ¢éÈçA
+ <emphasis>Hibernate in Action</emphasis>ihttp://www.manning.com/bauerj
+ 𨷷ߵܷB
+ ܽhttp://caveatemptor.hibernate.org©ç
+ Hibernate in ActionÌáèÌAvP[Vð_E[hÅ«Ü·B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ FAQÍHibernateEFuTCgÉ èÜ·B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ T[hp[eBÌfAáA`
[gAÍHibernateEFuTCgÉNª èÜ·B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ HibernateEFuTCgÌR~
jeBGAÍAfUCp^[â³Ü´ÜÈ\
[V
+ iTomcat, JBoss AS, Struts, EJB, jÉ¢ÄÌæ¢îñ¹Å·B
+ </para>
+ </listitem>
+ </orderedlist>
+
+ <para>
+ ¿âª êÎHibernateEFuTCgÌ[UtH[ðpµÄ¾³¢B
+ ܽoO|[gÆtB[`NGXg̽ßJIRAÛèÇÕVXeðpӵĢܷB
+ HibernateÌJÉ»¡ª êÎAJÒ[OXgÉQÁµÄ¾³¢B
+ ±ÌhL
gð ȽÌ̾tÉ|óµ½¢êÍA
+ JÒ[OXgŽ¿ÉR^NgðæÁľ³¢B
+ </para>
+
+ <para>
+ ¤pJÌT|[gA»iÌT|[gAHibernateÌg[jOÍJBoss Inc.ªñµÄ¢Ü·
+ ihttp://www.hibernate.org/SupportTraining/ð©Ä¾³¢jB
+ HibernateÍProfessional Open SourcevWFNgA
+ »µÄJBoss Enterprise Middleware SystemiJEMSjv_NgXC[gÌNeBJR|[lgÅ·B
+ </para>
+
+ <sect1 id="preface-s1" revision="-1">
+
+ <title>ú{êóÉ¢Ä</title>
+
+ <para id="preface-s1-p1" revision="-1">
+ ±Ìú{êÅHibernate Reference DocumentiȺAú{êÅjÍA
+ HibernatevWFNgÌ|óvZXÉîâÄ쬳êĢܷB
+ ú{êÅÈçÑÉ´¶ÍLGPLCZXɶܷB
+ </para>
+
+ <para id="preface-s1-p2" revision="-1">
+ ú{êÅÌpÉæÁÄæOÒªíé çäésvÉεÄA
+ ´ÒA|óÒÈçÑÉ»ÌgDÍêØÌÛØ𢽵©ËÜ·B
+ ú{êÅÍëèðÜÞÂ\«ª é±ÆðF¯µ½ãŲp¾³¢B
+ àe̳mÈÓ¡ðc¬·é½ßÉÍA´¶ðÇÞ±Æ𨷷ߵܷB
+ ܽAàµú{êÅÉëèð©Â¯çê½êÍA|óÒɲA¢½¾¯êÎK¢Å·B
+ ½¾µàeÉÖµĄ̈â¢í¹ÉͶ©ËÜ·ÌÅA²¹³¾³¢B
+ </para>
+
+ <sect2 id="preface-s1-1" revision="-1">
+
+ <title>ú{êÅ|óÒÉ¢Ä</title>
+
+ <para id="preface-s1-1-p4" revision="-1">
+ ú{êÅo[W3.2 cr3Ì|óÍ®ïÐGNT
+ i<ulink url="http://www.exa-corp.co.jp">z[y[Wͱ¿ç</ulink>j
+ ÌȺÌo[Ås¢Üµ½B
+ {ÔÍi6,18,19,21,22,23ÍSjA
+ LËTîiO«,2,5,13,14,24,25ÍSjA
+ s³li7,8,9,10ÍSjA
+ ß{Gji12,16,17ÍSjA
+ äÖm¶i1,3,11ÍSjA
+ Ñc_ii4,15ÍSjA
+ ½Ôêi20ÍSjA
+ X´ñir
[SjB
+ Ȩëó̲wEÍ{ÔALËÜŨ袢½µÜ·B
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/mailaddr.gif" format="GIF" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/mailaddr.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ </para>
+ </sect2>
+ </sect1>
+
+ </preface>
+
+ &tutorial;
+
+ &architecture;
+
+ &configuration;
+
+ &persistent-classes;
+
+ &basic-mapping;
+ &collection-mapping;
+ &association-mapping;
+ &component-mapping;
+ &inheritance-mapping;
+
+ &session-api;
+ &transactions;
+ &events;
+ &batch;
+
+ &query-hql;
+ &query-criteria;
+ &query-sql;
+ &filters;
+ &xml;
+
+ &performance;
+
+ &toolset-guide;
+
+ &example-parentchild;
+ &example-weblog;
+ &example-mappings;
+
+ &best-practices;
+
+</book>
+
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/architecture.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/architecture.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/architecture.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,375 @@
+<?xml version="1.0" encoding="Shift_JIS"?>
+<chapter id="architecture">
+
+ <title>A[LeN`</title>
+
+ <sect1 id="architecture-overview" revision="1">
+ <title>TÏ</title>
+
+ <para>
+ HibernateA[LeN`ÌiñíÉj¢x©çÌr
[F
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/overview.svg" format="SVG" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/overview.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ ±Ì}ÍHibernateªAAvP[VÉεÄi±»T[rX
+ iÆi±IuWFNgjðñ·é½ßÉAf[^x[XÆÝèf[^ðg¤±Æð
+ ¦µÄ¢Ü·B
+ </para>
+
+ <para>
+ ±±ÅÀsA[LeN`ÌæèÚ×Èr
[𨩹µÜµå¤B
+ ¢ÉAHibernateÍ_îÅ èA¢ë¢ëÈAv[`ðT|[gµÄ¢Ü·B
+ ±±ÅÍA2ÂÌÉ[Èá𨩹µÜ·B
+ uy¢vA[LeN`ÅÍAAvP[Vª©OÌJDBCRlNVðpÓµA
+ AvP[V©gªgUNVðǵܷB
+ ±Ìû@ÍAHibernate APIÌŬÀÌTuZbgðg¢Ü·F
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/lite.svg" format="SVG" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/lite.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ ud¢vA[LeN`ÍAAvP[V©çA»ÌºÉÊu·éJDBCâJTAÌAPIð
+ æè¥ÁÄÛ»µA»ÌÚ×ÌÊ|ðHibernateÉ©³¹Ü·B
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/full_cream.svg" format="SVG" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/full_cream.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ ȺÍAãÌ}ÉÜÜêéIuWFNgÌè`Å·F
+
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term>SessionFactory (<literal>org.hibernate.SessionFactory</literal>)</term>
+ <listitem>
+ <para>
+ 1ÂÌf[^x[XÉηéRpC³ê½}bsOÌ
+ XbhZ[tÈiXVs\ÌjLbV
B
+ <literal>Session</literal> Ìt@NgÅ èA
+ <literal>ConnectionProvider</literal> ÌNCAgB
+ IvVƵÄAvZXܽÍNX^xɨ¢ÄA
+ gUNVÔÅÄpÂ\Èf[^ÌiñjLbV
ð¿Ü·B
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Session (<literal>org.hibernate.Session</literal>)</term>
+ <listitem>
+ <para>
+ AvP[VÆi±XgAÆÌÎbð\·A
+ VOXbhÅZ½ÌIuWFNgB
+ JDBCRlNVðbvµÜ·B
+ <literal>Transaction</literal> Ìt@NgÅ·B
+ i±IuWFNgÌK{ÌiêjLbV
ð۵ܷB
+ ±ÌLbV
ÍIuWFNgOtðirQ[V·éâA
+ ¯ÊqÅIuWFNgðõ·éÉgíêÜ·B
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Persistent objects Æ Collections</term>
+ <listitem>
+ <para>
+ i±»óÔÆrWlX\bhðÂAZ½ÅVOXbhÌIuWFNgB
+ ±êÍÊíÌJavaBeans/POJO̱Æà èÜ·ªAÁ¥IȱÆÍA
+ »Ì_ÅÌi½¾1ÂÌj <literal>Session</literal> ÆÖAµÄ¢é±ÆÅ·B
+ <literal>Session</literal> ªN[Y³êéÆ·®ÉA
+ »êçÍØ裳êļÌAvP[Vw©ç©RÉg¤±ÆªÅ«Ü·B
+ iá¦Îf[^EgXt@EIuWFNgƵÄA
+ v[e[Vw©çAܽÍv[e[VwÖ¼ÚgpÅ«Ü·Bj
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Transient Æ detached È objects Æ Collections</term>
+ <listitem>
+ <para>
+ »_ÅÍ <literal>Session</literal> ÆÖAµÄ¢È¢A
+ i±NXÌCX^XB
+ ·ÅÉAvP[V¤ÅCX^X»³êÄ¢ÄAܾi±»³êĢȢ©A
+ N[Y³ê½ <literal>Session</literal> ÅCX^X»³ê½©ÌÇ¿ç©Å·B
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Transaction (<literal>org.hibernate.Transaction</literal>)</term>
+ <listitem>
+ <para>
+ (IvV)´q«ðÂìÆPÊ(Unit of Work)ðwè·é½ßÉAAvP[Vªgp·éA
+ VOXbhÅZ½ÈIuWFNgB
+ ºÉÊu·éJDBCAJTAACORBAgUNV©çAvP[VðÛ»µÜ·B
+ <literal>Session</literal> ÍAÉÍ
+ ¢Â©Ì <literal>Transaction</literal> ðܽªé©àµêܹñB
+ µ©µAºÌwÌAPIðg¤É¹æA <literal>Transaction</literal> ðg¤É¹æA
+ gUNV«EðÝè·é±ÆÍAµÄIvVÅÍ èܹñIB
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>ConnectionProvider (<literal>org.hibernate.connection.ConnectionProvider</literal>)</term>
+ <listitem>
+ <para>
+ (IvV)JDBCRlNViÆ»Ìv[jÌt@NgB
+ ºÌwÉÊu·é <literal>Datasource</literal> â
+ <literal>DriverManager</literal> ©çAvP[VðÛ»µÜ·B
+ AvP[VÉÍöJ³êܹñªAJÒªp³Ü½ÍÀ·é±ÆÍÂ\Å·B
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>TransactionFactory (<literal>org.hibernate.TransactionFactory</literal>)</term>
+ <listitem>
+ <para>
+ (IvV) <literal>Transaction</literal> CX^XÌt@NgB
+ AvP[VÉÍöJ³êܹñªAJÒªp³Ü½ÍÀ·é±ÆÍÂ\Å·B
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><emphasis>Extension Interfaces</emphasis></term>
+ <listitem>
+ <para>
+ HibernateÍAi±wÌUé¢ðJX^}CY·é½ßÉA
+ ½ÌIvVg£C^tF[XðpӵĢܷB
+ Ú×ÍAPIhL
gðQƵľ³¢B
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ <para>
+ uy¢vA[LeN`ÅÍAAvP[VͼÚJTAâJDBCÆÎb·é½ßÉA
+ <literal>Transaction</literal> â <literal>TransactionFactory</literal> â
+ <literal>ConnectionProvider</literal> ðoCpXµÜ·B
+ </para>
+ </sect1>
+
+ <sect1 id="architecture-states" revision="1">
+ <title>CX^XÌóÔ</title>
+ <para>
+ i±NXÌCX^XÍAÌRÂÌÙÈéóÔÌÇê©ÉÈèÜ·B
+ »êÍA <emphasis>i±ReLXg</emphasis> ÉæÁÄÜèÜ·B
+ HibernateÌ <literal>Session</literal> IuWFNgªAi±ReLXgÉÈèÜ·B
+ </para>
+
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term>transient</term>
+ <listitem>
+ <para>
+ ±ÌóÔÌCX^XÍA»Ý໵Äßɨ¢ÄàA
+ i±ReLXgÉÖAâĢܹñBܽAi±IDiåL[Ìljð
+ ÁĢܹñB
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>persistent</term>
+ <listitem>
+ <para>
+ ±ÌóÔÌCX^XÍA»Ì_Åi±ReLXgÉÖAâĢܷB
+ ܽAi±IDiåL[Ìljð¿A
+ ½¢Ä¢Íf[^x[XÉηésðÁÄ¢éŵå¤B
+ ÂXÌi±ReLXgÌÈ©ÅÍAi±IDª
+ JavaÌIDiIuWFNgÌãÌÊujƯ¶Å é±Æð
+ Hibernateª <emphasis>ÛØ</emphasis> µÜ·B
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>detached</term>
+ <listitem>
+ <para>
+ ±ÌóÔÌCX^XÍA©ÂÄi±ReLXgÉÖAïç꽪A
+ »ÌReLXgªN[Y³ê½©A é¢ÍA
+ ¼ÌvZXÉ»ÌCX^XªVACY³ê½©Å·B
+ ±ÌCX^XÍAi±IDð¿A½¢Ä¢Íf[^x[XÉ
+ ηésðÁÄ¢éŵå¤Bª£CX^XÉεÄÍA
+ i±IDÆJavaÌIDÆÌÖAÍAHibernateªÛصܹñB
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </sect1>
+
+ <sect1 id="architecture-jmx" revision="1">
+ <title>JMXÆÌ</title>
+
+ <para>
+ JMXÍJavaR|[lgÇÌJ2EEWÅ·B
+ JMXWT[rXðʵÄAHibernateÍdzêÜ·B
+ fBXgr
[VÌÉ <literal>org.hibernate.jmx.HibernateService</literal> Æ¢¤
+ MBeanÀðpӵĢܷB
+ </para>
+
+ <para>
+ JBoss AvP[VT[o[ãÉHibernateðJMXT[rXƵÄfvC·éû@ÌáƵÄÍA
+ JBoss [UKChðQƵľ³¢B JBoss AvP[VT[o[ɨ¢ÄA
+ JMXðgÁÄfvC·éÆAÌbgª¾çêÜ·B
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>ZbVÇ:</emphasis> HibernateÌ <literal>Session</literal> ÌCtTCNÍA
+ ©®IÉJTAgUNVÌXR[vÉѯçêÜ·B±êÍAàÍâè®Å
+ <literal>Session</literal> ðI[vµ½èAN[Yµ½è·éKvªÈ¢±ÆðÓ¡µÜ·B
+ ±êÍAJBoss EJB C^[Zv^ÌdÉÈèÜ·B
+ ܽAR[hÌDZÅgUNV«EðÝè·é©É¢ÄA
+ àÍâYÞKvª èܹñià¿ëñÚAÂ\Èi±wð©©ÈÄ¢¢ÌÈçÎA
+ IvVÌHibernateÌ <literal>Transaction</literal> ðgpµÄ¾³¢Bj
+ <literal>Session</literal> ÉANZX·é½ßÉÍA <literal>HibernateContext</literal> ð
+ R[µÄ¾³¢B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>HAR fvC:</emphasis> ÊíA(EAR Ü½Í SAR t@CÉ éjJBoss T[rX
+ fvCgfBXNv^ðgÁÄAHibernate JMX T[rXðfvCµÜ·B
+ »êÍAHibernateÌ <literal>SessionFactory</literal> ÌSÄÌêÊIÈÝèIvVð
+ T|[gµÜ·Bµ©µËRƵÄfvCgfBXNv^ÌÈ©É·×ÄÌ}bsOt@CÌ
+ ¼Oð°éKvª èÜ·B
+ àµAIvVÌHARfvCgðg¤±Æðß½ÈçA
+ JBossÍ©®IÉHARt@CÌÈ©ÌSÄÌ}bsOt@CðoµÜ·B
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ±êçÌIvVÉ¢ÄÌÚ×ÈîñÍAJBossAvP[VT[o[UKChð
+ Qlɵľ³¢B
+ </para>
+
+ <para>
+ JMXT[rXƵÄpÂ\ȼÌ@\ÉAHibernateÀsvîñª èÜ·B
+ <xref linkend="configuration-optional-statistics"/> ð©Ä¾³¢B
+ </para>
+ </sect1>
+
+ <sect1 id="architecture-jca" revision="1">
+ <title>JCA T|[g</title>
+ <para>
+ Hibernate Í JCA RlN^ƵÄàÝèÅ«Ü·BÚ×É¢ÄÍAWebTCgð©Ä¾³¢B
+ Hibernate JCA T|[gÍA¡ÌƱëÀ±iKƵÄl¦çêÄ¢é±ÆÉӵľ³¢B
+ </para>
+ </sect1>
+
+ <sect1 id="architecture-current-session" revision="2">
+ <title>ReLXgãÌZbV</title>
+ <para>
+ Hibernate ðgÁ½AvP[VÍAÙÆñÇAÈñç©Ì`Å"ReLXgãÌ"ZbVªKvÉÈèÜ·B
+ uReLXgãÌZbVvÍAÁèÌReLXgÌXR[vÌÈ©ÅLøÈZbV̱ÆÅ·B
+ µ©µAÊíAvP[V²ÆÉReLXgð\¬·éàÌÌè`ÍÙÈèÜ·B
+ µ©àAÙÈé¡ÌReLXgÍA»_ÉεÄÙÈéXR[vðè`µÜ·B
+ o[W3.0æèOÌ Hibernate ÅÍA©ìÌ <literal>ThreadLocal</literal> x[XÌuReLXgãÌZbVvð
+ p·é©A <literal>HibernateUtil</literal> Ìæ¤Èwp[NXðp·é©A
+ proxy/interception x[XÌuReLXgãÌZbVvðñ·é
+ iSpring â Pico Ìæ¤ÈjT[hp[eBÌt[[Nðp·é©Ì¢¸ê©Åµ½B
+ </para>
+
+ <para>
+ o[W 3.0.1 ©çAHibernate ÉÍ <literal>SessionFactory.getCurrentSession()</literal> ª
+ Áíèܵ½B ±êÍA <literal>JTA</literal> gUNVÌgpðOñɵĢܷB
+ <literal>JTA</literal> gUNVÍA»ÝÌZbVÌXR[vÆReLXg̼ûðè`µÜ·B
+ Hibernate `[ÍA̱Æð壵ܷB
+ åÈX^hAÌ <literal>JTA TransactionManager</literal> Àª¬nµ½çA
+ <literal>J2EE</literal> ReiãÉfvC³êé©Ç¤©É©©íç¸A
+ ÙÆñÇÌi·×ÄÆ;íÈ¢ªjAvP[VªA
+ <literal>JTA</literal> gUNVÇðgp·×«Å éÆB
+ ±Ìl¦ÉîÃÆA <literal>JTA</literal> x[XÌuReLXgãÌZbVvð
+ g¤µ©È¢Åµå¤B
+ </para>
+
+ <para>
+ µ©µAo[W 3.1 ©çÍA <literal>SessionFactory.getCurrentSession()</literal> Ìã̪A
+ vKuÉÈèܵ½B
+ ±êðó¯ÄA»ÝÌZbVðè`·éXR[vÆReLXgÌvKreBðÂ\É·é½ßÉA
+ Vµ¢g£C^tF[X ( <literal>org.hibernate.context.CurrentSessionContext</literal> ) Æ
+ Vµ¢\¬p[^ ( <literal>hibernate.current_session_context_class</literal> ) ªÇÁ³êܵ½B
+ </para>
+
+ <para>
+ <literal>org.hibernate.context.CurrentSessionContext</literal> C^tF[XÌKñÉ¢ÄÌ
+ Ú×ÈàeÍ Javadoc ðQƵľ³¢B
+ »êÉÍA <literal>currentSession()</literal> Æ¢¤1ÂÌ\bhªè`³êĨèA
+ »ÌÀÍA»ÝÌuReLXgãÌZbVvðÇÕ·é±ÆÉÓCð¿Ü·B
+ »ÌÜÜg¦éæ¤ÉAHibernateͱÌC^tF[XÌÀð2ÂñµÄ¢Ü·B
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>org.hibernate.context.JTASessionContext</literal> -
+ <literal>JTA</literal> gUNVÉæÁÄA»ÝÌZbVªÇÕ³êA
+ XR[vðßçêÜ·B±ÌÍAâJTA¾¯ÌAv[`ÆÜÁ½¯¶Å·B
+ Ú×ÍJavadocðQƵľ³¢B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>org.hibernate.context.ThreadLocalSessionContext</literal> -
+ XbhÌÀsÉæÁÄA»ÝÌZbVªÇÕ³êÜ·B
+ Ú×ÍJavadocðQƵľ³¢B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>org.hibernate.context.ManagedSessionContext</literal> -
+ XbhÌÀsÉæÁÄA»ÝÌZbVªÇÕ³êÜ·B
+ µ©µA±ÌNXÌstatic\bhÅ <literal>Session</literal> CX^Xð
+ oCh/AoCh·éÓCÍ È½É èÜ·B
+ ±ê굀 <literal>Session</literal> ðI[vAtbV
AN[YµÜ¹ñB
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ nßÌ2ÂÌÀÍA"1ZbV - 1f[^x[XgUNV" vO~OfðñµÜ·B
+ ±êÍ <emphasis>NGXg²ÆÌZbVisession-per-requestj</emphasis> ƵÄàmçêĨèAgíêĢܷB
+ Hibernate ZbVÌJnÆI¹ÍAf[^x[XgUNVÌúÔÅÜèÜ·B
+ JTAðgíÈ¢ÊÌJSEÅAvOãÌgUNV«EÝèðs¤ÈçA
+ R[h©çîbÌgUNVVXeðBÁ·é½ßÉA
+ Hibernate <literal>Transaction</literal> APIðg¤Ææ¢Åµå¤B
+ JTAðg¤ÈçAgUNVÌ«EÝèÉÍAJTAC^[tFCXðgÁľ³¢B
+ CMTðT|[g·éEJBReiÅÀs·éÂàèÈçAgUNV«EÍé¾IÉè`Å«é½ßA
+ R[hãÅgUNVâZbVÌ«EðÝè·éKvÍ èܹñB
+ ³çÉÚ×ÈîñâR[hÌáÍA <xref linkend="transactions"/> ðQƵľ³¢B
+ </para>
+
+ <para>
+ <literal>hibernate.current_session_context_class</literal> Ýèp[^ÍA
+ <literal>org.hibernate.context.CurrentSessionContext</literal> ÌÇÌÀðg¤©ðwèµÜ·B
+ ºÊÝ·«Ì½ßA±Ìp[^ªÝè³ê¸
+ <literal>org.hibernate.transaction.TransactionManagerLookup</literal> ªÝè³êÄ¢½êA
+ HibernateÍ <literal>org.hibernate.context.JTASessionContext</literal> ðg¤±ÆÉӵľ³¢B
+ Êí±Ìp[^ÌlÉÍA3ÂÌÀÌ©çgp·éÀNX̼Oð¼ÚwèµÜ·B
+ µ©µA"jta", "thread", "managed"Æ¢¤»ê¼êÌȪ¼àpÓ³êĢܷB
+ </para>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/association_mapping.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/association_mapping.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/association_mapping.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,620 @@
+<?xml version="1.0" encoding="Shift_JIS"?>
+<chapter id="associations">
+
+ <title>ÖA}bsO</title>
+
+ <sect1 id="assoc-intro" revision="1">
+ <title>Cg_NV</title>
+
+ <para>
+ ÖA}bsO͵εÎðªÅàïµ¢àÌÉÈèÜ·B
+ ±ÌÍÅÍAî{IÈêÂêÂÌP[XÉ¢Äq×Ü·B
+ PûüÌ}bsO©çnßA»ê©çoûüÌP[XÉ¢Äl¦Ä¢«Ü·B
+ áƵÄA<literal>Person</literal> Æ <literal>Address</literal> ðp¢Ü·B
+ </para>
+
+ <para>
+ ÖAÍAe[uðüê驩Ǥ©ÆA
+ ½dxÉæÁĪ޷é±ÆɵܷB
+ </para>
+
+ <para>
+ ·×ÄÌáÅnot nullÌOL[ðgpµÜ·B
+ ±êÍHibernateÌvÅÍ èܹñB
+ not null§ñðOµ½ÆµÄàA}bsOÍâèÈ®ìµÜ·B
+ </para>
+
+ </sect1>
+
+ <sect1 id="assoc-unidirectional" revision="1">
+ <title>PûüÖA</title>
+
+ <sect2 id="assoc-unidirectional-m21">
+ <title>½Îê</title>
+
+ <para>
+ <emphasis>Pûü½ÎêÖA</emphasis> ÍPûüÖAÌÅÅàêÊIÈàÌÅ·B
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key, addressId bigint not null )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-121">
+ <title>êÎê</title>
+
+ <para>
+ <emphasis>OL[ÌPûüêÎêÖA</emphasis> ÍÙÆñǯ¶àÌÅ·B
+ Bêá¤ÌÍAJÌj[NȧñÅ·B
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <many-to-one name="address"
+ column="addressId"
+ unique="true"
+ not-null="true"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key, addressId bigint not null unique )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ <para>
+ <emphasis>åL[ÌPûüêÎêÖA</emphasis> ÍÊíAÁÊÈIDWFl[^ðg¢Ü·B
+ i±ÌáÅÍÖAÌûüªtÉÈÁÄ¢é±ÆÉӵľ³¢j
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+</class>
+
+<class name="Address">
+ <id name="id" column="personId">
+ <generator class="foreign">
+ <param name="property">person</param>
+ </generator>
+ </id>
+ <one-to-one name="person" constrained="true"/>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table Address ( personId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-12m">
+ <title>êν</title>
+
+ <para>
+ <emphasis>OL[ÌPûüêνÖA</emphasis> ÍÆÄàÁêÈP[XÅA
+ Ü觳êĢܹñB
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses">
+ <key column="personId"
+ not-null="true"/>
+ <one-to-many class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table Address ( addressId bigint not null primary key, personId bigint not null )
+ ]]></programlisting>
+
+ <para>
+ ±Ìæ¤ÈÖA̽ßÉe[uðg¤±Æð¨EߵܷB
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="assoc-unidirectional-join" revision="1">
+ <title>e[uðgÁ½PûüÖA</title>
+
+ <sect2 id="assoc-unidirectional-join-12m">
+ <title>êν</title>
+
+ <para>
+ <emphasis>e[uðgÁ½PûüêνÖA</emphasis>
+ ÍæèDܵ¢Å·B
+ <literal>unique="true"</literal> ÌwèÉæèA½dxª½Î½©çêν
+ ÉÏíÁ½±ÆÉӵĺ³¢B
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses" table="PersonAddress">
+ <key column="personId"/>
+ <many-to-many column="addressId"
+ unique="true"
+ class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId not null, addressId bigint not null primary key )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-join-m21">
+ <title>½Îê</title>
+
+ <para>
+ <emphasis>e[uÌPûü½ÎêÖA</emphasis> Í
+ ÖAªCÓÅ éÆ«ÉñíÉêÊIÈàÌÅ·B
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ optional="true">
+ <key column="personId" unique="true"/>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"/>
+ </join>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null primary key, addressId bigint not null )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-join-121">
+ <title>êÎê</title>
+
+ <para>
+ <emphasis>e[uÌPûüêÎêÖA</emphasis> ÍA{ÉÁêÅ·ª
+ sÂ\ÅÍ èܹñB
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ optional="true">
+ <key column="personId"
+ unique="true"/>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"
+ unique="true"/>
+ </join>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-join-m2m">
+ <title>½Î½</title>
+
+ <para>
+ ÅãÉA<emphasis>Pûü½Î½ÖA</emphasis> ð¦µÜ·B
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses" table="PersonAddress">
+ <key column="personId"/>
+ <many-to-many column="addressId"
+ class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personId, addressId) )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="assoc-bidirectional" revision="1">
+ <title>oûüÖA</title>
+
+ <sect2 id="assoc-bidirectional-m21" revision="2">
+ <title>êν/½Îê</title>
+
+ <para>
+ <emphasis>oûü½ÎêÖA</emphasis> ÍÅàêÊIÈÖAÅ·B
+ iWIÈeqÖWÅ·j
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <set name="people" inverse="true">
+ <key column="addressId"/>
+ <one-to-many class="Person"/>
+ </set>
+</class>]]></programlisting>
+
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key, addressId bigint not null )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ <para>
+ <literal>List</literal> iܽͼÌCfbNXt«ÌRNVjðg¤ÈçA
+ OL[Ì <literal>key</literal> Jð <literal>not null</literal> ÉÝèµA
+ RNV¤ªevfÌCfbNXðeiX·éæ¤ÉA
+ ÖAðµ¤Kvª èÜ·
+ i<literal>update="false"</literal> ©Â <literal>insert="false"</literal>
+ ÆÝèµÄA½Î¤ð¼zIÉinverseɵܷjF
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id"/>
+ ...
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"
+ insert="false"
+ update="false"/>
+</class>
+
+<class name="Address">
+ <id name="id"/>
+ ...
+ <list name="people">
+ <key column="addressId" not-null="true"/>
+ <list-index column="peopleIdx"/>
+ <one-to-many class="Person"/>
+ </list>
+</class>]]></programlisting>
+
+ <para>
+ àµOL[Jª <literal>NOT NULL</literal> Å éÈçÎA
+ RNV}bsOÌ <literal><key></literal> vfð
+ <literal>not-null="true"</literal> É·é±ÆÍdvÅ·B
+ üêqÉÈÁ½ <literal><column></literal> vf¾¯ÅÍÈA
+ <literal><key></literal> vfà <literal>not-null="true"</literal>
+ Æè`µÈ¢æ¤ÉµÄ¾³¢B
+ </para>
+
+ </sect2>
+
+ <sect2 id="assoc-bidirectional-121">
+ <title>êÎê</title>
+
+ <para>
+ <emphasis>OL[ÌoûüêÎêÖA</emphasis> ÍñíÉêÊIÅ·B
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <many-to-one name="address"
+ column="addressId"
+ unique="true"
+ not-null="true"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <one-to-one name="person"
+ property-ref="address"/>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key, addressId bigint not null unique )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ <para>
+ <emphasis>åL[ÌoûüêÎêÖA</emphasis> ÍÁêÈIDWFl[^ðg¢Ü·B
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <one-to-one name="address"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="personId">
+ <generator class="foreign">
+ <param name="property">person</param>
+ </generator>
+ </id>
+ <one-to-one name="person"
+ constrained="true"/>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table Address ( personId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="assoc-bidirectional-join" revision="1">
+ <title>e[uðgÁ½oûüÖA</title>
+
+ <sect2 id="assoc-bidirectional-join-12m">
+ <title>êν/½Îê</title>
+
+ <para>
+ <emphasis>e[uÌoûüêνÖA</emphasis> Å·B
+ <literal>inverse="true"</literal> ªÖA[ARNVAÌ¢¸ê©É
+ ÝèÅ«é±ÆÉӵľ³¢B
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses"
+ table="PersonAddress">
+ <key column="personId"/>
+ <many-to-many column="addressId"
+ unique="true"
+ class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ inverse="true"
+ optional="true">
+ <key column="addressId"/>
+ <many-to-one name="person"
+ column="personId"
+ not-null="true"/>
+ </join>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null, addressId bigint not null primary key )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-bidirectional-join-121">
+ <title>êÎê</title>
+
+ <para>
+ <emphasis>e[uÌoûüêÎêÖA</emphasis>
+ ÍñíÉÁêÅ·ªAÂ\Å·B
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ optional="true">
+ <key column="personId"
+ unique="true"/>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"
+ unique="true"/>
+ </join>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ optional="true"
+ inverse="true">
+ <key column="addressId"
+ unique="true"/>
+ <many-to-one name="address"
+ column="personId"
+ not-null="true"
+ unique="true"/>
+ </join>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-bidirectional-join-m2m" revision="1">
+ <title>½Î½</title>
+
+ <para>
+ ÅãÉA<emphasis>oûü½Î½ÖA</emphasis> ð¦µÜ·B
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses" table="PersonAddress">
+ <key column="personId"/>
+ <many-to-many column="addressId"
+ class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <set name="people" inverse="true" table="PersonAddress">
+ <key column="addressId"/>
+ <many-to-many column="personId"
+ class="Person"/>
+ </set>
+</class>]]></programlisting>
+
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personId, addressId) )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="assoc-complex">
+ <title>æè¡GÈÖA}bsO</title>
+
+ <para>
+ æè¡GÈÖAÍ <emphasis>ÉßÄ</emphasis> HÅ·B
+ }bsOhL
gÉSQL¶ðßÞ±ÆÅA
+ ³çÉ¡GÈóµðµ¤±ÆªÅ«Ü·B
+ á¦ÎA<literal>accountNumber</literal>, <literal>effectiveEndDate</literal>,
+ <literal>effectiveStartDate</literal> JðÂaccountiûÀjîñÌððµ¤e[uÍA
+ ȺÌæ¤É}bsOµÜ·B
+ </para>
+
+ <programlisting><![CDATA[<properties name="currentAccountKey">
+ <property name="accountNumber" type="string" not-null="true"/>
+ <property name="currentAccount" type="boolean">
+ <formula>case when effectiveEndDate is null then 1 else 0 end</formula>
+ </property>
+</properties>
+<property name="effectiveEndDate" type="date"/>
+<property name="effectiveStateDate" type="date" not-null="true"/>]]></programlisting>
+
+ <para>
+ »µÄAÖAð <emphasis>»_Ì</emphasis> CX^X
+ i<literal>effectiveEndDate</literal> ªnullÅ éàÌjÉ}bsOµÜ·B
+ ȺÌæ¤ÉÈèÜ·F
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="currentAccountInfo"
+ property-ref="currentAccountKey"
+ class="AccountInfo">
+ <column name="accountNumber"/>
+ <formula>'1'</formula>
+</many-to-one>]]></programlisting>
+
+ <para>
+ ³çÉ¡GÈáÅÍA<literal>Employeei]Æõj</literal> Æ <literal>OrganizationigDj</literal>
+ ÔÌÖAª <literal>EmploymentiÙpj</literal> e[uÅÛ³êéêðzµÄ¾³¢B
+ ±Ìe[uÉÍÙpf[^Ìðª·×ÄÜÜêÜ·B
+ ·éÆ]ÆõÌ <emphasis>ÅàÅßÌ</emphasis> ÙpÒð\·ÖA
+ iÅàÅßÌ <literal>startDate</literal> ðÂàÌjÍA±Ìæ¤É}bsOÅ«Ü·F
+ </para>
+
+ <programlisting><![CDATA[<join>
+ <key column="employeeId"/>
+ <subselect>
+ select employeeId, orgId
+ from Employments
+ group by orgId
+ having startDate = max(startDate)
+ </subselect>
+ <many-to-one name="mostRecentEmployer"
+ class="Organization"
+ column="orgId"/>
+</join>]]></programlisting>
+
+ <para>
+ ±Ì@\ÍñíÉÍÅ·B
+ µ©µ±Ìæ¤ÈêAÊÍHQLâcriteriaNGðg¤ûªæèÀHIÅ·B
+ </para>
+
+ </sect1>
+
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/basic_mapping.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/basic_mapping.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/basic_mapping.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,3485 @@
+<?xml version="1.0" encoding="Shift_JIS"?>
+
+<chapter id="mapping">
+ <title>î{IÈO/R}bsO</title>
+
+ <sect1 id="mapping-declaration" revision="1">
+ <title>}bsOè`</title>
+
+ <para>
+ IuWFNg/[Vi}bsOÍÊíXMLhL
gÅè`µÜ·B
+ }bsOhL
gÍAÇÝâ·èìÆÅÒWµâ·¢æ¤ÉfUC³êĢܷB
+ }bsO¾êÍJavaSAÂÜèe[uè`ÅÍÈi±NXÌè`ÉîâÄ\z³êĢܷB
+ </para>
+
+ <para>
+ ½ÌHibernate[UÍXML}bsOÌLqðèìÆÅs¢Ü·ªA
+ XDoclet, Middlegen, AndroMDAÆ¢¤æ¤È}bsOhL
g𶬷éc[ª
+ ¢Â©¶ÝµÜ·B
+ </para>
+
+ <para>
+ TvÌ}bsO©çnßܵå¤F
+ </para>
+
+ <programlisting id="mapping-declaration-ex1" revision="1"><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="eg">
+
+ <class name="Cat"
+ table="cats"
+ discriminator-value="C">
+
+ <id name="id">
+ <generator class="native"/>
+ </id>
+
+ <discriminator column="subclass"
+ type="character"/>
+
+ <property name="weight"/>
+
+ <property name="birthdate"
+ type="date"
+ not-null="true"
+ update="false"/>
+
+ <property name="color"
+ type="eg.types.ColorUserType"
+ not-null="true"
+ update="false"/>
+
+ <property name="sex"
+ not-null="true"
+ update="false"/>
+
+ <property name="litterId"
+ column="litterId"
+ update="false"/>
+
+ <many-to-one name="mother"
+ column="mother_id"
+ update="false"/>
+
+ <set name="kittens"
+ inverse="true"
+ order-by="litter_id">
+ <key column="mother_id"/>
+ <one-to-many class="Cat"/>
+ </set>
+
+ <subclass name="DomesticCat"
+ discriminator-value="D">
+
+ <property name="name"
+ type="string"/>
+
+ </subclass>
+
+ </class>
+
+ <class name="Dog">
+ <!-- ±±ÉDogpÌ}bsO«Ü· -->
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ }bsOhL
gÌàeðྵܷBB
+ ½¾µA±±ÅÍHibernateªÀsÉg¤hL
gvfÆ®«É¢ÄÌÝྵܷB
+ }bsOhL
gÍA¢Â©ÌIvV®«ÆvfðÜñŢܷiá¦Î <literal>not-null</literal> ®«jB
+ »êçÍXL[}GNX|[gc[ªoÍ·éf[^x[XXL[}Ée¿ð^¦éàÌÅ·B
+
+ </para>
+
+
+
+ <sect2 id="mapping-declaration-doctype" revision="3">
+ <title>Doctype</title>
+
+ <para>
+ XML}bsOÅÍA¨©¹µ½æ¤ÈhL
g^ðK¸è`·×«Å·B
+ ÀÛÌDTDÍAãLÌURLÌ <literal>hibernate-x.x.x/src/org/hibernate</literal> fBNgA ܽÍ
+ <literal>hibernate.jar</literal> àÉ èÜ·B
+ HibernateÍíÉA»ÌNXpXàÅDTDðTµnßÜ·B
+ C^[lbgÉ éDTDt@CðT»¤Æµ½ÈçA
+ NXpXÌàeð©ÄADTDé¾ðmFµÄ¾³¢B
+ </para>
+
+
+
+ <sect3 id="mapping-declaration-entity-resolution">
+ <title>
+ GeBeBE]o
+ </title>
+ <para>
+ Oqµ½æ¤ÉAHibernateÍܸNXpXàÅDTDððµæ¤ÆµÜ·B
+ <literal>org.xml.sax.EntityResolver</literal> ÌJX^Àð
+ XMLt@CðÇÝÞ½ßÌSAXReaderÉo^·é±ÆÉæÁÄADTDððµÜ·B
+ ±ÌJX^Ì <literal>EntityResolver</literal> Í2ÂÌÙÈéVXeID¼OóÔðF¯µÜ·B
+
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>Hibernate¼OóÔ</literal> ÍA]oª
+ <literal>http://hibernate.sourceforge.net/</literal> ÅnÜéVXeIDÉBµ½Æ«ÉA
+ F¯³êÜ·B
+ »µÄ]oÍAHibernateÌNXð[hµ½NX[_ðp¢ÄA
+ ±êçÌGeBeBððµæ¤ÆµÜ·B
+
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>[U¼OóÔ</literal> ÍA]oª
+ URLvgRÌ <literal>classpath://</literal> ðgÁ½VXeIDÉBµ½Æ«ÉA
+ F¯³êÜ·B»µÄ]oÍA(1)JgXbhÌReLXgNX[_[A
+ ܽÍ(2)HibernateÌNXð[hµ½NX[_ðgÁÄA
+ ±êçÌGeBeBððµæ¤ÆµÜ·B
+
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+ ºLÍA[U¼OóÔðgÁ½áÅ·F
+
+ </para>
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" [
+ <!ENTITY types SYSTEM "classpath://your/domain/types.xml">
+]>
+
+<hibernate-mapping package="your.domain">
+ <class name="MyEntity">
+ <id name="id" type="my-custom-id-type">
+ ...
+ </id>
+ <class>
+ &types;
+</hibernate-mapping>]]></programlisting>
+ <para>
+ ±±Å <literal>types.xml</literal> Í <literal>your.domain</literal> pbP[WàÌ\[XÅ èA
+ JX^ <xref linkend="mapping-types-custom">typedef</xref> ðÜÞÜ·B
+ </para>
+ </sect3>
+
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-mapping" revision="3">
+ <title>hibernate-mapping</title>
+
+ <para>
+ ±ÌvfÉ͢©IvV®«ª èÜ·B<literal>schema</literal> ®«Æ <literal>catalog</literal> ®«ÍA
+ ±Ì}bsOªQÆ·ée[uªA±Ì®«ÉæÁÄwè³ê½XL[}Æ(ܽÍ)J^OÉ®·é±ÆðwèµÜ·B
+ ±Ì®«ªwè³êéÆAe[u¼Í^¦çê½XL[}¼ÆJ^O¼ÅCü³êÜ·B±êçÌ®«ªwè³êĢȯêÎA
+ e[u¼ÍCü³êܹñB<literal>default-cascade</literal> ®«ÍA
+ <literal>cascade</literal> ®«ðwèµÄ¢È¢vpeBâRNVÉA
+ ÇÌJXP[hX^CðèÄé©ðwèµÜ·B
+ <literal>auto-import</literal> ®«ÍA
+ NG¾êàÅCü³êĢȢNX¼ðAftHgÅg¦éæ¤ÉµÜ·B
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="hm1" coords="2 55"/>
+ <area id="hm2" coords="3 55"/>
+ <area id="hm3" coords="4 55"/>
+ <area id="hm4" coords="5 55"/>
+ <area id="hm5" coords="6 55"/>
+ <area id="hm6" coords="7 55"/>
+ <area id="hm7" coords="8 55"/>
+ </areaspec>
+ <programlisting><![CDATA[<hibernate-mapping
+ schema="schemaName"
+ catalog="catalogName"
+ default-cascade="cascade_style"
+ default-access="field|property|ClassName"
+ default-lazy="true|false"
+ auto-import="true|false"
+ package="package.name"
+ />]]></programlisting>
+ <calloutlist>
+ <callout arearefs="hm1">
+ <para>
+ <literal>schema</literal>iIvVjFf[^x[XXL[}̼OB
+ </para>
+ </callout>
+ <callout arearefs="hm2">
+ <para>
+ <literal>catalog</literal> iIvVjFf[^x[XJ^O̼OB
+ </para>
+ </callout>
+ <callout arearefs="hm3">
+ <para>
+ <literal>default-cascade</literal> iIvV - ftHgÍ <literal>none</literal>jF
+ ftHgÌJXP[hX^CB
+ </para>
+ </callout>
+ <callout arearefs="hm4">
+ <para>
+ <literal>default-access</literal> (IvV - ftHgÍ <literal>property</literal> jF
+ HibernateªvpeBÉANZX·éÛÉÌé׫íªB <literal>PropertyAccessor</literal>
+ ðÀ·é±ÆÅJX^}CYÂ\B
+
+ </para>
+ </callout>
+ <callout arearefs="hm5">
+ <para>
+ <literal>default-lazy</literal> (IvV - ftHgÍ <literal>true</literal> )F
+ <literal>lazy</literal> ®«ªwè³êĢȢNXâRNV}bsOÉηéftHglB
+ </para>
+ </callout>
+ <callout arearefs="hm6">
+ <para>
+ <literal>auto-import</literal> iIvV - ftHgÍ <literal>true</literal>jF
+ NG¾êàÅAi±Ì}bsOàÌNXÌjCü³êĢȢNX¼ðg¦é©Ç¤©ðwèµÜ·B
+ </para>
+ </callout>
+ <callout arearefs="hm7">
+ <para>
+ <literal>package</literal> (IvV): }bsOhL
gàÅCü³êĢȢNX¼ÉεÄèÄéA
+ pbP[WÌÚª«(prefix)ðwèµÜ·B
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ iCü³êĢȢj¯¶¼OÌi±NXª2 éÈçA
+ <literal>auto-import="false"</literal> ðÝè·×«Å·B
+ 2ÂÌNXÉhC|[g³ê½h¯¶¼OðèÄæ¤Æ·éÆAHibernateÍáOð°Ü·B
+ </para>
+
+ <para>
+ <literal>hibernate-mapping</literal> vfÍAÅÌáŦµ½æ¤É¢Â©Ìi± <literal><class></literal>
+ }bsOðlXgÅ«Ü·B
+ µ©µAPÂÌ}bsOt@CÅͽ¾ÐÆÂÌi±NX(ܽÍÐÆÂÌNXKw)É}bsO·éæ¤ÉµA
+ ³çÉi±X[p[NXÌãÅwè·é׫ŵå¤(¢Â©Ìc[ͱÌæ¤È}bsOt@CðzèµÄ¢Ü·)B
+ á¦ÎÌæ¤ÉÈèÜ·BF <literal>Cat.hbm.xml</literal> ,
+ <literal>Dog.hbm.xml</literal> , ܽÍp³ðg¤Èç <literal>Animal.hbm.xml</literal> B
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-class" revision="3">
+ <title>class</title>
+
+ <para>
+ <literal>class</literal> vfðgÁÄAi±NXðé¾Å«Ü·B
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="class1" coords="2 55"/>
+ <area id="class2" coords="3 55" />
+ <area id="class3" coords="4 55"/>
+ <area id="class4" coords="5 55" />
+ <area id="class5" coords="6 55"/>
+ <area id="class6" coords="7 55" />
+ <area id="class7" coords="8 55"/>
+ <area id="class8" coords="9 55" />
+ <area id="class9" coords="10 55" />
+ <area id="class10" coords="11 55"/>
+ <area id="class11" coords="12 55"/>
+ <area id="class12" coords="13 55"/>
+ <area id="class13" coords="14 55"/>
+ <area id="class14" coords="15 55"/>
+ <area id="class15" coords="16 55"/>
+ <area id="class16" coords="17 55"/>
+ <area id="class17" coords="18 55"/>
+ <area id="class18" coords="19 55"/>
+ <area id="class19" coords="20 55"/>
+ <area id="class20" coords="21 55"/>
+ <area id="class21" coords="22 55"/>
+
+ </areaspec>
+ <programlisting><![CDATA[<class
+ name="ClassName"
+ table="tableName"
+ discriminator-value="discriminator_value"
+ mutable="true|false"
+ schema="owner"
+ catalog="catalog"
+ proxy="ProxyInterface"
+ dynamic-update="true|false"
+ dynamic-insert="true|false"
+ select-before-update="true|false"
+ polymorphism="implicit|explicit"
+ where="arbitrary sql where condition"
+ persister="PersisterClass"
+ batch-size="N"
+ optimistic-lock="none|version|dirty|all"
+ lazy="true|false"
+ entity-name="EntityName"
+ check="arbitrary sql check condition"
+ rowid="rowid"
+ subselect="SQL expression"
+ abstract="true|false"
+ entity-name="EntityName"
+ node="element-name"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="class1">
+ <para>
+ <literal>name</literal> (IvV)Fi±NXiܽÍC^[tFCXjÌ®SCüJavaNX¼B
+ ൱̮«ðwèµÈ¯êÎAPOJOÅÍÈ¢GeBeBÉηé}bsOƵĵíêÜ·B
+
+ </para>
+ </callout>
+ <callout arearefs="class2">
+ <para>
+ <literal>table</literal> (IvV - ftHgÍCü³êĢȢNX¼)Ff[^x[Xe[u̼O
+ </para>
+ </callout>
+ <callout arearefs="class3">
+ <para>
+ <literal>discriminator-value</literal> (IvV - ftHgÍNX¼)F
+ |[tBbNÈUé¢ÉgíêéÂXÌTuNXð¯Ê·é½ßÌlB
+ lÍ <literal>null</literal> © <literal>not null</literal> Ì¢¸ê©ðæèÜ·B
+ </para>
+ </callout>
+ <callout arearefs="class4">
+ <para>
+ <literal>mutable</literal> (IvVA ftHgÍ <literal>true</literal> )F
+ »ÌNXÌCX^XªXVÂ\iܽÍsÂ\jÅ é±ÆðwèµÜ·B
+ </para>
+ </callout>
+ <callout arearefs="class5">
+ <para>
+ <literal>schema</literal> iIvVjF
+ [gÌ <literal><hibernate-mapping></literal> vfÅwè³ê½XL[}¼ðI[o[ChµÜ·B
+ </para>
+ </callout>
+ <callout arearefs="class6">
+ <para>
+ <literal>catalog</literal> iIvVjF[gÌ <literal><hibernate-mapping></literal>
+ vfÅwè³ê½J^O¼ðI[o[ChµÜ·B
+ </para>
+ </callout>
+ <callout arearefs="class7">
+ <para>
+ <literal>proxy</literal> iIvVjFxú»vLVÉg¤C^[tFCXðwèµÜ·B
+ i±»·éNX¼»ÌàÌðwè·é±ÆàÂ\Å·B
+ </para>
+ </callout>
+ <callout arearefs="class8">
+ <para>
+ <literal>dynamic-update</literal> iIvVA ftHgÍ <literal>false</literal> jF
+ lªÏX³ê½J¾¯ðÜÞSQLÌ <literal>UPDATE</literal> ¶ðAÀsɶ¬·é±ÆðwèµÜ·B
+ </para>
+ </callout>
+ <callout arearefs="class9">
+ <para>
+ <literal>dynamic-insert</literal> iIvV, ftHgÍ <literal>false</literal> jF
+ lªnullÅÍÈ¢J¾¯ðÜÞSQLÌ <literal>INSERT</literal> ¶ðAÀsɶ¬·é±ÆðwèµÜ·B
+ </para>
+ </callout>
+ <callout arearefs="class10">
+ <para>
+ <literal>select-before-update</literal> (IvV, ftHgÍ <literal>false</literal>):
+ IuWFNgªÏX³ê½ÌªmÀÅÈ¢ÈçÎAHibernateªSQLÌ <literal>UPDATE</literal> ð
+ <emphasis>µÄÀsµÈ¢</emphasis> ±ÆðwèµÜ·B
+ éÁèÌê(ÀÛIÉÍAêIuWFNgª <literal>update()</literal> ðg¢A
+ Vµ¢ZbVÆÖAt¯çê½¾¯)A<literal>UPDATE</literal> ªÀÛÉKv©Ç¤©ðè·é½ßÉA
+ Hibernateª]ªÈSQLÌ <literal>SELECT</literal> ¶ðÀs·é±ÆðÓ¡µÜ·B
+ </para>
+ </callout>
+ <callout arearefs="class11">
+ <para>
+ (optional, ftHgÅÍ <literal>implicit</literal> ): implicitiÃÙj©expliciti¾¦jÌA
+ Ç¿çÌNG|[tBYðg¤©èµÜ·B
+ </para>
+ </callout>
+ <callout arearefs="class12">
+ <para>
+ <literal>where</literal> iIvVjF
+ ±ÌNXÌIuWFNgðõ·éÆ«Égp·éACÓÌSQLÌ <literal>WHERE</literal> ððwèµÜ·B
+ </para>
+ </callout>
+ <callout arearefs="class13">
+ <para>
+ <literal>persister</literal> iIvVjFJX^ <literal>ClassPersister</literal> ðwèµÜ·B
+ </para>
+ </callout>
+ <callout arearefs="class14">
+ <para>
+ <literal>batch-size</literal> iIvV, ftHgÍ <literal>1</literal> jF
+ ¯ÊqűÌNXÌCX^Xð³·éÆ«Ìuob`TCYvðwèµÜ·B
+ </para>
+ </callout>
+ <callout arearefs="class15">
+ <para>
+ <literal>optimistic-lock</literal> iIvV,ftHgÍ <literal>version</literal> jF
+ yÏbNíªðèµÜ·B
+ </para>
+ </callout>
+ <callout arearefs="class16">
+ <para>
+ <literal>lazy</literal> iIvVjF <literal>lazy="false"</literal> ÆÝè·é±ÆÅA
+ xtFb`ªÅ«ÈÈèÜ·B
+ </para>
+ </callout>
+ <callout arearefs="class17">
+ <para>
+ <literal>entity-name</literal> iIvVAftHgÍNX¼jF
+ Hibernate3ÅÍNXª¡ñ}bsOÅ«iêÉæÁÄÍá¤e[uÉεÄàjA
+ JavaxÅMapâXMLÅ\»³êéGeBeB}bsOªÂ\Å·B
+ ±êçÌêAGeBeBÉεÄCÓ̼OðA¾¦IÉt¯ÈÄÍÈèܹñB
+ ÚµÍ <xref linkend="persistent-classes-dynamicmodels"/> Æ <xref linkend="xml"/> ðQƵľ³¢B
+ </para>
+ </callout>
+ <callout arearefs="class18">
+ <para>
+ <literal>check</literal> iIvVjF©®IÉXL[}𶬷é½ßÉA
+ ¡sÌ <emphasis>check</emphasis> §ñ𶬷éSQL®B
+ </para>
+ </callout>
+ <callout arearefs="class19">
+ <para>
+ <literal>rowid</literal> iIvVjFHibernateÍA»êðT|[gµÄ¢éf[^x[XÅROWIDÆ
+ ÄÎêéàÌðg¤±ÆªÅ«Ü·B
+ á¦ÎOracleðgÁÄ¢éÆ«A±ÌIvVÉ <literal>rowid</literal> ðÝè·êÎA
+ HiberanteÍupdate𬻷é½ßÉ <literal>rowid</literal> Æ¢¤ÁÊÈJðg¤±ÆªÅ«Ü·B
+ ROWIDÍÚ×ÈÀÅ èAÛ¶³ê½^v̨IÈÊuð\µÄ¢Ü·B
+ </para>
+ </callout>
+ <callout arearefs="class20">
+ <para>
+ <literal>subselect</literal> iIvVjFsÏ©ÂÇÝæèêpÅ éGeBeBð
+ f[^x[XÌâ¹isubselectjÉ}bsOµÜ·B
+ ൳Ìe[uÌãíèÉr
[𿽯êÎLpÅ·ªA
+ »¤ÅÈ¢ÌÈçLpÅÍ èܹñBæèÚµ¢îñͺLðQƵľ³¢B
+ </para>
+ </callout>
+ <callout arearefs="class21">
+ <para>
+ <literal>abstract</literal> iIvVjF
+ <literal><union-subclass></literal> KwàÌÛX[p[NXÉ}[N·é½ßÉg¢Ü·B
+ </para>
+ </callout>
+
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ i±NX̼OÉC^[tFCXðwèµÄàÜÁ½âè èܹñB
+ »ÌÆ«Í <literal><subclass></literal> vfðgÁÄA
+ »ÌC^[tFCXðÀ·éNXðè`µÄ¾³¢B
+ <emphasis>static</emphasis> ÈàNXÅài±»Å«Ü·B
+ »ÌÆ«ÍW`®Aá¦Î <literal>eg.Foo$Bar</literal> ðgÁÄNX¼ðwèµÄ¾³¢B
+ </para>
+
+ <para>
+ <literal>mutable="false"</literal> wèðµ½sÏNXÍA
+ AvP[VÉæéXVâíªoÈ¢±Æª èÜ·B
+ ±êÉæèAHibernateªptH[}XðµüPµÜ·B
+ </para>
+
+ <para>
+ IvVÌ <literal>proxy</literal> ®«ÉæèANXÌi±CX^XÌxú»ªÂ\ÉÈèÜ·B
+ HibernateÍÅÉAwèµ½C^[tFCXðÀµ½CGLIBvLVðԵܷB
+ ÀÛÌi±IuWFNgÍvLVÌ\bhðÄÑo·Æ«É[hµÜ·B
+ ȺÌuxú»Ì½ßÌvLVvðQƵľ³¢B
+ </para>
+
+ <para>
+ <emphasis>ÃÙI</emphasis> |[tBYÆÍAÌñÂðÓ¡µÄ¢Ü·B
+ êÂÍNXÌCX^XªAX[p[NXâÀµ½C^[tFCXAܽ»ÌNXðwè·éNGÉæÁÄÔ³êé±ÆÅA
+ à¤êÂÍ»ÌNXÌTuNXÌCX^XªA»ÌNX©gðwèµ½NGÉæÁÄÔ³êé±ÆÅ·B
+ ܽA<emphasis>¾¦I</emphasis> |[tBYÆÍAÌñÂðÓ¡µÄ¢Ü·B
+ êÂÍNXÌCX^XªA»ÌNXð¾¦IÉwèµ½NGÉæÁÄÌÝÔ³êé±ÆÅA
+ à¤êÂÍNXðwèµ½NGªA<literal><class></literal> vfÌÅ <literal><subclass></literal> â
+ <literal><joined-subclass></literal> Æ}bsO³êÄ¢éTuNXÌCX^X¾¯ðÔ·±ÆÅ·B
+ ÙÆñÇÌprÅÍftHgÌ <literal>polymorphism="implicit"</literal> ªKØÅ·B
+ ¾¦IÈ|[tBYÍA2ÂÌáÁ½NXª¯¶e[uÉ}bsO³êÄ¢éÆ«ÉLpÅ·
+ i±êÉæÁÄe[uJÌTuZbgðÜÞAuyÊÈvNXªÂ\ÉÈèÜ·jB
+ </para>
+
+ <para>
+ <literal>persister</literal> ®«ðwè·é±ÆÅANXÌi±»íªðJX^}CYÅ«Ü·B
+ á¦Î <literal>org.hibernate.persister.EntityPersister</literal> ©gÌTuNXðwèµ½èA
+ ܽá¦ÎXgAhvV[WR[Atbgt@CÖVACYA
+ LDAPÈÇðʵ½i±«ðÀ·é <literal>org.hibernate.persister.ClassPersister</literal>
+ C^[tFCXÌ®SÉVµ¢ÀðñÅ«Ü·BÈPÈáÆµÄ <literal>org.hibernate.test.CustomPersister</literal>
+ ð©Ä¾³¢i±êÍ <literal>Hashtable</literal> Ìui±»vÅ·jB
+
+ </para>
+
+ <para>
+ <literal>dynamic-update</literal> Æ <literal>dynamic-insert</literal> ÌÝèÍTuNXÉp³³êܹñB
+ »Ì½ß <literal><subclass></literal> â <literal><joined-subclass></literal> vfðwè·é±ÆàoÜ·B
+ ±êçÌÝèÍptH[}Xðüã³¹éà èÜ·ªAÆ·±Æà èÜ·ÌÅATdÉgpµÄ¾³¢B
+ </para>
+
+ <para>
+ <literal>select-before-update</literal> ÌgpÍÊíptH[}XðƵܷB
+ ൠ<literal>Session</literal> Öª£CX^XÌOtðÄÇÁ·éÈçA
+ f[^x[XXVÌgKðsKvÉÄÑo·Ìðð¯éÆ¢¤_ÅAñíÉLpÅ·B
+ </para>
+
+ <para>
+ <literal>dynamic-update</literal> ðLøÉ·êÎAyÏbNíªðIÔ±ÆÉÈèÜ·B
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>version</literal> o[W/^CX^vJð`FbNµÜ·
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>all</literal> ·×ÄÌJð`FbNµÜ·B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>dirty</literal> ÏXµ½Jð`FbNµA¯XVÅ«éæ¤ÉµÜ·B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>none</literal> yÏbNðgpµÜ¹ñ
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+ HibernateÅyÏIbNíªðg¤ÈçAo[W/^CX^vJðg¤±Æð
+ <emphasis> ñíÉ</emphasis> ¨©ßµÜ·B
+ yÏIbNÍptH[}XÌÏ_©çàÅKÅ èA³çɪ£CX^XÖÌC³
+ iÂÜè <literal>Session.marge()</literal> ªgíêéÆ«j
+ ð³mɵ¤±ÆÌÅ«éBêÌíªÅà èÜ·B
+ </para>
+
+ <para>
+ HibernateÌ}bsOÉÆÁÄr
[ÆÊÌe[uÌÔÉá¢ÍÈA
+ f[^x[XxÅͧßIÅ·
+ i½¾µr
[ð®SÉÍT|[gµÄ¢È¢DBMSà èÜ·B
+ ÁÉAXVÌ ér
[ÉεÄÍ»¤Å·jB
+ r
[ðg¢½ÄàAf[^x[XÅì¬Å«È¢±Æª èÜ·
+ iá¦ÎAKV[XL[}ÌêjB
+ ±ÌêÉÍAsÏ©ÂÇÝæèêpÌGeBeBÉ^¦çê½SQLÌ⹶ð}bvÅ«Ü·B
+ </para>
+
+ <programlisting><![CDATA[<class name="Summary">
+ <subselect>
+ select item.name, max(bid.amount), count(*)
+ from item
+ join bid on bid.item_id = item.id
+ group by item.name
+ </subselect>
+ <synchronize table="item"/>
+ <synchronize table="bid"/>
+ <id name="name"/>
+ ...
+</class>]]></programlisting>
+
+ <para>
+ e[uð±ÌGeBeBƯú·éæ¤Éè`µÄ¾³¢B
+ I[gtbV
ªmÀÉN±éæ¤ÉAܽ±oGeBeBÉηéNGªÃ¢f[^ð
+ ԳȢæ¤É·é½ßÅ·B
+ <literal><subselect></literal> Í®«ÆlXgµ½}bsO®«ÌÇ¿çÅàpÅ«Ü·B
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-id" revision="4">
+ <title>id</title>
+
+ <para>
+ }bv³ê½NXÍf[^x[Xe[uÌåL[Jðè` <emphasis>µÈ¯êÎÈèܹñ</emphasis> B
+ ÙÆñÇÌNXÉÍCX^XÌj[NȯÊqðÛ·éJavaBeansX^CÌvpeBà èÜ·B
+ <literal><id></literal> vfÍA»ÌvpeB©çåL[JÖÌ}bsOðè`µÜ·B
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="id1" coords="2 70"/>
+ <area id="id2" coords="3 70" />
+ <area id="id3" coords="4 70"/>
+ <area id="id4" coords="5 70" />
+ <area id="id5" coords="6 70" />
+ </areaspec>
+ <programlisting><![CDATA[<id
+ name="propertyName"
+ type="typename"
+ column="column_name"
+ unsaved-value="null|any|none|undefined|id_value"
+ access="field|property|ClassName">
+ node="element-name|@attribute-name|element/@attribute|."
+
+ <generator class="generatorClass"/>
+</id>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="id1">
+ <para>
+ <literal>name</literal>iIvVjF¯ÊqvpeB̼OB
+ </para>
+ </callout>
+ <callout arearefs="id2">
+ <para>
+ <literal>type</literal>iIvVjFHibernateÌ^𦷼OB
+ </para>
+ </callout>
+ <callout arearefs="id3">
+ <para>
+ <literal>column</literal>iIvV - ftHgÍvpeB¼jF
+ åL[J̼OB
+ </para>
+ </callout>
+ <callout arearefs="id4">
+ <para>
+ <literal>unsaved-value</literal>iIvV - ftHgÌlÍsensiblejF
+ CX^XªVµCX^X»³ê½
+ iZ[u³êĢȢj±Æð¦·A¯ÊqvpeBÌlB
+ ÈOÌSessionÅZ[uܽÍ[h³ê½êICX^XÆæÊ·é½ßÉ
+ g¢Ü·B
+ </para>
+ </callout>
+ <callout arearefs="id5">
+ <para>
+ <literal>access</literal>iIvV - ftHgÍ <literal>property</literal> jF
+ vpeBÌlÖANZX·é½ßÉHibernateªg¤íªÅ·B
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ <literal>name</literal> ®«ªÈ¯êÎANXÉͯÊqvpeBªÈ¢àÌÆÝȳêÜ·B
+ </para>
+
+ <para>
+ <literal>unsaved-value</literal> ®«ÍHibernate3ÅÍÙÆñÇÌêAKvÅÍ èܹñB
+ </para>
+
+ <para>
+ ¡L[ðÂKV[f[^ÉANZXÅ«éæ¤ÉA
+ <literal><composite-id></literal> Æ¢¤ãÖÌ}bsOè`ª èÜ·B
+ µ©µ¼ÌprÖÌgpÍS¨··ßūܹñB
+ </para>
+
+ <sect3 id="mapping-declaration-id-generator" revision="2">
+ <title>WFl[^</title>
+
+ <para>
+ IvVÌ <literal><generator></literal> qvfÍA
+ i±NXÌCX^XÌj[NȯÊq𶬷é½ßÉg¤AJavaNXðwèµÜ·B
+ WFl[^CX^XÌÝèAàµÍú»Ép[^ªKvÅ êÎA<literal><param></literal>
+ vfðgÁÄn·±ÆªÅ«Ü·B
+ </para>
+
+ <programlisting><![CDATA[<id name="id" type="long" column="cat_id">
+ <generator class="org.hibernate.id.TableHiLoGenerator">
+ <param name="table">uid_table</param>
+ <param name="column">next_hi_value_column</param>
+ </generator>
+</id>]]></programlisting>
+
+ <para>
+ ·×ÄÌWFl[^ÍAC^[tFCX
+ <literal>org.hibernate.id.IdentifierGenerator</literal> ðÀµÜ·B
+ ±êÍÆÄàPÈC^[tFCXÈÌÅAÁÊÈÀðÆ©ÉpÓ·éAvP[Và é©àµêܹñB
+ µ©µHibernateÍgÝÝÌÀð¢ÂàpӵĢܷB
+ gÝÝÌWFl[^ÉÍȺÌV[gJbg¼ª èÜ·F
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>increment</literal></term>
+ <listitem>
+ <para>
+ <literal>long</literal> , <literal>short</literal> ,
+ <literal>int</literal> ^̯Êq𶬵ܷB
+ ±êçͼÌvZXª¯¶e[uÉf[^ð}üµÈ¢Æ«¾¯j[NÅ·B
+ <emphasis>NX^àÅÍgíȢž³¢</emphasis> B
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>identity</literal></term>
+ <listitem>
+ <para>
+ DB2, MySQL, MS SQL Server, Sybase, HypersonicSQL̯ÊqJð
+ T|[gµÜ·B
+ Ô³êé¯ÊqÌ^Í <literal>long</literal> , <literal>short</literal> ,
+ <literal>int</literal> Ì¢¸ê©Å·B
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>sequence</literal></term>
+ <listitem>
+ <para>
+ DB2, PostgreSQL, Oracle, SAP DB, McKoiÌV[PXâAInterbaseÌWFl[^ðgpµÜ·B
+ Ô³êé¯ÊqÌ^Í <literal>long</literal> , <literal>short</literal> ,
+ <literal>int</literal> Ì¢¸ê©Å·B
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>hilo</literal></term>
+ <listitem>
+ <para id="mapping-declaration-id-hilodescription" revision="1">
+ <literal>long</literal> , <literal>short</literal> ,
+ <literal>int</literal> ^̯Êqðø¦Iɶ¬·éhi/loASYðg¢Ü·B
+ hilÌ\[XƵÄAe[uÆJð^¦Ü·(ftHgÅÍ»ê¼ê <literal>hibernate_unique_key</literal> Æ
+ <literal>next_hi</literal> )B
+
+ hi/loASYÍÁèÌf[^x[XÉεÄÌÝj[NȯÊq𶬵ܷB
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>seqhilo</literal></term>
+ <listitem>
+ <para>
+ <literal>long</literal> , <literal>short</literal> ,
+ <literal>int</literal> ^̯Êqðø¦Iɶ¬·éhi/loASYðg¢Ü·B
+ wè³ê½f[^x[XV[PXð^¦Ü·B
+
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>uuid</literal></term>
+ <listitem>
+ <para>
+ (IPAhXªgp³êé)lbg[NàÅj[Nȶñ^̯Êq𶬷é½ßÉA
+ 128rbgÌUUIDASYðgpµÜ·BUUIDÍ·³32Ì16i̶ñƵÄGR[h³êÜ·B
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>guid</literal></term>
+ <listitem>
+ <para>
+ MS SQLT[oÆMySQLÅf[^x[Xª¶¬·éGUID¶ñðgpµÜ·B
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>native</literal></term>
+ <listitem>
+ <para>
+ gp·éf[^x[XÌ«\Éæè <literal>identity</literal> , <literal>sequence</literal> ,
+ <literal>hilo</literal> Ì¢¸ê©ªIÎêÜ·B
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>assigned</literal></term>
+ <listitem>
+ <para>
+ <literal>save()</literal> ªÄÎêéOÉA
+ AvP[VªIuWFNgɯÊqðãüÅ«éæ¤ÉµÜ·B
+ <literal><generator></literal> ªwè³êĢȯêÎA±êªftHgÌíªÉÈèÜ·B
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>select</literal></term>
+ <listitem>
+ <para>
+ éj[NL[ÉæésÌIðÆåL[Ìl̳ÉæèA
+ f[^x[XgKªèĽåL[ð澵ܷB
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>foreign</literal></term>
+ <listitem>
+ <para>
+ ¼ÌÖAIuWFNg̯Êqðg¢Ü·B
+ ÊÍA<literal><one-to-one></literal> åL[ÖAÆgÝí¹Äg¢Ü·B
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ </para>
+ </sect3>
+
+ <sect3 id="mapping-declaration-id-hilo" revision="1">
+ <title>Hi/lo ASY</title>
+ <para>
+ <literal>hilo</literal> Æ <literal>seqhilo</literal> WFl[^ÍA
+ ¯Êq¶¬Ìã\IÈAv[`Å éhi/loASYÌ2ÂÌãÖÀðñµÜ·B
+
+ 1ÔÚÌÀÍAñÉp³êé"hi"lðÛ·éuÁÊÈvf[^x[Xe[uð
+ KvƵܷB
+ 2ÔÚÌÀÍAOracleX^CÌV[PXðg¢Ü·iT|[g³êÄ¢éêjB
+ </para>
+
+ <programlisting><![CDATA[<id name="id" type="long" column="cat_id">
+ <generator class="hilo">
+ <param name="table">hi_value</param>
+ <param name="column">next_value</param>
+ <param name="max_lo">100</param>
+ </generator>
+</id>]]></programlisting>
+
+ <programlisting><![CDATA[<id name="id" type="long" column="cat_id">
+ <generator class="seqhilo">
+ <param name="sequence">hi_value</param>
+ <param name="max_lo">100</param>
+ </generator>
+</id>]]></programlisting>
+
+ <para>
+ cOȪçHibernateÖÌÆ©Ì <literal>Connection</literal> ðñ·éÆ«ÉÍA<literal>hilo</literal> ðg¦Ü¹ñB
+ HibernateªJTAÅXg³êÄ¢éÚ±ðæ¾·é½ßÉAvP[VT[o[Ìf[^\[XðgpµÄ¢éÆ«ÉÍA
+ <literal>hibernate.transaction.manager_lookup_class</literal> ðKØÉÝèµÈ¯êÎÈèܹñB
+ </para>
+ </sect3>
+
+ <sect3 id="mapping-declaration-id-uuid">
+ <title>UUID ASY</title>
+ <para>
+ UUIDÉÍȺÌà̪ÜÜêÜ·F
+ IPAhXAJVMÌX^[gAbv^Ci4ªÌ1b̳m³jA
+ VXeÔAiJVMÉεÄj[NÈjJE^lB
+ JavaR[h©çMACAhXâAhXðæ¾·é±ÆÍÅ«È¢ÌÅA
+ JNIªg¦È¢Æ«ÌÅÇÌû@Å·B
+ </para>
+ </sect3>
+
+ <sect3 id="mapping-declaration-id-sequences">
+ <title>¯ÊqJÆV[PX</title>
+ <para>
+ ¯ÊqJðT|[gµÄ¢éf[^x[XiDB2, MySQL, Sybase, MS SQLjÅÍA
+ <literal>identity</literal> L[¶¬ðg¦Ü·B
+ V[PXðT|[g·éf[^x[XiDB2, Oracle, PostgreSQL, Interbase, McKoi, SAP DBjÅÍA
+ <literal>sequence</literal> X^CÌL[¶¬ðg¦Ü·B
+ Ç¿çÌíªàAVµ¢IuWFNgð}ü·é½ßÉASQLNGð2ÂKvƵܷB
+ </para>
+
+ <programlisting><![CDATA[<id name="id" type="long" column="person_id">
+ <generator class="sequence">
+ <param name="sequence">person_id_sequence</param>
+ </generator>
+</id>]]></programlisting>
+
+ <programlisting><![CDATA[<id name="id" type="long" column="person_id" unsaved-value="0">
+ <generator class="identity"/>
+</id>]]></programlisting>
+
+ <para>
+ NXvbgtH[ÌJÅÍA<literal>native</literal> íªÍ
+ <literal>identity</literal> , <literal>sequence</literal> ,
+ <literal>hilo</literal> íªÌ©ç1ÂðIðµÜ·ªA
+ ±êÍgpµÄ¢éf[^x[XÌ\ÍÉ˶µÜ·B
+ </para>
+ </sect3>
+
+ <sect3 id="mapping-declaration-id-assigned">
+ <title>¯ÊqÌèÄ</title>
+ <para>
+ AvP[VɯÊqðèij¹½¢ÌÅ êÎ(Hibernateª¶¬·éàÌÅÍÈjA
+ <literal>assigned</literal> WFl[^ðg¤±ÆªÅ«Ü·B
+ ±ÌÁÊÈWFl[^ÍA·ÅÉIuWFNg̯ÊqvpeBÉãü³ê½lð
+ ¯ÊqÉg¢Ü·B±ÌWFl[^ÍåL[ªãL[ÌãíèÉ©RL[Å éêÉgpµÜ·B
+ <literal><generator></literal> vfðwèµÈ¢êÌftHgÌ®ìÉÈèÜ·B
+ </para>
+
+ <para>
+ <literal>assigned</literal> WFl[^ðIð·éÆA
+ HibernateÍ <literal>unsaved-value="undefined"</literal> ðgpµÜ·B
+ »µÄAo[Wâ^CX^vÌvpeBªÈ¢êâ <literal>Interceptor.isUnsaved()</literal>
+ ðè`µÈ©Á½êÉÍACX^XªêI(transient)ÈàÌÅ éÌ©A
+ ܽÍZbV©çª£(detached)µ½à̩Ǥ©ðßé½ßÉAf[^x[Xð²×Ü·B
+ </para>
+ </sect3>
+
+ <sect3 id="mapping-declaration-id-select">
+ <title>gKÉæèèÄçê½åL[</title>
+ <para>
+ KV[XL[}̽ßÉÌÝwèµÜ·(HibernateÍgKðgÁÄDDL𶬵ܹñ)B
+ </para>
+
+ <programlisting><![CDATA[<id name="id" type="long" column="person_id">
+ <generator class="select">
+ <param name="key">socialSecurityNumber</param>
+ </generator>
+</id>]]></programlisting>
+
+ <para>
+ ãÌáÌÅANXÅ©RL[ƵÄè`³ê½
+ <literal>socialSecurityNumber</literal> Æ¢¤¼OÌj[NÈlÌvpeBÆA
+ lªgKÉæ趬³êé <literal>person_id</literal> Æ¢¤¼OÌãL[ª èÜ·B
+ </para>
+
+ </sect3>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-compositeid" revision="3">
+ <title>composite-id</title>
+
+ <programlisting><![CDATA[<composite-id
+ name="propertyName"
+ class="ClassName"
+ mapped="true|false"
+ access="field|property|ClassName">
+ node="element-name|."
+
+ <key-property name="propertyName" type="typename" column="column_name"/>
+ <key-many-to-one name="propertyName class="ClassName" column="column_name"/>
+ ......
+</composite-id>]]></programlisting>
+
+ <para>
+ ¡L[Ì ée[uÉεA
+ ¯ÊqvpeBƵÄNXÌ¡ÌvpeBð}bsO·é±ÆªÅ«Ü·B
+ <literal><composite-id></literal> vfÍAqvfƵÄ
+ <literal><key-property></literal> vpeB}bsOÆ
+ <literal><key-many-to-one></literal> }bsOðó¯üêÜ·B
+ </para>
+
+ <programlisting><![CDATA[<composite-id>
+ <key-property name="medicareNumber"/>
+ <key-property name="dependent"/>
+</composite-id>]]></programlisting>
+
+ <para>
+ ¡¯ÊqÌ¿«ðÀ·é½ßÉÍAi±NXª <literal>equals()</literal> Æ
+ <literal>hashCode()</literal> ðI[o[Ch <emphasis>µÈ¯êÎÈèܹñ</emphasis> B
+ ܽ <literal>Serializable</literal> àÀµÈ¯ê΢¯Ü¹ñB
+ </para>
+
+ <para>
+ cOȪ硯Êq̽ß̱Ìû@ÍA
+ i±IuWFNgª©g̯ÊqÅ é±ÆðÓ¡µÄ¢Ü·B
+ IuWFNg©gð¯ÊqÆ·éÈãÌÖÈuµ¢ûvÍ èܹñB
+ ¡L[ÉÖAµ½i±óÔð <literal>load()</literal> oéæ¤ÉÈéOÉA
+ i±NX©gðCX^X»µA¯ÊqvpeBðÝèµÈ¯êÎÈèܹñB
+ <emphasis> gÝÝÌ</emphasis> ¡¯ÊqÆÄÎêé±ÌAv[`ÍA
+ {iIÈAvP[VÉÍü¢Ä¢Ü¹ñB
+
+
+ </para>
+
+ <para>
+ 2ÂÚÌû@Í <emphasis> }bv³ê½</emphasis> ¡¯ÊqÆÄÎêéàÌÅA
+ <literal><composite-id></literal>GgàÅwèµ½¯ÊvpeBª
+ i±NXƪ£µ½¯ÊqNX̼ûÉd¡µÄ¶ÝµÜ·B
+ </para>
+
+ <programlisting><![CDATA[<composite-id class="MedicareId" mapped="true">
+ <key-property name="medicareNumber"/>
+ <key-property name="dependent"/>
+</composite-id>]]></programlisting>
+
+ <para>
+ ±ÌáÅÍA¡¯ÊqNXi <literal>MedicareId</literal> jÆGeBeBNX©g̼ûªA
+ <literal>medicareNumber</literal> Æ <literal>dependent</literal> Æ¢¤¼OÌvpeBð¿Ü·B
+ ¯ÊqNXÍA<literal>equals()</literal> Æ <literal>hashCode()</literal> ðI[oChµA
+ <literal>Serializable</literal> ðÀµÈÄÍÈèܹñB
+ ±Ìû@ÉÍA¾ç©ÉR[hªd¡·éÆ¢¤ssª èÜ·B
+ </para>
+
+ <para>
+ Ì®«Í}bsOµ½¡¯Êqðwè·é½ßÉgpµÜ·B
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>mapped</literal> (IvVAftHgÍ <literal>false</literal> ):
+ }bsOµ½¡¯Êqªgp³êé±ÆÆAïܳê½vpeBÌ}bsOªA
+ GeBeBNXÆ¡¯ÊqNX̼ûðQÆ·é±Æð¦µÜ·B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>class</literal> (IvV,½¾µ}bsOµ½¡¯ÊqÉÍK{):
+ ¡¯ÊqƵÄgp·éNXB
+
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ 3ÂÚ̳çÉÖÈû@ÍA¡¯Êqð<xref linkend="components-compositeid"/>àÌ
+ R|[lgNXƵÄÀ·é±ÆÅ·B
+ ºÅLqµÄ¢é®«ÍA±ÌãÖû@ÉÌÝKp³êÜ·B
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>name</literal> (IvV, ±ÌAv[`ÅÍK{):
+ ¡¯ÊqðÛ·éR|[lg^CvÌvpeB(9ÍðQƵľ³¢).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>access</literal> (IvV - ftHgÍ <literal>property</literal> ):
+ HibernateªvpeBÌlÉANZX·é½ßÉgp·×«íªB
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>class</literal>
+ iIvV - ftHgÍtNVÉæèè³êévpeBÌ^jF
+ ¡¯ÊqƵÄgíêéR|[lgÌNXiÌßð©Ä¾³¢jB
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ±Ì3ÂÚÌû@Í <emphasis> ¯ÊqR|[lg</emphasis> ÆÄÑA
+ ÙÆñÇ·×ÄÌAvP[VÉεħ·éû@Å·B
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-discriminator" revision="3">
+ <title>discriminator</title>
+
+ <para>
+ <literal><discriminator></literal> vfÍA
+ table-per-class-hierarchy}bsOíªðg¤|[tBbNÈi±»ÉKvÅ èA
+ e[u̯ÊJðè`µÜ·B
+ ¯ÊJÍA ésÉεÄi±wªÇÌTuNXðCX^X»·é©ð
+ `¦é}[J[lðÜñŢܷB
+ ȺÌæ¤È^ɧÀ³êÜ·F <literal>string</literal> , <literal>character</literal> ,
+ <literal>integer</literal>,
+ <literal>byte</literal> , <literal>short</literal> , <literal>boolean</literal> ,
+ <literal>yes_no</literal> , <literal>true_false</literal>.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="discriminator1" coords="2 60"/>
+ <area id="discriminator2" coords="3 60" />
+ <area id="discriminator3" coords="4 60" />
+ <area id="discriminator4" coords="5 60" />
+ <area id="discriminator5" coords="6 60" />
+ </areaspec>
+ <programlisting><![CDATA[<discriminator
+ column="discriminator_column"
+ type="discriminator_type"
+ force="true|false"
+ insert="true|false"
+ formula="arbitrary sql expression"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="discriminator1">
+ <para>
+ <literal>column</literal>iIvV - ftHgÍ <literal>class</literal> jF
+ ¯ÊJ̼OB
+ </para>
+ </callout>
+ <callout arearefs="discriminator2">
+ <para>
+ <literal>type</literal>
+ iIvV - ftHgÍ <literal>string</literal> jFHibernateÌ^𦷼OB
+ </para>
+ </callout>
+ <callout arearefs="discriminator3">
+ <para>
+ <literal>force</literal>
+ iIvV - ftHgÍ <literal>false</literal> jF
+ [gNXÌ·×ÄÌCX^Xðõ·éêÅ ÁÄàA
+ HibernateªgpÅ«é¯ÊJÌwèðu§vµÜ·B
+ </para>
+ </callout>
+ <callout arearefs="discriminator4">
+ <para>
+ <literal>insert</literal>
+ iIvV - ftHgÍ <literal>true</literal> jF
+ ൯ÊJª}bsO·é¡¯ÊqÌêÈçÎA<literal>false</literal> ÆÝèµÄ¾³¢B
+ (HibernateÉSQLÌ <literal>INSERT</literal> ÉÍÜÜêÈ¢±Æðmç¹é)
+
+ </para>
+ </callout>
+ <callout arearefs="discriminator5">
+ <para>
+ <literal>formula</literal> (IvV)^ª]¿³êéÆ«ÉÀs³êéCÓÌSQL®B
+ Recx[X̯ÊðÂ\ɵܷB
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ ¯ÊJÌÀÛÌlÍA <literal><class></literal> Æ
+ <literal><subclass></literal> vfÌ
+ <literal>discriminator-value</literal> ®«Åwè³êÜ·B
+ </para>
+
+ <para>
+ i±NXÖ}bsO³êÈ¢¢]ªÈv¯ÊlðÂsª
+ e[uÉ êÎAi»ÌÆ«ÉÀèj<literal>force</literal> ®«ÍLøÅ·B
+ ½¾µAÊÍ»¤¢¤±ÆÍ èܹñB
+ </para>
+
+ <para>
+ <literal>formula</literal> ®«ðg¤ÆAsÌ^ð]¿·é½ßÉCÓÌSQL®ðé¾Å«Ü·B
+ </para>
+
+ <programlisting><![CDATA[<discriminator
+ formula="case when CLASS_TYPE in ('a', 'b', 'c') then 0 else 1 end"
+ type="integer"/>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-version" revision="4">
+ <title>versioniIvVj</title>
+
+ <para>
+ <literal><version></literal> vfÍIvVÅ èA
+ e[uªo[Wf[^ðÜÞ±Æð¦µÜ·B
+ ±êÍ <emphasis>OgUNV</emphasis>
+ ðg¤ÂàèÈçAÁÉð§¿Ü·iȺð©Ä¾³¢jB
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="version1" coords="2 70"/>
+ <area id="version2" coords="3 70"/>
+ <area id="version3" coords="4 70"/>
+ <area id="version4" coords="5 70"/>
+ <area id="version5" coords="6 70"/>
+ <area id="version6" coords="7 70"/>
+ <area id="version7" coords="8 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<version
+ column="version_column"
+ name="propertyName"
+ type="typename"
+ access="field|property|ClassName"
+ unsaved-value="null|negative|undefined"
+ generated="never|always"
+ insert="true|false"
+ node="element-name|@attribute-name|element/@attribute|."
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="version1">
+ <para>
+ <literal>column</literal>iIvV - ftHgÍvpeB¼j:
+ o[WÔðÛ·éJ̼OB
+ </para>
+ </callout>
+ <callout arearefs="version2">
+ <para>
+ <literal>name</literal> Fi±NXÌvpeB̼OB
+ </para>
+ </callout>
+ <callout arearefs="version3">
+ <para>
+ <literal>type</literal>
+ iIvV - ftHgÍ <literal>integer</literal> jFo[WÔÌ^B
+ </para>
+ </callout>
+ <callout arearefs="version4">
+ <para>
+ <literal>access</literal>
+ iIvV - ftHgÍ <literal>property</literal> jF
+ vpeBÌlÖÌANZXÉHibernateªg¤íªB
+ </para>
+ </callout>
+ <callout arearefs="version5">
+ <para>
+ <literal>unsaved-value</literal>
+ iIvV - ftHgÍ <literal>undefined</literal> jF
+ CX^XªVµCX^X»³ê½±Æð¦·
+ iZ[u³êĢȢ±Æð¦·jo[WvpeBÌlB
+ ÈOÌSessionÅZ[uܽÍ[h³ê½êIÈCX^XÆæÊ·é½ßÉ
+ g¢Ü·B
+ i <literal>undefined</literal> ͯÊqvpeBÌlªgíêé±ÆðwèµÜ·Bj
+ </para>
+ </callout>
+
+ <callout arearefs="version6">
+ <para>
+ <literal>generated</literal> (IvV - ftHgÍ <literal>never</literal> ):
+ ±Ìo[WÌvpeBÌlªAf[^x[XÉæÁĶ¬³ê½±ÆðwèµÜ·B
+ <xref linkend="mapping-generated">¶¬vpeB</xref> Ìc_ð©Ä¾³¢B
+ </para>
+ </callout>
+ <callout arearefs="version7">
+ <para>
+ <literal>insert</literal> (IvV - ftHgÍ <literal>true</literal> ):
+ SQLÌinsert¶Éo[WEJðÜßé׫©Ç¤©ðwèµÜ·B
+ àµf[^x[XEJÌftHglª <literal>0</literal> Æè`³êéÆ«ÉÍA
+ <literal>false</literal> ÉÝè·éÆǢŵå¤B
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ o[WÔÍ HibernateÌ <literal>long</literal> , <literal>integer</literal> ,
+ <literal>short</literal> , <literal>timestamp</literal> ,
+ <literal>calendar</literal> ^Ì¢¸ê©Å·B
+ </para>
+
+ <para>
+ o[Wâ^CX^vÌvpeBÍAª£³ê½CX^XÉεÄnullÅ ÁÄÍÈèܹñB
+ »Ì½ßÇÌæ¤È <literal>unsaved-value</literal> íªªwè³êÄàA
+ HibernateÍnullÌo[Wâ^CX^vðÁ½·×ÄÌCX^XðA
+ êIÈàÌÅ éÆ»fµÜ·B
+
+ <emphasis> nullðe·éo[Wâ^CX^vÌvpeBðè`·é±ÆÍA
+ ßnIÉêIuWFNgÆ·é±Æðh®ÈPÈû@Å·B
+ ÁɯÊqÌèÄâ¡L[ðgpµÄ¢éÆ«ÉÍÁÉLpÅ·B</emphasis>
+ </para>
+ </sect2>
+
+ <sect2 id="mapping-declaration-timestamp" revision="4">
+ <title>timestampiIvVj</title>
+
+ <para>
+ IvVÌ <literal><timestamp></literal> vfÍA
+ e[uª^CX^vf[^ðÜÞ±Æð¦µÜ·B
+ ±êÍo[Wt¯ÌãíèÌû@ƵÄpÓ³êĢܷB
+ ^CX^vÍàÆàÆyÏIbNɨ¯éÀS«Ìá¢ÀÅ·B
+ µ©µAvP[VÍÙÈéprÅg¤±Æà é©àµêܹñB
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="timestamp1" coords="2 70"/>
+ <area id="timestamp2" coords="3 70" />
+ <area id="timestamp3" coords="4 70" />
+ <area id="timestamp4" coords="5 70" />
+ <area id="timestamp5" coords="6 70" />
+ <area id="timestamp6" coords="7 70" />
+ </areaspec>
+ <programlisting><![CDATA[<timestamp
+ column="timestamp_column"
+ name="propertyName"
+ access="field|property|ClassName"
+ unsaved-value="null|undefined"
+ source="vm|db"
+ generated="never|always"
+ node="element-name|@attribute-name|element/@attribute|."
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="timestamp1">
+ <para>
+ <literal>column</literal>iIvV - ftHgÍvpeB¼jF
+ ^CX^vðÛ·éJ̼OB
+ </para>
+ </callout>
+ <callout arearefs="timestamp2">
+ <para>
+ <literal>name</literal> F
+ i±NXÅ éJava Ì <literal>Date</literal >^ ܽÍ
+ <literal>Timestamp</literal> ^ ÌAJavaBeansX^CvpeB̼OB
+ </para>
+ </callout>
+ <callout arearefs="timestamp3">
+ <para>
+ <literal>access</literal>
+ iIvV - ftHgÍ <literal>property</literal> jF
+ vpeBÌlÖÌANZXÉHibernateªg¤íªB
+ </para>
+ </callout>
+ <callout arearefs="timestamp4">
+ <para>
+ <literal>unsaved-value</literal>
+ iIvV - ftHgÍ <literal>null</literal> jF
+ CX^XªVµCX^X»³ê½
+ iZ[u³êĢȢj±Æð¦·o[WvpeBÌlB
+ ÈOÌSessionÅZ[uܽÍ[h³ê½êIÈCX^XÆ
+ æÊ·é½ßÉgíêÜ·B
+ i <literal>undefined</literal> Æwè·éÆA
+ ¯ÊqvpeBÌlªgíêÜ·Bj
+ </para>
+ </callout>
+
+ <callout arearefs="timestamp5">
+ <para>
+ <literal>source</literal> (IvV - ftHgÍ <literal>vm</literal> ):
+ HibernateÍDZ©ç^CX^vÌlðæ¾·é׫ŵ天H
+ f[^x[X©çŵ天A»ÝÌJVM©çŵ天H
+ f[^x[XÉæé^CX^vÍAHibernateª"Ìl"ðè·é½ßÉ
+ f[^x[XðqbgµÈ¯êÎÈçÈ¢½ßAI[owbhðµ«Ü·B
+ µ©µNX^«ÅÍJVM©çæ¾·éæèÀSÅ·B
+ f[^x[XÌ»ÝÌ^CX^vÌæ¾ðT|[g·é
+ ·×ÄÌ <literal>f[^x[Xû¾</literal> ªmçêÄ¢éí¯ÅÍÈ¢±ÆÉ
+ ӵľ³¢BܽêûÅA¸§³ð½ßÉA
+ bNÅgp·éÉÍÀSÅÈ¢àÌà èÜ·(á¦ÎOracle 8)B
+ </para>
+ </callout>
+ <callout arearefs="timestamp6">
+ <para>
+ <literal>generated</literal> (IvV - ftHgÍ <literal>never</literal> ):
+ ±Ì^CX^vEvpeBÌlªAf[^x[XÉæÁĶ¬³êé±ÆðwèµÜ·B
+ <xref linkend="mapping-generated">¶¬vpeB</xref> ðQƵľ³¢B
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ <literal><timestamp></literal> Í
+ <literal><version type="timestamp"></literal> Æ¿Å é±ÆÉӵľ³¢B
+ <literal><timestamp source="db"></literal> Í
+ <literal><version type="dbtimestamp"></literal> Æ¿Å é±ÆÉӵľ³¢B
+ </para>
+ </sect2>
+
+ <sect2 id="mapping-declaration-property" revision="4">
+ <title>property</title>
+
+ <para>
+ <literal><property></literal> vfÍANXÌi±IÈJavaBeanX^CÌvpeBðè`µÜ·B
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="property1" coords="2 70"/>
+ <area id="property2" coords="3 70"/>
+ <area id="property3" coords="4 70"/>
+ <areaset id="property4-5" coords="">
+ <area id="property4" coords='5 70'/>
+ <area id="property5" coords='6 70'/>
+ </areaset>
+ <area id="property6" coords="7 70"/>
+ <area id="property7" coords="8 70"/>
+ <area id="property8" coords="9 70"/>
+ <area id="property9" coords="10 70"/>
+ <area id="property10" coords="11 70"/>
+ <area id="property11" coords="12 70"/>
+ <area id="property12" coords="13 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<property
+ name="propertyName"
+ column="column_name"
+ type="typename"
+ update="true|false"
+ insert="true|false"
+ formula="arbitrary SQL expression"
+ access="field|property|ClassName"
+ lazy="true|false"
+ unique="true|false"
+ not-null="true|false"
+ optimistic-lock="true|false"
+ generated="never|insert|always"
+ node="element-name|@attribute-name|element/@attribute|."
+ index="index_name"
+ unique_key="unique_key_id"
+ length="L"
+ precision="P"
+ scale="S"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="property1">
+ <para>
+ <literal>name</literal>F¬¶ÅnÜévpeB¼B
+ </para>
+ </callout>
+ <callout arearefs="property2">
+ <para>
+ <literal>column</literal>iIvV - ftHgÍvpeB¼jF
+ }bsO³ê½f[^x[Xe[uÌJ̼OB
+ lXgµ½ <literal><column></literal> vfÅàwèÅ«Ü·B
+ </para>
+ </callout>
+ <callout arearefs="property3">
+ <para>
+ <literal>type</literal>iIvVjFHibernateÌ^𦷼OB
+ </para>
+ </callout>
+ <callout arearefs="property4-5">
+ <para>
+ <literal>update, insert</literal>
+ iIvV - ftHgÍ <literal>true</literal> jF
+ }bsO³ê½JªSQLÌ
+ <literal>UPDATE</literal> â <literal>INSERT</literal> ÉÜÜêé±ÆðwèµÜ·B
+ ¼ûÆà <literal>false</literal> ÉÝè·éÆA
+ ¯¶JÉ}bsO³ê½¼ÌvpeBâgKâ
+ ¼ÌAvP[VÉæÁÄú»³ê½Èu±ovvpeBªÂ\ÉÈèÜ·B
+ </para>
+ </callout>
+ <callout arearefs="property6">
+ <para>
+ <literal>formula</literal>iIvVjF
+ <emphasis>vZ</emphasis> vpeB̽ßÌlðè`·éSQL®B
+ vZ³ê½vpeBÍ©gÌJÖÌ}bsOª èܹñB
+ </para>
+ </callout>
+ <callout arearefs="property7">
+ <para>
+ <literal>access</literal>iIvV - ftHgÍ <literal>property</literal> jF
+ vpeBÌlÖÌANZXÉHibernateªg¤íªB
+ </para>
+ </callout>
+ <callout arearefs="property8">
+ <para>
+ <literal>lazy</literal> (optional - ftHgÍ <literal>false</literal> ):
+ CX^XÏÉÅÉANZXµ½Æ«ÉAvpeBðxµÄæ¾·éæ¤wèµÜ·B
+ (oCgR[hÀð쬷éÔªKvÉÈèÜ·)B
+ </para>
+ </callout>
+ <callout arearefs="property9">
+ <para>
+ <literal>unique</literal> (IvV):JÉj[N§ñð¯éDDL̶¬ðÂ\ɵܷB
+ ܽA<literal>property-ref</literal> Ì^[QbgÆ·é±ÆàÅ«Ü·B
+ </para>
+ </callout>
+ <callout arearefs="property10">
+ <para>
+ <literal>not-null</literal> (IvV):JÉnulllð·éDDL̶¬ðÂ\ɵܷB
+ </para>
+ </callout>
+ <callout arearefs="property11">
+ <para>
+ <literal>optimistic-lock</literal> (IvV - ftHgÍ <literal>true</literal> ):
+ ±ÌvpeBÌXVÉyÏbNÌæ¾ðv·é©Ç¤©ðwèµÜ·B
+ ¾¢·¦êÎA±ÌvpeBª_[eBÅ éÆ«Éo[Wðâ·×«©ðèµÜ·B
+ </para>
+ </callout>
+ <callout arearefs="property12">
+ <para>
+ <literal>generated</literal> (IvV - ftHgÍ <literal>never</literal> ):
+ vpeBÌlªAf[^x[XÉæÁĶ¬³ê½±ÆðwèµÜ·B
+ <xref linkend="mapping-generated">¶¬vpeB</xref> ðQƵľ³¢B
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ <emphasis>typename</emphasis> ÉÍȺÌlªÂ\Å·F
+ </para>
+
+ <orderedlist spacing="compact">
+ <listitem>
+ <para>
+ HibernateÌî{^̼Oiá <literal>integer, string, character,
+ date, timestamp, float, binary, serializable, object, blob</literal> jB
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ftHgÌî{^ÌJavaNX¼ iá <literal>int, float,
+ char, java.lang.String, java.util.Date, java.lang.Integer, java.sql.Clob</literal> jB
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ VACYÂ\ÈJavaNX̼OB
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ JX^^ÌNX¼iá <literal>com.illflow.type.MyCustomType</literal> jB
+ </para>
+ </listitem>
+ </orderedlist>
+
+ <para>
+ ^ðwèµÈ¯êÎAHibernateͳµ¢HibernateÌ^ðª·é½ßÉA
+ wè³ê½vpeBÉεÄtNVðg¢Ü·B
+ HibernateÍ[2, 3, 4ð»ÌÉg¢A
+ gettervpeBÌÔèlÌNX̼Oððßµæ¤ÆµÜ·B
+ µ©µ±êÅíÉ\ªÅ éÆÍÀèܹñB
+ êÉæÁÄÍA<literal>type</literal> ®«ªKvÈêª èÜ·B
+ iá¦Î <literal>Hibernate.DATE</literal> Æ <literal>Hibernate.TIMESTAMP</literal> ðæÊ·é½ßA
+ ܽÍJX^^ðwè·é½ßÈÇÅ·Bj
+ </para>
+
+ <para>
+ <literal>access</literal> ®«ÅA
+ ÀsÉHibernateªÇÌæ¤ÉvpeBÉANZX·é©ð§äÅ«Ü·B
+ ftHgÅÍHibernateÍvpeBÌget/setÌyAðR[µÜ·B
+ <literal>access="field"</literal> Æwè·êÎA
+ HibernateÍtNVðg¢get/setÌyAðÉA¼ÚtB[hÉANZXµÜ·B
+ C^[tFCX <literal>org.hibernate.property.PropertyAccessor</literal> ð
+ À·éNXðwè·é±ÆÅAvpeBÖÌANZXÉÆ©Ìíªðwè·é±ÆªÅ«Ü·B
+ </para>
+
+ <para>
+ ÁÉÍÈÁ¥Í¶¬vpeBÅ·B
+ ±êçÌvpeBÍRÇÝæèêpÅ èAvpeBÌlÍ[hÉvZ³êÜ·B
+ vZðSQL®ÆµÄé¾·éÆA±ÌvpeBÍ
+ CX^Xð[h·éSQLNGÌ <literal>SELECT</literal> åÌTuNGÉÏ·³êÜ·B
+ </para>
+
+ <programlisting><![CDATA[
+<property name="totalPrice"
+ formula="( SELECT SUM (li.quantity*p.price) FROM LineItem li, Product p
+ WHERE li.productId = p.productId
+ AND li.customerId = customerId
+ AND li.orderNumber = orderNumber )"/>]]></programlisting>
+
+ <para>
+ ÁèÌJ(áÅÍ <literal>customerId</literal> ª»êÉ ½èÜ·)ÌGCAXðé¾·é±ÆÈA
+ GeBeB©gÌe[uðQÆÅ«é±ÆÉӵľ³¢B
+ ൮«ðgpµ½È¯êÎA
+ lXgµ½ <literal><formula></literal> }bsOvfðg¦é±ÆÉàӵľ³¢B
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-manytoone" revision="5">
+ <title>many-to-one</title>
+
+ <para>
+ ¼Ìi±NXÖÌÊíÌÖAÍ <literal>many-to-one</literal> vfðgÁÄè`µÜ·B
+ [VifͽÎêÖAÅ·B
+ ÂÜè ée[uÌOL[ÍA^[QbgÆÈée[uÌåL[JðQƵĢܷB
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="manytoone1" coords="2 70"/>
+ <area id="manytoone2" coords="3 70"/>
+ <area id="manytoone3" coords="4 70"/>
+ <area id="manytoone4" coords="5 70"/>
+ <area id="manytoone5" coords="6 70"/>
+ <areaset id="manytoone6-7" coords="">
+ <area id="manytoone6" coords='7 70'/>
+ <area id="manytoone7" coords='8 70'/>
+ </areaset>
+ <area id="manytoone8" coords="9 70"/>
+ <area id="manytoone9" coords="10 70"/>
+ <area id="manytoone10" coords="11 70"/>
+ <area id="manytoone11" coords="12 70"/>
+ <area id="manytoone12" coords="13 70"/>
+ <area id="manytoone13" coords="14 70"/>
+ <area id="manytoone14" coords="15 70"/>
+ <area id="manytoone15" coords="16 70"/>
+ <area id="manytoone16" coords="17 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<many-to-one
+ name="propertyName"
+ column="column_name"
+ class="ClassName"
+ cascade="cascade_style"
+ fetch="join|select"
+ update="true|false"
+ insert="true|false"
+ property-ref="propertyNameFromAssociatedClass"
+ access="field|property|ClassName"
+ unique="true|false"
+ not-null="true|false"
+ optimistic-lock="true|false"
+ lazy="proxy|no-proxy|false"
+ not-found="ignore|exception"
+ entity-name="EntityName"
+ formula="arbitrary SQL expression"
+ node="element-name|@attribute-name|element/@attribute|."
+ embed-xml="true|false"
+ index="index_name"
+ unique_key="unique_key_id"
+ foreign-key="foreign_key_name"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="manytoone1">
+ <para>
+ <literal>name</literal>FvpeB¼B
+ </para>
+ </callout>
+ <callout arearefs="manytoone2">
+ <para>
+ <literal>column</literal> (IvV):OL[J̼OB
+ lXgµ½ <literal><column></literal> vfÅàwèÅ«Ü·B
+ </para>
+ </callout>
+ <callout arearefs="manytoone3">
+ <para>
+ <literal>class</literal>iIvV - ftHgÍA
+ tNVÉæèè³êévpeBÌ^jFÖANX̼OB
+ </para>
+ </callout>
+ <callout arearefs="manytoone4">
+ <para>
+ <literal>cascade</literal>iIvVjF
+ ÇÌìðAeIuWFNg©çÖAIuWFNgÖÆJXP[h³¹é©ðwèµÜ·B
+ </para>
+ </callout>
+ <callout arearefs="manytoone5">
+ <para>
+ <literal>fetch</literal> (IvV - ftHgÍ <literal>select</literal> ):
+ OtFb`©IðtFb`isequential select fetchjðIðµÜ·B
+ </para>
+ </callout>
+ <callout arearefs="manytoone6-7">
+ <para>
+ <literal>update, insert</literal>iIvV - ftHgÍ <literal>true</literal> jF
+ }bsO³ê½JªSQLÌ <literal>UPDATE</literal> ܽÍ
+ <literal>INSERT</literal> ¶ÉÜÜêé±ÆðwèµÜ·B
+ ¼ûÆà <literal>false</literal> ÉÝè·éÆA
+ »Ìlª¯¶JÉ}bsO³ê½¼ÌvpeBâgKâ
+ ¼ÌAvP[VÉæÁÄú»³ê½Èu±ovvpeBªÂ\ÉÈèÜ·B
+ </para>
+ </callout>
+ <callout arearefs="manytoone8">
+ <para>
+ <literal>property-ref</literal>iIvVjF
+ ±ÌOL[ɳê½ÖANXÌvpeB¼B
+ ½àwèµÈ¯êÎAÖANXÌåL[ªgíêÜ·B
+ </para>
+ </callout>
+ <callout arearefs="manytoone9">
+ <para>
+ <literal>access</literal>iIvV - ftHgÍ <literal>property</literal> jF
+ vpeBÌlÖÌANZXÉHibernateªg¤íªB
+ </para>
+ </callout>
+ <callout arearefs="manytoone10">
+ <para>
+ <literal>unique</literal>iIvVjF
+ OL[JÉεÄj[N§ñð¯½DDL̶¬ðÂ\ɵܷB
+ ܽA<literal>property-ref</literal> Ì^[QbgÉ·é±ÆàÅ«Ü·B
+ ±êÉæèÖA̽dxðøÊIÉêÎêɵܷB
+ </para>
+ </callout>
+ <callout arearefs="manytoone11">
+ <para>
+ <literal>not-null</literal> (IvV):OL[JÉεÄA
+ nulllð·éDDL̶¬ðÂ\ɵܷ
+ </para>
+ </callout>
+ <callout arearefs="manytoone12">
+ <para>
+ <literal>optimistic-lock</literal> (IvV - ftHgÍ <literal>true</literal> ):
+ ±ÌvpeBÌXVÉyÏIbNÌæ¾ðv·é©Ç¤©ðwèµÜ·B
+ ¾¢·¦êÎA±ÌvpeBª_[eBÅ éÆ«Éo[Wðâ·×«©ðèµÜ·B
+ </para>
+ </callout>
+ <callout arearefs="manytoone13">
+ <para>
+ <literal>lazy</literal> (IvV - ftHgÍ <literal>proxy</literal> ):
+ ftHgÅÍA½dxPÌÖAªvLVÆÈèÜ·B
+ <literal>lazy="no-proxy"</literal> ÍACX^XÏÉÅÉANZXµ½Æ«ÉA
+ vpeBðxtFb`·éæ¤wèµÜ·
+ (rhÉoCgR[hÀªKvÉÈèÜ·)B
+ <literal>lazy="false"</literal> ÍÖAðíɦÉtFb`·éæ¤wèµÜ·B
+ </para>
+ </callout>
+ <callout arearefs="manytoone14">
+ <para>
+ <literal>not-found</literal> (IvV - ftHgÍ <literal>exception</literal> ):
+ µ½sðQÆ·éOL[ðÇÌæ¤Éµ¤©ðwèµÜ·B
+ <literal>ignore</literal> ͵½sðnullÖAƵĵ¢Ü·B
+ </para>
+ </callout>
+ <callout arearefs="manytoone15">
+ <para>
+ <literal>entity-name</literal> (IvV):ÖAµ½NXÌGeBeB¼B
+ </para>
+ </callout>
+ </calloutlist>
+ <callout arearefs="manytoone16">
+ <para>
+ <literal>formula</literal> (IvV):
+ <emphasis> vZ³ê½</emphasis> OL[ÉεÄlðè`·éSQL®
+ </para>
+ </callout>
+ </programlistingco>
+
+ <para>
+ <literal>cascade</literal> ®«É <literal>none</literal> ÈOÌÓ¡Ì élððÝè·éÆA
+ ÖAIuWFNgÖ éìª`d·é±ÆÉÈèÜ·B
+ Ó¡Ì élÆÍHibernateÌî{ì̼O̱ÆÅA
+ <literal>delete-orphan</literal> Æ <literal>all</literal> Aì¼ðJ}ÅæØÁ½gÝí¹
+ iá¦Î <literal>cascade="persist,merge,evict"</literal> â
+ <literal>cascade="all,delete-orphan"</literal>jA
+ ܽ»ê¾¯ÅÈ <literal>persist, merge, delete, save-update, evict, replicate, lock,
+ refresh</literal> ̱ÆðwµÜ·B
+ Úµ¢à¾Í <xref linkend="objectstate-transitive"/> ð©Ä¾³¢B
+ lªêÂÌÖA(many-to-oneÆone-to-oneÖA)ÍA
+ PÆÅÌíiorphan delete)ðT|[gµÄ¢È¢±ÆÉӵľ³¢B
+ </para>
+
+ <para>
+ T^IÈ <literal>many-to-one</literal> é¾ÍÌæ¤ÉVvÅ·BF
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="product" class="Product" column="PRODUCT_ID"/>]]></programlisting>
+
+ <para>
+ <literal>property-ref</literal> ®«ÍAOL[ªÖAt¯çê½e[uÌAåL[ÅÈ¢
+ j[NL[ðQƵĢéKV[f[^ð}bv·é½ßɾ¯g¤×«Å·B
+ ±êÍX¢[VifÅ·B
+ á¦Î <literal>Product</literal> NXªA
+ åL[ÅÈ¢j[NÈVAio[ðÁÄ¢éƼèµÄÝľ³¢B
+ i <literal>unique</literal> ®«ÍSchemaExportc[ðgÁ½HibernateÌDDL¶¬ð§äµÜ·Bj
+ </para>
+
+ <programlisting><![CDATA[<property name="serialNumber" unique="true" type="string" column="SERIAL_NUMBER"/>]]></programlisting>
+
+ <para>
+ ȺÌæ¤É <literal>OrderItem</literal> ÉεÄ}bsOðg¦Ü·F
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="product" property-ref="serialNumber" column="PRODUCT_SERIAL_NUMBER"/>]]></programlisting>
+
+ <para>
+ µ©µA±ê͵ħūܹñB
+ </para>
+
+ <para>
+ QƵ½j[NL[ªAÖA·éGeBeB̽ÌvpeB©ç\¬³êéêA
+ wèµ½ <literal><properties></literal> vfàÅAQÆ·évpeBð}bsO·é׫ŷB
+ </para>
+
+ <para>
+ àµQƵ½j[NL[ªR|[lgÌvpeBÅ éêÍAvpeBÌpXðwèÅ«Ü·B
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="owner" property-ref="identity.ssn" column="OWNER_SSN"/>]]></programlisting>
+
+
+ </sect2>
+
+
+ <sect2 id="mapping-declaration-onetoone" revision="3">
+ <title>one-to-one</title>
+
+ <para>
+ ¼Ìi±NXÖÌêÎêÖAÍA<literal>one-to-one</literal> vfÅè`µÜ·B
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="onetoone1" coords="2 70"/>
+ <area id="onetoone2" coords="3 70"/>
+ <area id="onetoone3" coords="4 70"/>
+ <area id="onetoone4" coords="5 70"/>
+ <area id="onetoone5" coords="6 70"/>
+ <area id="onetoone6" coords="7 70"/>
+ <area id="onetoone7" coords="8 70"/>
+ <area id="onetoone8" coords="9 70"/>
+ <area id="onetoone9" coords="10 70"/>
+ <area id="onetoone10" coords="11 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<one-to-one
+ name="propertyName"
+ class="ClassName"
+ cascade="cascade_style"
+ constrained="true|false"
+ fetch="join|select"
+ property-ref="propertyNameFromAssociatedClass"
+ access="field|property|ClassName"
+ formula="any SQL expression"
+ lazy="proxy|no-proxy|false"
+ entity-name="EntityName"
+ node="element-name|@attribute-name|element/@attribute|."
+ embed-xml="true|false"
+ foreign-key="foreign_key_name"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="onetoone1">
+ <para>
+ <literal>name</literal>FvpeB¼B
+ </para>
+ </callout>
+ <callout arearefs="onetoone2">
+ <para>
+ <literal>class</literal>iIvV -
+ ftHgÍtNVÉæèè³êévpeBÌ^jF
+ ÖANX̼OB
+ </para>
+ </callout>
+ <callout arearefs="onetoone3">
+ <para>
+ <literal>cascade</literal>iIvVjF
+ eIuWFNg©çÖAIuWFNgÖAÇÌìðJXP[h·é©ðwèµÜ·B
+ </para>
+ </callout>
+ <callout arearefs="onetoone4">
+ <para>
+ <literal>constrained</literal>iIvVjF
+ }bsO³ê½e[uÌåL[ÉηéOL[§ñªA
+ ÖANXÌe[uðQÆ·é±ÆðwèµÜ·B
+ ±ÌIvVÍ <literal>save()</literal> Æ
+ <literal>delete()</literal> ªJXP[h³êéÉe¿µA
+ »µÄÖAªvLV³êé©Ç¤©Éàe¿µÜ·
+ i»µÄXL[}GNX|[gc[ÉàgíêÜ·jB
+ </para>
+ </callout>
+ <callout arearefs="onetoone5">
+ <para>
+ <literal>fetch</literal>iIvV - ftHgÍ <literal>select</literal> jF
+ OtFb`ÆIðtFb`isequential select fetchjÌÇ¿ç©ðIðµÜ·B
+ </para>
+ </callout>
+ <callout arearefs="onetoone6">
+ <para>
+ <literal>property-ref</literal>iIvVjF
+ ±ÌNXÌåL[ɳê½ÖANXÌvpeB¼B
+ wè³êȯêÎAÖANXÌåL[ªgíêÜ·B
+ </para>
+ </callout>
+ <callout arearefs="onetoone7">
+ <para>
+ <literal>access</literal>iIvV - ftHgÍ <literal>property</literal> jF
+ vpeBÌlÖÌANZXÉHibernateªg¤íªB
+ </para>
+ </callout>
+ <callout arearefs="onetoone8">
+ <para>
+ <literal>formula</literal> (IvV):
+ ÙÆñÇ·×ÄÌêÎêÖAÍI[i[ÌGeBeBÌåL[ÖÆ}bsO³êÜ·B
+ ±êÈOÌHÈêÍA
+ ¼ÌJâA¡ÌJASQL\¶ðgÁ½·é½ßÌ®ðwèÅ«Ü·B
+ iáÍ <literal>org.hibernate.test.onetooneformula</literal> ðQƵľ³¢Bj
+ </para>
+ </callout>
+ <callout arearefs="onetoone9">
+ <para>
+ <literal>lazy</literal> (IvV - ftHgÍ <literal>proxy</literal> ):
+ ftHgÅÍA½dxPÌÖAªvLVÆÈèÜ·B
+ <literal>lazy="no-proxy"</literal> ÍACX^XÏÉÅÉANZXµ½Æ«ÉA
+ vpeBðxtFb`·éæ¤wèµÜ·
+ (rhÉoCgR[hÀªKvÉÈèÜ·)B
+ <literal>lazy="false"</literal> ÍÖAðíɦÉtFb`·éæ¤wèµÜ·B
+ <emphasis>ൠ<literal>constrained="false"</literal> ÈçÎA
+ vLVÍgpsÂ\ÆÈèAÖAð¦ÉtFb`·é±ÆÉӵľ³¢I</emphasis>
+
+ </para>
+ </callout>
+ <callout arearefs="onetoone10">
+ <para>
+ <literal>entity-name</literal> (IvV):ÖANXÌGeBeB¼
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ êÎêÖAÉÍ2íÞ èÜ·F
+ </para>
+ <itemizedlist>
+ <listitem><para>
+ åL[ÖA
+ </para></listitem>
+ <listitem><para>
+ j[NOL[ÖA
+ </para></listitem>
+ </itemizedlist>
+
+ <para>
+ åL[ÖAÉÍAÁÊÈe[uJÍKv èܹñB
+ àµ2ÂÌsªÖAÉæèÖWµÄ¢êÎA2ÂÌe[uͯ¶åL[Ìlð¤LµÜ·B
+ »Ì½ß2ÂÌIuWFNgðåL[ÖAÉæÁÄÖAt¯½¢ÌÅ êÎA
+ mÀɯ¶¯ÊqÌlðãüµÈ¯êÎÈèܹñB
+ </para>
+
+ <para>
+ åL[ÖAðs¤½ßÉÍAȺÌ}bsOð <literal>Employee</literal> Æ
+ <literal>Person</literal> Ì»ê¼êÉÇÁµÄ¾³¢B
+ </para>
+
+ <programlisting><![CDATA[<one-to-one name="person" class="Person"/>]]></programlisting>
+ <programlisting><![CDATA[<one-to-one name="employee" class="Employee" constrained="true"/>]]></programlisting>
+
+ <para>
+ ±±ÅAPERSONÆEMPLOYEEe[uÌÖW·ésÌåL[ª¯¶Å é±ÆðmÀɵȯê΢¯Ü¹ñB
+ ±±ÅÍA<literal>foreign</literal> Æ¢¤ÁêÈHibernate¯Êq¶¬íªðg¢Ü·F
+ </para>
+
+ <programlisting><![CDATA[<class name="person" table="PERSON">
+ <id name="id" column="PERSON_ID">
+ <generator class="foreign">
+ <param name="property">employee</param>
+ </generator>
+ </id>
+ ...
+ <one-to-one name="employee"
+ class="Employee"
+ constrained="true"/>
+</class>]]></programlisting>
+
+ <para>
+ <literal>Employee</literal> CX^XªA<literal>Person</literal> Ì
+ <literal>employee</literal> vpeBÅQƳêéæ¤ÉA
+ VµZ[u³ê½ <literal>Person</literal> ÌCX^XÉͯ¶åL[Ìlªãü³êÜ·B
+
+ VµZ[u·é <literal>Person</literal> CX^XÍA
+ »Ì <literal>Person</literal> Ì <literal>employee</literal> vpeBªQÆ·é
+ <literal>Employee</literal> CX^XƵį¶åL[ªèÄçêÜ·B
+ </para>
+
+ <para>
+ à¤1ÂÌû@ƵÄA<literal>Employee</literal> ©ç <literal>Person</literal> ÖÌ
+ j[N§ñðgÁ½OL[ÖAÍȺÌæ¤É\»³êÜ·F
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="person" class="Person" column="PERSON_ID" unique="true"/>]]></programlisting>
+
+ <para>
+ »µÄ±ÌÖAÍA
+ ȺÌLqð <literal>Person</literal> Ì}bsOÉÇÁ·é±ÆÅoûüÉ·é±ÆªÅ«Ü·F
+ </para>
+
+ <programlisting><![CDATA[<one-to-one name"employee" class="Employee" property-ref="person"/>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-naturalid">
+ <title>natural-id</title>
+
+ <programlisting><![CDATA[<natural-id mutable="true|false"/>
+ <property ... />
+ <many-to-one ... />
+ ......
+</natural-id>]]></programlisting>
+
+ <para>
+ åL[ƵÄãL[Ìgpð§µÜ·ªA
+ ·×ÄÌGeBeBÉεĩRL[ð¯Ê·éæ¤É·×«Å·B
+ ©RL[Íj[N©ÂñnullÈêÂÌvpeBAܽÍvpeBÌAÅ·B
+ sÏÅ êγçÉǢŷB
+ <literal><natural-id></literal> vfàÅ©RL[ÌvpeBð}bsOµÜ·B
+ HibernateÍKRIÉj[N©Ânulllð·é§ñ𶬵A
+ ±¤µÄ}bsOÍæè©ÈLqIÉÈèÜ·B
+ </para>
+
+ <para>
+ GeBeBÌ©RL[vpeBÌärÉÍA
+ <literal>equals()</literal> Æ <literal>hashCode()</literal> ÌÀð¨©ßµÜ·B
+ </para>
+
+ <para>
+ ±Ì}bsOÍ©RåL[ðgÁ½GeBeBÅÌgpðÓ}µÄ¢Ü¹ñB
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>mutable</literal> (IvV, ftHgÍ <literal>false</literal> ):
+ ftHgÅÍA©R¯ÊqvpeBÍsÏ(è)Æzè³êĢܷB
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-component" revision="2">
+ <title>component, dynamic-component</title>
+
+ <para>
+ <literal><component></literal> vfÍA
+ qIuWFNgÌvpeBðeNXÌe[uÌJÖ}bsOµÜ·B
+ R|[lgÍ©ªÌvpeBAR|[lgARNVÌÉè`Å«Ü·B
+ ȺÌuR|[lgvð©Ä¾³¢B
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="component1" coords="2 45"/>
+ <area id="component2" coords="3 45"/>
+ <area id="component3" coords="4 45"/>
+ <area id="component4" coords="5 45"/>
+ <area id="component5" coords="6 45"/>
+ <area id="component6" coords="7 45"/>
+ <area id="component7" coords="8 45"/>
+ <area id="component8" coords="9 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<component
+ name="propertyName"
+ class="className"
+ insert="true|false"
+ update="true|false"
+ access="field|property|ClassName"
+ lazy="true|false"
+ optimistic-lock="true|false"
+ unique="true|false"
+ node="element-name|."
+>
+
+ <property ...../>
+ <many-to-one .... />
+ ........
+</component>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="component1">
+ <para>
+ <literal>name</literal>FvpeB¼B
+ </para>
+ </callout>
+ <callout arearefs="component2">
+ <para>
+ <literal>class</literal>
+ iIvV - ftHgÍtNVÉæèè³êévpeBÌ^jF
+ R|[lgiqjNX̼OB
+ </para>
+ </callout>
+ <callout arearefs="component3">
+ <para>
+ <literal>insert</literal> F}bsO³ê½JªSQLÌ
+ <literal>INSERT</literal> É»êéæ¤É·éǤ©ðwèµÜ·B
+ </para>
+ </callout>
+ <callout arearefs="component4">
+ <para>
+ <literal>update</literal> : }bsO³ê½JªSQL Ì
+ <literal>UPDATE</literal> É»êéæ¤É·é©Ç¤©ðwèµÜ·B
+ </para>
+ </callout>
+ <callout arearefs="component5">
+ <para>
+ <literal>access</literal> iIvV - ftHgÍ <literal>property</literal> jF
+ vpeBÌlÖÌANZXÉHibernateªg¤íªB
+ </para>
+ </callout>
+ <callout arearefs="component6">
+ <para>
+ <literal>lazy</literal> (optional - ftHgÍ <literal>false</literal> ):
+ CX^XÏÉÅÉANZXµ½Æ«ÉA
+ R|[lgðxµÄtFb`·éæ¤wèµÜ·B
+ (oCgR[hÀð쬷éÔªKvÉÈèÜ·)
+ </para>
+ </callout>
+ <callout arearefs="component7">
+ <para>
+ <literal>optimistic-lock</literal> (IvV - ftHgÍ <literal>true</literal> ):
+ ±ÌvpeBÌXVÉAyÏbNÌæ¾ðv·é©Ç¤©ðwèµÜ·B
+ ¾¢·¦êÎA±ÌvpeBª_[eBÅ éÆ«Éo[Wðâ·×«©ðèµÜ·B
+ </para>
+ </callout>
+ <callout arearefs="component8">
+ <para>
+ <literal>unique</literal> (IvV - ftHgÍ <literal>false</literal> ):
+ R|[lgÌ·×ÄÌ}bsO·éJÉAj[N§ñª¶Ý·é©ðwèµÜ·B
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ qÌ <literal><property></literal> ^OÅA
+ qÌNXÌvpeBðe[uJÉ}bsOµÜ·B
+ </para>
+
+ <para>
+ <literal><component></literal> vfÍAeGeBeBÖßéQÆƵÄA
+ R|[lgÌNXÌvpeBð}bsO·é <literal><parent></literal> TuvfðµܷB
+ </para>
+
+ <para>
+ <literal><dynamic-component></literal> vfÍA
+ <literal>Map</literal> ªR|[lgƵÄ}bsO³êé±ÆðÂ\ɵܷB
+ vpeB¼ÍmapÌL[ðQƵܷB<xref linkend="components-dynamic"/> ðQƵľ³¢B
+
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-properties" revision="2">
+ <title>properties</title>
+
+ <para>
+ <literal><properties></literal> vfÍNXÌvpeBÌwè³ê½A
+ _IÈO[sOðÂ\ɵܷB
+ ±Ì\¢ÌÅàdvÈgpû@ÍA
+ <literal>property-ref</literal> Ì^[QbgÉÈévpeBÌð·é±ÆÅ·B
+ »êÍܽA¡JÌj[N§ñðè`·éÈPÈû@Åà èÜ·B
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="properties1" coords="2 45"/>
+ <area id="properties2" coords="3 45"/>
+ <area id="properties3" coords="4 45"/>
+ <area id="properties4" coords="5 45"/>
+ <area id="properties5" coords="6 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<properties
+ name="logicalName"
+ insert="true|false"
+ update="true|false"
+ optimistic-lock="true|false"
+ unique="true|false"
+>
+
+ <property ...../>
+ <many-to-one .... />
+ ........
+</properties>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="properties1">
+ <para>
+ <literal>name</literal> : O[sOÌ_¼B
+ ÀÛÌvpeB¼ÅÍ <emphasis> èܹñ</emphasis> B
+ </para>
+ </callout>
+ <callout arearefs="properties2">
+ <para>
+ <literal>insert</literal>F}bsO³ê½JªSQLÌ
+ <literal>INSERT</literal> É»êéæ¤É·é©Ç¤©ðwèµÜ·B
+ </para>
+ </callout>
+ <callout arearefs="properties3">
+ <para>
+ <literal>update</literal>F}bsO³ê½JªSQLÌ
+ <literal>UPDATE</literal> É»êéæ¤É·é©Ç¤©ðwèµÜ·B
+ </para>
+ </callout>
+ <callout arearefs="properties4">
+ <para>
+ <literal>optimistic-lock</literal> (IvV - ftHgÍ <literal>true</literal> ):
+ ±êçÌvpeBÌXVÉyÏIbNÌæ¾ðv·é©Ç¤©ðwèµÜ·B
+ ¾¢·¦êÎA±ÌvpeBª_[eBÅ éÆ«Éo[Wðâ·×«©ðèµÜ·B
+ </para>
+ </callout>
+ <callout arearefs="properties5">
+ <para>
+ <literal>unique</literal> (IvV - ftHgÍ <literal>false</literal> ):
+ R|[lgÌ·×ÄÌ}bsO·éJÉAj[N§ñª¶Ý·é©ðwèµÜ·B
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ á¦ÎAàµÈºÌæ¤È <literal><properties></literal> }bsOª Á½êF
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="personNumber"/>
+ ...
+ <properties name="name"
+ unique="true" update="false">
+ <property name="firstName"/>
+ <property name="initial"/>
+ <property name="lastName"/>
+ </properties>
+</class>]]></programlisting>
+
+ <para>
+ åL[ÌãíèÉ <literal>Person</literal> e[uÌj[NL[ÖÌQÆðÂA
+ KV[f[^ÌÖAð©àµêܹñBF
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="person"
+ class="Person" property-ref="name">
+ <column name="firstName"/>
+ <column name="initial"/>
+ <column name="lastName"/>
+</many-to-one>]]></programlisting>
+
+ <para>
+ µ©µA±Ìæ¤ÈKV[f[^}bsOÌReLXgOÖÌgpͧµÜ¹ñB
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-subclass" revision="4">
+ <title>subclass</title>
+
+ <para>
+ ÅãÉ|[tBbNÈi±»ÉÍA[gÌi±NXÌeTuNXÌè`ªKvÅ·B
+ table-per-class-hierarchy}bsOíªÅÍA
+ <literal><subclass></literal> è`ªgíêÜ·B
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="subclass1" coords="2 55"/>
+ <area id="subclass2" coords="3 55"/>
+ <area id="subclass3" coords="4 55"/>
+ <area id="subclass4" coords="5 55"/>
+ </areaspec>
+ <programlisting><![CDATA[<subclass
+ name="ClassName"
+ discriminator-value="discriminator_value"
+ proxy="ProxyInterface"
+ lazy="true|false"
+ dynamic-update="true|false"
+ dynamic-insert="true|false"
+ entity-name="EntityName"
+ node="element-name"
+ extends="SuperclassName">
+
+ <property .... />
+ .....
+</subclass>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="subclass1">
+ <para>
+ <literal>name</literal>FTuNXÌ®SCü³ê½NX¼B
+ </para>
+ </callout>
+ <callout arearefs="subclass2">
+ <para>
+ <literal>discriminator-value</literal>iIvV - ftHgÍNX¼jF
+ ÂXÌTuNXðæÊ·é½ßÌlB
+ </para>
+ </callout>
+ <callout arearefs="subclass3">
+ <para>
+ <literal>proxy</literal>iIvVjF
+ xú»vLVÉg¤NXâC^[tFCXðwèµÜ·B
+ </para>
+ </callout>
+ <callout arearefs="subclass4">
+ <para>
+ <literal>lazy</literal>iIvV, ftHgÍ <literal>true</literal> jF
+ <literal>lazy="false"</literal> ÆÝè·éÆAxtFb`ªgpūܹñB
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ eTuNXÅÍAi±vpeBÆTuNXðè`µÜ·B
+ <literal><version></literal> Æ <literal><id></literal> vpeBÍA
+ [gNX©çp³³êéƼè³êÜ·B
+ Kw\¢É¨¯éTuNXÍA
+ j[NÈ <literal>discriminator-value</literal> ðè`µÈ¯êÎÈèܹñB
+ noneªwè³êéÆA®SCü³ê½JavaNX¼ªgíêÜ·B
+ </para>
+
+<!--
+ <para>
+ It is possible to define <literal>subclass</literal>, <literal>union-subclass</literal>,
+ and <literal>joined-subclass</literal> mappings in separate mapping documents, directly beneath
+ <literal>hibernate-mapping</literal>. This allows you to extend a class hierachy just by adding
+ a new mapping file. You must specify an <literal>extends</literal> attribute in the subclass mapping,
+ naming a previously mapped superclass. Note: Previously this feature made the ordering of the mapping
+ documents important. Since Hibernate3, the ordering of mapping files does not matter when using the
+ extends keyword. The ordering inside a single mapping file still needs to be defined as superclasses
+ before subclasses.
+
+ <literal>subclass</literal> A<literal>union-subclass</literal> A
+ <literal>joined-subclass</literal> }bsOð
+ <literal>hibernate-mapping</literal> ¼ºÌÊÌ}bsOhL
gàÅé¾Å«Ü·B
+ ±êÉæèAVµ¢}bsOt@CðÇÁ·é¾¯ÅNXKwðg£·é±ÆªÅ«Ü·B
+ 究ß}bsO·éX[p[NXðwèµ½TuNX}bsOàÅA
+ <literal>extends</literal> ®«ðwèµÈ¯êÎÈèܹñB
+ ÓF±ÌÁ«Í}bsOhL
gÌÔt¯ªdvÉÈèÜ·B
+ Hibernate3ÅÍAg£L[[hðg¤Æ«ÉÍA}bsOt@CÌt¯ÍâèÉÈèܹñB
+ êÂÌ}bsOt@CàÅÌt¯Í¢¾ÉATuNXÌOÉX[p[NXƵÄè`·éKvª èÜ·B
+ </para>
+
+
+ <programlisting><![CDATA[
+<hibernate-mapping>
+ <subclass name="DomesticCat" extends="Cat" discriminator-value="D">
+ <property name="name" type="string"/>
+ </subclass>
+</hibernate-mapping>]]></programlisting>
+-->
+
+ <para>
+ p³Ì}bsOÉÖ·éîñÍ <xref linkend="inheritance"/> ð©Ä¾³¢B
+ </para>
+
+ </sect2>
+
+
+ <sect2 id="mapping-declaration-joinedsubclass" revision="3">
+ <title>joined-subclass</title>
+
+ <para>
+ à¤1ÂÌû@ƵÄAeTuNXð©gÌe[uÖ}bsO·é±ÆªÅ«Ü·
+ (table-per-subclass mapping strategy)B
+ p³µ½óÔÍX[p[NXÌe[uðgÁ½ÅõµÜ·B
+ <literal><joined-subclass></literal> vfðgpµÜ·B
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="joinedsubclass1" coords="2 45"/>
+ <area id="joinedsubclass2" coords="3 45"/>
+ <area id="joinedsubclass3" coords="4 45"/>
+ <area id="joinedsubclass4" coords="5 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<joined-subclass
+ name="ClassName"
+ table="tablename"
+ proxy="ProxyInterface"
+ lazy="true|false"
+ dynamic-update="true|false"
+ dynamic-insert="true|false"
+ schema="schema"
+ catalog="catalog"
+ extends="SuperclassName"
+ persister="ClassName"
+ subselect="SQL expression"
+ entity-name="EntityName"
+ node="element-name">
+
+ <key .... >
+
+ <property .... />
+ .....
+</joined-subclass>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="joinedsubclass1">
+ <para>
+ <literal>name</literal>FTuNXÌ®SCü³ê½NX¼B
+ </para>
+ </callout>
+ <callout arearefs="joinedsubclass2">
+ <para>
+ <literal>table</literal> :TuNXe[u̼OB
+ </para>
+ </callout>
+ <callout arearefs="joinedsubclass3">
+ <para>
+ <literal>proxy</literal> (IvV):
+ xú»vLVÉgp·éNXâC^[tFCXðwèµÜ·B
+ </para>
+ </callout>
+ <callout arearefs="joinedsubclass4">
+ <para>
+ <literal>lazy</literal> (IvV, ftHgÍ <literal>true</literal> ):
+ <literal>lazy="false"</literal> Æ·éÆAxtFb`ªgpūܹñB
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ ±Ì}bsOíªÉÍA¯ÊJÍKv èܹñB
+ µ©µeTuNXÍ <literal><key></literal> vfðg¢A
+ IuWFNg¯ÊqðÛ·ée[uJðè`µÈ¯êÎÈèܹñB
+ ±ÌÍÌßÌ}bsOÍȺÌæ¤É«¼¹Ü·F
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="eg">
+
+ <class name="Cat" table="CATS">
+ <id name="id" column="uid" type="long">
+ <generator class="hilo"/>
+ </id>
+ <property name="birthdate" type="date"/>
+ <property name="color" not-null="true"/>
+ <property name="sex" not-null="true"/>
+ <property name="weight"/>
+ <many-to-one name="mate"/>
+ <set name="kittens">
+ <key column="MOTHER"/>
+ <one-to-many class="Cat"/>
+ </set>
+ <joined-subclass name="DomesticCat" table="DOMESTIC_CATS">
+ <key column="CAT"/>
+ <property name="name" type="string"/>
+ </joined-subclass>
+ </class>
+
+ <class name="eg.Dog">
+ <!-- ±±ÉDogÌ}bsOð«Ü· -->
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ p³Ì}bsOÉÖ·éîñÍ <xref linkend="inheritance"/> ð©Ä¾³¢B
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-unionsubclass" revision="2">
+ <title>union-subclass</title>
+
+ <para>
+ 3ÂÚÌIðÍAp³KwÌïÛNXÌÝðe[uÉ}bsO·é±ÆÅ·
+ (the table-per-concrete-classíª)B
+ »ê¼êÌe[uÍp³ÌóÔðÜß·×ÄÌNXÌi±óÔðè`µÜ·B
+ HibernateÅÍ»ÌlÈp³KwªK¸µàKvÅÍ èܹñB
+ PÉ»ê¼êÌNXðA
+ ÊXÌ <literal><class></literal> é¾ðgÁÄ}bsO·é±ÆªÅ«Ü·B
+ µ©µ|[tBbNÈÖA(á¦Î KwÌX[p[NXÖÌÖA)ðg¢½¢ÈçA
+ <literal><union-subclass></literal> }bsOðg¤Kvª èÜ·B
+
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="unionsubclass1" coords="2 45"/>
+ <area id="unionsubclass2" coords="3 45"/>
+ <area id="unionsubclass3" coords="4 45"/>
+ <area id="unionsubclass4" coords="5 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<union-subclass
+ name="ClassName"
+ table="tablename"
+ proxy="ProxyInterface"
+ lazy="true|false"
+ dynamic-update="true|false"
+ dynamic-insert="true|false"
+ schema="schema"
+ catalog="catalog"
+ extends="SuperclassName"
+ abstract="true|false"
+ persister="ClassName"
+ subselect="SQL expression"
+ entity-name="EntityName"
+ node="element-name">
+
+ <property .... />
+ .....
+</union-subclass>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="unionsubclass1">
+ <para>
+ <literal>name</literal>FTuNXÌ®SCü³ê½NX¼B
+ </para>
+ </callout>
+ <callout arearefs="unionsubclass2">
+ <para>
+ <literal>table</literal> :TuNXe[u̼OB
+ </para>
+ </callout>
+ <callout arearefs="unionsubclass3">
+ <para>
+ <literal>proxy</literal> (IvV):
+ xú»vLVÉgp·éNXâC^[tFCXðwèµÜ·B
+ </para>
+ </callout>
+ <callout arearefs="unionsubclass4">
+ <para>
+ <literal>lazy</literal> (IvV, ftHgÍ <literal>true</literal> ):
+ <literal>lazy="false"</literal> Æ·éÆAxtFb`ªgpūܹñB
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ ±Ì}bsOíªÅͯÊJâL[JÍKv èܹñB
+ </para>
+
+ <para>
+ p³Ì}bsOÉÖ·éîñÍ <xref linkend="inheritance"/> ð©Ä¾³¢B
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-join" revision="3">
+ <title>join</title>
+
+ <para>
+ <literal><join></literal> vfðg¤±ÆÅA
+ PÂÌNXÌvpeBð¢ÂàÌe[uÉ}bsO·é±ÆªÅ«Ü·B
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="join1" coords="2 50"/>
+ <area id="join2" coords="3 50"/>
+ <area id="join3" coords="4 50"/>
+ <area id="join4" coords="5 50"/>
+ <area id="join5" coords="6 50"/>
+ <area id="join6" coords="7 50"/>
+ </areaspec>
+ <programlisting><![CDATA[<join
+ table="tablename"
+ schema="owner"
+ catalog="catalog"
+ fetch="join|select"
+ inverse="true|false"
+ optional="true|false">
+
+ <key ... />
+
+ <property ... />
+ ...
+</join>]]></programlisting>
+
+ <calloutlist>
+ <callout arearefs="join1">
+ <para>
+ <literal>table</literal> :µ½e[u̼O
+ </para>
+ </callout>
+ <callout arearefs="join2">
+ <para>
+ <literal>schema</literal> (IvV):
+ [gÌ <literal><hibernate-mapping></literal> vfÅwèµ½XL[}¼ð
+ I[o[ChµÜ·B
+ </para>
+ </callout>
+ <callout arearefs="join3">
+ <para>
+ <literal>catalog</literal> (IvV):
+ [gÌ <literal><hibernate-mapping></literal> vfÅwèµ½J^O¼ð
+ I[o[ChµÜ·B
+ </para>
+ </callout>
+ <callout arearefs="join4">
+ <para>
+ <literal>fetch</literal> (IvV - ftHgÍ <literal>join</literal> ):
+ <literal>join</literal> ðÝèµ½êA
+ HibernateÍftHgÅANXâX[p[NXÅè`³ê½ <literal><join></literal>
+ ðõ·éÌÉàðg¢ATuNXÅè`³ê½ <literal><join></literal>
+ ðõ·éÌÉOðg¢Ü·B
+ <literal>select</literal> ðÝèµ½êÉÍA
+ HibernateÍTuNXÅè`³ê½ <literal><join></literal>
+ ÌIðÉIððg¢Ü·B±ÌêA
+ sªTuNXÌCX^Xðã\·é±Æªí©Á½êÉÌÝs³êÜ·B
+ àÍNXâ»ÌX[p[NXÅè`³ê½ <literal><join></literal> ðõ
+ ·é½ßÉgpµÜ·B
+ </para>
+ </callout>
+ <callout arearefs="join5">
+ <para>
+ <literal>inverse</literal> (IvV - ftHgÍ <literal>false</literal> ):
+ àµÂ\Å êÎAHibernateͱÌÅè`³êÄ¢évpeBÉε
+ }üâXVðs¢Ü¹ñB
+ </para>
+ </callout>
+ <callout arearefs="join6">
+ <para>
+ <literal>optional</literal> (IvV - ftHgÍ <literal>false</literal> ):
+ àµÂ\Å êÎAHibernateͱÌÅè`³ê½vpeBªnullÅÈ¢êÉÌÝ
+ sð}üµA»ÌvpeBÌõÉÍíÉOðgpµÜ·B
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ á¦ÎlÌAhXÌîñ𪣵½e[uÉ}bsO·é±ÆªÂ\Å·
+ (·×ÄÌvpeBÉεÄl^ÌZ}eBNXð۵ܷ)B
+ </para>
+
+ <programlisting><![CDATA[<class name="Person"
+ table="PERSON">
+
+ <id name="id" column="PERSON_ID">...</id>
+
+ <join table="ADDRESS">
+ <key column="ADDRESS_ID"/>
+ <property name="address"/>
+ <property name="zip"/>
+ <property name="country"/>
+ </join>
+ ...]]></programlisting>
+
+ <para>
+ ±ÌÁ¥ÍµÎµÎKV[f[^fÉεÄÌÝLpÅ·ªA
+ NXæèàÈ¢e[uÆA«ßÌש¢hCfð§µÜ·B
+ µ©µãÅྷéæ¤ÉAPÂÌNXKwÅp³Ì}bsOíªðØèÖ¦éÉÍLpÅ·B
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-key">
+ <title>key</title>
+
+ <para>
+ ¡ÜŽx© <literal><key></literal> vfªoīܵ½B
+ ±ÌvfÍVµ¢e[uÖÌðè`µ½èA
+ e[uÅOL[ðè`µ½è·éevfÌDZÉÅà»êA
+ IWie[uÌåL[ðQƵܷB
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="key1" coords="2 50"/>
+ <area id="key2" coords="3 50"/>
+ <area id="key3" coords="4 50"/>
+ <area id="key4" coords="5 50"/>
+ <area id="key5" coords="6 50"/>
+ <area id="key6" coords="7 50"/>
+ </areaspec>
+ <programlisting><![CDATA[<key
+ column="columnname"
+ on-delete="noaction|cascade"
+ property-ref="propertyName"
+ not-null="true|false"
+ update="true|false"
+ unique="true|false"
+/>]]></programlisting>
+
+ <calloutlist>
+ <callout arearefs="key1">
+ <para>
+ <literal>column</literal> (IvV):OL[J̼OB
+ lXgµ½ <literal><column></literal> JÉæÁÄàwè³êÜ·B
+ </para>
+ </callout>
+ <callout arearefs="key2">
+ <para>
+ <literal>on-delete</literal> (IvV, ftHgÍ <literal>noaction</literal>):
+ OL[§ñªf[^x[XxÅJXP[híªÂ\©Ç¤©ðwèµÜ·B
+ </para>
+ </callout>
+ <callout arearefs="key3">
+ <para>
+ <literal>property-ref</literal> (IvV):
+ IWie[uÌåL[ÅÍÈ¢JðQÆ·éOL[ðwèµÜ·
+ (KV[f[^ÉεÄñ³êÜ·)B
+ </para>
+ </callout>
+ <callout arearefs="key4">
+ <para>
+ <literal>not-null</literal> (IvV):
+ OL[JªnulllðeµÈ¢±ÆðwèµÜ·
+ (±Ì±ÆÍOL[ªåL[ÌêÅ é±ÆðÃÙIɦµÜ·)B
+ </para>
+ </callout>
+ <callout arearefs="key5">
+ <para>
+ <literal>update</literal> (IvV):
+ OL[ðµÄXVµÄÍÈçÈ¢±ÆðwèµÜ·
+ (±Ì±ÆÍOL[ªåL[ÌêÅ é±ÆðÃÙIɦµÜ·)B
+
+ </para>
+ </callout>
+ <callout arearefs="key6">
+ <para>
+ <literal>unique</literal> (IvV):
+ OL[ªj[N§ñðÂ׫Šé±ÆðwèµÜ·
+ (±Ì±ÆÍOL[ªåL[ÌêÅ é±ÆðÃÙIɦµÜ·)B
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ íÌptH[}XªdvÅ éVXeÉÍA
+ ·×ÄÌL[ð <literal>on-delete="cascade"</literal> Æè`·é±Æð§µÜ·B
+ »¤·é±ÆÅHibernateÍA<literal>DELETE</literal> ¶ðñs·éãíèÉA
+ f[^x[XxÌ <literal>ON CASCADE DELETE</literal> §ñðgpµÜ·B
+ ±ÌÁ¥Ío[Wt¯çê½f[^ÉηéHibernateÌÊíÌyÏIbNíªð
+ ³·éÆ¢¤±ÆÉӵľ³¢B
+ </para>
+
+ <para>
+ <literal>not-null</literal> Æ <literal>update</literal> ®«ÍAPûüêνÖAÌÉÍLpÅ·B
+ PûüêνÖAðnullðeµÈ¢OL[É}bsO·éÆ«ÍA
+ <literal><key not-null="true"></literal> ðgÁÄL[Jðé¾
+ <emphasis> µÈÄÍÈèܹñ</emphasis> B
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-column" revision="4">
+ <title>column Æ formula vf</title>
+ <para>
+ <literal>column</literal> ®«ðLqÅ«éCÓÌ}bsOvfÍܽA
+ <literal><column></literal> TuvfàLqÅ«Ü·B
+ ¯lÉ <literal><formula></literal> à <literal>formula</literal> ®«ÌãÖèiÅ·B
+ </para>
+
+ <programlisting><![CDATA[<column
+ name="column_name"
+ length="N"
+ precision="N"
+ scale="N"
+ not-null="true|false"
+ unique="true|false"
+ unique-key="multicolumn_unique_key_name"
+ index="index_name"
+ sql-type="sql_type_name"
+ check="SQL expression"
+ default="SQL expression"/>]]></programlisting>
+
+ <programlisting><![CDATA[<formula>SQL expression</formula>]]></programlisting>
+
+ <para>
+ ¯¶vpeBâÖAÌ}bsOÌÅA
+ <literal>column</literal> Æ <literal>formula</literal> ®«ðgÝí¹é±ÆªÅ«Ü·B
+ á¦ÎAÁêÈðÈÇÅ·B
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="homeAddress" class="Address"
+ insert="false" update="false">
+ <column name="person_id" not-null="true" length="10"/>
+ <formula>'MAILING'</formula>
+</many-to-one>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-import">
+ <title>import</title>
+
+ <para>
+ AvP[Vɯ¶¼OÌ2ÂÌi±NXª èA
+ HibernateNGÅ®SCü³ê½ipbP[WÌj¼Oðw赽ȢƼèµÜ·B
+ »Ìæ¤ÈêÍ <literal>auto-import="true"</literal> Éç¸A
+ NXªuC|[gv³ê½àÌÅ éƾ¦Å«Ü·B
+ ¾¦IÉ}bsO³êĢȢNXâC^[tFCXų¦àC|[gÅ«Ü·B
+ </para>
+
+ <programlisting><![CDATA[<import class="java.lang.Object" rename="Universe"/>]]></programlisting>
+
+ <programlistingco>
+ <areaspec>
+ <area id="import1" coords="2 40"/>
+ <area id="import2" coords="3 40"/>
+ </areaspec>
+ <programlisting><![CDATA[<import
+ class="ClassName"
+ rename="ShortName"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="import1">
+ <para>
+ <literal>class</literal>FJavaNXÌ®SCü³ê½NX¼B
+ </para>
+ </callout>
+ <callout arearefs="import2">
+ <para>
+ <literal>rename</literal>iIvV - ftHgÍCü³êĢȢNX¼jF
+ NG¾êÅgíêé¼OB
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ </sect2>
+
+ <sect2 id="mapping-types-anymapping" revision="2">
+ <title>any</title>
+
+ <para>
+ vpeB}bsOÉͳçÉà¤1ÂÌ^ª èÜ·B
+ <literal><any></literal> }bsOvfÍA
+ ¡Ìe[u©çNXÖÌ|[tBbNÈÖAðè`µÜ·B
+ ±Ì^Ì}bsOÉÍK¸¡ÌJªKvÅ·B1ÔÚÌJÍÖAGeBeBÌ^ð۵ܷB
+ cèÌJͯÊqð۵ܷB±ÌíÞÌÖAÉÍOL[§ñðwè·é±ÆÍūܹñB
+ »Ì½ß±êÍÅàgíêé±ÆÌÈ¢i|[tBbNÈjÖAÌ}bsOû@Å·B
+ ñíÉÁÊÈêiá¦ÎA¸Oâ[UZbVf[^ÈÇ)ÉÀÁÄA±êðg¤×«Å·B
+ </para>
+
+ <para>
+ <literal>meta-type</literal> ÉæèA
+ AvP[VÍJX^^ðwèÅ«Ü·B±ÌJX^^Í
+ f[^x[XJÌlðA<literal>id-type</literal> Åwèµ½^Ì
+ ¯ÊqvpeBðÁ½i±NXÖ}bsOµÜ·B
+ meta-typeÌl©çNX¼ÖÌ}bsOðwèµÈ¯êÎÈèܹñB
+ </para>
+
+ <programlisting><![CDATA[<any name="being" id-type="long" meta-type="string">
+ <meta-value value="TBL_ANIMAL" class="Animal"/>
+ <meta-value value="TBL_HUMAN" class="Human"/>
+ <meta-value value="TBL_ALIEN" class="Alien"/>
+ <column name="table_name"/>
+ <column name="id"/>
+</any>]]></programlisting>
+
+ <programlistingco>
+ <areaspec>
+ <area id="any1" coords="2 50"/>
+ <area id="any2" coords="3 50"/>
+ <area id="any3" coords="4 50"/>
+ <area id="any4" coords="5 50"/>
+ <area id="any5" coords="6 50"/>
+ <area id="any6" coords="7 50"/>
+ </areaspec>
+ <programlisting><![CDATA[<any
+ name="propertyName"
+ id-type="idtypename"
+ meta-type="metatypename"
+ cascade="cascade_style"
+ access="field|property|ClassName"
+ optimistic-lock="true|false"
+>
+ <meta-value ... />
+ <meta-value ... />
+ .....
+ <column .... />
+ <column .... />
+ .....
+</any>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="any1">
+ <para>
+ <literal>name</literal>FvpeB¼B
+ </para>
+ </callout>
+ <callout arearefs="any2">
+ <para>
+ <literal>id-type</literal>F¯ÊqÌ^B
+ </para>
+ </callout>
+ <callout arearefs="any3">
+ <para>
+ <literal>meta-type</literal>iIvV - ftHgÍ <literal>string</literal> jF
+ fBXN~l[^}bsOųê½^
+ </para>
+ </callout>
+ <callout arearefs="any4">
+ <para>
+ <literal>cascade</literal>iIvV - ftHgÍ <literal>none</literal> jF
+ JXP[hÌX^CB
+ </para>
+ </callout>
+ <callout arearefs="any5">
+ <para>
+ <literal>access</literal>iIvV - ftHgÍ <literal>property</literal> jF
+ vpeBÌlÖÌANZXÉHibernateªg¤íªB
+ </para>
+ </callout>
+ <callout arearefs="any6">
+ <para>
+ <literal>optimistic-lock</literal> (IvV - ftHgÍ <literal>true</literal> ):
+ ±ÌvpeBÌXVÉyÏbNÌæ¾ðv·é©Ç¤©ðwèµÜ·B
+ ¾¢·¦êÎA±ÌvpeBª_[eBÅ éÆ«Éo[Wðâ·×«©ðè`µÜ·B
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="mapping-types">
+ <title>HibernateÌ^</title>
+
+ <sect2 id="mapping-types-entitiesvalues" revision="1">
+ <title>GeBeBÆl</title>
+
+ <para>
+ i±T[rXÉÖíélXÈJava¾êxÌIuWFNgÌUé¢ðð·é½ßÉÍA
+ IuWFNgð2ÂÌO[vɪ¯éKvª èÜ·F
+ </para>
+
+ <para>
+ <emphasis> GeBeB</emphasis> ÍGeBeBÖÌQÆðÛ·éA
+ ¼Ì·×ÄÌIuWFNg©çƧµÄ¶ÝµÜ·B
+ QƳêÈ¢IuWFNgªKx[WRNg³êĵܤ«¿ðÂÊíÌJavafÆA
+ ±êðä×ÄÝľ³¢B
+ ieGeBeB©çqÖAZ[uÆíª <emphasis>JXP[h</emphasis> ³ê¤é±Æð¢Äj
+ GeBeB;¦IÉZ[uܽÍí³êȯêÎÈèܹñB
+ ±êÍBÂ\«ÉæéIuWFNgi±»ÌODMGfÆÍÙÈÁĢܷB
+ åKÍÈVXeÅAvP[VIuWFNgªÊÇÌæ¤Égíêé©Éæè§ÚÉεܷB
+ GeBeBÍzÂÆQÆ̤LðT|[gµÜ·B
+ ܽ»êçÍo[Wt¯·é±ÆàÅ«Ü·B
+
+ </para>
+
+ <para>
+ GeBeBÌi±óÔͼÌGeBeBâ <emphasis>l</emphasis> ^Ì
+ CX^XÖÌQÆ©ç\¬³êÜ·B
+ lÍv~eBuARNV(RNVÌàÅÍÈ)A
+ R|[lgAsÏIuWFNgÅ·B
+ GeBeBÆÍá¢AlÍiÁÉRNVÆR|[lgɨ¢ÄjA
+ BÂ\«Éæéi±»âíª <emphasis>síêÜ·</emphasis> B
+ lIuWFNgiÆv~eBujÍAïÜ·éGeBeBÆêÉi±»âíªsíêéÌÅA
+ »êçðƧÉo[Wt¯·é±ÆÍūܹñB
+ lÉÍƧµ½ACfeBeBªÈ¢ÌÅA
+ ¡ÌGeBeBâRNVª±êð¤L·é±ÆÍūܹñB
+ </para>
+
+ <para>
+ ±êÜÅui±NXvÆ¢¤¾tðGeBeBÌÓ¡ÅgÁīܵ½B
+ ±ê©ç໤µÄ¢«Ü·B
+ µ§É¾¤ÆAi±óÔðÂ[Uè`ÌNXÌ·×Ī
+ GeBeBÆ¢¤í¯ÅÍ èܹñB
+ <emphasis>R|[lg</emphasis> ÍlÌZ}eBNXðÂ[Uè`NXÅ·B
+ <literal>java.lang.String</literal> ^ÌvpeBàܽlÌZ}eBNXð¿Ü·B
+ è`·éÈçAJDKÅñ³êÄ¢é·×ÄÌJavaÌ^(NX)ªlÌZ}eBNXðÂÆ¢¦Ü·B
+ êû[Uè`^ÍAGeBeBâl^ÌZ}eBNXÆÆàÉ}bsOÅ«Ü·B
+ ±ÌèÍAvP[VJÒæÅ·B
+ »ÌNXÌPÂÌCX^XÖ̤LQÆÍA
+ hCfàÌGeBeBNXÉηéÇ¢qgÉÈèÜ·B
+ êû¬WñâWñÍAÊíl^ÖÏ·³êÜ·B
+
+ </para>
+
+ <para>
+ {hL
gðʵÄA½xà±ÌTOðæèã°Ü·B
+ </para>
+
+ <para>
+ Java^ÌVXe(àµÍJÒªè`µ½GeBeBÆl^)ð
+ SQL/f[^x[X^ÌVXeÉ}bsO·é±ÆÍﵢŷB
+ HibernateÍQÂÌVXeÌ˯´ðñµÜ·B
+ GeBeBÉεÄÍ
+ <literal><class></literal> â <literal><subclass></literal> ÈÇðgpµÜ·B
+ l^ÉεÄÍ <literal><property></literal> â
+ <literal><component></literal> ÈÇðAÊí <literal>type</literal> ÆÆàÉg¢Ü·B
+ ±Ì®«ÌlÍHibernateÌ <emphasis>}bsO^</emphasis> ̼OÅ·B
+ HibernateÍ(WJDKÌl^ÉεÄ)½Ì©RÈ}bsOðñµÜ·B
+ ãÅ©éæ¤ÉA©gÌ}bsO^ðLqµA¯lÉJX^ÌÏ·íªðÀ·é±ÆªÅ«Ü·B
+ </para>
+
+ <para>
+ RNVðgÝÝÌHibernateÌ^Í·×ÄAnullZ}eBNXðT|[gµÜ·B
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-types-basictypes" revision="3">
+ <title>î{IÈ^</title>
+
+ <para>
+ gÝÝÌ <emphasis>î{IÈ}bsO^</emphasis> ÍåÜ©ÉȺÌæ¤Éª¯çêÜ·B
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>integer, long, short, float, double, character, byte,
+ boolean, yes_no, true_false</literal></term>
+ <listitem>
+ <para>
+ JavaÌv~eBuâbp[NX©çKØÈix_[ÅLÌj
+ SQLJ^ÖÌ^}bsOB
+ <literal>boolean, yes_no</literal> Æ <literal>true_false</literal> ÍA
+ ·×ÄJavaÌ <literal>boolean</literal>
+ Ü½Í <literal>java.lang.Boolean</literal> ÌãÖGR[hÅ·B
+
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>string</literal></term>
+ <listitem>
+ <para>
+ <literal>java.lang.String</literal> ©ç <literal>VARCHAR</literal>
+ iܽÍOracleÌ <literal>VARCHAR2</literal> jÖÌ^}bsOB
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>date, time, timestamp</literal></term>
+ <listitem>
+ <para>
+ <literal>java.util.Date</literal> Æ»ÌTuNX©çSQL^Ì <literal>DATE</literal>,
+ <literal>TIME</literal> , <literal>TIMESTAMP</literal> iܽͻêçÆ¿ÈàÌj
+ ÖÌ^}bsOB
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>calendar, calendar_date</literal></term>
+ <listitem>
+ <para>
+ <literal>java.util.Calendar</literal> ©çSQL^ Ìu <literal>TIMESTAMP</literal> ,
+ <literal>DATE</literal> (ܽͻêçÆ¿ÈàÌjÖÌ^}bsOB
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>big_decimal, big_integer</literal></term>
+ <listitem>
+ <para>
+ <literal>java.math.BigDecimal</literal> Æ <literal>java.math.BigInteger</literal>
+ ©ç <literal>NUMERIC</literal>iܽÍOracleÌ <literal>NUMBER</literal> jÖÌ^}bsOB
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>locale, timezone, currency</literal></term>
+ <listitem>
+ <para>
+ <literal>java.util.Locale</literal> , <literal>java.util.TimeZone</literal> ,
+ <literal>java.util.Currency</literal> ©ç <literal>VARCHAR</literal>
+ iܽÍOracleÌ <literal>VARCHAR2</literal> jÖÌ^}bsOB
+ <literal>Locale</literal> Æ <literal>Currency</literal> ÌCX^XÍA
+ »êçÌISOR[hÉ}bsO³êÜ·B
+ <literal>TimeZone</literal> ÌCX^XÍA
+ »êçÌ <literal>ID</literal> É}bsO³êÜ·B
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>class</literal></term>
+ <listitem>
+ <para>
+ <literal>java.lang.Class</literal> ©ç <literal>VARCHAR</literal>
+ iܽÍOracleÌ <literal>VARCHAR2</literal> jÖÌ^}bsOB
+ <literal>Class</literal> ͻ̮SCü³ê½¼OÉ}bsO³êÜ·B
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>binary</literal></term>
+ <listitem>
+ <para>
+ oCgzñÍAKØÈSQLÌoCi^É}bsO³êÜ·B
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>text</literal></term>
+ <listitem>
+ <para>
+ ·¢Java¶ñÍASQLÌ <literal>CLOB</literal> ܽÍ
+ <literal>TEXT</literal> ^É}bsO³êÜ·B
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>serializable</literal></term>
+ <listitem>
+ <para>
+ VACYÂ\ÈJava^ÍAKØÈSQLÌoCi^É}bsO³êÜ·B
+ ftHgÅî{^ÅÍÈ¢VACYÂ\ÈJavaNXâ
+ C^[tFCX̼Oðwè·é±ÆÅA
+ HibernateÌ^ð <literal>serializable</literal> Æ·é±ÆàÅ«Ü·B
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>clob, blob</literal></term>
+ <listitem>
+ <para>
+ JDBCNX <literal>java.sql.Clob</literal> Æ <literal>java.sql.Blob</literal>
+ Éηé^}bsOB
+ blobâclobIuWFNgÍgUNVÌOÅÍÄpÅ«È¢½ßA
+ AvP[VÉæÁÄÍsÖ©àµêܹñB
+ i³çÉÍhCoT|[gªêѵĢܹñBj
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <literal>imm_date, imm_time, imm_timestamp, imm_calendar, imm_calendar_date,
+ imm_serializable, imm_binary</literal>
+ </term>
+ <listitem>
+ <para>
+ ÙÆñÇÌêÉÂÏÅ éJavaÌ^Éηé^}bsOB
+ HibernateÍsÏÈJavaÌ^ÉεÄÍÅK»ðs¢A
+ AvP[VÍ»êðsÏIuWFNgƵĵ¢Ü·B
+ á¦Î <literal>imm_timestamp</literal> ƵÄ}bvµ½CX^XÉεÄA
+ <literal>Date.setTime()</literal> ðÄÑoµÄÍÈèܹñB
+ vpeBÌlðÏXµ»ÌÏXði±»·é½ßÉÍA
+ AvP[VÍvpeBÉεÄVµ¢(¯êÅÈ¢)IuWFNgðèÄȯêÎÈèܹñB
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ </para>
+
+ <para>
+ GeBeBÆRNVÌj[NȯÊqÍA<literal>binary</literal> , <literal>blob</literal> ,
+ <literal>clob</literal> ðAÇñÈî{^Åà\¢Ü¹ñB
+ i¡¯ÊqÅà\¢Ü¹ñBȺð©Ä¾³¢Bj
+ </para>
+
+ <para>
+ î{IÈl^ÉÍA<literal>org.hibernate.Hibernate</literal> Åè`³ê½
+ <literal>Type</literal> 誻ê¼ê èÜ·B
+ á¦ÎA<literal>Hibernate.STRING</literal> Í <literal>string</literal> ^ð\»µÄ¢Ü·B
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-types-custom" revision="2">
+ <title>JX^^</title>
+
+ <para>
+ JҪƩÌl^ð쬷é±ÆÍAärIÈPÅ·B
+ á¦ÎA<literal>java.lang.BigInteger</literal> ^ÌvpeBð
+ <literal>VARCHAR</literal> JÉi±»µ½¢©àµêܹñB
+ Hibernateͱ̽ßÌgÝÝ^ðpӵĢܹñB
+ µ©µJX^^ÍAvpeBiܽÍRNVÌvfjð1ÂÌe[uJÉ
+ }bsO·éÌɧÀÍ èܹñB
+ »Ì½ßá¦ÎA<literal>java.lang.String</literal> ^Ì
+ <literal>getName()</literal> / <literal>setName()</literal>
+ JavavpeBð <literal>FIRST_NAME</literal> , <literal>INITIAL</literal>, <literal>SURNAME</literal>
+ JÉi±»Å«Ü·B
+ </para>
+
+ <para>
+ JX^^ðÀ·éÉÍA<literal>org.hibernate.UserType</literal> ܽÍ
+ <literal>org.hibernate.CompositeUserType</literal> ðÀµA
+ ^Ì®SCü³ê½¼OðgÁÄvpeBðè`µÜ·B
+ ÇÌæ¤ÈíÞÌà̪Â\©ð²×éÉÍA
+ <literal>org.hibernate.test.DoubleStringType</literal> ðmFµÄ¾³¢B
+ </para>
+
+ <programlisting><![CDATA[<property name="twoStrings" type="org.hibernate.test.DoubleStringType">
+ <column name="first_string"/>
+ <column name="second_string"/>
+</property>]]></programlisting>
+
+ <para>
+ <literal><column></literal> ^OÅA
+ vpeBð¡ÌJÖ}bsOÅ«é±ÆÉڵľ³¢B
+ </para>
+
+ <para>
+ <literal>CompositeUserType</literal> , <literal>EnhancedUserType</literal> ,
+ <literal>UserCollectionType</literal> , <literal>UserVersionType</literal>
+ C^[tFCXÍAæèÁêÈgp@ÉεÄÌT|[gðñµÜ·B
+ </para>
+
+ <para>
+ }bsOt@CàÅ <literal>UserType</literal> Öp[^ðñÅ«Ü·B
+ ±Ì½ßÉÍA<literal>UserType</literal> Í
+ <literal>org.hibernate.usertype.ParameterizedType</literal> ðÀµÈÄÍÈèܹñB
+ JX^^p[^ðñ·é½ßÉA
+ }bsOt@CàÅ <literal><type></literal> vfðgpÅ«Ü·B
+ </para>
+
+ <programlisting><![CDATA[<property name="priority">
+ <type name="com.mycompany.usertypes.DefaultValueIntegerType">
+ <param name="default">0</param>
+ </type>
+</property>]]></programlisting>
+
+ <para>
+ <literal>UserType</literal> ÍA
+ øƵÄn³ê½ <literal>Properties</literal> IuWFNg©çA
+ <literal>default</literal> Åwèµ½p[^Éηélðõ·é±ÆªÅ«Ü·B
+ </para>
+
+ <para>
+ ÁèÌ <literal>UserType</literal> ðpÉÉgp·éÈçÎAZ¢¼Oðè`·éÆÖÉÈéŵå¤B
+ <literal><typedef></literal> vfðgÁıÌæ¤È±Æªs¦Ü·B
+ TypedefsÍJX^^ɼOðèÄÜ·B
+ »Ì^ªp[^ðÂÈçÎA
+ p[^ÌftHglÌXgðÜÞ±ÆàÅ«Ü·B
+ </para>
+
+ <programlisting><![CDATA[<typedef class="com.mycompany.usertypes.DefaultValueIntegerType" name="default_zero">
+ <param name="default">0</param>
+</typedef>]]></programlisting>
+
+ <programlisting><![CDATA[<property name="priority" type="default_zero"/>]]></programlisting>
+
+ <para>
+ vpeBÌ}bsOÅ^p[^ðg¤±ÆÅA
+ typedefÅñ³ê½p[^ð»ÌsxI[o[Ch·é±ÆªÂ\Å·B
+
+ </para>
+
+ <para>
+ HibernateÌL¢gÝÝ^ÆR|[lgÉηéT|[gÍA
+ JX^^ðßÁ½É <emphasis>gíÈ¢</emphasis> Æ¢¤±ÆðÓ¡µÜ·B
+ »êÅàȨAAvP[VÅpo·é(GeBeBÅÍÈ¢)NXÉηéJX^^ÌgpÍA
+ æ¢âèûÅ éÆÝȳêÜ·B
+ á¦Î <literal>MonetaryAmount</literal> NXÍR|[lgƵÄÈPÉ}bsOÅ«Ü·ªA
+ <literal>CompositeUserType</literal> ÌÇ¢óâÅ·B
+ JX^^ðgp·é®@Ì1ÂÍۻŷB
+ JX^^ðg¤±ÆÅAÊÝðÇÌæ¤É\»µæ¤Æà
+ }bsOhL
gÍN±è¤éÏ»ÉÎÅ«Ü·B
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+
+ <sect1 id="mapping-entityname">
+ <title>PÂÌNXÉPÂÈãÌ}bsO</title>
+ <para>
+ éi±NXÉAêÂÈãÌ}bsOðñ·é±ÆªoÜ·B
+ ±ÌêA}bsO·éQÂÌGeBeBÌCX^Xð¾mÉ·é½ßÉA
+ <emphasis>GeBeB¼</emphasis> ðwèµÈ¯êÎÈèܹñ
+ (ftHgÅÍGeBeB¼ÍNX¼Æ¯¶Å·B)B
+ i±IuWFNgðµ¤Æ«ANGð«ÞÆ«A
+ wè³ê½GeBeBÖÌÖAð}bsO·éÆ«ÉÍA
+ HibernateÅÍGeBeB¼ðwèµÈ¯êÎÈèܹñB
+ </para>
+
+ <programlisting><![CDATA[<class name="Contract" table="Contracts"
+ entity-name="CurrentContract">
+ ...
+ <set name="history" inverse="true"
+ order-by="effectiveEndDate desc">
+ <key column="currentContractId"/>
+ <one-to-many entity-name="HistoricalContract"/>
+ </set>
+</class>
+
+<class name="Contract" table="ContractHistory"
+ entity-name="HistoricalContract">
+ ...
+ <many-to-one name="currentContract"
+ column="currentContractId"
+ entity-name="CurrentContract"/>
+</class>]]></programlisting>
+
+ <para>
+ ÖAª<literal>class</literal> ÌãíèÉ <literal>entity-name</literal> ðgÁÄA
+ ÇÌæ¤Éwè³êéÌ©Éڵľ³¢B
+ </para>
+
+ </sect1>
+
+ <sect1 id="mapping-quotedidentifiers">
+ <title>obNNH[gÅÍñ¾ SQL ¯Êq</title>
+ <para>
+ }bsOhL
gÅe[uâJ̼OðobNNH[gÅÍÞ±ÆÅA
+ HibernateŶ¬³ê½SQL̯Êqðøp³¹é±ÆªÅ«Ü·B
+ HibernateÍSQLÌ <literal>Dialect</literal> ÉηéA³µ¢øpX^Cðg¢Ü·
+ iÊÍ_uNH[gÅ·ªASQL ServerÅÍ©¬ÊAMySQLÅÍobNNH[gÅ·jB
+ </para>
+
+ <programlisting><![CDATA[<class name="LineItem" table="`Line Item`">
+ <id name="id" column="`Item Id`"/><generator class="assigned"/></id>
+ <property name="itemNumber" column="`Item #`"/>
+ ...
+</class>]]></programlisting>
+
+ </sect1>
+
+
+ <sect1 id="mapping-alternatives">
+ <title>^f[^ÌãÖèi</title>
+
+ <para>
+ XMLÌLqÈOÉA
+ HibernateÅÍO/R}bsOÌ^f[^ðè`·éãÖû@ª èÜ·B
+ </para>
+
+ <sect2 id="mapping-xdoclet">
+ <title>XDoclet }[NAbvÌgp</title>
+
+ <para>
+ ½ÌHibernate[UÍXDocletÌ <literal>@hibernate.tags</literal> ðgÁÄA
+ \[XR[hàɼÚ}bsOîñðßÞ±ÆðDÝÜ·B
+ ±ê͵§É¾¦ÎXDoclet̪ìÈÌÅA{hL
gÅͱÌû@ðÎÛÆ͵ܹñB
+ µ©µXDocletðgÁ½ÈºÌ <literal>Cat</literal> }bsOÌáð¦µÜ·B
+ </para>
+
+ <programlisting><![CDATA[package eg;
+import java.util.Set;
+import java.util.Date;
+
+/**
+ * @hibernate.class
+ * table="CATS"
+ */
+public class Cat {
+ private Long id; // identifier
+ private Date birthdate;
+ private Cat mother;
+ private Set kittens
+ private Color color;
+ private char sex;
+ private float weight;
+
+ /*
+ * @hibernate.id
+ * generator-class="native"
+ * column="CAT_ID"
+ */
+ public Long getId() {
+ return id;
+ }
+ private void setId(Long id) {
+ this.id=id;
+ }
+
+ /**
+ * @hibernate.many-to-one
+ * column="PARENT_ID"
+ */
+ public Cat getMother() {
+ return mother;
+ }
+ void setMother(Cat mother) {
+ this.mother = mother;
+ }
+
+ /**
+ * @hibernate.property
+ * column="BIRTH_DATE"
+ */
+ public Date getBirthdate() {
+ return birthdate;
+ }
+ void setBirthdate(Date date) {
+ birthdate = date;
+ }
+ /**
+ * @hibernate.property
+ * column="WEIGHT"
+ */
+ public float getWeight() {
+ return weight;
+ }
+ void setWeight(float weight) {
+ this.weight = weight;
+ }
+
+ /**
+ * @hibernate.property
+ * column="COLOR"
+ * not-null="true"
+ */
+ public Color getColor() {
+ return color;
+ }
+ void setColor(Color color) {
+ this.color = color;
+ }
+ /**
+ * @hibernate.set
+ * inverse="true"
+ * order-by="BIRTH_DATE"
+ * @hibernate.collection-key
+ * column="PARENT_ID"
+ * @hibernate.collection-one-to-many
+ */
+ public Set getKittens() {
+ return kittens;
+ }
+ void setKittens(Set kittens) {
+ this.kittens = kittens;
+ }
+ // addKitten not needed by Hibernate
+ public void addKitten(Cat kitten) {
+ kittens.add(kitten);
+ }
+
+ /**
+ * @hibernate.property
+ * column="SEX"
+ * not-null="true"
+ * update="false"
+ */
+ public char getSex() {
+ return sex;
+ }
+ void setSex(char sex) {
+ this.sex=sex;
+ }
+}]]></programlisting>
+
+ <para>
+ HibernateÌEFuTCgÉÍAXDocletÆHibernateÉÖ·éTvª½ èÜ·B
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-annotations" revision="2">
+ <title>JDK 5.0 Ame[VÌgp</title>
+
+ <para>
+ JDK5.0ÅÍ^CvZ[t©ÂRpCÉ`FbNÅ«éA
+ ¾êxÌXDocletX^CÌAme[Vð±üµÜµ½B
+ ±ÌJjYÍXDocletÌAme[VæèàÍÅAc[âIDEཪT|[gµÄ¢Ü·B
+ á¦ÎIntelliJ IDEAÍAJDK5.0ÉAme[VÌ©®â®Æ\¶Ì²\¦ðT|[gµÄ¢Ü·B
+ EJBdl (JSR-220)ÌVµ¢o[WÅÍAGeBeBr[ÉηéåvÈ^f[^JjYƵÄ
+ JDK5.0ÌAme[VðgpµÄ¢Ü·B
+ Hibernate3ÅÍJSR-220 (i±»API)Ì <literal>EntityManager</literal> ðÀµA
+ ^f[^}bsOÉηéT|[gÍA
+ Ê_E[hÌ <emphasis>Hibernate Annotations</emphasis> pbP[WÉæèpÂ\Å·B
+ ±êÍEJB3(JSR-220)ÆHibernate3Ì^f[^ðÇ¿çàT|[gµÄ¢Ü·B
+
+ </para>
+
+ <para>
+ ȺÍEJBÌGeBeBr[ƵÄß³ê½POJONXÌáÅ·B
+ </para>
+
+ <programlisting><![CDATA[@Entity(access = AccessType.FIELD)
+public class Customer implements Serializable {
+
+ @Id;
+ Long id;
+
+ String firstName;
+ String lastName;
+ Date birthday;
+
+ @Transient
+ Integer age;
+
+ @Embedded
+ private Address homeAddress;
+
+ @OneToMany(cascade=CascadeType.ALL)
+ @JoinColumn(name="CUSTOMER_ID")
+ Set<Order> orders;
+
+ // Getter/setter and business methods
+}]]></programlisting>
+
+ <para>
+ JDK5.0ÌAme[V(ÆJSR-220)ÌT|[gÍisÌìÆÅ èA®SÅÍÈ¢±ÆÉӵľ³¢B
+ ³çÉÚµ¢îñÍHibernateÌAme[VEW
[ðQƵľ³¢B
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+
+ <sect1 id="mapping-generated" revision="1">
+ <title>¶¬vpeB</title>
+ <para>
+ ¶¬vpeBÆÍAf[^x[XÉæÁĶ¬³ê½lðÂvpeBÅ·B
+ ÊíAHibernateAvP[VÍAf[^x[Xªl𶬵½vpeBðÜÞIuWFNgð
+ <literal>tbV
</literal> ·éKvª èܵ½B
+ µ©µAvpeBª¶¬³ê½Æ¢¤±Æð}[N·é±ÆÅA
+ AvP[VÍtbV
ÌÓCðHibernateÉÏ÷µÜ·B
+
+ î{IÉA¶¬vpeBðÂÆè`µ½GeBeBÉεÄ
+ HibernateªINSERTâUPDATEÌSQLðsµ½ã·®ÉA
+ ¶¬³ê½lðÇÝÞ½ßÌ SELECT SQL ªs³êÜ·B
+ </para>
+ <para>
+ ¶¬vpeBÍA}üsÂ\©ÂXVsÂ\ÅȯêÎÈèܹñB
+ <xref linkend="mapping-declaration-version">versions</xref> A
+ <xref linkend="mapping-declaration-timestamp">timestamps</xref> A
+ <xref linkend="mapping-declaration-property">simple properties</xref> ¾¯ª¶¬³ê½Æ}[NÅ«Ü·B
+ </para>
+ <para>
+ <literal>never</literal> (ftHg) - ^¦çê½vpeBÌlÍA
+ f[^x[X©ç¶¬³êÈ¢±ÆðÓ¡µÜ·B
+ </para>
+ <para>
+ <literal>insert</literal> - ^¦çê½vpeBÌlÍ}üɶ¬³êéªA
+ ±¢ÄN±éXVÉͶ¬³êÈ¢±Æ¦µÜ·B
+ 쬳ê½útÈÇÍA±ÌJeSɪ޳êÜ·B
+ <xref linkend="mapping-declaration-version">version</xref> â
+ <xref linkend="mapping-declaration-timestamp">timestamp</xref> Ì
+ vpeBͶ¬³ê½Æ}[NÅ«Ü·ªA±ÌIvVÍpÅ«È¢±ÆÉӵľ³¢B
+ </para>
+ <para>
+ <literal>always</literal> - }üàXVàvpeBÌlª¶¬³êé±Æð¦µÜ·B
+ </para>
+ </sect1>
+
+ <sect1 id="mapping-database-object" revision="0">
+ <title>âIÈf[^x[XEIuWFNg</title>
+ <para>
+ HibernateÌXL[}EG{
[Vc[ÆA®·é±ÆÅA
+ CÓÌf[^x[XEIuWFNgigK[âXgAhvV[WÈÇjÌCREATEÆDROPÉæèA
+ HibernateÌ}bsOt@CàÌ[UEXL[}ð·×Äè`·é±ÆªoÜ·B
+ åÉgKâXgAhvV[WÌæ¤Èf[^x[XEIuWFNgð¶¬âí·é±ÆðÓ}µÄ¢Ü·ªA
+ ÀÛÉÍ <literal>java.sql.Statement.execute()</literal> \bhÉæÁÄÀsÅ«é
+ CÓÌSQLR}hiALTERAINSERTÈÇjªÀsÅ«Ü·B
+ âIÈf[^x[XEIuWFNgðè`·é½ßÌA2ÂÌî{IÈû@ª èÜ·B
+ </para>
+ <para>
+ 1ÂÚÌû@ÍACREATEÆDROPR}hð}bsOt@CÌOÉA¾¦IÉLÚ·é±ÆÅ·B
+ </para>
+ <programlisting><![CDATA[<hibernate-mapping>
+ ...
+ <database-object>
+ <create>CREATE TRIGGER my_trigger ...</create>
+ <drop>DROP TRIGGER my_trigger</drop>
+ </database-object>
+</hibernate-mapping>]]></programlisting>
+ <para>
+ 2ÂÚÌû@ÍACREATEÆDROPR}hÌgݧÄûðmÁÄ¢éJX^NXðñ·é±ÆÅ·B
+ ±ÌJX^NXÍ <literal>org.hibernate.mapping.AuxiliaryDatabaseObject</literal> C^tF[Xð
+ ÀµÈ¯êÎÈèܹñB
+ </para>
+ <programlisting><![CDATA[<hibernate-mapping>
+ ...
+ <database-object>
+ <definition class="MyTriggerDefinition"/>
+ </database-object>
+</hibernate-mapping>]]></programlisting>
+ <para>
+ ³çÉA éf[^x[Xû¾ªgp³êéɾ¯Kp·éÆ¢Á½æ¤ÉA
+ f[^x[XEIuWFNgªgíêéP[XðÀèÅ«Ü·B
+ </para>
+ <programlisting><![CDATA[<hibernate-mapping>
+ ...
+ <database-object>
+ <definition class="MyTriggerDefinition"/>
+ <dialect-scope name="org.hibernate.dialect.Oracle9Dialect"/>
+ <dialect-scope name="org.hibernate.dialect.OracleDialect"/>
+ </database-object>
+</hibernate-mapping>]]></programlisting>
+ </sect1>
+
+</chapter>
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/batch.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/batch.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/batch.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,386 @@
+<?xml version="1.0" encoding="Shift_JIS"?>
+<chapter id="batch">
+ <title>ob`</title>
+
+ <para>
+ HibernateðgÁÄf[^x[XÉ100,000sð}ü·éð¼Èû@ÍA±Ìæ¤ÈàÌÅ·F
+ </para>
+
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+for ( int i=0; i<100000; i++ ) {
+ Customer customer = new Customer(.....);
+ session.save(customer);
+}
+tx.commit();
+session.close();]]></programlisting>
+
+ <para>
+ ±êÍ50,000ÔÚÌsÌ ½èÅ <literal>OutOfMemoryException</literal> Ÿs·éŵå¤B
+ HibernateªZbVxLbV
ÅA
+ Vµ}ü³ê½·×ÄÌ <literal>Customer</literal>
+ CX^XðLbV
·é©çÅ·B
+ </para>
+
+ <para>
+ ±ÌÍÅÍA±Ìâèðñð·éû@ðÐîµÜ·B
+ µ©µob`ð·éÈçAJDBCob`ªgpÂ\Å é±ÆªñíÉdvÅ·B
+ »¤ÅȯêÎè ÈptH[}Xª¾çêܹñB
+ JDBCob`TCYðè Èliá¦ÎA10©ç50jÉÝèµÄ¾³¢F
+ </para>
+
+<programlisting><![CDATA[hibernate.jdbc.batch_size 20]]></programlisting>
+
+ <para>
+ ܽñLbV
ªSø©È¢vZXÅA
+ ±Ìæ¤ÈìÆðµ½¢Æv¤©àµêܹñF
+ </para>
+
+<programlisting><![CDATA[hibernate.cache.use_second_level_cache false]]></programlisting>
+
+ <para>
+ µ©µA±êÍâÎÉKvÆ¢¤í¯ÅÍ èܹñB
+ ȺÈ羦IÉ <literal>CacheMode</literal> ðÝèµÄA
+ ñLbV
ÆÌÝìpð³øÉ·é±ÆªÅ«é©çÅ·B
+
+ </para>
+
+ <sect1 id="batch-inserts">
+ <title>ob`}ü</title>
+
+ <para>
+ Vµ¢IuWFNgði±»·éÆ«AêLbV
ÌTCYð§À·é½ßA
+ ZbVð <literal>flush()</literal> µÄ <literal>clear()</literal>
+ µÈ¯êÎÈèܹñB
+ </para>
+
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+
+for ( int i=0; i<100000; i++ ) {
+ Customer customer = new Customer(.....);
+ session.save(customer);
+ if ( i % 20 == 0 ) { //20, same as the JDBC batch size
+ //flush a batch of inserts and release memory:
+ session.flush();
+ session.clear();
+ }
+}
+
+tx.commit();
+session.close();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="batch-update" >
+ <title>ob`XV</title>
+
+ <para>
+ f[^ð³µ½èXVµ½è·éÉͯ¶ACfBAðKpµÜ·B
+ »êÉÁ¦ÄAf[^Ìsð½Ô·NGÉεÄLøÈ
+ T[o[TChÌJ[\Ì_𶩵½¯êÎ
+ <literal>scroll()</literal> ðg¤Kvª èÜ·B
+ </para>
+
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+
+ScrollableResults customers = session.getNamedQuery("GetCustomers")
+ .setCacheMode(CacheMode.IGNORE)
+ .scroll(ScrollMode.FORWARD_ONLY);
+int count=0;
+while ( customers.next() ) {
+ Customer customer = (Customer) customers.get(0);
+ customer.updateStuff(...);
+ if ( ++count % 20 == 0 ) {
+ //flush a batch of updates and release memory:
+ session.flush();
+ session.clear();
+ }
+}
+
+tx.commit();
+session.close();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="batch-statelesssession">
+ <title>
+ StatelessSessionC^[tFCX
+ </title>
+
+ <para>
+ ܽÊÌû@ƵÄAHibernateÍR}hwüÌAPIðpӵĢܷB
+ ±êͪ£IuWFNgÌ`ÅA
+ f[^x[XÆÌf[^Xg[ÌâèæèÉg¤±ÆªÅ«Ü·B
+ <literal>StatelessSession</literal> ÍÖA·éi±ReLXgð½¸A
+ xÌCtTCNZ}eBNX̽ðñµÜ¹ñB
+ ÁÉXe[gXZbVÍAêLbV
ðÀ¹¸A
+ ܽÇÌæ¤ÈñLbV
âNGLbV
ÆàÝìpµÜ¹ñB
+ gUNViÈwrite-behindâ©®_[eB`FbNàÀµÜ¹ñB
+ Xe[gXZbVðgÁÄsíêéìªA
+ ÖA·éCX^XÖJXP[h³êé±Æ굀 èܹñB
+ RNVÍAXe[gXZbV©çͳ³êÜ·B
+ Xe[gXZbVðʵÄsíêéìÍA
+ HibernateÌCxgfâC^[Zv^Ìe¿ðó¯Ü¹ñB
+ êLbV
ð½È¢½ßA
+ Xe[gXZbVÍʼðÂf[^ÉãèÎūܹñB
+ Xe[gXZbVÍáxÌۻŠèAJDBCÉñíÉæĢܷB
+
+ </para>
+
+<programlisting><![CDATA[StatelessSession session = sessionFactory.openStatelessSession();
+Transaction tx = session.beginTransaction();
+
+ScrollableResults customers = session.getNamedQuery("GetCustomers")
+ .scroll(ScrollMode.FORWARD_ONLY);
+while ( customers.next() ) {
+ Customer customer = (Customer) customers.get(0);
+ customer.updateStuff(...);
+ session.update(customer);
+}
+
+tx.commit();
+session.close();]]></programlisting>
+
+ <para>
+ ±ÌR[háÅÍANGªÔ· <literal>Customer</literal>
+ CX^XͦÀÉiZbV©çjª£³êé±ÆÉӵľ³¢B
+ ±êÍAÇÌæ¤Èi±ReLXgÆàµÄÖAµÜ¹ñB
+
+ </para>
+
+ <para>
+ <literal>StatelessSession</literal> C^[tFCXÅè`³êÄ¢é
+ <literal>insert(), update(), delete()</literal> ÍA
+ áx̼ÚIÈf[^x[XìÆl¦çêÜ·B
+ ÊƵÄASQLÌ <literal>INSERT, UPDATE, DELETE</literal> ª»ê¼ê¦ÀÉÀs³êÜ·B
+ ±Ìæ¤ÉA±êçÍ <literal>Session</literal> C^[tFCXÅè`³êÄ¢é
+ <literal>save(), saveOrUpdate(), delete()</literal>
+ ÆÍñíÉÙÈéÓ¡ð¿Ü·B
+
+ </para>
+
+ </sect1>
+
+ <sect1 id="batch-direct" revision="3">
+ <title>
+ DMLX^CÌì
+ </title>
+
+ <para>
+ ·ÅÉc_µ½æ¤ÉA©®I©Â§ßIÈIuWFNg/[Vi}bsOÍA
+ IuWFNgÌóÔÌÇÅ éÆl¦çêÜ·B
+ ±êÍàÌIuWFNgÌóÔðpÅ«éÆ¢¤±ÆÅ·B
+ »Ì½ßiSQLÌ <literal>f[^ì¾ê</literal> (DML) ¶F
+ <literal>INSERT</literal>, <literal>UPDATE</literal>, <literal>DELETE</literal>
+ ðgÁÄjf[^x[XàÌf[^ð¼ÚìµÄàA
+ àÌóÔÉÍe¿ð^¦Ü¹ñB
+ µ©µHibernateÍAoNSQLX^CÌDML¶ÀsÉηé\bhðpӵĢܷB
+ ±êÍHibernateNG¾êi<xref linkend="queryhql">HQL</xref>j
+ ðʵÄÀs³êÜ·B
+
+ </para>
+
+ <para>
+ <literal>UPDATE</literal> Æ <literal>DELETE</literal> ¶Ì^\¶ÍF
+ <literal>( UPDATE | DELETE ) FROM? GeBeB¼ (WHERE ðß)?</literal> Å·B
+ ӷ׫_ª¢Â© èÜ·F
+
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ fromßɨ¢ÄAFROML[[hÍIvVÅ·B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ fromßÅÍPêÌGeBeB¼¾¯ªÂ\ÅA
+ CÓÅʼðt¯é±ÆªÅ«Ü·B
+ GeBeB¼Éʼª^¦çêéÆAÇÌæ¤ÈvpeBQÆàA
+ »ÌʼðgÁÄCüµÈ¯êÎÈèܹñB
+ àµGeBeB¼Éʼª^¦çêȯêÎA
+ ÇÌæ¤ÈvpeBQÆàCüµÄÍÈèܹñB
+
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ iÃÙIŠ꾦IÅ êj<xref linkend="queryhql-joins-forms"></xref>
+ ðoNHQLNGàÅwè·é±ÆÍūܹñB
+ TuNGÍwhereßÅg¤±ÆªÅ«Ü·
+ TuNG»ÌàÌÍAðÜßçêÜ·B
+
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ whereßÍIvVÅ·B
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ áƵÄAHQLÌ <literal>UPDATE</literal> ðÀs·éÉÍA
+ <literal>Query.executeUpdate()</literal> \bhðgÁľ³¢B
+ i±Ì\bhͨȶÝÌJDBC <literal>PreparedStatement.executeUpdate()</literal>
+ ©ç¼t¯çêܵ½jF
+ d
+ </para>
+
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+
+String hqlUpdate = "update Customer c set c.name = :newName where c.name = :oldName";
+// or String hqlUpdate = "update Customer set name = :newName where name = :oldName";
+int updatedEntities = s.createQuery( hqlUpdate )
+ .setString( "newName", newName )
+ .setString( "oldName", oldName )
+ .executeUpdate();
+tx.commit();
+session.close();]]></programlisting>
+
+ <para>
+ HQLÌ <literal>UPDATE</literal> ¶ÍAftHgÅÍAìp·éGeBeBÌ
+ <xref linkend="mapping-declaration-version">version</xref> â
+ <xref linkend="mapping-declaration-timestamp">timestamp</xref>
+ vpeBÌlÉÍe¿µÜ¹ñB
+ ±êÍEJB3ÌdlÉàó¯pªêĢܷB
+ µ©µ <literal>versioned update</literal> ðgÁÄA
+ <literal>version</literal> â <literal>timestamp</literal>
+ vpeBÌlð§IÉZbg³¹é±ÆªÅ«Ü·B
+ ±êÍ <literal>UPDATE</literal> L[[hÌãÉ <literal>VERSIONED</literal>
+ L[[hðÇÁ·é±ÆÅs¦Ü·B
+
+ </para>
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+String hqlVersionedUpdate = "update versioned Customer set name = :newName where name = :oldName";
+int updatedEntities = s.createQuery( hqlUpdate )
+ .setString( "newName", newName )
+ .setString( "oldName", oldName )
+ .executeUpdate();
+tx.commit();
+session.close();]]></programlisting>
+
+ <para>
+ JX^o[W^i<literal>org.hibernate.usertype.UserVersionType</literal>j
+ Í <literal>update versioned</literal> ¶ÆêÉg¦È¢±ÆÉӵľ³¢B
+ </para>
+
+ <para>
+ HQLÌ <literal>DELETE</literal> ðÀs·éÉÍA
+ ¯¶ <literal>Query.executeUpdate()</literal> \bhðgÁľ³¢F
+
+ </para>
+
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+
+String hqlDelete = "delete Customer c where c.name = :oldName";
+// or String hqlDelete = "delete Customer where name = :oldName";
+int deletedEntities = s.createQuery( hqlDelete )
+ .setString( "oldName", oldName )
+ .executeUpdate();
+tx.commit();
+session.close();]]></programlisting>
+
+ <para>
+ <literal>Query.executeUpdate()</literal> \bhªÔ· <literal>int</literal>
+ ÌlÍA±Ììªe¿ðyÚµ½GeBeBÌÅ·B
+ ±êªe¿·éf[^x[XàÌsÆAÝÉÖW·é©Ç¤©ðl¦ÄÝľ³¢B
+ HQLoNìÍAÊƵÄAÀÛÌSQL¶ª¡Às³êé±ÆÉÈèÜ·B
+ á¦Îjoined-subclassÅ·B
+ Ô³êéÍA»Ì¶ÉæÁÄe¿³ê½ÀÛÌGeBeBÌð¦µÜ·B
+ joined-subclassÌáÉßéÆATuNXÌêÂÉηéíÍA
+ »ÌTuNXª}bsO³ê½e[u¾¯ÅÍÈA
+ u[gve[uÆp³Kwð³çɺÁ½joined-subclassÌe[uÌíÉÈèÜ·B
+ </para>
+
+ <para>
+ <literal>INSERT</literal> ¶Ì^\¶ÍF
+ <literal>INSERT INTO GeBeB¼ vpeBXg select¶</literal> Å·B
+ ӷ׫_ª¢Â© èÜ·F
+
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ INSERT INTO ... SELECT ... Ì`®¾¯ªT|[g³êĢܷB
+ INSERT INTO ... VALUES ... Ì`®ÍT|[g³êĢܹñB
+ </para>
+ <para>
+ vpeBXgÍASQLÌ <literal>INSERT</literal> ¶É¨¯é <literal>JÌdl</literal>
+ É޵ĢܷB
+ p³Ì}bsOÉÜÜêéGeBeBÉεÄA
+ NXxżÚè`³ê½vpeB¾¯ªAvpeBXgÉg¦Ü·B
+ X[p[NXÌvpeBÍFßçê¸ATuNXÌvpeBÍøʪ èܹñB
+ ¾¢·¦éÆ <literal>INSERT</literal> ¶ÍA{¿IÉ|[tBbNÅÍ èܹñB
+
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ select¶ÌÔèlÌ^ªinsert¶ªúÒ·é^Æ}b`µÄ¢êÎA
+ »Ìselect¶ÍÃÈHQL selectNGÆÈè¦Ü·B
+ »Ý±Ì`FbNðf[^x[XÖC¹éÌÅÍÈANGÌRpCÉ`FbNµÜ·B
+ ±Ì±ÆÍA<emphasis>equal</emphasis>ÆÍá¢A
+ HibernateÌ <literal>Type</literal> ÔÌ <emphasis>equivalent</emphasis> ÉÖ·é
+ âèðø«N±·±ÆÉӵľ³¢B
+ ±êÍ <literal>org.hibernate.type.DataType</literal> ƵÄè`³ê½vpeBÆA
+ <literal>org.hibernate.type.TimestampType</literal>
+ ƵÄè`³ê½vpeBÌÔÌ~X}b`Ìâèðø«N±µÜ·B
+ f[^x[Xª»êçðæÊÅ«ÈÄàAÏ··é±ÆªÅ«ÄàA±ÌâèͶµÜ·B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ idvpeBÉεÄAinsert¶ÉÍñÂÌIðª èÜ·B
+ vpeBXgž¦IÉidvpeBðwè·é©
+ i±ÌêAηéselect®©çlªæçêÜ·jA
+ vpeBXg©çO·é©
+ i±ÌêA¶¬³êélªgíêÜ·jÌ¢¸ê©Å·B
+ ãÒÌIðÍAf[^x[Xàðì·éidWFl[^ðg¤Æ«ÌÝApÂ\Å·B
+ ±ÌIððÌéêAuCv^ÌWFl[^ðg¤ÆA\¶ðÍÉáOª¶µÜ·B
+ ±Ìc_ÅÍACf[^x[X^WFl[^Í <literal>org.hibernate.id.SequenceGenerator</literal>
+ iÆ»ÌTuNXjÆA<literal>org.hibernate.id.PostInsertIdentifierGenerator</literal>
+ ÌÀÅ éÆl¦Ä¢Ü·B
+ ±±ÅÅàӷ׫áOÍA<literal>org.hibernate.id.TableHiLoGenerator</literal> Å·B
+ lðæ¾·éIðÂ\Èû@ªÈ¢½ßA±ÌWFl[^ðg¤±ÆÍūܹñB
+
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>version</literal> â <literal>timestamp</literal> ƵÄ}bsO³êévpeBÉεÄA
+ insert¶ÉÍñÂÌIðª èÜ·B
+ vpeBXgž¦IÉvpeBðwè·é©
+ i±ÌêAηéselect®©çlªæçêÜ·jA
+ vpeBXg©çO·é©
+ i±ÌêA<literal>org.hibernate.type.VersionType</literal> Åè`³ê½
+ <literal>V[hl</literal> ªgíêÜ·jÌ¢¸ê©Å·B
+
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ HQLÌ <literal>INSERT</literal> ¶ÌÀsáÅ·F
+
+ </para>
+
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+
+String hqlInsert = "insert into DelinquentAccount (id, name) select c.id, c.name from Customer c where ...";
+int createdEntities = s.createQuery( hqlInsert )
+ .executeUpdate();
+tx.commit();
+session.close();]]></programlisting>
+
+ </sect1>
+
+</chapter>
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/best_practices.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/best_practices.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/best_practices.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,250 @@
+<?xml version="1.0" encoding="Shift_JIS"?>
+<chapter id="best-practices" revision="3">
+ <title>xXgvNeBX</title>
+
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term>
+ NXÍש¢±xÅ« <literal><component></literal> Å}bsOµÜµå¤B</term>
+ <listitem>
+ <para>
+ <literal>street</literal>iÊèj, <literal>suburb</literal>
+ issj, <literal>state</literal>iBj, <literal>postcode</literal>
+ iXÖÔjðJvZ»·é <literal>Address</literal>iZjNXðg¢Üµå¤B
+ »¤·êÎR[hªÄpµâ·ÈèAt@N^OàÈPÉÈèÜ·B
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ i±NXÉͯÊqvpeBðè`µÜµå¤B</term>
+ <listitem>
+ <para>
+ HibernateÅͯÊqvpeBÍIvVÅ·ªA
+ gp·×«Rª½³ñ èÜ·B
+ ¯ÊqÍulHIvi¶¬³ê½AƱIÈÓ¡ð½È¢j
+ ÈàÌÉ·é±Æ𨷷ߵܷB
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>©RL[ð©Â¯Üµå¤B</term>
+ <listitem>
+ <para>
+ ·×ÄÌGeBeBÉεĩRL[ð©Â¯ÄA
+ <literal><natural-id></literal> Å}bsOµÜµå¤B
+ ©RL[ð\¬·évpeBðär·é½ßÉA
+ <literal>equals()</literal> Æ <literal>hashCode()</literal> ðÀµÜµå¤B
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>NXÌ}bsOÍ»ê¼êÌNXêpÌt@Cɫܵå¤B</term>
+ <listitem>
+ <para>
+ PêÌåÈ}bsOhL
gðgpµÈ¢Å¾³¢B
+ <literal>com.eg.Foo</literal> NXÈç
+ <literal>com/eg/Foo.hbm.xml</literal> t@CÉ}bsOµÜµå¤B
+ ±Ì±ÆÍAÁÉ`[ÅÌJÉÓ¡ª èÜ·B
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>\[XƵÄ}bsOð[hµÜµå¤B</term>
+ <listitem>
+ <para>
+ }bsOðA»êçª}bsO·é·éNXÆêÉzuµÜµå¤B
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>NG¶ñðOÉu±Æðl¦Üµå¤</term>
+ <listitem>
+ <para>
+ NGªANSIWÅÈ¢SQLÖðÄñÅ¢éÈçA±êÍæ¢vNeBXÅ·B
+ NG¶ñð}bsOt@CÖOoµ·êÎAvP[Vª|[^uÉÈèÜ·B
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>oChÏðg¢Üµå¤B</term>
+ <listitem>
+ <para>
+ JDBCÌêƯ¶æ¤ÉAèÅÈ¢lÍK¸"?"Åu«·¦Üµå¤B
+ èÅÈ¢lðoCh·é½ßÉANGŶñìðgÁÄÍ¢¯Ü¹ñB
+ ¼Ot«Ìp[^ðg¤æ¤É·éƳçÉǢŷB
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>JDBCRlNVðǵÄÍ¢¯Ü¹ñB</term>
+ <listitem>
+ <para>
+ HibernateÅÍAvP[VªJDBCRlNVðÇ·é±Æª³êĢܷB
+ µ©µ±êÍÅIèi¾ÆvÁľ³¢B
+ gÝÝÌRlNVvoC_ðg¤±ÆªÅ«È¯êÎA
+ <literal>org.hibernate.connection.ConnectionProvider</literal> ðÀ·é±Æðl¦Ä¾³¢B
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>JX^^Ìgpðl¦Üµå¤B</term>
+ <listitem>
+ <para>
+ éCu©çÁÄ«½Java^ði±»·éKvª éƵܵå¤B
+ µ©µ»Ì^ÉÍAR|[lgƵÄ}bsO·é½ßÉKvÈANZTªÈ¢ÆµÜ·B
+ ±Ìæ¤ÈêÍ <literal>org.hibernate.UserType</literal> ÌÀðl¦é׫ŷB
+ »¤·êÎHibernate^ÆÌÀÏ·ðSz¹¸ÉAvP[VÌR[hðµ¦Ü·B
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>{glbNððÁ·éÉÍJDBCðnhR[hµÜµå¤B</term>
+ <listitem>
+ <para>
+ VXeÌptH[}XNeBJÈÌæÅÍA
+ éíÌìÉJDBCð¼Úg¤ÆÇ¢©àµêܹñB
+ µ©µ½ª{glbNÉÈÁÄ¢é© <emphasis>ÍÁ«è·é</emphasis> ÜÅÍÒÁľ³¢B
+ ܽJDBCð¼Úg¤©çÆ¢ÁÄAK¸µà¬ÈéÆÍÀçÈ¢±ÆàðµÄ¾³¢B
+ JDBCð¼Úg¤Kvª êÎAHibernateÌ <literal>Session</literal> ðI[vµÄA
+ JDBCRlNVðg¤ÆÇ¢©àµêܹñB
+ ËRƵį¶gUNVíªÆRlNVvoC_ªg¦é©çÅ·B
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>Session</literal> ÌtbV
ððµÜµå¤B</term>
+ <listitem>
+ <para>
+ Sessionªi±óÔðf[^x[XƯú³¹é±ÆªÆ«Ç« èÜ·B
+ µ©µ±êª ÜèÉpÉÉN±é椾ÆAptH[}XÉe¿ªoÄ«Ü·B
+ ©®tbV
ð³øɵ½èAÁèÌgUNVÌNGâìÌÔðÏX·é±ÆÅA
+ sKvÈtbV
ðŬÀÉÅ«Ü·B
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>3wA[LeN`Åͪ£IuWFNgÌgpðl¦Üµå¤B</term>
+ <listitem>
+ <para>
+ T[ubg / ZbVr[A[LeN`ðg¤Æ«A
+ T[ubgw / JSPwÔÅZbVr[Å[hµ½i±IuWFNgðâèæèÅ«Ü·B
+ »ÌÛNGXg²ÆÉVµ¢SessionðgÁľ³¢B
+ ܽ <literal>Session.merge()</literal> â <literal>Session.saveOrUpdate()</literal>
+ ðgÁÄAIuWFNgÆf[^x[Xð¯ú³¹Ä¾³¢B
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>2wA[LeN`ÅÍ·¢i±ReLXgÌgpðl¦Üµå¤B</term>
+ <listitem>
+ <para>
+ ÅÌXP[reBð¾éÉÍA
+ f[^x[XgUNVðū龯ZµÈ¯êÎÈèܹñB
+ µ©µ·¢ÔÀs·é <emphasis>AvP[VgUNV</emphasis>
+ ÌÀªKvȱÆ͵εÎÅ·B
+ ±êÍ[UÌ_©çÍ1ÂÌìÆPÊiunit of workjÉÈèÜ·B
+ AvP[VgUNV͢©ÌNCAgÌNGXg/X|XTCNÉܽªèÜ·B
+ AvP[VgUNVÌÀɪ£IuWFNgðg¤ÌÍêÊIÅ·B
+
+ »¤ÅȯêÎA2wA[LeN`ÌêÍÁÉKØȱÆÅ·ªA
+ AvP[VgUNVÌCtTCNSÌÉεÄ
+ PêÌI[vÈi±»ReLXgiZbVjð۵ľ³¢B
+ »µÄPÉNGXgÌÅãÉJDBCRlNV©çØfµA
+ ÌNGXgÌÅÉÄÚ±µÜ·B
+
+ µÄ¡ÌAvP[VgUNV[XP[XÉnÁÄ
+ 1ÂÌSessionðg¢ñ³È¢Å¾³¢B
+ »¤ÅȯêÎAâf[^ÅìÆ·é±ÆÉÈèÜ·B
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>áOðAÂ\ÈàÌƵĵÁÄÍ¢¯Ü¹ñB</term>
+ <listitem>
+ <para>
+ ±êÍuxXgvvNeBXÈãÌAK{ÌvNeBXÅ·B
+ áOª¶µ½Æ«Í <literal>Transaction</literal> ð[obNµÄA
+ <literal>Session</literal> ðN[YµÄ¾³¢B
+ »¤µÈ¢ÆHibernateÍÌóÔªi±óÔð³mÉ\»µÄ¢é±ÆðÛØūܹñB
+ ±ÌÁÊÈêƵÄA^¦ç꽯ÊqðÂCX^Xªf[^x[Xɶݷé©Ç¤©ð»è·é½ßÉA
+ <literal>Session.load()</literal> ðg¤±ÆÍâßľ³¢B
+ »ÌãíèÉ <literal>Session.get()</literal> ©NGðgÁľ³¢B
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>ÖAÉÍÈé×xtFb`ðg¢Üµå¤B</term>
+ <listitem>
+ <para>
+ ¦tFb`ÍT¦ßɵܵå¤B
+ ñLbV
ÉÍ®SÉÛ³êÈ¢æ¤ÈNXÌÖAÉÍA
+ vLVÆxRNVðgÁľ³¢B
+ LbV
³êéNXÌÖAAÂÜèLbV
ªqbg·éÂ\«ªñíÉ¢ÖAÍA
+ <literal>lazy="false"</literal> ÅÏÉIÈtFb`ð¾¦Iɳøɵľ³¢B
+ tFb`ªKØÈÁèÌ[XP[XÉÍA
+ NGÅ <literal>left join fetch</literal> ðgÁľ³¢B
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ tFb`³êĢȢf[^ÉÖíéâèðð¯é½ßÉA
+ <emphasis>r
[ÌÅI[vZbVðg¤(open session in view)</emphasis>
+ p^[©A§³ê½ <emphasis>gݧÄtF[Y(assembly phase)</emphasis> ðg¢Üµå¤B
+ </term>
+ <listitem>
+ <para>
+ HibernateÍ <emphasis>Data Transfer Objects</emphasis> (DTO)ðÞüÈìÆ©çJÒððúµÜ·B
+ `IÈEJBA[LeN`ÅÍDTOÍñÂÚIª èÜ·F
+ 1ÂÚÍAGeBeBr[ªVACY³êÈ¢âèÖÌÎôÅ·B
+ 2ÂÚÍAv[e[VwɧäªßéOÉA
+ r
[Égíêé·×ÄÌf[^ªtFb`³êÄADTOɳ³êéæ¤ÈgݧÄtF[YðÃÙIÉè`µÜ·B
+ HibernateÅÍ1ÂÚÌÚIªsvÉÈèÜ·B
+ µ©µr
[Ì_OÌÔAi±ReLXgiZbVjðI[vɵ½ÜÜɵȯêÎA
+ gݧÄtF[YÍܾKvÅ·iª£IuWFNgÌÌÇÌf[^ªpÂ\©É¢ÄA
+ v[e[VwƵ§ÈæèßðµÄ¢érWlX\bhðl¦ÄÝľ³¢jB
+ ±êÍHibernate¤ÌâèÅÍ èܹñB
+ gUNVàÅÀSÉf[^ANZX·é½ßÌî{IÈvÅ·B
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Hibernate©çrWlXWbNðÛ»·é±Æðl¦Üµå¤B</term>
+ <listitem>
+ <para>
+ C^[tFCXÅiHibernateÌjf[^ANZXR[hðBÁµÜµå¤B
+ <emphasis>DAO</emphasis> Æ <emphasis>Thread Local Session</emphasis> p^[ðgÝí¹Üµå¤B
+ <literal>UserType</literal> ÅHibernateÉÖAt¯éÆA
+ nhR[hµ½JDBCÅi±»·éNXð±ÆàÅ«Ü·B
+ i±ÌAhoCXÍu\ªå«ÈvAvP[VÉεÄÌàÌÅ·B
+ e[uª5µ©È¢æ¤ÈAvP[VÉÍÄÍÜèܹñBj
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>¿µ¢ÖA}bsOÍgíÈ¢æ¤ÉµÜµå¤B</term>
+ <listitem>
+ <para>
+ æ¢[XP[XÉ{̽νÖAª é±ÆÍHiÜêjÅ·B
+ ÙÆñÇÌêuNe[uvÌtÁIÈîñªKvÉÈèÜ·B
+ ±ÌêANNXÉ2ÂÌ1νÖAðg¤ûª¸ÁÆǢŷB
+ ÀÛÙÆñÇÌêÖAÍ1νƽÎ1ÈÌÅA
+ ¼ÌX^CÌÖAðg¤Æ«Í{ÉKv©Ç¤©ðl¦ÄÝľ³¢B
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Èé×oûüÖAɵܵå¤B</term>
+ <listitem>
+ <para>
+ PûüÖAÍoûüÉä×ÄõªïµÈèÜ·B
+ å«ÈAvP[VÅÍA
+ ÙÆñÇ·×ÄÌÖAªoûüÉirQ[VūȯêÎÈèܹñB
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/collection_mapping.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/collection_mapping.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/collection_mapping.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1226 @@
+<?xml version="1.0" encoding="Shift_JIS"?>
+<chapter id="collections">
+ <title>RNVÌ}bsO</title>
+
+ <sect1 id="collections-persistent" revision="3">
+ <title>RNVÌi±»</title>
+
+ <para>
+ RNV^ÌtB[hði±»·éÉÍA
+ »ÌRNV^ªC^[tFCX^Å éKvª èÜ·B
+ á¦ÎA
+ </para>
+
+ <programlisting><![CDATA[public class Product {
+ private String serialNumber;
+ private Set parts = new HashSet();
+
+ public Set getParts() { return parts; }
+ void setParts(Set parts) { this.parts = parts; }
+ public String getSerialNumber() { return serialNumber; }
+ void setSerialNumber(String sn) { serialNumber = sn; }
+}]]></programlisting>
+
+ <para>
+ ÀÝ·éC^[tFCXÉÍ <literal>java.util.Set</literal>A
+ <literal>java.util.Collection</literal>A<literal>java.util.List</literal>A
+ <literal>java.util.Map</literal>A<literal>java.util.SortedSet</literal>A
+ <literal>java.util.SortedMap</literal> ÈǪ èÜ·B
+ ܽÍACÓÌC^[tFCXªg¦Ü·I
+ i½¾µACÓÌC^[tFCXðgp·éêÍA
+ <literal>org.hibernate.usertype.UserCollectionType</literal>
+ ÌÀNXð쬷éKvª èÜ·Bj
+ </para>
+
+ <para>
+ <literal>HashSet</literal> ÌCX^XðÂCX^XϪ
+ ÇÌæ¤Éú»³êé©ÉÚµÄÝܵå¤B
+ ±êÍV½É¶¬³ê½ii±»³êĢȢjRNV^ÌvpeBð
+ ú»·éÅKÈû@Å·B
+ iá¦Î <literal>persist()</literal> ÉæèjCX^Xði±»µæ¤Æµ½Æ«A
+ HibernateÍ <literal>HashSet</literal> ðHibernateÆ©Ì <literal>Set</literal>
+ ÌÀNXÉu«·¦Ü·B
+ ±Ì½ßAÌæ¤ÈG[ÉÍÓªKvÅ·B
+ </para>
+
+ <programlisting><![CDATA[Cat cat = new DomesticCat();
+Cat kitten = new DomesticCat();
+....
+Set kittens = new HashSet();
+kittens.add(kitten);
+cat.setKittens(kittens);
+session.persist(cat);
+kittens = cat.getKittens(); // Okay, kittens collection is a Set
+(HashSet) cat.getKittens(); // Error!]]></programlisting>
+
+ <para>
+ HibernateÉæèü³ê½i±«RNVÍAC^[tFCX^ɶÄA
+ <literal>HashMap</literal> â <literal>HashSet</literal>A
+ <literal>TreeMap</literal>A <literal>TreeSet</literal>A
+ <literal>ArrayList</literal> Ìæ¤ÉU¢Ü·B
+ </para>
+
+ <para>
+ RNVCX^XÍAl^ƵÄÊÉU¢Ü·B
+ i±»IuWFNgÉQƳê½Æ«É©®IÉi±»³êA
+ QƪÈÈÁ½Æ«É©®IÉí³êÜ·B
+ ൠéi±»IuWFNg©çÊÌi±»IuWFNgÉn³ê½çA
+ »ÌvfÍ»ÝÌe[u©çÊÌe[uÉÚ®·é©àµêܹñB
+ QÂÌGeBeBª¯¶RNVCX^Xð¤LµÄÍ¢¯Ü¹ñB
+ [Vifðx[XɵĢé½ßARNV^ÌvpeBÉ
+ nulllðãüµÄàÓ¡ª èܹñB
+ ÂÜèHibernateÍQÆæÌÈ¢RNVÆóÌRNVðæʵܹñB
+ </para>
+
+ <para>
+ µ©µ»êÙÇSzµÈÄà\¢Ü¹ñB
+ igÁÄ¢éJavaÌRNVƯ¶æ¤ÉAi±»RNVðgÁľ³¢B
+ oûüÖAÌÓ¡ðð·êÎæ¢ÌÅ·i±êÍãÙÇྵܷjB
+ </para>
+
+ </sect1>
+
+ <sect1 id="collections-mapping" revision="4">
+ <title>RNVÌ}bsO</title>
+
+ <para>
+ RNVð}bsO·é½ßÌ}bsOvfÍAC^[tFCXÌ^É˶µÜ·B
+ á¦ÎA<literal><set></literal> vfÍ <literal>Set</literal> ^ð
+ }bsO·é½ßÉg¢Ü·B
+ </para>
+
+ <programlisting><![CDATA[<class name="Product">
+ <id name="serialNumber" column="productSerialNumber"/>
+ <set name="parts">
+ <key column="productSerialNumber" not-null="true"/>
+ <one-to-many class="Part"/>
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ }bsOvfÉÍ <literal><set></literal> Ì¼É <literal><list></literal>A
+ <literal><map></literal>A<literal><bag></literal>A
+ <literal><array></literal>A<literal><primitive-array></literal> ª èÜ·B
+ ã\ƵÄA<literal><map></literal> vfðºLɦµÜ·B
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="mappingcollection1" coords="2 65"/>
+ <area id="mappingcollection2" coords="3 65"/>
+ <area id="mappingcollection3" coords="4 65"/>
+ <area id="mappingcollection4" coords="5 65"/>
+ <area id="mappingcollection5" coords="6 65"/>
+ <area id="mappingcollection6" coords="7 65"/>
+ <area id="mappingcollection7" coords="8 65"/>
+ <area id="mappingcollection8" coords="9 65"/>
+ <area id="mappingcollection9" coords="10 65"/>
+ <area id="mappingcollection10" coords="11 65"/>
+ <area id="mappingcollection11" coords="12 65"/>
+ <area id="mappingcollection12" coords="13 65"/>
+ <area id="mappingcollection13" coords="14 65"/>
+ <area id="mappingcollection14" coords="15 65"/>
+ </areaspec>
+ <programlisting><![CDATA[<map
+ name="propertyName"
+ table="table_name"
+ schema="schema_name"
+ lazy="true|extra|false"
+ inverse="true|false"
+ cascade="all|none|save-update|delete|all-delete-orphan|delete-orphan"
+ sort="unsorted|natural|comparatorClass"
+ order-by="column_name asc|desc"
+ where="arbitrary sql where condition"
+ fetch="join|select|subselect"
+ batch-size="N"
+ access="field|property|ClassName"
+ optimistic-lock="true|false"
+ mutable="true|false"
+ node="element-name|."
+ embed-xml="true|false"
+>
+
+ <key .... />
+ <map-key .... />
+ <element .... />
+</map>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="mappingcollection1">
+ <para>
+ <literal>name</literal> RNV^Å évpeB̼O
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection2">
+ <para>
+ <literal>table</literal> iIvV - ftHgÍvpeB¼jRNVe[u̼O
+ iêνÖAÅÍgpµÜ¹ñjB
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection3">
+ <para>
+ <literal>schema</literal> iIvVje[uXL[}̼OB
+ [gvfÅé¾³êÄ¢éXL[}æèDæ³êÜ·B
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection4">
+ <para>
+ <literal>lazy</literal> iIvV - ftHgÍ <literal>true</literal>j
+ xtFb`ð³øɵAÖAðíɦÉtFb`É·é½ßÉgpµÜ·B
+ ܽÍAuextra-lazyvtFb`ðLøÉ·é½ßÉgpµÜ·B
+ uextra-lazyvtFb`ÍAÙÆñÇÌìÅÍRNVðú»µÜ¹ñ
+ iñíÉå«ÈRNVÉKµÄ¢Ü·jB
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection5">
+ <para>
+ <literal>inverse</literal> iIvV - ftHgÍ <literal>false</literal>j
+ ±ÌRNVªoûüÖAÌutv¤Å éÆ}[NµÜ·B
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection6">
+ <para>
+ <literal>cascade</literal> iIvV - ftHgÍ <literal>none</literal>j
+ qGeBeBÖÌJXP[hìðLøɵܷB
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection7">
+ <para>
+ <literal>sort</literal> iIvVjRNVð©RÈÅ\[g·éêÍ
+ <literal>natural</literal> ðwèµÜ·B
+ é¢ÍComparatorNXðwèµÜ·B
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection8">
+ <para>
+ <literal>order-by</literal> iIvVAJDK1.4ÌÝj
+ <literal>Map</literal>A<literal>Set</literal>AbagÌCe[Vðè`·é
+ e[uJðwè·éƤÉA
+ IvVÆµÄ <literal>asc</literal>A<literal>desc</literal> ðwèµÜ·B
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection9">
+ <para>
+ <literal>where</literal> iIvVjRNVÌõâíÌÛÉg¤
+ CÓÌSQLÌ<literal>WHERE</literal> ððwèµÜ·
+ ipÂ\Èf[^Ìꪾ¯ðRNVªÜÞ׫ƫÉA±êÍLpÅ·jB
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection10">
+ <para>
+ <literal>fetch</literal>iIvV - ftHgÍ <literal>select</literal>j
+ OÉæétFb`AIðtFb`isequential select fetchjA
+ TuZNgtFb`isequential subselect fetchjÌÇê©ðIðµÄ¾³¢B
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection11">
+ <para>
+ <literal>batch-size</literal> iIvV - ftHgÍ <literal>1</literal>j
+ RNVÌCX^XÌxtFb`̽ßÌuob`TCYvðwèµÜ·B
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection12">
+ <para>
+ <literal>access</literal> iIvV - ftHgÍ <literal>property</literal>j
+ RNV^vpeBÌlÉANZX·é½ßÉgp·éíªÅ·B
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection13">
+ <para>
+ <literal>optimistic-lock</literal>iIvV - ftHgÍ <literal>true</literal>j
+ RNVÌóÔðϦé±ÆÉæÁÄA
+ »ÌI[i[Å éGeBeBÌo[WªCNg³êé©ðwèµÜ·B
+ iêνÖAÅÍAÙÆñÇÌêɨ¢Ä³øÉÝè·é̪ÃÅ·Bj
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection14">
+ <para>
+ <literal>mutable</literal>iIvV - ftHgÍ <literal>true</literal>j
+ <literal>false</literal> lÍARNVÌvfªÏX³êÈ¢±Æð\µÜ·
+ i éêÉÍAµptH[}XðßÜ·jB
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <sect2 id="collections-foreignkeys" >
+ <title>RNVÌOL[</title>
+
+ <para>
+ RNVÌCX^XÍAf[^x[XàÅÍA
+ »ÌRNVðL·éGeBeBÌOL[ÉæÁįʳêÜ·B
+ ±ÌOL[ÍRNVe[uÌ <emphasis>RNVL[J</emphasis> ÆÄÎêÜ·B
+ RNVL[JÍ <literal><key></literal> vfÉæè}bsOµÜ·B
+ </para>
+
+ <para>
+ OL[JÉÍnullÝè§ñª é©àµêܹñB
+ ÙÆñÇÌRNVÉÄÍÜéŵå¤B
+ PûüÌêνÖAɨ¢ÄAOL[JÍftHgÅnullð·ÝèÉÈÁĢܷB
+ æÁÄA<literal>not-null="true"</literal> ðwè·éKvª é©àµêܹñB
+ </para>
+
+ <programlisting><![CDATA[<key column="productSerialNumber" not-null="true"/>]]></programlisting>
+
+ <para>
+ OL[̧ñª <literal>ON DELETE CASCADE</literal> ðg¤©àµêܹñB
+ </para>
+
+ <programlisting><![CDATA[<key column="productSerialNumber" on-delete="cascade"/>]]></programlisting>
+
+ <para>
+ <literal><key></literal> vfÌ·×ÄÌè`É¢ÄÍOÌÍðQƵľ³¢B
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-elements" >
+ <title>RNVÌvf</title>
+
+ <para>
+ RNVͼÌHibernateÌ^ÌÙÆñÇi·×ÄÌî{^AJX^^AR|[lgA
+ ¼ÌGeBeBÖÌQÆjði[·é±ÆªÅ«Ü·B
+ Ì_ÍdvÈá¢ÉÈèÜ·B
+ RNVÉi[³ê½IuWFNgªulvZ}eBNXƵĵíêéÌ©
+ iCtTCNÍRNVÌI[i[É®SÉ˶µÜ·jA
+ àµÍ»ê©gÌCtTCNðÁ½ÊÌGeBeBÖÌQÆÅ é©Ì©Æ¢¤á¢Å·B
+ ãÒÍA2ÂÌIuWFNgÔÌuNvðRNVÉ۵ĢéƩȵĢ龯ŷB
+ </para>
+
+ <para>
+ i[³êé^Í <emphasis>RNVvf^</emphasis> ÆÄÎêÜ·B
+ RNVvfÍA<literal><element></literal> Ü½Í <literal><composite-element></literal>
+ Éæè}bsO³êAGeBeBÖÌQÆÌêÉÍ
+ <literal><one-to-many></literal> Ü½Í <literal><many-to-many></literal>
+ Éæè}bsO³êÜ·B
+ ÅÌñÂÍlƵÄvfð}bsOµAÌñÂÍGeBeBÌÖAð}bsO·éÌÉgíêÜ·B
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-indexed">
+ <title>CfbNXt«ÌRNV</title>
+
+ <para>
+ setÆbagðSÄÌRNV}bsOÉÍA
+ RNVe[uÌÉ <emphasis>CfbNXpÌJ</emphasis> ªKvÅ·B
+ »ÌJÉAzñâ <literal>List</literal> ÌCfbNXA
+ àµÍ <literal>Map</literal> ÌL[ð}bsOµÜ·B
+ <literal>Map</literal> ÌCfbNXÍA
+ <literal><map-key></literal> Éæè}bsO³ê½î{^©A
+ <literal><map-key-many-to-many></literal> Éæè}bsO³ê½GeBeBÌÖA©A
+ é¢Í <literal><composite-map-key></literal> Éæè}bsO³ê½R|Wbg^ÉÈèÜ·B
+ zñ©XgÌCfbNXÍAíÉ <literal>integer</literal> ^ÅA
+ <literal><list-index></literal> vfÉæè}bsOµÜ·B
+ }bsO³ê½JÉÍV[PVÈ®ði[µÜ·iftHgÅÍ0©çÔªt¯çêÜ·jB
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="index1" coords="2 45"/>
+ <area id="index2" coords="3 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<list-index
+ column="column_name"
+ base="0|1|..."/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="index1">
+ <para>
+ <literal>column_name</literal>iK{j:RNVCfbNXÌlðÛ·éJ̼OB
+ </para>
+ </callout>
+ <callout arearefs="index2">
+ <para>
+ <literal>base</literal>iIvVAftHgÍ <literal>0</literal>j:
+ XgàµÍzñÌÅÌvfÉY·éCfbNXJÌlB
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <programlistingco>
+ <areaspec>
+ <area id="mapkey1" coords="2 45"/>
+ <area id="mapkey2" coords="3 45"/>
+ <area id="mapkey3" coords="4 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<map-key
+ column="column_name"
+ formula="any SQL expression"
+ type="type_name"
+ node="@attribute-name"
+ length="N"/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="mapkey1">
+ <para>
+ <literal>column</literal>iIvVj: RNVCfbNXÌlðÛ·éJ̼OB
+ </para>
+ </callout>
+ <callout arearefs="mapkey2">
+ <para>
+ <literal>formula</literal>iIvVj: MapÌL[ð]¿·éÌÉgíêéSQL®B
+ </para>
+ </callout>
+ <callout arearefs="mapkey3">
+ <para>
+ <literal>type</literal>iK{j: MapÌL[Ì^B
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <programlistingco>
+ <areaspec>
+ <area id="indexmanytomany1" coords="2 45"/>
+ <area id="indexmanytomany2" coords="3 45"/>
+ <area id="indexmanytomany3" coords="4 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<map-key-many-to-many
+ column="column_name"
+ formula="any SQL expression"
+ class="ClassName"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="indexmanytomany1">
+ <para>
+ <literal>column</literal> iIvVj:
+ RNVCfbNXÌl̽ßÌOL[J̼OB
+ </para>
+ </callout>
+ <callout arearefs="indexmanytomany2">
+ <para>
+ <literal>formula</literal> iIvVj:
+ MapÌL[̽ßÌOL[ð]¿·é½ßÉg¤SQL®B
+ </para>
+ </callout>
+ <callout arearefs="indexmanytomany3">
+ <para>
+ <literal>class</literal> iK{j:
+ MapÌL[ƵÄgíêéGeBeBNXB
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+
+ <para>
+ àµe[uÉCfbNXJªÈÄàAvpeB^ÆµÄ <literal>List</literal> ðg¢½¯êÎA
+ HibernateÌ <emphasis><bag></emphasis> ƵÄvpeBð}bsOµÜ·B
+ bagÍf[^x[X©ç³³êéAð۵ܹñB
+ µ©µAiãÅj\[gµ½èAiSQLÅjt¯µ½èiorder byj·é±ÆàÅ«Ü·B
+ </para>
+
+ </sect2>
+
+ <para>
+ ½ÌêÊIÈ[VifðJo[µ½½ßÉA
+ RNV̽ßÉpÅ«é}bsOÉÍ©Èè̪ èÜ·B
+ lXÈ}bsO龪ÇÌæ¤Éf[^x[Xe[uÉÏ·³êé©ðmé½ßÉA
+ XL[}¶¬c[ðgÁÄÝéÆǢŵå¤B
+ </para>
+
+ <sect2 id="collections-ofvalues" revision="2">
+ <title>lÌRNVƽνÖA</title>
+
+ <para>
+ lÌRNVâ½Î½ÖAÍAêpÌ <emphasis>RNVe[u</emphasis> ªKvÅ·B
+ ±Ìe[uÍAOL[JÆA
+ <emphasis>RNVvfÌJ</emphasis> ÆA
+ êÉæÁÄÍCfbNXJð¿Ü·B
+ </para>
+
+ <para>
+ lÌRNV̽ßÉA<literal><element></literal>^OðgpµÜ·B
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="element1b" coords="2 50"/>
+ <area id="element2b" coords="3 50"/>
+ <area id="element3b" coords="4 50"/>
+ </areaspec>
+ <programlisting><![CDATA[<element
+ column="column_name"
+ formula="any SQL expression"
+ type="typename"
+ length="L"
+ precision="P"
+ scale="S"
+ not-null="true|false"
+ unique="true|false"
+ node="element-name"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="element1b">
+ <para>
+ <literal>column</literal> iIvVj: RNVÌvfÌlðÛ·éJ̼OB
+ </para>
+ </callout>
+ <callout arearefs="element2b">
+ <para>
+ <literal>formula</literal> iIvVj: vfð]¿·é½ßÉg¤SQL®B
+ </para>
+ </callout>
+ <callout arearefs="element3b">
+ <para>
+ <literal>type</literal> iK{jRNVÌvfÌ^B
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ <emphasis>½Î½ÖA</emphasis> Í <literal><many-to-many></literal> vfÅwèµÜ·B
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="manytomany1" coords="2 60"/>
+ <area id="manytomany2" coords="3 60"/>
+ <area id="manytomany3" coords="4 60"/>
+ <area id="manytomany4" coords="5 60"/>
+ <area id="manytomany5" coords="6 60"/>
+ <area id="manytomany6" coords="7 60"/>
+ <area id="manytomany7" coords="8 60"/>
+ <area id="manytomany8" coords="9 60"/>
+ </areaspec>
+ <programlisting><![CDATA[<many-to-many
+ column="column_name"
+ formula="any SQL expression"
+ class="ClassName"
+ fetch="select|join"
+ unique="true|false"
+ not-found="ignore|exception"
+ entity-name="EntityName"
+ property-ref="propertyNameFromAssociatedClass"
+ node="element-name"
+ embed-xml="true|false"
+ />]]></programlisting>
+ <calloutlist>
+ <callout arearefs="manytomany1">
+ <para>
+ <literal>column</literal> iIvVj: OL[JÌvf̼OB
+ </para>
+ </callout>
+ <callout arearefs="manytomany2">
+ <para>
+ <literal>formula</literal> iIvVj: OL[lÌvfð]¿·é½ßÉg¤SQL®B
+ </para>
+ </callout>
+ <callout arearefs="manytomany3">
+ <para>
+ <literal>class</literal> iK{j: ÖANX̼OB
+ </para>
+ </callout>
+ <callout arearefs="manytomany4">
+ <para>
+ <literal>fetch</literal> (IvV - ftHgÍ <literal>join</literal>):
+ ÖA̽ßÉAO©IðtFb`ðLøɵܷB
+ ±êÍÁêÈP[XÅ·ªAGeBeBƼÌGeBeBÆ̽νÖWð
+ iPÂÌ <literal>SELECT</literal> Éæèj®SɦÉtFb`·é½ßÉÍA
+ »ÌRNV©Ì¾¯ÅÈA
+ lXgµ½vfÅ é <literal><many-to-many></literal> ̱̮«É¢Äà
+ <literal>join</literal> tFb`ðLø·éKvª èÜ·B
+ </para>
+ </callout>
+ <callout arearefs="manytomany5">
+ <para>
+ <literal>unique</literal> iIvVj:
+ OL[JÌj[N§ñÌDDL¶¬ðLøɵܷB
+ ±êÍAÖA̽dxðÀãêνɵܷB
+ </para>
+ </callout>
+ <callout arearefs="manytomany6">
+ <para>
+ <literal>not-found</literal> iIvV - ftHgÍ <literal>exception</literal>j
+ QÆæÌsªÈ¢OL[ðÇÌæ¤Éµ¤©ðwèµÜ·B
+ <literal>ignore</literal> É·éÆAsªÈ¢±ÆðÖAª³¢àÌƵĵ¢Ü·B
+ </para>
+ </callout>
+ <callout arearefs="manytomany7">
+ <para>
+ <literal>entity-name</literal> iIvVj:
+ <literal>class</literal> ÌãÖÅ éÖANXÌGeBeB¼B
+ <literal>class</literal> ÌãíèÉwè·éAÖANXÌGeBeB¼B
+ </para>
+ </callout>
+ <callout arearefs="manytomany8">
+ <para>
+ <literal>property-ref</literal>: iIvVj
+ ±ÌOL[É·éÖANXÌvpeB¼B
+ wèµÈ©Á½êÍAÖANXÌåL[ðg¢Ü·B
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Ⱥɢ©áð¦µÜ·B
+ ܸÍStringÌsetÉÖµÄÌáÅ·B
+ </para>
+
+ <programlisting><![CDATA[<set name="names" table="person_names">
+ <key column="person_id"/>
+ <element column="person_name" type="string"/>
+</set>]]></programlisting>
+
+ <para>
+ ®lðÜÞbagibagÍ <literal>order-by</literal> ®«ÉæÁĽªè`³êĢܷj
+ </para>
+
+ <programlisting><![CDATA[<bag name="sizes"
+ table="item_sizes"
+ order-by="size asc">
+ <key column="item_id"/>
+ <element column="size" type="integer"/>
+</bag>]]></programlisting>
+
+ <para>
+ GeBeBÌzñ - ±ÌêA½Î½ÌÖAÅ·B
+ </para>
+
+ <programlisting><![CDATA[<array name="addresses"
+ table="PersonAddress"
+ cascade="persist">
+ <key column="personId"/>
+ <list-index column="sortOrder"/>
+ <many-to-many column="addressId" class="Address"/>
+</array>]]></programlisting>
+
+ <para>
+ ¶ñÆútÌmap
+ </para>
+
+ <programlisting><![CDATA[<map name="holidays"
+ table="holidays"
+ schema="dbo"
+ order-by="hol_name asc">
+ <key column="id"/>
+ <map-key column="hol_name" type="string"/>
+ <element column="hol_date" type="date"/>
+</map>]]></programlisting>
+
+ <para>
+ R|[lgÌlistiÌÍÅڵྵܷj
+ </para>
+
+ <programlisting><![CDATA[<list name="carComponents"
+ table="CarComponents">
+ <key column="carId"/>
+ <list-index column="sortOrder"/>
+ <composite-element class="CarComponent">
+ <property name="price"/>
+ <property name="type"/>
+ <property name="serialNumber" column="serialNum"/>
+ </composite-element>
+</list>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="collections-onetomany">
+ <title>êνÖA</title>
+
+ <para>
+ <emphasis>êνÖA</emphasis> ÍARNV¥e[uðA
+ OL[Éæè2ÂÌNXÌe[uðÖAt¯Ü·B
+ ±Ì}bsOÍWIÈJavaÌRNVÌZ}eBNXð¢Â©¸¢Ü·B
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ GeBeBNXÌCX^XÍA
+ 2ÂÈãÌRNVÌCX^XÉ®µÄÍ¢¯Ü¹ñB
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ RNVÉÜÜêéGeBeBNXÌCX^XÍA
+ RNVCfbNXÌlƵÄ2xÈã»êÄÍ¢¯Ü¹ñB
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ <literal>Product</literal> ©ç <literal>Part</literal> ÖÌÖAÍA
+ <literal>Part</literal> e[uÖÌOL[JÆAêÉæÁÄÍCfbNXJªKvÅ·B
+ <literal><one-to-many></literal> ^OÍA±êªêνÖAÅ é±Æð\µÄ¢Ü·B
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="onetomany1" coords="2 60"/>
+ <area id="onetomany2" coords="3 60"/>
+ <area id="onetomany3" coords="4 60"/>
+ </areaspec>
+ <programlisting><![CDATA[<one-to-many
+ class="ClassName"
+ not-found="ignore|exception"
+ entity-name="EntityName"
+ node="element-name"
+ embed-xml="true|false"
+ />]]></programlisting>
+ <calloutlist>
+ <callout arearefs="onetomany1">
+ <para>
+ <literal>class</literal> iK{j: ÖANX̼OB
+ </para>
+ </callout>
+ <callout arearefs="onetomany2">
+ <para>
+ <literal>not-found</literal> iIvV - ftHgÍ <literal>exception</literal>j:
+ QÆæÌsªÈ¢LbV
³ê½¯ÊqðÇÌæ¤Éµ¤©ðwèµÜ·B
+ <literal>ignore</literal> ðwè·éÆAsªÈ¢±ÆðÖAªÈ¢àÌƵĵ¢Ü·B
+ </para>
+ </callout>
+ <callout arearefs="onetomany3">
+ <para>
+ <literal>entity-name</literal> iIvVj:
+ <literal>class</literal> ÌãÖÅ éÖANXÌGeBeB¼B
+ <literal>class</literal> ÌãíèÉwè·éAÖANXÌGeBeB¼B
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ <literal><one-to-many></literal> vfÍJðé¾·éKvªÈ¢±ÆÉӵľ³¢B
+ ¯lÉ <literal>e[u</literal> ¼ðwè·éKvà èܹñB
+ </para>
+
+ <para>
+ <emphasis>ÆÄàdvÈÓ:</emphasis> ൠ<literal><one-to-many></literal>
+ ÖAÌOL[Jª <literal>NOT NULL</literal>Æé¾³ê½êA
+ <literal><key></literal> }bsOÉ <literal>not-null="true"</literal> ðé¾·é©A
+ RNV}bsOÉ <literal>inverse="true"</literal> ðt¯½ãÅA
+ <emphasis>oûüÖAðg¤</emphasis> Kvª èÜ·B
+ oûüÖAÉ¢ÄͱÌÍÌãÌÙ¤ÅྵܷB
+ </para>
+
+ <para>
+ ÌáÍA¼Ìi<literal>Part</literal> Ìi±IÈvpeBÅ é <literal>partName</literal>j
+ Éæé <literal>Part</literal> GeBeBÌmapð\µÄ¢Ü·B
+ formulaÉæéCfbNXðgÁÄ¢é±ÆÉӵľ³¢B
+ </para>
+
+ <programlisting><![CDATA[<map name="parts"
+ cascade="all">
+ <key column="productId" not-null="true"/>
+ <map-key formula="partName"/>
+ <one-to-many class="Part"/>
+</map>]]></programlisting>
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="collections-advancedmappings">
+ <title>xÈRNV¥}bsO</title>
+
+ <sect2 id="collections-sorted" revision="2">
+ <title>\[g³ê½RNV</title>
+
+ <para>
+ HibernateÍ <literal>java.util.SortedMap</literal> Æ <literal>java.util.SortedSet</literal>
+ ðÀµ½RNVðT|[gµÄ¢Ü·B
+ JÒÍ}bsOè`t@CÉRp[^ðwèµÈ¯êÎÈèܹñB
+ </para>
+
+ <programlisting><![CDATA[<set name="aliases"
+ table="person_aliases"
+ sort="natural">
+ <key column="person"/>
+ <element column="name" type="string"/>
+</set>
+
+<map name="holidays" sort="my.custom.HolidayComparator">
+ <key column="year_id"/>
+ <map-key column="hol_name" type="string"/>
+ <element column="hol_date" type="date"/>
+</map>]]></programlisting>
+
+ <para>
+ <literal>sort</literal> ®«ÉÝèÅ«élÍ <literal>unsorted</literal> Æ <literal>natural</literal>
+ ¨æÑA<literal>java.util.Comparator</literal> ðÀµ½NX̼OÅ·B
+ </para>
+
+ <para>
+ \[g³ê½RNVÍÀ¿IÉÍ <literal>java.util.TreeSet</literal> â
+ <literal>java.util.TreeMap</literal> Ìæ¤ÉU¢Ü·B
+ </para>
+
+ <para>
+ àµf[^x[X©gÉRNVÌvfðÀ׳¹½¢ÈçA
+ <literal>set</literal> â <literal>bag</literal>A<literal>map</literal> Ì
+ <literal>order-by</literal> ®«ðg¢Ü·B
+ ±Ìð@ÍJDK1.4AàµÍ»êÈãÌo[WÅpÂ\Å·
+ i<literal>LinkedHashSet</literal> Ü½Í <literal>LinkedHashMap</literal>ðgÁÄÀ³êĢܷjB
+ ®ñÍãÅÍÈASQLNGàÅÀs³êÜ·B
+ </para>
+
+ <programlisting><![CDATA[<set name="aliases" table="person_aliases" order-by="lower(name) asc">
+ <key column="person"/>
+ <element column="name" type="string"/>
+</set>
+
+<map name="holidays" order-by="hol_date, hol_name">
+ <key column="year_id"/>
+ <map-key column="hol_name" type="string"/>
+ <element column="hol_date" type="date"/>
+</map>]]></programlisting>
+
+ <para>
+ <literal>order-by</literal> ®«ÌlªSQL½ßÅ ÁÄAHQL½ßÅÍÈ¢±ÆÉӵľ³¢I
+ </para>
+
+ <para>
+ ÖAÍARNVÌ <literal>filter()</literal> ðg¤±ÆÅA
+ ÀsÉCÓÌcriteriaÉæÁÄ\[g·é±ÆàÂ\Å·B
+ </para>
+
+ <programlisting><![CDATA[sortedUsers = s.createFilter( group.getUsers(), "order by this.name" ).list();]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="collections-bidirectional" revision="1">
+ <title>oûüÖA</title>
+
+ <para>
+ <emphasis>oûüÖA</emphasis> ÍÖAÌÇ¿çu¤v©çÅàirQ[VÅ«Ü·B
+ 2íÞÌoûüÖAªT|[g³êĢܷB
+
+ <variablelist>
+ <varlistentry>
+ <term>one-to-many</term>
+ <listitem>
+ <para>
+ Фªset©bagAà¤ÐûªPêlÅ·B
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>many-to-many</term>
+ <listitem>
+ <para>
+ ¼¤ªset©bagÅ·B
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ </para>
+
+ <para>
+ 2Â̽νÖAů¶f[^x[Xe[uð}bsOµA
+ Ðûð <emphasis>inverse</emphasis> ƵÄé¾·é±ÆÅA
+ oûü̽νÖAðwè·é±ÆªoÜ·
+ iÇ¿çðinverseÉIñ¾ÆµÄàA»¿ç¤ÉÍCfbNXt«ÌRNVÍg¦Ü¹ñjB
+ </para>
+
+ <para>
+ Éoûü̽νÖAÌáð¦µÜ·B
+ eJeSͽÌACeð±ƪūAeACeͽÌJeSÉ®·é±ÆªoÜ·B
+ </para>
+
+ <programlisting><![CDATA[<class name="Category">
+ <id name="id" column="CATEGORY_ID"/>
+ ...
+ <bag name="items" table="CATEGORY_ITEM">
+ <key column="CATEGORY_ID"/>
+ <many-to-many class="Item" column="ITEM_ID"/>
+ </bag>
+</class>
+
+<class name="Item">
+ <id name="id" column="CATEGORY_ID"/>
+ ...
+
+ <!-- inverse end -->
+ <bag name="categories" table="CATEGORY_ITEM" inverse="true">
+ <key column="ITEM_ID"/>
+ <many-to-many class="Category" column="CATEGORY_ID"/>
+ </bag>
+</class>]]></programlisting>
+
+ <para>
+ ÖAÌinverse¤ÉÌÝsíê½ÏXÍi±» <emphasis>³êܹñ</emphasis>B
+ ±êÍAHibernateÍSÄÌoûüÖAÉ¢ÄAãÉQÂÌ\»ðÁÄ¢éÆ¢¤Ó¡Å·B
+ ÂÜèêÂÍA©çBÖÌNÅAà¤êÂÍB©çAÖÌNÆ¢¤±ÆÅ·B
+ JavaÌIuWFNgfÉ¢Äl¦AJavaÅoûüÖWðǤâÁÄìé©ðl¦êÎA
+ ±êÍðµâ·¢Å·BºLÉAJavaÅÌoûüÖAð¦µÜ·B
+ </para>
+
+ <programlisting><![CDATA[
+category.getItems().add(item); // The category now "knows" about the relationship
+item.getCategories().add(category); // The item now "knows" about the relationship
+
+session.persist(item); // The relationship won't be saved!
+session.persist(category); // The relationship will be saved]]></programlisting>
+
+ <para>
+ ÖAÌinverseÅÍÈ¢¤ÍAãÌ\»ðf[^x[XÉÛ¶·éÌÉgíêÜ·B
+ </para>
+
+ <para>
+ oûüÌêνÖAðè`·éÉÍA
+ êνÖAð½ÎêÖAƯ¶e[uÌJÉ}bsOµA
+ ½¤É <literal>inverse="true"</literal> Æ龵ܷB
+ </para>
+
+ <programlisting><![CDATA[<class name="Parent">
+ <id name="id" column="parent_id"/>
+ ....
+ <set name="children" inverse="true">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+ </set>
+</class>
+
+<class name="Child">
+ <id name="id" column="child_id"/>
+ ....
+ <many-to-one name="parent"
+ class="Parent"
+ column="parent_id"
+ not-null="true"/>
+</class>]]></programlisting>
+
+ <para>
+ ÖAÌÐ¤É <literal>inverse="true"</literal> ðÝèµÄàA
+ JXP[hìÉe¿ð^¦Ü¹ñB±êçͼðµ½TOÅ·I
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-indexedbidirectional">
+ <title>CfbNXt«RNVÆoûüÖA</title>
+ <para>
+ Фª <literal><list></literal> â <literal><map></literal> Å é
+ oûüÖAÍAÁÉæl¦éKvª èÜ·B
+ CfbNXJÉ}bv³êéqNXÌvpeBª éêÍAâèȢŷB
+ RNVÌ}bsOÅ <literal>inverse="true"</literal> ðg¢±¯çêÜ·B
+ </para>
+
+ <programlisting><![CDATA[<class name="Parent">
+ <id name="id" column="parent_id"/>
+ ....
+ <map name="children" inverse="true">
+ <key column="parent_id"/>
+ <map-key column="name"
+ type="string"/>
+ <one-to-many class="Child"/>
+ </map>
+</class>
+
+<class name="Child">
+ <id name="id" column="child_id"/>
+ ....
+ <property name="name"
+ not-null="true"/>
+ <many-to-one name="parent"
+ class="Parent"
+ column="parent_id"
+ not-null="true"/>
+</class>]]></programlisting>
+
+ <para>
+ µ©µAqNXÉ»Ìæ¤ÈvpeBªÈ¢êÍA
+ ÖAð^ÉoûüÅ éÆl¦é±ÆªÅ«Ü¹ñ
+ iÖAÌФÉpÅ«éîñª èÜ·ªAà¤êûÉÍ èܹñjB
+ ±ÌêÍARNVÉ <literal>inverse="true"</literal> ð}bsOūܹñB
+ ãíèÉAÌæ¤È}bsOªg¦Ü·B
+ </para>
+
+ <programlisting><![CDATA[<class name="Parent">
+ <id name="id" column="parent_id"/>
+ ....
+ <map name="children">
+ <key column="parent_id"
+ not-null="true"/>
+ <map-key column="name"
+ type="string"/>
+ <one-to-many class="Child"/>
+ </map>
+</class>
+
+<class name="Child">
+ <id name="id" column="child_id"/>
+ ....
+ <many-to-one name="parent"
+ class="Parent"
+ column="parent_id"
+ insert="false"
+ update="false"
+ not-null="true"/>
+</class>]]></programlisting>
+
+ <para>
+ ÓF ±Ì}bsOÅÍAÖAÌRNVl̤ÍA
+ OL[ðAbvf[g·éÓCª èÜ·B
+ <!-- TODO: Does this really result in some unnecessary update statements? -->
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-ternary">
+ <title>3ÖA</title>
+
+ <para>
+ 3ÖAÌ}bsOÉÍ3ÂÌAv[`ª èÜ·B
+ 1ÂÚÍÖAðCfbNXÆµÄ <literal>Map</literal> ðgp·éAv[`Å·B
+ </para>
+
+ <programlisting><![CDATA[<map name="contracts">
+ <key column="employer_id" not-null="true"/>
+ <map-key-many-to-many column="employee_id" class="Employee"/>
+ <one-to-many class="Contract"/>
+</map>]]></programlisting>
+
+ <programlisting><![CDATA[<map name="connections">
+ <key column="incoming_node_id"/>
+ <map-key-many-to-many column="outgoing_node_id" class="Node"/>
+ <many-to-many column="connection_id" class="Connection"/>
+</map>]]></programlisting>
+
+ <para>
+ 2ÂÚÍPÉÖAðGeBeB¥NXƵÄfðìè¼·Av[`ÅA
+ pÉÉgíêÜ·B
+ </para>
+
+ <para>
+ ÅãÍcompositevfðg¤Av[`Å·B±êÉÖ·éc_ÍãÙÇs¢Ü·B
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-idbag" revision="1">
+ <title><literal><idbag></literal>Ìgp</title>
+
+ <para>
+ ¡L[Í«¢àÌÅAGeBeBÍlH̯Êq(ãL[)ðÂ׫ŠéÆ¢¤l¦û©çÍA
+ ½Î½ÖAÆlÌRNVð¡L[ðp¢½e[uÖ}bsO·éÌ͵ïÉ´¶é©àµêܹñI
+ m©É±Ìl¦ûÉÍc_Ì]nª èÜ·B
+ ÈÖAe[uÍãL[ðgÁÄàvð¾çêÈ¢æ¤Év¦é©çÅ·
+ i¬lÌRNVÍvª é <emphasis>©à</emphasis> µêܹñªjB
+ ÆÍ¢¦AHibernateÍãL[ðÂe[uֽνÖAÆlÌRNVð
+ }bsO·é@\àõ¦Ä¢Ü·B
+ </para>
+
+ <para>
+ bagÌZ}eBbNXðÁ½ <literal>List</literal>iÜ½Í <literal>Collection</literal>jð
+ <literal><idbag></literal> vfÉ}bsOÅ«Ü·B
+ </para>
+
+<programlisting><![CDATA[<idbag name="lovers" table="LOVERS">
+ <collection-id column="ID" type="long">
+ <generator class="sequence"/>
+ </collection-id>
+ <key column="PERSON1"/>
+ <many-to-many column="PERSON2" class="Person" fetch="join"/>
+</idbag>]]></programlisting>
+
+ <para>
+ ²¶mÌæ¤É <literal><idbag></literal> ÍGeBeB¥NXÌæ¤É
+ lHIÈidWFl[^ðÁĢܷI
+ ÙÈéãL[ð»ê¼êÌRNVÌñÉèÄÜ·B
+ µ©µÈªçAHibernateÍ ésÌãL[Ìlð©Â¯o·@\ðÁĢܹñB
+ </para>
+
+ <para>
+ <literal><idbag></literal> ðXV·éptH[}XÍ
+ ÊíÌ <literal><bag></literal> æèàÇ¢±ÆÉڵľ³¢I
+ HibernateÍÂXÌsðøÊIɩ¯é±ÆªÅ«A
+ listâmapAsetÌæ¤ÉÂÊÉ»ÌsðXVAíÅ«Ü·B
+ </para>
+
+ <para>
+ »ÝÌÀÅÍA<literal>native</literal> Æ¢¤id¶¬íªð
+ <literal><idbag></literal> RNV̯ÊqÉεÄg¦Ü¹ñB
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <!--undocumenting this stuff -->
+
+ <!--sect1 id="collections-heterogeneous">
+ <title>Heterogeneous Associations</title>
+
+ <para>
+ The <literal><many-to-any></literal> and <literal><index-many-to-any></literal>
+ elements provide for true heterogeneous associations. These mapping elements work in the
+ same way as the <literal><any></literal> element - and should also be used
+ rarely, if ever.
+ </para>
+
+ </sect1-->
+
+ <sect1 id="collections-example" revision="1">
+ <title>RNVÌá</title>
+
+ <para>
+ ±êÜÅÌßÌà¾ÅÍðµÉ¢ÌÅAȺÌáð©Ä¾³¢B
+ </para>
+
+ <programlisting><![CDATA[package eg;
+import java.util.Set;
+
+public class Parent {
+ private long id;
+ private Set children;
+
+ public long getId() { return id; }
+ private void setId(long id) { this.id=id; }
+
+ private Set getChildren() { return children; }
+ private void setChildren(Set children) { this.children=children; }
+
+ ....
+ ....
+}]]></programlisting>
+
+ <para>
+ ±ÌNXÍ <literal>Child</literal> CX^XÌRNVðÁĢܷB
+ àµeXÌchildªÅåÅàêÂÌparentðÁÄ¢éÈçÎAÅà©RÈ}bsOÍêνÖAÅ·B
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Parent">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <set name="children">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+ </set>
+ </class>
+
+ <class name="Child">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ ±êÍȺÌe[uè`É}bsOµÜ·B
+ </para>
+
+ <programlisting><![CDATA[create table parent ( id bigint not null primary key )
+create table child ( id bigint not null primary key, name varchar(255), parent_id bigint )
+alter table child add constraint childfk0 (parent_id) references parent]]></programlisting>
+
+ <para>
+ àµparentª <emphasis>v</emphasis> ³êéÈçAoûüÌêνÖAðgpµÄ¾³¢B
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Parent">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <set name="children" inverse="true">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+ </set>
+ </class>
+
+ <class name="Child">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <property name="name"/>
+ <many-to-one name="parent" class="Parent" column="parent_id" not-null="true"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ <literal>NOT NULL</literal> §ñÉӵľ³¢B
+ </para>
+
+ <programlisting><![CDATA[create table parent ( id bigint not null primary key )
+create table child ( id bigint not null
+ primary key,
+ name varchar(255),
+ parent_id bigint not null )
+alter table child add constraint childfk0 (parent_id) references parent]]></programlisting>
+
+ <para>
+ é¢ÍA൱ÌÖAÍPûüÅ é׫Æ壷éÌÅ êÎA
+ <literal><key></literal> É <literal>NOT NULL</literal> §ñðé¾Å«Ü·B
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Parent">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <set name="children">
+ <key column="parent_id" not-null="true"/>
+ <one-to-many class="Child"/>
+ </set>
+ </class>
+
+ <class name="Child">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ êûÅAàµchildª¡ÌparentðÄéÈçÎA½Î½ÖAªÃÅ·B
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Parent">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <set name="children" table="childset">
+ <key column="parent_id"/>
+ <many-to-many class="Child" column="child_id"/>
+ </set>
+ </class>
+
+ <class name="Child">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ e[uè`ÍȺÌæ¤ÉÈèÜ·B
+ </para>
+
+ <programlisting><![CDATA[create table parent ( id bigint not null primary key )
+create table child ( id bigint not null primary key, name varchar(255) )
+create table childset ( parent_id bigint not null,
+ child_id bigint not null,
+ primary key ( parent_id, child_id ) )
+alter table childset add constraint childsetfk0 (parent_id) references parent
+alter table childset add constraint childsetfk1 (child_id) references child]]></programlisting>
+
+ <para>
+ parent/childÖWÌ}bsOÉ¢ÄÌæè½Ìáâ®SÈèªKvÅ êÎA
+ <xref linkend="example-parentchild"/> ð²¾³¢B
+ </para>
+
+ <para>
+ ܽA³çÉÁêÈÖA}bsOàÂ\Å·BÌÍÅÚµq×Ü·B
+ </para>
+
+ </sect1>
+
+</chapter>
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/component_mapping.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/component_mapping.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/component_mapping.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,396 @@
+<?xml version="1.0" encoding="Shift_JIS"?>
+<chapter id="components">
+ <title>R|[lgÌ}bsO</title>
+
+ <para>
+ <emphasis>R|[lg</emphasis> ÌTOÍAHibernateðʵÄlXÈóµÌÅ
+ ÙÈéÚI̽ßÉÄp³êÜ·B
+ </para>
+
+ <sect1 id="components-dependentobjects" revision="2" >
+ <title>˶IuWFNg</title>
+
+ <para>
+ R|[lgÍAGeBeBÌQÆÅÍÈl^ƵÄi±»³ê½A
+ ïܳê½IuWFNgÅ·BR|[lgÆ¢¤¾tÉ¢ÄÍAR|WVÆ¢¤
+ IuWFNgwüÌTOðQƵľ³¢iA[LeN`xÌR|[lgÅÍ èܹñjB
+ á¦ÎAȺÌPersonfÌæ¤ÈàÌÅ·B
+ </para>
+
+ <programlisting><![CDATA[public class Person {
+ private java.util.Date birthday;
+ private Name name;
+ private String key;
+ public String getKey() {
+ return key;
+ }
+ private void setKey(String key) {
+ this.key=key;
+ }
+ public java.util.Date getBirthday() {
+ return birthday;
+ }
+ public void setBirthday(java.util.Date birthday) {
+ this.birthday = birthday;
+ }
+ public Name getName() {
+ return name;
+ }
+ public void setName(Name name) {
+ this.name = name;
+ }
+ ......
+ ......
+}]]></programlisting>
+
+<programlisting><![CDATA[public class Name {
+ char initial;
+ String first;
+ String last;
+ public String getFirst() {
+ return first;
+ }
+ void setFirst(String first) {
+ this.first = first;
+ }
+ public String getLast() {
+ return last;
+ }
+ void setLast(String last) {
+ this.last = last;
+ }
+ public char getInitial() {
+ return initial;
+ }
+ void setInitial(char initial) {
+ this.initial = initial;
+ }
+}]]></programlisting>
+
+ <para>
+ ¢ÜA<literal>Name</literal> Í <literal>Person</literal> ÌR|[lgƵÄ
+ i±»·é±ÆªoÜ·B±±Å <literal>Name</literal> Íi±»®«ÉεÄgetterA
+ setter\bhðè`µÜ·ªAC^[tFCXâ¯ÊqvpeBðè`·éKvª
+ È¢±ÆÉӵĺ³¢B
+ </para>
+
+ <para>
+ }bsOè`ÍȺÌæ¤ÉÈèÜ·B
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Person" table="person">
+ <id name="Key" column="pid" type="string">
+ <generator class="uuid"/>
+ </id>
+ <property name="birthday" type="date"/>
+ <component name="Name" class="eg.Name"> <!-- class attribute optional -->
+ <property name="initial"/>
+ <property name="first"/>
+ <property name="last"/>
+ </component>
+</class>]]></programlisting>
+
+ <para>
+ Persone[uÍ <literal>pid</literal>A
+ <literal>birthday</literal>A
+ <literal>initial</literal>A
+ <literal>first</literal>A
+ <literal>last</literal> Jð¿Ü·B
+ </para>
+
+ <para>
+ SÄÌl^Ìæ¤ÉAR|[lgÍQÆ̤Lð·é±ÆªÅ«Ü¹ñB
+ ¾¢·¦éÆAñlÌPersonͯ¶¼Oð±ƪūܷªAñÂÌPersonIuWFNgÍ
+ "lª¯¶¾¯"ÌÊXÌnameIuWFNgðÜñÅ¢éÆ¢¤±ÆÅ·B
+ R|[lgÌnulllÌZ}eBNXÍ <emphasis>AhzbN</emphasis> Å·B
+ R|[lgÌIuWFNgðÄÇÝÝ·éÛAHibernateÍR|[lgÌ·×ÄÌJª
+ nullÅ éÈçR|[lg©ÌªnullÅ éÆl¦Ü·B
+ ±êÍåïÌêâè èܹñB
+
+ </para>
+
+ <para>
+ R|[lgÌ®«ÍÇñÈHibernateÌ^Åà\¢Ü¹ñiRNVAmany-to-oneÖAA
+ ¼ÌR|[lgÈÇjBlXg³ê½R|[lgÍŽÉgíÈ¢Æl¦é׫ÅÍ
+ <emphasis> èܹñ</emphasis> BHibernateÍñíÉ«ßÌש¢IuWFNgfðT|[g·éæ¤ÉÓ}³êĢܷB
+ </para>
+
+ <para>
+ <literal><component></literal> vfÍeGeBeBÖÌtQÆƵÄAR|[lgNXÌ
+ ®«ð}bsO·é <literal><parent></literal> TuvfðgpÅ«Ü·B
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Person" table="person">
+ <id name="Key" column="pid" type="string">
+ <generator class="uuid"/>
+ </id>
+ <property name="birthday" type="date"/>
+ <component name="Name" class="eg.Name" unique="true">
+ <parent name="namedPerson"/> <!-- reference back to the Person -->
+ <property name="initial"/>
+ <property name="first"/>
+ <property name="last"/>
+ </component>
+</class>]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="components-incollections" revision="1">
+
+ <title>]®·éIuWFNgÌRNV</title>
+
+ <para>
+ HibernateÍR|[lgÌRNVðT|[gµÄ¢Ü·iá¦Î <literal>Name</literal> ^ÌzñjB
+ <literal><element></literal> ^Oð <literal><composite-element></literal> ^OÉæèÖ¦é±ÆÉæè
+ R|[lgRNVð龵ľ³¢B
+ </para>
+
+ <programlisting><![CDATA[<set name="someNames" table="some_names" lazy="true">
+ <key column="id"/>
+ <composite-element class="eg.Name"> <!-- class attribute required -->
+ <property name="initial"/>
+ <property name="first"/>
+ <property name="last"/>
+ </composite-element>
+</set>]]></programlisting>
+
+ <para>
+ Ó: R|WbgGgÌ <literal>Set</literal> ðè`µ½ÈçA
+ <literal>equals()</literal> Æ <literal>hashCode()</literal> ð³µÀ·é±ÆªdvÅ·B
+ </para>
+
+ <para>
+ R|WbgGgÍRNVðÜܸAR|[lgðÜÞ±Æà èÜ·B
+ R|WbgGg©gªR|[lgðÜñÅ¢éêÍ <literal><nested-composite-element></literal> ð
+ gpµÄ¾³¢BR|[lgÌRNV©gªR|[lgðÂÆ¢¤P[XÍßÁ½É èܹñB
+ ±ÌiKÜÅÉAone-to-manyÖAÌûªæèKØÅÈ¢©ÆnlµÄ¾³¢B
+ R|WbgGgðGeBeBƵÄÄxfOµÄÝľ³¢B
+ µ©µ±êÍJavaÌfƵÄͯ¶Å·ªA[VifÆi±®ìÍܾá±ÙÈé±ÆÉӵľ³¢B
+ </para>
+
+ <para>
+ ൠ<literal><set></literal> ðgp·éÌÅ êÎAR|WbgGgÌ}bsOªnulllªÂ\È
+ ®«ðT|[gµÄ¢È¢±ÆÉӵľ³¢BHibernateÍIuWFNgðí·éÆ«A
+ R[hð¯Ê·é½ßÉ»ê¼êÌJÌlðgp·éKvª é½ßAnulllð±ƪoܹñ
+ iR|WbgGge[uÉÍÊÌåL[JÍ èܹñjB
+ R|WbgGgÉnot-nullÌ®«ÌÝðgp·é©AÜ½Í <literal><list></literal>A<literal><map></literal>A
+ <literal><bag></literal>A<literal><idbag></literal> ðIð·éKvª èÜ·B
+ </para>
+
+ <para>
+ R|WbgGgÌÁÊÈP[XƵÄAlXg³ê½ <literal><many-to-one></literal> ®«ðÂ
+ R|WbgGgª èÜ·B
+ ±Ì}bsOÍAR|WbgGgNXð½Î½ÖAe[uÌ
+ ]ªÈJÖ}bsOµÜ·B
+ ÌáÍ <literal>Order</literal> ©çA<literal>Item</literal> Ö̽νÖAÅ·B
+ <literal>purchaseDate</literal>A<literal>price</literal>A<literal>quantity</literal> ÍÖAÌ®«ÆÈèÜ·B
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Order" .... >
+ ....
+ <set name="purchasedItems" table="purchase_items" lazy="true">
+ <key column="order_id">
+ <composite-element class="eg.Purchase">
+ <property name="purchaseDate"/>
+ <property name="price"/>
+ <property name="quantity"/>
+ <many-to-one name="item" class="eg.Item"/> <!-- class attribute is optional -->
+ </composite-element>
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ à¿ëñAoûüÖAÌirQ[V̽ßɽΤ©çpurchaseÖÌQÆðìé±ÆÍoܹñB
+ R|[lgÍl^Å èAQÆð¤LÅ«È¢±Æðo¦Ä¨¢Ä¾³¢B
+ êÂÌ <literal>Purchase</literal> ÍêÂÌ <literal>Order</literal> ÌsetɶÝÅ«Ü·ªA
+ ¯É <literal>Item</literal> ©çQÆ·é±ÆÍoܹñB
+ </para>
+
+ <para>
+ 3ÖAi é¢Í4ÈÇjàÂ\Å·B
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Order" .... >
+ ....
+ <set name="purchasedItems" table="purchase_items" lazy="true">
+ <key column="order_id">
+ <composite-element class="eg.OrderLine">
+ <many-to-one name="purchaseDetails" class="eg.Purchase"/>
+ <many-to-one name="item" class="eg.Item"/>
+ </composite-element>
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ R|WbgGgͼÌGeBeBÖÌÖAƵÄA
+ ¯¶V^bNXðgÁÄ¢éNGàÅgpÅ«Ü·B
+ </para>
+
+ </sect1>
+
+ <sect1 id="components-asmapindex">
+ <title>MapÌCfbNXƵÄÌR|[lg</title>
+
+ <para>
+ <literal><composite-map-key></literal> vfÍ <literal>Map</literal> ÌL[ƵÄR|[lgNXð
+ }bsOµÜ·BR|[lgNXãÅ <literal>hashCode()</literal> Æ <literal>equals()</literal>
+ ð³mÉI[o[ChµÄ¾³¢B
+ </para>
+ </sect1>
+
+ <sect1 id="components-compositeid" revision="1">
+ <title>¡¯ÊqƵÄÌR|[lg</title>
+
+ <para>
+ R|[lgðGeBeBNX̯ÊqƵÄg¤±ÆªÅ«Ü·B
+ R|[lgNXÍȺÌðð½·Kvª èÜ·B
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>java.io.Serializable</literal> ðÀµÈ¯êÎÈèܹñB
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ f[^x[XãÌ¡L[Ì¿«ÆµÌÈ¢æ¤ÉA<literal>equals()</literal>
+ Æ <literal>hashCode()</literal> ðÄÀµÈ¯êÎÈèܹñB
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ <emphasis>Ó: Hibernate3ɨ¢ÄA2ÔÚÌðÍâÎIÈðÅÍ èܹñB
+ µ©µÆÉ©ðð½µÄ¾³¢B
+ </emphasis>
+
+ </para>
+
+ <para>
+ ¡L[𶬷é½ßÉ <literal>IdentifierGenerator</literal> ðgp·é±ÆÍūܹñB
+ ãíèÉAvP[Vª¯ÊqðèÄÈÄÍÈèܹñB
+ </para>
+
+ <para>
+ ÊíÌ <literal><id></literal> é¾ÌãíèÉ <literal><composite-id></literal> ^Oð
+ ilXg³ê½ <literal><key-property></literal> ®«Æ¤Éjg¢Ü·B
+ ȺÌáÅÍA<literal>OrderLine</literal> NXÍ <literal>Order</literal> Ìi¡jåL[É
+ ˶µ½åL[ðÁĢܷB
+ </para>
+
+ <programlisting><![CDATA[<class name="OrderLine">
+
+ <composite-id name="id" class="OrderLineId">
+ <key-property name="lineId"/>
+ <key-property name="orderId"/>
+ <key-property name="customerId"/>
+ </composite-id>
+
+ <property name="name"/>
+
+ <many-to-one name="order" class="Order"
+ insert="false" update="false">
+ <column name="orderId"/>
+ <column name="customerId"/>
+ </many-to-one>
+ ....
+
+</class>]]></programlisting>
+
+ <para>
+ ±ÌÆ«A<literal>OrderLine</literal> e[uÖÖA·éOL[àܽ¡Å·B
+ ¼ÌNXÌ}bsOűêð龵ȯêÎÈèܹñB
+ <literal>OrderLine</literal> ÖÌÖAÍÌæ¤É}bsO³êÜ·B
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="orderLine" class="OrderLine">
+<!-- the "class" attribute is optional, as usual -->
+ <column name="lineId"/>
+ <column name="orderId"/>
+ <column name="customerId"/>
+</many-to-one>]]></programlisting>
+
+ <para>
+ i<literal><column></literal> ^OÍDZŠÁÄà <literal>column</literal> ®«Ì
+ ãíèÉÈèÜ·Bj
+ </para>
+
+ <para>
+ <literal>OrderLine</literal> ÖÌ <literal>many-to-many</literal> ÖAà
+ ¡OL[ðg¢Ü·B
+ </para>
+
+ <programlisting><![CDATA[<set name="undeliveredOrderLines">
+ <key column name="warehouseId"/>
+ <many-to-many class="OrderLine">
+ <column name="lineId"/>
+ <column name="orderId"/>
+ <column name="customerId"/>
+ </many-to-many>
+</set>]]></programlisting>
+
+ <para>
+ <literal>Order</literal> É é <literal>OrderLine</literal> ÌRNVÍ
+ ÌàÌðgpµÜ·B
+ </para>
+
+ <programlisting><![CDATA[<set name="orderLines" inverse="true">
+ <key>
+ <column name="orderId"/>
+ <column name="customerId"/>
+ </key>
+ <one-to-many class="OrderLine"/>
+</set>]]></programlisting>
+
+ <para>
+ i<literal><one-to-many></literal> ®«ÍAáÉæÁÄJð龵ܹñj
+ </para>
+
+ <para>
+ <literal>OrderLine</literal> ©gªRNVðÁÄ¢éêA
+ ¯É¡OL[àÁĢܷB
+ </para>
+
+ <programlisting><![CDATA[<class name="OrderLine">
+ ....
+ ....
+ <list name="deliveryAttempts">
+ <key> <!-- a collection inherits the composite key type -->
+ <column name="lineId"/>
+ <column name="orderId"/>
+ <column name="customerId"/>
+ </key>
+ <list-index column="attemptId" base="1"/>
+ <composite-element class="DeliveryAttempt">
+ ...
+ </composite-element>
+ </set>
+</class>]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="components-dynamic" revision="1">
+ <title>®IR|[lg</title>
+
+ <para>
+ <literal>Map</literal> ^ÌvpeBÌ}bsOàÂ\Å·B
+ </para>
+
+ <programlisting><![CDATA[<dynamic-component name="userAttributes">
+ <property name="foo" column="FOO" type="string"/>
+ <property name="bar" column="BAR" type="integer"/>
+ <many-to-one name="baz" class="Baz" column="BAZ_ID"/>
+</dynamic-component>]]></programlisting>
+
+ <para>
+ <literal><dynamic-component></literal> }bsOÌZ}eBNXÍ <literal><component></literal>
+ ƯêÌàÌÅ·B±ÌíÌ}bsOÌ_ÍA}bsOhL
gÌÒWÉæèAzuÉbeanÌ®«ð
+ èÅ«é_Å·BܽADOMp[TðpµÄA}bsOhL
gÌ^CìªÂ\Å·B
+ ³çÉA<literal>Configuration</literal> IuWFNgoRÅHibernateÌRtBO[VÌ^fÉ
+ ANZXiܽÍÏXjªÂ\Å·B
+ </para>
+
+ </sect1>
+
+</chapter>
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/configuration.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/configuration.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/configuration.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1725 @@
+<?xml version="1.0" encoding="Shift_JIS"?>
+<chapter id="session-configuration" revision="1">
+
+ <title>Ýè</title>
+
+ <para>
+ HibernateͳܴÜÈ«Ůì·éæ¤ÉfUC³êÄ¢é½ßAñíɽÌÝèvfª èÜ·B
+ K¢È±ÆÉAHibernateÍAöJ³êÄ¢épbP[WÌ <literal>etc/</literal> tH_Ì <literal>hibernate.properties</literal>
+ ÉAÙÆñÇÌÝèvfÌKØÈftHglªLq³êĢܷB
+ ±Ì <literal>hibernate.properties</literal> ðNXpXÉÝèµAÝèvfðJX^}CY·é¾¯Å·B
+ </para>
+
+ <sect1 id="configuration-programmatic" revision="1">
+ <title>vOãÌÝè</title>
+
+ <para>
+ <literal>org.hibernate.cfg.Configuration</literal> ÌCX^XÍA
+ JavaÌ^ÆSQLf[^x[XÌ}bsOîñð·×ÄÁĢܷB
+ The <literal>Configuration</literal> ÍAisÏÌj <literal>SessionFactory</literal>
+ 𶬷éÆ«ÉgpµÜ·B
+ ¡ÌXML}bsOt@CðÏ·µA}bsOîñɵܷB
+ </para>
+
+ <para>
+ ÊíA<literal>Configuration</literal> CX^XÍAÁèÌXML}bsOt@C
+ ÉæÁļÚú»³êÜ·BàµA}bsOt@CªNXpXÉÝè³êÄ¢éêA
+ Ì\bhðgÁľ³¢B <literal>addResource()</literal> :
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = new Configuration()
+ .addResource("Item.hbm.xml")
+ .addResource("Bid.hbm.xml");]]></programlisting>
+
+ <para>
+ ãÖÄ(±¿çÌÙ¤ªÇ¢Æ«à èÜ·)ƵÄ}bsONXðwè·éû@à èÜ·BHibernateÉA}bsOt@Cð
+ ©Â¯³¹Ä¾³¢F
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = new Configuration()
+ .addClass(org.hibernate.auction.Item.class)
+ .addClass(org.hibernate.auction.Bid.class);]]></programlisting>
+
+ <para>
+ HibernateÍANXpXÉ éȺÌæ¤È¼OÌ}bsOt@Cð©Â¯Ü·B
+ <literal>/org/hibernate/auction/Item.hbm.xml</literal> A
+ <literal>/org/hibernate/auction/Bid.hbm.xml</literal> B
+ ±Ìû@¾ÆAn[hR[fBO³ê½t@C¼ðrÅ«Ü·B
+ </para>
+
+ <para>
+ <literal>Configuration</literal> ÍAÝèvpeBðwè·é±ÆàÅ«Ü·F
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = new Configuration()
+ .addClass(org.hibernate.auction.Item.class)
+ .addClass(org.hibernate.auction.Bid.class)
+ .setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLInnoDBDialect")
+ .setProperty("hibernate.connection.datasource", "java:comp/env/jdbc/test")
+ .setProperty("hibernate.order_updates", "true");]]></programlisting>
+
+ <para>
+ HibernateÉÝèvpeBðÊ·û@Í1ÂÅÍ èܹñB
+ ³Ü´ÜÈIvVðpӵĢܷF
+ </para>
+
+ <orderedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>java.util.Properties</literal> CX^Xð
+ <literal>Configuration.setProperties()</literal> ÉnµÜ·B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>hibernate.properties</literal> ð
+ NXpXÌ[gfBNgÉu«Ü·B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>System</literal> vpeBª
+ <literal>java -Dproperty=value</literal> ðg¤æ¤ÉÝèµÜ·B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal><property></literal> vfð
+ <literal>hibernate.cfg.xml</literal> iãqjÉÝèµÜ·B
+ </para>
+ </listitem>
+ </orderedlist>
+
+ <para>
+ ¡·®nß½¢ÌÈçA<literal>hibernate.properties</literal>
+ ðg¤ÌªêÔÌ߹ŷB
+ </para>
+
+ <para>
+ <literal>Configuration</literal> ÍAN®É¾¯ éIuWFNgÅ èA
+ êx <literal>SessionFactory</literal> 𶬵½ãÍAjü³êé±ÆðÓ}µÄ¢Ü·B
+ </para>
+
+ </sect1>
+
+ <sect1 id="configuration-sessionfactory">
+ <title>SessionFactoryðæ¾·é</title>
+
+ <para>
+ <literal>Configuration</literal> ª·×ÄÌ}bsOîñðð͵½çA
+ AvP[VÍA <literal>Session</literal> t@NgCX^Xð澵ܷB
+ ±ÌSessionFactoryÍAHibernateðgp·é·×ÄÌXbhŤL³êé׫ŷB
+ </para>
+
+ <programlisting><![CDATA[SessionFactory sessions = cfg.buildSessionFactory();]]></programlisting>
+
+ <para>
+ HibernateÍA <literal>SessionFactory</literal> 𡶬·é±ÆªÅ«Ü·B
+ ±êÍA¡Ìf[^x[Xðgp·éêÉÖÅ·B
+ </para>
+
+ </sect1>
+
+ <sect1 id="configuration-hibernatejdbc" revision="1">
+ <title>JDBCRlNV</title>
+
+ <para>
+ ÊíAJÒÍ <literal>SessionFactory</literal> 𶬵ASessionFactoryÅJDBCRlNVðv[Oµ½¢Æl¦Ü·B
+ »ÌAv[`ðÌp·éêAPÉ <literal>Session</literal> ðI[vµÄ¾³¢F
+ </para>
+
+ <programlisting><![CDATA[Session session = sessions.openSession(); // open a new Session]]></programlisting>
+
+ <para>
+ ±ê¾¯ÅAv[Oµ½JDBCRlNVðgÁÄÚIÌf[^x[X
+ ÉANZX·é±ÆªÅ«Ü·B
+ </para>
+
+ <para>
+ »Ì½ßÉÍAJDBCRlNVÌvpeBðHibernateÉÝè·éKvª èÜ·B
+ ·×ÄÌHibernatevpeB¼ÆZ}eBNXÍ
+ <literal>org.hibernate.cfg.Environment</literal> NXÉè`³êĢܷB
+ ±ÌÝèÍJDBCRlNVÝèÌÅêÔdvÈàÌÅ·B
+ </para>
+
+ <para>
+ àµAȺÌvpeBðÝè·éÆAHibernateÍRlNVðæ¾·éiv[àj½ßÉ
+ <literal>java.sql.DriverManager</literal> ðg¢Ü·B
+ </para>
+
+ <table frame="topbot">
+ <title>Hibernate JDBCvpeB</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>vpeB¼</entry>
+ <entry>Ó¡</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.connection.driver_class</literal>
+ </entry>
+ <entry>
+ <emphasis>JDBChCoNX</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.url</literal>
+ </entry>
+ <entry>
+ <emphasis>jdbc URL</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.username</literal>
+ </entry>
+ <entry>
+ <emphasis>database user</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.password</literal>
+ </entry>
+ <entry>
+ <emphasis>database user password</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.pool_size</literal>
+ </entry>
+ <entry>
+ <emphasis>v[·éRlNVÌÅå</emphasis>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ HibernateÌRlNVv[ASYÍñíÉàIÈàÌÅ·B
+ ±êÍ·®Énßçêéæ¤ÉÆpÓ³ê½àÌÅA<emphasis>»iƵÄgp·é±ÆðÓ}µÄ¢Ü¹ñ</emphasis> B
+ ܽAptH[}XÌeXg̽ßÌàÌÅà èܹñB
+ ÅÌptH[}XÆÀè«ðÁ½v[ðÀ»µ½¯êÎAT[hp[eBÌc[ð¨©ßµÜ·B
+ <literal>hibernate.connection.pool_size</literal> vpeBÉ
+ KØÈRlNVv[TCYðLqµÄ¾³¢B
+ ±ÌÜܾÆHibernateÌRlNVv[ðg¢Ü·B
+ á¦ÎÌæ¤ÉC3P0ðg¢Ü·B
+ </para>
+
+ <para>
+ C3P0ÍI[v\[XJDBCRlNVv[ÅAHibernateÌ <literal>lib</literal>
+ fBNgÉ èÜ·BàµA<literal>hibernate.c3p0.*</literal> vpeBð
+ Zbg·êÎAHibernateÍA <literal>C3P0ConnectionProvider</literal> ðg¢Ü·B
+ àµProxoolðg¢½¢êÍA <literal>hibernate.properties</literal> pbP[Wð
+ QƵ½èAHibernateÌWebTCgÅæè½Ìîñð澵ľ³¢B
+ </para>
+
+ <para>
+ C3P0pÌ <literal>hibernate.properties</literal> t@CðáƵĦµÜ·F
+ </para>
+
+ <programlisting id="c3p0-configuration" revision="1"><![CDATA[hibernate.connection.driver_class = org.postgresql.Driver
+hibernate.connection.url = jdbc:postgresql://localhost/mydatabase
+hibernate.connection.username = myuser
+hibernate.connection.password = secret
+hibernate.c3p0.min_size=5
+hibernate.c3p0.max_size=20
+hibernate.c3p0.timeout=1800
+hibernate.c3p0.max_statements=50
+hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect]]></programlisting>
+
+ <para>
+ AvP[VT[oãÅg¤êÍAHibernateðÝèµA
+ AvP[VT[o©çRlNVðæ¾·éæ¤ÉµÄ¾³¢B
+ <literal>Datasource</literal> ðJNDIÉo^µÜ·B»µÄ
+ vpeBðȺÌæ¤ÉÝèµÄ¾³¢B
+ </para>
+
+ <table frame="topbot">
+ <title>Hibernate f[^\[XvpeB</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>vpeB¼</entry>
+ <entry>Ó¡</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.connection.datasource</literal>
+ </entry>
+ <entry>
+ <emphasis>f[^\[XÌJNDI¼</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jndi.url</literal>
+ </entry>
+ <entry>
+ <emphasis>JNDIvoC_ÌURL</emphasis> (IvV)
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jndi.class</literal>
+ </entry>
+ <entry>
+ <emphasis>JNDINX <literal>InitialContextFactory</literal> </emphasis> (IvV)
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.username</literal>
+ </entry>
+ <entry>
+ <emphasis>database user</emphasis> (IvV)
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.password</literal>
+ </entry>
+ <entry>
+ <emphasis>database user password</emphasis> (IvV)
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ AvP[VT[o©çñ³ê½JNDIf[^\[Xðg¤
+ <literal>hibernate.properties</literal> t@CÌáð¦µÜ·F
+ </para>
+
+ <programlisting><![CDATA[hibernate.connection.datasource = java:/comp/env/jdbc/test
+hibernate.transaction.factory_class = \
+ org.hibernate.transaction.JTATransactionFactory
+hibernate.transaction.manager_lookup_class = \
+ org.hibernate.transaction.JBossTransactionManagerLookup
+hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect]]></programlisting>
+
+ <para>
+ JNDIf[^\[X©çæ¾µ½JDBCRlNVÍAAvP[VT[oÌ
+ ReiÇgUNVÉ©®IÉQÁµÜ·B
+ </para>
+
+ <para>
+ CÓÌRlNVvpeBÍA^¦çê½h <literal>hibernate.connnection</literal> h
+ vpeB¼ÉæÁÄ^¦çêÜ·Bá¦ÎA <literal>charSet</literal> ðÝèµ½¢êÍA
+ <literal>hibernate.connection.charSet</literal> ðg¢Ü·B
+ </para>
+
+ <para>
+ JDBCRlNVðæ¾·éíªðÂÆ©ÌvOCðè`·éêÍA
+ <literal>org.hibernate.connection.ConnectionProvider</literal> C^[tFCXð
+ ÀµÄ¾³¢B»µÄAÀNXð <literal>hibernate.connection.provider_class</literal>
+ ÉÝèµÄ¾³¢B
+ </para>
+
+ </sect1>
+
+ <sect1 id="configuration-optional" revision="1">
+ <title>IvVÝèvpeB</title>
+
+ <para>
+ ±êçÌvpeBÍHibernateÌ®ð§ä·éàÌÅ·B
+ ±êçÌvpeBÍ·×ÄÃÈftHglª èACÓÅÝèµÜ·B
+ </para>
+
+ <para>
+ <emphasis>ÓF±êçÌvpeBÍ"VXex"ÌÝÅ·B</emphasis>
+ VXexvpeBÍ <literal>java -Dproperty=value</literal> AàµÍ
+ <literal>hibernate.properties</literal> ÅÌÝÝèÂ\Å·B
+ »êÈOÌÝèû@Í <emphasis> èܹñ</emphasis> B
+ </para>
+
+ <table frame="topbot" id="configuration-optional-properties" revision="8">
+ <title>HibernateÝèvpeB</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>vpeB¼</entry>
+ <entry>Ó¡</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.dialect</literal>
+ </entry>
+ <entry>
+ Hibernate <literal>Dialect</literal> NX¼ªüèÜ·B
+ ±êÍ[Vif[^x[X²ÆÉÅK»³ê½SQL𶬵ܷB
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ <literal>full.classname.of.Dialect</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.show_sql</literal>
+ </entry>
+ <entry>
+ s³ê½·×ÄÌSQLðR\[Éo͵ܷB
+ ±êÍOJeSÌ <literal>org.hibernate.SQL</literal>
+ É <literal>debug</literal> ðÝè·éû@ÌãÖèiÅ·B
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.format_sql</literal>
+ </entry>
+ <entry>
+ OÆR\[ÌSQLðüµ\¦µÜ·B
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.default_schema</literal>
+ </entry>
+ <entry>
+ ¶¬³êéSQL¶Ìe[uÉÝè·éXL[}/e[uXy[XÅ·B
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ <literal>SCHEMA_NAME</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.default_catalog</literal>
+ </entry>
+ <entry>
+ ¶¬³êéSQL¶Ìe[uÉÝè·éJ^OÅ·B
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ <literal>CATALOG_NAME</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.session_factory_name</literal>
+ </entry>
+ <entry>
+ <literal>SessionFactory</literal> Ͷ¬ãA±Ì¼OÅJNDIÉo^³êÜ·B
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ <literal>jndi/composite/name</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.max_fetch_depth</literal>
+ </entry>
+ <entry>
+ OtFb`ÌÅå[xðÝèµÜ·B·éÖAÍ
+ ÎêÖAÌÝiêÎêA½ÎêjÅ·B
+ <literal>0</literal> ðwè·éÆOtFb`ͳøÉÈèÜ·B
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ §·élÍ <literal>0</literal> ©ç <literal>3</literal> Å·B
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.default_batch_fetch_size</literal>
+ </entry>
+ <entry>
+ ÖAtFb`ÌftHgob`TCYðwèµÜ·B
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ §·élÍ <literal>4</literal> , <literal>8</literal> , <literal>16</literal> Å·B
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.default_entity_mode</literal>
+ </entry>
+ <entry>
+ <literal>SessionFactory</literal> ©çZbVðI[vµ½Æ«É
+ gp·éGeBeBÌftHg[hðÝèµÜ·B
+ <para>
+ <literal>dynamic-map</literal>, <literal>dom4j</literal>,
+ <literal>pojo</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.order_updates</literal>
+ </entry>
+ <entry>
+ ÚªXV³ê½Æ«ÉAÊÌSQLÅåL[ðXV·é±Æð§µÜ·B
+ ±ÌêA¯ÀsÂ\ÈVXeÅÍAÜêÉfbhbNª¶·éÂ\«ª èÜ·B
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.generate_statistics</literal>
+ </entry>
+ <entry>
+ LøÌêAHibernateÍptH[}X`
[jOÉ
+ LøÈvîñðûWµÜ·B
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.use_identifer_rollback</literal>
+ </entry>
+ <entry>
+ LøÌêAIuWFNgªí³ê½Æ«É
+ ¯ÊqvpeBðZbgµAftHglɵ½àÌ𶬵ܷB
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.use_sql_comments</literal>
+ </entry>
+ <entry>
+ LøÌêASQLàÉRg𶬵ܷB±êÍfobNðeÕɵܷB
+ ftHgÌlÍ <literal>false</literal> Å·B
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame="topbot" id="configuration-jdbc-properties" revision="8">
+ <title>Hibernate JDBC ÆRlNVvpeB</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>vpeB¼</entry>
+ <entry>Ó¡</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.fetch_size</literal>
+ </entry>
+ <entry>
+ lª0ÅÈ¢êAJDBCtFb`TCYðèµÜ·
+ ( <literal>Statement.setFetchSize()</literal> ðÄÑÜ·)B
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.batch_size</literal>
+ </entry>
+ <entry>
+ lª0ÅÈ¢êAHibernateªJDBC2ob`XVðgpµÜ·B
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ §·élÍ <literal>5</literal> ©ç <literal>30</literal> Å·B
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.batch_versioned_data</literal>
+ </entry>
+ <entry>
+ àµJDBChCoª <literal>executeBatch()</literal> ÉæÁijmÈsð
+ Ô·êA±ÌvpeBð <literal>true</literal> ɵľ³¢
+ iÊíͱÌIvVðONɵܷjB
+ HibernateÍA©®o[Wf[^̽ßob`DMLðg¢Ü·B
+ ftHgÌlÍ <literal>false</literal> Å·B
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.factory_class</literal>
+ </entry>
+ <entry>
+ JX^ <literal>Batcher</literal> ðIÑÜ·B
+ ÙÆñÇÌAvP[VÉA±ÌÝèÍKv èܹñB
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ <literal>classname.of.Batcher</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.use_scrollable_resultset</literal>
+ </entry>
+ <entry>
+ XN[Â\ÈUgZbgðAHibernateªgpµÜ·B
+ ±ÌvpeBÍAJDBCRlNVªRlNV^f[^ð
+ T|[gµÄ¢é±ÆªK{ðÉÈèÜ·B
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.use_streams_for_binary</literal>
+ </entry>
+ <entry>
+ JDBCÖ/©ç <literal>binary</literal> â <literal>serializable</literal>
+ Ì«Ý/ÇÝÝXg[ðg¢Ü·(VXexÌvpeB)B
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.use_get_generated_keys</literal>
+ </entry>
+ <entry>
+ }üÌãÉ©®¶¬³ê½åL[ðæ¾·é½ßÌ
+ JDBC3 <literal>PreparedStatement.getGeneratedKeys()</literal> ÌgpðLøɵܷB
+ ±êÍJDBC3+hCoÆJRE1.4+ðKvƵA
+ àµHibernate̯ÊqWFl[^Éâ誶·éæ¤ÈçfalseÉÝèµÄ¾³¢B
+ ftHgÅÍRlNV^f[^ðg¢hCoÌ\ÍðèµÜ·B
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.provider_class</literal>
+ </entry>
+ <entry>
+ JDBCRlNVðHibernateÉñ·éÆ©Ì <literal>ConnectionProvider</literal> Ì
+ ¼OðwèµÜ·B
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ <literal>classname.of.ConnectionProvider</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.isolation</literal>
+ </entry>
+ <entry>
+ JDBCgUNVª£xðÝèµÜ·B
+ ÃÈlð²×é½ßÉÍ <literal>java.sql.Connection</literal> ð`FbNµÄ¾³¢B
+ µ©µgp·éf[^x[XªA·×Ä̪£xðT|[gµÄ¢éÆÍÀèܹñB
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ <literal>1, 2, 4, 8</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.autocommit</literal>
+ </entry>
+ <entry>
+ v[³êÄ¢éJDBCRlNVÌ©®R~bgðLøÉ·éiñ§jB
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.release_mode</literal>
+ </entry>
+ <entry>
+ HibernateªJDBCRlNVð[X·é©ðwèµÜ·BftHgÅÍ
+ ZbVª¾¦IÉN[YܽÍØf³êÄàRlNVÍ۵ܷB
+ AvP[VT[oÌJTAf[^\[XÌêA
+ ·×ÄÌJDBCR[ÌãA§IÉRlNVð[X·é½ßÉ <literal>after_statement</literal> ð
+ gÁľ³¢B
+ ñJTARlNVÌêAegUNVªI¹µ½Æ«É <literal>after_transaction</literal>
+ ðg¢ARlNVð[XµÄ¾³¢B
+ <literal>auto</literal> É·éÆA
+ JTAâCMTgUNVÌêA <literal>after_statement</literal> ÅN[YµA
+ JDBCgUNVÌêA <literal>after_transaction</literal> ÅN[YµÜ·B
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ <literal>auto</literal> (default) | <literal>on_close</literal> |
+ <literal>after_transaction</literal> | <literal>after_statement</literal>
+ </para>
+ <para>
+ ӵľ³¢B±ÌÝèÍ <literal>SessionFactory.openSession</literal> ©ç
+ æ¾µ½ <literal>Session</literal> ¾¯Éøʪ èÜ·B
+ <literal>SessionFactory.getCurrentSession</literal> ðʶÄæ¾µ½
+ <literal>Session</literal> ÅÍA<literal>CurrentSessionContext</literal>
+ ÌÀÉæÁÄARlNVÌ[X[hðÝèµÜ·B
+ <xref linkend="architecture-current-session"/> ðQƵľ³¢B
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.<emphasis><propertyName></emphasis></literal>
+ </entry>
+ <entry>
+ JDBCÌ <literal>propertyName</literal> vpeBðA
+ <literal>DriverManager.getConnection()</literal> ÉnµÜ·B
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jndi.<emphasis><propertyName></emphasis></literal>
+ </entry>
+ <entry>
+ <literal>propertyName</literal> vpeBðA
+ JNDI <literal>InitialContextFactory</literal> ÉnµÜ·B
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame="topbot" id="configuration-cache-properties" revision="7">
+ <title>Hibernate LbV
vpeB</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>vpeB¼</entry>
+ <entry>Ó¡</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.cache.provider_class</literal>
+ </entry>
+ <entry>
+ JX^ <literal>CacheProvider</literal> ÌNX¼Å·B
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ <literal>classname.of.CacheProvider</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.use_minimal_puts</literal>
+ </entry>
+ <entry>
+ «ÝðŬÀÉ·é½ßÉAñLbV
ÌìðÅK»µÜ·B
+ »ÌãíèÉAÇÝݪæèpÉɶ·éæ¤ÉÈèÜ·B
+ ±ÌZbeBOÍNX^LbV
Åðɧ¿Ü·B
+ Hibernate3ÅÍNX^LbV
ÀpÉftHgÅÍLøÉÈÁĢܷB
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.use_query_cache</literal>
+ </entry>
+ <entry>
+ ÁèÌNGªLbV
Â\ÈêÉANGLbV
ðLøɵܷB
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.use_second_level_cache</literal>
+ </entry>
+ <entry>
+ ñLbV
ð®SɳøÉ·éêÉg¢Ü·B
+ ftHgÅÍLøÅANXÌ <literal><cache></literal> }bsO
+ ŧäµÜ·B
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.query_cache_factory</literal>
+ </entry>
+ <entry>
+ JX^ <literal>QueryCache</literal> C^[tFCXÌNX¼ð
+ wèµÜ·BftHgÅÍ <literal>StandardQueryCache</literal> ÉÈèÜ·B
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ <literal>classname.of.QueryCache</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.region_prefix</literal>
+ </entry>
+ <entry>
+ ñLbV
ÌÌæ¼ÌÚª«Å·B
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ <literal>prefix</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.use_structured_entries</literal>
+ </entry>
+ <entry>
+ ñLbV
Éi[·éf[^ðAlªðµâ·¢tH[}bgɵܷB
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame="topbot" id="configuration-transaction-properties" revision="9">
+ <title>Hibernate gUNVvpeB</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>vpeB¼</entry>
+ <entry>Ó¡</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.transaction.factory_class</literal>
+ </entry>
+ <entry>
+ Hibernate <literal>Transaction</literal> APIÆêÉgíêé
+ <literal>TransactionFactory</literal> ÌNX¼Å·B
+ iftHgÅÍ <literal>JDBCTransactionFactory</literal> Å·jB
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ <literal>classname.of.TransactionFactory</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>jta.UserTransaction</literal>
+ </entry>
+ <entry>
+ AvP[VT[o©çJTA <literal>UserTransaction</literal> ðæ¾·é½ßÉ
+ <literal>JTATransactionFactory</literal> ÉgíêéJNDI¼Å·B
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ <literal>jndi/composite/name</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.transaction.manager_lookup_class</literal>
+ </entry>
+ <entry>
+ <literal>TransactionManagerLookup</literal> ÌNX¼Å·B
+ JTA«ɨ¢ÄAJVMxÌLbV
ðLøÉ·é½ßÉKvÅ·B
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ <literal>classname.of.TransactionManagerLookup</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.transaction.flush_before_completion</literal>
+ </entry>
+ <entry>
+ LøÌêAgUNVÌcompletiontF[YÌOÉ©®IÉ
+ ZbVðtbV
µÜ·BàÌ©®ZbVReLXgÇÉKµÄ¢Ü·B
+ <xref linkend="architecture-current-session"/>ðQƵľ³¢B
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.transaction.auto_close_session</literal>
+ </entry>
+ <entry>
+ LøÌêAgUNVÌcompletiontF[YÌãÉ
+ ZbVð©®IÉN[YµÜ·BàÌ©®ZbVReLXgÇÉKµÄ¢Ü·B
+ <xref linkend="architecture-current-session"/>ðQƵľ³¢B
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame="topbot" id="configuration-misc-properties" revision="10">
+ <title>»Ì¼ÌvpeB</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>vpeB¼</entry>
+ <entry>Ó¡</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.current_session_context_class</literal>
+ </entry>
+ <entry>
+ u»ÝÌv<literal>Session</literal> ̽ßÌiJX^jíªðñµÜ·B
+ rgCXgeW[ÉÖ·é»Ì¼ÌîñÉ¢ÄÍ
+ <xref linkend="architecture-current-session"/>ðQƵľ³¢B
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ <literal>jta</literal> | <literal>thread</literal> |
+ <literal>managed</literal> | <literal>custom.Class</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.query.factory_class</literal>
+ </entry>
+ <entry>
+ HQLp[T[ÌÀðIðµÜ·B
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ <literal>org.hibernate.hql.ast.ASTQueryTranslatorFactory</literal> or
+ <literal>org.hibernate.hql.classic.ClassicQueryTranslatorFactory</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.query.substitutions</literal>
+ </entry>
+ <entry>
+ HQLÆSQLÌg[Nð}bsOµÜ·B
+ iá¦ÎAg[NÍÖâe¼Å·jB
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ <literal>hqlLiteral=SQL_LITERAL, hqlFunction=SQLFUNC</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.hbm2ddl.auto</literal>
+ </entry>
+ <entry>
+ <literal>SessionFactory</literal> 𶬵½Æ«ÉA
+ ©®IÉXL[}DDLðDBÉo͵ܷB
+ <literal>create-drop</literal> ÌêA <literal>SessionFactory</literal> ð
+ N[Yµ½Æ«ÉAf[^x[XXL[}ðhbvµÜ·B
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ <literal>validate</literal> | <literal>update</literal> |
+ <literal>create</literal> | <literal>create-drop</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cglib.use_reflection_optimizer</literal>
+ </entry>
+ <entry>
+ ÀstNVÌãíèÌCGLIBÌgpðLøɵܷ
+ iVXexÌvpeBj
+ tNVÍguV
[eBOÌÆ«Éð§Â±Æª èÜ·B
+ IveB}CUðItɵĢéƫų¦A
+ HibernateÉÍK¸CGLIBªKvȱÆÉӵľ³¢B
+ ±ÌvpeBÍ <literal>hibernate.cfg.xml</literal> ÅÝèūܹñB
+ <para>
+ <emphasis role="strong">áF</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <sect2 id="configuration-optional-dialects" revision="1">
+ <title>SQL û¾iDialectj</title>
+
+ <para>
+ <literal>hibernate.dialect</literal> vpeBÉÍA
+ gp·éf[^x[X̳µ¢ <literal>org.hibernate.dialect.Dialect</literal> ÌTuNXðA
+ K¸wè·×«Å·B
+ µ©µû¾ðwè·êÎAHibernateÍãqµ½vpeB̢©É¢ÄA
+ æèKØÈftHglðg¢Ü·B
+ »¤·êÎA»êçðèìÆÅÝè·éèԪȯܷB
+ </para>
+
+ <table frame="topbot" id="sql-dialects" revision="2">
+ <title>Hibernate SQL Dialects (<literal>hibernate.dialect</literal>)</title>
+ <tgroup cols="2">
+ <colspec colwidth="1*"/>
+ <colspec colwidth="2.5*"/>
+ <thead>
+ <row>
+ <entry>RDBMS</entry>
+ <entry>Dialect</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>DB2</entry> <entry><literal>org.hibernate.dialect.DB2Dialect</literal></entry>
+ </row>
+ <row>
+ <entry>DB2 AS/400</entry> <entry><literal>org.hibernate.dialect.DB2400Dialect</literal></entry>
+ </row>
+ <row>
+ <entry>DB2 OS390</entry> <entry><literal>org.hibernate.dialect.DB2390Dialect</literal></entry>
+ </row>
+ <row>
+ <entry>PostgreSQL</entry> <entry><literal>org.hibernate.dialect.PostgreSQLDialect</literal></entry>
+ </row>
+ <row>
+ <entry>MySQL</entry> <entry><literal>org.hibernate.dialect.MySQLDialect</literal></entry>
+ </row>
+ <row>
+ <entry>MySQL with InnoDB</entry> <entry><literal>org.hibernate.dialect.MySQLInnoDBDialect</literal></entry>
+ </row>
+ <row>
+ <entry>MySQL with MyISAM</entry> <entry><literal>org.hibernate.dialect.MySQLMyISAMDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Oracle (any version)</entry> <entry><literal>org.hibernate.dialect.OracleDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Oracle 9i/10g</entry> <entry><literal>org.hibernate.dialect.Oracle9Dialect</literal></entry>
+ </row>
+ <row>
+ <entry>Sybase</entry> <entry><literal>org.hibernate.dialect.SybaseDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Sybase Anywhere</entry> <entry><literal>org.hibernate.dialect.SybaseAnywhereDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Microsoft SQL Server</entry> <entry><literal>org.hibernate.dialect.SQLServerDialect</literal></entry>
+ </row>
+ <row>
+ <entry>SAP DB</entry> <entry><literal>org.hibernate.dialect.SAPDBDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Informix</entry> <entry><literal>org.hibernate.dialect.InformixDialect</literal></entry>
+ </row>
+ <row>
+ <entry>HypersonicSQL</entry> <entry><literal>org.hibernate.dialect.HSQLDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Ingres</entry> <entry><literal>org.hibernate.dialect.IngresDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Progress</entry> <entry><literal>org.hibernate.dialect.ProgressDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Mckoi SQL</entry> <entry><literal>org.hibernate.dialect.MckoiDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Interbase</entry> <entry><literal>org.hibernate.dialect.InterbaseDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Pointbase</entry> <entry><literal>org.hibernate.dialect.PointbaseDialect</literal></entry>
+ </row>
+ <row>
+ <entry>FrontBase</entry> <entry><literal>org.hibernate.dialect.FrontbaseDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Firebird</entry> <entry><literal>org.hibernate.dialect.FirebirdDialect</literal></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-outerjoin" revision="4">
+ <title>OtFb`</title>
+
+ <para>
+ àµDBªANSI©AOracle©SybaseX^CÌOðT|[gµÄ¢éêA
+ <emphasis>outer join fetching</emphasis> ÍADBÌSQLsñðßñµ
+ ptH[}XðǵܷBiDBàÅæè½ÌRXgª¶µÜ·j
+ OtFb`ÍA½ÎêAêνA½Î½AêÎêÌIuWFNgÖAÅ
+ O[vIuWFNgð1ÂÌSQLÅ <literal>SELECT</literal> µÜ·B
+ </para>
+
+ <para>
+ <literal>hibernate.max_fetch_depth</literal> vpeBÌlð <literal>0</literal> É·éÆ
+ Outer join fetchingð <emphasis>·×Ä</emphasis> ³øÉ·é±ÆÉÈèÜ·B
+ <literal>1</literal> â»êÈãÌlðÝè·éÆAOtFb`ªLøÉÈèA
+ êÎêƽÎêÖAª <literal>fetch="join"</literal> ƵÄ}bsO³êÜ·B
+ </para>
+
+ <para>
+ See <xref linkend="performance-fetching"/> for more information.
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-binarystreams" revision="1">
+ <title>oCiXg[</title>
+
+ <para>
+ OracleÍJDBChCoÆÌÔÅâèÆè³êé <literal>byte</literal> zñÌTCYð§ÀµÜ·B
+ <literal>binary</literal> â <literal>serializable</literal> ^Ìå«ÈCX^Xðg¢½¯êÎA
+ <literal>hibernate.jdbc.use_streams_for_binary</literal> ðLøɵľ³¢B
+ ½¾µ <emphasis>±êÍVXexÌÝ辯ŷ</emphasis> B
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-cacheprovider" revision="2">
+ <title>2LbV
ÆNG[LbV
</title>
+
+ <para>
+ <literal>hibernate.cache</literal> vpeBÚª«Í
+ HibernateÅvZXâNX^ñLbV
ðg¤Æ±ÆðµܷB
+ <xref linkend="performance-cache"/>Éæè½ÌÚת èÜ·B
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-querysubstitution">
+ <title>NG[¾êÌu«·¦</title>
+
+ <para>
+ <literal>hibernate.query.substitutions</literal> ðg¤±ÆÅA
+ Vµ¢HibernateNGg[Nðè`Å«Ü·B
+ áF
+ </para>
+
+ <programlisting>hibernate.query.substitutions true=1, false=0</programlisting>
+
+ <para>
+ ±êÍg[N <literal>true</literal> Æ <literal>false</literal> ðA
+ ¶¬³êéSQLɨ¢Ä®eÉ|óµÜ·B
+ </para>
+
+ <programlisting>hibernate.query.substitutions toLowercase=LOWER</programlisting>
+
+ <para>
+ ±êÍSQLÌ <literal>LOWER</literal> Ö̼OÌt¯Ö¦ðÂ\ɵܷB
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-statistics" revision="2">
+ <title>Hibernate v</title>
+
+ <para>
+ <literal>hibernate.generate_statistics</literal> ðLøɵ½êA
+ ®ìµÄ¢éVXeð`
[jOᎮǃA<literal>SessionFactory.getStatistics()</literal>
+ ðoRµÄAHibernateÍÖÈvîñðo͵ܷB
+ JMXðoRµÄvîñðoÍ·é±ÆàÂ\Å·B
+ JavadocÌ <literal>org.hibernate.stats</literal> pbP[WàÌ
+ C^[tFCXÉÍæè½Ìîñª èÜ·B
+ </para>
+
+ </sect2>
+ </sect1>
+
+ <sect1 id="configuration-logging">
+ <title>MO</title>
+
+ <para>
+ HibernateÍApache commons-logginðgÁÄA³Ü´ÜÈCxgðOƵÄ
+ o͵ܷB
+ </para>
+
+ <para>
+ commons-loggingT[rXÍiNXpXÉ <literal>log4j.jar</literal> ðÜßêÎjApache Log4jÉA
+ ܽiJDK1.4©»êÈãÅÀs³¹êÎjJDK1.4 loggingɼÚo͵ܷB
+ Log4jÍ <literal>http://jakarta.apache.org</literal> ©ç_E[hÅ«Ü·B
+ Log4jðg¤½ßÉÍANXpXÉ <literal>log4j.properties</literal> t@Cðzu·éKvª èÜ·B
+ áÌvpeBt@CÍHibernateÆêÉzz³êA»êÍ <literal>src/</literal> fBNgÉ èÜ·B
+ </para>
+
+ <para>
+ HibernateÌObZ[Wɵêé±Æ𨷷ߵܷB
+ HibernateÌOÍÇÝâ·AÅ«éÀèÚ×ÉÈéæ¤ÉwͳêĢܷB
+ ±êÍK{ÌguV
[eBOfoCXÅ·B
+ ȺÉdvÈOÌJeSð¦µÜ·B
+ </para>
+
+ <table frame="topbot" id="log-categories" revision="2">
+ <title>Hibernate OJeS</title>
+ <tgroup cols="2">
+ <colspec colwidth="1*"/>
+ <colspec colwidth="2.5*"/>
+ <thead>
+ <row>
+ <entry>JeS</entry>
+ <entry>@\</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>org.hibernate.SQL</literal></entry>
+ <entry>Àsµ½·×ÄÌSQLiDDLjXe[ggðMOµÜ·B</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.type</literal></entry>
+ <entry>·×ÄÌJDBCp[^ðMOµÜ·B</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.tool.hbm2ddl</literal></entry>
+ <entry>Àsµ½·×ÄÌSQLiDDLjXe[ggðMOµÜ·B</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.pretty</literal></entry>
+ <entry>
+ sessionÉÖA·é·×ÄÌGeBeBiÅåQOjÌtbV
ÔðMOµÜ·B
+ </entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.cache</literal></entry>
+ <entry>·×ÄÌQLbV
Ì®ìðMOµÜ·B</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction</literal></entry>
+ <entry>gUNVÉÖA·é®ìðMOµÜ·B</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.jdbc</literal></entry>
+ <entry>JDBC\[Xæ¾ðMOµÜ·B</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.hql.ast.AST</literal></entry>
+ <entry>
+ HQLÆSQLÌASTÌNG[p[XðMOµÜ·B
+ </entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.secure</literal></entry>
+ <entry>·×ÄÌJAASªÍðMOµÜ·B</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate</literal></entry>
+ <entry>
+ ·×ÄðMOµÜ·BiîñªåÊÉÈèÜ·ªAguV
[eBOÉÍÖÅ·j
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ HibernateÅAvP[Vð쬷éÆ«ÍA<literal>org.hibernate.SQL</literal>
+ JeSÌ <literal>debug</literal> ðíÉLøɵĨ¢½Ù¤ªÇ¢Åµå¤B
+ ãÖû@ƵÄA<literal>hibernate.show_sql</literal> ðLøÉ·éû@ª èÜ·B
+ </para>
+
+
+ </sect1>
+
+ <sect1 id="configuration-namingstrategy">
+ <title><literal>NamingStrategy</literal> ÌÀ</title>
+
+ <para>
+ C^[tFCX <literal>net.sf.hibernate.cfg.NamingStrategy</literal> ðg¤Æ
+ f[^x[XIuWFNgÆXL[}vf̽ßÌu½¼WvðwèÅ«Ü·B
+ </para>
+
+ <para>
+ Java̯Êq©çf[^x[X̯Êq𩮶¬·é½ßÌ[âA
+ }bsOt@CÅ^¦½u_IÈvJÆe[u¼©ç
+ u¨IÈve[uÆJ¼ð¶¬·é½ßÌ[ðpÓ·é±ÆªÅ«Ü·B
+ ±Ì@\ÍJèÔµÌG¹iá¦Î <literal>TBL_</literal> vtBbNXjðæè«A
+ }bsOhL
gÌç·³ð¸ç·±ÆÉð§¿Ü·B
+ Hibernateªg¤ftHgÌíªÍ©ÈèŬÀÉߢàÌÅ·B
+ </para>
+
+ <para>
+ }bsOðÇÁ·éOÉ <literal>Configuration.setNamingStrategy()</literal> ðÄÔ±ÆÅ
+ ȺÌæ¤ÉÙÈéíªðwè·é±ÆªÅ«Ü·F
+ </para>
+
+ <programlisting><![CDATA[SessionFactory sf = new Configuration()
+ .setNamingStrategy(ImprovedNamingStrategy.INSTANCE)
+ .addFile("Item.hbm.xml")
+ .addFile("Bid.hbm.xml")
+ .buildSessionFactory();]]></programlisting>
+
+ <para>
+ <literal>org.hibernate.cfg.ImprovedNamingStrategy</literal> ÍgÝÝÌíªÅ·B
+ ±ê͢©ÌAvP[VÉÆÁÄLpÈJn_ÆÈé©àµêܹñB
+ </para>
+
+ </sect1>
+
+ <sect1 id="configuration-xmlconfig" revision="2">
+ <title>XMLÝèt@C</title>
+
+ <para>
+ à¤1ÂÌû@Í <literal>hibernate.cfg.xml</literal> Æ¢¤¼OÌt@CÅ
+ \ªÈÝèðwè·éû@Å·B
+ ±Ìt@CÍ <literal>hibernate.properties</literal> t@CÌãíèÆÈèÜ·B
+ ർûÌt@Cª êÎAvpeBªu«·¦çêÜ·B
+ </para>
+
+ <para>
+ XMLÝèt@CÍúÝèÅ <literal>CLASSPATH</literal> ÉzuµÄ¾³¢B
+ ±êªáÅ·F
+ </para>
+
+ <programlisting><![CDATA[<?xml version='1.0' encoding='utf-8'?>
+<!DOCTYPE hibernate-configuration PUBLIC
+ "-//Hibernate/Hibernate Configuration DTD//EN"
+ "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
+
+<hibernate-configuration>
+
+ <!-- a SessionFactory instance listed as /jndi/name -->
+ <!-- /jndi/nameÌæ¤ÉXgAbv³ê½SessionFactoryCX^X -->
+ <session-factory
+ name="java:hibernate/SessionFactory">
+
+ <!-- properties -->
+ <property name="connection.datasource">java:/comp/env/jdbc/MyDB</property>
+ <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
+ <property name="show_sql">false</property>
+ <property name="transaction.factory_class">
+ org.hibernate.transaction.JTATransactionFactory
+ </property>
+ <property name="jta.UserTransaction">java:comp/UserTransaction</property>
+
+ <!-- mapping files -->
+ <mapping resource="org/hibernate/auction/Item.hbm.xml"/>
+ <mapping resource="org/hibernate/auction/Bid.hbm.xml"/>
+
+ <!-- cache settings -->
+ <class-cache class="org.hibernate.auction.Item" usage="read-write"/>
+ <class-cache class="org.hibernate.auction.Bid" usage="read-only"/>
+ <collection-cache collection="org.hibernate.auction.Item.bids" usage="read-write"/>
+
+ </session-factory>
+
+</hibernate-configuration>]]></programlisting>
+
+ <para>
+ ©ÄÌƨèA±Ìû@ÌDÊ«ÍÝè̽ßÌ}bsOt@C¼ðOoµÉÅ«é±ÆÅ·B
+ HibernateLbV
ð`
[jOµÈ¯êÎÈçÈ¢ÌÅ êÎA
+ <literal>hibernate.cfg.xml</literal> ÍæèÖÅ·B
+ <literal>hibernate.properties</literal> Æ <literal>hibernate.cfg.xml</literal> Ì
+ Ç¿ç©ðg¦é±Æðo¦Ä¨¢Ä¾³¢B
+ ñÂͯ¶àÌÅAá¤Æ±ëÆ¢¦ÎXML\¶ðg¤±ÆÌ_¾¯Å·B
+ </para>
+
+ <para>
+ XMLÝèðg¤±ÆÅAHibernateÍȺÌæ¤ÉVvÉÈèÜ·B
+ </para>
+
+ <programlisting><![CDATA[SessionFactory sf = new Configuration().configure().buildSessionFactory();]]></programlisting>
+
+ <para>
+ á¤XMLÝèt@Cðg¤±ÆàÅ«Ü·B
+ </para>
+
+ <programlisting><![CDATA[SessionFactory sf = new Configuration()
+ .configure("catdb.cfg.xml")
+ .buildSessionFactory();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="configuration-j2ee" revision="1">
+ <title>J2EEAvP[VT[oÆÌ</title>
+
+ <para>
+ HibernateÍJ2EE\¢Æ·é|CgðT|[gµÄ¢Ü·B
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>ReiÇf[^\[X</emphasis>FHibernateÍ
+ JNDIªñµAReiªÇ·éJDBCRlNVðgpÅ«Ü·B
+ ÊíAJTAÌ <literal>TransactionManager</literal> Æ
+ <literal>ResourceManager</literal> ªgUNVÇ(CMT)A
+ ÁÉlXÈf[^\[XÉܽªéªUgUNVðµ¢Ü·B
+ RvOÅgUNV«EðwèÅ«Ü·(BMT)B
+ é¢ÍALqµ½R[hÌ|[^reBðÛ½ßÉA
+ IvVÌHibernateÌ <literal>Transaction</literal> APIðg¢½Èé©àµêܹñB
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>©®JNDIoCfBO</emphasis>FHibernateÍJNDIª§¿ãªÁ½ãÉ
+ <literal>SessionFactory</literal> 𶬵ܷB
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>JTAZbVoCfBO</emphasis> F
+ Hibernate <literal>Session</literal> ÌgUNV«EÍJTAgUNVƯ¶ÉÈèÜ·B
+ PÉ <literal>SessionFactory</literal> ðJNDI©çlookupµÄA
+ »ÝÌ <literal>Session</literal> ð澵ܷB
+ JTAgUNVª®¹µ½Æ«ÉAHibernateª <literal>Session</literal> ðtbV
µAN[YµÜ·B
+ EJBfvCgfBXNv^ÌÉAgUNV«Eð龵ܷB
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>JMXfvC:</emphasis> àµJMXªgpÂ\ÈAvP[VT[oiá¦ÎJBOSSj
+ ª éêAHibernateðMBeanƵÄfvC·é±ÆðI×Ü·B
+ ±êÍ <literal>Configuration</literal> ©ç <literal>SessionFactory</literal> ð
+ ¶¬·éR[h𳷱ƪūܷB
+ ReiÍ <literal>HibernateService</literal> ðN®µA
+ T[rXÌ˶ðzIÉǵܷif[^\[XÍHibernateâ»Ì¼ªN®·éOÉ
+ gpÅ«éæ¤ÉµÈ¯êÎÈèܹñjB
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ «É˶µÜ·ªAàµAAvP[VT[oª"connection containment"ÌáOðX[·éÈç
+ ÝèÌIvV <literal>hibernate.connection.aggressive_release</literal>
+ ðtrueɵľ³¢B
+ </para>
+
+ <sect2 id="configuration-optional-transactionstrategy" revision="3">
+ <title>gUNVíªÝè</title>
+
+ <para>
+ Hibernate <literal>Session</literal> APIÍAA[LeN`àÌVXeÌÇÅ é
+ çäégUNVÉ˶µÜ¹ñB
+ àµRlNVv[ÌJDBCð¼Úg¢½¢êAJDBC API©ç
+ gUNVðÄԱƪūܷB
+ àµAJ2EEAvP[VT[oŮ쳹éÈçABeanÇgUNVðg¢A
+ KvÉ¶Ä <literal>UserTransaction</literal> ðJTA API©çÄÔ±ÆÉÈéŵå¤B
+ </para>
+
+ <para>
+ 2Âi»êÈãjÌ«ÅÝ·«Ì éR[hðÛ·é½ßÉAIvVƵĪ{IÈVXeð
+ bsO·éHibernate <literal>Transaction</literal> APIð§µÜ·B
+ HibernateÝèvpeBÌ <literal>hibernate.transaction.factory_class</literal> ðÝè·é±ÆÅ
+ éÁèÌ <literal>Transaction</literal> NXÌCX^Xð±ƪūܷB
+ </para>
+
+ <para>
+ 3ÂÌî{IÈiùÉ éjIðð°Ü·F
+ </para>
+
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term><literal>org.hibernate.transaction.JDBCTransactionFactory</literal></term>
+ <listitem>
+ <para>f[^x[X(JDBC)gUNVÉÏ÷µÜ·iftHgj</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>org.hibernate.transaction.JTATransactionFactory</literal></term>
+ <listitem>
+ <para>
+ àµA±ÌReLXgiá¦ÎAEJBZbVBean\bhjÅisÌgUNVª¶Ý·éAàµÍ
+ Vµ¢gUNVªJn³êĨèABeanÇgUNVªgíêÄ¢éêA
+ ReiÇgUNVÉÏ÷µÜ·B
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>org.hibernate.transaction.CMTTransactionFactory</literal></term>
+ <listitem>
+ <para>ReiÇJTAgUNVÉÏ÷µÜ·</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>
+ ©ª©gÌgUNVíªiá¦ÎACORBAgUNVT[rXjðè`·é±ÆàÅ«Ü·B
+ </para>
+
+ <para>
+ Hibernate̢©Ì@\iá¦ÎAñLbV
AJTAÉæéReLXgZbV jÍ
+ dzê½Â«ÌÌJTA <literal>TransactionManager</literal> ÖÌANZXðvµÜ·B
+ J2EEªÐÆÂÌJjYÉKi»³êĢȢÌÅA
+ AvP[VT[oɨ¢ÄAHibernateª <literal>TransactionManager</literal> Ìt@X
+ ðæ¾·éû@ð¾mÉ·éKvª èÜ·B
+ </para>
+
+ <table frame="topbot" id="jtamanagerlookup" revision="1">
+ <title>JTA gUNV}l[W</title>
+ <tgroup cols="2">
+ <colspec colwidth="2.5*"/>
+ <colspec colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Transaction Factory</entry>
+ <entry align="center">Application Server</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>org.hibernate.transaction.JBossTransactionManagerLookup</literal></entry>
+ <entry align="center">JBoss</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.WeblogicTransactionManagerLookup</literal></entry>
+ <entry align="center">Weblogic</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.WebSphereTransactionManagerLookup</literal></entry>
+ <entry align="center">WebSphere</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.WebSphereExtendedJTATransactionLookup</literal></entry>
+ <entry align="center">WebSphere 6</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.OrionTransactionManagerLookup</literal></entry>
+ <entry align="center">Orion</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.ResinTransactionManagerLookup</literal></entry>
+ <entry align="center">Resin</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.JOTMTransactionManagerLookup</literal></entry>
+ <entry align="center">JOTM</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.JOnASTransactionManagerLookup</literal></entry>
+ <entry align="center">JOnAS</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.JRun4TransactionManagerLookup</literal></entry>
+ <entry align="center">JRun4</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.BESTransactionManagerLookup</literal></entry>
+ <entry align="center">Borland ES</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-jndi" revision="3">
+ <title><literal>SessionFactory</literal> ÌJNDIÖÌo^</title>
+
+ <para>
+ JNDIÉo^µ½Hibernate <literal>SessionFactory</literal> ÍPÉ
+ t@NgðbNAbvµAVµ¢ <literal>Session</literal> ðìèÜ·B
+ ±êÍJNDIÉo^³ê½ <literal>Datasource</literal> ÉÍÖA¹¸A
+ ¨Ý¢ÉVvɱêçÌo^ðg¤±ÆÉӵľ³¢B
+ </para>
+
+ <para>
+ ൠ<literal>SessionFactory</literal> ðJNDIl[Xy[XÉo^µ½¢êA
+ ÁÊȼOiá¦ÎA <literal>java:hibernate/SessionFactory</literal> jð
+ <literal>hibernate.session_factory_name</literal> vpeBÉgÁľ³¢
+ ൱ÌvpeBðȪµ½êA <literal>SessionFactory</literal> Í
+ JNDIÉo^³êܹñBi±êÍTomcatÌæ¤ÈftHgÀÅJNDIªÇÝæèêpÌ«ÌêÁÉÖÅ·Bj
+ </para>
+
+ <para>
+ <literal>SessionFactory</literal> ðJNDIÉo^·éÆ«AHibernateÍ
+ <literal>hibernate.jndi.url</literal> ÌlðgpµA<literal>hibernate.jndi.class</literal>
+ ðCjVReLXgƵÄïÌ»µÜ·B
+ ൽàÝèµÈ¢êÍAftHgÌ <literal>InitialContext</literal> ðgpµÜ·B
+ </para>
+
+ <para>
+ <literal>cfg.buildSessionFactory()</literal> ðR[ã
+ HibernateÍ©®IÉ <literal>SessionFactory</literal> ðJNDIÉzuµÜ·B
+ <literal>HibernateService</literal> ÆêÉJMXfvCgðgíÈ¢ÀèA
+ ±êͱÌÄÑoµðAvP[Và̽ç©ÌX^[gAbvR[hiàµÍ[eBeBNXj
+ ÉzuµÈ¯êÎÈçÈ¢±ÆðÓ¡µÜ·BiãÅc_µÜ·j
+ </para>
+
+ <para>
+ àµJNDI <literal>SessionFactory</literal> ðg¤êAEJBâ¼ÌNXÍ
+ JNDIbNAbvðgÁÄ <literal>SessionFactory</literal> ð澵ܷB
+ </para>
+
+ <para>
+ dzê½Â«ÅÍ <literal>SessionFactory</literal> ðJNDIÉoChµA
+ »¤ÅȯêÎ <literal>static</literal> VOgðg¤±Æð§µÜ·B
+ ±¤¢Á½ÚשçAvP[VR[hðÛì·é½ßÉA
+ <literal>HibernateUtil.getSessionFactory()</literal> Ìæ¤Èwp[NXÌÉA
+ <literal>SessionFactory</literal> ðbNAbv·éR[hðB·±Æð§µÜ·B
+ ±Ìæ¤Èwp[NXÍHibernateðJn·éÖÈèiÅà èÜ·B
+ —PÍðQƵľ³¢B
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-j2ee-currentsession" revision="4">
+ <title>JTAÉæé»ÝÌZbVReLXg}l[Wg</title>
+
+ <para>
+ àÁÆàÈPÉ <literal>Session</literal> ÆgUNVðµ¤û@ÍA
+ Hibernateª©®IÉu»ÝÌv <literal>Session</literal> ðÇ·é±ÆÅ·B
+ <xref linkend="architecture-current-session">JgZbV</xref> Ìà¾ðQƵľ³¢B
+ ൠ<literal>uJTAv</literal> ZbVReLXgðgÁ½ãÅA
+ »ÝÌJTAgUNVÆHibernate <literal>Session</literal> ªÖAµÄ¢È¢êÍA
+ ÅÉ <literal>sessionFactory.getCurrentSession()</literal> ðR[µA
+ JTAgUNVÆÌÖAt¯ðsÁľ³¢B
+ <literal>uJTAv</literal> ReLXgÌ <literal>getCurrentSession()</literal> ð
+ ʶÄæ¾µ½ <literal>Session</literal> ÍAgUNVª®¹·éOÉ
+ ©®IÉtbV
µA®¹µ½ãÉÍ©®IÉN[YµÜ·B
+ ܽAeXe[ggãÉJDBCRlNVðÏÉIÉ[XµÜ·B
+ ±êÉæèJTAgUNVÌCtTCNÅ <literal>Session</literal> ðÇ·é±ÆªÅ«A
+ [U[ÌR[h©ç»Ìæ¤ÈÇð·éR[hðrÅ«Ü·B
+ <literal>UserTransaction</literal> ðʶÄJTAðvOÅÇ·é±ÆªÅ«Ü·B
+ ܽÍAi|[^uÈR[hÅ êÎjHibernate <literal>Transaction</literal> APIð
+ gUNV«EƵÄg¤±ÆàÅ«Ü·B
+ EJBReiðg¤Æ«ÍACMTÉæéé¾IgUNV«EªDܵ¢Å·B
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-j2ee-jmx" revision="1">
+ <title>JMXfvCg</title>
+
+ <para>
+ <literal>SessionFactory</literal> ðJNDI©çæ¾·é½ßÉÍ
+ <literal>cfg.buildSessionFactory()</literal> sðDZ©ÅÀsµÄ¢È¯êÎÈèܹñB
+ ȽͱêðA<literal>static</literal> ú»ubNài <literal>HibernateUtil</literal> Ìæ¤Èj©
+ <emphasis>managed service</emphasis> ƵÄHibernateðfvC·é©AÇ¿ç©ÅÀsÅ«Ü·B
+ </para>
+
+ <para>
+ JBOSSÌæ¤ÈJMXÌ@\ÅAvP[VT[oÉfvC·é½ßÉ
+ <literal>org.hibernate.jmx.HibernateService</literal> ðgÁÄAzuµÜ·B
+ ÀÛÌfvCgÆÝèÍx_[ÁLÅ·B
+ ±±ÅáƵÄJBOSS 4.0.xpÌ <literal>jboss-service.xml</literal> ð¦µÜ·B
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<server>
+
+<mbean code="org.hibernate.jmx.HibernateService"
+ name="jboss.jca:service=HibernateFactory,name=HibernateFactory">
+
+ <!-- Required services -->
+ <depends>jboss.jca:service=RARDeployer</depends>
+ <depends>jboss.jca:service=LocalTxCM,name=HsqlDS</depends>
+
+ <!-- Bind the Hibernate service to JNDI -->
+ <attribute name="JndiName">java:/hibernate/SessionFactory</attribute>
+
+ <!-- Datasource settings -->
+ <attribute name="Datasource">java:HsqlDS</attribute>
+ <attribute name="Dialect">org.hibernate.dialect.HSQLDialect</attribute>
+
+ <!-- Transaction integration -->
+ <attribute name="TransactionStrategy">
+ org.hibernate.transaction.JTATransactionFactory</attribute>
+ <attribute name="TransactionManagerLookupStrategy">
+ org.hibernate.transaction.JBossTransactionManagerLookup</attribute>
+ <attribute name="FlushBeforeCompletionEnabled">true</attribute>
+ <attribute name="AutoCloseSessionEnabled">true</attribute>
+
+ <!-- Fetching options -->
+ <attribute name="MaximumFetchDepth">5</attribute>
+
+ <!-- Second-level caching -->
+ <attribute name="SecondLevelCacheEnabled">true</attribute>
+ <attribute name="CacheProviderClass">org.hibernate.cache.EhCacheProvider</attribute>
+ <attribute name="QueryCacheEnabled">true</attribute>
+
+ <!-- Logging -->
+ <attribute name="ShowSqlEnabled">true</attribute>
+
+ <!-- Mapping files -->
+ <attribute name="MapResources">auction/Item.hbm.xml,auction/Category.hbm.xml</attribute>
+
+</mbean>
+
+</server>]]></programlisting>
+
+ <para>
+ ±Ìt@CÍ <literal>META-INF</literal> fBNgÉzu³êA
+ JARt@Cðg£µ½ <literal>.sar</literal> (service archive)ÅpbP[W»³êÜ·B
+ ¯lÉHibernatepbP[WàKvÅ·BܽAHibernateÍT[hp[eBÌCuàvµÜ·B
+ RpCµ½i±»NXÆ»Ì}bsOt@Cà¯lÉA[JCui.sart@CjÉüêÜ·B
+ G^[vCYbeaniÊíÍZbVbeanjÍ©gÌJARt@Cð۵ܷªA
+ 1ñÅizbgjfvCÂ\Èjbg̽ßÉCT[rXA[JCuƵıÌEJB JARt@CðCN[h·é
+ ±ÆªÅ«Ü·BJBossAvP[VT[oÌhL
gÉJXMT[rXÆ
+ EJBfvCgÌæè½Ìîñª èÜ·B
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/events.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/events.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/events.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,274 @@
+<?xml version="1.0" encoding="Shift_JIS"?>
+<chapter id="events">
+ <title>C^[Zv^ÆCxg</title>
+
+ <para>
+ AvP[VªHibernateÌàŶ·éCxgÉÎÅ«éÆðɧ±ƪ èÜ·B
+ éíÌêÊIÈ@\ðÀÅ«éæ¤ÉÈèA
+ ܽHibernateÌ@\ðg£·é±ÆàÅ«éæ¤ÉÈèÜ·B
+ </para>
+
+ <sect1 id="objectstate-interceptors" revision="3">
+ <title>C^[Zv^</title>
+
+ <para>
+ <literal>Interceptor</literal> C^[tFCXðgÁÄA
+ ZbV©çAvP[VÖR[obNð·é±ÆªÅ«Ü·B
+ ±êÉæèi±IuWFNgÌÛ¶AXVAíAÇÝÝÌOÉA
+ AvP[VªvpeB𸵽èìµ½èÅ«éæ¤ÉÈèÜ·B
+ ±êÍĸîñÌÇÕÉpÅ«Ü·B
+ ºÌáÅ <literal>Interceptor</literal> Í <literal>Auditable</literal>
+ ªì¬³êéÆ©®IÉ <literal>createTimestamp</literal> ðÝèµA
+ <literal>Auditable</literal> ªXV³êéÆ©®IÉ
+ <literal>lastUpdateTimestamp</literal> vpeBðXVµÜ·B
+ </para>
+
+ <para>
+ <literal>Interceptor</literal> ð¼ÚÀµ½èA
+ i³çÉæ¢ÌÍj<literal>EmptyInterceptor</literal> ðg£µ½èÅ«Ü·B
+ </para>
+
+ <programlisting><![CDATA[package org.hibernate.test;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.Iterator;
+
+import org.hibernate.EmptyInterceptor;
+import org.hibernate.Transaction;
+import org.hibernate.type.Type;
+
+public class AuditInterceptor extends EmptyInterceptor {
+
+ private int updates;
+ private int creates;
+ private int loads;
+
+ public void onDelete(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+ // do nothing
+ }
+
+ public boolean onFlushDirty(Object entity,
+ Serializable id,
+ Object[] currentState,
+ Object[] previousState,
+ String[] propertyNames,
+ Type[] types) {
+
+ if ( entity instanceof Auditable ) {
+ updates++;
+ for ( int i=0; i < propertyNames.length; i++ ) {
+ if ( "lastUpdateTimestamp".equals( propertyNames[i] ) ) {
+ currentState[i] = new Date();
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public boolean onLoad(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+ if ( entity instanceof Auditable ) {
+ loads++;
+ }
+ return false;
+ }
+
+ public boolean onSave(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+
+ if ( entity instanceof Auditable ) {
+ creates++;
+ for ( int i=0; i<propertyNames.length; i++ ) {
+ if ( "createTimestamp".equals( propertyNames[i] ) ) {
+ state[i] = new Date();
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public void afterTransactionCompletion(Transaction tx) {
+ if ( tx.wasCommitted() ) {
+ System.out.println("Creations: " + creates + ", Updates: " + updates, "Loads: " + loads);
+ }
+ updates=0;
+ creates=0;
+ loads=0;
+ }
+
+}]]></programlisting>
+
+ <para>
+ C^[Zv^ÉÍñíÞ èÜ·F
+ <literal>Session</literal> XR[vÌàÌÆ
+ <literal>SessionFactory</literal> XR[vÌàÌÅ·B
+ </para>
+
+ <para>
+ <literal>Session</literal> XR[vÌC^[Zv^ÍA
+ ZbVðI[v·éÆ«ÉwèµÜ·B
+ <literal>Interceptor</literal> ðøÉæéSessionFactory.openSession()
+ ÌI[o[[h\bhÌêÂðg¢Ü·B
+ </para>
+
+ <programlisting><![CDATA[Session session = sf.openSession( new AuditInterceptor() );]]></programlisting>
+
+ <para>
+ <literal>SessionFactory</literal> XR[vÌC^[Zv^Í <literal>Configuration</literal>
+ IuWFNgðgÁÄo^µÜ·B
+ ±êÍ <literal>SessionFactory</literal> Ì\zæèàDæ³êÜ·B
+ ±ÌêAñ³êéC^[Zv^Í <literal>SessionFactory</literal>
+ ©çI[v³ê½·×ÄÌZbVÉKp³êÜ·B
+ ±êÍgp·éC^[Zv^ð¾¦IÉwèµÄZbVðI[vµÈ¢ÀèA»¤ÈèÜ·B
+ <literal>SessionFactory</literal> XR[vÌC^[Zv^ÍXbhZ[tÅȯêÎÈèܹñB
+ ¡ÌZbVªiöÝIÉj±ÌC^[Zv^ð¯ÀsÅgp·é±ÆÉÈé½ßA
+ ZbVÅLÌóÔði[µÈ¢æ¤ÉCð¯ľ³¢B
+ </para>
+
+ <programlisting><![CDATA[new Configuration().setInterceptor( new AuditInterceptor() );]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="objectstate-events" revision="4">
+ <title>CxgVXe</title>
+
+ <para>
+ i±»wÅÁèÌCxgÉεȯêÎÈçÈ¢êA
+ Hibernate3Ì <emphasis>Cxg</emphasis> A[LeN`ðg¤±ÆàÅ«Ü·B
+ CxgVXeÍC^[Zv^ÆêÉg¤©AܽÍC^[Zv^ÌãíèƵÄg¤±ÆªÅ«Ü·B
+ </para>
+
+ <para>
+ {¿IÉ <literal>Session</literal> C^[tFCXÌ·×ÄÌ\bhÍA
+ 1ÂÌCxgÆÝÉÖAµÜ·B
+ á¦Î <literal>LoadEvent</literal>A<literal>FlushEvent</literal> ÈǪ èÜ·
+ iè`ÏÝÌCxg^ÌêÉ¢ÄÍAXMLÝèt@CÌDTDâ
+ <literal>org.hibernate.event</literal> pbP[Wð²×ľ³¢jB
+ NGXgª±êçÌ\bhÌ1©çìçêéÆ«A
+ HibernateÌ <literal>Session</literal> ÍKØÈCxg𶬵A
+ »ÌCxg^ÉÝè³ê½CxgXiÉnµÜ·B
+ ·Îçµ¢±ÆÉA±êçÌXiÍ»Ì\bhƯ¶ðÀµÜ·B
+ ÆÍ¢¦AXiC^[tFCXÌêÂð©RÉJX^ÀÅ«Ü·
+ iÂÜèA<literal>LoadEvent</literal> Ío^³ê½ <literal>LoadEventListener</literal>
+ C^[tFCXÌÀÉæè³êÜ·jB
+ »ÌêA»ÌÀÉÍ <literal>Session</literal> ©çìçê½ÇÌæ¤È <literal>load()</literal>
+ NGXgðà·éÓCª èÜ·B
+ </para>
+
+ <para>
+ XiÍÀãVOgÅ éƩȹܷB
+ ÂÜèXiÍNGXgÔŤL³êé½ßA
+ CX^XÏƵÄóÔðÛ·é׫ÅÍȢƢ¤±ÆÅ·B
+ </para>
+
+ <para>
+ JX^Xi͵½¢CxgÉ¢ÄKØÈC^[tFCXðÀ·é׫ŷB
+ ÖÈîêNX̤¿ÌêÂðp³µÄàæ¢Å·
+ iܽÍHibernateªftHgÅgp·éCxgXiðp³µÄàæ¢Å·B
+ ·Îçµ¢±ÆÉA±ÌÚI̽ßÉñfinalƵÄé¾³êĢܷjB
+ JX^XiÍ <literal>Configuration</literal> IuWFNgðgÁÄvO©ço^·é©A
+ HibernateÌXMLÝèt@CÅwèÅ«Ü·
+ ivpeBt@CÅé¾IÉÝè·éû@ÍT|[g³êĢܹñjB
+ JX^[hCxgXiÌáð¦µÜ·B
+ </para>
+
+ <programlisting><![CDATA[public class MyLoadListener implements LoadEventListener {
+ // this is the single method defined by the LoadEventListener interface
+ public void onLoad(LoadEvent event, LoadEventListener.LoadType loadType)
+ throws HibernateException {
+ if ( !MySecurity.isAuthorized( event.getEntityClassName(), event.getEntityId() ) ) {
+ throw MySecurityException("Unauthorized access");
+ }
+ }
+}]]></programlisting>
+
+ <para>
+ ftHgXiÈOÌXiðg¤ÉÍAHibernateÖÌÝèàKvÅ·F
+ </para>
+
+<programlisting><![CDATA[<hibernate-configuration>
+ <session-factory>
+ ...
+ <event type="load">
+ <listener class="com.eg.MyLoadListener"/>
+ <listener class="org.hibernate.event.def.DefaultLoadEventListener"/>
+ </event>
+ </session-factory>
+</hibernate-configuration>]]></programlisting>
+
+ <para>
+ ܽ»Ì¼ÉAvOÅo^·éû@à èÜ·F
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = new Configuration();
+LoadEventListener[] stack = { new MyLoadListener(), new DefaultLoadEventListener() };
+cfg.EventListeners().setLoadEventListeners(stack);]]></programlisting>
+
+ <para>
+ Xiðé¾IÉo^·éÆA»ÌXiÌCX^Xð¤LūܹñB
+ ¡Ì <literal><listener/></literal> vfů¶NX¼ªgíêéÆA
+ »ê¼êÌQÆÍ»ÌNXÌÊXÌCX^Xðw·±ÆÉÈèÜ·B
+ Xi^ÌÔÅXiCX^Xð¤L·éKvª êÎA
+ vOÅo^·éû@ðÌçȯêÎÈèܹñB
+ </para>
+
+ <para>
+ ȺC^[tFCXðÀµÄAÁ»µ½^ðÝèÉwè·éÌŵ天H
+ XiÌÀNXÉA¡ÌCxgXiC^[tFCXðÀÅ«é©çÅ·B
+ o^ÉÇÁÅ^ðwè·é±ÆÅAJX^XiÌon/offðÝèÉÈPÉØèÖ¦çêÜ·B
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-decl-security" revision="2">
+ <title>HibernateÌé¾IÈZL
eB</title>
+ <para>
+ êÊIÉHibernateAvP[VÌé¾IÈZL
eBÍAZbVt@T[hwÅǵܷB
+ »ÝAHiberenate3ÍJACCŵ©ÂAJAASÅFص½ANVðµÄ¢Ü·B
+ ±êÍCxgA[LeN`ÌÅãÊÉgÝÜêÄ¢éIvVÌ@\Å·B
+ </para>
+
+ <para>
+ ܸÅÉAKØÈCxgXiðÝèµÄJAASFØðg¦éæ¤ÉµÈ¯êÎÈèܹñB
+ </para>
+
+ <programlisting><![CDATA[<listener type="pre-delete" class="org.hibernate.secure.JACCPreDeleteEventListener"/>
+<listener type="pre-update" class="org.hibernate.secure.JACCPreUpdateEventListener"/>
+<listener type="pre-insert" class="org.hibernate.secure.JACCPreInsertEventListener"/>
+<listener type="pre-load" class="org.hibernate.secure.JACCPreLoadEventListener"/>]]></programlisting>
+
+ <para>
+ ÁèÌCxg^ÉεĿå¤ÇêÂÌXiª éÆ«A
+ <literal><listener type="..." class="..."/></literal>
+ Í <literal><event type="..."><listener class="..."/></event></literal>
+ ÌȪ`É߬Ȣ±ÆÉӵľ³¢B
+ </para>
+
+ <para>
+ ÉA¯¶ <literal>hibernate.cfg.xml</literal> Å[Ép[~bVð^¦Ä¾³¢F
+ </para>
+
+ <programlisting><![CDATA[<grant role="admin" entity-name="User" actions="insert,update,read"/>
+<grant role="su" entity-name="User" actions="*"/>]]></programlisting>
+
+ <para>
+ ±Ì[¼Ígp·éJACCvoC_Éð³êé[Å·B
+ </para>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/example_mappings.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/example_mappings.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/example_mappings.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,657 @@
+<?xml version="1.0" encoding="Shift_JIS"?>
+<chapter id="example-mappings">
+ <title>áF¢ë¢ëÈ}bsO</title>
+
+ <para>
+ ±ÌÍÅÍAæè¡GÈÖAÌ}bsOð¢Â©ÐîµÜ·B
+ </para>
+
+ <sect1 id="example-mappings-emp">
+ <title>ÙpÒ/]Æõ</title>
+
+ <para>
+ <literal>Employer</literal> Æ <literal>Employee</literal> ÌÖWð\·ÈºÌfÍA
+ ÖAÌ\»ÉÀÛÌGeBeBNXi <literal>Employment</literal> j
+ ðg¢Ü·B
+ ȺÈçA¯¶2ÂÌp[eBÉ¡ÌúÔÙp³êéÆ¢¤±Æª è¦é©çÅ·B
+ ¨àÌlÆ]Æõ̼Oðf»·é½ßÉR|[lgðgÁĢܷB
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/EmployerEmployee.gif" format="GIF" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/EmployerEmployee.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ }bsOhL
gÌêáÅ·F
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Employer" table="employers">
+ <id name="id">
+ <generator class="sequence">
+ <param name="sequence">employer_id_seq</param>
+ </generator>
+ </id>
+ <property name="name"/>
+ </class>
+
+ <class name="Employment" table="employment_periods">
+
+ <id name="id">
+ <generator class="sequence">
+ <param name="sequence">employment_id_seq</param>
+ </generator>
+ </id>
+ <property name="startDate" column="start_date"/>
+ <property name="endDate" column="end_date"/>
+
+ <component name="hourlyRate" class="MonetaryAmount">
+ <property name="amount">
+ <column name="hourly_rate" sql-type="NUMERIC(12, 2)"/>
+ </property>
+ <property name="currency" length="12"/>
+ </component>
+
+ <many-to-one name="employer" column="employer_id" not-null="true"/>
+ <many-to-one name="employee" column="employee_id" not-null="true"/>
+
+ </class>
+
+ <class name="Employee" table="employees">
+ <id name="id">
+ <generator class="sequence">
+ <param name="sequence">employee_id_seq</param>
+ </generator>
+ </id>
+ <property name="taxfileNumber"/>
+ <component name="name" class="Name">
+ <property name="firstName"/>
+ <property name="initial"/>
+ <property name="lastName"/>
+ </component>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ <literal>SchemaExport</literal> Ŷ¬µ½e[uXL[}Å·B
+ </para>
+
+ <programlisting><![CDATA[create table employers (
+ id BIGINT not null,
+ name VARCHAR(255),
+ primary key (id)
+)
+
+create table employment_periods (
+ id BIGINT not null,
+ hourly_rate NUMERIC(12, 2),
+ currency VARCHAR(12),
+ employee_id BIGINT not null,
+ employer_id BIGINT not null,
+ end_date TIMESTAMP,
+ start_date TIMESTAMP,
+ primary key (id)
+)
+
+create table employees (
+ id BIGINT not null,
+ firstName VARCHAR(255),
+ initial CHAR(1),
+ lastName VARCHAR(255),
+ taxfileNumber VARCHAR(255),
+ primary key (id)
+)
+
+alter table employment_periods
+ add constraint employment_periodsFK0 foreign key (employer_id) references employers
+alter table employment_periods
+ add constraint employment_periodsFK1 foreign key (employee_id) references employees
+create sequence employee_id_seq
+create sequence employment_id_seq
+create sequence employer_id_seq]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-mappings-authorwork">
+ <title>ìÒ/ìi</title>
+
+ <para>
+ <literal>Work</literal> , <literal>Author</literal> »µÄ <literal>Person</literal>
+ ÌÖWð\·ÈºÌfðl¦ÄÝľ³¢B
+ <literal>Work</literal> Æ <literal>Author</literal> ÌÖWð½Î½ÖAÅ\µÄ¢Ü·B
+ <literal>Author</literal> Æ <literal>Person</literal> ÌÖWÍêÎêÖAƵÄ\µÄ¢Ü·B
+ ¼ÉÍ <literal>Author</literal> ª <literal>Person</literal> ðg£·éÆ¢¤û@à èÜ·B
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/AuthorWork.gif" format="GIF" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/AuthorWork.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ ȺÌ}bsOhL
gͱÌæ¤ÈÖWð³mÉ\»µÄ¢Ü·B
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Work" table="works" discriminator-value="W">
+
+ <id name="id" column="id">
+ <generator class="native"/>
+ </id>
+ <discriminator column="type" type="character"/>
+
+ <property name="title"/>
+ <set name="authors" table="author_work">
+ <key column name="work_id"/>
+ <many-to-many class="Author" column name="author_id"/>
+ </set>
+
+ <subclass name="Book" discriminator-value="B">
+ <property name="text"/>
+ </subclass>
+
+ <subclass name="Song" discriminator-value="S">
+ <property name="tempo"/>
+ <property name="genre"/>
+ </subclass>
+
+ </class>
+
+ <class name="Author" table="authors">
+
+ <id name="id" column="id">
+ <!-- The Author must have the same identifier as the Person -->
+ <generator class="assigned"/>
+ </id>
+
+ <property name="alias"/>
+ <one-to-one name="person" constrained="true"/>
+
+ <set name="works" table="author_work" inverse="true">
+ <key column="author_id"/>
+ <many-to-many class="Work" column="work_id"/>
+ </set>
+
+ </class>
+
+ <class name="Person" table="persons">
+ <id name="id" column="id">
+ <generator class="native"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ ±Ì}bsOÉÍ4ÂÌe[uª èÜ·B
+ <literal>works</literal> , <literal>authors</literal> , <literal>persons</literal>
+ Í»ê¼êAdAìÒAlÌf[^ð۵ܷB
+ <literal>author_work</literal> ÍìÒÆìiðN·éÖAe[uÅ·B
+ ÈºÍ <literal>SchemaExport</literal> Ŷ¬µ½e[uXL[}Å·B
+ </para>
+
+ <programlisting><![CDATA[create table works (
+ id BIGINT not null generated by default as identity,
+ tempo FLOAT,
+ genre VARCHAR(255),
+ text INTEGER,
+ title VARCHAR(255),
+ type CHAR(1) not null,
+ primary key (id)
+)
+
+create table author_work (
+ author_id BIGINT not null,
+ work_id BIGINT not null,
+ primary key (work_id, author_id)
+)
+
+create table authors (
+ id BIGINT not null generated by default as identity,
+ alias VARCHAR(255),
+ primary key (id)
+)
+
+create table persons (
+ id BIGINT not null generated by default as identity,
+ name VARCHAR(255),
+ primary key (id)
+)
+
+alter table authors
+ add constraint authorsFK0 foreign key (id) references persons
+alter table author_work
+ add constraint author_workFK0 foreign key (author_id) references authors
+alter table author_work
+ add constraint author_workFK1 foreign key (work_id) references works]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-mappings-customerorderproduct">
+ <title>Úq/¶/»i</title>
+
+ <para>
+ ³ÄA <literal>Customer</literal> , <literal>Order</literal> , <literal>LineItem</literal>
+ <literal>Product</literal> ÌÖWð\·fðl¦ÄÝܵå¤B
+ <literal>Customer</literal> Æ <literal>Order</literal> ÍêνÌÖAÅ·ªA
+ <literal>Order</literal> / <literal>LineItem</literal> / <literal>Product</literal>
+ ÍÇÌæ¤É\»·é׫ŵ天H
+ <literal>LineItem</literal> ðA<literal>Order</literal> Æ <literal>Product</literal>
+ ̽νÖAð\»·éÖANXƵÄ}bsOµÜµ½B
+ HibernateÅͱêðR|WbgvfÆÄÑÜ·B
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/CustomerOrderProduct.gif" format="GIF" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/CustomerOrderProduct.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ }bsOhL
gF
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Customer" table="customers">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="name"/>
+ <set name="orders" inverse="true">
+ <key column="customer_id"/>
+ <one-to-many class="Order"/>
+ </set>
+ </class>
+
+ <class name="Order" table="orders">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="date"/>
+ <many-to-one name="customer" column="customer_id"/>
+ <list name="lineItems" table="line_items">
+ <key column="order_id"/>
+ <list-index column="line_number"/>
+ <composite-element class="LineItem">
+ <property name="quantity"/>
+ <many-to-one name="product" column="product_id"/>
+ </composite-element>
+ </list>
+ </class>
+
+ <class name="Product" table="products">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="serialNumber"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ <literal>customers</literal> , <literal>orders</literal> , <literal>line_items</literal> ,
+ <literal>products</literal> Í»ê¼êAÚqA¶A¶¾×A»iÌf[^ð۵ܷB
+ <literal>line_items</literal> ͶƻiðN·éÖAe[uƵÄà«Ü·B
+ </para>
+
+ <programlisting><![CDATA[create table customers (
+ id BIGINT not null generated by default as identity,
+ name VARCHAR(255),
+ primary key (id)
+)
+
+create table orders (
+ id BIGINT not null generated by default as identity,
+ customer_id BIGINT,
+ date TIMESTAMP,
+ primary key (id)
+)
+
+create table line_items (
+ line_number INTEGER not null,
+ order_id BIGINT not null,
+ product_id BIGINT,
+ quantity INTEGER,
+ primary key (order_id, line_number)
+)
+
+create table products (
+ id BIGINT not null generated by default as identity,
+ serialNumber VARCHAR(255),
+ primary key (id)
+)
+
+alter table orders
+ add constraint ordersFK0 foreign key (customer_id) references customers
+alter table line_items
+ add constraint line_itemsFK0 foreign key (product_id) references products
+alter table line_items
+ add constraint line_itemsFK1 foreign key (order_id) references orders]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="misc">
+ <title>íXG½È}bsOá</title>
+
+ <para>
+ ±±É éáÍ·×ÄHibernateÌeXgXC[g©çæèܵ½B
+ »±ÉÍA¼ÉཱིñÌ}bsOÌ᪠èÜ·B
+ HibernatefBXgr
[VÌ <literal>test</literal> tH_ð©Ä¾³¢B
+ </para>
+
+ <para>TODO: ±±É¶Íðßé</para>
+
+ <sect2 id="example-mappings-typed-onetone">
+ <title>u^t¯³ê½vêÎêÖA</title>
+<programlisting><![CDATA[<class name="Person">
+ <id name="name"/>
+ <one-to-one name="address"
+ cascade="all">
+ <formula>name</formula>
+ <formula>'HOME'</formula>
+ </one-to-one>
+ <one-to-one name="mailingAddress"
+ cascade="all">
+ <formula>name</formula>
+ <formula>'MAILING'</formula>
+ </one-to-one>
+</class>
+
+<class name="Address" batch-size="2"
+ check="addressType in ('MAILING', 'HOME', 'BUSINESS')">
+ <composite-id>
+ <key-many-to-one name="person"
+ column="personName"/>
+ <key-property name="type"
+ column="addressType"/>
+ </composite-id>
+ <property name="street" type="text"/>
+ <property name="state"/>
+ <property name="zip"/>
+</class>]]></programlisting>
+ </sect2>
+
+ <sect2 id="example-mappings-composite-key">
+ <title>¡L[Ìá</title>
+<programlisting><![CDATA[<class name="Customer">
+
+ <id name="customerId"
+ length="10">
+ <generator class="assigned"/>
+ </id>
+
+ <property name="name" not-null="true" length="100"/>
+ <property name="address" not-null="true" length="200"/>
+
+ <list name="orders"
+ inverse="true"
+ cascade="save-update">
+ <key column="customerId"/>
+ <index column="orderNumber"/>
+ <one-to-many class="Order"/>
+ </list>
+
+</class>
+
+<class name="Order" table="CustomerOrder" lazy="true">
+ <synchronize table="LineItem"/>
+ <synchronize table="Product"/>
+
+ <composite-id name="id"
+ class="Order$Id">
+ <key-property name="customerId" length="10"/>
+ <key-property name="orderNumber"/>
+ </composite-id>
+
+ <property name="orderDate"
+ type="calendar_date"
+ not-null="true"/>
+
+ <property name="total">
+ <formula>
+ ( select sum(li.quantity*p.price)
+ from LineItem li, Product p
+ where li.productId = p.productId
+ and li.customerId = customerId
+ and li.orderNumber = orderNumber )
+ </formula>
+ </property>
+
+ <many-to-one name="customer"
+ column="customerId"
+ insert="false"
+ update="false"
+ not-null="true"/>
+
+ <bag name="lineItems"
+ fetch="join"
+ inverse="true"
+ cascade="save-update">
+ <key>
+ <column name="customerId"/>
+ <column name="orderNumber"/>
+ </key>
+ <one-to-many class="LineItem"/>
+ </bag>
+
+</class>
+
+<class name="LineItem">
+
+ <composite-id name="id"
+ class="LineItem$Id">
+ <key-property name="customerId" length="10"/>
+ <key-property name="orderNumber"/>
+ <key-property name="productId" length="10"/>
+ </composite-id>
+
+ <property name="quantity"/>
+
+ <many-to-one name="order"
+ insert="false"
+ update="false"
+ not-null="true">
+ <column name="customerId"/>
+ <column name="orderNumber"/>
+ </many-to-one>
+
+ <many-to-one name="product"
+ insert="false"
+ update="false"
+ not-null="true"
+ column="productId"/>
+
+</class>
+
+<class name="Product">
+ <synchronize table="LineItem"/>
+
+ <id name="productId"
+ length="10">
+ <generator class="assigned"/>
+ </id>
+
+ <property name="description"
+ not-null="true"
+ length="200"/>
+ <property name="price" length="3"/>
+ <property name="numberAvailable"/>
+
+ <property name="numberOrdered">
+ <formula>
+ ( select sum(li.quantity)
+ from LineItem li
+ where li.productId = productId )
+ </formula>
+ </property>
+
+</class>]]></programlisting>
+ </sect2>
+
+ <sect2 id="example-mappings-composite-key-manytomany">
+ <title>¡L[®«ð¤L·é½Î½</title>
+<programlisting><![CDATA[<class name="User" table="`User`">
+ <composite-id>
+ <key-property name="name"/>
+ <key-property name="org"/>
+ </composite-id>
+ <set name="groups" table="UserGroup">
+ <key>
+ <column name="userName"/>
+ <column name="org"/>
+ </key>
+ <many-to-many class="Group">
+ <column name="groupName"/>
+ <formula>org</formula>
+ </many-to-many>
+ </set>
+</class>
+
+<class name="Group" table="`Group`">
+ <composite-id>
+ <key-property name="name"/>
+ <key-property name="org"/>
+ </composite-id>
+ <property name="description"/>
+ <set name="users" table="UserGroup" inverse="true">
+ <key>
+ <column name="groupName"/>
+ <column name="org"/>
+ </key>
+ <many-to-many class="User">
+ <column name="userName"/>
+ <formula>org</formula>
+ </many-to-many>
+ </set>
+</class>
+]]></programlisting>
+ </sect2>
+
+ <sect2 id="example-mappings-content-discrimination">
+ <title>discriminationÉîÃàe</title>
+<programlisting><![CDATA[<class name="Person"
+ discriminator-value="P">
+
+ <id name="id"
+ column="person_id"
+ unsaved-value="0">
+ <generator class="native"/>
+ </id>
+
+
+ <discriminator
+ type="character">
+ <formula>
+ case
+ when title is not null then 'E'
+ when salesperson is not null then 'C'
+ else 'P'
+ end
+ </formula>
+ </discriminator>
+
+ <property name="name"
+ not-null="true"
+ length="80"/>
+
+ <property name="sex"
+ not-null="true"
+ update="false"/>
+
+ <component name="address">
+ <property name="address"/>
+ <property name="zip"/>
+ <property name="country"/>
+ </component>
+
+ <subclass name="Employee"
+ discriminator-value="E">
+ <property name="title"
+ length="20"/>
+ <property name="salary"/>
+ <many-to-one name="manager"/>
+ </subclass>
+
+ <subclass name="Customer"
+ discriminator-value="C">
+ <property name="comments"/>
+ <many-to-one name="salesperson"/>
+ </subclass>
+
+</class>]]></programlisting>
+ </sect2>
+
+ <sect2 id="example-mappings-association-alternatekeys" revision="2">
+ <title>ãÖL[ÌÖA</title>
+<programlisting><![CDATA[<class name="Person">
+
+ <id name="id">
+ <generator class="hilo"/>
+ </id>
+
+ <property name="name" length="100"/>
+
+ <one-to-one name="address"
+ property-ref="person"
+ cascade="all"
+ fetch="join"/>
+
+ <set name="accounts"
+ inverse="true">
+ <key column="userId"
+ property-ref="userId"/>
+ <one-to-many class="Account"/>
+ </set>
+
+ <property name="userId" length="8"/>
+
+</class>
+
+<class name="Address">
+
+ <id name="id">
+ <generator class="hilo"/>
+ </id>
+
+ <property name="address" length="300"/>
+ <property name="zip" length="5"/>
+ <property name="country" length="25"/>
+ <many-to-one name="person" unique="true" not-null="true"/>
+
+</class>
+
+<class name="Account">
+ <id name="accountId" length="32">
+ <generator class="uuid"/>
+ </id>
+
+ <many-to-one name="user"
+ column="userId"
+ property-ref="userId"/>
+
+ <property name="type" not-null="true"/>
+
+</class>]]></programlisting>
+ </sect2>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/example_parentchild.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/example_parentchild.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/example_parentchild.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,362 @@
+<?xml version="1.0" encoding="Shift_JIS"?>
+<chapter id="example-parentchild">
+ <title>áFe/q
+ </title>
+
+ <para>
+ VK[UªHibernateðgÁÄܸÅɵ¤fÌêÂÉAeq^Ìf»ª èÜ·B
+ ±Ìf»ÉÍñÂÌAv[`ª¶ÝµÜ·BÆèí¯VK[UÉÆÁÄA
+ ³Ü´ÜÈR©çÅàÖ¾ÆvíêéAv[`ÍA<literal>e</literal> ©ç <literal>q</literal>
+ ÖÌ <literal><one-to-many></literal> ÖAÉæè <literal>e</literal> Æ <literal>q</literal>
+ ̼ûðGeBeBNXƵÄfO·éû@Å·
+ ià¤êÂÌû@ÍA<literal>q</literal> ð <literal><composite-element></literal> ƵÄè`·éàÌÅ·jB
+ ±êÅiHibernateɨ¯éjêνÖAÌftHgÌZ}eBNXªAÊíÌ¡vfÌ}bsOæèàA
+ eqÖWÌZ}eBNX©ç¢±Æªí©èÜ·B
+ »êÅÍeqÖWðø¦I©ÂGKgÉfO·é½ßÉA
+ <emphasis>JXP[hìðgÁ½oûüêνÖA</emphasis> ̵¢ûðྵܷB±êÍÜÁ½ïµ¢àÌÅÍ èܹñB
+
+ </para>
+
+ <sect1 id="example-parentchild-collections">
+ <title>RNVÉÖ·éÓ</title>
+
+ <para>
+ HibernateÌRNVÍ©gÌGeBeBÌ_IȪÆl¦çêA
+ µÄïÜ·éGeBeBÌàÌÅÍ èܹñB±êÍv½IÈá¢Å·I
+ ±êÍȺÌæ¤ÈÊÉÈèÜ·F
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ IuWFNgðRNV©çíAܽÍRNVÉÇÁ·éÆ«A
+ RNVÌI[i[Ìo[WÔÍCNg³êÜ·B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ àµRNV©çí³ê½IuWFNgªl^ÌCX^X
+ iá¦ÎR|WbgGg)¾Á½ÈçÎA»ÌIuWFNgÍi±IÅÍÈÈèA
+ »ÌóÔÍf[^x[X©ç®SÉí³êÜ·B
+ ¯¶æ¤ÉAl^ÌCX^XðRNVÉÇÁ·éÆA»ÌóÔÍ·®Éi±IÉÈèÜ·B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ êûAàµGeBeBªRNViêνܽͽνÖA)©çí³êÄàA
+ ftHgÅÍ»êÍí³êܹñB±Ì®ìÍ®SÉêѵĢܷB
+ ·Èí¿A¼ÌGeBeBÌàóÔðÏXµÄàAÖA·éGeBeBªÁŷ׫ÅÍȢƢ¤±ÆÅ·B
+ ¯lÉAGeBeBªRNVÉÇÁ³êÄàAftHgÅÍ»ÌGeBeBÍi±IÉÍÈèܹñB
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ »ÌãíèÉAftHgÌ®ìÅÍAGeBeBðRNVÉÇÁ·éÆPÉñÂÌGeBeBÔÌNð쬵A
+ êûGeBeBðí·éÆNàíµÜ·B±êÍ·×ÄÌP[Xɨ¢ÄñíÉKØÅ·B
+ ±êªKØÅÈ¢ÌÍe/qÖWÌêÅ·B±Ìêq̶¶ÍeÌCtTCNɧÀ³êé©çÅ·B
+ </para>
+
+ </sect1>
+
+ <sect1 id="example-parentchild-bidir">
+ <title>oûüêν
+ </title>
+
+ <para>
+ <literal>Parent</literal> ©ç <literal>Child</literal> ÖÌPÈ <literal><one-to-many></literal> ÖA©çnßéƵܷB
+ </para>
+
+ <programlisting><![CDATA[<set name="children">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ ȺÌR[hðÀs·éÆA
+ </para>
+
+ <programlisting><![CDATA[Parent p = .....;
+Child c = new Child();
+p.getChildren().add(c);
+session.save(c);
+session.flush();]]></programlisting>
+
+ <para>
+ HibernateÍñÂÌSQL¶ðsµÜ·:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>c</literal>ÉηéR[h𶬷é<literal>INSERT</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>p</literal>©ç<literal>c</literal>ÖÌNð쬷é<literal>UPDATE</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ±êÍñø¦IȾ¯ÅÍÈA<literal>parent_id</literal> Jɨ¢Ä <literal>NOT NULL</literal> §ñÉήܷB
+ RNVÌ}bsOÅ <literal>not-null="true"</literal> Æwè·é±ÆÅAnull§ñá½ðð·é±ÆªÅ«Ü·:
+ </para>
+
+ <programlisting><![CDATA[<set name="children">
+ <key column="parent_id" not-null="true"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ µ©µ±ÌðôͧūܹñB
+ </para>
+ <para>
+ ±Ì®ì̪{IÈ´öÍA<literal>p</literal> ©ç <literal>c</literal> ÖÌN
+ iOL[ <literal>parent_id</literal>)Í <literal>Child</literal> IuWFNgÌóÔÌêÆÍl¦çê¸A
+ »Ì½ß <literal>INSERT</literal> ÉæÁÄNª¶¬³êÈ¢±ÆÅ·B
+ Å·©çAðôÍNðChild}bsOÌêÉ·é±ÆÅ·B
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="parent" column="parent_id" not-null="true"/>]]></programlisting>
+
+ <para>
+ (ܽ <literal>Child</literal> NXÉ <literal>parent</literal> vpeBðÇÁ·éKvª èÜ·B)
+ </para>
+
+ <para>
+ »êÅÍ <literal>Child</literal> GeBeBªNÌóÔð§ä·éæ¤ÉÈÁ½ÌÅA
+ RNVªNðXVµÈ¢æ¤ÉµÜµå¤B»êÉÍ <literal>inverse</literal> ®«ðg¢Ü·B
+ </para>
+
+ <programlisting><![CDATA[<set name="children" inverse="true">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ ȺÌR[hðg¦ÎAVµ¢ <literal>Child</literal> ðÇÁ·é±ÆªÅ«Ü·B
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+Child c = new Child();
+c.setParent(p);
+p.getChildren().add(c);
+session.save(c);
+session.flush();]]></programlisting>
+
+ <para>
+ ±êÉæèASQLÌ <literal>INSERT</literal> ¶ªê¾¯ªs³êéæ¤ÉÈèܵ½I
+ </para>
+
+ <para>
+ व»·éÉÍA<literal>Parent</literal> Ì <literal>addChild()</literal> \bhð쬵ܷB
+ </para>
+
+ <programlisting><![CDATA[public void addChild(Child c) {
+ c.setParent(this);
+ children.add(c);
+}]]></programlisting>
+
+ <para>
+ <literal>Child</literal> ðÇÁ·éR[hͱÌæ¤ÉÈèÜ·B
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+Child c = new Child();
+p.addChild(c);
+session.save(c);
+session.flush();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-parentchild-cascades">
+ <title>CtTCNÌJXP[h</title>
+
+ <para>
+ ¾¦IÉ <literal>save()</literal> ðR[·éÌÍܾÏíµ¢àÌÅ·B±êðJXP[hðgÁÄεܷB
+ </para>
+
+ <programlisting><![CDATA[<set name="children" inverse="true" cascade="all">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ ±êÉæèæÙÇÌR[hð±Ìæ¤ÉP»µÜ·
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+Child c = new Child();
+p.addChild(c);
+session.flush();]]></programlisting>
+
+ <para>
+ ¯lÉ <literal>Parent</literal> ð۶ܽÍí·éÆ«ÉAqðêÂêÂæèoµÄµ¤KvÍ èܹñB
+ ȺÌR[hÍ <literal>p</literal> ðíµA»µÄf[^x[X©ç»Ìqð·×ÄíµÜ·B
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+session.delete(p);
+session.flush();]]></programlisting>
+
+ <para>
+ µ©µ±ÌR[hÍ
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+Child c = (Child) p.getChildren().iterator().next();
+p.getChildren().remove(c);
+c.setParent(null);
+session.flush();]]></programlisting>
+
+ <para>
+ f[^x[X©ç <literal>c</literal> ðíµÜ¹ñB<literal>p</literal> ÖÌNðí·é
+ i»µÄ±ÌP[XÅÍ <literal>NOT NULL</literal> §ñá½ðø«N±·j¾¯Å·B
+ <literal>Child</literal> Ì <literal>delete()</literal> 𾦷éKvª èÜ·B
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+Child c = (Child) p.getChildren().iterator().next();
+p.getChildren().remove(c);
+session.delete(c);
+session.flush();]]></programlisting>
+
+ <para>
+ ¡±ÌP[XÅÍÀÛÉ <literal>Child</literal> ªeȵÅͶÝÅ«È¢æ¤ÉÈèܵ½B
+ »Ì½ßAàµRNV©ç <literal>Child</literal> ðæèêA±êàíµ½¢Å·B
+ »Ì½ßÉÍ <literal>cascade="all-delete-orphan"</literal> ðgíȯêÎÈèܹñB
+ </para>
+
+ <programlisting><![CDATA[<set name="children" inverse="true" cascade="all-delete-orphan">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ ÓFRNVÌ}bsOÅ <literal>inverse="true"</literal> ÆwèµÄàA
+ RNVÌvfÌCe[VÉæÁÄAËRJXP[hªÀs³êÜ·B
+ »Ì½ßàµJXP[hÅIuWFNgðZ[uAíAXV·éKvª éÈçA
+ »êðRNVÉÇÁµÈ¯êÎÈèܹñBPÉ <literal>setParent()</literal> ðÄÔ¾¯ÅÍs\ªÅ·B
+ </para>
+
+ </sect1>
+
+ <sect1 id="example-parentchild-update">
+ <title>JXP[hÆ <literal>unsaved-value</literal></title>
+
+ <para>
+ <literal>Parent</literal> ªA é <literal>Session</literal> Å[h³êAUIÌANVÅÏXªÁ¦çêA
+ <literal>update()</literal> ðÄñűÌÏXðVµ¢ZbVÅi±»µ½¢ÆµÜ·B
+ <literal>Parent</literal> ªqÌRNVð¿AJXP[hXVªLøÉÈÁÄ¢é½ßA
+ HibernateÍÇÌqªVµCX^X»³ê½©AÇêªf[^x[XÌù¶ÌsÉ·éÌ©ðméKvª èÜ·B
+ <literal>Parent</literal> Æ <literal>Child</literal> ̼ûª <literal>java.lang.Long</literal>
+ ^̯ÊvpeB𶬵½ÆµÜµå¤B
+ HibernateÍÇÌqªVµ¢àÌ©ðè·é½ßɯÊvpeBÌlðg¢Ü·(versionâtimestampvpeBàg¦Ü·B
+ <xref linkend="manipulatingdata-updating-detached"/> QÆ)BHibernate3ÉÈÁÄA
+ ¾¦IÉ <literal>unsaved-value</literal> ðwè·éKvÍÈÈèܵ½B
+ </para>
+
+ <para>
+ ȺÌR[hÍ <literal>parent</literal> Æ <literal>child</literal> ðXVµA<literal>newChild</literal> ð}üµÜ·B
+ </para>
+
+ <programlisting><![CDATA[//parent and child were both loaded in a previous session
+//parentÆchildͼûÆàAÈOÌSessionÅ[h³êĢܷ
+parent.addChild(child);
+Child newChild = new Child();
+parent.addChild(newChild);
+session.update(parent);
+session.flush();]]></programlisting>
+
+ <para>
+ ±êçͶ¬³ê½¯ÊqÌêÉÍñíÉÇ¢ÌÅ·ªAèÄç꽯ÊqÆ¡¯ÊqÌêÍǤŵ天H
+ ±êÍHibernateªAi[UÉæèèÄç꽯ÊqðÂjVµCX^X»³ê½IuWFNgÆA
+ ÈOÌSessionÅ[h³ê½IuWFNgðæÊÅ«È¢½ßAæèﵢŷB
+ ±ÌêAHibernateÍ^CX^v©o[WÌvpeBÌÇ¿ç©ðg¤©AñLbV
Éâ¢í¹Ü·B
+ Å«ÌêAsª¶Ý·é©Ç¤©f[^x[Xð©Ü·B
+ </para>
+
+ <!-- undocumenting
+ <para>
+ There is one further possibility. The <literal>Interceptor</literal> method named
+ <literal>isUnsaved()</literal> lets the application implement its own strategy for distinguishing
+ newly instantiated objects. For example, you could define a base class for your persistent classes.
+ </para>
+
+ <programlisting><![CDATA[public class Persistent {
+ private boolean _saved = false;
+ public void onSave() {
+ _saved=true;
+ }
+ public void onLoad() {
+ _saved=true;
+ }
+ ......
+ public boolean isSaved() {
+ return _saved;
+ }
+}]]></programlisting>
+
+ <para>
+ (The <literal>saved</literal> property is non-persistent.)
+ Now implement <literal>isUnsaved()</literal>, along with <literal>onLoad()</literal>
+ and <literal>onSave()</literal> as follows.
+ </para>
+
+ <programlisting><![CDATA[public Boolean isUnsaved(Object entity) {
+ if (entity instanceof Persistent) {
+ return new Boolean( !( (Persistent) entity ).isSaved() );
+ }
+ else {
+ return null;
+ }
+}
+
+public boolean onLoad(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+
+ if (entity instanceof Persistent) ( (Persistent) entity ).onLoad();
+ return false;
+}
+
+public boolean onSave(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+
+ if (entity instanceof Persistent) ( (Persistent) entity ).onSave();
+ return false;
+}]]></programlisting>
+
+ <para>
+ Don't worry; in Hibernate3 you don't need to write any of this kind of code if you don't want to.
+ </para>
+ -->
+ </sect1>
+
+ <sect1 id="example-parentchild-conclusion">
+ <title>_
+ </title>
+
+ <para>
+ ±±ÅÍ©ÈèÌÊðvñµ½ÌÅAÅÌ Í¬µÄ¢éæ¤Évíêé©àµêܹñB
+ µ©µÀÛÍA·×ÄñíÉÇ®ìµÜ·BÙÆñÇÌHibernateAvP[VÅÍA½ÌêÊÅeqp^[ðgpµÜ·B
+ </para>
+
+ <para>
+ ÅÌiÅãÖû@É¢ÄGêܵ½BãLÌæ¤ÈâèÍ <literal><composite-element></literal> }bsOÌêͶݹ¸A
+ Éà©©í縻êÍm©ÉeqÖWÌZ}eBNXð¿Ü·B
+ µ©µcOȪçA¡vfNXÉÍñÂÌå«È§Àª èÜ·F
+ 1ÂÍ¡vfÍRNVð±ƪūȢ±ÆÅ·Bà¤ÐÆÂÍA
+ j[NÈeÅÍÈ¢GeBeBÌqÆÈé׫ÅÍȢƢ¤±ÆÅ·
+ </para>
+
+ </sect1>
+
+</chapter>
\ No newline at end of file
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/example_weblog.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/example_weblog.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/example_weblog.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,433 @@
+<?xml version="1.0" encoding="Shift_JIS"?>
+<chapter id="example-weblog">
+ <title>á: WeblogAvP[V</title>
+
+ <sect1 id="example-weblog-classes">
+ <title>i±NX
+ </title>
+
+ <para>
+ i±NXªEFuOÆAEFuOÉf¦³ê½Úð\µÄ¢Ü·B
+ »êçÍÊíÌeqÖWƵÄfO³êÜ·ªA
+ setÅÍÈðÁ½bagðgp·é±ÆɵܷB
+ </para>
+
+ <programlisting><![CDATA[package eg;
+
+import java.util.List;
+
+public class Blog {
+ private Long _id;
+ private String _name;
+ private List _items;
+
+ public Long getId() {
+ return _id;
+ }
+ public List getItems() {
+ return _items;
+ }
+ public String getName() {
+ return _name;
+ }
+ public void setId(Long long1) {
+ _id = long1;
+ }
+ public void setItems(List list) {
+ _items = list;
+ }
+ public void setName(String string) {
+ _name = string;
+ }
+}]]></programlisting>
+
+ <programlisting><![CDATA[package eg;
+
+import java.text.DateFormat;
+import java.util.Calendar;
+
+public class BlogItem {
+ private Long _id;
+ private Calendar _datetime;
+ private String _text;
+ private String _title;
+ private Blog _blog;
+
+ public Blog getBlog() {
+ return _blog;
+ }
+ public Calendar getDatetime() {
+ return _datetime;
+ }
+ public Long getId() {
+ return _id;
+ }
+ public String getText() {
+ return _text;
+ }
+ public String getTitle() {
+ return _title;
+ }
+ public void setBlog(Blog blog) {
+ _blog = blog;
+ }
+ public void setDatetime(Calendar calendar) {
+ _datetime = calendar;
+ }
+ public void setId(Long long1) {
+ _id = long1;
+ }
+ public void setText(String string) {
+ _text = string;
+ }
+ public void setTitle(String string) {
+ _title = string;
+ }
+}]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-weblog-mappings">
+ <title>HibernateÌ}bsO
+ </title>
+
+ <para>
+ XML}bsOÍA¡ÅÍÆÄàÈPÈ͸ŷB
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="eg">
+
+ <class
+ name="Blog"
+ table="BLOGS">
+
+ <id
+ name="id"
+ column="BLOG_ID">
+
+ <generator class="native"/>
+
+ </id>
+
+ <property
+ name="name"
+ column="NAME"
+ not-null="true"
+ unique="true"/>
+
+ <bag
+ name="items"
+ inverse="true"
+ order-by="DATE_TIME"
+ cascade="all">
+
+ <key column="BLOG_ID"/>
+ <one-to-many class="BlogItem"/>
+
+ </bag>
+
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="eg">
+
+ <class
+ name="BlogItem"
+ table="BLOG_ITEMS"
+ dynamic-update="true">
+
+ <id
+ name="id"
+ column="BLOG_ITEM_ID">
+
+ <generator class="native"/>
+
+ </id>
+
+ <property
+ name="title"
+ column="TITLE"
+ not-null="true"/>
+
+ <property
+ name="text"
+ column="TEXT"
+ not-null="true"/>
+
+ <property
+ name="datetime"
+ column="DATE_TIME"
+ not-null="true"/>
+
+ <many-to-one
+ name="blog"
+ column="BLOG_ID"
+ not-null="true"/>
+
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-weblog-code">
+ <title>HibernateÌR[h</title>
+
+ <para>
+ ȺÌNXÍA
+ HibernateűêçÌNXðgÁÄÅ«é±Æð¢Â©¦µÄ¢Ü·B
+ </para>
+
+ <programlisting><![CDATA[package eg;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Iterator;
+import java.util.List;
+
+import org.hibernate.HibernateException;
+import org.hibernate.Query;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.tool.hbm2ddl.SchemaExport;
+
+public class BlogMain {
+
+ private SessionFactory _sessions;
+
+ public void configure() throws HibernateException {
+ _sessions = new Configuration()
+ .addClass(Blog.class)
+ .addClass(BlogItem.class)
+ .buildSessionFactory();
+ }
+
+ public void exportTables() throws HibernateException {
+ Configuration cfg = new Configuration()
+ .addClass(Blog.class)
+ .addClass(BlogItem.class);
+ new SchemaExport(cfg).create(true, true);
+ }
+
+ public Blog createBlog(String name) throws HibernateException {
+
+ Blog blog = new Blog();
+ blog.setName(name);
+ blog.setItems( new ArrayList() );
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ session.persist(blog);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return blog;
+ }
+
+ public BlogItem createBlogItem(Blog blog, String title, String text)
+ throws HibernateException {
+
+ BlogItem item = new BlogItem();
+ item.setTitle(title);
+ item.setText(text);
+ item.setBlog(blog);
+ item.setDatetime( Calendar.getInstance() );
+ blog.getItems().add(item);
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ session.update(blog);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return item;
+ }
+
+ public BlogItem createBlogItem(Long blogid, String title, String text)
+ throws HibernateException {
+
+ BlogItem item = new BlogItem();
+ item.setTitle(title);
+ item.setText(text);
+ item.setDatetime( Calendar.getInstance() );
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ Blog blog = (Blog) session.load(Blog.class, blogid);
+ item.setBlog(blog);
+ blog.getItems().add(item);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return item;
+ }
+
+ public void updateBlogItem(BlogItem item, String text)
+ throws HibernateException {
+
+ item.setText(text);
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ session.update(item);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ }
+
+ public void updateBlogItem(Long itemid, String text)
+ throws HibernateException {
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ BlogItem item = (BlogItem) session.load(BlogItem.class, itemid);
+ item.setText(text);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ }
+
+ public List listAllBlogNamesAndItemCounts(int max)
+ throws HibernateException {
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ List result = null;
+ try {
+ tx = session.beginTransaction();
+ Query q = session.createQuery(
+ "select blog.id, blog.name, count(blogItem) " +
+ "from Blog as blog " +
+ "left outer join blog.items as blogItem " +
+ "group by blog.name, blog.id " +
+ "order by max(blogItem.datetime)"
+ );
+ q.setMaxResults(max);
+ result = q.list();
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return result;
+ }
+
+ public Blog getBlogAndAllItems(Long blogid)
+ throws HibernateException {
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ Blog blog = null;
+ try {
+ tx = session.beginTransaction();
+ Query q = session.createQuery(
+ "from Blog as blog " +
+ "left outer join fetch blog.items " +
+ "where blog.id = :blogid"
+ );
+ q.setParameter("blogid", blogid);
+ blog = (Blog) q.uniqueResult();
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return blog;
+ }
+
+ public List listBlogsAndRecentItems() throws HibernateException {
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ List result = null;
+ try {
+ tx = session.beginTransaction();
+ Query q = session.createQuery(
+ "from Blog as blog " +
+ "inner join blog.items as blogItem " +
+ "where blogItem.datetime > :minDate"
+ );
+
+ Calendar cal = Calendar.getInstance();
+ cal.roll(Calendar.MONTH, false);
+ q.setCalendar("minDate", cal);
+
+ result = q.list();
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return result;
+ }
+}]]></programlisting>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/filters.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/filters.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/filters.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding="Shift_JIS"?>
+<chapter id="filters">
+ <title>f[^ÌtB^O</title>
+
+ <para>
+ Hibernate3ÅÍu«v[ÉîâÄf[^𵤽ßÌæúIÈû@ðpӵĢܷB
+ <emphasis>Hibernate filter</emphasis> ÍO[oÅA¼Ot«ÅAp[^»³ê½tB^Å·B
+ ±êÍHibernateZbV²ÆÉLø³øðØèÖ¦çêÜ·B
+ </para>
+
+ <sect1 id="objectstate-filters">
+ <title>HibernateÌtB^</title>
+
+ <para>
+ Hibernate3ÍtB^NCeAð 究ßè`µA
+ ±êçÌtB^ðNXâRNVxÉÁ¦é@\ðÁ¦Üµ½B
+ tB^NCeAͧñßðè`·é@\Å·B
+ ±êçÌtB^ðÍp[^»Å«éÆ¢¤±Æð«A
+ NXâ³Ü´ÜÈRNVvfÅpÂ\È¢where£åÉñíÉæĢܷB
+ AvP[VÍA^¦çê½tB^ðÂ\ɷ׫©A
+ »µÄ»Ìp[^lð½É·×«©ðÀsÉè·é±ÆªÅ«Ü·B
+ tB^Íf[^x[Xr
[Ìæ¤Égp³êÜ·ªAAvP[VàÅÍp[^»³êÜ·B
+
+ </para>
+
+ <para>
+ tB^ðg¤½ßÉÍܸAKØÈ}bsOvfÉè`AÇÁµÈÄÍÈèܹñB
+ tB^ðè`·é½ßÉÍA
+ <literal><hibernate-mapping/></literal> vfàÅ <literal><filter-def/></literal> vfðgpµÜ·BF
+ </para>
+
+ <programlisting><![CDATA[<filter-def name="myFilter">
+ <filter-param name="myFilterParam" type="string"/>
+</filter-def>]]></programlisting>
+
+ <para>
+ »¤µÄtB^ÍNXÖÆÑt¯çêÜ·BF
+ </para>
+
+ <programlisting><![CDATA[<class name="myClass" ...>
+ ...
+ <filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
+</class>]]></programlisting>
+
+ <para>
+ ܽARNVÉεÄÍÌæ¤ÉÈèÜ·BF
+ </para>
+
+ <programlisting><![CDATA[<set ...>
+ <filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
+</set>]]></programlisting>
+
+ <para>
+ Ç¿çÉεÄà(ܽA»ê¼êð¡)¯ÉÝè·é±ÆàÅ«Ü·B
+ </para>
+
+ <para>
+ <literal>Session</literal> ãÌ\bhÍ <literal>enableFilter(String filterName)</literal>,
+ <literal>getEnabledFilter(String filterName)</literal>, <literal>disableFilter(String filterName)</literal> Å·B
+ ftHgÅÍAtB^Í^¦çê½ZbVÉεÄgp <emphasis>ūܹñ</emphasis> B
+ <literal>Filter</literal> CX^XðÔèlÆ·é <literal>Session.enabledFilter()</literal> \bhðg¤±ÆÅA
+ tB^;¦IÉgpÂ\ÆÈèÜ·B
+ ãÅè`µ½PÈtB^ÌgpÍA±Ìæ¤ÉÈèÜ·BF
+ </para>
+
+ <programlisting><![CDATA[session.enableFilter("myFilter").setParameter("myFilterParam", "some-value");]]></programlisting>
+
+ <para>
+ org.hibernate.FilterC^[tFCXÌ\bhÍA
+ Hibernate̽ɤʵĢé\bhA½ðµÄ¢é±ÆÉӵľ³¢B
+ </para>
+
+ <para>
+ LøÈR[hf[^p^[ðÂêf[^ðgÁ½®SÈáÅ·F
+ </para>
+
+ <programlisting><![CDATA[<filter-def name="effectiveDate">
+ <filter-param name="asOfDate" type="date"/>
+</filter-def>
+
+<class name="Employee" ...>
+...
+ <many-to-one name="department" column="dept_id" class="Department"/>
+ <property name="effectiveStartDate" type="date" column="eff_start_dt"/>
+ <property name="effectiveEndDate" type="date" column="eff_end_dt"/>
+...
+ <!--
+ Note that this assumes non-terminal records have an eff_end_dt set to
+ a max db date for simplicity-sake
+
+
+ -->
+ <filter name="effectiveDate"
+ condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
+</class>
+
+<class name="Department" ...>
+...
+ <set name="employees" lazy="true">
+ <key column="dept_id"/>
+ <one-to-many class="Employee"/>
+ <filter name="effectiveDate"
+ condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ íÉ»ÝÌLøR[hðÔp·é±ÆðÛØ·é½ßÉA
+ PÉAÐõf[^ÌõæèOÉZbVãÌtB^ðLøɵܷB
+
+
+ </para>
+
+<programlisting><![CDATA[Session session = ...;
+session.enabledFilter("effectiveDate").setParameter("asOfDate", new Date());
+List results = session.createQuery("from Employee as e where e.salary > :targetSalary")
+ .setLong("targetSalary", new Long(1000000))
+ .list();
+]]></programlisting>
+
+ <para>
+ ãLÌHQLÅÍAȨ̂̿ñÉ¢ľ¦IÉGê½¾¯Å·ªA
+ LøÉÈÁÄ¢étB^̨©°ÅA±ÌNGÍ¿ª100hÈãÌ»ðÌÐõ¾¯ðԵܷB
+ </para>
+
+ <para>
+ (HQL©[htFb`ÅjOðÂtB^ðg¤ÂàèÈçA
+ ð®ÌûüÉӵľ³¢B
+
+ ±êͶO̽ßÉÝè·é̪ÅàÀSÅ·B
+ êÊIÉAZqÌãJ¼É±¯ÄÅÌp[^ðzuµÄ¾³¢B
+ </para>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/inheritance_mapping.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/inheritance_mapping.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/inheritance_mapping.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,487 @@
+<?xml version="1.0" encoding="Shift_JIS"?>
+<chapter id="inheritance">
+ <title>p³}bsO</title>
+
+ <sect1 id="inheritance-strategies" revision="3">
+ <title>3ÂÌíª</title>
+
+ <para>
+ HibernateÍ3ÂÌî{IÈp³Ì}bsOíªðT|[gµÜ·B
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ NXKw²ÆÌe[uitable-per-class-hierarchyj
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ TuNX²ÆÌe[uitable-per-subclassj
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ïÛNX²ÆÌe[uitable-per-concrete-classj
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Á¦Ä4ÂÚÉAHibernateÍí¸©ÉÙÈé«¿ðÁ½|[tBYðT|[gµÜ·B
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ ÃÙI|[tBY
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ¯êÌp³KwÌÙÈéu`ÉεÄÙÈé}bsOíªðg¤±ÆªÅ«Ü·B
+ »ÌêÉÍSÌÌKwÉné|[tBYðÀ»·é½ßÉÃÙI|[tBYðgpµÜ·B
+ µ©µAHibernateͯ¶[g <literal><class></literal> vfàÅ
+ <literal><subclass></literal> }bsOA<literal><joined-subclass></literal> }bsOA
+ <literal><union-subclass></literal> }bsO̯gpðT|[gµÄ¢Ü¹ñB
+ <literal><subclass></literal> vfÆ <literal><join></literal> vfðgÝí¹é±ÆÅA
+ ¯ê <literal><class></literal> vfàÅÌ table-per-hierarchy íªÆ
+ table-per-subclass íªÌ¯gpÍÂ\Å·BÌáð©Ä¾³¢B
+ </para>
+
+ <para>
+ <literal>subclass</literal>, <literal>union-subclass</literal> Æ
+ <literal>joined-subclass</literal> }bsOð¡Ì}bsOhL
gÉè`·é±ÆªoA
+ <literal>hibernate-mapping</literal> ̼ºÉzuµÜ·B
+ ±êÍVµ¢}bsOt@CðÇÁ·é¾¯ÅANXKwðg£Å«éÆ¢¤±ÆÅ·B
+ 究ß}bvµ½X[p[NXðwèµÄATuNX}bsOÉ <literal>extends</literal>
+ ®«ðLqµÈ¯êÎÈèܹñB
+ ÓF±ÌÁ¥ÉæèAÈOÍ}bsOEhL
gÌÔªdvŵ½B
+ Hibernate3©çÍAextendsL[[hðg¤êA}bsOhL
gÌÔÍâèÉÈèܹñB
+
+ PÂÌ}bsOt@CàÅÔt¯ðs¤Æ«ÍA
+ ËRƵÄATuNXðè`·éOÉX[p[NXðè`·éKvª èÜ·B)
+ </para>
+
+ <programlisting><![CDATA[
+ <hibernate-mapping>
+ <subclass name="DomesticCat" extends="Cat" discriminator-value="D">
+ <property name="name" type="string"/>
+ </subclass>
+ </hibernate-mapping>]]></programlisting>
+
+
+ <sect2 id="inheritance-tableperclass" >
+ <title>NXKw²ÆÌe[uitable-per-class-hierarchyj</title>
+
+ <para>
+ á¦ÎAC^[tFCX <literal>Payment</literal> ÆA»êðÀµ½
+ <literal>CreditCardPayment</literal>A<literal>CashPayment</literal>A
+ <literal>ChequePayment</literal> ª éƵܷBKw²ÆÌe[u}bsOÍ
+ ȺÌæ¤ÉÈèÜ·B
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment" table="PAYMENT">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <discriminator column="PAYMENT_TYPE" type="string"/>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <subclass name="CreditCardPayment" discriminator-value="CREDIT">
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </subclass>
+ <subclass name="CashPayment" discriminator-value="CASH">
+ ...
+ </subclass>
+ <subclass name="ChequePayment" discriminator-value="CHEQUE">
+ ...
+ </subclass>
+</class>]]></programlisting>
+
+ <para>
+ ¿å¤ÇêÂÌe[uªKvÅ·B
+ ±Ì}bsOíªÉÍêÂå«È§Àª èÜ·B
+ <literal>CCTYPE</literal> Ìæ¤ÈATuNXÅé¾³ê½JÍ <literal>NOT NULL</literal>
+ §ñðÄܹñB
+ </para>
+
+ </sect2>
+
+ <sect2 id="inheritance-tablepersubclass">
+ <title>TuNX²ÆÌe[uitable-per-subclassj</title>
+
+ <para>
+ table-per-subclass }bsOÍȺÌæ¤ÉÈèÜ·B
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment" table="PAYMENT">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <joined-subclass name="CreditCardPayment" table="CREDIT_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </joined-subclass>
+ <joined-subclass name="CashPayment" table="CASH_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ ...
+ </joined-subclass>
+ <joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ ...
+ </joined-subclass>
+</class>]]></programlisting>
+
+ <para>
+ ±Ì}bsOÉÍ4ÂÌe[uªKvÅ·B3ÂÌTuNXe[uÍ
+ X[p[NXe[uÆÌÖAð¦·åL[ðÁĢܷ
+ iÀÛAÖWfãÍêÎêÖAÅ·jB
+ </para>
+
+ </sect2>
+
+ <sect2 id="inheritance-tablepersubclass-discriminator" revision="2">
+ <title>ÙÊqðp¢½ table-per-subclass</title>
+
+ <para>
+ HibernateÌ table-per-subclass ÀÍAdiscriminatorJðKvƵȢ±Æðo¦Ä¨¢Ä¾³¢B
+ HibernateÈOÌO/R}bp[ÍAtable-per-subclass ÉÙÈéÀðp¢Ü·B
+ »êÍAX[p[NXÌe[uÉ^CvdiscriminatorJðKvƵܷB
+ ±ÌAv[`ÍÀª¢ïÉÈèÜ·ªAÖWÌ_©ç©éÆAæè³mÈàÌÅ·B
+ table-per-subclass íªÅdiscriminatorJðg¢½¯êÎA
+ <literal><subclass></literal> Æ <literal><join></literal>
+ ðȺÌæ¤ÉgÝí¹ÄgÁľ³¢B
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment" table="PAYMENT">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <discriminator column="PAYMENT_TYPE" type="string"/>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <subclass name="CreditCardPayment" discriminator-value="CREDIT">
+ <join table="CREDIT_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </join>
+ </subclass>
+ <subclass name="CashPayment" discriminator-value="CASH">
+ <join table="CASH_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ ...
+ </join>
+ </subclass>
+ <subclass name="ChequePayment" discriminator-value="CHEQUE">
+ <join table="CHEQUE_PAYMENT" fetch="select">
+ <key column="PAYMENT_ID"/>
+ ...
+ </join>
+ </subclass>
+</class>]]></programlisting>
+
+ <para>
+ IvVÌ <literal>fetch="select"</literal> é¾ÍA
+ X[p[NXÌNGÀsÉOðgÁÄA
+ TuNXÌ <literal>ChequePayment</literal> f[^ð澵Ȣæ¤Éwè·é½ßÌàÌÅ·B
+ </para>
+
+ </sect2>
+
+ <sect2 id="inheritance-mixing-tableperclass-tablepersubclass">
+ <title>table-per-subclass Æ table-per-class-hierarchy ̬</title>
+
+ <para>
+ ±ÌAv[`ðgp·éÆAtable-per-hierarchy Æ table-per-subclass íªð
+ gÝí¹éàÂ\Å·B
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment" table="PAYMENT">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <discriminator column="PAYMENT_TYPE" type="string"/>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <subclass name="CreditCardPayment" discriminator-value="CREDIT">
+ <join table="CREDIT_PAYMENT">
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </join>
+ </subclass>
+ <subclass name="CashPayment" discriminator-value="CASH">
+ ...
+ </subclass>
+ <subclass name="ChequePayment" discriminator-value="CHEQUE">
+ ...
+ </subclass>
+</class>]]></programlisting>
+
+ <para>
+ ¢¸êÌ}bsOíªÅ ÁÄàA[gÅ é <literal>Payment</literal> NXÖÌ
+ |[tBbNÈÖAÍ <literal><many-to-one></literal> ðgÁÄ}bsOµÜ·B
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="payment" column="PAYMENT_ID" class="Payment"/>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="inheritance-tableperconcrete" revision="2">
+ <title>ïÛNX²ÆÌe[uitable-per-concrete-classj</title>
+
+ <para>
+ table-per-concrete-class íªÌ}bsOÉηéAv[`ÍA2 èÜ·B
+ 1ÂÚÍ <literal><union-subclass></literal> ðp·éû@Å·B
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="sequence"/>
+ </id>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <union-subclass name="CreditCardPayment" table="CREDIT_PAYMENT">
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </union-subclass>
+ <union-subclass name="CashPayment" table="CASH_PAYMENT">
+ ...
+ </union-subclass>
+ <union-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
+ ...
+ </union-subclass>
+</class>]]></programlisting>
+
+ <para>
+ TuNX²ÆÉ3ÂÌe[uªKvÅ·B»ê¼êÌe[uÍAp³vpeBðÜñ¾A
+ NXÌSÄÌvpeBÉηéJðè`µÜ·B
+ </para>
+
+ <para>
+ ±ÌAv[`ÉͧÀª èÜ·B
+ »êÍAvpeBªX[p[NXÉ}bsO³êÄ¢½êA
+ SÄÌTuNXɨ¢ÄJ¼ª¯¶ÅȯêÎÈçȢƢ¤àÌÅ·B
+ iHibernateÌ¡ãÌ[XÅÉa³êé©àµêܹñjB
+ <union-subclass> ðgÁ½ table-per-concrete-class íªÅͯÊq¶¬íªðgpūܹñB
+ åL[𶬷é½ßÌV[hÍASÄÌ union subclass ÌKwàŤL·éKvª é©çÅ·B
+ </para>
+
+ <para>
+ àµX[p[NXªÛNXÈçA<literal>abstract="true"</literal>Æ}bsOµÜ·B
+ à¿ëñAX[p[NXªÛNXÅÈ¢ÈçAX[p[NXÌCX^Xð
+ Û·é½ßÌe[uÌÇÁªKvÆÈèÜ·(ãÌáÅÌftHgÍ <literal>PAYMENT</literal> )B
+ </para>
+
+ </sect2>
+
+ <sect2 id="inheritance-tableperconcreate-polymorphism">
+ <title>ÃÙI|[tBYðp¢½ table-per-concrete-class</title>
+
+ <para>
+ à¤êÂÌAv[`ÍÃÙI|[tBYÌgpÅ·B
+ </para>
+
+ <programlisting><![CDATA[<class name="CreditCardPayment" table="CREDIT_PAYMENT">
+ <id name="id" type="long" column="CREDIT_PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="amount" column="CREDIT_AMOUNT"/>
+ ...
+</class>
+
+<class name="CashPayment" table="CASH_PAYMENT">
+ <id name="id" type="long" column="CASH_PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="amount" column="CASH_AMOUNT"/>
+ ...
+</class>
+
+<class name="ChequePayment" table="CHEQUE_PAYMENT">
+ <id name="id" type="long" column="CHEQUE_PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="amount" column="CHEQUE_AMOUNT"/>
+ ...
+</class>]]></programlisting>
+
+ <para>
+ <literal>Payment</literal> C^[tFCXªÇ±ÉྦIɦ³êĢȢ±ÆÉӵľ³¢B
+ »µÄA<literal>Payment</literal> vpeBª»ê¼êÌTuNXÉ}bsO³êÄ¢é±ÆÉàӵľ³¢B
+ àµd¡ð𯽢ÌÅ êÎAXMLGeBeBÌpðl¦Ä¾³¢B
+ iáF <literal>DOCTYPE</literal> é¾É¨¯é <literal>[ <!ENTITY allproperties SYSTEM "allproperties.xml"> ]</literal>
+ ÆA}bsOɨ¯é <literal>&allproperties;</literal>j
+ </para>
+
+ <para>
+ ±ÌAv[`Ì_ÍAHibernateª|[tBbNÈNGÌÀsÉSQL <literal>UNION</literal>
+ 𶬵Ȣ_ŷB
+ </para>
+
+ <para>
+ ±Ì}bsOíªÉεÄÍA<literal>Payment</literal> ÖÌ|[tBbNÈÖAÍ
+ ÊíA<literal><any></literal> ðgÁÄ}bsO³êÜ·B
+ </para>
+
+ <programlisting><![CDATA[<any name="payment" meta-type="string" id-type="long">
+ <meta-value value="CREDIT" class="CreditCardPayment"/>
+ <meta-value value="CASH" class="CashPayment"/>
+ <meta-value value="CHEQUE" class="ChequePayment"/>
+ <column name="PAYMENT_CLASS"/>
+ <column name="PAYMENT_ID"/>
+</any>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="inheritace-mixingpolymorphism">
+ <title>¼Ìp³}bsOÆÃÙI|[tBYÌgÝí¹</title>
+
+ <para>
+ ±Ì}bsOÉ¢ÄÌXÈéÓ_ª èÜ·B
+ TuNXª©gð <literal><class></literal> vfƵÄ}bsOµÄ¢éÌÅA
+ i©Â <literal>Payment</literal> ÍPÈéC^[tFCXÈÌÅjA
+ »ê¼êÌTuNXÍÈPɻ̼Ìp³KwÌêÆÈèÜ·B
+ iµ©àA¡ÜÅǨè <literal>Payment</literal> C^[tFCXÉηé|[tBbNÈNG
+ ðgp·é±ÆªÅ«Ü·j
+ </para>
+
+ <programlisting><![CDATA[<class name="CreditCardPayment" table="CREDIT_PAYMENT">
+ <id name="id" type="long" column="CREDIT_PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <discriminator column="CREDIT_CARD" type="string"/>
+ <property name="amount" column="CREDIT_AMOUNT"/>
+ ...
+ <subclass name="MasterCardPayment" discriminator-value="MDC"/>
+ <subclass name="VisaPayment" discriminator-value="VISA"/>
+</class>
+
+<class name="NonelectronicTransaction" table="NONELECTRONIC_TXN">
+ <id name="id" type="long" column="TXN_ID">
+ <generator class="native"/>
+ </id>
+ ...
+ <joined-subclass name="CashPayment" table="CASH_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ <property name="amount" column="CASH_AMOUNT"/>
+ ...
+ </joined-subclass>
+ <joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ <property name="amount" column="CHEQUE_AMOUNT"/>
+ ...
+ </joined-subclass>
+</class>]]></programlisting>
+
+ <para>
+ à¤êxq×Ü·ªA<literal>Payment</literal> ;¦IÉè`³êܹñB
+ àµA<literal>Payment</literal> C^[tFCXÉεÄNGðÀs·éÈç
+ iá¦Î <literal>from Payment</literal> ßðgÁÄjA
+ HibernateÍ©®IÉ <literal>CreditCardPayment</literal>
+ iÆCreditCardPaymentÌTuNXA<literal>Payment</literal> ÌÀÅ é½ßjA
+ ¨æÑA<literal>CashPayment</literal> A<literal>ChequePayment</literal>
+ ÌCX^XðԵܷB
+ <literal>NonelectronicTransaction</literal> CX^XÍԵܹñB
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="inheritance-limitations">
+ <title>§À</title>
+
+ <para>
+ table-per-concrete-class }bsOíªÖÌuÃÙI|[tBYvAv[`ÉÍ
+ ¢Â©Ì§Àª èÜ·B<literal><union-subclass></literal> }bsOÉεÄà
+ µãß̧Àª èÜ·B
+ </para>
+
+ <para>
+ Ìe[uÉAHibernateɨ¯étable-per-concrete-class}bsOÌ
+ §ÀâÃÙI|[tBY̧Àð¦µÜ·B
+ </para>
+
+ <table frame="topbot">
+ <title>p³}bsOÌ@\</title>
+ <tgroup cols='8' align='left' colsep='1' rowsep='1'>
+ <colspec colname='c1' colwidth="1*"/>
+ <colspec colname='c2' colwidth="1*"/>
+ <colspec colname='c3' colwidth="1*"/>
+ <colspec colname='c4' colwidth="1*"/>
+ <colspec colname='c5' colwidth="1*"/>
+ <colspec colname='c6' colwidth="1*"/>
+ <colspec colname='c7' colwidth="1*"/>
+ <colspec colname='c8' colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>p³íª</entry>
+ <entry>½ÎêÌ|[tBY</entry>
+ <entry>êÎêÌ|[tBY</entry>
+ <entry>êνÌ|[tBY</entry>
+ <entry>½Î½Ì|[tBY</entry>
+ <entry>|[tBYðgÁ½<literal>load()/get()</literal></entry>
+ <entry>|[tBYðgÁ½NG</entry>
+ <entry>|[tBYðgÁ½</entry>
+ <entry>OÉæétFb`</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>table per class-hierarchy</entry>
+ <entry><literal><many-to-one></literal></entry>
+ <entry><literal><one-to-one></literal></entry>
+ <entry><literal><one-to-many></literal></entry>
+ <entry><literal><many-to-many></literal></entry>
+ <entry><literal>s.get(Payment.class, id)</literal></entry>
+ <entry><literal>from Payment p</literal></entry>
+ <entry><literal>from Order o join o.payment p</literal></entry>
+ <entry><emphasis>T|[g</emphasis></entry>
+ </row>
+ <row>
+ <entry>table per subclass</entry>
+ <entry><literal><many-to-one></literal></entry>
+ <entry><literal><one-to-one></literal></entry>
+ <entry><literal><one-to-many></literal></entry>
+ <entry><literal><many-to-many></literal></entry>
+ <entry><literal>s.get(Payment.class, id)</literal></entry>
+ <entry><literal>from Payment p</literal></entry>
+ <entry><literal>from Order o join o.payment p</literal></entry>
+ <entry><emphasis>T|[g</emphasis></entry>
+ </row>
+ <row>
+ <entry>table per concrete-class (union-subclass)</entry>
+ <entry><literal><many-to-one></literal></entry>
+ <entry><literal><one-to-one></literal></entry>
+ <entry><literal><one-to-many></literal> (for <literal>inverse="true"</literal> only)</entry>
+ <entry><literal><many-to-many></literal></entry>
+ <entry><literal>s.get(Payment.class, id)</literal></entry>
+ <entry><literal>from Payment p</literal></entry>
+ <entry><literal>from Order o join o.payment p</literal></entry>
+ <entry><emphasis>T|[g</emphasis></entry>
+ </row>
+ <row>
+ <entry>table per concrete class (implicit polymorphism)</entry>
+ <entry><literal><any></literal></entry>
+ <entry><emphasis>T|[gµÄ¢Ü¹ñ</emphasis></entry>
+ <entry><emphasis>T|[gµÄ¢Ü¹ñ</emphasis></entry>
+ <entry><literal><many-to-any></literal></entry>
+ <entry><literal>s.createCriteria(Payment.class).add( Restrictions.idEq(id) ).uniqueResult()</literal></entry>
+ <entry><literal>from Payment p</literal></entry>
+ <entry><emphasis>T|[gµÄ¢Ü¹ñ</emphasis></entry>
+ <entry><emphasis>T|[gµÄ¢Ü¹ñ</emphasis></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect1>
+
+</chapter>
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/performance.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/performance.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/performance.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1378 @@
+<?xml version="1.0" encoding="Shift_JIS"?>
+<chapter id="performance">
+ <title>ptH[}XÌüP</title>
+
+ <sect1 id="performance-fetching" revision="2">
+ <title>tFb`íª</title>
+
+ <para>
+ <emphasis>tFb`íª</emphasis> ÍAAvP[VªÖAðirQ[g·éKvª é
+ Æ«ÉAHibernateªÖAIuWFNgð³·é½ßÉgp·éíªÅ·BtFb`íªÍO/R}bsOÌ
+ ^f[^Éé¾·é©AÁèÌHQLA <literal>Criteria</literal> NGÅI[o[ChµÜ·B
+ </para>
+
+ <para>
+ Hibernate3Íɦ·tFb`íªðè`µÄ¢Ü·B
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>tFb`</emphasis> - HibernateÍ <literal>OUTER JOIN</literal> ðgÁÄA
+ ÖA·éCX^XâRNVðPÂÌ <literal>SELECT</literal>
+ ųµÜ·B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>ZNgtFb`</emphasis> - 2ñÚÌ <literal>SELECT</literal>
+ ÅÖA·éGeBeBâRNVð³µÜ·B <literal>lazy="false"</literal>
+ ž¦IÉxtFb`ð³øɵȯêÎA±Ì2ñÚÌselectÍÀÛÉ
+ ÖAÉANZXµ½Æ«ÌÝÀs³êéŵå¤B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>TuZNgtFb`</emphasis> - 2ñÚÌ <literal>SELECT</literal>
+ ÅA¼OÌNGâtFb`ųµ½·×ÄÌvfÉÖA·éRNVð
+ ³µÜ·B <literal>lazy="false"</literal>
+ ž¦IÉxtFb`ð³øɵȯêÎA±Ì2ñÚÌselectÍÀÛÉ
+ ÖAÉANZXµ½Æ«ÌÝÀs³êéŵå¤B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>ob`tFb`</emphasis> - ZNgtFb`̽ßÌÅK»³ê½íª
+ - HibernateÍGeBeBÌCX^XâRNVÌêQð1ñÌ
+ <literal>SELECT</literal> ųµÜ·B±êÍåL[âOL[ÌXgðwè·é±ÆÉ
+ Éæès¢Ü·B
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ HibernateÍɦ·íªÆàæÊðµÜ·B
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>¦tFb`</emphasis> - LÒÌIuWFNgª[h³ê½Æ«ÉA
+ ÖAARNVͦÉtFb`³êÜ·B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>xRNVtFb`</emphasis> - AvP[VªRNVÉ
+ εÄìðsÁ½Æ«ÉRNVðtFb`µÜ·B
+ (±êÍRNVÉηéftHgÌ®ìÅ·)
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>"ÁÊÈx"RNVtFb`</emphasis> - RNVÌvf
+ PÂPªƧµÄAKvÈÆ«Éf[^x[X©çæ¾³êÜ·B
+ HibernateÍKvÈ¢ÈçÎARNVSÌðÉtFb`·é±ÆÍ
+ ð¯Ü·iÆÄàå«ÈRNVÉKµÄ¢Ü·jB
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>vLVtFb`</emphasis> - PêlÖAÍA¯ÊqÌgetterÈOÌ
+ \bhªÖAIuWFNgÅÄÑo³êéÆ«ÉtFb`³êÜ·B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>"vLVȵ"tFb`</emphasis> - PêlÖAÍACX^XÏÉ
+ ANZX³ê½Æ«ÉtFb`³êÜ·BvLVtFb`Æär·éÆA±Ìû@Í
+ xÌx¢ªÈ¢iÖAͯÊqÉANZXµ½¾¯ÅàtFb`³êÜ·j
+ Å·ªAæè§ßIÅAAvP[VÉvLVª¶ÝµÈ¢æ¤É©¹Ü·B
+ ±Ìû@ÍrhÌoCgR[hgÝݪKvÉÈèAg¤êÊÍÜêÅ·B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>x®«tFb`</emphasis> - ®«âPêlÖAÍACX^XÏÉANZXµ½
+ Æ«ÉtFb`³êÜ·B±Ìû@ÍrhÌoCgR[hgÝݪKvÉÈèA
+ g¤êÊÍÜêÅ·B
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ñÂ̼s·éTOª èÜ·: <emphasis>¢Â</emphasis> ÖAðtFb`·é©A
+ »µÄA <emphasis>ǤâÁÄ</emphasis> tFb`·é©iÇñÈSQLðgÁÄjB
+ ±êç𬯵Ȣž³¢I <literal>fetch</literal> ÍptH[}X`
[jOÉg¢Ü·B
+ <literal>lazy</literal> Í éNX̪£³ê½CX^X̤¿AÇÌf[^ðíÉ
+ gpÂ\É·é©Ìæèßðè`µÜ·B
+ </para>
+
+ <sect2 id="performance-fetching-lazy">
+ <title>xÖAÌ«</title>
+
+ <para>
+ ftHgÅÍAHibernate3ÍRNVÉεÄÍxZNgtFb`ðg¢A
+ PêlÖAÉÍxvLVtFb`ðg¢Ü·B±êçÌftHg®ìÍÙÚ·×ÄÌ
+ AvP[VÌÙÚ·×ÄÌÖAÅÓ¡ª èÜ·B
+ </para>
+
+ <para>
+ <emphasis>:</emphasis>
+ <literal>hibernate.default_batch_fetch_size</literal> ðZbgµ½Æ«ÍAHibernateÍ
+ xtFb`̽ßÌob`tFb`ÅK»ðg¤Åµå¤
+ i±ÌÅK»Íæèש¢xÅLøÉ·é±ÆàoÜ·jB
+ </para>
+
+ <para>
+ µ©µAxtFb`ÍmÁĨ©È¯êÎÈçÈ¢êÂÌâèª èÜ·B
+ HibernateÌsessionðI[vµÄ¢éReLXgÌO©çxÖAÉANZX·éÆA
+ áOª¶µÜ·BáF
+ </para>
+
+ <programlisting><![CDATA[s = sessions.openSession();
+Transaction tx = s.beginTransaction();
+
+User u = (User) s.createQuery("from User u where u.name=:userName")
+ .setString("userName", userName).uniqueResult();
+Map permissions = u.getPermissions();
+
+tx.commit();
+s.close();
+
+Integer accessLevel = (Integer) permissions.get("accounts"); // Error!]]></programlisting>
+
+ <para>
+ <literal>Session</literal> ªN[Y³ê½Æ«ApermissionsRNVÍ
+ ú»³êĢȢ½ßA±ÌRNVÍ©gÌóÔð[hūܹñB
+ <emphasis>HibernateÍØ裳ê½IuWFNgÌxú»ÍT|[g
+ µÄ¢Ü¹ñ</emphasis> BC³û@ƵÄARNV©çÇÝÝðs¤R[hð
+ gUNVðR~bg·é¼OÉÚ®³¹Ü·B
+ </para>
+
+ <para>
+ êûÅA <literal>lazy="false"</literal> ðÖA}bsOÉwè·é±ÆÅA
+ xðµÈ¢RNVâÖAðg¤±ÆªoÜ·B
+ µ©µÈªçAxú»ÍÙÚ·×ÄÌRNVâÖAÅgíêé±Æð
+ Ó}µÄ¢Ü·BൠȽÌIuWFNgfÌÉxðµÈ¢ÖAð
+ ½è`µÄµÜ¤ÆAHibernateÍÅIIÉÍgUNVÉ
+ ÙÚ®SÈf[^x[XðÌÉtFb`·é±ÆÉÈéŵå¤I
+ </para>
+
+ <para>
+ ¼ûÅÍAÁèÌgUNVɨ¢ÄZNgtFb`ÌãíèÉ
+ tFb`iR±êÍxÅÍÈÈèÜ·jðIðµ½¢±Æª
+ X èÜ·B±ê©çtFb`íªðJX^}CY·éû@𨩹µÜ·B
+ Hibernate3ÅÍAtFb`íªðIð·édgÝÍPêlÖAÆRNVÅ
+ ÏíèÍ èܹñB
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-fetching-custom" revision="4">
+ <title>tFb`íªÌ`
[jO</title>
+
+ <para>
+ ZNgtFb`iftHgjÍN+1ZNgâèÆ¢¤å«Èã_ª é½ßA
+ }bsOè`ÅtFb`ðLøÉ·é±ÆªÅ«Ü·B
+ </para>
+
+ <programlisting><![CDATA[<set name="permissions"
+ fetch="join">
+ <key column="userId"/>
+ <one-to-many class="Permission"/>
+</set]]></programlisting>
+
+ <programlisting><![CDATA[<many-to-one name="mother" class="Cat" fetch="join"/>]]></programlisting>
+
+ <para>
+ }bsOè`Åè`µ½ <literal>tFb`</literal> íªÍÌàÌÉe¿µÜ·B
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>get()</literal> â <literal>load()</literal> Éæé³
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+
+ ÖAÉirQ[gµ½Æ«É¶·éÃÙIȳ
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>Criteria</literal> NG
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>TuZNg</literal> tFb`ðg¤HQLNG
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ½Æ¦ÇñÈtFb`íªðgÁ½ÆµÄàAxÅÍÈ¢OtÍÉÇÝÜêé±Æª
+ ÛسêÜ·BÂÜèAÁèÌHQLNGðÀs·é½ßɢ©ÌSELECT¶ª¦Às³êé
+ ±Æª éÌÅӵľ³¢B
+ </para>
+
+ <para>
+ ÊíÍA}bsOè`ÅtFb`ÌJX^}CYÍs¢Ü¹ñB
+ ãíèÉAftHgÌ®ìÌÜÜɵĨ¢ÄAHQLÅ <literal>left join fetch</literal> ð
+ wè·é±ÆÅÁèÌgUNVÅ®ìðI[o[ChµÜ·B
+ ±êÍHibernateÉñÌZNgÅOðgÁÄÖAðæÉtFb`·éæ¤ÉwèµÄ¢Ü·B
+ <literal>Criteria</literal> NGÌAPIÅÍA
+ <literal>setFetchMode(FetchMode.JOIN)</literal> ðg¤±ÆªoÜ·B
+ </para>
+
+ <para>
+ ൠ<literal>get()</literal> â <literal>load()</literal> Ågíêé
+ tFb`íªðϦ½¢Æ´¶½Æ«ÉÍAPÉ
+ <literal>Criteria</literal> NGðgÁľ³¢BáF
+ </para>
+
+ <programlisting><![CDATA[User user = (User) session.createCriteria(User.class)
+ .setFetchMode("permissions", FetchMode.JOIN)
+ .add( Restrictions.idEq(userId) )
+ .uniqueResult();]]></programlisting>
+
+ <para>
+ i±ê͢©ÌORM\
[Vª"fetch plan"ÆÄñÅ¢éàÌƯ¶Å·Bj
+ </para>
+
+ <para>
+ N+1ZNgâèðð¯é½ßÌÜÁ½á¤û@ÍAæ2xLbV
ðg¤±ÆÅ·B
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-fetching-proxies" revision="2">
+ <title>Pê[ÖAvLV</title>
+
+ <para>
+ RNVÌxtFb`ÍAHibernate©gÌÀÉæéi±RNVðgÁÄ
+ À»µÄ¢Ü·Bµ©µAPê[ÖAɨ¯éxÅÍAá¤dgݪ
+ KvÅ·BÎÛÌÖAGeBeBÍvLVÅȯêÎÈèܹñBHibernateÍ
+ i·Îçµ¢CGLIBCuÉæéjÀsÌoCgR[hg£ð
+ gÁÄi±IuWFNgÌxú»vLVðÀ»µÄ¢Ü·B
+ </para>
+
+ <para>
+ ftHgÅÍAHibernate3ÍiJnÉj·×ÄÌi±NXÌvLV𶬵A
+ »êçðgÁÄA <literal>many-to-one</literal> â <literal>one-to-one</literal> ÖAÌ
+ xtFb`ðÂ\ɵĢܷB
+ </para>
+
+ <para>
+ }bsOt@CÅ <literal>proxy</literal> ®«ÉæÁÄANXÌvLVC^[tFCXƵÄ
+ g¤C^[tFCXðé¾Å«Ü·BftHgÅÍAHibernateÍ»ÌNXÌTuNXðg¢Ü·B
+ <emphasis>vLVNXÍÈÆàpbP[WÂÅftHgRXgN^ðÀµÈ¯êÎ
+ ÈçÈ¢±ÆÉӵľ³¢B·×ÄÌi±NXɱÌRXgN^ð§µÜ·I</emphasis>
+ </para>
+
+ <para>
+ |[tBYÌNXÉεıÌû@ðKp·éƫɢ©l¶·é±Æª èÜ·B
+ áF
+ </para>
+
+ <programlisting><![CDATA[<class name="Cat" proxy="Cat">
+ ......
+ <subclass name="DomesticCat">
+ .....
+ </subclass>
+</class>]]></programlisting>
+
+ <para>
+ æêÉA <literal>Cat</literal> ÌCX^XÍ <literal>DomesticCat</literal>
+ ÉLXgūܹñB½Æ¦îÆÈéCX^Xª <literal>DomesticCat</literal>
+ Å Á½ÆµÄàÅ·B
+ </para>
+
+ <programlisting><![CDATA[Cat cat = (Cat) session.load(Cat.class, id); // instantiate a proxy (does not hit the db)
+if ( cat.isDomesticCat() ) { // hit the db to initialize the proxy
+ DomesticCat dc = (DomesticCat) cat; // Error!
+ ....
+}]]></programlisting>
+
+ <para>
+ æñÉAvLVÌ <literal>==</literal> ͬ§µÈ¢±Æª èÜ·B
+ </para>
+
+ <programlisting><![CDATA[Cat cat = (Cat) session.load(Cat.class, id); // instantiate a Cat proxy
+DomesticCat dc =
+ (DomesticCat) session.load(DomesticCat.class, id); // acquire new DomesticCat proxy!
+System.out.println(cat==dc); // false]]></programlisting>
+
+ <para>
+ µ©µA±êÍ©©¯ÙÇ«¢óµÆ¢¤í¯ÅÍ èܹñB½Æ¦ÙÈÁ½vLVIuWFNgÖÌ
+ ñÂÌQƪ Á½ÆµÄàAîÆÈéCX^Xͯ¶IuWFNgÅ·B
+ </para>
+
+ <programlisting><![CDATA[cat.setWeight(11.0); // hit the db to initialize the proxy
+System.out.println( dc.getWeight() ); // 11.0]]></programlisting>
+
+ <para>
+ æOÉA <literal>final</literal> NXâ <literal>final</literal> \bhðÂNXÉ
+ CGLIBvLVðg¦Ü¹ñB
+ </para>
+
+ <para>
+ ÅãÉAàµi±IuWFNgÌCX^X»(á¦ÎAú»âftHgRXgN^ÌÅ)
+ ÉÈñç©Ì\[XªKvÆÈéÈçA»Ì\[XàܽvLVðʵÄæ¾³êÜ·B
+ ÀÛÉÍAvLVNXÍi±NXÌTuNXÅ·B
+ </para>
+
+ <para>
+ ±êçÌâèÍJavaÌPêp³fÌ´ã̧À̽ßÅ·B൱êçÌâèð𯽢ÌÈçA
+ rWlX\bhðé¾µ½C^[tFCXð»ê¼êi±NXÅÀµÈ¯êÎÈèܹñB
+ }bsOt@CűêçÌC^[tFCXðwè·éKvª èÜ·BáF
+ </para>
+
+ <programlisting><![CDATA[<class name="CatImpl" proxy="Cat">
+ ......
+ <subclass name="DomesticCatImpl" proxy="DomesticCat">
+ .....
+ </subclass>
+</class>]]></programlisting>
+
+ <para>
+ <literal>CatImpl</literal> Í <literal>Cat</literal> C^[tFCXðÀ·éÌÉεA
+ <literal>DomesticCatImpl</literal> Í <literal>DomesticCat</literal> ðÀµÜ·B
+ ·éÆA <literal>load()</literal> â <literal>iterate()</literal> ÍA
+ <literal>Cat</literal> â <literal>DomesticCat</literal> ÌCX^XÌvLVð
+ ԵܷBi <literal>list()</literal> ÍÊíÍvLVðԳȢ±ÆÉӵľ³¢Bj
+ </para>
+
+ <programlisting><![CDATA[Cat cat = (Cat) session.load(CatImpl.class, catid);
+Iterator iter = session.iterate("from CatImpl as cat where cat.name='fritz'");
+Cat fritz = (Cat) iter.next();]]></programlisting>
+
+ <para>
+ ÖAàxú»³êÜ·B±êÍvpeBð <literal>Cat</literal> ^Å龵ȯêÎ
+ ÈçÈ¢±ÆðÓ¡µÜ·B <literal>CatImpl</literal> ÅÍ èܹñB
+ </para>
+
+ <para>
+ vLVÌú»ð <emphasis>KvƵȢ</emphasis> ìà¶ÝµÜ·B
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>equals()</literal> ii±NXª <literal>equals()</literal> ð
+ I[o[ChµÈ¢Æ«j
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>hashCode()</literal> ii±NXª <literal>hashCode()</literal> ð
+ I[o[ChµÈ¢Æ«j
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ¯ÊqÌgetter\bh
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ HibernateÍ <literal>equals()</literal> â <literal>hashCode()</literal> ðI[o[Chµ½
+ i±NXðoµÜ·B
+ </para>
+
+ <para>
+ ftHgÌ <literal>lazy="proxy"</literal> ÌãíèÉA <literal>lazy="no-proxy"</literal> ð
+ Iñ¾±ÆÅA^Ï·ÉÖA·éâèðñð·é±ÆªoÜ·B
+ µ©µArhÌoCgR[hgÝݪKvÉÈèAÇÌæ¤ÈìÅ ÁÄàA
+ ½¾¿ÉvLVÌú»ðs¤±ÆÉÈéŵå¤B
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-fetching-initialization" revision="1">
+ <title>RNVÆvLVÌú»</title>
+
+ <para>
+ <literal>LazyInitializationException</literal> ÍA <literal>Session</literal> ÌXR[vO©ç
+ ú»µÄ¢È¢RNVâvLVÉANZX³ê½Æ«ÉAHibernateÉæÁÄX[³êÜ·B
+ ·Èí¿ARNVâvLVÖÌQÆðÂGeBeBªª£³ê½óÔÌÅ·B
+ </para>
+
+ <para>
+ <literal>Session</literal> ðN[Y·éOÉvLVâRNVÌú»ðmÀÉ
+ s¢½¢Æ«ª èÜ·Bà¿ëñA <literal>cat.getSex()</literal>
+ â <literal>cat.getKittens().size()</literal> ÈÇðíÉÄÑo·±ÆÅú»ð§·é±ÆÍÅ«Ü·B
+ µ©µ±êÍR[hðÇÞl𬳹AÄpIÈR[hÆ¢¤_©çàsÖÅ·B
+ </para>
+
+ <para>
+ static\bhÌ <literal>Hibernate.initialize()</literal> â <literal>Hibernate.isInitialized()</literal>
+ Íxú»ÌRNVâvLVðµ¤Æ«ÉÖÈû@ðAvP[VÉñµÜ·B
+ <literal>Hibernate.initialize(cat)</literal> ÍA <literal>Session</literal> ªI[vµÄ¢éÀèÍ
+ <literal>cat</literal> vLVð§IÉú»µÜ·B
+ <literal>Hibernate.initialize( cat.getKittens() )</literal> ÍkittensRNVÉεįlÌ
+ øʪ èÜ·B
+ </para>
+
+ <para>
+ ÊÌIðƵÄAKvÈ·×ÄÌRNVâvLVª[h³êéÜÅ
+ <literal>Session</literal> ðI[vɵĨû@ª èÜ·B¢Â©ÌAvP[VÌ
+ A[LeN`ÅÍAÁÉHibernateÉæéf[^ANZXðs¤R[hÆA»êðg¤
+ R[hªÙÈéAvP[VÌC[âA¨IÉÙÈévZbTÌÆ«ÉÍA
+ RNVªú»³êéÆ«É <literal>Session</literal> ªI[vµÄ¢é±Æð
+ ÛØ·éâèª èÜ·B±ÌâèÉεÄÍ2ÂÌî{IÈû@ª èÜ·B
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+
+ Webx[XÌAvP[VÅÍA
+ r
[Ì_Oª®¹µANGXgªIíéêÔÅãÅ <literal>Session</literal>
+ ðN[Y·é½ßÉAT[ubgtB^ðg¤±ÆªÅ«Ü·i <emphasis>Open Session in View</emphasis>
+ p^[Å·jBà¿ëñAAvP[VîÕÌáO̳m«ªñíÉdvÉÈèÜ·B
+ r
[Ì_OÉáOª¶µ½Æ«Å³¦A[UɪßéOÉ
+ <literal>Session</literal> ÌN[YÆgUNVÌI¹ðs¤
+ ±ÆªsÂÉÈèÜ·B
+ HibernateÌWikiÉÚÁÄ¢é"Open Session in View"p^[ÌáðQƵľ³¢B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ rWlXwªª£µÄ¢éAvP[VÅÍArWlXWbNÍ
+ WebwÅKvÉÈé·×ÄÌRNVðOÉ"õ"·éKvª èÜ·B
+ ±êÍÁèÌ[XP[XÅKvÆÈév[e[V/WebwÉεA
+ rWlXwª·×ÄÌf[^ð[hµA·×ÄÌf[^ðú»µÄԷ׫Æ
+ ¢¤±ÆðÓ¡µÄ¢Ü·BÊíÍAAvP[VÍWebwÅKvÈRNV
+ »ê¼êÉ뵀 <literal>Hibernate.initialize()</literal> ðÄÑo·©
+ i±ÌÄÑoµÍZbVðN[Y·éOÉs¤Kvª èÜ·jA
+ HibernateNGÌ <literal>FETCH</literal> ßâ <literal>Criteria</literal>
+ Ì <literal>FetchMode.JOIN</literal> ðgÁÄRNVðæɳµÜ·B
+ ÊÍ <emphasis>Session Facade</emphasis> p^[ÌãíèÉ
+ <emphasis>Command</emphasis> p^[ðÌp·éÙ¤ªæèÈPÅ·B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ú»³êĢȢRNViàµÍ¼ÌvLVjÉANZX·éOÉA
+ <literal>merge()</literal> â <literal>lock()</literal> ðgÁÄVµ¢
+ <literal>Session</literal> ÉÈOÉ[h³ê½IuWFNgðÇÁ·é±ÆàoÜ·B
+ AhzbNÈgUNVÌZ}eBNXð±üµ½ÌÅAHibernateÍ
+ ±êð©®IÉsí¸A <emphasis>s¤×«Åà èܹñ</emphasis> I
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ å«ÈRNVðú»µ½ÍÈ¢ªARNVÉ¢ÄÌÈñç©ÌîñiTCYÌæ¤Èj
+ âf[^ÌTuZbgðKvÆ·é±Æª èÜ·B
+ </para>
+
+ <para>
+ RNVtB^ðg¤±ÆÅAú»¹¸ÉRNVÌTCYðæ¾·é±ÆªoÜ·B
+ </para>
+
+ <programlisting><![CDATA[( (Integer) s.createFilter( collection, "select count(*)" ).list().get(0) ).intValue()]]></programlisting>
+
+ <para>
+ <literal>createFilter()</literal> \bhÍARNVSÌðú»·éKvȵÉARNVÌ
+ TuZbgð³·é½ßÉøÊIÉg¦Ü·B
+ </para>
+
+ <programlisting><![CDATA[s.createFilter( lazyCollection, "").setFirstResult(0).setMaxResults(10).list();]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="performance-fetching-batch">
+ <title>ob`tFb`Ìgp</title>
+
+ <para>
+ HibernateÍob`tFb`ðø¦IÉgpÅ«Ü·BêÂÌvLViàµÍRNVjªANZX
+ ³êéÆAHibernate͢©Ìú»µÄ¢È¢vLVð[h·é±ÆªÅ«Ü·Bob`tFb`Í
+ xZNgtFb`íªÉηéÅK»Å·Bob`tFb`̲®ÉÍQÂÌû@ª èÜ·B
+ NXxÆRNVxÅ·B
+ </para>
+
+ <para>
+ NXAvfÌob`tFb`ÍðªÈPÅ·BÀsÌÌêÊðzµÄ¾³¢B
+ <literal>Session</literal> É[h³ê½25ÂÌ <literal>Cat</literal> CX^Xª¶ÝµA
+ »ê¼êÌ <literal>Cat</literal> Í <literal>owner</literal> Å é <literal>Person</literal> ÖÌÖAð¿Ü·B
+ <literal>Person</literal> NXÍ <literal>lazy="true"</literal> ÌvLVÅ}bsO³êĢܷB
+ ൡ·×ÄÌCatÉεÄJèÔµ <literal>getOwner()</literal> ðÄÑo·ÆAHibernateÍ
+ ftHgÅÍ25ñÌ <literal>SELECT</literal> ðÀsµAownervLV̳ðµÜ·B
+ ±ÌUé¢ð <literal>Person</literal> Ì}bsOÌ <literal>batch-size</literal>
+ ÌwèŲ®Å«Ü·B
+ </para>
+
+ <programlisting><![CDATA[<class name="Person" batch-size="10">...</class>]]></programlisting>
+
+ <para>
+ HibernateÍNGðRñ¾¯ðÀs·éæ¤ÉÈèÜ·Bp^[Í10, 10, 5Å·B
+ </para>
+
+ <para>
+ RNVÌob`tFb`àLøÉ·é±ÆªoÜ·BáƵÄA»ê¼êÌ
+ <literal>Person</literal> ª <literal>Cat</literal> ÌxRNVðÁĨèA
+ 10ÂÌPersonª <literal>Sesssion</literal> É[h³ê½Æ·éÆA·×ÄÌPersonÉ
+ εÄJèÔµ <literal>getCats()</literal> ðÄÑo·±ÆÅAv10ñÌ <literal>SELECT</literal>
+ ª¶µÜ·Bൠ<literal>Person</literal> Ì}bsOÅ <literal>cats</literal>
+ RNVÌob`tFb`ðLøÉ·êÎAHibernateÍRNVÌOtFb`ªoÜ·B
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <set name="cats" batch-size="3">
+ ...
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ <literal>batch-size</literal> ª3ÈÌÅAHibernateÍ4ñÌ <literal>SELECT</literal>
+ Å3ÂA3ÂA3ÂA1Âð[hµÜ·BJèÔ·ÆA®«ÌlÍÁèÌ <literal>Session</literal>
+ ÌÌú»³êĢȢRNVÌúÒÉ˶µÜ·B
+ </para>
+
+ <para>
+ RNVÌob`tFb`ÍACeÌlXgµ½c[A ·Èí¿Aã\IÈi\Ìp^[ª
+ éêÉÁÉLpÅ·Biµ©µAÇÝݪ½¢c[ÅÍ <emphasis>lXgµ½set</emphasis>
+ â <emphasis>ïÌ»µ½pX</emphasis> ªæèæ¢IðÉÈèÜ·Bj
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-fetching-subselect">
+ <title>TuZNgtFb`Ìgp</title>
+
+ <para>
+ êÂÌxRNVâPêlvLVªtFb`³êȯê΢¯È¢Æ«AHibernateÍ
+ »êç·×Äð[hµATuZNgÌIWiNGªÄxÀs³êÜ·B±êÍ
+ ob`tFb`Ư¶û@Å®«Aµ¸ÂÌ[hÍs¢Ü¹ñB
+ </para>
+
+ <!-- TODO: Write more about this -->
+ <!-- ¯æ -->
+ </sect2>
+
+ <sect2 id="performance-fetching-lazyproperties">
+ <title>xvpeBtFb`Ìgp</title>
+
+ <para>
+ Hibernate3ÍvpeB²ÆÌxtFb`ðT|[gµÄ¢Ü·B±ÌÅK»è@Í
+ <emphasis>O[vÌtFb`</emphasis> ƵÄàmçêĢܷB±êÍÙÆñÇ
+ v]©ço½@\Å é±ÆÉӵľ³¢BÀÛÉÍñÇÝÝÌÅK»æèàA
+ sÇÝÝÌÅK»ªñíÉdvÅ·B
+ µ©µANX̢©ÌvpeB¾¯ðÇÝÞ±ÆÍAù¶Ìe[uª½SàÌñð¿A
+ f[^fðüPÅ«È¢ÈÇÌÉ[ÈêÉÍLpÅ·B
+ </para>
+
+ <para>
+ xvpeBÇÝÝðLøÉ·éÉÍAÎÛÌvpeBÌ}bsOÅ <literal>lazy</literal>
+ ®«ðZbgµÄ¾³¢B
+ </para>
+
+ <programlisting><![CDATA[<class name="Document">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="name" not-null="true" length="50"/>
+ <property name="summary" not-null="true" length="200" lazy="true"/>
+ <property name="text" not-null="true" length="2000" lazy="true"/>
+</class>]]></programlisting>
+
+ <para>
+ xvpeBÇÝÝÍrhÌoCgR[hgÝÝðKvƵܷIàµ
+ i±NXÉgÝݪ³êĢȢÈçAHibernateÍÙÁÄxvpeBÌÝèð³µÄA
+ ¦tFb`ÉߵܷB
+ </para>
+
+ <para>
+ oCgR[hgÝÝÍȺÌAnt^XNðgÁľ³¢B
+ </para>
+
+ <programlisting><![CDATA[<target name="instrument" depends="compile">
+ <taskdef name="instrument" classname="org.hibernate.tool.instrument.InstrumentTask">
+ <classpath path="${jar.path}"/>
+ <classpath path="${classes.dir}"/>
+ <classpath refid="lib.class.path"/>
+ </taskdef>
+
+ <instrument verbose="true">
+ <fileset dir="${testclasses.dir}/org/hibernate/auction/model">
+ <include name="*.class"/>
+ </fileset>
+ </instrument>
+</target>]]></programlisting>
+
+ <para>
+ svÈñðÇÝÜÈ¢½ßÌAÊÌiæèæ¢Hjû@ÍAÈÆà
+ ÇÝÝÌÝÌgUNVɨ¢ÄÍAHQLâCriteriaNGÌËe
+ @\ðg¤±ÆÅ·B±Ìû@ÍrhÌoCgR[hgÝݪsvÉÈèA
+ æèÇ¢ðû@Å·B
+ </para>
+
+ <para>
+ HQLÅ <literal>fetch all properties</literal> ðg¤±ÆÅAÊǨèÌ
+ vpeB̦tFb`Oð§·é±ÆªoÜ·B
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="performance-cache" revision="1">
+ <title>æ2xLbV
</title>
+
+ <para>
+ HibernateÌ <literal>Session</literal> Íi±f[^ÌgUNVxÌLbV
Å·B
+ class-by-classÆcollection-by-collection²ÆÌANX^xâJVMx
+ i <literal>SessionFactory</literal> xjÌLbV
ðÝè·é±ÆªoÜ·B
+ NX^»³ê½LbV
ÉÂÈ®±Æ³¦oÜ·Bµ©µÓµÄ¾³¢B
+ LbV
ͼÌAvP[VÉæéi±wÌÏXð
+ l¶µÜ¹ñiLbV
f[^ðèúIÉúÀØêÉ·éÝèÍoÜ·jB
+ </para>
+
+ <para revision="1">
+ Hibernateªgp·éLbV
ÀÍA<literal>hibernate.cache.provider_class</literal> vpeBÉ
+ <literal>org.hibernate.cache.CacheProvider</literal> ðÀµ½NX¼ðwè·é±ÆÅÏXÅ«Ü·B
+ HibernateͽÌI[v\[XÌLbV
voC_ðrgCÀÅÁĢܷiãÉXgª èÜ·jB
+ Á¦ÄAOÉྵ½æ¤ÉA Ƚ©gªÆ©ÌÀðµÄA»êðgÝÞ±ÆàoÜ·B
+ o[W3.2æèOÅÍEhCacheªftHgÌLbV
voC_Å é±ÆÉӵľ³¢B
+ o[W3.2ÅͱêÍÄÍÜèܹñB
+ </para>
+
+ <table frame="topbot" id="cacheproviders" revision="1">
+ <title>LbV
voC_</title>
+ <tgroup cols='5' align='left' colsep='1' rowsep='1'>
+ <colspec colname='c1' colwidth="1*"/>
+ <colspec colname='c2' colwidth="3*"/>
+ <colspec colname='c3' colwidth="1*"/>
+ <colspec colname='c4' colwidth="1*"/>
+ <colspec colname='c5' colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>LbV
</entry>
+ <entry>voC_NX</entry>
+ <entry>^Cv</entry>
+ <entry>NX^Z[t</entry>
+ <entry>NGLbV
ÌT|[g</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>Hashtablei»ipƵÄÓ}µÄ¢Ü¹ñj</entry>
+ <entry><literal>org.hibernate.cache.HashtableCacheProvider</literal></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry>yes</entry>
+ </row>
+ <row>
+ <entry>EHCache</entry>
+ <entry><literal>org.hibernate.cache.EhCacheProvider</literal></entry>
+ <entry>AfBXN</entry>
+ <entry></entry>
+ <entry>yes</entry>
+ </row>
+ <row>
+ <entry>OSCache</entry>
+ <entry><literal>org.hibernate.cache.OSCacheProvider</literal></entry>
+ <entry>AfBXN</entry>
+ <entry></entry>
+ <entry>yes</entry>
+ </row>
+ <row>
+ <entry>SwarmCache</entry>
+ <entry><literal>org.hibernate.cache.SwarmCacheProvider</literal></entry>
+ <entry>NX^iip}`LXgj</entry>
+ <entry>yesiNX^³ø»j</entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>JBoss TreeCache</entry>
+ <entry><literal>org.hibernate.cache.TreeCacheProvider</literal></entry>
+ <entry>NX^iip}`LXgjAgUNVi</entry>
+ <entry>yesi¡»j</entry>
+ <entry>yesi¯úªKvj</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <sect2 id="performance-cache-mapping" revision="2">
+ <title>LbV
Ì}bsO</title>
+
+ <para>
+ NXâRNVÌ}bsOÌ <literal><cache></literal> vfÍȺÌ`®Å·B
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="cache1" coords="2 70"/>
+ <area id="cache2" coords="3 70"/>
+ <area id="cache3" coords="4 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<cache
+ usage="transactional|read-write|nonstrict-read-write|read-only"
+ region="RegionName"
+ include="all|non-lazy"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="cache1">
+ <para>
+ <literal>usage</literal> (required) specifies the caching strategy:
+ <literal>transactional</literal>,
+ <literal>read-write</literal>,
+ <literal>nonstrict-read-write</literal> or
+ <literal>read-only</literal>
+ </para>
+ </callout>
+ <callout arearefs="cache2">
+ <para>
+ <literal>region</literal> (optional, defaults to the class or
+ collection role name) specifies the name of the second level cache
+ region
+ </para>
+ </callout>
+ <callout arearefs="cache3">
+ <para>
+ <literal>include</literal> (optional, defaults to <literal>all</literal>)
+ <literal>non-lazy</literal> specifies that properties of the entity mapped
+ with <literal>lazy="true"</literal> may not be cached when attribute-level
+ lazy fetching is enabled
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ ܽÍiæèæ¢û@ƵÄHjA <literal>hibernate.cfg.xml</literal> É <literal><class-cache></literal>
+ Æ <literal><collection-cache></literal> vfðwè·é±ÆàoÜ·B
+ </para>
+
+ <para>
+ <literal>usage</literal> ®«Í <emphasis>LbV
ÌÀñ«íª</emphasis> ðwèµÜ·B
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-cache-readonly">
+ <title>read onlyíª</title>
+
+ <para>
+ àµAvP[VªÇÝÝÌÝKvÅAi±NXÌCX^XðÏXµÈ¢ÈçA
+ <literal>read-only</literal> LbV
ðg¤±ÆªoÜ·B±êÍàÁÆàPÅ
+ àÁÆàptH[}XÌÇ¢íªÅ·BNX^ÅÌgpà®SÉÀSÅ·B
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Immutable" mutable="false">
+ <cache usage="read-only"/>
+ ....
+</class>]]></programlisting>
+
+ </sect2>
+
+
+ <sect2 id="performance-cache-readwrite">
+ <title>read/writeíª</title>
+
+ <para>
+ AvP[Vªf[^ðXV·éKvª éÈçA <literal>read-write</literal> LbV
ªK©à
+ µêܹñB±ÌLbV
íªÍAVACUuÈgUNVª£xªv³êéÈçA
+ µÄg¤×«ÅÍ èܹñBàµLbV
ªJTA«ÅgíêéÈçAJTA <literal>TransactionManager</literal>
+ ðæ¾·é½ßÌû@𦷠<literal>hibernate.transaction.manager_lookup_class</literal>
+ vpeBðwèµÈ¯êÎÈèܹñB¼Ì«ÅÍA <literal>Session.close()</literal>
+ â <literal>Session.disconnect()</literal> ªÄÎê½Æ«ÉAmÀÉgUNVª®¹
+ µÄ¢È¯êÎÈèܹñB
+ àµNX^űÌíªðg¢½¢ÈçAîÆÈéLbV
ÌÀªbNðT|[g
+ µÄ¢é±ÆðÛصȯêÎÈèܹñB
+ gÝÝÌLbV
voC_Í <emphasis>T|[gµÄ¢Ü¹ñ</emphasis> B
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Cat" .... >
+ <cache usage="read-write"/>
+ ....
+ <set name="kittens" ... >
+ <cache usage="read-write"/>
+ ....
+ </set>
+</class>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="performance-cache-nonstrict">
+ <title>µ§ÅÍÈ¢read/writeíª</title>
+
+ <para>
+ AvP[Vª½Üɵ©f[^ðXV·éKvÍÈi·Èí¿ñÂÌgUNVª
+ ¯É¯¶ACeðXVµæ¤Æ·é±ÆÍÙÆñÇN±çÈ¢jAµ§ÈgUNVª£ª
+ v³êÈ¢ÈçA <literal>nonstrict-read-write</literal> LbV
ªK©àµêܹñB
+ àµLbV
ªJTA«ÅgíêéÈçA <literal>hibernate.transaction.manager_lookup_class</literal>
+ ðwèµÈ¯êÎÈèܹñB¼Ì«ÅÍA <literal>Session.close()</literal>
+ â <literal>Session.disconnect()</literal> ªÄÎê½Æ«ÉAmÀÉgUNVª®¹
+ µÄ¢È¯êÎÈèܹñB
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-cache-transactional">
+ <title>transactionalíª</title>
+
+ <para>
+ <literal>transactional</literal> LbV
íªÍJBoss TreeCacheÌæ¤È®SÈgUNVi
+ LbV
voC_ÌT|[gðñµÜ·B
+ ±Ìæ¤ÈLbV
ÍJTA«ÅÌÝgpÂ\ÅA <literal>hibernate.transaction.manager_lookup_class</literal>
+ ðwèµÈ¯êÎÈèܹñB
+ </para>
+
+ </sect2>
+
+ <para>
+ ·×Ä̯Às«LbV
íªðT|[gµÄ¢éLbV
voC_Í èܹñB
+ ȺÌ\ÍÇÌvoC_ªÇ̯Àñ«íªÉηé©ð\µÄ¢Ü·B
+ </para>
+
+ <table frame="topbot">
+ <title>¯Às«LbV
íªÌT|[g</title>
+ <tgroup cols='5' align='left' colsep='1' rowsep='1'>
+ <colspec colname='c1' colwidth="1*"/>
+ <colspec colname='c2' colwidth="1*"/>
+ <colspec colname='c3' colwidth="1*"/>
+ <colspec colname='c4' colwidth="1*"/>
+ <colspec colname='c5' colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>LbV
</entry>
+ <entry>read-only</entry>
+ <entry>µ§ÅÍÈ¢read-write</entry>
+ <entry>read-write</entry>
+ <entry>transactional</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>Hashtablei»ipƵÄÓ}µÄ¢Ü¹ñj</entry>
+ <entry>yes</entry>
+ <entry>yes</entry>
+ <entry>yes</entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>EHCache</entry>
+ <entry>yes</entry>
+ <entry>yes</entry>
+ <entry>yes</entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>OSCache</entry>
+ <entry>yes</entry>
+ <entry>yes</entry>
+ <entry>yes</entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>SwarmCache</entry>
+ <entry>yes</entry>
+ <entry>yes</entry>
+ <entry></entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>JBoss TreeCache</entry>
+ <entry>yes</entry>
+ <entry></entry>
+ <entry></entry>
+ <entry>yes</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect1>
+
+ <sect1 id="performance-sessioncache" revision="2">
+ <title>LbV
ÌÇ</title>
+
+ <para>
+ IuWFNgð <literal>save()</literal> A <literal>update()</literal> A <literal>saveOrUpdate()</literal>
+ Én·Æ«A»µÄ <literal>load()</literal> A <literal>get()</literal> A <literal>list()</literal> A
+ <literal>iterate()</literal> A <literal>scroll()</literal> ðgÁÄIuWFNgð³·éÆ«ÉÍíÉA
+ »ÌIuWFNgÍ <literal>Session</literal> ÌàLbV
ÉÇÁ³êÜ·B
+ </para>
+ <para>
+ É <literal>flush()</literal> ªÄÎêéÆAIuWFNgÌóÔÍf[^x[XƯú»³êÜ·B
+ ൱̯úªN±é±Æð]ÜȢƫâAcåÈÌIuWFNgðµÄ¢Äðø¦IÉ
+ µ¤Kvª éÆ«ÍA <literal>evict()</literal> \bhðgÁÄêLbV
©ç
+ IuWFNgâRNVðí·é±ÆªoÜ·B
+ </para>
+
+ <programlisting><![CDATA[ScrollableResult cats = sess.createQuery("from Cat as cat").scroll(); //a huge result set
+while ( cats.next() ) {
+ Cat cat = (Cat) cats.get(0);
+ doSomethingWithACat(cat);
+ sess.evict(cat);
+}]]></programlisting>
+
+ <para>
+ <literal>Session</literal> ÍCX^XªZbVLbV
ÉÜÜêé©Ç¤©ð»f·é½ßÌ
+ <literal>contains()</literal> \bhàñµÜ·B
+ </para>
+
+ <para>
+ ·×ÄÌIuWFNgðZbVLbV
©ç®SÉæèÉÍA<literal>Session.clear()</literal>
+ ðÄÑoµÄ¾³¢B
+ </para>
+
+ <para>
+ ñLbV
̽ßÉA <literal>SessionFactory</literal> ÉÍ
+ CX^XANXSÌARNVÌCX^XARNVSÌðLbV
©ç
+ í·é½ßÌ\bhª»ê¼êè`³êĢܷB
+ </para>
+
+ <programlisting><![CDATA[sessionFactory.evict(Cat.class, catId); //evict a particular Cat
+sessionFactory.evict(Cat.class); //evict all Cats
+sessionFactory.evictCollection("Cat.kittens", catId); //evict a particular collection of kittens
+sessionFactory.evictCollection("Cat.kittens"); //evict all kitten collections]]></programlisting>
+
+ <para>
+ <literal>CacheMode</literal> ÍÁèÌZbVªñLbV
ÆÇÌæ¤ÉÝìp·é©ð
+ wèµÜ·B
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>CacheMode.NORMAL</literal> - ACeÌÇÝÝÆ«ÝÅñLbV
ðg¢Ü·
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>CacheMode.GET</literal> - ÇÝÝÍñLbV
©çs¢Ü·ªAf[^ð
+ XVµ½êð¢ÄñLbV
É«ÝðµÜ¹ñB
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>CacheMode.PUT</literal> - ñLbV
ÉACeð«ÝÜ·ªAÇÝÝÉÍ
+ ñLbV
ðg¢Ü¹ñB
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>CacheMode.REFRESH</literal> - ñLbV
ÉACeð«ÝÜ·ªAÇÝÝÉÍ
+ ñLbV
ðgí¸A <literal>hibernate.cache.use_minimal_puts</literal>
+ Ìe¿ðó¯¸ÉAf[^x[X©çÇÝÞ·×ÄÌACeÌñLbV
ð§IÉtbV
µÜ·B
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ñLbV
ÌàeâNGLbV
Ìæð©é½ßÉA <literal>Statistics</literal> APIð
+ gÁľ³¢B
+ </para>
+
+ <programlisting><![CDATA[Map cacheEntries = sessionFactory.getStatistics()
+ .getSecondLevelCacheStatistics(regionName)
+ .getEntries();]]></programlisting>
+
+ <para>
+ vîñðLøɵÄA³çÉIvVƵÄALbV
Ggð
+ lªæèðÂ\È`®ÅÛ·é±ÆðHibernateɧµÜ·B
+ </para>
+
+ <programlisting><![CDATA[hibernate.generate_statistics true
+hibernate.cache.use_structured_entries true]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="performance-querycache" revision="1">
+ <title>NGLbV
</title>
+
+ <para>
+ NGÌUgZbgàLbV
oÜ·B±êͯ¶p[^ŽxàÀs³êé
+ NGÉεÄÌÝLpÅ·BNGLbV
ðg¤ÉÍAܸÝèÅLøɵÈÄÍÈèܹñB
+ </para>
+
+ <programlisting><![CDATA[hibernate.cache.use_query_cache true]]></programlisting>
+
+ <para>
+ ±ÌÝèÍV½ÉñÂÌLbV
ÌæÌì¬ðs¢Ü·BêÂÍNGÌUgZbgÌ
+ LbV
i <literal>org.hibernate.cache.StandardQueryCache</literal> jðÛµA
+ à¤1ÂÍNGÂ\Èe[uÖÌÅVÌXV^CX^v
+ i <literal>org.hibernate.cache.UpdateTimestampsCache</literal> jð۵ܷB
+ NGLbV
ÍUgZbgÌÀÛÌvfÌóÔÍLbV
µÈ¢±ÆÉ
+ ӵľ³¢BLbV
·éÌͯÊqÌlÆAl^ÌÊÌÝÅ·B
+ »Ì½ßANGLbV
ÍíÉñLbV
ÆêÉg¤×«Å·B
+ </para>
+
+ <para>
+ ÙÆñÇÌNGÍLbV
̶bðó¯È¢ÌÅAftHgÅÍNGÍLbV
³êܹñB
+ LbV
ðLøÉ·éÉÍA <literal>Query.setCacheable(true)</literal> ðÄÑoµÄ¾³¢B
+ »¤·êÎNGªù¶ÌLbV
ÊðTµANGÀsÉ»ÌÊðLbV
ÉÇÁ·é
+ æ¤ÉÈèÜ·B
+ </para>
+
+ <para>
+ NGLbV
Ìjü|V[ðש§äµ½¢Æ«ÍA <literal>Query.setCacheRegion()</literal>
+ ðÄÑoµÄÁèÌNGÉηéLbV
Ìæðwè·é±ÆªoÜ·B
+ </para>
+
+ <programlisting><![CDATA[List blogs = sess.createQuery("from Blog blog where blog.blogger = :blogger")
+ .setEntity("blogger", blogger)
+ .setMaxResults(15)
+ .setCacheable(true)
+ .setCacheRegion("frontpages")
+ .list();]]></programlisting>
+
+ <para>
+ NGª©gÌNGLbV
ÌæÌtbV
ð§µÈ¯êÎÈçÈ¢ÈçA
+ <literal>Query.setCacheMode(CacheMode.REFRESH)</literal> ðÄÑo·×«Å·B±êÍ
+ ³ÆÈéf[^ªÊÌvZXÉæÁÄXV³ê½èi·Èí¿HibernateðʶÄXV³êÈ¢jA
+ AvP[VÉÁèÌNGUgZbgðIðµÄtbV
³¹éêÉÁÉLpÅ·B
+ ³çÉLpÈà¤êÂÌû@ÍA <literal>SessionFactory.evictQueries()</literal>
+ ÉæÁÄNGLbV
ÌæðÁ·é±ÆÅ·B
+ </para>
+
+ </sect1>
+14.1
+ <sect1 id="performance-collections">
+ <title>RNVÌptH[}XÌð</title>
+
+ <para>
+ RNVÌbèÉÍ·ÅɽÌÔðg¢Üµ½B±ÌßÅÍ
+ RNVªÀsÉÇÌæ¤ÉU¤©É¢ÄÌbèð2A3æèã°Ü·B
+ </para>
+
+ <sect2 id="performance-collections-taxonomy">
+ <title>ªÞ</title>
+
+ <para>HibernateÍ3ÂÌî{IÈRNVÌíÞðè`µÄ¢Ü·B
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>lÌRNV</para>
+ </listitem>
+ <listitem>
+ <para>êνÖA</para>
+ </listitem>
+ <listitem>
+ <para>½Î½ÖA</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ±ÌªÞͳܴÜÈe[uâOL[ÖAðæʵܷªA½¿ªméKvÌ é
+ ÖAfÉ¢ÄÙÆñÇÈÉ೦ÄêܹñBÖA\¢âptH[}XÌÁ¥ð
+ ®SÉð·éÉÍAHibernateªRNVÌsðXVAí·é½ßÉg¤åL[Ì
+ \¢àܽl¦È¯êÎÈèܹñB±êÍȺ̪Þðñ¦µÜ·B
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>CfbNXt«RNV</para>
+ </listitem>
+ <listitem>
+ <para>set</para>
+ </listitem>
+ <listitem>
+ <para>bag</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ·×ÄÌCfbNXt«RNVimapAlistAzñjÍ <literal><key></literal>
+ Æ <literal><index></literal> J©çÈéåL[ðÁĢܷB±ÌêÍ
+ RNVÌXVÍñíÉø¦IÅ·BåL[ÍLpÈCfbNXÉÈèAHibernateª
+ ÁèÌsðXVܽÍí·éÆ«ÉA»Ìsðø¦Iɩ¯é±ÆªÅ«Ü·B
+ </para>
+
+ <para>
+ setÍ <literal><key></literal> ©çÈéåL[ÆvfÌJðÁĢܷB
+ ±êÍRNVvf̢©Ì^É¢ÄÍø¦IÅÍÈ¢©àµêܹñB
+ ÁÉ¡vfAå«ÈeLXgAoCitB[hÅÍñø¦Å·Bf[^x[XÍ
+ ¡åL[Éø¦IÉCfbNXðt¯é±ÆªÅ«È¢©çÅ·BêûA1νâ½Î½ÖAɨ¢ÄA
+ ÁÉlH¯ÊqÌêͯ¶®ç¢ø¦IÅ·Bi]kF <literal>SchemaExport</literal>
+ ÅÀÛÉ <literal><set></literal> ÌåL[ðìè½¢ÈçA·×ÄÌJÅ
+ <literal>not-null="true"</literal> ð龵ȯêÎÈèܹñBj
+ </para>
+
+ <para>
+ <literal><idbag></literal> }bsOÍãL[ðè`µÜ·B»Ì½ß
+ XVÍíÉñíÉø¦IÅ·BÀãA±êÍÅPÌP[XÅ·B
+ </para>
+
+ <para>
+ bagÍÅ«ÌP[XÅ·BbagÍvfÌlÌd¡ªÂ\ÅACfbNXJð½È¢½ßA
+ åL[Íè`³êÈ¢©àµêܹñBHibernateÉÍd¡µ½sðæÊ·éû@ª èܹñB
+ HibernateͱÌâèÌð̽ßÉAÏXª Á½Æ«ÉÍíÉ®SÈí
+ iêÂÌ <literal>DELETE</literal> Éæéjðs¢ARNVÌÄì¬ðs¢Ü·B
+ ±êÍñíÉñø¦I©àµêܹñB
+ </para>
+
+ <para>
+ 1νÖAÅÍAuåL[vÍf[^x[XÌe[ųIÈ
+ åL[ÅÍÈ¢©àµêÈ¢±ÆÉӵľ³¢Bµ©µ±Ìêų¦AãL̪ÞÍܾLpÅ·B
+ iHibernateªRNVÌÂXÌsðǤâÁÄu©Â¯é©vð\µÄ¢Ü·Bj
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-collections-mostefficientupdate">
+ <title>XVÉàÁÆàø¦IÈRNV listAmapAidbagAset</title>
+
+ <para>
+ ãÅÌc_©çACfbNXt«RNVÆiÊÌjsetÍvfÌÇÁAíA
+ XVÅàÁÆàø¦IÈìªoé±Æ;ç©Å·B
+ </para>
+
+ <para>
+ ÙÚÔá¢ÈA½Î½ÖAâlÌRNVɨ¢ÄACfbNXt«RNVª
+ setæèàDêÄ¢é_ªêÂÈã èÜ·B <literal>Set</literal> Í»Ì
+ \¢Ì½ßÉAHibernateÍvfªuÏXv³ê½Æ«ÉsðµÄ <literal>UPDATE</literal>
+ µÜ¹ñB <literal>Set</literal> ÖÌÏXÍíÉiÂXÌsÌj<literal>INSERT</literal>
+ Æ <literal>DELETE</literal> ÉæÁÄs¢Ü·BJèԵܷªA±êÍêνÖAÉÍ
+ ÄÍÜèܹñB
+ </para>
+
+ <para>
+ zñÍxªÅ«È¢Æ¢¤ÜèÈÌÅA_ƵÄAlistAmapAidbagªàÁÆà
+ ptH[}XÌÇ¢iinverseÅÍÈ¢jRNV^CvÆÈèÜ·Bsetà
+ »êÙÇá¢Í èܹñBHibernateÌAvP[VÅÍAsetÍRNVÌàÁÆà
+ ¤ÊÌíÞƵÄúÒ³êÜ·BsetÌ\»ÍÖAfÅÍàÁÆà©R¾©çÅ·B
+ </para>
+
+ <para>
+ µ©µAæfUC³ê½HibernateÌhCfÅÍAÊíàÁÆརRNVÍ
+ Àã <literal>inverse="true"</literal> ðwèµ½1νÖAÅ·B±êçÌÖAÅÍA
+ XVͽÎêÌÖA[ŵíêARNVÌXVptH[}XÌâèÍÄÍÜèܹñB
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-collections-mostefficentinverse">
+ <title>inverseRNVÉàÁÆàÅKÈbagÆlist</title>
+
+ <para>
+ bagð©úµÄµÜ¤OÉAbagi»µÄlistàjªsetæèà¸ÁÆptH[}XªÇ¢ÁÊÈP[Xð
+ ÐîµÜ·B <literal>inverse="true"</literal> ÌRNViêÊIÈ1νÖAÌg¢ûÈÇjÅA
+ bagÌvfðú»itFb`j·éKvÈbagâlistÉvfðÇÁÅ«Ü·I
+ ±êÍ <literal>Collection.add()</literal> â <literal>Collection.addAll()</literal>
+ Íbagâ <literal>List</literal> ÅÍíÉtrueðԳȯêÎÈçÈ¢©çÅ·
+ i <literal>Set</literal> ÆÍÙÈèÜ·jB
+ ±êÍȺ̤Êðæ謷é±ÆªÅ«Ü·B
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) sess.load(Parent.class, id);
+Child c = new Child();
+c.setParent(p);
+p.getChildren().add(c); //no need to fetch the collection!
+sess.flush();]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="performance-collections-oneshotdelete">
+ <title>êí</title>
+
+ <para>
+ XARNVÌvfðêÂêÂí·é±ÆÍÉßÄñø¦IÉÈé±Æª èÜ·B
+ HibernateÍð©ÅÍÈ¢ÌÅAVµ¢óÌRNVÌêi <literal>list.clear()</literal>
+ ðÄÑoµ½êÈÇjÅͱêð·×«ÅÈ¢±ÆðmÁĢܷB±ÌêÍAHibernateÍ
+ <literal>DELETE</literal> ðêñsµÄA»êÅ·×ÄIíèÜ·I
+ </para>
+
+ <para>
+ TCY20ÌRNVÉêÂÌvfðÇÁµA»ê©çñÂÌvfðí·éƵܷB
+ HibernateÍêÂÌ <literal>INSERT</literal> ¶ÆñÂÌ <literal>DELETE</literal> ¶ðsµÜ·
+ iRNVªbagÅȯêÎjB±êÍm©É]ܵ¢®ìÅ·B
+ </para>
+
+ <para>
+ µ©µA18ÂÌvfðíµÄ2ÂðcµA»ê©ç3ÂVµ¢vfðÇÁ·éƵܷB
+ ±ÌÆ«ñÂÌû@ª èÜ·B
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ 18sðêÂêÂíµÄA3sðÇÁ·é
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ RNVSÌðíi <literal>DELETE</literal> ÌSQLðêñjµA»µÄ5ÂÌvf·×Äð
+ iê¸ÂjÇÁ·é
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ HibernateͱÌêÉ2ÔÚÌû@ªæ謢¾ë¤Æí©éÙÇ«Í èܹñB
+ i»µÄHibernateª±Ìæ¤É«¢±Æà]ܵȢŵå¤B±Ìæ¤ÈUé¢Í
+ f[^x[XÌgKÈÇ𬳹é©àµêܹñBj
+ </para>
+
+ <para>
+ K¢ÉàA³ÌRNVðÌÄiÂÜèQÆðâßÄjA»ÝÌvfð·×ÄÂVµ¢RNVÌ
+ CX^XðÔ·±ÆÅA¢ÂÅà±ÌUé¢i2ÔÚÌíªjð§·é±ÆªoÜ·B
+ ɱêÍÆÄàÖÅÍÅ·B
+ </para>
+
+ <para>
+ à¿ëñAêíÍ <literal>inverse="true"</literal> ðwèµ½RNVÉÍs¢Ü¹ñB
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="performance-monitoring" revision="1">
+ <title>ptH[}XÌj^O</title>
+
+ <para>
+ ÅK»Íj^OâptH[}Xð¦·lªÈ¯êÎ\ªÉs¦Ü¹ñB
+ HibernateÍàÌ·×ÄÌÍÍÌlðñµÜ·B
+ HibernateÌvîñÍ <literal>SessionFactory</literal> PÊÅæ¾Â\Å·B
+ </para>
+
+ <sect2 id="performance-monitoring-sf" revision="2">
+ <title>SessionFactoryÌj^O</title>
+
+ <para>
+ <literal>SessionFactory</literal> ÌgNXÉANZX·éÉÍ2ÂÌû@ª èÜ·B
+ ÅÌû@ÍA <literal>sessionFactory.getStatistics()</literal> ðÄÑoµA
+ ©ªÅ <literal>Statistics</literal> ÌÇÝÝâ\¦ðs¢Ü·B
+ </para>
+
+ <para>
+ <literal>StatisticsService</literal> MBeanðLøɵĢêÎAHibernateÍJMXðgÁÄ
+ gNXðs·é±ÆàÅ«Ü·B1ÂÌMBeanð·×ÄÌ <literal>SessionFactory</literal>
+ ÉεÄLøÉ·é©ASessionFactory²ÆÉêÂÌMBeanðLøÉ·é±ÆªoÜ·B
+ ŬÀÌÝèáÅ éȺÌR[hð©Ä¾³¢B
+ </para>
+
+ <programlisting><![CDATA[// MBean service registration for a specific SessionFactory
+Hashtable tb = new Hashtable();
+tb.put("type", "statistics");
+tb.put("sessionFactory", "myFinancialApp");
+ObjectName on = new ObjectName("hibernate", tb); // MBean object name
+
+StatisticsService stats = new StatisticsService(); // MBean implementation
+stats.setSessionFactory(sessionFactory); // Bind the stats to a SessionFactory
+server.registerMBean(stats, on); // Register the Mbean on the server]]></programlisting>
+
+
+<programlisting><![CDATA[// MBean service registration for all SessionFactory's
+Hashtable tb = new Hashtable();
+tb.put("type", "statistics");
+tb.put("sessionFactory", "all");
+ObjectName on = new ObjectName("hibernate", tb); // MBean object name
+
+StatisticsService stats = new StatisticsService(); // MBean implementation
+server.registerMBean(stats, on); // Register the MBean on the server]]></programlisting>
+
+ <para>
+ TODO: ±êÍÓ¡ª èܹñBÅÌP[XÍAMBeanð¼Ú³µÄgpµÜ·B2ÔÚÌP[XÍA
+ g¤OÉsession factoryªÁÄ¢éJNDI¼ðn³È¯êÎÈèܹñB
+ <literal>hibernateStatsBean.setSessionFactoryJNDIName("my/JNDI/Name")</literal> ðgÁľ³¢B
+ </para>
+ <para>
+ <literal>SessionFactory</literal> ÉεÄj^OÌJniI¹jðs¤±ÆªoÜ·B
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ ÝèÉÍA <literal>hibernate.generate_statistics</literal> ð <literal>false</literal> ɵܷ
+ </para>
+ </listitem>
+ </itemizedlist>
+ <itemizedlist>
+ <listitem>
+ <para>
+ ÀsÉA <literal>sf.getStatistics().setStatisticsEnabled(true)</literal> ܽÍ
+ <literal>hibernateStatsBean.setStatisticsEnabled(true)</literal> ðÄÑoµÜ·
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ vÍ <literal>clear()</literal> \bhðgÁÄè®ÅZbg·é±ÆªoÜ·B
+ T}Í <literal>logSummary()</literal> \bhðgÁÄloggerÉé±ÆªoÜ·
+ iinfoxÅ·jB
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-monitoring-metrics" revision="1">
+ <title>gNX</title>
+
+ <para>
+ ½Ìà̪ èÜ·B·×ÄÌgpÂ\ÈJE^Í <literal>Statistics</literal>
+ C^[tFCXÌAPIÉ©êĨèA3Â̪ު èÜ·B
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ gNXÍêÊIÈ <literal>Session</literal> Ìg¢ûÆÖWµÄ¢Ü·B
+ I[vµ½ZbV̪JDBCRlNVÆÖAµÄ¢éÌƯ¶Å·B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ gNXÍvfARNVANGâLbV
ÈÇSÌÉ
+ ÖWµÄ¢Ü·iʼÍO[ogNXÅ·jB
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ gNXÌÚ×ÍÁèÌGeBeBARNVANGALbV
ÌæÉÖWµÄ¢Ü·B
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ áƵÄALbV
ÌqbgAqbg~XâAvfARNVANGÌANGÌÀsÉ
+ KvȽÏÔðmFÅ«Ü·B~bÌlÍJavaÌßðó¯é±ÆÉӵľ³¢B
+ HibernateÍJVM̸xɧÀ³êAvbgtH[ÉæÁÄÍ10bPÊŵ©³mÅÈ¢©àµêܹñB
+ </para>
+
+ <para>
+ PÈgetterÍO[ogNXi·Èí¿ÁèÌGeBeBARNVALbV
ÌæÈÇÉçêÈ¢j
+ ÉANZX·é½ßÉg¢Ü·BÁèÌGeBeBARNVALbV
ÌæÌgNXÍA
+ »êç̼OâANGÌHQLASQL\»ÉæÁÄANZX·é±ÆªoÜ·B³çÉÚµ¢îñÍA
+ <literal>Statistics</literal> A <literal>EntityStatistics</literal> A <literal>CollectionStatistics</literal>
+ A <literal>SecondLevelCacheStatistics</literal> A <literal>QueryStatistics</literal> APIÌjavadocð
+ QƵľ³¢BȺÌR[hÍÈPÈáÅ·B
+ </para>
+
+ <programlisting><![CDATA[Statistics stats = HibernateUtil.sessionFactory.getStatistics();
+
+double queryCacheHitCount = stats.getQueryCacheHitCount();
+double queryCacheMissCount = stats.getQueryCacheMissCount();
+double queryCacheHitRatio =
+ queryCacheHitCount / (queryCacheHitCount + queryCacheMissCount);
+
+log.info("Query Hit ratio:" + queryCacheHitRatio);
+
+EntityStatistics entityStats =
+ stats.getEntityStatistics( Cat.class.getName() );
+long changes =
+ entityStats.getInsertCount()
+ + entityStats.getUpdateCount()
+ + entityStats.getDeleteCount();
+log.info(Cat.class.getName() + " changed " + changes + "times" );]]></programlisting>
+
+ <para>
+ ·×ÄÌGeBeBARNVANGALbV
ÌæÉεÄs¤êÍA
+ <literal>getQueries()</literal> A <literal>getEntityNames()</literal>A
+ <literal>getCollectionRoleNames()</literal> A <literal>getSecondLevelCacheRegionNames()</literal> \bhÅ
+ »ê¼ê̼OÌXgðæ¾·é±ÆªoÜ·B
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+</chapter>
\ No newline at end of file
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/persistent_classes.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/persistent_classes.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/persistent_classes.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,539 @@
+<?xml version="1.0" encoding="Shift_JIS"?>
+<chapter id="persistent-classes" revision="2">
+ <title>i±NX</title>
+
+ <para>
+ i±NXÍrWlXãÌâèÌGeBeBiá¦ÎAER}[XAvP[VÌÚqâ¶j
+ ðÀ·éAvP[VÌNXÅ·B
+ i±NXÌ·×ÄÌCX^Xªi±óÔÅ éƩȳêéí¯ÅÍ èܹñB
+ CX^XÍtÉêIitransientjÅ Á½èAª£óÔidetachedjÅ Á½è·é©àµêܹñB
+ </para>
+
+ <para>
+ Plain Old Java Object (POJO)vO~OfƵÄàmçêé
+ ¢Â©ÌPÈ[É]¤ÈçAHibernateÍÅàæ«Ü·B
+ µ©µ±êçÌ[Íïµ¢àÌÅÍ èܹñB
+ ÀÛHibernate3Íi±IuWFNgÌ«¿ÉÙÆñǽÌOñàu¢Ä¢Ü¹ñB
+ hCfͼÌû@Å\»·é±ÆàÅ«Ü·B
+ á¦Î <literal>Map</literal> CX^XÌc[ðg¤û@ª èÜ·B
+ </para>
+
+ <sect1 id="persistent-classes-pojo">
+ <title>PÈPOJOÌá</title>
+
+ <para>
+ ÙÆñÇÌJavaAvP[VÉÍlRÈÌ®¨ð\»·éi±NXªKvÅ·B
+ </para>
+
+ <programlisting><![CDATA[package eg;
+import java.util.Set;
+import java.util.Date;
+
+public class Cat {
+ private Long id; // identifier
+
+ private Date birthdate;
+ private Color color;
+ private char sex;
+ private float weight;
+ private int litterId;
+
+ private Cat mother;
+ private Set kittens = new HashSet();
+
+ private void setId(Long id) {
+ this.id=id;
+ }
+ public Long getId() {
+ return id;
+ }
+
+ void setBirthdate(Date date) {
+ birthdate = date;
+ }
+ public Date getBirthdate() {
+ return birthdate;
+ }
+
+ void setWeight(float weight) {
+ this.weight = weight;
+ }
+ public float getWeight() {
+ return weight;
+ }
+
+ public Color getColor() {
+ return color;
+ }
+ void setColor(Color color) {
+ this.color = color;
+ }
+
+ void setSex(char sex) {
+ this.sex=sex;
+ }
+ public char getSex() {
+ return sex;
+ }
+
+ void setLitterId(int id) {
+ this.litterId = id;
+ }
+ public int getLitterId() {
+ return litterId;
+ }
+
+ void setMother(Cat mother) {
+ this.mother = mother;
+ }
+ public Cat getMother() {
+ return mother;
+ }
+ void setKittens(Set kittens) {
+ this.kittens = kittens;
+ }
+ public Set getKittens() {
+ return kittens;
+ }
+
+ // addKitten not needed by Hibernate
+ public void addKitten(Cat kitten) {
+ kitten.setMother(this);
+ kitten.setLitterId( kittens.size() );
+ kittens.add(kitten);
+ }
+}]]></programlisting>
+
+ <para>
+ ]¤×«4ÂÌ[ª èÜ·F
+ </para>
+
+ <sect2 id="persistent-classes-pojo-constructor" revision="1">
+ <title>øÌÈ¢RXgN^ðÀ·é</title>
+
+ <para>
+ <literal>Cat</literal> ÉÍøÌÈ¢RXgN^ª èÜ·B
+ Hibernateª <literal>Constructor.newInstance()</literal> ðgÁÄi±NXÌ
+ CX^X»ðs¦éæ¤ÉA·×ÄÌi±NXÉÍftHgRXgN^
+ ipublicÅÈÄà\¢Ü¹ñjªÈ¯êÎÈèܹñB
+ HibernateÌÀsvLV¶¬Ì½ßÉAÈÆà <emphasis>package</emphasis>
+ Ì«ðÂftHgRXgN^ð¨©ßµÜ·B
+ </para>
+ </sect2>
+
+ <sect2 id="persistent-classes-pojo-identifier" revision="2">
+ <title>¯ÊqvpeBðpÓ·éiIvVj</title>
+
+ <para>
+ <literal>Cat</literal> ÉÍ <literal>id</literal> Æ¢¤vpeBª èÜ·B
+ ±ÌvpeBÍf[^x[Xe[uÌåL[JÖ}bsO³êÜ·B
+ ±ÌvpeB̼OͽÅà\¢Ü¹ñµA^ÍÇÌæ¤Èv~eBu^ÅàA
+ v~eBuÌubp[v^ÅàA<literal>java.lang.String</literal> â
+ <literal>java.util.Date</literal> Åà\¢Ü¹ñB
+ iàµKV[f[^x[Xe[uª¡L[ðÂÈçÎA
+ ¡q×½æ¤È^ÌvpeBðÂ[Uè`ÌNXðg¤±Æ³¦Â\Å·B
+ ãÅ¡¯ÊqÌßð©Ä¾³¢Bj
+ </para>
+
+ <para>
+ ¯ÊqvpeB͵§ÉÍIvVÅ·B
+ ±êðȪµÄAHibernateÉàIÉIuWFNg̯ÊqðÇÕ³¹é±ÆÍÂ\Å·B
+ µ©µ¨··ß͵ܹñB
+ </para>
+
+ <para>
+ ÀÛÉA¯ÊqvpeBðé¾·éNX¾¯ªpÂ\È@\ª¢Â© èÜ·F
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ ª£IuWFNgÌA½IÈÄÇÁiJXP[hXVâJXP[h}[WjB
+ <xref linkend="objectstate-transitive"/> ðQƵľ³¢B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>Session.saveOrUpdate()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>Session.merge()</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ i±NXÉÍAêѵ½¼O̯ÊqvpeBðè`·é±Æð¨©ßµÜ·B
+ ³çÉnulllðæêé(ÂÜèv~eBuÅÍÈ¢)^ðgÁ½ûªæ¢Åµå¤B
+ </para>
+ </sect2>
+
+ <sect2 id="persistent-classes-pojo-final">
+ <title>finalNXɵȢiIvVj</title>
+ <para>
+ HibernateÌSIÈÁ¥Å é <emphasis>vLV</emphasis> ÍA
+ i±NXªfinalÅÈ¢±ÆAܽÍ\bhðSpublicÅ龵Ģé
+ C^[tFCXªÀ³êÄ¢é©É˶µÄ¢Ü·B
+ </para>
+ <para>
+ HibernateÅC^[tFCXðÀµÄ¢È¢ <literal>final</literal> NXði±»·é±ÆÍÅ«Ü·ªA
+ xÖAtFb`ÉεÄvLVðg¤±ÆÍÅ«ÈÈèÜ·B
+ ±êÍptH[}X`
[jOÖÌIðð·ßé±ÆÉÈèÜ·B
+ </para>
+ <para>
+ finalÅÍÈ¢NXÅ <literal>public final</literal> \bhðè`·é±Æàð¯é׫ŷB
+ <literal>public final</literal> \bhðÂNXðg¢½¯êÎA
+ <literal>lazy="false"</literal> ÆÝèµÄ¾¦IÉvLVð³øɵȯêÎÈèܹñB
+ </para>
+ </sect2>
+
+ <sect2 id="persistent-classes-pojo-accessors" revision="2">
+ <title>i±tB[hÉηéANZTÆ~
[e[^ðè`·éiIvVj</title>
+
+ <para>
+ <literal>Cat</literal> ÅÍ·×ÄÌi±tB[hÉεÄANZT\bhðè`µÄ¢Ü·B
+ ¼Ì½ÌORMc[ÍAi±CX^XÏð¼Úi±»µÜ·B
+ ½¿Í[ViXL[}ÆNXÌà\¢ðª£·éûªÇ¢ÆM¶Ä¢Ü·B
+ ftHgÅÍAHibernateÍJavaBeanX^CÌvpeBði±»µA
+ <literal>getFoo</literal>, <literal>isFoo</literal>, <literal>setFoo</literal>
+ `®Ì\bh¼ðF¯µÜ·B
+ µ©µKvÈçAÁèÌvpeBÉεÄA¼ÚÌtB[hANZXÉØèÖ¦é±ÆÍÂ\Å·B
+ </para>
+
+ <para>
+ vpeBÍpublicÅé¾·éKvÍ <emphasis> èܹñ</emphasis> B
+ HibernateÍftHgA<literal>protected</literal> àµÍ <literal>private</literal>
+ Ìget / setÌyAðÂvpeBði±»·é±ÆªÅ«Ü·B
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="persistent-classes-inheritance">
+ <title>p³ÌÀ</title>
+
+ <para>
+ TuNXà1ÔÚÆ2ÔÚÌ[ðççȯêÎÈèܹñB
+ TuNXÍX[p[NX <literal>Cat</literal> ©ç¯ÊqvpeBðp³µÜ·B
+ </para>
+
+ <programlisting><![CDATA[package eg;
+
+public class DomesticCat extends Cat {
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+ protected void setName(String name) {
+ this.name=name;
+ }
+}]]></programlisting>
+ </sect1>
+
+ <sect1 id="persistent-classes-equalshashcode" revision="1">
+ <title><literal>equals()</literal> Æ <literal>hashCode()</literal> ÌÀ</title>
+
+ <para>
+ ȺÌðÌêA
+ <literal>equals()</literal> Æ <literal>hashCode()</literal> \bhðI[o[ChµÈ¯êÎÈèܹñA
+ </para>
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ i±NXÌCX^Xð <literal>Set</literal> ÉuêB
+ i±êͽlÌÖAð\»·é¨··ßÌû@Å·j
+ <emphasis>»µÄ¯É</emphasis>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ª£CX^XðZbVÖÄÇÁ·éêB
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ HibernateÍAi±IDif[^x[XÌsjÆAÁèÌZbVXR[vàÉ
+ ÀèÅ·ªJavaIDƪ¿Å é±ÆðÛصܷB
+ Å·©çÙÈéZbVÅõµ½CX^XðgÝí¹éêA
+ <literal>Set</literal> ÉÓ¡Ì éZ}eBNXð½¹æ¤ÆvÁÄ¢éÈç
+ ·®É<literal>equals()</literal> Æ <literal>hashCode()</literal>
+ ðÀµÈ¯êÎÈèܹñB
+ </para>
+
+ <para>
+ Åà¾Èû@ÍA¼ûÌIuWFNg̯ÊqÌlÌärÉæÁÄ <literal>equals()</literal>Æ
+ <literal>hashCode()</literal> ðÀ·éû@Å·B
+ lª¯¶ÈçA¼ÒÍf[^x[X̯¶sÅȯêÎÈçÈ¢½ßµÈèÜ·B
+ i¼Òª <literal>Set</literal> ÉÇÁ³êÄàA
+ <literal>Set</literal> ÉÍ1ÂÌvfµ©È¢±ÆÉÈèÜ·j
+ cOȱÆÉA¶¬³ê½¯ÊqÉͱÌAv[`ðg¤±ÆªÅ«Ü¹ñB
+ HibernateÍi±»³ê½IuWFNgÖ¯ÊqÌlðãü·é¾¯Å èA
+ Vµì¬³ê½CX^XÍÇÌæ¤È¯ÊqÌlàÁĢܹñB
+ ³çÉACX^XªZ[u³êĨç¸A»Ý <literal>Set</literal> ÌÉ êÎA
+ Z[u·éÆIuWFNgÖ¯ÊqÌlðãü·é±ÆÉÈèÜ·B
+ ൠ<literal>equals()</literal> Æ <literal>hashCode()</literal> ª¯ÊqÌlÉîâĢéÈçA
+ nbV
R[hªÏX³êéÆ <literal>Set</literal> ÌKñªjçêÜ·B
+ ±ÌâèÉ¢ÄÌ®SÈc_ÍAHibernateÌEFuTCgð©Ä¾³¢B
+ ±êÍHibernateÌâèÅÍÈAIuWFNg̯ê«Æ¿«É¢ÄÌA
+ ÊíÌJavaÌZ}eBNXÅ é±ÆÉӵľ³¢B
+ </para>
+
+ <para>
+ <emphasis>rWlXL[Ì¿«</emphasis> ðgÁÄA
+ <literal>equals()</literal> Æ <literal>hashCode()</literal> ðÀ·é±Æð¨©ßµÜ·B
+ rWlXL[Ì¿«ÆÍA<literal>equals()</literal> \bhªArWlXL[A
+ ÂÜè»ÀÌ¢Eɨ¢ÄCX^XðÁè·éL[i<emphasis>©R</emphasis> óâL[j
+ ð`¬·évpeB¾¯ðär·é±ÆðÓ¡µÜ·B
+ </para>
+
+ <programlisting><![CDATA[public class Cat {
+
+ ...
+ public boolean equals(Object other) {
+ if (this == other) return true;
+ if ( !(other instanceof Cat) ) return false;
+
+ final Cat cat = (Cat) other;
+
+ if ( !cat.getLitterId().equals( getLitterId() ) ) return false;
+ if ( !cat.getMother().equals( getMother() ) ) return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ int result;
+ result = getMother().hashCode();
+ result = 29 * result + getLitterId();
+ return result;
+ }
+
+}]]></programlisting>
+
+ <para>
+ rWlXL[Íf[^x[XÌåL[óâÙÇÀèÅ éKvÍÈ¢±ÆÉӵľ³¢
+ i<xref linkend="transactions-basics-identity"/> ð©Ä¾³¢jB
+ XVsÂÈvpeBâj[NÈvpeBÍAÊírWlXL[Ìæ¢óâÅ·B
+ </para>
+
+ </sect1>
+
+ <sect1 id="persistent-classes-dynamicmodels">
+ <title>®If</title>
+
+ <para>
+ <emphasis>ȺÌ@\Í»ÝÀ±iKÉ éƩȳêĨèA
+ ߢ«ÏX³êéÂ\«ª é±ÆÉӵľ³¢B</emphasis>
+ </para>
+
+ <para>
+ i±GeBeBÍAK¸µàÀsÉPOJONXâJavaBeanIuWFNgÅ\»·éKvÍ èܹñB
+ HibernateÍiÀsÉ <literal>Map</literal> Ì <literal>Map</literal> ðg¤j®IfÆA
+ DOM4Jc[ƵÄÌGeBeBÌ\»àT|[gµÜ·B
+ ±ÌAv[`ðg¤Æi±NXð©¸A}bsOt@C¾¯ð±ÆÉÈèÜ·B
+ </para>
+
+ <para>
+ ftHgÅÍAHibernateÍÊíÌPOJO[hÅ®ìµÜ·B
+ <literal>default_entity_mode</literal> ÝèIvVðgÁÄA
+ ÁèÌ <literal>SessionFactory</literal> ÉηéftHgÌGeBeB\»[hðÝè·é±ÆªÅ«Ü·
+ i<xref linkend="configuration-optional-properties"/> ð©Ä¾³¢jB
+ </para>
+
+ <para>
+ ȺÌáÅÍ <literal>Map</literal> ðgÁ½\»ðÐîµÜ·B
+ ܸ}bsOt@CÅANX¼ÌãíèÉiܽͻêÉÁ¦Äj
+ <literal>entity-name</literal> ðè`µÈ¯êÎÈèܹñF
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class entity-name="Customer">
+
+ <id name="id"
+ type="long"
+ column="ID">
+ <generator class="sequence"/>
+ </id>
+
+ <property name="name"
+ column="NAME"
+ type="string"/>
+
+ <property name="address"
+ column="ADDRESS"
+ type="string"/>
+
+ <many-to-one name="organization"
+ column="ORGANIZATION_ID"
+ class="Organization"/>
+
+ <bag name="orders"
+ inverse="true"
+ lazy="false"
+ cascade="all">
+ <key column="CUSTOMER_ID"/>
+ <one-to-many class="Order"/>
+ </bag>
+
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ ÖAª^[QbgÌNX¼ðgÁÄè`µÄ¢½ÆµÄàA
+ ÖAÌ^[Qbg^àPOJOÅÍÈ®IÈGeBeBÅà\íÈ¢±ÆÉӵľ³¢B
+ </para>
+
+ <para>
+ <literal>SessionFactory</literal> ÉεÄftHgÌGeBeB[hð
+ <literal>dynamic-map</literal> ÉÝèµ½ãA
+ ÀsÉ <literal>Map</literal> Ì <literal>Map</literal> ðg¤±ÆªÅ«Ü·F
+ </para>
+
+ <programlisting><![CDATA[Session s = openSession();
+Transaction tx = s.beginTransaction();
+Session s = openSession();
+
+// Create a customer
+Map david = new HashMap();
+david.put("name", "David");
+
+// Create an organization
+Map foobar = new HashMap();
+foobar.put("name", "Foobar Inc.");
+
+// Link both
+david.put("organization", foobar);
+
+// Save both
+s.save("Customer", david);
+s.save("Organization", foobar);
+
+tx.commit();
+s.close();]]></programlisting>
+
+ <para>
+ ®IÈ}bsOÌ_ÍAGeBeBNXÌÀðKvƵȢ½ßA
+ vg^CsOÉv·é^[AEh^Cª¢Æ¢¤±ÆÅ·B
+ µ©µRpCÌ^`FbNªÈ¢ÌÅAÀsÉñíɽÌáOðµíȯêÎÈçȢŵå¤B
+ Hibernate}bsǪ©°ÅAf[^x[XXL[}ÍeÕɳK»Å«ASÉÈèA
+ ãÅKØÈhCfÌÀðÇÁ·é±ÆªÂ\ÉÈèÜ·B
+ </para>
+
+ <para>
+ GeBeB\»[hÍ <literal>Session</literal> ²ÆÉÝè·é±ÆàÂ\Å·B
+ </para>
+
+ <programlisting><![CDATA[Session dynamicSession = pojoSession.getSession(EntityMode.MAP);
+
+// Create a customer
+Map david = new HashMap();
+david.put("name", "David");
+dynamicSession.save("Customer", david);
+...
+dynamicSession.flush();
+dynamicSession.close()
+...
+// Continue on pojoSession
+]]></programlisting>
+
+
+ <para>
+ <literal>EntityMode</literal> ðgÁ½ <literal>getSession()</literal>
+ ÌÄÑoµÍ <literal>SessionFactory</literal> ÅÍÈ <literal>Session</literal>
+ APIÉ é±ÆÉӵľ³¢B
+ »Ìû@ÅÍAVµ¢ <literal>Session</literal> ÍAx[XÆÈéJDBCRlNVA
+ gUNVA»Ì¼ÌReLXgîñð¤LµÜ·B
+ ±êÍ2ÔÚÌ <literal>Session</literal> ÅÍ <literal>flush()</literal> Æ <literal>close()</literal>
+ ðÄÔKvªÈ¢Æ¢¤±ÆA»Ì½ßgUNVÆRlNVÌÇð
+ 1ÔÚÌìÆPÊ(Unit of Work)ÉC¹é±ÆªÅ«éÆ¢¤±ÆÅ·B
+ </para>
+
+ <para>
+ XML\»Ì\ÍÉ¢Ä̳çÈéîñÍ <xref linkend="xml"/> ũ©èÜ·B
+ </para>
+
+ </sect1>
+
+ <sect1 id="persistent-classes-tuplizers" revision="1">
+ <title>Tuplizer</title>
+
+ <para>
+ <literal>org.hibernate.tuple.Tuplizer</literal> Æ»ÌTuC^[tFCXÍA
+ \»Ì <literal>org.hibernate.EntityMode</literal> ðpµÄA
+ f[^fÐÌ é\»ÌÇÉÓCð¿Ü·B
+ ^¦çê½f[^fÐðf[^\¢ÆµÄl¦éÈçATuplizerÍ»Ìæ¤Èf[^\¢ð
+ ÇÌæ¤É쬷é©ðmèA»Ìæ¤Èf[^\¢©çÇÌæ¤ÉlðoµA
+ ü·é©ðmÁĢܷB
+ á¦ÎPOJOGeBeB[hÅÍAηéTuplizerÍRXgN^ðʵÄA
+ POJOðÇÌæ¤É쬷é©Aè`³ê½vpeBANZTðg¢A
+ POJOvpeBÉÇÌæ¤ÉANZX·é©ðmèÜ·B
+ TuplizerÉÍñÂÌnCxÌ^ª èÜ·B
+ »êçÍA<literal>org.hibernate.tuple.EntityTuplizer</literal> Æ
+ <literal>org.hibernate.tuple.ComponentTuplizer</literal> C^[tFCXÅ\»³êÜ·B
+ <literal>EntityTuplizer</literal> ÍãÅq×½æ¤ÈGeBeBÉÖ·é_ñÌÇÉÓCð¿Ü·B
+ êûA<literal>ComponentTuplizer</literal> ÍR|[lgÉÖ·é_ñÌÇÉÓCð¿Ü·B
+ </para>
+
+ <para>
+ [UÍÆ©ÌTuplizerÉ·µÖ¦é±ÆàÂ\Å·B
+ ¨»çdynamic-map entity-modeÌÛÉ <literal>java.util.HashMap</literal> ðg¤ÌÅÍÈA
+ <literal>java.util.Map</literal> ÌÀªKvŵå¤B
+ àµÍA¨»çftHgÌàÌÅÍÈAÊÌvLV¶¬íªÌè`ªKvŵå¤B
+ ¼ÒÆàAJX^ÌTuplizerÀðè`·é±ÆÅB¬³êÜ·B
+ TuplizerÌè`ÍAǵæ¤Æ·éGeBeBâR|[lgÌ}bsOÉÑt¯çêÜ·B
+ ÚqGeBeBÌáÉßéÆF
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+ <class entity-name="Customer">
+ <!--
+ Override the dynamic-map entity-mode
+ tuplizer for the customer entity
+ -->
+ <tuplizer entity-mode="dynamic-map"
+ class="CustomMapTuplizerImpl"/>
+
+ <id name="id" type="long" column="ID">
+ <generator class="sequence"/>
+ </id>
+
+ <!-- other properties -->
+ ...
+ </class>
+</hibernate-mapping>
+
+
+public class CustomMapTuplizerImpl
+ extends org.hibernate.tuple.entity.DynamicMapEntityTuplizer {
+ // override the buildInstantiator() method to plug in our custom map...
+ protected final Instantiator buildInstantiator(
+ org.hibernate.mapping.PersistentClass mappingInfo) {
+ return new CustomMapInstantiator( mappingInfo );
+ }
+
+ private static final class CustomMapInstantiator
+ extends org.hibernate.tuple.DynamicMapInstantitor {
+ // override the generateMap() method to return our custom map...
+ protected final Map generateMap() {
+ return new CustomMap();
+ }
+ }
+}]]></programlisting>
+
+
+ </sect1>
+
+ <para>
+ TODO: vpeBÆvLVpbP[WÌ[Ug£t[[N𶻷é±Æ
+ </para>
+
+</chapter>
+
+
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/query_criteria.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/query_criteria.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/query_criteria.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,448 @@
+<?xml version="1.0" encoding="Shift_JIS"?>
+<chapter id="querycriteria">
+ <title>CriteriaNG</title>
+
+ <para>
+ HibernateÉÍA¼´IÅg£Â\ÈcriteriaNGAPIªpÓ³êĢܷB
+ </para>
+
+ <sect1 id="querycriteria-creating">
+ <title> <literal>Criteria</literal> CX^XÌì¬</title>
+
+ <para>
+ <literal>org.hibernate.Criteria</literal>
+ C^[tFCXÍÁèÌi±«NXÉηéNGð\»µÜ·B
+ <literal>Session</literal> Í <literal>Criteria</literal> CX^XÌt@NgÅ·B
+ </para>
+
+ <programlisting><![CDATA[Criteria crit = sess.createCriteria(Cat.class);
+crit.setMaxResults(50);
+List cats = crit.list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-narrowing">
+ <title>UgZbgÌiÝ</title>
+
+ <para>
+ <literal>org.hibernate.criterion.Criterion</literal> C^[tFCXÌCX^XÍA
+ ÂÊÌNGNCeIiâ¢í¹Ì»èîjð\µÜ·B
+ <literal>org.hibernate.criterion.Restrictions</literal>
+ NXÍA égÝÝÌ <literal>Criterion</literal>
+ ^ðæ¾·é½ßÌt@Ng\bhðÁĢܷB
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "Fritz%") )
+ .add( Restrictions.between("weight", minWeight, maxWeight) )
+ .list();]]></programlisting>
+
+ <para>
+ RestrictioniÀèjÍA_IÉO[v»Å«Ü·B
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "Fritz%") )
+ .add( Restrictions.or(
+ Restrictions.eq( "age", new Integer(0) ),
+ Restrictions.isNull("age")
+ ) )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.in( "name", new String[] { "Fritz", "Izi", "Pk" } ) )
+ .add( Restrictions.disjunction()
+ .add( Restrictions.isNull("age") )
+ .add( Restrictions.eq("age", new Integer(0) ) )
+ .add( Restrictions.eq("age", new Integer(1) ) )
+ .add( Restrictions.eq("age", new Integer(2) ) )
+ ) )
+ .list();]]></programlisting>
+
+ <para>
+ ³X éCriterion^i<literal>Restrictions</literal> ÌTuNXj
+ Í©ÈèÌÍÍÉyÑÜ·ªAÁÉLpÈÌÍSQLð¼ÚwèÅ«éàÌÅ·B
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.sqlRestriction("lower({alias}.name) like lower(?)", "Fritz%", Hibernate.STRING) )
+ .list();]]></programlisting>
+
+ <para>
+ <literal>{alias}</literal> Æ¢¤v[Xz_ÍA
+ â¢í¹ðó¯½GeBeBÌsÌʼÉæÁÄu«·¦çêÜ·B
+ </para>
+
+ <para>
+ criterionð¾éÊÌèiÍA
+ <literal>Property</literal> CX^X©çæ¾·é±ÆÅ·B
+ <literal>Property.forName()</literal> ðÄÑoµÄA
+ <literal>Property</literal> CX^Xðì¬Å«Ü·B
+ </para>
+
+ <programlisting><![CDATA[
+Property age = Property.forName("age");
+List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.disjunction()
+ .add( age.isNull() )
+ .add( age.eq( new Integer(0) ) )
+ .add( age.eq( new Integer(1) ) )
+ .add( age.eq( new Integer(2) ) )
+ ) )
+ .add( Property.forName("name").in( new String[] { "Fritz", "Izi", "Pk" } ) )
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-ordering">
+ <title>ÊÌ®ñ</title>
+
+ <para>
+ <literal>org.hibernate.criterion.Order</literal>
+ ðgÁÄÊðÀÑÖ¦é±ÆªÅ«Ü·B
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "F%")
+ .addOrder( Order.asc("name") )
+ .addOrder( Order.desc("age") )
+ .setMaxResults(50)
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Property.forName("name").like("F%") )
+ .addOrder( Property.forName("name").asc() )
+ .addOrder( Property.forName("age").desc() )
+ .setMaxResults(50)
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-associations" revision="2">
+ <title>ÖA</title>
+
+ <para>
+ <literal>createCriteria()</literal> ðg¢AÖAðirQ[g·é±ÆÅA
+ eÕÉÖW·éGeBeBɧñðwèÅ«Ü·B
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "F%") )
+ .createCriteria("kittens")
+ .add( Restrictions.like("name", "F%") )
+ .list();]]></programlisting>
+
+ <para>
+ 2ÔÚÌ <literal>createCriteria()</literal> ÍA<literal>kittens</literal>
+ RNVÌvfðQÆ·éVµ¢ <literal>Criteria</literal>
+ CX^XðÔ·±ÆÉӵľ³¢B
+ </para>
+
+ <para>
+ ȺÌæ¤Èû@àAóµÉæèLpÅ·B
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .createAlias("kittens", "kt")
+ .createAlias("mate", "mt")
+ .add( Restrictions.eqProperty("kt.name", "mt.name") )
+ .list();]]></programlisting>
+
+ <para>
+ i<literal>createAlias()</literal> ÍVµ¢
+ <literal>Criteria</literal> CX^Xð쬵ܹñBj
+ </para>
+
+ <para>
+ OÌ2ÂÌNGÉæÁÄÔ³êé <literal>Cat</literal>
+ CX^XÉæÁÄÛ³êékittensRNVÍA
+ criteriaÉæÁÄOÉtB^O <emphasis>³êÈ¢</emphasis>
+ ±ÆÉӵľ³¢B
+ àµcriteriaÉK·ékittenðæ¾µ½¢ÈçA
+ <literal>ResultTransformer</literal> ðgíȯêÎÈèܹñB
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .createCriteria("kittens", "kt")
+ .add( Restrictions.eq("name", "F%") )
+ .setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP)
+ .list();
+Iterator iter = cats.iterator();
+while ( iter.hasNext() ) {
+ Map map = (Map) iter.next();
+ Cat cat = (Cat) map.get(Criteria.ROOT_ALIAS);
+ Cat kitten = (Cat) map.get("kt");
+}]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-dynamicfetching" revision="1">
+ <title>ÖAÌ®ItFb`</title>
+
+ <para>
+ <literal>setFetchMode()</literal> ðg¢A
+ ÀsÉÖA̳û@ðwèµÄàæ¢Å·B
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "Fritz%") )
+ .setFetchMode("mate", FetchMode.EAGER)
+ .setFetchMode("kittens", FetchMode.EAGER)
+ .list();]]></programlisting>
+
+ <para>
+ ±ÌNGÍOÉæè <literal>mate</literal> Æ
+ <literal>kittens</literal> ̼ûðtFb`µÜ·B
+ æè½ÌîñÍ <xref linkend="performance-fetching"/> ðQƵľ³¢B
+ </para>
+
+ </sect1>
+
+ <sect1 id="querycriteria-examples">
+ <title>NGÌá</title>
+
+ <para>
+ <literal>org.hibernate.criterion.Example</literal> NXÍA
+ ^¦çê½CX^X©çNGNCeIð\zÅ«Ü·B
+ </para>
+
+ <programlisting><![CDATA[Cat cat = new Cat();
+cat.setSex('F');
+cat.setColor(Color.BLACK);
+List results = session.createCriteria(Cat.class)
+ .add( Example.create(cat) )
+ .list();]]></programlisting>
+
+ <para>
+ o[WvpeBA¯ÊqAÖAͳ³êÜ·B
+ ftHgÅÍnulllÌvpeBÍO³êÜ·B
+ </para>
+
+ <para>
+ ÇÌæ¤É <literal>Example</literal> ðKp·é©
+ ²®·é±ÆªÅ«Ü·B
+ </para>
+
+ <programlisting><![CDATA[Example example = Example.create(cat)
+ .excludeZeroes() //exclude zero valued properties
+ .excludeProperty("color") //exclude the property named "color"
+ .ignoreCase() //perform case insensitive string comparisons
+ .enableLike(); //use like for string comparisons
+List results = session.createCriteria(Cat.class)
+ .add(example)
+ .list();]]></programlisting>
+
+ <para>
+ ÖAIuWFNgÉcriteriaðwè·é½ßÉAExampleðg¤±ÆàÂ\Å·B
+ </para>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .add( Example.create(cat) )
+ .createCriteria("mate")
+ .add( Example.create( cat.getMate() ) )
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-projection">
+ <title>ËeAWñAO[v»</title>
+ <para>
+ <literal>org.hibernate.criterion.Projections</literal> NXÍ
+ <literal>Projection</literal> CX^XÌt@NgÅ·B
+ <literal>setProjection()</literal> ðÄÑo·±ÆÅA
+ NGÉËeðKpµÜ·B
+ </para>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.rowCount() )
+ .add( Restrictions.eq("color", Color.BLACK) )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.projectionList()
+ .add( Projections.rowCount() )
+ .add( Projections.avg("weight") )
+ .add( Projections.max("weight") )
+ .add( Projections.groupProperty("color") )
+ )
+ .list();]]></programlisting>
+
+ <para>
+ KvÅ ÁÄàAcriteriaNGÉugroup byv𾦷éKvÍ èܹñB
+ éíÌProjection^Í <emphasis>O[v»Ëe</emphasis> ƵÄè`³êA
+ SQLÌ <literal>group by</literal> ßÉà»êÜ·B
+ </para>
+
+ <para>
+ CÓÅËeÉʼðt¯çêé½ßAËe³êélÍrestrictionâorderingà©çQÆÅ«Ü·B
+ ʼð¯éQÂÌÙÈéû@ð¦µÜ·B
+ </para>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.alias( Projections.groupProperty("color"), "colr" ) )
+ .addOrder( Order.asc("colr") )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.groupProperty("color").as("colr") )
+ .addOrder( Order.asc("colr") )
+ .list();]]></programlisting>
+
+ <para>
+ <literal>alias()</literal> Æ <literal>as()</literal> \bhÍA
+ ProjectionCX^XðÊ̼OÌ <literal>Projection</literal> CX^XÅ
+ bv·é¾¯Å·B
+ V[gJbgƵÄAËeðËeXgÉÇÁ·éÛÉAʼð¯çêÜ·B
+ </para>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.projectionList()
+ .add( Projections.rowCount(), "catCountByColor" )
+ .add( Projections.avg("weight"), "avgWeight" )
+ .add( Projections.max("weight"), "maxWeight" )
+ .add( Projections.groupProperty("color"), "color" )
+ )
+ .addOrder( Order.desc("catCountByColor") )
+ .addOrder( Order.desc("avgWeight") )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Domestic.class, "cat")
+ .createAlias("kittens", "kit")
+ .setProjection( Projections.projectionList()
+ .add( Projections.property("cat.name"), "catName" )
+ .add( Projections.property("kit.name"), "kitName" )
+ )
+ .addOrder( Order.asc("catName") )
+ .addOrder( Order.asc("kitName") )
+ .list();]]></programlisting>
+
+ <para>
+ ËeÌ®É <literal>Property.forName()</literal> àgpÅ«Ü·B
+ </para>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Property.forName("name") )
+ .add( Property.forName("color").eq(Color.BLACK) )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.projectionList()
+ .add( Projections.rowCount().as("catCountByColor") )
+ .add( Property.forName("weight").avg().as("avgWeight") )
+ .add( Property.forName("weight").max().as("maxWeight") )
+ .add( Property.forName("color").group().as("color" )
+ )
+ .addOrder( Order.desc("catCountByColor") )
+ .addOrder( Order.desc("avgWeight") )
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-detachedqueries">
+ <title>NG¨æÑTuNG̪£</title>
+ <para>
+ <literal>DetachedCriteria</literal> NXÉæèA
+ ZbVXR[vOÉNGðì¬Å«Ü·B
+ ãÅACÓÌ <literal>Session</literal> ðgÁÄAÀsÅ«Ü·B
+ </para>
+
+ <programlisting><![CDATA[DetachedCriteria query = DetachedCriteria.forClass(Cat.class)
+ .add( Property.forName("sex").eq('F') );
+
+Session session = ....;
+Transaction txn = session.beginTransaction();
+List results = query.getExecutableCriteria(session).setMaxResults(100).list();
+txn.commit();
+session.close();]]></programlisting>
+
+ <para>
+ <literal>DetachedCriteria</literal> ÍATuNGð\»·é½ßÉàg¦Ü·B
+ TuNGðº¤CriterionCX^XÍA
+ <literal>Subqueries</literal> àµÍ <literal>Property</literal> ©ç¾çêÜ·B
+ </para>
+
+ <programlisting><![CDATA[DetachedCriteria avgWeight = DetachedCriteria.forClass(Cat.class)
+ .setProjection( Property.forName("weight").avg() );
+session.createCriteria(Cat.class)
+ .add( Property.forName("weight).gt(avgWeight) )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[DetachedCriteria weights = DetachedCriteria.forClass(Cat.class)
+ .setProjection( Property.forName("weight") );
+session.createCriteria(Cat.class)
+ .add( Subqueries.geAll("weight", weights) )
+ .list();]]></programlisting>
+
+ <para>
+ ÝÖWª éTuNGų¦àÂ\Å·B
+ </para>
+
+ <programlisting><![CDATA[DetachedCriteria avgWeightForSex = DetachedCriteria.forClass(Cat.class, "cat2")
+ .setProjection( Property.forName("weight").avg() )
+ .add( Property.forName("cat2.sex").eqProperty("cat.sex") );
+session.createCriteria(Cat.class, "cat")
+ .add( Property.forName("weight).gt(avgWeightForSex) )
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <!--TODO: ResultSetTransformer + aliasing. AliasToBeanTransformer allow returning arbitrary
+ user objects - similar to setResultClass in JDO2. General use of ResultTransformer
+ could also be explained. -->
+
+ <sect1 id="query-criteria-naturalid">
+ <title>©R¯ÊqÉæéNG</title>
+
+ <para>
+ criteriaNGðÜÞ½¢Ä¢ÌNGÉÆÁÄA
+ NGLbV
Í Üèø¦ªæȢŷB
+ ȺÈçANGLbV
ªpÉɳøÉÈé½ßÅ·B
+ µ©µÈªçALbV
ð³øÉ·éASYðÅK»Å«é
+ ÁÊÈNGÌíÞªP èÜ·B
+ XV³êÈ¢©RL[ÉæéõÅ·B
+ ¢Â©ÌAvP[VÅÍA±ÌíÞÌNGªpÉÉ»êÜ·B
+ ±Ìæ¤Ègíêû̽ßÉAcriteria APIÍÁÊÈÎôðñµÜ·B
+ </para>
+
+ <para>
+ ÅÉA<literal><natural-id></literal> ðgÁÄA
+ GeBeBÌ©RL[ð}bvµÄ¾³¢B
+ »µÄAñLbV
ðLøɵܷB
+ </para>
+
+ <programlisting><![CDATA[<class name="User">
+ <cache usage="read-write"/>
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <natural-id>
+ <property name="name"/>
+ <property name="org"/>
+ </natural-id>
+ <property name="password"/>
+</class>]]></programlisting>
+
+ <para>
+ ÓF <emphasis>ÏX³êé</emphasis> ©RL[ðÂGeBeBÉ
+ ±Ì@\ðg¤ÌÍAÓ}³êĢȢg¢ûÅ·B
+ </para>
+
+ <para>
+ ÉAHibernateNGLbV
ðLøɵܷB
+ </para>
+
+ <para>
+ ±êÅA<literal>Restrictions.naturalId()</literal> ÉæèA
+ æèø¦IÈLbV
ASYðgpÅ«Ü·B
+ </para>
+
+ <programlisting><![CDATA[session.createCriteria(User.class)
+ .add( Restrictions.naturalId()
+ .set("name", "gavin")
+ .set("org", "hb")
+ ).setCacheable(true)
+ .uniqueResult();]]></programlisting>
+
+ </sect1>
+
+</chapter>
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/query_hql.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/query_hql.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/query_hql.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1131 @@
+<?xml version="1.0" encoding="Shift_JIS"?>
+<chapter id="queryhql">
+ <title>HQL: The Hibernate Query Language</title>
+
+ <para>
+ HibernateÍSQLÉñíÉæ½(Ó}Iɹ½)ÍÈâ¢í¹¾êðõ¦Ä¢Ü·B
+ µ©µSQLɽ\¶Éfí³êȢž³¢BHQLÍ®SÉIuWFNgwüÅ èA
+ p³A|[tBYAÖAÆ¢Á½TOððµÜ·B
+ </para>
+
+ <sect1 id="queryhql-casesensitivity">
+ <title>å¶Æ¬¶ÌæÊ</title>
+
+ <para>
+ NGÍJavaÌNX¼ÆvpeB¼ð¢Äå¶A¬¶ðæʵܹñB
+ ]ÁÄ <literal>SeLeCT</literal> Í
+ <literal>sELEct</literal> Ư¶ÅA©Â
+ <literal>SELECT</literal> Æ௶ŷª
+ <literal>net.sf.hibernate.eg.FOO</literal> Í
+ <literal>net.sf.hibernate.eg.Foo</literal> ÆÍá¢A©Â
+ <literal>foo.barSet</literal> Í
+ <literal>foo.BARSET</literal> Æàá¢Ü·B
+ </para>
+
+ <para>
+ ±Ì}j
AÅͬ¶ÌHQLL[[hðgpµÜ·B
+ å¶ÌL[[hÌNGÌûªÇÝⷢƴ¶é[U[à¢éÆv¢Ü·B
+ Å·ªAJavaR[hàÉßÜê½Æ«ÉÍ©Ãç¢Æv¢Ü·B
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-from">
+ <title>fromß</title>
+
+ <para>
+ àÁÆàPÈHibernateNGÍÌ`®Å·B
+ </para>
+
+ <programlisting><![CDATA[from eg.Cat]]></programlisting>
+
+ <para>
+ ±êÍPÉ@<literal>eg.Cat</literal>@NXÌCX^Xð·×ÄԵܷB
+ K¸µàNX¼ðCü·éiNXÉpbP[W¼ðt¯éjKvÍ èܹñB
+ Æ¢¤ÌàA<literal>auto-import</literal> ªftHgÉÈÁÄ¢é©çÅ·B
+ »Ì½ßÙÆñÇÌêA±Ìæ¤É¾¯Å\ªÅ·B
+ </para>
+
+ <programlisting><![CDATA[from Cat]]></programlisting>
+
+ <para>
+ ÙÆñÇÌêNGÌ٩̪Å@<literal>Cat</literal>@ðQÆ·éÌÅAʼðèÄéKvª éŵå¤B
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat]]></programlisting>
+
+ <para>
+ ±ÌNGÅÍ <literal>Cat</literal> CX^XÉ <literal>cat</literal>
+ Æ¢¤Ê¼ðt¯Ä¢Ü·B
+ »Ì½ßAãűÌNGàÅA±Ìʼðg¤±ÆªÅ«Ü·B
+ <literal>as</literal> L[[hÍIvVÅ·BÂÜè±Ìæ¤É±ÆàÅ«Ü·F
+ </para>
+
+ <programlisting><![CDATA[from Cat cat]]></programlisting>
+
+ <para>
+ ¼ÏA é¢ÍNXÉæÁĽÌNXªo»·é±Æà èÜ·B
+ </para>
+
+ <programlisting><![CDATA[from Formula, Parameter]]></programlisting>
+ <programlisting><![CDATA[from Formula as form, Parameter as param]]></programlisting>
+
+ <para>
+ [JÏÌJavaÌl[~OîÆêvµ½A
+ ª¶É¬¶ðgÁ½NGÌʼðt¯é±ÆÍ¢¢KµÅ·(á¦Î <literal>domesticCat</literal> )B
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-joins" revision="2">
+ <title>ÖAÆ</title>
+
+ <para>
+ ÖA·éGeBeB é¢ÍlRNVÌvfÉàA<literal></literal> ðgÁÄʼðèÄé±ÆªoÜ·B
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+ inner join cat.mate as mate
+ left outer join cat.kittens as kitten]]></programlisting>
+
+ <programlisting><![CDATA[from Cat as cat left join cat.mate.kittens as kittens]]></programlisting>
+
+ <programlisting><![CDATA[from Formula form full join form.parameter param]]></programlisting>
+
+ <para>
+ T|[gµÄ¢éÌ^CvÍANSI SQLƯ¶Å·B
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>inner join</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>left outer join</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>right outer join</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>full join</literal> (½¢Ä¢Ìêg¢Ãç¢)
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ <literal>inner join</literal>A<literal>left outer join</literal>A<literal>right outer join</literal>ÉÍȪ`ðg¤±ÆàÅ«Ü·B
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+ join cat.mate as mate
+ left join cat.kittens as kitten]]></programlisting>
+
+ <para>
+ HQLÌ <literal>with</literal> L[[hðg¤ÆAððt¯Á¦é±ÆªÅ«Ü·B
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+ left join cat.kittens as kitten
+ with kitten.bodyWeight > 10.0]]></programlisting>
+
+ <para>
+ Á¦ÄAutFb`vÍÖAâlÌRNVðeIuWFNgÆêÉ1xÌselectåÅú»µÜ·B
+ ±êÍÁÉRNVÌêÉLpÅ·B±êÍÀ¿ãAÖAÆRNVÉηé}bsOè`t@CÌOÆlazyú»Ìè`ðã«·é±ÆÉÈèÜ·B
+
+ <xref linkend="performance-fetching"/> Éæè½Ìîñª èÜ·B
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+ inner join fetch cat.mate
+ left join fetch cat.kittens]]></programlisting>
+
+ <para>
+ ÉæétFb`ÍÖA·éIuWFNgª <literal>where</literal> ß(ܽͼÌÇñÈßÅà)
+ ÅgíêÄÍÈçÈ¢ÌÅAÊíʼðèÄéKvª èܹñBܽÖAIuWFNgÍâ¢í¹ÊƵÄ
+ ¼ÚÔ³êܹñBãíèÉeIuWFNgðʵÄANZXÅ«Ü·B
+ RNVðÄAIÉtFb`·éêÌÝAʼªKvÉÈèÜ·B
+ </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>fetch</literal> \¶Í <literal>iterate()</literal>
+ ðgÁ½NGÄÑoµÅgpÅ«È¢±ÆÉӵľ³¢
+ iêûÅ <literal>scroll()</literal> ÍgpÅ«Ü·jB
+
+ ܽA±êçÌìÍÊÌsÉîâĢé½ßA
+ <literal>fetch</literal> Í <literal>setMaxResults()</literal> â <literal>setFirstResult()</literal>
+ ÆêÉgp·×«ÅÍ èܹñB
+ ÊíeagerÈRNVtFb`ð·éÆd¡ªoĵܤ½ßA ȽªúÒ·éæ¤ÈsÉÍÈçÈ¢ÌÅ·B
+
+ »µÄܽ <literal>fetch</literal> ÍAAhzbNÈ <literal>with</literal> ðð
+ êÉg¤±ÆàūܹñB
+ êÂÌNGÅ¡ÌRNVðtFb`·é±ÆÉæè¼Ïðì¬Å«éÌÅA±Ìêӵľ³¢B
+ ܽA¡ÌRNVÉηétFb`Íbag}bsOÉεÄ\ú¹ÊÊðà½ç·±Æª éÌÅA
+ ±ÌêÌNGÌì¬ÉÍӵľ³¢B
+ ÅãÉ <literal>SOÉæétFb`</literal> Æ
+ <literal>EOÉæétFb`</literal> ÍLpÅÍÈ¢±ÆÉӵľ³¢B
+ </para>
+
+ <para>
+ àµvpeBxÌxtFb`ðg¤êiàIÉoCgR[hð·éêjA
+ <literal>fetch all properties</literal> ðg¤±ÆÅ
+ HibernateÉxvpeBð¬â©ÉiÅÌNGÅjtFb`³¹é±ÆªÅ«Ü·B
+ </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>
+
+ </sect1>
+
+ <sect1 id="queryhql-joins-forms">
+ <title>\¶Ì`®</title>
+
+ <para>
+ HQLÍQÂÌÖA`®ðT|[gµÜ·F<literal>ÃÙI</literal> Æ <literal>¾¦I</literal>B
+ </para>
+
+ <para>
+ ±êÜÅÌZNVŨ©¹µ½g¢ûÍ·×Ä <literal>¾¦IÈ</literal> `®ÅA
+ fromßž¦IÉjoinL[[hðgÁĢܷB
+ ±Ì`®ð¨··ßµÜ·B
+ </para>
+
+ <para>
+ <literal>ÃÙI</literal> tH[ÍAjoinL[[hðg¢Ü¹ñBãíèÉAQÆ·éÖAÉ
+ hbg\Lðg¢Ü·B<literal>ÃÙI</literal> ÍA³Ü´ÜÈHQLÉoÄ«Ü·B
+ <literal>ÃÙI</literal> ÌÊÍASQLXe[ggÌàÊÅ·B
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat where cat.mate.name like '%s%']]></programlisting>
+ </sect1>
+
+ <sect1 id="queryhql-select">
+ <title>Selectß</title>
+
+ <para>
+ <literal>select</literal> ßÍȺÌæ¤ÉÇÌIuWFNgÆ®«ðNGUgZbgÉÔ·©ðIðµÜ·B:
+ </para>
+
+ <programlisting><![CDATA[select mate
+from Cat as cat
+ inner join cat.mate as mate]]></programlisting>
+
+ <para>
+ ãLÌNGÍ¼Ì <literal>Cat</literal> Ì <literal>mate</literal> ðIðµÜ·B
+ ÀÛÉÍÌæ¤ÉAæèÈÉ\»Å«Ü·B:
+ </para>
+
+ <programlisting><![CDATA[select cat.mate from Cat cat]]></programlisting>
+
+ <para>
+ NGÍR|[lg^ÌvpeBðÜÞA çäél^ÌvpeBàԹܷB:
+ </para>
+
+ <programlisting><![CDATA[select cat.name from DomesticCat cat
+where cat.name like 'fri%']]></programlisting>
+
+ <programlisting><![CDATA[select cust.name.firstName from Customer as cust]]></programlisting>
+
+ <para>
+ NGÍ¡ÌIuWFNgÆ(ܽÍ)vpeBð <literal>Object[]</literal> ^ÌzñƵÄԹܷB
+ </para>
+
+ <programlisting><![CDATA[select mother, offspr, mate.name
+from DomesticCat as mother
+ inner join mother.mate as mate
+ left outer join mother.kittens as offspr]]></programlisting>
+
+ <para>
+ àµÍ <literal>List</literal> ƵÄA
+ </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>
+ ܽÍA^CvZ[tÈJavaIuWFNgðԹܷB
+ </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>
+ é¢Í <literal>Family</literal> NXªKØÈRXgN^ðÁÄ¢éÆ·éÈçÎA
+ </para>
+
+ <para>
+ selectßÉ <literal>as</literal> ðgÁÄʼð¯é±ÆàÅ«Ü·B
+ </para>
+
+ <programlisting><![CDATA[select max(bodyWeight) as max, min(bodyWeight) as min, count(*) as n
+from Cat cat]]></programlisting>
+
+ <para>
+ <literal>select new map</literal> ÆêÉg¤Æ«ÉÅàð§¿Ü·F
+ </para>
+
+ <programlisting><![CDATA[select new map( max(bodyWeight) as max, min(bodyWeight) as min, count(*) as n )
+from Cat cat]]></programlisting>
+
+ <para>
+ ±ÌNGÍʼ©çselectµ½lÖ <literal>Map</literal> ðԵܷB
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-aggregation">
+ <title>WñÖ</title>
+
+ <para>
+ HQLÌNGÍvpeBÌWñÖÌÊàԹܷF
+ </para>
+
+ <programlisting><![CDATA[select avg(cat.weight), sum(cat.weight), max(cat.weight), count(cat)
+from Cat cat]]></programlisting>
+
+<!-- NO LONGER SUPPORTED
+ <para>
+ Collections may also appear inside aggregate functions in the <literal>select</literal>
+ clause.
+ </para>
+
+ <programlisting><![CDATA[select cat, count( elements(cat.kittens) )
+from Cat cat group by cat]]></programlisting>
+-->
+
+ <para>
+ T|[gµÄ¢éWñÖÍȺÌàÌÅ·B
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>avg(...), sum(...), min(...), max(...)</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>count(*)</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>count(...), count(distinct ...), count(all...)</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ selectßɨ¢ÄZpìAAƳF³ê½SQLÖðg¤±ÆªÅ«Ü·B
+ </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>
+ SQLƯ¶Ó¡ð <literal>distinct</literal> Æ <literal>all</literal> L[[hðg¤±ÆªÅ«Ü·B
+ </para>
+
+ <programlisting><![CDATA[select distinct cat.name from Cat cat
+
+select count(distinct cat.name), count(cat) from Cat cat]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="queryhql-polymorphism">
+ <title>|[tBbNÈNG</title>
+
+ <para>
+ Ìæ¤ÈNGF
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat]]></programlisting>
+
+ <para>
+ <literal>Cat</literal> CX^X¾¯ÅÍÈA<literal>DomesticCat</literal>
+ Ìæ¤ÈTuNXàÔ³êÜ·BHibernateNGÍ <emphasis>ÇñÈ</emphasis>
+ JavaNXâC^[tFCXà <literal>from</literal> ßÉüêé±ÆªÅ«Ü·B
+ NGÍ»ÌNXðg£µ½AàµÍC^[tFCXðÀµ½SÄÌi±NXðԵܷB
+ ÌNGÍi±IuWFNgð·×ÄԵܷ:
+ </para>
+
+ <programlisting><![CDATA[from java.lang.Object o]]></programlisting>
+
+ <para>
+ <literal>Named</literal> C^[tFCXÍlXÈi±NXÉæÁÄÀ³êÜ·B:
+ </para>
+
+ <programlisting><![CDATA[from Named n, Named m where n.name = m.name]]></programlisting>
+
+ <para>
+ ÅãÌñÂÌNGÍAñÂÈãÌSQL <literal>SELECT</literal> ðvµÄ¢é±ÆÉӵľ³¢B
+ ±Ì±ÆÍ <literal>order by</literal> ߪUgZbgSÌð³mÉÍ®ñµÈ¢±ÆðÓ¡µÜ·
+ (³çÉ»êÍA<literal>Query.scroll()</literal> ðgpµÄ±êçÌNGðÄԱƪūȢ±ÆðÓ¡µÜ·B)B
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-where">
+ <title>whereß</title>
+
+ <para>
+ <literal>where</literal> ßÍÔ³êéCX^XÌXgðié±ÆªÅ«Ü·B
+ àµÊ¼ªÈ¢êA¼OÅvpeBðQƵܷB
+ </para>
+
+ <programlisting><![CDATA[from Cat where name='Fritz']]></programlisting>
+
+ <para>
+ àµÊ¼ª éêACü¼ðgÁľ³¢B
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat where cat.name='Fritz']]></programlisting>
+
+ <para>
+ ¼Oª'Fritz'Æ¢¤ <literal>Cat</literal> ÌCX^XðԵܷB
+ </para>
+
+ <programlisting><![CDATA[select foo
+from Foo foo, Bar bar
+where foo.startDate = bar.date]]></programlisting>
+
+ <para>
+ ãÌHQLÍA<literal>Foo</literal> Ì <literal>startDate</literal> vpeBƵ¢
+ <literal>date</literal> vpeBðÁ½ <literal>bar</literal> CX^Xª¶Ý·éA
+ ·×ÄÌ <literal>Foo</literal> CX^XðԵܷB
+ RpEhpX®iá¦Îucat.mate.namevjÍ <literal>where</literal> ßðñíÉÍɵܷBÚF
+ </para>
+
+ <programlisting><![CDATA[from Cat cat where cat.mate.name is not null]]></programlisting>
+
+ <para>
+ ±ÌNGÍe[uiàjðÂSQLNGÉÏ·³êÜ·B
+ »ÌãíèÉȺÌæ¤ÉÆA
+ </para>
+
+ <programlisting><![CDATA[from Foo foo
+where foo.bar.baz.customer.address.city is not null]]></programlisting>
+
+ <para>
+ àµãÌNGðLqµ½çNGàÉ4ÂÌe[uðKvÆ·éSQLNGÉÏ·³êÜ·B
+ </para>
+
+ <para>
+ <literal>=</literal> ZqÍȺÌæ¤ÉAvpeB¾¯ÅÈCX^Xðär·é½ßÉàgíêÜ·BF
+ </para>
+
+ <programlisting><![CDATA[from Cat cat, Cat rival where cat.mate = rival.mate]]></programlisting>
+
+ <programlisting><![CDATA[select cat, mate
+from Cat cat, Cat mate
+where cat.mate = mate]]></programlisting>
+
+ <para>
+ <literal>id</literal> (¬¶)ÍÁÊÈvpeBÅ èA
+ IuWFNgÌj[NȯÊqðQÆ·é½ßÉgpÅ«Ü·B(³çÉA»ÌvpeB¼ðgpÅ«Ü·B)
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat where cat.id = 123
+
+from Cat as cat where cat.mate.id = 69]]></programlisting>
+
+ <para>
+ 2ÔÚÌNGÍø¦IÅ·Be[uªKv èܹñI
+ </para>
+
+ <para>
+ ܽ¡¯ÊqÌvpeBàgpÅ«Ü·B±±Å <literal>Person</literal> ª
+ <literal>country</literal> Æ <literal>medicareNumber</literal> ©çÈ顯ÊqðÂƼèµÜ·B
+ </para>
+
+ <programlisting><![CDATA[from bank.Person person
+where person.id.country = 'AU'
+ and person.id.medicareNumber = 123456]]></programlisting>
+
+ <programlisting><![CDATA[from bank.Account account
+where account.owner.id.country = 'AU'
+ and account.owner.id.medicareNumber = 123456]]></programlisting>
+
+ <para>
+ à¤êx¾¢Ü·ªA2ÔÚÌNGÉÍe[uªKv èܹñB
+ </para>
+
+ <para>
+ ¯lÉ <literal>class</literal> ÍÁÊÈvpeBÅ èA
+ |[tBbNÈi±»É¨¯éCX^XÌdiscriminatorlÉANZXµÜ·B
+ whereßÉßÜê½JavaÌNX¼Í»ÌdiscriminatorlÉÏ·³êÜ·B
+ </para>
+
+ <programlisting><![CDATA[from Cat cat where cat.class = DomesticCat]]></programlisting>
+
+ <para>
+ ܽR|[lgâ¡[U^(ܽ»ÌR|[lgÌR|[lgÈÇ)ÌvpeBàwèÅ«Ü·B
+
+ µ©µµÄiR|[lgÌvpeBÅÍÈjR|[lg^ÌvpeBÅI¹·épX®ðgíȢž³¢B
+ á¦Îൠ<literal>store.owner</literal> ª <literal>address</literal>
+ R|[lgðÂGeBeBÈçÎȺÌæ¤ÈÊÆÈèÜ·B
+ </para>
+
+ <programlisting><![CDATA[store.owner.address.city // okay
+store.owner.address // error!]]></programlisting>
+
+ <para>
+ "any"^ÍÁÊÈvpeBÅ é <literal>id</literal> Æ <literal>class</literal> ð¿A
+ ȺÌû@Åð\»·é±ÆðÂ\ɵܷ(<literal>AuditLog.item</literal> Í
+ <literal><any></literal> Å}bsO³ê½vpeBÅ·)B
+ </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> ª
+ ãLÌNGÅSÙÈéf[^x[XJÌlðQÆ·éÆ¢¤±ÆÉӵľ³¢B
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-expressions">
+ <title>Expressions ®</title>
+
+ <para>
+ SQLÌ <literal>where</literal> ßÅLq·é±Æªoé®ÌÙÆñÇðHQLÅàLqÅ«Ü·B:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ ZpZqF<literal>+, -, *, /</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ 2ärZqF<literal>=, >=, <=, <>, !=, like</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ _ZqF<literal>and, or, not</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ O[vª¯ð\·ÊF<literal>( )</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>in</literal>,
+ <literal>not in</literal>,
+ <literal>between</literal>,
+ <literal>is null</literal>,
+ <literal>is not null</literal>,
+ <literal>is empty</literal>,
+ <literal>is not empty</literal>,
+ <literal>member of</literal> and
+ <literal>not member of</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ "Vv"Ècase <literal>case ... when ... then ... else ... end</literal>A
+ "TõI"Ècase <literal>case when ... then ... else ... end</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ XgOÌA <literal>...||...</literal> Ü½Í <literal>concat(...,...)</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>current_date()</literal>, <literal>current_time()</literal>,
+ <literal>current_timestamp()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>second(...)</literal>, <literal>minute(...)</literal>,
+ <literal>hour(...)</literal>, <literal>day(...)</literal>,
+ <literal>month(...)</literal>, <literal>year(...)</literal>,
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ EJB-QL 3.0Åè`³êÄ¢éÖâZq: <literal>substring(), trim(),
+ lower(), upper(), length(), locate(), abs(), sqrt(), bit_length()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>coalesce()</literal> Æ <literal>nullif()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ âÔÌlðStringÉRo[g·é <literal>str()</literal>j
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ 2ÔÚÌøªHibernate^̼OÅ é <literal>cast(... as ...)</literal> Æ
+ <literal>extract(... from ...)</literal>B
+ ½¾µgp·éf[^x[XªANSI <literal>cast()</literal> Æ <literal>extract()</literal>
+ ðT|[g·éêÉÀèÜ·B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ µ½CfbNXt«ÌRNVÌʼÉKp³êéHQLÌ
+ <literal>index()</literal> ÖB
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ RNVlÌpX®ðæéHQLÖF <literal>size(), minelement(), maxelement(),
+ minindex(), maxindex()</literal> B
+ <literal>some, all, exists, any, in</literal> ðgÁÄCü·é±ÆªÅ«éÁÊÈ
+ <literal>elements()</literal> Æ <literal>indices</literal> ÖÆêÉg¢Ü·B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>sign()</literal>, <literal>trunc()</literal>, <literal>rtrim()</literal>, <literal>sin()</literal>
+ Ìæ¤Èf[^x[XªT|[g·éSQLXJÖB
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ JDBCX^CÌÊup[^ <literal>?</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ¼Ot«p[^: <literal>:name</literal>, <literal>:start_date</literal>, <literal>:x1</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ SQLeF<literal>'foo'</literal>, <literal>69</literal>, <literal>6.66E+2</literal>,
+ <literal>'1970-01-01 10:00:01.0'</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ JavaÌ <literal>public static final</literal> èF<literal>eg.Color.TABBY</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ <literal>in</literal> Æ <literal>between</literal> ÍȺÌæ¤ÉgpÅ«Ü·B:
+ </para>
+
+ <programlisting><![CDATA[from DomesticCat cat where cat.name between 'A' and 'B']]></programlisting>
+
+ <programlisting><![CDATA[from DomesticCat cat where cat.name in ( 'Foo', 'Bar', 'Baz' )]]></programlisting>
+
+ <para>
+ ܽAÛè`ÅLq·é±ÆàÅ«Ü·B
+ </para>
+
+ <programlisting><![CDATA[from DomesticCat cat where cat.name not between 'A' and 'B']]></programlisting>
+
+ <programlisting><![CDATA[from DomesticCat cat where cat.name not in ( 'Foo', 'Bar', 'Baz' )]]></programlisting>
+
+ <para>
+ ¯lÉ <literal>is null</literal> â <literal>is not null</literal> ÍnulllðeXg·é½ßÉgpÅ«Ü·B
+ </para>
+
+ <para>
+ HibernateÝèt@CÅHQL query substitutionsðè`·êÎAbooleanlð®ÌÅÈPÉgpÅ«ÜBF
+ </para>
+
+ <programlisting><![CDATA[<property name="hibernate.query.substitutions">true 1, false 0</property>]]></programlisting>
+
+ <para>
+ ±¤·é±ÆźLÌHQLðSQLÉÏ··éÆ«É <literal>true</literal> ,
+ <literal>false</literal> L[[hÍ <literal>1</literal> , <literal>0</literal> Éu«·¦çêÜ·B:
+ </para>
+
+ <programlisting><![CDATA[from Cat cat where cat.alive = true]]></programlisting>
+
+ <para>
+ ÁÊÈvpeB <literal>size</literal>AܽÍÁÊÈÖ <literal>size()</literal>
+ ðgÁÄRNVÌTCYðeXgÅ«Ü·B:
+ </para>
+
+ <programlisting><![CDATA[from Cat cat where cat.kittens.size > 0]]></programlisting>
+
+ <programlisting><![CDATA[from Cat cat where size(cat.kittens) > 0]]></programlisting>
+
+ <para>
+ CfbNXt«ÌRNVÅÍA<literal>minindex</literal> Æ <literal>maxindex</literal>
+ ÖðgÁÄACfbNXÌŬlÆÅålðQÆÅ«Ü·B
+ ¯lÉA<literal>minelement</literal> Æ <literal>maxelement</literal> ðgÁÄA
+ î{^ÌRNVvfÌŬlÆÅålðQÆÅ«Ü·B
+ </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>
+ RNVÌvfâCfbNXÌZbgi<literal>elements</literal> Æ <literal>indices</literal>
+ ÖjAܽÍâ¢í¹iãqjÌʪó¯æêéÆ«ÍA
+ SQLÖ <literal>any, some, all, exists, in</literal> ªT|[g³êÜ·B
+ </para>
+
+ <programlisting><![CDATA[select mother from Cat as mother, Cat as kit
+where kit in elements(foo.kittens)]]></programlisting>
+
+ <programlisting><![CDATA[select p from NameList list, Person p
+where p.name = some elements(list.names)]]></programlisting>
+
+ <programlisting><![CDATA[from Cat cat where exists elements(cat.kittens)]]></programlisting>
+
+ <programlisting><![CDATA[from Player p where 3 > all elements(p.scores)]]></programlisting>
+
+ <programlisting><![CDATA[from Show show where 'fizard' in indices(show.acts)]]></programlisting>
+
+ <para>
+ <literal>size</literal>, <literal>elements</literal>, <literal>indices</literal>,
+ <literal>minindex</literal>, <literal>maxindex</literal>, <literal>minelement</literal>,
+ <literal>maxelement</literal> ÍHibernate3Ìwhereß¾¯ÅpÂ\Å é±ÆÉӵľ³¢B
+ </para>
+
+ <para>
+ CfbNXt«ÌRNViarrays, lists, mapsjÌvfÍA
+ CfbNXÅQÆÅ«Ü·iwhereßàÅÌÝjB
+ </para>
+
+ <programlisting><![CDATA[from Order order where order.items[0].id = 1234]]></programlisting>
+
+ <programlisting><![CDATA[select person from Person person, Calendar calendar
+where calendar.holidays['national day'] = person.birthDay
+ and person.nationality.calendar = calendar]]></programlisting>
+
+ <programlisting><![CDATA[select item from Item item, Order order
+where order.items[ order.deliveredItemIndices[0] ] = item and order.id = 11]]></programlisting>
+
+ <programlisting><![CDATA[select item from Item item, Order order
+where order.items[ maxindex(order.items) ] = item and order.id = 11]]></programlisting>
+
+ <para>
+ <literal>[]</literal> àÌ®ÍAZp®Åà\¢Ü¹ñB
+ </para>
+
+ <programlisting><![CDATA[select item from Item item, Order order
+where order.items[ size(order.items) - 1 ] = item]]></programlisting>
+
+ <para>
+ êνÖAâlÌRNVÌvfÉεÄÍAHQLÍgÝÝÌ <literal>index()</literal> ÖàpӵĢܷB
+ </para>
+
+ <programlisting><![CDATA[select item, index(item) from Order order
+ join order.items item
+where index(item) < 5]]></programlisting>
+
+ <para>
+ x[XÆÈéf[^x[XªT|[gµÄ¢éXJ[SQLÖªgpÅ«Ü·
+ </para>
+
+ <programlisting><![CDATA[from DomesticCat cat where upper(cat.name) like 'FRI%']]></programlisting>
+
+ <para>
+ àµÜ¾SÄððµÄ¢È¢ÈçAºÌNGðSQLÅÇ꾯·AÇÝÃçoé©l¦Ä¾³¢B:
+
+ </para>
+
+ <programlisting><![CDATA[select cust
+from Product prod,
+ Store store
+ inner join store.customers cust
+where prod.name = 'widget'
+ and store.location.name in ( 'Melbourne', 'Sydney' )
+ and prod = all elements(cust.currentOrder.lineItems)]]></programlisting>
+
+ <para>
+ <emphasis>qg:</emphasis> á¦Î±Ìæ¤ÉoÜ·B
+ </para>
+
+ <programlisting><![CDATA[SELECT cust.name, cust.address, cust.phone, cust.id, cust.current_order
+FROM customers cust,
+ stores store,
+ locations loc,
+ store_customers sc,
+ product prod
+WHERE prod.name = 'widget'
+ AND store.loc_id = loc.id
+ AND loc.name IN ( 'Melbourne', 'Sydney' )
+ AND sc.store_id = store.id
+ AND sc.cust_id = cust.id
+ AND prod.id = ALL(
+ SELECT item.prod_id
+ FROM line_items item, orders o
+ WHERE item.order_id = o.id
+ AND cust.current_order = o.id
+ )]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="queryhql-ordering">
+ <title>order byß</title>
+
+ <para>
+ NGªÔ·listÍAÔ³êéNXâR|[lgÌCÓÌ®«ÉæÁÄÀ×Ö¦çêÜ·BF
+ </para>
+
+ <programlisting><![CDATA[from DomesticCat cat
+order by cat.name asc, cat.weight desc, cat.birthdate]]></programlisting>
+
+ <para>
+ IvVÌ <literal>asc</literal> Æ <literal>desc</literal> Í»ê¼ê¸©~Ì®ñð¦µÜ·B
+ </para>
+ </sect1>
+
+ <sect1 id="queryhql-grouping">
+ <title>group byß</title>
+
+ <para>
+ WñlðÔ·NGÍAÔ³êéNXâR|[lgÌCÓÌvpeBÉæÁÄO[v»Å«Ü·B:
+ </para>
+
+ <programlisting><![CDATA[select cat.color, sum(cat.weight), count(cat)
+from Cat cat
+group by cat.color]]></programlisting>
+
+ <programlisting><![CDATA[select foo.id, avg(name), max(name)
+from Foo foo join foo.names name
+group by foo.id]]></programlisting>
+
+ <para>
+ <literal>having</literal> ßàg¦Ü·B
+ </para>
+
+ <programlisting><![CDATA[select cat.color, sum(cat.weight), count(cat)
+from Cat cat
+group by cat.color
+having cat.color in (eg.Color.TABBY, eg.Color.BLACK)]]></programlisting>
+
+ <para>
+ àµgp·éf[^x[XªT|[gµÄ¢éÈçA
+ <literal>having</literal> Æ <literal>order by</literal> ßÅSQLÖÆWñÖªg¦Ü·
+ iá¦ÎMySQLÉÍ èܹñjB
+ </para>
+
+ <programlisting><![CDATA[select cat
+from Cat cat
+ join cat.kittens kitten
+group by cat
+having avg(kitten.weight) > 100
+order by count(kitten) asc, sum(kitten.weight) desc]]></programlisting>
+
+ <para>
+ <literal>group by</literal> ßâ <literal>order by</literal> ßÉ
+ Zp®ðÜޱƪūȢ±ÆÉӵľ³¢B
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-subqueries" revision="2">
+ <title>â¢í¹</title>
+
+ <para>
+ TuZNgðT|[g·éf[^x[X̽ßAHibernateÍâ¢í¹ðT|[gµÄ¢Ü·B
+ â¢í¹ÍÊÅÍÜȯêÎÈèܹñiSQLÌWñÖÄÑoµÉæ骽¢Å·jB
+ ÖAâ¢í¹(ONGÌʼðQÆ·éâ¢í¹Ì±Æ)³¦Â³êÜ·B
+ </para>
+
+ <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>
+
+ <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>
+
+ <programlisting><![CDATA[select cat.id, (select max(kit.weight) from cat.kitten kit)
+from Cat as cat]]></programlisting>
+
+ <para>
+ HQLâ¢í¹ÍAselectܽÍwhereß¾¯Ågíêé±ÆÉӵľ³¢B
+ </para>
+
+ <para>
+ selectXgÉ¡Ì®ðÂâ¢í¹ÉÍA^vðg¤±ÆªÅ«Ü·B
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+where not ( cat.name, cat.color ) in (
+ select cat.name, cat.color from DomesticCat cat
+)]]></programlisting>
+
+ <para>
+ ¢Â©Ìf[^x[XiOracleâHSQLÉÍ èܹñjÅÍA
+ ¼ÌReLXgÅà^vªg¦Ü·B
+ á¦ÎANGR|[lgâ¡[U^ɨ¢ÄÅ·B
+ </para>
+
+ <programlisting><![CDATA[from Person where name = ('Gavin', 'A', 'King')]]></programlisting>
+
+ <para>
+ ¯Å·ªAæèç·ÈNGÅ·F
+ </para>
+
+ <programlisting><![CDATA[from Person where name.first = 'Gavin' and name.initial = 'A' and name.last = 'King')]]></programlisting>
+
+ <para>
+ ±Ìæ¤È±Æðµ½È¢ÌÉÍ2ÂÌRª èÜ·F
+ 1ÂÚÍAf[^x[XvbgtH[ÔÅ®SÈÝ·«ÍÈ¢©çÅ·B
+ 2ÂÚÍANGª}bsOhL
gÌvpeBÌÉ˶·é©çÅ·B
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-examples">
+ <title>HQLÌá</title>
+
+ <para>
+ HibernateNGÍñíÉÍÅ¡GÉÅ«Ü·BÀÛANG¾êÌÐÍÍHibernateÌåvÈZ[X|CgÌêÂÅ·B
+ ±±ÉÅßÌvWFNgÅgpµ½NGÆñíÉæ½áª èÜ·B
+ ÙÆñÇÌNGͱêçÌáæèÈPÉLqÅ«é±ÆÉӵľ³¢!
+ </para>
+
+ <para>
+ ȺÌNGÍÁèÌÚqÆ^¦çê½Å¬ÌvlÉη颥¢¶Ì¶IDA
+ ¤iÌA¶ÌvðvlÅ®ñµÄԵܷB
+ ¿iðè·éÛA»ÝÌJ^Oðg¢Ü·BÊƵÄÔ³êéSQLNGÍ
+ <literal>ORDER</literal>, <literal>ORDER_LINE</literal>, <literal>PRODUCT</literal>,
+ <literal>CATALOG</literal> ¨æÑ <literal>PRICE</literal> e[uÉε4ÂÌàÆ
+ (ÖAµÈ¢)â¢í¹ð¿Ü·B
+ </para>
+
+ <programlisting><![CDATA[select order.id, sum(price.amount), count(item)
+from Order as order
+ join order.lineItems as item
+ join item.product as product,
+ Catalog as catalog
+ join catalog.prices as price
+where order.paid = false
+ and order.customer = :customer
+ and price.product = product
+ and catalog.effectiveDate < sysdate
+ and catalog.effectiveDate >= all (
+ select cat.effectiveDate
+ from Catalog as cat
+ where cat.effectiveDate < sysdate
+ )
+group by order
+having sum(price.amount) > :minAmount
+order by sum(price.amount) desc]]></programlisting>
+
+ <para>
+ ½ÄåÈNGÈÌŵå¤! iÍâ¢í¹ð Üèg¢Ü¹ñBµ½ªÁÄÌNGÍÀÛÉÍȺÌæ¤ÉÈèÜ·B:
+ </para>
+
+ <programlisting><![CDATA[select order.id, sum(price.amount), count(item)
+from Order as order
+ join order.lineItems as item
+ join item.product as product,
+ Catalog as catalog
+ join catalog.prices as price
+where order.paid = false
+ and order.customer = :customer
+ and price.product = product
+ and catalog = :currentCatalog
+group by order
+having sum(price.amount) > :minAmount
+order by sum(price.amount) desc]]></programlisting>
+
+ <para>
+ ÌNGÍeXe[^XÌx¥¢ð¦Ü·B½¾µ·×ÄÌx¥¢ª»ÝÌpÒÉæé
+ ÅVÌXe[^XÏXÅ é <literal>AWAITING_APPROVAL</literal> Å éêð«Ü·B
+ ±ÌNGÍ2ÂÌàÆ <literal>PAYMENT</literal>, <literal>PAYMENT_STATUS</literal> ¨æÑ
+ <literal>PAYMENT_STATUS_CHANGE</literal> e[uÉηéÖAâ¢í¹ðõ¦½SQLNGÉÏ·³êÜ·B
+ </para>
+
+ <programlisting><![CDATA[select count(payment), status.name
+from Payment as payment
+ join payment.currentStatus as status
+ join payment.statusChanges as statusChange
+where payment.status.name <> PaymentStatus.AWAITING_APPROVAL
+ or (
+ statusChange.timeStamp = (
+ select max(change.timeStamp)
+ from PaymentStatusChange change
+ where change.payment = payment
+ )
+ and statusChange.user <> :currentUser
+ )
+group by status.name, status.sortOrder
+order by status.sortOrder]]></programlisting>
+
+ <para>
+ ൪setÌãíèÉlistÆµÄ <literal>statusChanges</literal> RNVð
+ }bsOµ½ÈçÎAÍé©ÉÈPÉNGðLqÅ«éŵå¤B
+ </para>
+
+ <programlisting><![CDATA[select count(payment), status.name
+from Payment as payment
+ join payment.currentStatus as status
+where payment.status.name <> PaymentStatus.AWAITING_APPROVAL
+ or payment.statusChanges[ maxIndex(payment.statusChanges) ].user <> :currentUser
+group by status.name, status.sortOrder
+order by status.sortOrder]]></programlisting>
+
+ <para>
+ ÌNGÍ»ÝÌ[Uª®·égDÉηéAJEg¨æÑ¢¥¢Ìx¥¢ð
+ ·×ÄÔ·MS SQL Server
+ Ì <literal>isNull()</literal> ÖðgpµÄ¢Ü·B
+ ±ÌNGÍ3ÂÌàÆ1ÂÌO A
+ »µÄ <literal>ACCOUNT</literal>, <literal>PAYMENT</literal>, <literal>PAYMENT_STATUS</literal>,
+ <literal>ACCOUNT_TYPE</literal>, <literal>ORGANIZATION</literal> ¨æÑ
+ <literal>ORG_USER</literal> e[uÉηéâ¢í¹Á½SQLÉÏ·³êÜ·B
+ </para>
+
+ <programlisting><![CDATA[select account, payment
+from Account as account
+ left outer join account.payments as payment
+where :currentUser in elements(account.holder.users)
+ and PaymentStatus.UNPAID = isNull(payment.currentStatus.name, PaymentStatus.UNPAID)
+order by account.type.sortOrder, account.accountNumber, payment.dueDate]]></programlisting>
+
+ <para>
+ ¢Â©Ìf[^x[XÉ¢ÄÍA(ÖA³¹çê½)â¢í¹Ìgpðð¯éKvª éŵå¤B
+ </para>
+
+ <programlisting><![CDATA[select account, payment
+from Account as account
+ join account.holder.users as user
+ left outer join account.payments as payment
+where :currentUser = user
+ and PaymentStatus.UNPAID = isNull(payment.currentStatus.name, PaymentStatus.UNPAID)
+order by account.type.sortOrder, account.accountNumber, payment.dueDate]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="queryhql-bulk" revision="2">
+ <title>åÊÌUPDATEÆDELETE</title>
+
+ <para>
+ HQLÍ¡Í <literal>update</literal> Æ <literal>delete</literal>A<literal>insert ... select ...</literal>
+ Xe[ggðHQLÉüêé±ÆðT|[gµÄ¢Ü·B
+ <xref linkend="batch-direct"/> ÉÚת èÜ·B
+ </para>
+ </sect1>
+
+ <sect1 id="queryhql-tipstricks">
+ <title>Tips & Tricks</title>
+
+ <para>
+ ÀÛÉÊðÔ³ÈÄàNGÌÊð¦é±ÆªÅ«Ü·B:
+ </para>
+
+ <programlisting><![CDATA[( (Integer) session.iterate("select count(*) from ....").next() ).intValue()]]></programlisting>
+
+ <para>
+ RNVÌTCYÉæèÊðÀ×Ö¦é½ßÉÍȺÌNGðgpµÜ·B:
+ </para>
+
+ <programlisting><![CDATA[select usr.id, usr.name
+from User as usr
+ left join usr.messages as msg
+group by usr.id, usr.name
+order by count(msg)]]></programlisting>
+
+ <para>
+ gpµÄ¢éf[^x[XªTuZNgðT|[g·éêANGÌwhereßÅTCYÉæéIðððÝèÅ«Ü·:
+ </para>
+
+ <programlisting><![CDATA[from User usr where size(usr.messages) >= 1]]></programlisting>
+
+ <para>
+ gpµÄ¢éf[^x[XªTuZNgðT|[gµÈ¢êÍAÌNGðgpµÄ¾³¢:
+ </para>
+
+ <programlisting><![CDATA[select usr.id, usr.name
+from User usr.name
+ join usr.messages msg
+group by usr.id, usr.name
+having count(msg) >= 1]]></programlisting>
+
+ <para>
+ àðµÄ¢é¹¢ÅãÌð@ªmessage̪
+ [Ì <literal>User</literal> ðÔ·±ÆªÅ«È¢ÈçÎAȺÌ`®ªg¦Ü·B
+ </para>
+
+ <programlisting><![CDATA[select usr.id, usr.name
+from User as usr
+ left join usr.messages as msg
+group by usr.id, usr.name
+having count(msg) = 0]]></programlisting>
+
+ <para>
+ JavaBeanÌvpeBÍA¼Ot«ÌNGp[^Éѯé±ÆªoÜ·BF
+ </para>
+
+ <programlisting><![CDATA[Query q = s.createQuery("from foo Foo as foo where foo.name=:name and foo.size=:size");
+q.setProperties(fooBean); // fooBean has getName() and getSize()
+List foos = q.list();]]></programlisting>
+
+ <para>
+ RNVÍtB^t« <literal>Query</literal> C^[tFCXðgp·é±ÆÅy[Wð¯é±ÆªÅ«Ü·B:
+ </para>
+
+ <programlisting><![CDATA[Query q = s.createFilter( collection, "" ); // the trivial filter
+q.setMaxResults(PAGE_SIZE);
+q.setFirstResult(PAGE_SIZE * pageNumber);
+List page = q.list();]]></programlisting>
+
+ <para>
+ RNVÌvfÍNGtB^ðgÁÄAÀ×Ö¦âO[vª¯ªoÜ·B:
+ </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>
+
+ <para>
+ RNVðú»¹¸ÉRNVÌTCYð¾é±ÆªÅ«Ü·B:
+ </para>
+
+ <programlisting><![CDATA[( (Integer) session.iterate("select count(*) from ....").next() ).intValue();]]></programlisting>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/query_sql.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/query_sql.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/query_sql.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,724 @@
+<?xml version="1.0" encoding="Shift_JIS"?>
+<chapter id="querysql" revision="2">
+ <title>lCeBuSQL</title>
+
+ <para>f[^x[XÌlCeBuSQLû¾ðgÁÄNGð\»·é±ÆàÅ«Ü·B
+ NGqgâOracleÌ <literal>CONNECT</literal> L[[hÌæ¤ÉA
+ f[^x[XÆ©Ì@\ðpµ½¢Æ«Ég¦Ü·B
+ SQL/JDBCð¼ÚgpµÄ¢éAvP[V©çHibernateÖÌÚsàeÕɵĢܷB</para>
+
+ <para>Hibernate3ÅÍA¶¬AXVAíAÇÝÝÌæ¤È·×ÄÌ
+ SQLiXgAhvV[WðÜÞjðè«Å«Ü·B</para>
+
+ <sect1 id="querysql-creating" revision="4">
+ <title><literal>SQLQuery</literal> Ìgp</title>
+
+ <para>lCeBuÈSQLNGÌÀsÍ <literal>SQLQuery</literal>
+ C^[tFCXðʵħäµÜ·B
+ <literal>SQLQuery</literal> C^[tFCXÍ
+ <literal>Session.createSQLQuery()</literal> ðÄÑoµÄ澵ܷB
+ ±ÌAPIðgÁÄâ¢í¹·éû@ðȺÅྵܷB</para>
+
+ <sect2>
+ <title>XJ[ÌNG</title>
+
+ <para>Åàî{IÈSQLNGÍXJ[iljÌXgð¾é±ÆÅ·B</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT * FROM CATS").list();
+sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS").list();
+]]></programlisting>
+
+ <para>±êçÍÇ¿çàACATSe[uÌeJÌXJ[lðÜÞ
+ ObjectzñiObject[]jÌXgðԵܷB
+ Ô·XJ[lÌÀÛÌÔÆ^ðè·é½ßÉA
+ HibernateÍResultSetMetadataðgpµÜ·B</para>
+
+ <para><literal>ResultSetMetadata</literal> ðgp·éI[o[wbhðð¯é½ßA
+ àµÍPɽªÔ³ê驾mÉ·é½ßA<literal>addScalar()</literal>
+ ðg¦Ü·B</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT * FROM CATS")
+ .addScalar("ID", Hibernate.LONG)
+ .addScalar("NAME", Hibernate.STRING)
+ .addScalar("BIRTHDATE", Hibernate.DATE)
+]]></programlisting>
+
+ <para>±ÌNGÅwè³êÄ¢éàÌðºLɦµÜ·B</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>SQLNG¶ñ</para>
+ </listitem>
+
+ <listitem>
+ <para>Ô³êéJÆ^</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>±êÍܾObjectzñðԵܷªA
+ <literal>ResultSetMetdata</literal> ðgpµÜ¹ñB
+ ½¾µA»ÌãíèÉîbÉ éUgZbg©ç
+ IDANAMEABIRTHDATE Jð»ê¼ê
+ LongAStringAShort Ƶľ¦IÉ澵ܷB
+ ±êÍRÂÌJðÔ·ÌÝÅ é±ÆàÓ¡µÜ·B
+ ½Æ¦ANGª <literal>*</literal> ðgpµA
+ ñµ½RÂæè½ÌJðÔ¹éƵÄàÅ·B</para>
+
+ <para>XJ[Ì^îñðȱÆàÅ«Ü·B</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT * FROM CATS")
+ .addScalar("ID", Hibernate.LONG)
+ .addScalar("NAME")
+ .addScalar("BIRTHDATE")
+]]></programlisting>
+
+ <para>
+ ±êÍ{¿IÉOƯ¶NGÅ·ªAA
+ NAME Æ BIRTHDATE Ì^ðßé½ßÉ <literal>ResultSetMetaData</literal>
+ ðgpµÜ·BêûAID Ì^;¦IÉwè³êĢܷB
+ </para>
+
+ <para>
+ ResultSetMetaData ©çÔ³êé java.sql.Types ð Hibernate Ì^É
+ }bsO·é±ÆÍADialect ª§äµÜ·B
+ ¾¦³ê½^ª}bsO³êĢȢ©AÊÌ^ªúÒµ½àÌÆÙÈéêA
+ Dialect Ì <literal>registerHibernateType</literal> ðÄÑoµA
+ JX^}CYÅ«Ü·B
+ </para>
+ </sect2>
+
+ <sect2>
+ <title>GeBeBÌNG</title>
+
+ <para>±±ÜÅÌNGÍA·×ÄXJ[lðÔ·àÌŵ½B
+ î{IÉAUgZbg©çu¢ÁHvÌlðԵܷB
+ È~ÅÍA<literal>addEntity()</literal> ÉæèAlCeBuSQLNG©ç
+ GeBeBIuWFNgðæ¾·éû@ð¦µÜ·B</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT * FROM CATS").addEntity(Cat.class);
+sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS").addEntity(Cat.class);
+]]></programlisting>
+
+ <para>±ÌNGÅwè³êÄ¢éàÌðºLɦµÜ·B</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>SQLNG¶ñ</para>
+ </listitem>
+
+ <listitem>
+ <para>NGªÔ·GeBeBÆSQLe[uÌʼ
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>CatªID, NAME, BIRTHDATEÌJðgÁÄNXÉ}bsO³êéêA
+ ãLÌNGÍÇ¿çàAvfªCatGeBeBÅ éXgðԵܷB</para>
+
+ <para>GeBeBðÊÌGeBeBÉ <literal>½Îê</literal> Å}bsOµÄ¢éêÍA
+ lCeBuNGðÀs·éÛÉA±ÌÊÌGeBeBðÔ·±ÆàvµÜ·B
+ ³àȯêÎAf[^x[XÅLÌucolumn not found(Jª©Â©èܹñ)vG[ª¶µÜ·B
+ * \Lðgpµ½ÛÍAÇÁÌJª©®IÉÔ³êÜ·ªA
+ ÌáÌæ¤ÉA<literal>Dog</literal> É <literal>½Îê</literal> Å é±Æð
+ ¾¦·é±Æð½¿ÍDÝÜ·B</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE, DOG_ID FROM CATS").addEntity(Cat.class);
+]]></programlisting>
+
+ <para>±êÉæè cat.getDog() ª³µ@\µÜ·B</para>
+ </sect2>
+
+ <sect2>
+ <title>ÖAÆRNVÌì</title>
+
+ <para>vLVðú»·é½ßÌ]ªÈðð¯é½ßA
+ <literal>Dog</literal> ÌŦūܷB
+ ±êÍ <literal>addJoin()</literal> \bhÉæès¢Ü·B
+ ÖAàµÍRNVÉÅ«Ü·B
+ </para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT c.ID, NAME, BIRTHDATE, DOG_ID, D_ID, D_NAME FROM CATS c, DOGS d WHERE c.DOG_ID = d.D_ID")
+ .addEntity("cat", Cat.class)
+ .addJoin("cat.dog");
+]]></programlisting>
+
+ <para>±ÌáÌÅAÔ³êé <literal>Cat</literal> ÍA
+ f[^x[XÖÌ]ªÈµÅA
+ ®SÉú»³ê½ <literal>dog</literal> vpeBð¿Ü·B
+ ÎÛÌvpeBÖÌpXðwèÅ«éæ¤ÉA
+ ʼiucatvjðÇÁµ½±ÆÉӵľ³¢B
+ RNV̦௶æ¤ÉÅ«Ü·B
+ ½Æ¦ÎA<literal>Cat</literal> ªêνŠ<literal>Dog</literal>
+ ðÁÄ¢½êAÌæ¤ÉÈèÜ·B</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE, D_ID, D_NAME, CAT_ID FROM CATS c, DOGS d WHERE c.ID = d.CAT_ID")
+ .addEntity("cat", Cat.class)
+ .addJoin("cat.dogs");
+]]></programlisting>
+
+ <para>»ÝÌƱëA
+ HibernateÅg¢â··é½ßÌSQLNGÌg£ÈµÉA
+ lCeBuNGŽ©ðÂ\É·éÀEÉĢܷB
+ ¯¶^ÌGeBeBð¡Ô·ÛâAftHgÌʼâñ¼Å\ªÅÍÈ¢êÉA
+ âèÍN±ènßÜ·B
+ </para>
+ </sect2>
+
+ <sect2>
+ <title>¡GeBeBÌæ¾</title>
+
+ <para>±±ÜÅÍAUgZbgÌJ¼ÍA
+ }bsOt@CÅwè³ê½J¼Æ¯¶Å éƼèµÄ¢Üµ½B
+ ¡Ìe[uª¯¶J¼ðÂêª é½ßA
+ ¡e[uð·éSQLNGÅâèÆÈéêª èÜ·B</para>
+
+ <para>ºLÌæ¤Èi¸sµ»¤ÈjNGÅÍA
+ JʼCWFNVicolumn alias injectionjªKvÅ·B</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT c.*, m.* FROM CATS c, CATS m WHERE c.MOTHER_ID = m.ID")
+ .addEntity("cat", Cat.class)
+ .addEntity("mother", Cat.class)
+]]></programlisting>
+
+ <para>±ÌNGÌÓ}ÍA
+ Ps²ÆÉQÂÌCatCX^XAÂÜèLÆ»ÌêeðÔ·Æ¢¤±ÆÅ·B
+ ¯¶J¼É}bsO·é±ÆÉæè¼OªÕË·é½ßA±ÌNG͸sµÜ·B
+ x[^x[XÉæÁÄÍAÔ³êéJÌʼª "c.ID"A"c.NAME" ÈÇÌ`®Å èA
+ }bsOÅwè³ê½Ji"ID" Æ "NAME"jƵȢ½ßA¸sµÜ·B</para>
+
+
+ <para>ºLÌ`®ÍAJ¼ªd¡µÄàåävÅ·B</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT {cat.*}, {mother.*} FROM CATS c, CATS m WHERE c.MOTHER_ID = m.ID")
+ .addEntity("cat", Cat.class)
+ .addEntity("mother", Cat.class)
+]]></programlisting>
+
+ <para>±ÌNGÅwè³êÄ¢éàÌðºLɦµÜ·B</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>SQLNG¶ñ
+ iHibernateªJÌʼð}ü·é½ßÌv[Xz_ðÜÞj</para>
+ </listitem>
+
+ <listitem>
+ <para>NGÉæÁÄÔ³êéGeBeB</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>ãLÅgpµÄ¢é {cat.*} Æ {mother.*} Æ¢¤\LÍA
+ u·×ÄÌvpeBvð\·Èª`Å·B
+ ãíèÉA¾¦IÉJðñµÄàæ¢Å·ªA
+ »ÌêÍAHibernateÉevpeBÉηéSQLJÌʼð}ü³¹é׫ŵå¤B
+ JÌʼ̽ßÌv[Xz_ÍAe[uÌʼÉæÁÄCü³ê½vpeB¼Å·B
+ ºLÌáÅÍAÊÌe[u cat_log ©ç
+ }bsO^f[^Åè`³ê½ Cat Æ»Ìêeð³µÜ·B
+ àµDÞÈçAwhereßÌÅàAvpeBÌʼðg¦Ü·B</para>
+
+ <programlisting><![CDATA[String sql = "SELECT ID as {c.id}, NAME as {c.name}, " +
+ "BIRTHDATE as {c.birthDate}, MOTHER_ID as {c.mother}, {mother.*} " +
+ "FROM CAT_LOG c, CAT_LOG m WHERE {c.mother} = c.ID";
+
+List loggedCats = sess.createSQLQuery(sql)
+ .addEntity("cat", Cat.class)
+ .addEntity("mother", Cat.class).list()
+]]></programlisting>
+
+ <sect3 id="querysql-aliasreferences" revision="2">
+ <title>ʼÆvpeBÌt@X</title>
+
+ <para>½ÌêAãLÌæ¤ÈʼCWFNVªKvÅ·B
+ ½¾µA¡vpeBAp³¯ÊqARNVÈÇÌæ¤È
+ æè¡GÈ}bsOÆÖA·éNGªÈ¯êÎÅ·B
+ éÁèÌʼðgp·é±ÆÉæèAHibernateÍKØÈʼð}üÅ«Ü·B</para>
+
+ <para>ʼCWFNVƵÄgpÅ«éàÌðº\ɦµÜ·B
+ ÓFº\ÌʼÍêáÅ·B
+ »ê¼êÌʼÍêÓÅ èAgp·éÛÉͨ»çÙÈé¼Oð¿Ü·B</para>
+
+ <table frame="topbot" id="aliasinjection-summary">
+ <title>ʼÉ}ü·é¼O</title>
+
+ <tgroup cols="3">
+ <colspec colwidth="1*" />
+
+ <colspec colwidth="1*" />
+
+ <colspec colwidth="2.5*" />
+
+ <thead>
+ <row>
+ <entry>à¾</entry>
+ <entry>\¶</entry>
+ <entry>á</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>PÈvpeB</entry>
+ <entry><literal>{[aliasname].[propertyname]}</literal></entry>
+ <entry><literal>A_NAME as {item.name}</literal></entry>
+ </row>
+ <row>
+ <entry>¡vpeB</entry>
+ <entry><literal>{[aliasname].[componentname].[propertyname]}</literal></entry>
+ <entry><literal>CURRENCY as {item.amount.currency}, VALUE as
+ {item.amount.value}</literal></entry>
+ </row>
+ <row>
+ <entry>GeBeBÌNXð¯Ê·él</entry>
+ <entry><literal>{[aliasname].class}</literal></entry>
+ <entry><literal>DISC as {item.class}</literal></entry>
+ </row>
+ <row>
+ <entry>GeBeBÌSvpeB</entry>
+ <entry><literal>{[aliasname].*}</literal></entry>
+ <entry><literal>{item.*}</literal></entry>
+ </row>
+ <row>
+ <entry>RNVÌL[</entry>
+ <entry><literal>{[aliasname].key}</literal></entry>
+ <entry><literal>ORGID as {coll.key}</literal></entry>
+ </row>
+ <row>
+ <entry>RNVÌID</entry>
+ <entry><literal>{[aliasname].id}</literal></entry>
+ <entry><literal>EMPID as {coll.id}</literal></entry>
+ </row>
+ <row>
+ <entry>RNVÌvf</entry>
+ <entry><literal>{[aliasname].element}</literal></entry>
+ <entry><literal>XID as {coll.element}</literal></entry>
+ </row>
+ <row>
+ <entry>RNVÌvfÌvpeB</entry>
+ <entry><literal>{[aliasname].element.[propertyname]}</literal></entry>
+ <entry><literal>NAME as {coll.element.name}</literal></entry>
+ </row>
+ <row>
+ <entry>RNVÌvfÌSvpeB</entry>
+ <entry><literal>{[aliasname].element.*}</literal></entry>
+ <entry><literal>{coll.element.*}</literal></entry>
+ </row>
+ <row>
+ <entry>RNVÌSvpeB</entry>
+ <entry><literal>{[aliasname].*}</literal></entry>
+ <entry><literal>{coll.*}</literal></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect3>
+ </sect2>
+
+ <sect2>
+ <title>dzêĢȢGeBeBÌæ¾</title>
+
+ <para>lCeBuSQLNGÉ ResultTransformer ðKpÅ«Ü·B
+ ºLÌæ¤ÉAá¦ÎAdzêĢȢGeBeBðԵܷB</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT NAME, BIRTHDATE FROM CATS")
+ .setResultTransformer(Transformers.aliasToBean(CatDTO.class))]]></programlisting>
+
+ <para>±ÌNGÅwè³êÄ¢éàÌðºLɦµÜ·B</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>SQLNG¶ñ</para>
+ </listitem>
+
+ <listitem>
+ <para>ÊðÏ·µ½àÌ</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>ãLÌNGÍACX^X»µANAME Æ BIRTHDATE Ìlð
+ ηévpeBàµÍtB[hÉ}üµ½ <literal>CatDTO</literal>
+ ÌXgðԵܷB</para>
+ </sect2>
+
+ <sect2>
+ <title>p³Ì§ä</title>
+
+ <para>p³ÌêƵÄ}bsO³ê½GeBeBðâ¢í¹élCeBuSQLNGÍA
+ x[XÌNXƻ̷×ÄÌTuNXÌvpeB·×ÄðÜÜȯêÎÈèܹñB</para>
+ </sect2>
+
+ <sect2>
+ <title>p[^</title>
+
+ <para>lCeBuSQLNGÍAȺÌæ¤ÉA
+ ¼Ot«p[^i:namejƯlÉÊup[^ðT|[gµÜ·B</para>
+
+ <programlisting><![CDATA[Query query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME like ?").addEntity(Cat.class);
+List pusList = query.setString(0, "Pus%").list();
+
+query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME like :name").addEntity(Cat.class);
+List pusList = query.setString("name", "Pus%").list(); ]]></programlisting>
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="querysql-namedqueries" revision="3">
+ <title>¼Ot«SQLNG</title>
+
+ <para>¼Ot«SQLNGÍ}bsOhL
gÅè`·é±ÆªÅ«A
+ ¼Ot«HQLNGÆS¯¶û@ÅÄԱƪūܷB
+ ±ÌêA<literal>addEntity()</literal> ðÄÑo·KvÍ <emphasis>È¢</emphasis> Å·B</para>
+
+ <programlisting><![CDATA[<sql-query name="persons">
+ <return alias="person" class="eg.Person"/>
+ SELECT person.NAME AS {person.name},
+ person.AGE AS {person.age},
+ person.SEX AS {person.sex}
+ FROM PERSON person
+ WHERE person.NAME LIKE :namePattern
+</sql-query>]]></programlisting>
+
+ <programlisting><![CDATA[List people = sess.getNamedQuery("persons")
+ .setString("namePattern", namePattern)
+ .setMaxResults(50)
+ .list();]]></programlisting>
+
+ <para>ÖAð·é½ßÆRNVðú»·éNGðè`·é½ßÉA»ê¼ê
+ <literal><return-join></literal> Æ <literal><load-collection></literal>
+ vfðg¢Ü·B</para>
+
+ <programlisting><![CDATA[<sql-query name="personsWith">
+ <return alias="person" class="eg.Person"/>
+ <return-join alias="address" property="person.mailingAddress"/>
+ SELECT person.NAME AS {person.name},
+ person.AGE AS {person.age},
+ person.SEX AS {person.sex},
+ adddress.STREET AS {address.street},
+ adddress.CITY AS {address.city},
+ adddress.STATE AS {address.state},
+ adddress.ZIP AS {address.zip}
+ FROM PERSON person
+ JOIN ADDRESS adddress
+ ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'
+ WHERE person.NAME LIKE :namePattern
+</sql-query>]]></programlisting>
+
+ <para>¼Ot«SQLNGÍXJlðÔ·±ÆàÅ«Ü·B
+ <literal><return-scalar></literal> vfðgÁÄA
+ ñÌʼÆHibernateÌ^ð龵ȯêÎÈèܹñB</para>
+
+ <programlisting><![CDATA[<sql-query name="mySqlQuery">
+ <return-scalar column="name" type="string"/>
+ <return-scalar column="age" type="long"/>
+ SELECT p.NAME AS name,
+ p.AGE AS age,
+ FROM PERSON p WHERE p.NAME LIKE 'Hiber%'
+</sql-query>]]></programlisting>
+
+
+ <para>UgZbgÌ}bsOîñð <literal><resultset></literal>
+ ÉOoµ·é±ÆªÅ«Ü·B
+ ¡Ì¼Ot«NGÅÄpµ½èA<literal>setResultSetMapping()</literal>
+ APIðʵÄÄpµ½èÅ«Ü·B
+ </para>
+
+ <programlisting><![CDATA[<resultset name="personAddress">
+ <return alias="person" class="eg.Person"/>
+ <return-join alias="address" property="person.mailingAddress"/>
+</resultset>
+
+<sql-query name="personsWith" resultset-ref="personAddress">
+ SELECT person.NAME AS {person.name},
+ person.AGE AS {person.age},
+ person.SEX AS {person.sex},
+ adddress.STREET AS {address.street},
+ adddress.CITY AS {address.city},
+ adddress.STATE AS {address.state},
+ adddress.ZIP AS {address.zip}
+ FROM PERSON person
+ JOIN ADDRESS adddress
+ ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'
+ WHERE person.NAME LIKE :namePattern
+</sql-query>]]></programlisting>
+
+ <para>ãíèÉAhbmt@CàÌUgZbgÌ}bsOîñð
+ ¼ÚJavaR[hÌÅgpÅ«Ü·B</para>
+
+ <programlisting><![CDATA[List cats = sess.createSQLQuery(
+ "select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id"
+ )
+ .setResultSetMapping("catAndKitten")
+ .list();]]></programlisting>
+
+ <sect2 id="propertyresults">
+ <title>ñÆñÌʼð¾¦IÉwè·é½ßÉ return-property ðg¤</title>
+
+ <para>ʼð}ü·é½ßÉ <literal>{}</literal> \¶ðg¤ãíèÉA
+ <literal><return-property></literal> ðg¢A
+ ÇÌñÌʼðg¤Ì©ð¾¦Å«Ü·B</para>
+
+ <programlisting><![CDATA[<sql-query name="mySqlQuery">
+ <return alias="person" class="eg.Person">
+ <return-property name="name" column="myName"/>
+ <return-property name="age" column="myAge"/>
+ <return-property name="sex" column="mySex"/>
+ </return>
+ SELECT person.NAME AS myName,
+ person.AGE AS myAge,
+ person.SEX AS mySex,
+ FROM PERSON person WHERE person.NAME LIKE :name
+</sql-query>
+]]></programlisting>
+
+ <para><literal><return-property></literal> Í¡Ìñ൦ܷB
+ ±êÍA¡ñÌvpeBð«ßש§äūȢƢ¤A
+ <literal>{}</literal> \¶Ì§ÀððµÜ·B</para>
+
+ <programlisting><![CDATA[<sql-query name="organizationCurrentEmployments">
+ <return alias="emp" class="Employment">
+ <return-property name="salary">
+ <return-column name="VALUE"/>
+ <return-column name="CURRENCY"/>
+ </return-property>
+ <return-property name="endDate" column="myEndDate"/>
+ </return>
+ SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer},
+ STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},
+ REGIONCODE as {emp.regionCode}, EID AS {emp.id}, VALUE, CURRENCY
+ FROM EMPLOYMENT
+ WHERE EMPLOYER = :id AND ENDDATE IS NULL
+ ORDER BY STARTDATE ASC
+</sql-query>]]></programlisting>
+
+ <para>±ÌáÅÍA}ü̽ßÌ <literal>{}</literal> \¶Æ¢ÁµåÉA
+ <literal><return-property></literal> ðgÁÄ¢é±ÆÉӵľ³¢B
+ ñÆvpeBðÇÌæ¤ÉQÆ·é©ðI×Ü·B</para>
+
+ <para>}bsOÉ discriminator ªÜÜêÄ¢éêA
+ discriminator Ìñðwè·é½ßÉA<return-discriminator>
+ ðgíȯêÎÈèܹñB</para>
+ </sect2>
+
+ <sect2 id="sp_query" revision="1">
+ <title>â¢í¹·é½ßÉXgAhvV[Wðg¤</title>
+
+ <para>HibernateÍo[W3©çAXgAhvV[WÆXgAhÖoRÌâ¢í¹ª
+ T|[g³êܵ½B
+ È~̶̽ÍA¼ûÉÄÍÜèÜ·B
+ XgAhvV[WâXgAhÖðHibernateÅg¤½ßÉÍA
+ 1ÔÚÌoÍp[^ƵÄUgZbgðԳȯêÎÈèܹñB
+ Oracle 9iàµÍ»êÈãÌo[WjÌXgAhvV[WÌáðȺɦµÜ·B</para>
+
+ <programlisting><![CDATA[CREATE OR REPLACE FUNCTION selectAllEmployments
+ RETURN SYS_REFCURSOR
+AS
+ st_cursor SYS_REFCURSOR;
+BEGIN
+ OPEN st_cursor FOR
+ SELECT EMPLOYEE, EMPLOYER,
+ STARTDATE, ENDDATE,
+ REGIONCODE, EID, VALUE, CURRENCY
+ FROM EMPLOYMENT;
+ RETURN st_cursor;
+ END;]]></programlisting>
+
+ <para>HibernateűÌNGðg¤½ßÉÍA
+ ¼Ot«NGÅ}bsO·éKvª èÜ·B</para>
+
+ <programlisting><![CDATA[<sql-query name="selectAllEmployees_SP" callable="true">
+ <return alias="emp" class="Employment">
+ <return-property name="employee" column="EMPLOYEE"/>
+ <return-property name="employer" column="EMPLOYER"/>
+ <return-property name="startDate" column="STARTDATE"/>
+ <return-property name="endDate" column="ENDDATE"/>
+ <return-property name="regionCode" column="REGIONCODE"/>
+ <return-property name="id" column="EID"/>
+ <return-property name="salary">
+ <return-column name="VALUE"/>
+ <return-column name="CURRENCY"/>
+ </return-property>
+ </return>
+ { ? = call selectAllEmployments() }
+</sql-query>]]></programlisting>
+
+ <para>ÓF¡ÌƱëAXgAhvV[WÍXJÆGeBeBðÔ·ÌÝÅ·B
+ <literal><return-join></literal> Æ <literal><load-collection></literal>
+ ÍT|[g³êĢܹñB</para>
+
+ <sect3 id="querysql-limits-storedprocedures" revision="1">
+ <title>XgAhvV[Wðg¤ãÅÌ[ƧÀ</title>
+
+ <para>HibernateÅXgAhvV[WâÖðg¤½ßÉÍA
+ »ÌvV[W͢©Ì[É·éKvª èÜ·B
+ [ɵĢȢvV[WÍAHibernateÅg¤±ÆÍūܹñB
+ »êÅàAµÄ¢È¢vV[Wðg¢½¢ÌÅ êÎA
+ <literal>session.connection()</literal> ðʶÄÀsµÈ¯êÎÈèܹñB
+ [Íf[^x[X²ÆÉÙÈèÜ·B
+ XgAhvV[WÌZ}eBbNXÆV^bNXÍA
+ f[^x[Xx_²ÆÉÙÈé½ßÅ·B</para>
+
+ <para><literal>setFirstResult()/setMaxResults()</literal> ðgÁÄA
+ XgAhvV[WNGðy[Wª¯·é±ÆÍūܹñB</para>
+
+ <para>§·éÄÑoµûÍAWÅ éSQL92É]¤±ÆÅ·B
+ <literal>{ ? = call functionName(<parameters>) }</literal> â
+ <literal>{ ? = call procedureName(<parameters>) }</literal> Å·B
+ lCeBuÈÄÑoµ\¶ÍT|[g³êĢܹñB</para>
+
+ <para>OracleÉͺLÌ[ªKp³êÜ·B</para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>ÖÍUgZbgðԳȯêÎÈèܹñB
+ vV[WÌæêøÍUgZbgðÔ·½ßA
+ <literal>OUT</literal> ÅȯêÎÈèܹñB
+ Oracle 9Æ10ÅÍA<literal>SYS_REFCURSOR</literal> ðg¤±ÆÉæÁÄÅ«Ü·B
+ OracleÅÍ <literal>REF CURSOR</literal> ^ðè`·éKvª èÜ·B
+ Oracle̶£ðQƵľ³¢B</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>SybaseÆMS SQLT[o[ÉKp³êé[ðºLɦµÜ·B</para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>vV[WÍUgZbgðԳȯêÎÈèܹñB
+ T[o[Í¡ÌUgZbgÆXVJEgðԵܷªA
+ HibernateÍ1ÂÚÌUgZbg¾¯ðÔ·±ÆÉӵľ³¢B
+ »Ì¼Í·×ÄÌÄçêÜ·B</para>
+ </listitem>
+
+ <listitem>
+ <para>vV[WÌÅ <literal>SET NOCOUNT ON</literal> ðLøÉÅ«êÎA
+ ¨»çø¦ªæÈéŵå¤B
+ µ©µA±êÍKvðÅÍ èܹñB</para>
+ </listitem>
+ </itemizedlist>
+ </sect3>
+ </sect2>
+ </sect1>
+
+ <sect1 id="querysql-cud">
+ <title>ì¬AXVAí̽ßÌJX^SQL</title>
+
+ <para>Hibernate3Íì¬AXVAí̽ßÌJX^SQL¶ðgpÅ«Ü·B
+ NXÆRNVÌi±»@\ÍARtBO[Vɶ¬³ê½¶ñ
+ iinsertsqlAdeletesqlAupdatesqlÈÇjÌZbgð·ÅÉ۵ĢܷB
+ ±êç̶ñæèA
+ <literal><sql-insert></literal>A
+ <literal><sql-delete></literal>A
+ <literal><sql-update></literal> Æ¢¤}bsO^OªDæ³êÜ·B</para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true"/>
+ <sql-insert>INSERT INTO PERSON (NAME, ID) VALUES ( UPPER(?), ? )</sql-insert>
+ <sql-update>UPDATE PERSON SET NAME=UPPER(?) WHERE ID=?</sql-update>
+ <sql-delete>DELETE FROM PERSON WHERE ID=?</sql-delete>
+</class>]]></programlisting>
+
+ <para>SQLð¼Úf[^x[XÅÀs·é½ßADÝÌû¾ð©RÉgpÅ«Ü·B
+ f[^x[XÆ©ÌSQLðg¦ÎAR}bsOÌ|[^reBªºªèÜ·B</para>
+
+ <para><literal>callable</literal> ®«ðZbg·êÎA
+ XgAhvV[WðgpÅ«Ü·B</para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true"/>
+ <sql-insert callable="true">{call createPerson (?, ?)}</sql-insert>
+ <sql-delete callable="true">{? = call deletePerson (?)}</sql-delete>
+ <sql-update callable="true">{? = call updatePerson (?, ?)}</sql-update>
+</class>]]></programlisting>
+
+ <para>¡ÌƱëAÊup[^ÌÔÍÆÄàdvÅ·B
+ ·Èí¿AHibernateªúÒ·éÅȯêÎÈèܹñB</para>
+
+ <para><literal>org.hiberante.persister.entity</literal> xÌfobOOð
+ LøÉ·é±ÆÉæÁÄAúÒ³êéÔðm©ßçêÜ·B
+ ±ÌxðLøÉ·é±ÆÉæèAGeBeBÌì¬AXVAíÈÇÅ
+ gp³êéÃIÈSQLªoͳêÜ·B
+ iúÒ³êéðmF·é½ßÉÍAHibernateª¶¬·éÃIÈSQLðI[o[Ch·é
+ JX^SQLð}bsOt@CÉÜßÈ¢±ÆðYêȢž³¢Bj</para>
+
+ <para>XgAhvV[WÍ}ü/XV/í³ê½sðÔ·Kvª èÜ·
+ iÇÝÝÌêÍAԳȢæèÍÔ·ûªæ¢Å·jB
+ ÀsÉHibernateªSQL¶Ì¬÷ð`FbN·é©çÅ·B
+ HibernateÍACUD̽ßÌlÌoÍp[^ƵÄA
+ SQL¶ÌÅÌp[^ð¢ÂàL^µÜ·B</para>
+
+ <programlisting><![CDATA[CREATE OR REPLACE FUNCTION updatePerson (uid IN NUMBER, uname IN VARCHAR2)
+ RETURN NUMBER IS
+BEGIN
+
+ update PERSON
+ set
+ NAME = uname,
+ where
+ ID = uid;
+
+ return SQL%ROWCOUNT;
+
+END updatePerson;]]></programlisting>
+ </sect1>
+
+ <sect1 id="querysql-load">
+ <title>[h̽ßÌJX^SQL</title>
+
+ <para>GeBeBðÇÝÞ½ßÌÆ©ÌSQLiàµÍHQLjNGàé¾Å«Ü·B</para>
+
+ <programlisting><![CDATA[<sql-query name="person">
+ <return alias="pers" class="Person" lock-mode="upgrade"/>
+ SELECT NAME AS {pers.name}, ID AS {pers.id}
+ FROM PERSON
+ WHERE ID=?
+ FOR UPDATE
+</sql-query>]]></programlisting>
+
+ <para>±êÍAܳÉiÈOc_µ½j¼Ot«NGÌé¾Å·B
+ ±Ì¼Ot«NGðNXÌ}bsO©çQÆÅ«Ü·B</para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true"/>
+ <loader query-ref="person"/>
+</class>]]></programlisting>
+
+
+ <para>±êÍXgAhvV[Wų¦à®ìµÜ·B</para>
+
+ <para>Ìæ¤ÉARNVð[h·é½ßÌNG³¦è`µÄæ¢Å·B</para>
+
+ <programlisting><![CDATA[<set name="employments" inverse="true">
+ <key/>
+ <one-to-many class="Employment"/>
+ <loader query-ref="employments"/>
+</set>]]></programlisting>
+
+ <programlisting><![CDATA[<sql-query name="employments">
+ <load-collection alias="emp" role="Person.employments"/>
+ SELECT {emp.*}
+ FROM EMPLOYMENT emp
+ WHERE EMPLOYER = :id
+ ORDER BY STARTDATE ASC, EMPLOYEE ASC
+</sql-query>]]></programlisting>
+
+ <para>Ìæ¤ÉAtFb`ÉæèRNVð[h·é
+ GeBeB[_[ðè`Å«Ü·B</para>
+
+ <programlisting><![CDATA[<sql-query name="person">
+ <return alias="pers" class="Person"/>
+ <return-join alias="emp" property="pers.employments"/>
+ SELECT NAME AS {pers.*}, {emp.*}
+ FROM PERSON pers
+ LEFT OUTER JOIN EMPLOYMENT emp
+ ON pers.ID = emp.PERSON_ID
+ WHERE ID=?
+</sql-query>]]></programlisting>
+ </sect1>
+
+</chapter>
\ No newline at end of file
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/session_api.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/session_api.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/session_api.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1125 @@
+<?xml version="1.0" encoding="Shift_JIS"?>
+<chapter id="objectstate">
+ <title>IuWFNgðµ¤</title>
+
+ <para>
+ Hibernate Í®SÈIuWFNg/[Vi}bsO\
[VÅ èA
+ f[^x[XÇVXeÌÚ×ðJÒ©çBÁ·é¾¯ÅÈA IuWFNgÌ <emphasis>óÔÇ</emphasis> às¢Ü·B
+ ±êÍAJDBC/SQLi±wƯ¶æ¤ÈSQL <literal>¶</literal> ÌÇÆÍÙÈèA
+ JavaAvP[Vɨ¯éi±»ÉηéAÆÄà©RÈIuWFNgwüÌl¦ûðñµÜ·B
+ </para>
+
+ <para>
+ ¾¢©¦êÎAHibernateðp¢éAvP[VJÒÍAIuWFNgÌ <emphasis>óÔ</emphasis> É¢ÄÍ
+ íÉÓ¯·×«Å èASQL¶ÌÀsÉ¢ÄÍK¸µà»¤ÅÍ èܹñB
+ ±ÌªÍAÊíAHibernateªµAVXeÌptH[}Xð`
[jO·éƫɾ¯A âèÉÈÁÄ«Ü·B
+ </para>
+
+ <sect1 id="objectstate-overview">
+ <title>Hibernateɨ¯éIuWFNgÌóÔ</title>
+
+ <para>
+ HibernateÍÌæ¤ÈIuWFNgÌóÔðè`µAT|[gµÄ¢Ü·B
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis> êI(Transient) </emphasis> - <literal>new</literal>
+ ZqðgÁÄ CX^X»³ê½¾¯ÅA HibernateÌ <literal>Session</literal>
+ ÉÖAt¯çêĢȢIuWFNgÍA êI(transient)Å·B
+ »êÍAf[^x[XÉi±IÈ\»ð½¸A¯ÊqÆÈélÍèÄçêĢܹñB
+ êIÈCX^XÍAAvP[Vª»ÌQÆðDZÉà۵ȢêÉA Kx[WRN^ÉæÁÄjü³êÜ·B
+ IuWFNgði±I(persistent)ÈóÔÉ·é½ßÉÍAHibernateÌ
+ <literal>Session</literal> ðg¢Üµå¤B
+ i±ÌóÔJÚÉKvÆÈéSQL¶ÌsÍAHibernateÉC¹Üµå¤Bj
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>i±I(Persistent)</emphasis> -
+ i±IÈCX^XÍf[^x[XÉ i±IÈ\»ð¿A¯ÊqÆÈélðÁĢܷB
+ »êÍAZ[u³ê½èA[h³ê½è·é©àµêܹñªA
+ è`ãÍA <literal>Session</literal> ÌXR[vÌɶݵĢܷB
+ HibernateÍAìÆPÊiUnit of workjª®¹µ½Æ«ÉA i±óÔÌIuWFNgÉÁ¦çê½ÏXðoµA
+ IuWFNgÌóÔÆf[^x[Xð¯úµÜ·B IuWFNgðêI(transient)É·éÆ«ÍAJÒÍAèìÆÅ
+ <literal>UPDATE</literal> ¶â <literal>DELETE</literal>
+ ¶ðÀsµÜ¹ñB
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>ª£(Detached)</emphasis> - ª£³ê½CX^XÆÍAi±»³êÄ¢éªA
+ »êÆÖAt¢Ä¢½ <literal>Session</literal> ªN[Y³êÄ¢éIuWFNg̱ÆÅ·B
+ »ÌIuWFNgÖÌQÆÍAËRƵÄLøÅ·B
+ »µÄAà¿ëñAª£³ê½óÔÉ éIuWFNgÍAC³·é±Æ³¦Å«Ü·B
+ ª£³ê½CX^XÍAà¤êxi±»µ½¢i»µÄA·×ÄÌÏXði±»µ½¢jÆ«ÉA Vµ¢
+ <literal>Session</literal> ÉÄÇÁÅ«Ü·B
+ ±Ì@\ÍA[Uªl¦éÔðKvÆ·éæ¤ÈA·úÔÉyÔìÆPÊÉηé vO~OfðÂ\ɵܷB
+ äXÍA±êð <emphasis>AvP[VÌgUNViapplication
+ transactionsj</emphasis> ÆÄñŢܷB ·Èí¿A[U©ç©½ìÆPʾƢ¤±ÆÅ·B
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ±ê©çAóÔÆóÔJÚi»µÄAJÚÌ«Á©¯ÆÈéHibernateÌ\bhjÉ墀 AÚ×Éq×Ü·B
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-makingpersistent" revision="1">
+ <title>IuWFNgði±óÔÉ·é</title>
+
+ <para>
+ VµCX^X»³ê½i±NXÌCX^XÍA HibernateÅÍ
+ <emphasis>êI(transient)</emphasis> ƩȳêÜ·B
+ ȺÌæ¤ÉAZbVÆÖAïé±ÆÅAêIÈCX^Xð
+ <emphasis>i±óÔ(persistent)</emphasis> ÉÅ«Ü·B
+ </para>
+
+
+ <programlisting><![CDATA[DomesticCat fritz = new DomesticCat();
+fritz.setColor(Color.GINGER);
+fritz.setSex('M');
+fritz.setName("Fritz");
+Long generatedId = (Long) sess.save(fritz);]]></programlisting>
+
+ <para>
+ <literal>Cat</literal> NX̯Êqª©®¶¬³êéÌÅ êÎA
+ <literal>save()</literal> ªÄÎêéÆ«ÉA ¯Êqª¶¬³êA <literal>cat</literal>
+ CX^XÉèÄçêÜ·B <literal>Cat</literal>
+ ̯Êqª¼©çèÄçêéi <literal>assigned</literal> ¯ÊqðÂj©A¡L[Å éÈçA
+ <literal>save()</literal> ðÄÑo·OÉA¯ÊqðèÄȯêÎÈèܹñB
+ <literal>save()</literal> ÌãíèÉAEJB3 ÌúhtgÅè`³ê½
+ <literal>persist()</literal> ðg¤±ÆàÂ\Å·B
+ </para>
+
+ <para>
+ ãíèÉA¯ÊqðøÉÆé <literal>save()</literal>
+ \bhðgÁÄA ¯ÊqðèÄé±ÆàÅ«Ü·B
+ </para>
+
+
+ <programlisting><![CDATA[DomesticCat pk = new DomesticCat();
+pk.setColor(Color.TABBY);
+pk.setSex('F');
+pk.setName("PK");
+pk.setKittens( new HashSet() );
+pk.addKitten(fritz);
+sess.save( pk, new Long(1234) );]]></programlisting>
+
+ <para>
+ i±»·éIuWFNgªÖAIuWFNgðÁÄ¢éê iá¦ÎAOÌáɨ¯é
+ <literal>kittens</literal> RNVÌæ¤ÉjA OL[JÉA <literal>NOT
+ NULL</literal> §ñð¯ȢÀèÍA ±êçÌêAÌIuWFNgðÇñÈÔÅi±»µÄà©Ü¢Ü¹ñB
+ OL[§ñðá½·é°êÍ èܹñB µ©µA <literal>NOT NULL</literal>
+ §ñª éêAÔáÁ½ÔÅIuWFNgð <literal>save()</literal> µÄµÜ¤ÆA
+ §ñÉá½·é©àµêܹñB </para>
+
+ <para>
+ ÖA·éIuWFNgð©®IÉÛ¶·éA HibernateÌ <emphasis>JÚIÈi±»(transitive
+ persistence)</emphasis> @\ð g¤ÂàèÈçÎA»Ìæ¤ÈÚ×ðCÉ·éKvÍ èܹñB
+ »µÄA <literal>NOT NULL</literal> §ñÌá½·çN±èܹñB
+ Hibernateª·×ÄÊ|ðÝÄêÜ·BJÚIÈi±»ÍA±ÌÍÌã¼É©êĢܷB </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-loading">
+ <title>IuWFNgÌ[h</title>
+
+ <para>
+ i±»³ê½CX^X̯Êqª 究ߪ©ÁÄ¢éÈçA <literal>Session</literal> Ì
+ <literal>load()</literal> \bhðgÁÄA³Å«Ü·B <literal>load()</literal>
+ ÍAClass IuWFNgðøÉÆèA »ÌNXÌCX^XðV½É¶¬µAóÔð[hµÜ·B
+ »ÌCX^XÌóÔÍAi±(persistent)óÔÅ·B </para>
+
+
+ <programlisting><![CDATA[Cat fritz = (Cat) sess.load(Cat.class, generatedId);]]></programlisting>
+
+
+ <programlisting><![CDATA[// you need to wrap primitive identifiers
+long id = 1234;
+DomesticCat pk = (DomesticCat) sess.load( DomesticCat.class, new Long(id) );]]></programlisting>
+ <para>
+ é¢ÍAȺÌæ¤ÉAù¶ÌCX^XÉóÔð[h·é±ÆàÅ«Ü·B </para>
+
+
+ <programlisting><![CDATA[Cat cat = new DomesticCat();
+// load pk's state into cat
+sess.load( cat, new Long(pkId) );
+Set kittens = cat.getKittens();]]></programlisting>
+
+ <para>
+ DBÉY·ésª³¢êA <literal>load()</literal> ÍñsÂ\ÈáOð °é±ÆÉӵܵå¤B
+ »ÌNXªvLVðgÁÄ}bsO³êÄ¢éêA <literal>load()</literal>
+ Íú»³êĢȢvLVðÔµAvLVÌ\bhªÄÎêéÜÅÀÛÉÍ f[^x[XÉANZXµÜ¹ñB
+ àµAÀÛÉf[^x[X©ç[h¹¸ÉAIuWFNgÉηéÖAðìè½¢êA ±ÌUé¢ÍÆÄàð§¿Ü·B
+ <literal>batch-size</literal> ªNX}bsOÉè`³êÄ¢éÈçÎA
+ ¡ÌCX^XðêÅ[h·é±ÆªÂ\Å·B </para>
+
+ <para>
+ Y·ésª¶Ý·é±ÆðmMÅ«È¢êÍA <literal>get()</literal> \bhðg¤×«Å·B
+ »êÍAf[^x[XÉ·®ÉANZXµAY·ésª³¢êÍnullðԵܷB </para>
+
+
+ <programlisting><![CDATA[Cat cat = (Cat) sess.get(Cat.class, id);
+if (cat==null) {
+ cat = new Cat();
+ sess.save(cat, id);
+}
+return cat;]]></programlisting>
+
+ <para>
+ <literal>LockMode</literal> ðg¦ÎA
+ <literal>SELECT ... FOR UPDATE</literal>Æ¢¤SQLð gÁÄIuWFNgð[h·é±ÆªÅ«Ü·B
+ Ú×ÈîñÍAAPIhL
gðQƵľ³¢B </para>
+
+
+ <programlisting><![CDATA[Cat cat = (Cat) sess.get(Cat.class, id, LockMode.UPGRADE);]]></programlisting>
+
+ <para>
+ ÖAÉηéJXP[hû@ƵÄ
+ <literal>lock</literal> â <literal>all</literal> ð
+ wèµÈ¢ÀèAÖA·éCX^XâÜÜêéRNVÍ <literal>FOR UPDATE</literal> ų
+ <emphasis>³êÈ¢</emphasis> ±ÆÉӵܵå¤B </para>
+
+ <para>
+ <literal>refresh()</literal> \bhðg¤±ÆÅAÇñÈÆ«ÅàAIuWFNgâ»ÌRNVð
+ [h·é±ÆªÅ«Ü·B f[^x[XÌgKªe[uðXVµ½ÛÉA
+ »Ìe[uÉηéIuWFNgÌvpeBð¯ú·éêA±Ì\bhªðɧ¿Ü·B </para>
+
+
+ <programlisting><![CDATA[sess.save(cat);
+sess.flush(); //force the SQL INSERT
+sess.refresh(cat); //re-read the state (after the trigger executes)]]></programlisting>
+ <para>
+ åØÈâèÍA¢ÂàÌ_ÉÖ·éàÌÅ·B»êÍAHibernateªf[^x[X©çA
+ ÇÌç¢ÌÊð³·éÌ©ÆAÇÌç¢ÌÌSQLÌ <literal>SELECT</literal> ¶ªgíêéÌ©Å·B
+ ±êÍA <emphasis>tFb`Ìíª</emphasis> ÉæèÜ·B±êÉ¢ÄÍA<xref linkend="performance-fetching"/> ÅྵĢܷB </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-querying" revision="1">
+ <title>NG</title>
+
+ <para>
+ Tµ½¢IuWFNg̯Êqªª©çÈ¢êÍANGªKvÉÈèÜ·B
+ HibernateÍg¢â·ÄÍÈIuWFNgwüÌNG¾ê (HQL)ðT|[gµÄ¢Ü·B
+ vOÉæÁÄNGªì¬Å«éæ¤ÉAHibernateÍôû³ê½CriteriaÆExampleNG@\(QBCÆQBEjð
+ T|[gµÄ¢Ü·BResultSetðIuWFNgÉÏ··éHibernateÌIvV@\ðg¤±ÆÅA
+ f[^x[XÌlCeBuÈSQLÅNGð\»·é±ÆàÅ«Ü·B </para>
+
+ <sect2 id="objectstate-querying-executing" revision="1">
+ <title>NGÌÀs</title>
+
+ <para>
+ HQLâlCeBuÈSQLNGÍA <literal>org.hibernate.Query</literal>
+ ÌCX^XƵÄ\»³êÜ·B ±ÌC^tF[XÍAp[^oCfBOâResultSetÌnhOâ
+ NGÌÀsðs¤\bhðpӵĢܷB ÊíA <literal>Query</literal> ÍAȺɦ·æ¤ÉA
+ »Ì_Ì <literal>Session</literal> ðgÁÄ澵ܷB </para>
+
+
+ <programlisting><![CDATA[List cats = session.createQuery(
+ "from Cat as cat where cat.birthdate < ?")
+ .setDate(0, date)
+ .list();
+
+List mothers = session.createQuery(
+ "select mother from Cat as cat join cat.mother as mother where cat.name = ?")
+ .setString(0, name)
+ .list();
+
+List kittens = session.createQuery(
+ "from Cat as cat where cat.mother = ?")
+ .setEntity(0, pk)
+ .list();
+
+Cat mother = (Cat) session.createQuery(
+ "select cat.mother from Cat as cat where cat = ?")
+ .setEntity(0, izi)
+ .uniqueResult();]]
+
+Query mothersWithKittens = (Cat) session.createQuery(
+ "select mother from Cat as mother left join fetch mother.kittens");
+Set uniqueMothers = new HashSet(mothersWithKittens.list());
+
+ ]]></programlisting>
+
+ <para>
+ NGÍAÊA <literal>list()</literal> ðÄÑo·±ÆÉæÁÄÀs³êÜ·B
+ NGÌÊÍAãÉ éRNVÉ·×Ä[h³êÜ·B
+ NGÉæÁij³ê½GeBeBÌCX^XÍAi±óÔÅ·B
+ àµANGª½Á½1ÂÌCX^XðԷƪ©ÁÄ¢éÈçA
+ <literal>uniqueResult()</literal> \bhªèÁæè¢û@Å·B
+ ¦tFb`ðpµ½NGÌêAÓ¤A¾çê½RNVÉÍA
+ [gÌIuWFNgªd¡µÄÜÜêĢܷ
+ iµ©µA[gªÂRNVÍú»([hj³êĢܷjB
+ ±Ìd¡Í <literal>Set</literal> ðgÁÄæè±ÆªÅ«Ü·B</para>
+
+ <sect3 id="objectstate-querying-executing-iterate">
+ <title>ÊðCe[g·é</title>
+
+ <para>
+ XA <literal>iterate()</literal> \bhðgÁÄNGðÀs·é±ÆÅA
+ æèÇ¢ptH[}Xð¾é±ÆªÅ«Ü·B ±êÍAÊíANGÉæÁľçê½ÀÛÌGeBeBÌCX^XªA
+ ·ÅÉZbVܽÍñLbV
ɶݷé±ÆªúÒÅ«é꾯ŷB
+ »êçªAܾLbV
³êĢȢÈçA <literal>iterate()</literal> ÍA
+ <literal>list()</literal> æèàxAÈPÈNGÉεÄà½Ìf[^x[XANZXð
+ KvƵܷB»ÌANZXÆÍA¯Êq¾¯ðæ¾·é½ßÌÅÌselectPñÆA
+ ÀÛÌCX^Xðú»·é½ßÉã©çs¤nñÌselecṯÆÅ·B </para>
+
+
+ <programlisting><![CDATA[// fetch ids
+Iterator iter = sess.createQuery("from eg.Qux q order by q.likeliness").iterate();
+while ( iter.hasNext() ) {
+ Qux qux = (Qux) iter.next(); // fetch the object
+ // something we couldnt express in the query
+ if ( qux.calculateComplicatedAlgorithm() ) {
+ // delete the current instance
+ iter.remove();
+ // dont need to process the rest
+ break;
+ }
+}]]></programlisting>
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-tuples">
+ <title>IuWFNgÌgituplejðÔ·NG</title>
+ <para>
+ HibernateÌNGÅÍAXAIuWFNgÌgðÔ·±Æª èÜ·B »ÌêÍAe^vÍzñƵÄÔ³êÜ·B
+ </para>
+
+
+ <programlisting><![CDATA[Iterator kittensAndMothers = sess.createQuery(
+ "select kitten, mother from Cat kitten join kitten.mother mother")
+ .list()
+ .iterator();
+
+while ( kittensAndMothers.hasNext() ) {
+ Object[] tuple = (Object[]) kittensAndMothers.next();
+ Cat kitten = tuple[0];
+ Cat mother = tuple[1];
+ ....
+}]]></programlisting>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-scalar" revision="1">
+ <title>XJ[ÌÊ</title>
+
+ <para>
+ NGÅÍA <literal>select</literal> ßÅNXÌvpeBðwèÅ«Ü·B
+ SQLÌWÖðÄÔ±ÆàÅ«Ü·BvpeBâWÖÍA
+ ii±óÔÌGeBeBÅÍÈjuXJ[lvÅ éƩȳêÜ·B </para>
+
+
+ <programlisting><![CDATA[Iterator results = sess.createQuery(
+ "select cat.color, min(cat.birthdate), count(cat) from Cat cat " +
+ "group by cat.color")
+ .list()
+ .iterator();
+
+while ( results.hasNext() ) {
+ Object[] row = (Object[]) results.next();
+ Color type = (Color) row[0];
+ Date oldest = (Date) row[1];
+ Integer count = (Integer) row[2];
+ .....
+}]]></programlisting>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-parameters">
+ <title>p[^ÌoCh</title>
+
+ <para>
+ <literal>Query</literal>
+ ÍA¼Ot«Ìp[^âJDBCX^CÌ <literal>?</literal>
+ p[^ÉlðoCh·é½ßÌ\bhðÁĢܷB
+ <emphasis>JDBCÆÍá¢AHibernateÍp[^É[©çÔðUÁÄ¢«Ü·B</emphasis>
+ ¼Ot«Ìp[^ÆÍANG¶ñÌÈ©É é <literal>:name</literal> `®Ì¯ÊqÅ·B
+ ¼Ot«p[^Ì_ÍÌÊèÅ·B </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para> ¼Ot«p[^ÍANG¶ñÉoê·éÔƳÖWÅ· </para>
+ </listitem>
+ <listitem>
+ <para> ¯¶NGàÉ¡ñoê·é±ÆªÅ«Ü· </para>
+ </listitem>
+ <listitem>
+ <para> ©ª©gðྵܷ </para>
+ </listitem>
+ </itemizedlist>
+
+
+ <programlisting><![CDATA[//named parameter (preferred)
+Query q = sess.createQuery("from DomesticCat cat where cat.name = :name");
+q.setString("name", "Fritz");
+Iterator cats = q.iterate();]]></programlisting>
+
+
+ <programlisting><![CDATA[//positional parameter
+Query q = sess.createQuery("from DomesticCat cat where cat.name = ?");
+q.setString(0, "Izi");
+Iterator cats = q.iterate();]]></programlisting>
+
+
+ <programlisting><![CDATA[//named parameter list
+List names = new ArrayList();
+names.add("Izi");
+names.add("Fritz");
+Query q = sess.createQuery("from DomesticCat cat where cat.name in (:namesList)");
+q.setParameterList("namesList", names);
+List cats = q.list();]]></programlisting>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-pagination">
+ <title>y[Wª¯
+ </title>
+
+ <para>
+ ResultSetɧÀi³µ½¢Ååsâ³µ½¢ÅÌsjðÁ¦éKvª êÎA
+ ȺÌæ¤ÉA <literal>Query</literal> C^[tFCXÌ\bhðg¢Ü·B </para>
+
+
+ <programlisting><![CDATA[Query q = sess.createQuery("from DomesticCat cat");
+q.setFirstResult(20);
+q.setMaxResults(10);
+List cats = q.list();]]></programlisting>
+
+ <para> §Àt«ÌNGðDBMSÌlCeBuÈSQLÉÏ··éû@ðAHibernateÍmÁĢܷB
+ </para>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-scrolling">
+ <title>XN[Â\ÈCe[V</title>
+
+ <para>
+ JDBChCoªXN[Â\È <literal>ResultSet</literal> ðT|[gµÄ¢êÎA
+ <literal>Query</literal>
+ C^[tFCXðgÁÄA <literal>ScrollableResults</literal> IuWFNgð
+ æ¾Å«Ü·B»êðg¤ÆANGÌÊÉεÄ_îÉirQ[VÅ«Ü·B </para>
+
+
+ <programlisting><![CDATA[Query q = sess.createQuery("select cat.name, cat from DomesticCat cat " +
+ "order by cat.name");
+ScrollableResults cats = q.scroll();
+if ( cats.first() ) {
+
+ // find the first name on each page of an alphabetical list of cats by name
+ firstNamesOfPages = new ArrayList();
+ do {
+ String name = cats.getString(0);
+ firstNamesOfPages.add(name);
+ }
+ while ( cats.scroll(PAGE_SIZE) );
+
+ // Now get the first page of cats
+ pageOfCats = new ArrayList();
+ cats.beforeFirst();
+ int i=0;
+ while( ( PAGE_SIZE > i++ ) && cats.next() ) pageOfCats.add( cats.get(1) );
+
+}
+cats.close()]]></programlisting>
+
+ <para>
+ ±Ì@\ÉÍI[vóÔÌf[^x[XRlNVªKvÅ é±ÆÉӵľ³¢B
+ àµAItCÌy[Wª¯@\ªKvÅ êÎA <literal>setMaxResult()</literal> /
+ <literal>setFirstResult()</literal> ðg¢Üµå¤B </para>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-named" revision="1">
+ <title>¼Ot«NGÌOoµ</title>
+
+ <para>
+ }bsOhL
gɼOt«ÌNGðè`·é±ÆªÅ«Ü·B
+ i}[NAbvÆðß³ê鶪NGÉÜÜêéÈçA <literal>CDATA</literal> ZNVð
+ g¤±ÆðYêÈ¢æ¤ÉµÜµå¤Bj </para>
+
+
+ <programlisting><![CDATA[<query name="ByNameAndMaximumWeight"><![CDATA[
+ from eg.DomesticCat as cat
+ where cat.name = ?
+ and cat.weight > ?
+] ]></query>]]></programlisting>
+
+ <para>p[^ÌoCfBOÆÀsÍAȺÌæ¤ÈvOÅsíêÜ·B </para>
+
+
+ <programlisting><![CDATA[Query q = sess.getNamedQuery("ByNameAndMaximumWeight");
+q.setString(0, name);
+q.setInt(1, minWeight);
+List cats = q.list();]]></programlisting>
+
+ <para> ÀÛÌvOR[hÍAgíêéNG¾êÉ˶µÄ¢È¢±ÆÉӵܵå¤B
+ ^f[^ÉÍAlCeBuSQLNGðè`·é±ÆàÅ«Ü·B ܽAù¶ÌNGð}bsOt@CÉÚ·±ÆÅA
+ HibernateÉÚs·é±ÆàÅ«Ü·B </para>
+ <para>
+ <literal><hibernate-mapping></literal> ÌÌNGè`ÍANGÉηé
+ j[NȼOªKvȱÆÉàӵľ³¢B»êÉεÄA <literal><class></literal> ÌÌ
+ NGè`ÍANXÌ®SÀ輪OÉt¯çêéÌÅA©®IÉj[NȼOÉÈèÜ·B
+ áF <literal>eg.Cat.ByNameAndMaximumWeight</literal>
+ </para>
+ </sect3>
+ </sect2>
+
+ <sect2 id="objectstate-filtering" revision="1">
+ <title>RNVÌtB^O</title>
+ <para> RNV <emphasis>tB^</emphasis> ÍAi±»³êÄ¢éRNVâzñÉKp³êé
+ ÁêÈ^CvÌNGÅ·B»ÌNG¶ñÅÍARNVÌ»Ì_ÅÌvfðÓ¡·é
+ <literal>this</literal> ðg¢Ü·B </para>
+
+
+ <programlisting><![CDATA[Collection blackKittens = session.createFilter(
+ pk.getKittens(),
+ "where this.color = ?")
+ .setParameter( Color.BLACK, Hibernate.custom(ColorUserType.class) )
+ .list()
+);]]></programlisting>
+
+ <para> Ô³êéRNVÍBagÆÝȳêÜ·B»µÄA»êÍàÆÌRNVÌRs[ÉÈèÜ·B
+ ³ÌRNVÍC³³êܹñi±êÍA"filter"Æ¢¤¼OÌÓ¡ÆÍÙÈèÜ·ªA úÒ³ê鮫ÆÍêvµÄ¢Ü·jB
+ </para>
+
+ <para> tB^ÉÍ <literal>from</literal>
+ ߪsvÅ é±ÆÉCÃŵå¤iKvÈçA±ÆàÂ\Å·ªjB tB^ÍARNVÌvf©ÌðÔµÄ\¢Ü¹ñB
+ </para>
+
+
+ <programlisting><![CDATA[Collection blackKittenMates = session.createFilter(
+ pk.getKittens(),
+ "select this.mate where this.color = eg.Color.BLACK.intValue")
+ .list();]]></programlisting>
+
+ <para> NGðÜÜÈ¢tB^àðɧ¿Ü·B
+ á¦ÎAñíÉå«ÈRNV̪Wð[h·é½ßÉg¦Ü·B </para>
+
+
+ <programlisting><![CDATA[Collection tenKittens = session.createFilter(
+ mother.getKittens(), "")
+ .setFirstResult(0).setMaxResults(10)
+ .list();]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="objecstate-querying-criteria" revision="1">
+ <title>NCeAÌNG</title>
+
+ <para> HQLÍñíÉÍÅ·ªANG¶ñðìéæèàAIuWFNgwüÌAPIðgÁÄ
+ ®IÉNGðìéûðDÞJÒà¢Ü·B ±¤¢Á½ê̽ßÉAHibernateͼ´IÈ
+ <literal>Criteria</literal> NGAPIðñµÄ¢Ü·B </para>
+
+
+ <programlisting><![CDATA[Criteria crit = session.createCriteria(Cat.class);
+crit.add( Expression.eq( "color", eg.Color.BLACK ) );
+crit.setMaxResults(10);
+List cats = crit.list();]]></programlisting>
+
+ <para> <literal>Criteria</literal> Æ
+ <literal>Example</literal> APIÌÚ×ÍA <xref linkend="querycriteria"/>
+ Éq×çêĢܷB </para>
+
+ </sect2>
+
+ <sect2 id="objectstate-querying-nativesql" revision="2">
+ <title>lCeBuSQLÌNG</title>
+
+ <para>
+ <literal>createSQLQuery()</literal> ðgÁÄASQLÅNGð\»·é±ÆàÅ«Ü·B
+ »µÄAHibernateÉAResultSet ©çIuWFNgÖÌ}bsOðÜ©¹Ü·B
+ <literal>session.connection()</literal> ðÄ×ÎÇñÈÆ«ÅàA¼ÚAJDBC
+ <literal>Connection</literal> ðgpÅ«é±Æðo¦Ä¨«Üµå¤B àµAHibernate
+ APIðg¤ÌÅ êÎAºLÌæ¤ÉSQLÌʼðÊÅçȯêÎÈèܹñB </para>
+
+
+ <programlisting><![CDATA[List cats = session.createSQLQuery(
+ "SELECT {cat.*} FROM CAT {cat} WHERE ROWNUM<10",
+ "cat",
+ Cat.class
+).list();]]></programlisting>
+
+
+ <programlisting><![CDATA[List cats = session.createSQLQuery(
+ "SELECT {cat}.ID AS {cat.id}, {cat}.SEX AS {cat.sex}, " +
+ "{cat}.MATE AS {cat.mate}, {cat}.SUBCLASS AS {cat.class}, ... " +
+ "FROM CAT {cat} WHERE ROWNUM<10",
+ "cat",
+ Cat.class
+).list()]]></programlisting>
+
+ <para>
+ SQLNGÍAHibernateNGƯ¶æ¤ÉA¼Ot«Ìp[^ÆÊup[^ð±ƪūܷB
+ Hibernateɨ¯élCeBuÈSQLNGÌÚ×É¢ÄÍA <xref linkend="querysql"/>
+ ðQƵľ³¢B </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="objectstate-modifying" revision="1">
+ <title>i±IuWFNgÌC³</title>
+
+ <para>
+ <emphasis>Ìi±CX^X</emphasis>
+ iáF <literal>Session</literal> ÉæÁÄA
+ [hAZ[uAì¬ANG³ê½IuWFNgjÍAAvP[VÉì³êÜ·B
+ »ÌÛÉÏX³ê½i±óÔÍA <literal>Session</literal> ª <emphasis>tbV
</emphasis>
+ ³êéÆ«ÉAi±»³êÜ·i±êÍA±ÌÍÌã¼Åq×ĢܷjB
+ ÏXði±»·é½ßÉAÁêÈ\bhi <literal>update()</literal>
+ Ìæ¤ÈàÌB±êÍAÊÌÚIÅgpµÜ·jð ÄÔKvÍ èܹñB IuWFNgÌóÔðXV·éêÔÈPÈû@ÍAIuWFNgð
+ <literal>load()</literal> µA <literal>Session</literal>
+ ðI[vɵĢéÔÉA¼Úì·é±ÆÅ·B </para>
+
+
+ <programlisting><![CDATA[DomesticCat cat = (DomesticCat) sess.load( Cat.class, new Long(69) );
+cat.setName("PK");
+sess.flush(); // changes to cat are automatically detected and persisted
+]]></programlisting>
+
+ <para>
+ iIuWFNgð[h·é½ßÌjSQLÌ <literal>SELECT</literal> ÆiXV³ê½óÔði±»·é½ßÌj
+ SQLÌ <literal>UPDATE</literal> ª¯¶ZbVÅKvÆÈéÌÅA±ÌvO~OfÍA
+ ø¦ª«Èéêª èÜ·B
+ »Ì½ßAHibernateÍÊÌû@ðpӵĢܷB»êÍACX^X𪣷éidetachedjû@Å·B </para>
+
+ <para>
+ <emphasis>HibernateÍA <literal>UPDATE</literal> ¶â
+ <literal>DELETE</literal> ¶ð¼ÚÀs·éAPIðpӵĢܹñB
+ HibernateÍA <emphasis>óÔÇ</emphasis> T[rXÅ èAgíêéSQL
+ <emphasis>¶</emphasis> ̱ÆðJÒªl¦éKvÍ èܹñB
+ JDBCÍSQL¶ðÀs·é®àøÈAPIÅ èA <literal>session.connection()</literal>
+ ðÄÔ±ÆÅ ¢ÂÅàAJDBC <literal>Connection</literal> ðJÒÍæ¾Å«Ü·B
+ ³çÉAåÊÌf[^ìÌl¦ûÍAICgUNVü«AvP[VÌ
+ IuWFNg/[Vi}bsOÆÕ˵ܷB
+ µ©µAHibernateÌ¡ãÌo[WÅÍAåÊf[^ð·éÁÊÈ@\ðñ·é©àµêܹñB
+ ob`ìÉpÅ«é¢Â©ÌHvÉ¢ÄÍA <xref linkend="batch"/> ðQƵľ³¢B </emphasis>
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-detached" revision="2">
+ <title>ª£IuWFNgÌC³</title>
+
+ <para>
+ ½ÌAvP[VÅÍ̱ƪKvÉÈèÜ·B
+ »êÍA égUNVÅIuWFNgð³µAì·é½ßÉ»êðUIwÉèA
+ »ÌãÉAVµ¢gUNVÅÏXðZ[u·éÆ¢Á½±ÆÅ·B
+ Às«Ì¢Â«ÅA±Ì^CvÌAv[`ðg¤AvP[VÅÍA "úÔÌ·¢"
+ ìÆPÊÌu£«ðÛØ·é½ßÉAo[Wf[^ªÊígíêÜ·B </para>
+
+ <para>
+ HibernateÍA <literal>Session.update()</literal> â
+ <literal>Session.merge()</literal> \bhð
+ gÁÄAª£CX^XðÄÇÁ·é±ÆÅA±ÌfÉεܷB </para>
+
+
+ <programlisting><![CDATA[// in the first session
+Cat cat = (Cat) firstSession.load(Cat.class, catId);
+Cat potentialMate = new Cat();
+firstSession.save(potentialMate);
+
+// in a higher layer of the application
+cat.setMate(potentialMate);
+
+// later, in a new session
+secondSession.update(cat); // update cat
+secondSession.update(mate); // update mate]]></programlisting>
+
+ <para>
+ ¯Êq<literal>catId</literal> ð <literal>Cat</literal> ªAùÉ
+ <literal>secondSession</literal> Å[h³êÄ¢½êÍAÄÇÁµæ¤Æµ½Æ«ÉAáOª°çêÜ·B
+ </para>
+
+ <para>
+ ¯¶¯ÊqðÂi±CX^XðZbVªùÉ۵ĢȢ±Æð
+ mMÅ«éÈç <literal>update()</literal> ðg¢Üµå¤B
+ »µÄAZbVÌóÔðl¦¸ÉAÇñÈêÅàÏXð}[Wµ½¢êÍA <literal>merge()</literal>
+ ðg¢Üµå¤B ·Èí¿Aª£IuWFNgÌÄÇÁìªAÅÉÀs³êé±ÆðmÀÉ·é½ßÉA ÊíÍ
+ <literal>update()</literal> ªVµ¢ZbVÌÈ©ÅÅÉÄÎêé\bhÉÈèÜ·B </para>
+
+ <para>
+ ª£CX^X©çBÂ\ÈAª£CX^XðAvP[VÍÂÊÉ <literal>update()</literal>
+ ·×«Å·B»êÍA»ÌóÔðXVµ½¢êÉ <emphasis>Àè</emphasis> Ü·B
+ <emphasis>JÚIÈi±»</emphasis> ðg¦ÎAà¿ëñ©®»Å«Ü·B <xref
+ linkend="objectstate-transitive"/> ðQƵľ³¢B </para>
+
+ <para>
+ \bhÅàܽAVµ¢ZbVÉIuWFNgðÄÖAt¯Å«Ü·B µ©µAª£CX^XͳC³ÅȯêÎÈèܹñB
+ </para>
+
+
+ <programlisting><![CDATA[//just reassociate:
+sess.lock(fritz, LockMode.NONE);
+//do a version check, then reassociate:
+sess.lock(izi, LockMode.READ);
+//do a version check, using SELECT ... FOR UPDATE, then reassociate:
+sess.lock(pk, LockMode.UPGRADE);]]></programlisting>
+
+ <para>
+ <literal>lock()</literal> ÍA³Ü´ÜÈ
+ <literal>LockMode</literal> ÆÆàÉg¤±ÆªÅ«Ü·B
+ Ú×ÍAAPIhL
gÆgUNVÌÍðQƵľ³¢B
+ ÄÇÁÌƫɾ¯A <literal>lock()</literal> ªgíêéí¯ÅÍ èܹñB </para>
+
+ <para>
+ úÔÌ·¢ìÆPÊÌA»Ì¼ÌfÍA<xref linked="transactions-optimistic"/> Åq×ĢܷB </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-saveorupdate">
+ <title>©®IÈóÔo</title>
+
+ <para>
+ HibernateÌ[UÍÌ2ÂÌP[XÌÇ¿çÉàg¦éÄpIÈ\bhðvµÄ¢Üµ½B
+ »êÍAVµ¢¯Êq𶬵ÄêIÈCX^XðZ[u·é±ÆÆA
+ »Ì_̯ÊqÆÖAâĢ骣CX^XðXV/ÄÇÁ·é±ÆÌÅ«é\bhÅ·B
+ <literal>saveOrUpdate()</literal> ͱÌæ¤È@\ðÀ»µ½\bhÅ·B </para>
+
+
+ <programlisting><![CDATA[// in the first session
+Cat cat = (Cat) firstSession.load(Cat.class, catID);
+
+// in a higher tier of the application
+Cat mate = new Cat();
+cat.setMate(mate);
+
+// later, in a new session
+secondSession.saveOrUpdate(cat); // update existing state (cat has a non-null id)
+secondSession.saveOrUpdate(mate); // save the new instance (mate has a null id)]]></programlisting>
+
+ <para>
+ <literal>saveOrUpdate()</literal> Ìgpû@ÆÓ¡ÍA
+ Vµ¢[UÉÆÁĬðµ©àµêܹñB
+ ܸæêÉA éZbVÅgpµ½CX^XðÊÌVµ¢ZbVÅg¨¤ÆµÈ¢ÀèA
+ <literal>update()</literal> â <literal>saveOrUpdate()</literal> â
+ <literal>merge()</literal> ðg¤KvÍ èܹñB
+ AvP[VSÌðʶÄA±êçÌ\bhðSgíÈ¢±Æà èÜ·B </para>
+
+ <para>
+ ÊíA <literal>update()</literal> â <literal>saveOrUpdate()</literal>
+ ÍÌViIÅ gíêÜ·B </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>AvP[VªÅÌZbVÅIuWFNgð[hµÜ·B </para>
+ </listitem>
+ <listitem>
+ <para>IuWFNgªUIwÉçêÜ·B </para>
+ </listitem>
+ <listitem>
+ <para>IuWFNgÉεÄÏXªÁ¦çêÜ·B
+ </para>
+ </listitem>
+ <listitem>
+ <para> IuWFNgªrWlXWbNwÉçêÜ·B </para>
+ </listitem>
+ <listitem>
+ <para>
+ AvP[VÍA2ÔÚÌZbVÅ <literal>update()</literal>
+ ðÄÔ±ÆÅA±êçÌÏXði±»µÜ·B </para>
+ </listitem>
+ </itemizedlist>
+
+ <para> <literal>saveOrUpdate()</literal> ÍȺ̱Æðs¢Ü·B </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>IuWFNgª±ÌZbVÅA·ÅÉi±»³êÄ¢êÎA½àµÜ¹ñB </para>
+ </listitem>
+ <listitem>
+ <para>
+ »ÌZbVÉÖAâĢéÊÌIuWFNgª¯¶¯ÊqðÁÄ¢éÈçA áOð°Ü·B </para>
+ </listitem>
+ <listitem>
+ <para>
+ IuWFNg̯Êqªlð½È¢ÈçÎA <literal>save()</literal> µÜ·B </para>
+ </listitem>
+ <listitem>
+ <para>
+ IuWFNg̯Êqªlð¿A»ÌlªV½ÉCX^X»³ê½IuWFNg̽ßÌlÅ éêA »ÌIuWFNgð
+ <literal>save()</literal> µÜ·B </para>
+ </listitem>
+ <listitem>
+ <para>
+ IuWFNgªi <literal><version></literal> â
+ <literal><timestamp></literal> ÉæÁÄj
+ o[Wï³êÄ¢ÄAo[WÌvpeBªlð¿A
+ »ÌlªVµCX^X»³ê½IuWFNg̽ßÌlÅ éêA »ÌIuWFNgð
+ <literal>save()</literal> µÜ·B </para>
+ </listitem>
+ <listitem>
+ <para>
+ »¤ÅÈ¢êÍA»ÌIuWFNgð <literal>update()</literal> µÜ·B </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ »µÄA <literal>merge()</literal> ÍȺÌæ¤ÉÆÄàÙÈèÜ·B </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ ¯¶¯ÊqðÂi±»CX^Xª»Ì_ÅZbVÆÖAt¢Ä¢éÈçÎA
+ øÅó¯æÁ½IuWFNgÌóÔði±»CX^XÉRs[µÜ·B </para>
+ </listitem>
+ <listitem>
+ <para>
+ i±»CX^Xª»Ì_ÅZbVÉÖAt¢Ä¢È¢ÈçA
+ f[^x[X©ç»êð[h·é©A é¢ÍAVµ¢i±»CX^Xð쬵ܷB </para>
+ </listitem>
+ <listitem>
+ <para>i±»CX^XªÔ³êÜ·B </para>
+ </listitem>
+ <listitem>
+ <para> øƵÄ^¦½CX^XÍZbVÆÖAð¿Ü¹ñB »êÍAª£óÔÌÜÜÅ·B
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect1>
+
+ <sect1 id="objectstate-deleting" revision="1">
+ <title>i±IuWFNgÌí</title>
+
+ <para>
+ <literal>Session.delete()</literal>
+ ÍIuWFNgÌóÔðf[^x[X©çíµÜ·B à¿ëñAíµ½IuWFNgðAvP[VªÛµ½ÜÜÅàæ¢Å·B
+ »Ì½ßA <literal>delete()</literal> Íi±CX^XðêIÉ·éàÌÆl¦é̪êÔÅ·B </para>
+
+ <programlisting><![CDATA[sess.delete(cat);]]></programlisting>
+
+ <para>
+ OL[§ñÉá½·éXNàÈAD«ÈÔÅIuWFNgðí·é±ÆªÅ«Ü·B
+ ½¾µAÔáÁ½ÔÅIuWFNgðí·éÆAOL[JÌ <literal>NOT NULL</literal>
+ §ñÉá½·éÂ\«ª èÜ·B á¦ÎAeIuWFNgðíµ½Æ«ÉAqIuWFNgðíµYê½êÅ·B </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-replicating" revision="1">
+ <title>ÙÈéñÂÌf[^XgAÔÅÌIuWFNgÌvP[V</title>
+
+ <para>
+ i±CX^XÌOtðÊÌf[^XgAÉi±»·éêÉA
+ ¯ÊqÌlðĶ¬¹¸É·ÞÆÖÈêª èÜ·B </para>
+
+
+ <programlisting><![CDATA[//retrieve a cat from one database
+Session session1 = factory1.openSession();
+Transaction tx1 = session1.beginTransaction();
+Cat cat = session1.get(Cat.class, catId);
+tx1.commit();
+session1.close();
+
+//reconcile with a second database
+Session session2 = factory2.openSession();
+Transaction tx2 = session2.beginTransaction();
+session2.replicate(cat, ReplicationMode.LATEST_VERSION);
+tx2.commit();
+session2.close();]]></programlisting>
+
+ <para>
+ vP[VæÌf[^x[XÉsªùÉ éêA <literal>replicate()</literal>
+ ªÕËðÇÌæ¤Éµ¤©ð <literal>ReplicationMode</literal> ÅwèµÜ·B </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>ReplicationMode.IGNORE</literal> -
+ ¯¶¯ÊqðÂsªf[^x[XɶݷéÈçA »ÌIuWFNgð³µÜ·B </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>ReplicationMode.OVERWRITE</literal> - ¯¶¯ÊqðÂù¶Ìsð
+ ·×Ä㫵ܷB </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>ReplicationMode.EXCEPTION</literal> -
+ ¯¶¯ÊqðÂsªf[^x[XɶݷéÈçA áOð°Ü·B </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>ReplicationMode.LATEST_VERSION</literal> -
+ sÉÛ¶³êÄ¢éo[WÔªA øÌIuWFNgÌo[WÔæèâÈçÎA»Ìsð㫵ܷB
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Ìæ¤ÈP[XÅA±Ì@\ðgpµÜ·B ÙÈéf[^x[XCX^XÉüêçê½f[^̯úA
+ »iXVɨ¯éVXeÝèîñÌXVAñACIDgUNVÌÈ©ÅÁ¦çê½ÏXÌ[obNÈÇÅ·B </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-flushing">
+ <title>ZbVÌtbV
</title>
+
+ <para>
+ JDBCRlNVÌóÔÆãÌIuWFNgÌóÔð¯ú³¹é½ßÉKvÈ
+ SQL¶ð <literal>Session</literal> ªÀs·é±ÆªÆ«Ç« èÜ·B ±Ì
+ <emphasis>flush</emphasis> ÍAftHgÅÍÌÆ«ÉN±èÜ·B </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para> NGðÀs·éO </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>org.hibernate.Transaction.commit()</literal> ðÀsµ½Æ«
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>Session.flush()</literal> ðÀsµ½Æ« </para>
+ </listitem>
+ </itemizedlist>
+
+ <para> SQL¶ÍȺÌÔÅs³êÜ·B
+ </para>
+
+ <orderedlist spacing="compact">
+ <listitem>
+ <para>
+ ·×ÄÌGeBeBÌ}üB±êÍA <literal>Session.save()</literal> ðgÁÄZ[uµ½
+ IuWFNgÌÉÀsµÄ¢«Ü·B </para>
+ </listitem>
+ <listitem>
+ <para> ·×ÄÌGeBeBÌXV </para>
+ </listitem>
+ <listitem>
+ <para> ·×ÄÌRNVÌí </para>
+ </listitem>
+ <listitem>
+ <para>
+ ·×ÄÌRNVÌvfÉηéíAXVA}ü </para>
+ </listitem>
+ <listitem>
+ <para>·×ÄÌRNVÌ}ü </para>
+ </listitem>
+ <listitem>
+ <para>
+ ·×ÄÌGeBeBÌíB±êÍA<literal>Session.delete()</literal> ðgÁÄ
+ íµ½IuWFNgÌÉÀsµÄ¢«Ü·B </para>
+ </listitem>
+ </orderedlist>
+
+ <para>
+ (PÂáOª èÜ·B <literal>native</literal> ID ¶¬ðgÁ½IuWFNgÍA
+ »êçªZ[u³ê½Æ«É}ü³êÜ·Bj </para>
+
+ <para>
+ ¾¦IÉ
+ <literal>flush()</literal> ·éÆ«ð¢ÄA <emphasis>¢Â</emphasis>
+ <literal>Session</literal> ªJDBCðR[·éÌ©É¢Ä
+ âÎIÈÛØÍ èܹñB½¾µA»êçªÀs³êé <emphasis>Ô</emphasis> ¾¯Í ÛسêÜ·B
+ ܽAHibernate ÍA <literal>Query.list(..)</literal> ªÃ¢f[^âÔáÁ½f[^ԳȢ±Æð
+ ÛصĢܷB </para>
+
+ <para>
+ tbV
ªpÉÉN±çÈ¢æ¤ÉftHgÌUé¢ðϦé±ÆªÅ«Ü·B <literal>FlushMode</literal>
+ NXÍ3ÂÌÙÈé[hðè`µÜ·B »êÍAR~bgɾ¯tbV
·é[h iHibernateÌ
+ <literal>Transaction</literal> APIªgíêé꾯ŷjA
+ à¾Ì Á½Éîâĩ®ÅtbV
·é[hA <literal>flush()</literal>
+ ª¾¦IÉÄÎêÈ¢ÀètbV
µÈ¢[hÌRÂÅ·B ÅãÌ[hÍAìÆPʪ·úÔÉyÔêÉðɧ¿Ü· ( <xref
+ linkend="transactions-optimistic-longsession"/> ðQƵľ³¢)B
+ </para>
+
+
+ <programlisting><![CDATA[sess = sf.openSession();
+Transaction tx = sess.beginTransaction();
+sess.setFlushMode(FlushMode.COMMIT); // allow queries to return stale state
+
+Cat izi = (Cat) sess.load(Cat.class, id);
+izi.setName(iznizi);
+
+// might return stale data
+sess.find("from Cat as cat left outer join cat.kittens kitten");
+
+// change to izi is not flushed!
+...
+tx.commit(); // flush occurs
+sess.close();]]></programlisting>
+
+ <para>tbV
ÌÆ«AáOª¶·é©àµêܹñB
+ iá¦ÎADML쪧ñðá½·éæ¤ÈêÅ·Bj
+ áOðð·é½ßÉÍAHibernateÌgUNVÌUé¢ðð·éKvª é½ßA <xref
+ linkend="transactions"/> ÅྵܷB </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-transitive" revision="1">
+ <title>A½IÈi±»</title>
+
+ <para>
+ ÂXÌIuWFNgðZ[uµ½èAíµ½èAÄÇÁµ½è·é±ÆÍ
+ ©ÈèÊ|Å·BÁÉAÖA·éIuWFNgðµ¤æ¤ÈêÉÍÛ§¿Ü·B æ éÌÍAeqÖWðµ¤P[XÅ·B
+ ȺÌáðl¦ÄÝܵå¤B </para>
+
+ <para>
+ àµAeqÖWÌqªl^Èçiá¦ÎAZâ¶ñÌRNVjA
+ »êçÌCtTCNÍeÉ˶µÄ¨èAÖÈóÔÏ»Ì"JXP[h"ðg¤½ßÉA ÇÁÌìÆÍKvÍ èܹñB
+ eªZ[u³ê½Æ«Al^ÌqIuWFNg௶æ¤ÉZ[u³êÜ·µA eªí³ê½Æ«ÍAqàí³êÜ·B»Ì¼Ìì௶ŷB
+ RNV©ç1ÂÌqðí·éæ¤ÈìÅà¤Ü¢«Ü·B ·Èí¿AHibernateͱÌíìðo·éÆA
+ l^ÌIuWFNgÍQÆð¤LÅ«È¢ÌÅAf[^x[X©ç»ÌqðíµÜ·B </para>
+
+ <para>±±ÅAeÆqªl^ÅÈGeBeBÅ éƵį¶ViIðl¦ÄÝܵå¤B
+ iá¦ÎAJeS[ÆiÚÌÖWâeÆqÌLÌÖWÅ·Bj GeBeBÍA»ê©gªCtTCNð¿AQÆ̤LðT|[gµÜ·B
+ i»Ì½ßARNV©çGeBeBðí·é±ÆÍA GeBeB©gÌíðÓ¡µÜ¹ñBj
+ ܽAGeBeBÍAftHgÅÍAÖA·é¼ÌGeBeBÖ óÔðJXP[h·é±ÆÍ èܹñB HibernateÍ
+ <emphasis>BÂ\«Éæéi±»</emphasis> ðftHgÅÍÀsµÜ¹ñB </para>
+
+ <para>
+ HibernateÌSessionÌî{ìi <literal>persist(), merge(), saveOrUpdate(),
+ delete(), lock(), refresh(), evict(), replicate()</literal> ªÜÜêÜ·jÉεÄA
+ »ê¼êÉηéJXP[hX^Cª èÜ·B »ê¼êÌJXP[hX^CÉÍA <literal>create, merge,
+ save-update, delete, lock, refresh, evict, replicate</literal>
+ Æ¢¤¼OªÂ¢Ä¢Ü·B àµAÖAÉÁÄJXP[h³¹½¢ìª éÈçA}bsOt@CÉ»¤wèµÈ¯êÎÈèܹñB
+ á¦ÎAȺÌæ¤ÉµÜ·B </para>
+
+
+ <programlisting><![CDATA[<one-to-one name="person" cascade="persist"/>]]></programlisting>
+
+ <para> JXP[hX^CÍAgÝí¹é±ÆªÅ«Ü·B </para>
+
+
+ <programlisting><![CDATA[<one-to-one name="person" cascade="persist,delete,lock"/>]]></programlisting>
+
+ <para>
+ <emphasis>·×ÄÌ</emphasis>
+ ìðÖAÉÁÄJXP[h·éæ¤wè·éÆ«ÍA <literal>cascade="all"</literal> ðg¢Ü·B
+ ftHgÌ <literal>cascade="none"</literal> ÍAÇÌìàJXP[hµÈ¢±ÆðÓ¡µÜ·B
+ </para>
+
+ <para>
+ ÁêÈJXP[hX^C
+ <literal>delete-orphan</literal> ÍAêνÖAɾ¯ KpÅ«Ü·B
+ ±êÍAÖA©çí³ê½qÌIuWFNgÉεÄA <literal>delete()</literal>
+ ìªKp³êé±ÆðÓ¡µÜ·B </para>
+
+ <para>¨··ßF </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ ÊA <literal><many-to-one></literal> â
+ <literal><many-to-many></literal> ÖAÉεÄÍA
+ JXP[hðÝè·éÓ¡Í èܹñB <literal><one-to-one></literal> Æ
+ <literal><one-to-many></literal> ÖAÉεÄÍA
+ JXP[hªðɧ±ƪ èÜ·B </para>
+ </listitem>
+ <listitem>
+ <para>
+ qIuWFNgÌõ½ªeIuWFNgÌõ½É§Àðó¯éÈçÎA
+ <literal>cascade="all,delete-orphan"</literal> ðwèµA qIuWFNgð
+ <emphasis>CtTCNIuWFNg</emphasis> ɵܷB </para>
+ </listitem>
+ <listitem>
+ <para> .
+ »êÈOÌêÍAJXP[hÍÙÆñÇKvȢŵå¤B
+ µ©µA¯¶gUNVÌÈ©ÅeÆqªêÉ®ì·é±Æª½¢Æv¢A
+ ¢ç©ÌR[hðèÔðÈ«½¢ÌÅ êÎA
+ <literal>cascade="persist,merge,save-update"</literal>
+ ðg¤±Æðl¦Üµå¤B </para>
+ </listitem>
+ </itemizedlist>
+
+ <para> <literal>cascade="all"</literal> Å}bsOµ½ÖAiPlÖAâRNVjÍA
+ <emphasis>eq</emphasis> X^CÌÖAÆ}[N³êÜ·B
+ »êÍAeÌZ[u/XV/íªAqÌZ[u/XV/íðø«N±·ÖW̱ÆÅ·B </para>
+
+ <para>
+ ³çÉAi±»³ê½eªqðPÉQƵĢ龯ÅAqÌZ[u/XVðø«N±µÜ·B
+ µ©µA±Ì^t@[Ís®SÅ·Be©çQƳêÈÈÁ½qÍA©®IÉí <emphasis>³êܹñ</emphasis> B
+ ½¾µA <literal>cascade="delete-orphan"</literal> Å}bsO³ê½
+ <literal><one-to-many></literal> ÖAð ¢ÄÅ·B
+ eqÖWÌJXP[hì̳mÈÓ¡ÍȺÌæ¤ÉÈèÜ·B </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ eª
+ <literal>persist()</literal> Én³ê½ÈçÎA ·×ÄÌqÍ
+ <literal>persist()</literal> Én³êÜ·B </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>merge()</literal> Én³ê½ÈçÎA ·×ÄÌqÍ
+ <literal>merge()</literal> Én³êÜ·B </para>
+ </listitem>
+ <listitem>
+ <para>
+ eª <literal>save()</literal>
+ A <literal>update()</literal>
+ A <literal>saveOrUpdate()</literal> Én³ê½ÈçÎA·×ÄÌqÍ
+ <literal>saveOrUpdate()</literal> Én³êÜ·B </para>
+ </listitem>
+ <listitem>
+ <para>
+ êIܽͪ£óÔÌqªAi±»³ê½eÉQƳê½ÈçÎA
+ <literal>saveOrUpdate()</literal> Én³êÜ·B </para>
+ </listitem>
+ <listitem>
+ <para>
+ eªí³ê½ÈçÎA·×ÄÌqÍA <literal>delete()</literal> Én³êÜ·B </para>
+ </listitem>
+ <listitem>
+ <para>
+ qªi±»³ê½e©çQƳêÈÈÁ½Æ«ÍA <emphasis>ÁɽàN±èܹñ</emphasis> B
+ æÁÄAAvP[VªKvÅ êÎA¾¦IÉí·éKvª èÜ·B
+ ½¾µA <literal>cascade="delete-orphan"</literal> Ìêð«Ü·B
+ ±ÌêAueÌÈ¢vqÍí³êÜ·B </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+ ÅãÉAìÌJXP[hªIuWFNgOtÉKp³êéÌÍA <emphasis>R[µ½</emphasis>
+ é¢ÍA <emphasis>flushµ½</emphasis> Å é±ÆÉӵľ³¢B
+ ·×ÄÌìÍA»ÌìªÀs³ê½Æ«ÉABÂ\ÈÖA·éGeBeBÉεÄ
+ JXP[hªÂ\ÈçJXP[hµÜ·B
+ µ©µA <literal>save-upate</literal> Æ
+ <literal>delete-orphan</literal> ÍA <literal>Session</literal>
+ ªflushµÄ¢éÔÉA ·×ÄÌBÂ\ÈÖA·éGeBeBÉ`dµÜ·B</para>
+ </sect1>
+
+ <sect1 id="objectstate-metadata">
+ <title>^f[^Ìgp</title>
+
+ <para>
+ HibernateÍA·×ÄÌGeBeBÆl^ÌñíÉb`È^xÌfðKvƵܷB
+ Æ«Ç«A±ÌfÍAvP[VÉÆÁÄÆÄàðɧ¿Ü·B
+ á¦ÎAAvP[VÍAHibernateÌ^f[^ðgÁÄA"«¢" fB[vRs[ASYð
+ ÀÅ«é©àµÜ¹ñB»ÌASYÆÍAÇÌIuWFNgªRs[³êé׫©iáFÂÏÌl^jâ
+ ÇÌIuWFNgÍRs[³êȢ׫©iáFsÏÈl^âÂ\ÈçÖA·éGeBeBjð »fÅ«éàÌÅ·B </para>
+
+
+ <para>
+ HibernateÍ<literal>ClassMetadata</literal>
+ Æ<literal>CollectionMetadata</literal> C^tF[XÆ
+ <literal>Type</literal> KwðʵÄ^f[^ðöJµÜ·B
+ ^f[^C^[tFCXÌCX^XÍA <literal>SessionFactory</literal> ©ç¾çêÜ·B
+ </para>
+
+
+ <programlisting><![CDATA[Cat fritz = ......;
+ClassMetadata catMeta = sessionfactory.getClassMetadata(Cat.class);
+
+Object[] propertyValues = catMeta.getPropertyValues(fritz);
+String[] propertyNames = catMeta.getPropertyNames();
+Type[] propertyTypes = catMeta.getPropertyTypes();
+
+// get a Map of all properties which are not collections or associations
+Map namedValues = new HashMap();
+for ( int i=0; i<propertyNames.length; i++ ) {
+ if ( !propertyTypes[i].isEntityType() && !propertyTypes[i].isCollectionType() ) {
+ namedValues.put( propertyNames[i], propertyValues[i] );
+ }
+}]]></programlisting>
+
+ </sect1>
+
+</chapter>
\ No newline at end of file
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/toolset_guide.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/toolset_guide.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/toolset_guide.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,634 @@
+<?xml version="1.0" encoding="Shift_JIS"?>
+<chapter id="toolsetguide" revision="2">
+ <title>Toolset Guide
+
+ c[ZbgKCh
+ </title>
+
+ <para>
+ HibernateðgÁ½EhgbvGWjAOÍA
+ EclipsevOCâR}hCc[Aà¿ëñAnt^XNðg¤±ÆÅÂ\Å·B
+ </para>
+
+ <para>
+ <emphasis>Hibernate Tools</emphasis> Í»ÝAù¶f[^x[XÌo[XGWjAOÌAnt^XNÉÁ¦ÄAEclipseIDEÌvOCðÜÝÜ·B
+ </para>
+
+ <itemizedlist>
+ <listitem><para>
+ <emphasis>}bsOGfB^:</emphasis>
+ HibernateÌXML}bsOt@CpÌGfB^ÅA
+ ©®â®Æ\¶²\¦ðT|[gµÄ¢Ü·BNX¼âvpeB/tB[h¼Éη驮â®àT|[gµA
+ ÊíÌXMLGfB^æèàÍÅ·B
+ </para></listitem>
+ <listitem><para>
+ <emphasis>Console:</emphasis>
+ R\[ÍGNvXÌVµ¢r
[Å·B
+ R\[RtBM
[VÌc[I[o[r
[ÉÁ¦ÄAi±NXÆ»ÌÖAÌÝìpr
[à¾çêÜ·B
+ f[^x[XÉHQLðÀsµAÊð¼ÚGNvXãÅ©é±ÆªÅ«Ü·B
+ </para></listitem>
+ <listitem><para>
+ <emphasis>JEBU[h</emphasis>
+ HibernateÌEclipsec[͢©ÌEBU[hðñµÜ·B
+ EBU[hðgÁÄHibernateÌÝèt@C(cfg.xml)ð·Îⶬµ½èA
+ ù¶Ìf[^x[XXL[}ðPOJOÌ\[Xt@CÆHibernateÌ}bsOt@CÖÆA
+ ®SÉo[XGWjAO·é±ÆªÅ«Ü·B
+ o[XGWjAOEBU[hÍJX^}CYÂ\Èev[gðT|[gµÜ·B
+ </para></listitem>
+ <listitem><para>
+ <emphasis>Ant Tasks:</emphasis>
+ </para></listitem>
+
+ </itemizedlist>
+
+ <para>
+ æèÚµ¢îñÍ <emphasis>Hibernate Tools</emphasis> pbP[WÆ»ÌhL
gðQƵľ³¢B
+ </para>
+
+ <para>
+ µ©µAHibernateÌCpbP[WÍ <emphasis>SchemaExport</emphasis> A
+ ʼ <literal>hbm2ddl</literal> àÜÝÜ·(HibernateàÅIUtCÅgpÅ«Ü·)B
+ </para>
+
+ <sect1 id="toolsetguide-s1" revision="2">
+ <title>XL[}Ì©®¶¬</title>
+
+ <para>
+ DDLÍHibernate[eBeBÉæè}bsOt@C©ç¶¬·é±ÆªÅ«Ü·B
+ ¶¬³ê½XL[}ÍGeBeBâRNVÌe[uÉηéQÆ®«§ñ(åL[ÆOL[)ðÜÝÜ·B
+ e[uÆV[PXÍ}bsO·é¯ÊqWFl[^ÉεĶ¬³êÜ·B
+ </para>
+
+ <para>
+ DDLÍx_[˶ÈÌÅA±Ìc[ðg¤Æ«ÍA<literal>hibernate.dialect</literal>
+ vpeBÅSQLÌ <literal>û¾</literal> ðwè <emphasis>µÈ¯êÎÈèܹñ</emphasis> B
+ </para>
+
+ <para>
+ ܸA¶¬³êéXL[}ðüP·éæ¤ÉA}bsOt@CðJX^}CYµÄ¾³¢B
+ </para>
+
+ <sect2 id="toolsetguide-s1-2" revision="3">
+ <title>XL[}ÌJX^}CY</title>
+
+ <para>
+ ½ÌHibernateÌ}bsOvfÅÍAIvVÌ <literal>length</literal> Æ¢¤¼Ì®«ðè`µÄ¢Ü·B
+ ±Ì®«ÅJ·ðÝè·é±ÆªÅ«Ü·(ܽÍNUMERIC/DECIMAL^Ìf[^̸xðÝèÅ«Ü·)B
+ </para>
+
+ <para>
+ <literal>not-null</literal> ®«ie[uÌJÖ <literal>NOT NULL</literal> §ñ𶬷éjÆ
+ <literal>unique</literal> ®«ie[uÌJÖ <literal>UNIQUE</literal> §ñ𶬷éjªÝèÅ«é^Oà èÜ·B
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="bar" column="barId" not-null="true"/>]]></programlisting>
+
+ <programlisting><![CDATA[<element column="serialNumber" type="long" not-null="true" unique="true"/>]]></programlisting>
+
+ <para>
+ <literal>unique-key</literal> ®«ÍJðO[v»µÄêÂÌL[§ñÉ·é½ßÉgíêÜ·B
+ »ÝA<literal>unique-key</literal> ®«Åwè³ê½lͧñÌwèÉÍ <emphasis>gíê¸</emphasis> A
+ }bsOt@CÅJðO[v»·é±ÆÉÌÝgíêÜ·B
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="org" column="orgId" unique-key="OrgEmployeeId"/>
+<property name="employeeId" unique-key="OrgEmployee"/>]]></programlisting>
+
+
+
+ <para>
+ <literal>index</literal> ®«Í}bsO·éJðgÁĶ¬µ½CfbNX̼OðwèµÜ·B
+ ¡JðPÂÌCfbNXÉO[v»Å«Ü·BPÉA¯¶CfbNX¼ðwè·é¾¯Å·B
+ </para>
+
+<programlisting><![CDATA[<property name="lastName" index="CustName"/>
+<property name="firstName" index="CustName"/>]]></programlisting>
+
+ <para>
+ <literal>foreign-key</literal> ®«ÍA¶¬³ê½OL[§ñ̼OðI[o[Ch·é½ßÉgpÅ«Ü·B
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="bar" column="barId" foreign-key="FKFooBar"/>]]></programlisting>
+
+ <para>
+ ½Ì}bsOvfÍAq <literal><column></literal> vfðLqÅ«Ü·B±êÍ¡J^Ì}bsOÉÍÁÉLpÅ·B
+ </para>
+
+ <programlisting><![CDATA[<property name="name" type="my.customtypes.Name"/>
+ <column name="last" not-null="true" index="bar_idx" length="30"/>
+ <column name="first" not-null="true" index="bar_idx" length="20"/>
+ <column name="initial"/>
+</property>]]></programlisting>
+
+ <para>
+ <literal>default</literal> ®«ÍJÌftHglðwèµÜ·
+ (}bsOµ½NXÌVµ¢CX^XðÛ¶·éOÉA
+ }bsOµ½vpeBÖ¯¶lðãü·×«Å·)B
+ </para>
+
+ <programlisting><![CDATA[<property name="credits" type="integer" insert="false">
+ <column name="credits" default="10"/>
+</property>]]></programlisting>
+
+ <programlisting><![CDATA[<version name="version" type="integer" insert="false">
+ <column name="version" default="0"/>
+</property>]]></programlisting>
+
+ <para>
+ <literal>sql-type</literal> ®«ÅAftHgÌHibernate^©çSQLÌf[^^ÖÌ}bsOðI[o[ChÅ«Ü·B
+ </para>
+
+
+ <programlisting><![CDATA[<property name="balance" type="float">
+ <column name="balance" sql-type="decimal(13,3)"/>
+</property>]]></programlisting>
+
+
+ <para>
+ <literal>check</literal> ®«Å`FbN§ñðwè·é±ÆªÅ«Ü·B
+ </para>
+
+ <programlisting><![CDATA[<property name="foo" type="integer">
+ <column name="foo" check="foo > 10"/>
+</property>]]></programlisting>
+
+ <programlisting><![CDATA[<class name="Foo" table="foos" check="bar < 100.0">
+ ...
+ <property name="bar" type="float"/>
+</class>]]></programlisting>
+
+
+ <table frame="topbot" id="schemattributes-summary" revision="2">
+ <title>ÜÆß</title>
+ <tgroup cols="3">
+ <colspec colwidth="1*"/>
+ <colspec colwidth="1*"/>
+ <colspec colwidth="2.5*"/>
+ <thead>
+ <row>
+ <entry>®«</entry>
+ <entry>l</entry>
+ <entry>à¾</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>length</literal></entry>
+ <entry>l</entry>
+ <entry>JÌ·³</entry>
+ </row>
+
+ <row>
+ <entry><literal>precision</literal></entry>
+ <entry>l</entry>
+ <entry>JÌDECIMAL^̸xiprecisionj</entry>
+ </row>
+
+ <row>
+ <entry><literal>scale</literal></entry>
+ <entry>l</entry>
+ <entry>JÌDECIMAL^ÌXP[iscalej</entry>
+ </row>
+
+ <row>
+ <entry><literal>not-null</literal></entry>
+ <entry><literal>true|false</literal></entry>
+ <entry>JªnulllðæçÈ¢±ÆðwèµÜ·
+ </entry>
+ </row>
+ <row>
+ <entry><literal>unique</literal></entry>
+ <entry><literal>true|false</literal></entry>
+ <entry>Jªj[N§ñð±ÆðwèµÜ·
+ </entry>
+ </row>
+ <row>
+ <entry><literal>index</literal></entry>
+ <entry><literal>CfbNX¼</literal> </entry>
+ <entry>(¡JÌ)CfbNX̼OðwèµÜ·
+ </entry>
+ </row>
+ <row>
+ <entry><literal>unique-key</literal></entry>
+ <entry><literal>j[NL[¼</literal></entry>
+ <entry>¡JÌj[N§ñ̼OðwèµÜ·
+ </entry>
+ </row>
+ <row>
+ <entry><literal>foreign-key</literal></entry>
+ <entry><literal>OL[¼</literal></entry>
+ <entry>
+ <one-to-one>A<many-to-one>A<many-to-many>}bsOvfðgÁÄA
+ ÖAÉ嶬³ê½OL[§ñ̼OðwèµÜ·B
+ <literal>SchemaExport</literal> Í <literal>inverse="true"</literal> ¤ðl¶µÈ¢±ÆÉӵľ³¢B
+ </entry>
+ </row>
+ <row>
+ <entry><literal>sql-type</literal></entry>
+ <entry><literal>SQLÌJ^</literal></entry>
+
+ <entry>
+ ftHgÌJ^ðI[o[ChµÜ·( <literal><column></literal> vfÌ®«ÌÝ)
+ </entry>
+ </row>
+
+ <row>
+ <entry><literal>default</literal></entry>
+ <entry>SQL®</entry>
+
+ <entry>
+ JÌftHglðwèµÜ·
+ </entry>
+ </row>
+
+ <row>
+ <entry><literal>check</literal></entry>
+ <entry>SQL®</entry>
+
+ <entry>
+ J©e[uÉSQLÌ`FbN§ñð쬵ܷ
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ <literal><comment></literal> vfŶ¬·éXL[}ÉRgðwè·é±ÆªÅ«Ü·B
+
+ </para>
+
+ <programlisting><![CDATA[<class name="Customer" table="CurCust">
+ <comment>Current customers only</comment>
+ ...
+</class>]]></programlisting>
+
+ <programlisting><![CDATA[<property name="balance">
+ <column name="bal">
+ <comment>Balance in USD</comment>
+ </column>
+</property>]]></programlisting>
+
+ <para>
+ ±êÉæèA¶¬µ½DDLÉ <literal>comment on table</literal> â <literal>comment on column</literal> ¶ª©êÜ·B
+ </para>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-3" revision="2">
+ <title>c[ÌÀs</title>
+
+ <para>
+ <literal>SchemaExport</literal> ÍWoÍÉεÄDDLXNvgð«oµADDL¶ðÀsµ½èàµÜ·B
+ </para>
+
+ <para>
+ <literal>java -cp </literal> <emphasis>hibernate_classpaths</emphasis>
+ <literal>org.hibernate.tool.hbm2ddl.SchemaExport</literal> <emphasis>options mapping_files</emphasis>
+ </para>
+
+ <table frame="topbot">
+ <title> <literal>SchemaExport</literal> Command Line Options
+
+ <literal>SchemaExport</literal> ÌR}hCIvV
+ </title>
+ <tgroup cols="2">
+ <colspec colwidth="1.5*"/>
+ <colspec colwidth="2*"/>
+ <thead>
+ <row>
+ <entry>IvV</entry>
+ <entry>à¾</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry> <literal>--quiet</literal> </entry>
+ <entry>XNvgðWoÍÉo͵ܹñ</entry>
+ </row>
+ <row>
+ <entry> <literal>--drop</literal> </entry>
+ <entry>e[uÌí¾¯ðs¢Ü·</entry>
+ </row>
+
+ <row>
+ <entry> <literal>--create</literal> </entry>
+ <entry>e[u̶¬ÌÝðs¢Ü·B
+ </entry>
+ </row>
+
+ <row>
+ <entry> <literal>--text</literal> </entry>
+ <entry>f[^x[XÉGNX|[gµÜ¹ñ</entry>
+ </row>
+ <row>
+ <entry> <literal>--output=my_schema.ddl</literal> </entry>
+ <entry> DDLXNvgðt@CÉo͵ܷ</entry>
+ </row>
+
+ <row>
+ <entry> <literal>--naming=eg.MyNamingStrategy</literal> </entry>
+ <entry> <literal>NamingStrategy</literal> ðIðµÜ·
+ </entry>
+ </row>
+
+ <row>
+ <entry><literal>--config=hibernate.cfg.xml</literal> </entry>
+ <entry>XMLt@C©çHibernateÌè`îñðÇÝÝÜ·</entry>
+ </row>
+ <row>
+ <entry> <literal>--properties=hibernate.properties</literal> </entry>
+ <entry>t@C©çf[^x[XvpeBðÇÝÝÜ·</entry>
+ </row>
+ <row>
+ <entry> <literal>--format</literal> </entry>
+ <entry>XNvgàɶ¬·éSQLðÇÝâ·¢æ¤ÉtH[}bgµÜ·</entry>
+ </row>
+ <row>
+ <entry> <literal>--delimiter=x</literal> </entry>
+ <entry>XNvgÌsæØè¶ðÝèµÜ·</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ AvP[VÉ <literal>SchemaExport</literal> ðgÝÞ±ÆàÅ«Ü·F
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = ....;
+new SchemaExport(cfg).create(false, true);]]></programlisting>
+
+ </sect2>
+
+
+ <sect2 id="toolsetguide-s1-4">
+ <title>vpeB</title>
+
+ <para>
+ Ìæ¤ÉAf[^x[XÌvpeBðwè·é±ÆªÅ«Ü·B
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para><literal>-D</literal> <emphasis><property></emphasis> ðgÁÄAVXevpeBƵÄ
+ </para>
+ </listitem>
+ <listitem>
+ <para><literal>hibernate.properties</literal> t@CàÅ
+ </para>
+ </listitem>
+ <listitem>
+ <para> <literal>--properties</literal> ðgÁÄwèµ½vpeBt@CàÅ
+
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ KvÈvpeBÍȺÌàÌÅ·F
+ </para>
+
+ <table frame="topbot">
+ <title>SchemaExportRlNVvpeB</title>
+ <tgroup cols="2">
+ <colspec colwidth="1.5*"/>
+ <colspec colwidth="2*"/>
+ <thead>
+ <row>
+ <entry>vpeB¼
+ </entry>
+ <entry>à¾
+ </entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry> <literal>hibernate.connection.driver_class</literal> </entry>
+ <entry>jdbcÌhCo[NX
+ </entry>
+ </row>
+ <row>
+ <entry> <literal>hibernate.connection.url</literal> </entry>
+ <entry>jdbcÌurl
+ </entry>
+ </row>
+ <row>
+ <entry> <literal>hibernate.connection.username</literal> </entry>
+ <entry>f[^x[XÌ[U
+ </entry>
+ </row>
+ <row>
+ <entry> <literal>hibernate.connection.password</literal> </entry>
+ <entry>[UpX[h
+ </entry>
+ </row>
+ <row>
+ <entry> <literal>hibernate.dialect</literal> </entry>
+ <entry>f[^x[Xû¾
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-5">
+ <title>Antðgp·é</title>
+
+ <para>
+ AntÌrhXNvg©ç <literal>SchemaExport</literal> ðÄÑo·±ÆªÅ«Ü·B:
+ </para>
+
+ <programlisting><![CDATA[<target name="schemaexport">
+ <taskdef name="schemaexport"
+ classname="org.hibernate.tool.hbm2ddl.SchemaExportTask"
+ classpathref="class.path"/>
+
+ <schemaexport
+ properties="hibernate.properties"
+ quiet="no"
+ text="no"
+ drop="no"
+ delimiter=";"
+ output="schema-export.sql">
+ <fileset dir="src">
+ <include name="**/*.hbm.xml"/>
+ </fileset>
+ </schemaexport>
+</target>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-6" revision="2">
+ <title>CN^ÈXL[}XV
+ </title>
+
+ <para>
+ <literal>SchemaUpdate</literal> c[Íù¶ÌXL[}ðCN^ÉXVµÜ·B
+ <literal>SchemaUpdate</literal> ÍJDBCÌ^f[^APIÉ˶µÜ·B
+ »Ì½ßA·×ÄÌJDBChCoŤܢÆÍÀçÈ¢±ÆÉӵľ³¢B
+ </para>
+
+ <para>
+ <literal>java -cp </literal> <emphasis>hibernate_classpaths</emphasis>
+ <literal>org.hibernate.tool.hbm2ddl.SchemaUpdate</literal> <emphasis>options mapping_files</emphasis>
+ </para>
+
+ <table frame="topbot">
+ <title> <literal>SchemaUpdate</literal> ÌR}hC¥IvV</title>
+ <tgroup cols="2">
+ <colspec colwidth="1.5*"/>
+ <colspec colwidth="2*"/>
+ <thead>
+ <row>
+ <entry>IvV</entry>
+ <entry>à¾</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry> <literal>--quiet</literal> </entry>
+ <entry>WoÍÉXNvgðo͵ܹñ</entry>
+ </row>
+
+ <row>
+ <entry> <literal>--text</literal> </entry>
+ <entry>f[^x[XÉXNvgðGNX|[gµÜ¹ñ
+ </entry>
+ </row>
+ <row>
+ <entry> <literal>--naming=eg.MyNamingStrategy</literal> </entry>
+ <entry><literal>NamingStrategy</literal> ðIðµÜ·B
+ </entry>
+ </row>
+
+ <row>
+ <entry> <literal>--properties=hibernate.properties</literal> </entry>
+ <entry>t@C©çf[^x[XvpeBðÇÝÝÜ·
+ </entry>
+ </row>
+
+ <row>
+ <entry> <literal>--config=hibernate.cfg.xml</literal> </entry>
+ <entry><literal>.cfg.xml</literal> t@CðwèµÜ·
+ </entry>
+ </row>
+
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ AvP[VÉ <literal>SchemaUpdate</literal> ðgÝޱƪūܷBF
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = ....;
+new SchemaUpdate(cfg).execute(false);]]></programlisting>
+
+ </sect2>
+
+
+ <sect2 id="toolsetguide-s1-7">
+ <title>CN^ÈXL[}XVÉηéAntÌgp</title>
+
+ <para>
+ AntXNvg©ç <literal>SchemaUpdate</literal> ðÄÑo·±ÆªÅ«Ü·F
+ </para>
+
+ <programlisting><![CDATA[<target name="schemaupdate">
+ <taskdef name="schemaupdate"
+ classname="org.hibernate.tool.hbm2ddl.SchemaUpdateTask"
+ classpathref="class.path"/>
+
+ <schemaupdate
+ properties="hibernate.properties"
+ quiet="no">
+ <fileset dir="src">
+ <include name="**/*.hbm.xml"/>
+ </fileset>
+ </schemaupdate>
+</target>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-8" revision="1">
+ <title>Schema validation</title>
+
+ <para>
+ <literal>SchemaValidator</literal> c[ÍAù¶Ìf[^x[XXL[}Æ쬵½}bsOhL
gª"êv·é"±ÆðصܷB <literal>SchemaValidator</literal> ÍJDBCÌ^f[^APIÉ˶·é±ÆÉӵľ³¢B»Ì½ßA·×ÄÌJDBChCo[Åì®·éàÌÅÍ èܹñB±Ìc[ÍeXgÉñíÉLpÅ·B
+ </para>
+
+ <para>
+ <literal>java -cp </literal> <emphasis>hibernate_classpaths</emphasis>
+ <literal>org.hibernate.tool.hbm2ddl.SchemaValidator</literal> <emphasis>options mapping_files</emphasis>
+ </para>
+
+ <table frame="topbot">
+ <title> <literal>SchemaValidator</literal> ÌR}hCEIvV
+ </title>
+ <tgroup cols="2">
+ <colspec colwidth="1.5*"/>
+ <colspec colwidth="2*"/>
+ <thead>
+ <row>
+ <entry>IvV</entry>
+ <entry>à¾</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry> <literal>--naming=eg.MyNamingStrategy</literal> </entry>
+ <entry><literal>NamingStrategy</literal> ðIðµÜ·
+ </entry>
+ </row>
+ <row>
+ <entry> <literal>--properties=hibernate.properties</literal> </entry>
+ <entry>t@C©çf[^x[XÌvpeBðÇÝÝÜ·
+ </entry>
+ </row>
+ <row>
+ <entry> <literal>--config=hibernate.cfg.xml</literal> </entry>
+ <entry><literal>.cfg.xml</literal> t@CðwèµÜ·
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ <literal>SchemaValidator</literal> ðAvP[VÉgÝޱƪoÜ·:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = ....;
+new SchemaValidator(cfg).validate();]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-9">
+ <title>XL[}Ìof[VÉAntðgpµÜ·</title>
+
+ <para>
+ AntXNvg©ç <literal>SchemaValidator</literal> ðÄÑo¹Ü·:
+ </para>
+
+ <programlisting><![CDATA[<target name="schemavalidate">
+ <taskdef name="schemavalidator"
+ classname="org.hibernate.tool.hbm2ddl.SchemaValidatorTask"
+ classpathref="class.path"/>
+
+ <schemavalidator
+ properties="hibernate.properties">
+ <fileset dir="src">
+ <include name="**/*.hbm.xml"/>
+ </fileset>
+ </schemaupdate>
+</target>]]></programlisting>
+
+ </sect2>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/transactions.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/transactions.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/transactions.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1276 @@
+<?xml version="1.0" encoding="Shift_JIS"?>
+<chapter id="transactions" revision="2">
+ <title>gUNVÆÀs«</title>
+
+ <para>
+ HibernateƯÀs§äÉ¢ÄÅàdvÈ_ÍAeÕÉðÅ«é±ÆÅ·B
+ HibernateÍV½ÈbNÌUé¢ðÇÁµÄ¨ç¸A¼ÚJDBCRlNVÆJTA\[XðgpµÜ·B
+ JDBCAANSIA¨æÑf[^x[XÇVXeiDBMSjÌgUNVª£Ìdlð
+ µÔð©¯Ä×·é±Æð§µÜ·B
+ </para>
+
+ <para>
+ HibernateÍàÌIuWFNgðbNµÜ¹ñB
+ AvP[VÍAf[^x[XgUNV̪£xÅ
+ è`µ½Ué¢ðúÒÅ«Ü·B
+ gUNVXR[vÌLbV
Åà é <literal>Session</literal> ̨AÅA
+ ¯ÊqâNGÉæèõµ½GeBeBÍs[^u[hÉÈèÜ·
+ iXJ[lðÔ·æ¤È|[gNGÍá¢Ü·jB
+ </para>
+
+ <para>
+ o[WjOÉæé©®IÈyÏI¯Às§äÉÁ¦ÄA
+ <literal>SELECT FOR UPDATE</literal> ¶ðgpµÄA
+ sðßÏIbN·é½ßÌi}Ci[ÈjAPIàñµÜ·B
+ yÏI¯Às§äƱÌAPIÉ¢ÄÍA±ÌÍÌãÌÙ¤Åc_µÜ·B
+ </para>
+
+ <para>
+ f[^x[XgUNVâ·¢ÎbiconversationAOgUNVj¾¯ÅÈA
+ <literal>Configuration</literal>A<literal>SessionFactory</literal>A¨æÑ
+ <literal>Session</literal>
+ Æ¢¤±xÅHibernateªs¤¯Às§äÌc_ðnßÜ·B
+ </para>
+
+ <sect1 id="transactions-basics" revision="1">
+ <title>sessionXR[vÆtransactionXR[v</title>
+
+ <para>
+ <literal>SessionFactory</literal> Ͷ¬·é±Æª¿ÅA
+ XbhZ[tÈIuWFNgÅ·B
+ æÁÄAAvP[VÌ·×ÄÌXbhŤL·×«Å·B
+ ÊíAAvP[VÌN®ÉA
+ <literal>Configuration</literal> CX^X©çPx¾¯¶¬µÜ·B
+ </para>
+
+ <para>
+ <literal>Session</literal> Í¿ÅÍÈAXbhZ[tÈIuWFNgÅà èܹñB
+ æÁÄAPÂÌvâPÂÌÎbAPÂÌìÆPÊiunit of workjÉεÄPx¾¯g¢A
+ »ÌãÅÌÄé׫ŷB
+ <literal>Session</literal> ÍKvÉÈéÜÅA
+ JDBC <literal>Connection</literal>iàµÍ <literal>DataSource</literal>jðl¾µÜ¹ñB
+ ä¦ÉAÀÛÉgp·éÆ«ÜÅ\[XðÁïµÜ¹ñB
+ </para>
+
+ <para>
+ ±Ìóµð®¹³¹é½ßÉA f[^x[XgUNVÉ¢Äàl¦È¯êÎÈèܹñB
+ f[^x[XàÌbNÌ£ðÈ·é½ßÉA
+ f[^x[XgUNVÍÂ\ÈÀèZ·é׫ŷB
+ ·¢f[^x[XgUNVÍAAvP[VÌ¢ÀñÀs«ðjQµÜ·B
+ ä¦ÉA[U[ªl¦Ä¢éÔiìÆPʪ®¹·éÜÅjf[^x[XgUNVð
+ J¢½ÜÜÉ·éÌÍA½¢Ä¢Ìêæ¢ÝvÆÍ¢¦Ü¹ñB
+ </para>
+
+ <para>
+ ìÆPÊÆ¢¤XR[vÆͽŵ天H
+ PÂÌHibernate <literal>Session</literal> ÍA
+ ¢Â©Ìf[^x[XgUNVðܽªé±ÆªÅ«éŵ天H
+ ܽÍAXR[vÆêÎêÌÖWŵ天H
+ ¢Â <literal>Session</literal> ðJ«A¶é׫ŵ天H
+ »µÄAf[^x[XgUNV«EðÇÌæ¤Éª¯éÌŵ天H
+ </para>
+
+ <sect2 id="transactions-basics-uow" revision="1">
+ <title>ìÆPÊiUnit of workj</title>
+
+ <para>
+ PÂÚÍA<emphasis>session-per-operation</emphasis> A`p^[ðgÁÄÍ¢¯Ü¹ñB
+ ·Èí¿APÂÌXbhÌÅAPÈf[^x[XÄÑoµÌ½ÑÉ
+ <literal>Session</literal> ðJ¢ÄA¶ÄÍ¢¯Ü¹ñI
+ à¿ëñAf[^x[XgUNVÉ¢Äà¯lÅ·B
+ AvP[VÌf[^x[XÄÑoµÍA
+ væ³ê½V[PXiplanned sequencejðg¢A
+ Ag~bNÈìÆPÊɪ޳êÜ·B
+ iPÂÌSQL¶²ÆÉR~bg·é©®R~bgªA
+ gíêȢƢ¤Ó¡Åà é±ÆÉӵľ³¢B
+ ©®R~bgÍASQLR\[ÅAhzbNÈìÆð·éÛÉg¤àÌÅ·B
+ Hibernateͼ¿É©®R~bg[hð³øɵܷB
+ àµÍAAvP[VT[o[ª³ø»·é±ÆðúҵܷBj
+ f[^x[XgUNVÍIvVÅÍ èܹñB
+ f[^x[XÆÌ·×ÄÌÊMÍAf[^ÌÇÝÝÅ ÁÄàA«ÝÅ ÁÄàA
+ gUNVÌÅsíȯêÎÈèܹñB
+ ྷéÆAf[^ÇÝÝÉεÄA©®R~bgÍð¯é׫ŷB
+ ȺÈçA½Ì¬³ÈgUNVÍA¾mÉè`³ê½PÂÌìÆPÊÆä×ÄA
+ ptH[}XªæÈé±ÆÍ èܹñB
+ ãÒÍÛç«âg£«àæè·®êĢܷB
+ </para>
+
+ <para>
+ }`[U[ÌNCAg^T[o[AvP[VÌÅA
+ Åàægíêép^[ÍA<emphasis>session-per-request</emphasis> Å·B
+ ±ÌfÌÅÍA
+ NCAg©çiHibernatei±»wª®ì·éjT[o[ÖNGXgªçêA
+ Vµ¢Hibernate <literal>Session</literal> ªJ©êÜ·B
+ »µÄA±ÌìÆPÊÌÅ·×ÄÌf[^x[XªÀs³êÜ·B
+ ìƪ®¹µ½i»µÄANCAgÖÌX|XªõÅ«½j_ÅA
+ session ðtbV
µA¶ܷB
+ NCAgÌvð·é½ßÉAPÂÌf[^x[XgUNVðgp·éŵå¤B
+ <literal>Session</literal> ðJ«A¶éÛÉA
+ f[^x[XgUNVðJnµAR~bgµÜ·B
+ ñÂÌÖWÍêÎêÅ·B
+ ±ÌfͽÌAvP[VÉ®SÉKµÜ·B
+ </para>
+
+ <para>
+ È~ÌÀÉ`WµÄ¾³¢B
+ <literal>Session</literal> ð¿AgUNVð³mÉJnµAIíç¹é¾¯ÅÈA
+ f[^ANZXìà¾â··éKvª èÜ·B
+ NGXgªT[o[Éqbgµ½ÛÆAX|XðM·éOÉAÀs³êéinterceptor
+ iá¦ÎA<literal>ServletFilter</literal>jðgÁÄAìÆPÊÌ«EðÀ·é̪zIÅ·B
+ <literal>ThreadLocal</literal> ÏðgÁÄA
+ NGXgð·éXbhÉ <literal>Session</literal> ðѯé±Æð§µÜ·B
+ ±êÉæèAXbhàÅÀs³êé·×ÄÌR[hÅA
+ istaticÏÉANZX·éæ¤ÉjÈPÉ <literal>Session</literal> ÉANZXÅ«éæ¤ÉÈèÜ·B
+ Iðµ½f[^x[XgUNV«EÌ@\æÅA<literal>ThreadLocal</literal>
+ ÏÌÉgUNVReLXgðÛ·é©àµêܹñB
+ ±ÌÀp^[ÍA<emphasis>ThreadLocal Session</emphasis> ¨æÑ
+ <emphasis>Open Session in View</emphasis> Æ¢¤¼OÅmçêĢܷB
+ {hL
gÅæÉ©¹½ <literal>HibernateUtil</literal> wp[NXðg£·é±ÆÅA
+ ±Ìp^[ðeÕÉÀÅ«Ü·B
+ à¿ëñAinterceptor ðÀ·éû@ð©Â¯A«ðZbgAbv·éKvª èÜ·B
+ HibernateÌWebTCgÉ étipsÆáðQƵľ³¢B
+ </para>
+
+ <para>
+ È~ÌÀÉ`WµÄ¾³¢B
+ HibernateÍPȱÌp^[̽ßÉA\ßgÝÜê½
+ ucurrent sessionvÌÇðñµÜ·B
+ T[o[NGXgð·éÛÍgUNVðJnµÈ¯êÎÈèܹñB
+ »µÄAX|XðNCAgÉM·éOÉgUNVðIíç¹Ü·B
+ D«Èû@ÅÀ»Å«Ü·BêÊIÈðôÍ <literal>ServletFilter</literal> â
+ T[rX\bhð|CgJbgµÄAOPC^[Zv^[A
+ proxy/interception ReiÅ·B
+ EJBReiÍEJBZbVr[ðgUNV«EƵÄ
+ AXyNgðNXJbg·éÀÌWIÈû@Å·iCMTÉæéé¾IjB
+ vOÉæégUNV«Eðg¤Æß½êA
+ ÈPÉg¤½ßAÝ·«Ì éR[hÉ·é½ßÉA
+ ±ÌÍÌãÌÙ¤É éHibernate <literal>Transaction</literal> APIÌÙ¤ªæ¢Å·B
+ </para>
+
+ <para>
+ AvP[VÌR[hÍAKvÈÆ«ÉDZÅàA½ñÅàA
+ PÉ <literal>sessionFactory.getCurrentSession()</literal> ðÄÑo·¾¯Å
+ u»ÝÌZbVvÉANZXÅ«Ü·B
+ »ÝÌf[^x[XgUNVÖÌ <literal>ZbV</literal> ðíÉ澵ܷB
+ \[XE[JÈ«AàµÍJTA«ð\¬µÈ¯êÎÈèܹñ
+ i<xref linkend="architecture-current-session"/> ðQƵľ³¢jB
+ </para>
+
+ <para>
+ Æ«Ç«Aur
[ð`æ·évÜÅ <literal>ZbV</literal>
+ Æf[^x[XgUNVÌXR[vðg£·éÆÖȱƪ èÜ·B
+ ±êÍAvÌÆ`æÌtF[Yðª¯Ä¢é
+ T[ubgAvP[Vɨ¢ÄÁÉð§¿Ü·B
+ Æ©ÌC^[Zv^ðÀ·êÎA
+ r
[ð`æ·éÜÅf[^x[XgUNVðg£·éÌÍÈPÅ·B
+ µ©µAReiÇgUNVÌEJBÉéêÍAÈPÉÍūܹñB
+ ȺÈçAr
[Ì`æðJn·éOÉAEJBÌ\bhª^[µ½ÛÉA
+ gUNVª®¹·é½ßÅ·B
+ ±Ì <emphasis>Open Session in View</emphasis> p^[ÉÖA·éqgÆáÉ¢ÄÍA
+ HibernateÌWebTCgâtH[ðQƵľ³¢B
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-basics-apptx" revision="1">
+ <title>·¢Îb</title>
+
+ <para>
+ session-per-requestp^[ÍAìÆPÊðÝv·éÛÉð§Âl¦Æ¢¤¾¯ÅÍ èܹñB
+ ½ÌrWlXvZXÍA[U[ÆÌêAÌÝìpSÌðvµÜ·B
+ »ÌÝìpÉÍAf[^x[XANZXªÜÜêÜ·B
+
+ WebÆG^[vCYAvP[VÅÍAf[^x[XgUNVª
+ [UÆÌÝìpÉÜÅné±ÆͳêܹñB
+
+ Ìáðæl¦ÄÝľ³¢B
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ _CAOÌÅÌæʪJ«AÂXÌ <literal>Session</literal>
+ Æf[^x[XgUNVÌÅ[h³ê½f[^ð[U[É©¹Ü·B
+ [U[ÍIuWFNgð©RÉC³Å«Ü·B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ 5ªãÉ[U[ÍuSavevðNbNµAC³ªi±»³êéÌðúҵܷB
+ ܽA±ÌîñðÒWµ½ÌÍ©ªPl¾¯ÅA
+ C³ÌRtNgͶµÈ¢ÆúҵܷB
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ±ÌìÆPÊði[U[Ì_Åj·úÌ <emphasis>Îb</emphasis>
+ iàµÍA<emphasis>AvP[VgUNV</emphasis> jÆÄÑÜ·B
+ AvP[VɱêðÀ·éû@ͽ³ñ èÜ·B
+ </para>
+
+ <para>
+ ÅÉv¢ÂÀÍA[U[ªl¦Ä¢éÔA<literal>Session</literal>
+ Æf[^x[XgUNVðJ¢½ÜÜɵĨ±ÆÅ·B
+ ¯ÉC³³ê¸Aª£Æ´q«ªÛسêéæ¤ÉA
+ f[^x[XàÌbNÍÛµ½ÜÜɵܷB
+ à¿ëñA±êÍA`p^[Å·B
+ ȺÈçAbNÌ£ª¶·éÆA
+ AvP[Vª¯[U[ɶÄXP[AbvÅ«ÈÈé©çÅ·B
+ </para>
+
+ <para>
+ ¾ç©ÉAÎbðÀ·é½ßÉÍA
+ ¢Â©Ìf[^x[XgUNVðgp·é׫ŷB
+ ±ÌêArWlXvZX̪£ðÛ·é±ÆÍA
+ AvP[VwÌÓ±ÌPÂÉÈèÜ·B
+ PÂÌÎbÍA
+ Êí¢Â©Ìf[^x[XgUNVÉyÑÜ·B
+ f[^x[XgUNVÌPÂÌÝiÅãÌPÂjªXVµ½f[^ðÛ¶µA
+ ¼Íf[^ðÇÞ¾¯Å êÎA»êÍAg~bNÅ·
+ iá¦ÎA¢Â©Ìv^ðJèÔ·EBU[h`®Ì_CAOjB
+ ±êÍ·æèAÀµ½Ù¤ªÈPÅ·B
+ HibernateÌ@\ðg¤ÌÅ êÎAÁÉÈPÅ·B
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>©®o[WjO</emphasis>
+ - HibernateÍ©®IÉyÏI¯Às§äªÅ«Ü·B
+ [U[ªl¦Ä¢éÔɯÉC³ª¨«½êA©®IÉoÅ«Ü·B
+ ÊíAÎbÌI¹É`FbN·é¾¯Å·B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>ª£iDetachedjIuWFNg</emphasis> - ·ÅÉc_µ½
+ <emphasis>session-per-request</emphasis> p^[ðg¤Æèµ½êA
+ [h³ê½·×ÄÌCX^XÍA[Uªl¦Ä¢éÔÍA
+ ZbV©çª£³ê½óÔÉÈèÜ·B
+ IuWFNgðZbVÉÄÇÁµAC³ði±»Å«Ü·B
+ ±êð <emphasis>session-per-request-with-detached-objects</emphasis>
+ p^[ÆÄÑÜ·B
+ ©®o[WjOðg¤±ÆÅA¯ÉsíêéC³ðª£Å«Ü·B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>g£iàµÍA·¢jZbV</emphasis>
+ - HibernateÌ <literal>Session</literal> ÍA
+ f[^x[XgUNVðR~bgµ½ãA
+ ÅÑ¢ĢéJDBCRlNVðØfÅ«Ü·B
+ »µÄANCAg©çÌVµ¢vª¶µ½ÛÉAÄڱūܷB
+ ±Ìp^[ÍA<emphasis>session-per-conversation</emphasis>
+ Æ¢¤¼ÅmçêĨèAIuWFNgðZbVÖÄÇÁ·é±Æ³¦svɵܷB
+ ©®o[WjOðg¤±ÆÅA¯ÉsíêéC³ðª£Å«Ü·B
+
+ Êí <literal>Session</literal> ð©®IÉtbV
³¹¸A
+ ¾¦IÉtbV
µÜ·B
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ <emphasis>session-per-request-with-detached-objects</emphasis> Æ
+ <emphasis>session-per-conversation</emphasis> ÌQÂÍA
+ _Æ_ðÁĢܷB
+ ±êÉ¢ÄÍA±ÌÍÌãÌÙ¤ÅAyÏI¯Às§ä̶¬ÌÅc_µÜ·B
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-basics-identity">
+ <title>IuWFNg¯Êqðl¦é</title>
+
+ <para>
+ AvP[VÍAQÂÌÙÈé <literal>Session</literal> ©ç
+ ¯¶i±óÔɯÉANZXÅ«Ü·B
+ µ©µAQÂÌ <literal>Session</literal> CX^Xª
+ i±«NXÌPÂÌCX^Xð¤L·é±ÆÍūܹñB
+ ä¦ÉA¯ÊqÉÍQÂÌÙÈéTOª éÆ¢¤±ÆÉÈèÜ·B
+ </para>
+
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term>f[^x[X¯Êq</term>
+ <listitem>
+ <para>
+ <literal>foo.getId().equals( bar.getId() )</literal>
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>JVM¯Êq</term>
+ <listitem>
+ <para>
+ <literal>foo==bar</literal>
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>
+ <emphasis>ÂXÌ</emphasis> <literal>Session</literal> ÉÇÁ³ê½IuWFNgÉÆÁÄ
+ i·Èí¿APÂÌ <literal>Session</literal> ÌXR[vÌÅÍjAQÂÌTOͯ¶Å·B
+ f[^x[X¯ê«ÆJVM¯ê«ªêv·é±ÆðAHibernateªÛصܷB
+ µ©µAAvP[VªQÂÌÙÈéZbV©ç
+ u¯¶vii±«¯ÊqÌjrWlXIuWFNgɯÉANZX·éÀèA
+ QÂÌCX^XÍÀÛÉiJVM¯ÊqªjuÙÈèvÜ·B
+ yÏIAv[`ÉæÁÄAi©®o[WjOÌjtbV
^R~bgÉ
+ RtNgªð³êÜ·B
+ </para>
+
+ <para>
+ ±ÌAv[`ÅÍAHibernateÆf[^x[XɯÀsÉ¢ÄÌSzªcèÜ·B
+ êûÅAÅÌXP[reBªñ³êÜ·B
+ ȺÈçAPXbhÌìÆPÊÌÅêÓ«ªÛسêêÎA
+ ¿ÈbNâ¯ú»ªsvÉÈé½ßÅ·B
+ <literal>Session</literal> ²ÆÉPÂÌXbhð\èt¯éÀèA
+ AvP[VÍrWlXIuWFNgðsynchronize·éKvÍ èܹñB
+ <literal>Session</literal> àÅÍAAvP[VÍIuWFNgðär·é½ßÉA
+ <literal>==</literal> ðÀSÉgpÅ«Ü·B
+ </para>
+
+ <para>
+ ¯êÇàA<literal>Session</literal> ÌOÅ <literal>==</literal>
+ ðg¤AvP[VÍA\úµÈ¢ÊÉöµÜ·B
+ ±êÍ\úµÈ¢êÅN±è¦Ü·B
+ á¦ÎAQÂ̪£CX^X𯶠<literal>Set</literal> É
+ putµ½Æ«ÈÇÅ·B
+ ¼ûÆ௶f[^x[X¯Êqð¿Ü·i·Èí¿A¯¶sð\µÜ·jB
+ µ©µAª£óÔÌCX^XÌJVM¯ÊqÍRÛسêܹñB
+ JÒÍAi±«NXÌ <literal>equals()</literal> Æ <literal>hashCode()</literal>
+ \bhðI[o[ChµAIuWFNg¿«ÌTOðÀ·×«Å·B
+ xªP èÜ·B¿«ÌÀÉf[^x[X¯ÊqðgíȢž³¢B
+ j[NÈiÊÍsÏÌj®«ÌgÝí¹Å érWlXL[ðgÁľ³¢B
+ àµAêIuWFNgªi±»³ê½êAf[^x[X¯ÊqªÏíèÜ·B
+ êIuWFNgðiÊíª£CX^XƤÉj <literal>Set</literal>
+ ÉÛ·éêAnbV
R[hªÏíéÆ¢¤±ÆÍA<literal>Set</literal>
+ Ì_ñðjéÆ¢¤±ÆÅ·B
+ rWlXL[̽ßÌ®«ÍAf[^x[XÌåL[ÙÇÀè·×«ÅÍȢŷB
+ IuWFNgª¯¶ <literal>Set</literal> ÌÉ¢éÔ¾¯AÀèðÛط׫ŷB
+ ±ÌâèÌæèOêIÈc_ÍAHibernateÌWebTCgðQƵľ³¢B
+ ܽA±êÍHibernateÌâèÅÍÈAPÉJavaIuWFNg̯Êqâ¿«ð
+ ÇÌæ¤ÉÀ·×«©Æ¢¤±ÆÅ·B
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-basics-issues">
+ <title>êÊIÈâè</title>
+
+ <para>
+ <emphasis>session-per-user-session</emphasis> Æ
+ <emphasis>session-per-application</emphasis> A`p^[
+ ÍgÁÄÍ¢¯Ü¹ñià¿ëñAÜêÉáOª èÜ·jB
+ ÓFºLÌâè̢©ÍA§³êép^[ƵÄào»µÜ·B
+ Ývðè·éOÉA ÌÓ¡ðð·éæ¤ÉµÄ¾³¢B
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>Session</literal> ÍXbhZ[tÅÍ èܹñB
+ HTTPNGXgAZbVBeanASwing[J[Ìæ¤ÉA
+ ¯ÀsªÂ\Èà̪ <literal>Session</literal> CX^Xð¤L·éÆA
+ £óÔðø«N±µÜ·B
+ iãÅc_·éj<literal>HttpSession</literal> ÌÅ
+ Hibernate <literal>Session</literal> ðÛ·éêA
+ HttpSessionÖÌANZXð¯ú»·é±Æðl¶·×«Å·B
+ ³àȯêÎA[U[ª\ª[hðNbN·éÆA
+ ¯ÉéQÂÌXbhÌÅA¯¶ <literal>Session</literal> ªgíêÜ·B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ HibernateªáOð°½êÍAf[^x[XgUNVð[obNµA
+ ¼¿É <literal>Session</literal> ð¶é׫ŷiÚ×ðãÅc_µÜ·jB
+ <literal>Session</literal> ªAvP[VÉÑt¯çêÄ¢éÌÅ êÎA
+ AvP[Vðâ~·×«Å·B
+ f[^x[XgUNVð[obNµÄàArWlXIuWFNgÍ
+ gUNVðJnµ½Æ«ÌóÔÉßèܹñB
+ ±êÍAf[^x[XÌóÔÆrWlXIuWFNgͯúµÄ¢È¢±ÆðÓ¡µÜ·B
+ Êí±êÍâèÉÈèܹñB
+ ȺÈçAáOÍñÅ«È¢©çÅ·B
+ ÆÉ©[obNµ½ãÉâ輷׫ŷB
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>Session</literal> Íi±ipersistentjóÔÌ·×ÄÌIuWFNgð
+ LbV
µÜ·iHibernateÍĵA_[eBóÔ©`FbNµÜ·jB
+ ±êÍA·¢ÔZbVðJ¢½ÜÜÉ·é©A
+ ñíɽÌf[^ð[hµ±¯é©µ½êÍA
+ OutOfMemoryExceptionª¶·éÜųÀÉå«Èé±ÆðÓ¡µÜ·B
+ ðôÌPÂÍA<literal>Session</literal> LbV
ðÇ·é½ßÉA
+ <literal>clear()</literal> © <literal>evict()</literal> ðÄÔ±ÆÅ·B
+ µ©µAå«Èf[^ð·éKvª éÈçA
+ ½ÔñXgAhvV[Wðl¶·é׫ŵå¤B
+ ¢Â©ÌðôÍA<xref linkend="batch"/> ÅÐî³êĢܷB
+ [U[ZbVÌÔA<literal>Session</literal> ðJ¢½ÜÜÉ·éÆ¢¤±ÆÍA
+ f[^ªVNÅÈÈém¦ªÈé±ÆðÓ¡µÜ·B
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="transactions-demarcation">
+ <title>f[^x[XgUNV«E</title>
+
+ <para>
+ f[^x[XiàµÍVXejgUNVÌ«EÍAíÉKvÅ·B
+ f[^x[XgUNVÌOÅAf[^x[XÆÌÊMÍN«Ü¹ñ
+ i±êÍ©®R~bg[hɵêÄ¢é½ÌJÒ𬳹é©àµêܹñjB
+ ÇÝÞ¾¯ÌìÉÅàA¢Âà¾mÈgUNV«EðgpµÄ¾³¢B
+ ª£xÆf[^x[XÌ\ÍæÅA±êÍKvÈ¢©àµêܹñªA
+ íÉgUNV«Eð¾¦IÉwèµÄàA}CiXÊÍS èܹñB
+
+ m©ÉAPÂÌf[^x[XgUNVͽ̬³ÈgUNVæè
+ if[^ÌÇÝÝÅ ÁÄàjptH[}Xª·®êĢܷB
+ </para>
+
+ <para>
+ J2EE«ÉdzêĢȢóÔ
+ i·Èí¿AX^hAAPÈWebâSwingAvP[VjÅàA
+ dzê½óÔÅàAHibernateAvP[VðÀsÅ«Ü·B
+ dzêĢȢ«ÅÍAHiberanteªf[^x[XÌRlNVv[ðñµÜ·B
+ AvP[VJÒÍAgUNV«Eðè®ÅÝèµÈ¯êÎÈèܹñB
+ ¾¢·¦éÆAf[^x[XgUNVÌJnAR~bgA[obNð
+ JÒ©gªÝè·éKvª éÆ¢¤±ÆÅ·B
+
+ ÊíAdzê½Â«ÅÍAReiÇÉæégUNViCMTjªñ³êÜ·B
+ á¦ÎAZbVBeanÌfvCgfBXNv^Åé¾IÉè`µA
+ gUNVðgݧÄÜ·B
+ vOÉæégUNV«EÍà¤Kv èܹñB
+ </para>
+
+ <para>
+ µ©µÈªçAdzêĢȢ\[XE[JÈ«Æ
+ JTAÉ˶µ½VXeiCMTÅÍÈBMTj̼ûÉA
+ i±»wð|[^uÉÛÂÌÍAµÎµÎ]ܵ¢±ÆÅ·B
+
+ fvC«ÌlCeBuÌgUNVVXeðÄÑo·
+ <literal>Transaction</literal> Æ¢¤bp[APIðHibernateªñµÜ·B
+ ±ÌAPIðg¤©ÍCÓÅ·ªACMTÌZbVBeanðgíÈ¢ÌÅ êÎAg¤±Æð§µÜ·B
+ </para>
+
+ <para>
+ ÊíA<literal>Session</literal> I¹ÍASÂÌÙÈétF[YðÜÝÜ·B
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ ZbVÌtbV
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ gUNVÌR~bg
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ZbVÌN[Y
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ áOÌnhO
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ZbVÌtbV
É¢ÄÍAOÌ٤ŷÅÉc_µÜµ½B
+ dzê½Â«ÆdzêĢȢ«̼ûÉ¢ÄA
+ gUNV«EÆáOnhOðàÁÆÚµ©Ä¢«Üµå¤B
+ </para>
+
+
+ <sect2 id="transactions-demarcation-nonmanaged" revision="2">
+ <title>dzêĢȢ«</title>
+
+ <para>
+ Hibernatei±»wðdzêĢȢ«ÅÀ·éêÍA
+
+ ÊíPÈRlNVv[i·Èí¿DataSourceÅÍÈ¢jÉæÁÄA
+ f[^x[XRlNVð§äµÜ·B
+ HibernateÍ»ÌRlNVv[©çKvÈRlNVð澵ܷB
+
+ ZbV^gUNV§äÌCfBIÍÌæ¤ÉÈèÜ·B
+ </para>
+
+ <programlisting><![CDATA[// Non-managed environment idiom
+Session sess = factory.openSession();
+Transaction tx = null;
+try {
+ tx = sess.beginTransaction();
+
+ // do some work
+ ...
+
+ tx.commit();
+}
+catch (RuntimeException e) {
+ if (tx != null) tx.rollback();
+ throw e; // or display error message
+}
+finally {
+ sess.close();
+}]]></programlisting>
+
+ <para>
+ ¾¦IÉ <literal>Session</literal> Ì <literal>flush()</literal> ðÄÑo·×«ÅÍÈA
+ <literal>commit()</literal> ðÄÑo·±ÆÉæèA©®Iɯú»ªÀs³êÜ·
+ iZbVÌ <xref linkend="objectstate-flushing">FlushMode</xref> É˶µÄ¢éjB
+
+ <literal>close()</literal> ðÄÑo·±ÆÉæèAZbVÌIíèð¾mɵܷB
+ <literal>close()</literal> ªÃÙIÉs¤åȱÆÍA
+ ZbVªJDBCRlNVðJú·é±ÆÅ·B
+
+ ãLÌJavaR[hÍ|[^uÅ èAdzêĢȢ«ÆJTA«̼ûÅÀsÅ«Ü·B
+ </para>
+
+ <para>
+ æèK«Ì éðôÍAHibernateÉ\ßgÝÜêÄ¢é
+ ucurrent sessionvReLXgÇÅ·B
+ ¾tÅྷéæèºLð©½Ù¤ª¬¢Åµå¤B
+ </para>
+
+ <programlisting><![CDATA[// Non-managed environment idiom with getCurrentSession()
+try {
+ factory.getCurrentSession().beginTransaction();
+
+ // do some work
+ ...
+
+ factory.getCurrentSession().getTransaction().commit();
+}
+catch (RuntimeException e) {
+ factory.getCurrentSession().getTransaction().rollback();
+ throw e; // or display error message
+}]]></programlisting>
+
+ <para>
+ ³KÌAvP[VÌÅÍA±Ìæ¤ÈR[hÌØê[ðµÄ©È¢Åµå¤B
+ v½IÈiVXejáOÍAíÉuÅãÊvÅLb`·×«Å·B
+ ¾¢·¦êÎAii±»wÅjHibernateÄÑoµðÀs·éR[hÆA
+ <literal>RuntimeException</literal> ð§ä·é
+ iÊíÍN[AbvÆI¹ÌÝs¤±ÆªÅ«éjR[hÍAÊXÌwÌÉ èÜ·B
+ HibernateÉæéJgReLXgÇÍA±ÌÝvð©ÈèPɵܷB
+ KvÈÌÍA<literal>SessionFactory</literal> ÉANZX·é±Æ¾¯Å·B
+ áOÍA±ÌÍÌãÌÙ¤Åc_µÜ·B
+ </para>
+
+ <para>
+ ÓFiftHgÅ·ªj
+ <literal>org.hibernate.transaction.JDBCTransactionFactory</literal>
+ ðIð·é׫ŷB
+ æ2ÌpáƵÄÍA
+ <literal>hibernate.current_session_context_class</literal> ð
+ <literal>"thread"</literal> Æ·éÆæ¢Åµå¤B
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-demarcation-jta" revision="3">
+ <title>JTAðgp·é</title>
+
+ <para>
+ i±»wðAvP[VT[o[iá¦ÎAEJBZbVBeanÌwãjÅÀs·éêA
+ Hibernate©çæ¾·é·×ÄÌf[^\[XRlNVÍA
+ ©®IÉO[oJTAgUNVÌêÉÈèÜ·B
+
+ EJBðgí¸ÉAX^hAÌJTAÀð±ü·é±ÆàÅ«Ü·B
+ JTA̽ßÉAHibernateÍQÂÌíªðñµÜ·B
+ </para>
+
+ <para>
+ BeanÇgUNViBMTjðg¢A<literal>Transaction</literal> APIðg¤êA
+ HibernateÍAvP[VT[o[ÉBMTgUNVÌJnÆIíèð°Ü·B
+ ·Èí¿AgUNVÇÌR[hÍAdzêȢ«Ư¶ÉÈèÜ·B
+ </para>
+
+ <programlisting><![CDATA[// BMT idiom
+Session sess = factory.openSession();
+Transaction tx = null;
+try {
+ tx = sess.beginTransaction();
+
+ // do some work
+ ...
+
+ tx.commit();
+}
+catch (RuntimeException e) {
+ if (tx != null) tx.rollback();
+ throw e; // or display error message
+}
+finally {
+ sess.close();
+}]]></programlisting>
+
+ <para>
+ gUNV«EÆµÄ <literal>Session</literal> ðg¢½¢êA
+ ÈPÉReLXgð`d·é@\Å é <literal>getCurrentSession()</literal> ª éÌÅA
+ JTAÌ <literal>UserTransaction</literal> APIð¼Úgp·×«Åµå¤B
+ </para>
+
+ <programlisting><![CDATA[// BMT idiom with getCurrentSession()
+try {
+ UserTransaction tx = (UserTransaction)new InitialContext()
+ .lookup("java:comp/UserTransaction");
+
+ tx.begin();
+
+ // Do some work on Session bound to transaction
+ factory.getCurrentSession().load(...);
+ factory.getCurrentSession().persist(...);
+
+ tx.commit();
+}
+catch (RuntimeException e) {
+ tx.rollback();
+ throw e; // or display error message
+}]]></programlisting>
+
+ <para>
+ CMTÅÍAgUNV«EðZbVBeanÌfvCgfBXNv^Åè`µA
+ vOÅÍs¢Ü¹ñB
+ ä¦ÉAR[hÍÌæ¤ÉÈÈèÜ·B
+ </para>
+
+ <programlisting><![CDATA[// CMT idiom
+ Session sess = factory.getCurrentSession();
+
+ // do some work
+ ...
+]]></programlisting>
+
+ <para>
+ CMT/EJBÌÅÍAíÉ[obNª©®IÉÀ{³êÜ·B
+ ȺÈçAZbVr[Ì\bhÉæè°ç꽧ä³êĢȢ
+ <literal>RuntimeException</literal> ÍAO[ogUNVð
+ [obN·éæ¤ÉReiÉ`¦é½ßÅ·B
+ <emphasis>±êÍABMTàµÍCMTÆ¢ÁµåÉHibernate <literal>Transaction</literal>
+ APIðg¤Kv͵àȢƢ¤Ó¡Å·B</emphasis>
+ </para>
+
+ <para>
+ HibernateÌgUNVt@NgðÝè·éÛÉA
+ JTAð¼Úg¤iBMTÌjêÍ <literal>org.hibernate.transaction.JTATransactionFactory</literal> ðA
+ CMTZbVr[ÌÅÍ <literal>org.hibernate.transaction.CMTTransactionFactory</literal> ð
+ Ið·×«¾Æ¢¤±ÆÉӵľ³¢B
+ <literal>hibernate.transaction.manager_lookup_class</literal>
+ ðZbg·é±Æàv¢oµÄ¾³¢B
+ ȨA<literal>hibernate.current_session_context_class</literal> ÍA
+ ZbgµÈ¢©iãûÝ·jA<literal>"jta"</literal> ðZbgµÄ¾³¢B
+ </para>
+
+ <para>
+ <literal>getCurrentSession()</literal> Iy[VÍAJTA«ÅÍPÂÌ_ð¿Ü·B
+
+ ftHgÅgíêé <literal>after_statement</literal> RlNV[X[hðgp·éãÅA
+ xªP èÜ·B
+
+ JTAdlÌð©È§ñ̽ßÉA
+ <literal>scroll()</literal> Ü½Í <literal>iterate()</literal> ªÔµ½A
+ ¶çêĢȢ <literal>ScrollableResults</literal> Ü½Í <literal>Iterator</literal>
+ CX^XðHibernateª©®IÉN[Abv·é±ÆÍūܹñB
+
+ <literal>finally</literal> ubNÌÅA
+ <literal>ScrollableResults.close()</literal> ܽÍ
+ <literal>Hibernate.close(Iterator)</literal> ð¾¦IÉÄÑoµÄA
+ Éöñ¾f[^x[XJ[\ððú <emphasis>µÈ¯êÎÈèܹñ</emphasis>B
+
+ ià¿ëñA½ÌAvP[VÅÍAJTA©CMTR[hÅ <literal>scroll()</literal>
+ â <literal>iterate()</literal> Ìgpðð¯éÌÍeÕÅ·Bj
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-demarcation-exceptions">
+ <title>áOnhO</title>
+
+ <para>
+ <literal>Session</literal> ªáOi<literal>SQLException</literal>ðÜÞjð°½êA
+ ¼¿ÉAf[^x[XgUNVð[obNµA<literal>Session.close()</literal>
+ ðÄÑA<literal>Session</literal> CX^Xðjü·×«Å·B
+ <literal>Session</literal> ̢©Ì\bhÍA
+ ZbVÌóÔð <emphasis>µµ½ÜÜ</emphasis> ɵܷB
+ Hibernateª°½áOðAñÅ«éàÌƵĵ¤±ÆÍūܹñB
+ <literal>finally</literal> ubNÌÅ <literal>close()</literal> ðÄñÅA
+ <literal>Session</literal> ðmÀɶľ³¢B
+ </para>
+
+ <para>
+ <literal>HibernateException</literal> ÍAHibernatei±»wÌŶ·é½ÌG[ð
+ bv·éA¸³êÈ¢áOÅ·iHibernateÌâo[WÍá¢Üµ½jB
+ ½¿ÌÓ©ÍAAvP[VJÒÉñsÂ\ÈáOð
+ ºwÅLb`·é±Æðv·×«ÅÍȢƢ¤±ÆÅ·B
+ ½ÌVXeÅÍA¸³êÈ¢áOÆv½IÈáOÍA
+ R[X^bNÌÅÌt[ÌPÂiá¦ÎAÅãÊÌwÅjÅnhOµA
+ G[bZ[WðAvP[V[U[É\¦µÜ·
+ iàµÍA¼ÌKØÈðÀ{µÜ·jB
+ HibernateÍA<literal>HibernateException</literal> ÈO̸³êÈ¢áOà
+ °é±ÆÉӵľ³¢B
+ ±êçàܽAñsÂ\Å èAKØÈðÀ{·×«Å·B
+ </para>
+
+ <para>
+ HibernateÍAf[^x[XÆÌÎbÉ°çê½ <literal>SQLException</literal> ð
+ <literal>JDBCException</literal> ÅbvµÜ·B
+ ÀÍAáOðæèÓ¡Ì é <literal>JDBCException</literal> ÌTuNXÉ
+ Ï·µæ¤ÆÝÜ·B
+ ³Ì <literal>SQLException</literal> ÍA<literal>JDBCException.getCause()</literal>
+ Éæè¢ÂÅà¾çêÜ·B
+ HibernateÍA<literal>SessionFactory</literal> ÉÇÁ³êÄ¢é
+ <literal>SQLExceptionConverter</literal> ðg¢A<literal>SQLException</literal> ð
+ KÈ <literal>JDBCException</literal> TuNXÉÏ·µÜ·B
+ ftHgÅÍA<literal>SQLExceptionConverter</literal> ÍÝè³êÄ¢éSQLû¾Éæè
+ è`³êÜ·B
+ êûÅAÆ©ÌÀÉ·µÖ¦é±ÆàÅ«Ü·
+ iÚ×ÍA<literal>SQLExceptionConverterFactory</literal> NXÌJavadocðQƵľ³¢jB
+ WIÈ <literal>JDBCException</literal> ÌTu^CvðºLɦµÜ·B
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>JDBCConnectionException</literal> -
+ îbÆÈéJDBCÊMÌG[ð\µÜ·B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>SQLGrammarException</literal> -
+ s·éSQL̶@àµÍ\¶Ìâèð\µÜ·B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>ConstraintViolationException</literal> -
+ ½ç©Ì`®Ì®S«§ñá½ð\µÜ·B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>LockAcquisitionException</literal> -
+ v³ê½ìðÀ{·éÌÉKvÈbNxð¾éÛÌG[ð\µÜ·B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>GenericJDBCException</literal> -
+ ¼ÌJeSÉêvµÈ©Á½êÊIÈáOÅ·B
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect2>
+
+ <sect2 id="transactions-demarcation-timeout">
+ <title>gUNVÌ^CAEg</title>
+
+ <para>
+ EJBÌæ¤Èdzê½Â«ªñ·é«íßÄdvÈÁ¥ÌPÂÍA
+ gUNVÌ^CAEgÅ·B
+ ±êÍdzêĢȢR[hÉÍñūܹñB
+ gUNV^CAEgÍAsisÈgUNVª
+ [U[ÉX|XðԳȢÜÜA³úÀÉ\[Xðg¢±¯È¢
+ ±ÆðÛáµÜ·B
+ dzê½Â«iJTAjÌOÅÍAHibernateͱÌ@\ðtÉñūܹñB
+ µ©µÈªçAHibernateÍÌæ¤Èf[^ANZXì̧äç¢ÍÅ«Ü·B
+ f[^x[XxÌfbhbNâå«ÈUgZbgðÔ·NGð
+ è`³ê½^CAEgÉæÁÄmÀɧÀµÜ·B
+ dzê½Â«ÅÍAHibernateÍgUNV^CAEgðJTAÉÏ÷µÜ·B
+ ±Ì@\ÍAHibernateÌ <literal>Transaction</literal> IuWFNgÉ
+ æÁÄÛ»³êĢܷB
+ </para>
+
+ <programlisting><![CDATA[
+Session sess = factory.openSession();
+try {
+ //set transaction timeout to 3 seconds
+ sess.getTransaction().setTimeout(3);
+ sess.getTransaction().begin();
+
+ // do some work
+ ...
+
+ sess.getTransaction().commit()
+}
+catch (RuntimeException e) {
+ sess.getTransaction().rollback();
+ throw e; // or display error message
+}
+finally {
+ sess.close();
+}]]></programlisting>
+
+ <para>
+ CMTr[ÌÅÍ <literal>setTimeout()</literal> ð
+ ÄÑo¹È¢±ÆÉӵľ³¢B
+ gUNV^CAEgÍé¾IÉè`³êé׫ŷB
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="transactions-optimistic">
+ <title>yÏI¯Às§ä</title>
+
+ <para>
+ ¢Àñ«Æ¢XP[reB̼ûðÀ»·éAv[`ÍA
+ o[WjOðgÁ½yÏI¯Às§äÌÝÅ·B
+ XVÌÕËð©Â¯é½ßÉi¨æÑAXVª¸íêéÌðh®½ßÉjA
+ o[WÔàµÍ^CX^vðgÁÄAo[Wð`FbNµÜ·B
+ HibernateÍAyÏI¯Àsðs¤AvP[VR[hð½ßÌ
+ Av[`ðRÂñµÜ·B
+ ½¿ª©¹é[XP[XÍA·¢Îbð¿Ü·ªA
+ o[W`FbNÍܾPÂÌf[^x[XgUNVÌÅXV𸤱Æðh®
+ _àÁĢܷB
+ </para>
+
+ <sect2 id="transactions-optimistic-manual">
+ <title>AvP[VÉæéo[W`FbN</title>
+
+ <para>
+ HibernateÉÙÆñǯÄàçí¸ÉÀ·éP[XÅ·B
+ f[^x[XÆÌâèæèÍA»ê¼êVµ¢ <literal>Session</literal> ÌÅN±èÜ·B
+ JÒÍA·×ÄÌi±«CX^Xðì·éOÉA
+ f[^x[X©çÄÇÝÝ·éÓ±ª èÜ·B
+ ±ÌAv[`ÅÍAÎbgUNV̪£ðçé½ßÉA
+ AvP[V©gªo[W`FbNðs¤Kvª èÜ·B
+ ±ÌAv[`ÍAf[^x[XANZXÌÅÍAÅàñø¦Å·B
+ GeBeBEJBÆÅàÄ¢éAv[`Å·B
+ </para>
+
+ <programlisting><![CDATA[// foo is an instance loaded by a previous Session
+session = factory.openSession();
+Transaction t = session.beginTransaction();
+
+int oldVersion = foo.getVersion();
+session.load( foo, foo.getKey() ); // load the current state
+if ( oldVersion!=foo.getVersion ) throw new StaleObjectStateException();
+foo.setProperty("bar");
+
+t.commit();
+session.close();]]></programlisting>
+
+ <para>
+ <literal><version></literal> ðgÁÄA<literal>version</literal>
+ vpeBð}bsOµÜ·B
+ HibernateÍAGeBeBª_[eBÅ éêAtbV
µA
+ »ÌÔÉ <literal>version</literal> vpeBð©®IÉCNgµÜ·B
+ </para>
+
+ <para>
+ à¿ëñAf[^ÌÀñ«ªá¢Â«Å^pµÄ¨èAo[W`FbNªsvÈçA
+ ±ÌAv[`ðg¢Ao[W`FbNðXLbv·é¾¯Å·B
+ »ÌêÍA·¢ÎbÉÍA
+ <emphasis>uÅãÉR~bgµ½à̪Âv</emphasis> ªftHgÌíªÅµå¤B
+ ±ÌAv[`ÍAAvP[VÌ[U[𬳹é©àµêÈ¢±Æð
+ SɯßÄu¢Ä¾³¢B»êÍAG[bZ[W⣵½ÏXð}[W·é@ïª
+ È¢ÜÜAXVð¸¤o±ð·é½ßÅ·B
+ </para>
+
+ <para>
+ m©ÉA}j
AÉæéo[W`FbNÍA±×ÈV®¾¯ÅÀsÅ«Ü·ªA
+ ½ÌAvP[VÉÆÁÄÀpIÅÍ èܹñB
+ µÎµÎAPÂÌCX^X¾¯ÅÈA
+ C³³ê½IuWFNgÌ®SÈOtð`FbNµÈ¯êÎÈèܹñB
+ HibernateÍAÝvp_CƵÄAg£ <literal>Session</literal> ©
+ ª£³ê½CX^Xð©®IÉo[W`FbNµÜ·B
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-optimistic-longsession">
+ <title>g£ZbVÆ©®o[WjO</title>
+
+ <para>
+ PÂÌ <literal>Session</literal> CX^XÆ»Ìi±«CX^XÍA
+ <emphasis>session-per-conversation</emphasis> ƵÄmçêéA
+ ÎbSÌÅgíêÜ·B
+ HibernateÍtbV
·éÛÉACX^XÌo[Wð`FbNµÜ·B
+ ¯ÉC³³ê½±Æðo·éÆAáOð°Ü·B
+ ±ÌáOðLb`µÄµ¤ÌÍAJÒÌÓCÅ·
+ iêÊIÈIðÍAÏXð}[W·é©ÃÈ¢f[^ÅrWlXÎbð
+ ÄX^[g·é@ïð[U[Éñ·é±ÆÅ·jB
+ </para>
+
+ <para>
+ [U[ÌÎbðÒÁÄ¢éÆ«ÍA
+ <literal>Session</literal> ðîbÆÈéJDBCRlNV©çØ裵ܷB
+ ±ÌAv[`ÍAf[^x[XANZXÌÅÍAÅàø¦IÅ·B
+ AvP[VÍAo[W`FbN⪣³ê½CX^XðÄÇÁ·é±Æ
+ ÉÖSðÂKvÍ èܹñBܽA çäéf[^x[XgUNVÌÅ
+ CX^XðÄÇÝÝ·éKvÍ èܹñB
+ </para>
+
+ <programlisting><![CDATA[// foo is an instance loaded earlier by the old session
+Transaction t = session.beginTransaction(); // Obtain a new JDBC connection, start transaction
+
+foo.setProperty("bar");
+
+session.flush(); // Only for last transaction in conversation
+t.commit(); // Also return JDBC connection
+session.close(); // Only for last transaction in conversation]]></programlisting>
+
+ <para>
+ <literal>foo</literal> IuWFNgÍA©ªð[hµ½ <literal>Session</literal>
+ ðܾmÁĢܷB
+ âZbVÌãÅVµ¢f[^x[XgUNVðJn·é±ÆÅA
+ Vµ¢RlNVðæ¾µA»ÌZbVªÄJ³êÜ·B
+ f[^x[XgUNVðR~bg·é±ÆÅA
+ ZbV©çJDBCRlNVðØfµARlNVðv[ÉԵܷB
+ ÄÚ±µ½ãAXVµÄ¢È¢f[^Ìo[W`FbNð§·é½ßÉA
+ ¼ÌgUNVÉæèXV³êÄ¢é©àµêÈ¢IuWFNgÉÖµÄA
+ <literal>LockMode.READ</literal> ðÂ¯Ä <literal>Session.lock()</literal>
+ ðÄÑo·±ÆªÅ«Ü·B
+ XVµÄ <emphasis>¢é</emphasis> f[^ðbN·éKvÍ èܹñB
+
+ ÊíAg£ <literal>Session</literal> É <literal>FlushMode.NEVER</literal>
+ ðZbgµÜ·B
+ ÅãÌf[^x[XgUNVÌüúÅÌÝA
+ ÎbÌÅÏX³ê½·×ÄðÀÛÉi±»³¹é½ßÅ·B
+ ä¦ÉAÅãÌf[^x[XgUNVÌÝ <literal>flush()</literal>
+ Iy[VðÜÝÜ·B»µÄAÎbðIíç¹é½ßÉA
+ ZbVà <literal>close()</literal> µÜ·B
+ </para>
+
+ <para>
+ [U[ªl¶ÉAi[·é±ÆªÅ«È¢ç¢ <literal>Session</literal>
+ ªå«¢ÌÅ êÎA±Ìp^[Íâèª èÜ·B
+ á¦ÎA<literal>HttpSession</literal> ÍÂ\ÈÀ謳ÛÂ׫ŷB
+ <literal>Session</literal> Íi§IÉjPLbV
Åà èA
+ [hµ½IuWFNgð·×Ä۵ܷB
+ ¨»çANGXg^X|XÌTCNªñÅ êÎA±Ìíªªg¦Ü·B
+
+ PÂÌÎb̽߾¯É <literal>Session</literal> ðg¤×«Å·B
+ ȺÈçA·®ÉVNÅÈ¢f[^ð½ßÅ·B
+ </para>
+
+ <para>
+ iHibernateÌÈOÌo[WÍA¾¦IÈ <literal>Session</literal> Ì
+ ØfÆÄÚ±ªKv¾Á½±ÆÉӵľ³¢B
+ ±êçÌ\bhÍñ§ÉÈèܵ½B
+ ȺÈçAgUNVÌJnÆI¹Í¯¶øʪ é½ßÅ·Bj
+ </para>
+
+ <para>
+ Øfµ½ <literal>Session</literal> ði±»wÌßÅ۷׫Šé±ÆÉ
+ ӵľ³¢B
+ ¾¢·¦éÆARw«ÌÅ <literal>Session</literal> ðÛ·é½ßÉA
+ EJBXe[gtZbVBeanðgÁľ³¢B
+ <literal>HttpSession</literal> Éi[·é½ßÉAWebwÉ]µÈ¢Å¾³¢
+ iÊÌwÖÌVACYàµÈ¢Å¾³¢jB
+ </para>
+
+ <para>
+ g£ZbVp^[iàµÍA<emphasis>session-per-conversation</emphasis>jÍA
+ ©®IÈJgZbVReLXgÇðÀ{·éæèïµ¢B
+ ±Ì½ßÉA È½Í <literal>CurrentSessionContext</literal> ÌÀð·éKvª èÜ·B
+ Hibernate WikiÉ éáðQƵľ³¢B
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-optimistic-detached">
+ <title>f^b`³ê½IuWFNgÆ©®o[WjO</title>
+
+ <para>
+ Vµ¢ <literal>Session</literal>ÉæèAi±»XgAióFcajÆÌÎbª¶µÜ·B
+ ܽêûA¯¶i±«CX^XªAf[^x[XÆÌÎb²ÆÉÄp³êÜ·B
+ AvP[VÍA³XÍ¼Ì <literal>Session</literal> Å[h³êA
+ f^b`³ê½CX^XÌóÔðìµÜ·B
+ »µÄA<literal>Session.update()</literal> àµÍA<literal>Session.saveOrUpdate()</literal>A
+ <literal>Session.merge()</literal> ðgÁÄA»êçÌCX^XðÄÇÁµÜ·B
+ </para>
+
+ <programlisting><![CDATA[// foo is an instance loaded by a previous Session
+foo.setProperty("bar");
+session = factory.openSession();
+Transaction t = session.beginTransaction();
+session.saveOrUpdate(foo); // Use merge() if "foo" might have been loaded already
+t.commit();
+session.close();]]></programlisting>
+
+ <para>
+ ±ÌêàâÍèAHibernateÍtbV
·éÛÉACX^XÌo[Wð`FbNµÜ·B
+ XVÌ£ª¶µ½êÉÍAáOð°Ü·B
+ </para>
+
+ <para>
+ IuWFNgªC³³êĢȢ±ÆðmMµÄ¢éêÍA
+ <literal>update()</literal> ÌãíèÉA<literal>LockMode.READ</literal> ðgÁÄA
+ <literal>lock()</literal> ðÄÑo·±ÆàÅ«Ü·
+ i·×ÄÌLbV
ðIñµAo[W`FbNðÀ{µÜ·jB
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-optimistic-customizing">
+ <title>©®o[WjOÌJX^}CY</title>
+
+ <para>
+ }bsOÌ <literal>optimistic-lock</literal> ®«É <literal>false</literal>
+ ðÝè·é±ÆÉæèAÁèÌvpeBâRNV̽ßÉ
+ ©®o[WCNgð³øÉÅ«Ü·B
+ vpeBª_[eBÅ ÁÄàAo[WðCNgµÜ¹ñB
+ </para>
+
+ <para>
+ KV[Ìf[^x[XXL[}ÍAµÎµÎÅèIÅ èAÏXūܹñB
+ ܽÍA¼ÌAvP[Vª¯¶f[^x[XÉANZXµÈ¯êÎÈç¸A
+ »ÌAvP[VÍo[WÔâ^CX^v³¦ì·éû@ðmèܹñB
+ Ç¿çÌêàAe[uÌÁèÌJðÄɵÄAo[WjOðs¦Ü¹ñB
+ o[Wâ^CX^vÌvpeBð}bsO¹¸ÉAo[W`FbN³¹é½ßÉA
+ <literal><class></literal> }bsOÉ <literal>optimistic-lock="all"</literal> ð
+ wèµÄ¾³¢B
+ sÌ·×ÄÌtB[hÌóÔðär·éæ¤ÉÈèÜ·B
+ ±êÍAHibernateªÃ¢óÔÆVµ¢óÔðärÅ«éêÉA
+ _IÉ®ì·é¾¯Å é±ÆÉӵľ³¢B
+ á¦ÎAsession-per-request-with-detached-objects ÅÍÈA
+ PÂÌ·¢ <literal>Session</literal> ðg¤êÅ·B
+ </para>
+
+ <para>
+ Æ«Ç«Asíê½ÏXªdÈçÈ¢ÀèA¯Ésíê½ÏXðó¯üêé±ÆªÅ«Ü·B
+ <literal><class></literal> }bsOÉ <literal>optimistic-lock="dirty"</literal>
+ ðÝèµ½êAtbV
·éÛÉAHibernateÍ_[eBtB[hÌÝðärµÜ·B
+ </para>
+
+ <para>
+ êpÌo[W^^CX^vÌJðg¤êA
+ àµÍ·×Ä^_[eBÌtB[hðär·éêÇ¿çÅ ÁÄàA
+ HibernateÍGeBeB²ÆÉ1ÂÌ <literal>UPDATE</literal> ¶ð
+ iKØÈ <literal>WHERE</literal> ßƤÉjg¢A
+ o[W`FbNÆîñÌXVðs¢Ü·B
+ ÖA·éGeBeBÌÄÇÁðJXP[h·é½ßÉA
+ A½IÈi±»ðgpµ½êAsKvÈXVðÀs·é©àµêܹñB
+ ±êÍÊíâèÉÈèܹñB
+ µ©µAª£µ½CX^XðÏXµÄ¢ÈÆàA
+ f[^x[XÌ <emphasis>on update</emphasis> gK[ªÀs³êé©àµêܹñB
+ <literal><class></literal> }bsOÉ
+ <literal>select-before-update="true"</literal> ðÝè·é±ÆÉæÁÄA
+ ±ÌUé¢ðJX^}CYÅ«Ü·B
+ mÀÉÏX³ê½©ðmF·é½ßÉAsðXV·éOÉA
+ K¸CX^Xð <literal>SELECT</literal> µÜ·B
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="transactions-locking">
+ <title>ßÏIbN</title>
+
+ <para>
+ [UªbNíªÉYÞÌɽÌÔðïâ·±ÆðÓ}µÄ¢Ü¹ñB
+ ÊíÍAJDBCRlNVɪ£xðwèµA
+ PÉf[^x[XÉ·×ÄÌdð³¹êÎ\ªÅ·B
+ µ©µÈªçAxÈ[UÍAr¼IÈßÏIbNðl¾·é±Æ©A
+ Vµ¢gUNVªJn³êéÛÉbNðÄl¾·é±Æð
+ Æ«Ç«]Þ©àµêܹñB
+ </para>
+
+ <para>
+ HibernateÍ¢Âàf[^x[XÌbNÌdgÝðg¢Ü·B
+ àÌIuWFNgðµÄbNµÜ¹ñI
+ </para>
+
+ <para>
+ <literal>LockMode</literal> NXÍAHibernateªl¾Å«éÙÈébNxðè`µÜ·B
+ ȺÌdgÝÉæèAbNðl¾Å«Ü·B
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>LockMode.WRITE</literal> ÍA
+ HibernateªsðXVàµÍ}ü·éÛÉ©®IɾçêÜ·B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>LockMode.UPGRADE</literal> ÍA
+ f[^x[XÅT|[g³êÄ¢é¶@ <literal>SELECT ... FOR UPDATE</literal>
+ ðgÁ½A¾¦IÈ[U[vÉæè¾çêé©àµêܹñB
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>LockMode.UPGRADE_NOWAIT</literal> ÍA
+ Oracle Å <literal>SELECT ... FOR UPDATE NOWAIT</literal> ðgÁ½A
+ ¾¦IÈ[U[vÉæè¾çêé©àµêܹñB
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>LockMode.READ</literal> ÍA
+ Repeatable ReadàµÍSerializable̪£xÅAf[^ðÇñ¾ÛÉ©®IɾçêÜ·B
+ ¨»çA¾¦IÈ[U[vÉæèAÄæ¾³êÜ·B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>LockMode.NONE</literal> ÍAbNµÈ¢±Æð\µÜ·B
+ <literal>Transaction</literal> ÌIíèÉA
+ ·×ÄÌIuWFNgͱÌbN[hÉØèÖíèÜ·B
+ <literal>update()</literal> â <literal>saveOrUpdate()</literal> ðÄÑo·±ÆÉæÁÄA
+ ZbVÉÖAt¯çê½IuWFNgàA±ÌbN[hÅoµÜ·B
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ u¾¦IÈ[U[vvÆÍAºLÌû@ÌPž¢\¹Ü·B
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>LockMode</literal> ðwèµ½ <literal>Session.load()</literal> ÌÄÑoµB
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>Session.lock()</literal> ÌÄÑoµB
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>Query.setLockMode()</literal> ÌÄÑoµB
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ <literal>UPGRADE</literal> àµÍ <literal>UPGRADE_NOWAIT</literal> ªwè³ê½
+ <literal>Session.load()</literal> ªÄÑo³êA
+ ©Âv³ê½IuWFNgªZbVÉæÁÄܾ[h³êĢȩÁ½êÍA
+ <literal>SELECT ... FOR UPDATE</literal> ðgÁÄAIuWFNgª[h³êÜ·B
+ <literal>load()</literal> ÅÄÑo³ê½IuWFNgªA
+ v³êÄ¢éæè§ÀªÈ¢bNÅ·ÅÉ[h³êÄ¢½êÍA
+ HibernateÍ»ÌIuWFNg̽ßÉA<literal>lock()</literal> ðÄÑoµÜ·B
+ </para>
+
+ <para>
+ wè³ê½bN[hª <literal>READ</literal> àµÍA<literal>UPGRADE</literal>A
+ <literal>UPGRADE_NOWAIT</literal> ¾Á½êA<literal>Session.lock()</literal>
+ ÍAo[WÔÌ`FbNðÀ{µÜ·B
+ i<literal>UPGRADE</literal> àµÍ <literal>UPGRADE_NOWAIT</literal> ÌêA
+ <literal>SELECT ... FOR UPDATE</literal> ªgíêÜ·Bj
+ </para>
+
+ <para>
+ f[^x[Xªv³ê½bN[hðT|[gµÄ¢È¢êA
+ HibernateÍiáOð°éãíèÉAjKØÈãíèÌ[hðg¢Ü·B
+ ±êÍAAvP[Vª|[^uÅ é±ÆðÛصܷB
+ </para>
+
+ </sect1>
+
+ <sect1 id="transactions-connection-release">
+ <title>RlNVJú[h</title>
+
+ <para>
+ HibernateÌKV[i2.xjÌJDBCRlNVÇÉÖ·éUé¢ÍA
+ ÅÉKvƵ½ÛÉ <literal>Session</literal> ªRlNVð¾éÆ¢¤àÌŵ½B
+ »µÄAZbVªÂ¶çêéÜÅA»ÌRlNVð۵ܵ½B
+ Hibernate 3.xÍAZbVÉJDBCRlNVðÇÌæ¤É§ä·é©ð`¦é
+ RlNVJú[hÆ¢¤TOð±üµÜµ½B
+ È~Ìc_ÍA\¬³ê½ <literal>ConnectionProvider</literal> ðʵÄñ³êé
+ RlNVÉKØÅ é±ÆÉӵľ³¢B
+ ÙÈéJú[hÍA<literal>org.hibernate.ConnectionReleaseMode</literal> É
+ ñ³ê½lÉæèmF³êÜ·B
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>ON_CLOSE</literal> - {¿IÉãLÅq×½KV[ÌUé¢Å·B
+ HibernateZbVÍÅÉJDBCANZXðÀs·éKvª éÛÉRlNVð¾Ü·B
+ »µÄAZbVªÂ¶çêéÜÅARlNVð۵ܷB
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>AFTER_TRANSACTION</literal> -
+ <literal>org.hibernate.Transaction</literal> ª®¹µ½ãA
+ RlNVðJúµÜ·B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>AFTER_STATEMENT</literal> iÏÉIÈJúÆàÄÎêéj -
+ ·×ÄÌXe[ggª»ê¼êÀs³ê½ãARlNVªJú³êÜ·B
+ Xe[ggªZbVÉÖA·é\[XðJ¢½ÜÜÉ·éêÍA
+ ±ÌÏÉIÈJúÍXLbv³êÜ·B
+ ¡ÌƱëA±êªN±éÌÍ <literal>org.hibernate.ScrollableResults</literal>
+ ªgíêéêÌÝÅ·B
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ RtBO[Vp[^Ì <literal>hibernate.connection.release_mode</literal>
+ ÍAgp·éJú[hðwè·é½ßÉg¢Ü·B
+ wèÅ«élÍÌÊèÅ·B
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>auto</literal> iftHgj - ±êðIð·éÆ
+ <literal>org.hibernate.transaction.TransactionFactory.getDefaultReleaseMode()</literal>
+ \bhÉæÁÄÔ³êéJú[hÉÏ÷³êÜ·B
+ ±Ì\bhÍA
+ JTATransactionFactoryÉÍConnectionReleaseMode.AFTER_STATEMENTðÔµA
+ JDBCTransactionFactoryÉÍConnectionReleaseMode.AFTER_TRANSACTIONðԵܷB
+ ±ÌftHgÌUé¢ðϦĤܢÁ½µª èܹñB
+ »êÍA±ÌÝèlª´öÅN±éáQÍA
+ [UR[hÌÅoOâÔáÁ½ðÉÈèâ·¢©çÅ·B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>on_close</literal> - ConnectionReleaseMode.ON_CLOSEðg¢Ü·B
+ ±ÌÝèÍãûݷ̽ßÉc³êĢܷªAgíÈ¢±Æð©ßÜ·B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>after_transaction</literal> - ConnectionReleaseMode.AFTER_TRANSACTIONðg¢Ü·B
+ ±ÌÝèÍJTA«ÌÅÍg¤×«ÅÍ èܹñB
+ ConnectionReleaseMode.AFTER_TRANSACTIONðwèµA©®R~bg[hÌÅÍA
+ Jú[hªAFTER_STATEMENTÅ é©Ìæ¤ÉARlNVÍJú³êé±ÆÉӵľ³¢B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>after_statement</literal> - ConnectionReleaseMode.AFTER_STATEMENTðg¢Ü·B
+ ³çÉAÝè³ê½ <literal>ConnectionProvider</literal> ÍA
+ ±ÌÝè (<literal>supportsAggressiveRelease()</literal>) ðT|[g·é©Ç¤©
+ ð²×é½ßÉgpµÜ·B
+ ൻ¤ÅÈ¢êAJú[hÍConnectionReleaseMode.AFTER_TRANSACTION
+ ÉZbg³êÜ·B
+ ±ÌÝèÍÌ«ÅÌÝÀSÅ·B
+ »êÍA<literal>ConnectionProvider.getConnection()</literal> ðÄÑo·½ÑÉ
+ îÕÆÈéJDBCRlNVª¯¶àÌðæ¾Å«é©A
+ ¯¶RlNVª¾çêé±ÆªâèÆÈçÈ¢©®R~bg«ÌÅ·B
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect1>
+
+</chapter>
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/tutorial.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/tutorial.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/tutorial.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1600 @@
+<?xml version="1.0" encoding="Shift_JIS"?>
+<chapter id="tutorial">
+ <title>Hibernate̱ü</title>
+
+ <sect1 id="tutorial-intro" revision="1">
+ <title>O«</title>
+
+ <para>
+ ±ÌÍÍHibernateðßÄg¤[Uü¯ÌüåIÈ`
[gAÅ·B
+ Cf[^x[Xðg¤ÈPÈR}hCAvP[V©çnßA
+ êàêàí©èâ·¢âèûÅJðißÜ·B
+ </para>
+
+ <para>
+ ±Ì`
[gAÍHibernateðßÄg¤[UðzèµÄ¢Ü·ªA
+ ð·éÉÍJavaÆSQLÉ¢ÄÌm¯ªKvÅ·B
+ ±êÍMichael GloeglÌèÉæé`
[gAðº~«ÉµÄ¢Ü·ªA
+ ±±ÅT[hp[eBCuƾÁÄ¢éÌÍAJDK 1.4Æ5.0pÌàÌÅ·B
+ JDK 1.3ðp·éÌÅ êμÌCuªKv©àµêܹñB
+ </para>
+
+ <para>
+ `
[gAÌ\[XR[hÍHibernatefBXgr
[VÌ
+ <literal>doc/reference/tutorial/</literal> É èÜ·B
+ </para>
+
+ </sect1>
+
+ <sect1 id="tutorial-firstapp" revision="2">
+ <title>p[g1 - ßÄÌHibernateAvP[V</title>
+
+ <para>
+ ÅÉR\[x[XÌÈPÈHibernateAvP[Vð쬵ܷB
+ Javaf[^x[XiHSQL DBjðp·éÌÅA
+ f[^x[XT[oðCXg[·éKvÍ èܹñB
+ </para>
+
+ <para>
+ ¼É¬³Èf[^x[XAvP[VªKv¾ÆµÜµå¤B
+ »ÌAvP[VÉÍoȵ½¢CxgÆA
+ »ÌCxgÌzXgÉ¢ÄÌîñði[·éàÌƵܷB
+ </para>
+
+ <para>
+ ܸÅÉ·é±ÆÍJpÌfBNgðZbgAbvµÄA
+ KvÆÈé·×ÄÌJavaCuðzu·é±ÆÅ·B
+ HibernateEFuTCg©çHibernatefBXgr
[Vð_E[hµÄ¾³¢B
+ t@CððµÄ <literal>/lib</literal> É éKvÈCuÌ·×ÄðA
+ Vµ¢JpfBNgÌ <literal>/lib</literal> fBNgÉzuµÄ¾³¢B
+ ±Ìæ¤ÉÈÁÄ¢é͸ŷF
+ </para>
+
+ <programlisting><![CDATA[.
++lib
+ antlr.jar
+ cglib.jar
+ asm.jar
+ asm-attrs.jars
+ commons-collections.jar
+ commons-logging.jar
+ hibernate3.jar
+ jta.jar
+ dom4j.jar
+ log4j.jar ]]></programlisting>
+
+ <para>
+ ±êª <emphasis>{hL
g·M_ÅÌ</emphasis> HibernateÌKvÅáÀÌCuÅ·
+ iCA[JCuÌhibernate3.jaràRs[µÄ¢é±ÆÉӵľ³¢jB
+ HibernateÌo[WÉæÁÄͳçÉKvÈCuâAsvÈCuª é©àµêܹñB
+ HibernatefBXgr
[VÌ <literal>lib/</literal> fBNgÉ é
+ <literal>README.txt</literal> t@Cð©Ä¾³¢B
+ K{ܽÍIvVÌT[hp[eBCuÉ¢ÄÌîñðڹĢܷ
+ iÀÛLog4jÍK{ÅÍ èܹñªA½ÌJÒªDñŢܷjB
+ </para>
+
+ <para>
+ Éf[^x[XÉi[·éCxgð\·NXð쬵ܷB
+ </para>
+
+ <sect2 id="tutorial-firstapp-firstclass" revision="1">
+ <title>ÅÌNX</title>
+
+ <para>
+ ÅÌi±NXÍAvpeBð¢Â©ÂVvÈJavaBeanÅ·F
+ </para>
+
+ <programlisting><![CDATA[package events;
+
+import java.util.Date;
+
+public class Event {
+ private Long id;
+
+ private String title;
+ private Date date;
+
+ public Event() {}
+
+ public Long getId() {
+ return id;
+ }
+
+ private void setId(Long id) {
+ this.id = id;
+ }
+
+ public Date getDate() {
+ return date;
+ }
+
+ public void setDate(Date date) {
+ this.date = date;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+}]]></programlisting>
+
+ <para>
+ ²ÌƨèA±ÌNXÍtB[hªprivateÌ«ðÁÄ¢éÌƯÉA
+ JavaBeanWÌQb^[AZb^[\bh̽¼KñÉ]ÁĢܷB
+ ±Ìæ¤ÈÝvͧ³êĢܷªK{ÅÍ èܹñB
+ ANZT\bhðݯéÌÍt@N^Oðl¦½æ«Ì½ßÅA
+ HibernateÍtB[hɼÚANZX·é±ÆàÂ\Å·B
+ øÌÈ¢RXgN^ÍAtNVűÌNXÌCX^Xð쬷é½ßÉ
+ KvÅ·B
+ </para>
+
+ <para>
+ <literal>id</literal> vpeBÍA éÁèÌCxgÉηéj[NȯÊqÌlð۵ܷB
+ HibernateÌ®SÈ@\ðg¢½¯êÎA·×ÄÌi±GeBeBNX
+ i»êÙÇdvÅÍȢ˶NXÆ¢¤àÌà èÜ·j
+ ɱÌæ¤È¯ÊqvpeBªKvÉÈèÜ·B
+ ÀãÙÆñÇÌAvP[ViÁÉwebAvP[VjÅÍA
+ ¯ÊqÅIuWFNgðæÊ·éKvª é½ßA±êͧÀÆ¢¤æèàÁ¥Å éÆl¦é׫ŷB
+ µ©µÊíIuWFNgÌIDðì·éæ¤È±Æ͵ܹñB
+ »Ì½ßZb^[\bhÍprivateÉ·é׫ŷB
+ Hibernate¾¯ªIuWFNgªZ[u³ê½Æ«É¯ÊqÖlðãüµÜ·B
+ Hibernateªipublic, private, protectedjtB[hɼÚANZXÅ«éÌƯlÉA
+ public, private, protectedÌANZT\bhÉANZXÅ«éÆ¢¤±Æªí©éŵå¤B
+ IðÍ È½ÉC³êÄ¢éÌÅA ȽÌAvP[VÌÝvÉí¹é±ÆªÅ«Ü·B
+ </para>
+
+ <para>
+ øÌÈ¢RXgN^Í·×ÄÌi±NXÉK{Å·B
+ ±êÍHibernateªJavaÌtNVðgÁÄAIuWFNgð쬵ȯêÎÈçÈ¢½ßÅ·B
+ RXgN^ðprivateÉ·é±ÆÍÂ\Å·ªA
+ ÀsÌvLV¶¬ÆAoCgR[hìȵÌø¦IÈf[^ÌoÉÍA
+ package«ªKvÅ·B
+ </para>
+
+ <para>
+ JtH_Ì <literal>src</literal> Æ¢¤fBNgÌKØÈpbP[WÉA
+ ±ÌJava\[Xt@CðzuµÄ¾³¢B
+ ±Ì_ÅfBNgÍȺÌæ¤ÉÈÁÄ¢é͸ŷF
+ </para>
+
+ <programlisting><![CDATA[.
++lib
+ <Hibernate and third-party libraries>
++src
+ +events
+ Event.java]]></programlisting>
+
+ <para>
+ ÌXebvÅÍAHibernateɱÌi±NXÌîñð³¦Ü·B
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-mapping" revision="1">
+ <title>}bsOt@C</title>
+
+ <para>
+ HibernateÍAÇÌæ¤Éi±NXÌIuWFNgð[hµi[·êÎ梩ðméKvª èÜ·B
+ ±±ÅHibernate}bsOt@CªoêµÜ·B
+ }bsOt@CÍAf[^x[XàÌÇÌe[uÉANZXµÈ¯êÎÈçÈ¢©A
+ »Ìe[uÌÇÌJðg¤×«©ðAHibernateɳ¦Ü·B
+ </para>
+
+ <para>
+ }bsOt@CÌî{IÈ\¢Í±Ìæ¤ÉÈèÜ·F
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping>
+[...]
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Hibernate DTDªñíÉôû³êÄ¢é±ÆÉڵľ³¢B
+ ±ÌDTDÍAGfB^âIDEÅÌXML}bsOvfÆ®«ÌI[gRv[V@\ÉpÅ«Ü·B
+ ܽDTDt@CðeLXgGfB^ÅJ¯ÄÝľ³¢B
+ Æ¢¤ÌàA·×ÄÌvfÆ®«ðTϵA
+ RgâftHgÌlð©éÉÍêÔÈPÈû@¾©çÅ·B
+ HibernateÍAweb©çDTDt@Cð[h¹¸ÉA
+ ܸAvP[VÌNXpX©ç±êðTµo»¤Æ·é±ÆÉӵľ³¢B
+ DTDt@CÍHibernatefBXgr
[VÌ <literal>src/</literal>
+ fBNgƯlA<literal>hibernate3.jar</literal> ÉàÜÜêĢܷB
+ </para>
+
+ <para>
+ È~ÌáÅÍR[hðZ·é½ßÉDTDé¾ðȪµÜ·B
+ RÅ·ª±êÍIvVÅÍ èܹñB
+ </para>
+
+ <para>
+ 2ÂÌ <literal>hibernate-mapping</literal> ^OÌÔÉ
+ <literal>class</literal> vfðÜßľ³¢B
+ ·×ÄÌi±GeBeBNXiOðµÜ·ªA
+ t@[XgNXÌGeBeBÅÍȢ˶NXÆ¢¤à̪ãÙÇoêµÜ·j
+ ÍSQLf[^x[XàÌe[uÖ̱Ìæ¤È}bsOðKvƵܷB
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="events.Event" table="EVENTS">
+
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ ±êÜŽ¿ÍA<literal>Event</literal> NXÌIuWFNgð
+ <literal>EVENTS</literal> e[uÉεÄAÇÌæ¤Éi±»µ½è[hµ½è·éÌ©ð
+ Hibernateɳ¦Ä«Üµ½B»µÄÂXÌCX^XÍe[uÌsƵÄ\»³êÜ·B
+ »êÅÍø«±«e[uÌåL[Éηéj[NȯÊqvpeBð}bsOµÄ¢«Ü·B
+ ³çÉA±Ì¯Êq̵¢ÉCðg¢½È©Á½ÌƯlÉA
+ ãÌåL[JÉηéHibernate̯Êq¶¬íªðÝèµÜ·B
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="events.Event" table="EVENTS">
+ <id name="id" column="EVENT_ID">
+ <generator class="native"/>
+ </id>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ <literal>id</literal> vfͯÊqvpeBÌé¾Å èA
+ <literal>name="id"</literal> ÅJavavpeB̼Oð龵ܷB
+ HibernateͱÌvpeBÖANZX·é½ßÉQb^[AZb^[\bhðg¢Ü·B
+ J®«ÅÍ <literal>EVENTS</literal> e[uÌÇÌJðåL[ƵÄg¤Ì©ð
+ Hibernateɳ¦Ü·B
+ üêqÉÈÁÄ¢é <literal>generator</literal> vfÍA¯Êq𶬷éÌíªðwèµÜ·B
+ i±ÌáÅÍ <literal>native</literal> ðp¢Üµ½jB
+ ±ÌvfÍAÝèµ½f[^x[XidialectjÉηéÅÇȯÊq¶¬íªðIè·éàÌÅ·B
+ HibernateÍAAvP[VÅlðèÄéíªiàµÍÆ©Ég£µ½íªjƯlÉA
+ O[oÉj[NÈlðf[^x[Xɶ¬³¹éíªàT|[gµÄ¢Ü·B
+ </para>
+
+ <para>
+ ÅãÉNXÌi±vpeBÌé¾ð}bsOt@CÉÜßÜ·B
+ ftHgÅÍANXÌvpeBÍi±Æ©È³êܹñF
+ </para>
+
+ <programlisting><![CDATA[
+<hibernate-mapping>
+
+ <class name="events.Event" table="EVENTS">
+ <id name="id" column="EVENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="date" type="timestamp" column="EVENT_DATE"/>
+ <property name="title"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ <literal>id</literal> vfÌêƯlÉA <literal>property</literal> vfÌ
+ <literal>name</literal> ®«ÅAÇÌQb^[ÆZb^[\bhðg¤×«©ðHibernateɳ¦Ü·B
+ ±ÌáÅÍAHibernateÍ <literal>getDate()/setDate()</literal> Æ <literal>getTitle()/setTitle()</literal> ð
+ TµÜ·B
+ </para>
+
+ <para>
+ Ⱥ <literal>date</literal> vpeBÌ}bsOÉÍ <literal>column</literal>
+ ®«ª èA<literal>title</literal> vpeBÉÍÈ¢Ìŵ天H
+ <literal>column</literal> ®«ªÈ¯êÎAHibernateÍvpeB¼ðJ¼ÆµÄg¢Ü·B
+ ±êÍ <literal>title</literal> ÅÍãès«Ü·B
+ µ©µ <literal>date</literal> ÍÙÆñÇÌf[^x[XÅ\ñêÈÌÅA
+ ᤼OÅ}bsOµ½ûªæ¢ÌÅ·B
+ </para>
+
+ <para>
+ É»¡[¢ÌÍ <literal>title</literal> }bsOª <literal>type</literal>
+ ®«ðà¢Ä¢é_Å·B
+ }bsOt@CÅé¾µÄg¤typeÍA¨í©è©àµêܹñªJavaÌf[^^ÅÍ èܹñB
+ SQLf[^x[XÌ^Åà èܹñB
+ ±êÍ <emphasis>Hibernate}bsO^</emphasis> ÆÄÎêéA
+ Java©çSQLf[^Ì^ÖܽÍSQL©çJavaf[^^Ö|ó·éRo[^Å·B
+ JèÔµÉÈèÜ·ªAHibernateÍ <literal>type</literal> ®«ª}bsOt@CàÉȯêÎA
+ ³µ¢Ro[WÆ}bsO^ð©ªÅðµæ¤ÆµÜ·B
+ iJavaNXÌtNVðgÁ½j±Ì©®mÍA
+ êÉæÁÄÍ È½ªúÒܽÍKvÆ·éftHglÉÈçÈ¢©àµêܹñB
+ <literal>date</literal> vpeBÌꪻ¤Åµ½B
+ HibernateͱÌi <literal>java.util.Date</literal> ÌjvpeBð
+ SQLÌ <literal>date</literal> , <literal>timestamp</literal> ,
+ <literal>time</literal> ̤¿AÇÌJÉ}bsO·é׫ÈÌ©í©èܹñB
+ <literal>timestamp</literal> Ro[^ÅvpeBð}bsO·é±ÆÉæèA®SÈúðÛ¶µÜ·B
+ </para>
+
+ <para>
+ ±Ì}bsOt@CÍA<literal>Event.hbm.xml</literal> ÆµÄ <literal>Event</literal>
+ JavaNX\[Xt@CÌ·®×ÉZ[u·é׫ŷB
+ }bsOt@C̽¼û@ÍCÓÅ·ªA<literal>hbm.xml</literal>
+ TtBbNXªHibernateÌJÒÌR~
jeBàÅÌKµÆÈÁĢܷB
+ »ÝfBNg\¢Í±Ìæ¤ÉÈÁÄ¢é͸ŷF
+ </para>
+
+ <programlisting><![CDATA[.
++lib
+ <Hibernate and third-party libraries>
++src
+ +events
+ Event.java
+ Event.hbm.xml]]></programlisting>
+
+ <para>
+ HibernateÌåvÈÝèð±¯Ü·B
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-configuration" revision="2">
+ <title>HibernateÌÝè</title>
+
+ <para>
+ ±±ÜÅÅi±NXÆ}bsOt@Cªµ¢Üµ½B±ê©çHibernateÌÝèðs¢Ü·ªA
+ »ÌOÉf[^x[XªKvÅ·B
+ HSQL DBÍJavax[XÌCSQL DBMSÅ èAHSQL DBEFuTCg©ç_E[hÅ«Ü·B
+ ÀÛÉÍ_E[hµ½Ì <literal>hsqldb.jar</literal> ¾¯ªKvÅ·B
+ ±Ìt@CðJtH_Ì <literal>lib/</literal> fBNgÉzuµÄ¾³¢B
+ </para>
+
+ <para>
+ <literal>data</literal> Æ¢¤fBNgðJfBNgÌ[gÉ쬵ľ³¢B
+ HSQL DBͱ±Éf[^t@Cði[µÜ·B
+ ±Ìf[^fBNgɨ¢Ä <literal>java -classpath lib/hsqldb.jar org.hsqldb.Server</literal> ðÀsµA
+ f[^x[XðN®³¹Ä¾³¢B
+ ®ìÌJnÆATCP/IP\PbgÌoChªmFÅ«Ü·B
+ ãÙÇ쬷éAvP[Vͱ±ÉÚ±µÜ·B
+ ൱Ì`
[gAÉf[^x[Xðú»µ½¯êÎA
+ HSQL DBðVbg_EµÄiìÆEBhEÅ <literal>CTRL + C</literal> ðµÜ·j
+ <literal>data/</literal> fBNgàÌt@CðSÄÁµ½ãA
+ HSQL DBðÄN®µÜ·B
+ </para>
+
+ <para>
+ HibernateÍAvP[VÌf[^x[XÉÚ±·éwÈÌÅA
+ RlNVÌîñªKvÉÈèÜ·B
+ RlNVÍJDBCRlNVv[ðʶÄsíêÜ·ªA±êàÝè·éKvª èÜ·B
+ HibernatefBXgr
[VÉ͢©ÌI[v\[XÌJDBCRlNVv[c[ªÜÜêĢܷªA
+ ±Ì`
[gAÅÍHibernateÉgÝÜê½RlNVv[ðg¢Ü·B
+ ൻixÌi¿ÌT[hp[eBJDBCRlNVv[\tgEFAðg¢½¯êÎA
+ NXpXÉKvÈCuðRs[µÄAÙÈéRlNVv[ðÝèµÈ¯êÎ
+ ÈçÈ¢±ÆÉӵľ³¢B
+ </para>
+
+ <para>
+ HibernateÌÝèÅÍAPÈ <literal>hibernate.properties</literal> t@CA
+ »êæèµôû³êÄ¢é <literal>hibernate.cfg.xml</literal> t@CA
+ ܽͮSÉvOãÅZbgAbv·éû@ªpÅ«Ü·B
+ ÙÆñÇÌ[UªDÞÌÍXMLÝèt@CÅ·F
+ </para>
+
+ <programlisting><![CDATA[<?xml version='1.0' encoding='utf-8'?>
+<!DOCTYPE hibernate-configuration PUBLIC
+ "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
+
+<hibernate-configuration>
+
+ <session-factory>
+
+ <!-- Database connection settings -->
+ <property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
+ <property name="connection.url">jdbc:hsqldb:hsql://localhost</property>
+ <property name="connection.username">sa</property>
+ <property name="connection.password"></property>
+
+ <!-- JDBC connection pool (use the built-in) -->
+ <property name="connection.pool_size">1</property>
+
+ <!-- SQL dialect -->
+ <property name="dialect">org.hibernate.dialect.HSQLDialect</property>
+
+ <!-- Enable Hibernate's automatic session context management -->
+ <property name="current_session_context_class">thread</property>
+
+ <!-- Disable the second-level cache -->
+ <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
+
+ <!-- Echo all executed SQL to stdout -->
+ <property name="show_sql">true</property>
+
+ <!-- Drop and re-create the database schema on startup -->
+ <property name="hbm2ddl.auto">create</property>
+
+ <mapping resource="events/Event.hbm.xml"/>
+
+ </session-factory>
+
+</hibernate-configuration>]]></programlisting>
+
+ <para>
+ ±ÌXMLÌÝèªÙÈéDTDðg¤±ÆÉӵľ³¢B
+ ÁèÌf[^x[Xðó¯ÂO[ot@NgÅ éHibernateÌ
+ <literal>SessionFactory</literal> ðÝèµÜ·B
+ ൡÌf[^x[Xª éêÉÍA
+ iX^[gAbvðÈPÉ·é½ßjÊí¢Â©ÌÝèt@CàÅA
+ ¢Â©Ì <literal><session-factory></literal> ðg¤Ýèɵľ³¢B
+ </para>
+
+ <para>
+ ÅÌ4ÂÌ <literal>property</literal> vfÍJDBCRlNVÉKvÈÝèðÜñŢܷB
+ dialectÆ¢¤¼OÌ <literal>property</literal> vfÍAHibernateª¶¬·éÁèÌSQLû¾ðwèµÜ·B
+ i±IÈReLXgÉηéHibernateÌZbVÌ©®ÇÍAãÌáÅ·®Éí©éæ¤ÉAðɧ±Æŵå¤B
+ <literal>hbm2ddl.auto</literal> IvVÍf[^x[XXL[}Ì©®¶¬ðonɵܷB
+ ±êͼÚf[^x[XÉεĶ¬³êÜ·B
+ RiconfigIvVðíµÄjoffɵ½èA
+ <literal>SchemaExport</literal> Æ¢¤Ant^XN̯ðØèÄt@CÉ_CNgµ½èÅ«Ü·B
+ ÅãÉi±NX̽ßÌ}bsOt@CðÝèÉÇÁµÜ·B
+ </para>
+
+ <para>
+ ±Ìt@Cð\[XfBNgÉRs[µÄ¾³¢B
+ ·éƱêÍNXpXÌ[gÉ é±ÆÉÈèÜ·B
+ HibernateÍAX^[gAbvÉNXpXÌ[gÅ <literal>hibernate.cfg.xml</literal>
+ Æ¢¤t@Cð©®IÉTµÜ·B
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-ant" revision="1">
+ <title>AntÉæérh</title>
+
+ <para>
+ »êÅÍAntðgÁÄ`
[gAðrhµÜµå¤B
+ »êÉÍAntªCXg[³êĢȯêÎÈèܹñB
+ <ulink url="http://ant.apache.org/bindownload.cgi">Ant_E[hy[W</ulink>
+ ©ç_E[hµÄ¾³¢B
+ AntÌCXg[û@ͱ±ÅÍྵܹñÌÅA
+ <ulink url="http://ant.apache.org/manual/index.html">Ant}j
A</ulink>
+ ðQƵľ³¢B
+ AntðCXg[·êÎArht@CÌì¬ðJnÅ«Ü·B
+ ±Ìt@CÍ <literal>build.xml</literal> ÆÄÎêAJfBNgɼÚzuµÜ·B
+ </para>
+
+ <para>
+ î{IÈrht@CͱÌæ¤ÉÈèÜ·F
+ </para>
+
+ <programlisting><![CDATA[<project name="hibernate-tutorial" default="compile">
+
+ <property name="sourcedir" value="${basedir}/src"/>
+ <property name="targetdir" value="${basedir}/bin"/>
+ <property name="librarydir" value="${basedir}/lib"/>
+
+ <path id="libraries">
+ <fileset dir="${librarydir}">
+ <include name="*.jar"/>
+ </fileset>
+ </path>
+
+ <target name="clean">
+ <delete dir="${targetdir}"/>
+ <mkdir dir="${targetdir}"/>
+ </target>
+
+ <target name="compile" depends="clean, copy-resources">
+ <javac srcdir="${sourcedir}"
+ destdir="${targetdir}"
+ classpathref="libraries"/>
+ </target>
+
+ <target name="copy-resources">
+ <copy todir="${targetdir}">
+ <fileset dir="${sourcedir}">
+ <exclude name="**/*.java"/>
+ </fileset>
+ </copy>
+ </target>
+
+</project>]]></programlisting>
+
+ <para>
+ ±êÍ <literal>.jar</literal> ÅIíélibfBNgÌ·×ÄÌt@CðA
+ RpCÉgp·éNXpXÉÇÁ·é±ÆðAntɳ¦Ü·B
+ ܽAJava\[Xt@CÅÈ¢·×ÄÌt@Cð^[QbgfBNgÉRs[·éÆ¢¤±ÆÅà èÜ·B
+ á¦ÎÝèt@CâHibernate}bsOt@CÈÇÅ·B
+ ¡AntðÀs·éÆA±Ìæ¤Èoͪ é͸ŷF
+ </para>
+
+ <programlisting><![CDATA[C:\hibernateTutorial\>ant
+Buildfile: build.xml
+
+copy-resources:
+ [copy] Copying 2 files to C:\hibernateTutorial\bin
+
+compile:
+ [javac] Compiling 1 source file to C:\hibernateTutorial\bin
+
+BUILD SUCCESSFUL
+Total time: 1 second ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-helpers" revision="3">
+ <title>X^[gAbvÆwp</title>
+
+ <para>
+ ³Ä <literal>Event</literal> IuWFNgð[hµ½èi[µ½è·éõªÅ«Üµ½B
+ µ©µÜ¸ÍCtXgN`ÌR[hð¢ÄAZbgAbv𮹷éKvª èÜ·B
+ ܸÍHibernateðX^[gAbvµÈ¯êÎÈèܹñB
+ ±ÌX^[gAbvÉÍAO[oÌ <literal>SessionFactory</literal> IuWFNg𶬵ÄA
+ »êðAvP[VÌR[hÅANZXµâ·¢êÉi[·é±ÆªÜÜêÜ·B
+ <literal>SessionFactory</literal> ÍVµ <literal>Session</literal> ðI[v·é±ÆªÅ«Ü·B
+ <literal>Session</literal> ÍVOXbhÌìÆPÊ(Unit of Work)ð\»µÜ·B
+ »êÉε<literal>SessionFactory</literal> ÍXbhZ[tÌO[oIuWFNgÅ èA
+ êx¾¯CX^X»³êÜ·B
+ </para>
+
+ <para>
+ ±±ÅX^[gAbvðs¢A ÖÉ <literal>SessionFactory</literal> ÖANZX·é
+ <literal>HibernateUtil</literal> wpNXð쬵ܷB
+ Àð©ÄÝܵå¤F
+ </para>
+
+ <programlisting><![CDATA[package util;
+
+import org.hibernate.*;
+import org.hibernate.cfg.*;
+
+public class HibernateUtil {
+
+ private static final SessionFactory sessionFactory;
+
+ static {
+ try {
+ // Create the SessionFactory from hibernate.cfg.xml
+ sessionFactory = new Configuration().configure().buildSessionFactory();
+ } catch (Throwable ex) {
+ // Make sure you log the exception, as it might be swallowed
+ System.err.println("Initial SessionFactory creation failed." + ex);
+ throw new ExceptionInInitializerError(ex);
+ }
+ }
+
+ public static SessionFactory getSessionFactory() {
+ return sessionFactory;
+ }
+
+}]]></programlisting>
+
+ <para>
+ ±ÌNXÍÃIú»ubNiNXª[h³êéÆ«ÉJVMÉæÁÄêx¾¯ÄÎêéj
+ ÅO[oÌ <literal>SessionFactory</literal> 𶬷龯ÅÍÈA
+ ÃIVOgÌgpðBÁµÜ·B
+ AvP[VT[o[ÌJNDI©ç <literal>SessionFactory</literal> ð
+ bNAbv·éÌƯlÅ·B
+ </para>
+
+ <para>
+ Ýèt@CàÅ <literal>SessionFactory</literal> ɼOð^¦éÆA
+ HibernateÍ <literal>SessionFactory</literal> \zãÉJNDIÉεoChðs¨¤ÆµÜ·B
+ ±ÌR[hð®SÉr·é½ßÉÍA
+ JMXfvCgðpµÄ
+ JMXðpÅ«éReiðCX^X»µA
+ <literal>HibernateService</literal> ðJNDIÖoCh·é±ÆàÅ«Ü·B
+ ±êçÌxÈIvVÍAHibernateÌt@XhL
gÅླêĢܷB
+ </para>
+
+ <para>
+ <literal>HibernateUtil.java</literal> ðJ\[XfBNgÉ é <literal>events</literal> pbP[WÌ
+ ×ÉzuµÄ¾³¢B
+ </para>
+
+ <programlisting><![CDATA[.
++lib
+ <Hibernate and third-party libraries>
++src
+ +events
+ Event.java
+ Event.hbm.xml
+ +util
+ HibernateUtil.java
+ hibernate.cfg.xml
++data
+build.xml]]></programlisting>
+
+ <para>
+ ±êÍâèÈÄRpCÅ«é͸ŷB
+ ÅãÉMOVXeðÝè·éKvª èÜ·B
+ HibernateÍcommons loggingðg¤½ßALog4jÆJDK 1.4 loggingÌIðð·éKvª èܹñB
+ ÙÆñÇÌJÒªDÞÌÍLog4jÅ·B
+ HibernatefBXgr
[ViÌ<literal>etc/</literal> fBNgj©ç
+ <literal>log4j.properties</literal> ð È½Ì <literal>src</literal> fBNgàÌ
+ <literal>hibernate.cfg.xml</literal> Ì×ÉRs[µÄ¾³¢B
+ Ýèáð©ÄAç·Èoͪæ¯êÎÝèðÏXµÄ¾³¢B
+ ftHgÅÍHibernateÌX^[gAbvbZ[W¾¯ªWoÍÉ\¦³êÜ·B
+ </para>
+
+ <para>
+ `
[gAÌCtÍ®SÅ·B
+ HibernateðgÁÄÀÛÌìÆð·éõª®¢Üµ½B
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-workingpersistence" revision="4">
+ <title>IuWFNgÌ[hÆi[</title>
+
+ <para>
+ ¢ÉIuWFNgÌ[hÆi[ÉHibernateðg¤±ÆªÅ«Ü·B
+ <literal>main()</literal> \bhð <literal>EventManager</literal>
+ NXð«Ü·F
+ </para>
+
+ <programlisting><![CDATA[package events;
+import org.hibernate.Session;
+
+import java.util.Date;
+
+import util.HibernateUtil;
+
+public class EventManager {
+
+ public static void main(String[] args) {
+ EventManager mgr = new EventManager();
+
+ if (args[0].equals("store")) {
+ mgr.createAndStoreEvent("My Event", new Date());
+ }
+
+ HibernateUtil.getSessionFactory().close();
+ }
+
+ private void createAndStoreEvent(String title, Date theDate) {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+
+ session.beginTransaction();
+
+ Event theEvent = new Event();
+ theEvent.setTitle(title);
+ theEvent.setDate(theDate);
+
+ session.save(theEvent);
+
+ session.getTransaction().commit();
+ }
+
+}]]></programlisting>
+
+ <para>
+ Vµ¢ <literal>Event</literal> IuWFNg𶬵A»êðHibernateÉnµÜ·B
+ ¡ÅÍHibernateªSQLðµAf[^x[XÅ <literal>INSERT</literal> ðÀsµÜ·B
+ ÀsOÉ <literal>Session</literal> Æ <literal>Transaction</literal>
+ ð·éR[hðmFµÄ¾³¢B
+ </para>
+
+ <para>
+ <literal>Session</literal> Í1ÂÌìÆPÊ(Unit of Work)Å·B
+ ªÌÔAbðÈPÉ·é½ßÉA
+ <literal>Session</literal> Æ <literal>Transaction</literal> ÌÎðêÎêƼèµÜ·B
+ gpµÄ¢égUNVEVXei±ÌP[XÅÍJTAƤ¶Å«éPÈJDBCj©çR[hðÛì·é½ßÉA
+ Hibernate <literal>Session</literal> ãÅpÂ\È <literal>Transaction</literal> APIðgpµÜ·B
+ </para>
+
+ <para>
+ <literal>sessionFactory.getCurrentSession()</literal> ÍÈÉð·éÌŵ天H
+ ܸA¢Á½ñ <literal>SessionFactory</literal> ðæ¾µÛ·êÎ
+ i <literal>HibernateUtil</literal> ̨©°ÅÈPÅ·jA±Ì\bhð½xÅàA
+ DZ©çÅàÄÑo·±ÆªoÜ·B
+ <literal>getCurrentSession()</literal> \bhÍíÉu»ÝÌvìÆPÊ(Unit of Work)ðԵܷB
+ <literal>hibernate.cfg.xml</literal> ̱Ì@\ÌÝèÅA"thread"ðwèµ½±Æðv¢oµÄ¾³¢B
+ ±Ì½ß»ÝÌìÆPÊÌXR[vÍA±ÌAvP[VðÀs·é»ÝÌJavaXbhÅ·B
+ µ©µ±êÅSÄÅÍ èܹñB
+ <literal>Session</literal> ÍÅÉKvÆÈÁ½Æ«AÂÜèÅÉ <literal>getCurrentSession()</literal> ª
+ ÄÎê½Æ«ÉJnµÜ·B
+ »ÌÆ«HibernateÉæè»ÝÌXbhÉÑt¯çêÜ·B
+ gUNVªI¹iR~bgàµÍ[obNjµ½Æ«A
+ HibernateàXbh©ç <literal>Session</literal> ðØ裵AN[YµÜ·B
+ ÄÑ <literal>getCurrentSession()</literal> ðÄÔÆA
+ Vµ¢ <literal>Session</literal> ðæ¾µÄVµ¢ìÆPÊðX^[gÅ«Ü·B
+ ±Ì <emphasis>thread-bound</emphasis> vO~OEfÍHibernateðp·éãÅÅàlCª èÜ·B
+ </para>
+
+ <para>
+ gUNV̵¢Æ«EÌÚµ¢îñÉ¢ÄÍA
+ <xref linkend="transactions"/> ð©Ä¾³¢B
+ ±ÌáÅÍG[â[obNवܷB
+ </para>
+
+ <para>
+ ±ÌÅÌ[`ðÀs·éÉÍAAntÌrht@CÉÄÑoµÂ\È^[Qbgð
+ ÇÁµÈ¯êÎÈèܹñF
+ </para>
+
+ <programlisting><![CDATA[<target name="run" depends="compile">
+ <java fork="true" classname="events.EventManager" classpathref="libraries">
+ <classpath path="${targetdir}"/>
+ <arg value="${action}"/>
+ </java>
+</target>]]></programlisting>
+
+ <para>
+ <literal>action</literal> øÌlÍA^[QbgðÄÔÆ«ÉR}hCÅÝèµÜ·F
+ </para>
+
+ <programlisting><![CDATA[C:\hibernateTutorial\>ant run -Daction=store]]></programlisting>
+
+ <para>
+ RpC·éÆAHibernateªX^[gµA ȽÌÝèÉæèÜ·ªA
+ ½ÌOoͪ é͸ŷB
+ »ÌÅãÉÍȺÌsª éŵå¤F
+ </para>
+
+ <programlisting><![CDATA[[java] Hibernate: insert into EVENTS (EVENT_DATE, title, EVENT_ID) values (?, ?, ?)]]></programlisting>
+
+ <para>
+ ±êÍHibernateªÀs·é <literal>INSERT</literal> ÅA
+ NGX`}[NÍJDBCoChp[^ð\µÄ¢Ü·B
+ øƵÄoCh³êélð©é½ßA é¢ÍOÌç·«ð¸ç·½ßÉÍA
+ <literal>log4j.properties</literal> ð`FbNµÄ¾³¢B
+ </para>
+
+ <para>
+ »êÅͯ¶æ¤Éi[³ê½CxgÌêð©æ¤Æv¢Ü·B
+ »Ì½ßC\bhÉIvVðÇÁµÜ·F
+ </para>
+
+ <programlisting><![CDATA[if (args[0].equals("store")) {
+ mgr.createAndStoreEvent("My Event", new Date());
+}
+else if (args[0].equals("list")) {
+ List events = mgr.listEvents();
+ for (int i = 0; i < events.size(); i++) {
+ Event theEvent = (Event) events.get(i);
+ System.out.println("Event: " + theEvent.getTitle() +
+ " Time: " + theEvent.getDate());
+ }
+}]]></programlisting>
+
+ <para>
+ Vµ¢ <literal>listEvents()\bh</literal> àÇÁµÜ·B
+ </para>
+
+ <programlisting><![CDATA[private List listEvents() {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+
+ session.beginTransaction();
+
+ List result = session.createQuery("from Event").list();
+
+ session.getTransaction().commit();
+
+ return result;
+}]]></programlisting>
+
+ <para>
+ ±±Å·é±ÆÍAf[^x[X©ç¶Ý·é·×ÄÌ <literal>Event</literal>
+ IuWFNgð[h·éHQL (Hibernate Query Language) NGðg¤±ÆÅ·B
+ HibernateÍKØÈSQL𶬵A»êðf[^x[XÉèA
+ »Ìf[^ðgÁÄ <literal>Event</literal> IuWFNg𶬵ܷB
+ RHQLųçÉ¡GÈNGðì¬Å«Ü·B
+ </para>
+
+ <para>
+ ȺÌXebvÅA·×ÄÌÀsÆeXgðs¢Ü·B
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ hbm2ddlðÊ·OÉf[^x[XÌf[^ð쬵Af[^x[XXL[}𶬷é½ßÉA
+ <literal>ant run -Daction=store</literal> ðÀsµÄ¾³¢B
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ¡Í <literal>hibernate.cfg.xml</literal> t@CÌvpeBðRgAEgµÄhbm2ddlð³øɵľ³¢B
+ ÊíÍp±IÉPÌeXgðµÄ¢éÔÍhbm2ddlðLøɵĨÌÅ·ªA
+ »êÈOÌêÉhbm2ddlðN®·éÆi[µÄ¨¢½SÄÌf[^ð <emphasis>hbv</emphasis> ·éŵå¤B
+ Ýèð <literal>create</literal> É·éÆAÊƵÄ
+ uSessionFactory¶¬ÌÛAXL[}©çSÄÌe[uðhbvµÄÄ쬷évÆ¢¤ÝèÉÈèÜ·B
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ¡ <literal>-Daction=list</literal> ÆwèµÄAntðÄÔÆA
+ ±êÜÅi[µ½Cxgª©¦é͸ŷB
+ <literal>store</literal> ANVðñÈãÄÔ±ÆàÂ\Å·B
+ </para>
+
+ <para>
+ ÓFßÄHibernateÉGêélX̽ª±±Å¸s·é½ßA<emphasis>Table not found</emphasis> G[bZ[WÉ
+ Ö·é¿âðèúIÉ©©¯Ü·B
+ µ©µãLÌXebvÉ]¦ÎAhbm2ddlªÅÉÀs³ê½Æ«Éf[^x[XXL[}ð쬵A
+ »ÌãÌÀsɨ¢Äà±ÌXL[}ðgp·éÌÅAâèÍN±çȢŵå¤B
+ }bsOâf[^x[XXL[}ðÏXµ½Æ«ÍAà¤êxhbm2ddlðLøɵľ³¢B
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="tutorial-associations">
+ <title>p[g2 - ÖAÌ}bsO</title>
+
+ <para>
+ i±GeBeBNXðe[uÉ}bsOµÜµ½B
+ ³çɱÌãɢ©ÌNXÌÖAðÇÁµÜµå¤B
+ ܸßÉAvP[VÉlXðÇÁµAÞçªQÁ·éCxgÌXgði[µÜ·B
+ </para>
+
+ <sect2 id="tutorial-associations-mappinguser" revision="1">
+ <title>PersonNXÌ}bsO</title>
+
+ <para>
+ ÅÌ <literal>Person</literal> NXÍPÅ·F
+ </para>
+
+ <programlisting><![CDATA[package events;
+
+public class Person {
+
+ private Long id;
+ private int age;
+ private String firstname;
+ private String lastname;
+
+ public Person() {}
+
+ // Accessor methods for all properties, private setter for 'id'
+
+}]]></programlisting>
+
+ <para>
+ <literal>Person.hbm.xml</literal> Æ¢¤Vµ¢}bsOt@Cð쬵ľ³¢
+ it@CÌÅÉDTDÖÌQÆðYê¸Éüêľ³¢jF
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="events.Person" table="PERSON">
+ <id name="id" column="PERSON_ID">
+ <generator class="native"/>
+ </id>
+ <property name="age"/>
+ <property name="firstname"/>
+ <property name="lastname"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ ÅãÉHibernateÌÝèÉVµ¢}bsOðÇÁµÄ¾³¢F
+ </para>
+
+ <programlisting><![CDATA[<mapping resource="events/Event.hbm.xml"/>
+<mapping resource="events/Person.hbm.xml"/>]]></programlisting>
+
+ <para>
+ »êÅͱêç2ÂÌGeBeBÔÌÖAð쬵ܷB
+ lXªCxgÉQÁÅ«ACxgªQÁÒðÂÌ;ç©Å·B
+ µíȯêÎÈçÈ¢ÝvÌâèÍAûüA½dxARNVÌUé¢Å·B
+ </para>
+ </sect2>
+
+ <sect2 id="tutorial-associations-unidirset" revision="3">
+ <title>PûüSetx[XÖA</title>
+
+ <para>
+ CxgÌRNVð <literal>Person</literal> NXÉÇÁµÜ·B
+ ±¤µÄ¨ÆA¾¦IÈNGAÂÜèí´í´<literal>aPerson.getEvents()</literal>ðÄÑo³¸ÉA
+ ÁèÌlÉRtCxgðÈPÉirQ[g·é±ÆªÅ«Ü·B
+ ½¿ÍJavaÌRNVA<literal>Set</literal> ðg¢Ü·B
+ RNVÍd¡vfð½È¢µAÔͽ¿ÉÍÓ¡ªÈ¢©çÅ·B
+ </para>
+
+ <para>
+ <literal>Set</literal> ÅÀ³êéPûüA½lÖAªKvÅ·B
+ JavaNXàÉηéR[hð¢Ä}bsOµÜµå¤F
+ </para>
+
+ <programlisting><![CDATA[public class Person {
+
+ private Set events = new HashSet();
+
+ public Set getEvents() {
+ return events;
+ }
+
+ public void setEvents(Set events) {
+ this.events = events;
+ }
+}]]></programlisting>
+
+ <para>
+ ±ÌÖAð}bsO·éOÉA½Î¤É¢Äl¦Ä¾³¢B
+ ¾ç©È±ÆÅ·ªA¡Í±êðPûüɵ½¾¯Å·B
+ tÉA <literal>Event</literal> ¤ÉàÊÌRNVðìé±ÆàÅ«Ü·B
+ á¦Î <literal>anEvent.getParticipants()</literal> Ìæ¤ÉA
+ oûüÉirQ[gµ½¯êÎA»¤·é±ÆàÅ«Ü·B
+ ±êÍ@\IÉÝÄKvÅÍ èܹñB
+ ÁèÌCxgÉÖW·éf[^ðæ¾·é¾mÈNGðA¢ÂÅàÀs·é±Æªoܵ½B
+ ±ÌÝvÌIðÍJÒÉC³êÄ¢ÄA±Ìc_Éæè¾ç©ÈÌÍÖA̽dxÅ·B
+ ÂÜ輤ðu½vlÉ·éA <emphasis>½Î½</emphasis> ÆÄÎêéÖAÅ·B
+ »Ì½ßHibernateÌmany-to-many}bsOðg¢Ü·F
+ </para>
+
+ <programlisting><![CDATA[<class name="events.Person" table="PERSON">
+ <id name="id" column="PERSON_ID">
+ <generator class="native"/>
+ </id>
+ <property name="age"/>
+ <property name="firstname"/>
+ <property name="lastname"/>
+
+ <set name="events" table="PERSON_EVENT">
+ <key column="PERSON_ID"/>
+ <many-to-many column="EVENT_ID" class="events.Event"/>
+ </set>
+
+</class>]]></programlisting>
+
+ <para>
+ HibernateÍ èÆ çäéíÞÌRNV}bsOðT|[gµÄ¢Ü·ªA
+ ÅàêÊIÈà̪ <literal><set></literal> Å·B
+ ½Î½ÖAiÜ½Í <emphasis>n:m</emphasis> GeBeB[VVbvjÉÍA
+ ÖAe[uªKvÅ·B
+ ±Ìe[uÌ»ê¼êÌsÍAlÆCxgÔÌNð\»µÜ·B
+ e[u¼Í <literal>set</literal> vfÌ <literal>table</literal> ®«ÅÝèµÜ·B
+ l¤ÌÖA̯ÊqJ¼Í <literal><key></literal> vfÅA
+ Cxg¤ÌJ¼Í <literal><many-to-many></literal> Ì <literal>column</literal>
+ ®«Åè`µÜ·B
+ HibernateÉRNVÌIuWFNgÌNX
+ i³mÉÍAQÆÌRNV̽ΤÌNXjð³¦È¯êÎÈèܹñB
+ </para>
+
+ <para>
+ »Ì½ß±Ì}bsOÌf[^x[XXL[}ÍȺÌæ¤ÉÈèÜ·BF
+ </para>
+
+ <programlisting><![CDATA[
+ _____________ __________________
+ | | | | _____________
+ | EVENTS | | PERSON_EVENT | | |
+ |_____________| |__________________| | PERSON |
+ | | | | |_____________|
+ | *EVENT_ID | <--> | *EVENT_ID | | |
+ | EVENT_DATE | | *PERSON_ID | <--> | *PERSON_ID |
+ | TITLE | |__________________| | AGE |
+ |_____________| | FIRSTNAME |
+ | LASTNAME |
+ |_____________|
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-working" revision="2">
+ <title>ÖAð©¹é</title>
+
+ <para>
+ <literal>EventManager</literal> ÌVµ¢\bhÅlXÆCxgðêɵܵå¤F
+ </para>
+
+ <programlisting><![CDATA[private void addPersonToEvent(Long personId, Long eventId) {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+ session.beginTransaction();
+
+ Person aPerson = (Person) session.load(Person.class, personId);
+ Event anEvent = (Event) session.load(Event.class, eventId);
+
+ aPerson.getEvents().add(anEvent);
+
+ session.getTransaction().commit();
+}]]></programlisting>
+
+ <para>
+ <literal>Person</literal> Æ <literal>Event</literal> ð[hµ½ãA
+ ÊÌRNV\bhðgÁÄPÉ»ÌRNVðC³µÄ¾³¢B
+ ²Ìƨè <literal>update()</literal> â <literal>save()</literal>
+ ̾¦IÈÄÑoµÍ èܹñB
+ HibernateÍAC³³ê½±ÆÉæèXV·éKvÌ éRNVð©®IÉmµÜ·B
+ ±êÍ <emphasis>©®_[eB`FbN</emphasis> ÆÄÎêA
+ IuWFNg̼OâdatevpeBðC³·é±ÆÅ·±ÆàÂ\Å·B
+ »êçª <emphasis>i±</emphasis> óÔÉ éÀèA
+ ÂÜèÁèÌHibernate <literal>Session</literal> ÉoCh³êÄ¢éÀè
+ iá¦ÎìÆPÊ(Unit of Work)ÌÅPÉ[hܽÍZ[u³ê½jA
+ HibernateÍÇñÈÏXàj^[µAx«Ý(write-behind)ÅSQLðÀsµÜ·B
+ ÊíAìÆPÊ(Unit of Work)ÌÅãɾ¯síêéf[^x[XÆÌóÔð¯ú³¹éÍA
+ <emphasis>tbV
</emphasis> ÆÄÎêÜ·B
+ ±ÌR[hÅÍAìÆPÊ(Unit of Work)Íf[^x[XgUNVÌR~bgiàµÍ[obNjÅI¹µÜ·B
+ ±êÍA <literal>CurrentSessionContext</literal> NXÉ뵀 <literal>thread</literal> ðÝèµ½½ßÅ·B
+ </para>
+
+ <para>
+ ÙÈéìÆPÊ(Unit of Work)ÅlXÆCxgð[h·é±ÆàRÅ«Ü·B
+ »¤ÅȯêÎAi±óÔÉȢƫiÈOÉi±Å Á½ÈçA±ÌóÔð <emphasis>ª£idetachedj</emphasis>
+ ÆÄÑÜ·jA <literal>Session</literal> ÌOÅIuWFNgðC³µÜ·B
+ ª£³êéÆ«ÉÍRNVðÏX·é±ÆàÂ\Å·F
+ </para>
+
+ <programlisting><![CDATA[private void addPersonToEvent(Long personId, Long eventId) {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+ session.beginTransaction();
+
+ Person aPerson = (Person) session
+ .createQuery("select p from Person p left join fetch p.events where p.id = :pid")
+ .setParameter("pid", personId)
+ .uniqueResult(); // Eager fetch the collection so we can use it detached
+
+ Event anEvent = (Event) session.load(Event.class, eventId);
+
+ session.getTransaction().commit();
+
+ // End of first unit of work
+
+ aPerson.getEvents().add(anEvent); // aPerson (and its collection) is detached
+
+ // Begin second unit of work
+
+ Session session2 = HibernateUtil.getSessionFactory().getCurrentSession();
+ session2.beginTransaction();
+
+ session2.update(aPerson); // Reattachment of aPerson
+
+ session2.getTransaction().commit();
+}]]></programlisting>
+
+ <para>
+ <literal>update</literal> ÌÄÑoµÍª£IuWFNgðÄÑi±»µÜ·B
+ ±êÍAVµ¢ìÆPÊ(Unit of Work)ÉoCh·éƾ¦éŵå¤B
+ »Ì½ßª£ÌÔÉÁ¦çê½ÇÌæ¤ÈC³àf[^x[XÉZ[uÅ«Ü·B
+ GeBeBIuWFNgÌRNVÖÌC³iÇÁ¥íjà¯lÉZ[uÅ«Ü·B
+ </para>
+
+ <para>
+ ±êÍ¡Í Üèg¢Ý¿ª èܹñªA
+ ©ªÌAvP[VÌÝvÉgÝޱƪūédvÈRZvgÅ·B
+ »êÅͱÌGNTTCYÌÅãÉA
+ <literal>EventManager</literal> ÌC\bhÉVµ¢ANVðÇÁµÄ
+ R}hC©çÄÑoµÄÝܵå¤B
+ lâCxg̯ÊqªKvÈçA <literal>save()</literal> \bhªÔµÄêÜ·
+ iêÉæÁÄͯÊqðÔ·½ßÉ\bhðC³·éKvª é©àµêܹñjB
+ </para>
+
+ <programlisting><![CDATA[else if (args[0].equals("addpersontoevent")) {
+ Long eventId = mgr.createAndStoreEvent("My Event", new Date());
+ Long personId = mgr.createAndStorePerson("Foo", "Bar");
+ mgr.addPersonToEvent(personId, eventId);
+ System.out.println("Added person " + personId + " to event " + eventId);
+}]]></programlisting>
+
+ <para>
+ ±êͯ¶æ¤ÉdvÈ2ÂÌNXAÂÜè2ÂÌGeBeBÔÌÖAÌáŵ½B
+ OÉq×½æ¤ÉAT^IÈfÉÍAÊuärIdvÅÍÈ¢v¼ÌNXÆ^ª èÜ·B
+ ±êÜÅÉ©½æ¤È <literal>int</literal> â <literal>String</literal> Ìæ¤ÈàÌÅ·B
+ ±Ìæ¤ÈNXð <emphasis>l^</emphasis> ƾ¢Ü·B
+ ±ÌCX^XÍÁèÌGeBeBÉ <emphasis>˶</emphasis> µÜ·B
+ ±Ì^ÌCX^XÍÆ©ÌIDð¿Ü¹ñµA
+ GeBeBÔŤL³êé±Æà èܹñ
+ it@[Xgl[ª¯¶¾Á½ÆµÄàA2lÌlͯ¶ <literal>firstname</literal>
+ IuWFNgðQƵܹñjB
+ l^Íà¿ëñJDKàɩ©èÜ·ªA»ê¾¯ÅÍÈ
+ iÀÛAHibernateAvP[Vɨ¢Ä·×ÄÌJDKNXÍl^ƩȹܷjA
+ á¦Î <literal>Address</literal> â <literal>MonetaryAmount</literal>
+ Ìæ¤ÈÆ©Ì˶NXð±ÆàÅ«Ü·B
+ </para>
+
+ <para>
+ l^ÌRNVðÝv·é±ÆàÅ«Ü·B
+ ±êͼÌGeBeBÖÌQÆÌRNVÆÍTOIÉñíÉÙÈèÜ·ªA
+ JavaÅÍÙÆñǯ¶æ¤É©¦Ü·B
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-valuecollections">
+ <title>lÌRNV</title>
+
+ <para>
+ l^IuWFNgÌRNVð <literal>Person</literal> GeBeBÖÇÁµÜ·B
+ E[AhXði[µ½¢ÌÅ·ªA<literal>String</literal> ^ðgÁÄ¢éÌÅA
+ RNVÍÄÑ <literal>Set</literal> Å·F
+ </para>
+ <programlisting><![CDATA[private Set emailAddresses = new HashSet();
+
+public Set getEmailAddresses() {
+ return emailAddresses;
+}
+
+public void setEmailAddresses(Set emailAddresses) {
+ this.emailAddresses = emailAddresses;
+}]]></programlisting>
+
+ <para>
+ ±Ì <literal>Set</literal> Ì}bsOÅ·F
+ </para>
+
+ <programlisting><![CDATA[<set name="emailAddresses" table="PERSON_EMAIL_ADDR">
+ <key column="PERSON_ID"/>
+ <element type="string" column="EMAIL_ADDR"/>
+</set>]]></programlisting>
+
+ <para>
+ OÌ}bsOÆä×Äá¤ÌÍ <literal>element</literal> ̪ŷªA
+ HibernateɱÌRNVª¼ÌGeBeBÖÌQÆðÜܸA
+ <literal>String</literal> ^ÌvfÌRNVðÜÞ±Æð³¦Ü·B
+ i¬¶Ì¼O(string)ÍHibernateÌ}bsO^ܽÍRo[^Å éÆ¢¤±ÆÅ·jB
+ JèԵܷªA<literal>set</literal> vfÌ <literal>table</literal> ®«ÍA
+ RNV̽ßÌe[u¼ðwèµÜ·B
+ <literal>key</literal> vfÍRNVe[uÌOL[J¼ðè`µÜ·B
+ <literal>element</literal> vfÌ <literal>column</literal> ®«Í <literal>String</literal>
+ ÌlªÀÛÉi[³êéJ̼Oðè`µÜ·B
+ </para>
+
+ <para>
+ XVµ½XL[}ð©Ä¾³¢F
+ </para>
+
+ <programlisting><![CDATA[
+ _____________ __________________
+ | | | | _____________
+ | EVENTS | | PERSON_EVENT | | | ___________________
+ |_____________| |__________________| | PERSON | | |
+ | | | | |_____________| | PERSON_EMAIL_ADDR |
+ | *EVENT_ID | <--> | *EVENT_ID | | | |___________________|
+ | EVENT_DATE | | *PERSON_ID | <--> | *PERSON_ID | <--> | *PERSON_ID |
+ | TITLE | |__________________| | AGE | | *EMAIL_ADDR |
+ |_____________| | FIRSTNAME | |___________________|
+ | LASTNAME |
+ |_____________|
+ ]]></programlisting>
+
+ <para>
+ RNVe[uÌåL[ÍAÀÛͼûÌJðgÁ½¡L[Å é±Æªí©èÜ·B
+ ±êÍl²ÆÉE[AhXªd¡Å«È¢Æ¢¤±ÆÅA
+ JavaÌsetÉv³êéZ}eBNX»ÌàÌÅ·B
+ </para>
+
+ <para>
+ ÈOlÆCxgðÖAï½Æ«ÆS¯¶æ¤ÉA
+ ¡âµÉRNVÉvfðÇÁ·é±ÆªÅ«éæ¤ÉÈèܵ½B
+ ¼ûÆàJavaÅͯ¶R[hÅ·B
+ </para>
+
+ <programlisting><![CDATA[private void addEmailToPerson(Long personId, String emailAddress) {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+ session.beginTransaction();
+
+ Person aPerson = (Person) session.load(Person.class, personId);
+
+ // The getEmailAddresses() might trigger a lazy load of the collection
+ aPerson.getEmailAddresses().add(emailAddress);
+
+ session.getTransaction().commit();
+}]]></programlisting>
+
+ <para>
+ ¡ñARNVÌú»É <emphasis>fetch</emphasis> NGðgpµÜ¹ñŵ½B
+ »Ì½ßAgetter\bhÌÄÑoµÉæÁÄRNVðú»·é½ßÌSELECTª
+ Às³êéÌÅARNVÉvfðÇÁÅ«Ü·B
+ SQLÌOðĵÄA¦tFb`ðgÁÄÅK»µÄ¾³¢B
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-bidirectional" revision="1">
+ <title>oûüÖA</title>
+
+ <para>
+ ÉoûüÖAð}bsOµÜ·B
+ Javaż¤©çlÆCxgÌÖAð®ì³¹Ü·B
+ à¿ëñAf[^x[XXL[}ÍÏíèܹñªA½dxͽνÌÜÜÅ·B
+ [Vif[^x[XÍlbg[NvO~O¾êæèà_îÈÌÅA
+ irQ[VÌûüÌæ¤ÈàÌðKvƵܹñB
+ f[^Í çäéÌû@Å©½è³Å«éÆ¢¤±ÆÅ·B
+ </para>
+
+ <para>
+ ܸ <literal>Event</literal> CxgNXÉQÁÒÌRNVðÇÁµÜ·F
+ </para>
+
+ <programlisting><![CDATA[private Set participants = new HashSet();
+
+public Set getParticipants() {
+ return participants;
+}
+
+public void setParticipants(Set participants) {
+ this.participants = participants;
+}]]></programlisting>
+
+ <para>
+ »êÅÍ <literal>Event.hbm.xml</literal> ÅÖA̱¿ç¤ð}bsOµÄ¾³¢B
+ </para>
+
+ <programlisting><![CDATA[<set name="participants" table="PERSON_EVENT" inverse="true">
+ <key column="EVENT_ID"/>
+ <many-to-many column="PERSON_ID" class="events.Person"/>
+</set>]]></programlisting>
+
+ <para>
+ ²ÌƨèA¢¸êÌ}bsOhL
g(XMLt@C)ÅàAÊÌ <literal>set</literal>
+ }bsOðgÁĢܷB
+ <literal>key</literal> Æ <literal>many-to-many</literal> ÌJ¼ªA
+ ¼ûÌ}bsOhL
gÅüêÖ¦ÉÈÁÄ¢é±ÆÉڵľ³¢B
+ ±±ÅÅàdvÈÇÁÚÍA <literal>Event</literal> ÌRNV}bsOÌ <literal>set</literal>
+ vfÉ é <literal>inverse="true"</literal> ®«Å·B
+ </para>
+
+ <para>
+ ±ÌwèÌÓ¡ÍA2ÂÌÔÌGeBeBÔÌNÉ¢ÄÌîñðT·Kvª éÆ«A
+ HibernateͽΤÌGeBeBAÂÜè <literal>Person</literal> NX©çT·Æ¢¤±ÆÅ·B
+ êx2ÂÌGeBeBÔÌoûüNªÇÌæ¤É쬳ê驪í©êÎA
+ ±êðð·é±ÆÍÆÄàÈPÅ·B
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-usingbidir">
+ <title>oûüNÌ®ì</title>
+
+ <para>
+ ܸAHibernateªÊíÌJavaÌZ}eBNXÉe¿ðyڳȢ±ÆðSɯßĨ¢Ä¾³¢B
+ ½¿ÍAPûüÌáƵÄÇÌæ¤É <literal>Person</literal> Æ <literal>Event</literal>
+ ÌÔÌNð쬵½Åµå¤©H
+ <literal>Person</literal> ÌCX^XÌCxgÖÌQÆÌRNVÉ
+ <literal>Event</literal> ÌCX^XðÇÁµÜµ½B
+ »Ì½ß±ÌNðoûüɵ½¯êÎA
+ ½èOÅ·ª½Î¤É௶±ÆðµÈ¯êÎÈèܹñB
+ <literal>Event</literal> ÌRNVÉ <literal>Person</literal> ÖÌ
+ QÆðÇÁ·éÆ¢¤±ÆÅ·B
+ ±Ìu¼¤ÅNðÝè·é±ÆvÍâÎÉKvÈÌÅAµÄYêȢž³¢B
+ </para>
+
+ <para>
+ ½ÌJÒÍTdÉvO·éÌÅA
+ GeBeB̼¤É³µÖAðÝè·éNÇ\bhð쬵ܷB
+ á¦Î <literal>Person</literal> ÅÍȺÌæ¤ÉÈèÜ·BF
+ </para>
+
+ <programlisting><![CDATA[protected Set getEvents() {
+ return events;
+}
+
+protected void setEvents(Set events) {
+ this.events = events;
+}
+
+public void addToEvent(Event event) {
+ this.getEvents().add(event);
+ event.getParticipants().add(this);
+}
+
+public void removeFromEvent(Event event) {
+ this.getEvents().remove(event);
+ event.getParticipants().remove(this);
+}]]></programlisting>
+
+ <para>
+ RNVÌQbgÆZbg\bhª»ÝprotectedÉÈÁÄ¢é±ÆÉӵľ³¢B
+ ±êͯ¶pbP[WÌNXâTuNXÌ\bhÍËRANZXªÂ\Å·ªA
+ iÙÆñÇj»ÌpbP[WOÌÇÌNXÅà¼Ú»ÌRNVð䳵ɷé±Æðh¬Ü·B
+ ¨»ç½Î¤ÌRNVÉ௶±Æðµ½ûª¢¢Åµå¤B
+ </para>
+
+ <para>
+ <literal>inverse</literal> }bsO®«ÆÍ¢Á½¢½Åµå¤©H
+ JÒÆJavaÉÆÁÄÍAoûüNÍPɼ¤ÌQÆð³µÝè·éÆ¢¤±ÆÅ·B
+ µ©µHibernateÍi§ñá½ðð¯é½ßÉjSQLÌ <literal>INSERT</literal> Æ <literal>UPDATE</literal>
+ ¶ð³mÉÏX·é½ßÌ\ªÈîñðÁĢȢÌÅA
+ oûüÖAvpeB𵤽ß̽ç©Ì¯ðKvƵܷB
+ ÖAÌФð <literal>inverse</literal> ÉÝè·é±ÆÅAHibernateÍî{IÉÍÝèµ½¤ð³µA
+ ½Î¤Ì <emphasis>¾</emphasis> ƵÄl¦Ü·B
+ ±ê¾¯ÅAHibernateÍûüðÂirQ[VfðSQLf[^x[XXL[}ÖÏ··éÆ«Ì
+ ·×ÄÌâèɤÜÎÅ«Ü·B
+ o¦Ä¨©È¯êÎÈçÈ¢[ÍÈPÅ·B
+ oûüÖAÍK¸Ð¤ð <literal>inverse</literal> É·éKvª éÆ¢¤±ÆÅ·B
+ êνÖAÅÍ»êͽ¤ÅȯêÎÈèܹñB
+ ½Î½ÖAÅÍÇ¿ç¤Åà\¢Ü¹ñBÇ¿çÅàá¢Í èܹñB
+ </para>
+
+ </sect2>
+
+ <para>
+ Åͱêð¬³ÈWebAvP[VɵÄÝܵå¤B
+ </para>
+
+ </sect1>
+
+ <sect1 id="tutorial-webapp">
+ <title>p[g3 - EventManager WebAvP[V</title>
+
+ <para>
+ HibernateÌWebAvP[VÍAX^hA[ÌAvP[VÌæ¤É
+ <literal>Session</literal> Æ <literal>Transaction</literal> ðgpµÜ·B
+ µ©µ¢Â©ÌêÊIÈp^[ªð§¿Ü·B
+ ±±Å <literal>EventManagerServlet</literal> ð쬵ܷB±ÌT[ubgÍA
+ f[^x[XÉi[µ½SÄÌCxgðXgÉÅ«A³çÉHTMLtH[©çVµ¢CxgðüÍÅ«éàÌÅ·B
+ </para>
+
+ <sect2 id="tutorial-webapp-servlet" revision="1">
+ <title>î{IÈServletÌLq</title>
+
+ <para>
+ Vµ¢NXðA\[XfBNgÌ <literal>events</literal> pbP[WÉ쬵ľ³¢B
+ </para>
+
+ <programlisting><![CDATA[package events;
+
+// Imports
+
+public class EventManagerServlet extends HttpServlet {
+
+ // Servlet code
+}]]></programlisting>
+
+ <para>
+ ServletÍHTTPÌ <literal>GET</literal> NGXgÌÝð·éÌÅA
+ <literal>doGet()</literal> ðÀµÜ·B
+ </para>
+
+ <programlisting><![CDATA[protected void doGet(HttpServletRequest request,
+ HttpServletResponse response)
+ throws ServletException, IOException {
+
+ SimpleDateFormat dateFormatter = new SimpleDateFormat("dd.MM.yyyy");
+
+ try {
+ // Begin unit of work
+ HibernateUtil.getSessionFactory()
+ .getCurrentSession().beginTransaction();
+
+ // Process request and render page...
+
+ // End unit of work
+ HibernateUtil.getSessionFactory()
+ .getCurrentSession().getTransaction().commit();
+
+ } catch (Exception ex) {
+ HibernateUtil.getSessionFactory()
+ .getCurrentSession().getTransaction().rollback();
+ throw new ServletException(ex);
+ }
+
+}]]></programlisting>
+
+ <para>
+ ±êÍ <emphasis>session-per-request</emphasis> Æ¢¤p^[Å·B
+ ServletªNGXgðó¯æéÆA <literal>SessionFactory</literal> Ì
+ <literal>getCurrentSession()</literal> ÌÅÌÄÑoµÅA
+ HibernateÌVµ¢ <literal>Session</literal> ªJ©êÜ·B
+ »ÌÆ«f[^x[XgUNVªJn³êÜ·B
+ f[^ÌÇÝ«ÉÖíç¸A·×ÄÌf[^ANZXÍgUNVàÅs¢Ü·B
+ iAvP[VàÅÍI[gR~bg[hðgpµÜ¹ñjB
+ </para>
+
+ <para>
+ ÉANGXgÌANVͳêAX|XÅ éHTMLª`æ³êÜ·B
+ ±êÉ¢ÄÍ·®ÉྵܷB
+ </para>
+
+ <para>
+ ÅãÉNGXgÌÆHTML`檮¹µ½Æ«ÉAìÆPÊ(Unit of Work)ðI¹µÜ·B
+ àµâ`æÉâ誶µ½êAexceptionª°çêÄf[^x[XgUNVð[obNµÜ·B
+ ±êÅ <literal>session-per-request</literal> p^[ª®¹µÜ·B
+ SÄÌT[ubgÉgUNV«EÌR[hðãíèÉAT[ubgtB^ÉLq·é±ÆàÂ\Å·B
+ <emphasis>Open Session in View</emphasis> ÆÄÎêé±Ìp^[É¢ÄÍA
+ HibernateÌWebTCgâWikiðQƵľ³¢B
+ T[ubgÅÍÈJSPÅHTML`æðµæ¤Æ·éÆA·®É±Ìp^[É¢ÄÌîñªKvÉÈéŵå¤B
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-webapp-processing" revision="1">
+ <title>Æ`æ</title>
+
+ <para>
+ ÅÍANGXgÌÆy[WÌ`æðÀµÜ·B
+ </para>
+
+<programlisting><![CDATA[// Write HTML header
+PrintWriter out = response.getWriter();
+out.println("<html><head><title>Event Manager</title></head><body>");
+
+// Handle actions
+if ( "store".equals(request.getParameter("action")) ) {
+
+ String eventTitle = request.getParameter("eventTitle");
+ String eventDate = request.getParameter("eventDate");
+
+ if ( "".equals(eventTitle) || "".equals(eventDate) ) {
+ out.println("<b><i>Please enter event title and date.</i></b>");
+ } else {
+ createAndStoreEvent(eventTitle, dateFormatter.parse(eventDate));
+ out.println("<b><i>Added event.</i></b>");
+ }
+}
+
+// Print page
+printEventForm(out);
+listEvents(out, dateFormatter);
+
+// Write HTML footer
+out.println("</body></html>");
+out.flush();
+out.close();]]></programlisting>
+
+ <para>
+ JavaÆHTMLª¬Ý·éR[fBOX^CÍAæè¡GÈAvP[VÉÍKµÄ¢È¢Åµå¤
+ i±Ì`
[gAÅÍAî{IÈHibernateÌRZvgð¦µÄ¢é¾¯Å é±Æðo¦Ä¨¢Ä¾³¢jB
+ ±ÌR[hÍHTMLÌwb_[Ætb^[ÌLqÅ·B
+ ±Ìy[WÉÍACxgðüÍ·éHTMLtH[ÆAf[^x[XÉ éSÄÌCxgÌXgª\¦³êÜ·B
+ ÅÌ\bhͲPÈHTMLoÍÅ·B
+ </para>
+
+ <programlisting><![CDATA[private void printEventForm(PrintWriter out) {
+ out.println("<h2>Add new event:</h2>");
+ out.println("<form>");
+ out.println("Title: <input name='eventTitle' length='50'/><br/>");
+ out.println("Date (e.g. 24.12.2009): <input name='eventDate' length='10'/><br/>");
+ out.println("<input type='submit' name='action' value='store'/>");
+ out.println("</form>");
+}]]></programlisting>
+
+ <para>
+ <literal>listEvents()</literal> \bhÍA»ÝÌXbhÉÑÂ
+ HibernateÌ <literal>Session</literal> ðgpµÄANGðÀsµÜ·B
+ </para>
+
+ <programlisting><![CDATA[private void listEvents(PrintWriter out, SimpleDateFormat dateFormatter) {
+
+ List result = HibernateUtil.getSessionFactory()
+ .getCurrentSession().createCriteria(Event.class).list();
+ if (result.size() > 0) {
+ out.println("<h2>Events in database:</h2>");
+ out.println("<table border='1'>");
+ out.println("<tr>");
+ out.println("<th>Event title</th>");
+ out.println("<th>Event date</th>");
+ out.println("</tr>");
+ for (Iterator it = result.iterator(); it.hasNext();) {
+ Event event = (Event) it.next();
+ out.println("<tr>");
+ out.println("<td>" + event.getTitle() + "</td>");
+ out.println("<td>" + dateFormatter.format(event.getDate()) + "</td>");
+ out.println("</tr>");
+ }
+ out.println("</table>");
+ }
+}]]></programlisting>
+
+ <para>
+ ÅãÉA <literal>store</literal> ANVª <literal>createAndStoreEvent()</literal> \bhð
+ ÄÑoµÜ·B±Ì\bhÅà»ÝÌXbhÌ <literal>Session</literal> ðpµÜ·B
+ </para>
+
+ <programlisting><![CDATA[protected void createAndStoreEvent(String title, Date theDate) {
+ Event theEvent = new Event();
+ theEvent.setTitle(title);
+ theEvent.setDate(theDate);
+
+ HibernateUtil.getSessionFactory()
+ .getCurrentSession().save(theEvent);
+}]]></programlisting>
+
+ <para>
+ ±êÅT[ubgÌ®¬Å·B
+ T[ubgÖÌNGXgÍAêÂÌ <literal>Session</literal> Æ
+ <literal>Transaction</literal> ųêéŵå¤B
+ ÅÌX^hA[ÌAvP[VÌæ¤ÉA
+ HibernateÍ©®IɱêçÌIuWFNgðÀs·éXbhÉÑt¯é±ÆªÅ«Ü·B
+ ±êÉæèAJÒª©RÉR[hðC[ª¯Å«A
+ D«Èû@Å <literal>SessionFactory</literal> ÖÌANZXªÅ«éæ¤ÉÈèÜ·B
+ ÊíAJÒÍæèôû³ê½fUCðgpµÄAf[^ANZXÌR[hð
+ f[^ANZXIuWFNgÉÚ®·éŵå¤iDAOp^[jB
+ æè½ÌáÍAHibernateÌWikiðQƵľ³¢B
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-webapp-deploy">
+ <title>fvCÆeXg</title>
+
+ <para>
+ ±ÌAvP[VÌfvC̽ßÉAWebA[JCuiWARjð쬵ľ³¢B
+ ȺÌAnt^[Qbgð <literal>build.xml</literal> ÉÁ¦Ä¾³¢B
+ </para>
+
+<programlisting><![CDATA[<target name="war" depends="compile">
+ <war destfile="hibernate-tutorial.war" webxml="web.xml">
+ <lib dir="${librarydir}">
+ <exclude name="jsdk*.jar"/>
+ </lib>
+
+ <classes dir="${targetdir}"/>
+ </war>
+</target>]]></programlisting>
+
+ <para>
+ ±Ì^[QbgÍ <literal>hibernate-tutorial.war</literal> Æ¢¤t@Cð
+ vWFNgfBNgÉ쬵ܷB
+ ±Ìt@CÍ·×ÄÌCuÆ <literal>web.xml</literal> LqqðÜñŨèA
+ vWFNgÌx[XfBNgÉu©êé±ÆðúÒ³êÜ·B
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
+<web-app version="2.4"
+ xmlns="http://java.sun.com/xml/ns/j2ee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
+
+ <servlet>
+ <servlet-name>Event Manager</servlet-name>
+ <servlet-class>events.EventManagerServlet</servlet-class>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>Event Manager</servlet-name>
+ <url-pattern>/eventmanager</url-pattern>
+ </servlet-mapping>
+</web-app>]]></programlisting>
+
+ <para>
+ WebAvP[VÌRpCÆfvCÌOÉA <literal>jsdk.jar</literal> Æ¢¤
+ ÇÁÌCuªKvȱÆÉӵľ³¢B
+ ±êÍJavaT[ubgÌJLbgÅ·B
+ àµÜ¾±ÌCuðÁĢȢÈçASunÌEFuTCgÅüèµÄA
+ CufBNgÉRs[µÄ¾³¢B
+ µ©µA±êÍRpCÉÌÝgp³êAWARpbP[W©çÍO³êÜ·B
+ </para>
+
+ <para>
+ rhÆfvC̽ßÉAvWFNgfBNgÅ <literal>ant war</literal> ðÄÑoµA
+ <literal>hibernate-tutorial.war</literal> t@CðTomcatÌ <literal>webapp</literal>
+ fBNgÉRs[µÄ¾³¢B
+ ܾTomcatðCXg[µÄ¢È¯êÎA_E[hµÄAȺÌCXg[KChÉ]Áľ³¢B
+ µ©µA±ÌAvP[VÌfvC·é½ßÉATomcatÌÝèðÏX·éKvÍ èܹñB
+ </para>
+
+ <para>
+ êxfvCµÄTomcatðN®·êÎA <literal>http://localhost:8080/hibernate-tutorial/eventmanager</literal> Å
+ AvP[VÖÌANZXªÂ\Å·B
+ ÅÌNGXgªì¬µ½T[ubgÉnÁ½Æ«ÉATomcatÌOÅ
+ HibernateÌú»ðmFµÄ¾³¢
+ i <literal>HibernateUtil</literal> àÌÃIú»ubNªÄÎêĢܷjB
+ ܽAexceptionª¶µ½ÈçÚ×ðmFµÄ¾³¢B
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="tutorial-summary" revision="1">
+ <title>vñ</title>
+
+ <para>
+ ±Ì`
[gAÅÍAÈPÈX^hA[ÌHibernateAvP[VÆ
+ ¬KÍÌWebAvP[Vð½ßÌî{ðÐîµÜµ½B
+ </para>
+
+ <para>
+ à¤HibernateÉ©Mª êÎAt@XhL
gÌÚÉÚðʵÄA
+ Ê»¤¾Æv¤gsbNðTµÄ¾³¢B
+ ÅàpÉɿ⪠éÌÍAgUNVi<xref linkend="transactions"/>jA
+ tFb`ÌptH[}Xi<xref linkend="performance"/>jA
+ APIÌg¢ûi<xref linkend="objectstate"/>jÆNG
+ i<xref linkend="objectstate-querying"/>jÅ·B
+ </para>
+
+ <para>
+ ³çÉiÁÊÈj`
[gAªKvÈçAHibernateEFuTCgðYê¸É`FbNµÄ¾³¢B
+ </para>
+
+ </sect1>
+
+</chapter>
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/xml.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/xml.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ja-JP/src/main/docbook/modules/xml.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,299 @@
+<?xml version="1.0" encoding="Shift_JIS"?>
+<chapter id="xml">
+ <title>XML}bsO</title>
+
+ <para><emphasis>XML}bsOÍHibernate3.0ÅͱIÈ@\Å èAñíÉ®IÉJÅ·B</emphasis></para>
+
+ <sect1 id="xml-intro" revision="1">
+ <title>XMLf[^ÅÌìÆ</title>
+
+ <para>
+ HibernateÅÍi±«ÌPOJOðgÁÄìÆ·éÌÆÙÚ¯¶æ¤ÈâèûÅA
+ i±«ÌXMLf[^ðgÁÄìÆÅ«Ü·B
+ </para>
+
+ <para>
+ HibernateÍXMLc[ðì·é½ßÌAPIƵÄdom4jðT|[gµÄ¢Ü·B
+ f[^x[X©çdom4jÌc[ð³·éNGð±ÆªÅ«A
+ c[ÉεÄsÁ½C³Í©®IÉf[^x[XƯú³êÜ·B
+
+ ܽXMLhL
gðæ¾·é±ÆªÅ«Adom4jðgÁÄhL
gðp[XµA
+ HibernateÌCÓÌî{ìðgÁÄf[^x[X֫ޱƪūܷBF
+ ÂÜèA<literal>persist(), saveOrUpdate(), merge(), delete(), replicate()</literal>
+ ìÅ·(}[WÍܾT|[gµÄ¢Ü¹ñ)B
+
+ </para>
+
+ <para>
+ f[^ÌC|[g/GNX|[gA
+ JMSÉæéGeBeBf[^ÌO»âSOAPAXSLTx[XÌ|[gÈÇA
+ ±Ì@\ÉͽÌprª èÜ·B
+ </para>
+
+ <para>
+ PêÌ}bsOÍANXÌvpeBÆXMLhL
gÌm[hð
+ ¯Éf[^x[XÖ}bsO·é½ßÉg¤±ÆªÅ«Ü·B
+ ܽ}bsO·éNXªÈ¯êÎA
+ XML¾¯ð}bsO·é½ßÉg¤±ÆªÅ«Ü·B
+
+ </para>
+
+ <sect2 id="xml-intro-mapping">
+ <title>XMLÆNXÌ}bsOð¯Éwè·é</title>
+
+ <para>
+ ±êÍPOJOÆXMLð¯É}bsO·éáÅ·BF
+ </para>
+
+ <programlisting><![CDATA[<class name="Account"
+ table="ACCOUNTS"
+ node="account">
+
+ <id name="accountId"
+ column="ACCOUNT_ID"
+ node="@id"/>
+
+ <many-to-one name="customer"
+ column="CUSTOMER_ID"
+ node="customer/@id"
+ embed-xml="false"/>
+
+ <property name="balance"
+ column="BALANCE"
+ node="balance"/>
+
+ ...
+
+</class>]]></programlisting>
+ </sect2>
+
+ <sect2 id="xml-onlyxml">
+ <title>XML}bsO¾¯ðwè·é</title>
+
+ <para>
+ ±êÍPOJONXªÈ¢}bsOÌáÅ·BF
+ </para>
+
+ <programlisting><![CDATA[<class entity-name="Account"
+ table="ACCOUNTS"
+ node="account">
+
+ <id name="id"
+ column="ACCOUNT_ID"
+ node="@id"
+ type="string"/>
+
+ <many-to-one name="customerId"
+ column="CUSTOMER_ID"
+ node="customer/@id"
+ embed-xml="false"
+ entity-name="Customer"/>
+
+ <property name="balance"
+ column="BALANCE"
+ node="balance"
+ type="big_decimal"/>
+
+ ...
+
+</class>]]></programlisting>
+
+ <para>
+ ±Ì}bsOÉæèAdom4jc[©A
+ vpeB¼/lÌgÌOtijavaÌ <literal>Map</literal>jƵÄ
+ f[^ÉANZXÅ«Ü·B
+
+ vpeB̼OÍAHQLNG[àÅQÆÅ«éÈ_\¢Å·B
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="xml-mapping" revision="1">
+ <title>XML}bsOÌ^f[^</title>
+
+ <para>
+ ½ÌHibernateÌ}bsOvfÍ@<literal>node</literal>@®«ªgpÅ«Ü·B
+ ±êÉæèXML®«Ì¼OâvpeBâGeBeBf[^ðÛ·évfðwèÅ«Ü·B
+ <literal>node</literal>@®«ÌtH[}bgÍȺÌÌ1ÂÅȯêÎÈèܹñBF
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>"element-name"</literal> - wèµ½XMLvfÖ}bsOµÜ·
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>"@attribute-name"</literal> - wèµ½XML®«Ö}bsOµÜ·
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>"."</literal> -@evfÖ}bsOµÜ·
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>"element-name/@attribute-name"</literal> -
+ wèµ½GgÌwèµ½®«Ö}bsOµÜ·
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ RNVÆPêÌlÌÖAÉεÄA
+ ¨Ü¯Ì <literal>embed-xml</literal> ®«ª èÜ·B
+ ftHgÌ <literal>embed-xml="true"</literal> ÆÝèµ½êA
+ ÖA·éGeBeB(l^ÌRNV)ÌXMLc[ÍA
+ ¼ÚÖAðL·éGeBeBÌXMLc[àÉßÜêÜ·B
+ ½ÎÉA<literal>embed-xml="false"</literal> ÆÝèµ½êA
+ QƳêé¯ÊqÌl¾¯ª½dxP¤ÌÖAÉηéXMLÉ»êA
+ PÉRNVÍÜÁ½»êÈÈèÜ·B
+ </para>
+
+ <para>
+ ÜèɽÌÖAÉεÄ
+ <literal>embed-xml="true"</literal> Ƶ½ÜÜÉ·éÌÍӷ׫ŷB
+ XMLÍzÂð¤Üµ¦Ü¹ñB
+ </para>
+
+ <programlisting><![CDATA[<class name="Customer"
+ table="CUSTOMER"
+ node="customer">
+
+ <id name="id"
+ column="CUST_ID"
+ node="@id"/>
+
+ <map name="accounts"
+ node="."
+ embed-xml="true">
+ <key column="CUSTOMER_ID"
+ not-null="true"/>
+ <map-key column="SHORT_DESC"
+ node="@short-desc"
+ type="string"/>
+ <one-to-many entity-name="Account"
+ embed-xml="false"
+ node="account"/>
+ </map>
+
+ <component name="name"
+ node="name">
+ <property name="firstName"
+ node="first-name"/>
+ <property name="initial"
+ node="initial"/>
+ <property name="lastName"
+ node="last-name"/>
+ </component>
+
+ ...
+
+</class>]]></programlisting>
+
+ <para>
+ ±ÌáÅÍAÀÛÌaccountÌf[^ÅÍÈA
+ accountÌidÌRNVðßÞ±Æɵܵ½B
+ ±«ÌHQLNGÅ·F
+ </para>
+
+ <programlisting><![CDATA[from Customer c left join fetch c.accounts where c.lastName like :lastName]]></programlisting>
+
+ <para>
+ ±Ìæ¤Èf[^ZbgðԷŵå¤
+ </para>
+
+ <programlisting><![CDATA[<customer id="123456789">
+ <account short-desc="Savings">987632567</account>
+ <account short-desc="Credit Card">985612323</account>
+ <name>
+ <first-name>Gavin</first-name>
+ <initial>A</initial>
+ <last-name>King</last-name>
+ </name>
+ ...
+</customer>]]></programlisting>
+
+ <para>
+ <literal><one-to-many></literal> }bsOÅ
+ <literal>embed-xml="true"</literal> ÆÝèµ½êA
+ f[^ͱÌæ¤ÉÈéŵå¤B
+ </para>
+
+ <programlisting><![CDATA[<customer id="123456789">
+ <account id="987632567" short-desc="Savings">
+ <customer id="123456789"/>
+ <balance>100.29</balance>
+ </account>
+ <account id="985612323" short-desc="Credit Card">
+ <customer id="123456789"/>
+ <balance>-2370.34</balance>
+ </account>
+ <name>
+ <first-name>Gavin</first-name>
+ <initial>A</initial>
+ <last-name>King</last-name>
+ </name>
+ ...
+</customer>]]></programlisting>
+
+ </sect1>
+
+
+ <sect1 id="xml-manipulation" revision="1">
+ <title>XMLf[^ðµ¤</title>
+
+ <para>
+ XMLhL
gðAAvP[VàÅÄÇÝÝâXVðµÄÝܵå¤B
+ ȺÅÍdom4jÌZbVðæ¾·é±ÆÅs¢Ü·BF
+ </para>
+
+ <programlisting><![CDATA[Document doc = ....;
+
+Session session = factory.openSession();
+Session dom4jSession = session.getSession(EntityMode.DOM4J);
+Transaction tx = session.beginTransaction();
+
+List results = dom4jSession
+ .createQuery("from Customer c left join fetch c.accounts where c.lastName like :lastName")
+ .list();
+for ( int i=0; i<results.size(); i++ ) {
+ //add the customer data to the XML document
+ Element customer = (Element) results.get(i);
+ doc.add(customer);
+}
+
+tx.commit();
+session.close();]]></programlisting>
+
+ <programlisting><![CDATA[Session session = factory.openSession();
+Session dom4jSession = session.getSession(EntityMode.DOM4J);
+Transaction tx = session.beginTransaction();
+
+Element cust = (Element) dom4jSession.get("Customer", customerId);
+for ( int i=0; i<results.size(); i++ ) {
+ Element customer = (Element) results.get(i);
+ //change the customer name in the XML and database
+ Element name = customer.element("name");
+ name.element("first-name").setText(firstName);
+ name.element("initial").setText(initial);
+ name.element("last-name").setText(lastName);
+}
+
+tx.commit();
+session.close();]]></programlisting>
+
+ <para>
+ XMLx[XÌf[^ÌC|[g/GNX|[gðÀ·é½ßÉA
+ HibernateÌ <literal>replicate()</literal> ìð±Ì@\ðѯéÌÍ
+ ÉßÄLøÅ·B
+ </para>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/css/html.css
===================================================================
--- trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/css/html.css (rev 0)
+++ trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/css/html.css 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,97 @@
+A {
+ color: #003399;
+}
+
+A:active {
+ color: #003399;
+}
+
+A:visited {
+ color: #888888;
+}
+
+P, OL, UL, LI, DL, DT, DD, BLOCKQUOTE {
+ color: #000000;
+}
+
+TD, TH, SPAN {
+ color: #000000;
+}
+
+BLOCKQUOTE {
+ margin-right: 0px;
+}
+
+
+H1, H2, H3, H4, H5, H6 {
+ color: #000000;
+ font-weight:500;
+ margin-top:10px;
+ padding-top:15px;
+}
+
+TABLE {
+ border-collapse: collapse;
+ border-spacing:0;
+ border: 1px thin black;
+ empty-cells: hide;
+}
+
+TD {
+ padding: 4pt;
+}
+
+H1 { font-size: 150%; }
+H2 { font-size: 140%; }
+H3 { font-size: 110%; font-weight: bold; }
+H4 { font-size: 110%; font-weight: bold;}
+H5 { font-size: 100%; font-style: italic; }
+H6 { font-size: 100%; font-style: italic; }
+
+TT {
+font-size: 90%;
+ font-family: "Courier New", Courier, monospace;
+ color: #000000;
+}
+
+PRE {
+font-size: 100%;
+ padding: 5px;
+ border-style: solid;
+ border-width: 1px;
+ border-color: #CCCCCC;
+ background-color: #F4F4F4;
+}
+
+UL, OL, LI {
+ list-style: disc;
+}
+
+HR {
+ width: 100%;
+ height: 1px;
+ background-color: #CCCCCC;
+ border-width: 0px;
+ padding: 0px;
+ color: #CCCCCC;
+}
+
+.variablelist {
+ padding-top: 10;
+ padding-bottom:10;
+ margin:0;
+}
+
+.itemizedlist, UL {
+ padding-top: 0;
+ padding-bottom:0;
+ margin:0;
+}
+
+.term {
+ font-weight:bold;
+}
+
+
+
+
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/AuthorWork.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/AuthorWork.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/AuthorWork.zargo
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/AuthorWork.zargo
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/CustomerOrderProduct.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/CustomerOrderProduct.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/CustomerOrderProduct.zargo
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/CustomerOrderProduct.zargo
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/EmployerEmployee.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/EmployerEmployee.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/EmployerEmployee.zargo
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/EmployerEmployee.zargo
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/full_cream.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/full_cream.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/full_cream.svg
===================================================================
--- trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/full_cream.svg (rev 0)
+++ trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/full_cream.svg 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,429 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"
+[
+ <!ATTLIST svg
+ xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink">
+]>
+<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
+<svg
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="354.331"
+ height="336.614"
+ id="svg1">
+ <defs
+ id="defs3">
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop128" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="1"
+ id="stop129" />
+ </linearGradient>
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient130"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ <radialGradient
+ cx="0.5"
+ cy="0.5"
+ fx="0.5"
+ fy="0.5"
+ r="0.5"
+ id="radialGradient131"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ </defs>
+ <g
+ transform="matrix(0.823795,0,0,0.823795,0.120302,5.25349)"
+ style="font-size:12;"
+ id="g659">
+ <rect
+ width="212.257"
+ height="57.2441"
+ x="17.9576"
+ y="100.132"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect137" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ transform="matrix(0.743454,0,0,0.482981,6.46949,52.2178)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect132" />
+ </g>
+ <rect
+ width="325.86"
+ height="63.6537"
+ x="17.4083"
+ y="15.194"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect136" />
+ <rect
+ width="325.86"
+ height="63.6537"
+ x="13.6713"
+ y="12.4966"
+ style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect126" />
+ <g
+ transform="matrix(1.14345,0,0,0.729078,-1.67818,105.325)"
+ style="font-size:12;"
+ id="g164">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="222.966"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect138" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="221.002"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect133" />
+ </g>
+ <text
+ x="170.824753"
+ y="58.402939"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text183">
+ <tspan
+ x="170.824997"
+ y="58.402901"
+ id="tspan360">
+Application</tspan>
+ </text>
+ <text
+ x="178.076340"
+ y="364.281433"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text197">
+ <tspan
+ x="178.076004"
+ y="364.281006"
+ id="tspan421">
+Database</tspan>
+ </text>
+ <text
+ x="68.605331"
+ y="138.524582"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text216">
+ <tspan
+ x="68.605301"
+ y="138.524994"
+ id="tspan384">
+SessionFactory</tspan>
+ </text>
+ <rect
+ width="67.0014"
+ height="101.35"
+ x="196.927"
+ y="89.2389"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect387" />
+ <rect
+ width="67.0014"
+ height="101.35"
+ x="194.633"
+ y="86.4389"
+ style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect388" />
+ <text
+ x="249.108841"
+ y="173.885559"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text389">
+ <tspan
+ x="249.108994"
+ y="173.886002"
+ id="tspan392">
+Session</tspan>
+ </text>
+ <rect
+ width="73.0355"
+ height="101.35"
+ x="270.995"
+ y="90.0018"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect395" />
+ <rect
+ width="73.0355"
+ height="101.35"
+ x="267.869"
+ y="87.2018"
+ style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect396" />
+ <text
+ x="328.593658"
+ y="174.715622"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text397">
+ <tspan
+ x="328.593994"
+ y="174.716003"
+ id="tspan563">
+Transaction</tspan>
+ </text>
+ <g
+ transform="matrix(0.29544,0,0,0.397877,9.70533,103.96)"
+ style="font-size:12;"
+ id="g565">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect566" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect567" />
+ </g>
+ <text
+ x="25.592752"
+ y="204.497803"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:10;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text568">
+ <tspan
+ x="25.592800"
+ y="204.498001"
+ id="tspan662">
+TransactionFactory</tspan>
+ </text>
+ <g
+ transform="matrix(0.298082,0,0,0.397877,99.6898,103.96)"
+ style="font-size:12;"
+ id="g573">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect574" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect575" />
+ </g>
+ <text
+ x="134.030670"
+ y="205.532791"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:10;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text576">
+ <tspan
+ x="134.031006"
+ y="205.533005"
+ id="tspan664">
+ConnectionProvider</tspan>
+ </text>
+ <g
+ transform="matrix(1.14345,0,0,0.729078,-1.67818,38.9539)"
+ style="font-size:12;"
+ id="g587">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="222.966"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect588" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="221.002"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect589" />
+ </g>
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="25.6196"
+ y="206.028"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect594" />
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="24.4229"
+ y="204.135"
+ style="font-size:12;fill:#b3b3b3;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect595" />
+ <text
+ x="85.575645"
+ y="282.300354"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text596">
+ <tspan
+ x="85.575600"
+ y="282.299988"
+ id="tspan607">
+JNDI</tspan>
+ </text>
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="236.937"
+ y="206.791"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect610" />
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="235.741"
+ y="204.898"
+ style="font-size:12;fill:#b3b3b3;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect611" />
+ <text
+ x="342.093201"
+ y="283.226410"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text612">
+ <tspan
+ x="342.092987"
+ y="283.226013"
+ id="tspan621">
+JTA</tspan>
+ </text>
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="130.134"
+ y="206.791"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect616" />
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="128.937"
+ y="204.898"
+ style="font-size:12;fill:#b3b3b3;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect617" />
+ <text
+ x="212.445343"
+ y="283.226410"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text618">
+ <tspan
+ x="212.445007"
+ y="283.226013"
+ id="tspan623">
+JDBC</tspan>
+ </text>
+ <g
+ transform="matrix(0.823795,0,0,0.823795,0.120302,6.19341)"
+ style="font-size:12;"
+ id="g637">
+ <g
+ transform="matrix(0.499515,0,0,0.415467,-0.237339,5.61339)"
+ id="g167">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect134" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect135" />
+ </g>
+ <text
+ x="33.749969"
+ y="50.589706"
+ style="font-size:11;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text188">
+ <tspan
+ x="33.750000"
+ y="50.589699"
+ id="tspan635">
+Transient Objects</tspan>
+ </text>
+ </g>
+ <g
+ transform="matrix(0.823795,0,0,0.823795,0.120302,5.25349)"
+ style="font-size:12;"
+ id="g644">
+ <g
+ transform="matrix(0.297486,0,0,0.516482,230.251,36.9178)"
+ id="g364">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect365" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect366" />
+ </g>
+ <text
+ x="277.123230"
+ y="85.155571"
+ style="font-size:11;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text367">
+ <tspan
+ x="277.122986"
+ y="85.155602"
+ id="tspan631">
+Persistent</tspan>
+ <tspan
+ x="277.122986"
+ y="96.155602"
+ id="tspan633">
+Objects</tspan>
+ </text>
+ </g>
+</svg>
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/hibernate_logo_a.png
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/hibernate_logo_a.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/lite.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/lite.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/lite.svg
===================================================================
--- trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/lite.svg (rev 0)
+++ trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/lite.svg 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,334 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"
+[
+ <!ATTLIST svg
+ xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink">
+]>
+<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
+<svg
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="318.898"
+ height="248.031"
+ id="svg1">
+ <defs
+ id="defs3">
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop128" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="1"
+ id="stop129" />
+ </linearGradient>
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient130"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ <radialGradient
+ cx="0.5"
+ cy="0.5"
+ fx="0.5"
+ fy="0.5"
+ r="0.5"
+ id="radialGradient131"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ </defs>
+ <rect
+ width="291.837"
+ height="57.0074"
+ x="17.3169"
+ y="18.646"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect136" />
+ <rect
+ width="291.837"
+ height="57.0074"
+ x="13.9703"
+ y="16.2302"
+ style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect126" />
+ <g
+ transform="matrix(0.326107,0,0,0.765831,9.59261,8.98517)"
+ style="font-size:12;"
+ id="g161">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect137" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect132" />
+ </g>
+ <g
+ transform="matrix(1.02406,0,0,0.652953,0.223384,39.9254)"
+ style="font-size:12;"
+ id="g164">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="222.966"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect138" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="221.002"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect133" />
+ </g>
+ <g
+ transform="matrix(0.449834,0,0,0.338463,-3.15909,9.73319)"
+ style="font-size:12;"
+ id="g167">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect134" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect135" />
+ </g>
+ <text
+ x="302.277679"
+ y="65.943230"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text183">
+ <tspan
+ x="302.277954"
+ y="65.943184"
+ id="tspan360">
+Application</tspan>
+ </text>
+ <text
+ x="36.235924"
+ y="63.796055"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:14;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text188">
+ <tspan
+ x="36.235950"
+ y="63.796051"
+ id="tspan427">
+Transient Objects</tspan>
+ </text>
+ <text
+ x="180.416245"
+ y="290.543701"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text197">
+ <tspan
+ x="180.415939"
+ y="290.543549"
+ id="tspan421">
+Database</tspan>
+ </text>
+ <text
+ x="25.037701"
+ y="179.154755"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text216">
+ <tspan
+ x="25.037655"
+ y="179.154648"
+ id="tspan384">
+SessionFactory</tspan>
+ </text>
+ <g
+ transform="matrix(0.252763,0,0,0.765831,109.104,8.98517)"
+ style="font-size:12;"
+ id="g386">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect387" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect388" />
+ </g>
+ <g
+ transform="matrix(0.297394,0,0,0.572692,101.502,21.6359)"
+ style="font-size:12;"
+ id="g364">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect365" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect366" />
+ </g>
+ <text
+ x="202.746506"
+ y="102.992203"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:14;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text367">
+ <tspan
+ x="202.746948"
+ y="102.992249"
+ id="tspan423">
+Persistent</tspan>
+ <tspan
+ x="202.746948"
+ y="116.992355"
+ id="tspan425">
+Objects</tspan>
+ </text>
+ <text
+ x="174.458496"
+ y="180.080795"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text389">
+ <tspan
+ x="174.458618"
+ y="180.080338"
+ id="tspan392">
+Session</tspan>
+ </text>
+ <g
+ transform="matrix(0.127369,0,0,0.765831,188.675,8.98517)"
+ style="font-size:12;"
+ id="g394">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect395" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect396" />
+ </g>
+ <text
+ x="260.413269"
+ y="179.154739"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text397">
+ <tspan
+ x="260.412964"
+ y="179.154343"
+ id="tspan400">
+JDBC</tspan>
+ </text>
+ <g
+ transform="matrix(0.127369,0,0,0.765831,229.156,8.98517)"
+ style="font-size:12;"
+ id="g405">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect406" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect407" />
+ </g>
+ <text
+ x="320.606903"
+ y="179.154739"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text408">
+ <tspan
+ x="320.606964"
+ y="179.154343"
+ id="tspan417">
+JNDI</tspan>
+ </text>
+ <g
+ transform="matrix(0.127369,0,0,0.765831,269.281,8.98517)"
+ style="font-size:12;"
+ id="g411">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect412" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect413" />
+ </g>
+ <text
+ x="377.096313"
+ y="179.154739"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text414">
+ <tspan
+ x="377.096008"
+ y="179.154999"
+ id="tspan145">
+JTA</tspan>
+ </text>
+</svg>
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/overview.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/overview.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/overview.svg
===================================================================
--- trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/overview.svg (rev 0)
+++ trunk/Hibernate3/documentation/manual/ja-JP/src/main/resources/shared/images/overview.svg 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,250 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"
+[
+ <!ATTLIST svg
+ xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink">
+]>
+<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
+<svg
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="248.031"
+ height="248.031"
+ id="svg1">
+ <defs
+ id="defs3">
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop128" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="1"
+ id="stop129" />
+ </linearGradient>
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient130"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ <radialGradient
+ cx="0.5"
+ cy="0.5"
+ fx="0.5"
+ fy="0.5"
+ r="0.5"
+ id="radialGradient131"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ </defs>
+ <g
+ transform="matrix(0.771934,0,0,0.771934,4.36019,-3.02123)"
+ style="font-size:12;"
+ id="g158">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="17.3527"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect136" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="15.3883"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect126" />
+ </g>
+ <g
+ transform="matrix(0.771934,0,0,0.771934,4.36019,3.04452)"
+ style="font-size:12;"
+ id="g161">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect137" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect132" />
+ </g>
+ <g
+ transform="matrix(0.771934,0,0,0.771934,4.36019,8.0993)"
+ style="font-size:12;"
+ id="g164">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="222.966"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect138" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="221.002"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect133" />
+ </g>
+ <g
+ transform="matrix(0.771934,0,0,0.543505,2.59104,21.1103)"
+ style="font-size:12;"
+ id="g167">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect134" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect135" />
+ </g>
+ <text
+ x="105.392174"
+ y="56.568123"
+ transform="scale(0.771934,0.771934)"
+ style="font-size:24;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text183">
+ <tspan
+ x="105.392273"
+ y="56.568146"
+ id="tspan186">
+Application</tspan>
+ </text>
+ <text
+ x="81.820183"
+ y="103.149330"
+ transform="scale(0.771934,0.771934)"
+ style="font-size:20;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text188">
+ <tspan
+ x="81.820213"
+ y="103.149727"
+ id="tspan206">
+Persistent Objects</tspan>
+ </text>
+ <text
+ x="111.548180"
+ y="278.927887"
+ transform="scale(0.771934,0.771934)"
+ style="font-size:24;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text197">
+ <tspan
+ x="111.547874"
+ y="278.927551"
+ id="tspan200">
+Database</tspan>
+ </text>
+ <text
+ x="94.436180"
+ y="153.805740"
+ transform="scale(0.771934,0.771934)"
+ style="font-size:24;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text216">
+ <tspan
+ x="94.436180"
+ y="153.805740"
+ id="tspan221">
+HIBERNATE</tspan>
+ </text>
+ <g
+ transform="matrix(0.771934,0,0,0.771934,2.59083,1.02261)"
+ style="font-size:12;"
+ id="g254">
+ <g
+ transform="translate(4.58374,2.61928)"
+ id="g176">
+ <g
+ transform="matrix(0.571429,0,0,0.67347,-10.6174,117.093)"
+ id="g170">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect171" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect172" />
+ </g>
+ <g
+ transform="matrix(0.571429,0,0,0.67347,138.682,117.093)"
+ id="g173">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect174" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect175" />
+ </g>
+ </g>
+ <text
+ x="47.259438"
+ y="182.367538"
+ style="font-weight:bold;stroke-width:1pt;font-family:Courier;"
+ id="text191">
+ <tspan
+ x="47.259399"
+ y="182.367996"
+ id="tspan212">
+hibernate.</tspan>
+ <tspan
+ x="47.259399"
+ y="194.367996"
+ id="tspan214">
+properties</tspan>
+ </text>
+ <text
+ x="198.523010"
+ y="188.260941"
+ style="font-weight:normal;stroke-width:1pt;font-family:helvetica;"
+ id="text194">
+ <tspan
+ id="tspan195">
+XML Mapping</tspan>
+ </text>
+ </g>
+</svg>
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/no-idea-what-to-do-with-these-yet/fop/sazanami-gothic.ttf
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/ja-JP/src/no-idea-what-to-do-with-these-yet/fop/sazanami-gothic.ttf
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/no-idea-what-to-do-with-these-yet/fop/sazanami-gothic.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ja-JP/src/no-idea-what-to-do-with-these-yet/fop/sazanami-gothic.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ja-JP/src/no-idea-what-to-do-with-these-yet/fop/sazanami-gothic.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="utf-8"?><font-metrics type="TYPE0"><font-name>SazanamiGothic,Gothic-Regular</font-name><embed/><cap-height>0</cap-height><x-height>0</x-height><ascender>995</ascender><descender>-203</descender><bbox><left>-50</left><bottom>-203</bottom><right>1041</right><top>995</top></bbox><flags>33</flags><stemv>0</stemv><italicangle>0</italicangle><ttc-name>Sazanami Gothic</ttc-name><subtype>TYPE0</subtype><multibyte-extras><cid-type>CIDFontType2</cid-type><default-width>0</default-width><bfranges><bf gi="0" ue="0" us="0"/><bf gi="3" ue="126" us="32"/><bf gi="98" ue="255" us="160"/><bf gi="194" ue="305" us="305"/><bf gi="195" ue="322" us="321"/><bf gi="197" ue="331" us="331"/><bf gi="198" ue="339" us="338"/><bf gi="200" ue="402" us="402"/><bf gi="201" ue="593" us="593"/><bf gi="202" ue="602" us="602"/><bf gi="203" ue="658" us="658"/><bf gi="204" ue="711" us="710"/><bf gi="206" ue="715" us="714"/><bf gi="208" ue="728" us="728"/><bf gi="209" ue="730" us="730!
"/><bf gi="210" ue="732" us="732"/><bf gi="211" ue="929" us="913"/><bf gi="228" ue="937" us="931"/><bf gi="235" ue="969" us="945"/><bf gi="260" ue="977" us="977"/><bf gi="261" ue="981" us="981"/><bf gi="262" ue="1025" us="1025"/><bf gi="263" ue="1103" us="1040"/><bf gi="327" ue="1105" us="1105"/><bf gi="328" ue="8195" us="8195"/><bf gi="329" ue="8208" us="8208"/><bf gi="330" ue="8217" us="8213"/><bf gi="335" ue="8221" us="8220"/><bf gi="337" ue="8225" us="8224"/><bf gi="339" ue="8230" us="8229"/><bf gi="341" ue="8240" us="8240"/><bf gi="342" ue="8243" us="8242"/><bf gi="344" ue="8251" us="8251"/><bf gi="345" ue="8254" us="8254"/><bf gi="346" ue="8364" us="8364"/><bf gi="347" ue="8451" us="8451"/><bf gi="348" ue="8467" us="8467"/><bf gi="349" ue="8470" us="8470"/><bf gi="350" ue="8481" us="8481"/><bf gi="351" ue="8491" us="8491"/><bf gi="352" ue="8553" us="8544"/><bf gi="362" ue="8595" us="8592"/><bf gi="366" ue="8658" us="8658"/><bf gi="367" ue="8660" us="8660"/><bf gi="368!
" ue="8704" us="8704"/><bf gi="369" ue="8707" us="8706"/><bf gi="371"
ue="8712" us="8711"/><bf gi="373" ue="8715" us="8715"/><bf gi="374" ue="8722" us="8722"/><bf gi="375" ue="8730" us="8730"/><bf gi="376" ue="8736" us="8733"/><bf gi="380" ue="8741" us="8741"/><bf gi="381" ue="8750" us="8743"/><bf gi="389" ue="8757" us="8756"/><bf gi="391" ue="8765" us="8765"/><bf gi="392" ue="8771" us="8771"/><bf gi="393" ue="8786" us="8786"/><bf gi="394" ue="8801" us="8800"/><bf gi="396" ue="8807" us="8806"/><bf gi="398" ue="8811" us="8810"/><bf gi="400" ue="8819" us="8818"/><bf gi="402" ue="8835" us="8834"/><bf gi="404" ue="8839" us="8838"/><bf gi="406" ue="8869" us="8869"/><bf gi="407" ue="8895" us="8895"/><bf gi="408" ue="8978" us="8978"/><bf gi="409" ue="9331" us="9312"/><bf gi="429" ue="9475" us="9472"/><bf gi="433" ue="9484" us="9484"/><bf gi="434" ue="9488" us="9487"/><bf gi="436" ue="9492" us="9491"/><bf gi="438" ue="9496" us="9495"/><bf gi="440" ue="9501" us="9499"/><bf gi="443" ue="9504" us="9504"/><bf gi="444" ue="9509" us="9507"/><bf gi="447" ue=!
"9512" us="9512"/><bf gi="448" ue="9516" us="9515"/><bf gi="450" ue="9520" us="9519"/><bf gi="452" ue="9524" us="9523"/><bf gi="454" ue="9528" us="9527"/><bf gi="456" ue="9532" us="9531"/><bf gi="458" ue="9535" us="9535"/><bf gi="459" ue="9538" us="9538"/><bf gi="460" ue="9547" us="9547"/><bf gi="461" ue="9634" us="9632"/><bf gi="464" ue="9651" us="9650"/><bf gi="466" ue="9661" us="9660"/><bf gi="468" ue="9671" us="9670"/><bf gi="470" ue="9675" us="9675"/><bf gi="471" ue="9679" us="9678"/><bf gi="473" ue="9711" us="9711"/><bf gi="474" ue="9734" us="9733"/><bf gi="476" ue="9792" us="9792"/><bf gi="477" ue="9794" us="9794"/><bf gi="478" ue="9826" us="9825"/><bf gi="480" ue="9828" us="9828"/><bf gi="481" ue="9831" us="9831"/><bf gi="482" ue="9834" us="9834"/><bf gi="483" ue="9837" us="9837"/><bf gi="484" ue="9839" us="9839"/><bf gi="485" ue="12291" us="12288"/><bf gi="489" ue="12309" us="12293"/><bf gi="506" ue="12316" us="12316"/><bf gi="507" ue="12341" us="12339"/><bf gi="51!
0" ue="12438" us="12353"/><bf gi="596" ue="12446" us="12443"/><bf gi="
600" ue="12542" us="12449"/><bf gi="694" ue="12850" us="12849"/><bf gi="696" ue="12857" us="12857"/><bf gi="697" ue="12968" us="12964"/><bf gi="702" ue="13059" us="13059"/><bf gi="703" ue="13069" us="13069"/><bf gi="704" ue="13076" us="13076"/><bf gi="705" ue="13080" us="13080"/><bf gi="706" ue="13091" us="13090"/><bf gi="708" ue="13095" us="13094"/><bf gi="710" ue="13099" us="13099"/><bf gi="711" ue="13110" us="13110"/><bf gi="712" ue="13115" us="13115"/><bf gi="713" ue="13130" us="13129"/><bf gi="715" ue="13133" us="13133"/><bf gi="716" ue="13137" us="13137"/><bf gi="717" ue="13143" us="13143"/><bf gi="718" ue="13182" us="13179"/><bf gi="722" ue="13199" us="13198"/><bf gi="724" ue="13214" us="13212"/><bf gi="727" ue="13217" us="13217"/><bf gi="728" ue="13252" us="13252"/><bf gi="729" ue="13259" us="13259"/><bf gi="730" ue="13261" us="13261"/><bf gi="731" ue="19973" us="19968"/><bf gi="737" ue="19982" us="19975"/><bf gi="745" ue="19986" us="19984"/><bf gi="748" ue="19993" u!
s="19988"/><bf gi="754" ue="19999" us="19998"/><bf gi="756" ue="20001" us="20001"/><bf gi="757" ue="20004" us="20003"/><bf gi="759" ue="20006" us="20006"/><bf gi="760" ue="20008" us="20008"/><bf gi="761" ue="20011" us="20010"/><bf gi="763" ue="20018" us="20013"/><bf gi="769" ue="20022" us="20021"/><bf gi="771" ue="20025" us="20024"/><bf gi="773" ue="20028" us="20027"/><bf gi="775" ue="20037" us="20031"/><bf gi="782" ue="20039" us="20039"/><bf gi="783" ue="20043" us="20043"/><bf gi="784" ue="20047" us="20045"/><bf gi="787" ue="20049" us="20049"/><bf gi="788" ue="20058" us="20053"/><bf gi="794" ue="20063" us="20060"/><bf gi="798" ue="20067" us="20066"/><bf gi="800" ue="20073" us="20072"/><bf gi="802" ue="20081" us="20081"/><bf gi="803" ue="20085" us="20083"/><bf gi="806" ue="20089" us="20089"/><bf gi="807" ue="20096" us="20094"/><bf gi="810" ue="20098" us="20098"/><bf gi="811" ue="20102" us="20101"/><bf gi="813" ue="20110" us="20104"/><bf gi="820" ue="20114" us="20113"/><bf g!
i="822" ue="20121" us="20116"/><bf gi="828" ue="20130" us="20123"/><bf
gi="836" ue="20134" us="20132"/><bf gi="839" ue="20136" us="20136"/><bf gi="840" ue="20144" us="20139"/><bf gi="846" ue="20147" us="20147"/><bf gi="847" ue="20150" us="20150"/><bf gi="848" ue="20154" us="20153"/><bf gi="850" ue="20164" us="20160"/><bf gi="855" ue="20167" us="20166"/><bf gi="857" ue="20171" us="20170"/><bf gi="859" ue="20176" us="20173"/><bf gi="863" ue="20187" us="20180"/><bf gi="871" ue="20197" us="20189"/><bf gi="880" ue="20200" us="20200"/><bf gi="881" ue="20211" us="20205"/><bf gi="888" ue="20215" us="20213"/><bf gi="891" ue="20227" us="20219"/><bf gi="900" ue="20242" us="20232"/><bf gi="911" ue="20247" us="20245"/><bf gi="914" ue="20250" us="20249"/><bf gi="916" ue="20253" us="20252"/><bf gi="918" ue="20273" us="20270"/><bf gi="922" ue="20286" us="20275"/><bf gi="934" ue="20288" us="20288"/><bf gi="935" ue="20291" us="20290"/><bf gi="937" ue="20297" us="20294"/><bf gi="941" ue="20320" us="20299"/><bf gi="963" ue="20323" us="20323"/><bf gi="964" ue="203!
30" us="20329"/><bf gi="966" ue="20332" us="20332"/><bf gi="967" ue="20337" us="20334"/><bf gi="971" ue="20339" us="20339"/><bf gi="972" ue="20351" us="20341"/><bf gi="983" ue="20358" us="20353"/><bf gi="989" ue="20372" us="20360"/><bf gi="1002" ue="20379" us="20374"/><bf gi="1008" ue="20385" us="20381"/><bf gi="1013" ue="20395" us="20395"/><bf gi="1014" ue="20399" us="20397"/><bf gi="1017" ue="20402" us="20402"/><bf gi="1018" ue="20407" us="20405"/><bf gi="1021" ue="20409" us="20409"/><bf gi="1022" ue="20422" us="20411"/><bf gi="1034" ue="20434" us="20424"/><bf gi="1045" ue="20436" us="20436"/><bf gi="1046" ue="20440" us="20439"/><bf gi="1048" ue="20445" us="20442"/><bf gi="1052" ue="20453" us="20447"/><bf gi="1059" ue="20464" us="20462"/><bf gi="1062" ue="20467" us="20466"/><bf gi="1064" ue="20470" us="20469"/><bf gi="1066" ue="20472" us="20472"/><bf gi="1067" ue="20474" us="20474"/><bf gi="1068" ue="20481" us="20476"/><bf gi="1074" ue="20487" us="20484"/><bf gi="1078" ue!
="20500" us="20489"/><bf gi="1090" ue="20511" us="20502"/><bf gi="1100
" ue="20526" us="20513"/><bf gi="1114" ue="20528" us="20528"/><bf gi="1115" ue="20531" us="20530"/><bf gi="1117" ue="20534" us="20533"/><bf gi="1119" ue="20537" us="20537"/><bf gi="1120" ue="20539" us="20539"/><bf gi="1121" ue="20547" us="20544"/><bf gi="1125" ue="20554" us="20549"/><bf gi="1131" ue="20556" us="20556"/><bf gi="1132" ue="20563" us="20558"/><bf gi="1138" ue="20567" us="20565"/><bf gi="1141" ue="20570" us="20569"/><bf gi="1143" ue="20572" us="20572"/><bf gi="1144" ue="20576" us="20575"/><bf gi="1146" ue="20579" us="20578"/><bf gi="1148" ue="20583" us="20581"/><bf gi="1151" ue="20586" us="20586"/><bf gi="1152" ue="20589" us="20588"/><bf gi="1154" ue="20594" us="20592"/><bf gi="1157" ue="20598" us="20596"/><bf gi="1160" ue="20600" us="20600"/><bf gi="1161" ue="20605" us="20605"/><bf gi="1162" ue="20609" us="20608"/><bf gi="1164" ue="20614" us="20611"/><bf gi="1168" ue="20618" us="20618"/><bf gi="1169" ue="20628" us="20621"/><bf gi="1177" ue="20630" us="20630"/><b!
f gi="1178" ue="20636" us="20632"/><bf gi="1183" ue="20642" us="20638"/><bf gi="1188" ue="20650" us="20650"/><bf gi="1189" ue="20653" us="20652"/><bf gi="1191" ue="20656" us="20655"/><bf gi="1193" ue="20661" us="20658"/><bf gi="1197" ue="20663" us="20663"/><bf gi="1198" ue="20666" us="20665"/><bf gi="1200" ue="20670" us="20669"/><bf gi="1202" ue="20672" us="20672"/><bf gi="1203" ue="20677" us="20674"/><bf gi="1207" ue="20679" us="20679"/><bf gi="1208" ue="20682" us="20681"/><bf gi="1210" ue="20689" us="20684"/><bf gi="1216" ue="20694" us="20691"/><bf gi="1220" ue="20696" us="20696"/><bf gi="1221" ue="20698" us="20698"/><bf gi="1222" ue="20703" us="20700"/><bf gi="1226" ue="20713" us="20706"/><bf gi="1234" ue="20719" us="20717"/><bf gi="1237" ue="20722" us="20721"/><bf gi="1239" ue="20726" us="20724"/><bf gi="1242" ue="20731" us="20729"/><bf gi="1245" ue="20734" us="20734"/><bf gi="1246" ue="20740" us="20736"/><bf gi="1251" ue="20745" us="20742"/><bf gi="1255" ue="20750" us=!
"20747"/><bf gi="1259" ue="20752" us="20752"/><bf gi="1260" ue="20754"
us="20754"/><bf gi="1261" ue="20767" us="20756"/><bf gi="1273" ue="20769" us="20769"/><bf gi="1274" ue="20771" us="20771"/><bf gi="1275" ue="20776" us="20775"/><bf gi="1277" ue="20778" us="20778"/><bf gi="1278" ue="20781" us="20780"/><bf gi="1280" ue="20783" us="20783"/><bf gi="1281" ue="20789" us="20785"/><bf gi="1286" ue="20796" us="20791"/><bf gi="1292" ue="20816" us="20799"/><bf gi="1310" ue="20821" us="20818"/><bf gi="1314" ue="20824" us="20823"/><bf gi="1316" ue="20826" us="20826"/><bf gi="1317" ue="20828" us="20828"/><bf gi="1318" ue="20831" us="20831"/><bf gi="1319" ue="20834" us="20834"/><bf gi="1320" ue="20838" us="20836"/><bf gi="1323" ue="20846" us="20840"/><bf gi="1330" ue="20849" us="20849"/><bf gi="1331" ue="20856" us="20853"/><bf gi="1335" ue="20860" us="20860"/><bf gi="1336" ue="20862" us="20862"/><bf gi="1337" ue="20864" us="20864"/><bf gi="1338" ue="20870" us="20866"/><bf gi="1343" ue="20883" us="20873"/><bf gi="1354" ue="20889" us="20885"/><bf gi="1359" !
ue="20893" us="20893"/><bf gi="1360" ue="20902" us="20896"/><bf gi="1367" ue="20909" us="20904"/><bf gi="1373" ue="20920" us="20912"/><bf gi="1382" ue="20922" us="20922"/><bf gi="1383" ue="20927" us="20924"/><bf gi="1387" ue="20930" us="20930"/><bf gi="1388" ue="20934" us="20932"/><bf gi="1391" ue="20937" us="20936"/><bf gi="1393" ue="20941" us="20939"/><bf gi="1396" ue="20943" us="20943"/><bf gi="1397" ue="20947" us="20945"/><bf gi="1400" ue="20950" us="20949"/><bf gi="1402" ue="20952" us="20952"/><bf gi="1403" ue="20958" us="20955"/><bf gi="1407" ue="20962" us="20960"/><bf gi="1410" ue="20967" us="20965"/><bf gi="1413" ue="20970" us="20969"/><bf gi="1415" ue="20974" us="20972"/><bf gi="1418" ue="20986" us="20976"/><bf gi="1429" ue="20990" us="20989"/><bf gi="1431" ue="21000" us="20992"/><bf gi="1440" ue="21003" us="21002"/><bf gi="1442" ue="21006" us="21006"/><bf gi="1443" ue="21016" us="21009"/><bf gi="1451" ue="21021" us="21021"/><bf gi="1452" ue="21026" us="21026"/><bf!
gi="1453" ue="21029" us="21028"/><bf gi="1455" ue="21034" us="21031"/
><bf gi="1459" ue="21038" us="21038"/><bf gi="1460" ue="21043" us="21040"/><bf gi="1464" ue="21052" us="21045"/><bf gi="1472" ue="21061" us="21059"/><bf gi="1475" ue="21063" us="21063"/><bf gi="1476" ue="21069" us="21065"/><bf gi="1481" ue="21071" us="21071"/><bf gi="1482" ue="21080" us="21076"/><bf gi="1487" ue="21084" us="21082"/><bf gi="1490" ue="21089" us="21086"/><bf gi="1494" ue="21094" us="21091"/><bf gi="1498" ue="21098" us="21097"/><bf gi="1500" ue="21109" us="21102"/><bf gi="1508" ue="21113" us="21111"/><bf gi="1511" ue="21117" us="21117"/><bf gi="1512" ue="21120" us="21119"/><bf gi="1514" ue="21123" us="21122"/><bf gi="1516" ue="21125" us="21125"/><bf gi="1517" ue="21130" us="21127"/><bf gi="1521" ue="21133" us="21132"/><bf gi="1523" ue="21144" us="21137"/><bf gi="1531" ue="21148" us="21146"/><bf gi="1534" ue="21152" us="21151"/><bf gi="1536" ue="21159" us="21155"/><bf gi="1541" ue="21165" us="21161"/><bf gi="1546" ue="21169" us="21167"/><bf gi="1549" ue="21182" u!
s="21172"/><bf gi="1560" ue="21185" us="21184"/><bf gi="1562" ue="21193" us="21187"/><bf gi="1569" ue="21197" us="21196"/><bf gi="1571" ue="21199" us="21199"/><bf gi="1572" ue="21202" us="21201"/><bf gi="1574" ue="21209" us="21204"/><bf gi="1580" ue="21226" us="21211"/><bf gi="1596" ue="21228" us="21228"/><bf gi="1597" ue="21242" us="21232"/><bf gi="1608" ue="21251" us="21246"/><bf gi="1614" ue="21256" us="21253"/><bf gi="1618" ue="21261" us="21258"/><bf gi="1622" ue="21265" us="21263"/><bf gi="1625" ue="21267" us="21267"/><bf gi="1626" ue="21281" us="21269"/><bf gi="1639" ue="21285" us="21283"/><bf gi="1642" ue="21293" us="21287"/><bf gi="1649" ue="21299" us="21295"/><bf gi="1654" ue="21301" us="21301"/><bf gi="1655" ue="21315" us="21304"/><bf gi="1667" ue="21325" us="21317"/><bf gi="1676" ue="21332" us="21329"/><bf gi="1680" ue="21340" us="21335"/><bf gi="1686" ue="21342" us="21342"/><bf gi="1687" ue="21345" us="21344"/><bf gi="1689" ue="21347" us="21347"/><bf gi="1690" u!
e="21350" us="21349"/><bf gi="1692" ue="21353" us="21353"/><bf gi="169
3" ue="21365" us="21356"/><bf gi="1703" ue="21369" us="21367"/><bf gi="1706" ue="21371" us="21371"/><bf gi="1707" ue="21375" us="21374"/><bf gi="1709" ue="21380" us="21378"/><bf gi="1712" ue="21384" us="21383"/><bf gi="1714" ue="21390" us="21390"/><bf gi="1715" ue="21396" us="21395"/><bf gi="1717" ue="21398" us="21398"/><bf gi="1718" ue="21402" us="21400"/><bf gi="1721" ue="21405" us="21405"/><bf gi="1722" ue="21409" us="21407"/><bf gi="1725" ue="21414" us="21412"/><bf gi="1728" ue="21419" us="21416"/><bf gi="1732" ue="21424" us="21421"/><bf gi="1736" ue="21432" us="21426"/><bf gi="1743" ue="21435" us="21434"/><bf gi="1745" ue="21437" us="21437"/><bf gi="1746" ue="21440" us="21440"/><bf gi="1747" ue="21443" us="21442"/><bf gi="1749" ue="21445" us="21445"/><bf gi="1750" ue="21455" us="21448"/><bf gi="1758" ue="21463" us="21458"/><bf gi="1764" ue="21467" us="21465"/><bf gi="1767" ue="21491" us="21469"/><bf gi="1790" ue="21496" us="21493"/><bf gi="1794" ue="21498" us="21498"/><!
bf gi="1795" ue="21508" us="21505"/><bf gi="1799" ue="21521" us="21512"/><bf gi="1809" ue="21523" us="21523"/><bf gi="1810" ue="21531" us="21530"/><bf gi="1812" ue="21533" us="21533"/><bf gi="1813" ue="21537" us="21535"/><bf gi="1816" ue="21551" us="21542"/><bf gi="1826" ue="21553" us="21553"/><bf gi="1827" ue="21558" us="21556"/><bf gi="1830" ue="21561" us="21560"/><bf gi="1832" ue="21566" us="21563"/><bf gi="1836" ue="21568" us="21568"/><bf gi="1837" ue="21572" us="21570"/><bf gi="1840" ue="21578" us="21574"/><bf gi="1845" ue="21583" us="21581"/><bf gi="1848" ue="21585" us="21585"/><bf gi="1849" ue="21599" us="21598"/><bf gi="1851" ue="21602" us="21602"/><bf gi="1852" ue="21604" us="21604"/><bf gi="1853" ue="21611" us="21606"/><bf gi="1859" ue="21614" us="21613"/><bf gi="1861" ue="21617" us="21616"/><bf gi="1863" ue="21623" us="21619"/><bf gi="1868" ue="21629" us="21627"/><bf gi="1871" ue="21633" us="21631"/><bf gi="1874" ue="21638" us="21635"/><bf gi="1878" ue="21650" us!
="21640"/><bf gi="1889" ue="21654" us="21653"/><bf gi="1891" ue="21660
" us="21660"/><bf gi="1892" ue="21663" us="21663"/><bf gi="1893" ue="21666" us="21665"/><bf gi="1895" ue="21679" us="21668"/><bf gi="1907" ue="21683" us="21681"/><bf gi="1910" ue="21698" us="21687"/><bf gi="1922" ue="21700" us="21700"/><bf gi="1923" ue="21706" us="21702"/><bf gi="1928" ue="21710" us="21709"/><bf gi="1930" ue="21720" us="21720"/><bf gi="1931" ue="21730" us="21728"/><bf gi="1934" ue="21734" us="21733"/><bf gi="1936" ue="21738" us="21736"/><bf gi="1939" ue="21743" us="21740"/><bf gi="1943" ue="21746" us="21746"/><bf gi="1944" ue="21750" us="21750"/><bf gi="1945" ue="21754" us="21754"/><bf gi="1946" ue="21761" us="21756"/><bf gi="1952" ue="21769" us="21764"/><bf gi="1958" ue="21776" us="21772"/><bf gi="1963" ue="21782" us="21780"/><bf gi="1966" ue="21803" us="21802"/><bf gi="1968" ue="21807" us="21806"/><bf gi="1970" ue="21811" us="21809"/><bf gi="1973" ue="21814" us="21813"/><bf gi="1975" ue="21817" us="21816"/><bf gi="1977" ue="21822" us="21819"/><bf gi="1981"!
ue="21825" us="21824"/><bf gi="1983" ue="21831" us="21828"/><bf gi="1987" ue="21834" us="21833"/><bf gi="1989" ue="21837" us="21836"/><bf gi="1991" ue="21841" us="21839"/><bf gi="1994" ue="21843" us="21843"/><bf gi="1995" ue="21848" us="21846"/><bf gi="1998" ue="21854" us="21850"/><bf gi="2003" ue="21857" us="21856"/><bf gi="2005" ue="21860" us="21859"/><bf gi="2007" ue="21862" us="21862"/><bf gi="2008" ue="21884" us="21883"/><bf gi="2010" ue="21892" us="21886"/><bf gi="2017" ue="21899" us="21894"/><bf gi="2023" ue="21903" us="21902"/><bf gi="2025" ue="21908" us="21905"/><bf gi="2029" ue="21914" us="21911"/><bf gi="2033" ue="21919" us="21916"/><bf gi="2037" ue="21924" us="21923"/><bf gi="2039" ue="21934" us="21927"/><bf gi="2047" ue="21936" us="21936"/><bf gi="2048" ue="21938" us="21938"/><bf gi="2049" ue="21942" us="21942"/><bf gi="2050" ue="21951" us="21951"/><bf gi="2051" ue="21953" us="21953"/><bf gi="2052" ue="21959" us="21955"/><bf gi="2057" ue="21961" us="21961"/><b!
f gi="2058" ue="21964" us="21963"/><bf gi="2060" ue="21966" us="21966"
/><bf gi="2061" ue="21972" us="21969"/><bf gi="2065" ue="21976" us="21975"/><bf gi="2067" ue="21980" us="21978"/><bf gi="2070" ue="21983" us="21982"/><bf gi="2072" ue="21988" us="21986"/><bf gi="2075" ue="21993" us="21993"/><bf gi="2076" ue="22007" us="22006"/><bf gi="2078" ue="22009" us="22009"/><bf gi="2079" ue="22015" us="22013"/><bf gi="2082" ue="22022" us="22021"/><bf gi="2084" ue="22026" us="22024"/><bf gi="2087" ue="22034" us="22029"/><bf gi="2093" ue="22036" us="22036"/><bf gi="2094" ue="22041" us="22038"/><bf gi="2098" ue="22043" us="22043"/><bf gi="2099" ue="22057" us="22057"/><bf gi="2100" ue="22060" us="22060"/><bf gi="2101" ue="22073" us="22063"/><bf gi="2112" ue="22077" us="22075"/><bf gi="2115" ue="22084" us="22079"/><bf gi="2121" ue="22086" us="22086"/><bf gi="2122" ue="22089" us="22089"/><bf gi="2123" ue="22096" us="22091"/><bf gi="2129" ue="22100" us="22100"/><bf gi="2130" ue="22107" us="22107"/><bf gi="2131" ue="22110" us="22110"/><bf gi="2132" ue="22116" !
us="22112"/><bf gi="2137" ue="22118" us="22118"/><bf gi="2138" ue="22125" us="22120"/><bf gi="2144" ue="22127" us="22127"/><bf gi="2145" ue="22130" us="22129"/><bf gi="2147" ue="22133" us="22132"/><bf gi="2149" ue="22136" us="22136"/><bf gi="2150" ue="22138" us="22138"/><bf gi="2151" ue="22144" us="22144"/><bf gi="2152" ue="22152" us="22148"/><bf gi="2157" ue="22156" us="22154"/><bf gi="2160" ue="22159" us="22159"/><bf gi="2161" ue="22165" us="22164"/><bf gi="2163" ue="22170" us="22169"/><bf gi="2165" ue="22176" us="22173"/><bf gi="2169" ue="22178" us="22178"/><bf gi="2170" ue="22185" us="22181"/><bf gi="2175" ue="22190" us="22187"/><bf gi="2179" ue="22193" us="22193"/><bf gi="2180" ue="22196" us="22195"/><bf gi="2182" ue="22199" us="22198"/><bf gi="2184" ue="22204" us="22204"/><bf gi="2185" ue="22206" us="22206"/><bf gi="2186" ue="22211" us="22208"/><bf gi="2190" ue="22213" us="22213"/><bf gi="2191" ue="22225" us="22216"/><bf gi="2201" ue="22227" us="22227"/><bf gi="2202" !
ue="22241" us="22231"/><bf gi="2213" ue="22248" us="22243"/><bf gi="22
19" ue="22251" us="22251"/><bf gi="2220" ue="22254" us="22253"/><bf gi="2222" ue="22259" us="22256"/><bf gi="2226" ue="22263" us="22262"/><bf gi="2228" ue="22266" us="22265"/><bf gi="2230" ue="22269" us="22269"/><bf gi="2231" ue="22276" us="22271"/><bf gi="2237" ue="22285" us="22279"/><bf gi="2244" ue="22287" us="22287"/><bf gi="2245" ue="22291" us="22289"/><bf gi="2248" ue="22294" us="22293"/><bf gi="2250" ue="22296" us="22296"/><bf gi="2251" ue="22301" us="22298"/><bf gi="2255" ue="22304" us="22303"/><bf gi="2257" ue="22314" us="22306"/><bf gi="2266" ue="22320" us="22316"/><bf gi="2271" ue="22324" us="22323"/><bf gi="2273" ue="22328" us="22327"/><bf gi="2275" ue="22331" us="22331"/><bf gi="2276" ue="22336" us="22333"/><bf gi="2280" ue="22338" us="22338"/><bf gi="2281" ue="22343" us="22341"/><bf gi="2284" ue="22346" us="22346"/><bf gi="2285" ue="22354" us="22348"/><bf gi="2292" ue="22361" us="22361"/><bf gi="2293" ue="22370" us="22369"/><bf gi="2295" ue="22379" us="22372"/>!
<bf gi="2303" ue="22385" us="22381"/><bf gi="2308" ue="22389" us="22387"/><bf gi="2311" ue="22391" us="22391"/><bf gi="2312" ue="22396" us="22393"/><bf gi="2316" ue="22399" us="22398"/><bf gi="2318" ue="22403" us="22401"/><bf gi="2321" ue="22409" us="22408"/><bf gi="2323" ue="22412" us="22411"/><bf gi="2325" ue="22421" us="22419"/><bf gi="2328" ue="22423" us="22423"/><bf gi="2329" ue="22426" us="22425"/><bf gi="2331" ue="22436" us="22428"/><bf gi="2340" ue="22442" us="22439"/><bf gi="2344" ue="22444" us="22444"/><bf gi="2345" ue="22448" us="22448"/><bf gi="2346" ue="22451" us="22451"/><bf gi="2347" ue="22456" us="22456"/><bf gi="2348" ue="22461" us="22461"/><bf gi="2349" ue="22464" us="22464"/><bf gi="2350" ue="22467" us="22467"/><bf gi="2351" ue="22472" us="22470"/><bf gi="2354" ue="22476" us="22475"/><bf gi="2356" ue="22479" us="22478"/><bf gi="2358" ue="22486" us="22482"/><bf gi="2363" ue="22497" us="22492"/><bf gi="2369" ue="22500" us="22499"/><bf gi="2371" ue="22503" u!
s="22502"/><bf gi="2373" ue="22505" us="22505"/><bf gi="2374" ue="2250
9" us="22509"/><bf gi="2375" ue="22512" us="22512"/><bf gi="2376" ue="22522" us="22516"/><bf gi="2383" ue="22528" us="22524"/><bf gi="2388" ue="22534" us="22530"/><bf gi="2393" ue="22541" us="22536"/><bf gi="2399" ue="22549" us="22549"/><bf gi="2400" ue="22553" us="22553"/><bf gi="2401" ue="22555" us="22555"/><bf gi="2402" ue="22561" us="22557"/><bf gi="2407" ue="22564" us="22564"/><bf gi="2408" ue="22567" us="22566"/><bf gi="2410" ue="22570" us="22570"/><bf gi="2411" ue="22573" us="22573"/><bf gi="2412" ue="22578" us="22575"/><bf gi="2416" ue="22581" us="22580"/><bf gi="2418" ue="22586" us="22585"/><bf gi="2420" ue="22589" us="22589"/><bf gi="2421" ue="22593" us="22591"/><bf gi="2424" ue="22605" us="22601"/><bf gi="2429" ue="22610" us="22607"/><bf gi="2433" ue="22613" us="22612"/><bf gi="2435" ue="22618" us="22615"/><bf gi="2439" ue="22623" us="22622"/><bf gi="2441" ue="22626" us="22625"/><bf gi="2443" ue="22628" us="22628"/><bf gi="2444" ue="22633" us="22631"/><bf gi="2447!
" ue="22635" us="22635"/><bf gi="2448" ue="22640" us="22640"/><bf gi="2449" ue="22642" us="22642"/><bf gi="2450" ue="22645" us="22645"/><bf gi="2451" ue="22649" us="22648"/><bf gi="2453" ue="22652" us="22652"/><bf gi="2454" ue="22657" us="22654"/><bf gi="2458" ue="22659" us="22659"/><bf gi="2459" ue="22661" us="22661"/><bf gi="2460" ue="22666" us="22663"/><bf gi="2464" ue="22669" us="22668"/><bf gi="2466" ue="22672" us="22671"/><bf gi="2468" ue="22676" us="22675"/><bf gi="2470" ue="22679" us="22678"/><bf gi="2472" ue="22690" us="22684"/><bf gi="2479" ue="22694" us="22694"/><bf gi="2480" ue="22697" us="22696"/><bf gi="2482" ue="22699" us="22699"/><bf gi="2483" ue="22702" us="22702"/><bf gi="2484" ue="22707" us="22705"/><bf gi="2487" ue="22716" us="22712"/><bf gi="2492" ue="22718" us="22718"/><bf gi="2493" ue="22722" us="22721"/><bf gi="2495" ue="22725" us="22724"/><bf gi="2497" ue="22728" us="22727"/><bf gi="2499" ue="22730" us="22730"/><bf gi="2500" ue="22734" us="22732"/><!
bf gi="2503" ue="22746" us="22736"/><bf gi="2514" ue="22751" us="22748
"/><bf gi="2518" ue="22754" us="22753"/><bf gi="2520" ue="22757" us="22756"/><bf gi="2522" ue="22761" us="22761"/><bf gi="2523" ue="22764" us="22763"/><bf gi="2525" ue="22771" us="22766"/><bf gi="2531" ue="22775" us="22775"/><bf gi="2532" ue="22781" us="22777"/><bf gi="2537" ue="22786" us="22786"/><bf gi="2538" ue="22790" us="22789"/><bf gi="2540" ue="22796" us="22793"/><bf gi="2544" ue="22800" us="22799"/><bf gi="2546" ue="22806" us="22802"/><bf gi="2551" ue="22813" us="22808"/><bf gi="2557" ue="22821" us="22817"/><bf gi="2562" ue="22835" us="22823"/><bf gi="2575" ue="22840" us="22837"/><bf gi="2579" ue="22847" us="22846"/><bf gi="2581" ue="22852" us="22851"/><bf gi="2583" ue="22857" us="22854"/><bf gi="2587" ue="22869" us="22862"/><bf gi="2595" ue="22875" us="22871"/><bf gi="2600" ue="22883" us="22877"/><bf gi="2607" ue="22885" us="22885"/><bf gi="2608" ue="22895" us="22887"/><bf gi="2617" ue="22902" us="22898"/><bf gi="2622" ue="22905" us="22904"/><bf gi="2624" ue="22909"!
us="22907"/><bf gi="2627" ue="22916" us="22913"/><bf gi="2631" ue="22926" us="22922"/><bf gi="2636" ue="22931" us="22930"/><bf gi="2638" ue="22935" us="22933"/><bf gi="2641" ue="22937" us="22937"/><bf gi="2642" ue="22939" us="22939"/><bf gi="2643" ue="22941" us="22941"/><bf gi="2644" ue="22943" us="22943"/><bf gi="2645" ue="22949" us="22947"/><bf gi="2648" ue="22952" us="22951"/><bf gi="2650" ue="22960" us="22956"/><bf gi="2655" ue="22963" us="22962"/><bf gi="2657" ue="22967" us="22967"/><bf gi="2658" ue="22972" us="22969"/><bf gi="2662" ue="22974" us="22974"/><bf gi="2663" ue="22977" us="22977"/><bf gi="2664" ue="22980" us="22979"/><bf gi="2666" ue="22982" us="22982"/><bf gi="2667" ue="22987" us="22984"/><bf gi="2671" ue="22989" us="22989"/><bf gi="2672" ue="22996" us="22992"/><bf gi="2677" ue="23002" us="23001"/><bf gi="2679" ue="23007" us="23004"/><bf gi="2683" ue="23016" us="23011"/><bf gi="2689" ue="23019" us="23018"/><bf gi="2691" ue="23023" us="23022"/><bf gi="2693"!
ue="23026" us="23025"/><bf gi="2695" ue="23028" us="23028"/><bf gi="2
696" ue="23031" us="23030"/><bf gi="2698" ue="23035" us="23035"/><bf gi="2699" ue="23041" us="23039"/><bf gi="2702" ue="23044" us="23043"/><bf gi="2704" ue="23049" us="23049"/><bf gi="2705" ue="23054" us="23052"/><bf gi="2708" ue="23059" us="23057"/><bf gi="2711" ue="23064" us="23064"/><bf gi="2712" ue="23066" us="23066"/><bf gi="2713" ue="23068" us="23068"/><bf gi="2714" ue="23072" us="23070"/><bf gi="2717" ue="23077" us="23075"/><bf gi="2720" ue="23082" us="23079"/><bf gi="2724" ue="23085" us="23085"/><bf gi="2725" ue="23088" us="23087"/><bf gi="2727" ue="23094" us="23093"/><bf gi="2729" ue="23100" us="23100"/><bf gi="2730" ue="23105" us="23104"/><bf gi="2732" ue="23113" us="23108"/><bf gi="2738" ue="23116" us="23116"/><bf gi="2739" ue="23120" us="23120"/><bf gi="2740" ue="23125" us="23125"/><bf gi="2741" ue="23130" us="23130"/><bf gi="2742" ue="23134" us="23134"/><bf gi="2743" ue="23139" us="23138"/><bf gi="2745" ue="23143" us="23141"/><bf gi="2748" ue="23146" us="23146"/!
><bf gi="2749" ue="23149" us="23148"/><bf gi="2751" ue="23159" us="23159"/><bf gi="2752" ue="23163" us="23162"/><bf gi="2754" ue="23167" us="23166"/><bf gi="2756" ue="23179" us="23179"/><bf gi="2757" ue="23184" us="23184"/><bf gi="2758" ue="23187" us="23186"/><bf gi="2760" ue="23190" us="23190"/><bf gi="2761" ue="23196" us="23193"/><bf gi="2765" ue="23200" us="23198"/><bf gi="2768" ue="23202" us="23202"/><bf gi="2769" ue="23207" us="23207"/><bf gi="2770" ue="23212" us="23212"/><bf gi="2771" ue="23219" us="23217"/><bf gi="2774" ue="23221" us="23221"/><bf gi="2775" ue="23224" us="23224"/><bf gi="2776" ue="23231" us="23226"/><bf gi="2782" ue="23234" us="23233"/><bf gi="2784" ue="23236" us="23236"/><bf gi="2785" ue="23238" us="23238"/><bf gi="2786" ue="23241" us="23240"/><bf gi="2788" ue="23244" us="23243"/><bf gi="2790" ue="23248" us="23247"/><bf gi="2792" ue="23255" us="23254"/><bf gi="2794" ue="23258" us="23258"/><bf gi="2795" ue="23260" us="23260"/><bf gi="2796" ue="23265" !
us="23264"/><bf gi="2798" ue="23267" us="23267"/><bf gi="2799" ue="232
70" us="23269"/><bf gi="2801" ue="23274" us="23273"/><bf gi="2803" ue="23278" us="23278"/><bf gi="2804" ue="23286" us="23285"/><bf gi="2806" ue="23291" us="23290"/><bf gi="2808" ue="23293" us="23293"/><bf gi="2809" ue="23297" us="23296"/><bf gi="2811" ue="23305" us="23304"/><bf gi="2813" ue="23308" us="23307"/><bf gi="2815" ue="23319" us="23318"/><bf gi="2817" ue="23321" us="23321"/><bf gi="2818" ue="23323" us="23323"/><bf gi="2819" ue="23325" us="23325"/><bf gi="2820" ue="23330" us="23329"/><bf gi="2822" ue="23333" us="23333"/><bf gi="2823" ue="23338" us="23338"/><bf gi="2824" ue="23341" us="23340"/><bf gi="2826" ue="23344" us="23344"/><bf gi="2827" ue="23346" us="23346"/><bf gi="2828" ue="23348" us="23348"/><bf gi="2829" ue="23350" us="23350"/><bf gi="2830" ue="23352" us="23352"/><bf gi="2831" ue="23358" us="23358"/><bf gi="2832" ue="23361" us="23360"/><bf gi="2834" ue="23363" us="23363"/><bf gi="2835" ue="23365" us="23365"/><bf gi="2836" ue="23372" us="23371"/><bf gi="283!
8" ue="23378" us="23376"/><bf gi="2841" ue="23384" us="23380"/><bf gi="2846" ue="23391" us="23386"/><bf gi="2852" ue="23398" us="23395"/><bf gi="2856" ue="23401" us="23400"/><bf gi="2858" ue="23403" us="23403"/><bf gi="2859" ue="23409" us="23406"/><bf gi="2863" ue="23411" us="23411"/><bf gi="2864" ue="23413" us="23413"/><bf gi="2865" ue="23416" us="23416"/><bf gi="2866" ue="23418" us="23418"/><bf gi="2867" ue="23425" us="23420"/><bf gi="2873" ue="23441" us="23427"/><bf gi="2888" ue="23453" us="23443"/><bf gi="2899" ue="23455" us="23455"/><bf gi="2900" ue="23462" us="23458"/><bf gi="2905" ue="23465" us="23464"/><bf gi="2907" ue="23482" us="23468"/><bf gi="2922" ue="23484" us="23484"/><bf gi="2923" ue="23495" us="23487"/><bf gi="2932" ue="23497" us="23497"/><bf gi="2933" ue="23501" us="23500"/><bf gi="2935" ue="23504" us="23503"/><bf gi="2937" ue="23508" us="23506"/><bf gi="2940" ue="23515" us="23510"/><bf gi="2946" ue="23522" us="23517"/><bf gi="2952" ue="23529" us="23524"/>!
<bf gi="2958" ue="23532" us="23531"/><bf gi="2960" ue="23537" us="2353
4"/><bf gi="2964" ue="23542" us="23539"/><bf gi="2968" ue="23544" us="23544"/><bf gi="2969" ue="23546" us="23546"/><bf gi="2970" ue="23551" us="23549"/><bf gi="2973" ue="23554" us="23553"/><bf gi="2975" ue="23567" us="23556"/><bf gi="2987" ue="23569" us="23569"/><bf gi="2988" ue="23571" us="23571"/><bf gi="2989" ue="23575" us="23574"/><bf gi="2991" ue="23578" us="23578"/><bf gi="2992" ue="23584" us="23582"/><bf gi="2995" ue="23588" us="23586"/><bf gi="2998" ue="23590" us="23590"/><bf gi="2999" ue="23593" us="23592"/><bf gi="3001" ue="23598" us="23595"/><bf gi="3005" ue="23602" us="23600"/><bf gi="3008" ue="23606" us="23605"/><bf gi="3010" ue="23617" us="23608"/><bf gi="3020" ue="23622" us="23621"/><bf gi="3022" ue="23624" us="23624"/><bf gi="3023" ue="23627" us="23626"/><bf gi="3025" ue="23633" us="23629"/><bf gi="3030" ue="23635" us="23635"/><bf gi="3031" ue="23637" us="23637"/><bf gi="3032" ue="23642" us="23641"/><bf gi="3034" ue="23644" us="23644"/><bf gi="3035" ue="23646!
" us="23646"/><bf gi="3036" ue="23653" us="23648"/><bf gi="3042" ue="23657" us="23655"/><bf gi="3045" ue="23665" us="23660"/><bf gi="3051" ue="23670" us="23668"/><bf gi="3054" ue="23677" us="23673"/><bf gi="3059" ue="23688" us="23687"/><bf gi="3061" ue="23690" us="23690"/><bf gi="3062" ue="23692" us="23692"/><bf gi="3063" ue="23698" us="23695"/><bf gi="3067" ue="23700" us="23700"/><bf gi="3068" ue="23709" us="23709"/><bf gi="3069" ue="23715" us="23711"/><bf gi="3074" ue="23718" us="23718"/><bf gi="3075" ue="23724" us="23720"/><bf gi="3080" ue="23736" us="23729"/><bf gi="3088" ue="23740" us="23738"/><bf gi="3091" ue="23742" us="23742"/><bf gi="3092" ue="23749" us="23749"/><bf gi="3093" ue="23751" us="23751"/><bf gi="3094" ue="23753" us="23753"/><bf gi="3095" ue="23755" us="23755"/><bf gi="3096" ue="23762" us="23762"/><bf gi="3097" ue="23767" us="23767"/><bf gi="3098" ue="23769" us="23769"/><bf gi="3099" ue="23773" us="23773"/><bf gi="3100" ue="23777" us="23776"/><bf gi="3102!
" ue="23786" us="23784"/><bf gi="3105" ue="23794" us="23789"/><bf gi="
3111" ue="23798" us="23796"/><bf gi="3114" ue="23803" us="23802"/><bf gi="3116" ue="23805" us="23805"/><bf gi="3117" ue="23809" us="23809"/><bf gi="3118" ue="23815" us="23814"/><bf gi="3120" ue="23819" us="23819"/><bf gi="3121" ue="23822" us="23821"/><bf gi="3123" ue="23826" us="23825"/><bf gi="3125" ue="23835" us="23828"/><bf gi="3133" ue="23839" us="23839"/><bf gi="3134" ue="23844" us="23842"/><bf gi="3137" ue="23847" us="23846"/><bf gi="3139" ue="23849" us="23849"/><bf gi="3140" ue="23851" us="23851"/><bf gi="3141" ue="23857" us="23857"/><bf gi="3142" ue="23860" us="23860"/><bf gi="3143" ue="23865" us="23865"/><bf gi="3144" ue="23869" us="23869"/><bf gi="3145" ue="23871" us="23871"/><bf gi="3146" ue="23875" us="23874"/><bf gi="3148" ue="23878" us="23878"/><bf gi="3149" ue="23880" us="23880"/><bf gi="3150" ue="23884" us="23882"/><bf gi="3153" ue="23886" us="23886"/><bf gi="3154" ue="23890" us="23888"/><bf gi="3157" ue="23893" us="23893"/><bf gi="3158" ue="23897" us="23897"!
/><bf gi="3159" ue="23900" us="23900"/><bf gi="3160" ue="23906" us="23903"/><bf gi="3164" ue="23908" us="23908"/><bf gi="3165" ue="23914" us="23913"/><bf gi="3167" ue="23917" us="23916"/><bf gi="3169" ue="23920" us="23919"/><bf gi="3171" ue="23923" us="23923"/><bf gi="3172" ue="23926" us="23926"/><bf gi="3173" ue="23930" us="23929"/><bf gi="3175" ue="23935" us="23934"/><bf gi="3177" ue="23940" us="23937"/><bf gi="3181" ue="23944" us="23943"/><bf gi="3183" ue="23948" us="23946"/><bf gi="3186" ue="23952" us="23952"/><bf gi="3187" ue="23957" us="23954"/><bf gi="3191" ue="23961" us="23961"/><bf gi="3192" ue="23963" us="23963"/><bf gi="3193" ue="23965" us="23965"/><bf gi="3194" ue="23968" us="23967"/><bf gi="3196" ue="23970" us="23970"/><bf gi="3197" ue="23975" us="23975"/><bf gi="3198" ue="23980" us="23979"/><bf gi="3200" ue="23982" us="23982"/><bf gi="3201" ue="23984" us="23984"/><bf gi="3202" ue="23986" us="23986"/><bf gi="3203" ue="23988" us="23988"/><bf gi="3204" ue="23994"!
us="23991"/><bf gi="3208" ue="23997" us="23996"/><bf gi="3210" ue="24
003" us="24003"/><bf gi="3211" ue="24007" us="24007"/><bf gi="3212" ue="24009" us="24009"/><bf gi="3213" ue="24014" us="24011"/><bf gi="3217" ue="24016" us="24016"/><bf gi="3218" ue="24019" us="24018"/><bf gi="3220" ue="24022" us="24022"/><bf gi="3221" ue="24025" us="24024"/><bf gi="3223" ue="24027" us="24027"/><bf gi="3224" ue="24030" us="24029"/><bf gi="3226" ue="24033" us="24032"/><bf gi="3228" ue="24041" us="24035"/><bf gi="3235" ue="24043" us="24043"/><bf gi="3236" ue="24046" us="24046"/><bf gi="3237" ue="24053" us="24049"/><bf gi="3242" ue="24057" us="24055"/><bf gi="3245" ue="24059" us="24059"/><bf gi="3246" ue="24062" us="24061"/><bf gi="3248" ue="24064" us="24064"/><bf gi="3249" ue="24067" us="24066"/><bf gi="3251" ue="24071" us="24070"/><bf gi="3253" ue="24077" us="24075"/><bf gi="3256" ue="24082" us="24081"/><bf gi="3258" ue="24086" us="24084"/><bf gi="3261" ue="24091" us="24088"/><bf gi="3265" ue="24093" us="24093"/><bf gi="3266" ue="24096" us="24095"/><bf gi="32!
68" ue="24101" us="24101"/><bf gi="3269" ue="24104" us="24104"/><bf gi="3270" ue="24107" us="24107"/><bf gi="3271" ue="24112" us="24109"/><bf gi="3275" ue="24115" us="24114"/><bf gi="3277" ue="24120" us="24117"/><bf gi="3281" ue="24126" us="24125"/><bf gi="3283" ue="24128" us="24128"/><bf gi="3284" ue="24133" us="24131"/><bf gi="3287" ue="24135" us="24135"/><bf gi="3288" ue="24137" us="24137"/><bf gi="3289" ue="24140" us="24139"/><bf gi="3291" ue="24142" us="24142"/><bf gi="3292" ue="24145" us="24144"/><bf gi="3294" ue="24152" us="24148"/><bf gi="3299" ue="24156" us="24155"/><bf gi="3301" ue="24159" us="24158"/><bf gi="3303" ue="24164" us="24161"/><bf gi="3307" ue="24168" us="24168"/><bf gi="3308" ue="24174" us="24170"/><bf gi="3313" ue="24176" us="24176"/><bf gi="3314" ue="24182" us="24178"/><bf gi="3319" ue="24193" us="24184"/><bf gi="3329" ue="24196" us="24195"/><bf gi="3331" ue="24199" us="24199"/><bf gi="3332" ue="24203" us="24202"/><bf gi="3334" ue="24207" us="24206"/!
><bf gi="3336" ue="24215" us="24213"/><bf gi="3339" ue="24218" us="242
18"/><bf gi="3340" ue="24220" us="24220"/><bf gi="3341" ue="24224" us="24224"/><bf gi="3342" ue="24226" us="24226"/><bf gi="3343" ue="24232" us="24228"/><bf gi="3348" ue="24237" us="24234"/><bf gi="3352" ue="24241" us="24241"/><bf gi="3353" ue="24243" us="24243"/><bf gi="3354" ue="24248" us="24245"/><bf gi="3358" ue="24255" us="24253"/><bf gi="3361" ue="24259" us="24257"/><bf gi="3364" ue="24262" us="24262"/><bf gi="3365" ue="24268" us="24264"/><bf gi="3370" ue="24278" us="24270"/><bf gi="3379" ue="24291" us="24282"/><bf gi="3389" ue="24293" us="24293"/><bf gi="3390" ue="24297" us="24296"/><bf gi="3392" ue="24300" us="24299"/><bf gi="3394" ue="24305" us="24304"/><bf gi="3396" ue="24308" us="24307"/><bf gi="3398" ue="24312" us="24310"/><bf gi="3401" ue="24316" us="24314"/><bf gi="3404" ue="24319" us="24318"/><bf gi="3406" ue="24324" us="24321"/><bf gi="3410" ue="24337" us="24326"/><bf gi="3422" ue="24345" us="24339"/><bf gi="3429" ue="24349" us="24347"/><bf gi="3432" ue="2435!
1" us="24351"/><bf gi="3433" ue="24361" us="24353"/><bf gi="3442" ue="24369" us="24363"/><bf gi="3449" ue="24376" us="24372"/><bf gi="3454" ue="24385" us="24379"/><bf gi="3461" ue="24389" us="24388"/><bf gi="3463" ue="24392" us="24391"/><bf gi="3465" ue="24394" us="24394"/><bf gi="3466" ue="24398" us="24396"/><bf gi="3469" ue="24401" us="24400"/><bf gi="3471" ue="24404" us="24403"/><bf gi="3473" ue="24409" us="24406"/><bf gi="3477" ue="24413" us="24411"/><bf gi="3480" ue="24420" us="24416"/><bf gi="3485" ue="24423" us="24422"/><bf gi="3487" ue="24429" us="24425"/><bf gi="3492" ue="24437" us="24431"/><bf gi="3499" ue="24442" us="24439"/><bf gi="3503" ue="24453" us="24444"/><bf gi="3513" ue="24461" us="24455"/><bf gi="3520" ue="24467" us="24463"/><bf gi="3525" ue="24473" us="24470"/><bf gi="3529" ue="24478" us="24476"/><bf gi="3532" ue="24482" us="24480"/><bf gi="3535" ue="24484" us="24484"/><bf gi="3536" ue="24497" us="24487"/><bf gi="3547" ue="24500" us="24499"/><bf gi="354!
9" ue="24505" us="24503"/><bf gi="3552" ue="24509" us="24508"/><bf gi=
"3554" ue="24517" us="24515"/><bf gi="3557" ue="24521" us="24519"/><bf gi="3560" ue="24525" us="24523"/><bf gi="3563" ue="24532" us="24528"/><bf gi="3568" ue="24537" us="24534"/><bf gi="3572" ue="24542" us="24540"/><bf gi="3575" ue="24546" us="24544"/><bf gi="3578" ue="24548" us="24548"/><bf gi="3579" ue="24563" us="24552"/><bf gi="3591" ue="24566" us="24565"/><bf gi="3593" ue="24568" us="24568"/><bf gi="3594" ue="24573" us="24570"/><bf gi="3598" ue="24575" us="24575"/><bf gi="3599" ue="24583" us="24583"/><bf gi="3600" ue="24586" us="24586"/><bf gi="3601" ue="24592" us="24589"/><bf gi="3605" ue="24605" us="24594"/><bf gi="3617" ue="24609" us="24607"/><bf gi="3620" ue="24619" us="24612"/><bf gi="3628" ue="24621" us="24621"/><bf gi="3629" ue="24623" us="24623"/><bf gi="3630" ue="24625" us="24625"/><bf gi="3631" ue="24627" us="24627"/><bf gi="3632" ue="24629" us="24629"/><bf gi="3633" ue="24634" us="24634"/><bf gi="3634" ue="24643" us="24640"/><bf gi="3638" ue="24653" us="24646!
"/><bf gi="3646" ue="24658" us="24656"/><bf gi="3649" ue="24663" us="24660"/><bf gi="3653" ue="24666" us="24665"/><bf gi="3655" ue="24669" us="24669"/><bf gi="3656" ue="24677" us="24671"/><bf gi="3663" ue="24685" us="24679"/><bf gi="3670" ue="24689" us="24687"/><bf gi="3673" ue="24693" us="24693"/><bf gi="3674" ue="24695" us="24695"/><bf gi="3675" ue="24703" us="24702"/><bf gi="3677" ue="24710" us="24705"/><bf gi="3683" ue="24718" us="24712"/><bf gi="3690" ue="24728" us="24721"/><bf gi="3698" ue="24731" us="24730"/><bf gi="3700" ue="24736" us="24733"/><bf gi="3704" ue="24746" us="24738"/><bf gi="3713" ue="24760" us="24752"/><bf gi="3722" ue="24766" us="24763"/><bf gi="3726" ue="24770" us="24770"/><bf gi="3727" ue="24779" us="24772"/><bf gi="3735" ue="24783" us="24782"/><bf gi="3737" ue="24785" us="24785"/><bf gi="3738" ue="24789" us="24787"/><bf gi="3741" ue="24803" us="24792"/><bf gi="3753" ue="24805" us="24805"/><bf gi="3754" ue="24808" us="24807"/><bf gi="3756" ue="24829!
" us="24816"/><bf gi="3770" ue="24835" us="24832"/><bf gi="3774" ue="2
4842" us="24838"/><bf gi="3779" ue="24855" us="24844"/><bf gi="3791" ue="24860" us="24857"/><bf gi="3795" ue="24866" us="24862"/><bf gi="3800" ue="24872" us="24871"/><bf gi="3802" ue="24876" us="24874"/><bf gi="3805" ue="24881" us="24880"/><bf gi="3807" ue="24887" us="24884"/><bf gi="3811" ue="24889" us="24889"/><bf gi="3812" ue="24895" us="24892"/><bf gi="3816" ue="24898" us="24897"/><bf gi="3818" ue="24910" us="24900"/><bf gi="3829" ue="24915" us="24915"/><bf gi="3830" ue="24917" us="24917"/><bf gi="3831" ue="24922" us="24920"/><bf gi="3834" ue="24928" us="24925"/><bf gi="3838" ue="24931" us="24930"/><bf gi="3840" ue="24933" us="24933"/><bf gi="3841" ue="24936" us="24935"/><bf gi="3843" ue="24940" us="24939"/><bf gi="3845" ue="24952" us="24942"/><bf gi="3856" ue="24956" us="24955"/><bf gi="3858" ue="24964" us="24958"/><bf gi="3865" ue="24967" us="24967"/><bf gi="3866" ue="24971" us="24970"/><bf gi="3868" ue="24974" us="24973"/><bf gi="3870" ue="24980" us="24976"/><bf gi="3!
875" ue="24986" us="24982"/><bf gi="3880" ue="24989" us="24988"/><bf gi="3882" ue="24992" us="24991"/><bf gi="3884" ue="24997" us="24996"/><bf gi="3886" ue="25006" us="24999"/><bf gi="3894" ue="25010" us="25010"/><bf gi="3895" ue="25014" us="25014"/><bf gi="3896" ue="25018" us="25016"/><bf gi="3899" ue="25020" us="25020"/><bf gi="3900" ue="25022" us="25022"/><bf gi="3901" ue="25027" us="25024"/><bf gi="3905" ue="25040" us="25030"/><bf gi="3916" ue="25045" us="25045"/><bf gi="3917" ue="25055" us="25052"/><bf gi="3921" ue="25059" us="25057"/><bf gi="3924" ue="25063" us="25061"/><bf gi="3927" ue="25065" us="25065"/><bf gi="3928" ue="25069" us="25068"/><bf gi="3930" ue="25071" us="25071"/><bf gi="3931" ue="25074" us="25074"/><bf gi="3932" ue="25076" us="25076"/><bf gi="3933" ue="25080" us="25078"/><bf gi="3936" ue="25082" us="25082"/><bf gi="3937" ue="25089" us="25084"/><bf gi="3943" ue="25092" us="25091"/><bf gi="3945" ue="25098" us="25095"/><bf gi="3949" ue="25102" us="25100"!
/><bf gi="3952" ue="25110" us="25104"/><bf gi="3959" ue="25123" us="25
114"/><bf gi="3969" ue="25127" us="25126"/><bf gi="3971" ue="25131" us="25129"/><bf gi="3974" ue="25136" us="25134"/><bf gi="3977" ue="25140" us="25138"/><bf gi="3980" ue="25145" us="25144"/><bf gi="3982" ue="25147" us="25147"/><bf gi="3983" ue="25149" us="25149"/><bf gi="3984" ue="25156" us="25151"/><bf gi="3990" ue="25161" us="25158"/><bf gi="3994" ue="25166" us="25163"/><bf gi="3998" ue="25174" us="25168"/><bf gi="4005" ue="25176" us="25176"/><bf gi="4006" ue="25180" us="25178"/><bf gi="4009" ue="25182" us="25182"/><bf gi="4010" ue="25184" us="25184"/><bf gi="4011" ue="25188" us="25187"/><bf gi="4013" ue="25192" us="25192"/><bf gi="4014" ue="25199" us="25197"/><bf gi="4017" ue="25201" us="25201"/><bf gi="4018" ue="25203" us="25203"/><bf gi="4019" ue="25206" us="25206"/><bf gi="4020" ue="25210" us="25209"/><bf gi="4022" ue="25216" us="25212"/><bf gi="4027" ue="25220" us="25218"/><bf gi="4030" ue="25226" us="25225"/><bf gi="4032" ue="25240" us="25229"/><bf gi="4044" ue="252!
44" us="25243"/><bf gi="4046" ue="25246" us="25246"/><bf gi="4047" ue="25254" us="25254"/><bf gi="4048" ue="25256" us="25256"/><bf gi="4049" ue="25260" us="25259"/><bf gi="4051" ue="25265" us="25265"/><bf gi="4052" ue="25267" us="25267"/><bf gi="4053" ue="25271" us="25269"/><bf gi="4056" ue="25279" us="25273"/><bf gi="4063" ue="25282" us="25282"/><bf gi="4064" ue="25290" us="25284"/><bf gi="4071" ue="25309" us="25292"/><bf gi="4089" ue="25313" us="25312"/><bf gi="4091" ue="25322" us="25322"/><bf gi="4092" ue="25327" us="25324"/><bf gi="4096" ue="25335" us="25329"/><bf gi="4103" ue="25343" us="25340"/><bf gi="4107" ue="25348" us="25345"/><bf gi="4111" ue="25357" us="25351"/><bf gi="4118" ue="25361" us="25360"/><bf gi="4120" ue="25363" us="25363"/><bf gi="4121" ue="25366" us="25366"/><bf gi="4122" ue="25369" us="25368"/><bf gi="4124" ue="25375" us="25375"/><bf gi="4125" ue="25387" us="25383"/><bf gi="4130" ue="25389" us="25389"/><bf gi="4131" ue="25391" us="25391"/><bf gi="41!
32" ue="25398" us="25397"/><bf gi="4134" ue="25402" us="25401"/><bf gi
="4136" ue="25407" us="25404"/><bf gi="4140" ue="25412" us="25409"/><bf gi="4144" ue="25414" us="25414"/><bf gi="4145" ue="25424" us="25417"/><bf gi="4153" ue="25429" us="25426"/><bf gi="4157" ue="25432" us="25431"/><bf gi="4159" ue="25436" us="25435"/><bf gi="4161" ue="25449" us="25445"/><bf gi="4166" ue="25454" us="25451"/><bf gi="4170" ue="25458" us="25457"/><bf gi="4172" ue="25464" us="25460"/><bf gi="4177" ue="25469" us="25466"/><bf gi="4181" ue="25472" us="25471"/><bf gi="4183" ue="25476" us="25474"/><bf gi="4186" ue="25482" us="25479"/><bf gi="4190" ue="25484" us="25484"/><bf gi="4191" ue="25488" us="25486"/><bf gi="4194" ue="25490" us="25490"/><bf gi="4195" ue="25494" us="25492"/><bf gi="4198" ue="25499" us="25496"/><bf gi="4202" ue="25519" us="25502"/><bf gi="4220" ue="25522" us="25522"/><bf gi="4221" ue="25525" us="25524"/><bf gi="4223" ue="25531" us="25531"/><bf gi="4224" ue="25534" us="25533"/><bf gi="4226" ue="25537" us="25536"/><bf gi="4228" ue="25542" us="2553!
9"/><bf gi="4232" ue="25545" us="25544"/><bf gi="4234" ue="25558" us="25550"/><bf gi="4243" ue="25564" us="25562"/><bf gi="4246" ue="25569" us="25568"/><bf gi="4248" ue="25571" us="25571"/><bf gi="4249" ue="25573" us="25573"/><bf gi="4250" ue="25578" us="25577"/><bf gi="4252" ue="25580" us="25580"/><bf gi="4253" ue="25582" us="25582"/><bf gi="4254" ue="25590" us="25586"/><bf gi="4259" ue="25594" us="25592"/><bf gi="4262" ue="25606" us="25606"/><bf gi="4263" ue="25610" us="25609"/><bf gi="4265" ue="25613" us="25613"/><bf gi="4266" ue="25616" us="25615"/><bf gi="4268" ue="25620" us="25618"/><bf gi="4271" ue="25624" us="25622"/><bf gi="4274" ue="25628" us="25628"/><bf gi="4275" ue="25630" us="25630"/><bf gi="4276" ue="25632" us="25632"/><bf gi="4277" ue="25634" us="25634"/><bf gi="4278" ue="25638" us="25636"/><bf gi="4281" ue="25642" us="25640"/><bf gi="4284" ue="25645" us="25644"/><bf gi="4286" ue="25648" us="25647"/><bf gi="4288" ue="25654" us="25652"/><bf gi="4291" ue="2565!
8" us="25658"/><bf gi="4292" ue="25663" us="25661"/><bf gi="4295" ue="
25666" us="25666"/><bf gi="4296" ue="25675" us="25675"/><bf gi="4297" ue="25679" us="25678"/><bf gi="4299" ue="25684" us="25681"/><bf gi="4303" ue="25688" us="25688"/><bf gi="4304" ue="25693" us="25690"/><bf gi="4308" ue="25697" us="25695"/><bf gi="4311" ue="25699" us="25699"/><bf gi="4312" ue="25703" us="25703"/><bf gi="4313" ue="25705" us="25705"/><bf gi="4314" ue="25709" us="25709"/><bf gi="4315" ue="25711" us="25711"/><bf gi="4316" ue="25716" us="25715"/><bf gi="4318" ue="25718" us="25718"/><bf gi="4319" ue="25720" us="25720"/><bf gi="4320" ue="25723" us="25722"/><bf gi="4322" ue="25725" us="25725"/><bf gi="4323" ue="25731" us="25731"/><bf gi="4324" ue="25733" us="25733"/><bf gi="4325" ue="25736" us="25735"/><bf gi="4327" ue="25747" us="25743"/><bf gi="4332" ue="25749" us="25749"/><bf gi="4333" ue="25755" us="25752"/><bf gi="4337" ue="25759" us="25757"/><bf gi="4340" ue="25761" us="25761"/><bf gi="4341" ue="25766" us="25763"/><bf gi="4345" ue="25769" us="25768"/><bf gi="!
4347" ue="25774" us="25771"/><bf gi="4351" ue="25776" us="25776"/><bf gi="4352" ue="25779" us="25778"/><bf gi="4354" ue="25785" us="25785"/><bf gi="4355" ue="25791" us="25787"/><bf gi="4360" ue="25794" us="25793"/><bf gi="4362" ue="25797" us="25796"/><bf gi="4364" ue="25799" us="25799"/><bf gi="4365" ue="25806" us="25801"/><bf gi="4371" ue="25810" us="25808"/><bf gi="4374" ue="25813" us="25812"/><bf gi="4376" ue="25816" us="25815"/><bf gi="4378" ue="25818" us="25818"/><bf gi="4379" ue="25831" us="25824"/><bf gi="4387" ue="25834" us="25833"/><bf gi="4389" ue="25837" us="25836"/><bf gi="4391" ue="25842" us="25839"/><bf gi="4395" ue="25847" us="25844"/><bf gi="4399" ue="25851" us="25850"/><bf gi="4401" ue="25857" us="25853"/><bf gi="4406" ue="25861" us="25860"/><bf gi="4408" ue="25866" us="25864"/><bf gi="4411" ue="25871" us="25871"/><bf gi="4412" ue="25876" us="25875"/><bf gi="4414" ue="25878" us="25878"/><bf gi="4415" ue="25881" us="25880"/><bf gi="4417" ue="25887" us="25883!
"/><bf gi="4422" ue="25892" us="25890"/><bf gi="4425" ue="25894" us="2
5894"/><bf gi="4426" ue="25900" us="25897"/><bf gi="4430" ue="25903" us="25902"/><bf gi="4432" ue="25905" us="25905"/><bf gi="4433" ue="25919" us="25908"/><bf gi="4445" ue="25923" us="25923"/><bf gi="4446" ue="25925" us="25925"/><bf gi="4447" ue="25929" us="25927"/><bf gi="4450" ue="25938" us="25933"/><bf gi="4456" ue="25945" us="25940"/><bf gi="4462" ue="25952" us="25949"/><bf gi="4466" ue="25955" us="25954"/><bf gi="4468" ue="25959" us="25958"/><bf gi="4470" ue="25964" us="25963"/><bf gi="4472" ue="25968" us="25968"/><bf gi="4473" ue="25970" us="25970"/><bf gi="4474" ue="25973" us="25972"/><bf gi="4476" ue="25976" us="25975"/><bf gi="4478" ue="25978" us="25978"/><bf gi="4479" ue="25981" us="25981"/><bf gi="4480" ue="25987" us="25985"/><bf gi="4483" ue="25989" us="25989"/><bf gi="4484" ue="25994" us="25991"/><bf gi="4488" ue="25996" us="25996"/><bf gi="4489" ue="25998" us="25998"/><bf gi="4490" ue="26002" us="26000"/><bf gi="4493" ue="26005" us="26005"/><bf gi="4494" ue="26!
009" us="26007"/><bf gi="4497" ue="26013" us="26011"/><bf gi="4500" ue="26017" us="26015"/><bf gi="4503" ue="26023" us="26019"/><bf gi="4508" ue="26032" us="26027"/><bf gi="4514" ue="26036" us="26034"/><bf gi="4517" ue="26039" us="26039"/><bf gi="4518" ue="26041" us="26041"/><bf gi="4519" ue="26045" us="26044"/><bf gi="4521" ue="26047" us="26047"/><bf gi="4522" ue="26054" us="26049"/><bf gi="4528" ue="26057" us="26056"/><bf gi="4530" ue="26060" us="26059"/><bf gi="4532" ue="26064" us="26062"/><bf gi="4535" ue="26066" us="26066"/><bf gi="4536" ue="26068" us="26068"/><bf gi="4537" ue="26073" us="26070"/><bf gi="4541" ue="26075" us="26075"/><bf gi="4542" ue="26082" us="26079"/><bf gi="4546" ue="26089" us="26085"/><bf gi="4551" ue="26093" us="26092"/><bf gi="4553" ue="26098" us="26096"/><bf gi="4556" ue="26101" us="26100"/><bf gi="4558" ue="26107" us="26105"/><bf gi="4561" ue="26112" us="26110"/><bf gi="4564" ue="26116" us="26114"/><bf gi="4567" ue="26122" us="26118"/><bf gi="4!
572" ue="26127" us="26124"/><bf gi="4576" ue="26134" us="26129"/><bf g
i="4582" ue="26161" us="26140"/><bf gi="4604" ue="26167" us="26163"/><bf gi="4609" ue="26169" us="26169"/><bf gi="4610" ue="26172" us="26172"/><bf gi="4611" ue="26182" us="26175"/><bf gi="4619" ue="26188" us="26185"/><bf gi="4623" ue="26191" us="26190"/><bf gi="4625" ue="26194" us="26193"/><bf gi="4627" ue="26201" us="26199"/><bf gi="4630" ue="26210" us="26203"/><bf gi="4638" ue="26220" us="26212"/><bf gi="4647" ue="26224" us="26222"/><bf gi="4650" ue="26236" us="26227"/><bf gi="4660" ue="26241" us="26238"/><bf gi="4664" ue="26244" us="26243"/><bf gi="4666" ue="26249" us="26247"/><bf gi="4669" ue="26254" us="26251"/><bf gi="4673" ue="26258" us="26256"/><bf gi="4676" ue="26269" us="26262"/><bf gi="4684" ue="26272" us="26271"/><bf gi="4686" ue="26274" us="26274"/><bf gi="4687" ue="26276" us="26276"/><bf gi="4688" ue="26278" us="26278"/><bf gi="4689" ue="26283" us="26283"/><bf gi="4690" ue="26286" us="26285"/><bf gi="4692" ue="26290" us="26289"/><bf gi="4694" ue="26293" us="262!
92"/><bf gi="4696" ue="26297" us="26296"/><bf gi="4698" ue="26300" us="26299"/><bf gi="4700" ue="26308" us="26302"/><bf gi="4707" ue="26313" us="26311"/><bf gi="4710" ue="26316" us="26316"/><bf gi="4711" ue="26319" us="26318"/><bf gi="4713" ue="26324" us="26324"/><bf gi="4714" ue="26326" us="26326"/><bf gi="4715" ue="26333" us="26329"/><bf gi="4720" ue="26336" us="26335"/><bf gi="4722" ue="26342" us="26342"/><bf gi="4723" ue="26345" us="26344"/><bf gi="4725" ue="26348" us="26347"/><bf gi="4727" ue="26350" us="26350"/><bf gi="4728" ue="26352" us="26352"/><bf gi="4729" ue="26357" us="26354"/><bf gi="4733" ue="26368" us="26359"/><bf gi="4743" ue="26371" us="26371"/><bf gi="4744" ue="26373" us="26373"/><bf gi="4745" ue="26377" us="26375"/><bf gi="4748" ue="26379" us="26379"/><bf gi="4749" ue="26383" us="26381"/><bf gi="4752" ue="26391" us="26387"/><bf gi="4757" ue="26393" us="26393"/><bf gi="4758" ue="26400" us="26395"/><bf gi="4764" ue="26402" us="26402"/><bf gi="4765" ue="264!
08" us="26406"/><bf gi="4768" ue="26414" us="26410"/><bf gi="4773" ue=
"26417" us="26417"/><bf gi="4774" ue="26420" us="26419"/><bf gi="4776" ue="26424" us="26422"/><bf gi="4779" ue="26426" us="26426"/><bf gi="4780" ue="26431" us="26429"/><bf gi="4783" ue="26433" us="26433"/><bf gi="4784" ue="26441" us="26437"/><bf gi="4789" ue="26444" us="26444"/><bf gi="4790" ue="26449" us="26446"/><bf gi="4794" ue="26454" us="26451"/><bf gi="4798" ue="26457" us="26457"/><bf gi="4799" ue="26470" us="26460"/><bf gi="4810" ue="26474" us="26474"/><bf gi="4811" ue="26487" us="26476"/><bf gi="4823" ue="26492" us="26491"/><bf gi="4825" ue="26495" us="26494"/><bf gi="4827" ue="26497" us="26497"/><bf gi="4828" ue="26501" us="26500"/><bf gi="4830" ue="26503" us="26503"/><bf gi="4831" ue="26505" us="26505"/><bf gi="4832" ue="26508" us="26507"/><bf gi="4834" ue="26513" us="26510"/><bf gi="4838" ue="26515" us="26515"/><bf gi="4839" ue="26525" us="26517"/><bf gi="4848" ue="26530" us="26528"/><bf gi="4851" ue="26534" us="26534"/><bf gi="4852" ue="26537" us="26537"/><bf gi=!
"4853" ue="26553" us="26543"/><bf gi="4864" ue="26557" us="26555"/><bf gi="4867" ue="26566" us="26560"/><bf gi="4874" ue="26570" us="26568"/><bf gi="4877" ue="26580" us="26574"/><bf gi="4884" ue="26586" us="26583"/><bf gi="4888" ue="26590" us="26588"/><bf gi="4891" ue="26594" us="26593"/><bf gi="4893" ue="26596" us="26596"/><bf gi="4894" ue="26599" us="26598"/><bf gi="4896" ue="26601" us="26601"/><bf gi="4897" ue="26604" us="26604"/><bf gi="4898" ue="26615" us="26606"/><bf gi="4908" ue="26617" us="26617"/><bf gi="4909" ue="26619" us="26619"/><bf gi="4910" ue="26623" us="26622"/><bf gi="4912" ue="26628" us="26625"/><bf gi="4916" ue="26644" us="26643"/><bf gi="4918" ue="26647" us="26646"/><bf gi="4920" ue="26649" us="26649"/><bf gi="4921" ue="26655" us="26653"/><bf gi="4924" ue="26658" us="26657"/><bf gi="4926" ue="26669" us="26663"/><bf gi="4933" ue="26676" us="26671"/><bf gi="4939" ue="26681" us="26680"/><bf gi="4941" ue="26685" us="26683"/><bf gi="4944" ue="26694" us="2668!
7"/><bf gi="4952" ue="26696" us="26696"/><bf gi="4953" ue="26698" us="
26698"/><bf gi="4954" ue="26702" us="26700"/><bf gi="4957" ue="26709" us="26704"/><bf gi="4963" ue="26713" us="26711"/><bf gi="4966" ue="26717" us="26715"/><bf gi="4969" ue="26719" us="26719"/><bf gi="4970" ue="26723" us="26723"/><bf gi="4971" ue="26727" us="26727"/><bf gi="4972" ue="26731" us="26731"/><bf gi="4973" ue="26738" us="26734"/><bf gi="4978" ue="26743" us="26740"/><bf gi="4982" ue="26748" us="26745"/><bf gi="4986" ue="26751" us="26750"/><bf gi="4988" ue="26758" us="26753"/><bf gi="4994" ue="26760" us="26760"/><bf gi="4995" ue="26765" us="26765"/><bf gi="4996" ue="26767" us="26767"/><bf gi="4997" ue="26772" us="26771"/><bf gi="4999" ue="26776" us="26774"/><bf gi="5002" ue="26781" us="26778"/><bf gi="5006" ue="26787" us="26783"/><bf gi="5011" ue="26794" us="26789"/><bf gi="5017" ue="26803" us="26797"/><bf gi="5024" ue="26806" us="26805"/><bf gi="5026" ue="26812" us="26809"/><bf gi="5030" ue="26822" us="26820"/><bf gi="5033" ue="26829" us="26824"/><bf gi="5039" ue="2!
6842" us="26831"/><bf gi="5051" ue="26845" us="26844"/><bf gi="5053" ue="26849" us="26847"/><bf gi="5056" ue="26851" us="26851"/><bf gi="5057" ue="26853" us="26853"/><bf gi="5058" ue="26856" us="26855"/><bf gi="5060" ue="26866" us="26858"/><bf gi="5069" ue="26870" us="26869"/><bf gi="5071" ue="26877" us="26873"/><bf gi="5076" ue="26881" us="26880"/><bf gi="5078" ue="26886" us="26884"/><bf gi="5081" ue="26899" us="26888"/><bf gi="5093" ue="26903" us="26902"/><bf gi="5095" ue="26908" us="26905"/><bf gi="5099" ue="26915" us="26913"/><bf gi="5102" ue="26918" us="26917"/><bf gi="5104" ue="26920" us="26920"/><bf gi="5105" ue="26922" us="26922"/><bf gi="5106" ue="26929" us="26928"/><bf gi="5108" ue="26934" us="26931"/><bf gi="5112" ue="26937" us="26936"/><bf gi="5114" ue="26939" us="26939"/><bf gi="5115" ue="26941" us="26941"/><bf gi="5116" ue="26943" us="26943"/><bf gi="5117" ue="26946" us="26946"/><bf gi="5118" ue="26949" us="26949"/><bf gi="5119" ue="26954" us="26953"/><bf gi="!
5121" ue="26958" us="26958"/><bf gi="5122" ue="26965" us="26963"/><bf
gi="5125" ue="26967" us="26967"/><bf gi="5126" ue="26974" us="26969"/><bf gi="5132" ue="26982" us="26976"/><bf gi="5139" ue="26997" us="26984"/><bf gi="5153" ue="27010" us="26999"/><bf gi="5165" ue="27018" us="27018"/><bf gi="5166" ue="27022" us="27021"/><bf gi="5168" ue="27026" us="27025"/><bf gi="5170" ue="27030" us="27028"/><bf gi="5173" ue="27032" us="27032"/><bf gi="5174" ue="27036" us="27035"/><bf gi="5176" ue="27041" us="27040"/><bf gi="5178" ue="27048" us="27045"/><bf gi="5182" ue="27051" us="27051"/><bf gi="5183" ue="27055" us="27053"/><bf gi="5186" ue="27058" us="27057"/><bf gi="5188" ue="27060" us="27060"/><bf gi="5189" ue="27064" us="27063"/><bf gi="5191" ue="27068" us="27066"/><bf gi="5194" ue="27071" us="27070"/><bf gi="5196" ue="27073" us="27073"/><bf gi="5197" ue="27075" us="27075"/><bf gi="5198" ue="27077" us="27077"/><bf gi="5199" ue="27080" us="27079"/><bf gi="5201" ue="27086" us="27082"/><bf gi="5206" ue="27089" us="27088"/><bf gi="5208" ue="27091" us="27!
091"/><bf gi="5209" ue="27097" us="27094"/><bf gi="5213" ue="27102" us="27101"/><bf gi="5215" ue="27106" us="27106"/><bf gi="5216" ue="27109" us="27109"/><bf gi="5217" ue="27112" us="27111"/><bf gi="5219" ue="27115" us="27115"/><bf gi="5220" ue="27119" us="27117"/><bf gi="5223" ue="27123" us="27121"/><bf gi="5226" ue="27125" us="27125"/><bf gi="5227" ue="27129" us="27129"/><bf gi="5228" ue="27131" us="27131"/><bf gi="5229" ue="27139" us="27133"/><bf gi="5236" ue="27141" us="27141"/><bf gi="5237" ue="27148" us="27146"/><bf gi="5240" ue="27151" us="27151"/><bf gi="5241" ue="27157" us="27153"/><bf gi="5246" ue="27159" us="27159"/><bf gi="5247" ue="27163" us="27161"/><bf gi="5250" ue="27172" us="27165"/><bf gi="5258" ue="27179" us="27176"/><bf gi="5262" ue="27182" us="27182"/><bf gi="5263" ue="27184" us="27184"/><bf gi="5264" ue="27186" us="27186"/><bf gi="5265" ue="27195" us="27188"/><bf gi="5273" ue="27199" us="27197"/><bf gi="5276" ue="27211" us="27204"/><bf gi="5284" ue="27!
214" us="27214"/><bf gi="5285" ue="27218" us="27216"/><bf gi="5288" ue
="27222" us="27221"/><bf gi="5290" ue="27225" us="27224"/><bf gi="5292" ue="27227" us="27227"/><bf gi="5293" ue="27231" us="27231"/><bf gi="5294" ue="27234" us="27233"/><bf gi="5296" ue="27236" us="27236"/><bf gi="5297" ue="27239" us="27238"/><bf gi="5299" ue="27243" us="27242"/><bf gi="5301" ue="27251" us="27249"/><bf gi="5304" ue="27256" us="27256"/><bf gi="5305" ue="27265" us="27262"/><bf gi="5309" ue="27268" us="27267"/><bf gi="5311" ue="27271" us="27270"/><bf gi="5313" ue="27273" us="27273"/><bf gi="5314" ue="27275" us="27275"/><bf gi="5315" ue="27278" us="27277"/><bf gi="5317" ue="27281" us="27280"/><bf gi="5319" ue="27287" us="27287"/><bf gi="5320" ue="27296" us="27291"/><bf gi="5326" ue="27299" us="27298"/><bf gi="5328" ue="27301" us="27301"/><bf gi="5329" ue="27308" us="27306"/><bf gi="5332" ue="27313" us="27310"/><bf gi="5336" ue="27316" us="27315"/><bf gi="5338" ue="27320" us="27320"/><bf gi="5339" ue="27323" us="27323"/><bf gi="5340" ue="27327" us="27325"/><bf gi!
="5343" ue="27331" us="27329"/><bf gi="5346" ue="27334" us="27334"/><bf gi="5347" ue="27337" us="27336"/><bf gi="5349" ue="27340" us="27340"/><bf gi="5350" ue="27345" us="27344"/><bf gi="5352" ue="27350" us="27347"/><bf gi="5356" ue="27359" us="27354"/><bf gi="5362" ue="27362" us="27362"/><bf gi="5363" ue="27364" us="27364"/><bf gi="5364" ue="27368" us="27367"/><bf gi="5366" ue="27370" us="27370"/><bf gi="5367" ue="27372" us="27372"/><bf gi="5368" ue="27378" us="27376"/><bf gi="5371" ue="27389" us="27386"/><bf gi="5375" ue="27399" us="27394"/><bf gi="5381" ue="27402" us="27401"/><bf gi="5383" ue="27410" us="27407"/><bf gi="5387" ue="27415" us="27414"/><bf gi="5389" ue="27419" us="27419"/><bf gi="5390" ue="27425" us="27421"/><bf gi="5395" ue="27428" us="27427"/><bf gi="5397" ue="27432" us="27431"/><bf gi="5399" ue="27436" us="27435"/><bf gi="5401" ue="27439" us="27439"/><bf gi="5402" ue="27442" us="27442"/><bf gi="5403" ue="27451" us="27445"/><bf gi="5410" ue="27455" us="274!
53"/><bf gi="5413" ue="27459" us="27459"/><bf gi="5414" ue="27463" us=
"27462"/><bf gi="5416" ue="27466" us="27465"/><bf gi="5418" ue="27470" us="27468"/><bf gi="5421" ue="27472" us="27472"/><bf gi="5422" ue="27476" us="27474"/><bf gi="5425" ue="27478" us="27478"/><bf gi="5426" ue="27481" us="27480"/><bf gi="5428" ue="27483" us="27483"/><bf gi="5429" ue="27485" us="27485"/><bf gi="5430" ue="27492" us="27487"/><bf gi="5436" ue="27495" us="27494"/><bf gi="5438" ue="27499" us="27497"/><bf gi="5441" ue="27504" us="27502"/><bf gi="5444" ue="27509" us="27507"/><bf gi="5447" ue="27513" us="27512"/><bf gi="5449" ue="27515" us="27515"/><bf gi="5450" ue="27520" us="27517"/><bf gi="5454" ue="27526" us="27522"/><bf gi="5459" ue="27531" us="27529"/><bf gi="5462" ue="27533" us="27533"/><bf gi="5463" ue="27544" us="27541"/><bf gi="5467" ue="27547" us="27547"/><bf gi="5468" ue="27552" us="27550"/><bf gi="5471" ue="27556" us="27554"/><bf gi="5474" ue="27573" us="27560"/><bf gi="5488" ue="27584" us="27575"/><bf gi="5498" ue="27590" us="27587"/><bf gi="5502" ue="!
27593" us="27593"/><bf gi="5503" ue="27598" us="27595"/><bf gi="5507" ue="27604" us="27602"/><bf gi="5510" ue="27606" us="27606"/><bf gi="5511" ue="27608" us="27608"/><bf gi="5512" ue="27611" us="27610"/><bf gi="5514" ue="27615" us="27615"/><bf gi="5515" ue="27617" us="27617"/><bf gi="5516" ue="27619" us="27619"/><bf gi="5517" ue="27623" us="27622"/><bf gi="5519" ue="27628" us="27627"/><bf gi="5521" ue="27631" us="27630"/><bf gi="5523" ue="27633" us="27633"/><bf gi="5524" ue="27635" us="27635"/><bf gi="5525" ue="27639" us="27639"/><bf gi="5526" ue="27641" us="27641"/><bf gi="5527" ue="27647" us="27647"/><bf gi="5528" ue="27650" us="27650"/><bf gi="5529" ue="27653" us="27652"/><bf gi="5531" ue="27657" us="27656"/><bf gi="5533" ue="27668" us="27661"/><bf gi="5541" ue="27671" us="27671"/><bf gi="5542" ue="27673" us="27673"/><bf gi="5543" ue="27675" us="27675"/><bf gi="5544" ue="27679" us="27679"/><bf gi="5545" ue="27684" us="27683"/><bf gi="5547" ue="27688" us="27686"/><bf gi=!
"5550" ue="27692" us="27692"/><bf gi="5551" ue="27694" us="27694"/><bf
gi="5552" ue="27704" us="27699"/><bf gi="5558" ue="27707" us="27706"/><bf gi="5560" ue="27714" us="27710"/><bf gi="5565" ue="27723" us="27722"/><bf gi="5567" ue="27728" us="27725"/><bf gi="5571" ue="27730" us="27730"/><bf gi="5572" ue="27733" us="27732"/><bf gi="5574" ue="27735" us="27735"/><bf gi="5575" ue="27744" us="27737"/><bf gi="5583" ue="27746" us="27746"/><bf gi="5584" ue="27752" us="27751"/><bf gi="5586" ue="27755" us="27754"/><bf gi="5588" ue="27757" us="27757"/><bf gi="5589" ue="27760" us="27759"/><bf gi="5591" ue="27764" us="27762"/><bf gi="5594" ue="27766" us="27766"/><bf gi="5595" ue="27771" us="27768"/><bf gi="5599" ue="27774" us="27773"/><bf gi="5601" ue="27779" us="27777"/><bf gi="5604" ue="27785" us="27781"/><bf gi="5609" ue="27789" us="27788"/><bf gi="5611" ue="27792" us="27792"/><bf gi="5612" ue="27804" us="27794"/><bf gi="5623" ue="27807" us="27807"/><bf gi="5624" ue="27810" us="27809"/><bf gi="5626" ue="27819" us="27819"/><bf gi="5627" ue="27822" us="2!
7822"/><bf gi="5628" ue="27828" us="27824"/><bf gi="5633" ue="27839" us="27832"/><bf gi="5641" ue="27842" us="27841"/><bf gi="5643" ue="27846" us="27844"/><bf gi="5646" ue="27850" us="27849"/><bf gi="5648" ue="27853" us="27852"/><bf gi="5650" ue="27863" us="27855"/><bf gi="5659" ue="27869" us="27865"/><bf gi="5664" ue="27875" us="27872"/><bf gi="5668" ue="27877" us="27877"/><bf gi="5669" ue="27884" us="27879"/><bf gi="5675" ue="27892" us="27886"/><bf gi="5682" ue="27908" us="27908"/><bf gi="5683" ue="27911" us="27911"/><bf gi="5684" ue="27916" us="27914"/><bf gi="5687" ue="27919" us="27918"/><bf gi="5689" ue="27923" us="27921"/><bf gi="5692" ue="27927" us="27927"/><bf gi="5693" ue="27931" us="27929"/><bf gi="5696" ue="27935" us="27934"/><bf gi="5698" ue="27947" us="27941"/><bf gi="5705" ue="27951" us="27950"/><bf gi="5707" ue="27955" us="27953"/><bf gi="5710" ue="27958" us="27957"/><bf gi="5712" ue="27961" us="27960"/><bf gi="5714" ue="27967" us="27963"/><bf gi="5719" ue="2!
7969" us="27969"/><bf gi="5720" ue="27973" us="27972"/><bf gi="5722" u
e="27991" us="27991"/><bf gi="5723" ue="27994" us="27993"/><bf gi="5725" ue="27996" us="27996"/><bf gi="5726" ue="27999" us="27998"/><bf gi="5728" ue="28001" us="28001"/><bf gi="5729" ue="28007" us="28003"/><bf gi="5734" ue="28010" us="28009"/><bf gi="5736" ue="28012" us="28012"/><bf gi="5737" ue="28016" us="28014"/><bf gi="5740" ue="28020" us="28020"/><bf gi="5741" ue="28025" us="28023"/><bf gi="5744" ue="28028" us="28028"/><bf gi="5745" ue="28034" us="28034"/><bf gi="5746" ue="28037" us="28037"/><bf gi="5747" ue="28040" us="28039"/><bf gi="5749" ue="28044" us="28044"/><bf gi="5750" ue="28046" us="28046"/><bf gi="5751" ue="28057" us="28049"/><bf gi="5760" ue="28060" us="28059"/><bf gi="5762" ue="28074" us="28074"/><bf gi="5763" ue="28076" us="28076"/><bf gi="5764" ue="28079" us="28079"/><bf gi="5765" ue="28082" us="28082"/><bf gi="5766" ue="28085" us="28084"/><bf gi="5768" ue="28089" us="28087"/><bf gi="5771" ue="28093" us="28092"/><bf gi="5773" ue="28096" us="28095"/><bf g!
i="5775" ue="28104" us="28100"/><bf gi="5780" ue="28108" us="28106"/><bf gi="5783" ue="28111" us="28110"/><bf gi="5785" ue="28114" us="28113"/><bf gi="5787" ue="28118" us="28117"/><bf gi="5789" ue="28121" us="28120"/><bf gi="5791" ue="28123" us="28123"/><bf gi="5792" ue="28130" us="28125"/><bf gi="5798" ue="28134" us="28132"/><bf gi="5801" ue="28140" us="28136"/><bf gi="5806" ue="28156" us="28142"/><bf gi="5821" ue="28160" us="28160"/><bf gi="5822" ue="28165" us="28164"/><bf gi="5824" ue="28171" us="28167"/><bf gi="5829" ue="28179" us="28179"/><bf gi="5830" ue="28181" us="28181"/><bf gi="5831" ue="28187" us="28185"/><bf gi="5834" ue="28199" us="28189"/><bf gi="5845" ue="28201" us="28201"/><bf gi="5846" ue="28207" us="28203"/><bf gi="5851" ue="28210" us="28210"/><bf gi="5852" ue="28214" us="28214"/><bf gi="5853" ue="28220" us="28216"/><bf gi="5858" ue="28222" us="28222"/><bf gi="5859" ue="28229" us="28227"/><bf gi="5862" ue="28235" us="28232"/><bf gi="5866" ue="28239" us="28!
237"/><bf gi="5869" ue="28244" us="28241"/><bf gi="5873" ue="28248" us
="28246"/><bf gi="5876" ue="28255" us="28251"/><bf gi="5881" ue="28259" us="28258"/><bf gi="5883" ue="28264" us="28263"/><bf gi="5885" ue="28267" us="28267"/><bf gi="5886" ue="28271" us="28270"/><bf gi="5888" ue="28275" us="28274"/><bf gi="5890" ue="28278" us="28278"/><bf gi="5891" ue="28283" us="28283"/><bf gi="5892" ue="28288" us="28285"/><bf gi="5896" ue="28290" us="28290"/><bf gi="5897" ue="28301" us="28300"/><bf gi="5899" ue="28304" us="28303"/><bf gi="5901" ue="28307" us="28307"/><bf gi="5902" ue="28310" us="28310"/><bf gi="5903" ue="28313" us="28312"/><bf gi="5905" ue="28317" us="28316"/><bf gi="5907" ue="28320" us="28319"/><bf gi="5909" ue="28322" us="28322"/><bf gi="5910" ue="28325" us="28325"/><bf gi="5911" ue="28327" us="28327"/><bf gi="5912" ue="28330" us="28330"/><bf gi="5913" ue="28335" us="28333"/><bf gi="5916" ue="28339" us="28337"/><bf gi="5919" ue="28343" us="28342"/><bf gi="5921" ue="28347" us="28346"/><bf gi="5923" ue="28349" us="28349"/><bf gi="5924" ue=!
"28357" us="28351"/><bf gi="5931" ue="28367" us="28359"/><bf gi="5940" ue="28369" us="28369"/><bf gi="5941" ue="28373" us="28371"/><bf gi="5944" ue="28382" us="28381"/><bf gi="5946" ue="28399" us="28395"/><bf gi="5951" ue="28402" us="28402"/><bf gi="5952" ue="28404" us="28404"/><bf gi="5953" ue="28409" us="28407"/><bf gi="5956" ue="28411" us="28411"/><bf gi="5957" ue="28415" us="28413"/><bf gi="5960" ue="28418" us="28417"/><bf gi="5962" ue="28420" us="28420"/><bf gi="5963" ue="28422" us="28422"/><bf gi="5964" ue="28426" us="28424"/><bf gi="5967" ue="28429" us="28428"/><bf gi="5969" ue="28431" us="28431"/><bf gi="5970" ue="28433" us="28433"/><bf gi="5971" ue="28438" us="28435"/><bf gi="5975" ue="28440" us="28440"/><bf gi="5976" ue="28443" us="28442"/><bf gi="5978" ue="28448" us="28448"/><bf gi="5979" ue="28451" us="28450"/><bf gi="5981" ue="28454" us="28454"/><bf gi="5982" ue="28461" us="28457"/><bf gi="5987" ue="28467" us="28463"/><bf gi="5992" ue="28470" us="28470"/><bf gi!
="5993" ue="28472" us="28472"/><bf gi="5994" ue="28476" us="28475"/><b
f gi="5996" ue="28479" us="28478"/><bf gi="5998" ue="28481" us="28481"/><bf gi="5999" ue="28485" us="28485"/><bf gi="6000" ue="28495" us="28495"/><bf gi="6001" ue="28500" us="28497"/><bf gi="6005" ue="28511" us="28503"/><bf gi="6014" ue="28514" us="28513"/><bf gi="6016" ue="28516" us="28516"/><bf gi="6017" ue="28518" us="28518"/><bf gi="6018" ue="28520" us="28520"/><bf gi="6019" ue="28528" us="28524"/><bf gi="6024" ue="28532" us="28532"/><bf gi="6025" ue="28536" us="28536"/><bf gi="6026" ue="28538" us="28538"/><bf gi="6027" ue="28542" us="28540"/><bf gi="6030" ue="28548" us="28544"/><bf gi="6035" ue="28552" us="28550"/><bf gi="6038" ue="28558" us="28555"/><bf gi="6042" ue="28564" us="28560"/><bf gi="6047" ue="28567" us="28566"/><bf gi="6049" ue="28570" us="28570"/><bf gi="6050" ue="28577" us="28575"/><bf gi="6053" ue="28584" us="28579"/><bf gi="6059" ue="28586" us="28586"/><bf gi="6060" ue="28593" us="28590"/><bf gi="6064" ue="28595" us="28595"/><bf gi="6065" ue="28598" us="!
28597"/><bf gi="6067" ue="28601" us="28601"/><bf gi="6068" ue="28604" us="28604"/><bf gi="6069" ue="28611" us="28608"/><bf gi="6073" ue="28616" us="28613"/><bf gi="6077" ue="28618" us="28618"/><bf gi="6078" ue="28629" us="28628"/><bf gi="6080" ue="28632" us="28632"/><bf gi="6081" ue="28635" us="28634"/><bf gi="6083" ue="28641" us="28638"/><bf gi="6087" ue="28644" us="28644"/><bf gi="6088" ue="28649" us="28648"/><bf gi="6090" ue="28652" us="28651"/><bf gi="6092" ue="28657" us="28654"/><bf gi="6096" ue="28659" us="28659"/><bf gi="6097" ue="28662" us="28661"/><bf gi="6099" ue="28666" us="28665"/><bf gi="6101" ue="28670" us="28668"/><bf gi="6104" ue="28673" us="28672"/><bf gi="6106" ue="28679" us="28677"/><bf gi="6109" ue="28681" us="28681"/><bf gi="6110" ue="28683" us="28683"/><bf gi="6111" ue="28685" us="28685"/><bf gi="6112" ue="28687" us="28687"/><bf gi="6113" ue="28689" us="28689"/><bf gi="6114" ue="28693" us="28693"/><bf gi="6115" ue="28696" us="28695"/><bf gi="6117" ue="!
28699" us="28698"/><bf gi="6119" ue="28704" us="28701"/><bf gi="6123"
ue="28707" us="28707"/><bf gi="6124" ue="28712" us="28710"/><bf gi="6127" ue="28716" us="28716"/><bf gi="6128" ue="28720" us="28719"/><bf gi="6130" ue="28722" us="28722"/><bf gi="6131" ue="28724" us="28724"/><bf gi="6132" ue="28727" us="28727"/><bf gi="6133" ue="28729" us="28729"/><bf gi="6134" ue="28732" us="28732"/><bf gi="6135" ue="28734" us="28734"/><bf gi="6136" ue="28740" us="28739"/><bf gi="6138" ue="28748" us="28744"/><bf gi="6143" ue="28750" us="28750"/><bf gi="6144" ue="28753" us="28753"/><bf gi="6145" ue="28757" us="28756"/><bf gi="6147" ue="28760" us="28760"/><bf gi="6148" ue="28766" us="28765"/><bf gi="6150" ue="28773" us="28771"/><bf gi="6153" ue="28780" us="28779"/><bf gi="6155" ue="28784" us="28782"/><bf gi="6158" ue="28790" us="28789"/><bf gi="6160" ue="28792" us="28792"/><bf gi="6161" ue="28798" us="28796"/><bf gi="6164" ue="28801" us="28801"/><bf gi="6165" ue="28806" us="28805"/><bf gi="6167" ue="28810" us="28809"/><bf gi="6169" ue="28814" us="28814"/><bf !
gi="6170" ue="28818" us="28818"/><bf gi="6171" ue="28825" us="28820"/><bf gi="6177" ue="28827" us="28827"/><bf gi="6178" ue="28836" us="28836"/><bf gi="6179" ue="28849" us="28843"/><bf gi="6186" ue="28852" us="28851"/><bf gi="6188" ue="28859" us="28855"/><bf gi="6193" ue="28872" us="28872"/><bf gi="6194" ue="28875" us="28874"/><bf gi="6196" ue="28879" us="28879"/><bf gi="6197" ue="28881" us="28881"/><bf gi="6198" ue="28886" us="28883"/><bf gi="6202" ue="28889" us="28888"/><bf gi="6204" ue="28893" us="28892"/><bf gi="6206" ue="28895" us="28895"/><bf gi="6207" ue="28900" us="28900"/><bf gi="6208" ue="28913" us="28913"/><bf gi="6209" ue="28922" us="28921"/><bf gi="6211" ue="28925" us="28925"/><bf gi="6212" ue="28935" us="28931"/><bf gi="6217" ue="28937" us="28937"/><bf gi="6218" ue="28940" us="28939"/><bf gi="6220" ue="28943" us="28943"/><bf gi="6221" ue="28948" us="28948"/><bf gi="6222" ue="28954" us="28953"/><bf gi="6224" ue="28956" us="28956"/><bf gi="6225" ue="28958" us="2!
8958"/><bf gi="6226" ue="28961" us="28960"/><bf gi="6228" ue="28966" u
s="28966"/><bf gi="6229" ue="28971" us="28971"/><bf gi="6230" ue="28973" us="28973"/><bf gi="6231" ue="28977" us="28975"/><bf gi="6234" ue="28982" us="28982"/><bf gi="6235" ue="28984" us="28984"/><bf gi="6236" ue="28988" us="28988"/><bf gi="6237" ue="28993" us="28993"/><bf gi="6238" ue="28999" us="28997"/><bf gi="6241" ue="29004" us="29001"/><bf gi="6245" ue="29006" us="29006"/><bf gi="6246" ue="29008" us="29008"/><bf gi="6247" ue="29010" us="29010"/><bf gi="6248" ue="29015" us="29013"/><bf gi="6251" ue="29018" us="29017"/><bf gi="6253" ue="29020" us="29020"/><bf gi="6254" ue="29022" us="29022"/><bf gi="6255" ue="29024" us="29024"/><bf gi="6256" ue="29026" us="29026"/><bf gi="6257" ue="29033" us="29028"/><bf gi="6263" ue="29036" us="29036"/><bf gi="6264" ue="29038" us="29038"/><bf gi="6265" ue="29049" us="29049"/><bf gi="6266" ue="29053" us="29053"/><bf gi="6267" ue="29056" us="29056"/><bf gi="6268" ue="29061" us="29060"/><bf gi="6270" ue="29064" us="29063"/><bf gi="6272" ue!
="29066" us="29066"/><bf gi="6273" ue="29068" us="29068"/><bf gi="6274" ue="29071" us="29071"/><bf gi="6275" ue="29074" us="29074"/><bf gi="6276" ue="29077" us="29076"/><bf gi="6278" ue="29083" us="29081"/><bf gi="6281" ue="29088" us="29087"/><bf gi="6283" ue="29090" us="29090"/><bf gi="6284" ue="29096" us="29096"/><bf gi="6285" ue="29100" us="29100"/><bf gi="6286" ue="29107" us="29103"/><bf gi="6291" ue="29114" us="29113"/><bf gi="6293" ue="29121" us="29118"/><bf gi="6297" ue="29124" us="29123"/><bf gi="6299" ue="29129" us="29128"/><bf gi="6301" ue="29132" us="29131"/><bf gi="6303" ue="29134" us="29134"/><bf gi="6304" ue="29136" us="29136"/><bf gi="6305" ue="29143" us="29138"/><bf gi="6311" ue="29146" us="29145"/><bf gi="6313" ue="29148" us="29148"/><bf gi="6314" ue="29152" us="29151"/><bf gi="6316" ue="29159" us="29157"/><bf gi="6319" ue="29166" us="29164"/><bf gi="6322" ue="29173" us="29173"/><bf gi="6323" ue="29177" us="29176"/><bf gi="6325" ue="29180" us="29179"/><bf g!
i="6327" ue="29184" us="29182"/><bf gi="6330" ue="29193" us="29190"/><
bf gi="6334" ue="29197" us="29197"/><bf gi="6335" ue="29200" us="29200"/><bf gi="6336" ue="29203" us="29203"/><bf gi="6337" ue="29207" us="29207"/><bf gi="6338" ue="29211" us="29210"/><bf gi="6340" ue="29213" us="29213"/><bf gi="6341" ue="29215" us="29215"/><bf gi="6342" ue="29220" us="29220"/><bf gi="6343" ue="29224" us="29224"/><bf gi="6344" ue="29229" us="29226"/><bf gi="6348" ue="29232" us="29231"/><bf gi="6350" ue="29234" us="29234"/><bf gi="6351" ue="29238" us="29236"/><bf gi="6354" ue="29251" us="29240"/><bf gi="6366" ue="29256" us="29253"/><bf gi="6370" ue="29260" us="29259"/><bf gi="6372" ue="29264" us="29262"/><bf gi="6375" ue="29267" us="29266"/><bf gi="6377" ue="29270" us="29269"/><bf gi="6379" ue="29283" us="29272"/><bf gi="6391" ue="29289" us="29287"/><bf gi="6394" ue="29291" us="29291"/><bf gi="6395" ue="29295" us="29294"/><bf gi="6397" ue="29298" us="29297"/><bf gi="6399" ue="29300" us="29300"/><bf gi="6400" ue="29305" us="29303"/><bf gi="6403" ue="29314" us=!
"29307"/><bf gi="6411" ue="29316" us="29316"/><bf gi="6412" ue="29319" us="29319"/><bf gi="6413" ue="29321" us="29321"/><bf gi="6414" ue="29326" us="29325"/><bf gi="6416" ue="29331" us="29330"/><bf gi="6418" ue="29334" us="29334"/><bf gi="6419" ue="29339" us="29339"/><bf gi="6420" ue="29344" us="29344"/><bf gi="6421" ue="29346" us="29346"/><bf gi="6422" ue="29352" us="29351"/><bf gi="6424" ue="29359" us="29356"/><bf gi="6428" ue="29362" us="29361"/><bf gi="6430" ue="29364" us="29364"/><bf gi="6431" ue="29366" us="29366"/><bf gi="6432" ue="29369" us="29369"/><bf gi="6433" ue="29374" us="29374"/><bf gi="6434" ue="29380" us="29377"/><bf gi="6438" ue="29383" us="29382"/><bf gi="6440" ue="29385" us="29385"/><bf gi="6441" ue="29388" us="29388"/><bf gi="6442" ue="29390" us="29390"/><bf gi="6443" ue="29392" us="29392"/><bf gi="6444" ue="29394" us="29394"/><bf gi="6445" ue="29401" us="29397"/><bf gi="6450" ue="29403" us="29403"/><bf gi="6451" ue="29410" us="29407"/><bf gi="6455" ue=!
"29413" us="29413"/><bf gi="6456" ue="29417" us="29417"/><bf gi="6457"
ue="29421" us="29420"/><bf gi="6459" ue="29428" us="29427"/><bf gi="6461" ue="29438" us="29431"/><bf gi="6469" ue="29442" us="29442"/><bf gi="6470" ue="29445" us="29444"/><bf gi="6472" ue="29447" us="29447"/><bf gi="6473" ue="29451" us="29450"/><bf gi="6475" ue="29453" us="29453"/><bf gi="6476" ue="29459" us="29458"/><bf gi="6478" ue="29465" us="29462"/><bf gi="6482" ue="29471" us="29467"/><bf gi="6487" ue="29474" us="29474"/><bf gi="6488" ue="29477" us="29476"/><bf gi="6490" ue="29484" us="29479"/><bf gi="6496" ue="29487" us="29486"/><bf gi="6498" ue="29490" us="29489"/><bf gi="6500" ue="29495" us="29492"/><bf gi="6504" ue="29499" us="29498"/><bf gi="6506" ue="29503" us="29501"/><bf gi="6509" ue="29509" us="29507"/><bf gi="6512" ue="29520" us="29517"/><bf gi="6516" ue="29522" us="29522"/><bf gi="6517" ue="29528" us="29526"/><bf gi="6520" ue="29536" us="29533"/><bf gi="6524" ue="29539" us="29539"/><bf gi="6525" ue="29548" us="29542"/><bf gi="6532" ue="29554" us="29550"/><bf!
gi="6537" ue="29557" us="29557"/><bf gi="6538" ue="29564" us="29559"/><bf gi="6544" ue="29569" us="29568"/><bf gi="6546" ue="29575" us="29571"/><bf gi="6551" ue="29577" us="29577"/><bf gi="6552" ue="29579" us="29579"/><bf gi="6553" ue="29582" us="29582"/><bf gi="6554" ue="29584" us="29584"/><bf gi="6555" ue="29587" us="29587"/><bf gi="6556" ue="29592" us="29589"/><bf gi="6560" ue="29596" us="29596"/><bf gi="6561" ue="29600" us="29598"/><bf gi="6564" ue="29602" us="29602"/><bf gi="6565" ue="29606" us="29605"/><bf gi="6567" ue="29611" us="29609"/><bf gi="6570" ue="29613" us="29613"/><bf gi="6571" ue="29619" us="29618"/><bf gi="6573" ue="29621" us="29621"/><bf gi="6574" ue="29623" us="29623"/><bf gi="6575" ue="29625" us="29625"/><bf gi="6576" ue="29629" us="29627"/><bf gi="6579" ue="29632" us="29631"/><bf gi="6581" ue="29634" us="29634"/><bf gi="6582" ue="29638" us="29637"/><bf gi="6584" ue="29647" us="29640"/><bf gi="6592" ue="29651" us="29650"/><bf gi="6594" ue="29654" us="!
29654"/><bf gi="6595" ue="29657" us="29657"/><bf gi="6596" ue="29662"
us="29661"/><bf gi="6598" ue="29665" us="29664"/><bf gi="6600" ue="29667" us="29667"/><bf gi="6601" ue="29671" us="29669"/><bf gi="6604" ue="29674" us="29673"/><bf gi="6606" ue="29678" us="29677"/><bf gi="6608" ue="29681" us="29681"/><bf gi="6609" ue="29685" us="29684"/><bf gi="6611" ue="29691" us="29687"/><bf gi="6616" ue="29697" us="29693"/><bf gi="6621" ue="29703" us="29699"/><bf gi="6626" ue="29706" us="29705"/><bf gi="6628" ue="29713" us="29713"/><bf gi="6629" ue="29723" us="29722"/><bf gi="6631" ue="29730" us="29730"/><bf gi="6632" ue="29734" us="29732"/><bf gi="6635" ue="29750" us="29736"/><bf gi="6650" ue="29754" us="29753"/><bf gi="6652" ue="29761" us="29759"/><bf gi="6655" ue="29764" us="29763"/><bf gi="6657" ue="29767" us="29766"/><bf gi="6659" ue="29771" us="29771"/><bf gi="6660" ue="29773" us="29773"/><bf gi="6661" ue="29778" us="29777"/><bf gi="6663" ue="29781" us="29781"/><bf gi="6664" ue="29783" us="29783"/><bf gi="6665" ue="29792" us="29785"/><bf gi="6673" u!
e="29796" us="29794"/><bf gi="6676" ue="29803" us="29798"/><bf gi="6682" ue="29811" us="29805"/><bf gi="6689" ue="29814" us="29814"/><bf gi="6690" ue="29822" us="29822"/><bf gi="6691" ue="29825" us="29824"/><bf gi="6693" ue="29827" us="29827"/><bf gi="6694" ue="29831" us="29829"/><bf gi="6697" ue="29833" us="29833"/><bf gi="6698" ue="29835" us="29835"/><bf gi="6699" ue="29842" us="29839"/><bf gi="6703" ue="29850" us="29848"/><bf gi="6706" ue="29852" us="29852"/><bf gi="6707" ue="29859" us="29854"/><bf gi="6713" ue="29867" us="29862"/><bf gi="6719" ue="29874" us="29870"/><bf gi="6724" ue="29877" us="29877"/><bf gi="6725" ue="29881" us="29881"/><bf gi="6726" ue="29883" us="29883"/><bf gi="6727" ue="29885" us="29885"/><bf gi="6728" ue="29887" us="29887"/><bf gi="6729" ue="29898" us="29896"/><bf gi="6732" ue="29900" us="29900"/><bf gi="6733" ue="29904" us="29903"/><bf gi="6735" ue="29908" us="29907"/><bf gi="6737" ue="29912" us="29912"/><bf gi="6738" ue="29916" us="29914"/><bf !
gi="6741" ue="29920" us="29918"/><bf gi="6744" ue="29924" us="29922"/>
<bf gi="6747" ue="29931" us="29926"/><bf gi="6753" ue="29938" us="29934"/><bf gi="6758" ue="29940" us="29940"/><bf gi="6759" ue="29944" us="29942"/><bf gi="6762" ue="29948" us="29946"/><bf gi="6765" ue="29951" us="29951"/><bf gi="6766" ue="29953" us="29953"/><bf gi="6767" ue="29958" us="29955"/><bf gi="6771" ue="29966" us="29964"/><bf gi="6774" ue="29971" us="29969"/><bf gi="6777" ue="29976" us="29973"/><bf gi="6781" ue="29978" us="29978"/><bf gi="6782" ue="29980" us="29980"/><bf gi="6783" ue="29985" us="29982"/><bf gi="6787" ue="29996" us="29987"/><bf gi="6797" ue="30003" us="29999"/><bf gi="6802" ue="30016" us="30006"/><bf gi="6813" ue="30020" us="30019"/><bf gi="6815" ue="30034" us="30022"/><bf gi="6828" ue="30036" us="30036"/><bf gi="6829" ue="30039" us="30039"/><bf gi="6830" ue="30050" us="30041"/><bf gi="6840" ue="30055" us="30052"/><bf gi="6844" ue="30059" us="30057"/><bf gi="6847" ue="30061" us="30061"/><bf gi="6848" ue="30065" us="30063"/><bf gi="6851" ue="30068" us!
="30067"/><bf gi="6853" ue="30079" us="30070"/><bf gi="6863" ue="30082" us="30081"/><bf gi="6865" ue="30087" us="30085"/><bf gi="6868" ue="30091" us="30089"/><bf gi="6871" ue="30101" us="30094"/><bf gi="6879" ue="30106" us="30105"/><bf gi="6881" ue="30109" us="30108"/><bf gi="6883" ue="30117" us="30114"/><bf gi="6887" ue="30123" us="30123"/><bf gi="6888" ue="30133" us="30129"/><bf gi="6893" ue="30138" us="30136"/><bf gi="6896" ue="30151" us="30140"/><bf gi="6908" ue="30154" us="30154"/><bf gi="6909" ue="30159" us="30156"/><bf gi="6913" ue="30162" us="30162"/><bf gi="6914" ue="30165" us="30164"/><bf gi="6916" ue="30169" us="30167"/><bf gi="6919" ue="30172" us="30171"/><bf gi="6921" ue="30180" us="30174"/><bf gi="6928" ue="30183" us="30183"/><bf gi="6929" ue="30185" us="30185"/><bf gi="6930" ue="30188" us="30188"/><bf gi="6931" ue="30196" us="30190"/><bf gi="6938" ue="30202" us="30201"/><bf gi="6940" ue="30204" us="30204"/><bf gi="6941" ue="30212" us="30206"/><bf gi="6948" ue!
="30221" us="30215"/><bf gi="6955" ue="30223" us="30223"/><bf gi="6956
" ue="30227" us="30226"/><bf gi="6958" ue="30230" us="30229"/><bf gi="6960" ue="30233" us="30233"/><bf gi="6961" ue="30247" us="30235"/><bf gi="6974" ue="30249" us="30249"/><bf gi="6975" ue="30253" us="30253"/><bf gi="6976" ue="30256" us="30256"/><bf gi="6977" ue="30261" us="30258"/><bf gi="6981" ue="30268" us="30264"/><bf gi="6986" ue="30284" us="30272"/><bf gi="6999" ue="30290" us="30290"/><bf gi="7000" ue="30294" us="30293"/><bf gi="7002" ue="30297" us="30296"/><bf gi="7004" ue="30300" us="30300"/><bf gi="7005" ue="30303" us="30303"/><bf gi="7006" ue="30306" us="30305"/><bf gi="7008" ue="30309" us="30308"/><bf gi="7010" ue="30314" us="30311"/><bf gi="7014" ue="30322" us="30316"/><bf gi="7021" ue="30324" us="30324"/><bf gi="7022" ue="30326" us="30326"/><bf gi="7023" ue="30328" us="30328"/><bf gi="7024" ue="30334" us="30330"/><bf gi="7029" ue="30344" us="30336"/><bf gi="7038" ue="30350" us="30347"/><bf gi="7042" ue="30352" us="30352"/><bf gi="7043" ue="30355" us="30355"/><b!
f gi="7044" ue="30358" us="30357"/><bf gi="7046" ue="30368" us="30361"/><bf gi="7054" ue="30376" us="30370"/><bf gi="7061" ue="30378" us="30378"/><bf gi="7062" ue="30382" us="30381"/><bf gi="7064" ue="30384" us="30384"/><bf gi="7065" ue="30388" us="30388"/><bf gi="7066" ue="30394" us="30391"/><bf gi="7070" ue="30397" us="30397"/><bf gi="7071" ue="30399" us="30399"/><bf gi="7072" ue="30403" us="30401"/><bf gi="7075" ue="30406" us="30405"/><bf gi="7077" ue="30414" us="30408"/><bf gi="7084" ue="30418" us="30418"/><bf gi="7085" ue="30420" us="30420"/><bf gi="7086" ue="30423" us="30422"/><bf gi="7088" ue="30425" us="30425"/><bf gi="7089" ue="30428" us="30427"/><bf gi="7091" ue="30433" us="30430"/><bf gi="7095" ue="30440" us="30435"/><bf gi="7101" ue="30442" us="30442"/><bf gi="7102" ue="30444" us="30444"/><bf gi="7103" ue="30446" us="30446"/><bf gi="7104" ue="30450" us="30448"/><bf gi="7107" ue="30452" us="30452"/><bf gi="7108" ue="30454" us="30454"/><bf gi="7109" ue="30457" us=!
"30456"/><bf gi="7111" ue="30460" us="30459"/><bf gi="7113" ue="30462"
us="30462"/><bf gi="7114" ue="30465" us="30464"/><bf gi="7116" ue="30468" us="30468"/><bf gi="7117" ue="30476" us="30470"/><bf gi="7124" ue="30478" us="30478"/><bf gi="7125" ue="30482" us="30482"/><bf gi="7126" ue="30485" us="30484"/><bf gi="7128" ue="30487" us="30487"/><bf gi="7129" ue="30492" us="30489"/><bf gi="7133" ue="30496" us="30494"/><bf gi="7136" ue="30498" us="30498"/><bf gi="7137" ue="30502" us="30500"/><bf gi="7140" ue="30505" us="30504"/><bf gi="7142" ue="30511" us="30509"/><bf gi="7145" ue="30522" us="30516"/><bf gi="7152" ue="30526" us="30524"/><bf gi="7155" ue="30528" us="30528"/><bf gi="7156" ue="30530" us="30530"/><bf gi="7157" ue="30535" us="30533"/><bf gi="7160" ue="30538" us="30538"/><bf gi="7161" ue="30543" us="30541"/><bf gi="7164" ue="30546" us="30546"/><bf gi="7165" ue="30551" us="30550"/><bf gi="7167" ue="30556" us="30554"/><bf gi="7170" ue="30568" us="30558"/><bf gi="7181" ue="30572" us="30570"/><bf gi="7184" ue="30576" us="30576"/><bf gi="7185" !
ue="30580" us="30578"/><bf gi="7188" ue="30586" us="30585"/><bf gi="7190" ue="30592" us="30589"/><bf gi="7194" ue="30596" us="30596"/><bf gi="7195" ue="30606" us="30603"/><bf gi="7199" ue="30609" us="30609"/><bf gi="7200" ue="30614" us="30612"/><bf gi="7203" ue="30618" us="30618"/><bf gi="7204" ue="30624" us="30622"/><bf gi="7207" ue="30626" us="30626"/><bf gi="7208" ue="30629" us="30629"/><bf gi="7209" ue="30631" us="30631"/><bf gi="7210" ue="30634" us="30634"/><bf gi="7211" ue="30641" us="30636"/><bf gi="7217" ue="30643" us="30643"/><bf gi="7218" ue="30646" us="30645"/><bf gi="7220" ue="30649" us="30649"/><bf gi="7221" ue="30655" us="30651"/><bf gi="7226" ue="30659" us="30659"/><bf gi="7227" ue="30663" us="30663"/><bf gi="7228" ue="30665" us="30665"/><bf gi="7229" ue="30669" us="30669"/><bf gi="7230" ue="30674" us="30673"/><bf gi="7232" ue="30677" us="30677"/><bf gi="7233" ue="30679" us="30679"/><bf gi="7234" ue="30684" us="30681"/><bf gi="7238" ue="30688" us="30686"/><bf!
gi="7241" ue="30695" us="30690"/><bf gi="7247" ue="30698" us="30697"/
><bf gi="7249" ue="30705" us="30700"/><bf gi="7255" ue="30708" us="30707"/><bf gi="7257" ue="30712" us="30712"/><bf gi="7258" ue="30716" us="30715"/><bf gi="7260" ue="30722" us="30722"/><bf gi="7261" ue="30726" us="30725"/><bf gi="7263" ue="30729" us="30729"/><bf gi="7264" ue="30734" us="30732"/><bf gi="7267" ue="30738" us="30737"/><bf gi="7269" ue="30741" us="30740"/><bf gi="7271" ue="30749" us="30749"/><bf gi="7272" ue="30755" us="30752"/><bf gi="7276" ue="30759" us="30757"/><bf gi="7279" ue="30766" us="30765"/><bf gi="7281" ue="30768" us="30768"/><bf gi="7282" ue="30770" us="30770"/><bf gi="7283" ue="30773" us="30772"/><bf gi="7285" ue="30775" us="30775"/><bf gi="7286" ue="30778" us="30778"/><bf gi="7287" ue="30783" us="30783"/><bf gi="7288" ue="30789" us="30787"/><bf gi="7291" ue="30792" us="30791"/><bf gi="7293" ue="30796" us="30796"/><bf gi="7294" ue="30798" us="30798"/><bf gi="7295" ue="30802" us="30802"/><bf gi="7296" ue="30814" us="30812"/><bf gi="7299" ue="30817" u!
s="30816"/><bf gi="7301" ue="30820" us="30819"/><bf gi="7303" ue="30824" us="30824"/><bf gi="7304" ue="30828" us="30826"/><bf gi="7307" ue="30831" us="30830"/><bf gi="7309" ue="30834" us="30834"/><bf gi="7310" ue="30836" us="30836"/><bf gi="7311" ue="30842" us="30842"/><bf gi="7312" ue="30844" us="30844"/><bf gi="7313" ue="30846" us="30846"/><bf gi="7314" ue="30849" us="30849"/><bf gi="7315" ue="30855" us="30854"/><bf gi="7317" ue="30858" us="30858"/><bf gi="7318" ue="30863" us="30860"/><bf gi="7322" ue="30865" us="30865"/><bf gi="7323" ue="30869" us="30867"/><bf gi="7326" ue="30872" us="30871"/><bf gi="7328" ue="30874" us="30874"/><bf gi="7329" ue="30879" us="30877"/><bf gi="7332" ue="30881" us="30881"/><bf gi="7333" ue="30884" us="30883"/><bf gi="7335" ue="30890" us="30887"/><bf gi="7339" ue="30893" us="30892"/><bf gi="7341" ue="30899" us="30895"/><bf gi="7346" ue="30901" us="30901"/><bf gi="7347" ue="30911" us="30906"/><bf gi="7353" ue="30913" us="30913"/><bf gi="7354" u!
e="30924" us="30917"/><bf gi="7362" ue="30926" us="30926"/><bf gi="736
3" ue="30934" us="30928"/><bf gi="7370" ue="30939" us="30938"/><bf gi="7372" ue="30945" us="30943"/><bf gi="7375" ue="30948" us="30948"/><bf gi="7376" ue="30952" us="30950"/><bf gi="7379" ue="30954" us="30954"/><bf gi="7380" ue="30956" us="30956"/><bf gi="7381" ue="30959" us="30959"/><bf gi="7382" ue="30964" us="30962"/><bf gi="7385" ue="30967" us="30966"/><bf gi="7387" ue="30971" us="30970"/><bf gi="7389" ue="30973" us="30973"/><bf gi="7390" ue="30977" us="30975"/><bf gi="7393" ue="30983" us="30982"/><bf gi="7395" ue="30988" us="30988"/><bf gi="7396" ue="30990" us="30990"/><bf gi="7397" ue="30994" us="30992"/><bf gi="7400" ue="31002" us="31001"/><bf gi="7402" ue="31004" us="31004"/><bf gi="7403" ue="31008" us="31006"/><bf gi="7406" ue="31015" us="31013"/><bf gi="7409" ue="31021" us="31017"/><bf gi="7414" ue="31025" us="31024"/><bf gi="7416" ue="31029" us="31028"/><bf gi="7418" ue="31041" us="31034"/><bf gi="7426" ue="31051" us="31044"/><bf gi="7434" ue="31057" us="31055"/><!
bf gi="7437" ue="31064" us="31059"/><bf gi="7443" ue="31072" us="31066"/><bf gi="7450" ue="31074" us="31074"/><bf gi="7451" ue="31077" us="31077"/><bf gi="7452" ue="31081" us="31079"/><bf gi="7455" ue="31083" us="31083"/><bf gi="7456" ue="31085" us="31085"/><bf gi="7457" ue="31090" us="31090"/><bf gi="7458" ue="31095" us="31095"/><bf gi="7459" ue="31100" us="31097"/><bf gi="7463" ue="31105" us="31102"/><bf gi="7467" ue="31109" us="31108"/><bf gi="7469" ue="31119" us="31114"/><bf gi="7475" ue="31121" us="31121"/><bf gi="7476" ue="31126" us="31123"/><bf gi="7480" ue="31128" us="31128"/><bf gi="7481" ue="31133" us="31131"/><bf gi="7484" ue="31137" us="31137"/><bf gi="7485" ue="31147" us="31142"/><bf gi="7491" ue="31153" us="31150"/><bf gi="7495" ue="31156" us="31155"/><bf gi="7497" ue="31163" us="31160"/><bf gi="7501" ue="31170" us="31165"/><bf gi="7507" ue="31172" us="31172"/><bf gi="7508" ue="31179" us="31175"/><bf gi="7513" ue="31183" us="31183"/><bf gi="7514" ue="31186" us!
="31185"/><bf gi="7516" ue="31190" us="31188"/><bf gi="7519" ue="31192
" us="31192"/><bf gi="7520" ue="31194" us="31194"/><bf gi="7521" ue="31207" us="31197"/><bf gi="7532" ue="31213" us="31209"/><bf gi="7537" ue="31217" us="31216"/><bf gi="7539" ue="31224" us="31224"/><bf gi="7540" ue="31228" us="31227"/><bf gi="7542" ue="31232" us="31232"/><bf gi="7543" ue="31235" us="31234"/><bf gi="7545" ue="31246" us="31239"/><bf gi="7553" ue="31249" us="31249"/><bf gi="7554" ue="31253" us="31252"/><bf gi="7556" ue="31260" us="31255"/><bf gi="7562" ue="31265" us="31262"/><bf gi="7566" ue="31271" us="31271"/><bf gi="7567" ue="31275" us="31275"/><bf gi="7568" ue="31282" us="31277"/><bf gi="7574" ue="31285" us="31284"/><bf gi="7576" ue="31296" us="31287"/><bf gi="7586" ue="31305" us="31298"/><bf gi="7594" ue="31312" us="31308"/><bf gi="7599" ue="31319" us="31317"/><bf gi="7602" ue="31321" us="31321"/><bf gi="7603" ue="31325" us="31324"/><bf gi="7605" ue="31331" us="31327"/><bf gi="7610" ue="31333" us="31333"/><bf gi="7611" ue="31335" us="31335"/><bf gi="7612"!
ue="31339" us="31337"/><bf gi="7615" ue="31341" us="31341"/><bf gi="7616" ue="31344" us="31344"/><bf gi="7617" ue="31350" us="31348"/><bf gi="7620" ue="31354" us="31352"/><bf gi="7623" ue="31366" us="31357"/><bf gi="7633" ue="31368" us="31368"/><bf gi="7634" ue="31371" us="31370"/><bf gi="7636" ue="31384" us="31376"/><bf gi="7645" ue="31392" us="31390"/><bf gi="7648" ue="31395" us="31395"/><bf gi="7649" ue="31402" us="31401"/><bf gi="7651" ue="31404" us="31404"/><bf gi="7652" ue="31408" us="31406"/><bf gi="7655" ue="31411" us="31411"/><bf gi="7656" ue="31414" us="31413"/><bf gi="7658" ue="31420" us="31417"/><bf gi="7662" ue="31423" us="31423"/><bf gi="7663" ue="31439" us="31427"/><bf gi="7676" ue="31443" us="31441"/><bf gi="7679" ue="31445" us="31445"/><bf gi="7680" ue="31453" us="31449"/><bf gi="7685" ue="31459" us="31455"/><bf gi="7690" ue="31469" us="31461"/><bf gi="7699" ue="31473" us="31471"/><bf gi="7702" ue="31476" us="31476"/><bf gi="7703" ue="31478" us="31478"/><b!
f gi="7704" ue="31483" us="31480"/><bf gi="7708" ue="31487" us="31485"
/><bf gi="7711" ue="31490" us="31490"/><bf gi="7712" ue="31492" us="31492"/><bf gi="7713" ue="31496" us="31494"/><bf gi="7716" ue="31499" us="31498"/><bf gi="7718" ue="31503" us="31503"/><bf gi="7719" ue="31505" us="31505"/><bf gi="7720" ue="31508" us="31508"/><bf gi="7721" ue="31513" us="31512"/><bf gi="7723" ue="31515" us="31515"/><bf gi="7724" ue="31520" us="31518"/><bf gi="7727" ue="31523" us="31523"/><bf gi="7728" ue="31537" us="31525"/><bf gi="7741" ue="31542" us="31539"/><bf gi="7745" ue="31545" us="31545"/><bf gi="7746" ue="31549" us="31549"/><bf gi="7747" ue="31553" us="31551"/><bf gi="7750" ue="31561" us="31557"/><bf gi="7755" ue="31570" us="31563"/><bf gi="7763" ue="31574" us="31572"/><bf gi="7766" ue="31581" us="31581"/><bf gi="7767" ue="31584" us="31584"/><bf gi="7768" ue="31591" us="31588"/><bf gi="7772" ue="31594" us="31593"/><bf gi="7774" ue="31605" us="31596"/><bf gi="7784" ue="31607" us="31607"/><bf gi="7785" ue="31610" us="31610"/><bf gi="7786" ue="31620" !
us="31620"/><bf gi="7787" ue="31623" us="31622"/><bf gi="7789" ue="31625" us="31625"/><bf gi="7790" ue="31627" us="31627"/><bf gi="7791" ue="31634" us="31629"/><bf gi="7797" ue="31649" us="31636"/><bf gi="7811" ue="31653" us="31653"/><bf gi="7812" ue="31658" us="31658"/><bf gi="7813" ue="31661" us="31660"/><bf gi="7815" ue="31666" us="31663"/><bf gi="7819" ue="31670" us="31668"/><bf gi="7822" ue="31672" us="31672"/><bf gi="7823" ue="31677" us="31674"/><bf gi="7827" ue="31682" us="31680"/><bf gi="7830" ue="31692" us="31684"/><bf gi="7839" ue="31695" us="31695"/><bf gi="7840" ue="31700" us="31700"/><bf gi="7841" ue="31703" us="31702"/><bf gi="7843" ue="31707" us="31705"/><bf gi="7846" ue="31709" us="31709"/><bf gi="7847" ue="31712" us="31712"/><bf gi="7848" ue="31718" us="31716"/><bf gi="7851" ue="31722" us="31720"/><bf gi="7854" ue="31725" us="31725"/><bf gi="7855" ue="31738" us="31730"/><bf gi="7864" ue="31740" us="31740"/><bf gi="7865" ue="31742" us="31742"/><bf gi="7866" !
ue="31748" us="31744"/><bf gi="7871" ue="31751" us="31750"/><bf gi="78
73" ue="31753" us="31753"/><bf gi="7874" ue="31759" us="31755"/><bf gi="7879" ue="31764" us="31761"/><bf gi="7883" ue="31767" us="31767"/><bf gi="7884" ue="31769" us="31769"/><bf gi="7885" ue="31771" us="31771"/><bf gi="7886" ue="31777" us="31775"/><bf gi="7889" ue="31779" us="31779"/><bf gi="7890" ue="31784" us="31781"/><bf gi="7894" ue="31788" us="31786"/><bf gi="7897" ue="31793" us="31793"/><bf gi="7898" ue="31796" us="31795"/><bf gi="7900" ue="31802" us="31798"/><bf gi="7905" ue="31808" us="31805"/><bf gi="7909" ue="31811" us="31811"/><bf gi="7910" ue="31814" us="31814"/><bf gi="7911" ue="31818" us="31818"/><bf gi="7912" ue="31821" us="31820"/><bf gi="7914" ue="31830" us="31823"/><bf gi="7922" ue="31841" us="31832"/><bf gi="7932" ue="31845" us="31843"/><bf gi="7935" ue="31847" us="31847"/><bf gi="7936" ue="31849" us="31849"/><bf gi="7937" ue="31854" us="31852"/><bf gi="7940" ue="31856" us="31856"/><bf gi="7941" ue="31859" us="31858"/><bf gi="7943" ue="31861" us="31861"/>!
<bf gi="7944" ue="31865" us="31865"/><bf gi="7945" ue="31870" us="31868"/><bf gi="7948" ue="31875" us="31873"/><bf gi="7951" ue="31879" us="31878"/><bf gi="7953" ue="31881" us="31881"/><bf gi="7954" ue="31883" us="31883"/><bf gi="7955" ue="31885" us="31885"/><bf gi="7956" ue="31888" us="31887"/><bf gi="7958" ue="31890" us="31890"/><bf gi="7959" ue="31893" us="31892"/><bf gi="7961" ue="31896" us="31895"/><bf gi="7963" ue="31899" us="31899"/><bf gi="7964" ue="31906" us="31902"/><bf gi="7969" ue="31912" us="31908"/><bf gi="7974" ue="31915" us="31915"/><bf gi="7975" ue="31918" us="31917"/><bf gi="7977" ue="31923" us="31920"/><bf gi="7981" ue="31927" us="31926"/><bf gi="7983" ue="31936" us="31929"/><bf gi="7991" ue="31938" us="31938"/><bf gi="7992" ue="31941" us="31940"/><bf gi="7994" ue="31946" us="31943"/><bf gi="7998" ue="31951" us="31949"/><bf gi="8001" ue="31962" us="31954"/><bf gi="8010" ue="31968" us="31964"/><bf gi="8015" ue="31970" us="31970"/><bf gi="8016" ue="31975" u!
s="31974"/><bf gi="8018" ue="31977" us="31977"/><bf gi="8019" ue="3197
9" us="31979"/><bf gi="8020" ue="31983" us="31983"/><bf gi="8021" ue="31986" us="31986"/><bf gi="8022" ue="31990" us="31988"/><bf gi="8025" ue="31992" us="31992"/><bf gi="8026" ue="31995" us="31994"/><bf gi="8028" ue="31998" us="31998"/><bf gi="8029" ue="32000" us="32000"/><bf gi="8030" ue="32011" us="32002"/><bf gi="8040" ue="32013" us="32013"/><bf gi="8041" ue="32030" us="32015"/><bf gi="8057" ue="32035" us="32032"/><bf gi="8061" ue="32038" us="32038"/><bf gi="8062" ue="32051" us="32042"/><bf gi="8072" ue="32053" us="32053"/><bf gi="8073" ue="32058" us="32057"/><bf gi="8075" ue="32072" us="32060"/><bf gi="8088" ue="32081" us="32075"/><bf gi="8095" ue="32083" us="32083"/><bf gi="8096" ue="32087" us="32086"/><bf gi="8098" ue="32094" us="32089"/><bf gi="8104" ue="32099" us="32097"/><bf gi="8107" ue="32104" us="32101"/><bf gi="8111" ue="32106" us="32106"/><bf gi="8112" ue="32110" us="32110"/><bf gi="8113" ue="32115" us="32112"/><bf gi="8117" ue="32118" us="32117"/><bf gi="8119!
" ue="32123" us="32120"/><bf gi="8123" ue="32125" us="32125"/><bf gi="8124" ue="32127" us="32127"/><bf gi="8125" ue="32131" us="32129"/><bf gi="8128" ue="32134" us="32133"/><bf gi="8130" ue="32137" us="32136"/><bf gi="8132" ue="32141" us="32139"/><bf gi="8135" ue="32143" us="32143"/><bf gi="8136" ue="32145" us="32145"/><bf gi="8137" ue="32147" us="32147"/><bf gi="8138" ue="32151" us="32150"/><bf gi="8140" ue="32160" us="32153"/><bf gi="8148" ue="32163" us="32162"/><bf gi="8150" ue="32167" us="32166"/><bf gi="8152" ue="32187" us="32170"/><bf gi="8170" ue="32191" us="32189"/><bf gi="8173" ue="32199" us="32194"/><bf gi="8179" ue="32207" us="32202"/><bf gi="8185" ue="32210" us="32209"/><bf gi="8187" ue="32218" us="32213"/><bf gi="8193" ue="32222" us="32220"/><bf gi="8196" ue="32226" us="32224"/><bf gi="8199" ue="32230" us="32228"/><bf gi="8202" ue="32237" us="32232"/><bf gi="8208" ue="32239" us="32239"/><bf gi="8209" ue="32242" us="32241"/><bf gi="8211" ue="32246" us="32244"/><!
bf gi="8214" ue="32251" us="32249"/><bf gi="8217" ue="32257" us="32256
"/><bf gi="8219" ue="32261" us="32260"/><bf gi="8221" ue="32267" us="32264"/><bf gi="8225" ue="32274" us="32272"/><bf gi="8228" ue="32277" us="32277"/><bf gi="8229" ue="32279" us="32279"/><bf gi="8230" ue="32291" us="32283"/><bf gi="8239" ue="32296" us="32294"/><bf gi="8242" ue="32303" us="32299"/><bf gi="8247" ue="32307" us="32305"/><bf gi="8250" ue="32311" us="32309"/><bf gi="8253" ue="32315" us="32313"/><bf gi="8256" ue="32319" us="32317"/><bf gi="8259" ue="32321" us="32321"/><bf gi="8260" ue="32327" us="32323"/><bf gi="8265" ue="32331" us="32330"/><bf gi="8267" ue="32334" us="32333"/><bf gi="8269" ue="32336" us="32336"/><bf gi="8270" ue="32338" us="32338"/><bf gi="8271" ue="32342" us="32340"/><bf gi="8274" ue="32346" us="32344"/><bf gi="8277" ue="32351" us="32349"/><bf gi="8280" ue="32354" us="32353"/><bf gi="8282" ue="32359" us="32357"/><bf gi="8285" ue="32363" us="32361"/><bf gi="8288" ue="32368" us="32365"/><bf gi="8292" ue="32371" us="32371"/><bf gi="8293" ue="32377"!
us="32376"/><bf gi="8295" ue="32383" us="32379"/><bf gi="8300" ue="32387" us="32385"/><bf gi="8303" ue="32394" us="32390"/><bf gi="8308" ue="32406" us="32396"/><bf gi="8319" ue="32408" us="32408"/><bf gi="8320" ue="32414" us="32410"/><bf gi="8325" ue="32566" us="32566"/><bf gi="8326" ue="32568" us="32568"/><bf gi="8327" ue="32575" us="32570"/><bf gi="8333" ue="32581" us="32579"/><bf gi="8336" ue="32583" us="32583"/><bf gi="8337" ue="32597" us="32588"/><bf gi="8347" ue="32600" us="32600"/><bf gi="8348" ue="32605" us="32603"/><bf gi="8351" ue="32609" us="32607"/><bf gi="8354" ue="32619" us="32611"/><bf gi="8363" ue="32622" us="32621"/><bf gi="8365" ue="32626" us="32624"/><bf gi="8368" ue="32629" us="32629"/><bf gi="8369" ue="32633" us="32631"/><bf gi="8372" ue="32640" us="32637"/><bf gi="8376" ue="32643" us="32642"/><bf gi="8378" ue="32648" us="32645"/><bf gi="8382" ue="32657" us="32650"/><bf gi="8390" ue="32660" us="32660"/><bf gi="8391" ue="32663" us="32662"/><bf gi="8393"!
ue="32666" us="32666"/><bf gi="8394" ue="32670" us="32668"/><bf gi="8
397" ue="32676" us="32673"/><bf gi="8401" ue="32678" us="32678"/><bf gi="8402" ue="32682" us="32680"/><bf gi="8405" ue="32687" us="32685"/><bf gi="8408" ue="32690" us="32690"/><bf gi="8409" ue="32692" us="32692"/><bf gi="8410" ue="32694" us="32694"/><bf gi="8411" ue="32697" us="32696"/><bf gi="8413" ue="32701" us="32700"/><bf gi="8415" ue="32705" us="32703"/><bf gi="8418" ue="32707" us="32707"/><bf gi="8419" ue="32710" us="32709"/><bf gi="8421" ue="32712" us="32712"/><bf gi="8422" ue="32714" us="32714"/><bf gi="8423" ue="32716" us="32716"/><bf gi="8424" ue="32719" us="32718"/><bf gi="8426" ue="32722" us="32722"/><bf gi="8427" ue="32725" us="32724"/><bf gi="8429" ue="32731" us="32731"/><bf gi="8430" ue="32737" us="32735"/><bf gi="8433" ue="32739" us="32739"/><bf gi="8434" ue="32742" us="32741"/><bf gi="8436" ue="32745" us="32744"/><bf gi="8438" ue="32748" us="32747"/><bf gi="8440" ue="32752" us="32750"/><bf gi="8443" ue="32755" us="32754"/><bf gi="8445" ue="32769" us="32761"/!
><bf gi="8454" ue="32776" us="32771"/><bf gi="8460" ue="32793" us="32778"/><bf gi="8476" ue="32801" us="32796"/><bf gi="8482" ue="32804" us="32804"/><bf gi="8483" ue="32806" us="32806"/><bf gi="8484" ue="32808" us="32808"/><bf gi="8485" ue="32812" us="32812"/><bf gi="8486" ue="32814" us="32814"/><bf gi="8487" ue="32816" us="32816"/><bf gi="8488" ue="32823" us="32819"/><bf gi="8493" ue="32832" us="32825"/><bf gi="8501" ue="32836" us="32836"/><bf gi="8502" ue="32838" us="32838"/><bf gi="8503" ue="32842" us="32842"/><bf gi="8504" ue="32850" us="32850"/><bf gi="8505" ue="32854" us="32854"/><bf gi="8506" ue="32856" us="32856"/><bf gi="8507" ue="32858" us="32858"/><bf gi="8508" ue="32866" us="32862"/><bf gi="8513" ue="32868" us="32868"/><bf gi="8514" ue="32870" us="32870"/><bf gi="8515" ue="32872" us="32872"/><bf gi="8516" ue="32877" us="32877"/><bf gi="8517" ue="32887" us="32879"/><bf gi="8526" ue="32889" us="32889"/><bf gi="8527" ue="32895" us="32893"/><bf gi="8530" ue="32897" !
us="32897"/><bf gi="8531" ue="32905" us="32900"/><bf gi="8537" ue="329
08" us="32907"/><bf gi="8539" ue="32910" us="32910"/><bf gi="8540" ue="32915" us="32915"/><bf gi="8541" ue="32918" us="32918"/><bf gi="8542" ue="32920" us="32920"/><bf gi="8543" ue="32926" us="32922"/><bf gi="8548" ue="32930" us="32929"/><bf gi="8550" ue="32935" us="32933"/><bf gi="8553" ue="32941" us="32937"/><bf gi="8558" ue="32943" us="32943"/><bf gi="8559" ue="32946" us="32945"/><bf gi="8561" ue="32948" us="32948"/><bf gi="8562" ue="32954" us="32952"/><bf gi="8565" ue="32964" us="32963"/><bf gi="8567" ue="32966" us="32966"/><bf gi="8568" ue="32968" us="32968"/><bf gi="8569" ue="32975" us="32972"/><bf gi="8573" ue="32978" us="32978"/><bf gi="8574" ue="32987" us="32980"/><bf gi="8582" ue="32990" us="32989"/><bf gi="8584" ue="32993" us="32992"/><bf gi="8586" ue="32997" us="32996"/><bf gi="8588" ue="33012" us="33005"/><bf gi="8596" ue="33014" us="33014"/><bf gi="8597" ue="33018" us="33016"/><bf gi="8600" ue="33022" us="33020"/><bf gi="8603" ue="33027" us="33026"/><bf gi="860!
5" ue="33035" us="33029"/><bf gi="8612" ue="33048" us="33046"/><bf gi="8615" ue="33052" us="33050"/><bf gi="8618" ue="33054" us="33054"/><bf gi="8619" ue="33056" us="33056"/><bf gi="8620" ue="33060" us="33059"/><bf gi="8622" ue="33063" us="33063"/><bf gi="8623" ue="33065" us="33065"/><bf gi="8624" ue="33068" us="33068"/><bf gi="8625" ue="33073" us="33071"/><bf gi="8628" ue="33075" us="33075"/><bf gi="8629" ue="33077" us="33077"/><bf gi="8630" ue="33082" us="33081"/><bf gi="8632" ue="33084" us="33084"/><bf gi="8633" ue="33086" us="33086"/><bf gi="8634" ue="33095" us="33093"/><bf gi="8637" ue="33100" us="33098"/><bf gi="8640" ue="33102" us="33102"/><bf gi="8641" ue="33109" us="33104"/><bf gi="8647" ue="33111" us="33111"/><bf gi="8648" ue="33121" us="33119"/><bf gi="8651" ue="33129" us="33125"/><bf gi="8656" ue="33131" us="33131"/><bf gi="8657" ue="33137" us="33133"/><bf gi="8662" ue="33140" us="33140"/><bf gi="8663" ue="33146" us="33143"/><bf gi="8667" ue="33158" us="33151"/>!
<bf gi="8675" ue="33160" us="33160"/><bf gi="8676" ue="33163" us="3316
2"/><bf gi="8678" ue="33168" us="33166"/><bf gi="8681" ue="33171" us="33171"/><bf gi="8682" ue="33174" us="33173"/><bf gi="8684" ue="33176" us="33176"/><bf gi="8685" ue="33182" us="33178"/><bf gi="8690" ue="33184" us="33184"/><bf gi="8691" ue="33188" us="33186"/><bf gi="8694" ue="33193" us="33192"/><bf gi="8696" ue="33198" us="33198"/><bf gi="8697" ue="33200" us="33200"/><bf gi="8698" ue="33205" us="33202"/><bf gi="8702" ue="33208" us="33208"/><bf gi="8703" ue="33211" us="33210"/><bf gi="8705" ue="33216" us="33213"/><bf gi="8709" ue="33219" us="33218"/><bf gi="8711" ue="33222" us="33221"/><bf gi="8713" ue="33227" us="33224"/><bf gi="8717" ue="33231" us="33229"/><bf gi="8720" ue="33233" us="33233"/><bf gi="8721" ue="33235" us="33235"/><bf gi="8722" ue="33237" us="33237"/><bf gi="8723" ue="33243" us="33239"/><bf gi="8728" ue="33249" us="33245"/><bf gi="8733" ue="33253" us="33251"/><bf gi="8736" ue="33256" us="33255"/><bf gi="8738" ue="33261" us="33258"/><bf gi="8742" ue="33270!
" us="33264"/><bf gi="8749" ue="33283" us="33272"/><bf gi="8761" ue="33285" us="33285"/><bf gi="8762" ue="33290" us="33287"/><bf gi="8766" ue="33296" us="33292"/><bf gi="8771" ue="33300" us="33298"/><bf gi="8774" ue="33311" us="33302"/><bf gi="8784" ue="33314" us="33313"/><bf gi="8786" ue="33324" us="33320"/><bf gi="8791" ue="33326" us="33326"/><bf gi="8792" ue="33338" us="33330"/><bf gi="8801" ue="33344" us="33344"/><bf gi="8802" ue="33351" us="33347"/><bf gi="8807" ue="33355" us="33355"/><bf gi="8808" ue="33359" us="33358"/><bf gi="8810" ue="33361" us="33361"/><bf gi="8811" ue="33366" us="33366"/><bf gi="8812" ue="33370" us="33368"/><bf gi="8815" ue="33373" us="33372"/><bf gi="8817" ue="33376" us="33375"/><bf gi="8819" ue="33380" us="33378"/><bf gi="8822" ue="33384" us="33382"/><bf gi="8825" ue="33387" us="33386"/><bf gi="8827" ue="33391" us="33389"/><bf gi="8830" ue="33394" us="33393"/><bf gi="8832" ue="33396" us="33396"/><bf gi="8833" ue="33400" us="33398"/><bf gi="8836!
" ue="33403" us="33403"/><bf gi="8837" ue="33409" us="33405"/><bf gi="
8842" ue="33412" us="33411"/><bf gi="8844" ue="33415" us="33415"/><bf gi="8845" ue="33419" us="33417"/><bf gi="8848" ue="33422" us="33421"/><bf gi="8850" ue="33426" us="33425"/><bf gi="8852" ue="33428" us="33428"/><bf gi="8853" ue="33430" us="33430"/><bf gi="8854" ue="33435" us="33432"/><bf gi="8858" ue="33437" us="33437"/><bf gi="8859" ue="33441" us="33439"/><bf gi="8862" ue="33460" us="33443"/><bf gi="8880" ue="33471" us="33463"/><bf gi="8889" ue="33478" us="33477"/><bf gi="8891" ue="33493" us="33488"/><bf gi="8897" ue="33495" us="33495"/><bf gi="8898" ue="33500" us="33497"/><bf gi="8902" ue="33512" us="33502"/><bf gi="8913" ue="33515" us="33514"/><bf gi="8915" ue="33517" us="33517"/><bf gi="8916" ue="33519" us="33519"/><bf gi="8917" ue="33521" us="33521"/><bf gi="8918" ue="33524" us="33523"/><bf gi="8920" ue="33527" us="33526"/><bf gi="8922" ue="33531" us="33529"/><bf gi="8925" ue="33534" us="33533"/><bf gi="8927" ue="33547" us="33536"/><bf gi="8939" ue="33550" us="33550"!
/><bf gi="8940" ue="33560" us="33558"/><bf gi="8943" ue="33567" us="33563"/><bf gi="8948" ue="33571" us="33569"/><bf gi="8951" ue="33576" us="33576"/><bf gi="8952" ue="33594" us="33579"/><bf gi="8968" ue="33597" us="33596"/><bf gi="8970" ue="33600" us="33600"/><bf gi="8971" ue="33605" us="33602"/><bf gi="8975" ue="33607" us="33607"/><bf gi="8976" ue="33610" us="33609"/><bf gi="8978" ue="33624" us="33613"/><bf gi="8990" ue="33634" us="33634"/><bf gi="8991" ue="33648" us="33648"/><bf gi="8992" ue="33651" us="33651"/><bf gi="8993" ue="33653" us="33653"/><bf gi="8994" ue="33656" us="33655"/><bf gi="8996" ue="33661" us="33659"/><bf gi="8999" ue="33664" us="33663"/><bf gi="9001" ue="33666" us="33666"/><bf gi="9002" ue="33671" us="33668"/><bf gi="9006" ue="33674" us="33673"/><bf gi="9008" ue="33678" us="33677"/><bf gi="9010" ue="33686" us="33682"/><bf gi="9015" ue="33696" us="33688"/><bf gi="9024" ue="33698" us="33698"/><bf gi="9025" ue="33709" us="33702"/><bf gi="9033" ue="33713"!
us="33713"/><bf gi="9034" ue="33717" us="33717"/><bf gi="9035" ue="33
729" us="33725"/><bf gi="9040" ue="33733" us="33733"/><bf gi="9041" ue="33735" us="33735"/><bf gi="9042" ue="33738" us="33737"/><bf gi="9044" ue="33740" us="33740"/><bf gi="9045" ue="33745" us="33742"/><bf gi="9049" ue="33748" us="33747"/><bf gi="9051" ue="33750" us="33750"/><bf gi="9052" ue="33752" us="33752"/><bf gi="9053" ue="33757" us="33756"/><bf gi="9055" ue="33760" us="33759"/><bf gi="9057" ue="33771" us="33768"/><bf gi="9061" ue="33778" us="33775"/><bf gi="9065" ue="33780" us="33780"/><bf gi="9066" ue="33785" us="33782"/><bf gi="9070" ue="33789" us="33787"/><bf gi="9073" ue="33793" us="33793"/><bf gi="9074" ue="33796" us="33795"/><bf gi="9076" ue="33799" us="33798"/><bf gi="9078" ue="33807" us="33802"/><bf gi="9084" ue="33809" us="33809"/><bf gi="9085" ue="33811" us="33811"/><bf gi="9086" ue="33813" us="33813"/><bf gi="9087" ue="33817" us="33817"/><bf gi="9088" ue="33824" us="33824"/><bf gi="9089" ue="33826" us="33826"/><bf gi="9090" ue="33834" us="33833"/><bf gi="90!
92" ue="33836" us="33836"/><bf gi="9093" ue="33839" us="33839"/><bf gi="9094" ue="33841" us="33841"/><bf gi="9095" ue="33845" us="33845"/><bf gi="9096" ue="33849" us="33848"/><bf gi="9098" ue="33853" us="33852"/><bf gi="9100" ue="33866" us="33861"/><bf gi="9106" ue="33871" us="33869"/><bf gi="9109" ue="33874" us="33873"/><bf gi="9111" ue="33884" us="33878"/><bf gi="9118" ue="33895" us="33888"/><bf gi="9126" ue="33905" us="33897"/><bf gi="9135" ue="33914" us="33907"/><bf gi="9143" ue="33917" us="33916"/><bf gi="9145" ue="33922" us="33921"/><bf gi="9147" ue="33925" us="33924"/><bf gi="9149" ue="33931" us="33931"/><bf gi="9150" ue="33936" us="33936"/><bf gi="9151" ue="33941" us="33938"/><bf gi="9155" ue="33945" us="33945"/><bf gi="9156" ue="33948" us="33948"/><bf gi="9157" ue="33951" us="33950"/><bf gi="9159" ue="33953" us="33953"/><bf gi="9160" ue="33958" us="33958"/><bf gi="9161" ue="33962" us="33960"/><bf gi="9164" ue="33965" us="33965"/><bf gi="9165" ue="33967" us="33967"/!
><bf gi="9166" ue="33970" us="33969"/><bf gi="9168" ue="33972" us="339
72"/><bf gi="9169" ue="33986" us="33976"/><bf gi="9180" ue="33988" us="33988"/><bf gi="9181" ue="33997" us="33990"/><bf gi="9189" ue="34001" us="33999"/><bf gi="9192" ue="34003" us="34003"/><bf gi="9193" ue="34006" us="34006"/><bf gi="9194" ue="34010" us="34009"/><bf gi="9196" ue="34012" us="34012"/><bf gi="9197" ue="34023" us="34023"/><bf gi="9198" ue="34026" us="34026"/><bf gi="9199" ue="34028" us="34028"/><bf gi="9200" ue="34034" us="34030"/><bf gi="9205" ue="34036" us="34036"/><bf gi="9206" ue="34039" us="34039"/><bf gi="9207" ue="34045" us="34042"/><bf gi="9211" ue="34048" us="34047"/><bf gi="9213" ue="34051" us="34050"/><bf gi="9215" ue="34055" us="34054"/><bf gi="9217" ue="34060" us="34060"/><bf gi="9218" ue="34062" us="34062"/><bf gi="9219" ue="34065" us="34064"/><bf gi="9221" ue="34069" us="34067"/><bf gi="9224" ue="34072" us="34071"/><bf gi="9226" ue="34074" us="34074"/><bf gi="9227" ue="34076" us="34076"/><bf gi="9228" ue="34079" us="34078"/><bf gi="9230" ue="3408!
7" us="34081"/><bf gi="9237" ue="34093" us="34090"/><bf gi="9241" ue="34095" us="34095"/><bf gi="9242" ue="34102" us="34098"/><bf gi="9247" ue="34109" us="34109"/><bf gi="9248" ue="34113" us="34111"/><bf gi="9251" ue="34115" us="34115"/><bf gi="9252" ue="34118" us="34118"/><bf gi="9253" ue="34123" us="34120"/><bf gi="9257" ue="34131" us="34126"/><bf gi="9263" ue="34138" us="34133"/><bf gi="9269" ue="34148" us="34140"/><bf gi="9278" ue="34155" us="34152"/><bf gi="9282" ue="34157" us="34157"/><bf gi="9283" ue="34159" us="34159"/><bf gi="9284" ue="34167" us="34167"/><bf gi="9285" ue="34171" us="34169"/><bf gi="9288" ue="34177" us="34173"/><bf gi="9293" ue="34188" us="34180"/><bf gi="9302" ue="34193" us="34191"/><bf gi="9305" ue="34196" us="34195"/><bf gi="9307" ue="34201" us="34199"/><bf gi="9310" ue="34205" us="34203"/><bf gi="9313" ue="34208" us="34207"/><bf gi="9315" ue="34210" us="34210"/><bf gi="9316" ue="34224" us="34212"/><bf gi="9329" ue="34228" us="34228"/><bf gi="933!
0" ue="34234" us="34230"/><bf gi="9335" ue="34239" us="34236"/><bf gi=
"9339" ue="34242" us="34241"/><bf gi="9341" ue="34247" us="34247"/><bf gi="9342" ue="34251" us="34249"/><bf gi="9345" ue="34256" us="34253"/><bf gi="9349" ue="34261" us="34261"/><bf gi="9350" ue="34264" us="34264"/><bf gi="9351" ue="34266" us="34266"/><bf gi="9352" ue="34269" us="34268"/><bf gi="9354" ue="34272" us="34271"/><bf gi="9356" ue="34278" us="34276"/><bf gi="9359" ue="34282" us="34280"/><bf gi="9362" ue="34285" us="34285"/><bf gi="9363" ue="34291" us="34291"/><bf gi="9364" ue="34295" us="34294"/><bf gi="9366" ue="34300" us="34297"/><bf gi="9370" ue="34304" us="34302"/><bf gi="9373" ue="34306" us="34306"/><bf gi="9374" ue="34311" us="34308"/><bf gi="9378" ue="34315" us="34314"/><bf gi="9380" ue="34318" us="34317"/><bf gi="9382" ue="34323" us="34320"/><bf gi="9386" ue="34331" us="34326"/><bf gi="9392" ue="34334" us="34334"/><bf gi="9393" ue="34338" us="34337"/><bf gi="9395" ue="34343" us="34343"/><bf gi="9396" ue="34345" us="34345"/><bf gi="9397" ue="34349" us="34349!
"/><bf gi="9398" ue="34352" us="34351"/><bf gi="9400" ue="34358" us="34358"/><bf gi="9401" ue="34360" us="34360"/><bf gi="9402" ue="34362" us="34362"/><bf gi="9403" ue="34365" us="34364"/><bf gi="9405" ue="34370" us="34367"/><bf gi="9409" ue="34374" us="34374"/><bf gi="9410" ue="34382" us="34381"/><bf gi="9412" ue="34384" us="34384"/><bf gi="9413" ue="34394" us="34386"/><bf gi="9422" ue="34404" us="34396"/><bf gi="9431" ue="34407" us="34407"/><bf gi="9432" ue="34409" us="34409"/><bf gi="9433" ue="34412" us="34411"/><bf gi="9435" ue="34415" us="34415"/><bf gi="9436" ue="34417" us="34417"/><bf gi="9437" ue="34423" us="34421"/><bf gi="9440" ue="34427" us="34425"/><bf gi="9443" ue="34440" us="34440"/><bf gi="9444" ue="34445" us="34442"/><bf gi="9448" ue="34449" us="34449"/><bf gi="9449" ue="34451" us="34451"/><bf gi="9450" ue="34454" us="34453"/><bf gi="9452" ue="34456" us="34456"/><bf gi="9453" ue="34458" us="34458"/><bf gi="9454" ue="34460" us="34460"/><bf gi="9455" ue="34465!
" us="34465"/><bf gi="9456" ue="34468" us="34467"/><bf gi="9458" ue="3
4475" us="34470"/><bf gi="9464" ue="34477" us="34477"/><bf gi="9465" ue="34481" us="34479"/><bf gi="9468" ue="34489" us="34483"/><bf gi="9475" ue="34497" us="34495"/><bf gi="9478" ue="34503" us="34499"/><bf gi="9483" ue="34505" us="34505"/><bf gi="9484" ue="34507" us="34507"/><bf gi="9485" ue="34510" us="34509"/><bf gi="9487" ue="34514" us="34513"/><bf gi="9489" ue="34517" us="34516"/><bf gi="9491" ue="34519" us="34519"/><bf gi="9492" ue="34524" us="34521"/><bf gi="9496" ue="34528" us="34526"/><bf gi="9499" ue="34533" us="34531"/><bf gi="9502" ue="34535" us="34535"/><bf gi="9503" ue="34537" us="34537"/><bf gi="9504" ue="34543" us="34540"/><bf gi="9508" ue="34558" us="34552"/><bf gi="9515" ue="34560" us="34560"/><bf gi="9516" ue="34571" us="34562"/><bf gi="9526" ue="34580" us="34573"/><bf gi="9534" ue="34586" us="34584"/><bf gi="9537" ue="34588" us="34588"/><bf gi="9538" ue="34591" us="34590"/><bf gi="9540" ue="34593" us="34593"/><bf gi="9541" ue="34595" us="34595"/><bf gi="9!
542" ue="34597" us="34597"/><bf gi="9543" ue="34601" us="34600"/><bf gi="9545" ue="34607" us="34606"/><bf gi="9547" ue="34610" us="34609"/><bf gi="9549" ue="34612" us="34612"/><bf gi="9550" ue="34615" us="34615"/><bf gi="9551" ue="34624" us="34617"/><bf gi="9559" ue="34627" us="34627"/><bf gi="9560" ue="34629" us="34629"/><bf gi="9561" ue="34633" us="34633"/><bf gi="9562" ue="34638" us="34635"/><bf gi="9566" ue="34643" us="34643"/><bf gi="9567" ue="34645" us="34645"/><bf gi="9568" ue="34649" us="34647"/><bf gi="9571" ue="34653" us="34653"/><bf gi="9572" ue="34657" us="34655"/><bf gi="9575" ue="34662" us="34659"/><bf gi="9579" ue="34664" us="34664"/><bf gi="9580" ue="34666" us="34666"/><bf gi="9581" ue="34671" us="34670"/><bf gi="9583" ue="34674" us="34673"/><bf gi="9585" ue="34676" us="34676"/><bf gi="9586" ue="34678" us="34678"/><bf gi="9587" ue="34680" us="34680"/><bf gi="9588" ue="34683" us="34683"/><bf gi="9589" ue="34687" us="34687"/><bf gi="9590" ue="34697" us="34690"!
/><bf gi="9598" ue="34701" us="34699"/><bf gi="9601" ue="34704" us="34
704"/><bf gi="9602" ue="34707" us="34707"/><bf gi="9603" ue="34709" us="34709"/><bf gi="9604" ue="34713" us="34711"/><bf gi="9607" ue="34720" us="34718"/><bf gi="9610" ue="34723" us="34722"/><bf gi="9612" ue="34727" us="34727"/><bf gi="9613" ue="34735" us="34731"/><bf gi="9618" ue="34737" us="34737"/><bf gi="9619" ue="34739" us="34739"/><bf gi="9620" ue="34741" us="34741"/><bf gi="9621" ue="34747" us="34746"/><bf gi="9623" ue="34753" us="34749"/><bf gi="9628" ue="34756" us="34756"/><bf gi="9629" ue="34763" us="34758"/><bf gi="9635" ue="34766" us="34766"/><bf gi="9636" ue="34768" us="34768"/><bf gi="9637" ue="34770" us="34770"/><bf gi="9638" ue="34774" us="34773"/><bf gi="9640" ue="34778" us="34777"/><bf gi="9642" ue="34780" us="34780"/><bf gi="9643" ue="34784" us="34783"/><bf gi="9645" ue="34788" us="34786"/><bf gi="9648" ue="34795" us="34794"/><bf gi="9650" ue="34797" us="34797"/><bf gi="9651" ue="34799" us="34799"/><bf gi="9652" ue="34803" us="34801"/><bf gi="9655" ue="348!
11" us="34806"/><bf gi="9661" ue="34815" us="34814"/><bf gi="9663" ue="34817" us="34817"/><bf gi="9664" ue="34819" us="34819"/><bf gi="9665" ue="34823" us="34821"/><bf gi="9668" ue="34827" us="34825"/><bf gi="9671" ue="34838" us="34829"/><bf gi="9681" ue="34844" us="34840"/><bf gi="9686" ue="34847" us="34846"/><bf gi="9688" ue="34851" us="34849"/><bf gi="9691" ue="34856" us="34855"/><bf gi="9693" ue="34862" us="34861"/><bf gi="9695" ue="34866" us="34864"/><bf gi="9698" ue="34870" us="34869"/><bf gi="9700" ue="34876" us="34873"/><bf gi="9704" ue="34886" us="34880"/><bf gi="9711" ue="34894" us="34888"/><bf gi="9718" ue="34899" us="34897"/><bf gi="9721" ue="34916" us="34901"/><bf gi="9737" ue="34921" us="34920"/><bf gi="9739" ue="34923" us="34923"/><bf gi="9740" ue="34930" us="34928"/><bf gi="9743" ue="34933" us="34933"/><bf gi="9744" ue="34935" us="34935"/><bf gi="9745" ue="34937" us="34937"/><bf gi="9746" ue="34939" us="34939"/><bf gi="9747" ue="34946" us="34941"/><bf gi="97!
53" ue="34952" us="34952"/><bf gi="9754" ue="34955" us="34955"/><bf gi
="9755" ue="34957" us="34957"/><bf gi="9756" ue="34962" us="34962"/><bf gi="9757" ue="34972" us="34966"/><bf gi="9764" ue="34976" us="34974"/><bf gi="9767" ue="34978" us="34978"/><bf gi="9768" ue="34980" us="34980"/><bf gi="9769" ue="34984" us="34984"/><bf gi="9770" ue="34987" us="34986"/><bf gi="9772" ue="34990" us="34990"/><bf gi="9773" ue="34993" us="34992"/><bf gi="9775" ue="34997" us="34996"/><bf gi="9777" ue="34999" us="34999"/><bf gi="9778" ue="35002" us="35002"/><bf gi="9779" ue="35013" us="35005"/><bf gi="9788" ue="35023" us="35018"/><bf gi="9794" ue="35029" us="35025"/><bf gi="9799" ue="35033" us="35032"/><bf gi="9801" ue="35039" us="35035"/><bf gi="9806" ue="35041" us="35041"/><bf gi="9807" ue="35048" us="35047"/><bf gi="9809" ue="35061" us="35055"/><bf gi="9816" ue="35065" us="35063"/><bf gi="9819" ue="35070" us="35068"/><bf gi="9822" ue="35074" us="35073"/><bf gi="9824" ue="35076" us="35076"/><bf gi="9825" ue="35079" us="35078"/><bf gi="9827" ue="35082" us="3508!
2"/><bf gi="9828" ue="35088" us="35084"/><bf gi="9833" ue="35091" us="35090"/><bf gi="9835" ue="35094" us="35093"/><bf gi="9837" ue="35098" us="35096"/><bf gi="9840" ue="35102" us="35100"/><bf gi="9843" ue="35104" us="35104"/><bf gi="9844" ue="35112" us="35109"/><bf gi="9848" ue="35115" us="35114"/><bf gi="9850" ue="35122" us="35120"/><bf gi="9853" ue="35126" us="35125"/><bf gi="9855" ue="35131" us="35128"/><bf gi="9859" ue="35134" us="35134"/><bf gi="9860" ue="35142" us="35136"/><bf gi="9867" ue="35145" us="35145"/><bf gi="9868" ue="35149" us="35148"/><bf gi="9870" ue="35151" us="35151"/><bf gi="9871" ue="35154" us="35154"/><bf gi="9872" ue="35159" us="35158"/><bf gi="9874" ue="35164" us="35162"/><bf gi="9877" ue="35172" us="35166"/><bf gi="9884" ue="35174" us="35174"/><bf gi="9885" ue="35179" us="35178"/><bf gi="9887" ue="35184" us="35181"/><bf gi="9891" ue="35189" us="35186"/><bf gi="9895" ue="35191" us="35191"/><bf gi="9896" ue="35199" us="35194"/><bf gi="9902" ue="3520!
1" us="35201"/><bf gi="9903" ue="35203" us="35203"/><bf gi="9904" ue="
35211" us="35206"/><bf gi="9910" ue="35213" us="35213"/><bf gi="9911" ue="35216" us="35215"/><bf gi="9913" ue="35224" us="35219"/><bf gi="9919" ue="35228" us="35226"/><bf gi="9922" ue="35233" us="35231"/><bf gi="9925" ue="35239" us="35237"/><bf gi="9928" ue="35242" us="35241"/><bf gi="9930" ue="35244" us="35244"/><bf gi="9931" ue="35248" us="35247"/><bf gi="9933" ue="35255" us="35250"/><bf gi="9939" ue="35258" us="35258"/><bf gi="9940" ue="35261" us="35260"/><bf gi="9942" ue="35264" us="35263"/><bf gi="9944" ue="35282" us="35282"/><bf gi="9945" ue="35288" us="35284"/><bf gi="9950" ue="35290" us="35290"/><bf gi="9951" ue="35293" us="35292"/><bf gi="9953" ue="35299" us="35299"/><bf gi="9954" ue="35303" us="35301"/><bf gi="9957" ue="35305" us="35305"/><bf gi="9958" ue="35307" us="35307"/><bf gi="9959" ue="35309" us="35309"/><bf gi="9960" ue="35313" us="35313"/><bf gi="9961" ue="35316" us="35315"/><bf gi="9963" ue="35318" us="35318"/><bf gi="9964" ue="35321" us="35320"/><bf gi="!
9966" ue="35325" us="35325"/><bf gi="9967" ue="35328" us="35327"/><bf gi="9969" ue="35333" us="35330"/><bf gi="9973" ue="35336" us="35335"/><bf gi="9975" ue="35338" us="35338"/><bf gi="9976" ue="35340" us="35340"/><bf gi="9977" ue="35352" us="35342"/><bf gi="9988" ue="35355" us="35355"/><bf gi="9989" ue="35360" us="35357"/><bf gi="9993" ue="35366" us="35362"/><bf gi="9998" ue="35373" us="35370"/><bf gi="10002" ue="35375" us="35375"/><bf gi="10003" ue="35377" us="35377"/><bf gi="10004" ue="35383" us="35379"/><bf gi="10009" ue="35390" us="35386"/><bf gi="10014" ue="35393" us="35392"/><bf gi="10016" ue="35395" us="35395"/><bf gi="10017" ue="35401" us="35397"/><bf gi="10022" ue="35406" us="35405"/><bf gi="10024" ue="35416" us="35408"/><bf gi="10033" ue="35422" us="35419"/><bf gi="10037" ue="35427" us="35424"/><bf gi="10041" ue="35431" us="35429"/><bf gi="10044" ue="35433" us="35433"/><bf gi="10045" ue="35438" us="35435"/><bf gi="10049" ue="35443" us="35440"/><bf gi="10053" ue="!
35447" us="35445"/><bf gi="10056" ue="35452" us="35449"/><bf gi="10060
" ue="35456" us="35454"/><bf gi="10063" ue="35463" us="35458"/><bf gi="10069" ue="35465" us="35465"/><bf gi="10070" ue="35469" us="35467"/><bf gi="10073" ue="35475" us="35471"/><bf gi="10078" ue="35482" us="35477"/><bf gi="10084" ue="35489" us="35486"/><bf gi="10088" ue="35497" us="35491"/><bf gi="10095" ue="35504" us="35500"/><bf gi="10100" ue="35507" us="35506"/><bf gi="10102" ue="35511" us="35510"/><bf gi="10104" ue="35513" us="35513"/><bf gi="10105" ue="35516" us="35515"/><bf gi="10107" ue="35519" us="35518"/><bf gi="10109" ue="35524" us="35522"/><bf gi="10112" ue="35533" us="35526"/><bf gi="10120" ue="35535" us="35535"/><bf gi="10121" ue="35543" us="35537"/><bf gi="10128" ue="35554" us="35546"/><bf gi="10137" ue="35556" us="35556"/><bf gi="10138" ue="35559" us="35558"/><bf gi="10140" ue="35566" us="35563"/><bf gi="10144" ue="35569" us="35568"/><bf gi="10146" ue="35576" us="35571"/><bf gi="10152" ue="35578" us="35578"/><bf gi="10153" ue="35580" us="35580"/><bf gi="10154"!
ue="35586" us="35582"/><bf gi="10159" ue="35591" us="35588"/><bf gi="10163" ue="35596" us="35594"/><bf gi="10166" ue="35598" us="35598"/><bf gi="10167" ue="35601" us="35600"/><bf gi="10169" ue="35604" us="35604"/><bf gi="10170" ue="35607" us="35606"/><bf gi="10172" ue="35617" us="35609"/><bf gi="10181" ue="35622" us="35622"/><bf gi="10182" ue="35624" us="35624"/><bf gi="10183" ue="35629" us="35627"/><bf gi="10186" ue="35632" us="35632"/><bf gi="10187" ue="35635" us="35635"/><bf gi="10188" ue="35639" us="35639"/><bf gi="10189" ue="35641" us="35641"/><bf gi="10190" ue="35644" us="35644"/><bf gi="10191" ue="35646" us="35646"/><bf gi="10192" ue="35654" us="35649"/><bf gi="10198" ue="35657" us="35656"/><bf gi="10200" ue="35663" us="35660"/><bf gi="10204" ue="35668" us="35666"/><bf gi="10207" ue="35670" us="35670"/><bf gi="10208" ue="35676" us="35672"/><bf gi="10213" ue="35679" us="35678"/><bf gi="10215" ue="35683" us="35683"/><bf gi="10216" ue="35686" us="35686"/><bf gi="10217"!
ue="35693" us="35691"/><bf gi="10220" ue="35698" us="35695"/><bf gi="
10224" ue="35700" us="35700"/><bf gi="10225" ue="35705" us="35702"/><bf gi="10229" ue="35713" us="35708"/><bf gi="10235" ue="35717" us="35715"/><bf gi="10238" ue="35728" us="35722"/><bf gi="10245" ue="35734" us="35730"/><bf gi="10250" ue="35738" us="35737"/><bf gi="10252" ue="35740" us="35740"/><bf gi="10253" ue="35743" us="35742"/><bf gi="10255" ue="35898" us="35895"/><bf gi="10259" ue="35903" us="35901"/><bf gi="10262" ue="35905" us="35905"/><bf gi="10263" ue="35916" us="35909"/><bf gi="10271" ue="35921" us="35918"/><bf gi="10275" ue="35925" us="35923"/><bf gi="10278" ue="35931" us="35927"/><bf gi="10283" ue="35933" us="35933"/><bf gi="10284" ue="35940" us="35937"/><bf gi="10288" ue="35942" us="35942"/><bf gi="10289" ue="35949" us="35944"/><bf gi="10295" ue="35955" us="35955"/><bf gi="10296" ue="35958" us="35957"/><bf gi="10298" ue="35964" us="35960"/><bf gi="10303" ue="35966" us="35966"/><bf gi="10304" ue="35970" us="35970"/><bf gi="10305" ue="35975" us="35973"/><bf gi="1!
0308" ue="35982" us="35977"/><bf gi="10314" ue="35984" us="35984"/><bf gi="10315" ue="35988" us="35986"/><bf gi="10318" ue="35993" us="35992"/><bf gi="10320" ue="35998" us="35995"/><bf gi="10324" ue="36002" us="36000"/><bf gi="10327" ue="36004" us="36004"/><bf gi="10328" ue="36016" us="36007"/><bf gi="10338" ue="36020" us="36018"/><bf gi="10341" ue="36029" us="36022"/><bf gi="10349" ue="36043" us="36031"/><bf gi="10362" ue="36047" us="36045"/><bf gi="10365" ue="36049" us="36049"/><bf gi="10366" ue="36051" us="36051"/><bf gi="10367" ue="36054" us="36053"/><bf gi="10369" ue="36062" us="36057"/><bf gi="10375" ue="36068" us="36064"/><bf gi="10380" ue="36070" us="36070"/><bf gi="10381" ue="36072" us="36072"/><bf gi="10382" ue="36074" us="36074"/><bf gi="10383" ue="36077" us="36076"/><bf gi="10385" ue="36080" us="36079"/><bf gi="10387" ue="36082" us="36082"/><bf gi="10388" ue="36085" us="36084"/><bf gi="10390" ue="36088" us="36087"/><bf gi="10392" ue="36095" us="36090"/><bf gi="1!
0398" ue="36097" us="36097"/><bf gi="10399" ue="36101" us="36099"/><bf
gi="10402" ue="36107" us="36103"/><bf gi="10407" ue="36109" us="36109"/><bf gi="10408" ue="36112" us="36111"/><bf gi="10410" ue="36116" us="36114"/><bf gi="10413" ue="36119" us="36118"/><bf gi="10415" ue="36123" us="36123"/><bf gi="10416" ue="36199" us="36196"/><bf gi="10420" ue="36201" us="36201"/><bf gi="10421" ue="36206" us="36203"/><bf gi="10425" ue="36209" us="36208"/><bf gi="10427" ue="36212" us="36211"/><bf gi="10429" ue="36215" us="36214"/><bf gi="10431" ue="36223" us="36223"/><bf gi="10432" ue="36226" us="36225"/><bf gi="10434" ue="36229" us="36228"/><bf gi="10436" ue="36232" us="36232"/><bf gi="10437" ue="36234" us="36234"/><bf gi="10438" ue="36237" us="36237"/><bf gi="10439" ue="36241" us="36240"/><bf gi="10441" ue="36245" us="36245"/><bf gi="10442" ue="36249" us="36249"/><bf gi="10443" ue="36256" us="36254"/><bf gi="10446" ue="36259" us="36259"/><bf gi="10447" ue="36262" us="36262"/><bf gi="10448" ue="36264" us="36264"/><bf gi="10449" ue="36268" us="36267"/><bf !
gi="10451" ue="36271" us="36271"/><bf gi="10452" ue="36275" us="36274"/><bf gi="10454" ue="36277" us="36277"/><bf gi="10455" ue="36279" us="36279"/><bf gi="10456" ue="36284" us="36281"/><bf gi="10460" ue="36286" us="36286"/><bf gi="10461" ue="36288" us="36288"/><bf gi="10462" ue="36290" us="36290"/><bf gi="10463" ue="36296" us="36293"/><bf gi="10467" ue="36300" us="36298"/><bf gi="10470" ue="36303" us="36302"/><bf gi="10472" ue="36305" us="36305"/><bf gi="10473" ue="36311" us="36308"/><bf gi="10477" ue="36315" us="36313"/><bf gi="10480" ue="36317" us="36317"/><bf gi="10481" ue="36319" us="36319"/><bf gi="10482" ue="36321" us="36321"/><bf gi="10483" ue="36325" us="36323"/><bf gi="10486" ue="36328" us="36327"/><bf gi="10488" ue="36332" us="36330"/><bf gi="10491" ue="36341" us="36335"/><bf gi="10498" ue="36349" us="36348"/><bf gi="10500" ue="36351" us="36351"/><bf gi="10501" ue="36353" us="36353"/><bf gi="10502" ue="36358" us="36356"/><bf gi="10505" ue="36363" us="36360"/><bf !
gi="10509" ue="36369" us="36367"/><bf gi="10512" ue="36372" us="36372"
/><bf gi="10513" ue="36374" us="36374"/><bf gi="10514" ue="36387" us="36381"/><bf gi="10521" ue="36391" us="36390"/><bf gi="10523" ue="36394" us="36394"/><bf gi="10524" ue="36401" us="36400"/><bf gi="10526" ue="36409" us="36403"/><bf gi="10533" ue="36413" us="36413"/><bf gi="10534" ue="36418" us="36416"/><bf gi="10537" ue="36420" us="36420"/><bf gi="10538" ue="36432" us="36423"/><bf gi="10548" ue="36437" us="36436"/><bf gi="10550" ue="36441" us="36441"/><bf gi="10551" ue="36452" us="36443"/><bf gi="10561" ue="36457" us="36457"/><bf gi="10562" ue="36461" us="36460"/><bf gi="10564" ue="36466" us="36463"/><bf gi="10568" ue="36468" us="36468"/><bf gi="10569" ue="36470" us="36470"/><bf gi="10570" ue="36476" us="36473"/><bf gi="10574" ue="36485" us="36481"/><bf gi="10579" ue="36487" us="36487"/><bf gi="10580" ue="36491" us="36489"/><bf gi="10583" ue="36493" us="36493"/><bf gi="10584" ue="36501" us="36496"/><bf gi="10590" ue="36507" us="36505"/><bf gi="10593" ue="36510" us="36509"/!
><bf gi="10595" ue="36514" us="36513"/><bf gi="10597" ue="36519" us="36519"/><bf gi="10598" ue="36529" us="36521"/><bf gi="10607" ue="36531" us="36531"/><bf gi="10608" ue="36533" us="36533"/><bf gi="10609" ue="36539" us="36538"/><bf gi="10611" ue="36542" us="36542"/><bf gi="10612" ue="36545" us="36544"/><bf gi="10614" ue="36552" us="36547"/><bf gi="10620" ue="36557" us="36554"/><bf gi="10624" ue="36559" us="36559"/><bf gi="10625" ue="36562" us="36561"/><bf gi="10627" ue="36564" us="36564"/><bf gi="10628" ue="36572" us="36571"/><bf gi="10630" ue="36575" us="36575"/><bf gi="10631" ue="36579" us="36578"/><bf gi="10633" ue="36584" us="36584"/><bf gi="10634" ue="36587" us="36587"/><bf gi="10635" ue="36590" us="36589"/><bf gi="10637" ue="36593" us="36592"/><bf gi="10639" ue="36606" us="36599"/><bf gi="10647" ue="36608" us="36608"/><bf gi="10648" ue="36611" us="36610"/><bf gi="10650" ue="36613" us="36613"/><bf gi="10651" ue="36618" us="36615"/><bf gi="10655" ue="36620" us="36620"/!
><bf gi="10656" ue="36624" us="36623"/><bf gi="10658" ue="36633" us="3
6626"/><bf gi="10666" ue="36641" us="36635"/><bf gi="10673" ue="36643" us="36643"/><bf gi="10674" ue="36650" us="36645"/><bf gi="10680" ue="36655" us="36652"/><bf gi="10684" ue="36667" us="36659"/><bf gi="10693" ue="36679" us="36670"/><bf gi="10703" ue="36681" us="36681"/><bf gi="10704" ue="36687" us="36684"/><bf gi="10708" ue="36693" us="36689"/><bf gi="10713" ue="36696" us="36695"/><bf gi="10715" ue="36703" us="36700"/><bf gi="10719" ue="36709" us="36705"/><bf gi="10724" ue="36769" us="36763"/><bf gi="10731" ue="36776" us="36771"/><bf gi="10737" ue="36786" us="36781"/><bf gi="10743" ue="36792" us="36789"/><bf gi="10747" ue="36796" us="36794"/><bf gi="10750" ue="36802" us="36798"/><bf gi="10755" ue="36806" us="36804"/><bf gi="10758" ue="36811" us="36810"/><bf gi="10760" ue="36814" us="36813"/><bf gi="10762" ue="36821" us="36816"/><bf gi="10768" ue="36826" us="36826"/><bf gi="10769" ue="36832" us="36832"/><bf gi="10770" ue="36838" us="36834"/><bf gi="10775" ue="36843" us="36!
840"/><bf gi="10779" ue="36849" us="36845"/><bf gi="10784" ue="36859" us="36852"/><bf gi="10792" ue="36862" us="36861"/><bf gi="10794" ue="36870" us="36864"/><bf gi="10801" ue="36872" us="36872"/><bf gi="10802" ue="36881" us="36875"/><bf gi="10809" ue="36891" us="36883"/><bf gi="10818" ue="36899" us="36893"/><bf gi="10825" ue="36906" us="36903"/><bf gi="10829" ue="36911" us="36908"/><bf gi="10833" ue="36921" us="36913"/><bf gi="10842" ue="36924" us="36924"/><bf gi="10843" ue="36927" us="36926"/><bf gi="10845" ue="36933" us="36929"/><bf gi="10850" ue="36935" us="36935"/><bf gi="10851" ue="36950" us="36937"/><bf gi="10865" ue="36953" us="36952"/><bf gi="10867" ue="36958" us="36955"/><bf gi="10871" ue="36963" us="36960"/><bf gi="10875" ue="36969" us="36965"/><bf gi="10880" ue="36976" us="36972"/><bf gi="10885" ue="36978" us="36978"/><bf gi="10886" ue="36986" us="36980"/><bf gi="10893" ue="36989" us="36988"/><bf gi="10895" ue="36997" us="36991"/><bf gi="10902" ue="37004" us="36!
999"/><bf gi="10908" ue="37009" us="37006"/><bf gi="10912" ue="37013"
us="37013"/><bf gi="10913" ue="37017" us="37015"/><bf gi="10916" ue="37019" us="37019"/><bf gi="10917" ue="37027" us="37024"/><bf gi="10921" ue="37030" us="37029"/><bf gi="10923" ue="37032" us="37032"/><bf gi="10924" ue="37034" us="37034"/><bf gi="10925" ue="37046" us="37039"/><bf gi="10933" ue="37048" us="37048"/><bf gi="10934" ue="37054" us="37053"/><bf gi="10936" ue="37057" us="37057"/><bf gi="10937" ue="37061" us="37059"/><bf gi="10940" ue="37064" us="37063"/><bf gi="10942" ue="37066" us="37066"/><bf gi="10943" ue="37068" us="37068"/><bf gi="10944" ue="37070" us="37070"/><bf gi="10945" ue="37074" us="37074"/><bf gi="10946" ue="37077" us="37077"/><bf gi="10947" ue="37081" us="37079"/><bf gi="10950" ue="37087" us="37083"/><bf gi="10955" ue="37090" us="37089"/><bf gi="10957" ue="37093" us="37092"/><bf gi="10959" ue="37096" us="37096"/><bf gi="10960" ue="37099" us="37099"/><bf gi="10961" ue="37101" us="37101"/><bf gi="10962" ue="37104" us="37103"/><bf gi="10964" ue="37111" u!
s="37108"/><bf gi="10968" ue="37120" us="37117"/><bf gi="10972" ue="37122" us="37122"/><bf gi="10973" ue="37126" us="37124"/><bf gi="10976" ue="37128" us="37128"/><bf gi="10977" ue="37133" us="37133"/><bf gi="10978" ue="37136" us="37136"/><bf gi="10979" ue="37138" us="37138"/><bf gi="10980" ue="37146" us="37140"/><bf gi="10987" ue="37148" us="37148"/><bf gi="10988" ue="37150" us="37150"/><bf gi="10989" ue="37152" us="37152"/><bf gi="10990" ue="37155" us="37154"/><bf gi="10992" ue="37157" us="37157"/><bf gi="10993" ue="37159" us="37159"/><bf gi="10994" ue="37161" us="37161"/><bf gi="10995" ue="37170" us="37165"/><bf gi="11001" ue="37172" us="37172"/><bf gi="11002" ue="37175" us="37174"/><bf gi="11004" ue="37178" us="37177"/><bf gi="11006" ue="37181" us="37180"/><bf gi="11008" ue="37187" us="37187"/><bf gi="11009" ue="37199" us="37191"/><bf gi="11018" ue="37204" us="37202"/><bf gi="11021" ue="37211" us="37206"/><bf gi="11027" ue="37221" us="37217"/><bf gi="11032" ue="37223" u!
s="37223"/><bf gi="11033" ue="37226" us="37225"/><bf gi="11035" ue="37
229" us="37228"/><bf gi="11037" ue="37237" us="37234"/><bf gi="11041" ue="37243" us="37239"/><bf gi="11046" ue="37251" us="37249"/><bf gi="11049" ue="37255" us="37253"/><bf gi="11052" ue="37259" us="37257"/><bf gi="11055" ue="37262" us="37261"/><bf gi="11057" ue="37269" us="37264"/><bf gi="11063" ue="37272" us="37271"/><bf gi="11065" ue="37276" us="37276"/><bf gi="11066" ue="37278" us="37278"/><bf gi="11067" ue="37282" us="37281"/><bf gi="11069" ue="37284" us="37284"/><bf gi="11070" ue="37286" us="37286"/><bf gi="11071" ue="37288" us="37288"/><bf gi="11072" ue="37302" us="37290"/><bf gi="11085" ue="37304" us="37304"/><bf gi="11086" ue="37309" us="37306"/><bf gi="11090" ue="37315" us="37311"/><bf gi="11095" ue="37321" us="37317"/><bf gi="11100" ue="37329" us="37323"/><bf gi="11107" ue="37332" us="37331"/><bf gi="11109" ue="37343" us="37334"/><bf gi="11119" ue="37345" us="37345"/><bf gi="11120" ue="37351" us="37347"/><bf gi="11125" ue="37354" us="37353"/><bf gi="11127" ue="373!
61" us="37356"/><bf gi="11133" ue="37367" us="37365"/><bf gi="11136" ue="37369" us="37369"/><bf gi="11137" ue="37373" us="37371"/><bf gi="11140" ue="37377" us="37375"/><bf gi="11143" ue="37383" us="37380"/><bf gi="11147" ue="37386" us="37385"/><bf gi="11149" ue="37390" us="37388"/><bf gi="11152" ue="37398" us="37392"/><bf gi="11159" ue="37400" us="37400"/><bf gi="11160" ue="37406" us="37404"/><bf gi="11163" ue="37414" us="37411"/><bf gi="11167" ue="37417" us="37416"/><bf gi="11169" ue="37420" us="37420"/><bf gi="11170" ue="37424" us="37422"/><bf gi="11173" ue="37434" us="37427"/><bf gi="11181" ue="37436" us="37436"/><bf gi="11182" ue="37440" us="37438"/><bf gi="11185" ue="37451" us="37442"/><bf gi="11195" ue="37457" us="37453"/><bf gi="11200" ue="37470" us="37463"/><bf gi="11208" ue="37474" us="37472"/><bf gi="11211" ue="37481" us="37476"/><bf gi="11217" ue="37489" us="37486"/><bf gi="11221" ue="37497" us="37493"/><bf gi="11226" ue="37504" us="37499"/><bf gi="11232" ue="375!
07" us="37507"/><bf gi="11233" ue="37509" us="37509"/><bf gi="11234" u
e="37514" us="37512"/><bf gi="11237" ue="37518" us="37517"/><bf gi="11239" ue="37523" us="37521"/><bf gi="11242" ue="37532" us="37525"/><bf gi="11250" ue="37536" us="37535"/><bf gi="11252" ue="37541" us="37540"/><bf gi="11254" ue="37544" us="37543"/><bf gi="11256" ue="37547" us="37547"/><bf gi="11257" ue="37549" us="37549"/><bf gi="11258" ue="37551" us="37551"/><bf gi="11259" ue="37554" us="37554"/><bf gi="11260" ue="37565" us="37558"/><bf gi="11268" ue="37571" us="37567"/><bf gi="11273" ue="37576" us="37573"/><bf gi="11277" ue="37584" us="37579"/><bf gi="11283" ue="37587" us="37586"/><bf gi="11285" ue="37589" us="37589"/><bf gi="11286" ue="37593" us="37591"/><bf gi="11289" ue="37597" us="37596"/><bf gi="11291" ue="37601" us="37599"/><bf gi="11294" ue="37605" us="37603"/><bf gi="11297" ue="37610" us="37607"/><bf gi="11301" ue="37614" us="37612"/><bf gi="11304" ue="37616" us="37616"/><bf gi="11305" ue="37619" us="37618"/><bf gi="11307" ue="37628" us="37624"/><bf gi="11312" ue!
="37632" us="37631"/><bf gi="11314" ue="37634" us="37634"/><bf gi="11315" ue="37638" us="37638"/><bf gi="11316" ue="37640" us="37640"/><bf gi="11317" ue="37645" us="37645"/><bf gi="11318" ue="37649" us="37647"/><bf gi="11321" ue="37653" us="37652"/><bf gi="11323" ue="37658" us="37656"/><bf gi="11326" ue="37676" us="37660"/><bf gi="11343" ue="37679" us="37678"/><bf gi="11345" ue="37687" us="37682"/><bf gi="11351" ue="37691" us="37690"/><bf gi="11353" ue="37700" us="37700"/><bf gi="11354" ue="37705" us="37703"/><bf gi="11357" ue="37707" us="37707"/><bf gi="11358" ue="37709" us="37709"/><bf gi="11359" ue="37714" us="37712"/><bf gi="11362" ue="37720" us="37716"/><bf gi="11367" ue="37724" us="37722"/><bf gi="11370" ue="37726" us="37726"/><bf gi="11371" ue="37728" us="37728"/><bf gi="11372" ue="37733" us="37732"/><bf gi="11374" ue="37735" us="37735"/><bf gi="11375" ue="37738" us="37737"/><bf gi="11377" ue="37745" us="37740"/><bf gi="11383" ue="37750" us="37747"/><bf gi="11387" ue!
="37754" us="37754"/><bf gi="11388" ue="37762" us="37756"/><bf gi="113
95" ue="37768" us="37768"/><bf gi="11396" ue="37773" us="37770"/><bf gi="11400" ue="37775" us="37775"/><bf gi="11401" ue="37778" us="37778"/><bf gi="11402" ue="37784" us="37780"/><bf gi="11407" ue="37787" us="37786"/><bf gi="11409" ue="37790" us="37790"/><bf gi="11410" ue="37793" us="37793"/><bf gi="11411" ue="37796" us="37795"/><bf gi="11413" ue="37801" us="37798"/><bf gi="11417" ue="37806" us="37803"/><bf gi="11421" ue="37808" us="37808"/><bf gi="11422" ue="37814" us="37812"/><bf gi="11425" ue="37818" us="37817"/><bf gi="11427" ue="37825" us="37825"/><bf gi="11428" ue="37837" us="37827"/><bf gi="11439" ue="37841" us="37840"/><bf gi="11441" ue="37843" us="37843"/><bf gi="11442" ue="37849" us="37846"/><bf gi="11446" ue="37855" us="37852"/><bf gi="11450" ue="37858" us="37857"/><bf gi="11452" ue="37864" us="37860"/><bf gi="11457" ue="37883" us="37879"/><bf gi="11462" ue="37885" us="37885"/><bf gi="11463" ue="37892" us="37889"/><bf gi="11467" ue="37897" us="37895"/><bf gi="1147!
0" ue="37904" us="37901"/><bf gi="11474" ue="37914" us="37907"/><bf gi="11482" ue="37919" us="37919"/><bf gi="11483" ue="37921" us="37921"/><bf gi="11484" ue="37931" us="37931"/><bf gi="11485" ue="37935" us="37934"/><bf gi="11487" ue="37942" us="37937"/><bf gi="11493" ue="37944" us="37944"/><bf gi="11494" ue="37947" us="37946"/><bf gi="11496" ue="37949" us="37949"/><bf gi="11497" ue="37951" us="37951"/><bf gi="11498" ue="37953" us="37953"/><bf gi="11499" ue="37957" us="37955"/><bf gi="11502" ue="37960" us="37960"/><bf gi="11503" ue="37962" us="37962"/><bf gi="11504" ue="37964" us="37964"/><bf gi="11505" ue="37971" us="37969"/><bf gi="11508" ue="37973" us="37973"/><bf gi="11509" ue="37980" us="37977"/><bf gi="11513" ue="37987" us="37982"/><bf gi="11519" ue="37992" us="37992"/><bf gi="11520" ue="37995" us="37994"/><bf gi="11522" ue="38002" us="37997"/><bf gi="11528" ue="38005" us="38005"/><bf gi="11529" ue="38007" us="38007"/><bf gi="11530" ue="38015" us="38012"/><bf gi="1153!
4" ue="38017" us="38017"/><bf gi="11535" ue="38020" us="38019"/><bf gi
="11537" ue="38265" us="38263"/><bf gi="11540" ue="38270" us="38270"/><bf gi="11541" ue="38272" us="38272"/><bf gi="11542" ue="38276" us="38274"/><bf gi="11545" ue="38287" us="38279"/><bf gi="11554" ue="38292" us="38289"/><bf gi="11558" ue="38294" us="38294"/><bf gi="11559" ue="38297" us="38296"/><bf gi="11561" ue="38313" us="38301"/><bf gi="11574" ue="38317" us="38315"/><bf gi="11577" ue="38322" us="38322"/><bf gi="11578" ue="38324" us="38324"/><bf gi="11579" ue="38326" us="38326"/><bf gi="11580" ue="38335" us="38329"/><bf gi="11587" ue="38339" us="38339"/><bf gi="11588" ue="38349" us="38342"/><bf gi="11596" ue="38358" us="38352"/><bf gi="11603" ue="38362" us="38360"/><bf gi="11606" ue="38370" us="38364"/><bf gi="11613" ue="38374" us="38372"/><bf gi="11616" ue="38430" us="38428"/><bf gi="11619" ue="38434" us="38433"/><bf gi="11621" ue="38438" us="38436"/><bf gi="11624" ue="38440" us="38440"/><bf gi="11625" ue="38442" us="38442"/><bf gi="11626" ue="38444" us="38444"/><bf gi=!
"11627" ue="38447" us="38446"/><bf gi="11629" ue="38451" us="38449"/><bf gi="11632" ue="38461" us="38455"/><bf gi="11639" ue="38466" us="38463"/><bf gi="11643" ue="38468" us="38468"/><bf gi="11644" ue="38477" us="38475"/><bf gi="11647" ue="38480" us="38479"/><bf gi="11649" ue="38482" us="38482"/><bf gi="11650" ue="38484" us="38484"/><bf gi="11651" ue="38488" us="38486"/><bf gi="11654" ue="38495" us="38491"/><bf gi="11659" ue="38502" us="38497"/><bf gi="11665" ue="38506" us="38506"/><bf gi="11666" ue="38508" us="38508"/><bf gi="11667" ue="38510" us="38510"/><bf gi="11668" ue="38512" us="38512"/><bf gi="11669" ue="38520" us="38514"/><bf gi="11676" ue="38527" us="38522"/><bf gi="11682" ue="38534" us="38529"/><bf gi="11688" ue="38539" us="38536"/><bf gi="11692" ue="38543" us="38541"/><bf gi="11695" ue="38545" us="38545"/><bf gi="11696" ue="38557" us="38548"/><bf gi="11706" ue="38560" us="38559"/><bf gi="11708" ue="38570" us="38563"/><bf gi="11716" ue="38580" us="38574"/><bf gi=!
"11723" ue="38588" us="38582"/><bf gi="11730" ue="38593" us="38592"/><
bf gi="11732" ue="38599" us="38596"/><bf gi="11736" ue="38606" us="38601"/><bf gi="11742" ue="38610" us="38609"/><bf gi="11744" ue="38614" us="38613"/><bf gi="11746" ue="38623" us="38616"/><bf gi="11754" ue="38627" us="38626"/><bf gi="11756" ue="38635" us="38632"/><bf gi="11760" ue="38642" us="38639"/><bf gi="11764" ue="38647" us="38646"/><bf gi="11766" ue="38651" us="38649"/><bf gi="11769" ue="38656" us="38656"/><bf gi="11770" ue="38666" us="38658"/><bf gi="11779" ue="38671" us="38669"/><bf gi="11782" ue="38673" us="38673"/><bf gi="11783" ue="38675" us="38675"/><bf gi="11784" ue="38678" us="38678"/><bf gi="11785" ue="38686" us="38681"/><bf gi="11791" ue="38692" us="38689"/><bf gi="11795" ue="38696" us="38695"/><bf gi="11797" ue="38698" us="38698"/><bf gi="11798" ue="38707" us="38704"/><bf gi="11802" ue="38713" us="38712"/><bf gi="11804" ue="38715" us="38715"/><bf gi="11805" ue="38718" us="38717"/><bf gi="11807" ue="38724" us="38721"/><bf gi="11811" ue="38726" us="38726"/><b!
f gi="11812" ue="38730" us="38728"/><bf gi="11815" ue="38735" us="38733"/><bf gi="11818" ue="38738" us="38737"/><bf gi="11820" ue="38748" us="38741"/><bf gi="11828" ue="38750" us="38750"/><bf gi="11829" ue="38756" us="38752"/><bf gi="11834" ue="38763" us="38758"/><bf gi="11840" ue="38766" us="38765"/><bf gi="11842" ue="38769" us="38769"/><bf gi="11843" ue="38772" us="38771"/><bf gi="11845" ue="38781" us="38774"/><bf gi="11853" ue="38785" us="38783"/><bf gi="11856" ue="38790" us="38788"/><bf gi="11859" ue="38793" us="38793"/><bf gi="11860" ue="38795" us="38795"/><bf gi="11861" ue="38797" us="38797"/><bf gi="11862" ue="38800" us="38799"/><bf gi="11864" ue="38810" us="38805"/><bf gi="11870" ue="38812" us="38812"/><bf gi="11871" ue="38816" us="38814"/><bf gi="11874" ue="38819" us="38818"/><bf gi="11876" ue="38822" us="38822"/><bf gi="11877" ue="38824" us="38824"/><bf gi="11878" ue="38830" us="38827"/><bf gi="11882" ue="38838" us="38833"/><bf gi="11888" ue="38842" us="38840"/><b!
f gi="11891" ue="38844" us="38844"/><bf gi="11892" ue="38847" us="3884
6"/><bf gi="11894" ue="38849" us="38849"/><bf gi="11895" ue="38862" us="38851"/><bf gi="11907" ue="38865" us="38864"/><bf gi="11909" ue="38868" us="38867"/><bf gi="11911" ue="38873" us="38871"/><bf gi="11914" ue="38878" us="38875"/><bf gi="11918" ue="38881" us="38880"/><bf gi="11920" ue="38884" us="38884"/><bf gi="11921" ue="38895" us="38893"/><bf gi="11924" ue="38904" us="38897"/><bf gi="11932" ue="38907" us="38906"/><bf gi="11934" ue="38911" us="38911"/><bf gi="11935" ue="38915" us="38913"/><bf gi="11938" ue="38920" us="38917"/><bf gi="11942" ue="38922" us="38922"/><bf gi="11943" ue="38932" us="38924"/><bf gi="11952" ue="38938" us="38934"/><bf gi="11957" ue="38940" us="38940"/><bf gi="11958" ue="38942" us="38942"/><bf gi="11959" ue="38945" us="38944"/><bf gi="11961" ue="38950" us="38947"/><bf gi="11965" ue="38960" us="38955"/><bf gi="11971" ue="38965" us="38962"/><bf gi="11975" ue="38968" us="38967"/><bf gi="11977" ue="38974" us="38971"/><bf gi="11981" ue="38980" us="38980!
"/><bf gi="11982" ue="38983" us="38982"/><bf gi="11984" ue="38991" us="38986"/><bf gi="11990" ue="39003" us="38993"/><bf gi="12001" ue="39006" us="39006"/><bf gi="12002" ue="39011" us="39010"/><bf gi="12004" ue="39015" us="39013"/><bf gi="12007" ue="39020" us="39018"/><bf gi="12010" ue="39025" us="39023"/><bf gi="12013" ue="39028" us="39027"/><bf gi="12015" ue="39080" us="39080"/><bf gi="12016" ue="39083" us="39082"/><bf gi="12018" ue="39089" us="39085"/><bf gi="12023" ue="39092" us="39092"/><bf gi="12024" ue="39096" us="39094"/><bf gi="12027" ue="39099" us="39098"/><bf gi="12029" ue="39103" us="39103"/><bf gi="12030" ue="39110" us="39106"/><bf gi="12035" ue="39112" us="39112"/><bf gi="12036" ue="39116" us="39116"/><bf gi="12037" ue="39132" us="39131"/><bf gi="12039" ue="39135" us="39135"/><bf gi="12040" ue="39139" us="39137"/><bf gi="12043" ue="39143" us="39141"/><bf gi="12046" ue="39147" us="39145"/><bf gi="12049" ue="39151" us="39149"/><bf gi="12052" ue="39156" us="39154!
"/><bf gi="12055" ue="39158" us="39158"/><bf gi="12056" ue="39166" us=
"39164"/><bf gi="12059" ue="39171" us="39170"/><bf gi="12061" ue="39173" us="39173"/><bf gi="12062" ue="39178" us="39175"/><bf gi="12066" ue="39180" us="39180"/><bf gi="12067" ue="39192" us="39184"/><bf gi="12076" ue="39202" us="39194"/><bf gi="12085" ue="39204" us="39204"/><bf gi="12086" ue="39208" us="39206"/><bf gi="12089" ue="39212" us="39211"/><bf gi="12091" ue="39214" us="39214"/><bf gi="12092" ue="39221" us="39217"/><bf gi="12097" ue="39230" us="39225"/><bf gi="12103" ue="39234" us="39232"/><bf gi="12106" ue="39241" us="39237"/><bf gi="12111" ue="39246" us="39243"/><bf gi="12115" ue="39250" us="39248"/><bf gi="12118" ue="39253" us="39252"/><bf gi="12120" ue="39257" us="39255"/><bf gi="12123" ue="39260" us="39259"/><bf gi="12125" ue="39264" us="39262"/><bf gi="12128" ue="39321" us="39318"/><bf gi="12132" ue="39323" us="39323"/><bf gi="12133" ue="39327" us="39325"/><bf gi="12136" ue="39334" us="39333"/><bf gi="12138" ue="39336" us="39336"/><bf gi="12139" ue="39342" us="!
39340"/><bf gi="12142" ue="39349" us="39344"/><bf gi="12148" ue="39354" us="39353"/><bf gi="12150" ue="39357" us="39356"/><bf gi="12152" ue="39359" us="39359"/><bf gi="12153" ue="39361" us="39361"/><bf gi="12154" ue="39366" us="39363"/><bf gi="12158" ue="39369" us="39368"/><bf gi="12160" ue="39381" us="39376"/><bf gi="12166" ue="39391" us="39384"/><bf gi="12174" ue="39394" us="39394"/><bf gi="12175" ue="39399" us="39399"/><bf gi="12176" ue="39406" us="39402"/><bf gi="12181" ue="39410" us="39408"/><bf gi="12184" ue="39413" us="39412"/><bf gi="12186" ue="39417" us="39416"/><bf gi="12188" ue="39419" us="39419"/><bf gi="12189" ue="39423" us="39421"/><bf gi="12192" ue="39429" us="39425"/><bf gi="12197" ue="39436" us="39435"/><bf gi="12199" ue="39443" us="39438"/><bf gi="12205" ue="39446" us="39446"/><bf gi="12206" ue="39449" us="39449"/><bf gi="12207" ue="39454" us="39454"/><bf gi="12208" ue="39456" us="39456"/><bf gi="12209" ue="39460" us="39458"/><bf gi="12212" ue="39464" us="!
39463"/><bf gi="12214" ue="39467" us="39467"/><bf gi="12215" ue="39470
" us="39469"/><bf gi="12217" ue="39472" us="39472"/><bf gi="12218" ue="39475" us="39475"/><bf gi="12219" ue="39480" us="39477"/><bf gi="12223" ue="39486" us="39486"/><bf gi="12224" ue="39493" us="39488"/><bf gi="12230" ue="39495" us="39495"/><bf gi="12231" ue="39502" us="39498"/><bf gi="12236" ue="39505" us="39505"/><bf gi="12237" ue="39511" us="39508"/><bf gi="12241" ue="39515" us="39514"/><bf gi="12243" ue="39517" us="39517"/><bf gi="12244" ue="39519" us="39519"/><bf gi="12245" ue="39522" us="39522"/><bf gi="12246" ue="39525" us="39524"/><bf gi="12248" ue="39531" us="39529"/><bf gi="12251" ue="39592" us="39592"/><bf gi="12252" ue="39594" us="39594"/><bf gi="12253" ue="39600" us="39596"/><bf gi="12258" ue="39602" us="39602"/><bf gi="12259" ue="39606" us="39604"/><bf gi="12262" ue="39609" us="39608"/><bf gi="12264" ue="39612" us="39611"/><bf gi="12266" ue="39617" us="39614"/><bf gi="12270" ue="39620" us="39619"/><bf gi="12272" ue="39622" us="39622"/><bf gi="12273" ue="39624"!
us="39624"/><bf gi="12274" ue="39641" us="39630"/><bf gi="12286" ue="39644" us="39643"/><bf gi="12288" ue="39648" us="39646"/><bf gi="12291" ue="39655" us="39650"/><bf gi="12297" ue="39663" us="39657"/><bf gi="12304" ue="39669" us="39665"/><bf gi="12309" ue="39671" us="39671"/><bf gi="12310" ue="39675" us="39673"/><bf gi="12313" ue="39677" us="39677"/><bf gi="12314" ue="39686" us="39679"/><bf gi="12322" ue="39689" us="39688"/><bf gi="12324" ue="39694" us="39691"/><bf gi="12328" ue="39696" us="39696"/><bf gi="12329" ue="39698" us="39698"/><bf gi="12330" ue="39702" us="39702"/><bf gi="12331" ue="39708" us="39704"/><bf gi="12336" ue="39712" us="39711"/><bf gi="12338" ue="39715" us="39714"/><bf gi="12340" ue="39723" us="39717"/><bf gi="12347" ue="39727" us="39725"/><bf gi="12350" ue="39733" us="39729"/><bf gi="12355" ue="39735" us="39735"/><bf gi="12356" ue="39741" us="39737"/><bf gi="12361" ue="39749" us="39745"/><bf gi="12366" ue="39752" us="39752"/><bf gi="12367" ue="39759"!
us="39755"/><bf gi="12372" ue="39761" us="39761"/><bf gi="12373" ue="
39768" us="39764"/><bf gi="12378" ue="39771" us="39770"/><bf gi="12380" ue="39774" us="39774"/><bf gi="12381" ue="39777" us="39777"/><bf gi="12382" ue="39779" us="39779"/><bf gi="12383" ue="39782" us="39781"/><bf gi="12385" ue="39784" us="39784"/><bf gi="12386" ue="39791" us="39786"/><bf gi="12392" ue="39797" us="39794"/><bf gi="12396" ue="39801" us="39799"/><bf gi="12399" ue="39808" us="39807"/><bf gi="12401" ue="39815" us="39811"/><bf gi="12406" ue="39819" us="39817"/><bf gi="12409" ue="39828" us="39821"/><bf gi="12417" ue="39831" us="39830"/><bf gi="12419" ue="39834" us="39834"/><bf gi="12420" ue="39840" us="39837"/><bf gi="12424" ue="39854" us="39846"/><bf gi="12433" ue="39858" us="39856"/><bf gi="12436" ue="39860" us="39860"/><bf gi="12437" ue="39865" us="39863"/><bf gi="12440" ue="39868" us="39867"/><bf gi="12442" ue="39873" us="39870"/><bf gi="12446" ue="39882" us="39878"/><bf gi="12451" ue="39890" us="39886"/><bf gi="12456" ue="39892" us="39892"/><bf gi="12457" ue="3!
9896" us="39894"/><bf gi="12460" ue="39899" us="39899"/><bf gi="12461" ue="39901" us="39901"/><bf gi="12462" ue="39903" us="39903"/><bf gi="12463" ue="39909" us="39905"/><bf gi="12468" ue="39912" us="39911"/><bf gi="12470" ue="39915" us="39914"/><bf gi="12472" ue="39923" us="39919"/><bf gi="12477" ue="39925" us="39925"/><bf gi="12478" ue="39930" us="39927"/><bf gi="12482" ue="39933" us="39933"/><bf gi="12483" ue="39936" us="39935"/><bf gi="12485" ue="39938" us="39938"/><bf gi="12486" ue="39940" us="39940"/><bf gi="12487" ue="39942" us="39942"/><bf gi="12488" ue="39949" us="39944"/><bf gi="12494" ue="39958" us="39951"/><bf gi="12502" ue="39964" us="39960"/><bf gi="12507" ue="39966" us="39966"/><bf gi="12508" ue="39978" us="39969"/><bf gi="12518" ue="39986" us="39981"/><bf gi="12524" ue="39991" us="39989"/><bf gi="12527" ue="39995" us="39993"/><bf gi="12530" ue="39998" us="39997"/><bf gi="12532" ue="40001" us="40001"/><bf gi="12533" ue="40010" us="40003"/><bf gi="12541" ue="4!
0016" us="40014"/><bf gi="12544" ue="40020" us="40018"/><bf gi="12547"
ue="40024" us="40022"/><bf gi="12550" ue="40032" us="40026"/><bf gi="12557" ue="40035" us="40035"/><bf gi="12558" ue="40043" us="40039"/><bf gi="12563" ue="40046" us="40046"/><bf gi="12564" ue="40048" us="40048"/><bf gi="12565" ue="40050" us="40050"/><bf gi="12566" ue="40056" us="40053"/><bf gi="12570" ue="40059" us="40059"/><bf gi="12571" ue="40167" us="40165"/><bf gi="12574" ue="40169" us="40169"/><bf gi="12575" ue="40172" us="40171"/><bf gi="12577" ue="40176" us="40176"/><bf gi="12578" ue="40180" us="40178"/><bf gi="12581" ue="40183" us="40182"/><bf gi="12583" ue="40185" us="40185"/><bf gi="12584" ue="40195" us="40194"/><bf gi="12586" ue="40201" us="40198"/><bf gi="12590" ue="40203" us="40203"/><bf gi="12591" ue="40206" us="40206"/><bf gi="12592" ue="40210" us="40209"/><bf gi="12594" ue="40213" us="40213"/><bf gi="12595" ue="40216" us="40215"/><bf gi="12597" ue="40223" us="40219"/><bf gi="12602" ue="40227" us="40227"/><bf gi="12603" ue="40230" us="40230"/><bf gi="12604" !
ue="40232" us="40232"/><bf gi="12605" ue="40236" us="40234"/><bf gi="12608" ue="40240" us="40239"/><bf gi="12610" ue="40244" us="40242"/><bf gi="12613" ue="40255" us="40250"/><bf gi="12619" ue="40264" us="40257"/><bf gi="12627" ue="40266" us="40266"/><bf gi="12628" ue="40273" us="40272"/><bf gi="12630" ue="40276" us="40275"/><bf gi="12632" ue="40281" us="40281"/><bf gi="12633" ue="40293" us="40284"/><bf gi="12643" ue="40300" us="40297"/><bf gi="12647" ue="40304" us="40303"/><bf gi="12649" ue="40306" us="40306"/><bf gi="12650" ue="40311" us="40310"/><bf gi="12652" ue="40316" us="40314"/><bf gi="12655" ue="40318" us="40318"/><bf gi="12656" ue="40324" us="40323"/><bf gi="12658" ue="40327" us="40326"/><bf gi="12660" ue="40330" us="40329"/><bf gi="12662" ue="40335" us="40333"/><bf gi="12665" ue="40339" us="40338"/><bf gi="12667" ue="40344" us="40341"/><bf gi="12671" ue="40346" us="40346"/><bf gi="12672" ue="40353" us="40353"/><bf gi="12673" ue="40356" us="40356"/><bf gi="12674" !
ue="40364" us="40361"/><bf gi="12678" ue="40367" us="40366"/><bf gi="1
2680" ue="40370" us="40369"/><bf gi="12682" ue="40373" us="40372"/><bf gi="12684" ue="40380" us="40376"/><bf gi="12689" ue="40383" us="40383"/><bf gi="12690" ue="40388" us="40385"/><bf gi="12694" ue="40391" us="40390"/><bf gi="12696" ue="40394" us="40393"/><bf gi="12698" ue="40399" us="40399"/><bf gi="12699" ue="40407" us="40403"/><bf gi="12704" ue="40410" us="40409"/><bf gi="12706" ue="40416" us="40414"/><bf gi="12709" ue="40423" us="40421"/><bf gi="12712" ue="40425" us="40425"/><bf gi="12713" ue="40427" us="40427"/><bf gi="12714" ue="40432" us="40429"/><bf gi="12718" ue="40436" us="40434"/><bf gi="12721" ue="40442" us="40440"/><bf gi="12724" ue="40446" us="40445"/><bf gi="12726" ue="40450" us="40450"/><bf gi="12727" ue="40455" us="40455"/><bf gi="12728" ue="40458" us="40458"/><bf gi="12729" ue="40462" us="40462"/><bf gi="12730" ue="40466" us="40464"/><bf gi="12733" ue="40470" us="40469"/><bf gi="12735" ue="40478" us="40473"/><bf gi="12741" ue="40565" us="40565"/><bf gi="12!
742" ue="40573" us="40568"/><bf gi="12748" ue="40581" us="40575"/><bf gi="12755" ue="40584" us="40583"/><bf gi="12757" ue="40588" us="40587"/><bf gi="12759" ue="40591" us="40590"/><bf gi="12761" ue="40595" us="40593"/><bf gi="12764" ue="40600" us="40597"/><bf gi="12768" ue="40603" us="40603"/><bf gi="12769" ue="40607" us="40605"/><bf gi="12772" ue="40614" us="40612"/><bf gi="12775" ue="40618" us="40616"/><bf gi="12778" ue="40624" us="40620"/><bf gi="12783" ue="40629" us="40627"/><bf gi="12786" ue="40636" us="40632"/><bf gi="12791" ue="40639" us="40638"/><bf gi="12793" ue="40644" us="40644"/><bf gi="12794" ue="40646" us="40646"/><bf gi="12795" ue="40648" us="40648"/><bf gi="12796" ue="40658" us="40651"/><bf gi="12804" ue="40661" us="40660"/><bf gi="12806" ue="40665" us="40664"/><bf gi="12808" ue="40672" us="40667"/><bf gi="12814" ue="40677" us="40676"/><bf gi="12816" ue="40680" us="40679"/><bf gi="12818" ue="40690" us="40684"/><bf gi="12825" ue="40697" us="40692"/><bf gi="12!
831" ue="40701" us="40699"/><bf gi="12834" ue="40703" us="40703"/><bf
gi="12835" ue="40707" us="40706"/><bf gi="12837" ue="40713" us="40711"/><bf gi="12840" ue="40727" us="40718"/><bf gi="12850" ue="40731" us="40729"/><bf gi="12853" ue="40738" us="40735"/><bf gi="12857" ue="40742" us="40742"/><bf gi="12858" ue="40748" us="40746"/><bf gi="12861" ue="40751" us="40751"/><bf gi="12862" ue="40754" us="40753"/><bf gi="12864" ue="40756" us="40756"/><bf gi="12865" ue="40759" us="40759"/><bf gi="12866" ue="40767" us="40761"/><bf gi="12873" ue="40769" us="40769"/><bf gi="12874" ue="40775" us="40771"/><bf gi="12879" ue="40779" us="40778"/><bf gi="12881" ue="40783" us="40782"/><bf gi="12883" ue="40792" us="40786"/><bf gi="12890" ue="40794" us="40794"/><bf gi="12891" ue="40803" us="40797"/><bf gi="12898" ue="40810" us="40806"/><bf gi="12903" ue="40819" us="40812"/><bf gi="12911" ue="40823" us="40821"/><bf gi="12914" ue="40826" us="40826"/><bf gi="12915" ue="40829" us="40829"/><bf gi="12916" ue="40845" us="40845"/><bf gi="12917" ue="40850" us="40847"/><bf g!
i="12921" ue="40855" us="40852"/><bf gi="12925" ue="40862" us="40860"/><bf gi="12928" ue="40867" us="40864"/><bf gi="12932" ue="40869" us="40869"/><bf gi="12933" ue="63785" us="63785"/><bf gi="12934" ue="63964" us="63964"/><bf gi="12935" ue="64045" us="64014"/><bf gi="12967" ue="65374" us="65281"/><bf gi="13061" ue="65439" us="65377"/><bf gi="13124" ue="65507" us="65504"/><bf gi="13128" ue="65509" us="65509"/><bf gi="0" ue="65535" us="65535"/></bfranges><cid-widths start-index="0"><wx w="1000"/><wx w="0"/><wx w="333"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="50!
0"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><
wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="446"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="490"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="50!
0"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="875"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="812"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w=!
"500"/><wx w="500"/><wx w="500"/><wx w="875"/><wx w="875"/><wx w="500"
/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="915"/><wx w="1000"/><wx w="1000"/><wx w="915"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="765"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="915"/><wx w="1000"/><wx w="1000"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="915"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="915"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="915"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="976"/><wx w="976"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="915"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="976"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="976"/><wx w="976"/><wx w="976"/><wx w="976"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><!
wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="1000"/><wx w="915"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx !
w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="50
0"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/></cid-widths></multibyte-extras></fo!
nt-metrics>
\ No newline at end of file
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/no-idea-what-to-do-with-these-yet/fop/sazanami-mincho.ttf
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/ja-JP/src/no-idea-what-to-do-with-these-yet/fop/sazanami-mincho.ttf
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/no-idea-what-to-do-with-these-yet/fop/sazanami-mincho.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ja-JP/src/no-idea-what-to-do-with-these-yet/fop/sazanami-mincho.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ja-JP/src/no-idea-what-to-do-with-these-yet/fop/sazanami-mincho.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="utf-8"?><font-metrics type="TYPE0"><font-name>SazanamiMincho,Mincho-Regular</font-name><embed/><cap-height>0</cap-height><x-height>0</x-height><ascender>973</ascender><descender>-156</descender><bbox><left>-50</left><bottom>-156</bottom><right>1250</right><top>973</top></bbox><flags>33</flags><stemv>0</stemv><italicangle>0</italicangle><ttc-name>Sazanami Mincho</ttc-name><subtype>TYPE0</subtype><multibyte-extras><cid-type>CIDFontType2</cid-type><default-width>0</default-width><bfranges><bf gi="0" ue="0" us="0"/><bf gi="3" ue="126" us="32"/><bf gi="98" ue="383" us="160"/><bf gi="322" ue="402" us="402"/><bf gi="323" ue="476" us="461"/><bf gi="339" ue="501" us="501"/><bf gi="340" ue="511" us="506"/><bf gi="346" ue="680" us="592"/><bf gi="435" ue="711" us="710"/><bf gi="437" ue="715" us="713"/><bf gi="440" ue="733" us="728"/><bf gi="446" ue="885" us="884"/><bf gi="448" ue="890" us="890"/><bf gi="449" ue="894" us="894"/><bf gi="450" ue="906" us="900!
"/><bf gi="457" ue="908" us="908"/><bf gi="458" ue="929" us="910"/><bf gi="478" ue="974" us="931"/><bf gi="522" ue="977" us="976"/><bf gi="524" ue="981" us="981"/><bf gi="525" ue="1013" us="1013"/><bf gi="526" ue="1036" us="1025"/><bf gi="538" ue="1103" us="1038"/><bf gi="604" ue="1116" us="1105"/><bf gi="616" ue="1158" us="1118"/><bf gi="657" ue="1220" us="1168"/><bf gi="710" ue="1224" us="1223"/><bf gi="712" ue="1228" us="1227"/><bf gi="714" ue="1259" us="1232"/><bf gi="742" ue="1269" us="1262"/><bf gi="750" ue="1273" us="1272"/><bf gi="752" ue="7813" us="7808"/><bf gi="758" ue="7923" us="7922"/><bf gi="760" ue="8238" us="8192"/><bf gi="807" ue="8262" us="8240"/><bf gi="830" ue="8304" us="8304"/><bf gi="831" ue="8334" us="8308"/><bf gi="858" ue="8362" us="8352"/><bf gi="869" ue="8364" us="8364"/><bf gi="870" ue="8504" us="8448"/><bf gi="927" ue="8578" us="8531"/><bf gi="975" ue="8682" us="8592"/><bf gi="1066" ue="8945" us="8704"/><bf gi="1308" ue="8962" us="8962"/><bf gi=!
"1309" ue="8967" us="8967"/><bf gi="1310" ue="8976" us="8976"/><bf gi=
"1311" ue="8978" us="8978"/><bf gi="1312" ue="8993" us="8992"/><bf gi="1314" ue="9450" us="9312"/><bf gi="1453" ue="9621" us="9472"/><bf gi="1603" ue="9711" us="9632"/><bf gi="1683" ue="9747" us="9728"/><bf gi="1703" ue="9839" us="9754"/><bf gi="1789" ue="9988" us="9985"/><bf gi="1793" ue="9993" us="9990"/><bf gi="1797" ue="10023" us="9996"/><bf gi="1825" ue="10059" us="10025"/><bf gi="1860" ue="10061" us="10061"/><bf gi="1861" ue="10066" us="10063"/><bf gi="1865" ue="10070" us="10070"/><bf gi="1866" ue="10078" us="10072"/><bf gi="1873" ue="10087" us="10081"/><bf gi="1880" ue="10132" us="10102"/><bf gi="1911" ue="10159" us="10136"/><bf gi="1935" ue="10174" us="10161"/><bf gi="1949" ue="12319" us="12288"/><bf gi="1981" ue="12336" us="12336"/><bf gi="1982" ue="12341" us="12339"/><bf gi="1985" ue="12438" us="12353"/><bf gi="2071" ue="12447" us="12443"/><bf gi="2076" ue="12542" us="12449"/><bf gi="2170" ue="12850" us="12849"/><bf gi="2172" ue="12857" us="12857"/><bf gi="2173" ue!
="12968" us="12964"/><bf gi="2178" ue="13059" us="13059"/><bf gi="2179" ue="13069" us="13069"/><bf gi="2180" ue="13076" us="13076"/><bf gi="2181" ue="13080" us="13080"/><bf gi="2182" ue="13091" us="13090"/><bf gi="2184" ue="13095" us="13094"/><bf gi="2186" ue="13099" us="13099"/><bf gi="2187" ue="13110" us="13110"/><bf gi="2188" ue="13115" us="13115"/><bf gi="2189" ue="13130" us="13129"/><bf gi="2191" ue="13133" us="13133"/><bf gi="2192" ue="13137" us="13137"/><bf gi="2193" ue="13143" us="13143"/><bf gi="2194" ue="13182" us="13179"/><bf gi="2198" ue="13199" us="13198"/><bf gi="2200" ue="13214" us="13212"/><bf gi="2203" ue="13217" us="13217"/><bf gi="2204" ue="13252" us="13252"/><bf gi="2205" ue="13259" us="13259"/><bf gi="2206" ue="13261" us="13261"/><bf gi="2207" ue="19973" us="19968"/><bf gi="2213" ue="19982" us="19975"/><bf gi="2221" ue="19986" us="19984"/><bf gi="2224" ue="19993" us="19988"/><bf gi="2230" ue="19999" us="19998"/><bf gi="2232" ue="20001" us="20001"/><bf g!
i="2233" ue="20004" us="20003"/><bf gi="2235" ue="20006" us="20006"/><
bf gi="2236" ue="20008" us="20008"/><bf gi="2237" ue="20011" us="20010"/><bf gi="2239" ue="20018" us="20013"/><bf gi="2245" ue="20022" us="20021"/><bf gi="2247" ue="20025" us="20024"/><bf gi="2249" ue="20028" us="20027"/><bf gi="2251" ue="20037" us="20031"/><bf gi="2258" ue="20039" us="20039"/><bf gi="2259" ue="20043" us="20043"/><bf gi="2260" ue="20047" us="20045"/><bf gi="2263" ue="20049" us="20049"/><bf gi="2264" ue="20058" us="20053"/><bf gi="2270" ue="20063" us="20060"/><bf gi="2274" ue="20067" us="20066"/><bf gi="2276" ue="20073" us="20072"/><bf gi="2278" ue="20081" us="20081"/><bf gi="2279" ue="20085" us="20083"/><bf gi="2282" ue="20089" us="20089"/><bf gi="2283" ue="20096" us="20094"/><bf gi="2286" ue="20098" us="20098"/><bf gi="2287" ue="20102" us="20101"/><bf gi="2289" ue="20110" us="20104"/><bf gi="2296" ue="20114" us="20113"/><bf gi="2298" ue="20121" us="20116"/><bf gi="2304" ue="20130" us="20123"/><bf gi="2312" ue="20134" us="20132"/><bf gi="2315" ue="20136" us=!
"20136"/><bf gi="2316" ue="20144" us="20139"/><bf gi="2322" ue="20147" us="20147"/><bf gi="2323" ue="20150" us="20150"/><bf gi="2324" ue="20154" us="20153"/><bf gi="2326" ue="20164" us="20160"/><bf gi="2331" ue="20167" us="20166"/><bf gi="2333" ue="20171" us="20170"/><bf gi="2335" ue="20176" us="20173"/><bf gi="2339" ue="20187" us="20180"/><bf gi="2347" ue="20197" us="20189"/><bf gi="2356" ue="20200" us="20200"/><bf gi="2357" ue="20211" us="20205"/><bf gi="2364" ue="20215" us="20213"/><bf gi="2367" ue="20227" us="20219"/><bf gi="2376" ue="20242" us="20232"/><bf gi="2387" ue="20247" us="20245"/><bf gi="2390" ue="20250" us="20249"/><bf gi="2392" ue="20253" us="20252"/><bf gi="2394" ue="20273" us="20270"/><bf gi="2398" ue="20286" us="20275"/><bf gi="2410" ue="20288" us="20288"/><bf gi="2411" ue="20291" us="20290"/><bf gi="2413" ue="20297" us="20294"/><bf gi="2417" ue="20320" us="20299"/><bf gi="2439" ue="20323" us="20323"/><bf gi="2440" ue="20330" us="20329"/><bf gi="2442" ue=!
"20332" us="20332"/><bf gi="2443" ue="20337" us="20334"/><bf gi="2447"
ue="20339" us="20339"/><bf gi="2448" ue="20351" us="20341"/><bf gi="2459" ue="20358" us="20353"/><bf gi="2465" ue="20372" us="20360"/><bf gi="2478" ue="20379" us="20374"/><bf gi="2484" ue="20385" us="20381"/><bf gi="2489" ue="20395" us="20395"/><bf gi="2490" ue="20399" us="20397"/><bf gi="2493" ue="20402" us="20402"/><bf gi="2494" ue="20407" us="20405"/><bf gi="2497" ue="20409" us="20409"/><bf gi="2498" ue="20422" us="20411"/><bf gi="2510" ue="20434" us="20424"/><bf gi="2521" ue="20436" us="20436"/><bf gi="2522" ue="20440" us="20439"/><bf gi="2524" ue="20445" us="20442"/><bf gi="2528" ue="20453" us="20447"/><bf gi="2535" ue="20464" us="20462"/><bf gi="2538" ue="20467" us="20466"/><bf gi="2540" ue="20470" us="20469"/><bf gi="2542" ue="20472" us="20472"/><bf gi="2543" ue="20474" us="20474"/><bf gi="2544" ue="20481" us="20476"/><bf gi="2550" ue="20487" us="20484"/><bf gi="2554" ue="20500" us="20489"/><bf gi="2566" ue="20511" us="20502"/><bf gi="2576" ue="20526" us="20513"/><bf!
gi="2590" ue="20528" us="20528"/><bf gi="2591" ue="20531" us="20530"/><bf gi="2593" ue="20534" us="20533"/><bf gi="2595" ue="20537" us="20537"/><bf gi="2596" ue="20539" us="20539"/><bf gi="2597" ue="20547" us="20544"/><bf gi="2601" ue="20554" us="20549"/><bf gi="2607" ue="20556" us="20556"/><bf gi="2608" ue="20563" us="20558"/><bf gi="2614" ue="20567" us="20565"/><bf gi="2617" ue="20570" us="20569"/><bf gi="2619" ue="20572" us="20572"/><bf gi="2620" ue="20576" us="20575"/><bf gi="2622" ue="20579" us="20578"/><bf gi="2624" ue="20583" us="20581"/><bf gi="2627" ue="20586" us="20586"/><bf gi="2628" ue="20589" us="20588"/><bf gi="2630" ue="20594" us="20592"/><bf gi="2633" ue="20598" us="20596"/><bf gi="2636" ue="20600" us="20600"/><bf gi="2637" ue="20605" us="20605"/><bf gi="2638" ue="20609" us="20608"/><bf gi="2640" ue="20614" us="20611"/><bf gi="2644" ue="20618" us="20618"/><bf gi="2645" ue="20628" us="20621"/><bf gi="2653" ue="20630" us="20630"/><bf gi="2654" ue="20636" us="!
20632"/><bf gi="2659" ue="20642" us="20638"/><bf gi="2664" ue="20650"
us="20650"/><bf gi="2665" ue="20653" us="20652"/><bf gi="2667" ue="20656" us="20655"/><bf gi="2669" ue="20661" us="20658"/><bf gi="2673" ue="20663" us="20663"/><bf gi="2674" ue="20666" us="20665"/><bf gi="2676" ue="20670" us="20669"/><bf gi="2678" ue="20672" us="20672"/><bf gi="2679" ue="20677" us="20674"/><bf gi="2683" ue="20679" us="20679"/><bf gi="2684" ue="20682" us="20681"/><bf gi="2686" ue="20689" us="20684"/><bf gi="2692" ue="20694" us="20691"/><bf gi="2696" ue="20696" us="20696"/><bf gi="2697" ue="20698" us="20698"/><bf gi="2698" ue="20703" us="20700"/><bf gi="2702" ue="20713" us="20706"/><bf gi="2710" ue="20719" us="20717"/><bf gi="2713" ue="20722" us="20721"/><bf gi="2715" ue="20726" us="20724"/><bf gi="2718" ue="20731" us="20729"/><bf gi="2721" ue="20734" us="20734"/><bf gi="2722" ue="20740" us="20736"/><bf gi="2727" ue="20745" us="20742"/><bf gi="2731" ue="20750" us="20747"/><bf gi="2735" ue="20752" us="20752"/><bf gi="2736" ue="20754" us="20754"/><bf gi="2737" u!
e="20767" us="20756"/><bf gi="2749" ue="20769" us="20769"/><bf gi="2750" ue="20771" us="20771"/><bf gi="2751" ue="20776" us="20775"/><bf gi="2753" ue="20778" us="20778"/><bf gi="2754" ue="20781" us="20780"/><bf gi="2756" ue="20783" us="20783"/><bf gi="2757" ue="20789" us="20785"/><bf gi="2762" ue="20796" us="20791"/><bf gi="2768" ue="20816" us="20799"/><bf gi="2786" ue="20821" us="20818"/><bf gi="2790" ue="20824" us="20823"/><bf gi="2792" ue="20826" us="20826"/><bf gi="2793" ue="20828" us="20828"/><bf gi="2794" ue="20831" us="20831"/><bf gi="2795" ue="20834" us="20834"/><bf gi="2796" ue="20838" us="20836"/><bf gi="2799" ue="20846" us="20840"/><bf gi="2806" ue="20849" us="20849"/><bf gi="2807" ue="20856" us="20853"/><bf gi="2811" ue="20860" us="20860"/><bf gi="2812" ue="20862" us="20862"/><bf gi="2813" ue="20864" us="20864"/><bf gi="2814" ue="20870" us="20866"/><bf gi="2819" ue="20883" us="20873"/><bf gi="2830" ue="20889" us="20885"/><bf gi="2835" ue="20893" us="20893"/><bf !
gi="2836" ue="20902" us="20896"/><bf gi="2843" ue="20909" us="20904"/>
<bf gi="2849" ue="20920" us="20912"/><bf gi="2858" ue="20922" us="20922"/><bf gi="2859" ue="20927" us="20924"/><bf gi="2863" ue="20930" us="20930"/><bf gi="2864" ue="20934" us="20932"/><bf gi="2867" ue="20937" us="20936"/><bf gi="2869" ue="20941" us="20939"/><bf gi="2872" ue="20943" us="20943"/><bf gi="2873" ue="20947" us="20945"/><bf gi="2876" ue="20950" us="20949"/><bf gi="2878" ue="20952" us="20952"/><bf gi="2879" ue="20958" us="20955"/><bf gi="2883" ue="20962" us="20960"/><bf gi="2886" ue="20967" us="20965"/><bf gi="2889" ue="20970" us="20969"/><bf gi="2891" ue="20974" us="20972"/><bf gi="2894" ue="20986" us="20976"/><bf gi="2905" ue="20990" us="20989"/><bf gi="2907" ue="21000" us="20992"/><bf gi="2916" ue="21003" us="21002"/><bf gi="2918" ue="21006" us="21006"/><bf gi="2919" ue="21016" us="21009"/><bf gi="2927" ue="21021" us="21021"/><bf gi="2928" ue="21026" us="21026"/><bf gi="2929" ue="21029" us="21028"/><bf gi="2931" ue="21034" us="21031"/><bf gi="2935" ue="21038" us!
="21038"/><bf gi="2936" ue="21043" us="21040"/><bf gi="2940" ue="21052" us="21045"/><bf gi="2948" ue="21061" us="21059"/><bf gi="2951" ue="21063" us="21063"/><bf gi="2952" ue="21069" us="21065"/><bf gi="2957" ue="21071" us="21071"/><bf gi="2958" ue="21080" us="21076"/><bf gi="2963" ue="21084" us="21082"/><bf gi="2966" ue="21089" us="21086"/><bf gi="2970" ue="21094" us="21091"/><bf gi="2974" ue="21098" us="21097"/><bf gi="2976" ue="21109" us="21102"/><bf gi="2984" ue="21113" us="21111"/><bf gi="2987" ue="21117" us="21117"/><bf gi="2988" ue="21120" us="21119"/><bf gi="2990" ue="21123" us="21122"/><bf gi="2992" ue="21125" us="21125"/><bf gi="2993" ue="21130" us="21127"/><bf gi="2997" ue="21133" us="21132"/><bf gi="2999" ue="21144" us="21137"/><bf gi="3007" ue="21148" us="21146"/><bf gi="3010" ue="21152" us="21151"/><bf gi="3012" ue="21159" us="21155"/><bf gi="3017" ue="21165" us="21161"/><bf gi="3022" ue="21169" us="21167"/><bf gi="3025" ue="21182" us="21172"/><bf gi="3036" ue!
="21185" us="21184"/><bf gi="3038" ue="21193" us="21187"/><bf gi="3045
" ue="21197" us="21196"/><bf gi="3047" ue="21199" us="21199"/><bf gi="3048" ue="21202" us="21201"/><bf gi="3050" ue="21209" us="21204"/><bf gi="3056" ue="21226" us="21211"/><bf gi="3072" ue="21228" us="21228"/><bf gi="3073" ue="21242" us="21232"/><bf gi="3084" ue="21251" us="21246"/><bf gi="3090" ue="21256" us="21253"/><bf gi="3094" ue="21261" us="21258"/><bf gi="3098" ue="21265" us="21263"/><bf gi="3101" ue="21267" us="21267"/><bf gi="3102" ue="21281" us="21269"/><bf gi="3115" ue="21285" us="21283"/><bf gi="3118" ue="21293" us="21287"/><bf gi="3125" ue="21299" us="21295"/><bf gi="3130" ue="21301" us="21301"/><bf gi="3131" ue="21315" us="21304"/><bf gi="3143" ue="21325" us="21317"/><bf gi="3152" ue="21332" us="21329"/><bf gi="3156" ue="21340" us="21335"/><bf gi="3162" ue="21342" us="21342"/><bf gi="3163" ue="21345" us="21344"/><bf gi="3165" ue="21347" us="21347"/><bf gi="3166" ue="21350" us="21349"/><bf gi="3168" ue="21353" us="21353"/><bf gi="3169" ue="21365" us="21356"/><b!
f gi="3179" ue="21369" us="21367"/><bf gi="3182" ue="21371" us="21371"/><bf gi="3183" ue="21375" us="21374"/><bf gi="3185" ue="21380" us="21378"/><bf gi="3188" ue="21384" us="21383"/><bf gi="3190" ue="21390" us="21390"/><bf gi="3191" ue="21396" us="21395"/><bf gi="3193" ue="21398" us="21398"/><bf gi="3194" ue="21402" us="21400"/><bf gi="3197" ue="21405" us="21405"/><bf gi="3198" ue="21409" us="21407"/><bf gi="3201" ue="21414" us="21412"/><bf gi="3204" ue="21419" us="21416"/><bf gi="3208" ue="21424" us="21421"/><bf gi="3212" ue="21432" us="21426"/><bf gi="3219" ue="21435" us="21434"/><bf gi="3221" ue="21437" us="21437"/><bf gi="3222" ue="21440" us="21440"/><bf gi="3223" ue="21443" us="21442"/><bf gi="3225" ue="21445" us="21445"/><bf gi="3226" ue="21455" us="21448"/><bf gi="3234" ue="21463" us="21458"/><bf gi="3240" ue="21467" us="21465"/><bf gi="3243" ue="21491" us="21469"/><bf gi="3266" ue="21496" us="21493"/><bf gi="3270" ue="21498" us="21498"/><bf gi="3271" ue="21508" us=!
"21505"/><bf gi="3275" ue="21521" us="21512"/><bf gi="3285" ue="21523"
us="21523"/><bf gi="3286" ue="21531" us="21530"/><bf gi="3288" ue="21533" us="21533"/><bf gi="3289" ue="21537" us="21535"/><bf gi="3292" ue="21551" us="21542"/><bf gi="3302" ue="21553" us="21553"/><bf gi="3303" ue="21558" us="21556"/><bf gi="3306" ue="21561" us="21560"/><bf gi="3308" ue="21566" us="21563"/><bf gi="3312" ue="21568" us="21568"/><bf gi="3313" ue="21572" us="21570"/><bf gi="3316" ue="21578" us="21574"/><bf gi="3321" ue="21583" us="21581"/><bf gi="3324" ue="21585" us="21585"/><bf gi="3325" ue="21599" us="21598"/><bf gi="3327" ue="21602" us="21602"/><bf gi="3328" ue="21604" us="21604"/><bf gi="3329" ue="21611" us="21606"/><bf gi="3335" ue="21614" us="21613"/><bf gi="3337" ue="21617" us="21616"/><bf gi="3339" ue="21623" us="21619"/><bf gi="3344" ue="21629" us="21627"/><bf gi="3347" ue="21633" us="21631"/><bf gi="3350" ue="21638" us="21635"/><bf gi="3354" ue="21650" us="21640"/><bf gi="3365" ue="21654" us="21653"/><bf gi="3367" ue="21660" us="21660"/><bf gi="3368" !
ue="21663" us="21663"/><bf gi="3369" ue="21666" us="21665"/><bf gi="3371" ue="21679" us="21668"/><bf gi="3383" ue="21683" us="21681"/><bf gi="3386" ue="21698" us="21687"/><bf gi="3398" ue="21700" us="21700"/><bf gi="3399" ue="21706" us="21702"/><bf gi="3404" ue="21710" us="21709"/><bf gi="3406" ue="21720" us="21720"/><bf gi="3407" ue="21730" us="21728"/><bf gi="3410" ue="21734" us="21733"/><bf gi="3412" ue="21738" us="21736"/><bf gi="3415" ue="21743" us="21740"/><bf gi="3419" ue="21746" us="21746"/><bf gi="3420" ue="21750" us="21750"/><bf gi="3421" ue="21754" us="21754"/><bf gi="3422" ue="21761" us="21756"/><bf gi="3428" ue="21769" us="21764"/><bf gi="3434" ue="21776" us="21772"/><bf gi="3439" ue="21782" us="21780"/><bf gi="3442" ue="21803" us="21802"/><bf gi="3444" ue="21807" us="21806"/><bf gi="3446" ue="21811" us="21809"/><bf gi="3449" ue="21814" us="21813"/><bf gi="3451" ue="21817" us="21816"/><bf gi="3453" ue="21822" us="21819"/><bf gi="3457" ue="21825" us="21824"/><bf!
gi="3459" ue="21831" us="21828"/><bf gi="3463" ue="21834" us="21833"/
><bf gi="3465" ue="21837" us="21836"/><bf gi="3467" ue="21841" us="21839"/><bf gi="3470" ue="21843" us="21843"/><bf gi="3471" ue="21848" us="21846"/><bf gi="3474" ue="21854" us="21850"/><bf gi="3479" ue="21857" us="21856"/><bf gi="3481" ue="21860" us="21859"/><bf gi="3483" ue="21862" us="21862"/><bf gi="3484" ue="21884" us="21883"/><bf gi="3486" ue="21892" us="21886"/><bf gi="3493" ue="21899" us="21894"/><bf gi="3499" ue="21903" us="21902"/><bf gi="3501" ue="21908" us="21905"/><bf gi="3505" ue="21914" us="21911"/><bf gi="3509" ue="21919" us="21916"/><bf gi="3513" ue="21924" us="21923"/><bf gi="3515" ue="21934" us="21927"/><bf gi="3523" ue="21936" us="21936"/><bf gi="3524" ue="21938" us="21938"/><bf gi="3525" ue="21942" us="21942"/><bf gi="3526" ue="21951" us="21951"/><bf gi="3527" ue="21953" us="21953"/><bf gi="3528" ue="21959" us="21955"/><bf gi="3533" ue="21961" us="21961"/><bf gi="3534" ue="21964" us="21963"/><bf gi="3536" ue="21966" us="21966"/><bf gi="3537" ue="21972" u!
s="21969"/><bf gi="3541" ue="21976" us="21975"/><bf gi="3543" ue="21980" us="21978"/><bf gi="3546" ue="21983" us="21982"/><bf gi="3548" ue="21988" us="21986"/><bf gi="3551" ue="21993" us="21993"/><bf gi="3552" ue="22007" us="22006"/><bf gi="3554" ue="22009" us="22009"/><bf gi="3555" ue="22015" us="22013"/><bf gi="3558" ue="22022" us="22021"/><bf gi="3560" ue="22026" us="22024"/><bf gi="3563" ue="22034" us="22029"/><bf gi="3569" ue="22036" us="22036"/><bf gi="3570" ue="22041" us="22038"/><bf gi="3574" ue="22043" us="22043"/><bf gi="3575" ue="22057" us="22057"/><bf gi="3576" ue="22060" us="22060"/><bf gi="3577" ue="22073" us="22063"/><bf gi="3588" ue="22077" us="22075"/><bf gi="3591" ue="22084" us="22079"/><bf gi="3597" ue="22086" us="22086"/><bf gi="3598" ue="22089" us="22089"/><bf gi="3599" ue="22096" us="22091"/><bf gi="3605" ue="22100" us="22100"/><bf gi="3606" ue="22107" us="22107"/><bf gi="3607" ue="22110" us="22110"/><bf gi="3608" ue="22116" us="22112"/><bf gi="3613" u!
e="22118" us="22118"/><bf gi="3614" ue="22125" us="22120"/><bf gi="362
0" ue="22127" us="22127"/><bf gi="3621" ue="22130" us="22129"/><bf gi="3623" ue="22133" us="22132"/><bf gi="3625" ue="22136" us="22136"/><bf gi="3626" ue="22138" us="22138"/><bf gi="3627" ue="22144" us="22144"/><bf gi="3628" ue="22152" us="22148"/><bf gi="3633" ue="22156" us="22154"/><bf gi="3636" ue="22159" us="22159"/><bf gi="3637" ue="22165" us="22164"/><bf gi="3639" ue="22170" us="22169"/><bf gi="3641" ue="22176" us="22173"/><bf gi="3645" ue="22178" us="22178"/><bf gi="3646" ue="22185" us="22181"/><bf gi="3651" ue="22190" us="22187"/><bf gi="3655" ue="22193" us="22193"/><bf gi="3656" ue="22196" us="22195"/><bf gi="3658" ue="22199" us="22198"/><bf gi="3660" ue="22204" us="22204"/><bf gi="3661" ue="22206" us="22206"/><bf gi="3662" ue="22211" us="22208"/><bf gi="3666" ue="22213" us="22213"/><bf gi="3667" ue="22225" us="22216"/><bf gi="3677" ue="22227" us="22227"/><bf gi="3678" ue="22241" us="22231"/><bf gi="3689" ue="22248" us="22243"/><bf gi="3695" ue="22251" us="22251"/><!
bf gi="3696" ue="22254" us="22253"/><bf gi="3698" ue="22259" us="22256"/><bf gi="3702" ue="22263" us="22262"/><bf gi="3704" ue="22266" us="22265"/><bf gi="3706" ue="22269" us="22269"/><bf gi="3707" ue="22276" us="22271"/><bf gi="3713" ue="22285" us="22279"/><bf gi="3720" ue="22287" us="22287"/><bf gi="3721" ue="22291" us="22289"/><bf gi="3724" ue="22294" us="22293"/><bf gi="3726" ue="22296" us="22296"/><bf gi="3727" ue="22301" us="22298"/><bf gi="3731" ue="22304" us="22303"/><bf gi="3733" ue="22314" us="22306"/><bf gi="3742" ue="22320" us="22316"/><bf gi="3747" ue="22324" us="22323"/><bf gi="3749" ue="22328" us="22327"/><bf gi="3751" ue="22331" us="22331"/><bf gi="3752" ue="22336" us="22333"/><bf gi="3756" ue="22338" us="22338"/><bf gi="3757" ue="22343" us="22341"/><bf gi="3760" ue="22346" us="22346"/><bf gi="3761" ue="22354" us="22348"/><bf gi="3768" ue="22361" us="22361"/><bf gi="3769" ue="22370" us="22369"/><bf gi="3771" ue="22379" us="22372"/><bf gi="3779" ue="22385" us!
="22381"/><bf gi="3784" ue="22389" us="22387"/><bf gi="3787" ue="22391
" us="22391"/><bf gi="3788" ue="22396" us="22393"/><bf gi="3792" ue="22399" us="22398"/><bf gi="3794" ue="22403" us="22401"/><bf gi="3797" ue="22409" us="22408"/><bf gi="3799" ue="22412" us="22411"/><bf gi="3801" ue="22421" us="22419"/><bf gi="3804" ue="22423" us="22423"/><bf gi="3805" ue="22426" us="22425"/><bf gi="3807" ue="22436" us="22428"/><bf gi="3816" ue="22442" us="22439"/><bf gi="3820" ue="22444" us="22444"/><bf gi="3821" ue="22448" us="22448"/><bf gi="3822" ue="22451" us="22451"/><bf gi="3823" ue="22456" us="22456"/><bf gi="3824" ue="22461" us="22461"/><bf gi="3825" ue="22464" us="22464"/><bf gi="3826" ue="22467" us="22467"/><bf gi="3827" ue="22472" us="22470"/><bf gi="3830" ue="22476" us="22475"/><bf gi="3832" ue="22479" us="22478"/><bf gi="3834" ue="22486" us="22482"/><bf gi="3839" ue="22497" us="22492"/><bf gi="3845" ue="22500" us="22499"/><bf gi="3847" ue="22503" us="22502"/><bf gi="3849" ue="22505" us="22505"/><bf gi="3850" ue="22509" us="22509"/><bf gi="3851"!
ue="22512" us="22512"/><bf gi="3852" ue="22522" us="22516"/><bf gi="3859" ue="22528" us="22524"/><bf gi="3864" ue="22534" us="22530"/><bf gi="3869" ue="22541" us="22536"/><bf gi="3875" ue="22549" us="22549"/><bf gi="3876" ue="22553" us="22553"/><bf gi="3877" ue="22555" us="22555"/><bf gi="3878" ue="22561" us="22557"/><bf gi="3883" ue="22564" us="22564"/><bf gi="3884" ue="22567" us="22566"/><bf gi="3886" ue="22570" us="22570"/><bf gi="3887" ue="22573" us="22573"/><bf gi="3888" ue="22578" us="22575"/><bf gi="3892" ue="22581" us="22580"/><bf gi="3894" ue="22586" us="22585"/><bf gi="3896" ue="22589" us="22589"/><bf gi="3897" ue="22593" us="22591"/><bf gi="3900" ue="22605" us="22601"/><bf gi="3905" ue="22610" us="22607"/><bf gi="3909" ue="22613" us="22612"/><bf gi="3911" ue="22618" us="22615"/><bf gi="3915" ue="22623" us="22622"/><bf gi="3917" ue="22626" us="22625"/><bf gi="3919" ue="22628" us="22628"/><bf gi="3920" ue="22633" us="22631"/><bf gi="3923" ue="22635" us="22635"/><b!
f gi="3924" ue="22640" us="22640"/><bf gi="3925" ue="22642" us="22642"
/><bf gi="3926" ue="22645" us="22645"/><bf gi="3927" ue="22649" us="22648"/><bf gi="3929" ue="22652" us="22652"/><bf gi="3930" ue="22657" us="22654"/><bf gi="3934" ue="22659" us="22659"/><bf gi="3935" ue="22661" us="22661"/><bf gi="3936" ue="22666" us="22663"/><bf gi="3940" ue="22669" us="22668"/><bf gi="3942" ue="22672" us="22671"/><bf gi="3944" ue="22676" us="22675"/><bf gi="3946" ue="22679" us="22678"/><bf gi="3948" ue="22690" us="22684"/><bf gi="3955" ue="22694" us="22694"/><bf gi="3956" ue="22697" us="22696"/><bf gi="3958" ue="22699" us="22699"/><bf gi="3959" ue="22702" us="22702"/><bf gi="3960" ue="22707" us="22705"/><bf gi="3963" ue="22716" us="22712"/><bf gi="3968" ue="22718" us="22718"/><bf gi="3969" ue="22722" us="22721"/><bf gi="3971" ue="22725" us="22724"/><bf gi="3973" ue="22728" us="22727"/><bf gi="3975" ue="22730" us="22730"/><bf gi="3976" ue="22734" us="22732"/><bf gi="3979" ue="22746" us="22736"/><bf gi="3990" ue="22751" us="22748"/><bf gi="3994" ue="22754" !
us="22753"/><bf gi="3996" ue="22757" us="22756"/><bf gi="3998" ue="22761" us="22761"/><bf gi="3999" ue="22764" us="22763"/><bf gi="4001" ue="22771" us="22766"/><bf gi="4007" ue="22775" us="22775"/><bf gi="4008" ue="22781" us="22777"/><bf gi="4013" ue="22786" us="22786"/><bf gi="4014" ue="22790" us="22789"/><bf gi="4016" ue="22796" us="22793"/><bf gi="4020" ue="22800" us="22799"/><bf gi="4022" ue="22806" us="22802"/><bf gi="4027" ue="22813" us="22808"/><bf gi="4033" ue="22821" us="22817"/><bf gi="4038" ue="22835" us="22823"/><bf gi="4051" ue="22840" us="22837"/><bf gi="4055" ue="22847" us="22846"/><bf gi="4057" ue="22852" us="22851"/><bf gi="4059" ue="22857" us="22854"/><bf gi="4063" ue="22869" us="22862"/><bf gi="4071" ue="22875" us="22871"/><bf gi="4076" ue="22883" us="22877"/><bf gi="4083" ue="22885" us="22885"/><bf gi="4084" ue="22895" us="22887"/><bf gi="4093" ue="22902" us="22898"/><bf gi="4098" ue="22905" us="22904"/><bf gi="4100" ue="22909" us="22907"/><bf gi="4103" !
ue="22916" us="22913"/><bf gi="4107" ue="22926" us="22922"/><bf gi="41
12" ue="22931" us="22930"/><bf gi="4114" ue="22935" us="22933"/><bf gi="4117" ue="22937" us="22937"/><bf gi="4118" ue="22939" us="22939"/><bf gi="4119" ue="22941" us="22941"/><bf gi="4120" ue="22943" us="22943"/><bf gi="4121" ue="22949" us="22947"/><bf gi="4124" ue="22952" us="22951"/><bf gi="4126" ue="22960" us="22956"/><bf gi="4131" ue="22963" us="22962"/><bf gi="4133" ue="22967" us="22967"/><bf gi="4134" ue="22972" us="22969"/><bf gi="4138" ue="22974" us="22974"/><bf gi="4139" ue="22977" us="22977"/><bf gi="4140" ue="22980" us="22979"/><bf gi="4142" ue="22982" us="22982"/><bf gi="4143" ue="22987" us="22984"/><bf gi="4147" ue="22989" us="22989"/><bf gi="4148" ue="22996" us="22992"/><bf gi="4153" ue="23002" us="23001"/><bf gi="4155" ue="23007" us="23004"/><bf gi="4159" ue="23016" us="23011"/><bf gi="4165" ue="23019" us="23018"/><bf gi="4167" ue="23023" us="23022"/><bf gi="4169" ue="23026" us="23025"/><bf gi="4171" ue="23028" us="23028"/><bf gi="4172" ue="23031" us="23030"/>!
<bf gi="4174" ue="23035" us="23035"/><bf gi="4175" ue="23041" us="23039"/><bf gi="4178" ue="23044" us="23043"/><bf gi="4180" ue="23049" us="23049"/><bf gi="4181" ue="23054" us="23052"/><bf gi="4184" ue="23059" us="23057"/><bf gi="4187" ue="23064" us="23064"/><bf gi="4188" ue="23066" us="23066"/><bf gi="4189" ue="23068" us="23068"/><bf gi="4190" ue="23072" us="23070"/><bf gi="4193" ue="23077" us="23075"/><bf gi="4196" ue="23082" us="23079"/><bf gi="4200" ue="23085" us="23085"/><bf gi="4201" ue="23088" us="23087"/><bf gi="4203" ue="23094" us="23093"/><bf gi="4205" ue="23100" us="23100"/><bf gi="4206" ue="23105" us="23104"/><bf gi="4208" ue="23113" us="23108"/><bf gi="4214" ue="23116" us="23116"/><bf gi="4215" ue="23120" us="23120"/><bf gi="4216" ue="23125" us="23125"/><bf gi="4217" ue="23130" us="23130"/><bf gi="4218" ue="23134" us="23134"/><bf gi="4219" ue="23139" us="23138"/><bf gi="4221" ue="23143" us="23141"/><bf gi="4224" ue="23146" us="23146"/><bf gi="4225" ue="23149" u!
s="23148"/><bf gi="4227" ue="23159" us="23159"/><bf gi="4228" ue="2316
3" us="23162"/><bf gi="4230" ue="23167" us="23166"/><bf gi="4232" ue="23179" us="23179"/><bf gi="4233" ue="23184" us="23184"/><bf gi="4234" ue="23187" us="23186"/><bf gi="4236" ue="23190" us="23190"/><bf gi="4237" ue="23196" us="23193"/><bf gi="4241" ue="23200" us="23198"/><bf gi="4244" ue="23202" us="23202"/><bf gi="4245" ue="23207" us="23207"/><bf gi="4246" ue="23212" us="23212"/><bf gi="4247" ue="23219" us="23217"/><bf gi="4250" ue="23221" us="23221"/><bf gi="4251" ue="23224" us="23224"/><bf gi="4252" ue="23231" us="23226"/><bf gi="4258" ue="23234" us="23233"/><bf gi="4260" ue="23236" us="23236"/><bf gi="4261" ue="23238" us="23238"/><bf gi="4262" ue="23241" us="23240"/><bf gi="4264" ue="23244" us="23243"/><bf gi="4266" ue="23248" us="23247"/><bf gi="4268" ue="23255" us="23254"/><bf gi="4270" ue="23258" us="23258"/><bf gi="4271" ue="23260" us="23260"/><bf gi="4272" ue="23265" us="23264"/><bf gi="4274" ue="23267" us="23267"/><bf gi="4275" ue="23270" us="23269"/><bf gi="4277!
" ue="23274" us="23273"/><bf gi="4279" ue="23278" us="23278"/><bf gi="4280" ue="23286" us="23285"/><bf gi="4282" ue="23291" us="23290"/><bf gi="4284" ue="23293" us="23293"/><bf gi="4285" ue="23297" us="23296"/><bf gi="4287" ue="23305" us="23304"/><bf gi="4289" ue="23308" us="23307"/><bf gi="4291" ue="23319" us="23318"/><bf gi="4293" ue="23321" us="23321"/><bf gi="4294" ue="23323" us="23323"/><bf gi="4295" ue="23325" us="23325"/><bf gi="4296" ue="23330" us="23329"/><bf gi="4298" ue="23333" us="23333"/><bf gi="4299" ue="23338" us="23338"/><bf gi="4300" ue="23341" us="23340"/><bf gi="4302" ue="23344" us="23344"/><bf gi="4303" ue="23346" us="23346"/><bf gi="4304" ue="23348" us="23348"/><bf gi="4305" ue="23350" us="23350"/><bf gi="4306" ue="23352" us="23352"/><bf gi="4307" ue="23358" us="23358"/><bf gi="4308" ue="23361" us="23360"/><bf gi="4310" ue="23363" us="23363"/><bf gi="4311" ue="23365" us="23365"/><bf gi="4312" ue="23372" us="23371"/><bf gi="4314" ue="23378" us="23376"/><!
bf gi="4317" ue="23384" us="23380"/><bf gi="4322" ue="23391" us="23386
"/><bf gi="4328" ue="23398" us="23395"/><bf gi="4332" ue="23401" us="23400"/><bf gi="4334" ue="23403" us="23403"/><bf gi="4335" ue="23409" us="23406"/><bf gi="4339" ue="23411" us="23411"/><bf gi="4340" ue="23413" us="23413"/><bf gi="4341" ue="23416" us="23416"/><bf gi="4342" ue="23418" us="23418"/><bf gi="4343" ue="23425" us="23420"/><bf gi="4349" ue="23441" us="23427"/><bf gi="4364" ue="23453" us="23443"/><bf gi="4375" ue="23455" us="23455"/><bf gi="4376" ue="23462" us="23458"/><bf gi="4381" ue="23465" us="23464"/><bf gi="4383" ue="23482" us="23468"/><bf gi="4398" ue="23484" us="23484"/><bf gi="4399" ue="23495" us="23487"/><bf gi="4408" ue="23497" us="23497"/><bf gi="4409" ue="23501" us="23500"/><bf gi="4411" ue="23504" us="23503"/><bf gi="4413" ue="23508" us="23506"/><bf gi="4416" ue="23515" us="23510"/><bf gi="4422" ue="23522" us="23517"/><bf gi="4428" ue="23529" us="23524"/><bf gi="4434" ue="23532" us="23531"/><bf gi="4436" ue="23537" us="23534"/><bf gi="4440" ue="23542"!
us="23539"/><bf gi="4444" ue="23544" us="23544"/><bf gi="4445" ue="23546" us="23546"/><bf gi="4446" ue="23551" us="23549"/><bf gi="4449" ue="23554" us="23553"/><bf gi="4451" ue="23567" us="23556"/><bf gi="4463" ue="23569" us="23569"/><bf gi="4464" ue="23571" us="23571"/><bf gi="4465" ue="23575" us="23574"/><bf gi="4467" ue="23578" us="23578"/><bf gi="4468" ue="23584" us="23582"/><bf gi="4471" ue="23588" us="23586"/><bf gi="4474" ue="23590" us="23590"/><bf gi="4475" ue="23593" us="23592"/><bf gi="4477" ue="23598" us="23595"/><bf gi="4481" ue="23602" us="23600"/><bf gi="4484" ue="23606" us="23605"/><bf gi="4486" ue="23617" us="23608"/><bf gi="4496" ue="23622" us="23621"/><bf gi="4498" ue="23624" us="23624"/><bf gi="4499" ue="23627" us="23626"/><bf gi="4501" ue="23633" us="23629"/><bf gi="4506" ue="23635" us="23635"/><bf gi="4507" ue="23637" us="23637"/><bf gi="4508" ue="23642" us="23641"/><bf gi="4510" ue="23644" us="23644"/><bf gi="4511" ue="23646" us="23646"/><bf gi="4512"!
ue="23653" us="23648"/><bf gi="4518" ue="23657" us="23655"/><bf gi="4
521" ue="23665" us="23660"/><bf gi="4527" ue="23670" us="23668"/><bf gi="4530" ue="23677" us="23673"/><bf gi="4535" ue="23688" us="23687"/><bf gi="4537" ue="23690" us="23690"/><bf gi="4538" ue="23692" us="23692"/><bf gi="4539" ue="23698" us="23695"/><bf gi="4543" ue="23700" us="23700"/><bf gi="4544" ue="23709" us="23709"/><bf gi="4545" ue="23715" us="23711"/><bf gi="4550" ue="23718" us="23718"/><bf gi="4551" ue="23724" us="23720"/><bf gi="4556" ue="23736" us="23729"/><bf gi="4564" ue="23740" us="23738"/><bf gi="4567" ue="23742" us="23742"/><bf gi="4568" ue="23749" us="23749"/><bf gi="4569" ue="23751" us="23751"/><bf gi="4570" ue="23753" us="23753"/><bf gi="4571" ue="23755" us="23755"/><bf gi="4572" ue="23762" us="23762"/><bf gi="4573" ue="23767" us="23767"/><bf gi="4574" ue="23769" us="23769"/><bf gi="4575" ue="23773" us="23773"/><bf gi="4576" ue="23777" us="23776"/><bf gi="4578" ue="23786" us="23784"/><bf gi="4581" ue="23794" us="23789"/><bf gi="4587" ue="23798" us="23796"/!
><bf gi="4590" ue="23803" us="23802"/><bf gi="4592" ue="23805" us="23805"/><bf gi="4593" ue="23809" us="23809"/><bf gi="4594" ue="23815" us="23814"/><bf gi="4596" ue="23819" us="23819"/><bf gi="4597" ue="23822" us="23821"/><bf gi="4599" ue="23826" us="23825"/><bf gi="4601" ue="23835" us="23828"/><bf gi="4609" ue="23839" us="23839"/><bf gi="4610" ue="23844" us="23842"/><bf gi="4613" ue="23847" us="23846"/><bf gi="4615" ue="23849" us="23849"/><bf gi="4616" ue="23851" us="23851"/><bf gi="4617" ue="23857" us="23857"/><bf gi="4618" ue="23860" us="23860"/><bf gi="4619" ue="23865" us="23865"/><bf gi="4620" ue="23869" us="23869"/><bf gi="4621" ue="23871" us="23871"/><bf gi="4622" ue="23875" us="23874"/><bf gi="4624" ue="23878" us="23878"/><bf gi="4625" ue="23880" us="23880"/><bf gi="4626" ue="23884" us="23882"/><bf gi="4629" ue="23886" us="23886"/><bf gi="4630" ue="23890" us="23888"/><bf gi="4633" ue="23893" us="23893"/><bf gi="4634" ue="23897" us="23897"/><bf gi="4635" ue="23900" !
us="23900"/><bf gi="4636" ue="23906" us="23903"/><bf gi="4640" ue="239
08" us="23908"/><bf gi="4641" ue="23914" us="23913"/><bf gi="4643" ue="23917" us="23916"/><bf gi="4645" ue="23920" us="23919"/><bf gi="4647" ue="23923" us="23923"/><bf gi="4648" ue="23926" us="23926"/><bf gi="4649" ue="23930" us="23929"/><bf gi="4651" ue="23935" us="23934"/><bf gi="4653" ue="23940" us="23937"/><bf gi="4657" ue="23944" us="23943"/><bf gi="4659" ue="23948" us="23946"/><bf gi="4662" ue="23952" us="23952"/><bf gi="4663" ue="23957" us="23954"/><bf gi="4667" ue="23961" us="23961"/><bf gi="4668" ue="23963" us="23963"/><bf gi="4669" ue="23965" us="23965"/><bf gi="4670" ue="23968" us="23967"/><bf gi="4672" ue="23970" us="23970"/><bf gi="4673" ue="23975" us="23975"/><bf gi="4674" ue="23980" us="23979"/><bf gi="4676" ue="23982" us="23982"/><bf gi="4677" ue="23984" us="23984"/><bf gi="4678" ue="23986" us="23986"/><bf gi="4679" ue="23988" us="23988"/><bf gi="4680" ue="23994" us="23991"/><bf gi="4684" ue="23997" us="23996"/><bf gi="4686" ue="24003" us="24003"/><bf gi="468!
7" ue="24007" us="24007"/><bf gi="4688" ue="24009" us="24009"/><bf gi="4689" ue="24014" us="24011"/><bf gi="4693" ue="24016" us="24016"/><bf gi="4694" ue="24019" us="24018"/><bf gi="4696" ue="24022" us="24022"/><bf gi="4697" ue="24025" us="24024"/><bf gi="4699" ue="24027" us="24027"/><bf gi="4700" ue="24030" us="24029"/><bf gi="4702" ue="24033" us="24032"/><bf gi="4704" ue="24041" us="24035"/><bf gi="4711" ue="24043" us="24043"/><bf gi="4712" ue="24046" us="24046"/><bf gi="4713" ue="24053" us="24049"/><bf gi="4718" ue="24057" us="24055"/><bf gi="4721" ue="24059" us="24059"/><bf gi="4722" ue="24062" us="24061"/><bf gi="4724" ue="24064" us="24064"/><bf gi="4725" ue="24067" us="24066"/><bf gi="4727" ue="24071" us="24070"/><bf gi="4729" ue="24077" us="24075"/><bf gi="4732" ue="24082" us="24081"/><bf gi="4734" ue="24086" us="24084"/><bf gi="4737" ue="24091" us="24088"/><bf gi="4741" ue="24093" us="24093"/><bf gi="4742" ue="24096" us="24095"/><bf gi="4744" ue="24101" us="24101"/>!
<bf gi="4745" ue="24104" us="24104"/><bf gi="4746" ue="24107" us="2410
7"/><bf gi="4747" ue="24112" us="24109"/><bf gi="4751" ue="24115" us="24114"/><bf gi="4753" ue="24120" us="24117"/><bf gi="4757" ue="24126" us="24125"/><bf gi="4759" ue="24128" us="24128"/><bf gi="4760" ue="24133" us="24131"/><bf gi="4763" ue="24135" us="24135"/><bf gi="4764" ue="24137" us="24137"/><bf gi="4765" ue="24140" us="24139"/><bf gi="4767" ue="24142" us="24142"/><bf gi="4768" ue="24145" us="24144"/><bf gi="4770" ue="24152" us="24148"/><bf gi="4775" ue="24156" us="24155"/><bf gi="4777" ue="24159" us="24158"/><bf gi="4779" ue="24164" us="24161"/><bf gi="4783" ue="24168" us="24168"/><bf gi="4784" ue="24174" us="24170"/><bf gi="4789" ue="24176" us="24176"/><bf gi="4790" ue="24182" us="24178"/><bf gi="4795" ue="24193" us="24184"/><bf gi="4805" ue="24196" us="24195"/><bf gi="4807" ue="24199" us="24199"/><bf gi="4808" ue="24203" us="24202"/><bf gi="4810" ue="24207" us="24206"/><bf gi="4812" ue="24215" us="24213"/><bf gi="4815" ue="24218" us="24218"/><bf gi="4816" ue="24220!
" us="24220"/><bf gi="4817" ue="24224" us="24224"/><bf gi="4818" ue="24226" us="24226"/><bf gi="4819" ue="24232" us="24228"/><bf gi="4824" ue="24237" us="24234"/><bf gi="4828" ue="24241" us="24241"/><bf gi="4829" ue="24243" us="24243"/><bf gi="4830" ue="24248" us="24245"/><bf gi="4834" ue="24255" us="24253"/><bf gi="4837" ue="24259" us="24257"/><bf gi="4840" ue="24262" us="24262"/><bf gi="4841" ue="24268" us="24264"/><bf gi="4846" ue="24278" us="24270"/><bf gi="4855" ue="24291" us="24282"/><bf gi="4865" ue="24293" us="24293"/><bf gi="4866" ue="24297" us="24296"/><bf gi="4868" ue="24300" us="24299"/><bf gi="4870" ue="24305" us="24304"/><bf gi="4872" ue="24308" us="24307"/><bf gi="4874" ue="24312" us="24310"/><bf gi="4877" ue="24316" us="24314"/><bf gi="4880" ue="24319" us="24318"/><bf gi="4882" ue="24324" us="24321"/><bf gi="4886" ue="24337" us="24326"/><bf gi="4898" ue="24345" us="24339"/><bf gi="4905" ue="24349" us="24347"/><bf gi="4908" ue="24351" us="24351"/><bf gi="4909!
" ue="24361" us="24353"/><bf gi="4918" ue="24369" us="24363"/><bf gi="
4925" ue="24376" us="24372"/><bf gi="4930" ue="24385" us="24379"/><bf gi="4937" ue="24389" us="24388"/><bf gi="4939" ue="24392" us="24391"/><bf gi="4941" ue="24394" us="24394"/><bf gi="4942" ue="24398" us="24396"/><bf gi="4945" ue="24401" us="24400"/><bf gi="4947" ue="24404" us="24403"/><bf gi="4949" ue="24409" us="24406"/><bf gi="4953" ue="24413" us="24411"/><bf gi="4956" ue="24420" us="24416"/><bf gi="4961" ue="24423" us="24422"/><bf gi="4963" ue="24429" us="24425"/><bf gi="4968" ue="24437" us="24431"/><bf gi="4975" ue="24442" us="24439"/><bf gi="4979" ue="24453" us="24444"/><bf gi="4989" ue="24461" us="24455"/><bf gi="4996" ue="24467" us="24463"/><bf gi="5001" ue="24473" us="24470"/><bf gi="5005" ue="24478" us="24476"/><bf gi="5008" ue="24482" us="24480"/><bf gi="5011" ue="24484" us="24484"/><bf gi="5012" ue="24497" us="24487"/><bf gi="5023" ue="24500" us="24499"/><bf gi="5025" ue="24505" us="24503"/><bf gi="5028" ue="24509" us="24508"/><bf gi="5030" ue="24517" us="24515"!
/><bf gi="5033" ue="24521" us="24519"/><bf gi="5036" ue="24525" us="24523"/><bf gi="5039" ue="24532" us="24528"/><bf gi="5044" ue="24537" us="24534"/><bf gi="5048" ue="24542" us="24540"/><bf gi="5051" ue="24546" us="24544"/><bf gi="5054" ue="24548" us="24548"/><bf gi="5055" ue="24563" us="24552"/><bf gi="5067" ue="24566" us="24565"/><bf gi="5069" ue="24568" us="24568"/><bf gi="5070" ue="24573" us="24570"/><bf gi="5074" ue="24575" us="24575"/><bf gi="5075" ue="24583" us="24583"/><bf gi="5076" ue="24586" us="24586"/><bf gi="5077" ue="24592" us="24589"/><bf gi="5081" ue="24605" us="24594"/><bf gi="5093" ue="24609" us="24607"/><bf gi="5096" ue="24619" us="24612"/><bf gi="5104" ue="24621" us="24621"/><bf gi="5105" ue="24623" us="24623"/><bf gi="5106" ue="24625" us="24625"/><bf gi="5107" ue="24627" us="24627"/><bf gi="5108" ue="24629" us="24629"/><bf gi="5109" ue="24634" us="24634"/><bf gi="5110" ue="24643" us="24640"/><bf gi="5114" ue="24653" us="24646"/><bf gi="5122" ue="24658"!
us="24656"/><bf gi="5125" ue="24663" us="24660"/><bf gi="5129" ue="24
666" us="24665"/><bf gi="5131" ue="24669" us="24669"/><bf gi="5132" ue="24677" us="24671"/><bf gi="5139" ue="24685" us="24679"/><bf gi="5146" ue="24689" us="24687"/><bf gi="5149" ue="24693" us="24693"/><bf gi="5150" ue="24695" us="24695"/><bf gi="5151" ue="24703" us="24702"/><bf gi="5153" ue="24710" us="24705"/><bf gi="5159" ue="24718" us="24712"/><bf gi="5166" ue="24728" us="24721"/><bf gi="5174" ue="24731" us="24730"/><bf gi="5176" ue="24736" us="24733"/><bf gi="5180" ue="24746" us="24738"/><bf gi="5189" ue="24760" us="24752"/><bf gi="5198" ue="24766" us="24763"/><bf gi="5202" ue="24770" us="24770"/><bf gi="5203" ue="24779" us="24772"/><bf gi="5211" ue="24783" us="24782"/><bf gi="5213" ue="24785" us="24785"/><bf gi="5214" ue="24789" us="24787"/><bf gi="5217" ue="24803" us="24792"/><bf gi="5229" ue="24805" us="24805"/><bf gi="5230" ue="24808" us="24807"/><bf gi="5232" ue="24829" us="24816"/><bf gi="5246" ue="24835" us="24832"/><bf gi="5250" ue="24842" us="24838"/><bf gi="52!
55" ue="24855" us="24844"/><bf gi="5267" ue="24860" us="24857"/><bf gi="5271" ue="24866" us="24862"/><bf gi="5276" ue="24872" us="24871"/><bf gi="5278" ue="24876" us="24874"/><bf gi="5281" ue="24881" us="24880"/><bf gi="5283" ue="24887" us="24884"/><bf gi="5287" ue="24889" us="24889"/><bf gi="5288" ue="24895" us="24892"/><bf gi="5292" ue="24898" us="24897"/><bf gi="5294" ue="24910" us="24900"/><bf gi="5305" ue="24915" us="24915"/><bf gi="5306" ue="24917" us="24917"/><bf gi="5307" ue="24922" us="24920"/><bf gi="5310" ue="24928" us="24925"/><bf gi="5314" ue="24931" us="24930"/><bf gi="5316" ue="24933" us="24933"/><bf gi="5317" ue="24936" us="24935"/><bf gi="5319" ue="24940" us="24939"/><bf gi="5321" ue="24952" us="24942"/><bf gi="5332" ue="24956" us="24955"/><bf gi="5334" ue="24964" us="24958"/><bf gi="5341" ue="24967" us="24967"/><bf gi="5342" ue="24971" us="24970"/><bf gi="5344" ue="24974" us="24973"/><bf gi="5346" ue="24980" us="24976"/><bf gi="5351" ue="24986" us="24982"/!
><bf gi="5356" ue="24989" us="24988"/><bf gi="5358" ue="24992" us="249
91"/><bf gi="5360" ue="24997" us="24996"/><bf gi="5362" ue="25006" us="24999"/><bf gi="5370" ue="25010" us="25010"/><bf gi="5371" ue="25014" us="25014"/><bf gi="5372" ue="25018" us="25016"/><bf gi="5375" ue="25020" us="25020"/><bf gi="5376" ue="25022" us="25022"/><bf gi="5377" ue="25027" us="25024"/><bf gi="5381" ue="25040" us="25030"/><bf gi="5392" ue="25045" us="25045"/><bf gi="5393" ue="25055" us="25052"/><bf gi="5397" ue="25059" us="25057"/><bf gi="5400" ue="25063" us="25061"/><bf gi="5403" ue="25065" us="25065"/><bf gi="5404" ue="25069" us="25068"/><bf gi="5406" ue="25071" us="25071"/><bf gi="5407" ue="25074" us="25074"/><bf gi="5408" ue="25076" us="25076"/><bf gi="5409" ue="25080" us="25078"/><bf gi="5412" ue="25082" us="25082"/><bf gi="5413" ue="25089" us="25084"/><bf gi="5419" ue="25092" us="25091"/><bf gi="5421" ue="25098" us="25095"/><bf gi="5425" ue="25102" us="25100"/><bf gi="5428" ue="25110" us="25104"/><bf gi="5435" ue="25123" us="25114"/><bf gi="5445" ue="2512!
7" us="25126"/><bf gi="5447" ue="25131" us="25129"/><bf gi="5450" ue="25136" us="25134"/><bf gi="5453" ue="25140" us="25138"/><bf gi="5456" ue="25145" us="25144"/><bf gi="5458" ue="25147" us="25147"/><bf gi="5459" ue="25149" us="25149"/><bf gi="5460" ue="25156" us="25151"/><bf gi="5466" ue="25161" us="25158"/><bf gi="5470" ue="25166" us="25163"/><bf gi="5474" ue="25174" us="25168"/><bf gi="5481" ue="25176" us="25176"/><bf gi="5482" ue="25180" us="25178"/><bf gi="5485" ue="25182" us="25182"/><bf gi="5486" ue="25184" us="25184"/><bf gi="5487" ue="25188" us="25187"/><bf gi="5489" ue="25192" us="25192"/><bf gi="5490" ue="25199" us="25197"/><bf gi="5493" ue="25201" us="25201"/><bf gi="5494" ue="25203" us="25203"/><bf gi="5495" ue="25206" us="25206"/><bf gi="5496" ue="25210" us="25209"/><bf gi="5498" ue="25216" us="25212"/><bf gi="5503" ue="25220" us="25218"/><bf gi="5506" ue="25226" us="25225"/><bf gi="5508" ue="25240" us="25229"/><bf gi="5520" ue="25244" us="25243"/><bf gi="552!
2" ue="25246" us="25246"/><bf gi="5523" ue="25254" us="25254"/><bf gi=
"5524" ue="25256" us="25256"/><bf gi="5525" ue="25260" us="25259"/><bf gi="5527" ue="25265" us="25265"/><bf gi="5528" ue="25267" us="25267"/><bf gi="5529" ue="25271" us="25269"/><bf gi="5532" ue="25279" us="25273"/><bf gi="5539" ue="25282" us="25282"/><bf gi="5540" ue="25290" us="25284"/><bf gi="5547" ue="25309" us="25292"/><bf gi="5565" ue="25313" us="25312"/><bf gi="5567" ue="25322" us="25322"/><bf gi="5568" ue="25327" us="25324"/><bf gi="5572" ue="25335" us="25329"/><bf gi="5579" ue="25343" us="25340"/><bf gi="5583" ue="25348" us="25345"/><bf gi="5587" ue="25357" us="25351"/><bf gi="5594" ue="25361" us="25360"/><bf gi="5596" ue="25363" us="25363"/><bf gi="5597" ue="25366" us="25366"/><bf gi="5598" ue="25369" us="25368"/><bf gi="5600" ue="25375" us="25375"/><bf gi="5601" ue="25387" us="25383"/><bf gi="5606" ue="25389" us="25389"/><bf gi="5607" ue="25391" us="25391"/><bf gi="5608" ue="25398" us="25397"/><bf gi="5610" ue="25402" us="25401"/><bf gi="5612" ue="25407" us="25404!
"/><bf gi="5616" ue="25412" us="25409"/><bf gi="5620" ue="25414" us="25414"/><bf gi="5621" ue="25424" us="25417"/><bf gi="5629" ue="25429" us="25426"/><bf gi="5633" ue="25432" us="25431"/><bf gi="5635" ue="25436" us="25435"/><bf gi="5637" ue="25449" us="25445"/><bf gi="5642" ue="25454" us="25451"/><bf gi="5646" ue="25458" us="25457"/><bf gi="5648" ue="25464" us="25460"/><bf gi="5653" ue="25469" us="25466"/><bf gi="5657" ue="25472" us="25471"/><bf gi="5659" ue="25476" us="25474"/><bf gi="5662" ue="25482" us="25479"/><bf gi="5666" ue="25484" us="25484"/><bf gi="5667" ue="25488" us="25486"/><bf gi="5670" ue="25490" us="25490"/><bf gi="5671" ue="25494" us="25492"/><bf gi="5674" ue="25499" us="25496"/><bf gi="5678" ue="25519" us="25502"/><bf gi="5696" ue="25522" us="25522"/><bf gi="5697" ue="25525" us="25524"/><bf gi="5699" ue="25531" us="25531"/><bf gi="5700" ue="25534" us="25533"/><bf gi="5702" ue="25537" us="25536"/><bf gi="5704" ue="25542" us="25539"/><bf gi="5708" ue="25545!
" us="25544"/><bf gi="5710" ue="25558" us="25550"/><bf gi="5719" ue="2
5564" us="25562"/><bf gi="5722" ue="25569" us="25568"/><bf gi="5724" ue="25571" us="25571"/><bf gi="5725" ue="25573" us="25573"/><bf gi="5726" ue="25578" us="25577"/><bf gi="5728" ue="25580" us="25580"/><bf gi="5729" ue="25582" us="25582"/><bf gi="5730" ue="25590" us="25586"/><bf gi="5735" ue="25594" us="25592"/><bf gi="5738" ue="25606" us="25606"/><bf gi="5739" ue="25610" us="25609"/><bf gi="5741" ue="25613" us="25613"/><bf gi="5742" ue="25616" us="25615"/><bf gi="5744" ue="25620" us="25618"/><bf gi="5747" ue="25624" us="25622"/><bf gi="5750" ue="25628" us="25628"/><bf gi="5751" ue="25630" us="25630"/><bf gi="5752" ue="25632" us="25632"/><bf gi="5753" ue="25634" us="25634"/><bf gi="5754" ue="25638" us="25636"/><bf gi="5757" ue="25642" us="25640"/><bf gi="5760" ue="25645" us="25644"/><bf gi="5762" ue="25648" us="25647"/><bf gi="5764" ue="25654" us="25652"/><bf gi="5767" ue="25658" us="25658"/><bf gi="5768" ue="25663" us="25661"/><bf gi="5771" ue="25666" us="25666"/><bf gi="5!
772" ue="25675" us="25675"/><bf gi="5773" ue="25679" us="25678"/><bf gi="5775" ue="25684" us="25681"/><bf gi="5779" ue="25688" us="25688"/><bf gi="5780" ue="25693" us="25690"/><bf gi="5784" ue="25697" us="25695"/><bf gi="5787" ue="25699" us="25699"/><bf gi="5788" ue="25703" us="25703"/><bf gi="5789" ue="25705" us="25705"/><bf gi="5790" ue="25709" us="25709"/><bf gi="5791" ue="25711" us="25711"/><bf gi="5792" ue="25716" us="25715"/><bf gi="5794" ue="25718" us="25718"/><bf gi="5795" ue="25720" us="25720"/><bf gi="5796" ue="25723" us="25722"/><bf gi="5798" ue="25725" us="25725"/><bf gi="5799" ue="25731" us="25731"/><bf gi="5800" ue="25733" us="25733"/><bf gi="5801" ue="25736" us="25735"/><bf gi="5803" ue="25747" us="25743"/><bf gi="5808" ue="25749" us="25749"/><bf gi="5809" ue="25755" us="25752"/><bf gi="5813" ue="25759" us="25757"/><bf gi="5816" ue="25761" us="25761"/><bf gi="5817" ue="25766" us="25763"/><bf gi="5821" ue="25769" us="25768"/><bf gi="5823" ue="25774" us="25771"!
/><bf gi="5827" ue="25776" us="25776"/><bf gi="5828" ue="25779" us="25
778"/><bf gi="5830" ue="25785" us="25785"/><bf gi="5831" ue="25791" us="25787"/><bf gi="5836" ue="25794" us="25793"/><bf gi="5838" ue="25797" us="25796"/><bf gi="5840" ue="25799" us="25799"/><bf gi="5841" ue="25806" us="25801"/><bf gi="5847" ue="25810" us="25808"/><bf gi="5850" ue="25813" us="25812"/><bf gi="5852" ue="25816" us="25815"/><bf gi="5854" ue="25818" us="25818"/><bf gi="5855" ue="25831" us="25824"/><bf gi="5863" ue="25834" us="25833"/><bf gi="5865" ue="25837" us="25836"/><bf gi="5867" ue="25842" us="25839"/><bf gi="5871" ue="25847" us="25844"/><bf gi="5875" ue="25851" us="25850"/><bf gi="5877" ue="25857" us="25853"/><bf gi="5882" ue="25861" us="25860"/><bf gi="5884" ue="25866" us="25864"/><bf gi="5887" ue="25871" us="25871"/><bf gi="5888" ue="25876" us="25875"/><bf gi="5890" ue="25878" us="25878"/><bf gi="5891" ue="25881" us="25880"/><bf gi="5893" ue="25887" us="25883"/><bf gi="5898" ue="25892" us="25890"/><bf gi="5901" ue="25894" us="25894"/><bf gi="5902" ue="259!
00" us="25897"/><bf gi="5906" ue="25903" us="25902"/><bf gi="5908" ue="25905" us="25905"/><bf gi="5909" ue="25919" us="25908"/><bf gi="5921" ue="25923" us="25923"/><bf gi="5922" ue="25925" us="25925"/><bf gi="5923" ue="25929" us="25927"/><bf gi="5926" ue="25938" us="25933"/><bf gi="5932" ue="25945" us="25940"/><bf gi="5938" ue="25952" us="25949"/><bf gi="5942" ue="25955" us="25954"/><bf gi="5944" ue="25959" us="25958"/><bf gi="5946" ue="25964" us="25963"/><bf gi="5948" ue="25968" us="25968"/><bf gi="5949" ue="25970" us="25970"/><bf gi="5950" ue="25973" us="25972"/><bf gi="5952" ue="25976" us="25975"/><bf gi="5954" ue="25978" us="25978"/><bf gi="5955" ue="25981" us="25981"/><bf gi="5956" ue="25987" us="25985"/><bf gi="5959" ue="25989" us="25989"/><bf gi="5960" ue="25994" us="25991"/><bf gi="5964" ue="25996" us="25996"/><bf gi="5965" ue="25998" us="25998"/><bf gi="5966" ue="26002" us="26000"/><bf gi="5969" ue="26005" us="26005"/><bf gi="5970" ue="26009" us="26007"/><bf gi="59!
73" ue="26013" us="26011"/><bf gi="5976" ue="26017" us="26015"/><bf gi
="5979" ue="26023" us="26019"/><bf gi="5984" ue="26032" us="26027"/><bf gi="5990" ue="26036" us="26034"/><bf gi="5993" ue="26039" us="26039"/><bf gi="5994" ue="26041" us="26041"/><bf gi="5995" ue="26045" us="26044"/><bf gi="5997" ue="26047" us="26047"/><bf gi="5998" ue="26054" us="26049"/><bf gi="6004" ue="26057" us="26056"/><bf gi="6006" ue="26060" us="26059"/><bf gi="6008" ue="26064" us="26062"/><bf gi="6011" ue="26066" us="26066"/><bf gi="6012" ue="26068" us="26068"/><bf gi="6013" ue="26073" us="26070"/><bf gi="6017" ue="26075" us="26075"/><bf gi="6018" ue="26082" us="26079"/><bf gi="6022" ue="26089" us="26085"/><bf gi="6027" ue="26093" us="26092"/><bf gi="6029" ue="26098" us="26096"/><bf gi="6032" ue="26101" us="26100"/><bf gi="6034" ue="26107" us="26105"/><bf gi="6037" ue="26112" us="26110"/><bf gi="6040" ue="26116" us="26114"/><bf gi="6043" ue="26122" us="26118"/><bf gi="6048" ue="26127" us="26124"/><bf gi="6052" ue="26134" us="26129"/><bf gi="6058" ue="26161" us="2614!
0"/><bf gi="6080" ue="26167" us="26163"/><bf gi="6085" ue="26169" us="26169"/><bf gi="6086" ue="26172" us="26172"/><bf gi="6087" ue="26182" us="26175"/><bf gi="6095" ue="26188" us="26185"/><bf gi="6099" ue="26191" us="26190"/><bf gi="6101" ue="26194" us="26193"/><bf gi="6103" ue="26201" us="26199"/><bf gi="6106" ue="26210" us="26203"/><bf gi="6114" ue="26220" us="26212"/><bf gi="6123" ue="26224" us="26222"/><bf gi="6126" ue="26236" us="26227"/><bf gi="6136" ue="26241" us="26238"/><bf gi="6140" ue="26244" us="26243"/><bf gi="6142" ue="26249" us="26247"/><bf gi="6145" ue="26254" us="26251"/><bf gi="6149" ue="26258" us="26256"/><bf gi="6152" ue="26269" us="26262"/><bf gi="6160" ue="26272" us="26271"/><bf gi="6162" ue="26274" us="26274"/><bf gi="6163" ue="26276" us="26276"/><bf gi="6164" ue="26278" us="26278"/><bf gi="6165" ue="26283" us="26283"/><bf gi="6166" ue="26286" us="26285"/><bf gi="6168" ue="26290" us="26289"/><bf gi="6170" ue="26293" us="26292"/><bf gi="6172" ue="2629!
7" us="26296"/><bf gi="6174" ue="26300" us="26299"/><bf gi="6176" ue="
26308" us="26302"/><bf gi="6183" ue="26313" us="26311"/><bf gi="6186" ue="26316" us="26316"/><bf gi="6187" ue="26319" us="26318"/><bf gi="6189" ue="26324" us="26324"/><bf gi="6190" ue="26326" us="26326"/><bf gi="6191" ue="26333" us="26329"/><bf gi="6196" ue="26336" us="26335"/><bf gi="6198" ue="26342" us="26342"/><bf gi="6199" ue="26345" us="26344"/><bf gi="6201" ue="26348" us="26347"/><bf gi="6203" ue="26350" us="26350"/><bf gi="6204" ue="26352" us="26352"/><bf gi="6205" ue="26357" us="26354"/><bf gi="6209" ue="26368" us="26359"/><bf gi="6219" ue="26371" us="26371"/><bf gi="6220" ue="26373" us="26373"/><bf gi="6221" ue="26377" us="26375"/><bf gi="6224" ue="26379" us="26379"/><bf gi="6225" ue="26383" us="26381"/><bf gi="6228" ue="26391" us="26387"/><bf gi="6233" ue="26393" us="26393"/><bf gi="6234" ue="26400" us="26395"/><bf gi="6240" ue="26402" us="26402"/><bf gi="6241" ue="26408" us="26406"/><bf gi="6244" ue="26414" us="26410"/><bf gi="6249" ue="26417" us="26417"/><bf gi="!
6250" ue="26420" us="26419"/><bf gi="6252" ue="26424" us="26422"/><bf gi="6255" ue="26426" us="26426"/><bf gi="6256" ue="26431" us="26429"/><bf gi="6259" ue="26433" us="26433"/><bf gi="6260" ue="26441" us="26437"/><bf gi="6265" ue="26444" us="26444"/><bf gi="6266" ue="26449" us="26446"/><bf gi="6270" ue="26454" us="26451"/><bf gi="6274" ue="26457" us="26457"/><bf gi="6275" ue="26470" us="26460"/><bf gi="6286" ue="26474" us="26474"/><bf gi="6287" ue="26487" us="26476"/><bf gi="6299" ue="26492" us="26491"/><bf gi="6301" ue="26495" us="26494"/><bf gi="6303" ue="26497" us="26497"/><bf gi="6304" ue="26501" us="26500"/><bf gi="6306" ue="26503" us="26503"/><bf gi="6307" ue="26505" us="26505"/><bf gi="6308" ue="26508" us="26507"/><bf gi="6310" ue="26513" us="26510"/><bf gi="6314" ue="26515" us="26515"/><bf gi="6315" ue="26525" us="26517"/><bf gi="6324" ue="26530" us="26528"/><bf gi="6327" ue="26534" us="26534"/><bf gi="6328" ue="26537" us="26537"/><bf gi="6329" ue="26553" us="26543!
"/><bf gi="6340" ue="26557" us="26555"/><bf gi="6343" ue="26566" us="2
6560"/><bf gi="6350" ue="26570" us="26568"/><bf gi="6353" ue="26580" us="26574"/><bf gi="6360" ue="26586" us="26583"/><bf gi="6364" ue="26590" us="26588"/><bf gi="6367" ue="26594" us="26593"/><bf gi="6369" ue="26596" us="26596"/><bf gi="6370" ue="26599" us="26598"/><bf gi="6372" ue="26601" us="26601"/><bf gi="6373" ue="26604" us="26604"/><bf gi="6374" ue="26615" us="26606"/><bf gi="6384" ue="26617" us="26617"/><bf gi="6385" ue="26619" us="26619"/><bf gi="6386" ue="26623" us="26622"/><bf gi="6388" ue="26628" us="26625"/><bf gi="6392" ue="26644" us="26643"/><bf gi="6394" ue="26647" us="26646"/><bf gi="6396" ue="26649" us="26649"/><bf gi="6397" ue="26655" us="26653"/><bf gi="6400" ue="26658" us="26657"/><bf gi="6402" ue="26669" us="26663"/><bf gi="6409" ue="26676" us="26671"/><bf gi="6415" ue="26681" us="26680"/><bf gi="6417" ue="26685" us="26683"/><bf gi="6420" ue="26694" us="26687"/><bf gi="6428" ue="26696" us="26696"/><bf gi="6429" ue="26698" us="26698"/><bf gi="6430" ue="26!
702" us="26700"/><bf gi="6433" ue="26709" us="26704"/><bf gi="6439" ue="26713" us="26711"/><bf gi="6442" ue="26717" us="26715"/><bf gi="6445" ue="26719" us="26719"/><bf gi="6446" ue="26723" us="26723"/><bf gi="6447" ue="26727" us="26727"/><bf gi="6448" ue="26731" us="26731"/><bf gi="6449" ue="26738" us="26734"/><bf gi="6454" ue="26743" us="26740"/><bf gi="6458" ue="26748" us="26745"/><bf gi="6462" ue="26751" us="26750"/><bf gi="6464" ue="26758" us="26753"/><bf gi="6470" ue="26760" us="26760"/><bf gi="6471" ue="26765" us="26765"/><bf gi="6472" ue="26767" us="26767"/><bf gi="6473" ue="26772" us="26771"/><bf gi="6475" ue="26776" us="26774"/><bf gi="6478" ue="26781" us="26778"/><bf gi="6482" ue="26787" us="26783"/><bf gi="6487" ue="26794" us="26789"/><bf gi="6493" ue="26803" us="26797"/><bf gi="6500" ue="26806" us="26805"/><bf gi="6502" ue="26812" us="26809"/><bf gi="6506" ue="26822" us="26820"/><bf gi="6509" ue="26829" us="26824"/><bf gi="6515" ue="26842" us="26831"/><bf gi="6!
527" ue="26845" us="26844"/><bf gi="6529" ue="26849" us="26847"/><bf g
i="6532" ue="26851" us="26851"/><bf gi="6533" ue="26853" us="26853"/><bf gi="6534" ue="26856" us="26855"/><bf gi="6536" ue="26866" us="26858"/><bf gi="6545" ue="26870" us="26869"/><bf gi="6547" ue="26877" us="26873"/><bf gi="6552" ue="26881" us="26880"/><bf gi="6554" ue="26886" us="26884"/><bf gi="6557" ue="26899" us="26888"/><bf gi="6569" ue="26903" us="26902"/><bf gi="6571" ue="26908" us="26905"/><bf gi="6575" ue="26915" us="26913"/><bf gi="6578" ue="26918" us="26917"/><bf gi="6580" ue="26920" us="26920"/><bf gi="6581" ue="26922" us="26922"/><bf gi="6582" ue="26929" us="26928"/><bf gi="6584" ue="26934" us="26931"/><bf gi="6588" ue="26937" us="26936"/><bf gi="6590" ue="26939" us="26939"/><bf gi="6591" ue="26941" us="26941"/><bf gi="6592" ue="26943" us="26943"/><bf gi="6593" ue="26946" us="26946"/><bf gi="6594" ue="26949" us="26949"/><bf gi="6595" ue="26954" us="26953"/><bf gi="6597" ue="26958" us="26958"/><bf gi="6598" ue="26965" us="26963"/><bf gi="6601" ue="26967" us="269!
67"/><bf gi="6602" ue="26974" us="26969"/><bf gi="6608" ue="26982" us="26976"/><bf gi="6615" ue="26997" us="26984"/><bf gi="6629" ue="27010" us="26999"/><bf gi="6641" ue="27018" us="27018"/><bf gi="6642" ue="27022" us="27021"/><bf gi="6644" ue="27026" us="27025"/><bf gi="6646" ue="27030" us="27028"/><bf gi="6649" ue="27032" us="27032"/><bf gi="6650" ue="27036" us="27035"/><bf gi="6652" ue="27041" us="27040"/><bf gi="6654" ue="27048" us="27045"/><bf gi="6658" ue="27051" us="27051"/><bf gi="6659" ue="27055" us="27053"/><bf gi="6662" ue="27058" us="27057"/><bf gi="6664" ue="27060" us="27060"/><bf gi="6665" ue="27064" us="27063"/><bf gi="6667" ue="27068" us="27066"/><bf gi="6670" ue="27071" us="27070"/><bf gi="6672" ue="27073" us="27073"/><bf gi="6673" ue="27075" us="27075"/><bf gi="6674" ue="27077" us="27077"/><bf gi="6675" ue="27080" us="27079"/><bf gi="6677" ue="27086" us="27082"/><bf gi="6682" ue="27089" us="27088"/><bf gi="6684" ue="27091" us="27091"/><bf gi="6685" ue="270!
97" us="27094"/><bf gi="6689" ue="27102" us="27101"/><bf gi="6691" ue=
"27106" us="27106"/><bf gi="6692" ue="27109" us="27109"/><bf gi="6693" ue="27112" us="27111"/><bf gi="6695" ue="27115" us="27115"/><bf gi="6696" ue="27119" us="27117"/><bf gi="6699" ue="27123" us="27121"/><bf gi="6702" ue="27125" us="27125"/><bf gi="6703" ue="27129" us="27129"/><bf gi="6704" ue="27131" us="27131"/><bf gi="6705" ue="27139" us="27133"/><bf gi="6712" ue="27141" us="27141"/><bf gi="6713" ue="27148" us="27146"/><bf gi="6716" ue="27151" us="27151"/><bf gi="6717" ue="27157" us="27153"/><bf gi="6722" ue="27159" us="27159"/><bf gi="6723" ue="27163" us="27161"/><bf gi="6726" ue="27172" us="27165"/><bf gi="6734" ue="27179" us="27176"/><bf gi="6738" ue="27182" us="27182"/><bf gi="6739" ue="27184" us="27184"/><bf gi="6740" ue="27186" us="27186"/><bf gi="6741" ue="27195" us="27188"/><bf gi="6749" ue="27199" us="27197"/><bf gi="6752" ue="27211" us="27204"/><bf gi="6760" ue="27214" us="27214"/><bf gi="6761" ue="27218" us="27216"/><bf gi="6764" ue="27222" us="27221"/><bf gi=!
"6766" ue="27225" us="27224"/><bf gi="6768" ue="27227" us="27227"/><bf gi="6769" ue="27231" us="27231"/><bf gi="6770" ue="27234" us="27233"/><bf gi="6772" ue="27236" us="27236"/><bf gi="6773" ue="27239" us="27238"/><bf gi="6775" ue="27243" us="27242"/><bf gi="6777" ue="27251" us="27249"/><bf gi="6780" ue="27256" us="27256"/><bf gi="6781" ue="27265" us="27262"/><bf gi="6785" ue="27268" us="27267"/><bf gi="6787" ue="27271" us="27270"/><bf gi="6789" ue="27273" us="27273"/><bf gi="6790" ue="27275" us="27275"/><bf gi="6791" ue="27278" us="27277"/><bf gi="6793" ue="27281" us="27280"/><bf gi="6795" ue="27287" us="27287"/><bf gi="6796" ue="27296" us="27291"/><bf gi="6802" ue="27299" us="27298"/><bf gi="6804" ue="27301" us="27301"/><bf gi="6805" ue="27308" us="27306"/><bf gi="6808" ue="27313" us="27310"/><bf gi="6812" ue="27316" us="27315"/><bf gi="6814" ue="27320" us="27320"/><bf gi="6815" ue="27323" us="27323"/><bf gi="6816" ue="27327" us="27325"/><bf gi="6819" ue="27331" us="2732!
9"/><bf gi="6822" ue="27334" us="27334"/><bf gi="6823" ue="27337" us="
27336"/><bf gi="6825" ue="27340" us="27340"/><bf gi="6826" ue="27345" us="27344"/><bf gi="6828" ue="27350" us="27347"/><bf gi="6832" ue="27359" us="27354"/><bf gi="6838" ue="27362" us="27362"/><bf gi="6839" ue="27364" us="27364"/><bf gi="6840" ue="27368" us="27367"/><bf gi="6842" ue="27370" us="27370"/><bf gi="6843" ue="27372" us="27372"/><bf gi="6844" ue="27378" us="27376"/><bf gi="6847" ue="27389" us="27386"/><bf gi="6851" ue="27399" us="27394"/><bf gi="6857" ue="27402" us="27401"/><bf gi="6859" ue="27410" us="27407"/><bf gi="6863" ue="27415" us="27414"/><bf gi="6865" ue="27419" us="27419"/><bf gi="6866" ue="27425" us="27421"/><bf gi="6871" ue="27428" us="27427"/><bf gi="6873" ue="27432" us="27431"/><bf gi="6875" ue="27436" us="27435"/><bf gi="6877" ue="27439" us="27439"/><bf gi="6878" ue="27442" us="27442"/><bf gi="6879" ue="27451" us="27445"/><bf gi="6886" ue="27455" us="27453"/><bf gi="6889" ue="27459" us="27459"/><bf gi="6890" ue="27463" us="27462"/><bf gi="6892" ue="2!
7466" us="27465"/><bf gi="6894" ue="27470" us="27468"/><bf gi="6897" ue="27472" us="27472"/><bf gi="6898" ue="27476" us="27474"/><bf gi="6901" ue="27478" us="27478"/><bf gi="6902" ue="27481" us="27480"/><bf gi="6904" ue="27483" us="27483"/><bf gi="6905" ue="27485" us="27485"/><bf gi="6906" ue="27492" us="27487"/><bf gi="6912" ue="27495" us="27494"/><bf gi="6914" ue="27499" us="27497"/><bf gi="6917" ue="27504" us="27502"/><bf gi="6920" ue="27509" us="27507"/><bf gi="6923" ue="27513" us="27512"/><bf gi="6925" ue="27515" us="27515"/><bf gi="6926" ue="27520" us="27517"/><bf gi="6930" ue="27526" us="27522"/><bf gi="6935" ue="27531" us="27529"/><bf gi="6938" ue="27533" us="27533"/><bf gi="6939" ue="27544" us="27541"/><bf gi="6943" ue="27547" us="27547"/><bf gi="6944" ue="27552" us="27550"/><bf gi="6947" ue="27556" us="27554"/><bf gi="6950" ue="27573" us="27560"/><bf gi="6964" ue="27584" us="27575"/><bf gi="6974" ue="27590" us="27587"/><bf gi="6978" ue="27593" us="27593"/><bf gi="!
6979" ue="27598" us="27595"/><bf gi="6983" ue="27604" us="27602"/><bf
gi="6986" ue="27606" us="27606"/><bf gi="6987" ue="27608" us="27608"/><bf gi="6988" ue="27611" us="27610"/><bf gi="6990" ue="27615" us="27615"/><bf gi="6991" ue="27617" us="27617"/><bf gi="6992" ue="27619" us="27619"/><bf gi="6993" ue="27623" us="27622"/><bf gi="6995" ue="27628" us="27627"/><bf gi="6997" ue="27631" us="27630"/><bf gi="6999" ue="27633" us="27633"/><bf gi="7000" ue="27635" us="27635"/><bf gi="7001" ue="27639" us="27639"/><bf gi="7002" ue="27641" us="27641"/><bf gi="7003" ue="27647" us="27647"/><bf gi="7004" ue="27650" us="27650"/><bf gi="7005" ue="27653" us="27652"/><bf gi="7007" ue="27657" us="27656"/><bf gi="7009" ue="27668" us="27661"/><bf gi="7017" ue="27671" us="27671"/><bf gi="7018" ue="27673" us="27673"/><bf gi="7019" ue="27675" us="27675"/><bf gi="7020" ue="27679" us="27679"/><bf gi="7021" ue="27684" us="27683"/><bf gi="7023" ue="27688" us="27686"/><bf gi="7026" ue="27692" us="27692"/><bf gi="7027" ue="27694" us="27694"/><bf gi="7028" ue="27704" us="27!
699"/><bf gi="7034" ue="27707" us="27706"/><bf gi="7036" ue="27714" us="27710"/><bf gi="7041" ue="27723" us="27722"/><bf gi="7043" ue="27728" us="27725"/><bf gi="7047" ue="27730" us="27730"/><bf gi="7048" ue="27733" us="27732"/><bf gi="7050" ue="27735" us="27735"/><bf gi="7051" ue="27744" us="27737"/><bf gi="7059" ue="27746" us="27746"/><bf gi="7060" ue="27752" us="27751"/><bf gi="7062" ue="27755" us="27754"/><bf gi="7064" ue="27757" us="27757"/><bf gi="7065" ue="27760" us="27759"/><bf gi="7067" ue="27764" us="27762"/><bf gi="7070" ue="27766" us="27766"/><bf gi="7071" ue="27771" us="27768"/><bf gi="7075" ue="27774" us="27773"/><bf gi="7077" ue="27779" us="27777"/><bf gi="7080" ue="27785" us="27781"/><bf gi="7085" ue="27789" us="27788"/><bf gi="7087" ue="27792" us="27792"/><bf gi="7088" ue="27804" us="27794"/><bf gi="7099" ue="27807" us="27807"/><bf gi="7100" ue="27810" us="27809"/><bf gi="7102" ue="27819" us="27819"/><bf gi="7103" ue="27822" us="27822"/><bf gi="7104" ue="27!
828" us="27824"/><bf gi="7109" ue="27839" us="27832"/><bf gi="7117" ue
="27842" us="27841"/><bf gi="7119" ue="27846" us="27844"/><bf gi="7122" ue="27850" us="27849"/><bf gi="7124" ue="27853" us="27852"/><bf gi="7126" ue="27863" us="27855"/><bf gi="7135" ue="27869" us="27865"/><bf gi="7140" ue="27875" us="27872"/><bf gi="7144" ue="27877" us="27877"/><bf gi="7145" ue="27884" us="27879"/><bf gi="7151" ue="27892" us="27886"/><bf gi="7158" ue="27908" us="27908"/><bf gi="7159" ue="27911" us="27911"/><bf gi="7160" ue="27916" us="27914"/><bf gi="7163" ue="27919" us="27918"/><bf gi="7165" ue="27923" us="27921"/><bf gi="7168" ue="27927" us="27927"/><bf gi="7169" ue="27931" us="27929"/><bf gi="7172" ue="27935" us="27934"/><bf gi="7174" ue="27947" us="27941"/><bf gi="7181" ue="27951" us="27950"/><bf gi="7183" ue="27955" us="27953"/><bf gi="7186" ue="27958" us="27957"/><bf gi="7188" ue="27961" us="27960"/><bf gi="7190" ue="27967" us="27963"/><bf gi="7195" ue="27969" us="27969"/><bf gi="7196" ue="27973" us="27972"/><bf gi="7198" ue="27991" us="27991"/><bf gi!
="7199" ue="27994" us="27993"/><bf gi="7201" ue="27996" us="27996"/><bf gi="7202" ue="27999" us="27998"/><bf gi="7204" ue="28001" us="28001"/><bf gi="7205" ue="28007" us="28003"/><bf gi="7210" ue="28010" us="28009"/><bf gi="7212" ue="28012" us="28012"/><bf gi="7213" ue="28016" us="28014"/><bf gi="7216" ue="28020" us="28020"/><bf gi="7217" ue="28025" us="28023"/><bf gi="7220" ue="28028" us="28028"/><bf gi="7221" ue="28034" us="28034"/><bf gi="7222" ue="28037" us="28037"/><bf gi="7223" ue="28040" us="28039"/><bf gi="7225" ue="28044" us="28044"/><bf gi="7226" ue="28046" us="28046"/><bf gi="7227" ue="28057" us="28049"/><bf gi="7236" ue="28060" us="28059"/><bf gi="7238" ue="28074" us="28074"/><bf gi="7239" ue="28076" us="28076"/><bf gi="7240" ue="28079" us="28079"/><bf gi="7241" ue="28082" us="28082"/><bf gi="7242" ue="28085" us="28084"/><bf gi="7244" ue="28089" us="28087"/><bf gi="7247" ue="28093" us="28092"/><bf gi="7249" ue="28096" us="28095"/><bf gi="7251" ue="28104" us="281!
00"/><bf gi="7256" ue="28108" us="28106"/><bf gi="7259" ue="28111" us=
"28110"/><bf gi="7261" ue="28114" us="28113"/><bf gi="7263" ue="28118" us="28117"/><bf gi="7265" ue="28121" us="28120"/><bf gi="7267" ue="28123" us="28123"/><bf gi="7268" ue="28130" us="28125"/><bf gi="7274" ue="28134" us="28132"/><bf gi="7277" ue="28140" us="28136"/><bf gi="7282" ue="28156" us="28142"/><bf gi="7297" ue="28160" us="28160"/><bf gi="7298" ue="28165" us="28164"/><bf gi="7300" ue="28171" us="28167"/><bf gi="7305" ue="28179" us="28179"/><bf gi="7306" ue="28181" us="28181"/><bf gi="7307" ue="28187" us="28185"/><bf gi="7310" ue="28199" us="28189"/><bf gi="7321" ue="28201" us="28201"/><bf gi="7322" ue="28207" us="28203"/><bf gi="7327" ue="28210" us="28210"/><bf gi="7328" ue="28214" us="28214"/><bf gi="7329" ue="28220" us="28216"/><bf gi="7334" ue="28222" us="28222"/><bf gi="7335" ue="28229" us="28227"/><bf gi="7338" ue="28235" us="28232"/><bf gi="7342" ue="28239" us="28237"/><bf gi="7345" ue="28244" us="28241"/><bf gi="7349" ue="28248" us="28246"/><bf gi="7352" ue="!
28255" us="28251"/><bf gi="7357" ue="28259" us="28258"/><bf gi="7359" ue="28264" us="28263"/><bf gi="7361" ue="28267" us="28267"/><bf gi="7362" ue="28271" us="28270"/><bf gi="7364" ue="28275" us="28274"/><bf gi="7366" ue="28278" us="28278"/><bf gi="7367" ue="28283" us="28283"/><bf gi="7368" ue="28288" us="28285"/><bf gi="7372" ue="28290" us="28290"/><bf gi="7373" ue="28301" us="28300"/><bf gi="7375" ue="28304" us="28303"/><bf gi="7377" ue="28307" us="28307"/><bf gi="7378" ue="28310" us="28310"/><bf gi="7379" ue="28313" us="28312"/><bf gi="7381" ue="28317" us="28316"/><bf gi="7383" ue="28320" us="28319"/><bf gi="7385" ue="28322" us="28322"/><bf gi="7386" ue="28325" us="28325"/><bf gi="7387" ue="28327" us="28327"/><bf gi="7388" ue="28330" us="28330"/><bf gi="7389" ue="28335" us="28333"/><bf gi="7392" ue="28339" us="28337"/><bf gi="7395" ue="28343" us="28342"/><bf gi="7397" ue="28347" us="28346"/><bf gi="7399" ue="28349" us="28349"/><bf gi="7400" ue="28357" us="28351"/><bf gi=!
"7407" ue="28367" us="28359"/><bf gi="7416" ue="28369" us="28369"/><bf
gi="7417" ue="28373" us="28371"/><bf gi="7420" ue="28382" us="28381"/><bf gi="7422" ue="28399" us="28395"/><bf gi="7427" ue="28402" us="28402"/><bf gi="7428" ue="28404" us="28404"/><bf gi="7429" ue="28409" us="28407"/><bf gi="7432" ue="28411" us="28411"/><bf gi="7433" ue="28415" us="28413"/><bf gi="7436" ue="28418" us="28417"/><bf gi="7438" ue="28420" us="28420"/><bf gi="7439" ue="28422" us="28422"/><bf gi="7440" ue="28426" us="28424"/><bf gi="7443" ue="28429" us="28428"/><bf gi="7445" ue="28431" us="28431"/><bf gi="7446" ue="28433" us="28433"/><bf gi="7447" ue="28438" us="28435"/><bf gi="7451" ue="28440" us="28440"/><bf gi="7452" ue="28443" us="28442"/><bf gi="7454" ue="28448" us="28448"/><bf gi="7455" ue="28451" us="28450"/><bf gi="7457" ue="28454" us="28454"/><bf gi="7458" ue="28461" us="28457"/><bf gi="7463" ue="28467" us="28463"/><bf gi="7468" ue="28470" us="28470"/><bf gi="7469" ue="28472" us="28472"/><bf gi="7470" ue="28476" us="28475"/><bf gi="7472" ue="28479" us="2!
8478"/><bf gi="7474" ue="28481" us="28481"/><bf gi="7475" ue="28485" us="28485"/><bf gi="7476" ue="28495" us="28495"/><bf gi="7477" ue="28500" us="28497"/><bf gi="7481" ue="28511" us="28503"/><bf gi="7490" ue="28514" us="28513"/><bf gi="7492" ue="28516" us="28516"/><bf gi="7493" ue="28518" us="28518"/><bf gi="7494" ue="28520" us="28520"/><bf gi="7495" ue="28528" us="28524"/><bf gi="7500" ue="28532" us="28532"/><bf gi="7501" ue="28536" us="28536"/><bf gi="7502" ue="28538" us="28538"/><bf gi="7503" ue="28542" us="28540"/><bf gi="7506" ue="28548" us="28544"/><bf gi="7511" ue="28552" us="28550"/><bf gi="7514" ue="28558" us="28555"/><bf gi="7518" ue="28564" us="28560"/><bf gi="7523" ue="28567" us="28566"/><bf gi="7525" ue="28570" us="28570"/><bf gi="7526" ue="28577" us="28575"/><bf gi="7529" ue="28584" us="28579"/><bf gi="7535" ue="28586" us="28586"/><bf gi="7536" ue="28593" us="28590"/><bf gi="7540" ue="28595" us="28595"/><bf gi="7541" ue="28598" us="28597"/><bf gi="7543" ue="2!
8601" us="28601"/><bf gi="7544" ue="28604" us="28604"/><bf gi="7545" u
e="28611" us="28608"/><bf gi="7549" ue="28616" us="28613"/><bf gi="7553" ue="28618" us="28618"/><bf gi="7554" ue="28629" us="28628"/><bf gi="7556" ue="28632" us="28632"/><bf gi="7557" ue="28635" us="28634"/><bf gi="7559" ue="28641" us="28638"/><bf gi="7563" ue="28644" us="28644"/><bf gi="7564" ue="28649" us="28648"/><bf gi="7566" ue="28652" us="28651"/><bf gi="7568" ue="28657" us="28654"/><bf gi="7572" ue="28659" us="28659"/><bf gi="7573" ue="28662" us="28661"/><bf gi="7575" ue="28666" us="28665"/><bf gi="7577" ue="28670" us="28668"/><bf gi="7580" ue="28673" us="28672"/><bf gi="7582" ue="28679" us="28677"/><bf gi="7585" ue="28681" us="28681"/><bf gi="7586" ue="28683" us="28683"/><bf gi="7587" ue="28685" us="28685"/><bf gi="7588" ue="28687" us="28687"/><bf gi="7589" ue="28689" us="28689"/><bf gi="7590" ue="28693" us="28693"/><bf gi="7591" ue="28696" us="28695"/><bf gi="7593" ue="28699" us="28698"/><bf gi="7595" ue="28704" us="28701"/><bf gi="7599" ue="28707" us="28707"/><bf g!
i="7600" ue="28712" us="28710"/><bf gi="7603" ue="28716" us="28716"/><bf gi="7604" ue="28720" us="28719"/><bf gi="7606" ue="28722" us="28722"/><bf gi="7607" ue="28724" us="28724"/><bf gi="7608" ue="28727" us="28727"/><bf gi="7609" ue="28729" us="28729"/><bf gi="7610" ue="28732" us="28732"/><bf gi="7611" ue="28734" us="28734"/><bf gi="7612" ue="28740" us="28739"/><bf gi="7614" ue="28748" us="28744"/><bf gi="7619" ue="28750" us="28750"/><bf gi="7620" ue="28753" us="28753"/><bf gi="7621" ue="28757" us="28756"/><bf gi="7623" ue="28760" us="28760"/><bf gi="7624" ue="28766" us="28765"/><bf gi="7626" ue="28773" us="28771"/><bf gi="7629" ue="28780" us="28779"/><bf gi="7631" ue="28784" us="28782"/><bf gi="7634" ue="28790" us="28789"/><bf gi="7636" ue="28792" us="28792"/><bf gi="7637" ue="28798" us="28796"/><bf gi="7640" ue="28801" us="28801"/><bf gi="7641" ue="28806" us="28805"/><bf gi="7643" ue="28810" us="28809"/><bf gi="7645" ue="28814" us="28814"/><bf gi="7646" ue="28818" us="28!
818"/><bf gi="7647" ue="28825" us="28820"/><bf gi="7653" ue="28827" us
="28827"/><bf gi="7654" ue="28836" us="28836"/><bf gi="7655" ue="28849" us="28843"/><bf gi="7662" ue="28852" us="28851"/><bf gi="7664" ue="28859" us="28855"/><bf gi="7669" ue="28872" us="28872"/><bf gi="7670" ue="28875" us="28874"/><bf gi="7672" ue="28879" us="28879"/><bf gi="7673" ue="28881" us="28881"/><bf gi="7674" ue="28886" us="28883"/><bf gi="7678" ue="28889" us="28888"/><bf gi="7680" ue="28893" us="28892"/><bf gi="7682" ue="28895" us="28895"/><bf gi="7683" ue="28900" us="28900"/><bf gi="7684" ue="28913" us="28913"/><bf gi="7685" ue="28922" us="28921"/><bf gi="7687" ue="28925" us="28925"/><bf gi="7688" ue="28935" us="28931"/><bf gi="7693" ue="28937" us="28937"/><bf gi="7694" ue="28940" us="28939"/><bf gi="7696" ue="28943" us="28943"/><bf gi="7697" ue="28948" us="28948"/><bf gi="7698" ue="28954" us="28953"/><bf gi="7700" ue="28956" us="28956"/><bf gi="7701" ue="28958" us="28958"/><bf gi="7702" ue="28961" us="28960"/><bf gi="7704" ue="28966" us="28966"/><bf gi="7705" ue=!
"28971" us="28971"/><bf gi="7706" ue="28973" us="28973"/><bf gi="7707" ue="28977" us="28975"/><bf gi="7710" ue="28982" us="28982"/><bf gi="7711" ue="28984" us="28984"/><bf gi="7712" ue="28988" us="28988"/><bf gi="7713" ue="28993" us="28993"/><bf gi="7714" ue="28999" us="28997"/><bf gi="7717" ue="29004" us="29001"/><bf gi="7721" ue="29006" us="29006"/><bf gi="7722" ue="29008" us="29008"/><bf gi="7723" ue="29010" us="29010"/><bf gi="7724" ue="29015" us="29013"/><bf gi="7727" ue="29018" us="29017"/><bf gi="7729" ue="29020" us="29020"/><bf gi="7730" ue="29022" us="29022"/><bf gi="7731" ue="29024" us="29024"/><bf gi="7732" ue="29026" us="29026"/><bf gi="7733" ue="29033" us="29028"/><bf gi="7739" ue="29036" us="29036"/><bf gi="7740" ue="29038" us="29038"/><bf gi="7741" ue="29049" us="29049"/><bf gi="7742" ue="29053" us="29053"/><bf gi="7743" ue="29056" us="29056"/><bf gi="7744" ue="29061" us="29060"/><bf gi="7746" ue="29064" us="29063"/><bf gi="7748" ue="29066" us="29066"/><bf gi!
="7749" ue="29068" us="29068"/><bf gi="7750" ue="29071" us="29071"/><b
f gi="7751" ue="29074" us="29074"/><bf gi="7752" ue="29077" us="29076"/><bf gi="7754" ue="29083" us="29081"/><bf gi="7757" ue="29088" us="29087"/><bf gi="7759" ue="29090" us="29090"/><bf gi="7760" ue="29096" us="29096"/><bf gi="7761" ue="29100" us="29100"/><bf gi="7762" ue="29107" us="29103"/><bf gi="7767" ue="29114" us="29113"/><bf gi="7769" ue="29121" us="29118"/><bf gi="7773" ue="29124" us="29123"/><bf gi="7775" ue="29129" us="29128"/><bf gi="7777" ue="29132" us="29131"/><bf gi="7779" ue="29134" us="29134"/><bf gi="7780" ue="29136" us="29136"/><bf gi="7781" ue="29143" us="29138"/><bf gi="7787" ue="29146" us="29145"/><bf gi="7789" ue="29148" us="29148"/><bf gi="7790" ue="29152" us="29151"/><bf gi="7792" ue="29159" us="29157"/><bf gi="7795" ue="29166" us="29164"/><bf gi="7798" ue="29173" us="29173"/><bf gi="7799" ue="29177" us="29176"/><bf gi="7801" ue="29180" us="29179"/><bf gi="7803" ue="29184" us="29182"/><bf gi="7806" ue="29193" us="29190"/><bf gi="7810" ue="29197" us="!
29197"/><bf gi="7811" ue="29200" us="29200"/><bf gi="7812" ue="29203" us="29203"/><bf gi="7813" ue="29207" us="29207"/><bf gi="7814" ue="29211" us="29210"/><bf gi="7816" ue="29213" us="29213"/><bf gi="7817" ue="29215" us="29215"/><bf gi="7818" ue="29220" us="29220"/><bf gi="7819" ue="29224" us="29224"/><bf gi="7820" ue="29229" us="29226"/><bf gi="7824" ue="29232" us="29231"/><bf gi="7826" ue="29234" us="29234"/><bf gi="7827" ue="29238" us="29236"/><bf gi="7830" ue="29251" us="29240"/><bf gi="7842" ue="29256" us="29253"/><bf gi="7846" ue="29260" us="29259"/><bf gi="7848" ue="29264" us="29262"/><bf gi="7851" ue="29267" us="29266"/><bf gi="7853" ue="29270" us="29269"/><bf gi="7855" ue="29283" us="29272"/><bf gi="7867" ue="29289" us="29287"/><bf gi="7870" ue="29291" us="29291"/><bf gi="7871" ue="29295" us="29294"/><bf gi="7873" ue="29298" us="29297"/><bf gi="7875" ue="29300" us="29300"/><bf gi="7876" ue="29305" us="29303"/><bf gi="7879" ue="29314" us="29307"/><bf gi="7887" ue="!
29316" us="29316"/><bf gi="7888" ue="29319" us="29319"/><bf gi="7889"
ue="29321" us="29321"/><bf gi="7890" ue="29326" us="29325"/><bf gi="7892" ue="29331" us="29330"/><bf gi="7894" ue="29334" us="29334"/><bf gi="7895" ue="29339" us="29339"/><bf gi="7896" ue="29344" us="29344"/><bf gi="7897" ue="29346" us="29346"/><bf gi="7898" ue="29352" us="29351"/><bf gi="7900" ue="29359" us="29356"/><bf gi="7904" ue="29362" us="29361"/><bf gi="7906" ue="29364" us="29364"/><bf gi="7907" ue="29366" us="29366"/><bf gi="7908" ue="29369" us="29369"/><bf gi="7909" ue="29374" us="29374"/><bf gi="7910" ue="29380" us="29377"/><bf gi="7914" ue="29383" us="29382"/><bf gi="7916" ue="29385" us="29385"/><bf gi="7917" ue="29388" us="29388"/><bf gi="7918" ue="29390" us="29390"/><bf gi="7919" ue="29392" us="29392"/><bf gi="7920" ue="29394" us="29394"/><bf gi="7921" ue="29401" us="29397"/><bf gi="7926" ue="29403" us="29403"/><bf gi="7927" ue="29410" us="29407"/><bf gi="7931" ue="29413" us="29413"/><bf gi="7932" ue="29417" us="29417"/><bf gi="7933" ue="29421" us="29420"/><bf !
gi="7935" ue="29428" us="29427"/><bf gi="7937" ue="29438" us="29431"/><bf gi="7945" ue="29442" us="29442"/><bf gi="7946" ue="29445" us="29444"/><bf gi="7948" ue="29447" us="29447"/><bf gi="7949" ue="29451" us="29450"/><bf gi="7951" ue="29453" us="29453"/><bf gi="7952" ue="29459" us="29458"/><bf gi="7954" ue="29465" us="29462"/><bf gi="7958" ue="29471" us="29467"/><bf gi="7963" ue="29474" us="29474"/><bf gi="7964" ue="29477" us="29476"/><bf gi="7966" ue="29484" us="29479"/><bf gi="7972" ue="29487" us="29486"/><bf gi="7974" ue="29490" us="29489"/><bf gi="7976" ue="29495" us="29492"/><bf gi="7980" ue="29499" us="29498"/><bf gi="7982" ue="29503" us="29501"/><bf gi="7985" ue="29509" us="29507"/><bf gi="7988" ue="29520" us="29517"/><bf gi="7992" ue="29522" us="29522"/><bf gi="7993" ue="29528" us="29526"/><bf gi="7996" ue="29536" us="29533"/><bf gi="8000" ue="29539" us="29539"/><bf gi="8001" ue="29548" us="29542"/><bf gi="8008" ue="29554" us="29550"/><bf gi="8013" ue="29557" us="2!
9557"/><bf gi="8014" ue="29564" us="29559"/><bf gi="8020" ue="29569" u
s="29568"/><bf gi="8022" ue="29575" us="29571"/><bf gi="8027" ue="29577" us="29577"/><bf gi="8028" ue="29579" us="29579"/><bf gi="8029" ue="29582" us="29582"/><bf gi="8030" ue="29584" us="29584"/><bf gi="8031" ue="29587" us="29587"/><bf gi="8032" ue="29592" us="29589"/><bf gi="8036" ue="29596" us="29596"/><bf gi="8037" ue="29600" us="29598"/><bf gi="8040" ue="29602" us="29602"/><bf gi="8041" ue="29606" us="29605"/><bf gi="8043" ue="29611" us="29609"/><bf gi="8046" ue="29613" us="29613"/><bf gi="8047" ue="29619" us="29618"/><bf gi="8049" ue="29621" us="29621"/><bf gi="8050" ue="29623" us="29623"/><bf gi="8051" ue="29625" us="29625"/><bf gi="8052" ue="29629" us="29627"/><bf gi="8055" ue="29632" us="29631"/><bf gi="8057" ue="29634" us="29634"/><bf gi="8058" ue="29638" us="29637"/><bf gi="8060" ue="29647" us="29640"/><bf gi="8068" ue="29651" us="29650"/><bf gi="8070" ue="29654" us="29654"/><bf gi="8071" ue="29657" us="29657"/><bf gi="8072" ue="29662" us="29661"/><bf gi="8074" ue!
="29665" us="29664"/><bf gi="8076" ue="29667" us="29667"/><bf gi="8077" ue="29671" us="29669"/><bf gi="8080" ue="29674" us="29673"/><bf gi="8082" ue="29678" us="29677"/><bf gi="8084" ue="29681" us="29681"/><bf gi="8085" ue="29685" us="29684"/><bf gi="8087" ue="29691" us="29687"/><bf gi="8092" ue="29697" us="29693"/><bf gi="8097" ue="29703" us="29699"/><bf gi="8102" ue="29706" us="29705"/><bf gi="8104" ue="29713" us="29713"/><bf gi="8105" ue="29723" us="29722"/><bf gi="8107" ue="29730" us="29730"/><bf gi="8108" ue="29734" us="29732"/><bf gi="8111" ue="29750" us="29736"/><bf gi="8126" ue="29754" us="29753"/><bf gi="8128" ue="29761" us="29759"/><bf gi="8131" ue="29764" us="29763"/><bf gi="8133" ue="29767" us="29766"/><bf gi="8135" ue="29771" us="29771"/><bf gi="8136" ue="29773" us="29773"/><bf gi="8137" ue="29778" us="29777"/><bf gi="8139" ue="29781" us="29781"/><bf gi="8140" ue="29783" us="29783"/><bf gi="8141" ue="29792" us="29785"/><bf gi="8149" ue="29796" us="29794"/><bf g!
i="8152" ue="29803" us="29798"/><bf gi="8158" ue="29811" us="29805"/><
bf gi="8165" ue="29814" us="29814"/><bf gi="8166" ue="29822" us="29822"/><bf gi="8167" ue="29825" us="29824"/><bf gi="8169" ue="29827" us="29827"/><bf gi="8170" ue="29831" us="29829"/><bf gi="8173" ue="29833" us="29833"/><bf gi="8174" ue="29835" us="29835"/><bf gi="8175" ue="29842" us="29839"/><bf gi="8179" ue="29850" us="29848"/><bf gi="8182" ue="29852" us="29852"/><bf gi="8183" ue="29859" us="29854"/><bf gi="8189" ue="29867" us="29862"/><bf gi="8195" ue="29874" us="29870"/><bf gi="8200" ue="29877" us="29877"/><bf gi="8201" ue="29881" us="29881"/><bf gi="8202" ue="29883" us="29883"/><bf gi="8203" ue="29885" us="29885"/><bf gi="8204" ue="29887" us="29887"/><bf gi="8205" ue="29898" us="29896"/><bf gi="8208" ue="29900" us="29900"/><bf gi="8209" ue="29904" us="29903"/><bf gi="8211" ue="29908" us="29907"/><bf gi="8213" ue="29912" us="29912"/><bf gi="8214" ue="29916" us="29914"/><bf gi="8217" ue="29920" us="29918"/><bf gi="8220" ue="29924" us="29922"/><bf gi="8223" ue="29931" us=!
"29926"/><bf gi="8229" ue="29938" us="29934"/><bf gi="8234" ue="29940" us="29940"/><bf gi="8235" ue="29944" us="29942"/><bf gi="8238" ue="29948" us="29946"/><bf gi="8241" ue="29951" us="29951"/><bf gi="8242" ue="29953" us="29953"/><bf gi="8243" ue="29958" us="29955"/><bf gi="8247" ue="29966" us="29964"/><bf gi="8250" ue="29971" us="29969"/><bf gi="8253" ue="29976" us="29973"/><bf gi="8257" ue="29978" us="29978"/><bf gi="8258" ue="29980" us="29980"/><bf gi="8259" ue="29985" us="29982"/><bf gi="8263" ue="29996" us="29987"/><bf gi="8273" ue="30003" us="29999"/><bf gi="8278" ue="30016" us="30006"/><bf gi="8289" ue="30020" us="30019"/><bf gi="8291" ue="30034" us="30022"/><bf gi="8304" ue="30036" us="30036"/><bf gi="8305" ue="30039" us="30039"/><bf gi="8306" ue="30050" us="30041"/><bf gi="8316" ue="30055" us="30052"/><bf gi="8320" ue="30059" us="30057"/><bf gi="8323" ue="30061" us="30061"/><bf gi="8324" ue="30065" us="30063"/><bf gi="8327" ue="30068" us="30067"/><bf gi="8329" ue=!
"30079" us="30070"/><bf gi="8339" ue="30082" us="30081"/><bf gi="8341"
ue="30087" us="30085"/><bf gi="8344" ue="30091" us="30089"/><bf gi="8347" ue="30101" us="30094"/><bf gi="8355" ue="30106" us="30105"/><bf gi="8357" ue="30109" us="30108"/><bf gi="8359" ue="30117" us="30114"/><bf gi="8363" ue="30123" us="30123"/><bf gi="8364" ue="30133" us="30129"/><bf gi="8369" ue="30138" us="30136"/><bf gi="8372" ue="30151" us="30140"/><bf gi="8384" ue="30154" us="30154"/><bf gi="8385" ue="30159" us="30156"/><bf gi="8389" ue="30162" us="30162"/><bf gi="8390" ue="30165" us="30164"/><bf gi="8392" ue="30169" us="30167"/><bf gi="8395" ue="30172" us="30171"/><bf gi="8397" ue="30180" us="30174"/><bf gi="8404" ue="30183" us="30183"/><bf gi="8405" ue="30185" us="30185"/><bf gi="8406" ue="30188" us="30188"/><bf gi="8407" ue="30196" us="30190"/><bf gi="8414" ue="30202" us="30201"/><bf gi="8416" ue="30204" us="30204"/><bf gi="8417" ue="30212" us="30206"/><bf gi="8424" ue="30221" us="30215"/><bf gi="8431" ue="30223" us="30223"/><bf gi="8432" ue="30227" us="30226"/><bf!
gi="8434" ue="30230" us="30229"/><bf gi="8436" ue="30233" us="30233"/><bf gi="8437" ue="30247" us="30235"/><bf gi="8450" ue="30249" us="30249"/><bf gi="8451" ue="30253" us="30253"/><bf gi="8452" ue="30256" us="30256"/><bf gi="8453" ue="30261" us="30258"/><bf gi="8457" ue="30268" us="30264"/><bf gi="8462" ue="30284" us="30272"/><bf gi="8475" ue="30290" us="30290"/><bf gi="8476" ue="30294" us="30293"/><bf gi="8478" ue="30297" us="30296"/><bf gi="8480" ue="30300" us="30300"/><bf gi="8481" ue="30303" us="30303"/><bf gi="8482" ue="30306" us="30305"/><bf gi="8484" ue="30309" us="30308"/><bf gi="8486" ue="30314" us="30311"/><bf gi="8490" ue="30322" us="30316"/><bf gi="8497" ue="30324" us="30324"/><bf gi="8498" ue="30326" us="30326"/><bf gi="8499" ue="30328" us="30328"/><bf gi="8500" ue="30334" us="30330"/><bf gi="8505" ue="30344" us="30336"/><bf gi="8514" ue="30350" us="30347"/><bf gi="8518" ue="30352" us="30352"/><bf gi="8519" ue="30355" us="30355"/><bf gi="8520" ue="30358" us="!
30357"/><bf gi="8522" ue="30368" us="30361"/><bf gi="8530" ue="30376"
us="30370"/><bf gi="8537" ue="30378" us="30378"/><bf gi="8538" ue="30382" us="30381"/><bf gi="8540" ue="30384" us="30384"/><bf gi="8541" ue="30388" us="30388"/><bf gi="8542" ue="30394" us="30391"/><bf gi="8546" ue="30397" us="30397"/><bf gi="8547" ue="30399" us="30399"/><bf gi="8548" ue="30403" us="30401"/><bf gi="8551" ue="30406" us="30405"/><bf gi="8553" ue="30414" us="30408"/><bf gi="8560" ue="30418" us="30418"/><bf gi="8561" ue="30420" us="30420"/><bf gi="8562" ue="30423" us="30422"/><bf gi="8564" ue="30425" us="30425"/><bf gi="8565" ue="30428" us="30427"/><bf gi="8567" ue="30433" us="30430"/><bf gi="8571" ue="30440" us="30435"/><bf gi="8577" ue="30442" us="30442"/><bf gi="8578" ue="30444" us="30444"/><bf gi="8579" ue="30446" us="30446"/><bf gi="8580" ue="30450" us="30448"/><bf gi="8583" ue="30452" us="30452"/><bf gi="8584" ue="30454" us="30454"/><bf gi="8585" ue="30457" us="30456"/><bf gi="8587" ue="30460" us="30459"/><bf gi="8589" ue="30462" us="30462"/><bf gi="8590" u!
e="30465" us="30464"/><bf gi="8592" ue="30468" us="30468"/><bf gi="8593" ue="30476" us="30470"/><bf gi="8600" ue="30478" us="30478"/><bf gi="8601" ue="30482" us="30482"/><bf gi="8602" ue="30485" us="30484"/><bf gi="8604" ue="30487" us="30487"/><bf gi="8605" ue="30492" us="30489"/><bf gi="8609" ue="30496" us="30494"/><bf gi="8612" ue="30498" us="30498"/><bf gi="8613" ue="30502" us="30500"/><bf gi="8616" ue="30505" us="30504"/><bf gi="8618" ue="30511" us="30509"/><bf gi="8621" ue="30522" us="30516"/><bf gi="8628" ue="30526" us="30524"/><bf gi="8631" ue="30528" us="30528"/><bf gi="8632" ue="30530" us="30530"/><bf gi="8633" ue="30535" us="30533"/><bf gi="8636" ue="30538" us="30538"/><bf gi="8637" ue="30543" us="30541"/><bf gi="8640" ue="30546" us="30546"/><bf gi="8641" ue="30551" us="30550"/><bf gi="8643" ue="30556" us="30554"/><bf gi="8646" ue="30568" us="30558"/><bf gi="8657" ue="30572" us="30570"/><bf gi="8660" ue="30576" us="30576"/><bf gi="8661" ue="30580" us="30578"/><bf !
gi="8664" ue="30586" us="30585"/><bf gi="8666" ue="30592" us="30589"/>
<bf gi="8670" ue="30596" us="30596"/><bf gi="8671" ue="30606" us="30603"/><bf gi="8675" ue="30609" us="30609"/><bf gi="8676" ue="30614" us="30612"/><bf gi="8679" ue="30618" us="30618"/><bf gi="8680" ue="30624" us="30622"/><bf gi="8683" ue="30626" us="30626"/><bf gi="8684" ue="30629" us="30629"/><bf gi="8685" ue="30631" us="30631"/><bf gi="8686" ue="30634" us="30634"/><bf gi="8687" ue="30641" us="30636"/><bf gi="8693" ue="30643" us="30643"/><bf gi="8694" ue="30646" us="30645"/><bf gi="8696" ue="30649" us="30649"/><bf gi="8697" ue="30655" us="30651"/><bf gi="8702" ue="30659" us="30659"/><bf gi="8703" ue="30663" us="30663"/><bf gi="8704" ue="30665" us="30665"/><bf gi="8705" ue="30669" us="30669"/><bf gi="8706" ue="30674" us="30673"/><bf gi="8708" ue="30677" us="30677"/><bf gi="8709" ue="30679" us="30679"/><bf gi="8710" ue="30684" us="30681"/><bf gi="8714" ue="30688" us="30686"/><bf gi="8717" ue="30695" us="30690"/><bf gi="8723" ue="30698" us="30697"/><bf gi="8725" ue="30705" us!
="30700"/><bf gi="8731" ue="30708" us="30707"/><bf gi="8733" ue="30712" us="30712"/><bf gi="8734" ue="30716" us="30715"/><bf gi="8736" ue="30722" us="30722"/><bf gi="8737" ue="30726" us="30725"/><bf gi="8739" ue="30729" us="30729"/><bf gi="8740" ue="30734" us="30732"/><bf gi="8743" ue="30738" us="30737"/><bf gi="8745" ue="30741" us="30740"/><bf gi="8747" ue="30749" us="30749"/><bf gi="8748" ue="30755" us="30752"/><bf gi="8752" ue="30759" us="30757"/><bf gi="8755" ue="30766" us="30765"/><bf gi="8757" ue="30768" us="30768"/><bf gi="8758" ue="30770" us="30770"/><bf gi="8759" ue="30773" us="30772"/><bf gi="8761" ue="30775" us="30775"/><bf gi="8762" ue="30778" us="30778"/><bf gi="8763" ue="30783" us="30783"/><bf gi="8764" ue="30789" us="30787"/><bf gi="8767" ue="30792" us="30791"/><bf gi="8769" ue="30796" us="30796"/><bf gi="8770" ue="30798" us="30798"/><bf gi="8771" ue="30802" us="30802"/><bf gi="8772" ue="30814" us="30812"/><bf gi="8775" ue="30817" us="30816"/><bf gi="8777" ue!
="30820" us="30819"/><bf gi="8779" ue="30824" us="30824"/><bf gi="8780
" ue="30828" us="30826"/><bf gi="8783" ue="30831" us="30830"/><bf gi="8785" ue="30834" us="30834"/><bf gi="8786" ue="30836" us="30836"/><bf gi="8787" ue="30842" us="30842"/><bf gi="8788" ue="30844" us="30844"/><bf gi="8789" ue="30846" us="30846"/><bf gi="8790" ue="30849" us="30849"/><bf gi="8791" ue="30855" us="30854"/><bf gi="8793" ue="30858" us="30858"/><bf gi="8794" ue="30863" us="30860"/><bf gi="8798" ue="30865" us="30865"/><bf gi="8799" ue="30869" us="30867"/><bf gi="8802" ue="30872" us="30871"/><bf gi="8804" ue="30874" us="30874"/><bf gi="8805" ue="30879" us="30877"/><bf gi="8808" ue="30881" us="30881"/><bf gi="8809" ue="30884" us="30883"/><bf gi="8811" ue="30890" us="30887"/><bf gi="8815" ue="30893" us="30892"/><bf gi="8817" ue="30899" us="30895"/><bf gi="8822" ue="30901" us="30901"/><bf gi="8823" ue="30911" us="30906"/><bf gi="8829" ue="30913" us="30913"/><bf gi="8830" ue="30924" us="30917"/><bf gi="8838" ue="30926" us="30926"/><bf gi="8839" ue="30934" us="30928"/><b!
f gi="8846" ue="30939" us="30938"/><bf gi="8848" ue="30945" us="30943"/><bf gi="8851" ue="30948" us="30948"/><bf gi="8852" ue="30952" us="30950"/><bf gi="8855" ue="30954" us="30954"/><bf gi="8856" ue="30956" us="30956"/><bf gi="8857" ue="30959" us="30959"/><bf gi="8858" ue="30964" us="30962"/><bf gi="8861" ue="30967" us="30966"/><bf gi="8863" ue="30971" us="30970"/><bf gi="8865" ue="30973" us="30973"/><bf gi="8866" ue="30977" us="30975"/><bf gi="8869" ue="30983" us="30982"/><bf gi="8871" ue="30988" us="30988"/><bf gi="8872" ue="30990" us="30990"/><bf gi="8873" ue="30994" us="30992"/><bf gi="8876" ue="31002" us="31001"/><bf gi="8878" ue="31004" us="31004"/><bf gi="8879" ue="31008" us="31006"/><bf gi="8882" ue="31015" us="31013"/><bf gi="8885" ue="31021" us="31017"/><bf gi="8890" ue="31025" us="31024"/><bf gi="8892" ue="31029" us="31028"/><bf gi="8894" ue="31041" us="31034"/><bf gi="8902" ue="31051" us="31044"/><bf gi="8910" ue="31057" us="31055"/><bf gi="8913" ue="31064" us=!
"31059"/><bf gi="8919" ue="31072" us="31066"/><bf gi="8926" ue="31074"
us="31074"/><bf gi="8927" ue="31077" us="31077"/><bf gi="8928" ue="31081" us="31079"/><bf gi="8931" ue="31083" us="31083"/><bf gi="8932" ue="31085" us="31085"/><bf gi="8933" ue="31090" us="31090"/><bf gi="8934" ue="31095" us="31095"/><bf gi="8935" ue="31100" us="31097"/><bf gi="8939" ue="31105" us="31102"/><bf gi="8943" ue="31109" us="31108"/><bf gi="8945" ue="31119" us="31114"/><bf gi="8951" ue="31121" us="31121"/><bf gi="8952" ue="31126" us="31123"/><bf gi="8956" ue="31128" us="31128"/><bf gi="8957" ue="31133" us="31131"/><bf gi="8960" ue="31137" us="31137"/><bf gi="8961" ue="31147" us="31142"/><bf gi="8967" ue="31153" us="31150"/><bf gi="8971" ue="31156" us="31155"/><bf gi="8973" ue="31163" us="31160"/><bf gi="8977" ue="31170" us="31165"/><bf gi="8983" ue="31172" us="31172"/><bf gi="8984" ue="31179" us="31175"/><bf gi="8989" ue="31183" us="31183"/><bf gi="8990" ue="31186" us="31185"/><bf gi="8992" ue="31190" us="31188"/><bf gi="8995" ue="31192" us="31192"/><bf gi="8996" !
ue="31194" us="31194"/><bf gi="8997" ue="31207" us="31197"/><bf gi="9008" ue="31213" us="31209"/><bf gi="9013" ue="31217" us="31216"/><bf gi="9015" ue="31224" us="31224"/><bf gi="9016" ue="31228" us="31227"/><bf gi="9018" ue="31232" us="31232"/><bf gi="9019" ue="31235" us="31234"/><bf gi="9021" ue="31246" us="31239"/><bf gi="9029" ue="31249" us="31249"/><bf gi="9030" ue="31253" us="31252"/><bf gi="9032" ue="31260" us="31255"/><bf gi="9038" ue="31265" us="31262"/><bf gi="9042" ue="31271" us="31271"/><bf gi="9043" ue="31275" us="31275"/><bf gi="9044" ue="31282" us="31277"/><bf gi="9050" ue="31285" us="31284"/><bf gi="9052" ue="31296" us="31287"/><bf gi="9062" ue="31305" us="31298"/><bf gi="9070" ue="31312" us="31308"/><bf gi="9075" ue="31319" us="31317"/><bf gi="9078" ue="31321" us="31321"/><bf gi="9079" ue="31325" us="31324"/><bf gi="9081" ue="31331" us="31327"/><bf gi="9086" ue="31333" us="31333"/><bf gi="9087" ue="31335" us="31335"/><bf gi="9088" ue="31339" us="31337"/><bf!
gi="9091" ue="31341" us="31341"/><bf gi="9092" ue="31344" us="31344"/
><bf gi="9093" ue="31350" us="31348"/><bf gi="9096" ue="31354" us="31352"/><bf gi="9099" ue="31366" us="31357"/><bf gi="9109" ue="31368" us="31368"/><bf gi="9110" ue="31371" us="31370"/><bf gi="9112" ue="31384" us="31376"/><bf gi="9121" ue="31392" us="31390"/><bf gi="9124" ue="31395" us="31395"/><bf gi="9125" ue="31402" us="31401"/><bf gi="9127" ue="31404" us="31404"/><bf gi="9128" ue="31408" us="31406"/><bf gi="9131" ue="31411" us="31411"/><bf gi="9132" ue="31414" us="31413"/><bf gi="9134" ue="31420" us="31417"/><bf gi="9138" ue="31423" us="31423"/><bf gi="9139" ue="31439" us="31427"/><bf gi="9152" ue="31443" us="31441"/><bf gi="9155" ue="31445" us="31445"/><bf gi="9156" ue="31453" us="31449"/><bf gi="9161" ue="31459" us="31455"/><bf gi="9166" ue="31469" us="31461"/><bf gi="9175" ue="31473" us="31471"/><bf gi="9178" ue="31476" us="31476"/><bf gi="9179" ue="31478" us="31478"/><bf gi="9180" ue="31483" us="31480"/><bf gi="9184" ue="31487" us="31485"/><bf gi="9187" ue="31490" u!
s="31490"/><bf gi="9188" ue="31492" us="31492"/><bf gi="9189" ue="31496" us="31494"/><bf gi="9192" ue="31499" us="31498"/><bf gi="9194" ue="31503" us="31503"/><bf gi="9195" ue="31505" us="31505"/><bf gi="9196" ue="31508" us="31508"/><bf gi="9197" ue="31513" us="31512"/><bf gi="9199" ue="31515" us="31515"/><bf gi="9200" ue="31520" us="31518"/><bf gi="9203" ue="31523" us="31523"/><bf gi="9204" ue="31537" us="31525"/><bf gi="9217" ue="31542" us="31539"/><bf gi="9221" ue="31545" us="31545"/><bf gi="9222" ue="31549" us="31549"/><bf gi="9223" ue="31553" us="31551"/><bf gi="9226" ue="31561" us="31557"/><bf gi="9231" ue="31570" us="31563"/><bf gi="9239" ue="31574" us="31572"/><bf gi="9242" ue="31581" us="31581"/><bf gi="9243" ue="31584" us="31584"/><bf gi="9244" ue="31591" us="31588"/><bf gi="9248" ue="31594" us="31593"/><bf gi="9250" ue="31605" us="31596"/><bf gi="9260" ue="31607" us="31607"/><bf gi="9261" ue="31610" us="31610"/><bf gi="9262" ue="31620" us="31620"/><bf gi="9263" u!
e="31623" us="31622"/><bf gi="9265" ue="31625" us="31625"/><bf gi="926
6" ue="31627" us="31627"/><bf gi="9267" ue="31634" us="31629"/><bf gi="9273" ue="31649" us="31636"/><bf gi="9287" ue="31653" us="31653"/><bf gi="9288" ue="31658" us="31658"/><bf gi="9289" ue="31661" us="31660"/><bf gi="9291" ue="31666" us="31663"/><bf gi="9295" ue="31670" us="31668"/><bf gi="9298" ue="31672" us="31672"/><bf gi="9299" ue="31677" us="31674"/><bf gi="9303" ue="31682" us="31680"/><bf gi="9306" ue="31692" us="31684"/><bf gi="9315" ue="31695" us="31695"/><bf gi="9316" ue="31700" us="31700"/><bf gi="9317" ue="31703" us="31702"/><bf gi="9319" ue="31707" us="31705"/><bf gi="9322" ue="31709" us="31709"/><bf gi="9323" ue="31712" us="31712"/><bf gi="9324" ue="31718" us="31716"/><bf gi="9327" ue="31722" us="31720"/><bf gi="9330" ue="31725" us="31725"/><bf gi="9331" ue="31738" us="31730"/><bf gi="9340" ue="31740" us="31740"/><bf gi="9341" ue="31742" us="31742"/><bf gi="9342" ue="31748" us="31744"/><bf gi="9347" ue="31751" us="31750"/><bf gi="9349" ue="31753" us="31753"/><!
bf gi="9350" ue="31759" us="31755"/><bf gi="9355" ue="31764" us="31761"/><bf gi="9359" ue="31767" us="31767"/><bf gi="9360" ue="31769" us="31769"/><bf gi="9361" ue="31771" us="31771"/><bf gi="9362" ue="31777" us="31775"/><bf gi="9365" ue="31779" us="31779"/><bf gi="9366" ue="31784" us="31781"/><bf gi="9370" ue="31788" us="31786"/><bf gi="9373" ue="31793" us="31793"/><bf gi="9374" ue="31796" us="31795"/><bf gi="9376" ue="31802" us="31798"/><bf gi="9381" ue="31808" us="31805"/><bf gi="9385" ue="31811" us="31811"/><bf gi="9386" ue="31814" us="31814"/><bf gi="9387" ue="31818" us="31818"/><bf gi="9388" ue="31821" us="31820"/><bf gi="9390" ue="31830" us="31823"/><bf gi="9398" ue="31841" us="31832"/><bf gi="9408" ue="31845" us="31843"/><bf gi="9411" ue="31847" us="31847"/><bf gi="9412" ue="31849" us="31849"/><bf gi="9413" ue="31854" us="31852"/><bf gi="9416" ue="31856" us="31856"/><bf gi="9417" ue="31859" us="31858"/><bf gi="9419" ue="31861" us="31861"/><bf gi="9420" ue="31865" us!
="31865"/><bf gi="9421" ue="31870" us="31868"/><bf gi="9424" ue="31875
" us="31873"/><bf gi="9427" ue="31879" us="31878"/><bf gi="9429" ue="31881" us="31881"/><bf gi="9430" ue="31883" us="31883"/><bf gi="9431" ue="31885" us="31885"/><bf gi="9432" ue="31888" us="31887"/><bf gi="9434" ue="31890" us="31890"/><bf gi="9435" ue="31893" us="31892"/><bf gi="9437" ue="31896" us="31895"/><bf gi="9439" ue="31899" us="31899"/><bf gi="9440" ue="31906" us="31902"/><bf gi="9445" ue="31912" us="31908"/><bf gi="9450" ue="31915" us="31915"/><bf gi="9451" ue="31918" us="31917"/><bf gi="9453" ue="31923" us="31920"/><bf gi="9457" ue="31927" us="31926"/><bf gi="9459" ue="31936" us="31929"/><bf gi="9467" ue="31938" us="31938"/><bf gi="9468" ue="31941" us="31940"/><bf gi="9470" ue="31946" us="31943"/><bf gi="9474" ue="31951" us="31949"/><bf gi="9477" ue="31962" us="31954"/><bf gi="9486" ue="31968" us="31964"/><bf gi="9491" ue="31970" us="31970"/><bf gi="9492" ue="31975" us="31974"/><bf gi="9494" ue="31977" us="31977"/><bf gi="9495" ue="31979" us="31979"/><bf gi="9496"!
ue="31983" us="31983"/><bf gi="9497" ue="31986" us="31986"/><bf gi="9498" ue="31990" us="31988"/><bf gi="9501" ue="31992" us="31992"/><bf gi="9502" ue="31995" us="31994"/><bf gi="9504" ue="31998" us="31998"/><bf gi="9505" ue="32000" us="32000"/><bf gi="9506" ue="32011" us="32002"/><bf gi="9516" ue="32013" us="32013"/><bf gi="9517" ue="32030" us="32015"/><bf gi="9533" ue="32035" us="32032"/><bf gi="9537" ue="32038" us="32038"/><bf gi="9538" ue="32051" us="32042"/><bf gi="9548" ue="32053" us="32053"/><bf gi="9549" ue="32058" us="32057"/><bf gi="9551" ue="32072" us="32060"/><bf gi="9564" ue="32081" us="32075"/><bf gi="9571" ue="32083" us="32083"/><bf gi="9572" ue="32087" us="32086"/><bf gi="9574" ue="32094" us="32089"/><bf gi="9580" ue="32099" us="32097"/><bf gi="9583" ue="32104" us="32101"/><bf gi="9587" ue="32106" us="32106"/><bf gi="9588" ue="32110" us="32110"/><bf gi="9589" ue="32115" us="32112"/><bf gi="9593" ue="32118" us="32117"/><bf gi="9595" ue="32123" us="32120"/><b!
f gi="9599" ue="32125" us="32125"/><bf gi="9600" ue="32127" us="32127"
/><bf gi="9601" ue="32131" us="32129"/><bf gi="9604" ue="32134" us="32133"/><bf gi="9606" ue="32137" us="32136"/><bf gi="9608" ue="32141" us="32139"/><bf gi="9611" ue="32143" us="32143"/><bf gi="9612" ue="32145" us="32145"/><bf gi="9613" ue="32147" us="32147"/><bf gi="9614" ue="32151" us="32150"/><bf gi="9616" ue="32160" us="32153"/><bf gi="9624" ue="32163" us="32162"/><bf gi="9626" ue="32167" us="32166"/><bf gi="9628" ue="32187" us="32170"/><bf gi="9646" ue="32191" us="32189"/><bf gi="9649" ue="32199" us="32194"/><bf gi="9655" ue="32207" us="32202"/><bf gi="9661" ue="32210" us="32209"/><bf gi="9663" ue="32218" us="32213"/><bf gi="9669" ue="32222" us="32220"/><bf gi="9672" ue="32226" us="32224"/><bf gi="9675" ue="32230" us="32228"/><bf gi="9678" ue="32237" us="32232"/><bf gi="9684" ue="32239" us="32239"/><bf gi="9685" ue="32242" us="32241"/><bf gi="9687" ue="32246" us="32244"/><bf gi="9690" ue="32251" us="32249"/><bf gi="9693" ue="32257" us="32256"/><bf gi="9695" ue="32261" !
us="32260"/><bf gi="9697" ue="32267" us="32264"/><bf gi="9701" ue="32274" us="32272"/><bf gi="9704" ue="32277" us="32277"/><bf gi="9705" ue="32279" us="32279"/><bf gi="9706" ue="32291" us="32283"/><bf gi="9715" ue="32296" us="32294"/><bf gi="9718" ue="32303" us="32299"/><bf gi="9723" ue="32307" us="32305"/><bf gi="9726" ue="32311" us="32309"/><bf gi="9729" ue="32315" us="32313"/><bf gi="9732" ue="32319" us="32317"/><bf gi="9735" ue="32321" us="32321"/><bf gi="9736" ue="32327" us="32323"/><bf gi="9741" ue="32331" us="32330"/><bf gi="9743" ue="32334" us="32333"/><bf gi="9745" ue="32336" us="32336"/><bf gi="9746" ue="32338" us="32338"/><bf gi="9747" ue="32342" us="32340"/><bf gi="9750" ue="32346" us="32344"/><bf gi="9753" ue="32351" us="32349"/><bf gi="9756" ue="32354" us="32353"/><bf gi="9758" ue="32359" us="32357"/><bf gi="9761" ue="32363" us="32361"/><bf gi="9764" ue="32368" us="32365"/><bf gi="9768" ue="32371" us="32371"/><bf gi="9769" ue="32377" us="32376"/><bf gi="9771" !
ue="32383" us="32379"/><bf gi="9776" ue="32387" us="32385"/><bf gi="97
79" ue="32394" us="32390"/><bf gi="9784" ue="32406" us="32396"/><bf gi="9795" ue="32408" us="32408"/><bf gi="9796" ue="32414" us="32410"/><bf gi="9801" ue="32566" us="32566"/><bf gi="9802" ue="32568" us="32568"/><bf gi="9803" ue="32575" us="32570"/><bf gi="9809" ue="32581" us="32579"/><bf gi="9812" ue="32583" us="32583"/><bf gi="9813" ue="32597" us="32588"/><bf gi="9823" ue="32600" us="32600"/><bf gi="9824" ue="32605" us="32603"/><bf gi="9827" ue="32609" us="32607"/><bf gi="9830" ue="32619" us="32611"/><bf gi="9839" ue="32622" us="32621"/><bf gi="9841" ue="32626" us="32624"/><bf gi="9844" ue="32629" us="32629"/><bf gi="9845" ue="32633" us="32631"/><bf gi="9848" ue="32640" us="32637"/><bf gi="9852" ue="32643" us="32642"/><bf gi="9854" ue="32648" us="32645"/><bf gi="9858" ue="32657" us="32650"/><bf gi="9866" ue="32660" us="32660"/><bf gi="9867" ue="32663" us="32662"/><bf gi="9869" ue="32666" us="32666"/><bf gi="9870" ue="32670" us="32668"/><bf gi="9873" ue="32676" us="32673"/>!
<bf gi="9877" ue="32678" us="32678"/><bf gi="9878" ue="32682" us="32680"/><bf gi="9881" ue="32687" us="32685"/><bf gi="9884" ue="32690" us="32690"/><bf gi="9885" ue="32692" us="32692"/><bf gi="9886" ue="32694" us="32694"/><bf gi="9887" ue="32697" us="32696"/><bf gi="9889" ue="32701" us="32700"/><bf gi="9891" ue="32705" us="32703"/><bf gi="9894" ue="32707" us="32707"/><bf gi="9895" ue="32710" us="32709"/><bf gi="9897" ue="32712" us="32712"/><bf gi="9898" ue="32714" us="32714"/><bf gi="9899" ue="32716" us="32716"/><bf gi="9900" ue="32719" us="32718"/><bf gi="9902" ue="32722" us="32722"/><bf gi="9903" ue="32725" us="32724"/><bf gi="9905" ue="32731" us="32731"/><bf gi="9906" ue="32737" us="32735"/><bf gi="9909" ue="32739" us="32739"/><bf gi="9910" ue="32742" us="32741"/><bf gi="9912" ue="32745" us="32744"/><bf gi="9914" ue="32748" us="32747"/><bf gi="9916" ue="32752" us="32750"/><bf gi="9919" ue="32755" us="32754"/><bf gi="9921" ue="32769" us="32761"/><bf gi="9930" ue="32776" u!
s="32771"/><bf gi="9936" ue="32793" us="32778"/><bf gi="9952" ue="3280
1" us="32796"/><bf gi="9958" ue="32804" us="32804"/><bf gi="9959" ue="32806" us="32806"/><bf gi="9960" ue="32808" us="32808"/><bf gi="9961" ue="32812" us="32812"/><bf gi="9962" ue="32814" us="32814"/><bf gi="9963" ue="32816" us="32816"/><bf gi="9964" ue="32823" us="32819"/><bf gi="9969" ue="32832" us="32825"/><bf gi="9977" ue="32836" us="32836"/><bf gi="9978" ue="32838" us="32838"/><bf gi="9979" ue="32842" us="32842"/><bf gi="9980" ue="32850" us="32850"/><bf gi="9981" ue="32854" us="32854"/><bf gi="9982" ue="32856" us="32856"/><bf gi="9983" ue="32858" us="32858"/><bf gi="9984" ue="32866" us="32862"/><bf gi="9989" ue="32868" us="32868"/><bf gi="9990" ue="32870" us="32870"/><bf gi="9991" ue="32872" us="32872"/><bf gi="9992" ue="32877" us="32877"/><bf gi="9993" ue="32887" us="32879"/><bf gi="10002" ue="32889" us="32889"/><bf gi="10003" ue="32895" us="32893"/><bf gi="10006" ue="32897" us="32897"/><bf gi="10007" ue="32905" us="32900"/><bf gi="10013" ue="32908" us="32907"/><bf gi=!
"10015" ue="32910" us="32910"/><bf gi="10016" ue="32915" us="32915"/><bf gi="10017" ue="32918" us="32918"/><bf gi="10018" ue="32920" us="32920"/><bf gi="10019" ue="32926" us="32922"/><bf gi="10024" ue="32930" us="32929"/><bf gi="10026" ue="32935" us="32933"/><bf gi="10029" ue="32941" us="32937"/><bf gi="10034" ue="32943" us="32943"/><bf gi="10035" ue="32946" us="32945"/><bf gi="10037" ue="32948" us="32948"/><bf gi="10038" ue="32954" us="32952"/><bf gi="10041" ue="32964" us="32963"/><bf gi="10043" ue="32966" us="32966"/><bf gi="10044" ue="32968" us="32968"/><bf gi="10045" ue="32975" us="32972"/><bf gi="10049" ue="32978" us="32978"/><bf gi="10050" ue="32987" us="32980"/><bf gi="10058" ue="32990" us="32989"/><bf gi="10060" ue="32993" us="32992"/><bf gi="10062" ue="32997" us="32996"/><bf gi="10064" ue="33012" us="33005"/><bf gi="10072" ue="33014" us="33014"/><bf gi="10073" ue="33018" us="33016"/><bf gi="10076" ue="33022" us="33020"/><bf gi="10079" ue="33027" us="33026"/><bf gi=!
"10081" ue="33035" us="33029"/><bf gi="10088" ue="33048" us="33046"/><
bf gi="10091" ue="33052" us="33050"/><bf gi="10094" ue="33054" us="33054"/><bf gi="10095" ue="33056" us="33056"/><bf gi="10096" ue="33060" us="33059"/><bf gi="10098" ue="33063" us="33063"/><bf gi="10099" ue="33065" us="33065"/><bf gi="10100" ue="33068" us="33068"/><bf gi="10101" ue="33073" us="33071"/><bf gi="10104" ue="33075" us="33075"/><bf gi="10105" ue="33077" us="33077"/><bf gi="10106" ue="33082" us="33081"/><bf gi="10108" ue="33084" us="33084"/><bf gi="10109" ue="33086" us="33086"/><bf gi="10110" ue="33095" us="33093"/><bf gi="10113" ue="33100" us="33098"/><bf gi="10116" ue="33102" us="33102"/><bf gi="10117" ue="33109" us="33104"/><bf gi="10123" ue="33111" us="33111"/><bf gi="10124" ue="33121" us="33119"/><bf gi="10127" ue="33129" us="33125"/><bf gi="10132" ue="33131" us="33131"/><bf gi="10133" ue="33137" us="33133"/><bf gi="10138" ue="33140" us="33140"/><bf gi="10139" ue="33146" us="33143"/><bf gi="10143" ue="33158" us="33151"/><bf gi="10151" ue="33160" us="33160"/><b!
f gi="10152" ue="33163" us="33162"/><bf gi="10154" ue="33168" us="33166"/><bf gi="10157" ue="33171" us="33171"/><bf gi="10158" ue="33174" us="33173"/><bf gi="10160" ue="33176" us="33176"/><bf gi="10161" ue="33182" us="33178"/><bf gi="10166" ue="33184" us="33184"/><bf gi="10167" ue="33188" us="33186"/><bf gi="10170" ue="33193" us="33192"/><bf gi="10172" ue="33198" us="33198"/><bf gi="10173" ue="33200" us="33200"/><bf gi="10174" ue="33205" us="33202"/><bf gi="10178" ue="33208" us="33208"/><bf gi="10179" ue="33211" us="33210"/><bf gi="10181" ue="33216" us="33213"/><bf gi="10185" ue="33219" us="33218"/><bf gi="10187" ue="33222" us="33221"/><bf gi="10189" ue="33227" us="33224"/><bf gi="10193" ue="33231" us="33229"/><bf gi="10196" ue="33233" us="33233"/><bf gi="10197" ue="33235" us="33235"/><bf gi="10198" ue="33237" us="33237"/><bf gi="10199" ue="33243" us="33239"/><bf gi="10204" ue="33249" us="33245"/><bf gi="10209" ue="33253" us="33251"/><bf gi="10212" ue="33256" us="33255"/><b!
f gi="10214" ue="33261" us="33258"/><bf gi="10218" ue="33270" us="3326
4"/><bf gi="10225" ue="33283" us="33272"/><bf gi="10237" ue="33285" us="33285"/><bf gi="10238" ue="33290" us="33287"/><bf gi="10242" ue="33296" us="33292"/><bf gi="10247" ue="33300" us="33298"/><bf gi="10250" ue="33311" us="33302"/><bf gi="10260" ue="33314" us="33313"/><bf gi="10262" ue="33324" us="33320"/><bf gi="10267" ue="33326" us="33326"/><bf gi="10268" ue="33338" us="33330"/><bf gi="10277" ue="33344" us="33344"/><bf gi="10278" ue="33351" us="33347"/><bf gi="10283" ue="33355" us="33355"/><bf gi="10284" ue="33359" us="33358"/><bf gi="10286" ue="33361" us="33361"/><bf gi="10287" ue="33366" us="33366"/><bf gi="10288" ue="33370" us="33368"/><bf gi="10291" ue="33373" us="33372"/><bf gi="10293" ue="33376" us="33375"/><bf gi="10295" ue="33380" us="33378"/><bf gi="10298" ue="33384" us="33382"/><bf gi="10301" ue="33387" us="33386"/><bf gi="10303" ue="33391" us="33389"/><bf gi="10306" ue="33394" us="33393"/><bf gi="10308" ue="33396" us="33396"/><bf gi="10309" ue="33400" us="33398!
"/><bf gi="10312" ue="33403" us="33403"/><bf gi="10313" ue="33409" us="33405"/><bf gi="10318" ue="33412" us="33411"/><bf gi="10320" ue="33415" us="33415"/><bf gi="10321" ue="33419" us="33417"/><bf gi="10324" ue="33422" us="33421"/><bf gi="10326" ue="33426" us="33425"/><bf gi="10328" ue="33428" us="33428"/><bf gi="10329" ue="33430" us="33430"/><bf gi="10330" ue="33435" us="33432"/><bf gi="10334" ue="33437" us="33437"/><bf gi="10335" ue="33441" us="33439"/><bf gi="10338" ue="33460" us="33443"/><bf gi="10356" ue="33471" us="33463"/><bf gi="10365" ue="33478" us="33477"/><bf gi="10367" ue="33493" us="33488"/><bf gi="10373" ue="33495" us="33495"/><bf gi="10374" ue="33500" us="33497"/><bf gi="10378" ue="33512" us="33502"/><bf gi="10389" ue="33515" us="33514"/><bf gi="10391" ue="33517" us="33517"/><bf gi="10392" ue="33519" us="33519"/><bf gi="10393" ue="33521" us="33521"/><bf gi="10394" ue="33524" us="33523"/><bf gi="10396" ue="33527" us="33526"/><bf gi="10398" ue="33531" us="33529!
"/><bf gi="10401" ue="33534" us="33533"/><bf gi="10403" ue="33547" us=
"33536"/><bf gi="10415" ue="33550" us="33550"/><bf gi="10416" ue="33560" us="33558"/><bf gi="10419" ue="33567" us="33563"/><bf gi="10424" ue="33571" us="33569"/><bf gi="10427" ue="33576" us="33576"/><bf gi="10428" ue="33594" us="33579"/><bf gi="10444" ue="33597" us="33596"/><bf gi="10446" ue="33600" us="33600"/><bf gi="10447" ue="33605" us="33602"/><bf gi="10451" ue="33607" us="33607"/><bf gi="10452" ue="33610" us="33609"/><bf gi="10454" ue="33624" us="33613"/><bf gi="10466" ue="33634" us="33634"/><bf gi="10467" ue="33648" us="33648"/><bf gi="10468" ue="33651" us="33651"/><bf gi="10469" ue="33653" us="33653"/><bf gi="10470" ue="33656" us="33655"/><bf gi="10472" ue="33661" us="33659"/><bf gi="10475" ue="33664" us="33663"/><bf gi="10477" ue="33666" us="33666"/><bf gi="10478" ue="33671" us="33668"/><bf gi="10482" ue="33674" us="33673"/><bf gi="10484" ue="33678" us="33677"/><bf gi="10486" ue="33686" us="33682"/><bf gi="10491" ue="33696" us="33688"/><bf gi="10500" ue="33698" us="!
33698"/><bf gi="10501" ue="33709" us="33702"/><bf gi="10509" ue="33713" us="33713"/><bf gi="10510" ue="33717" us="33717"/><bf gi="10511" ue="33729" us="33725"/><bf gi="10516" ue="33733" us="33733"/><bf gi="10517" ue="33735" us="33735"/><bf gi="10518" ue="33738" us="33737"/><bf gi="10520" ue="33740" us="33740"/><bf gi="10521" ue="33745" us="33742"/><bf gi="10525" ue="33748" us="33747"/><bf gi="10527" ue="33750" us="33750"/><bf gi="10528" ue="33752" us="33752"/><bf gi="10529" ue="33757" us="33756"/><bf gi="10531" ue="33760" us="33759"/><bf gi="10533" ue="33771" us="33768"/><bf gi="10537" ue="33778" us="33775"/><bf gi="10541" ue="33780" us="33780"/><bf gi="10542" ue="33785" us="33782"/><bf gi="10546" ue="33789" us="33787"/><bf gi="10549" ue="33793" us="33793"/><bf gi="10550" ue="33796" us="33795"/><bf gi="10552" ue="33799" us="33798"/><bf gi="10554" ue="33807" us="33802"/><bf gi="10560" ue="33809" us="33809"/><bf gi="10561" ue="33811" us="33811"/><bf gi="10562" ue="33813" us="!
33813"/><bf gi="10563" ue="33817" us="33817"/><bf gi="10564" ue="33824
" us="33824"/><bf gi="10565" ue="33826" us="33826"/><bf gi="10566" ue="33834" us="33833"/><bf gi="10568" ue="33836" us="33836"/><bf gi="10569" ue="33839" us="33839"/><bf gi="10570" ue="33841" us="33841"/><bf gi="10571" ue="33845" us="33845"/><bf gi="10572" ue="33849" us="33848"/><bf gi="10574" ue="33853" us="33852"/><bf gi="10576" ue="33866" us="33861"/><bf gi="10582" ue="33871" us="33869"/><bf gi="10585" ue="33874" us="33873"/><bf gi="10587" ue="33884" us="33878"/><bf gi="10594" ue="33895" us="33888"/><bf gi="10602" ue="33905" us="33897"/><bf gi="10611" ue="33914" us="33907"/><bf gi="10619" ue="33917" us="33916"/><bf gi="10621" ue="33922" us="33921"/><bf gi="10623" ue="33925" us="33924"/><bf gi="10625" ue="33931" us="33931"/><bf gi="10626" ue="33936" us="33936"/><bf gi="10627" ue="33941" us="33938"/><bf gi="10631" ue="33945" us="33945"/><bf gi="10632" ue="33948" us="33948"/><bf gi="10633" ue="33951" us="33950"/><bf gi="10635" ue="33953" us="33953"/><bf gi="10636" ue="33958"!
us="33958"/><bf gi="10637" ue="33962" us="33960"/><bf gi="10640" ue="33965" us="33965"/><bf gi="10641" ue="33967" us="33967"/><bf gi="10642" ue="33970" us="33969"/><bf gi="10644" ue="33972" us="33972"/><bf gi="10645" ue="33986" us="33976"/><bf gi="10656" ue="33988" us="33988"/><bf gi="10657" ue="33997" us="33990"/><bf gi="10665" ue="34001" us="33999"/><bf gi="10668" ue="34003" us="34003"/><bf gi="10669" ue="34006" us="34006"/><bf gi="10670" ue="34010" us="34009"/><bf gi="10672" ue="34012" us="34012"/><bf gi="10673" ue="34023" us="34023"/><bf gi="10674" ue="34026" us="34026"/><bf gi="10675" ue="34028" us="34028"/><bf gi="10676" ue="34034" us="34030"/><bf gi="10681" ue="34036" us="34036"/><bf gi="10682" ue="34039" us="34039"/><bf gi="10683" ue="34045" us="34042"/><bf gi="10687" ue="34048" us="34047"/><bf gi="10689" ue="34051" us="34050"/><bf gi="10691" ue="34055" us="34054"/><bf gi="10693" ue="34060" us="34060"/><bf gi="10694" ue="34062" us="34062"/><bf gi="10695" ue="34065"!
us="34064"/><bf gi="10697" ue="34069" us="34067"/><bf gi="10700" ue="
34072" us="34071"/><bf gi="10702" ue="34074" us="34074"/><bf gi="10703" ue="34076" us="34076"/><bf gi="10704" ue="34079" us="34078"/><bf gi="10706" ue="34087" us="34081"/><bf gi="10713" ue="34093" us="34090"/><bf gi="10717" ue="34095" us="34095"/><bf gi="10718" ue="34102" us="34098"/><bf gi="10723" ue="34109" us="34109"/><bf gi="10724" ue="34113" us="34111"/><bf gi="10727" ue="34115" us="34115"/><bf gi="10728" ue="34118" us="34118"/><bf gi="10729" ue="34123" us="34120"/><bf gi="10733" ue="34131" us="34126"/><bf gi="10739" ue="34138" us="34133"/><bf gi="10745" ue="34148" us="34140"/><bf gi="10754" ue="34155" us="34152"/><bf gi="10758" ue="34157" us="34157"/><bf gi="10759" ue="34159" us="34159"/><bf gi="10760" ue="34167" us="34167"/><bf gi="10761" ue="34171" us="34169"/><bf gi="10764" ue="34177" us="34173"/><bf gi="10769" ue="34188" us="34180"/><bf gi="10778" ue="34193" us="34191"/><bf gi="10781" ue="34196" us="34195"/><bf gi="10783" ue="34201" us="34199"/><bf gi="10786" ue="3!
4205" us="34203"/><bf gi="10789" ue="34208" us="34207"/><bf gi="10791" ue="34210" us="34210"/><bf gi="10792" ue="34224" us="34212"/><bf gi="10805" ue="34228" us="34228"/><bf gi="10806" ue="34234" us="34230"/><bf gi="10811" ue="34239" us="34236"/><bf gi="10815" ue="34242" us="34241"/><bf gi="10817" ue="34247" us="34247"/><bf gi="10818" ue="34251" us="34249"/><bf gi="10821" ue="34256" us="34253"/><bf gi="10825" ue="34261" us="34261"/><bf gi="10826" ue="34264" us="34264"/><bf gi="10827" ue="34266" us="34266"/><bf gi="10828" ue="34269" us="34268"/><bf gi="10830" ue="34272" us="34271"/><bf gi="10832" ue="34278" us="34276"/><bf gi="10835" ue="34282" us="34280"/><bf gi="10838" ue="34285" us="34285"/><bf gi="10839" ue="34291" us="34291"/><bf gi="10840" ue="34295" us="34294"/><bf gi="10842" ue="34300" us="34297"/><bf gi="10846" ue="34304" us="34302"/><bf gi="10849" ue="34306" us="34306"/><bf gi="10850" ue="34311" us="34308"/><bf gi="10854" ue="34315" us="34314"/><bf gi="10856" ue="3!
4318" us="34317"/><bf gi="10858" ue="34323" us="34320"/><bf gi="10862"
ue="34331" us="34326"/><bf gi="10868" ue="34334" us="34334"/><bf gi="10869" ue="34338" us="34337"/><bf gi="10871" ue="34343" us="34343"/><bf gi="10872" ue="34345" us="34345"/><bf gi="10873" ue="34349" us="34349"/><bf gi="10874" ue="34352" us="34351"/><bf gi="10876" ue="34358" us="34358"/><bf gi="10877" ue="34360" us="34360"/><bf gi="10878" ue="34362" us="34362"/><bf gi="10879" ue="34365" us="34364"/><bf gi="10881" ue="34370" us="34367"/><bf gi="10885" ue="34374" us="34374"/><bf gi="10886" ue="34382" us="34381"/><bf gi="10888" ue="34384" us="34384"/><bf gi="10889" ue="34394" us="34386"/><bf gi="10898" ue="34404" us="34396"/><bf gi="10907" ue="34407" us="34407"/><bf gi="10908" ue="34409" us="34409"/><bf gi="10909" ue="34412" us="34411"/><bf gi="10911" ue="34415" us="34415"/><bf gi="10912" ue="34417" us="34417"/><bf gi="10913" ue="34423" us="34421"/><bf gi="10916" ue="34427" us="34425"/><bf gi="10919" ue="34440" us="34440"/><bf gi="10920" ue="34445" us="34442"/><bf gi="10924" !
ue="34449" us="34449"/><bf gi="10925" ue="34451" us="34451"/><bf gi="10926" ue="34454" us="34453"/><bf gi="10928" ue="34456" us="34456"/><bf gi="10929" ue="34458" us="34458"/><bf gi="10930" ue="34460" us="34460"/><bf gi="10931" ue="34465" us="34465"/><bf gi="10932" ue="34468" us="34467"/><bf gi="10934" ue="34475" us="34470"/><bf gi="10940" ue="34477" us="34477"/><bf gi="10941" ue="34481" us="34479"/><bf gi="10944" ue="34489" us="34483"/><bf gi="10951" ue="34497" us="34495"/><bf gi="10954" ue="34503" us="34499"/><bf gi="10959" ue="34505" us="34505"/><bf gi="10960" ue="34507" us="34507"/><bf gi="10961" ue="34510" us="34509"/><bf gi="10963" ue="34514" us="34513"/><bf gi="10965" ue="34517" us="34516"/><bf gi="10967" ue="34519" us="34519"/><bf gi="10968" ue="34524" us="34521"/><bf gi="10972" ue="34528" us="34526"/><bf gi="10975" ue="34533" us="34531"/><bf gi="10978" ue="34535" us="34535"/><bf gi="10979" ue="34537" us="34537"/><bf gi="10980" ue="34543" us="34540"/><bf gi="10984" !
ue="34558" us="34552"/><bf gi="10991" ue="34560" us="34560"/><bf gi="1
0992" ue="34571" us="34562"/><bf gi="11002" ue="34580" us="34573"/><bf gi="11010" ue="34586" us="34584"/><bf gi="11013" ue="34588" us="34588"/><bf gi="11014" ue="34591" us="34590"/><bf gi="11016" ue="34593" us="34593"/><bf gi="11017" ue="34595" us="34595"/><bf gi="11018" ue="34597" us="34597"/><bf gi="11019" ue="34601" us="34600"/><bf gi="11021" ue="34607" us="34606"/><bf gi="11023" ue="34610" us="34609"/><bf gi="11025" ue="34612" us="34612"/><bf gi="11026" ue="34615" us="34615"/><bf gi="11027" ue="34624" us="34617"/><bf gi="11035" ue="34627" us="34627"/><bf gi="11036" ue="34629" us="34629"/><bf gi="11037" ue="34633" us="34633"/><bf gi="11038" ue="34638" us="34635"/><bf gi="11042" ue="34643" us="34643"/><bf gi="11043" ue="34645" us="34645"/><bf gi="11044" ue="34649" us="34647"/><bf gi="11047" ue="34653" us="34653"/><bf gi="11048" ue="34657" us="34655"/><bf gi="11051" ue="34662" us="34659"/><bf gi="11055" ue="34664" us="34664"/><bf gi="11056" ue="34666" us="34666"/><bf gi="11!
057" ue="34671" us="34670"/><bf gi="11059" ue="34674" us="34673"/><bf gi="11061" ue="34676" us="34676"/><bf gi="11062" ue="34678" us="34678"/><bf gi="11063" ue="34680" us="34680"/><bf gi="11064" ue="34683" us="34683"/><bf gi="11065" ue="34687" us="34687"/><bf gi="11066" ue="34697" us="34690"/><bf gi="11074" ue="34701" us="34699"/><bf gi="11077" ue="34704" us="34704"/><bf gi="11078" ue="34707" us="34707"/><bf gi="11079" ue="34709" us="34709"/><bf gi="11080" ue="34713" us="34711"/><bf gi="11083" ue="34720" us="34718"/><bf gi="11086" ue="34723" us="34722"/><bf gi="11088" ue="34727" us="34727"/><bf gi="11089" ue="34735" us="34731"/><bf gi="11094" ue="34737" us="34737"/><bf gi="11095" ue="34739" us="34739"/><bf gi="11096" ue="34741" us="34741"/><bf gi="11097" ue="34747" us="34746"/><bf gi="11099" ue="34753" us="34749"/><bf gi="11104" ue="34756" us="34756"/><bf gi="11105" ue="34763" us="34758"/><bf gi="11111" ue="34766" us="34766"/><bf gi="11112" ue="34768" us="34768"/><bf gi="11!
113" ue="34770" us="34770"/><bf gi="11114" ue="34774" us="34773"/><bf
gi="11116" ue="34778" us="34777"/><bf gi="11118" ue="34780" us="34780"/><bf gi="11119" ue="34784" us="34783"/><bf gi="11121" ue="34788" us="34786"/><bf gi="11124" ue="34795" us="34794"/><bf gi="11126" ue="34797" us="34797"/><bf gi="11127" ue="34799" us="34799"/><bf gi="11128" ue="34803" us="34801"/><bf gi="11131" ue="34811" us="34806"/><bf gi="11137" ue="34815" us="34814"/><bf gi="11139" ue="34817" us="34817"/><bf gi="11140" ue="34819" us="34819"/><bf gi="11141" ue="34823" us="34821"/><bf gi="11144" ue="34827" us="34825"/><bf gi="11147" ue="34838" us="34829"/><bf gi="11157" ue="34844" us="34840"/><bf gi="11162" ue="34847" us="34846"/><bf gi="11164" ue="34851" us="34849"/><bf gi="11167" ue="34856" us="34855"/><bf gi="11169" ue="34862" us="34861"/><bf gi="11171" ue="34866" us="34864"/><bf gi="11174" ue="34870" us="34869"/><bf gi="11176" ue="34876" us="34873"/><bf gi="11180" ue="34886" us="34880"/><bf gi="11187" ue="34894" us="34888"/><bf gi="11194" ue="34899" us="34897"/><bf g!
i="11197" ue="34916" us="34901"/><bf gi="11213" ue="34921" us="34920"/><bf gi="11215" ue="34923" us="34923"/><bf gi="11216" ue="34930" us="34928"/><bf gi="11219" ue="34933" us="34933"/><bf gi="11220" ue="34935" us="34935"/><bf gi="11221" ue="34937" us="34937"/><bf gi="11222" ue="34939" us="34939"/><bf gi="11223" ue="34946" us="34941"/><bf gi="11229" ue="34952" us="34952"/><bf gi="11230" ue="34955" us="34955"/><bf gi="11231" ue="34957" us="34957"/><bf gi="11232" ue="34962" us="34962"/><bf gi="11233" ue="34972" us="34966"/><bf gi="11240" ue="34976" us="34974"/><bf gi="11243" ue="34978" us="34978"/><bf gi="11244" ue="34980" us="34980"/><bf gi="11245" ue="34984" us="34984"/><bf gi="11246" ue="34987" us="34986"/><bf gi="11248" ue="34990" us="34990"/><bf gi="11249" ue="34993" us="34992"/><bf gi="11251" ue="34997" us="34996"/><bf gi="11253" ue="34999" us="34999"/><bf gi="11254" ue="35002" us="35002"/><bf gi="11255" ue="35013" us="35005"/><bf gi="11264" ue="35023" us="35018"/><bf g!
i="11270" ue="35029" us="35025"/><bf gi="11275" ue="35033" us="35032"/
><bf gi="11277" ue="35039" us="35035"/><bf gi="11282" ue="35041" us="35041"/><bf gi="11283" ue="35048" us="35047"/><bf gi="11285" ue="35061" us="35055"/><bf gi="11292" ue="35065" us="35063"/><bf gi="11295" ue="35070" us="35068"/><bf gi="11298" ue="35074" us="35073"/><bf gi="11300" ue="35076" us="35076"/><bf gi="11301" ue="35079" us="35078"/><bf gi="11303" ue="35082" us="35082"/><bf gi="11304" ue="35088" us="35084"/><bf gi="11309" ue="35091" us="35090"/><bf gi="11311" ue="35094" us="35093"/><bf gi="11313" ue="35098" us="35096"/><bf gi="11316" ue="35102" us="35100"/><bf gi="11319" ue="35104" us="35104"/><bf gi="11320" ue="35112" us="35109"/><bf gi="11324" ue="35115" us="35114"/><bf gi="11326" ue="35122" us="35120"/><bf gi="11329" ue="35126" us="35125"/><bf gi="11331" ue="35131" us="35128"/><bf gi="11335" ue="35134" us="35134"/><bf gi="11336" ue="35142" us="35136"/><bf gi="11343" ue="35145" us="35145"/><bf gi="11344" ue="35149" us="35148"/><bf gi="11346" ue="35151" us="35151"/>!
<bf gi="11347" ue="35154" us="35154"/><bf gi="11348" ue="35159" us="35158"/><bf gi="11350" ue="35164" us="35162"/><bf gi="11353" ue="35172" us="35166"/><bf gi="11360" ue="35174" us="35174"/><bf gi="11361" ue="35179" us="35178"/><bf gi="11363" ue="35184" us="35181"/><bf gi="11367" ue="35189" us="35186"/><bf gi="11371" ue="35191" us="35191"/><bf gi="11372" ue="35199" us="35194"/><bf gi="11378" ue="35201" us="35201"/><bf gi="11379" ue="35203" us="35203"/><bf gi="11380" ue="35211" us="35206"/><bf gi="11386" ue="35213" us="35213"/><bf gi="11387" ue="35216" us="35215"/><bf gi="11389" ue="35224" us="35219"/><bf gi="11395" ue="35228" us="35226"/><bf gi="11398" ue="35233" us="35231"/><bf gi="11401" ue="35239" us="35237"/><bf gi="11404" ue="35242" us="35241"/><bf gi="11406" ue="35244" us="35244"/><bf gi="11407" ue="35248" us="35247"/><bf gi="11409" ue="35255" us="35250"/><bf gi="11415" ue="35258" us="35258"/><bf gi="11416" ue="35261" us="35260"/><bf gi="11418" ue="35264" us="35263"/>!
<bf gi="11420" ue="35282" us="35282"/><bf gi="11421" ue="35288" us="35
284"/><bf gi="11426" ue="35290" us="35290"/><bf gi="11427" ue="35293" us="35292"/><bf gi="11429" ue="35299" us="35299"/><bf gi="11430" ue="35303" us="35301"/><bf gi="11433" ue="35305" us="35305"/><bf gi="11434" ue="35307" us="35307"/><bf gi="11435" ue="35309" us="35309"/><bf gi="11436" ue="35313" us="35313"/><bf gi="11437" ue="35316" us="35315"/><bf gi="11439" ue="35318" us="35318"/><bf gi="11440" ue="35321" us="35320"/><bf gi="11442" ue="35325" us="35325"/><bf gi="11443" ue="35328" us="35327"/><bf gi="11445" ue="35333" us="35330"/><bf gi="11449" ue="35336" us="35335"/><bf gi="11451" ue="35338" us="35338"/><bf gi="11452" ue="35340" us="35340"/><bf gi="11453" ue="35352" us="35342"/><bf gi="11464" ue="35355" us="35355"/><bf gi="11465" ue="35360" us="35357"/><bf gi="11469" ue="35366" us="35362"/><bf gi="11474" ue="35373" us="35370"/><bf gi="11478" ue="35375" us="35375"/><bf gi="11479" ue="35377" us="35377"/><bf gi="11480" ue="35383" us="35379"/><bf gi="11485" ue="35390" us="353!
86"/><bf gi="11490" ue="35393" us="35392"/><bf gi="11492" ue="35395" us="35395"/><bf gi="11493" ue="35401" us="35397"/><bf gi="11498" ue="35406" us="35405"/><bf gi="11500" ue="35416" us="35408"/><bf gi="11509" ue="35422" us="35419"/><bf gi="11513" ue="35427" us="35424"/><bf gi="11517" ue="35431" us="35429"/><bf gi="11520" ue="35433" us="35433"/><bf gi="11521" ue="35438" us="35435"/><bf gi="11525" ue="35443" us="35440"/><bf gi="11529" ue="35447" us="35445"/><bf gi="11532" ue="35452" us="35449"/><bf gi="11536" ue="35456" us="35454"/><bf gi="11539" ue="35463" us="35458"/><bf gi="11545" ue="35465" us="35465"/><bf gi="11546" ue="35469" us="35467"/><bf gi="11549" ue="35475" us="35471"/><bf gi="11554" ue="35482" us="35477"/><bf gi="11560" ue="35489" us="35486"/><bf gi="11564" ue="35497" us="35491"/><bf gi="11571" ue="35504" us="35500"/><bf gi="11576" ue="35507" us="35506"/><bf gi="11578" ue="35511" us="35510"/><bf gi="11580" ue="35513" us="35513"/><bf gi="11581" ue="35516" us="355!
15"/><bf gi="11583" ue="35519" us="35518"/><bf gi="11585" ue="35524" u
s="35522"/><bf gi="11588" ue="35533" us="35526"/><bf gi="11596" ue="35535" us="35535"/><bf gi="11597" ue="35543" us="35537"/><bf gi="11604" ue="35554" us="35546"/><bf gi="11613" ue="35556" us="35556"/><bf gi="11614" ue="35559" us="35558"/><bf gi="11616" ue="35566" us="35563"/><bf gi="11620" ue="35569" us="35568"/><bf gi="11622" ue="35576" us="35571"/><bf gi="11628" ue="35578" us="35578"/><bf gi="11629" ue="35580" us="35580"/><bf gi="11630" ue="35586" us="35582"/><bf gi="11635" ue="35591" us="35588"/><bf gi="11639" ue="35596" us="35594"/><bf gi="11642" ue="35598" us="35598"/><bf gi="11643" ue="35601" us="35600"/><bf gi="11645" ue="35604" us="35604"/><bf gi="11646" ue="35607" us="35606"/><bf gi="11648" ue="35617" us="35609"/><bf gi="11657" ue="35622" us="35622"/><bf gi="11658" ue="35624" us="35624"/><bf gi="11659" ue="35629" us="35627"/><bf gi="11662" ue="35632" us="35632"/><bf gi="11663" ue="35635" us="35635"/><bf gi="11664" ue="35639" us="35639"/><bf gi="11665" ue="35641" us!
="35641"/><bf gi="11666" ue="35644" us="35644"/><bf gi="11667" ue="35646" us="35646"/><bf gi="11668" ue="35654" us="35649"/><bf gi="11674" ue="35657" us="35656"/><bf gi="11676" ue="35663" us="35660"/><bf gi="11680" ue="35668" us="35666"/><bf gi="11683" ue="35670" us="35670"/><bf gi="11684" ue="35676" us="35672"/><bf gi="11689" ue="35679" us="35678"/><bf gi="11691" ue="35683" us="35683"/><bf gi="11692" ue="35686" us="35686"/><bf gi="11693" ue="35693" us="35691"/><bf gi="11696" ue="35698" us="35695"/><bf gi="11700" ue="35700" us="35700"/><bf gi="11701" ue="35705" us="35702"/><bf gi="11705" ue="35713" us="35708"/><bf gi="11711" ue="35717" us="35715"/><bf gi="11714" ue="35728" us="35722"/><bf gi="11721" ue="35734" us="35730"/><bf gi="11726" ue="35738" us="35737"/><bf gi="11728" ue="35740" us="35740"/><bf gi="11729" ue="35743" us="35742"/><bf gi="11731" ue="35898" us="35895"/><bf gi="11735" ue="35903" us="35901"/><bf gi="11738" ue="35905" us="35905"/><bf gi="11739" ue="35916" us!
="35909"/><bf gi="11747" ue="35921" us="35918"/><bf gi="11751" ue="359
25" us="35923"/><bf gi="11754" ue="35931" us="35927"/><bf gi="11759" ue="35933" us="35933"/><bf gi="11760" ue="35940" us="35937"/><bf gi="11764" ue="35942" us="35942"/><bf gi="11765" ue="35949" us="35944"/><bf gi="11771" ue="35955" us="35955"/><bf gi="11772" ue="35958" us="35957"/><bf gi="11774" ue="35964" us="35960"/><bf gi="11779" ue="35966" us="35966"/><bf gi="11780" ue="35970" us="35970"/><bf gi="11781" ue="35975" us="35973"/><bf gi="11784" ue="35982" us="35977"/><bf gi="11790" ue="35984" us="35984"/><bf gi="11791" ue="35988" us="35986"/><bf gi="11794" ue="35993" us="35992"/><bf gi="11796" ue="35998" us="35995"/><bf gi="11800" ue="36002" us="36000"/><bf gi="11803" ue="36004" us="36004"/><bf gi="11804" ue="36016" us="36007"/><bf gi="11814" ue="36020" us="36018"/><bf gi="11817" ue="36029" us="36022"/><bf gi="11825" ue="36043" us="36031"/><bf gi="11838" ue="36047" us="36045"/><bf gi="11841" ue="36049" us="36049"/><bf gi="11842" ue="36051" us="36051"/><bf gi="11843" ue="3605!
4" us="36053"/><bf gi="11845" ue="36062" us="36057"/><bf gi="11851" ue="36068" us="36064"/><bf gi="11856" ue="36070" us="36070"/><bf gi="11857" ue="36072" us="36072"/><bf gi="11858" ue="36074" us="36074"/><bf gi="11859" ue="36077" us="36076"/><bf gi="11861" ue="36080" us="36079"/><bf gi="11863" ue="36082" us="36082"/><bf gi="11864" ue="36085" us="36084"/><bf gi="11866" ue="36088" us="36087"/><bf gi="11868" ue="36095" us="36090"/><bf gi="11874" ue="36097" us="36097"/><bf gi="11875" ue="36101" us="36099"/><bf gi="11878" ue="36107" us="36103"/><bf gi="11883" ue="36109" us="36109"/><bf gi="11884" ue="36112" us="36111"/><bf gi="11886" ue="36116" us="36114"/><bf gi="11889" ue="36119" us="36118"/><bf gi="11891" ue="36123" us="36123"/><bf gi="11892" ue="36199" us="36196"/><bf gi="11896" ue="36201" us="36201"/><bf gi="11897" ue="36206" us="36203"/><bf gi="11901" ue="36209" us="36208"/><bf gi="11903" ue="36212" us="36211"/><bf gi="11905" ue="36215" us="36214"/><bf gi="11907" ue="3622!
3" us="36223"/><bf gi="11908" ue="36226" us="36225"/><bf gi="11910" ue
="36229" us="36228"/><bf gi="11912" ue="36232" us="36232"/><bf gi="11913" ue="36234" us="36234"/><bf gi="11914" ue="36237" us="36237"/><bf gi="11915" ue="36241" us="36240"/><bf gi="11917" ue="36245" us="36245"/><bf gi="11918" ue="36249" us="36249"/><bf gi="11919" ue="36256" us="36254"/><bf gi="11922" ue="36259" us="36259"/><bf gi="11923" ue="36262" us="36262"/><bf gi="11924" ue="36264" us="36264"/><bf gi="11925" ue="36268" us="36267"/><bf gi="11927" ue="36271" us="36271"/><bf gi="11928" ue="36275" us="36274"/><bf gi="11930" ue="36277" us="36277"/><bf gi="11931" ue="36279" us="36279"/><bf gi="11932" ue="36284" us="36281"/><bf gi="11936" ue="36286" us="36286"/><bf gi="11937" ue="36288" us="36288"/><bf gi="11938" ue="36290" us="36290"/><bf gi="11939" ue="36296" us="36293"/><bf gi="11943" ue="36300" us="36298"/><bf gi="11946" ue="36303" us="36302"/><bf gi="11948" ue="36305" us="36305"/><bf gi="11949" ue="36311" us="36308"/><bf gi="11953" ue="36315" us="36313"/><bf gi="11956" ue=!
"36317" us="36317"/><bf gi="11957" ue="36319" us="36319"/><bf gi="11958" ue="36321" us="36321"/><bf gi="11959" ue="36325" us="36323"/><bf gi="11962" ue="36328" us="36327"/><bf gi="11964" ue="36332" us="36330"/><bf gi="11967" ue="36341" us="36335"/><bf gi="11974" ue="36349" us="36348"/><bf gi="11976" ue="36351" us="36351"/><bf gi="11977" ue="36353" us="36353"/><bf gi="11978" ue="36358" us="36356"/><bf gi="11981" ue="36363" us="36360"/><bf gi="11985" ue="36369" us="36367"/><bf gi="11988" ue="36372" us="36372"/><bf gi="11989" ue="36374" us="36374"/><bf gi="11990" ue="36387" us="36381"/><bf gi="11997" ue="36391" us="36390"/><bf gi="11999" ue="36394" us="36394"/><bf gi="12000" ue="36401" us="36400"/><bf gi="12002" ue="36409" us="36403"/><bf gi="12009" ue="36413" us="36413"/><bf gi="12010" ue="36418" us="36416"/><bf gi="12013" ue="36420" us="36420"/><bf gi="12014" ue="36432" us="36423"/><bf gi="12024" ue="36437" us="36436"/><bf gi="12026" ue="36441" us="36441"/><bf gi="12027" ue=!
"36452" us="36443"/><bf gi="12037" ue="36457" us="36457"/><bf gi="1203
8" ue="36461" us="36460"/><bf gi="12040" ue="36466" us="36463"/><bf gi="12044" ue="36468" us="36468"/><bf gi="12045" ue="36470" us="36470"/><bf gi="12046" ue="36476" us="36473"/><bf gi="12050" ue="36485" us="36481"/><bf gi="12055" ue="36487" us="36487"/><bf gi="12056" ue="36491" us="36489"/><bf gi="12059" ue="36493" us="36493"/><bf gi="12060" ue="36501" us="36496"/><bf gi="12066" ue="36507" us="36505"/><bf gi="12069" ue="36510" us="36509"/><bf gi="12071" ue="36514" us="36513"/><bf gi="12073" ue="36519" us="36519"/><bf gi="12074" ue="36529" us="36521"/><bf gi="12083" ue="36531" us="36531"/><bf gi="12084" ue="36533" us="36533"/><bf gi="12085" ue="36539" us="36538"/><bf gi="12087" ue="36542" us="36542"/><bf gi="12088" ue="36545" us="36544"/><bf gi="12090" ue="36552" us="36547"/><bf gi="12096" ue="36557" us="36554"/><bf gi="12100" ue="36559" us="36559"/><bf gi="12101" ue="36562" us="36561"/><bf gi="12103" ue="36564" us="36564"/><bf gi="12104" ue="36572" us="36571"/><bf gi="12106!
" ue="36575" us="36575"/><bf gi="12107" ue="36579" us="36578"/><bf gi="12109" ue="36584" us="36584"/><bf gi="12110" ue="36587" us="36587"/><bf gi="12111" ue="36590" us="36589"/><bf gi="12113" ue="36593" us="36592"/><bf gi="12115" ue="36606" us="36599"/><bf gi="12123" ue="36608" us="36608"/><bf gi="12124" ue="36611" us="36610"/><bf gi="12126" ue="36613" us="36613"/><bf gi="12127" ue="36618" us="36615"/><bf gi="12131" ue="36620" us="36620"/><bf gi="12132" ue="36624" us="36623"/><bf gi="12134" ue="36633" us="36626"/><bf gi="12142" ue="36641" us="36635"/><bf gi="12149" ue="36643" us="36643"/><bf gi="12150" ue="36650" us="36645"/><bf gi="12156" ue="36655" us="36652"/><bf gi="12160" ue="36667" us="36659"/><bf gi="12169" ue="36679" us="36670"/><bf gi="12179" ue="36681" us="36681"/><bf gi="12180" ue="36687" us="36684"/><bf gi="12184" ue="36693" us="36689"/><bf gi="12189" ue="36696" us="36695"/><bf gi="12191" ue="36703" us="36700"/><bf gi="12195" ue="36709" us="36705"/><bf gi="12200!
" ue="36769" us="36763"/><bf gi="12207" ue="36776" us="36771"/><bf gi=
"12213" ue="36786" us="36781"/><bf gi="12219" ue="36792" us="36789"/><bf gi="12223" ue="36796" us="36794"/><bf gi="12226" ue="36802" us="36798"/><bf gi="12231" ue="36806" us="36804"/><bf gi="12234" ue="36811" us="36810"/><bf gi="12236" ue="36814" us="36813"/><bf gi="12238" ue="36821" us="36816"/><bf gi="12244" ue="36826" us="36826"/><bf gi="12245" ue="36832" us="36832"/><bf gi="12246" ue="36838" us="36834"/><bf gi="12251" ue="36843" us="36840"/><bf gi="12255" ue="36849" us="36845"/><bf gi="12260" ue="36859" us="36852"/><bf gi="12268" ue="36862" us="36861"/><bf gi="12270" ue="36870" us="36864"/><bf gi="12277" ue="36872" us="36872"/><bf gi="12278" ue="36881" us="36875"/><bf gi="12285" ue="36891" us="36883"/><bf gi="12294" ue="36899" us="36893"/><bf gi="12301" ue="36906" us="36903"/><bf gi="12305" ue="36911" us="36908"/><bf gi="12309" ue="36921" us="36913"/><bf gi="12318" ue="36924" us="36924"/><bf gi="12319" ue="36927" us="36926"/><bf gi="12321" ue="36933" us="36929"/><bf gi="!
12326" ue="36935" us="36935"/><bf gi="12327" ue="36950" us="36937"/><bf gi="12341" ue="36953" us="36952"/><bf gi="12343" ue="36958" us="36955"/><bf gi="12347" ue="36963" us="36960"/><bf gi="12351" ue="36969" us="36965"/><bf gi="12356" ue="36976" us="36972"/><bf gi="12361" ue="36978" us="36978"/><bf gi="12362" ue="36986" us="36980"/><bf gi="12369" ue="36989" us="36988"/><bf gi="12371" ue="36997" us="36991"/><bf gi="12378" ue="37004" us="36999"/><bf gi="12384" ue="37009" us="37006"/><bf gi="12388" ue="37013" us="37013"/><bf gi="12389" ue="37017" us="37015"/><bf gi="12392" ue="37019" us="37019"/><bf gi="12393" ue="37027" us="37024"/><bf gi="12397" ue="37030" us="37029"/><bf gi="12399" ue="37032" us="37032"/><bf gi="12400" ue="37034" us="37034"/><bf gi="12401" ue="37046" us="37039"/><bf gi="12409" ue="37048" us="37048"/><bf gi="12410" ue="37054" us="37053"/><bf gi="12412" ue="37057" us="37057"/><bf gi="12413" ue="37061" us="37059"/><bf gi="12416" ue="37064" us="37063"/><bf gi="!
12418" ue="37066" us="37066"/><bf gi="12419" ue="37068" us="37068"/><b
f gi="12420" ue="37070" us="37070"/><bf gi="12421" ue="37074" us="37074"/><bf gi="12422" ue="37077" us="37077"/><bf gi="12423" ue="37081" us="37079"/><bf gi="12426" ue="37087" us="37083"/><bf gi="12431" ue="37090" us="37089"/><bf gi="12433" ue="37093" us="37092"/><bf gi="12435" ue="37096" us="37096"/><bf gi="12436" ue="37099" us="37099"/><bf gi="12437" ue="37101" us="37101"/><bf gi="12438" ue="37104" us="37103"/><bf gi="12440" ue="37111" us="37108"/><bf gi="12444" ue="37120" us="37117"/><bf gi="12448" ue="37122" us="37122"/><bf gi="12449" ue="37126" us="37124"/><bf gi="12452" ue="37128" us="37128"/><bf gi="12453" ue="37133" us="37133"/><bf gi="12454" ue="37136" us="37136"/><bf gi="12455" ue="37138" us="37138"/><bf gi="12456" ue="37146" us="37140"/><bf gi="12463" ue="37148" us="37148"/><bf gi="12464" ue="37150" us="37150"/><bf gi="12465" ue="37152" us="37152"/><bf gi="12466" ue="37155" us="37154"/><bf gi="12468" ue="37157" us="37157"/><bf gi="12469" ue="37159" us="37159"/><bf!
gi="12470" ue="37161" us="37161"/><bf gi="12471" ue="37170" us="37165"/><bf gi="12477" ue="37172" us="37172"/><bf gi="12478" ue="37175" us="37174"/><bf gi="12480" ue="37178" us="37177"/><bf gi="12482" ue="37181" us="37180"/><bf gi="12484" ue="37187" us="37187"/><bf gi="12485" ue="37199" us="37191"/><bf gi="12494" ue="37204" us="37202"/><bf gi="12497" ue="37211" us="37206"/><bf gi="12503" ue="37221" us="37217"/><bf gi="12508" ue="37223" us="37223"/><bf gi="12509" ue="37226" us="37225"/><bf gi="12511" ue="37229" us="37228"/><bf gi="12513" ue="37237" us="37234"/><bf gi="12517" ue="37243" us="37239"/><bf gi="12522" ue="37251" us="37249"/><bf gi="12525" ue="37255" us="37253"/><bf gi="12528" ue="37259" us="37257"/><bf gi="12531" ue="37262" us="37261"/><bf gi="12533" ue="37269" us="37264"/><bf gi="12539" ue="37272" us="37271"/><bf gi="12541" ue="37276" us="37276"/><bf gi="12542" ue="37278" us="37278"/><bf gi="12543" ue="37282" us="37281"/><bf gi="12545" ue="37284" us="37284"/><bf!
gi="12546" ue="37286" us="37286"/><bf gi="12547" ue="37288" us="37288
"/><bf gi="12548" ue="37302" us="37290"/><bf gi="12561" ue="37304" us="37304"/><bf gi="12562" ue="37309" us="37306"/><bf gi="12566" ue="37315" us="37311"/><bf gi="12571" ue="37321" us="37317"/><bf gi="12576" ue="37329" us="37323"/><bf gi="12583" ue="37332" us="37331"/><bf gi="12585" ue="37343" us="37334"/><bf gi="12595" ue="37345" us="37345"/><bf gi="12596" ue="37351" us="37347"/><bf gi="12601" ue="37354" us="37353"/><bf gi="12603" ue="37361" us="37356"/><bf gi="12609" ue="37367" us="37365"/><bf gi="12612" ue="37369" us="37369"/><bf gi="12613" ue="37373" us="37371"/><bf gi="12616" ue="37377" us="37375"/><bf gi="12619" ue="37383" us="37380"/><bf gi="12623" ue="37386" us="37385"/><bf gi="12625" ue="37390" us="37388"/><bf gi="12628" ue="37398" us="37392"/><bf gi="12635" ue="37400" us="37400"/><bf gi="12636" ue="37406" us="37404"/><bf gi="12639" ue="37414" us="37411"/><bf gi="12643" ue="37417" us="37416"/><bf gi="12645" ue="37420" us="37420"/><bf gi="12646" ue="37424" us="37422"!
/><bf gi="12649" ue="37434" us="37427"/><bf gi="12657" ue="37436" us="37436"/><bf gi="12658" ue="37440" us="37438"/><bf gi="12661" ue="37451" us="37442"/><bf gi="12671" ue="37457" us="37453"/><bf gi="12676" ue="37470" us="37463"/><bf gi="12684" ue="37474" us="37472"/><bf gi="12687" ue="37481" us="37476"/><bf gi="12693" ue="37489" us="37486"/><bf gi="12697" ue="37497" us="37493"/><bf gi="12702" ue="37504" us="37499"/><bf gi="12708" ue="37507" us="37507"/><bf gi="12709" ue="37509" us="37509"/><bf gi="12710" ue="37514" us="37512"/><bf gi="12713" ue="37518" us="37517"/><bf gi="12715" ue="37523" us="37521"/><bf gi="12718" ue="37532" us="37525"/><bf gi="12726" ue="37536" us="37535"/><bf gi="12728" ue="37541" us="37540"/><bf gi="12730" ue="37544" us="37543"/><bf gi="12732" ue="37547" us="37547"/><bf gi="12733" ue="37549" us="37549"/><bf gi="12734" ue="37551" us="37551"/><bf gi="12735" ue="37554" us="37554"/><bf gi="12736" ue="37565" us="37558"/><bf gi="12744" ue="37571" us="37567"!
/><bf gi="12749" ue="37576" us="37573"/><bf gi="12753" ue="37584" us="
37579"/><bf gi="12759" ue="37587" us="37586"/><bf gi="12761" ue="37589" us="37589"/><bf gi="12762" ue="37593" us="37591"/><bf gi="12765" ue="37597" us="37596"/><bf gi="12767" ue="37601" us="37599"/><bf gi="12770" ue="37605" us="37603"/><bf gi="12773" ue="37610" us="37607"/><bf gi="12777" ue="37614" us="37612"/><bf gi="12780" ue="37616" us="37616"/><bf gi="12781" ue="37619" us="37618"/><bf gi="12783" ue="37628" us="37624"/><bf gi="12788" ue="37632" us="37631"/><bf gi="12790" ue="37634" us="37634"/><bf gi="12791" ue="37638" us="37638"/><bf gi="12792" ue="37640" us="37640"/><bf gi="12793" ue="37645" us="37645"/><bf gi="12794" ue="37649" us="37647"/><bf gi="12797" ue="37653" us="37652"/><bf gi="12799" ue="37658" us="37656"/><bf gi="12802" ue="37676" us="37660"/><bf gi="12819" ue="37679" us="37678"/><bf gi="12821" ue="37687" us="37682"/><bf gi="12827" ue="37691" us="37690"/><bf gi="12829" ue="37700" us="37700"/><bf gi="12830" ue="37705" us="37703"/><bf gi="12833" ue="37707" us="3!
7707"/><bf gi="12834" ue="37709" us="37709"/><bf gi="12835" ue="37714" us="37712"/><bf gi="12838" ue="37720" us="37716"/><bf gi="12843" ue="37724" us="37722"/><bf gi="12846" ue="37726" us="37726"/><bf gi="12847" ue="37728" us="37728"/><bf gi="12848" ue="37733" us="37732"/><bf gi="12850" ue="37735" us="37735"/><bf gi="12851" ue="37738" us="37737"/><bf gi="12853" ue="37745" us="37740"/><bf gi="12859" ue="37750" us="37747"/><bf gi="12863" ue="37754" us="37754"/><bf gi="12864" ue="37762" us="37756"/><bf gi="12871" ue="37768" us="37768"/><bf gi="12872" ue="37773" us="37770"/><bf gi="12876" ue="37775" us="37775"/><bf gi="12877" ue="37778" us="37778"/><bf gi="12878" ue="37784" us="37780"/><bf gi="12883" ue="37787" us="37786"/><bf gi="12885" ue="37790" us="37790"/><bf gi="12886" ue="37793" us="37793"/><bf gi="12887" ue="37796" us="37795"/><bf gi="12889" ue="37801" us="37798"/><bf gi="12893" ue="37806" us="37803"/><bf gi="12897" ue="37808" us="37808"/><bf gi="12898" ue="37814" us="3!
7812"/><bf gi="12901" ue="37818" us="37817"/><bf gi="12903" ue="37825"
us="37825"/><bf gi="12904" ue="37837" us="37827"/><bf gi="12915" ue="37841" us="37840"/><bf gi="12917" ue="37843" us="37843"/><bf gi="12918" ue="37849" us="37846"/><bf gi="12922" ue="37855" us="37852"/><bf gi="12926" ue="37858" us="37857"/><bf gi="12928" ue="37864" us="37860"/><bf gi="12933" ue="37883" us="37879"/><bf gi="12938" ue="37885" us="37885"/><bf gi="12939" ue="37892" us="37889"/><bf gi="12943" ue="37897" us="37895"/><bf gi="12946" ue="37904" us="37901"/><bf gi="12950" ue="37914" us="37907"/><bf gi="12958" ue="37919" us="37919"/><bf gi="12959" ue="37921" us="37921"/><bf gi="12960" ue="37931" us="37931"/><bf gi="12961" ue="37935" us="37934"/><bf gi="12963" ue="37942" us="37937"/><bf gi="12969" ue="37944" us="37944"/><bf gi="12970" ue="37947" us="37946"/><bf gi="12972" ue="37949" us="37949"/><bf gi="12973" ue="37951" us="37951"/><bf gi="12974" ue="37953" us="37953"/><bf gi="12975" ue="37957" us="37955"/><bf gi="12978" ue="37960" us="37960"/><bf gi="12979" ue="37962" !
us="37962"/><bf gi="12980" ue="37964" us="37964"/><bf gi="12981" ue="37971" us="37969"/><bf gi="12984" ue="37973" us="37973"/><bf gi="12985" ue="37980" us="37977"/><bf gi="12989" ue="37987" us="37982"/><bf gi="12995" ue="37992" us="37992"/><bf gi="12996" ue="37995" us="37994"/><bf gi="12998" ue="38002" us="37997"/><bf gi="13004" ue="38005" us="38005"/><bf gi="13005" ue="38007" us="38007"/><bf gi="13006" ue="38015" us="38012"/><bf gi="13010" ue="38017" us="38017"/><bf gi="13011" ue="38020" us="38019"/><bf gi="13013" ue="38265" us="38263"/><bf gi="13016" ue="38270" us="38270"/><bf gi="13017" ue="38272" us="38272"/><bf gi="13018" ue="38276" us="38274"/><bf gi="13021" ue="38287" us="38279"/><bf gi="13030" ue="38292" us="38289"/><bf gi="13034" ue="38294" us="38294"/><bf gi="13035" ue="38297" us="38296"/><bf gi="13037" ue="38313" us="38301"/><bf gi="13050" ue="38317" us="38315"/><bf gi="13053" ue="38322" us="38322"/><bf gi="13054" ue="38324" us="38324"/><bf gi="13055" ue="38326" !
us="38326"/><bf gi="13056" ue="38335" us="38329"/><bf gi="13063" ue="3
8339" us="38339"/><bf gi="13064" ue="38349" us="38342"/><bf gi="13072" ue="38358" us="38352"/><bf gi="13079" ue="38362" us="38360"/><bf gi="13082" ue="38370" us="38364"/><bf gi="13089" ue="38374" us="38372"/><bf gi="13092" ue="38430" us="38428"/><bf gi="13095" ue="38434" us="38433"/><bf gi="13097" ue="38438" us="38436"/><bf gi="13100" ue="38440" us="38440"/><bf gi="13101" ue="38442" us="38442"/><bf gi="13102" ue="38444" us="38444"/><bf gi="13103" ue="38447" us="38446"/><bf gi="13105" ue="38451" us="38449"/><bf gi="13108" ue="38461" us="38455"/><bf gi="13115" ue="38466" us="38463"/><bf gi="13119" ue="38468" us="38468"/><bf gi="13120" ue="38477" us="38475"/><bf gi="13123" ue="38480" us="38479"/><bf gi="13125" ue="38482" us="38482"/><bf gi="13126" ue="38484" us="38484"/><bf gi="13127" ue="38488" us="38486"/><bf gi="13130" ue="38495" us="38491"/><bf gi="13135" ue="38502" us="38497"/><bf gi="13141" ue="38506" us="38506"/><bf gi="13142" ue="38508" us="38508"/><bf gi="13143" ue="38!
510" us="38510"/><bf gi="13144" ue="38512" us="38512"/><bf gi="13145" ue="38520" us="38514"/><bf gi="13152" ue="38527" us="38522"/><bf gi="13158" ue="38534" us="38529"/><bf gi="13164" ue="38539" us="38536"/><bf gi="13168" ue="38543" us="38541"/><bf gi="13171" ue="38545" us="38545"/><bf gi="13172" ue="38557" us="38548"/><bf gi="13182" ue="38560" us="38559"/><bf gi="13184" ue="38570" us="38563"/><bf gi="13192" ue="38580" us="38574"/><bf gi="13199" ue="38588" us="38582"/><bf gi="13206" ue="38593" us="38592"/><bf gi="13208" ue="38599" us="38596"/><bf gi="13212" ue="38606" us="38601"/><bf gi="13218" ue="38610" us="38609"/><bf gi="13220" ue="38614" us="38613"/><bf gi="13222" ue="38623" us="38616"/><bf gi="13230" ue="38627" us="38626"/><bf gi="13232" ue="38635" us="38632"/><bf gi="13236" ue="38642" us="38639"/><bf gi="13240" ue="38647" us="38646"/><bf gi="13242" ue="38651" us="38649"/><bf gi="13245" ue="38656" us="38656"/><bf gi="13246" ue="38666" us="38658"/><bf gi="13255" ue="38!
671" us="38669"/><bf gi="13258" ue="38673" us="38673"/><bf gi="13259"
ue="38675" us="38675"/><bf gi="13260" ue="38678" us="38678"/><bf gi="13261" ue="38686" us="38681"/><bf gi="13267" ue="38692" us="38689"/><bf gi="13271" ue="38696" us="38695"/><bf gi="13273" ue="38698" us="38698"/><bf gi="13274" ue="38707" us="38704"/><bf gi="13278" ue="38713" us="38712"/><bf gi="13280" ue="38715" us="38715"/><bf gi="13281" ue="38718" us="38717"/><bf gi="13283" ue="38724" us="38721"/><bf gi="13287" ue="38726" us="38726"/><bf gi="13288" ue="38730" us="38728"/><bf gi="13291" ue="38735" us="38733"/><bf gi="13294" ue="38738" us="38737"/><bf gi="13296" ue="38748" us="38741"/><bf gi="13304" ue="38750" us="38750"/><bf gi="13305" ue="38756" us="38752"/><bf gi="13310" ue="38763" us="38758"/><bf gi="13316" ue="38766" us="38765"/><bf gi="13318" ue="38769" us="38769"/><bf gi="13319" ue="38772" us="38771"/><bf gi="13321" ue="38781" us="38774"/><bf gi="13329" ue="38785" us="38783"/><bf gi="13332" ue="38790" us="38788"/><bf gi="13335" ue="38793" us="38793"/><bf gi="13336" u!
e="38795" us="38795"/><bf gi="13337" ue="38797" us="38797"/><bf gi="13338" ue="38800" us="38799"/><bf gi="13340" ue="38810" us="38805"/><bf gi="13346" ue="38812" us="38812"/><bf gi="13347" ue="38816" us="38814"/><bf gi="13350" ue="38819" us="38818"/><bf gi="13352" ue="38822" us="38822"/><bf gi="13353" ue="38824" us="38824"/><bf gi="13354" ue="38830" us="38827"/><bf gi="13358" ue="38838" us="38833"/><bf gi="13364" ue="38842" us="38840"/><bf gi="13367" ue="38844" us="38844"/><bf gi="13368" ue="38847" us="38846"/><bf gi="13370" ue="38849" us="38849"/><bf gi="13371" ue="38862" us="38851"/><bf gi="13383" ue="38865" us="38864"/><bf gi="13385" ue="38868" us="38867"/><bf gi="13387" ue="38873" us="38871"/><bf gi="13390" ue="38878" us="38875"/><bf gi="13394" ue="38881" us="38880"/><bf gi="13396" ue="38884" us="38884"/><bf gi="13397" ue="38895" us="38893"/><bf gi="13400" ue="38904" us="38897"/><bf gi="13408" ue="38907" us="38906"/><bf gi="13410" ue="38911" us="38911"/><bf gi="13411" u!
e="38915" us="38913"/><bf gi="13414" ue="38920" us="38917"/><bf gi="13
418" ue="38922" us="38922"/><bf gi="13419" ue="38932" us="38924"/><bf gi="13428" ue="38938" us="38934"/><bf gi="13433" ue="38940" us="38940"/><bf gi="13434" ue="38942" us="38942"/><bf gi="13435" ue="38945" us="38944"/><bf gi="13437" ue="38950" us="38947"/><bf gi="13441" ue="38960" us="38955"/><bf gi="13447" ue="38965" us="38962"/><bf gi="13451" ue="38968" us="38967"/><bf gi="13453" ue="38974" us="38971"/><bf gi="13457" ue="38980" us="38980"/><bf gi="13458" ue="38983" us="38982"/><bf gi="13460" ue="38991" us="38986"/><bf gi="13466" ue="39003" us="38993"/><bf gi="13477" ue="39006" us="39006"/><bf gi="13478" ue="39011" us="39010"/><bf gi="13480" ue="39015" us="39013"/><bf gi="13483" ue="39020" us="39018"/><bf gi="13486" ue="39025" us="39023"/><bf gi="13489" ue="39028" us="39027"/><bf gi="13491" ue="39080" us="39080"/><bf gi="13492" ue="39083" us="39082"/><bf gi="13494" ue="39089" us="39085"/><bf gi="13499" ue="39092" us="39092"/><bf gi="13500" ue="39096" us="39094"/><bf gi="135!
03" ue="39099" us="39098"/><bf gi="13505" ue="39103" us="39103"/><bf gi="13506" ue="39110" us="39106"/><bf gi="13511" ue="39112" us="39112"/><bf gi="13512" ue="39116" us="39116"/><bf gi="13513" ue="39132" us="39131"/><bf gi="13515" ue="39135" us="39135"/><bf gi="13516" ue="39139" us="39137"/><bf gi="13519" ue="39143" us="39141"/><bf gi="13522" ue="39147" us="39145"/><bf gi="13525" ue="39151" us="39149"/><bf gi="13528" ue="39156" us="39154"/><bf gi="13531" ue="39158" us="39158"/><bf gi="13532" ue="39166" us="39164"/><bf gi="13535" ue="39171" us="39170"/><bf gi="13537" ue="39173" us="39173"/><bf gi="13538" ue="39178" us="39175"/><bf gi="13542" ue="39180" us="39180"/><bf gi="13543" ue="39192" us="39184"/><bf gi="13552" ue="39202" us="39194"/><bf gi="13561" ue="39204" us="39204"/><bf gi="13562" ue="39208" us="39206"/><bf gi="13565" ue="39212" us="39211"/><bf gi="13567" ue="39214" us="39214"/><bf gi="13568" ue="39221" us="39217"/><bf gi="13573" ue="39230" us="39225"/><bf gi="135!
79" ue="39234" us="39232"/><bf gi="13582" ue="39241" us="39237"/><bf g
i="13587" ue="39246" us="39243"/><bf gi="13591" ue="39250" us="39248"/><bf gi="13594" ue="39253" us="39252"/><bf gi="13596" ue="39257" us="39255"/><bf gi="13599" ue="39260" us="39259"/><bf gi="13601" ue="39264" us="39262"/><bf gi="13604" ue="39321" us="39318"/><bf gi="13608" ue="39323" us="39323"/><bf gi="13609" ue="39327" us="39325"/><bf gi="13612" ue="39334" us="39333"/><bf gi="13614" ue="39336" us="39336"/><bf gi="13615" ue="39342" us="39340"/><bf gi="13618" ue="39349" us="39344"/><bf gi="13624" ue="39354" us="39353"/><bf gi="13626" ue="39357" us="39356"/><bf gi="13628" ue="39359" us="39359"/><bf gi="13629" ue="39361" us="39361"/><bf gi="13630" ue="39366" us="39363"/><bf gi="13634" ue="39369" us="39368"/><bf gi="13636" ue="39381" us="39376"/><bf gi="13642" ue="39391" us="39384"/><bf gi="13650" ue="39394" us="39394"/><bf gi="13651" ue="39399" us="39399"/><bf gi="13652" ue="39406" us="39402"/><bf gi="13657" ue="39410" us="39408"/><bf gi="13660" ue="39413" us="39412"/><bf gi!
="13662" ue="39417" us="39416"/><bf gi="13664" ue="39419" us="39419"/><bf gi="13665" ue="39423" us="39421"/><bf gi="13668" ue="39429" us="39425"/><bf gi="13673" ue="39436" us="39435"/><bf gi="13675" ue="39443" us="39438"/><bf gi="13681" ue="39446" us="39446"/><bf gi="13682" ue="39449" us="39449"/><bf gi="13683" ue="39454" us="39454"/><bf gi="13684" ue="39456" us="39456"/><bf gi="13685" ue="39460" us="39458"/><bf gi="13688" ue="39464" us="39463"/><bf gi="13690" ue="39467" us="39467"/><bf gi="13691" ue="39470" us="39469"/><bf gi="13693" ue="39472" us="39472"/><bf gi="13694" ue="39475" us="39475"/><bf gi="13695" ue="39480" us="39477"/><bf gi="13699" ue="39486" us="39486"/><bf gi="13700" ue="39493" us="39488"/><bf gi="13706" ue="39495" us="39495"/><bf gi="13707" ue="39502" us="39498"/><bf gi="13712" ue="39505" us="39505"/><bf gi="13713" ue="39511" us="39508"/><bf gi="13717" ue="39515" us="39514"/><bf gi="13719" ue="39517" us="39517"/><bf gi="13720" ue="39519" us="39519"/><bf gi!
="13721" ue="39522" us="39522"/><bf gi="13722" ue="39525" us="39524"/>
<bf gi="13724" ue="39531" us="39529"/><bf gi="13727" ue="39592" us="39592"/><bf gi="13728" ue="39594" us="39594"/><bf gi="13729" ue="39600" us="39596"/><bf gi="13734" ue="39602" us="39602"/><bf gi="13735" ue="39606" us="39604"/><bf gi="13738" ue="39609" us="39608"/><bf gi="13740" ue="39612" us="39611"/><bf gi="13742" ue="39617" us="39614"/><bf gi="13746" ue="39620" us="39619"/><bf gi="13748" ue="39622" us="39622"/><bf gi="13749" ue="39624" us="39624"/><bf gi="13750" ue="39641" us="39630"/><bf gi="13762" ue="39644" us="39643"/><bf gi="13764" ue="39648" us="39646"/><bf gi="13767" ue="39655" us="39650"/><bf gi="13773" ue="39663" us="39657"/><bf gi="13780" ue="39669" us="39665"/><bf gi="13785" ue="39671" us="39671"/><bf gi="13786" ue="39675" us="39673"/><bf gi="13789" ue="39677" us="39677"/><bf gi="13790" ue="39686" us="39679"/><bf gi="13798" ue="39689" us="39688"/><bf gi="13800" ue="39694" us="39691"/><bf gi="13804" ue="39696" us="39696"/><bf gi="13805" ue="39698" us="39698"/><!
bf gi="13806" ue="39702" us="39702"/><bf gi="13807" ue="39708" us="39704"/><bf gi="13812" ue="39712" us="39711"/><bf gi="13814" ue="39715" us="39714"/><bf gi="13816" ue="39723" us="39717"/><bf gi="13823" ue="39727" us="39725"/><bf gi="13826" ue="39733" us="39729"/><bf gi="13831" ue="39735" us="39735"/><bf gi="13832" ue="39741" us="39737"/><bf gi="13837" ue="39749" us="39745"/><bf gi="13842" ue="39752" us="39752"/><bf gi="13843" ue="39759" us="39755"/><bf gi="13848" ue="39761" us="39761"/><bf gi="13849" ue="39768" us="39764"/><bf gi="13854" ue="39771" us="39770"/><bf gi="13856" ue="39774" us="39774"/><bf gi="13857" ue="39777" us="39777"/><bf gi="13858" ue="39779" us="39779"/><bf gi="13859" ue="39782" us="39781"/><bf gi="13861" ue="39784" us="39784"/><bf gi="13862" ue="39791" us="39786"/><bf gi="13868" ue="39797" us="39794"/><bf gi="13872" ue="39801" us="39799"/><bf gi="13875" ue="39808" us="39807"/><bf gi="13877" ue="39815" us="39811"/><bf gi="13882" ue="39819" us="39817"/><!
bf gi="13885" ue="39828" us="39821"/><bf gi="13893" ue="39831" us="398
30"/><bf gi="13895" ue="39834" us="39834"/><bf gi="13896" ue="39840" us="39837"/><bf gi="13900" ue="39854" us="39846"/><bf gi="13909" ue="39858" us="39856"/><bf gi="13912" ue="39860" us="39860"/><bf gi="13913" ue="39865" us="39863"/><bf gi="13916" ue="39868" us="39867"/><bf gi="13918" ue="39873" us="39870"/><bf gi="13922" ue="39882" us="39878"/><bf gi="13927" ue="39890" us="39886"/><bf gi="13932" ue="39892" us="39892"/><bf gi="13933" ue="39896" us="39894"/><bf gi="13936" ue="39899" us="39899"/><bf gi="13937" ue="39901" us="39901"/><bf gi="13938" ue="39903" us="39903"/><bf gi="13939" ue="39909" us="39905"/><bf gi="13944" ue="39912" us="39911"/><bf gi="13946" ue="39915" us="39914"/><bf gi="13948" ue="39923" us="39919"/><bf gi="13953" ue="39925" us="39925"/><bf gi="13954" ue="39930" us="39927"/><bf gi="13958" ue="39933" us="39933"/><bf gi="13959" ue="39936" us="39935"/><bf gi="13961" ue="39938" us="39938"/><bf gi="13962" ue="39940" us="39940"/><bf gi="13963" ue="39942" us="3994!
2"/><bf gi="13964" ue="39949" us="39944"/><bf gi="13970" ue="39958" us="39951"/><bf gi="13978" ue="39964" us="39960"/><bf gi="13983" ue="39966" us="39966"/><bf gi="13984" ue="39978" us="39969"/><bf gi="13994" ue="39986" us="39981"/><bf gi="14000" ue="39991" us="39989"/><bf gi="14003" ue="39995" us="39993"/><bf gi="14006" ue="39998" us="39997"/><bf gi="14008" ue="40001" us="40001"/><bf gi="14009" ue="40010" us="40003"/><bf gi="14017" ue="40016" us="40014"/><bf gi="14020" ue="40020" us="40018"/><bf gi="14023" ue="40024" us="40022"/><bf gi="14026" ue="40032" us="40026"/><bf gi="14033" ue="40035" us="40035"/><bf gi="14034" ue="40043" us="40039"/><bf gi="14039" ue="40046" us="40046"/><bf gi="14040" ue="40048" us="40048"/><bf gi="14041" ue="40050" us="40050"/><bf gi="14042" ue="40056" us="40053"/><bf gi="14046" ue="40059" us="40059"/><bf gi="14047" ue="40167" us="40165"/><bf gi="14050" ue="40169" us="40169"/><bf gi="14051" ue="40172" us="40171"/><bf gi="14053" ue="40176" us="4017!
6"/><bf gi="14054" ue="40180" us="40178"/><bf gi="14057" ue="40183" us
="40182"/><bf gi="14059" ue="40185" us="40185"/><bf gi="14060" ue="40195" us="40194"/><bf gi="14062" ue="40201" us="40198"/><bf gi="14066" ue="40203" us="40203"/><bf gi="14067" ue="40206" us="40206"/><bf gi="14068" ue="40210" us="40209"/><bf gi="14070" ue="40213" us="40213"/><bf gi="14071" ue="40216" us="40215"/><bf gi="14073" ue="40223" us="40219"/><bf gi="14078" ue="40227" us="40227"/><bf gi="14079" ue="40230" us="40230"/><bf gi="14080" ue="40232" us="40232"/><bf gi="14081" ue="40236" us="40234"/><bf gi="14084" ue="40240" us="40239"/><bf gi="14086" ue="40244" us="40242"/><bf gi="14089" ue="40255" us="40250"/><bf gi="14095" ue="40264" us="40257"/><bf gi="14103" ue="40266" us="40266"/><bf gi="14104" ue="40273" us="40272"/><bf gi="14106" ue="40276" us="40275"/><bf gi="14108" ue="40281" us="40281"/><bf gi="14109" ue="40293" us="40284"/><bf gi="14119" ue="40300" us="40297"/><bf gi="14123" ue="40304" us="40303"/><bf gi="14125" ue="40306" us="40306"/><bf gi="14126" ue="40311" us=!
"40310"/><bf gi="14128" ue="40316" us="40314"/><bf gi="14131" ue="40318" us="40318"/><bf gi="14132" ue="40324" us="40323"/><bf gi="14134" ue="40327" us="40326"/><bf gi="14136" ue="40330" us="40329"/><bf gi="14138" ue="40335" us="40333"/><bf gi="14141" ue="40339" us="40338"/><bf gi="14143" ue="40344" us="40341"/><bf gi="14147" ue="40346" us="40346"/><bf gi="14148" ue="40353" us="40353"/><bf gi="14149" ue="40356" us="40356"/><bf gi="14150" ue="40364" us="40361"/><bf gi="14154" ue="40367" us="40366"/><bf gi="14156" ue="40370" us="40369"/><bf gi="14158" ue="40373" us="40372"/><bf gi="14160" ue="40380" us="40376"/><bf gi="14165" ue="40383" us="40383"/><bf gi="14166" ue="40388" us="40385"/><bf gi="14170" ue="40391" us="40390"/><bf gi="14172" ue="40394" us="40393"/><bf gi="14174" ue="40399" us="40399"/><bf gi="14175" ue="40407" us="40403"/><bf gi="14180" ue="40410" us="40409"/><bf gi="14182" ue="40416" us="40414"/><bf gi="14185" ue="40423" us="40421"/><bf gi="14188" ue="40425" us=!
"40425"/><bf gi="14189" ue="40427" us="40427"/><bf gi="14190" ue="4043
2" us="40429"/><bf gi="14194" ue="40436" us="40434"/><bf gi="14197" ue="40442" us="40440"/><bf gi="14200" ue="40446" us="40445"/><bf gi="14202" ue="40450" us="40450"/><bf gi="14203" ue="40455" us="40455"/><bf gi="14204" ue="40458" us="40458"/><bf gi="14205" ue="40462" us="40462"/><bf gi="14206" ue="40466" us="40464"/><bf gi="14209" ue="40470" us="40469"/><bf gi="14211" ue="40478" us="40473"/><bf gi="14217" ue="40565" us="40565"/><bf gi="14218" ue="40573" us="40568"/><bf gi="14224" ue="40581" us="40575"/><bf gi="14231" ue="40584" us="40583"/><bf gi="14233" ue="40588" us="40587"/><bf gi="14235" ue="40591" us="40590"/><bf gi="14237" ue="40595" us="40593"/><bf gi="14240" ue="40600" us="40597"/><bf gi="14244" ue="40603" us="40603"/><bf gi="14245" ue="40607" us="40605"/><bf gi="14248" ue="40614" us="40612"/><bf gi="14251" ue="40618" us="40616"/><bf gi="14254" ue="40624" us="40620"/><bf gi="14259" ue="40629" us="40627"/><bf gi="14262" ue="40636" us="40632"/><bf gi="14267" ue="40639!
" us="40638"/><bf gi="14269" ue="40644" us="40644"/><bf gi="14270" ue="40646" us="40646"/><bf gi="14271" ue="40648" us="40648"/><bf gi="14272" ue="40658" us="40651"/><bf gi="14280" ue="40661" us="40660"/><bf gi="14282" ue="40665" us="40664"/><bf gi="14284" ue="40672" us="40667"/><bf gi="14290" ue="40677" us="40676"/><bf gi="14292" ue="40680" us="40679"/><bf gi="14294" ue="40690" us="40684"/><bf gi="14301" ue="40697" us="40692"/><bf gi="14307" ue="40701" us="40699"/><bf gi="14310" ue="40703" us="40703"/><bf gi="14311" ue="40707" us="40706"/><bf gi="14313" ue="40713" us="40711"/><bf gi="14316" ue="40727" us="40718"/><bf gi="14326" ue="40731" us="40729"/><bf gi="14329" ue="40738" us="40735"/><bf gi="14333" ue="40742" us="40742"/><bf gi="14334" ue="40748" us="40746"/><bf gi="14337" ue="40751" us="40751"/><bf gi="14338" ue="40754" us="40753"/><bf gi="14340" ue="40756" us="40756"/><bf gi="14341" ue="40759" us="40759"/><bf gi="14342" ue="40767" us="40761"/><bf gi="14349" ue="40769!
" us="40769"/><bf gi="14350" ue="40775" us="40771"/><bf gi="14355" ue=
"40779" us="40778"/><bf gi="14357" ue="40783" us="40782"/><bf gi="14359" ue="40792" us="40786"/><bf gi="14366" ue="40794" us="40794"/><bf gi="14367" ue="40803" us="40797"/><bf gi="14374" ue="40810" us="40806"/><bf gi="14379" ue="40819" us="40812"/><bf gi="14387" ue="40823" us="40821"/><bf gi="14390" ue="40826" us="40826"/><bf gi="14391" ue="40829" us="40829"/><bf gi="14392" ue="40845" us="40845"/><bf gi="14393" ue="40850" us="40847"/><bf gi="14397" ue="40855" us="40852"/><bf gi="14401" ue="40862" us="40860"/><bf gi="14404" ue="40867" us="40864"/><bf gi="14408" ue="40869" us="40869"/><bf gi="14409" ue="63785" us="63785"/><bf gi="14410" ue="63964" us="63964"/><bf gi="14411" ue="64045" us="64014"/><bf gi="14443" ue="64260" us="64259"/><bf gi="14445" ue="65374" us="65281"/><bf gi="14539" ue="65439" us="65377"/><bf gi="14602" ue="65509" us="65504"/><bf gi="0" ue="65535" us="65535"/></bfranges><cid-widths start-index="0"><wx w="1000"/><wx w="0"/><wx w="333"/><wx w="500"/><wx w="50!
0"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="50!
0"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><
wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="490"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="875"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="!
500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="812"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="718"/><wx w="515"/><wx w="718"/><wx w="515"/><wx w="718"/><wx w="515"/><wx w="640"/><wx w="515"/><wx w="640"/><wx w="515"/><wx w="500"/><wx w="500"/><wx w="640"/><wx w="515"/><wx w="687"/><wx w="531"/><wx w="500"/><wx w="531"/><wx w="609"/><wx w="515"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="609"/><wx w="515"/><wx w="609"/><wx w="515"/><wx w="703"/><wx w="531"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="671"/><wx w="625"/><wx w="500"/><wx w="625"/><wx w="500"/><wx w="500"/><wx w="296"/><wx w=!
"328"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"
/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="390"/><wx w="390"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="578"/><wx w="296"/><wx w="500"/><wx w="500"/><wx w="578"/><wx w="296"/><wx w="500"/><wx w="500"/><wx w="578"/><wx w="296"/><wx w="687"/><wx w="609"/><wx w="500"/><wx w="500"/><wx w="687"/><wx w="609"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="718"/><wx w="546"/><wx w="500"/><wx w="500"/><wx w="718"/><wx w="546"/><wx w="1000"/><wx w="937"/><wx w="625"/><wx w="468"/><wx w="500"/><wx w="500"/><wx w="625"/><wx w="468"/><wx w="531"/><wx w="406"/><wx w="531"/><wx w="406"/><wx w="531"/><wx w="406"/><wx w="531"/><wx w="406"/><wx w="703"/><wx w="390"/><wx w="703"/><wx w="390"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="703"/><wx w="593"/><wx w="703"/><wx w="593"/><wx w="703"/><wx w="593"/><wx w="703"/><wx w="593"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="593"/><wx w="468"/><wx w="593"/><wx w="468"!
/><wx w="593"/><wx w="468"/><wx w="500"/><wx w="500"/><wx w="718"/><wx w="515"/><wx w="1000"/><wx w="328"/><wx w="718"/><wx w="546"/><wx w="1000"/><wx w="593"/><wx w="1000"/><wx w="593"/><wx w="1000"/><wx w="593"/><wx w="1000"/><wx w="593"/><wx w="1000"/><wx w="593"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="828"/><wx w="500"/><wx w="500"/><wx w="515"/><wx w="500"/><wx w="531"/><wx w="531"/><wx w="515"/><wx w="500"/><wx w="640"/><wx w="640"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="468"/><wx w="500"/><wx w="500"/><wx w="375"/><wx w="671"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="625"/><wx w="625"/><wx w="625"/><wx w="578"/><wx w="328"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="296"/><wx w="375"/><wx w="578"/><wx w="890"/><wx w="859"/><wx w="828"/><wx w="718"/><wx w="1000"/><wx w="500"/><wx w="546"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="468"/><wx w="453"/><wx w="546"/><wx w="500"/><wx w="468"/><wx w="437"/><wx w="500"/><wx!
w="500"/><wx w="593"/><wx w="406"/><wx w="500"/><wx w="515"/><wx w="5
00"/><wx w="500"/><wx w="500"/><wx w="375"/><wx w="1000"/><wx w="500"/><wx w="531"/><wx w="625"/><wx w="890"/><wx w="609"/><wx w="500"/><wx w="593"/><wx w="484"/><wx w="500"/><wx w="500"/><wx w="515"/><wx w="515"/><wx w="500"/><wx w="500"/><wx w="718"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="359"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="515"/><wx w="515"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/!
><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/!
><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx
w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="500"/><w!
x w="500"/><wx w="500"/><wx w="1000"/><wx w="915"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="531"/><wx w="500"/><wx w="500"/><wx w="750"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/>!
<wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w
="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="781"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="976"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="812"/><wx w="906"/><wx w="812"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/!
><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1171"/><wx w="1265"/><wx w="1062"/><wx w="1000"/><wx w="1062"/><wx w="1171"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="843"/><wx w="656"/><wx w="937"/><wx w="1078"/><wx w="1125"/><wx w="968"/><wx w="703"/><wx w="968"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="976"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><!
wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w=
"500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="976"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="976"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="656"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="562"/><wx w="500"/><wx w="500"/><wx w=!
"500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="750"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="976"/><wx w="500"/><wx w="562"/><wx w="500"/><wx w="500"/><wx w="562"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/>!
<wx w="1000"/><wx w="562"/><wx w="500"/><wx w="500"/><wx w="500"/><wx
w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="976"/><wx w="976"/><wx w="500"/><wx w="500"/><wx w="562"/><wx w="562"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="656"/><wx w="656"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="656"/><wx w="656"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="976"/><wx w="976"/><wx w="976"/><wx w="976"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="976"/><wx w="500"/><wx w="976"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="50!
0"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="562"/><wx w="562"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="976"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/!
><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="500"/><w
x w="500"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx !
w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="976"/><wx
w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="976"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="976"/><wx w="976"/><wx w="1000"/><wx w="937"/><wx w="976"/><wx w="1000"/><wx w="406"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="976"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="976"/><wx w="976"/><wx w="976"/><wx w="976"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="976"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="915"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="915"/><wx w="1000"/><wx w="1000"/><wx w="915"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="781"/><wx w="781"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="50!
0"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/></cid-widths></multibyte-extras></font-metrics>
\ No newline at end of file
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/no-idea-what-to-do-with-these-yet/fop/userconfig.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ja-JP/src/no-idea-what-to-do-with-these-yet/fop/userconfig.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ja-JP/src/no-idea-what-to-do-with-these-yet/fop/userconfig.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,164 @@
+<?xml version="1.0" encoding="Shift_JIS"?>
+<!--<!DOCTYPE configuration SYSTEM "config.dtd">-->
+<!--
+ this file contains templates which allow an user easy
+ configuration of Fop. Actually normally you don't need this configuration
+ file, but if you need to change configuration, you should
+ always use this file and *not* config.xml.
+ Usage: java org.apache.fop.apps.Fop -c userconfig.xml -fo fo-file -pdf pdf-file
+-->
+
+
+<configuration>
+
+<!--
+baseDir: normally the base directory is the directory where the fo file is
+ located. if you want to specify your own, uncomment this entry.
+ This value can also be a URL. Actually, the value is converted to
+ a URL.
+-->
+<!--
+ <entry>
+ <key>baseDir</key>
+ <value></value>
+ </entry>
+-->
+
+<!--
+fontBaseDir: Similar to baseDir, except that this value is used for fonts. If
+ it isn't specified, the value from baseDir is used.
+-->
+<!--
+ <entry>
+ <key>fontBaseDir</key>
+ <value></value>
+ </entry>
+-->
+
+<!--
+************************************************************************
+ HYPHENATION
+************************************************************************
+-->
+
+<!--
+ hyphenation directory
+ if you want to specify your own directory with hyphenation pattern
+ then uncomment the next entry and add the directory name
+-->
+
+<!--
+ <entry>
+ <key>hyphenation-dir</key>
+ <value>/java/xml-fop/hyph</value>
+ </entry>
+-->
+
+<!--
+************************************************************************
+ Add fonts here
+************************************************************************
+-->
+
+<fonts>
+ <!-- Example Japanese fonts
+ <font metrics-file="msgothic.xml" embed-file="D:\winnt\font\msgothic.ttc" kerning="yes">
+ <font-triplet name="Gothic" style="normal" weight="normal"/>
+ <font-triplet name="Gothic" style="normal" weight="bold"/>
+ <font-triplet name="Gothic" style="italic" weight="normal"/>
+ <font-triplet name="Gothic" style="italic" weight="bold"/>
+ </font>
+ <font metrics-file="msmincho.xml" embed-file="Cyberbit.ttf" kerning="yes">
+ <font-triplet name="Mincho" style="normal" weight="normal"/>
+ <font-triplet name="Mincho" style="normal" weight="bold"/>
+ <font-triplet name="Mincho" style="italic" weight="normal"/>
+ <font-triplet name="Mincho" style="italic" weight="bold"/>
+ </font>
+ -->
+
+<font metrics-file="sazanami-mincho.xml" kerning="yes" embed-file="sazanami-mincho.ttf">
+ <font-triplet name="MS-Mincho" style="normal" weight="normal"/>
+ <font-triplet name="MS-Mincho" style="normal" weight="bold"/>
+ <font-triplet name="MS-Mincho" style="italic" weight="normal"/>
+ <font-triplet name="MS-Mincho" style="italic" weight="bold"/>
+ <font-triplet name="MSMincho" style="normal" weight="normal"/>
+ <font-triplet name="MSMincho" style="normal" weight="bold"/>
+ <font-triplet name="MSMincho" style="italic" weight="normal"/>
+ <font-triplet name="MSMincho" style="italic" weight="bold"/>
+ <font-triplet name="Mincho" style="normal" weight="normal"/>
+ <font-triplet name="Mincho" style="normal" weight="bold"/>
+ <font-triplet name="Mincho" style="italic" weight="normal"/>
+ <font-triplet name="Mincho" style="italic" weight="bold"/>
+ <font-triplet name="lr¾©" style="normal" weight="normal"/>
+ <font-triplet name="lr¾©" style="normal" weight="bold"/>
+ <font-triplet name="lr¾©" style="italic" weight="normal"/>
+ <font-triplet name="lr¾©" style="italic" weight="bold"/>
+ <font-triplet name="serif" style="normal" weight="normal"/>
+ <font-triplet name="serif" style="normal" weight="bold"/>
+ <font-triplet name="serif" style="italic" weight="normal"/>
+ <font-triplet name="serif" style="italic" weight="bold"/>
+ <font-triplet name="Mincho" style="normal" weight="normal"/>
+ <font-triplet name="Mincho" style="normal" weight="bold"/>
+ <font-triplet name="Mincho" style="italic" weight="normal"/>
+ <font-triplet name="Mincho" style="italic" weight="bold"/>
+ <font-triplet name="SazanamiMincho" style="normal" weight="normal"/>
+ <font-triplet name="SazanamiMincho" style="normal" weight="bold"/>
+ <font-triplet name="SazanamiMincho" style="italic" weight="normal"/>
+ <font-triplet name="SazanamiMincho" style="italic" weight="bold"/>
+ <font-triplet name="Mincho-Regular" style="normal" weight="normal"/>
+ <font-triplet name="Mincho-Regular" style="normal" weight="bold"/>
+ <font-triplet name="Mincho-Regular" style="italic" weight="normal"/>
+ <font-triplet name="Mincho-Regular" style="italic" weight="bold"/>
+ <font-triplet name="³´Èݾ©" style="normal" weight="normal"/>
+ <font-triplet name="³´Èݾ©" style="normal" weight="bold"/>
+ <font-triplet name="³´Èݾ©" style="italic" weight="normal"/>
+ <font-triplet name="³´Èݾ©" style="italic" weight="bold"/>
+ </font>
+
+ <font metrics-file="sazanami-gothic.xml" kerning="yes" embed-file="sazanami-gothic.ttf">
+ <font-triplet name="MS-Gothic" style="normal" weight="normal"/>
+ <font-triplet name="MS-Gothic" style="normal" weight="bold"/>
+ <font-triplet name="MS-Gothic" style="italic" weight="normal"/>
+ <font-triplet name="MS-Gothic" style="italic" weight="bold"/>
+ <font-triplet name="MSGothic" style="normal" weight="normal"/>
+ <font-triplet name="MSGothic" style="normal" weight="bold"/>
+ <font-triplet name="MSGothic" style="italic" weight="normal"/>
+ <font-triplet name="MSGothic" style="italic" weight="bold"/>
+ <font-triplet name="Gothic" style="normal" weight="normal"/>
+ <font-triplet name="Gothic" style="normal" weight="bold"/>
+ <font-triplet name="Gothic" style="italic" weight="normal"/>
+ <font-triplet name="Gothic" style="italic" weight="bold"/>
+ <font-triplet name="lr¾©" style="normal" weight="normal"/>
+ <font-triplet name="lr¾©" style="normal" weight="bold"/>
+ <font-triplet name="lr¾©" style="italic" weight="normal"/>
+ <font-triplet name="lr¾©" style="italic" weight="bold"/>
+ <font-triplet name="sans-serif" style="normal" weight="normal"/>
+ <font-triplet name="sans-serif" style="normal" weight="bold"/>
+ <font-triplet name="sans-serif" style="italic" weight="normal"/>
+ <font-triplet name="sans-serif" style="italic" weight="bold"/>
+ <font-triplet name="Gothic" style="normal" weight="normal"/>
+ <font-triplet name="Gothic" style="normal" weight="bold"/>
+ <font-triplet name="Gothic" style="italic" weight="normal"/>
+ <font-triplet name="Gothic" style="italic" weight="bold"/>
+ <font-triplet name="SazanamiGothic" style="normal" weight="normal"/>
+ <font-triplet name="SazanamiGothic" style="normal" weight="bold"/>
+ <font-triplet name="SazanamiGothic" style="italic" weight="normal"/>
+ <font-triplet name="SazanamiGothic" style="italic" weight="bold"/>
+ <font-triplet name="Gothic-Regular" style="normal" weight="normal"/>
+ <font-triplet name="Gothic-Regular" style="normal" weight="bold"/>
+ <font-triplet name="Gothic-Regular" style="italic" weight="normal"/>
+ <font-triplet name="Gothic-Regular" style="italic" weight="bold"/>
+ <font-triplet name="³´ÈÝSVbN" style="normal" weight="normal"/>
+ <font-triplet name="³´ÈÝSVbN" style="normal" weight="bold"/>
+ <font-triplet name="³´ÈÝSVbN" style="italic" weight="normal"/>
+ <font-triplet name="³´ÈÝSVbN" style="italic" weight="bold"/>
+ </font>
+
+
+
+</fonts>
+
+</configuration>
+
+
+
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/no-idea-what-to-do-with-these-yet/styles/fopdf.xsl
===================================================================
--- trunk/Hibernate3/documentation/manual/ja-JP/src/no-idea-what-to-do-with-these-yet/styles/fopdf.xsl (rev 0)
+++ trunk/Hibernate3/documentation/manual/ja-JP/src/no-idea-what-to-do-with-these-yet/styles/fopdf.xsl 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,516 @@
+<?xml version="1.0" encoding="Shift_JIS"?>
+
+<!--
+
+ This is the XSL FO configuration file for the Hibernate
+ Reference Documentation. It defines a custom titlepage and
+ the parameters for the A4 sized PDF printable output.
+
+ It took me days to figure out this stuff and fix most of
+ the obvious bugs in the DocBook XSL distribution. Some of
+ the workarounds might not be appropriate with a newer version
+ of DocBook XSL. This file is released as part of Hibernate,
+ hence LGPL licensed.
+
+ christian at hibernate.org
+
+-->
+
+<!DOCTYPE xsl:stylesheet [
+ <!ENTITY db_xsl_path "../../support/docbook-xsl/">
+]>
+
+<xsl:stylesheet
+ version="1.0"
+ xmlns="http://www.w3.org/TR/xhtml1/transitional"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:fo="http://www.w3.org/1999/XSL/Format"
+ exclude-result-prefixes="#default">
+
+ <xsl:import href="&db_xsl_path;/fo/docbook.xsl"/>
+
+ <!--###################################################
+ Custom Title Page
+ ################################################### -->
+
+ <xsl:template name="book.titlepage.recto">
+ <fo:block>
+ <fo:table table-layout="fixed" width="175mm">
+ <fo:table-column column-width="175mm"/>
+ <fo:table-body>
+ <fo:table-row>
+ <fo:table-cell text-align="center">
+ <fo:block>
+ <fo:external-graphic src="file:images/hibernate_logo_a.png"/>
+ </fo:block>
+ <fo:block font-family="Helvetica" font-size="22pt" padding-before="10mm">
+ <xsl:value-of select="bookinfo/subtitle"/>
+ </fo:block>
+ <fo:block font-family="Helvetica" font-size="12pt" padding="10mm">
+ Version:
+ <xsl:value-of select="bookinfo/releaseinfo"/>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ </fo:table-body>
+ </fo:table>
+ </fo:block>
+ </xsl:template>
+
+ <!-- Prevent blank pages in output -->
+ <xsl:template name="book.titlepage.before.verso">
+ </xsl:template>
+ <xsl:template name="book.titlepage.verso">
+ </xsl:template>
+ <xsl:template name="book.titlepage.separator">
+ </xsl:template>
+
+ <!--###################################################
+ Header
+ ################################################### -->
+
+ <!-- More space in the center header for long text -->
+ <xsl:attribute-set name="header.content.properties">
+ <xsl:attribute name="font-family">
+ <xsl:value-of select="$body.font.family"/>
+ </xsl:attribute>
+ <xsl:attribute name="margin-left">-5em</xsl:attribute>
+ <xsl:attribute name="margin-right">-5em</xsl:attribute>
+ </xsl:attribute-set>
+
+ <!--###################################################
+ Custom Footer
+ ################################################### -->
+
+ <!-- This footer prints the Hibernate version number on the left side -->
+ <xsl:template name="footer.content">
+ <xsl:param name="pageclass" select="''"/>
+ <xsl:param name="sequence" select="''"/>
+ <xsl:param name="position" select="''"/>
+ <xsl:param name="gentext-key" select="''"/>
+
+ <xsl:variable name="Version">
+ <xsl:choose>
+ <xsl:when test="//releaseinfo">
+ <xsl:text>Hibernate </xsl:text>
+ <xsl:value-of select="//releaseinfo"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- nop -->
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:choose>
+ <xsl:when test="$sequence='blank'">
+ <xsl:choose>
+ <xsl:when test="$double.sided != 0 and $position = 'left'">
+ <xsl:value-of select="$Version"/>
+ </xsl:when>
+
+ <xsl:when test="$double.sided = 0 and $position = 'center'">
+ <!-- nop -->
+ </xsl:when>
+
+ <xsl:otherwise>
+ <fo:page-number/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+
+ <xsl:when test="$pageclass='titlepage'">
+ <!-- nop: other titlepage sequences have no footer -->
+ </xsl:when>
+
+ <xsl:when test="$double.sided != 0 and $sequence = 'even' and $position='left'">
+ <fo:page-number/>
+ </xsl:when>
+
+ <xsl:when test="$double.sided != 0 and $sequence = 'odd' and $position='right'">
+ <fo:page-number/>
+ </xsl:when>
+
+ <xsl:when test="$double.sided = 0 and $position='right'">
+ <fo:page-number/>
+ </xsl:when>
+
+ <xsl:when test="$double.sided != 0 and $sequence = 'odd' and $position='left'">
+ <xsl:value-of select="$Version"/>
+ </xsl:when>
+
+ <xsl:when test="$double.sided != 0 and $sequence = 'even' and $position='right'">
+ <xsl:value-of select="$Version"/>
+ </xsl:when>
+
+ <xsl:when test="$double.sided = 0 and $position='left'">
+ <xsl:value-of select="$Version"/>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <!-- nop -->
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--###################################################
+ Custom Toc Line
+ ################################################### -->
+
+ <!-- Improve the TOC. -->
+ <xsl:template name="toc.line">
+ <xsl:variable name="id">
+ <xsl:call-template name="object.id"/>
+ </xsl:variable>
+
+ <xsl:variable name="label">
+ <xsl:apply-templates select="." mode="label.markup"/>
+ </xsl:variable>
+
+ <fo:block text-align-last="justify"
+ end-indent="{$toc.indent.width}pt"
+ last-line-end-indent="-{$toc.indent.width}pt">
+ <fo:inline keep-with-next.within-line="always">
+ <fo:basic-link internal-destination="{$id}">
+
+ <!-- Chapter titles should be bold. -->
+ <xsl:choose>
+ <xsl:when test="local-name(.) = 'chapter'">
+ <xsl:attribute name="font-weight">bold</xsl:attribute>
+ </xsl:when>
+ </xsl:choose>
+
+ <xsl:if test="$label != ''">
+ <xsl:copy-of select="$label"/>
+ <xsl:value-of select="$autotoc.label.separator"/>
+ </xsl:if>
+ <xsl:apply-templates select="." mode="titleabbrev.markup"/>
+ </fo:basic-link>
+ </fo:inline>
+ <fo:inline keep-together.within-line="always">
+ <xsl:text> </xsl:text>
+ <fo:leader leader-pattern="dots"
+ leader-pattern-width="3pt"
+ leader-alignment="reference-area"
+ keep-with-next.within-line="always"/>
+ <xsl:text> </xsl:text>
+ <fo:basic-link internal-destination="{$id}">
+ <fo:page-number-citation ref-id="{$id}"/>
+ </fo:basic-link>
+ </fo:inline>
+ </fo:block>
+ </xsl:template>
+
+ <!--###################################################
+ Extensions
+ ################################################### -->
+
+ <!-- These extensions are required for table printing and other stuff -->
+ <xsl:param name="use.extensions">1</xsl:param>
+ <xsl:param name="tablecolumns.extension">0</xsl:param>
+ <!-- FOP provide only PDF Bookmarks at the moment -->
+ <xsl:param name="fop.extensions">1</xsl:param>
+
+ <!--###################################################
+ Table Of Contents
+ ################################################### -->
+
+ <!-- Generate the TOCs for named components only -->
+ <xsl:param name="generate.toc">
+ book toc
+ </xsl:param>
+
+ <!-- Show only Sections up to level 3 in the TOCs -->
+ <xsl:param name="toc.section.depth">3</xsl:param>
+
+ <!-- Dot and Whitespace as separator in TOC between Label and Title-->
+ <xsl:param name="autotoc.label.separator" select="'. '"/>
+
+
+ <!--###################################################
+ Paper & Page Size
+ ################################################### -->
+
+ <!-- Paper type, no headers on blank pages, no double sided printing -->
+ <xsl:param name="paper.type" select="'A4'"/>
+ <xsl:param name="double.sided">0</xsl:param>
+ <xsl:param name="headers.on.blank.pages">0</xsl:param>
+ <xsl:param name="footers.on.blank.pages">0</xsl:param>
+
+ <!-- Space between paper border and content (chaotic stuff, don't touch) -->
+ <xsl:param name="page.margin.top">5mm</xsl:param>
+ <xsl:param name="region.before.extent">10mm</xsl:param>
+ <xsl:param name="body.margin.top">10mm</xsl:param>
+
+ <xsl:param name="body.margin.bottom">15mm</xsl:param>
+ <xsl:param name="region.after.extent">10mm</xsl:param>
+ <xsl:param name="page.margin.bottom">0mm</xsl:param>
+
+ <xsl:param name="page.margin.outer">18mm</xsl:param>
+ <xsl:param name="page.margin.inner">18mm</xsl:param>
+
+ <!-- No intendation of Titles -->
+ <xsl:param name="title.margin.left">0pc</xsl:param>
+
+ <!--###################################################
+ Fonts & Styles
+ ################################################### -->
+
+ <!-- Default Font size -->
+ <xsl:param name="body.font.master">11</xsl:param>
+
+ <!-- Line height in body text -->
+ <xsl:param name="line-height">1.4</xsl:param>
+
+ <!-- Monospaced fonts are smaller than regular text -->
+ <xsl:attribute-set name="monospace.properties">
+ <xsl:attribute name="font-family">
+ <xsl:value-of select="$monospace.font.family"/>
+ </xsl:attribute>
+ <xsl:attribute name="font-size">0.8em</xsl:attribute>
+ </xsl:attribute-set>
+
+ <!--###################################################
+ Tables
+ ################################################### -->
+
+ <!-- The table width should be adapted to the paper size -->
+ <xsl:param name="default.table.width">17.4cm</xsl:param>
+
+ <!-- Some padding inside tables -->
+ <xsl:attribute-set name="table.cell.padding">
+ <xsl:attribute name="padding-left">4pt</xsl:attribute>
+ <xsl:attribute name="padding-right">4pt</xsl:attribute>
+ <xsl:attribute name="padding-top">4pt</xsl:attribute>
+ <xsl:attribute name="padding-bottom">4pt</xsl:attribute>
+ </xsl:attribute-set>
+
+ <!-- Only hairlines as frame and cell borders in tables -->
+ <xsl:param name="table.frame.border.thickness">0.1pt</xsl:param>
+ <xsl:param name="table.cell.border.thickness">0.1pt</xsl:param>
+
+ <!--###################################################
+ Labels
+ ################################################### -->
+
+ <!-- Label Chapters and Sections (numbering) -->
+ <xsl:param name="chapter.autolabel">1</xsl:param>
+ <xsl:param name="section.autolabel" select="1"/>
+ <xsl:param name="section.label.includes.component.label" select="1"/>
+
+ <!-- Label only Sections up to level 2 -->
+ <xsl:param name="local.l10n.xml" select="document('')"/>
+ <l:i18n xmlns:l="http://docbook.sourceforge.net/xmlns/l10n/1.0">
+ <l:l10n language="en">
+ <l:context name="title-numbered">
+ <l:template name="sect3" text="%t"/>
+ <l:template name="sect4" text="%t"/>
+ <l:template name="sect5" text="%t"/>
+ </l:context>
+ <l:context name="section-xref-numbered">
+ <l:template name="sect3" text="the section called %t"/>
+ <l:template name="sect4" text="the section called %t"/>
+ <l:template name="sect5" text="the section called %t"/>
+ </l:context>
+ </l:l10n>
+ </l:i18n>
+
+ <!--###################################################
+ Titles
+ ################################################### -->
+
+ <!-- Chapter title size -->
+ <xsl:attribute-set name="chapter.titlepage.recto.style">
+ <xsl:attribute name="text-align">left</xsl:attribute>
+ <xsl:attribute name="font-weight">bold</xsl:attribute>
+ <xsl:attribute name="font-size">
+ <xsl:value-of select="$body.font.master * 1.8"/>
+ <xsl:text>pt</xsl:text>
+ </xsl:attribute>
+ </xsl:attribute-set>
+
+ <!-- Why is the font-size for chapters hardcoded in the XSL FO templates?
+ Let's remove it, so this sucker can use our attribute-set only... -->
+ <xsl:template match="title" mode="chapter.titlepage.recto.auto.mode">
+ <fo:block xmlns:fo="http://www.w3.org/1999/XSL/Format"
+ xsl:use-attribute-sets="chapter.titlepage.recto.style">
+ <xsl:call-template name="component.title">
+ <xsl:with-param name="node" select="ancestor-or-self::chapter[1]"/>
+ </xsl:call-template>
+ </fo:block>
+ </xsl:template>
+
+ <!-- Sections 1, 2 and 3 titles have a small bump factor and padding -->
+ <xsl:attribute-set name="section.title.level1.properties">
+ <xsl:attribute name="space-before.optimum">0.8em</xsl:attribute>
+ <xsl:attribute name="space-before.minimum">0.8em</xsl:attribute>
+ <xsl:attribute name="space-before.maximum">0.8em</xsl:attribute>
+ <xsl:attribute name="font-size">
+ <xsl:value-of select="$body.font.master * 1.5"/>
+ <xsl:text>pt</xsl:text>
+ </xsl:attribute>
+ <xsl:attribute name="space-after.optimum">0.1em</xsl:attribute>
+ <xsl:attribute name="space-after.minimum">0.1em</xsl:attribute>
+ <xsl:attribute name="space-after.maximum">0.1em</xsl:attribute>
+ </xsl:attribute-set>
+ <xsl:attribute-set name="section.title.level2.properties">
+ <xsl:attribute name="space-before.optimum">0.6em</xsl:attribute>
+ <xsl:attribute name="space-before.minimum">0.6em</xsl:attribute>
+ <xsl:attribute name="space-before.maximum">0.6em</xsl:attribute>
+ <xsl:attribute name="font-size">
+ <xsl:value-of select="$body.font.master * 1.25"/>
+ <xsl:text>pt</xsl:text>
+ </xsl:attribute>
+ <xsl:attribute name="space-after.optimum">0.1em</xsl:attribute>
+ <xsl:attribute name="space-after.minimum">0.1em</xsl:attribute>
+ <xsl:attribute name="space-after.maximum">0.1em</xsl:attribute>
+ </xsl:attribute-set>
+ <xsl:attribute-set name="section.title.level3.properties">
+ <xsl:attribute name="space-before.optimum">0.4em</xsl:attribute>
+ <xsl:attribute name="space-before.minimum">0.4em</xsl:attribute>
+ <xsl:attribute name="space-before.maximum">0.4em</xsl:attribute>
+ <xsl:attribute name="font-size">
+ <xsl:value-of select="$body.font.master * 1.0"/>
+ <xsl:text>pt</xsl:text>
+ </xsl:attribute>
+ <xsl:attribute name="space-after.optimum">0.1em</xsl:attribute>
+ <xsl:attribute name="space-after.minimum">0.1em</xsl:attribute>
+ <xsl:attribute name="space-after.maximum">0.1em</xsl:attribute>
+ </xsl:attribute-set>
+
+ <!-- Titles of formal objects (tables, examples, ...) -->
+ <xsl:attribute-set name="formal.title.properties" use-attribute-sets="normal.para.spacing">
+ <xsl:attribute name="font-weight">bold</xsl:attribute>
+ <xsl:attribute name="font-size">
+ <xsl:value-of select="$body.font.master"/>
+ <xsl:text>pt</xsl:text>
+ </xsl:attribute>
+ <xsl:attribute name="hyphenate">false</xsl:attribute>
+ <xsl:attribute name="space-after.minimum">0.4em</xsl:attribute>
+ <xsl:attribute name="space-after.optimum">0.6em</xsl:attribute>
+ <xsl:attribute name="space-after.maximum">0.8em</xsl:attribute>
+ </xsl:attribute-set>
+
+ <!--###################################################
+ Programlistings
+ ################################################### -->
+
+ <!-- Verbatim text formatting (programlistings) -->
+ <xsl:attribute-set name="verbatim.properties">
+ <xsl:attribute name="space-before.minimum">1em</xsl:attribute>
+ <xsl:attribute name="space-before.optimum">1em</xsl:attribute>
+ <xsl:attribute name="space-before.maximum">1em</xsl:attribute>
+ <xsl:attribute name="space-after.minimum">0.1em</xsl:attribute>
+ <xsl:attribute name="space-after.optimum">0.1em</xsl:attribute>
+ <xsl:attribute name="space-after.maximum">0.1em</xsl:attribute>
+ <xsl:attribute name="border-color">#444444</xsl:attribute>
+ <xsl:attribute name="border-style">solid</xsl:attribute>
+ <xsl:attribute name="border-width">0.1pt</xsl:attribute>
+ <xsl:attribute name="padding-top">0.5em</xsl:attribute>
+ <xsl:attribute name="padding-left">0.5em</xsl:attribute>
+ <xsl:attribute name="padding-right">0.5em</xsl:attribute>
+ <xsl:attribute name="padding-bottom">0.5em</xsl:attribute>
+ <xsl:attribute name="margin-left">0.5em</xsl:attribute>
+ <xsl:attribute name="margin-right">0.5em</xsl:attribute>
+ </xsl:attribute-set>
+
+ <!-- Shade (background) programlistings -->
+ <xsl:param name="shade.verbatim">1</xsl:param>
+ <xsl:attribute-set name="shade.verbatim.style">
+ <xsl:attribute name="background-color">#F0F0F0</xsl:attribute>
+ </xsl:attribute-set>
+
+ <!--###################################################
+ Callouts
+ ################################################### -->
+
+ <!-- We want to use callouts... -->
+ <xsl:param name="callout.extensions">1</xsl:param>
+
+ <!-- Place callout bullets at this column in programmlisting.-->
+ <xsl:param name="callout.defaultcolumn">90</xsl:param>
+
+ <!--
+ No, don't use crappy graphics for the callout bullets. This setting
+ enables some weird Unicode rendering for some fancy bullet points
+ in callouts. By default, this can only count to 10 and produces
+ strange results if you ever have more than 10 callouts for one
+ programlisting. We will fix that next.
+ -->
+ <xsl:param name="callout.graphics">0</xsl:param>
+
+ <!--
+ Again, fun with DocBook XSL: The callout bullets are rendered in
+ two places: In the programlisting itself and in the list below
+ the listing, with the actual callout text. The rendering in the
+ programlisting is some XSL transformer extension (e.g. a Saxon
+ extension), so we can't change that without messing with the
+ extensions. We only can turn it off by setting this limit to
+ zero, then, a simple bracket style like "(3)" and "(4)" will
+ be used in the programlisting.
+ -->
+ <xsl:param name="callout.unicode.number.limit" select="'0'"></xsl:param>
+
+ <!--
+ The callout bullets in the actual callout list will be rendered
+ with an XSL FO template. The default template is broken: limited to 10
+ nice looking Unicode bullet points and then it doesn't print anything,
+ the fallback doesn't work. We implement our own template, which is not
+ as complicated, more ugly, but works. As always, function is more
+ important than form.
+ -->
+ <xsl:template name="callout-bug">
+ <xsl:param name="conum" select='1'/>
+ <fo:inline
+ color="black"
+ padding-top="0.1em"
+ padding-bottom="0.1em"
+ padding-start="0.2em"
+ padding-end="0.2em"
+ baseline-shift="0.1em"
+ font-family="{$monospace.font.family}"
+ font-weight="bold"
+ font-size="75%">
+ <xsl:text>(</xsl:text>
+ <xsl:value-of select="$conum"/>
+ <xsl:text>)</xsl:text>
+ </fo:inline>
+
+ </xsl:template>
+
+ <!--###################################################
+ Misc
+ ################################################### -->
+
+ <!-- Correct placement of titles for figures and examples. -->
+ <xsl:param name="formal.title.placement">
+ figure after
+ example before
+ equation before
+ table before
+ procedure before
+ </xsl:param>
+
+ <!-- Format Variable Lists as Blocks (prevents horizontal overflow). -->
+ <xsl:param name="variablelist.as.blocks">1</xsl:param>
+
+ <!-- The horrible list spacing problems, this is much better. -->
+ <xsl:attribute-set name="list.block.spacing">
+ <xsl:attribute name="space-before.optimum">0.8em</xsl:attribute>
+ <xsl:attribute name="space-before.minimum">0.8em</xsl:attribute>
+ <xsl:attribute name="space-before.maximum">0.8em</xsl:attribute>
+ <xsl:attribute name="space-after.optimum">0.1em</xsl:attribute>
+ <xsl:attribute name="space-after.minimum">0.1em</xsl:attribute>
+ <xsl:attribute name="space-after.maximum">0.1em</xsl:attribute>
+ </xsl:attribute-set>
+
+ <!-- Newer DocBook XSL apparently thinks that some sections are by
+ default "draft" status, and this idiotic thing is by default
+ also set to "maybe", so it spits out a lot of errors with the
+ latest FOP as the XSL/FO styles have references to some draft
+ watermarks, which you actually don't want in the first place.
+ Turn this crap off. If you have to work with the "status"
+ attribute, don't.
+ -->
+ <xsl:param name="draft.mode" select="'no'"/>
+
+</xsl:stylesheet>
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/no-idea-what-to-do-with-these-yet/styles/html.xsl
===================================================================
--- trunk/Hibernate3/documentation/manual/ja-JP/src/no-idea-what-to-do-with-these-yet/styles/html.xsl (rev 0)
+++ trunk/Hibernate3/documentation/manual/ja-JP/src/no-idea-what-to-do-with-these-yet/styles/html.xsl 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="Shift_JIS"?>
+
+<!--
+
+ This is the XSL HTML configuration file for the Hibernate
+ Reference Documentation.
+
+ It took me days to figure out this stuff and fix most of
+ the obvious bugs in the DocBook XSL distribution. Some of
+ the workarounds might not be appropriate with a newer version
+ of DocBook XSL. This file is released as part of Hibernate,
+ hence LGPL licensed.
+
+ christian at hibernate.org
+-->
+
+<!DOCTYPE xsl:stylesheet [
+ <!ENTITY db_xsl_path "../../support/docbook-xsl/">
+]>
+
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ version="1.0"
+ xmlns="http://www.w3.org/TR/xhtml1/transitional"
+ exclude-result-prefixes="#default">
+
+<xsl:import href="&db_xsl_path;/html/docbook.xsl"/>
+
+<!--###################################################
+ HTML Settings
+ ################################################### -->
+
+ <xsl:param name="html.stylesheet">../shared/css/html.css</xsl:param>
+
+ <!-- These extensions are required for table printing and other stuff -->
+ <xsl:param name="use.extensions">1</xsl:param>
+ <xsl:param name="tablecolumns.extension">0</xsl:param>
+ <xsl:param name="callout.extensions">1</xsl:param>
+ <xsl:param name="graphicsize.extension">0</xsl:param>
+
+<!--###################################################
+ Table Of Contents
+ ################################################### -->
+
+ <!-- Generate the TOCs for named components only -->
+ <xsl:param name="generate.toc">
+ book toc
+ </xsl:param>
+
+ <!-- Show only Sections up to level 3 in the TOCs -->
+ <xsl:param name="toc.section.depth">3</xsl:param>
+
+<!--###################################################
+ Labels
+ ################################################### -->
+
+ <!-- Label Chapters and Sections (numbering) -->
+ <xsl:param name="chapter.autolabel">1</xsl:param>
+ <xsl:param name="section.autolabel" select="1"/>
+ <xsl:param name="section.label.includes.component.label" select="1"/>
+
+<!--###################################################
+ Callouts
+ ################################################### -->
+
+ <!-- Don't use graphics, use a simple number style -->
+ <xsl:param name="callout.graphics">0</xsl:param>
+
+ <!-- Place callout marks at this column in annotated areas -->
+ <xsl:param name="callout.defaultcolumn">90</xsl:param>
+
+<!--###################################################
+ Misc
+ ################################################### -->
+
+ <!-- Placement of titles -->
+ <xsl:param name="formal.title.placement">
+ figure after
+ example before
+ equation before
+ table before
+ procedure before
+ </xsl:param>
+
+</xsl:stylesheet>
Added: trunk/Hibernate3/documentation/manual/ja-JP/src/no-idea-what-to-do-with-these-yet/styles/html_chunk.xsl
===================================================================
--- trunk/Hibernate3/documentation/manual/ja-JP/src/no-idea-what-to-do-with-these-yet/styles/html_chunk.xsl (rev 0)
+++ trunk/Hibernate3/documentation/manual/ja-JP/src/no-idea-what-to-do-with-these-yet/styles/html_chunk.xsl 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="Shift_JIS"?>
+
+<!--
+
+ This is the XSL HTML configuration file for the Hibernate
+ Reference Documentation.
+
+ It took me days to figure out this stuff and fix most of
+ the obvious bugs in the DocBook XSL distribution. Some of
+ the workarounds might not be appropriate with a newer version
+ of DocBook XSL. This file is released as part of Hibernate,
+ hence LGPL licensed.
+
+ christian at hibernate.org
+-->
+
+<!DOCTYPE xsl:stylesheet [
+ <!ENTITY db_xsl_path "../../support/docbook-xsl/">
+]>
+
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ version="1.0"
+ xmlns="http://www.w3.org/TR/xhtml1/transitional"
+ exclude-result-prefixes="#default">
+
+<xsl:import href="&db_xsl_path;/html/chunk.xsl"/>
+
+<!--###################################################
+ HTML Settings
+ ################################################### -->
+
+ <xsl:param name="chunk.section.depth">'5'</xsl:param>
+ <xsl:param name="use.id.as.filename">'1'</xsl:param>
+ <xsl:param name="html.stylesheet">../shared/css/html.css</xsl:param>
+
+ <!-- These extensions are required for table printing and other stuff -->
+ <xsl:param name="use.extensions">1</xsl:param>
+ <xsl:param name="tablecolumns.extension">0</xsl:param>
+ <xsl:param name="callout.extensions">1</xsl:param>
+ <xsl:param name="graphicsize.extension">0</xsl:param>
+
+<!--###################################################
+ Table Of Contents
+ ################################################### -->
+
+ <!-- Generate the TOCs for named components only -->
+ <xsl:param name="generate.toc">
+ book toc
+ </xsl:param>
+
+ <!-- Show only Sections up to level 3 in the TOCs -->
+ <xsl:param name="toc.section.depth">3</xsl:param>
+
+<!--###################################################
+ Labels
+ ################################################### -->
+
+ <!-- Label Chapters and Sections (numbering) -->
+ <xsl:param name="chapter.autolabel">1</xsl:param>
+ <xsl:param name="section.autolabel" select="1"/>
+ <xsl:param name="section.label.includes.component.label" select="1"/>
+
+<!--###################################################
+ Callouts
+ ################################################### -->
+
+ <!-- Don't use graphics, use a simple number style -->
+ <xsl:param name="callout.graphics">0</xsl:param>
+
+ <!-- Place callout marks at this column in annotated areas -->
+ <xsl:param name="callout.defaultcolumn">90</xsl:param>
+
+<!--###################################################
+ Misc
+ ################################################### -->
+
+ <!-- Placement of titles -->
+ <xsl:param name="formal.title.placement">
+ figure after
+ example before
+ equation before
+ table before
+ procedure before
+ </xsl:param>
+
+</xsl:stylesheet>
Added: trunk/Hibernate3/documentation/manual/ko-KR/README-KO.txt
===================================================================
--- trunk/Hibernate3/documentation/manual/ko-KR/README-KO.txt (rev 0)
+++ trunk/Hibernate3/documentation/manual/ko-KR/README-KO.txt 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,85 @@
+[NOTICE]FOR Korean,
+ ÀÌ ¹ø¿ª¹°¿¡¼ ¿¹Á¦ ³»ÀÇ ÁÖ¼®¿¡ ´ëÇÑ ¿µ¹®À» ¿ø¹® ±×´ë·Î µÎ¾ú½À´Ï´Ù.
+ ÀÌÀ¯´Â ¿¹Á¦ ºÎºÐ¿¡ ÇÑ±Û ÆùÆ®¸¦ Àû¿ëÇÒ °æ¿ì¿¡, ÅؽºÆ®µéÀÌ Àϱ׷¯Áö´Â ¹®Á¦°¡
+ Á¸ÀçÇϱ⿡ ±âÁ¸ÀÇ ¹ø¿ªµÈ ¿¹Á¦ ÁÖ¼®À» ¸ðµÎ ·Ñ¹é½ÃÄ×½À´Ï´Ù.
+ Â÷ÈÄ¿¡ ÇÑ±Û ÆùÆ®¿¡ ¸Âµµ·Ï Çü½ÄÀ» ÀçÆíÁýÇؾßÇÒ ºÎºÐÀÌ´Ï ¾çÇعٶø´Ï´Ù.
+ ±×·³ ÇÏÀ̹ö³×ÀÌÆ®¿Í Áñ°Å¿î ½Ã°£À» º¸³»½Ã±æ ¹Ù¶ø´Ï´Ù.
+
+ ¹ø¿ªÀÚ ±èÁ¾´ë(jdkim528 at korea.com)
+ Blog : http://blog.naver.com/jdkim528/
+
+[Down/Config/Build]
+ * ±âÁ¸ÀÇ CVS¿¡¼ SVNÀ¸·Î ¹Ù²î¸é¼ ½Å±Ô »ç¿ëÀÚµéÀ» À§ÇÑ ±æÀâÀ̸¦ ÀÛ¼ºÇÒ ÇÊ¿ä°¡
+ »ý°å³×¿ä...
+
+ ÇÊÀÚ´Â °³ÀÎÀûÀ¸·Î TortoiseSVN°ú Subclipse¸¦ »ç¿ëÇÏ°í ÀÖÀ¸³ª Subclpse¸¦ Áß½ÉÀ¸·Î
+ ¼³¸íÇÒ±î ÇÕ´Ï´Ù.(¼±È£ÇÏ´Â svn Ŭ¶óÀ̾ðÆ®°¡ ÀÖ´Ù¸é ÇØ´ç Ŭ¶óÀ̾ðÆ®ÀÇ ¼³Á¤¿¡
+ µû¶ó ¼³Á¤ÇÑ ÈÄ¿¡ »ç¿ëÇϼŵµ µÉ °ÍÀÔ´Ï´Ù.)
+
+ [Subclipse ¼³Ä¡]
+
+ Subclipse¸¦ ¼³Ä¡ÇÏ´Â ¹æ¹ýÀº http://subclipse.tigris.org/install.html À» ÂüÁ¶ÇÏ¿©
+ eclipseÀÇ µµ¿ò¸»>¼ÒÇÁÆ®¿þ¾î°»½Å>ã±â ¹× ¼³Ä¡ ¸Þ´º¸¦ ÅëÇØ ½±°Ô ¼³Ä¡ÇÒ ¼ö ÀÖ½À´Ï´Ù.
+
+ [Subclipse Checkout]
+ 0) Subclipse ¼³Ä¡°¡ ³¡³µÀ¸¸é ÀÌŬ¸³½º¸¦ Àç½ÃÀÛÇϽʽÿÀ.
+ 1) ÀÌŬ¸³½ºÀÇ Perspective ¿±â¿¡¼ SVN Repository exploringÀ» ¿¾î ÀúÀå¼Ò¸¦ µî·ÏÇÏ¿© »ç¿ëÇϰųª
+ ÀÌŬ¸³½ºÀÇ ÆÐÅ°Áö Ž»ö±â¿¡¼ ¸¶¿ì½º ¿À¸¥ÂÊ Æ˾÷ ¸Þ´º¿¡¼ "ÇÁ·ÎÁ§Æ®"¸¦ »ý¼º½Ãŵ´Ï´Ù.
+ ¿©±â¼´Â "ÇÁ·ÎÁ§Æ®" »ý¼º ¹æ¹ýÀ» ¼³¸íÇÕ´Ï´Ù.
+ 2) ÀÌŬ¸³½ºÀÇ ÆÐÅ°Áö Ž»ö±â¿¡¼ ¸¶¿ì½º ¿À¸¥ÂÊ Æ˾÷ ¸Þ´º¿¡¼ "ÇÁ·ÎÁ§Æ®"¸¦ Ŭ¸¯ÇÑ´Ù
+ 3) Æ˾÷ âÀÇ "SVN" ³ëµå¿¡¼ "Checkout Projects from SVN"À» ¼±ÅÃÇÑ´Ù
+ 4) "´ÙÀ½" ¹öÆ°À» Ŭ¸¯ÇÑ´Ù
+ 5) "Create a new respository location"À» ¼±ÅÃÇÑ´Ù
+ 6) ´ÙÀ½" ¹öÆ°À» Ŭ¸¯ÇÑ´Ù
+ 7) Location url¿¡ "http://anonhibernate.labs.jboss.com/trunk/Hibernate3" ¶Ç´Â
+ "https://hibernate.labs.jboss.com/repos/hibernate/trunk" À» ÀÔ·ÂÇÕ´Ï´Ù.
+ 8) "Hibernate3" ³ëµå´Â ¼±ÅÃÇϰųª ÇÏÀ§ÀÇ Æ¯Á¤ ³ëµå¸¦ ¼±ÅÃÇÏ°í "¿Ï·á" ¹öÆ°À» Ŭ¸¯ÇÑ´Ù.
+ 9) ÇÁ·ÎÁ§Æ® ¸íÀ» hibernate3 µîÀÇ ¿øÇÏ´Â À̸§À¸·Î ¸í¸íÇÑ´Ù.
+ 10) checkoutÀÌ ½ÇÇàµË´Ï´Ù.
+
+ [TortoiseSVN ¼³Ä¡]
+ TortoiseSVN Ŭ¶óÀ̾ðÆ®¸¦ ¼³Ä¡Çϼ̴ٸé, ½Ã½ºÅÛÀ» Àç½ÃÀÛ ÇϽʽÿÀ.
+ 1)·¹ÆÄÁöÅ丮¸¦ À§ÇÑ Æú´õ¸¦ Çϳª »ý¼º½Ãŵ´Ï´Ù.(D:\repo)
+ 2)À©µµ¿ìŽ»ö±â¿¡¼ D:\repo¸¦ ¸¶¿ì½º ¿À¸¥ÂÊ Å¬¸¯ÇÑ ÈÄ TortoiseSVN ¸Þ´º¿¡¼
+ "Create repository Here..."¸¦ Ŭ¸¯Çϸé Æ˾÷ÀÌ ¶ß´Âµ¥,
+ ÆÄÀϽýºÅÛ/¹öŬ¸®DB Çü½Ä Áß Çϳª¸¦ ¼±ÅÃÇÏ°í OK ¹öÆ°À» ´©¸£¼¼¿ä.
+ 3)hibernate¸¦ ³»·Á ¹Þ±â À§ÇÑ Æú´õ¸¦ Çϳª »ý¼º½ÃÅ°¼¼¿ä(D:\repo\Hibernate3)
+ 4)D:\repo\hibernate Æú´õ¸¦ ¸¶¿ì½º ¿À¸¥ÂÊ Å¬¸¯ÇÑ ÈÄ,
+ TortoiseSVN Æ˾÷ ¸Þ´º¿¡¼ CheckOutÀ» Ŭ¸¯ÇϽʽÿÀ.
+ 5)URL repository¿¡ "http://anonhibernate.labs.jboss.com/trunk/Hibernate3" ¶Ç´Â
+ "https://hibernate.labs.jboss.com/repos/hibernate/trunk" ¸¦ ÀÔ·ÂÇÏ°í,
+ OK ¹öÆ°À» Ŭ¸¯ÇϽʽÿÀ
+ 6)¸ðµÎ ³»·Á¹ÞÀ¸½Å ´ÙÀ½¿¡ D:\repo\Hibernate3\doc\reference·Î À̵¿ÇÕ´Ï´Ù.
+ 7)ÀÌÁ¦ ºôµåÇÏ½Ã¸é µË´Ï´Ù.
+
+ *) ÇÑ±Û ¹ø¿ªº»¸¸ ÇÊ¿äÇÏ½Ã´Ù¸é ´ÙÀ½°ú °°ÀÌ Çϼŵµ µË´Ï´Ù.
+ /doc/reference/build.xml ÆÄÀÏÀ»
+ ¾Æ·¡ ¿¹¿Í °°ÀÌ ÇÑ±Û ¹ø¿ªº» ¿ÜÀÇ ºÎºÐµéÀ» ÁÖ¼®Ã³¸®ÇÕ´Ï´Ù.
+ [°¡]. ºôµåÇϱâ
+ ±×·± ´ÙÀ½ [·ÎÄà °æ·Î]/reference/ ¿¡¼ ant all.doc·Î ºôµåÇÏ½Ã¸é µË´Ï´Ù.
+ ºôµå ½Ã°£Àº 2ºÐ °¡·® ¼Ò¿äµË´Ï´Ù.
+ [³ª]. ¹®¼ º¸±â
+ µð·ºÅ丮 [·ÎÄà °æ·Î]/reference/build/ko/ µð·ºÅ丮¿¡ ºôµåµÈ ¹®¼¸¦ º¸½Ã±â ¹Ù¶ø´Ï´Ù.
+ ±×·³ ÇÏÀ̹ö³×ÀÌÆ®¿Í ÇÔ²² Áñ°Å¿î ½Ã°£À» º¸³»¼¼¿ä.
+
+ [¿¹]
+ <target name="all.doc"
+ depends="clean"
+ description="Compile documentation for all languages and all formats.">
+
+ <!-- TRANSLATOR: Duplicate this line for your language -->
+ <!--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="es"/></antcall-->
+ <antcall target="lang.all"><param name="lang" value="ko"/></antcall>
+
+ </target>
+ <target name="all.revdiff"
+ description="Generates a diff report for all translated versions.">
+
+ <!-- TRANSLATOR: Duplicate this line for your language -->
+ <!--antcall target="lang.revdiff"><param name="lang" value="zh-cn"/></antcall-->
+ <!--antcall target="lang.revdiff"><param name="lang" value="es"/></antcall-->
+ <antcall target="lang.revdiff"><param name="lang" value="ko"/></antcall>
+
+ </target>
Added: trunk/Hibernate3/documentation/manual/ko-KR/pom.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ko-KR/pom.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ko-KR/pom.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-manual</artifactId>
+ <version>3.3.0.beta1</version>
+ <relativePath>../../../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>hibernate-manual-${translation}</artifactId>
+ <packaging>pom</packaging>
+ <name>Hibernate Manual (${translation})</name>
+
+ <properties>
+ <translation>ko-KR</translation>
+ </properties>
+
+</project>
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/master.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/master.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/master.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,184 @@
+<?xml version='1.0' encoding="UTF-8"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3CR3//EN"
+ "../support/docbook-dtd/docbookx.dtd"
+[
+<!ENTITY tutorial SYSTEM "modules/tutorial.xml">
+<!ENTITY architecture SYSTEM "modules/architecture.xml">
+<!ENTITY configuration SYSTEM "modules/configuration.xml">
+<!ENTITY persistent-classes SYSTEM "modules/persistent_classes.xml">
+<!ENTITY basic-mapping SYSTEM "modules/basic_mapping.xml">
+<!ENTITY collection-mapping SYSTEM "modules/collection_mapping.xml">
+<!ENTITY association-mapping SYSTEM "modules/association_mapping.xml">
+<!ENTITY component-mapping SYSTEM "modules/component_mapping.xml">
+<!ENTITY inheritance-mapping SYSTEM "modules/inheritance_mapping.xml">
+<!ENTITY session-api SYSTEM "modules/session_api.xml">
+<!ENTITY transactions SYSTEM "modules/transactions.xml">
+<!ENTITY events SYSTEM "modules/events.xml">
+<!ENTITY batch SYSTEM "modules/batch.xml">
+<!ENTITY query-hql SYSTEM "modules/query_hql.xml">
+<!ENTITY query-criteria SYSTEM "modules/query_criteria.xml">
+<!ENTITY query-sql SYSTEM "modules/query_sql.xml">
+<!ENTITY filters SYSTEM "modules/filters.xml">
+<!ENTITY xml SYSTEM "modules/xml.xml">
+<!ENTITY performance SYSTEM "modules/performance.xml">
+<!ENTITY toolset-guide SYSTEM "modules/toolset_guide.xml">
+<!ENTITY example-parentchild SYSTEM "modules/example_parentchild.xml">
+<!ENTITY example-weblog SYSTEM "modules/example_weblog.xml">
+<!ENTITY example-mappings SYSTEM "modules/example_mappings.xml">
+<!ENTITY best-practices SYSTEM "modules/best_practices.xml">
+]>
+
+<book lang="ko">
+
+ <bookinfo lang="ko">
+ <title>HIBERNATE - ê°ì±ìë ìë°ë¥¼ ìí ê´ê³ ìì</title>
+ <subtitle>íì´ë²ë¤ì´í¸ 참조 문ì</subtitle>
+ <releaseinfo lang="ko">3.2 cr3</releaseinfo>
+ </bookinfo>
+
+ <toc lang="ko" />
+
+ <preface id="preface" revision="2">
+ <title>머리ë§</title>
+
+ <para>
+ ê°ì²´ ì§í¥ ìíí¸ì¨ì´ì ê´ê³í ë°ì´í°ë² ì´ì¤ë¡ ìì
íë ê²ì ì¤ëë ì ìí°íë¼ì´ì¦ íê²½ë¤ìì ì±ê°ìê³ ìê°ì´
+ ìë¹ë ì ìë¤. Hibernateë ìë° íê²½ë¤ì ìí ê°ì²´/ê´ê³í 매í ë구ì´ë¤.
+ object/relational mapping(ORM) ì©ì´ë ê°ì²´ 모íì¼ë¡ë¶í° SQL-기ë°ì ì¤í¤ë§ë¥¼ ê°ì§
+ ê´ê³í ë°ì´í° 모íì¼ë¡ì ë°ì´í° íìì 매ííë 기ì ì ì¸ê¸íë¤.
+ </para>
+
+ <para>
+ Hibernateë ìë° í´ëì¤ë¤ë¡ë¶í° ë°ì´í°ë² ì´ì¤ë¡ì 매í(ê·¸ë¦¬ê³ ìë° ë°ì´í° íì
ë¤ë¡ë¶í° SQL ë°ì´í°
+ íì
ë¤ë¡ì 매í)ì ì²ë¦¬í ë¿ë§ ìëë¼, ëí ë°ì´í° ì§ìì ê²ì í¸ìë¤ì ì ê³µíë©°, SQLê³¼ JDBCë¡
+ ììì
ë°ì´í° í¸ë¤ë§ì ììëë ê°ë° ìê°ì íì íê² ë¨ì¶ìì¼ì¤ ì ìë¤.
+ </para>
+
+ <para>
+ Hibernateì 목ì ì ê³µíµë ë°ì´í° ììí ê´ë ¨ íë¡ê·¸ëë° ìì
ë¤ì 95%를 ëì´ì£¼ë ê²ì´ë¤.
+ Hibernateë ë°ì´í°ë² ì´ì¤ìì ë¹ì§ëì¤ ë¡ì§ì 구ííëë° ë´ì¥ íë¡ìì ë¤ì ì ì©ì¼ë¡ ì¬ì©íë ë°ì´í°
+ ì¤ì¬ì ì´í리ì¼ì´ì
ì ëí ìµìì ì루ì
ì´ ìë ìë ìì§ë§, ê·¸ê²ì ìë° ê¸°ë° ë¯¸ë¤-í°ì´ìì ê°ì²´ ì§í¥
+ ëë©ì¸ 모íë¤ê³¼ ë¹ì§ëì¤ ë¡ì§ì ê°ì¥ ì ì©íë¤. íì§ë§ Hibernateë 벤ë ì§ì ì ì¸ SQL ì½ë를
+ ì ê±°íê±°ë 캡ìí ìí¤ë ë¹ì ì íì¤í ëìì¤ ì ìê³ í
ì´ë¸ íìì ííì¼ë¡ë¶í° ê°ì²´ë¤ì ê·¸ëíë¡ ê²°ê³¼
+ ì
ì ë³ííë ê³µíµë íì¤í¬ë¥¼ ëìì¤ ê²ì´ë¤.
+ </para>
+
+ <para>
+ ë§ì¼ ë¹ì ì´ Hibernateì Object/Relational 매í ëë ì¬ì§ì´ ìë°ì ì´ì¬ìë¼ë©´, ë¤ì
+ ë¨ê³ë¤ì ë°ë¥´ê¸° ë°ëë¤:
+ </para>
+
+ <orderedlist>
+ <listitem>
+ <para>
+ ë ë§ì ë¨ê³ì ì¸ ì¬ì© ì¤ëª
ìë¤ì ê°ì§ ë 긴 íí 리ì¼ì
+ <xref linkend="tutorial"/>
+ ì ì½ì´ë¼. íí 리ì¼ì ìí ìì¤ ì½ëë <literal>doc/reference/tutorial/</literal>
+ ëë í 리ì í¬í¨ëì´ ìë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Hibernateê° ì¬ì©ë ì ìë íê²½ì ì´í´ë ¤ë©´
+ <xref linkend="architecture" />
+ 를 ì½ì´ë¼.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Hibernate ë°°í¬ë³¸ ë´ì
+ <literal>eg/</literal>
+ ëë í 리를 ì´í´ ë³´ë¼. ì´ ëë í 리ë ê°ë¨í ì¤í ëì¼ë¡ ì´í리ì¼ì´ì
ì í¬í¨íê³ ìë¤. ë¹ì ì
+ JDBC ëë¼ì´ë²ë¥¼
+ <literal>lib/</literal>
+ ëë í 리ì ë³µì¬íê³ ë¹ì ì ë°ì´í°ë² ì´ì¤ì ë§ë ì íí ê°ì ì§ì íì¬
+ <literal>etc/hibernate.properties</literal>
+ 를 í¸ì§íë¼. 배보본 ëë í 리ìì ëª
ë ¹ ë¼ì¸ í롬íí¸ìì (Ant를 ì¬ì©íì¬)
+ <literal>ant eg</literal>
+ 를 íì´í íê±°ë , Windows íìì
+ <literal>build eg</literal>
+ 를 íì´í íë¼.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ë¹ì ì 주ë ì ë³´ì ìì¤ë¡ì ì´ ì°¸ì¡° 문ì를 ì¬ì©íë¼. ë§ì¼ ì´í리ì¼ì´ì
ì¤ê³ì ëí´ ë ë§ì
+ ëìì íìë¡ íê±°ë ë¹ì ì´ ë¨ê³ì ì¸ íí 리ì¼ì ì í¸íë¤ë©´
+ <emphasis>Hibernate in Action</emphasis>
+ (http://www.manning.com/bauer)ì ì½ë ê²ì ê³ ë ¤íë¼.ëí
+ http://caveatemptor.hibernate.orgì 방문íì¬ Hibernate in
+ Actionì© ìì ì´í리ì¼ì´ì
ì ë¤ì´ë¡ë íë¼.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ FAQë¤ì Hibernate ì¹ ì¬ì´í¸ ìì ëµë³ëì´ ìë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ì 3ì ë°ëª¨ë¤, ìì ë¤, ê·¸ë¦¬ê³ íí 리ì¼ë¤ì Hibernate ì¹ ì¬ì´í¸ ìì ë§í¬ëì´
+ ìë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Hibernate ì¹ì¬ì´í¸ ìì ê³µëì²´ ììì ì¤ê³ í¨í´ê³¼ ë¤ìí íµí© ì루ì
ë¤(Tomcat,
+ JBoss AS, Struts, EJB ë±.)ì ê´í ì¢ì 리ìì¤ì´ë¤.
+ </para>
+ </listitem>
+ </orderedlist>
+
+ <para>
+ ì§ë¬¸ì´ ìë¤ë©´, Hibernate ìì ë§í¬ëì´ ìë ì¬ì©ì í¬ë¼ì ì¬ì©íë¼. ì°ë¦¬ë ëí ë²ê·¸ ë³´ê³ ì í¹ì§
+ ìì²ë¤ì ìí JIRA ìì ì¶ì ìì¤í
ì ì ê³µíë¤. ë¹ì ì´ Hibernateì ê°ë°ì ê´ì¬ì´ ìë¤ë©´,
+ ê°ë°ì ë©ì¼ë§ 리ì¤í¸ì ì°¸ì¬íë¼. ë§ì¼ ë¹ì ì´ ì´ ë¬¸ì를 ë¹ì ì ì¸ì´ë¡ ë²ìíë ê²ì ê´ì¬ì´ ìë¤ë©´, ê°ë°ì
+ ë©ì¼ë§ 리ì¤í¸ì ì ì´íë¼.
+ </para>
+
+ <para>
+ Hibernate를 ìí ìì© ê°ë° ì§ì, ì í ì§ì, ê·¸ë¦¬ê³ êµì¡ì JBoss Inc를 íµí´ ì´ì© ê°ë¥íë¤
+ (http://www.hibernate.org/SupportTraining/를 ë³´ë¼). Hibernateë
+ JBoss Professional Open Source product íë¡ì í¸ì´ê³ ì íë¤ì ëí JBoss
+ Enterprise Middleware System (JEMS) suiteì ì¤ëí ì»´í¬ëí¸ì´ë¤.
+ </para>
+
+ </preface>
+
+ &tutorial;
+
+ &architecture;
+
+ &configuration;
+
+ &persistent-classes;
+
+ &basic-mapping;
+ &collection-mapping;
+ &association-mapping;
+ &component-mapping;
+ &inheritance-mapping;
+
+ &session-api;
+ &transactions;
+ &events;
+ &batch;
+
+ &query-hql;
+ &query-criteria;
+ &query-sql;
+ &filters;
+ &xml;
+
+ &performance;
+
+ &toolset-guide;
+
+ &example-parentchild;
+ &example-weblog;
+ &example-mappings;
+
+ &best-practices;
+
+</book>
+
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/architecture.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/architecture.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/architecture.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,308 @@
+<chapter id="architecture">
+
+ <title>ìí¤í
ì²</title>
+
+ <sect1 id="architecture-overview" revision="1">
+ <title>ê°ì</title>
+
+ <para>
+ Hibernate ìí¤í
ì²ì ëí (매ì°) ëì ìì¤ì ê°ì::
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/overview.svg" format="SVG" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/overview.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ ì´ ë¤ì´ì´ê·¸ë¨ì ì´í리ì¼ì´ì
ì ììí ìë¹ì¤ë¤(ê³¼ ìì ê°ì²´ë¤)ì ì ê³µí기 ìí´ ë°ì´í°ë² ì´ì¤ì
+ 컨í¼ê·¸ë ì´ì
ì ì¬ì©íë Hibernate를 ë³´ì¬ì¤ë¤.
+ </para>
+
+ <para>
+ ì°ë¦¬ë ë°íì ìí¤í
ì²ì ëí ë³´ë¤ ìì¸í 뷰를 ë³´ì¬ì£¼ê³ ì¶ë¤. ë¶ííê²ë, Hibernateë ì ì°íë©°
+ ëª ê°ì§ ì ê·¼ë²ë¤ì ì ê³µíë¤. ì°ë¦¬ë ë ê°ì§ ê·¹ë¨ì ë³´ì¬ì¤ ê²ì´ë¤. "ê²½ëê¸" ìí¤í
ì²ë ê·¸ê² ìì ì
+ JDBC 커ë¥ì
ë¤ì ì ê³µíê³ ê·¸ê² ìì ì í¸ëìì
ë¤ì ê´ë¦¬íë ì´í리ì¼ì´ì
ì ê°ëë¤. ì´ ì ê·¼ë²ì
+ Hibernateì APIì ìµì ë¶ë¶ì§í©ì ì¬ì©íë¤:
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/lite.svg" format="SVG" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/lite.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ "ì ì²´ ì ì" ìí¤í
ì²ë 기본 JDBC/JTAë¡ë¶í° ì´í리ì¼ì´ì
ì ì¶ìí ìí¤ê³ Hibernateë¡ íì¬ê¸
+ ìì¸í ê²ì ì²ë¦¬íê² íë¤.
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/full_cream.svg" format="SVG" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/full_cream.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ ë¤ìì ë¤ì´ì´ê·¸ë¨ë¤ ë´ì ìë ê°ì²´ë¤ì ëí ëªê°ì§ ì ìë¤ì´ë¤:
+
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term>SessionFactory (<literal>org.hibernate.SessionFactory</literal>)</term>
+ <listitem>
+ <para>
+ ë¨ì¼ ë°ì´í°ë² ì´ì¤ì ëí ì»´íì¼ë 매íë¤ì threadsafe (ë¶ë³ì) ìºì. Sessionê³¼ ConnectionProviderì
+ í´ë¼ì´ì¸í¸ë¥¼ ìí í©í 리. íë¡ì¸ì¤ ë 벨 ëë í´ë¬ì¤í° ë 벨ìì í¸ëìì
ë¤ ì¬ì´ì ì¬ì¬ì© ê°ë¥í ë°ì´í°ì ì íì ì¸
+ (second-level) ìºì를 ë³´ê´í ìë ìë¤.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Session (<literal>org.hibernate.Session</literal>)</term>
+ <listitem>
+ <para>
+ ì´í리ì¼ì´ì
ê³¼ ìì ì ì¥ì ì¬ì´ì ëí를 íííë ë¨ì¼ ì°ë ëì´ê³ , ìëª
ì´ ì§§ì ê°ì²´. JDBC 커ë¥ì
ì í¬ì¥íë¤.
+ <literal>Transaction</literal> ì© í©í 리. ê°ì²´ ê·¸ëí를 ë¤ë¹ê²ì´í¸ íê±°ë ìë³ìë¡ ê°ì²´ë¤ì 룩ì
í ë
+ ì¬ì©ëë ìì ê°ì²´ë¤ì ëí íìì ì¸(첫 ë²ì§¸ ë 벨ì) ìºì를 ë³´ê´íë¤.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>ìì ê°ì²´ë¤ê³¼ ì½ë ì
ë¤</term>
+ <listitem>
+ <para>
+ persistent ìíì ë¹ì§ëì¤ ê¸°ë¥ì í¬í¨íë ìëª
ì´ ì§§ê³ , ë¨ì¼ ì°ë ëì¸ ê°ì²´ë¤. ì´ê²ë¤ì íµìì JavaBeans/POJOë¤ì¼
+ ì ìê³ , ì¤ì§ ê·¸ê²ë¤ì ëí ì¤ì§ í¹ë³í ê²ì ê·¸ê²ë¤ì´ íì¬ (ì ííê² í ê°ì) <literal>Session</literal>ê³¼ ì°ê´ëì´
+ ìë¤ë ì ì´ë¤. <literal>Session</literal>ì´ ë«íìë§ì, ê·¸ê²ë¤ì ë¶ë¦¬ë (detached ìíê° ë ) ê²ì´ê³ ì´í리ì¼ì´ì
+ ë ì´ì´ìì ì¬ì©íë ê²ì´ ìì ë¡ìì§ë¤(ì를 ë¤ë©´. ì§ì ì ì¼ë¡ í리ì í
ì´ì
ê³ì¸µì¼ë¡
+ ë°ì´í° ì ì¡ ê°ì²´ë¤ë¡ì ê·¸ë¦¬ê³ ì§ì ì ì¼ë¡ í리ì í
ì´ì
ê³ì¸µì¼ë¡ë¶í° ë°ì´í° ì ì¡ ê°ì²´ë¤ë¡ì).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>ì ì´(Transient, íì 주-ê³¼ë) ê°ì²´ë¤ê³¼ ì½ë ì
ë¤</term>
+ <listitem>
+ <para>
+ <literal>Session</literal>ê³¼ íì¬ ì°ê´ëì´ ìì§ ìì ìì í´ëì¤ë¤ì ì¸ì¤í´ì¤ë¤. ê·¸ê²ë¤ì ì´í리ì¼ì´ì
ì ìí´ ì´ê¸°í
+ ëìê³ (ìì§) ììí ëì§ ììê±°ë ê·¸ê²ë¤ì ë«íì§<literal>Session</literal>ì ìí´ ì´ê¸°í ëìì ìë ìë¤.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Transaction (<literal>org.hibernate.Transaction</literal>)</term>
+ <listitem>
+ <para>
+ (ìµì
) ìì
ì ìì ë¨ì를 ì§ì í기 ìí´ ì´í리ì¼ì´ì
ì ìí´ ì¬ì©ëë ë¨ì¼ ì°ë ëì´ê³ , ìëª
ì´ ì§§ì ê°ì²´. 기본 JDBC, JTA
+ ëë CORBA í¸ëìì
ì¼ë¡ë¶í° ì´í리ì¼ì´ì
ì ì¶ìí ìí¨ë¤. ëªëª ê²½ì°ë¤ìì íëì <literal>Session</literal>ì
+ ì¬ë¬ ê°ì <literal>Transaction</literal>ë¤ì ê±¸ì¹ ì ìë¤. íì§ë§ 기본 API ëë <literal>Transaction</literal>ì
+ ì¬ì©íë í¸ëìì
ê²½ê³ ì¤ì ì ê²°ì½ ìµì
ì´ ìëë¤!
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>ConnectionProvider (<literal>org.hibernate.connection.ConnectionProvider</literal>)</term>
+ <listitem>
+ <para>
+ (ìµì
) JDBC 커ë¥ì
ë¤ì ëí í©í 리(ê·¸ë¦¬ê³ ê·¸ê²ë¤ì pool). 기본 <literal>Datasource</literal> ëë
+ <literal>DriverManager</literal>ë¡ë¶í° ì´í리ì¼ì´ì
ì ì¶ìí ìí¨ë¤. ì´í리ì¼ì´ì
ì ë
¸ì¶ëì§ë ìì§ë§
+ ê°ë°ìì ìí´ íì¥/구í ëë¤.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>TransactionFactory (<literal>org.hibernate.TransactionFactory</literal>)</term>
+ <listitem>
+ <para>
+ (ìµì
) <literal>Transaction</literal> ì¸ì¤í´ì¤ë¤ì ëí í©í 리. ì´í리ì¼ì´ì
ì ë
¸ì¶ëì§ë ìì§ë§ ê°ë°ìì ìí´
+ íì¥/구í ëë¤.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><emphasis>Extension Interfaces</emphasis></term>
+ <listitem>
+ <para>
+ Hibernateë ë¹ì ì ìì ê³ì¸µì í¹ì±ì ë§ì¶¤í ìí¤ê¸° ìí´ ë¹ì ì´ êµ¬íí ì ìë ì íì ì¸ íì¥ ì¸í°íì´ì¤ë¤ì ì ê³µíë¤.
+ ìì¸í ê²ì API 문ì를 ë³´ë¼.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ <para>
+ 주ì´ì§ "ê²½ëê¸" ìí¤í
ì²ì ê²½ì°, ì´í리ì¼ì´ì
ì JTA ëë JDBCì ì§ì ëíí기 ìí´ì <literal>Transaction</literal>/<literal>TransactionFactory</literal>
+ ê·¸ë¦¬ê³ /ëë <literal>ConnectionProvider</literal> APIë¤ì 무ìíë¤.
+ </para>
+ </sect1>
+
+ <sect1 id="architecture-states" revision="1">
+ <title>ì¸ì¤í´ì¤ ìíë¤</title>
+ <para>
+ ìì í´ëì¤ë¤ì ì¸ì¤í´ì¤ë ì¸ê°ì ìíë¤ ì¤ íë ì¼ ì ìë¤. ê·¸ê²ë¤(ìíë¤)ì ìì 컨í
ì¤í¸(<emphasis>persistence context</emphasis>)ì
+ ëí´ ì ìëë¤. Hibernate <literal>Session</literal> ê°ì²´ë ìì 컨í
ì¤í¸ì´ë¤:
+ </para>
+
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term>transient</term>
+ <listitem>
+ <para>
+ ì¸ì¤í´ì¤ë ììì 컨í
ì¤í¸ì ì°ê´ëì´ ìì§ ìê³ , ê²°ì½ ì°ê´ë ì ì´ ììë¤. ê·¸ê²ì ìì ìë³ì(íë¼ì´ë¨¸ë¦¬ í¤ ê°)ì ê°ì§ ìëë¤.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>persistent</term>
+ <listitem>
+ <para>
+ ì¸ì¤í´ì¤ë íì¬ ìì 컨í
ì¤í¸ì ì°ê´ëì´ ìë¤. ê·¸ê²ì ìì ìë³ì(íë¼ì´ë¨¸ë¦¬ í¤ ê°) ê·¸ë¦¬ê³ ìë§ ë°ì´í°ë² ì´ì¤ ë´ì ìë ëìíë
+ íì ê°ëë¤. í¹ë³í ìì 컨í
ì¤í¸ì ê²½ì°, Hibernateë ìì identityê° Java identity(ê°ì²´ì ë©ëª¨ë¦¬ ë´ ìì¹)ì ê°ë¤ë ì ì
+ <emphasis>ë³´ì¦íë¤</emphasis>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>detached</term>
+ <listitem>
+ <para>
+ ì¸ì¤í´ì¤ë ìì 컨í
ì¤í¸ì íë² ì°ê´ëìì§ë§, ê·¸ 컨í
ì¤í¸ê° ë«íê±°ë, ê·¸ ì¸ì¤í´ì¤ê° ë ë¤ë¥¸ íë¡ì¸ì¤ë¡ ì§ë ¬í ëìë¤. ê·¸ê²ì ìì
+ identity ê·¸ë¦¬ê³ , ìë§ ë°ì´í°ë² ì´ì¤ ë´ì ëìíë íì ê°ëë¤. detached ì¸ì¤í´ì¤ë¤ì ê²½ì°, Hibernateë ìì identityê³¼
+ Java identity ì¬ì´ì ê´ê³ë¥¼ ë³´ì¦íì§ ìëë¤.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </sect1>
+
+ <sect1 id="architecture-jmx" revision="1">
+ <title>JMX íµí©</title>
+
+ <para>
+ JMXë ìë° ì»´í¬ëí¸ ê´ë¦¬ë¥¼ ìí J2EE íì¤ì´ë¤. Hibernateë JMX íì¤ ìë¹ì¤ë¥¼ íµí´ ê´ë¦¬ë ìë ìë¤. ì°ë¦¬ë ë°°í¬ë³¸ ë´ì MBean 구í,
+ <literal>org.hibernate.jmx.HibernateService</literal>를 ì ê³µíë¤.
+ </para>
+
+ <para>
+ JBoss ì´í리ì¼ì´ì
ìë² ìì Hibernae를 JMX ìë¹ì¤ë¡ì ë°°ì¹íë ë°©ë²ì ëí ìì ë JBoss ì¬ì©ì ê°ì´ë를 보길 ë°ëë¤. JBoss
+ ì´í리ì¼ì´ì
ìë² ììì, ë§ì¼ ë¹ì ì´ JMX를 ì¬ì©íì¬ ë°°ì¹í ê²½ì° ë¹ì ì ëí ë¤ì ì´ì ë¤ì ì»ëë¤:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Session ê´ë¦¬:</emphasis> Hibernate <literal>Session</literal>ì ìëª
ì£¼ê¸°ê° JTA í¸ëìì
ì
+ ìì ë´ì ìëì ì¼ë¡ ë°ì¸ë ë ì ìë¤. ì´ê²ì ë¹ì ì´ ëì´ì <literal>Session</literal>ì ììì
ì¼ë¡ ì´ê³ ë«ì§ ììë
+ ë¨ì ì미íê³ , ì´ê²ì JBoss EJB ì¸í°ì
í°ì ì
ë¬´ê° ëë¤. ë¹ì ì ëí ë ì´ì ë¹ì ì ì½ë ì´ë ê³³ììë í¸ëìì
ê²½ê³ì¤ì ì
+ ëí´ ê±±ì íì§ ììë ëë¤(ë¹ì ì´ ë¬¼ë¡ ì´ìì± ìë ìì ê³ì¸µì ìì±íê³ ì ìíì§ ìëí, ì´ë¥¼ ìí´ ìµì
Hibernate <literal>Transaction</literal>
+ API를 ì¬ì©íë¼). ë¹ì ì <literal>Session</literal>ì ì ê·¼í기 ìí´ <literal>HibernateContext</literal>를 í¸ì¶íë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>HAR ë°°ì¹:</emphasis> ëê° ë¹ì ì JBoss ìë¹ì¤ ë°°ì¹ ëì¤í¬ë¦½í°ë¥¼ ì¬ì©íì¬ Hibernate JMX ìë¹ì¤ë¥¼
+ (EAR ê³¼/ëë SAR íì¼ë¡) ë°°ì¹íê³ , ê·¸ê²ì Hibernate <literal>SessionFactory</literal>ì íµìì ì¸ êµ¬ì±
+ ìµì
ë¤ ëª¨ë를 ì§ìíë¤. íì§ë§ ë¹ì ì ì¬ì í 모ë ë¹ì ì 매í íì¼ë¤ì ë°°ì¹ ëì¤í¬ë¦½í° ìì ëª
ëª
í´ì¼ íë¤. ë§ì¼ ë¹ì ì´
+ ìµì
HAR ë°°ì¹ë¥¼ ì¬ì©íê³ ì ê²°ì íë ê²½ì°, JBossë ë¹ì ì HAR íì¼ ë´ì ìë 모ë 매í íì¼ë¤ì ìëì ì¼ë¡ ê²ì¶í´ë¼ ê²ì´ë¤.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ì´ë¤ ìµì
ë¤ì ëí ì¶ê° ì ë³´ë JBoss ì´í리ì¼ì´ì
ìë² ì¬ì©ì ê°ì´ë를 참조íë¼.
+ </para>
+
+ <para>
+ JMX ìë¹ì¤ë¡ì ì´ì© ê°ë¥í ëë¤ë¥¸ í¹ì§ì ë°íì Hibernate íµê³ì´ë¤.
+ <xref linkend="configuration-optional-statistics"/>를 ë³´ë¼.
+ </para>
+ </sect1>
+
+ <sect1 id="architecture-jca" revision="1">
+ <title>JCA ì§ì</title>
+ <para>
+ Hibernateë JCA 커ë¥í°ë¡ì 구ì±ë ìë ìë¤. ìì¸í ê²ì ì¹ ì¬ì´í¸ë¥¼ 보길 ë°ëë¤. Hibernate JCA ì§ìì ì¬ì í ì¤íì ì¼ë¡
+ ê²í ì¤ì ììì ë
¸í¸í길 ë°ëë¤.
+ </para>
+ </sect1>
+
+ <sect1 id="architecture-current-session" revision="2">
+ <title>컨í
ì¤í¸ ìì ì¸ì
ë¤</title>
+ <para>
+ Hibernate를 ì¬ì©íë ëë¶ë¶ì ì´í리ì¼ì´ì
ë¤ì ì´ë¤ ììì "컨í
ì¤í¸ìì(contextual)" ì¸ì
ë¤ì íìë¡ íë¤. ì¬ê¸°ì
+ 주ì´ì§ ì¸ì
ì 주ì´ì§ 컨í
ì¤í¸ì ììì ê±¸ì³ íëì íë¤. íì§ë§ ì´í리ì¼ì´ì
ë¤ì ê°ë¡ì§ë¬ 컨í
ì¤í¸ë¥¼ 구ì±íë ê²ì ëí ì ìë
+ ì¼ë°ì ì¼ë¡ ë¤ë¥´ë¤; ê·¸ë¦¬ê³ ë¤ë¥¸ 컨í
ì¤í¸ë¤ì íì¬ë¼ê³ íë ê°ë
ì ëí´ ë¤ë¥¸ ììë¤ì ì ìíë¤. ë²ì 3.0 ì ì Hibernate를
+ ì¬ì©íë ì´í리ì¼ì´ì
ë¤ì ìê°ìì°ë <literal>ThreadLocal</literal>-기ë°ì 컨í
ì¤ìì ì¸ì
ë¤,
+ <literal>HibernateUtil</literal>ê³¼ ê°ì helper í´ëì¤ë¤ì íì©íê±°ë
+ íë½ì/ì¸í°ì
ì
기ë°ì 컨í
ì¤í¸ìì ì¸ì
ë¤ì ì ê³µí´ì£¼ìë (Spring ëë Picoì ê°ì )ì 3ì íë ììë¤ì íì©íë ê²½í¥ì´ ììë¤.
+ </para>
+ <para>
+ ë²ì 3.0.1ììë¶í° ììíì¬, Hibernateë <literal>SessionFactory.getCurrentSession()</literal> ë©ìë를
+ ì¶ê°íë¤. ì´ê¸°ì ì´ê²ì <literal>JTA</literal> í¸ëìì
ë¤ì ì¬ì©íë ê²ì ì ì íë¤. ì¬ê¸°ì <literal>JTA</literal>
+ í¸ëìì
ì íì¬ ì¸ì
ì ììê³¼ 컨í
ì¤í¸ë¥¼ ì ìíë¤. Hibernate íì ì±ìë ë¤ìì ì¤í ëì¼ë¡ <literal>JTA TransactionManager</literal>
+ 구íë¤ì´ ë°íëë©´, (ì ë¶ë ìëê² ì§ë§) ëë¶ë¶ì ì´í리ì¼ì´ì
ë¤ì´ ê·¸ê²ë¤ì´ <literal>J2EE</literal> 컨í
ì´ë ë´ë¡ ë°°ì¹ëë
+ ê·¸ë ì§ ìë ê°ì <literal>JTA</literal> í¸ëìì
ê´ë¦¬ë¥¼ ì¬ì©íê² ë ê²ì´ë¼ê³ 주ì¥íë¤. ê·¸ê²ì 기ì´íì¬, <literal>JTA</literal>ì 기ë°í
+ 컨í
ì¤í¸ìì ì¸ì
ë¤ì ì¸ì ê° ë¹ì ì´ ì¬ì©ì íìë¡ íê² ë ì ë¶ë¤.
+ </para>
+ <para>
+ íì§ë§ ë²ì 3.1 ì´íë¡ <literal>SessionFactory.getCurrentSession()</literal> ì´ë©´ì ì²ë¦¬ê³¼ì ì
+ ì´ì íë¬ê·¸ ê°ë¥íë¤. ê·¸ê²ì ëë´ê¸° ìí´, íëì ìë¡ì´ íì¥ ì¸í°íì´ì¤
+ (<literal>org.hibernate.context.CurrentSessionContext</literal>)ì
+ íëì ìë¡ì´ êµ¬ì± íë¼ë¯¸í°(<literal>hibernate.current_session_context_class</literal>)ê° íì¬ ì¸ì
ë¤ì
+ ì ìíë ììê³¼ 컨í
ì¤í¸ì íë¬ê·¸ ê°ë¥ì±ì íì©í기 ìí´ ì¶ê°ëìë¤.
+ </para>
+ <para>
+ ê·¸ê²ì ê³ì½ì ëí ìì¸í ë
¼ìë <literal>org.hibernate.context.CurrentSessionContext</literal> ì¸í°íì´ì¤ì
+ ê´í javadocs를 ë³´ë¼. ê·¸ê²ì íëì ë©ìë, <literal>currentSession()</literal>를 ì ìíë©°, ê·¸ 구íì
+ íì¬ì 컨í
ì¤í¸ ìì ì¸ì
ì ì¶ì í ì±
ìì´ ìë¤. ë¹ê³µìì ì¼ë¡, Hibernateë ì´ ì¸í°íì´ì¤ì ëí ì¸ ê°ì 구íë¤ì ë¶ìì ì¼ë¡
+ í¬í¨íê³ ìë¤.
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>org.hibernate.context.JTASessionContext</literal> - íì¬ì ì¸ì
ë¤ì íëì
+ <literal>JTA</literal>ì ìí´ ì¶ì ëê³ ììí ëë¤. ì¬ê¸°ì ì²ë¦¬ê³¼ì ì ì´ì ì JTA-ì ì© ì 근과 ì ííê²
+ ëì¼íë¤. ìì¸í ê²ì javadocs를 ë³´ë¼.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>org.hibernate.context.ThreadLocalSessionContext</literal> - íì¬ì ì¸ì
ë¤ì
+ ì¤í ì°ë ëì ìí´ ì¶ì ëë¤. ìì¸í ê²ì ë¤ì javadocs를 ë³´ë¼.
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+ ì²ìì ë 구íë¤ì <emphasis>session-per-request</emphasis>ë¡ ìë ¤ì§ê³ ì¬ì©ëê³ ìë
+ "íëì ì¸ì
- íëì ë°ì´í°ë² ì´ì¤ í¸ëìì
" íë¡ê·¸ëë° ëª¨íì ì ê³µíë¤. íëì Hibernate ì¸ì
ì ììê³¼ ëì
+ ë°ì´í°ë² ì´ì¤ í¸ëìì
ì ì¡´ì 기ê°ì ìí´ ì ìëë¤. ë§ì¼ (ì를 ë¤ë©´ ìì J2SEìì ëë JTA/UserTransaction/BMTì ê²½ì°ì)
+ ë¹ì ì´ íë¡ê·¸ë¨ ìì í¸ëìì
ê²½ê³êµ¬ë¶ì ì¬ì©í ê²½ì°, ë¹ì ì ë¹ì ì ì½ëë¡ë¶í° 기본 í¸ëìì
ìì¤í
ì ìíìí¤ëë°
+ Hibernate <literal>Transaction</literal> API를 ì¬ì©íë ê²ì´ ê¶ì¥ëë¤. ë§ì¼ ë¹ì ì´ CMT를 ì§ìíë
+ íëì EJB 컨í
ì´ëìì ì¤íí ê²½ì°, í¸ëìì
ê²½ê³ë¤ì´ ì ì¸ì ì¼ë¡ ì ìëê³ ë¹ì ì ë¹ì ì ì½ë ë´ì ì´ë¤ í¸ëìì
ë
+ ì¸ì
ê²½ê³êµ¬ë¶ ì¤í¼ë ì´ì
ë¤ì íìë¡ íì§ ìëë¤. ì¶ê° ì ë³´ì ì½ë ìì ë¤ì <xref linkend="transactions"/>를 참조íë¼.
+ </para>
+
+ <para>
+ <literal>hibernate.current_session_context_class</literal> êµ¬ì± íë¼ë¯¸í°ë
+ <literal>org.hibernate.context.CurrentSessionContext</literal> 구íì´ ì¬ì©ë ê²ìì ì ìíë¤. ìí¸íì ìí´.
+ ë§ì¼ ì´ êµ¬ì± íë¼ë¯¸í°ê° ì¤ì ëì§ ììì§ë§ íëì <literal>org.hibernate.transaction.TransactionManagerLookup</literal>ì´
+ 구ì±ëì´ ìì ê²½ì°, Hibernateë <literal>org.hibernate.context.JTASessionContext</literal>를 ì¬ì©í ê²ìì
+ ë
¸í¸íë¼. ì¼ë°ì ì¼ë¡, ì´ íë¼ë¯¸í°ì ê°ì ë¨ì§ ì¬ì©í 구í í´ëì¤ë¥¼ ëª
ëª
í ê²ì´ë¤; íì§ë§ ë ê°ì§ ë¹ê³µìì ì¸ êµ¬íë¤ë¡ì ë ê°ì
+ ëìíë 짧ì ì´ë¦ë¤ "jta"ì "thread"ì´ ì¡´ì¬íë¤.
+ </para>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/association_mapping.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/association_mapping.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/association_mapping.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,603 @@
+<chapter id="associations">
+
+ <title>ì°ê´ 매íë¤</title>
+
+ <sect1 id="assoc-intro" revision="1">
+ <title>ê°ì</title>
+
+ <para>
+ ì°ê´ 매íë¤ì ì¬ë°ë¥¸ ê²ì ì»ê¸°ê° ì¢
ì¢
ê°ì¥ ì´ë ¤ì´ ê²ì´ë¤. ì´ ì ìì ì°ë¦¬ë ë¨ë°©í¥ 매íë¤ìì ììíê³ , ê·¸ë° ë¤ì ìë°©í¥ ê²½ì°ë¤ì
+ ê²í í¨ì¼ë¡ì¨, íëì© íì¤ì ì¸ ê²½ì°ë¤ì ìì¸í ë
¼ìí ê²ì´ë¤. ì°ë¦¬ë 모ë ìì ë¤ìì <literal>Person</literal>ê³¼
+ <literal>Address</literal>를 ì¬ì©í ê²ì´ë¤.
+ </para>
+
+ <para>
+ ì°ë¦¬ë ì°ê´ë¤ì ì¤ì¬íë join í
ì´ë¸ë¡ 매íìí¬ ê²ì¸ì§ ì¬ë¶ì ë°ë¼, ê·¸ë¦¬ê³ multiplicity(ë¤ì¤ì±)ì ë°ë¼ ì°ê´ë¤ì ë¶ë¥í ê²ì´ë¤.
+ </para>
+
+ <para>
+ null íì© ê°ë¥í foreign í¤ë¤ì ì íµì ì¸ ë°ì´í° 모ë¸ë§ìì ì¢ì ì¤ë¡ë¡ ê°ì£¼ëì§ ììì, 모ë ì°ë¦¬ì ìì ë¤ì not null
+ foreign í¤ë¤ì ì¬ì©íë¤. ì´ê²ì Hibernateìì íìê° ìëê³ , ë¹ì ì´ null íì© ê°ë¥ 컨ì¤í¸ë ì¸í¸ë¤ì ëë¡ìí¬ ê²½ì° ë§¤íë¤ì
+ 모ë ëìí ê²ì´ë¤.
+ </para>
+
+ </sect1>
+
+ <sect1 id="assoc-unidirectional" revision="1">
+ <title>ë¨ë°©í¥ ì°ê´ë¤</title>
+
+ <sect2 id="assoc-unidirectional-m21">
+ <title>many to one</title>
+
+ <para>
+ <emphasis>ë¨ë°©í¥ many-to-one ì°ê´</emphasis>ì ê°ì¥ ê³µíµì ì¸ ì¢
ë¥ì ë¨ë°©í¥ ì°ê´ì´ë¤.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key, addressId bigint not null )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-121">
+ <title>one to one</title>
+
+ <para>
+ <emphasis>foreign í¤ì ëí ë¨ë°©í¥ one-to-one ì°ê´ì ëê° ì주 ëì¼íë¤.</emphasis> ì ì¼í ì°¨ì´ì ì
+ ì»¬ë¼ ì ì¼(unique) 컨ì¤í¸ë ì¸í¸ì´ë¤.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <many-to-one name="address"
+ column="addressId"
+ unique="true"
+ not-null="true"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key, addressId bigint not null unique )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ <para>
+ <emphasis>íëì íë¼ì´ë¨¸ë¦¬ í¤ì ëí ë¨ë°©í¥ one-to-one ì°ê´</emphasis>ì ëê° í¹ë³í id ìì±ê¸°ë¥¼ ì¬ì©íë¤.
+ (ì´ ìì ìì ì°ê´ì ë°©í¥ì´ ìì ëììì 주목íë¼.)
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+</class>
+
+<class name="Address">
+ <id name="id" column="personId">
+ <generator class="foreign">
+ <param name="property">person</param>
+ </generator>
+ </id>
+ <one-to-one name="person" constrained="true"/>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table Address ( personId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-12m">
+ <title>one to many</title>
+
+ <para>
+ <emphasis>íëì foreign í¤ì ëí ë¨ë°©í¥ one-to-many ì°ê´</emphasis>ì ë§¤ì° ìë¤ë¥¸ ê²½ì°ì´ê³ , ì¤ì ë¡ ê¶ì¥ëì§ ìëë¤.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses">
+ <key column="personId"
+ not-null="true"/>
+ <one-to-many class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table Address ( addressId bigint not null primary key, personId bigint not null )
+ ]]></programlisting>
+
+ <para>
+ ì°ë¦¬ë ì´ë° ì¢
ë¥ì ì°ê´ì ëí´ íëì join í
ì´ë¸ì ì¬ì©íë ê²ì´ ë ì¢ë¤ê³ ìê°íë¤.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="assoc-unidirectional-join" revision="1">
+ <title>join í
ì´ë¸ë¤ì ëí ë¨ë°©í¥ ì°ê´ë¤</title>
+
+ <sect2 id="assoc-unidirectional-join-12m">
+ <title>one to many</title>
+
+ <para>
+ <emphasis>íëì join í
ì´ë¸ì ëí ë¨ë°©í¥ one-to-many ì°ê´</emphasis>ì´ ë³´ë¤ ë ì í¸ëë¤. <literal>unique="true"</literal>를
+ ì§ì í¨ì¼ë¡ì¨ ì°ë¦¬ë many-to-manyìì one-to-manyë¡ ìì¤ì±(multiplicity)를 ë³ê²½ìì¼°ìì 주목íë¼.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses" table="PersonAddress">
+ <key column="personId"/>
+ <many-to-many column="addressId"
+ unique="true"
+ class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId not null, addressId bigint not null primary key )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-join-m21">
+ <title>many to one</title>
+
+ <para>
+ <emphasis>íëì join í
ì´ë¸ì ëí ë¨ë°©í¥ many-to-one ì°ê´</emphasis>ì ê·¸ ì°ê´ì´ ì íì ì¼ ë ë§¤ì° ê³µíµì ì´ë¤.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ optional="true">
+ <key column="personId" unique="true"/>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"/>
+ </join>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null primary key, addressId bigint not null )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-join-121">
+ <title>one to one</title>
+
+ <para>
+ <emphasis>íëì join í
ì´ë¸ì ëí ë¨ë°©í¥ one-to-one ì°ê´</emphasis>ì ê·¹í íµìì ì´ì§ ìì§ë§ ê°ë¥íë¤.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ optional="true">
+ <key column="personId"
+ unique="true"/>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"
+ unique="true"/>
+ </join>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-join-m2m">
+ <title>many to many</title>
+
+ <para>
+ ë§ì§ë§ì¼ë¡, ì°ë¦¬ë <emphasis>ë¨ë°©í¥ many-to-many ì°ê´</emphasis>ì ê°ëë¤.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses" table="PersonAddress">
+ <key column="personId"/>
+ <many-to-many column="addressId"
+ class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personId, addressId) )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="assoc-bidirectional" revision="1">
+ <title>ìë°©í¥ ì°ê´ë¤</title>
+
+ <sect2 id="assoc-bidirectional-m21" revision="2">
+ <title>one to many / many to one</title>
+
+ <para>
+ <emphasis>ìë°©í¥ many-to-one ì°ê´</emphasis>ì ê°ì¥ ê³µíµë ì¢
ë¥ì ì°ê´ì´ë¤.(ì´ê²ì íì¤ ë¶ëª¨/ìì ê´ê³ì´ë¤. )
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <set name="people" inverse="true">
+ <key column="addressId"/>
+ <one-to-many class="Person"/>
+ </set>
+</class>]]></programlisting>
+
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key, addressId bigint not null )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ <para>
+ ë§ì¼ ë¹ì ì´ <literal>List</literal>(ëë ë¤ë¥¸ ì¸ë±ì± ë ì½ë ì
)ì ì¬ì©í ê²½ì° ë¹ì ì foreign keyì
+ <literal>key</literal> 컬ë¼ì <literal>not null</literal>ë¡ ì¤ì íê³ , Hibernateë¡ íì¬ê¸
+ ê°ê°ì ììì ì¸ë±ì¤ë¥¼ ì ì§ê´ë¦¬í기 ìí´ (<literal>update="false"</literal>ì
+ <literal>insert="false"</literal>를 ì¤ì í¨ì¼ë¡ì¨ ë¤ë¥¸ 측ì ê°ìì ì¼ë¡ inverseë¡ ë§ë¤ì´)
+ ê·¸ ì½ë ì
ë¤ ì¸¡ì¼ë¡ë¶í° ì°ê´ì ê´ë¦¬íëë¡ í íìê° ìë¤:
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id"/>
+ ...
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"
+ insert="false"
+ update="false"/>
+</class>
+
+<class name="Address">
+ <id name="id"/>
+ ...
+ <list name="people">
+ <key column="addressId" not-null="true"/>
+ <list-index column="peopleIdx"/>
+ <one-to-many class="Person"/>
+ </list>
+</class>]]></programlisting>
+
+ <para>
+ ë§ì¼ 기본 ì¸ë í¤ ì»¬ë¼ì´ <literal>NOT NULL</literal>ì¼ ê²½ì° ì½ë ì
매íì <literal><key></literal>
+ ìì ìì <literal>not-null="true"</literal>를 ì ìíë ê²ì´ ì¤ìíë¤. ë´í¬ë
+ <literal><column></literal> ìì ìì <literal>not-null="true"</literal>를
+ ì ì¸íì§ ë§ê³ , <literal><key></literal> ìì ìì ì ì¸íë¼.
+ </para>
+
+ </sect2>
+
+ <sect2 id="assoc-bidirectional-121">
+ <title>one to one</title>
+
+ <para>
+ <emphasis>foreignì ëí ìë°©í¥ one-to-one ì°ê´</emphasis>ì 꽤 ê³µíµì ì´ë¤.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <many-to-one name="address"
+ column="addressId"
+ unique="true"
+ not-null="true"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <one-to-one name="person"
+ property-ref="address"/>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key, addressId bigint not null unique )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ <para>
+ <emphasis>íëì íë¼ì´ë¨¸ë¦¬ í¤ì ëí ìë°©í¥ one-to-one ì°ê´</emphasis>ì í¹ë³í id ìì±ê¸°ë¥¼ ì¬ì©íë¤.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <one-to-one name="address"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="personId">
+ <generator class="foreign">
+ <param name="property">person</param>
+ </generator>
+ </id>
+ <one-to-one name="person"
+ constrained="true"/>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table Address ( personId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="assoc-bidirectional-join" revision="1">
+ <title>join í
ì´ë¸ë¤ì ëí ìë°©í¥ ì°ê´ë¤</title>
+
+ <sect2 id="assoc-bidirectional-join-12m">
+ <title>one to many / many to one</title>
+
+ <para>
+ <emphasis>íëì join í
ì´ë¸ì ëí ìë°©í¥ one-to-many ì°ê´</emphasis>. <literal>inverse="true"</literal>ë
+ ì°ê´ì ì´ë 쪽 ëì´ë ì½ë ì
측ì¼ë¡ ëë join 측ì¼ë¡ ê° ì ìë¤.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses"
+ table="PersonAddress">
+ <key column="personId"/>
+ <many-to-many column="addressId"
+ unique="true"
+ class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ inverse="true"
+ optional="true">
+ <key column="addressId"/>
+ <many-to-one name="person"
+ column="personId"
+ not-null="true"/>
+ </join>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null, addressId bigint not null primary key )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-bidirectional-join-121">
+ <title>one to one</title>
+
+ <para>
+ <emphasis>íëì join í
ì´ë¸ì ëí ìë°©í¥ one-to-one ì°ê´</emphasis>ì ê·¹í íµìì ì´ì§ ìì§ë§, ê°ë¥íë¤.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ optional="true">
+ <key column="personId"
+ unique="true"/>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"
+ unique="true"/>
+ </join>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ optional="true"
+ inverse="true">
+ <key column="addressId"
+ unique="true"/>
+ <many-to-one name="person"
+ column="personId"
+ not-null="true"
+ unique="true"/>
+ </join>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-bidirectional-join-m2m" revision="1">
+ <title>many to many</title>
+
+ <para>
+ ë§ì§ë§ì¼ë¡, ì°ë¦¬ë íëì <emphasis>ìë°©í¥ many-to-many ì°ê´</emphasis>ì ê°ëë¤.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses" table="PersonAddress">
+ <key column="personId"/>
+ <many-to-many column="addressId"
+ class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <set name="people" inverse="true" table="PersonAddress">
+ <key column="addressId"/>
+ <many-to-many column="personId"
+ class="Person"/>
+ </set>
+</class>]]></programlisting>
+
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personId, addressId) )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="assoc-complex">
+ <title>ë³´ë¤ ë³µì¡í ì°ê´ 매íë¤</title>
+
+ <para>
+ ë³´ë¤ ë³µì¡í ì°ê´ ì¡°ì¸ë¤ì <emphasis>극기</emphasis> ë물ë¤.
+ Hibernateë 매í 문ìë¤ ë´ì ì½ì
ë SQL ì¡°ê°ë¤ì ì¬ì©íì¬ ë³´ë¤ ë³µì¡í ìí©ì ì²ë¦¬íë ê²ì
+ ê°ë¥íëë¡ í´ì¤ë¤. ì를 ë¤ì´, ë§ì¼ ê³ì¢ ë´ì ì ë³´ ë°ì´í°ë¥¼ ê°ì§ íëì´ í
ì´ë¸ì´
+ <literal>accountNumber</literal>, <literal>effectiveEndDate</literal>
+ ê·¸ë¦¬ê³ <literal>effectiveStartDate</literal> 컬ë¼ë¤ì ì ìí ê²½ì°, ë¤ìê³¼ ê°ì´ 매íëë¤:
+ </para>
+
+ <programlisting><![CDATA[<properties name="currentAccountKey">
+ <property name="accountNumber" type="string" not-null="true"/>
+ <property name="currentAccount" type="boolean">
+ <formula>case when effectiveEndDate is null then 1 else 0 end</formula>
+ </property>
+</properties>
+<property name="effectiveEndDate" type="date"/>
+<property name="effectiveStateDate" type="date" not-null="true"/>]]></programlisting>
+
+ <para>
+ ê·¸ë ì°ë¦¬ë ë¤ìì ì¬ì©íì¬ íëì ì°ê´ì <emphasis>íì¬</emphasis> ì¸ì¤í´ì¤
+ (null <literal>effectiveEndDate</literal>ì ê°ì§ ì¸ì¤í´ì¤)ë¡ ë§¤íìí¬ ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="currentAccountInfo"
+ property-ref="currentAccountKey"
+ class="AccountInfo">
+ <column name="accountNumber"/>
+ <formula>'1'</formula>
+</many-to-one>]]></programlisting>
+
+ <para>
+ ë³´ë¤ ë³µì¡í ìì ìì, <literal>Employee</literal>ì <literal>Organization</literal> ì¬ì´ì
+ ì°ê´ì´ ì ì²´ ê³ ì© ë´ì ë°ì´í°ë¥¼ ê°ì§ <literal>Employment</literal> í
ì´ë¸ ë´ì ì ì§ëë¤ê³ ê°ì íì.
+ ê·¸ë ì¢
ì
ìì <emphasis>ê°ì¥ ìµê·¼ì</emphasis> ê³ ì©ì£¼ì ëí íëì ì°ê´(ê°ì¥ ìµê·¼ì
+ <literal>startDate</literal>를 ê°ê³ ìë ê²)ì´ ë¤ì ë°©ë²ì¼ë¡ 매íë ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[<join>
+ <key column="employeeId"/>
+ <subselect>
+ select employeeId, orgId
+ from Employments
+ group by orgId
+ having startDate = max(startDate)
+ </subselect>
+ <many-to-one name="mostRecentEmployer"
+ class="Organization"
+ column="orgId"/>
+</join>]]></programlisting>
+
+ <para>
+ ë¹ì ì ì´ ê¸°ë¥ì¼ë¡ ì주 ìì°ì±ì ì»ì ì ìì§ë§, ê·¸ê²ì ëê° HQL ëë criteria ì§ì를 ì¬ì©íì¬ ì´ë¤ ì¢
ë¥ì ê²½ì°ë¤ì
+ ì²ë¦¬íë ê²ì´ ë³´ë¤ ì¤ì©ì ì´ë¤.
+ </para>
+
+ </sect1>
+
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/basic_mapping.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/basic_mapping.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/basic_mapping.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,3202 @@
+<chapter id="mapping">
+ <title>기본 O/R 매í</title>
+
+ <sect1 id="mapping-declaration" revision="1">
+ <title>매í ì ì¸</title>
+
+ <para>
+ ê°ì²´/ê´ê³í 매íë¤ì ëê° XML 문ì ë´ì ì ìëë¤. 매í 문ìë ê°ë
ì±ì´ ìê³ ììì
í¸ì§ì´ ê°ë¥íëë¡
+ ì¤ê³ëì´ ìë¤. 매í ì¸ì´ë 매íë¤ì´ í
ì´ë¸ ì ì¸ë¤ì´ ìë, ìì í´ëì¤ ì ì¸ë¤ë¡ ìì±ëë¤ë ì미ìì
+ ìë° ì¤ì¬ì ì´ë¤.
+ </para>
+
+ <para>
+ ì¬ì§ì´ ë§ì Hibernate ì¬ì©ìë¤ì´ ììì
ì¼ë¡ XMLì ìì±íê³ ì ì íí ì§ë¼ë, XDoclet, Middlegen,
+ ê·¸ë¦¬ê³ AndroMDA를 í¬í¨íë, 매í 문ì를 ìì±ìí¤ë ë§ì ë구ë¤ì´ ì¡´ì¬íë¤ë ì ì ë
¸í¸íë¼.
+ </para>
+
+ <para>
+ ìì 매íì¼ë¡ ììíì:
+ </para>
+
+ <programlisting id="mapping-declaration-ex1" revision="1"><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="eg">
+
+ <class name="Cat"
+ table="cats"
+ discriminator-value="C">
+
+ <id name="id">
+ <generator class="native"/>
+ </id>
+
+ <discriminator column="subclass"
+ type="character"/>
+
+ <property name="weight"/>
+
+ <property name="birthdate"
+ type="date"
+ not-null="true"
+ update="false"/>
+
+ <property name="color"
+ type="eg.types.ColorUserType"
+ not-null="true"
+ update="false"/>
+
+ <property name="sex"
+ not-null="true"
+ update="false"/>
+
+ <property name="litterId"
+ column="litterId"
+ update="false"/>
+
+ <many-to-one name="mother"
+ column="mother_id"
+ update="false"/>
+
+ <set name="kittens"
+ inverse="true"
+ order-by="litter_id">
+ <key column="mother_id"/>
+ <one-to-many class="Cat"/>
+ </set>
+
+ <subclass name="DomesticCat"
+ discriminator-value="D">
+
+ <property name="name"
+ type="string"/>
+
+ </subclass>
+
+ </class>
+
+ <class name="Dog">
+ <!-- mapping for Dog could go here -->
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ ì°ë¦¬ë ì´ì 매í 문ìì ë´ì©ì ë
¼ìí ê²ì´ë¤. ì°ë¦¬ë Hibernateì ìí´ ì¤í ìì ì¬ì©ëë 문ì ììë¤ê³¼
+ ìì±ë¤ ë§ì ì¤ëª
í ê²ì´ë¤. 매í 문ìë ëí ì¤í¤ë§ ë´ë³´ë´ê¸° ë구ì ìí´ ë´ë³´ë´ì§ ë°ì´í°ë² ì´ì¤ ì¤í¤ë§ì
+ ìí¥ì 주ë ì´ë¤ í¹ë³í ìµì
ìì±ë¤ê³¼ ììë¤ì í¬í¨íë¤. (ì를 ë¤ì´ <literal>not-null</literal> ìì±.)
+ </para>
+
+
+
+ <sect2 id="mapping-declaration-doctype" revision="3">
+ <title>Doctype</title>
+
+ <para>
+ 모ë XML 매íë¤ì doctypeì´ ë³´ì´ê² ì ì¸í´ì¼ íë¤. ì¤ì DTDë ìì URLìì,
+ <literal>hibernate-x.x.x/src/org/hibernate</literal> ëë í 리 ë´ìì ëë <literal>hibernate3.jar</literal>
+ ë´ìì ì°¾ì ì ìë¤. Hibernateë íì 첫 ë²ì§¸ë¡ ê·¸ê²ì classpath ììì DTD를 ì°¾ê² ë ê²ì´ë¤. ë§ì¼
+ ë¹ì ì´ ì¸í°ë· ì°ê²°ì ì¬ì©íë DTDì ëí 룩ì
ë¤ì ê²ªê² ë ê²½ì°, ë¹ì ì classpathì 컨í
ì¸ ì ëí´ ë¹ì ì DTD ì ì¸ì
+ ì²´í¬íë¼.
+ </para>
+
+ <sect3 id="mapping-declaration-entity-resolution">
+ <title>EntityResolver</title>
+ <para>
+ ìì ì¸ê¸íë¯ì´, Hibernateë 먼ì ê·¸ê²ì classpathìì DTDë¤ì í´ìíë ¤ê³ ìëí ê²ì´ë¤.
+ Hibernateê° ì´ê²ì ííë ë°©ë²ì ê·¸ê²ì´ xml íì¼ë¤ì ì½ì´ë¤ì´ëë° ì¬ì©íë SAXReaderì ë§ì¶¤í
+ <literal>org.xml.sax.EntityResolver</literal> 구íì ë±ë¡íë ê²ì´ë¤. ì´ ë§ì¶¤í
+ <literal>EntityResolver</literal>ë ë ê°ì ë¤ë¥¸ systemId namespaceë¤ì ì¸ì§í´ë¸ë¤.
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>hibernate namespace</literal>ë resolverê°
+ <literal>http://hibernate.sourceforge.net/</literal>ë¡ ììíë íëì
+ systemIdì ë§ë ëë§ë¤ ì¸ì§ëë¤; resolverë Hibernate í´ëì¤ë¤ì ë¡ëìì¼°ë í´ëì¤ë¡ë를 íµí´
+ ì´ë¤ ìí°í°ë¤ì í´ìíë ¤ê³ ìëíë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>user namespace</literal>ë resolverê° <literal>classpath://</literal>
+ URL íë¡í ì½ì ì¬ì©íë systemId를 ë§ë ëë§ë¤ ì¸ì§ëë¤; resolverë (1)íì¬ ì°ë ë 컨í
ì¤í¸
+ í´ëì¤ë¡ëì (2)Hibernate í´ëì¤ë¤ì ë¡ëìì¼°ë í´ëì¤ë¡ë를 íµí´ ì´ë¤ ìí°í°ë¤ì í´ìíë ¤ê³
+ ìëí ê²ì´ë¤.
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+ user namespacingì íì©íë ìì :
+ </para>
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" [
+ <!ENTITY types SYSTEM "classpath://your/domain/types.xml">
+]>
+
+<hibernate-mapping package="your.domain">
+ <class name="MyEntity">
+ <id name="id" type="my-custom-id-type">
+ ...
+ </id>
+ <class>
+ &types;
+</hibernate-mapping>]]></programlisting>
+ <para>
+ ì¬ê¸°ì <literal>types.xml</literal>ì <literal>your.domain</literal> í¨í¤ì§ ë´ì ìë 리ìì¤ì´ê³
+ ë§ì¶¤í <xref linkend="mapping-types-custom">typedef</xref>를 í¬í¨íë¤.
+ </para>
+ </sect3>
+ </sect2>
+
+ <sect2 id="mapping-declaration-mapping" revision="3">
+ <title>hibernate-mapping</title>
+
+ <para>
+ ì´ ììë ëª ê°ì ì íì ì¸ ìì±ë¤ì ê°ëë¤. <literal>schema</literal> ìì±ê³¼ <literal>catalog</literal>
+ ìì±ì ì´ ë§¤í ë´ìì 참조ë í
ì´ë¸ë¤ì´ ëª
ëª
ë schema ì/ëë catalogì ìíë¤ë ì ì ì§ì íë¤. ë§ì¼ ì§ì ë ê²½ì°,
+ í
ì´ë¸ ì´ë¦ë¤ì 주ì´ì§ schema ì´ë¦ê³¼ catalog ì´ë¦ì ìí´ íì (ìì)ë ê²ì´ë¤. ëë½ë ê²½ì°, í
ì´ë¸ ì´ë¦ë¤ì
+ íì ëì§((ììì´ê° ë¶ì§) ìì ê²ì´ë¤. <literal>default-cascade</literal> ìì±ì <literal>cascade</literal>
+ ìì±ì ì§ì íì§ ìì íë¡í¼í°ë¤ê³¼ ì½ë ì
ë¤ì ëí´ ì ì ë <literal>cascade</literal> ì¤íì¼ì´ 무ìì¸ì§ë¥¼ ì§ì íë¤.
+ <literal>auto-import</literal> ìì±ì ëí´í¸ë¡ ì°ë¦¬ê° ì§ì ì¸ì´ ììì ììì´ê° ë¶ì§ ìì(unqualified)
+ í´ëì¤ ì´ë¦ë¤ì ì¬ì©íê² í ê²ì´ë¤.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="hm1" coords="2 55"/>
+ <area id="hm2" coords="3 55"/>
+ <area id="hm3" coords="4 55"/>
+ <area id="hm4" coords="5 55"/>
+ <area id="hm5" coords="6 55"/>
+ <area id="hm6" coords="7 55"/>
+ <area id="hm7" coords="8 55"/>
+ </areaspec>
+ <programlisting><![CDATA[<hibernate-mapping
+ schema="schemaName"
+ catalog="catalogName"
+ default-cascade="cascade_style"
+ default-access="field|property|ClassName"
+ default-lazy="true|false"
+ auto-import="true|false"
+ package="package.name"
+ />]]></programlisting>
+ <calloutlist>
+ <callout arearefs="hm1">
+ <para>
+ <literal>schema</literal> (ìµì
): ë°ì´í°ë² ì´ì¤ ì¤í¤ë§ì ì´ë¦.
+ </para>
+ </callout>
+ <callout arearefs="hm2">
+ <para>
+ <literal>catalog</literal> (ìµì
): ë°ì´í°ë² ì´ì¤ ì¹´ë¤ë¡ì ì´ë¦.
+ </para>
+ </callout>
+ <callout arearefs="hm3">
+ <para>
+ <literal>default-cascade</literal> (ìµì
- ëí´í¸ë <literal>none</literal>):
+ ëí´í¸ cascade ì¤íì¼.
+ </para>
+ </callout>
+ <callout arearefs="hm4">
+ <para>
+ <literal>default-access</literal> (ìµì
- ëí´í¸ë <literal>property</literal>):
+ Hibernateê° ëª¨ë íë¡í¼í°ë¤ì ì¡ì¸ì¤íëë° ì¬ì©íê² ë ë°©ë. <literal>PropertyAccessor</literal>ì
+ ëí ë§ì¶¤í 구íì¼ ì ìë¤.
+ </para>
+ </callout>
+ <callout arearefs="hm5">
+ <para>
+ <literal>default-lazy</literal> (ìµì
- ëí´í¸ë <literal>true</literal>):
+ class ë° collection 매íë¤ì ì§ì ëì§ ìì <literal>lazy</literal> ìì±ë¤ì ëí ëí´í¸ ê°.
+ </para>
+ </callout>
+ <callout arearefs="hm6">
+ <para>
+ <literal>auto-import</literal> (ìµì
- ëí´í¸ë <literal>true</literal>):
+ ì°ë¦¬ê° ì§ì ì¸ì´ ë´ì (ì´ ë§¤íìì í´ëì¤ë¤ì ëí´) ììì´ê° ë¶ì§ ìì í´ëì¤ ì´ë¦ë¤ì ì¬ì©í ì ìëì§ë¥¼
+ ì§ì íë¤.
+ </para>
+ </callout>
+ <callout arearefs="hm7">
+ <para>
+ <literal>package</literal> (ìµì
): 매í 문ì ë´ìì ììì´ê° ë¶ì§ ìì í´ëì¤ ì´ë¦ë¤ì ëí´
+ ê°ì í í¨í¤ì§ ì ëì´ë¥¼ ì§ì íë¤.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ ë§ì¼ ë¹ì ì´ ëì¼í (ììì´ê° ë¶ì§ ìì) ì´ë¦ì ê°ì§ ë ê°ì ìì í´ëì¤ë¤ì ê°ê³ ìë¤ë©´, ë¹ì ì <literal>auto-import="false"</literal>를
+ ì¤ì í´ì¼ íë¤. ë§ì¼ ë¹ì ì´ ë ê°ì í´ëì¤ë¤ì ëì¼í "imported" ì´ë¦ì í ë¹íë ¤ê³ ìëí ê²½ì°ì Hibernateë ìì¸ìí©ì ëì§ ê²ì´ë¤.
+ </para>
+
+ <para>
+ ìì ë³´ì¬ì§ ê²ì²ë¼ <literal>hibernate-mapping</literal> ììë ëªëª ìì <literal><class></literal> 매íë¤ì ë´ë¶ì í¬í¨íë
+ ê²ì íì©í´ì¤ë¤ë ì ì ë
¸í¸íë¼. íì§ë§ í ê°ì 매í íì¼ ìì í ê°ì ìì í´ëì¤(ëë í ê°ì í´ëì¤ ê³ì¸µêµ¬ì¡°) ë§ì 매ííê³ ìì ìë¸ í´ëì¤ ë¤ì
+ ê·¸ê²ì ëª
ëª
íë ê²ì´ ì¢ì ì°ìµì´ë¤ (ê·¸ë¦¬ê³ ëªëª ë구ë¤ì ìí´ ê¸°ëëë¤). ì를 ë¤ë©´ <literal>Cat.hbm.xml</literal>,
+ <literal>Dog.hbm.xml</literal> ëë ììì ì¬ì©í ê²½ì°ìë <literal>Animal.hbm.xml</literal>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-class" revision="3">
+ <title>class</title>
+
+ <para>
+ ë¹ì ì <literal>class</literal> ìì를 ì¬ì©íì¬ ìì í´ëì¤ë¥¼ ì ì¸í ìë ìë¤:
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="class1" coords="2 55"/>
+ <area id="class2" coords="3 55" />
+ <area id="class3" coords="4 55"/>
+ <area id="class4" coords="5 55" />
+ <area id="class5" coords="6 55"/>
+ <area id="class6" coords="7 55" />
+ <area id="class7" coords="8 55"/>
+ <area id="class8" coords="9 55" />
+ <area id="class9" coords="10 55" />
+ <area id="class10" coords="11 55"/>
+ <area id="class11" coords="12 55"/>
+ <area id="class12" coords="13 55"/>
+ <area id="class13" coords="14 55"/>
+ <area id="class14" coords="15 55"/>
+ <area id="class15" coords="16 55"/>
+ <area id="class16" coords="17 55"/>
+ <area id="class17" coords="18 55"/>
+ <area id="class18" coords="19 55"/>
+ <area id="class19" coords="20 55"/>
+ <area id="class20" coords="21 55"/>
+ <area id="class21" coords="22 55"/>
+ </areaspec>
+ <programlisting><![CDATA[<class
+ name="ClassName"
+ table="tableName"
+ discriminator-value="discriminator_value"
+ mutable="true|false"
+ schema="owner"
+ catalog="catalog"
+ proxy="ProxyInterface"
+ dynamic-update="true|false"
+ dynamic-insert="true|false"
+ select-before-update="true|false"
+ polymorphism="implicit|explicit"
+ where="arbitrary sql where condition"
+ persister="PersisterClass"
+ batch-size="N"
+ optimistic-lock="none|version|dirty|all"
+ lazy="true|false"
+ entity-name="EntityName"
+ check="arbitrary sql check condition"
+ rowid="rowid"
+ subselect="SQL expression"
+ abstract="true|false"
+ node="element-name"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="class1">
+ <para>
+ <literal>name</literal> (ìµì
): ìì í´ëì¤(ëë ì¸í°íì´ì¤)ì ì ì²´ ììì´ê° ë¶ì Java í´ëì¤ ì´ë¦.
+ ë§ì¼ ì´ ìì±ì´ ëë½ë ê²½ì°, 매íì´ non-POJO ìí°í°ë¼ê³ ê°ì ëë¤.
+ </para>
+ </callout>
+ <callout arearefs="class2">
+ <para>
+ <literal>table</literal> (ìµì
- ëí´í¸ë ììì´ê° ë¶ì§ ìì í´ëì¤ëª
): ê·¸ê²ì ë°ì´í°ë² ì´ì¤ í
ì´ë¸ì ì´ë¦.
+ </para>
+ </callout>
+ <callout arearefs="class3">
+ <para>
+ <literal>discriminator-value</literal> (ìµì
- ëí´í¸ë í´ëì¤ ì´ë¦): ë¤íì±(polymorphic) í¹ì§ì ì¬ì©ëë,
+ ê°ë³ ìë¸ í´ëì¤ë¤ë¥¼ 구ë³ì§ë ê°. íì©ê°ë¥í ê°ë¤ì<literal>null</literal>ê³¼ <literal>not null</literal>ì í¬í¨íë¤.
+ </para>
+ </callout>
+ <callout arearefs="class4">
+ <para>
+ <literal>mutable</literal> (ìµì
- ëí´í¸ë <literal>true</literal>): í´ëì¤ë¤ì ì¸ì¤í´ì¤ë¤ì´ ê°ë³ì ì¸ì§ë¥¼
+ (ê°ë³ì ì´ì§ ììì§ë¥¼) ì§ì íë¤.
+ </para>
+ </callout>
+ <callout arearefs="class5">
+ <para>
+ <literal>schema</literal> (ìµì
): ë£¨í¸ <literal><hibernate-mapping></literal> ììì ìí´ ì§ì ë
+ ì¤í¤ë§ ì´ë¦ì ì¤ë²ë¼ì´ë ìí¨ë¤.
+ </para>
+ </callout>
+ <callout arearefs="class6">
+ <para>
+ <literal>catalog</literal> (ìµì
): ë£¨í¸ <literal><hibernate-mapping></literal> ììì ìí´ ì§ì ë
+ ì¹´ë¤ë¡ ì´ë¦ì ì¤ë²ë¼ì´ë ìí¨ë¤.
+ </para>
+ </callout>
+ <callout arearefs="class7">
+ <para>
+ <literal>proxy</literal> (ìµì
): lazy initializing proxyë¤ì ì¬ì©í ì¸í°íì´ì¤ë¥¼ ì§ì íë¤. ë¹ì ì í´ëì¤
+ ê·¸ ìì²´ì ì´ë¦ì ì§ì í ì ë ìë¤.
+ </para>
+ </callout>
+ <callout arearefs="class8">
+ <para>
+ <literal>dynamic-update</literal> (ìµì
- ëí´í¸ë <literal>false</literal>):
+ <literal>UPDATE</literal> SQLì´ ì¤í ìì ìì±ëê³ ê·¸ë¤ ì»¬ë¼ë¤ì ê°ë¤ì´ ë³ê²½ë ê·¸ë¤ ì»¬ë¼ë¤ ë§ì
+ í¬í¨í ê²ì¸ì§ë¥¼ ì§ì íë¤.
+ </para>
+ </callout>
+ <callout arearefs="class9">
+ <para>
+ <literal>dynamic-insert</literal> (ìµì
- ëí´í¸ë <literal>false</literal>):
+ ìì±ë <literal>INSERT</literal>ì´ ì¤í ìì ìì±ëê³ ê·¸ë¤ ì»¬ë¼ë¤ì ê°ì´ nullì´ ìë 컬ë¼ë¤ ë§ì
+ í¬í¨í ê²ì¸ì§ë¥¼ ì§ì íë¤.
+ </para>
+ </callout>
+ <callout arearefs="class10">
+ <para>
+ <literal>select-before-update</literal> (ìµì
- ëí´í¸ë <literal>false</literal>):
+ ê°ì²´ê° ì¤ì ë¡ ë³ê²½ëë ê²ì´ íì¤íì§ ìë í, Hibernateê° SQL <literal>UPDATE</literal>를
+ <emphasis>ê²°ì½</emphasis> ì¤ííì§ ìì ê²ìì ì§ì íë¤. ì´ë¤ ê²½ì°ë¤ìì(ì¤ì ë¡ transient
+ ê°ì²´ê° <literal>update()</literal>를 ì¬ì©íì¬ ìë¡ì´ sessionì ì°ê´ëìì ëìë§), ì´ê²ì
+ íëì <literal>UPDATE</literal>ê° ì¤ì ë¡ íìí ê²½ì°ì¸ì§ ì¬ë¶ë¥¼ ê²°ì í기 ìí´ Hibernateë
+ í¹ë³í SQL <literal>SELECT</literal>를 ì¤íí ê²ìì ì미íë¤.
+ </para>
+ </callout>
+ <callout arearefs="class11">
+ <para>
+ <literal>polymorphism</literal> (ìµì
- ëí´í¸ë <literal>implicit</literal>):
+ implicit ì§ì ë¤íì±ì´ë explicit ì§ì ë¤íì± ì¤ ì´ë ê²ì´ ì¬ì©ë ê²ì¸ì§ë¥¼ ê²°ì íë¤.
+ </para>
+ </callout>
+ <callout arearefs="class12">
+ <para>
+ <literal>where</literal> (ìµì
) ì´ í´ëì¤ì ê°ì²´ë¤ì ê²ìí ë ì¬ì©ë ììì ì¸ SQL
+ <literal>WHERE</literal> ì¡°ê±´ì ì§ì íë¤
+ </para>
+ </callout>
+ <callout arearefs="class13">
+ <para>
+ <literal>persister</literal> (ìµì
): ë§ì¶¤í <literal>ClassPersister</literal>를 ì§ì íë¤.
+ </para>
+ </callout>
+ <callout arearefs="class14">
+ <para>
+ <literal>batch-size</literal> (ìµì
- ëí´í¸ë <literal>1</literal>) ìë³ìì ìí´ ì´ í´ëì¤ì
+ ì¸ì¤í´ì¤ë¤ì íì¹ìí¤ë "ë°°ì¹ ì¬ì´ì¦"를 ì§ì íë¤.
+ </para>
+ </callout>
+ <callout arearefs="class15">
+ <para>
+ <literal>optimistic-lock</literal> (ìµì
- ëí´í¸ë <literal>version</literal>):
+ optimistic ì ê¸ ë°©ë를 ê²°ì íë¤.
+ </para>
+ </callout>
+ <callout arearefs="class16">
+ <para>
+ <literal>lazy</literal> (ìµì
): <literal>lazy="false"</literal>를 ì¤ì í¨ì¼ë¡ì¨
+ Lazy fetchingì´ ì ì²´ì ì¼ë¡ ì¬ì©ë¶ê°ë¥íê² ë ì ìë¤.
+ </para>
+ </callout>
+ <callout arearefs="class17">
+ <para>
+ <literal>entity-name</literal>(ìµì
, ëí´í¸ë í´ëì¤ ì´ë¦): Hibernate3ë íëì í´ëì¤ê°
+ (ì ì ì ì¼ë¡ ë¤ë¥¸ í
ì´ë¸ë¤ë¡) ì¬ë¬ë² 매íëë ê²ì íì©í´ì£¼ê³ , Java ë 벨ìì Map ëë XMLì ìí´ íí
+ ëë ìí°í° 매íë¤ì íì©íë¤. ì´ë¤ ê²½ì°ë¤ìì, ë¹ì ì ê·¸ ìí°í°ì ëí ëª
ìì ì¸ ììì ì´ë¦ì ì ê³µí´ì¼ íë¤.
+ <literal>entity-name</literal> (ìµì
): Hibernate3ë íëì í´ëì¤ê° (ì ì ì ì¼ë¡ ë¤ë¥¸ í
ì´ë¸ë¤ë¡)
+ ì¬ë¬ ë² ë§¤íëë ê²ì íì©íë©°, ìë° ë 벨ìì Mapë¤ ëë XMLì ìí´ ííëë ìí°í° 매íë¤ì íì©íë¤.
+ ì´ë¤ ê²½ì°ë¤ìì, ë¹ì ì ê·¸ ìí°í°ë¤ì ëí ëª
ìì ì¸ ììì ì´ë¦ì ì ê³µí´ì¼ íë¤. ì¶ê° ì ë³´ë
+ <xref linkend="persistent-classes-dynamicmodels"/>ê³¼ <xref linkend="xml"/>ì ë³´ë¼.
+ </para>
+ </callout>
+ <callout arearefs="class18">
+ <para>
+ <literal>check</literal> (ìµì
): ìëì ì¸ ì¤í¤ë§ ìì±ì ìí ë¤ì¤-í <emphasis>check</emphasis>
+ constraint를 ìì±ìí¤ëë° ì¬ì©ëë SQL ííì.
+ </para>
+ </callout>
+ <callout arearefs="class19">
+ <para>
+ <literal>rowid</literal> (ìµì
): Hibernateë ì§ìëë ë°ì´í°ë² ì´ì¤ë¤, ì를 ë¤ì´ Oracle ììì ì´ë¥¸ë°
+ ROWIDë¤ì ì¬ì©í ì ìê³ , Hibernateë ë¹ì ì´ ì´ ìµì
ì <literal>rowid</literal>ë¡ ì¤ì íë ê²½ì°ì ë¹ ë¥¸
+ ì
ë°ì´í¸ë¥¼ ìí í¹ë³í <literal>rowid</literal> 컬ë¼ì ì¬ì©í ì ìë¤. ROWIDë 구í ìì¸ì´ê³ ì ì¥ë
+ íí(tuple)ì 물리ì ì´ë ìì¹ë¥¼ íííë¤.
+ </para>
+ </callout>
+ <callout arearefs="class20">
+ <para>
+ <literal>subselect</literal> (ìµì
): ë¶ë³ì ì½ê¸° ì ì© ìí°í°ë¥¼ ë°ì´í°ë² ì´ì¤ subselectë¡ ë§¤íìí¨ë¤.
+ ë¹ì ì´ ê¸°ë³¸ í
ì´ë¸ ëì ì 뷰를 ê°ê³ ì ìí ê²½ì°ì ì ì©íì§ë§, ì¬ì©ì ìì íë¼. ì¶ê° ì ë³´ë ìë를 ë³´ë¼.
+ </para>
+ </callout>
+ <callout arearefs="class21">
+ <para>
+ <literal>abstract</literal> (ìµì
): <literal><union-subclass></literal> ê³ì¸µ êµ¬ì¡°ë¤ ë´ìì
+ abstract ìí¼í´ëì¤ë¤ì ë§í¬íëë° ì¬ì©ëë¤.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ ëª
ëª
ë ìì í´ëì¤ê° ì¸í°íì´ì¤ê° ëë ê²ì ìì í ìì©ê°ë¥íë¤. ê·¸ë° ë¤ì ë¹ì ì <literal><subclass></literal>
+ ìì를 ì¬ì©íì¬ ê·¸ ì¸í°íì´ì¤ì ëí 구í í´ëì¤ë¤ì ì ì¸í ê²ì´ë¤. ë¹ì ì ììì <emphasis>static</emphasis> inner
+ í´ëì¤ë¥¼ ììí ìí¬ ì ìë¤. ë¹ì ì íì¤ íì, ì를 ë¤ì´ <literal>eg.Foo$Bar</literal>를 ì¬ì©íì¬ í´ëì¤ ì´ë¦ì
+ ì§ì í´ì¼ íë¤.
+ </para>
+
+ <para>
+ ë¶ë³ì í´ëì¤, <literal>mutable="false"</literal>ë ì´í리ì¼ì´ì
ì ìí´ ì
ë°ì´í¸ëì§ ìì ê²ì´ê±°ë ìì ëì§ ìì ê²ì´ë¤.
+ ì´ê²ì Hibernateë¡ íì¬ê¸ ì´ë¤ ë§ì´ë í¼í¬ë¨¼ì¤ ìµì í를 ííê²ë íì©í´ì¤ë¤.
+ </para>
+
+ <para>
+ ì íì ì¸ <literal>proxy</literal> ìì±ì ê·¸ í´ëì¤ì ìì ì¸ì¤í´ì¤ë¤ì ëí lazy ì´ê¸°í를 ê°ë¥íê² í´ì¤ë¤. Hibernateë
+ ëª
ëª
ë ì¸í°íì´ì¤ë¥¼ 구ííë CGLIB íë½ìë¤ì ì´ê¸°ì ë°íí ê²ì´ë¤. ì¤ì ìì ê°ì²´ë íë½ìì ë©ìëê° í¸ì¶ë ë ë¡ëë ê²ì´ë¤.
+ ìë "Lazy ì´ê¸°í를 ìí íë½ìë¤"ì ë³´ë¼.
+ </para>
+
+ <para><emphasis>Implicit</emphasis> ë¤íì±ì í´ëì¤ì ì¸ì¤í´ì¤ë¤ì´ ì´ë¤ ìë¸í´ëì¤ë 구íë ì¸í°íì´ì¤ ëë í´ëì¤ë¥¼ ëª
ëª
íë
+ ì§ìì ìí´ ë°íë ê²ìì ì미íê³ ê·¸ í´ëì¤ì ì´ë¤ ìë¸í´ëì¤ì ëí ì¸ì¤í´ì¤ë¤ì´ ê·¸ í´ëì¤ ì체를 ëª
ëª
íë ì§ìì ìí´ ë°íë ê²ìì
+ ì미íë¤. <emphasis>Explicit</emphasis> ë¤íì±ì í´ëì¤ ì¸ì¤í´ì¤ë¤ì´ ê·¸ í´ëì¤ë¥¼ ëª
ìì ì¼ë¡ ëª
ëª
íë ì§ìë¤ì ìí´ìë§
+ ë°íë ê²ìì ìë¯¸ê³ ê·¸ í´ëì¤ë¥¼ ëª
ëª
íë ì§ìë¤ì´ ì´ <literal><class></literal> ì ì¸ ë´ë¶ìì <literal><subclass></literal>
+ ëë <literal><joined-subclass></literal>ë¡ ë§¤íë ìë¸ í´ëì¤ë¤ì ì¸ì¤í´ì¤ë¤ ë§ì ë°ííê² ë ê²ìì ì미íë¤.
+ ëë¶ë¶ì ì©ëë¡, ëí´í¸ì¸ <literal>polymorphism="implicit"</literal>ê° ì ì íë¤.ë ê°ì ë¤ë¥¸ í´ëì¤ë¤ì´ ëì¼í í
ì´ë¸ë¡
+ 매íë ë Explicit ë¤íì±ì´ ì ì©íë¤(ì´ê²ì í
ì´ë¸ 컬ë¼ë¤ì ìë¸ì
ì í¬í¨íë "ê²½ëê¸" í´ëì¤ë¥¼ íì©íë¤).
+ </para>
+
+ <para>
+ <literal>persister</literal> ìì±ì í´ëì¤ì ì¬ì©ëë ììí ë°©ë를 ë¹ì ì´ ì»¤ì¤í¸ë§ì´ì§ í ì ìëë¡ í´ì¤ë¤. ì를 ë¤ì´
+ ë¹ì ì <literal>org.hibernate.persister.EntityPersister</literal>ì ëí ë¹ì ìì ì ìë¸í´ëì¤ë¥¼ ì§ì í ìë
+ ìê±°ë ë¹ì ì ì¬ì§ì´ ì를 ë¤ì´ íë« íì¼ë¤ì´ë LDAPë¡ì ì§ë ¬í,ë´ì¥ íë¡ìì í¸ì¶ë¤ì íµí´ ììí를 구ííë ì¸í°íì´ì¤
+ <literal>org.hibernate.persister.ClassPersister</literal>ì ëí ìì í ìë¡ì´ 구íì ì ê³µí ìë ìë¤.
+ (<literal>Hashtable</literal>ë¡ì "ììì±"ì ê´í) ê°ë¨í ìì ë
+ <literal>org.hibernate.test.CustomPersister</literal>를 ë³´ë¼.
+ </para>
+
+ <para>
+ <literal>dynamic-update</literal> ì¤ì ê³¼ <literal>dynamic-insert</literal> ì¤ì ì ìë¸í´ëì¤ë¤ì ìí´
+ ììëì§ ìê³ ë°ë¼ì ëí <literal><subclass></literal> ëë <literal><joined-subclass></literal>
+ ììë¤ ìì ì§ì ë ìë ììì ë
¸í¸íë¼. ì´ë¤ ì¤ì ë¤ì ëªëª ê²½ì°ë¤ìì í¼í¬ë¨¼ì¤ë¥¼ ì¦ê°ìí¤ì§ë§ ë¤ë¥¸ ê²½ì°ë¤ììë í¼í¬ë¨¼ì¤ë¥¼
+ ì¤ì ë¡ ê°ììí¬ ìë ìë¤. ì ì íê² ì¬ì©íë¼.
+ </para>
+
+ <para>
+ <literal>select-before-update</literal> ì¬ì©ì ëê° í¼í¬ë¨¼ì¤ë¥¼ ê°ììí¬ ê²ì´ë¤. ë¹ì ì´ detached ì¸ì¤í´ì¤ë¤ì
+ ê·¸ëí를 <literal>Session</literal>ì ë¤ì 첨ë¶í ê²½ì°ì ê·¸ê²ì ë°ì´í°ë² ì´ì¤ ì
ë°ì´í¸ í¸ë¦¬ê±°ê° ë¶íìíê² í¸ì¶ëë
+ ê²ì ë°©ì§íëë° ë§¤ì° ì ì©íë¤.
+ </para>
+
+ <para>
+ <literal>dynamic-update</literal>를 ì¬ì©ê°ë¥íê² í ê²½ì°, ë¹ì ì ë¤ì optimistic ì ê¸ ì ëµë¤ì ì ííê² ë ê²ì´ë¤:
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>version</literal>ì version/timestamp 컬ë¼ë¤ì ì²´í¬íë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>all</literal>ì 모ë 컬ë¼ë¤ì ì²´í¬íë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>dirty</literal>ë ëªëª ëìì± ì
ë°ì´í¸ë¤ì íì©íì¬, ë³ê²½ë 컬ë¼ë¤ì ì²´í¬íë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>none</literal>ì optimistic ì ê¸ì ì¬ì©íì§ ìëë¤
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+ ì°ë¦¬ë ë¹ì ì´ Hibernateìì optimistic ì ê¸ì ìí´ version/timestamp 컬ë¼ë¤ì ì¬ì©í ê²ì <emphasis>매ì°</emphasis> ê°ë ¥íê²
+ ê¶ì¥íë¤. ì´ê²ì í¼í¬ë¨¼ì¤ì ëí´ ìµì ì ë°©ëì´ê³ detached ì¸ì¤í´ì¤ë¤ì ëí´ íí´ì§ ë³ê²½ë¤ì ì ííê² í¸ë¤ë§íë ì ì¼í ë°©ëì´ë¤(ì를 ë¤ì´
+ <literal>Session.merge()</literal>ê° ì¬ì©ë ë).
+ </para>
+
+ <para>
+ Hibernate 매íì ê²½ì°ì ë² ì´ì¤ í
ì´ë¸ê³¼ ë·° ì¬ì´ì ì°¨ì´ì ì´ ì¡´ì¬íì§ ìëë¤. ìëíë©´ ì´ê²ì´ ë°ì´í°ë² ì´ì¤ ë 벨ììë í¬ëª
íë¤ê³ 기ëë기
+ ë문ì´ë¤(ëªëª DBMSë 뷰를 ê³ ì íê² ì§ìíì§ ìê³ í¹í ë·° ì
ë°ì´í¸ë¥¼ ì§ìíì§ ììì ë
¸í¸íë¼). ëëë¡ ë¹ì ì´ ë·°ë¥¼ ì¬ì©íê³ ì ìíì§ë§,
+ (ì를 ë¤ì´ 리거ì ì¤í¤ë§ë¡) ë°ì´í°ë² ì´ì¤ ìì 뷰를 ìì±ìí¬ ì ìë¤. ì´ ê²½ì°ì, ë¹ì ì ë¶ë³ì ì½ê¸° ì ì© ìí°í°ë¥¼ 주ì´ì§ SQL subselect
+ ííìì¼ë¡ 매íìí¬ ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[<class name="Summary">
+ <subselect>
+ select item.name, max(bid.amount), count(*)
+ from item
+ join bid on bid.item_id = item.id
+ group by item.name
+ </subselect>
+ <synchronize table="item"/>
+ <synchronize table="bid"/>
+ <id name="name"/>
+ ...
+</class>]]></programlisting>
+
+ <para>
+ auto-flushê° ì ííê² ë°ìíëë¡ íê³ , ê·¸ë¦¬ê³ íìë ìí°í°ì ëí ì§ìë¤ì´ ì¸í¨ì± ìë ë°ì´í°ë¥¼ ë°ííì§ ìëë¡ í¨ì¼ë¡ì¨, ì´ ìí°í°ì ë기í ë
+ í
ì´ë¸ì ì ì¸íë¼. <literal><subselect></literal>ë ìì±ê³¼ ë´í¬ë 매í ìì ììë¡ì ì´ì© ê°ë¥íë¤.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-id" revision="4">
+ <title>id</title>
+
+ <para>
+ 매íë í´ëì¤ë¤ì ë°ì´í°ë² ì´ì¤ í
ì´ë¸ì íë¼ì´ë¨¸ë¦¬ í¤ ì»¬ë¼ì ì ì¸<emphasis>í´ì¼ íë¤</emphasis>. ëë¶ë¶ì í´ëì¤ë¤ì ëí ì¸ì¤í´ì¤ì ì ì¼ ìë³ì를 ìì íë
+ ìë°ë¹ì¦-ì¤íì¼ íë¡í¼í°ë¥¼ ê°ì§ ê²ì´ë¤. <literal><id></literal> ììë ê·¸ íë¡í¼í°ë¡ë¶í° íë¼ì´ë¨¸ë¦¬ í¤ ì»¬ë¼ì¼ë¡ì 매íì ì ìíë¤.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="id1" coords="2 70"/>
+ <area id="id2" coords="3 70" />
+ <area id="id3" coords="4 70"/>
+ <area id="id4" coords="5 70" />
+ <area id="id5" coords="6 70" />
+ </areaspec>
+ <programlisting><![CDATA[<id
+ name="propertyName"
+ type="typename"
+ column="column_name"
+ unsaved-value="null|any|none|undefined|id_value"
+ access="field|property|ClassName">
+ node="element-name|@attribute-name|element/@attribute|."
+
+ <generator class="generatorClass"/>
+</id>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="id1">
+ <para>
+ <literal>name</literal> (ìµì
): ìë³ì íë¡í¼í°ì ì´ë¦.
+ </para>
+ </callout>
+ <callout arearefs="id2">
+ <para>
+ <literal>type</literal> (ìµì
): Hibernate íì
ì ëíë´ë ì´ë¦.
+ </para>
+ </callout>
+ <callout arearefs="id3">
+ <para>
+ <literal>column</literal> (ìµì
- ëí´í¸ë íë¡í¼í° ì´ë¦): íë¼ì´ë¨¸ë¦¬ í¤ ì»¬ë¼ì ì´ë¦.
+ </para>
+ </callout>
+ <callout arearefs="id4">
+ <para>
+ <literal>unsaved-value</literal> (ìµì
- ëí´í¸ë "sensible" ê°):
+ ì´ì ì¸ì
ìì ì ì¥ëìê±°ë ë¡ëëìë detached(ë¶ë¦¬ë) ì¸ì¤í´ì¤ë¤ë¡ë¶í° ê·¸ê²ì 구ë¶ì§ì°ëë¡, ì¸ì¤í´ì¤ê° ìë¡ì´ ì´ê¸°íëì´ ìì(ì ì¥ëì´
+ ìì§ ìì)ì ëíë´ë ìë³ì íë¡í¼í° ê°.
+ </para>
+ </callout>
+ <callout arearefs="id5">
+ <para>
+ <literal>access</literal> (ìµì
- ëí´í¸ë <literal>property</literal>):
+ Hibernateê° íë¡í¼í° ê°ì ì¡ì¸ì¤íëë° ì¬ì©í ë°©ë.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ <literal>name</literal> ìì±ì´ ëë½ëë©´, í´ëì¤ë ìë³ì íë¡í¼í°ë¥¼ ê°ì§ ìëë¤ê³ ê°ì ëë¤.
+ </para>
+
+ <para>
+ <literal>unsaved-value</literal> ìì±ì Hibernate3ììë ê±°ì íìíì§ ìë¤.
+ </para>
+
+ <para>
+ composite í¤ë¤ë¡ì 리거ì ë°ì´í°ì ì¡ì¸ì¤íë ê²ì íì©í´ì£¼ë ëì²´ì ì¸ <literal><composite-id></literal>
+ ì ì¸ì´ ì¡´ì¬íë¤. ì°ë¦¬ë ê·¸ ë°ì ì´ë¤ê²ì ëí ê·¸ê²ì ì¬ì©ì ëí´ ê°ë ¥íê² ë°ëíë¤.
+ </para>
+
+ <sect3 id="mapping-declaration-id-generator" revision="2">
+ <title>Generator</title>
+
+ <para>
+ ì íì ì¸ <literal><generator></literal> ìì ììë ìì í´ëì¤ì ì¸ì¤í´ì¤ë¤ì ëí ì ì¼ ìë³ìë¤ì ìì±ìí¤ëë° ì¬ì©ëë
+ ìë° í´ëì¤ë¥¼ ëª
ëª
íë¤. ë§ì¼ ììì íë¼ë¯¸í°ë¤ì´ ìì±ê¸° ì¸ì¤í´ì¤ë¥¼ 구ì±íê±°ë ì´ê¸°í ìí¤ëë° íìí ê²½ì°, ê·¸ê²ë¤ì <literal><param></literal>
+ ìì 를 ì¬ì©íì¬ ì ë¬ëë¤.
+ </para>
+
+ <programlisting><![CDATA[<id name="id" type="long" column="cat_id">
+ <generator class="org.hibernate.id.TableHiLoGenerator">
+ <param name="table">uid_table</param>
+ <param name="column">next_hi_value_column</param>
+ </generator>
+</id>]]></programlisting>
+
+ <para>
+ 모ë ìì±ê¸°ë¤ì <literal>org.hibernate.id.IdentifierGenerator</literal> ì¸í°íì´ì¤ë¥¼ 구ííë¤. ì´ê²ì ë§¤ì° ê°ë¨í ì¸í°íì´ì¤ì´ë¤;
+ ëªëª ì´í리ì¼ì´ì
ë¤ì ê·¸ê²ë¤ ìì ì í¹íë 구íë¤ì ì ê³µíëë¡ ì íí ì ìë¤. íì§ë§ Hibernateë 미리 ë¹ëë 구íë¤ì ììë¤ì ì ê³µíë¤.
+ ë¹ë-ì¸ ìì±ê¸°(generator)ë¤ì ëí ë¨ì¶ ì´ë¦ë¤ì´ ì¡´ì¬íë¤:
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>increment</literal></term>
+ <listitem>
+ <para>
+ ëì¼í í
ì´ë¸ ìì¼ë¡ ë°ì´í°ë¥¼ ì
ë ¥íë ë¤ë¥¸ íë¡ì¸ì¤ê° ìì ëìë§ ì ì¼í <literal>long</literal>, <literal>short</literal> ëë <literal>int</literal>
+ íì
ì ìë³ìë¤ì ìì±ìí¨ë¤. <emphasis>í´ë¬ì¤í° ë´ììë ì¬ì©íì§ ë§ë¼.</emphasis>
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>identity</literal></term>
+ <listitem>
+ <para>
+ DB2, MySQL, MS SQL Server, Sybase, HypersonicSQLìì ìë³ ì»¬ë¼ë¤ì ì§ìíë¤. ë°íëë ìë³ìë <literal>long</literal>,
+ <literal>short</literal> ëë <literal>int</literal> íì
ì´ë¤.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>sequence</literal></term>
+ <listitem>
+ <para>
+ DB2, PostgreSQL, Oracle, SAP DB, McKoiìì ìíì¤ë¥¼ ì¬ì©íê±°ë Interbaseìì ìì±ê¸°(generator)를 ì¬ì©íë¤. ë°íëë ìë³ìë
+ <literal>long</literal>, <literal>short</literal> ëë <literal>int</literal> íì
ì´ë¤.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>hilo</literal></term>
+ <listitem>
+ <para id="mapping-declaration-id-hilodescription" revision="1">
+ í
ì´ë¸ê³¼ 컬ë¼(ëí´í¸ë¡ ê°ê° <literal>hibernate_unique_key</literal>ì <literal>next_hi</literal>)ì´ hi ê°ë¤ì
+ ìì¤ë¡ì 주ì´ì§ë©´, <literal>long</literal>, <literal>short</literal> ëë <literal>int</literal> íì
ì
+ ìë³ìë¤ì í¨ê³¼ì ì¼ë¡ ìì±ìí¤ëë° hi/lo ìê³ ë¦¬ì¦ì ì¬ì©íë¤. hi/lo ìê³ ë¦¬ì¦ì í¹ì ë°ì´í°ë² ì´ì¤ì ëí´ìë§ ì ì¼í ìë³ìë¤ì
+ ìì±ìí¨ë¤.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>seqhilo</literal></term>
+ <listitem>
+ <para>
+ ëª
ëª
ë ë°ì´í°ë² ì´ì¤ ìíì¤ê° 주ì´ì§ë©´, <literal>long</literal>, <literal>short</literal> ëë <literal>int</literal>
+ íì
ì ìë³ìë¤ì í¨ê³¼ì ì¼ë¡ ìì±ìí¤ëë° hi/lo ìê³ ë¦¬ì¦ì ì¬ì©íë¤.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>uuid</literal></term>
+ <listitem>
+ <para>
+ ë¤í¸ì ë´ìì ì ì¼í(IP 주ìê° ì¬ì©ëë¤) string íì
ì ìë³ìë¤ì ìì±ìí¤ê¸° ìí´ 128 ë¹í¸ UUID ìê³ ë¦¬ì¦ì ì¬ì©íë¤.
+ UUIDë 길ì´ê° 32ì¸ 16ì§ìë¤ì 문ìì´ë¡ì ì¸ì½ë© ëë¤.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>guid</literal></term>
+ <listitem>
+ <para>
+ MS SQL Serverì MySQL ììì ë°ì´í°ë² ì´ì¤ ìì± GUID 문ìì´ì ì¬ì©íë¤.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>native</literal></term>
+ <listitem>
+ <para>
+ 기본 ë°ì´í°ë² ì´ì¤ì ê°ì©ì±ë¤ì ìì¡´íì¬ <literal>identity</literal>, <literal>sequence</literal>
+ ëë <literal>hilo</literal>를 ì°¾ìë¸ë¤.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>assigned</literal></term>
+ <listitem>
+ <para>
+ ì´í리ì¼ì´ì
ì¼ë¡ íì¬ê¸ <literal>save()</literal>ê° í¸ì¶ë기 ì ì ìë³ì를 ê°ì²´ì í ë¹íëë¡ íë¤.
+ <literal><generator></literal> ììê° ì§ì ëì§ ìì ê²½ì° ì´ê²ì´ ëí´í¸ ë°©ëì´ë¤.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>select</literal></term>
+ <listitem>
+ <para>
+ ì´ë¤ ì ì¼ í¤ì ìí´ íì selectíê³ íë¼ì´ë¨¸ë¦¬ í¤ ê°ì ê²ìí¨ì¼ë¡ì¨ ë°ì´í°ë² ì´ì¤ í¸ë¦¬ê±°ì ìí´ í ë¹ë
+ íë¼ì´ë¨¸ë¦¬ í¤ë¥¼ ê²ìíë¤.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>foreign</literal></term>
+ <listitem>
+ <para>
+ ë ë¤ë¥¸ ì°ê´ë ê°ì²´ì ìë³ì를 ì¬ì©íë¤. ëê° <literal><one-to-one></literal> íë¼ì´ë¨¸ë¦¬ í¤
+ ì°ê´ê´ê³ì í¨ê» ì¬ì©ëë¤.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>sequence-identity</literal></term>
+ <listitem>
+ <para>
+ ì¤ì ê° ìì±ì ìí´ ë°ì´í°ë² ì´ì¤ ìíì¤ë¥¼ íì©íì§ë§, ìì±ë ìë³ì ê°ì insert ë¬¸ì¥ ì¤íì ë¶ë¶ì¼ë¡ì
+ ì¤ì ë¡ ë°íìí¤ê¸° ìí´ ì´ê²ì JDBC3 getGeneratedKeysì ê²°í©ìí¨ í¹íë ìíì¤ ìì± ë°©ë. ì´ ë°©ëë
+ JDK 1.4ì ëìíë Oracle 10g ëë¼ì´ë²ë¤ ìììë§ ì§ìëë ê±°ì¹ë¡ ìë ¤ì ¸ ìë¤. ì´ë¤ insert 문ì¥ë¤ì
+ ëí 주ìë¤ì Oracle ëë¼ì´ë²ë¤ ë´ì ìë ë²ê·¸ ë문ì ì¬ì©ë¶ê°ë¥íê² ëì´ ììì ë
¸í¸íë¼.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ </para>
+ </sect3>
+
+ <sect3 id="mapping-declaration-id-hilo" revision="1">
+ <title>Hi/lo algorithm</title>
+ <para>
+ <literal>hilo</literal>ì <literal>seqhilo</literal> ìì±ê¸°ë¤ì ìë³ì ìì±ì ëí ë§ìì ëë ì ê·¼ë²ì¸,
+ hi/lo ìê³ ë¦¬ì¦ì ëí ë ê°ì ëì²´ 구íë¤ì ì ê³µíë¤. 첫 ë²ì§¸ 구íì ë¤ìì ì´ì© ê°ë¥í "hi" ê°ì ìì©í기 ìí "í¹ë³í"
+ ë°ì´í°ë² ì´ì¤ í
ì´ë¸ì íìë¡ íë¤. ë ë²ì§¸ë (ì§ìëë) Oracle ì¤íì¼ì ìíì¤ë¥¼ ì¬ì©íë¤.
+ </para>
+
+ <programlisting><![CDATA[<id name="id" type="long" column="cat_id">
+ <generator class="hilo">
+ <param name="table">hi_value</param>
+ <param name="column">next_value</param>
+ <param name="max_lo">100</param>
+ </generator>
+</id>]]></programlisting>
+
+ <programlisting><![CDATA[<id name="id" type="long" column="cat_id">
+ <generator class="seqhilo">
+ <param name="sequence">hi_value</param>
+ <param name="max_lo">100</param>
+ </generator>
+</id>]]></programlisting>
+
+ <para>
+ ë¶ííë ë¹ì ì Hibernateì ë¹ì ìì ì <literal>Connection</literal>ì ì ê³µí ë <literal>hilo</literal>를
+ ì¬ì©í ì ìë¤. Hibernateê° JTAì ëìì ë°ë 커ë¥ì
ë¤ì ì»ê¸° ìí´ ì´í리ì¼ì´ì
ìë² ë°ì´í°ìì¤ë¥¼ ì¬ì©í ë ë¹ì ì
+ <literal>hibernate.transaction.manager_lookup_class</literal>를 ì ì íê² êµ¬ì±í´ì¼ íë¤.
+ </para>
+ </sect3>
+
+ <sect3 id="mapping-declaration-id-uuid">
+ <title>UUID ìê³ ë¦¬ì¦</title>
+ <para>
+ UUID ë ë¤ìì í¬í¨íë¤: IP 주ì, JVMì ìì ìê°(ì íí 1/4 ì´), ìì¤í
ìê°ê³¼ (JVM ë´ìì ì ì¼í) counter ê°.
+ Java ì½ëë¡ë¶í° MAC 주ì ëë ë©ëª¨ë¦¬ 주ì를 ì»ë ê²ì ë¶ê°ë¥íì¬ì, ì´ê²ì ì°ë¦¬ê° JNI를 ì¬ì©íì§ ìê³ ì íí ì ìë
+ ìµìì ê²ì´ë¤.
+ </para>
+ </sect3>
+
+ <sect3 id="mapping-declaration-id-sequences">
+ <title>ìë³ ì»¬ë¼ë¤ê³¼ ìíì¤ë¤</title>
+ <para>
+ ìë³ ì»¬ë¼ë¤ì ì§ìíë ë°ì´í°ë² ì´ì¤ë¤(DB2, MySQL, Sybase, MS SQL)ì ê²½ì°, ë¹ì ì <literal>identity</literal> í¤
+ ìì±ì ì¬ì©í ì ìë¤. ìíì¤ë¤ì ì§ìíë ë°ì´í°ë² ì´ì¤ë¤(DB2, Oracle, PostgreSQL, Interbase, McKoi, SAP DB)ì
+ ê²½ì°, ë¹ì ì <literal>sequence</literal> ì¤íì¼ í¤ ìì±ì ì¬ì©í ìë ìë¤. ì´ë¤ ë°©ëë¤ ëª¨ë ìë¡ì´ ê°ì²´ë¥¼ insertí기
+ ìí´ ë ê°ì SQL ì§ìë¤ì íìë¡ íë¤.
+ </para>
+
+ <programlisting><![CDATA[<id name="id" type="long" column="person_id">
+ <generator class="sequence">
+ <param name="sequence">person_id_sequence</param>
+ </generator>
+</id>]]></programlisting>
+
+ <programlisting><![CDATA[<id name="id" type="long" column="person_id" unsaved-value="0">
+ <generator class="identity"/>
+</id>]]></programlisting>
+
+ <para>
+ í¬ë¡ì¤ íë«í¼ ê°ë°ì ìí´ì, <literal>native</literal> ë°©ëê° ê¸°ì¤ ë°ì´í°ë² ì´ì¤ë¤ì ê°ì©ì±ë¤ì ë°ë¼ <literal>identity</literal>,
+ <literal>sequence</literal>, <literal>hilo</literal> ë°©ë ì¤ìì ì íë ê²ì´ë¤.
+ </para>
+ </sect3>
+
+ <sect3 id="mapping-declaration-id-assigned">
+ <title>í ë¹ë ìë³ìë¤</title>
+ <para>
+ (Hibernateë¡ íì¬ê¸ ìë³ìë¤ì ìì±ìí¤ëë¡ íë ê²ê³¼ë ë°ëë¡) ë¹ì ì´ ì´í리ì¼ì´ì
ì¼ë¡ íì¬ê¸ ìë³ìë¤ì í ë¹íëë¡ ìí ê²½ì°,
+ ë¹ì ì <literal>assigned</literal> ìì±ê¸°ë¥¼ ì¬ì©í ì ìë¤. ì´ í¹ë³í ìì±ê¸°ë ê°ì²´ì identifier íë¡í¼í°ì ì´ë¯¸ í ë¹ë
+ ìë³ì ê°ì ì¬ì©í ê²ì´ë¤. ì´ ìì±ê¸°(generator)ë íë¼ì´ë¨¸ë¦¬ í¤ê° ëì©(surrogate ) í¤ ëì ì natural í¤ì¼ ë ì¬ì©ëë¤.
+ ë¹ì ì´ <literal><generator></literal> ìì를 ì§ì íì§ ìì ê²½ì°ì ì´ê²ì´ ëí´í¸ í¹ì§ì´ë¤
+ </para>
+
+ <para>
+ <literal>assigned</literal> ìì±ê¸°(generator)를 ì ííë ê²ì , version ëë timestamp íë¡í¼í°ê° ì¡´ì¬íì§ ìë í
+ ëë ë¹ì ì´ <literal>Interceptor.isUnsaved()</literal>를 ì ìíì§ ìë í, íëì ì¸ì¤í´ì¤ê° transient ëë detachedì¸ì§ë¥¼
+ ê²°ì í기 ìí´ Hibernaeë¡ íì¬ê¸ ë°ì´í°ë² ì´ì¤ì ì ì´íëë¡ ê°ì íë, <literal>unsaved-value="undefined"</literal>를
+ Hibernateìê² ì¬ì©íëë¡ íë¤.
+ </para>
+ </sect3>
+
+ <sect3 id="mapping-declaration-id-select">
+ <title>í¸ë¦¬ê±°ë¤ì ìí´ í ë¹ë íë¼ì´ë¨¸ë¦¬ í¤ë¤</title>
+ <para>
+ 리거ì ì¤í¤ë§ì ëí´ìë§(Hibernateë í¸ë¦¬ê±°ë¤ì ê°ì§ DDLì ìì±ìí¤ì§ ìëë¤).
+ </para>
+
+ <programlisting><![CDATA[<id name="id" type="long" column="person_id">
+ <generator class="select">
+ <param name="key">socialSecurityNumber</param>
+ </generator>
+</id>]]></programlisting>
+
+ <para>
+ ìì ìì ìì, natural í¤ë¡ì í´ëì¤ì ìí´ <literal>socialSecurityNumber</literal>ë¡ ëª
ëª
ë ì ì¼ ê°ì ê°ì§ íë¡í¼í°ê° ì¡´ì¬íê³ ,
+ í¸ë¦¬ê±°ì ìí´ ê·¸ ê°ì´ ìì±ëë <literal>person_id</literal>ë¡ ëª
ëª
ë ëì©í¤ê° ì¡´ì¬íë¤.
+ </para>
+
+ </sect3>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-compositeid" revision="3">
+ <title>composite-id</title>
+
+ <programlisting><![CDATA[<composite-id
+ name="propertyName"
+ class="ClassName"
+ mapped="true|false"
+ access="field|property|ClassName">
+ node="element-name|."
+
+ <key-property name="propertyName" type="typename" column="column_name"/>
+ <key-many-to-one name="propertyName class="ClassName" column="column_name"/>
+ ......
+</composite-id>]]></programlisting>
+
+ <para>
+ composite í¤ë¥¼ ê°ì§ í
ì´ë¸ì ê²½ì°, ë¹ì ì í´ëì¤ì ì¬ë¬ íë¡í¼í°ë¤ì ìë³ì íë¡í¼í°ë¤ë¡ì 매íí ì ìë¤.
+ <literal><composite-id></literal> ììë ìì ììë¤ë¡ì <literal><key-property></literal>
+ íë¡í¼í° 매íê³¼ <literal><key-many-to-one></literal> 매íë¤ì íì©íë¤.
+ </para>
+
+ <programlisting><![CDATA[<composite-id>
+ <key-property name="medicareNumber"/>
+ <key-property name="dependent"/>
+</composite-id>]]></programlisting>
+
+ <para>
+ ë¹ì ì ìì í´ëì¤ë composite ìë³ì ëë±ì±ì 구íí기 ìí´ì <literal>equals()</literal>ì
+ <literal>hashCode()</literal>를 ì¤ë²ë¼ì´ë <emphasis>ìì¼ì¼ íë¤</emphasis>. ê·¸ê²ì ëí
+ <literal>Serializable</literal>ì 구íí´ì¼ íë¤.
+ </para>
+
+ <para>
+ ë¶ííë, composite ìë³ìë¤ì ëí ì´ ì ê·¼ë²ì ìì ê°ì²´ê° ê·¸ê² ìì ì ìë³ìë¼ë ì ì ì미íë¤. ê°ì²´
+ ìì ì¸ì ë¤ë¥¸ "í¸ë¤"ì´ ì¡´ì¬íì§ ìëë¤. ë¹ì ì ë¹ì ì´ composite keyë¡ ì°ê´ë ìì ìí를 <literal>load()</literal>
+ í ì ì기 ì´ì ì ìì í´ëì¤ ê·¸ ìì²´ì ì¸ì¤í´ì¤ë¥¼ ì´ê¸°í íê³ ê·¸ê²ì ìë³ì íë¡í¼í°ë¤ì êµ°ì§í ìì¼ì¼ íë¤. ì°ë¦¬ë
+ ì´ ì ê·¼ë²ì <emphasis>embedded</emphasis> composite ìë³ìë¡ ë¶ë¥´ê³ , ì¤ëí ì´í리ì¼ì´ì
ë¤ì ëí´ ê·¸ê²ì ìµì ìí¨ë¤.
+ </para>
+
+ <para>
+ ë ë²ì§¸ ì ê·¼ë²ì ì°ë¦¬ê° <emphasis>mapped</emphasis> composite ìë³ìë¼ê³ ë¶ë¥´ë ê²ì¸ë°, ì¬ê¸°ì
+ <literal><composite-id></literal> ìì ë´ì ëª
ëª
ë ì¬ê¸°ì ìë³ì íë¡í¼í°ë¤ì ìì í´ëì¤ì ë³ëì ìë³ì í´ëì¤
+ ìì ìì ì¤ë³µëë¤.
+ </para>
+
+ <programlisting><![CDATA[<composite-id class="MedicareId" mapped="true">
+ <key-property name="medicareNumber"/>
+ <key-property name="dependent"/>
+</composite-id>]]></programlisting>
+
+ <para>
+ ì´ ìì ìì, composite ìë³ì í´ëì¤ì¸ <literal>MedicareId</literal>ì ìí°í° í¬ëì¤ ê·¸ ìì²´ ììë
+ <literal>medicareNumber</literal>ì <literal>dependent</literal>ë¡ ëª
ëª
ë íë¡í¼í°ë¤ì ê°ëë¤. ìë³ì í´ëì¤ë
+ <literal>equals()</literal>ì <literal>hashCode()</literal>를 ì¤ë²ë¼ì´ë ìì¼ê³ <literal>Serializable</literal>ì
+ 구íí´ì¼ íë¤. ì´ ì ê·¼ë²ì ë¨ì ì ì주 ëª
ë°±í—ì½ë ì¤ë³µì´ë¤.
+ </para>
+
+ <para>
+ ë¤ì ìì±ë¤ì 매íë composite ìë³ì를 ì§ì íëë° ì¬ì©ëë¤:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>mapped</literal> (ìµì
, ëí´í¸ë <literal>false</literal>):
+ íëì 매íë composite ìë³ìê° ì¬ì©ë¨ì, ê·¸ë¦¬ê³ í¬í¨ë íë¡í¼í° 매íë¤ì´ ìí°í° í´ëì¤ì
+ composite ìë³ì í´ëì¤ ìì를 참조í¨ì ëíë¸ë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>class</literal> (ìµì
, íì§ë§ íëì 매íë commposite ìë³ìì ëí´ìë íìì ì):
+ íëì composite ìë³ìë¡ì ì¬ì©ëë í´ëì¤.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ì°ë¦¬ë <xref linkend="components-compositeid"/>ìì composite ìë³ìê° íëì component í´ëì¤ë¡ì 구íëë
+ ë³´ë¤ í¸ë¦¬í ì ê·¼ë²ì¸ ì¸ë²ì§¸ ë°©ë를 ì¤ëª
í ê²ì´ë¤. ìëì ì¤ëª
ëì´ ìë ìì±ë¤ì ì´ ëì²´ì ì¸ ì ê·¼ë²ìë§ ì ì©ëë¤:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>name</literal> (ìµì
, ì´ ì ê·¼ë²ì ê²½ì°ìë íìì): íëì component
+ ìë³ì를 ìì íë ì»´í¬ëí¸ íì
ì íë¡í¼í°(9ì¥ì ë³´ë¼).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>access</literal> (ìµì
- ëí´í¸ë <literal>property</literal>):
+ Hibernateê° íë¡í¼í° ê°ì ì ê·¼íëë° ì¬ì©í ë°©ë.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>class</literal> (ìµì
- ëí´í¸ë reflectionì ìí´ ê²°ì ë íë¡í¼í° íì
):
+ íëì composite ìë³ìë¡ì ì¬ì©ëë ì»´í¬ëí¸ í´ëì¤(ë¤ì ì ì ë³´ë¼).
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ì´ ì¸ë²ì§¸ ì ê·¼ë², <emphasis>identifier component</emphasis>ì ê±°ì 모ë ì´í리ì¼ì´ì
ë¤ì ëí´ ì°ë¦¬ê° ê¶ì¥íë ê²ì´ë¤.
+ </para>
+ </sect2>
+
+ <sect2 id="mapping-declaration-discriminator" revision="3">
+ <title>discriminator</title>
+
+ <para>
+ <literal><discriminator></literal> ììë table-per-class-hierarchy(í
ì´ë¸ ë¹ í´ëì¤ ê³ì¸µêµ¬ì¡°)
+ 매í ë°©ë를 ì¬ì©íë ë¤íì± ììíì íìíê³ í
ì´ë¸ì discriminator(íë³ì) 컬ë¼ì ì ì¸íë¤. discriminator 컬ë¼ì
+ í¹ì íì ëí´ ì´ê¸°í ìí¬ ìë¸ í´ëì¤ê° 무ìì¸ì§ë¥¼ ìì ê³ì¸µì ìë ¤ì£¼ë íìì ê°ë¤ì í¬í¨íë¤. íì
ë¤ì ì íì ì¸ ì§í©ì´
+ ì¬ì©ë ì ìë¤: <literal>string</literal>, <literal>character</literal>, <literal>integer</literal>,
+ <literal>byte</literal>, <literal>short</literal>, <literal>boolean</literal>,
+ <literal>yes_no</literal>, <literal>true_false</literal>.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="discriminator1" coords="2 60"/>
+ <area id="discriminator2" coords="3 60" />
+ <area id="discriminator3" coords="4 60" />
+ <area id="discriminator4" coords="5 60" />
+ <area id="discriminator5" coords="6 60" />
+ </areaspec>
+ <programlisting><![CDATA[<discriminator
+ column="discriminator_column"
+ type="discriminator_type"
+ force="true|false"
+ insert="true|false"
+ formula="arbitrary sql expression"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="discriminator1">
+ <para>
+ <literal>column</literal> (ìµì
- ëí´í¸ë <literal>class</literal>)
+ discriminator 컬ë¼ëª
.
+ </para>
+ </callout>
+ <callout arearefs="discriminator2">
+ <para>
+ <literal>type</literal> (ìµì
- ëí´í¸ë <literal>string</literal>)
+ Hibernate íì
ì ëíë´ë ì´ë¦
+ </para>
+ </callout>
+ <callout arearefs="discriminator3">
+ <para>
+ <literal>force</literal> (ìµì
- ëí´í¸ë <literal>false</literal>)
+ ì´ê²ì Hibernateë¡ íì¬ê¸ ë£¨í¸ í´ëì¤ì 모ë ì¸ì¤í´ì¤ë¤ì ê²ìí ëì¡°ì°¨ë íì©ë discriminator
+ ê°ë¤ì ì§ì íëë¡ "ê°ì íë¤".
+ </para>
+ </callout>
+ <callout arearefs="discriminator4">
+ <para>
+ <literal>insert</literal> (ìµì
- ëí´í¸ë <literal>true</literal>)
+ ë¹ì ì discriminator 컬ë¼ì´ ëí 매íë composite ìë³ìì ë¶ë¶ì¼ ê²½ì°ì ì´ê²ì <literal>false</literal>ë¡ ì¤ì íë¼.
+ (Hibernateìê² SQL <literal>INSERT</literal>ë¤ ìì ê·¸ 컬ë¼ì í¬í¨íì§ ìëë¡ íµë³´íë¤.)
+ </para>
+ </callout>
+ <callout arearefs="discriminator5">
+ <para>
+ <literal>formula</literal> (ìµì
)
+ íì
ì´ íê° ëì´ì¼ í ë ì¤íëë ììì SQL ííì. 컨í
ì¸ ê¸°ë°ì íë³ì íì©í´ì¤ë¤.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ discriminator 컬ë¼ì ì¤ì ê°ë¤ì <literal><class></literal> ììì
+ <literal><subclass></literal> ììì <literal>discriminator-value</literal> ìì±ì ìí´ ì§ì ëë¤.
+ </para>
+
+ <para>
+ <literal>force</literal> ìì±ì í
ì´ë¸ì´ ìì í´ëì¤ë¡ 매íëì§ ìë "í¹ë³í" discriminator ê°ë¤ì ê°ì§ íë¤ì
+ í¬í¨í ê²½ì°ì(ë§) ì ì©íë¤. ì´ê²ì ëê° ê·¸ ê²½ì°ê° ìë ê²ì´ë¤.
+ </para>
+
+ <para>
+ <literal>formula</literal> ìì±ì ì¬ì©íì¬ ë¹ì ì íì íì
ì íë¨íëë° ì¬ì©ë ììì SQL ííìì ì ì¸í ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[<discriminator
+ formula="case when CLASS_TYPE in ('a', 'b', 'c') then 0 else 1 end"
+ type="integer"/>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-version" revision="4">
+ <title>version (ìµì
)</title>
+
+ <para>
+ <literal><version></literal> ììë ìµì
ì´ê³ í
ì´ë¸ì´ ë²ì íë ë°ì´í°ë¥¼ í¬í¨íë¤ë ê²ì ëíë¸ë¤.
+ ì´ê²ì ë¹ì ì´ <emphasis>긴 í¸ëìì
(long transaction)ë¤</emphasis>ì ì¬ì©í ê³íì´ë¼ë©´ í¹í ì ì©íë¤
+ (ìë를 ë³´ë¼).
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="version1" coords="2 70"/>
+ <area id="version2" coords="3 70"/>
+ <area id="version3" coords="4 70"/>
+ <area id="version4" coords="5 70"/>
+ <area id="version5" coords="6 70"/>
+ <area id="version6" coords="7 70"/>
+ <area id="version7" coords="8 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<version
+ column="version_column"
+ name="propertyName"
+ type="typename"
+ access="field|property|ClassName"
+ unsaved-value="null|negative|undefined"
+ generated="never|always"
+ insert="true|false"
+ node="element-name|@attribute-name|element/@attribute|."
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="version1">
+ <para>
+ <literal>column</literal> (ìµì
- ëí´í¸ë íë¡í¼í° ëª
):
+ ë²ì ë²í¸ë¥¼ ê°ì§ 컬ë¼ì ì´ë¦.
+ </para>
+ </callout>
+ <callout arearefs="version2">
+ <para>
+ <literal>name</literal>: ìì í´ëì¤ì íë¡í¼í° ëª
.
+ </para>
+ </callout>
+ <callout arearefs="version3">
+ <para>
+ <literal>type</literal> (ìµì
- ëí´í¸ë <literal>integer</literal>):
+ ë²ì ë²í¸ì íì
.
+ </para>
+ </callout>
+ <callout arearefs="version4">
+ <para>
+ <literal>access</literal> (ìµì
- ëí´í¸ë <literal>property</literal>):
+ Hibernateê° íë¡í¼í° ê°ì ì¡ì¸ì¤íëë° ì¬ì©í ë°©ë.
+ </para>
+ </callout>
+ <callout arearefs="version5">
+ <para>
+ <literal>unsaved-value</literal> (ìµì
- ëí´í¸ë <literal>undefined</literal>):
+ ì´ì ì¸ì
ìì ì ì¥ëìê±°ë ë¡ëëìë detached ì¸ì¤í´ì¤ë¡ë¶í° 구ë³ì§ì´ì, ì¸ì¤í´ì¤ê° ìë¡ì´ ì´ê¸°íë¨(unsaved)ì
+ ëíë´ë version íë¡í¼í° ê°.(<literal>undefined</literal>ë ìë³ì íë¡í¼í° ê°ì´ ì¬ì©ë ê²ìì ì§ì íë¤.)
+ </para>
+ </callout>
+ <callout arearefs="version6">
+ <para>
+ <literal>generated</literal> (ìµì
- ëí´í¸ë <literal>never</literal>):
+ ì´ version íë¡í¼í° ê°ì´ ë°ì´í°ë² ì´ì¤ì ìí´ ì¤ì ë¡ ì°ì¶ëëì§ë¥¼ ì§ì íë¤.
+ <xref linkend="mapping-generated">ì°ì¶ëë íë¡í¼í°ë¤</xref>ì ê´í ë
¼ì를 ë³´ë¼.
+ </para>
+ </callout>
+ <callout arearefs="version7">
+ <para>
+ <literal>insert</literal> (ìµì
- ëí´í¸ë <literal>true</literal>):
+ version 컬ë¼ì´ SQL insert 문ì¥ë¤ ìì í¬í¨ë ê²ì¸ì§ ì¬ë¶ë¥¼ ì§ì íë¤.
+ ë°ì´í°ë² ì´ì¤ 컬ë¼ì´ ëí´í¸ ê° <literal>0</literal>ì¼ë¡ ì ìëë ê²½ì°ìë§ <literal>false</literal>ë¡
+ ì¤ì ë ì ìë¤.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ ë²ì ë²í¸ë¤ì <literal>long</literal>, <literal>integer</literal>, <literal>short</literal>,
+ <literal>timestamp</literal> ëë <literal>calendar</literal> íì
ì¼ ì ìë¤.
+ </para>
+
+ <para>
+ version ëë timestamp íë¡í¼í°ë detached ì¸ì¤í´ì¤ì ëí´ ê²°ì½ nullì¼ ìê° ìì´ì, Hibernateë
+ ë¤ë¥¸ <literal>unsaved-value</literal> ë°©ëë¤ì´ ì§ì ëë ê²ì ìê´ìì´, null versionì´ë timestamp를
+ ê°ì§ ììì ì¸ì¤í´ì¤ë¥¼ transientë¡ì ê²ì¶í ê²ì´ë¤. <emphasis>null íì©ëë version ì´ë property를
+ ì ì¸íë ê²ì Hibernateìì transitive reattachmentì ëí ììì 문ì ë¤ì í¼íë ì¬ì´ ë°©ë²ì´ê³ ,
+ assigned ìë³ìë¤ì´ë composite keyë¤ì ì¬ì©íë ì¬ëë¤ìê² í¹í ì ì©íë¤!</emphasis>
+ </para>
+ </sect2>
+
+ <sect2 id="mapping-declaration-timestamp" revision="4" >
+ <title>timestamp (ìµì
)</title>
+
+ <para>
+ ìµì
<literal><timestamp></literal> ììë í
ì´ë¸ì´ íìì¤í¬íí ë ë°ì´í°ë¥¼ í¬í¨í¨ì ëíë¸ë¤. ì´ê²ì
+ ë²ì íì ëí ë체물ë¡ì ê³ ìëìë¤. Timestampì ê³ ì íê² optimistic ì ê¸ì ëí ë¤ì ìì í 구íì´ë¤. íì§ë§ ëëë¡
+ ì´í리ì¼ì´ì
ì ë¤ë¥¸ ë°©ë²ë¤ë¡ timestampë¤ì ì¬ì©í ìë ìë¤.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="timestamp1" coords="2 70"/>
+ <area id="timestamp2" coords="3 70" />
+ <area id="timestamp3" coords="4 70" />
+ <area id="timestamp4" coords="5 70" />
+ <area id="timestamp5" coords="6 70" />
+ <area id="timestamp6" coords="7 70" />
+ </areaspec>
+ <programlisting><![CDATA[<timestamp
+ column="timestamp_column"
+ name="propertyName"
+ access="field|property|ClassName"
+ unsaved-value="null|undefined"
+ source="vm|db"
+ generated="never|always"
+ node="element-name|@attribute-name|element/@attribute|."
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="timestamp1">
+ <para>
+ <literal>column</literal> (ìµì
- ëí´í¸ë íë¡í¼í° ëª
):
+ íìì¤í¬í를 í¬í¨íë ì»¬ë¼ ëª
.
+ </para>
+ </callout>
+ <callout arearefs="timestamp2">
+ <para>
+ <literal>name</literal>:
+ ìì í´ëì¤ì ëí´ ìë° <literal>Date</literal> ëë <literal>Timestamp</literal> íì
ì
+ ê°ì§ ìë°ë¹ì¦ ì¤íì¼ì íë¡í¼í° ì´ë¦.
+ </para>
+ </callout>
+ <callout arearefs="timestamp3">
+ <para>
+ <literal>access</literal> (ìµì
- ëí´í¸ë <literal>property</literal>):
+ Hibernateê° íë¡í¼í° ê°ì ì ê·¼íëë° ì¬ì©í ë°©ë.
+ </para>
+ </callout>
+ <callout arearefs="timestamp4">
+ <para>
+ <literal>unsaved-value</literal> (ìµì
- ëí´í¸ë <literal>null</literal>):
+ ì´ì ì¸ì
ìì ì ì¥ëìê±°ë ë¡ëëìë detached ì¸ì¤í´ì¤ë¡ë¶í° ì¸ì¤í´ì¤ë¥¼ 구ë³ì§ì°ë, ì¸ì¤í´ì¤ê° ìë¡ì´
+ ì´ê¸°íë¨(unsaved)ì ëíë´ë version íë¡í¼í° ê°.(<literal>undefined</literal>ë ìë³ì
+ íë¡í¼í° ê°ì´ ì¬ì©ë ê²ìì ì§ì íë¤.)
+ </para>
+ </callout>
+ <callout arearefs="timestamp5">
+ <para>
+ <literal>source</literal> (ìµì
- ëí´í¸ë <literal>vm</literal>):
+ Hibernateë ì´ëì timestamp ê°ì ê²ìí ê²ì¸ê°? ë°ì´í°ë² ì´ì¤ë¡ë¶í°ì¸ê° íì¬ì JVMì¼ë¡ë¶í°ì¸ê°?
+ ë°ì´í°ë² ì´ì¤ 기ë°ì timestampë¤ì Hibernateê° "ë¤ì ê°"ì ê²°ì í기 ìí´ ë°ì´í°ë² ì´ì¤ì ì ìí´ì¼
+ í기 ë문ì ì¤ë²í¤ë를 ì´ëíì§ë§, í´ë¬ì¤í°ë§ë íê²½ë¤ììì ì©ëë¡ ë³´ë¤ ë ìì í ê²ì´ë¤. ëí 모ë
+ <literal>Dialect</literal>ë¤ì´ ë°ì´í°ë² ì´ì¤ì íì¬ì timestampì ëí ê²ìì ì§ìíë ê²ì¼ë¡ ìë ¤ì ¸
+ ìì§ ìì§ë§, ë¤ë¥¸ <literal>Dialect</literal>ë¤ì ì ë°ë ê²°í ë문ì ì ê¸ì ìì´ ì¬ì©ì´ ìì íì§ ìì
+ ì ììì ë
¸í¸íë¼(ì를 ë¤ë©´ ì¤ë¼í´ 8).
+ </para>
+ </callout>
+ <callout arearefs="timestamp6">
+ <para>
+ <literal>generated</literal> (ìµì
- ëí´í¸ë <literal>never</literal>):
+ ì´ timestamp íë¡í¼í° ê°ì´ ë°ì´í°ë² ì´ì¤ì ìí´ ì¤ì ë¡ ìì±ë¨ì ì§ì íë¤.
+ <xref linkend="mapping-generated">ì°ì¶ëë íë¡í¼í°ë¤</xref>ì ëí ë
¼ìë¤ ë³´ë¼.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ <literal><timestamp></literal>ë <literal><version type="timestamp"></literal>ê³¼
+ ê°ìì ë
¸í¸íë¼. ê·¸ë¦¬ê³ <literal><timestamp use-db="true"></literal>ë
+ <literal><version type="dbtimestamp"></literal>ê³¼ ê°ë¤
+ </para>
+ </sect2>
+
+
+ <sect2 id="mapping-declaration-property" revision="4">
+ <title>íë¡í¼í°</title>
+
+ <para>
+ <literal><property></literal> ììë í´ëì¤ì ìë°ë¹ì¦ ì¤íì¼ì ìì íë¡í¼í°ë¥¼ ì ì¸íë¤.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="property1" coords="2 70"/>
+ <area id="property2" coords="3 70"/>
+ <area id="property3" coords="4 70"/>
+ <areaset id="property4-5" coords="">
+ <area id="property4" coords='5 70'/>
+ <area id="property5" coords='6 70'/>
+ </areaset>
+ <area id="property6" coords="7 70"/>
+ <area id="property7" coords="8 70"/>
+ <area id="property8" coords="9 70"/>
+ <area id="property9" coords="10 70"/>
+ <area id="property10" coords="11 70"/>
+ <area id="property11" coords="12 70"/>
+ <area id="property12" coords="13 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<property
+ name="propertyName"
+ column="column_name"
+ type="typename"
+ update="true|false"
+ insert="true|false"
+ formula="arbitrary SQL expression"
+ access="field|property|ClassName"
+ lazy="true|false"
+ unique="true|false"
+ not-null="true|false"
+ optimistic-lock="true|false"
+ generated="never|insert|always"
+ node="element-name|@attribute-name|element/@attribute|."
+ index="index_name"
+ unique_key="unique_key_id"
+ length="L"
+ precision="P"
+ scale="S"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="property1">
+ <para>
+ <literal>name</literal>: 첫 ì문ìë¡ ììíë íë¡í¼í° ì´ë¦.
+ </para>
+ </callout>
+ <callout arearefs="property2">
+ <para>
+ <literal>column</literal> (ìµì
- ëí´í¸ë íë¡í¼í° ì´ë¦):
+ 매íë ë°ì´í°ë² ì´ì¤ í
ì´ë¸ 컬ë¼ì ì´ë¦. ì´ê²ì ëí ë´ë¶ì í¬í¨ëë <literal><column></literal> ìì(ë¤)ì
+ ìí´ ì§ì ë ìë ìë¤.
+ </para>
+ </callout>
+ <callout arearefs="property3">
+ <para>
+ <literal>type</literal> (ìµì
): Hibernate íì
ì ëíë´ë ì´ë¦.
+ </para>
+ </callout>
+ <callout arearefs="property4-5">
+ <para>
+ <literal>update, insert</literal> (ìµì
- ëí´í¸ë <literal>true</literal>) :
+ 매íë 컬ë¼ë¤ì´ <literal>UPDATE</literal>ì/ëë <literal>INSERT</literal> 문ì¥ë¤ìì í¬í¨ë ê²ìì ì§ì íë¤.
+ ëë¤ <literal>false</literal>ë¡ ì¤ì íë ê²ì ê·¸ ê°ì´ ëì¼í 컬ë¼(ë¤)ë¡ ë§¤íëë ì´ë¤ ë¤ë¥¸ íë¡í¼í°ë¡ë¶í° ëë í¸ë¦¬ê±°ì
+ ìí´ ëë ë¤ë¥¸ ì´í리ì¼ì´ì
ì¼ë¡ë¶í° ì´ê¸°í ëë ììíê² "íìë(derived)" íë¡í¼í°ë¥¼ íì©í´ì¤ë¤.
+ </para>
+ </callout>
+ <callout arearefs="property6">
+ <para>
+ <literal>formula</literal> (ìµì
):
+ <emphasis>ê³ì°ëë</emphasis> íë¡í¼í°ì ëí´ ê°ì ì ìíë SQL ííì. ê³ì°ëë íë¡í¼í°ë¤ì
+ ê·¸ê²ë¤ ìì ì ëí ì»¬ë¼ ë§¤íì ê°ì§ ìëë¤.
+ </para>
+ </callout>
+ <callout arearefs="property7">
+ <para>
+ <literal>access</literal> (ìµì
- ëí´í¸ë <literal>property</literal>):
+ Hibernateê° íë¡í¼í° ê°ì ì ê·¼íëë° ì¬ì©í ë°©ë.
+ </para>
+ </callout>
+ <callout arearefs="property8">
+ <para>
+ <literal>lazy</literal> (ìµì
- ëí´í¸ë <literal>false</literal>):
+ ì¸ì¤í´ì¤ ë³ìê° ì²ìì¼ë¡ ì¡ì¸ì¤ ë ë ì´ íë¡í¼í°ê° lazilyíê² íì¹ë ê²ìì ì§ì íë¤(ë¹ë-ì ë°ì´í¸ì½ë
+ ìë¨ì íìë¡ íë¤).
+ </para>
+ </callout>
+ <callout arearefs="property9">
+ <para>
+ <literal>unique</literal> (ìµì
):
+ 컬ë¼ë¤ì ëí ì ì¼ ì»¨ì¤í¸ë ì¸í¸ì DDL ìì±ì ê°ë¥íê² ë§ë ë¤. ëí ì´ê²ì´ <literal>property-ref</literal>ì
+ íì¼ì´ ëë ê²ì íì©í´ì¤ë¤.
+ </para>
+ </callout>
+ <callout arearefs="property10">
+ <para>
+ <literal>not-null</literal> (ìµì
):
+ 컬ë¼ë¤ì ëí´ null ê°ë¥ 컨ì¤í¸ë ì¸í¸ì DDL ìì±ì ê°ë¥íê² ë§ë ë¤.
+ </para>
+ </callout>
+ <callout arearefs="property11">
+ <para>
+ <literal>optimistic-lock</literal> (ìµì
- ëí´í¸ë <literal>true</literal>):
+ ì´ íë¡í¼í°ì ëí ì
ë°ì´í¸ë¤ì´ optimistic ì ê¸ì íëíë ê²ì íìë¡ íê±°ë íìë¡ íì§ ììì ì§ì íë¤.
+ ë¬ë¦¬ë§í´, ì´ íë¡í¼í°ê° dirtyì¼ ë ë²ì ì¦ê°ê° ë°ìí ê²½ì°ì¸ì§ë¥¼ ê²°ì íë¤.
+ </para>
+ </callout>
+ <callout arearefs="property12">
+ <para>
+ <literal>generated</literal> (ìµì
- ëí´í¸ë <literal>never</literal>):
+ ì´ íë¡í¼í° ê°ì´ ë°ì´í°ë² ì´ì¤ì ìí´ ì¤ì ë¡ ì°ì¶ëëì§ë¥¼ ì§ì íë¤.
+ <xref linkend="mapping-generated">ì°ì¶ëë íë¡í¼í°ë¤</xref>ì ëí ë
¼ì를 ë³´ë¼.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ <emphasis>typename</emphasis>ì ë¤ìì¼ ì ìë¤:
+ </para>
+
+ <orderedlist spacing="compact">
+ <listitem>
+ <para>
+ Hibernate 기본 íì
ì ì´ë¦ (ì를 ë¤ì´. <literal>integer, string, character, date, timestamp,
+ float, binary, serializable, object, blob</literal>).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ëí´í¸ 기본 íì
ì ê°ì§ Java í´ëì¤ì ì´ë¦(ì를 ë¤ì´. <literal>int, float,
+ char, java.lang.String, java.util.Date, java.lang.Integer, java.sql.Clob</literal>).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ serializable Java í´ëì¤ì ì´ë¦.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ë§ì¶¤ íì
ì í´ëì¤ ì´ë¦(ì를 ë¤ì´. <literal>com.illflow.type.MyCustomType</literal>).
+ </para>
+ </listitem>
+ </orderedlist>
+
+ <para>
+ ë§ì¼ ë¹ì ì´ íì
ì ì§ì íì§ ìì ê²½ì°, Hibernateë ì íí Hibernate íì
ì ì¶ì í기 ìí´ ëª
ëª
ë íë¡í¼í°ì ëí´
+ reflectionì ì¬ì©í ê²ì´ë¤. Hibernateë ê·¸ ìììì 2,3,4 ê·ì¹ë¤ì ì¬ì©íì¬ íë¡í¼í° getterì ë°í í´ëì¤ì
+ ì´ë¦ì í´ìíë ¤ê³ ìëí ê²ì´ë¤. íì§ë§ ì´ê²ì íì 충ë¶íì§ë ìë¤. ì´ë¤ ê²½ì°ë¤ìì, ë¹ì ì ì¬ì í <literal>type</literal>
+ ìì±ì íìë¡ í ê²ì´ë¤.(ì를 ë¤ì´, <literal>Hibernate.DATE</literal>ì <literal>Hibernate.TIMESTAMP</literal>
+ ì¬ì´ë¥¼ 구ë³í기 ìí´, ëë ë§ì¶¤ íì
ì ì§ì í기 ìí´.)
+ </para>
+
+ <para>
+ <literal>access</literal> ìì±ì ë¹ì ì¼ë¡ íì¬ê¸ Hibernateê° ë°íì ìì íë¡í¼í°ì ì¡ì¸ì¤íë ë°©ë²ì ì ì´íëë¡ í´ì¤ë¤.
+ ëí´í¸ë¡ Hibernateë íë¡í¼í° get/set ìì í¸ì¶í ê²ì´ë¤. ë§ì¼ ë¹ì ì´ <literal>access="field"</literal>를 ì§ì í
+ ê²½ì°, Hibernateë get/set ìì í¼íê³ reflectionì ì¬ì©íì¬ ì§ì íëì ì¡ì¸ì¤ í ê²ì´ë¤. ë¹ì ì
+ <literal>org.hibernate.property.PropertyAccessor</literal> ì¸í°íì´ì¤ë¥¼ 구ííë í´ëì¤ë¥¼ ëª
ëª
í¨ì¼ë¡ì¨
+ íë¡í¼í° ì ê·¼ì ìí ë¹ì ìì ì ë°©ë를 ì§ì í ìë ìë¤.
+ </para>
+
+ <para>
+ í¹ë³í ê°ë ¥í í¹ì§ì íìë íë¡í¼í°ë¤ì´ë¤. ì´ë¤ íë¡í¼í°ë¤ì ì ìì ì½ê¸° ì ì©ì´ê³ , ê·¸ íë¡í¼í° ê°ì ë¡ë ìì ê³ì°ëë¤. ë¹ì ì
+ ê·¸ ê³ì°ì SQL ííìì¼ë¡ ì ì¸íê³ , ì´ê²ì ì¸ì¤í´ì¤ë¥¼ ë¡ëìí¤ë SQL ì§ì ë´ì <literal>SELECT</literal> ì ìë¸ì§ìë¡
+ ë²ìëë¤:
+ </para>
+
+ <programlisting><![CDATA[
+<property name="totalPrice"
+ formula="( SELECT SUM (li.quantity*p.price) FROM LineItem li, Product p
+ WHERE li.productId = p.productId
+ AND li.customerId = customerId
+ AND li.orderNumber = orderNumber )"/>]]></programlisting>
+
+ <para>
+ ë¹ì ì í¹ì 컬ë¼(주ì´ì§ ìì ììë <literal>customerId</literal>)ì ëí´ alias를 ì ì¸íì§ ììì¼ë¡ì¨ ìí°í°ë¤ ìì ì
+ í
ì´ë¸ì 참조í ì ììì ë
¸í¸íë¼. ëí ë¹ì ì ë§ì¼ ë¹ì ì´ ê·¸ ìì±ì ì¬ì©íê³ ì¶ì§ ìì ê²½ì°ì ë´í¬ë <literal><formula></literal>
+ 매í ìì를 ì¬ì©í ì ììì ë
¸í¸íë¼.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-manytoone" revision="5">
+ <title>many-to-one</title>
+
+ <para>
+ ë ë¤ë¥¸ ìì í´ëì¤ì ëí ì ê· ì°ê´ê´ê³ë <literal>many-to-one</literal> ìì를 ì¬ì©íì¬ ì ì¸ëë¤. ê´ê³í 모íì
+ many-to-one ì°ê´ê´ê³ì´ë¤.: íëì í
ì´ë¸ ë´ì ìë foreign í¤ë ëì í
ì´ë¸ì íë¼ì´ë¨¸ë¦¬ í¤ ì»¬ë¼(ë¤)ì 참조íê³ ìë¤.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="manytoone1" coords="2 70"/>
+ <area id="manytoone2" coords="3 70"/>
+ <area id="manytoone3" coords="4 70"/>
+ <area id="manytoone4" coords="5 70"/>
+ <area id="manytoone5" coords="6 70"/>
+ <areaset id="manytoone6-7" coords="">
+ <area id="manytoone6" coords='7 70'/>
+ <area id="manytoone7" coords='8 70'/>
+ </areaset>
+ <area id="manytoone8" coords="9 70"/>
+ <area id="manytoone9" coords="10 70"/>
+ <area id="manytoone10" coords="11 70"/>
+ <area id="manytoone11" coords="12 70"/>
+ <area id="manytoone12" coords="13 70"/>
+ <area id="manytoone13" coords="14 70"/>
+ <area id="manytoone14" coords="15 70"/>
+ <area id="manytoone15" coords="16 70"/>
+ <area id="manytoone16" coords="17 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<many-to-one
+ name="propertyName"
+ column="column_name"
+ class="ClassName"
+ cascade="cascade_style"
+ fetch="join|select"
+ update="true|false"
+ insert="true|false"
+ property-ref="propertyNameFromAssociatedClass"
+ access="field|property|ClassName"
+ unique="true|false"
+ not-null="true|false"
+ optimistic-lock="true|false"
+ lazy="proxy|no-proxy|false"
+ not-found="ignore|exception"
+ entity-name="EntityName"
+ formula="arbitrary SQL expression"
+ node="element-name|@attribute-name|element/@attribute|."
+ embed-xml="true|false"
+ index="index_name"
+ unique_key="unique_key_id"
+ foreign-key="foreign_key_name"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="manytoone1">
+ <para>
+ <literal>name</literal>: íë¡í¼í°ì ì´ë¦.
+ </para>
+ </callout>
+ <callout arearefs="manytoone2">
+ <para>
+ <literal>column</literal> (ìµì
):
+ foreign key 컬ë¼ì ì´ë¦. ì´ê²ì ëí ë´í¬ë <literal><column></literal> ìì(ë¤)ì ìí´
+ ì§ì ëë¤.
+ </para>
+ </callout>
+ <callout arearefs="manytoone3">
+ <para>
+ <literal>class</literal> (ìµì
- ëí´í¸ë reflectionì ìí´ ê²°ì ë íë¡í¼í° íì
):
+ ì°ê´ë í´ëì¤ì ì´ë¦.
+ </para>
+ </callout>
+ <callout arearefs="manytoone4">
+ <para>
+ <literal>cascade</literal> (ìµì
):
+ ì´ë ì¤í¼ë ì´ì
ë¤ì´ ë¶ëª¨ ê°ì²´ë¡ë¶í° ì°ê´ë ê°ì²´ë¡ ì¼ì¤ì¼ì´ë ë ê²ì¸ì§ë¥¼ ì§ì íë¤.
+ </para>
+ </callout>
+ <callout arearefs="manytoone5">
+ <para>
+ <literal>fetch</literal> (ìµì
- ëí´í¸ë <literal>select</literal>):
+ outer-join íì¹ ëë sequential select íì¹ ì¬ì´ìì ì ííë¼.
+ </para>
+ </callout>
+ <callout arearefs="manytoone6-7">
+ <para>
+ <literal>update, insert</literal> (ìµì
- ëí´í¸ë <literal>true</literal>)
+ 매íë 컬ë¼ë¤ì´ SQL <literal>UPDATE</literal>ì/ëë <literal>INSERT</literal> 문ì¥ë¤ ìì í¬í¨ë ê²ì¸ì§ë¥¼ ì§ì íë¤.
+ ëë¤ <literal>false</literal>ë¡ ì¤ì íë ê²ì ê·¸ ê°ì´ ëì¼í 컬ë¼(ë¤)ë¡ ë§¤íìí¤ë ì´ë¤ ë¤ë¥¸ 컬ë¼ë¤ë¡ë¶í° ì´ê¸°í ëê±°ë í¸ë¦¬ê±°ë
+ ë¤ë¥¸ ì´í리ì¼ì´ì
ì ìí´ ì´ê¸°íëë ë¨ìí "íìë" ì°ê´ê´ê³ ê°ì íì©íë¤.
+ </para>
+ </callout>
+ <callout arearefs="manytoone8">
+ <para>
+ <literal>property-ref</literal>: (ìµì
) ì´ foreign keyì ì¡°ì¸ëë ì°ê´ë í´ëì¤ì íë¡í¼í° ì´ë¦. ì§ì ëì§ ìì ê²½ì°,
+ ì°ê´ í´ëì¤ì íë¼ì´ë¨¸ë¦¬ í¤ê° ì¬ì©ëë¤.
+ </para>
+ </callout>
+ <callout arearefs="manytoone9">
+ <para>
+ <literal>access</literal> (ìµì
- ëí´í¸ë <literal>property</literal>):
+ Hibernateê° íë¡í¼í° ê°ì ì ê·¼íëë° ì¬ì©í ë°©ë.
+ </para>
+ </callout>
+ <callout arearefs="manytoone10">
+ <para>
+ <literal>unique</literal> (ìµì
):
+ foreign-key 컬ë¼ì ìí ì ì¼ ì»¨ì¤í¸ë ì¸í¸ì DDL ìì±ì ê°ë¥íëë¡ í´ì¤ë¤. ëí ì´ê²ì´ property-refì ëìì´ ëë ê²ì íì©í´ì¤ë¤.
+ ì´ê²ì ì°ê´ ë¤ì¤ì±(association multiplicity)ì í¨ì¨ì ì¼ë¡ ì¼ ë ì¼ë¡ ë§ë ë¤.
+ </para>
+ </callout>
+ <callout arearefs="manytoone11">
+ <para>
+ <literal>not-null</literal> (ìµì
):
+ foreign key 컬ë¼ë¤ì ìí null ê°ë¥í 컨ì¤í¸ë ì¸í¸ì DDL ìì±ì ê°ë¥íëë¡ í´ì¤ë¤.
+ </para>
+ </callout>
+ <callout arearefs="manytoone12">
+ <para>
+ <literal>optimistic-lock</literal> (ìµì
- ëí´í¸ë <literal>true</literal>):
+ ì´ íë¡í¼í°ì ëí ì
ë°ì´í¸ë¤ì´ optimistic lockì íëì íìë¡ íëì§ ìëì§ ì¬ë¶ë¥¼ ì§ì íë¤.
+ ë¬ë¦¬ ë§í´ ì´ íë¡í¼í°ê° dirtyì¼ ë version ì¦ê°ê° ë°ìí´ì¼ í ê²½ì°ì¸ì§ë¥¼ ê²°ì íë¤.
+ </para>
+ </callout>
+ <callout arearefs="manytoone13">
+ <para>
+ <literal>lazy</literal> (ìµì
- ëí´í¸ë <literal>proxy</literal>):
+ ëí´í¸ë¡, ë¨ì¼ í¬ì¸í¸ ì°ê´ë¤ì´ íë½ìëë¤. <literal>lazy="no-proxy"</literal>ë ì¸ì¤í´ì¤ ë³ìê° ì²ìì¼ë¡ ì ê·¼ë ë
+ ê·¸ íë¡í¼í°ê° lazily íì¹ë ê²ìì ì§ì íë¤(ë¹ë-ì ë°ì´í¸ì½ë ìë¨ì íìë¡ íë¤). <literal>lazy="false"</literal>ë
+ ê·¸ ì°ê´ì´ íì eagerly íì¹ë ê²ìì ì§ì íë¤.
+ </para>
+ </callout>
+ <callout arearefs="manytoone14">
+ <para>
+ <literal>not-found</literal> (ìµì
- ëí´í¸ë <literal>exception</literal>):
+ ëë½ë íë¤ì 참조íë foreign keyë¤ì´ ì´ë»ê² ì²ë¦¬ë ê²ì¸ì§ë¥¼ ì§ì íë¤: <literal>ignore</literal>ë í ê°ì ëë½ë íì
+ í ê°ì null ì°ê´ì¼ë¡ ì·¨ê¸í ê²ì´ë¤.
+ </para>
+ </callout>
+ <callout arearefs="manytoone15">
+ <para>
+ <literal>entity-name</literal> (ìµì
): ì°ê´ë í´ëì¤ì ìí°í° ì´ë¦.
+ </para>
+ </callout>
+ </calloutlist>
+ <callout arearefs="manytoone16">
+ <para>
+ <literal>formula</literal> (ìµì
): <emphasis>ê³ì°ë</emphasis> foreign keyì ëí ê°ì ì ìíë SQL ííì.
+ </para>
+ </callout>
+ </programlistingco>
+
+ <para>
+ <literal>cascade</literal> ìì± ê°ì <literal>none</literal> ìë ì´ë¤ ì미ìë ë¤ë¥¸ ê°ì¼ë¡ ì¤ì íë ê²ì
+ ì´ë¤ ì¤í¼ë ì´ì
ë¤ì ì°ê´ë ê°ì²´ìê² ë³´ê¸í ê²ì´ë¤. ì ì미í ê°ë¤ì Hibernateì 기본 ì¤í¼ë ì´ì
ë¤ì ì´ë¦ë¤,
+ ì¦ <literal>persist, merge, delete, save-update, evict, replicate, lock, refresh</literal>
+ ë¿ë§ ìëë¼ í¹ë³í ê°ë¤, ì¦ <literal>delete-orphan</literal>ê³¼ <literal>all</literal> ê·¸ë¦¬ê³ ì¤í¼ë ì´ì
ì´ë¦ë¤ì
+ ì¼í ë¶ë¦¬ë ì¡°í©ë¤, ì를 ë¤ë©´ <literal>cascade="persist,merge,evict"</literal> ëë
+ <literal>cascade="all,delete-orphan"</literal>ì´ë¤. ì ì²´ ì¤ëª
ì
+ <xref linkend="objectstate-transitive"/>를 ë³´ë¼. ë¨ì¼ê° ì°ê´ë¤(many-to-one ì°ê´ê³¼
+ one-to-one ì°ê´)ì orphan delete를 ì§ìíì§ ììì ë
¸í¸íë¼.
+ </para>
+
+ <para>
+ ì¼ë°ì ì¸ <literal>many-to-one</literal> ì ì¸ì ë¤ìê³¼ ê°ì´ ê°ë¨íê² ë³´ì¬ì§ë¤:
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="product" class="Product" column="PRODUCT_ID"/>]]></programlisting>
+
+ <para>
+ <literal>property-ref</literal> ìì±ì ì¤ì§ foreign keyê° íë¼ì´ë¨¸ë¦¬ í¤ê° ìë ì°ê´ë í
ì´ë¸ì ì ì¼ í¤ë¥¼ 참조íë
+ 리거ì ë°ì´í°ë¥¼ 매ííëë°ë§ ì¬ì©ëë¤. ì´ê²ì ê¼´ì¬ëì´ ê´ê³í 모íì´ë¤. ì를 ë¤ì´, <literal>Product</literal>
+ í´ëì¤ê° íë¼ì´ë¨¸ë¦¬ í¤ë¥¼ ìë, ì ì¼í ìë¦¬ì¼ ë²í¸ë¥¼ ê°ëë¤ê³ ê°ì íì.(<literal>unique</literal> ìì±ì
+ SchemaExport ëêµ¬ë¡ Hibernateì DDL ìì±ì ì ì´íë¤.)
+ </para>
+
+ <programlisting><![CDATA[<property name="serialNumber" unique="true" type="string" column="SERIAL_NUMBER"/>]]></programlisting>
+
+ <para>
+ ê·¸ë° ë¤ì <literal>OrderItem</literal>ì ëí 매íì ë¤ìì ì¬ì©í ê²ì´ë¤:
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="product" property-ref="serialNumber" column="PRODUCT_SERIAL_NUMBER"/>]]></programlisting>
+
+ <para>
+ íì§ë§ ì´ê²ì íì¤í ê¶ì¥ëì§ ìëë¤.
+ </para>
+
+ <para>
+ ë§ì¼ 참조ë ì ì¼ í¤ê° ì°ê´ë ìí°í°ì ì¬ë¬ íë¡í¼í°ë¤ì í¬í¨í ê²½ì°, ë¹ì ì ëª
ëª
ë <literal><properties></literal>
+ ìì ë´ë¶ì 참조ë íë¡í¼í°ë¤ì 매íí ê²ì´ë¤.
+ </para>
+
+ <para>
+ ë§ì¼ 참조ë ì ì¼í¤ê° ì»´í¬ëí¸ì íë¡í¼í°ì¼ ê²½ì°, ë¹ì ì íëì íë¡í¼í° ê²½ë¡ë¥¼ ì§ì í ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="owner" property-ref="identity.ssn" column="OWNER_SSN"/>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-onetoone" revision="3">
+ <title>one-to-one</title>
+
+ <para>
+ ë ë¤ë¥¸ ìì í´ëì¤ì ëí one-to-one ì°ê´ê´ê³ë <literal>one-to-one</literal> ìì를 ì¬ì©íì¬ ì ì¸ëë¤.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="onetoone1" coords="2 70"/>
+ <area id="onetoone2" coords="3 70"/>
+ <area id="onetoone3" coords="4 70"/>
+ <area id="onetoone4" coords="5 70"/>
+ <area id="onetoone5" coords="6 70"/>
+ <area id="onetoone6" coords="7 70"/>
+ <area id="onetoone7" coords="8 70"/>
+ <area id="onetoone8" coords="9 70"/>
+ <area id="onetoone9" coords="10 70"/>
+ <area id="onetoone10" coords="11 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<one-to-one
+ name="propertyName"
+ class="ClassName"
+ cascade="cascade_style"
+ constrained="true|false"
+ fetch="join|select"
+ property-ref="propertyNameFromAssociatedClass"
+ access="field|property|ClassName"
+ formula="any SQL expression"
+ lazy="proxy|no-proxy|false"
+ entity-name="EntityName"
+ node="element-name|@attribute-name|element/@attribute|."
+ embed-xml="true|false"
+ foreign-key="foreign_key_name"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="onetoone1">
+ <para>
+ <literal>name</literal>: íë¡í¼í°ì ì´ë¦.
+ </para>
+ </callout>
+ <callout arearefs="onetoone2">
+ <para>
+ <literal>class</literal> (ìµì
- ëí´í¸ë reflectionì ìí´ ê²°ì ë íë¡í¼í° íì
):
+ ì°ê´ë í´ëì¤ì ì´ë¦.
+ </para>
+ </callout>
+ <callout arearefs="onetoone3">
+ <para>
+ <literal>cascade</literal> (ìµì
) ì´ë ì¤í¼ë ì´ì
ë¤ì´ ë¶ëª¨ ê°ì²´ë¡ë¶í° ì°ê´ë ê°ì²´ë¡
+ ì¼ì¤ì¼ì´ë ë ê²ì¸ì§ë¥¼ ì§ì íë¤.
+ </para>
+ </callout>
+ <callout arearefs="onetoone4">
+ <para>
+ <literal>constrained</literal> (ìµì
) 매íë í
ì´ë¸ì íë¼ì´ë¨¸ë¦¬ í¤ì ëí foreign í¤
+ 컨ì¤í¸ë ì¸í¸ê° ì°ê´ë í´ëì¤ì í
ì´ë¸ì 참조íëì§ ì¬ë¶ë¥¼ ì§ì íë¤. ì´ ìµì
ì <literal>save()</literal>ì
+ <literal>delete()</literal>ê° ì¼ì¤ì¼ì´ë ëë ììì ìí¥ì ì£¼ê³ , ê·¸ ì°ê´ì´ íë½ì ë ê²ì¸ì§ ì¬ë¶ë¥¼ ê²°ì íë¤
+ (ëí ì¤í¤ë§ ë´ë³´ë´ê¸° ë구ì ìí´ ì¬ì©ëë¤).
+ </para>
+ </callout>
+ <callout arearefs="onetoone5">
+ <para>
+ <literal>fetch</literal> (ìµì
- ëí´í¸ë <literal>select</literal>):
+ outer-join íì¹ ëë ìì°¨ì ì¸ select íì¹ ì¤ìì ì ííë¼.
+ </para>
+ </callout>
+ <callout arearefs="onetoone6">
+ <para>
+ <literal>property-ref</literal>: (ìµì
) ì´ í´ëì¤ì íë¼ì´ë¨¸ë¦¬ í¤ì ì°ê²°ë ì°ê´ í´ëì¤ì íë¡í¼í°ì ì´ë¦.
+ ë§ì¼ ì§ì ëì§ ìì ê²½ì°, ì°ê´ í´ëì¤ì íë¼ì´ë¨¸ë¦¬ í¤ê° ì¬ì©ëë¤.
+ </para>
+ </callout>
+ <callout arearefs="onetoone7">
+ <para>
+ <literal>access</literal> (ìµì
- ëí´í¸ë <literal>property</literal>):
+ Hibernateê° íë¡í¼í° ê°ì ì ê·¼ íëë° ì¬ì©í ë°©ë.
+ </para>
+ </callout>
+ <callout arearefs="onetoone8">
+ <para>
+ <literal>formula</literal> (ìµì
):
+ ê±°ì 모ë one to one ì°ê´ê´ê³ë¤ì ìì íë ìí°í°ì íë¼ì´ë¨¸ë¦¬ í¤ë¡ 매íëë¤. ì´ê²ì´ ê·¸ ê²½ì°ê° ìë ë문 ê²½ì°ë¤ìì,
+ ë¹ì ì SQL formula ì¬ì©ì ê²°í©ìí¬ ëªëª ë¤ë¥¸ 컬ë¼, 컬ë¼ë¤, ëë ííìì ì§ì í ì ìë¤.(ìì ë
+ <literal>org.hibernate.test.onetooneformula</literal>를 ë³´ë¼.)
+ </para>
+ </callout>
+ <callout arearefs="onetoone9">
+ <para>
+ <literal>lazy</literal> (ìµì
- ëí´í¸ë <literal>proxy</literal>):
+ ëí´í¸ë¡ í쪽 ë ì°ê´ë¤ì´ íë½ì ëë¤. <literal>lazy="no-proxy"</literal>ë ì¸ì¤í´ì¤ ë³ìê° ì²ì ì ê·¼ë ë
+ ê·¸ íë¡í¼í°ê° lazily íì¹ë ê²ìì ì§ì íë¤(ë¹ë-ì ë°ì´í¸ì½ë ìë¨ì íìë¡ íë¤).
+ <literal>lazy="false"</literal>ë ê·¸ ì°ê´ë¤ì´ íì eagerly íì¹ë ê²ìì ì§ì íë¤. <emphasis>ë§ì¼
+ <literal>constrained="false"</literal>ì¸ ê²½ì°ì, íë½ì±ì ë¶ê°ë¥íê³ Hibernateë ê·¸ ì°ê´ì eager íì¹ìí¬ ê²ì´ë¤!</emphasis>
+ </para>
+ </callout>
+ <callout arearefs="onetoone10">
+ <para>
+ <literal>entity-name</literal> (ìµì
): ì°ê´ë í´ëì¤ì ìí°í° ì´ë¦.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ one-to-one ì°ê´ê´ê³ìë ë ê°ì§ ë³ì¢
ì´ ì¡´ì¬íë¤:
+ </para>
+ <itemizedlist>
+ <listitem><para>
+ íë¼ì´ë¨¸ë¦¬ í¤ ì°ê´ê´ê³ë¤
+ </para></listitem>
+ <listitem><para>
+ ì ì¼ foreign í¤ ì°ê´ê´ê³ë¤
+ </para></listitem>
+ </itemizedlist>
+
+ <para>
+ íë¼ì´ë¨¸ë¦¬ í¤ ì°ê´ë¤ì í¹ë³í í
ì´ë¸ 컬ë¼ì íìë¡ íì§ ìëë¤; ë§ì¼ ë ê°ì íë¤ì´ ê·¸ ì°ê´ì ìí´ ê´ê³ì§ìì§ë©´,
+ ë ê°ì í
ì´ë¸ íë¤ì ëì¼í íë¼ì´ë¨¸ë¦¬ í¤ ê°ì ê³µì íë¤. ë°ë¼ì ë§ì¼ ë ê°ì ê°ì²´ë¤ì´ íë¼ì´ë¨¸ë¦¬ í¤ ì°ê´ì ìí´
+ ê´ê³ì§ìì§ëë¡ ë¹ì ì´ ìí ê²½ì°, ë¹ì ì ê·¸ê²ë¤ì ëì¼í ìë³ì ê°ì´ í ë¹ëëë¡ í´ì¼ íë¤!
+ </para>
+
+ <para>
+ íë¼ì´ë¨¸ë¦¬ í¤ ì°ê´ì ëí´, ë¤ì 매íë¤ì <literal>Employee</literal>ì <literal>Person</literal> ê°ê°ì ì¶ê°íë¼.
+ </para>
+
+ <programlisting><![CDATA[<one-to-one name="person" class="Person"/>]]></programlisting>
+ <programlisting><![CDATA[<one-to-one name="employee" class="Employee" constrained="true"/>]]></programlisting>
+
+ <para>
+ ì´ì ì°ë¦¬ë PERSON ê³¼ EMPLOYEE í
ì´ë¸ë¤ìì ê´ê³ì§ìì§ íë¤ì íë¼ì´ë¨¸ë¦¬ í¤ë¤ì´ ëì¼í¨ì íì¤í í´ì¼ íë¤! ì°ë¦¬ë
+ <literal>foreign</literal>ë¡ ëª
ëª
ëë í¹ë³í Hibernate ìë³ì ìì± ë°©ë를 ì¬ì©íë¤:
+ </para>
+
+ <programlisting><![CDATA[<class name="person" table="PERSON">
+ <id name="id" column="PERSON_ID">
+ <generator class="foreign">
+ <param name="property">employee</param>
+ </generator>
+ </id>
+ ...
+ <one-to-one name="employee"
+ class="Employee"
+ constrained="true"/>
+</class>]]></programlisting>
+
+ <para>
+ ê·¸ë <literal>Person</literal>ì ìë¡ì´ ì ì¥ë ì¸ì¤í´ì¤ë ê·¸ <literal>Person</literal>ì
+ <literal>employee</literal> íë¡í¼í°ì ëí´ ì°¸ì¡°ë <literal>Employee</literal> ì¸ì¤í´ì¤ì ëì¼í
+ íë¼ì´ë¨¸ë¦¬ í¤ë¥¼ í ë¹ë°ëë¤.
+ </para>
+
+ <para>
+ ë¬ë¦¬, <literal>Employee</literal>ë¡ë¶í° <literal>Person</literal>ì¼ë¡ì ì ì¼ ì»¨ì¤í¸ë ì¸í¸ë¥¼ ê°ì§
+ íëì foreign keyë ë¤ìê³¼ ê°ì´ ííë ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="person" class="Person" column="PERSON_ID" unique="true"/>]]></programlisting>
+
+ <para>
+ ê·¸ë¦¬ê³ ì´ ì°ê´ì ë¤ìì <literal>Person</literal> 매íì ì¶ê°í¨ì¼ë¡ì¨ ìë°©í¥ì´ ë ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[<one-to-one name"employee" class="Employee" property-ref="person"/>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-naturalid">
+ <title>natural-id</title>
+
+ <programlisting><![CDATA[<natural-id mutable="true|false"/>
+ <property ... />
+ <many-to-one ... />
+ ......
+</natural-id>]]></programlisting>
+
+ <para>
+ ë¹ë¡ ì°ë¦¬ê° íë¼ì´ë¨¸ë¦¬ í¤ë¤ë¡ì ëì©í¤ë¤ì ì¬ì©íë ê²ì ê¶ì¥íìì§ë¼ë, ë¹ì ì ì¬ì í 모ë ìí°í°ë¤ì ëí natural
+ í¤ë¤ì ìë³íê³ ì ìí ê²ì´ë¤. narutal í¤ë ì ì¼(unique)íê³ nullì´ ìë íë¡í¼í° ëë íë¡í¼í°ë¤ì ì¡°í©ì´ë¤.
+ ê·¸ê²ì´ ëí ë¶ë³íë ê²ì¼ ê²½ì°ê° ë ì¢ë¤. <literal><natural-id></literal> ìì ë´ë¶ì ìë natural í¤ì
+ íë¡í¼í°ë¤ì 매ííë¼. Hibernateë íìì ì¸ ì ì¼ í¤ì null íì©ê°ë¥í 컨ì¤í¸ë ì¸í¸ë¤ì ìì±ìí¬ ê²ì´ê³ , ë¹ì ì
+ 매íì ë³´ë¤ ìê° ì¤ëª
ì ì´ê² ë ê²ì´ë¤.
+ </para>
+
+ <para>
+ ì°ë¦¬ë ë¹ì ì´ ìí°í°ì ëí narutal í¤ íë¡í¼í°ë¤ì ë¹êµíëë° <literal>equals()</literal>ì
+ <literal>hashCode()</literal>를 구íí ê²ì ê°ë ¥íê² ê¶ì¥íë¤.
+ </para>
+
+ <para>
+ ì´ ë§¤íì natural íë¼ì´ë¨¸ë¦¬ í¤ë¤ì ê°ì§ ìí°í°ë¤ì ìí ì©ëë¡ ê³ ìë ê²ì ìëë¤.
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>mutable</literal> (ìµì
, ëí´í¸ë <literal>false</literal>):
+ ëí´í¸ë¡, narutal ìë³ì íë¡í¼í°ë¤ì ë³ê²½ë ì ìë ê²(ìì)ì¼ë¡ ê°ì ëë¤.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-component" revision="2">
+ <title>component, dynamic-component</title>
+
+ <para>
+ <literal><component></literal> ììë ìì ê°ì²´ì íë¡í¼í°ë¤ì ë¶ëª¨ í´ëì¤ì ëí í
ì´ë¸ì 컬ë¼ë¤ë¡ 매íìí¨ë¤.
+ ì»´í¬ëí¸ë¤ì ê·¸ê²ë¤ ìì ì íë¡í¼í°ë¤, ì»´í¬ëí¸ë¤, ëë ì½ë ì
ë¤ì ì ì¸íë¤. ì´ë "ì»´í¬ëí¸ë¤"ì ë³´ë¼.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="component1" coords="2 45"/>
+ <area id="component2" coords="3 45"/>
+ <area id="component3" coords="4 45"/>
+ <area id="component4" coords="5 45"/>
+ <area id="component5" coords="6 45"/>
+ <area id="component6" coords="7 45"/>
+ <area id="component7" coords="8 45"/>
+ <area id="component8" coords="9 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<component
+ name="propertyName"
+ class="className"
+ insert="true|false"
+ update="true|false"
+ access="field|property|ClassName"
+ lazy="true|false"
+ optimistic-lock="true|false"
+ unique="true|false"
+ node="element-name|."
+>
+
+ <property ...../>
+ <many-to-one .... />
+ ........
+</component>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="component1">
+ <para>
+ <literal>name</literal>: íë¡í¼í°ì ì´ë¦.
+ </para>
+ </callout>
+ <callout arearefs="component2">
+ <para>
+ <literal>class</literal> (ìµì
- ëí´í¸ë reflectionì ìí´ ê²°ì ë íë¡í¼í° íì
):
+ ì»´í¬ëí¸(ìì) í´ëì¤ì ì´ë¦.
+ </para>
+ </callout>
+ <callout arearefs="component3">
+ <para>
+ <literal>insert</literal>: 매íë 컬ë¼ë¤ì´ SQL <literal>INSERT</literal>ë¤ ìì
+ ëíëì¼ íëê°?
+ </para>
+ </callout>
+ <callout arearefs="component4">
+ <para>
+ <literal>update</literal>: 매íë 컬ë¼ë¤ì´ SQL <literal>UPDATE</literal>ë¤ ìì
+ ëíëì¼ íëê°?
+ </para>
+ </callout>
+ <callout arearefs="component5">
+ <para>
+ <literal>access</literal> (ìµì
- ëí´í¸ë <literal>property</literal>):
+ Hibernateê° íë¡í¼í° ê°ì ì¡ì¸ì¤íëë° ì¬ì©í ë°©ë.
+ </para>
+ </callout>
+ <callout arearefs="component6">
+ <para>
+ <literal>lazy</literal> (ìµì
- ëí´í¸ë <literal>false</literal>):
+ ì¸ì¤í´ì¤ ë³ìê° ì²ìì¼ë¡ ì¡ì¸ì¤ë ë ì´ ì»´í¬ëí¸ê° lazily(ë리ê²) íì¹ëì´ì¼ íëì§ ì¬ë¶ë¥¼ ì§ì íë¤
+ (ë¹ë ì ë°ì´í¸ì½ë ìë¨ì íìë¡ íë¤).
+ </para>
+ </callout>
+ <callout arearefs="component7">
+ <para>
+ <literal>optimistic-lock</literal> (ìµì
- ëí´í¸ë <literal>true</literal>):
+ ì´ ì»´í¬ëí¸ì ëí ì
ë°ì´í¸ë¤ì´ optimistic ì ê¸ì íëíë ê²ì íìë¡ íëì§ ì¬ë¶ë¥¼ ì§ì íë¤.
+ ë¬ë¦¬ ë§í´ ì´ íë¡í¼í°ê° dirty ì¼ ë ë²ì ì¦ê°ê° ë°ìí ê²ì¸ì§ ì¬ë¶ë¥¼ ê²°ì íë¤.
+ </para>
+ </callout>
+ <callout arearefs="component8">
+ <para>
+ <literal>unique</literal> (ìµì
- ëí´í¸ë <literal>false</literal>):
+ ì ì¼ ì»¨ì¤í¸ë ì¸í¸ê° ì»´í¬ëí¸ì 모ë 매íë 컬ë¼ë¤ì ëí´ ì¡´ì¬íëì§ ì¬ë¶ë¥¼ ì§ì íë¤.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ ìì <literal><property></literal> íê·¸ë¤ì ìì í´ëì¤ì íë¡í¼í°ë¤ì í
ì´ë¸ 컬ë¼ë¤ë¡ 매íìí¨ë¤.
+ </para>
+
+ <para>
+ <literal><component></literal> ììë ì»´í¬ëí¸ í´ëì¤ì íë¡í¼í°ë¥¼ í¬í¨íë ìí°í°ì ëí 참조ë¡ì 매íìí¤ë
+ <literal><parent></literal> ìë¸ìì를 íì©íë¤.
+ </para>
+
+ <para>
+ <literal><dynamic-component></literal> ììë ì»´í¬ëí¸ë¡ì 매íë <literal>Map</literal>ì íì©íë¤.
+ ì¬ê¸°ì íë¡í¼í° ì´ë¦ë¤ì mapì í¤ë¤ì 참조íë¤. <xref linkend="components-dynamic"/>ì ë³´ë¼.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-properties" revision="2">
+ <title>properties</title>
+
+ <para>
+ <literal><properties></literal> ììë í´ëì¤ì íë¡í¼í°ë¤ì ëª
ëª
ë, ë
¼ë¦¬ì 그룹íì ëí ì ì를 íì©íë¤.
+ ê·¸ 구조ì ëí ê°ì¥ ì¤ìí ì¬ì©ì ê·¸ê²ì´ íë¡í¼í°ë¤ì ì¡°í©ì´ <literal>property-ref</literal>ì ëìì´ ëë ê²ì
+ íì©í´ì¤ë¤ë ì ì´ë¤. ëí ê·¸ê²ì ë¤ì¤ ì»¬ë¼ ì ì¼ ì»¨ì¤í¸ë ì¸ë를 ì ìíë í¸ë¦¬í ë°©ë²ì´ë¤.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="properties1" coords="2 45"/>
+ <area id="properties2" coords="3 45"/>
+ <area id="properties3" coords="4 45"/>
+ <area id="properties4" coords="5 45"/>
+ <area id="properties5" coords="6 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<properties
+ name="logicalName"
+ insert="true|false"
+ update="true|false"
+ optimistic-lock="true|false"
+ unique="true|false"
+>
+
+ <property ...../>
+ <many-to-one .... />
+ ........
+</properties>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="properties1">
+ <para>
+ <literal>name</literal>: 그룹íì ë
¼ë¦¬ì ì´ë¦ - ì¤ì íë¡í¼í° ì´ë¦ì´ <emphasis>ìëë¤</emphasis>.
+ </para>
+ </callout>
+ <callout arearefs="properties2">
+ <para>
+ <literal>insert</literal>: 매íë 컬ë¼ë¤ì´ SQL <literal>INSERT</literal>ë¤ ë´ì ëíë ê²ì¸ê°?
+ </para>
+ </callout>
+ <callout arearefs="properties3">
+ <para>
+ <literal>update</literal>: 매íë 컬ë¼ë¤ì´ SQL <literal>UPDATE</literal>ë¤ ë´ì ëíë ê²ì¸ê°?
+ </para>
+ </callout>
+ <callout arearefs="properties4">
+ <para>
+ <literal>optimistic-lock</literal> (ìµì
- ëí´í¸ë <literal>true</literal>):
+ ì´ë¤ íë¡í¼í°ë¤ì ëí ì
ë°ì´í¸ë¤ì´ optimistic ì ê¸ì íëì íìë¡ íëì§ ì¬ë¶ë¥¼ ì§ì íë¤. ë¬ë¦¬ ë§í´
+ ì´ íë¡í¼í°ê° dirty ì¼ ë ë²ì ì¦ê°ê° ë°ìí ê²ì¸ì§ ì¬ë¶ë¥¼ ê²°ì íë¤.
+ </para>
+ </callout>
+ <callout arearefs="properties5">
+ <para>
+ <literal>unique</literal> (ìµì
- ëí´í¸ë <literal>false</literal>):
+ ì ì¼ ì»¨ì¤í¸ë ì¸í¸ê° ì»´í¬ëí¸ì 모ë 매íë 컬ë¼ë¤ì ëí´ ì¡´ì¬íëì§ ì¬ë¶ë¥¼ ì§ì íë¤.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ ì를 ë¤ì´, ë§ì¼ ì°ë¦¬ê° ë¤ì <literal><properties></literal> 매íì ê°ì§ ê²½ì°:
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="personNumber"/>
+ ...
+ <properties name="name"
+ unique="true" update="false">
+ <property name="firstName"/>
+ <property name="initial"/>
+ <property name="lastName"/>
+ </properties>
+</class>]]></programlisting>
+
+ <para>
+ ê·¸ ë ì°ë¦¬ë íë¼ì´ë¨¸ë¦¬ í¤ê° ìë, <literal>Person</literal> í
ì´ë¸ì ì´ ì ì¼ í¤ë¥¼ 참조íë ì´ë¤ 리거ì ë°ì´í° ì°ê´ì
+ ê°ì§ ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="person"
+ class="Person" property-ref="name">
+ <column name="firstName"/>
+ <column name="initial"/>
+ <column name="lastName"/>
+</many-to-one>]]></programlisting>
+
+ <para>
+ ì°ë¦¬ë 리거ì ë°ì´í°ë¥¼ 매íìí¤ë 컨í
ì¤í¸ ë°ê¹¥ìì ì´ë° ì¢
ë¥ì ê²ì ì¬ì©íë ê²ì ê¶ì¥íì§ ìëë¤.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-subclass" revision="4">
+ <title>subclass</title>
+
+ <para>
+ ë§ì§ë§ì¼ë¡, ë¤íì± ììì±ì ë£¨í¸ ìì í´ëì¤ì ëí ê°ê°ì ìë¸í´ëì¤ ì ì¸ì íìë¡ íë¤.(ê¶ì¥ëë)
+ table-per-class-hierarchy(í
ì´ë¸ ë¹ í´ëì¤ ê³ì¸µêµ¬ì¡°) 매í ë°©ëì ê²½ì°, <literal><subclass></literal>
+ ì ì¸ì´ ì¬ì©ëë¤.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="subclass1" coords="2 55"/>
+ <area id="subclass2" coords="3 55"/>
+ <area id="subclass3" coords="4 55"/>
+ <area id="subclass4" coords="5 55"/>
+ </areaspec>
+ <programlisting><![CDATA[<subclass
+ name="ClassName"
+ discriminator-value="discriminator_value"
+ proxy="ProxyInterface"
+ lazy="true|false"
+ dynamic-update="true|false"
+ dynamic-insert="true|false"
+ entity-name="EntityName"
+ node="element-name"
+ extends="SuperclassName">
+
+ <property .... />
+ .....
+</subclass>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="subclass1">
+ <para>
+ <literal>name</literal>: ìë¸í´ëì¤ì ì ì²´ ììì´ê° ë¶ì í´ëì¤ ì´ë¦.
+ </para>
+ </callout>
+ <callout arearefs="subclass2">
+ <para>
+ <literal>discriminator-value</literal> (ìµì
- ëí´í¸ë í´ëì¤ ì´ë¦):
+ ê°ê°ì ìë¸í´ëì¤ë¤ì 구ë¶ì§ë ê°.
+ </para>
+ </callout>
+ <callout arearefs="subclass3">
+ <para>
+ <literal>proxy</literal> (ìµì
): lazy ì´ê¸°í íë½ìë¤ì ì¬ì©íëë° í´ëì¤ ëë ì¸í°íì´ì¤ë¥¼ ì§ì íë¤.
+ </para>
+ </callout>
+ <callout arearefs="subclass4">
+ <para>
+ <literal>lazy</literal> (ìµì
- ëí´í¸ë <literal>true</literal>):
+ <literal>lazy="false"</literal> ì¤ì ì lazy íì¹ì ì¬ì©ì ë¶ê°ë¥íê² ë§ë ë¤.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ ê°ê°ì ìë¸í´ëì¤ë ê·¸ê² ìì ì ìì íë¡í¼í°ë¤ê³¼ ìë¸í´ëì¤ë¤ì ì ì¸í ê²ì´ë¤. <literal><version></literal>
+ íë¡í¼í°ì <literal><id></literal> íë¡í¼í°ë ë£¨í¸ í´ëì¤ë¡ë¶í° ììëë¤ê³ ê°ì ëë¤. ê³ì¸µêµ¬ì¡° ë´ìì ê°ê°ì
+ ìë¸í´ëì¤ë ì ì¼í <literal>discriminator-value</literal>를 ì ìí´ì¼ íë¤. noneì´ ì§ì ë ê²½ì°, ì ì²´ ììì´ê°
+ ë¶ì ìë° í´ëì¤ ì´ë¦ì´ ì¬ì©ëë¤.
+ </para>
+
+ <para>
+ ìì 매íë¤ì ëí ì ë³´ë <xref linkend="inheritance"/>ì ë³´ë¼.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-joinedsubclass" revision="3">
+ <title>joined-subclass</title>
+
+ <para>
+ ë¤ë¥¸ ë°©ë²ì¼ë¡ ê°ê°ì ìë¸í´ëì¤ë ê·¸ê² ìì ì´ í
ì´ë¸ë¡ 매íë ì ìë¤(table-per-subclass 매í ë°©ë). ììë ìíë
+ ìí¼í´ëì¤ì í
ì´ë¸ê³¼ ì¡°ì¸í¨ì¼ë¡ì¨ ê²ìëë¤. ì°ë¦¬ë <literal><joined-subclass></literal> ìì를 ì¬ì©íë¤.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="joinedsubclass1" coords="2 45"/>
+ <area id="joinedsubclass2" coords="3 45"/>
+ <area id="joinedsubclass3" coords="4 45"/>
+ <area id="joinedsubclass4" coords="5 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<joined-subclass
+ name="ClassName"
+ table="tablename"
+ proxy="ProxyInterface"
+ lazy="true|false"
+ dynamic-update="true|false"
+ dynamic-insert="true|false"
+ schema="schema"
+ catalog="catalog"
+ extends="SuperclassName"
+ persister="ClassName"
+ subselect="SQL expression"
+ entity-name="EntityName"
+ node="element-name">
+
+ <key .... >
+
+ <property .... />
+ .....
+</joined-subclass>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="joinedsubclass1">
+ <para>
+ <literal>name</literal>: ìë¸í´ëì¤ì ì ì²´ ììì´ê° ë¶ì í´ëì¤ ëª
.
+ </para>
+ </callout>
+ <callout arearefs="joinedsubclass2">
+ <para>
+ <literal>table</literal>: ìë¸í´ëì¤ í
ì´ë¸ì ì´ë¦.
+ </para>
+ </callout>
+ <callout arearefs="joinedsubclass3">
+ <para>
+ <literal>proxy</literal> (ìµì
): íë½ìë¤ì lazy ì´ê¸°í ìí¤ëë° ì¬ì©í í´ëì¤ ëë ì¸í°íì´ì¤ë¥¼ ì§ì íë¤.
+ </para>
+ </callout>
+ <callout arearefs="joinedsubclass4">
+ <para>
+ <literal>lazy</literal> (ìµì
- ëí´í¸ë <literal>true</literal>):
+ <literal>lazy="false"</literal> ì¤ì ì lazy íì¹ì ì¬ì©ë¶ê°ë¥íê² ë§ë ë¤
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ íë³ì(discriminator) 컬ë¼ì ì´ ë§¤í ë°©ëì íìíì§ ìë¤. íì§ë§ ê°ê°ì ìë¸í´ëì¤ë <literal><key></literal> ìì를
+ ì¬ì©íì¬ ê°ì²´ ìë³ì를 ë³´ê´íë í
ì´ë¸ 컬ë¼ì ì ì¸í´ì¼ íë¤. ì´ ì¥ì ìì ë¶ë¶ì ìë 매íì ë¤ìê³¼ ê°ì´ ë¤ì ìì±ë ê²ì´ë¤:
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="eg">
+
+ <class name="Cat" table="CATS">
+ <id name="id" column="uid" type="long">
+ <generator class="hilo"/>
+ </id>
+ <property name="birthdate" type="date"/>
+ <property name="color" not-null="true"/>
+ <property name="sex" not-null="true"/>
+ <property name="weight"/>
+ <many-to-one name="mate"/>
+ <set name="kittens">
+ <key column="MOTHER"/>
+ <one-to-many class="Cat"/>
+ </set>
+ <joined-subclass name="DomesticCat" table="DOMESTIC_CATS">
+ <key column="CAT"/>
+ <property name="name" type="string"/>
+ </joined-subclass>
+ </class>
+
+ <class name="eg.Dog">
+ <!-- mapping for Dog could go here -->
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ ìì 매íë¤ì ëí ì ë³´ë <xref linkend="inheritance"/>ì ë³´ë¼.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-unionsubclass" revision="2">
+ <title>union-subclass</title>
+
+ <para>
+ ì 3ì ìµì
ì ìì ê³ì¸µêµ¬ì¡°ì concrete í´ëì¤ë¤ ë§ì í
ì´ë¸ë¤ë¡ 매ííë ê²ì´ë¤ (table-per-concrete-class ë°©ë). ì¬ê¸°ì
+ ê°ê°ì í
ì´ë¸ì ììë ìí를 í¬í¨íì¬ í´ëì¤ì 모ë ìì ìí를 ì ìíë¤. Hibernateìì, ê·¸ê²ì ê·¸ë° ìì ê³ì¸µêµ¬ì¡°ë¤ì
+ ëª
ìì ì¼ë¡ 매ííëë° íìì ì´ì§ ìë¤. ë¹ì ì ë³ëì <literal><class></literal> ì ì¸ì ê°ì§ ê°ê°ì í´ëì¤ë¥¼
+ ê°ë¨í 매íìí¬ ì ìë¤. íì§ë§ ë¹ì ì´ ë¤íì± ì°ê´ê´ê³ë¤(ì를 ë¤ë©´ ë¹ì ì ê³ì¸µêµ¬ì¡°ì ìí¼í´ëì¤ì ëí ì°ê´)ì ì¬ì©íê³ ì ìí
+ ê²½ì°, ë¹ì ì <literal><union-subclass></literal> 매íì ì¬ì©í íìê° ìë¤.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="unionsubclass1" coords="2 45"/>
+ <area id="unionsubclass2" coords="3 45"/>
+ <area id="unionsubclass3" coords="4 45"/>
+ <area id="unionsubclass4" coords="5 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<union-subclass
+ name="ClassName"
+ table="tablename"
+ proxy="ProxyInterface"
+ lazy="true|false"
+ dynamic-update="true|false"
+ dynamic-insert="true|false"
+ schema="schema"
+ catalog="catalog"
+ extends="SuperclassName"
+ abstract="true|false"
+ persister="ClassName"
+ subselect="SQL expression"
+ entity-name="EntityName"
+ node="element-name">
+
+ <property .... />
+ .....
+</union-subclass>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="unionsubclass1">
+ <para>
+ <literal>name</literal>: ìë¸í´ëì¤ì ì ì²´ ììì´ê° ë¶ì í´ëì¤ ëª
.
+ </para>
+ </callout>
+ <callout arearefs="unionsubclass2">
+ <para>
+ <literal>table</literal>: ìë¸í´ëì¤ í
ì´ë¸ì ì´ë¦.
+ </para>
+ </callout>
+ <callout arearefs="unionsubclass3">
+ <para>
+ <literal>proxy</literal> (ìµì
): íë½ìë¤ì lazy ì´ê¸°í ìí¤ëë° ì¬ì©í í´ëì¤ ëë ì¸í°íì´ì¤ë¥¼ ì§ì íë¤.
+ </para>
+ </callout>
+ <callout arearefs="unionsubclass4">
+ <para>
+ <literal>lazy</literal> (ìµì
- ëí´í¸ë <literal>true</literal>):
+ <literal>lazy="false"</literal> ì¤ì ì lazy íì¹ì ì¬ì©ë¶ê°ë¥íê² ë§ë ë¤.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ ì´ ë§¤í ë°©ëìë íë³ì 컬ë¼ì´ë í¤ ì»¬ë¼ì´ íìíì§ ìë¤.
+ </para>
+
+ <para>
+ ìì 매íë¤ì ëí ì ë³´ë <xref linkend="inheritance"/>ì ë³´ë¼.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-join" revision="3">
+ <title>join</title>
+
+ <para>
+ <literal><join></literal>ìì를 ì¬ì©íë©´, í
ì´ë¸ë¤ ì¬ì´ì 1ëì¼ ê´ê³ê° ì¡´ì¬í ë,
+ íëì í´ëì¤ì ìí íë¡í¼í°ë¤ì ì¬ë¬ í
ì´ë¸ë¤ë¡ 매íìí¤ë ê²ì´
+ ê°ë¥íë¤.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="join1" coords="2 50"/>
+ <area id="join2" coords="3 50"/>
+ <area id="join3" coords="4 50"/>
+ <area id="join4" coords="5 50"/>
+ <area id="join5" coords="6 50"/>
+ <area id="join6" coords="7 50"/>
+ </areaspec>
+ <programlisting><![CDATA[<join
+ table="tablename"
+ schema="owner"
+ catalog="catalog"
+ fetch="join|select"
+ inverse="true|false"
+ optional="true|false">
+
+ <key ... />
+
+ <property ... />
+ ...
+</join>]]></programlisting>
+
+ <calloutlist>
+ <callout arearefs="join1">
+ <para>
+ <literal>table</literal>: ì¡°ì¸ë í
ì´ë¸ì ì´ë¦.
+ </para>
+ </callout>
+ <callout arearefs="join2">
+ <para>
+ <literal>schema</literal> (ìµì
): ë£¨í¸ <literal><hibernate-mapping></literal> ììì
+ ìí´ ì§ì ë ì¤í¤ë§ ì´ë¦ì ì¤ë²ë¼ì´ë ìí¨ë¤
+ </para>
+ </callout>
+ <callout arearefs="join3">
+ <para>
+ <literal>catalog</literal> (ìµì
): ë£¨í¸ <literal><hibernate-mapping></literal> ììì
+ ìí´ ì§ì ë ì¹´íë¡ ì´ë¦ì ì¤ë²ë¼ì´ë ìí¨ë¤.
+ </para>
+ </callout>
+ <callout arearefs="join4">
+ <para>
+ <literal>fetch</literal> (ìµì
- ëí´í¸ë <literal>join</literal>):
+ <literal>join</literal>ì¼ë¡ ì¤ì ë ê²½ì°, ëí´í¸ë¡ Hibernateë íëì í´ëì¤ ëë ê·¸ê²ì ìí¼ í´ëì¤ë¤ì ìí´
+ ì ìë <literal><join></literal>ì ê²ìíëë° inner joinì ì¬ì©íê³ ìë¸í´ëì¤ì ìí´ ì ìë
+ <literal><join></literal>ì ê²ìíëë° outer joinì ì¬ì©í ê²ì´ë¤. ë§ì¼ <literal>select</literal>ë¡
+ ì¤ì í ê²½ì°, Hibernateë ìë¸í´ëì¤ ìì ì ìë <literal><join></literal>ì ëí´ sequential
+ select를 ì¬ì©í ê²ì´ê³ , ê·¸ê²ì í íì´ ìë¸í´ëì¤ì ì¸ì¤í´ì¤ë¥¼ íííë ê²ì¼ë¡ íëª
ëë ê²½ì°ìë§ ëª
ë ¹ì´ ë´ë ¤ì§ ê²ì´ë¤.
+ inner joinë¤ì ì¬ì í í´ëì¤ì ê·¸ê²ì ìí¼í´ëì¤ë¤ì ìí´ ì ìë <literal><join></literal>ì ê²ìíëë°
+ ì¬ì©ë ê²ì´ë¤.
+ </para>
+ </callout>
+ <callout arearefs="join5">
+ <para>
+ <literal>inverse</literal> (ìµì
- ëí´í¸ë <literal>false</literal>):
+ ì´ì© ê°ë¥í ê²½ì°, Hibernateë ì´ ì¡°ì¸ì ìí´ ì ìë íë¡í¼í°ë¤ì ì½ì
ìí¤ê±°ë ì
ë°ì´í¸íë ¤ê³ ìëíì§
+ ìì ê²ì´ë¤.
+ </para>
+ </callout>
+ <callout arearefs="join6">
+ <para>
+ <literal>optional</literal> (ìµì
- ëí´í¸ë <literal>false</literal>):
+ ì´ì© ê°ë¥í ê²½ì°, Hibernateë ì´ ì¡°ì¸ì ìí´ ì ìë íë¡í¼í°ë¤ì´ nullì´ ìë ê²½ì°ìë§ í íì ì½ì
ìí¬ ê²ì´ê³
+ ê·¸ íë¡í¼í°ë¤ì ê²ìíëë° outer joinì íì ì¬ì©í ê²ì´ë¤.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ ì를 ë¤ì´, (모ë íë¡í¼í°ë¤ì ëí´ value íì
ì미를 ì ì§íë©´ì) ê°ì¸ì 주ì ì ë³´ë ë³ëì í
ì´ë¸ì 매íë ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[<class name="Person"
+ table="PERSON">
+
+ <id name="id" column="PERSON_ID">...</id>
+
+ <join table="ADDRESS">
+ <key column="ADDRESS_ID"/>
+ <property name="address"/>
+ <property name="zip"/>
+ <property name="country"/>
+ </join>
+ ...]]></programlisting>
+
+ <para>
+ ì´ í¹ì§ì ì주 리거ì ë°ì´í° 모íë¤ì ëí´ìë§ ì ì©íê³ , ì°ë¦¬ë í´ëì¤ë¤ê³¼ ì ì ì ë ëë©ì¸ 모í ë³´ë¤ ë ì ì í
ì´ë¸ë¤ì
+ ê¶ì¥íë¤. íì§ë§ ë· ë¶ë¶ì ì¤ëª
ëì´ ìë¯ì´, ê·¸ê²ì íëì ê³ì¸µêµ¬ì¡° ë´ì ìë ìì 매í ë°©ëë¤ ì¬ì´ë¥¼ ì ííë ê²ì ì ì©íë¤.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-key">
+ <title>key</title>
+
+ <para>
+ ì°ë¦¬ë ì§ê¸ê¹ì§ ëª ë² ëíë¬ë <literal><key></literal> ìì를 ë³´ìë¤. ê·¸ê²ì ë¶ëª¨ 매í ììê° ìë¡ì´ í
ì´ë¸ì
+ ëí ì¡°ì¸ì ì ìíë ì´ë ê³³ììë ëíëê³ , ê·¸ê²ì ì¡°ì¸ë í
ì´ë¸ì foreign í¤ë¥¼ ì ìíê³ , ê·¸ê²ì ìëì í
ì´ë¸ì
+ íë¼ì´ë¨¸ë¦¬ í¤ë¥¼ 참조íë¤.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="key1" coords="2 50"/>
+ <area id="key2" coords="3 50"/>
+ <area id="key3" coords="4 50"/>
+ <area id="key4" coords="5 50"/>
+ <area id="key5" coords="6 50"/>
+ <area id="key6" coords="7 50"/>
+ </areaspec>
+ <programlisting><![CDATA[<key
+ column="columnname"
+ on-delete="noaction|cascade"
+ property-ref="propertyName"
+ not-null="true|false"
+ update="true|false"
+ unique="true|false"
+/>]]></programlisting>
+
+ <calloutlist>
+ <callout arearefs="key1">
+ <para>
+ <literal>column</literal> (ìµì
):
+ foreign key 컬ë¼ì ì´ë¦. ì´ê²ì ëí ë´í¬ë <literal><column></literal> ìì(ë¤)ì ìí´ ì§ì ë ì ìë¤.
+ </para>
+ </callout>
+ <callout arearefs="key2">
+ <para>
+ <literal>on-delete</literal> (ìµì
- ëí´í¸ë <literal>noaction</literal>):
+ foreign key 컨ì¤í¸ë ì¸í¸ê° ë°ì´í°ë² ì´ì¤ ë 벨ì cascade delete를 ì¬ì©ê°ë¥íëë¡ í ê²ì¸ì§ ì¬ë¶ë¥¼ ì§ì íë¤.
+ </para>
+ </callout>
+ <callout arearefs="key3">
+ <para>
+ <literal>property-ref</literal> (ìµì
):
+ foreign keyê° ìëì í
ì´ë¸ì íë¼ì´ë¨¸ë¦¬ í¤ê° ìë 컬ë¼ë¤ì 참조í¨ì ì§ì íë¤. (리거ì ë°ì´í°ì ì ê³µë¨.)
+ </para>
+ </callout>
+ <callout arearefs="key4">
+ <para>
+ <literal>not-null</literal> (ìµì
):
+ foreign í¤ ì»¬ë¼ë¤ì´ not null ì를 ì§ì íë¤(ì´ê²ì foreign í¤ê° ëí íë¼ì´ë¨¸ë¦¬ í¤ì ë¶ë¶ì¼ ëë§ë¤ í¨ì¶ëë¤).
+ </para>
+ </callout>
+ <callout arearefs="key5">
+ <para>
+ <literal>update</literal> (ìµì
):
+ foreign í¤ê° ê²°ì½ ì
ë°ì´í¸ëì§ ììì¼ í¨ì ì§ì íë¤(ì´ê²ì foreign í¤ê° ëí íë¼ì´ë¨¸ë¦¬ í¤ì ë¶ë¶ì¼ ëë§ë¤
+ í¨ì¶ëë¤).
+ </para>
+ </callout>
+ <callout arearefs="key6">
+ <para>
+ <literal>unique</literal> (ìµì
):
+ foreign í¤ê° ì ì¼ ì»¨ì¤í¸ë ì¸í¸ë¥¼ ê°ì ¸ì¼ í¨ì ì§ì íë¤ (ì´ê²ì foreign í¤ê° ëí íë¼ì´ë¨¸ë¦¬ í¤ì ë¶ë¶ì¼
+ ëë§ë¤ í¨ì¶ëë¤).
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ ì°ë¦¬ë delete í¼í¬ë¨¼ì¤ê° ì¤ìí ìì¤í
ë¤ì ëí´ ê¶ì¥íê³ , 모ë í¤ë¤ì <literal>on-delete="cascade"</literal>ë¡
+ ì ìëê³ , Hibernateë ë§ì <literal>DELETE</literal> 문ì¥ë¤ ëì ì, ë°ì´í°ë² ì´ì¤ ë 벨ì
+ <literal>ON CASCADE DELETE</literal> 컨ì¤í¸ë ì¸í¸ë¥¼ ì¬ì©í ê²ì´ë¤. ì´ í¹ì§ì Hibernateì íµìì ì¸ ë²ì íë
+ ë°ì´í°ì ëí optimistic ì ê¸ ë°©ë를 무ìíë¤ë ì ì ìê³ ìì´ë¼.
+ </para>
+
+ <para>
+ <literal>not-null</literal> ìì±ê³¼ <literal>update</literal> ìì±ë¤ì ë¨ë°©í¥ one to many ì°ê´ê´ê³ë¥¼ 매íí ë
+ ì ì©íë¤. ë§ì¼ ë¹ì ì´ ë¨ë°©í¥ one to many를 nullì´ íì©ëì§ ìë foreign í¤ë¡ 매íí ê²½ì°, ë¹ì ì
+ <literal><key not-null="true"></literal>를 ì¬ì©íì¬ ê·¸ í¤ ì»¬ë¼ì ì ì¸<emphasis>í´ì¼ íë¤</emphasis>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-column" revision="4">
+ <title>column ììì formula ìì</title>
+ <para>
+ column ìì±ì íì©íë ììì 매í ììë ëìì ì¼ë¡ íëì <literal><column></literal> ìë¸ìì를 ìì©í ê²ì´ë¤.
+ ë¹ì·íê² <literal><formula></literal>ë <literal>formula</literal> ìì±ì ëí ëìì´ë¤.
+ </para>
+
+ <programlisting><![CDATA[<column
+ name="column_name"
+ length="N"
+ precision="N"
+ scale="N"
+ not-null="true|false"
+ unique="true|false"
+ unique-key="multicolumn_unique_key_name"
+ index="index_name"
+ sql-type="sql_type_name"
+ check="SQL expression"
+ default="SQL expression"/>]]></programlisting>
+
+ <programlisting><![CDATA[<formula>SQL expression</formula>]]></programlisting>
+
+ <para>
+ <literal>column</literal> ìì±ê³¼ <literal>formula</literal> ìì±ì ì를 ë¤ì´ ì ì¢
ì¡°ì¸ ì¡°ê±´ë¤ì ííí기 ìí´
+ ëì¼í property ëë ì°ê´ê´ê³ 매í ë´ì ê²°í©ë ì ìë¤.
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="homeAddress" class="Address"
+ insert="false" update="false">
+ <column name="person_id" not-null="true" length="10"/>
+ <formula>'MAILING'</formula>
+</many-to-one>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-import">
+ <title>import</title>
+
+ <para>
+ ë¹ì ì ì´í리ì¼ì´ì
ì´ ëì¼í ì´ë¦ì ê°ì§ ë ê°ì ìì í´ëì¤ë¤ì ê°ê³ , ë¹ì ì´ Hibernate ì§ìë¤ ë´ìì ì ì²´ ììì´ê° ë¶ì
+ (í¨í¤ì§)ì´ë¦ì ì§ì íë ê²ì ìíì§ ìëë¤ê³ ê°ì íì. í´ëì¤ë¤ì <literal>auto-import="true"</literal>ì ìì¡´í기
+ ë³´ë¤ ëª
ìì ì¼ë¡ "ìí¬í¸ ë " ê²ì´ë¤. ë¹ì ì ì¬ì§ì´ ëª
ìì ì¼ë¡ 매íëì§ ìë í´ëì¤ë¤ê³¼ ì¸í°íì´ì¤ë¤ì ìí¬í¸ ìí¬ ì(ê°ì ¸ì¤ê¸° í ì)
+ ìë¤.
+ </para>
+
+ <programlisting><![CDATA[<import class="java.lang.Object" rename="Universe"/>]]></programlisting>
+
+ <programlistingco>
+ <areaspec>
+ <area id="import1" coords="2 40"/>
+ <area id="import2" coords="3 40"/>
+ </areaspec>
+ <programlisting><![CDATA[<import
+ class="ClassName"
+ rename="ShortName"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="import1">
+ <para>
+ <literal>class</literal>: ììì Java í´ëì¤ì ì ì²´ ììì´ê° ë¶ì í´ëì¤ ì´ë¦.
+ </para>
+ </callout>
+ <callout arearefs="import2">
+ <para>
+ <literal>rename</literal> (ìµì
- ëí´í¸ë ììì´ê° ë¶ì§ ìì í´ëì¤ ì´ë¦):
+ ì§ì ì¸ì´ ë´ìì ì¬ì©ë ì´ë¦.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ </sect2>
+
+ <sect2 id="mapping-types-anymapping" revision="2">
+ <title>any</title>
+
+ <para>
+ íë ì´ìì íë¡í¼í° 매í íì
ì´ ì¡´ì¬íë¤. <literal><any></literal> 매í ììë ì¬ë¬ í
ì´ë¸ë¤ë¡ë¶í° í´ëì¤ë¤ì
+ ëí íëì ë¤íì± ì°ê´ê´ê³ë¥¼ ì ìíë¤. ì´ ë§¤í íì
ì ì¸ì ë íë ì´ìì 컬ë¼ì íìë¡ íë¤. 첫 ë²ì§¸ 컬ë¼ì ì°ê´ë ìí°í°ì
+ íì
ì ë³´ê´íë¤. ëë¨¸ì§ ì»¬ë¼ë¤ì ìë³ì를 ë³´ê´íë¤. ì´ë° ì¢
ë¥ì ì°ê´ê´ê³ë¤ì ëí´ foreign key 컨ì¤í¸ë ì¸í¸ë¥¼ ì§ì íë ê²ì´
+ ë¶ê°ë¥í´ì, ì´ê²ì (ë¤íì±) ì°ê´ê´ê³ë¤ì 매ííë íµìì ì¸ ë°©ë²ì¼ë¡ì ê°ì¥ íì¤í ìë¨ì´ ìëë¤. ë¹ì ì ë§¤ì° í¹ë³í ê²½ì°ë¤
+ (ì를 ë¤ì´ ê°ì¬ ë¡ê·¸ë¤, ì¬ì©ì ì¸ì
ë°ì´í° ë±)ììë§ ì´ê²ì ì¬ì©í´ì¼ íë¤.
+ </para>
+
+ <para>
+ <literal>meta-type</literal> ìì±ì ì´í리ì¼ì´ì
ì¼ë¡ íì¬ê¸ ë°ì´í°ë² ì´ì¤ ì»¬ë¼ ê°ë¤ì <literal>id-type</literal>ì
+ ìí´ ì§ì ë íì
ì ìë³ì íë¡í¼í°ë¤ì ê°ì§ ìì í´ëì¤ë¤ë¡ 매íìí¤ë ë§ì¶¤í íì
ì ì§ì íëë¡ íë¤. ë¹ì ì meta-typeì
+ ê°ë¤ë¡ë¶í° í´ëì¤ ì´ë¦ë¤ë¡ì 매íì ì§ì í´ì¼ íë¤.
+ </para>
+
+ <programlisting><![CDATA[<any name="being" id-type="long" meta-type="string">
+ <meta-value value="TBL_ANIMAL" class="Animal"/>
+ <meta-value value="TBL_HUMAN" class="Human"/>
+ <meta-value value="TBL_ALIEN" class="Alien"/>
+ <column name="table_name"/>
+ <column name="id"/>
+</any>]]></programlisting>
+
+ <programlistingco>
+ <areaspec>
+ <area id="any1" coords="2 50"/>
+ <area id="any2" coords="3 50"/>
+ <area id="any3" coords="4 50"/>
+ <area id="any4" coords="5 50"/>
+ <area id="any5" coords="6 50"/>
+ <area id="any6" coords="7 50"/>
+ </areaspec>
+ <programlisting><![CDATA[<any
+ name="propertyName"
+ id-type="idtypename"
+ meta-type="metatypename"
+ cascade="cascade_style"
+ access="field|property|ClassName"
+ optimistic-lock="true|false"
+>
+ <meta-value ... />
+ <meta-value ... />
+ .....
+ <column .... />
+ <column .... />
+ .....
+</any>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="any1">
+ <para>
+ <literal>name</literal>: íë¡í¼í° ì´ë¦.
+ </para>
+ </callout>
+ <callout arearefs="any2">
+ <para>
+ <literal>id-type</literal>: ìë³ì íì
.
+ </para>
+ </callout>
+ <callout arearefs="any3">
+ <para>
+ <literal>meta-type</literal> (ìµì
- ëí´í¸ë <literal>string</literal>):
+ discriminator 매íì íì©ëë ììì íì
.
+ </para>
+ </callout>
+ <callout arearefs="any4">
+ <para>
+ <literal>cascade</literal> (optional- defaults to <literal>none</literal>):
+ cascade ì¤íì¼.
+ </para>
+ </callout>
+ <callout arearefs="any5">
+ <para>
+ <literal>access</literal> (ìµì
- ëí´í¸ë <literal>property</literal>):
+ Hibernateê° íë¡í¼í° ê°ì ì ê·¼íëë° ì¬ì©í ë°©ë.
+ </para>
+ </callout>
+ <callout arearefs="any6">
+ <para>
+ <literal>optimistic-lock</literal> (ìµì
- ëí´í¸ë <literal>true</literal>):
+ ì´ íë¡í¼í°ì ëí ì
ë°ì´í¸ë¤ì´ optimistic ì ê¸ íëì íìë¡ íëì§ ì¬ë¶ë¥¼ ì§ì íë¤. ë¬ë¦¬ ë§í´,
+ ì´ íë¡í¼í°ê° dirtyì¼ ê²½ì°ì ë²ì ì¦ê°ê° ë°ìí ê²ì¸ì§ ì¬ë¶ë¥¼ ì ìíë¤.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="mapping-types">
+ <title>Hibernate íì
ë¤</title>
+
+ <sect2 id="mapping-types-entitiesvalues" revision="1">
+ <title>ìí°í°ë¤ê³¼ ê°ë¤</title>
+
+ <para>
+ ìì ìë¹ì¤ì ê´í ì¬ë¬ Java ì¸ì´-ë 벨ì ê°ì²´ë¤ì ì´í´í기 ìí´, ì°ë¦¬ë ê·¸ê²ë¤ì ë¤ì ë ê°ì 그룹ë¤ë¡ ë¶ë¥í íìê° ìë¤:
+ </para>
+
+ <para>
+ <emphasis>entity</emphasis>ë ìí°í°ì ëí 참조ë¤ì ë³´ê´íë ììì ë¤ë¥¸ ê°ì²´ë¤ê³¼ë ë
립ì ì¼ë¡ ì¡´ì¬íë¤. 참조ëì§ ìì
+ ê°ì²´ê° ì°ë 기 ìì§ëë íµìì ìë° ëª¨íê³¼ ì´ê²ì ëì¡°ì ì´ë¤. (ì ì¥ë¤ê³¼ ìì ë¤ì´ ë¶ëª¨ ìí°í°ë¡ë¶í° ê·¸ê²ì ììì¼ë¡ì ì¼ì¤ì¼ì´ë
+ ëë ê²½ì°ë¥¼ ì ì¸íë©´) ìí°í°ë¤ì ëª
ìì ì¼ë¡ ì ì¥ëê³ ìì ëì´ì¼ íë¤. ì´ê²ì ëë¬ ê°ë¥ì±(reachablity)ì ìí ê°ì²´ ììì±ì
+ ODMG 모íê³¼ë ë¤ë¥´ë¤ - ê·¸ë¦¬ê³ ì´í리ì¼ì´ì
ê°ì²´ë¤ì´ ëí ìì¤í
ë¤ìì ëê° ì´ë»ê² ì¬ì©ëëê°ì í¨ì¬ ë ê°ê¹ê² ëìíë¤.
+ ìí°í°ë¤ì ìí 참조ì ê³µì 참조ë¤ì ì§ìíë¤. ê·¸ê²ë¤ ëí ë²ì í ë ì ìë¤.
+ </para>
+
+ <para>
+ ìí°í°ì ìì ìíë ë¤ë¥¸ ìí°í°ë¤ì ëí 참조ë¤ê³¼ <emphasis>value</emphasis> íì
ë¤ë¡ 구ì±ëë¤. ê°ë¤ì ìì íì
ë¤,
+ ì½ë ì
ë¤(íëì ì½ë ì
ë´ë¶ì ìì§ ìë ê²ë¤), ì»´í¬ëí¸ë¤, ê·¸ë¦¬ê³ ì´ë¤ ë¶ë³ì ê°ì²´ë¤ì´ë¤. entitiesìë ë¬ë¦¬, (í¹ë³í
+ ì½ë ì
ë¤ê³¼ ì»´í¬ëí¸ë¤ìì) ê°ë¤ì ëë¬ê°ë¥ì±(reachability)ì ìí´ ììí <emphasis>ëê³ </emphasis> ìì ëë¤.
+ value ê°ì²´ë¤(ê³¼ ìì íì
ë¤)ì´ ê·¸ê²ë¤ì í¬í¨íë ìí°í°ì ë°ë¼ ììí ëê³ ìì ëë¯ë¡, ê·¸ê²ë¤ì ë
립ì ì¼ë¡ ë²ì í ëì§
+ ìëë¤. ê°ë¤ì ë
립ì ì¸ ìí°í°ë¥¼ ê°ì§ ììì, ê·¸ê²ë¤ì ë ê°ì ìí°í°ë¤ì´ë ì½ë ì
ë¤ì ìí´ ê³µì ë ì ìë¤.
+ </para>
+
+ <para>
+ ì§ê¸ê¹ì§ ì°ë¦¬ë ìí°í°ë¤ì 참조í기 ìí´ "ìì í´ëì¤"를 ì¬ì©í´ ìë¤. ì°ë¦¬ë ê·¸ê²ì ê³ì ì¬ì©í ê²ì´ë¤. íì§ë§ ì격í ë§í´,
+ ìì ìí를 ê°ì§ 모ë ì¬ì©ì ì ì í´ëì¤ë¤ì ìí°í°ë¤ì´ ìëë¤. <emphasis>ì»´í¬ëí¸</emphasis>ë value ì미를 ê°ì§
+ ì¬ì©ì ì ì í´ëì¤ì´ë¤. <literal>java.lang.String</literal> íì
ì ìë° íë¡í¼í°ë ëí value ì미를 ê°ëë¤.
+ ì´ ì ìê° ì£¼ì´ì§ë©´, ì°ë¦¬ë JDKì ìí´ ì ê³µë 모ë íì
ë¤(í´ëì¤ë¤)ì´ ìë°ìì value íì
ì미를 ê°ê³ , ë°ë©´ì ì¬ì©ì ì ì
+ íì
ë¤ì ìí°í° ëë type ì미ë¡ì 매íëë¤ê³ ë§í ì ìë¤. ì´ íë¨ì ì´í리ì¼ì´ì
ê°ë°ììê² ë¬ë ¤ ìë¤. ëë©ì¸ 모íìì
+ ìí°í° í´ëì¤ì ëí ì¢ì íí¸ë ê·¸ í´ëì¤ì íëì ì¸ì¤í´ì¤ì ëí ê³µì ë 참조ë¤ì¸ ë°ë©´ì, compositionì´ë aggregationì
+ ëê° value íì
ì¼ë¡ ë³íëë¤.
+ </para>
+
+ <para>
+ ì°ë¦¬ë 문ì를 íµí´ ë ê°ë
ë¤ì ë¤ì ê³ ì°°í ê²ì´ë¤.
+ </para>
+
+ <para>
+ ëì ì ì Java type ìì¤í
(ê³¼ ìí°í°ë¤ ë° value íì
ë¤ì ëí ê°ë°ìì ì ì)를 SQL/ë°ì´í°ë² ì´ì¤ type íì
ì¼ë¡ 매ííë
+ ê²ì´ë¤. ë ìì¤í
ë¤ ì¬ì´ì ë¤ë¦¬ë Hibernateì ìí´ ì ê³µëë¤: ìí°í°ë¤ì ê²½ì° ì°ë¦¬ë <literal><class></literal>,
+ <literal><subclass></literal> ë±ì ì¬ì©íë¤.value íì
ë¤ì ê²½ì° ì°ë¦¬ë ëê°<literal>type</literal> ìì±ì ê°ì§
+ <literal><property></literal>, <literal><component></literal> ë±ì ì¬ì©íë¤. ì´ ìì±ì ê°ì
+ Hibernate <emphasis>매í íì
</emphasis>ì ì´ë¦ì´ë¤. Hibernateë (íì¤ JDK value íì
ë¤ì ëí´) ë§ì 매íë¤ì
+ ì ê³µíë¤. ëì¤ì ë³´ê² ëë¯ì´, ë¹ì ì ë¹ì ìì ì 매í íì
ë¤ì ìì±í ì ìê³ ë§ì°¬ê°ì§ë¡ ë¹ì ì ë§ì¶¤í ë³í ë°©ëë¤ì 구íí ì
+ ìë¤.
+ </para>
+
+ <para>
+ ì½ë ì
ë¤ì ì ì¸í 모ë 미리 ë¹ëë Hibernate íì
ë¤ì null ì미를 ì§ìíë¤.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-types-basictypes" revision="3">
+ <title>기본 value íì
ë¤</title>
+
+ <para>
+ 미리-ë§ë¤ì´ì§ <emphasis>기본 매í íì
ë¤</emphasis>ì ëëµ ë¤ìê³¼ ê°ì´ ì¹´í
ê³ ë¦¬ë¡ ë¶ë¥ëë¤
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>integer, long, short, float, double, character, byte,
+ boolean, yes_no, true_false</literal></term>
+ <listitem>
+ <para>
+ ìë° ììíì
ë¤ì´ë wrapper í´ëì¤ë¤ë¡ë¶í° ì ì í(벤ë-ì§ì ì ì¸) SQL ì»¬ë¼ íì
ë¤ë¡ì íì
매í.
+ <literal>boolean, yes_no</literal>ì <literal>true_false</literal>ë
+ Java <literal>boolean</literal>ì´ë <literal>java.lang.Boolean</literal>ì ëí
+ 모ë ëì²´ì ì¸ ì¸ì½ë©ë¤ì´ë¤.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>string</literal></term>
+ <listitem>
+ <para>
+ <literal>java.lang.String</literal>ì¼ë¡ë¶í° <literal>VARCHAR</literal>
+ (ëë Oracle <literal>VARCHAR2</literal>)ë¡ì íì
매í.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>date, time, timestamp</literal></term>
+ <listitem>
+ <para>
+ <literal>java.util.Date</literal>ì ê·¸ê²ì ìë¸í´ëì¤ë¡ë¶í° SQL íì
ë¤ì¸
+ <literal>DATE</literal>, <literal>TIME</literal>, <literal>TIMESTAMP</literal>
+ (ëë ë±ê°ë¬¼)ë¡ì íì
매íë¤.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>calendar, calendar_date</literal></term>
+ <listitem>
+ <para>
+ <literal>java.util.Calendar</literal>ë¡ë¶í° SQL íì
ë¤ì¸
+ <literal>TIMESTAMP</literal>, <literal>DATE</literal> (ëë ë±ê°ë¬¼)ë¡ì íì
매íë¤.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>big_decimal, big_integer</literal></term>
+ <listitem>
+ <para>
+ <literal>java.math.BigDecimal</literal>ê³¼ <literal>java.math.BigInteger</literal>ë¡ë¶í°
+ <literal>NUMERIC</literal> (ëë Oracle <literal>NUMBER</literal>)ë¡ì íì
매íë¤.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>locale, timezone, currency</literal></term>
+ <listitem>
+ <para>
+ <literal>java.util.Locale</literal>, <literal>java.util.TimeZone</literal>, 그리ê³
+ <literal>java.util.Currency</literal>ë¡ë¶í° <literal>VARCHAR</literal>(ëë Oracle
+ <literal>VARCHAR2</literal>)ë¡ì íì
매í. <literal>Locale</literal>ê³¼ <literal>Currency</literal>ì
+ ì¸ì¤í´ì¤ë¤ì ê·¸ê²ë¤ì ISO ì½ëë¤ë¡ 매íëë¤. <literal>TimeZone</literal>ì ì¸ì¤í´ì¤ë¤ì ê·¸ê²ë¤ì
+ <literal>ID</literal>ë¡ ë§¤íëë¤.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>class</literal></term>
+ <listitem>
+ <para>
+ <literal>java.lang.Class</literal>ë¡ë¶í° <literal>VARCHAR</literal> (ëë Oracle
+ <literal>VARCHAR2</literal>)ë¡ì íì
매í. <literal>Class</literal>ë ê·¸ê²ì ì ì²´
+ ììì´ê° ë¶ì ì´ë¦ì¼ë¡ 매íëë¤.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>binary</literal></term>
+ <listitem>
+ <para>
+ byte ë°°ì´ë¤ì ì ì í SQL binary íì
ì¼ë¡ 매íìí¨ë¤.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>text</literal></term>
+ <listitem>
+ <para>
+ long Java 문ìì´ì SQL <literal>CLOB</literal> ëë <literal>TEXT</literal> íì
ì¼ë¡
+ 매íìí¨ë¤
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>serializable</literal></term>
+ <listitem>
+ <para>
+ serializable Java íì
ë¤ì ì ì í SQL binary íì
ì¼ë¡ 매íìí¨ë¤. ë¹ì ì ëí ëí´í¸ë¡ 기본 íì
ì´
+ ìë serializable ìë° í´ëì¤ ëë ì¸í°íì´ì¤ì ì´ë¦ì ê°ì§ Hibernate íì
<literal>serializable</literal>ì
+ ëíë¼ ìë ìë¤.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>clob, blob</literal></term>
+ <listitem>
+ <para>
+ <literal>java.sql.Clob</literal>ì <literal>java.sql.Blob</literal> JDBC í´ëì¤ë¤ì ëí íì
매íë¤.
+ ì´ë¤ íì
ë¤ì ëªëª ì´í리ì¼ì´ì
ë¤ììë ë¶í¸íë¤. ìëíë©´ blob ëë clob ê°ì²´ë í¸ëìì
ì¸ë¶ìì ì¬ì¬ì©ë ì ì기
+ ë문ì´ë¤.(ê²ë¤ê° ëë¼ì´ë² ì§ìì´ ë¹ì¼ê´ì ì´ê³ íì¹ëì´ì¼ íë¤)
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <literal>imm_date, imm_time, imm_timestamp, imm_calendar, imm_calendar_date,
+ imm_serializable, imm_binary</literal>
+ </term>
+ <listitem>
+ <para>
+ ëê° ê°ë³ì ì¸ Java íì
ë¤ë¡ ê°ì£¼ëë ê²ì ëí íì
매íë¤. ì¬ê¸°ì Hibernateë ë¶ë³ì ì¸ Java íì
ë¤ì ëí´ìë§ ì ì í
+ ì´ë¤ ìµì í를 ííê³ , ì´í리ì¼ì´ì
ê·¸ ê°ì²´ë¥¼ ë³í ì ìë ê²ì¼ë¡ ì·¨ê¸íë¤. ì를 ë¤ì´, ë¹ì ì <literal>imm_timestamp</literal>ë¡ì
+ 매íë ì¸ì¤í´ì¤ì ëí´ <literal>Date.setTime()</literal>ì í¸ì¶íì§ ìì ê²ì´ë¤. íë¡í¼í°ì ê°ì ë³ê²½ìí¤ê³ ,
+ ê·¸ ë³ê²½ì ììí ìí¤ê¸° ìí´ì, ì´í리ì¼ì´ì
ì íëì ìë¡ì´ (ëì¼íì§ ìì) ê°ì²´ë¥¼ ê·¸ íë¡í¼í°ì í ë¹í´ì¼ íë¤.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ </para>
+
+ <para>
+ ìí¸ë¦¬ë¤ê³¼ ì½ë ì
ë¤ì ì ì¼ ìë³ìë¤ì <literal>binary</literal>, <literal>blob</literal> ê·¸ë¦¬ê³ <literal>clob</literal>를
+ ì ì¸í 기본 íì
ì¤ ì´ë ê²ì¼ ì ìë¤. (Composite ìë³ìë¤ì´ ëí íì©ëë¤. ìë를 ë³´ë¼.)
+ </para>
+
+ <para>
+ 기본 value íì
ë¤ì <literal>org.hibernate.Hibernate</literal>ì ì ìëì´ ìë ëìíë
+ <literal>Type</literal> ììë¤ì ê°ëë¤. ì를 ë¤ì´, <literal>Hibernate.STRING</literal>ì
+ <literal>string</literal> íì
ì íííë¤.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-types-custom" revision="2">
+ <title>ë§ì¶¤í value íì
ë¤</title>
+
+ <para>
+ ê°ë°ìë¤ì´ ê·¸ë¤ ìì ë¤ì value íì
ë¤ì ìì±ìí¤ë ê²ì´ ìëì ì¼ë¡ ì½ë¤. ì를 ë¤ì´, ë¹ì ì
+ <literal>java.lang.BigInteger</literal> íì
ì íë¡í¼í°ë¤ì <literal>VARCHAR</literal> 컬ë¼ë¤ë¡
+ ììí ìí¤ê³ ì ìí ì ìë¤. Hibernateë ì´ê²ì ìí 미리 ë§ë¤ì´ì§ íì
ì ì ê³µíì§ ìëë¤. ê·¸ë¬ë ë§ì¶¤í íì
ë¤ì
+ íë¡í¼í°(ëë ì½ë ì
ìì)를 íëì í
ì´ë¸ 컬ë¼ì¼ë¡ì 매ííë ê²ì ì ì½ëì§ ìëë¤. ë°ë¼ì ì를 ë¤ì´, ë¹ì ì
+ <literal>FIRST_NAME</literal>, <literal>INITIAL</literal>, <literal>SURNAME</literal> 컬ë¼ë¤ë¡
+ ììí ëë <literal>java.lang.String</literal> íì
ì ìë° íë¡í¼í°<literal>getName()</literal>/
+ <literal>setName()</literal>를 ê°ì§ ì ìë¤.
+ </para>
+
+ <para>
+ ë§ì¶¤í íì
ì 구ííë ¤ë©´, <literal>org.hibernate.UserType</literal> ëë <literal>org.hibernate.CompositeUserType</literal>ì
+ 구ííê³ ê·¸ íì
ì ì ì²´ ììì´ê° ë¶ì í´ëì¤ëª
ì ì¬ì©íì¬ íë¡í¼í°ë¤ì ì ì¸íë¼. ê°ë¥í ì¢
ë¥ì ê²ë¤ì ë³´ë ¤ë©´
+ <literal>org.hibernate.test.DoubleStringType</literal>ì ì²´í¬íë¼.
+ </para>
+
+ <programlisting><![CDATA[<property name="twoStrings" type="org.hibernate.test.DoubleStringType">
+ <column name="first_string"/>
+ <column name="second_string"/>
+</property>]]></programlisting>
+
+ <para>
+ íëì íë¡í¼í°ë¥¼ ì¬ë¬ ê°ì 컬ë¼ë¤ë¡ 매íìí¤ë <literal><column></literal> íê·¸ì ì¬ì©ì 주목íë¼.
+ </para>
+
+ <para>
+ <literal>CompositeUserType</literal>, <literal>EnhancedUserType</literal>,
+ <literal>UserCollectionType</literal>, ê·¸ë¦¬ê³ <literal>UserVersionType</literal> ì¸í°íì´ì¤ë¤ì
+ ë ë§ì í¹íë ì¬ì©ë¤ì ìí ì§ìì ì ê³µíë¤.
+ </para>
+
+ <para>
+ ë¹ì ì 매í íì¼ ìì <literal>UserType</literal>ì ëí íë¼ë¯¸í°ë¤ì ì ê³µí ìë ìë¤. ì´ê²ì íí기 ìí´,
+ ë¹ì ì <literal>UserType</literal>ì <literal>org.hibernate.usertype.ParameterizedType</literal>
+ ì¸í°íì´ì¤ë¥¼ 구íí´ì¼ íë¤. ë¹ì ì ë§ì¶¤í íì
ì íë¼ë¯¸í°ë¤ì ì ê³µí기 ìí´, ë¹ì ì ë¹ì ì 매í íì¼ë¤ ìì
+ <literal><type></literal> ìì를 ì¬ì©í ì ìë¤.
+ </para>
+
+ <programlisting><![CDATA[<property name="priority">
+ <type name="com.mycompany.usertypes.DefaultValueIntegerType">
+ <param name="default">0</param>
+ </type>
+</property>]]></programlisting>
+
+ <para>
+ <literal>UserType</literal>ì ì´ì ê·¸ê²ì ì ë¬ë <literal>Properties</literal> ê°ì²´ë¡ë¶í°
+ <literal>default</literal>ë¡ ëª
ëª
ë íë¼ë¯¸í°ì ëí ê°ì ê²ìí ì ìë¤.
+ </para>
+
+ <para>
+ ë§ì¼ ë¹ì ì´ ë§¤ì° ì주 ì´ë¤ UserTypeì ì¬ì©í ê²½ì°, ê·¸ê²ì ê·¸ê²ì ëí ë 짧ì ì´ë¦ì ì ìíë ê²ì´ ì ì©í ì ìë¤.
+ <literal><typedef></literal> ìì를 ì¬ì©íì¬ ì´ê²ì íí ì ìë¤. Typedefë¤ì ì´ë¦ì ë§ì¶¤í íì
ì
+ í ë¹íê³ , ëí ë§ì¼ ê·¸ íì
ì´ íë¼ë¯¸í°í ë ê²½ì°ì ëí´í¸ íë¼ë¯¸í° ê°ë¤ì 리ì¤í¸ë¥¼ í¬í¨í ìë ìë¤.
+ </para>
+
+ <programlisting><![CDATA[<typedef class="com.mycompany.usertypes.DefaultValueIntegerType" name="default_zero">
+ <param name="default">0</param>
+</typedef>]]></programlisting>
+
+ <programlisting><![CDATA[<property name="priority" type="default_zero"/>]]></programlisting>
+
+ <para>
+ property 매í ìì type íë¼ë¯¸í°ë¤ì ì¬ì©í¨ì¼ë¡ì¨ ê²½ì°ì ë§ê² typedef ë´ì ì ê³µë íë¼ë¯¸í°ë¤ì ì¤ë²ë¼ì´ë ìí¤ë ê²ì´
+ ê°ë¥íë¤.
+ </para>
+
+ <para>
+ ë¹ë¡ Hibernateì íë¶í ë²ìì 미리 ë§ë¤ì´ì§ íì
ë¤ê³¼ ì»´í¬ëí¸ë¤ì ëí ì§ìì´ ë¹ì ì´ ê°ë ë§ì¶¤í íì
ì ì¬ì©í <emphasis>íìê°</emphasis>
+ ê±°ì ìì ê²ìì ì미í ì§ë¼ë, ê·¸ë¼ìë ë¶êµ¬íê³ ê·¸ê²ì ë¹ì ì ì´í리ì¼ì´ì
ìì ì주 ë°ìíë (ìí°í°ê° ìë) í´ëì¤ë¤ì ëí´
+ ë§ì¶¤í íì
ë¤ì ì¬ì©íë ì¢ì íìì¼ë¡ ê°ì£¼ëë¤. ì를 ë¤ì´ <literal>MonetaryAmount</literal> í´ëì¤ë ë¹ë¡ ê·¸ê²ì´
+ ì»´í¬ëí¸ë¡ì ì½ê² 매íë ì ììì§ë¼ë, <literal>CompositeUserType</literal>ì ëí ì¢ì íë³´ì´ë¤. ì´ê²ì ëí
+ íëì ë기ë ì¶ìíì´ë¤. ë§ì¶¤í íì
ì¼ë¡, ë¹ì ì 매í 문ìë¤ì íí ê°ë¤ì íííë ë¹ì ì ë°©ë²ìì ê°ë¥í ë³ê²½ë¤ì ëí´ ì¥ì°¨
+ ê²ì¦ë ê²ì´ë¤.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="mapping-entityname">
+ <title>íëì í´ëì¤ë¥¼ í ë² ì´ì 매íí기</title>
+ <para>
+ íëì í¹ì í ìì í´ëì¤ì ëí´ íë ì´ìì 매íì ì ê³µíë ê²ì´ ê°ë¥íë¤. ì´ ê²½ì°ì ë¹ì ì ë ê°ì 매íë ìí°í°ë¤ì
+ ì¸ì¤í´ì¤ë¤ ì¬ì´ë¥¼ ëª
ííê² í기 ìí´ <emphasis>íëì ìí°í° ì´ë¦</emphasis>ì ì§ì í´ì¼ íë¤. (ëí´í¸ë¡, ìí°í°
+ ì´ë¦ì í´ëì¤ ì´ë¦ê³¼ ëì¼í ê²ì´ë¤.) Hibernateë ìì ê°ì²´ë¤ì ëí´ ìì
í ë, ì§ìë¤ì ìì±í ë, ëë ëª
ëª
ë ìí°í°ì
+ ëí ì°ê´ë¤ì 매íí ë ë¹ì ì¼ë¡ íì¬ê¸ ìí°í° ì´ë¦ì ì§ì íëë¡ íë¤.
+ </para>
+
+ <programlisting><![CDATA[<class name="Contract" table="Contracts"
+ entity-name="CurrentContract">
+ ...
+ <set name="history" inverse="true"
+ order-by="effectiveEndDate desc">
+ <key column="currentContractId"/>
+ <one-to-many entity-name="HistoricalContract"/>
+ </set>
+</class>
+
+<class name="Contract" table="ContractHistory"
+ entity-name="HistoricalContract">
+ ...
+ <many-to-one name="currentContract"
+ column="currentContractId"
+ entity-name="CurrentContract"/>
+</class>]]></programlisting>
+
+ <para>
+ ì°ê´ë¤ì ì´ì <literal>class</literal> ëì ì <literal>entity-name</literal>ì ì¬ì©íì¬ ì´ë»ê² ì§ì ëëì§ë¥¼
+ 주목íë¼.
+ </para>
+
+ </sect1>
+
+ <sect1 id="mapping-quotedidentifiers">
+ <title>SQL ì¸ì©ë¶í¸ íìë ìë³ìë¤</title>
+ <para>
+ ë¹ì ì 매í 문ì ë´ìì í
ì´ë¸ ëë ì»¬ë¼ ì´ë¦ì ìì¸ì©ê¸°í¸(`)ë¤ ìì ë£ì´ì ìì±ë SQLìì ìë³ì를 ì¸ì©ë¶í¸ ì²ë¦¬íëë¡
+ Hibernateìê² ê°ì í ìë ìë¤. Hibernateë SQL <literal>Dialect</literal>ì ëí´ ì íí ì¸ì© ì¤íì¼ì ì¬ì©í
+ ê²ì´ë¤(ëê° ì´ì¤ ì¸ì©ë¶í¸ ì´ì§ë§, SQL Serverì ê²½ì°ìë 모ë ê´í¸ë¤ì´ê³ MySQLì ê²½ì°ìë ìì¸ì©ë¶í¸(`)).
+ </para>
+
+ <programlisting><![CDATA[<class name="LineItem" table="`Line Item`">
+ <id name="id" column="`Item Id`"/><generator class="assigned"/></id>
+ <property name="itemNumber" column="`Item #`"/>
+ ...
+</class>]]></programlisting>
+
+ </sect1>
+
+
+ <sect1 id="mapping-alternatives">
+ <title>Metadata ëìë¤</title>
+
+ <para>
+ XMLì 모ë ì¬ëë¤ì ìí ê²ì´ ìëì§ë§, Hibernateìì O/R 매í ë©íë°ì´í°ë¥¼ ì ìíë ëªëª ëìì ì¸ ë°©ë²ë¤ì´ ì¡´ì¬íë¤.
+ </para>
+
+ <sect2 id="mapping-xdoclet">
+ <title>XDoclet ë§í¬ì
ì¬ì©í기</title>
+
+ <para>
+ ë§ì Hibernate ì¬ì©ìë¤ì XDoclet <literal>@hibernate.tags</literal>를 ì¬ì©íì¬ ìì¤ ì½ë ìì
+ ì§ì 매í ì 보를 ì½ì
ìí¤ë ê²ì ì í¸íë¤. ì°ë¦¬ë ì´ ë¬¸ììì ì´ ì ê·¼ë²ì ë¤ë£¨ì§ ìì ê²ì´ë¤. ìëíë©´ ê·¸ê²ì
+ ì격íê²ë XDocletì ë¶ë¶ì¼ë¡ ê°ì£¼ë기 ë문ì´ë¤. íì§ë§ ì°ë¦¬ë XDoclet 매íë¤ì ê°ì§ <literal>Cat</literal>
+ í´ëì¤ì ê´í ë¤ì ìì 를 í¬í¨íë¤.
+ </para>
+
+ <programlisting><![CDATA[package eg;
+import java.util.Set;
+import java.util.Date;
+
+/**
+ * @hibernate.class
+ * table="CATS"
+ */
+public class Cat {
+ private Long id; // identifier
+ private Date birthdate;
+ private Cat mother;
+ private Set kittens
+ private Color color;
+ private char sex;
+ private float weight;
+
+ /*
+ * @hibernate.id
+ * generator-class="native"
+ * column="CAT_ID"
+ */
+ public Long getId() {
+ return id;
+ }
+ private void setId(Long id) {
+ this.id=id;
+ }
+
+ /**
+ * @hibernate.many-to-one
+ * column="PARENT_ID"
+ */
+ public Cat getMother() {
+ return mother;
+ }
+ void setMother(Cat mother) {
+ this.mother = mother;
+ }
+
+ /**
+ * @hibernate.property
+ * column="BIRTH_DATE"
+ */
+ public Date getBirthdate() {
+ return birthdate;
+ }
+ void setBirthdate(Date date) {
+ birthdate = date;
+ }
+ /**
+ * @hibernate.property
+ * column="WEIGHT"
+ */
+ public float getWeight() {
+ return weight;
+ }
+ void setWeight(float weight) {
+ this.weight = weight;
+ }
+
+ /**
+ * @hibernate.property
+ * column="COLOR"
+ * not-null="true"
+ */
+ public Color getColor() {
+ return color;
+ }
+ void setColor(Color color) {
+ this.color = color;
+ }
+ /**
+ * @hibernate.set
+ * inverse="true"
+ * order-by="BIRTH_DATE"
+ * @hibernate.collection-key
+ * column="PARENT_ID"
+ * @hibernate.collection-one-to-many
+ */
+ public Set getKittens() {
+ return kittens;
+ }
+ void setKittens(Set kittens) {
+ this.kittens = kittens;
+ }
+ // addKitten not needed by Hibernate
+ public void addKitten(Cat kitten) {
+ kittens.add(kitten);
+ }
+
+ /**
+ * @hibernate.property
+ * column="SEX"
+ * not-null="true"
+ * update="false"
+ */
+ public char getSex() {
+ return sex;
+ }
+ void setSex(char sex) {
+ this.sex=sex;
+ }
+}]]></programlisting>
+
+ <para>
+ XDocletê³¼ ibernateì ê´í ì¶ê° ìì ë¤ì Hibernate ì¹ ì¬ì´í¸ë¥¼ ë³´ë¼.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-annotations" revision="2">
+ <title>JDK 5.0 Annotations ì¬ì©í기</title>
+
+ <para>
+ JDK 5.0ì ì¸ì´ ë 벨ìì XDoclet-ì¤íì¼ì 주ìë¤, type-safeì ì»´íì¼ ì ì²´í¹ì ëì
íë¤. ì´ ë©ì»¤ëì¦ì XDoclet
+ 주ìë¤ ë³´ë¤ ë ê°ë ¥íë©° ë구ë¤ê³¼ IDEë¤ì ìí´ ë ì¢ê² ì§ìëë¤. ì를 ë¤ì´ IntelliJ IDEAë JDK 5.0 주ìë¤ì ëí
+ ìë-ìì± ê¸°ë¥ê³¼ 구문 ê°ì¡°ë¥¼ ì§ìíë¤. EJB ëª
ì¸ìì ìë¡ì´ ê°ì í(JSR-220)ì ìí°í° ë¹ì¦ì ëí íë¼ì´ë¨¸ë¦¬ ë©íë°ì´í°
+ ë©ì»¤ëì¦ì¼ë¡ì JDK 5.0 Annotationsì ì¬ì©íë¤. Hibernate3ë JSR-220(ìì API)ì <literal>EntityManager</literal>를
+ 구ííê³ , 매í ë©íë°ì´í°ì ëí ì§ìì ë³ëì ë´ë ¤ë°ê¸°ë¡ì <emphasis>Hibernate Annotations</emphasis> í¨í¤ì§ë¥¼
+ íµí´ ì´ì© ê°ë¥íë¤. EJB3 (JSR-220)ê³¼ Hibernate3 metadata ììê° ì§ìëë¤.
+ </para>
+
+ <para>
+ ë¤ìì EJB ìí°í° ë¹ì¼ë¡ì 주ìì´ ë¶ì POJO í´ëì¤ì ê´í ìì ì´ë¤:
+ </para>
+
+ <programlisting><![CDATA[@Entity(access = AccessType.FIELD)
+public class Customer implements Serializable {
+
+ @Id;
+ Long id;
+
+ String firstName;
+ String lastName;
+ Date birthday;
+
+ @Transient
+ Integer age;
+
+ @Embedded
+ private Address homeAddress;
+
+ @OneToMany(cascade=CascadeType.ALL)
+ @JoinColumn(name="CUSTOMER_ID")
+ Set<Order> orders;
+
+ // Getter/setter and business methods
+}]]></programlisting>
+
+ <para>
+ JDK 5.0 Annotations(ê·¸ë¦¬ê³ JSR-220)ì ëí ì§ìì ì¬ì í ìì
ì´ ì§í ì¤ì´ê³ ìì±ëì§ ìììì ë
¸í¸íë¼. ìì¸í ê²ì
+ Hibernate Anotations를 참조íë¼.
+ </para>
+
+ </sect2>
+ </sect1>
+
+ <sect1 id="mapping-generated" revision="1">
+ <title>ì°ì¶ëë íë¡í¼í°ë¤</title>
+ <para>
+ ì°ì¶ëë íë¡í¼í°ë¤ì ë°ì´í°ë² ì´ì¤ì ìí´ ì°ì¶ëë ê·¸ê²ë¤ì ê°ë¤ì ê°ë íë¡í¼í°ë¤ì´ë¤. ì íì ì¼ë¡,
+ Hibernate ì´í리ì¼ì´ì
ë¤ì ë°ì´í°ë² ì´ì¤ê° ê°ë¤ì ìì±ìì¼°ë ììì íë¡í¼í°ë¤ì í¬í¨íë ê°ì²´ë¤ì
+ <literal>ê°±ì </literal>ìí¬ íìê° ììë¤.íì§ë§ generatedë¡ ë§í¬ë íë¡í¼í°ë¤ì ì´í리ì¼ì´ì
ì¼ë¡
+ íì¬ê¸ ì´ ì±
ìì Hibernateìê² ìììí¤ëë¡ íë¤. 본ì§ì ì¼ë¡ Hibernateê° ì°ì¶ëë íë¡í¼í°ë¤ì ì ìíë ìí°í°ì
+ ëí´ SQL INSERT ëë UPDATE ëª
ë ¹ì ë´ë¦´ ëë§ë¤ ë°ë¡ ì§íì ì°ì¶ëë ê°ë¤ì ê²ìí기 ìí´ íëì select ëª
ë ¹ì
+ ë´ë¦°ë¤.
+ </para>
+ <para>
+ generatedë¡ ë§í¬ë íë¡í¼í°ë¤ì ë¶ê°ì ì¼ë¡ inser ê°ë¥íì§ ììì¼ íê³ update ë¶ê°ë¥í´ì¼ íë¤. ì¤ì§
+ Properties marked as generated must additionally be non-insertable and non-updateable.
+ <xref linkend="mapping-declaration-version">versions</xref>,
+ <xref linkend="mapping-declaration-timestamp">timestamps</xref>, 그리ê³
+ <xref linkend="mapping-declaration-property">ë¨ì íë¡í¼í°ë¤</xref> ë§ì´ generatedë¡
+ ë§í¬ë ì ìë¤.
+ </para>
+ <para>
+ <literal>never</literal> (ëí´í¸) - ë 주ì´ì§ íë¡í¼í° ê°ì´ ë°ì´í°ë² ì´ì¤ ë´ì ìì±ëì§ ìì ê²ìì ì미íë¤.
+ </para>
+ <para>
+ <literal>insert</literal> - ë 주ì´ì§ íë¡í¼í° ê°ì´ insert ìì ìì±ëì§ë§, ì°¨íì ì
ë°ì´í¸ë¤ìì ë¤ì
+ ìì±ëì§ ìì ê²ìì 기ì íë¤. ìì±-ë ì§ì ê°ì ê²ë¤ì´ ì´ ì¹´í
ê³ ë¦¬ ë´ë¡ í¬í¨ë ê²ì´ë¤. ë¹ë¡
+ <xref linkend="mapping-declaration-version">version</xref>ê³¼
+ <xref linkend="mapping-declaration-timestamp">timestamp</xref> íë¡í¼í°ë¤ì´ ìì±ëë ê²ì¼ë¡ì
+ ë§í¬ë ì ìì ì§ë¼ë, ì´ ìµì
ì 거기ì ì´ì© ë¶ê°ë¥í¨ì ë
¸í¸íë¼...
+ </para>
+ <para>
+ <literal>always</literal> - ë íë¡í¼í° ê°ì´ insert ìì update ì 모ëì ìì±ë¨ì 기ì íë¤.
+ </para>
+ </sect1>
+
+ <sect1 id="mapping-database-object">
+ <title>ë³´ì¡° ë°ì´í°ë² ì´ì¤ ê°ì²´ë¤</title>
+ <para>
+ Hibernate 매í íì¼ë¤ ë´ì ì¬ì©ì ì¤í¤ë§ë¥¼ ìì íê² ì ìí기 ìí ë¥ë ¥ì ì ê³µí기 ìí´ì,
+ Hibernateì ì¤í¤ë§ ë°©ì¶ ë구ë¤ê³¼ í¨ê» ììì ì¸ ë°ì´í°ë² ì´ì¤ ê°ì²´ë¤ì ëí CREATEì DROPì íì©í´ì¤ë¤.
+ ë¹ë¡ í¸ë¦¬ê±°ë¤ ëë ë´ì¥ íë¡ìì ë¤ê³¼ ê°ì ê²ë¤ì ìì±ìí¤ê³ ëë¡ìí¤ê¸° ì´í´ í¹ë³í ê³ ìëììì§ë¼ë
+ íëì <literal>java.sql.Statement.execute()</literal> ë©ìë를 íµí´ ì¤íë ì ìë SQL ëª
ë ¹ì´
+ ì¬ê¸°ì ì í¨íë¤(ALTERs, INSERTS, 기í). ë³´ì¡° ë°0ì´í°ë² ì´ì¤ ê°ì²´ë¤ì ì ìíë ë ê°ì§ 모ëë¤ì´ 본ì§ì ì¼ë¡
+ ì¡´ì¬íë¤...
+ </para>
+ <para>
+ 첫 ë²ì§¸ 모ëë 매í íì¼ ë°ê¹¥ìì CREATE ë° DROP ëª
ë ¹ë¤ì ëª
ìì ì¼ë¡ ëì´íë ê²ì´ë¤:
+ </para>
+ <programlisting><![CDATA[<hibernate-mapping>
+ ...
+ <database-object>
+ <create>CREATE TRIGGER my_trigger ...</create>
+ <drop>DROP TRIGGER my_trigger</drop>
+ </database-object>
+</hibernate-mapping>]]></programlisting>
+ <para>
+ ëë²ì§¸ 모ëë CREATE ë° DROP ëª
ë ¹ë¤ì ìì±ìí¤ë ë°©ë²ì ìê³ ìë íëì ë§ì¶¤ í´ëì¤ë¥¼ ì ê³µíë ê²ì´ë¤.
+ ì´ ë§ì¶¤ í´ëì¤ë <literal>org.hibernate.mapping.AuxiliaryDatabaseObject</literal>
+ ì¸í°íì´ì¤ë¥¼ 구íí´ì¼ íë¤.
+ </para>
+ <programlisting><![CDATA[<hibernate-mapping>
+ ...
+ <database-object>
+ <definition class="MyTriggerDefinition"/>
+ </database-object>
+</hibernate-mapping>]]></programlisting>
+ <para>
+ ë§ë¶ì¬ ì´ë¤ ë°ì´í°ë² ì´ì¤ ê°ì²´ë¤ì ì´ë¤ dialectë¤ì´ ì¬ì©ë ë ê·¸ê²ë¤ì´ ë¨ì§ ì ì©ë ì ìëë¡ ì íì ì¼ë¡
+ ë³ëë ì ìë¤.
+ </para>
+ <programlisting><![CDATA[<hibernate-mapping>
+ ...
+ <database-object>
+ <definition class="MyTriggerDefinition"/>
+ <dialect-scope name="org.hibernate.dialect.Oracle9Dialect"/>
+ <dialect-scope name="org.hibernate.dialect.OracleDialect"/>
+ </database-object>
+</hibernate-mapping>]]></programlisting>
+ </sect1>
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/batch.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/batch.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/batch.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,320 @@
+<chapter id="batch">
+ <title>Batch ì²ë¦¬</title>
+
+ <para>
+ Hibernate를 ì¬ì©íì¬ ë°ì´í°ë² ì´ì¤ ë´ìì 100 000 ê°ì íë¤ì ì½ì
ìí¤ë 본ëì ì ê·¼ë²ì ë¤ìê³¼ ê°ë¤:
+ </para>
+
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+for ( int i=0; i<100000; i++ ) {
+ Customer customer = new Customer(.....);
+ session.save(customer);
+}
+tx.commit();
+session.close();]]></programlisting>
+
+ <para>
+ ì´ê²ì 50 000ë²ì§¸ í ê°ê¹ì´ ê³³ìì <literal>OutOfMemoryException</literal>ì¼ë¡ ë¨ì´ì§ ê²ì´ë¤. ê·¸ê²ì Hibernateê°
+ session-level ìºì ìì 모ë ìë¡ì´ ì½ì
ë <literal>Customer</literal> ì¸ì¤í´ì¤ë¤ì ìºììí¤ê¸° ë문ì´ë¤.
+ </para>
+
+ <para>
+ ì´ ì¥ìì ì°ë¦¬ë ì´ ë¬¸ì 를 í¼íë ë°©ë²ì ë¹ì ìê² ë³´ì¬ì¤ ê²ì´ë¤. íì§ë§ 먼ì ë¹ì ì´ ë°°ì¹ ì²ë¦¬ë¥¼ ííë ì¤ì´ë¼ë©´, ë¹ì ì´ ì ë¹í í¼í¬ë¨¼ì¤ë¥¼
+ ì±ì·¨íë ¤ê³ í ê²½ì°ì ë¹ì ì´ JDBC ë°°ì¹ ì¬ì©ì ê°ë¥íê² íë ê²ì ì ëì ì¼ë¡ íìíë¤. JDBC ë°°ì¹ ì¬ì´ì¦ë¥¼ ì ë¹í ì«ì(10-50)ë¡ ì¤ì íë¼:
+ </para>
+
+<programlisting><![CDATA[hibernate.jdbc.batch_size 20]]></programlisting>
+
+ <para id="disablebatching" revision="1">
+ ë§ì¼ ë¹ì ì´ <literal>identiy</literal> ìë³ì ìì±ì를 ì¬ì©í ê²½ì°
+ Hibernateê° JDBC ë 벨ìì í¬ëª
íê² insert ë°°ì¹ìì
ì ì¬ì©ë¶ê°ë¥íê² íë¤ë ì ì ë
¸í¸íë¼.
+ </para>
+
+ <para>
+ ë¹ì ì ëí second-level ìºì를 ê°ì§ ìí¸ìì©ì´ ìì íê² ë¶ê°ë¥í íë¡ì¸ì¤ ë´ìì ì´ë° ì¢
ë¥ì ìì
ì ííê³ ì¶ì´í ìë ìë¤:
+ </para>
+
+<programlisting><![CDATA[hibernate.cache.use_second_level_cache false]]></programlisting>
+
+ <para>
+ íì§ë§ ì´ê²ì ì ëì ì¼ë¡ íìíì§ ìë¤. ìëíë©´ ì°ë¦¬ë second-level ìºììì ìí¸ìì©ì ë¶ê°ë¥íëë¡ í기 ìí´
+ ëª
ìì ì¼ë¡ <literal>CacheMode</literal>를 ì¤ì í ì ì기 ë문ì´ë¤.
+ </para>
+
+ <sect1 id="batch-inserts">
+ <title>Batch inserts</title>
+
+ <para>
+ ìë¡ì´ ê°ì²´ë¤ì ììí ìí¬ ë, ë¹ì ì first-level ìºìì ì¬ì´ì¦ë¥¼ ì ì´í기 ìí´ ì¸ì
ì ì 기ì ì¼ë¡ <literal>flush()</literal>
+ ìí¤ê³ ëì <literal>clear()</literal> ìì¼ì¼ íë¤.
+ </para>
+
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+
+for ( int i=0; i<100000; i++ ) {
+ Customer customer = new Customer(.....);
+ session.save(customer);
+ if ( i % 20 == 0 ) { //20, same as the JDBC batch size
+ //flush a batch of inserts and release memory:
+ session.flush();
+ session.clear();
+ }
+}
+
+tx.commit();
+session.close();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="batch-update" >
+ <title>Batch updates</title>
+
+ <para>
+ ë°ì´í° ê²ìê³¼ ì
ë°ì´í¸ì ê²½ì° ëì¼í ê°ë
ë¤ì´ ì ì©ëë¤. ê²ë¤ê° ë¹ì ì ë§ì ë°ì´í° íë¤ì ë°ííë ì§ìë¤ì ëí´ ìë²-측 커ìë¤ì ì¥ì ì
+ ì·¨íëë° <literal>scroll()</literal>ì ì¬ì©í íìê° ìë¤.
+ </para>
+
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+
+ScrollableResults customers = session.getNamedQuery("GetCustomers")
+ .setCacheMode(CacheMode.IGNORE)
+ .scroll(ScrollMode.FORWARD_ONLY);
+int count=0;
+while ( customers.next() ) {
+ Customer customer = (Customer) customers.get(0);
+ customer.updateStuff(...);
+ if ( ++count % 20 == 0 ) {
+ //flush a batch of updates and release memory:
+ session.flush();
+ session.clear();
+ }
+}
+
+tx.commit();
+session.close();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="batch-statelesssession">
+ <title>StatelessSession ì¸í°íì´ì¤</title>
+ <para>
+ ë¤ë¥¸ ë°©ë²ì¼ë¡ Hibernateë ë¶ë¦¬ë(detached) ê°ì²´ë¤ì íìì¼ë¡ ë°ì´í°ë² ì´ì¤ë¡ ê·¸ë¦¬ê³ ë°ì´í°ë² ì´ì¤ë¡ë¶í°
+ ë°ì´í°ë¥¼ ì¤í¸ë¦¬ë°íëë° ì¬ì©ë ì ìë íëì ëª
ë ¹ ì§í¥ API를 ì ê³µíë¤. <literal>StatelessSession</literal>ì
+ ê·¸ê²ê³¼ ì°ê´ë ìì 컨í
ì¤í¸ë¥¼ ê°ì§ ìê³ ë§ì ë³´ë¤ ëì ë 벨ì ìëª
주기 ì미를 ì ê³µíì§ ìëë¤.
+ í¹í íëì ìí ìë ì¸ì
ì 첫 ë²ì§¸ ë 벨 ìºì를 구ííì§ ìê±°ë ììì ë ë²ì§¸ ë 벨 ìºì ëë ì§ì ìºìì
+ ìí¸ìì©íì§ ìëë¤. ê·¸ê²ì ì íµì ì¸ ì°ê¸°-ì´ë©´(write-behind)ì ì²´í¹ì´ë ìëì ì¸ dirty ì²´í¹ì 구ííì§ ìëë¤.
+ íëì ìí ìë ì¸ì
ì ì¬ì©íì¬ ìíë ì°ì°ë¤ì ì°ê´ë ì¸ì¤í´ì¤ë¤ì ëí´ ì¼ì¤ì¼ì´ë ëì§ ìëë¤. ì½ë ì
ë¤ì
+ ìí ìë ì¸ì
ì ìí´ ë¬´ìëë¤. ìíìë ì¸ì
ì íµí´ ìíë ì°ì°ë¤ì Hibernateì ì´ë²¤í¸ 모íê³¼ ì¸í°ì
í°ë¤ì
+ ì°ííë¤. ìíìë ì¸ì
ë¤ì 첫ë²ì§¸-ë 벨ì ìºìì ë¶ì¡±ì¼ë¡ ì¸í´ ë°ì´í° alias í¨ê³¼ë¤ì ì·¨ì½íë¤. ìíìë ì¸ì
ì
+ 기본 JDBCì í¨ì¬ ë ê°ê¹ì´ íëì ë³´ë¤ ë®ì ë 벨ì ì¶ìí(abstraction)ì´ë¤.
+ </para>
+
+<programlisting><![CDATA[StatelessSession session = sessionFactory.openStatelessSession();
+Transaction tx = session.beginTransaction();
+
+ScrollableResults customers = session.getNamedQuery("GetCustomers")
+ .scroll(ScrollMode.FORWARD_ONLY);
+while ( customers.next() ) {
+ Customer customer = (Customer) customers.get(0);
+ customer.updateStuff(...);
+ session.update(customer);
+}
+
+tx.commit();
+session.close();]]></programlisting>
+
+ <para>
+ ì´ ì½ë ìì ìì, ì§ìì ìí´ ë°íë <literal>Customer</literal> ì¸ì¤í´ì¤ë¤ì 곧ë°ê³ ë¶ë¦¬ëë¤(detached).
+ ê·¸ê²ë¤ì ììì ìì 컨í
ì¤í¸ì ê²°ì½ ì°ê´ëì§ ìëë¤.
+ </para>
+
+ <para>
+ <literal>StatelessSession</literal> ì¸í°íì´ì¤ì ìí´ ì ìë
+ <literal>insert(), update()</literal> ê·¸ë¦¬ê³ <literal>delete()</literal> ì°ì°ë¤ì ì§ì ì ì¸
+ ë°ì´í°ë² ì´ì¤ ì ê¸ ì°ì°ë¤ë¡ ê°ì£¼ëê³ , ê·¸ê²ì ê°ê° íëì SQL <literal>INSERT, UPDATE</literal> ëë
+ <literal>DELETE</literal>ì ì¦ê°ì ì¸ ì¤íì¼ë¡ ê·ê²°ëë¤. ë°ë¼ì ê·¸ê²ë¤ì <literal>Session</literal>
+ ì¸í°íì´ì¤ì ìí´ ì ìë <literal>save(), saveOrUpdate()</literal>ì <literal>delete()</literal>
+ ì°ì°ë¤ê³¼ë ë§¤ì° ë¤ë¥¸ ì미ë¤ì ê°ëë¤.
+ </para>
+
+ </sect1>
+
+ <sect1 id="batch-direct" revision="3">
+ <title>DML-ì¤íì¼ ì°ì°ë¤</title>
+
+ <para>
+ ì´ë¯¸ ë
¼ìíë¯ì´, ìëì ì´ê³ í¬ëª
í ê°ì²´/ê´ê³í 매íì ê°ì²´ ìíì ëí ê´ë¦¬ì ê´ê³ëë¤. ì´ê²ì ê°ì²´ ìíê° ë©ëª¨ë¦¬ ë´ìì ì´ì© ê°ë¥í¨ì
+ ì미íë¯ë¡, (SQL <literal>Data Manipulation Language</literal>(DML) 문ì¥ë¤ : <literal>INSERT</literal>,
+ <literal>UPDATE</literal>, <literal>DELETE</literal>를 ì¬ì©íì¬) ë°ì´í°ë² ì´ì¤ìì ì§ì ë°ì´í°ë¥¼
+ ì
ë°ì´í¸íê±°ë ìì íë ê²ì ë©ëª¨ë¦¬ ë´ ìíì ìí¥ì ì£¼ì§ ìì ê²ì´ë¤. íì§ë§ Hibernateë Hibernate Query Language
+ (<xref linkend="queryhql">HQL</xref>)를 íµí´ ì¤íëë ëë SQL-ì¤íì¼ì DML ë¬¸ì¥ ì¤íì ìí ë©ìëë¤ì ì ê³µíë¤.
+ </para>
+
+ <para>
+ <literal>UPDATE</literal>ì <literal>DELETE</literal> 문ì¥ë¤ì ëí ì ì¬ êµ¬ë¬¸ì ë¤ìê³¼ ê°ë¤:
+ <literal>( UPDATE | DELETE ) FROM? EntityName (WHERE where_conditions)?</literal>. ë
¸í¸í ëª ê°ì§:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ from-ì ìì, FROM í¤ìëë ìµì
ì´ë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ from-ì ë´ì í ê°ì ëª
ëª
ë ìí°í°ê° ì¤ì§ ì¡´ì¬í ì ìë¤; ê·¸ê²ì ì íì ì¼ë¡ aliasë ì ìë¤. ë§ì¼ ìí°í°
+ ì´ë¦ì´ aliasëë©´, ê·¸ë ììì íë¡í¼í° 참조ë¤ì ê·¸ alias를 ì¬ì©íì¬ ììëì´ì¼ íë¤; ë§ì¼ ìí°í° ì´ë¦ì´
+ aliasëì§ ìì ê²½ì°, ììì íë¡í¼í° 참조ë¤ì ëí´ ììëë ê²ì ê·ì¹ì ì´ê¸ëë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <xref linkend="queryhql-joins-forms">joinë¤</xref>ì (í¨ì¶ì ì´ë ëª
ìì ì´ë )
+ ëë HQL ì§ì ìì ì§ì ë ì ìë¤. ìë¸-ì§ìë¤ì´ where-ì ì ì¬ì©ë ì ìë¤;
+ ìë¸ì§ìë¤ ê·¸ ìì ë¤ì ì¡°ì¸ë¤ì í¬í¨í ì ìë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ where-ì ëí ìµì
ì´ë¤.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ íëì ìì ë¡ì, í ê°ì HQL <literal>UPDATE</literal>를 ì¤íí기 ìí´, <literal>Query.executeUpdate()</literal>
+ ë©ìë(ì´ ë©ìëë JDBCì <literal>PreparedStatement.executeUpdate()</literal>ì ì ì¬íê² ëª
ëª
ëë¤)를 ì¬ì©íë¼:
+ </para>
+
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+
+String hqlUpdate = "update Customer c set c.name = :newName where c.name = :oldName";
+// or String hqlUpdate = "update Customer set name = :newName where name = :oldName";
+int updatedEntities = s.createQuery( hqlUpdate )
+ .setString( "newName", newName )
+ .setString( "oldName", oldName )
+ .executeUpdate();
+tx.commit();
+session.close();]]></programlisting>
+
+ <para>
+ HQL <literal>UPDATE</literal> 문ì¥ë¤ì ëí´í¸ë¡ ìí¥ë°ë ìí°í°ë¤ì ëí
+ <xref linkend="mapping-declaration-version">version</xref> ëë
+ <xref linkend="mapping-declaration-timestamp">timestamp</xref> íë¡í¼í° ê°ë¤ì ìí¥ì ì£¼ì§ ìëë¤;
+ ì´ê²ì EJB3 ëª
ì¸ììì ì ì§ëê³ ìë¤. íì§ë§ ë¹ì ì íì´ë²ë¤ì´í¸ë¡ íì¬ê¸ <literal>versioned update</literal>ì
+ ì¬ì©ì íµí´ <literal>version</literal> ëë <literal>timestamp</literal> íë¡í¼í° ê°ë¤ì ì ì íê²
+ ì¬ì¤ì íëë¡ ê°ì í ì ìë¤. ì´ê²ì <literal>UPDATE</literal> í¤ìë ë¤ì <literal>VERSIONED</literal> í¤ìë를
+ ì¶ê°ìì¼ì ì±ì·¨ëë¤.
+ </para>
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+String hqlVersionedUpdate = "update versioned Customer set name = :newName where name = :oldName";
+int updatedEntities = s.createQuery( hqlUpdate )
+ .setString( "newName", newName )
+ .setString( "oldName", oldName )
+ .executeUpdate();
+tx.commit();
+session.close();]]></programlisting>
+
+ <para>
+ ë§ì¶¤í version íì
ë¤(<literal>org.hibernate.usertype.UserVersionType</literal>)ì
+ <literal>update versioned</literal> 문ì¥ê³¼ í¨ê» ì¬ì©íë ê²ì´ íì©ëì§ ììì ë
¸í¸íë¼.
+ </para>
+
+ <para>
+ HQL <literal>DELETE</literal>를 ì¤ííë ¤ë©´, ê°ì ë©ìë <literal>Query.executeUpdate()</literal>를
+ ì¬ì©íë¼:
+ </para>
+
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+
+String hqlDelete = "delete Customer c where c.name = :oldName";
+// or String hqlDelete = "delete Customer where name = :oldName";
+int deletedEntities = s.createQuery( hqlDelete )
+ .setString( "oldName", oldName )
+ .executeUpdate();
+tx.commit();
+session.close();]]></programlisting>
+
+ <para>
+ <literal>Query.executeUpdate()</literal> ë©ìëì ìí´ ë°íëë <literal>int</literal> ê°ì ê·¸ ì¤í¼ë ì´ì
ì ìí´
+ ìí¥ë°ì ìí°í°ë¤ì ê°ì를 ëíë¸ë¤. ì´ê²ì´ ë°ì´í°ë² ì´ì¤ ë´ìì ìí¥ë°ì íë¤ì ê°ìì ìê´ì´ ìëì§ ìëì§ ì¬ë¶ë¥¼ ì´í´ë³´ì. HQL
+ ëë ì¤í¼ë ì´ì
ì ì를 ë¤ì´ joined-subclassì ê²½ì°ì ì¤í ì¤ì¸ ì¬ë¬ ê°ì ì¤ì SQL 문ì¥ë¤ë¡ ê·ê²°ë ì ìë¤. ë°íëë ì«ìë
+ ê·¸ 문ì¥ì ìí´ ìí¥ë°ì ì¤ì ìí°í°ë¤ì ê°ì를 ëíë¸ë¤. joined-subclass ìì ë¡ ëëìê°ë©´, ìë¸ í´ëì¤ë¤ ì¤ íëì ëí ìì ë
+ ë¨ì§ ê·¸ ìë¸í´ëì¤ê° 매íëì´ ìë í
ì´ë¸ì ëí ìì ë¿ë§ ìëë¼ ëí "루í¸" í
ì´ë¸ê³¼ ìì ê³ì¸µìì ë ë´ë ¤ì¨ ì ì ì ì¼ë¡
+ ì¡°ì¸ë-ìë¸í´ëì¤ í
ì´ë¸ë¤ì ëí ìì ë¤ë¡ ê·ê²°ë ì ìë¤.
+ </para>
+
+ <para>
+ ì¥ëì ë°°í¬ë³¸ë¤ìì ì ë¬ë ëë HQL ì¤í¼ë ì´ì
ë¤ì ëí ëª ê°ì§ ì íë¤ì´ íì¬ ì¡´ì¬í¨ì ë
¸í¸íë¼; ìì¸í ê²ì JIRA ë¡ë맵ì 참조íë¼.
+ <literal>INSERT</literal> 문ì¥ë¤ì ìí ì ì¬-구문ì ë¤ìê³¼ ê°ë¤:
+ <literal>INSERT INTO EntityName properties_list select_statement</literal>. ë
¸í¸í ëª ê°ì§:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ ì¤ì§ INSERT INTO ... SELECT ... íì ë§ì¼ ì§ìëë¤; INSERT INTO ... VALUES ... íìì ì§ìëì§ ìëë¤.
+ </para>
+ <para>
+ properties_listë SQL <literal>INSERT</literal> ë´ìì <literal>column speficiation</literal>ê³¼ ì ì¬íë¤.
+ 매íë ììì ì°¸ì¬íë ìí°í°ë¤ì ê²½ì°, ê·¸ 주ì´ì§ í´ëì¤-ë 벨 ìì ì§ì ì ìë íë¡í¼í°ë¤ ë§ì´ properties_listì ì¬ì©ë ì ìë¤.
+ ìí¼í´ëì¤ íë¡í¼í°ë¤ì íì©ëì§ ìëë¤; ê·¸ë¦¬ê³ ìë¸í´ëì¤ íë¡í¼í°ë¤ì ìë¯¸ê° ìë¤. ë¬ë¦¬ ë§í´ <literal>INSERT</literal> 문ì¥ë¤ì
+ 본ëì ì¼ë¡ ë¤íì ì´ì§ ìë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ select_statementë ë°í íì
ë¤ì´ insertì ìí´ ê¸°ëëë íì
ë¤ê³¼ ì¼ì¹í´ì¼ íë¤ë ë¨ì íì ììì ì í¨í HQL select ì§ìì¼ ì
+ ìë¤. íì¬ ì´ê²ì ì²´í¬ë¥¼ ë°ì´í°ë² ì´ì¤ë¡ ì´ê´ìí¤ë ê²ì íì©í기 ë³´ë¤ë ì§ì ì»´íì¼ ëìì ì²´í¬ëë¤. íì§ë§ ì´ê²ì
+ <emphasis>equal</emphasis>ê³¼ë ëì¡°ì ì¼ë¡ <emphasis>ë±ê°ì¸(equivalent)</emphasis> Hibernate <literal>Type</literal>ë¤
+ ì¬ì´ìì 문ì ì ë¤ì ì¼ì¼í¬ ìë ììì ë
¸í¸íë¼. ë¹ë¡ ë°ì´í°ë² ì´ì¤ê° 구ë³ì§ì§ ìì ì ìê±°ë ë³íì ì²ë¦¬í ì ìì ì§ë¼ë, ì´ê²ì
+ <literal>org.hibernate.type.DateType</literal>ë¡ì ì ìë íë¡í¼í°ì <literal>org.hibernate.type.TimestampType</literal>ì¼ë¡
+ ì ìë íë¡í¼í° ì¬ì´ì ë¶ì¼ì¹ ìì ë¤ì ì¼ì¼í¨ë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ id íë¡í¼í°ì ê²½ì°, insert 문ì¥ì ë¹ì ìê² ë ê°ì ìµì
ì ì¤ë¤. ë¹ì ì properties_list ë´ì id íë¡í¼í°ë¥¼ ëª
ìì ì¼ë¡
+ ì§ì í ì ìê±°ë(ê·¸ê²ì ê°ì´ ëìíë select ííìì¼ë¡ë¶í° ì»ì´ì§ ê²½ì°) ëë properties_listìì ê·¸ê²ì ìëµí ìë ìë¤
+ (ì°ì¶ë ê°ì´ ì¬ì©ëë ê²½ì°). ì´ íìì ìµì
ì ë°ì´í°ë² ì´ì¤ ë´ìì ì°ì°ëë id ì°ì°ìë¤ì ì¬ì©í ëìë§ ì´ì© ê°ë¥íë¤;
+ ììì "ë©ëª¨ë¦¬ ë´" íì
ì°ì°ìë¤ê³¼ í¨ê» ì´ ìµì
ì ì¬ì©íë ¤ê³ ìëíë ê²ì íì± ëìì ìì¸ìí©ì ì¼ì¼í¬ ê²ì´ë¤. ì´ ë
¼ìì
+ 목ì ì, ë°ì´í°ë² ì´ì¤ ë´ ì°ì¶ì(generator)ë¤ì <literal>org.hibernate.id.SequenceGenerator</literal>
+ (ê·¸ë¦¬ê³ ê·¸ê²ì ìë¸í´ëì¤ë¤) ê·¸ë¦¬ê³ ììì <literal>org.hibernate.id.PostInsertIdentifierGenerator</literal>ì
+ 구íìë¤ì´ë¼ê³ ê°ì£¼ë¨ì ë
¸í¸íë¼. ì¬ê¸°ì ê°ì¥ 주목í ë§í ìì¸ìí©ì ê·¸ê²ì´ ê·¸ê²ì ê°ë¤ì ì»ê¸° ìí select ê°ë¥í ë°©ë²ì
+ ë
¸ì¶ìí¤ì§ ì기 ë문ã
ì ì¬ì©ë ì ìë <literal>org.hibernate.id.TableHiLoGenerator</literal>ì´ë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>version</literal> ëë <literal>timestamp</literal>ë¡ì 매íë íë¡í¼í°ë¤ì ê²½ì°ì,
+ insert 문ì¥ì ë¹ì ìê² ë ê°ì ìµì
ë¤ì ì¤ë¤. ë¹ì ì properties_list ë´ì ê·¸ íë¡í¼í°ë¥¼ ì§ì í ì ìê±°ë
+ (ê·¸ ê²ì ê°ì´ ëìíë select ííìì¼ë¡ë¶í° ì»ì´ì§ ê²½ì°) ëë properties_listìì ê·¸ê²ì ìëµí ì ìë¤
+ (<literal>org.hibernate.type.VersionType</literal>ì ìí´ ì ìë <literal>seed value</literal>
+ ê°ì´ ì¬ì©ëë ê²½ì°).
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ìì HQL <literal>INSERT</literal> ë¬¸ì¥ ì¤í:
+ </para>
+
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+
+String hqlInsert = "insert into DelinquentAccount (id, name) select c.id, c.name from Customer c where ...";
+int createdEntities = s.createQuery( hqlInsert )
+ .executeUpdate();
+tx.commit();
+session.close();]]></programlisting>
+
+ </sect1>
+
+</chapter>
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/best_practices.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/best_practices.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/best_practices.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,204 @@
+<chapter id="best-practices" revision="3">
+ <title>ìµìì ì¤ì ê²½íë¤</title>
+
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term>fine-grained í´ëì¤ë¤ì ìì±íê³ <literal><component></literal>를 ì¬ì©íì¬ ê·¸ê²ë¤ì 매ííë¼. </term>
+ <listitem>
+ <para>
+ <literal>street</literal>, <literal>suburb</literal>, <literal>state</literal>, <literal>postcode</literal>를
+ 캡ìí ìí¤ëë° <literal>Address</literal> í´ëì¤ë¥¼ ì¬ì©íë¼. ì´ê²ì ì½ë ì¬ì¬ì©ì±ì ì´ì§ìí¤ê³ 리í©í ë§ì ë¨ìí ìí¨ë¤.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>ìì í´ëì¤ë¤ì ëí´ ìë³ì íë¡í¼í°ë¤ì ì ì¸íë¼.</term>
+ <listitem>
+ <para>
+ Hibernateë ìë³ì íë¡í¼í°ë¤ì ìµì
ì¼ë¡ ë§ë ë¤. ì ì°ë¦¬ê° ê·¸ê²ë¤ì ì¬ì©í´ì¼ íëê°ì ëí 모ë ì¢
ë¥ì ì´ì ë¤ì´ ì¡´ì¬íë¤.
+ ì°ë¦¬ë ìë³ìë¤ì´ 'í©ì±(synthetic)'ì´ ëë(ë¹ì§ëì¤ ì미 ìì´ ìì±ëë) ê²ì ê¶ì¥íë¤.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>ê³ ì í¤ë¤ì ìë³íë¼.</term>
+ <listitem>
+ <para>
+ 모ë ìí°í°ë¤ì ëí´ ê³ ì í¤ë¤ì ìë³íê³ , <literal><natural-id></literal>를 ì¬ì©íì¬ ê·¸ê²ë¤ì 매ííë¼.
+ ê³ ì í¤ë¥¼ 구ì±íë íë¡í¼í°ë¤ì ë¹êµí기 ìí´ <literal>equals()</literal>ì <literal>hashCode()</literal>를
+ 구ííë¼.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry><term>ê°ê°ì í´ëì¤ ë§¤íì ê·¸ê² ìì ì íì¼ ë´ì ìì¹ì§ìë¼.</term>
+ <listitem>
+ <para>
+ íëì íë©ì´ë¦¬ 매í 문ì를 ì¬ì©íì§ ë§ë¼. <literal>com/eg/Foo.hbm.xml</literal> íì¼ ìì
+ <literal>com.eg.Foo</literal>를 매ííë¼. ì´ê²ì í íê²½ìì í¹í ì¢ì ì미를 ì¤ë¤.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>매íë¤ì 리ìì¤ë¤ë¡ì ë¡ëìì¼ë¼.</term>
+ <listitem>
+ <para>
+ ê·¸ê²ë¤ì´ 매ííë í´ëì¤ë¤ì ë°ë¼ì 매íë¤ì ë°°ì¹íë¼
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>ì§ì 문ìì´ë¤ì ê°ê´í ìí¤ë ê²ì ê³ ë ¤íë¼.</term>
+ <listitem>
+ <para>
+ ë¹ì ì ì§ìë¤ì´ ANSI íì¤ì´ ìë SQL í¨ìë¤ì í¸ì¶íë ê²½ì°ë ì¢ì ì¤ì ì°ìµì´ë¤. ì§ì 문ìì´ë¤ì 매í íì¼ë¤ìë¤ê°
+ ì¸ë¶í ìí¤ë ê²ì ì´í리ì¼ì´ì
ì ë³´ë¤ ì´ìì± ìëë¡ ë§ë¤ì´ì¤ ê²ì´ë¤.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>ë°ì¸ë ë³ìë¤ì ì¬ì©íë¼.</term>
+ <listitem>
+ <para>
+ JDBCììì²ë¼, ìì ìë ê°ë¤ì "?"ë¡ ëì²´ìì¼ë¼. ì§ì ìì ìì ìë ê°ì ë°ì¸ë ìí¤ëë° ë¬¸ìì´ ì²ë¦¬ë¥¼ ê²°ì½
+ ì¬ì©íì§ ë§ë¼! ë ì¢ê²ë ì§ìë¤ ìì ëª
ëª
ë íë¼ë¯¸í°ë¤ì ì¬ì©íë ê²ì ê³ ë ¤íë¼.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>ë¹ì ìì ì JDBC 커ë¥ì
ë¤ì ê´ë¦¬íì§ ë§ë¼.</term>
+ <listitem>
+ <para>
+ Hibernateë ì´í리ì¼ì´ì
ì¼ë¡ íì¬ê¸ JDBC 커ë¥ì
ë¤ì ê´ë¦¬íëë¡ íë¤. ì´ ì ê·¼ë²ì ë§ì§ë§ ìë¨ì¼ë¡ì ê³ ë ¤ëì´ì¼ íë¤.
+ ë§ì¼ ë¹ì ì´ ë¯¸ë¦¬ ë§ë¤ì´ì§ 커ë¥ì
íë¡ë°ì´ëë¤ì ì¬ì©í ì ìì ê²½ì°, <literal>org.hibernate.connection.ConnectionProvider</literal>ì
+ ëí ë¹ì ìì ì 구íì ì ê³µíë ê²ì ê³ ë ¤íë¼.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>ë§ì¶¤í íì
ì¬ì©ì ê³ ë ¤íë¼.</term>
+ <listitem>
+ <para>
+ ë¹ì ì´ ìë° íì
ì ê°ê³ ìê³ , ì´ë¤ ë¼ì´ë¸ë¬ë¦¬ë¡ë¶í° ë§íê³ , ê·¸ê²ì´ ììí ë íìê° ìì§ë§ ê·¸ê²ì ì»´í¬ëí¸ë¡ì 매íìí¤ëë°
+ íìí accessorë¤ì ì ê³µí íìê° ìë¤ê³ ê°ì íì. ë¹ì ì <literal>org.hibernate.UserType</literal>ì 구ííë
+ ê²ì ê³ ë ¤í´ì¼ í ê²ì´ë¤. ì´ ì ê·¼ë²ì Hibernate íì
ì¼ë¡/ì¼ë¡ë¶í° ë³íë¤ì 구ííë ê²ì¼ë¡ë¶í° ì´í리ì¼ì´ì
ì½ë를 ìì ë¡ê²
+ í´ì¤ë¤.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>ë³ëª© ì§ì ë¤ìì ììì
ì¼ë¡ ì½ë©ë JDBC를 ì¬ì©íë¼.</term>
+ <listitem>
+ <para>
+ ìì¤í
ì í¼í¬ë¨¼ì¤ê° ì¤ëí ììë¤ìì, ëªëª ì¢
ë¥ì ì¤í¼ë ì´ì
ë¤ì ì§ì ì ì¸ JDBCìì ì´ëì 본ë¤. ê·¸ë¬ë ë¹ì ì´ ì´ë¤ ê²ì´ ë³ëª©ì¸ì§ë¥¼
+ <emphasis>ì기</emphasis> ì ê¹ì§ 기ë¤ë¦¬ê¸¸ ë°ëë¤. ê·¸ë¦¬ê³ ì§ì ì ì¸ JDBCê° ë°ëì ë ë¹ ë¥´ë¤ê³ ê°ì íì§ ë§ë¼. ë§ì¼ ë¹ì ì´ ì§ì ì ì¸
+ JDBC를 ì¬ì©í íìê° ìì ê²½ì°, Hibernate <literal>Session</literal>ì ì´ê³ ê·¸ SQL 커ë¥ì
ì ì¬ì©í ê°ì¹ê° ìë¤. ê·¸ ë°©ë²ì¼ë¡
+ ë¹ì ì ëì¼í í¸ëìì
ë°©ëì 기본 커ë¥ì
íë¡ë°ì´ë를 ì¬ì í ì¬ì©í ì ìë¤
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>Session</literal> flushingì ì´í´íë¼.</term>
+ <listitem>
+ <para>
+ ìê°ì´ ì§ë¨ì ë°ë¼ Sessionì ê·¸ê²ì ìì ìí를 ë°ì´í°ë² ì´ì¤ì ë기í ìí¨ë¤. ë§ì¼ ì´ ê³¼ì ì´ ë무 ì주 ë°ìí ê²½ì° í¼í¬ë¨¼ì¤ê°
+ ìí¥ì ë°ì ê²ì´ë¤. ë¹ì ì ëëë¡ ìëì ì¸ flushingì ì¬ì© ë¶ê°ë¥íê² ë§ë¤ê±°ë í¹ì í¸ëìì
ë´ìì ì§ìë¤ì ììì ë¤ë¥¸
+ ì¤í¼ë ì´ì
ë¤ì ìì를 ë³ê²½ìì¼ì ë¶íìí flushingì ìµìí ìí¬ ì ìë¤.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>3-tier ìí¤í
ì²ìì, <literal>saveOrUpdate()</literal> ì¬ì©ì ê³ ë ¤íë¼.</term>
+ <listitem>
+ <para>
+ servlet / session ë¹ ìí¤í
ì²ë¥¼ ì¬ì©í ë, ë¹ì ì sesson bean ë´ì ë¡ëë ìì ê°ì²´ë¤ì ìë¸ë¦¿/JSP ê³ì¸µì¼ë¡/ì¼ë¡ë¶í°
+ ì ë¬í /ë°ì ì ìë¤. ê°ê°ì ìì²ì ìë¹ì¤íëë° ìë¡ì´ ì¸ì
ì ì¬ì©íë¼. ê°ì²´ë¤ì ë°ì´í°ë² ì´ì¤ì ë기í ìí¤ê¸° ìí´ì
+ <literal>Session.merge()</literal> ëë <literal>Session.saveOrUpdate()</literal>를 ì¬ì©íë¼.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>2-tier ìí¤í
ì²ìì, ìëª
ì´ ê¸´ ìì 컨í
ì¤í¸ë¤ì ì¬ì©íë ê²ì ê³ ë ¤íë¼.</term>
+ <listitem>
+ <para>
+ ë°ì´í°ë² ì´ì¤ í¸ëìì
ë¤ì ìµìì ê°ì©ì±ì ìí´ ê°ë¥í í 짧ìì¼ íë¤. íì§ë§ ì¥ê¸°ê° ì¤íëë <emphasis>ì´í리ì¼ì´ì
í¸ëìì
ë¤</emphasis>,
+ ì¬ì©ìì ë·° ê´ì ìì í ê°ì ë¨ì ìì
ì 구ííë ê²ì´ ê°ë íìì ì´ë¤. íëì ì´í리ì¼ì´ì
í¸ëìì
ì ëª ê°ì í´ë¼ì´ì¸í¸ ìì²ë¤ê³¼
+ ìëµ ì£¼ê¸°ë¤ì ê±¸ì¹ ìë ìë¤. ì´í리ì¼ì´ì
í¸ëìì
ë¤ì 구ííëë° detached ê°ì²´ë¤ì ì¬ì©íë ê²ì´ ê³µíµì ì´ë¤. 2-í°ì´ ìí¤í
ì²ìì
+ ë§¤ì° ì ì í ëìì ì´í리ì¼ì´ì
í¸ëìì
ì ì ì²´ ìëª
주기 ëìì í ê°ì ì´ë ¤ì§ ìì ì ì (ì¸ì
)ì ì ì§íë ê²ì´ê³ ê°ê°ì ìì²ì ëìì
+ JDBC 커ë¥ì
ì ê°ë¨íê² ì°ê²°í´ì íê³ ì°¨íì ìì²ì ìì ìì ë¤ì ì°ê²°íë ê²ì´ë¤. í ê° ì´ìì ì´í리ì¼ì´ì
í¸ëìì
ì ê°ë¡ì§ë¬ì
+ íëì ë¨ì¼ ì¸ì
ì ê²°ì½ ê³µì íì§ ë§ë¼. ê³µì í ê²½ì°ì ë¹ì ì ì¤í¨ì±ì´ ìë ë°ì´í°ë¡ ìì
íê² ë ê²ì´ë¤.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>ìì¸ìí©ë¤ì 복구ê°ë¥í ê²ì¼ë¡ì ë¤ë£¨ì§ ë§ë¼.</term>
+ <listitem>
+ <para>
+ ì´ê²ì "ìµìì" ì¤ì ì´ ìë ë³´ë¤ íìì ì¸ ì¤ì ì´ë¤. ìì¸ìí©ì´ ë°ìí ë, <literal>Transaction</literal>ì 롤백ìí¤ê³
+ <literal>Session</literal>ì ë«ìë¼. ë§ì¼ ë¹ì ì´ ê·¸ë ê² íì§ ìì ê²½ì°, Hibernateë ë©ëª¨ë¦¬ ë´ ìíê° ìì ìí를 ì ííê²
+ íííë ê²ì ë³´ì¦í ì ìë¤. ì´ í¹ë³í ê²½ì°ì²ë¼, ë§ì¼ 주ì´ì§ ìë³ì를 ê°ì§ ì¸ì¤í´ì¤ê° ë°ì´í°ë² ì´ì¤ ìì ì¡´ì¬íëì§ ì¬ë¶ë¥¼ ê²°ì íëë°
+ <literal>Session.load()</literal>를 ì¬ì©íì§ ë§ë¼; ëì ì <literal>Session.get()</literal> ëë íëì ì§ì를
+ ì¬ì©íë¼.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>ì°ê´ë¤ì ëí lazy íì¹ì ì í¸íë¼.</term>
+ <listitem>
+ <para>
+ eager íì¹ì ê´ëíê² ì¬ì©íë¼. second-level ìºì ë´ì ìì íê² ë³´ê´ëì§ ìì ê² ê°ì í´ëì¤ë¤ì ëí ëë¶ë¶ì ì°ê´ë¤ì
+ ëí´ íë½ìë¤ê³¼ lazy ì½ë ì
ë¤ì ì¬ì©íë¼. ìºìë í´ëì¤ë¤ì ëí ì°ê´ë¤ì ê²½ì°, ì´ê³³ì ìºì ì±ê³µì ë§¤ì° ëì íë¥ ì´ ì¡´ì¬íë
+ ê³³ì´ë©°, <literal>lazy="false"</literal>를 ì¬ì©íì¬ eager íì¹ì ëª
ìì ì¼ë¡ ì¬ì© ë¶ê°ë¥íê² íë¼. íì join íì¹ì´
+ í¹ì ì°ììì ëí´ ì ì í ë, íëì <literal>left join fetch</literal>를 ê°ì§ ì§ì를 ì¬ì©íë¼.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ íì¹ëì§ ìì ë°ì´í° 문ì ì ë¤ì í¼í기 ìí´ <emphasis>ë·° ë´ì ì´ë ¤ì§ ì¸ì
(open session in view)</emphasis> í¨í´ ëë
+ íëì ì 리ë <emphasis>ì´ì
ë¸ë¦¬ ë¨ê³(assembly phase)</emphasis>를 ì¬ì©íë¼.
+ </term>
+ <listitem>
+ <para>
+ Hibernateë ê°ë°ìë¤ì´ <emphasis>Data Transfer Objects</emphasis> (DTO)를 ì§ë£¨íê² ìì±íë ê²ì¼ë¡ë¶í°
+ ìì ë¡ê² í´ì¤ë¤. ì íµì ì¸ EJB ìí¤í
ì²ìì, DTOë ì´ì¤ ì©ëë¡ ê¸°ë¥íë¤: 첫 ë²ì§¸ë¡ ê·¸ê²ë¤ì ìí°í° ë¹ì¦ê° ì§ë ¬í ê°ë¥íì§ ìë
+ 문ì ì ì ëí´ ì°©ìíë¤; ë ë²ì§¸ë¡ ê·¸ê²ë¤ì ë·°ì ìí´ ì¬ì©ëë 모ë ë°ì´í°ê° í리ì í
ì´ì
í°ì´ë¡ 컨í¸ë¡¤ì ë°íí기 ì ì DTOë¤ ìì¼ë¡
+ íì¹ëê³ ë§ì¬ë§ëë ì´ì
ë¸ë¦¬ ë¨ê³ë¥¼ ì묵ì ì¼ë¡ ì ìíë¤. Hibernateë 첫 ë²ì§¸ ì©ë를 ì ê±°ìí¨ë¤. íì§ë§ ë¹ì ì´ ë·° ë ëë§ íë¡ì¸ì¤ë¥¼
+ ê°ë¡ì§ë¬ ì´ë ¤ì ¸ ìë ìì 컨í
ì¤í¸(ì¸ì
)ì ë³´ê´í ì¤ë¹ê° ëì´ ìì§ ìë í, ë¹ì ì ì¬ì í ì´ì
ë¸ë¦¬ ë¨ê³ë¥¼ íìë¡ í ê²ì´ë¤(detached
+ ê°ì²´ë¤ìì ì´ì©ê°ë¥í ë°ì´í°ê° 무ìì¸ì§ì ëí´ í리ì í
ì´ì
í°ì´ì ì격íê² ê³ì½ì ê°ëë¡ ë¹ì ì ë¹ì§ëì¤ ë©ìëë¤ì ê³ ë ¤íë¼)
+ ì´ê²ì Hibernateì íê³ì ì´ ìëë¤! ê·¸ê²ì ìì í í¸ëìì
ë°ì´í° ì ê·¼ì íì ì¡°ê±´ì´ë¤.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Hibernateìì ë¹ì ì ë¹ì§ëì¤ ë¡ì§ì ì¶ìí ìí¤ë ê²ì ê³ ë ¤íë¼.</term>
+ <listitem>
+ <para>
+ (Hibernate) ë°ì´í° ì¡ì¸ì¤ ì½ë를 ì¸í°íì´ì¤ ì´ë©´ì ìíìì¼ë¼. <emphasis>DAO</emphasis>ì
+ <emphasis>Thread Local Session</emphasis> í¨í´ë¤ì ê²°í©ìì¼ë¼. ë¹ì ì ì¬ì§ì´ <literal>UserType</literal>ì
+ íµí´ Hibernateì ì°ê´ë, ììì
ì¼ë¡ ì½ë©ë JDBCë¡ì ëªëª í´ëì¤ë¤ì ììí ìí¬ ìë ìë¤. (ì´ ì¶©ê³ ë "충ë¶í í°"
+ ì´í리ì¼ì´ì
ë¤ì ëí ê²ì´ë¤; ê·¸ê²ì 5ê°ì í
ì´ë¸ë¤ì ê°ì§ ì´í리ì¼ì´ì
ì ëí´ìë ì ì íì§ ìë¤!)
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>ì ì¢
ì ì°ê´ 매íì ì¬ì©íì§ ë§ë¼.</term>
+ <listitem>
+ <para>
+ ì¤ì ì many-to-many ì°ê´ë¤ì ëí ì¢ì ì°ììë¤ì ë물ë¤. ëë¶ë¶ì ìê° ëì ë¹ì ì "ì°ê²° í
ì´ë¸" ë´ì ì ì¥ë ì¶ê°ì ì¸ ì 보를 íìë¡ íë¤.
+ ì´ ê²½ì°ì, 매ê°íë ì°ê²° í´ëì¤ì ëí´ ë ê°ì one-to-many ì°ê´ë¤ì ì¬ì©íë ê²ì´ í¨ì¬ ë ì¢ë¤. ì¬ì¤ ì°ë¦¬ë ëë¶ë¶ì ì°ê´ë¤ì´ one-to-manyì
+ many-to-oneì´ë¼ê³ ìê°íë©°, ë¹ì ì ë¤ë¥¸ ì°ê´ ì¤íì¼ì ì¬ì©í ë 주ìí´ì¼ íê³ ê·¸ê²ì´ ì§ì íìì ì¸ì§ë¥¼ ë¹ì ì¤ì¤ë¡ ì§ë¬¸íë¼.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>ìë°©í¥ ì°ê´ë¤ì ì í¸íë¼.</term>
+ <listitem>
+ <para>
+ ë¨ë°©í¥ ì°ê´ë¤ì ì§ìíê¸°ê° ë ì´ë µë¤. ë§ì ì´í리ì¼ì´ì
ìì, ê±°ì 모ë ì°ê´ë¤ì ì§ìë¤ ë´ìì ì ë°©í¥ì¼ë¡ ë¤ë¹ê²ì´í¸ ê°ë¥í´ì¼ íë¤.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/collection_mapping.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/collection_mapping.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/collection_mapping.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1158 @@
+<chapter id="collections">
+ <title>ì½ë ì
매í</title>
+
+ <sect1 id="collections-persistent" revision="3">
+ <title>ìì ì½ë ì
ë¤</title>
+
+ <para>
+ ì를 ë¤ì´ Hibernateë ìì ì½ë ì
-ê°ì ê°ì§ íëë¤ì´ ì¸í°íì´ì¤ íì
ì¼ë¡ì ì ì¸ë ê²ì íìë¡ íë¤:
+ </para>
+
+ <programlisting><![CDATA[public class Product {
+ private String serialNumber;
+ private Set parts = new HashSet();
+
+ public Set getParts() { return parts; }
+ void setParts(Set parts) { this.parts = parts; }
+ public String getSerialNumber() { return serialNumber; }
+ void setSerialNumber(String sn) { serialNumber = sn; }
+}]]></programlisting>
+
+ <para>
+ ì¤ì ì¸í°íì´ì¤ë <literal>java.util.Set</literal>, <literal>java.util.Collection</literal>,
+ <literal>java.util.List</literal>, <literal>java.util.Map</literal>, <literal>java.util.SortedSet</literal>,
+ <literal>java.util.SortedMap</literal> ëë ë¹ì ì´ ì¢ìíë ì´ë¤ ê²ì¼ ì ìë¤!(ì¬ê¸°ì "ë¹ì ì´ ì¢ìíë ì´ë¤ ê²"ì´ë
+ ë¹ì ì´ <literal>org.hibernate.usertype.UserCollectionType</literal>ì ëí 구íì ìì±í´ì¼ í¨ì ì미íë¤.)
+ </para>
+
+ <para>
+ ì°ë¦¬ê° <literal>HashSet</literal>ì ì¸ì¤í´ì¤ë¥¼ ê°ì§ ì¸ì¤í´ì¤ ë³ì를 ì´ê¸°í ìì¼°ë ë°©ë²ì 주목íë¼. ì´ê²ì ìë¡ì´
+ ì´ê¸°í ë(ë¹-ìì) ì¸ì¤í´ì¤ë¤ì ê°ì§ ì½ë ì
ê° íë¡í¼í°ë¤ì ì´ê¸°í ìí¤ë ìµì ì ë°©ë²ì´ë¤. ë¹ì ì´ -ì를 ë¤ì´
+ <literal>persist()</literal>를 í¸ì¶íì¬- ì¸ì¤í´ì¤ë¥¼ ììí ìí¬ ë Hibernateë ì¤ì ë¡ <literal>HashSet</literal>ì
+ <literal>Set</literal>ì ëí Hibernate ìì ì 구íì ì¸ì¤í´ì¤ë¡ ëì²´ìí¬ ê²ì´ë¤. ë¤ìê³¼ ê°ì ì¤ë¥ë¤ì ê´ì°°íë¼:
+ </para>
+
+ <programlisting><![CDATA[Cat cat = new DomesticCat();
+Cat kitten = new DomesticCat();
+....
+Set kittens = new HashSet();
+kittens.add(kitten);
+cat.setKittens(kittens);
+session.persist(cat);
+kittens = cat.getKittens(); // Okay, kittens collection is a Set
+(HashSet) cat.getKittens(); // Error!]]></programlisting>
+
+ <para>
+ Hibernateì ìí´ ëì
ë ìì ì½ë ì
ë¤ì ì¸í°íì´ì¤ íì
ì ë°ë¼ <literal>HashMap</literal>, <literal>HashSet</literal>,
+ <literal>TreeMap</literal>, <literal>TreeSet</literal> ëë <literal>ArrayList</literal>ì ê°ì´ íìíë¤.
+ </para>
+
+ <para>
+ ì½ë ì
ì¸ì¤í´ì¤ë¤ì value íì
ë¤ì ê°ì§ íµìì í¹ì§ì ê°ëë¤. ê·¸ê²ë¤ì ìì ê°ì²´ì ìí´ ì°¸ì¡°ë ë ìëì ì¼ë¡ ììí ëê³
+ 참조 í´ì ë ë ìëì ì¼ë¡ ìì ëë¤. ë§ì¼ íëì ì½ë ì
ì´ íëì ìì ê°ì²´ë¡ë¶í° ë ë¤ë¥¸ ìì ê°ì²´ë¡ ì ë¬ë ë, ê·¸ê²ì ììë¤ì
+ íëì í
ì´ë¸ë¡ë¶í° ë¤ë¥¸ í
ì´ë¸ë¡ ì´ëë ì ìë¤. ë ê°ì ìí°í°ë¤ì ëì¼í ì½ë ì
ì¸ì¤í´ì¤ì ëí 참조를 ê³µì íì§ ìëë¤.
+ 기본 ê´ê³í 모í ë문ì ì½ë ì
ê° íë¡í¼í°ë¤ì null ê° ì미ë¤ì ì§ìíì§ ìëë¤; Hibernateë null ì½ë ì
참조ì 공백ì
+ ì½ë ì
ì¬ì´ë¥¼ êµ¬ë³ ì§ì§ ìëë¤.
+ </para>
+
+ <para>
+ ë¹ì ì ì´ê²ì ì´ë¤ ê²ì ëí´ ë무 ë§ì´ ê±±ì íì§ ììë ë ê²ì´ë¤. ë¹ì ì´ íµìì ìë° ì½ë ì
ë¤ì ì¬ì©íë ê²ê³¼ ëì¼í ë°©ë²ì¼ë¡
+ ìì ì½ë ì
ë¤ì ì¬ì©íë¼. ë¨ì§ ë¹ì ì´ ìë°©í¥ ì°ê´ê´ê³ë¤ì ëí ì미를 íì¤í ì´í´íëë¡ íë¼(ëì¤ì ë
¼ìë¨).
+ </para>
+
+ </sect1>
+
+ <sect1 id="collections-mapping" revision="4">
+ <title>ì½ë ì
매íë¤</title>
+
+ <para>
+ ì½ë ì
ì 매ííëë° ì¬ì©ëë Hiberante 매í ììë ì¸í°íì´ì¤ì íì
ì ìì¡´íë¤. ì를 ë¤ì´<literal><set></literal>
+ ììë <literal>Set</literal> íì
ì 매í íë¡í¼í°ë¤ì ì¬ì©ëë¤
+ </para>
+
+ <programlisting><![CDATA[<class name="Product">
+ <id name="serialNumber" column="productSerialNumber"/>
+ <set name="parts">
+ <key column="productSerialNumber" not-null="true"/>
+ <one-to-many class="Part"/>
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ <literal><set></literal>ê³¼ë ë³ëë¡, ëí <literal><list></literal>, <literal><map></literal>,
+ <literal><bag></literal>, <literal><array></literal>, ê·¸ë¦¬ê³ <literal><map></literal>
+ 매í ììë¤ì´ ì¡´ì¬íë¤. <literal><map></literal> ììê° ëíì ì´ë¤:
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="mappingcollection1" coords="2 65"/>
+ <area id="mappingcollection2" coords="3 65"/>
+ <area id="mappingcollection3" coords="4 65"/>
+ <area id="mappingcollection4" coords="5 65"/>
+ <area id="mappingcollection5" coords="6 65"/>
+ <area id="mappingcollection6" coords="7 65"/>
+ <area id="mappingcollection7" coords="8 65"/>
+ <area id="mappingcollection8" coords="9 65"/>
+ <area id="mappingcollection9" coords="10 65"/>
+ <area id="mappingcollection10" coords="11 65"/>
+ <area id="mappingcollection11" coords="12 65"/>
+ <area id="mappingcollection12" coords="13 65"/>
+ <area id="mappingcollection13" coords="14 65"/>
+ <area id="mappingcollection14" coords="15 65"/>
+ </areaspec>
+ <programlisting><![CDATA[<map
+ name="propertyName"
+ table="table_name"
+ schema="schema_name"
+ lazy="true|extra|false"
+ inverse="true|false"
+ cascade="all|none|save-update|delete|all-delete-orphan|delete-orphan"
+ sort="unsorted|natural|comparatorClass"
+ order-by="column_name asc|desc"
+ where="arbitrary sql where condition"
+ fetch="join|select|subselect"
+ batch-size="N"
+ access="field|property|ClassName"
+ optimistic-lock="true|false"
+ mutable="true|false"
+ node="element-name|."
+ embed-xml="true|false"
+>
+
+ <key .... />
+ <map-key .... />
+ <element .... />
+</map>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="mappingcollection1">
+ <para>
+ <literal>name</literal> ì½ë ì
íë¡í¼í° ì´ë¦
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection2">
+ <para>
+ <literal>table</literal> (ìµì
- ëí´í¸ë íë¡í¼í° ì´ë¦)
+ ì½ë ì
í
ì´ë¸ì ì´ë¦(one-to-many ì°ê´ê´ê³ë¤ì ëí´ìë ì¬ì©ëì§ ìì)
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection3">
+ <para>
+ <literal>schema</literal> (ìµì
)
+ ë£¨í¸ ìì ìì ì ì¸ë ì¤í¤ë§ë¥¼ ì¤ë²ë¼ì´ë ìí¤ë í
ì´ë¸ ì¤í¤ë§ì ì´ë¦
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection4">
+ <para>
+ <literal>lazy</literal> (ìµì
- ëí´í¸ë <literal>true</literal>)ë
+ lazy íì¹ì ì¬ì© ë¶ê°ë¥íëë¡ íê³ ê·¸ ì°ê´ì´ íì eagerly íì¹ë¨ì ì§ì íëë° , ëë ëë¶ë¶ì
+ ì°ì°ë¤ì´ ì½ë ì
ì ì´ê¸°íìí¤ì§ ìë ê³³ìì "extra-lazy" íì¹ì ì´ì© ê°ë¥íëë¡ íëë°(ë§¤ì° í° ì½ë ì
ë¤ì
+ ì ë¹í¨) ì¬ì©ë ì ìë¤
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection5">
+ <para>
+ <literal>inverse</literal> (ìµì
- ëí´í¸ë <literal>false</literal>)
+ ì´ ì½ë ì
ì ìë°©í¥ ì°ê´ê´ê³ì "inverse" ë(end)ì¼ë¡ íìíë¤
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection6">
+ <para>
+ <literal>cascade</literal> (ìµì
- ëí´í¸ë <literal>none</literal>)
+ ì¤í¼ë ì´ì
ë¤ì´ ìì ìí°í°ë¤ì ëí´ ì¼ì¤ì¼ì´ëíë ê²ì ì´ì© ê°ë¥íê² íë¤
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection7">
+ <para>
+ <literal>sort</literal> (ìµì
)
+ <literal>natural</literal> ì ë ¬ ììë¡ ì ë ¬ë(sorted) ì½ë ì
ëë 주ì´ì§ comparator í´ëì¤ë¥¼ ì§ì íë¤
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection8">
+ <para>
+ <literal>order-by</literal> (ìµì
, JDK1.4ììë§)
+ <literal>asc</literal> ëë <literal>desc</literal> ìµì
ê³¼ í¨ê» <literal>Map</literal>,
+ <literal>Set</literal> ëë bagì ë°ë³µ ìì를 ì ìíë í
ì´ë¸ 컬ë¼(ëë 컬ë¼ë¤)ì ì§ì íë¤
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection9">
+ <para>
+ <literal>where</literal> (ìµì
)
+ ì½ë ì
ì ê²ìíê±°ë ì ê±°í ë ì¬ì©ë ììì ì¸ SQL <literal>WHERE</literal> ì¡°ê±´ì ì§ì íë¤
+ (ì½ë ì
ì´ ì¤ì§ ì´ì© ê°ë¥í ë°ì´í°ì ë¶ë¶ì§í© ë§ì í¬í¨í ê²½ì°ì ì ì©íë¤)
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection10">
+ <para>
+ <literal>fetch</literal> (ìµì
, ëí´í¸ë <literal>select</literal>)
+ outer-join íì¹, sequential select íì¹, ê·¸ë¦¬ê³ sequential subselect íì¹ ì¬ì´ìì ì ííë¼.
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection11">
+ <para>
+ <literal>batch-size</literal> (ìµì
, ëí´í¸ë <literal>1</literal>)
+ ì´ ì½ë ì
ì lazily fetching ì¸ì¤í´ì¤ì ëí´ "ë°°ì¹ ì¬ì´ì¦"를 ì§ì íë¼.
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection12">
+ <para>
+ <literal>access</literal> (ìµì
- ëí´í¸ë <literal>property</literal>):
+ Hibernateê° ì½ë ì
íë¡í¼í° ê°ì ì ê·¼íëë° ì¬ì©í ë°©ë.
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection13">
+ <para>
+ <literal>optimistic-lock</literal> (ìµì
- ëí´í¸ë <literal>true</literal>):
+ ì½ë ì
ì ìíì ëí ë³ê²½ë¤ì´ ìì íë ìí°í°ì ë²ì ì ì¦ê°ë¡ ê·ê²°ë ê²ì¸ì§ë¥¼ ì§ì íë¤.
+ (one to many ì°ê´ë¤ì ëí´, ì´ ì¤ì ì ì¬ì© ë¶ê°ë¥íê² íë ê²ì´ ì¢
ì¢
í©ë¹íë¤.)
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection14">
+ <para>
+ <literal>mutable</literal> (ìµì
- ëí´í¸ë <literal>true</literal>):
+ <literal>false</literal> ê°ì ì½ë ì
ì ììë¤ì´ ê²°ì½ ë³ê²½ëì§ ììì ì§ì íë¤.
+ (ëªëª ê²½ì°ë¤ìì ë§ì´ë í¼í¬ë¨¼ì¤ ìµì í).
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <sect2 id="collections-foreignkeys" >
+ <title>ì½ë ì
foreign í¤ë¤</title>
+
+ <para>
+ ì½ë ì
ì¸ì¤í´ì¤ë¤ì ê·¸ ì½ë ì
ì ìì íë ìí°í°ì foreign í¤ì ìí´ ë°ì´í°ë² ì´ì¤ ë´ìì 구ë³ì§ìì§ë¤. ì´ foreign í¤ë
+ ê·¸ ì½ë ì
í
ì´ë¸ì <emphasis>ì½ë ì
í¤ ì»¬ë¼</emphasis> (ëë 컬ë¼ë¤)ë¡ì 참조ëë¤. ê·¸ ì½ë ì
í¤ ì»¬ë¼ì
+ <literal><key></literal> ììì ìí´ ë§¤íëë¤.
+ </para>
+
+ <para>
+ foreign í¤ ì»¬ë¼ì ëí null íì© ê°ë¥ 컨ì¤í¸ë ì¸í¸ê° ì¡´ì¬í ì ìë¤. ëë¶ë¶ì ì½ë ì
ë¤ì ëí´, ì´ê²ì´ ë¹ì°í ìë°ëë¤.
+ ë¨ë°©í¥ one to many ì°ê´ë¤ì ê²½ì°, foreign í¤ë ëí´í¸ë¡ null íì© ê°ë¥íì¬ì, ë¹ì ì <literal>not-null="true"</literal>를
+ ì§ì í íìê° ìì ì ìë¤.
+ </para>
+
+ <programlisting><![CDATA[<key column="productSerialNumber" not-null="true"/>]]></programlisting>
+
+ <para>
+ foreign í¤ ì»¨ì¤í¸ë ì¸í¸ë <literal>ON DELETE CASCADE</literal>를 ì¬ì©í ìë ìë¤.
+ </para>
+
+ <programlisting><![CDATA[<key column="productSerialNumber" on-delete="cascade"/>]]></programlisting>
+
+ <para>
+ <literal><key></literal> ììì ëí ì ì²´ ì ìë ì ì¥ì ë³´ë¼.
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-elements" >
+ <title>ì½ë ì
ììë¤</title>
+
+ <para>
+ ì½ë ì
ë¤ì 모ë 기본 íì
ë¤, ì»´í¬ëí¸ë¤, ê·¸ë¦¬ê³ ë¬¼ë¡ ë¤ë¥¸ ìí°í°ë¤ì ëí 참조ë¤ì í¬í¨íì¬ ê±°ì ëë¶ë¶ì
+ ì´ë¤ ë¤ë¥¸ Hibernate íì
ì í¬í¨í ìë ìë¤. ì´ê²ì ì¤ìí 구ë¶ì´ë¤: ì½ë ì
ë´ì ìë ê°ì²´ë "ê°(value)"
+ ìë¯¸ë¡ ì²ë¦¬ë ìë ìê±°ë(ê·¸ê²ì ìëª
주기ë ì½ë ì
ìì ìì ìì¡´íë¤) ê·¸ê²ì ê·¸ê² ìì ì ìëª
주기를 ê°ì§
+ ë ë¤ë¥¸ ìí°í°ì ëí ì°¸ì¡°ì¼ ì ìë¤. íìì ê²½ì°, ë ê°ì ê°ì²´ë¤ ì¬ì´ì "ë§í¬" ë§ì´ ê·¸ ì½ë ì
ì ìí´ ìì ë
+ ìíë¡ ê°ì£¼ëë¤.
+ </para>
+
+ <para>
+ í¬í¨ë íì
ì ì½ë ì
ìì íì
ì¼ë¡ì ë¶ë ¤ì§ë¤. ì½ë ì
ììë¤ì <literal><element></literal>
+ ëë <literal><composite-element></literal>ì ìí´ ë§¤íëê±°ë, ìí°í° 참조ë¤ì ê²½ì°ì
+ <literal><one-to-many></literal> ëë <literal><many-to-many></literal>ë¡ì
+ 매íëë¤. ìì ë ê°ë value ì미를 ê°ì§ ììë¤ì 매íìí¤ê³ , ë¤ì ëê°ë ìí°í° ì°ê´ë¤ì 매ííëë° ì¬ì©ëë¤.
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-indexed">
+ <title>ì¸ë±ì± ë ì½ë ì
ë¤</title>
+
+ <para>
+ set ê³¼ bag ì미ë¤ì ê°ì§ ê²ë¤ì ì ì¸íë©´, 모ë ì½ë ì
매íë¤ì ì½ë ì
í
ì´ë¸ ë´ì <emphasis>ì¸ë±ì¤ 컬ë¼</emphasis>-
+ ë°°ì´ ì¸ë±ì¤, ëë <literal>List</literal> ì¸ë±ì¤ ëë <literal>Map</literal> í¤ë¡ 매íëë 컬ë¼-ì íìë¡ íë¤.
+ <literal>Map</literal>ì ì¸ë±ì¤ë <literal><map-key></literal>ë¡ ë§¤íë, ì´ë¤ 기본 íì
ì¼ ì ìê³ ,
+ ê·¸ê²ì <literal><map-key-many-to-many></literal>ë¡ ë§¤íë ìí°í° ì°¸ì¡°ì¼ ì ìê±°ë, ê·¸ê²ì
+ <literal><composite-map-key></literal>ë¡ ë§¤íë composite íì
ì¼ ì ìë¤. ë°°ì´ ëë 리ì¤í¸ì ì¸ë±ì¤ë
+ íì <literal>integer</literal> íì
ì´ê³ <literal><list-index></literal> ìì를 ì¬ì©íì¬ ë§¤íëë¤.
+ 매íë 컬ë¼ì ìì°¨ì ì¸ ì ìë¤ì í¬í¨íë¤(ëí´í¸ë¡ 0ìì ììíë ë²í¸ê° ë¶ì¬ì§).
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="index1" coords="2 45"/>
+ <area id="index2" coords="3 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<list-index
+ column="column_name"
+ base="0|1|..."/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="index1">
+ <para>
+ <literal>column_name</literal> (íì):
+ ì½ë ì
ì¸ë±ì¤ ê°ë¤ì ë³´ê´íë 컬ë¼ì ì´ë¦.
+ </para>
+ </callout>
+ <callout arearefs="index1">
+ <para>
+ <literal>base</literal> (ìµì
, ëí´í¸ë <literal>0</literal>):
+ 리ì¤í¸ ëë ë°°ì´ì 첫 ë²ì§¸ ììì ëìíë ì¸ë±ì¤ 컬ë¼ì ê°.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <programlistingco>
+ <areaspec>
+ <area id="mapkey1" coords="2 45"/>
+ <area id="mapkey2" coords="3 45"/>
+ <area id="mapkey3" coords="4 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<map-key
+ column="column_name"
+ formula="any SQL expression"
+ type="type_name"
+ node="@attribute-name"
+ length="N"/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="mapkey1">
+ <para>
+ <literal>column</literal> (ìµì
):
+ ì½ë ì
ì¸ë±ì¤ ê°ë¤ì ë³´ê´íë 컬ë¼ì ì´ë¦.
+ </para>
+ </callout>
+ <callout arearefs="mapkey2">
+ <para>
+ <literal>formula</literal> (ìµì
):
+ mapì í¤ë¥¼ íê°íëë° ì¬ì©ëë SQL formula.
+ </para>
+ </callout>
+ <callout arearefs="mapkey3">
+ <para>
+ <literal>type</literal> (íì): The type of the map í¤ë¤ì íì
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <programlistingco>
+ <areaspec>
+ <area id="indexmanytomany1" coords="2 45"/>
+ <area id="indexmanytomany2" coords="3 45"/>
+ <area id="indexmanytomany3" coords="3 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<map-key-many-to-many
+ column="column_name"
+ formula="any SQL expression"
+ class="ClassName"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="indexmanytomany1">
+ <para>
+ <literal>column</literal> (ìµì
):
+ ì½ë ì
ì¸ë±ì¤ ê°ë¤ì ëí foreign í¤ ì»¬ë¼ì ì´ë¦.
+ </para>
+ </callout>
+ <callout arearefs="indexmanytomany2">
+ <para>
+ <literal>formula</literal> (ìµì
):
+ mapì foreign í¤ë¥¼ íê°íëë° ì¬ì©ëë SQL formula.
+ </para>
+ </callout>
+ <callout arearefs="indexmanytomany3">
+ <para>
+ <literal>class</literal> (íì): map í¤ë¡ì ì¬ì©ëë ìí°í° í´ëì¤.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+
+ <para>
+ ë§ì¼ ë¹ì ì í
ì´ë¸ì´ ì¸ë±ì¤ 컬ë¼ì ê°ì§ ìê³ , ë¹ì ì´ ì¬ì í íë¡í¼í° íì
ì¼ë¡ì <literal>List</literal>를 ì¬ì©íê³ ì
+ ìí ê²½ì°, ë¹ì ì ê·¸ íë¡í¼í°ë¥¼ Hibernate <emphasis><bag></emphasis>ì¼ë¡ì 매íí´ì¼ íë¤. bagì´
+ ë°ì´í°ë² ì´ì¤ë¡ë¶í° ê²ìë ë ê·¸ê²ì ê·¸ê²ì ìì를 ë³´ì íì§ ìì§ë§, ê·¸ê²ì ì íì ì¼ë¡ ì ë ¬(sorting)ëê±°ë orderingë
+ ìë ìë¤.
+ </para>
+
+ </sect2>
+
+ <para>
+ ë§ì ê³µíµë ê´ê³í 모íë¤ì ë¤ë£¨ë, ì½ë ì
ë¤ì ëí´ ìì±ë ì ìë 매íë¤ì ììì´ ê½¤ ì¡´ì¬íë¤. ì¬ë¬ê°ì§ 매í ì ì¸ë¤ì´ ë°ì´í°ë² ì´ì¤
+ í
ì´ë¸ë¤ë¡ ë³íëë ë°©ë²ì ë¹ì ì´ ëë¼ë ¤ë©´ ì¤í¤ë§ ìì± ëêµ¬ë¡ ì¤íí ê²ì ì°ë¦¬ë ì ìíë¤.
+ </para>
+
+ <sect2 id="collections-ofvalues" revision="2">
+ <title>ê°ë¤ì ê°ì§ ì½ë ì
ë¤ê³¼ many-to-many ì°ê´ë¤</title>
+
+ <para>
+ ì´ë¤ ê°ë¤ì ê°ì§ ì½ë ì
ëë many-to-many ì°ê´ì foreign í¤ ì»¬ë¼ì´ë 컬ë¼ë¤, <emphasis>ì½ë ì
ìì 컬ë¼</emphasis>ì´ë
+ 컬ë¼ë¤ ê·¸ë¦¬ê³ ê°ë¥íë©´ ì¸ë±ì¤ 컬ë¼ë¤ì´ë 컬ë¼ë¤ì ê°ì§ ì ì© <emphasis>ì½ë ì
í
ì´ë¸</emphasis>ì íìë¡ íë¤.
+ </para>
+
+ <para>
+ ê°ë¤ì ê°ì§ ì½ë ì
ì ê²½ì°, ì°ë¦¬ë <literal><element></literal> í그를 ì¬ì©íë¤.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="element1b" coords="2 50"/>
+ <area id="element2b" coords="3 50"/>
+ <area id="element3b" coords="4 50"/>
+ </areaspec>
+ <programlisting><![CDATA[<element
+ column="column_name"
+ formula="any SQL expression"
+ type="typename"
+ length="L"
+ precision="P"
+ scale="S"
+ not-null="true|false"
+ unique="true|false"
+ node="element-name"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="element1b">
+ <para>
+ <literal>column</literal> (ìµì
): ì½ë ì
ìì ê°ë¤ì ìì íë 컬ë¼ì ì´ë¦.
+ </para>
+ </callout>
+ <callout arearefs="element2b">
+ <para>
+ <literal>formula</literal> (ìµì
): ìì를 íê°íëë° ì¬ì©ëë SQL formula.
+ </para>
+ </callout>
+ <callout arearefs="element3b">
+ <para>
+ <literal>type</literal> (íì): ì½ë ì
ììì íì
.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ <emphasis>many-to-many association</emphasis> ì°ê´ì <literal><many-to-many></literal> ìì를
+ ì¬ì©íì¬ ì§ì ëë¤.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="manytomany1" coords="2 60"/>
+ <area id="manytomany2" coords="3 60"/>
+ <area id="manytomany3" coords="4 60"/>
+ <area id="manytomany4" coords="5 60"/>
+ <area id="manytomany5" coords="6 60"/>
+ <area id="manytomany6" coords="7 60"/>
+ <area id="manytomany7" coords="8 60"/>
+ <area id="manytomany8" coords="9 60"/>
+ </areaspec>
+ <programlisting><![CDATA[<many-to-many
+ column="column_name"
+ formula="any SQL expression"
+ class="ClassName"
+ fetch="select|join"
+ unique="true|false"
+ not-found="ignore|exception"
+ entity-name="EntityName"
+ property-ref="propertyNameFromAssociatedClass"
+ node="element-name"
+ embed-xml="true|false"
+ />]]></programlisting>
+ <calloutlist>
+ <callout arearefs="manytomany1">
+ <para>
+ <literal>column</literal> (ìµì
): ìì foreign í¤ ì»¬ë¼ì ì´ë¦.
+ </para>
+ </callout>
+ <callout arearefs="manytomany2">
+ <para>
+ <literal>formula</literal> (ìµì
): ìì foreign í¤ ê°ì íê°íëë° ì¬ì©ëë SQL formula.
+ </para>
+ </callout>
+ <callout arearefs="manytomany3">
+ <para>
+ <literal>class</literal> (íì): ì°ê´ë í´ëì¤ì ì´ë¦.
+ </para>
+ </callout>
+ <callout arearefs="manytomany4">
+ <para>
+ <literal>fetch</literal> (ìµì
- ëí´í¸ë <literal>join</literal>):
+ ì´ ì°ê´ì ëí´ outer-join íì¹ ëë sequential select íì¹ì ì´ì© ê°ë¥íê² ë§ë ë¤. ì´ê²ì í¹ë³í ê²½ì°ì´ë¤;
+ ìí°í° ê·¸ë¦¬ê³ ë¤ë¥¸ ìí°í°ë¤ê³¼ ê·¸ê²ì many-to-many ê´ê³ë¤ì ëí (íëì <literal>SELECT</literal>
+ ë´ìì) ì ì²´ eager íì¹ì ê²½ì°, ë¹ì ì ì½ë ì
ê·¸ ìì²´ì ëí´ì ë¿ë§ ìëë¼ ë´í¬ë ìì <literal><many-to-many></literal>
+ ìì ì´ ìì±ì ëí´ <literal>join</literal> íì¹ì ì´ì© ê°ë¥íê² í ê²ì´ë¤.
+ </para>
+ </callout>
+ <callout arearefs="manytomany5">
+ <para>
+ <literal>unique</literal> (ìµì
):
+ foreign-key 컬ë¼ì ëí ì ì¼ ì»¨ì¤í¸ë ì¸í¸ì DDL ìì±ì ê°ë¥íëë¡ íë¤. ì´ê²ì ì°ê´ ë¤ì¤ì±
+ (association multiplicity)ì í¨ì¨ì ì¼ë¡ one to manyë¡ ë§ë ë¤.
+ </para>
+ </callout>
+ <callout arearefs="manytomany6">
+ <para>
+ <literal>not-found</literal> (ìµì
- ëí´í¸ë <literal>exception</literal>):
+ ëë½ë íë¤ì 참조íë foreign í¤ë¤ì´ ì´ë»ê² ì²ë¦¬ë ê²ì¸ì§ë¥¼ ì§ì íë¤:
+ <literal>ignore</literal>ë ëë½ë í íì í ê°ì ì°ê´ì¼ë¡ ë¤ë£° ê²ì´ë¤.
+ </para>
+ </callout>
+ <callout arearefs="manytomany7">
+ <para>
+ <literal>entity-name</literal> (ìµì
): <literal>class</literal>ì ëí íëì ëìì¼ë¡ì,
+ ì°ê´ë í´ëì¤ì ìí°í° ì´ë¦.
+ </para>
+ </callout>
+ <callout arearefs="manytomany8">
+ <para>
+ <literal>property-ref</literal>: (ìµì
) ì´ foreign í¤ì ì¡°ì¸ë ì°ê´ í´ëì¤ì íë¡í¼í°ì ì´ë¦.
+ ì§ì ëì§ ìì ê²½ì°, ì°ê´ í´ëì¤ì íë¼ì´ë¨¸ë¦¬ í¤ê° ì¬ì©ëë¤.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ ëªëª ìì ë¤, 먼ì 문ìì´ë¤ì ê°ì§ set:
+ </para>
+
+ <programlisting><![CDATA[<set name="names" table="person_names">
+ <key column="person_id"/>
+ <element column="person_name" type="string"/>
+</set>]]></programlisting>
+
+ <para>
+ (<literal>order-by</literal> ìì±ì ìí´ ê²°ì ëë ë°ë³µ ìì를 ê°ì§) ì ìë¤ì í¬í¨íë bag :
+ </para>
+
+ <programlisting><![CDATA[<bag name="sizes"
+ table="item_sizes"
+ order-by="size asc">
+ <key column="item_id"/>
+ <element column="size" type="integer"/>
+</bag>]]></programlisting>
+
+ <para>
+ ìí°í°ë¤ì ê°ì§ ë°°ì´ - ì´ ê²½ì°ì, many to many ì°ê´ :
+ </para>
+
+ <programlisting><![CDATA[<array name="addresses"
+ table="PersonAddress"
+ cascade="persist">
+ <key column="personId"/>
+ <list-index column="sortOrder"/>
+ <many-to-many column="addressId" class="Address"/>
+</array>]]></programlisting>
+
+ <para>
+ ë ì§ë¤ì ëí 문ìì´ ì¸ë±ì¤ë¤ì ê°ì§ map :
+ </para>
+
+ <programlisting><![CDATA[<map name="holidays"
+ table="holidays"
+ schema="dbo"
+ order-by="hol_name asc">
+ <key column="id"/>
+ <map-key column="hol_name" type="string"/>
+ <element column="hol_date" type="date"/>
+</map>]]></programlisting>
+
+ <para>
+ ì»´í¬ëí¸ë¤ì 리ì¤í¸(ë¤ì ì¥ìì ë
¼ìë¨):
+ </para>
+
+ <programlisting><![CDATA[<list name="carComponents"
+ table="CarComponents">
+ <key column="carId"/>
+ <list-index column="sortOrder"/>
+ <composite-element class="CarComponent">
+ <property name="price"/>
+ <property name="type"/>
+ <property name="serialNumber" column="serialNum"/>
+ </composite-element>
+</list>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="collections-onetomany">
+ <title>One-to-many ì°ê´ë¤</title>
+
+ <para>
+ <emphasis>one to many ì°ê´</emphasis>ì ì¤ì¬íë ì½ë ì
í
ì´ë¸ ìì´ foreign í¤ë¥¼ íµí´ ë ê°ì í´ëì¤ë¤ì í
ì´ë¸ë¤ì
+ ì°ê²°ìí¨ë¤. ì´ ë§¤íì íµìì ìë° ì½ë ì
ë¤ì ì´ë¤ ì미를 ìì¤íë¤:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ í¬í¨ë ìí°í° í´ëì¤ì ì¸ì¤í´ì¤ë ê·¸ ì½ë ì
ì íë ì´ìì ì¸ì¤í´ì¤ì ìíì§ ìì ì ìë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ í¬í¨ë ìí°í° í´ëì¤ì ì¸ì¤í´ì¤ë ì½ë ì
ì¸ë±ì¤ì íë ì´ìì ê°ìì ëíëì§ ìì ì ìë¤
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ <literal>Product</literal>ë¡ë¶í° <literal>Part</literal>ë¡ì ì°ê´ì foreign í¤ ì»¬ë¼ê³¼ <literal>Part</literal>
+ í
ì´ë¸ì ëí ì¸ë±ì¤ 컬ë¼ì ì¡´ì¬ë¥¼ íìë¡ íë¤. <literal><one-to-many></literal> íê·¸ë ì´ê²ì´ one to many
+ ì°ê´ìì ëíë¸ë¤.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="onetomany1" coords="2 60"/>
+ <area id="onetomany2" coords="3 60"/>
+ <area id="onetomany3" coords="4 60"/>
+ </areaspec>
+ <programlisting><![CDATA[<one-to-many
+ class="ClassName"
+ not-found="ignore|exception"
+ entity-name="EntityName"
+ node="element-name"
+ embed-xml="true|false"
+ />]]></programlisting>
+ <calloutlist>
+ <callout arearefs="onetomany1">
+ <para>
+ <literal>class</literal> (íì): ì°ê´ë í´ëì¤ì ì´ë¦.
+ </para>
+ </callout>
+ <callout arearefs="onetomany2">
+ <para>
+ <literal>not-found</literal> (ìµì
- ëí´í¸ë <literal>exception</literal>):
+ ëë½ë íë¤ì 참조íë ìºìë ìë³ìë¤ì´ ì´ë»ê² ì²ë¦¬ë ê²ì¸ì§ë¥¼ ì§ì íë¤:
+ <literal>ignore</literal>ë ëë½ë í íì í ê°ì ì°ê´ì¼ë¡ ë¤ë£° ê²ì´ë¤.
+ </para>
+ </callout>
+ <callout arearefs="onetomany3">
+ <para>
+ <literal>entity-name</literal> (ìµì
): <literal>class</literal>ì ëí ëìì¼ë¡ì,
+ ì°ê´ë í´ëì¤ì ìí°í° ì´ë¦.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ <literal><one-to-many></literal> ììë ì´ë¤ 컬ë¼ë¤ì ì ì¸íëë° íìíì§ ììì 주목íë¼. ì´ëê°ì
+ <literal>table</literal> ì´ë¦ì ì§ì íë ê²ë íìì ì´ì§ ìë¤.
+ </para>
+
+ <para>
+ <emphasis>ë§¤ì° ì¤ìí ë
¸í¸</emphasis>: ë§ì¼ <literal><one-to-many></literal> ì°ê´ì foreign í¤ ì»¬ë¼ì´
+ <literal>NOT NULL</literal>ë¡ ì ì¸ë ê²½ì°, ë¹ì ì <literal><key></literal> 매íì <literal>not-null="true"</literal>ë¡
+ ì ì¸í´ì¼ íê±°ë <literal>inverse="true"</literal>ë¡ ë§í¬ë ì½ë ì
매íì ê°ì§ <emphasis>ìë°©í¥ ì°ê´ì ì¬ì©í´ì¼ íë¤</emphasis>.
+ ìë°©í¥ ì°ê´ë¤ì ëí ë
¼ìë ì´ ì¥ì ë·ë¶ë¶ì ë³´ë¼.
+ </para>
+
+ <para>
+ ì´ ìì ë nameì¼ë¡ <literal>Part</literal> ìí°í°ë¤ì ê°ì§ mapì ë³´ì¬ì¤ë¤(ì¬ê¸°ì <literal>partName</literal>ì
+ <literal>Part</literal>ì ìì íë¡í¼í°ì´ë¤). formula-ê¸°ë° indexì ì¬ì©ì 주목íë¼.
+ </para>
+
+ <programlisting><![CDATA[<map name="parts"
+ cascade="all">
+ <key column="productId" not-null="true"/>
+ <map-key formula="partName"/>
+ <one-to-many class="Part"/>
+</map>]]></programlisting>
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="collections-advancedmappings">
+ <title>ê°ì ë ì½ë ì
매íë¤</title>
+
+ <sect2 id="collections-sorted" revision="2">
+ <title>Sorted ì½ë ì
ë¤</title>
+
+ <para>
+ Hibernateë <literal>java.util.SortedMap</literal>ê³¼ <literal>java.util.SortedSet</literal>를 구ííë
+ ì½ë ì
ë¤ì ì§ìíë¤. ë¹ì ì 매í íì¼ ìì íëì comparator를 ì§ì í´ì¼ íë¤:
+ </para>
+
+ <programlisting><![CDATA[<set name="aliases"
+ table="person_aliases"
+ sort="natural">
+ <key column="person"/>
+ <element column="name" type="string"/>
+</set>
+
+<map name="holidays" sort="my.custom.HolidayComparator">
+ <key column="year_id"/>
+ <map-key column="hol_name" type="string"/>
+ <element column="hol_date" type="date"/>
+</map>]]></programlisting>
+
+ <para>
+ <literal>sort</literal> ìì±ì íì©ëë ê°ë¤ì <literal>unsorted</literal>, <literal>natural</literal>,
+ ê·¸ë¦¬ê³ <literal>java.util.Comparator</literal>를 구ííë í´ëì¤ì ì´ë¦ì´ë¤.
+ </para>
+
+ <para>
+ Sorted ì½ë ì
ë¤ì <literal>java.util.TreeSet</literal> ëë <literal>java.util.TreeMap</literal>ì²ë¼ íëíë¤.
+ </para>
+
+ <para>
+ ë§ì¼ ë¹ì ì´ ë°ì´í°ë² ì´ì¤ ê·¸ ìì²´ê° ì½ë ì
ììë¤ì ììì§ì°ëë¡(order)ìí ê²½ì° <literal>set</literal>,
+ <literal>bag</literal> ëë<literal>map</literal> 매íë¤ì ëí´ <literal>order-by</literal> ìì±ì ì¬ì©íë¼.
+ ì´ í´ê²°ì±
ì JDK 1.4 ì´ìì ë²ì ììë§ ì´ì© ê°ë¥íë¤(ê·¸ê²ì <literal>LinkedHashSet</literal> ëë <literal>LinkedHashMap</literal>ì
+ ì¬ì©íì¬ êµ¬íëë¤). ì´ê²ì ë©ëª¨ë¦¬ ë´ê° ìë, SQL ì§ì ë´ìì ììì§ì(ordering)ì ìííë¤.
+ </para>
+
+ <programlisting><![CDATA[<set name="aliases" table="person_aliases" order-by="lower(name) asc">
+ <key column="person"/>
+ <element column="name" type="string"/>
+</set>
+
+<map name="holidays" order-by="hol_date, hol_name">
+ <key column="year_id"/>
+ <map-key column="hol_name" type="string"/>
+ <element column="hol_date type="date"/>
+</map>]]></programlisting>
+
+ <para>
+ <literal>order-by</literal> ìì±ì ê°ì HQL ììì§ì(ordering)ì´ ìëë¼ SQL ììì§ì(ordering)ìì ë
¸í¸íë¼!
+ </para>
+
+ <para>
+ ì°ê´ë¤ì ì½ë ì
<literal>filter()</literal>를 ì¬ì©íì¬ ì¤í ìì ì´ë¤ ììì criteria(기ì¤)ì ì¬ì©íì¬ ì ë ¬(sort)ë
+ ìë ìë¤.
+ </para>
+
+ <programlisting><![CDATA[sortedUsers = s.createFilter( group.getUsers(), "order by this.name" ).list();]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="collections-bidirectional" revision="1">
+ <title>ìë°©í¥ ì°ê´ë¤</title>
+
+ <para>
+ <emphasis>ìë°©í¥ ì°ê´</emphasis>ì ì°ê´ì ì "ë(end)ë¤"ë¡ë¶í° ë¤ë¹ê²ì´ì
ì íì©íë¤.
+ ë ê°ì§ ì¢
ë¥ì ìë°©í¥ ì°ê´ë¤ì´ ì§ìëë¤:
+
+ <variablelist>
+ <varlistentry>
+ <term>one-to-many</term>
+ <listitem>
+ <para>
+ í쪽 ëì set ëë bag ê°ì ê°ê³ , ë¤ë¥¸ 쪽 ê¸ì ë¨ì¼ ê°ì ê°ì§ ì°ê´
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>many-to-many</term>
+ <listitem>
+ <para>
+ ì ëìì set ëë bag ê°ì ê°ì§ ì°ê´
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ </para>
+
+ <para>
+ ë¹ì ì ëì¼í ë ê°ì many-to-many ì°ê´ë¤ì ëì¼í ë°ì´í°ë² ì´ì¤ í
ì´ë¸ë¡ ê°ë¨íê² ë§¤ííê³ í 쪽 ëì
+ <emphasis>inverse</emphasis>(ë¹ì ì ì íì íëì´ì§ë§, ê·¸ê²ì ì¸ë±ì±ë ì½ë ì
ì¼ ì ìë¤)ë¡ ì ì¸í¨ì¼ë¡ì¨ íëì ìë°©í¥
+ many-to-many ì°ê´ì ì§ì í ìë ìë¤.
+ </para>
+
+ <para>
+ ë¤ìì ìë°©í¥ many-to-many ì°ê´ì ê´í ìì ì´ë¤; ê°ê°ì ì¹´í
ê³ ë¦¬ë ë§ì ìì´í
ë¤ì ê°ì§ ì ìê³ ê°ê°ì ìì´í
ì ë§ì
+ ì¹´í
ê³ ë¦¬ë¤ ìì ìì ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[<class name="Category">
+ <id name="id" column="CATEGORY_ID"/>
+ ...
+ <bag name="items" table="CATEGORY_ITEM">
+ <key column="CATEGORY_ID"/>
+ <many-to-many class="Item" column="ITEM_ID"/>
+ </bag>
+</class>
+
+<class name="Item">
+ <id name="id" column="CATEGORY_ID"/>
+ ...
+
+ <!-- inverse end -->
+ <bag name="categories" table="CATEGORY_ITEM" inverse="true">
+ <key column="ITEM_ID"/>
+ <many-to-many class="Category" column="CATEGORY_ID"/>
+ </bag>
+</class>]]></programlisting>
+
+ <para>
+ ì°ê´ì inverse ë(end)ì ëí´ìë§ íí´ì§ ë³ê²½ë¤ì ììí ëì§ <emphasis>ìëë¤</emphasis>. ì´ê²ì Hibernateê°
+ 모ë ìë°©í¥ ì°ê´ì ëí´ ë©ëª¨ë¦¬ ë´ì ë ê°ì íìë¤ì ê°ëë¤ë ì ì ì미íë¤: Aë¡ë¶í° Bë¡ì íëì ë§í¬ì Bë¡ë¶í° Aë¡ì ë ë¤ë¥¸ ë§í¬.
+ ë§ì¼ ë¹ì ì´ ìë° ê°ì²´ 모íì ëí´ ê·¸ë¦¬ê³ ìë°ìì many-to-many ê´ê³ë¥¼ ìì±ìí¤ë ë°©ë²ì ëí´ ìê°íë©´ ì´ê²ì ì´í´íê¸°ê° ë ì½ë¤:
+ </para>
+
+ <programlisting><![CDATA[
+category.getItems().add(item); // The category now "knows" about the relationship
+item.getCategories().add(category); // The item now "knows" about the relationship
+
+session.persist(item); // The relationship won't be saved!
+session.persist(category); // The relationship will be saved]]></programlisting>
+
+ <para>
+ non-inverse 측ì ë©ëª¨ë¦¬ ë´ íìì ë°ì´í°ë² ì´ì¤ë¡ ì ì¥íëë° ì¬ì©ëë¤.
+ </para>
+
+ <para>
+ ë¹ì ì íëì one-to-many ì°ê´ì íëì many-to-one ì°ê´ì¼ë¡ì ëì¼í í
ì´ë¸ 컬ë¼(ë¤)ë¡ ë§¤ííê³ many-ê°ì ê°ì§ ë(end)ì
+ <literal>inverse="true"</literal>ë¡ ì ì¸í¨ì¼ë¡ì¨ íëì ìë°©í¥ ì°ê´ì ì ìí ìë ìë¤.
+ </para>
+
+ <programlisting><![CDATA[<class name="Parent">
+ <id name="id" column="parent_id"/>
+ ....
+ <set name="children" inverse="true">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+ </set>
+</class>
+
+<class name="Child">
+ <id name="id" column="child_id"/>
+ ....
+ <many-to-one name="parent"
+ class="Parent"
+ column="parent_id"
+ not-null="true"/>
+</class>]]></programlisting>
+
+ <para>
+ ì°ê´ì í쪽 ëì <literal>inverse="true"</literal>ë¡ ë§¤ííë ê²ì cascadeë¤ì ê°ì§ ì¤í¼ë ì´ì
ì ìí¥ì ì£¼ì§ ìì¼ë©°,
+ ì´ê²ë¤ì orthogonal(ì§êµ) ê°ë
ë¤ì´ë¤!
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-indexedbidirectional">
+ <title>ì¸ë±ì±ë ì½ë ì
ë¤ì ê°ì§ ìë°©í¥ ì°ê´ë¤</title>
+ <para>
+ í쪽 ëì´ íëì <literal><list></literal> ëë <literal><map></literal>ì¼ë¡ì ííëë ìë°©í¥
+ ì°ê´ì í¹ë³í ê²í 를 íìë¡ íë¤. ë§ì¼ ì¸ë±ì¤ 컬ë¼ì¼ë¡ 매íëë child í´ëì¤ì íë¡í¼í°ê° íë ì¡´ì¬íë¤ë©´ 문ì ê° ìê³ ,
+ ì°ë¦¬ë ì½ë ì
매í ìì <literal>inverse="true"</literal>를 ì¬ì©íì¬ ê³ìí ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[<class name="Parent">
+ <id name="id" column="parent_id"/>
+ ....
+ <map name="children" inverse="true">
+ <key column="parent_id"/>
+ <map-key column="name"
+ type="string"/>
+ <one-to-many class="Child"/>
+ </map>
+</class>
+
+<class name="Child">
+ <id name="id" column="child_id"/>
+ ....
+ <property name="name"
+ not-null="true"/>
+ <many-to-one name="parent"
+ class="Parent"
+ column="parent_id"
+ not-null="true"/>
+</class>]]></programlisting>
+
+ <para>
+ ê·¸ë¬ë ë§ì¼ child í´ëì¤ ìì ê·¸ë° íë¡í¼í°ê° ì¡´ì¬íì§ ìì ê²½ì°, ì°ë¦¬ë ê·¸ ì°ê´ì ì§ì íê² ìë°©í¥ì¼ë¡ ê°ì£¼í ì ìë¤
+ (ë¤ë¥¸ 쪽 ëìì ì´ì©ê°ë¥íì§ ìì ê·¸ ì°ê´ì ëìì ì´ì© ê°ë¥í ì ë³´ê° ì¡´ì¬íë¤). ì´ ê²½ì°ì ì°ë¦¬ë ê·¸ ì½ë ì
ì
+ <literal>inverse="true"</literal>ë¡ ë§¤íìí¬ ì ìë¤. ëì ì ì°ë¦¬ë ë¤ì 매íì ì¬ì©í ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[<class name="Parent">
+ <id name="id" column="parent_id"/>
+ ....
+ <map name="children">
+ <key column="parent_id"
+ not-null="true"/>
+ <map-key column="name"
+ type="string"/>
+ <one-to-many class="Child"/>
+ </map>
+</class>
+
+<class name="Child">
+ <id name="id" column="child_id"/>
+ ....
+ <many-to-one name="parent"
+ class="Parent"
+ column="parent_id"
+ insert="false"
+ update="false"
+ not-null="true"/>
+</class>]]></programlisting>
+
+ <para>
+ ì´ ë§¤íìì ê·¸ ì°ê´ì ì½ë ì
ê°ì ê°ì§ ëì´ foreign í¤ì ëí ì
ë°ì´í¸ ì±
ìì´ ììì ë
¸í¸íë¼.
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-ternary">
+ <title>Ternary associations(ì¸ê²¹ ì°ê´ë¤)</title>
+
+ <para>
+ ì¸ ê²¹ì ì°ê´ì 매ííë ì¸ ê°ì§ ê°ë¥í ì ê·¼ë²ë¤ì´ ì¡´ì¬íë¤. íëì ì ê·¼ë²ì ê·¸ê²ì ì¸ë±ì¤ë¡ì ì°ê´ê´ê³ë¥¼ ê°ì§
+ <literal>Map</literal>ì ì¬ì©íë ê²ì´ë¤:
+ </para>
+
+ <programlisting><![CDATA[<map name="contracts">
+ <key column="employer_id" not-null="true"/>
+ <map-key-many-to-many column="employee_id" class="Employee"/>
+ <one-to-many class="Contract"/>
+</map>]]></programlisting>
+
+ <programlisting><![CDATA[<map name="connections">
+ <key column="incoming_node_id"/>
+ <map-key-many-to-many column="outgoing_node_id" class="Node"/>
+ <many-to-many column="connection_id" class="Connection"/>
+</map>]]></programlisting>
+
+ <para>
+ ë ë²ì§¸ ì ê·¼ë²ì ê·¸ ì°ê´ì ìí°í° í´ëì¤ë¡ì ë¨ìíê² ë¦¬ëª¨ë¸ë§ íë ê²ì´ë¤. ì´ê²ì ì°ë¦¬ê° ê°ì¥ ê³µíµì ì¼ë¡ ì¬ì©íë ì ê·¼ë²ì´ë¤.
+ </para>
+
+ <para>
+ ë§ì§ë§ ëìì ì°ë¦¬ê° ëì¤ì ë
¼ìíê² ë composite ììë¤ì ì¬ì©íë ê²ì´ë¤.
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-idbag" revision="1">
+ <title><literal><idbag></literal> ì¬ì©í기</title>
+
+ <para>
+ ë§ì¼ ë¹ì ì´ composite í¤ë¤ì´ ëì ê²ì´ê³ ìí°í°ë¤ì´ í©ì± ìë³ìë¤(ëì© í¤ë¤, surrogate keys)ì ê°ì ¸ì¼ íë¤ë
+ ì°ë¦¬ì 견í´ë¥¼ ì ì ì¼ë¡ ìì©í ê²½ì°, ë¹ì ì ì°ë¦¬ê° ì§ê¸ê¹ì§ ë³´ì¬ì£¼ìë ê°ë¤ì ê°ì§ ì½ë ì
ë¤ê³¼ many to many ì°ê´ë¤ì´ 모ë
+ composite í¤ë¤ì ê°ì§ í
ì´ë¸ë¤ë¡ 매íëë¤ë ì½ê° ì´ìí ì ì ë°ê²¬í ìë ìë¤! ì´ì ì´ ì ì 꽤 ë
¼ìì ì¬ì§ê° ìë¤; ììí
+ ì°ê´ í
ì´ë¸ì (ë¹ë¡ composite ê°ë¤ì ê°ì§ ì½ë ì
<emphasis>ì¼ ìë</emphasis> ììì§ë¼ë) ëì© í¤ë¡ë¶í° ë§ì ì´ì ì
+ ì·¨íì§ ìë ê²ì²ë¼ ë³´ì¸ë¤. ê·¸ë¼ìë ë¶êµ¬íê³ Hibernateë ë¹ì ì´ ê°ë¤ì ê°ì§ ì½ë ì
ë¤ê³¼ many to many ì°ê´ë¤ì ëì© í¤ë¥¼ ê°ì§
+ í
ì´ë¸ë¡ 매íìí¤ë ê²ì ë¹ì ìê² íì©í´ì£¼ë í¹ì§ì ì ê³µíë¤.
+ </para>
+
+ <para>
+ <literal><idbag></literal> ììë bag ì미를 ê°ì§ <literal>List</literal>(ëë <literal>Collection</literal>)ì
+ 매ííëë¡ ë¹ì ìê² íì©í´ì¤ë¤.
+ </para>
+
+<programlisting><![CDATA[<idbag name="lovers" table="LOVERS">
+ <collection-id column="ID" type="long">
+ <generator class="sequence"/>
+ </collection-id>
+ <key column="PERSON1"/>
+ <many-to-many column="PERSON2" class="Person" fetch="join"/>
+</idbag>]]></programlisting>
+
+ <para>
+ ë¹ì ì´ ë³¼ ì ìë¯ì´, <literal><idbag></literal>ì ë§ì¹ ìí°í° í´ëì¤ì¸ì synthetic id generator(í©ì± id ìì±ê¸°)를
+ ê°ëë¤! ë¤ë¥¸ ëì© í¤ë ê°ê°ì ì½ë ì
íì í ë¹ëë¤. íì§ë§ Hibernateë í¹ì íì ëì© í¤ ê°ì ë°ê²¬íë ë©ì»¤ëì¦ì ì ê³µíì§ ìëë¤.
+ </para>
+
+ <para>
+ <literal><idbag></literal>ì ì
ë°ì´í¸ í¼í¬ë¨¼ì¤ë ì ê· <literal><bag></literal> ë³´ë¤ í¨ì¬ ì¢ë¤ë ì ì
+ ë
¸í¸íë¼! Hibernateë ë§ì¹ list, map, ëë setì¸ì, ê°ë³ íë¤ì í¨ì¨ì ì¼ë¡ ìì¹ì§ì¸ ì ìê³ ê·¸ê²ë¤ì ê°ë³ì ì¼ë¡ ì
ë°ì´í¸
+ íê±°ë ìì ìí¬ ì ìë¤.
+ </para>
+
+ <para>
+ íì¬ êµ¬íìì, <literal>native</literal> ìë³ì ìì± ë°©ëë <literal><idbag></literal> ì½ë ì
ìë³ìë¤ì ëí´
+ ì§ìëì§ ìëë¤.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <!--ë¤ì ìì를 문ìííì§ ìì -->
+
+ <!--sect1 id="collections-heterogeneous">
+ <title>Heterogeneous Associations</title>
+
+ <para>
+ <literal><many-to-any></literal>ì <literal><index-many-to-any></literal> ììë¤ì
+ ì§ì í ì´ì¢
ì(heterogeneous) ì°ê´ë¤ì ì ê³µíë¤. ì´ë¤ 매í ììë¤ì <literal><any></literal> ììì
+ ëì¼í ë°©ë²ì¼ë¡ ëìíë¤ - ê·¸ë¦¬ê³ ëí ìë§ ëë¬¼ê² ì¬ì©ë ì ìì ê²ì´ë¤.
+ </para>
+
+ </sect1-->
+
+ <sect1 id="collections-example" revision="1">
+ <title>ì½ë ì
ìì ë¤</title>
+
+ <para>
+ ìì ì ë¤ì 꽤 í¼ëì¤ë½ë¤. ë°ë¼ì ìì 를 ì´í´ë³´ì. ë¤ì í´ëì¤:
+ </para>
+
+ <programlisting><![CDATA[package eg;
+import java.util.Set;
+
+public class Parent {
+ private long id;
+ private Set children;
+
+ public long getId() { return id; }
+ private void setId(long id) { this.id=id; }
+
+ private Set getChildren() { return children; }
+ private void setChildren(Set children) { this.children=children; }
+
+ ....
+ ....
+}]]></programlisting>
+
+ <para>
+ ë <literal>Child</literal> ì¸ì¤í´ì¤ë¤ì ê°ì§ íëì ì½ë ì
ì ê°ê³ ìë¤. ë§ì¼ ê°ê°ì ììì´ ìµìí í ê°ì ë¶ëª¨ë¥¼ ê°ì§ ê²½ì°,
+ ëë¶ë¶ì ê³ ì í 매íì one-to-many ì°ê´ì´ë¤:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Parent">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <set name="children">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+ </set>
+ </class>
+
+ <class name="Child">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ ì´ê²ì ë¤ì í
ì´ë¸ ì ìë¤ë¡ 매íëë¤:
+ </para>
+
+ <programlisting><![CDATA[create table parent ( id bigint not null primary key )
+create table child ( id bigint not null primary key, name varchar(255), parent_id bigint )
+alter table child add constraint childfk0 (parent_id) references parent]]></programlisting>
+
+ <para>
+ ë§ì¼ ë¶ëª¨ê° <emphasis>íìì </emphasis>ì´ë¼ë©´, ìë°©í¥ one-to-many ì°ê´ê´ê³ë¥¼ ì¬ì©íë¼:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Parent">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <set name="children" inverse="true">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+ </set>
+ </class>
+
+ <class name="Child">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <property name="name"/>
+ <many-to-one name="parent" class="Parent" column="parent_id" not-null="true"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ <literal>NOT NULL</literal> 컨ì¤í¸ë ì¸í¸ë¥¼ 주목íë¼:
+ </para>
+
+ <programlisting><![CDATA[create table parent ( id bigint not null primary key )
+create table child ( id bigint not null
+ primary key,
+ name varchar(255),
+ parent_id bigint not null )
+alter table child add constraint childfk0 (parent_id) references parent]]></programlisting>
+
+ <para>
+ ëìì ì¼ë¡, ë§ì¼ ë¹ì ì´ ì´ ì°ê´ê´ê³ê° ë¨ë°©í¥ì´ì´ì¼ í¨ì ì ëì ì¼ë¡ ìì¤í ê²½ì°, ë¹ì ì <literal><key></literal>
+ 매í ìì <literal>NOT NULL</literal> 컨ì¤í¸ë ì¸í¸ë¥¼ ì ì¸í ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Parent">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <set name="children">
+ <key column="parent_id" not-null="true"/>
+ <one-to-many class="Child"/>
+ </set>
+ </class>
+
+ <class name="Child">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ ë°ë©´ì, ë§ì¼ ììì´ ì¬ë¬ ë¶ëª¨ë¤ì ê°ì§ ì ìì ê²½ì°, many-to-many ì°ê´ì´ ì ì íë¤:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Parent">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <set name="children" table="childset">
+ <key column="parent_id"/>
+ <many-to-many class="Child" column="child_id"/>
+ </set>
+ </class>
+
+ <class name="Child">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ í
ì´ë¸ ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[create table parent ( id bigint not null primary key )
+create table child ( id bigint not null primary key, name varchar(255) )
+create table childset ( parent_id bigint not null,
+ child_id bigint not null,
+ primary key ( parent_id, child_id ) )
+alter table childset add constraint childsetfk0 (parent_id) references parent
+alter table childset add constraint childsetfk1 (child_id) references child]]></programlisting>
+
+ <para>
+ ë¶ëª¨/ìì ê´ê³ 매íì ì°ìµíë ë ë§ì ìì ë¤ê³¼ ì ì²´ 리íì¤ì <xref linkend="example-parentchild"/>를 ë³´ë¼.
+ </para>
+
+ <para>
+ ë¹ë¡ ë ë§ì ì ì¢
ì°ê´ 매íë¤ì´ ê°ë¥í ì§ë¼ë, ì°ë¦¬ë ë¤ì ì¥ìì 모ë ê°ë¥ì±ë¤ì ë¶ë¥í ê²ì´ë¤.
+ </para>
+
+ </sect1>
+
+</chapter>
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/component_mapping.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/component_mapping.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/component_mapping.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,373 @@
+<chapter id="components">
+ <title>Component 매í</title>
+
+ <para>
+ <emphasis>component</emphasis>ì ê°ë
ì Hibernateìì ë¤ë¥¸ ì©ëë¡ ëªëª ë¤ë¥¸ 컨í
ì¤í¸ë¤ ë´ìì ì¬ì¬ì©ëë¤.
+ </para>
+
+ <sect1 id="components-dependentobjects" revision="2" >
+ <title>ì¢
ì ê°ì²´ë¤</title>
+
+ <para>
+ íëì ì»´í¬ëí¸ë ìí°í° ì°¸ì¡°ê° ìë, value íì
ì¼ë¡ì ììí ëë íëì í¬í¨ë ê°ì²´ì´ë¤. "ì»´í¬ëí¸" ì©ì´ë
+ (ìí¤í
ì² ìì¤ì ì»´í¬ëí¸ë¤ì´ ìë) composition(구ì±,í©ì±)ì ëí ê°ì²´-ì§í¥ì ì¸ ê°ë
ì ì¸ê¸íë¤.
+ ì를 ë¤ì´ ë¹ì ì ë¤ìê³¼ ê°ì´ ê°ì¸ì 모íí ìí¬ ìë ìë¤:
+ </para>
+
+ <programlisting><![CDATA[public class Person {
+ private java.util.Date birthday;
+ private Name name;
+ private String key;
+ public String getKey() {
+ return key;
+ }
+ private void setKey(String key) {
+ this.key=key;
+ }
+ public java.util.Date getBirthday() {
+ return birthday;
+ }
+ public void setBirthday(java.util.Date birthday) {
+ this.birthday = birthday;
+ }
+ public Name getName() {
+ return name;
+ }
+ public void setName(Name name) {
+ this.name = name;
+ }
+ ......
+ ......
+}]]></programlisting>
+
+<programlisting><![CDATA[public class Name {
+ char initial;
+ String first;
+ String last;
+ public String getFirst() {
+ return first;
+ }
+ void setFirst(String first) {
+ this.first = first;
+ }
+ public String getLast() {
+ return last;
+ }
+ void setLast(String last) {
+ this.last = last;
+ }
+ public char getInitial() {
+ return initial;
+ }
+ void setInitial(char initial) {
+ this.initial = initial;
+ }
+}]]></programlisting>
+
+ <para>
+ ì´ì <literal>Name</literal>ì <literal>Person</literal>ì ì»´í¬ëí¸ë¡ì ììí ë ìë ìë¤. <literal>Name</literal>ì´
+ ê·¸ê²ì ìì íë¡í¼í°ë¤ì ëí getter ë©ìëì setter ë©ìë를 ì ìíì§ë§, ì´ë¤ ì¸í°íì´ì¤ë¤ì´ë ìë³ì íë¡í¼í°ë¤ì ì ì¸íë ê²ì
+ íìë¡ íì§ ììì 주목íë¼.
+ </para>
+
+ <para>
+ ì°ë¦¬ì Hibernate 매íì ë¤ìê³¼ ê°ì ê²ì´ë¤:
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Person" table="person">
+ <id name="Key" column="pid" type="string">
+ <generator class="uuid"/>
+ </id>
+ <property name="birthday" type="date"/>
+ <component name="Name" class="eg.Name"> <!-- class attribute optional -->
+ <property name="initial"/>
+ <property name="first"/>
+ <property name="last"/>
+ </component>
+</class>]]></programlisting>
+
+ <para>
+ person í
ì´ë¸ì
+ <literal>pid</literal>,
+ <literal>birthday</literal>,
+ <literal>initial</literal>,
+ <literal>first</literal>,
+ <literal>last</literal> 컬ë¼ë¤ì ê°ì§ ê²ì´ë¤.
+ </para>
+
+ <para>
+ 모ë ê°(value) íì
ë¤ì²ë¼, ì»´í¬ëí¸ë¤ì ê³µì ë 참조ë¤ì ì§ìíì§ ìëë¤. ë¬ë¦¬ ë§í´, ë ëª
ì ê°ì¸ë¤ì ëì¼í ì´ë¦ì ê°ì§ ì
+ ìì§ë§, ë ê°ì person ê°ì²´ë¤ì ì¤ì§ ê° ë§ì´ "ëì¼í" ë ê°ì ë
립ì ì¸ name ê°ì²´ë¤ì í¬í¨í ê²ì´ë¤. ì»´í¬ëí¸ì null ê° ì미ë
+ <emphasis>í¹ë³í ì©ë를 ìí ê²ì´ë¤</emphasis>. í¬í¨ë ê°ì²´ë¥¼ ë¤ì ë¡ëìí¬ ë, Hibernateë 모ë ì»´í¬ëí¸ ì»¬ë¼ë¤ì´
+ nullì¼ ê²½ì°ì ì ì²´ ì»´í¬ëí¸ê° nullì´ë¼ê³ ê°ì í ê²ì´ë¤. ì´ê²ì ëë¶ë¶ì ì©ëì ë§ì ê²ì´ë¤.
+ </para>
+
+ <para>
+ ì»´í¬ëí¸ì íë¡í¼í°ë¤ì ììì Hibernate íì
ì¼ ì ìë¤(ì½ë ì
ë¤, many-to-one ì°ê´ë¤, ë¤ë¥¸ ì»´í¬ëí¸ë¤, 기í).
+ ë´í¬ë ì»´í¬ëí¸ë¤ì ì ì¢
ì ì¬ì©ë¡ë¡ ê°ì£¼ëì§ <emphasis>ìì</emphasis> ê²ì´ë¤. Hibernateë ë§¤ì° ì ì ì ë ê°ì²´ 모íì
+ ì§ìíëë¡ ê³ ìëì´ìë¤.
+ </para>
+
+ <para>
+ <literal><component></literal> ììë ì»´í¬ëí¸ í´ëì¤ì íë¡í¼í°ë¥¼ í¬í¨ëë ìí°í°ì ëí ì 참조ë¡ì 매íìí¤ë
+ <literal><parent></literal> ìë¸ìì를 íì©íë¤.
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Person" table="person">
+ <id name="Key" column="pid" type="string">
+ <generator class="uuid"/>
+ </id>
+ <property name="birthday" type="date"/>
+ <component name="Name" class="eg.Name" unique="true">
+ <parent name="namedPerson"/> <!-- reference back to the Person -->
+ <property name="initial"/>
+ <property name="first"/>
+ <property name="last"/>
+ </component>
+</class>]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="components-incollections" revision="1">
+ <title>ì¢
ì ê°ì²´ë¤ì ê°ì§ ì½ë ì
ë¤</title>
+
+ <para>
+ ì»´í¬ëí¸ë¤ì ê°ì§ ì½ë ì
ë¤ì´ ì§ìëë¤(ì를 ë¤ë©´ <literal>Name</literal> íì
ì ê°ì§ ë°°ì´).
+ <literal><element></literal> í그를 <literal><composite-element></literal> íê·¸ë¡
+ ëì²´ìì¼ì ë¹ì ì ì»´í¬ëí¸ ì½ë ì
ì ì ì¸íë¼.
+ </para>
+
+ <programlisting><![CDATA[<set name="someNames" table="some_names" lazy="true">
+ <key column="id"/>
+ <composite-element class="eg.Name"> <!-- class attribute required -->
+ <property name="initial"/>
+ <property name="first"/>
+ <property name="last"/>
+ </composite-element>
+</set>]]></programlisting>
+
+ <para>
+ ë
¸í¸: ë§ì¼ ë¹ì ì´ composite ìì를 ê°ì§ íëì <literal>Set</literal>를 ì ìí ê²½ì°, <literal>equals()</literal>ì
+ <literal>hashCode()</literal>를 ì ííê² êµ¬ííë ê²ì´ ë§¤ì° ì¤ìíë¤.
+ </para>
+
+ <para>
+ Composite ììë¤ì ì»´í¬ëí¸ë¤ì í¬í¨íì§ë§ ì½ë ì
ë¤ì í¬í¨íì§ ìëë¤. ë§ì¼ ë¹ì ì composite ìì ìì²´ê° ì»´í¬ëí¸ë¤ì í¬í¨í
+ ê²½ì°, <literal><nested-composite-element></literal> í그를 ì¬ì©íë¼. ì´ê²ì 꽤 ì ì¢
ì ê²½ì°-ê·¸ê²ë¤ ìì²´ê°
+ ì»´í¬ëí¸ë¤ì ê°ê³ ìë ì»´í¬ëí¸ë¤ì ì½ë ì
-ì´ë¤. ì´ ë¨ê³ìì ë¹ì ì one-to-many ì°ê´ì´ ë ì ì íì§ë¥¼ ë¹ì ì¤ì¤ë¡ìê² ì§ë¬¸íê²
+ ë ê²ì´ë¤. íëì ìí°í°ë¡ì composite ìì를 ë¤ì 모ë¸ë§íë ¤ê³ ìëíë¼ - ê·¸ë¬ë ìë° ëª¨íë¤ì´ ëì¼í ì§ë¼ë,
+ ê´ê³í 모íê³¼ ììí ì미ë¤ì ì¬ì í ì½ê° ë¤ë¥´ë¤.
+ </para>
+
+ <para>
+ ë¹ì ì´ íëì <literal><set></literal>ì ì¬ì© ì¤ì´ë¼ë©´, íëì composite ìì 매íì null ê°ë¥í íë¡í¼í°ë¤ì
+ ì§ìíì§ ììì ë
¸í¸í길 ë°ëë¤. Hibernateë ê°ì²´ë¤ì ìì í ë íëì ë ì½ë를 ìë³íëë° ê°ê°ì 컬ë¼ë¤ ê°ì ì¬ì©í´ì¼ íë©°
+ (composite ìì í
ì´ë¸ ë´ì ë³ëì íë¼ì´ë¨¸ë¦¬ í¤ ì»¬ë¼ì´ ì¡´ì¬íì§ ìëë¤), ê·¸ê²ì null ê°ë¤ì ëí´ìë ë¶ê°ë¥íë¤. ë¹ì ì
+ íëì composite-ìì ë´ì not-null íë¡í¼í°ë¤ ë§ì ì¬ì©í´ì¼ íê±°ë íëì <literal><list></literal>,
+ <literal><map></literal>, <literal><bag></literal> ëë <literal><idbag></literal>ì
+ ì íí´ì¼ íë¤.
+ </para>
+
+ <para>
+ composite ììì ëí íëì í¹ë³í ê²½ì°ë ë´í¬ë <literal><many-to-one></literal> ìì를 ê°ì§ composite ììì´ë¤.
+ ì´ê°ì 매íì many-to-many ì°ê´ í
ì´ë¸ì í¹ë³í 컬ë¼ë¤ì composite ìì í´ëì¤ë¡ 매íìí¤ë ê²ì ë¹ì ìê² íì©í´ì¤ë¤. ë¤ìì
+ <literal>Order</literal>ë¡ë¶í° <literal>Item</literal>ì¼ë¡ì many-to-many ì°ê´ì´ë¤. ì¬ê¸°ì
+ <literal>purchaseDate</literal>, <literal>price</literal>, ê·¸ë¦¬ê³ <literal>quantity</literal>ë ì°ê´ì
+ íë¡í¼í°ë¤ì´ë¤:
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Order" .... >
+ ....
+ <set name="purchasedItems" table="purchase_items" lazy="true">
+ <key column="order_id">
+ <composite-element class="eg.Purchase">
+ <property name="purchaseDate"/>
+ <property name="price"/>
+ <property name="quantity"/>
+ <many-to-one name="item" class="eg.Item"/> <!-- class attribute is optional -->
+ </composite-element>
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ ë¬¼ë¡ , ìë°©í¥ ì°ê´ ë¤ë¹ê²ì´ì
ì ê²½ì°, ë¤ë¥¸ 측 ìì ìë purchaseì ëí ì°¸ì¡°ê° ì¡´ì¬í ì ìë¤. ì»´í¬ëí¸ë¤ì´ ê°(value) íì
ë¤ì´ê³
+ ê³µì ë 참조ë¤ì íì©íì§ ììì 기ìµíë¼. íëì <literal>Purchase</literal>ë<literal>Order</literal>를 ê°ì§ set
+ ë´ì ìì ì ìì§ë§, ê·¸ê²ì ëìì <literal>Item</literal>ì ìí´ ì°¸ì¡°ë ì ìë¤.
+ </para>
+
+ <para>ì¬ì§ì´ ì¸ê²¹ì(ëë ë¤ ê²¹ì, 기í) ì°ê´ë¤ì´ ê°ë¥íë¤:</para>
+
+ <programlisting><![CDATA[<class name="eg.Order" .... >
+ ....
+ <set name="purchasedItems" table="purchase_items" lazy="true">
+ <key column="order_id">
+ <composite-element class="eg.OrderLine">
+ <many-to-one name="purchaseDetails class="eg.Purchase"/>
+ <many-to-one name="item" class="eg.Item"/>
+ </composite-element>
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ composite ììë¤ì ë¤ë¥¸ ìí°í°ë¤ì ëí ì°ê´ë¤ê³¼ ëì¼í 구문ì ì¬ì©íì¬ ì§ìë¤ ë´ì ëíë ìë ìë¤.
+ </para>
+
+ </sect1>
+
+ <sect1 id="components-asmapindex">
+ <title>Map ì¸ë±ì¤ë¤ë¡ì ì»´í¬ëí¸ë¤</title>
+
+ <para>
+ <literal><composite-map-key></literal> ììë ë¹ì ìê² íëì ì»´í¬ëí¸ í´ëì¤ë¥¼ íëì <literal>Map</literal>ì
+ í¤ë¡ì 매íìí¤ëë¡ íë¤. ë¹ì ì ì»´í¬ëí¸ í´ëì¤ ììì <literal>hashCode()</literal>ì <literal>equals()</literal>를
+ ì ííê² ì¤ë²ë¼ì´ë ìí¤ëë¡ íë¼.
+ </para>
+ </sect1>
+
+ <sect1 id="components-compositeid" revision="1">
+ <title>composite ìë³ìë¤ë¡ì ì»´í¬ëí¸ë¤</title>
+
+ <para>
+ ë¹ì ì íëì ì»´í¬ëí¸ë¥¼ íëì ìí°í° í´ëì¤ì ëí íëì ìë³ìë¡ì ì¬ì©í ìë ìë¤. ë¹ì ì ì»´í¬ëí¸ í´ëì¤ë ì´ë¤ ì¬ìë¤ì
+ 충족ìì¼ì¼ íë¤:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ ê·¸ê²ì <literal>java.io.Serializable</literal>ì 구íí´ì¼ íë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ê·¸ê²ì composite í¤ ë±ê°(equality)ì ëí ë°ì´í°ë² ì´ì¤ ê°ë
ê³¼ ì¼ì¹ëê², <literal>equals()</literal>ì
+ <literal>hashCode()</literal>를 ë¤ì 구íí´ì¼ íë¤.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ <emphasis>ë
¸í¸: Hibernate3ìì, ë ë²ì§¸ ì¬ìì Hibernateì ì ëì ì¼ë¡ ì격í ì¬ìì´ ìëë¤.
+ ê·¸ë¬ë ìë¬´í¼ ê·¸ê²ì ííë¼.</emphasis>
+ </para>
+
+ <para>
+ ë¹ì ì compsite í¤ë¤ì ìì±ìí¤ëë° <literal>IdentifierGenerator</literal>를 ì¬ì©í ì ìë¤. ëì ì ì´í리ì¼ì´ì
ì
+ ê·¸ê² ìì ì ìë³ìë¤ì í ë¹í´ì¼ íë¤.
+ </para>
+
+ <para>
+ íµìì <literal><id></literal> ì ì¸ ìì¹ì (ë´í¬ë <literal><key-property></literal> ììë¤ì ê°ì§)
+ <literal><composite-id></literal> í그를 ì¬ì©íë¼. ì를 ë¤ì´, <literal>OrderLine</literal> í´ëì¤ë
+ <literal>Order</literal>ì (composite) íë¼ì´ë¨¸ë¦¬ í¤ì ìì¡´íë íë¼ì´ë¨¸ë¦¬ í¤ë¥¼ ê°ëë¤.
+ </para>
+
+ <programlisting><![CDATA[<class name="OrderLine">
+
+ <composite-id name="id" class="OrderLineId">
+ <key-property name="lineId"/>
+ <key-property name="orderId"/>
+ <key-property name="customerId"/>
+ </composite-id>
+
+ <property name="name"/>
+
+ <many-to-one name="order" class="Order"
+ insert="false" update="false">
+ <column name="orderId"/>
+ <column name="customerId"/>
+ </many-to-one>
+ ....
+
+</class>]]></programlisting>
+
+ <para>
+ ì´ì <literal>OrderLine</literal> í
ì´ë¸ì 참조íë ììì foreign í¤ë¤ì´ ëí compsiteì´ë¤. ë¹ì ì ë¤ë¥¸ í´ëì¤ë¤ì ëí
+ ë¹ì ì 매íë¤ ìì ì´ê²ì ì ì¸í´ì¼ íë¤. <literal>OrderLine</literal>ì ëí íëì ì°ê´ì ë¤ìê³¼ ê°ì´ 매íë ê²ì´ë¤:
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="orderLine" class="OrderLine">
+<!-- the "class" attribute is optional, as usual -->
+ <column name="lineId"/>
+ <column name="orderId"/>
+ <column name="customerId"/>
+</many-to-one>]]></programlisting>
+
+ <para>
+ (<literal><column></literal> íê·¸ê° ëª¨ë ê³³ìì <literal>column</literal> ìì±ì ëí ëììì ë
¸í¸íë¼.)
+ </para>
+
+ <para>
+ <literal>OrderLine</literal>ì ëí <literal>many-to-many</literal> ì°ê´ì ëí composite foreign í¤ë¥¼ ì¬ì©íë¤:
+ </para>
+
+ <programlisting><![CDATA[<set name="undeliveredOrderLines">
+ <key column name="warehouseId"/>
+ <many-to-many class="OrderLine">
+ <column name="lineId"/>
+ <column name="orderId"/>
+ <column name="customerId"/>
+ </many-to-many>
+</set>]]></programlisting>
+
+ <para>
+ <literal>Order</literal>ìì <literal>OrderLine</literal>ë¤ì ì½ë ì
ì´ ì¬ì©ë ê²ì´ë¤:
+ </para>
+
+ <programlisting><![CDATA[<set name="orderLines" inverse="true">
+ <key>
+ <column name="orderId"/>
+ <column name="customerId"/>
+ </key>
+ <one-to-many class="OrderLine"/>
+</set>]]></programlisting>
+
+ <para>
+ (íµìì ì¼ë¡ <literal><one-to-many></literal> ììë 컬ë¼ë¤ì ì ì¸íì§ ìëë¤.)
+ </para>
+
+ <para>
+ ë§ì¼ <literal>OrderLine</literal> ìì²´ê° íëì ì½ë ì
ì ìì í ê²½ì°, ê·¸ê²ì ëí íëì composite foreign í¤ë¥¼ ê°ëë¤.
+ </para>
+
+ <programlisting><![CDATA[<class name="OrderLine">
+ ....
+ ....
+ <list name="deliveryAttempts">
+ <key> <!-- a collection inherits the composite key type -->
+ <column name="lineId"/>
+ <column name="orderId"/>
+ <column name="customerId"/>
+ </key>
+ <list-index column="attemptId" base="1"/>
+ <composite-element class="DeliveryAttempt">
+ ...
+ </composite-element>
+ </set>
+</class>]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="components-dynamic" revision="1">
+ <title>ëì ì¸ ì»´í¬ëí¸ë¤</title>
+
+ <para>
+ ë¹ì ì <literal>Map</literal> íì
ì íë¡í¼í°ë¥¼ 매íìí¬ ìë ìë¤:
+ </para>
+
+ <programlisting><![CDATA[<dynamic-component name="userAttributes">
+ <property name="foo" column="FOO" type="string"/>
+ <property name="bar" column="BAR" type="integer"/>
+ <many-to-one name="baz" class="Baz" column="BAZ_ID"/>
+</dynamic-component>]]></programlisting>
+
+ <para>
+ <literal><dynamic-component></literal> 매íì ì미ë <literal><component></literal>ì ëì¼íë¤.
+ ì´ë° ì¢
ë¥ì 매íì ì¥ì ì ë°°ì¹ ìì ë¨ì§ 매í 문ì를 í¸ì§í¨ì¼ë¡ì¨ ê·¸ beanì ì¤ì íë¡í¼í°ë¤ì ê²°ì íë ê°ì©ì±ì´ë¤. 매í 문ìì
+ ëí ë°íì ì²ë¦¬ë ëí DOM íì를 ì¬ì©íì¬ ê°ë¥íë¤. ë ì¢ê²ë ë¹ì ì´ <literal>Configuration</literal> ê°ì²´ë¥¼ íµí´
+ Hibernateì 구ì±-ì ë©í모íì ì ê·¼í ì ìë¤(ê·¸ë¦¬ê³ ë³ê²½ìí¬ ì ìë¤)
+ </para>
+
+ </sect1>
+
+</chapter>
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/configuration.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/configuration.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/configuration.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1648 @@
+<chapter id="session-configuration" revision="1">
+
+ <title>구ì±</title>
+
+ <para>
+ Hibernateê° ë§ì ë¤ë¥¸ íê²½ë¤ìì ëìíëë¡ ì¤ê³ëì´ ìì¼ë¯ë¡, ë§ì ê°ìì êµ¬ì± íë¼ë¯¸í°ë¤ì´ ì¡´ì¬íë¤. ë¤íí ëë¶ë¶ì ì ì미í
+ ëí´í¸ ê°ë¤ì´ê³ Hibernateë ë¤ìí ìµì
ë¤ì ë³´ì¬ì£¼ë <literal>etc/</literal> ë´ì ìì íì¼ <literal>hibernate.properties</literal>ë¡
+ ë°°í¬ëë¤. ë¹ì ì ë¨ì§ ë¹ì ì classpath ê²½ë¡ ìì ê·¸ íì¼ì ì§ì´ë£ê³ ê·¸ê²ì 커ì¤í¸ë§ì´ì§íê¸°ë§ í´ì¼ íë¤.
+ </para>
+
+ <sect1 id="configuration-programmatic" revision="1">
+ <title>íë¡ê·¸ë¨ ìì 구ì±</title>
+
+ <para>
+ <literal>org.hibernate.cfg.Configuration</literal>ì ì¸ì¤í´ì¤ë ì´í리ì¼ì´ì
ì Java íì
ë¤ì SQL ë°ì´í°ë² ì´ì¤
+ íì
ì¼ë¡ì ì ì²´ 매í ì§í©ì íííë¤. <literal>Configuration</literal>ì (ë¶ë³ì) <literal>SessionFactory</literal>를
+ ë¹ëíëë° ì¬ì©ëë¤. 매íë¤ì ì¬ë¬ XML 매í íì¼ë¤ë¡ë¶í° ì»´íì¼ ëë¤.
+ </para>
+
+ <para>
+ ë¹ì ì <literal>Configuration</literal> ì¸ì¤í´ì¤ë¥¼ ì´ê¸°í ìí¤ê³ XML 매í 문ìë¤ì ì§ì í¨ì¼ë¡ì¨ <literal>Configuration</literal>
+ ì¸ì¤í´ì¤ë¥¼ ì»ì ì ìë¤. ë§ì¼ 매í íì¼ë¤ì´ classpath ë´ì ìë¤ë©´, <literal>addResource()</literal>를 ì¬ì©íë¼:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = new Configuration()
+ .addResource("Item.hbm.xml")
+ .addResource("Bid.hbm.xml");]]></programlisting>
+
+ <para>
+ (ëëë¡ ë ëì) ë¤ë¥¸ ë°©ë²ì 매íë í´ëì¤ë¥¼ ì§ì íë ê²ì´ê³ , Hibernateë¡ íì¬ê¸ ë¹ì ì ìí´ ë§¤í 문ì를 ì°¾ëë¡ íë¼:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = new Configuration()
+ .addClass(org.hibernate.auction.Item.class)
+ .addClass(org.hibernate.auction.Bid.class);]]></programlisting>
+
+ <para>
+ ê·¸ë Hibernateë classpath ë´ìì <literal>/org/hibernate/auction/Item.hbm.xml</literal>ê³¼
+ <literal>/org/hibernate/auction/Bid.hbm.xml</literal>ë¡ ëª
ëª
ë 매í íì¼ë¤ì 룩ì
í ê²ì´ë¤. ì´ ì ê·¼ë²ì
+ ììì íëì½ë©ë íì¼ ì´ë¦ë¤ì ì ê±°íë¤.
+ </para>
+
+ <para>
+ <literal>Configuration</literal>ì ëí êµ¬ì± íë¡í¼í°ë¤ì ì§ì íë ê²ì íì©í´ì¤ë¤:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = new Configuration()
+ .addClass(org.hibernate.auction.Item.class)
+ .addClass(org.hibernate.auction.Bid.class)
+ .setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLInnoDBDialect")
+ .setProperty("hibernate.connection.datasource", "java:comp/env/jdbc/test")
+ .setProperty("hibernate.order_updates", "true");]]></programlisting>
+
+ <para>
+ ì´ê²ì 컨í¼ê·¸ë ì´ì
íë¡í¼í°ë¤ì Hibernateì ì ë¬íë ì ì¼í ë°©ë²ì´ ìëë¤. ì¬ë¬ ê°ì§ ìµì
ë¤ì ë¤ìì í¬í¨íë¤:
+ </para>
+
+ <orderedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>java.util.Properties</literal>ì ì¸ì¤í´ì¤ë¥¼ <literal>Configuration.setProperties()</literal>ì
+ ì ë¬íë¤ .
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ classpathì ë£¨í¸ ëë í 리ì <literal>hibernate.properties</literal>를 ìì¹ì§ì´ë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>java -Dproperty=value</literal>를 ì¬ì©íì¬ <literal>System</literal> íë¡í¼í°ë¤ì ì¤ì íë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>hibernate.cfg.xml</literal>ì <literal><property></literal> ììë¤ì í¬í¨íë¤
+ (ëì¤ì ë
¼ìë¨).
+ </para>
+ </listitem>
+ </orderedlist>
+
+ <para>
+ ë¹ì ì´ ë¹ ë¥´ê² ììíê³ ìí ê²½ì° <literal>hibernate.properties</literal>ë ê°ì¥ ì¬ì´ ì ê·¼ë²ì´ë¤.
+ </para>
+
+ <para>
+ <literal>Configuration</literal>ì ìì ì(startup-time) ê°ì²´ë¡ì ì¼ë¨ <literal>SessionFactory</literal>ê°
+ ìì±ëë©´ í기ëê²ë ìì ëì´ ìë¤.
+ </para>
+
+ </sect1>
+
+ <sect1 id="configuration-sessionfactory">
+ <title>SessionFactory ì»ê¸°</title>
+
+ <para>
+ 모ë 매íë¤ì´ <literal>Configuration</literal>ì ìí´ íì±ëìì ë, ì´í리ì¼ì´ì
ì <literal>Session</literal>
+ ì¸ì¤í´ì¤ë¤ì ëí í©í 리를 ì»ì´ì¼ íë¤. ì´ í©í 리ë 모ë ì´í리ì¼ì´ì
ì°ë ëë¤ì ìí´ ê³µì ëëë¡ ê³ ìëìë¤:
+ </para>
+
+ <programlisting><![CDATA[SessionFactory sessions = cfg.buildSessionFactory();]]></programlisting>
+
+ <para>
+ íì§ë§ Hibernateë ë¹ì ì ì´í리ì¼ì´ì
ì´ íë ì´ìì <literal>SessionFactory</literal>를 ì´ê¸°í ìí¤ë ê²ì íì©íë¤.
+ ì´ê²ì ë¹ì ì´ íë ì´ìì ë°ì´í°ë² ì´ì¤ë¥¼ ì¬ì©íë ê²½ì°ì ì ì©íë¤.
+ </para>
+
+ </sect1>
+
+ <sect1 id="configuration-hibernatejdbc" revision="1">
+ <title>JDBC 커ë¥ì
ë¤</title>
+
+ <para>
+ ëê° ë¹ì ì <literal>SessionFactory</literal>ë¡ íì¬ê¸ ë¹ì ì ìí JDBC 커ë¥ì
ë¤ì ìì±ìí¤ê³ íë§ìí¤ë ê²ì ìíë¤.
+ ë§ì¼ ë¹ì ì´ ì´ ì ê·¼ë²ì ì·¨í ê²½ì°, í ê°ì <literal>Session</literal>ì ì¬ë ê²ì ë¤ìê³¼ ê°ì´ ê°ë¨íë¤:
+ </para>
+
+ <programlisting><![CDATA[Session session = sessions.openSession(); // open a new Session]]></programlisting>
+
+ <para>
+ ë¹ì ì´ ë°ì´í°ë² ì´ì¤ì ëí ì ê·¼ì ìì²íë ì´ë¤ ê²ì ííì ë§ì, í ê°ì JDBC 커ë¥ì
ì´ ê·¸ íë¡ë¶í° ì»ì´ì§ ê²ì´ë¤.
+ </para>
+
+ <para>
+ ì´ê²ì´ ëìíëë¡ í기 ìí´ì, ì°ë¦¬ë ëªëª JDBC 커ë¥ì
íë¡í¼í°ë¤ì Hibernateì ì ë¬í íìê° ìë¤. 모ë Hibernate íë¡í¼í°
+ ì´ë¦ë¤ê³¼ ìë¯¸ë¡ ë¤ì <literal>org.hibernate.cfg.Environment</literal> í´ëì¤ ìì ì ìëì´ ìë¤. ì°ë¦¬ë ì´ì JDBC
+ 커ë¥ì
구ì±ì ìí ê°ì¥ ì¤ìí ì¤ì ë¤ì ì¤ëª
í ê²ì´ë¤.
+ </para>
+
+ <para>
+ ë§ì¼ ë¹ì ì´ ë¤ì íë¡í¼í°ë¤ì ì¤ì í ê²½ì° Hibernateë <literal>java.sql.DriverManager</literal>를 ì¬ì©íì¬ ì»¤ë¥ì
ë¤ì
+ ì»ì ê²ì´ë¤(ê·¸ë¦¬ê³ íë§ìí¬ ê²ì´ë¤):
+ </para>
+
+ <table frame="topbot">
+ <title>Hibernate JDBC íë¡í¼í°ë¤</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>íë¡í¼í° ì´ë¦</entry>
+ <entry>ì©ë</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.connection.driver_class</literal>
+ </entry>
+ <entry>
+ <emphasis>jdbc ëë¼ì´ë² í´ëì¤</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.url</literal>
+ </entry>
+ <entry>
+ <emphasis>jdbc URL</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.username</literal>
+ </entry>
+ <entry>
+ <emphasis>ë°ì´í°ë² ì´ì¤ ì¬ì©ì</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.password</literal>
+ </entry>
+ <entry>
+ <emphasis>ë°ì´í°ë² ì´ì¤ ì¬ì©ì í¨ì¤ìë</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.pool_size</literal>
+ </entry>
+ <entry>
+ <emphasis>íë§ë 커ë¥ì
ë¤ì ìµë ê°ì</emphasis>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ íì§ë§ Hibernate ìì ì 커ë¥ì
íë§ ìê³ ë¦¬ì¦ì ì주 기본ì ì´ë¤. ê·¸ê²ì ë¹ì ì´ ììíë ê²ì ëìì£¼ë ¤ê³ ìëëìê³ <emphasis>ì í
+ ìì¤í
ì©ë ëë í¼í¬ë¨¼ì¤ í
ì¤í¸ì©ì¼ë¡ë ê³ ìëì§ ììë¤</emphasis>. ìµìì í¼í¬ë¨¼ì¤ì ìì ì±ì ìí´ìë ì 3ì íì ì¬ì©íë¼. ì¦
+ <literal>hibernate.connection.pool_size</literal> íë¡í¼í°ë¥¼ 커ë¥ì
í ì§ì ì¤ì ë¤ë¡ ëì²´íë¼. ì´ê²ì Hibernateì ë´ë¶
+ poolì ì¤íìí¬ ê²ì´ë¤. ì를 ë¤ì´ ë¹ì ì C3P0를 ì¬ì©í ìë ìë¤.
+ </para>
+
+ <para>
+ C3P0ë <literal>lib</literal> ëíí 리 ìì Hibernateì ë°°í¬ë ì¤í ìì¤ JDBC 커ë¥ì
íì´ë¤. ë¹ì ì´ <literal>hibernate.c3p0.*</literal>
+ íë¡í¼í°ë¤ì ì¤ì í ê²½ì° Hibernateë 커ë¥ì
íë§ì ìí´ ê·¸ê²ì <literal>C3P0ConnectionProvider</literal>를 ì¬ì©í ê²ì´ë¤.
+ ë§ì¼ ë¹ì ì´ Proxoolì ì¬ì©íê³ ì ìí ê²½ì° í¨í¤ì§í ë <literal>hibernate.properties</literal>를 참조íê³ ì¶ê° ì ë³´ë
+ Hibernate ì¹ ì¬ì´í¸ë¥¼ 참조íë¼.
+ </para>
+
+ <para>
+ ë¤ìì C3P0ì ëí ì¬ì©íë ìì <literal>hibernate.properties</literal> íì¼ì´ë¤:
+ </para>
+
+ <programlisting id="c3p0-configuration" revision="1"><![CDATA[hibernate.connection.driver_class = org.postgresql.Driver
+hibernate.connection.url = jdbc:postgresql://localhost/mydatabase
+hibernate.connection.username = myuser
+hibernate.connection.password = secret
+hibernate.c3p0.min_size=5
+hibernate.c3p0.max_size=20
+hibernate.c3p0.timeout=1800
+hibernate.c3p0.max_statements=50
+hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect]]></programlisting>
+
+ <para>
+ ì´í리ì¼ì´ì
ìë² ë´ë¶ì ì©ëë¡, ë¹ì ì JNDIë¡ ë±ë¡ë ì´í리ì¼ì´ì
ìë² <literal>Datasource</literal>ë¡ë¶í° 커ë¥ì
ì ì»ê¸°
+ ìí´ íì Hibernate를 구ì±í´ì¼ íë¤. ë¹ì ì ì ì´ë ë¤ì íë¡í¼í°ë¤ ì¤ íë를 ìµìíì¼ë¡ ì¤ì í íìê° ìì ê²ì´ë¤.
+ </para>
+
+ <table frame="topbot">
+ <title>Hibernate Datasource Properties</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>íë¡í¼í° ì´ë¦</entry>
+ <entry>ì©ë</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.connection.datasource</literal>
+ </entry>
+ <entry>
+ <emphasis>ë°ì´í°ìì¤ JNDI ì´ë¦</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jndi.url</literal>
+ </entry>
+ <entry>
+ <emphasis>JNDI íë¡ë°ì´ëì URL</emphasis> (ìµì
)
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jndi.class</literal>
+ </entry>
+ <entry>
+ <emphasis>JNDI <literal>InitialContextFactory</literal>ì í´ëì¤ </emphasis> (ìµì
)
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.username</literal>
+ </entry>
+ <entry>
+ <emphasis>ë°ì´í°ë² ì´ì¤ ì¬ì©ì</emphasis> (ìµì
)
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.password</literal>
+ </entry>
+ <entry>
+ <emphasis>ë°ì´í°ë² ì´ì¤ ì¬ì©ì í¨ì¤ìë</emphasis> (ìµì
)
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ ë¤ìì ì´í리ì¼ì´ì
ìë² ì ê³µ JNDI ë°ì´í°ìì¤ì© ìì <literal>hibernate.properties</literal> íì¼ì´ë¤:
+ </para>
+
+ <programlisting><![CDATA[hibernate.connection.datasource = java:/comp/env/jdbc/test
+hibernate.transaction.factory_class = \
+ org.hibernate.transaction.JTATransactionFactory
+hibernate.transaction.manager_lookup_class = \
+ org.hibernate.transaction.JBossTransactionManagerLookup
+hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect]]></programlisting>
+
+ <para>
+ JNDI datasourceë¡ë¶í° ì»ì´ì§ JDBC 커ë¥ì
ë¤ì ì´í리ì¼ì´ì
ìë²ì 컨í
ì´ëì ìí´ ê´ë¦¬ëë í¸ëìì
ë¤ì ìëì ì¼ë¡ ì°¸ì¬í ê²ì´ë¤.
+ </para>
+
+ <para>
+ ììì 커ë¥ì
íë¡í¼í°ë¤ì íë¡í¼í° ì´ë¦ ìì "<literal>hibernate.connnection</literal>"ì 첨ê°íì¬ ë¶ì¬ë ì ìë¤. ì를 ë¤ì´
+ ë¹ì ì <literal>hibernate.connection.charSet</literal>ì ì¬ì©íì¬ <literal>charSet</literal>ì ì§ì í ìë ìë¤.
+ </para>
+
+ <para>
+ ë¹ì ì <literal>org.hibernate.connection.ConnectionProvider</literal> ì¸í°íì´ì¤ë¥¼ 구íí¨ì¼ë¡ì¨ JDBC 커ë¥ì
ë¤ì
+ ì»ë ë¹ì ìì ì íë¬ê·¸ì¸ ë°©ë를 ì ìí ìë ìë¤. ë¹ì ì <literal>hibernate.connection.provider_class</literal>를
+ ì¤ì íì¬ ë§ì¶¤í 구íì ì íí ìë ìë¤.
+ </para>
+
+ </sect1>
+
+ <sect1 id="configuration-optional" revision="1">
+ <title>ì íì ì¸ êµ¬ì± íë¡í¼í°ë¤</title>
+
+ <para>
+ ì¤í ìì Hibernateì íì를 ì ì´íë ë§ì ë¤ë¥¸ íë¡í¼í°ë¤ì´ ì¡´ì¬íë¤. 모ë ê²ì´ ìµì
ì´ì§ë§ í©ë¹í ëí´í¸ ê°ë¤ì ê°ëë¤.
+ </para>
+
+ <para>
+ <emphasis>ê²½ê³ : ì´ë¤ íë¡í¼í°ë¤ ì¤ ëªëªì "system-level" ì ì©ì´ë¤.</emphasis> ìì¤í
ë 벨 íë¡í¼í°ë¤ì ì¤ì§
+ <literal>java -Dproperty=value</literal> ëë <literal>hibernate.properties</literal>를 íµí´ìë§ ì¤ì ë ì
+ ìë¤. ê·¸ê²ë¤ì ìì ì¤ëª
ë ë¤ë¥¸ 기ë²ë¤ì ìí´ ì¤ì ë ì <emphasis>ìë¤</emphasis>.
+ </para>
+
+ <table frame="topbot" id="configuration-optional-properties" revision="8">
+ <title>Hibernate êµ¬ì± íë¡í¼í°ë¤</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>íë¡í¼í° ì´ë¦</entry>
+ <entry>ì©ë</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.dialect</literal>
+ </entry>
+ <entry>
+ í¹ì ê´ê³í ë°ì´í°ë² ì´ì¤ì ìµì í ë SQLì ìì±ìí¤ë ê²ì Hibernateìê² íì©í´ì£¼ë
+ Hibernate <literal>Dialect</literal>ì í´ëì¤ëª
.
+ <para>
+ <emphasis role="strong">ì.</emphasis>
+ <literal>full.classname.of.Dialect</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.show_sql</literal>
+ </entry>
+ <entry>
+ 모ë SQL 문ì¥ë¤ì ì½ìì 기ë¡íë¤. ì´ê²ì ë¡ê·¸ ì¹´í
ê³ ë¦¬ <literal>org.hibernate.SQL</literal>를
+ <literal>debug</literal>ë¡ ì¤ì íë ê²ì ëí íëì ë¤ë¥¸ ë°©ë²ì´ë¤.
+ <para>
+ <emphasis role="strong">ì.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.format_sql</literal>
+ </entry>
+ <entry>
+ ë¡ê·¸ì ì½ì ìì SQLì ê¹ëíê² í린í¸íë¤.
+ <para>
+ <emphasis role="strong">ì.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.default_schema</literal>
+ </entry>
+ <entry>
+ ìì±ë SQL ë´ì 주ì´ì§ schema/tablespaceë¡ì ììì´ ìë í
ì´ë¸ì´ë¦ë¤ì ììíë¤.
+ <para>
+ <emphasis role="strong">ì.</emphasis>
+ <literal>SCHEMA_NAME</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.default_catalog</literal>
+ </entry>
+ <entry>
+ 주ì´ì§ SQL ë´ì 주ì´ì§ ì¹´íë¡ì¼ë¡ì ììì´ ìë í
ì´ë¸ì´ë¦ë¤ì ììíë¤.
+ <para>
+ <emphasis role="strong">ì.</emphasis>
+ <literal>CATALOG_NAME</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.session_factory_name</literal>
+ </entry>
+ <entry>
+ <literal>SessionFactory</literal>ë ê·¸ê²ì´ ìì±ë íì JNDI ë´ìì ì´ ì´ë¦ì ìëì ì¼ë¡ ë°ì¸ë
+ ë ê²ì´ë¤.
+ <para>
+ <emphasis role="strong">ì.</emphasis>
+ <literal>jndi/composite/name</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.max_fetch_depth</literal>
+ </entry>
+ <entry>
+ single-ended ì°ê´ê´ê³ë¤(one-to-one, many-to-one)ì ê²½ì°ì outer join fetch í¸ë¦¬ì ìµë "ê¹ì´"를
+ ì¤ì íë¤. <literal>0</literal>ì ëí´í¸ outer join fetchingì ì¬ì©ë¶ê°ë¥íê² ë§ë ë¤.
+ <para>
+ <emphasis role="strong">ì.</emphasis>
+ <literal>0</literal>ê³¼
+ <literal>3</literal> ì¬ì´ì ê°ë¤ì´ê¶ì¥ëë¤
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.default_batch_fetch_size</literal>
+ </entry>
+ <entry>
+ ì°ê´ë¤ì Hibernate ë°°ì¹ íì¹ì ëí ëí´í¸ í¬ê¸°ë¥¼ ì¤ì íë¤.
+ <para>
+ <emphasis role="strong">ì.</emphasis>
+ ê¶ì¥ëë ê°ë¤ì <literal>4</literal>, <literal>8</literal>,
+ <literal>16</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.default_entity_mode</literal>
+ </entry>
+ <entry>
+ ì´ <literal>SessionFactory</literal>ë¡ë¶í° ì´ë ¤ì§ 모ë ì¸ì
ë¤ì ëí´ ìí°í° ííì ëí´í¸ 모ëë¡
+ ì¤ì íë¤
+ <para>
+ <literal>dynamic-map</literal>, <literal>dom4j</literal>,
+ <literal>pojo</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.order_updates</literal>
+ </entry>
+ <entry>
+ ì
ë°ì´í¸ ì¤ì¸ í목ë¤ì íë¼ì´ë¨¸ë¦¬ í¤ ê°ì ìí´ SQL ì
ë°ì´í¸ë¤ì´ ìì(ordering)ì§ìì§ëë¡ Hibernateìê²
+ ê°ì ìí¨ë¤. ì´ê²ì ê³ ëì ëìì± ìì¤í
ë¤ìì ë ì ì í¸ëìì
ë°ëë½(deadlock)ë¤ë¡ ê·ê²°ë ê²ì´ë¤
+ <para>
+ <emphasis role="strong">ì.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.generate_statistics</literal>
+ </entry>
+ <entry>
+ ì´ì© ê°ë¥íê² ëë©´, Hibernateë í¼í¬ë¨¼ì¤ íëì ì ì©í íµê³ë¤ì ìì§í ê²ì´ë¤.
+ <para>
+ <emphasis role="strong">ì.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.use_identifer_rollback</literal>
+ </entry>
+ <entry>
+ ì´ì© ê°ë¥íê² ëë©´, ê°ì²´ê° ìì ë ë ìì±ë ìë³ì íë¡í¼í°ë¤ì ëí´í¸ ê°ë¤ë¡ ì¬ì¤ì ë ê²ì´ë¤.
+ <para>
+ <emphasis role="strong">ì.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.use_sql_comments</literal>
+ </entry>
+ <entry>
+ ì´ì© ê°ë¥íê² ëë©´, Hibernateë ë³´ë¤ ì¬ì´ ëë²ê¹
ì ìí´ SQL ë´ì 주ìë¤ì ìì±ìí¬ ê²ì´ë¤.
+ ëí´í¸ë <literal>false</literal>.
+ <para>
+ <emphasis role="strong">ì.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame="topbot" id="configuration-jdbc-properties" revision="8">
+ <title>Hibernate JDBC ë° ì»¤ë¥ì
íë¡í¼í°ë¤</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>íë¡í¼í° ì´ë¦</entry>
+ <entry>ì©ë</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.fetch_size</literal>
+ </entry>
+ <entry>
+ 0 ìë ê°ì JDBC fetch ì¬ì´ì¦ë¥¼ ê²°ì íë¤(<literal>Statement.setFetchSize()</literal>ì í¸ì¶íë¤ ).
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.batch_size</literal>
+ </entry>
+ <entry>
+ 0 ìë ê°ì Hibernateì ìí JDBC2 ë°°ì¹ ì
ë°ì´í¸ì ì¬ì©ì ì´ì© ê°ë¥íê² íë¤.
+ <para>
+ <emphasis role="strong">ì.</emphasis>
+ <literal>5</literal>ì <literal>30</literal> ì¬ì´ì ê°ë¤ì´ ê¶ì¥ëë¤
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.batch_versioned_data</literal>
+ </entry>
+ <entry>
+ ë¹ì ì JDBC ëë¼ì´ë²ê° <literal>executeBatch()</literal>ë¡ë¶í° ì íí í ì¹´ì´í¸ë¤ì ë°íí ê²½ì°ì
+ ì´ íë¡í¼í°ë¥¼ <literal>true</literal>ë¡ ì¤ì íë¼(ëê° ì´ ìµì
ì ì¬ì© ê°ë¥íê² íë ê²ì´ ìì íë¤).
+ ê·¸ë¬ë©´ Hibernateë ìëì ì¼ë¡ ë²ì í ë ë°ì´í°ì ëí´ ë°°ì¹íë(batched) DMLì ì¬ì©í ê²ì´ë¤.
+ ëí´í¸ë <literal>false</literal>.
+ <para>
+ <emphasis role="strong">ì.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.factory_class</literal>
+ </entry>
+ <entry>
+ ë§ì¶¤í <literal>Batcher</literal>를 ì ííë¤. ëë¶ë¶ì ì´í리ì¼ì´ì
ë¤ì ì´ êµ¬ì± íë¡í¼í°ë¥¼ íìë¡ íì§
+ ìì ê²ì´ë¤.
+ <para>
+ <emphasis role="strong">ì.</emphasis>
+ <literal>classname.of.Batcher</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.use_scrollable_resultset</literal>
+ </entry>
+ <entry>
+ Hibernateì ìí JDBC2 ì¤í¬ë¡¤ ê°ë¥í ê²°ê³¼ì
ë¤ì ì¬ì©ì ê°ë¥íê² í´ì¤ë¤. ì´ íë¡í¼í°ë ì¬ì©ìê° ì ê³µí
+ JDBC커ë¥ì
ë¤ì ì¬ì©í ëìë§ íìì ì´ê³ , ê·¸ ë°ì ê²½ì° Hibernateë 커ë¥ì
ë©íë°ì´í°ë¥¼ ì¬ì©íë¤.
+ <para>
+ <emphasis role="strong">ì.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.use_streams_for_binary</literal>
+ </entry>
+ <entry>
+ <literal>binary</literal> ëë <literal>serializable</literal> íì
ë¤ì JDBCë¡ ê¸°ë¡íê³
+ /JDBCë¡ë¶í° <literal>binary</literal> ëë <literal>serializable</literal> íì
ë¤ì ì½ì´ë¤ì¼ ë
+ ì¤í¸ë¦¼ë¤ì ì¬ì©íë¤(ìì¤í
-ë 벨 íë¡í¼í°).
+ <para>
+ <emphasis role="strong">ì.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.use_get_generated_keys</literal>
+ </entry>
+ <entry>
+ insert íì ê³ ì íê² ìì±ë í¤ë¤ì ê²ìíëë° JDBC3 <literal>PreparedStatement.getGeneratedKeys()</literal>ì
+ ì¬ì©ì ì´ì© ê°ë¥íëë¡ ë§ë ë¤. JDBC3+ ëë¼ì´ë²ì JRE1.4+를 íìë¡ íê³ , ë¹ì ì ëë¼ì´ë²ê° Hibernate
+ ìë³ì ìì±ìë¤ì 문ì ê° ìì ê²½ì°ì falseë¡ ì¤ì íë¼. ëí´í¸ë¡ 커ë¥ì
ë©í ë°ì´í°ë¥¼ ì¬ì©íì¬ ëë¼ì´ë²
+ ê°ì©ì±ë¤ì ê²°ì íë ¤ê³ ìëíë¼.
+ <para>
+ <emphasis role="strong">ì.</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.provider_class</literal>
+ </entry>
+ <entry>
+ Hibernateì JDBC 커ë¥ì
ë¤ì ì ê³µíë ë§ì¶¤í <literal>ConnectionProvider</literal>ì í´ëì¤ëª
.
+ <para>
+ <emphasis role="strong">ì.</emphasis>
+ <literal>classname.of.ConnectionProvider</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.isolation</literal>
+ </entry>
+ <entry>
+ JDBC transaction isolation ë 벨ì ì¤ì íë¤. ì미ìë ê°ë¤ë¡ <literal>java.sql.Connection</literal>ì
+ ì²´í¬íì§ë§ ëë¶ë¶ì ë°ì´í°ë² ì´ì¤ë¤ì´ 모ë 격리(isolate) ë 벨ë¤ì ì§ìíì§ ììì ë
¸í¸íë¼.
+ <para>
+ <emphasis role="strong">ì.</emphasis>
+ <literal>1, 2, 4, 8</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.autocommit</literal>
+ </entry>
+ <entry>
+ JDBC íë§ë 커ë¥ì
ë¤ì ëí´ ìë커ë°ì ì´ì© ê°ë¥íëë¡ íë¤(ê¶ì¥ëì§ ìì).
+ <para>
+ <emphasis role="strong">ì.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.release_mode</literal>
+ </entry>
+ <entry>
+ Hibernateê° JDBC 커ë¥ì
ë¤ì í´ì íê² ë ìì ì ì§ì íë¤. ëí´í¸ë¡ í ê°ì JDBC 커ë¥ì
ì ê·¸ ì¸ì
ì´ ëª
ìì ì¼ë¡
+ ë«íê±°ë ì°ê²°í´ì ë기 ì ê¹ì§ ë³´ê´ëë¤. ì´í리ì¼ì´ì
í¸ëìì
ìë² JTA ë°ì´í°ìì¤ì ê²½ì°, ë¹ì ì 모ë JDBC
+ í¸ì¶ íì 커ë¥ì
ë¤ì ê³¼ê°íê² í´ì ìí¤ê¸° ìí´ <literal>after_statement</literal>를 ì¬ì©í´ì¼ íë¤. ë¹-JTA
+ ì°ê²°ì ê²½ì°, <literal>after_transaction</literal>ì ì¬ì©íì¬ ê°ê°ì í¸ëìì
ì ëìì 커ë¥ì
ë¤ì
+ í´ì ìí¤ë ê²ì´ ì¢
ì¢
ìë¯¸ê° ìë¤. <literal>auto</literal>ë JTA ë° CMT í¸ëìì
ë°©ëë¤ì ê²½ì°ì
+ <literal>after_statement</literal>를 ì ííê³ JDBC í¸ëìì
ë°©ëì ëí´ <literal>after_transaction</literal>를
+ ì íí ê²ì´ë¤.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>auto</literal> (ëí´í¸) | <literal>on_close</literal> |
+ <literal>after_transaction</literal> | <literal>after_statement</literal>
+ </para>
+ <para>
+ ì´ ì¤ì ì´ <literal>SessionFactory.openSession</literal>ë¡ë¶í° ë°íë <literal>Session</literal>ë¤ìë§
+ ìí¥ì ì¤ë¤ë ì ì ë
¸í¸íë¼. <literal>SessionFactory.getCurrentSession</literal>ì íµí´ ì»ì´ì§
+ <literal>Session</literal>ë¤ì ê²½ì°, ì¬ì©í기 ìí´ êµ¬ì±ë <literal>CurrentSessionContext</literal> 구íì´
+ ê·¸ë¤ <literal>Session</literal>ë¤ì ëí ì°ê²° í´ì 를 ì ì´íë¤. <xref linkend="architecture-current-session"/>를
+ ë³´ë¼.
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.<emphasis><propertyName></emphasis></literal>
+ </entry>
+ <entry>
+ JDBC íë¡í¼í° <literal>propertyName</literal>ì <literal>DriverManager.getConnection()</literal>ì
+ ì ë¬íë¤.
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jndi.<emphasis><propertyName></emphasis></literal>
+ </entry>
+ <entry>
+ <literal>propertyName</literal> íë¡í¼í°ë¥¼ JNDI <literal>InitialContextFactory</literal>ì ì ë¬íë¤.
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame="topbot" id="configuration-cache-properties" revision="7">
+ <title>Hibernate Cache íë¡í¼í°ë¤</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>íë¡í¼í° ì´ë¦</entry>
+ <entry>ì©ë</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.cache.provider_class</literal>
+ </entry>
+ <entry>
+ ë§ì¶¤í <literal>CacheProvider</literal>ì í´ëì¤ëª
.
+ <para>
+ <emphasis role="strong">ì.</emphasis>
+ <literal>classname.of.CacheProvider</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.use_minimal_puts</literal>
+ </entry>
+ <entry>
+ ì½ê¸°ê° ë§¤ì° ë¹ë²í ê²½ì°ì, ì°ê¸°ë¥¼ ìµìí ìí¤ê¸° ìí´ second-level ìºì ì°ì°ì ìµì í ìí¨ë¤. ì´ ì¤ì ì Hibernate3ìì í´ë¬ì¤í°ë§ ë
+ ìºìë¤ì ê°ì¥ ì ì©íê³ , Hibernate3ììë í´ë¬ì¤í°ë§ë ìºì 구íë¤ì ëí´ ëí´í¸ë¡ ì´ì© ê°ë¥íë¤.
+ <para>
+ <emphasis role="strong">ì.</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.use_query_cache</literal>
+ </entry>
+ <entry>
+ ì§ì ìºì를 ê°ë¥íê² ë§ë ë¤. ê°ë³ ì§ìë¤ì ì¬ì í ìºì ê°ë¥í ê²ì¼ë¡ ì¤ì ëì´ì¼ íë¤.
+ <para>
+ <emphasis role="strong">ì.</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.use_second_level_cache</literal>
+ </entry>
+ <entry>
+ second-level ìºì를 ìì í ì¬ì© ë¶ê°ë¥íê² íëë° ì¬ì©ë ì ìê³ , ê·¸ê²ì <literal><cache></literal> 매íì
+ ì§ì íë í´ëì¤ë¤ì ëí´ ëí´í¸ë¡ ì´ì© ê°ë¥ì´ë¤.
+ <para>
+ <emphasis role="strong">ì.</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.query_cache_factory</literal>
+ </entry>
+ <entry>
+ ë§ì¶¤í <literal>QueryCache</literal> ì¸í°íì´ì¤ì í´ëì¤ëª
. ëí´í¸ë 미리 ë¹ëë
+ <literal>StandardQueryCache</literal>.
+ <para>
+ <emphasis role="strong">ì.</emphasis>
+ <literal>classname.of.QueryCache</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.region_prefix</literal>
+ </entry>
+ <entry>
+ second-level ìºì ìì ì´ë¦ë¤ì ì¬ì©í ì ëì´.
+ <para>
+ <emphasis role="strong">ì.</emphasis>
+ <literal>prefix</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.use_structured_entries</literal>
+ </entry>
+ <entry>
+ ì¸ê°ìê² ë³´ë¤ ë ì¹ìí íìì¼ë¡ second-level ìºì ìì ë°ì´í°ë¥¼ ì ì¥íëë¡ Hibernateìê² ê°ì ìí¨ë¤..
+ <para>
+ <emphasis role="strong">ì.</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame="topbot" id="configuration-transaction-properties" revision="9">
+ <title>Hibernate í¸ëìì
íë¡í¼í°ë¤</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>íë¡í¼í° ì´ë¦</entry>
+ <entry>ì©ë</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.transaction.factory_class</literal>
+ </entry>
+ <entry>
+ Hibernate <literal>Transaction</literal> API ì ì¬ì©í <literal>TransactionFactory</literal>ì
+ í´ëì¤ ì´ë¦.(ëí´í¸ë <literal>JDBCTransactionFactory</literal>).
+ <para>
+ <emphasis role="strong">ì.</emphasis>
+ <literal>classname.of.TransactionFactory</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>jta.UserTransaction</literal>
+ </entry>
+ <entry>
+ ì´í리ì¼ì´ì
ìë²ë¡ë¶í° JTA <literal>UserTransaction</literal>ì ì»ê¸° ìí´ <literal>JTATransactionFactory</literal>ì
+ ìí´ ì¬ì©ëë JNDI ì´ë¦.
+ <para>
+ <emphasis role="strong">ì.</emphasis>
+ <literal>jndi/composite/name</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.transaction.manager_lookup_class</literal>
+ </entry>
+ <entry>
+ <literal>TransactionManagerLookup</literal>ì í´ëì¤ëª
- JVM ë 벨ì ìºì±ì´ ì´ì© ê°ë¥í ë ëë JTA íê²½ìì
+ hilo generator를 ì¬ì©í ë íìíë¤.
+ <para>
+ <emphasis role="strong">ì.</emphasis>
+ <literal>classname.of.TransactionManagerLookup</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.transaction.flush_before_completion</literal>
+ </entry>
+ <entry>
+ ë§ì¼ ì¬ì©ê°ë¥íëë¡ ëë©´, ì¸ì
ì í¸ëìì
ì before completion ë¨ê³ ëìì ìëì ì¼ë¡ flush ë ê²ì´ë¤.
+ ë¹ëëì´ ìë ìëì ì¸ ì¸ì
컨í
ì¤í¸ ê´ë¦¬ê° ì í¸ëëë°,
+ <xref linkend="architecture-current-session"/>를 ë³´ë¼.
+ <para>
+ <emphasis role="strong">ì.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.transaction.auto_close_session</literal>
+ </entry>
+ <entry>
+ ë§ì¼ ì¬ì©ê°ë¥í ë¡ íë©´, after completion ë¨ê³ ëìì ì¸ì
ì´ ìëì ì¼ë¡ ë«íì§ ê²ì´ë¤.
+ (CMTì ëí´ Hibernate를 ì¬ì©í ë ë§¤ì° ì ì©íë¤.)
+ <para>
+ <emphasis role="strong">ì.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame="topbot" id="configuration-misc-properties" revision="10">
+ <title>ì¬ë¬ê°ì§ íë¡í¼í°ë¤</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>íë¡í¼í° ì´ë¦</entry>
+ <entry>ì©ë</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.current_session_context_class</literal>
+ </entry>
+ <entry>
+ "íì¬" <literal>Session</literal>ì ììí를 ìí íëì (ë§ì¶¤) ë°©ë를
+ ì ê³µíë¤. ë¹ëëì´ ìë ë°©ëë¤ì ëí ì¶ê° ì ë³´ë
+ <xref linkend="architecture-current-session"/>를 ë³´ë¼.
+ <para>
+ <emphasis role="strong">ì.</emphasis>
+ <literal>jta</literal> | <literal>thread</literal> |
+ <literal>managed</literal> | <literal>custom.Class</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.query.factory_class</literal>
+ </entry>
+ <entry>
+ Chooses the HQL íì 구íì ì ííë¤.
+ <para>
+ <emphasis role="strong">ì.</emphasis>
+ <literal>org.hibernate.hql.ast.ASTQueryTranslatorFactory</literal> or
+ <literal>org.hibernate.hql.classic.ClassicQueryTranslatorFactory</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.query.substitutions</literal>
+ </entry>
+ <entry>
+ Hibernate ì§ìë¤ ë´ì í í°ë¤ë¡ë¶í° SQL í í°ë¤ë¡ì 매í
+ (ì를 ë¤ì´ í í°ë¤ì í¨ì ì´ë¦ ëë 리í°ë´ ì´ë¦ì¼ ì ìë¤).
+ <para>
+ <emphasis role="strong">ì.</emphasis>
+ <literal>hqlLiteral=SQL_LITERAL, hqlFunction=SQLFUNC</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.hbm2ddl.auto</literal>
+ </entry>
+ <entry>
+ <literal>SessionFactory</literal>ê° ìì±ë ë, ìëì ì¼ë¡ ì í¨ì±ì ê²ì¬íê±°ë
+ schema DDLì ë°ì´í°ë² ì´ì¤ë¡ ë´ë³´ë´ê¸° íë¤. <literal>create-drop</literal>ì ê²½ì°,
+ <literal>SessionFactory</literal>ê° ëª
ìì ì¼ë¡ ë«íì§ ë ë°ì´í°ë² ì´ì¤ ì¤í¤ë§ê°
+ ëë¡ë ê²ì´ë¤.
+ <para>
+ <emphasis role="strong">ì.</emphasis>
+ <literal>validate</literal> | <literal>update</literal> |
+ <literal>create</literal> | <literal>create-drop</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cglib.use_reflection_optimizer</literal>
+ </entry>
+ <entry>
+ ë°íì reflection ëì ì CGLIBì ì¬ì©ì ê°ë¥íëë¡ ë§ë ë¤(ìì¤í
ë 벨 íë¡í¼í°).
+ Reflectionì 문ì ê° ë°ìí ìì ëëë¡ ì ì©í ì ìê³ ,
+ ë¹ì ì´ optimizer를 ì¬ì©íì§ ìì ê²½ì°ì¡°ì°¨ë Hibernateë íì íìë¡ í¨ì ì ìíë¼.
+ ë¹ì ì <literal>hibernate.cfg.xml</literal>
+ ìì ì´ íë¡í¼í°ë¥¼ ì¤ì í ì ìë¤.
+ <para>
+ <emphasis role="strong">ì.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <sect2 id="configuration-optional-dialects" revision="1">
+ <title>SQL Dialects</title>
+
+ <para>
+ ë¹ì ì íì ë¹ì ì ë°ì´í°ë² ì´ì¤ë¥¼ ìí´ <literal>hibernate.dialect</literal> íë¡í¼í°ë¥¼ ì íí <literal>org.hibernate.dialect.Dialect</literal>
+ ìë¸í´ëì¤ë¡ ì¤ì í´ì¼ íë¤. ë§ì¼ ë¹ì ì´ dialect를 ì§ì í ê²½ì°, ë¹ì ì´ íë¡í¼í°ë¤ì ììì
ì¼ë¡ ì§ì íë ë
¸ë ¥ì ì ì½íëë¡ Hibernateë ìì ì´ê±°ë ë¤ë¥¸ íë¡í¼í°ë¤
+ ì¤ ëªëªì ëí´ ì미ìë ëí´í¸ë¤ì ì¬ì©í ê²ì´ë¤.
+ </para>
+
+ <table frame="topbot" id="sql-dialects" revision="2">
+ <title>Hibernate SQL Dialects (<literal>hibernate.dialect</literal>)</title>
+ <tgroup cols="2">
+ <colspec colwidth="1*"/>
+ <colspec colwidth="2.5*"/>
+ <thead>
+ <row>
+ <entry>RDBMS</entry>
+ <entry>Dialect</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>DB2</entry> <entry><literal>org.hibernate.dialect.DB2Dialect</literal></entry>
+ </row>
+ <row>
+ <entry>DB2 AS/400</entry> <entry><literal>org.hibernate.dialect.DB2400Dialect</literal></entry>
+ </row>
+ <row>
+ <entry>DB2 OS390</entry> <entry><literal>org.hibernate.dialect.DB2390Dialect</literal></entry>
+ </row>
+ <row>
+ <entry>PostgreSQL</entry> <entry><literal>org.hibernate.dialect.PostgreSQLDialect</literal></entry>
+ </row>
+ <row>
+ <entry>MySQL</entry> <entry><literal>org.hibernate.dialect.MySQLDialect</literal></entry>
+ </row>
+ <row>
+ <entry>MySQL with InnoDB</entry> <entry><literal>org.hibernate.dialect.MySQLInnoDBDialect</literal></entry>
+ </row>
+ <row>
+ <entry>MySQL with MyISAM</entry> <entry><literal>org.hibernate.dialect.MySQLMyISAMDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Oracle (any version)</entry> <entry><literal>org.hibernate.dialect.OracleDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Oracle 9i/10g</entry> <entry><literal>org.hibernate.dialect.Oracle9Dialect</literal></entry>
+ </row>
+ <row>
+ <entry>Sybase</entry> <entry><literal>org.hibernate.dialect.SybaseDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Sybase Anywhere</entry> <entry><literal>org.hibernate.dialect.SybaseAnywhereDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Microsoft SQL Server</entry> <entry><literal>org.hibernate.dialect.SQLServerDialect</literal></entry>
+ </row>
+ <row>
+ <entry>SAP DB</entry> <entry><literal>org.hibernate.dialect.SAPDBDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Informix</entry> <entry><literal>org.hibernate.dialect.InformixDialect</literal></entry>
+ </row>
+ <row>
+ <entry>HypersonicSQL</entry> <entry><literal>org.hibernate.dialect.HSQLDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Ingres</entry> <entry><literal>org.hibernate.dialect.IngresDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Progress</entry> <entry><literal>org.hibernate.dialect.ProgressDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Mckoi SQL</entry> <entry><literal>org.hibernate.dialect.MckoiDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Interbase</entry> <entry><literal>org.hibernate.dialect.InterbaseDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Pointbase</entry> <entry><literal>org.hibernate.dialect.PointbaseDialect</literal></entry>
+ </row>
+ <row>
+ <entry>FrontBase</entry> <entry><literal>org.hibernate.dialect.FrontbaseDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Firebird</entry> <entry><literal>org.hibernate.dialect.FirebirdDialect</literal></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-outerjoin" revision="4">
+ <title>Outer Join Fetching</title>
+
+ <para>
+ ë§ì¼ ë¹ì ì ë°ì´í°ë² ì´ì¤ê° ANSI, Oracle, ëë Sybase ì¤íì¼ì outer joinë¤ì ì§ìí ê²½ì°, <emphasis>outer join
+ fetching</emphasis>ì (ë°ì´í°ë² ì´ì¤ ê·¸ ìì²´ì ìí´ ë³´ë¤ ë ë§ì ìì
ì´ ìíëë ë¹ì©ì¼ë¡) ë°ì´í°ë² ì´ì¤ë¡ì 그리ê³
+ ë°ì´í°ë² ì´ì¤ë¡ë¶í°ì ë¼ì´ë í¸ë¦½ë¤ì ê°ì를 ì íí¨ì¼ë¡ì¨ ì¢
ì¢
í¼í¬ë¨¼ì¤ë¥¼ ì¦ê°ìí¬ ê²ì´ë¤. Outer join fetchingì
+ many-to-one, one-to-many, many-to-many,one-to-one ì°ê´ê´ê³ë¤ì´ ì ìí´ ì°ê²°ë ê°ì²´ë¤ì ì ì²´ ê·¸ëíê°
+ íëì SQL <literal>SELECT</literal> ììì ê²ìëê²ë íì©í´ì¤ë¤.
+ </para>
+
+ <para>
+ Outer join fetchingì hibernate.max_fetch_depth íë¡í¼í°ë¥¼ <literal>0</literal>ì¼ë¡ ì¤ì í¨ì¼ë¡ì¨
+ <emphasis>ì ìì ì¼ë¡</emphasis> ì¬ì© ë¶ê°ë¥íê² í ì ìë¤. <literal>1</literal> ì´ìì ê°ì ì¤ì íë
+ ê²ì <literal>fetch="join"</literal>ì¼ë¡ 매íëìë 모ë one-to-one ë° many-to-one ì°ê´ê´ê³ë¤ì ëí´
+ outer join fetchingì ì¬ì© ê°ë¥íëë¡ ë§ë ë¤.
+ </para>
+
+ <para>
+ ì¶ê° ì ë³´ë <xref linkend="performance-fetching"/>를 ë³´ë¼.
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-binarystreams" revision="1">
+ <title>Binary Streams</title>
+
+ <para>
+ Oracleì JDBC ëë¼ì´ë² ë¡/ë¶í° ì ë¬ëë <literal>byte</literal> ë°°ì´ë¤ì í¬ê¸°ë¥¼ ì íìí¨ë¤. ë§ì¼ ë¹ì ì´
+ <literal>binary</literal> ëë <literal>serializable</literal> íì
ì ëí ì¸ì¤í´ì¤ë¥¼ ì¬ì©íê³ ì
+ ìí ê²½ì°ì, ë¹ì ì <literal>hibernate.jdbc.use_streams_for_binary</literal>를 ì¬ì© ê°ë¥íê² í´ì¼
+ í ê²ì´ë¤. <emphasis>ì´ê²ì ì¤ì§ ìì¤í
ë 벨 ì¤ì ì´ë¤.</emphasis>
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-cacheprovider" revision="2">
+ <title>Second-level ìºìì query ìºì</title>
+
+ <para>
+ <literal>hibernate.cache</literal> ì ëì´ê° ë¶ì íë¡í¼í°ë¤ì Hibernateì ëí´ íë¡ì¸ì¤ ëë
+ í´ë¬ì¤í° ë²ìì ë ë²ì§¸ ë 벨 ìºì ìì¤í
ì ì¬ì©íë ê²ì íì©í´ì¤ë¤. ìì¸í ê²ì
+ <xref linkend="performance-cache"/>를 ë³´ë¼.
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-querysubstitution">
+ <title>Query Language ì¹í</title>
+
+ <para>
+ ë¹ì ì <literal>hibernate.query.substitutions</literal>ì ì¬ì©íì¬ ìë¡ì´ Hibernate ì§ì í í°ë¤ì
+ ì ìí ì ìë¤. ì를 ë¤ì´:
+ </para>
+
+ <programlisting>hibernate.query.substitutions true=1, false=0</programlisting>
+
+ <para>
+ ì<literal>true</literal>ì <literal>false</literal> í í°ë¤ì´ ìì±ë SQL ë´ìì
+ ì ì 리í°ë´ë¤ë¡ ë²ìëëë¡ ê°ì í ê²ì´ë¤.
+ </para>
+
+ <programlisting>hibernate.query.substitutions toLowercase=LOWER</programlisting>
+
+ <para>
+ ì SQL <literal>LOWER</literal> function í¨ì ì´ë¦ì ë³ê²½íë ê²ì ë¹ì ìê² íì©í´ ì¤ ê²ì´ë¤
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-statistics" revision="2">
+ <title>Hibernate íµê³</title>
+
+ <para>
+ ë§ì¼ ë¹ì ì´ <literal>hibernate.generate_statistics</literal>를 ì¬ì© ê°ë¥íëë¡ í ê²½ì°, Hibernateë
+ <literal>SessionFactory.getStatistics()</literal>를 íµí´ ê°ë ì¤ì¸ ìì¤í
ì íëí ë ì ì©í ë§ì íµê³ë¤ì
+ ë
¸ì¶ìí¬ ê²ì´ë¤. Hibernateë ì¬ì§ì´ JMX를 íµí´ ì´ë¤ íµê³ë¤ì ë
¸ì¶ìí¤ëë¡ êµ¬ì±ë ì ìë¤. ì¶ê° ì ë³´ë
+ <literal>org.hibernate.stats</literal>ì ìë ì¸í°íì´ì¤ë¤ì ê´í Javadoc를 ì½ì´ë¼.
+ </para>
+
+ </sect2>
+ </sect1>
+
+ <sect1 id="configuration-logging">
+ <title>ë¡ê¹
</title>
+
+ <para>
+ Hibernateë Apache commons-logging를 ì¬ì©íì¬ ë¤ìí ì´ë²¤í¸ë¤ì ë¡ê·¸ìí¨ë¤.
+ </para>
+
+ <para>
+ commons-logging ìë¹ì¤ë (ë§ì¼ ë¹ì ì´ classpath ë´ì <literal>log4j.jar</literal>를 í¬í¨í ê²½ì°) Apache Log4jë¡
+ ëë (JDK1.4 ì´ìì ë²ì ìì ì¤íë ê²½ì°) JDK 1.4 ë¡ê¹
ì¼ë¡ ì§ì ì¶ë ¥í ê²ì´ë¤. ë¹ì ì <literal>http://jakarta.apache.org</literal>ìì
+ Log4j를 ë¤ì´ë¡ë í ì ìë¤. Log4j를 ì¬ì©í기 ìí´, ë¹ì ì <literal>log4j.properties</literal> íì¼ì ë¹ì ì classpath
+ ë´ì ìì¹ì§ì¸ íìê° ìì ê²ì´ê³ , ìì properties íì¼ì Hibernateì <literal>src/</literal> ëë í 리 ë´ì ë°°í¬ëì´ ìë¤.
+ </para>
+
+ <para>
+ ì°ë¦¬ë ë¹ì ì´ Hibernateì ë¡ê·¸ ë©ìì§ë¤ì ìµìí´ì§ê¸°ë¥¼ ê°ë ¥í ê¶ì¥íë¤. ì½ê¸° ë¶ê°ë¥íì§ ìê²ë ê°ë¥í í ìì¸íê² Hibernate ë¡ê·¸ë¥¼
+ ë§ë¤ëë¡ ë§ì ìì
ì´ íí´ì¡ë¤. ê·¸ê²ì 본ì§ì ì¸ ë¬¸ì ëì§ê¸° ì¥ì¹ì´ë¤. ê°ì¥ í¥ë¯¸ë¡ì´ ë¡ê·¸ ì¹´í
ê³ ë¦¬ë¤ì´ ë¤ìì ìë¤:
+ </para>
+
+ <table frame="topbot" id="log-categories" revision="2">
+ <title>Hibernate ë¡ê·¸ ì¹´í
ê³ ë¦¬ë¤</title>
+ <tgroup cols="2">
+ <colspec colwidth="1*"/>
+ <colspec colwidth="2.5*"/>
+ <thead>
+ <row>
+ <entry>ì¹´í
ê³ ë¦¬</entry>
+ <entry>기ë¥</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>org.hibernate.SQL</literal></entry>
+ <entry>SQL DML 문ì¥ë¤ì´ ì¤íë ë ê·¸ê²ë¤ 모ë를 ë¡ê·¸ ìí¨ë¤</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.type</literal></entry>
+ <entry>모ë JDBC íë¼ë¯¸í°ë¤ì ë¡ê·¸ìí¨ë¤</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.tool.hbm2ddl</literal></entry>
+ <entry>SQL DDL 문ì¥ë¤ì´ ì¤íë ë ê·¸ê²ë¤ 모ë를 ë¡ê·¸ ìí¨ë¤</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.pretty</literal></entry>
+ <entry>
+ flush ìì ìì ì¸ì
ê³¼ ì°ê´ë 모ë ìí°í°ë¤(ìµë 20ê°ì ìí°í°ë¤)ì ìí를 ë¡ê·¸ ìí¨ë¤
+ </entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.cache</literal></entry>
+ <entry>모ë second-level ìºì ì¡í°ë¹í°ë¥¼ ë¡ê·¸ìí¨ë¤</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction</literal></entry>
+ <entry>í¸ëìì
ê´ë ¨ ì¡í°ë¹í°ë¥¼ ë¡ê·¸ ìí¨ë¤</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.jdbc</literal></entry>
+ <entry>모ë JDBC 리ìì¤ ì·¨ëì ë¡ê·¸ ìí¨ë¤</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.hql.ast.AST</literal></entry>
+ <entry>
+ ì§ì íì± ëìì HQL ASTì SQL AST를 ë¡ê·¸ìí¨ë¤
+ </entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.secure</literal></entry>
+ <entry>모ë JAAS íê° ìì²ë¤ì ë¡ê·¸ìí¨ë¤</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate</literal></entry>
+ <entry>
+ 모ë ê²ì ë¡ê·¸ìí¨ë¤(ë§ì ì ë³´ì´ì§ë§, 문ì í´ê²°ì ë§¤ì° ì ì©íë¤)
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ Hibernateë¡ ì´í리ì¼ì´ì
ë¤ì ê°ë°í ë, ë¹ì ì ê±°ì íì <literal>org.hibernate.SQL</literal> ì¹´í
ê³ ë¦¬ì ëí´
+ ì´ì© ê°ë¥í <literal>debug</literal> 모ëë¡ ìì
íê±°ë, ë¤ë¥¸ ë°©ë²ì¼ë¡ <literal>hibernate.show_sql</literal>
+ íë¡í¼í°ë¥¼ ì´ì©ê°ë¥íê² íì¬ ìì
í´ì¼ í ê²ì´ë¤.
+ </para>
+
+
+ </sect1>
+
+ <sect1 id="configuration-namingstrategy">
+ <title><literal>NamingStrategy</literal> 구íí기</title>
+
+ <para>
+ <literal>org.hibernate.cfg.NamingStrategy</literal> ì¸í°íì´ì¤ë ë°ì´í°ë² ì´ì¤ ê°ì²´ë¤ê³¼ ì¤í¤ë§ ììë¤ì ëí
+ "ë¤ì´ë° íì¤"ì ì§ì íë ê²ì ë¹ì ìê² íì©í´ì¤ë¤.
+ </para>
+
+ <para>
+ ë¹ì ì Java ìë³ìë¤ë¡ë¶í° ë°ì´í°ë² ì´ì¤ ìë³ìë¤ì ìëì ì¼ë¡ ìì±ìí¤ê±°ë 매í íì¼ì 주ì´ì§ "ë
¼ë¦¬ì " 컬ë¼ê³¼ í
ì´ë¸
+ ì´ë¦ë¤ì "물리ì " í
ì´ë¸ê³¼ ì»¬ë¼ ì´ë¦ë¤ë¡ ìëì ì¼ë¡ ì²ë¦¬íë ê·ì¹ë¤ì ì ê³µí ì ìë¤. ì´ í¹ì§ì ë°ë³µëë ì¡ì(ì를 ë¤ì´
+ <literal>TBL_</literal>ì ëì´ë¤)ì ì ê±°í¨ì¼ë¡ì¨, 매í 문ìì ë§ë§ì ì¥í©í¨ì ê°ììí¤ëë¡ ëìì¤ë¤. Hibernateì
+ ìí´ ì¬ì©ëë ëí´í¸ ë°©ëë ì주 ìì ìíì´ë¤.
+ </para>
+
+ <para>
+ ë¹ì ì 매íë¤ì ì¶ê°í기 ì´ì ì <literal>Configuration.setNamingStrategy()</literal>를 í¸ì¶í¨ì¼ë¡ì¨ ë¤ë¥¸ ë°©ë를
+ ì§ì í ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[SessionFactory sf = new Configuration()
+ .setNamingStrategy(ImprovedNamingStrategy.INSTANCE)
+ .addFile("Item.hbm.xml")
+ .addFile("Bid.hbm.xml")
+ .buildSessionFactory();]]></programlisting>
+
+ <para>
+ <literal>org.hibernate.cfg.ImprovedNamingStrategy</literal>ë ì´ë¤ ì´í리ì¼ì´ì
ë¤ì ëí ì ì©í ììì ì¼ ì ìë
+ 미리 ë¹ëë ë°©ëì´ë¤.
+ </para>
+
+ </sect1>
+
+ <sect1 id="configuration-xmlconfig" revision="2">
+ <title>XML êµ¬ì± íì¼</title>
+
+ <para>
+ 구ì±ì ëí ë¤ë¥¸ ì ê·¼ë²ì <literal>hibernate.cfg.xml</literal>ë¡ ëª
ëª
ë íì¼ ìì ì ì²´ 구ì±ì ì§ì íë ê²ì´ë¤. ì´ íì¼ì
+ <literal>hibernate.properties</literal> íì¼ì ëí ëì©ë¬¼ë¡ì ì¬ì©ë ì ìê±°ë, ë§ì¼ ë ë¤ ì¡´ì¬í ê²½ì°ì íë¡í¼í°ë¤ì
+ ì¤ë³µì ìíëë° ì¬ì©ë ì ìë¤.
+ </para>
+
+ <para>
+ XML êµ¬ì± íì¼ì ëí´í¸ë¡ ë¹ì ì <literal>CLASSPATH</literal>ì 루í¸ì ì¡´ì¬íë ê²ì´ 기ëëë¤. ë¤ìì ìì ì´ë¤:
+ </para>
+
+ <programlisting><![CDATA[<?xml version='1.0' encoding='utf-8'?>
+<!DOCTYPE hibernate-configuration PUBLIC
+ "-//Hibernate/Hibernate Configuration DTD//EN"
+ "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
+
+<hibernate-configuration>
+
+ <!-- a SessionFactory instance listed as /jndi/name -->
+ <session-factory
+ name="java:hibernate/SessionFactory">
+
+ <!-- properties -->
+ <property name="connection.datasource">java:/comp/env/jdbc/MyDB</property>
+ <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
+ <property name="show_sql">false</property>
+ <property name="transaction.factory_class">
+ org.hibernate.transaction.JTATransactionFactory
+ </property>
+ <property name="jta.UserTransaction">java:comp/UserTransaction</property>
+
+ <!-- mapping files -->
+ <mapping resource="org/hibernate/auction/Item.hbm.xml"/>
+ <mapping resource="org/hibernate/auction/Bid.hbm.xml"/>
+
+ <!-- cache settings -->
+ <class-cache class="org.hibernate.auction.Item" usage="read-write"/>
+ <class-cache class="org.hibernate.auction.Bid" usage="read-only"/>
+ <collection-cache collection="org.hibernate.auction.Item.bids" usage="read-write"/>
+
+ </session-factory>
+
+</hibernate-configuration>]]></programlisting>
+
+ <para>
+ ë¹ì ì´ ë³¼ ì ìë¯ì´, ì´ ì ê·¼ë²ì ì¥ì ì 구ì±ì ëí 매í íì¼ ì´ë¦ë¤ì 구체í ìí¤ë ê²ì´ë¤. <literal>hibernate.cfg.xml</literal>ì
+ ëí ë¹ì ì´ Hibernate ìºì를 íëí´ì¼í ë ë³´ë¤ í¸ë¦¬íë¤. <literal>hibernate.properties</literal> ëë
+ <literal>hibernate.cfg.xml</literal> ì¤ ì´ë ê²ì ì¬ì©íëê°ë ë¹ì ì ì íì´ë¤. XML 구문ì ì¬ì©íë ìì ì¸ê¸ë
+ ì´ì ë¤ì ì ì¸íë©´ ëë¤ ê°ì ê²ìì ë
¸í¸íë¼.
+ </para>
+
+ <para>
+ Hibernate 구ì±ì¼ë¡, Hibernate를 ììíë ê²ì ë¤ìê³¼ ê°ì´ ê°ë¨íë¤
+ </para>
+
+ <programlisting><![CDATA[SessionFactory sf = new Configuration().configure().buildSessionFactory();]]></programlisting>
+
+ <para>
+ ë¹ì ì ë¤ìì ì¬ì©íì¬ ë¤ë¥¸ XML êµ¬ì± íì¼ì ì°¾ìë¼ ì ìë¤
+ </para>
+
+ <programlisting><![CDATA[SessionFactory sf = new Configuration()
+ .configure("catdb.cfg.xml")
+ .buildSessionFactory();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="configuration-j2ee" revision="1">
+ <title>J2EE ì´í리ì¼ì´ì
ìë² íµí©</title>
+
+ <para>
+ Hibernateë J2EE ì¸íë¼ì¤í¸ëì²ì ëí ë¤ì íµí© ì ë¤ì ê°ê³ ìë¤:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Container-managed datasources</emphasis>: Hibernateë 컨í
ì´ëì ìí´ ê´ë¦¬ëë JDBC 커ë¥ì
ë¤ì
+ ì¬ì©í ì ìê³ JNDI를 íµí´ ì ê³µëë¤. ëê° JTA í¸í <literal>TransactionManager</literal>ì <literal>ResourceManager</literal>ë
+ í¸ëìì
ê´ë¦¬(CMT), í¹í ëªëª ë°ì´í°ìì¤ë¤ì ê°ë¡ì§ë¬ ë¶ì°ë í¸ëìì
í¸ë¤ë§ì ì²ë¦¬íë¤. ë¬¼ë¡ ë¹ì ì ëí íë¡ê·¸ë¨ ìì¼ë¡
+ í¸ëìì
ê²½ê³ë¤ì íì í ìë ìê±°ë(BMT) ë¹ì ì ë¹ì ì ì½ëê° ì´ìì±ì ì ì§íëë¡ ì´ê²ì ëí ì íì ì¸ Hibernate
+ <literal>Transaction</literal> API를 ì¬ì©íê³ ì ìí ìë ìë¤.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>ìëì ì¸ JNDI ë°ì¸ë©</emphasis>: Hibernateë ìì íì ê·¸ê²ì <literal>SessionFactory</literal>를
+ JNDIì ë°ì¸ë ìí¬ ì ìë¤.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>JTA Session ë°ì¸ë©</emphasis>: Hibernate <literal>Session</literal>ì
+ JTA í¸ëìì
ë¤ì ìì(scope)ì ìëì ì¼ë¡ ë°ì¸ë ìí¬ ìë ìë¤. ê°ë¨íê² JNDIë¡ë¶í° <literal>SessionFactory</literal>를
+ 룩ì
íê³ íì¬ <literal>Session</literal>ì ì»ì´ë¼. Hibernateë¡ íì¬ê¸ ë¹ì ì JTA í¸ëìì
ì´ ìë£ë ë <literal>Session</literal>ì
+ flushìí¤ê³ ë«ë ê²ì ì²ë¦¬íëë¡ íë¼. í¸ëìì
ê²½ê³ êµ¬ë¶ì ì ì¸ì (CMT)ì´ê±°ë íë¡ê·¸ëë°ì ((BMT/UserTransaction))ì´ë¤.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>JMX ë°°ì¹</emphasis>: ë§ì¼ ë¹ì ì´ JMX ê°ì©ì±ì´ ìë ì´í리ì¼ì´ì
ìë²(ì를 ë¤ë©´ JBoss AS)를 ê°ê³ ìë¤ë©´,
+ ë¹ì ì Hibernate를 íëì managed MBeanì¼ë¡ì ë°°ì¹íë ê²ì ì íí ì ìë¤. ì´ê²ì <literal>Configuration</literal>ì¼ë¡ë¶í°
+ ë¹ì ì <literal>SessionFactory</literal>를 ë¹ë ìí¤ë í ì¤ì ìì ì½ë를 ì ì½í´ì¤ë¤. 컨í
ì´ëë ë¹ì ì <literal>HibernateService</literal>를
+ ììí ê²ì´ê³ , ëí ì´ìì ì¼ë¡ ìë¹ì¤ ìì¡´ì±ë¤ì ì²ë¦¬í ê²ì´ë¤(ë°ì´í°ìì¤ë Hibernateê° ììë기 ì ì ì´ì© ê°ë¥í´ì¼ íë¤).
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ë¹ì ì íê²½ì ë°ë¼, ë¹ì ì ë¹ì ì ì´í리ì¼ì´ì
ìë²ê° "connection containment(ì°ê²° ë´ì)" ìì¸ìí©ë¤ì ë³´ì¼ ê²½ì°ì êµ¬ì± ìµì
+ <literal>hibernate.connection.aggressive_release</literal>를 trueë¡ ì¤ì í´ì¼ ë ìë ìë¤.
+ </para>
+
+ <sect2 id="configuration-optional-transactionstrategy" revision="3">
+ <title>í¸ëìì
ë°©ë 구ì±</title>
+
+ <para>
+ Hibernate <literal>Session</literal> APIë ë¹ì ì ìì¹´í
ì² ë´ìì ììì í¸ëìì
ê´í ìì¤í
ì ë
립ì ì´ë¤. ë§ì¼
+ ë¹ì ì´ Hibernateë¡ íì¬ê¸ 커ë¥ì
íì íµí´ ì§ì JDBC를 ì¬ì©íëë¡ ê°ì í ê²½ì°, ë¹ì ì JDBC API를 í¸ì¶íì¬ ë¹ì ì í¸ëìì
ì
+ ììíê³ ëë¼ ì ìë¤. ë§ì¼ ë¹ì ì´ J2EE ì´í리ì¼ì´ì
ìë²ë¥¼ ì¤í ì¤ì´ë¼ë©´, ë¹ì ì íìí ë bean-managed í¸ëìì
ë¤ì ì¬ì©íê³
+ JTA APIì <literal>UserTransaction</literal>ì í¸ì¶íê³ ì ìí ì ìë¤.
+ </para>
+
+ <para>
+ ì´ë¤ ë ê°ì (ê·¸ë¦¬ê³ ë¤ë¥¸) íê²½ë¤ìì ë¹ì ì ì½ëì ì´ìì±ì ì ì§í기 ìí´ ì°ë¦¬ë 기본 ìì¤í
ì í¬ì¥íê³ ìíìí¤ë ì íì ì¸
+ Hibernate <literal>Transaction</literal> API를 ê¶ì¥íë¤. ë¹ì ì Hibernate êµ¬ì± íë¡í¼í° <literal>hibernate.transaction.factory_class</literal>를
+ ì¬ì©íì¬ <literal>Transaction</literal> ì¸ì¤í´ì¤ë¤ì ëí í©í 리 í´ëì¤ë¥¼ ì§ì í´ì¼ íë¤.
+ </para>
+
+ <para>
+ ì¸ ê°ì íì¤(미리 ë§ë¤ì´ì§) ì íë¤ì´ ì¡´ì¬íë¤:
+ </para>
+
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term><literal>org.hibernate.transaction.JDBCTransactionFactory</literal></term>
+ <listitem>
+ <para>ë°ì´í°ë² ì´ì¤ (JDBC) í¸ëìì
ë¤ìê² ìììí¨ë¤(ëí´í¸)</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>org.hibernate.transaction.JTATransactionFactory</literal></term>
+ <listitem>
+ <para>
+ 기존ì í¸ëìì
ì´ ì´ ì»¨í
ì¤í¸(ì를 ë¤ë©´ EJB session bean ë©ìë) ë´ìì ì§í ì¤ì¼ ê²½ì°ì
+ container-managed transactionìê² ìììí¤ê³ , ê·¸ ë°ì ê²½ì° ìë¡ì´ í¸ëìì
ì´ ììëê³
+ bean-managed transactionì´ ì¬ì©ëë¤.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>org.hibernate.transaction.CMTTransactionFactory</literal></term>
+ <listitem>
+ <para>container-managed JTA í¸ëìì
ë¤ìê² ìììí¨ë¤</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>
+ ë¹ì ì ëí ë¹ì ìì ì í¸ëìì
ë°©ëë¤(ì를 ë¤ë©´ CORBA í¸ëìì
ìë¹ì¤)ì ì ìí ìë ìë¤.
+ </para>
+
+ <para>
+ Hibernateì ìë ëªëª í¹ì§ë¤(ì를 ë¤ë©´. second level ìºì, JTA를 ê°ì§ 컨í
ì¤í¸ ìì ì¸ì
ë¤, 기í.)ì ê´ë¦¬ëë íê²½ìì
+ JTA <literal>TransactionManager</literal>ì ëí ì ê·¼ì íìë¡ íë¤. ì´í리ì¼ì´ì
ìë²ìì ë¹ì ì Hibernateê°
+ <literal>TransactionManager</literal>ì ëí 참조를 íëíë ë°©ë²ì ì§ì í´ì¼ íë¤. ìëíë©´ J2EEê° í ê°ì ë©ì»¤ëì¦ì
+ íì¤í ìí¤ê³ ìì§ ì기 ë문ì´ë¤:
+ </para>
+
+ <table frame="topbot" id="jtamanagerlookup" revision="1">
+ <title>JTA TransactionManagers</title>
+ <tgroup cols="2">
+ <colspec colwidth="2.5*"/>
+ <colspec colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>í¸ëìì
í©í 리</entry>
+ <entry align="center">ì´í리ì¼ì´ì
ìë²</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>org.hibernate.transaction.JBossTransactionManagerLookup</literal></entry>
+ <entry align="center">JBoss</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.WeblogicTransactionManagerLookup</literal></entry>
+ <entry align="center">Weblogic</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.WebSphereTransactionManagerLookup</literal></entry>
+ <entry align="center">WebSphere</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.WebSphereExtendedJTATransactionLookup</literal></entry>
+ <entry align="center">WebSphere 6</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.OrionTransactionManagerLookup</literal></entry>
+ <entry align="center">Orion</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.ResinTransactionManagerLookup</literal></entry>
+ <entry align="center">Resin</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.JOTMTransactionManagerLookup</literal></entry>
+ <entry align="center">JOTM</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.JOnASTransactionManagerLookup</literal></entry>
+ <entry align="center">JOnAS</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.JRun4TransactionManagerLookup</literal></entry>
+ <entry align="center">JRun4</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.BESTransactionManagerLookup</literal></entry>
+ <entry align="center">Borland ES</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-jndi" revision="3">
+ <title>JNDI-bound <literal>SessionFactory</literal></title>
+
+ <para>
+ íëì JNDI ë°ì¸ëë Hibernate <literal>SessionFactory</literal>ë ê·¸ í©í 리ì ëí 룩ì
ê³¼ ìë¡ì´
+ <literal>Session</literal>ë¤ì ìì±ì ë¨ìí ìí¬ ì ìë¤. ì´ê²ì JNDI ë°ì¸ëë <literal>Datasource</literal>ì
+ ê´ë ¨ëì´ ìì§ ìê³ , ë¨ìíê² ë ë¤ ëì¼í ë ì§ì¤í¸ë¦¬ë¥¼ ì¬ì©íë¤ë ì ì ë
¸í¸íë¼!
+ </para>
+
+ <para>
+ ë§ì¼ ë¹ì ì´ <literal>SessionFactory</literal>를 íëì JNDI namespaceì ë°ì¸ë ìí¤ê³ ì ìí ê²½ì°,
+ <literal>hibernate.session_factory_name</literal> íë¡í¼í°ë¥¼ ì¬ì©íì¬ í ê°ì ì´ë¦(ì를 ë¤ë©´.
+ <literal>java:hibernate/SessionFactory</literal>)ì ì§ì íë¼. ë§ì¼ ì´ íë¡í¼í°ê° ìëµë ê²½ì°,
+ <literal>SessionFactory</literal>ë JNDIì ë°ì¸ë ëì§ ìì ê²ì´ë¤. (ì´ê²ì ì½ê¸°-ì ì© JNDI ëí´í¸
+ 구íì ê°ì§ íê²½ë¤, ì를 ë¤ë©´ Tomcatìì í¹í ì ì©íë¤.)
+ </para>
+
+ <para>
+ <literal>SessionFactory</literal>를 JNDIì ë°ì¸ë ìí¬ ë, Hibernateë ì´ê¸° 컨í
ì¤í¸ë¥¼ ì´ê¸°í ìí¤ê¸° ìí´
+ <literal>hibernate.jndi.url</literal>, <literal>hibernate.jndi.class</literal>ì ê°ë¤ì ì¬ì©í ê²ì´ë¤.
+ ë§ì¼ ê·¸ê²ë¤ì´ ì§ì ëì´ ìì§ ìì ê²½ì°, ëí´í¸ <literal>InitialContext</literal>ê° ì¬ì©ë ê²ì´ë¤.
+ </para>
+
+ <para>
+ Hibernateë ë¹ì ì´ <literal>cfg.buildSessionFactory()</literal>를 í¸ì¶í íì <literal>SessionFactory</literal>를 JNDI ë´ì
+ ìëì ì¼ë¡ ìì¹ì§ì¸ ê²ì´ë¤. ì´ê²ì ë¹ì ì´ (ëì¤ì ë
¼ìëë) <literal>HibernateService</literal>를 ê°ì§ JMX ë°°ì¹ë¥¼
+ ì¬ì©íì§ ìë í, ë¹ì ì´ ì ì´ë ë¹ì ì ì´í리ì¼ì´ì
ë´ì ìë ì´ë¤ ìì ì½ë (ëë ì í¸ë¦¬í° í´ëì¤) ë´ìì ì´ê²ì í¸ì¶í ê²ìì
+ ì미íë¤.
+ </para>
+
+ <para>
+ ë§ì¼ ë¹ì ì´ íëì JNDI <literal>SessionFactory</literal>를 ì¬ì©í ê²½ì°, íëì EJB ëë ì´ë¤ ë¤ë¥¸ í´ëì¤ë JNDI
+ 룩ì
ì ì¬ì©íì¬ <literal>SessionFactory</literal>를 ì»ì ì ìë¤.
+ </para>
+
+ <para>
+ ì°ë¦¬ë ê´ë¦¬ëë íê²½ë¤ìì <literal>SessionFactory</literal>를 JNDIì ë°ì¸ë ìí¤ê³ ê·¸ ë°ì ê²½ì°ìë
+ íëì <literal>static</literal> ì±ê¸í¤ì ì¬ì©íë ê²ì ê¶ì¥íë¤. ì´ë¤ ìì¸ë¡ë¶í° ë¹ì ì ì´í리ì¼ì´ì
ì½ë를
+ ìíìí¤ê¸° ìí´, ì°ë¦¬ë ëí <literal>HibernateUtil.getSessionFactory()</literal>ê³¼ ê°ì íëì
+ helper í´ëì¤ ë´ìì <literal>SessionFactory</literal>ì ëí ì¤ì 룩ì
ì½ë를 ìíìí¤ê¸°ë¥¼ ê¶ì¥íë¤.
+ ê·¸ë° í´ëì¤ë ëí Hibernate를 ììíë í¸ë¦¬í ë°©ë²ìì ë
¸í¸íë¼— 1ì¥ì ë³´ë¼.
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-j2ee-currentsession" revision="4">
+ <title>Current Session context management with JTA</title>
+
+ <para>
+ ë¹ì ìì ì <literal>ThreadLocal</literal> ì í¸ë¦¬í°ë¥¼ ìëìí¤ë ëì ì, ì°ë¦¬ë ëí Hibernate <literal>Session</literal>를
+ ì»ê¸° ìí´ <literal>SessionFactory</literal> ìì <literal>getCurrentSession()</literal> ë©ìë ì¬ì©ì
+ ê¶ì¥íë¤. <xref linkend="architecture-current-session">íì¬ ì¸ì
ë¤</xref>ì ê´í ë
¼ì를 ë³´ë¼. <literal>"jta"</literal>
+ ì¸ì
컨í
ì¤í¸ë¥¼ ì¬ì©íê³ ìë ê²½ì°ì, íì¬ì JTA í¸ëìì
ì¼ë¡ ììëê³ ì°ê´ë Hibernate <literal>Session</literal>ì´
+ ì¡´ì¬íì§ ìì ê²½ì°, ì°ë¦¬ë JTA í¸ëìì
ì¼ë¡ ììëê³ ì°ê´ë ê²ì´ë¤. <literal>"jta"</literal> 컨í
ì¤í¸ìì
+ <literal>getCurrentSession()</literal>를 íµí´ ê²ìë <literal>Session</literal>ë¤ì ê·¸ í¸ëìì
ì´ ìë£ë기 ì ì
+ ìëì ì¼ë¡ flushë ê²ì´ê³ í¸ëìì
ìë£ íì ë«íì§ ê²ì´ê³ , ê·¸ë¦¬ê³ ê°ê°ì ë¬¸ì¥ ë¤ì JDBC 커ë¥ì
ë¤ì ì ê·¹ì ì¼ë¡ í´ì í ê²ì´ë¤.
+ ì´ê²ì ê·¸ë° ê´ë¦¬ ì¬íë¤ì ëí´ ì¬ì©ì ì½ë를 ëª
ë£íê² ì ì§ìì¼ì, ì°ê´ëì´ ìë JTA í¸ëìì
ì ìëª
주기ì ìí´ <literal>Session</literal>ë¤ì´
+ ê´ë¦¬ëëë¡ íì©í´ì¤ë¤. ì´ê²ì ê·¸ë° ê´ë¦¬ ì¬íë¤ì ëí´ ì¬ì©ì ì½ë를 ëª
ë£íê² ì ì§ìì¼ì, ì¸ì
ì´ ì°ê´ëì´ ìë JTA í¸ëìì
ì
+ ìëª
주기ì ìí´ ê´ë¦¬ëë ê²ì <literal>Session</literal>ë¤ìê² íì©í´ì¤ë¤. ë¹ì ì ì½ëë
+
+ <literal>Session</literal>ë¤ê³¼ í¸ëìì
ë¤ì ì²ë¦¬íë ê°ì¥ ìì¬ì´ ë°©ë²ì Hibernateì ìëì ì¸ "íì¬"
+ <literal>Session</literal>ì´ë¤.
+ <xref linkend="architecture-current-session">current sessions</xref>ì ê´í ë
¼ì를 ë³´ë¼.
+ <literal>"jta"</literal> ì¸ì
컨í
ì¤í¸ë¥¼ ì¬ì©íë ê²½ì°, íì¬ì JTA í¸ëìì
ì¼ë¡ ììëê³ ì°ê´ë
+ Hibernate <literal>Session</literal>ë¤ì´ ì¡´ì¬íì§ ìì ê²½ì°, ë¹ì ì´
+ <literal>sessionFactory.getCurrentSession()</literal>ì ì²ì í¸ì¶í ë íëì ì¸ì
ì´ íì¬ì JTA í¸ëìì
ì ëí´
+ ììëê³ ì°ê´ë ê²ì´ë¤. <literal>"jta"</literal> 컨í
ì¤í¸ìì <literal>getCurrentSession()</literal>ì
+ íµí´ ê²ìë <literal>Session</literal>ë¤ì ê·¸ í¸ëìì
ì´ ìë£ë기 ì ì ìëì ì¼ë¡ flushë ê²ì´ê³
+ ê·¸ í¸ëìì
ìë£ë¤ íì ë«íì§ ê²ì´ê³ ê°ê°ì ë¬¸ì¥ íì JDBC 커ë¥ì
ë¤ì ì ê·¹ì ì¼ë¡ í´ì í ê²ì´ë¤.
+ ì´ê²ì ê·¸ë° ê´ë¦¬ ì¬íë¤ì ëí´ ì¬ì©ì ì½ë를 ëª
ë£íê² ì ì§ìì¼ì, ì°ê´ëì´ ìë JTA í¸ëìì
ì ìëª
주기ì ìí´ <literal>Session</literal>ë¤ì´
+ ê´ë¦¬ëëë¡ íì©í´ì¤ë¤. ì´ê²ì ê·¸ë° ê´ë¦¬ ì¬íë¤ì ëí´ ì¬ì©ì ì½ë를 ëª
ë£íê² ì ì§ìì¼ì, ì¸ì
ì´ ì°ê´ëì´ ìë JTA í¸ëìì
ì
+ ìëª
주기ì ìí´ ê´ë¦¬ëë ê²ì <literal>Session</literal>ë¤ìê² íì©í´ì¤ë¤. ë¹ì ì ì½ëë í¸ëìì
ê²½ê³ë¤ì ì¤ì íëë°
+ <literal>UserTransaction</literal>ì íµí´ íë¡ê·¸ë¨ ìì¼ë¡ JTA를 ì¬ì©íê±°ë, Hibernate <literal>Transaction</literal> API를 (ì´ìì±ì ìí ì½ëë¡ ê¶ì¥ë¨)
+ ì¬ì©í ì ìë¤. ë§ì¼ ë¹ì ì´ íëì EJB 컨í
ì´ëìì ì¤ííê³ ìì ê²½ì°, CMTì ê²½ì°ì ì ì¸ì ì¸ í¸ëìì
ê²½ê³ì¤ì ì´ ì í¸ëë¤.
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-j2ee-jmx" revision="1">
+ <title>JMX ë°°ì¹</title>
+
+ <para>
+ <literal>cfg.buildSessionFactory()</literal> ì¤ì ì¬ì í JNDIì ë¶ì íëì <literal>SessionFactory</literal>를 ì»ê¸° ìí´
+ ì´ëê°ìì ì¤íëì´ì¼ íë¤. ë¹ì ì (<literal>HibernateUtil</literal> ë´ì ìë ê²ì²ë¼) <literal>static</literal> initializer
+ ë¸ë¡ ììì ì´ê²ì íí ì ìê±°ë ë¹ì ì Hibernate를 <emphasis>managed service</emphasis>ë¡ì ë°°ì¹í ì ìë¤.
+ </para>
+
+ <para>
+ Hibernateë JBoss ASì ê°ì JMX ê°ì©ì±ë¤ì ê°ì§ ì´í리ì¼ì´ì
ìë² ìì ë°°ì¹ë¥¼ ìí´ <literal>org.hibernate.jmx.HibernateService</literal>를
+ ë°°í¬íê³ ìë¤. ì¤ì ë°°ì¹ì 구ì±ì 벤ë ì§ì ì ì´ë¤. ë¤ìì JBoss 4.0.x를 ìí <literal>jboss-service.xml</literal> ìì ì´ë¤:
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<server>
+
+<mbean code="org.hibernate.jmx.HibernateService"
+ name="jboss.jca:service=HibernateFactory,name=HibernateFactory">
+
+ <!-- Required services -->
+ <depends>jboss.jca:service=RARDeployer</depends>
+ <depends>jboss.jca:service=LocalTxCM,name=HsqlDS</depends>
+
+ <!-- Bind the Hibernate service to JNDI -->
+ <attribute name="JndiName">java:/hibernate/SessionFactory</attribute>
+
+ <!-- Datasource settings -->
+ <attribute name="Datasource">java:HsqlDS</attribute>
+ <attribute name="Dialect">org.hibernate.dialect.HSQLDialect</attribute>
+
+ <!-- Transaction integration -->
+ <attribute name="TransactionStrategy">
+ org.hibernate.transaction.JTATransactionFactory</attribute>
+ <attribute name="TransactionManagerLookupStrategy">
+ org.hibernate.transaction.JBossTransactionManagerLookup</attribute>
+ <attribute name="FlushBeforeCompletionEnabled">true</attribute>
+ <attribute name="AutoCloseSessionEnabled">true</attribute>
+
+ <!-- Fetching options -->
+ <attribute name="MaximumFetchDepth">5</attribute>
+
+ <!-- Second-level caching -->
+ <attribute name="SecondLevelCacheEnabled">true</attribute>
+ <attribute name="CacheProviderClass">org.hibernate.cache.EhCacheProvider</attribute>
+ <attribute name="QueryCacheEnabled">true</attribute>
+
+ <!-- Logging -->
+ <attribute name="ShowSqlEnabled">true</attribute>
+
+ <!-- Mapping files -->
+ <attribute name="MapResources">auction/Item.hbm.xml,auction/Category.hbm.xml</attribute>
+
+</mbean>
+
+</server>]]></programlisting>
+
+ <para>
+ ì´ íì¼ì <literal>META-INF</literal>ë¡ ëª
ëª
ë ëë í 리 ìì ë°°ì¹ëê³ íì¥ì <literal>.sar</literal>
+ (service archive)를 ê°ì§ í ê°ì JAR íì¼ ìì í¨í¤ì§ëë¤. ë¹ì ì ëí Hibernate, ê·¸ê²ì íìí ì 3ì ë¼ì´ë¸ë¬ë¦¬ë¤, ë¹ì ì ì»´íì¼ë
+ ìì í´ëì¤ë¤ ë¿ë§ ìëë¼ ë¹ì ì 매í íì¼ë¤ì ëì¼í ìì¹´ì´ë¸ ìì í¨í¤ì§í íìê° ìë¤. ë¹ì ì ìí°íë¼ì´ì¦ ë¹ì¦(ëê° session beans)ë
+ ê·¸ê²ë¤ ìì ì JAR íì¼ ìì ì ì§ë ì ìì§ë§, ë¹ì ì í ê°ì (hot-)ë°°ì¹ ê°ë¥í ë¨ì를 ì»ê¸° ìí´ ë©ì¸ ìë¹ì¤ ìì¹´ì´ë¸ ìì ì´ EJB JAR íì¼ì
+ í¬í¨ìí¬ ìë ìë¤. JMX ìë¹ì¤ì EJB ë°°ì¹ì ê´í ì¶ê° ì ë³´ë JBoss AS 문ì를 참조íë¼.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/events.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/events.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/events.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,255 @@
+<chapter id="events">
+ <title>ì¸í°ì
í°ë¤ê³¼ ì´ë²¤í¸ë¤</title>
+
+ <para>
+ ì´í리ì¼ì´ì
ì´ Hibernate ë´ë¶ìì ë°ìíë ì´ë¤ ì´ë²¤í¸ë¤ì ëí´ ë°ìíë ê²ì íí ì ì©íë¤. ì´ê²ì ì´ë¤ ì¢
ë¥ì ì¼ë°ì ì¸ ê¸°ë¥,
+ ê·¸ë¦¬ê³ Hibernateì íì¥ ê¸°ë¥ì 구íì íì©í´ì¤ë¤.
+ </para>
+
+ <sect1 id="objectstate-interceptors" revision="3">
+ <title>ì¸í°ì
í°ë¤</title>
+
+ <para>
+ <literal>Interceptor</literal> ì¸í°íì´ì¤ë ìì ê°ì²´ê° ì ì¥ëê³ , ì
ë°ì´í¸ëê³ , ìì ëê±°ë ë¡ëë기 ì ì ìì ê°ì²´ì
+ íë¡í¼í°ë¤ì ì¡°ì¬íê³ /íê±°ë ì²ë¦¬íë ê²ì ì´í리ì¼ì´ì
ì íì©í´ì¤ì¼ë¡ì¨ ì¸ì
ì¼ë¡ë¶í° ì´í리ì¼ì´ì
ì¼ë¡ì ì½ë°±ë¤ì ì ê³µíë¤.
+ ì´ê²ì ëí í ê°ì§ ê°ë¥í ì¬ì©ì ê°ì¬ ì 보를 ì¶ì íë ê²ì´ë¤. ì를 ë¤ì´, ë¤ì <literal>Interceptor</literal>ë
+ <literal>Auditable</literal>ì´ ìì±ë ë <literal>createTimestamp</literal>를 ìëì ì¼ë¡ ì¤ì íê³
+ <literal>Auditable</literal>ì´ ì
ë°ì´í¸ë ë <literal>lastUpdateTimestamp</literal> íë¡í¼í°ë¥¼ ì
ë°ì´í¸ íë¤.
+ </para>
+
+ <para>
+ ë¹ì ì <literal>Interceptor</literal>를 ì§ì 구íí´ì¼ íê±°ë (ë ì¢ê²ë)
+ <literal>EmptyInterceptor</literal>를 íì¥(extend)í´ì¼ íë¤.
+ </para>
+
+ <programlisting><![CDATA[package org.hibernate.test;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.Iterator;
+
+import org.hibernate.EmptyInterceptor;
+import org.hibernate.Transaction;
+import org.hibernate.type.Type;
+
+public class AuditInterceptor extends EmptyInterceptor {
+
+ private int updates;
+ private int creates;
+ private int loads;
+
+ public void onDelete(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+ // do nothing
+ }
+
+ public boolean onFlushDirty(Object entity,
+ Serializable id,
+ Object[] currentState,
+ Object[] previousState,
+ String[] propertyNames,
+ Type[] types) {
+
+ if ( entity instanceof Auditable ) {
+ updates++;
+ for ( int i=0; i < propertyNames.length; i++ ) {
+ if ( "lastUpdateTimestamp".equals( propertyNames[i] ) ) {
+ currentState[i] = new Date();
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public boolean onLoad(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+ if ( entity instanceof Auditable ) {
+ loads++;
+ }
+ return false;
+ }
+
+ public boolean onSave(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+
+ if ( entity instanceof Auditable ) {
+ creates++;
+ for ( int i=0; i<propertyNames.length; i++ ) {
+ if ( "createTimestamp".equals( propertyNames[i] ) ) {
+ state[i] = new Date();
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public void afterTransactionCompletion(Transaction tx) {
+ if ( tx.wasCommitted() ) {
+ System.out.println("Creations: " + creates + ", Updates: " + updates, "Loads: " + loads);
+ }
+ updates=0;
+ creates=0;
+ loads=0;
+ }
+
+}]]></programlisting>
+
+ <para>
+ ì¸í°ì
í°ë¤ì ë¤ì ë ê°ì í¹ì§ë¤ë¡ ëíëë¤: <literal>Session</literal>-ììí 그리ê³
+ <literal>SessionFactory</literal>-ììí.
+ </para>
+
+ <para>
+ <literal>Session</literal>-ììì ì¸í°ì
í°ë ì¸ì
ì´ íëì <literal>Interceptor</literal>를 ìì©íë
+ ì¤ë²ë¡ëë SessionFactory.openSession() ë©ìëë¤ ì¤ íë를 ì¬ì©íì¬ ì´ë¦´ ë
+ ì§ì ëë¤.
+ </para>
+
+ <programlisting><![CDATA[Session session = sf.openSession( new AuditInterceptor() );]]></programlisting>
+
+ <para>
+ ë¹ì ì ëí <literal>Configuration</literal>ì ì¬ì©íì¬ ì¸í°ì
í°ë¥¼ ì ì ë 벨 ìì ì¤ì í ìë ìë¤.
+ ì´ ê²½ì°ì, ì¸í°ì
í°ë threadsafeì´ì´ì¼ íë¤.
+ </para>
+
+ <programlisting><![CDATA[Session session = sf.openSession( new AuditInterceptor() );]]></programlisting>
+
+ <para>
+ <literal>SessionFactory</literal>-ììì ì¸í°ì
í°ë <literal>SessionFactory</literal>ì ë¹ëí기ì ìì
+ <literal>Configuration</literal> ê°ì²´ì ë±ë¡ëë¤. ì´ ê²½ì°ì, ê³µê¸ëë ì¸í°ì
í°ë ê·¸ <literal>SessionFactory</literal>ë¡ë¶í°
+ ì´ë ¤ì§ 모ë ì¸ì
ë¤ì ì ì©ë ê²ì´ë¤; íëì ì¸ì
ì´ ì¬ì©í ì¸í°ì
í°ë¥¼ ëª
ìì ì¼ë¡ ì§ì íì¬ ì´ë¦¬ì§ ìë í ì´ê²ì ì°¸ì´ë¤.
+ <literal>SessionFactory</literal>-ììì ì¸í°ì
í°ë¤ì ì¸ì
-ì§ì ì ì¸ ìí를 ì ì¥íì§ ìëë¡ ì£¼ìíì¬ ì°ë ë-ìì í´ì¼ íë¤.
+ ìëíë©´ ë¤ì¤ ì¸ì
ë¤ì (ì ì ì ì¼ë¡) ì´ ì¸í°ì
í°ë¥¼ ëìì ì¼ë¡ ì¬ì©í ê²ì´ê¸° ë문ì´ë¤.
+ </para>
+
+ <programlisting><![CDATA[new Configuration().setInterceptor( new AuditInterceptor() );]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="objectstate-events" revision="4">
+ <title>ì´ë²¤í¸ ìì¤í
</title>
+
+ <para>
+ ë§ì¼ ë¹ì ì´ ë¹ì ì ìì ê³ì¸µìì í¹ì ì´ë²¤í¸ë¤ì ëí´ ë°ìí´ì¼ íë¤ë©´, ë¹ì ì ëí Hibernate3 <emphasis>event</emphasis>
+ ìí¤í
ì²ë¥¼ ì¬ì©í ìë ìë¤. ì´ë²¤í¸ ìì¤í
ì ë¶ê°ë¬¼ë¡ ì¬ì©ë ì ìê±°ë ì¸í°ì
í°ë¤ì ëí ëì²´ë¬¼ë¡ ì¬ì©ë ì ìë¤.
+ </para>
+
+ <para>
+ 본ì§ì ì¼ë¡ <literal>Session</literal> ì¸í°íì´ì¤ì 모ë ë©ìëë¤ì ì´ë²¤í¸ì ìë¡ ê´ë ¨ëì´ ìë¤.
+ ë¹ì ì <literal>LoadEvent</literal>,
+ <literal>FlushEvent</literal>, ë±ì ê°ëë¤ (ì ìë ì´ë²¤í¸ íì
ë¤ì ì ì²´ 리ì¤í¸ì ëí´ìë XML êµ¬ì± íì¼ DTD ëë
+ <literal>org.hibernate.event</literal> í¨í¤ì§ë¥¼ 참조íë¼). íëì ìì²ì´ ì´ë¤ ë©ìëë¤ ì¤ íëì ìí´ ë§ë¤ì´ì§ ë,
+ Hibernate <literal>Session</literal>ì ì ì í ì´ë²¤í¸ë¥¼ ìì±ìí¤ê³ ê·¸ê²ì ê·¸ íì
ì 구ì±ë ì´ë²¤í¸ 리ì¤ëìê² ì ë¬íë¤.
+ ë°ì±ìì´, ì´ë¤ 리ì¤ëë¤ì ê·¸ë¤ ë©ìëë¤ì´ íì ê·ê²°ëìë ëì¼í íë¡ì¸ì±ì 구ííë¤. íì§ë§ ë¹ì ì´ ë¦¬ì¤ë ì¸í°íì´ì¤ë¤ ì¤ íëì ë§ì¶¤ì
+ 구ííë ê²ì´ ìì ë¡ê³ (ì를 ë¤ì´ <literal>LoadEvent</literal>ë <literal>LoadEventListener</literal> ì¸í°íì´ì¤ì
+ ë±ë¡ë 구íì ìí´ ì²ë¦¬ëë¤), ê·¸ ê²½ì°ì ê·¸ë¤ êµ¬íì <literal>Session</literal>ì ëí´ íí´ì§ ììì <literal>load()</literal>
+ ìì²ë¤ì ì²ë¦¬í ì±
ìì´ ìì ê²ì´ë¤.
+ </para>
+
+ <para>
+ 리ì¤ëë¤ì í¨ì¨ì ì´ê²ë ì±ê¸í¤(singleton)ë¤ë¡ ê°ì£¼ëì´ì¼ í ê²ì´ë¤; ì´ê²ì ê·¸ê²ë¤ì´ ìì²ë¤ ì¬ì´ìì ê³µì ëê³ , ë°ë¼ì ììì ìí를
+ ì¸ì¤í´ì¤ ë³ìë¤ë¡ì ì ì¥íì§ ë§ìì¼ í¨ì ì미íë¤.
+ </para>
+
+ <para>
+ ë§ì¶¤í 리ì¤ëë ê·¸ê²ì´ í¸ìì ì¸ ê¸°ì í´ëì¤ë¤(ëë 리ì¤ëë¤ì´ ì´ ì©ëë¡ finalì´ ìë ê²ì¼ë¡ ì ì¸ëë¯ë¡ Hibernate
+ out-of-the-boxì ìí´ ì¬ì©ë ëí´í¸ ì´ë²¤í¸ 리ì¤ëë¤) ì¤ íë를 ì²ë¦¬íê³ /íê±°ë íì¥íê³ ì ìíë ì´ë²¤í¸ë¤ì ëí´
+ ì ì í ì¸í°íì´ì¤ë¥¼ 구íí´ì¼ íë¤. ë§ì¶¤í 리ì¤ëë¤ì <literal>Configuration</literal> ê°ì²´ë¥¼ íµí´ íë¡ê·¸ë¨ ìì¼ë¡
+ ë±ë¡ë ì ìê±°ë, Hibernate êµ¬ì± XML ìì ì§ì ë ì ìë¤ (properties íì¼ì íµí ì ì¸ì ì¸ êµ¬ì±ì ì§ìëì§ ìëë¤).
+ ë¤ìì ë§ì¶¤í load ì´ë²¤í¸ 리ì¤ëì ëí ìì ì´ë¤:
+ </para>
+
+ <programlisting><![CDATA[public class MyLoadListener implements LoadEventListener {
+ // this is the single method defined by the LoadEventListener interface
+ public void onLoad(LoadEvent event, LoadEventListener.LoadType loadType)
+ throws HibernateException {
+ if ( !MySecurity.isAuthorized( event.getEntityClassName(), event.getEntityId() ) ) {
+ throw MySecurityException("Unauthorized access");
+ }
+ }
+}]]></programlisting>
+
+ <para>
+ ë¹ì ì ëí ëí´í¸ 리ì¤ëì ë§ë¶ì¬ ê·¸ 리ì¤ë를 ì¬ì©íëë¡ Hibernateìê² ìë ¤ì£¼ë êµ¬ì± ìí¸ë¦¬ë¥¼ íìë¡ íë¤:
+ </para>
+
+<programlisting><![CDATA[<hibernate-configuration>
+ <session-factory>
+ ...
+ <event type="load">
+ <listener class="com.eg.MyLoadListener"/>
+ <listener class="org.hibernate.event.def.DefaultLoadEventListener"/>
+ </event>
+ </session-factory>
+</hibernate-configuration>]]></programlisting>
+
+ <para>
+ ëì ì ë¹ì ì ê·¸ê²ì íë¡ê·¸ëë° ë°©ìì¼ë¡ ë±ë¡í ìë ìë¤:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = new Configuration();
+LoadEventListener[] stack = { new MyLoadListener(), new DefaultLoadEventListener() };
+cfg.EventListeners().setLoadEventListeners(stack);]]></programlisting>
+
+ <para>
+ ì ì¸ì ì¼ë¡ ë±ë¡ë 리ì¤ëë¤ì ì¸ì¤í´ì¤ë¤ì ê³µì í ì ìë¤. ë§ì¼ ëì¼í í´ëì¤ ì´ë¦ì´ ì¬ë¬ ê°ì <literal><listener/></literal>
+ ììë¤ìì ì¬ì©ë ê²½ì°, ê°ê°ì 참조ë ê·¸ í´ëì¤ì ëí ë³ëì ì¸ì¤í´ì¤ë¡ ê·ê²°ë ê²ì´ë¤. ë§ì¼ ë¹ì ì´ ë¦¬ì¤ë íì
ë¤ ì¬ì´ìì 리ì¤ë ì¸ì¤í´ì¤ë¤ì
+ ê³µì í ê°ì©ì±ì íìë¡ í ê²½ì° ë¹ì ì íë¡ê·¸ëë° ë°©ìì ë±ë¡ ì ê·¼ë²ì ì¬ì©í´ì¼ íë¤.
+ </para>
+
+ <para>
+ êµ¬ì± ëìì ì ì¸í°íì´ì¤ë¥¼ 구ííê³ í¹ì íì
ì ì§ì íëê°? ë¬¼ë¡ ë¦¬ì¤ë 구íì ì¬ë¬ ê°ì ì´ë²¤í¸ 리ì¤ë ì¸í°íì´ì¤ë¤ì
+ 구íí ì ìë¤. ë±ë¡ ëìì ì¶ê°ì ì¼ë¡ íì
ì ì ìíë ê²ì 컨í¼ê·¸ë ì´ì
ëìì ë§ì¶¤í 리ì¤ëë¤ì ì¬ì© ì¬ë¶ë¥¼ ì íìí¤ë ê²ì
+ ë ì½ê² í´ì¤ë¤.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-decl-security" revision="2">
+ <title>Hibernate ì ì¸ì ì¸ ë³´ì</title>
+ <para>
+ ëê° Hibernate ì´í리ì¼ì´ì
ë¤ìì ì ì¸ì ì¸ ë³´ìì session facade ê³ì¸µ ë´ìì ê´ë¦¬ëë¤. ì´ì , Hibernate3ë ì´ë¤ ì¡ì
ë¤ì´
+ JACC를 íµí´ í¼ë¯¸ì
ì 주ì´ì§ê³ , JAAS를 íµí´ ì¸ê°ëë ê²ì íì©í´ì¤ë¤. ì´ê²ì 모ë ìí¤í
ì²ì ìë¨ì ë¹ëë ìµì
기ë¥ì´ë¤.
+ </para>
+
+ <para>
+ 먼ì , ë¹ì ì JAAS authorization ì¬ì©ì ì´ì© ê°ë¥íëë¡ í기 ìí´ ì ì í ì´ë²¤í¸ 리ì¤í°ë¤ì 구ì±í´ì¼ íë¤.
+ </para>
+
+ <programlisting><![CDATA[<listener type="pre-delete" class="org.hibernate.secure.JACCPreDeleteEventListener"/>
+<listener type="pre-update" class="org.hibernate.secure.JACCPreUpdateEventListener"/>
+<listener type="pre-insert" class="org.hibernate.secure.JACCPreInsertEventListener"/>
+<listener type="pre-load" class="org.hibernate.secure.JACCPreLoadEventListener"/>]]></programlisting>
+
+ <para>
+ <literal><listener type="..." class="..."/></literal>ë í¹ì ì´ë²¤í¸ íì
ì ëí´ ì íí í ê°ì
+ 리ì¤ëê° ì¡´ì¬í ë ë¨ì§ <literal><event type="..."><listener class="..."/></event></literal>ì
+ ë¨ì¶íìì ë
¸í¸íë¼.
+ </para>
+
+ <para>
+ ë¤ìì¼ë¡, ì¬ì í <literal>hibernate.cfg.xml</literal> ë´ìì í¼ë¯¸ì
ë¤ì roleë¤ì ë°ì¸ë ìí¨ë¤ :
+ </para>
+
+ <programlisting><![CDATA[<grant role="admin" entity-name="User" actions="insert,update,read"/>
+<grant role="su" entity-name="User" actions="*"/>]]></programlisting>
+
+ <para>
+ ìí (role) ì´ë¦ë¤ì ë¹ì ì JACC íë¡ë°ì´ëì ìí´ ì¸ì§ë ìí (role)ë¤ì´ë¤.
+ </para>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/example_mappings.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/example_mappings.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/example_mappings.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,652 @@
+<chapter id="example-mappings">
+ <title>ìì : ì¬ë¬ ê°ì§ 매íë¤</title>
+
+ <para>
+ ì´ ì¥ì ëªëª ë³´ë¤ ë³µì¡í ì°ê´ 매íë¤ì ë³´ì¬ì¤ë¤.
+ </para>
+
+ <sect1 id="example-mappings-emp">
+ <title>Employer/Employee</title>
+
+ <para>
+ <literal>Employer</literal>ì <literal>Employee</literal> ì¬ì´ì ê´ê³ì ëí ë¤ì 모íì ê·¸ ì°ê´ë¥¼ íííë ë°
+ ì¤ì ìí°í° í´ëì¤(<literal>Employment</literal>)를 ì¬ì©íë¤. ëì¼í ë ë¶ë¶ë¤ì ëí´ íë ì´ìì ì±ì© ì£¼ê¸°ê° ì¡´ì¬í ì ì기
+ ë문ì ì´ê²ì´ íí´ì§ë¤. ì»´í¬ëí¸ë¤ì´ íí ê°ë¤ê³¼ ì¢
ì
ì ì´ë¦ë¤ì 모íí ìí¤ëë° ì¬ì©ëë¤.
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/EmployerEmployee.gif" format="GIF" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/EmployerEmployee.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ ë¤ìì ê°ë¥í 매í 문ìì´ë¤:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Employer" table="employers">
+ <id name="id">
+ <generator class="sequence">
+ <param name="sequence">employer_id_seq</param>
+ </generator>
+ </id>
+ <property name="name"/>
+ </class>
+
+ <class name="Employment" table="employment_periods">
+
+ <id name="id">
+ <generator class="sequence">
+ <param name="sequence">employment_id_seq</param>
+ </generator>
+ </id>
+ <property name="startDate" column="start_date"/>
+ <property name="endDate" column="end_date"/>
+
+ <component name="hourlyRate" class="MonetaryAmount">
+ <property name="amount">
+ <column name="hourly_rate" sql-type="NUMERIC(12, 2)"/>
+ </property>
+ <property name="currency" length="12"/>
+ </component>
+
+ <many-to-one name="employer" column="employer_id" not-null="true"/>
+ <many-to-one name="employee" column="employee_id" not-null="true"/>
+
+ </class>
+
+ <class name="Employee" table="employees">
+ <id name="id">
+ <generator class="sequence">
+ <param name="sequence">employee_id_seq</param>
+ </generator>
+ </id>
+ <property name="taxfileNumber"/>
+ <component name="name" class="Name">
+ <property name="firstName"/>
+ <property name="initial"/>
+ <property name="lastName"/>
+ </component>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ ê·¸ë¦¬ê³ ë¤ìì <literal>SchemaExport</literal>ì ìí´ ìì±ë í
ì´ë¸ ì¤í¤ë§ì´ë¤.
+ </para>
+
+ <programlisting><![CDATA[create table employers (
+ id BIGINT not null,
+ name VARCHAR(255),
+ primary key (id)
+)
+
+create table employment_periods (
+ id BIGINT not null,
+ hourly_rate NUMERIC(12, 2),
+ currency VARCHAR(12),
+ employee_id BIGINT not null,
+ employer_id BIGINT not null,
+ end_date TIMESTAMP,
+ start_date TIMESTAMP,
+ primary key (id)
+)
+
+create table employees (
+ id BIGINT not null,
+ firstName VARCHAR(255),
+ initial CHAR(1),
+ lastName VARCHAR(255),
+ taxfileNumber VARCHAR(255),
+ primary key (id)
+)
+
+alter table employment_periods
+ add constraint employment_periodsFK0 foreign key (employer_id) references employers
+alter table employment_periods
+ add constraint employment_periodsFK1 foreign key (employee_id) references employees
+create sequence employee_id_seq
+create sequence employment_id_seq
+create sequence employer_id_seq]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-mappings-authorwork">
+ <title>Author/Work</title>
+
+ <para>
+ <literal>Work</literal>, <literal>Author</literal> ê·¸ë¦¬ê³ <literal>Person</literal> ì¬ì´ì ê´ê³ë¤ì ëí
+ ë¤ì 모íì ê²í íì. ì°ë¦¬ë <literal>Work</literal>ê³¼ <literal>Author</literal> ì¬ì´ì ê´ê³ë¥¼ many-to-many
+ ì°ê´ì¼ë¡ íííë¤. ì°ë¦¬ë <literal>Author</literal>ì <literal>Person</literal> ì¬ì´ì ê´ê³ë¥¼ one-to-one
+ ì°ê´ì¼ë¡ íííê³ ì ì ííë¤. ë ë¤ë¥¸ ê°ë¥ì±ì <literal>Author</literal>ê° <literal>Person</literal>ì íì¥íëë¡
+ íë ê²ì¼ ê²ì´ë¤.
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/AuthorWork.gif" format="GIF" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/AuthorWork.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ ë¤ì 매í 문ìë ì´ë¤ ê´ê³ë¤ì ì ííê² íííë¤:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Work" table="works" discriminator-value="W">
+
+ <id name="id" column="id">
+ <generator class="native"/>
+ </id>
+ <discriminator column="type" type="character"/>
+
+ <property name="title"/>
+ <set name="authors" table="author_work">
+ <key column name="work_id"/>
+ <many-to-many class="Author" column name="author_id"/>
+ </set>
+
+ <subclass name="Book" discriminator-value="B">
+ <property name="text"/>
+ </subclass>
+
+ <subclass name="Song" discriminator-value="S">
+ <property name="tempo"/>
+ <property name="genre"/>
+ </subclass>
+
+ </class>
+
+ <class name="Author" table="authors">
+
+ <id name="id" column="id">
+ <!-- The Author must have the same identifier as the Person -->
+ <generator class="assigned"/>
+ </id>
+
+ <property name="alias"/>
+ <one-to-one name="person" constrained="true"/>
+
+ <set name="works" table="author_work" inverse="true">
+ <key column="author_id"/>
+ <many-to-many class="Work" column="work_id"/>
+ </set>
+
+ </class>
+
+ <class name="Person" table="persons">
+ <id name="id" column="id">
+ <generator class="native"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ ì´ ë§¤íìë ë¤ ê°ì í
ì´ë¸ë¤ì´ ì¡´ì¬íë¤. <literal>works</literal>, <literal>authors</literal>ì
+ <literal>persons</literal>ì ê°ê° ìì
ë°ì´í°, ì ì ë°ì´í°, ê°ì¸ ë°ì´í°ë¥¼ ë³´ê´íë¤. <literal>author_work</literal>ë
+ ì ìë¤ì ìì
ë¤ì ì°ê²°ìí¤ë ì°ê´ í
ì´ë¸ì´ë¤. ë¤ìì <literal>SchemaExport</literal>ì ìí´ ìì±ë í
ì´ë¸
+ ì¤í¤ë§ì´ë¤.
+ </para>
+
+ <programlisting><![CDATA[create table works (
+ id BIGINT not null generated by default as identity,
+ tempo FLOAT,
+ genre VARCHAR(255),
+ text INTEGER,
+ title VARCHAR(255),
+ type CHAR(1) not null,
+ primary key (id)
+)
+
+create table author_work (
+ author_id BIGINT not null,
+ work_id BIGINT not null,
+ primary key (work_id, author_id)
+)
+
+create table authors (
+ id BIGINT not null generated by default as identity,
+ alias VARCHAR(255),
+ primary key (id)
+)
+
+create table persons (
+ id BIGINT not null generated by default as identity,
+ name VARCHAR(255),
+ primary key (id)
+)
+
+alter table authors
+ add constraint authorsFK0 foreign key (id) references persons
+alter table author_work
+ add constraint author_workFK0 foreign key (author_id) references authors
+alter table author_work
+ add constraint author_workFK1 foreign key (work_id) references works]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-mappings-customerorderproduct">
+ <title>Customer/Order/Product</title>
+
+ <para>
+ ì´ì <literal>Customer</literal>, <literal>Order</literal>ì <literal>LineItem</literal>
+ ê·¸ë¦¬ê³ <literal>Product</literal> ì¬ì´ì ê´ê³ë¤ì ê´í 모íì ê²í íì. <literal>Customer</literal>ì
+ <literal>Order</literal> ì¬ì´ì one-to-many ì°ê´ì´ ì¡´ì¬íì§ë§, ì°ë¦¬ë ì´ë»ê²
+ <literal>Order</literal> / <literal>LineItem</literal> / <literal>Product</literal>를
+ ííí ê²ì¸ê°? ëë <literal>Order</literal>ì <literal>Product</literal> ì¬ì´ì many-to-many
+ ì°ê´ë¥¼ ëíë´ë íëì ì°ê´ í´ëì¤ë¡ì <literal>LineItem</literal>ì 매ííê¸°ë¡ ì ííë¤. Hibernateìì
+ ì´ê²ì composite ììë¡ ëª
ëª
ëë¤.
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/CustomerOrderProduct.gif" format="GIF" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/CustomerOrderProduct.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ 매í 문ì:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Customer" table="customers">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="name"/>
+ <set name="orders" inverse="true">
+ <key column="customer_id"/>
+ <one-to-many class="Order"/>
+ </set>
+ </class>
+
+ <class name="Order" table="orders">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="date"/>
+ <many-to-one name="customer" column="customer_id"/>
+ <list name="lineItems" table="line_items">
+ <key column="order_id"/>
+ <list-index column="line_number"/>
+ <composite-element class="LineItem">
+ <property name="quantity"/>
+ <many-to-one name="product" column="product_id"/>
+ </composite-element>
+ </list>
+ </class>
+
+ <class name="Product" table="products">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="serialNumber"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ <literal>customers</literal>, <literal>orders</literal>, <literal>line_items</literal> 그리ê³
+ <literal>products</literal>ë ê°ê° ê³ ê° ë°ì´í°, 주문 ë°ì´í°, 주문 ë¼ì¸ ìì´í
ë°ì´í°, ê·¸ë¦¬ê³ ì í ë°ì´í°ë¥¼
+ ë³´ê´íë¤. <literal>line_items</literal>ë ëí 주문ë¤ì ì íë¤ê³¼ ì°ê²°ìí¤ë ì°ê´ í
ì´ë¸ë¡ì ëìíë¤.
+ </para>
+
+ <programlisting><![CDATA[create table customers (
+ id BIGINT not null generated by default as identity,
+ name VARCHAR(255),
+ primary key (id)
+)
+
+create table orders (
+ id BIGINT not null generated by default as identity,
+ customer_id BIGINT,
+ date TIMESTAMP,
+ primary key (id)
+)
+
+create table line_items (
+ line_number INTEGER not null,
+ order_id BIGINT not null,
+ product_id BIGINT,
+ quantity INTEGER,
+ primary key (order_id, line_number)
+)
+
+create table products (
+ id BIGINT not null generated by default as identity,
+ serialNumber VARCHAR(255),
+ primary key (id)
+)
+
+alter table orders
+ add constraint ordersFK0 foreign key (customer_id) references customers
+alter table line_items
+ add constraint line_itemsFK0 foreign key (product_id) references products
+alter table line_items
+ add constraint line_itemsFK1 foreign key (order_id) references orders]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="misc">
+ <title>기í ìì 매íë¤</title>
+
+ <para>
+ ì´ë¤ ìì ë¤ì 모ë Hiberante test suiteë¡ë¶í° ì·¨íë¤. ë¹ì ì 거기ì ë§ì
+ ë¤ë¥¸ ì ì©í ìì 매íë¤ì ë°ê²¬í ê²ì´ë¤. Hibernate ë°°í¬ë³¸ì <literal>test</literal>
+ í´ë를 ì´í´ë³´ë¼.
+ </para>
+
+ <para>TODO: ì´ ë´ì©ì ëë¬ì¼ ë§ë¤ì ì§ì´ë£ì ê².</para>
+
+ <sect2 id="example-mappings-typed-onetone">
+ <title>"íìíë(Typed)" one-to-one ì°ê´</title>
+<programlisting><![CDATA[<class name="Person">
+ <id name="name"/>
+ <one-to-one name="address"
+ cascade="all">
+ <formula>name</formula>
+ <formula>'HOME'</formula>
+ </one-to-one>
+ <one-to-one name="mailingAddress"
+ cascade="all">
+ <formula>name</formula>
+ <formula>'MAILING'</formula>
+ </one-to-one>
+</class>
+
+<class name="Address" batch-size="2"
+ check="addressType in ('MAILING', 'HOME', 'BUSINESS')">
+ <composite-id>
+ <key-many-to-one name="person"
+ column="personName"/>
+ <key-property name="type"
+ column="addressType"/>
+ </composite-id>
+ <property name="street" type="text"/>
+ <property name="state"/>
+ <property name="zip"/>
+</class>]]></programlisting>
+ </sect2>
+
+ <sect2 id="example-mappings-composite-key">
+ <title>Composite í¤ ìì </title>
+<programlisting><![CDATA[<class name="Customer">
+
+ <id name="customerId"
+ length="10">
+ <generator class="assigned"/>
+ </id>
+
+ <property name="name" not-null="true" length="100"/>
+ <property name="address" not-null="true" length="200"/>
+
+ <list name="orders"
+ inverse="true"
+ cascade="save-update">
+ <key column="customerId"/>
+ <index column="orderNumber"/>
+ <one-to-many class="Order"/>
+ </list>
+
+</class>
+
+<class name="Order" table="CustomerOrder" lazy="true">
+ <synchronize table="LineItem"/>
+ <synchronize table="Product"/>
+
+ <composite-id name="id"
+ class="Order$Id">
+ <key-property name="customerId" length="10"/>
+ <key-property name="orderNumber"/>
+ </composite-id>
+
+ <property name="orderDate"
+ type="calendar_date"
+ not-null="true"/>
+
+ <property name="total">
+ <formula>
+ ( select sum(li.quantity*p.price)
+ from LineItem li, Product p
+ where li.productId = p.productId
+ and li.customerId = customerId
+ and li.orderNumber = orderNumber )
+ </formula>
+ </property>
+
+ <many-to-one name="customer"
+ column="customerId"
+ insert="false"
+ update="false"
+ not-null="true"/>
+
+ <bag name="lineItems"
+ fetch="join"
+ inverse="true"
+ cascade="save-update">
+ <key>
+ <column name="customerId"/>
+ <column name="orderNumber"/>
+ </key>
+ <one-to-many class="LineItem"/>
+ </bag>
+
+</class>
+
+<class name="LineItem">
+
+ <composite-id name="id"
+ class="LineItem$Id">
+ <key-property name="customerId" length="10"/>
+ <key-property name="orderNumber"/>
+ <key-property name="productId" length="10"/>
+ </composite-id>
+
+ <property name="quantity"/>
+
+ <many-to-one name="order"
+ insert="false"
+ update="false"
+ not-null="true">
+ <column name="customerId"/>
+ <column name="orderNumber"/>
+ </many-to-one>
+
+ <many-to-one name="product"
+ insert="false"
+ update="false"
+ not-null="true"
+ column="productId"/>
+
+</class>
+
+<class name="Product">
+ <synchronize table="LineItem"/>
+
+ <id name="productId"
+ length="10">
+ <generator class="assigned"/>
+ </id>
+
+ <property name="description"
+ not-null="true"
+ length="200"/>
+ <property name="price" length="3"/>
+ <property name="numberAvailable"/>
+
+ <property name="numberOrdered">
+ <formula>
+ ( select sum(li.quantity)
+ from LineItem li
+ where li.productId = productId )
+ </formula>
+ </property>
+
+</class>]]></programlisting>
+ </sect2>
+
+ <sect2 id="example-mappings-composite-key-manytomany">
+ <title>ê³µì ë í©ì± í¤ ìì±ì ê°ì§ Many-to-many</title>
+<programlisting><![CDATA[<class name="User" table="`User`">
+ <composite-id>
+ <key-property name="name"/>
+ <key-property name="org"/>
+ </composite-id>
+ <set name="groups" table="UserGroup">
+ <key>
+ <column name="userName"/>
+ <column name="org"/>
+ </key>
+ <many-to-many class="Group">
+ <column name="groupName"/>
+ <formula>org</formula>
+ </many-to-many>
+ </set>
+</class>
+
+<class name="Group" table="`Group`">
+ <composite-id>
+ <key-property name="name"/>
+ <key-property name="org"/>
+ </composite-id>
+ <property name="description"/>
+ <set name="users" table="UserGroup" inverse="true">
+ <key>
+ <column name="groupName"/>
+ <column name="org"/>
+ </key>
+ <many-to-many class="User">
+ <column name="userName"/>
+ <formula>org</formula>
+ </many-to-many>
+ </set>
+</class>
+]]></programlisting>
+ </sect2>
+
+ <sect2 id="example-mappings-content-discrimination">
+ <title>ë´ì© ê¸°ë° íë³</title>
+<programlisting><![CDATA[<class name="Person"
+ discriminator-value="P">
+
+ <id name="id"
+ column="person_id"
+ unsaved-value="0">
+ <generator class="native"/>
+ </id>
+
+
+ <discriminator
+ type="character">
+ <formula>
+ case
+ when title is not null then 'E'
+ when salesperson is not null then 'C'
+ else 'P'
+ end
+ </formula>
+ </discriminator>
+
+ <property name="name"
+ not-null="true"
+ length="80"/>
+
+ <property name="sex"
+ not-null="true"
+ update="false"/>
+
+ <component name="address">
+ <property name="address"/>
+ <property name="zip"/>
+ <property name="country"/>
+ </component>
+
+ <subclass name="Employee"
+ discriminator-value="E">
+ <property name="title"
+ length="20"/>
+ <property name="salary"/>
+ <many-to-one name="manager"/>
+ </subclass>
+
+ <subclass name="Customer"
+ discriminator-value="C">
+ <property name="comments"/>
+ <many-to-one name="salesperson"/>
+ </subclass>
+
+</class>]]></programlisting>
+ </sect2>
+
+ <sect2 id="example-mappings-association-alternatekeys" revision="2">
+ <title>ëì²´ í¤ë¤ì ëí ì°ê´ë¤</title>
+<programlisting><![CDATA[<class name="Person">
+
+ <id name="id">
+ <generator class="hilo"/>
+ </id>
+
+ <property name="name" length="100"/>
+
+ <one-to-one name="address"
+ property-ref="person"
+ cascade="all"
+ fetch="join"/>
+
+ <set name="accounts"
+ inverse="true">
+ <key column="userId"
+ property-ref="userId"/>
+ <one-to-many class="Account"/>
+ </set>
+
+ <property name="userId" length="8"/>
+
+</class>
+
+<class name="Address">
+
+ <id name="id">
+ <generator class="hilo"/>
+ </id>
+
+ <property name="address" length="300"/>
+ <property name="zip" length="5"/>
+ <property name="country" length="25"/>
+ <many-to-one name="person" unique="true" not-null="true"/>
+
+</class>
+
+<class name="Account">
+ <id name="accountId" length="32">
+ <generator class="uuid"/>
+ </id>
+
+ <many-to-one name="user"
+ column="userId"
+ property-ref="userId"/>
+
+ <property name="type" not-null="true"/>
+
+</class>]]></programlisting>
+ </sect2>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/example_parentchild.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/example_parentchild.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/example_parentchild.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,343 @@
+<chapter id="example-parentchild">
+ <title>ìì : ë¶ëª¨/ìì</title>
+
+ <para>
+ ìë¡ì´ ì¬ì©ìë¤ì´ Hibernateë¡ ííê³ ì ìëíë ë°ë¡ 첫 ë²ì§¸ ê²ë¤ ì¤ íëë ë¶ëª¨/ìì íì
ì ê´ê³ë¥¼ 모íí ìí¤ë ê²ì´ë¤. ì´ê²ì ëí
+ ë ê°ì§ ë¤ë¥¸ ì ê·¼ë²ë¤ì´ ì¡´ì¬íë¤. ì¬ë¬ê°ì§ ì´ì ë¤ë¡ ì¸í´ í¹í ìë¡ì´ ì¬ì©ìë¤ìê² ê°ì¥ í¸í ì ê·¼ë²ì <literal>Parent</literal>ë¡ë¶í°
+ <literal>Child</literal>ë¡ì <literal><one-to-many></literal> ì°ê´ì ê°ì§ ìí°í° í´ëì¤ë¤ë¡ì <literal>Parent</literal>ì
+ <literal>Child</literal> ìì를 모íí ìí¤ë ê²ì´ë¤. (ë¤ë¥¸ ì ê·¼ë²ì <literal>Child</literal>를
+ <literal><composite-element></literal>ë¡ ì ì¸íë ê²ì´ë¤.) ì´ì , (Hibernateìì) one to many ì°ê´ì ëí ëí´í¸
+ ì미ë composite ìì 매íì ìë¯¸ë³´ë¤ ë¶ëª¨/ìì ê´ê³ì íµìì ì¸ ì미ì í¨ì¬ ë ê°ê¹ë¤ë ê²ì´ íëª
ëë¤. ì°ë¦¬ë ë¶ëª¨/ìì ê´ê³ë¥¼ í¨ì¨ì ì´ê³
+ ê°ë ¥íê² ëª¨íí ìí¤ê¸° ìí´ <emphasis>ì¼ì¤ì¼ì´ëë¤ì ê°ì§ ìë°©í¥ one to many ì°ê´</emphasis>ì ì¬ì©íë ë°©ë²ì ì¤ëª
í ê²ì´ë¤.
+ ê·¸ê²ì ì í ì´ë µì§ ìë¤!
+ </para>
+
+ <sect1 id="example-parentchild-collections">
+ <title>ì½ë ì
ë¤ì ê´í ë
¸í¸</title>
+
+ <para>
+ Hibernate ì½ë ì
ë¤ì ê·¸ê²ë¤ì ìì íê³ ìë ìí°í°ì ë
¼ë¦¬ì ë¶ë¶ì¼ë¡ ê°ì£¼ëë¤; ê²°ì½ í¬í¨ë ìí°í°ë¤ì ë¶ë¶ì´ ìëë¤. ì´ê²ì
+ ì¤ëí 구ë¶ì ì´ë¤! ê·¸ê²ì ë¤ìì ë¤ì ê²°ê³¼ë¤ì ê°ëë¤:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ ì½ë ì
ì¼ë¡ë¶í° ê°ì²´ë¥¼ ì ê±°íê³ /ì½ë ì
ì ê°ì²´ë¥¼ ì¶ê° ìí¬ ë, ì½ë ì
ìì ìì ë²ì ë²í¸ê° ì¦ê°ëë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ë§ì¼ ì½ë ì
ì¼ë¡ë¶í° ì ê±°ëìë ê°ì²´ê° íëì ê° íì
ì ì¸ì¤í´ì¤(ì를 ë¤ì´ composite ìì)ì´ë©´, ê·¸ ê°ì²´ë ìììí를 ëë´ê³
+ ê·¸ê²ì ìíê° ë°ì´í°ë² ì´ì¤ë¡ë¶í° ìì í ì ê±°ë ê²ì´ë¤. ë§ì°¬ê°ì§ë¡ íëì ê° íì
ì ì¸ì¤í´ì¤ë¥¼ ì½ë ì
ì ì¶ê°ìí¤ë ê²ì ê·¸ê²ì
+ ìíê° ì¦ì ììí ëëë¡ ê°ì ìí¬ ê²ì´ë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ë°ë©´ì, ë§ì¼ ìí°í°ê° ì½ë ì
ì¼ë¡ë¶í° ì ê±°ë ê²½ì°(one-to-many ëë many-to-many ì°ê´), ê·¸ê²ì ëí´í¸ë¡ ìì ëì§ ìì
+ ê²ì´ë¤. ì´ í¹ì§ì ìì íê² ì¼ê´ì ì´ë¤ - ë¤ë¥¸ ìí°í°ì ë´ë¶ ìíì ëí ë³ê²½ì ì°ê´ë ìí°í°ë¥¼ ì¬ë¼ì§ëë¡ ê°ì íì§ ìì ê²ì´ë¤!
+ ë§ì°¬ê°ì§ë¡ ì½ë ì
ì ìí°í°ë¥¼ ì¶ê°ìí¤ë ê²ì ëí´í¸ë¡ ê·¸ ìí°í°ê° ììí ëëë¡ ê°ì ìí¤ì§ ìëë¤.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ëì ì ì½ë ì
ì¼ë¡ì ìí°í° ì¶ê°ê° ë ìí°í°ë¤ ì¬ì´ì ë¨ì§ íëì ë§í¬ë¥¼ ìì±ìí¤ë ë°ë©´ì, ê·¸ê²ì ì ê±°íë ê²ì ë§í¬ë¥¼ ì ê±°íë¤ë ì ì´
+ ëí´í¸ í¹ì§ì´ë¤. ì´ê²ì 모ë ì¢
ë¥ì ê²½ì°ë¤ì ëí´ ë§¤ì° ì ì íë¤. ê·¸ê²ì´ ì í ì ì íì§ ìì ê³³ì ë¶ëª¨/ìì ê´ê³ì¸ ê²½ì°ì´ê³ , ì¬ê¸°ì
+ ììì ìì ë ë¶ëª¨ì ìëª
주기ì 묶ì¬ì ¸ ìë¤.
+ </para>
+
+ </sect1>
+
+ <sect1 id="example-parentchild-bidir">
+ <title>ìë°©í¥ one-to-many</title>
+
+ <para>
+ <literal>Parent</literal>ë¡ë¶í° <literal>Child</literal>ë¡ì ê°ë¨í <literal><one-to-many></literal>
+ ì°ê´ê´ê³ë¡ ììíë¤ê³ ê°ì íì.
+ </para>
+
+ <programlisting><![CDATA[<set name="children">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ ì°ë¦¬ê° ë¤ì ì½ë를 ì¤íìì¼°ë¤ë©´
+ </para>
+
+ <programlisting><![CDATA[Parent p = .....;
+Child c = new Child();
+p.getChildren().add(c);
+session.save(c);
+session.flush();]]></programlisting>
+
+ <para>
+ Hibernateë ë ê°ì SQL 문ì¥ë¤ì ì¤íí ê²ì´ë¤:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para><literal>c</literal>ì ëí ë ì½ë를 ìì±ìí¤ë <literal>INSERT</literal></para>
+ </listitem>
+ <listitem>
+ <para><literal>p</literal>ë¡ë¶í° <literal>c</literal>ë¡ì ë§í¬ë¥¼ ìì±ìí¤ë <literal>UPDATE</literal></para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ì´ê²ì ë¹í¨ì¨ì ì¼ ë¿ë§ ìëë¼, ëí <literal>parent_id</literal> ì»¬ë¼ ìì ììì <literal>NOT NULL</literal>
+ 컨ì¤í¸ë ì¸í¸ì ìë°°ëë¤. ì°ë¦¬ë ì½ë ì
매íìì <literal>not-null="true"</literal>를 ì§ì í¨ì¼ë¡ì¨ null íì© ê°ë¥
+ 컨ì¤í¸ë ì¸í¸ ìë°ì ì ì í ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[<set name="children">
+ <key column="parent_id" not-null="true"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ íì§ë§ ì´ê²ì ê¶ì¥ëë í´ê²°ì±
ì´ ìëë¤.
+ </para>
+ <para>
+ ì´ íìì 기본 ìì¸ì <literal>p</literal>ë¡ë¶í° <literal>c</literal>ë¡ì ë§í¬(foreign key <literal>parent_id</literal>)ê°
+ <literal>Child</literal> ê°ì²´ì ìíì ë¶ë¶ì¼ë¡ ê°ì£¼ëì§ ìê³ ê·¸ë¬ë¯ë¡ <literal>INSERT</literal>ë¡ ìì±ëì§ ìëë¤ë
+ ì ì´ë¤. ë°ë¼ì í´ê²°ì±
ì <literal>Child</literal> 매íì ë§í¬ ë¶ë¶ì ë§ëë ê²ì´ë¤.
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="parent" column="parent_id" not-null="true"/>]]></programlisting>
+
+ <para>
+ (ì°ë¦¬ë ëí <literal>parent</literal> íë¡í¼í°ë¥¼ <literal>Child</literal> í´ëì¤ì ì¶ê°ìí¬ íìê° ìë¤.)
+ </para>
+
+ <para>
+ ì´ì <literal>Child</literal> ìí°í°ê° ë§í¬ì ìí를 ê´ë¦¬íë¤ë ì ì ë
¸í¸íê³ , ì°ë¦¬ë ë§í¬ë¥¼ ì
ë°ì´í¸ ìí¤ì§ ë§ëë¡ ì½ë ì
ìê²
+ íµë³´íë¤. ì°ë¦¬ë <literal>inverse</literal> ìì±ì ì¬ì©íë¤.
+ </para>
+
+ <programlisting><![CDATA[<set name="children" inverse="true">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ ë¤ì ì½ëë ìë¡ì´ <literal>Child</literal>를 ì¶ê°ìí¤ëë° ì¬ì©ë ê²ì´ë¤
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+Child c = new Child();
+c.setParent(p);
+p.getChildren().add(c);
+session.save(c);
+session.flush();]]></programlisting>
+
+ <para>
+ ê·¸ë¦¬ê³ ì´ì , ì ì¼íê² í ê°ì SQL <literal>INSERT</literal>ê° ì¤íë ê²ì´ë¤!
+ </para>
+
+ <para>
+ ì½ê° ê±°ì¹ ê², ì°ë¦¬ë <literal>Parent</literal>ì <literal>addChild()</literal> ë©ìë를 ìì±ìí¬ ì ìë¤.
+ </para>
+
+ <programlisting><![CDATA[public void addChild(Child c) {
+ c.setParent(this);
+ children.add(c);
+}]]></programlisting>
+
+ <para>
+ ì´ì , <literal>Child</literal>를 ì¶ê°íë ì½ëë ë¤ìê³¼ ê°ë¤
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+Child c = new Child();
+p.addChild(c);
+session.save(c);
+session.flush();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-parentchild-cascades">
+ <title>ì¼ì¤ì¼ì´ë© ìëª
주기</title>
+
+ <para>
+ <literal>save()</literal>ì ëí ëª
ìì ì¸ í¸ì¶ì ì¬ì í ì±ê°ìë¤. ì°ë¦¬ë ì¼ì¤ì¼ì´ë©ì ì¬ì©íì¬ ì´ê²ì ì기í ê²ì´ë¤.
+ </para>
+
+ <programlisting><![CDATA[<set name="children" inverse="true" cascade="all">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ ë¤ìì ìì ì½ë를 ë¨ìí ìí¨ë¤
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+Child c = new Child();
+p.addChild(c);
+session.flush();]]></programlisting>
+
+ <para>
+ ì ì¬íê², ì°ë¦¬ë <literal>Parent</literal>를 ì ì¥íê±°ë ìì í ë ììë¤ì ëí´ ë°ë³µíë ê²ì íìë¡ íì§ ìëë¤. ë¤ìì
+ ë°ì´í°ë² ì´ì¤ë¡ë¶í° <literal>p</literal>ì 모ë ê·¸ê²ì ììë¤ì ì ê±°ìí¨ë¤.
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+session.delete(p);
+session.flush();]]></programlisting>
+
+ <para>
+ íì§ë§, ë¤ì ì½ë
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+Child c = (Child) p.getChildren().iterator().next();
+p.getChildren().remove(c);
+c.setParent(null);
+session.flush();]]></programlisting>
+
+ <para>
+ ë ë°ì´í°ë² ì´ì¤ë¡ë¶í° <literal>c</literal>를 ì ê±°íì§ ìì ê²ì´ë¤; ê·¸ê²ì ì¤ì§ <literal>p</literal>ì ëí ë§í¬ë§ì ì ê±°í
+ ê²ì´ë¤(ê·¸ë¦¬ê³ ì´ ê²½ì°ì <literal>NOT NULL</literal> 컨ì¤í¸ë ì¸í¸ ìë°ì ì¼ì¼í¬ ê²ì´ë¤ ). ë¹ì ì ëª
ìì ì¼ë¡
+ <literal>Child</literal>를 <literal>delete()</literal> ìí¬ íìê° ìë¤.
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+Child c = (Child) p.getChildren().iterator().next();
+p.getChildren().remove(c);
+session.delete(c);
+session.flush();]]></programlisting>
+
+ <para>
+ ì´ì ì°ë¦¬ì ê²½ì°ì <literal>Child</literal>ë ê·¸ê²ì ë¶ëª¨ ìì´ë ì§ì ì¼ë¡ ì¡´ì¬í ì ìë¤. ë°ë¼ì ë§ì¼ ì°ë¦¬ê° ì½ë ì
ì¼ë¡ë¶í°
+ íëì <literal>Child</literal>를 ì ê±°í ê²½ì°, ì°ë¦¬ë ê·¸ê²ì´ ì ë§ë¡ ìì ë기를 ìíë¤. ì´ë¥¼ ìí´ ì°ë¦¬ë
+ <literal>cascade="all-delete-orphan"</literal>ì ì¬ì©í´ì¼ íë¤.
+ </para>
+
+ <programlisting><![CDATA[<set name="children" inverse="true" cascade="all-delete-orphan">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ ë
¸í¸: ë¹ë¡ ì½ë ì
매íì´ <literal>inverse="true"</literal>를 ì§ì í ì§ë¼ë, ì¼ì¤ì¼ì´ëë¤ì ì¬ì í ì½ë ì
ììë¤ì
+ ë°ë³µí¨ì¼ë¡ì¨ ì²ë¦¬ëë¤. ë°ë¼ì ê°ì²´ê° ì¼ì¤ì¼ì´ëì ìí´ ì ì¥ëê³ , ìì ëê±°ë ì
ë°ì´í¸ ëë ê²ì ë¹ì ì´ íìë¡ í ê²½ì°, ë¹ì ì
+ ê·¸ê²ì ê·¸ ì½ë ì
ì ì¶ê°í´ì¼ íë¤. ë¨ìí <literal>setParent()</literal>를 í¸ì¶íë ê²ì¼ë¡ë 충ë¶íì§ ìë¤.
+ </para>
+
+ </sect1>
+
+ <sect1 id="example-parentchild-update">
+ <title>ì¼ì¤ì¼ì´ëë¤ê³¼ <literal>unsaved-value</literal></title>
+
+ <para>
+ ì°ë¦¬ê° íëì <literal>Session</literal> ìì <literal>Parent</literal>를 ë¡ëìì¼°ê³ UI ì¡ì
ìì ì´ë¤ ë³ê²½ë¤ì ííê³ ,
+ <literal>update()</literal>를 í¸ì¶íì¬ ìë¡ì´ ì¸ì
ìì ì´ë¤ ë³ê²½ë¤ì ììí ìí¤ë ê²ì ìíë¤ê³ ê°ì íì. <literal>Parent</literal>ë
+ ììë¤ì ê°ì§ ì½ë ì
ì í¬í¨í ê²ì´ê³ , ì¼ì¤ì¼ì´ë© ì
ë°ì´í¸ê° ì¬ì© ê°ë¥í기 ë문ì, Hibernateë ì´ë ììë¤ì´ ìë¡ì´ ì´ê¸°í ëëì§
+ ê·¸ë¦¬ê³ ì´ë ê²ì´ ë°ì´í°ë² ì´ì¤ìì íì¬ íë¤ì íííëì§ë¥¼ ì íìê° ìë¤. <literal>Parent</literal>ì <literal>Child</literal>
+ 모ë <literal>Long</literal> íì
ì ìë³ì íë¡í¼í°ë¤ì ìì±ìì¼°ë¤ê³ ê°ì íì. Hibernateë ì´ë ììë¤ì´ ìë¡ì´ ê²ì¸ì§ë¥¼
+ ê²°ì íëë° ìë³ìì version/timestamp íë¡í¼í° ê°ì ì¬ì©í ê²ì´ë¤.(<xref linkend="objectstate-saveorupdate"/>ì ë³´ë¼.)
+ <emphasis>Hibernate3ììë<literal>unsaved-value</literal>를 ëì´ì ëª
ìì ì¼ë¡ ì§ì í íìê° ìë¤.</emphasis>
+ </para>
+
+ <para>
+ ë¤ì ì½ëë <literal>parent</literal>ì <literal>child</literal>를 ì
ë°ì´í¸íê³ <literal>newChild</literal>를
+ ì½ì
ìí¬ ê²ì´ë¤.
+ </para>
+
+ <programlisting><![CDATA[//parent and child were both loaded in a previous session
+parent.addChild(child);
+Child newChild = new Child();
+parent.addChild(newChild);
+session.update(parent);
+session.flush();]]></programlisting>
+
+ <para>
+ ë¬¼ë¡ ê·¸ê²ì ìì±ëë ìë³ìì ê²½ì°ìë 모ë ë§¤ì° ì¢ì§ë§, í ë¹ëë ìë³ìë¤ê³¼ composite ìë³ìë¤ì ëí´ìë ì´ë íê°? ì´ê²ì ë³´ë¤
+ ì´ë µë¤. ìëíë©´ Hibernateë (ì¬ì©ìì ìí´ í ë¹ë ìë³ì를 ê°ì§) ìë¡ì´ ì´ê¸°í ë ê°ì²´ì ì´ì ì¸ì
ìì ë¡ëëìë ê°ì²´ ì¬ì´ë¥¼
+ 구ë³ì§ëë° ìë³ì íë¡í¼í°ë¥¼ ì¬ì©í ì ì기 ë문ì´ë¤. ì´ ê²½ì°ì, Hibernateë timestamp íë¡í¼í° ëë version íë¡í¼í°ë¥¼
+ ì¬ì©íê±°ë ì¤ì ë¡ second-level ìºì를 ì§ìíê±°ë ê°ì¥ ëì ê²½ì°ìë íì´ ì¡´ì¬íëì§ë¥¼ ì기 ìí´ ë°ì´í°ë² ì´ì¤ë¥¼ ì§ìí ê²ì´ë¤.
+ </para>
+
+ <!-- undocumenting
+ <para>
+ There is one further possibility. The <literal>Interceptor</literal> method named
+ <literal>isUnsaved()</literal> lets the application implement its own strategy for distinguishing
+ newly instantiated objects. For example, you could define a base class for your persistent classes.
+ </para>
+
+ <programlisting><![CDATA[public class Persistent {
+ private boolean _saved = false;
+ public void onSave() {
+ _saved=true;
+ }
+ public void onLoad() {
+ _saved=true;
+ }
+ ......
+ public boolean isSaved() {
+ return _saved;
+ }
+}]]></programlisting>
+
+ <para>
+ (The <literal>saved</literal> property is non-persistent.)
+ Now implement <literal>isUnsaved()</literal>, along with <literal>onLoad()</literal>
+ and <literal>onSave()</literal> as follows.
+ </para>
+
+ <programlisting><![CDATA[public Boolean isUnsaved(Object entity) {
+ if (entity instanceof Persistent) {
+ return new Boolean( !( (Persistent) entity ).isSaved() );
+ }
+ else {
+ return null;
+ }
+}
+
+public boolean onLoad(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+
+ if (entity instanceof Persistent) ( (Persistent) entity ).onLoad();
+ return false;
+}
+
+public boolean onSave(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+
+ if (entity instanceof Persistent) ( (Persistent) entity ).onSave();
+ return false;
+}]]></programlisting>
+
+ <para>
+ Don't worry; in Hibernate3 you don't need to write any of this kind of code if you don't want to.
+ </para>
+ -->
+ </sect1>
+
+ <sect1 id="example-parentchild-conclusion">
+ <title>ê²°ë¡ </title>
+
+ <para>
+ ì¬ê¸°ì ìì§í ê²ì´ ì½ê° ìê³ ê·¸ê²ì ì²ììë í¼ëì¤ë¬ì´ ê²ì²ë¼ ë³´ì¼ ì ìë¤. íì§ë§ ì¤ì ë¡ ê·¸ê²ì 모ë ë§¤ì° ì¢ê² ëìíë¤. ëë¶ë¶ì
+ Hibernate ì´í리ì¼ì´ì
ë¤ì ë§ì ì¥ìë¤ìì ë¶ëª¨/ìì í¨í´ì ì¬ì©íë¤.
+ </para>
+
+ <para>
+ ì°ë¦¬ë 첫 ë²ì§¸ ë¨ë½ìì ëìì ì¸ê¸íë¤. ìì ìì ë¤ ì¤ ì´ë ê²ë ì ííê² ë¶ëª¨/ìì ê´ê³ì ì미를 ê°ì§,
+ <literal><composite-element></literal> 매íë¤ì ê²½ì°ìë ì¡´ì¬íì§ ìëë¤. ë¶ííë, composite ìì í´ëì¤ë¤ì
+ ëí ë ê°ì 커ë¤ë ì ì½ë¤ì´ ì¡´ì¬íë¤: composite ììë¤ì ì½ë ì
ë¤ì ìì íì§ ìê³ , ê·¸ê²ë¤ì ì ì¼í ë¶ëª¨ê° ìë ë¤ë¥¸ ì´ë¤
+ ìí°í°ì ììì¼ ìë ìë¤.
+ </para>
+
+ </sect1>
+
+</chapter>
\ No newline at end of file
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/example_weblog.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/example_weblog.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/example_weblog.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,428 @@
+<chapter id="example-weblog">
+ <title>ìì : Weblog ì´í리ì¼ì´ì
</title>
+
+ <sect1 id="example-weblog-classes">
+ <title>ìì í´ëì¤ë¤</title>
+
+ <para>
+ ìì í´ëì¤ë¤ì ì¹ë¡ê·¸, ê·¸ë¦¬ê³ ì¹ ë¡ê·¸ ë´ì ê²ìë í목ì íííë¤.ê·¸ê²ë¤ì
+ íì¤ ë¶ëª¨/ìì ê´ê³ë¡ 모íí ë ê²ì´ì§ë§, ì°ë¦¬ë set ëì ì ììì§ìì§ bag를 ì¬ì©í ê²ì´ë¤.
+ </para>
+
+ <programlisting><![CDATA[package eg;
+
+import java.util.List;
+
+public class Blog {
+ private Long _id;
+ private String _name;
+ private List _items;
+
+ public Long getId() {
+ return _id;
+ }
+ public List getItems() {
+ return _items;
+ }
+ public String getName() {
+ return _name;
+ }
+ public void setId(Long long1) {
+ _id = long1;
+ }
+ public void setItems(List list) {
+ _items = list;
+ }
+ public void setName(String string) {
+ _name = string;
+ }
+}]]></programlisting>
+
+ <programlisting><![CDATA[package eg;
+
+import java.text.DateFormat;
+import java.util.Calendar;
+
+public class BlogItem {
+ private Long _id;
+ private Calendar _datetime;
+ private String _text;
+ private String _title;
+ private Blog _blog;
+
+ public Blog getBlog() {
+ return _blog;
+ }
+ public Calendar getDatetime() {
+ return _datetime;
+ }
+ public Long getId() {
+ return _id;
+ }
+ public String getText() {
+ return _text;
+ }
+ public String getTitle() {
+ return _title;
+ }
+ public void setBlog(Blog blog) {
+ _blog = blog;
+ }
+ public void setDatetime(Calendar calendar) {
+ _datetime = calendar;
+ }
+ public void setId(Long long1) {
+ _id = long1;
+ }
+ public void setText(String string) {
+ _text = string;
+ }
+ public void setTitle(String string) {
+ _title = string;
+ }
+}]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-weblog-mappings">
+ <title>Hibernate 매íë¤</title>
+
+ <para>
+ XML 매íë¤ì ì´ì ë§¤ì° ê°ë¨í´ì§ ê²ì´ë¤.
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="eg">
+
+ <class
+ name="Blog"
+ table="BLOGS">
+
+ <id
+ name="id"
+ column="BLOG_ID">
+
+ <generator class="native"/>
+
+ </id>
+
+ <property
+ name="name"
+ column="NAME"
+ not-null="true"
+ unique="true"/>
+
+ <bag
+ name="items"
+ inverse="true"
+ order-by="DATE_TIME"
+ cascade="all">
+
+ <key column="BLOG_ID"/>
+ <one-to-many class="BlogItem"/>
+
+ </bag>
+
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="eg">
+
+ <class
+ name="BlogItem"
+ table="BLOG_ITEMS"
+ dynamic-update="true">
+
+ <id
+ name="id"
+ column="BLOG_ITEM_ID">
+
+ <generator class="native"/>
+
+ </id>
+
+ <property
+ name="title"
+ column="TITLE"
+ not-null="true"/>
+
+ <property
+ name="text"
+ column="TEXT"
+ not-null="true"/>
+
+ <property
+ name="datetime"
+ column="DATE_TIME"
+ not-null="true"/>
+
+ <many-to-one
+ name="blog"
+ column="BLOG_ID"
+ not-null="true"/>
+
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-weblog-code">
+ <title>Hibernate ì½ë</title>
+
+ <para>
+ ë¤ì í´ëì¤ë ì°ë¦¬ê° Hibernate를 ì¬ì©íì¬ ì´ë¤ í´ëì¤ë¤ë¡ íí ì ìë ëªëª ì¢
ë¥ì ê²ë¤ì ì¤ëª
íë¤.
+ </para>
+
+ <programlisting><![CDATA[package eg;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Iterator;
+import java.util.List;
+
+import org.hibernate.HibernateException;
+import org.hibernate.Query;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.tool.hbm2ddl.SchemaExport;
+
+public class BlogMain {
+
+ private SessionFactory _sessions;
+
+ public void configure() throws HibernateException {
+ _sessions = new Configuration()
+ .addClass(Blog.class)
+ .addClass(BlogItem.class)
+ .buildSessionFactory();
+ }
+
+ public void exportTables() throws HibernateException {
+ Configuration cfg = new Configuration()
+ .addClass(Blog.class)
+ .addClass(BlogItem.class);
+ new SchemaExport(cfg).create(true, true);
+ }
+
+ public Blog createBlog(String name) throws HibernateException {
+
+ Blog blog = new Blog();
+ blog.setName(name);
+ blog.setItems( new ArrayList() );
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ session.persist(blog);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return blog;
+ }
+
+ public BlogItem createBlogItem(Blog blog, String title, String text)
+ throws HibernateException {
+
+ BlogItem item = new BlogItem();
+ item.setTitle(title);
+ item.setText(text);
+ item.setBlog(blog);
+ item.setDatetime( Calendar.getInstance() );
+ blog.getItems().add(item);
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ session.update(blog);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return item;
+ }
+
+ public BlogItem createBlogItem(Long blogid, String title, String text)
+ throws HibernateException {
+
+ BlogItem item = new BlogItem();
+ item.setTitle(title);
+ item.setText(text);
+ item.setDatetime( Calendar.getInstance() );
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ Blog blog = (Blog) session.load(Blog.class, blogid);
+ item.setBlog(blog);
+ blog.getItems().add(item);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return item;
+ }
+
+ public void updateBlogItem(BlogItem item, String text)
+ throws HibernateException {
+
+ item.setText(text);
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ session.update(item);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ }
+
+ public void updateBlogItem(Long itemid, String text)
+ throws HibernateException {
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ BlogItem item = (BlogItem) session.load(BlogItem.class, itemid);
+ item.setText(text);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ }
+
+ public List listAllBlogNamesAndItemCounts(int max)
+ throws HibernateException {
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ List result = null;
+ try {
+ tx = session.beginTransaction();
+ Query q = session.createQuery(
+ "select blog.id, blog.name, count(blogItem) " +
+ "from Blog as blog " +
+ "left outer join blog.items as blogItem " +
+ "group by blog.name, blog.id " +
+ "order by max(blogItem.datetime)"
+ );
+ q.setMaxResults(max);
+ result = q.list();
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return result;
+ }
+
+ public Blog getBlogAndAllItems(Long blogid)
+ throws HibernateException {
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ Blog blog = null;
+ try {
+ tx = session.beginTransaction();
+ Query q = session.createQuery(
+ "from Blog as blog " +
+ "left outer join fetch blog.items " +
+ "where blog.id = :blogid"
+ );
+ q.setParameter("blogid", blogid);
+ blog = (Blog) q.uniqueResult();
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return blog;
+ }
+
+ public List listBlogsAndRecentItems() throws HibernateException {
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ List result = null;
+ try {
+ tx = session.beginTransaction();
+ Query q = session.createQuery(
+ "from Blog as blog " +
+ "inner join blog.items as blogItem " +
+ "where blogItem.datetime > :minDate"
+ );
+
+ Calendar cal = Calendar.getInstance();
+ cal.roll(Calendar.MONTH, false);
+ q.setCalendar("minDate", cal);
+
+ result = q.list();
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return result;
+ }
+}]]></programlisting>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/filters.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/filters.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/filters.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,138 @@
+<chapter id="filters">
+ <title>ë°ì´í° íí°ë§í기</title>
+
+ <para>
+ Hibernate3ì íì ì ì¸ "ê°ìì±(visibility)" ê·ì¹ë¤ë¡ì ë°ì´í°ë¥¼ ì²ë¦¬íë ìë¡ì´ ì ê·¼ë²ì ì ê³µíë¤. <emphasis>Hibernate
+ íí°</emphasis>ë í¹ì Hibernate ì¸ì
ì ëí´ ì´ì© ê°ë¥íê² ëê±°ë ì´ì© ë¶ê°ë¥íê² ë ìë ìë ì ì, ëª
ëª
ë íë¼ë¯¸í°í ë íí°ì´ë¤.
+ </para>
+
+ <sect1 id="objectstate-filters" revision="1">
+ <title>Hibernate íí°ë¤</title>
+
+ <para>
+ Hibernate3ì íí° ê¸°ì¤(criteria)ì 미리 ì ìíê³ í´ëì¤ ë 벨과 ì½ë ì
ë 벨 ìììì ê·¸ë¤ íí°ë¤ì 첨ë¶í ë¥ë ¥ì ì¶ê°ìí¨ë¤.
+ íí° ê¸°ì¤(criteria)ì í´ëì¤ ììì ë¤ìí ì½ë ì
ììë¤ì ëí´ ì´ì© ê°ë¥í 기존ì "where" ìì±ê³¼ ë§¤ì° ì ì¬í íëì ì í ì ì
+ ì ìíë ë¥ë ¥ì´ë¤. ì´ê²ë¤ì ì ì¸íë©´ íí° ì¡°ê±´ë¤ì íë¼ë¯¸í°í ë ì ìë¤. ê·¸ë ì´í리ì¼ì´ì
ì 주ì´ì§ íí°ë¤ì´ ì´ì© ê°ë¥íì§ ì¬ë¶
+ ê·¸ë¦¬ê³ ê·¸ë¤ íë¼ë¯¸í° ê°ë¤ì´ 무ìì´ì´ì¼ íëì§ë¥¼ ì¤í ìì ê²°ì í ì ìë¤. íí°ë¤ì ë°ì´í°ë² ì´ì¤ ë·°ë¤ ì²ë¼ ì¬ì©ë ì ìì§ë§,
+ ì´í리ì¼ì´ì
ë´ë¶ì íë¼ë¯¸í°í ëë¤.
+ </para>
+
+ <para>
+ íí°ë¤ì ì¬ì©í기 ìí´ì, ê·¸ê²ë¤ì 먼ì ì ìëê³ ëì ì ì í 매í ììë¤ì 첨ê°ëì´ì¼ íë¤. íí°ë¥¼ ì ìí기 ìí´,
+ <literal><hibernate-mapping/></literal> ìì ë´ë¶ì <literal><filter-def/></literal> ìì를
+ ì¬ì©íë¼:
+ </para>
+
+ <programlisting><![CDATA[<filter-def name="myFilter">
+ <filter-param name="myFilterParam" type="string"/>
+</filter-def>]]></programlisting>
+
+ <para>
+ ê·¸ë ì´ íí°ë í´ëì¤ì 첨ê°ë ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[<class name="myClass" ...>
+ ...
+ <filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
+</class>]]></programlisting>
+
+ <para>
+ ëë ì½ë ì
ì 첨ê°ë ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[<set ...>
+ <filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
+</set>]]></programlisting>
+
+ <para>
+ ëë ëìì ììì(ëë ê°ê°ì ì¬ë¬ë²) 첨ê°ë ì ìë¤.
+ </para>
+
+ <para>
+ <literal>Session</literal> ìì ë©ìëë¤ì ë¤ìê³¼ ê°ë¤: <literal>enableFilter(String filterName)</literal>,
+ <literal>getEnabledFilter(String filterName)</literal>, <literal>disableFilter(String filterName)</literal>.
+ ëí´í¸ë¡, íí°ë¤ì 주ì´ì§ ì¸ì
ì ëí´ ì´ì© ê°ë¥íì§ <emphasis>ìë¤</emphasis>; ê·¸ê²ë¤ì <literal>Session.enabledFilter()</literal>
+ ë©ìëì ì¬ì©ì íµí´ ëª
ìì ì¼ë¡ ì´ì© ê°ë¥íê² ëì´ì¼ íë¤. <literal>Session.enabledFilter()</literal>ë
+ <literal>Filter</literal> ì¸í°íì´ì¤ì ì¸ì¤í´ì¤ë¥¼ ë°ííë¤. ìì ì ìë ê°ë¨í íí°ë¥¼ ì¬ì©íë©´, ì´ê²ì ë¤ìê³¼ ê°ì ê²ì´ë¤:
+ </para>
+
+ <programlisting><![CDATA[session.enableFilter("myFilter").setParameter("myFilterParam", "some-value");]]></programlisting>
+
+ <para>
+ org.hibernate.Filter ì¸í°íì´ì¤ ìì ë©ìëë¤ì Hibernateì ë§¤ì° ê³µíµë method-chainingì íì©íë¤ë ì ì ë
¸í¸íë¼.
+ </para>
+
+ <para>
+ ì í¨í ê¸°ë¡ ë ì§ í¨í´ì ê°ì§ ìê° ë°ì´í°ë¥¼ ì¬ì©íë ì ì²´ ìì :
+ </para>
+
+ <programlisting><![CDATA[<filter-def name="effectiveDate">
+ <filter-param name="asOfDate" type="date"/>
+</filter-def>
+
+<class name="Employee" ...>
+...
+ <many-to-one name="department" column="dept_id" class="Department"/>
+ <property name="effectiveStartDate" type="date" column="eff_start_dt"/>
+ <property name="effectiveEndDate" type="date" column="eff_end_dt"/>
+...
+ <!--
+ Note that this assumes non-terminal records have an eff_end_dt set to
+ a max db date for simplicity-sake
+ -->
+ <filter name="effectiveDate"
+ condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
+</class>
+
+<class name="Department" ...>
+...
+ <set name="employees" lazy="true">
+ <key column="dept_id"/>
+ <one-to-many class="Employee"/>
+ <filter name="effectiveDate"
+ condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ ê·¸ë ë¹ì ì´ íì¬ ì í¨í ë ì½ëë¤ì íì ì»ë ê²ì íì¤í í기 ìí´, employee ë°ì´í°ë¥¼ ê²ìí기 ì ì ì¸ì
ìì íí°ë¥¼
+ ê°ë¨íê² ì´ì© ê°ë¥íê² íë¼:
+ </para>
+
+<programlisting><![CDATA[Session session = ...;
+session.enabledFilter("effectiveDate").setParameter("asOfDate", new Date());
+List results = session.createQuery("from Employee as e where e.salary > :targetSalary")
+ .setLong("targetSalary", new Long(1000000))
+ .list();
+]]></programlisting>
+
+ <para>
+ ìì HQL ìì, ì¬ì§ì´ ë¹ë¡ ì°ë¦¬ê° ê²°ê³¼ë¤ì ëí ë´ê¸ 컨ì¤í¸ë ì¸í¸ë¥¼ ëª
ìì ì¼ë¡ ì¸ê¸ë§ íìì§ë¼ë, ì´ì© ê°ë¥í íí° ë문ì
+ ê·¸ ì§ìë ë´ê¸ì´ ë°±ë§ë¬ë¬ ì´ìì¸ íì¬ ì±ì©ì¤ì¸ ì§ìë¤ë§ì ë°íí ê²ì´ë¤.
+ </para>
+
+ <para>
+ ë
¸í¸: ë§ì¼ ë¹ì ì´ outer ì¡°ì¸ì ëí´ íí°ë¤ì ì¬ì©í ê³íì´ë¼ë©´ (HQLì´ë ë¡ë íì¹ì´ë ) ì¡°ê±´ ííìì ë°©í¥ì 주ìíë¼.
+ ì´ê²ì left outer joinì¼ë¡ ì¤ì íë ê²ì´ ê°ì¥ ìì íë¤; ì¼ë°ì ì¼ë¡ ì¤í¼ë ì´í° ë¤ì ìë ì»¬ë¼ ì´ë¦(ë¤)ì´ ë¤ë°ë¥´ë 첫ë²ì§¸ì
+ íë¼ë¯¸í°ë¥¼ ìì¹ì§ìë¼.
+ </para>
+
+ </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>
+
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/inheritance_mapping.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/inheritance_mapping.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/inheritance_mapping.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,459 @@
+<chapter id="inheritance">
+ <title>ìì 매í</title>
+
+ <sect1 id="inheritance-strategies" revision="3">
+ <title>ì¸ ê°ì§ ë°©ëë¤</title>
+
+ <para>
+ Hibernateë ì¸ ê°ì§ 기본ì ì¸ ìì 매í ë°©ëë¤ì ì§ìíë¤:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ table per class hierarchy
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ table per subclass
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ table per concrete class
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ê²ë¤ê° Hibernateë ë¤ ë²ì§¸ì ì½ê° ë¤ë¥¸ ì¢
ë¥ì ë¤íì±ì ì§ìíë¤:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ implicit polymorphism(í¨ì¶ì ì¸ ë¤íì±)
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ëì¼í ìì ê³ì¸µêµ¬ì¡°ì ë¤ë¥¸ ê°ì§ë¤ì ëí´ ë¤ë¥¸ 매í ë°©ëë¤ì ì¬ì©íë ê²ì´ ê°ë¥íê³ , ê·¸ë° ë¤ì ì ì²´ ê³ì¸µ 구조를 ê°ë¡ì§ë¬
+ ë¤íì±ì ì±ì·¨íëë° í¨ì¶ì ì¸ ë¤íì±ì ì¬ì©íë¼. íì§ë§ Hibernateë ëì¼í ë£¨í¸ <literal><class></literal> ìì
+ íìì <literal><subclass></literal> ê·¸ë¦¬ê³ <literal><joined-subclass></literal> 그리ê³
+ <literal><union-subclass></literal> 매íë¤ì í¼í©íë ê²ì ì§ìíì§ ìëë¤. ëì¼í <literal><class></literal>
+ ìì íìì <literal><subclass></literal> ììì <literal><join></literal> ìì를 ê²°í©ìí´ì¼ë¡ì¨
+ table per hierarchy ë°©ëì table per subclass ë°©ë를 í¨ê» í¼í©ìí¤ë ê²ì´ ê°ë¥íë¤(ìë를 ë³´ë¼).
+ </para>
+
+ <para>
+ ë³ëì 매í 문ì ë´ì, <literal>hibernate-mapping</literal> ë°ë¡ ë°ì <literal>subclass</literal>,
+ <literal>union-subclass</literal>, ê·¸ë¦¬ê³ <literal>joined-subclass</literal> 매íë¤ì ì ìíë ê²ì´
+ ê°ë¥íë¤. ì´ê²ì ë¨ì§ íëì ìë¡ì´ 매í íì¼ì ì¶ê°ìì¼ì íëì class ê³ì¸µêµ¬ì¡°ë¥¼ íì¥íë ê²ì ë¹ì ìê² íì©í´ì¤ë¤.
+ ë¹ì ì subclass 매í ë´ì ìì 매íë ìí¼í´ëì¤ë¥¼ ëª
ëª
íì¬ <literal>extends</literal> ìì±ì ì§ì í´ì¼ íë¤.
+ ë
¸í¸ : ëª
ë°±íê² ì´ í¹ì§ì 매í 문ìë¤ì ìì를 ì¤ìíê²ë ë§ë¤ìë¤. Hibernate3 ì´íë¡, 매í íì¼ë¤ì ììë extends í¤ìë를
+ ì¬ì©í ë ìê´ìë¤. íëì 매í íì¼ ë´ì ììë ì¬ì í ìë¸í´ëì¤ë¤ì ìì ìí¼í´ëì¤ë¤ì ì ìíëë° ì¬ì í íìíë¤.
+ </para>
+
+ <programlisting><![CDATA[
+ <hibernate-mapping>
+ <subclass name="DomesticCat" extends="Cat" discriminator-value="D">
+ <property name="name" type="string"/>
+ </subclass>
+ </hibernate-mapping>]]></programlisting>
+
+
+ <sect2 id="inheritance-tableperclass" >
+ <title>Table per class hierarchy</title>
+
+ <para>
+ ì°ë¦¬ê° <literal>CreditCardPayment</literal>, <literal>CashPayment</literal>, <literal>ChequePayment</literal>
+ 구íìë¤ì ê°ì§ íëì ì¸í°íì´ì¤ <literal>Payment</literal>를 ê°ê³ ìë¤ê³ ê°ì íì. table per hierarchy 매íì
+ ë¤ìê³¼ ê°ì ê²ì´ë¤:
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment" table="PAYMENT">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <discriminator column="PAYMENT_TYPE" type="string"/>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <subclass name="CreditCardPayment" discriminator-value="CREDIT">
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </subclass>
+ <subclass name="CashPayment" discriminator-value="CASH">
+ ...
+ </subclass>
+ <subclass name="ChequePayment" discriminator-value="CHEQUE">
+ ...
+ </subclass>
+</class>]]></programlisting>
+
+ <para>
+ ì íí íëì í
ì´ë¸ì´ íìíë¤. ì´ ë§¤í ë°©ëìë ë¤ìì íëì í° ì ì½ì´ ì¡´ì¬íë¤: <literal>CCTYPE</literal>ê³¼ ê°ì´,
+ ìë¸ í´ëì¤ë¤ì ìí´ ì ì¸ë 컬ë¼ë¤ì <literal>NOT NULL</literal> 컨ì¤í¸ë ì¸í¸ë¤ì ê°ì§ ì ìë¤.
+ </para>
+
+ </sect2>
+
+ <sect2 id="inheritance-tablepersubclass">
+ <title>Table per subclass</title>
+
+ <para>
+ table per subclass 매íì ë¤ìê³¼ ê°ì ê²ì´ë¤:
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment" table="PAYMENT">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <joined-subclass name="CreditCardPayment" table="CREDIT_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </joined-subclass>
+ <joined-subclass name="CashPayment" table="CASH_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ ...
+ </joined-subclass>
+ <joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ ...
+ </joined-subclass>
+</class>]]></programlisting>
+
+ <para>
+ ë¤ ê°ì í
ì´ë¸ë¤ì´ íìíë¤. ì¸ ê°ì ìë¸í´ëì¤ í
ì´ë¸ë¤ì ìí¼í´ëì¤ í
ì´ë¸ì ëí íë¼ì´ë¨¸ë¦¬ í¤ ì°ê´ë¤ì ê°ëë¤
+ (ë°ë¼ì ê·¸ ê´ê³í 모íì ì¤ì ë¡ one-to-one ì°ê´ì´ë¤).
+ </para>
+
+ </sect2>
+
+ <sect2 id="inheritance-tablepersubclass-discriminator" revision="2">
+ <title>discriminator를 ì¬ì©íë, table per subclass</title>
+
+ <para>
+ table-per-subclassì ëí Hibernateì 구íì discriminator(íë³ì) 컬ë¼ì íìë¡ íì§ ììì ë
¸í¸íë¼.
+ ë¤ë¥¸ ê°ì²´/ê´ê³í 매í기ë¤ì ìí¼í´ëì¤ í
ì´ë¸ ìì íëì íì
íë³ì 컬ë¼ì íìë¡ íë table-per-subclassì ëí ë¤ë¥¸ 구íì
+ ì¬ì©íë¤. Hibernateì ìí´ ì±íë ì ê·¼ë²ì 구ííê¸°ê° í¨ì¬ ë ì´ë µì§ë§ ê´ê³í ê´ì ììë ìë§ í림ìì´ ë³´ë¤ ë ì ííë¤.
+ ë§ì¼ ë¹ì ì´ table per subclass ë°©ëì ëí´ íëì íë³ì 컬ë¼ì ì¬ì©íê³ ì¶ë¤ë©´, ë¹ì ì ë¤ìê³¼ ê°ì´
+ <literal><subclass></literal>ì <literal><join></literal>ì ì¬ì©ì ê²°í©ìí¬ ìë ìë¤:
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment" table="PAYMENT">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <discriminator column="PAYMENT_TYPE" type="string"/>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <subclass name="CreditCardPayment" discriminator-value="CREDIT">
+ <join table="CREDIT_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </join>
+ </subclass>
+ <subclass name="CashPayment" discriminator-value="CASH">
+ <join table="CASH_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ ...
+ </join>
+ </subclass>
+ <subclass name="ChequePayment" discriminator-value="CHEQUE">
+ <join table="CHEQUE_PAYMENT" fetch="select">
+ <key column="PAYMENT_ID"/>
+ ...
+ </join>
+ </subclass>
+</class>]]></programlisting>
+
+ <para>
+ ì íì ì¸ <literal>fetch="select"</literal> ì ì¸ì ìí¼í´ëì¤ë¥¼ ì§ìí ë outer joinì ì¬ì©íì¬
+ <literal>ChequePayment</literal> ìë¸í´ëì¤ ë°ì´í°ë¥¼ íì¹ìí¤ì§ ìëë¡ Hibernateìê² ìë ¤ì¤ë¤.
+ </para>
+
+ </sect2>
+
+ <sect2 id="inheritance-mixing-tableperclass-tablepersubclass">
+ <title>table per class hierarchyì table per subclass를 í¼í©í기</title>
+
+ <para>
+ ë¹ì ì ì´ ì ê·¼ë²ì ì¬ì©íì¬ table per hierarchy ë°©ëì table per subclass ë°©ë를 í¼í©ìí¬ ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment" table="PAYMENT">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <discriminator column="PAYMENT_TYPE" type="string"/>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <subclass name="CreditCardPayment" discriminator-value="CREDIT">
+ <join table="CREDIT_PAYMENT">
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </join>
+ </subclass>
+ <subclass name="CashPayment" discriminator-value="CASH">
+ ...
+ </subclass>
+ <subclass name="ChequePayment" discriminator-value="CHEQUE">
+ ...
+ </subclass>
+</class>]]></programlisting>
+
+ <para>
+ ì´ë¤ 매í ë°©ëë¤ ì¤ ì´ë¤ ê²ì ëí´, ë£¨í¸ <literal>Payment</literal> í´ëì¤ì ëí íëì ë¤íì± ì°ê´ì
+ <literal><many-to-one></literal>ì ì¬ì©íì¬ ë§¤íëë¤.
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="payment" column="PAYMENT_ID" class="Payment"/>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="inheritance-tableperconcrete" revision="2">
+ <title>Table per concrete class</title>
+
+ <para>
+ ì°ë¦¬ê° table per concrete class ë°©ë 매íì ëí´ ì·¨í ì ìë ë ê°ì§ ë°©ë²ë¤ì´ ì¡´ì¬íë¤. 첫 ë²ì§¸ë
+ <literal><union-subclass></literal>를 ì¬ì©íë ê²ì´ë¤.
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="sequence"/>
+ </id>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <union-subclass name="CreditCardPayment" table="CREDIT_PAYMENT">
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </union-subclass>
+ <union-subclass name="CashPayment" table="CASH_PAYMENT">
+ ...
+ </union-subclass>
+ <union-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
+ ...
+ </union-subclass>
+</class>]]></programlisting>
+
+ <para>
+ ì¸ ê°ì í
ì´ë¸ë¤ì´ ìí¼í´ëì¤ë¤ì ëí´ ìë°ëë¤. ê°ê°ì í
ì´ë¸ì ììë íë¡í¼í°ë¤ì í¬í¨íì¬, ê·¸ í´ëì¤ì 모ë íë¡í¼í°ë¤ì ëí 컬ë¼ë¤ì ì ìíë¤.
+ </para>
+
+ <para>
+ ì´ ì ê·¼ë²ì ì ì½ì ë§ì¼ íëì íë¡í¼í°ê° ìí¼í´ëì¤ ìì¼ë¡ 매íë ê²½ì°, ê·¸ ì»¬ë¼ ì´ë¦ì´ 모ë ìë¸í´ëì¤ í
ì´ë¸ë¤ ììì ê°ìì¼ íë¤ë
+ ì ì´ë¤.(ì¥ëì Hibernate ë°°í¬ë³¸ìì ì°ë¦¬ë ì´ ì ì½ì í ìë ìë¤.) identity ìì±ê¸° ë°©ëë union ìë¸í´ëì¤ ìììì íì©ëì§
+ ìì¼ë©°, ì§ì íë¼ì´ë¨¸ë¦¬ í¤ ìëë íëì ê³ì¸µêµ¬ì¡°ì 모ë unioned ìë¸í´ëì¤ë¤ì ê°ë¡ì§ë¬ ê³µì ëì´ì¼ íë¤.
+ </para>
+
+ <para>
+ ë§ì¼ ë¹ì ì ìí¼í´ëì¤ê° abstractì¼ ê²½ì°ì, ê·¸ê²ì <literal>abstract="true"</literal>ë¡ ë§¤ííë¼.
+ ë¬¼ë¡ ë§ì¼ ê·¸ê²ì´ abstractê° ìë ê²½ì°, ì¶ê°ì ì¸ í
ì´ë¸(ìì ìì ììë ëí´í¸ë¡ <literal>PAYMENT</literal>)ì´
+ ìí¼í´ëì¤ì ì¸ì¤í´ì¤ë¤ì ìì íëë° íìíë¤.
+ </para>
+
+ </sect2>
+
+ <sect2 id="inheritance-tableperconcreate-polymorphism">
+ <title>í¨ì¶ì ì¸ ë¤íì±ì ì¬ì©íë, table per concrete class</title>
+
+ <para>
+ ëìì ì¸ ì ê·¼ë²ì í¨ì¶ì ì¸ ë¤íì±ì ì¬ì©íë ê²ì´ë¤:
+ </para>
+
+ <programlisting><![CDATA[<class name="CreditCardPayment" table="CREDIT_PAYMENT">
+ <id name="id" type="long" column="CREDIT_PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="amount" column="CREDIT_AMOUNT"/>
+ ...
+</class>
+
+<class name="CashPayment" table="CASH_PAYMENT">
+ <id name="id" type="long" column="CASH_PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="amount" column="CASH_AMOUNT"/>
+ ...
+</class>
+
+<class name="ChequePayment" table="CHEQUE_PAYMENT">
+ <id name="id" type="long" column="CHEQUE_PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="amount" column="CHEQUE_AMOUNT"/>
+ ...
+</class>]]></programlisting>
+
+ <para>
+ ì´ë ê³³ììë ì°ë¦¬ê° ëª
ìì ì¼ë¡ <literal>Payment</literal> ì¸í°íì´ì¤ë¥¼ ì¸ê¸íì§ ììì 주목íë¼.
+ ëí <literal>Payment</literal>ì íë¡í¼í°ë¤ì´ ìë¸í´ëì¤ë¤ ê°ê°ìì 매íëë¤ë ì ì 주목íë¼.
+ ë§ì¼ ë¹ì ì´ ì¤ë³µì í¼íê³ ì ìíë¤ë©´, XML ìí°í°ë¤ì ì¬ì©íë ê²ì ê³ ë ¤íë¼(ì를 ë¤ì´ 매íìì
+ <literal>DOCTYPE</literal> ì ì¸ê³¼ <literal>&allproperties;</literal>ìì
+ <literal>[ <!ENTITY allproperties SYSTEM "allproperties.xml"> ]</literal>).
+ </para>
+
+ <para>
+ ì´ ì ê·¼ë²ì ë¨ì ì ë¤íì± ì§ìë¤ì ìíí ë Hibernateê° ìì±ë SQl <literal>UNION</literal>ë¤ì ìì±ìí¤ë
+ ìëë¤ë ì ì´ë¤.
+ </para>
+
+ <para>
+ ì´ ë§¤í ë°©ëì ê²½ì°, <literal>Payment</literal>ì ëí íëì ë¤íì± ì°ê´ì ëê° <literal><any></literal>를
+ ì¬ì©íì¬ ë§¤íëë¤.
+ </para>
+
+ <programlisting><![CDATA[<any name="payment" meta-type="string" id-type="long">
+ <meta-value value="CREDIT" class="CreditCardPayment"/>
+ <meta-value value="CASH" class="CashPayment"/>
+ <meta-value value="CHEQUE" class="ChequePayment"/>
+ <column name="PAYMENT_CLASS"/>
+ <column name="PAYMENT_ID"/>
+</any>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="inheritace-mixingpolymorphism">
+ <title>í¨ì¶ì ì¸ ë¤íì±ì ë¤ë¥¸ ìì 매íë¤ê³¼ í¼í©í기</title>
+
+ <para>
+ ì´ ë§¤íì ëí´ ì£¼ëª©í íë ì´ìì ê²ì´ ì¡´ì¬íë¤. ìë¸í´ëì¤ë¤ì´ ê·¸ê²ë¤ ìì ì<literal><class></literal> ìì ë´ì
+ ê°ê° 매íëë¯ë¡(ê·¸ë¦¬ê³ <literal>Payment</literal>ê° ë¨ì§ ì¸í°íì´ì¤ì´ë¯ë¡), ìë¸í´ëì¤ë¤ ê°ê°ì ì½ê² ë ë¤ë¥¸ ìì ê³ì¸µêµ¬ì¡°ì
+ ë¶ë¶ì¼ ì ìë¤! (ê·¸ë¦¬ê³ ë¹ì ì <literal>Payment</literal> ì¸í°íì´ì¤ì ëí´ ì¬ì í ë¤íì± ì§ìë¤ì ì¬ì©í ì ìë¤.)
+ </para>
+
+ <programlisting><![CDATA[<class name="CreditCardPayment" table="CREDIT_PAYMENT">
+ <id name="id" type="long" column="CREDIT_PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <discriminator column="CREDIT_CARD" type="string"/>
+ <property name="amount" column="CREDIT_AMOUNT"/>
+ ...
+ <subclass name="MasterCardPayment" discriminator-value="MDC"/>
+ <subclass name="VisaPayment" discriminator-value="VISA"/>
+</class>
+
+<class name="NonelectronicTransaction" table="NONELECTRONIC_TXN">
+ <id name="id" type="long" column="TXN_ID">
+ <generator class="native"/>
+ </id>
+ ...
+ <joined-subclass name="CashPayment" table="CASH_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ <property name="amount" column="CASH_AMOUNT"/>
+ ...
+ </joined-subclass>
+ <joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ <property name="amount" column="CHEQUE_AMOUNT"/>
+ ...
+ </joined-subclass>
+</class>]]></programlisting>
+
+ <para>
+ ë¤ì íë², ì°ë¦¬ë <literal>Payment</literal>를 ëª
ìì ì¼ë¡ ì¸ê¸íì§ ìëë¤. ë§ì¼ ì°ë¦¬ê° <literal>Payment</literal>
+ ì¸í°íì´ì¤ì ëí´ íëì ì§ì를 ì¤íí ê²½ì°-ì를 ë¤ì´, from Payment-, Hibernateë <literal>CreditCardPayment</literal>
+ (ì ê·¸ê²ì ìë¸í´ëì¤ë¤, ìëíë©´ ê·¸ê²ë¤ ëí <literal>Payment</literal>를 구ííë¯ë¡), <literal>CashPayment</literal>
+ ê·¸ë¦¬ê³ <literal>ChequePayment</literal> ì¸ì¤í´ì¤ë¤ì ìëì ì¼ë¡ ë°íí ê²ì´ì§ë§
+ <literal>NonelectronicTransaction</literal>ì ì¸ì¤í´ì¤ë¤ì ë°ííì§ ìëë¤.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="inheritance-limitations">
+ <title>ì ì½ë¤</title>
+
+ <para>
+ table per concrete-class 매í ë°©ëì ëí "í¨ì¶ì ì¸ ë¤íì±" ì ê·¼ë²ìë ì´ë¤ ì ì½ë¤ì´ ì¡´ì¬íë¤.
+ <literal><union-subclass></literal> 매íë¤ì ëí´ìë ë¤ì ë ì íì ì¸ ì ì½ë¤ì´ ì¡´ì¬íë¤:
+ </para>
+
+ <para>
+ ë¤ì íë Hibernateìì table per concrete-class 매íë¤ì ëí ì ì½ë¤, ê·¸ë¦¬ê³ í¨ì¶ì ì¸ ë¤íì±ì ëí ì ì½ë¤ì ë³´ì¬ì¤ë¤.
+ </para>
+
+ <table frame="topbot">
+ <title>ìì 매íë¤ì í¹ì§ë¤</title>
+ <tgroup cols='8' align='left' colsep='1' rowsep='1'>
+ <colspec colname='c1' colwidth="1*"/>
+ <colspec colname='c2' colwidth="1*"/>
+ <colspec colname='c3' colwidth="1*"/>
+ <colspec colname='c4' colwidth="1*"/>
+ <colspec colname='c5' colwidth="1*"/>
+ <colspec colname='c6' colwidth="1*"/>
+ <colspec colname='c7' colwidth="1*"/>
+ <colspec colname='c8' colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>ìì ë°©ë</entry>
+ <entry>ë¤íì± ë¤ëì¼</entry>
+ <entry>ë¤íì± ì¼ëì¼</entry>
+ <entry>ë¤íì± ì¼ëë¤</entry>
+ <entry>ë¤íì± ë¤ëë¤</entry>
+ <entry>ë¤íì± <literal>load()/get()</literal></entry>
+ <entry>ë¤íì± ì§ìë¤</entry>
+ <entry>ë¤íì± ì¡°ì¸ë¤</entry>
+ <entry>Outer ì¡°ì¸ íì¹</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>table per class-hierarchy</entry>
+ <entry><literal><many-to-one></literal></entry>
+ <entry><literal><one-to-one></literal></entry>
+ <entry><literal><one-to-many></literal></entry>
+ <entry><literal><many-to-many></literal></entry>
+ <entry><literal>s.get(Payment.class, id)</literal></entry>
+ <entry><literal>from Payment p</literal></entry>
+ <entry><literal>from Order o join o.payment p</literal></entry>
+ <entry><emphasis>ì§ìë¨</emphasis></entry>
+ </row>
+ <row>
+ <entry>table per subclass</entry>
+ <entry><literal><many-to-one></literal></entry>
+ <entry><literal><one-to-one></literal></entry>
+ <entry><literal><one-to-many></literal></entry>
+ <entry><literal><many-to-many></literal></entry>
+ <entry><literal>s.get(Payment.class, id)</literal></entry>
+ <entry><literal>from Payment p</literal></entry>
+ <entry><literal>from Order o join o.payment p</literal></entry>
+ <entry><emphasis>ì§ìë¨</emphasis></entry>
+ </row>
+ <row>
+ <entry>table per concrete-class (union-subclass)</entry>
+ <entry><literal><many-to-one></literal></entry>
+ <entry><literal><one-to-one></literal></entry>
+ <entry><literal><one-to-many></literal> (for <literal>inverse="true"</literal> only)</entry>
+ <entry><literal><many-to-many></literal></entry>
+ <entry><literal>s.get(Payment.class, id)</literal></entry>
+ <entry><literal>from Payment p</literal></entry>
+ <entry><literal>from Order o join o.payment p</literal></entry>
+ <entry><emphasis>ì§ìë¨</emphasis></entry>
+ </row>
+ <row>
+ <entry>table per concrete class (implicit polymorphism)</entry>
+ <entry><literal><any></literal></entry>
+ <entry><emphasis>ì§ìëì§ ìì</emphasis></entry>
+ <entry><emphasis>ì§ìëì§ ìì</emphasis></entry>
+ <entry><literal><many-to-any></literal></entry>
+ <entry><literal>s.createCriteria(Payment.class).add( Restrictions.idEq(id) ).uniqueResult()</literal></entry>
+ <entry><literal>from Payment p</literal></entry>
+ <entry><emphasis>ì§ìëì§ ìì</emphasis></entry>
+ <entry><emphasis>ì§ìëì§ ìì</emphasis></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect1>
+
+</chapter>
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/performance.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/performance.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/performance.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1293 @@
+<chapter id="performance">
+ <title>í¼í¬ë¨¼ì¤ ê°ì í기</title>
+
+ <sect1 id="performance-fetching" revision="2">
+ <title>íì¹ ë°©ëë¤</title>
+
+ <para>
+ <emphasis>íì¹ ë°©ë</emphasis>ë ì´í리ì¼ì´ì
ì´ ì°ê´ì ë¤ë¹ê²ì´í¸í íìê° ìì ë Hibernateê° ì°ê´ë ê°ì²´ë¤ì ê²ìíëë°
+ ì¬ì©íê² ë ë°©ëì´ë¤.íì¹ ë°©ëë¤ì O/R 매í ë©íë°ì´í° ë´ìì ì ì¸ë ì ìê±°ë íëì í¹ì HQL ëë <literal>Criteria</literal>
+ ì§ìì ìí´ ì¤ë²ë¼ì´ë ë ìë ìë¤.
+ </para>
+
+ <para>
+ Hibernate3ë ë¤ì íì¹ ë°©ëë¤ì ì ìíë¤:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Join íì¹</emphasis> - Hibernateë <literal>OUTER JOIN</literal>ì ì¬ì©íì¬ ì°ê´ë ì¸ì¤í´ì¤
+ ëë ëì¼í <literal>SELECT</literal> ë´ìì ì½ë ì
ì ê²ìíë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Select íì¹</emphasis> - ë ë²ì§¸ <literal>SELECT</literal>ë ì°ê³¼ë ìí°í° ëë ì½ë ì
ì ê²ìíëë°
+ ì¬ì©ëë¤. ë¹ì ì´ <literal>lazy="false"</literal>를 ì§ì í¨ì¼ë¡ì¨ ëª
ìì ì¼ë¡ lazy íì¹ì ì¬ì© ë¶ê°ë¥íê² íì§ ìë
+ í, ì´ ë ë²ì§¸ selectë ë¹ì ì´ ê·¸ ì°ê´ì ì¤ì ë¡ ì¡ì¸ì¤í ë ì¤ì§ ì¤íë ê²ì´ë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Subselect íì¹</emphasis> - ë ë²ì§¸ <literal>SELECT</literal>ë ì´ì ì§ì ëë íì¹ìì ê²ìë
+ 모ë ìí°í°ë¤ì ëí´ ì°ê´ë ì½ë ì
ë¤ì ê²ìíëë° ì¬ì©ëë¤. ë¹ì ì´ <literal>lazy="false"</literal>를 ì§ì íì¬
+ ëª
ìì ì¼ë¡ lazy íì¹ì ì¬ì© ë¶ê°ë¥íê² íì§ ìë í, ì´ ë ë²ì§¸ selectë ë¹ì ì´ ì¤ì ë¡ ê·¸ ì°ê´ì ì ê·¼í ë ì¤ì§ ì¤íë
+ ê²ì´ë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Batch íì¹</emphasis> - select íì¹ì ìí ìµì í ë°©ë - Hibernateë íë¼ì´ë¨¸ë¦¬ í¤ë¤ì´ë foreign
+ í¤ë¤ì 리ì¤í¸ë¥¼ ì§ì í¨ì¼ë¡ì¨ íëì<literal>SELECT</literal> ë´ìì ìí°í° ì¸ì¤í´ì¤ë¤ì´ë ì½ë ì
ë¤ì ëí batch를
+ ê²ìíë¤.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Hibernateë ëí ë¤ì ì¬ì´ë¥¼ êµ¬ë³ ì§ëë¤:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>ì¦ê°ì ì¸ íì¹</emphasis> - ìì ìê° ë¡ëë ë, ì°ê´, ì½ë ì
ëë ìì±ì´ ì¦ì íì¹ëë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Lazy ì½ë ì
íì¹</emphasis> - ì´í리ì¼ì´ì
ì´ ê·¸ ì½ë ì
ì ëí´ íëì ì¤í¼ë ì´ì
ì í¸ì¶í ë
+ ì½ë ì
ì´ íì¹ëë¤.(ì´ê²ì ì½ë ì
ë¤ì ëí´ ëí´í¸ì´ë¤.)
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>"Extra-lazy" ì½ë ì
íì¹</emphasis> - ì½ë ì
ì ê°ë³ ììë¤ì íìí ë
+ ë°ì´í°ë² ì´ì¤ë¡ë¶í° ì ê·¼ëë¤. Hibernateë ì ëì ì¼ë¡ íìíì§ ìì í ì ì²´ ì½ë ì
ì ë©ëª¨ë¦¬ ë´ë¡
+ íì¹íë ¤ê³ ìëíì§ ìëë¤(ë§¤ì° í° ì½ë ì
ì ì í©í¨)
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>íë½ì íì¹</emphasis> - ìë³ì getterê° ìë ë¤ë¥¸ ë©ìëê° ì°ê´ë ê°ì²´ì ëí´ í¸ì¶ë ë
+ ë¨ì¼ ê° ì°ê´ì´ íì¹ëë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>"No-proxy" íì¹</emphasis> - ì¸ì¤í´ì¤ ë³ìê° ì ê·¼ë ë ë¨ì¼ ê° ì°ê´ì´ íì¹ëë¤.
+ íë½ì íì¹ê³¼ ë¹êµí ë, ì´ ì ê·¼ë²ì ë¤ì ë lazyíì§ë§(ê·¸ ì°ê´ì ì¬ì§ì´ ì ì¼íê² ìë³ìê° ì ê·¼ë ëìë
+ íì¹ëë¤)ë³´ë¤ í¬ëª
íë¤. ìëíë©´ íë½ìë ì´í리ì¼ì´ì
ì ê°ìì ì´ì§ ì기 ë문ì´ë¤. ì´ ì ê·¼ë²ì ë¹ë ì
+ ë°ì´í¸ì½ë ìë¨ì íìë¡ íë©° ëë¬¼ê² íìíë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Lazy ìì± íì¹</emphasis> - ì¸ì¤í´ì¤ ë³ìê° ì ê·¼ë ë ìì± ëë ë¨ì¼ ê° ì°ê´ì´ íì¹ëë¤
+ ì´ ì ê·¼ë²ì ë¹ëì ë°ì´í¸ì½ë ìë¨ì íìë¡ íë©° ëë¬¼ê² íìíë¤.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ì°ë¦¬ë ì¬ê¸°ì ë ê°ì ì§êµíë ê°ë
ë¤ì ê°ëë¤: ì°ê´ì´ íì¹ë <emphasis>ë</emphasis>, ê·¸ë¦¬ê³ ê·¸ê²ì´ íì¹ëë
+ <emphasis>ë°©ë²</emphasis>(ì¬ì©ëë SQL). ê·¸ê²ë¤ì í¼ëíì§ ë§ë¼! ì°ë¦¬ë í¼í¬ë¨¼ì¤ë¥¼ íí
íëë°
+ <literal>íì¹</literal>를 ì¬ì©íë¤. ì°ë¦¬ë í¹ì í´ëì¤ì ì´ë¤ detached ì¸ì¤í´ì¤ ë´ìì íì ì´ì© ê°ë¥í
+ ë°ì´í°ê° 무ìì¸ì§ì ëí ê³ì½ì ì ìíëë° <literal>lazy</literal>를 ì¬ì©í ì ìë¤.
+ </para>
+
+ <sect2 id="performance-fetching-lazy">
+ <title>lazy ì°ê´ë¤ë¡ ìì
í기</title>
+
+ <para>
+ ëí´í¸ë¡ Hibernate3ë ì½ë ì
ë¤ì ëí´ lazy select íì¹ì ì¬ì©íê³ , ë¨ì¼ ê° ì°ê´ë¤ì ëí´ lazy
+ íë½ì íì¹ì ì¬ì©íë¤. ì´ë¤ ëí´í¸ë¤ì ê±°ì 모ë ì´í리ì¼ì´ì
ë¤ìì ê±°ì 모ë ì°ê´ë¤ì ëí´ ì ì미íë¤.
+ </para>
+
+ <para>
+ <emphasis>ë
¸í¸:</emphasis> ë§ì¼ ë¹ì ì´ <literal>hibernate.default_batch_fetch_size</literal>를 ì¤ì íë
+ ê²½ì°, Hibernateë lazy íì¹ì ìí batch íì¹ ìµì í를 ì¬ì©í ê²ì´ë¤(ì´ ìµì íë ëí ë ë§ì 과립ìì ë 벨ìì ì´ì© ê°ë¥í
+ ì ìë¤).
+ </para>
+
+ <para>
+ íì§ë§, lazy íì¹ì ë¹ì ì´ ìê³ ìì´ì¼ íë í ê°ì§ 문ì 를 ì 기íë¤. ì´ë ¤ì§ Hibernate ì¸ì
컨í
ì¤í¸ ì¸ë¶ìì lazy ì°ê´ì
+ ëí ì ê·¼ì ìì¸ìí©ì¼ë¡ ê·ê²°ë ê²ì´ë¤. ì를 ë¤ë©´ :
+ </para>
+
+ <programlisting><![CDATA[s = sessions.openSession();
+Transaction tx = s.beginTransaction();
+
+User u = (User) s.createQuery("from User u where u.name=:userName")
+ .setString("userName", userName).uniqueResult();
+Map permissions = u.getPermissions();
+
+tx.commit();
+s.close();
+
+Integer accessLevel = (Integer) permissions.get("accounts"); // Error!]]></programlisting>
+
+ <para>
+ <literal>Session</literal>ì´ ë«íì ë permissions ì½ë ì
ì´ ì´ê¸°í ëì§ ììì¼ë¯ë¡, ê·¸ ì½ë ì
ì ê·¸ê²ì ìí를 ë¡ëìí¬
+ ìê° ìì ê²ì´ë¤. <emphasis>Hibernate ë detached ê°ì²´ë¤ì ëí lazy ì´ê¸°í를 ì§ìíì§ ìëë¤.</emphasis> ì ì ì
+ ì½ë ì
ì¼ë¡ë¶í° ì½ì´ë¤ì´ë ì½ë를 ì»¤ë° ë°ë¡ ì§ì ì¼ë¡ ì´ëìí¤ë ê²ì´ë¤.
+ </para>
+
+ <para>
+ ë¤ë¥¸ ë°©ë²ì¼ë¡ ì°ê´ 매íì ëí´ <literal>lazy="false"</literal>를 ì§ì í¨ì¼ë¡ì¨, non-lazy ì½ë ì
ëë
+ non-lazy ì°ê´ì ì¬ì©í ì ìë¤. íì§ë§ lazy ì´ê¸°íë ê±°ì 모ë ì½ë ì
ë¤ê³¼ ì°ê´ë¤ì ëí´ ì¬ì©ëëë¡ ê³ ìëì´ ìë¤.
+ ë§ì¼ ë¹ì ì´ ë¹ì ì ê°ì²´ 모í ë´ì ë무 ë§ì non-lazy ì°ê´ë¤ì ì ìí ê²½ì°, Hibernateë 모ë í¸ëìì
ìì ì ì²´
+ ë°ì´í°ë² ì´ì¤ë¥¼ ë©ëª¨ë¦¬ ìì¼ë¡ íì¹íë íìì±ì ëë´ê² ë ê²ì´ë¤!
+ </para>
+
+ <para>
+ ë¤ë¥¸ íí¸ì¼ë¡, ì°ë¦¬ë í¹ì í¸ëìì
ë´ìì select íì¹ ëì ì (ê³ ì íê² non-lazyì¸) join íì¹ì ì íí기를 ì주 ìíë¤.
+ ì°ë¦¬ë ì´ì íì¹ ë°©ë를 ë§ì¶¤í ìí¤ë ë°©ë²ì ìê² ë ê²ì´ë¤. Hibernate3ìì, íì¹ ë°©ë를 ì ííë ë©ì»¤ëì¦ì ë¨ì¼ ê° ì°ê´ë¤ê³¼
+ ì½ë ì
ë¤ì ëí´ ëì¼íë¤.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-fetching-custom" revision="4">
+ <title>íì¹ ë°©ëë¤ì íëí기</title>
+
+ <para>
+ select íì¹(ëí´í¸)ì N+1 selects 문ì ì ë¤ì ë§¤ì° ì·¨ì½í´ì, ì°ë¦¬ë 매í 문ììì join íì¹ì ì¬ì© ê°ë¥íê² í기를 ìí
+ ìë ìë¤:
+ </para>
+
+ <programlisting><![CDATA[<set name="permissions"
+ fetch="join">
+ <key column="userId"/>
+ <one-to-many class="Permission"/>
+</set]]></programlisting>
+
+ <programlisting><![CDATA[<many-to-one name="mother" class="Cat" fetch="join"/>]]></programlisting>
+
+ <para>
+ 매í 문ì ë´ì ì ìë <literal>fetch</literal> ë°©ëë ë¤ìì ìí¥ì ì¤ë¤:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>get()</literal> ëë <literal>load()</literal>를 íµí ê²ì
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ì°ê´ì´ ë¤ë¹ê²ì´í¸ë ë í¨ì¶ì ì¼ë¡ ë°ìíë ê²ì
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>Criteria</literal> ì§ìë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>subselect</literal> íì¹ì´ ì¬ì©ë ê²½ì°ì HQL ì§ìë¤
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ë¹ì ì´ ì¬ì©íë íì¹ ë°©ëê° ë¬´ìì¸ê°ì ìê´ìì´, ì ìë ë¹-lazy ê·¸ëíê° ë©ëª¨ë¦¬ ë´ë¡ ë¡ëëë ê²ì´ ë³´ì¥ëë¤.
+ ì´ê²ì íëì í¹ë³í HQL ì§ì를 ì¤íìí¤ëë° ì¬ì©ëë ëªëª ì¦ìì ì¸ selectë¤ë¡ ê·ê²°ë ì ììì ë
¸í¸íë¼.
+ </para>
+
+ <para>
+ ëê°, ì°ë¦¬ë íì¹ì ë§ì¶¤í ìí¤ëë° ë§¤í 문ì를 ì¬ì©íì§ ìëë¤. ëì ì, ì°ë¦¬ë ëí´í¸ í¹ì§ì ì ì§íê³ , HQLìì
+ <literal>left join fetch</literal>를 ì¬ì©íì¬, í¹ì í¸ëìì
ì ëí´ ê·¸ê²ì ì¤ë²ë¼ì´ë ìí¨ë¤. ì´ê²ì
+ outer joinì ì¬ì©íì¬ ì²« ë²ì§¸ selectìì ì´ê¸°ì ê·¸ ì°ê´ì eagerly íì¹ìí¬ ê²ì Hibernateìê² ìë ¤ì¤ë¤.
+ <literal>Criteria</literal> query APIìì, ì°ë¦¬ë <literal>setFetchMode(FetchMode.JOIN)</literal>ì
+ ì¬ì©íë¤.
+ </para>
+
+ <para>
+ ë§ì¼ ë¹ì ì´ <literal>get()</literal> ëë <literal>load()</literal>ì ìí´ ì¬ì©ë íì¹ ë°©ë를 ë³ê²½ìí¬ ì ì기를
+ ë¹ì ì´ ìíë¤ê³ ëë ê²½ì°, ë¨ìíê² <literal>Criteria</literal> ì§ì를 ì¬ì©íë¼. ì를 ë¤ë©´:
+ </para>
+
+ <programlisting><![CDATA[User user = (User) session.createCriteria(User.class)
+ .setFetchMode("permissions", FetchMode.JOIN)
+ .add( Restrictions.idEq(userId) )
+ .uniqueResult();]]></programlisting>
+
+ <para>
+ (ì´ê²ì ëªëª ORM ì루ì
ë¤ì´ "íì¹ ê³í"ì´ë¼ê³ ë¶ë¥´ë ê²ì ëí Hibernateì ë±ê°ë¬¼ì´ë¤.)
+ </para>
+
+ <para>
+ N+1 ê°ì selectë¤ì ê°ì§ 문ì ì ë¤ì í¼íë ìì í ë¤ë¥¸ ë°©ë²ì second-level ìºì를 ì¬ì©íë ê²ì´ë¤.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-fetching-proxies" revision="2">
+ <title>Single-ended ì°ê´ íë½ì</title>
+
+ <para>
+ ì½ë ì
ë¤ì ëí Lazy íì¹ì ìì ì½ë ì
ë¤ì ëí Hibernate ìì ì 구íì ì¬ì©íì¬ êµ¬íëë¤. íì§ë§ ë¤ë¥¸ ë©ì»¤ëì¦ì
+ single-ended ì°ê´ë¤ìì lazy í¹ì§ì íìíë¤. ì°ê´ì ëì ìí°í°ë íë½ì ëì´ì¼ íë¤. Hibernateë
+ (íë¥í CGLIB ë¼ì´ë¸ë¬ë¦¬ë¥¼ íµí´) ë°íì ë°ì´í¸ì½ë ì¦ì§ì ì¬ì©íì¬ ìì ê°ì²´ë¤ì ëí lazy ì´ê¸°í íë½ìë¤ì 구ííë¤.
+ </para>
+
+ <para>
+ ëí´í¸ë¡, Hibernate3ë 모ë ìì í´ëì¤ë¤ì ëí´ (ìì ìì) íë½ìë¤ì ìì±ìí¤ê³ <literal>many-to-one</literal>
+ ì°ê´ê³¼ <literal>one-to-one</literal> ì°ê´ì ëí´ lazy íì¹ì ì´ì© ê°ë¥íê² íëë° ê·¸ê²ë¤ì ì¬ì©íë¤.
+ </para>
+
+ <para>
+ 매í íì¼ì ê·¸ í´ëì¤ì ëí íë½ì ì¸í°íì´ì¤ë¡ì ì¬ì©í , <literal>proxy</literal> ìì±ì ê°ì§, ì¸í°íì´ì¤ë¥¼ ì ì¸í
+ ìë ìë¤. ëí´í¸ë¡ Hibernateë ê·¸ í´ëì¤ì ìë¸í´ëì¤ë¥¼ ì¬ì©íë¤. <emphasis>íë½ìë í´ëì¤ë ìµìíì í¨í¤ì§ ê°ìì±
+ (visibility)ì ê°ì§ ëí´í¸ ìì±ì를 구íí´ì¼ í¨ì ë
¸í¸íë¼. ì°ë¦¬ë 모ë ìì í´ëì¤ë¤ì ëí´ ì´ ìì±ì를 ê¶ì¥íë¤!</emphasis>
+ </para>
+
+ <para>
+ ë¤íì± í´ëì¤ë¤ì ëí´ ì´ ì ê·¼ë²ì íì¥í ë ììí´ì¼ íë ëªëª ëì²í¨ë¤ì´ ì¡´ì¬íë¤. ì를 ë¤ë©´.
+ </para>
+
+ <programlisting><![CDATA[<class name="Cat" proxy="Cat">
+ ......
+ <subclass name="DomesticCat">
+ .....
+ </subclass>
+</class>]]></programlisting>
+
+ <para>
+ 첫 ë²ì§¸ë¡, ì¬ì§ì´ 기본 ì¸ì¤í´ì¤ê° <literal>DomesticCat</literal>ì ì¸ì¤í´ì¤ì¸ ê²½ì°ì¡°ì°¨ë,
+ <literal>Cat</literal>ì ì¸ì¤í´ì¤ë¤ì ê²°ì½ <literal>DomesticCat</literal>ì¼ë¡ íì
ìºì¤í¸ê° ê°ë¥íì§ ìì ê²ì´ë¤:
+ </para>
+
+ <programlisting><![CDATA[Cat cat = (Cat) session.load(Cat.class, id); // instantiate a proxy (does not hit the db)
+if ( cat.isDomesticCat() ) { // hit the db to initialize the proxy
+ DomesticCat dc = (DomesticCat) cat; // Error!
+ ....
+}]]></programlisting>
+
+ <para>
+ ëë²ì§¸ë¡, íë½ì <literal>==</literal>를 í기í ê°ë¥ì±ì´ ìë¤.
+ </para>
+
+ <programlisting><![CDATA[Cat cat = (Cat) session.load(Cat.class, id); // instantiate a Cat proxy
+DomesticCat dc =
+ (DomesticCat) session.load(DomesticCat.class, id); // acquire new DomesticCat proxy!
+System.out.println(cat==dc); // false]]></programlisting>
+
+ <para>
+ íì§ë§, ê·¸ ê²½ì°ë ë³´ì´ë ë§í¼ ê·¸ë ê² ëìì§ë ìë¤. ì¬ì§ì´ ì°ë¦¬ê° ì´ì ë¤ë¥¸ íë½ì ê°ì²´ë¤ì ëí ë ê°ì 참조를 ê°ì§ì§ë¼ë,
+ 기본 ì¸ì¤í´ì¤ë ì¬ì í ëì¼í ê°ì²´ë¤ì¼ ê²ì´ë¤:
+ </para>
+
+ <programlisting><![CDATA[cat.setWeight(11.0); // hit the db to initialize the proxy
+System.out.println( dc.getWeight() ); // 11.0]]></programlisting>
+
+ <para>
+ ì¸ë²ì§¸ë¡, ë¹ì ì <literal>final</literal> í´ëì¤ ëë ììì <literal>final</literal> ë©ìëë¤ì ê°ì§ í´ëì¤ì
+ ëí´ CGLIB íë½ì를 ì¬ì©íì§ ìì ì ìë¤.
+ </para>
+
+ <para>
+ ë§ì§ë§ì¼ë¡, ë§ì¼ ë¹ì ì ìì ê°ì²´ê° ì´ê¸°í ìì ì´ë¤ 리ìì¤ë¤ì íìë¡ í ê²½ì°(ì를 ë¤ì´, initializerë¤ ëë ëí´í¸ ìì±ì
+ ë´ìì), ê·¸ë ê·¸ë¤ ë¦¬ìì¤ë¤ì´ ëí íë½ìì ìí´ íëë ê²ì´ë¤. íë½ì í´ëì¤ë ìì í´ëì¤ì ëí ì¤ì ìë¸í´ëì¤ì´ë¤.
+ </para>
+
+ <para>
+ ì´ë¤ 문ì ì ë¤ì 모ë ìë°ì ë¨ì¼ ìì 모íì 기본ì ì¸ ì ì½ ë문ì´ë¤. ë§ì¼ ë¹ì ì´ ì´ë¤ 문ì ì ë¤ì í¼íê³ ì ìí ê²½ì° ë¹ì ì ìì
+ í´ëì¤ë¤ì ê°ê° ê·¸ê²ì ë¹ì§ëì¤ ë©ìëë¤ì ì ì¸íë ì¸í°íì´ì¤ë¥¼ 구íí´ì¼ íë¤. ë¹ì ì 매í íì¼ ìì ì´ë¤ ì¸í°íì´ì¤ë¤ì
+ ì§ì í´ì¼ íë¤. ì를 ë¤ë©´.
+ </para>
+
+ <programlisting><![CDATA[<class name="CatImpl" proxy="Cat">
+ ......
+ <subclass name="DomesticCatImpl" proxy="DomesticCat">
+ .....
+ </subclass>
+</class>]]></programlisting>
+
+ <para>
+ ì¬ê¸°ì <literal>CatImpl</literal>ì <literal>Cat</literal> ì¸í°íì´ì¤ë¥¼ 구ííê³ <literal>DomesticCatImpl</literal>ì
+ <literal>DomesticCat</literal> ì¸í°íì´ì¤ë¥¼ 구ííë¤. ê·¸ë <literal>Cat</literal>ê³¼ <literal>DomesticCat</literal>ì
+ ì¸ì¤í´ì¤ë¤ì ëí íë½ìë¤ì <literal>load()</literal> ëë <literal>iterate()</literal>ì ìí´ ë°íë ì ìë¤.
+ (<literal>list()</literal>ê° ëê° íë½ìë¤ì ë°ííì§ ììì ë
¸í¸íë¼.)
+ </para>
+
+ <programlisting><![CDATA[Cat cat = (Cat) session.load(CatImpl.class, catid);
+Iterator iter = session.iterate("from CatImpl as cat where cat.name='fritz'");
+Cat fritz = (Cat) iter.next();]]></programlisting>
+
+ <para>
+ ê´ê³ë¤ì ëí lazy ì´ê¸°í ëë¤. ì´ê²ì ë¹ì ì´ ììì íë¡í¼í°ë¤ì <literal>CatImpl</literal> íì
ì´ ìë
+ <literal>Cat</literal> íì
ì¼ë¡ ì ì¸í´ì¼ í¨ì ì미íë¤.
+ </para>
+
+ <para>
+ ì´ë¤ ì¤í¼ë ì´ì
ë¤ì íë½ì ì´ê¸°í를 íìë¡ íì§ <emphasis>ìëë¤</emphasis>
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>equals()</literal>, ë§ì¼ ìì í´ëì¤ê° <literal>equals()</literal>를 ì¤ë²ë¼ì´ë ìí¤ì§ ìë ê²½ì°
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>hashCode()</literal>, ë§ì¼ ìì í´ëì¤ê°<literal>hashCode()</literal>를 ì¤ë²ë¼ì´ë ìí¤ì§
+ ìë ê²½ì°
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ìë³ì getter ë©ìë
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Hibernateë <literal>equals()</literal> ëë <literal>hashCode()</literal>를 ì¤ë²ë¼ì´ë ìí¤ë ìì í´ëì¤ë¤ì
+ ê²ì¶í ê²ì´ë¤.
+ </para>
+
+ <para>
+ ëí´í¸ <literal>lazy="proxy"</literal> ëì ì <literal>lazy="no-proxy"</literal>를 ì ííì¬,
+ ì°ë¦¬ë íì
ìºì¤í
ê³¼ ì°ê´ë 문ì ì ë¤ì í¼í ì ìë¤. íì§ë§ ì°ë¦¬ë ë¹ë ì ë°ì´í¸ì½ë ìë¨ì íìë¡ í ê²ì´ê³ ,
+ 모ë ì°ì°ë¤ì ì¦ê°ì ì¸ íë½ì ì´ê¸°íë¡ ê·ê²°ë ê²ì´ë¤.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-fetching-initialization" revision="1">
+ <title>ì½ë ì
ë¤ê³¼ íë½ìë¤ì ì´ê¸°í ìí¤ê¸°</title>
+
+ <para>
+ ë§ì¼ ì´ê¸°í ëì§ ìì ì½ë ì
ì´ë íë½ìê° <literal>Session</literal> ììì ì¸ë¶ìì ì ê·¼ë ê²½ì°ì, ì를 ë¤ì´ ì½ë ì
ì
+ ìì íê±°ë íë½ìì ëí 참조를 ê°ì§ ìí°í°ê° detached ìíì ìì ë, <literal>LazyInitializationException</literal>ì´
+ Hibernateì ìí´ ëì ¸ì§ ê²ì´ë¤.
+ </para>
+
+ <para>
+ ëëë¡ ì°ë¦¬ë<literal>Session</literal>ì ë«ê¸° ì ì íë½ì ëë ì½ë ì
ì´ ì´ê¸°í ë¨ì íì¤í í íìê° ìë¤. ë¬¼ë¡ ì°ë¦¬ë
+ ì를 ë¤ì´ <literal>cat.getSex()</literal> ëë <literal>cat.getKittens().size()</literal>를 í¸ì¶íì¬ íì
+ ì´ê¸°í를 ê°ì ìí¬ ì ìë¤. ê·¸ë¬ë ê·¸ê²ì ì½ëì ë
ìë¤ìê²ë í¼ëì¤ë½ê³ ì¼ë°ì ì¸ ì½ëë¡ í¸ìì ì´ì§ ìë¤.
+ </para>
+
+ <para>
+ static ë©ìëë¤ <literal>Hibernate.initialize()</literal>ì <literal>Hibernate.isInitialized()</literal>ë
+ lazy ì´ê¸°í ë ì½ë ì
ë¤ì´ë íë½ìë¤ì ëí´ ìì
íë í¸ë¦¬í ë°©ë²ì ì´í리ì¼ì´ì
ì ì ê³µíë¤. <literal>Hibernate.initialize(cat)</literal>ì
+ ê·¸ê²ì <literal>Session</literal>ì´ ì¬ì í ì´ë ¤ì ¸ ìë í íë½ì <literal>cat</literal>ì ì´ê¸°í를 ê°ì í ê²ì´ë¤.
+ <literal>Hibernate.initialize( cat.getKittens())</literal>ë kittensì ì½ë ì
ì ëí´ ì ì¬í í¨ê³¼ë¥¼ ê°ëë¤.
+ </para>
+
+ <para>
+ ë ë¤ë¥¸ ìµì
ì 모ë íìí ì½ë ì
ë¤ê³¼ íë½ìë¤ì´ ë¡ëë기 ì ê¹ì§ <literal>Session</literal>ì ì´ë¦° ì±ë¡ ì ì§íë ê²ì´ë¤.
+ ëªëª ì´í리ì¼ì´ì
ìí¤í
ì²ë¤, í¹í Hibernate를 ì¬ì©íì¬ ë°ì´í°ì ì ê·¼íë ì½ë, ê·¸ë¦¬ê³ ë¤ë¥¸ ì´í리ì¼ì´ì
ê³ì¸µë¤ì´ë ë¤ë¥¸
+ 물리ì íë¡ì¸ì¤ë¤ ë´ìì ê·¸ê²ì ì¬ì©íë ì½ëìì, ê·¸ê²ì ì½ë ì
ì´ ì´ê¸°í ë ë <literal>Session</literal>ì´ ì´ë ¤ì ¸
+ ììì íì¤í íë 문ì ì¼ ì ìë¤. ì´ ìì ì ë¤ë£¨ë ë ê°ì§ 기본 ë°©ë²ë¤ì´ ì¡´ì¬íë¤:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ ì¹ ê¸°ë° ì´í리ì¼ì´ì
ìì, ìë¸ë¦¿ íí°ë ë·° ë ëë§ì´ ìë£ëë, ì¬ì©ì ìì²ì ë°ë¡ ëììë§ <literal>Session</literal>ì
+ ë«ëë° ì¬ì©ë ì ìë¤(<emphasis>Open Session in View</emphasis> í¨í´). ë¬¼ë¡ ì´ê²ì ë¹ì ì ì´í리ì¼ì´ì
+ ì¸íë¼ì¤í¸ëì²ì ìì¸ìí© ì²ë¦¬ì ì ì ì ê´í ë¬´ê±°ì´ ì구를 ë¶ê³¼íë¤.
+ ë·° ë ëë§ ëìì íëì ìì¸ìí©ì´ ë°ìí ëìë ì¬ì©ììê² ë°íë기 ì ì <literal>Session</literal>ì´ ë«íì§ê³
+ í¸ëìì
ì´ ì¢
ë£ëë ê²ì ì§ê·¹í ì¤ìíë¤. ì´ "Open Session in View" í¨í´ì ê´í ìì ë¤ì Hibernate ìí¤ë¥¼ ë³´ë¼.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ë³ëì ë¹ì§ëì¤ í°ì´ë¥¼ ê°ì§ ì´í리ì¼ì´ì
ìì, ë¹ì§ëì¤ ë¡ì§ì ë°í ì ì ì¹ í°ì´ì íìí 모ë ì½ë ì
ë¤ì
+ "ì¤ë¹"í´ì¼ íë¤. ì´ê²ì ë¹ì§ëì¤ í°ì´ê° 모ë ë°ì´í°ë¥¼ ë¡ëìí¤ê³ ì´ë¯¸ ì´ê¸°íë 모ë ë°ì´í°ë¥¼ í¹ì ì°ììì
+ íìí í리ì í
ì´ì
/ì¹ í°ì´ë¡ ë°íí´ì¼ í¨ì ì미íë¤. ëê° ì´í리ì¼ì´ì
ì ì¹ í°ì´ì íìíê² ë ê°ê°ì ì½ë ì
ì
+ ëí´ <literal>Hibernate.initialize()</literal>를 í¸ì¶íê±°ë(ì´ í¸ì¶ì ì¸ì
ì´ ë«í기 ì ì ë°ìí´ì¼ íë¤)
+ ëë <literal>FETCH</literal> ì ì ê°ê±°ë ëë <literal>Criteria</literal> ë´ì
+ <literal>FetchMode.JOIN</literal>ì ê°ì§ Hibernate ì§ì를 ì¬ì©íì¬ ì½ë ì
ì ì´ì¬í ê²ìíë¤. ì´ê²ì
+ ëê° ë¹ì ì´ <emphasis>Session Facade</emphasis> ëì <emphasis>Command</emphasis> í¨í´ì ì±íí
+ ê²½ì°ì ë ì½ë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ë¹ì ì ëí ì´ê¸°í ëì§ ìì ì½ë ì
ë¤(ëë ë¤ë¥¸ íë½ìë¤)ì ì ê·¼í기 ì ì <literal>merge()</literal> ëë
+ <literal>lock()</literal>ì¼ë¡ ìì ë¡ëë ê°ì²´ë¥¼ ìë¡ì´ <literal>Session</literal>nì 첨ë¶í ìë ìë¤.
+ ìëë¤. Hibernateë ì´ê²ì ìëì ì¼ë¡ ííì§ ìê³ , íì¤í ìëì ì¼ë¡ ííì§ <emphasis>ìì ê²ì´ë¤</emphasis>.
+ ìëíë©´ ê·¸ê²ì í¹ë³í 목ì ì ìí í¸ëìì
ì미를 ëì
í ê²ì´ê¸° ë문ì´ë¤!
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ëëë¡ ë¹ì ì ê±°ëí ì½ë ì
ì ì´ê¸°í ìí¤ë ê²ì ìíì§ ìì§ë§, ì¬ì í (ê·¸ê²ì ì¬ì´ì¦ì ê°ì) ê·¸ê²ì ëí ì´ë¤ ì ë³´ ëë
+ ë°ì´í°ì ë¶ë¶ì§í©ì íìë¡ íë¤.
+ </para>
+
+ <para>
+ ë¹ì ì ê·¸ê²ì ì´ê¸°í ìí¤ì§ ìê³ ì ì½ë ì
ì ì¬ì´ì¦ë¥¼ ì»ëë° ì½ë ì
íí°ë¥¼ ì¬ì©í ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[( (Integer) s.createFilter( collection, "select count(*)" ).list().get(0) ).intValue()]]></programlisting>
+
+ <para>
+ <literal>createFilter()</literal> ë©ìëë ëí ì ì²´ ì½ë ì
ì ì´ê¸°í ìí¬ íì ìì´ ì½ë ì
ì ë¶ë¶ì§í©ë¤ì í¨ì¨ì ì¼ë¡
+ ê²ìíëë° ì¬ì©ëë¤:
+ </para>
+
+ <programlisting><![CDATA[s.createFilter( lazyCollection, "").setFirstResult(0).setMaxResults(10).list();]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="performance-fetching-batch">
+ <title>batch íì¹ ì¬ì©í기</title>
+
+ <para>
+ Hibernateë ë°°ì¹ íì¹ì í¨ì¨ì ì¼ë¡ ì¬ì©í ì ìë¤. ì¦ íëì íë½ìê° ì¡ì¸ì¤ ë ê²½ì°ì Hibernateë ëªëª ì´ê¸°í ëì§ ìì
+ íë½ìë¤ì ë¡ëìí¬ ì ìë¤(ëë ì½ë ì
ë¤). batch íì¹ì lazy select íì¹ ë°©ëì ëí ìµì íì´ë¤. ë¹ì ì´ batch
+ íì¹ì íëìí¬ ì ìë ë ê°ì§ ë°©ë²ë¤ì´ ì¡´ì¬íë¤: í´ëì¤ ë 벨ìì ê·¸ë¦¬ê³ ì½ë ì
ë 벨ìì.
+ </para>
+
+ <para>
+ í´ëì¤ë¤/ìí°í°ë¤ì ëí batch íì¹ì ì´í´íê¸°ê° ë ì½ë¤. ë¹ì ì´ ì¤í ìì ë¤ì ìí©ì ì²íë¤ê³ ììíë¼: ë¹ì ì íëì
+ <literal>Session</literal> ìì ë¡ëë 25ê°ì <literal>Cat</literal> ì¸ì¤í´ì¤ë¤ì ê°ê³ ìê³ , ê°ê°ì
+ <literal>Cat</literal>ì ê·¸ê²ì <literal>ìì ì</literal> ì¦, <literal>Person</literal>ì ëí 참조를
+ ê°ê³ ìë¤. <literal>Person</literal> í´ëì¤ë íë½ì <literal>lazy="true"</literal>ë¡ì 매íëë¤. ë§ì¼
+ ë¹ì ì´ ì´ì 모ë catë¤ì íµí´ ë°ë³µíê³ ê°ê°ì catì ëí´ <literal>getOwner()</literal>를 í¸ì¶í ê²½ì°, Hibernateë
+ íë½ìë ìì ìë¤ì ê²ìí기 ìí´ 25ê°ì <literal>SELECT</literal> 문ì¥ë¤ì ëí´í¸ë¡ ì¤íìí¬ ê²ì´ë¤. ë¹ì ì
+ <literal>Person</literal> 매íìì <literal>batch-size</literal>를 ì§ì í¨ì¼ë¡ì¨ ì´ ëìì íëìí¬ ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[<class name="Person" batch-size="10">...</class>]]></programlisting>
+
+ <para>
+ Hibernateë ì´ì ì¸ ê°ì ì§ìë¤ ë§ì ì¤íìí¬ ê²ì´ê³ , ê·¸ í¨í´ì 10,10, 5 ì´ë¤.
+ </para>
+
+ <para>
+ ë¹ì ì ëí ì½ë ì
ë¤ì ëí´ batch íì¹ì ì´ì© ê°ë¥íê² í ìë ìë¤. ì를 ë¤ì´, ë§ì¼ ê°ê°ì <literal>Person</literal>ì´
+ <literal>Cat</literal>ë¤ì ê°ì§ lazy ì½ë ì
ì ê°ê³ , 10ê°ì personë¤ì´ <literal>Sesssion</literal> ë´ì íì¬
+ ë¡ëëì´ ìì ê²½ì°, 모ë personë¤ì ëí ë°ë³µì 10ê°ì <literal>SELECT</literal>ë¤ì ìì±ìí¬ ê²ì´ê³ , <literal>getCats()</literal>ì
+ ëí 매ë²ì í¸ì¶ì ëí´ íëì <literal>SELECT</literal>를 ìì±ìí¬ ê²ì´ë¤. ë§ì¼ ë¹ì ì´ <literal>Person</literal>
+ 매íìì <literal>cats</literal> ì½ë ì
ì ëí´ batch íì¹ì ì¬ì©ê°ë¥íê² í ê²½ì°, Hibernateë ì½ë ì
ë¤ì 미리-íì¹
+ ìí¬ ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <set name="cats" batch-size="3">
+ ...
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ <literal>batch-size</literal> 8ë¡ì, Hibernateë 4ê°ì SELECTë¤ìì 3, 3, 3, 1 ê°ì ì½ë ì
ë¤ì ë¡ëìí¬ ê²ì´ë¤.
+ ë¤ì ê·¸ ìì±ì ê°ì í¹ì <literal>Session</literal> ë´ìì ì´ê¸°í ëì§ ìì ì½ë ì
ë¤ì ììëë ê°ìì ìì¡´íë¤.
+ </para>
+
+ <para>
+ ë§ì¼ ë¹ì ì´ í목ë¤ì í¬ê°ì§ í¸ë¦¬ë¥¼ ê°ì§ ê²½ì°, ì를 ë¤ì´ ì íì ì¸ bill-of-materials í¨í´ì¸ ê²½ì°, (ë¹ë¡
+ <emphasis>ë´í¬ë set</emphasis> ëë <emphasis>ì¤ì²´íë ê²½ë¡(materialized path)</emphasis>ê°
+ 주ë¡-ì½ê¸°-í¸ë¦¬ë¤ì ëí´ ë ì¢ì ìµì
ì¼ ì ììì§ë¼ë) ì½ë ì
ë¤ì ëí batch íì¹ì´ í¹í ì ì©íë¤.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-fetching-subselect">
+ <title>subselect íì¹ ì¬ì©í기</title>
+
+ <para>
+ ë§ì¼ í ê°ì lazy ì½ë ì
ì´ë ë¨ì¼ ê° íë½ìê° íì¹ëì´ì¼ íë¤ë©´, Hibernateë íëì subselect ë´ìì ìëì
+ ì§ì를 ë¤ì ì¤ííì¬ ê·¸ê²ë¤ 모ë를 ë¡ëìí¨ë¤. ì´ê²ì ì¡°ê°ë ë¡ë© ìì´ batch íì¹ê³¼ ëì¼í ë°©ìì¼ë¡ ëìíë¤.
+ </para>
+
+ <!-- TODO: ì´ê²ì ëí´ ë ë§ì´ ìì±í ê² -->
+
+ </sect2>
+
+ <sect2 id="performance-fetching-lazyproperties">
+ <title>lazy íë¡í¼í° íì¹ ì¬ì©í기</title>
+
+ <para>
+ Hibernate3ì ê°ë³ì ì¸ íë¡í¼í°ë¤ì ëí lazy íì¹ì ì§ìíë¤. ì´ ìµì í 기ì ì ëí <emphasis>fetch groups</emphasis>
+ ì¼ë¡ ìë ¤ì ¸ ìë¤. ì´ê²ì´ ëê° ë§ì¼í
í¹ì§ìì ë
¸í¸í길 ë°ëë¤. ìëíë©´ ì¤ì ë¡ í ì½ê¸°ë¥¼ ìµì í ìí¤ë ê²ì´ ì»¬ë¼ ì½ê¸°ì ëí
+ ìµì í ë³´ë¤ í¨ì¬ ë ì¤ìí기 ë문ì´ë¤. íì§ë§ 리거ì í
ì´ë¸ë¤ì´ ìë°± ê°ì 컬ë¼ë¤ì ê°ê³ ë°ì´í° 모íì´ ê°ì ë ì ìì ë,
+ ì¤ì§ í´ëì¤ì ëªëª íë¡í¼í°ë¤ì ë¡ëìí¤ë ê² ë§ì´ ì ì©í ìë ìë¤.
+ </para>
+
+ <para>
+ lazy íë¡í¼í° ë¡ë©ì ì´ì©ê°ë¥íê² íë ¤ë©´, ë¹ì ì í¹ì property 매íë¤ì ëí´ <literal>lazy</literal> ìì±ì ì¤ì íë¼:
+ </para>
+
+ <programlisting><![CDATA[<class name="Document">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="name" not-null="true" length="50"/>
+ <property name="summary" not-null="true" length="200" lazy="true"/>
+ <property name="text" not-null="true" length="2000" lazy="true"/>
+</class>]]></programlisting>
+
+ <para>
+ Lazy property ë¡ë©ì ë¹ë ì ë°ì´í¸ì½ë ìë¨ì íìë¡ íë¤! ë§ì¼ ë¹ì ì ìì í´ëì¤ë¤ì´ ê°ì ëì§ ìì ê²½ì°,
+ Hibernateë ì¡°ì©íê² lazy íë¡í¼í° ì¤ì ë¤ì 무ìíê³ ì¦ê°ì ì¸ íì¹ì¼ë¡ íí´í ê²ì´ë¤.
+ </para>
+
+ <para>
+ bytecode ìë¨ì¼ë¡, ë¤ì Ant íì¤í¬ë¥¼ ì¬ì©íë¼:
+ </para>
+
+ <programlisting><![CDATA[<target name="instrument" depends="compile">
+ <taskdef name="instrument" classname="org.hibernate.tool.instrument.InstrumentTask">
+ <classpath path="${jar.path}"/>
+ <classpath path="${classes.dir}"/>
+ <classpath refid="lib.class.path"/>
+ </taskdef>
+
+ <instrument verbose="true">
+ <fileset dir="${testclasses.dir}/org/hibernate/auction/model">
+ <include name="*.class"/>
+ </fileset>
+ </instrument>
+</target>]]></programlisting>
+
+ <para>
+ ë¶íìí ì»¬ë¼ ì½ê¸°ë¥¼ í¼íë ë¤ë¥¸ (ë ì¢ì?) ë°©ë²ì ì ì´ë ì½ê¸° ì ì© í¸ëìì
ì ê²½ì°ì HQL ì§ì ëë Criteria ì§ìì
+ í¬ì¬(projection) í¹ì§ë¤ì ì¬ì©íë ê²ì´ë¤. ì´ê²ì ë¹ë ì ë°ì´í¸ì½ë ì²ë¦¬ì ëí íìì±ì í¼íê² í´ì£¼ê³ íì¤í ì í¸ëë
+ í´ê²°ì±
ì´ë¤.
+ </para>
+
+ <para>
+ ë¹ì ì HQLìì <literal>fetch all properties</literal>를 ì¬ì©íì¬ íë¡í¼í°ë¤ì ëí íµìì eager íì¹ì
+ ê°ì ìí¬ ì ìë¤.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="performance-cache" revision="1">
+ <title>ëë²ì§¸ ë 벨 ìºì</title>
+
+ <para>
+ Hibernate <literal>Session</literal>ì ìì ë°ì´í°ì ëí í¸ëìì
ë 벨 ìºìì´ë¤. class-by-classì
+ collection-by-collection ê¸°ë° ìì í´ë¬ì¤í° ëë JVM-ë 벨(<literal>SessionFactory</literal>-ë 벨) ìºì를 구ì±íë
+ ê²ì´ ê°ë¥íë¤. ë¹ì ì í´ë¬ì¤í°ë§ ë ìºì ìì íë¬ê·¸ì¸ í ìë ìë¤. 주ìíë¼. ìºìë¤ì (ë¹ë¡ ê·¸ê²ë¤ì´ ìºìë ë°ì´í°ë¥¼ ì 기ì ì¼ë¡
+ ë§ë£ëëë¡ êµ¬ì±ëì´ ììì§ë¼ë) ë ë¤ë¥¸ ì´í리ì¼ì´ì
ì ìí´ ìì ì ì¥ìì ëí´ íí´ì§ ë³ê²½ë¤ì ê²°ì½ ìì§ ëª»íë¤.
+ </para>
+
+ <para revision="1">
+ ëí´í¸ë¡, Hibernateë JVM-ë 벨ì ìºì±ì EHCache를 ì¬ì©íë¤. (JCS ì§ìì ì´ì ì§ë¶íê² ëìê³ Hibernateì ì¥ë ë²ì ìì
+ ì ê±°ë ê²ì´ë¤.) ë¹ì ì <literal>hibernate.cache.provider_class</literal> íë¡í¼í°ë¥¼ ì¬ì©íì¬
+ <literal>org.hibernate.cache.CacheProvider</literal>를 구ííë í´ëì¤ì ì´ë¦ì ì§ì í¨ì¼ë¡ì¨ ë¤ë¥¸ 구íì ì íí ìë
+ ìë¤.
+ You have the option to tell Hibernate which caching implementation to use by
+ specifying the name of a class that implements <literal>org.hibernate.cache.CacheProvider</literal>
+ using the property <literal>hibernate.cache.provider_class</literal>. Hibernate
+ comes bundled with a number of built-in integrations with open-source cache providers
+ (listed below); additionally, you could implement your own and plug it in as
+ outlined above. Note that versions prior to 3.2 defaulted to use EhCache as the
+ default cache provider; that is no longer the case as of 3.2.
+ ë¹ì ì <literal>hibernate.cache.provider_class</literal> íë¡í¼í°ë¥¼ ì¬ì©íì¬
+ <literal>org.hibernate.cache.CacheProvider</literal>를 구ííë í´ëì¤ì ì´ë¦ì ì§ì í¨ì¼ë¡ì¨ ì´ë ìºì± 구íì
+ ì¬ì©í ê²ì¸ì§ë¥¼ Hibernateìê² ìë ¤ì£¼ë ìµì
ì ê°ëë¤. Hibernateë (ìëì ì´ê±°ë) ì¤í-ìì¤ íë¡ë°ì´ëë¤ì ê°ì§
+ ë§ì ë¹ëëì´ ìë íµí©ë¤ì ë²ë¤ë¡ ê°ê³ ìë¤; ì¶ê°ì ì¼ë¡ ë¹ì ì ììì ì¸ê¸íë¯ì´ ê·¸ê²ì ë¹ì ìì ì ê²ì 구íí ì ìê³ ê·¸ê²ì
+ íë¬ê·¸ ìí¬ ì ìë¤. 3.2 ì´ë² ë²ì ë¤ì ëíí¸ ìºì íë¡ë°ì´ëë¡ì EhCache를 ì¬ì©íëë¡ ëí¬ë¦ë¡ ë´ì¥ëì´ ììì ë
¸í¸íë¼;
+ ë²ì 3.2ì ê²½ì°ì ê·¸ê²ì ëì´ì ëí´í¸ ë´ì¥ì´ ìëë¤.
+ </para>
+
+ <table frame="topbot" id="cacheproviders" revision="1">
+ <title>ìºì íë¡ë°ì´ëë¤</title>
+ <tgroup cols='5' align='left' colsep='1' rowsep='1'>
+ <colspec colname='c1' colwidth="1*"/>
+ <colspec colname='c2' colwidth="3*"/>
+ <colspec colname='c3' colwidth="1*"/>
+ <colspec colname='c4' colwidth="1*"/>
+ <colspec colname='c5' colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>ìºì</entry>
+ <entry>íë¡ë°ì´ë í´ëì¤</entry>
+ <entry>íì
</entry>
+ <entry>í´ë¬ì¤í° ìì </entry>
+ <entry>ì§ì ìºì ì§ì</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>Hashtable (ì í ì©ëë¡ ê³ ìëì´ ìì§ ìì)</entry>
+ <entry><literal>org.hibernate.cache.HashtableCacheProvider</literal></entry>
+ <entry>memory</entry>
+ <entry></entry>
+ <entry>yes</entry>
+ </row>
+ <row>
+ <entry>EHCache</entry>
+ <entry><literal>org.hibernate.cache.EhCacheProvider</literal></entry>
+ <entry>memory, disk</entry>
+ <entry></entry>
+ <entry>yes</entry>
+ </row>
+ <row>
+ <entry>OSCache</entry>
+ <entry><literal>org.hibernate.cache.OSCacheProvider</literal></entry>
+ <entry>memory, disk</entry>
+ <entry></entry>
+ <entry>yes</entry>
+ </row>
+ <row>
+ <entry>SwarmCache</entry>
+ <entry><literal>org.hibernate.cache.SwarmCacheProvider</literal></entry>
+ <entry>clustered (ip multicast)</entry>
+ <entry>yes (clustered invalidation)</entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>JBoss TreeCache</entry>
+ <entry><literal>org.hibernate.cache.TreeCacheProvider</literal></entry>
+ <entry>clustered (ip multicast), transactional</entry>
+ <entry>yes (replication)</entry>
+ <entry>yes (clock sync req.)</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <sect2 id="performance-cache-mapping" revision="2">
+ <title>Cache 매íë¤</title>
+
+ <para>
+ í´ëì¤ ëë ì½ë ì
매íì <literal><cache></literal> ììë ë¤ì íìì ê°ëë¤:
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="cache1" coords="2 70"/>
+ <area id="cache2" coords="3 70"/>
+ <area id="cache3" coords="4 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<cache
+ usage="transactional|read-write|nonstrict-read-write|read-only"
+ region="RegionName"
+ include="all|non-lazy"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="cache1">
+ <para>
+ <literal>usage</literal>(íì) ìºì± ë°©ë를 ì§ì íë¤:
+ <literal>transactional</literal>,
+ <literal>read-write</literal>,
+ <literal>nonstrict-read-write</literal> ëë
+ <literal>read-only</literal>
+ </para>
+ </callout>
+ <callout arearefs="cache2">
+ <para>
+ <literal>region</literal> (ìµì
, ëí´í¸ë class ëë ì½ë ì
role ì´ë¦)
+ second level ìºì ììì ì´ë¦ì ì§ì íë¤
+ </para>
+ </callout>
+ <callout arearefs="cache3">
+ <para>
+ <literal>include</literal> (ìµì
, ëí´í¸ë <literal>all</literal>)
+ <literal>non-lazy</literal>ë <literal>lazy="true"</literal>ë¡ ë§¤íë ìí°í°ì
+ íë¡í¼í°ë¤ì ì§ì íë©° ìì±-ë 벨 lazy íì¹ì´ ì´ì© ê°ë¥í ë í¤ìë ì ìë¤
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ ë¤ë¥¸ ë°©ë²ì¼ë¡ (ì í¸ì ì¼ë¡?), ë¹ì ì <literal>hibernate.cfg.xml</literal> ë´ì <literal><class-cache></literal>ì
+ <literal><collection-cache></literal> ììë¤ì ì§ì í ìë ìë¤.
+ </para>
+
+ <para>
+ <literal>usage</literal> ìì±ì <emphasis> ìºì ëìì± ë°©ë</emphasis>를 ì§ì íë¤.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-cache-readonly">
+ <title>ë°©ë: ì½ê¸° ì ì©</title>
+
+ <para>
+ ë¹ì ì ì´í리ì¼ì´ì
ì´ ìì í´ëì¤ì ì¸ì¤í´ì¤ë¤ì ì½ì´ë¤ì¼ íìê° ìì§ë§ ê²°ì½ ë³ê²½í íìê° ìì ê²½ì°ì <literal>read-only</literal>
+ ìºìê° ì¬ì©ë ì ìë¤. ì´ê²ì ê°ì¥ ê°ë¨í ìµìì í¼í¬ë¨¼ì¤ë¥¼ ìí ë°©ëì´ë¤. ê·¸ê²ì í´ë¬ì¤í° ë´ ì¬ì©ìë ìë²½íê² ìì íë¤.
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Immutable" mutable="false">
+ <cache usage="read-only"/>
+ ....
+</class>]]></programlisting>
+
+ </sect2>
+
+
+ <sect2 id="performance-cache-readwrite">
+ <title>ë°©ë: ì½ê¸°/ì°ê¸°</title>
+
+ <para>
+ ì´í리ì¼ì´ì
ì´ ë°ì´í°ë¥¼ ì
ë°ì´í¸ í íìê° ìì ê²½ì°, <literal>read-write</literal> ìºìê° ì ì íë¤. ë§ì¼
+ ì§ë ¬í ê°ë¥í(serializable) í¸ëìì
격리 ë ë²¨ì´ íìí ê²½ì°ìë ì´ ìºì ë°©ëê° ê²°ì½ ì¬ì©ëì§ ë§ìì¼ íë¤. ë§ì¼ ìºìê° JTA
+ íê²½ìì ì¬ì©ë ê²½ì°, ë¹ì ì JTA <literal>TransactionManager</literal>를 ì»ë ë°©ë를 ëª
ëª
íë
+ <literal>hibernate.transaction.manager_lookup_class</literal> íë¡í¼í°ë¥¼ ì§ì í´ì¼ íë¤. ë¤ë¥¸ íê²½ë¤ìì,
+ ë¹ì ì<literal>Session.close()</literal> ëë <literal>Session.disconnect()</literal>ê° í¸ì¶ë ë
+ í¸ëìì
ì´ ìë£ëë ê²ì íì¤í í´ì¼ íë¤. ë§ì¼ ë¹ì ì´ í´ë¬ì¤í° ë´ì ì´ ë°©ë를 ì¬ì©íê³ ì ìí ê²½ì°, ë¹ì ì 기본 ìºì 구íì´
+ ì ê¸ì ì§ìíëë¡ íë ê²ì íì¤í í´ì¼ íë¤. 미리 ë§ë¤ì´ì§ ìºì íë¡ë°ì´ëë¤ì ê·¸ë ê² ííì§ <emphasis>ìëë¤</emphasis>.
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Cat" .... >
+ <cache usage="read-write"/>
+ ....
+ <set name="kittens" ... >
+ <cache usage="read-write"/>
+ ....
+ </set>
+</class>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="performance-cache-nonstrict">
+ <title>ë°©ë: ì격íì§ ìì ì½ê¸°/ì°ê¸°</title>
+
+ <para>
+ ë§ì¼ ì´í리ì¼ì´ì
ì´ ì¤ì§ ë°ì´í°ë¥¼ ì주 ì
ë°ì´í¸í íìê° ìê³ (ì를 ë¤ì´, ë§ì¼ ë ê°ì í¸ëìì
ë¤ì´ ëìì ëì¼í í목ì ì
ë°ì´í¸
+ íë ¤ê³ ìëíë ì ë§ ììì§íì§ ìì ê²½ì°) ê·¸ë¦¬ê³ ì격í í¸ëìì
ê²©ë¦¬ê° íìíì§ ìì ê²½ì°, <literal>nonstrict-read-write</literal>
+ ìºìê° ì ì í ì ìë¤. ë§ì¼ ê·¸ ìºìê° JTA íê²½ìì ì¬ì©ë ê²½ì°, ë¹ì ì <literal>hibernate.transaction.manager_lookup_class</literal>를
+ ì§ì í´ì¼ íë¤. ë¤ë¥¸ íê²½ë¤ìì, ë¹ì ì <literal>Session.close()</literal> ëë <literal>Session.disconnect()</literal>ê° í¸ì¶ë ë
+ í¸ëìì
ì´ ìë£ëëë¡ íì¤í í´ì¼ íë¤.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-cache-transactional">
+ <title>ë°©ë: transactional</title>
+
+ <para>
+ <literal>transactional</literal> ìºì ë°©ëë JBoss TreeCacheì ê°ì ì ì²´ í¸ëìì
ì ì¸ ìºì íë¡ë°ì´ëë¤ì ëí
+ ì§ìì ì ê³µíë¤. ê·¸ë° ìºìë ì¤ì§ JTA íê²½ ë´ìì ì¬ì©ë ì ìê³ ë¹ì ì
+ <literal>hibernate.transaction.manager_lookup_class</literal>를 ì§ì í´ì¼ íë¤.
+ </para>
+
+ </sect2>
+
+ <para>
+ ìºì íë¡ë°ì´ëë¤ ì¤ ì´ë ê²ë 모ë ìºì ëìì± ë°©ëë¤ì ì§ìíì§ ìëë¤. ë¤ì í
ì´ë¸ì ì´ë íë¡ë°ì´ëë¤ì´ ì´ë ëìì± ë°©ëë¤ê³¼
+ í¸íëëì§ë¥¼ ë³´ì¬ì¤ë¤.
+ </para>
+
+ <table frame="topbot">
+ <title>ìºì ëìì± ë°©ë ì§ì</title>
+ <tgroup cols='5' align='left' colsep='1' rowsep='1'>
+ <colspec colname='c1' colwidth="1*"/>
+ <colspec colname='c2' colwidth="1*"/>
+ <colspec colname='c3' colwidth="1*"/>
+ <colspec colname='c4' colwidth="1*"/>
+ <colspec colname='c5' colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>ìºì</entry>
+ <entry>ì½ê¸° ì ì©</entry>
+ <entry>ì격íì§ ìì ì½ê¸°-ì°ê¸°</entry>
+ <entry>ì½ê¸°-ì°ê¸°</entry>
+ <entry>transactional</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>Hashtable (ì íì©ì¼ë¡ ê³ ìëì§ ìì)</entry>
+ <entry>ì</entry>
+ <entry>ì</entry>
+ <entry>ì</entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>EHCache</entry>
+ <entry>ì</entry>
+ <entry>ì</entry>
+ <entry>ì</entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>OSCache</entry>
+ <entry>ì</entry>
+ <entry>ì</entry>
+ <entry>ì</entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>SwarmCache</entry>
+ <entry>ì</entry>
+ <entry>ì</entry>
+ <entry></entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>JBoss TreeCache</entry>
+ <entry>ì</entry>
+ <entry></entry>
+ <entry></entry>
+ <entry>ì</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect1>
+
+ <sect1 id="performance-sessioncache" revision="2">
+ <title>ìºìë¤ì ê´ë¦¬í기</title>
+
+ <para>
+ ë¹ì ì´ ê°ì²´ë¥¼ <literal>save()</literal>, <literal>update()</literal> ëë <literal>saveOrUpdate()</literal>ì
+ ì ë¬í ëë§ë¤ ê·¸ë¦¬ê³ ë¹ì ì´ <literal>load()</literal>, <literal>get()</literal>, <literal>list()</literal>,
+ <literal>iterate()</literal> ëë <literal>scroll()</literal>ì ì¬ì©íì¬ ê°ì²´ë¥¼ ê²ìí ëë§ë¤, ê·¸ ê°ì²´ë
+ <literal>Session</literal>ì ë´ë¶ ìºìì ì¶ê°ëë¤.
+ </para>
+ <para>
+ <literal>flush()</literal>ê° ì°¨íì ì¼ë¡ í¸ì¶ë ë, ê·¸ ê°ì²´ì ìíë ë°ì´í°ë² ì´ì¤ì ë기í ë ê²ì´ë¤. ë§ì¼ ë¹ì ì´ ì´ ë기íê°
+ ë°ìëë ê²ì ìíì§ ìê±°ë ë§ì¼ ë¹ì ì´ ëëì ê°ì²´ë¤ì ì²ë¦¬ ì¤ì´ê³ ë©ëª¨ë¦¬ë¥¼ í¨ì¨ì ì¼ë¡ ê´ë¦¬í íìê° ìì ê²½ì°, <literal>evict()</literal>
+ ë©ìëë first-level ìºìë¡ë¶í° ê·¸ ê°ì²´ì ê·¸ê²ì ì½ë ì
ë¤ì ì ê±°íëë° ì¬ì©ë ì ìë¤.
+ </para>
+
+ <programlisting><![CDATA[ScrollableResult cats = sess.createQuery("from Cat as cat").scroll(); //a huge result set
+while ( cats.next() ) {
+ Cat cat = (Cat) cats.get(0);
+ doSomethingWithACat(cat);
+ sess.evict(cat);
+}]]></programlisting>
+
+ <para>
+ <literal>Session</literal>ì ëí ì¸ì¤í´ì¤ê° ì¸ì
ìºìì ìíëì§ ì¬ë¶ë¥¼ ê²°ì íëë° <literal>contains()</literal>
+ ë©ìë를 ì ê³µíë¤.
+ </para>
+
+ <para>
+ ì¸ì
ìºìë¡ë¶í° 모ë ê°ì²´ë¤ì ìì íê² í´ê±°ìí¤ê¸° ìí´, <literal>Session.clear()</literal>를 í¸ì¶íë¼.
+ </para>
+
+ <para>
+ second-level ìºìì ê²½ì°, íëì ì¸ì¤í´ì¤, ì ì²´ í´ëì¤, ì½ë ì
ì¸ì¤í´ì¤ ëë ì ì²´ ì½ë ì
roleì ìºìë ìí를 í´ê±°ìí¤ë
+ <literal>SessionFactory</literal> ìì ì ìë ë©ìëë¤ì´ ì¡´ì¬íë¤.
+ </para>
+
+ <programlisting><![CDATA[sessionFactory.evict(Cat.class, catId); //evict a particular Cat
+sessionFactory.evict(Cat.class); //evict all Cats
+sessionFactory.evictCollection("Cat.kittens", catId); //evict a particular collection of kittens
+sessionFactory.evictCollection("Cat.kittens"); //evict all kitten collections]]></programlisting>
+
+ <para>
+ <literal>CacheMode</literal>ë í¹ì ì¸ì
ì´ second-level ìºìì ì´ë»ê² ìí¸ìì©íëì§ë¥¼ ì ì´íë¤
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>CacheMode.NORMAL</literal> - second-level ìºìë¡ë¶í° ìì´í
ë¤ì ì½ì´ë¤ì´ê³ second-level ìºìë¡
+ ìì´í
ë¤ì 기ë¡íë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>CacheMode.GET</literal> - second-level ìºìë¡ë¶í° ìì´í
ë¤ì ì½ì´ë¤ì´ì§ë§, ë°ì´í°ë¥¼ ì
ë°ì´í¸í ë를 ì ì¸íë©´
+ second-level ìºìë¡ ê¸°ë¡íì§ ìëë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>CacheMode.PUT</literal> - ìì´í
ë¤ì second-level ìºìì 기ë¡íì§ë§, second-level ìºìë¡ë¶í° ì½ì´ë¤ì´ì§
+ ìëë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>CacheMode.REFRESH</literal> - ìì´í
ë¤ì second-level ìºìë¡ê¸°ë¡íì§ë§, second-level ìºìë¡ë¶í°
+ ì½ì´ë¤ì´ì§ ìê³ , ë°ì´í°ë² ì´ì¤ë¡ë¶í° ì½ì´ë¤ì¸ 모ë ìì´í
ë¤ì ëí second-level ìºìì ê°±ì ì ê°ì ìì¼,
+ <literal>hibernate.cache.use_minimal_puts</literal>ì í¨ê³¼ë¥¼ 무ìíë¤
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ second-level ìºì ëë ì§ì ìºì ììì ë´ì©ë¬¼ì ë¸ë¼ì°ì§íë ¤ë©´ <literal>Statistics</literal> API를 ì¬ì©íë¼:
+ </para>
+
+ <programlisting><![CDATA[Map cacheEntries = sessionFactory.getStatistics()
+ .getSecondLevelCacheStatistics(regionName)
+ .getEntries();]]></programlisting>
+
+ <para>
+ ë¹ì ì íµê³ë¥¼ ì´ì© ê°ë¥íê² íê³ , ì íì ì¼ë¡ Hibernateë¡ íì¬ê¸ ìºì ìí¸ë¦¬ë¤ì ë³´ë¤ ì¸ê°ìê² ì´í´ê°ë¥í íìì¼ë¡ ì ì§ìí¤ëë¡
+ ê°ì ìí¤ë ê²ì´ íìí ê²ì´ë¤:
+ </para>
+
+ <programlisting><![CDATA[hibernate.generate_statistics true
+hibernate.cache.use_structured_entries true]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="performance-querycache" revision="1">
+ <title>ì§ì ìºì</title>
+
+ <para>
+ ì§ì ê²°ê³¼ ì
ë¤ì´ ëí ìºìë ìë ìë¤. ì´ê²ì ëì¼í íë¼ë¯¸í°ë¤ì ê°ì§ê³ ì주 ì¤íëë ì§ìë¤ìë§ ì ì©íë¤.
+ ì§ì ìºì를 ì¬ì©í기 ìí´ ë¹ì ì 먼ì ê·¸ê²ì ì´ì© ê°ë¥íëë¡ í´ì¼ íë¤:
+ </para>
+
+ <programlisting><![CDATA[hibernate.cache.use_query_cache true]]></programlisting>
+
+ <para>
+ ì´ ì¤ì ì ë ê°ì ìë¡ì´ ìºì ììë¤ - ìºìë ì§ì ê²°ê³¼ ì
ë¤ì ë³´ê´íë ê²
+ (<literal>org.hibernate.cache.StandardQueryCache</literal>), ì§ì ê°ë¥í í
ì´ë¸ë¤ì ëí ê°ì¥
+ ìµì ì
ë°ì´í¸ë¤ì ëí timestampë¤ì ë³´ê´íë ë¤ë¥¸ ê²
+ (<literal>org.hibernate.cache.UpdateTimestampsCache</literal>)-ì ìì±ì ê°ì íë¤ . ì§ì ìºìë
+ ê²°ê³¼ ì
ë´ì ì¤ì ìí°í°ë¤ì ìí를 ìºììí¤ì§ ììì ë
¸í¸íë¼; ê·¸ê²ì ì¤ì§ ìë³ì ê°ë¤ê³¼ ê° íì
ì ê²°ê³¼ë¤
+ ë§ì ìºììí¨ë¤. ë°ë¼ì ì§ì ìºìë íì second-level ìºìì í¨ê» ì¬ì©ëì´ì¼ íë¤.
+ </para>
+
+ <para>
+ ëë¶ë¶ì ì§ìë¤ì ìºì±ì¼ë¡ë¶í° ì´ì ì´ ì기ì, ëí´í¸ë¡ ì§ìë¤ì ìºìëì§ ìëë¤. ìºì±ì ì´ì© ê°ë¥íëë¡ íë ¤ë©´,
+ <literal>Query.setCacheable(true)</literal>를 í¸ì¶íë¼. ì´ í¸ì¶ì 기존 ìºì ê²°ê³¼ë¤ì ì°¾ë ê²ì ì§ììê²
+ íì©í´ì£¼ê±°ë ì§ìê° ì¤íë ë ê·¸ê²ì ê²°ê³¼ë¤ì ìºìì ì¶ê°íë ê²ì íì©í´ì¤ë¤.
+ </para>
+
+ <para>
+ ë§ì¼ ë¹ì ì´ ì§ì ìºì ë§ë£ ì ì±
ë¤ì ëí ì¸ë°í ì ì´ë¥¼ íìë¡ í ê²½ì°, ë¹ì ì
+ <literal>Query.setCacheRegion()</literal>ì í¸ì¶í¨ì¼ë¡ì¨ í¹ë³í ì§ìì ëí´ ëª
ëª
ëë ìºì ììì
+ ì§ì í ìë ìë¤.
+ </para>
+
+ <programlisting><![CDATA[List blogs = sess.createQuery("from Blog blog where blog.blogger = :blogger")
+ .setEntity("blogger", blogger)
+ .setMaxResults(15)
+ .setCacheable(true)
+ .setCacheRegion("frontpages")
+ .list();]]></programlisting>
+
+ <para>
+ ë§ì¼ ì§ìê° ê·¸ê²ì ì§ì ìºì ììì ê°±ì ì ê°ì ìì¼ì¼ íë ê²½ì°ì, ë¹ì ì <literal>Query.setCacheMode(CacheMode.REFRESH)</literal>를
+ í¸ì¶í´ì¼ íë¤. ì´ê²ì 기본 ë°ì´í°ê° ë³ëì íë¡ì¸ì¤ë¥¼ íµí´ ì
ë°ì´í¸ëìê³ (ì를 ë¤ë©´, Hibernate를 íµí´ ë³ê²½ëì§ ììê³ )
+ í¹ì ì§ì ê²°ê³¼ ì
ë¤ì ì íì ì¼ë¡ ê°±ì íë ê²ì ì´í리ì¼ì´ì
ìê² íì©í´ì£¼ë ê²½ì°ë¤ìì í¹ë³í ì ì©íë¤. ì´ê²ì
+ <literal>SessionFactory.evictQueries()</literal>를 íµí´ ì§ì ìºì ììì í´ê±°ìí¤ë ë³´ë¤ í¨ê³¼ì ì¸ ëìì´ë¤.
+ </para>
+
+ </sect1>
+
+ <sect1 id="performance-collections">
+ <title>ì½ë ì
í¼í¬ë¨¼ì¤ ì´í´í기</title>
+
+ <para>
+ ì°ë¦¬ëì´ë¯¸ ì½ë ì
ë¤ì ê´í´ ì기íëë° ê½¤ ë§ì ìê°ì ììíë¤. ì´ ì ìì ì°ë¦¬ë ì½ë ì
ë¤ì´ ì¤í ìì ì´ë»ê² íìíëì§ì ê´í
+ í ìì ìì ë¤ì ì¡°ëª
í ê²ì´ë¤.
+ </para>
+
+ <sect2 id="performance-collections-taxonomy">
+ <title>ë¶ë¥</title>
+
+ <para>Hibernateë ì¸ ê°ì§ 기본ì ì¸ ì¢
ë¥ì ì½ë ì
ë¤ì ì ìíë¤:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>ê°ë¤ì ê°ì§ ì½ë ì
ë¤</para>
+ </listitem>
+ <listitem>
+ <para>one to many ì°ê´ë¤</para>
+ </listitem>
+ <listitem>
+ <para>many to many ì°ê´ë¤</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ì´ ë¶ë¥ë ì¬ë¬ ê°ì§ í
ì´ë¸ê³¼ foreign key ê´ê³ë¤ì 구ë³ì§ì§ë§ ì°ë¦¬ê° ê´ê³í 모íì ëí´ ì íìê° ìë 모ë ê²ì ì°ë¦¬ìê²
+ ë§í´ì£¼ì§ ìëë¤. ê´ê³í 구조ì í¼í¬ë¨¼ì¤ í¹ì§ë¤ì ìì íê² ì´í´í기 ìí´, ì°ë¦¬ë ëí ì½ë ì
íë¤ì ì
ë°ì´í¸íê±°ë ìì í기
+ ìí´ Hibernateì ìí´ ì¬ì©ëë íë¼ì´ë¨¸ë¦¬ í¤ì 구조를 ê²í í´ì¼ íë¤. ì´ê²ì ë¤ì ë¶ë¥ë¥¼ ì ìíë¤:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>ì¸ë±ì± ë ì½ë ì
ë¤ </para>
+ </listitem>
+ <listitem>
+ <para>setë¤</para>
+ </listitem>
+ <listitem>
+ <para>bagë¤</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ 모ë ì¸ë±ì±ë ì½ë ì
ë¤(mapë¤, listë¤, arrayë¤)ì <literal><key></literal>ì
+ <literal><index></literal> 컬ë¼ë¤ë¡ ì´ë£¨ì´ì§ íë¼ì´ë¨¸ë¦¬ í¤ë¥¼ ê°ëë¤. ì´ ê²½ì°ì ì½ë ì
ì
ë°ì´í¸ë¤ì ëê°
+ ê·¹í í¨ì¨ì ì´ë¤ - Hibernateê° ê·¸ê²ì ì
ë°ì´í¸ë ìì 를 ìëí ë íë¼ì´ë¨¸ë¦¬ í¤ë í¨ì¨ì ì¼ë¡ ì¸ë±ì±ë ì ìê³ í¹ì íì
+ í¨ì¨ì ì¼ë¡ ìì¹ì§ìì§ ì ìë¤.
+ </para>
+
+ <para>
+ Setë¤ì <literal><key></literal>ì ìì 컬ë¼ë¤ë¡ 구ì±ë íë¼ì´ë¨¸ë¦¬ í¤ë¥¼ ê°ëë¤. ì´ê²ì ëªëª ì íì ì½ë ì
ìì,
+ í¹í composite ììë¤ ëë ëí í
ì¤í¸ ëë ë°ì´ë리 íëë¤ì ëí´ ë í¨ì¨ì ì¼ ì ìë¤; ë°ì´í°ë² ì´ì¤ë ë³µì¡í íë¼ì´ë¨¸ë¦¬
+ í¤ë¥¼ í¨ì¨ì ì¼ë¡ ì¸ë±ì±íë ê²ì´ ë¶ê°ë¥í ìë ìë¤. ë°ë©´ì one to many ëë many to many ì°ê´ë¤ì ê²½ì°, í¹í í©ì±
+ ìë³ìë¤ì ê²½ì°ìë í¨ì¨ì ì¼ ì ìì ê² ê°ë¤.(ë¶ì-ë
¸í¸: ë§ì¼ ë¹ì ì´ ë¹ì ì ìí <literal><set></literal>ì
+ íë¼ì´ë¨¸ë¦¬ í¤ë¥¼ ì¤ì ë¡ ìì±ìí¤ê¸° ìí´ <literal>SchemaExport</literal>를 ìíë¤ë©´ ë¹ì ì 모ë 컬ë¼ë¤ì
+ <literal>not-null="true"</literal>ë¡ ì ì¸í´ì¼ íë¤.)
+ </para>
+
+ <para>
+ <literal><idbag></literal> 매íë¤ì ëì© í¤ë¥¼ ì ìíì¬ì, ê·¸ê²ë¤ì íì ì
ë°ì´í¸ì ë§¤ì° í¨ì¨ì ì´ë¤. ì¬ì¤,
+ ê·¸ê²ë¤ì ìµìì ê²½ì°ì´ë¤.
+ </para>
+
+ <para>
+ Bagë¤ì ê°ì¥ ëì ê²½ì°ì´ë¤. ìëíë©´ íëì bagì ì¤ë³µ ìì ê°ë¤ì íì©íê³ ì¸ë±ì¤ 컬ë¼ì ê°ì§ ì기 ë문ì, íë¼ì´ë¨¸ë¦¬ í¤ê°
+ ì ìë ì ìë¤. Hibernateë ì¤ë³µ íë¤ ì¬ì´ë¥¼ êµ¬ë¶ ì§ë ë°©ë²ì ê°ê³ ìì§ ìë¤. Hibernateë ê·¸ê²ì´ ë³ê²½ë ëë§ë¤
+ (í ê°ì DELETEë¡) ì½ë ì
ì ìì íê² ì ê±°íê³ ë¤ì ìì±ìí´ì¼ë¡ì¨ ì´ ë¬¸ì 를 í´ê²°íë¤. ì´ê²ì ë§¤ì° ë¹í¨ì¨ì ì´ë¤.
+ </para>
+
+ <para>
+ one-to-many ì°ê´ì ê²½ì°, "íë¼ì´ë¨¸ë¦¬ í¤"ë ë°ì´í°ë² ì´ì¤ í
ì´ë¸ì 물리ì ì¸ íë¼ì´ë¨¸ë¦¬ í¤ê° ìë ìë ìì§ë§- ì´ ê²½ì°ììë
+ ìì ë¶ë¥ë ì¬ì í ì ì©íë¤. (ê·¸ê²ì ì¬ì í Hibernateê° ì½ë ì
ì ê°ë³ íë¤ì ì´ë»ê² "ìì¹ì§ì°ë"ì§ë¥¼ ë°ìíë¤.)
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-collections-mostefficientupdate">
+ <title>List, map, idbag, setë¤ì updateì ê°ì¥ í¨ì¨ì ì¸ ì½ë ì
ë¤ì´ë¤</title>
+
+ <para>
+ ìì ë
¼ììì, ì¸ë±ì±ë ì½ë ì
ë¤ê³¼ (ëê°) setë¤ì´ ììë¤ì ì¶ê°íê³ , ì ê±°íê³ ì
ë°ì´í¸í¨ì ìì´ ê°ì¥ í¨ì¨ì ì¸ ì¤í¼ë ì´ì
ì
+ íì©í´ì¤ë¤.
+ </para>
+
+ <para>
+ ìë§ ì¸ë±ì± ë ì½ë ì
ë¤ì´ many to many ì°ê´ë¤ì ìí ëë ê°ë¤ì ê°ì§ ì½ë ì
ë¤ì ìí setë¤ì ëí´ ê°ê³ ìë íë ì´ìì
+ ì¥ì ë¤ì´ ì¡´ì¬íë¤. <literal>Set</literal>ì 구조 ë문ì, Hibernateë ììê° "ë³ê²½"ë ë íì <literal>UPDATE</literal>
+ íì§ ìëë¤. <literal>Set</literal>ì ëí ë³ê²½ë¤ì íì (ê°ë³ íë¤ì ëí) <literal>INSERT</literal>ì
+ <literal>DELETE</literal>를 íµí´ ëìíë¤. ë¤ì ì´ ê²í ë one to many ì°ê´ë¤ì ì ì©ëì§ ìëë¤.
+ </para>
+
+ <para>
+ ë°°ì´ë¤ì´ lazy ë ì ììì ê´ì°° í íì, ì°ë¦¬ë listë¤, mapë¤, ê·¸ë¦¬ê³ idbagë¤ì´ ë¨ë
ì´ ìë setë¤ì ê°ì§ ê°ì¥ í¼í¬ë¨¼ì¤ê°
+ ì¢ì(non-inverse) ì½ë ì
íì
ë¤ìì ê²°ë¡ ì§ì ê²ì´ë¤. Setë¤ì Hibernate ì´í리ì¼ì´ì
ë¤ìì ê°ì¥ ê³µíµë ì¢
ë¥ì ì½ë ì
ì´
+ ë ê²ì´ë¼ ììëë¤. ì´ê²ì "set" ìë¯¸ê° ê´ê³í 모íìì ê°ì¥ ê³ ì í ê²ì´ê¸° ë문ì´ë¤.
+ </para>
+
+ <para>
+ íì§ë§, ì ì¤ê³ë Hibernate ëë©ì¸ 모íë¤ìì, ì°ë¦¬ë ëê° ëë¶ë¶ì ì½ë ì
ë¤ì´ ì¬ì¤ <literal>inverse="true"</literal>를
+ ê°ì§ one-to-many ì°ê´ë¤ìì ë³´ê² ëë¤. ì´ë¤ ì°ê´ë¤ì ê²½ì°, ì
ë°ì´í¸ë ì°ê´ì many-to-one ìëì ìí´ ì²ë¦¬ëê³ , ë°ë¼ì
+ ì½ë ì
ì
ë°ì´í¸ í¼í¬ë¨¼ì¤ì ëí ê²í ë¤ì ë¨ìí ì ì©ëì§ ìëë¤.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-collections-mostefficentinverse">
+ <title>Bagë¤ê³¼ listë¤ì ê°ì¥ í¨ì¨ì ì¸ inverse ì½ë ì
ë¤ì´ë¤</title>
+
+ <para>
+ ë¨ì§ ë¹ì ì´ ììí bagë¤ì ë²ë¦¬ê¸° ì ì, bagë¤(ê³¼ ëí listë¤)ì´ setë¤ë³´ë¤ í¨ì¬ ë ì±ë¥ì´ ì¢ì í¹ë³í ê²½ì°ë¤ì´ ì¡´ì¬íë¤.
+ <literal>inverse="true"</literal>를 ê°ì§ ì½ë ì
ë¤(ì를 ë¤ì´, íì¤ ìë°©í¥ one-to-many ê´ê³ í¹ì§)ì ê²½ì°, ì°ë¦¬ë
+ bag ììë¤ì ì´ê¸°í(íì¹) ìí¬ íì ìì´ bag ëë listì ììë¤ì ì¶ê°ìí¬ ì ìë¤! ì´ê²ì <literal>Collection.add()</literal>
+ ëë <literal>Collection.addAll()</literal>ì´ (<literal>Set</literal>ê³¼ë ë¬ë¦¬) íì bag ëë
+ <literal>List</literal>ì ëí´ true를 ë°íí´ì¼í기 ë문ì´ë¤. ì´ê²ì í¨ì¬ ë¤ì ê³µíµì ì¸ ì½ë를 ë ë¹ ë¥´ê² ë§ë¤ ì ìë¤.
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) sess.load(Parent.class, id);
+Child c = new Child();
+c.setParent(p);
+p.getChildren().add(c); //no need to fetch the collection!
+sess.flush();]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="performance-collections-oneshotdelete">
+ <title>ì ì· delete</title>
+
+ <para>
+ ì¢
ì¢
ì½ë ì
ììë¤ì íëì© ìì íë ê²ì ê·¹í ë¹í¨ì¨ì ì¼ ì ìë¤! Hibernateë ìì íê² ë°ë³´ê° ìëì´ì, ê·¸ê²ì ìë¡ì´ 공백ì
+ ì½ë ì
ì ê²½ì°(ì를 ë¤ì´ ë¹ì ì´ <literal>list.clear()</literal>를 í¸ì¶íì ê²½ì°)ì ê·¸ê²ì ííì§ ìì ê²ìì ìê³ ìë¤.
+ ì´ ê²½ì°ì, Hibernateë íëì <literal>DELETE</literal> ëª
ë ¹ì ë´ë¦´ ê²ì´ê³ ì°ë¦¬ë 모ë ííë¤!
+ </para>
+
+ <para>
+ ì°ë¦¬ê° ê¸¸ì´ 20ì¸ íëì ì½ë ì
ì í ê°ì ìì를 ì¶ê°íê³ ê·¸ë° ë¤ì ë ê°ì ììë¤ì ì ê±°íë¤ê³ ê°ì íì. Hibernateë
+ (ì½ë ì
ì´ bagê° ìë í) í ê°ì <literal>INSERT</literal> 문ì¥ê³¼ ë ê°ì <literal>DELETE</literal> 문ì¥ì ëª
ë ¹
+ ë´ë¦´ ê²ì´ë¤. ì´ê²ì íì¤í ë§ìì ë ë¤.
+ </para>
+
+ <para>
+ íì§ë§, ì°ë¦¬ê° ë ê°ì ììë¤ì ë¨ê²¨ëì± 18 ê°ì ììë¤ì ì ê±°íê³ ëì ì¸ ê°ì ìë¡ì´ ììë¤ì ì¶ê°íë¤ê³ ê°ì íì.
+ ë ê°ì§ ê°ë¥í ì²ë¦¬ ë°©ë²ë¤ì´ ì¡´ì¬íë¤.
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>íëì© ì´ ì¬ë ê°ì íë¤ì ìì í ë¤ìì ì¸ ê°ì íë¤ì ì½ì
ìí¨ë¤</para>
+ </listitem>
+ <listitem>
+ <para>
+ (í ê°ì SQL <literal>DELETE</literal>ë¡)ì ì²´ ì½ë ì
ì ìì íê³ ëª¨ë ë¤ì¯ê°ì íì¬ ììë¤ì (íëì©) insert ìí¨ë¤
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Hibernateë ë ë²ì§¸ ìµì
ì´ ìë§ ì´ ê²½ì°ì ë ë¹ ë¥´ë¤ë ì ì ì ë§í¼ 충ë¶í ì리íì§ ìë¤.(ê·¸ë¦¬ê³ Hibernateê° ê·¸ë ê² ì리í´ì§ë ê²ì í¬ë§
+ íë ê²ì ê°ë¥íì§ ìì ê²ì´ë¤; ê·¸ë° í¹ì§ì ë°ì´í°ë² ì´ì¤ í¸ë¦¬ê±°ë¤ ë±ì í¼ëì¤ë½ê² í ìë ìë¤.)
+ </para>
+
+ <para>
+ ë¤íí, ë¹ì ì ìëì ì½ë ì
ì í기ìí¤ê³ (ì를 ë¤ì´ 참조 í´ì íê³ ) 모ë íì¬ ììë¤ì ê°ì§ ìë¡ì´ ì´ê¸°íë ì½ë ì
ì
+ ë°íí¨ì¼ë¡ì¨ ì무ëë ì§ ì´ í¹ì§ì ê°ì ìí¬ ì ìë¤. ì´ê²ì ìê°ì´ íë¦ì ë°ë¼ ë§¤ì° ì ì©íê³ ê°ë ¥í´ì§ ì ìë¤.
+ </para>
+
+ <para>
+ ë¬¼ë¡ ë¨ íë²ì ìì (one-shot-delete)ë <literal>inverse="true"</literal>ë¡ ë§¤íë ì½ë ì
ë¤ì ì ì©ëì§ ìëë¤.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="performance-monitoring" revision="1">
+ <title>í¼í¬ë¨¼ì¤ 모ëí°ë§í기</title>
+
+ <para>
+ ìµì íë í¼í¬ë¨¼ì¤ ê´ë ¨ ì«ìë¤ì ëí 모ëí°ë§ê³¼ ì ê·¼ ìì´ë ë§ì´ ì¬ì©ëì§ ìëë¤. Hibernateë ê·¸ê²ì ë´ë¶ì ì¸ ì¤í¼ë ì´ì
ë¤ì ëí
+ ì ì²´ ììì í¹ì§ë¤ì ì ê³µíë¤. Hibernateìì Statisticsë <literal>SessionFactory</literal>ì ëí´ ì´ì© ê°ë¥íë¤.
+ </para>
+
+ <sect2 id="performance-monitoring-sf" revision="2">
+ <title>SessionFactory 모ëí°ë§ í기</title>
+
+ <para>
+ ë¹ì ì ë ê°ì§ ë°©ë²ë¤ë¡ <literal>SessionFactory</literal> metricsì ì ê·¼í ì ìë¤. ë¹ì ì 첫 ë²ì§¸ ìµì
ì
+ <literal>sessionFactory.getStatistics()</literal>를 í¸ì¶íê³ ë¹ì ì¤ì¤ë¡ <literal>Statistics</literal>를
+ ì½ê±°ë ëì¤íë ì´ íë ê²ì´ë¤.
+ </para>
+
+ <para>
+ ë§ì¼ ë¹ì ì´ <literal>StatisticsService</literal> MBeanì ì´ì© ê°ë¥íëë¡ í ê²½ì° Hibernateë ëí metrics를
+ ë°ííëë° JMX를 ì¬ì©í ì ìë¤. ë¹ì ì 모ë ë¹ì ì<literal>SessionFactory</literal>ì ëí´ í ê°ì MBean ëë
+ í©í 리 ë¹ í ê°ë¥¼ ì´ì© ê°ë¥íê² í ì ìë¤. ìµìíì êµ¬ì± ìì ë¤ì ë¤ì ì½ë를 ë³´ë¼:
+ </para>
+
+ <programlisting><![CDATA[// MBean service registration for a specific SessionFactory
+Hashtable tb = new Hashtable();
+tb.put("type", "statistics");
+tb.put("sessionFactory", "myFinancialApp");
+ObjectName on = new ObjectName("hibernate", tb); // MBean object name
+
+StatisticsService stats = new StatisticsService(); // MBean implementation
+stats.setSessionFactory(sessionFactory); // Bind the stats to a SessionFactory
+server.registerMBean(stats, on); // Register the Mbean on the server]]></programlisting>
+
+
+<programlisting><![CDATA[// MBean service registration for all SessionFactory's
+Hashtable tb = new Hashtable();
+tb.put("type", "statistics");
+tb.put("sessionFactory", "all");
+ObjectName on = new ObjectName("hibernate", tb); // MBean object name
+
+StatisticsService stats = new StatisticsService(); // MBean implementation
+server.registerMBean(stats, on); // Register the MBean on the server]]></programlisting>
+
+ <para>
+ TODO: ì´ê²ì ìë¯¸ê° ìë¤: 첫ë²ì§¸ ê²½ì°ì, ì°ë¦¬ë ì§ì MBeanì ê²ìíê³ ì¬ì©íë¤.
+ ë ë²ì§¸ ê²½ì°ì ì°ë¦¬ë JNDI ì´ë¦ì ì¬ì©í기 ì ì ì¸ì
í©í ë¦¬ê° ë³´ê´íê³ ìë JNDI ì´ë¦ì ë¶ì¬í´ì¼ íë¤.
+ <literal>hibernateStatsBean.setSessionFactoryJNDIName("my/JNDI/Name")</literal>ì ì¬ì©íë¼.
+ </para>
+ <para>
+ ë¹ì ì <literal>SessionFactory</literal>ì ëí 모ëí°ë§ì (ë¹)íì±í ìí¬ ì ìë¤
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ êµ¬ì± ì : <literal>hibernate.generate_statistics</literal>, ëí´í¸ë <literal>false</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+ <itemizedlist>
+ <listitem>
+ <para>
+ ì¤í ì : <literal>sf.getStatistics().setStatisticsEnabled(true)</literal> ëë
+ <literal>hibernateStatsBean.setStatisticsEnabled(true)</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Statistics(íµê³ëë¤)ì <literal>clear()</literal> ë©ìë를 ì¬ì©íì¬ íë¡ê·¸ëë° ë°©ìì¼ë¡ ì¬ì¤ì ë ì ìë¤. ìì½ì
+ <literal>logSummary()</literal> ë©ìë를 ì¬ì©íì¬ logger(info ë 벨)ìê² ì ì¡ë ì ìë¤.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-monitoring-metrics" revision="1">
+ <title>Metrics</title>
+
+ <para>
+ Hibernateë ë§¤ì° ê¸°ë³¸ì ì¸ ê²ììë¶í° ì´ë¤ ìë리ì¤ë¤ìë§ ê´ë ¨ë ì 문 ì ë³´ì ì´ë¥´ë ë§ì metrics를 ì ê³µíë¤.
+ 모ë ì´ì© ê°ë¥í ì¹´ì´í°ë¤ì <literal>Statistics</literal> interface APIìì 3ê°ì ì¹´í
ê³ ë¦¬ë¡ ì¤ëª
ëì´ ìë¤:
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ ì´ë ¤ì§ ì¸ì
ë¤ì ê°ì, ê²ìë JDBC 커ë¥ì
ë¤ì ê°ì ë±ê³¼ ê°ì ì¼ë°ì ì¸ <literal>Session</literal> ì¬ì©ì
+ ê´ë ¨ë metrics.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ì ì²´ì ì¼ë¡ ìí°í°ë¤, ì½ë ì
ë¤, ì§ìë¤, ê·¸ë¦¬ê³ ìºìë¤ì ê´ë ¨ë metrics(ì ì metricsë¡ ìë ¤ì ¸ ìì),
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ í¹ì í ìí°í°, ì½ë ì
, ì§ì ëë ìºì ììì ê´ë ¨ë ìì¸ metrics.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ì를 ë¤ì´ ë¹ì ì ìí°í°, ì½ë ì
, ì§ìë¤ì ìºì ì±ê³µì¨ ë° ì¤í¨ì¨, put(ìì 주, ìºì ìë, putt) ë¹ì¨, ì½ë ì
ë¤ê³¼ ì§ìë¤,
+ ê·¸ë¦¬ê³ íê· ì§ì ì구 ìê° ë±ì ì°¾ì ì ìë¤. ì ë°ë¦¬ì´ë¤ê° ìë°ìì ê·¼ì¬ì¹ì ì¢
ìë¨ì ììíë¼. Hibernateë JVM ì ë°ëì
+ ë¬¶ì¬ ìê³ , ëªëª íë«í¼ë¤ìì ì´ê²ì ì¬ì§ì´ ì½ 10ì´ê° ë ìë ìë¤.
+ </para>
+
+ <para>
+ ê°ë¨í getterë¤ì (ì를 ë¤ì´ í¹ì ìí°í°, ì½ë ì
, ìºì ììì 묶ì´ì§ ìì) ì ì metricsì ì ê·¼íëë° ì¬ì©ëë¤. ë¹ì ì
+ ê·¸ê²(í¹ì ìí°í°, ì½ë ì
, ëë ìºì ìì)ì ì´ë¦ì íµí´, ê·¸ë¦¬ê³ ì§ìë¤ì ëí ê·¸ê²ì HQL ëë SQL ííì íµí´ í¹ì ìí°í°,
+ ì½ë ì
, ëë ìºì ììì metricsì ì ê·¼í ì ìë¤. ì¶ê° ì ë³´ë <literal>Statistics</literal>, <literal>EntityStatistics</literal>,
+ <literal>CollectionStatistics</literal>, <literal>SecondLevelCacheStatistics</literal>, 그리ê³
+ <literal>QueryStatistics</literal>를 참조íë¼. ë¤ì ì½ëë ê°ë¨í ìì 를 ë³´ì¬ì¤ë¤:
+ </para>
+
+ <programlisting><![CDATA[Statistics stats = HibernateUtil.sessionFactory.getStatistics();
+
+double queryCacheHitCount = stats.getQueryCacheHitCount();
+double queryCacheMissCount = stats.getQueryCacheMissCount();
+double queryCacheHitRatio =
+ queryCacheHitCount / (queryCacheHitCount + queryCacheMissCount);
+
+log.info("Query Hit ratio:" + queryCacheHitRatio);
+
+EntityStatistics entityStats =
+ stats.getEntityStatistics( Cat.class.getName() );
+long changes =
+ entityStats.getInsertCount()
+ + entityStats.getUpdateCount()
+ + entityStats.getDeleteCount();
+log.info(Cat.class.getName() + " changed " + changes + "times" );]]></programlisting>
+
+ <para>
+ 모ë ìí°í°ë¤, ì½ë ì
ë¤, ì½ë ì
ë¤,ì§ìë¤ ê·¸ë¦¬ê³ ìì ìºìë¤ì ëí´ ìì
í기 ìí´, ë¹ì ì ë¤ì ë©ìëë¤ë¡ì ìí°í°ë¤,
+ ì½ë ì
ë¤, ì§ìë¤, ê·¸ë¦¬ê³ ìì ìºìë¤ì ëí ì´ë¦ë¤ì 목ë¡ì ê²ìí ì ìë¤: <literal>getQueries()</literal>,
+ <literal>getEntityNames()</literal>, <literal>getCollectionRoleNames()</literal>, 그리ê³
+ <literal>getSecondLevelCacheRegionNames()</literal>.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+</chapter>
\ No newline at end of file
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/persistent_classes.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/persistent_classes.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/persistent_classes.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,502 @@
+<chapter id="persistent-classes" revision="2">
+ <title>ìì í´ëì¤ë¤</title>
+
+ <para>
+ ìì í´ëì¤ë¤ì ë¹ì§ëì¤ ë¬¸ì ì ìí°í°ë¤(ì를 ë¤ì´ E-Commerce ì´í리ì¼ì´ì
ìì ê³ ê°ì´ë 주문)ì 구ííë
+ ì´í리ì¼ì´ì
ë´ì í´ëì¤ë¤ì´ë¤. ìì í´ëì¤ë¤ì ì¸ì¤í´ì¤ë¤ì ìì ìíì ìë ê²ì¼ë¡ ì í ê°ì£¼ëì§ ìëë¤ -
+ ëì ì íëì ì¸ì¤í´ì¤ë transient ëë detached ìíì¼ ì ìë¤.
+ </para>
+
+ <para>
+ Hibernateë ì´ë¤ í´ëì¤ë¤ì´ Plain Old Java Object (POJO) íë¡ê·¸ëë° ëª¨íì¼ë¡ì ìë ¤ì§, ëªëª ê°ë¨í
+ ê·ì¹ë¤ì ë°ë¥¼ ê²½ì°ì ê°ì¥ ì ëìíë¤. íì§ë§ ì´ë¤ ê·ì¹ë¤ ì¤ ì´ë¤ ê²ë ì´ë ¤ì´ ì¬ìë¤ì´ ìëë¤. ì§ì Hibernate3ë
+ ë¹ì ì ìì ê°ì²´ë¤ì í¹ì§ì ëí´ ë§¤ì° ì ì ê²ì ê°ì íë¤. ë¹ì ì ë¤ë¥¸ ë°©ë²ë¤ë¡ ëë©ì¸ 모íì ííí ì ìë¤ :
+ ì를 ë¤ì´ <literal>Map</literal> ì¸ì¤í´ì¤ì í¸ë¦¬ë¤ì ì¬ì©í기.
+ </para>
+
+ <sect1 id="persistent-classes-pojo">
+ <title>ê°ë¨í POJO ìì </title>
+
+ <para>
+ ëë¶ë¶ì ìë° ì´í리ì¼ì´ì
ë¤ì ê³ ìì´ê³¼ë¤ì íííë ìì í´ëì¤ë¥¼ íìë¡ íë¤.
+ </para>
+
+ <programlisting><![CDATA[package eg;
+import java.util.Set;
+import java.util.Date;
+
+public class Cat {
+ private Long id; // identifier
+
+ private Date birthdate;
+ private Color color;
+ private char sex;
+ private float weight;
+ private int litterId;
+
+ private Cat mother;
+ private Set kittens = new HashSet();
+
+ private void setId(Long id) {
+ this.id=id;
+ }
+ public Long getId() {
+ return id;
+ }
+
+ void setBirthdate(Date date) {
+ birthdate = date;
+ }
+ public Date getBirthdate() {
+ return birthdate;
+ }
+
+ void setWeight(float weight) {
+ this.weight = weight;
+ }
+ public float getWeight() {
+ return weight;
+ }
+
+ public Color getColor() {
+ return color;
+ }
+ void setColor(Color color) {
+ this.color = color;
+ }
+
+ void setSex(char sex) {
+ this.sex=sex;
+ }
+ public char getSex() {
+ return sex;
+ }
+
+ void setLitterId(int id) {
+ this.litterId = id;
+ }
+ public int getLitterId() {
+ return litterId;
+ }
+
+ void setMother(Cat mother) {
+ this.mother = mother;
+ }
+ public Cat getMother() {
+ return mother;
+ }
+ void setKittens(Set kittens) {
+ this.kittens = kittens;
+ }
+ public Set getKittens() {
+ return kittens;
+ }
+
+ // addKitten not needed by Hibernate
+ public void addKitten(Cat kitten) {
+ kitten.setMother(this);
+ kitten.setLitterId( kittens.size() );
+ kittens.add(kitten);
+ }
+}]]></programlisting>
+
+ <para>
+ ì¤ìí ë¤ ê°ì 주ì ê·ì¹ë¤ì´ ë¤ìì ìë¤:
+ </para>
+
+
+ <sect2 id="persistent-classes-pojo-constructor" revision="1">
+ <title>ìê·ë¨¼í¸ ìë ìì±ì를 구ííë¼ </title>
+
+ <para>
+ <literal>Cat</literal>ì ìê·ë¨¼í¸ ìë ìì±ì를 ê°ëë¤. 모ë ìì í´ëì¤ë¤ì Hibernateë
+ <literal>Constructor.newInstance()</literal>를 ì¬ì©íì¬ ê·¸ê²ë¤ì ì´ê¸°í ìí¬ ì ìëë¡ ëí´í¸ ìì±ì
+ (publicì´ ìë ì ìë¤)를 ê°ì ¸ì¼ íë¤. ì°ë¦¬ë Hibernate ë´ìì ë°íì íë½ì ìì±ì ìí ìµìíì
+ <emphasis>í¨í¤ì§</emphasis> ê°ìì±(visibility)를 ê°ì§ ëí´í¸ ìì±ì를 ê°ì§ ê²ì ê°ë ¥íê² ê¶ì¥íë¤.
+ </para>
+ </sect2>
+
+ <sect2 id="persistent-classes-pojo-identifier" revision="2">
+ <title>identifier íë¡í¼í°ë¥¼ ì ê³µíë¼(ìµì
)</title>
+
+ <para>
+ <literal>Cat</literal>ì <literal>id</literal>ë¡ ëª
ëª
ë íëì íë¡í¼í°ë¥¼ ê°ëë¤. ì´ íë¡í¼í°ë
+ ë°ì´í°ë² ì´ì¤ í
ì´ë¸ì íë¼ì´ë¨¸ë¦¬ í¤ ì»¬ë¼ì¼ë¡ 매íëë¤. ì´ íë¡í¼í°ë ì´ë¤ ê²ì¼ë¡ ëª
ëª
ë ìë ìê³ , ê·¸ê²ì íì
ì
+ ììì ìì íì
, ìì "wrapper" íì
, <literal>java.lang.String</literal> ëë <literal>java.util.Date</literal>ì¼
+ ì ìë¤. (ë§ì¼ ë¹ì ì 리거ì ë°ì´í°ë² ì´ì¤ í
ì´ë¸ì´ composite í¤ë¤ì ê°ê³ ìë¤ë©´, ë¹ì ì ì´ë¤ íì
ë¤ì ê°ì§
+ ì¬ì©ì ì ì í´ëì¤ë¥¼ ì¬ì©í ìë ìë¤ - ëì¤ì composite ìë³ìë¤ì ëí ì ì ë³´ë¼)
+ </para>
+
+ <para>
+ identifier íë¡í¼í°ë ì격íê² ìµì
ì´ë¤. ë¹ì ì ê·¸ê²ì ìëµí ìë ìê³ , Hibernateë¡ íì¬ê¸ ë´ë¶ì ì¼ë¡
+ ê°ì²´ ìë³ìë¤ì ì¶ì íëë¡ í ì ìë¤. íì§ë§ ì°ë¦¬ë ì´ê²ì ê¶ì¥íì§ ìëë¤.
+ </para>
+
+ <para>
+ ì¬ì¤, ì´ë¤ 기ë¥ì identifier íë¡í¼í°ë¥¼ ì ì¸íë í´ëì¤ë¤ì ëí´ìë§ ì´ì© ê°ë¥íë¤:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ detached ê°ì²´ë¤ì ëí Transitive reattachment(cascade update ëë cascade merge)
+ - <xref linkend="objectstate-transitive"/>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>Session.saveOrUpdate()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>Session.merge()</literal>
+ </para>
+ <para>
+ 를 ë³´ë¼
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ì°ë¦¬ë ë¹ì ì´ ìì í´ëì¤ë¤ì ëí´ ì¼ê´ëê² ëª
ëª
ë identifier íë¡í¼í°ë¤ì ì ì¸í ê²ì ê¶ì¥íë¤. ê²ë¤ê° ì°ë¦¬ë
+ ë¹ì ì´ nullable íì
(ì를 ë¤ì´ non-primitive)ì ì¬ì©í ê²ì ê¶ì¥íë¤.
+ </para>
+ </sect2>
+
+ <sect2 id="persistent-classes-pojo-final">
+ <title>finalì´ ìë í´ëì¤ë¤ì ì í¸íë¼(ìµì
)</title>
+ <para>
+ Hibernateì ì¤ì¬ í¹ì§ì¸, íë½ì(<emphasis>proxies</emphasis>)ë¤ì finalì´ ìë ìì í´ëì¤ë¤ ëë 모ë
+ public ë©ìëë¤ë¡ ì ì¸ë ì¸í°íì´ì¤ì 구íì¸ ìì í´ëì¤ë¤ì ìì¡´íë¤.
+ </para>
+ <para>
+ ë¹ì ì Hibernateë¡ ì¸í°íì´ì¤ë¥¼ 구ííì§ ìì <literal>final</literal> í´ëì¤ë¤ì ììí ìí¬ ì ìì§ë§
+ ë¹ì ì lazy ì°ê´ íì¹(lazy association fetching)ì ëí´ íë½ìë¤ì ì¬ì©í ì ìì ê²ì´ë¤ -ê·¸ê²ì í¼í¬ë¨¼ì¤
+ íëì ìí ë¹ì ì ìµì
ë¤ì ì íìí¬ ê²ì´ë¤.
+ </para>
+ <para>
+ ë¹ì ì ëí non-final í´ëì¤ë¤ ìì <literal>public final</literal> ë©ìëë¤ì ì ì¸íë ê²ì í¼í´ì¼ íë¤.
+ ë§ì¼ ë¹ì ì´ <literal>public final</literal> ë©ìë를 ê°ì§ í´ëì¤ë¥¼ ì¬ì©íê³ ì ìí ê²½ì°, ë¹ì ì
+ <literal>lazy="false"</literal>를 ì¤ì í¨ì¼ë¡ì¨ ëª
ìì ì¼ë¡ íë½ì±ì ì¬ì© ë¶ê°ë¥íëë¡ í´ì¼ íë¤.
+ </para>
+ </sect2>
+
+ <sect2 id="persistent-classes-pojo-accessors" revision="2">
+ <title>ìì íëë¤ì ìí accessorë¤ê³¼ mutatorë¤ì ì ì¸íë¼(ìµì
)</title>
+
+ <para>
+ <literal>Cat</literal>ì ê·¸ê²ì 모ë ìì íëë¤ì ëí´ accessor ë©ìëë¤ì ì ì¸íë¤. ë§ì ë¤ë¥¸ ORM ë구ë¤ì
+ ì¸ì¤í´ì¤ ë³ìë¤ì ì§ì ììí ìí¨ë¤. ì°ë¦¬ë ê´ê³í ì¤í¤ë§ì í´ëì¤ì ë´ë¶ì ì¸ ë°ì´í° êµ¬ì¡°ë¤ ì¬ì´ì ê°ì ì ì¸ ìë¨ì
+ ì ê³µíë ê²ì´ ë ì¢ë¤ê³ ë¯¿ê³ ìë¤. ëí´í¸ë¡ Hibernateë ìë°ë¹ì¦ ì¤íì¼ íë¡í¼í°ë¤ì ììí ìí¤ê³ , <literal>getFoo</literal>,
+ <literal>isFoo</literal>ì <literal>setFoo</literal> íìì ë©ìë ì´ë¦ë¤ì ì¸ì§íë¤. ë¹ì ì ì§ì ì¼ë¡
+ í¹ì íë¡í¼í°ì ëí ì§ì ì ì¸ íë ì ê·¼ì¼ë¡ ì íí ìë ìë¤.
+ </para>
+
+ <para>
+ íë¡í¼í°ë¤ì publicì¼ë¡ ì ì¸ë íìê° <emphasis>ìë¤</emphasis> - Hibernateë ëí´í¸ë¡
+ <literal>protected</literal> get/set ì ëë <literal>private</literal> get/set
+ ìì ê°ì§ íë¡í¼í°ë¥¼ ììí ìí¬ ì ìë¤.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="persistent-classes-inheritance">
+ <title>ìì 구íí기</title>
+
+ <para>
+ ìë¸í´ëì¤ë ëí 첫 ë²ì§¸ ê·ì¹ë¤ê³¼ ë ë²ì§¸ ê·ì¹ë¤ì 주ìí´ì¼ íë¤. ê·¸ê²ì ìí¼í´ëì¤ <literal>Cat</literal>ì¼ë¡ë¶í°
+ ê·¸ê²ì identifier íë¡í¼í°ë¥¼ ììë°ëë¤.
+ </para>
+
+ <programlisting><![CDATA[package eg;
+
+public class DomesticCat extends Cat {
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+ protected void setName(String name) {
+ this.name=name;
+ }
+}]]></programlisting>
+ </sect1>
+
+ <sect1 id="persistent-classes-equalshashcode" revision="1">
+ <title><literal>equals()</literal>ì <literal>hashCode()</literal> 구íí기</title>
+
+ <para>
+ ë§ì¼ ë¹ì ì´ ë¤ìì ê²½ì°ë¼ë©´, ë¹ì ì <literal>equals()</literal>ì <literal>hashCode()</literal>
+ ë©ìëë¤ì ì¤ë²ë¼ì´ë ìì¼ì¼ íë¤.
+ </para>
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ íëì <literal>Set</literal> ìì ìì í´ëì¤ë¤ì ì¸ì¤í´ì¤ë¤ì ì§ì´ë£ê³ ì ìëíê³
+ (many-valued ì°ê´ë¤ì ëí´ ê¶ì¥ëë ë°©ë²)
+ <emphasis>ê·¸ë¦¬ê³ </emphasis>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ detached ì¸ì¤í´ì¤ë¤ì reattachment(ì¬ì²¨ë¶)를 ì¬ì©íê³ ì ìëíë
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Hibernateë í¹ì session ë²ì ë´ììë§ persistent identity(ë°ì´í°ë² ì´ì¤ í)ê³¼ Java identityì ê°ìì ë³´ì¥íë¤.
+ ë°ë¼ì ì°ë¦¬ê° ë¤ë¥¸ ì¸ì
ë¤ìì ê²ìë ì¸ì¤í´ì¤ë¤ì í¼í©ìí¤ìë§ì, ì°ë¦¬ê° <literal>Set</literal>ë¤ì ëí´ ì ì미íê²
+ ë§ë¤ê³ ì ìí ê²½ì°, ì°ë¦¬ë <literal>equals()</literal>ì <literal>hashCode()</literal>를 구íí´ì¼ íë¤.
+ </para>
+
+ <para>
+ ê°ì¥ ëª
ë°±í ë°©ë²ì ë ê°ì²´ë¤ì identifier ê°ì ë¹êµí¨ì¼ë¡ì¨ <literal>equals()</literal>/<literal>hashCode()</literal>를
+ 구ííë ê²ì´ë¤. ë§ì¼ ê·¸ ê°ì´ ëì¼íë¤ë©´, ëë¤ ëì¼í ë°ì´í°ë² ì´ì¤ íì´ì´ì¼ íê³ , ê·¸ë¬ë¯ë¡ ê·¸ê²ë¤ì ê°ë¤(ëë¤ íëì
+ <literal>Set</literal>ì ì¶ê°ëë ê²½ì°ì, ì°ë¦¬ë <literal>Set</literal> ììì íëì ììë§ì ê°ê² ë ê²ì´ë¤).
+ ë¶ííê²ë, ì°ë¦¬ë ìì±ëë ìë³ìë¤ì ê°ë ê·¸ ì ê·¼ë²ì ì¬ì©í ì ìë¤! Hibernateë ì¤ì§ ìë³ì ê°ë¤ì ììí ëë ê°ì²´ë¤ì
+ í ë¹í ê²ì´ê³ , ìë¡ì´ ìì±ë ì¸ì¤í´ì¤ë ììì identifier ê°ì ê°ì§ ìì ê²ì´ë¤! ë§ì¼ ì¸ì¤í´ì¤ê° ì ì¥ëì§ ìê³ íì¬ íëì
+ <literal>Set</literal> ìì ìì ê²½ì°ì, ê·¸ê²ì ì ì¥íëê²ì íëì ìë³ì ê°ì ê·¸ ê°ì²´ìê² í ë¹í ê²ì´ë¤. ë§ì¼
+ <literal>equals()</literal>ì <literal>hashCode()</literal>ê° ê·¸ ìë³ì ê°ì 기ì´í ê²½ì°, hash ì½ëë
+ <literal>Set</literal>ì ê³ì½ì í기íì¬ ë³ê²½ë ê²ì´ë¤. ì´ ë¬¸ì ì ëí ì ì²´ ë
¼ìì ëí´ìë Hibernate ì¹ ì¬ì´í¸ë¥¼ ë³´ë¼.
+ ì´ê²ì Hibernate ìì ì´ ìë, ê°ì²´ identityì equalityì ê´í íµìì ìë° ìë¯¸ë¡ ìì ë
¸í¸íë¼.
+ </para>
+
+ <para>
+ ì°ë¦¬ë <emphasis>Business key equality</emphasis>를 ì¬ì©íì¬ <literal>equals()</literal>ì <literal>hashCode()</literal>를
+ 구íí ê² ê¶ì¥íë¤. Business key equalityë <literal>equals()</literal> ë©ìëê° ë¹ì§ëì¤ í¤, ì¦ ì¤ì¸ê³ìì ì°ë¦¬ì ì¸ì¤í´ì¤ë¥¼
+ ìë³íê² ë í¤(<emphasis>natural</emphasis> íë³´ í¤)를 íì±íë íë¡í¼í°ë¤ë§ì ë¹êµíë¤ë ì ì ì미íë¤ :
+ </para>
+
+ <programlisting><![CDATA[public class Cat {
+
+ ...
+ public boolean equals(Object other) {
+ if (this == other) return true;
+ if ( !(other instanceof Cat) ) return false;
+
+ final Cat cat = (Cat) other;
+
+ if ( !cat.getLitterId().equals( getLitterId() ) ) return false;
+ if ( !cat.getMother().equals( getMother() ) ) return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ int result;
+ result = getMother().hashCode();
+ result = 29 * result + getLitterId();
+ return result;
+ }
+
+}]]></programlisting>
+
+ <para>
+ íëì ë¹ì§ëì¤ í¤ë ë°ì´í°ë² ì´ì¤ íë¼ì´ë¨¸ë¦¬ í¤ íë³´ ë§í¼ ê²¬ê³ íì§ ììì¼ íë¤(<xref linkend="transactions-basics-identity"/>를 ë³´ë¼).
+ ëê° ë³ê²½í ì ìë íë¡í¼í° ëë ì ì¼í(unique) íë¡í¼í°ë ëê° ë¹ì§ëì¤ í¤ì ëí ì¢ì íë³´ë¤ì´ë¤.
+ </para>
+
+ </sect1>
+
+ <sect1 id="persistent-classes-dynamicmodels">
+ <title>ëì ì¸ ëª¨íë¤</title>
+
+ <para>
+ <emphasis>ë¤ì í¹ì§ë¤ì íì¬ ì¤íì ì¼ë¡ ê³ ë ¤ëê³ ìì¼ë©° ì¥ëìë ë³ê²½ë ì ììì ë
¸í¸íë¼.</emphasis>
+ </para>
+
+ <para>
+ ìì ìí°í°ë¤ì ë°ëì ì¤íìì POJO í´ëì¤ë¤ë¡ ëë ìë°ë¹ì¦ ê°ì²´ë¤ë¡ ííëì´ì¼í íìë ìë¤. Hibernateë
+ ëí (ì¤í ìì <literal>Map</literal>ë¤ì ê°ì§ <literal>Map</literal>ë¤ì ì¬ì©íì¬) ëì ì¸ ëª¨íë¤ì ì§ìíê³
+ DOM4J í¸ë¦¬ë¤ë¡ì ìí°í°ë¤ì ëí ííì ì§ìíë¤. ì´ ì ê·¼ë²ì¼ë¡, ë¹ì ì ìì í´ëì¤ë¤ì ìì±íì§ ìê³ , ì¤ì§ 매í íì¼ë¤
+ ë§ì ìì±íë¤.
+ </para>
+
+ <para>
+ ëí´í¸ë¡, Hibernateë íµì°ì POJO 모ëë¡ ëìíë¤. ë¹ì ì <literal>default_entity_mode</literal> êµ¬ì± ìµì
ì
+ ì¬ì©íì¬ í¹ë³í <literal>SessionFactory</literal>ì ëí´ ëí´í¸ ìí°í° íí 모ë를 ì¤ì í ì ìë¤
+ (<xref linkend="configuration-optional-properties"/>ì ë³´ë¼).
+ </para>
+
+ <para>
+ ë¤ì ìì ë¤ì<literal>Map</literal>ë¤ì ì¬ì©íë ííì ì¤ëª
íë¤. 먼ì 매í íì¼ìì, <literal>entity-name</literal>ì
+ í´ëì¤ ì´ë¦ ëì ì(ëë í´ëì¤ ì´ë¦ì ë§ë¶ì¬) ì ì¸ëì´ì¼ íë¤:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class entity-name="Customer">
+
+ <id name="id"
+ type="long"
+ column="ID">
+ <generator class="sequence"/>
+ </id>
+
+ <property name="name"
+ column="NAME"
+ type="string"/>
+
+ <property name="address"
+ column="ADDRESS"
+ type="string"/>
+
+ <many-to-one name="organization"
+ column="ORGANIZATION_ID"
+ class="Organization"/>
+
+ <bag name="orders"
+ inverse="true"
+ lazy="false"
+ cascade="all">
+ <key column="CUSTOMER_ID"/>
+ <one-to-many class="Order"/>
+ </bag>
+
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ ì¬ì§ì´ ë¹ë¡ ì°ê´ë¤ì´ ëì(target) í´ëì¤ ì´ë¦ë¤ì ì¬ì©íì¬ ì ì¸ë ì§ë¼ë, ì°ê´ë¤ì ëì(target) íì
ì ëí POJOê° ìë
+ ëì ì¸ ìí°í°ì¼ ì ììì ë
¸í¸íë¼.
+ </para>
+
+ <para>
+ <literal>SessionFactory</literal>ì ëí ëí´í¸ ìí°í° 모ë를 <literal>dynamic-map</literal>ì¼ë¡ ì¤ì í íì,
+ ì°ë¦¬ë <literal>Map</literal>ë¤ì ê°ì§ <literal>Map</literal>ë¤ì ëí´ ì¤í ìì ìì
í ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[Session s = openSession();
+Transaction tx = s.beginTransaction();
+Session s = openSession();
+
+// Create a customer
+Map david = new HashMap();
+david.put("name", "David");
+
+// Create an organization
+Map foobar = new HashMap();
+foobar.put("name", "Foobar Inc.");
+
+// Link both
+david.put("organization", foobar);
+
+// Save both
+s.save("Customer", david);
+s.save("Organization", foobar);
+
+tx.commit();
+s.close();]]></programlisting>
+
+ <para>
+ dynamic 매íì ì¥ì ë¤ì ìí°í° í´ëì¤ êµ¬íì ëí íì ìì´ë íë¡í íì´íì ìí ë¹ ë¥¸ ì í ìê°ì´ë¤. íì§ë§ ë¹ì ì
+ ì»´íì¼ ì íì
ì²´í¹ì ìê³ ì¤í ìì ë§ì ìì¸ìí©ë¤ì ë¤ë£¨ê² ë ê²ì´ë¤. Hibernate 매í ëë¶ì,
+ ëì¤ì ê³ ì í ëë©ì¸ 모í 구íì ìë¨ì ì¶ê°íë ê²ì´ íì©ëì´ì, ë°ì´í°ë² ì´ì¤ ì¤í¤ë§ê° ì½ê² ì ê·í ëê³ ìë¦¬ê° ì¸ë ¤ í¼ì§ ì ìë¤.
+ </para>
+
+ <para>
+ ìí°í° íí 모ëë¤ì ëí íëì ë¨ì <literal>Session</literal> 기ì¤ì ëí´ ì¤ì ë ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[Session dynamicSession = pojoSession.getSession(EntityMode.MAP);
+
+// Create a customer
+Map david = new HashMap();
+david.put("name", "David");
+dynamicSession.save("Customer", david);
+...
+dynamicSession.flush();
+dynamicSession.close()
+...
+// Continue on pojoSession
+]]></programlisting>
+
+
+ <para>
+ <literal>EntityMode</literal>를 ì¬ì©íë <literal>getSession()</literal>ì ëí í¸ì¶ì
+ <literal>SessionFactory</literal>ê° ìë, <literal>Session</literal> APIì ëí ê²ìì ë
¸í¸í길 ë°ëë¤.
+ ê·¸ ë°©ë²ì¼ë¡, ìë¡ì´ <literal>Session</literal>ì 기본 JDBC 커ë¥ì
, í¸ëìì
, ê·¸ë¦¬ê³ ë¤ë¥¸ 컨í
ì¤í¸ ì 보를 ê³µì íë¤.
+ ì´ê²ì ë¹ì ì´ ë ë²ì§¸ <literal>Session</literal> ììì <literal>flush()</literal>ì <literal>close()</literal>를
+ í¸ì¶íì§ ë§ìì¼ íê³ , ëí í¸ëìì
ë° ì»¤ë¥ì
í¸ë¤ë§ì 주ë ìì
ë¨ììê² ë§¡ê¸´ë¤ë ì ì ì미íë¤.
+ </para>
+
+ <para>
+ XML íí ê°ì©ì±ë¤ì ëí ì¶ê° ì ë³´ë <xref linkend="xml"/>ìì ì°¾ì ì ìë¤.
+ </para>
+
+ </sect1>
+
+ <sect1 id="persistent-classes-tuplizers" revision="1">
+ <title>Tuplizerë¤</title>
+
+ <para>
+ <literal>org.hibernate.tuple.Tuplizer</literal>, ê·¸ë¦¬ê³ ê·¸ê²ì ìë¸-ì¸í°íì´ì¤ë¤ì ë°ì´í°ì ì¡°ê°ì ëí
+ í¹ë³í ííì <literal>org.hibernate.EntityMode</literal>ê° ì£¼ì´ì§ë©´ ê·¸ ííì ê´ë¦¬íë ì±
ìì´ ìë¤. ë§ì¼
+ 주ì´ì§ ë°ì´í° ì¡°ê°ì´ íëì ë°ì´í° êµ¬ì¡°ë¡ ê°ì£¼ë ê²½ì°, ê·¸ë íëì tuplizerë ê·¸ë° ë°ì´í° 구조를 ìì±ìí¤ë ë°©ë²ê³¼
+ ê·¸ë° ë°ì´í° 구조ë¡ë¶í° ê°ë¤ì ì¶ì¶ìí¤ë ë°©ë² ê·¸ë¦¬ê³ ê·¸ë° ë°ì´í°êµ¬ì¡° ìì¼ë¡ ê°ë¤ì ì½ì
ìí¤ë ë°©ë²ì ìê³ ìë ê²ì´ë¤.
+ ì를 ë¤ì´, POJO ìí°í° 모ëì ê²½ì°, ëìíë tuplizerë ê·¸ê²ì ìì±ì를 íµí´ POJO를 ìì±ìí¤ë ë°©ë², ê·¸ë¦¬ê³ ì ìë
+ íë¡í¼í° ì ê·¼ìë¤ì ì¬ì©íì¬ POJO íë¡í¼í°ë¤ì ì ê·¼íë ë°©ë²ì ìë¤.
+ <literal>org.hibernate.tuple.entity.EntityTuplizer</literal> ì¸í°íì´ì¤ì
+ <literal>org.hibernate.tuple.component.ComponentTuplizer</literal> ì¸í°íì´ì¤ì ìí´ ííëë ë ê°ì§ ê³ ê¸ ì íì
+ Tuplizerë¤ì´ ì¡´ì¬íë¤. <literal>EntityTuplizer</literal>ë¤ì ìí°í°ë¤ì ê´í´ìë ìì ì¸ê¸ë ê³ì½ë¤ì 매íí
+ ì±
ìì´ ìë ë°ë©´ì, <literal>ComponentTuplizer</literal>ë¤ì ì»´í¬ëí¸ë¤ì ëí´ìë ëì¼í ê²ì ííë¤.
+ </para>
+
+ <para>
+ ì¬ì©ìë¤ì ëí ê·¸ë¤ ìì ì tuplizerë¤ì íë¬ê·¸ ìí¬ ì ìë¤. ìë§ ë¹ì ì dynamic-map entity-mode ëìì ì¬ì©ëë
+ <literal>java.util.HashMap</literal> ëì ì íëì <literal>java.util.Map</literal> 구íì íìë¡ íë¤;
+ ëë ìë§ ë¹ì ì ëí´í¸ë¡ ì¬ì©ëë ë°©ë ë³´ë¤ë íëì ë¤ë¥¸ ë¤ë¥¸ íë¦ì ì°ì¶ ë°©ë를 íìë¡ íë¤. ëë¤ íëì ë§ì¶¤í tuplizer를
+ ì ìí¨ì¼ë¡ì¨ ì±ì·¨ë ê²ì´ë¤. Tuplizerë¤ ì ìë¤ì ê·¸ê²ë¤ì´ ê´ë¦¬í ìë¨ì¸ ìí°í° 매í ëë ì»´í¬ëí¸ ë§¤íì 첨ë¶ëë¤. ì°ë¦¬ì
+ ê³ ê° ìí°í°ì ëí ìì ë¡ ëëìê°ë©´:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+ <class entity-name="Customer">
+ <!--
+ Override the dynamic-map entity-mode
+ tuplizer for the customer entity
+ -->
+ <tuplizer entity-mode="dynamic-map"
+ class="CustomMapTuplizerImpl"/>
+
+ <id name="id" type="long" column="ID">
+ <generator class="sequence"/>
+ </id>
+
+ <!-- other properties -->
+ ...
+ </class>
+</hibernate-mapping>
+
+
+public class CustomMapTuplizerImpl
+ extends org.hibernate.tuple.entity.DynamicMapEntityTuplizer {
+ // override the buildInstantiator() method to plug in our custom map...
+ protected final Instantiator buildInstantiator(
+ org.hibernate.mapping.PersistentClass mappingInfo) {
+ return new CustomMapInstantiator( mappingInfo );
+ }
+
+ private static final class CustomMapInstantiator
+ extends org.hibernate.tuple.DynamicMapInstantitor {
+ // override the generateMap() method to return our custom map...
+ protected final Map generateMap() {
+ return new CustomMap();
+ }
+ }
+}]]></programlisting>
+
+
+ </sect1>
+
+ <para>
+ TODO: property í¨í¤ì§ì proxy í¨í¤ì§ ë´ì user-extension íë ììì 문ìí í ê².
+ </para>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/query_criteria.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/query_criteria.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/query_criteria.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,418 @@
+<chapter id="querycriteria">
+ <title>Criteria ì§ìë¤</title>
+
+ <para>
+ Hibernateë ì§ê´ì ì¸, íì¥ ê°ë¥í criteria query API를 í¹ì§ ì§ëë¤.
+ </para>
+
+ <sect1 id="querycriteria-creating">
+ <title><literal>Criteria</literal> ì¸ì¤í´ì¤ ìì±í기</title>
+
+ <para>
+ <literal>org.hibernate.Criteria</literal>ì¸í°íì´ì¤ë í¹ì ìì í´ëì¤ì ëí ì§ì를 íííë¤.
+ <literal>Session</literal>ì <literal>Criteria</literal> ì¸ì¤í´ì¤ë¤ì ëí í©í 리ì´ë¤.
+ </para>
+
+ <programlisting><![CDATA[Criteria crit = sess.createCriteria(Cat.class);
+crit.setMaxResults(50);
+List cats = crit.list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-narrowing">
+ <title>ê²°ê³¼ ì
ì íí기</title>
+
+ <para>
+ ê°ë³ì ì¸ ì§ì 기ì¤ì <literal>org.hibernate.criterion.Criterion</literal> ì¸í°íì´ì¤ì ì¸ì¤í´ì¤ì´ë¤.
+ <literal>org.hibernate.criterion.Restrictions</literal> í´ëì¤ë ì´ë¤ 미리 ë§ë¤ì´ì§ <literal>Criterion</literal>
+ íì
ë¤ì ì»ë í©í 리 ë©ìëë¤ì ì ìíë¤.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "Fritz%") )
+ .add( Restrictions.between("weight", minWeight, maxWeight) )
+ .list();]]></programlisting>
+
+ <para>
+ ì íë¤ì ë
¼ë¦¬ì ì¼ë¡ 그룹ì§ìì§ ìë ìë¤.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "Fritz%") )
+ .add( Restrictions.or(
+ Restrictions.eq( "age", new Integer(0) ),
+ Restrictions.isNull("age")
+ ) )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.in( "name", new String[] { "Fritz", "Izi", "Pk" } ) )
+ .add( Restrictions.disjunction()
+ .add( Restrictions.isNull("age") )
+ .add( Restrictions.eq("age", new Integer(0) ) )
+ .add( Restrictions.eq("age", new Integer(1) ) )
+ .add( Restrictions.eq("age", new Integer(2) ) )
+ ) )
+ .list();]]></programlisting>
+
+ <para>
+ 미리 ë§ë¤ì´ì§ criterion íì
ë¤(<literal>Restrictions</literal> ìë¸í´ëì¤ë¤)ì ììì´ ê½¤ ì¡´ì¬íì§ë§, í¹í ì ì©í ê²ì
+ ë¹ì ì¼ë¡ íì¬ê¸ SQLì ì§ì ì§ì íëë¡ í´ì¤ë¤.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.sqlRestriction("lower({alias}.name) like lower(?)", "Fritz%", Hibernate.STRING) )
+ .list();]]></programlisting>
+
+ <para>
+ ì§ìë ìí°í°ì í aliasì ìí´ ëì²´ë <literal>{alias}</literal> placeholder.
+ </para>
+
+ <para>
+ criterionì ì»ë ëìì ì¸ ì ê·¼ë²ì <literal>Property</literal> ì¸ì¤í´ì¤ë¡ë¶í° ê·¸ê²ì ì»ë ê²ì´ë¤. ë¹ì ì
+ <literal>Property.forName()</literal>ì í¸ì¶íì¬ <literal>Property</literal>를 ìì±ìí¬ ì ìë¤.
+ </para>
+
+ <programlisting><![CDATA[
+Property age = Property.forName("age");
+List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.disjunction()
+ .add( age.isNull() )
+ .add( age.eq( new Integer(0) ) )
+ .add( age.eq( new Integer(1) ) )
+ .add( age.eq( new Integer(2) ) )
+ ) )
+ .add( Property.forName("name").in( new String[] { "Fritz", "Izi", "Pk" } ) )
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-ordering">
+ <title>ê²°ê³¼ë¤ì ììì§ì°ê¸°(ordering)</title>
+
+ <para>
+ ë¹ì ì <literal>org.hibernate.criterion.Order</literal>를 ì¬ì©íì¬ ê²°ê³¼ë¤ì ìì(ordering)ì§ì¸ ì ìë¤.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "F%")
+ .addOrder( Order.asc("name") )
+ .addOrder( Order.desc("age") )
+ .setMaxResults(50)
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Property.forName("name").like("F%") )
+ .addOrder( Property.forName("name").asc() )
+ .addOrder( Property.forName("age").desc() )
+ .setMaxResults(50)
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-associations" revision="2">
+ <title>ì°ê´ë¤</title>
+
+ <para>
+ ë¹ì ì <literal>createCriteria()</literal>를 ì¬ì©íì¬ ì°ê´ë¤ì ë¤ë¹ê²ì´í¸í¨ì¼ë¡ì¨ ê´ê³ë ìí°í°ë¤ì ëí 컨ì¤í¸ë ì¸í¸ë¤ì
+ ì½ê² ì§ì í ì ìë¤.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "F%") )
+ .createCriteria("kittens")
+ .add( Restrictions.like("name", "F%") )
+ .list();]]></programlisting>
+
+ <para>
+ ë ë²ì§¸ <literal>createCriteria()</literal>ë <literal>Criteria</literal>ì ìë¡ì´ ì¸ì¤í´ì¤ë¥¼ ë°ííë©°, ê·¸ê²ì
+ <literal>kittens</literal> ì½ë ì
ì ììë¤ì 참조íë¤ë ì ì ë
¸í¸íë¼.
+ </para>
+
+ <para>
+ ë¤ì ëì²´ íìì ì´ë¤ íê²½ë¤ìì ì ì©íë¤.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .createAlias("kittens", "kt")
+ .createAlias("mate", "mt")
+ .add( Restrictions.eqProperty("kt.name", "mt.name") )
+ .list();]]></programlisting>
+
+ <para>
+ (<literal>createAlias()</literal>ë <literal>Criteria</literal>ì ìë¡ì´ ì¸ì¤í´ì¤ë¥¼ ìì±ìí¤ì§ ìëë¤.)
+ </para>
+
+ <para>
+ ìì ë ê°ì ì§ìë¤ì ìí´ ë°íë <literal>Cat</literal> ì¸ì¤í´ì¤ë¤ì ìí´ ë³´ê´ë kittens ì½ë ì
ë¤ì criteriaì ìí´
+ ì¬ì -íí°ë§ëì§ <emphasis>ìëë¤</emphasis>ë ì ì ë
¸í¸íë¼! ë§ì¼ ë¹ì ì´ criteria(기ì¤)ê³¼ ì¼ì¹íë ê³ ìì´ ìë¼ë¤ì
+ ë¨ì§ ê²ìíê³ ì ìí ê²½ì°, ë¹ì ì íëì <literal>ResultTransformer</literal>를 ì¬ì©í´ì¼ íë¤.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .createCriteria("kittens", "kt")
+ .add( Restrictions.eq("name", "F%") )
+ .setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP)
+ .list();
+Iterator iter = cats.iterator();
+while ( iter.hasNext() ) {
+ Map map = (Map) iter.next();
+ Cat cat = (Cat) map.get(Criteria.ROOT_ALIAS);
+ Cat kitten = (Cat) map.get("kt");
+}]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-dynamicfetching" revision="1">
+ <title>ëì ì¸ ì°ê´ íì¹</title>
+
+ <para>
+ ë¹ì ì <literal>setFetchMode()</literal>를 ì¬ì©íì¬ ì¤í ìì ì°ê´ íì¹ ì미를 ì§ì í ì ìë¤.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "Fritz%") )
+ .setFetchMode("mate", FetchMode.EAGER)
+ .setFetchMode("kittens", FetchMode.EAGER)
+ .list();]]></programlisting>
+
+ <para>
+ ì´ ì§ìë outer ì¡°ì¸ì¼ë¡ <literal>mate</literal>ì <literal>kittens</literal> 모ë를 íì¹í ê²ì´ë¤. ì¶ê° ì ë³´ë
+ <xref linkend="performance-fetching"/>ì ë³´ë¼.
+ </para>
+
+ </sect1>
+
+ <sect1 id="querycriteria-examples">
+ <title>ìì ì§ìë¤</title>
+
+ <para>
+ <literal>org.hibernate.criterion.Example</literal> í´ëì¤ë 주ì´ì§ ì¸ì¤í´ì¤ë¡ë¶í° ì§ì 기ì¤(criterion)ì 구조í
+ ìí¤ë ê²ì ë¹ì ìê² íì©í´ì¤ë¤.
+ </para>
+
+ <programlisting><![CDATA[Cat cat = new Cat();
+cat.setSex('F');
+cat.setColor(Color.BLACK);
+List results = session.createCriteria(Cat.class)
+ .add( Example.create(cat) )
+ .list();]]></programlisting>
+
+ <para>
+ ë²ì íë¡í¼í°ë¤, ìë³ìë¤, ì°ê´ê´ê³ë¤ì´ 무ìëë¤. ëí´í¸ë¡ null ê° íë¡í¼í°ë¤ì´ ì ì¸ëë¤.
+ </para>
+
+ <para>
+ ë¹ì ì <literal>Example</literal>ì´ ì ì©ëë ë°©ë²ì ì¡°ì í ì ìë¤.
+ </para>
+
+ <programlisting><![CDATA[Example example = Example.create(cat)
+ .excludeZeroes() //exclude zero valued properties
+ .excludeProperty("color") //exclude the property named "color"
+ .ignoreCase() //perform case insensitive string comparisons
+ .enableLike(); //use like for string comparisons
+List results = session.createCriteria(Cat.class)
+ .add(example)
+ .list();]]></programlisting>
+
+ <para>
+ ë¹ì ì ì°ê´ë ê°ì²´ë¤ì ëí criteria(기ì¤)ì ìì¹ì§ì°ëë° examples를 ì¬ì©í ì ìë¤.
+ </para>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .add( Example.create(cat) )
+ .createCriteria("mate")
+ .add( Example.create( cat.getMate() ) )
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-projection">
+ <title>Projections, aggregation ê·¸ë¦¬ê³ grouping</title>
+ <para>
+ <literal>org.hibernate.criterion.Projections</literal> í´ëì¤ë <literal>Projection</literal> ì¸ì¤í´ì¤ë¤ì
+ ëí í©í 리ì´ë¤. ì°ë¦¬ë <literal>setProjection()</literal>ì í¸ì¶íì¬ íëì ì§ìì projection(í¬ì¬,í¬ì)ì ì ì©ìí¨ë¤.
+ </para>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.rowCount() )
+ .add( Restrictions.eq("color", Color.BLACK) )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.projectionList()
+ .add( Projections.rowCount() )
+ .add( Projections.avg("weight") )
+ .add( Projections.max("weight") )
+ .add( Projections.groupProperty("color") )
+ )
+ .list();]]></programlisting>
+
+ <para>
+ criteria ì§ì ë´ììë ëª
ìì ì¸ "group by"ê° íìì ì´ì§ ìë¤. ì´ë¤ projection íì
ë¤ì
+ <emphasis>grouping projections</emphasis>ë¤ì´ê²ë ì ìëê³ , ê·¸ê²ì ëí SQL <literal>group by</literal> ì ìì
+ ëíëë¤.
+ </para>
+
+ <para>
+ aliasë ì íì ì¼ë¡ projectionì í ë¹ë ì ìì´ì, í¬ì¬ë(projected) ê°ì ì í(restriction)ë¤ ëë orderingë¤ ë´ìì
+ 참조ë ì ìë¤. ë¤ìì ì´ê²ì ííë ë ê°ì ë¤ë¥¸ ë°©ë²ë¤ì´ë¤:
+ </para>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.alias( Projections.groupProperty("color"), "colr" ) )
+ .addOrder( Order.asc("colr") )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.groupProperty("color").as("colr") )
+ .addOrder( Order.asc("colr") )
+ .list();]]></programlisting>
+
+ <para>
+ <literal>alias()</literal> ë©ìëì <literal>as()</literal> ë©ìëë ë ë¤ë¥¸ alias ë
+ <literal>Projection</literal>ì ì¸ì¤í´ì¤ ë´ì íëì projection ì¸ì¤í´ì¤ë¥¼ ê°ë¨íê² í¬ì¥íë¤. ì§ë¦ê¸¸ë¡ì,
+ ë¹ì ì´ projectionì projection 리ì¤í¸ì ì¶ê°í ë ë¹ì ì alias를 í ë¹í ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.projectionList()
+ .add( Projections.rowCount(), "catCountByColor" )
+ .add( Projections.avg("weight"), "avgWeight" )
+ .add( Projections.max("weight"), "maxWeight" )
+ .add( Projections.groupProperty("color"), "color" )
+ )
+ .addOrder( Order.desc("catCountByColor") )
+ .addOrder( Order.desc("avgWeight") )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Domestic.class, "cat")
+ .createAlias("kittens", "kit")
+ .setProjection( Projections.projectionList()
+ .add( Projections.property("cat.name"), "catName" )
+ .add( Projections.property("kit.name"), "kitName" )
+ )
+ .addOrder( Order.asc("catName") )
+ .addOrder( Order.asc("kitName") )
+ .list();]]></programlisting>
+
+ <para>
+ ë¹ì ì ëí projectionë¤ì íííëë° <literal>Property.forName()</literal>ì ì¬ì©í ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Property.forName("name") )
+ .add( Property.forName("color").eq(Color.BLACK) )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.projectionList()
+ .add( Projections.rowCount().as("catCountByColor") )
+ .add( Property.forName("weight").avg().as("avgWeight") )
+ .add( Property.forName("weight").max().as("maxWeight") )
+ .add( Property.forName("color").group().as("color" )
+ )
+ .addOrder( Order.desc("catCountByColor") )
+ .addOrder( Order.desc("avgWeight") )
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-detachedqueries">
+ <title>Detached ì§ìë¤ê³¼ ìë¸ì§ìë¤</title>
+ <para>
+ <literal>DetachedCriteria</literal> í´ëì¤ë ë¹ì ìê² ì¸ì
ììì ì¸ë¶ìì ì§ì를 ìì±ìí¤ëë¡ íê³ , ê·¸ë° ë¤ì ëì¤ì
+ ì´ë¤ ììì <literal>Session</literal>ì ì¬ì©íì¬ ê·¸ê²ì ì¤ííëë¡ íë¤.
+ </para>
+
+ <programlisting><![CDATA[DetachedCriteria query = DetachedCriteria.forClass(Cat.class)
+ .add( Property.forName("sex").eq('F') );
+
+Session session = ....;
+Transaction txn = session.beginTransaction();
+List results = query.getExecutableCriteria(session).setMaxResults(100).list();
+txn.commit();
+session.close();]]></programlisting>
+
+ <para>
+ <literal>DetachedCriteria</literal>ë ëí ìë¸ì§ì를 íííëë° ì¬ì©ëë¤. ìë¸ì§ìë¤ì í¬í¨íë Criterion ì¸ì¤í´ì¤ë¤ì
+ <literal>Subqueries</literal> ëë <literal>Property</literal>를 íµí´ ì»ì´ì§ ì ìë¤.
+ </para>
+
+ <programlisting><![CDATA[DetachedCriteria avgWeight = DetachedCriteria.forClass(Cat.class)
+ .setProjection( Property.forName("weight").avg() );
+session.createCriteria(Cat.class)
+ .add( Property.forName("weight).gt(avgWeight) )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[DetachedCriteria weights = DetachedCriteria.forClass(Cat.class)
+ .setProjection( Property.forName("weight") );
+session.createCriteria(Cat.class)
+ .add( Subqueries.geAll("weight", weights) )
+ .list();]]></programlisting>
+
+ <para>
+ ì¬ì§ì´ ìê´ê´ê³ ì§ìì§ ìë¸ì§ìë¤ì´ ê°ë¥íë¤:
+ </para>
+
+ <programlisting><![CDATA[DetachedCriteria avgWeightForSex = DetachedCriteria.forClass(Cat.class, "cat2")
+ .setProjection( Property.forName("weight").avg() )
+ .add( Property.forName("cat2.sex").eqProperty("cat.sex") );
+session.createCriteria(Cat.class, "cat")
+ .add( Property.forName("weight).gt(avgWeightForSex) )
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <!--TODO: ResultSetTransformer + aliasing. AliasToBeanTransformerë -JDO2ìì setResultClassì ì ì¬í- ììì ì¸
+ ì¬ì©ì ê°ì²´ë¤ì ë°ííë ê²ì íì©íë¤. ResultTransformerì ëí ì¼ë°ì ì¸ ì¬ì©ì´ ëí ì¤ëª
ë ì ìë¤. -->
+
+ <sect1 id="query-criteria-naturalid">
+ <title>natural ìë³ìì ìí ì§ìë¤</title>
+
+ <para>
+ ëë¶ë¶ì ì§ìë¤ìì, criteria ì§ìë¤ì í¬í¨íì¬, ì§ì ìºìë ë§¤ì° í¨ì¨ì ì´ì§ ìë¤. ìëíë©´ ì§ì ìºì ë¹ì í¨ì±ì´
+ ë무 ì주 ë°ìí기 ë문ì´ë¤. íì§ë§, ì°ë¦¬ê° ìºì ë¹ì í¨ì± ìê³ ë¦¬ì¦ì ìµì í ìí¬ ì ìë í ê°ì§ í¹ë³í ì¢
ë¥ì ì§ìê°
+ ì¡´ì¬íë¤: ìì natural í¤ì ìí 룩ì
. ëªëª ì´í리ì¼ì´ì
ë¤ìì, ì´ë° ì¢
ë¥ì ì§ìê° ì주 ë°ìíë¤. criteria APIë
+ ì´ ì°ìì를 ìí í¹ë³í ì¤ë¹ë¥¼ ì ê³µíë¤.
+ </para>
+
+ <para>
+ 첫ë²ì§¸ë¡ ë¹ì ì <literal><natural-id></literal>를 ì¬ì©íì¬ ë¹ì ì ìí°í°ì ëí natural í¤ë¥¼ 매í
+ ìì¼ì¼ íê³ , second-level ìºì ì¬ì©ì ê°ë¥íê² í´ì¼ íë¤.
+ </para>
+
+ <programlisting><![CDATA[<class name="User">
+ <cache usage="read-write"/>
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <natural-id>
+ <property name="name"/>
+ <property name="org"/>
+ </natural-id>
+ <property name="password"/>
+</class>]]></programlisting>
+
+ <para>
+ ì´ ê¸°ë¥ì <emphasis>ê°ë³ì± ìë</emphasis> natural í¤ë¤ì ê°ì§ ìí°í°ë¤ì ì©ëë¡ ê³ ìëì´ ìì§ ììì ë
¸í¸íë¼.
+ </para>
+
+ <para>
+ ë¤ìì¼ë¡ , Hibernate ì§ì ìºì를 ì¬ì© ê°ë¥íëë¡ íë¼.
+ </para>
+
+ <para>
+ ì´ì <literal>Restrictions.naturalId()</literal>ë ìºì ìê³ ë¦¬ì¦ì ë³´ë¤ í¨ì¨ì ì¼ë¡ ì¬ì©í ì ìëë¡ ì°ë¦¬ìê² íì©í´ì¤ë¤.
+ </para>
+
+ <programlisting><![CDATA[session.createCriteria(User.class)
+ .add( Restrictions.naturalId()
+ .set("name", "gavin")
+ .set("org", "hb")
+ ).setCacheable(true)
+ .uniqueResult();]]></programlisting>
+
+ </sect1>
+
+</chapter>
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/query_hql.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/query_hql.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/query_hql.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1178 @@
+<chapter id="queryhql" revision="1">
+ <title>HQL: íì´ë²ë¤ì´í¸ ì§ì ì¸ì´(Hibernate Query Language)</title>
+
+ <para>
+ Hibernateë (ì주 ìëì ì¼ë¡) SQLê³¼ ë§¤ì° í¡ì¬íê² ë³´ì´ë ê·¹í ê°ë ¥í ì§ì ì¸ì´ë¥¼ 구ë¹íê³ ìë¤. ê·¸ë¬ë ê·¸ 구문ì ìí´ ì°ë¡±ë¹íì§ ë§ë¼;
+ HQLì ìì, ë¤íì± ê·¸ë¦¬ê³ ì°ê´ê³¼ ê°ì ê°ë
ë¤ì ì´í´íì¬ì, ì ì²´ì ì¼ë¡ ê°ì²´ ì§í¥ì ì´ë¤.
+ </para>
+
+ <sect1 id="queryhql-casesensitivity">
+ <title>ëì문ì 구ë¶</title>
+
+ <para>
+ ì§ìë¤ì Java í´ëì¤ë¤ê³¼ íë¡í¼í°ë¤ì ì´ë¦ë¤ì ì ì¸íë©´ ëì문ì를 구ë¶íì§ ìëë¤. ë°ë¼ì <literal>SeLeCT</literal>ë
+ <literal>SELECT</literal>ì ê°ê³ <literal>SELECT</literal>ìë ê°ì§ë§ <literal>org.hibernate.eg.FOO</literal>ë
+ <literal>org.hibernate.eg.Foo</literal>ê³¼ ê°ì§ ìê³ <literal>foo.barSet</literal>ì
+ <literal>foo.BARSET</literal>ê³¼ ê°ì§ ìë¤.
+ </para>
+
+ <para>
+ ì´ ë§¤ë´ì¼ì ì문ì HQL í¤ìë를 ì¬ì©íë¤. ëªëª ì¬ì©ìë¤ì ë³´ë¤ ëì ê°ë
ì±ì ìí´ ë문ì í¤ìëë¤ì ê°ì§ ì§ìë¤ì ì°¾ì§ë§, ì°ë¦¬ë
+ ìë° ì½ë ìì ì½ì
ë ë ì´ ì»¨ë²¤ì
ì´ ì¶íë¤ë ì ì ë°ê²¬íë¤.
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-from">
+ <title>from ì </title>
+
+ <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>ì 참조íê³ ì ìí ê²ì´ê¸° ë문ì´ë¤.
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat]]></programlisting>
+
+ <para>
+ ì´ ì§ìë 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">
+ <title>ì°ê´ë¤ê³¼ ì¡°ì¸ë¤</title>
+
+ <para>
+ ì°ë¦¬ë ëí <literal>join</literal>ì ì¬ì©íì¬ , ì°ê´ë ìí°í°ë¤ì ëë ê°ë¤ì ê°ì§ ì½ë ì
ì ììë¤ìë aliasë¤ì í ë¹í ìë
+ ìë¤.
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+ inner join cat.mate as mate
+ left outer join cat.kittens as kitten]]></programlisting>
+
+ <programlisting><![CDATA[from Cat as cat left join cat.mate.kittens as kittens]]></programlisting>
+
+ <programlisting><![CDATA[from Formula form full join form.parameter param]]></programlisting>
+
+ <para>
+ ì§ìëë join íì
ë¤ì ANSI SQLë¡ë¶í° ë¹ë ¤ìë¤
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>inner join</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>left outer join</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>right outer join</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>full join</literal> (ëê° ì ì©íì§ ìì)
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ <literal>inner join</literal>, <literal>left outer join</literal>, ê·¸ë¦¬ê³ <literal>right outer join</literal>
+ 구조체ë¤ì´ ì½ì¹ë ì ìë¤.
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+ join cat.mate as mate
+ left join cat.kittens as kitten]]></programlisting>
+
+ <para>
+ ë¹ì ì HQL <literal>with</literal> í¤ìë를 ì¬ì©íì¬ í¹ë³í ì¡°ì¸ ì¡°ê±´ë¤ì ì ê³µí ì ìë¤.
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+ left join cat.kittens as kitten
+ with kitten.bodyWeight > 10.0]]></programlisting>
+
+ <para>
+ ê²ë¤ê°, "fetch" joinì ê°ë¤ì ê°ì§ ì½ë ì
ë¤ì´ë ì°ê´ê´ê³ë¤ì´ í ê°ì select를 ì¬ì©íì¬, ê·¸ê²ë¤ì ë¶ëª¨ ê°ì²´ë¤ì ë°ë¼ ì´ê¸°í ëë
+ ê²ì íì©í´ì¤ë¤. ì´ê²ì ì½ë ì
ì ê²½ì°ì í¹í ì ì©íë¤. ê·¸ê²ì ì°ê´ê´ê³ë¤ê³¼ ì½ë ì
ë¤ì ëí 매í íì¼ì 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>를 ì¬ì©íì¬ í¸ì¶ëë ì§ìë¤ ë´ì ì¬ì©ë ì ììì ë
¸í¸íë¼. ì´ë¤ ì¤í¼ë ì´ì
ë¤ì´ ê²°ê³¼ íë¤ì
+ 기ì´íê³ ì기 ë문ì <literal>fetch</literal>ë <literal>setMaxResults()</literal> ëë
+ <literal>setFirstResult()</literal>ê³¼ í¨ê» ì¬ì©ëì§ ë§ìì¼ íë©°, ê·¸ê²ë¤(ê²°ê³¼ íë¤)ì ëê° ê°ê°ì eager ì½ë ì
íì¹ì ëí´
+ ì¤ë³µë¤ì í¬í¨íë¯ë¡ ë§ì íë¤ì´ ë¹ì ì´ ê¸°ëíë ê²ì´ ìëë¤.
+ <literal>fetch</literal>ë í¹ë³í ì©ëì <literal>with</literal> ì¡°ê±´ê³¼ë í¨ê» ì¬ì©ë ì ìë¤.í ê°ì ì§ì ë´ì íë
+ ì´ìì ì½ë ì
ì ì¡°ì¸ íì¹ìì¼ ì¹´í°ì ¼ ê³±ì ìì±ìí¤ë ê²ì´ ê°ë¥íë°, ì´ ê²½ì°ì 주ìíë¼. ë¤ì¤ ì½ë ì
roleë¤ì ì¡°ì¸ íì¹ìí¤ë ê²ì
+ ëí ëëë¡ 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>
+
+ </sect1>
+
+ <sect1 id="queryhql-joins-forms">
+ <title>join 구문ì íìë¤</title>
+
+ <para>
+ HQLì ë ê°ì§ íìì ì°ê´ ì¡°ì¸ì ì§ìíë¤: <literal>ì묵ì </literal> ê·¸ë¦¬ê³ <literal>ëª
ìì </literal>.
+ </para>
+
+ <para>
+ ìì ì ìì ë³´ì¬ì§ ì§ìë¤ì 모ë join í¤ìëê° from ì ë´ì ëª
ìì ì¼ë¡ ì¬ì©ëë <literal>ëª
ìì ì¸</literal> íìì
+ ì¬ì©íë¤. ì´ê²ì ê¶ì¥ëë íìì´ë¤.
+ </para>
+
+ <para>
+ <literal>í¨ì¶ì ì¸</literal> íìì join í¤ìë를 ì¬ì©íì§ ìëë¤. ëì ì, ì°ê´ë¤ì dot(.) í기를
+ ì¬ì©íì¬ "dot-참조ëë¤(dereferenced)". <literal>í¨ì¶ì ì¸</literal> ì¡°ì¸ë¤ì ììì HQL ì ë¤ë´ì
+ ëíë ì ìë¤. <literal>í¨ì¶ì ì¸</literal> joinì ê²°ê³¼ëë SQL 문ì¥ìì inner joinì¼ë¡ ê·ê²°ëë¤.
+ </para>
+
+ <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>
+
+ <para>
+ <literal>select</literal> ì ì ì§ì ê²°ê³¼ ì
ìì ë°íí ê°ì²´ë¤ê³¼ íë¡í¼í°ë¤ì´ ì´ë ê²ì¸ì§ë¥¼ ê³¨ë¼ ë´ëë¡ ê°ì íë¤. ë¤ìì
+ ê²í íì:
+ </para>
+
+ <programlisting><![CDATA[select mate
+from Cat as cat
+ inner join cat.mate as mate]]></programlisting>
+
+ <para>
+ ì§ìë ë¤ë¥¸ <literal>Cat</literal>ë¤ì <literal>mate</literal>ë¤ì select í ê²ì´ë¤. ì¤ì ë¡ ë¹ì ì ì´ ì§ìë¤ì ë¤ìê³¼
+ ê°ì´ ë³´ë¤ ì¶ì½íì¼ë¡ ííí ìë ìë¤:
+ </para>
+
+ <programlisting><![CDATA[select cat.mate from Cat cat]]></programlisting>
+
+ <para>
+ ì§ìë¤ì ì»´í¬ëí¸ íì
ì íë¡í¼í°ë¤ì í¬í¨íë ììì ê° íì
ì íë¡í¼í°ë¤ì ë°íí ìë ìë¤:
+ </para>
+
+ <programlisting><![CDATA[select cat.name from DomesticCat cat
+where cat.name like 'fri%']]></programlisting>
+
+ <programlisting><![CDATA[select cust.name.firstName from Customer as cust]]></programlisting>
+
+ <para>
+ <literal>Family</literal> í´ëì¤ê° ì ë¹í ìì±ì를 ê°ê³ ììì ê°ì íë©´,
+ ì§ìë¤ì ì¬ë¬ ê°ì²´ë¤ ê·¸ë¦¬ê³ /ëë íë¡í¼í°ë¤ì <literal>Object[]</literal> íì
ì ë°°ì´ë¡ì,
+ </para>
+
+ <programlisting><![CDATA[select mother, offspr, mate.name
+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>
+
+ <programlisting><![CDATA[select max(bodyWeight) as max, min(bodyWeight) as min, count(*) as n
+from Cat cat]]></programlisting>
+
+ <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">
+ <title>ì§ê³ í¨ìë¤</title>
+
+ <para>
+ HQL ì§ìë¤ì íë¡í¼í°ë¤ì ëí ì§ê³(aggregate) í¨ìë¤ì ê²°ê³¼ë¤ì ë°íí ìë ìë¤:
+ </para>
+
+ <programlisting><![CDATA[select avg(cat.weight), sum(cat.weight), max(cat.weight), count(cat)
+from Cat cat]]></programlisting>
+
+<!-- NO LONGER SUPPORTED
+ <para>
+ Collections may also appear inside aggregate functions in the <literal>select</literal>
+ clause.
+ </para>
+
+ <programlisting><![CDATA[select cat, count( elements(cat.kittens) )
+from Cat cat group by cat]]></programlisting>
+-->
+
+ <para>
+ ì§ìëë ì§ê³ í¨ìë¤ì ë¤ìê³¼ ê°ë¤
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>avg(...), sum(...), min(...), max(...)</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>count(*)</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>count(...), count(distinct ...), count(all...)</literal>
+ </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>
+
+ <programlisting><![CDATA[select distinct cat.name from Cat cat
+
+select count(distinct cat.name), count(cat) from Cat cat]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="queryhql-polymorphism">
+ <title>Polymorphic(ë¤íì±) ì§ìë¤</title>
+
+ <para>
+ ë¤ìê³¼ ê°ì ì§ì:
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat]]></programlisting>
+
+ <para>
+ ì <literal>Cat</literal>ì ì¸ì¤í´ì¤ë¤ ë¿ë§ ìëë¼, ëí <literal>DomesticCat</literal>ê³¼ ê°ì ìë¸í´ëì¤ë¤ ëí
+ ë°ííë¤. Hibernate ì§ìë¤ì <literal>from</literal> ì ë´ì <emphasis>ììì</emphasis> ìë° í´ëì¤ë ì¸í°íì´ì¤ë¥¼
+ ëª
ëª
í ì ìë¤. ì§ìë ê·¸ í´ëì¤ë¥¼ íì¥íê±°ë ê·¸ ì¸í°íì´ì¤ë¥¼ 구ííë 모ë ìì í´ëì¤ë¤ì ì¸ì¤í´ì¤ë¤ì ë°íí ê²ì´ë¤. ë¤ì ì§ìë
+ 모ë ìì ê°ì²´ë¤ì ë°íí ê²ì´ë¤:
+ </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" revision="1">
+ <title>where ì </title>
+
+ <para>
+ <literal>where</literal> ì ì ë°íë ì¸ì¤í´ì¤ë¤ì 목ë¡ì ì íìí¤ë ê²ì ë¹ì ìê² íì©í´ì¤ë¤. ë§ì¼ aliasê° ì¡´ì¬íì§ ìì ê²½ì°,
+ ë¹ì ì ì´ë¦ì ìí´ íë¡í¼í°ë¤ì 참조í ìë ìë¤:
+ </para>
+
+ <programlisting><![CDATA[from Cat where name='Fritz']]></programlisting>
+
+ <para>
+ ë§ì¼ í ê°ì aliasê° ì¡´ì¬í ê²½ì°, íëì ììì´ê° ë¶ì íë¡í¼í° ì´ë¦ì ì¬ì©íë¼:
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat where cat.name='Fritz']]></programlisting>
+
+ <para>
+ ë 'Fritz'ë¡ ëª
ëª
ë <literal>Cat</literal>ì ì¸ì¤í´ì¤ë¤ì ë°ííë¤.
+ </para>
+
+ <programlisting><![CDATA[select foo
+from Foo foo, Bar bar
+where foo.startDate = bar.date]]></programlisting>
+
+ <para>
+ ë <literal>Foo</literal>ì <literal>startDate</literal> íë¡í¼í°ì ëì¼í <literal>date</literal> íë¡í¼í°ë¥¼
+ ê°ì§ <literal>bar</literal>ì ì¸ì¤í´ì¤ê° ì¡´ì¬íë <literal>Foo</literal>ì 모ë ì¸ì¤í´ì¤ë¥¼ ë°íí ê²ì´ë¤. í©ì± ê²½ë¡
+ ííìë¤ì <literal>where</literal> ì ì ë§¤ì° ê°ë ¥íê² ë§ë¤ì´ì¤ë¤. ë¤ìì ê²í íì:
+ </para>
+
+ <programlisting><![CDATA[from Cat cat where cat.mate.name is not null]]></programlisting>
+
+ <para>
+ ì´ ì§ìë í
ì´ë¸ (inner) joinì ê°ì§ SQL ì§ìë¡ ë²ìëë¤. ë§ì¼ ë¹ì ì´ ë¤ìê³¼ ê°ì ì´ë¤ ê²ì ìì±íë¤ë©´
+ </para>
+
+ <programlisting><![CDATA[from Foo foo
+where foo.bar.baz.customer.address.city is not null]]></programlisting>
+
+ <para>
+ ë¹ì ì SQLìì ë¤ ê°ì í
ì´ë¸ joinë¤ì íìë¡ íë íëì ì§ìë¡ ëë¼ ê²ì´ë¤.
+ </para>
+
+ <para>
+ <literal>=</literal> ì°ì°ìë íë¡í¼í°ë¤ ë¿ë§ ìëë¼ ëí ì¸ì¤í´ì¤ë¤ì ë¹êµíëë° ì¬ì©ë ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[from Cat cat, Cat rival where cat.mate = rival.mate]]></programlisting>
+
+ <programlisting><![CDATA[select cat, mate
+from Cat cat, Cat mate
+where cat.mate = mate]]></programlisting>
+
+ <para>
+ í¹ë³í íë¡í¼í°(ì문ì) <literal>id</literal>ë ê°ì²´ì ì ì¼ ìë³ì를 참조íëë° ì¬ì©ë ì ìë¤.
+ ì¶ê° ì ë³´ë <xref linkend="queryhql-identifier-property"/>를 ë³´ë¼.
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat where cat.id = 123
+
+from Cat as cat where cat.mate.id = 69]]></programlisting>
+
+ <para>
+ ë ë²ì§¸ ì§ìê° í¨ì¨ì ì´ë¤. í
ì´ë¸ joinì´ íì ìë¤!
+ </para>
+
+ <para>
+ 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'
+ and person.id.medicareNumber = 123456]]></programlisting>
+
+ <programlisting><![CDATA[from bank.Account account
+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) ê°ì¼ë¡
+ ë³íë ê²ì´ë¤.
+ </para>
+
+ <programlisting><![CDATA[from Cat cat where cat.class = DomesticCat]]></programlisting>
+
+ <para>
+ ë¹ì ì ëí ì»´í¬ëí¸ë¤ ëë composite ì¬ì©ì íì
ë¤, ëë ëª
ëª
ë ì»´í¬ëí¸ íì
ë¤ì ìì±ë¤ì ì¬ì©í ìë ìë¤. ìì¸í ê²ì
+ <xref linkend="queryhql-components"/>를 ë³´ë¼.
+ </para>
+
+ <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">
+ <title>ííìë¤</title>
+
+ <para>
+ <literal>where</literal> ì ìì íì©ëë ííìë¤ì ë¹ì ì´ SQLë¡ ìì±í ì ìë ëë¶ë¶ì ì¢
ë¥ì ê²ë¤ì í¬í¨íë¤:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ ì°ì ì°ì°ìë¤ <literal>+, -, *, /</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ë°ì´ë리 ë¹êµ ì°ì°ìë¤ <literal>=, >=, <=, <>, !=, like</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ë
¼ë¦¬ ì°ì°ë¤ <literal>and, or, not</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ 그룹íì ëíë´ë ê´í¸ë¤ <literal>( )</literal>, indicating grouping
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>in</literal>,
+ <literal>not in</literal>,
+ <literal>between</literal>,
+ <literal>is null</literal>,
+ <literal>is not null</literal>,
+ <literal>is empty</literal>,
+ <literal>is not empty</literal>,
+ <literal>member of</literal> and
+ <literal>not member of</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ "ê°ë¨í" ê²½ì°, <literal>case ... when ... then ... else ... end</literal>, 그리ê³
+ "ê²ìì¸" ê²½ì°, <literal>case when ... then ... else ... end</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ 문ìì´ ì°ê²° <literal>...||...</literal> or <literal>concat(...,...)</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>current_date()</literal>, <literal>current_time()</literal>,
+ <literal>current_timestamp()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>second(...)</literal>, <literal>minute(...)</literal>,
+ <literal>hour(...)</literal>, <literal>day(...)</literal>,
+ <literal>month(...)</literal>, <literal>year(...)</literal>,
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ EJB-QL 3.0ì ìí´ ì ìë ììì í¨ì ëë ì¤í¼ë ì´í°: <literal>substring(), trim(),
+ lower(), upper(), length(), locate(), abs(), sqrt(), bit_length(), mod()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>coalesce()</literal> ê·¸ë¦¬ê³ <literal>nullif()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ numeric ê°ë¤ì´ë temporal ê°ë¤ì ê°ë
ì± ìë 문ìì´ë¡ ë³íìí¤ë <literal>str()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>cast(... as ...)</literal>, ì¬ê¸°ì ëë² ì§¸ ìê·ë¨¼í¸ë Hibernate íì
ì ì´ë¦ì´ê³ ,
+ ANSI <literal>cast()</literal>ì <literal>extract()</literal>ê° ê¸°ë° ë°ì´í°ë² ì´ì¤ì ìí´
+ ì§ìë ê²½ì°ìë <literal>extract(... from ...)</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ì¡°ì¸ë ì¸ë±ì±ë ì½ë ì
ì aliasë¤ì ì ì©ëë, HQL <literal>index()</literal> í¨ì
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ì½ë ì
ê°ì ê°ì§ ê²½ë¡ ííìë¤ì ì·¨íë HQL í¨ìë¤ : <literal>some, all, exists, any, in</literal>ì
+ ì¬ì©íì¬ ì ëí ë ì ìë í¹ë³í <literal>elements()</literal>ì <literal>indices</literal>
+ í¨ìë¤ê³¼ í¨ê» <literal>size(), minelement(), maxelement(), minindex(), maxindex()</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>sign()</literal>, <literal>trunc()</literal>, <literal>rtrim()</literal>,
+ <literal>sin()</literal>ê³¼ ê°ì´ ììì ë°ì´í°ë² ì´ì¤ ì§ì SQL ì¤ì¹¼ë¼ í¨ì
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ JDBC-ì¤íì¼ ìì¹ íë¼ë¯¸í°ë¤ <literal>?</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ëª
ëª
ë íë¼ë¯¸í°ë¤ <literal>:name</literal>, <literal>:start_date</literal>, <literal>:x1</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ SQL 리í°ë´ë¤ <literal>'foo'</literal>, <literal>69</literal>, <literal>6.66E+2</literal>,
+ <literal>'1970-01-01 10:00:01.0'</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Java <literal>public static final</literal> ììë¤. <literal>eg.Color.TABBY</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ <literal>in</literal> ê³¼ <literal>between</literal>ì ë¤ìê³¼ ê°ì´ ì¬ì©ë ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[from DomesticCat cat where cat.name between 'A' and 'B']]></programlisting>
+
+ <programlisting><![CDATA[from DomesticCat cat where cat.name in ( 'Foo', 'Bar', 'Baz' )]]></programlisting>
+
+ <para>
+ ê·¸ë¦¬ê³ ë¶ì íë¤ì ë¤ìê³¼ ê°ì´ ìì±ë ì ìë¤
+ </para>
+
+ <programlisting><![CDATA[from DomesticCat cat where cat.name not between 'A' and 'B']]></programlisting>
+
+ <programlisting><![CDATA[from DomesticCat cat where cat.name not in ( 'Foo', 'Bar', 'Baz' )]]></programlisting>
+
+ <para>
+ ë§ì°¬ê°ì§ë¡, <literal>is null</literal>ê³¼ <literal>is not null</literal>ì null ê°ë¤ì í
ì¤í¸íëë° ì¬ì©ë ì ìë¤.
+ </para>
+
+ <para>
+ Booleanë¤ì Hibernate êµ¬ì± ë´ì HQL ì§ì ì¹íë¤ì ì ì¸í¨ì¼ë¡ì¨ ííìë¤ ë´ì ì½ê² ì¬ì©ë ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[<property name="hibernate.query.substitutions">true 1, false 0</property>]]></programlisting>
+
+ <para>
+ ì´ê²ì í¤ìë <literal>true</literal>ì <literal>false</literal> í¤ìëë¤ì ì´ HQLë¡ë¶í° ë²ìë SQLìì
+ 리í°ë´ <literal>1</literal>ê³¼ <literal>0</literal>ì¼ë¡ ëì²´ë ê²ì´ë¤:
+ </para>
+
+ <programlisting><![CDATA[from Cat cat where cat.alive = true]]></programlisting>
+
+ <para>
+ ë¹ì ì í¹ë³í íë¡í¼í° <literal>size</literal>ë¡ì ëë í¹ë³í <literal>size()</literal> í¨ìë¡ì ì½ë ì
ì ì¬ì´ì¦ë¥¼
+ í
ì¤í¸í ì ìë¤.
+ </para>
+
+ <programlisting><![CDATA[from Cat cat where cat.kittens.size > 0]]></programlisting>
+
+ <programlisting><![CDATA[from Cat cat where size(cat.kittens) > 0]]></programlisting>
+
+ <para>
+ ì¸ë±ì±ë ì½ë ì
ë¤ì ëí´, ë¹ì ì<literal>minindex</literal>ì <literal>maxindex</literal> í¨ìë¤ì ì¬ì©íì¬
+ ìµì ì¸ë±ì¤ê³¼ ìµë ì¸ë±ì¤ë¥¼ 참조í ì ìë¤. ì ì¬íê² ë¹ì ì <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> í¨ì), ëë ìë¸ì§ìì 결과를 ì ë¬íì ë ì§ìëë¤(ìë를 ë³´ë¼).
+ </para>
+
+ <programlisting><![CDATA[select mother from Cat as mother, Cat as kit
+where kit in elements(foo.kittens)]]></programlisting>
+
+ <programlisting><![CDATA[select p from NameList list, Person p
+where p.name = some elements(list.names)]]></programlisting>
+
+ <programlisting><![CDATA[from Cat cat where exists elements(cat.kittens)]]></programlisting>
+
+ <programlisting><![CDATA[from Player p where 3 > all elements(p.scores)]]></programlisting>
+
+ <programlisting><![CDATA[from Show show where 'fizard' in indices(show.acts)]]></programlisting>
+
+ <para>
+ ì´ë¤ 구조체ë¤-<literal>size</literal>, <literal>elements</literal>, <literal>indices</literal>,
+ <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
+where calendar.holidays['national day'] = person.birthDay
+ and person.nationality.calendar = calendar]]></programlisting>
+
+ <programlisting><![CDATA[select item from Item item, Order order
+where order.items[ order.deliveredItemIndices[0] ] = item and order.id = 11]]></programlisting>
+
+ <programlisting><![CDATA[select item from Item item, Order order
+where order.items[ maxindex(order.items) ] = item and order.id = 11]]></programlisting>
+
+ <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> í¨ì를
+ ì ê³µíë¤.
+ </para>
+
+ <programlisting><![CDATA[select item, index(item) from Order order
+ join order.items item
+where index(item) < 5]]></programlisting>
+
+ <para>
+ 기본 ë°ì´í°ë² ì´ì¤ì ìí´ ì ê³µëë Scalar SQL í¨ìë¤ì´ ì¬ì©ë ìë ìë¤
+ </para>
+
+ <programlisting><![CDATA[from DomesticCat cat where upper(cat.name) like 'FRI%']]></programlisting>
+
+ <para>
+ ë¹ì ì´ ìì§ ì´ ëª¨ë ê²ì ë©ëíì§ ëª»íë¤ë©´, SQL ë´ìì ë¤ì ì§ìê° ê°ë
ì±ì´ ì¼ë§ë ë§ê³ ì ìì§ë¥¼ ìê°í´ë³´ë¼:
+ </para>
+
+ <programlisting><![CDATA[select cust
+from Product prod,
+ Store store
+ inner join store.customers cust
+where prod.name = 'widget'
+ and store.location.name in ( 'Melbourne', 'Sydney' )
+ and prod = all elements(cust.currentOrder.lineItems)]]></programlisting>
+
+ <para>
+ <emphasis>íí¸</emphasis> : ë¤ìê³¼ ê°ì ì´ë¤ ê²
+ </para>
+
+ <programlisting><![CDATA[SELECT cust.name, cust.address, cust.phone, cust.id, cust.current_order
+FROM customers cust,
+ stores store,
+ locations loc,
+ store_customers sc,
+ product prod
+WHERE prod.name = 'widget'
+ AND store.loc_id = loc.id
+ AND loc.name IN ( 'Melbourne', 'Sydney' )
+ AND sc.store_id = store.id
+ AND sc.cust_id = cust.id
+ AND prod.id = ALL(
+ SELECT item.prod_id
+ FROM line_items item, orders o
+ WHERE item.order_id = o.id
+ AND cust.current_order = o.id
+ )]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="queryhql-ordering">
+ <title>order by ì </title>
+
+ <para>
+ ì§ìì ìí´ ë°íë 리ì¤í¸ë ë°íë í´ëì¤ ëë ì»´í¬ëí¸ë¤ì íë¡í¼í°ì ìí´ ìì(ordering)ì§ìì§ ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[from DomesticCat cat
+order by cat.name asc, cat.weight desc, cat.birthdate]]></programlisting>
+
+ <para>
+ <literal>asc</literal> ìµì
ëë <literal>desc</literal> ìµì
ì ê°ê° ì¤ë¦ì°¨ì ëë ë´ë¦¼ì°¨ì ì ë ¬ì ëíë¸ë¤.
+ </para>
+ </sect1>
+
+ <sect1 id="queryhql-grouping" revision="1">
+ <title>group by ì </title>
+
+ <para>
+ aggregate ê°ë¤ì ë°ííë ì§ìë ë°íë í´ëì¤ë ì»´í¬ëí¸ë¤ì íë¡í¼í°ì ìí´ ê·¸ë£¹ì§ìì§ ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[select cat.color, sum(cat.weight), count(cat)
+from Cat cat
+group by cat.color]]></programlisting>
+
+ <programlisting><![CDATA[select foo.id, avg(name), max(name)
+from Foo foo join foo.names name
+group by foo.id]]></programlisting>
+
+ <para>
+ ëí <literal>having</literal> ì ì´ íì©ëë¤.
+ </para>
+
+ <programlisting><![CDATA[select cat.color, sum(cat.weight), count(cat)
+from Cat cat
+group by cat.color
+having cat.color in (eg.Color.TABBY, eg.Color.BLACK)]]></programlisting>
+
+ <para>
+ SQL í¨ìë¤ê³¼ ì§ê³ í¨ìë¤ì´ 기본 ë°ì´í°ë² ì´ì¤ì ìí´ ì§ìë ê²½ì°(ì를 ë¤ì´ MySQLì ì§ìëì§ ìëë¤)
+ <literal>having</literal> ì ê³¼ <literal>order by</literal> ì ìì íì©ëë¤.
+ </para>
+
+ <programlisting><![CDATA[select cat
+from Cat cat
+ join cat.kittens kitten
+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="3">
+ <title>ìë¸ì§ìë¤</title>
+
+ <para>
+ subselectë¤ì ì§ìíë ë°ì´í°ë² ì´ì¤ë¤ì ê²½ì°, Hibernateë ì§ìë¤ ë´ì ìë¸ì§ìë¤ì ì§ìíë¤. ìë¸ì§ìë ê´í¸ë¡ 묶ì¬ì ¸ì¼
+ íë¤(ì주 SQL ì§ê³í¨ì í¸ì¶ì ìí´). ì¬ì§ì´ ìë¡ ìê´ë ìë¸ì§ìë¤(ì¸ë¶ ì§ì ë´ìì alias를 참조íë ìë¸ì§ìë¤)ì´ íì©ëë¤.
+ </para>
+
+ <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>
+
+ <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>
+
+ <programlisting><![CDATA[select cat.id, (select max(kit.weight) from cat.kitten kit)
+from Cat as cat]]></programlisting>
+
+ <para>
+ HQL ìë¸ì§ìë¤ì´ select ì ëë where ì ë´ììë§ ì¼ì´ë ì ììì ë
¸í¸íë¼.
+ </para>
+
+
+ <para>
+ ìë¸ì§ìë¤ì ëí <literal>row value constructor</literal> 구문ì íì©í ì ìë¤. ìì¸í ê²ì
+ <xref linkend="queryhql-tuple"/>를 ë³´ë¼.
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-examples">
+ <title>HQL ìì ë¤</title>
+
+ <para>
+
+ Hibernate ì§ìë¤ì ë§¤ì° ê°ë ¥íê³ ë³µì¡í ì ìë¤. ì¬ì¤, ì§ì ì¸ì´ì íì Hibernateì 주ì í매 í¬ì¸í¸ë¤ ì¤ íëì´ë¤. ë¤ìì
+ ë´ê° ìµê·¼ì íë¡ì í¸ìì ì¬ì©íë ì§ìë¤ê³¼ ë§¤ì° ì ì¬í ëªëª ìì ì§ìë¤ì´ë¤. ë¹ì ì´ ìì±íê² ë ëë¶ë¶ì ì§ìë¤ì ì´ê²ë¤ë³´ë¤ í¨ì¬
+ ê°ë¨íë¤ë ì ì ë
¸í¸íë¼!
+ </para>
+
+ <para>
+ ë¤ì ì§ìë í¹ì ê³ ê°ì ëí 모ë ì§ë¶ëì§ ìì 주문ë¤ì 주문 id, í목ë¤ì ê°ì, ê·¸ë¦¬ê³ ì£¼ë¬¸ì ì ì²´ í©ê³ê° ê·¸ë¦¬ê³ ì£¼ì´ì§
+ ìµì ì ì²´ í©ê³ë¥¼ ì ì²´ í©ê³ê°ì ë°ë¼ ê²°ê³¼ë¤ì ììì§ìì ë°ííë¤. ê°ê²© ê²°ì ì ìì´, ê·¸ê²ì íì¬ì ì¹´íë¡ì ì¬ì©íë¤.
+ ê·ê²°ëë SQL ì§ìë <literal>ORDER</literal>, <literal>ORDER_LINE</literal>, <literal>PRODUCT</literal>,
+ <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
+ join item.product as product,
+ Catalog as catalog
+ join catalog.prices as price
+where order.paid = false
+ and order.customer = :customer
+ and price.product = product
+ and catalog.effectiveDate < sysdate
+ and catalog.effectiveDate >= all (
+ select cat.effectiveDate
+ from Catalog as cat
+ where cat.effectiveDate < sysdate
+ )
+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
+ join item.product as product,
+ Catalog as catalog
+ join catalog.prices as price
+where order.paid = false
+ and order.customer = :customer
+ and price.product = product
+ and catalog = :currentCatalog
+group by order
+having sum(price.amount) > :minAmount
+order by sum(price.amount) desc]]></programlisting>
+
+ <para>
+ ë¤ì ì§ìë íì¬ ì¬ì©ìì ìí´ ê°ì¥ ìµê·¼ì ìí ë³ê²½ì´ íí´ì¡ë <literal>AWAITING_APPROVAL</literal> ìíì ìë 모ë
+ ì§ë¶ë¤ì ì ì¸í, ê°ê°ì ìíì ìë ì§ë¶ë¤ì ê°ì를 ì¹´ì´í¸ íë¤. ê·¸ê²ì <literal>PAYMENT</literal>,
+ <literal>PAYMENT_STATUS</literal>, <literal>PAYMENT_STATUS_CHANGE</literal> í
ì´ë¸ë¤ì ëí ë ê°ì inner
+ ì¡°ì¸ë¤ê³¼ íëì ìê´ê´ê³ ì§ìì§ subselect를 ê°ì§ SQL ì§ìë¡ ë³íëë¤.
+ </para>
+
+ <programlisting><![CDATA[select count(payment), status.name
+from Payment as payment
+ join payment.currentStatus as status
+ join payment.statusChanges as statusChange
+where payment.status.name <> PaymentStatus.AWAITING_APPROVAL
+ or (
+ statusChange.timeStamp = (
+ select max(change.timeStamp)
+ from PaymentStatusChange change
+ where change.payment = payment
+ )
+ and statusChange.user <> :currentUser
+ )
+group by status.name, status.sortOrder
+order by status.sortOrder]]></programlisting>
+
+ <para>
+ ë§ì¼ ë´ê° <literal>statusChanges</literal> ì½ë ì
ì setê° ìë listë¡ ë§¤ííë¤ë©´, ê·¸ ì§ìë ìì±íê¸°ê° í¨ì¬ ë
+ ê°ë¨íì ê²ì´ë¤.
+ </para>
+
+ <programlisting><![CDATA[select count(payment), status.name
+from Payment as payment
+ join payment.currentStatus as status
+where payment.status.name <> PaymentStatus.AWAITING_APPROVAL
+ or payment.statusChanges[ maxIndex(payment.statusChanges) ].user <> :currentUser
+group by status.name, status.sortOrder
+order by status.sortOrder]]></programlisting>
+
+ <para>
+ ë¤ì ì§ìë íì¬ì ì¬ì©ìê° ìí´ ìë ì¡°ì§ì 모ë ê³ì ë¤ê³¼ ì§ë¶ëì§ ìì ì§ë¶ë¤ì ë°ííëë° MS SQL Server
+ <literal>isNull()</literal> í¨ì를 ì¬ì©íë¤. ê·¸ê²ì <literal>ACCOUNT</literal>, <literal>PAYMENT</literal>,
+ <literal>PAYMENT_STATUS</literal>, <literal>ACCOUNT_TYPE</literal>, <literal>ORGANIZATION</literal>,
+ <literal>ORG_USER</literal> í
ì´ë¸ë¤ì ëí ì¸ ê°ì inner ì¡°ì¸ë¤, íëì outer ì¡°ì¸, ê·¸ë¦¬ê³ íëì subselect를 ê°ì§
+ í ê°ì SQL ì§ìë¡ ë²ìëë¤.
+ </para>
+
+ <programlisting><![CDATA[select account, payment
+from Account as account
+ left outer join account.payments as payment
+where :currentUser in elements(account.holder.users)
+ and PaymentStatus.UNPAID = isNull(payment.currentStatus.name, PaymentStatus.UNPAID)
+order by account.type.sortOrder, account.accountNumber, payment.dueDate]]></programlisting>
+
+ <para>
+ ëªëª ë°ì´í°ë² ì´ì¤ë¤ì ê²½ì°, ì°ë¦¬ë (ìê´ê´ê³ ì§ìì§) subselect를 ìì¨ íìê° ìì ê²ì´ë¤.
+ </para>
+
+ <programlisting><![CDATA[select account, payment
+from Account as account
+ join account.holder.users as user
+ left outer join account.payments as payment
+where :currentUser = user
+ and PaymentStatus.UNPAID = isNull(payment.currentStatus.name, PaymentStatus.UNPAID)
+order by account.type.sortOrder, account.accountNumber, payment.dueDate]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="queryhql-bulk" revision="2">
+ <title>ëë updateì delete</title>
+
+ <para>
+ HQLì ì´ì <literal>update</literal>, <literal>delete</literal> 그리ê³
+ <literal>insert ... select ...</literal> 문ì¥ë¤ì ì§ìíë¤.
+ ìì¸í ê²ì <xref linkend="batch-direct"/>를 ë³´ë¼.
+ </para>
+ </sect1>
+
+ <sect1 id="queryhql-tipstricks">
+ <title>íë¤ & í¸ë¦ë¤</title>
+
+ <para>
+ ë¹ì ì ì¤ì ë¡ ì§ì ê²°ê³¼ë¤ì ë°ííì§ ìê³ ì ê·¸ê²ë¤(ì§ì ê²°ê³¼ë¤)ì ê°ì를 ì¹´ì´í¸í ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[( (Integer) session.iterate("select count(*) from ....").next() ).intValue()]]></programlisting>
+
+ <para>
+ ì½ë ì
ì í¬ê¸°ì ë°ë¼ 결과를 ìì(ordering)ì§ì°ë ¤ë©´, ë¤ì ì§ì를 ì¬ì©íë¼:
+ </para>
+
+ <programlisting><![CDATA[select usr.id, usr.name
+from User as usr
+ left join usr.messages as msg
+group by usr.id, usr.name
+order by count(msg)]]></programlisting>
+
+ <para>
+ ë§ì¼ ë¹ì ì ë°ì´í°ë² ì´ì¤ê° subselectë¤ì ì§ìí ê²½ì°, ë¹ì ì ë¹ì ì ì§ìì where ì ë´ì selection ì¬ì´ì¦ì ëí ì¡°ê±´ì
+ ìì¹ì§ì¸ ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[from User usr where size(usr.messages) >= 1]]></programlisting>
+
+ <para>
+ ë§ì¼ ë¹ì ì ë°ì´í°ë² ì´ì¤ê° subselect를 ì§ìíì§ ìì ê²½ì°, ë¤ì ì§ì를 ì¬ì©íë¼:
+ </para>
+
+ <programlisting><![CDATA[select usr.id, usr.name
+from User usr.name
+ join usr.messages msg
+group by usr.id, usr.name
+having count(msg) >= 1]]></programlisting>
+
+ <para>
+ ì´ í´ê²°ì±
ì´ inner ì¡°ì¸ ë문ì 0ê°ì ë©ìì§ë¥¼ ê°ì§ <literal>User</literal>를 ë°íí ì ìì¼ë¯ë¡, ë¤ì íìì´ ëí ì ì©íë¤:
+ </para>
+
+ <programlisting><![CDATA[select usr.id, usr.name
+from User as usr
+ left join usr.messages as msg
+group by usr.id, usr.name
+having count(msg) = 0]]></programlisting>
+
+ <para>
+ íëì JavaBeanì íë¡í¼í°ë¤ì ëª
ëª
ë ì§ì íë¼ë¯¸í°ë¤ì ë°ì¸ëë ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[Query q = s.createQuery("from foo Foo as foo where foo.name=:name and foo.size=:size");
+q.setProperties(fooBean); // fooBean has getName() and getSize()
+List foos = q.list();]]></programlisting>
+
+ <para>
+ ì½ë ì
ë¤ì íí°ë¥¼ ê°ì§ <literal>Query</literal> ì¸í°íì´ì¤ë¥¼ ì¬ì©íì¬ ìª¼ë§¤ê¹íë ê²ì´ ê°ë¥íë¤:
+ </para>
+
+ <programlisting><![CDATA[Query q = s.createFilter( collection, "" ); // the trivial filter
+q.setMaxResults(PAGE_SIZE);
+q.setFirstResult(PAGE_SIZE * pageNumber);
+List page = q.list();]]></programlisting>
+
+ <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>
+
+ <para>
+ ë¹ì ì ì½ë ì
ì ì´ê¸°í íì§ ìê³ ì ê·¸ê²(ì½ë ì
)ì í¬ê¸°ë¥¼ ì°¾ì ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[( (Integer) session.iterate("select count(*) from ....").next() ).intValue();]]></programlisting>
+
+ </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>
+
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/query_sql.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/query_sql.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/query_sql.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,694 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<chapter id="querysql" revision="2">
+ <title>Native SQL</title>
+
+ <para>ë¹ì ì ëí ë¹ì ì ë°ì´í°ë² ì´ì¤ì native SQL dialectë¡ ì§ìë¤ì ííí ìë ìë¤.
+ ë¹ì ì´ ì¤ë¼í´ì ì§ì íí¸ë¤ ëë <literal>CONNECT</literal> í¤ìëì ê°ì
+ ë°ì´í°ë² ì´ì¤ ì§ì ì ì¸ í¹ì§ë¤ì íì©íê³ ì ìí ê²½ì°ì ì´ê²ì´ ì ì©íë¤.
+ ê·¸ê²ì ëí ì§ì ì ì¸ SQL/JDBC 기ë°ì ì´í리ì¼ì´ì
ì¼ë¡ë¶í° Hibernateë¡ì ë³´ë¤ ëª
ë£í
+ ì´ì ê²½ë¡ë¥¼ ì ê³µíë¤.</para>
+
+ <para>Hibernate3ì ëí 모ë create, update, delete, load ì¤í¼ë ì´ì
ë¤ì ëí´
+ (ë´ì¥ íë¡ìì ë¤ì í¬í¨íì¬) ìì¼ë¡ ìì±ë SQLì ì§ì íë ê²ì ë¹ì ìê² íì©í´ì¤ë¤.</para>
+
+ <sect1 id="querysql-creating" revision="4">
+ <title><literal>SQLQuery</literal> ì¬ì©í기</title>
+
+ <para>native SQL ì§ìë¤ì ì¤íì <literal>SQLQuery</literal> ì¸í°íì´ì¤ë¥¼ íµí´
+ ì ì´ëë©°, ê·¸ê²ì <literal>Session.createSQLQuery()</literal>ì í¸ì¶íì¬
+ íëëë¤. ë¤ìì ì´ API를 ì§ìì ì¬ì©íë ë°©ë²ì ì¤ëª
íë¤.</para>
+
+ <sect2>
+ <title>ì¤ì¹¼ë¼ ì§ìë¤</title>
+
+ <para>ê°ì¥ 기본ì ì¸ SQL ì§ìë ì¤ì¹¼ë¼ë¤(ê°ë¤)ì 목ë¡ì ì»ë
+ ê²ì´ë¤.</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT * FROM CATS").list();
+sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS").list();
+]]></programlisting>
+
+ <para>ì´ê²ë¤ì ëë¤ CATS í
ì´ë¸ ë´ì ìë ê°ê°ì 컬ë¼ì ëí ì¤ì¹¼ë¼ ê°ë¤ì ê°ì§ Object
+ ë°°ì´ë¤ì List를 ë°íí ê²ì´ë¤. Hibernateë ë°íëë ì¤ì¹¼ë¼ ê°ë¤ì ì¤ì ììì íì
ë¤ì
+ ëì¶íëë° ResultSetMetadata를 ì¬ì©í ê²ì´ë¤.</para>
+
+ <para><literal>ResultSetMetadata</literal> ì¬ì©ì ì¤ë²í¤ë를 í¼íê±°ë
+ ê°ë¨íê²ë ë°íëë ê²ì ë³´ë¤ ëª
ìì ì´ê²ë í기 ìí´ ì°ë¦¬ë
+ <literal>addScalar()</literal>를 ì¬ì©í ì ìë¤.</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT * FROM CATS")
+ .addScalar("ID", Hibernate.LONG)
+ .addScalar("NAME", Hibernate.STRING)
+ .addScalar("BIRTHDATE", Hibernate.DATE)
+]]></programlisting>
+
+ <para>ì´ ì§ìë ë¤ìì ì§ì íë¤:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>SQL ì§ì 문ìì´</para>
+ </listitem>
+
+ <listitem>
+ <para>ë°íí 컬ë¼ë¤ê³¼ íì
ë¤</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>ì´ê²ì ì¬ì í Object ë°°ì´ë¤ì ë°íí ê²ì´ì§ë§, ì´ì ê·¸ê²ì
+ <literal>ResultSetMetdata</literal>를 ì¬ì©íì§ ìì ê²ì´ê³ ëì ì
+ ê¸°ë° ê²°ê³¼ì
ì¼ë¡ë¶í° ID, NAME ê·¸ë¦¬ê³ BIRTHDATE 컬ë¼ì ê°ê° Long, String
+ ê·¸ë¦¬ê³ Short íì
ì¼ë¡ ë°íí ê²ì´ë¤. ì¬ì§ì´ ê·¸ ì§ìê° <literal>*</literal>를
+ ì¬ì©íê³ ì¸ ê°ì ì´ê±°ë 컬ë¼ë¤ ë³´ë¤ ë ë§ì ê²ì ë°íí ì ììì§ë¼ë, ì´ê²ì
+ ëí ì¤ì§ ì´ë¤ ì¸ ê°ì 컬ë¼ë¤ ë§ì´ ë°íë ê²ìì ì미íë¤.</para>
+
+ <para>ì¤ì¹¼ë¼ë¤ ì¤ ëªëª ëë ì ë¶ì ëí íì
ì 보를 ë¨ê²¨ëë ê²ì´ ê°ë¥íë¤.</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT * FROM CATS")
+ .addScalar("ID", Hibernate.LONG)
+ .addScalar("NAME")
+ .addScalar("BIRTHDATE")
+]]></programlisting>
+
+ <para>ì´ê²ì 본ì§ì ì¼ë¡ ìì ê²ê³¼ ëì¼í ì§ìì´ì§ë§, ì´ì
+ <literal>ResultSetMetaData</literal>ë IDì íì
ì´ ëª
ìì ì¼ë¡ ì§ì ëì´ ìì¼ë¯ë¡
+ NAMEê³¼ BIRTHDATEì íì
ì ê²°ì íëë° ì¬ì©ëë¤.</para>
+
+ <para>java.sql.Types returned from ResultSetMetaDataì´ Hibernate
+ íì
ë¤ë¡ 매íëë ë°©ë²ì Dialectì ìí´ ì ì´ëë¤. ë§ì¼ í¹ì íì
ì´ ë§¤íëì§ ìê±°ë
+ ììëë íì
ì¼ë¡ ê·ê²°ëì§ ìì ê²½ì°ì Dialect ë´ì ìë
+ <literal>registerHibernateType</literal>ì ëí í¸ì¶ë¤ì íµí´ ê·¸ê²ì
+ ë§ì¶¤í ìí¤ë ê²ì´ ê°ë¥íë¤.</para>
+ </sect2>
+
+ <sect2>
+ <title>Entity ì§ìë¤</title>
+
+ <para>ìì ì§ìë¤ì ì¤ì¹¼ë¼ ê°ë¤ì ë°ííë ê², ê²°ê³¼ì
ë¤ë¡ë¶í° "ìëì" ê°ë¤ì
+ 기본ì ì¼ë¡ ë°ííë ê²ì ëí ì ë¶ìë¤. ë¤ìì <literal>addEntity()</literal>를
+ íµí´ native sql ì§ìë¡ë¶í° ìí°í° ê°ì²´ë¤ì ì»ë ë°©ë²ì ë³´ì¬ì¤ë¤.</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT * FROM CATS").addEntity(Cat.class);
+sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS").addEntity(Cat.class);
+]]></programlisting>
+
+ <para>ì´ ì§ìë ë¤ìì ì§ì íë¤:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>SQL ì§ì 문ìì´</para>
+ </listitem>
+
+ <listitem>
+ <para>ê·¸ ì§ìì ìí´ ë°íëë ìí°í°</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>Catì´ ì»¬ë¼ ID, NAME ê·¸ë¦¬ê³ BIRTHDATEë¡ì 매íëë¤ê³ ê°ì íë©´,
+ ìì ì§ìë¤ì ëë¤ ê°ê°ì ììê° íëì Cat ìí°í°ì¸ íëì List를 ë°íí ê²ì´ë¤.</para>
+
+ <para>ë§ì¼ ê·¸ ìí°í°ê° ë ë¤ë¥¸ ìí°í°ì ëí´ <literal>many-to-one</literal>ë¡
+ 매íëì´ ìë¤ë©´ ëí native ì§ì를 ì¤íí ë ì´ê²ì ë°ííë ê²ì´ íìì ê³ , ê·¸ ë°ì ê²½ì°
+ ë°ì´í°ë² ì´ì¤ ì§ì ì ì¸ "컬ë¼ì´ ë°ê²¬ëì§ ìììµëë¤" ì¤ë¥ê° ì¼ì´ë ê²ì´ë¤. ì¶ê°ì ì¸
+ 컬ë¼ì * í기를 ì¬ì©í ìëì ì¼ë¡ ë°íë ê²ì´ì§ë§, ì°ë¦¬ë ë¤ì <literal>Dog</literal>ì
+ ëí <literal>many-to-one</literal> ìì ììì²ë¼ ëª
ìì ì¸ ê²ì ë ì í¸íë¤:</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE, DOG_ID FROM CATS").addEntity(Cat.class);
+]]></programlisting>
+
+ <para>ì´ê²ì cat.getDog()ì´ ê³ ì íê² ê¸°ë¥íë ê²ì íì©íë¤.</para>
+ </sect2>
+
+ <sect2>
+ <title>ì°ê´ë¤ê³¼ ì½ë ì
ë¤ì ì²ë¦¬í기</title>
+
+ <para>íë½ì를 ì´ê¸°í ìí´ì ìì´ ê°ë¥í í¹ë³í ë¼ì´ëí¸ë¦½ì í¼í기 ìí´ì
+ <literal>Dog</literal>ìì eagerly joinìí¤ë ê²ì´ ê°ì¼íë¤. ì´ê²ì
+ <literal>addJoin()</literal> ë©ìë를 íµí´ íí´ì§ëë°, ê·¸ê²ì
+ ì°ê´ì´ë ì½ë ì
ë´ìì ì¡°ì¸ìí¤ë ê²ì ë¹ì ìê² íì©í´ì¤ë¤.</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT c.ID, NAME, BIRTHDATE, DOG_ID, D_ID, D_NAME FROM CATS c, DOGS d WHERE c.DOG_ID = d.D_ID")
+ .addEntity("cat", Cat.class)
+ .addJoin("cat.dog");
+]]></programlisting>
+
+ <para>ì´ ìì ìì ë°íëë <literal>Cat</literal>ë¤ì ë°ì´í°ë² ì´ì¤ì
+ ëí ììì í¹ë³í ë¼ì´ëí¬ë¦½ ìì´ ì ì²´ì ì¼ë¡ ì´ê¸°íë ê·¸ê²ë¤ì <literal>dog</literal>
+ íë¡í¼í°ë¥¼ ê°ëë¤. ì°ë¦¬ê° joinì ëì íë¡í¼í° ê²½ë¡ë¥¼ ì§ì íë ê²ì ê°ë¥íëë¡ í기 ìí´
+ íëì alias ì´ë¦("cat")ì ì¶ê°íìì 주ì§íë¼. ëì ì ì를 ë¤ì´ <literal>Cat</literal>ì´
+ <literal>Dog</literal>ì ëí´ one-to-many를 ê°ì§ ê²½ì°, ì½ë ì
ë¤ì ëí´ ëì¼í eager joiningì
+ ííë ê²ì´ ê°ë¥íë¤.</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE, D_ID, D_NAME, CAT_ID FROM CATS c, DOGS d WHERE c.ID = d.CAT_ID")
+ .addEntity("cat", Cat.class)
+ .addJoin("cat.dogs");
+]]></programlisting>
+
+ <p>ì´ ë¨ê³ìì ì°ë¦¬ë Hibernateìì native ì§ìë¤ì ì¬ì©ê°ë¥íëë¡ ë§ë¤ê¸° ìí´
+ sql ì§ìë¤ì ê°íìí¤ì§ë ê²ì ììíì§ ìê³ ìë native ì§ìë¤ë¡ì ê°ë¥í ê²ì íê³ì
+ ëë¬íê³ ìë¤; 문ì ì ë¤ì ëì¼í íì
ì ì¬ë¬ ìí°í°ë¤ì ë°íí ë ëë ëí´í¸ alias/column
+ ì´ë¦ë¤ì´ 충ë¶íì§ ìì ë ë°ìí기 ììíë¤.</p>
+ </sect2>
+
+ <sect2>
+ <title>ì¬ë¬ ê°ì ìí°í°ë¤ì ë°íí기</title>
+
+ <para>ì§ê¸ê¹ì§ ê²°ê³¼ ì
ì»¬ë¼ ì´ë¦ë¤ì 매í 문ì ë´ì ì§ì ë ì»¬ë¼ ì´ë¦ë¤ê³¼ ëì¼íë¤ê³ ê°ì ëì´ ìë¤.
+ ëì¼í 컬ë¼ì´ íë ì´ìì í
ì´ë¸ ë´ìì ëíë ì ì기 ë문ì, ì´ê²ì ì¬ë¬ ê°ì í
ì´ë¸ë¤ì ì¡°ì¸ìí¤ë
+ SQL ì§ìë¤ì ëí´ ë¬¸ì ê° ë ì ìë¤.</para>
+
+ <para>ì»¬ë¼ alias 주ì
ì ë¤ì ì§ì(ìë§ ì¤í¨í ê²ì´ë¤)ìì íìíë¤:</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT c.*, m.* FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID")
+ .addEntity("cat", Cat.class)
+ .addEntity("mother", Cat.class)
+]]></programlisting>
+
+ <para>ì´ ì§ìì 목ì ì ë¨ì í ë¹ ë ê°ì Cat ì¸ì¤í´ì¤ë¤, íëì cat ê·¸ë¦¬ê³ ê·¸ê²ì mother를
+ ë°ííë ê²ì´ë¤. ìëíë©´ ê·¸ê²ë¤ì´ ëì¼í ì»¬ë¼ ì´ë¦ë¤ë¡ 매íëì´ ì기 ë문ì ì´ê²ì ì¤í¨í ê²ì´ê³
+ ë°ì´ë² ì´ì¤ ììì ë°íë ì»¬ë¼ aliasë¤ì ìë§ ë§¤íë¤ ë´ì ì§ì ë 컬ë¼ë¤("ID" ì "NAME")ê³¼ ê°ì§ ìì
+ "c.ID", "c.NAME" ë±ì íìì¼ ê²ì´ë¤.</para>
+
+ <para>ë¤ì íìì ì»¬ë¼ ì´ë¦ ì¤ë³µ ì·¨ì½ì ì ê°ì§ ìëë¤:</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT {cat.*}, {mother.*} FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID")
+ .addEntity("cat", Cat.class)
+ .addEntity("mother", Cat.class)
+]]></programlisting>
+
+ <para>ì´ ì§ìë ë¤ìì ì§ì íë¤:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>ì»¬ë¼ aliasë¤ì 주ì
í기 ìí Hibernateì© placeholderë¤ì ê°ì§ SQL ì§ì 문ìì´</para>
+ </listitem>
+
+ <listitem>
+ <para>ê·¸ ì§ìì ìí´ ë°íëë ìí°í°ë¤</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>ìì ì¬ì©ë {cat.*} ê³¼ {mother.*} í기ë "모ë íë¡í¼í°ë¤"ì ëí ìëµì´ë¤.
+ ë¤ë¥¸ ë°©ë²ì¼ë¡ ë¹ì ì 컬ë¼ë¤ì ëª
ìì ì¼ë¡ ì´ê±°í ìë ìì§ë§, ì´ ê²½ì°ì ì°ë¦¬ë Hibernateë¡ íì¬ê¸
+ SQL ì»¬ë¼ aliasë¤ì ê°ê°ì 컬ë¼ì 주ì
ìí¤ëë¡ ê°ì íë¤. ì»¬ë¼ alias를 ìí placeholderë ë¨ì§
+ ê·¸ í
ì´ë¸ aliasì ìí´ ììë íë¡í¼í° ì´ë¦ì´ë¤. ë¤ì ìì ìì, ì°ë¦¬ë ë¤ë¥¸ í
ì´ë¸(cat_log)ë¡ë¶í°
+ 매í ë©íë°ì´í° ë´ì ì ì¸ë ê²ì¼ë¡ì Catë¤ê³¼ ê·¸ê²ë¤ì motherë¤ì ê²ìíë¤. ì°ë¦¬ë ì°ë¦¬ê° ì¢ë¤ë©´
+ ì¬ì§ì´ where ì ë´ì íë¡í¼í° alias를 ì¬ì©í ìë ììì 주ì§íë¼.</para>
+
+ <programlisting><![CDATA[String sql = "SELECT ID as {c.id}, NAME as {c.name}, " +
+ "BIRTHDATE as {c.birthDate}, MOTHER_ID as {c.mother}, {mother.*} " +
+ "FROM CAT_LOG c, CAT_LOG m WHERE {c.mother} = c.ID";
+
+List loggedCats = sess.createSQLQuery(sql)
+ .addEntity("cat", Cat.class)
+ .addEntity("mother", Cat.class).list()
+]]></programlisting>
+
+ <sect3 id="querysql-aliasreferences" revision="2">
+ <title>alias 참조ì íë¡í¼í° 참조</title>
+
+ <para>ëë¶ë¶ì ê²½ì°ì ìì alias 주ì
ì´ íìíì§ë§, composite íë¡í¼í°ë¤, ìì íë³ìë¤
+ (inheritance discriminators), ì½ë ì
ë±ê³¼ ê°ì ë³´ë¤ ë³µì¡í 매íë¤ê³¼ ê´ë ¨ë ì§ìë¤ì ê²½ì°ìë
+ ê³ ì í aliasë¤ì 주ì
ìí¤ë ê²ì Hibernateìê² íì©íëë° ì¬ì©ë ëªëª í¹ë³í aliasë¤ì´ ì¡´ì¬íë¤.</para>
+
+ <para>ë¤ì í
ì´ë¸ì alias 주ì
ì ì¬ì©íë ë¤ë¥¸ ê°ë¥ì±ë¤ì ë³´ì¬ì¤ë¤. ë
¸í¸: ê²°ê³¼ ë´ì ìë alias ì´ë¦ë¤ì´
+ ìì ì´ë©°, ê°ê°ì aliasë ì¬ì©ë ë íëì ì ì¼í ì´ë¦ê³¼ ìë§ ë¤ë¥¸ ì´ë¦ì ê°ì§ ê²ì´ë¤.</para>
+
+ <table frame="topbot" id="aliasinjection-summary">
+ <title>alias 주ì
ì´ë¦ë¤</title>
+
+ <tgroup cols="3">
+ <colspec colwidth="1*" />
+
+ <colspec colwidth="1*" />
+
+ <colspec colwidth="2.5*" />
+
+ <thead>
+ <row>
+ <entry>ì¤ëª
</entry>
+
+ <entry>구문</entry>
+
+ <entry>ìì </entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry>ê°ë¨í íë¡í¼í°</entry>
+ <entry><literal>{[aliasname].[propertyname]</literal></entry>
+
+ <entry><literal>A_NAME as {item.name}</literal></entry>
+ </row>
+
+ <row>
+ <entry>composite íë¡í¼í°</entry>
+
+ <entry><literal>{[aliasname].[componentname].[propertyname]}</literal></entry>
+
+ <entry><literal>CURRENCY as {item.amount.currency}, VALUE as {item.amount.value}</literal></entry>
+ </row>
+
+ <row>
+ <entry>ìí°í°ì íë³ì(Discriminator)</entry>
+ <entry><literal>{[aliasname].class}</literal></entry>
+
+ <entry><literal>DISC as {item.class}</literal></entry>
+ </row>
+
+ <row>
+ <entry>ìí°í°ì 모ë íë¡í¼í°ë¤</entry>
+ <entry><literal>{[aliasname].*}</literal></entry>
+
+ <entry><literal>{item.*}</literal></entry>
+ </row>
+
+ <row>
+ <entry>ì½ë ì
í¤</entry>
+ <entry><literal>{[aliasname].key}</literal></entry>
+
+ <entry><literal>ORGID as {coll.key}</literal></entry>
+ </row>
+
+ <row>
+ <entry>ì½ë ì
ì id</entry>
+ <entry><literal>{[aliasname].id}</literal></entry>
+
+ <entry><literal>EMPID as {coll.id}</literal></entry>
+ </row>
+
+ <row>
+ <entry>ì½ë ì
ì ìì</entry>
+ <entry><literal>{[aliasname].element}</literal></entry>
+
+ <entry><literal>XID as {coll.element}</literal></entry>
+ <entry></entry>
+ </row>
+
+ <row>
+ <entry>ì½ë ì
ë´ì ìë ììì íë¡í¼í°</entry>
+ <entry><literal>{[aliasname].element.[propertyname]}</literal></entry>
+
+ <entry><literal>NAME as {coll.element.name}</literal></entry>
+ </row>
+
+ <row>
+ <entry>ì½ë ì
ë´ì ìë ììì 모ë íë¡í¼í°ë¤</entry>
+ <entry><literal>{[aliasname].element.*}</literal></entry>
+
+ <entry><literal>{coll.element.*}</literal></entry>
+ </row>
+
+ <row>
+ <entry>ì½ë ì
ì 모ë íë¡í¼í°ë¤</entry>
+ <entry><literal>{[aliasname].*}</literal></entry>
+
+ <entry><literal>{coll.*}</literal></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect3>
+ </sect2>
+
+ <sect2>
+ <title>non-managed ìí°í°ë¤ì ë°íí기</title>
+
+ <para>native sql ì§ìì ResultTransformer를 ì ì©íë ê²ì´ ê°ë¥íë¤. ì를 ë¤ì´
+ non-managed ìí°í°ë¤ì ë°íí기 ìí´ ResultTransformer를 íì©íë ê².</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT NAME, BIRTHDATE FROM CATS")
+ .setResultTransformer(Transformers.aliasToBean(CatDTO.class))]]></programlisting>
+
+ <para>ì´ ì§ìë ë¤ìì ì§ì íë¤:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>SQL ì§ì 문ìì´</para>
+ </listitem>
+
+ <listitem>
+ <para>ê²°ê³¼ ë³íì(transformer)</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ìì ì§ìë ì´ê¸°íëì´ ìê³ NAMEê³¼ BIRTHNAMEì ê°ë¤ì <literal>CatDTO</literal>ì
+ ëìíë íë¡í¼í°ë¤ê³¼ íëë¤ ìì¼ë¡ 주ì
ìí¨ <literal>CatDTO</literal>ì 리ì¤í¸ë¥¼ ë°íí ê²ì´ë¤.
+ </para>
+ </sect2>
+
+ <sect2>
+ <title>ìì ì²ë¦¬í기</title>
+
+ <para>ììì ë¶ë¶ì¼ë¡ì 매íëë ìí°í°ë¤ì ì§ìíë native sql ì§ìë¤ì baseclassì
+ 모ë íë¡í¼í°ë¤ì í¬í¨í´ì¼ íê³ ê·¸ 모ë ê²ì´ ìë¸í´ëì¤í ëì´ì¼ íë¤.</para>
+ </sect2>
+
+ <sect2>
+ <title>íë¼ë¯¸í°ë¤</title>
+
+ <para>Native sql ì§ìë¤ì ìì¹ íë¼ë¯¸í°ë¤ ë¿ë§ ìëë¼ ëª
ëª
ë íë¼ë¯¸í°ë¤ì ì§ìíë¤:</para>
+
+ <programlisting><![CDATA[Query query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME like ?").addEntity(Cat.class);
+List pusList = query.setString(0, "Pus%").list();
+
+query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME like :name").addEntity(Cat.class);
+List pusList = query.setString("name", "Pus%").list(); ]]></programlisting>
+ </sect2>
+
+
+
+ </sect1>
+
+ <sect1 id="querysql-namedqueries" revision="3">
+ <title>ëª
ëª
ë SQL ì§ìë¤</title>
+
+ <para>
+ ëª
ëª
ë SQL ì§ìë¤ì HQL ì§ìì ëì¼í ë°©ë²ì¼ë¡ 매í 문ì ìì ì ìë ì ìê³ ì ííê² í¸ì¶ë ìë ìë¤. ì´ ê²½ì°ì, ì°ë¦¬ë
+ <literal>addEntity()</literal> í¸ì¶ì íìë¡ íì§ <emphasis>ìëë¤</emphasis>.
+ </para>
+
+ <programlisting><![CDATA[<sql-query name="persons">
+ <return alias="person" class="eg.Person"/>
+ SELECT person.NAME AS {person.name},
+ person.AGE AS {person.age},
+ person.SEX AS {person.sex}
+ FROM PERSON person
+ WHERE person.NAME LIKE :namePattern
+</sql-query>]]></programlisting>
+
+ <programlisting><![CDATA[List people = sess.getNamedQuery("persons")
+ .setString("namePattern", namePattern)
+ .setMaxResults(50)
+ .list();]]></programlisting>
+
+ <para><literal><return-join></literal> ììì
+ <literal><load-collection></literal>
+ ììë ì°ê´ë¤ì ì¡°ì¸ìí¤ê³ ì½ë ì
ë¤ì ê°ê° ì´ê¸°í ìí¤ë ì§ìë¤ì
+ ì ìíëë° ì¬ì©ëë¤.</para>
+
+ <programlisting><![CDATA[<sql-query name="personsWith">
+ <return alias="person" class="eg.Person"/>
+ <return-join alias="address" property="person.mailingAddress"/>
+ SELECT person.NAME AS {person.name},
+ person.AGE AS {person.age},
+ person.SEX AS {person.sex},
+ adddress.STREET AS {address.street},
+ adddress.CITY AS {address.city},
+ adddress.STATE AS {address.state},
+ adddress.ZIP AS {address.zip}
+ FROM PERSON person
+ JOIN ADDRESS adddress
+ ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'
+ WHERE person.NAME LIKE :namePattern
+</sql-query>]]></programlisting>
+
+ <para>ëª
ëª
ë SQL ì§ìë ì¤ì¹¼ë¼ ê°ì ë°íí ìë ìë¤. ë¹ì ì
+ <literal><return-scalar></literal> ìì를 ì¬ì©íì¬
+ ì»¬ë¼ aliasì Hibernate íì
ì ì ì¸í´ì¼ íë¤:</para>
+
+ <programlisting><![CDATA[<sql-query name="mySqlQuery">
+ <return-scalar column="name" type="string"/>
+ <return-scalar column="age" type="long"/>
+ SELECT p.NAME AS name,
+ p.AGE AS age,
+ FROM PERSON p WHERE p.NAME LIKE 'Hiber%'
+</sql-query>]]></programlisting>
+
+ <para>ë¹ì ì ì¬ë¬ ê°ì ëª
ëª
ë ì§ìë¤ì ê°ë¡ì§ë¬ ì¬ì¬ì©íê±°ë <literal>setResultSetMapping()</literal> API를 íµí´ ê²°ê³¼ì
+ 매íì ë³´ë¤ì ì¬ì¬ì©í기 ìí´ <literal><resultset></literal> ìì ìì ê²°ê³¼ì
매í ì ë³´ë¤ì 구체í ìí¬ ì ìë¤.</para>
+
+ <programlisting><![CDATA[<resultset name="personAddress">
+ <return alias="person" class="eg.Person"/>
+ <return-join alias="address" property="person.mailingAddress"/>
+</resultset>
+
+<sql-query name="personsWith" resultset-ref="personAddress">
+ SELECT person.NAME AS {person.name},
+ person.AGE AS {person.age},
+ person.SEX AS {person.sex},
+ adddress.STREET AS {address.street},
+ adddress.CITY AS {address.city},
+ adddress.STATE AS {address.state},
+ adddress.ZIP AS {address.zip}
+ FROM PERSON person
+ JOIN ADDRESS adddress
+ ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'
+ WHERE person.NAME LIKE :namePattern
+</sql-query>]]></programlisting>
+
+ <para>ë¤ë¥¸ë°©ë²ì¼ë¡ ë¹ì ì ë¹ì ì hbm íì¼ë¤ ë´ì ì§ì ìë° ì½ëë¡ ë ê²°ê³¼ì
매í ì 보를 ì¬ì©í ì ìë¤.</para>
+
+ <programlisting><![CDATA[List cats = sess.createSQLQuery(
+ "select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id"
+ )
+ .setResultSetMapping("catAndKitten")
+ .list();]]></programlisting>
+
+ <sect2 id="propertyresults">
+ <title>ëª
ìì ì¼ë¡ column/alias ì´ë¦ë¤ì ì§ì íëë° return-property ì¬ì©í기</title>
+
+ <para>Hibernateë¡ íì¬ê¸ ê·¸ê² ìì ì aliasë¤ì ë¼ìë£ëë¡ í기 ìí´
+ <literal>{}</literal>-구문ì ì¬ì©íë ê² ëì ì,
+ <literal><return-property></literal>ë¡ì ë¹ì ì ì¬ì©í
+ ì»¬ë¼ aliasë¤ì´ 무ìì¸ì§ë¥¼ Hibernateìê² ëª
ìì ì¼ë¡ ìë ¤ì¤ ì ìë¤.</para>
+
+ <programlisting><![CDATA[<sql-query name="mySqlQuery">
+ <return alias="person" class="eg.Person">
+ <return-property name="name" column="myName"/>
+ <return-property name="age" column="myAge"/>
+ <return-property name="sex" column="mySex"/>
+ </return>
+ SELECT person.NAME AS myName,
+ person.AGE AS myAge,
+ person.SEX AS mySex,
+ FROM PERSON person WHERE person.NAME LIKE :name
+</sql-query>
+]]></programlisting>
+
+ <para><literal><return-property></literal>ë ëí ë¤ì¤ 컬ë¼ë¤ì ëí´ ëìíë¤.
+ ì´ê²ì ë¤ì¤-ì»¬ë¼ íë¡í¼í°ë¤ì ëí fine grained ì ì´ë¥¼ íì©í ì ìë
+ <literal>{}</literal>-구문ì ê°ì§ ì ì½ì í´ê²°í´ì¤ë¤.</para>
+
+ <programlisting><![CDATA[<sql-query name="organizationCurrentEmployments">
+ <return alias="emp" class="Employment">
+ <return-property name="salary">
+ <return-column name="VALUE"/>
+ <return-column name="CURRENCY"/>
+ </return-property>
+ <return-property name="endDate" column="myEndDate"/>
+ </return>
+ SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer},
+ STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},
+ REGIONCODE as {emp.regionCode}, EID AS {emp.id}, VALUE, CURRENCY
+ FROM EMPLOYMENT
+ WHERE EMPLOYER = :id AND ENDDATE IS NULL
+ ORDER BY STARTDATE ASC
+</sql-query>]]></programlisting>
+
+ <para>ì´ ìì ìì ì°ë¦¬ë ë¼ìë£ê¸°(injection)를 ìí´ <literal>{}</literal>-구문과 í¨ê» <literal><return-property></literal>를
+ ì¬ì©íìì 주목íë¼. ì¬ì©ìë¤ì´ 컬ë¼ê³¼ íë¡í¼í°ë¤ì 참조íê³ ì ìíë ë°©ë²ì ì ííë ê²ì ì¬ì©ìë¤ìê² íì©í´ì¤ì¼ë¡ì¨.</para>
+
+ <para>ë§ì¼ ë¹ì ì 매íì´ í ê°ì íë³ì(discriminator )를 ê°ì§ ê²½ì° ë¹ì ì íë³ì 컬ë¼ì ì§ì íëë°
+ <literal><return-discriminator></literal>를 ì¬ì©í´ì¼ íë¤.</para>
+ </sect2>
+
+ <sect2 id="sp_query" revision="1">
+ <title>ì§ì를 ìí ë´ì¥ íë¡ìì ì¬ì©í기</title>
+
+ <para>Hibernate 3ì ë´ì¥ íë¡ìì ë¤ê³¼ í¨ìë¤ì íµí ì§ì ì§ìì ëì
íë¤. ëë¶ë¶ì ë¤ì 문ìë ìì 모ëì ëì¼íê² ì ì©ëë¤.
+ ë´ì¥ íë¡ìì /í¨ìë Hibernateì ëìíë ê²ì´ ê°ë¥íëë¡ ì²« ë²ì§¸ out-íë¼ë¯¸í°ë¡ì í ê°ì ê²°ê³¼ì
ì ë°íí´ì¼ íë¤.
+ Oracle9 ì´ìì ë²ì ìì ê·¸ë° ë´ì¥ íë¡ìì ì ëí ìì ë ë¤ìê³¼ ê°ë¤:</para>
+
+ <programlisting><![CDATA[CREATE OR REPLACE FUNCTION selectAllEmployments
+ RETURN SYS_REFCURSOR
+AS
+ st_cursor SYS_REFCURSOR;
+BEGIN
+ OPEN st_cursor FOR
+ SELECT EMPLOYEE, EMPLOYER,
+ STARTDATE, ENDDATE,
+ REGIONCODE, EID, VALUE, CURRENCY
+ FROM EMPLOYMENT;
+ RETURN st_cursor;
+ END;]]></programlisting>
+
+ <para>Hibernateìì ì´ ì§ì를 ì¬ì©í기 ìí´ ë¹ì ì íëì ëª
ëª
ë ì§ì(a named query)를 íµí´
+ ê·¸ê²ì 매íí íìê° ìë¤.</para>
+
+ <programlisting><![CDATA[<sql-query name="selectAllEmployees_SP" callable="true">
+ <return alias="emp" class="Employment">
+ <return-property name="employee" column="EMPLOYEE"/>
+ <return-property name="employer" column="EMPLOYER"/>
+ <return-property name="startDate" column="STARTDATE"/>
+ <return-property name="endDate" column="ENDDATE"/>
+ <return-property name="regionCode" column="REGIONCODE"/>
+ <return-property name="id" column="EID"/>
+ <return-property name="salary">
+ <return-column name="VALUE"/>
+ <return-column name="CURRENCY"/>
+ </return-property>
+ </return>
+ { ? = call selectAllEmployments() }
+</sql-query>]]></programlisting>
+
+ <para>ë´ì¥ íë¡ìì ë¤ì íì¬ ì¤ì¹¼ë¼ë¤ê³¼ ìí°í°ë¤ ë§ì ë°íí¨ì 주목íë¼. <literal><return-join></literal>ê³¼
+ <literal><load-collection></literal>ì ì§ìëì§ ìëë¤.</para>
+
+ <sect3 id="querysql-limits-storedprocedures" revision="1">
+ <title>ë´ì¥ íë¡ìì ë¤ì ì¬ì©íë ê·ì¹ë¤/ì ì½ë¤</title>
+
+ <para>Hibernateìì ë´ì¥ íë¡ìì ë¤ì ì¬ì©í기 ìí´ì íë¡ìì ë¤/í¨ìë¤ì ë¤ì ëªëª ê·ì¹ë¤ì ë°ë¼ì¼ íë¤. ë§ì¼ ê·¸ê²ë¤ì´ ê·¸ë¤ ê·ì¹ë¤ì
+ ë°ë¥´ì§ ìì ê²½ì° ê·¸ê²ë¤ì Hibernateì í¨ê» ì¬ì© ë¶ê°ë¥íë¤. ë§ì¼ ë¹ì ì´ ì¬ì í ì´ë¤ íë¡ìì ë¤ì ì¬ì©íê³ ì ìí ê²½ì°,
+ ë¹ì ì <literal>session.connection()</literal>ì íµí´ ê·¸ê²ë¤ì ì¤íìì¼ì¼ íë¤. ë°ì´í°ë² ì´ì¤ 벤ëë¤ì´ ë¤ë¥¸ ë´ì¥
+ íë¡ìì ìë¯¸ë¡ /구문ì ê°ê³ ì기 ë문ì, ê·ì¹ë¤ì ê°ê°ì ë°ì´í°ë² ì´ì¤ì ë°ë¼ ì°¨ì´ê° ëë¤.</para>
+
+ <para>ë´ì¥ íë¡ìì ì§ìë¤ì <literal>setFirstResult()/setMaxResults()</literal>ë¡ì ìª½ë§¤ê¹ ë ì ìë¤.</para>
+
+ <para>ê¶ì¥ëë í¸ì¶ íìì íì¤ SQL92ì´ë¤: <literal>{ ? = call
+ functionName(<parameters>) }</literal> ëë <literal>{ ? = call
+ procedureName(<parameters>}</literal>. Native í¸ì¶ 구문ì ì§ìëì§ ìëë¤.</para>
+
+ <para>Oracleì ê²½ì° ë¤ì ê·ì¹ë¤ì´ ì ì©ëë¤:</para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>íëì í¨ìë íëì ê²°ê³¼ ì
ì ë°íí´ì¼ íë¤. íë¡ìì ì 첫 ë²ì§¸ íë¼ë¯¸í°ë íëì ê²°ê³¼ ì
ì ë°ííë
+ íëì <literal>OUT</literal>ì´ì´ì¼ íë¤. ì´ê²ì Oracle 9 ëë 10ìì íëì
+ <literal>SYS_REFCURSOR</literal>를 ì¬ì©íì¬ íí´ì§ë¤. Oracleìì ë¹ì ì
+ <literal>REF CURSOR</literal> íì
ì ì ìí íìê° ìëë°, Oracle ë³´ê³ ì를 ë³´ë¼.</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>Sybase ëë MS SQL serverì ê²½ì° ë¤ì ê·ì¹ë¤ì´ ì ì©ëë¤:</para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>íë¡ìì ë í ê°ì ê²°ê³¼ ì
ì ë°íí´ì¼ íë¤. ì´ë¤ ìë²ë¤ì´ ì¬ë¬ ê°ì ê²°ê³¼ì
ë¤ê³¼ ì
ë°ì´í¸ ì¹´ì´í¸ë¤ì ë°í í ì ìë¤/í ê²ì´ì´ë¯ë¡,
+ Hibernateë ê²°ê³¼ë¤ì ë°ë³µ ìíí ê²ì´ê³ ê·¸ê²ì ë°í ê°ì¼ë¡ì íëì ê²°ê³¼ ì
ì¸ ì²« ë²ì§¸ 결과를 ì·¨í ê²ì´ë¤. ê·¸ ë°ì 모ë ê²ì
+ í기ë ê²ì´ë¤.</para>
+ </listitem>
+ <listitem>
+ <para>ë§ì¼ ë¹ì ì´ ë¹ì ì íë¡ìì ë´ì <literal>SET NOCOUNT ON</literal>ì ì´ì© ê°ë¥íê² í ì ìë¤ë©´ ê·¸ê²ì ìë§
+ ë³´ë¤ í¨ì¨ì ì´ê² ë ê²ì´ì§ë§ ì´ê²ì íì ì¡°ê±´ì´ ìëë¤.</para>
+ </listitem>
+ </itemizedlist>
+ </sect3>
+ </sect2>
+ </sect1>
+
+ <sect1 id="querysql-cud">
+ <title>create, update ê·¸ë¦¬ê³ delete를 ìí ë§ì¶¤í SQL</title>
+
+ <para>Hibernate3ë create, update, delete ì¤í¼ë ì´ì
ë¤ì ìí ë§ì¶¤í 문ì¥ë¤ì ì¬ì©í ì ìë¤. Hibernateìì í´ëì¤ì ì½ë ì
+ ìììë¤ì êµ¬ì± ìì ìì±ë 문ìì´ë¤ì ì§í©(insertsql, deletesql, updatesql ë±)ì ì´ë¯¸ í¬í¨íê³ ìë¤.
+ <literal><sql-insert></literal>, <literal><sql-delete></literal>,
+ <literal><sql-update></literal> 매í íê·¸ë¤ì ì´ë¤ 문ìì´ë¤ì ì¤ë²ë¼ì´ë ìí¨ë¤:</para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true"/>
+ <sql-insert>INSERT INTO PERSON (NAME, ID) VALUES ( UPPER(?), ? )</sql-insert>
+ <sql-update>UPDATE PERSON SET NAME=UPPER(?) WHERE ID=?</sql-update>
+ <sql-delete>DELETE FROM PERSON WHERE ID=?</sql-delete>
+</class>]]></programlisting>
+
+ <para>SQLì´ ë¹ì ì ë°ì´í°ë² ì´ì¤ ë´ìì ì§ì ì¤íëì´ì, ë¹ì ì´ ì¢ìíë ììì dialect를 ì¬ì©íë ê²ì´ ìì ë¡ë¤. ë§ì¼ ë¹ì ì´ ë°ì´í°ë² ì´ì¤
+ ì§ì ì ì¸ SQLì ì¬ì©í ê²½ì° ì´ê²ì ë¬¼ë¡ ë¹ì ì 매íì ì´ìì±ì ê°ììí¬ ê²ì´ë¤.</para>
+
+ <para>ë§ì¼ <literal>callable</literal> ìì±ì´ ì¤ì ëë©´ ë´ì¥ íë¡ìì ë¤ì´ ì§ìëë¤:</para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true"/>
+ <sql-insert callable="true">{call createPerson (?, ?)}</sql-insert>
+ <sql-delete callable="true">{? = call deletePerson (?)}</sql-delete>
+ <sql-update callable="true">{? = call updatePerson (?, ?)}</sql-update>
+</class>]]></programlisting>
+
+ <para>ìì¹ íë¼ë¯¸í°ë¤ì Hibernateê° ê·¸ê²ë¤ì 기ëíë ê²ê³¼ ê°ì ììê° ëì´ì¼ íë¯ë¡,
+ ìì¹ íë¼ë¯¸í°ë¤ì ììë íì¬ ì ëì ì¼ë¡ ì¤ìíë¤.</para>
+
+ <para>ë¹ì ì <literal>org.hiberate.persister.entity</literal> ë ë²¨ë¡ ëë²ê·¸ ë¡ê¹
ì ì¬ì© ê°ë¥íê² í¨ì¼ë¡ì¨ ììë ìì를 ë³¼ ì
+ ìë¤. ì´ ë 벨ì ì´ì© ê°ë¥íê² íë©´ Hibernateë ìí°í°ë¤ì ìì±ìí¤ê³ , ì
ë°ì´í¸íê³ , ìì íëë° ì¬ì©ëë ì ì ì¸ SQLì ì¶ë ¥í ê²ì´ë¤.
+ (ììëë 결과를 ë³´ë ¤ë©´, Hibernate ìì±ë ì ì ì¸ sqlì ì¤ë²ë¼ì´ë ìí¤ê² 매í íì¼ë¤ ìì ë¹ì ì ë§ì¶¤í SQLì í¬í¨ìí¤ì§ ìëë¡ ì¼ëì
+ ëë¼.)</para>
+
+ <para>Hibernateê° ë¬¸ì¥ì ì±ê³µì ìí´ ëªëª ì¤í ì ì²´í¬ë¤ì ííë¯ë¡, ë´ì¥ íë¡ìì ë¤ì ëë¶ë¶ì ê²½ì°ë¤(ì½ê¸°:ë¤ë¥¸ ê²½ì°ë¤ ë³´ë¤ ê·¸ê²ì ë ì
+ ííë¤)ìì insertëê³ /ì
ë°ì´í¸ëê³ /ìì ë íë¤ì ê°ì를 ë°ííëë° íìíë¤. Hibernateë íì CUD ì¤í¼ë ì´ì
ë¤ì ëí ì«ì
+ ì¶ë ¥ íë¼ë¯¸í°ë¡ì 첫 ë²ì§¸ ë¬¸ì¥ íë¼ë¯¸í°ë¥¼ ë±ë¡ìí¨ë¤:</para>
+
+ <programlisting><![CDATA[CREATE OR REPLACE FUNCTION updatePerson (uid IN NUMBER, uname IN VARCHAR2)
+ RETURN NUMBER IS
+BEGIN
+
+ update PERSON
+ set
+ NAME = uname,
+ where
+ ID = uid;
+
+ return SQL%ROWCOUNT;
+
+END updatePerson;]]></programlisting>
+ </sect1>
+
+ <sect1 id="querysql-load">
+ <title>ë¡ë©ì ìí ë§ì¶¤í SQL</title>
+
+ <para>ë¹ì ì ëí ìí°í° ë¡ë©ì ìí ë¹ì ìì ì SQL (ëë HQL)ì ì ì¸í ìë ìë¤:</para>
+
+ <programlisting><![CDATA[<sql-query name="person">
+ <return alias="pers" class="Person" lock-mode="upgrade"/>
+ SELECT NAME AS {pers.name}, ID AS {pers.id}
+ FROM PERSON
+ WHERE ID=?
+ FOR UPDATE
+</sql-query>]]></programlisting>
+
+ <para>ì´ê²ì ìì ë
¼ìíë¯ì´ ë¨ì§ ëª
ëª
ë ì§ì ì ì¸ì´ë¤. ë¹ì ì class 매í ìì ì´ ëª
ëª
ë ì§ì를 참조í ì ìë¤:</para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true"/>
+ <loader query-ref="person"/>
+</class>]]></programlisting>
+
+ <para>ì´ê²ì ì¬ì§ì´ ë´ì¥ íë¡ìì ë¤ì ëìíë¤.</para>
+
+ <para>ë¹ì ì ì½ë ì
ë¡ë©ì ìí í ê°ì ì§ì를 ì ìí ìë ìë¤:</para>
+
+ <programlisting><![CDATA[<set name="employments" inverse="true">
+ <key/>
+ <one-to-many class="Employment"/>
+ <loader query-ref="employments"/>
+</set>]]></programlisting>
+
+ <programlisting><![CDATA[<sql-query name="employments">
+ <load-collection alias="emp" role="Person.employments"/>
+ SELECT {emp.*}
+ FROM EMPLOYMENT emp
+ WHERE EMPLOYER = :id
+ ORDER BY STARTDATE ASC, EMPLOYEE ASC
+</sql-query>]]></programlisting>
+
+ <para>ë¹ì ì ì¬ì§ì´ ì¡°ì¸ íì¹ì ìí´ íëì ì½ë ì
ì ë¡ëìí¤ë íëì ìí°í°ë¥¼ ì ìí ì ìë¤:</para>
+
+ <programlisting><![CDATA[<sql-query name="person">
+ <return alias="pers" class="Person"/>
+ <return-join alias="emp" property="pers.employments"/>
+ SELECT NAME AS {pers.*}, {emp.*}
+ FROM PERSON pers
+ LEFT OUTER JOIN EMPLOYMENT emp
+ ON pers.ID = emp.PERSON_ID
+ WHERE ID=?
+</sql-query>]]></programlisting>
+ </sect1>
+</chapter>
\ No newline at end of file
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/quickstart.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/quickstart.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/quickstart.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,572 @@
+<chapter id="quickstart">
+ <title>Tomcatì¼ë¡ ë¹ ë¥¸ ìì</title>
+
+ <sect1 id="quickstart-intro" revision="2">
+ <title>Hibernate ììí기</title>
+
+ <para>
+ ì´ íí 리ì¼ì ì¹ ê¸°ë°ì ì´í리ì¼ì´ì
ì© Apache Tomcat ìë¸ë¦¿ 컨í
ì´ëì ëí Hibernate 3.0 ì
ì
ì
+ ì¤ëª
íë¤(ì°ë¦¬ë ë²ì 4.1ì ì¬ì©íë¤. 5.0ì ëí ì°¨ì´ì ë¤ì ì ì ê²ì´ë¤). Hibernateë 모ë 주ì J2EE
+ ì´í리ì¼ì´ì
ìë²ë¤ì ê´ë¦¬ëë íê²½ìì ì ëìíê³ , ëë ì¬ì§ì´ ì¤í ëì¼ë¡ ì´í리ì¼ì´ì
ë¤ììë ì ëì
+ íë¤. ì´ íí 리ì¼ì ì¬ì©ëë ë°ì´í°ë² ì´ì¤ ìì¤í
ì PostgreSQL 7.4ì´ê³ , ë¤ë¥¸ ë°ì´í°ë² ì´ì¤ì ëí ì§ìì
+ ë¨ì§ Hibernate SQL dialect 구ì±ê³¼ 커ë¥ì
íë¡í¼í°ë¤ì ë³ê²½ìí¤ë ê²ìë§ ê´ê³ëë¤.
+ </para>
+
+ <para>
+ 먼ì ì°ë¦¬ë 모ë íìì ì¸ ë¼ì´ë¸ë¬ë¦¬ë¤ì Tomcat ì¤ì¹ ì¥ìì ë³µì¬í´ì¼ íë¤. ì°ë¦¬ë ì´ íí 리ì¼ì ìí´ ë³ëì
+ ì¹ ì»¨í
ì¤í¸(<literal>webapps/quickstart</literal>)를 ì¬ì©íë©°, ë°ë¼ì ì°ë¦¬ë ì ì ë¼ì´ë¸ë¬ë¦¬ ê²ì ê²½ë¡
+ (<literal>TOMCAT/common/lib</literal>)ì <literal>webapps/quickstart/WEB-INF/lib</literal>
+ (JAR íì¼ë¤ì ê²½ì°)ì <literal>webapps/quickstart/WEB-INF/classes</literal> ë´ ì ìë 컨í
ì¤í¸
+ ë 벨ìì í´ëì¤ë¡ë ìì를 ê³ ë ¤í´ì¼ íë¤. ì°ë¦¬ë ë ê°ì í´ëì¤ë¡ë ë 벨ë¤ì ì ì classpathì 컨í
ì¤í¸
+ classpathë¡ì ì¸ê¸íë¤.
+ </para>
+
+ <para>
+ ì´ì ë¼ì´ë¸ë¬ë¦¬ë¤ì ë ê°ì classpathë¤ì ë³µì¬íë¼:
+ </para>
+
+ <orderedlist>
+ <listitem>
+ <para>
+ ë°ì´í°ë² ì´ì¤ ì© JDBC ëë¼ì´ë²ë¥¼ ì ì classpathë¡ ë³µì¬íë¼. ì´ê²ì Tomcatì ë²ë¤ë¡êµ¬ì±ë DBCP 커ë¥ì
+ í ìíí¸ì¨ì´ì íìíë¤. Hibernateë ë°ì´í°ë² ì´ì¤ ììì SQLì ì¤íìí¤ëë° JDBC 커ë¥ì
ë¤ì ì¬ì©íë¯ë¡,
+ ë¹ì ì íë§ë JDBC 커ë¥ì
ë¤ì ì ê³µí´ì¼ íê±°ë, ì§ì ì§ìëë íë¤(C3P0, Proxool) ì¤ íë를 ì¬ì©í기
+ ìí´ Hibernate를 구ì±í´ì¼ íë¤. ì´ íí 리ì¼ì ìí´, (PostgreSQL 7.4ì JDK 1.4ì©) <literal>pg74jdbc3.jar</literal>
+ ë¼ì´ë¸ë¬ë¦¬ë¥¼ ì ì classloaders ê²½ë¡ë¡ ë³µì¬íë¼. ë§ì¼ ë¹ì ì´ ë¤ë¥¸ ë°ì´í°ë² ì´ì¤ë¥¼ ì¬ì©íê³ ì ìí ê²½ì°,
+ ê°ë¨íê² ê·¸ê²ì ì ì í JDBC ëë¼ì´ë²ë¥¼ ë³µì¬íë¼.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ê·¸ ë°ì ì´ë¤ ê²ì Tomcat ë´ì ì ì í´ëì¤ë¡ë ê²½ë¡ì ë³µì¬íì§ ë§ë¼. ëë ë¹ì ì Log4j, commons-logging
+ ê·¸ë¦¬ê³ ë¤ë¥¸ ê²ë¤ì í¬í¨íë ì¬ë¬ ê°ì§ ë구ë¤ì ê´ë ¨ë 문ì ì ë¤ì ì»ê² ë ê²ì´ë¤. ê°ê°ì ì¹ ì´í리ì¼ì´ì
ì ëí´
+ 컨í
ì¤í¸ classpath를 ì¬ì©íë¼. ì¦ ë¼ì´ë¸ë¬ë¦¬ë¤ì <literal>WEB-INF/lib</literal>ì ë³µì¬íê³ , ë¹ì ìì ì í´ëì¤ë¤ê³¼
+ êµ¬ì± íì¼ë¤/íë¡í¼í° íì¼ë¤ì <literal>WEB-INF/classes</literal>ì ë³µì¬íë¼. ë ëë í ë¦¬ë¤ ììë ëí´í¸ë¡ 컨í
ì¤í¸
+ classpath ë´ì ìë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Hibernateë JAR ë¼ì´ë¸ë¬ë¦¬ë¡ì í¨í¤ì§í ëì´ ìë¤. <literal>hibernate3.jar</literal> íì¼ì ì´í리ì¼ì´ì
ì ë¤ë¥¸ í´ëì¤ë¤ê³¼
+ í¨ê» 컨í
ì¤í¸ classpath ìì ë³µì¬ëì´ì¼ íë¤. Hibernateë ì¤í ìì ì´ë¤ ì 3ì ë¼ì´ë¸ë¬ë¦¬ë¤ì íìë¡íê³ ,
+ ì´ê²ë¤ì <literal>lib/</literal> ëë í 리 ë´ì Hibernate ë°°í¬ë³¸ì ë²ë¤íëì´ ìë¤; <xref linkend="3rdpartylibs"/>를
+ ë³´ë¼. íìí ì 3ì ë¼ì´ë¸ë¬ë¦¬ë¤ì 컨í
ì¤í¸ classpathë¡ ë³µì¬íë¼.
+ </para>
+ </listitem>
+ </orderedlist>
+
+ <table frame="topbot" id="3rdpartylibs">
+ <title>
+ Hibernate ì 3ì ë¼ì´ë¸ë¬ë¦¬
+ </title>
+ <tgroup cols="2" rowsep="1" colsep="1">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="2*"/>
+ <thead>
+ <row>
+ <entry align="center">
+ ë¼ì´ë¸ë¬ë¦¬
+ </entry>
+ <entry align="center">
+ ì¤ëª
+ </entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ antlr (íì)
+ </entry>
+ <entry>
+ Hibernateë ì§ì íìë¤ì ì°ì¶íëë° ANTLRì ì¬ì©íê³ , ì´ ë¼ì´ë¸ë¬ë¦¬ë ëí
+ ì¤í ìì íìíë¤.
+ </entry>
+ </row>
+ <row>
+ <entry>
+ dom4j (íì)
+ </entry>
+ <entry>
+ Hibernateë XML 구ì±ê³¼ XML 매í ë©íë°ì´í° íì¼ë¤ì íì±íëë° dom4j를 ì¬ì©
+ íë¤.
+ </entry>
+ </row>
+ <row>
+ <entry>
+ CGLIB, asm (íì)
+ </entry>
+ <entry>
+ Hibernateë (Java reflectionê³¼ ê²°í©íì¬) ë°íì ìì í´ëì¤ë¤ì ê³ ììí¤ëë°
+ ì½ë ìì± ë¼ì´ë¸ë¬ë¦¬ë¥¼ ì¬ì©íë¤.
+ </entry>
+ </row>
+ <row>
+ <entry>
+ Commons Collections, Commons Logging (íì)
+ </entry>
+ <entry>
+ Hibernateë Apache Jakarta Commons íë¡ì í¸ë¡ë¶í° ë¤ìí ì í¸ë¦¬í° ë¼ì´ë¸ë¬ë¦¬
+ ë¤ì ì¬ì©íë¤.
+ </entry>
+ </row>
+ <row>
+ <entry>
+ EHCache (íì)
+ </entry>
+ <entry>
+ Hibernateë second-level ìºì를 ìí ë¤ìí ìºì íë¡ë°ì´ëë¤ì ì¬ì©í ì ìë¤.
+ ë§ì¼ 구ì±ìì ë³íì§ ìì ê²½ì° EHCacheê° ëí´í¸ ìºì íë¡ë°ì´ëì´ë¤.
+ </entry>
+ </row>
+ <row>
+ <entry>
+ Log4j (ìµì
)
+ </entry>
+ <entry>
+ Hibernateë 기본 ë¡ê¹
ë©ì»¤ëì¦ì¼ë¡ì Log4j를 ì¬ì©í ì ìë, Commons Logging
+ API를 ì¬ì©íë¤. ë§ì¼ Log4j ë¼ì´ë¸ë¬ë¦¬ê° 컨í
ì¤í¸ ë¼ì´ë¸ë¬ë¦¬ ëë í 리 ììì ì´ì©
+ ê°ë¥íë¤ë©´, Commons Loggingì Log4jì 컨í
ì¤í¸ classpath ë´ì ìë <literal>log4j.properties</literal>
+ 구ì±ì ì¬ì©í ê²ì´ë¤. Log4jì ëí ìì properties íì¼ì Hibernate ë°°í¬ë³¸ì
+ ë²ë¤í ëì´ ìë¤. ë°ë¼ì ë¹ì ì´ ì´ë©´ìì 무ìì´ ì§íëë ì§ì ë³´ê³ ì ìí ê²½ì°ì log4j.jarì
+ (<literal>src/</literal>ì ìë) êµ¬ì± íì¼ì ë¹ì ì 컨í
ì¤í¸ classpath ìì¼ë¡ ë³µì¬íë¼.
+ </entry>
+ </row>
+ <row>
+ <entry>
+ íì ì¬ë¶?
+ </entry>
+ <entry>
+ Hibernate ë°°í¬ë³¸ ë´ì ìë <literal>lib/README.txt</literal> íì¼ì ì´í´ë³´ë¼.
+ ì´ê²ì Hibernateì ë°°í¬ë ì 3ì ë¼ì´ë¸ë¬ë¦¬ë¤ì ìµì 목ë¡ì´ë¤. ë¹ì ì 그곳ì ì´ê±°ë
+ 모ë íì ë¼ì´ë¸ë¬ë¦¬ë¤ê³¼ ìµì
ë¼ì´ë¸ë¬ë¦¬ë¤ì ì°¾ê² ë ê²ì´ë¤(ì¬ê¸°ì "ë¹ë ì íìí¨"ì
+ ë¹ì ì ì´í리ì¼ì´ì
ì´ ìëë¼ Hibernateì ëí ì미ìì ë
¸í¸íë¼).
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ ì°ë¦¬ë ì´ì Tomcatê³¼ Hibernate ìììì ë°ì´í°ë² ì´ì¤ 커ë¥ì
íë§ê³¼ ê³µì 를 ì¤ì íë¤. ì´ê²ì Tomcatì´
+ (ê·¸ê²ì 미리 ë¹ëëì´ ìë DBCP íë§ í¹ì§ì ì¬ì©íì¬) íë§ë JDBC 커ë¥ì
ë¤ì ì ê³µí ê²ì´ê³ , Hibernateê°
+ JNDI를 íµí´ ì´ë¤ 커ë¥ì
ë¤ì ìì²íë¤ë ê²ì ì미íë¤. ë¬ë¦¬ ë¹ì ì Hibernateë¡ íì¬ê¸ 커ë¥ì
íì ê´ë¦¬íëë¡
+ í ì ìë¤. Tomcatì ê·¸ê²ì 커ë¥ì
íì JNDIì ë°ì¸ë ìí¨ë¤; ì°ë¦¬ë 리ìì¤ ì ì¸ì Tomcat ë©ì¸ êµ¬ì± íì¼ì¸
+ <literal>TOMCAT/conf/server.xml</literal>ì ì¶ê°íë¤:
+ </para>
+
+ <programlisting><![CDATA[<Context path="/quickstart" docBase="quickstart">
+ <Resource name="jdbc/quickstart" scope="Shareable" type="javax.sql.DataSource"/>
+ <ResourceParams name="jdbc/quickstart">
+ <parameter>
+ <name>factory</name>
+ <value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
+ </parameter>
+
+ <!-- DBCP database connection settings -->
+ <parameter>
+ <name>url</name>
+ <value>jdbc:postgresql://localhost/quickstart</value>
+ </parameter>
+ <parameter>
+ <name>driverClassName</name><value>org.postgresql.Driver</value>
+ </parameter>
+ <parameter>
+ <name>username</name>
+ <value>quickstart</value>
+ </parameter>
+ <parameter>
+ <name>password</name>
+ <value>secret</value>
+ </parameter>
+
+ <!-- DBCP connection pooling options -->
+ <parameter>
+ <name>maxWait</name>
+ <value>3000</value>
+ </parameter>
+ <parameter>
+ <name>maxIdle</name>
+ <value>100</value>
+ </parameter>
+ <parameter>
+ <name>maxActive</name>
+ <value>10</value>
+ </parameter>
+ </ResourceParams>
+</Context>]]></programlisting>
+
+ <para>
+ ì°ë¦¬ê° ì´ ìì ìì 구ì±íë 컨í
ì¤í¸ë <literal>quickstart</literal>ë¡ ëª
ëª
ëê³ , ê·¸ê²ì ë² ì´ì¤ë
+ <literal>TOMCAT/webapp/quickstart</literal> ëë í 리ì´ë¤. ììì ìë¸ë¦¿ë¤ì ì ê·¼í기 ìí´,
+ (ë¬¼ë¡ ë¹ì ì <literal>web.xml</literal> ìì 매íë ìë¸ë¦¿ì ì´ë¦ì ì¶ê°íì¬) ë¹ì ì ë¸ë¼ì°ì ìì
+ <literal>http://localhost:8080/quickstart</literal> ê²½ë¡ë¥¼ í¸ì¶íë¼. ë¹ì ì ëí ê³ì ì§ííê³
+ ì´ì 공백ì <literal>process()</literal> ë©ìë를 ê°ì§ ê°ë¨í ìë¸ë¦¿ì ìì±ìí¬ ì ìë¤.
+ </para>
+
+ <para>
+ Tomcatì ì´ì <literal>java:comp/env/jdbc/quickstart</literal>ë¡ JNDIì íµí´ 커ë¥ì
ë¤ì ì ê³µíë¤.
+ ë§ì¼ ë¹ì ì´ ì¤í ì¤ì¸ 커ë¥ì
íì ì»ë ê²ì 문ì ê° ìë¤ë©´ Tomcat 문ì를 참조íë¼. ë¹ì ì´ JDBC ëë¼ì´ë² ìì¸ìí©
+ ë©ìì§ë¥¼ ì»ì ê²½ì°, 먼ì Hibernate ìì´ JDBC 커ë¥ì
íì ì
ì
íë¼. Tomcat & JDBC íí 리ì¼ë¤ì
+ ê·¸ ì¹ ìì´í¸ìì ì´ì© ê°ë¥íë¤.
+ </para>
+
+ <para>
+ ë¹ì ì ë¤ì ë¨ê³ë Hibernate를 구ì±íë ê²ì´ë¤. Hibernateë ê·¸ê²ì´ JDBC 커ë¥ì
ë¤ì ì»ë ë°©ë²ì ìê³ ìì´ì¼ íë¤.
+ ì°ë¦¬ë Hibernateì XML ê¸°ë° êµ¬ì±ì ì¬ì©íë¤. properties íì¼ì ì¬ì©íë ë¤ë¥¸ ì ê·¼ë²ì ê±°ì ëì¼íì§ë§ XML
+ êµ¬ë¬¸ì´ íì©íë ëªëª í¹ì§ë¤ì ëë½íê³ ìë¤. XML êµ¬ì± íì¼ì <literal>hibernate.cfg.xml</literal>ë¡ì
+ 컨í
ì¤í¸ classpath (<literal>WEB-INF/classes</literal>) ë´ì ìì¹í´ ìë¤:
+ </para>
+
+ <programlisting><![CDATA[<?xml version='1.0' encoding='utf-8'?>
+<!DOCTYPE hibernate-configuration PUBLIC
+ "-//Hibernate/Hibernate Configuration DTD//EN"
+ "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
+
+<hibernate-configuration>
+
+ <session-factory>
+
+ <property name="connection.datasource">java:comp/env/jdbc/quickstart</property>
+ <property name="show_sql">false</property>
+ <property name="dialect">org.hibernate.dialect.PostgreSQLDialect</property>
+
+ <!-- Mapping files -->
+ <mapping resource="Cat.hbm.xml"/>
+
+ </session-factory>
+
+</hibernate-configuration>]]></programlisting>
+
+ <para>
+ ì°ë¦¬ë SQL ëª
ë ¹ë¤ì ëí ë¡ê¹
ì ì¬ì©íì§ ìê³ Hibernateìê² ì¬ì©ëë ë°ì´í°ë² ì´ì¤ SQL directê° ë¬´ìì¸ì§
+ ê·¸ë¦¬ê³ (Tomcat ë°ì¸ëë íì JNDI 주ì를 ì ì¸íì¬) JDBC 커ë¥ì
ë¤ì ì»ë ê³³ì ìë ¤ì¤ë¤. dialectë
+ íìì ì¸ ì¤ì ì´ê³ , ë°ì´í°ë² ì´ì¤ë¤ì SQL "íì¤"ì ëí ê·¸ê²ë¤ì í´ìì ë¬ë¦¬íë¤. Hibernateë ì°¨ì´ì ë¤ì
+ ì²ë¦¬íê³ ëª¨ë 주ì ìì© ë°ì´í°ë² ì´ì¤ë¤ ë° ì¤í ìì¤ ë°ì´í°ë² ì´ì¤ë¤ ì©ëì directë¤ì ë²ë¤ë¡ í¬í¨íê³ ìë¤.
+ </para>
+
+ <para>
+ <literal>SessionFactory</literal>ë ë¨ì¼ ë°ì´í°ì ì¥ìì ê´í ê°ë
ì´ê³ , ì¬ë¬ ë°ì´í°ë² ì´ì¤ë¤ì ì¬ë¬
+ ê°ì XML êµ¬ì± íì¼ë¤ì ìì±ìí¤ê³ ë¹ì ì ì´í리ì¼ì´ì
ììì ì¬ë¬ ê°ì <literal>Configuration</literal>
+ ë° <literal>SessionFactory</literal> ê°ì²´ë¤ì ìì±ìì¼ì ì¬ì©ë ì ìë¤.
+ </para>
+
+ <para>
+ <literal>hibernate.cfg.xml</literal>ì ë§ì§ë§ ììë ìì í´ëì¤ <literal>Cat</literal>ì ëí
+ Hibernate XML 매í íì¼ì ì´ë¦ì¼ë¡ì¨ <literal>Cat.hbm.xml</literal>ì ì ì¸íë¤. ì´ íì¼ì ë°ì´í°ë² ì´ì¤
+ í
ì´ë¸(ëë í
ì´ë¸ë¤)ë¡ POJO í´ëì¤ <literal>Cat</literal> ì 매íìí¤ë ë©íë°ì´í°ë¥¼ í¬í¨íë¤. ì°ë¦¬ë
+ 곧 ê·¸ íì¼ë¡ ëëì ê° ê²ì´ë¤. 먼ì POJO í´ëì¤ë¥¼ ìì±íê³ ê·¸ë° ë¤ì ê·¸ê²ì ìí 매í ë©íë°ì´í°ë¥¼ ì ì¸íì.
+ </para>
+
+ </sect1>
+
+ <sect1 id="quickstart-persistentclass" revision="1">
+ <title>첫 ë²ì§¸ ìì í´ëì¤</title>
+
+ <para>
+ Hibernateë ìì í´ëì¤ë¤ì ëí Plain Old Java Objects (POJOs, ì¢
ì¢
Plain Ordinary Java Objectsë¡
+ ëª
ëª
ëë¤) íë¡ê·¸ëë° ëª¨íì¼ë¡ ê°ì¥ ì ëìíë¤. POJOë ê³µì©ì¼ë¡ ê°ìì ì¸ ì¸í°íì´ì¤ë¡ë¶í° ë´ë¶ì ì¸ íìì ìíìì¼,
+ getterì setter ë©ìëë¤ì íµí´ ì ê·¼ê°ë¥í í´ëì¤ë¤ì íë¡í¼í°ë¤ì ê°ì§ ìë°ë¹ê³¼ 꽤 ì ì¬íë¤(íìíë¤ë©´ Hibernateë
+ ëí íëë¤ì ì§ì ì ê·¼í ì ìë¤):
+ </para>
+
+ <programlisting><![CDATA[package org.hibernate.examples.quickstart;
+
+public class Cat {
+
+ private String id;
+ private String name;
+ private char sex;
+ private float weight;
+
+ public Cat() {
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ private void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public char getSex() {
+ return sex;
+ }
+
+ public void setSex(char sex) {
+ this.sex = sex;
+ }
+
+ public float getWeight() {
+ return weight;
+ }
+
+ public void setWeight(float weight) {
+ this.weight = weight;
+ }
+
+}]]></programlisting>
+
+ <para>
+ Hibernateë ê·¸ê²ì íë¡í¼í° íì
ë¤ì ì¬ì©ì ì ì½ëì§ ìê³ , ìë° ì½ë ì
íë ììììì í´ëì¤ë¤ì í¬í¨íì¬, 모ë
+ ìë° JDK íì
ë¤ê³¼ (<literal>String</literal>, <literal>char</literal>ì <literal>Date</literal>
+ ê°ì) ììíì
ë¤ì´ 매íë ì ìë¤. ë¹ì ì ê·¸ê²ì ê°ë¤ë¡ì, ê°ë¤ì ê°ì§ ì½ë ì
ë¤ë¡ì, ëë ë¤ë¥¸ ìí°í°ë¤ì ëí
+ ì°ê´ë¤ë¡ì 매íìí¬ ì ìë¤. <literal>id</literal>ë ê·¸ í´ëì¤ì ë°ì´í°ë² ì´ì¤ ìë³ì(íë¼ì´ë¨¸ë¦¬ í¤)를 íííë
+ í¹ë³í íë¡í¼í°ì´ê³ , ê·¸ê²ì <literal>Cat</literal>ê³¼ ê°ì ìí°í°ë¤ì ëí´ ë§¤ì° ê¶ì¥ëë¤. Hibernateë ë´ë¶ì ì¼ë¡ë§
+ ìë³ìë¤ì ì¬ì©í ì ìì§ë§, ì°ë¦¬ë ì°ë¦¬ì ì´í리ì¼ì´ì
ìí¤í
ì²ìì ì´ë¤ ì ì°ì±ì ìì¤íê² ë ê²ì´ë¤.
+ </para>
+
+ <para>
+ í¹ì ì¸í°íì´ì¤ë ìì í´ëì¤ë¤ì ëí´ êµ¬íëì§ ë§ìì¼ íê±°ë í¹ì ë£¨í¸ ìì í´ëì¤ë¡ë¶í° ìë¸ í´ëì¤ë¡ ë§ë¤ì§ ë§ìì¼ íë¤.
+ Hibernateë ëí ë°ì´í¸ ì½ë ì²ë¦¬ì ê°ì, ì´ë¤ ë¹ë ì ì²ë¦¬ë¥¼ íìë¡ íì§ ìê³ , ê·¸ê²ì ì¤ì§ ìë° reflectionê³¼
+ (CGLIB를 íµí) ë°íì í´ëì¤ ê³ ììë§ ìì¡´íë¤. ë°ë¼ì Hibernateì ëí POJO í´ëì¤ì ì´ë¤ ìì¡´ì± ìì´ë, ì°ë¦¬ë
+ ê·¸ê²ì ë°ì´í°ë² ì´ì¤ í
ì´ë¸ë¡ 매íí ì ìë¤.
+ </para>
+
+ </sect1>
+
+ <sect1 id="quickstart-mapping" revision="2">
+ <title>cat 매íí기</title>
+
+ <para>
+ <literal>Cat.hbm.xml</literal> 매ííì¼ì ê°ì²´/ê´ê³í 매íì íìí ë©íë°ì´í°ë¥¼ í¬í¨íë¤. ë©íë°ì´í°ë ìì
+ í´ëì¤ë¤ì ì ì¸ê³¼ ë°ì´í°ë² ì´ì¤ í
ì´ë¸ë¤ì ëí (컬ë¼ë¤ê³¼ ë¤ë¥¸ ìí°í°ë¤ì ëí foreign í¤ ê´ê³ë¤ì ëí) íë¡í¼í°ë¤ì
+ 매íì í¬í¨íë¤.
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping>
+
+ <class name="org.hibernate.examples.quickstart.Cat" table="CAT">
+
+ <!-- A 32 hex character is our surrogate key. It's automatically
+ generated by Hibernate with the UUID pattern. -->
+ <id name="id" type="string" unsaved-value="null" >
+ <column name="CAT_ID" sql-type="char(32)" not-null="true"/>
+ <generator class="uuid"/>
+ </id>
+
+ <!-- A cat has to have a name, but it shouldn' be too long. -->
+ <property name="name">
+ <column name="NAME" length="16" not-null="true"/>
+ </property>
+
+ <property name="sex"/>
+
+ <property name="weight"/>
+
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ 모ë ìì í´ëì¤ë ìë³ì ìì±ì ê°ì ¸ì¼ íë¤(ì¤ì ë¡ value-íì
ì í´ëì¤ë¤ì ìì¡´íì§ ìë, ìí°í°ë¤ì íííë ì ì¼í
+ í´ëì¤ë¤ì ìí°í°ì ê°ì§ ì»´í¬ëí¸ë¤ë¡ 매íëë¤). ì´ íë¡í¼í°ë ìì ê°ì²´ë¤ì 구ë³ì§ëë° ì¬ì©ëë¤: ë§ì¼
+ <literal>catA.getId().equals(catB.getId())</literal>ê° trueì¼ ê²½ì°, ë ê°ì catë¤ì ê°ê³ , ì´ ê°ë
ì
+ <emphasis>database identity</emphasis>ë¡ ëª
ëª
ëë¤. Hibernateë (ë°ì´í°ë² ì´ì¤ ìíì¤, hi/lo ìë³ì í
ì´ë¸ë¤,
+ ê·¸ë¦¬ê³ ì´í리ì¼ì´ì
í ë¹ ìë³ìë¤ì ëí native ìì±ê¸°ë¤ì í¬í¨íë) ë¤ë¥¸ ìë리ì¤ë¤ì ëí´ ì¬ë¬ ê°ì§ ìë³ì ìì±ê¸°ë¤ì
+ ë²ë¤ë¡ ê°ê³ ìë¤. ì°ë¦¬ë UUID ìì±ê¸°(ë°ì´í°ì´ì¤ì ìí´ ìì±ë ì ì ëì© í¤ë¤ì´ ì í¸ë ê²ì´ë¯ë¡, í
ì¤í¸ì©ì¼ë¡ë§ ê¶ì¥ë¨)를
+ ì¬ì©íê³ ëí Hibernate ìì±ë ìë³ì ê°ì ìí <literal>CAT</literal> í
ì´ë¸ì <literal>CAT_ID</literal>
+ 컬ë¼ì (í
ì´ë¸ì íë¼ì´ë¨¸ë¦¬ í¤ë¡ì) ì§ì íë¤.
+ </para>
+
+ <para>
+ <literal>Cat</literal>ì 모ë ë¤ë¥¸ íë¡í¼í°ë¤ì ëì¼í í
ì´ë¸ë¡ 매íëë¤. <literal>name</literal> íë¡í¼í°ì ê²½ì°ì,
+ ì°ë¦¬ë ê·¸ê²ì ëª
ìì ì¸ ë°ì´í°ë² ì´ì¤ ì»¬ë¼ ì ì¸ì¼ë¡ 매íìì¼°ë¤. ë°ì´í°ë² ì´ì¤ ì¤í¤ë§ê° Hibernateì <emphasis>SchemaExport</emphasis>
+ ë구ì ìí´ ë§¤í ì ì¸ì¼ë¡ë¶í° (SQL DDL 문ì¥ë¤ë¡) ìëì ì¼ë¡ ìì±ë ë ì´ê²ì´ í¹ë³í ì ì©íë¤. 모ë ë¤ë¥¸ íë¡í¼í°ë¤ì
+ Hibernateì ëí´í¸ ì¤ì ë¤ì ì¬ì©íì¬ ë§¤íëê³ , ëí´í¸ ì¤ì ë¤ì ë¹ì ì´ ê°ì¥ ë§ì ìê°ì íìë¡ íë ê²ì´ë¤. ë°ì´í°ë² ì´ì¤
+ ë´ì í
ì´ë¸ <literal>CAT</literal>ì ë¤ìê³¼ ê°ë¤:
+ </para>
+
+ <programlisting><![CDATA[ Column | Type | Modifiers
+--------+-----------------------+-----------
+ cat_id | character(32) | not null
+ name | character varying(16) | not null
+ sex | character(1) |
+ weight | real |
+Indexes: cat_pkey primary key btree (cat_id)]]></programlisting>
+
+ <para>
+ ë¹ì ì ì´ì ììì
ì¼ë¡ ë¹ì ì ë°ì´í°ë² ì´ì¤ ë´ì ì´ í
ì´ë¸ì ìì±ìí¬ ê²ì´ê³ , ë§ì¼ ë¹ì ì´ <literal>hbm2ddl</literal>
+ ëêµ¬ë¡ ì´ ë¨ê³ë¥¼ ìëí ìí¤ê³ ì ìí ê²½ì° <xref linkend="toolsetguide"/>를 ì½ì´ë¼. ì´ ë구ë í
ì´ë¸ ì ì, ë§ì¶¤í
+ ì»¬ë¼ íì
컨ì¤í¸ë ì¸í¸ë¤, ì ì¼ ì»¨ì¤í¸ë ì¸í¸ë¤ê³¼ ì¸ë±ì¤ë¤ì í¬í¨íë, ì ì²´ SQL DDLì ìì±ìí¬ ì ìë¤.
+ </para>
+
+ </sect1>
+
+ <sect1 id="quickstart-playingwithcats" revision="3">
+ <title>catë¤ì ìì
í기</title>
+
+ <para>
+ ì°ë¦¬ë ì´ì Hibernateì <literal>Session</literal>ì ììí ì¤ë¹ê° ëì´ ìë¤. ê·¸ê²ì <emphasis>persistence manager</emphasis>
+ (ìì ê´ë¦¬ì)ì´ê³ , ì°ë¦¬ë ë°ì´í°ë² ì´ì¤ë¡ <literal>Cat</literal>ë¤ì ì ì¥íê³ ë°ì´í°ë² ì´ì¤ë¡ë¶í°
+ <literal>Cat</literal>ë¤ì ê²ìíëë° ê·¸ê²ì ì¬ì©íë¤. ê·¸ë¬ë 먼ì ì°ë¦¬ë <literal>SessionFactory</literal>ë¡ë¶í°
+ <literal>Session</literal>(Hibernateì ìì
ë¨ì)를 ì»ì´ì¼ íë¤:
+ </para>
+
+ <programlisting><![CDATA[SessionFactory sessionFactory =
+ new Configuration().configure().buildSessionFactory();]]></programlisting>
+
+ <para>
+ <literal>configure()</literal> í¸ì¶ì <literal>hibernate.cfg.xml</literal> êµ¬ì± íì¼ì ë¡ëìí¤ê³
+ <literal>Configuration</literal> ì¸ì¤í´ì¤ë¥¼ ì´ê¸°í ìí¨ë¤. ë¹ì ì´ SessionFactory(ë¶ë³ì ì)를 ë¹ëí기
+ <emphasis>ì´ì ì</emphasis> ë¹ì ì <literal>Configuration</literal>ì ì ê·¼í¨ì¼ë¡ì¨ ë¤ë¥¸ íë¡í¼í°ë¤ì
+ ì¤ì í ì ìë¤(ê·¸ë¦¬ê³ ì¬ì§ì´ 매í ë©íë°ì´í°ë¥¼ ë³ê²½ìí¬ ì ìë¤). ì°ë¦¬ë ì´ëì <literal>SessionFactory</literal>를
+ ìì±ìí¤ê³ ì°ë¦¬ì ì´í리ì¼ì´ì
ììì ì´ë»ê² ê·¸ê²ì ì ê·¼í ì ìë?
+ </para>
+
+ <para>
+ <literal>SessionFactory</literal>ë ëê° ì¤ì§ íë²ë§, ì를 ë¤ì´ ëê° <emphasis>load-on-startup</emphasis>
+ ìë¸ë¦¿ì¼ë¡ ìì ìì ë¹ëëë¤. ì´ê²ì ëí ë¹ì ì´ ë¹ì ì ìë¸ë¦¿ë¤ ë´ì ìë ì¸ì¤í´ì¤ ë³ì ìì ê·¸ê²ì ì ì§íì§ ìì ê²ì´ì§ë§
+ ì´ë¤ ë¤ë¥¸ ìì¹ì ì ì§ìí¬ ê²ìì ì미íë¤. ë구ë ì°ë¦¬ë ì´ë¤ ì¢
ë¥ì <emphasis>Singleton</emphasis>ì íìë¡ íë©°,
+ ë°ë¼ì ì°ë¦¬ë ì´í리ì¼ì´ì
ì½ëë¡ ì½ê² <literal>SessionFactory</literal>ì ì¡ì¸ì¤ í ì ìë¤. ë¤ìì ë³´ì¬ì§ ì ê·¼ë²ì
+ ë 문ì 모ë를 í´ê²°íë¤: ìì 구ì±ê³¼ <literal>SessionFactory</literal>ì ëí ì¬ì´ ì ê·¼.
+ </para>
+
+ <para>
+ ì°ë¦¬ë <literal>HibernateUtil</literal> helper í´ëì¤ë¥¼ 구ííë¤:
+ </para>
+
+ <programlisting><![CDATA[import org.hibernate.*;
+import org.hibernate.cfg.*;
+
+public class HibernateUtil {
+
+ private static Log log = LogFactory.getLog(HibernateUtil.class);
+
+ private static final SessionFactory sessionFactory;
+
+ static {
+ try {
+ // Create the SessionFactory
+ sessionFactory = new Configuration().configure().buildSessionFactory();
+ } catch (Throwable ex) {
+ // Make sure you log the exception, as it might be swallowed
+ log.error("Initial SessionFactory creation failed.", ex);
+ throw new ExceptionInInitializerError(ex);
+ }
+ }
+
+ public static final ThreadLocal session = new ThreadLocal();
+
+ public static Session getCurrentSession() {
+ Session s = (Session) session.get();
+ // Open a new Session, if this Thread has none yet
+ if (s == null) {
+ s = sessionFactory.openSession();
+ session.set(s);
+ }
+ return s;
+ }
+
+ public static void closeSession() {
+ Session s = (Session) session.get();
+ if (s != null)
+ s.close();
+ session.set(null);
+ }
+}]]></programlisting>
+
+ <para>
+ ì´ í´ëì¤ë static ì´ê¸°ì를 ê°ì§ <literal>SessionFactory</literal>를 ì²ë¦¬í ë¿ë§ ìëë¼ ëí íì¬ì ì°ë ë를
+ ìí <literal>Session</literal>ì ìì íë <literal>ThreadLocal</literal> ë³ì를 ê°ëë¤. ì´ helper를
+ ì¬ì©íë ¤ê³ ìëí기 ì ì thread-local ë³ìì ëí ìë° ê°ë
ì ì´í´í´ì¼ íë¤. ë³´ë¤ ë³µì¡íê³ ê°ë ¥í <literal>HibernateUtil</literal>
+ í´ëì¤ë http://caveatemptor.hibernate.org/ì <literal>CaveatEmptor</literal>ìì ì°¾ì ì ìë¤.
+ </para>
+
+ <para>
+ <literal>SessionFactory</literal>ë threadsafeì´ê³ , ë§ì ì°ë ëë¤ì´ ëìì ê·¸ê²ì ì ê·¼í ì ìê³
+ <literal>Session</literal>ë¤ì ìì²í ì ìë¤. íëì <literal>Session</literal>ì ë°ì´í°ë² ì´ì¤ì ëí´
+ í ê°ì ë¨ì ìì
ì ëíë´ë non-threadsafe ê°ì²´ì´ë¤. <literal>Session</literal>ë¤ì <literal>SessionFactory</literal>
+ ë¡ë¶í° ì´ë ¤ì§ê³ 모ë ìì
ì´ ìë£ë ë ë«íì§ë¤. ë¹ì ì ìë¸ë¦¿ì <literal>process()</literal> ë©ìë ë´ì ìë
+ ìì ë ë¤ìê³¼ ê°ì ì ìë¤(ìì¸ìí© ì²ë¦¬ ìì´):
+ </para>
+
+ <programlisting><![CDATA[Session session = HibernateUtil.getCurrentSession();
+Transaction tx = session.beginTransaction();
+
+Cat princess = new Cat();
+princess.setName("Princess");
+princess.setSex('F');
+princess.setWeight(7.4f);
+
+session.save(princess);
+
+tx.commit();
+HibernateUtil.closeSession();]]></programlisting>
+
+ <para>
+ íëì <literal>Session</literal> ë´ìì 모ë ë°ì´í°ë² ì´ì¤ ì¤í¼ë ì´ì
ì ë°ì´í°ë² ì´ì¤ ì¤í¼ë ì´ì
ë¤(ì¬ì§ì´ ì½ê¸° ì ì©
+ ì¤í¼ë ì´ì
ë¤ ì¡°ì°¨ë)ì 격리ìí¤ë íëì í¸ëìì
ë´ë¶ìì ë°ìíë¤. ì°ë¦¬ë 기본 í¸ëìì
ë°©ë(ì°ë¦¬ì ê²½ì°, JDBC
+ í¸ëìì
ë¤)ë¡ë¶í° ì¶ìíìí¤ëë° Hibernates <literal>Transaction</literal> API 를 ì¬ì©íë¤. ì´ê²ì ì°ë¦¬ì
+ ì½ëê° ììì ë³ê²½ë¤ ìì´ë (JTA를 ì¬ì©íë) 컨í
ì´ë-ê´ë¦¬ëë í¸ëìì
ë¤ì ë°°ì¹ëë ê²ì íì©í´ì¤ë¤.
+ </para>
+
+ <para>
+ ë¹ì ì´ ìíë ë§í¼ ë¹ì ì´ <literal>HibernateUtil.getCurrentSession();</literal>ì í¸ì¶í ì ìê³ , ë¹ì ì
+ ì´ ì°ë ëì íì¬ <literal>Session</literal>ì íì ì»ì ê²ìì ë
¸í¸íë¼. ë¹ì ì ìë¸ë¦¿ ì½ë ë´ììë ëë ìë¸ë¦¿ íí°
+ ë´ììë HTTP responseê° ì ì¡ë기 ì ì, ë¹ì ì ë¨ì ìì
ì´ ìë£ë íì <literal>Session</literal>ì´ íì¤í
+ ë«íì§ëë¡ í´ì¼ íë¤. ë ë²ì§¸ ìµì
ì ì¢ì 측면ì ì¬ì´ lazy ì´ê¸°íì´ë¤: ë·°ê° ë ëë§ ë ë <literal>Session</literal>ì´
+ ì¬ì í ì´ë ¤ì ¸ ìì´ì, Hibernateë ë¹ì ì´ íì¬ ê°ì²´ ê·¸ëí를 ë¤ë¹ê²ì´í¸ íë ëì ì´ê¸°í ëì§ ìì ê°ì²´ë¤ì ë¡ëìí¬ ì ìë¤.
+ </para>
+
+ <para>
+ Hibernateë ë°ì´í°ë² ì´ì¤ë¡ë¶í° ê°ì²´ë¤ì ê²ìíëë° ì¬ì©ë ì ìë ë¤ìí ë©ìëë¤ì ê°ê³ ìë¤. ê°ì¥ ì ì°í ë°©ë²ì Hibernate
+ Query Language (HQL)ì ì¬ì©íë ê²ì´ë¤. Hibernate Query Language (HQL)ì ë°°ì°ê¸°ê° ì½ê³ SQLì ëí ê°ë ¥í ê°ì²´ ì§í¥
+ íì¥ì´ë¤:
+ </para>
+
+ <programlisting><![CDATA[Transaction tx = session.beginTransaction();
+
+Query query = session.createQuery("select c from Cat as c where c.sex = :sex");
+query.setCharacter("sex", 'F');
+for (Iterator it = query.iterate(); it.hasNext();) {
+ Cat cat = (Cat) it.next();
+ out.println("Female Cat: " + cat.getName() );
+}
+
+tx.commit();]]></programlisting>
+
+ <para>
+ Hibernateë ëí type-safe ì§ìë¤ì ê³µìí ìí¤ëë° ì¬ì©ë ì ìë ê°ì²´-ì§í¥ <emphasis>query by criteria</emphasis>
+ APIì ì ê³µíë¤. ë¬¼ë¡ Hibernateë ë°ì´í°ë² ì´ì¤ìì 모ë SQL íµì ì ìí´ <literal>PreparedStatement</literal>ë¤ê³¼
+ íë¼ë¯¸í° ë°ì¸ë©ì ì¬ì©íë¤. ë¹ì ì ëí Hibernate ì§ì ì ì¸ SQL ì§ì í¹ì§ì ì¬ì©í ìë ìê±°ë ë문 ê²½ì°ì
+ <literal>Session</literal>ì¼ë¡ë¶í° plain JDBC 커ë¥ì
ì ì»ì ìë ìë¤.
+ </para>
+
+ </sect1>
+
+ <sect1 id="quickstart-summary" revision="1">
+ <title>ë§ì§ë§ì¼ë¡</title>
+
+ <para>
+ ì°ë¦¬ë ì´ ìì íí ë¦¬ì¼ ë´ìì ë¨ì§ Hibernateì íë©´ì íê¸°ë§ íë¤. ì°ë¦¬ë ì°ë¦¬ì ìì ë¤ ìì ì´ë¤ ìë¸ë¦¿ ì§ì ì ì½ë를
+ í¬í¨íì§ ììì ë
¸í¸íë¼. ë¹ì ì´ ì í©íì§ë¥¼ ìë ¤ê³ í ë ë¹ì ì ë¹ì ìì ì ìë¸ë¦¿ì ìì±ìì¼ì¼ íê³ Hibernate ì½ë를
+ ì½ì
í´ì¼ íë¤.
+ </para>
+
+ <para>
+ ë°ì´í° ì ê·¼ ê³ì¸µì¼ë¡ì Hibernateë ë¹ì ì ì´í리ì¼ì´ì
ì ê°íê² íµí©ë¨ì ì¼ëì ëë¼. ëê° ëª¨ë ë¤ë¥¸ ë ì´ì´ë¤ì ìì
+ ë©ì»¤ëì¦ì ìì¡´íë¤. ë¹ì ì ì´ ì¤ê³ì í¨ì¶ì íì¤í ì´í´íëë¡ íë¼.
+ </para>
+
+ <para>
+ ë³´ë¤ ë³µì¡í ì´í리ì¼ì´ì
ìì ë http://caveatemptor.hibernate.org/ 를 ë³´ê³ http://caveatemptor.hibernate.org/ì
+ ìë ë¤ë¥¸ íí 리ì¼ë¤ì ì´í´ë³´ë¼.
+ </para>
+
+ </sect1>
+
+</chapter>
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/session_api.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/session_api.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/session_api.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1136 @@
+<chapter id="objectstate">
+ <title>ê°ì²´ë¤ë¡ ìì
í기</title>
+
+ <para>
+ Hibernateë 기본 ë°ì´í°ë² ì´ì¤ ê´ë¦¬ ìì¤í
ì ìì¸ë¡ë¶í° ê°ë°ìë¤ì ìíìì¼ì¤ ë¿ë§ ìëë¼, ëí ê°ì²´ë¤ì ëí
+ <emphasis>ìí ê´ë¦¬</emphasis>를 ì ê³µíë íëì ìì í ê°ì²´/ê´ê³í 매í ì루ì
ì´ë¤. ì´ê²ì ê³µíµì ì¸
+ JDBC/SQL ìì ê³ì¸µë¤ ë´ììì SQL <literal>문ì¥ë¤</literal>ì ëí ê´ë¦¬ìë ë°ëë¡, ìë° ì´í리ì¼ì´ì
ë¤ìì
+ ììì ê´í ë§¤ì° ê³ ì í ê°ì²´-ì§í¥ì ê´ì ì´ë¤.
+ </para>
+
+ <para>
+ ë¬ë¦¬ ë§í´, Hibernate ì´í리ì¼ì´ì
ê°ë°ìë¤ì ê·¸ë¤ì ê°ì²´ë¤ì <emphasis>ìí</emphasis>ì ëí´ íì ìê°í´ì¼ íê³ ,
+ SQL 문ì¥ë¤ì ì¤íì ëí´ìë íìì ì´ì§ ìë¤. ì´ ë¶ë¶ì Hibernateì ìí´ ì²ë¦¬ëê³ ìì¤í
ì í¼í¬ë¨¼ì¤ë¥¼ íëí ë
+ ì´í리ì¼ì´ì
ê°ë°ìì ì ì¼íê² ê´ë ¨ëë¤.
+ </para>
+
+ <sect1 id="objectstate-overview">
+ <title>Hibernate ê°ì²´ ìíë¤</title>
+
+ <para>
+ Hibernate ë¤ì ê°ì²´ ìíë¤ì ì ìíê³ ì§ìíë¤:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Transient</emphasis> - ë§ì¼ ê°ì²´ê° <literal>new</literal> ì°ì°ì를 ì¬ì©íì¬ ë°©ê¸ ì´ê¸°í ëìë¤ë©´
+ ê°ì²´ë transientì´ê³ , ê·¸ê²ì Hibernate <literal>Session</literal>ê³¼ ì°ê´ëì´ ìì§ ìë¤. ê·¸ê²ì ë°ì´í°ë² ì´ì¤
+ ë´ìì ìì íìì ê°ì§ ìê³ ìë³ì ê°ì´ í ë¹ëì§ ììë¤. ë§ì¼ ì´í리ì¼ì´ì
ì´ ë ì´ì 참조를 ìì íì§ ìì ê²½ì°
+ transient ì¸ì¤í´ì¤ë¤ì ì°ë 기 ìì§ê¸°ì ìí´ íê´´ë ê²ì´ë¤. ê°ì²´ë¥¼ ììí ìí¤ëë°
+ (ê·¸ë¦¬ê³ ì´ ì ì´(transition)ì ëí´ ì¤íë íìê° ìë SQL 문ì¥ë¤ì Hibernateë¡ íì¬ê¸ ì²ë¦¬íëë¡ íëë°)
+ Hibernate <literal>Session</literal>ì ì¬ì©íë¼.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Persistent</emphasis> - íëì ìì ì¸ì¤í´ì¤ë ë°ì´í°ë² ì´ì¤ ë´ìì íëì íìì ê°ê³ íëì ìë³ì ê°ì
+ ê°ëë¤. ê·¸ê²ì ë°©ê¸ ì ì¥ëìê±°ë ë¡ëëìì ì ìì§ë§, ì ìì ê·¸ê²ì <literal>Session</literal>ì ë²ì ë´ì ìë¤.
+ Hibernateë ìì ìíìì ê°ì²´ì ëí´ íí´ì§ ììì ë³ê²½ë¤ì ê²ì¶í´ë¼ ê²ì´ê³ ë¨ì ìì
ì´ ìë£ë ë ê·¸ ìí를 ë°ì´í°ë² ì´ì¤ì
+ ë기í ìí¬ ê²ì´ë¤. ê°ë°ìë¤ì íëì ê°ì²´ê° transientë¡ ëì´ì¼ í ë <literal>UPDATE</literal> 문ì¥ë¤ì´ë
+ <literal>DELETE</literal> 문ì¥ë¤ì ììì
ì¼ë¡ ì¤ííì§ ìëë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Detached</emphasis> - íëì detached ì¸ì¤í´ì¤ë ììí ëìì§ë§, ê·¸ê²ì
+ <literal>Session</literal>ì´ ë«íì§ ê°ì²´ì´ë¤. ë¬¼ë¡ ê·¸ ê°ì²´ì ëí 참조ë ì¬ì í ì í¨íê³ ,
+ ê·¸ detached ì¸ì¤í´ì¤ë ì´ ìíììë ë³ê²½ë ìë ìë¤. íëì detached ì¸ì¤í´ì¤ë ëì¤ì
+ ê·¸ê²(ê³¼ 모ë ë³ê²½ë¤)ì ë¤ì ììí ìì¼ì ìë¡ì´ <literal>Session</literal>ì ë¤ì 첨ë¶ë ì ìë¤.
+ ì´ í¹ì§ì ì¬ì©ìê° ìê°í ìê°ì íìë¡ íë ì¥ ê¸°ê° ì¤íëë ìì
ë¨ì를 ìí íë¡ê·¸ëë° ëª¨íì ê°ë¥íê²
+ í´ì¤ë¤. ì°ë¦¬ë ê·¸ê²ë¤ì <emphasis>ì´í리ì¼ì´ì
í¸ëìì
ë¤</emphasis>, ì¦ ì¬ì©ìì ê´ì ì ìì
ë¨ìë¼ê³
+ ë¶ë¥¸ë¤.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ì´ì ì°ë¦¬ë ìíë¤ê³¼ ìí ì ì´(transition)ë¤(ê·¸ë¦¬ê³ ì ì´ë¥¼ í¸ë¦¬ê±° ìí¤ë Hibernate ë©ìëë¤)ì ìì¸íê² ë
¼ìí ê²ì´ë¤.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-makingpersistent" revision="1">
+ <title>ê°ì²´ë¤ì ììí ìí¤ê¸°</title>
+
+ <para>
+ íëì ìì í´ëì¤ì ìë¡ì´ ì´ê¸°í ë ì¸ì¤í´ì¤ë¤ì Hibernateì ìí´ <emphasis>transient</emphasis>ë¡ ê°ì£¼ëë¤.
+ ì°ë¦¬ë ê·¸ê²ì ì¸ì
ê³¼ ì°ê´ì§ì´ì transient ì¸ì¤í´ì¤ë¥¼ <emphasis>ììí</emphasis> ìí¬ ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[DomesticCat fritz = new DomesticCat();
+fritz.setColor(Color.GINGER);
+fritz.setSex('M');
+fritz.setName("Fritz");
+Long generatedId = (Long) sess.save(fritz);]]></programlisting>
+
+ <para>
+ ë§ì¼ <literal>Cat</literal>ì´ ìì±ë ìë³ì를 ê°ì§ ê²½ì°, <literal>save()</literal>ê° í¸ì¶ë ë
+ ê·¸ ìë³ìê° ìì±ëê³ <literal>cat</literal>ì í ë¹ëë¤. ë§ì¼ <literal>Cat</literal>ì´ íëì
+ <literal>assigned</literal> ìë³ìë íëì composite key를 ê°ì§ ê²½ì°, <literal>save()</literal>를
+ í¸ì¶í기 ì ì ê·¸ ìë³ìê°<literal>cat</literal> ì¸ì¤í´ì¤ì í ë¹ë ê²ì´ë¤. ë¹ì ì ëí EJB3 ì´ê¸° ëëíí¸ìì
+ ì ìë ìë¯¸ë¡ <literal>save()</literal> ëì <literal>persist()</literal>를 ì¬ì©í ìë ìë¤.
+ </para>
+
+ <para>
+ ë¤ë¥¸ ë°©ë²ì¼ë¡, ë¹ì ì <literal>save()</literal>ì ì¤ë²ë¡ëë ë²ì ì ì¬ì©íì¬ ìë³ì를 í ë¹í ì ìë¤.
+ </para>
+
+<programlisting><![CDATA[DomesticCat pk = new DomesticCat();
+pk.setColor(Color.TABBY);
+pk.setSex('F');
+pk.setName("PK");
+pk.setKittens( new HashSet() );
+pk.addKitten(fritz);
+sess.save( pk, new Long(1234) );]]></programlisting>
+
+ <para>
+ ë§ì¼ ë¹ì ì´ ììí ìí¤ë ê°ì²´ê° ì°ê´ë ê°ì²´ë¤(ì를 ë¤ë©´. ìì ìì ì ìë <literal>kittens</literal> ì½ë ì
)ì ê°ê³ ìë¤ë©´,
+ ë¹ì ì´ íëì foreign í¤ ì»¬ë¼ì ëí´ íëì <literal>NOT NULL</literal> 컨ì¤í¸ë ì¸í¸ë¥¼ ê°ì§ ìë í,
+ ì´ë¤ ê°ì²´ë¤ì ë¹ì ì´ ì¢ìíë ììì ììë¡ ììí ëìì ìë ìë¤. foreign í¤ ì»¨ì¤í¸ë ì¸í¸ë¤ì ìë°°íë ìíì±ì´
+ ê²°ì½ ì¡´ì¬íì§ ìëë¤. íì§ë§ ë¹ì ì´ ì못ë ììë¡ ê·¸ ê°ì²´ë¤ì <literal>save()</literal> ìí¬ ê²½ì° ë¹ì ì
+ <literal>NOT NULL</literal> 컨ì¤í¸ë ì¸í¸ë¥¼ ìë°°í ìë ìë¤.
+ </para>
+
+ <para>
+ ë¹ì ì´ ì°ê´ë ê°ì²´ë¤ì ìëì ì¼ë¡ ì ì¥ìí¤ëë° Hibernateì <emphasis>transitive persistence(ì ì´ ìì)</emphasis>
+ í¹ì§ì ì¬ì©íë ê²ì ë§¤ì° ì¢ìí ê²ì´ë¯ë¡ ëê° ë¹ì ì ì´ ìì¸ë¥¼ ë´ë²ë ¤ëë¤. ê·¸ë <literal>NOT NULL</literal> 컨ì¤í¸ë ì¸í¸
+ ìë°°ë¤ì´ ë°ìëì§ ììì§ë¼ë - Hibernateë 모ë ê²ì ì²ë¦¬í ê²ì´ë¤. Transitive persistence(ì ì´ ìì)ì ì´ ì¥ìì
+ íë°ë¶ìì ë
¼ìëë¤.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-loading">
+ <title>ê°ì²´ë¥¼ ë¡ëìí¤ê¸°</title>
+
+ <para>
+ <literal>Session</literal>ì <literal>load()</literal> ë©ìëë¤ì ë§ì¼ ë¹ì ì´ ê·¸ê²(ìì ì¸ì¤í´ì¤)ì ìë³ìë¤ì
+ ì´ë¯¸ ìê³ ìì ê²½ì°ì ìì ì¸ì¤í´ì¤ë¥¼ ê²ìíë ë°©ë²ì ë¹ì ìê² ì ê³µíë¤. <literal>load()</literal>ë íëì í´ëì¤ ê°ì²´ë¥¼
+ ì·¨íê³ ê·¸ ìí를 ìì(persistent) ìíë¡ ê·¸ í´ëì¤ì ìë¡ì´ ì´ê¸°í ë ì¸ì¤í´ì¤ ìì¼ë¡ ë¡ëìí¬ ê²ì´ë¤.
+ </para>
+
+ <programlisting><![CDATA[Cat fritz = (Cat) sess.load(Cat.class, generatedId);]]></programlisting>
+
+<programlisting><![CDATA[// you need to wrap primitive identifiers
+long id = 1234;
+DomesticCat pk = (DomesticCat) sess.load( DomesticCat.class, new Long(id) );]]></programlisting>
+
+ <para>
+ ë¤ë¥¸ ë°©ë²ì¼ë¡ ë¹ì ì 주ì´ì§ ì¸ì¤í´ì¤ ìì¼ë¡ ìí를 ë¡ëìí¬ ì ìë¤:
+ </para>
+
+<programlisting><![CDATA[Cat cat = new DomesticCat();
+// load pk's state into cat
+sess.load( cat, new Long(pkId) );
+Set kittens = cat.getKittens();]]></programlisting>
+
+ <para>
+ ë§ì¼ ì¼ì¹íë ë°ì´í°ë² ì´ì¤ íì´ ì¡´ì¬íì§ ìì ê²½ì°ì <literal>load()</literal>ê° unrecoverable(복구 ë¶ê°ë¥í) ìì¸ìí©ì
+ ëì§ ê²ìì ë
¸í¸íë¼. ë§ì¼ í´ëì¤ê° íë½ì를 ê°ëë¡ ë§¤íë ê²½ì°, <literal>load()</literal>ë ì´ê¸°í ëì§ ìì íë½ì를 ë¨ì§
+ ë°ííê³ ë¹ì ì´ ê·¸ íë½ìì ë©ìë를 í¸ì¶í기 ì ê¹ì§ë ì¤ì ë¡ ë°ì´í°ë² ì´ì¤ì ì ìíì§ ìëë¤. ë¹ì ì´ ë°ì´í°ë² ì´ì¤ë¡ë¶í°
+ ê°ì²´ì ëí ì°ê´ì ì¤ì ë¡ ë¡ëìí¤ì§ ìê³ ì ê°ì²´ì ëí ì°ê´ì ìì±ìí¤ê³ ì ìí ê²½ì°ì ì´ í¹ì§ì´ ë§¤ì° ì ì©íë¤. ë§ì¼
+ <literal>batch-size</literal>ê° class 매íì ì ìëë ê²½ì° ê·¸ê²ì ëí ë¤ì¤ ì¸ì¤í´ì¤ë¤ì´ íëì ë°°ì¹ë¡ì ë¡ëëë ê²ì
+ íì©í´ì¤ë¤.
+ </para>
+
+ <para>
+ ë§ì¼ ë¹ì ì´ íëì ì¼ì¹íë íì´ ì¡´ì¬íëì§ë¥¼ íì í ì ìì ê²½ì°, ë¹ì ì <literal>get()</literal> ë©ìë를 ì¬ì©í´ì¼ íë¤.
+ ê·¸ê²(<literal>get()</literal> ë©ìë)ë ë°ì´í°ë² ì´ì¤ì ì¦ì ì ìíê³ ë§ì¼ ì¼ì¹íë íì´ ìì ê²½ì° nullì ë°ííë¤.
+ </para>
+
+ <programlisting><![CDATA[Cat cat = (Cat) sess.get(Cat.class, id);
+if (cat==null) {
+ cat = new Cat();
+ sess.save(cat, id);
+}
+return cat;]]></programlisting>
+
+ <para>
+ ë¹ì ì íëì <literal>LockMode</literal>를 ì¬ì©íë, <literal>SELECT ... FOR UPDATE</literal>를 ì¬ì©íì¬
+ íëì ê°ì²´ë¥¼ ë¡ëìí¬ ìë ìë¤. ì¶ê° ì ë³´ë API 문ì를 ë³´ë¼.
+ </para>
+
+ <programlisting><![CDATA[Cat cat = (Cat) sess.get(Cat.class, id, LockMode.UPGRADE);]]></programlisting>
+
+ <para>
+ ì´ë¤ ì°ê´ë ì¸ì¤í´ì¤ë¤ì´ë í¬í¨ë ì½ë ì
ë¤ì ë¹ì ì´ ê·¸ ì°ê´ì ëí ì¼ì¤ì¼ì´ë ì¤íì¼ë¡ì <literal>lock</literal> ëë
+ <literal>all</literal>ì ì§ì íëë¡ ê²°ì íì§ ìë í, <literal>FOR UPDATE</literal>ë¡ ì íëì§ ììì ë
¸í¸íë¼.
+ </para>
+
+ <para>
+ <literal>refresh()</literal> ë©ìë를 ì¬ì©íì¬, ì무ëë íëì ê°ì²´ì 모ë ê·¸ê²ì ì½ë ì
ë¤ì ë¤ì ë¡ëìí¤ë ê²ì´
+ ê°ë¥íë¤. ë°ì´í°ë² ì´ì¤ í¸ë¦¬ê±°ë¤ì´ ê·¸ ê°ì²´ì íë¡í¼í°ë¤ ì¤ ì´ë¤ ê²ì ì´ê¸°í ìí¤ëë° ì¬ì©ë ë ì´ê²ì´ ì ì©íë¤.
+ </para>
+
+ <programlisting><![CDATA[sess.save(cat);
+sess.flush(); //force the SQL INSERT
+sess.refresh(cat); //re-read the state (after the trigger executes)]]></programlisting>
+
+ <para>
+ ì¤ìí ì§ë¬¸ì´ ëê° ì´ ì§ì ìì ëíëë¤: Hibernateë ë°ì´í°ë² ì´ë¡ë¶í° ê·¸ê²ì ì¼ë§ë ë§ì´ ë¡ëìí¤ê³ ê·¸ë¦¬ê³ ì¼ë§ë ë§ì
+ SQL <literal>SELECT</literal>ë¤ì´ ê·¸ê²ì ì¬ì©í ê²ì¸ê°? ì´ê²ì <emphasis>íì¹ ë°©ë</emphasis>ì ìì¡´íê³
+ <xref linkend="performance-fetching"/>ì ì¤ëª
ëì´ ìë¤.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-querying" revision="1">
+ <title>ì§ìí기</title>
+
+ <para>
+ ë§ì¼ ë¹ì ì´ ë¹ì ì´ ì°¾ê³ ìë ê°ì²´ë¤ì ëí ìë³ìë¤ì 모를 ê²½ì°, ë¹ì ì íëì ì§ì를 íìë¡ íë¤. Hibernateë ì¬ì©ì´ ì½ì§ë§
+ ê°ë ¥í ê°ì²´ ì§í¥ ì§ì ì¸ì´(HQL)를 ì§ìíë¤. íë¡ê·¸ë¨ ìì ì§ì ìì±ì ìí´, Hibernateë ì êµí Criteria ë° Example ì§ì
+ í¹ì§(QBCì QBE)를 ì§ìíë¤. ë¹ì ì ëí ê°ì²´ë¤ë¡ì ê²°ê³¼ ì
ë³íì ìí ì íì ì¸ Hibernateì ì§ìì¼ë¡, ë¹ì ì ë°ì´í°ë² ì´ì¤ì
+ native SQL ìì ë¹ì ì ì§ì를 ííí ìë ìë¤.
+ </para>
+
+ <sect2 id="objectstate-querying-executing" revision="1">
+ <title>ì§ìë¤ì ì¤íí기</title>
+
+ <para>
+ HQL ì§ìì native SQL ì§ìë <literal>org.hibernate.Query</literal>ì ì¸ì¤í´ì¤ë¡ ííëë¤. ì´ ì¸í°íì´ì¤ë
+ íë¼ë¯¸í° ë°ì¸ë©, ê²°ê³¼ì
í¸ë¤ë§ì ìí, ê·¸ë¦¬ê³ ì¤ì ì§ìì ì¤íì ìí ë©ìëë¤ì ì ê³µíë¤. ë¹ì ì íì íì¬
+ <literal>Session</literal>ì ì¬ì©íì¬ íëì <literal>Query</literal>를 ì»ëë¤:
+ </para>
+
+ <programlisting><![CDATA[List cats = session.createQuery(
+ "from Cat as cat where cat.birthdate < ?")
+ .setDate(0, date)
+ .list();
+
+List mothers = session.createQuery(
+ "select mother from Cat as cat join cat.mother as mother where cat.name = ?")
+ .setString(0, name)
+ .list();
+
+List kittens = session.createQuery(
+ "from Cat as cat where cat.mother = ?")
+ .setEntity(0, pk)
+ .list();
+
+Cat mother = (Cat) session.createQuery(
+ "select cat.mother from Cat as cat where cat = ?")
+ .setEntity(0, izi)
+ .uniqueResult();]]
+
+Query mothersWithKittens = (Cat) session.createQuery(
+ "select mother from Cat as mother left join fetch mother.kittens");
+Set uniqueMothers = new HashSet(mothersWithKittens.list());]]></programlisting>
+
+ <para>
+ íëì ì§ìë ëê° <literal>list()</literal>를 í¸ì¶íì¬ ì¤íëê³ , ì§ìì ê²°ê³¼ë ë©ëª¨ë¦¬ ë´ìì íëì ì½ë ì
ìì¼ë¡
+ ì ì²´ì ì¼ë¡ ë¡ëë ê²ì´ë¤. íëì ì§ìì ìí´ ê²ìë ìí°í° ì¸ì¤í´ì¤ë¤ì ìì(persistent) ìíì ìë¤. ë¹ì ì ì§ìê°
+ íëì ê°ì²´ë¥¼ ì¤ì§ ë°íí ê²ìì ë¹ì ì´ ìê³ ìì ê²½ì°ì <literal>uniqueResult()</literal> ë©ìëë ë¨ì¶ì ì ê³µíë¤.
+ ì½ë ì
ë¤ì ëí´ eager íì¹ì ì¬ì©íë ì§ìë¤ì ëê° (ê·¸ê²ë¤ì ì´ê¸°íë ì½ë ì
ë¤ì ê°ì§) ë£¨í¸ ê°ì²´ë¤ì ëí ì¤ë³µë¤ì ëê°
+ ë°ííë¤. ë¹ì ì <literal>Set</literal>ì íµí´ ì´ë¤ ì¤ë³µë¤ì ê°ë¨íê² íí°ë§í ì ìë¤.
+ </para>
+
+ <sect3 id="objectstate-querying-executing-iterate">
+ <title>ê²°ê³¼ë¤ì ë°ë³µí기</title>
+
+ <para>
+ ì¢
ì¢
, ë¹ì ì <literal>iterate()</literal> ë©ìë를 ì¬ì©íì¬ ì§ì를 ì¤íí¨ì¼ë¡ì¨ ë ëì í¼í¬ë¨¼ì¤ë¥¼ ì±ì·¨íë
+ ê²ì´ ê°ë¥í ì ìë¤. ì´ê²ì ì¤ì§ ëê° ì§ìì ìí´ ë°íëë ì¤ì ìí°í° ì¸ì¤í´ì¤ë¤ì´ ì´ë¯¸ ì¸ì
ë´ì ìê±°ë
+ second-level ìºì ë´ì ìì ê²ìì ë¹ì ì´ ììíë ê²½ì°ì¼ ê²ì´ë¤. ë§ì¼ ê·¸ê²ë¤ì´ ì´ë¯¸ ìºìëì§ ììë¤ë©´,
+ <literal>iterate()</literal>ë <literal>list()</literal> ë³´ë¤ ë ë릴 ê²ì´ê³ ê°ë¨í ì§ìì ëí´
+ ë§ì ë°ì´í°ë² ì´ì¤ ì ìë¤ì, ëê° ì¤ì§ ìë³ìë¤ì ë°ííë ì´ê¸° selectì ëí´ <emphasis>1</emphasis>ë²ì ì ìê³¼
+ ì¤ì ì¸ì¤í´ì¤ë¤ì ì´ê¸°í ìí¤ë ì¶ê°ì ì¸ selectë¤ì ëí´ <emphasis>n</emphasis> ë²ì ì ìì íìë¡ í ì ìë¤.
+ </para>
+
+ <programlisting><![CDATA[// fetch ids
+Iterator iter = sess.createQuery("from eg.Qux q order by q.likeliness").iterate();
+while ( iter.hasNext() ) {
+ Qux qux = (Qux) iter.next(); // fetch the object
+ // something we couldnt express in the query
+ if ( qux.calculateComplicatedAlgorithm() ) {
+ // delete the current instance
+ iter.remove();
+ // dont need to process the rest
+ break;
+ }
+}]]></programlisting>
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-tuples">
+ <title>ííë¤ì ë°ííë ì§ìë¤</title>
+
+ <para>
+ Hibernate ì§ìë¤ì ëëë¡ ê°ì²´ë¤ì ííë¤ì ë°ííê³ , ê·¸ ê²½ì°ì ê°ê°ì ííì ë°°ì´ë¡ì ë°íëë¤:
+ </para>
+
+ <programlisting><![CDATA[Iterator kittensAndMothers = sess.createQuery(
+ "select kitten, mother from Cat kitten join kitten.mother mother")
+ .list()
+ .iterator();
+
+while ( kittensAndMothers.hasNext() ) {
+ Object[] tuple = (Object[]) kittensAndMothers.next();
+ Cat kitten = tuple[0];
+ Cat mother = tuple[1];
+ ....
+}]]></programlisting>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-scalar" revision="1">
+ <title>ì¤ì¹¼ë¼ ê²°ê³¼ë¤</title>
+
+ <para>
+ ì§ìë¤ì <literal>select</literal> ì ë´ì íëì í´ëì¤ì ëí íëì íë¡í¼í°ë¥¼ ì§ì í ì ìë¤.
+ ê·¸ê²ë¤ì ì¬ì§ì´ SQL ì§ê³ í¨ìë¤ì í¸ì¶í ìë ìë¤. íë¡í¼í°ë¤ì´ë aggregateë¤ì
+ "ì¤ì¹¼ë¼" ê²°ê³¼ë¤(ê·¸ë¦¬ê³ ìì ìíì ìë ìí°í°ë¤ì´ ìë ê²ì¼)ë¡ ê°ì£¼ëë¤.
+ </para>
+
+ <programlisting><![CDATA[Iterator results = sess.createQuery(
+ "select cat.color, min(cat.birthdate), count(cat) from Cat cat " +
+ "group by cat.color")
+ .list()
+ .iterator();
+
+while ( results.hasNext() ) {
+ Object[] row = (Object[]) results.next();
+ Color type = (Color) row[0];
+ Date oldest = (Date) row[1];
+ Integer count = (Integer) row[2];
+ .....
+}]]></programlisting>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-parameters">
+ <title>ë°ì¸ë íë¼ë¯¸í°ë¤</title>
+
+ <para>
+ <literal>Query</literal> ìì ë©ìëë¤ì ëª
ëª
ë íë¼ë¯¸í°ë¤ ëë JDBC-ì¤íì¼ì <literal>?</literal>
+ íë¼ë¯¸í°ë¤ì ë°ì¸ë© ê°ë¤ì ì ê³µíë¤. <emphasis>JDBCìë ëì¡°ì ì¼ë¡, Hibernate ì«ì íë¼ë¯¸í°ë¤ì 0ìì
+ ììëë¤.</emphasis> ëª
ëª
ë íë¼ë¯¸í°ë¤ì ì§ì 문ìì´ ììì <literal>:name</literal> íìì ìë³ìë¤ì´ë¤.
+ ëª
ëª
ë íë¼ë¯¸í°ë¤ì ì¥ì ë¤ì ë¤ìê³¼ ê°ë¤:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ ëª
ëª
ë íë¼ë¯¸í°ë¤ì ê·¸ê²ë¤ì´ ì§ì 문ìì´ ë´ì ë°ìíë ììì ê´ê³ìë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ê·¸ê²ë¤ì ëì¼í ì§ì ë´ìì ì¬ë¬ ë² ë°ìí ì ìë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ê·¸ê²ì ì기-ì¤ëª
ì ì´ë¤
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <programlisting><![CDATA[//named parameter (preferred)
+Query q = sess.createQuery("from DomesticCat cat where cat.name = :name");
+q.setString("name", "Fritz");
+Iterator cats = q.iterate();]]></programlisting>
+
+ <programlisting><![CDATA[//positional parameter
+Query q = sess.createQuery("from DomesticCat cat where cat.name = ?");
+q.setString(0, "Izi");
+Iterator cats = q.iterate();]]></programlisting>
+
+ <programlisting><![CDATA[//named parameter list
+List names = new ArrayList();
+names.add("Izi");
+names.add("Fritz");
+Query q = sess.createQuery("from DomesticCat cat where cat.name in (:namesList)");
+q.setParameterList("namesList", names);
+List cats = q.list();]]></programlisting>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-pagination">
+ <title>쪽매ê¹</title>
+
+ <para>
+ ë§ì¼ ë¹ì ì´ ë¹ì ì ê²°ê³¼ ì
ì ê²½ê³(ë¹ì ì´ ê²ìíê³ ì ìíë ìµë ê°ì ê·¸ë¦¬ê³ /ëë ë¹ì ì´ ê²ìíê³ ì ìíë 첫 ë²ì§¸ í)ì
+ ì§ì í íìê° ìë¤ë©´ ë¹ì ì <literal>Query</literal> ì¸í°íì´ì¤ì ë©ìëë¤ì ì¬ì©í´ì¼ íë¤:
+ </para>
+
+ <programlisting><![CDATA[Query q = sess.createQuery("from DomesticCat cat");
+q.setFirstResult(20);
+q.setMaxResults(10);
+List cats = q.list();]]></programlisting>
+
+ <para>
+ Hibernateë ì´ limit ì§ì를 ë¹ì ì DBMSì native SQLë¡ ë²ìíë ë°©ë²ì ìê³ ìë¤.
+ </para>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-scrolling">
+ <title>ì¤í¬ë¡¤ ê°ë¥í iteration</title>
+
+ <para>
+ ë¹ì ì JDBC ëë¼ì´ë²ê° ì¤í¬ë¡¤ ê°ë¥í <literal>ResultSet</literal>ë¤ì ì§ìí ê²½ì°,
+ <literal>Query</literal> ì¸í°íì´ì¤ë <literal>ScrollableResults</literal> ê°ì²´ë¥¼ ì»ëë°
+ ì¬ì©ë ì ìê³ , ê·¸ê²ì ì§ì ê²°ê³¼ë¤ì ëí ì ì°í ë¤ë¹ê²ì´ì
ì íì©í´ì¤ë¤.
+ </para>
+
+ <programlisting><![CDATA[Query q = sess.createQuery("select cat.name, cat from DomesticCat cat " +
+ "order by cat.name");
+ScrollableResults cats = q.scroll();
+if ( cats.first() ) {
+
+ // find the first name on each page of an alphabetical list of cats by name
+ firstNamesOfPages = new ArrayList();
+ do {
+ String name = cats.getString(0);
+ firstNamesOfPages.add(name);
+ }
+ while ( cats.scroll(PAGE_SIZE) );
+
+ // Now get the first page of cats
+ pageOfCats = new ArrayList();
+ cats.beforeFirst();
+ int i=0;
+ while( ( PAGE_SIZE > i++ ) && cats.next() ) pageOfCats.add( cats.get(1) );
+
+}
+cats.close()]]></programlisting>
+
+ <para>
+ ì´ë ¤ì§ ë°ì´í°ë² ì´ì¤ 커ë¥ì
(ê³¼ 커ì)ê° ì´ ê¸°ë¥ì íìí¨ì ë
¸í¸íê³ , ë§ì¼ ë¹ì ì´ ìª½ë§¤ê¹ ê¸°ë¥ì ìëìí¬ íìê° ìë¤ë©´
+ <literal>setMaxResult()</literal>/<literal>setFirstResult()</literal>를 ì¬ì©íë¼.
+ </para>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-named" revision="1">
+ <title>ëª
ëª
ë ì§ìë¤ì 구체í ìí¤ê¸°</title>
+
+ <para>
+ ë¹ì ì ëí 매í 문ì ìì ëª
ëª
ë ì§ìë¤ì ì ìí ì ìë¤.(ë§ì¼ ë¹ì ì ì§ìê° ë§í¬ì
ì¼ë¡ì í´ìë ì ìë 문ìë¤ì
+ í¬í¨í ê²½ì°ì <literal>CDATA</literal> ì¹ì
ì ì¬ì©íë ê²ì 기ìµíë¼))
+ </para>
+
+ <programlisting><![CDATA[<query name="ByNameAndMaximumWeight"><![CDATA[
+ from eg.DomesticCat as cat
+ where cat.name = ?
+ and cat.weight > ?
+] ]></query>]]></programlisting>
+
+ <para>
+ íë¼ë¯¸í° ë°ì¸ë©ê³¼ ì¤íì íë¡ê·¸ë¨ ìì¼ë¡ íí´ì§ë¤:
+ </para>
+
+ <programlisting><![CDATA[Query q = sess.getNamedQuery("ByNameAndMaximumWeight");
+q.setString(0, name);
+q.setInt(1, minWeight);
+List cats = q.list();]]></programlisting>
+
+ <para>
+ ì¤ì íë¡ê·¸ë¨ ì½ëë ì¬ì©ëë ì§ì ì¸ì´ì ë
립ì ì´ê³ , ë¹ì ì ëí ë©íë°ì´í°ë¡ native SQL ì§ìë¤ì ì ìí ìë ìê±°ë
+ ê·¸ê²ë¤ì 매í íì¼ë¤ ìì 기존 ì§ìë¤ì ìì¹ì§ìì¼ë¡ì¨ 기존 ì§ìë¤ì Hibernateë¡ ì´ì ìí¬ ìë ììì ë
¸í¸íë¼.
+ </para>
+
+ <para>
+ ëí <literal><hibernate-mapping></literal> ìì ë´ìì íëì ì§ì ì ì¸ì ê·¸ ì§ìì ëí
+ ì ì ì ì¼ ì´ë¦ì íìë¡ íê³ , ë°ë©´ì <literal><class></literal> ìì ë´ììì ì§ì ì ì¸ì
+ í´ëì¤ ì´ë¦ì¼ë¡ ìì§ëì´ë¦ì 첨ê°íì¬ ìëì ì¼ë¡ ì ì¼íê² ë§ë¤ì´ì§ë¤.
+ ì를 ë¤ì´ <literal>eg.Cat.ByNameAndMaximumWeight</literal>.
+ </para>
+
+ </sect3>
+
+ </sect2>
+
+ <sect2 id="objectstate-filtering" revision="1">
+ <title>ì½ë ì
ë¤ì íí°ë§ í기</title>
+ <para>
+ ì½ë ì
<emphasis>íí°</emphasis>ë ìì ì½ë ì
ëë ë°°ì´ì ì ì©ë ì ìë ì§ìì í¹ë³í íì
ì´ë¤. ì§ì 문ìì´ì
+ íì¬ì ì½ë ì
ìì를 ì미íë <literal>this</literal>를 참조í ì ìë¤.
+ </para>
+
+ <programlisting><![CDATA[Collection blackKittens = session.createFilter(
+ pk.getKittens(),
+ "where this.color = ?")
+ .setParameter( Color.BLACK, Hibernate.custom(ColorUserType.class) )
+ .list()
+);]]></programlisting>
+
+ <para>
+ ë°íëë ì½ë ì
ì íëì bagì¼ë¡ ê°ì£¼ëê³ , ê·¸ê²ì 주ì´ì§ ì½ë ì
ì ëí ì¬ë³¸ì´ë¤. ìëì ì½ë ì
ì ë³ê²½ëì§ ìëë¤
+ (ì´ê²ì ì´ë¦ "filter"ì ì미ìë ì ë°ëì´ì§ë§, ììëë íìì ì¼ì¹ëë¤).
+ </para>
+
+ <para>
+ íí°ë¤ì <literal>from</literal> ì ì íìë¡ íì§ ììì ê´ì°°íë¼(íìí ê²½ì°ì íí°ë¤ì´ í ê°ì from ì ì ê°ì§ ì
+ ììì§ë¼ë). íí°ë¤ì ì½ë ì
ììë¤ ìì²´ë¤ì ë°ííë ê²ì¼ë¡ íì ëì§ ìëë¤.
+ </para>
+
+ <programlisting><![CDATA[Collection blackKittenMates = session.createFilter(
+ pk.getKittens(),
+ "select this.mate where this.color = eg.Color.BLACK.intValue")
+ .list();]]></programlisting>
+
+ <para>
+ ì¬ì§ì´ íëì 공백ì íí° ì§ìë ì를 ë¤ì´ ê±°ëí ì½ë ì
ë´ì ìë ììë¤ì ë¶ë¶ì§í©ë¤ì ë¡ëìí¤ëë° ì ì©íë¤:
+ </para>
+
+ <programlisting><![CDATA[Collection tenKittens = session.createFilter(
+ mother.getKittens(), "")
+ .setFirstResult(0).setMaxResults(10)
+ .list();]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="objecstate-querying-criteria" revision="1">
+ <title>Criteria ì§ìë¤</title>
+
+ <para>
+ HQLì ê·¹í ê°ë ¥íì§ë§ ëªëª ê°ë°ìë¤ì ì§ì 문ìì´ë¤ì ë¹ëí기 ë³´ë¤, ê°ì²´ ì§í¥ API를 ì¬ì©íì¬ ëì ì¼ë¡ ì§ìë¤ì ë¹ëìí¤ë
+ ê²ì ì í¸íë¤. Hibernateë ì´ë¤ ê²½ì°ë¤ì ìí ì§ê´ì ì¸ <literal>Criteria</literal> query API를 ì ê³µíë¤:
+ </para>
+
+ <programlisting><![CDATA[Criteria crit = session.createCriteria(Cat.class);
+crit.add( Expression.eq( "color", eg.Color.BLACK ) );
+crit.setMaxResults(10);
+List cats = crit.list();]]></programlisting>
+
+ <para>
+ <literal>Criteria</literal>ì ì°ê´ë <literal>Example</literal> API ë
+ <xref linkend="querycriteria"/>ìì ìì¸íê² ë
¼ìëë¤.
+ </para>
+
+ </sect2>
+
+ <sect2 id="objectstate-querying-nativesql" revision="2">
+ <title>native SQLìì ì§ìë¤</title>
+
+ <para>
+ ë¹ì ì <literal>createSQLQuery()</literal>를 ì¬ì©íì¬ SQL ìì íëì ì§ì를 ííí ì ìê³ ,
+ Hibernateë¡ íì¬ê¸ ê²°ê³¼ ì
ë¤ë¡ë¶í° ê°ì²´ë¤ë¡ì 매íì ì²ë¦¬íëë¡ í ìë ìë¤. ë¹ì ì ì무ëë
+ <literal>session.connection()</literal>ì í¸ì¶í ì ìê³ ì§ì JDBC <literal>Connection</literal>ì
+ ì¬ì©í ì ììì ë
¸í¸íë¼. ë§ì¼ ë¹ì ì´ Hibernate API를 ì¬ì©íê³ ì ì íí ê²½ì°ì , ë¹ì ì SQL aliasë¤ì
+ ì¤ê´í¸ë¤ ìì í¬í¨ìì¼ì¼ íë¤:
+ </para>
+
+ <programlisting><![CDATA[List cats = session.createSQLQuery(
+ "SELECT {cat.*} FROM CAT {cat} WHERE ROWNUM<10",
+ "cat",
+ Cat.class
+).list();]]></programlisting>
+
+ <programlisting><![CDATA[List cats = session.createSQLQuery(
+ "SELECT {cat}.ID AS {cat.id}, {cat}.SEX AS {cat.sex}, " +
+ "{cat}.MATE AS {cat.mate}, {cat}.SUBCLASS AS {cat.class}, ... " +
+ "FROM CAT {cat} WHERE ROWNUM<10",
+ "cat",
+ Cat.class
+).list()]]></programlisting>
+
+ <para>
+ SQL ì§ìë¤ì Hibernate ì§ìë¤ì²ë¼ ëª
ëª
ë íë¼ë¯¸í°ë¤ê³¼ ìì¹ íë¼ë¯¸í°ë¤ì í¬í¨í ìë ìë¤. SQL ì§ìë¤ì ëí
+ ì¶ê° ì ë³´ë <xref linkend="querysql"/>ìì ì°¾ì ì ìë¤.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="objectstate-modifying" revision="1">
+ <title>ìì ê°ì²´ë¤ì ë³ê²½í기</title>
+
+ <para>
+ <emphasis>í¸ëìì
ìì ìì ì¸ì¤í´ì¤ë¤</emphasis> (ì를ë¤ë©´. <literal>Session</literal>ì ìí´ ë¡ëëê³ ,
+ ì ì¥ëê³ , ìì±ëê±°ë ì§ìë ê°ì²´ë¤)ì ì´í리ì¼ì´ì
ì ìí´ ì²ë¦¬ë ì ìê³ ìì ìíì ëí ììì ë³ê²½ë¤ì
+ <literal>Session</literal>ì´ <emphasis>flushë </emphasis> ë ììí ë ê²ì´ë¤(ì´ ì¥ì ë· ë¶ë¶ìì ë
¼ìë¨).
+ ë¹ì ì ë³ê²½ë¤ì ììí ìí¤ê¸° ìí´ (ë¤ë¥¸ ì©ë를 ê°ì§ <literal>update()</literal>ì ê°ì) í¹ë³í ë©ìë를 í¸ì¶í
+ íìê° ìë¤. ë°ë¼ì ê°ì²´ì ìí를 ì
ë°ì´í¸ ìí¤ë ê°ì¥ ê°ë¨í ë°©ë²ì <literal>Session</literal>ì´ ì´ë ¤ ìë ëì,
+ ê·¸ê²ì <literal>load()</literal>ìí¤ê³ ëì, ê·¸ê²ì ì§ì ì²ë¦¬íë ê²ì´ë¤:
+ </para>
+
+ <programlisting><![CDATA[DomesticCat cat = (DomesticCat) sess.load( Cat.class, new Long(69) );
+cat.setName("PK");
+sess.flush(); // changes to cat are automatically detected and persisted]]></programlisting>
+
+ <para>
+ ëëë¡ ì´ íë¡ê·¸ëë° ëª¨íì ë¶ì¶©ë¶íë¤. ìëíë©´ ê·¸ê²ì ëì¼í ì¸ì
ë´ìì (ê°ì²´ë¥¼ ë¡ëìí¤ë) SQL
+ <literal>SELECT</literal>ì (ê·¸ê²ì ì
ë°ì´í¸ë ìí를 ììí ìí¤ë) SQL <literal>UPDATE</literal>
+ ìì를 íìë¡ í ê²ì´ê¸° ë문ì´ë¤. ê·¸ë¬ë¯ë¡ Hibernateë detached ì¸ì¤í´ì¤ë¤ì ì¬ì©íë ëìì ì¸ ì ê·¼ë²ì ì ê³µíë¤.
+ </para>
+
+ <para>
+ <emphasis>Hibernateë <literal>UPDATE</literal> ë¬¸ì¥ ëë <literal>DELETE</literal> 문ì¥ì ì§ì ì ì¸ ì¤íì ìí
+ ê·¸ê² ìì ì API를 ì ê³µíì§ ììì ë
¸í¸íë¼. Hibernateë íëì <emphasis>ìí ê´ë¦¬</emphasis> ìë¹ì¤ì´ê³ , ë¹ì ì ê·¸ê²ì
+ ì¬ì©í <emphasis>문ì¥ë¤</emphasis>ì ìê°íì§ ë§ìì¼ íë¤. JDBCë SQL 문ì¥ë¤ì ì¤íìí¤ë ìì í APIì´ê³ , ë¹ì ì
+ <literal>session.connection()</literal>ì í¸ì¶íì¬ ì무ëë í ê°ì JDBC <literal>Connection</literal>ì
+ ì»ì ì ìë¤. ê²ë¤ê° ëë ì¤í¼ë ì´ì
ì ê°ë
ì ì¨ë¼ì¸ í¸ëìì
ì²ë¦¬-ì§í¥ì ì¸ ì´í리ì¼ì´ì
ë¤ì ìí ê°ì²´/ê´ê³í 매íê³¼ 충ëíë¤.
+ íì§ë§ Hibernateì ì¥ë ë²ì ë¤ì í¹ë³í ëì©ëì ì¤í¼ë ì´ì
기ë¥ë¤ì ì ê³µí ìë ìë¤. ëªëª ê°ë¥í ë°°ì¹ ì¤í¼ë ì´ì
í¸ë¦ë¤ì
+ ëí´ìë <xref linkend="batch"/>ì ë³´ë¼.</emphasis>
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-detached" revision="2">
+ <title>detached ê°ì²´ë¤ì ë³ê²½ìí¤ê¸°</title>
+
+ <para>
+ ë§ì ì´í리ì¼ì´ì
ë¤ì íëì í¸ëìì
ë´ìì íëì ê°ì²´ë¥¼ ê²ìíê³ , ì²ë¦¬ë¥¼ ìí UI ê³ì¸µì¼ë¡ ê·¸ê²ì ì ì¡íê³ ,
+ ê·¸ë° ë¤ì ìë¡ì´ í¸ëìì
ë´ìì ë³ê²½ë¤ì ì ì¥í íìê° ìë¤. ê³ ëì-ëìì± íê²½ìì ì´ë° ì¢
ë¥ì ì ê·¼ë²ì ì¬ì©íë
+ ì´í리ì¼ì´ì
ë¤ì ëê° ìì
ì "긴" ë¨ì를 íì¤í 격리ìí¤ê¸° ìí´ ë²ì í ë ë°ì´í°ë¥¼ ì¬ì©íë¤.
+ </para>
+
+ <para>
+ Hibernateë <literal>Session.update()</literal> ë©ìë ëë <literal>Session.merge()</literal> ë©ìë를
+ ì¬ì©íì¬ detached ì¸ì¤í´ì¤ë¤ì ì¬ì²¨ë¶ë¥¼ ì ê³µí¨ì¼ë¡ì¨ ì´ ëª¨íì ì§ìíë¤:
+ </para>
+
+ <programlisting><![CDATA[// in the first session
+Cat cat = (Cat) firstSession.load(Cat.class, catId);
+Cat potentialMate = new Cat();
+firstSession.save(potentialMate);
+
+// in a higher layer of the application
+cat.setMate(potentialMate);
+
+// later, in a new session
+secondSession.update(cat); // update cat
+secondSession.update(mate); // update mate]]></programlisting>
+
+ <para>
+ ë§ì¼ <literal>catId</literal> ìë³ì를 ê°ì§ <literal>Cat</literal>ì´ <literal>secondSession</literal>ì ìí´
+ ì´ë¯¸ ë¡ëëìì ê²½ì°ì ì´í리ì¼ì´ì
ì´ ê·¸ê²ì ë¤ì ì¬ì²¨ë¶íë ¤ê³ ìëí ë, ìì¸ìí©ì´ ëì ¸ì¡ì ê²ì´ë¤.
+ </para>
+
+ <para>
+ ë§ì¼ ê·¸ ì¸ì
ì´ ëì¼í ìë³ì를 ê°ì§ ìì ì¸ì¤í´ì¤ë¥¼ ì´ë¯¸ í¬í¨íì§ ììì ë¹ì ì´ íì íë ê²½ì°ìë
+ <literal>update()</literal>를 ì¬ì©íê³ , ë§ì¼ ë¹ì ì´ ì¸ì
ì ìí를 ê³ ë ¤íì§ ììì±ë¡ ì무ëë ë¹ì ì ë³ê²½ì ë³í©ìí¤ê³ ì
+ ìí ê²½ì°ìë <literal>merge()</literal>를 ì¬ì©íë¼. ë¬ë¦¬ ë§í´, ë¹ì ì detached ì¸ì¤í´ì¤ë¤ì ëí ì¬ì²¨ë¶ê° ì¤íëë
+ 첫 ë²ì§¸ ì¤í¼ë ì´ì
ìì íì¤í í¨ì¼ë¡ì¨, <literal>update()</literal>ë ëê° ê° ë§ë¤ì´ì§ ì¸ì
ìì ë¹ì ì´ í¸ì¶íê² ë
+ 첫 ë²ì§¸ ë©ìëì´ë¤.
+ </para>
+
+ <para>
+ ì´í리ì¼ì´ì
ì ë§ì¼ ê·¸ê²ì´ detached ì¸ì¤í´ì¤ë¤ì ìíê° ëí ì
ë°ì´í¸ ëë ê²ì ìí ê²½ì°ì<emphasis>ë§</emphasis>
+ 주ì´ì§ detached ì¸ì¤í´ì¤ë¡ë¶í° ëë¬ ê°ë¥í detached ì¸ì¤í´ì¤ë¤ì ê°ë³ì ì¼ë¡<literal>update()</literal> ìí¬ ê²ì´ë¤.
+ ì´ê²ì ë¬¼ë¡ <emphasis>transitive persistence(ì ì´ ìì)</emphasis>ì ì¬ì©íì¬ ìëí ë ì ìê³ ,
+ <xref linkend="objectstate-transitive"/>를 ë³´ë¼.
+ </para>
+
+ <para>
+ <literal>lock()</literal> ë©ìëë ëí íëì ê°ì²´ë¥¼ ìë¡ì´ ì¸ì
ì ëí´ ë¤ì ì°ê´ìí¤ëê²ì ì´í리ì¼ì´ì
ìê² íì©í´ì¤ë¤.
+ íì§ë§ detached ì¸ì¤í´ì¤ë ë³ê²½ëì§ ììì¼ íë¤!
+ </para>
+
+ <programlisting><![CDATA[//just reassociate:
+sess.lock(fritz, LockMode.NONE);
+//do a version check, then reassociate:
+sess.lock(izi, LockMode.READ);
+//do a version check, using SELECT ... FOR UPDATE, then reassociate:
+sess.lock(pk, LockMode.UPGRADE);]]></programlisting>
+
+ <para>
+ <literal>lock()</literal>ì´ ì¬ë¬ê°ì§ <literal>LockMode</literal>ë¤ì ì¬ì©ë ì ììì ë
¸í¸íê³ ,
+ ìì¸í ê²ì API 문ìì í¸ëìì
ì²ë¦¬ì ê´í ì¥ì ë³´ë¼. ì¬ì²¨ë¶ë <literal>lock()</literal>ì ëí
+ ì ì¼í ì°ììë ìëë¤.
+ </para>
+
+ <para>
+ 긴 ìì
ë¨ìì ëí ë¤ë¥¸ 모íë¤ì <xref linkend="transactions-optimistic"/>ìì ë
¼ìëë¤.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-saveorupdate">
+ <title>ìëì ì¸ ìí ê²ì¶</title>
+
+ <para>
+ Hibernate ì¬ì©ìë¤ì ìë¡ì´ ìë³ì를 ìì±ìì¼ì transient ì¸ì¤í´ì¤ë¥¼ ì ì¥íê±°ë
+ ê·¸ê²ì íì¬ ìë³ìì ì°ê´ë detached ì¸ì¤í´ì¤ë¤ì ì
ë°ì´í¸/ì¬ì²¨ë¶ ìí¤ë ì¼ë°ì ì¸ ì©ëì ë©ìë를 ìì²íë¤.
+ <literal>saveOrUpdate()</literal> ë©ìëë ì´ ê¸°ë¥ì 구ííë¤.
+ </para>
+
+ <programlisting><![CDATA[// in the first session
+Cat cat = (Cat) firstSession.load(Cat.class, catID);
+
+// in a higher tier of the application
+Cat mate = new Cat();
+cat.setMate(mate);
+
+// later, in a new session
+secondSession.saveOrUpdate(cat); // update existing state (cat has a non-null id)
+secondSession.saveOrUpdate(mate); // save the new instance (mate has a null id)]]></programlisting>
+
+ <para>
+ <literal>saveOrUpdate()</literal>ì ì¬ì© ìì ì ì미ë ì´ì¬ìë¤ìê²ë í¼ëì¤ë¬ì ë³´ì¸ë¤. 먼ì , íëì ì¸ì
ìì ì¨
+ ì¸ì¤í´ì¤ë¥¼ ë ë¤ë¥¸ ìë¡ì´ ì¸ì
ë´ìì ì¬ì©íë ¤ê³ ìëíì§ ìë í, ë¹ì ì <literal>update()</literal>,
+ <literal>saveOrUpdate()</literal>, ëë <literal>merge()</literal>를 ì¬ì©í íìë ìì ê²ì´ë¤.
+ ëªëª ì ì²´ ì´í리ì¼ì´ì
ë¤ì ì´ë¤ ë©ìëë¤ ì¤ ì´ë ê²ë ê²°ì½ ì¬ì©íì§ ìì ê²ì´ë¤.
+ </para>
+
+ <para>
+ ëê° <literal>update()</literal> ëë <literal>saveOrUpdate()</literal>ë ë¤ì ìë리ì¤ìì ì¬ì©ëë¤:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ ì´í리ì¼ì´ì
ì´ ì²« ë²ì§¸ ì¸ì
ë´ì ê°ì²´ë¥¼ ë¡ëìí¨ë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ê°ì²´ê° UI í°ì´ë¡ ì ë¬ëë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ëªëª ë³ê²½ë¤ì´ ê·¸ ê°ì²´ì íí´ì§ë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ê°ì²´ê° ë¹ì§ëì¤ ë¡ì§ í°ì´ë¡ ì ë¬ëë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ì´í리ì¼ì´ì
ì ë ë²ì§¸ ì¸ì
ìì <literal>update()</literal>를 í¸ì¶í¨ì¼ë¡ì¨ ì´ë¤ ë³ê²½ë¤ì ììí ìí¨ë¤
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ <literal>saveOrUpdate()</literal>ë ë¤ìì ííë¤:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ ë§ì¼ ê°ì²´ê° ì´ ì¸ì
ë´ìì ì´ë¯¸ ììí ëì´ ìì ê²½ì°, ì무ê²ë ííì§ ìëë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ë§ì¼ ê·¸ ì¸ì
ê³¼ ì°ê´ë ë ë¤ë¥¸ ê°ì²´ê° ëì¼í ìë³ì를 ê°ì§ ê²½ì°, ìì¸ìí©ì ëì§ë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ë§ì¼ ê·¸ ê°ì²´ê° ìë³ì íë¡í¼í°ë¥¼ ê°ì§ ìì ê²½ì°, ê·¸ê²ì <literal>save()</literal> ìí¨ë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ë§ì¼ ê°ì²´ì ìë³ìê° ìë¡ì´ ì´ê¸°í ë ê°ì²´ì í ë¹ë ê°ì ê°ì§ ê²½ì°, ê·¸ê²ì <literal>save()</literal> ìí¨ë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ë§ì¼ ê°ì²´ê° (<literal><version></literal> ëë <literal><timestamp></literal>ì ìí´)
+ ë²ì í ëê³ , version íë¡í¼í° ê°ì´ ìë¡ì´ ì´ê¸°í ë ê°ì²´ì í ë¹ë ê²ê³¼ ëì¼í ê°ì¼ ê²½ì°, ê·¸ê²ì <literal>save()</literal>
+ ìí¨ë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ê·¸ ë°ì ê²½ì° ê·¸ ê°ì²´ë¥¼ <literal>update()</literal> ìí¨ë¤
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ê·¸ë¦¬ê³ <literal>merge()</literal>ë ë§¤ì° ë¤ë¥´ë¤:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ ë§ì¼ ì¸ì
ê³¼ íì¬ ì°ê´ë ëì¼í ìë³ì를 ê°ì§ ìì ì¸ì¤í´ì¤ê° ì¡´ì¬í ê²½ì°,
+ 주ì´ì§ ê°ì²´ì ìí를 ìì ì¸ì¤í´ì¤ ìì¼ë¡ ë³µì¬íë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ë§ì¼ ì¸ì
ê³¼ íì¬ ì°ê´ë ìì ì¸ì¤í´ì¤ê° ì¡´ì¬íì§ ìì ê²½ì°, ë°ì´í°ë² ì´ì¤ë¡ë¶í° ê·¸ê²ì ë¡ëìí¤ë ¤ê³ ìëíê±°ë
+ ìë¡ì´ ìì ì¸ì¤í´ì¤ë¥¼ ìì±ìí¤ë ¤ê³ ìëíë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ìì ì¸ì¤í´ì¤ê° ë°íëë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ 주ì´ì§ ì¸ì¤í´ì¤ë ì¸ì
ê³¼ ì°ê´ëì§ ìê³ , ê·¸ê²ì detached ìíì 머무른ë¤
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect1>
+
+ <sect1 id="objectstate-deleting" revision="1">
+ <title>ìì ê°ì²´ë¤ì ìì í기</title>
+
+ <para>
+ <literal>Session.delete()</literal>ë ë°ì´í°ë² ì´ì¤ë¡ë¶í° ê°ì²´ì ìí를 ì ê±°í ê²ì´ë¤.
+ ë¬¼ë¡ ë¹ì ì ì´í리ì¼ì´ì
ì ì¬ì í detached ê°ì²´ì ëí 참조를 ìì í ê²ì´ë¤. ìì ì¸ì¤í´ì¤ë¥¼ transientë¡ ë§ë¤ ë
+ <literal>delete()</literal>를 ìê°íë ê²ì´ ìµìì´ë¤.
+ </para>
+
+ <programlisting><![CDATA[sess.delete(cat);]]></programlisting>
+
+ <para>
+ ë¹ì ì foreign í¤ ì»¨ì¤í¸ë ì¸í¸ ìë°°ë¤ì ëí ìíì± ìì´ ë¹ì ì´ ì¢ìíë ì´ë¤ ììë¡ ê°ì²´ë¤ì ìì í ìë ìë¤.
+ ì못ë ììë¡ ê°ì²´ë¤ì ìì í¨ì¼ë¡ì¨ foreign í¤ ì»¬ë¼ì ëí <literal>NOT NULL</literal> 컨ì¤í¸ë ì¸í¸ë¥¼ ìë°°í ê°ë¥ì±ì´
+ ì¬ì í ì¡´ì¬íë¤. ì를 ë¤ì´, ë¹ì ì´ ë¶ëª¨ë¥¼ ìì íì§ë§, ê·¸ ììë¤ì ìì íë ê²ì ìì ê²½ì°.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-replicating" revision="1">
+ <title>ë ê°ì ë¤ë¥¸ ë°ì´í°ì ì¥ìë¤ ì¬ì´ì ê°ì²´ë¤ì ë³µì í기</title>
+
+ <para>
+ ìì ì¸ì¤í´ì¤ë¤ì ê·¸ëí를 ì·¨íê³ ìë³ì ê°ë¤ì ë¤ì ìì±ìí¤ì§ ìê³ ì ê·¸ê²ë¤ì ë¤ë¥¸ ì ì¥ì ìì ììí ìí¤ë ê²ì ê°ë¥íëë¡
+ ë§ëë ê²ì´ ì¢
ì¢
ì ì©íë¤.
+ </para>
+
+ <programlisting><![CDATA[//retrieve a cat from one database
+Session session1 = factory1.openSession();
+Transaction tx1 = session1.beginTransaction();
+Cat cat = session1.get(Cat.class, catId);
+tx1.commit();
+session1.close();
+
+//reconcile with a second database
+Session session2 = factory2.openSession();
+Transaction tx2 = session2.beginTransaction();
+session2.replicate(cat, ReplicationMode.LATEST_VERSION);
+tx2.commit();
+session2.close();]]></programlisting>
+
+ <para>
+ <literal>ReplicationMode</literal>ë <literal>replicate()</literal>ê° ë°ì´í°ë² ì´ì¤ ë´ì ìë 기존ì
+ íë¤ê³¼ì 충ëì ì²ë¦¬íê²ë ë°©ë²ì ê²°ì íë¤.
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>ReplicationMode.IGNORE</literal> - ëì¼í ìë³ì를 ê°ì§ 기존 ë°ì´í°ë² ì´ì¤ íì´ ì¡´ì¬í
+ ê²½ì°ì ê·¸ ê°ì²´ë¥¼ 무ìíë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>ReplicationMode.OVERWRITE</literal> - ëì¼í ìë³ì를 ê°ì§ ì´ë¤ 기존ì ë°ì´í°ë² ì´ì¤ íì ë®ì´ ì´ë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>ReplicationMode.EXCEPTION</literal> - ë§ì¼ ëì¼í ìë³ì를 ê°ì§ 기존 ë°ì´í°ë² ì´ì¤ íì´ ì¡´ì¬í ê²½ì°ì
+ ìì¸ìí©ì ëì§ë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>ReplicationMode.LATEST_VERSION</literal> - íì ë²ì ë²í¸ê° ê°ì²´ì ë²ì ë²í¸ ë³´ë¤ ì´ì ì ê²ì´ë©´ ë®ì´ì°ê³ ,
+ ê·¸ ë°ì ê²½ì°ì ê·¸ ê°ì²´ë¥¼ 무ìíë¤
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ì´ í¹ì§ì ì°ììë¤ì ë¤ë¥¸ ë°ì´í°ë² ì´ì¤ ì¸ì¤í´ì¤ë¤ ìì¼ë¡ ì
ë ¥ë ë°ì´í° ì¼ì¹ìí¤ê¸°, ì í ì
ê·¸ë ì´ë ëìì ìì¤í
êµ¬ì± ì ë³´
+ ì
ë°ì´í¸ í기, non-ACID í¸ëìì
ë¤ ëìì íí´ì§ ë³ê²½ë¤ì 롤백ìí¤ê¸° ë±ì í¬í¨íë¤.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-flushing">
+ <title>Sessionì flush ìí¤ê¸°</title>
+
+ <para>
+ ìê°ì´ ì§ë¨ì ë°ë¼ <literal>Session</literal>ì JDBC 커ë¥ì
ì ìíì ë©ëª¨ë¦¬ ë´ì ë³´ê´ë ê°ì²´ë¤ì ìí를 ë기í ìí¤ëë°
+ íìí SQL 문ì¥ë¤ì ì¤íìí¬ ê²ì´ë¤. ì´ íë¡ì¸ì¤ <emphasis>flush</emphasis>ë ë¤ì ìì ë¤ìì ëí´í¸ë¡ ë°ìíë¤
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ ëªëª ì§ìë¤ì´ ì¤íë기 ì ì
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>org.hibernate.Transaction.commit()</literal> ìì ìì
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>Session.flush()</literal> ìì ìì
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ SQL 문ì¥ë¤ì´ ë¤ì ììë¡ ì¤í ëª
ë ¹ì´ ë´ë ¤ì§ë¤
+ </para>
+
+ <orderedlist spacing="compact">
+ <listitem>
+ <para>
+
+ ëìíë ê°ì²´ë¤ì´ <literal>Session.save()</literal>를 ì¬ì©íì¬ ì ì¥ëìë ê²ê³¼ ê°ì ììë¡, 모ë ìí°í° ì½ì
ë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ 모ë ìí°í° ì
ë°ì´í¸ë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ 모ë ì½ë ì
ìì ë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ 모ë ì½ë ì
ìì ìì ë¤, ì
ë°ì´í¸ë¤ ê·¸ë¦¬ê³ ì½ì
ë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ 모ë ì½ë ì
ì½ì
ë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ëìíë ê°ì²´ë¤ì´ <literal>Session.delete()</literal>를 ì¬ì©íì¬ ìì ëìë ê²ê³¼ ê°ì ììë¡ ëª¨ë ìí°í° ìì ë¤.
+ </para>
+ </listitem>
+ </orderedlist>
+
+ <para>
+ (íê°ì§ ìì¸ë ê°ì²´ë¤ì´ ì ì¥ë ë <literal>native</literal> ID ìì±ì ì¬ì©íë ê°ì²´ë¤ì´ insert ëë ì ì´ë¤.)
+ </para>
+
+ <para>
+ ë¹ì ì´ ëª
ìì ì¼ë¡ <literal>flush()</literal> ìí¬ ë를 ì ì¸íë©´, <literal>Session</literal>ì´ JDBC í¸ì¶ë¤ì
+ ì¤íìí¤ë <emphasis>ìì </emphasis>, ê·¸ê²ë¤ì´ ì¤íëë <emphasis>ìì</emphasis>ë§ì ì ëì ì¼ë¡ ë³´ì¥íì§ë ìëë¤.
+ íì§ë§ Hibernateë <literal>Query.list(..)</literal>ê° ì¤í¨ì±ì´ ìë ë°ì´í°ë¥¼ ê²°ì½ ë°ííì§ ìì ê²ìì ë³´ì¥íê±°ë;
+ ê·¸ê²ë¤ì´ ì못ë ë°ì´í°ë ë°ííì§ ìì ê²ìì ë³´ì¥íë¤.
+ </para>
+
+ <para>
+ flushê° ë무 ì주 ë°ìíì§ ìëë¡ ëí´í¸ íì를 ë³ê²½íë ê²ì´ ê°ë¥íë¤. <literal>FlushMode</literal> í´ëì¤ë ì¸ ê°ì
+ ë¤ë¥¸ 모ëë¤ì ì ìíë¤: ì¤ì§ ì»¤ë° ì(ê·¸ë¦¬ê³ Hibernate <literal>Transaction</literal> APIê° ì¬ì©ë ëìë§) flush 모ë,
+ ì¤ëª
ë 루í´ì ì¬ì©íë ìëì ì¸ flush 모ë, ëë <literal>flush()</literal>ê° ëª
ìì ì¼ë¡ í¸ì¶ëì§ ìë í flush ìí¤ì§
+ ìë 모ë. ë§ì§ë§ 모ëë ì¤ë ëì ì¤íëë ìì
ë¨ìì ëí´ ì ì©íê³ , ì¬ê¸°ì <literal>Session</literal>ì ì´ë ¤ì§ì±ë¡
+ ì ì§ëê³ ì¤ë ìê° ëì ì°ê²°ì´ í´ì ë ì±ë¡ ì ì§ëë¤. (<xref linkend="transactions-optimistic-longsession"/>를 ë³´ë¼).
+ </para>
+
+ <programlisting><![CDATA[sess = sf.openSession();
+Transaction tx = sess.beginTransaction();
+sess.setFlushMode(FlushMode.COMMIT); // allow queries to return stale state
+
+Cat izi = (Cat) sess.load(Cat.class, id);
+izi.setName(iznizi);
+
+// might return stale data
+sess.find("from Cat as cat left outer join cat.kittens kitten");
+
+// change to izi is not flushed!
+...
+tx.commit(); // flush occurs
+sess.close();]]></programlisting>
+
+ <para>
+ flush ëìì, íëì ìì¸ìí©ì´ ë°ìí ìë ìë¤(ì를 ë¤ë©´. ë§ì¼ DML ì¤í¼ë ì´ì
ì´ ì»¨ì¤í¸ë ì¸í¸ë¥¼ ìë°í ê²½ì°). ìì¸ìí©ë¤ì
+ ì²ë¦¬íë ê²ì Hibernatemì í¸ëìì
í¹ì§ì ê´í ì´ë¤ ì´í´ë¥¼ ìë°íë©°, ì°ë¦¬ë <xref linkend="transactions"/>ìì ê·¸ê²ì
+ ë
¼ìíë¤.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-transitive" revision="1">
+ <title>Transitive persistence(ì ì´ ìì)</title>
+
+ <para>
+ í¹í ë¹ì ì´ ì°ê´ë ê°ì²´ë¤ì ê·¸ëí를 ë¤ë£° ê²½ì°ì, í¹í ê°ë³ ê°ì²´ë¤ì ì ì¥íê³ , ìì íê±°ë, ì¬ì²¨ë¶ìí¤ë ê²ì´ 꽤 ë²ê±°ë¡ë¤.
+ ê³µíµë ê²½ì°ë íëì ë¶ëª¨/ìì ê´ê³ì´ë¤. ë¤ì ìì 를 ê²í íì:
+ </para>
+
+ <para>
+ ë§ì¼ ë¶ëª¨/ìì ê´ê³ìì ììë¤ì´ ê°(value) íì
(ì를 ë¤ë©´. 주ìë¤ ëë 문ìì´ë¤ì ê°ì§ íëì ì½ë ì
)ì¼ ê²½ì°, ê·¸ê²ë¤ì ìëª
주기ë
+ ë¶ëª¨ì ìì¡´í ê²ì´ê³ ìí ë³ê²½ë¤ì ëí´ í¸ë¦¬í "ì¼ì¤ì¼ì´ë©"ì ë ì´ìì ì¡ì
ì´ íìíì§ ìì ê²ì´ë¤. ë§ì¼ ë¶ëª¨ê° ì ì¥ë ë,
+ ê°(value)-íì
ì ìì ê°ì²´ë¤ë ë§ì°¬ê°ì§ë¡ ì ì¥ëê³ , ë¶ëª¨ê° ìì ë ë, ììë¤ì´ ìì ë ê²ì´ë¤. ì´ê²ì ì¬ì§ì´ ì½ë ì
ì¼ë¡ë¶í° íëì
+ ììì ì ê±°íë ê·¸ë° ì¤í¼ë ì´ì
ë¤ì ëí´ìë ëìíë¤; Hibernateë ì´ê²ì ê²ì¶íê³ , ê°(value)-íì
ì ê°ì²´ë¤ì 참조를 ê³µì í ì
+ ìì¼ë¯ë¡, ë°ì´í°ë² ì´ì¤ë¡ë¶í° ê·¸ ììì ìì ìí¨ë¤.
+ </para>
+
+ <para>
+ ì´ì ê°(value) íì
ì´ ìë, ìí°í°ë¤ì¸ ë¶ëª¨ì ìì ê°ì²´ë¤ì ê°ì§ ëì¼í ìë리ì¤ë¥¼ ê²í íì(ì를 ë¤ë©´. ì¹´í
ê³ ë¦¬ë¤ê³¼ ìì´í
ë¤,
+ ëë ë¶ëª¨ ê³ ìì´ë ìì ê³ ìì´). ìí°í°ë¤ì ê·¸ê²ë¤ ìì ì ìëª
주기를 ê°ê³ , ê³µì ë 참조ë¤ì ì§ìíê³ (ë°ë¼ì ì½ë ì
ì¼ë¡ë¶í° íëì
+ ìí°í°ë¥¼ ì ê±°íë ê²ì ê·¸ê²ì´ ìì ë ì ììì ì미íì§ ìëë¤), ê·¸ë¦¬ê³ ëí´í¸ë¡ íëì ìí°í°ë¡ë¶í° ì´ë¤ ë¤ë¥¸ ì°ê´ë ìí°í°ë¤ë¡ì
+ ìíì ì¼ì¤ì¼ì´ë©ì ì¡´ì¬íì§ ìëë¤. Hibernateë ëí´í¸ë¡ <emphasis>ëë¬ê°ë¥ì±ì ìí ììì±(persistence by reachability)</emphasis>ì
+ 구ííì§ ìëë¤.
+ </para>
+
+ <para>
+ - <literal>persist(), merge(), saveOrUpdate(), delete(), lock(), refresh(), evict(), replicate()</literal>를
+ í¬í¨íë- Hibernate ì¸ì
ì ëí ê°ê°ì 기본 ì¤í¼ë ì´ì
ì ëí´ì íëì ëìíë ì¼ì¤ì¼ì´ë© ì¤íì¼ì´ ì¡´ì¬íë¤. ì¼ì¤ì¼ì´ë ì¤íì¼ë¤
+ ê°ê°ì <literal>create, merge, save-update, delete, lock, refresh, evict, replicate</literal>ë¡ ëª
ëª
ëë¤.
+ ë§ì¼ ë¹ì ì´ íëì ì¤í¼ë ì´ì
ì´ íëì ì°ê´ì ë°ë¼ ì¼ì¤ì¼ì´ë©ëë ê²ì ìí ê²½ì°, ë¹ì ì 매í 문ì ë´ì ê·¸ê²ì ì§ìí´ì¼ íë¤.
+ ì를 ë¤ë©´:
+ </para>
+
+ <programlisting><![CDATA[<one-to-one name="person" cascade="persist"/>]]></programlisting>
+
+ <para>
+ ì¼ì¤ì¼ì´ë© ì¤íì¼ë¤ì´ ê²°í©ë ìë ìë¤:
+ </para>
+
+ <programlisting><![CDATA[<one-to-one name="person" cascade="persist,delete,lock"/>]]></programlisting>
+
+ <para>
+ ë¹ì ì <emphasis>모ë </emphasis> ì¤í¼ë ì´ì
ë¤ì´ ê·¸ ì°ê´ì ë°ë¼ ì¼ì¤ì¼ì´ë ëì´ì¼ í¨ì ì§ì íëë°
+ <literal>cascade="all"</literal>ì ì¬ì©í ìë ìë¤. ëí´í¸<literal>cascade="none"</literal>ì ì¤í¼ë ì´ì
ë¤ì´
+ ì¼ì¤ì¼ì´ë ëì§ ìì ê²ìì ì§ì íë¤.
+ </para>
+
+ <para>
+ í¹ì í ì¼ì¤ì¼ì´ë ì¤íì¼ì¸, <literal>delete-orphan</literal>ì ì¤ì§ one-to-many ì°ê´ë¤ìë§ ì ì©ëê³ ,
+ <literal>delete()</literal> ì¤í¼ë ì´ì
ì´ ê·¸ ì°ê´ì¼ë¡ë¶í° ì ê±°ëë ììì ìì ê°ì²´ì ì ì©ëì´ì¼ í¨ì ëíë¸ë¤.
+ </para>
+
+
+ <para>
+ ê¶ì¥ì¬íë¤ :
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ íëì <literal><many-to-one></literal> ëë <literal><many-to-many></literal> ì°ê´ì
+ ëí´ ì¼ì¤ì¼ì´ë를 ê°ë¥íê² íë ê²ì ëê° ìë¯¸ê° ìë¤. ì¼ì¤ì¼ì´ëë <literal><one-to-one></literal> ì°ê´ê³¼
+ <literal><one-to-many></literal> ì°ê´ì ëí´ ì¢
ì¢
ì ì©íë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ë§ì¼ ìì ê°ì²´ì ìëª
ì´ ê·¸ ë¶ëª¨ ê°ì²´ì ìëª
ì 묶ì¬ì ¸ ìì ê²½ì°, <literal>cascade="all,delete-orphan"</literal>ì
+ ì§ì í¨ì¼ë¡ì¨ ê·¸ê²ì <emphasis>ìëª
주기 ê°ì²´</emphasis>ë¡ ë§ë¤ì´ë¼.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ê·¸ ë°ì ê²½ì°, ë¹ì ì ì¼ì¤ì¼ì´ë를 ì í íìë¡ íì§ ìì ì ìë¤. ê·¸ë¬ë ë§ì¼ ë¹ì ì´ ëì¼í í¸ëìì
ë´ìì ë¶ëª¨ì ììì
+ ëí´ ì주 í¨ê» ìì
íê² ë ê²ì´ë¼ ìê°ëê³ , ë¹ì ì¤ì¤ë¡ íì´í íë ê²ì ì ì½íê³ ì ìí ê²½ì°,
+ <literal>cascade="persist,merge,save-update"</literal>를 ì¬ì©íë ê²ì ê³ ë ¤íë¼.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ <literal>cascade="all"</literal>ì ê°ì§ (ë¨ì¼ ê° ì°ê´ì´ë íëì ì½ë ì
ì´ë ) íëì ì°ê´ì 매íìí¤ë ê²ì
+ ê·¸ ì°ê´ì ë¶ëª¨ì ì ì¥/ì
ë°ì´í¸/ìì ê° ìì ëë ììë¤ì ì ì¥/ì
ë°ì´í¸/ìì ë¡ ê·ê²°ëë <emphasis>ë¶ëª¨/ìì</emphasis>
+ ì¤íì¼ì ê´ê³ë¡ ë§í¬íë¤.
+ </para>
+ <para>
+ ê²ë¤ê°, íëì ìì ë¶ëª¨ë¡ë¶í° íëì ììì ëí ë¨ìí 참조ë ììì ì ì¥/ì
ë°ì´í¸ë¡ ê·ê²°ë ê²ì´ë¤. íì§ë§ ì´ ë©íí¬ë ë¶ìì íë¤.
+ ê·¸ê²ì ë¶ëª¨ì ìí´ ì°¸ì¡° í´ì ëë ììì <literal>cascade="delete-orphan"</literal>ì¼ë¡ 매íë íëì
+ <literal><one-to-many></literal> ì°ê´ì ê²½ì°ë¥¼ ì ì¸íë©´, ìëì ì¼ë¡ ìì ëì§ <emphasis>ìëë¤</emphasis>.
+ íëì ë¶ëª¨/ìì ê´ê³ì ëí ì¼ì¤ì¼ì´ë© ì¤í¼ë ì´ì
ì ì íí ì미ë ë¤ìê³¼ ê°ë¤:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ ë§ì¼ ë¶ëª¨ê° <literal>persist()</literal>ì ì ë¬ë ê²½ì°, 모ë ììë¤ì´ <literal>persist()</literal>ì ì ë¬ëë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ë§ì¼ ë¶ëª¨ê° <literal>merge()</literal>ì ì ë¬ë ê²½ì°, 모ë ììë¤ì´ <literal>merge()</literal>ì ì ë¬ëë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ë§ì¼ ë¶ëª¨ê° <literal>save()</literal>, <literal>update()</literal> ëë <literal>saveOrUpdate()</literal>ì
+ ì ë¬ë ê²½ì°, 모ë ììë¤ì´ <literal>saveOrUpdate()</literal>ì ì ë¬ëë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ë§ì¼ transient ëë detached ììì´ ìì ë¶ëª¨ì ìí´ ì°¸ì¡°ë ê²½ì°, ê·¸ê²ì <literal>saveOrUpdate()</literal>ì ì ë¬ëë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ë§ì¼ ë¶ëª¨ê° ìì ë ê²½ì°, 모ë ììë¤ì´ <literal>delete()</literal>ì ì ë¬ëë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ë§ì¼ ììì´ ìì ë¶ëª¨ì ìí´ ì°¸ì¡° í´ì ë ê²½ì°, <literal>cascade="delete-orphan"</literal>ì´ ìë í,
+ <emphasis>í¹ë³í ì´ë¤ ê²ë ë°ìíì§ ìëë¤</emphasis> - ì´í리ì¼ì´ì
ì íìí ê²½ì°ì ììì ëª
ìì ì¼ë¡ ìì í´ì¼ íë¤ -,
+ <literal>cascade="delete-orphan"</literal>ì¸ ê²½ì°ì "orphaned(ê³ ì)"ì¸ ê²½ì° ììì´ ìì ëë¤.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect1>
+
+ <para>
+ ë§ì§ë§ì¼ë¡ ì¤í¼ë ì´ì
ë¤ì ì¼ì¤ì¼ì´ë©ì´ <emphasis>í¸ì¶ ìì </emphasis>ìì ëë <emphasis>flush ìì </emphasis>ìì
+ ê°ì²´ ê·¸ëíì ì ì©ë ì ììì ë
¸í¸íë¼. ì´ì© ê°ë¥í ê²½ì°ì 모ë ì¤í¼ë ì´ì
ë¤ì ê·¸ ì¤í¼ë ì´ì
ì´ ì¤íë ë ëë¬ ê°ë¥í ì°ê´ë
+ ìí°í°ë¤ì ëí´ ì¼ì¤ì¼ì´ë ëë¤. íì§ë§ <literal>save-upate</literal>ì <literal>delete-orphan</literal>ì
+ <literal>Session</literal>ì flush ëìì ëë¬ ê°ë¥í 모ë ì°ê´ë ìí°í°ë¤ì ëí´ ì´íì (transitive)ì´ë¤.
+ </para>
+
+ <sect1 id="objectstate-metadata">
+ <title>ë©íë°ì´í° ì¬ì©í기</title>
+
+ <para>
+ Hibernateë 모ë ìí°í°ì ê°(value) íì
ë¤ì ê°ì§ ë§¤ì° íë¶í ë©í-ë 벨 모íì íìë¡ íë¤. ìê°ì´ ì§ë¨ì ë°ë¼, ì´ ëª¨íì
+ ì´í리ì¼ì´ì
ê·¸ ìì²´ì ë§¤ì° ì ì©íë¤. ì를 ë¤ì´, ì´í리ì¼ì´ì
ì ì´ë ê°ì²´ë¤ì´ ë³µì¬ëì´ì¼ íëì§(ì를 ë¤ë©´ ê°ë³ì ì¸ ê°(value) íì
ë¤)
+ ê·¸ë¦¬ê³ ì´ë ê²ì´ ë³µì¬ëì§ ë§ìì¼ íëì§(ì를 ë¤ë©´, ë¶ë³ì value íì
ë¤ê³¼ ê°ë¥í ì°ê´ë ìí°í°ë¤)를 ì¸ì§íë "ì¤ë§í¸" deep-copy
+ ìê³ ë¦¬ì¦ì 구ííëë° Hibernateì ë©íë°ì´í°ë¥¼ ì¬ì©í ìë ìë¤.
+ </para>
+ <para>
+ Hibernateë <literal>ClassMetadata</literal> ì¸í°íì´ì¤ì <literal>CollectionMetadata</literal> ì¸í°íì´ì¤
+ ê·¸ë¦¬ê³ <literal>Type</literal> ê³ì¸µêµ¬ì¡°ë¥¼ íµí´ ë©íë°ì´í°ë¥¼ ë
¸ì¶ìí¨ë¤. ë©íë°ì´í° ì¸í°íì´ì¤ë¤ì ì¸ì¤í´ì¤ë¤ì
+ <literal>SessionFactory</literal>ë¡ë¶í° ì»ì´ì§ ìë ìë¤.
+ </para>
+
+ <programlisting><![CDATA[Cat fritz = ......;
+ClassMetadata catMeta = sessionfactory.getClassMetadata(Cat.class);
+
+Object[] propertyValues = catMeta.getPropertyValues(fritz);
+String[] propertyNames = catMeta.getPropertyNames();
+Type[] propertyTypes = catMeta.getPropertyTypes();
+
+// get a Map of all properties which are not collections or associations
+Map namedValues = new HashMap();
+for ( int i=0; i<propertyNames.length; i++ ) {
+ if ( !propertyTypes[i].isEntityType() && !propertyTypes[i].isCollectionType() ) {
+ namedValues.put( propertyNames[i], propertyValues[i] );
+ }
+}]]></programlisting>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/toolset_guide.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/toolset_guide.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/toolset_guide.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,588 @@
+<chapter id="toolsetguide" revision="2">
+ <title>ë구ì
ìë´</title>
+
+ <para>
+ Hibernateì ëí ë¼ì´ëí¸ë¦½ ìì§ëì´ë§ì Eclipse íë¬ê·¸ì¸ ì¸í¸, ëª
ë ¹ë¼ì¸ ë구ë¤, ë¿ë§ ìëë¼ Ant íì¤í¬ë¤ì ì¬ì©íì¬ ê°ë¥íë¤.
+ </para>
+
+ <para>
+ <emphasis>Hibernate ë구ë¤</emphasis>ì íì¬ ê¸°ì¡´ ë°ì´í°ë² ì´ì¤ë¤ì ëí 리ë²ì¤ ìì§ëì´ë§ì ìí´ Ant íì¤í¬ë¤ ë¿ë§ ìëë¼
+ Eclipse IDEì© íë¬ê·¸ì¸ë¤ì í¬í¨íê³ ìë¤:
+ </para>
+
+ <itemizedlist>
+ <listitem><para>
+ <emphasis>매í í¸ì§ê¸°:</emphasis> ìë ìì± ê¸°ë¥ê³¼ 구문 ê°ì¡°ë¥¼ ì§ìíë Hibernate XML 매í íì¼ë¤ì ëí í¸ì§ê¸°. ê·¸ê²ì ëí
+ íµìì XML í¸ì§ê¸° ë³´ë¤ í¨ì¬ ë ìµíµì± ìê² ë§ë¤ì´ì í´ëì¤ ì´ë¦ë¤ê³¼ íë¡í¼í°/íë ì´ë¦ë¤ì ëí ìë¯¸ë¡ ì ìë ìì± ê¸°ë¥ì ì§ìíë¤.
+ </para></listitem>
+ <listitem><para>
+ <emphasis>ì½ì:</emphasis> ì½ìì Eclipseìì ìë¡ì´ ë·°ì´ë¤. ë¹ì ì ì½ì 구ì±ë¤ì ëí tree overviewì ë§ë¶ì¬, ë¹ì ì ëí
+ ë¹ì ì ìì í´ëì¤ë¤ê³¼ ê·¸ê²ë¤ì ê´ê³ë¤ì ëí ìí¸ìì© ë·°ë¥¼ ì»ëë¤. ì½ìì ë¹ì ì ë°ì´í°ë² ì´ì¤ì ëí´ HQL ì§ìë¤ì ì¤ííê³ ê·¸ 결과를
+ Eclipse ë´ìì ì§ì ë¸ë¼ì°ì§ íëë¡ ë¹ì ìê² íì©í´ì¤ë¤.
+ </para></listitem>
+ <listitem><para>
+ <emphasis>ê°ë° ë§ë²ì¬ë¤:</emphasis> ëªëª ë§ë²ì¬ë¤ì´ Hibernate Eclipse ë구ë¤ì ì ê³µëë¤; ë¹ì ì Hibernate 구ì±
+ (cfg.xml) íì¼ë¤ì ë¹ ë¥´ê² ìì±ìí¤ëë° ë§ë²ì¬ë¥¼ ì¬ì©íê±°ë, ì¬ì§ì´ ë¹ì ì 기존 ë°ì´í°ë² ì´ì¤ ì¤í¤ë§ë¥¼ POJO ìì¤ íì¼ë¤ê³¼
+ Hibernate 매í íì¼ë¤ë¡ ìì íê² ë¦¬ë²ì¤ ìì§ëì´ë§í ìë ìë¤. 리ë²ì¤ ìì§ëì´ë§ ë§ë²ì¬ë ë§ì¶¤ ê°ë¥í í
í릿ë¤ì ì ê³µíë¤.
+ </para></listitem>
+ <listitem><para>
+ <emphasis>Ant íì¤í¬ë¤:</emphasis>
+ </para></listitem>
+
+ </itemizedlist>
+
+ <para>
+ ì¶ê° ì ë³´ë <emphasis>Hibernate Tools</emphasis> í¨í¤ì§ì ê·¸ê²ì 문ì를 참조í길 ë°ëë¤.
+ </para>
+
+ <para>
+ íì§ë§ Hibernate ë©ì¸ í¨í¤ì§ë íµí© ë구ì ë²ë¤í ëì´ ìë¤(ê·¸ê²ì ì¬ì§ì´ íë¼ì´ ììì Hibernate "ë´ìì" ì¬ì©ë ì ìë¤):
+ <emphasis>SchemaExport</emphasis> ë³ì¹ì <literal>hbm2ddl</literal>.
+ </para>
+
+ <sect1 id="toolsetguide-s1" revision="2">
+ <title>ìëì ì¸ ì¤í¤ë§ ìì±</title>
+
+ <para>
+ DDLì Hibernate ì í¸ë¦¬í°ì ìí´ ë¹ì ì 매í íì¼ë¤ë¡ë¶í° ìì±ë ì ìë¤. ìì±ë ì¤í¤ë§ë ìí°í° í
ì´ë¸ê³¼ ì½ë ì
í
ì´ë¸ì ëí
+ 참조 ë¬´ê²°ì± ì»¨ì¤í¸ë ì¸í¸ë¤(íë¼ì´ë¨¸ë¦¬ í¤ì foreign í¤ë¤)ì í¬í¨íë¤. í
ì´ë¸ë¤ê³¼ ìíì¤ë¤ì ëí íì¹ë ìë³ì ìì±ê¸°ë¤ì ëí´
+ ìì±ëë¤.
+ </para>
+
+ <para>
+ DDLì´ ë§¤ì° ë²¤ëì í¹ì íë¯ë¡, ì´ ë구를 ì¬ì©í ë ë¹ì ì <literal>hibernate.dialect</literal> íë¡í¼í°ë¥¼ íµí´ í ê°ì SQL
+ <literal>Dialect</literal>를 ì§ì <emphasis>í´ì¼ íë¤</emphasis>.
+ </para>
+
+ <para>
+ 먼ì ìì±ë ì¤í¤ë§ë¥¼ ê°ì ìí¤ê¸° ìí´ ë¹ì ì 매í íì¼ë¤ì ë§ì¶¤í ìì¼ë¼.
+ </para>
+
+ <sect2 id="toolsetguide-s1-2" revision="3">
+ <title>ì¤í¤ë§ ë§ì¶¤í ìí¤ê¸°</title>
+
+ <para>
+ ë§ì Hibernate 매í ììë¤ì <literal>length</literal>ë¡ ëª
ëª
ë ìµì
ìì±ì ì ìíë¤. ë¹ì ì ì´ ìì±ì¼ë¡ 컬ë¼ì 길ì´ë¥¼
+ ì¤ì í ì ìë¤.(ëë numeric/decimal ë°ì´í° íì
ë¤, ë°°ì ëì ëí´ ).
+ ë§ì Hibernate ë§¤ì¹ ììë¤ì <literal>length</literal>, <literal>precision</literal> 그리ê³
+ <literal>scale</literal>ë¡ ëª
ëª
ë ìµì
ìì±ë¤ì ì ìíê³ ìë¤. ë¹ì ì ì´ ìì±ì¼ë¡ì 컬ë¼ì´ 길ì´, ì ë°ë, í¬ê¸°ë¥¼ ì¤ì í ì ìë¤.
+
+ </para>
+
+ <programlisting><![CDATA[<property name="zip" length="5"/>]]></programlisting>
+ <programlisting><![CDATA[<property name="balance" precision="12" scale="2"/>]]></programlisting>
+
+ <para>
+ ëªëª íê·¸ë¤ì ëí (í
ì´ë¸ 컬ë¼ë¤ì ëí <literal>NOT NULL</literal> 컨ì¤í¸ë ì¸í¸ë¥¼ ìì±ìí¤ë) <literal>not-null</literal>
+ ìì±ê³¼ (í
ì´ë¸ 컬ë¼ë¤ì ëí <literal>UNIQUE</literal> 컨ì¤í¸ë ì¸í¸ë¥¼ ìì±ìí¤ë) <literal>unique</literal> ìì±ì ìì©íë¤.
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="bar" column="barId" not-null="true"/>]]></programlisting>
+
+ <programlisting><![CDATA[<element column="serialNumber" type="long" not-null="true" unique="true"/>]]></programlisting>
+
+ <para>
+ <literal>unique-key</literal> ìì±ì íëì ì ì¼ í¤ ì»¨ì¤í¸ë ì¸í¸ë¡ 컬ë¼ë¤ì 그룹ì§ì°ëë° ì¬ì©ë ìë ìë¤. íì¬
+ <literal>unique-key</literal> ìì±ì ì§ì ë ê°ì ì°ì¶ë DDLë´ìì 컨ì¤í¸ë ì¸í¸ë¥¼ ëª
ëª
íëë° ì¬ì©ëì§ <emphasis>ìê³ </emphasis>,
+ ì¤ì§ 매í íì¼ ë´ìì 컬ë¼ë¤ì 그룹 ì§ì°ëë° ì¬ì©ëë¤.
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="org" column="orgId" unique-key="OrgEmployeeId"/>
+<property name="employeeId" unique-key="OrgEmployee"/>]]></programlisting>
+
+
+ <para>
+ <literal>index</literal> ìì±ì 매íë ì»¬ë¼ ëë 컬ë¼ë¤ì ì¬ì©íì¬ ìì±ë ì¸ë±ì¤ì ì´ë¦ì ì§ì íë¤. ê°ë¨íê²
+ ëì¼í ì¸ë±ì¤ ì´ë¦ì ì§ì í¨ì¼ë¡ì¨ ì¬ë¬ 컬ë¼ë¤ì´ ëì¼í ì¸ë±ì¤ ë´ë¡ 그룹ì§ìì§ ìë ìë¤.
+ </para>
+
+ <programlisting><![CDATA[<property name="lastName" index="CustName"/>
+<property name="firstName" index="CustName"/>]]></programlisting>
+
+ <para>
+ <literal>foreign-key</literal> ìì±ì ììì ì°ì¶ë foreign í¤ ì»¨ì¤í¸ë ì¸í¸ì ì´ë¦ì
+ ì¤ë²ë¼ì´ë ìí¤ëë° ì¬ì©ë ì ë ìë¤.
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="bar" column="barId" foreign-key="FKFooBar"/>]]></programlisting>
+
+ <para>
+ ë§ì 매í ììë¤ì ëí íëì ìì <literal><column></literal> ìì를 íì©íë¤.
+ ì´ê²ì í¹í ë¤ì¤ ì»¬ë¼ íì
ë¤ì 매ííëë° ì ì©íë¤:
+ </para>
+
+ <programlisting><![CDATA[<property name="name" type="my.customtypes.Name"/>
+ <column name="last" not-null="true" index="bar_idx" length="30"/>
+ <column name="first" not-null="true" index="bar_idx" length="20"/>
+ <column name="initial"/>
+</property>]]></programlisting>
+
+ <para>
+ ë¤ë¥¸ ë°©ë²ì¼ë¡, ì´ë¤ ììë¤ì ëí ìì <literal><column></literal> ìì를 ìì©íë¤. ì´ê²ì ë¤ì¤ ì»¬ë¼ íì
ë¤ì
+ í¹í ì ì©íë¤:
+ <literal>default</literal> ìì±ì ë¹ì ì¼ë¡ íì¬ê¸ íëì 컬ë¼ì ëí ëí´í¸ ê°ì ì¤ì íëë¡ íë¤(ë¹ì ì 매íë
+ í´ëì¤ì ìë¡ì´ ì¸ì¤í´ì¤ë¥¼ ì ì¥í기 ì ì 매íë íë¡í¼í°ì ëì¼í ê°ì í ë¹íê² ë ê²ì´ë¤).
+ </para>
+
+ <programlisting><![CDATA[<property name="credits" type="integer" insert="false">
+ <column name="credits" default="10"/>
+</property>]]></programlisting>
+
+ <programlisting><![CDATA[<version name="version" type="integer" insert="false">
+ <column name="version" default="0"/>
+</property>]]></programlisting>
+
+ <para>
+ <literal>sql-type</literal> ìì±ì SQL ë°ì´í°íì
ì ëí Hibernate íì
ì ëí´í¸ 매íì ì¤ë²ë¼ì´ë ìí¤ë ê²ì
+ ì¬ì©ììê² íì©í´ì¤ë¤.
+ </para>
+
+ <programlisting><![CDATA[<property name="balance" type="float">
+ <column name="balance" sql-type="decimal(13,3)"/>
+</property>]]></programlisting>
+
+ <para>
+ <literal>check</literal> ìì±ì check 컨ì¤í¸ë ì¸í¸ë¥¼ ì§ì íë ê²ì ë¹ì ìê² íì©í´ì¤ë¤.
+ </para>
+
+ <programlisting><![CDATA[<property name="foo" type="integer">
+ <column name="foo" check="foo > 10"/>
+</property>]]></programlisting>
+
+ <programlisting><![CDATA[<class name="Foo" table="foos" check="bar < 100.0">
+ ...
+ <property name="bar" type="float"/>
+</class>]]></programlisting>
+
+
+ <table frame="topbot" id="schemattributes-summary" revision="2">
+ <title>ìì½</title>
+ <tgroup cols="3">
+ <colspec colwidth="1*"/>
+ <colspec colwidth="1*"/>
+ <colspec colwidth="2.5*"/>
+ <thead>
+ <row>
+ <entry>ìì±</entry>
+ <entry>ê°ë¤</entry>
+ <entry>í´ì</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>length</literal></entry>
+ <entry>number</entry>
+ <entry>ì»¬ë¼ ê¸¸ì´</entry>
+ </row>
+ <row>
+ <entry><literal>precision</literal></entry>
+ <entry>number</entry>
+ <entry>ì»¬ë¼ decimal ì ë°ë</entry>
+ </row>
+ <row>
+ <entry><literal>scale</literal></entry>
+ <entry>number</entry>
+ <entry>ì»¬ë¼ decimal í¬ê¸°</entry>
+ </row>
+ <row>
+ <entry><literal>not-null</literal></entry>
+ <entry><literal>true|false</literal></entry>
+ <entry>컬ë¼ì´ nullì´ ìëì´ì¼ í¨ì ì§ì íë¤</entry>
+ </row>
+ <row>
+ <entry><literal>unique</literal></entry>
+ <entry><literal>true|false</literal></entry>
+ <entry>컬ë¼ì´ íëì ì ì¼ ì»¨ì¤í¸ë ì¸í¸ë¥¼ ê°ì ¸ì¼í¨ì ì§ì íë¤</entry>
+ </row>
+ <row>
+ <entry><literal>index</literal></entry>
+ <entry><literal>index_name</literal></entry>
+ <entry>(ë¤ì¤-컬ë¼) ì¸ë±ì¤ì ì´ë¦ì ì§ì íë¤ </entry>
+ </row>
+ <row>
+ <entry><literal>unique-key</literal></entry>
+ <entry><literal>unique_key_name</literal></entry>
+ <entry>ë¤ì¤-ì»¬ë¼ ì ì¼ ì»¨ì¤í¸ë ì¸í¸ì ì´ë¦ì ì§ì íë¤</entry>
+ </row>
+ <row>
+ <entry><literal>foreign-key</literal></entry>
+ <entry><literal>foreign_key_name</literal></entry>
+ <entry>
+ íëì ì°ê´ì ëí´, <literal><one-to-one></literal>,
+ <literal><many-to-one></literal>,
+ <literal><key></literal>, ëë <literal><many-to-many></literal>
+ 매í ììì ëí´ foreign key 컨ì¤í¸ë ì¸í¸ì ì´ë¦ì ì§ì íë¤.<literal>inverse="true"</literal>
+ 측ë¤ì <literal>SchemaExport</literal>ì ìí´ ê³ ë ¤ëì§ ìì ê²ìì ë
¸í¸íë¼.
+ </entry>
+ </row>
+ <row>
+ <entry><literal>sql-type</literal></entry>
+ <entry><literal>SQL column type</literal></entry>
+ <entry>
+ ëí´í¸ ì»¬ë¼ íì
ì ì¤ë²ë¼ì´ë ìí¨ë¤
+ (<literal><column></literal> ììì ìì±ìë§)
+ </entry>
+ </row>
+ <row>
+ <entry><literal>default</literal></entry>
+ <entry>SQL expression</entry>
+ <entry>
+ 컬ë¼ì ëí ëí´í¸ ê°ì ì§ì íë¤
+ </entry>
+ </row>
+ <row>
+ <entry><literal>check</literal></entry>
+ <entry>SQL expression</entry>
+ <entry>
+ ì»¬ë¼ ëë í
ì´ë¸ì ëí SQL check 컨ì¤í¸ë ì¸í¸ë¥¼ ìì±ìí¨ë¤
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ <literal><comment></literal> ììë ìì±ë ì¤í¤ë§ì ëí 주ìë¤ì ì§ì íë ê²ì ë¹ì ìê² íì©í´ì¤ë¤.
+ </para>
+
+ <programlisting><![CDATA[<class name="Customer" table="CurCust">
+ <comment>Current customers only</comment>
+ ...
+</class>]]></programlisting>
+
+ <programlisting><![CDATA[<property name="balance">
+ <column name="bal">
+ <comment>Balance in USD</comment>
+ </column>
+</property>]]></programlisting>
+
+ <para>
+ ì´ê²ì (ì§ìëë ê²½ì°) ìì±ë DDLìì <literal>comment on table</literal> ëë
+ <literal>comment on column</literal> 문ì¥ì¼ë¡ ê·ê²°ëë¤.
+ </para>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-3" revision="2">
+ <title>ë구 ì¤íí기</title>
+
+ <para>
+ <literal>SchemaExport</literal> ë구ë DDL ì¤í¬ë¦½í¸ë¥¼ íì¤ ì¶ë ¥ì¼ë¡ ê¸°ë¡ íê³ /íê±°ë DDL 문ì¥ë¤ì ì¤íìí¨ë¤.
+ </para>
+
+ <para>
+ <literal>java -cp </literal><emphasis>hibernate_classpaths</emphasis>
+ <literal>org.hibernate.tool.hbm2ddl.SchemaExport</literal> <emphasis>options mapping_files</emphasis>
+ </para>
+
+ <table frame="topbot">
+ <title><literal>SchemaExport</literal> ëª
ë ¹ ë¼ì¸ ìµì
ë¤</title>
+ <tgroup cols="2">
+ <colspec colwidth="1.5*"/>
+ <colspec colwidth="2*"/>
+ <thead>
+ <row>
+ <entry>ìµì
</entry>
+ <entry>ì¤ëª
</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>--quiet</literal></entry>
+ <entry>ì¤í¬ë¦½í¸ë¥¼ íì¤ì¶ë ¥ì¼ë¡ ì¶ë ¥íì§ ìëë¤</entry>
+ </row>
+ <row>
+ <entry><literal>--drop</literal></entry>
+ <entry>ì¤ì§ í
ì´ë¸ë¤ì ëë¡ìí¨ë¤</entry>
+ </row>
+ <row>
+ <entry><literal>--create</literal></entry>
+ <entry>ì¤ì§ í
ì´ë¸ë¤ì ìì±ìí¨ë¤</entry>
+ </row>
+ <row>
+ <entry><literal>--text</literal></entry>
+ <entry>ë°ì´í°ë² ì´ì¤ë¡ ë´ë³´ë´ê¸° íì§ ìëë¤</entry>
+ </row>
+ <row>
+ <entry><literal>--output=my_schema.ddl</literal></entry>
+ <entry>ddl ì¤í¬ë¦½í¸ë¥¼ íì¼ë¡ ì¶ë ¥íë¤</entry>
+ </row>
+ <row>
+ <entry><literal>--naming=eg.MyNamingStrategy</literal></entry>
+ <entry>íëì <literal>NamingStrategy</literal>를 ì ííë¤</entry>
+ </row>
+ <row>
+ <entry><literal>--config=hibernate.cfg.xml</literal></entry>
+ <entry>XML íì¼ë¡ë¶í° Hibernate 구ì±ì ì½ì´ë¤ì¸ë¤</entry>
+ </row>
+ <row>
+ <entry><literal>--properties=hibernate.properties</literal></entry>
+ <entry>íì¼ë¡ë¶í° ë°ì´í°ë² ì´ì¤ íë¡í¼í°ë¤ì ì½ì´ë¤ì¸ë¤</entry>
+ </row>
+ <row>
+ <entry><literal>--format</literal></entry>
+ <entry>ìì±ë SQLì ì¤í¬ë¦½í¸ ë´ì ì¢ê² íìì§ì´ë¤</entry>
+ </row>
+ <row>
+ <entry><literal>--delimiter=;</literal></entry>
+ <entry>ì¤í¬ë¦½í¸ë¥¼ ìí ë¼ì¸ ê²½ê³ì ëì ì¤ì íë¤ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ ë¹ì ì ë¹ì ì ì´í리ì¼ì´ì
ë´ì <literal>SchemaExport</literal>를 ì½ì
ìí¬ ìë ìë¤:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = ....;
+new SchemaExport(cfg).create(false, true);]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-4">
+ <title>íë¡í¼í°ë¤</title>
+
+ <para>
+ ë°ì´í°ë² ì´ì¤ íë¡í¼í°ë¤ì ë¤ìê³¼ ê°ì´ ì§ì ë ì ìë¤
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para><literal>-D</literal><emphasis><property></emphasis>를 ê°ì§ ìì¤í
íë¡í¼í°ë¡ì</para>
+ </listitem>
+ <listitem>
+ <para><literal>hibernate.properties</literal> ë´ìì</para>
+ </listitem>
+ <listitem>
+ <para><literal>--properties</literal>를 ê°ì§ ëª
ëª
ë íë¡í¼í°ë¤ ë´ìì</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ íìí íë¡í¼í°ë¤ì ë¤ìê³¼ ê°ë¤:
+ </para>
+
+ <table frame="topbot">
+ <title>SchemaExport 커ë¥ì
íë¡í¼í°ë¤</title>
+ <tgroup cols="2">
+ <colspec colwidth="1.5*"/>
+ <colspec colwidth="2*"/>
+ <thead>
+ <row>
+ <entry>íë¡í¼í° ì´ë¦</entry>
+ <entry>ì¤ëª
</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>hibernate.connection.driver_class</literal></entry>
+ <entry>jdbc ëë¼ì´ë² í´ëì¤</entry>
+ </row>
+ <row>
+ <entry><literal>hibernate.connection.url</literal></entry>
+ <entry>jdbc url</entry>
+ </row>
+ <row>
+ <entry><literal>hibernate.connection.username</literal></entry>
+ <entry>ë°ì´í°ë² ì´ì¤ ì¬ì©ì</entry>
+ </row>
+ <row>
+ <entry><literal>hibernate.connection.password</literal></entry>
+ <entry>ì¬ì©ì í¨ì¤ìë</entry>
+ </row>
+ <row>
+ <entry><literal>hibernate.dialect</literal></entry>
+ <entry>dialect</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-5">
+ <title>Ant ì¬ì©í기</title>
+
+ <para>
+ ë¹ì ì ë¹ì ì Ant ë¹ë ì¤í¬ë¦½í¸ìì <literal>SchemaExport</literal>를 í¸ì¶í ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[<target name="schemaexport">
+ <taskdef name="schemaexport"
+ classname="org.hibernate.tool.hbm2ddl.SchemaExportTask"
+ classpathref="class.path"/>
+
+ <schemaexport
+ properties="hibernate.properties"
+ quiet="no"
+ text="no"
+ drop="no"
+ delimiter=";"
+ output="schema-export.sql">
+ <fileset dir="src">
+ <include name="**/*.hbm.xml"/>
+ </fileset>
+ </schemaexport>
+</target>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-6" revision="2">
+ <title>ì ì¦íë ì¤í¤ë§ ì
ë°ì´í¸ë¤</title>
+
+ <para>
+ <literal>SchemaUpdate</literal> ë구ë "ì ì¦íë" ë³ê²½ë¤ì ê°ì§ 기존 ì¤í¤ë§ë¥¼ ë³ê²½ìí¬ ê²ì´ë¤. <literal>SchemaUpdate</literal>ë
+ JDBC ë©íë°ì´í° APIì 무ê²ê² ìì¡´íì¬ì, ê·¸ê²ì 모ë JDBC ëë¼ì´ë²ë¤ì ëìíì§ ìì ê²ìì 주목íë¼.
+ </para>
+
+ <para>
+ <literal>java -cp </literal><emphasis>hibernate_classpaths</emphasis>
+ <literal>org.hibernate.tool.hbm2ddl.SchemaUpdate</literal> <emphasis>options mapping_files</emphasis>
+ </para>
+
+ <table frame="topbot">
+ <title><literal>SchemaUpdate</literal> ëª
ë ¹ ë¼ì¸ ìµì
ë¤</title>
+ <tgroup cols="2">
+ <colspec colwidth="1.5*"/>
+ <colspec colwidth="2*"/>
+ <thead>
+ <row>
+ <entry>ìµì
</entry>
+ <entry>ì¤ëª
</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>--quiet</literal></entry>
+ <entry>ì¤í¬ë¦½í¸ë¥¼ íì¤ì¶ë ¥ì¼ë¡ ì¶ë ¥íì§ ìëë¤</entry>
+ </row>
+ <row>
+ <entry><literal>--text</literal></entry>
+ <entry>ì¤í¬ë¦½í¸ë¥¼ ë°ì´í°ë² ì´ì¤ë¡ ë´ë³´ë´ê¸° íì§ ìëë¤</entry>
+ </row>
+ <row>
+ <entry><literal>--naming=eg.MyNamingStrategy</literal></entry>
+ <entry>íëì <literal>NamingStrategy</literal>를 ì ííë¤</entry>
+ </row>
+ <row>
+ <entry><literal>--properties=hibernate.properties</literal></entry>
+ <entry>íì¼ë¡ë¶í° ë°ì´í°ë² ì´ì¤ íë¡í¼í°ë¤ì ì½ì´ ë¤ì¸ë¤</entry>
+ </row>
+ <row>
+ <entry><literal>--config=hibernate.cfg.xml</literal></entry>
+ <entry><literal>.cfg.xml</literal> íì¼ì ì§ì íë¤</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ ë¹ì ì ë¹ì ì ì´í리ì¼ì´ì
ë´ì <literal>SchemaUpdate</literal>를 ì½ì
ìí¬ ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = ....;
+new SchemaUpdate(cfg).execute(false);]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-7">
+ <title>ì ì¦íë ì¤í¤ë§ ì
ë°ì´í¸ë¤ì Ant ì¬ì©í기</title>
+
+ <para>
+ ë¹ì ì Ant ì¤í¬ë¦½í¸ìì<literal>SchemaUpdate</literal>를 í¸ì¶í ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[<target name="schemaupdate">
+ <taskdef name="schemaupdate"
+ classname="org.hibernate.tool.hbm2ddl.SchemaUpdateTask"
+ classpathref="class.path"/>
+
+ <schemaupdate
+ properties="hibernate.properties"
+ quiet="no">
+ <fileset dir="src">
+ <include name="**/*.hbm.xml"/>
+ </fileset>
+ </schemaupdate>
+</target>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-8" revision="1">
+ <title>ì¤í¤ë§ ì í¨ì± ê²ì¬</title>
+
+ <para>
+ <literal>SchemaValidator</literal> ë구ë 기존ì ë°ì´í°ë² ì´ì¤ ì¤í¤ë§ê° ë¹ì ì 매í 문ìë¤ê³¼ "ì¼ì¹íëì§"를 ì í¨ì±
+ ê²ì¬í ê²ì´ë¤. <literal>SchemaValidator</literal>ê° JDBC ë©íë°ì´í° APIì 무ê²ê² ìì¡´íë¯ë¡ ê·¸ê²ì 모ë
+ JDBC ëë¼ì´ë²ë¤ì ëí´ ëìíì§ ìì ê²ì´ë¤. ì´ ë구ë í
ì¤í
ì ê·¹í ì ì©íë¤.
+ </para>
+
+ <para>
+ <literal>java -cp </literal><emphasis>hibernate_classpaths</emphasis>
+ <literal>org.hibernate.tool.hbm2ddl.SchemaValidator</literal> <emphasis>options mapping_files</emphasis>
+ </para>
+
+ <table frame="topbot">
+ <title><literal>SchemaValidator</literal> ëª
ë ¹ ë¼ì¸ ìµì
ë¤</title>
+ <tgroup cols="2">
+ <colspec colwidth="1.5*"/>
+ <colspec colwidth="2*"/>
+ <thead>
+ <row>
+ <entry>ìµì
</entry>
+ <entry>ì¤ëª
</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>--naming=eg.MyNamingStrategy</literal></entry>
+ <entry>íëì <literal>NamingStrategy</literal>를 ì ííë¤</entry>
+ </row>
+ <row>
+ <entry><literal>--properties=hibernate.properties</literal></entry>
+ <entry>íì¼ë¡ë¶í° ë°ì´í°ë² ì´ì¤ íë¡í¼í°ë¤ì ì½ì´ë¤ì¸ë¤</entry>
+ </row>
+ <row>
+ <entry><literal>--config=hibernate.cfg.xml</literal></entry>
+ <entry><literal>.cfg.xml</literal> íì¼ì ì§ì íë¤</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ ë¹ì ì ë¹ì ì ì´í리ì¼ì´ì
ë´ì <literal>SchemaValidator</literal>ì ì½ì
ìí¬ ì ë ìë¤:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = ....;
+new SchemaValidator(cfg).validate();]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-9">
+ <title>ì¤í¤ë§ ì í¨ì± ê²ì¬ë¥¼ ìí´ Ant ì¬ì©í기</title>
+
+ <para>
+ ë¹ì ì Ant ì¤í¬ë¦½í¸ìì <literal>SchemaValidator</literal>를 í¸ì¶í ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[<target name="schemavalidate">
+ <taskdef name="schemavalidator"
+ classname="org.hibernate.tool.hbm2ddl.SchemaValidatorTask"
+ classpathref="class.path"/>
+
+ <schemavalidator
+ properties="hibernate.properties">
+ <fileset dir="src">
+ <include name="**/*.hbm.xml"/>
+ </fileset>
+ </schemaupdate>
+</target>]]></programlisting>
+
+ </sect2>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/transactions.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/transactions.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/transactions.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1004 @@
+<chapter id="transactions" revision="2">
+ <title>í¸ëìì
ë¤ê³¼ ëìì±</title>
+
+ <para>
+ Hibernateì ëìì± ì ì´ì ëí ê°ì¥ ì¤ìí ì ì ì´í´íê¸°ê° ë§¤ì° ì½ë¤ë ì ì´ë¤. Hibernateë ì´ë¤ ì¶ê°ì ì¸ ì ê¸ íì ìì´
+ JDBC 커ë¥ì
ë¤ê³¼ JTA 리ìì¤ë¤ì ì§ì ì¬ì©íë¤. ì°ë¦¬ë ë¹ì ì ë°ì´í°ë² ì´ì¤ ê´ë¦¬ ìì¤í
ì JDBC, ANSI, ê·¸ë¦¬ê³ í¸ëìì
격리 ëª
ì¸ì
+ ì½ê°ì ìê°ì í ì í ê²ì ë§¤ì° ê¶ì¥íë¤.
+ </para>
+
+ <para>
+ Hibernateë ë©ëª¨ë¦¬ ë´ìì ê°ì²´ë¤ì ì ê·¸ì§ ìëë¤. ë¹ì ì ì´í리ì¼ì´ì
ì 격리 ë 벨ì ìí´ ì ìë ëë¡ íì를 기ëí ì ìë¤.
+ ëí transaction-ììì ìºìì¸ <literal>Session</literal> ëë¶ì, Hibernateë (ì¤ì¹¼ë¼ ê°ë¤ì ë°ííë ì§ìë¤ì ë³´ê³ íì§ ìë)
+ ìë³ìì ìí°í° ì§ìë¤ì ìí 룩ì
ì ìí´ ë°ë³µ ê°ë¥í ì½ê¸°ë¥¼ ì ê³µíë¤.
+ </para>
+
+ <para>
+ ìëì ì¸ optimistic ëìì± ì ì´ë¥¼ ìí ë²ì íì ë§ë¶ì¬, Hibernateë ëí <literal>SELECT FOR UPDATE</literal>
+ 구문ì ì¬ì©íì¬ íë¤ì ëí pessimistic ì ê¸ì ìí íëì (ë§ì´ë)API를 ì ê³µíë¤. optimistic ëìì± ì ì´ì ì´ APIë
+ ì´ ì¥ì ë·ë¶ë¶ìì ë
¼ìëë¤.
+ </para>
+
+ <para>
+ ì°ë¦¬ë <literal>Configuration</literal>, <literal>SessionFactory</literal>, <literal>Session</literal>,
+ ìê°±ì´ë¥¼ ê°ì§ Hibernateììì ëìì± ì ì´ ë¿ë§ ìëë¼ ë°ì´í°ë² ì´ì¤ í¸ëìì
ê³¼ ì¥ê¸°ê°ì ê±¸ì¹ (ì»´í¨í°ìì)ëíë¤ì ëí ë
¼ì를 ììíë¤.
+ </para>
+
+ <sect1 id="transactions-basics" revision="1">
+ <title>ì¸ì
ììê³¼ í¸ëìì
ìì</title>
+
+ <para>
+ <literal>SessionFactory</literal>ë 모ë ì´í리ì¼ì´ì
ì°ë ëë¤ì ìí´ ê³µì ëëë¡ ê³ ìë ìì±ì ë¹ì©ì´ ëë,
+ ì°ë ëìì (threadsafe) ê°ì²´ì´ë¤. ê·¸ê²ì ëê° ì´í리ì¼ì´ì
ìì ìì <literal>Configuration</literal> ì¸ì¤í´ì¤ë¡ë¶í°
+ íë² ìì±ëë¤.
+ </para>
+
+ <para>
+ <literal>Session</literal>ì íëì ìì², íëì ëí , íëì ìì
ë¨ì를 ìí´ íë²ë§ ì¬ì©ëê³ ëì í기ë ìì ì¸,
+ ë¹ì©ì´ ë¤ì§ ìë, ì°ë ë ìì íì§ ìì ê°ì²´ì´ë¤. <literal>Session</literal>ì 커ë¥ì
ì´ íìíì§ ìì¼ë©´ íëì JDBC
+ <literal>Connection</literal>(ëë <literal>Datasource</literal>)를 ì»ì§ ìì ê²ì´ë¯ë¡, ì¬ì©ë ëê¹ì§
+ 리ìì¤ë¤ì ìë¹íì§ ìëë¤.
+ </para>
+
+ <para>
+ ì´ ê·¸ë¦¼ì ìì±í기 ìí´ ë¹ì ì ëí ë°ì´í°ë² ì´ì¤ í¸ëì¬ì
ë¤ì ëí´ ìê°í´ì¼ íë¤. ë°ì´í°ë² ì´ì¤ í¸ëìì
ì ë°ì´í°ë² ì´ì¤ìì
+ ì ê¸ ë¤í¼ì ì¤ì´ê¸° ìí´ ê°ë¥í 짧ìì¼ íë¤. 긴 ë°ì´í°ë² ì´ì¤ í¸ëìì
ë¤ì ë¹ì ì ì´í리ì¼ì´ì
ì´ ê³ ëì ëìì± ë¡ëë¡ì ê°ì©ì±ì
+ ëì´ë ê²ì ë°©í´í ê²ì´ë¤. ê·¸ë¬ë¯ë¡ ì¬ì©ìê° ìê°íë ìê° ëì ë¨ì ìì
ì´ ìë£ë ëê¹ì§ ë°ì´í°ë² ì´ì¤ í¸ëìì
ì ì´ë ¤ì§ì±ë¡
+ ìì íë ê²ì ëê° ê²°ì½ ì¢ì ì¤ê³ë ìëë¤.
+ </para>
+
+ <para>
+ íëì ìì
ë¨ìì ììì 무ìì¸ê°? íëì Hibernate <literal>Session</literal>ì ëªëª ë°ì´í°ë² ì´ì¤ í¸ëìì
ë¤ì
+ ê±¸ì¹ ì ìëê° ëë ì´ê²ì ììë¤ì one-to-one ê´ê³ì¸ê°? ë¹ì ì ì¸ì <literal>Session</literal>ì ì´ê³ ë«ëê°
+ ê·¸ë¦¬ê³ ë¹ì ì ë°ì´í°ë² ì´ì¤ í¸ëìì
ê²½ê³ë¤ì ì´ë»ê² íì íëê°?
+ </para>
+
+ <sect2 id="transactions-basics-uow" revision="1">
+ <title>ìì
ë¨ì</title>
+
+ <para>
+ 첫ë²ì§¸ë¡, <emphasis>session-per-operation</emphasis> anti-í¨í´ì ì¬ì©íì§ ë§ë¼. ì¦, ë¨ì¼ ì°ë ë ë´ìì
+ 모ë ê°ë¨í ë°ì´í°ë² ì´ì¤ í¸ì¶ì ëí´ <literal>Session</literal>ì ì´ê³ ë«ì§ ë§ë¼! ë¬¼ë¡ ê°ì ê²ì´ ë°ì´í°ë² ì´ì¤
+ í¸ëìì
ë¤ì ëí´ìë ì°¸ì´ë¤. ì´í리ì¼ì´ì
ë´ì ë°ì´í°ë² ì´ì¤ í¸ì¶ë¤ì ê³íë ìì를ì¬ì©íì¬ íí´ì§ë©°, ê·¸ê²ë¤ì ìì
+ ìì
ë¨ì ìì¼ë¡ 그룹ì§ìì§ë¤. (ì´ê²ì ëí 모ë íëì SQL ë¬¸ì¥ ë¤ì auto-commit(ìë-커ë°)ì´ ì´í리ì¼ì´ì
ë´ìì
+ 무ì©ì§ë¬¼ìì ì미íê³ , ì´ ëª¨ëê° SQL ì½ì ìì
ì ëëë¡ ê³ ìëììì ë
¸í¸íë¼. Hibernateë
+ ì미íê³ , ì´ ëª¨ëë Hibernateë ì¦ì ìë-ì»¤ë° ëª¨ë를 ì¬ì© ë¶ê°ë¥íê² íê±°ë, ì´í리ì¼ì´ì
ìë²ê° ê·¸ë ê² ííê³ ,
+ ì¦ì ìë-커ë°ìí¤ë ê²ì ì¬ì©ë¶ê°ë¥íê² íê±°ë ,ê·¸ë ê² ííë ê²ì 기ëíë¤.) ë°ì´í°ë² ì´ì¤ í¸ëìì
ë¤ì ê²°ì½ ìµì
ì´ ìëë©°,
+ íëì ë°ì´í°ë² ì´ì¤ìì 모ë íµì ì ë¹ì ì´ ë°ì´í°ë¥¼ ì½ë ì°ë¨ê°ì ìê´ìì´ íëì í¸ëìì
ë´ìì ë°ìí´ì¼ íë¤.
+ ì¤ëª
íìë¯ì´, ë°ì´í° ì½ê¸°ë¥¼ ìí auto-commit í¹ì§ì í¼í´ì¼ í ê²ì´ë©°, ë§ì ìì í¸ëìì
ë¤ì íëì ëª
ë£íê² ì ìë
+ ìì
ë¨ìë³´ë¤ ë ì¢ê² ìíë ê² ê°ì§ ìë¤. íìê° ëí í¨ì¬ ë ì ì§ê°ë¥íê³ íì¥ì ì´ë¤.
+ </para>
+
+ <para>
+ ë¤ì¤ ì¬ì©ì í´ë¼ì´ì¸í¸/ìë² ì´í리ì¼ì´ì
ìì ê°ì¥ ê³µíµë í¨í´ì <emphasis>session-per-request</emphasis>ì´ë¤.
+ ì´ ëª¨íìì, í´ë¼ì´ì¸í¸ë¡ë¶í°ì ìì²ì (Hibernate ìì ê³ì¸µì´ ì¤íëë) ìë²ë¡ ì ì¡ëê³ , ìë¡ì´ Hibernate
+ <literal>Session</literal>ì´ ì´ë ¤ì§ê³ , 모ë ë°ì´í°ë² ì´ì¤ ì¤í¼ë ì´ì
ë¤ì´ ì´ ìì
ë¨ì ë´ìì ì¤íëë¤. ì¼ë¨ ê·¸ ìì
ì´
+ ìë£ëìë¤ë©´(ê·¸ë¦¬ê³ í´ë¼ì´ì¸í¸ì ëí ìëµì´ ì¤ë¹ëìë¤ë©´), ê·¸ ì¸ì
ì flush ëê³ ë«íì§ë¤. ë¹ì ì ëí ë¹ì ì´
+ <literal>Session</literal>ì ì´ê³ ë«ì ë ê·¸ê²ì ììíê³ ì»¤ë°ìì¼ì í´ë¼ì´ì¸í¸ ìì²ì ìë¹ì¤íëë° í ê°ì ë°ì´í°ë² ì´ì¤
+ í¸ëìì
ì ì¬ì©íê² ë ê²ì´ë¤. ë ì¬ì´ì ê´ê³ë ì¼ëì¼ ëìì´ê³ ì´ ëª¨íì ë§ì ì´í리ì¼ì´ì
ë¤ìì ìì íê² ì í©íë¤.
+ </para>
+
+ <para>
+ ëì ë 구íì ëì¬ìë¤: Hibernateë ì´ í¨í´ì ë¨ìí ìí¤ê¸° ìí´ "íì¬ ì¸ì
"ì ê´í 미리 ë¹ëë ê´ë¦¬ë¥¼ ì ê³µíë¤. ë¹ì ì´
+ íí´ì¼í 모ë ê²ì ìë² ìì²ì´ ì²ë¦¬ëì´ì¼ í ë í¸ëìì
ì ììíê³ , ê·¸ ìëµì´ í´ë¼ì´ì¸í¸ë¡ ì ì¡ë기 ì ì í¸ëìì
ì ëë´ë
+ ê²ì´ë¤. ë¹ì ì ë¹ì ì´ ì¢ìíë ììì ë°©ë²ì¼ë¡ ì´ê²ì íí ì ìì¼ë©°, ê³µíµë í´ê²°ì±
ë¤ì ìë¹ì¤ ë©ìëë¤ ìì 첨ë¨, ëë
+ íëì íë½ì/ì¸í°ì
ì
컨í
ì´ë를 ê°ì§ APO ì¸í°ì
í°ì¸, <literal>ServletFilter</literal>ì´ë¤. íëì EJB
+ 컨í
ì´ëë CMTì ê²½ì°ì ì ì¸ì ì¼ë¡ EJB session beans ìì í¸ëìì
ê²½ê³êµ¬ë¶ê³¼ ê°ì ëì ëì¡° 측면ë¤ì 구ííë
+ íì¤ ë°©ë²ì´ë¤. ë§ì¼ ë¹ì ì´ íë¡ê·¸ë¨ ìì í¸ëìì
ê²½ê³êµ¬ë¶ì ì¬ì©íê³ ì ê²°ì íë¤ë©´, ì¬ì©ì ì©ì´ì±ê³¼ ì½ë ì´ìì±ì ìí´
+ ì´ ì¥ì ë· ë¶ë¶ìì ë³´ì¬ì§ Hibernate <literal>Transaction</literal>ì ì í¸íë¼.
+ </para>
+
+ <para>
+ ë¹ì ì ì´í리ì¼ì´ì
ì½ëë ì´ëìë íìí ë ì¢
ì¢
<literal>sessionFactory.getCurrentSession()</literal>ì
+ ê°ë¨í í¸ì¶í¨ì¼ë¡ì¨ ìì²ì ì²ë¦¬í "íì¬ ì¸ì
"ì ì ê·¼í ì ìë¤. ë¹ì ì íì¬ ë°ì´í°ë² ì´ì¤ í¸ëìì
ì¼ë¡ ììíë íëì
+ <literal>Session</literal>ì íì ì»ê² ë ê²ì´ë¤. ì´ê²ì resource-local íê²½ ëë JTA íê²½ì ëí´ êµ¬ì±ëì´ì¼
+ íë©°, <xref linkend="architecture-current-session"/>ì ë³´ë¼.
+ </para>
+
+ <para>
+ ëëë¡ "ë·°ê° ë ëë§ë ëê¹ì§" íëì <literal>Session</literal> ê³¼ ë°ì´í°ë² ì´ì¤ í¸ëìì
ì ììì íì¥ìí¤ë ê²ì´
+ í¸ë¦¬íë¤. ì´ê²ì ìì²ì´ ì²ë¦¬ë íì íëì ë³ëì ë ëë§ ë¨ê³ë¥¼ íì©íë ìë¸ë¦¿ ì´í리ì¼ì´ì
ë¤ìì í¹í ì ì©íë¤.
+ ë·° ë ëë§ì´ ìë£ë ëê¹ì§ ë°ì´í°ë² ì´ì¤ í¸ëìì
ì íì¥íë ê²ì ë¹ì ì´ ë¹ì ìì ì ì¸í°ì
í°ë¥¼ 구ííë ê²½ì°ì ííê¸°ê° ì½ë¤.
+ íì§ë§ ë§ì¼ ë¹ì ì´ ì»¨í
ì´ëì ìí´ ê´ë¦¬ëë í¸ëìì
ë¤ì ê°ì§ EJBë¤ì ìì¡´í ê²½ì°ì, íëì EJB ë©ìëê° ë°íë ë
+ ììì ë·° ë ëë§ì´ ììë ì ì기 ì ì íëì í¸ëìì
ì´ ìë£ë기 ë문ì, ííë ê²ì´ ì½ì§ ìë¤. ì´
+ <emphasis>Open Session in View</emphasis> í¨í´ì ëë¬ì¼ íë¤ê³¼ ìì ë¤ì Hibernate ì¹ ì¬ì´í¸ì í¬ë¼ì ë³´ë¼.
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-basics-apptx" revision="1">
+ <title>ì¥ê¸°ê°ì ëí</title>
+
+ <para>
+ session-per-request í¨í´ì ë¹ì ì´ ìì
ë¨ìë¤ì ì¤ê³íëë° ì¬ì©í ì ìë ì ì¼í ì ì©í ê°ë
ì´ ìëë¤. ë§ì ë¹ì§ëì¤
+ íë¡ì¸ì¤ë¤ì ë°ì´í°ë² ì´ì¤ ì ê·¼ë¤ì ì¤ì¬íë ì¬ì©ì ì¬ì´ì ì ì²´ ì¼ë ¨ì ìí¸ìì©ë¤ì íìë¡ íë¤. ì¹ê³¼ ìí°íë¼ì´ì¦
+ ì´í리ì¼ì´ì
ìì ì¬ì©ì ìí¸ìì©ì 걸ì¹ë ê²ì ë°ì´í°ë² ì´ì¤ í¸ëìì
ì íì©ëì§ ìëë¤. ë¤ì ìì 를 ê²í íì:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ ëíììì 첫 ë²ì§¸ íë©´ì´ ì´ë¦¬ê³ , ì¬ì©ììê² ë³´ì¬ì§ ë°ì´í°ë í¹ì <literal>Session</literal>ê³¼ ë°ì´í°ë² ì´ì¤
+ í¸ëìì
ìì ë¡ëëìë¤. ì¬ì©ìê° ê°ì²´ë¤ì ë³ê²½ìí¤ë ê²ì´ ìì ë¡ë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+
+ ì¬ì©ìë 5ë¶ íì "ì ì¥"ì í´ë¦íê³ ê·¸ì ë³ê²½ë¤ì´ ììí ë기를 기ëíë¤; ê·¸ë ëí ê·¸ê° ì´ ì 보를 í¸ì§íë ì ì¼í
+ ê°ì¸ì´ê³ ë³ê²½ 충ëì´ ë°ìíì§ ì기를 기ëíë¤.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ì°ë¦¬ë ì¬ì©ìì ê´ì ìì, ì´ê²ì ìì
ë¨ì, ì¥ê¸°ê° ì¤íëë <emphasis>ëí</emphasis>
+ (ëë <emphasis>ì´í리ì¼ì´ì
í¸ëìì
</emphasis>)ì´ë¼ê³ ëª
ëª
íë¤.
+ ë¹ì ì´ ë¹ì ì ì´í리ì¼ì´ì
ìì ì´ê²ì ì´ë»ê² 구íí ì ìë ë§ì ë°©ë²ë¤ì´ ì¡´ì¬íë¤.
+ </para>
+
+ <para>
+ 첫 ë²ì§¸ naive 구íì ëìì± ë³ê²½ì ë°©ì§íê³ , 격리ì atomicity(ìì ë¨ìì±)ì ë³´ì¥í기 ìí´ ë°ì´í°ë² ì´ì¤ì ìí´
+ ìì ë ì ê¸ì¼ë¡ ì¬ì©ìê° ìê°íëëì <literal>Session</literal>ê³¼ ë°ì´í°ë² ì´ì¤ í¸ëìì
ì ì ì§í ìë ìë¤.
+ ì´ê²ì ë¬¼ë¡ anti-í¨í´ì´ë¤. ìëíë©´ ì ê¸ ë¤í¼ì ì´í리ì¼ì´ì
ì´ ëì ì¬ì©ìë¤ì ê°ì© ì«ì를 ëì´ë ê²ì íì©íì§
+ ìì ê²ì´ê¸° ë문ì´ë¤.
+ </para>
+
+ <para>
+ ëª
ë£íê², ì°ë¦¬ë ëí(<emphasis>ì´í리ì¼ì´ì
í¸ëìì
</emphasis>)를 구ííëë° ëªëª ë°ì´í°ë² ì´ì¤ í¸ëìì
ë¤ì
+ ì¬ì©í´ì¼ íë¤. ì´ ê²½ì°ì, ë¹ì§ëì¤ íë¡ì¸ì¤ë¤ì 격리를 ì ì§íë ê²ì ì´í리ì¼ì´ì
í°ì´ì ë¶ë¶ì ì¸ ì±
ìì´ ëë¤.
+ ë¨ì¼ ëíë ëê° ì¬ë¬ ê°ì ë°ì´í°ë² ì´ì¤ í¸ëìì
ë¤ì 걸ì¹ë¤. ê·¸ê²ì ì´ë¤ ë°ì´í°ë² ì´ì¤ í¸ëìì
ë¤ ì¤
+ ì¤ì§ í ê°(ë§ì§ë§ í¸ëìì
)ê° ì
ë°ì´í¸ë ë°ì´í°ë¥¼ ì ì¥íê³ , 모ë ë¤ë¥¸ í¸ëìì
ë¤ì´ ë¨ìí ë°ì´í°ë¥¼ ì½ë (ì를 ë¤ë©´,
+ ëªëª ìì²/ìëµ ì£¼ê¸°ì 걸ì¹ë ë§ë²ì¬ ì¤íì¼ì ëí ìììì) ê²½ì°ìë§ ììë¨ìê° ë ê²ì´ë¤. í¹í ë¹ì ì´ Hibernateì
+ í¹ì§ë¤ì ì¬ì©í ê²½ì°ì , ì´ê²ì ë¤ë¦¬ë ê²ë³´ë¤ 구ííê¸°ê° ë ì½ë¤:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>ìëì ì¸ ë²ì í</emphasis> - Hibernateë ë¹ì ì ìí´ ìëì ì¸ optimistic
+ ëìì± ì ì´ë¥¼ íí ì ìê³ , ê·¸ê²ì ì¬ì©ìê° ìê°íë ìê° ëì ëìì ì¸ ë³ê²½ì´ ë°ìíëì§ë¥¼ ìëì ì¼ë¡ ê²ì¶í ì ìë¤.
+ ëê° ì°ë¦¬ë ì¤ì§ ëíì ëìì ì²´í¬íë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Detached ê°ì²´ë¤</emphasis> - ë§ì¼ ë¹ì ì´ ì´ë¯¸ ë
¼ìë <emphasis>session-per-request</emphasis>
+ í¨í´ì ì¬ì©íê³ ì ê²°ì íë ê²½ì°, 모ë ë¡ëë ì¸ì¤í´ì¤ë¤ì ì¬ì©ìê° ìê°íë ìê° ëì detached ìíì ìì ê²ì´ë¤.
+ Hibernateë ê·¸ ê°ì²´ë¤ì ì¬ì²¨ë¶ìí¤ê³ ë³ê²½ë¤ì ììí ìí¤ë ê²ì íì©í´ì£¼ë©°, ê·¸ í¨í´ì
+ <emphasis>session-per-request-with-detached-objects(detached-ê°ì²´ë¤ì ê°ì§ ìì² ë¹ ì¸ì
)</emphasis>ì¼ë¡ ëª
ëª
ëë¤.
+ ìëì ì¸ ë²ì íë ëìì± ë³ê²½ë¤ì 격리ìí¤ëë° ì¬ì©ëë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>íì¥ë (ëë Long) Session</emphasis> - Hibernate <emphasis>Session</emphasis>ì ë°ì´í°ë² ì´ì¤
+ í¸ëìì
ì´ ì»¤ë°ë íì 기본 JDBC 커ë¥ì
ì´ ì°ê²° í´ì ë ìë ìê³ , ìë¡ì´ í´ë¼ì´ì¸í¸ ìì²ì´ ë°ìí ë ë¤ì ì°ê²°ë ì
+ ìë¤. ì´ í¨í´ì <emphasis>session-per-conversation(ëí ë¹ ì¸ì
)</emphasis>ì¼ë¡
+ ìë ¤ì ¸ ìê³ ì¬ì²¨ë¶ë¥¼ ë¶íìíê² ë§ë ë¤. ìëì ì¸ ë²ì íë ëìì± ë³ê²½ë¤ì 격리ìí¤ëë° ì¬ì©ëê³ <literal>Session</literal>ì
+ ìëì ì¼ë¡ flushëë ê²ì´ íì©ëì§ ìì§ë§ ëª
ìì ì¼ë¡ flushëë ê²ì íì©ëë¤.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ <emphasis>session-per-request-with-detached-objects</emphasis>ê³¼
+ <emphasis>session-per-conversation</emphasis> ììë ì¥ì ë¤ê³¼ ë¨ì ë¤ì ê°ëë°, ì°ë¦¬ë ì´ ì¥ì
+ ë· ë¶ë¶ìì optimistic ëìì± ì ì´ ë¨ë½ìì ê·¸ê²ë¤ì ë
¼ìíë¤.
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-basics-identity">
+ <title>ê°ì²´ identity ê³ ë ¤í기</title>
+
+ <para>
+ ì´í리ì¼ì´ì
ì ë ê°ì ë¤ë¥¸ <literal>Session</literal>ë¤ ë´ì ìë ëì¼í ìì ìíì ëìì ì ê·¼í ìë ìë¤.
+ íì§ë§ ìì í´ëì¤ì ì¸ì¤í´ì¤ë ë ê°ì <literal>Session</literal> ì¸ì¤í´ì¤ë¤ ì¬ì´ì ê²°ì½ ê³µì ëì§ ìëë¤.
+ ê·¸ë¬ë¯ë¡ identityì ëí ë ê°ì ë¤ë¥¸ ê°ë
ë¤ì´ ì¡´ì¬íë¤:
+ </para>
+
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term>ë°ì´í°ë² ì´ì¤ Identity</term>
+ <listitem>
+ <para>
+ <literal>foo.getId().equals( bar.getId() )</literal>
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>JVM Identity</term>
+ <listitem>
+ <para>
+ <literal>foo==bar</literal>
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>
+ ê·¸ë (ì를 ë¤ì´ <literal>Session</literal> ìììì) <emphasis>í¹ì </emphasis> <literal>Session</literal>ì
+ 첨ë¶ë ê°ì²´ë¤ì ê²½ì° ë ê°ì ê°ë
ë¤ì ëë±í ê²ì´ê³ , ë°ì´í°ë² ì´ì¤ identityì ëí JVM identityê° Hibernateì ìí´ ë³´ì¥ëë¤.
+ íì§ë§, ì´í리ì¼ì´ì
ì´ ë ê°ì ë¤ë¥¸ ì¸ì
ë¤ìì "ëì¼í" (ìì identity) ë¹ì§ëì¤ ê°ì²´ì ëìì ì ê·¼íë ëì, ë ê°ì ì¸ì¤í´ì¤ë¤ì
+ ì¤ì ë¡ "ë¤ë¥´ë¤"(JVM identity). 충ëë¤ì flush/ì»¤ë° ìì (ìëì ì¸ ë²ì í)를 ì¬ì©íì¬, optimistic ì ê·¼ë²ì ì¬ì©íì¬ í´ê²°ëë¤.
+ </para>
+
+ <para>
+ ì´ ì ê·¼ë²ì Hibernateì ë°ì´í°ë² ì´ì¤ê° ëìì±ì ëí´ ê±±ì íì§ ìëë¡ í´ì¤ë¤; ê·¸ê²ì ëí ìµìì scalability를 ì ê³µíë¤.
+ ìëíë©´ ë¨ì¼ ì°ë ë-ìì
ë¨ì ë´ìì identity ë³´ì¥ì ë¨ì§ ë¹ì©ì´ ëë ì ê¸ì´ë ë¤ë¥¸ ë기í ìë¨ë¤ì íìë¡ íì§ ì기 ë문ì´ë¤.
+ ì´í리ì¼ì´ì
ì ê·¸ê²ì´ <literal>Session</literal> ë¹ ë¨ì¼ ì°ë ë를 ê°ì íë í, ì´ë¤ ë¹ì§ëì¤ ê°ì²´ì ëí´ ê²°ì½ ë기í ìí¬
+ íìê° ìë¤. íëì <literal>Session</literal> ë´ìì ì´í리ì¼ì´ì
ì ê°ì²´ë¤ì ë¹êµíëë° <literal>==</literal>를
+ ìì íê² ì¬ì©í ìê° ìë¤.
+ </para>
+
+ <para>
+ íì§ë§, íëì <literal>Session</literal> ì¸ë¶ìì <literal>==</literal>를 ì¬ì©íë ì´í리ì¼ì´ì
ì ìê¸°ì¹ ìì
+ ê²°ê³¼ë¤ì ë³´ê² ë ìë ìë¤. ì´ê²ì ì´ë¤ ìê¸°ì¹ ìì ì¥ìë¤ìì, ì를 ë¤ì´ ë¹ì ì´ ë ê°ì detached ì¸ì¤í´ì¤ë¤ì ëì¼í
+ <literal>Set</literal> ë´ì ì§ì´ë£ì ê²½ì°ì ë°ìí ìë ìë¤. ë ë¤ ëì¼í ë°ì´í°ë² ì´ì¤ identity를 ê°ì§ ì ìì§ë§
+ (ì를 ë¤ì´ ê·¸ê²ë¤ì ëì¼í íì íííë¤), JVM identityë ì ì ì detached ìíì ìë ì¸ì¤í´ì¤ë¤ì ë³´ì¥íì§ ìëë¤.
+ ê°ë°ìë ìì í´ëì¤ë¤ë´ì <literal>equals()</literal> ë©ìëì <literal>hashCode()</literal> ë©ìë를 ì¤ë²ë¼ì´ë ìì¼ì¼ íê³
+ ê°ì²´ equalityì ëí ê·¸ ìì ì ê°ë
ì 구íí´ì¼ íë¤. íëì ê²½ê³ ê° ì¡´ì¬íë¤: equality를 구ííëë° ë°ì´í°ë² ì´ì¤ identifier를 ê²°ì½
+ ì¬ì©íì§ ë§ê³ , íëì ë¹ì§ëì¤ í¤, ì ì¼í, ëê° ë¶ë³ì¸ ìì±ë¤ì ì¡°í©ì ì¬ì©íë¼. ë°ì´í°ë² ì´ì¤ ìë³ìë ë§ì¼ transient ê°ì²´ê° ììíëë
+ ê²½ì°ì ë³ê²½ë ê²ì´ë¤. ë§ì¼ transient ì¸ì¤í´ì¤ê°(ëê° detached ì¸ì¤í´ì¤ë¤ê³¼ í¨ê») <literal>Set</literal> ë´ì ë³´ê´ëë ê²½ì°ì,
+ hashcode ë³ê²½ì <literal>Set</literal>ì ê³ì½ì í기ìí¨ë¤. ë¹ì§ëì¤ í¤ë¤ì ëí ìì±ë¤ì ë°ì´í°ë² ì´ì¤ íë¼ì´ë¨¸ë¦¬ í¤ë¤ ë§í¼
+ ìì ì ì´ì´ìë ìëë©°, ë¹ì ì ì¤ì§ ê°ì²´ë¤ì´ ëì¼í <literal>Set</literal> ë´ì ìë íìì ìì ì±ì ë³´ì¥í´ì¼ë§ íë¤.
+ ì´ ìì ì ê´í ë
¼ìì ëí ë ë§ì ê²ì Hibernate ì¹ ì¬ì´í¸ë¥¼ ë³´ë¼. ëí ì´ê²ì´ Hibernate ìì ì´ ìëë©°, ë¨ì§ ìë° ê°ì²´
+ identityì equalityê° êµ¬íëì´ì¼ íë ë°©ë²ìì ë
¸í¸íë¼.
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-basics-issues">
+ <title>ê³µíµë ìì ë¤</title>
+
+ <para>
+ ìí°-í¨í´ë¤ <emphasis>session-per-user-session</emphasis> ëë
+ <emphasis>session-per-application</emphasis>ì ê²°ì½ ì¬ì©íì§ ë§ë¼(ë¬¼ë¡ ì´ ê·ì¹ì ëí ë문 ìì¸ìí©ë¤ì´ ì¡´ì¬íë¤).
+ ë¤ì ìì ë¤ ì¤ ëªëªì´ ëí ê¶ì¥ëë í¨í´ë¤ë¡ ëíë ì ììì ë
¸í¸íê³ , ë¹ì ì´ ì¤ê³ ê²°ì ì ë´ë¦¬ê¸° ì ì ë´í¬ë ì미ë¤ì
+ íì¤í ì´í´íë¼:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>Session</literal>ì ì°ë ë-ìì íì§ ìë¤. HTTP ìì²ë¤, ì¸ì
ë¹ì¦, ëë Swing workerë¤ì²ë¼
+ ëìì ìì
íë ê²ì¼ë¡ ê°ì ëë ê²ë¤ì íëì <literal>Session</literal> ì¸ì¤í´ì¤ê° ê³µì ë ê²½ì°ì ê²½ì ì¡°ê±´ë¤ì
+ ë°ììí¬ ê²ì´ë¤. ë§ì¼ ë¹ì ì´ ë¹ì ì <literal>HttpSession</literal> ë´ì Hibernate
+ <literal>Session</literal>ì ì ì§ìí¤ë ê²½ì°(ëì¤ì ë
¼ìë¨), ë¹ì ì ë¹ì ì Http ì¸ì
ì ëí ì ê·¼ì ë기í
+ ìí¤ë ê²ì ê³ ë ¤í´ì¼ íë¤. ê·¸ ë°ì ê²½ì°, 충ë¶í ë¹ ë¥´ê² reload를 í´ë¦íë ì¬ì©ìë ë ê°ì ëìì ì¼ë¡ ì¤íëë
+ ì°ë ëë¤ ë´ìì ëì¼í <literal>Session</literal>ì ì¬ì©í ìë ìë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Hibernateì ìí´ ëì ¸ì§ ìì¸ìí©ì ë¹ì ì´ ë¹ì ì ë°ì´í°ë² ì´ì¤ í¸ëìì
ì 롤백 ìí¤ê³ ì¦ì <literal>Session</literal>ì
+ ë«ìì¼ í¨ì ì미íë¤(ëì¤ì ìì¸í ë
¼ìë¨). ë§ì¼ ë¹ì ì <literal>Session</literal>ì´ ì´í리ì¼ì´ì
ì ë°ì¸ë ëì´ ìë ê²½ì°,
+ ë¹ì ì ì´í리ì¼ì´ì
ì ì¤ì§ìì¼ì¼ íë¤. ë°ì´í°ë² ì´ì¤ í¸ëìì
롤백ì ë¹ì ì ë¹ì§ëì¤ ê°ì²´ë¤ì ê·¸ê²ë¤ì´ í¸ëìì
ì ìì ìì
+ 머물ë ë ìíë¡ ëë리ì§ë ìëë¤. ì´ê²ì ë°ì´í°ë² ì´ì¤ ìíì ë¹ì§ëì¤ ê°ì²´ë¤ì´ ë기í를 ë²ì´ë¨ì ì미íë¤. ëê° ì´ê²ì 문ì ê° ìëë¤.
+ ìëíë©´ ìì¸ìí©ë¤ì íë³µê°ë¥í ê²ì´ ìëê³ ë¹ì ì´ ì´ë»ê²ë 롤백 íì ììí´ì¼ í기 ë문ì´ë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>Session</literal>ì (Hibernateì ìí´ dirty ìíë¡ ê´ì°°ëìê±°ë ì²´í¬ë) ìì ìíì ìë
+ 모ë ê°ì²´ë¥¼ ìºì ìí¨ë¤. ì´ê²ì ë¹ì ì´ ì¤ë ìê° ëì <literal>Session</literal>ì ì´ì´ë ì±ë¡ ì ì§íê±°ë
+ ë¨ìíê² ë무 ë§ì ë°ì´í°ë¥¼ ë¡ëìí¬ ê²½ì°ì, ë¹ì ì´ OutOfMemoryExceptionì ì»ê¸° ì ê¹ì§, ê·¸ê²ì´ ëìì´
+ ì±ì¥íë¤ë ì ì ì미íë¤. ì´ê²ì ëí íëì í´ê²°ì±
ì <literal>Session</literal> ìºì를 ê´ë¦¬í기 ìí´
+ <literal>clear()</literal>ì <literal>evict()</literal>를 í¸ì¶íë ê²ì´ì§ë§, ë¹ì ì´ ëì©ë ë°ì´í°
+ ì¤í¼ë ì´ì
ë¤ì íìë¡ íë ê²½ì°ì ë¹ì ì ëê° ë´ì¥ íë¡ìì 를 ê³ ë ¤í´ì¼ í ê²ì´ë¤. ëªëª í´ê²°ì±
ë¤ì´
+ <xref linkend="batch"/>ì ë³´ì¬ì ¸ ìë¤. ì¬ì©ì ì¸ì
ëìì <literal>Session</literal>ì ì´ë ¤ì§ ì±ë¡
+ ì ì§íë ê²ì ëí ì¤í¨ì±ì´ ë¨ì´ì§ ë°ì´í°ì ëí ëì íë¥ ì ì미íë¤.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="transactions-demarcation">
+ <title>ë°ì´í°ë² ì´ì¤ í¸ëìì
ê²½ê³ ì¤ì </title>
+
+ <para>
+ ë°ì´í°ë² ì´ì¤ (ëë ìì¤í
) í¸ëìì
ê²½ê³ë¤ì íì íìì ì´ë¤. ë°ì´í°ë² ì´ì¤ìì íµì ì ë°ì´í°ë² ì´ì¤ í¸ëìì
ì ì¸ë¶ìì ë°ìí ì
+ ìë¤(ì´ê²ì ìë-ì»¤ë° ëª¨ëë¡ ì¬ì©ëë ë§ì ê°ë°ìë¤ìê²ë í¼ëì¤ë¬ì ë³´ì¸ë¤). íì ì¬ì§ì´ ì½ê¸° ì ì© ì¤í¼ë ì´ì
ë¤ì ëí´ìë ëª
ë£í
+ í¸ëìì
ê²½ê³ë¤ì ì¬ì©íë¼. ë¹ì ì 격리 ë 벨과 ë°ì´í°ë² ì´ì¤ ê°ì©ì±ë¤ì ë°ë¼, ì´ê²ì íìíì§ ìì ì ìì§ë§, ë§ì¼ ë¹ì ì´ íì
+ í¸ëìì
ë¤ì ëª
ìì ì¼ë¡ ê²½ê³ ì¤ì í ê²½ì°ìë íê°íë ê²°ì ë¤ì´ ì¡´ì¬íì§ ìëë¤. íì¤í, íëì ë°ì´í°ë² ì´ì¤ í¸ëìì
ì ì¬ì§ì´
+ ë°ì´í° ì½ê¸°ì¡°ì°¨ë ë§ì ìì í¸ëìì
ë¤ì ê²½ì°ë³´ë¤ë ë ì¢ê² ìíë ê²ì´ë¤.
+ </para>
+
+ <para>
+ Hibernate ì´í리ì¼ì´ì
ì ê´ë¦¬ëì§ ìë íê²½(ì를 ë¤ë©´. ì¤í ëì¼ë¡ , ê°ë¨í ì¹ ì´í리ì¼ì´ì
ë¤ ëë Swing ì´í리ì¼ì´ì
ë¤)ê³¼
+ ê´ë¦¬ëë J2EE íê²½ìì ì¤íë ì ìë¤. ê´ë¦¬ëì§ ìë íê²½ìì, Hibernateë ëê° ê·¸ê² ìì ì ë°ì´í°ë² ì´ì¤ 커ë¥ì
íì ëí
+ ì±
ìì´ ìë¤. ì´í리ì¼ì´ì
ê°ë°ìë í¸ëìì
ê²½ê³ë¤ì ìì ì¤ì í´ì¼ íë¤. ë¬ë¦¬ ë§í´, ê°ë°ì ì¤ì¤ë¡ ë°ì´í°ë² ì´ì¤ í¸ëìì
ë¤ì
+ ììíê³ , 커ë°ìí¤ê±°ë 롤백ìì¼ì¼ íë¤. ê´ë¦¬ëë íê²½ì ëê° ì를 ë¤ì´ EJB ì¸ì
ë¹ì¦ì ë°°ì¹ ëì¤í¬ë¦½í° ìì ì ì¸ì ì¼ë¡ ì ìë
+ í¸ëìì
ì´ì
ë¸ë¦¬ë¥¼ ê°ì§, 컨í
ì´ëì ìí´-ê´ë¦¬ëë í¸ëìì
ë¤(CMT)ì ì ê³µíë¤. ê·¸ë íë¡ê·¸ë¨ ìì í¸ëìì
ê²½ê³ ì¤ì ì ë ì´ì íìíì§
+ ìë¤.
+ </para>
+
+ <para>
+ íì§ë§, CMT ëì BMT를 ì¬ì©íë JTAì ìì¡´í ì ìë ìì¤í
ë¤, ê·¸ë¦¬ê³ ê´ë¦¬ëì§ ìë resource-local íê²½ë¤ ì¬ì´ìì
+ ë¹ì ì ìì ê³ì¸µì ì´ìì±ì ì ì§ìí¤ë ê²ì´ ì주 í¬ë§ëë¤. ë ê²½ì°ë¤ìì ë¹ì ì íë¡ê·¸ë¨ ìì í¸ëìì
ê²½ê³ì¤ì ì ì¬ì©í ê²ì´ë¤.
+ Hibernateë ë¹ì ì ë°°ì¹ íê²½ì ê³ ì í í¸ëìì
ìì¤í
ìì¼ë¡ ë³íëë <literal>Transaction</literal>ì´ë¼ ëª
ëª
ëë
+ wrapper API 를 ì ê³µíë¤. ì´ APIë ì¤ì ë¡ ìµì
ì´ì§ë§ ì°ë¦¬ë ë¹ì ì´ CMT session bean ìì ìì§ ìë í ê·¸ê²ì ì¬ì©ì
+ ê°ë ¥íê² ê¶ì¥íë¤.
+ </para>
+
+ <para>
+ ëê° <literal>Session</literal> ì¢
ë£ë ë¤ ê°ì 구ë¶ëë ë¨ê³ë¤ì ìë°íë¤:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ ì¸ì
ì flush ìí¨ë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ í¸ëìì
ì ì»¤ë° ìí¨ë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ì¸ì
ì ë«ëë¤
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ìì¸ìí©ë¤ì ì²ë¦¬íë¤
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ì¸ì
ì flush ìí¤ë ê²ì ìì ë
¼ìëìê³ , ì°ë¦¬ë ì´ì ê´ë¦¬ëë í경과 ê´ë¦¬ëì§ ìë íê²½ ìììì í¸ëìì
ê²½ê³ ì¤ì ê³¼ ìì¸ìí©ì
+ ë ìì¸í ì´í´ë³¼ ê²ì´ë¤.
+ </para>
+
+
+ <sect2 id="transactions-demarcation-nonmanaged" revision="2">
+ <title>ê´ë¦¬ëì§ ìë íê²½</title>
+
+ <para>
+ ë§ì¼ Hibernate ìì ê³ì¸µì´ ê´ë¦¬ëì§ ìë(non-managed) íê²½ìì ì¤íë ê²½ì°, ë°ì´í°ë² ì´ì¤ 커ë¥ì
ë¤ì ëê° Hibernateê°
+ íìë¡í ë 커ë¥ì
ë¤ì íëíë ê°ë¨í (ì를 ë¤ë©´ DataSourceê° ìë) 커ë¥ì
í(pool)ë¤ë¡ë¶í° ì²ë¦¬ëë¤.
+ session/transaction ì²ë¦¬ ê´ì©êµ¬ë ë¤ìê³¼ ê°ë¤:
+ </para>
+
+ <programlisting><![CDATA[// Non-managed environment idiom
+Session sess = factory.openSession();
+Transaction tx = null;
+try {
+ tx = sess.beginTransaction();
+
+ // do some work
+ ...
+
+ tx.commit();
+}
+catch (RuntimeException e) {
+ if (tx != null) tx.rollback();
+ throw e; // or display error message
+}
+finally {
+ sess.close();
+}]]></programlisting>
+
+ <para>
+ ë¹ì ì <literal>Session</literal>ì ëª
ìì ì¼ë¡ <literal>flush()</literal> íì§ ë§ìì¼ íë¤ -
+ <literal>commit()</literal>ì ëí í¸ì¶ì (ê·¸ ì¸ì
ì ëí
+ <xref linkend="objectstate-flushing">FlushMode</xref>ì ë°ë¼)ìëì ì¼ë¡ ë기í를 í¸ë¦¬ê±°ìí¨ë¤.
+ <literal>close()</literal>ì ëí í¸ì¶ì ì¸ì
ì ëì ë§í¬íë¤. <literal>close()</literal>ì
+ 주ë 구íì JDBC 커ë¥ì
ì´ ê·¸ ì¸ì
ì ìí´ í¬ê¸°ë ê²ì´ë¼ë ì ì´ë¤. ì´ Java ì½ëë ê´ë¦¬ëì§ ìë í경과
+ JTA íê²½ ìììì ì´ìì±ì´ ìê³ ì¤íëë¤.
+ </para>
+
+ <para>
+ ë³´ë¤ ë ì ì°í í´ê²°ì±
ì ìì ì¤ëª
íë¯ì´ Hibernateì 미리 ë¹ëëì´ ìë "íì¬ ì¸ì
" 컨í
ì¤í¸ ê´ë¦¬ì´ë¤:
+ </para>
+
+ <programlisting><![CDATA[// Non-managed environment idiom with getCurrentSession()
+try {
+ factory.getCurrentSession().beginTransaction();
+
+ // do some work
+ ...
+
+ factory.getCurrentSession().getTransaction().commit();
+}
+catch (RuntimeException e) {
+ factory.getCurrentSession().getTransaction().rollback();
+ throw e; // or display error message
+}]]></programlisting>
+
+ <para>
+ ë¹ì ì íµìì ì´í리ì¼ì´ì
ìì ë¹ì§ëì¤ ì½ë ìì ì´ ê´ì©êµ¬ë¥¼ ê²°ì½ ë³´ì§ ìì ê²ì´ë¤; ì¹ëª
ì ì¸(ìì¤í
) ìì¸ìí©ë¤ì íì
+ "ìë¨"ìì ì¡íì¼ íë¤. ë¬ë¦¬ ë§í´, (ìì ê³ì¸µìì) Hibernate í¸ì¶ë¤ì ì¤íìí¤ë ì½ëì <literal>RuntimeException</literal>ì
+ ì²ë¦¬í(ê³ ëê° ì¤ì§ ì ê±°íê³ ë¹ ì ¸ëê° ì ìë) ì½ëë ë¤ë¥¸ ê³ì¸µë¤ ìì ìë¤. Hibernateì ìí íì¬ ì»¨í
ì¤í¸ ê´ë¦¬ë ì´ ì¤ê³ë¥¼
+ í격íê² ë¨ìíìì¼ì, ë¹ì ì´ íìë¡ íë 모ë ê²ì <literal>SessionFactory</literal>ì ëí ì ê·¼ì´ë¤.ìì¸ìí©
+ ì²ë¦¬ë ì´ ì¥ì ë·ë¶ë¶ìì ë
¼ìëë¤.
+ </para>
+
+ <para>
+ ë¹ì ì (ëí´í¸ì¸) <literal>org.hibernate.transaction.JDBCTransactionFactory</literal>를 ì íí´ì¼ íê³ ,
+ ëë²ì§¸ ìì ì ê²½ì° ë¹ì ì <literal>hibernate.current_session_context_class</literal>를 ì íí´ì¼ í¨ì ë
¸í¸íë¼.
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-demarcation-jta" revision="3">
+ <title>JTA ì¬ì©í기</title>
+
+ <para>
+ ë§ì¼ ë¹ì ì ìì ê³ì¸µì´ ì´í리ì¼ì´ì
ìë²ìì(ì를 ë¤ì´, EJB ì¸ì
ë¹ì¦ ì´ë©´ìì) ì¤íë ê²½ì°, Hibernateì ìí´
+ íëë 모ë ë°ì´í°ìì¤ ì»¤ë¥ì
ì ìëì ì¼ë¡ ì ì JTA í¸ëìì
ì ë¶ë¶ì¼ ê²ì´ë¤. ë¹ì ì ëí ì¤í ëì¼ë¡ JTA 구íì
+ ì¤ì¹í ì ìê³ EJB ìì´ ê·¸ê²ì ì¬ì©í ì ìë¤. Hibernateë JTA íµí©ì ìí ë ê°ì ë°©ëë¤ì ì ê³µíë¤.
+ </para>
+
+ <para>
+ ë§ì¼ ë¹ì ì´ bean-managed transactions(BMT)를 ì¬ì©í ê²½ì° Hibernateë ë¹ì ì´ <literal>Transaction</literal>
+ API를 ì¬ì©í ê²½ì°ì BMT í¸ëìì
ì ììíê³ ì¢
ë£íëë¡ ì´í리ì¼ì´ì
ìë²ìê² ìë ¤ì¤ ê²ì´ë¤. ë°ë¼ì í¸ëìì
ê´ë¦¬ ì½ëë
+ non-managed í경과 ëì¼íë¤.
+ </para>
+
+ <programlisting><![CDATA[// BMT idiom
+Session sess = factory.openSession();
+Transaction tx = null;
+try {
+ tx = sess.beginTransaction();
+
+ // do some work
+ ...
+
+ tx.commit();
+}
+catch (RuntimeException e) {
+ if (tx != null) tx.rollback();
+ throw e; // or display error message
+}
+finally {
+ sess.close();
+}]]></programlisting>
+
+ <para>
+ ë§ì¼ ë¹ì ì´ í¸ëìì
ì ë¬¶ì¸ <literal>Session</literal>, ì¦ ì¬ì´ 컨í
ì¤í¸ ë³´ê¸ì ìí
+ <literal>getCurrentSession()</literal> 기ë¥ì ì¬ì©íê³ ì ìí ê²½ì°, ë¹ì ì
+ JTA <literal>UserTransaction</literal> API를 ì§ì ì¬ì©í´ì¼ í ê²ì´ë¤:
+ </para>
+
+ <programlisting><![CDATA[// BMT idiom with getCurrentSession()
+try {
+ UserTransaction tx = (UserTransaction)new InitialContext()
+ .lookup("java:comp/UserTransaction");
+
+ tx.begin();
+
+ // Do some work on Session bound to transaction
+ factory.getCurrentSession().load(...);
+ factory.getCurrentSession().persist(...);
+
+ tx.commit();
+}
+catch (RuntimeException e) {
+ tx.rollback();
+ throw e; // or display error message
+}]]></programlisting>
+
+ <para>
+ CMTì ê²½ì°, í¸ëìì
ê´í [ê²½ê³ ì¤ì ]ì íë¡ê·¸ë¨ ìì´ ìë, session bean ë°°ì¹ ëì¤í¬ë¦½í°ë¤ ììì íí´ì§ë¤.
+ ê·¸ë¬ë¯ë¡ ì½ëë ë¤ìì¼ë¡ ê°ìëë¤:
+ </para>
+
+ <programlisting><![CDATA[// CMT idiom
+ Session sess = factory.getCurrentSession();
+
+ // do some work
+ ...
+]]></programlisting>
+
+ <para>
+ ìëíë©´ íëì ì¸ì
ë¹ ë©ìëì ìí´ ëì ¸ì§ ì²ë¦¬ëì§ ìì <literal>RuntimeException</literal>ì´
+ ê¸ë¡ë² í¸ëìì
ì rollbackì¼ë¡ ì¤ì íëë¡ ì»¨í
ì´ëìê² ìë ¤ì£¼ê¸° ë문ì, CMT/EJBììì¡°ì°¨ 롤백ì ìëì ì¼ë¡ ë°ìëë¤.
+ <emphasis>ì´ê²ì ë¹ì ì´ BMT ì´ë CMTì´ë 모ëìì Hibernate <literal>Transaction</literal> API를 ì¬ì©í íìê°
+ ìì¼ë©°, ë¹ì ì ê·¸ í¸ëìì
ì ë¬¶ì¸ "íì¬" Sessionì ìëì ì¸ ë³´ê¸(propagation)ì ì»ê² ë¨ì ì미íë¤.</emphasis>
+ </para>
+
+ <para>
+ ë¹ì ì´ Hibernateì í¸ëìì
í©í 리를 구ì±í ë, ë¹ì ì´ JTA를 ì§ì ì¬ì©í ê²½ì°(BMT) ë¹ì ì
+ <literal>org.hibernate.transaction.JTATransactionFactory</literal>를 ì íí´ì¼íê³ , CMT session beanììë
+ <literal>org.hibernate.transaction.CMTTransactionFactory</literal>를 ì íí´ì¼ í¨ì ë
¸í¸íë¼. ëí
+ <literal>org.hibernate.transaction.manager_lookup_class</literal>를 ì¤ì íë ê²ì ì¼ëì ëë¼. ê²ë¤ê°
+ ë°ëì ë¹ì ì <literal>hibernate.current_session_context_class</literal>ì´ ì¤ì ëì§ ìëë¡ íê±°ë(ìí¸íì±),
+ ëë <literal>"jta"</literal>ë¡ ì¤ì ëëë¡ íë¼.
+ </para>
+
+ <para>
+ <literal>getCurrentSession()</literal> ì¤í¼ë ì´ì
ë¤ì JTA íê²½ìì í ê°ì§ ë¨ì ì ê°ê³ ìë¤.
+ ëí´í¸ë¡ ì¬ì©ëë, <literal>after_statement</literal> 커ë¥ì
í´ì 모ë ì¬ì©ì ëí íëì ë³´ë¥ íµë³´ê° ì¡´ì¬íë¤.
+ JTA ëª
ì¸ìì ì´ë¦¬ìì ì íì¼ë¡ ì¸í´, Hibernateê° <literal>scroll()</literal> ëë <literal>iterate()</literal>ì
+ ìí´ ë°íëë ììì ë«íì§ì§ ìì <literal>ScrollableResults</literal> ëë <literal>Iterator</literal>
+ ì¸ì¤í´ì¤ë¤ì ìëì ì¼ë¡ ì ê±°íë ê²ì´ ë¶ê°ë¥íë¤. ë¹ì ì <literal>finally</literal> ë¸ë¡ ë´ìì ëª
ìì ì¼ë¡
+ <literal>ScrollableResults.close()</literal> ëë <literal>Hibernate.close(Iterator)</literal>를
+ í¸ì¶íì¬ ê¸°ë³¸ ë°ì´í°ë² ì´ì¤ 커ì를 í´ì <emphasis>ìì¼ì¼ íë¤</emphasis>.(ë¬¼ë¡ ëë¶ë¶ì ì´í리ì¼ì´ì
ë¤ì JTA ëë CMT ì½ëìì
+ <literal>scroll()</literal>ì´ë <literal>iterate()</literal>를 ì¬ì©íë ê²ì ì½ê² í¼í ì ìë¤.)
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-demarcation-exceptions">
+ <title>ìì¸ìí© ì²ë¦¬</title>
+
+ <para>
+ ë§ì¼<literal>Session</literal>ì´ (ì´ë¤ <literal>SQLException</literal>ì í¬í¨íë) ìì¸ìí©ì ëì§ ê²½ì°,
+ ë¹ì ì ë°ì´í°ë² ì´ì¤ í¸ëìì
ì ì¦ì 롤백ìí¤ê³ , <literal>Session.close()</literal>를 í¸ì¶íê³
+ <literal>Session</literal> ì¸ì¤í´ì¤ë¥¼ í기ìì¼ì¼íë¤. <literal>Session</literal>ì ì´ë¤ ë©ìëë¤ì
+ ê·¸ ì¸ì
ì ì¼ê´ë ìíë¡ ë¨ê²¨ëì§ <emphasis>ìì</emphasis> ê²ì´ë¤. Hibernateì ìí´ ëì ¸ì§ ìì¸ìí©ì 복구ê°ë¥í
+ ê²ì¼ë¡ ì·¨ê¸ë ì ìë¤. ê·¸ <literal>Session</literal>ì´ <literal>finally</literal> ë¸ë¡ ë´ìì
+ <literal>close()</literal>를 í¸ì¶íì¬ ë«íì§ëë¡ íì¤í íë¼.
+ </para>
+
+ <para>
+ Hibernate ìì ê³ì¸µìì ë°ìí ì ìë ëë¶ë¶ì ì¤ë¥ë¤ì í¬ì¥íë, <literal>HibernateException</literal>ì
+ ì²´í¬ëì§ ìì ìì¸ìí©ì´ë¤(ê·¸ê²ì Hibernateì ì´ì ë²ì ìë ììë¤). ì°ë¦¬ì ì견ì¼ë¡, ì°ë¦¬ë ë®ì ê³ì¸µìì 복구ë¶ê°ë¥í
+ ìì¸ìí©ì ë¶ì¡ëë¡ ì´í리ì¼ì´ì
ê°ë°ììê² ê°ì íì§ ìì ê²ì´ë¤. ëë¶ë¶ì ìì¤í
ë¤ìì, ì²´í¬ëì§ ìì ì¹ëª
ì ì¸ ìì¸ìí©ë¤ì
+ (ì를 ë¤ì´, ë ëì ê³ì¸µìì) ë©ìë í¸ì¶ ì¤íì 첫 ë²ì§¸ íë ìë¤ ì¤ íë ììì ì²ë¦¬ëê³ , í ê°ì ì¤ë¥ ë©ìì§ê° ì´í리ì¼ì´ì
+ ì¬ì©ììê² íìëë¤(ëë ì´ë¤ ë¤ë¥¸ ì ì í ì¡ì
ì´ ì·¨í´ì§ë¤). Hibernateë ëí <literal>HibernateException</literal>ì´
+ ìë, ë¤ë¥¸ ì²´í¬ëì§ ìì ìì¸ìí©ë¤ì ëì§ ìë ììì ë
¸í¸íë¼. ë¤ì ì´ê²ë¤ì 복구ê°ë¥íì§ ìê³ ì ì í ì¡ì
ì´ ì·¨í´ì ¸ì¼ íë¤.
+ </para>
+
+ <para>
+ Hibernateë ë°ì´í°ë² ì´ì¤ì ìí¸ìì©íë ëìì ëì ¸ì§ <literal>SQLException</literal>ë¤ì íëì
+ <literal>JDBCException</literal> ìì í¬ì¥íë¤. ì¬ì¤, Hibernateë ê·¸ ìì¸ìí©ì
+ <literal>JDBCException</literal>ì ë³´ë¤ ì미ìë ìë¸í´ëì¤ë¡ ë³ííë ¤ê³ ìëí ê²ì´ë¤. 기본
+ <literal>SQLException</literal>ì <literal>JDBCException.getCause()</literal>를 íµí´ íì ì´ì© ê°ë¥íë¤.
+ Hibernateë<literal>SessionFactory</literal>ì 첨ë¶ë <literal>SQLExceptionConverter</literal>를
+ ì¬ì©íì¬ <literal>SQLException</literal>ì ì ë¹í íëì <literal>JDBCException</literal> ìë¸í´ëì¤ë¡
+ ë³íìí¨ë¤. ëí´í¸ë¡ <literal>SQLExceptionConverter</literal>ë 구ì±ë dialectì ìí´ ì ìëë¤; íì§ë§
+ ë§ì¶¤ 구í ìì íë¬ê·¸ì¸ ìí¤ë ê²ì´ ëí ê°ë¥íë¤(ìì¸í ê²ì <literal>SQLExceptionConverterFactory</literal>
+ í´ëì¤ì ê´í javadocs를 ë³´ë¼). íì¤ <literal>JDBCException</literal> ìë¸íì
ì ë¤ìê³¼ ê°ë¤:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>JDBCConnectionException</literal> - 기본 JDBC íµì ì ëí ì¤ë¥ë¥¼ ëíë¸ë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>SQLGrammarException</literal> - ì겨ë SQLì ëí ë¬¸ë² ëë 구문 문ì ì ì ëíë¸ë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>ConstraintViolationException</literal> - ë¬´ê²°ì± ì ì½ ìë°ì ê´í ì´ë¤ íìì ëíë¸ë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>LockAcquisitionException</literal> - ìì²ë ì¤í¼ë ì´ì
ì ì¤ííëë° íìì ì¸ ì ê¸ ë 벨ì
+ íëíë ì¤ë¥ë¥¼ ëíë¸ë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>GenericJDBCException</literal> - ë¤ë¥¸ ì¹´í
ê³ ë¦¬ë¤ ì¤ ì´ë¤ ê²ì¼ë¡ ë¶ë¥ëì§ ììë ì¼ë°ì ì¸ ìì¸ìí©.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect2>
+
+ <sect2 id="transactions-demarcation-timeout">
+ <title>í¸ëìì
íììì</title>
+
+ <para>
+ ê´ë¦¬ëì§ ìë ì½ëì ëí´ìë ê²°ì½ ì ê³µëì§ ìë EJBì ê°ì´ ê´ë¦¬ëë íê²½ì ìí´ ì ê³µëë í ê°ì§ ê·¹í
+ ì¤ìí í¹ì§ì í¸ëìì
íìììì´ë¤. í¸ëìì
íìììì ì¬ì©ììê² ìëµì ë°ííì§ ìë ëìì
+ 무ë¡í íëì ííë í¸ëìì
ì´ ììë¤ì 무íì 묶ì´ë ì ììì ë³´ì¦í´ì¤ë¤. ê´ë¦¬ëë (JTA) íê²½
+ ì¸ë¶ìì, Hibernateë ì´ ê¸°ë¥ì ì¨ì íê² ì ê³µí ì ìë¤. íì§ë§ Hibernateë ë°ì´í°ë² ì´ì¤
+ ë 벨 ë°ëë½ë¤ê³¼ ê±°ëí ê²°ê³¼ ì
ë¤ì ê°ì§ ì§ìë¤ì´ ì ìë íìììì ìí´ ì ì½ëë ê²ì ë³´ì¥í¨ì¼ë¡ì¨
+ ìµìí ë°ì´í° ì ê·¼ ì¤í¼ë ì´ì
ë¤ì ì ì´í ì ìë¤. ê´ë¦¬ëë íê²½ìì, Hibernateë í¸ëìì
íìììì
+ JTAìê² ìììí¬ ì ìë¤. ì´ ê¸°ë¥ì Hibernate <literal>Transaction</literal> ê°ì²´ì ìí´
+ ì¶ìí ëë¤.
+ </para>
+
+ <programlisting><![CDATA[
+Session sess = factory.openSession();
+try {
+ //set transaction timeout to 3 seconds
+ sess.getTransaction().setTimeout(3);
+ sess.getTransaction().begin();
+
+ // do some work
+ ...
+
+ sess.getTransaction().commit()
+}
+catch (RuntimeException e) {
+ sess.getTransaction().rollback();
+ throw e; // or display error message
+}
+finally {
+ sess.close();
+}]]></programlisting>
+
+ <para>
+ <literal>setTimeout()</literal>ì CMT bean ë´ìì í¸ì¶ë리 ì ììì ë
¸í¸íë¼.
+ ì¬ê¸°ì í¸ëìì
íìììë¤ì ì ì¸ì ì¼ë¡ ì ìëì´ì¼ íë¤.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="transactions-optimistic">
+ <title>Optimistic ëìì± ì ì´</title>
+
+ <para>
+ ê³ ëì ëìì±ê³¼ ê³ ëì ê°ì©ì±ì ì¼ì¹ìí¤ë ì ì¼í ì ê·¼ë²ì ë²ì í를 ê°ì§ optimisticëìì± ì ì´ì´ë¤. ë²ì ì²´í¹ì ì
ë°ì´í¸ 충ëì
+ ê²ì¶í기 ìí´(ê·¸ë¦¬ê³ ì
ë°ì´í¸ ìì¤ì ë°©ì§í기 ìí´) ë²ì ë²í¸ë¤ ëë timestampë¤ì ì¬ì©íë¤. Hibernateë optimistic ëìì±ì
+ ì¬ì©íë ì´í리ì¼ì´ì
ì½ë ìì±ì ì¸ ê°ì§ ê°ë¥í ì ê·¼ë²ë¤ì ì ê³µíë¤. ì°ë¦¬ê° ë³´ì¬ì£¼ë ì°ììë¤ì ì¥ìê°ì ëíì ìí©
+ ìì ìì§ë§, ë²ì ì²´í¹ ëí ë¨ì¼ ë°ì´í°ë² ì´ì¤ í¸ëìì
ë¤ìì ì
ë°ì´í¸ ìì¤ì ë°©ì§íë ì´ì ì ê°ê³ ìë¤.
+ </para>
+
+ <sect2 id="transactions-optimistic-manual">
+ <title>ì´í리ì¼ì´ì
ë²ì ì²´í¹</title>
+
+ <para>
+ íëì 구íìì Hibernateë¡ë¶í° ë§ì ëìì´ ìì´, ë°ì´í°ë² ì´ì¤ì ëí ê°ê°ì ìí¸ìì©ì ìë¡ì´ <literal>Session</literal>
+ ë´ìì ì¼ì´ëê³ , ê°ë°ìë ìì ì¸ì¤í´ì¤ë¤ì ì²ë¦¬í기 ì ì ë°ì´í°ë² ì´ì¤ë¡ë¶í° 모ë ìì ì¸ì¤í´ì¤ë¤ì ë¤ì ë¡ëìí¬ ì±
ìì´ ìë¤.
+ ì´ ì ê·¼ë²ì ëí í¸ëìì
ì íì¤í 격리ìí¤ê¸° ìí´ ê·¸ê² ìì ì ë²ì ì²´í¹ì ìííëë¡ ì´í리ì¼ì´ì
ìê² ê°ì ìí¨ë¤.
+ </para>
+
+ <programlisting><![CDATA[// foo is an instance loaded by a previous Session
+session = factory.openSession();
+Transaction t = session.beginTransaction();
+
+int oldVersion = foo.getVersion();
+session.load( foo, foo.getKey() ); // load the current state
+if ( oldVersion!=foo.getVersion ) throw new StaleObjectStateException();
+foo.setProperty("bar");
+
+t.commit();
+session.close();]]></programlisting>
+
+ <para>
+ version íë¡í¼í°ë <literal><version></literal>ì ì¬ì©íì¬ ë§¤íëê³ , Hibernateë ë§ì¼ ìí°í°ê° dirtyì¼ ê²½ì°
+ flush ëìì ê·¸ê²ì ìëì ì¼ë¡ ì¦ê°ìí¬ ê²ì´ë¤.
+ </para>
+
+ <para>
+ ë¬¼ë¡ , ë¹ì ì´ ë®ì ë°ì´í° ëìì± íê²½ìì ìì
íê³ ìê³ ë²ì ì²´í¹ì íìë¡ íì§ ìì ê²½ì°ì, ë¹ì ì ì´ ì ê·¼ë²ì ì¬ì©í ì ë ìê³
+ ë¨ì§ ë²ì ì²´í¬ë¥¼ ìëµí ìë ìë¤. ê·¸ ê²½ì°ì, <emphasis>ë§ì§ë§ì ì»¤ë° ì±ê³µ</emphasis>ì ë¹ì ì ì¥ìê°ì ëíì
+ ëí ëí´í¸ ë°©ëê° ë ê²ì´ë¤. ì´ê²ì´ ì´í리ì¼ì´ì
ì ì¬ì©ìë¤ì í¼ëìí¬ ì ììì ì¼ëì ëë¼. ìëíë©´ ì¬ì©ìë¤ì ì¤ë¥ ë©ìì§ë¤
+ ëë 충ë ë³ê²½ë¤ì ë³í©ìí¬ ê¸°í ìì´ ì
ë°ì´í¸ë¤ ìì¤ì 겪ì ìë ì기 ë문ì´ë¤.
+ </para>
+
+ <para>
+ ëª
ë£íê² ììì
ë²ì ì²´í¹ì ë§¤ì° ì¬ìí íê²½ë¤ììë ê³µí¬ì ì´ê³ ëë¶ë¶ì ì´í리ì¼ì´ì
ë¤ì ëí´ ì¤ì ì ì´ì§ ìë¤. íí ë¨ì¼
+ ì¸ì¤í´ì¤ ë¿ë§ ìëë¼ ë³ê²½ë ê°ì²´ë¤ì ì ì²´ ê·¸ëíë¤ì´ ì²´í¬ëì´ì¼ íë¤. Hibernateë ì¤ê³ í¨ë¬ë¤ìì¼ë¡ì íëì íì¥ë <literal>Session</literal>
+ ëë detached ì¸ì¤í´ì¤ë¤ì ëí´ ìëì ì¸ ë²ì ì²´í¹ì ì ê³µíë¤.
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-optimistic-longsession">
+ <title>íì¥ë ì¸ì
ê³¼ ìëì ì¸ ë²ì í</title>
+
+ <para>
+ íëì <literal>Session</literal> ì¸ì¤í´ì¤ì ê·¸ê²ì ìì ì¸ì¤í´ì¤ë¤ì ì ì²´ ì´í리ì¼ì´ì
í¸ëìì
ì ì¬ì©ëë¤. Hibernateë
+ flush í ë ì¸ì¤í´ì¤ ë²ì ë¤ì ì²´í¬íê³ ë§ì¼ ëìì± ë³ê²½ì´ ê²ì¶ë ê²½ì°ì ìì¸ìí©ì ëì§ë¤. ì´ ìì¸ìí©ì ì¡ìë´ê³ ì²ë¦¬íë ê²ì
+ ê°ë°ìì 몫ì´ë¤(ê³µíµë ìµì
ë¤ì ë³ê²½ë¤ì ë³í©ìí¤ê±°ë ëë ì¸ëª¨ê° ìì§ ìì ë°ì´í°ë¡ ë¹ì§ëì¤ íë¡ì¸ì¤ë¥¼ ë¤ì ììíë 기í를
+ ì¬ì©ììê² ì£¼ë ê²ì´ë¤).
+ íëì <literal>Session</literal> ì¸ì¤í´ì¤ì ê·¸ê²ì ìì ì¸ì¤í´ì¤ë¤ì <emphasis>session-per-conversation</emphasis>ë¡
+ ìë ¤ì§ ì ì²´ ëíì ì¬ì©ëë¤. Hibernateë flush ìì ìì ì¸ì¤í´ì¤ ë²ì ë¤ì ì²´í¬íë©°, ë§ì¼ ëìì ì¸ ë³ê²½ì´ ê²ì¶ë ê²½ì°ì
+ íëì ìì¸ìí©ì ëì§ë¤. ì´ ìì¸ìí©ì í¬ì°©íê³ ì²ë¦¬íë ê²ì ê°ë°ìì 몫ì´ë¤(ê³µíµë ìµì
ë¤ì ì¬ì©ìê° ë³ê²½ë¤ì ë³í©íê±°ë
+ ìì¤ëì§ ìì ë°ì´í°ë¥¼ ê°ì§ê³ ë¹ì§ëì¤ ëí를 ì¬ììíë 기íì´ë¤).
+ </para>
+
+ <para>
+ <literal>Session</literal>ì ì¬ì©ì ìí¸ìì©ì 기ë¤ë¦´ ë ì´ë¤ 기본 JDBC 커ë¥ì
ì¼ë¡ë¶í° ì°ê²°í´ì ëë¤. ì´ ì ê·¼ë²ì
+ ë°ì´í°ë² ì´ì¤ ì ê·¼ì ê´ì ìì ë³´ë©´ ê°ì¥ í¨ì¨ì ì´ë¤. ì´í리ì¼ì´ì
ì ë²ì ì²´í¹ ëë detached ì¸ì¤í´ì¤ë¤ì ì¬ì²¨ë¶íë ê²ì
+ ê·¸ ìì²´ ê´ê³í íìê° ìê±°ë ê·¸ê²ì 모ë ë°ì´í°ë² ì´ì¤ í¸ëìì
ìì ì¸ì¤í´ì¤ë¤ì ë¤ì ë¡ëìí¬ íìê° ìë¤.
+ </para>
+
+ <programlisting><![CDATA[// foo is an instance loaded earlier by the old session
+Transaction t = session.beginTransaction(); // Obtain a new JDBC connection, start transaction
+
+foo.setProperty("bar");
+
+session.flush(); // Only for last transaction in conversation
+t.commit(); // Also return JDBC connection
+session.close(); // Only for last transaction in conversation]]></programlisting>
+ <para>
+ <literal>foo</literal> ê°ì²´ë ê·¸ê²ì´ ë¡ëëìë <literal>Session</literal>ì´ ì´ë ê²ì¸ì§ë¥¼ ì¬ì í ìê³ ìë¤.
+ ì´ì ì¸ì
ììì íëì ìë¡ì´ ë°ì´í°ë² ì´ì¤ í¸ëìì
ì ììíë ê²ì íëì ìë¡ì´ 커ë¥ì
ì íëíê³ ê·¸ ì¸ì
ì ìë¹íë¤.
+ ë°ì´í°ë² ì´ì¤ í¸ëìì
ì 커ë°(íì½)ìí¤ë ê²ì ê·¸ JDBC 커ë¥ì
ì¼ë¡ë¶í° íëì ì¸ì
ì ì°ê²°í´ì ìí¤ê³ ê·¸ 커ë¥ì
ì í(pool)ë¡
+ ë°íìí¬ ê²ì´ë¤. ì¬ì°ê²° íì, ë¹ì ì´ ì
ë°ì´í¸íê³ ìì§ ìì ë°ì´í°ì ëí ë²ì ì²´í¬ë¥¼ ê°ì ìí¤ê¸° ìí´ì, ë¹ì ì
+ ë ë¤ë¥¸ í¸ëìì
ì ìí´ ì
ë°ì´í¸ëìì ìë ìë ììì ê°ì²´ë¤ì ëí´ <literal>LockMode.READ</literal>ë¡ì
+ <literal>Session.lock()</literal>ì í¸ì¶í ìë ìë¤. ë¹ì ì ë¹ì ì´ ì
ë°ì´í¸ ì¤ì¸ ììì ë°ì´í°ë¥¼ ì ê¸í íìê° ìë¤.
+ ëê° ë¹ì ì ë§ì§ë§ ë°ì´í°ë² ì´ì¤ í¸ëìì
주기ë§ì´ ì´ ëí ë´ìì íí´ì§ 모ë ë³ê²½ë¤ì ì¤ì ë¡ ììíìí¤ë ê²ì´ íì©ëëë¡ í기 ìí´,
+ íëì íì¥ë <literal>Session</literal>ì ëí´ <literal>FlushMode.NEVER</literal>를 ì¤ì í ê²ì´ë¤.
+ ê·¸ë¬ë¯ë¡ ì¤ì§ ì´ ë§ì§ë§ ë°ì´í°ë² ì´ì¤ í¸ëìì
ë§ì´ <literal>flush()</literal> ì¤í¼ë ì´ì
ì í¬í¨í ê²ì´ê³ , ëí
+ ëí를 ì¢
ë£ìí¤ê¸° ìí´ ì¸ì
ì <literal>close()</literal>í ê²ì´ë¤.
+ </para>
+
+ <para>
+ ë§ì¼ ì¬ì©ìê° ìê°íëìê° ëì <literal>Session</literal>ì´ ì ì¥ë기ì ë무 í° ê²½ì° ì´ í¨í´ì 문ì ì±ì´ ìë¤. ì를 ë¤ì´
+ <literal>HttpSession</literal>ì ê°ë¥í ìì ê²ì¼ë¡ ì ì§ëì´ì¼ íë¤. ëí <literal>Session</literal>ì (íìì)
+ 첫 ë²ì§¸ ë 벨 ìºìì´ê³ 모ë ë¡ëë ê°ì²´ë¤ì í¬í¨í기 ë문ì, ì°ë¦¬ë ìë§ ì ì ìì²/ìëµ ì£¼ê¸°ë¤ì ëí´ìë§ ì´ ë°©ë를 ì¬ì©í ì ìë¤.
+ ë¹ì ì íëì ëíì ëí´ìë§ íëì <literal>Session</literal>ì ì¬ì©í´ì¼ íë¤. ìëíë©´ ê·¸ê²ì ëí 곧 ì¤ìë ë°ì´í°ê°
+ ë ê²ì´ê¸° ë문ì´ë¤.
+ </para>
+
+ <para>
+ (ì´ê¸°ì Hibernate ë²ì ë¤ì <literal>Session</literal>ì ëí ëª
ìì ì¸ ì°ê²°í´ì ì ì¬ì°ê²°ì íìë¡ íìì
+ ë
¸í¸íë¼. í¸ëìì
ì ììíê³ ëë´ë ê²ì´ ëì¼í í¨ê³¼ë¥¼ ê°ì§ë¯ë¡, ì´ë¤ ë°©ë²ë¤ì ì§ë¶íê² ëìë¤.)
+ </para>
+
+ <para>
+ ëí ë¹ì ì ìì ê³ì¸µì ëí´ ì°ê²°í´ì ë <literal>Session</literal>ì ë«íì§ì±ë¡ ì ì§í´ì¼í¨ì ë
¸í¸íë¼. ë¬
+ 리ë§í´, íëì 3-tier íê²½ìì <literal>Session</literal>ì ìì íëë° EJB stateful session beanì
+ ì¬ì©íê³ , <literal>HttpSession</literal> ë´ì ê·¸ê²ì ì ì¥í기 ìí´ ê·¸ê²ì ì¹ ê³ì¸µì ì ì¡íì§ ë§ë¼
+ (ëë ê·¸ê²ì ë³ëì í°ì´ì ì§ë ¬í ìí¤ì§ë ë§ë¼).
+ </para>
+
+ <para>
+ íì¥ë ì¸ì
í¨í´, ëë <emphasis>session-per-conversation</emphasis>ì ìëì ì¸ íì¬ ì¸ì
컨í
ì¤í¸ ê´ë¦¬ì ëí´
+ 구ííê¸°ê° ë ì´ë µë¤. ë¹ì ì ì´ë¥¼ ìí´ ë¹ì ìì ì <literal>CurrentSessionContext</literal> 구íì ê³µê¸í íìê°
+ ìì¼ë©°, ìì ë¤ì Hibernate Wiki를 ë³´ë¼.
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-optimistic-detached">
+ <title>Detached ê°ì²´ë¤ê³¼ ìëì ì¸ ë²ì í</title>
+
+ <para>
+ ìì ì ì¥ìì ëí ê°ê°ì ìí¸ìì©ì ìë¡ì´ <literal>Session</literal>ìì ì¼ì´ëë¤. íì§ë§ ëì¼í ìì ì¸ì¤í´ì¤ë¤ì
+ ë°ì´í°ë² ì´ì¤ìì ê°ê°ì ìí¸ìì©ì ì¬ì¬ì©ëë¤. ì´í리ì¼ì´ì
ì ìë ë¡ëëìë detached ì¸ì¤í´ì¤ë¤ì ìí를 ë ë¤ë¥¸
+ <literal>Session</literal> ë´ìì ì²ë¦¬íê³ ëì <literal>Session.update()</literal>,
+ <literal>Session.saveOrUpdate()</literal>, <literal>Session.merge()</literal>를 ì¬ì©íì¬ ê·¸ê²ë¤ì
+ ë¤ì 첨ë¶ìí¨ë¤.
+ </para>
+
+ <programlisting><![CDATA[// foo is an instance loaded by a previous Session
+foo.setProperty("bar");
+session = factory.openSession();
+Transaction t = session.beginTransaction();
+session.saveOrUpdate(foo); // Use merge() if "foo" might have been loaded already
+t.commit();
+session.close();]]></programlisting>
+
+ <para>
+ ë¤ì, Hibernateë flush ëìì ì¸ì¤í´ì¤ ë²ì ë¤ì ì²´í¬í ê²ì´ê³ ì
ë°ì´í¸ 충ëì´ ë°ìí ê²½ì°ì ìì¸ìí©ì ëì§ ê²ì´ë¤.
+ </para>
+
+ <para>
+ ë¹ì ì ëí <literal>update()</literal>ëì ì <literal>lock()</literal>ì í¸ì¶í ìë ìê³ ë§ì¼ ê·¸ ê°ì²´ê°
+ ë³ê²½ëì§ ìììì ë¹ì ì´ íì íë ê²½ì°ì (ë²ì ì²´í¹ì ìííê³ ëª¨ë ìºìë¤ì 무ìíë) <literal>LockMode.READ</literal>를
+ ì¬ì©í ì ìë¤.
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-optimistic-customizing">
+ <title>ìëì ì¸ ë²ì í를 ë§ì¶¤í ìí¤ê¸°</title>
+
+ <para>
+ ë¹ì ì <literal>optimistic-lock</literal> 매í ìì±ì <literal>false</literal>ë¡ ì¤ì í¨ì¼ë¡ì¨ í¹ì íë¡í¼í°ë¤ê³¼
+ ì½ë ì
ë¤ì ëí Hibernateì ìëì ì¸ ë²ì ì¦ê°ë¥¼ ë¶ê°ë¥íëë¡ í ìë ìë¤. ê·¸ë Hibernateë ê·¸ íë¡í¼í°ê° dirty ì¼ ê²½ì°ì
+ ë ì´ì ë²ì ì ì¦ê°ìí¤ì§ ìì ê²ì´ë¤.
+ </para>
+
+ <para>
+ 리거ì ë°ì´í°ë² ì´ì¤ ì¤í¤ë§ë¤ì ì주 staticì´ê³ ë³ê²½ë ì ìë¤. ëë ë¤ë¥¸ ì´í리ì¼ì´ì
ë¤ì ëí ëì¼í ë°ì´í°ë² ì´ì¤ì ì ê·¼íê³
+ ë²ì ë²í¸ë¤ ëë ì¬ì§ì´ timestampë¤ì ì²ë¦¬íë ë°©ë²ì 모를 ìë ìë¤. ë ê²½ì°ë¤ìì, ë²ì íë í
ì´ë¸ ë´ì í¹ì 컬ë¼ì ìì§í ì
+ ìë¤. version ëë timestamp íë¡í¼í° 매í ìì´ í ë´ì 모ë íëë¤ì ëí ìí를 ë¹êµíì¬ ë²ì ì²´í¬ë¥¼ ê°ì ìí¤ê¸° ìí´ì,
+ <literal><class></literal> 매í ìì <literal>optimistic-lock="all"</literal>ì íìíë¼. ë§ì¼
+ Hibernateê° ì´ì ìíì ìë¡ì´ ìí를 ë¹êµí ì ìì ê²½ì°ì, ì를 ë¤ë©´ ë¹ì ì´ íëì 긴 <literal>Session</literal>ì
+ ì¬ì©íê³ session-per-request-with-detached-objectsì ì¬ì©íì§ ìì ê²½ì° ì´ê²ì ê°ë
ì ì¼ë¡ë§ ëìí¨ì ë
¸í¸íë¼.
+ </para>
+
+ <para>
+ ëëë¡ íí´ì¡ë ë³ê²½ë¤ì´ ì¤ì²©ëì§ ìë í ëìì ì¸ ë³ê²½ì´ íì©ë ì ìë¤. ë§ì¼ <literal><class></literal>를
+ 매íí ë ë¹ì ì´ <literal>optimistic-lock="dirty"</literal>를 ì¤ì íë©´, Hibernateë flush ëìì dirty íëë¤ì
+ ë¹êµë§ í ê²ì´ë¤.
+ </para>
+
+ <para>
+ ë ê²½ì°ë¤ìì, ì ì© version/timestamp 컬ë¼ì ê²½ì° ëë full/dirty íë ë¹êµì ê²½ì°, Hibernateë ë²ì ì²´í¬ë¥¼ ì¤ííê³
+ ì 보를 ì
ë°ì´í¸íëë° ìí°í° ë¹ (ì ì í <literal>WHERE</literal> ì ì ê°ì§) í ê°ì<literal>UPDATE</literal>
+ 문ì¥ì ì¬ì©íë¤. ë§ì¼ ë¹ì ì´ ì°ê´ë ìí°í°ë¤ì ëí ì¬ì²¨ë¶ë¥¼ ì¼ì¤ì¼ì´ë íëë° transitive ììì ì¬ì©í ê²½ì°,
+ Hibernateë ë¶íìíê² ì
ë°ì´í¸ë¤ì ì¤íí ìë ìë¤. ì´ê²ì ëê° ë¬¸ì ê° ìëì§ë§, ì¬ì§ì´ ë³ê²½ë¤ì´ detached ì¸ì¤í´ì¤ë¤ì
+ ëí´ íí´ì§ì§ ììì ëìë ë°ì´í°ë² ì´ì¤ ë´ìì <emphasis>on update</emphasis> í¸ë¦¬ê±°ë¤ì´ ì¤íë ìë ìë¤. ê·¸ íì
+ ì
ë°ì´í¸í기 ì ì ë³ê²½ë¤ì´ ì¤ì ë¡ ì¼ì´ë¬ìì íì¸í기 ìí´ ì¸ì¤í´ì¤ë¥¼ <literal>SELECT</literal>íë ê²ì Hibernateìê²
+ ê°ì ìí¤ë, <literal><class></literal> 매í ìì <literal>select-before-update="true"</literal>를
+ ì¤ì í¨ì¼ë¡ì¨ ë¹ì ì ì´ í¹ì§ì ë§ì¶¤í ìí¬ ì ìë¤.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="transactions-locking">
+ <title>Pessimistic ì ê¸</title>
+
+ <para>
+ ì¬ì©ìë¤ì ì ê¸ ë°©ëì ëí´ ê±±ì íëë° ë§ì ìê°ì í ì ííë ¤ê³ ìê°íì§ ìëë¤. ëê° JDBC 커ë¥ì
ë¤ì ëí 격리 ë 벨ì ì§ì íë
+ ê²ì¼ë¡ 충ë¶íê³ ê·¸ë° ë¤ì ë¨ìí ë°ì´í°ë² ì´ì¤ë¡ íì¬ê¸ 모ë ìì
ì ííëë¡ íë¤. íì§ë§ ì§ì¼ë³´í ì¬ì©ìë¤ì ëëë¡ ë°°íì ì¸
+ pessimistic ì ê¸ë¤ì ì»ê±°ë ëë ìë¡ì´ í¸ëìì
ì ìì ìì ì ê¸ë¤ì ë¤ì ì»ê³ ì ìí ìë ìë¤.
+ </para>
+
+ <para>
+ Hibernateë ê²°ì½ ë©ëª¨ë¦¬ ë´ì ìë ê°ì²´ë¤ì´ ìë, ë°ì´í°ë² ì´ì¤ì ì ê¸ ë©ì»¤ëì¦ì íì ì¬ì©í ê²ì´ë¤!
+ </para>
+
+ <para>
+ <literal>LockMode</literal> í´ëì¤ë Hibernateì ìí´ íëë ì ìë ë¤ë¥¸ ì ê¸ ë 벨ë¤ì ì ìíë¤. ì ê¸ì ë¤ì ë©ì»¤ëì¦ë¤ì
+ ìí´ ì»ì´ì§ë¤:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>LockMode.WRITE</literal>ë Hibernateê° í íì ì
ë°ì´í¸ íê±°ë insert í ë ìëì ì¼ë¡ íëëë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>LockMode.UPGRADE</literal>ë <literal>SELECT ... FOR UPDATE</literal> 구문ì ì§ìíë
+ ë°ì´í°ë² ì´ì¤ ììì <literal>SELECT ... FOR UPDATE</literal>를 ì¬ì©íì¬ ëª
ìì ì¸ ì¬ì©ì ìì² ììì
+ ì»ì´ì§ ì ìë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>LockMode.UPGRADE_NOWAIT</literal>ë ì¤ë¼í´ìì <literal>SELECT ... FOR UPDATE NOWAIT</literal>를
+ ì¬ì©íì¬ ëª
ìì ì¸ ì¬ì©ì ìì² ììì ì»ì´ì§ ìë ìë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>LockMode.READ</literal>ë Hibernateê° ë°ë³µ ê°ë¥í ì½ê¸°(Repeatable Read) ëë Serialization
+ 격리 ë 벨ìì ë°ì´í°ë¥¼ ì½ì´ë¤ì¼ ë ìëì ì¼ë¡ ì»ì´ì§ ìë ìë¤. ëª
ìì ì¸ ì¬ì©ì ìì²ì ìí´ ë¤ì ì»ì´ì§ ìë ìë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>LockMode.NONE</literal>ì ì ê¸ì´ ììì ëíë¸ë¤. 모ë ê°ì²´ë¤ì <literal>Transaction</literal>ì ëìì
+ ì´ ì ê¸ ëª¨ëë¡ ì íëë¤. <literal>update()</literal> ëë <literal>saveOrUpdate()</literal>ì ëí í¸ì¶ì íµí´
+ ì¸ì
ê³¼ ì°ê´ë ê°ì²´ë¤ì´ ëí ì´ ì ê¸ ëª¨ëë¡ ììëë¤.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ "ëª
ìì ì¸ ì¬ì©ì ìì²"ì ë¤ì ë°©ë²ë¤ ì¤ íëë¡ ííëë¤:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>LockMode</literal>를 ì§ì í <literal>Session.load()</literal>ì ëí í¸ì¶.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>Session.lock()</literal>ì ëí í¸ì¶.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>Query.setLockMode()</literal>ì ëí í¸ì¶.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ë§ì¼ <literal>Session.load()</literal>ê° <literal>UPGRADE</literal> ëë <literal>UPGRADE_NOWAIT</literal>
+ 모ëë¡ í¸ì¶ëê³ ,ìì²ë ê°ì²´ê° ìì§ ì´ ì¸ì
ì ìí´ ë¡ëëì§ ììë¤ë©´, ê·¸ ê°ì²´ë <literal>SELECT ... FOR UPDATE</literal>를
+ ì¬ì©íì¬ ë¡ëëë¤. ë§ì¼ ìì²ë ê²ì´ ìë ë¤ì ì íì ì¸ ì ê¸ì¼ë¡ ì´ë¯¸ ë¡ëëì´ ìë ê°ì²´ì ëí´ <literal>load()</literal>ê°
+ í¸ì¶ë ê²½ì°, Hibernateë ê·¸ ê°ì²´ì ëí´ <literal>lock()</literal>ì í¸ì¶íë¤.
+ </para>
+
+ <para>
+ ë§ì¼ ì§ì ë ì ê¸ ëª¨ëê° <literal>READ</literal>, <literal>UPGRADE</literal> ëë
+ <literal>UPGRADE_NOWAIT</literal> ì¼ ê²½ì°ì <literal>Session.lock()</literal>ì ë²ì ë²í¸ ì²´í¬ë¥¼ ìííë¤.
+ (<literal>UPGRADE</literal> ëë <literal>UPGRADE_NOWAIT</literal> ì¸ ê²½ì°ì,
+ <literal>SELECT ... FOR UPDATE</literal>ê° ì¬ì©ëë¤.)
+ </para>
+
+ <para>
+ ë§ì¼ ë°ì´í°ë² ì´ì¤ê° ìì²ë ì ê¸ ëª¨ë를 ì§ìíì§ ìì ê²½ì°, (ìì¸ìí©ì ëì§ë ëì ì) Hibernateë ì ì í ëì²´ 모ë를 ì¬ì©í ê²ì´ë¤.
+ ì´ê²ì ì´í리ì¼ì´ì
ì´ ì´ì ê°ë¥í ê²ìì íì¤í í´ì¤ë¤.
+ </para>
+
+ </sect1>
+
+ <sect1 id="transactions-connection-release">
+ <title>ì°ê²° í´ì 모ëë¤</title>
+
+ <para>
+ JDBC 커ë¥ì
ê´ë¦¬ì ê´í Hibernateì 리거ì(2.x) í¹ì§ì ê·¸ê²ì´ ì²ìì¼ë¡ íìë¡ íì ë
+ íëì <literal>Session</literal>ì´ íëì 커ë¥ì
ì íëí ê²ì´ê³ , ê·¸ë° ë¤ì ê·¸ 커ë¥ì
ì ê·¸ ì¸ì
ì´
+ ë«íì§ëê¹ì§ ë³´ê´ëë¤ë ê²ì´ìë¤.
+ Hibernate 3.xë ì¸ì
ìê² ê·¸ê²ì JDBC 커ë¥ì
ë¤ì ì²ë¦¬íë ë°©ë²ì ìë ¤ì£¼ê¸° ìí´ ì°ê²° í´ì 모ëë¤ì ê´í
+ ê°ë
ì ëì
íë¤. ë¤ì ë
¼ìë 구ì±ë <literal>ConnectionProvider</literal>를 íµí´ ì ê³µëë 커ë¥ì
ë¤ì
+ ëí´ìë§ ì ì íë¤ë ì ì ë
¸í¸íë¼; ì¬ì©ìê° ì ê³µíë 커ë¥ì
ë¤ì
+ <literal>org.hibernate.ConnectionReleaseMode</literal>ì ì´ê±°ë ê°ë¤ì ìí´ ìë³ëë¤:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>ON_CLOSE</literal> - ë 본ì§ì ì¼ë¡ ìì ì¤ëª
ë 리거ì í¹ì§ì´ë¤. Hibernate ì¸ì
ì ê·¸ê²ì´
+ ì´ë¤ JDBC ì ê·¼ì ìííê³ ì¸ì
ì´ ë«íì§ ëê¹ì§ ê·¸ 커ë¥ì
ì ë³´ê´í íìê° ìì ë íëì 커ë¥ì
ì íëíë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>AFTER_TRANSACTION</literal> - ì íëì
+ <literal>org.hibernate.Transaction</literal>ì´ ìë£ë íì ì°ê²°ë¤ì í´ì íë¼ê³ ë§íë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>AFTER_STATEMENT</literal> (ëí ì ê·¹ì ì¸ í´ì ë¼ê³ ì¸ê¸ë¨) - ë ê°ê°ì 모ë ë¬¸ì¥ ì¤í íì
+ 커ë¥ì
ë¤ì í´ì íë¼ê³ ë§íë¤. ì´ ì ê·¹ì ì¸ í´ì ë ê·¸ 문ì¥ì´ 주ì´ì§ ì¸ì
ê³¼ ì°ê´ë 리ìì¤ë¤ì ì´ë ¤ì§ì±ë¡ ë¨ê²¨ë
+ ê²½ì°ìë ê±´ëë°ê²(skip) ëë¤; íì¬ ì´ê²ì´ ì¼ì´ëë ì ì¼í ìí©ì
+ <literal>org.hibernate.ScrollableResults</literal>ì ì¬ì©ì íµí´ìì´ë¤.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ì¬ì©í í´ì 모ë를 ì§ì í기 ìí´ êµ¬ì± íë¼ë¯¸í° <literal>hibernate.connection.release_mode</literal>ê° ì¬ì©ëë¤.
+ ê°ë¥í ê°ë¤ì ë¤ìê³¼ ê°ë¤:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>auto</literal> (ëí´í¸) - ì´ ì íì
+ <literal>org.hibernate.transaction.TransactionFactory.getDefaultReleaseMode()</literal>
+ ë©ìëì ìí´ ë°íë í´ì 모ëë¡ ìììí¨ë¤. JTATransactionFactoryì¸ ê²½ì°, ì´ê²ì
+ ConnectionReleaseMode.AFTER_STATEMENT를 ë°ííë¤; JDBCTransactionFactoryì¸ ê²½ì°, ì´ê²ì
+ ConnectionReleaseMode.AFTER_TRANSACTIONì ë°ííë¤. ì´ ì¤ì ì ê°ì´ ì¬ì©ì ì½ë ë´ì ë²ê·¸ë¤ ê·¸ë¦¬ê³ /ëë
+ ì í¨íì§ ìì ê°ì ë¤ì ê°ë¦¬í¤ë ê²½í¥ì´ ììì¼ë¡ ì¸í´ ì´ ëí´í¸ í¹ì§ì ì¤í¨ë¡ ë³ê²½íë ê²ì ê±°ì ì¢ì ìê°ì´ ìëë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>on_close</literal> - ë ConnectionReleaseMode.ON_CLOSE를 ì¬ì©íë¼ê³ ë§íë¤. ì´ ì¤ì ì
+ ìí¸íì±ì ìí´ ë¨ê²¨ì¡ì§ë§, ê·¸ê²ì ì¬ì©ì ë§¤ì° ê¶ì¥ëì§ ìëë¤.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>after_transaction</literal> - ì ConnectionReleaseMode.AFTER_TRANSACTIONì ì¬ì©íë¼ê³ ë§íë¤.
+ ì´ ì¤ì ì JTA íê²½ë¤ìì ì¬ì©ëì§ ìì ê²ì´ë¤. ëí
+ ConnectionReleaseMode.AFTER_TRANSACTIONì¸ ê²½ì°ì ë§ì¼ ì¸ì
ì´ auto-commit 모ëì ìëë¡ ê³ ë ¤ë ê²½ì°,
+ 커ë¥ì
ë¤ì ë§ì¹ í´ì 모ëê° AFTER_STATEMENTì¸ ê²ì²ë¼ í´ì ë ê²ìì ëí ë
¸í¸íë¼.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>after_statement</literal> - ë ConnectionReleaseMode.AFTER_STATEMENT를 ì¬ì©íë¼ê³ ë§íë¤.
+ ì¶ê°ì ì¼ë¡ 구ì±ë <literal>ConnectionProvider</literal>ë ê·¸ê²ì´ ì´ ì¤ì
+ (<literal>supportsAggressiveRelease()</literal>)ì ì§ìíëì§ ì¬ë¶ë¥¼ ì기 ìí´ ì°¸ê³ ëë¤.
+ ë§ì¼ ì§ìíì§ ìì ê²½ì°, í´ì 모ëë ConnectionReleaseMode.AFTER_TRANSACTIONì¼ë¡ ì¬ì¤ì ëë¤. ì´ ì¤ì ì
+ ì°ë¦¬ê° <literal>ConnectionProvider.getConnection()</literal>ì í¸ì¶í ëë§ë¤ ì°ë¦¬ê° ëì¼í 기본 JDBC
+ 커ë¥ì
ì ë¤ì íìë¡ í ì ìë íê²½ë¤ìì ëë ì°ë¦¬ê° ëì¼í 커ë¥ì
ì ì»ë ê²ì ìê´ìë auto-commit íê²½ìì ì¤ì§
+ ìì íë¤.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/tutorial.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/tutorial.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/tutorial.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1486 @@
+<chapter id="tutorial">
+ <title>Hibernate ê°ì</title>
+
+ <sect1 id="tutorial-intro" revision="1">
+ <title>머리ë§</title>
+
+ <para>
+ ì´ ì¥ì Hibernate ì´ì¬ì를 ìí ê°ë¡ ì ì¸ íí 리ì¼ì´ë¤. ì°ë¦¬ë ë©ëª¨ë¦¬-ë´ ë°ì´í°ë² ì´ì¤ë¥¼
+ ì¬ì©íë ê°ë¨í ëª
ë ¹ ë¼ì¸ ì´í리ì¼ì´ì
ì¼ë¡ ììíê³ ë¨ê³ë¤ì ì´í´íëë¡ ì½ê² ê·¸ê²ì ê°ë°íë¤.
+ </para>
+
+ <para>
+ ì´ íí 리ì¼ì Hibernate ì ê· ì¬ì©ìë¤ì ìëíê³ ìì§ë§ Javaì SQL ì§ìì íìë¡ íë¤.
+ ê·¸ê²ì Michael Gloeglì´ ìì±í íí 리ì¼ì 기ì´íë©°, ì°ë¦¬ê° ëª
ëª
íë ì 3ì ë¼ì´ë¸ë¬ë¦¬ë¤ì
+ JDK 1.4ì 5.0 ë²ì ì©ì´ë¤. ë¹ì ì JDK1.3ì ëí´ ë¤ë¥¸ ë¼ì´ë¸ë¬ë¦¬ë¤ì íìë¡ í ìë ìë¤.
+ </para>
+
+ <para>
+ íí 리ì¼ì© ìì¤ë <literal>doc/reference/tutorial/</literal> ëë í 리 ë´ì ìë
+ ë°°í¬ë³¸ ë´ì í¬í¨ëì´ ìë¤.
+ </para>
+
+ </sect1>
+
+ <sect1 id="tutorial-firstapp" revision="2">
+ <title>íí¸ 1 - 첫 ë²ì§¸ Hibernate ì´í리ì¼ì´ì
</title>
+
+ <para>
+ 먼ì , ì°ë¦¬ë í ê°ì ê°ë¨í ì½ì-ê¸°ë° Hibernate ì´í리ì¼ì´ì
ì ìì±ìí¬ ê²ì´ë¤. ì°ë¦¬ë ë©ëª¨ë¦¬-ë´
+ ë°ì´í°ë² ì´ì¤(HSQL DB)를 ì¬ì©íë¯ë¡, ì°ë¦¬ë ì´ë¤ ë°ì´í°ë² ì´ì¤ ìë²ë¥¼ ì¤ì¹íì§ ììë ëë¤.
+ </para>
+
+ <para>
+ ì°ë¦¬ê° ì°ë¦¬ê° ìë°íê³ ì ìíë ì´ë²¤í¸ë¤ì ì ì¥í ì ìë ìì ë°ì´í°ë² ì´ì¤ ì´í리ì¼ì´ì
ê³¼ ì´ë¤
+ ì´ë²¤í¸ë¤ì í¸ì¤í¸ë¤ì ëí ì 보를 íìë¡ íë¤ê³ ê°ì íì.
+ </para>
+
+ <para>
+ ì°ë¦¬ê° íí 첫 ë²ì§¸ ê²ì ì°ë¦¬ì ê°ë° ëë í 리를 ì¤ì íê³ , ì°ë¦¬ê° íìë¡ íë 모ë Java ë¼ì´ë¸ë¬ë¦¬ë¤ì
+ ê·¸ê² ìì ì§ì´ ë£ë ê²ì´ë¤. Hibernate ì¹ ì¬ì´í¸ë¡ë¶í° Hibernate ë°°í¬ë³¸ì ë´ë ¤ ë°ìë¼. í¨í¤ì§ë¥¼
+ ì¶ì¶í´ë´ê³ <literal>/lib</literal> ììì ë°ê²¬ëë 모ë íìí ë¼ì´ë¸ë¬ë¦¬ë¤ì ë¹ì ì ìë¡ì´ ê°ë° ìì
+ ëë í 리ì <literal>/lib</literal> ëë í 리 ìì ìì¹ì§ìë¼. ê·¸ê²ì ë¤ìê³¼ ê°ì ê²ì´ë¤:
+ </para>
+
+ <programlisting><![CDATA[.
++lib
+ antlr.jar
+ cglib.jar
+ asm.jar
+ asm-attrs.jars
+ commons-collections.jar
+ commons-logging.jar
+ hibernate3.jar
+ jta.jar
+ dom4j.jar
+ log4j.jar ]]></programlisting>
+
+ <para>
+ ì´ê²ì <emphasis>ê¸ì ìì± ìì ìì</emphasis> Hibernateì íìì ì¸ ìµìíì ì¸í¸ì´ë¤(ì°ë¦¬ë
+ ëí ë©ì¸ ìì¹´ì´ë¸ì¸ hibernate3.jar를 ë³µì¬íìì ë
¸í¸íë¼). ë¹ì ì´ ì¬ì© ì¤ì¸ Hibernate ë°°í¬ë³¸ì´
+ ë ë§ê±°ë ë³´ë¤ ì ì ë¼ì´ë¸ë¬ë¦¬ë¤ì íìë¡ í ìë ìë¤. íì ë¼ì´ë¸ë¬ë¦¬ë¤ê³¼ ì íì ì¸ ì 3ì ë¼ì´ë¸ë¬ë¦¬ë¤ì ëí
+ ì¶ê° ì ë³´ë Hibernate ë°°í¬ë³¸ì <literal>lib/</literal> ëë í 리 ë´ì ìë <literal>README.txt</literal>
+ íì¼ì ë³´ë¼. (ì¤ì ë¡, Log4jë íìë ìëì§ë§ ë§ì ê°ë°ìë¤ì ìí´ ì í¸ëë¤.)
+ </para>
+
+ <para>
+ ë¤ìì¼ë¡ ì°ë¦¬ë ì°ë¦¬ê° ë°ì´í°ë² ì´ì¤ ìì ì ì¥ìí¤ê³ ì ìíë ì´ë²¤í¸ë¥¼ íííë í ê°ì í´ëì¤ë¥¼ ìì±ìí¨ë¤.
+ </para>
+
+ <sect2 id="tutorial-firstapp-firstclass" revision="1">
+ <title>첫 ë²ì§¸ í´ëì¤</title>
+
+ <para>
+ ì°ë¦¬ì 첫 ë²ì§¸ ìì í´ëì¤ë ëªëª íë¡í¼í°ë¤ì ê°ì§ ê°ë¨í ìë°ë¹ì¦ í´ëì¤ì´ë¤:
+ </para>
+
+ <programlisting><![CDATA[package events;
+
+import java.util.Date;
+
+public class Event {
+ private Long id;
+
+ private String title;
+ private Date date;
+
+ public Event() {}
+
+ public Long getId() {
+ return id;
+ }
+
+ private void setId(Long id) {
+ this.id = id;
+ }
+
+ public Date getDate() {
+ return date;
+ }
+
+ public void setDate(Date date) {
+ this.date = date;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+}]]></programlisting>
+
+ <para>
+ ë¹ì ì ì´ í´ëì¤ê° íë¡í¼í° getterì setter ë©ìëë¤ì ëí íì¤ ìë°ë¹ì¦ ëª
ëª
ë²ë¤ ë¿ë§ ìëë¼ íëë¤ì ëí
+ private ê°ìì±ì ì¬ì©íê³ ììì ì ì ìë¤. ì´ê²ì ê¶ì¥ëë ì¤ê³ì´ì§ë§, íìì ì´ì§ë ìë¤. Hibernateë
+ ëí íëë¤ì ì§ì ì ê·¼í ì ìì¼ë©°, accessor ë©ìëë¤ì ì´ì ì ê°ê±´í 리í©í ë§ì´ë¤. ìê·ë¨¼í¸ ìë ìì±ìë
+ reflectionì íµí´ ì´ í´ëì¤ì ê°ì²´ë¥¼ ì´ê¸°í ìí¬ íìê° ìë¤.
+ </para>
+
+ <para>
+ <literal>id</literal> íë¡í¼í°ë í¹ë³í ì´ë²¤í¸ë¥¼ ìí ì ì¼ ìë³ì를 ìì íë¤. 모ë ìì ìí°í° í´ëì¤ë¤
+ (ë³´ë¤ ë ì¤ìí ì¢
ì í´ëì¤ë¤ë ì¡´ì¬íë¤)ì ì°ë¦¬ê° Hibernateì ì ì²´ í¹ì§ ì§í©ì ì¬ì©íê³ ì ìí ê²½ì°ì
+ ê·¸ë° ìë³ì íë¡í¼í°ë¥¼ íìë¡ í ê²ì´ë¤. ì¬ì¤ ëë¶ë¶ì ì´í리ì¼ì´ì
ë¤(í¹í ì¹ ì´í리ì¼ì´ì
ë¤)ì ìë³ìì ìí´
+ ê°ì²´ë¤ì 구ë¶ì§ì íìê° ìì´ì, ë¹ì ì ì´ê²ì ì´ë¤ ì ì½ì ì´ë¼ê¸° ë³´ë¤ë íëì í¹ì§ì¼ë¡ ê°ì£¼í ê²ì´ë¤. íì§ë§
+ ì°ë¦¬ë ëê° ê°ì²´ì íë±(identity)를 ì²ë¦¬íì§ ìì¼ë¯ë¡, setter ë©ìëë privateì´ì´ì¼ íë¤. ê°ì²´ê° ì ì¥ë ë,
+ Hibernateë ë¨ì§ ìë³ìë¤ì í ë¹í ê²ì´ë¤. ë¹ì ì Hibernateê° public, private, protected ì ê·¼ì ë©ìëë¤
+ ë¿ë§ ìëë¼ (public, private, protected) íëë¤ìë ì§ì ì ê·¼í ì ììì ì ì ìë¤. ì íì ë¹ì ìê² ë¬ë ¤
+ ìì¼ë©°, ë¹ì ì ë¹ì ì ì´í리ì¼ì´ì
ì¤ê³ì ì í©íëë¡ ê·¸ê²ì ë¶í©ìí¬ ì ìë¤.
+ </para>
+
+ <para>
+ ìê·ë¨¼í¸ ìë ìì±ìë 모ë ìì í´ëì¤ë¤ì ëí íìì¡°ê±´ì´ë¤; Hibernateë ë¹ì ì ìí´ Java Reflectionì
+ ì¬ì©íì¬ ê°ì²´ë¤ì ìì±ìì¼ì¼ íë¤. íì§ë§ ìì±ìë private ì¼ ì ìê³ , í¨í¤ì§ ê°ìì±ì ë°íì íë½ì ìì±ê³¼
+ ë°ì´í¸ì½ë ë°©í¸ ìë í¨ì¨ì ì¸ ë°ì´í° ê²ìì íìíë¤.
+ </para>
+
+ <para>
+ ì´ Java ìì¤ íì¼ì ê°ë° í´ë ë´ì <literal>src</literal>ë¡ ëª
ëª
ë ëë í 리 ìì ìë ìì¹ì§ìë¼. ì´ì
+ ê·¸ ëë í 리ë ë¤ìê³¼ ê°ì ê²ì´ë¤:
+ </para>
+
+ <programlisting><![CDATA[.
++lib
+ <Hibernate and third-party libraries>
++src
+ +events
+ Event.java]]></programlisting>
+
+ <para>
+ ë¤ì ë¨ê³ìì, ì°ë¦¬ë Hiberanteìê² ì´ ìì í´ëì¤ì ëí´ ìë ¤ ì¤ë¤.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-mapping" revision="1">
+ <title>The mapping file</title>
+
+ <para>
+ Hibernateë ìì í¬ëì¤ë¤ì ëí ê°ì²´ë¤ì ë¡ëìí¤ê³ ì ì¥ìí¤ë ë°©ë²ì ì íìê° ìë¤.
+ ì´ê³³ì Hibernate 매í íì¼ì´ ìí ì ííë ê³³ì´ë¤. 매í íì¼ì Hibernateê° ì ê·¼í´ì¼ íë
+ ë°ì´í°ë² ì´ì¤ ë´ì í
ì´ë¸ì´ 무ìì¸ì§, ê·¸ë¦¬ê³ ê·¸ê²ì´ ì¬ì©í´ì¼ íë ê·¸ í
ì´ë¸ ë´ì 컬ë¼ë¤ì´
+ 무ìì¸ì§ë¥¼ Hibernateìê² ìë ¤ì¤ë¤.
+ </para>
+
+ <para>
+ 매í íì¼ì 기본 구조ë ë¤ìê³¼ ê°ë¤:
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping>
+[...]
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Hibernate DTDë ë§¤ì° ì êµíë¤. ë¹ì ì ë¹ì ì í¸ì§ê¸° ëë IDE ë´ìì XML 매í ììë¤ê³¼ ìì±ë¤ì
+ ëí ìë ìì± ê¸°ë¥ì ìí´ ê·¸ê²ì ì¬ì©í ì ìë¤. ë¹ì ì ëí ë¹ì ì í
ì¤í¸ í¸ì§ê¸° ë´ì DTD íì¼ì
+ ì´ ì ìì ê²ì´ë¤ - ê·¸ê²ì 모ë ììë¤ê³¼ ìì±ë¤ì ëí ì ì²´ìì ì»ê³ ëí´í¸ë¤ ë¿ë§ ìëë¼ ëªëª 주ìë¤ì
+ ë³´ë ê°ì¥ ìì¬ì´ ë°©ë²ì´ë¤. Hibernateë ì¹ì¼ë¡ë¶í° DTD íì¼ì ë¡ëìí¤ì§ ìì§ë§, 먼ì ì´í리ì¼ì´ì
ì
+ classpath ê²½ë¡ë¡ë¶í° ê·¸ê²ì 먼ì 룩ì
í ê²ìì ë
¸í¸íë¼. DTD íì¼ì <literal>hibernate3.jar</literal>
+ ìì í¬í¨ëì´ ìì ë¿ë§ ìëë¼ Hibernate ë°°í¬ë³¸ì <literal>src/</literal> ëë í 리 ìì í¬í¨ëì´
+ ìë¤.
+ </para>
+
+ <para>
+ ì°ë¦¬ë ì½ë를 ê°ëµí ìí¤ê¸° ìí´ ì¥ëì ìì ìì DTD ì ì¸ì ìëµí ê²ì´ë¤. ê·¸ê²ì ë¬¼ë¡ ìµì
ì´ ìëë¤.
+ </para>
+
+ <para>
+ ë ê°ì <literal>hibernate-mapping</literal> íê·¸ë¤ ì¬ì´ì <literal>class</literal> ìì를
+ í¬í¨ìì¼ë¼. 모ë ìì ìí°í° í´ëì¤ë¤(ë¤ìê¸ ì¢
ì í´ëì¤ë¤ì¼ ì ìê³ , ê·¸ê²ì 첫ë²ì§¸-ê¸ì ìí°í°ë¤ì´ ìëë¤)ì
+ SQL ë°ì´í°ë² ì´ì¤ ë´ì í
ì´ë¸ì ëí ê·¸ë° ë§¤íì íìë¡ íë¤:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="events.Event" table="EVENTS">
+
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ ì§ê¸ê¹ì§ ì°ë¦¬ë ê·¸ í
ì´ë¸ ë´ì ìë í íì ìí´ ííë ê°ê°ì ì¸ì¤í´ì¤ì¸, í´ëì¤ì ê°ì²´ë¥¼ ììí ìí¤ê³
+ ë¡ëìí¤ë ë°©ë²ì Hibernateìê² ìë ¤ì£¼ìë¤. ì´ì ì°ë¦°ë í
ì´ë¸ íë¼ì´ë¨¸ë¦¬ í¤ì ëí ì ì¼ ìë³ì íë¡í¼í° 매íì
+ ê³ì ííë¤. ê²ë¤ê° ì°ë¦¬ë ì´ ìë³ì를 ì²ë¦¬íë ê²ì 주ì를 기ì¸ì´ê³ ì ìíì§ ìì¼ë¯ë¡, ì°ë¦¬ë ëì© í¤ íë¼ì´ë¨¸ë¦¬ í¤
+ 컬ë¼ì ëí Hibernateì ìë³ì ìì± ë°©ë를 구ì±íë¤:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="events.Event" table="EVENTS">
+ <id name="id" column="EVENT_ID">
+ <generator class="native"/>
+ </id>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ <literal>id</literal> ììë ìë³ì íë¡í¼í°ì ì ì¸ì´ê³ , <literal>name="id"</literal>ë
+ Java íë¡í¼í°ì ì´ë¦ì ì ì¸íë¤ - Hibernateë ê·¸ íë¡í¼í°ì ì ê·¼íëë° getter ë° setter ë©ìëë¤ì
+ ì¬ì©í ê²ì´ë¤. column ìì±ì ì°ë¦¬ê° <literal>EVENTS</literal> í
ì´ë¸ì ì´ë 컬ë¼ì ì´ íë¼ì´ë¨¸ë¦¬ í¤ë¡
+ ì¬ì©íëì§ë¥¼ Hibernateìê² ìë ¤ì¤ë¤. ë´í¬ë <literal>generator</literal> ììë ìë³ì ìì± ë°©ë를
+ ì§ì íë©°, ì´ ê²½ì°ì ì°ë¦¬ë <literal>increment</literal>를 ì¬ì©íê³ , ê·¸ê²ì ëê° í
ì¤í
(ê³¼ íí 리ì¼ë¤)ì
+ ì ì©í ë§¤ì° ê°ë¨í ë©ëª¨ë¦¬-ë´ ì«ì ì¦ê° ë°©ë²ì´ë¤. Hibernateë ëí ì ìì ì¼ë¡ ì ì¼í ë°ì´í°ë² ì´ì¤ì ìí´ ìì±ë
+ ìë³ì ë¿ë§ ìëë¼ ì´í리ì¼ì´ì
ì ìí´ í ë¹ë ìë³ì(ëë ë¹ì ì´ íì¥ì¼ë¡ ìì±í ì´ë¤ ë°©ë)를 ì§ìíë¤.
+ </para>
+
+ <para>
+ ë§ì§ë§ì¼ë¡ ì°ë¦¬ë 매í íì¼ ììì í´ëì¤ì ìì íë¡í¼í°ë¤ì ëí ì ì¸ë¤ì í¬í¨íë¤. ëí´í¸ë¡, í´ëì¤ì
+ íë¡í¼í°ë¤ì ììì ì¸ ê²ì¼ë¡ ê°ì£¼ëì§ ìëë¤:
+ </para>
+
+ <programlisting><![CDATA[
+<hibernate-mapping>
+
+ <class name="events.Event" table="EVENTS">
+ <id name="id" column="EVENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="date" type="timestamp" column="EVENT_DATE"/>
+ <property name="title"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ <literal>id</literal> ììì ê²½ì°ì²ë¼, <literal>property</literal> ììì <literal>name</literal>
+ ìì±ì ì¬ì©í getter ë° setter ë©ìëë¤ì´ ì´ë ê²ì¸ì§ë¥¼ Hibernateìê² ìë ¤ì¤ë¤. ë°ë¼ì ì´ ê²½ì°ì Hibernateë
+ <literal>getDate()/setDate()</literal> ë¿ë§ ìëë¼ <literal>getTitle()/setTitle()</literal>ì
+ ì°¾ê² ë ê²ì´ë¤.
+ </para>
+
+ <para>
+ <literal>date</literal> íë¡í¼í° 매íì <literal>column</literal> ìì±ì í¬í¨íëë°,
+ ì <literal>title</literal>ì <literal>column</literal> ìì±ì í¬í¨íì§ ìëê°?
+ <literal>column</literal> ìì±ì´ ìì ê²½ì° Hibernateë ëí´í¸ë¡ ì»¬ë¼ ì´ë¦ì¼ë¡ì íë¡í¼í° ì´ë¦ì
+ ì¬ì©íë¤. ì´ê²ì ì ëí´ ì ëìíë¤. íì§ë§ <literal>date</literal>ë ëë¶ë¶ì ë°ì´í°ë² ì´ì¤ìì
+ ìì½ë í¤ìëì´ì´ì, ì°ë¦¬ë ê·¸ê²ì ë¤ë¥¸ ì´ë¦ì¼ë¡ ë ì¢ê² 매í ìí¨ë¤.
+ </para>
+
+ <para>
+ ë¤ì í¥ë¯¸ë¡ì´ ì ì <literal>title</literal> 매í ëí <literal>type</literal> ìì±ì ê°ì§ ìëë¤.
+ ì°ë¦¬ê° 매ííì¼ë¤ ììì ì ì¸íê³ ì¬ì©íë íì
ë¤ì ë¹ì ì´ ììíë Java ë°ì´í° íì
ë¤ì´ ìëë¤. ê·¸ê²ë¤ì
+ ëí SQL ë°ì´í°ë² ì´ì¤ íì
ë¤ë ìëë¤. ì´ë¤ íì
ë¤ì ì´ë¥¸ë° <emphasis>Hibernate 매í íì
ë¤</emphasis>,
+ ì¦ Java íì
ë¤ë¡ë¶í° SQL íì
ë¤ë¡ ë³íë ì ìê³ ë°ëë¡ SQL íì
ë¤ë¡ë¶í° Java íì
ë¤ë¡ 매íë ì ìë 컨ë²í°ë¤ì´ë¤.
+ ë¤ìë§í´, <literal>type</literal> ìì±ì´ 매í ìì ì¡´ì¬íì§ ìì ê²½ì° Hibernateë ì íí ë³í ë° ë§¤í íì
+ ê·¸ ì체를 ê²°ì íë ¤ê³ ìëí ê²ì´ë¤. ëªëª ê²½ì°ë¤ìì (Java í´ëì¤ì ëí Reflectionì ì¬ì©íë) ì´ ìëì ì¸ ê²ì¶ì
+ ë¹ì ì´ ììíê±°ë íìë¡ íë ëí´í¸ë¥¼ ê°ì§ ìì ìë ìë¤. ì´ê²ì <literal>date</literal> íë¡í¼í°ë¥¼ ê°ì§
+ ê²½ì°ì´ë¤. Hibernateë ê·¸ íë¡í¼í°ê° SQL <literal>date</literal> 컬ë¼, <literal>timestamp</literal>
+ ì»¬ë¼ ëë <literal>time</literal> ì»¬ë¼ ì¤ ì´ë ê²ì¼ë¡ 매íëì´ì¼ íëì§ë¥¼ ì ìê° ìë¤. ì°ë¦¬ë
+ <literal>timestamp</literal> 컨ë²í°ë¥¼ ê°ì§ íë¡í¼í°ë¥¼ 매íí¨ì¼ë¡ì¨ ì ì²´ ë ì§ì ìê° ì 보를 ë³´ì¡´íê³ ì¶ë¤ê³
+ ì ì¸íë¤.
+ </para>
+
+ <para>
+ ë¤ì 매í íì¼ì <literal>Event</literal> Java í´ëì¤ ìì¤ íì¼ê³¼ ê°ì ëë í 리 ìì
+ <literal>Event.hbm.xml</literal>ë¡ì ì ì¥ë ê²ì´ë¤. 매í íì¼ë¤ì ëí ë¤ì´ë°ì ììì ì¼ ì
+ ìì§ë§, ì ë¯¸ì¬ <literal>hbm.xml</literal>ì Hibernate ê°ë°ì ê³µëì²´ ë´ìì 컨벤ì
ì´ ëìë¤.
+ ëë í 리 구조ë ì´ì ë¤ìê³¼ ê°ì ê²ì´ë¤:
+ </para>
+
+ <programlisting><![CDATA[.
++lib
+ <Hibernate and third-party libraries>
++src
+ +events
+ Event.java
+ Event.hbm.xml]]></programlisting>
+
+ <para>
+ ì°ë¦¬ë Hibernateì ë©ì¸ 구ì±ì ê³ì ííë¤.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-configuration" revision="2">
+ <title>Hibernate 구ì±</title>
+
+ <para>
+ ì°ë¦¬ë ì´ì ì ì í ê³³ì í ê°ì ìì í´ëì¤ì ê·¸ê²ì 매í íì¼ì ê°ê³ ìë¤. Hibernate를 구ì±í ì°¨ë¡ì´ë¤.
+ ì°ë¦¬ê° ì´ê²ì íí기 ì ì, ì°ë¦¬ë ë°ì´í°ë² ì´ì¤ë¥¼ íìë¡ í ê²ì´ë¤. ìë° ê¸°ë°ì ë©ëª¨ë¦¬-ë´ SQL DBMSì¸
+ HSQL DBë HSQL DB ì¹ ì¬ì´í¸ìì ë´ë ¤ë°ì ì ìë¤. ì¤ì ë¡, ë¹ì ì ì´ ë¤ì´ë¡ëìì ì¤ì§ <literal>hsqldb.jar</literal>
+ ë§ì íìë¡ íë¤. ê°ë° í´ëì <literal>lib/</literal> ëë í 리 ìì ì´ íì¼ì ìì¹ì§ìë¼.
+ </para>
+
+ <para>
+ ê°ë° ëë í 리ì 루í¸ì <literal>data</literal>ë¡ ëª
ëª
ë ëë í 리를 ìì±ìì¼ë¼ - ì´ ëë í 리ë
+ HSQL DBê° ê·¸ê²ì ë°ì´í° íì¼ë¤ì ì ì¥íê² ë ì¥ìì´ë¤. ì´ì ì´ ë°ì´í° ëë í 리ìì
+ <literal>java -classpath ../lib/hsqldb.jar org.hsqldb.Server</literal>를 ì¤íìì¼ì
+ ë°ì´í°ë² ì´ì¤ë¥¼ ìììì¼ë¼. ë¹ì ì ê·¸ê²ì´ ììëê³ ì´ê²ì ì°ë¦¬ì ì´í리ì¼ì´ì
ì´ ëì¤ì ì°ê²°íê² ë ì¥ìì¸,
+ íëì TCP/IP ìì¼ì ë°ì¸ë ëë ê²ì ë³¼ ì ìë¤. ë§ì¼ ì´ íí ë¦¬ì¼ ëìì ë¹ì ì´ ì ë°ì´í°ë² ì´ì¤ë¡
+ ììíê³ ì ìí ê²½ì°, HSQL DB를 ì
§ë¤ì´ìí¤ê³ (ì¼ëì°ìì <literal>CTRL + C</literal>를 ëë¬ë¼),
+ <literal>data/</literal> ëë í 리 ë´ì ìë 모ë íì¼ë¤ì ìì íê³ ë¤ì HSQL DB를 ììíë¼.
+ </para>
+
+ <para>
+ Hibernateë ë¹ì ì ì´í리ì¼ì´ì
ë´ìì ì´ ë°ì´í°ë² ì´ì¤ì ì°ê²°íë ê³ì¸µì´ê³ , ë°ë¼ì ê·¸ê²ì 커ë¥ì
ì 보를
+ íìë¡ íë¤. 커ë¥ì
ë¤ì ë§ì°¬ê°ì§ë¡ 구ì±ëì´ì¼ íë íëì JDBC 커ë¥ì
íì íµí´ íí´ì§ë¤. Hibernate
+ ë°°í¬ë³¸ì ëªëª ì¤í ìì¤ JDBC 커ë¥ì
íë§ ë구ë¤ì í¬í¨íê³ ìì§ë§, ì´ íí 리ì¼ììë Hibernateì ìí´
+ 미리 ë¹ëë 커ë¥ì
íë§ì ì¬ì©í ê²ì´ë¤. ë¹ì ì´ íì ë¼ì´ë¸ë¬ë¦¬ë¥¼ ë¹ì ì classpath ìì ë³µì¬í´ì¼ íê³
+ ë§ì¼ ë¹ì ì´ ì í-í¹ì§ì ì 3ì JDBC íë§ ìíí¸ì¨ì´ë¥¼ ì¬ì©íê³ ì ìí ê²½ì°ìë ë¤ë¥¸ 커ë¥ì
íë§ ì¤ì ë¤ì
+ ì¬ì©í´ì¼ í¨ì ë
¸í¸íë¼.
+ </para>
+
+ <para>
+ Hibernateì 구ì±ì ìí´, ì°ë¦¬ë í ê°ì ê°ë¨í <literal>hibernate.properties</literal> íì¼,
+ í ê°ì ì½ê° ë ì¸ë ¨ë <literal>hibernate.cfg.xml</literal> íì¼, ëë ì¬ì§ì´ ìì í íë¡ê·¸ë¨
+ ìì ì¤ì ì ì¬ì©í ì ìë¤. ëë¶ë¶ì ì¬ì©ìë¤ì XMl êµ¬ì± íì¼ì ì í¸íë¤:
+ </para>
+
+ <programlisting><![CDATA[<?xml version='1.0' encoding='utf-8'?>
+<!DOCTYPE hibernate-configuration PUBLIC
+ "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
+
+<hibernate-configuration>
+
+ <session-factory>
+
+ <!-- Database connection settings -->
+ <property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
+ <property name="connection.url">jdbc:hsqldb:hsql://localhost</property>
+ <property name="connection.username">sa</property>
+ <property name="connection.password"></property>
+
+ <!-- JDBC connection pool (use the built-in) -->
+ <property name="connection.pool_size">1</property>
+
+ <!-- SQL dialect -->
+ <property name="dialect">org.hibernate.dialect.HSQLDialect</property>
+
+ <!-- Enable Hibernate's automatic session context management -->
+ <property name="current_session_context_class">thread</property>
+
+ <!-- Disable the second-level cache -->
+ <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
+
+ <!-- Echo all executed SQL to stdout -->
+ <property name="show_sql">true</property>
+
+ <!-- Drop and re-create the database schema on startup -->
+ <property name="hbm2ddl.auto">create</property>
+
+ <mapping resource="events/Event.hbm.xml"/>
+
+ </session-factory>
+
+</hibernate-configuration>]]></programlisting>
+
+ <para>
+ ì´ XML 구ì±ì´ ë¤ë¥¸ DTD를 ì¬ì©í¨ì ë
¸í¸íë¼. ì°ë¦¬ë Hibernateì <literal>SessionFactory</literal>
+ -í¹ì ë°ì´í°ë² ì´ì¤ì ëí´ ì±
ìì´ ìë ì ì í©í 리-를 구ì±íë¤. ë§ì¼ ë¹ì ì´ ì¬ë¬ ë°ì´í°ë² ì´ì¤ë¤ì
+ ê°ê³ ìë¤ë©´, (ë³´ë¤ ì¬ì´ ììì ìí´) ëª ê°ì êµ¬ì± íì¼ë¤ ìì ì¬ë¬ ê°ì <literal><session-factory></literal>
+ 구ì±ë¤ì ì¬ì©íë¼.
+ </para>
+
+ <para>
+ ì²ì ë¤ ê°ì <literal>property</literal> ììë¤ì JDBC 커ë¥ì
ì ìí íì 구ì±ì í¬í¨íë¤.
+ dialect <literal>property</literal> ììë Hibernateê° ë°ììí¤ë í¹ë³í SQL ì´í(ç°å½¢)ì
+ ì§ì íë¤.
+ <literal>hbm2ddl.auto</literal> ìµì
ì -ì§ì ë°ì´í°ë² ì´ì¤ ìì¼ë¡- ë°ì´í°ë² ì´ì¤ ì¤í¤ë§ì ìëì ì¸
+ ìì±ì íì±í ìí¨ë¤. ë¬¼ë¡ ì´ê²ì (config ìµì
ì ì ê±°í¨ì¼ë¡ì¨) ë¹íì±í ìí¬ ì ìê±°ë
+ <literal>SchemaExport</literal> Ant íì¤í¬ì ëìì¼ë¡ íì¼ë¡ 리ë¤ì´ë í¸ ë ì ìë¤. ë§ì§ë§ì¼ë¡
+ ì°ë¦¬ë ìì í´ëì¤ë¤ì ìí 매í íì¼(ë¤)ì ì¶ê°ìí¨ë¤.
+ </para>
+
+ <para>
+ ì´ íì¼ì ìì¤ ëë í 리 ìì¼ë¡ ë³µì¬íê³ , ë°ë¼ì ê·¸ê²ì classpathì 루í¸ìì ëë ê²ì´ë¤. Hibernateë
+ ìì ìì classpathì 루í¸ìì <literal>hibernate.cfg.xml</literal>ë¡ ëª
ëª
ë íì¼ì ìëì ì¼ë¡
+ ì°¾ëë¤.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-ant" revision="1">
+ <title>Antë¡ ë¹ëí기</title>
+
+ <para>
+ ì°ë¦¬ë ì´ì Antë¡ íí 리ì¼ì ë¹ëí ê²ì´ë¤. ë¹ì ì Ant를 ì¤ì¹í íìê° ìì ê²ì´ë¤ -
+ <ulink url="http://ant.apache.org/bindownload.cgi">Ant ë´ë ¤ë°ê¸° íì´ì§</ulink>ìì
+ Ant를 ì»ì´ë¼. Ant를 ì¤ì¹íë ë°©ë²ì ì¬ê¸°ì ë¤ë£¨ì§ ìì ê²ì´ë¤. <ulink url="http://ant.apache.org/manual/index.html">Ant 매ë´ì¼</ulink>ì
+ 참조í길 ë°ëë¤. ë¹ì ì´ Ant를 ì¤ì¹í íì, ì°ë¦¬ë ë¹ëíì¼ ìì±ì ììí ì ìë¤. ê·¸ê²ì <literal>build.xml</literal>ë¡
+ ëª
ëª
ëê³ ê°ë° ëë í 리 ìì ì§ì ìì¹ë ê²ì´ë¤.
+ </para>
+
+ <para>
+ 기본 ë¹ë íì¼ì ë¤ìê³¼ ê°ë¤:
+ </para>
+
+ <programlisting><![CDATA[<project name="hibernate-tutorial" default="compile">
+
+ <property name="sourcedir" value="${basedir}/src"/>
+ <property name="targetdir" value="${basedir}/bin"/>
+ <property name="librarydir" value="${basedir}/lib"/>
+
+ <path id="libraries">
+ <fileset dir="${librarydir}">
+ <include name="*.jar"/>
+ </fileset>
+ </path>
+
+ <target name="clean">
+ <delete dir="${targetdir}"/>
+ <mkdir dir="${targetdir}"/>
+ </target>
+
+ <target name="compile" depends="clean, copy-resources">
+ <javac srcdir="${sourcedir}"
+ destdir="${targetdir}"
+ classpathref="libraries"/>
+ </target>
+
+ <target name="copy-resources">
+ <copy todir="${targetdir}">
+ <fileset dir="${sourcedir}">
+ <exclude name="**/*.java"/>
+ </fileset>
+ </copy>
+ </target>
+
+</project>]]></programlisting>
+
+ <para>
+ ì´ê²ì <literal>.jar</literal>ë¡ ëëë lib ëë í 리 ë´ì ìë 모ë íì¼ë¤ì ì»´íì¼ì ì¬ì©ëë classpathì
+ ì¶ê°íëë¡ Antìê² ìë ¤ì¤ ê²ì´ë¤. ê·¸ê²ì ëí 모ë ë¹-Java ìì¤ íì¼ë¤ì ëì ëë í ë¦¬ë¡ ë³µì¬í ê²ì´ë¤. ì를 ë¤ë©´,
+ êµ¬ì± ë° Hibernate 매í íì¼ë¤. ë§ì¼ ë¹ì ì´ Ant를 ì´ì ì¤íí ê²½ì°, ë¹ì ì ë¤ì ì¶ë ¥ì ì»ê² ë ê²ì´ë¤:
+ </para>
+
+ <programlisting><![CDATA[C:\hibernateTutorial\>ant
+Buildfile: build.xml
+
+copy-resources:
+ [copy] Copying 2 files to C:\hibernateTutorial\bin
+
+compile:
+ [javac] Compiling 1 source file to C:\hibernateTutorial\bin
+
+BUILD SUCCESSFUL
+Total time: 1 second ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-helpers" revision="3">
+ <title>ììê³¼ helperë¤</title>
+
+ <para>
+ ëªëª <literal>Event</literal> ê°ì²´ë¤ì ë¡ëìí¤ê³ ì ì¥í ì°¨ë¡ì´ì§ë§, 먼ì ì°ë¦¬ë ì´ë¤ ì¸íë¼ì¤í¸ëì²
+ ì½ëë¡ ì¤ì ì ìë£í´ì¼ íë¤. ì°ë¦¬ë Hibernate를 ììí´ì¼ íë¤. ì´ ììì ì ì <literal>SessionFactory</literal>
+ ê°ì²´ë¥¼ ë¹ëíê³ ì´í리ì¼ì´ì
ë´ìì ì©ì´í ì ê·¼ì ìí´ ê·¸ê²ì ì´ë¤ ê³³ì ì ì¥íë ê²ì í¬í¨íë¤.
+ <literal>SessionFactory</literal>ë ìë¡ì´ <literal>Session</literal>ë¤ì ì´ ì ìë¤.
+ <literal>Session</literal>ì ìì
ì ë¨ì¼-ì°ë ë ë¨ì를 íííë©°, <literal>SessionFactory</literal>ë
+ íë² ì´ê¸°í ëë íëì thread-safe ì ì ê°ì²´ì´ë¤.
+ </para>
+
+ <para>
+ ì°ë¦¬ë ììì ì²ë¦¬íê³ <literal>Session</literal> ì²ë¦¬ë¥¼ í¸ë¦¬íê² í´ì£¼ë <literal>HibernateUtil</literal>
+ helper í´ëì¤ë¥¼ ìì±ìí¬ ê²ì´ë¤. ì´ë¥¸ë° <emphasis>ThreadLocal Session</emphasis> í¨í´ì´ ì¬ê¸°ì
+ ì ì©íë©°, ì°ë¦¬ë íì¬ì ìì
ë¨ì를 íì¬ì ì°ë ëì ì°ê´ì§ì ì ì§íë¤. 구íì ì´í´ë³´ì:
+ </para>
+
+ <programlisting><![CDATA[package util;
+
+import org.hibernate.*;
+import org.hibernate.cfg.*;
+
+public class HibernateUtil {
+
+ private static final SessionFactory sessionFactory;
+
+ static {
+ try {
+ // Create the SessionFactory from hibernate.cfg.xml
+ sessionFactory = new Configuration().configure().buildSessionFactory();
+ } catch (Throwable ex) {
+ // Make sure you log the exception, as it might be swallowed
+ System.err.println("Initial SessionFactory creation failed." + ex);
+ throw new ExceptionInInitializerError(ex);
+ }
+ }
+
+ public static SessionFactory getSessionFactory() {
+ return sessionFactory;
+ }
+
+}]]></programlisting>
+
+ <para>
+ ì´ í´ëì¤ë (í´ëì¤ê° ë¡ëë ë JVMì ìí´ íë² í¸ì¶ëë) ê·¸ê²ì static ì´ê¸°ì ë´ì ì ì <literal>SessionFactory</literal>를
+ ì°ì¶í ë¿ë§ ìëë¼ ëí íì¬ ì°ë ëì ëí <literal>Session</literal>ì ìì íë <literal>ThreadLocal</literal> ë³ì를
+ ê°ëë¤. ë¹ì ì´ <literal>HibernateUtil.getCurrentSession()</literal>ì í¸ì¶íë ìì ìë 문ì ê° ìì¼ë©°,
+ ê·¸ê²ì íì ëì¼ ì°ë ë ë´ì ëì¼í Hibernate ìì
ë¨ì를 ë°íí ê²ì´ë¤. <literal>HibernateUtil.closeSession()</literal>ì
+ ëí í¸ì¶ì ì°ë ëì íì¬ ì°ê´ëì´ ìë ìì
ë¨ì를 ì¢
ë£ìí¨ë¤.
+ </para>
+
+ <para>
+ ë¹ì ì´ ì´ helper를 ì¬ì©í기 ì ì thread-local ë³ìë¤ì ëí Java ê°ë
ì íì¤í ì´í´íëë¡ íë¼.
+ ë³´ë¤ ê°ë ¥í <literal>HibernateUtil</literal> helperë http://caveatemptor.hibernate.org/ì
+ ìë <literal>CaveatEmptor</literal> ë¿ë§ ìëë¼ "Hibernate in Action" ì±
ìì ì°¾ì ì ìë¤.
+ ë¹ì ì´ J2EE ì´í리ì¼ì´ì
ìë² ë´ì Hibernate를 ë°°ì¹í ê²½ì°ì ì´ í´ëì¤ë íìì ì´ì§ ìë¤: íëì
+ <literal>Session</literal>ì íì¬ì JTA í¸ëìì
ì ìëì ì¼ë¡ ë°ì¸ë ë ê²ì´ê³ ë¹ì ì JNDI를 íµí´
+ <literal>SessionFactory</literal>를 룩ì
í ì ìë¤. ë§ì¼ ë¹ì ì´ JBoss AS를 ì¬ì©í ê²½ì°,
+ Hibernateë ê´ë¦¬ëë ìì¤í
ìë¹ì¤ë¡ì ë°°ì¹ë ì ìê³ <literal>SessionFactory</literal>를
+ JNDI ì´ë¦ì ìëì ì¼ë¡ ë°ì¸ëìí¬ ì ìì ê²ì´ë¤.
+ </para>
+
+ <para>
+ ê°ë° ìì¤ ëë í 리 ìì <literal>HibernateUtil.java</literal> 를 ìì¹ì§ì°ê³ , ë¤ìì¼ë¡ <literal>Event.java</literal>를
+ ìì¹ì§ìë¼:
+ </para>
+
+ <programlisting><![CDATA[.
++lib
+ <Hibernate and third-party libraries>
++src
+ +events
+ Event.java
+ Event.hbm.xml
+ +util
+ HibernateUtil.java
+ hibernate.cfg.xml
++data
+build.xml]]></programlisting>
+
+ <para>
+ ì´ê²ì 문ì ìì´ ë¤ì ì»´íì¼ ë ê²ì´ë¤. ì°ë¦¬ë ë§ì§ë§ì¼ë¡ ë¡ê¹
ìì¤í
ì 구ì±í íìê° ìë¤ - Hibernateë
+ commons logging를 ì¬ì©íê³ Log4jì JDK 1.4 ì¬ì´ì ì íì ë¹ì ì 몫ì¼ë¡ ë¨ê²¨ëë¤. ëë¶ë¶ì ê°ë°ìë¤ì
+ Log4j를 ì í¸íë¤: Hibernate ë°°í¬ë³¸ì ìë <literal>log4j.properties</literal>(ì´ê²ì ëë í 리
+ <literal>etc/</literal> ë´ì ìë¤)를 <literal>src</literal> ëë í ë¦¬ë¡ ë³µì¬íê³ , ë¤ìì¼ë¡
+ <literal>hibernate.cfg.xml</literal>ì ë³µì¬íë¼. ìì 구ì±ì ì´í´ë³´ê³ ë¹ì ì´ ë ë§ì verbose ì¶ë ¥ì
+ ìí ê²½ì°ì ì¤ì ë¤ì ë³ê²½íë¼. ëí´í¸ë¡ Hibernate ìì ë©ìì§ë stdout ìì ë³´ì¬ì§ë¤.
+ </para>
+
+ <para>
+ íí ë¦¬ì¼ ì¸íë¼ì¤í¸ëì²ë ìì íë¤ - ê·¸ë¦¬ê³ ì°ë¦¬ë Hibernateë¡ ì´ë¤ ì¤ì ìì
ì íí ì¤ë¹ê° ëì´ ìë¤.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-workingpersistence" revision="5">
+ <title>ê°ì²´ ë¡ë©ê³¼ ê°ì²´ ì ì¥</title>
+
+ <para>
+ ë§ì§ë§ì¼ë¡ ì°ë¦¬ë ê°ì²´ë¤ì ë¡ëìí¤ê³ ì ì¥íëë° Hibernate를 ì¬ì©í ì ìë¤. ì°ë¦¬ë í ê°ì <literal>main()</literal>
+ ë©ìë를 ê°ì§ í ê°ì <literal>EventManager</literal> í´ëì¤ë¥¼ ìì±íë¤:
+ </para>
+
+ <programlisting><![CDATA[package events;
+import org.hibernate.Session;
+
+import java.util.Date;
+
+import util.HibernateUtil;
+
+public class EventManager {
+
+ public static void main(String[] args) {
+ EventManager mgr = new EventManager();
+
+ if (args[0].equals("store")) {
+ mgr.createAndStoreEvent("My Event", new Date());
+ }
+
+ HibernateUtil.getSessionFactory().close();
+ }
+
+ private void createAndStoreEvent(String title, Date theDate) {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+
+ session.beginTransaction();
+
+ Event theEvent = new Event();
+ theEvent.setTitle(title);
+ theEvent.setDate(theDate);
+
+ session.save(theEvent);
+
+ session.getTransaction().commit();
+ }
+
+}]]></programlisting>
+
+ <para>
+ ì°ë¦¬ë í ê°ì ìë¡ì´ <literal>Event</literal> ê°ì²´ë¥¼ ìì±ìí¤ê³ , ê·¸ê²ì Hibernateìê² ê±´ë¤ì¤ë¤.
+ Hibernateë ì´ì SQLì ì²ë¦¬íê³ ë°ì´í°ë² ì´ì¤ ììì <literal>INSERT</literal>ë¤ì ì¤íìí¨ë¤.
+ -ì°ë¦¬ê° ì´ê²ì ì¤íí기 ì ì ì½ë를 ì²ë¦¬íë- <literal>Session</literal>ê³¼ <literal>Transaction</literal>ì
+ ì´í´ë³´ì.
+ </para>
+
+ <para>
+ <literal>Session</literal>ì í ê°ì ìì
ë¨ìì´ë¤. ì§ê¸ë¶í° ì°ë¦¬ë ë¨ì¨í¨ì ì ì§í ê²ì´ê³
+ Hibernate <literal>Session</literal>ê³¼ ë°ì´í°ë² ì´ì¤ í¸ëìì
ì¬ì´ì ì¼-ë-ì¼ ê³¼ë¦½í(granularity)ì ê°ì í ê²ì´ë¤.
+ ì¤ì ê¸°ë° í¸ëìì
ìì¤í
ì¼ë¡ë¶í° ì°ë¦¬ì ìì¤ë¥¼ ìíìí¤ê¸° ìí´(ì´ ê²½ì° íµìì JDBCì´ì§ë§, ê·¸ê²ì ëí JTAìë ì¤íëë¤)
+ ì°ë¦¬ë Hibernate <literal>Session</literal> ììì ì´ì© ê°ë¥í <literal>Transaction</literal> API를
+ ì¬ì©íë¤.
+ </para>
+
+ <para>
+
+ <literal>sessionFactory.getCurrentSession()</literal>ì 무ìì ííëê°? 먼ì
+ ë¹ì ì ë¹ì ì´ (<literal>HibernateUtil</literal> ëë¶ì ì½ê²) <literal>SessionFactory</literal>ì
+ ë¹ì ì´ ìì íê³ ìë¤ë©´, ìíë ë§í¼ ì´ëìë ì¬ë¬ë² ê·¸ê²ì í¸ì¶í ì ìë¤.
+ <literal>getCurrentSession()</literal> ë©ìëë íì "íì¬ì" ìì
ë¨ì를 ë°ííë¤. ì°ë¦¬ê°
+ <literal>hibernate.cfg.xml</literal> ë´ìì ì´ ë§¤ì»¤ëì¦ì ëí êµ¬ì± ìµì
ì "thread"ë¡ ì íìì¼°ìì 기ìµíëê°?
+ ê·¸ë¬ë¯ë¡ ìì
ì íì¬ ë¨ìë ì°ë¦¬ì ì´í리ì¼ì´ì
ì ì¤íìí¤ë íì¬ ìë° ì°ë ëì ë¬¶ì¬ ìë¤. íì§ë§
+ ì´ê²ì ì ì²´ ê·¸ë¦¼ì´ ìëë©°, ë¹ì ì ëí 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>
+
+ <para>
+ ì´ ì²« ë²ì§¸ 루í´ì ì¤íí기 ìí´ì ì°ë¦¬ë í¸ì¶ ê°ë¥í ëìì Ant ë¹ë íì¼ì ì¶ê°í´ì¼ íë¤:
+ </para>
+
+ <programlisting><![CDATA[<target name="run" depends="compile">
+ <java fork="true" classname="events.EventManager" classpathref="libraries">
+ <classpath path="${targetdir}"/>
+ <arg value="${action}"/>
+ </java>
+</target>]]></programlisting>
+
+ <para>
+ <literal>action</literal> ìê·ë¨¼í¸ì ê°ì ëìì í¸ì¶í ë ëª
ë ¹ ë¼ì¸ ììì ì¤ì ëë¤:
+ </para>
+
+ <programlisting><![CDATA[C:\hibernateTutorial\>ant run -Daction=store]]></programlisting>
+
+ <para>
+ ì»´íì¼, 구ì±ì ë°ë¥¸ Hibernate ìì íì, ë¹ì ì ë§ì ë¡ê·¸ ì¶ë ¥ì ë³´ê² ë ê²ì´ë¤. ëìì ë¹ì ì
+ ë¤ì ë¼ì¸ì ë°ê²¬í ê²ì´ë¤:
+ </para>
+
+ <programlisting><![CDATA[[java] Hibernate: insert into EVENTS (EVENT_DATE, title, EVENT_ID) values (?, ?, ?)]]></programlisting>
+
+ <para>
+ ì´ê²ì Hibernateì ìí´ ì¤íë <literal>INSERT</literal>ì´ê³ , 물ìí 기í¸ë JDBC ë°ì¸ë
+ íë¼ë¯¸í°ë¤ì ëíë¸ë¤. ìê·ë¨¼í¸ë¡ì ë°ì¸ë ë ê°ë¤ì ë³´ê±°ë ì¥í©í ë¡ê·¸ë¥¼ ì¤ì´ë ¤ë©´ ë¹ì ì
+ <literal>log4j.properties</literal>를 ì²´í¬íë¼.
+ </para>
+
+ <para>
+ ì´ì ì°ë¦¬ë ë§ì°¬ê°ì§ë¡ ì ì¥ë ì´ë²¤í¸ë¤ì ì´ê±°íê³ ì ìíë©°, ì°ë¦¬ë main ë©ìëì í ê°ì ìµì
ì
+ ì¶ê°íë¤:
+ </para>
+
+ <programlisting><![CDATA[if (args[0].equals("store")) {
+ mgr.createAndStoreEvent("My Event", new Date());
+}
+else if (args[0].equals("list")) {
+ List events = mgr.listEvents();
+ for (int i = 0; i < events.size(); i++) {
+ Event theEvent = (Event) events.get(i);
+ System.out.println("Event: " + theEvent.getTitle() +
+ " Time: " + theEvent.getDate());
+ }
+}]]></programlisting>
+
+ <para>
+ ì°ë¦¬ë ëí ìë¡ì´ <literal>listEvents() method</literal> ë©ìë를 ì¶ê° ìí¨ë¤:
+ </para>
+
+ <programlisting><![CDATA[private List listEvents() {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+
+ session.beginTransaction();
+
+ List result = session.createQuery("from Event").list();
+
+ session.getTransaction().commit();
+
+ return result;
+}]]></programlisting>
+
+ <para>
+ ì¬ê¸°ì ì°ë¦¬ê° íí ê²ì ë°ì´í°ë² ì´ì¤ë¡ë¶í° 모ë ì¡´ì¬íë <literal>Event</literal> ê°ì²´ë¤ì ë¡ëìí¤ê¸°
+ ìí´ HQL (Hibernate Query Language) ì§ì를 ì¬ì©íë ê²ì´ë¤. Hibernateë ì ì í SQLì ìì±ìí¬
+ ê²ì´ê³ , ê·¸ê²ì ë°ì´í°ë² ì´ì¤ë¡ ì ì¡íê³ ë°ì´í°ë¥¼ <literal>Event</literal> ê°ì²´ë¤ì 거주ìí¬ ê²ì´ë¤.
+ ë¹ì ì ë¬¼ë¡ HQLë¡ì ë³´ë¤ ë³µì¡í ì§ìë¤ì ìì±ìí¬ ì ìë¤.
+ </para>
+
+ <para>
+ ì´ì ì´ ëª¨ë ê²ì ì¤ííê³ í
ì¤í¸í기 ìí´, ë¤ì ë¨ê³ë¤ì ë°ë¥´ë¼:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ ë°ì´í°ë² ì´ì¤ ìì¼ë¡ ì´ë¤ ê²ì ì ì¥íê³ ë¬¼ë¡ ìì hbm2ddlì íµí´ ë°ì´í°ë² ì´ì¤ ì¤í¤ë§ë¥¼ ì°ì¶ìí¤ê¸° ìí´
+ <literal>ant run -Daction=store</literal>를 ì¤ííë¼.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ì´ì ë¹ì ì <literal>hibernate.cfg.xml</literal> íì¼ ììì ê·¸ íë¡í¼í°ë¥¼ 주ìì²ë¦¬í¨ì¼ë¡ì¨ hbm2ddlì
+ ì¬ì©ë¶ê°ë¥íê² íë¼. ëê° ë¹ì ì ì§ìëë ë¨ì í
ì¤í
ììë ê·¸ê²ì ì¬ì© ê°ë¥íê² ë´ë²ë ¤ëì´ë ëì§ë§, ë ë¤ë¥¸
+ hbm2ddlì ì¤íì ë¹ì ì´ ì ì¥íë 모ë ê²ì <emphasis>drop</emphasis>ìí¬ ê²ì´ë¤ - <literal>create</literal>
+ êµ¬ì± ì¤ì ì ì¤ì ë¡ "ì¤í¤ë§ë¡ë¶í° 모ë í
ì´ë¸ë¤ì ëë¡ìí¤ê³ ëì, SessionFactoryê° ë¹ëë ë 모ë í
ì´ë¸ë¤ì
+ ë¤ì ìì±ìí¤ë ê²"ì¼ë¡ ë³íëë¤.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ë§ì¼ ë¹ì ì´ ì§ê¸ <literal>-Daction=list</literal>ë¡ Ant를 í¸ì¶í ê²½ì°, ë¹ì ì ë¹ì ì´ ì§ê¸ê¹ì§ ì ì¥íë ì´ë²¤í¸ë¤ì
+ ë³´ê² ë ê²ì´ë¤. ë¬¼ë¡ ë¹ì ì ëí ì¬ë¬ ë² <literal>store</literal> ì¡ì
ì í¸ì¶í ì ìë¤.
+ </para>
+
+ <para>
+ ë
¸í¸ : ëë¶ë¶ì Hibernate ì¬ì©ìë¤ì ì´ ì§ì ìì ì¤í¨íê³ ì°ë¦¬ë ì 기ì ì¼ë¡ <emphasis>Table not found</emphasis>
+ ì¤ë¥ ë©ìì§ë¤ì ê´í ì§ë¬¸ì ë°ëë¤. íì§ë§ ë§ì¼ ë¹ì ì´ ìì ì¡°ëª
ë ë¨ê²ë¤ì ë°ë¥¼ ê²½ì° ë¹ì ì ì´ ë¬¸ì 를 ê²ªì§ ìì ê²ì´ê³ ,
+ hbm2ddlì´ ì²ì ì¤í ìì ë°ì´í°ë² ì´ì¤ ì¤í¤ë§ë¥¼ ìì±ìí¤ë¯ë¡, ì°¨íì ì´í리ì¼ì´ì
ì¬ììì ì´ ì¤í¤ë§ë¥¼ ì¬ì©í ê²ì´ë¤.
+ ë§ì¼ ë¹ì ì´ ë§¤í ê·¸ë¦¬ê³ /ëë ë°ì´í°ë² ì´ì¤ ì¤í¤ë§ë¥¼ ë³ê²½í ê²½ì°ì, ë¹ì ì ë¤ì íë² ë hbm2ddlì ì´ì© ê°ë¥íëë¡ í´ì¼ íë¤.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="tutorial-associations">
+ <title>íí¸ 2 - ì°ê´ë¤ì 매íí기</title>
+
+ <para>
+ ì°ë¦¬ë í ê°ì ìì ìí°í° í´ëì¤ë¥¼ í ê°ì í
ì´ë¸ë¡ 매ííë¤. ì´ê² ììì ë¹ëíê³ ëªëª í´ëì¤ ì°ê´ë¤ì ì¶ê°ìí¤ì. 먼ì
+ ì°ë¦¬ë ì°ë¦¬ì ì´í리ì¼ì´ì
ì ì¬ëë¤ì ì¶ê°íê³ ê·¸ë¤ì´ ì°¸ì¬íë ì´ë²¤í¸ë¤ì 목ë¡ì ì ì¥í ê²ì´ë¤.
+ </para>
+
+ <sect2 id="tutorial-associations-mappinguser" revision="1">
+ <title>Person í´ëì¤ ë§¤íí기</title>
+
+ <para>
+ í´ëì¤ì 첫 ë²ì§¸ ì¥ë©´ì ê°ë¨íë¤:
+ </para>
+
+ <programlisting><![CDATA[package events;
+
+public class Person {
+
+ private Long id;
+ private int age;
+ private String firstname;
+ private String lastname;
+
+ public Person() {}
+
+ // Accessor methods for all properties, private setter for 'id'
+
+}]]></programlisting>
+
+ <para>
+ <literal>Person.hbm.xml</literal>ë¡ ëª
ëª
ëë ìë¡ì´ 매í íì¼ì ìì±ìì¼ë¼
+ (맨ìì DTD 참조를 ìì§ë§ë¼):
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="events.Person" table="PERSON">
+ <id name="id" column="PERSON_ID">
+ <generator class="native"/>
+ </id>
+ <property name="age"/>
+ <property name="firstname"/>
+ <property name="lastname"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ ë§ì§ë§ì¼ë¡ ìë¡ì´ 매íì Hibernateì 구ì±ì ì¶ê°íë¼:
+ </para>
+
+ <programlisting><![CDATA[<mapping resource="events/Event.hbm.xml"/>
+<mapping resource="events/Person.hbm.xml"/>]]></programlisting>
+
+ <para>
+ ì´ì ì°ë¦¬ë ì´ë¤ ë ê°ì ìí°í°ë¤ ì¬ì´ì í ê°ì ì°ê´ì ìì±ìí¬ ê²ì´ë¤. ëª
ë°±íê², ê°ì¸ë¤ì ì´ë²¤í¸ë¤ì
+ ì°¸ì¬í ì ìê³ , ì´ë²¤í¸ë¤ì ì°¸ì¬ìë¤ì ê°ëë¤. ì°ë¦¬ê° ë¤ë£¨ì´ì¼ íë ì¤ê³ ì§ë¬¸ë¤ì ë¤ìê³¼ ê°ë¤ :
+ ë°©í¥ì±(directionality), ë¤ì¤ì±(multiplicity), ê·¸ë¦¬ê³ ì½ë ì
í¹ì§.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-unidirset" revision="3">
+ <title>ë¨ë°©í¥ Set-기ë°ì ì°ê´</title>
+
+ <para>
+ ì°ë¦¬ë <literal>Person</literal> í´ëì¤ì ì´ë²¤í¸ë¤ì ê°ì§ í ê°ì ì½ë ì
ì ì¶ê°í ê²ì´ë¤. ê·¸ ë°©ë²ì¼ë¡ ì°ë¦¬ë
+ ëª
ìì ì¸ ì§ì-<literal>aPerson.getEvents()</literal>를 í¸ì¶í¨ì¼ë¡ì¨-를 ì¤íìí¤ì§ ìê³ ì í¹ì ê°ì¸ì ëí
+ ì´ë²¤í¸ë¤ì ì½ê² ë¤ë¹ê²ì´í¸í ì ìë¤. ì°ë¦¬ë íëì Java ì½ë ì
, íëì <literal>Set</literal>를 ì¬ì©íë¤. ìëíë©´
+ ê·¸ ì½ë ì
ì ì¤ë³µ ììë¤ì í¬í¨í기 ìì ê²ì´ê³ ê·¸ ììê° ì°ë¦¬ì ê´ë ¨ëì´ ìì§ ì기 ë문ì´ë¤.
+ </para>
+
+ <para>
+ ì°ë¦¬ë íëì <literal>Set</literal>ì¼ë¡ 구íë, íëì ë¨ë°©í¥, ë¤ì¤ê° ì°ê´ë¤ì íìë¡ íë¤. Java í´ëì¤ë¤ ë´ì
+ ì´ë¥¼ ìí ì½ë를 ìì±íê³ ê·¸ë° ë¤ì ê·¸ê²ì 매íìí¤ì:
+ </para>
+
+ <programlisting><![CDATA[public class Person {
+
+ private Set events = new HashSet();
+
+ public Set getEvents() {
+ return events;
+ }
+
+ public void setEvents(Set events) {
+ this.events = events;
+ }
+}]]></programlisting>
+
+ <para>
+ ì°ë¦¬ê° ì´ ì°ê´ì 매íí기 ì ì, ë¤ë¥¸ 측ì ëí´ ìê°íë¼. ëª
ë°±íê² ì°ë¦¬ë ì´ê²ì ë¨ì§ ë¨ë°©í¥ì¼ë¡ ì ì§ìí¬ ì ìë¤.
+ ëë ì°ë¦¬ê° ê·¸ê²ì ìë°©í¥ì¼ë¡ ë¤ë¹ê²ì´í¸íë ê²-ì를 ë¤ì´ <literal>anEvent.getParticipants()</literal>-ì´
+ ê°ë¥íëë¡ ìí ê²½ì°ì, <literal>Event</literal>측 ìì ë ë¤ë¥¸ ì½ë ì
ì ìì±ìí¬ ì ìë¤. ì´ê²ì ë¹ì ìê²
+ ë¨ê²¨ì§ ì¤ê³ ì íì´ì§ë§, ì´ ë
¼ììì ëª
ë£í ì ì ì°ê´ì ë¤ì¤ì±ì´ë¤: ì 측 ììì "ë¤ì¤" ê°ì ê°ë ê²½ì°, ì°ë¦¬ë ì´ê²ì
+ <emphasis>many-to-many</emphasis> ì°ê´ì´ë¼ê³ ëª
ëª
íë¤. ê·¸ë¬ë¯ë¡ ì°ë¦¬ë Hibernateì many-to-many 매íì
+ ì¬ì©íë¤:
+ </para>
+
+ <programlisting><![CDATA[<class name="events.Person" table="PERSON">
+ <id name="id" column="PERSON_ID">
+ <generator class="native"/>
+ </id>
+ <property name="age"/>
+ <property name="firstname"/>
+ <property name="lastname"/>
+
+ <set name="events" table="PERSON_EVENT">
+ <key column="PERSON_ID"/>
+ <many-to-many column="EVENT_ID" class="events.Event"/>
+ </set>
+
+</class>]]></programlisting>
+
+ <para>
+ Hibernateë 모ë ì¢
ë¥ì ì½ë ì
매íë¤, ê°ì¥ ê³µíµì ì¸ <literal><set></literal>ì ì§ìíë¤. many-to-many
+ ì°ê´ (ëë <emphasis>n:m</emphasis> ìí°í° ê´ê³)ì ê²½ì°, í ê°ì ì°ê´ í
ì´ë¸ì´ íìíë¤. ì´ í
ì´ë¸ ë´ì ìë ê°ê°ì
+ íì í ëª
ì ê°ì¸ê³¼ í ê°ì ì´ë²¤í¸ ì¬ì´ì ë§í¬ë¥¼ íííë¤. í
ì´ë¸ ì´ë¦ì <literal>set</literal> ììì <literal>table</literal>
+ ìì±ì¼ë¡ 구ì±ëë¤. ì°ê´ ë´ì ìë³ì ì»¬ë¼ ì´ë¦ì ê°ì¸ 측ì ëí´ <literal><key></literal> ììë¡ ì ìëê³
+ ì´ë²¤í¸ 측ì ëí ì»¬ë¼ ì´ë¦ì <literal><many-to-many></literal>ì <literal>column</literal> ìì±ì¼ë¡
+ ì ìëë¤. ë¹ì ì ëí ë¹ì ì ì½ë ì
ë´ì ìë ê°ì²´ë¤ì í´ëì¤(ì ííê² : 참조ë¤ì ê°ì§ ì½ë ì
ì ë¤ë¥¸ 측 ìì ìë í´ëì¤)를
+ Hibernateìê² ìë ¤ì£¼ì´ì¼ íë¤.
+ </para>
+
+ <para>
+ ë°ë¼ì ì´ ë§¤íì ìí ë°ì´í°ë² ì´ì¤ ì¤í¤ë§ë ë¤ìê³¼ ê°ë¤:
+ </para>
+
+ <programlisting><![CDATA[
+ _____________ __________________
+ | | | | _____________
+ | EVENTS | | PERSON_EVENT | | |
+ |_____________| |__________________| | PERSON |
+ | | | | |_____________|
+ | *EVENT_ID | <--> | *EVENT_ID | | |
+ | EVENT_DATE | | *PERSON_ID | <--> | *PERSON_ID |
+ | TITLE | |__________________| | AGE |
+ |_____________| | FIRSTNAME |
+ | LASTNAME |
+ |_____________|
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-working" revision="2">
+ <title>ì°ê´ë¤ì ìì
í기</title>
+
+ <para>
+ <literal>EventManager</literal> ìì ìë í ê°ì ìë¡ì´ ë©ìë ë´ì ëªëª ì¬ëë¤ê³¼ ì´ë²¤í¸ë¤ì í¨ê» ê°ì ¸ì¤ì:
+ </para>
+
+ <programlisting><![CDATA[private void addPersonToEvent(Long personId, Long eventId) {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+ session.beginTransaction();
+
+ Person aPerson = (Person) session.load(Person.class, personId);
+ Event anEvent = (Event) session.load(Event.class, eventId);
+
+ aPerson.getEvents().add(anEvent);
+
+ session.getTransaction().commit();
+}]]></programlisting>
+
+ <para>
+ <literal>Person</literal>ê³¼ <literal>Event</literal>를 ë¡ëìí¨ íì, ì ê· ì½ë ì
ë©ìëë¤ì ì¬ì©íì¬
+ ì½ë ì
ì ê°ë¨íê² ë³ê²½íë¼. ë¹ì ì´ ì ì ìë¯ì´, <literal>update()</literal> ëë <literal>save()</literal>ì
+ ëí ëª
ìì ì¸ í¸ì¶ì´ ì¡´ì¬íì§ ìê³ , ë³ê²½ëìê³ ì ì¥í íìê° ìë ì½ë ì
ì Hibernateê° ìëì ì¼ë¡ ê²ì¶í´ë¸ë¤. ì´ê²ì
+ <emphasis>ìëì ì¸ dirty ì²´í¹</emphasis>ì´ë¼ ë¶ë ¤ì§ë©°, ë¹ì ì ëí ë¹ì ì ììì ê°ì²´ë¤ì ëí name ëë date
+ íë¡í¼í°ë¥¼ ë³ê²½í¨ì¼ë¡ì¨ ê·¸ê²ì ìëí ì ìë¤. ê·¸ê²ë¤ì´ <emphasis>ìì(persistent)</emphasis> ìíì ìë ëì, ì¦ í¹ì Hibernate
+ <literal>Session</literal>ì ë°ì¸ëëì´ ìë ëì(ì를 ë¤ë©´. ê·¸ê²ë¤ì ìì
ë¨ì ìì ë°©ê¸ ë¡ëëìê±°ë ì ì¥ëìë¤),
+ Hibernateë ììì ë³ê²½ë¤ì 모ëí°ë§íê³ ì°ê¸° ì´ë©´ì ííë¡ SQLì ì¤íìí¨ë¤. ë©ëª¨ë¦¬ ìí를 ë°ì´í°ë² ì´ì¤ì ë기í ìí¤ë
+ ê³¼ì ì ëê° ì¤ì§ ìì
ë¨ìì ëììì´ê³ , <emphasis>flushing</emphasis>ì´ë¼ ëª
ëª
ëë¤. ì°ë¦¬ì ì½ëìì, ìì
ë¨ìë
+ <literal>CurrentSessionContext</literal> í´ëì¤ì ëí <literal>thread</literal> êµ¬ì± ìµì
ì ìí´ ì ìë
+ ëë¡ - ë°ì´í°ë² ì´ì¤ í¸ëìì
ì 커ë°(ëë 롤백)ì¼ë¡ ëì´ëë¤.
+ </para>
+
+ <para>
+ ë¬¼ë¡ ë¹ì ì ë¤ë¥¸ ìì
ë¨ì ìì ê°ì¸ê³¼ ì´ë²¤í¸ë¥¼ ë¡ëìí¬ ì ë ìë¤. ëë ë¹ì ì íëì ê°ì²´ê·¸ ìì ìíì ìì§ ìì ë
+ <literal>Session</literal>ì ì¸ë¶ìì ê°ì²´ë¥¼ ë³ê²½ìí¬ ìë ìë¤(ë§ì¼ ê°ì²´ê° ì´ì ì ììí ëìë¤ë©´, ì°ë¦¬ë ì´ ìí를
+ <emphasis>detached</emphasis>ë¼ê³ ë¶ë¥¸ë¤). (ë§¤ì° ì¬ì¤ì ì´ì§ ìì) ì½ë ë´ìì ì´ê²ì ë¤ìê³¼ ê°ì ì ìë¤:
+ </para>
+
+ <programlisting><![CDATA[private void addPersonToEvent(Long personId, Long eventId) {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+ session.beginTransaction();
+
+ Person aPerson = (Person) session
+ .createQuery("select p from Person p left join fetch p.events where p.id = :pid")
+ .setParameter("pid", personId)
+ .uniqueResult(); // Eager fetch the collection so we can use it detached
+
+ Event anEvent = (Event) session.load(Event.class, eventId);
+
+ session.getTransaction().commit();
+
+ // End of first unit of work
+
+ aPerson.getEvents().add(anEvent); // aPerson (and its collection) is detached
+
+ // Begin second unit of work
+
+ Session session2 = HibernateUtil.getSessionFactory().getCurrentSession();
+ session2.beginTransaction();
+
+ session2.update(aPerson); // Reattachment of aPerson
+
+ session2.getTransaction().commit();
+}]]></programlisting>
+
+ <para>
+ <literal>update</literal>ì ëí í¸ì¶ì í ê°ì detached ê°ì²´ë¥¼ ë¤ì ììí ìí¤ê³ , ë¹ì ì ê·¸ê²ì´ ìë¡ì´ ìì
ë¨ìì
+ ë°ì¸ëëë¤ê³ ë§í ì ìê³ , ë°ë¼ì detached ëìì ë¹ì ì´ ê·¸ê²ì ëí´ íí ììì ë³ê²½ë¤ì´ ë°ì´í°ë² ì´ì¤ì ì ì¥ë ì ìë¤.
+ ì´ê²ì ë¹ì ì´ ê·¸ ìí°í° ê°ì²´ì ì½ë ì
ì ëí´ ííë ììì ë³ê²½ë¤(ì¶ê°/ìì )를 í¬í¨íë¤.
+ </para>
+
+ <para>
+ ë¬¼ë¡ , ì°ë¦¬ì íì¬ ìí©ìì ì´ê²ì ë§ì´ ì¬ì©ëì§ ìì§ë§, ê·¸ê²ì ë¹ì ì´ ë¹ì ìì ì ì´í리ì¼ì´ì
ë´ë¡ ì¤ê³í ì ìë ì¤ìí
+ ê°ë
ì´ë¤. ì§ê¸ <literal>EventManager</literal>ì main ë©ìëì í ê°ì ìë¡ì´ ì¡ì
ì ì¶ê°íê³ ëª
ë ¹ ë¼ì¸ìì ê·¸ê²ì
+ í¸ì¶íì¬ ì´ ì°ìµì ìë£íë¼. ë§ì¼ ë¹ì ì´ íëª
ì ê°ì¸ê³¼ í ê°ì ì´ë²¤í¸ì ëí ìë³ìë¤ì íìë¡ í ê²½ì° - <literal>save()</literal>
+ ë©ìëê° ê·¸ê²ì ë°íìí¨ë¤(ë¹ì ì ê·¸ ìë³ì를 ë°íìí¤ë ìì ë©ìëë¤ ì¤ ëªëªì ë³ê²½ìì¼ì¼ í ê²ì´ë¤):
+ </para>
+
+ <programlisting><![CDATA[else if (args[0].equals("addpersontoevent")) {
+ Long eventId = mgr.createAndStoreEvent("My Event", new Date());
+ Long personId = mgr.createAndStorePerson("Foo", "Bar");
+ mgr.addPersonToEvent(personId, eventId);
+ System.out.println("Added person " + personId + " to event " + eventId);
+}]]></programlisting>
+
+ <para>
+ ì´ê²ì ë ê°ì ëë±íê² ì¤ìí í´ëì¤ë¤, ë ê°ì ìí°í°ë¤ ì¬ì´ìì í ê°ì ì°ê´ì ê´í ìì ìë¤. ìì ì¸ê¸íë¯ì´, ì íì ì¸ ëª¨í ë´ìë
+ ë¤ë¥¸ í´ëì¤ë¤ê³¼ íì´ë¤ì´ ì¡´ì¬íëë°, ëê° "ë ì¤ìíë¤". ë¹ì ì ì´ë¯¸ <literal>int</literal> ëë <literal>String</literal>ê³¼
+ ê°ì ì´ë¤ ê²ì ì´ë¯¸ ë³´ìë¤. ì°ë¦¬ë ì´ë¤ í´ëì¤ë¤ì <emphasis>ê° íì
ë¤(value types)</emphasis>ì´ë¼ ëª
ëª
íê³ , ê·¸ë¤ ì¸ì¤í´ì¤ë¤ì
+ í¹ì ìí°í°ì <emphasis>ìì¡´íë¤(depend)</emphasis>. ì´ë¤ íì
ë¤ì ê°ì§ ì¸ì¤í´ì¤ë¤ì ê·¸ê²ë¤ ìì ì ìë³ì±(identity)를 ê°ì§ ìê±°ë,
+ ê·¸ê²ë¤ì ìí°í°ë¤ ì¬ì´ìì ê³µì ëì§ë ìëë¤(ëê°ì personë¤ì ì¬ì§ì´ ê·¸ê²ë¤ì´ ê°ì 첫 ë²ì§¸ ì´ë¦ì ê°ë ê²½ì°ìë ëì¼í <literal>firstname</literal>ì
+ 참조íì§ ìëë¤ ). ë¬¼ë¡ ê° íì
ë¤ì JDK ë´ìì ë°ê²¬ë ë¿ë§ ìëë¼(ì¬ì¤, Hibernate ì´í리ì¼ì´ì
ìì 모ë JDK í´ëì¤ë¤ì ê° íì
ë¤ë¡ ê°ì£¼ëë¤),
+ ë¹ì ì ëí ë¹ì ì¤ì¤ë¡ ì¢
ì í´ëì¤ë¤, ì를 ë¤ë©´ <literal>Address</literal> ëë <literal>MonetaryAmount</literal>ì ìì±í ì ìë¤.
+ </para>
+
+ <para>
+ ë¹ì ì ëí ê° íì
ë¤ì ì¤ê³í ì ìë¤. ì´ê²ì ë¤ë¥¸ ìí°í°ë¤ì ëí 참조ë¤ì ê°ì§ ì½ë ì
ê³¼ë ê°ë
ì ì¼ë¡ ë§¤ì° ë¤ë¥´ì§ë§,
+ Javaììë ëê° ëì¼í ê²ì¼ë¡ ë³´ì¬ì§ë¤.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-valuecollections">
+ <title>ê°ë¤ì ê°ì§ ì½ë ì
</title>
+
+ <para>
+ ì°ë¦¬ë ê° íì
ì ê°ì²´ë¤ì ê°ì§ í ê°ì ì½ë ì
ì <literal>Person</literal> ìí°í°ì ì¶ê°ìí¨ë¤. ì°ë¦¬ë email 주ì를
+ ì ì¥íê³ ì ìíë¯ë¡, ì°ë¦¬ê° ì¬ì©íë íì
ì <literal>String</literal>ì´ê³ , ê·¸ ì½ë ì
ì ë¤ì í ê°ì <literal>Set</literal>ì´ë¤:
+ </para>
+ <programlisting><![CDATA[private Set emailAddresses = new HashSet();
+
+public Set getEmailAddresses() {
+ return emailAddresses;
+}
+
+public void setEmailAddresses(Set emailAddresses) {
+ this.emailAddresses = emailAddresses;
+}]]></programlisting>
+
+ <para>
+ ì´ <literal>Set</literal>ì ëí 매íì ë¤ìê³¼ ê°ë¤:
+ </para>
+
+ <programlisting><![CDATA[<set name="emailAddresses" table="PERSON_EMAIL_ADDR">
+ <key column="PERSON_ID"/>
+ <element type="string" column="EMAIL_ADDR"/>
+</set>]]></programlisting>
+
+ <para>
+ ìì 매íê³¼ ë¹êµí ì°¨ì´ì ì <literal>element</literal> ë¶ë¶ì¸ë°, ê·¸ê²ì ê·¸ ì½ë ì
ì´ ë ë¤ë¥¸ ìí°í°ì ëí
+ 참조ë¤ì í¬í¨íì§ ìì ê²ì´ì§ë§ <literal>String</literal>(ì문ì ì´ë¦ì ê·¸ê²ì´ Hibernate 매í íì
/ë³íììì
+ ë¹ì ìê² ë§í´ì¤ë¤) íì
ì ììë¤ì ê°ì§ í ê°ì ì½ë ì
ì í¬í¨í ê²ìì Hibernateìê² ìë ¤ì¤ë¤. ì¼ë¨ ë¤ì <literal>set</literal>
+ ììì <literal>table</literal> ìì±ì ê·¸ ì½ë ì
ì ëí í
ì´ë¸ ì´ë¦ì ê²°ì íë¤. <literal>key</literal> ììë
+ ì½ë ì
í
ì´ë¸ ë´ìì foreign-key ì»¬ë¼ ì´ë¦ì ì ìíë¤. <literal>element</literal> ìì ë´ì ìë <literal>column</literal>
+ ìì±ì <literal>String</literal> ê°ë¤ì´ ì¤ì ë¡ ì ì¥ë ì»¬ë¼ ì´ë¦ì ì ìíë¤.
+ </para>
+
+ <para>
+ ì
ë°ì´í¸ë ì¤í¤ë§ë¥¼ ì´í´ë³´ë¼:
+ </para>
+
+ <programlisting><![CDATA[
+ _____________ __________________
+ | | | | _____________
+ | EVENTS | | PERSON_EVENT | | | ___________________
+ |_____________| |__________________| | PERSON | | |
+ | | | | |_____________| | PERSON_EMAIL_ADDR |
+ | *EVENT_ID | <--> | *EVENT_ID | | | |___________________|
+ | EVENT_DATE | | *PERSON_ID | <--> | *PERSON_ID | <--> | *PERSON_ID |
+ | TITLE | |__________________| | AGE | | *EMAIL_ADDR |
+ |_____________| | FIRSTNAME | |___________________|
+ | LASTNAME |
+ |_____________|
+ ]]></programlisting>
+
+ <para>
+ ë¹ì ì ì½ë ì
í
ì´ë¸ì íë¼ì´ë¨¸ë¦¬ í¤ê° ì¬ì¤ì ë 컬ë¼ë¤ì ì¬ì©íë í ê°ì í©ì± í¤(composite key)ìì ì ì ìë¤.
+ ì´ê²ì ëí ê°ì¸ì ëí´ email 주ìê° ì¤ë³µë ì ììì ì미íë©°, ê·¸ê²ì ì ííê² ì°ë¦¬ê° Javaìì setì íìë¡ íë
+ ìë¯¸ë¡ ì´ë¤.
+ </para>
+
+ <para>
+ ë§ì¹ ê°ì¸ë¤ê³¼ ì´ë²¤í¸ë¤ì ë§í¬ìì¼ì ì´ì ì ì°ë¦¬ê° ííë ê²ì²ë¼ ì´ì ë¹ì ì ììë¤ì ìëíê³ ì´ ì½ë ì
ì ì¶ê°í ì ìë¤.
+ ê·¸ê²ì Javaìì ëì¼í ì½ëì´ë¤.
+ </para>
+
+ <programlisting><![CDATA[private void addEmailToPerson(Long personId, String emailAddress) {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+ session.beginTransaction();
+
+ Person aPerson = (Person) session.load(Person.class, personId);
+
+ // The getEmailAddresses() might trigger a lazy load of the collection
+ aPerson.getEmailAddresses().add(emailAddress);
+
+ session.getTransaction().commit();
+}]]></programlisting>
+
+ <para>
+ ì§ê¸ ì°ë¦¬ë ì½ë ì
ì ì´ê¸°í ìí¤ëë° <emphasis>fetch</emphasis> ì§ì를 ì¬ì©íì§ ììë¤.
+ ê·¸ë¬ë¯ë¡ ì½ë ì
ì getter ë©ìëì ëí í¸ì¶ì ì½ë ì
ì ì´ê¸°í ìí¤ê¸° ìí´ ì¶ê°ì ì¸ select를 í¸ë¦¬ê±° ìí¬
+ ê²ì´ì´ì, ì°ë¦¬ë ê·¸ê²ì ìì를 ì¶ê°ìí¬ ì ìë¤. SQL ë¡ê·¸ë¥¼ ê´ì°°íê³ ì´ê²ì eager fetchë¡ ìµì í ìí¤ë ¤ê³
+ ìëíë¼.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-bidirectional" revision="1">
+ <title>Bi-directional associations</title>
+
+ <para>
+ ë¤ìì¼ë¡ ì°ë¦¬ë ìë°©í¥ ì°ê´ì 매íìí¬ ìì ì´ë¤-ê°ì¸ê³¼ ì´ë²¤í¸ ì¬ì´ì ì°ê´ì ë§ëë ê²ì Javaìì ì 측ë¤ìì ëìíë¤.
+ ë¬¼ë¡ ë°ì´í°ë² ì´ì¤ ì¤í¤ë§ë ë³ê²½ëì§ ìê³ , ì°ë¦¬ë ì¬ì í many-to-many ë¤ì¤ì±ì ê°ëë¤. ê´ê³í ë°ì´í°ë² ì´ì¤ë
+ ë¤í¸ì íë¡ê·¸ëë° ì¸ì´ ë³´ë¤ í¨ì¬ ë ì ì°íì¬ì, ê·¸ê²ì ë¤ë¹ê²ì´ì
ë°©í¥ê³¼ ê°ì ì´ë¤ ê²ì íìë¡ íì§ ìëë¤ - ë°ì´í°ë
+ ì´ë¤ ê°ë¥í ë°ì
ã
ì¼ë¡ ë³´ì¬ì§ ì ìê³ ê²ìë ì ìë¤.
+ </para>
+
+ <para>
+ 먼ì , ì°¸ì¬ìë¤ì ê°ì§ í ê°ì ì½ë ì
ì <literal>Event</literal> Event í´ëì¤ì ì¶ê°ìì¼ë¼:
+ </para>
+
+ <programlisting><![CDATA[private Set participants = new HashSet();
+
+public Set getParticipants() {
+ return participants;
+}
+
+public void setParticipants(Set participants) {
+ this.participants = participants;
+}]]></programlisting>
+
+ <para>
+ ì´ì <literal>Event.hbm.xml</literal> ë´ì ì°ê´ì ì´ ìª½ë 매ííë¼.
+ </para>
+
+ <programlisting><![CDATA[<set name="participants" table="PERSON_EVENT" inverse="true">
+ <key column="EVENT_ID"/>
+ <many-to-many column="PERSON_ID" class="events.Person"/>
+</set>]]></programlisting>
+
+ <para>
+ ë¹ì ì´ ë³¼ ì ìë¯ì´, ì´ê²ë¤ì ë 매í 문ìë¤ ë´ìì ì ê· <literal>set</literal> 매íë¤ì´ë¤. <literal>key</literal>ì
+ <literal>many-to-many</literal>ìì ì»¬ë¼ ì´ë¦ë¤ì ë 매í 문ìë¤ìì ë°ëì´ì§ë¤. ì¬ê¸°ì ê°ì¥ ì¤ìí ë¶ê°ë¬¼ì
+ <literal>Event</literal>ì ì½ë ì
매íì ê´í <literal>set</literal> ìì ë´ì ìë <literal>inverse="true"</literal>
+ ìì±ì´ë¤.
+ </para>
+
+ <para>
+ ì´ê²ì´ ì미íë ë°ë Hibernateê° ë ì¬ì´ì ë§í¬ì ëí ì 보를 ì íìê° ìì ë ë¤ë¥¸ 측-<literal>Person</literal> í´ëì¤-를 ì·¨í
+ ê²ì´ë¼ë ì ì´ë¤. ì¼ë¨ ë¹ì ì´ ì°ë¦¬ì ë ìí°í°ë¤ ì¬ì´ì ìë°©í¥ ë§í¬ê° ìì±ëë ë°©ë²ì ìë¤ë©´ ì´ê²ì ì´í´íê¸°ê° í¨ì¬ ë ì¬ì¸ ê²ì´ë¤.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-usingbidir">
+ <title>ìë°©í¥ ë§í¬ë¤ì ìì
í기</title>
+
+ <para>
+ 첫 ë²ì§¸ë¡ Hibernateê° ì ê· Java ìë¯¸ë¡ ì ìí¥ì ì£¼ì§ ììì ì¼ëì ëë¼. ì°ë¦¬ë ë¨ë°©í¥ ìì ìì <literal>Person</literal>ê³¼
+ <literal>Event</literal> ì¬ì´ì ì´ë»ê² í ê°ì ë§í¬ë¥¼ ìì±ìì¼°ëê°? ì°ë¦¬ë <literal>Event</literal> íì
ì ì¸ì¤í´ì¤ë¥¼
+ <literal>Person</literal> íì
ì ì´ë²¤í¸ 참조ë¤ì ê°ì§ ì½ë ì
ì ì¶ê°ìì¼°ë¤. ë°ë¼ì ëª
ë°±íê² ì°ë¦¬ê° ì´ ë§í¬ë¥¼ ìë°©í¥ì¼ë¡
+ ëìíëë¡ ë§ë¤ê³ ì ìíë¤ë©´, ì°ë¦¬ë ë¤ë¥¸ 측 ììì -íëì <literal>Person</literal> 참조를 íëì <literal>Event</literal>
+ ë´ì ìë ì½ë ì
ì ì¶ê°ìí´ì¼ë¡ì¨- ëì¼í ê²ì íí´ì¼ íë¤. ì´ "ì 측 ìì ë§í¬ ì¤ì í기"ë ì ëì ì¼ë¡ íìì ì´ê³ ë¹ì ì ê·¸ê²ì ííë
+ ê²ì ê²°ì½ ìì§ ë§ìì¼ íë¤.
+ </para>
+
+ <para>
+ ë§ì ê°ë°ìë¤ì ë°©ë¹ì±
ì íë¡ê·¸ë¨íê³ ì 측ë¤ì ì ííê² ì¤ì í기 ìí íëì ë§í¬ ê´ë¦¬ ë©ìëë¤ì ìì±ìí¨ë¤. ì를 ë¤ë©´ <literal>Person</literal>ìì :
+ </para>
+
+ <programlisting><![CDATA[protected Set getEvents() {
+ return events;
+}
+
+protected void setEvents(Set events) {
+ this.events = events;
+}
+
+public void addToEvent(Event event) {
+ this.getEvents().add(event);
+ event.getParticipants().add(this);
+}
+
+public void removeFromEvent(Event event) {
+ this.getEvents().remove(event);
+ event.getParticipants().remove(this);
+}]]></programlisting>
+
+ <para>
+ ì½ë ì
ì ëí get ë° set ë©ìëëì ì´ì protectedìì ì¸ì§íë¼ - ì´ê²ì ëì¼í í¨í¤ì§ ë´ì ìë í´ëì¤ë¤ê³¼ ìë¸í´ëì¤ë¤ì´
+ ê·¸ ë©ìëë¤ì ì ê·¼íë ê²ì íì©í´ì£¼ì§ë§, ê·¸ ë°ì 모ë ê²ë¤ì´ ê·¸ ì½ë ì
ë¤ì (ë¬¼ë¡ , ëê°) ì§ì ë§ì§ë ê²ì ê¸ì§ìí¨ë¤. ë¹ì ì
+ ë¤ë¥¸ 측 ìì ìë ì½ë ì
ì ëí´ ëì¼í ê²ì íí ê²ì´ë¤.
+ </para>
+
+ <para>
+ <literal>inverse</literal> 매í ìì±ì 무ìì¸ê°? ë¹ì ì ê²½ì°, ê·¸ë¦¬ê³ Javaì ê²½ì°, í ê°ì ìë°©í¥ ë§í¬ë ë¨ìí ì 측ë¤ì ëí
+ 참조ë¤ì ì ííê² ì¤ì íë 문ì ì´ë¤. íì§ë§ Hibernateë (컨ì¤í¸ë ì¸í¸ ì배를 í¼í기 ìí´ì) SQL <literal>INSERT</literal> 문ì¥ê³¼
+ <literal>UPDATE</literal> 문ì¥ì ì ííê² ë§ë ¨í기ì 충ë¶í ì 보를 ê°ê³ ìì§ ìì¼ë©°, ìë°©í¥ ì°ê´ë¤ì ì¬ë°ë¥´ê² ì²ë¦¬í기 ìí´
+ ì´ë¤ ëìì íìë¡ íë¤. ì°ê´ì í 측ì <literal>inverse</literal>ë¡ ë§ëë ê²ì 기본ì ì¼ë¡ ê·¸ê²ì 무ìíê³ ê·¸ê²ì ë¤ë¥¸ 측ì
+ <emphasis>ê±°ì¸(mirror)</emphasis>ë¡ ê°ì£¼íëë¡ Hibernateìê² ìë ¤ì¤ë¤. ê·¸ê²ì Hibernateê° íëì ë°©í¥ì± ë¤ë¹ê²ì´ì
모íì
+ í ê°ì SQL ì¤í¤ë§ë¡ ë³íìí¬ ë 모ë ìì ë¤ì ì í´ê²°íëë° íìì ì¸ ëª¨ë ê²ì´ë¤. ë¹ì ì´ ì¼ëì ëì´ì¼ íë ê·ì¹ë¤ì ê°ë¨íë¤ :
+ 모ë ìë°©í¥ ì°ê´ë¤ì í ìª½ì´ <literal>inverse</literal>ì¼ íìê° ìë¤. one-to-many ì°ê´ìì ê·¸ê²ì many-측ì´ì´ì¼ íê³ ,
+ many-to-many ì°ê´ìì ë¹ì ì ì´ë 측ì´ë ì íí ì ìì¼ë©° ì°¨ì´ì ì ìë¤.
+ </para>
+
+ </sect2>
+
+ <para>
+ Let's turn this into a small web application.
+ </para>
+
+ </sect1>
+
+ <sect1 id="tutorial-webapp">
+ <title>íí¸ 3 - EventManager ì¹ ì´í리ì¼ì´ì
</title>
+
+ <para>
+ Hibernate ì¹ ì´í리ì¼ì´ì
ì ëë¶ë¶ì ì¤í ëì¼ë¡ ì´í리ì¼ì´ì
ê³¼ ê°ì´ <literal>Session</literal>ê³¼
+ <literal>Transaction</literal>ì ì¬ì©íë¤. íì§ë§ ëªëª ê³µíµ í¨í´ë¤ì´ ì ì©íë¤. ì°ë¦¬ë ì´ì
+ <literal>EventManagerServlet</literal>를 ìì±íë¤. ì´ ìë¸ë¦¿ì ë°ì´í°ë² ì´ì¤ ë´ì ì ì¥ë 모ë ì´ë²¤í¸ë¤ì ëì´í ì ìê³ ,
+ ê·¸ê²ì ìë¡ì´ ì´ë²¤í¸ë¤ì ì
ë ¥í기 ìí HTML formì ì ê³µíë¤.
+ </para>
+
+ <sect2 id="tutorial-webapp-servlet" revision="2">
+ <title>기본 ìë¸ë¦¿ ìì±í기</title>
+
+ <para>
+ ë¤ì ì¥ìì ì°ë¦¬ë Hibernate를 Tomcat ë° WebWorkì íµí©ìí¨ë¤. <literal>EventManager</literal>ë ì°ë¦¬ì ì±ì¥íë
+ ì´í리ì¼ì´ì
ì ëì´ì ê°ë¹íì§ ëª»íë¤.
+ ë¹ì ì ìì¤ ëë í 리ìì <literal>events</literal> í¨í¤ì§ ë´ì ìë¡ì´ í´ëì¤ë¥¼ ìì±ìì¼ë¼:
+ </para>
+
+ <programlisting><![CDATA[package events;
+
+// Imports
+
+public class EventManagerServlet extends HttpServlet {
+
+ // Servlet code
+}]]></programlisting>
+
+ <para>
+ ìë¸ë¦¿ì HTTP <literal>GET</literal> ìì²ë¤ ë§ì ì²ë¦¬íë¯ë¡, ì°ë¦¬ê° 구ííë ë©ìëë <literal>doGet()</literal>ì´ë¤:
+ </para>
+
+ <programlisting><![CDATA[protected void doGet(HttpServletRequest request,
+ HttpServletResponse response)
+ throws ServletException, IOException {
+
+ SimpleDateFormat dateFormatter = new SimpleDateFormat("dd.MM.yyyy");
+
+ try {
+ // Begin unit of work
+ HibernateUtil.getSessionFactory()
+ .getCurrentSession().beginTransaction();
+
+ // Process request and render page...
+
+ // End unit of work
+ HibernateUtil.getSessionFactory()
+ .getCurrentSession().getTransaction().commit();
+
+ } catch (Exception ex) {
+ HibernateUtil.getSessionFactory()
+ .getCurrentSession().getTransaction().rollback();
+ throw new ServletException(ex);
+ }
+
+}]]></programlisting>
+
+ <para>
+ ì°ë¦¬ê° ì¬ê¸°ì ì ì©íë í¨í´ì <emphasis>session-per-request</emphasis>ì´ë¤. íëì ìì²ì´
+ ìë¸ë¦¿ì ëë¬í ë, íëì ìë¡ì´ Hibernate <literal>Session</literal>ì´
+ <literal>SessionFactory</literal> ìì <literal>getCurrentSession()</literal>ì ëí
+ 첫ë²ì§¸ í¸ì¶ì íµí´ ì´ë¦°ë¤. ê·¸ë íëì ë°ì´í°ë² ì´ì¤ í¸ëìì
ì´ ììëê³ , 모ë ë°ì´í° ì ê·¼ì´ íëì í¸ëìì
+ ë´ìì ë°ìíë í, ë°ì´í°ê° ì½íì§ê±°ë 기ë¡ëëë° ë¬¸ì ê° ìë¤(ì°ë¦¬ë ì´í리ì¼ì´ì
ë¤ ë´ìì auto-commit 모ë를
+ ì¬ì©íì§ ìëë¤).
+ </para>
+
+ <para>
+ 모ë ë°ì´í°ë² ì´ì¤ ì¤í¼ë ì´ì
ê°ê°ì ëí´ ìë¡ì´ Hibernate <literal>Session</literal>ì ì¬ì©íì§ <emphasis>ë§ë¼</emphasis>.
+ ì ì²´ ìì²ì ëí´ ììì§ìì§ í ê°ì Hibernate <literal>Session</literal>ì ì¬ì©íë¼. ê·¸ê²ì´ ìëì ì¼ë¡ íì¬ì ìë° ì°ë ëì ë°ì¸ëëëë¡
+ <literal>getCurrentSession()</literal>ì ì¬ì©íë¼.
+ </para>
+
+ <para>
+ ë¤ìì¼ë¡, ìì²ì ê°ë¥í ì¡ì
ë¤ì´ ì²ë¦¬ëê³ ìëµ HTMLì´ ë ëë§ëë¤. ì°ë¦¬ë ê³§ì¥ ê·¸ë¶ë¶ì¼ë¡ ê° ê²ì´ë¤.
+ </para>
+
+ <para>
+ ë§ì§ë§ì¼ë¡, íë¡ì¸ì±ê³¼ ë ëë§ì´ ìë£ë ë ìì
ë¨ìê° ì¢
ë£ëë¤. ë§ì¼ ì´ë¤ 문ì ê° íë¡ì¸ì±ê³¼ ë ëë§ ëìì ë°ìë ê²½ì°,
+ íëì ìì¸ìí©ì´ ëì ¸ì§ ê²ì´ê³ ë°ì´í°ë² ì´ì¤ í¸ëìì
ì 롤백ë ê²ì´ë¤. ì´ê²ì <literal>session-per-request</literal>ì
+ ìë£ìí¨ë¤. 모ë ìë¸ë¦¿ ë´ì ìë í¸ëìì
구í ì½ë ëì ì ë¹ì ì ëí ìë¸ë¦¿ íí°ë¥¼ ì¬ì©í ì ìë¤.
+ <emphasis>Open Session in View</emphasis>ë¡ ëª
ëª
ëë ì´ í¨í´ì ëí ì¶ê° ì ë³´ë Hibernate ì¹ ì¬ì´í¸ì ìí¤ë¥¼ ë³´ë¼.
+ ë¹ì ì ìë¸ë¦¿ ë´ììê° ìë JSP ë´ì ë¹ì ì 뷰를 ë ëë§íë ê²ì ê³ ë ¤í ë ê·¸ê²ì íìë¡ í ê²ì´ë¤.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-webapp-processing" revision="1">
+ <title>íë¡ì¸ì±ê³¼ ë ëë§</title>
+
+ <para>
+ ìì²ì ì²ë¦¬ì íì´ì§ì ë ëë§ì 구ííì.
+ </para>
+
+<programlisting><![CDATA[// Write HTML header
+PrintWriter out = response.getWriter();
+out.println("<html><head><title>Event Manager</title></head><body>");
+
+// Handle actions
+if ( "store".equals(request.getParameter("action")) ) {
+
+ String eventTitle = request.getParameter("eventTitle");
+ String eventDate = request.getParameter("eventDate");
+
+ if ( "".equals(eventTitle) || "".equals(eventDate) ) {
+ out.println("<b><i>Please enter event title and date.</i></b>");
+ } else {
+ createAndStoreEvent(eventTitle, dateFormatter.parse(eventDate));
+ out.println("<b><i>Added event.</i></b>");
+ }
+}
+
+// Print page
+printEventForm(out);
+listEvents(out, dateFormatter);
+
+// Write HTML footer
+out.println("</body></html>");
+out.flush();
+out.close();]]></programlisting>
+
+ <para>
+ Javaì HTMLì´ í¼í©ë ì´ ì½ë©ì´ ë³´ë¤ ë³µì¡í ì´í리ì¼ì´ì
ìì 기ì¤ì´ ë ì ìë¤ í ì§ë¼ë, ì°ë¦¬ë
+ ë¨ì§ ì´ íí ë¦¬ì¼ ë´ìì 기본 Hibernate ê°ë
ë¤ì ì¤ëª
íê³ ììì ì¼ëì ëë¼. ì½ëë
+ íëì HTML í¤ëì íëì footer를 í린í¸íë¤. ì´ íì´ì§ ë´ì ì´ë²¤í¸ ìí¸ë¦¬ë¥¼ ìí
+ íëì HTML formê³¼ ë°ì´í°ë² ì´ì¤ ë´ì ìë 모ë ì´ë²¤í¸ë¤ì 목ë¡ì´ í린í¸ëë¤. 첫 ë²ì§¸ ë©ìëë
+ ìíì ì´ê³ ì¤ì§ HTMLì ì¶ë ¥íë¤:
+ </para>
+
+ <programlisting><![CDATA[private void printEventForm(PrintWriter out) {
+ out.println("<h2>Add new event:</h2>");
+ out.println("<form>");
+ out.println("Title: <input name='eventTitle' length='50'/><br/>");
+ out.println("Date (e.g. 24.12.2009): <input name='eventDate' length='10'/><br/>");
+ out.println("<input type='submit' name='action' value='store'/>");
+ out.println("</form>");
+}]]></programlisting>
+
+ <para>
+ <literal>listEvents()</literal> ë©ìëë íëì ì§ì를 ì¤íí기 ìí´ì íì¬ì
+ ì°ë ëì ê²°í©ë Hibernate <literal>Session</literal>ì ì¬ì©íë¤:
+ </para>
+
+ <programlisting><![CDATA[private void listEvents(PrintWriter out, SimpleDateFormat dateFormatter) {
+
+ List result = HibernateUtil.getSessionFactory()
+ .getCurrentSession().createCriteria(Event.class).list();
+ if (result.size() > 0) {
+ out.println("<h2>Events in database:</h2>");
+ out.println("<table border='1'>");
+ out.println("<tr>");
+ out.println("<th>Event title</th>");
+ out.println("<th>Event date</th>");
+ out.println("</tr>");
+ for (Iterator it = result.iterator(); it.hasNext();) {
+ Event event = (Event) it.next();
+ out.println("<tr>");
+ out.println("<td>" + event.getTitle() + "</td>");
+ out.println("<td>" + dateFormatter.format(event.getDate()) + "</td>");
+ out.println("</tr>");
+ }
+ out.println("</table>");
+ }
+}]]></programlisting>
+
+ <para>
+ ë§ì§ë§ì¼ë¡, <literal>store</literal> ì¡ì
ì <literal>createAndStoreEvent()</literal>
+ ë©ìëë¡ ëì¤í¨ì¹ëë¤. ê·¸ê²ì íì¬ ì°ë ëì <literal>Session</literal>ì ì¬ì©íë¤:
+ </para>
+
+ <programlisting><![CDATA[protected void createAndStoreEvent(String title, Date theDate) {
+ Event theEvent = new Event();
+ theEvent.setTitle(title);
+ theEvent.setDate(theDate);
+
+ HibernateUtil.getSessionFactory()
+ .getCurrentSession().save(theEvent);
+}]]></programlisting>
+
+ <para>
+ ì¦ ìë¸ë¦¿ì´ ìì±ëë¤. ìë¸ë¦¿ì ëí ìì²ì íëì ë¨ì¼ <literal>Session</literal>ê³¼
+ <literal>Transaction</literal> ë´ìì ì²ë¦¬ë ê²ì´ë¤. ì´ì ì²ë¼ ì¤í ëì¼ë¡ ì´í리ì¼ì´ì
ìì,
+ Hibernateë ì´ë¤ ê°ì²´ë¤ì ì¤í ì¤ì¸ íì¬ ì°ë ëì ìëì ì¼ë¡ ë°ì¸ëìí¬ ì ìë¤. ì´ê²ì ë¹ì ì ì½ë를
+ ê³ì¸µí ìí¤ê³ ë¹ì ì´ ì¢ìíë ììì ë°©ë²ì¼ë¡ <literal>SessionFactory</literal>ì ì ê·¼íë ìì 를
+ ë¹ì ìê² ë¶ì¬íë¤. ëê° ë¹ì ì ë³´ë¤ ì¸ë ¨ë ì¤ê³ë¥¼ ì¬ì©í ê²ì´ê³ ë°ì´í° ì ê·¼ ì½ë를 ë°ì´í° ì ê·¼ ê°ì²´ë¤ ë´ë¡
+ ì´ëìí¬ ê²ì´ë¤(DAO í¨í´). ì¶ê° ìì ë¤ì Hibernate ìí¤ë¥¼ ë³´ë¼.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-webapp-deploy">
+ <title>ë°°ì¹í기 ê·¸ë¦¬ê³ í
ì¤í¸í기</title>
+
+ <para>
+ ì´ ì´í리ì¼ì´ì
ì ë°°ì¹í기 ìí´ì ë¹ì ì íëì ì¹ ìì¹´ì´ë¸, WAR를 ìì±ìì¼ì¼ íë¤. ë¤ì Ant targetì
+ ë¹ì ì <literal>build.xml</literal> ë´ì ì¶ê°íë¼:
+ </para>
+
+<programlisting><![CDATA[<target name="war" depends="compile">
+ <war destfile="hibernate-tutorial.war" webxml="web.xml">
+ <lib dir="${librarydir}">
+ <exclude name="jsdk*.jar"/>
+ </lib>
+
+ <classes dir="${targetdir}"/>
+ </war>
+</target>]]></programlisting>
+
+ <para>
+ ì´ targetì ë¹ì ì íë¡ì í¸ ëë í 리 ë´ì <literal>hibernate-tutorial.war</literal>ë¡ ëª
ëª
ë
+ íëì íì¼ì ìì±ìí¨ë¤. ê·¸ê²ì ë¹ì ì íë¡ì í¸ì 기본 ëë í 리 ë´ì 기ëëë 모ë ë¼ì´ë¸ë¬ë¦¬ë¤ê³¼
+ <literal>web.xml</literal> ëì¤í¬ë¦½í°ë¥¼ í¨í¤ì§íë¤:
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
+<web-app version="2.4"
+ xmlns="http://java.sun.com/xml/ns/j2ee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
+
+ <servlet>
+ <servlet-name>Event Manager</servlet-name>
+ <servlet-class>events.EventManagerServlet</servlet-class>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>Event Manager</servlet-name>
+ <url-pattern>/eventmanager</url-pattern>
+ </servlet-mapping>
+</web-app>]]></programlisting>
+
+ <para>
+ ë¹ì ì´ ì¹ ì´í리ì¼ì´ì
ì ì»´íì¼íê³ ë°°ì¹í기 ì ì, íëì ë¶ê°ì ì¸ ë¼ì´ë¸ë¬ë¦¬ê° íìí¨ì ë
¸í¸íë¼:
+ <literal>jsdk.jar</literal>. ë¹ì ì´ ì´ë¯¸ ì´ ë¼ì´ë¸ë¬ë¦¬ë¥¼ ê°ê³ ìì§ ìì ê²½ì°, ì´ê²ì Java servlet
+ development kitì´ë©°, Sun ì¹ ì¬ì´í¸ë¡ë¶í° ê·¸ê²ì ì»ì´ì ê·¸ê²ì ë¹ì ì ë¼ì´ë¸ë¬ë¦¬ ëë í 리ì ë³µì¬íë¼.
+ íì§ë§ ê·¸ê²ì ì¤ì§ ì»´íì¼ ììë§ ì¬ì©ë ê²ì´ê³ WAR í¨í¤ì§ììë ì ì¸ëë¤.
+ </para>
+
+ <para>
+ ë¹ëíê³ ë°°ì¹í기 ìí´ ë¹ì ì íë¡ì í¸ ëë í 리 ë´ìì <literal>ant war</literal>를 í¸ì¶íê³
+ <literal>hibernate-tutorial.war</literal> íì¼ì ë¹ì ì Tomcat <literal>webapp</literal>
+ ëë í ë¦¬ë¡ ë³µì¬íë¼. ë§ì¼ ë¹ì ì´ Tomcatì ì¤ì¹íì§ ììë¤ë©´, ê·¸ê²ì ë´ë ¤ë°ì ì¤ì¹ ì§ì¹¨ë¤ì ë°ë¥´ë¼. ë¹ì ì
+ ì´ ì´í리ì¼ì´ì
ì ë°°ì¹í기 ìí´ ììì Tomcat 구ì±ì ë³ê²½íì§ ììì¼ íë¤.
+ </para>
+
+ <para>
+ ì¼ë¨ ë°°ì¹íê³ Tomcatì´ ì¤íì¤ì´ë©´, <literal>http://localhost:8080/hibernate-tutorial/eventmanager</literal>ë¡
+ ì´í리ì¼ì´ì
ì ì ê·¼íë¼. 첫 ë²ì§¸ ìì²ì´ ë¹ì ì ìë¸ë¦¿ì ëë¬í ë Hibernateê° ì´ê¸°í(<literal>HibernateUtil</literal>
+ ë´ì ìë static initializerê° í¸ì¶ëë¤) ëë ê²ì 보기 ìí´ ê·¸ë¦¬ê³ ë§ì¼ ì´ë¤ ìì¸ìí©ë¤ì´ ë°ìí ê²½ì°
+ ìì¸í ì¶ë ¥ì ì»ê¸° ìí´ì Tomcat ë¡ê·¸ë¥¼ ì§ì¼ë³´ëë¡ íë¼.
+ </para>
+
+ </sect2>
+ </sect1>
+
+ <sect1 id="tutorial-summary" revision="1">
+ <title>ìì½</title>
+
+ <para>
+ ì´ íí 리ì¼ì ê°ë¨í ì¤í ëì¼ë¡ Hibernate ì´í리ì¼ì´ì
ê³¼ íëì ìì ì¹ ì´í리ì¼ì´ì
ì ìì±íë 기ì´ë¥¼ ë¤ë£¨ìë¤.
+ </para>
+
+ <para>
+ ë§ì¼ ë¹ì ì´ ì´ë¯¸ Hibernateì ìì ì´ ìë¤ê³ ëëë¤ë©´, ë¹ì ì´ í¥ë¯¸ë¥¼ ì°¾ë 주ì ë¤ì ëí 참조 문ì 목차를 ê³ì ë¸ë¼ì°ì§íë¼
+ - ê°ì¥ ë§ì´ ìì²ëë ê²ì í¸ëìì
ì²ë¦¬(<xref linkend="transactions"/>), íì¹ í¼í¬ë¨¼ì¤(<xref linkend="performance"/>),
+ ëë API ì¬ì©ë²(<xref linkend="objectstate"/>), ê·¸ë¦¬ê³ ì§ì í¹ì§ë¤(<xref linkend="objectstate-querying"/>)ì´ë¤.
+ </para>
+
+ <para>
+ ë ë§ì(í¹íë) íí 리ì¼ë¤ì ëí´ìë Hibernate ì¹ ì¬ì´í¸ë¥¼ ì²´í¬íë ê²ì ìì§ ë§ë¼.
+ </para>
+
+ </sect1>
+
+</chapter>
\ No newline at end of file
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/xml.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/xml.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ko-KR/src/main/docbook/modules/xml.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,273 @@
+<chapter id="xml">
+ <title>XML 매í</title>
+
+ <para><emphasis>
+ ì´ê²ì Hibernate3.0ìì ì¤íì ì¸ í¹ì§ì´ê³ ë§¤ì° íëì ì¼ë¡ ê°ë° ì¤ì ììì ë
¸í¸íë¼.
+ </emphasis></para>
+
+ <sect1 id="xml-intro" revision="1">
+ <title>XML ë°ì´í°ë¡ ìì
í기</title>
+
+ <para>
+ Hibernateë ë¹ì ì´ ìì POJOë¤ë¡ ìì
íë ê²ê³¼ ì주 ëì¼í ë°©ë²ì¼ë¡ ìì XML ë°ì´í°ì ìì
íëë¡ í´ì¤ë¤. íì±ë XML í¸ë¦¬ë
+ ë¨ì§ ê°ì²´ ë 벨ìì ê´ê³í ë°ì´í°ë¥¼ ëíë´ë ë ë¤ë¥¸ ë°©ë²ì¼ë¡ ê°ì£¼ë ì ìë¤. íëì íì±ë XML í¸ë¦¬ë POJOë¤ ëì , ê°ì²´ ë 벨ìì
+ ê´ê³í ë°ì´í°ë¥¼ íííë ë¨ì§ ë ë¤ë¥¸ ë°©ë²ì¼ë¡ ê°ì£¼ë ì ìë¤.
+ </para>
+
+ <para>
+ Hibernateë XML í¸ë¦¬ë¤ì ì²ë¦¬íë APIë¡ì dom4j를 ì§ìíë¤. ë¹ì ì ë°ì´í°ë² ì´ì¤ë¡ë¶í° dom4j í¸ë¦¬ë¤ì ê²ìíê³ ë¹ì ì´ ê·¸ í¸ë¦¬ë¥¼
+ ë°ì´í°ë² ì´ì¤ì ìëì ì¼ë¡ ë기íìí¤ê¸° ìí´ ì´ë¤ ë³ê²½ì ííëë¡ íë ì§ìë¤ì ìì±í ì ìë¤. ë¹ì ì ì¬ì§ì´ XML 문ì를 ì·¨íê³ ,
+ dom4j를 ì¬ì©íì¬ ê·¸ê²ì íì±íê³ , Hibernateì ë¤ì 기본ì ì¸ ì¤í¼ë ì´ì
ë¤ ì¤ ì´ë¤ ê²ì¼ë¡ì ê·¸ê²ì ë°ì´í°ë² ì´ì¤ì ì ì¥ìí¬ ì ìë¤:
+ <literal>persist(), saveOrUpdate(), merge(), delete(), replicate()</literal>(merging(ë³í©)ì ìì§ ì§ìëì§
+ ìëë¤).
+ </para>
+
+ <para>
+ ì´ í¹ì§ì ë°ì´í° ê°ì ¸ì¤ê¸°/ë´ë³´ë´ê¸°,JMS ëë SOAP ê·¸ë¦¬ê³ XSLT-기ë°ì ë í¬í
ì íµí ìí°í° ë°ì´í°ì 구체í를 í¬í¨íë ë§ì
+ ì´í리ì¼ì´ì
ë¤ì ê°ëë¤.
+ </para>
+
+ <para>
+ íëì 매íì í´ëì¤ë¤ì íë¡í¼í°ë¤ê³¼ XML 문ìì ë
¸ëë¤ì ë°ì´í°ë² ì´ì¤ë¡ ëìì 매íìí¤ëë° ì¬ì©ë ì ìê±°ë, ë§ì¼ 매íí í´ëì¤ê°
+ ì¡´ì¬íì§ ìì ê²½ì°, ê·¸ê²ì ë¨ì§ XMLì 매íìí¤ëë° ì¬ì©ë ìë ìë¤.
+ </para>
+
+ <sect2 id="xml-intro-mapping">
+ <title>XMLê³¼ í´ëì¤ ë§¤íì í¨ê» ì§ì í기</title>
+
+ <para>
+ ë¤ìì POJOì XMLì ëìì 매íìí¤ë ìì ì´ë¤ :
+ </para>
+
+ <programlisting><![CDATA[<class name="Account"
+ table="ACCOUNTS"
+ node="account">
+
+ <id name="accountId"
+ column="ACCOUNT_ID"
+ node="@id"/>
+
+ <many-to-one name="customer"
+ column="CUSTOMER_ID"
+ node="customer/@id"
+ embed-xml="false"/>
+
+ <property name="balance"
+ column="BALANCE"
+ node="balance"/>
+
+ ...
+
+</class>]]></programlisting>
+ </sect2>
+
+ <sect2 id="xml-onlyxml">
+ <title>XML 매íë§ì ì§ì í기</title>
+
+ <para>
+ ë¤ìì POJO í´ëì¤ê° ì¡´ì¬íì§ ìë ìì ì´ë¤:
+ </para>
+
+ <programlisting><![CDATA[<class entity-name="Account"
+ table="ACCOUNTS"
+ node="account">
+
+ <id name="id"
+ column="ACCOUNT_ID"
+ node="@id"
+ type="string"/>
+
+ <many-to-one name="customerId"
+ column="CUSTOMER_ID"
+ node="customer/@id"
+ embed-xml="false"
+ entity-name="Customer"/>
+
+ <property name="balance"
+ column="BALANCE"
+ node="balance"
+ type="big_decimal"/>
+
+ ...
+
+</class>]]></programlisting>
+
+ <para>
+ ì´ ë§¤íì dom4j í¸ë¦¬ë¡ì ëë íë¡í¼í° name/value ìë¤(java <literal>Map</literal>ë¤)ì ê·¸ëíë¡ì ë°ì´í°ì
+ ì ê·¼íë ê²ì ë¹ì ìê² íì©í´ì¤ë¤. íë¡í¼í° ì´ë¦ë¤ì HQL ì§ìë¤ ë´ìì 참조ë ìë ìë ììíê² ë
¼ë¦¬ì ì¸ êµ¬ì¡°ì²´ë¤ì´ë¤.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="xml-mapping" revision="1">
+ <title>XML 매í ë©íë°ì´í°</title>
+
+ <para>
+ ë§ì Hibernate 매í ììë¤ì <literal>node</literal> ìì±ì ìì©íë¤. ì´ê²ì ë¹ì ì´ íë¡í¼í° ëë ìí°í° ë°ì´í°ë¥¼ ìì íë
+ XML ìì±ì´ë ììì ì´ë¦ì ì§ì íëë¡ íë¤. <literal>node</literal> ìì±ì í¬ë§·ì ë¤ì ì¤ íëì´ì´ì¼ íë¤:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para><literal>"element-name"</literal> - ëª
ëª
ë XML ììë¡ ë§¤íìí¨ë¤</para>
+ </listitem>
+ <listitem>
+ <para><literal>"@attribute-name"</literal> - ëª
ëª
ë XML ìì±ì¼ë¡ 매íìí¨ë¤ </para>
+ </listitem>
+ <listitem>
+ <para><literal>"."</literal> - ë¶ëª¨ ììë¡ ë§¤í ìí¨ë¤</para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>"element-name/@attribute-name"</literal> -
+ ëª
ëª
ë ììì ëª
ëª
ë ìì±ì¼ë¡ 매íìí¨ë¤
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ì½ë ì
ë¤ê³¼ ë¨ì¼ ê° ì½ë ì
ë¤ì ê²½ì°, ì¶ê°ì ì¸ <literal>embed-xml</literal> ìì±ì´ ì¡´ì¬íë¤. ë§ì¼ <literal>embed-xml="true"</literal>
+ ì¼ ê²½ì°, ì°ê´ë ìí°í°(ëë value íì
ì ê°ì§ ì½ë ì
)ì ëí ëí´í¸ XML í¸ë¦¬ë ê·¸ ì°ê´ì ìì íë ìí°í°ì ëí XML í¸ë¦¬ ìì
+ ì§ì ì½ì
ë ê²ì´ë¤. ê·¸ ë°ì ê²½ì° <literal>embed-xml="false"</literal> ì¼ ê²½ì°, 참조ë ìë³ì ê° ë§ì´ ë¨ì¼ í¬ì¸í¸ ì°ê´ë¤ì
+ ëí´ ëíë ê²ì´ê³ ì½ë ì
ë¤ì ë¨ìí ì í ëíëì§ ìì ê²ì´ë¤.
+ </para>
+
+ <para>
+ ë¹ì ì ë무 ë§ì ì°ê´ë¤ì ëí´ <literal>embed-xml="true"</literal>ë¡ ë¨ê²¨ëì§ ë§ëë¡ ì£¼ìí´ì¼ íë¤. ìëíë©´ XMLì´
+ ìíì ì¼ë¡ ì ì²ë¦¬íì§ ëª»í기 ë문ì´ë¤!
+ </para>
+
+ <programlisting><![CDATA[<class name="Customer"
+ table="CUSTOMER"
+ node="customer">
+
+ <id name="id"
+ column="CUST_ID"
+ node="@id"/>
+
+ <map name="accounts"
+ node="."
+ embed-xml="true">
+ <key column="CUSTOMER_ID"
+ not-null="true"/>
+ <map-key column="SHORT_DESC"
+ node="@short-desc"
+ type="string"/>
+ <one-to-many entity-name="Account"
+ embed-xml="false"
+ node="account"/>
+ </map>
+
+ <component name="name"
+ node="name">
+ <property name="firstName"
+ node="first-name"/>
+ <property name="initial"
+ node="initial"/>
+ <property name="lastName"
+ node="last-name"/>
+ </component>
+
+ ...
+
+</class>]]></programlisting>
+
+ <para>
+ ì´ ê²½ì°ì, ì°ë¦¬ë ì¤ì account ë°ì´í°ê° ìë, account idë¤ì ê°ì§ ì½ë ì
ì ì½ì
ìí¤ê¸°ë¡ ê²°ì íë¤. ë¤ì HQL ì§ì:
+ </para>
+
+ <programlisting><![CDATA[from Customer c left join fetch c.accounts where c.lastName like :lastName]]></programlisting>
+
+ <para>
+ ë ë¤ìê³¼ ê°ì ë°ì´í°ì
ë¤ì ë°íí ê²ì´ë¤:
+ </para>
+
+ <programlisting><![CDATA[<customer id="123456789">
+ <account short-desc="Savings">987632567</account>
+ <account short-desc="Credit Card">985612323</account>
+ <name>
+ <first-name>Gavin</first-name>
+ <initial>A</initial>
+ <last-name>King</last-name>
+ </name>
+ ...
+</customer>]]></programlisting>
+
+ <para>
+ ë§ì¼ ë¹ì ì´ <literal><one-to-many></literal> 매íì ëí´ <literal>embed-xml="true"</literal>를 ì¤ì í ê²½ì°,
+ ë°ì´í°ë ë¤ìê³¼ ê°ì´ ë³´ì¼ ìë ìë¤:
+ </para>
+
+ <programlisting><![CDATA[<customer id="123456789">
+ <account id="987632567" short-desc="Savings">
+ <customer id="123456789"/>
+ <balance>100.29</balance>
+ </account>
+ <account id="985612323" short-desc="Credit Card">
+ <customer id="123456789"/>
+ <balance>-2370.34</balance>
+ </account>
+ <name>
+ <first-name>Gavin</first-name>
+ <initial>A</initial>
+ <last-name>King</last-name>
+ </name>
+ ...
+</customer>]]></programlisting>
+
+ </sect1>
+
+
+ <sect1 id="xml-manipulation" revision="1">
+ <title>XML ë°ì´í° ì²ë¦¬í기</title>
+
+ <para>
+ ì°ë¦¬ì ì´í리ì¼ì´ì
ë´ìì XML 문ìë¤ì ë¤ì ì½ì´ë¤ì´ê³ ì
ë°ì´í¸ ìí¤ì. ì°ë¦¬ë dom4j ì¸ì
ì ì»ì´ì ì´ê²ì ííë¤:
+ </para>
+
+ <programlisting><![CDATA[Document doc = ....;
+
+Session session = factory.openSession();
+Session dom4jSession = session.getSession(EntityMode.DOM4J);
+Transaction tx = session.beginTransaction();
+
+List results = dom4jSession
+ .createQuery("from Customer c left join fetch c.accounts where c.lastName like :lastName")
+ .list();
+for ( int i=0; i<results.size(); i++ ) {
+ //add the customer data to the XML document
+ Element customer = (Element) results.get(i);
+ doc.add(customer);
+}
+
+tx.commit();
+session.close();]]></programlisting>
+
+ <programlisting><![CDATA[Session session = factory.openSession();
+Session dom4jSession = session.getSession(EntityMode.DOM4J);
+Transaction tx = session.beginTransaction();
+
+Element cust = (Element) dom4jSession.get("Customer", customerId);
+for ( int i=0; i<results.size(); i++ ) {
+ Element customer = (Element) results.get(i);
+ //change the customer name in the XML and database
+ Element name = customer.element("name");
+ name.element("first-name").setText(firstName);
+ name.element("initial").setText(initial);
+ name.element("last-name").setText(lastName);
+}
+
+tx.commit();
+session.close();]]></programlisting>
+
+ <para>
+ XML 기ë°ì ë°ì´í° ê°ì ¸ì¤ê¸°/ë´ë³´ë´ê¸°ë¥¼ 구ííëë° ì´ í¹ì§ê³¼ Hibernateì <literal>replicate()</literal> ì¤í¼ë ì´ì
ì
+ ê²°í©ìí¤ë ê²ì´ ë§¤ì° ì ì©íë¤.
+ </para>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/css/html.css
===================================================================
--- trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/css/html.css (rev 0)
+++ trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/css/html.css 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,97 @@
+A {
+ color: #003399;
+}
+
+A:active {
+ color: #003399;
+}
+
+A:visited {
+ color: #888888;
+}
+
+P, OL, UL, LI, DL, DT, DD, BLOCKQUOTE {
+ color: #000000;
+}
+
+TD, TH, SPAN {
+ color: #000000;
+}
+
+BLOCKQUOTE {
+ margin-right: 0px;
+}
+
+
+H1, H2, H3, H4, H5, H6 {
+ color: #000000;
+ font-weight:500;
+ margin-top:10px;
+ padding-top:15px;
+}
+
+TABLE {
+ border-collapse: collapse;
+ border-spacing:0;
+ border: 1px thin black;
+ empty-cells: hide;
+}
+
+TD {
+ padding: 4pt;
+}
+
+H1 { font-size: 150%; }
+H2 { font-size: 140%; }
+H3 { font-size: 110%; font-weight: bold; }
+H4 { font-size: 110%; font-weight: bold;}
+H5 { font-size: 100%; font-style: italic; }
+H6 { font-size: 100%; font-style: italic; }
+
+TT {
+font-size: 90%;
+ font-family: "Courier New", Courier, monospace;
+ color: #000000;
+}
+
+PRE {
+font-size: 100%;
+ padding: 5px;
+ border-style: solid;
+ border-width: 1px;
+ border-color: #CCCCCC;
+ background-color: #F4F4F4;
+}
+
+UL, OL, LI {
+ list-style: disc;
+}
+
+HR {
+ width: 100%;
+ height: 1px;
+ background-color: #CCCCCC;
+ border-width: 0px;
+ padding: 0px;
+ color: #CCCCCC;
+}
+
+.variablelist {
+ padding-top: 10;
+ padding-bottom:10;
+ margin:0;
+}
+
+.itemizedlist, UL {
+ padding-top: 0;
+ padding-bottom:0;
+ margin:0;
+}
+
+.term {
+ font-weight:bold;
+}
+
+
+
+
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/AuthorWork.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/AuthorWork.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/AuthorWork.zargo
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/AuthorWork.zargo
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/CustomerOrderProduct.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/CustomerOrderProduct.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/CustomerOrderProduct.zargo
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/CustomerOrderProduct.zargo
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/EmployerEmployee.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/EmployerEmployee.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/EmployerEmployee.zargo
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/EmployerEmployee.zargo
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/full_cream.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/full_cream.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/full_cream.svg
===================================================================
--- trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/full_cream.svg (rev 0)
+++ trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/full_cream.svg 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,429 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"
+[
+ <!ATTLIST svg
+ xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink">
+]>
+<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
+<svg
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="354.331"
+ height="336.614"
+ id="svg1">
+ <defs
+ id="defs3">
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop128" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="1"
+ id="stop129" />
+ </linearGradient>
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient130"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ <radialGradient
+ cx="0.5"
+ cy="0.5"
+ fx="0.5"
+ fy="0.5"
+ r="0.5"
+ id="radialGradient131"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ </defs>
+ <g
+ transform="matrix(0.823795,0,0,0.823795,0.120302,5.25349)"
+ style="font-size:12;"
+ id="g659">
+ <rect
+ width="212.257"
+ height="57.2441"
+ x="17.9576"
+ y="100.132"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect137" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ transform="matrix(0.743454,0,0,0.482981,6.46949,52.2178)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect132" />
+ </g>
+ <rect
+ width="325.86"
+ height="63.6537"
+ x="17.4083"
+ y="15.194"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect136" />
+ <rect
+ width="325.86"
+ height="63.6537"
+ x="13.6713"
+ y="12.4966"
+ style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect126" />
+ <g
+ transform="matrix(1.14345,0,0,0.729078,-1.67818,105.325)"
+ style="font-size:12;"
+ id="g164">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="222.966"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect138" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="221.002"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect133" />
+ </g>
+ <text
+ x="170.824753"
+ y="58.402939"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text183">
+ <tspan
+ x="170.824997"
+ y="58.402901"
+ id="tspan360">
+Application</tspan>
+ </text>
+ <text
+ x="178.076340"
+ y="364.281433"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text197">
+ <tspan
+ x="178.076004"
+ y="364.281006"
+ id="tspan421">
+Database</tspan>
+ </text>
+ <text
+ x="68.605331"
+ y="138.524582"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text216">
+ <tspan
+ x="68.605301"
+ y="138.524994"
+ id="tspan384">
+SessionFactory</tspan>
+ </text>
+ <rect
+ width="67.0014"
+ height="101.35"
+ x="196.927"
+ y="89.2389"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect387" />
+ <rect
+ width="67.0014"
+ height="101.35"
+ x="194.633"
+ y="86.4389"
+ style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect388" />
+ <text
+ x="249.108841"
+ y="173.885559"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text389">
+ <tspan
+ x="249.108994"
+ y="173.886002"
+ id="tspan392">
+Session</tspan>
+ </text>
+ <rect
+ width="73.0355"
+ height="101.35"
+ x="270.995"
+ y="90.0018"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect395" />
+ <rect
+ width="73.0355"
+ height="101.35"
+ x="267.869"
+ y="87.2018"
+ style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect396" />
+ <text
+ x="328.593658"
+ y="174.715622"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text397">
+ <tspan
+ x="328.593994"
+ y="174.716003"
+ id="tspan563">
+Transaction</tspan>
+ </text>
+ <g
+ transform="matrix(0.29544,0,0,0.397877,9.70533,103.96)"
+ style="font-size:12;"
+ id="g565">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect566" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect567" />
+ </g>
+ <text
+ x="25.592752"
+ y="204.497803"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:10;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text568">
+ <tspan
+ x="25.592800"
+ y="204.498001"
+ id="tspan662">
+TransactionFactory</tspan>
+ </text>
+ <g
+ transform="matrix(0.298082,0,0,0.397877,99.6898,103.96)"
+ style="font-size:12;"
+ id="g573">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect574" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect575" />
+ </g>
+ <text
+ x="134.030670"
+ y="205.532791"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:10;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text576">
+ <tspan
+ x="134.031006"
+ y="205.533005"
+ id="tspan664">
+ConnectionProvider</tspan>
+ </text>
+ <g
+ transform="matrix(1.14345,0,0,0.729078,-1.67818,38.9539)"
+ style="font-size:12;"
+ id="g587">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="222.966"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect588" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="221.002"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect589" />
+ </g>
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="25.6196"
+ y="206.028"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect594" />
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="24.4229"
+ y="204.135"
+ style="font-size:12;fill:#b3b3b3;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect595" />
+ <text
+ x="85.575645"
+ y="282.300354"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text596">
+ <tspan
+ x="85.575600"
+ y="282.299988"
+ id="tspan607">
+JNDI</tspan>
+ </text>
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="236.937"
+ y="206.791"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect610" />
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="235.741"
+ y="204.898"
+ style="font-size:12;fill:#b3b3b3;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect611" />
+ <text
+ x="342.093201"
+ y="283.226410"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text612">
+ <tspan
+ x="342.092987"
+ y="283.226013"
+ id="tspan621">
+JTA</tspan>
+ </text>
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="130.134"
+ y="206.791"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect616" />
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="128.937"
+ y="204.898"
+ style="font-size:12;fill:#b3b3b3;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect617" />
+ <text
+ x="212.445343"
+ y="283.226410"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text618">
+ <tspan
+ x="212.445007"
+ y="283.226013"
+ id="tspan623">
+JDBC</tspan>
+ </text>
+ <g
+ transform="matrix(0.823795,0,0,0.823795,0.120302,6.19341)"
+ style="font-size:12;"
+ id="g637">
+ <g
+ transform="matrix(0.499515,0,0,0.415467,-0.237339,5.61339)"
+ id="g167">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect134" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect135" />
+ </g>
+ <text
+ x="33.749969"
+ y="50.589706"
+ style="font-size:11;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text188">
+ <tspan
+ x="33.750000"
+ y="50.589699"
+ id="tspan635">
+Transient Objects</tspan>
+ </text>
+ </g>
+ <g
+ transform="matrix(0.823795,0,0,0.823795,0.120302,5.25349)"
+ style="font-size:12;"
+ id="g644">
+ <g
+ transform="matrix(0.297486,0,0,0.516482,230.251,36.9178)"
+ id="g364">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect365" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect366" />
+ </g>
+ <text
+ x="277.123230"
+ y="85.155571"
+ style="font-size:11;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text367">
+ <tspan
+ x="277.122986"
+ y="85.155602"
+ id="tspan631">
+Persistent</tspan>
+ <tspan
+ x="277.122986"
+ y="96.155602"
+ id="tspan633">
+Objects</tspan>
+ </text>
+ </g>
+</svg>
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/hibernate_logo_a.png
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/hibernate_logo_a.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/lite.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/lite.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/lite.svg
===================================================================
--- trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/lite.svg (rev 0)
+++ trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/lite.svg 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,334 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"
+[
+ <!ATTLIST svg
+ xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink">
+]>
+<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
+<svg
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="318.898"
+ height="248.031"
+ id="svg1">
+ <defs
+ id="defs3">
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop128" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="1"
+ id="stop129" />
+ </linearGradient>
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient130"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ <radialGradient
+ cx="0.5"
+ cy="0.5"
+ fx="0.5"
+ fy="0.5"
+ r="0.5"
+ id="radialGradient131"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ </defs>
+ <rect
+ width="291.837"
+ height="57.0074"
+ x="17.3169"
+ y="18.646"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect136" />
+ <rect
+ width="291.837"
+ height="57.0074"
+ x="13.9703"
+ y="16.2302"
+ style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect126" />
+ <g
+ transform="matrix(0.326107,0,0,0.765831,9.59261,8.98517)"
+ style="font-size:12;"
+ id="g161">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect137" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect132" />
+ </g>
+ <g
+ transform="matrix(1.02406,0,0,0.652953,0.223384,39.9254)"
+ style="font-size:12;"
+ id="g164">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="222.966"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect138" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="221.002"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect133" />
+ </g>
+ <g
+ transform="matrix(0.449834,0,0,0.338463,-3.15909,9.73319)"
+ style="font-size:12;"
+ id="g167">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect134" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect135" />
+ </g>
+ <text
+ x="302.277679"
+ y="65.943230"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text183">
+ <tspan
+ x="302.277954"
+ y="65.943184"
+ id="tspan360">
+Application</tspan>
+ </text>
+ <text
+ x="36.235924"
+ y="63.796055"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:14;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text188">
+ <tspan
+ x="36.235950"
+ y="63.796051"
+ id="tspan427">
+Transient Objects</tspan>
+ </text>
+ <text
+ x="180.416245"
+ y="290.543701"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text197">
+ <tspan
+ x="180.415939"
+ y="290.543549"
+ id="tspan421">
+Database</tspan>
+ </text>
+ <text
+ x="25.037701"
+ y="179.154755"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text216">
+ <tspan
+ x="25.037655"
+ y="179.154648"
+ id="tspan384">
+SessionFactory</tspan>
+ </text>
+ <g
+ transform="matrix(0.252763,0,0,0.765831,109.104,8.98517)"
+ style="font-size:12;"
+ id="g386">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect387" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect388" />
+ </g>
+ <g
+ transform="matrix(0.297394,0,0,0.572692,101.502,21.6359)"
+ style="font-size:12;"
+ id="g364">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect365" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect366" />
+ </g>
+ <text
+ x="202.746506"
+ y="102.992203"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:14;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text367">
+ <tspan
+ x="202.746948"
+ y="102.992249"
+ id="tspan423">
+Persistent</tspan>
+ <tspan
+ x="202.746948"
+ y="116.992355"
+ id="tspan425">
+Objects</tspan>
+ </text>
+ <text
+ x="174.458496"
+ y="180.080795"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text389">
+ <tspan
+ x="174.458618"
+ y="180.080338"
+ id="tspan392">
+Session</tspan>
+ </text>
+ <g
+ transform="matrix(0.127369,0,0,0.765831,188.675,8.98517)"
+ style="font-size:12;"
+ id="g394">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect395" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect396" />
+ </g>
+ <text
+ x="260.413269"
+ y="179.154739"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text397">
+ <tspan
+ x="260.412964"
+ y="179.154343"
+ id="tspan400">
+JDBC</tspan>
+ </text>
+ <g
+ transform="matrix(0.127369,0,0,0.765831,229.156,8.98517)"
+ style="font-size:12;"
+ id="g405">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect406" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect407" />
+ </g>
+ <text
+ x="320.606903"
+ y="179.154739"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text408">
+ <tspan
+ x="320.606964"
+ y="179.154343"
+ id="tspan417">
+JNDI</tspan>
+ </text>
+ <g
+ transform="matrix(0.127369,0,0,0.765831,269.281,8.98517)"
+ style="font-size:12;"
+ id="g411">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect412" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect413" />
+ </g>
+ <text
+ x="377.096313"
+ y="179.154739"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text414">
+ <tspan
+ x="377.096008"
+ y="179.154999"
+ id="tspan145">
+JTA</tspan>
+ </text>
+</svg>
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/overview.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/overview.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/overview.svg
===================================================================
--- trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/overview.svg (rev 0)
+++ trunk/Hibernate3/documentation/manual/ko-KR/src/main/resources/shared/images/overview.svg 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,250 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"
+[
+ <!ATTLIST svg
+ xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink">
+]>
+<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
+<svg
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="248.031"
+ height="248.031"
+ id="svg1">
+ <defs
+ id="defs3">
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop128" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="1"
+ id="stop129" />
+ </linearGradient>
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient130"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ <radialGradient
+ cx="0.5"
+ cy="0.5"
+ fx="0.5"
+ fy="0.5"
+ r="0.5"
+ id="radialGradient131"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ </defs>
+ <g
+ transform="matrix(0.771934,0,0,0.771934,4.36019,-3.02123)"
+ style="font-size:12;"
+ id="g158">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="17.3527"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect136" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="15.3883"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect126" />
+ </g>
+ <g
+ transform="matrix(0.771934,0,0,0.771934,4.36019,3.04452)"
+ style="font-size:12;"
+ id="g161">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect137" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect132" />
+ </g>
+ <g
+ transform="matrix(0.771934,0,0,0.771934,4.36019,8.0993)"
+ style="font-size:12;"
+ id="g164">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="222.966"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect138" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="221.002"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect133" />
+ </g>
+ <g
+ transform="matrix(0.771934,0,0,0.543505,2.59104,21.1103)"
+ style="font-size:12;"
+ id="g167">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect134" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect135" />
+ </g>
+ <text
+ x="105.392174"
+ y="56.568123"
+ transform="scale(0.771934,0.771934)"
+ style="font-size:24;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text183">
+ <tspan
+ x="105.392273"
+ y="56.568146"
+ id="tspan186">
+Application</tspan>
+ </text>
+ <text
+ x="81.820183"
+ y="103.149330"
+ transform="scale(0.771934,0.771934)"
+ style="font-size:20;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text188">
+ <tspan
+ x="81.820213"
+ y="103.149727"
+ id="tspan206">
+Persistent Objects</tspan>
+ </text>
+ <text
+ x="111.548180"
+ y="278.927887"
+ transform="scale(0.771934,0.771934)"
+ style="font-size:24;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text197">
+ <tspan
+ x="111.547874"
+ y="278.927551"
+ id="tspan200">
+Database</tspan>
+ </text>
+ <text
+ x="94.436180"
+ y="153.805740"
+ transform="scale(0.771934,0.771934)"
+ style="font-size:24;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text216">
+ <tspan
+ x="94.436180"
+ y="153.805740"
+ id="tspan221">
+HIBERNATE</tspan>
+ </text>
+ <g
+ transform="matrix(0.771934,0,0,0.771934,2.59083,1.02261)"
+ style="font-size:12;"
+ id="g254">
+ <g
+ transform="translate(4.58374,2.61928)"
+ id="g176">
+ <g
+ transform="matrix(0.571429,0,0,0.67347,-10.6174,117.093)"
+ id="g170">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect171" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect172" />
+ </g>
+ <g
+ transform="matrix(0.571429,0,0,0.67347,138.682,117.093)"
+ id="g173">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect174" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect175" />
+ </g>
+ </g>
+ <text
+ x="47.259438"
+ y="182.367538"
+ style="font-weight:bold;stroke-width:1pt;font-family:Courier;"
+ id="text191">
+ <tspan
+ x="47.259399"
+ y="182.367996"
+ id="tspan212">
+hibernate.</tspan>
+ <tspan
+ x="47.259399"
+ y="194.367996"
+ id="tspan214">
+properties</tspan>
+ </text>
+ <text
+ x="198.523010"
+ y="188.260941"
+ style="font-weight:normal;stroke-width:1pt;font-family:helvetica;"
+ id="text194">
+ <tspan
+ id="tspan195">
+XML Mapping</tspan>
+ </text>
+ </g>
+</svg>
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/no-idea-what-to-do-with-these-yet/fop/Bangwool.ttf
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/ko-KR/src/no-idea-what-to-do-with-these-yet/fop/Bangwool.ttf
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/no-idea-what-to-do-with-these-yet/fop/Bangwool.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ko-KR/src/no-idea-what-to-do-with-these-yet/fop/Bangwool.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ko-KR/src/no-idea-what-to-do-with-these-yet/fop/Bangwool.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><font-metrics type="TYPE0"><font-name>Bangwool</font-name><embed/><cap-height>750</cap-height><x-height>500</x-height><ascender>901</ascender><descender>-325</descender><bbox><left>-638</left><bottom>-341</bottom><right>2735</right><top>1068</top></bbox><flags>33</flags><stemv>0</stemv><italicangle>0</italicangle><ttc-name>Bangwool</ttc-name><subtype>TYPE0</subtype><multibyte-extras><cid-type>CIDFontType2</cid-type><default-width>0</default-width><bfranges><bf gi="2" ue="13" us="13"/><bf gi="3" ue="126" us="32"/><bf gi="98" ue="255" us="160"/><bf gi="194" ue="263" us="262"/><bf gi="196" ue="269" us="268"/><bf gi="198" ue="273" us="273"/><bf gi="199" ue="287" us="286"/><bf gi="201" ue="305" us="304"/><bf gi="203" ue="322" us="321"/><bf gi="205" ue="339" us="338"/><bf gi="207" ue="353" us="350"/><bf gi="211" ue="376" us="376"/><bf gi="212" ue="382" us="381"/><bf gi="214" ue="402" us="402"/><bf gi="215" ue="711" us="710"/><bf gi="217" ue="!
720" us="720"/><bf gi="218" ue="733" us="728"/><bf gi="224" ue="937" us="937"/><bf gi="225" ue="960" us="960"/><bf gi="226" ue="4370" us="4352"/><bf gi="245" ue="4469" us="4449"/><bf gi="266" ue="4516" us="4515"/><bf gi="268" ue="4546" us="4520"/><bf gi="295" ue="8212" us="8211"/><bf gi="297" ue="8218" us="8216"/><bf gi="300" ue="8222" us="8220"/><bf gi="303" ue="8226" us="8224"/><bf gi="306" ue="8230" us="8230"/><bf gi="307" ue="8240" us="8240"/><bf gi="308" ue="8251" us="8249"/><bf gi="311" ue="8361" us="8361"/><bf gi="312" ue="8364" us="8364"/><bf gi="313" ue="8451" us="8451"/><bf gi="314" ue="8457" us="8457"/><bf gi="315" ue="8467" us="8467"/><bf gi="316" ue="8482" us="8481"/><bf gi="318" ue="8706" us="8706"/><bf gi="319" ue="8710" us="8710"/><bf gi="320" ue="8719" us="8719"/><bf gi="321" ue="8722" us="8721"/><bf gi="323" ue="8725" us="8725"/><bf gi="324" ue="8730" us="8729"/><bf gi="326" ue="8734" us="8734"/><bf gi="327" ue="8747" us="8747"/><bf gi="328" ue="8757" us="!
8756"/><bf gi="330" ue="8776" us="8776"/><bf gi="331" ue="8786" us="87
86"/><bf gi="332" ue="8801" us="8800"/><bf gi="334" ue="8805" us="8804"/><bf gi="336" ue="9674" us="9674"/><bf gi="337" ue="9679" us="9679"/><bf gi="338" ue="12305" us="12296"/><bf gi="348" ue="12311" us="12308"/><bf gi="352" ue="12643" us="12593"/><bf gi="403" ue="13249" us="13184"/><bf gi="469" ue="55203" us="44032"/><bf gi="11641" ue="65374" us="65281"/><bf gi="0" ue="65535" us="65535"/></bfranges><cid-widths start-index="0"><wx w="583"/><wx w="0"/><wx w="333"/><wx w="583"/><wx w="333"/><wx w="333"/><wx w="583"/><wx w="500"/><wx w="666"/><wx w="666"/><wx w="250"/><wx w="333"/><wx w="333"/><wx w="500"/><wx w="500"/><wx w="250"/><wx w="500"/><wx w="250"/><wx w="500"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="333"/><wx w="333"/><wx w="416"/><wx w="500"/><wx w="416"/><wx w="500"/><wx w="666"/><wx w="666"/><wx w="583"/><wx w="666"/><wx w="666"/><wx w="583"/><wx w="583"/><wx w="666"/>!
<wx w="666"/><wx w="500"/><wx w="550"/><wx w="583"/><wx w="583"/><wx w="833"/><wx w="666"/><wx w="666"/><wx w="583"/><wx w="666"/><wx w="583"/><wx w="583"/><wx w="633"/><wx w="666"/><wx w="666"/><wx w="833"/><wx w="666"/><wx w="650"/><wx w="583"/><wx w="333"/><wx w="500"/><wx w="333"/><wx w="500"/><wx w="583"/><wx w="333"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="333"/><wx w="333"/><wx w="500"/><wx w="333"/><wx w="666"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="666"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="466"/><wx w="333"/><wx w="466"/><wx w="583"/><wx w="583"/><wx w="333"/><wx w="500"/><wx w="583"/><wx w="583"/><wx w="650"/><wx w="333"/><wx w="500"/><wx w="500"/><wx w="833"/><wx w="500"/><wx w="666"/><wx w="750"/><wx w="416"/><wx w="833"/><wx w="416"/><wx w="333"/><wx w="500"/><wx w="416"/><wx w="416"/><wx w="333"/>!
<wx w="500"/><wx w="583"/><wx w="250"/><wx w="500"/><wx w="416"/><wx w
="500"/><wx w="666"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="666"/><wx w="666"/><wx w="666"/><wx w="666"/><wx w="666"/><wx w="666"/><wx w="1000"/><wx w="666"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="750"/><wx w="666"/><wx w="666"/><wx w="666"/><wx w="666"/><wx w="666"/><wx w="666"/><wx w="500"/><wx w="666"/><wx w="666"/><wx w="666"/><wx w="666"/><wx w="666"/><wx w="650"/><wx w="583"/><wx w="583"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="833"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="333"/><wx w="333"/><wx w="333"/><wx w="333"/><wx w="583"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="666"/><wx w="500"/><wx w="666"/><wx w="500"/><wx w="583"/><wx w="666"/><w!
x w="500"/><wx w="500"/><wx w="333"/><wx w="750"/><wx w="500"/><wx w="1083"/><wx w="833"/><wx w="583"/><wx w="500"/><wx w="583"/><wx w="500"/><wx w="650"/><wx w="583"/><wx w="500"/><wx w="583"/><wx w="500"/><wx w="500"/><wx w="416"/><wx w="500"/><wx w="500"/><wx w="333"/><wx w="500"/><wx w="500"/><wx w="416"/><wx w="750"/><wx w="666"/><wx w="383"/><wx w="800"/><wx w="383"/><wx w="383"/><wx w="800"/><wx w="383"/><wx w="383"/><wx w="383"/><wx w="800"/><wx w="383"/><wx w="800"/><wx w="383"/><wx w="383"/><wx w="800"/><wx w="383"/><wx w="383"/><wx w="383"/><wx w="383"/><wx w="383"/><wx w="376"/><wx w="540"/><wx w="376"/><wx w="533"/><wx w="373"/><wx w="540"/><wx w="373"/><wx w="533"/><wx w="200"/><wx w="460"/><wx w="620"/><wx w="456"/><wx w="200"/><wx w="200"/><wx w="456"/><wx w="623"/><wx w="456"/><wx w="200"/><wx w="200"/><wx w="456"/><wx w="373"/><wx w="50"/><wx w="50"/><wx w="50"/><wx w="50"/><wx w="50"/><wx w="50"/><wx w="50"/><wx w="50"/><wx w="50"/><wx w="50"/><wx w="50"/!
><wx w="50"/><wx w="50"/><wx w="50"/><wx w="50"/><wx w="50"/><wx w="50
"/><wx w="50"/><wx w="50"/><wx w="50"/><wx w="50"/><wx w="50"/><wx w="50"/><wx w="50"/><wx w="50"/><wx w="50"/><wx w="50"/><wx w="50"/><wx w="50"/><wx w="500"/><wx w="1000"/><wx w="250"/><wx w="250"/><wx w="250"/><wx w="416"/><wx w="416"/><wx w="416"/><wx w="500"/><wx w="500"/><wx w="583"/><wx w="583"/><wx w="916"/><wx w="416"/><wx w="416"/><wx w="666"/><wx w="1000"/><wx w="750"/><wx w="916"/><wx w="916"/><wx w="500"/><wx w="1000"/><wx w="750"/><wx w="500"/><wx w="750"/><wx w="666"/><wx w="666"/><wx w="500"/><wx w="500"/><wx w="333"/><wx w="666"/><wx w="833"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="416"/><wx w="416"/><wx w="500"/><wx w="750"/><wx w="416"/><wx w="416"/><wx w="666"/><wx w="666"/><wx w="500"/><wx w="500"/><wx w="583"/><wx w="583"/><wx w="416"/><wx w="416"/><wx w="333"/><wx w="333"/><wx w="500"/><wx w="500"/><wx w="583"/><wx w="966"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="!
966"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="966"/><wx w="583"/><wx w="583"/><wx w="966"/><wx w="583"/><wx w="583"/><wx w="966"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="916"/><wx w="1000"/><wx w="833"/><wx w="583"/><wx w="583"/><wx w="833"/><wx w="1000"/><wx w="833"/><wx w="583"/><wx w="583"/><wx w="833"/><wx w="583"/><wx w="1166"/><wx w="1166"/><wx w="1166"/><wx w="1333"/><wx w="1166"/><wx w="1083"/><wx w="1416"/><wx w="1250"/><wx w="1250"/><wx w="1750"/><wx w="1083"/><wx w="1083"/><wx w="1083"/><wx w="1000"/><wx w="1166"/><wx w="1000"/><wx w="1166"/><wx w="1666"/><wx w="2000"/><wx w="1833"/><wx w="1833"/><wx w="1000"/><wx w="1166"/><wx w="1000"/><wx w="1000"/><wx w="1166"/><wx w="1166"/><wx w="1166"/><wx w="1333"/><wx w="1166"/><!
wx w="1166"/><wx w="1750"/><wx w="1583"/><wx w="1083"/><wx w="1583"/><
wx w="1750"/><wx w="1583"/><wx w="1083"/><wx w="1583"/><wx w="1583"/><wx w="2000"/><wx w="1083"/><wx w="1583"/><wx w="1916"/><wx w="1750"/><wx w="1500"/><wx w="2416"/><wx w="2833"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1166"/><wx w="1166"/><wx w="1166"/><wx w="1166"/><wx w="1333"/><wx w="1166"/><wx w="1500"/><wx w="1333"/><wx w="1333"/><wx w="1333"/><wx w="1500"/><wx w="1333"/><wx w="1666"/><wx w="1250"/><wx w="1583"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960!
"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793!
"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><w
x w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx!
w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx!
w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="1040"/><wx w="
1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="!
626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="104!
3"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="104
3"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"!
/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"!
/><wx w="793"/><wx w="1290"/><wx w="1293"/><wx w="1293"/><wx w="1293"/
><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1373"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1290"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1!
293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1373"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1206"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1373"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><w!
x w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><w
x w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1206"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1373"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376!
"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1373"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1456"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w!
="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w
="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1290"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1040"/><wx w="1043"/><wx w="1043"/>!
<wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1290"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1456"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1!
460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1
460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1290"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx!
w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1290"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1206"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210!
"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="873"
/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/!
><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/!
><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx
w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w!
="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx!
w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="8
76"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="62!
6"/><wx w="626"/><wx w="626"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="!
876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/
><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/>!
<wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/>!
<wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w
="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="790"/><wx w="793"/><wx w=!
"793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w=!
"793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"
/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/!
><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626!
"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><w
x w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="104!
3"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><!
wx w="626"/><wx w="626"/><wx w="626"/><wx w="623"/><wx w="626"/><wx w=
"626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="!
793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="1290"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1373"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1290"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="!
1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="
1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1373"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1206"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1373"/><w!
x w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1206"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1373"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="137!
6"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="137
6"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1373"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1456"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w!
="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1290"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/!
><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/
><wx w="1043"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1290"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1456"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1!
460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1290"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><w!
x w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><w
x w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1290"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1206"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210!
"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><w!
x w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="
876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="9!
60"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="6!
23"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/>
<wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx!
w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/!
><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx
w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx!
w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="873"/><wx w="876"/><wx w="876"/><wx!
w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="8
76"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="95!
6"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="96!
0"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><
wx w="960"/><wx w="960"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><w!
x w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><w!
x w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="
960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w=!
"626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w=!
"876"/><wx w="876"/><wx w="876"/><wx w="1040"/><wx w="1043"/><wx w="10
43"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626!
"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793!
"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><w
x w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx!
w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="956"/><wx w="960"/><wx!
w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="9
60"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="96!
0"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx !
w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx
w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/>!
<wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"!
/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx
w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="873"/><wx w="876"/><wx !
w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="1290"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="129!
3"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="129
3"/><wx w="1293"/><wx w="1293"/><wx w="1373"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1290"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1373"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w!
="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1206"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1373"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/!
><wx w="1376"/><wx w="1206"/><wx w="1210"/><wx w="1210"/><wx w="1210"/
><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1373"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1!
043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1373"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1456"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1290"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><w!
x w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><w
x w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043!
"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1290"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1456"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1290"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w!
="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w
="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1290"/><wx w="1293"/><wx w="1293"/>!
<wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1206"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/!
><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx
w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w!
="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w!
="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793
"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"!
/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="87!
6"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="623"/><
wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><w!
x w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/>!
<wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w
="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w=!
"793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="1290"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1373"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w=!
"1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1290"/><wx w=
"1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1373"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1206"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><!
wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1373"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1206"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1373"/><wx w="1376"/><wx w="13!
76"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="13
76"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1373"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx !
w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1456"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1290"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"!
/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"
/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1290"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="!
1456"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1290"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><!
wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><
wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1290"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1206"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="121!
0"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><!
wx w="960"/><wx w="960"/><wx w="960"/><wx w="873"/><wx w="876"/><wx w=
"876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="!
793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="!
960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/
><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><w!
x w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="623"/><wx w="626"!
/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx
w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w!
="1043"/><wx w="1043"/><wx w="1043"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><w!
x w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="
626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="8!
76"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="9!
60"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/>
<wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><!
wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><!
wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="956"/><wx w=
"960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w!
="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w!
="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876
"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="62!
6"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="87!
6"/><wx w="876"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><
wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="1290"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1373"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w=!
"1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1290"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1373"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1206"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/>!
<wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/>
<wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1373"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1206"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="12!
10"/><wx w="1210"/><wx w="1373"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1373"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx!
w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx
w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1376"/><wx w="1456"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1290"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"!
/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1290"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w=!
"1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w=
"1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1456"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1460"/><wx w="1290"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><!
wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1290"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="1293"/><wx w="12!
93"/><wx w="1293"/><wx w="1293"/><wx w="1206"/><wx w="1210"/><wx w="12
10"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="1210"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960!
"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793!
"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><w
x w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx!
w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="1040"/><wx w="1043"/><!
wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><
wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="!
626"/><wx w="626"/><wx w="626"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx !
w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx
w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"!
/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"!
/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx
w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="956"/><wx w="960"/><wx !
w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx !
w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="96
0"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626!
"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="8!
76"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/>
<wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><!
wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/!
><wx w="876"/><wx w="876"/><wx w="876"/><wx w="623"/><wx w="626"/><wx
w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w!
="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w!
="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960
"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"!
/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"!
/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx
w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="1040"/><wx!
w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><w!
x w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="
626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w=!
"1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w=!
"626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"
/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/!
><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/!
><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="956"/><wx
w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w!
="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w!
="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626
"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="87!
6"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="87!
3"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><
wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/>!
<wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/>!
<wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w
="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w=!
"960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w=!
"793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"
/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="956"/><wx w="960"/><wx w="960"/!
><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="956"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/!
><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx w="960"/><wx
w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx!
w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="1040"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="!
1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="
1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="1043"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="623"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="!
626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="873"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="876"/><wx w="790"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="793"/><wx w="!
793"/><wx w="793"/><wx w="793"/><wx w="333"/><wx w="333"/><wx w="583"/
><wx w="500"/><wx w="666"/><wx w="666"/><wx w="250"/><wx w="333"/><wx w="333"/><wx w="500"/><wx w="500"/><wx w="250"/><wx w="500"/><wx w="250"/><wx w="500"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="583"/><wx w="333"/><wx w="333"/><wx w="416"/><wx w="500"/><wx w="416"/><wx w="500"/><wx w="666"/><wx w="666"/><wx w="583"/><wx w="666"/><wx w="666"/><wx w="583"/><wx w="583"/><wx w="666"/><wx w="666"/><wx w="500"/><wx w="550"/><wx w="583"/><wx w="583"/><wx w="833"/><wx w="666"/><wx w="666"/><wx w="583"/><wx w="666"/><wx w="583"/><wx w="583"/><wx w="633"/><wx w="666"/><wx w="666"/><wx w="833"/><wx w="666"/><wx w="650"/><wx w="583"/><wx w="333"/><wx w="500"/><wx w="333"/><wx w="500"/><wx w="583"/><wx w="333"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="333"/><wx w="333"/><wx w="500"/><wx w="333"/><wx w="666"/><wx w="500"/><wx w="500"/>!
<wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="666"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="466"/><wx w="333"/><wx w="466"/><wx w="583"/></cid-widths></multibyte-extras></font-metrics>
\ No newline at end of file
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/no-idea-what-to-do-with-these-yet/fop/Hankc.ttf
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/ko-KR/src/no-idea-what-to-do-with-these-yet/fop/Hankc.ttf
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/no-idea-what-to-do-with-these-yet/fop/Hankc.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ko-KR/src/no-idea-what-to-do-with-these-yet/fop/Hankc.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ko-KR/src/no-idea-what-to-do-with-these-yet/fop/Hankc.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><font-metrics type="TYPE0"><font-name>Hankc</font-name><embed/><cap-height>740</cap-height><x-height>473</x-height><ascender>829</ascender><descender>263</descender><bbox><left>-45</left><bottom>-240</bottom><right>1192</right><top>904</top></bbox><flags>33</flags><stemv>0</stemv><italicangle>0</italicangle><ttc-name>Hankc</ttc-name><subtype>TYPE0</subtype><multibyte-extras><cid-type>CIDFontType2</cid-type><default-width>0</default-width><bfranges><bf gi="13009" ue="13" us="13"/><bf gi="3" ue="32" us="32"/><bf gi="9603" ue="33" us="33"/><bf gi="9602" ue="34" us="34"/><bf gi="4" ue="38" us="36"/><bf gi="13001" ue="39" us="39"/><bf gi="7" ue="41" us="40"/><bf gi="13002" ue="42" us="42"/><bf gi="9" ue="59" us="43"/><bf gi="12434" ue="60" us="60"/><bf gi="26" ue="61" us="61"/><bf gi="12435" ue="62" us="62"/><bf gi="27" ue="95" us="63"/><bf gi="13000" ue="96" us="96"/><bf gi="60" ue="127" us="97"/><bf gi="3" ue="160" us="160"/><bf gi="10472"!
ue="161" us="161"/><bf gi="12430" ue="162" us="162"/><bf gi="12429" ue="163" us="163"/><bf gi="10473" ue="164" us="164"/><bf gi="12428" ue="165" us="165"/><bf gi="10474" ue="168" us="167"/><bf gi="10476" ue="170" us="170"/><bf gi="10477" ue="180" us="176"/><bf gi="12424" ue="181" us="181"/><bf gi="10482" ue="186" us="182"/><bf gi="10461" ue="191" us="188"/><bf gi="12380" ue="197" us="192"/><bf gi="10487" ue="198" us="198"/><bf gi="12386" ue="199" us="199"/><bf gi="12373" ue="201" us="200"/><bf gi="12372" ue="202" us="202"/><bf gi="12375" ue="207" us="203"/><bf gi="10488" ue="208" us="208"/><bf gi="12387" ue="214" us="209"/><bf gi="10489" ue="216" us="215"/><bf gi="12393" ue="220" us="217"/><bf gi="12426" ue="221" us="221"/><bf gi="10491" ue="223" us="222"/><bf gi="12397" ue="229" us="224"/><bf gi="10493" ue="230" us="230"/><bf gi="12403" ue="239" us="231"/><bf gi="10494" ue="240" us="240"/><bf gi="12421" ue="241" us="241"/><bf gi="12412" ue="242" us="242"/><bf gi="12414" u!
e="243" us="243"/><bf gi="12413" ue="244" us="244"/><bf gi="12415" ue=
"246" us="245"/><bf gi="10495" ue="248" us="247"/><bf gi="12417" ue="252" us="249"/><bf gi="12422" ue="253" us="253"/><bf gi="10497" ue="254" us="254"/><bf gi="12423" ue="255" us="255"/><bf gi="10498" ue="273" us="273"/><bf gi="10499" ue="295" us="294"/><bf gi="10501" ue="307" us="305"/><bf gi="10504" ue="312" us="312"/><bf gi="10505" ue="323" us="319"/><bf gi="10510" ue="331" us="330"/><bf gi="10469" ue="338" us="338"/><bf gi="10471" ue="339" us="339"/><bf gi="12433" ue="352" us="352"/><bf gi="12431" ue="353" us="353"/><bf gi="10512" ue="359" us="358"/><bf gi="12425" ue="376" us="376"/><bf gi="12427" ue="382" us="382"/><bf gi="10514" ue="711" us="711"/><bf gi="10515" ue="720" us="720"/><bf gi="10516" ue="731" us="728"/><bf gi="12432" ue="732" us="732"/><bf gi="10520" ue="733" us="733"/><bf gi="10250" ue="929" us="913"/><bf gi="10267" ue="937" us="931"/><bf gi="10274" ue="961" us="945"/><bf gi="10291" ue="969" us="963"/><bf gi="10298" ue="1025" us="1025"/><bf gi="10299" ue="!
1103" us="1040"/><bf gi="10363" ue="1105" us="1105"/><bf gi="10364" ue="8213" us="8213"/><bf gi="13005" ue="8217" us="8216"/><bf gi="13007" ue="8221" us="8220"/><bf gi="10466" ue="8225" us="8224"/><bf gi="10365" ue="8229" us="8229"/><bf gi="10465" ue="8230" us="8230"/><bf gi="10468" ue="8240" us="8240"/><bf gi="10366" ue="8243" us="8242"/><bf gi="10368" ue="8251" us="8251"/><bf gi="10369" ue="8308" us="8308"/><bf gi="10370" ue="8319" us="8319"/><bf gi="10371" ue="8324" us="8321"/><bf gi="10375" ue="8361" us="8361"/><bf gi="10376" ue="8451" us="8451"/><bf gi="10377" ue="8457" us="8457"/><bf gi="10378" ue="8467" us="8467"/><bf gi="10379" ue="8470" us="8470"/><bf gi="10380" ue="8481" us="8481"/><bf gi="10470" ue="8482" us="8482"/><bf gi="10381" ue="8486" us="8486"/><bf gi="10382" ue="8491" us="8491"/><bf gi="10383" ue="8532" us="8531"/><bf gi="10385" ue="8542" us="8539"/><bf gi="10389" ue="8553" us="8544"/><bf gi="10399" ue="8569" us="8560"/><bf gi="10409" ue="8601" us="8592"/!
><bf gi="10419" ue="8658" us="8658"/><bf gi="10420" ue="8660" us="8660
"/><bf gi="10421" ue="8704" us="8704"/><bf gi="10422" ue="8707" us="8706"/><bf gi="10424" ue="8712" us="8711"/><bf gi="10426" ue="8715" us="8715"/><bf gi="10427" ue="8719" us="8719"/><bf gi="10428" ue="8721" us="8721"/><bf gi="10458" ue="8722" us="8722"/><bf gi="10429" ue="8730" us="8730"/><bf gi="10430" ue="8734" us="8733"/><bf gi="10432" ue="8736" us="8736"/><bf gi="10433" ue="8741" us="8741"/><bf gi="10434" ue="8748" us="8743"/><bf gi="10440" ue="8750" us="8750"/><bf gi="10441" ue="8757" us="8756"/><bf gi="10443" ue="8765" us="8764"/><bf gi="10445" ue="8786" us="8786"/><bf gi="10446" ue="8801" us="8800"/><bf gi="10448" ue="8805" us="8804"/><bf gi="10450" ue="8811" us="8810"/><bf gi="10452" ue="8835" us="8834"/><bf gi="10454" ue="8839" us="8838"/><bf gi="10456" ue="8857" us="8857"/><bf gi="10457" ue="8869" us="8869"/><bf gi="10615" ue="8978" us="8978"/><bf gi="10459" ue="8993" us="8992"/><bf gi="9689" ue="9326" us="9312"/><bf gi="9704" ue="9346" us="9332"/><bf gi="9719" ue!
="9397" us="9372"/><bf gi="9745" ue="9449" us="9424"/><bf gi="9771" ue="9475" us="9472"/><bf gi="9775" ue="9547" us="9484"/><bf gi="9605" ue="9618" us="9618"/><bf gi="9839" ue="9633" us="9632"/><bf gi="9841" ue="9641" us="9635"/><bf gi="9848" ue="9651" us="9650"/><bf gi="9850" ue="9655" us="9654"/><bf gi="9852" ue="9661" us="9660"/><bf gi="9854" ue="9665" us="9664"/><bf gi="9856" ue="9672" us="9670"/><bf gi="9859" ue="9675" us="9675"/><bf gi="9860" ue="9681" us="9678"/><bf gi="9864" ue="9734" us="9733"/><bf gi="9866" ue="9743" us="9742"/><bf gi="9868" ue="9756" us="9756"/><bf gi="9869" ue="9758" us="9758"/><bf gi="9870" ue="9792" us="9792"/><bf gi="9871" ue="9794" us="9794"/><bf gi="9872" ue="9825" us="9824"/><bf gi="9874" ue="9829" us="9827"/><bf gi="9877" ue="9834" us="9831"/><bf gi="9881" ue="9837" us="9836"/><bf gi="9883" ue="12291" us="12288"/><bf gi="9887" ue="12305" us="12296"/><bf gi="9897" ue="12309" us="12307"/><bf gi="9900" ue="12435" us="12353"/><bf gi="9983" ue!
="12534" us="12449"/><bf gi="9636" ue="12643" us="12593"/><bf gi="1006
9" ue="12686" us="12644"/><bf gi="10112" ue="12828" us="12800"/><bf gi="10141" ue="12923" us="12896"/><bf gi="10169" ue="12927" us="12927"/><bf gi="10170" ue="13188" us="13184"/><bf gi="10175" ue="13258" us="13192"/><bf gi="10242" ue="13264" us="13263"/><bf gi="10244" ue="13267" us="13267"/><bf gi="10245" ue="13270" us="13270"/><bf gi="10246" ue="13272" us="13272"/><bf gi="10247" ue="13277" us="13275"/><bf gi="10616" ue="44618" us="44032"/><bf gi="91" ue="44619" us="44619"/><bf gi="11203" ue="45201" us="44620"/><bf gi="92" ue="45207" us="45202"/><bf gi="11785" ue="45794" us="45208"/><bf gi="98" ue="55203" us="45795"/><bf gi="9507" ue="57344" us="57344"/><bf gi="9606" ue="57370" us="57345"/><bf gi="9509" ue="57402" us="57371"/><bf gi="9541" ue="57446" us="57441"/><bf gi="9687" ue="57448" us="57447"/><bf gi="9547" ue="57503" us="57449"/><bf gi="9604" ue="57622" us="57622"/><bf gi="12981" ue="60266" us="60266"/><bf gi="10535" ue="65281" us="65281"/><bf gi="10526" ue="65284" us=!
"65282"/><bf gi="10536" ue="65289" us="65285"/><bf gi="10529" ue="65291" us="65290"/><bf gi="10541" ue="65292" us="65292"/><bf gi="9508" ue="65293" us="65293"/><bf gi="10542" ue="65338" us="65294"/><bf gi="10531" ue="65339" us="65339"/><bf gi="10587" ue="65340" us="65340"/><bf gi="10532" ue="65343" us="65341"/><bf gi="10588" ue="65370" us="65344"/><bf gi="9632" ue="65374" us="65371"/><bf gi="10521" ue="65505" us="65504"/><bf gi="10524" ue="65506" us="65506"/><bf gi="10523" ue="65509" us="65509"/><bf gi="10525" ue="65510" us="65510"/><bf gi="0" ue="65535" us="65535"/></bfranges><cid-widths start-index="0"><wx w="250"/><wx w="0"/><wx w="731"/><wx w="250"/><wx w="500"/><wx w="823"/><wx w="676"/><wx w="296"/><wx w="296"/><wx w="665"/><wx w="238"/><wx w="497"/><wx w="238"/><wx w="300"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="281"/><wx w="281"/><wx w="791"/><wx w="502"/><wx w="696"/><!
wx w="623"/><wx w="605"/><wx w="601"/><wx w="658"/><wx w="603"/><wx w=
"547"/><wx w="658"/><wx w="713"/><wx w="329"/><wx w="385"/><wx w="604"/><wx w="548"/><wx w="767"/><wx w="659"/><wx w="659"/><wx w="546"/><wx w="659"/><wx w="658"/><wx w="491"/><wx w="548"/><wx w="656"/><wx w="618"/><wx w="878"/><wx w="659"/><wx w="611"/><wx w="547"/><wx w="296"/><wx w="258"/><wx w="296"/><wx w="950"/><wx w="475"/><wx w="439"/><wx w="494"/><wx w="438"/><wx w="496"/><wx w="438"/><wx w="274"/><wx w="439"/><wx w="494"/><wx w="275"/><wx w="264"/><wx w="493"/><wx w="274"/><wx w="713"/><wx w="493"/><wx w="438"/><wx w="493"/><wx w="496"/><wx w="329"/><wx w="384"/><wx w="273"/><wx w="493"/><wx w="437"/><wx w="603"/><wx w="441"/><wx w="439"/><wx w="382"/><wx w="475"/><wx w="475"/><wx w="475"/><wx w="618"/><wx w="950"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="400"/><wx w="400"/><wx w="400"/><wx w="400"/><wx w="400"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="250"/><wx w="250"/>
<wx w="400"/><wx w="400"/><wx w="400"/><wx w="400"/><wx w="250"/><wx w="250"/><wx w="400"/><wx w="1000"/><wx w="250"/><wx w="500"/><wx w="500"/><wx w="400"/><wx w="751"/><wx w="1000"/><wx w="400"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="800"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="400"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="401"/><wx w="485"/><wx w="901"/><wx w="631"/><wx w="1000"/><wx w="800"/><wx w="768"/><wx w="900"/><wx w="900"/><wx w="770"/><wx w="900"/><wx w="900"/><wx w="900"/><wx w="900"/><wx w="450"/><wx w="450"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="646"/><wx w="571"/><wx w="675"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="314"/><wx w="281"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="332"/><wx w="332"/><wx w="500"/!
><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="384"/><wx w="384"/><wx w="384"/><wx w="226"/><wx w="384"/><wx w="651"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="398"/><wx w="499"/><wx w="699"/><wx w="400"/><wx w="1000"/><wx w="300"/><wx w="350"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="400"/><wx w="400"/><wx w="400"/><wx w="400"/><wx w="450"/><wx w="450"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="912"/><wx w="898"/><wx w="1000"/><wx w="1002"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1152"/><wx w="1202"/><wx w="1010"/><wx w="1191"/><wx w="1152"/><wx w="1201"/><wx w="101!
0"/><wx w="1193"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="881"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="649"/><wx w="753"/><wx w="902"/><wx w="613"/><wx w="747"/><wx w="764"/><wx w="949"/><wx w="650"/><wx w="599"/><wx w="888"/><wx w="650"/><wx w="946"/><wx w="854"/><wx w="899"/><wx w="624"/><wx w="963"/><wx w="753"/><wx w="750"/><wx w="703"/><wx w="703"/><wx w="599"/><wx w="798"/><wx w="750"/><wx w="750"/><wx w="501"/><wx w="504"/><wx w="500"/><wx !
w="501"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="50
0"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="501"/><wx w="500"/><wx w="669"/><wx w="749"/><wx w="619"/><wx w="624"/><wx w="651"/><wx w="658"/><wx w="659"/><wx w="870"/><wx w="548"/><wx w="668"/><wx w="668"/><wx w="691"/><wx w="628"/><wx w="779"/><wx w="669"/><wx w="605"/><wx w="664"/><wx w="600"/><wx w="588"/><wx w="641"/><wx w="560"/><wx w="629"/><wx w="676"/><wx w="683"/><wx w="591"/><wx w="860"/><wx w="882"/><wx w="809"/><wx w="857"/><wx w="601"/><wx w="581"/><wx w="754"/><wx w="704"/><wx w="450"/><wx w="450"/><wx w="450"/><wx w="450"/><wx w="500"/><wx w="450"/><wx w="586"/><wx w="450"/><wx w="500"/><wx w="500"/><wx w="467"/><wx w="450"/><wx w="581"/><wx w="500"/><wx w="450"/><wx w="500"/><wx w="452"/><wx w="450"/><wx w="477"/><wx w="450"/><wx w="550"/><wx w="502"/><wx w="500"/><wx w="450"/><wx w="623"/><wx w="631"/><wx w="593"/><wx w="611!
"/><wx w="430"/><wx w="450"/><wx w="548"/><wx w="512"/><wx w="450"/><wx w="1000"/><wx w="1000"/><wx w="251"/><wx w="399"/><wx w="1000"/><wx w="350"/><wx w="399"/><wx w="350"/><wx w="350"/><wx w="350"/><wx w="350"/><wx w="1000"/><wx w="751"/><wx w="852"/><wx w="301"/><wx w="1000"/><wx w="1000"/><wx w="649"/><wx w="658"/><wx w="699"/><wx w="699"/><wx w="699"/><wx w="699"/><wx w="699"/><wx w="699"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="746"/><wx w="400"/><wx w="668"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="452"/><wx w="719"/><wx w="551"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="600"/><wx w="600"/><wx w="699"/><wx w="701"/><wx w="699"/><wx w="450"/><wx w="950"/><wx w="950"/><wx w="950"/><wx w="903"/><wx w="651"/><wx w="950"/><wx w="601"/><wx w="300"/><wx w="950"/><wx w="400"/><wx w="950"/><wx w="490"/><wx w="352"/><wx w="950"/><wx w="400"/><wx w="400"/><wx w="301"/><wx w="950"/><wx w="950"/><wx w="950"/><wx w="400"/><wx w="500"/><wx w="950"/><wx w="621"/><wx w="950"/><wx w="552"/><wx w="411"/><wx w="499"/><wx w="950"/><wx w="400"/><wx w="950"/><wx w="415"/><wx w="400"/><wx w="528"/><wx w="700"/><wx w="559"/><wx w="328"/><wx w="682"/><wx w="463"/><wx w="526"/><wx w="536"/><wx w="448"/><wx w="528"/><wx w="484"/><wx w="440"/><wx w="448"!
/><wx w="422"/><wx w="591"/><wx w="359"/><wx w="1000"/><wx w="482"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="484"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="573"/><wx w="573"/><wx w="573"/><wx w="573"/><wx w="313"/><wx w="313"/><wx w="313"/><wx w="313"/><wx w="592"/><wx w="592"/><wx w="592"/><wx w="592"/><wx w="592"/><wx w="592"/><wx w="571"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="626"/><wx w="623"/><wx w="623"/><wx w="623"/><wx w="623"/><wx w="417"/><wx w="417"/><wx w="417"/><wx w="417"/><wx w="417"/><wx w="417"/><wx w="416"/><wx w="416"/><wx w="416"/><wx w="416"/><wx w="416"/><wx w="261"/><wx w="261"/><wx w="261"/><wx w="261"/><wx w="416"/><wx w="416"/><wx w="416"/><wx!
w="416"/><wx w="416"/><wx w="468"/><wx w="468"/><wx w="468"/><wx w="468"/><wx w="468"/><wx w="417"/><wx w="417"/><wx w="588"/><wx w="580"/><wx w="580"/><wx w="363"/><wx w="503"/><wx w="475"/><wx w="475"/><wx w="365"/><wx w="475"/><wx w="466"/><wx w="475"/><wx w="475"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="995"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="998"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="250"/><wx w="250"!
/><wx w="631"/><wx w="401"/><wx w="401"/><wx w="250"/><wx w="250"/><wx w="400"/><wx w="400"/><wx w="250"/></cid-widths></multibyte-extras></font-metrics>
\ No newline at end of file
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/no-idea-what-to-do-with-these-yet/fop/userconfig.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/ko-KR/src/no-idea-what-to-do-with-these-yet/fop/userconfig.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/ko-KR/src/no-idea-what-to-do-with-these-yet/fop/userconfig.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,113 @@
+<!--<!DOCTYPE configuration SYSTEM "config.dtd">-->
+<!--
+ this file contains templates which allow an user easy
+ configuration of Fop. Actually normally you don't need this configuration
+ file, but if you need to change configuration, you should
+ always use this file and *not* config.xml.
+ Usage: java org.apache.fop.apps.Fop -c userconfig.xml -fo fo-file -pdf pdf-file
+-->
+
+
+<configuration>
+
+<!--
+baseDir: normally the base directory is the directory where the fo file is
+ located. if you want to specify your own, uncomment this entry.
+ This value can also be a URL. Actually, the value is converted to
+ a URL.
+-->
+<!--
+<entry>
+ <key>baseDir</key>
+ <value></value>
+ </entry>
+ -->
+<!--
+fontBaseDir: Similar to baseDir, except that this value is used for fonts. If
+ it isn't specified, the value from baseDir is used.
+-->
+<!--
+ <entry>
+ <key>fontBaseDir</key>
+ <value></value>
+ </entry>
+-->
+
+<!--
+************************************************************************
+ HYPHENATION
+************************************************************************
+-->
+
+<!--
+ hyphenation directory
+ if you want to specify your own directory with hyphenation pattern
+ then uncomment the next entry and add the directory name
+-->
+
+<!--
+ <entry>
+ <key>hyphenation-dir</key>
+ <value>/java/xml-fop/hyph</value>
+ </entry>
+-->
+
+<!--
+************************************************************************
+ Add fonts here
+************************************************************************
+-->
+
+<fonts>
+ <!-- example -->
+ <!--
+ <font metrics-file="arial.xml" kerning="yes" embed-file="arial.ttf">
+ <font-triplet name="Arial" style="normal" weight="normal"/>
+ <font-triplet name="ArialMT" style="normal" weight="normal"/>
+ </font>
+ <font metrics-file="arialb.xml" kerning="yes" embed-file="arialb.ttf">
+ <font-triplet name="Arial" style="normal" weight="bold"/>
+ <font-triplet name="ArialMT" style="normal" weight="bold"/>
+ </font>
+ <font metrics-file="ariali.xml" kerning="yes" embed-file="ariali.ttf">
+ <font-triplet name="Arial" style="italic" weight="normal"/>
+ <font-triplet name="ArialMT" style="italic" weight="normal"/>
+ </font>
+ <font metrics-file="arialbi.xml" kerning="yes" embed-file="arialbi.ttf">
+ <font-triplet name="Arial" style="italic" weight="bold"/>
+ <font-triplet name="ArialMT" style="italic" weight="bold"/>
+ </font>
+ -->
+ <!-- Example Japanese fonts
+ <font metrics-file="msgothic.xml" embed-file="D:\winnt\font\msgothic.ttc" kerning="yes">
+ <font-triplet name="Gothic" style="normal" weight="normal"/>
+ <font-triplet name="Gothic" style="normal" weight="bold"/>
+ <font-triplet name="Gothic" style="italic" weight="normal"/>
+ <font-triplet name="Gothic" style="italic" weight="bold"/>
+ </font>
+ <font metrics-file="msmincho.xml" embed-file="Cyberbit.ttf" kerning="yes">
+ <font-triplet name="Mincho" style="normal" weight="normal"/>
+ <font-triplet name="Mincho" style="normal" weight="bold"/>
+ <font-triplet name="Mincho" style="italic" weight="normal"/>
+ <font-triplet name="Mincho" style="italic" weight="bold"/>
+ </font>
+ -->
+ <font metrics-file="Hankc.xml" kerning="yes" embed-file="Hankc.ttf">
+ <font-triplet name="Hankc" style="normal" weight="normal"/>
+ <font-triplet name="Hankc" style="normal" weight="bold"/>
+ <font-triplet name="Hankc" style="italic" weight="normal"/>
+ <font-triplet name="Hankc" style="italic" weight="bold"/>
+</font>
+<font metrics-file="Bangwool.xml" kerning="yes" embed-file="Bangwool.ttf">
+<font-triplet name="Bangwool" style="normal" weight="normal"/>
+<font-triplet name="Bangwool" style="normal" weight="bold"/>
+<font-triplet name="Bangwool" style="italic" weight="normal"/>
+<font-triplet name="Bangwool" style="italic" weight="bold"/>
+</font>
+</fonts>
+
+
+</configuration>
+
+
+
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/no-idea-what-to-do-with-these-yet/styles/fopdf.xsl
===================================================================
--- trunk/Hibernate3/documentation/manual/ko-KR/src/no-idea-what-to-do-with-these-yet/styles/fopdf.xsl (rev 0)
+++ trunk/Hibernate3/documentation/manual/ko-KR/src/no-idea-what-to-do-with-these-yet/styles/fopdf.xsl 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,525 @@
+<?xml version="1.0"?>
+
+<!--
+
+ This is the XSL FO configuration file for the Hibernate
+ Reference Documentation. It defines a custom titlepage and
+ the parameters for the A4 sized PDF printable output.
+
+ It took me days to figure out this stuff and fix most of
+ the obvious bugs in the DocBook XSL distribution. Some of
+ the workarounds might not be appropriate with a newer version
+ of DocBook XSL. This file is released as part of Hibernate,
+ hence LGPL licensed.
+
+ christian at hibernate.org
+
+-->
+
+<!DOCTYPE xsl:stylesheet [
+ <!ENTITY db_xsl_path "../../support/docbook-xsl/">
+]>
+
+<xsl:stylesheet
+ version="1.0"
+ xmlns="http://www.w3.org/TR/xhtml1/transitional"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:fo="http://www.w3.org/1999/XSL/Format"
+ exclude-result-prefixes="#default">
+
+ <xsl:import href="&db_xsl_path;/fo/docbook.xsl"/>
+
+ <!--###################################################
+ Custom Title Page
+ ################################################### -->
+
+ <xsl:template name="book.titlepage.recto">
+ <fo:block>
+ <fo:table table-layout="fixed" width="175mm">
+ <fo:table-column column-width="175mm"/>
+ <fo:table-body>
+ <fo:table-row>
+ <fo:table-cell text-align="center">
+ <fo:block>
+ <fo:external-graphic src="file:images/hibernate_logo_a.png"/>
+ </fo:block>
+ <fo:block font-family="Bangwool" font-size="18pt" padding-before="10mm">
+ <xsl:value-of select="bookinfo/subtitle"/>
+ </fo:block>
+ <fo:block font-family="Bangwool" font-size="12pt" padding="10mm">
+ ë²ì :
+ <xsl:value-of select="bookinfo/releaseinfo"/>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ </fo:table-body>
+ </fo:table>
+ </fo:block>
+ </xsl:template>
+
+ <!-- Prevent blank pages in output -->
+ <xsl:template name="book.titlepage.before.verso">
+ </xsl:template>
+ <xsl:template name="book.titlepage.verso">
+ </xsl:template>
+ <xsl:template name="book.titlepage.separator">
+ </xsl:template>
+
+ <!--###################################################
+ Header
+ ################################################### -->
+
+ <!-- More space in the center header for long text -->
+ <xsl:attribute-set name="header.content.properties">
+ <xsl:attribute name="font-family">
+ <xsl:value-of select="$body.font.family"/>
+ </xsl:attribute>
+ <xsl:attribute name="margin-left">-5em</xsl:attribute>
+ <xsl:attribute name="margin-right">-5em</xsl:attribute>
+ </xsl:attribute-set>
+
+ <!--###################################################
+ Custom Footer
+ ################################################### -->
+
+ <!-- This footer prints the Hibernate version number on the left side -->
+ <xsl:template name="footer.content">
+ <xsl:param name="pageclass" select="''"/>
+ <xsl:param name="sequence" select="''"/>
+ <xsl:param name="position" select="''"/>
+ <xsl:param name="gentext-key" select="''"/>
+
+ <xsl:variable name="Version">
+ <xsl:choose>
+ <xsl:when test="//releaseinfo">
+ <xsl:text>íì´ë²ë¤ì´í¸</xsl:text>
+ <xsl:value-of select="//releaseinfo"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- nop -->
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:choose>
+ <xsl:when test="$sequence='blank'">
+ <xsl:choose>
+ <xsl:when test="$double.sided != 0 and $position = 'left'">
+ <xsl:value-of select="$Version"/>
+ </xsl:when>
+
+ <xsl:when test="$double.sided = 0 and $position = 'center'">
+ <!-- nop -->
+ </xsl:when>
+
+ <xsl:otherwise>
+ <fo:page-number/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+
+ <xsl:when test="$pageclass='titlepage'">
+ <!-- nop: other titlepage sequences have no footer -->
+ </xsl:when>
+
+ <xsl:when test="$double.sided != 0 and $sequence = 'even' and $position='left'">
+ <fo:page-number/>
+ </xsl:when>
+
+ <xsl:when test="$double.sided != 0 and $sequence = 'odd' and $position='right'">
+ <fo:page-number/>
+ </xsl:when>
+
+ <xsl:when test="$double.sided = 0 and $position='right'">
+ <fo:page-number/>
+ </xsl:when>
+
+ <xsl:when test="$double.sided != 0 and $sequence = 'odd' and $position='left'">
+ <xsl:value-of select="$Version"/>
+ </xsl:when>
+
+ <xsl:when test="$double.sided != 0 and $sequence = 'even' and $position='right'">
+ <xsl:value-of select="$Version"/>
+ </xsl:when>
+
+ <xsl:when test="$double.sided = 0 and $position='left'">
+ <xsl:value-of select="$Version"/>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <!-- nop -->
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--###################################################
+ Custom Toc Line
+ ################################################### -->
+
+ <!-- Improve the TOC. -->
+ <xsl:template name="toc.line">
+ <xsl:variable name="id">
+ <xsl:call-template name="object.id"/>
+ </xsl:variable>
+
+ <xsl:variable name="label">
+ <xsl:apply-templates select="." mode="label.markup"/>
+ </xsl:variable>
+
+ <fo:block text-align-last="justify"
+ end-indent="{$toc.indent.width}pt"
+ last-line-end-indent="-{$toc.indent.width}pt">
+ <fo:inline keep-with-next.within-line="always">
+ <fo:basic-link internal-destination="{$id}">
+
+ <!-- Chapter titles should be bold. -->
+ <xsl:choose>
+ <xsl:when test="local-name(.) = 'chapter'">
+ <xsl:attribute name="font-weight">bold</xsl:attribute>
+ </xsl:when>
+ </xsl:choose>
+
+ <xsl:if test="$label != ''">
+ <xsl:copy-of select="$label"/>
+ <xsl:value-of select="$autotoc.label.separator"/>
+ </xsl:if>
+ <xsl:apply-templates select="." mode="titleabbrev.markup"/>
+ </fo:basic-link>
+ </fo:inline>
+ <fo:inline keep-together.within-line="always">
+ <xsl:text> </xsl:text>
+ <fo:leader leader-pattern="dots"
+ leader-pattern-width="3pt"
+ leader-alignment="reference-area"
+ keep-with-next.within-line="always"/>
+ <xsl:text> </xsl:text>
+ <fo:basic-link internal-destination="{$id}">
+ <fo:page-number-citation ref-id="{$id}"/>
+ </fo:basic-link>
+ </fo:inline>
+ </fo:block>
+ </xsl:template>
+
+ <!--###################################################
+ Extensions
+ ################################################### -->
+
+ <!-- These extensions are required for table printing and other stuff -->
+ <xsl:param name="use.extensions">1</xsl:param>
+ <xsl:param name="tablecolumns.extension">0</xsl:param>
+ <!-- FOP provide only PDF Bookmarks at the moment -->
+ <xsl:param name="fop.extensions">1</xsl:param>
+
+ <!--###################################################
+ Table Of Contents
+ ################################################### -->
+
+ <!-- Generate the TOCs for named components only -->
+ <xsl:param name="generate.toc">
+ book toc
+ </xsl:param>
+
+ <!-- Show only Sections up to level 3 in the TOCs -->
+ <xsl:param name="toc.section.depth">3</xsl:param>
+
+ <!-- Dot and Whitespace as separator in TOC between Label and Title-->
+ <xsl:param name="autotoc.label.separator" select="'. '"/>
+
+
+ <!--###################################################
+ Paper & Page Size
+ ################################################### -->
+
+ <!-- Paper type, no headers on blank pages, no double sided printing -->
+ <xsl:param name="paper.type" select="'A4'"/>
+ <xsl:param name="double.sided">0</xsl:param>
+ <xsl:param name="headers.on.blank.pages">0</xsl:param>
+ <xsl:param name="footers.on.blank.pages">0</xsl:param>
+
+ <!-- Space between paper border and content (chaotic stuff, don't touch) -->
+ <xsl:param name="page.margin.top">5mm</xsl:param>
+ <xsl:param name="region.before.extent">10mm</xsl:param>
+ <xsl:param name="body.margin.top">10mm</xsl:param>
+
+ <xsl:param name="body.margin.bottom">15mm</xsl:param>
+ <xsl:param name="region.after.extent">10mm</xsl:param>
+ <xsl:param name="page.margin.bottom">0mm</xsl:param>
+
+ <xsl:param name="page.margin.outer">18mm</xsl:param>
+ <xsl:param name="page.margin.inner">18mm</xsl:param>
+
+ <!-- No intendation of Titles -->
+ <xsl:param name="title.margin.left">0pc</xsl:param>
+
+ <!--###################################################
+ Fonts & Styles
+ ################################################### -->
+
+ <!-- Default Font size -->
+ <xsl:param name="body.font.master">10</xsl:param>
+
+ <!-- Line height in body text -->
+ <xsl:param name="line-height">1.6</xsl:param>
+
+ <!-- Monospaced fonts are smaller than regular text -->
+ <xsl:attribute-set name="monospace.properties">
+ <xsl:attribute name="font-family">
+ <xsl:value-of select="$monospace.font.family"/>
+ </xsl:attribute>
+ <xsl:attribute name="font-size">0.8em</xsl:attribute>
+ </xsl:attribute-set>
+
+ <!--###################################################
+ Tables
+ ################################################### -->
+
+ <!-- The table width should be adapted to the paper size -->
+ <xsl:param name="default.table.width">17.4cm</xsl:param>
+
+ <!-- Some padding inside tables -->
+ <xsl:attribute-set name="table.cell.padding">
+ <xsl:attribute name="padding-left">4pt</xsl:attribute>
+ <xsl:attribute name="padding-right">4pt</xsl:attribute>
+ <xsl:attribute name="padding-top">4pt</xsl:attribute>
+ <xsl:attribute name="padding-bottom">4pt</xsl:attribute>
+ </xsl:attribute-set>
+
+ <!-- Only hairlines as frame and cell borders in tables -->
+ <xsl:param name="table.frame.border.thickness">0.1pt</xsl:param>
+ <xsl:param name="table.cell.border.thickness">0.1pt</xsl:param>
+
+ <!--###################################################
+ Labels
+ ################################################### -->
+
+ <!-- Label Chapters and Sections (numbering) -->
+ <xsl:param name="chapter.autolabel">1</xsl:param>
+ <xsl:param name="section.autolabel" select="1"/>
+ <xsl:param name="section.label.includes.component.label" select="1"/>
+
+ <!-- Label only Sections up to level 2 -->
+ <xsl:param name="local.l10n.xml" select="document('')"/>
+ <l:i18n xmlns:l="http://docbook.sourceforge.net/xmlns/l10n/1.0">
+ <l:l10n language="ko">
+ <l:context name="title-numbered">
+ <l:template name="sect3" text="%t"/>
+ <l:template name="sect4" text="%t"/>
+ <l:template name="sect5" text="%t"/>
+ </l:context>
+ <l:context name="section-xref-numbered">
+ <l:template name="sect3" text="the section called %t"/>
+ <l:template name="sect4" text="the section called %t"/>
+ <l:template name="sect5" text="the section called %t"/>
+ </l:context>
+ </l:l10n>
+ </l:i18n>
+
+ <!--###################################################
+ Titles
+ ################################################### -->
+
+ <!-- Chapter title size -->
+ <xsl:attribute-set name="chapter.titlepage.recto.style">
+ <xsl:attribute name="text-align">left</xsl:attribute>
+ <xsl:attribute name="font-weight">bold</xsl:attribute>
+ <xsl:attribute name="font-size">
+ <xsl:value-of select="$body.font.master * 1.8"/>
+ <xsl:text>pt</xsl:text>
+ </xsl:attribute>
+ </xsl:attribute-set>
+
+ <!-- Why is the font-size for chapters hardcoded in the XSL FO templates?
+ Let's remove it, so this sucker can use our attribute-set only... -->
+ <xsl:template match="title" mode="chapter.titlepage.recto.auto.mode">
+ <fo:block xmlns:fo="http://www.w3.org/1999/XSL/Format"
+ xsl:use-attribute-sets="chapter.titlepage.recto.style">
+ <xsl:call-template name="component.title">
+ <xsl:with-param name="node" select="ancestor-or-self::chapter[1]"/>
+ </xsl:call-template>
+ </fo:block>
+ </xsl:template>
+
+ <!-- Sections 1, 2 and 3 titles have a small bump factor and padding -->
+ <xsl:attribute-set name="section.title.level1.properties">
+ <xsl:attribute name="space-before.optimum">0.8em</xsl:attribute>
+ <xsl:attribute name="space-before.minimum">0.8em</xsl:attribute>
+ <xsl:attribute name="space-before.maximum">0.8em</xsl:attribute>
+ <xsl:attribute name="font-size">
+ <xsl:value-of select="$body.font.master * 1.5"/>
+ <xsl:text>pt</xsl:text>
+ </xsl:attribute>
+ <xsl:attribute name="space-after.optimum">0.1em</xsl:attribute>
+ <xsl:attribute name="space-after.minimum">0.1em</xsl:attribute>
+ <xsl:attribute name="space-after.maximum">0.1em</xsl:attribute>
+ </xsl:attribute-set>
+ <xsl:attribute-set name="section.title.level2.properties">
+ <xsl:attribute name="space-before.optimum">0.6em</xsl:attribute>
+ <xsl:attribute name="space-before.minimum">0.6em</xsl:attribute>
+ <xsl:attribute name="space-before.maximum">0.6em</xsl:attribute>
+ <xsl:attribute name="font-size">
+ <xsl:value-of select="$body.font.master * 1.25"/>
+ <xsl:text>pt</xsl:text>
+ </xsl:attribute>
+ <xsl:attribute name="space-after.optimum">0.1em</xsl:attribute>
+ <xsl:attribute name="space-after.minimum">0.1em</xsl:attribute>
+ <xsl:attribute name="space-after.maximum">0.1em</xsl:attribute>
+ </xsl:attribute-set>
+ <xsl:attribute-set name="section.title.level3.properties">
+ <xsl:attribute name="space-before.optimum">0.4em</xsl:attribute>
+ <xsl:attribute name="space-before.minimum">0.4em</xsl:attribute>
+ <xsl:attribute name="space-before.maximum">0.4em</xsl:attribute>
+ <xsl:attribute name="font-size">
+ <xsl:value-of select="$body.font.master * 1.0"/>
+ <xsl:text>pt</xsl:text>
+ </xsl:attribute>
+ <xsl:attribute name="space-after.optimum">0.1em</xsl:attribute>
+ <xsl:attribute name="space-after.minimum">0.1em</xsl:attribute>
+ <xsl:attribute name="space-after.maximum">0.1em</xsl:attribute>
+ </xsl:attribute-set>
+
+ <!-- Titles of formal objects (tables, examples, ...) -->
+ <xsl:attribute-set name="formal.title.properties" use-attribute-sets="normal.para.spacing">
+ <xsl:attribute name="font-weight">bold</xsl:attribute>
+ <xsl:attribute name="font-size">
+ <xsl:value-of select="$body.font.master"/>
+ <xsl:text>pt</xsl:text>
+ </xsl:attribute>
+ <xsl:attribute name="hyphenate">false</xsl:attribute>
+ <xsl:attribute name="space-after.minimum">0.4em</xsl:attribute>
+ <xsl:attribute name="space-after.optimum">0.6em</xsl:attribute>
+ <xsl:attribute name="space-after.maximum">0.8em</xsl:attribute>
+ </xsl:attribute-set>
+
+ <!--###################################################
+ Programlistings
+ ################################################### -->
+
+ <!-- Verbatim text formatting (programlistings) -->
+ <xsl:attribute-set name="verbatim.properties">
+ <xsl:attribute name="space-before.minimum">1em</xsl:attribute>
+ <xsl:attribute name="space-before.optimum">1em</xsl:attribute>
+ <xsl:attribute name="space-before.maximum">1em</xsl:attribute>
+ <xsl:attribute name="space-after.minimum">0.1em</xsl:attribute>
+ <xsl:attribute name="space-after.optimum">0.1em</xsl:attribute>
+ <xsl:attribute name="space-after.maximum">0.1em</xsl:attribute>
+ <xsl:attribute name="border-color">#444444</xsl:attribute>
+ <xsl:attribute name="border-style">solid</xsl:attribute>
+ <xsl:attribute name="border-width">0.1pt</xsl:attribute>
+ <xsl:attribute name="padding-top">0.5em</xsl:attribute>
+ <xsl:attribute name="padding-left">0.5em</xsl:attribute>
+ <xsl:attribute name="padding-right">0.5em</xsl:attribute>
+ <xsl:attribute name="padding-bottom">0.5em</xsl:attribute>
+ <xsl:attribute name="margin-left">0.5em</xsl:attribute>
+ <xsl:attribute name="margin-right">0.5em</xsl:attribute>
+ </xsl:attribute-set>
+
+ <!-- Shade (background) programlistings -->
+ <xsl:param name="shade.verbatim">1</xsl:param>
+ <xsl:attribute-set name="shade.verbatim.style">
+ <xsl:attribute name="background-color">#F0F0F0</xsl:attribute>
+ </xsl:attribute-set>
+
+ <!--###################################################
+ Callouts
+ ################################################### -->
+
+ <!-- We want to use callouts... -->
+ <xsl:param name="callout.extensions">1</xsl:param>
+
+ <!-- Place callout bullets at this column in programmlisting.-->
+ <xsl:param name="callout.defaultcolumn">90</xsl:param>
+
+ <!--
+ No, don't use crappy graphics for the callout bullets. This setting
+ enables some weird Unicode rendering for some fancy bullet points
+ in callouts. By default, this can only count to 10 and produces
+ strange results if you ever have more than 10 callouts for one
+ programlisting. We will fix that next.
+ -->
+ <xsl:param name="callout.graphics">0</xsl:param>
+
+ <!--
+ Again, fun with DocBook XSL: The callout bullets are rendered in
+ two places: In the programlisting itself and in the list below
+ the listing, with the actual callout text. The rendering in the
+ programlisting is some XSL transformer extension (e.g. a Saxon
+ extension), so we can't change that without messing with the
+ extensions. We only can turn it off by setting this limit to
+ zero, then, a simple bracket style like "(3)" and "(4)" will
+ be used in the programlisting.
+ -->
+ <xsl:param name="callout.unicode.number.limit" select="'0'"></xsl:param>
+
+ <!--
+ The callout bullets in the actual callout list will be rendered
+ with an XSL FO template. The default template is broken: limited to 10
+ nice looking Unicode bullet points and then it doesn't print anything,
+ the fallback doesn't work. We implement our own template, which is not
+ as complicated, more ugly, but works. As always, function is more
+ important than form.
+ -->
+ <xsl:template name="callout-bug">
+ <xsl:param name="conum" select='1'/>
+ <fo:inline
+ color="black"
+ padding-top="0.1em"
+ padding-bottom="0.1em"
+ padding-start="0.2em"
+ padding-end="0.2em"
+ baseline-shift="0.1em"
+ font-family="{$monospace.font.family}"
+ font-weight="bold"
+ font-size="75%">
+ <xsl:text>(</xsl:text>
+ <xsl:value-of select="$conum"/>
+ <xsl:text>)</xsl:text>
+ </fo:inline>
+
+ </xsl:template>
+
+ <!--###################################################
+ Misc
+ ################################################### -->
+
+ <!-- Correct placement of titles for figures and examples. -->
+ <xsl:param name="formal.title.placement">
+ figure after
+ example before
+ equation before
+ table before
+ procedure before
+ </xsl:param>
+
+ <!-- Format Variable Lists as Blocks (prevents horizontal overflow). -->
+ <xsl:param name="variablelist.as.blocks">1</xsl:param>
+
+ <!-- The horrible list spacing problems, this is much better. -->
+ <xsl:attribute-set name="list.block.spacing">
+ <xsl:attribute name="space-before.optimum">0.8em</xsl:attribute>
+ <xsl:attribute name="space-before.minimum">0.8em</xsl:attribute>
+ <xsl:attribute name="space-before.maximum">0.8em</xsl:attribute>
+ <xsl:attribute name="space-after.optimum">0.1em</xsl:attribute>
+ <xsl:attribute name="space-after.minimum">0.1em</xsl:attribute>
+ <xsl:attribute name="space-after.maximum">0.1em</xsl:attribute>
+ </xsl:attribute-set>
+
+ <!-- Newer DocBook XSL apparently thinks that some sections are by
+ default "draft" status, and this idiotic thing is by default
+ also set to "maybe", so it spits out a lot of errors with the
+ latest FOP as the XSL/FO styles have references to some draft
+ watermarks, which you actually don't want in the first place.
+ Turn this crap off. If you have to work with the "status"
+ attribute, don't.
+ -->
+ <xsl:param name="draft.mode" select="'no'"/>
+
+ <!-- Korean related Settings -->
+ <xsl:param name="hyphenate">false</xsl:param>
+ <xsl:param name="body.font.family">Hankc</xsl:param>
+ <!-- xsl:param name="monospace.font.family">Bangwool</xsl:param-->
+ <xsl:param name="title.font.family">Bangwool</xsl:param>
+ <xsl:param name="saxon.character.representation" select="native"/>
+ <xsl:param name="callout.unicode" select="1"/>
+ <xsl:param name="callout.unicode.start.character" select="10102"/>
+ <xsl:param name="l10n.gentext.default.language" select="ko"/>
+</xsl:stylesheet>
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/no-idea-what-to-do-with-these-yet/styles/html.xsl
===================================================================
--- trunk/Hibernate3/documentation/manual/ko-KR/src/no-idea-what-to-do-with-these-yet/styles/html.xsl (rev 0)
+++ trunk/Hibernate3/documentation/manual/ko-KR/src/no-idea-what-to-do-with-these-yet/styles/html.xsl 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,84 @@
+<?xml version="1.0"?>
+
+<!--
+
+ This is the XSL HTML configuration file for the Hibernate
+ Reference Documentation.
+
+ It took me days to figure out this stuff and fix most of
+ the obvious bugs in the DocBook XSL distribution. Some of
+ the workarounds might not be appropriate with a newer version
+ of DocBook XSL. This file is released as part of Hibernate,
+ hence LGPL licensed.
+
+ christian at hibernate.org
+-->
+
+<!DOCTYPE xsl:stylesheet [
+ <!ENTITY db_xsl_path "../../support/docbook-xsl/">
+]>
+
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ version="1.0"
+ xmlns="http://www.w3.org/TR/xhtml1/transitional"
+ exclude-result-prefixes="#default">
+
+<xsl:import href="&db_xsl_path;/html/docbook.xsl"/>
+
+<!--###################################################
+ HTML Settings
+ ################################################### -->
+
+ <xsl:param name="html.stylesheet">../shared/css/html.css</xsl:param>
+
+ <!-- These extensions are required for table printing and other stuff -->
+ <xsl:param name="use.extensions">1</xsl:param>
+ <xsl:param name="tablecolumns.extension">0</xsl:param>
+ <xsl:param name="callout.extensions">1</xsl:param>
+ <xsl:param name="graphicsize.extension">0</xsl:param>
+
+<!--###################################################
+ Table Of Contents
+ ################################################### -->
+
+ <!-- Generate the TOCs for named components only -->
+ <xsl:param name="generate.toc">
+ book toc
+ </xsl:param>
+
+ <!-- Show only Sections up to level 3 in the TOCs -->
+ <xsl:param name="toc.section.depth">3</xsl:param>
+
+<!--###################################################
+ Labels
+ ################################################### -->
+
+ <!-- Label Chapters and Sections (numbering) -->
+ <xsl:param name="chapter.autolabel">1</xsl:param>
+ <xsl:param name="section.autolabel" select="1"/>
+ <xsl:param name="section.label.includes.component.label" select="1"/>
+
+<!--###################################################
+ Callouts
+ ################################################### -->
+
+ <!-- Don't use graphics, use a simple number style -->
+ <xsl:param name="callout.graphics">0</xsl:param>
+
+ <!-- Place callout marks at this column in annotated areas -->
+ <xsl:param name="callout.defaultcolumn">90</xsl:param>
+
+<!--###################################################
+ Misc
+ ################################################### -->
+
+ <!-- Placement of titles -->
+ <xsl:param name="formal.title.placement">
+ figure after
+ example before
+ equation before
+ table before
+ procedure before
+ </xsl:param>
+
+</xsl:stylesheet>
Added: trunk/Hibernate3/documentation/manual/ko-KR/src/no-idea-what-to-do-with-these-yet/styles/html_chunk.xsl
===================================================================
--- trunk/Hibernate3/documentation/manual/ko-KR/src/no-idea-what-to-do-with-these-yet/styles/html_chunk.xsl (rev 0)
+++ trunk/Hibernate3/documentation/manual/ko-KR/src/no-idea-what-to-do-with-these-yet/styles/html_chunk.xsl 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,86 @@
+<?xml version="1.0"?>
+
+<!--
+
+ This is the XSL HTML configuration file for the Hibernate
+ Reference Documentation.
+
+ It took me days to figure out this stuff and fix most of
+ the obvious bugs in the DocBook XSL distribution. Some of
+ the workarounds might not be appropriate with a newer version
+ of DocBook XSL. This file is released as part of Hibernate,
+ hence LGPL licensed.
+
+ christian at hibernate.org
+-->
+
+<!DOCTYPE xsl:stylesheet [
+ <!ENTITY db_xsl_path "../../support/docbook-xsl/">
+]>
+
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ version="1.0"
+ xmlns="http://www.w3.org/TR/xhtml1/transitional"
+ exclude-result-prefixes="#default">
+
+<xsl:import href="&db_xsl_path;/html/chunk.xsl"/>
+
+<!--###################################################
+ HTML Settings
+ ################################################### -->
+
+ <xsl:param name="chunk.section.depth">'5'</xsl:param>
+ <xsl:param name="use.id.as.filename">'1'</xsl:param>
+ <xsl:param name="html.stylesheet">../shared/css/html.css</xsl:param>
+
+ <!-- These extensions are required for table printing and other stuff -->
+ <xsl:param name="use.extensions">1</xsl:param>
+ <xsl:param name="tablecolumns.extension">0</xsl:param>
+ <xsl:param name="callout.extensions">1</xsl:param>
+ <xsl:param name="graphicsize.extension">0</xsl:param>
+
+<!--###################################################
+ Table Of Contents
+ ################################################### -->
+
+ <!-- Generate the TOCs for named components only -->
+ <xsl:param name="generate.toc">
+ book toc
+ </xsl:param>
+
+ <!-- Show only Sections up to level 3 in the TOCs -->
+ <xsl:param name="toc.section.depth">3</xsl:param>
+
+<!--###################################################
+ Labels
+ ################################################### -->
+
+ <!-- Label Chapters and Sections (numbering) -->
+ <xsl:param name="chapter.autolabel">1</xsl:param>
+ <xsl:param name="section.autolabel" select="1"/>
+ <xsl:param name="section.label.includes.component.label" select="1"/>
+
+<!--###################################################
+ Callouts
+ ################################################### -->
+
+ <!-- Don't use graphics, use a simple number style -->
+ <xsl:param name="callout.graphics">0</xsl:param>
+
+ <!-- Place callout marks at this column in annotated areas -->
+ <xsl:param name="callout.defaultcolumn">90</xsl:param>
+
+<!--###################################################
+ Misc
+ ################################################### -->
+
+ <!-- Placement of titles -->
+ <xsl:param name="formal.title.placement">
+ figure after
+ example before
+ equation before
+ table before
+ procedure before
+ </xsl:param>
+
+</xsl:stylesheet>
Added: trunk/Hibernate3/documentation/manual/pom.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/pom.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/pom.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,100 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-core-project</artifactId>
+ <version>3.3.0.beta1</version>
+ <relativePath>../../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>hibernate-manual</artifactId>
+ <packaging>pom</packaging>
+ <name>Hibernate Manual</name>
+ <description>The Hibernate reference manual</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.jboss.maven.plugins</groupId>
+ <artifactId>maven-jboss-docbook-plugin</artifactId>
+ <version>1.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-docbook-xslt</artifactId>
+ <version>0.1</version>
+ </dependency>
+ </dependencies>
+
+ <modules>
+ <module>en-US</module>
+ <module>fr-FR</module>
+ </modules>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.jboss.maven.plugins</groupId>
+ <artifactId>maven-jboss-docbook-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>generate</goal>
+ </goals>
+ </execution>
+ </executions>
+ <dependencies>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-docbook-xslt</artifactId>
+ <version>0.1</version>
+ </dependency>
+ </dependencies>
+ <configuration>
+ <sourceDocumentName>master.xml</sourceDocumentName>
+ <formats>
+ <format>
+ <formatName>pdf</formatName>
+ <stylesheetResource>/standard/fopdf.xsl</stylesheetResource>
+ <finalName>hibernate_reference.pdf</finalName>
+ </format>
+ <format>
+ <formatName>html</formatName>
+ <stylesheetResource>/standard/html.xsl</stylesheetResource>
+ <finalName>index.html</finalName>
+ </format>
+ </formats>
+ <xincludeSupported>true</xincludeSupported>
+ <options>
+ <xincludeSupported>true</xincludeSupported>
+ <xmlTransformerType>saxon</xmlTransformerType>
+ <transformerParameters>
+ <property>
+ <name>custom.titlepage.img</name>
+ <value>${basedir}/src/main/docbook/standard/resources/shared/images/hibernate_logo_a.png</value>
+ </property>
+ </transformerParameters>
+ </options>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+<!-- report plugin is currently hosed
+ <reporting>
+ <plugins>
+ <plugin>
+ <groupId>org.jboss.maven.plugin</groupId>
+ <artifactId>jboss-docbook-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </reporting>
+-->
+ <properties>
+ <masterTranslation>en-US</masterTranslation>
+ </properties>
+
+</project>
Added: trunk/Hibernate3/documentation/manual/pt-BR/pom.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/pt-BR/pom.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/pt-BR/pom.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-manual</artifactId>
+ <version>3.3.0.beta1</version>
+ <relativePath>../../../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>hibernate-manual-${translation}</artifactId>
+ <packaging>pom</packaging>
+ <name>Hibernate Manual (${translation})</name>
+
+ <properties>
+ <translation>pt-BR</translation>
+ </properties>
+
+</project>
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/master.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/master.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/master.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,261 @@
+<?xml version='1.0' encoding="iso-8859-1"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3CR3//EN"
+ "../support/docbook-dtd/docbookx.dtd"
+[
+<!ENTITY tutorial SYSTEM "modules/tutorial.xml">
+<!ENTITY architecture SYSTEM "modules/architecture.xml">
+<!ENTITY configuration SYSTEM "modules/configuration.xml">
+<!ENTITY persistent-classes SYSTEM "modules/persistent_classes.xml">
+<!ENTITY basic-mapping SYSTEM "modules/basic_mapping.xml">
+<!ENTITY collection-mapping SYSTEM "modules/collection_mapping.xml">
+<!ENTITY association-mapping SYSTEM "modules/association_mapping.xml">
+<!ENTITY component-mapping SYSTEM "modules/component_mapping.xml">
+<!ENTITY inheritance-mapping SYSTEM "modules/inheritance_mapping.xml">
+<!ENTITY session-api SYSTEM "modules/session_api.xml">
+<!ENTITY transactions SYSTEM "modules/transactions.xml">
+<!ENTITY events SYSTEM "modules/events.xml">
+<!ENTITY batch SYSTEM "modules/batch.xml">
+<!ENTITY query-hql SYSTEM "modules/query_hql.xml">
+<!ENTITY query-criteria SYSTEM "modules/query_criteria.xml">
+<!ENTITY query-sql SYSTEM "modules/query_sql.xml">
+<!ENTITY filters SYSTEM "modules/filters.xml">
+<!ENTITY xml SYSTEM "modules/xml.xml">
+<!ENTITY performance SYSTEM "modules/performance.xml">
+<!ENTITY toolset-guide SYSTEM "modules/toolset_guide.xml">
+<!ENTITY example-parentchild SYSTEM "modules/example_parentchild.xml">
+<!ENTITY example-weblog SYSTEM "modules/example_weblog.xml">
+<!ENTITY example-mappings SYSTEM "modules/example_mappings.xml">
+<!ENTITY best-practices SYSTEM "modules/best_practices.xml">
+]>
+
+<book lang="en">
+
+ <bookinfo>
+ <title>HIBERNATE - Relational Persistence for Idiomatic Java</title>
+ <subtitle>Documentação da Referência do Hibernate</subtitle>
+ <releaseinfo>3.2 cr2</releaseinfo>
+ </bookinfo>
+
+ <toc/>
+
+ <preface id="preface" revision="2">
+ <title>Prefácio</title>
+
+ <para>
+ <emphasis>Advertencia! Esta é uma versão traduzida do inglês da
+ documentação de referencia do Hibernate. A versão traduziada pode estar
+ desatualizada. Sem dúvida, as diferenças devem ser pequenas e serão corrigidas o
+ mais breve possivel. Consulte a documentação de referencia em inglês, se estiver
+ faltando alguma informação ou você encotrar erros de tradução. Se quiser
+ colaborar com ama tradução em particular, entre em contato com um dos tradutores
+ abaixo:</emphasis>. Gamarra
+
+ </para>
+
+ <para>
+ Tradutor(es) em ordem alfabética:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Alvaro Netto</emphasis> alvaronetto at cetip.com.br
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Anderson Braulio</emphasis> andersonbraulio at gmail.com
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Daniel Vieira Costa</emphasis> danielvc at gmail.com
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Francisco gamarra</emphasis> francisco.gamarra at gmail.com
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Gamarra</emphasis> mauricio.gamarra at gmail.com
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Luiz Carlos Rodrigues</emphasis> luizcarlos_rodrigues at yahoo.com.br
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Marcel Castelo</emphasis> marcel.castelo at gmail.com
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ <emphasis>Paulo César</emphasis> paulocol at gmail.com
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Pablo L. de Miranda</emphasis> pablolmiranda at gmail.com
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Renato Deggau</emphasis> rdeggau at gmail.com
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Rogério Araújo</emphasis> rgildoaraujo at yahoo.com.br
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Wanderson Siqueira</emphasis> wandersonxs at gmail.com
+ </para>
+ </listitem>
+
+
+ </itemizedlist>
+
+ <para>
+ Trabalhando com software orientado a objetos e banco de dados relacional, podemos
+ ter alguns incômodos hoje em dia em ambientes empresariais. Hibernate é uma ferramenta
+ que mapeia o objeto/relacional para o ambiente Java. O termo de mapeamento de
+ objeto/relacional (ou ORM Object/Relational Mapping) se refere a técnica de mapear
+ uma representação de dados de um modelo de objeto para dados de modelo
+ relacional com o esquema baseado em SQL
+ </para>
+
+ <para>
+ O Hibernate não somente cuida do mapeamento de classes em Java
+ para tabelas de banco de dados (e de tipos de dados em Java para tipos de dados em SQL), como também
+ fornece facilidade de consultas e recuperação de dados, podendo também reduzir significantemente o
+ tempo de desenvolvimento gasto com a manipulação manual de dados no SQL e JDBC.
+ </para>
+
+ <para>
+ O objetivo do Hibernate é de aliviar o desenvolvedor de 95 por cento das tarefas de programação
+ relacionadas aos dados comuns de persistência. O Hibernate talvez não seja a melhor solução para
+ aplicações de dados-data-centric que somente usa stored-procedures para implementar a lógica
+ de negócio no banco de dados, isto é muito utilizado com o domínio de modelos orientado a objetos e
+ lógicas de negócio em camadas do meio (middle-tier) baseadas em Java. Porém, o Hibernate
+ poderá certamente ajuda-lo a remover ou encapsular o código SQL de um vendedor específico,
+ ajudando também com a tarefa comum da tradução do resultado ajustado de uma representação
+ para um gráfico de objetos.
+ </para>
+
+ <para>
+ Se você for novo no Hibernate e no mapeamento Objeto/Relacional, ou até mesmo em Java,
+ por favor, siga os seguintes passos.
+ </para>
+
+ <orderedlist>
+ <listitem>
+ <para>
+ Leia <xref linkend="tutorial"/> para um tutorial com instruções passo-a-passo.
+ O código fonte para do tutorial está incluído na distribuição no diretório
+ <literal>doc/reference/tutorial/</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Leia o <xref linkend="architecture"/> para entender o ambiente onde o Hibernate pode ser utilizado.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Dê uma olhada no diretório de exemplo <literal>eg/</literal> da distribuição
+ do Hibernate, ele contém uma simples aplicação standalone.
+ Copie seu driver JDBC para o diretório <literal>lib/</literal> e edite o arquivo
+ <literal>etc/hibernate.properties</literal>, especificando corretamente os valores
+ para seu banco de dados. Usando o prompt de commando no diretorio de distribuicao,
+ digite <literal>ant eg</literal> (usando Ant), ou no Windows, digite
+ <literal>build eg</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Use esta documentação de referencia como sua fonte primaria de informação.
+ Considere ler também o livro <emphasis>Hibernate in Action</emphasis>
+ (http://www.manning.com/bauer) caso você precise de mais ajuda com o
+ desenvolvimento de aplicações ou caso prefira um tutorial passo-a-passo.
+ Também visite o site http://caveatemptor.hibernate.org e faça o download
+ da aplicação de exemplo do Hibernate em Ação.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ FAQs (perguntas feitas com mais freqüência) estão respondidas no site do Hibernate
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Demonstrações, exemplos e tutorials estão disponíveis no site do Hibernate.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ A Área da comunidade no site do Hibernate é uma boa fonte de recursos
+ para padrões de projeto e várias soluções de integração (Tomcat, JBoss AS, Struts, EJB, etc.).
+ </para>
+ </listitem>
+ </orderedlist>
+
+ <para>
+ Caso você tenha dúvidas, use o fórum dos usuários encontrado no site do Hibernate.
+ Nós também fornecemos um sistema para controle de bugs (JIRA) para relatórios de erros
+ e requisições de features. Se você está interessado no desenvolvimento do Hibernate,
+ junte-se a lista de e-mail dos desenvolvedores.
+ </para>
+
+ <para>
+ Suporte comercial de desenvolvimento, suporte de produção e treinamento para o Hibernate
+ está disponível através do JBoss Inc. (veja http://www.hibernate.org/SupportTraining).
+ O Hibernate é um Projeto Profissional de Código Aberto e um componente crítico da suíte
+ de produtos JBoss Enterprise Middleware System (JEMS).
+ </para>
+
+ </preface>
+
+ &tutorial;
+
+ &architecture;
+
+ &configuration;
+
+ &persistent-classes;
+
+ &basic-mapping;
+ &collection-mapping;
+ &association-mapping;
+ &component-mapping;
+ &inheritance-mapping;
+
+ &session-api;
+ &transactions;
+ &events;
+ &batch;
+
+ &query-hql;
+ &query-criteria;
+ &query-sql;
+ &filters;
+ &xml;
+
+ &performance;
+
+ &toolset-guide;
+
+ &example-parentchild;
+ &example-weblog;
+ &example-mappings;
+
+ &best-practices;
+
+</book>
+
Property changes on: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/master.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/architecture.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/architecture.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/architecture.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,359 @@
+<chapter id="architecture">
+
+ <title>Arquitetura</title>
+
+ <sect1 id="architecture-overview" revision="1">
+ <title>Visão Geral</title>
+
+ <para>
+ Uma visão bem ampla da arquitetura do Hibernate:
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/overview.svg" format="SVG" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/overview.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ Esse diagrama mostra o Hibernate usando o banco de dados e a configuração
+ de dados para prover persistência de serviços (e persistência de objetos)
+ para o aplicativo.
+ </para>
+
+ <para>
+ Nós gostarÃamos de mostrar uma visão mais detalhada da arquitetura em execução.
+ Infelizmente, o Hibernate é muito flexÃvel e suporta várias aproximações.
+ Nós iremos mostrar os dois extremos. Na arquitetura mais simples o aplicativo
+ fornece suas próprias conexões JDBC e gerencia suas transações. Esta abordagem
+ usa o mÃnimo de subconjuntos das APIs do Hibernate:
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/lite.svg" format="SVG" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/lite.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ A arquitetura "completa" abstrai a aplicação de ter de lidar diretamente
+ com JDBC/JTA e APIs e deixa o Hibernate tomar conta dos detalhes.
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/full_cream.svg" format="SVG" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/full_cream.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ Algumas definições dos objetos do diagrama:
+
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term>SessionFactory (<literal>org.hibernate.SessionFactory</literal>)</term>
+ <listitem>
+ <para>
+ Um cache threadsafe (imutáveis) composto de identidades compiladas para um
+ único banco de dados. Uma fabrica para <literal>Session</literal> e um cliente
+ de <literal>ConnectionProvider</literal>. Pode conter um cachê opcional de
+ dados (segundo nÃvel) reutilizáveis entre transações, no nÃvel de processo- ou cluster.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Session (<literal>org.hibernate.Session</literal>)</term>
+ <listitem>
+ <para>
+ Objeto single-threaded, de vida curta, representando uma conversação entre
+ o aplicativo e o armazenamento persistente. Cria uma camada sobre uma conexão JDBC.
+ à uma fabrica de <literal>Transaction</literal>. Possui um cachê obrigatório
+ (primeiro nÃvel) de objetos persistentes, usado para navegação no gráficos
+ de objetos e pesquisa de objetos pelo identificador.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Objetos persistentes e coleções</term>
+ <listitem>
+ <para>
+ Objetos, de vida curta, single threaded contendo estado persistente e função
+ de negócios. Esses podem ser JavaBeans/POJOs, onde única coisa especial sobre
+ eles é que são associados a (exatamente uma) <literal>Session</literal>.
+ Quando a <literal>Session</literal> é fechada, eles são separados e liberados
+ para serem usados dentro de qualquer camada da aplicacao (Ex. diretamente como
+ data transfer objects de e para a camada de apresentação)
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Objetos e coleções desatachados e transientes</term>
+ <listitem>
+ <para>
+ Instâncias de classes persistentes que ainda não estão associadas a uma
+ <literal>Session</literal>. Eles podem ter sido instanciados pela aplicação
+ e não persistido (ainda) ou eles foram instanciados por uma <literal>Session</literal>
+ que foi encerrada.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Transaction (<literal>org.hibernate.Transaction</literal>)</term>
+ <listitem>
+ <para>
+ (Opcional) Objeto de vida curta, single threaded, usado pela aplicação para
+ especificar unidades atômicas de trabalho. Abstrai o aplicativo de lidar
+ diretamente com transações JDBC, JTA ou CORBA. Uma <literal>Session</literal> pode,
+ em alguns casos, iniciar várias <literal>Transaction</literal>s. Entretanto,
+ a demarcação da transação, mesmo utilizando API ou Transaction subjacentes,
+ nunca é opcional!
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>ConnectionProvider (<literal>org.hibernate.connection.ConnectionProvider</literal>)</term>
+ <listitem>
+ <para>
+ (Opcional) Uma fábrica de (e combinações de) conexões JDBC. Abstrai a aplicação
+ de lidar diretamente com <literal>Datasource</literal> ou <literal>DriverManager</literal>.
+ Não exposto para a aplicação, mas pode ser implementado ou estendido pelo programador.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>TransactionFactory (<literal>org.hibernate.TransactionFactory</literal>)</term>
+ <listitem>
+ <para>
+ (Opcional) Uma fábrica para instâncias de <literal>Transaction</literal>. Não exposta
+ a aplicação, mas pode ser extendida/implementada pelo programador.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><emphasis>Extension Interfaces</emphasis></term>
+ <listitem>
+ <para>
+ O Hibernate oferece várias opções de interfaces estendidas que você pode implementar
+ para customizar sua camada persistente. Veja a documentação da API para maiores detalhes.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ <para>
+ Dada uma arquitetura simples, o aplicativo passa pelas APIs
+ <literal>Transaction</literal>/<literal>TransactionFactory</literal> e/ou
+ <literal>ConnectionProvider</literal> para se comunicar diretamente com a transação JTA ou JDBC.
+ </para>
+ </sect1>
+
+ <sect1 id="architecture-states" revision="1">
+ <title>Estados de instância</title>
+ <para>
+ Uma instância de classes persistentes pode estar em um dos três diferentes estados,
+ que são definidos respeitando um <emphasis>contexto persistente</emphasis>.
+ O objeto <literal>Session</literal> do Hibernate é o contexto persistente:
+ </para>
+
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term>transiente</term>
+ <listitem>
+ <para>
+ A instância não é, e nunca foi associada com nenhum
+ contexto persistente. Não possui uma identidade persistente
+ (valor de chave primária).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>persistente</term>
+ <listitem>
+ <para>
+ A instância está atualmente associada a um contexto persistente.
+ Possui uma identidade persistente (valor de chave primária) e,
+ talvez, correspondente a um registro no banco de dados. Para um
+ contexto persistente em particular, o Hibernate
+ <emphasis>guarantees</emphasis> que a identidade persistente
+ é equivalente a identidade Java (na localização em memória do
+ objeto).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>desatachado</term>
+ <listitem>
+ <para>
+ A instância foi associada com um contexto persistente,
+ porém este contexto foi fechado, ou a instância
+ foi serializada por outro processo. Possui uma identidade
+ persistente, e, talvez, correspondenta a um registro no
+ banco de dados. Para instâncias desatachadas, o Hibernate
+ não garante o relacionamento entre identidade persistente
+ e identidade Java.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </sect1>
+
+ <sect1 id="architecture-jmx" revision="1">
+ <title>Integração JMX</title>
+
+ <para>
+ JMX é padrão J2EE para manipulação de componentes Java. O Hibernate pode ser manipulado
+ por um serviço JMX padrão. Nós fornecemos uma implementação do MBean na distribuição,
+ <literal>org.hibernate.jmx.HibernateService</literal>.
+ </para>
+
+ <para>
+ Para um exemplo de como instalar o Hibernate como um serviço JMX em um servidor de
+ aplicativo JBoss, por favor, consulte o manual do usuário do JBoss. No JBoss As, você
+ poderá ver os benefÃcios de de se fazer o deploy usando JMX:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Session Management:</emphasis> O ciclo de vida de uma <literal>Session</literal> do
+ Hibernate pode ser automaticamente conectada a um escopo de transação JTA. Isso significa
+ que você não precisará mais abrir e fechar manualmente uma <literal>Session</literal>, isso
+ se torna trabalho para um interceptor EJB do JBoss . Você também não precisa se preocupar,
+ nunca mais, com demarcação de transação em seu código (a não ser que você prefira escrever
+ uma camada persistente portável, para isso, use a API opcional do Hibernate
+ <literal>Transaction</literal>). Você deve chamar <literal>HibernateContext</literal>
+ para acessar uma <literal>Session</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>HAR deployment:</emphasis>: Normalmente você faz o deploy de um serviço JMX do
+ Hibernate usando um serviço descritor de deploy do JBoss (em um EAR e/ou arquivo SAR),
+ que suporta todas as configurações usuais de uma <literal>SessionFactory</literal> do
+ Hibernate. Entretanto, você ainda precisa nomear todos os seus arquivos de mapeamento
+ no descritor de deploração. Se você decidir usar o deploy opcional HAR, o JBoss irá
+ automaticamente detectar todos os seus arquivos de mapeamento no seu arquivo HAR.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Consulte o manual do usuário do JBoss AS, para obter maiores informações sobre essas opções.
+ </para>
+
+ <para>
+ Another feature available as a JMX service are runtime Hibernate statistics. See
+ <xref linkend="configuration-optional-statistics"/>.
+ Outra opção disponÃvel como um serviço JMX são as estatÃsticas de execução do Hibernate.
+ Veja a <xref linkend="configuration-optional-statistics"/>.
+ </para>
+ </sect1>
+
+ <sect1 id="architecture-jca" revision="1">
+ <title>Suporte JCA</title>
+ <para>
+ Hibernate may also be configured as a JCA connector. Please see the website for more
+ details. Please note that Hibernate JCA support is still considered experimental.
+ O Hibernate pode também ser configurado como um conector JCA. Por favor, visite o
+ website para maiores detalhes. Entretanto, note que o suporte JCA do Hibernate
+ ainda é considerado experimental.
+ </para>
+ </sect1>
+
+ <sect1 id="architecture-current-session" revision="2">
+ <title>Sessões contextuais</title>
+ <para>
+ Muitas aplicações que usam o Hibernate necessita de algum tipo de sessão "contextual",
+ onde uma sessão dada é na verdade um escopo de um contexto. Entretanto, através de aplicações
+ a definição sobre um contexto é geralmente diferente; e contextos diferentes definem escopos
+ diferentes. Aplicações usando versões anteriores ao Hibernate 3.0 tendem a utilizar tanto
+ sessões contextuais baseadas em <literal>ThreadLocal</literal>, classes utilitárias como
+ <literal>HibernateUtil</literal>, ou utilizar frameworks de terceiros(como Spring ou Pico)
+ que provê sessões contextuais baseadas em proxy.
+
+ </para>
+ <para>
+ A partir da versão 3.0.1, o Hibernate adicionou o método <literal>SessionFactory.getCurrentSession()</literal>.
+ Inicialmente, este assume o uso de transações <literal>JTA</literal>, onde a transação
+ <literal>JTA</literal> define tanto o escopo quanto o contexto de uma sessão atual.
+ O time do Hibernate mantém este recurso, desenvolvendo as diversas implementações do
+ <literal>JTA TransactionManager</literal>, a maioria (se não todos) aplicativos deveria
+ utilizar o gerenciador de transações <literal>JTA</literal> sendo ou não instalados dentro
+ de um container <literal>J2EE</literal>. Baseado neste recurso, você deveria sempre utilizar sessões
+ contextuais baseadas em <literal>JTA</literal>.
+ </para>
+ <para>
+ Entretanto, na versão 3.1, o processo por trás do método <literal>SessionFactory.getCurrentSession()</literal>
+ é agora plugavel. Com isso, uma nova interface (<literal>org.hibernate.context.CurrentSessionContext</literal>)
+ e um novo parâmetro de configuração (<literal>hibernate.current_session_context_class</literal>)
+ foram adicionados para possibilitar a compatibilidade do contexto e do escopo na definição de sessões correntes.
+ </para>
+ <para>
+ De uma olhada em Javadocs sobre a interface <literal>org.hibernate.context.CurrentSessionContext</literal>
+ para uma discussão detalhada. Ela define um método único, <literal>currentSession()</literal>,
+ com o qual a implementação é responsável por rastrear a sessão contextual corrente.
+ Por fora do "encapsulamento", o Hibernate possui duas implementações dessa interface.
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>org.hibernate.context.JTASessionContext</literal> - As sessões correntes
+ são rastreadas e recebem um escopo por uma transação <literal>JTA</literal>.
+ O processamento aqui é exatamente igual ao antigo processo JTA. Consulte em
+ Javadocs para maiores detalhes.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>org.hibernate.context.ThreadLocalSessionContext</literal> - As sessões
+ correntes são rastreadas por uma thread de execução. Novamente, consulte em
+ Javadocs para maiores detalhes.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>org.hibernate.context.ManagedSessionContext</literal> - current
+ sessions are tracked by thread of execution. However, you are responsible to
+ bind and unbind a <literal>Session</literal> instance with static methods
+ on this class, it does never open, flush, or close a <literal>Session</literal>.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ As duas primeiras implementações usam o modelo de programação "uma sessão â uma transação
+ do banco de dados", também conhecida e usado como <emphasis>sessão por requisição</emphasis>.
+ O começo e o fim de uma sessão Hibernate é definida pela duração da transação do banco de dados.
+ Se você usa demarcação programática de transação (por exemplo. em J2SE puro ou com JTA
+ /UserTransaction/BMT), você é recomendado a usar a API Hibernate <literal>Transaction</literal>
+ para esconder a base do sistema de transação do seu código. Se você executa em um container EJB
+ que suporta CMT, os limites das transações são definidas declarativamente e você não necessita
+ de qualquer transação ou operação de demarcação de sessão no seu código. Consulte o <xref linkend="transactions"/>
+ para mais informações exemplos de código.
+ </para>
+
+ <para>
+ O parâmetro de configuração <literal>hibernate.current_session_context_class</literal>
+ define que a implementação <literal>org.hibernate.context.CurrentSessionContext</literal>
+ deve ser usada. Note que para compatibilidade anterior, se este parâmetro de configuração
+ não é determinado mas um <literal>org.hibernate.transaction.TransactionManagerLookup</literal>
+ é configurado, Hibernate usará o <literal>org.hibernate.context.JTASessionContext</literal>.
+ Tipicamente, o valor deste parâmetro nomearia apenas a classe de implementação para usar;
+ para as duas implementações out-of-the-box, entretanto, há dois pequenos nomes correspondentes,
+ "jta", "thread", and "managed".
+ </para>
+
+ </sect1>
+
+</chapter>
Property changes on: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/architecture.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/association_mapping.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/association_mapping.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/association_mapping.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,635 @@
+<chapter id="associations">
+
+ <title>Mapeamento de Associações</title>
+
+ <sect1 id="assoc-intro" revision="1">
+ <title>Introdução</title>
+
+ <para>
+ Mapeamentos de associações são freqüentemente a coisa mais difÃcil de se
+ acertar. Nesta seção nós passaremos pêlos casos canônicos um pôr um,
+ começando com mapeamentos unidirecionais e considerando os casos
+ bidirecionais. Nos vamos usar <literal>Person</literal> e
+ <literal>Address</literal> em todos os exemplos.
+ </para>
+
+ <para>
+ Nós classificaremos as associações pelo seu mapeamento ou a falta do
+ mesmo, sua intervenção na tabela associativa, e pela sua multiplicidade.
+ </para>
+
+ <para>
+ O uso de chaves estrangeiras não obrigatórias não é considerada uma boa
+ prática na modelagem de dados tradicional, assim todos nossos exemplos
+ usam chaves estrangeiras obrigatórias. Esta não é uma exigência do
+ Hibernate, e todas as mapeamentos funcionarão se você remover as
+ constraints de obrigatoriedade.
+ </para>
+
+ </sect1>
+
+ <sect1 id="assoc-unidirectional" revision="1">
+ <title>Associações Unidirecionais</title>
+
+ <sect2 id="assoc-unidirectional-m21">
+ <title>muitos para um</title>
+
+ <para>
+ Uma <emphasis>associação unidirecional muitos-para-um</emphasis> é o
+ tipo mais comum de associação unidirecional.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key, addressId bigint not null )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-121">
+ <title>um para um</title>
+
+ <para>
+ Uma <emphasis>associação unidirecional um-para-um em uma chave estrangeira
+ </emphasis> é quase idêntica. A única diferença é a constraint unique
+ na coluna.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <many-to-one name="address"
+ column="addressId"
+ unique="true"
+ not-null="true"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key, addressId bigint not null unique )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ <para>
+ Uma <emphasis>associação unidirecional um-para-um na chave primaria</emphasis>
+ geralmente usa um gerador de id special. ( Note que nós invertemos a
+ direção da associação nesse exemplo).
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+</class>
+
+<class name="Address">
+ <id name="id" column="personId">
+ <generator class="foreign">
+ <param name="property">person</param>
+ </generator>
+ </id>
+ <one-to-one name="person" constrained="true"/>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table Address ( personId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-12m">
+ <title>um para muitos</title>
+
+ <para>
+ Uma <emphasis>associação unidirecional um-para-muitos em uma chave
+ estrangeira</emphasis> é um caso muito incomum, e realmente não é recomendada.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses">
+ <key column="personId"
+ not-null="true"/>
+ <one-to-many class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table Address ( addressId bigint not null primary key, personId bigint not null )
+ ]]></programlisting>
+
+ <para>
+ Nós achamos que é melhor usar uma tabela associativa para este tipo de
+ associação.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="assoc-unidirectional-join" revision="1">
+ <title>Associações Unidirecionais com tabelas associativas</title>
+
+ <sect2 id="assoc-unidirectional-join-12m">
+ <title>um para muitos</title>
+
+ <para>
+ Uma <emphasis>associação um-para-muitos unidirecional usando uma tabela
+ associativa</emphasis> e o mais comum. Note que se especificarmos
+ <literal>unique="true"</literal>, estaremos modificando a cardinalidade
+ de muitos-para-muitos para um-para-muitos.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses" table="PersonAddress">
+ <key column="personId"/>
+ <many-to-many column="addressId"
+ unique="true"
+ class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId not null, addressId bigint not null primary key )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-join-m21">
+ <title>muitos para um</title>
+
+ <para>
+ Uma <emphasis>associação unidirecional muitos-para-um em uma tabela
+ associativa</emphasis> é bastante comum quando a associação for opcional.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ optional="true">
+ <key column="personId" unique="true"/>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"/>
+ </join>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null primary key, addressId bigint not null )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-join-121">
+ <title>um para um</title>
+
+ <para>
+ Uma <emphasis>associação unidirecional um-para-um em uma tabela
+ associativa</emphasis> é extremamente incomum, mas possÃvel.
+
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ optional="true">
+ <key column="personId"
+ unique="true"/>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"
+ unique="true"/>
+ </join>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-join-m2m">
+ <title>muitos para muitos</title>
+
+ <para>
+ Finalmente, nós temos a <emphasis>associação unidirecional muitos-para-
+ muitos</emphasis>.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses" table="PersonAddress">
+ <key column="personId"/>
+ <many-to-many column="addressId"
+ class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personId, addressId) )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="assoc-bidirectional" revision="1">
+ <title>Associações Bidirecionais</title>
+
+ <sect2 id="assoc-bidirectional-m21" revision="2">
+ <title>um para muitos / muitos para um</title>
+
+ <para>
+ Uma <emphasis>associação bidirecional muitos-para-um</emphasis> é o
+ tipo mais comum de associação. (Esse é o relacionamento padrão
+ pai / filho. )
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <set name="people" inverse="true">
+ <key column="addressId"/>
+ <one-to-many class="Person"/>
+ </set>
+</class>]]></programlisting>
+
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key, addressId bigint not null )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ <para>
+ Se você usar uma <literal>List</literal> ( ou outra coleção indexada ), você
+ precisa especificar a coluna <literal>chave</literal> estrangeira como not null,
+ e deixar o Hibernate administrar a associação do lado da coleção para que
+ seja mantido o Ãndice de cada elemento da coleção (fazendo com que o outro
+ lado seja virtualmente inverso setando <literal>update="false"</literal>
+ e <literal>insert="false"</literal>):
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id"/>
+ ...
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"
+ insert="false"
+ update="false"/>
+</class>
+
+<class name="Address">
+ <id name="id"/>
+ ...
+ <list name="people">
+ <key column="addressId" not-null="true"/>
+ <list-index column="peopleIdx"/>
+ <one-to-many class="Person"/>
+ </list>
+</class>]]></programlisting>
+
+ <para>
+ à importante que você defina <literal>not-null="true"</literal> no elemento
+ <literal><key></literal> no mapeamento na coleção se a coluna de chave
+ estrangeira for <literal>NOT NULL</literal>. Não declare como
+ <literal>not-null="true"</literal> apenas um elemento aninhado
+ <literal><column></literal>, mas sim o elemento
+ <literal><key></literal>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="assoc-bidirectional-121">
+ <title>um para um</title>
+
+ <para>
+ Uma <emphasis>associação bidirecional um para um em uma chave estrangeira</emphasis>
+ é bastante comum.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <many-to-one name="address"
+ column="addressId"
+ unique="true"
+ not-null="true"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <one-to-one name="person"
+ property-ref="address"/>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key, addressId bigint not null unique )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ <para>
+ Uma <emphasis>associação bidirecional um para um em uma chave primária</emphasis>
+ usa um gerador de id especial.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <one-to-one name="address"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="personId">
+ <generator class="foreign">
+ <param name="property">person</param>
+ </generator>
+ </id>
+ <one-to-one name="person"
+ constrained="true"/>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table Address ( personId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="assoc-bidirectional-join" revision="1">
+ <title>Associações Bidirecionais com tabelas associativas</title>
+
+ <sect2 id="assoc-bidirectional-join-12m">
+ <title>um para muitos / muitos para um</title>
+
+ <para>
+ Uma <emphasis>associação bidirecional um para muitos em uma tabela
+ associativa</emphasis>. Veja que <literal>inverse="true"</literal> pode ser
+ colocado em qualquer ponta associação, na coleção, ou no join.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses"
+ table="PersonAddress">
+ <key column="personId"/>
+ <many-to-many column="addressId"
+ unique="true"
+ class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ inverse="true"
+ optional="true">
+ <key column="addressId"/>
+ <many-to-one name="person"
+ column="personId"
+ not-null="true"/>
+ </join>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null, addressId bigint not null primary key )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-bidirectional-join-121">
+ <title>one to one</title>
+
+ <para>
+ Uma <emphasis>associação bidirecional um-para-um em uma tabela de
+ associação</emphasis> é algo bastante incomum, mas possivel.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ optional="true">
+ <key column="personId"
+ unique="true"/>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"
+ unique="true"/>
+ </join>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ optional="true"
+ inverse="true">
+ <key column="addressId"
+ unique="true"/>
+ <many-to-one name="person"
+ column="personId"
+ not-null="true"
+ unique="true"/>
+ </join>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-bidirectional-join-m2m" revision="1">
+ <title>muitos para muitos</title>
+
+ <para>
+ Finally, we have a <emphasis>bidirectional many-to-many association</emphasis>.
+ Finalmente, nós temos uma associação bidirecional de muitos para muitos.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses" table="PersonAddress">
+ <key column="personId"/>
+ <many-to-many column="addressId"
+ class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <set name="people" inverse="true" table="PersonAddress">
+ <key column="addressId"/>
+ <many-to-many column="personId"
+ class="Person"/>
+ </set>
+</class>]]></programlisting>
+
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personId, addressId) )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="assoc-complex">
+ <title>Mapeamento de associações mais complexas</title>
+
+ <para>
+ More complex association joins are <emphasis>extremely</emphasis> rare.
+ Hibernate makes it possible to handle more complex situations using
+ SQL fragments embedded in the mapping document. For example, if a table
+ with historical account information data defines
+ <literal>accountNumber</literal>, <literal>effectiveEndDate</literal>
+ and <literal>effectiveStartDate</literal>columns, mapped as follows:
+Joins de associações mais complexas são extremamente raros. O Hibernate torna possÃvel tratar mapeamentos mais complexos usando fragmentos de SQL embutidos no documento de mapeamento. Por exemplo, se uma tabela com informações de dados históricos de uma conta define a coluna accountNumber, effectiveEndDate e effectiveStartDate, mapeadas assim como segue:
+ </para>
+
+ <programlisting><![CDATA[<properties name="currentAccountKey">
+ <property name="accountNumber" type="string" not-null="true"/>
+ <property name="currentAccount" type="boolean">
+ <formula>case when effectiveEndDate is null then 1 else 0 end</formula>
+ </property>
+</properties>
+<property name="effectiveEndDate" type="date"/>
+<property name="effectiveStateDate" type="date" not-null="true"/>]]></programlisting>
+
+ <para>
+ Then we can map an association to the <emphasis>current</emphasis> instance
+ (the one with null <literal>effectiveEndDate</literal>) using:
+Então nós podemos mapear uma associação para a instância corrente (aquela com a effectiveEndDate igual a null) usando:
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="currentAccountInfo"
+ property-ref="currentAccountKey"
+ class="AccountInfo">
+ <column name="accountNumber"/>
+ <formula>'1'</formula>
+</many-to-one>]]></programlisting>
+
+ <para>
+ In a more complex example, imagine that the association between
+ <literal>Employee</literal> and <literal>Organization</literal> is maintained
+ in an <literal>Employment</literal> table full of historical employment data.
+ Then an association to the employee's <emphasis>most recent</emphasis> employer
+ (the one with the most recent <literal>startDate</literal>) might be mapped this way:
+Em um exemplo mais complexo, imagine que a associação entre Employee e Organization é mantida em uma tabela Employment cheia de dados históricos do trabalho. Então a associação do funcionário mais recentemente empregado (aquele com a mais recente startDate) deve ser mapeado desta maneira:
+ </para>
+
+ <programlisting><![CDATA[<join>
+ <key column="employeeId"/>
+ <subselect>
+ select employeeId, orgId
+ from Employments
+ group by orgId
+ having startDate = max(startDate)
+ </subselect>
+ <many-to-one name="mostRecentEmployer"
+ class="Organization"
+ column="orgId"/>
+</join>]]></programlisting>
+
+ <para>
+ You can get quite creative with this functionality, but it is usually more practical
+ to handle these kinds of cases using HQL or a criteria query.
+Você pode ser criativo com esta funcionalidade, mas geralmente é mais prático tratar estes tipos de casos, usando uma pesquisa HQL ou uma pesquisa por criteria.
+ </para>
+
+ </sect1>
+
+
+</chapter>
+
Property changes on: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/association_mapping.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/basic_mapping.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/basic_mapping.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/basic_mapping.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,3491 @@
+<chapter id="mapping">
+ <title>Mapeamento O/R Bassico</title>
+
+ <sect1 id="mapping-declaration" revision="1">
+ <title>Declaração de mapeamento</title>
+
+ <para>
+ Object/relational mappings are usually defined in an XML document. The mapping
+ document is designed to be readable and hand-editable. The mapping language is
+ Java-centric, meaning that mappings are constructed around persistent class
+ declarations, not table declarations.
+ </para>
+
+ <para>
+ Note that, even though many Hibernate users choose to write the XML by hand,
+ a number of tools exist to generate the mapping document, including XDoclet,
+ Middlegen and AndroMDA.
+ </para>
+
+ <para>
+ Lets kick off with an example mapping:
+ </para>
+
+ <programlisting id="mapping-declaration-ex1" revision="1"><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="eg">
+
+ <class name="Cat"
+ table="cats"
+ discriminator-value="C">
+
+ <id name="id">
+ <generator class="native"/>
+ </id>
+
+ <discriminator column="subclass"
+ type="character"/>
+
+ <property name="weight"/>
+
+ <property name="birthdate"
+ type="date"
+ not-null="true"
+ update="false"/>
+
+ <property name="color"
+ type="eg.types.ColorUserType"
+ not-null="true"
+ update="false"/>
+
+ <property name="sex"
+ not-null="true"
+ update="false"/>
+
+ <property name="litterId"
+ column="litterId"
+ update="false"/>
+
+ <many-to-one name="mother"
+ column="mother_id"
+ update="false"/>
+
+ <set name="kittens"
+ inverse="true"
+ order-by="litter_id">
+ <key column="mother_id"/>
+ <one-to-many class="Cat"/>
+ </set>
+
+ <subclass name="DomesticCat"
+ discriminator-value="D">
+
+ <property name="name"
+ type="string"/>
+
+ </subclass>
+
+ </class>
+
+ <class name="Dog">
+ <!-- mapping for Dog could go here -->
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Discutir agora o conteúdo deste documento de mapeamento. Iremos apenas descrever
+ os elementos do documento e atributos que são utilizados pelo Hibernate em tempo
+ de execução. O documento de mapeamento também contém alguns atributos adicionais
+ e opcionais além de elementos que afetam os esquemas de banco de dados exportados
+ pela ferramenta de exportação de esquemas. (Por exemplo, o atributo
+ <literal>not-null</literal>).
+ </para>
+
+
+
+ <sect2 id="mapping-declaration-doctype" revision="3">
+ <title>Doctype</title>
+
+ <para>
+ Todos os mapeamentos de XML devem declarar o doctype exibido. O DTD atual pode
+ ser encontrado na URL abaixo, no diretório <literal>hibernate-x.x.x/src/org/
+ hibernate </literal> ou no <literal>hibernate3.jar</literal>. O Hibernate sempre
+ irá procurar pelo DTD inicialmente no seu classpath. Se você tentar localizar
+ o DTD usando uma conexão de internet, compare a declaração do seu DTD com o
+ conteúdo do seu classpath
+ </para>
+
+ <sect3 id="mapping-declaration-entity-resolution">
+ <title>EntityResolver</title>
+ <para>
+ As mentioned previously, Hibernate will first attempt to resolve DTDs in its classpath. The
+ manner in which it does this is by registering a custom <literal>org.xml.sax.EntityResolver</literal>
+ implementation with the SAXReader it uses to read in the xml files. This custom
+ <literal>EntityResolver</literal> recognizes two different systemId namespaces.
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ a <literal>hibernate namespace</literal> is recognized whenever the
+ resolver encounteres a systemId starting with
+ <literal>http://hibernate.sourceforge.net/</literal>; the resolver
+ attempts to resolve these entities via the classlaoder which loaded
+ the Hibernate classes.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ a <literal>user namespace</literal> is recognized whenever the
+ resolver encounteres a systemId using a <literal>classpath://</literal>
+ URL protocol; the resolver will attempt to resolve these entities
+ via (1) the current thread context classloader and (2) the
+ classloader which loaded the Hibernate classes.
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+ An example of utilizing user namespacing:
+ </para>
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" [
+ <!ENTITY types SYSTEM "classpath://your/domain/types.xml">
+]>
+
+<hibernate-mapping package="your.domain">
+ <class name="MyEntity">
+ <id name="id" type="my-custom-id-type">
+ ...
+ </id>
+ <class>
+ &types;
+</hibernate-mapping>]]></programlisting>
+ <para>
+ Where <literal>types.xml</literal> is a resource in the <literal>your.domain</literal>
+ package and contains a custom <xref linkend="mapping-types-custom">typedef</xref>.
+ </para>
+ </sect3>
+ </sect2>
+
+ <sect2 id="mapping-declaration-mapping" revision="3">
+ <title>hibernate-mapping</title>
+
+ <para>
+ Este elemento tem diversos atributos opcionais. Os atributos
+ <literal>schema</literal> e <literal>catalog</literal> especificam que tabelas
+ referenciadas neste mapeamento pertencem ao esquema e/ou ao catalogo nomeado.
+ Se especificados, os nomes das tabelas irão ser qualificados no schema ou catalog dado.
+ Se não, os nomes das tabelas não serão qualificados. O atributo <literal>default-cascade
+ </literal> especifica qual estilo de cascata será assumido pelas propriedades e
+ coleções que não especificarm um atributo <literal>cascade</literal>. O atributo
+ <literal>auto-import</literal> nos deixa utilizar nomes de classes não qualificados
+ na linguagem de consulta, por default.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="hm1" coords="2 55"/>
+ <area id="hm2" coords="3 55"/>
+ <area id="hm3" coords="4 55"/>
+ <area id="hm4" coords="5 55"/>
+ <area id="hm5" coords="6 55"/>
+ <area id="hm6" coords="7 55"/>
+ <area id="hm7" coords="8 55"/>
+ </areaspec>
+ <programlisting><![CDATA[<hibernate-mapping
+ schema="schemaName"
+ catalog="catalogName"
+ default-cascade="cascade_style"
+ default-access="field|property|ClassName"
+ default-lazy="true|false"
+ auto-import="true|false"
+ package="package.name"
+ />]]></programlisting>
+ <calloutlist>
+ <callout arearefs="hm1">
+ <para>
+ <literal>schema</literal> (opcional): O nome do esquema do banco de dados.
+ </para>
+ </callout>
+ <callout arearefs="hm2">
+ <para>
+ <literal>catalog</literal> (opcional): O nome do catálogo do banco de dados.
+ </para>
+ </callout>
+ <callout arearefs="hm3">
+ <para>
+ <literal>default-cascade</literal> (opcional â default é <literal>nenhum
+ </literal>): Um estilo cascata default.
+ </para>
+ </callout>
+ <callout arearefs="hm4">
+ <para>
+ <literal>default-access</literal> (opcional â default é <literal>property</literal>):
+ A estratégia que o Hibernate deve utilizar para acessar todas as propridades. Pode
+ ser uma implementação própria de <literal>PropertyAccessor</literal>.
+ </para>
+ </callout>
+ <callout arearefs="hm5">
+ <para>
+ <literal>default-lazy</literal> (opcional - default é <literal>true</literal>):
+ O valor default para atributos <literal>lazy</literal> da classe e dos
+ mapeamentos de coleções.
+ </para>
+ </callout>
+ <callout arearefs="hm6">
+ <para>
+ <literal>auto-import</literal> ((opcional - default é <literal>true</literal>):
+ Especifica se nós podemos usar nomes de classess não qualificados
+ (das classes deste mapeamento) na linguagem de consulta.
+ </para>
+ </callout>
+ <callout arearefs="hm7">
+ <para>
+ <literal>package</literal> (opcional): Especifica um prefixo da package para
+ assumir para nomes de classes não qualificadas no documento de mapeamento.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Se voce tem duas classes persistentes com o mesmo nome (não qualificadas), você deve
+ setar <literal>auto-import="false"</literal>. O Hibernate irá gerar uma exceção se
+ você tentar setar duas classes para o mesmo nome "importado".
+ </para>
+
+ <para>
+ Observe que o elemento <literal>hibernate-mapping</literal> permite a você
+ aninhar diversos mapeamentos de <literal><class></literal> persistentes,
+ como mostrado abaixo. Entretanto, é uma boa prática (e esperado por algumas
+ ferramentas)o mapeamento de apenas uma classe persistente simples (ou uma
+ hierarquia de classes simples) em um arquivo de mapeamento e nomea-la após
+ a superclasse persistente, por exemplo: <literal>Cat.hbm.xml</literal>,
+ <literal>Dog.hbm.xml</literal>, ou se estiver usando herança,
+ <literal>Animal.hbm.xml</literal>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-class" revision="3">
+ <title>class</title>
+
+ <para>
+ Você pode declarar uma classe persistente utilizando o elemento
+ <literal>class</literal>:
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="class1" coords="2 55"/>
+ <area id="class2" coords="3 55" />
+ <area id="class3" coords="4 55"/>
+ <area id="class4" coords="5 55" />
+ <area id="class5" coords="6 55"/>
+ <area id="class6" coords="7 55" />
+ <area id="class7" coords="8 55"/>
+ <area id="class8" coords="9 55" />
+ <area id="class9" coords="10 55" />
+ <area id="class10" coords="11 55"/>
+ <area id="class11" coords="12 55"/>
+ <area id="class12" coords="13 55"/>
+ <area id="class13" coords="14 55"/>
+ <area id="class14" coords="15 55"/>
+ <area id="class15" coords="16 55"/>
+ <area id="class16" coords="17 55"/>
+ <area id="class17" coords="18 55"/>
+ <area id="class18" coords="19 55"/>
+ <area id="class19" coords="20 55"/>
+ <area id="class20" coords="21 55"/>
+ <area id="class21" coords="22 55"/>
+ </areaspec>
+ <programlisting><![CDATA[<class
+ name="ClassName"
+ table="tableName"
+ discriminator-value="discriminator_value"
+ mutable="true|false"
+ schema="owner"
+ catalog="catalog"
+ proxy="ProxyInterface"
+ dynamic-update="true|false"
+ dynamic-insert="true|false"
+ select-before-update="true|false"
+ polymorphism="implicit|explicit"
+ where="arbitrary sql where condition"
+ persister="PersisterClass"
+ batch-size="N"
+ optimistic-lock="none|version|dirty|all"
+ lazy="true|false"
+ entity-name="EntityName"
+ check="arbitrary sql check condition"
+ rowid="rowid"
+ subselect="SQL expression"
+ abstract="true|false"
+ node="element-name"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="class1">
+ <para>
+ <literal>name</literal> (opcional): O nome da classe Java inteiramente qualificado
+ da classe persistente (ou interface); Se o atributo é ausente, assume-se que o
+ mapeamento é para intidades não-POJO.
+ </para>
+ </callout>
+ <callout arearefs="class2">
+ <para>
+ <literal>table</literal> (opcional â default para nomes de classes não
+ qualificadas) O nome da sua tabela do banco de dados.
+ </para>
+ </callout>
+ <callout arearefs="class3">
+ <para>
+ <literal>discriminator-value</literal> (opcional â default para o nome da classe):
+ Um valor que distingue subclasses individuais, usadas para o comportamento polimorfico.
+ Valores aceitos incluem <literal>null</literal> e <literal>not null</literal>
+ </para>
+ </callout>
+ <callout arearefs="class4">
+ <para>
+ <literal>mutable</literal> (opcional - valor default <literal>true</literal>):
+ Especifica que instancias da classe são (ou não) mutáveis
+ </para>
+ </callout>
+ <callout arearefs="class5">
+ <para>
+ <literal>schema</literal> (opcional): Sobrepõe o nome do esquema especificado
+ pelo elemento root <literal><hibernate-mapping></literal>.
+ </para>
+ </callout>
+ <callout arearefs="class6">
+ <para>
+ <literal>catalog</literal> (opcional): Sobrepõe o nome do catálogo especificado
+ pelo elemento root <literal><hibernate-mapping></literal>.
+ </para>
+ </callout>
+ <callout arearefs="class7">
+ <para>
+ <literal>proxy</literal> (opcional): Especifica um interface para ser
+ utilizada pelos proxies de inicialização tardia. Você pode especificar o
+ nome da própria classe.
+ </para>
+ </callout>
+ <callout arearefs="class8">
+ <para>
+ <literal>dynamic-update</literal> (opcional, valor default <literal>false</literal>):
+ Especifica que o SQL de <literal>UPDATE</literal> deve ser gerado em tempo de
+ execução e conter apenas aquelas colunas cujos valores foram alterados.
+ </para>
+ </callout>
+ <callout arearefs="class9">
+ <para>
+ <literal>dynamic-insert</literal> (opcional, valor default <literal>false</literal>):
+ Especifica que o SQL de <literal>INSERT</literal> deve ser gerado em tempo de
+ execução e conter apenas aquelas colunas cujos valores não estão nulos.
+ </para>
+ </callout>
+ <callout arearefs="class10">
+ <para>
+ <literal>select-before-update</literal> (opcional, valor default <literal>false</literal>):
+ Especifica que o Hibernate <emphasis>never</emphasis> deve executar um SQL de
+ <literal>UPDATE</literal> a não ser que com certeza um objeto está atualmente modificado.
+ Em certos casos (atualmente, apenas quando um objeto transiente foi associado com uma nova
+ sessão utilizando <literal>update()</literal>), isto significa que o Hibernate ira executar
+ uma instrução SQL de <literal>SELECT</literal> adicional para determinar se um
+ <literal>UPDATE</literal> é necessário nesse momento.
+ </para>
+ </callout>
+ <callout arearefs="class11">
+ <para>
+ <literal>polymorphism</literal> (opcional, default para <literal>implicit</literal>):
+ Determina se deve ser utilizado a query polimorfica implicita ou explicitamente.
+ </para>
+ </callout>
+ <callout arearefs="class12">
+ <para>
+ <literal>where</literal> (opicional) especifica um comando SQL <literal>WHERE</literal>
+ arbitrário para ser usado quando da recuperação de objetos desta classe.
+ </para>
+ </callout>
+ <callout arearefs="class13">
+ <para>
+ <literal>persister</literal> (opcional): Espeicifca uma <literal>ClassPersister</literal>
+ customizada.
+ </para>
+ </callout>
+ <callout arearefs="class14">
+ <para>
+ <literal>batch-size</literal> (opcional, valor default <literal>1</literal>) especifica um
+ "tamanho de lote" para a recuperação de instancias desta classe pelo identificador.
+ </para>
+ </callout>
+ <callout arearefs="class15">
+ <para>
+ <literal>optimistic-lock</literal> (octional, valor default <literal>version</literal>):
+ Determina a estratégia de bloqueio.
+ </para>
+ </callout>
+ <callout arearefs="class16">
+ <para>
+ <literal>lazy</literal> (opcional): A recuperação tardia pode ser completamente
+ desabilitada, setando <literal>lazy="false"</literal>.
+ </para>
+ </callout>
+ <callout arearefs="class17">
+ <para>
+ <literal>entity-name</literal> (opcional, default para o nome da classe): O
+ Hibernate3 permite uma classe ser mapeada multiplas vezes, (potencialmente,para
+ diferentes tabelas), e permite mapeamentos de entidades que são representadas
+ por Maps ou XML no nÃvel Java. Nestes casos, você deve especificar um nome
+ arbitrário explicitamente para a entidade. Veja <xref linkend="persistent-classes-dynamicmodels"/>
+ e <xref linkend="xml"/> para maiores informações.
+ </para>
+ </callout>
+ <callout arearefs="class18">
+ <para>
+ <literal>check</literal> (opcional): Uma expressão SQL utilizada para gerar uma
+ constraint de <emphasis>verificação</emphasis> de múltiplas linhas para a geração
+ automática do esquema.
+ </para>
+ </callout>
+ <callout arearefs="class19">
+ <para>
+ <literal>rowid</literal> (opcional): O Hibernate poder usar as assim chamadas
+ ROWIDs em bancos de dados que a suportam. Por exemplo, no Oracle, o Hibernate
+ pode utilizar a coluna extra rowid para atualizações mais rápidas se você
+ configurar esta opção para <literal>rowid</literal>. Um ROWID é uma implementação
+ que representa de maneira detalhada a localização fÃsica de uma determinada
+ tupla armazenado.
+ </para>
+ </callout>
+ <callout arearefs="class20">
+ <para>
+ <literal>subselect</literal> (optional): Maps an immutable and read-only entity
+ to a database subselect. Useful if you want to have a view instead of a base table,
+ but don't. See below for more information.
+ <literal>subselect</literal> (opcional): Mapeia uma entidade imutavel e somente
+ de leitura para um subconjunto do banco de dados. Ãtil se você quiser ter uma
+ view em vez de uma tabela. Veja abaixo para mais informações.
+ </para>
+ </callout>
+ <callout arearefs="class21">
+ <para>
+ <literal>abstract</literal> (opcional): Utilizada para marcar superclasses
+ abstratas em hierarquias <literal><union-subclass></literal>.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ à perfeitamente aceitável para uma classe persitente nomeada ser uma interface. Você deverá
+ então declarar as classes implementadas desta interface utilizando o elemento
+ <literal><subclass></literal>. Você pode persistir qualquer classe de aninhada
+ <emphasis>estatica</emphasis>. Você deverá especificar o nome da classe usando a forma
+ padrão, por exemplo: <literal>eg.Foo$Bar</literal>.
+ </para>
+
+ <para>
+ Classes imutáveis, <literal>mutable="false"</literal>, não podem ser modificadas ou excluÃdas
+ pela aplicação. Isso permite ao Hibernate fazer alguns aperfeiçoamentos de performance.
+ </para>
+
+ <para>
+ O atributo opcional <literal>proxy</literal> habilita a inicialização tardia das
+ instâncias persistentes da classe. O Hibernate irá retornar CGLIB proxies como implementado
+ na interface nomeada. O objeto persistente atual será carregado quando um método do proxy
+ for invocado. Veja "Proxies para Inicialização Lazy" abaixo.
+ </para>
+
+ <para>Polimorfismo <emphasis>implÃcito</emphasis> significa que instâncias de uma classe
+ serão retornada por uma query que dá nome a qualquer superclasse ou interface implementada,
+ ou a classe e as instancias de qualquer subclasse da classe será retornada por umq query
+ que nomeia a classe por si. Polimorfismo <emphasis>explÃcito</emphasis> significa que
+ instancias da classe serão retornadas apenas por queries que explicitamente nomeiam a
+ classe e que queries que nomeiam as classes irão retornar apenas instancias de subclasses
+ mapeadas dentro da declaração <literal><class></literal> como uma
+ <literal><subclass></literal> ou <literal><joined-subclass></literal>.
+ Para a maioria dos casos, o valor default <literal>polymorphism="implicit"</literal>,
+ é apropriado. Polimorfismo explicito é útil quando duas classes distintas estão mapeadas
+ para a mesma tabela (isso permite um classe "peso leve" que contem um subconjunto
+ de colunas da tabela).
+ </para>
+
+ <para>
+ O atributo <literal>persister</literal> deixa você customizar a estratégia de persistência
+ utilizada para a classe. Você pode, por exemplo, especificar sua prórpia subclasse do
+ <literal>org.hibernate.persister.EntityPersister</literal> ou você pode criar
+ uma implementação completamente nova da interface
+ <literal>org.hibernate.persister.ClassPersister</literal> que implementa a persistência
+ através de, por exemplo, chamadas a stored procedeures, serialização de arquivos flat ou
+ LDAP. Veja <literal>org.hibernate.test.CustomPersister</literal> para um exemplo
+ simples (de "persistencia" para uma <literal>Hashtable</literal>).
+ </para>
+
+ <para>
+ Observe que as configurações <literal>dynamic-update</literal> e
+ <literal>dynamic-insert</literal> não sao herdadas pelas subclasses e assim podem tambem
+ ser especificadas em elementos <literal><subclass></literal> or
+ <literal><joined-subclass></literal>. Estas configurações podem incrementar a
+ performance em alguns casos, mas pode realmente diminuir a performance em outras.
+ Use-as de forma bastante criteriosa.
+ </para>
+
+ <para>
+ O uso de <literal>select-before-update</literal> geralmente irá diminuir a performance. Ela é
+ muito útil para prevenir que uma trigger de atualização no banco de dados seja ativada
+ desnecessariamente, se você reconectar um nó de uma instancia desconectada em uma
+ <literal>Session</literal>.
+ </para>
+
+ <para>
+ Se você ativar <literal>dynamic-update</literal>, você terá de escolher
+ a estratégia de bloqueio otimista:
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>version</literal> verifica a versão e a hora das colunas
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>all</literal> cverifica todas as colunas
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>dirty</literal> verifica as colunas modificadas, permitindo
+ alguns updates concorrentes
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>none</literal> não utiliza o bloqueio otimista
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+ Nós <emphasis>recomendamos</emphasis> com muita enfase que você utilize a
+ versão e a hora das colunas para o bloqueio otimista com o Hibernate.
+ Esta é a melhor estratégia com respeito a performance e é a única estratégia
+ que trata corretamente as modificações efetuadas em instancias desconectadas
+ (por exemplo, quando <literal>Session.merge()</literal> é utilizado).
+
+ </para>
+
+ <para>
+ Não ha diferença entre uma view e uma tabela para o mapeamento do Hibernate, e como
+ esperado isto é transparente no nÃvel do banco de dados (observe que alguns bancos de
+ dados não suportam views apropriadamente, especialmente com updates). Algumas vezes,
+ você quer utilizar uma view, ma snão pode cria-la no banco de dados (por exemplo,
+ com um esquema legado). Neste caso, você pode mapear uma entidade imutável e de
+ somente leitura, para uma dada expressão SQL, que representa um subselect:
+ </para>
+
+ <programlisting><![CDATA[<class name="Summary">
+ <subselect>
+ select item.name, max(bid.amount), count(*)
+ from item
+ join bid on bid.item_id = item.id
+ group by item.name
+ </subselect>
+ <synchronize table="item"/>
+ <synchronize table="bid"/>
+ <id name="name"/>
+ ...
+</class>]]></programlisting>
+
+ <para>
+ Declare as tabelas para sincronizar com esta entidade, garantindo que o auto-flush
+ ocorra corretamente, e que as queries para esta entidade derivada não retornem dados
+ desatualizados. O <literal><subselect></literal> está disponÃvel tanto como um
+ atributo como um elemento mapeado nested.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-id" revision="4">
+ <title>id</title>
+
+ <para>
+ Classes mapeadas <emphasis>precisam</emphasis> declarar a coluna de chave primaria da
+ tabela do banco de dados. Muitas classes irão tambem ter uma propriedade ao estilo
+ Java-Beans declarando o identificador unico de uma instancia. O elemento
+ <literal><id></literal> define o mapeamento desta propriedade para a chave primária.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="id1" coords="2 70"/>
+ <area id="id2" coords="3 70" />
+ <area id="id3" coords="4 70"/>
+ <area id="id4" coords="5 70" />
+ <area id="id5" coords="6 70" />
+ </areaspec>
+ <programlisting><![CDATA[<id
+ name="propertyName"
+ type="typename"
+ column="column_name"
+ unsaved-value="null|any|none|undefined|id_value"
+ access="field|property|ClassName">
+ node="element-name|@attribute-name|element/@attribute|."
+
+ <generator class="generatorClass"/>
+</id>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="id1">
+ <para>
+ <literal>name</literal> (opcional): O nome do identificador.
+ </para>
+ </callout>
+ <callout arearefs="id2">
+ <para>
+ <literal>type</literal> (opcional): Um nome que indica o tipo no Hibernate.
+ </para>
+ </callout>
+ <callout arearefs="id3">
+ <para>
+ <literal>column</literal> (opcional â default para o a propridade name): O
+ nome coluna chave primaria.
+ </para>
+ </callout>
+ <callout arearefs="id4">
+ <para>
+ <literal>unsaved-value</literal> (opcional - default para um valor "sensÃvel"):
+ Uma propriedade de identificação que indica que a instancia foi novamente
+ instanciada (unsaved), diferenciando de instancias desconectadas que foram
+ salvas ou carregadas em uma sessão anterior.
+ </para>
+ </callout>
+ <callout arearefs="id5">
+ <para>
+ <literal>access</literal> (opcional - valor default <literal>property</literal>): A
+ estratégia que o Hiberante deve utilizar para acessar o valor da propriedade
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Se o atributo <literal>name</literal> não for declarado, assume-se que a classe não tem
+ a propriedade de identificação.
+ </para>
+
+ <para>
+ O atributo <literal>unsaved-value</literal> não é mais necessário no Hibernate 3.
+ </para>
+
+ <para>
+ Há declaração alternativa <literal><composite-id></literal> permite o acesso a
+ dados legados com chaves compostas. Nós desencorajamos fortemente o seu uso por
+ qualquer pessoa.
+ </para>
+
+ <sect3 id="mapping-declaration-id-generator" revision="2">
+ <title>Generator</title>
+
+ <para>
+ O elemento filho opcional <literal><generator></literal> nomeia uma classe Java
+ usada para gerar identificadores unicos para instancias de uma classe persistente.
+ Se algum parâmetro é requerido para configurar ou inicializar a instancia geradora,
+ eles são passados utilizando o elemento <literal><param></literal>.
+ </para>
+
+ <programlisting><![CDATA[<id name="id" type="long" column="cat_id">
+ <generator class="org.hibernate.id.TableHiLoGenerator">
+ <param name="table">uid_table</param>
+ <param name="column">next_hi_value_column</param>
+ </generator>
+</id>]]></programlisting>
+
+ <para>
+ Todos os generators implementam a interface <literal>org.hibernate.id.IdentifierGenerator</literal>.
+ Esta é uma interface bem simples; algumas aplicações podem prover sua própria implementação
+ esepecializada. Entretanto, o Hibernate disponibiliza um conjunto de implementações internamente.
+ Há nomes de atalhos para estes generators próprios:
+ <variablelist>
+ <varlistentry>
+ <term><literal>increment</literal></term>
+ <listitem>
+ <para>
+ gera identificadores dos tipos <literal>long</literal>, <literal>short</literal> ou
+ <literal>int</literal> que são unicos apenas quando nenhum outro processo está
+ inserindo dados na mesma tabela. <emphasis>Não utilize em ambientes
+ de cluster.</emphasis>
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>identity</literal></term>
+ <listitem>
+ <para>
+ suporta colunas de identidade em DB2, MySQL, MS SQL Server, Sybase e
+ HypersonicSQL. O identificador retornado é do tipo <literal>long</literal>,
+ <literal>short</literal> ou <literal>int</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>sequence</literal></term>
+ <listitem>
+ <para>
+ utiliza uma sequence em DB2, PostgreSQL, Oracle, SAP DB, McKoi ou um
+ generator no Interbase. O identificador de retorno é do tipo <literal>
+ long</literal>, <literal>short</literal> ou <literal>int</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>hilo</literal></term>
+ <listitem>
+ <para id="mapping-declaration-id-hilodescription" revision="1">
+ utiliza um algoritmo hi/lo para gerar de forma eficiente identificadores do tipo
+ <literal>long</literal>, <literal>short</literal> ou <literal>int</literal>,
+ a partir de uma tabela e coluna fornecida (por default
+ <literal>hibernate_unique_key</literal> e <literal>next_hi</literal>)
+ como fonte para os valores hi. O algoritmo hi/lo gera identificadores que são
+ únicos apenas para um banco de dados particular.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>seqhilo</literal></term>
+ <listitem>
+ <para>
+ utiliza um algoritmo hi/lo para gerar de forma eficinete identificadores do tipo
+ <literal>long</literal>, <literal>short</literal> ou <literal>int</literal>,
+ a partir de uma sequence de banco de dados fornecida.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>uuid</literal></term>
+ <listitem>
+ <para>
+ utiliza um algortimo UUID de 128-bits para gerar identificadores do
+ tipo string, unicos em uma rede(o endereço IP é utilizado). O UUID é
+ codificado como um string de digitos hexadecimais de tamanho 32.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>guid</literal></term>
+ <listitem>
+ <para>
+ utiliza um string GUID gerado pelo banco de dados no MS SQL Server
+ e MySQL.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>native</literal></term>
+ <listitem>
+ <para>
+ seleciona entre <literal>identity</literal>, <literal>sequence</literal>
+ ou <literal>hilo</literal> dependendo das capacidades do banco de dados
+ utilizado.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>assigned</literal></term>
+ <listitem>
+ <para>
+ deixa a aplicação definir um identificador para o objeto antes que o
+ <literal>save()</literal> seja chamado. Esta é a estratégia default
+ se nenhum elemento <literal><generator></literal> é especificado.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>select</literal></term>
+ <listitem>
+ <para>
+ retorna a chave primaria recuperada por uma trigger do banco de
+ dados, selecionado uma linha pela chave única e recuperando o valor
+ da chave primária.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>foreign</literal></term>
+ <listitem>
+ <para>
+ utiliza o identificador de um outro objeto associado. Normalmente utilizado
+ em conjunto com uma associaçõa de chave primária do tipo
+ <literal><one-to-one></literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>sequence-identity</literal></term>
+ <listitem>
+ <para>
+ a specialized sequence generation strategy which utilizes a
+ database sequence for the actual value generation, but combines
+ this with JDBC3 getGeneratedKeys to actually return the generated
+ identifier value as part of the insert statement execution. This
+ strategy is only known to be supported on Oracle 10g drivers
+ targetted for JDK 1.4. Note comments on these insert statements
+ are disabled due to a bug in the Oracle drivers.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ </para>
+ </sect3>
+
+ <sect3 id="mapping-declaration-id-hilo" revision="1">
+ <title>Algoritmo Hi/lo</title>
+ <para>
+ Os geradores <literal>hilo</literal> e <literal>seqhilo</literal> fornecem duas
+ implementações alternativas do algoritmo hi/lo, uma solução preferencial para a geração
+ de identificadores. A primeira implementação requer uma tabela especial do banco de
+ dados para manter o proximo valor "hi" disponÃvel. A segunda utiliza uma seqüência
+ do estilo Oracle (quando suportado).
+ </para>
+
+ <programlisting><![CDATA[<id name="id" type="long" column="cat_id">
+ <generator class="hilo">
+ <param name="table">hi_value</param>
+ <param name="column">next_value</param>
+ <param name="max_lo">100</param>
+ </generator>
+</id>]]></programlisting>
+
+ <programlisting><![CDATA[<id name="id" type="long" column="cat_id">
+ <generator class="seqhilo">
+ <param name="sequence">hi_value</param>
+ <param name="max_lo">100</param>
+ </generator>
+</id>]]></programlisting>
+
+ <para>
+ Infelizemente, voce não pode utilizar <literal>hilo</literal> quando estiver
+ fornecendo sia propria <literal>Connection</literal> para o Hibernate. Quando o
+ Hibernate está usando um datasource do servidor de aplicações para obter conexões
+ suportadas com JTA, você precisa configurar adequadamente o
+ <literal>hibernate.transaction.manager_lookup_class</literal>.
+ </para>
+ </sect3>
+
+ <sect3 id="mapping-declaration-id-uuid">
+ <title>UUID algorithm</title>
+ <para>
+ O UUID contem: o endereço IP, hora de inicio da JVM (com precisão de um quarto
+ de segundo), a hora do sistema e um valor contador (unico dentro da JVM).
+ Não é possivel obter o endereço MAC ou um endereço de memória do código Java,
+ assim este é o melhor que pode ser feito sem utilizar JNI.
+ </para>
+ </sect3>
+
+ <sect3 id="mapping-declaration-id-sequences">
+ <title>Colunas de identidade e sequencias</title>
+ <para>
+ Para bancos de dados que suportam colunas de identidade (DB2, MySQL, Sybase,
+ MS SQL), você pode utilizar uma geração de chave <literal>identity</literal>.
+ Para bancos de dados que suportam sequencias (DB2, Oracle, PostgreSQL, Interbase,
+ McKoi, SAP DB) voce pode utilizar a geração de chaves no estilo
+ <literal>sequence</literal>. As duas estratégias requerem duas consultas SQL
+ para inserir um novo objeto.
+ </para>
+
+ <programlisting><![CDATA[<id name="id" type="long" column="person_id">
+ <generator class="sequence">
+ <param name="sequence">person_id_sequence</param>
+ </generator>
+</id>]]></programlisting>
+
+ <programlisting><![CDATA[<id name="id" type="long" column="person_id" unsaved-value="0">
+ <generator class="identity"/>
+</id>]]></programlisting>
+
+ <para>
+ Para desenvolvimento multi-plataforma, a estratégia <literal>native</literal>
+ irá escolher entre as estratégias i <literal>identity</literal>,
+ <literal>sequence</literal> e <literal>hilo</literal>, dependendo das
+ capacidades do banco de dados utilizado.
+ </para>
+ </sect3>
+
+ <sect3 id="mapping-declaration-id-assigned">
+ <title>Identificadores especificados</title>
+ <para>
+ Se você quer que a aplicação especifique os identificadores
+ (em vez do Hibernate gerá-los) você deve utilizar o gerador
+ <literal>assigned</literal>. Este gerador especial irá utilizar o valor
+ do identificador especificado para a propriedade de identificação do objeto.
+ Este gerador é usado quando a chave primaria é a chave natural em vez de uma
+ surrogate key. Este é o comportamento padrão se você não especificar
+ um elemento <literal><generator></literal>.
+ </para>
+
+ <para>
+ Escolher o gerador <literal>assigned</literal> faz com que o Hibernate
+ utilize <literal>unsaved-value="undefined"</literal>, forçando o Hibernate
+ ir até o banco de dados para determinar se uma instância está transiente ou
+ desasociada, a menos que haja uma versão ou uma propriedade timestamp,
+ ou você pode definir <literal>Interceptor.isUnsaved()</literal>.
+ </para>
+ </sect3>
+
+ <sect3 id="mapping-declaration-id-select">
+ <title>Chaves primárias geradas por triggers</title>
+ <para>
+ Apenas para sistemas legados (o Hibernate nao gera DDL com triggers).
+ </para>
+
+ <programlisting><![CDATA[<id name="id" type="long" column="person_id">
+ <generator class="select">
+ <param name="key">socialSecurityNumber</param>
+ </generator>
+</id>]]></programlisting>
+
+ <para>
+ No exemplo acima, há uma única propriedade com valor nomeada
+ <literal>socialSecurityNumber</literal> definida pela classe,
+ uma chave natural, e uma surrogate key nomeada
+ <literal>person_id</literal> cujo valor é gerado pro uma trigger.
+ </para>
+
+ </sect3>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-compositeid" revision="3">
+ <title>composite-id</title>
+
+ <programlisting><![CDATA[<composite-id
+ name="propertyName"
+ class="ClassName"
+ mapped="true|false"
+ access="field|property|ClassName">
+ node="element-name|."
+
+ <key-property name="propertyName" type="typename" column="column_name"/>
+ <key-many-to-one name="propertyName class="ClassName" column="column_name"/>
+ ......
+</composite-id>]]></programlisting>
+
+ <para>
+ Para tabelas com uma chave composta, você pode mapear múltiplas propriedades
+ da classe como propriedades de identificação. O elemento
+ <literal><composite-id></literal> aceita o mapeamento da propriedade
+ <literal><key-property></literal> e mapeamentos
+ <literal><key-many-to-one></literal>como elements filhos.
+ </para>
+
+ <programlisting><![CDATA[<composite-id>
+ <key-property name="medicareNumber"/>
+ <key-property name="dependent"/>
+</composite-id>]]></programlisting>
+
+ <para>
+ Sua classe persistente <emphasis>precisa</emphasis> sobre escrever
+ <literal>equals()</literal> e <literal>hashCode()</literal> para implementar
+ identificadores compostos igualmente. E precisa também implementar
+ <literal>Serializable</literal>.
+ </para>
+
+ <para>
+ Infelizmente, esta solução para um identificador composto significa que um objeto
+ persistente é seu próprio identificador. Não há outro "handle" que o próprio objeto.
+ Você mesmo precisa instanciar uma instância de outra classe persistente e preencher
+ suas propriedades de identificação antes que você possa dar um <literal>load()</literal>
+ para o estado persistente associado com uma chave composta. Nos chamamos esta
+ solução de identificador composto <emphasis>embedded</emphasis> e não aconselhamos
+ para aplicações sérias.
+ </para>
+
+ <para>
+ Uma segunda solução é o que podemos chamar de identificador composto
+ <emphasis>mapped</emphasis> quando a propriedades de identificação nomeadas dentro do
+ elemento <literal><composite-id></literal> estão duplicadas tando na classe
+ persistente como em uma classe de identificação separada.
+ </para>
+
+ <programlisting><![CDATA[<composite-id class="MedicareId" mapped="true">
+ <key-property name="medicareNumber"/>
+ <key-property name="dependent"/>
+</composite-id>]]></programlisting>
+
+ <para>
+ No exemplo, ambas as classes de identificação compostas, <literal>MedicareId</literal>,
+ e a própria classe entidade tem propriedades nomeadas <literal>medicareNumber</literal>
+ e <literal>dependent</literal>. A classe identificadora precisa sobrepor
+ <literal>equals()</literal> e <literal>hashCode()</literal> e implementar
+ <literal>Serializable</literal>. A desvantagem desta solução é obvia â
+ duplicação de código.
+ </para>
+
+ <para>
+ Os seguintes atributos ão utilizados para especificar o mapeamento de
+ um identificador composto:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>mapped</literal> mapped (opcional, valor default <literal>false
+ </literal>): indica que um identificar composto mapeado é usado, e que as
+ propriedades de mapeamento contidas refere-se tanto a classe entidade e
+ a classe de identificação composta.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>class</literal> (opcional, mas requerida para um identificar composto
+ mapeado): A classe usada como um identificador composto.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Nós iremos descrever uma terceira e as vezes mais conveniente solução, onde o
+ identificador composto é implementado como uma classe componente na
+ <xref linkend="components-compositeid"/>. Os atributos descritos abaixo aplicam-se
+ apenas para esta solução:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>name</literal> (opcional, requerida para esta solução): Uma
+ propriedade do tipo componente que armazena o identificador composto
+ (veja capÃtulo 9)
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>access</literal> (opcional - valor default <literal>property</literal>):
+ A estartégia Hibernate que deve ser utilizada para acessar o valor da propriedade.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>class</literal> (opcional - valor default para o tipo de propriedade
+ determiando por reflexão) : A classe componente utilizada como um identificador
+ composto (veja a próxima sessão).
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Esta terceira solução, um <emphasis>componente de identificação</emphasis>, é o que nós
+ recomendamos para a maioria das aplicações.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-discriminator" revision="3">
+ <title>discriminator</title>
+
+ <para>
+ O elemento <literal><discriminator></literal> é necessário para persistência
+ polimórfica utilizando a estratégia de mapeamento table-per-class-hierarchy e declara
+ uma coluna discriminadora da tabela. A coluna discriminadora contem valores de marcação
+ que dizem a camada de persistência qual subclasse instanciar para uma linha particular.
+ Um restrito conjunto de tipos que podem ser utilizados: <literal>string</literal>,
+ <literal>character</literal>, <literal>integer</literal>, <literal>byte</literal>,
+ <literal>short</literal>, <literal>boolean</literal>,
+ <literal>yes_no</literal>, <literal>true_false</literal>.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="discriminator1" coords="2 60"/>
+ <area id="discriminator2" coords="3 60" />
+ <area id="discriminator3" coords="4 60" />
+ <area id="discriminator4" coords="5 60" />
+ <area id="discriminator5" coords="6 60" />
+ </areaspec>
+ <programlisting><![CDATA[<discriminator
+ column="discriminator_column"
+ type="discriminator_type"
+ force="true|false"
+ insert="true|false"
+ formula="arbitrary sql expression"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="discriminator1">
+ <para>
+ <literal>column</literal> (opcional - valor default <literal>class</literal>)
+ o nome da coluna discriminadora
+ </para>
+ </callout>
+ <callout arearefs="discriminator2">
+ <para>
+ <literal>type</literal> (opcional - valor default <literal>string</literal>)
+ o nome que indica o tipo Hibernate
+ </para>
+ </callout>
+ <callout arearefs="discriminator3">
+ <para>
+ <literal>force</literal> (opcional - valor default <literal>false</literal>)
+ "força" o Hibernate a especificar valores discriminadores permitidos mesmo
+ quando recuperando todas as instancias da classe root.
+ </para>
+ </callout>
+ <callout arearefs="discriminator4">
+ <para>
+ <literal>insert</literal> (opcional - valor default para <literal>true</literal>)
+ sete isto para <literal>false</literal> se sua coluna discriminadora é também
+ parte do identificador composto mapeado. (Diz ao Hibernate para não incluir a
+ coluna em comandos SQL <literal>INSERT</literal>s).
+ </para>
+ </callout>
+ <callout arearefs="discriminator5">
+ <para>
+ <literal>formula</literal> (opcional) uma expressão SQL arbitraria que é e
+ xecutada quando um tipo tem que ser avaliado. Permite discriminação baseada
+ em conteúdo.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Valores atuais de uma coluna discriminada são especificados pelo atributo
+ <literal>discriminator-value</literal> da <literal><class></literal>
+ e elementos da <literal><subclass></literal>.
+ </para>
+
+ <para>
+ O atributo <literal>force</literal> é util (apenas) em tabelas contendo linhas com
+ valores discriminadores "extras" que não estão mapeados para uma classe persistente.
+ Este não é geralmente o caso.
+ </para>
+
+ <para>
+ Usando o atributo <literal>formula</literal> voce pode declarar uma expressão SQL
+ arbitrária que sera utilizada para avaliar o tipo de uma linha :
+ </para>
+
+ <programlisting><![CDATA[<discriminator
+ formula="case when CLASS_TYPE in ('a', 'b', 'c') then 0 else 1 end"
+ type="integer"/>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-version" revision="4">
+ <title>version (optional)</title>
+
+ <para>
+ O elemento <literal><version></literal> é opcional e indica que a tabela
+ possui dados versionados. Isto é particularmente útil se você planeja utilizar
+ <emphasis>transações longas</emphasis> (veja abaixo):
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="version1" coords="2 70"/>
+ <area id="version2" coords="3 70"/>
+ <area id="version3" coords="4 70"/>
+ <area id="version4" coords="5 70"/>
+ <area id="version5" coords="6 70"/>
+ <area id="version6" coords="7 70"/>
+ <area id="version7" coords="8 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<version
+ column="version_column"
+ name="propertyName"
+ type="typename"
+ access="field|property|ClassName"
+ unsaved-value="null|negative|undefined"
+ generated="never|always"
+ insert="true|false"
+ node="element-name|@attribute-name|element/@attribute|."
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="version1">
+ <para>
+ <literal>column</literal> (opcional - default a a propriedade name): O nome da
+ coluna mantendo o numero da versão
+ </para>
+ </callout>
+ <callout arearefs="version2">
+ <para>
+ <literal>name</literal>: O nome da propriedade da classe persistente.
+ </para>
+ </callout>
+ <callout arearefs="version3">
+ <para>
+ <literal>type</literal> (opcional - valor default para <literal>integer</literal>):
+ O tipo do numero da versão
+ </para>
+ </callout>
+ <callout arearefs="version4">
+ <para>
+ <literal>access</literal> (opcional - valor default <literal>property</literal>):
+ A estratégia Hibernate que deve ser usada para acessar o valor da propriedade.
+ </para>
+ </callout>
+ <callout arearefs="version5">
+ <para>
+ <literal>unsaved-value</literal> (opcional â valor default para <literal>undefined
+ </literal>): Um valor para a propriedade versão que indica que uma instancia é
+ uma nova instanciada (unsaved), distinguindo de instancias desconectadas que foram
+ salvas ou carregadas em sessões anteriores. ((<literal>undefined</literal> especifica
+ que o valor da propriedade de identificação deve ser utilizado).
+ </para>
+ </callout>
+ <callout arearefs="version6">
+ <para>
+ <literal>generated</literal> (optional - defaults to <literal>never</literal>):
+ Specifies that this version property value is actually generated by the database.
+ See the discussion of <xref linkend="mapping-generated">generated properties</xref>.
+ <literal>generated</literal> (opcional - valor default <literal>never</literal>):
+ Especifica que valor para a propriedade versão é na verdade gerado pelo banco de
+ dados. Veja a discussão da Seção
+ <xref linkend="mapping-generated">generated properties</xref>.
+ </para>
+ </callout>
+ <callout arearefs="version7">
+ <para>
+ <literal>insert</literal> (opcional - valor default para <literal>true</literal>):
+ Especifica se a coluna de versão deve ser incluÃda no comando SQL de insert.
+ Pode ser configurado como <literal>false</literal> se a coluna do banco de dados
+ está definida com um valor default de <literal>0</literal>.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Números de versão podem ser dos tipos Hibernate <literal>long</literal>,
+ <literal>integer</literal>, <literal>short</literal>, <literal>timestamp</literal> ou
+ <literal>calendar</literal>.
+ </para>
+
+ <para>
+ A versão de uma propriedade timestamp nunca deve ser nula para uma instancia
+ desconectada, assim o Hibernate irá identificar qualquer instância com uma versão
+ nula ou timestamp como transiente, não importando qual estratégia para foi
+ especificada para <literal>unsaved-value</literal>. <emphasis>Declarando uma versão
+ nula ou a propriedade timestamp é um caminho fácil para tratar problemas com
+ reconexões transitivas no Hibernate, especialmente úteis para pessoas utilizando
+ identificadores assinaldados ou chaves compostas!</emphasis>
+ </para>
+ </sect2>
+
+ <sect2 id="mapping-declaration-timestamp" revision="4" >
+ <title>timestamp (optional)</title>
+
+ <para>
+ O elemento opcional <literal><timestamp></literal> indica que uma tabela contém
+ dados timestamped. Isso tem por objetivo dar uma alternativa para versionamento. Timestamps
+ são por natureza uma implementação menos segura do locking otimista. Entretanto, algumas
+ vezes a aplicação pode usar timestamps em outros caminhos.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="timestamp1" coords="2 70"/>
+ <area id="timestamp2" coords="3 70" />
+ <area id="timestamp3" coords="4 70" />
+ <area id="timestamp4" coords="5 70" />
+ <area id="timestamp5" coords="6 70" />
+ <area id="timestamp6" coords="7 70" />
+ </areaspec>
+ <programlisting><![CDATA[<timestamp
+ column="timestamp_column"
+ name="propertyName"
+ access="field|property|ClassName"
+ unsaved-value="null|undefined"
+ source="vm|db"
+ generated="never|always"
+ node="element-name|@attribute-name|element/@attribute|."
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="timestamp1">
+ <para>
+ <literal>column</literal> (opcional - valor default para a propriedade name):
+ O nome da coluna que mantem o timestamp.
+ </para>
+ </callout>
+ <callout arearefs="timestamp2">
+ <para>
+ <literal>name</literal>: O nome da propriedade no estilo JavaBeans do
+ tipo <literal>Date</literal> ou <literal>Timestamp</literal> da classe
+ persistente Java.
+ </para>
+ </callout>
+ <callout arearefs="timestamp3">
+ <para>
+ <literal>access</literal> (opcional - valor default para <literal>property</literal>):
+ A estretagia Hibernate que deve ser utilizada para acessar o valor da propriedade.
+ </para>
+ </callout>
+ <callout arearefs="timestamp4">
+ <para>
+ <literal>unsaved-value</literal> (opcional - valor default <literal>null</literal>):
+ Uma propriedade para a versão de que indica que uma instância é uma nova instanciada
+ (unsaved), distinguindo-a de instancias desconectadas que foram salvas ou carregadas
+ em sessões previas. (<literal>undefined</literal> especifica que um valor de
+ propriedade de identificação deve ser utilizado)
+ </para>
+ </callout>
+ <callout arearefs="timestamp5">
+ <para>
+ <literal>source</literal> (opcional - valor default para <literal>vm</literal>):
+ De onde o Hibernate deve recuperar o valor timestamp? Do banco de dados ou da JVM
+ corrente? Timestamps baseados em banco de dados levam a um overhead porque o
+ Hibernate precisa acessar o banco de dados para determinar o "próximo valor", mas é
+ mais seguro para uso em ambientes de "cluster". Observe também, que nem todos
+ <literal>Dialect</literal>s suportam a recuperação do timestamp corrente do banco
+ de dados, enquando outros podem não ser seguros para utilização em bloqueios
+ pela falta de precisão (Oracle 8 por exemplo)
+ </para>
+ </callout>
+ <callout arearefs="timestamp6">
+ <para>
+ <literal>generated</literal> (opcional - valor default <literal>never</literal>):
+ Especifica que o valor da propriedade timestamp é gerado pelo banco de dados.
+ Veja a discussão <xref linkend="mapping-generated">generated properties</xref>.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Observe que <literal><timestamp></literal> é equivalente a
+ <literal><version type="timestamp"></literal>. E
+ <literal><timestamp source="db"></literal> é equivalente a
+ <literal><version type="dbtimestamp"></literal>.
+ </para>
+ </sect2>
+
+
+ <sect2 id="mapping-declaration-property" revision="4">
+ <title>property</title>
+
+ <para>
+ O elemento <literal><property></literal> declara uma propriedade
+ persistente de uma classe, no estilo JavaBean.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="property1" coords="2 70"/>
+ <area id="property2" coords="3 70"/>
+ <area id="property3" coords="4 70"/>
+ <areaset id="property4-5" coords="">
+ <area id="property4" coords='5 70'/>
+ <area id="property5" coords='6 70'/>
+ </areaset>
+ <area id="property6" coords="7 70"/>
+ <area id="property7" coords="8 70"/>
+ <area id="property8" coords="9 70"/>
+ <area id="property9" coords="10 70"/>
+ <area id="property10" coords="11 70"/>
+ <area id="property11" coords="12 70"/>
+ <area id="property12" coords="13 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<property
+ name="propertyName"
+ column="column_name"
+ type="typename"
+ update="true|false"
+ insert="true|false"
+ formula="arbitrary SQL expression"
+ access="field|property|ClassName"
+ lazy="true|false"
+ unique="true|false"
+ not-null="true|false"
+ optimistic-lock="true|false"
+ generated="never|insert|always"
+ node="element-name|@attribute-name|element/@attribute|."
+ index="index_name"
+ unique_key="unique_key_id"
+ length="L"
+ precision="P"
+ scale="S"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="property1">
+ <para>
+ <literal>name</literal>: o nome da propriedade, iniciando com letra minúscula.
+ </para>
+ </callout>
+ <callout arearefs="property2">
+ <para>
+ <literal>column</literal> (opcional - default para a propriedade name): o nome
+ da coluna mapeada do banco de dados, Isto pode também ser especificado pelo(s)
+ elemento(s) <literal><column></literal> aninhados.
+
+ </para>
+ </callout>
+ <callout arearefs="property3">
+ <para>
+ <literal>type</literal> (opcional): um nome que indica o tipo Hibernate.
+ </para>
+ </callout>
+ <callout arearefs="property4-5">
+ <para>
+ <literal>update, insert</literal> (opcional - valor default <literal>true</literal>):
+ especifica que as colunas mapeadas devem ser incluidas nas instruções SQL de
+ <literal>UPDATE</literal> e/ou <literal>INSERT</literal> . Setar ambas para to
+ <literal>false</literal> permite uma propridade "derivada" pura cujo valor é
+ inicializado de outra propriedade que mapeie a mesma coluna(s) ou por uma trigger
+ ou outra aplicação.
+ </para>
+ </callout>
+ <callout arearefs="property6">
+ <para>
+ <literal>formula</literal> (opcional): uma expressão SQL que definie o valor para
+ uma propriedade <emphasis>calculada</emphasis>. Propriedades calculadas nao tem
+ uma coluna de mapeamento para elas.
+ </para>
+ </callout>
+ <callout arearefs="property7">
+ <para>
+ <literal>access</literal> (opcional â valor default <literal>property</literal>):
+ A estratégia que o Hibernate deve utilizar para acessar o valor da propriedade
+ </para>
+ </callout>
+ <callout arearefs="property8">
+ <para>
+ <literal>lazy</literal> (opcional - valor default para <literal>false</literal>):
+ Especifica que esta propriedade deve ser trazida de forma "lazy" quando a
+ instancia da variável é acessada pela primeira vez (requer instrumentação
+ bytecode em tempo de criação).
+ </para>
+ </callout>
+ <callout arearefs="property9">
+ <para>
+ <literal>unique</literal> (opcional): Habilita a geração de DDL de uma
+ unica constraint para as colunas. Assim, permite que isto seja o
+ alvo de uma <literal>property-ref</literal>.
+ </para>
+ </callout>
+ <callout arearefs="property10">
+ <para>
+ <literal>not-null</literal> (opcional): Habilita a geração de DDL de uma
+ constraint de nulidade para as colunas.
+ </para>
+ </callout>
+ <callout arearefs="property11">
+ <para>
+ <literal>optimistic-lock</literal> (opcional - valor default <literal>true</literal>):
+ Especifica se mudanças para esta propriedade requerem ou não bloqueio otimista.
+ Em outras palavras, determina se um incremento de versão deve ocorrer quando
+ esta propriedade está suja.
+ </para>
+ </callout>
+ <callout arearefs="property12">
+ <para>
+ <literal>generated</literal> (opcional - valor default <literal>never</literal>):
+ Especifica que o valor da propriedade é na verdade gerado pelo banco de dados.
+ Veja a discussão da seção
+ <xref linkend="mapping-generated">generated properties</xref>.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ <emphasis>typename</emphasis> pode ser:
+ </para>
+
+ <orderedlist spacing="compact">
+ <listitem>
+ <para>
+ The name of a Hibernate basic type (eg. <literal>integer, string, character,
+ date, timestamp, float, binary, serializable, object, blob</literal>).
+ O nome do tipo basico do Hibernate (ex., <literal>integer, string, character,
+ date, timestamp, float, binary, serializable, object, blob</literal>).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ O nome da classe Java com um tipo básico default (ex. <literal>int, float,
+ char, java.lang.String, java.util.Date, java.lang.Integer, java.sql.Clob</literal>).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ O nome da classe Java serializable
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ O nome da classe de um tipo customizado (ex. <literal>com.illflow.type.MyCustomType</literal>).
+ </para>
+ </listitem>
+ </orderedlist>
+
+ <para>
+ Se você não especificar um tipo, o Hibernate ira utilizar reflexão sobre a
+ propriedade nomeada para ter uma idéia do tipo Hibernate correto. O Hibernate ira
+ tentar interpretar o nome da classe retornada, usando as regras 2, 3 e 4 nesta ordem.
+ Entretanto, isto não é sempre suficiente Em certos casos, você ainda irá necessitar
+ do atributo <literal>type</literal>. (Por exemplo, para distinguir entre
+ <literal>Hibernate.DATE</literal> ou <literal>Hibernate.TIMESTAMP</literal>,
+ ou para espcificar uma tipo ciustomizado.)
+ </para>
+
+ <para>
+ O atributo <literal>access</literal> permite voce controlar como o Hibernate irá
+ acessar a propriedade em tempo de execução. Por default, o Hibernate irá chamar os
+ métodos get/set da propriedades. Se voce especificar <literal>access="field"</literal>,
+ o Hibernate ira bipassar os metodos get/set, acessnado o campo diretamente,
+ usando reflexão. Voc epode especificar sua própria estratégia para acesso da
+ propriedade criando uma classe que implemente a interface
+ <literal>org.hibernate.property.PropertyAccessor</literal>.
+ </para>
+
+ <para>
+ Um recurso especialmente poderoso é o de propriedades derivadas. Estas propriedades
+ são por definição read-only, e o valor da propriedade é calculado em tempo de execução.
+ Você declara este calculo como uma expressão SQL, que traduz para clausula
+ <literal>SELECT</literal> de uma subquery daquery SQL que carrega a instancia:
+ </para>
+
+ <programlisting><![CDATA[
+<property name="totalPrice"
+ formula="( SELECT SUM (li.quantity*p.price) FROM LineItem li, Product p
+ WHERE li.productId = p.productId
+ AND li.customerId = customerId
+ AND li.orderNumber = orderNumber )"/>]]></programlisting>
+
+ <para>
+ Observe que você pode referenciar as entidades da própria tabela,
+ através da não declaração de um alias para uma coluna particular (
+ <literal>customerId</literal> no exemplo dado). Observe tambem que voce pode usar o
+ mapeamento de elemento aninhado <literal><formula></literal>, se você não
+ gostar de usar o atributo.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-manytoone" revision="5">
+ <title>many-to-one</title>
+
+ <para>
+ Uma associação ordinária para outra classe persistente é declarada usando o
+ elemento <literal>many-to-one</literal>. O modelo relacional é uma
+ associação many-to-one: a uma chave estrangeira de uma tabela referenciando
+ a chave primaria da tabela destino.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="manytoone1" coords="2 70"/>
+ <area id="manytoone2" coords="3 70"/>
+ <area id="manytoone3" coords="4 70"/>
+ <area id="manytoone4" coords="5 70"/>
+ <area id="manytoone5" coords="6 70"/>
+ <areaset id="manytoone6-7" coords="">
+ <area id="manytoone6" coords='7 70'/>
+ <area id="manytoone7" coords='8 70'/>
+ </areaset>
+ <area id="manytoone8" coords="9 70"/>
+ <area id="manytoone9" coords="10 70"/>
+ <area id="manytoone10" coords="11 70"/>
+ <area id="manytoone11" coords="12 70"/>
+ <area id="manytoone12" coords="13 70"/>
+ <area id="manytoone13" coords="14 70"/>
+ <area id="manytoone14" coords="15 70"/>
+ <area id="manytoone15" coords="16 70"/>
+ <area id="manytoone16" coords="17 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<many-to-one
+ name="propertyName"
+ column="column_name"
+ class="ClassName"
+ cascade="cascade_style"
+ fetch="join|select"
+ update="true|false"
+ insert="true|false"
+ property-ref="propertyNameFromAssociatedClass"
+ access="field|property|ClassName"
+ unique="true|false"
+ not-null="true|false"
+ optimistic-lock="true|false"
+ lazy="proxy|no-proxy|false"
+ not-found="ignore|exception"
+ entity-name="EntityName"
+ formula="arbitrary SQL expression"
+ node="element-name|@attribute-name|element/@attribute|."
+ embed-xml="true|false"
+ index="index_name"
+ unique_key="unique_key_id"
+ foreign-key="foreign_key_name"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="manytoone1">
+ <para>
+ <literal>name</literal>: O nome da propriedade.
+ </para>
+ </callout>
+ <callout arearefs="manytoone2">
+ <para>
+ <literal>column</literal> (opcional): O nome da coluna foreign key. Isto
+ pode também ser especificado através de elementos aninhados
+ <literal><column></literal>.
+ </para>
+ </callout>
+ <callout arearefs="manytoone3">
+ <para>
+ <literal>class</literal> (opcional â default para o tipo de propriedade
+ determinado pela reflexão). O nome da classe associada.
+ </para>
+ </callout>
+ <callout arearefs="manytoone4">
+ <para>
+ <literal>cascade</literal> (opcional): Especifica quais operações dever
+ ser em cascata do objeto pai para o objeto associado.
+ </para>
+ </callout>
+ <callout arearefs="manytoone5">
+ <para>
+ <literal>fetch</literal> (opcional - default para <literal>select</literal>):
+ Escolhe entre recuperação outer-join ou recuperação seqüencial.
+ </para>
+ </callout>
+ <callout arearefs="manytoone6-7">
+ <para>
+ <literal>update, insert</literal> (opcional - valor default <literal>true</literal>):
+ especifica que as colunas mapeadas dever ser incluidas em instruções SQL de
+ <literal>UPDATE</literal> e/ou <literal>INSERT</literal>. Setando ambas para
+ <literal>false</literal> você permite uma associação "derivada" pura cujos valores
+ são inicializados de algumas outras propriedades que mapeiam a mesma coluna ou
+ por uma trigger ou outra aplicação.
+ </para>
+ </callout>
+ <callout arearefs="manytoone8">
+ <para>
+ <literal>property-ref</literal>: (opcional) O nome da propriedade da classe associada
+ que faz a junção desta foreign key. Se não especificada, a chave primaria da
+ classe associada será utilizada.
+ </para>
+ </callout>
+ <callout arearefs="manytoone9">
+ <para>
+ <literal>access</literal> (opcional - valor default <literal>property</literal>): A
+ estrategia que o Hibernate deve utilizar para acessar o valor da propriedade.
+ </para>
+ </callout>
+ <callout arearefs="manytoone10">
+ <para>
+ <literal>unique</literal> (opcional): Habilita a geração DDL de uma constraint
+ unique para a coluna foreign-key. Alem disso, permite ser o alvo de uma
+ <literal>property-ref</literal>. Isso torna a associação multipla
+ efetivamente um para um.
+ </para>
+ </callout>
+ <callout arearefs="manytoone11">
+ <para>
+ <literal>not-null</literal> (opcional): Habilita a geração DDL de uma constraint de
+ nulidade para as foreign keys.
+ </para>
+ </callout>
+ <callout arearefs="manytoone12">
+ <para>
+ <literal>optimistic-lock</literal> (opcional - valor default <literal>true</literal>):
+ Especifica se mudanças desta propriedade requerem ou não travamento otimista.
+ Em outras palavras, determina se um incremento de versão deve ocorrer quando
+ esta propriedade está suja.
+ </para>
+ </callout>
+ <callout arearefs="manytoone13">
+ <para>
+ <literal>lazy</literal>(opcional â valor default <literal>proxy</literal>):
+ Por default, associações de ponto unico são envoltas em um proxie.
+ <literal>lazy="no-proxy"</literal> especifica que a propriedade deve ser
+ trazida de forma tardia quando a instancia da variável é acessada pela
+ primeira vez (requer instrumentação bytecode em tempo de criação)
+ <literal>lazy="false"</literal> especifica que a associação será
+ sempre recuperada fortemente.
+ </para>
+ </callout>
+ <callout arearefs="manytoone14">
+ <para>
+ <literal>not-found</literal> (opcional - valor default <literal>exception</literal>):
+ Especifica como as foreign keys que referenciam linhas ausentes serão tratadas:
+ <literal>ignore</literal> irá tratar a linha ausente como ama associaççao de null
+ </para>
+ </callout>
+ <callout arearefs="manytoone15">
+ <para>
+ <literal>entity-name</literal> (opcional): O nome da entidade da classe associada.
+ </para>
+ </callout>
+ </calloutlist>
+ <callout arearefs="manytoone16">
+ <para>
+ <literal>formula</literal> (optional): Uma expressão SQL que define um valor
+ para um foreign key <emphasis>computed</emphasis>.
+ </para>
+ </callout>
+ </programlistingco>
+
+ <para>
+ Setar o valor do atributo <literal>cascade</literal> para qualquer valor
+ significativo diferente de <literal>none</literal> irá propagar certas operações
+ ao objeto associado. Os valores significativos são os nomes das operações básicas
+ do Hibernate, <literal>persist, merge, delete, save-update, evict, replicate, lock,
+ refresh</literal>, assim como os valores especiais <literal>delete-orphan</literal>
+ e <literal>all</literal> e combinações de nomes de operações separadas por vÃrgula,
+ como por exemplo, <literal>cascade="persist,merge,evict"</literal> ou
+ <literal>cascade="all,delete-orphan"</literal>. Veja a seção
+ <xref linkend="objectstate-transitive"/> para uma explicação completa. Note que
+ associações valoradas simples (associações muitos-pra-um, e um-pra-um) não suportam
+ orphan delete.
+ </para>
+
+ <para>
+ Uma tÃpica declaração <literal>muitos-pra-um</literal> se parece com esta:
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="product" class="Product" column="PRODUCT_ID"/>]]></programlisting>
+
+ <para>
+ O atributo <literal>property-ref</literal> deve apenas ser usado para mapear dados
+ legados onde uma chave estrangeira se referencia a uma chave exclusiva da tabela
+ associada que não seja à chave primária. Este é um modelo relacional desagradável.
+ Por exemplo, suponha que a classe <literal>Product</literal> tenha um número
+ seqüencial exclusivo, que não é a chave primária. (O atributo <literal>unique</literal>
+ controla a geração de DDL do Hibernate com a ferramenta SchemaExport.)
+ </para>
+
+ <programlisting><![CDATA[<property name="serialNumber" unique="true" type="string" column="SERIAL_NUMBER"/>]]></programlisting>
+
+ <para>
+ Então o mapeamento para <literal>OrderItem</literal> poderia usar:
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="product" property-ref="serialNumber" column="PRODUCT_SERIAL_NUMBER"/>]]></programlisting>
+
+ <para>
+ Porém, isto obviamente não é indicado, nunca.
+ </para>
+
+ <para>
+ Se a chave exclusiva referenciada engloba múltiplas propriedades da entidade associada,
+ você deve mapear as propriedades referenciadas dentro de um elemento chamado
+ <literal><properties></literal>
+
+ </para>
+
+ <para>
+ Se a chave exclusiva referenciada é a propriedade de um componente, você pode especificar
+ um caminho para a propriedade.
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="owner" property-ref="identity.ssn" column="OWNER_SSN"/>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-onetoone" revision="3">
+ <title>one-to-one (um-pra-um)</title>
+
+ <para>
+ Uma associação um-pra-um para outra classe persistente é declarada usando
+ um elemento <literal>one-to-one </literal>.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="onetoone1" coords="2 70"/>
+ <area id="onetoone2" coords="3 70"/>
+ <area id="onetoone3" coords="4 70"/>
+ <area id="onetoone4" coords="5 70"/>
+ <area id="onetoone5" coords="6 70"/>
+ <area id="onetoone6" coords="7 70"/>
+ <area id="onetoone7" coords="8 70"/>
+ <area id="onetoone8" coords="9 70"/>
+ <area id="onetoone9" coords="10 70"/>
+ <area id="onetoone10" coords="11 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<one-to-one
+ name="propertyName"
+ class="ClassName"
+ cascade="cascade_style"
+ constrained="true|false"
+ fetch="join|select"
+ property-ref="propertyNameFromAssociatedClass"
+ access="field|property|ClassName"
+ formula="any SQL expression"
+ lazy="proxy|no-proxy|false"
+ entity-name="EntityName"
+ node="element-name|@attribute-name|element/@attribute|."
+ embed-xml="true|false"
+ foreign-key="foreign_key_name"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="onetoone1">
+ <para>
+ <literal>name</literal>: O nome da propriedade.
+ </para>
+ </callout>
+ <callout arearefs="onetoone2">
+ <para>
+ <literal>class</literal> (opcional â default para o tipo da propriedade
+ definido via reflection): O nome da classe associada.
+ </para>
+ </callout>
+ <callout arearefs="onetoone3">
+ <para>
+ <literal>cascade</literal> (opcional): Especifica qual operação deve
+ ser cascateada do objeto pai para o objeto associado.
+ </para>
+ </callout>
+ <callout arearefs="onetoone4">
+ <para>
+ <literal>constrained</literal> (opcional): Especifica que uma chave estrangeira
+ constraint na chave primária da tabela mapeada referencia a tabela da classe
+ associada, Esta opção afeta a ordem em queh <literal>save()</literal> e
+ <literal>delete()</literal> são cascateadas, e determina se a associação
+ pode ser substituÃda (isto também é usado pela ferramenta schema export).
+ </para>
+ </callout>
+ <callout arearefs="onetoone5">
+ <para>
+ <literal>fetch</literal> ((opcional â valor default <literal>select</literal>):
+ Escolhe entre outer-join fetching ou sequential select fetching.
+ </para>
+ </callout>
+ <callout arearefs="onetoone6">
+ <para>
+ <literal>property-ref</literal>(opcional): O nome da propriedade da classe associada
+ que é ligada a chave primária desta classe. Se não for especificada, a chave primária
+ da classe associada é utilizada.
+ </para>
+ </callout>
+ <callout arearefs="onetoone7">
+ <para>
+ <literal>access</literal> (opcional - valor default padrão <literal>property</literal>):
+ A estratégia que o Hibernate pode usar para acessar o valor da propriedade.
+ </para>
+ </callout>
+ <callout arearefs="onetoone8">
+ <para>
+ <literal>formula</literal> (opcional): Quase todas associações um-pra-um mapeiam
+ para a chave primária da entidade dona. No caso raro, que não é o caso, você
+ pode especificar uma outra coluna, colunas ou expressões para juntar utilizando
+ uma formula SQL. (Veja <literal>org.hibernate.test.onetooneformula</literal>
+ para exemplo).
+ </para>
+ </callout>
+ <callout arearefs="onetoone9">
+ <para>
+ <literal>lazy</literal> (opcional â valor default <literal>proxy</literal>):
+ Por default, associações single point são proxied. <literal>lazy="no-proxy"</literal>
+ especifica que a propriedade deve ser fetched lazily quando o atributo é acessado
+ pela primeira vez (requer build-time bytecode instrumentation).
+ <literal>lazy="false"</literal> especifica que a associação vai sempre ser
+ avidamente fetched. <emphasis>Note que se <literal>constrained="false"</literal>,
+ proxing é impossÃvel e o Hibernate vai ávido fetch a associação!</emphasis>
+ </para>
+ </callout>
+ <callout arearefs="onetoone10">
+ <para>
+ <literal>entity-name</literal> (opcional): O nome da entidade da classe associada.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Existem duas variedades de associações um-pra-um:
+ </para>
+ <itemizedlist>
+ <listitem><para>
+ associações de chave primária
+ </para></listitem>
+ <listitem><para>
+ associações de chave estrangeira exclusiva
+ </para></listitem>
+ </itemizedlist>
+
+ <para>
+ Associações de chave primária não necessitam de uma coluna extra de tabela; se duas
+ linhas são relacionadas pela associação então as duas linhas da tabela dividem a mesmo
+ valor da chave primária. Assim, se você quer que dois objetos sejam relacionados por
+ uma associação de chave primária, você deve ter certeza que eles são assinados com o
+ mesmo valor identificador!
+ </para>
+
+ <para>
+ Para uma associação de chave primária, adicione os seguintes mapeamentos em
+ <literal>Employee</literal> e <literal>Person</literal>, respectivamente.
+ </para>
+
+ <programlisting><![CDATA[<one-to-one name="person" class="Person"/>]]></programlisting>
+ <programlisting><![CDATA[<one-to-one name="employee" class="Employee" constrained="true"/>]]></programlisting>
+
+ <para>
+ Agora nós devemos assegurar que as chaves primárias de linhas relacionadas nas
+ tabelas PERSON e EMPLOYEE são iguais. Nós usamos uma estratégia especial de geração
+ de identificador do Hibernate chamada <literal>foreign</literal>:
+ </para>
+
+ <programlisting><![CDATA[<class name="person" table="PERSON">
+ <id name="id" column="PERSON_ID">
+ <generator class="foreign">
+ <param name="property">employee</param>
+ </generator>
+ </id>
+ ...
+ <one-to-one name="employee"
+ class="Employee"
+ constrained="true"/>
+</class>]]></programlisting>
+
+ <para>
+ Uma nova instância de <literal>Person</literal> salva recentemente é então assinada
+ com o mesmo valor da chave primária da instância de <literal>employee</literal> referenciada
+ com a propriedade <literal>employee</literal> daquela <literal>Person</literal>.
+ </para>
+
+ <para>
+ Alternativamente, uma chave estrangeira com uma unique constraint, de
+ <literal>Employee</literal> para <literal>Person</literal>, pode ser expressa como:
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="person" class="Person" column="PERSON_ID" unique="true"/>]]></programlisting>
+
+ <para>
+ E esta associação pode ser feita de forma bi-direcional adicionando o seguinte
+ no mapeamento de <literal>Person</literal>:
+ </para>
+
+ <programlisting><![CDATA[<one-to-one name"employee" class="Employee" property-ref="person"/>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-naturalid">
+ <title>natural-id</title>
+
+ <programlisting><![CDATA[<natural-id mutable="true|false"/>
+ <property ... />
+ <many-to-one ... />
+ ......
+</natural-id>]]></programlisting>
+
+ <para>
+ Embora nós recomendemos o uso de surrogate keys como chaves primárias, você deve
+ ainda identificar chaves naturais para todas as entidades. Uma chave natural é
+ uma propriedade ou combinação de propriedades que é exclusiva e não nula. Se não
+ pude ser modificada, melhor ainda. Mapeie as propriedades da chave natural dentro do
+ elemento <literal><natural-id></literal>. O Hibernate irá gerar a chave
+ exclusiva necessária e as constraints de nullability , e seu mapeamento será
+ apropriadamente auto documentado.
+ </para>
+
+ <para>
+ Nós recomendamos com enfase que você implemente <literal>equals()</literal> e
+ <literal>hashCode()</literal> para comparar as propriedades da chave natural da
+ entidade.
+ </para>
+
+ <para>
+ Este mapeamento não tem o objetivo de uso com entidades com natural chaves primárias.
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>mutable</literal> mutable (opcional, valor default<literal>false</literal>):
+ Por default, propriedades naturais identificadoras são consideradas imutáveis (constante).
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-component" revision="2">
+ <title>componente, componente dinâmico</title>
+
+ <para>
+ O elemento<literal><component></literal> mapeia propriedades de um
+ objeto filho para colunas da tabela de uma classe pai. Componentes podem,
+ um após o outro, declarar suas próprias propriedades, componentes ou coleções.
+ Veja "Components" abaixo.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="component1" coords="2 45"/>
+ <area id="component2" coords="3 45"/>
+ <area id="component3" coords="4 45"/>
+ <area id="component4" coords="5 45"/>
+ <area id="component5" coords="6 45"/>
+ <area id="component6" coords="7 45"/>
+ <area id="component7" coords="8 45"/>
+ <area id="component8" coords="9 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<component
+ name="propertyName"
+ class="className"
+ insert="true|false"
+ update="true|false"
+ access="field|property|ClassName"
+ lazy="true|false"
+ optimistic-lock="true|false"
+ unique="true|false"
+ node="element-name|."
+>
+
+ <property ...../>
+ <many-to-one .... />
+ ........
+</component>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="component1">
+ <para>
+ <literal>name</literal>: O nome da propriedade.
+ </para>
+ </callout>
+ <callout arearefs="component2">
+ <para>
+ <literal>class</literal> (opcional â valor default para o tipo de
+ propriedade determinada por reflection): O nome da classe (filha) do
+ componente.
+ </para>
+ </callout>
+ <callout arearefs="component3">
+ <para>
+ <literal>insert</literal>: As colunas mapeadas aparecem nos
+ SQL de <literal>INSERT</literal>s?
+ </para>
+ </callout>
+ <callout arearefs="component4">
+ <para>
+ <literal>update</literal>: As colunas mapeadas aparecem nos
+ SQL de <literal>UPDATE</literal>s?
+ </para>
+ </callout>
+ <callout arearefs="component5">
+ <para>
+ <literal>access</literal> (opcional â valor default <literal>property</literal>):
+ A estratégia que o Hibernate pode usar para acessar o valor da propriedade.
+ </para>
+ </callout>
+ <callout arearefs="component6">
+ <para>
+ <literal>lazy</literal> (opcional - valor default <literal>false</literal>):
+ Especifica que este componente deve ser fetched lazily quando o atributo for
+ acessado pela primeira vez (requer build-time bytecode instrumentation).
+ </para>
+ </callout>
+ <callout arearefs="component7">
+ <para>
+ <literal>optimistic-lock</literal> (opcional â valor default <literal>true</literal>):
+ Especifica que atualizações para este componente requerem ou não aquisição
+ de um lock otimista. Em outras palavras, determina se uma versão de incremento deve
+ ocorrer quando esta propriedade estiver modificada.
+ </para>
+ </callout>
+ <callout arearefs="component8">
+ <para>
+ <literal>unique</literal> (opcional â valor default <literal>false</literal>):
+ Especifica que existe uma unique constraint em todas as colunas mapeadas do
+ componente.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ A tag filha <literal><property></literal> acrescenta a propriedade
+ de mapeamento da classe filha para colunas de uma tabela.
+ </para>
+
+ <para>
+ O elemento <literal><component></literal> permite um sub-elemento
+ <literal><parent></literal> mapeie uma propriedade da classe do componente
+ como uma referencia de volta para a entidade que o contém.
+ </para>
+
+ <para>
+ O elemento <literal><dynamic-component></literal> permite que um
+ <literal>Map</literal> possa ser mapeado como um componente onde os nomes das
+ propriedades referem-se para as chaves no mapa, veja
+ <xref linkend="components-dynamic"/>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-properties" revision="2">
+ <title>propriedades</title>
+
+ <para>
+ O elemento <literal><properties></literal> permite a definição de um grupo
+ com nome, lógico de propriedades de uma classe. O uso mais importante do construtor
+ é que este permite uma combinação de propriedades para ser o objetivo de uma
+ <literal>property-ref</literal>. à também um modo conveninente para definir uma
+ unique constraint de múltiplas colunas.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="properties1" coords="2 45"/>
+ <area id="properties2" coords="3 45"/>
+ <area id="properties3" coords="4 45"/>
+ <area id="properties4" coords="5 45"/>
+ <area id="properties5" coords="6 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<properties
+ name="logicalName"
+ insert="true|false"
+ update="true|false"
+ optimistic-lock="true|false"
+ unique="true|false"
+>
+
+ <property ...../>
+ <many-to-one .... />
+ ........
+</properties>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="properties1">
+ <para>
+ <literal>name</literal>:: O nome lógico do agrupamento â
+ <emphasis>não </emphasis> é o nome atual de propriedade.
+ </para>
+ </callout>
+ <callout arearefs="properties2">
+ <para>
+ <literal>insert</literal>: As colunas mapeadas aparecem nos
+ SQL de <literal>INSERT</literal>s?
+ </para>
+ </callout>
+ <callout arearefs="properties3">
+ <para>
+ <literal>update</literal>: As colunas mapeadas aparecem nos
+ SQL de <literal>UPDATE</literal>s?
+ </para>
+ </callout>
+ <callout arearefs="properties4">
+ <para>
+ <literal>optimistic-lock</literal> (opcional â valor default <literal>true</literal>):
+ Especifica que atualizações para estes componentes requerem ou não aquisição de um
+ lock otimista. Em outras palavras, determina se uma versão de incremento deve ocorrer
+ quando estas propriedades estiverem modificadas.
+ </para>
+ </callout>
+ <callout arearefs="properties5">
+ <para>
+ <literal>unique</literal> (opcional â valor defautl <literal>false</literal>):
+ Especifica que uma unique constraint existe em todas as colunas mapeadas do componente.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Por exemplo, se nós temos o seguinte mapeamento de <literal><properties></literal>:
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="personNumber"/>
+ ...
+ <properties name="name"
+ unique="true" update="false">
+ <property name="firstName"/>
+ <property name="initial"/>
+ <property name="lastName"/>
+ </properties>
+</class>]]></programlisting>
+
+ <para>
+ Então nós podemos ter uma associação de dados herdados que referem a esta chave
+ exclusiva da tabela <literal>Person</literal>, ao invés de se referirem a chave
+ primária:
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="person"
+ class="Person" property-ref="name">
+ <column name="firstName"/>
+ <column name="initial"/>
+ <column name="lastName"/>
+</many-to-one>]]></programlisting>
+
+ <para>
+ Nós não recomendamos o uso deste tipo de coisa fora do contexto de mapeamento de
+ dados herdados.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-subclass" revision="4">
+ <title>subclass (subclasse)</title>
+
+ <para>
+ Finalmente, a persistência polimórfica requer a declaração de cada subclasse
+ da classe de persistência raiz. Para a estratégia de mapeamento
+ table-per-class-hierarchy, a declaração <literal><subclass></literal>
+ deve ser usada.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="subclass1" coords="2 55"/>
+ <area id="subclass2" coords="3 55"/>
+ <area id="subclass3" coords="4 55"/>
+ <area id="subclass4" coords="5 55"/>
+ </areaspec>
+ <programlisting><![CDATA[<subclass
+ name="ClassName"
+ discriminator-value="discriminator_value"
+ proxy="ProxyInterface"
+ lazy="true|false"
+ dynamic-update="true|false"
+ dynamic-insert="true|false"
+ entity-name="EntityName"
+ node="element-name"
+ extends="SuperclassName">
+
+ <property .... />
+ .....
+</subclass>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="subclass1">
+ <para>
+ <literal>name</literal>: O nome de classe completamente qualificada da subclasse.
+ </para>
+ </callout>
+ <callout arearefs="subclass2">
+ <para>
+ <literal>discriminator-value</literal> (opcional â valor default o nome da classe):
+ Um valor que distingue subclasses individuais.
+ </para>
+ </callout>
+ <callout arearefs="subclass3">
+ <para>
+ <literal>proxy</literal> (opcional): Especifica a classe ou interface que
+ usará os proxies de inicialização atrasada.
+ </para>
+ </callout>
+ <callout arearefs="subclass4">
+ <para>
+ <literal>lazy</literal> (opcional, valor default <literal>true</literal>):
+ Configurar <literal>lazy="false"</literal> desabilitará o uso de
+ inicialização atrasada.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+ <para>
+ Cada subclasse deve declarar suas próprias propriedades persistentes e subclasses.
+ As propriedades <literal><version></literal> e <literal><id></literal>
+ são configuradas para serem herdades da classe raiz. Cada subclasse numa hierarquia
+ deve definir um único <literal>discriminator-value</literal>. Se nenhum for
+ especificado, o nome da classe Java completamente qualificada será usada.
+ </para>
+
+ <para>
+ Para informações sobre mapeamento de heranças, veja o <xref linkend="inheritance"/>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-joinedsubclass" revision="3">
+ <title>joined-subclass</title>
+
+ <para>
+ Alternativamente, cada subclasse pode ser mapeada para sua própria tabela
+ (Estratégia de mapeamento table-per-subclass). O estado herdado é devolvido
+ por associação com a tabela da superclasse. Nós usamos o elemento
+ <literal><joined-subclass></literal>.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="joinedsubclass1" coords="2 45"/>
+ <area id="joinedsubclass2" coords="3 45"/>
+ <area id="joinedsubclass3" coords="4 45"/>
+ <area id="joinedsubclass4" coords="5 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<joined-subclass
+ name="ClassName"
+ table="tablename"
+ proxy="ProxyInterface"
+ lazy="true|false"
+ dynamic-update="true|false"
+ dynamic-insert="true|false"
+ schema="schema"
+ catalog="catalog"
+ extends="SuperclassName"
+ persister="ClassName"
+ subselect="SQL expression"
+ entity-name="EntityName"
+ node="element-name">
+
+ <key .... >
+
+ <property .... />
+ .....
+</joined-subclass>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="joinedsubclass1">
+ <para>
+ <literal>name</literal>: O nome da classe completamente qualificada da
+ subclasse.
+ </para>
+ </callout>
+ <callout arearefs="joinedsubclass2">
+ <para>
+ <literal>table</literal>: O nome da tabela da subclasse.
+ </para>
+ </callout>
+ <callout arearefs="joinedsubclass3">
+ <para>
+ <literal>proxy</literal> (opcional): Especifica a classe ou interface
+ para usar os proxies de recuperação atrasada.
+ </para>
+ </callout>
+ <callout arearefs="joinedsubclass4">
+ <para>
+ <literal>lazy</literal> (opcional, valor default <literal>true</literal>):
+ Fixanr <literal>lazy="false"</literal> desabilita o uso recuperação
+ atrasada.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ A coluna discriminator requerida para esta estratégia de mapeamento. Porém,
+ cada subclasse deve declarar uma coluna de tabela com o identificador do objeto
+ usando o elemento <literal><key></literal>. O mapeamento no inÃcio do
+ capÃtulo poderia ser re-escrito assim:
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="eg">
+
+ <class name="Cat" table="CATS">
+ <id name="id" column="uid" type="long">
+ <generator class="hilo"/>
+ </id>
+ <property name="birthdate" type="date"/>
+ <property name="color" not-null="true"/>
+ <property name="sex" not-null="true"/>
+ <property name="weight"/>
+ <many-to-one name="mate"/>
+ <set name="kittens">
+ <key column="MOTHER"/>
+ <one-to-many class="Cat"/>
+ </set>
+ <joined-subclass name="DomesticCat" table="DOMESTIC_CATS">
+ <key column="CAT"/>
+ <property name="name" type="string"/>
+ </joined-subclass>
+ </class>
+
+ <class name="eg.Dog">
+ <!-- mapping for Dog could go here -->
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Para informações de mapeamentos de herança, veja <xref linkend="inheritance"/>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-unionsubclass" revision="2">
+ <title>union-subclass</title>
+
+ <para>
+ Uma terceira opção é mapear para tabelas apenas as classes concretas de uma
+ hierarquia de heranças, (a estratégia table-per-concrete-class) onde cada tabela
+ define todos os estados persistentes da classe, incluindo estados herdados.
+ No Hibernate, não é absolutamente necessário mapear explicitamente como hierarquia
+ de heranças. Você pode simplesmente mapear cada classe com uma declaração
+ <literal><class></literal> separada. Porém, se você deseja usar associações
+ polimórficas (por exemplo: uma associação para a superclasse de sua hierarquia),
+ você precisa usar o mapeamento <literal><union-subclass></literal>.
+
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="unionsubclass1" coords="2 45"/>
+ <area id="unionsubclass2" coords="3 45"/>
+ <area id="unionsubclass3" coords="4 45"/>
+ <area id="unionsubclass4" coords="5 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<union-subclass
+ name="ClassName"
+ table="tablename"
+ proxy="ProxyInterface"
+ lazy="true|false"
+ dynamic-update="true|false"
+ dynamic-insert="true|false"
+ schema="schema"
+ catalog="catalog"
+ extends="SuperclassName"
+ abstract="true|false"
+ persister="ClassName"
+ subselect="SQL expression"
+ entity-name="EntityName"
+ node="element-name">
+
+ <property .... />
+ .....
+</union-subclass>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="unionsubclass1">
+ <para>
+ <literal>name</literal>: O nome da subclasse completamente qualificada.
+ </para>
+ </callout>
+ <callout arearefs="unionsubclass2">
+ <para>
+ <literal>table</literal>: O nome da tabela da subclasse.
+ </para>
+ </callout>
+ <callout arearefs="unionsubclass3">
+ <para>
+ <literal>proxy</literal> (optional): Specifies a class or interface to use
+ for lazy initializing proxies.
+
+ <literal>proxy</literal> (opcional): Especifica a classe ou interface para usar
+ os proxies de recuperação atrasada.
+ </para>
+ </callout>
+ <callout arearefs="unionsubclass4">
+ <para>
+ <literal>lazy</literal> (optional, defaults to <literal>true</literal>): Setting
+ <literal>lazy="false"</literal> disables the use of lazy fetching.
+ <literal>lazy</literal> (opcional, valor default p<literal>true</literal>):
+ Fixando <literal>lazy="false"</literal> desabilita o uso da recuperação atrasada.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ A coluna discriminatõria não é requerida para esta estratégia de mapeamento.
+
+ </para>
+
+ <para>
+ Para informações sobre mapeamentos de herança, veja <xref linkend="inheritance"/>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-join" revision="3">
+ <title>join</title>
+
+ <para>
+ Usando o elemento <literal><join></literal>>, é possÃvel mapear
+ propriedades de uma classe para várias tabelas.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="join1" coords="2 50"/>
+ <area id="join2" coords="3 50"/>
+ <area id="join3" coords="4 50"/>
+ <area id="join4" coords="5 50"/>
+ <area id="join5" coords="6 50"/>
+ <area id="join6" coords="7 50"/>
+ </areaspec>
+ <programlisting><![CDATA[<join
+ table="tablename"
+ schema="owner"
+ catalog="catalog"
+ fetch="join|select"
+ inverse="true|false"
+ optional="true|false">
+
+ <key ... />
+
+ <property ... />
+ ...
+</join>]]></programlisting>
+
+ <calloutlist>
+ <callout arearefs="join1">
+ <para>
+ <literal>table</literal>: O nome da tabela associada.
+ </para>
+ </callout>
+ <callout arearefs="join2">
+ <para>
+ <literal>schema</literal> (opcional): Sobrepõe o nome do esquema
+ especificado pelo elemento raiz <literal><hibernate-mapping></literal>.
+ </para>
+ </callout>
+ <callout arearefs="join3">
+ <para>
+ <literal>catalog</literal> (opcional): Sobrepõe o nome do catálogo
+ especificado pelo elemento raiz<literal><hibernate-mapping></literal>.
+ </para>
+ </callout>
+ <callout arearefs="join4">
+ <para>
+ <literal>fetch</literal>(opcional â valor default <literal>join</literal>): Se setado
+ para <literal>join</literal>, o padrão, o Hibernate irá usar um inner join para
+ restaurar um <literal>join</literal> definido por uma classe ou suas subclasses e
+ uma outer join para um <literal>join</literal> definido por uma subclasse.
+ Se setado para <literal>select</literal>, então o Hibernate irá usar uma seleção
+ seqüencial para um <literal><join></literal> definida numa subclasse, que irá
+ ser emitido apenas se uma linha se concentrar para representar uma instância
+ da subclasse. Inner joins irá ainda ser usado para restaurar um
+ <literal><join></literal> definido pela classe e suas superclasses.
+ </para>
+ </callout>
+ <callout arearefs="join5">
+ <para>
+ <literal>inverse</literal> (opcional â valor default <literal>false</literal>):
+ Se habilitado, o Hibernate não irá tentar inserir ou atualizar as propriedades
+ definidas por este join.
+ </para>
+ </callout>
+ <callout arearefs="join6">
+ <para>
+ <literal>optional</literal> (opcional â valor default <literal>false</literal>):
+ Se habilitado, o Hibernate irá inserir uma linha apenas se as propriedades definidas
+ por esta junção não forem nulas e irá sempre usar uma outer join para
+ recuperar as propriedades.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Por exemplo, a informação de endereço para uma pessoa pode ser mapeada para uma
+ tabela separada (enquanto preservando o valor da semântica de tipos para
+ todas as propriedades):
+ </para>
+
+ <programlisting><![CDATA[<class name="Person"
+ table="PERSON">
+
+ <id name="id" column="PERSON_ID">...</id>
+
+ <join table="ADDRESS">
+ <key column="ADDRESS_ID"/>
+ <property name="address"/>
+ <property name="zip"/>
+ <property name="country"/>
+ </join>
+ ...]]></programlisting>
+
+ <para>
+ Esta caracterÃstica é útil apenas para modelos de dados legados, nós recomendamos
+ menos tabelas do que classes e um modelo de domÃnio bem granulado. Porém, é
+ útil para ficar trocando entre estratégias de mapeamento de herança
+ numa hierarquia simples, como explicado mais a frente.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-key">
+ <title>key</title>
+
+ <para>
+ Nós vimos que o elemento <literal><key></literal> surgiu algumas vezes
+ até agora. Ele aparece em qualquer lugar que o elemento pai define uma junção
+ para a nova tabela, e define a chave estrangeira para a tabela associada, que
+ referencia a chave primária da tabela original.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="key1" coords="2 50"/>
+ <area id="key2" coords="3 50"/>
+ <area id="key3" coords="4 50"/>
+ <area id="key4" coords="5 50"/>
+ <area id="key5" coords="6 50"/>
+ <area id="key6" coords="7 50"/>
+ </areaspec>
+ <programlisting><![CDATA[<key
+ column="columnname"
+ on-delete="noaction|cascade"
+ property-ref="propertyName"
+ not-null="true|false"
+ update="true|false"
+ unique="true|false"
+/>]]></programlisting>
+
+ <calloutlist>
+ <callout arearefs="key1">
+ <para>.
+ <literal>column</literal> (opcional): O nome da coluna da chave estrangeira.
+ Isto também pode ser especificado por aninhamento de elemento(s)
+ <literal><column></literal>.
+ </para>
+ </callout>
+ <callout arearefs="key2">
+ <para>
+ <literal>on-delete</literal> (opcional, valor default <literal>noaction</literal>):
+ Especifica se a constraint da chave estrangeira no banco de dados esta
+ habilitada para cascade delete .
+ </para>
+ </callout>
+ <callout arearefs="key3">
+ <para>
+ <literal>property-ref</literal> (opcional): Especifica que a chave estrangeira
+ se refere a colunas que não são chave primária da tabela original.
+ (Util para base de dados legadas.)
+ </para>
+ </callout>
+ <callout arearefs="key4">
+ <para>
+ <literal>not-null</literal> (opcional): Especifica que a coluna da chave
+ estrangeira não aceita valores nulos (isto é implÃcito em qualquer momento
+ que a chave estrangeira também fizer parte da chave primária).
+ </para>
+ </callout>
+ <callout arearefs="key5">
+ <para>
+ <literal>update</literal> (optional): Specifies that the foreign key should never
+ be updated (this is implied whenever the foreign key is also part of the primary
+ key).
+ <literal>update</literal> (opcional): Especifica que a chave estrangeira nunca
+ deve ser atualizada (isto é implÃcito em qualquer momento que a chave estrangeira
+ também fizer parte da chave primária).
+ </para>
+ </callout>
+ <callout arearefs="key6">
+ <para>
+ <literal>unique</literal> (opcional): Especifica que a chave estrangeira deve ter
+ uma constraint unique (sto é implÃcito em qualquer momento que a chave estrangeira
+ também fizer parte da chave primária).
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Nós recomendamos que para sistemas que a performance de delete seja importante, todas as
+ chaves deve ser definida <literal>on-delete="cascade"</literal>, e o Hibernate irá usar
+ uma constraint a nÃvel de banco de dados <literal>ON CASCADE DELETE</literal>, ao invés
+ de muitas instruções <literal>DELETE</literal>. Esteja ciente que esta caracterÃstica é
+ um atalho da estratégia usual de optimistic locking do Hibernate para dados versionados.
+ </para>
+
+ <para>
+ Os atributos <literal>not-null</literal> e <literal>update</literal> são úteis quando
+ estamos mapeamos uma associação unidirecional um para muitos. Se você mapear uma
+ asociação unidirecional um para muitos para uma chave estrangeira non-nullable, você
+ <emphasis>deve</emphasis> declarar a coluna chave usando
+ <literal><key not-null="true"></literal>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-column" revision="4">
+ <title>elementos column e formula</title>
+ <para>
+ Qualquer elemento de mapeamente que aceita um atributo <literal>column</literal> irá
+ aceitar alternativamente um subelemento <literal><column></literal>. Da mesma forma,
+ <literal>formula</literal> é uma alternativa para o atributo <literal>formula</literal>.
+ </para>
+
+ <programlisting><![CDATA[<column
+ name="column_name"
+ length="N"
+ precision="N"
+ scale="N"
+ not-null="true|false"
+ unique="true|false"
+ unique-key="multicolumn_unique_key_name"
+ index="index_name"
+ sql-type="sql_type_name"
+ check="SQL expression"
+ default="SQL expression"/>]]></programlisting>
+
+ <programlisting><![CDATA[<formula>SQL expression</formula>]]></programlisting>
+
+ <para>
+ O atributo <literal>column</literal> e <literal>formula</literal> podem até ser combinados
+ dentro da mesma propriedade ou associação mapeando para expressar,
+ por exemplo, associações exóticas.
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="homeAddress" class="Address"
+ insert="false" update="false">
+ <column name="person_id" not-null="true" length="10"/>
+ <formula>'MAILING'</formula>
+</many-to-one>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-import">
+ <title>import</title>
+
+ <para>
+ Suponha que a sua aplicação tem duas classes persistentes com o mesmo nome, e você não quer
+ especificar o nome qualificado (do pacote) nas queries do Hibernate. As Classes devem
+ ser "importadas" explicitamente, de preferência contando com <literal>auto-import="true"</literal>.
+ Você pode até importar classes e interfaces que não estão explicitamente mapeadas.
+ </para>
+
+ <programlisting><![CDATA[<import class="java.lang.Object" rename="Universe"/>]]></programlisting>
+
+ <programlistingco>
+ <areaspec>
+ <area id="import1" coords="2 40"/>
+ <area id="import2" coords="3 40"/>
+ </areaspec>
+ <programlisting><![CDATA[<import
+ class="ClassName"
+ rename="ShortName"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="import1">
+ <para>
+ <literal>class</literal>: O nome qualificado (do pacote) de qualquer classe Java.
+ </para>
+ </callout>
+ <callout arearefs="import2">
+ <para>
+ <literal>rename</literal> (opcional â valor default, o nome da classe não
+ qualificada): Um nome que pode ser usado numa linguagem de consulta.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ </sect2>
+
+ <sect2 id="mapping-types-anymapping" revision="2">
+ <title>any</title>
+
+ <para>
+ Existe mais um tipo de propriedade de mapeamento. O elemento de mapeamento
+ <literal><any></literal> define uma associação polimórfica para classes de múltiplas tabelas.
+ Este tipo de mapeamento sempre requer mais de uma coluna. A primeira coluna possui o tipo da entidade
+ associada. A outra coluna que ficou possui o identificador. à impossÃvel especificar uma restrição
+ de chave estrangeira para este tipo de associação, assim isto claramente não é visto
+ como um caminho usual para associações (polimórficas) de mapeamento. Você deve usar este mapeamento
+ apenas em casos muito especiais (exemplo: audit logs, dados de sessão do usuário, etc).
+
+ </para>
+
+ <para>
+ O atributo <literal>meta-type</literal> permite a aplicação especificar um tipo adaptado
+ que mapeia valores de colunas de banco de dados para classes persistentes que tem propriedades
+ identificadoras do tipo especificado através do <literal>id-type</literal>. Você deve especificar
+ o mapeamento de valores do meta-type para nome de classes.
+ </para>
+
+ <programlisting><![CDATA[<any name="being" id-type="long" meta-type="string">
+ <meta-value value="TBL_ANIMAL" class="Animal"/>
+ <meta-value value="TBL_HUMAN" class="Human"/>
+ <meta-value value="TBL_ALIEN" class="Alien"/>
+ <column name="table_name"/>
+ <column name="id"/>
+</any>]]></programlisting>
+
+ <programlistingco>
+ <areaspec>
+ <area id="any1" coords="2 50"/>
+ <area id="any2" coords="3 50"/>
+ <area id="any3" coords="4 50"/>
+ <area id="any4" coords="5 50"/>
+ <area id="any5" coords="6 50"/>
+ <area id="any6" coords="7 50"/>
+ </areaspec>
+ <programlisting><![CDATA[<any
+ name="propertyName"
+ id-type="idtypename"
+ meta-type="metatypename"
+ cascade="cascade_style"
+ access="field|property|ClassName"
+ optimistic-lock="true|false"
+>
+ <meta-value ... />
+ <meta-value ... />
+ .....
+ <column .... />
+ <column .... />
+ .....
+</any>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="any1">
+ <para>
+ <literal>name</literal>: o nome da propriedade.
+ </para>
+ </callout>
+ <callout arearefs="any2">
+ <para>
+ <literal>id-type</literal>: o tipo identificador.
+ </para>
+ </callout>
+ <callout arearefs="any3">
+ <para>
+ <literal>meta-type</literal> (opcional â valor default <literal>string</literal>):
+ Qualquer tipo que é permitido para um mapeamento discriminador.
+ </para>
+ </callout>
+ <callout arearefs="any4">
+ <para>
+ <literal>cascade</literal> (opcional â valor default <literal>none</literal>):
+ o estilo do cascade.
+ </para>
+ </callout>
+ <callout arearefs="any5">
+ <para>
+ <literal>access</literal> (opcional â valor default <literal>property</literal>):
+ A estratégia que o hibernate deve usar para acessar o valor da propriedade.
+ </para>
+ </callout>
+ <callout arearefs="any6">
+ <para>
+ <literal>optimistic-lock</literal> (opcional - valor default<literal>true</literal>):
+ Especifica que as atualizações para esta propriedade requerem ou não aquisição da
+ trava otimista. Em outras palavras, define se uma versão de incremento deve ocorrer
+ se esta propriedade está modificada.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="mapping-types">
+ <title>Tipos do Hibernate</title>
+
+ <sect2 id="mapping-types-entitiesvalues" revision="1">
+ <title>Entidades e valores</title>
+
+ <para>
+ Para entender o comportamento de vários objetos em nÃvel de linguagem de Java a
+ respeito do serviço de persistência, nós precisamos classificá-los em dois grupos.
+ </para>
+
+ <para>
+ Uma <emphasis>entidade </emphasis> existe independentemente de qualquer outro
+ objeto guardando referências para a entidade. Em contraste com o modelo usual de
+ Java que um objeto não referenciado é coletado pelo garbage collector. Entidades
+ devem ser explicitamente salvas ou deletada (exceto em operações de salvamento
+ ou deleção que possam ser executada em <emphasis>cascata</emphasis> de uma entidade
+ pai para seus filhos). Isto é diferente do modelo ODMG de persistência do objeto
+ por acessibilidade â e corresponde quase a como objetos de aplicações são
+ geralmente usados em grandes sistemas. Entidades suportam referências circulares
+ e comuns. Eles podem ser versionadas.
+ </para>
+
+ <para>
+ Uma entidade em estado persistente consiste de referências para outras entidades
+ e instâncias de tipos de <emphasis>valor</emphasis>. Valores são primitivos,
+ coleções (não o que tem dentro de uma coleção), componentes e certos objetos
+ imutáveis. Entidades distintas, valores (em coleções e componentes particulares)
+ <emphasis>são </emphasis> persistidos e apagados por acessibilidade. Visto que
+ objetos value (e primitivos) são persistidos e apagados junto com as entidades
+ que os contém e não podem ser versionados independentemente. Valores têm
+ identidade não independente, assim eles não podem ser comuns para duas
+ entidades ou coleções.
+
+ </para>
+
+ <para>
+ Até agora, nós estivemos usando o termo "classe persistente" para referir
+ a entidades. Nós iremos continuar a fazer isto. Falando a rigor, porém, nem todas
+ as classes definidas pelo usuário com estados persistentes são entidades. Um
+ <emphasis>componente</emphasis> é uma classe de usuário definida com valores
+ semânticos. Uma propriedade de Java de tipo <literal>java.lang.String</literal>
+ também tem um valor semêntico. Dada esta definição, nós podemos dizer que
+ todos os tipos (classes) fornecida pelo JDK tem tipo de valor semântico em Java,
+ enquanto que tipos definidos pelo usuário pode ser mapeados com entidade ou valor
+ de tipo semântico. Esta decisão pertence ao desenvolvedor da aplicação. Uma boa
+ dica para uma classe entidade em um modelo de domÃnio são referências comuns
+ para uma instância simples daquela classe, enquanto a composição ou agregação
+ geralmente se traduz para um valor de tipo.
+ </para>
+
+ <para>
+ Nós iremos rever ambos os conceitos durante toda a documentação.
+
+ </para>
+
+ <para>
+ O desafio pe mapear o sistema de tipo de Java (e a definição do desenvolvedor de
+ entidades e tipos de valor) para o sistema de tipo SQL/banco de dados. A ponte entre ambos
+ os sistemas é fornecido pelo Hibernate: para entidades que usam
+ <literal><class></literal>, <literal><subclass></literal> e assim por diante.
+ Para tipos de valores nós usamos <literal><property></literal>,
+ <literal><component></literal>, etc, geralmente com um atributo
+ <literal>type</literal>. O valor deste atributo é o nome de um <emphasis>tipo de
+ mapeamento</emphasis> do Hibernate. O Hibernate fornece muitos mapeamentos
+ (para tipos de valores do JDK padrão) ut of the box. Você pode escrever os seus
+ próprios tipos de mapeamentos e implementar sua estratégia de conversão adaptada,
+ como você verá adiante.
+ </para>
+
+ <para>
+ Todos os tipos internos do hibernate exceto coleções suportam semânticas nulas.
+
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-types-basictypes" revision="3">
+ <title>Valores de tipos básicos</title>
+
+ <para>
+ O tipos internos de mapeamentos básicos podem ser a grosso modo categorizado como:
+ <variablelist>
+ <varlistentry>
+ <term><literal>integer, long, short, float, double, character, byte,
+ boolean, yes_no, true_false</literal></term>
+ <listitem>
+ <para>
+ Tipos de mapeamentos de classes primitivas ou wrapper Java especificos
+ (vendor-specific) para tipos de coluna SQL. Boolean,
+ <literal>boolean, yes_no</literal> são todas codificações alternativas
+ para um <literal>boolean</literal> ou <literal>java.lang.Boolean</literal>
+ do Java.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>string</literal></term>
+ <listitem>
+ <para>
+ Um tipo de mapeamento de <literal>java.lang.String</literal> para
+ <literal>VARCHAR</literal> (ou <literal>VARCHAR2</literal> no Oracle).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>date, time, timestamp</literal></term>
+ <listitem>
+ <para>
+ Tipos de mapeamento de <literal>java.util.Date</literal> e suas
+ subclasses para os tipos SQL <literal>DATE</literal>,
+ <literal>TIME</literal> e <literal>TIMESTAMP</literal>
+ (ou equivalente).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>calendar, calendar_date</literal></term>
+ <listitem>
+ <para>
+ Tipo de mapeamento de <literal>java.util.Calendar</literal> para
+ os tipos SQL <literal>TIMESTAMP</literal> e
+ <literal>DATE</literal> (ou equivalente).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>big_decimal, big_integer</literal></term>
+ <listitem>
+ <para>
+ Tipo de mapeamento de <literal>java.math.BigDecimal</literal> and
+ <literal>java.math.BigInteger</literal> para <literal>NUMERIC</literal>
+ (ou <literal>NUMBER</literal> no Oracle).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>locale, timezone, currency</literal></term>
+ <listitem>
+ <para>
+ Tipos de mapeamentos de <literal>java.util.Locale</literal>,
+ <literal>java.util.TimeZone</literal> e <literal>java.util.Currency</literal>
+ para <literal>VARCHAR</literal> (ou <literal>VARCHAR2</literal> no Oracle).
+ Instâncias de f <literal>Locale</literal> e <literal>Currency</literal>
+ são mapeados para seus códigos ISO. Instâncias de <literal>TimeZone</literal>
+ são mapeados para seu <literal>ID</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>class</literal></term>
+ <listitem>
+ <para>
+ um tipo de mapeamento de <literal>java.lang.Class</literal> para
+ <literal>VARCHAR</literal> (ou <literal>VARCHAR2</literal> no
+ Oracle). Uma <literal>Class</literal> é mapeada pelo
+ seu nome qualificado (completo).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>binary</literal></term>
+ <listitem>
+ <para>
+ Mapeia arrays de bytes para um tipo binário de SQL apropriado.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>text</literal></term>
+ <listitem>
+ <para>
+ Maps long Java strings to a SQL <literal>CLOB</literal> or
+ <literal>TEXT</literal> type.
+ Mapeia strings longas de Java para um tipo SQL
+ <literal>CLOB</literal> ou <literal>TEXT</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>serializable</literal></term>
+ <listitem>
+ <para>
+ Mapeia tipos Java serializáveis para um tipo binário SQL apropriado.
+ Você pode também indicar o tipo <literal>serializable</literal> do
+ Hibernate com o nome da classe ou interface Java serializável que
+ não é padrão para um tipo básico.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>clob, blob</literal></term>
+ <listitem>
+ <para>
+ Tipos de mapeamentos para as classes JDBC <literal>java.sql.Clob</literal> and
+ <literal>java.sql.Blob</literal>. Estes tipos podem ser inconveniente para
+ algumas aplicações, visto que o objeto blob ou clob pode não ser reusado
+ fora de uma transação. (Além disso, o suporte de driver é imcompleto e
+ inconsistente.)
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <literal>imm_date, imm_time, imm_timestamp, imm_calendar, imm_calendar_date,
+ imm_serializable, imm_binary</literal>
+ </term>
+ <listitem>
+ <para>
+ Mapeando tipos para o que geralmente são consideradas tipos mutáveis de
+ Java, onde o Hibernate faz determinadas otimizações apropriadas somente
+ para tipos imutáveis de Java, e a aplicação trata o objeto como imutável.
+ Por exemplo, você não deve chamar <literal>Date.setTime()</literal> para
+ uma instância mapeada como <literal>imm_timestamp</literal>. Para mudar
+ o valor da propriedade, e ter a mudança feita persistente, a aplicação
+ deve atribuir um novo objeto (nonidentical) Ã propriedade.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ </para>
+
+ <para>
+ Identificadores únicos das entidades e coleções podem ser de qualquer tipo
+ básico exceto <literal>binary</literal>, <literal>blob</literal> ou
+ <literal>clob</literal>. (Identificadores compostos também são permitidos,
+ veja abaixo.)
+ </para>
+
+ <para>
+ Os tipos de valores básicos têm suas constantes <literal>Type</literal>
+ correspondentes definidas em <literal>org.hibernate.Hibernate</literal>. Por exemplo,
+ <literal>Hibernate.STRING</literal> representa o tipo <literal>string</literal>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-types-custom" revision="2">
+ <title>Tipos de valores personalizados</title>
+
+ <para>
+ à relativamente fácil para desenvolvedores criar seus próprios tipos de valor.
+ Por exemplo, você pode querer persistir propriedades do tipo
+ <literal>java.lang.BigInteger</literal> para colunas <literal>VARCHAR</literal>. O
+ Hibernate não fornece um tipo correspondente para isso. Mas os tipos adaptados
+ não são limitados a mapeamento de uma propriedade (ou elemento de coleção) a uma
+ única coluna da tabela. Assim, por exemplo, você pôde ter uma propriedade Java
+ <literal>getName()</literal>/<literal>setName()</literal> do tipo
+ <literal>java.lang.String</literal> que é persistido para colunas
+ <literal>FIRST_NAME</literal>, <literal>INITIAL</literal>, <literal>SURNAME</literal>.
+
+ </para>
+
+ <para>
+ Para implementar um tipo personalizado, implemente <literal>org.hibernate.UserType</literal>
+ or <literal>org.hibernate.CompositeUserType</literal> e declare propriedades usando o nome
+ qualificado da classe do tipo. Veja <literal>org.hibernate.test.DoubleStringType</literal>
+ para ver o tipo das coisas que são possÃveis.
+ </para>
+
+ <programlisting><![CDATA[<property name="twoStrings" type="org.hibernate.test.DoubleStringType">
+ <column name="first_string"/>
+ <column name="second_string"/>
+</property>]]></programlisting>
+
+ <para>
+ Observe o uso da tag <literal><column></literal> para mapear uma propriedade
+ para colunas múltiplas.
+ </para>
+
+ <para>
+ As interfaces <literal>CompositeUserType</literal>, <literal>EnhancedUserType</literal>,
+ <literal>UserCollectionType</literal>, e <literal>UserVersionType</literal>
+ fornecem suporte para usos mais especializados.
+ </para>
+
+ <para>
+ Você pode mesmo fornecer parâmetros a um <literal>UserType</literal> no arquivo de mapeamento.
+ Para isto, seu <literal>UserType</literal> deve implementar a interface
+ <literal>org.hibernate.usertype.ParameterizedType</literal>. Para fornecer parâmetros a seu
+ tipo personalizado, você pode usar o elemento <literal><type></literal> em seus
+ arquivos de mapeamento.
+ </para>
+
+ <programlisting><![CDATA[<property name="priority">
+ <type name="com.mycompany.usertypes.DefaultValueIntegerType">
+ <param name="default">0</param>
+ </type>
+</property>]]></programlisting>
+
+ <para>
+ O <literal>UserType</literal> pode agora recuperar o valor para o parâmetro chamado
+ <literal>default</literal> da <literal>Propriedade</literal> do passado a ele.
+ </para>
+
+ <para>
+ Se você usar freqüentemente um determinado <literal>UserType</literal>, pode ser útil definir
+ um nome mais curto para ele. Você pode fazer isto usando o elemento
+ <literal><typedef></literal>. Typedefs atribui um nome a um tipo personalizado, e pode também
+ conter uma lista de valores default de parâmetro se o tipo for parametrizado.
+ </para>
+
+ <programlisting><![CDATA[<typedef class="com.mycompany.usertypes.DefaultValueIntegerType" name="default_zero">
+ <param name="default">0</param>
+</typedef>]]></programlisting>
+
+ <programlisting><![CDATA[<property name="priority" type="default_zero"/>]]></programlisting>
+
+ <para>
+ It is also possible to override the parameters supplied in a typedef on a case-by-case basis
+ by using type parameters on the property mapping.
+ </para>
+
+ <para>
+ Even though Hibernate's rich range of built-in types and support for components means you
+ will very rarely <emphasis>need</emphasis> to use a custom type, it is nevertheless
+ considered good form to use custom types for (non-entity) classes that occur frequently
+ in your application. For example, a <literal>MonetaryAmount</literal> class is a good
+ candidate for a <literal>CompositeUserType</literal>, even though it could easily be mapped
+ as a component. One motivation for this is abstraction. With a custom type, your mapping
+ documents would be future-proofed against possible changes in your way of representing
+ monetary values.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="mapping-entityname">
+ <title>Mapping a class more than once</title>
+ <para>
+ It is possible to provide more than one mapping for a particular persistent class. In this
+ case you must specify an <emphasis>entity name</emphasis> do disambiguate between instances
+ of the two mapped entities. (By default, the entity name is the same as the class name.)
+ Hibernate lets you specify the entity name when working with persistent objects, when writing
+ queries, or when mapping associations to the named entity.
+ </para>
+
+ <programlisting><![CDATA[<class name="Contract" table="Contracts"
+ entity-name="CurrentContract">
+ ...
+ <set name="history" inverse="true"
+ order-by="effectiveEndDate desc">
+ <key column="currentContractId"/>
+ <one-to-many entity-name="HistoricalContract"/>
+ </set>
+</class>
+
+<class name="Contract" table="ContractHistory"
+ entity-name="HistoricalContract">
+ ...
+ <many-to-one name="currentContract"
+ column="currentContractId"
+ entity-name="CurrentContract"/>
+</class>]]></programlisting>
+
+ <para>
+ Notice how associations are now specified using <literal>entity-name</literal> instead of
+ <literal>class</literal>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="mapping-quotedidentifiers">
+ <title>SQL quoted identifiers</title>
+ <para>
+ You may force Hibernate to quote an identifier in the generated SQL by enclosing the table or
+ column name in backticks in the mapping document. Hibernate will use the correct quotation
+ style for the SQL <literal>Dialect</literal> (usually double quotes, but brackets for SQL
+ Server and backticks for MySQL).
+ </para>
+
+ <programlisting><![CDATA[<class name="LineItem" table="`Line Item`">
+ <id name="id" column="`Item Id`"/><generator class="assigned"/></id>
+ <property name="itemNumber" column="`Item #`"/>
+ ...
+</class>]]></programlisting>
+
+ </sect1>
+
+
+ <sect1 id="mapping-alternatives">
+ <title>Metadata alternatives</title>
+
+ <para>
+ XML isn't for everyone, and so there are some alternative ways to define O/R mapping metadata in Hibernate.
+ </para>
+
+ <sect2 id="mapping-xdoclet">
+ <title>Using XDoclet markup</title>
+
+ <para>
+ Many Hibernate users prefer to embed mapping information directly in sourcecode using
+ XDoclet <literal>@hibernate.tags</literal>. We will not cover this approach in this
+ document, since strictly it is considered part of XDoclet. However, we include the
+ following example of the <literal>Cat</literal> class with XDoclet mappings.
+ </para>
+
+ <programlisting><![CDATA[package eg;
+import java.util.Set;
+import java.util.Date;
+
+/**
+ * @hibernate.class
+ * table="CATS"
+ */
+public class Cat {
+ private Long id; // identifier
+ private Date birthdate;
+ private Cat mother;
+ private Set kittens
+ private Color color;
+ private char sex;
+ private float weight;
+
+ /*
+ * @hibernate.id
+ * generator-class="native"
+ * column="CAT_ID"
+ */
+ public Long getId() {
+ return id;
+ }
+ private void setId(Long id) {
+ this.id=id;
+ }
+
+ /**
+ * @hibernate.many-to-one
+ * column="PARENT_ID"
+ */
+ public Cat getMother() {
+ return mother;
+ }
+ void setMother(Cat mother) {
+ this.mother = mother;
+ }
+
+ /**
+ * @hibernate.property
+ * column="BIRTH_DATE"
+ */
+ public Date getBirthdate() {
+ return birthdate;
+ }
+ void setBirthdate(Date date) {
+ birthdate = date;
+ }
+ /**
+ * @hibernate.property
+ * column="WEIGHT"
+ */
+ public float getWeight() {
+ return weight;
+ }
+ void setWeight(float weight) {
+ this.weight = weight;
+ }
+
+ /**
+ * @hibernate.property
+ * column="COLOR"
+ * not-null="true"
+ */
+ public Color getColor() {
+ return color;
+ }
+ void setColor(Color color) {
+ this.color = color;
+ }
+ /**
+ * @hibernate.set
+ * inverse="true"
+ * order-by="BIRTH_DATE"
+ * @hibernate.collection-key
+ * column="PARENT_ID"
+ * @hibernate.collection-one-to-many
+ */
+ public Set getKittens() {
+ return kittens;
+ }
+ void setKittens(Set kittens) {
+ this.kittens = kittens;
+ }
+ // addKitten not needed by Hibernate
+ public void addKitten(Cat kitten) {
+ kittens.add(kitten);
+ }
+
+ /**
+ * @hibernate.property
+ * column="SEX"
+ * not-null="true"
+ * update="false"
+ */
+ public char getSex() {
+ return sex;
+ }
+ void setSex(char sex) {
+ this.sex=sex;
+ }
+}]]></programlisting>
+
+ <para>
+ See the Hibernate web site for more examples of XDoclet and Hibernate.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-annotations" revision="2">
+ <title>Using JDK 5.0 Annotations</title>
+
+ <para>
+ JDK 5.0 introduced XDoclet-style annotations at the language level, type-safe and
+ checked at compile time. This mechnism is more powerful than XDoclet annotations and
+ better supported by tools and IDEs. IntelliJ IDEA, for example, supports auto-completion
+ and syntax highlighting of JDK 5.0 annotations. The new revision of the EJB specification
+ (JSR-220) uses JDK 5.0 annotations as the primary metadata mechanism for entity beans.
+ Hibernate3 implements the <literal>EntityManager</literal> of JSR-220 (the persistence API),
+ support for mapping metadata is available via the <emphasis>Hibernate Annotations</emphasis>
+ package, as a separate download. Both EJB3 (JSR-220) and Hibernate3 metadata is supported.
+ </para>
+
+ <para>
+ This is an example of a POJO class annotated as an EJB entity bean:
+ </para>
+
+ <programlisting><![CDATA[@Entity(access = AccessType.FIELD)
+public class Customer implements Serializable {
+
+ @Id;
+ Long id;
+
+ String firstName;
+ String lastName;
+ Date birthday;
+
+ @Transient
+ Integer age;
+
+ @Embedded
+ private Address homeAddress;
+
+ @OneToMany(cascade=CascadeType.ALL)
+ @JoinColumn(name="CUSTOMER_ID")
+ Set<Order> orders;
+
+ // Getter/setter and business methods
+}]]></programlisting>
+
+ <para>
+ Note that support for JDK 5.0 Annotations (and JSR-220) is still work in progress and
+ not completed. Please refer to the Hibernate Annotations module for more details.
+ </para>
+
+ </sect2>
+ </sect1>
+
+ <sect1 id="mapping-generated" revision="1">
+ <title>Generated Properties</title>
+ <para>
+ Generated properties are properties which have their values generated by the
+ database. Typically, Hibernate applications needed to <literal>refresh</literal>
+ objects which contain any properties for which the database was generating values.
+ Marking properties as generated, however, lets the application delegate this
+ responsibility to Hibernate. Essentially, whenever Hibernate issues an SQL INSERT
+ or UPDATE for an entity which has defined generated properties, it immediately
+ issues a select afterwards to retrieve the generated values.
+ </para>
+ <para>
+ Properties marked as generated must additionally be non-insertable and non-updateable.
+ Only <xref linkend="mapping-declaration-version">versions</xref>,
+ <xref linkend="mapping-declaration-timestamp">timestamps</xref>, and
+ <xref linkend="mapping-declaration-property">simple properties</xref> can be marked as
+ generated.
+ </para>
+ <para>
+ <literal>never</literal> (the default) - means that the given property value
+ is not generated within the database.
+ </para>
+ <para>
+ <literal>insert</literal> - states that the given property value is generated on
+ insert, but is not regenerated on subsequent updates. Things like created-date would
+ fall into this category. Note that even thought
+ <xref linkend="mapping-declaration-version">version</xref> and
+ <xref linkend="mapping-declaration-timestamp">timestamp</xref> properties can
+ be marked as generated, this option is not available there...
+ </para>
+ <para>
+ <literal>always</literal> - states that the property value is generated both
+ on insert and on update.
+ </para>
+ </sect1>
+
+ <sect1 id="mapping-database-object">
+ <title>Auxiliary Database Objects</title>
+ <para>
+ Allows CREATE and DROP of arbitrary database objects, in conjunction with
+ Hibernate's schema evolution tools, to provide the ability to fully define
+ a user schema within the Hibernate mapping files. Although designed specifically
+ for creating and dropping things like triggers or stored procedures, really any
+ SQL command that can be run via a <literal>java.sql.Statement.execute()</literal>
+ method is valid here (ALTERs, INSERTS, etc). There are essentially two modes for
+ defining auxiliary database objects...
+ </para>
+ <para>
+ The first mode is to explicitly list the CREATE and DROP commands out in the mapping
+ file:
+ </para>
+ <programlisting><![CDATA[<hibernate-mapping>
+ ...
+ <database-object>
+ <create>CREATE TRIGGER my_trigger ...</create>
+ <drop>DROP TRIGGER my_trigger</drop>
+ </database-object>
+</hibernate-mapping>]]></programlisting>
+ <para>
+ The second mode is to supply a custom class which knows how to construct the
+ CREATE and DROP commands. This custom class must implement the
+ <literal>org.hibernate.mapping.AuxiliaryDatabaseObject</literal> interface.
+ </para>
+ <programlisting><![CDATA[<hibernate-mapping>
+ ...
+ <database-object>
+ <definition class="MyTriggerDefinition"/>
+ </database-object>
+</hibernate-mapping>]]></programlisting>
+ <para>
+ Additionally, these database objects can be optionally scoped such that they only
+ apply when certain dialects are used.
+ </para>
+ <programlisting><![CDATA[<hibernate-mapping>
+ ...
+ <database-object>
+ <definition class="MyTriggerDefinition"/>
+ <dialect-scope name="org.hibernate.dialect.Oracle9Dialect"/>
+ <dialect-scope name="org.hibernate.dialect.OracleDialect"/>
+ </database-object>
+</hibernate-mapping>]]></programlisting>
+ </sect1>
+</chapter>
+
Property changes on: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/basic_mapping.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/batch.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/batch.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/batch.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,357 @@
+<chapter id="batch">
+ <title>Processamento de lotes</title>
+
+ <para>
+ Uma alternativa para inserir 100.000 linhas no banco de dados usando o Hibernate
+ pode ser a seguinte:
+ </para>
+
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+for ( int i=0; i<100000; i++ ) {
+ Customer customer = new Customer(.....);
+ session.save(customer);
+}
+tx.commit();
+session.close();]]></programlisting>
+
+ <para>
+ Isto irá falhar em algum lugar próximo a linha 50.000, lançando uma
+ <literal>OutOfMemoryException</literal>. Isso ocorre devido ao fato do Hibernate
+ fazer cache de todas as instâncias de <literal>Customer</literal> inseridas num
+ cachê em nÃvel de sessão.
+ </para>
+
+ <para>
+ Neste capÃtulo veremos como contornar esse problema. Entretanto, se você vai realizar
+ processamento de lotes, é muito importante que você habilite o uso de lotes JDBC, se
+ você pretende obter um desempenho razoável. Defina o tamanho do lote JDBC em um
+ valor razoável (algo entre 10-50):
+ </para>
+
+<programlisting><![CDATA[hibernate.jdbc.batch_size 20]]></programlisting>
+
+ <para>
+ Você também pode querer rodar esse tipo de processamento de lotes com o cache
+ secundário completamente desabilitado:
+ </para>
+
+<programlisting><![CDATA[hibernate.cache.use_second_level_cache false]]></programlisting>
+
+ <para>
+ Mas isto não é absolutamente necessário, desde que nós possamos ajustar o
+ <literal>CacheMode</literal> para desabilitar a interação com o cache secundário.
+ </para>
+
+ <sect1 id="batch-inserts">
+ <title>Inserção de lotes</title>
+
+ <para>
+ Quando você estiver inserindo novos objetos persistentes, vocês deve executar
+ os métodos <literal>flush()</literal> e <literal>clear()</literal> regularmente
+ na sessão, para controlar o tamanho do cache primário.
+ </para>
+
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+
+for ( int i=0; i<100000; i++ ) {
+ Customer customer = new Customer(.....);
+ session.save(customer);
+ if ( i % 20 == 0 ) { //20, same as the JDBC batch size
+ //flush a batch of inserts and release memory:
+ session.flush();
+ session.clear();
+ }
+}
+
+tx.commit();
+session.close();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="batch-update" >
+ <title>Batch updates</title>
+
+ <para>
+ Para recuperar e atualizar informações a mesma idéia é válida. Adicionalmente,
+ pode precisar usar o <literal>scroll()</literal> para usar recursos no lado
+ do servidor em queries que retornam muita informação.
+ </para>
+
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+
+ScrollableResults customers = session.getNamedQuery("GetCustomers")
+ .setCacheMode(CacheMode.IGNORE)
+ .scroll(ScrollMode.FORWARD_ONLY);
+int count=0;
+while ( customers.next() ) {
+ Customer customer = (Customer) customers.get(0);
+ customer.updateStuff(...);
+ if ( ++count % 20 == 0 ) {
+ //flush a batch of updates and release memory:
+ session.flush();
+ session.clear();
+ }
+}
+
+tx.commit();
+session.close();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="batch-statelesssession">
+ <title>A interface StatelessSession</title>
+ <para>
+ Alternativamente, o Hibernate provê uma API orientada à comandos, usada para
+ transmitir um fluxo de dados de e para o banco de dados na forma de objetos soltos.
+ Uma <literal>StatelessSession</literal> não tem um contexto persistente associado e
+ não fornece muito das semânticas de alto nÃvel para controle do ciclo de vida.
+ Em especial, uma StatelessSession não implemente o cache primário e nem interage
+ com o cache secundário ou query cache. Ele não implementa salvamento transacional
+ automatico ou checagem automática de mudanças. Operação realizadas usando uma
+ StatelessSession não fazem nenhum tipo de cascade com as instancias associadas.
+ As coleções são ignoradas por uma StatelessSession. Operações realizadas com um
+ StatelessSession ignoram a arquitetura de eventos e os interceptadores.
+ StatelessSession são vulneráveis aos efeitos do aliasing dos dados, devido a
+ falta do cache primário. Uma StatelessSession é uma abstração de baixo nÃvel,
+ muito mais próxima do JDBC.
+ </para>
+
+<programlisting><![CDATA[StatelessSession session = sessionFactory.openStatelessSession();
+Transaction tx = session.beginTransaction();
+
+ScrollableResults customers = session.getNamedQuery("GetCustomers")
+ .scroll(ScrollMode.FORWARD_ONLY);
+while ( customers.next() ) {
+ Customer customer = (Customer) customers.get(0);
+ customer.updateStuff(...);
+ session.update(customer);
+}
+
+tx.commit();
+session.close();]]></programlisting>
+
+ <para>
+ Veja neste exempo, as instancias de <literal>Customer</literal> retornadas pela query
+ são imediatamente desvinculadas. Elas nunca serão assossiadas à um contexto persistente.
+ </para>
+
+ <para>
+ As operações <literal>insert(), update()</literal> e <literal>delete()</literal>
+ definidos pela interface <literal>StatelessSession</literal> são considerados
+ operações diretas no banco de dados (row-level operations), isso resulta em uma
+ execução imediata de comandos SQL <literal>INSERT, UPDATE</literal> ou
+ <literal>DELETE</literal> respectivamente. Devido a isso, eles possuem uma
+ semântica bem diferente das operações <literal>save(), saveOrUpdate()</literal>
+ ou <literal>delete()</literal> definidas na interface <literal>Session</literal>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="batch-direct" revision="3">
+ <title>Operações no estilo DML</title>
+
+ <para>
+ Como já discutido, mapeamento objeto/relacional automático e transparente é conseguido
+ com a gerência do estado do objeto. Com isto o estado daquele objeto fica disponÃvel na
+ memória, manipulando(usando as expressões SQL <literal>Data Manipulation Language</literal>
+ (SQL-style DML): <literal>INSERT</literal>, <literal>UPDATE</literal>, <literal>DELETE</literal>)
+ os dados diretamente no banco de dados não irá afetar o estado registrado em memória.
+ Entretanto, o Hibernate provê métodos para executar queries SQL-style DML, que são
+ totalmente executas com HQL (Hibernate Query Language)
+ (<xref linkend="queryhql">HQL</xref>).
+ </para>
+
+ <para>
+ A pseudo-sintaxe para expressões <literal>UPDATE</literal> e <literal>DELETE</literal> é:
+ <literal>( UPDATE | DELETE ) FROM? NomeEntidade (WHERE condições_where)?</literal>.
+ Algumas observações:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ Na clausula from, a palavra chave FROM é opcional;
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Somente uma entidade pode ser chamada na clausula from; opcionalmente pode ter
+ um alias. Se o nome da entidade for possuir um alias, então qualquer propriedade
+ referenciada deve usar esse alias qualificado; se o nome da entidade não possuir
+ um alias, então nenhuma das propriedade precisa usar o acesso qualificado.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Na <xref linkend="queryhql-joins-forms">joins</xref> (ambas implÃcita ou explicita)
+ pode ser especificada em um bulk HQL query. Sub-queries podem ser usadas na clausula
+ where; as subqueries podem conter joins.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ A clausula where também é opcional.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Como exemplo para executar um HQL <literal>UPDATE</literal>, use o
+ método <literal>Query.executeUpdate()</literal>(o método ganhou o nome
+ devido a sua familiaridade com o do JDBC
+ <literal>PreparedStatement.executeUpdate()</literal>):
+ </para>
+
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+
+String hqlUpdate = "update Customer c set c.name = :newName where c.name = :oldName";
+// or String hqlUpdate = "update Customer set name = :newName where name = :oldName";
+int updatedEntities = s.createQuery( hqlUpdate )
+ .setString( "newName", newName )
+ .setString( "oldName", oldName )
+ .executeUpdate();
+tx.commit();
+session.close();]]></programlisting>
+
+ <para>
+ HQL <literal>UPDATE</literal> statements, by default do not effect the
+ <xref linkend="mapping-declaration-version">version</xref>
+ or the <xref linkend="mapping-declaration-timestamp">timestamp</xref> property values
+ for the affected entities; this is in keeping with the EJB3 specification. However,
+ you can force Hibernate to properly reset the <literal>version</literal> or
+ <literal>timestamp</literal> property values through the use of a <literal>versioned update</literal>.
+ This is achieved by adding the <literal>VERSIONED</literal> keyword after the <literal>UPDATE</literal>
+ keyword.
+
+ </para>
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+String hqlVersionedUpdate = "update versioned Customer set name = :newName where name = :oldName";
+int updatedEntities = s.createQuery( hqlUpdate )
+ .setString( "newName", newName )
+ .setString( "oldName", oldName )
+ .executeUpdate();
+tx.commit();
+session.close();]]></programlisting>
+
+ <para>
+ Note that custom version types (<literal>org.hibernate.usertype.UserVersionType</literal>)
+ are not allowed in conjunction with a <literal>update versioned</literal> statement.
+ </para>
+
+ <para>
+
+ Para executar um HQL <literal>DELETE</literal>, use o mesmo método
+ <literal>Query.executeUpdate()</literal>:
+ </para>
+
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+
+String hqlDelete = "delete Customer c where c.name = :oldName";
+// or String hqlDelete = "delete Customer where name = :oldName";
+int deletedEntities = s.createQuery( hqlDelete )
+ .setString( "oldName", oldName )
+ .executeUpdate();
+tx.commit();
+session.close();]]></programlisting>
+
+ <para>
+ O valor <literal>int</literal> retornado pelo método <literal>Query.executeUpdate()</literal>
+ indica o numero de entidade afetadas pela operação. Lembre-se que isso pode estar ou não
+ relacionado ao número de linhas alteradas no banco de dados. Uma operação bulk HQL pode resultar
+ em várias expressões SQL reais a serem executadas, por exemplo, no caso de joined-subclass.
+ O número retornado indica a quantidade real de entidades afetadas pela expressão. Voltando
+ ao exemplo da joined-subclass, a exclusão de uma das subclasses pode resultar numa
+ exclusão em outra tabelas, não apenas na tabela para qual a subclasses está mapeada, mas
+ também tabela "root" e possivelmente nas tabelas joined-subclass num nÃvel hierárquico
+ imediatamente abaixo.
+ </para>
+
+ <para>
+
+ A pseudo-sintaxe para o comando <literal>INSERT</literal> é:
+ <literal>INSERT INTO EntityName properties_list select_statement</literal>. Alguns
+ pontos a observar:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ Apenas a forma INSERT INTO ... SELECT ... é suportada; INSERT INTO ... VALUES ...
+ não é suportada.
+ </para>
+ <para>
+ A lista de propriedade é análoga à <literal>especificação da coluna</literal>
+ do comando SQL <literal>INSERT</literal>. Para entidades envolvidas em mapeamentos,
+ apenas a propriedades definidas diretamente a nÃvel da classe podem ser usandas na
+ properties_list. Propriedades da superclass não são permitidas; e as propriedades
+ da subclasse não faz sentido. Em outras palavras, os comandos
+ <literal>INSERT</literal> não são polimorficos.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ O camando select pode ser qualquer query HQL válida, que tenha um retorno compatÃvel
+ com o tipo com o esperado pela inclusão. Atualmente, isto é verificado durante a compilação
+ da query, isto é melhor do que permitir que a verificação chegue ao banco de dados.
+ Entretanto perceba que isso pode causar problemas entre os <literal>Tipo</literal> do Hibernate
+ que são <emphasis>equivalentes</emphasis> em oposição a <emphasis>equal</emphasis>.
+ Isso pode causar problemas nas combinações entre a propriedade definida como
+ <literal>org.hibernate.type.DateType</literal>e um propriedade definida como
+ <literal>org.hibernate.type.TimestampType</literal>, embora o banco de dados não possa
+ fazer uma distinção ou possa ser capaz de manusear a conversão.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Para a propriedade id, a expressão insert oferece duas opções. Você pode especificar
+ qualquer propriedade id explicitamente no properties_list (em alguns casos esse valor
+ é obtido diretamente da expressão select) ou pode omitir do properties_list (nesse caso,
+ um valor gerado é usado). Essa ultima opção só é válida quando são usados geradores de ids
+ que operam no banco de dados; a tentativa de usar essa opção com geradores do tipo
+ "em memória" vai causar um exceção durante a etapa de parser. Veja a finalidades desta
+ discussão, os seguintes geradores operam com o banco de dados
+ <literal>org.hibernate.id.SequenceGenerator</literal> (e suas subclasses)
+ e qualquer implementação de <literal>org.hibernate.id.PostInsertIdentifierGenerator</literal>.
+ Aqui, a exceção mais notável é o <literal>org.hibernate.id.TableHiLoGenerator</literal>, que
+ não pode ser usado porque ele não dispõe de mecanismos para recuperar o seu valor.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ For properties mapped as either <literal>version</literal> or <literal>timestamp</literal>,
+ the insert statement gives you two options. You can either specify the property in the
+ properties_list (in which case its value is taken from the corresponding select expressions)
+ or omit it from the properties_list (in which case the <literal>seed value</literal> defined
+ by the <literal>org.hibernate.type.VersionType</literal> is used).
+
+ Para propriedades mapeadas como <literal>version</literal> ou <literal>timestamp</literal>,
+ a expressão insert oferece a você duas opções. Você pode especificar a propriedade na
+ properties_list (nesse caso o seu valor é obtido a partir da expressão select correspondente)
+ ou ele pode ser omitido da properties_list (neste caso o usa o <literal>valor semente</literal>
+ definido pela classe <literal>org.hibernate.type.VersionType</literal>).
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Exemplo da execução de um HQL <literal>INSERT</literal>:
+ </para>
+
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+
+String hqlInsert = "insert into DelinquentAccount (id, name) select c.id, c.name from Customer c where ...";
+int createdEntities = s.createQuery( hqlInsert )
+ .executeUpdate();
+tx.commit();
+session.close();]]></programlisting>
+
+ </sect1>
+
+</chapter>
Property changes on: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/batch.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/best_practices.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/best_practices.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/best_practices.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,225 @@
+<chapter id="best-practices" revision="3">
+ <title>Boas práticas</title>
+
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term>Escreva classes compactas e mapeie-as usando <literal><component></literal>.</term>
+ <listitem>
+ <para>
+ Use uma classe <literal>Endereco</literal> para encapsular <literal>rua</literal>,
+ <literal>bairro</literal>, <literal>estado</literal>, <literal>CEP</literal>. Isto promove
+ a reutilização de código e simplifica o refactoring.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Declare propriedades identificadoras em classes persistentes.</term>
+ <listitem>
+ <para>
+ O Hibernate constrói propriedades identificadoras opcionais. Existem todos os tipos
+ de razões que explicam porquê você deveria utilizá-las. Nós recomendamos que os
+ identificadores sejam 'sintéticos' (gerados, sem significado para o negocio).
+
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Identifique chaves naturais.</term>
+ <listitem>
+ <para>
+ Identifique chaves naturais para todas as entidades, e mapeie-as usando
+ <literal><natural-id></literal>. Implemente <literal>equals()</literal> e
+ <literal>hashCode()</literal> para comparar as propriedades que compõem a chave natural.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Coloque cada classe de mapeamento em seu próprio arquivo.</term>
+ <listitem>
+ <para>
+ Não use um único código de mapeamento monolÃtico. Mapeie <literal>com.eg.Foo</literal>
+ no arquivo <literal>com/eg/Foo.hbm.xml</literal>. Isto promove particularmente o bom
+ senso no time de desenvolvimento.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Carregue os mapeamentos como recursos.</term>
+ <listitem>
+ <para>
+ Faça o deploy dos mapeamentos junto com as classes que eles mapeiam.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Considere externalizar as strings de consultas.</term>
+ <listitem>
+ <para>
+ Esta é uma boa prática se suas consultas chamam funções SQL que não sejam ANSI.
+ Externalizar as strings de consultas para mapear arquivos irão tornar a aplicação
+ mais portável.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Use bind variables.</term>
+ <listitem>
+ <para>
+ Assim como em JDBC, sempre substitua valores não constantes por "?". Nunca use a manipulação
+ de strings para concatenar valores não constantes em uma consulta! Até melhor, considere
+ usar parâmetros nomeados nas consultas.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Não gerencie suas conexões JDBC.</term>
+ <listitem>
+ <para>
+ O Hibernate permite que a aplicação gerencie conexões JDBC. Esta abordagem deve ser considerada
+ um último recurso. Se você não pode usar os provedores de conexão embutidos, considere fazer
+ sua implementação a partir de <literal>org.hibernate.connection.ConnectionProvider</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Considere usar tipos customizados.</term>
+ <listitem>
+ <para>
+ Suponha que você tenha um tipo Java, de alguma biblioteca, que precisa ser persistido mas não
+ provê os acessórios necessários para mapeá-lo como um componente. Você deve implementar
+ <literal>org.hibernate.UserType</literal>. Esta abordagem livra o código da aplicação de
+ implementar transformações de/para o tipo Hibernate.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term> Use código manual JDBC nos gargalos.</term>
+ <listitem>
+ <para>
+ Nas áreas de desempenho crÃtico do sistema, alguns tipos de operações podem se beneficiar do
+ uso direto do JDBC. Mas por favor, espere até você <emphasis>saber </emphasis> se é um gargalo.
+ E não suponha que o uso direto do JDBC é necessariamente mais rápido. Se você precisar usar
+ diretamente o JDBC, vale a pena abrir uma <literal>Session</literal> do Hibernate e usar uma
+ conexão JDBC. De modo que você possa ainda usar a mesma estratégia de transação e ocultar
+ o provedor a conexão
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Entenda o <literal>Session</literal> flushing.</term>
+ <listitem>
+ <para>
+ De tempos em tempos a sessão sincroniza seu estado persistente com o banco de dados. O desempenho
+ será afetado se este processo ocorrer frequentemente. Você pode algumas vezes minimizar o fluxo
+ desnecessário desabilitando o fluxo automático ou até mesmo mudando a ordem das consultas e outras
+ operações em uma transação particular.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Em uma arquitetura de três camadas, considere o uso de objetos separados.</term>
+ <listitem>
+ <para>
+ When using a servlet / session bean architecture, you could pass persistent objects loaded in
+ the session bean to and from the servlet / JSP layer. Use a new session to service each request.
+ Use <literal>Session.merge()</literal> or <literal>Session.saveOrUpdate()</literal> to
+ synchronize objects with the database.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>In a two tiered architecture, consider using long persistence contexts.</term>
+ <listitem>
+ <para>
+ Database Transactions have to be as short as possible for best scalability. However, it is often
+ neccessary to implement long running <emphasis>application transactions</emphasis>, a single
+ unit-of-work from the point of view of a user. An application transaction might span several
+ client request/response cycles. It is common to use detached objects to implement application
+ transactions. An alternative, extremely appropriate in two tiered architecture, is to maintain
+ a single open persistence contact (session) for the whole lifecycle of the application transaction
+ and simply disconnect from the JDBC connection at the end of each request and reconnect at the
+ beginning of the subsequent request. Never share a single session across more than one application
+ transaction, or you will be working with stale data.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Don't treat exceptions as recoverable.</term>
+ <listitem>
+ <para>
+ This is more of a necessary practice than a "best" practice. When an exception occurs, roll back
+ the <literal>Transaction</literal> and close the <literal>Session</literal>. If you don't, Hibernate
+ can't guarantee that in-memory state accurately represents persistent state. As a special case of this,
+ do not use <literal>Session.load()</literal> to determine if an instance with the given identifier
+ exists on the database; use <literal>Session.get()</literal> or a query instead.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Prefer lazy fetching for associations.</term>
+ <listitem>
+ <para>
+ Use eager fetching sparingly. Use proxies and lazy collections for most associations to classes that
+ are not likely to be completely held in the second-level cache. For associations to cached classes,
+ where there is an a extremely high probability of a cache hit, explicitly disable eager fetching using
+ <literal>lazy="false"</literal>. When an join fetching is appropriate to a particular use
+ case, use a query with a <literal>left join fetch</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ Use the <emphasis>open session in view</emphasis> pattern, or a disciplined
+ <emphasis>assembly phase</emphasis> to avoid problems with unfetched data.
+ </term>
+ <listitem>
+ <para>
+ Hibernate frees the developer from writing tedious <emphasis>Data Transfer Objects</emphasis> (DTO).
+ In a traditional EJB architecture, DTOs serve dual purposes: first, they work around the problem
+ that entity beans are not serializable; second, they implicitly define an assembly phase where
+ all data to be used by the view is fetched and marshalled into the DTOs before returning control
+ to the presentation tier. Hibernate eliminates the first purpose. However, you will still need
+ an assembly phase (think of your business methods as having a strict contract with the presentation
+ tier about what data is available in the detached objects) unless you are prepared to hold the
+ persistence context (the session) open across the view rendering process. This is not a limitation
+ of Hibernate! It is a fundamental requirement of safe transactional data access.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Consider abstracting your business logic from Hibernate.</term>
+ <listitem>
+ <para>
+ Hide (Hibernate) data-access code behind an interface. Combine the <emphasis>DAO</emphasis> and
+ <emphasis>Thread Local Session</emphasis> patterns. You can even have some classes persisted by
+ handcoded JDBC, associated to Hibernate via a <literal>UserType</literal>. (This advice is
+ intended for "sufficiently large" applications; it is not appropriate for an application with
+ five tables!)
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Don't use exotic association mappings.</term>
+ <listitem>
+ <para>
+ Good usecases for a real many-to-many associations are rare. Most of the time you need
+ additional information stored in the "link table". In this case, it is much better to
+ use two one-to-many associations to an intermediate link class. In fact, we think that
+ most associations are one-to-many and many-to-one, you should be careful when using any
+ other association style and ask yourself if it is really neccessary.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Prefer bidirectional associations.</term>
+ <listitem>
+ <para>
+ Unidirectional associations are more difficult to query. In a large application, almost
+ all associations must be navigable in both directions in queries.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+</chapter>
+
Property changes on: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/best_practices.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/collection_mapping.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/collection_mapping.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/collection_mapping.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1238 @@
+<chapter id="collections">
+ <title>Mapeamento de Coleções</title>
+
+ <sect1 id="collections-persistent" revision="3">
+ <title>Persistent collections</title>
+
+ <para>
+ Hibernate requires that persistent collection-valued fields be declared
+ as an interface type, for example:
+ </para>
+
+ <programlisting><![CDATA[public class Product {
+ private String serialNumber;
+ private Set parts = new HashSet();
+
+ public Set getParts() { return parts; }
+ void setParts(Set parts) { this.parts = parts; }
+ public String getSerialNumber() { return serialNumber; }
+ void setSerialNumber(String sn) { serialNumber = sn; }
+}]]></programlisting>
+
+ <para>
+ The actual interface might be <literal>java.util.Set</literal>,
+ <literal>java.util.Collection</literal>, <literal>java.util.List</literal>,
+ <literal>java.util.Map</literal>, <literal>java.util.SortedSet</literal>,
+ <literal>java.util.SortedMap</literal> or ... anything you like! (Where
+ "anything you like" means you will have to write an implementation of
+ <literal>org.hibernate.usertype.UserCollectionType</literal>.)
+ </para>
+
+ <para>
+ Notice how we initialized the instance variable with an instance of
+ <literal>HashSet</literal>. This is the best way to initialize collection
+ valued properties of newly instantiated (non-persistent) instances. When
+ you make the instance persistent - by calling <literal>persist()</literal>,
+ for example - Hibernate will actually replace the <literal>HashSet</literal>
+ with an instance of Hibernate's own implementation of <literal>Set</literal>.
+ Watch out for errors like this:
+ </para>
+
+ <programlisting><![CDATA[Cat cat = new DomesticCat();
+Cat kitten = new DomesticCat();
+....
+Set kittens = new HashSet();
+kittens.add(kitten);
+cat.setKittens(kittens);
+session.persist(cat);
+kittens = cat.getKittens(); // Okay, kittens collection is a Set
+(HashSet) cat.getKittens(); // Error!]]></programlisting>
+
+ <para>
+ The persistent collections injected by Hibernate behave like
+ <literal>HashMap</literal>, <literal>HashSet</literal>,
+ <literal>TreeMap</literal>, <literal>TreeSet</literal> or
+ <literal>ArrayList</literal>, depending upon the interface type.
+ </para>
+
+ <para>
+ Collections instances have the usual behavior of value types. They are
+ automatically persisted when referenced by a persistent object and
+ automatically deleted when unreferenced. If a collection is passed from one
+ persistent object to another, its elements might be moved from one table to
+ another. Two entities may not share a reference to the same collection
+ instance. Due to the underlying relational model, collection-valued properties
+ do not support null value semantics; Hibernate does not distinguish between
+ a null collection reference and an empty collection.
+ </para>
+
+ <para>
+ You shouldn't have to worry much about any of this. Use persistent collections
+ the same way you use ordinary Java collections. Just make sure you understand
+ the semantics of bidirectional associations (discussed later).
+ </para>
+
+ </sect1>
+
+ <sect1 id="collections-mapping" revision="4">
+ <title>Collection mappings</title>
+
+ <para>
+ The Hibernate mapping element used for mapping a collection depends upon
+ the type of the interface. For example, a <literal><set></literal>
+ element is used for mapping properties of type <literal>Set</literal>.
+ </para>
+
+ <programlisting><![CDATA[<class name="Product">
+ <id name="serialNumber" column="productSerialNumber"/>
+ <set name="parts">
+ <key column="productSerialNumber" not-null="true"/>
+ <one-to-many class="Part"/>
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ Apart from <literal><set></literal>, there is also
+ <literal><list></literal>, <literal><map></literal>,
+ <literal><bag></literal>, <literal><array></literal> and
+ <literal><primitive-array></literal> mapping elements. The
+ <literal><map></literal> element is representative:
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="mappingcollection1" coords="2 65"/>
+ <area id="mappingcollection2" coords="3 65"/>
+ <area id="mappingcollection3" coords="4 65"/>
+ <area id="mappingcollection4" coords="5 65"/>
+ <area id="mappingcollection5" coords="6 65"/>
+ <area id="mappingcollection6" coords="7 65"/>
+ <area id="mappingcollection7" coords="8 65"/>
+ <area id="mappingcollection8" coords="9 65"/>
+ <area id="mappingcollection9" coords="10 65"/>
+ <area id="mappingcollection10" coords="11 65"/>
+ <area id="mappingcollection11" coords="12 65"/>
+ <area id="mappingcollection12" coords="13 65"/>
+ <area id="mappingcollection13" coords="14 65"/>
+ <area id="mappingcollection14" coords="15 65"/>
+ </areaspec>
+ <programlisting><![CDATA[<map
+ name="propertyName"
+ table="table_name"
+ schema="schema_name"
+ lazy="true|extra|false"
+ inverse="true|false"
+ cascade="all|none|save-update|delete|all-delete-orphan|delete-orphan"
+ sort="unsorted|natural|comparatorClass"
+ order-by="column_name asc|desc"
+ where="arbitrary sql where condition"
+ fetch="join|select|subselect"
+ batch-size="N"
+ access="field|property|ClassName"
+ optimistic-lock="true|false"
+ mutable="true|false"
+ node="element-name|."
+ embed-xml="true|false"
+>
+
+ <key .... />
+ <map-key .... />
+ <element .... />
+</map>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="mappingcollection1">
+ <para>
+ <literal>name</literal> the collection property name
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection2">
+ <para>
+ <literal>table</literal> (optional - defaults to property name) the
+ name of the collection table (not used for one-to-many associations)
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection3">
+ <para>
+ <literal>schema</literal> (optional) the name of a table schema to
+ override the schema declared on the root element
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection4">
+ <para>
+ <literal>lazy</literal> (optional - defaults to <literal>true</literal>)
+ may be used to disable lazy fetching and specify that the association is
+ always eagerly fetched, or to enable "extra-lazy" fetching where most
+ operations do not initialize the collection (suitable for very large
+ collections)
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection5">
+ <para>
+ <literal>inverse</literal> (optional - defaults to <literal>false</literal>)
+ mark this collection as the "inverse" end of a bidirectional association
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection6">
+ <para>
+ <literal>cascade</literal> (optional - defaults to <literal>none</literal>)
+ enable operations to cascade to child entities
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection7">
+ <para>
+ <literal>sort</literal> (optional) specify a sorted collection with
+ <literal>natural</literal> sort order, or a given comparator class
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection8">
+ <para>
+ <literal>order-by</literal> (optional, JDK1.4 only) specify a table column (or columns)
+ that define the iteration order of the <literal>Map</literal>, <literal>Set</literal>
+ or bag, together with an optional <literal>asc</literal> or <literal>desc</literal>
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection9">
+ <para>
+ <literal>where</literal> (optional) specify an arbitrary SQL <literal>WHERE</literal>
+ condition to be used when retrieving or removing the collection (useful if the
+ collection should contain only a subset of the available data)
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection10">
+ <para>
+ <literal>fetch</literal> (optional, defaults to <literal>select</literal>) Choose
+ between outer-join fetching, fetching by sequential select, and fetching by sequential
+ subselect.
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection11">
+ <para>
+ <literal>batch-size</literal> (optional, defaults to <literal>1</literal>) specify a
+ "batch size" for lazily fetching instances of this collection.
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection12">
+ <para>
+ <literal>access</literal> (optional - defaults to <literal>property</literal>): The
+ strategy Hibernate should use for accessing the collection property value.
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection13">
+ <para>
+ <literal>optimistic-lock</literal> (optional - defaults to <literal>true</literal>):
+ Species that changes to the state of the collection results in increment of the
+ owning entity's version. (For one to many associations, it is often reasonable to
+ disable this setting.)
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection14">
+ <para>
+ <literal>mutable</literal> (optional - defaults to <literal>true</literal>):
+ A value of <literal>false</literal> specifies that the elements of the
+ collection never change (a minor performance optimization in some cases).
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <sect2 id="collections-foreignkeys" >
+ <title>Collection foreign keys</title>
+
+ <para>
+ Collection instances are distinguished in the database by the foreign key of
+ the entity that owns the collection. This foreign key is referred to as the
+ <emphasis>collection key column</emphasis> (or columns) of the collection
+ table. The collection key column is mapped by the <literal><key></literal>
+ element.
+ </para>
+
+ <para>
+ There may be a nullability constraint on the foreign key column. For most
+ collections, this is implied. For unidirectional one to many associations,
+ the foreign key column is nullable by default, so you might need to specify
+ <literal>not-null="true"</literal>.
+ </para>
+
+ <programlisting><![CDATA[<key column="productSerialNumber" not-null="true"/>]]></programlisting>
+
+ <para>
+ The foreign key constraint may use <literal>ON DELETE CASCADE</literal>.
+ </para>
+
+ <programlisting><![CDATA[<key column="productSerialNumber" on-delete="cascade"/>]]></programlisting>
+
+ <para>
+ See the previous chapter for a full definition of the <literal><key></literal>
+ element.
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-elements" >
+ <title>Collection elements</title>
+
+ <para>
+ Collections may contain almost any other Hibernate type, including all basic types,
+ custom types, components, and of course, references to other entities. This is an
+ important distinction: an object in a collection might be handled with "value"
+ semantics (its lifecycle fully depends on the collection owner) or it might be a
+ reference to another entity, with its own lifecycle. In the latter case, only the
+ "link" between the two objects is considered to be state held by the collection.
+ </para>
+
+ <para>
+ The contained type is referred to as the <emphasis>collection element type</emphasis>.
+ Collection elements are mapped by <literal><element></literal> or
+ <literal><composite-element></literal>, or in the case of entity references,
+ with <literal><one-to-many></literal> or <literal><many-to-many></literal>.
+ The first two map elements with value semantics, the next two are used to map entity
+ associations.
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-indexed">
+ <title>Indexed collections</title>
+
+ <para>
+ All collection mappings, except those with set and bag semantics, need an
+ <emphasis>index column</emphasis> in the collection table - a column that maps to an
+ array index, or <literal>List</literal> index, or <literal>Map</literal> key. The
+ index of a <literal>Map</literal> may be of any basic type, mapped with
+ <literal><map-key></literal>, it may be an entity reference mapped with
+ <literal><map-key-many-to-many></literal>, or it may be a composite type,
+ mapped with <literal><composite-map-key></literal>. The index of an array or
+ list is always of type <literal>integer</literal> and is mapped using the
+ <literal><list-index></literal> element. The mapped column contains
+ sequential integers (numbered from zero, by default).
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="index1" coords="2 45"/>
+ <area id="index2" coords="3 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<list-index
+ column="column_name"
+ base="0|1|..."/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="index1">
+ <para>
+ <literal>column_name</literal> (required): The name of the column holding the
+ collection index values.
+ </para>
+ </callout>
+ <callout arearefs="index1">
+ <para>
+ <literal>base</literal> (optional, defaults to <literal>0</literal>): The value
+ of the index column that corresponds to the first element of the list or array.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <programlistingco>
+ <areaspec>
+ <area id="mapkey1" coords="2 45"/>
+ <area id="mapkey2" coords="3 45"/>
+ <area id="mapkey3" coords="4 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<map-key
+ column="column_name"
+ formula="any SQL expression"
+ type="type_name"
+ node="@attribute-name"
+ length="N"/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="mapkey1">
+ <para>
+ <literal>column</literal> (optional): The name of the column holding the
+ collection index values.
+ </para>
+ </callout>
+ <callout arearefs="mapkey2">
+ <para>
+ <literal>formula</literal> (optional): A SQL formula used to evaluate the
+ key of the map.
+ </para>
+ </callout>
+ <callout arearefs="mapkey3">
+ <para>
+ <literal>type</literal> (reguired): The type of the map keys.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <programlistingco>
+ <areaspec>
+ <area id="indexmanytomany1" coords="2 45"/>
+ <area id="indexmanytomany2" coords="3 45"/>
+ <area id="indexmanytomany3" coords="3 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<map-key-many-to-many
+ column="column_name"
+ formula="any SQL expression"
+ class="ClassName"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="indexmanytomany1">
+ <para>
+ <literal>column</literal> (optional): The name of the foreign key
+ column for the collection index values.
+ </para>
+ </callout>
+ <callout arearefs="indexmanytomany2">
+ <para>
+ <literal>formula</literal> (optional): A SQL formula used to evaluate the
+ foreign key of the map key.
+ </para>
+ </callout>
+ <callout arearefs="indexmanytomany3">
+ <para>
+ <literal>class</literal> (required): The entity class used as the map key.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+
+ <para>
+ If your table doesn't have an index column, and you still wish to use <literal>List</literal>
+ as the property type, you should map the property as a Hibernate <emphasis><bag></emphasis>.
+ A bag does not retain its order when it is retrieved from the database, but it may be
+ optionally sorted or ordered.
+ </para>
+
+ </sect2>
+
+ <para>
+ There are quite a range of mappings that can be generated for collections, covering
+ many common relational models. We suggest you experiment with the schema generation tool
+ to get a feeling for how various mapping declarations translate to database tables.
+ </para>
+
+ <sect2 id="collections-ofvalues" revision="2">
+ <title>Collections of values and many-to-many associations</title>
+
+ <para>
+ Any collection of values or many-to-many association requires a dedicated
+ <emphasis>collection table</emphasis> with a foreign key column or columns,
+ <emphasis>collection element column</emphasis> or columns and possibly
+ an index column or columns.
+ </para>
+
+ <para>
+ For a collection of values, we use the <literal><element></literal> tag.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="element1b" coords="2 50"/>
+ <area id="element2b" coords="3 50"/>
+ <area id="element3b" coords="4 50"/>
+ </areaspec>
+ <programlisting><![CDATA[<element
+ column="column_name"
+ formula="any SQL expression"
+ type="typename"
+ length="L"
+ precision="P"
+ scale="S"
+ not-null="true|false"
+ unique="true|false"
+ node="element-name"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="element1b">
+ <para>
+ <literal>column</literal> (optional): The name of the column holding the
+ collection element values.
+ </para>
+ </callout>
+ <callout arearefs="element2b">
+ <para>
+ <literal>formula</literal> (optional): An SQL formula used to evaluate the
+ element.
+ </para>
+ </callout>
+ <callout arearefs="element3b">
+ <para>
+ <literal>type</literal> (required): The type of the collection element.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ A <emphasis>many-to-many association</emphasis> is specified using the
+ <literal><many-to-many></literal> element.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="manytomany1" coords="2 60"/>
+ <area id="manytomany2" coords="3 60"/>
+ <area id="manytomany3" coords="4 60"/>
+ <area id="manytomany4" coords="5 60"/>
+ <area id="manytomany5" coords="6 60"/>
+ <area id="manytomany6" coords="7 60"/>
+ <area id="manytomany7" coords="8 60"/>
+ <area id="manytomany8" coords="9 60"/>
+ </areaspec>
+ <programlisting><![CDATA[<many-to-many
+ column="column_name"
+ formula="any SQL expression"
+ class="ClassName"
+ fetch="select|join"
+ unique="true|false"
+ not-found="ignore|exception"
+ entity-name="EntityName"
+ property-ref="propertyNameFromAssociatedClass"
+ node="element-name"
+ embed-xml="true|false"
+ />]]></programlisting>
+ <calloutlist>
+ <callout arearefs="manytomany1">
+ <para>
+ <literal>column</literal> (optional): The name of the element foreign key column.
+ </para>
+ </callout>
+ <callout arearefs="manytomany2">
+ <para>
+ <literal>formula</literal> (optional): An SQL formula used to evaluate the element
+ foreign key value.
+ </para>
+ </callout>
+ <callout arearefs="manytomany3">
+ <para>
+ <literal>class</literal> (required): The name of the associated class.
+ </para>
+ </callout>
+ <callout arearefs="manytomany4">
+ <para>
+ <literal>fetch</literal> (optional - defaults to <literal>join</literal>):
+ enables outer-join or sequential select fetching for this association. This
+ is a special case; for full eager fetching (in a single <literal>SELECT</literal>)
+ of an entity and its many-to-many relationships to other entities, you would
+ enable <literal>join</literal> fetching not only of the collection itself,
+ but also with this attribute on the <literal><many-to-many></literal>
+ nested element.
+ </para>
+ </callout>
+ <callout arearefs="manytomany5">
+ <para>
+ <literal>unique</literal> (optional): Enable the DDL generation of a unique
+ constraint for the foreign-key column. This makes the association multiplicity
+ effectively one to many.
+ </para>
+ </callout>
+ <callout arearefs="manytomany6">
+ <para>
+ <literal>not-found</literal> (optional - defaults to <literal>exception</literal>):
+ Specifies how foreign keys that reference missing rows will be handled:
+ <literal>ignore</literal> will treat a missing row as a null association.
+ </para>
+ </callout>
+ <callout arearefs="manytomany7">
+ <para>
+ <literal>entity-name</literal> (optional): The entity name of the associated class,
+ as an alternative to <literal>class</literal>.
+ </para>
+ </callout>
+ <callout arearefs="manytomany8">
+ <para>
+ <literal>property-ref</literal>: (optional) The name of a property of the associated
+ class that is joined to this foreign key. If not specified, the primary key of
+ the associated class is used.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Some examples, first, a set of strings:
+ </para>
+
+ <programlisting><![CDATA[<set name="names" table="person_names">
+ <key column="person_id"/>
+ <element column="person_name" type="string"/>
+</set>]]></programlisting>
+
+ <para>
+ A bag containing integers (with an iteration order determined by the
+ <literal>order-by</literal> attribute):
+ </para>
+
+ <programlisting><![CDATA[<bag name="sizes"
+ table="item_sizes"
+ order-by="size asc">
+ <key column="item_id"/>
+ <element column="size" type="integer"/>
+</bag>]]></programlisting>
+
+ <para>
+ An array of entities - in this case, a many to many association:
+ </para>
+
+ <programlisting><![CDATA[<array name="addresses"
+ table="PersonAddress"
+ cascade="persist">
+ <key column="personId"/>
+ <list-index column="sortOrder"/>
+ <many-to-many column="addressId" class="Address"/>
+</array>]]></programlisting>
+
+ <para>
+ A map from string indices to dates:
+ </para>
+
+ <programlisting><![CDATA[<map name="holidays"
+ table="holidays"
+ schema="dbo"
+ order-by="hol_name asc">
+ <key column="id"/>
+ <map-key column="hol_name" type="string"/>
+ <element column="hol_date" type="date"/>
+</map>]]></programlisting>
+
+ <para>
+ A list of components (discussed in the next chapter):
+ </para>
+
+ <programlisting><![CDATA[<list name="carComponents"
+ table="CarComponents">
+ <key column="carId"/>
+ <list-index column="sortOrder"/>
+ <composite-element class="CarComponent">
+ <property name="price"/>
+ <property name="type"/>
+ <property name="serialNumber" column="serialNum"/>
+ </composite-element>
+</list>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="collections-onetomany">
+ <title>One-to-many associations</title>
+
+ <para>
+ A <emphasis>one to many association</emphasis> links the tables of two classes
+ via a foreign key, with no intervening collection table. This mapping loses
+ certain semantics of normal Java collections:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ An instance of the contained entity class may not belong to more than
+ one instance of the collection
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ An instance of the contained entity class may not appear at more than
+ one value of the collection index
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ An association from <literal>Product</literal> to <literal>Part</literal> requires
+ existence of a foreign key column and possibly an index column to the <literal>Part</literal>
+ table. A <literal><one-to-many></literal> tag indicates that this is a one to many
+ association.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="onetomany1" coords="2 60"/>
+ <area id="onetomany2" coords="3 60"/>
+ <area id="onetomany3" coords="4 60"/>
+ </areaspec>
+ <programlisting><![CDATA[<one-to-many
+ class="ClassName"
+ not-found="ignore|exception"
+ entity-name="EntityName"
+ node="element-name"
+ embed-xml="true|false"
+ />]]></programlisting>
+ <calloutlist>
+ <callout arearefs="onetomany1">
+ <para>
+ <literal>class</literal> (required): The name of the associated class.
+ </para>
+ </callout>
+ <callout arearefs="onetomany2">
+ <para>
+ <literal>not-found</literal> (optional - defaults to <literal>exception</literal>):
+ Specifies how cached identifiers that reference missing rows will be handled:
+ <literal>ignore</literal> will treat a missing row as a null association.
+ </para>
+ </callout>
+ <callout arearefs="onetomany3">
+ <para>
+ <literal>entity-name</literal> (optional): The entity name of the associated class,
+ as an alternative to <literal>class</literal>.
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Notice that the <literal><one-to-many></literal> element does not need to
+ declare any columns. Nor is it necessary to specify the <literal>table</literal>
+ name anywhere.
+ </para>
+
+ <para>
+ <emphasis>Very important note:</emphasis> If the foreign key column of a
+ <literal><one-to-many></literal> association is declared <literal>NOT NULL</literal>,
+ you must declare the <literal><key></literal> mapping
+ <literal>not-null="true"</literal> or <emphasis>use a bidirectional association</emphasis>
+ with the collection mapping marked <literal>inverse="true"</literal>. See the discussion
+ of bidirectional associations later in this chapter.
+ </para>
+
+ <para>
+ This example shows a map of <literal>Part</literal> entities by name (where
+ <literal>partName</literal> is a persistent property of <literal>Part</literal>).
+ Notice the use of a formula-based index.
+ </para>
+
+ <programlisting><![CDATA[<map name="parts"
+ cascade="all">
+ <key column="productId" not-null="true"/>
+ <map-key formula="partName"/>
+ <one-to-many class="Part"/>
+</map>]]></programlisting>
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="collections-advancedmappings">
+ <title>Advanced collection mappings</title>
+
+ <sect2 id="collections-sorted" revision="2">
+ <title>Sorted collections</title>
+
+ <para>
+ Hibernate supports collections implementing <literal>java.util.SortedMap</literal> and
+ <literal>java.util.SortedSet</literal>. You must specify a comparator in the mapping file:
+ </para>
+
+ <programlisting><![CDATA[<set name="aliases"
+ table="person_aliases"
+ sort="natural">
+ <key column="person"/>
+ <element column="name" type="string"/>
+</set>
+
+<map name="holidays" sort="my.custom.HolidayComparator">
+ <key column="year_id"/>
+ <map-key column="hol_name" type="string"/>
+ <element column="hol_date" type="date"/>
+</map>]]></programlisting>
+
+ <para>
+ Allowed values of the <literal>sort</literal> attribute are <literal>unsorted</literal>,
+ <literal>natural</literal> and the name of a class implementing
+ <literal>java.util.Comparator</literal>.
+ </para>
+
+ <para>
+ Sorted collections actually behave like <literal>java.util.TreeSet</literal> or
+ <literal>java.util.TreeMap</literal>.
+ </para>
+
+ <para>
+ If you want the database itself to order the collection elements use the
+ <literal>order-by</literal> attribute of <literal>set</literal>, <literal>bag</literal>
+ or <literal>map</literal> mappings. This solution is only available under
+ JDK 1.4 or higher (it is implemented using <literal>LinkedHashSet</literal> or
+ <literal>LinkedHashMap</literal>). This performs the ordering in the SQL query,
+ not in memory.
+ </para>
+
+ <programlisting><![CDATA[<set name="aliases" table="person_aliases" order-by="lower(name) asc">
+ <key column="person"/>
+ <element column="name" type="string"/>
+</set>
+
+<map name="holidays" order-by="hol_date, hol_name">
+ <key column="year_id"/>
+ <map-key column="hol_name" type="string"/>
+ <element column="hol_date type="date"/>
+</map>]]></programlisting>
+
+ <para>
+ Note that the value of the <literal>order-by</literal> attribute is an SQL ordering, not
+ a HQL ordering!
+ </para>
+
+ <para>
+ Associations may even be sorted by some arbitrary criteria at runtime using a collection
+ <literal>filter()</literal>.
+ </para>
+
+ <programlisting><![CDATA[sortedUsers = s.createFilter( group.getUsers(), "order by this.name" ).list();]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="collections-bidirectional" revision="1">
+ <title>Bidirectional associations</title>
+
+ <para>
+ A <emphasis>bidirectional association</emphasis> allows navigation from both
+ "ends" of the association. Two kinds of bidirectional association are
+ supported:
+
+ <variablelist>
+ <varlistentry>
+ <term>one-to-many</term>
+ <listitem>
+ <para>
+ set or bag valued at one end, single-valued at the other
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>many-to-many</term>
+ <listitem>
+ <para>
+ set or bag valued at both ends
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ </para>
+
+ <para>
+ You may specify a bidirectional many-to-many association simply by mapping two
+ many-to-many associations to the same database table and declaring one end as
+ <emphasis>inverse</emphasis> (which one is your choice, but it can not be an
+ indexed collection).
+ </para>
+
+ <para>
+ Here's an example of a bidirectional many-to-many association; each category can
+ have many items and each item can be in many categories:
+ </para>
+
+ <programlisting><![CDATA[<class name="Category">
+ <id name="id" column="CATEGORY_ID"/>
+ ...
+ <bag name="items" table="CATEGORY_ITEM">
+ <key column="CATEGORY_ID"/>
+ <many-to-many class="Item" column="ITEM_ID"/>
+ </bag>
+</class>
+
+<class name="Item">
+ <id name="id" column="CATEGORY_ID"/>
+ ...
+
+ <!-- inverse end -->
+ <bag name="categories" table="CATEGORY_ITEM" inverse="true">
+ <key column="ITEM_ID"/>
+ <many-to-many class="Category" column="CATEGORY_ID"/>
+ </bag>
+</class>]]></programlisting>
+
+ <para>
+ As mudanças feitas somente de um lado da associação <emphasis>não</emphasis> são
+ persistidas. Isto significa que o Hibernate tem duas representações na memória
+ para cada associação bidirecional, uma associação de A para B e uma outra
+ associação de B para A. Isto é mais fácil de compreender se você pensa
+ sobre o modelo do objetos do Java e como nós criamos um relacionamento muitos
+ para muitos em Java:
+ </para>
+
+ <programlisting><![CDATA[
+category.getItems().add(item); // The category now "knows" about the relationship
+item.getCategories().add(category); // The item now "knows" about the relationship
+
+session.persist(item); // The relationship won't be saved!
+session.persist(category); // The relationship will be saved]]></programlisting>
+
+ <para>
+ A outra ponta é usada salvar a representação em memória à base de dados.
+ </para>
+
+ <para>
+ Você pode definir uma associação bidirecional um para muitos através de uma associação
+ um-para-muitos indicando as mesmas colunas da tabela que à associação muitos-para-um e
+ declarando a propriedade <literal>inverse="true"</literal>
+
+ </para>
+
+ <programlisting><![CDATA[<class name="Parent">
+ <id name="id" column="parent_id"/>
+ ....
+ <set name="children" inverse="true">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+ </set>
+</class>
+
+<class name="Child">
+ <id name="id" column="child_id"/>
+ ....
+ <many-to-one name="parent"
+ class="Parent"
+ column="parent_id"
+ not-null="true"/>
+</class>]]></programlisting>
+
+ <para>
+ Mapear apenas uma das pontas da associação com <literal>inverse="true"</literal>
+ não afeta as operações em cascata, isso é um conceito ortogonal.
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-indexedbidirectional">
+ <title>Associações bidirecionais com coleções indexadas</title>
+ <para>
+ Uma associação bidirecional onde uma dos lados e representa pôr uma
+ <literal><list></literal> ou <literal><map></literal> requer uma consideração
+ especial. Se houver uma propriedade da classe filha que faça o mapeamento da coluna do
+ Ãndice, sem problema, pode-se continuar usando <literal>inverse="true"</literal> no
+ mapeamento da coleção.
+ </para>
+
+ <programlisting><![CDATA[<class name="Parent">
+ <id name="id" column="parent_id"/>
+ ....
+ <map name="children" inverse="true">
+ <key column="parent_id"/>
+ <map-key column="name"
+ type="string"/>
+ <one-to-many class="Child"/>
+ </map>
+</class>
+
+<class name="Child">
+ <id name="id" column="child_id"/>
+ ....
+ <property name="name"
+ not-null="true"/>
+ <many-to-one name="parent"
+ class="Parent"
+ column="parent_id"
+ not-null="true"/>
+</class>]]></programlisting>
+
+ <para>
+ Mas, se não houver nenhuma propriedade na classe filha, não podemos ver essa associação
+ como verdadeiramente bidirecional (há uma informação disponÃvel em um lado da associação
+ que não está disponÃvel no extremo oposto). Nesse caso, nos não podemos mapear a coleção
+ usando <literal>inverse="true"</literal>. Nos devemos usar o seguinte mapeamento:
+ </para>
+
+ <programlisting><![CDATA[<class name="Parent">
+ <id name="id" column="parent_id"/>
+ ....
+ <map name="children">
+ <key column="parent_id"
+ not-null="true"/>
+ <map-key column="name"
+ type="string"/>
+ <one-to-many class="Child"/>
+ </map>
+</class>
+
+<class name="Child">
+ <id name="id" column="child_id"/>
+ ....
+ <many-to-one name="parent"
+ class="Parent"
+ column="parent_id"
+ insert="false"
+ update="false"
+ not-null="true"/>
+</class>]]></programlisting>
+
+ <para>
+ Veja que neste mapeamento, que um dos lado da associação, a coleção, é responsável pela
+ atualização da chave estrangeira. TODO: Isso realmente resulta em updates desnecessários ?.
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-ternary">
+ <title>Associações Ternárias</title>
+
+ <para>
+ Há três meios possÃveis de se mapear uma associação ternária. Uma é usar um
+ <literal>Map</literal> com uma associação como seu Ãndice:
+ </para>
+
+ <programlisting><![CDATA[<map name="contracts">
+ <key column="employer_id" not-null="true"/>
+ <map-key-many-to-many column="employee_id" class="Employee"/>
+ <one-to-many class="Contract"/>
+</map>]]></programlisting>
+
+ <programlisting><![CDATA[<map name="connections">
+ <key column="incoming_node_id"/>
+ <map-key-many-to-many column="outgoing_node_id" class="Node"/>
+ <many-to-many column="connection_id" class="Connection"/>
+</map>]]></programlisting>
+
+ <para>
+ A segunda maneira é simplesmente remodelar a associação das classes da entidade.
+ Esta é a maneira que nós usamos de uma maneira geral.
+ </para>
+
+ <para>
+ Uma alternativa final é usar os elementos compostos, que nós
+ discutiremos mais tarde.
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-idbag" revision="1">
+ <title><literal>Usando o <idbag></literal></title>
+
+ <para>
+ Se você concorda com nossa visão que chaves compostas são uma coisa ruim e que
+ as entidades devem ter identificadores sintéticos (surrogate keys), então você
+ deve estar achando um pouco estranho que as associações muitos para muitos
+ usando coleções de valores que nós mostramos estejam mapeadas com chaves
+ compostas! Bem, este ponto é bastante discutÃvel; um simples tabela de associação
+ não parece se beneficiar muito de uma surrogate key (entretanto uma coleção de
+ valores compostos <emphasis>sim</emphasis>). Opcionalmente, o Hibernate prove
+ uma maneira de mapear uma associação muitos para muitos com uma coleção
+ de valores para uma tabela com uma surrogate key.
+ </para>
+
+ <para>
+ O elemento <literal><idbag></literal> permite mapear um <literal>List</literal>
+ (ou uma <literal>Collection</literal> com uma semântica de bag.
+ </para>
+
+<programlisting><![CDATA[<idbag name="lovers" table="LOVERS">
+ <collection-id column="ID" type="long">
+ <generator class="sequence"/>
+ </collection-id>
+ <key column="PERSON1"/>
+ <many-to-many column="PERSON2" class="Person" fetch="join"/>
+</idbag>]]></programlisting>
+
+ <para>
+ Como você pode ver, um <literal><idbag></literal> possui um gerador de id
+ sintético, igual uma classe de entidade! Uma surrogate key diferente é associada
+ para cada elemento de coleção. Porém, o Hibernate não prove nenhum mecanismo
+ para descobrir qual a surrogate key de uma linha em particular.
+ </para>
+
+ <para>
+ Note que o desempenho de atualização de um <literal><idbag></literal> é
+ <emphasis>much</emphasis> melhor que um <literal><bag></literal> normal!
+ O Hibernate pode localizar uma linha individual eficazmente e atualizar ou
+ deletar individualmente, como um list, map ou set.
+ </para>
+
+ <para>
+ Na implementação atual, a estratégia de geração de identificador <literal>native</literal>
+ não é suportada para identificadores de coleção usando o <literal><idbag></literal>.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <!--undocumenting this stuff -->
+
+ <!--sect1 id="collections-heterogeneous">
+ <title>Heterogeneous Associations</title>
+
+ <para>
+ The <literal><many-to-any></literal> and <literal><index-many-to-any></literal>
+ elements provide for true heterogeneous associations. These mapping elements work in the
+ same way as the <literal><any></literal> element - and should also be used
+ rarely, if ever.
+ </para>
+
+ </sect1-->
+
+ <sect1 id="collections-example" revision="1">
+ <title>Exemplos de coleções</title>
+
+ <para>
+ As seções anteriores são uma grande confusão. Assim sendo vejamos uma exemplo.
+ Essa classe:
+ </para>
+
+ <programlisting><![CDATA[package eg;
+import java.util.Set;
+
+public class Parent {
+ private long id;
+ private Set children;
+
+ public long getId() { return id; }
+ private void setId(long id) { this.id=id; }
+
+ private Set getChildren() { return children; }
+ private void setChildren(Set children) { this.children=children; }
+
+ ....
+ ....
+}]]></programlisting>
+
+ <para>
+ tem uma coleção de instancias de <literal>Child</literal>. Se
+ cada Child tiver no máximo um parent, o mapeamento natural é
+ uma associação um para muitos:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Parent">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <set name="children">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+ </set>
+ </class>
+
+ <class name="Child">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Esse mapeamento gera a seguinte definição de tabelas
+ </para>
+
+ <programlisting><![CDATA[create table parent ( id bigint not null primary key )
+create table child ( id bigint not null primary key, name varchar(255), parent_id bigint )
+alter table child add constraint childfk0 (parent_id) references parent]]></programlisting>
+
+ <para>
+ Se o parent for <emphasis>obrigatório</emphasis>, use uma associação bidirecional
+ um para muitos:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Parent">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <set name="children" inverse="true">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+ </set>
+ </class>
+
+ <class name="Child">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <property name="name"/>
+ <many-to-one name="parent" class="Parent" column="parent_id" not-null="true"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Repare na constraint <literal>NOT NULL</literal>:
+ </para>
+
+ <programlisting><![CDATA[create table parent ( id bigint not null primary key )
+create table child ( id bigint not null
+ primary key,
+ name varchar(255),
+ parent_id bigint not null )
+alter table child add constraint childfk0 (parent_id) references parent]]></programlisting>
+
+ <para>
+ Uma outra alternativa, no caso de você insistir que esta associação devea ser
+ unidirecional, você pode declarar a constraint como <literal>NOT NULL</literal>
+ na tag <literal><key></literal> do mapeamento:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Parent">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <set name="children">
+ <key column="parent_id" not-null="true"/>
+ <one-to-many class="Child"/>
+ </set>
+ </class>
+
+ <class name="Child">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Por outro lado, se uma child puder ter os múltiplos parents, a associação
+ apropriada é muitos-para-muitos:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Parent">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <set name="children" table="childset">
+ <key column="parent_id"/>
+ <many-to-many class="Child" column="child_id"/>
+ </set>
+ </class>
+
+ <class name="Child">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Definições das tabelas:
+ </para>
+
+ <programlisting><![CDATA[create table parent ( id bigint not null primary key )
+create table child ( id bigint not null primary key, name varchar(255) )
+create table childset ( parent_id bigint not null,
+ child_id bigint not null,
+ primary key ( parent_id, child_id ) )
+alter table childset add constraint childsetfk0 (parent_id) references parent
+alter table childset add constraint childsetfk1 (child_id) references child]]></programlisting>
+
+ <para>
+ For more examples and a complete walk-through a parent/child relationship mapping,
+ see <xref linkend="example-parentchild"/>.
+ Para mais exemplos e um exemplo completo de mapeamento de relacionamento
+ de mestre/detalhe, veja <xref linkend="example-parentchild"/>.
+ </para>
+
+ <para>
+ Até mesmo o mapeamento de associações mais exóticas são possÃveis, nós catalogaremos
+ todas as possibilidades no próximo capÃtulo.
+ </para>
+
+ </sect1>
+
+</chapter>
Property changes on: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/collection_mapping.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/component_mapping.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/component_mapping.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/component_mapping.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,413 @@
+<chapter id="components">
+ <title>Mapeamento de Componentes</title>
+
+ <para>
+ A noção de <emphasis>componente</emphasis> é reusada em vários contextos diferentes,
+ para propósitos diferentes, pelo Hibernate.
+
+
+ </para>
+
+ <sect1 id="components-dependentobjects" revision="2" >
+ <title>Objetos dependentes</title>
+
+ <para>
+ Um componente é um objeto contido que é persistido como um tipo de valor, não uma
+ referência de entidade. O termo "componente" refere-se à noção de composição da
+ orientação a objetos (não a componentes no nÃvel de arquitetura). Por exemplo, você
+ pode modelar uma pessoa desta maneira:
+ </para>
+
+ <programlisting><![CDATA[public class Person {
+ private java.util.Date birthday;
+ private Name name;
+ private String key;
+ public String getKey() {
+ return key;
+ }
+ private void setKey(String key) {
+ this.key=key;
+ }
+ public java.util.Date getBirthday() {
+ return birthday;
+ }
+ public void setBirthday(java.util.Date birthday) {
+ this.birthday = birthday;
+ }
+ public Name getName() {
+ return name;
+ }
+ public void setName(Name name) {
+ this.name = name;
+ }
+ ......
+ ......
+}]]></programlisting>
+
+<programlisting><![CDATA[public class Name {
+ char initial;
+ String first;
+ String last;
+ public String getFirst() {
+ return first;
+ }
+ void setFirst(String first) {
+ this.first = first;
+ }
+ public String getLast() {
+ return last;
+ }
+ void setLast(String last) {
+ this.last = last;
+ }
+ public char getInitial() {
+ return initial;
+ }
+ void setInitial(char initial) {
+ this.initial = initial;
+ }
+}]]></programlisting>
+
+ <para>
+ Agora <literal>Name</literal> pode ser persistido como um componente
+ de <literal>Person</literal>. Note que <literal>Name</literal> define métodos
+ getter e setter para suas propriedades persistentes, mas não necessita declarar
+ nenhuma interface ou propriedades identificadoras.
+ </para>
+
+ <para>
+ Nosso mapeamento do Hibernate seria semelhante a isso
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Person" table="person">
+ <id name="Key" column="pid" type="string">
+ <generator class="uuid"/>
+ </id>
+ <property name="birthday" type="date"/>
+ <component name="Name" class="eg.Name"> <!-- class attribute optional -->
+ <property name="initial"/>
+ <property name="first"/>
+ <property name="last"/>
+ </component>
+</class>]]></programlisting>
+
+ <para>
+ A tabela pessoa teria as seguintes colunas <literal>pid</literal>,
+ <literal>birthday</literal>,
+ <literal>initial</literal>,
+ <literal>first</literal> and
+ <literal>last</literal>.
+ </para>
+
+ <para>
+ Assim como todos tipos por valor, componentes não suportam referencias cruzadas. Em
+ outras palavras, duas pessoas poderiam possuir o mesmo nome, mas os dois objetos
+ pessoa poderiam conter dois objetos nome independentes, apenas com "o mesmo" por valor.
+ A semântica dos valores null de um componente são <emphasis>ad hoc</emphasis>. No
+ recarregameno do conteúdo do objeto, O Hibernate assumira que se todas as colunas
+ do componente são null, então todo o componente é null. Isto seria o certo para
+ a maioria dos propósitos.
+ </para>
+
+ <para>
+ As propriedades de um componente podem ser de qualquer tipo do Hibernate(collections,
+ associações muitos-para-um, outros componentes, etc). Componentes agrupados
+ <emphasis>não </emphasis> devem ser consideros um uso exótico. O Hibernate tem
+ a intenção de suportar um modelo de objetos muito bem granulado.
+ </para>
+
+ <para>
+ O elemento <literal><component></literal> permite um subelemento
+ <literal><parent></literal> que mapeia uma propriedade da classe componente
+ como uma referência de volta para a entidade que a contém.
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Person" table="person">
+ <id name="Key" column="pid" type="string">
+ <generator class="uuid"/>
+ </id>
+ <property name="birthday" type="date"/>
+ <component name="Name" class="eg.Name" unique="true">
+ <parent name="namedPerson"/> <!-- reference back to the Person -->
+ <property name="initial"/>
+ <property name="first"/>
+ <property name="last"/>
+ </component>
+</class>]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="components-incollections" revision="1">
+ <title>Collections de objetos dependentes</title>
+
+ <para>
+ Collections de componentes são suportadadas(ex. uma array de
+ tipo <literal>Name</literal>). Declare a sua collection de
+ componentes substituindo a tag<literal><element></literal>
+ pela tag <literal><composite-element></literal>.
+ </para>
+
+ <programlisting><![CDATA[<set name="someNames" table="some_names" lazy="true">
+ <key column="id"/>
+ <composite-element class="eg.Name"> <!-- class attribute required -->
+ <property name="initial"/>
+ <property name="first"/>
+ <property name="last"/>
+ </composite-element>
+</set>]]></programlisting>
+
+ <para>
+ Nota: se você definir um <literal>Set</literal> de elementos compostos,
+ é muito importante implementar <literal>equals()</literal> e
+ <literal>hashCode()</literal> corretamente.
+ </para>
+
+ <para>
+ Elementos compostos podem conter componentes mas não collections. Se o
+ seu elemento composto contiver componentes , use a tag
+ <literal><nested-composite-element></literal> . Este é um caso
+ bastante exótico â uma collections de componentes que por si própria
+ possui componentes. Neste momento você deve estar se perguntando se
+ uma associação de um-para-muitos seria mais apropriada. Tente remodelar
+ o elemento composto como uma entidade â mas note que mesmo pensando
+ que o modelo Java é o mesmo, o modelo relacional e a semântica de
+ persistência ainda são diferentes.
+ </para>
+
+ <para>
+ Por favor, note que um mapeamento de elemento composto não suporta propriedades
+ capazes de serem null se você estiver usando um <literal><set></literal>.
+ O Hibernate tem que usar cada valor das colunas para identificar um registro
+ quando estiver deletando objetos (não existe coluna chave primária separada na
+ tabela de elemento composto), que não é possÃvel com valores null.
+ Você tem que usar um dos dois, ou apenas propriedades não null em um elemento
+ composto ou escolher uma <literal><list></literal>,
+ <literal><map></literal>, <literal><bag></literal> ou
+ <literal><idbag></literal>.
+ </para>
+
+ <para>
+ Um caso especial de elemento composto é um elemento composto com um elemento
+ <literal><many-to-one></literal> aninhado. Um mapeamento como este
+ permite você a mapear colunas extras de uma tabela de associação de
+ muitos-para-muitos para a classe de elemento composto. A seguinte
+ associação de muitos-para-muitos de <literal>Order</literal> para um
+ <literal>Item</literal> onde <literal>purchaseDate</literal>,
+ <literal>price</literal> e <literal>quantity</literal> são propriedades
+ da associação:
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Order" .... >
+ ....
+ <set name="purchasedItems" table="purchase_items" lazy="true">
+ <key column="order_id">
+ <composite-element class="eg.Purchase">
+ <property name="purchaseDate"/>
+ <property name="price"/>
+ <property name="quantity"/>
+ <many-to-one name="item" class="eg.Item"/> <!-- class attribute is optional -->
+ </composite-element>
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ Claro, que não pode ter uma referência para purchase no outro lado, para a
+ navegação da associação bidirecional. Lembre-se que componentes são tipos por
+ valor e não permitem referências compartilhadas. Uma classe <literal>Purchase</literal>
+ simples pode estar no set de uma classe <literal>Order</literal>, mas ela não
+ pode ser referenciada por <literal>Item</literal> no mesmo momento.
+ </para>
+
+ <para>Até mesmo associações ternárias (ou quaternária, etc) são possÃveis:</para>
+
+
+ <programlisting><![CDATA[<class name="eg.Order" .... >
+ ....
+ <set name="purchasedItems" table="purchase_items" lazy="true">
+ <key column="order_id">
+ <composite-element class="eg.OrderLine">
+ <many-to-one name="purchaseDetails class="eg.Purchase"/>
+ <many-to-one name="item" class="eg.Item"/>
+ </composite-element>
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ Elementos compostos podem aparecer em pesquisas usando a mesma
+ sintaxe assim como associações para outras entidades.
+ </para>
+
+ </sect1>
+
+ <sect1 id="components-asmapindex">
+ <title>Componentes como Ãndices de Map</title>
+
+ <para>
+ O elemento <literal><composite-map-key></literal> permite você mapear
+ uma classe componente como uma chave de um <literal>Map</literal>. Tenha
+ certeza que você sobrescreveu <literal>hashCode()</literal> e
+ <literal>equals()</literal> corretamente na classe componente.
+ </para>
+ </sect1>
+
+ <sect1 id="components-compositeid" revision="1">
+ <title>. Componentes como identificadores compostos</title>
+
+ <para>
+ Você pode usar um componente como um identificador de uma classe entidade.
+ Sua classe componente deve satisfazer certos requisitos:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ Ele deve implementar <literal>java.io.Serializable</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Ele deve re-implementar <literal>equals()</literal> e
+ <literal>hashCode()</literal>, consistentemente com a noção
+ de igualdade de chave composta do banco de dados.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ <emphasis>Nota: no Hibernate 3, o segundo requisito não é um requisito absolutamente
+ necessário. Mas atenda ele de qualquer forma.</emphasis>
+ </para>
+
+ <para>
+ Você não pode usar um <literal>IdentifierGenerator</literal> para gerar chaves compostas.
+ Ao invés disso o aplicativo deve gerenciar seus próprios identificadores.
+ </para>
+
+ <para>
+ Use a tag <literal><composite-id></literal> (com elementos
+ <literal><key-property></literal> aninhados) no lugar da declaração
+ <literal><id></literal> de costume. Por exemplo, a classe
+ <literal>OrderLine</literal> possui uma chave primária que depende da
+ chave primária (composta) de <literal>Order</literal>.
+ </para>
+
+ <programlisting><![CDATA[<class name="OrderLine">
+
+ <composite-id name="id" class="OrderLineId">
+ <key-property name="lineId"/>
+ <key-property name="orderId"/>
+ <key-property name="customerId"/>
+ </composite-id>
+
+ <property name="name"/>
+
+ <many-to-one name="order" class="Order"
+ insert="false" update="false">
+ <column name="orderId"/>
+ <column name="customerId"/>
+ </many-to-one>
+ ....
+
+</class>]]></programlisting>
+
+ <para>
+ Agora, qualquer chave estrangeira referenciando a tabela <literal>OrderLine</literal>
+ também serão compostas. Você deve declarar isto em seus mapeamentos para outras
+ classes. Uma associação para <literal>OrderLine</literal> seria mapeada dessa forma:
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="orderLine" class="OrderLine">
+<!-- the "class" attribute is optional, as usual -->
+ <column name="lineId"/>
+ <column name="orderId"/>
+ <column name="customerId"/>
+</many-to-one>]]></programlisting>
+
+ <para>
+ (Note que a tag <literal><column></literal> é uma alternativa para o
+ atributo <literal>column</literal> por toda a parte.)
+
+
+ </para>
+
+ <para>
+ Uma associação <literal>many-to-many</literal> para <literal>many-to-many</literal>
+ também usa a chave estrangeira composta:
+ </para>
+
+ <programlisting><![CDATA[<set name="undeliveredOrderLines">
+ <key column name="warehouseId"/>
+ <many-to-many class="OrderLine">
+ <column name="lineId"/>
+ <column name="orderId"/>
+ <column name="customerId"/>
+ </many-to-many>
+</set>]]></programlisting>
+
+ <para>
+ A collection de <literal>OrderLine</literal>s em <literal>Order</literal> usaria:
+ </para>
+
+ <programlisting><![CDATA[<set name="orderLines" inverse="true">
+ <key>
+ <column name="orderId"/>
+ <column name="customerId"/>
+ </key>
+ <one-to-many class="OrderLine"/>
+</set>]]></programlisting>
+
+ <para>
+ (O elemento <literal><one-to-many></literal>, como de costume, não declara colunas.)
+
+
+ </para>
+
+ <para>
+ Se <literal>OrderLine</literal> possui uma collection, ela também tem uma
+ chave estrangeira composta.
+ </para>
+
+ <programlisting><![CDATA[<class name="OrderLine">
+ ....
+ ....
+ <list name="deliveryAttempts">
+ <key> <!-- a collection inherits the composite key type -->
+ <column name="lineId"/>
+ <column name="orderId"/>
+ <column name="customerId"/>
+ </key>
+ <list-index column="attemptId" base="1"/>
+ <composite-element class="DeliveryAttempt">
+ ...
+ </composite-element>
+ </set>
+</class>]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="components-dynamic" revision="1">
+ <title>Componentes Dinâmicos</title>
+
+ <para>
+ Você pode até mesmo mapear uma propriedade do tipo <literal>Map</literal>:
+ </para>
+
+ <programlisting><![CDATA[<dynamic-component name="userAttributes">
+ <property name="foo" column="FOO" type="string"/>
+ <property name="bar" column="BAR" type="integer"/>
+ <many-to-one name="baz" class="Baz" column="BAZ_ID"/>
+</dynamic-component>]]></programlisting>
+
+ <para>
+ A semântica de um mapeamento <literal><dynamic-component></literal> é idêntica
+ à <literal><component></literal>. A vantagem deste tipo de mapeamento é a
+ habilidade de determinar as propriedades atuais do bean no momento de deploy,
+ apenas editando o documento de mapeamento. A Manipulação em tempo de execução do
+ documento de mapeamento também é possÃvel, usando o parser DOM. Até melhor,
+ você pode acessar (e mudar) o metamodelo configuration-time do Hibernate
+ através do objeto <literal>Configuration</literal>.
+ </para>
+
+ </sect1>
+
+</chapter>
Property changes on: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/component_mapping.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/configuration.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/configuration.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/configuration.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1789 @@
+<chapter id="session-configuration" revision="1">
+
+ <title>Configuração</title>
+
+ <para>
+ Devido ao fato de o Hibernate ser projetado para operar em vários ambientes diferentes,
+ há um grande número de parâmetros de configuração. Felizmente, a maioria tem valores default
+ lógicos e o Hibernate é distribuÃdo com um arquivo <literal>hibernate.properties</literal>
+ de exemplo no <literal>etc/</literal> que mostra várias opções. Apenas coloque o arquivo
+ de exemplo no seu classpath e personalize-o.
+ </para>
+
+ <sect1 id="configuration-programmatic" revision="1">
+ <title>1.11 Configuração programática</title>
+
+ <para>
+ Uma instância de <literal>org.hibernate.cfg.Configuration</literal>
+ representa um conjunto inteiro de mapeamentos de tipos Java da aplicação para
+ um banco de dados SQL. O <literal>Configuration</literal> é usado para construir
+ uma <literal>SessionFactory</literal> (imutável). Os mapeamentos são compilados
+ a partir de arquivos de mapeamento XML.
+
+ </para>
+
+ <para>
+ Você pode obter uma instância <literal>Configuration</literal> intanciando-
+ o diretamente e especificando documentos de mapeamento XML. Se o arquivo
+ de mapeamento estão no classpath, use use <literal>addResource()</literal>:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = new Configuration()
+ .addResource("Item.hbm.xml")
+ .addResource("Bid.hbm.xml");]]></programlisting>
+
+ <para>
+ Uma alternativa (às vezes melhor) é especificar a classe mapeada,
+ e permitir que o Hibernate encontre o documento de mapeamento para você:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = new Configuration()
+ .addClass(org.hibernate.auction.Item.class)
+ .addClass(org.hibernate.auction.Bid.class);]]></programlisting>
+
+ <para>
+ Então o Hibernate procurará pelos arquivos de mapeamento chamados
+ <literal>/org/hibernate/auction/Item.hbm.xml</literal> e
+ <literal>/org/hibernate/auction/Bid.hbm.xml</literal> no classpath.
+ Esta abordagem elimina qualquer nome de arquivo de difÃcil compreensão.
+ </para>
+
+ <para>
+ Uma <literal>Configuration</literal> também permite você especificar
+ propriedades de configuração:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = new Configuration()
+ .addClass(org.hibernate.auction.Item.class)
+ .addClass(org.hibernate.auction.Bid.class)
+ .setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLInnoDBDialect")
+ .setProperty("hibernate.connection.datasource", "java:comp/env/jdbc/test")
+ .setProperty("hibernate.order_updates", "true");]]></programlisting>
+
+ <para>
+ Este não é o único caminho para passar as propriedades de configuração
+ para o Hibernate. As várias opções incluem:
+ </para>
+
+ <orderedlist spacing="compact">
+ <listitem>
+ <para>
+ Passar uma instância de <literal>java.util.Properties</literal>
+ para <literal>Configuration.setProperties()</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Colocar <literal>hibernate.properties</literal> no diretório
+ raiz do classpath.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Determinar as propriedades do <literal>System</literal>
+ usando <literal>java -Dproperty=value</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Include <literal><property></literal> elements in
+ <literal>hibernate.cfg.xml</literal> (discussed later).
+ Incluir elementos <literal><property></literal> no
+ <literal>hibernate.cfg.xml</literal> (discutido mais tarde).
+ </para>
+ </listitem>
+ </orderedlist>
+
+ <para>
+ <literal>hibernate.properties</literal> é o caminho mais facil
+ se você quer começar mais rápido.
+ </para>
+
+ <para>
+ O <literal>Configuration</literal> é entendido como um objeto startup-time,
+ é descartado uma vez que a <literal>SessionFactory</literal> é criada.
+ </para>
+
+ </sect1>
+
+ <sect1 id="configuration-sessionfactory">
+ <title>Obtendo uma SessionFactory</title>
+
+ <para>
+ Quando todos os mapeamentos têm sido analisados pelo <literal>Configuration</literal>,
+ a aplicação deve obter uma factory para as instâncias da <literal>Session</literal>.
+ O objetivo desta factory é ser compartilhado por todas as threads da aplicação:
+ </para>
+
+ <programlisting><![CDATA[SessionFactory sessions = cfg.buildSessionFactory();]]></programlisting>
+
+ <para>
+ Hibernate permite sua aplicação instanciar mais do que uma
+ <literal>SessionFactory</literal>. Isto é útil se você está usando mais
+ do que um banco de dados.
+ </para>
+
+ </sect1>
+
+ <sect1 id="configuration-hibernatejdbc" revision="1">
+ <title>Conexões JDBC</title>
+
+ <para>
+ Normalmente, você quer mandar criar a <literal>SessionFactory</literal> criar um
+ pool de conexões JDBC para você. Se você seguir essa abordagem, a abertura de uma
+ <literal>Session</literal> é tão simples quanto:
+
+ </para>
+
+ <programlisting><![CDATA[Session session = sessions.openSession(); // open a new Session]]></programlisting>
+
+ <para>
+ Assim que você fizer algo que requer o acesso ao banco de dados, uma
+ conexão JDBC será obtida do pool.
+ </para>
+
+ <para>
+ Para esse trabalho, nós necessitamos passar algumas propriedades da conexão JDBC
+ para o Hibernate. Todos os nomes de propriedades Hibernate e semânticas são definidas
+ <literal>org.hibernate.cfg.Environment</literal>. Nós iremos descrever agora
+ o mais importantes configurações de conexão JDBC.
+ </para>
+
+ <para>
+ O Hibernate obterá conexões( e pool) usando <literal>java.sql.DriverManager</literal>
+ se você determinar as seguintes propriedades:
+ </para>
+
+ <table frame="topbot">
+ <title>Propriedades JDBC Hibernate</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Nome da Propriedade</entry>
+ <entry>Propósito</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.connection.driver_class</literal>
+ </entry>
+ <entry>
+ <emphasis>Classe driver jdbc</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.url</literal>
+ </entry>
+ <entry>
+ <emphasis>URL jdbc</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.username</literal>
+ </entry>
+ <entry>
+ <emphasis>Usuário do banco de dados</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.password</literal>
+ </entry>
+ <entry>
+ <emphasis>Senha do usuário do banco de dados</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.pool_size</literal>
+ </entry>
+ <entry>
+ <emphasis>Número máximo de connecxões no pool</emphasis>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ O algoritmo de pool de conexões do próprio Hibernate entretanto é completamente
+ rudimentar. A intenção dele e ajudar a iniciar e <emphasis>não para usar em um
+ sistema de produção</emphasis> ou até para testar desempenho. Você deveria usar
+ uma ferramente de pool de terceiros para conseguir melhor desempenho e estabilidade.
+ Apenas especifique a propriedade <literal>hibernate.connection.pool_size</literal>
+ com a definição do pool de conexões. Isto irá desligar o pool interno do Hibernate.
+ Por exemplo, você pode gostar de usar C3P0.
+ </para>
+
+ <para>
+ O C3P0 é um pool conexão JDBC de código aberto distribuÃdo junto com
+ Hibernate no diretório <literal>lib</literal>. O Hibernate usará o
+ <literal>C3P0ConnectionProvider</literal> para o pool de conexão se
+ você configurar a propriedade <literal>hibernate.c3p0.*</literal>. Se você
+ gostar de usar Proxool consulte ao pacote <literal>hibernate.properties</literal>
+ e o web site do Hibernate para mais informações.
+ </para>
+
+ <para>
+ Aqui é um exemplo de arquivo <literal>hibernate.properties</literal> para C3P0:
+ </para>
+
+ <programlisting id="c3p0-configuration" revision="1"><![CDATA[hibernate.connection.driver_class = org.postgresql.Driver
+hibernate.connection.url = jdbc:postgresql://localhost/mydatabase
+hibernate.connection.username = myuser
+hibernate.connection.password = secret
+hibernate.c3p0.min_size=5
+hibernate.c3p0.max_size=20
+hibernate.c3p0.timeout=1800
+hibernate.c3p0.max_statements=50
+hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect]]></programlisting>
+
+ <para>
+ Para usar dentro de um servidor de aplicação, você deve configurar
+ o Hibernate para obter conexões de um application server
+ <literal>Datasource</literal> registrado no JNDI. Você necessitará
+ determinar pelo menos uma das seguintes propriedades:
+ </para>
+
+ <table frame="topbot">
+ <title>Propriedades do Datasource do Hibernate</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Nome da Propriedade</entry>
+ <entry>Propósito</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.connection.datasource</literal>
+ </entry>
+ <entry>
+ <emphasis>Nome datasource JNDI</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jndi.url</literal>
+ </entry>
+ <entry>
+ <emphasis>URL do fornecedor JNDI</emphasis> (opcional)
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jndi.class</literal>
+ </entry>
+ <entry>
+ <emphasis>Classe do JNDI <literal>InitialContextFactory</literal></emphasis> (opcional)
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.username</literal>
+ </entry>
+ <entry>
+ <emphasis>Usuário do banco de dados</emphasis> (opcional)
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.password</literal>
+ </entry>
+ <entry>
+ <emphasis>Senha do usuário do banco de dados</emphasis> (opcional)
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ Eis um exemplo de arquivo <literal>hibernate.properties</literal> para
+ um servidor de aplicação fornecedor de datasources JNDI:
+ </para>
+
+ <programlisting><![CDATA[hibernate.connection.datasource = java:/comp/env/jdbc/test
+hibernate.transaction.factory_class = \
+ org.hibernate.transaction.JTATransactionFactory
+hibernate.transaction.manager_lookup_class = \
+ org.hibernate.transaction.JBossTransactionManagerLookup
+hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect]]></programlisting>
+
+ <para>
+ Conexões JDBC obtidas de um datasource JNDI irão automaticamente irão participar
+ das transações gerenciadas pelo container no servidor de aplicação.
+ </para>
+
+ <para>
+ Arbitrariamente as propriedades de conexão podem ser acrescentandas ao
+ "<literal>hibernate.connnection</literal>" ao nome da propriedade. Por exemplo,
+ você deve especificar o <literal>charSet</literal> usando <literal>hibernate.connection.charSet</literal>.t.
+ </para>
+
+ <para>
+ Você pode definir sua própria estratégia de plugin para obter conexões JDBC implementando
+ a interface <literal>org.hibernate.connection.ConnectionProvider</literal>. Você pode
+ escolher uma implementação customizada setando <literal>hibernate.connection.provider_class</literal>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="configuration-optional" revision="1">
+ <title>Propriedades opcionais de configuração</title>
+
+ <para>
+ Há um grande número de outras propriedades que controlam o comportamento do Hibernate
+ em tempo de execução. Todos são opcionais e tem valores default lógicos.
+ </para>
+
+ <para>
+ <emphasis>Aviso: algumas destas propriedades são somente a "nÃvel de sistema".</emphasis>
+ Propriedades nÃvel de sistema podem ser determinados somente via <literal>java -Dproperty=value</literal>
+ ou <literal>hibernate.properties</literal>. Elas <emphasis>não</emphasis>podem ser
+ configuradas por outras técnicas descritas abaixo.
+ </para>
+
+ <table frame="topbot" id="configuration-optional-properties" revision="8">
+ <title>Hibernate Configuration Properties</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Nome da Propriedade</entry>
+ <entry>Propósito</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.dialect</literal>
+ </entry>
+ <entry>
+ O nome da classe de um <literal>Dialeto</literal>
+ que permite o Hibernate gerar SQL otimizado para um banco de
+ dados relacional em particular.
+ <para>
+ <emphasis role="strong">Ex.</emphasis>
+ <literal>full.classname.of.Dialect</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.show_sql</literal>
+ </entry>
+ <entry>
+ Escreve todas as instruções SQL no console. Esta é uma alternativa
+ a configurar a categoria de log <literal>org.hibernate.SQL</literal>
+ para <literal>debug</literal>.
+ <para>
+ <emphasis role="strong">Ex.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.format_sql</literal>
+ </entry>
+ <entry>
+ Imprime o SQL formatado no log e console.
+ <para>
+ <emphasis role="strong">Ex.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.default_schema</literal>
+ </entry>
+ <entry>
+ Qualifica no sql gerado, os nome das tabelas sem qualificar
+ com schena/tablespace dado
+ <para>
+ <emphasis role="strong">Ex.</emphasis>
+ <literal>SCHEMA_NAME</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.default_catalog</literal>
+ </entry>
+ <entry>
+ Qualifica no sql gerado, os nome das tabelas sem qualificar
+ com catálogo dado
+ <para>
+ <emphasis role="strong">Ex.</emphasis>
+ <literal>CATALOG_NAME</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.session_factory_name</literal>
+ </entry>
+ <entry>
+ O <literal>SessionFactory</literal> irá automaticamente
+ se ligar a este nome no JNDI depois de ter sido criado.
+ <para>
+ <emphasis role="strong">Ex.</emphasis>
+ <literal>jndi/composite/name</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.max_fetch_depth</literal>
+ </entry>
+ <entry>
+ Estabelece a "profundidade" máxima para árvore outer join fetch
+ para associações finais únicas(one-to-one,many-to-one).
+ Um <literal>0</literal> desativa por default a busca outer join.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ Valores recomendados entre<literal>0</literal> e <literal>3</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.default_batch_fetch_size</literal>
+ </entry>
+ <entry>
+ Determina um tamanho default para busca de associações em lotes do Hibernate
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ Valores recomendados <literal>4</literal>, <literal>8</literal>,
+ <literal>16</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.default_entity_mode</literal>
+ </entry>
+ <entry>
+ Determina um modo default para representação de entidades
+ para todas as sessões abertas desta <literal>SessionFactory</literal>
+ <para>
+ <literal>dynamic-map</literal>, <literal>dom4j</literal>,
+ <literal>pojo</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.order_updates</literal>
+ </entry>
+ <entry>
+ Força o Hibernate a ordenar os updates SQL pelo valor da chave
+ primária dos itens a serem atualizados. Isto resultará em menos
+ deadlocks nas transações em sistemas altamente concorrente.
+ <para>
+ <emphasis role="strong">Ex.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.generate_statistics</literal>
+ </entry>
+ <entry>
+ If enabled, Hibernate will collect statistics useful for
+ performance tuning.
+ Se habilitado, o Hibernate coletará estatÃsticas úties
+ para performance tuning dos bancos.
+ <para>
+ <emphasis role="strong">Ex.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.use_identifer_rollback</literal>
+ </entry>
+ <entry>
+ Se habilitado, propriedades identificadoras geradas
+ serão zeradas para os valores default quando os
+ objetos forem apagados.
+ <para>
+ <emphasis role="strong">Ex.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.use_sql_comments</literal>
+ </entry>
+ <entry>
+ Se ligado, o Hibernate irá gerar comentários dentro do SQL,
+ para facilitar o debugging, o valor default é <literal>false</literal>.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame="topbot" id="configuration-jdbc-properties" revision="8">
+ <title>JDBC Hibernate e Propriedades de Conexão</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Nome da Propriedade</entry>
+ <entry>Propósito</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.fetch_size</literal>
+ </entry>
+ <entry>
+ Um valor maior que zero determina o tamanho do fetch
+ do JDBC( chamadas <literal>Statement.setFetchSize()</literal>).
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.batch_size</literal>
+ </entry>
+ <entry>
+ Um valor maior que zero habilita uso de batch updates JDBC2 pelo Hibernate.
+ <para>
+ <emphasis role="strong">Ex.</emphasis>
+ valores recomentados entre <literal>5</literal> e <literal>30</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.batch_versioned_data</literal>
+ </entry>
+ <entry>
+ Sete esta propriedade como <literal>true</literal> se seu driver JDBC retorna
+ o número correto de linhas no <literal>executeBatch()</literal> ( à usualmente
+ seguro tornar esta opção ligada). O Hibernate então irá usar betched DML
+ para automaticamente versionar dados. <literal>false</literal> por default.
+ <para>
+ <emphasis role="strong">Ex.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.factory_class</literal>
+ </entry>
+ <entry>
+ Escolher um <literal>Batcher</literal> customizado. Muitas
+ aplicações não irão necessitar desta propriedade de configuração
+ <para>
+ <emphasis role="strong">Ex.</emphasis>
+ <literal>classname.of.Batcher</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.use_scrollable_resultset</literal>
+ </entry>
+ <entry>
+ Habilita o uso de JDBC2 scrollable resultsets pelo Hibernate.
+ Essa propriedade somente é necessaria quando se usa Conexeções
+ JDBC providas pelo usuário, caso contrário o Hibernate os os
+ metadados da conexão.
+ <para>
+ <emphasis role="strong">Ex.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.use_streams_for_binary</literal>
+ </entry>
+ <entry>
+ Use streams para escrever/ler tipos <literal>binary</literal>
+ ou <literal>serializable</literal> para/a o JDBC( propriedade a nÃvel de sistema).
+ <para>
+ <emphasis role="strong">Ex.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.use_get_generated_keys</literal>
+ </entry>
+ <entry>
+ Possibilita o uso <literal>PreparedStatement.getGeneratedKeys()</literal>
+ do JDBC3 para recuperar chaves geradas nativamente depois da inserçãp.
+ Requer driver JDBC3+ e JRE1.4+, determine para false se seu driver tem
+ problemas com gerador de indentificadores Hibernate. Por default, tente
+ determinar o driver capaz de usar metadados da conexão.
+ <para>
+ <emphasis role="strong">Ex.</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.provider_class</literal>
+ </entry>
+ <entry>
+ O nome da classe de um <literal>ConnectionProvider</literal> personalizado
+ o qual proverá conexões JDBC para o Hibernate.
+ <para>
+ <emphasis role="strong">Ex.</emphasis>
+ <literal>classname.of.ConnectionProvider</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.isolation</literal>
+ </entry>
+ <entry>
+ Determina o nÃvel de isolamento de uma transação JDBC.
+ Verifique <literal>java.sql.Connection</literal> para valores
+ siginificativos mas note que a maior parte dos bancos de dados
+ não suportam todos os nÃveis de isolamento.
+ <para>
+ <emphasis role="strong">Ex.</emphasis>
+ <literal>1, 2, 4, 8</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.autocommit</literal>
+ </entry>
+ <entry>
+ Habilita autocommit para conexões no pool JDBC( não recomendado).
+ <para>
+ <emphasis role="strong">Ex.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.release_mode</literal>
+ </entry>
+ <entry>
+ Especifica quando o Hibernate deve liberar conexões JDBC. Por default,
+ uma conexão JDBC é retida até a sessão está explicitamente fechada
+ ou desconectada. Para um datasource JTA do servidor de aplicação, você deve
+ usar <literal>after_statement</literal> para forçar s liberação da conexões
+ depois de todas as chamadas JDBC. Para uma conexão não-JTA, freqüentemente
+ faz sentido liberar a conexão ao fim de cada transação, usando
+ <literal>after_transaction</literal>. <literal>auto</literal> escolheremos
+ <literal>after_statement</literal> para as estratégias de transaçãoes JTA e CMT
+ e <literal>after_transaction</literal> para as estratégias de transação JDBC
+ <para>
+ <emphasis role="strong">Ex.</emphasis>
+ <literal>auto</literal> (default) | <literal>on_close</literal> |
+ <literal>after_transaction</literal> | <literal>after_statement</literal>
+ </para>
+ <para>
+ Note that this setting only affects <literal>Session</literal>s returned from
+ <literal>SessionFactory.openSession</literal>. For <literal>Session</literal>s
+ obtained through <literal>SessionFactory.getCurrentSession</literal>, the
+ <literal>CurrentSessionContext</literal> implementation configured for use
+ controls the connection release mode for those <literal>Session</literal>s.
+ See <xref linkend="architecture-current-session"/>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.<emphasis><propertyName></emphasis></literal>
+ </entry>
+ <entry>
+ Passa a propriedade JDBC <literal>propertyName</literal>
+ para <literal>DriverManager.getConnection()</literal>.
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jndi.<emphasis><propertyName></emphasis></literal>
+ </entry>
+ <entry>
+ Passar a propriedade <literal>propertyName</literal> para
+ o <literal>InitialContextFactory</literal> JNDI.
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame="topbot" id="configuration-cache-properties" revision="7">
+ <title>Propriedades de Cachê do Hibernate</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Nome da Propriedade</entry>
+ <entry>Propósito</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.cache.provider_class</literal>
+ </entry>
+ <entry>
+ O nome da classe de um <literal>CacheProvider</literal> customizado.
+ <para>
+ <emphasis role="strong">Ex.</emphasis>
+ <literal>classname.of.CacheProvider</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.use_minimal_puts</literal>
+ </entry>
+ <entry>
+ Otimizar operação de cachê de segundo nÃvel para minimizar escritas,
+ ao custo de leituras mais frequantes. Esta configuração é mais útil
+ para cachês clusterizados e, no Hibernate3, é habilitado por default
+ para implementações de cachê clusterizar.
+ <para>
+ <emphasis role="strong">Ex.</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.use_query_cache</literal>
+ </entry>
+ <entry>
+ Habilita a cache de consultas, Mesmo assim, consultas individuais ainda tem que ser
+ habilitadas para o cache.
+ <para>
+ <emphasis role="strong">Ex.</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.use_second_level_cache</literal>
+ </entry>
+ <entry>
+ May be used to completely disable the second level cache, which is enabled
+ by default for classes which specify a <literal><cache></literal>
+ mapping.
+ Pode ser usada para desabilitar completamente ocache de segundo nÃvel,
+ o qual está habilitado por default para classes que especificam
+ um mapeamento <literal><cache></literal>.
+
+ <para>
+ <emphasis role="strong">Ex.</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.query_cache_factory</literal>
+ </entry>
+ <entry>
+ O nome de uma classe que implementa a interface
+ <literal>QueryCache</literal> personalizada, por
+ default, um <literal>StandardQueryCache</literal>
+ criado automaticamente.
+ <para>
+ <emphasis role="strong">Ex.</emphasis>
+ <literal>classname.of.QueryCache</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.region_prefix</literal>
+ </entry>
+ <entry>
+ Um prefixo para usar nos nomes da área especial
+ do cachê de segundo nÃvel.
+ <para>
+ <emphasis role="strong">Ex.</emphasis>
+ <literal>prefix</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.use_structured_entries</literal>
+ </entry>
+ <entry>
+ Forces Hibernate to store data in the second-level cache
+ in a more human-friendly format.
+ Força o Hibernate armazenar dados no cachê se segundo
+ nÃvel em um formato mais legivel.
+ <para>
+ <emphasis role="strong">Ex.</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame="topbot" id="configuration-transaction-properties" revision="9">
+ <title>Propriedades de Transação do Hibernate</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Nome da Propriedade</entry>
+ <entry>Propósito</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.transaction.factory_class</literal>
+ </entry>
+ <entry>
+ O nome da clase de um a <literal>TransactionFactory</literal>
+ para usar com API <literal>Transaction</literal>
+ ( por default JDBCTransactionFactory).
+ <para>
+ <emphasis role="strong">Ex.</emphasis>
+ <literal>classname.of.TransactionFactory</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>jta.UserTransaction</literal>
+ </entry>
+ <entry>
+ Um nome JNDI usado pelo <literal>JTATransactionFactory</literal>
+ para obter uma <literal>UserTransaction</literal> JTA a partir
+ do servidor de aplicação.
+ <para>
+ <emphasis role="strong">Ex.</emphasis>
+ <literal>jndi/composite/name</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.transaction.manager_lookup_class</literal>
+ </entry>
+ <entry>
+ O nome da classe de um <literal>TransactionManagerLookup</literal>
+ â requerido quando caching a nÃvel JVM esta habilitado ou quando
+ estivermos usando um generator hilo em um ambiente JTA.
+ <para>
+ <emphasis role="strong">Ex.</emphasis>
+ <literal>classname.of.TransactionManagerLookup</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.transaction.flush_before_completion</literal>
+ </entry>
+ <entry>
+ Se habilitado, a sessão será automaticamente limpa antes da fase de
+ conclusão da transação. à preferivel a gerência interna e
+ automática do contexto da sessão, veja
+ <xref linkend="architecture-current-session"/>
+ <para>
+ <emphasis role="strong">Ex.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.transaction.auto_close_session</literal>
+ </entry>
+ <entry>
+ Se habilitado, a sessão será automaticamente fechada após a fase de
+ conclusão da transação. à preferivel a gerência interna e
+ automática do contexto da sessão, veja
+ <xref linkend="architecture-current-session"/>
+ <para>
+ <emphasis role="strong">Ex.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame="topbot" id="configuration-misc-properties" revision="10">
+ <title>Propriedades Variadas</title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Nome da Propriedade</entry>
+ <entry>Propósito</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.current_session_context_class</literal>
+ </entry>
+ <entry>
+ Forneçe uma estratégia (personalizada) para extensão
+ da <literal>Session</literal> "corrente". Veja
+ <xref linkend="architecture-current-session"/> para
+ mais informação sobre estratégias internas.
+ <para>
+ <emphasis role="strong">Ex.</emphasis>
+ <literal>jta</literal> | <literal>thread</literal> |
+ <literal>managed</literal> | <literal>custom.Class</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.query.factory_class</literal>
+ </entry>
+ <entry>
+ Escolha a implementação de análise HQL.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>org.hibernate.hql.ast.ASTQueryTranslatorFactory</literal> or
+ <literal>org.hibernate.hql.classic.ClassicQueryTranslatorFactory</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.query.substitutions</literal>
+ </entry>
+ <entry>
+ Mapeamento a partir de sÃmbolos em consultas HQL para
+ sÃmbolos SQL( sÃmbolos devem ser funções ou nome literais
+ , por exemplo).
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>hqlLiteral=SQL_LITERAL, hqlFunction=SQLFUNC</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.hbm2ddl.auto</literal>
+ </entry>
+ <entry>
+ Automaticamente valida ou exporta schema DDL para o banco de
+ dados quando o <literal>SessionFactory</literal> é criads.
+ Com <literal>create-drop</literal>, o schema do banco de dados
+ será excluido quando a <literal>create-drop</literal> for
+ fechada esplicitamente.
+ <para>
+ <emphasis role="strong">Ex.</emphasis>
+ <literal>validate</literal> | <literal>update</literal> |
+ <literal>create</literal> | <literal>create-drop</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cglib.use_reflection_optimizer</literal>
+ </entry>
+ <entry>
+ Habilita o uso de CGLIB em vez de reflexão em tempo de execução
+ ( propriedade a nÃvel de sistema). Reflexão pode algumas vezes ser ú
+ til quando controlar erros, note que o Hibernate sempre irá requerer a CGLIB
+ mesmo se você desligar o otimizador. Você não pode determinar esta
+ propriedade no hibernate.cfg.xml.
+ <para>
+ <emphasis role="strong">Ex.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <sect2 id="configuration-optional-dialects" revision="1">
+ <title>Dialetos SQL</title>
+
+ <para>
+ Você deve sempre determinar a propriedade <literal>hibernate.dialect</literal>
+ para a subclasse de <literal>org.hibernate.dialect.Dialect</literal> correta de seu
+ banco de dados. Se você especificar um dialeto, Hibernate usará defaults lógicos
+ para qualquer um das outras propriedades listadas abaixo, reduzindo o esforço de
+ especificá-los manualmente.
+ </para>
+
+ <table frame="topbot" id="sql-dialects" revision="2">
+ <title>Hibernate SQL Dialects (<literal>hibernate.dialect</literal>)</title>
+ <tgroup cols="2">
+ <colspec colwidth="1*"/>
+ <colspec colwidth="2.5*"/>
+ <thead>
+ <row>
+ <entry>RDBMS</entry>
+ <entry>Dialect</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>DB2</entry> <entry><literal>org.hibernate.dialect.DB2Dialect</literal></entry>
+ </row>
+ <row>
+ <entry>DB2 AS/400</entry> <entry><literal>org.hibernate.dialect.DB2400Dialect</literal></entry>
+ </row>
+ <row>
+ <entry>DB2 OS390</entry> <entry><literal>org.hibernate.dialect.DB2390Dialect</literal></entry>
+ </row>
+ <row>
+ <entry>PostgreSQL</entry> <entry><literal>org.hibernate.dialect.PostgreSQLDialect</literal></entry>
+ </row>
+ <row>
+ <entry>MySQL</entry> <entry><literal>org.hibernate.dialect.MySQLDialect</literal></entry>
+ </row>
+ <row>
+ <entry>MySQL with InnoDB</entry> <entry><literal>org.hibernate.dialect.MySQLInnoDBDialect</literal></entry>
+ </row>
+ <row>
+ <entry>MySQL with MyISAM</entry> <entry><literal>org.hibernate.dialect.MySQLMyISAMDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Oracle (any version)</entry> <entry><literal>org.hibernate.dialect.OracleDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Oracle 9i/10g</entry> <entry><literal>org.hibernate.dialect.Oracle9Dialect</literal></entry>
+ </row>
+ <row>
+ <entry>Sybase</entry> <entry><literal>org.hibernate.dialect.SybaseDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Sybase Anywhere</entry> <entry><literal>org.hibernate.dialect.SybaseAnywhereDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Microsoft SQL Server</entry> <entry><literal>org.hibernate.dialect.SQLServerDialect</literal></entry>
+ </row>
+ <row>
+ <entry>SAP DB</entry> <entry><literal>org.hibernate.dialect.SAPDBDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Informix</entry> <entry><literal>org.hibernate.dialect.InformixDialect</literal></entry>
+ </row>
+ <row>
+ <entry>HypersonicSQL</entry> <entry><literal>org.hibernate.dialect.HSQLDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Ingres</entry> <entry><literal>org.hibernate.dialect.IngresDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Progress</entry> <entry><literal>org.hibernate.dialect.ProgressDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Mckoi SQL</entry> <entry><literal>org.hibernate.dialect.MckoiDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Interbase</entry> <entry><literal>org.hibernate.dialect.InterbaseDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Pointbase</entry> <entry><literal>org.hibernate.dialect.PointbaseDialect</literal></entry>
+ </row>
+ <row>
+ <entry>FrontBase</entry> <entry><literal>org.hibernate.dialect.FrontbaseDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Firebird</entry> <entry><literal>org.hibernate.dialect.FirebirdDialect</literal></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-outerjoin" revision="4">
+ <title>Recuperação por união externa (Outer Join Fetching)</title>
+
+ <para>
+ Se seu banco de dados suporta Recuperação por união externa (Outer Join Fetching) no estilo ANSI,
+ Oracle ou Sybase, A recuperação por união externa (Outer Join Fetching) frequentemente aumentará
+ o desempenho limitando o número de chamadas (round trips) ao banco de dados( ao custo de
+ possivelmente mais trabalho desempenhado pelo próprio banco de dados). A recuperação por
+ união externa (Outer Join Fetching)permite um gráfico completo de objetos conectados
+ por muitos-para-um, um-para-muitos, muitos-para-muitos e associações um-para-um para ser
+ recuperadas em um simples instrução SQL SELECT .
+
+ </para>
+
+ <para>
+ A recuperação por união externa (Outer Join Fetching) pode ser desabilitado
+ <emphasis>globalmente</emphasis> setando a propriedade
+ <literal>hibernate.max_fetch_depth</literal> para <literal>0</literal>.
+ Uma valor 1 ou maior habilita o outer join fetching para associações um-para-um
+ e muitos-para-umos cujos quais tem sido mapeado com <literal>fetch="join"</literal>.
+ </para>
+
+ <para>
+ Veja <xref linkend="performance-fetching"/> para mais informações.
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-binarystreams" revision="1">
+ <title>Fluxos Binários (Binary Streams)</title>
+
+ <para>
+ O Oracle limita o tamanho de arrays de <literal>byte</literal> que pode ser
+ passado para/de o driver JDBC. Se você desejar usar grandes instâncias de
+ tipos <literal>binary</literal> ou <literal>serializable</literal>, você
+ deve habilitar <literal>hibernate.jdbc.use_streams_for_binary</literal>.
+ <emphasis>Essa é uma configuração que só pode ser feita a nÃvel de sistema.</emphasis>
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-cacheprovider" revision="2">
+ <title>Cachê de segundo nÃvel e query</title>
+
+ <para>
+ As propriedades prefixadas pelo <literal>hibernate.cache</literal>
+ permite você usar um sistema de cachê de segundo nÃvel
+ em um processo executado em clustercom Hibernate.
+ Veja <xref linkend="performance-cache"/> para mais detalhes.
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-querysubstitution">
+ <title>Substituições na Linguagem de Consulta</title>
+
+ <para>
+ Você pode definir novos sÃmbolos de consulta Hibernate usando
+ <literal>hibernate.query.substitutions</literal>.
+ Por exemplo:
+ </para>
+
+ <programlisting>hibernate.query.substitutions true=1, false=0</programlisting>
+
+ <para>
+ Faria com que os sÃmbolos <literal>true</literal> e <literal>false</literal>
+ passasem a ser traduzidos para literais inteiro no SQL gerado.
+ </para>
+
+ <programlisting>hibernate.query.substitutions toLowercase=LOWER</programlisting>
+
+ <para>
+ permitirá você renomear a função <literal>LOWER</literal> no SQL.
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-statistics" revision="2">
+ <title>EstatÃsticas do Hibernate</title>
+
+ <para>
+ If you enable <literal>hibernate.generate_statistics</literal>, Hibernate will
+ expose a number of metrics that are useful when tuning a running system via
+ <literal>SessionFactory.getStatistics()</literal>. Hibernate can even be configured
+ to expose these statistics via JMX. Read the Javadoc of the interfaces in
+ <literal>org.hibernate.stats</literal> for more information.
+
+ Se você habilitar <literal>hibernate.generate_statistics</literal>, o Hibernate
+ exibirá um número de métricas bastante útil ao ajustar um sistema via
+ <literal>SessionFactory.getStatistics()</literal>. O Hibernate pode até ser
+ configurado para exibir essas estatÃsticas via JMX. Leia o Javadoc da interface
+ <literal>org.hibernate.stats</literal> para mais informações.
+ </para>
+
+ </sect2>
+ </sect1>
+
+ <sect1 id="configuration-logging">
+ <title>Logging</title>
+
+ <para>
+ Hibernate registra vários eventos usando Apache commons-logging.
+ </para>
+
+ <para>
+ O serviço commons-logging direcionará a saÃda para o Apache Log4j
+ ( se você incluir <literal>log4j.jar</literal>r no seu classpath) ou
+ JDK1.4 logging( se estiver em uso JDK1.4 ou maior). Você pode fazer o
+ download do Log4j a partir de <literal>http://jakarta.apache.org</literal>.
+ Para usar Log4j você necessitará colocar um arquivo
+ <literal>log4j.properties</literal> no seu classpath, um exemplo de arquivo
+ de propriedades é distribuÃdo com o Hibernate no diretório
+ <literal>src/</literal>.
+
+ </para>
+
+ <para>
+ We strongly recommend that you familiarize yourself with Hibernate's log
+ messages. A lot of work has been put into making the Hibernate log as
+ detailed as possible, without making it unreadable. It is an essential
+ troubleshooting device. The most interesting log categories are the
+ following:
+
+ Nós recomendamos enfaticamente que você se familiarize-se com mensagens de
+ log do Hibernate. Uma parte do trabalho tem sido posto em fazer o log
+ Hibernate tão detalhado quanto possÃvel, sem fazê-lo ilegÃvel.
+ Ã um essencial dispositivos de controle de erros. As categorias de log
+ mais interessantes são as seguintes:
+ </para>
+
+ <table frame="topbot" id="log-categories" revision="2">
+ <title>Categorias de Log do Hibernate</title>
+ <tgroup cols="2">
+ <colspec colwidth="1*"/>
+ <colspec colwidth="2.5*"/>
+ <thead>
+ <row>
+ <entry>Categoria</entry>
+ <entry>Função</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>org.hibernate.SQL</literal></entry>
+ <entry>Registra todas as instruções SQL DML a medida que elas são executadas</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.type</literal></entry>
+ <entry>Registra todos os parâmetros JDBC</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.tool.hbm2ddl</literal></entry>
+ <entry>Registra todas as instruções SQL DDL a medida que elas são executadas</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.pretty</literal></entry>
+ <entry>
+ Log the state of all entities (max 20 entities) associated
+ with the session at flush time
+ Registra o estado de todas as entidades (máximo 20 entidades)
+ associadas a session no momento da limpeza (flush).
+ </entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.cache</literal></entry>
+ <entry>Registra todas as atividades de cachê de segundo nÃvel</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction</literal></entry>
+ <entry>Registra atividades relacionada a transação</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.jdbc</literal></entry>
+ <entry>Registra todas as requisições de recursos JDBC</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.hql.ast.AST</literal></entry>
+ <entry>
+ Registra instruções SQL e HQL durante a análise da consultas
+ </entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.secure</literal></entry>
+ <entry>Registra todas as requisições de autorização JAAS</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate</literal></entry>
+ <entry>
+ Registra tudo ( uma parte das informações, mas muito
+ útil para controle de erros )
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ Quando desenvolver aplicações com Hibernate, você deve quase sempre trabalhar com
+ debug <literal>debug</literal> para a categoria <literal>org.hibernate.SQL</literal>,
+ ou, alternativamente, a com a propriedade <literal>hibernate.show_sql</literal> habilitada.
+ </para>
+
+
+ </sect1>
+
+ <sect1 id="configuration-namingstrategy">
+ <title>Implementado uma <literal>NamingStrategy</literal></title>
+
+ <para>
+ A interface <literal>org.hibernate.cfg.NamingStrategy</literal> permite você
+ especificar um "padrão de nomeação" para objetos do banco de dados e elementos schema.
+ </para>
+
+ <para>
+ Você deve criar regras para a geração automaticamente de identificadores
+ do banco de dados a partir de identificadores Java ou para processar
+ colunas "computadas" e nomes de tabelas dado o arquivo de mapeamento
+ para nomes "fÃsicos" de tabelas e colunas. Esta caracterÃstica ajuda a
+ reduzir a verbosidade do documento de mapeamento, eliminando interferências
+ repetitivas( <literal>TBL_</literal>prefixos, por exemplo). A estratégia
+ default usada pelo Hibernate é completamente mÃnima.
+ </para>
+
+ <para>
+ Você pode especificar uma estratégia diferente ao chamar
+ <literal>Configuration.setNamingStrategy()</literal> antes de adicionar
+ os mapeamentos:
+ </para>
+
+ <programlisting><![CDATA[SessionFactory sf = new Configuration()
+ .setNamingStrategy(ImprovedNamingStrategy.INSTANCE)
+ .addFile("Item.hbm.xml")
+ .addFile("Bid.hbm.xml")
+ .buildSessionFactory();]]></programlisting>
+
+ <para>
+ <literal>org.hibernate.cfg.ImprovedNamingStrategy</literal> é uma estratégia
+ interna que pode ser um ponto de começo útil para algumas aplicações.
+ </para>
+
+ </sect1>
+
+ <sect1 id="configuration-xmlconfig" revision="2">
+ <title>Arquivo de configuração XML</title>
+
+ <para>
+ Uma maneira alternativa de configuração é especificar uma configuração completa
+ em um arquivo chamado <literal>hibernate.cfg.xml</literal>. Este arquivo pode
+ ser usado como um substituto para o arquivo <literal>hibernate.properties</literal>
+ ou, se ambos estão presentes, sobrescrever propriedades.
+ </para>
+
+ <para>
+ The XML configuration file is by default expected to be in the root o
+ your <literal>CLASSPATH</literal>. Here is an example:
+ O arquivo XML de configuração é por default esperado para estar na
+ raiz do seu <literal>CLASSPATH</literal>. Veja um exemplo:
+ </para>
+
+ <programlisting><![CDATA[<?xml version='1.0' encoding='utf-8'?>
+<!DOCTYPE hibernate-configuration PUBLIC
+ "-//Hibernate/Hibernate Configuration DTD//EN"
+ "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
+
+<hibernate-configuration>
+
+ <!-- a SessionFactory instance listed as /jndi/name -->
+ <session-factory
+ name="java:hibernate/SessionFactory">
+
+ <!-- properties -->
+ <property name="connection.datasource">java:/comp/env/jdbc/MyDB</property>
+ <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
+ <property name="show_sql">false</property>
+ <property name="transaction.factory_class">
+ org.hibernate.transaction.JTATransactionFactory
+ </property>
+ <property name="jta.UserTransaction">java:comp/UserTransaction</property>
+
+ <!-- mapping files -->
+ <mapping resource="org/hibernate/auction/Item.hbm.xml"/>
+ <mapping resource="org/hibernate/auction/Bid.hbm.xml"/>
+
+ <!-- cache settings -->
+ <class-cache class="org.hibernate.auction.Item" usage="read-write"/>
+ <class-cache class="org.hibernate.auction.Bid" usage="read-only"/>
+ <collection-cache collection="org.hibernate.auction.Item.bids" usage="read-write"/>
+
+ </session-factory>
+
+</hibernate-configuration>]]></programlisting>
+
+ <para>
+ Como você pode ver, a vantagem deste enfoque é a externalização dos nomes dos
+ arquivos de mapeamento para configuração. O <literal>hibernate.cfg.xml</literal>
+ também é mais conveniente caso você tenha que ajustar o cache do Hibernate.
+ Note que a escolha é sua em usar <literal>hibernate.properties</literal> ou
+ <literal>hibernate.cfg.xml</literal>, ambos são equivalente, à exceção dos benefÃcios
+ acima mencionados de usar a sintaxe de XML.
+ </para>
+
+ <para>
+ Com a configuração do XML, iniciar o Hibernate é então tão simples como
+ </para>
+
+ <programlisting><![CDATA[SessionFactory sf = new Configuration().configure().buildSessionFactory();]]></programlisting>
+
+ <para>
+ You can pick a different XML configuration file using
+ </para>
+
+ <programlisting><![CDATA[SessionFactory sf = new Configuration()
+ .configure("catdb.cfg.xml")
+ .buildSessionFactory();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="configuration-j2ee" revision="1">
+ <title>Integração com servidores de aplicação J2EE</title>
+
+ <para>
+ O Hibernate tem os seguintes pontos da integração para o infraestrutura de J2EE:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>DataSources gerenciados pelo container</emphasis>: O Hibernate pode
+ usar conexões JDBC gerenciadas pelo Container e fornecidas pela JNDI. Geralmente,
+ um <literal>TransactionManager</literal> compatÃvel com JTA e um
+ <literal>ResourceManager</literal> cuidam do gerenciamento da transação ( CMT ),
+ especialmente em transações distribuÃdas manipuladas através de vários DataSources.
+ Naturalmente, você também pode demarcar os limites das transações programaticamente (BMT)
+ ou você poderia querer usar a API opcional do Hibernate <literal>Transaction</literal>
+ para esta manter seu código portável.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Ligação (binding) automática a JNDI</emphasis>: O Hibernate pode
+ associar sua <literal>SessionFactory</literal> a JNDI depois de iniciado.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Ligação (binding) Session na JTA:</emphasis>
+ A <literal>Session</literal> do Hibernate pode automaticamente ser ligada
+ ao escopo da transações JTA. Simplesmente localizando a <literal>SessionFactory</literal>
+ da JNDI e obtendo a<literal>Session</literal> corrente. Deixe o Hibernate cuidar
+ da limpeza e encerramento da <literal>Session</literal> quando as transações JTA
+ terminarem. A Demarcação de transação pode ser declarativa (CMT) ou
+ programática(BMT/Transação do usuário).
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>JMX deployment:</emphasis> Se você usa um JMX servidor de
+ aplicações capaz (ex. Jboss AS), você pode fazer a instação do Hibernate
+ como um Mbean controlado. Isto evita ter que iniciar uma linha de
+ código para construir sua <literal>SessionFactory</literal> de uma
+ <literal>Configuration</literal>. O container iniciará seu
+ <literal>HibernateService</literal>, e idealmente também cuidará
+ das dependências de serviços (DataSources, têm que estar disponÃveis
+ antes do Hibernate iniciar, etc.).
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Dependendo em seu ambiente, você poderia ter que ajustar a opção de configuração
+ <literal>hibernate.connection.aggressive_release</literal> para verdadeiro ( true ),
+ se seu servidor de aplicações lançar exeções "retenção de conecção".
+ </para>
+
+ <sect2 id="configuration-optional-transactionstrategy" revision="3">
+ <title>Configuração de estratégia de transação</title>
+
+ <para>
+ A API Hibernate <literal>Session</literal> é independente de qualquer sistema de
+ demarcação de transação em sua arquitetura. Se você deixar o Hibernate usar
+ a JDBC diretamente, através de um pool de conexões, você pode inicializar e
+ encerrar suas transações chamando a API JDBC. Se você rodar em um servidor de
+ aplicações J2EE, você poderá usar transações controladas por beans e chamar
+ a API JTA e <literal>UserTransaction</literal> quando necessário.
+
+ </para>
+
+ <para>
+ Para manter seu código portável entre estes dois ( e outros ) ambientes, recomendamos
+ a API Hibernate <literal>Transaction</literal>, que envolve e esconde o sistema subjacente.
+ Você tem que especificar um classe construtora para <literal>Transaction</literal> instanciar
+ ajustando a propriedade de configuração do <literal>hibernate.transaction.factory_class</literal>.
+
+ </para>
+
+ <para>
+ Existem três escolhas (internas) padrões:
+ </para>
+
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term><literal>org.hibernate.transaction.JDBCTransactionFactory</literal></term>
+ <listitem>
+ <para>delegada as transações (JDBC)a bases de dados (Padrão)</para>
+
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>org.hibernate.transaction.JTATransactionFactory</literal></term>
+ <listitem>
+ <para>
+ delegada a transação a um container gerenciador se a transação
+ existente estiver de acordo neste contexto (ex: método bean sessão EJB),
+ se não uma nova transação é iniciada e uma transação controlado por
+ um bean é usada.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>org.hibernate.transaction.CMTTransactionFactory</literal></term>
+ <listitem>
+ <para>delega para um container gerenciador de transações JTA</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>
+ Você também pode definir suas próprias estratégias de transação ( para um serviço de
+ transação CORBA por exemplo).
+ </para>
+
+ <para>
+ Algumas caracterÃsticas no Hibernate (ex., o cache de segundo nÃvel, sessões contextuais
+ com JTA, etc.) requerem acesso a JTA <literal>TransactionManager</literal> em um ambiente
+ controlado. Em um servidor de aplicação você tem que especificar como o Hibernate pode
+ obter uma referência para a <literal>TransactionManager</literal>, pois o J2EE não
+ padronize um mecanismo simples :
+ </para>
+
+ <table frame="topbot" id="jtamanagerlookup" revision="1">
+ <title>Gerenciadores de transações JTA</title>
+ <tgroup cols="2">
+ <colspec colwidth="2.5*"/>
+ <colspec colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Transaction Factory</entry>
+ <entry align="center">Application Server</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>org.hibernate.transaction.JBossTransactionManagerLookup</literal></entry>
+ <entry align="center">JBoss</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.WeblogicTransactionManagerLookup</literal></entry>
+ <entry align="center">Weblogic</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.WebSphereTransactionManagerLookup</literal></entry>
+ <entry align="center">WebSphere</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.WebSphereExtendedJTATransactionLookup</literal></entry>
+ <entry align="center">WebSphere 6</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.OrionTransactionManagerLookup</literal></entry>
+ <entry align="center">Orion</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.ResinTransactionManagerLookup</literal></entry>
+ <entry align="center">Resin</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.JOTMTransactionManagerLookup</literal></entry>
+ <entry align="center">JOTM</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.JOnASTransactionManagerLookup</literal></entry>
+ <entry align="center">JOnAS</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.JRun4TransactionManagerLookup</literal></entry>
+ <entry align="center">JRun4</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.BESTransactionManagerLookup</literal></entry>
+ <entry align="center">Borland ES</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-jndi" revision="3">
+ <title><literal>SessionFactory</literal> ligada a JNDI</title>
+
+ <para>
+ Uma <literal>SessionFactory</literal> de Hibernate ligada a JNDI pode simplificar
+ a localização da fabrica e a criação de novas <literal>Session</literal>s.
+ Observe que isto não relacionado a um <literal>Datasource</literal> ligado
+ a JNDI, simplemente ambos usam o mesmo registro!
+ </para>
+
+ <para>
+ If you wish to have the <literal>SessionFactory</literal> bound to a JNDI namespace, specify
+ a name (eg. <literal>java:hibernate/SessionFactory</literal>) using the property
+ <literal>hibernate.session_factory_name</literal>. If this property is omitted, the
+ <literal>SessionFactory</literal> will not be bound to JNDI. (This is especially useful in
+ environments with a read-only JNDI default implementation, e.g. Tomcat.)
+ </para>
+
+ <para>
+ When binding the <literal>SessionFactory</literal> to JNDI, Hibernate will use the values of
+ <literal>hibernate.jndi.url</literal>, <literal>hibernate.jndi.class</literal> to instantiate
+ an initial context. If they are not specified, the default <literal>InitialContext</literal>
+ will be used.
+ </para>
+
+ <para>
+ Hibernate will automatically place the <literal>SessionFactory</literal> in JNDI after
+ you call <literal>cfg.buildSessionFactory()</literal>. This means you will at least have
+ this call in some startup code (or utility class) in your application, unless you use
+ JMX deployment with the <literal>HibernateService</literal> (discussed later).
+ </para>
+
+ <para>
+ If you use a JNDI <literal>SessionFactory</literal>, an EJB or any other class may
+ obtain the <literal>SessionFactory</literal> using a JNDI lookup.
+ </para>
+
+ <para>
+ We recommend that you bind the <literal>SessionFactory</literal> to JNDI in
+ a managend environment and use a <literal>static</literal> singleton otherwise.
+ To shield your application code from these details, we also recommend to hide the
+ actual lookup code for a <literal>SessionFactory</literal> in a helper class,
+ such as <literal>HibernateUtil.getSessionFactory()</literal>. Note that such a
+ class is also a convenient way to startup Hibernate—see chapter 1.
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-j2ee-currentsession" revision="4">
+ <title>Current Session context management with JTA</title>
+
+ <para>
+ The easiest way to handle <literal>Session</literal>s and transactions is
+ Hibernates automatic "current" <literal>Session</literal> management.
+ See the discussion of <xref linkend="architecture-current-session">current sessions</xref>.
+ Using the <literal>"jta"</literal> session context, if there is no Hibernate
+ <literal>Session</literal> associated with the current JTA transaction, one will
+ be started and associated with that JTA transaction the first time you call
+ <literal>sessionFactory.getCurrentSession()</literal>. The <literal>Session</literal>s
+ retrieved via <literal>getCurrentSession()</literal> in <literal>"jta"</literal> context
+ will be set to automatically flush before the transaction completes, close
+ after the transaction completes, and aggressively release JDBC connections
+ after each statement. This allows the <literal>Session</literal>s to
+ be managed by the lifecycle of the JTA transaction to which it is associated,
+ keeping user code clean of such management concerns. Your code can either use
+ JTA programmatically through <literal>UserTransaction</literal>, or (recommended
+ for portable code) use the Hibernate <literal>Transaction</literal> API to set
+ transaction boundaries. If you run in an EJB container, declarative transaction
+ demarcation with CMT is preferred.
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-j2ee-jmx" revision="1">
+ <title>JMX deployment</title>
+
+ <para>
+ The line <literal>cfg.buildSessionFactory()</literal> still has to be executed
+ somewhere to get a <literal>SessionFactory</literal> into JNDI. You can do this
+ either in a <literal>static</literal> initializer block (like the one in
+ <literal>HibernateUtil</literal>) or you deploy Hibernate as a <emphasis>managed
+ service</emphasis>.
+ </para>
+
+ <para>
+ Hibernate is distributed with <literal>org.hibernate.jmx.HibernateService</literal>
+ for deployment on an application server with JMX capabilities, such as JBoss AS.
+ The actual deployment and configuration is vendor specific. Here is an example
+ <literal>jboss-service.xml</literal> for JBoss 4.0.x:
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<server>
+
+<mbean code="org.hibernate.jmx.HibernateService"
+ name="jboss.jca:service=HibernateFactory,name=HibernateFactory">
+
+ <!-- Required services -->
+ <depends>jboss.jca:service=RARDeployer</depends>
+ <depends>jboss.jca:service=LocalTxCM,name=HsqlDS</depends>
+
+ <!-- Bind the Hibernate service to JNDI -->
+ <attribute name="JndiName">java:/hibernate/SessionFactory</attribute>
+
+ <!-- Datasource settings -->
+ <attribute name="Datasource">java:HsqlDS</attribute>
+ <attribute name="Dialect">org.hibernate.dialect.HSQLDialect</attribute>
+
+ <!-- Transaction integration -->
+ <attribute name="TransactionStrategy">
+ org.hibernate.transaction.JTATransactionFactory</attribute>
+ <attribute name="TransactionManagerLookupStrategy">
+ org.hibernate.transaction.JBossTransactionManagerLookup</attribute>
+ <attribute name="FlushBeforeCompletionEnabled">true</attribute>
+ <attribute name="AutoCloseSessionEnabled">true</attribute>
+
+ <!-- Fetching options -->
+ <attribute name="MaximumFetchDepth">5</attribute>
+
+ <!-- Second-level caching -->
+ <attribute name="SecondLevelCacheEnabled">true</attribute>
+ <attribute name="CacheProviderClass">org.hibernate.cache.EhCacheProvider</attribute>
+ <attribute name="QueryCacheEnabled">true</attribute>
+
+ <!-- Logging -->
+ <attribute name="ShowSqlEnabled">true</attribute>
+
+ <!-- Mapping files -->
+ <attribute name="MapResources">auction/Item.hbm.xml,auction/Category.hbm.xml</attribute>
+
+</mbean>
+
+</server>]]></programlisting>
+
+ <para>
+ This file is deployed in a directory called <literal>META-INF</literal> and packaged
+ in a JAR file with the extension <literal>.sar</literal> (service archive). You also need
+ to package Hibernate, its required third-party libraries, your compiled persistent classes,
+ as well as your mapping files in the same archive. Your enterprise beans (usually session
+ beans) may be kept in their own JAR file, but you may include this EJB JAR file in the
+ main service archive to get a single (hot-)deployable unit. Consult the JBoss AS
+ documentation for more information about JMX service and EJB deployment.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+</chapter>
+
Property changes on: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/configuration.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/events.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/events.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/events.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,271 @@
+<chapter id="events">
+ <title>Interceptadores e Eventos</title>
+
+ <para>
+ à muito útil quando a aplicação precisa executar alguma "coisa" no momento em
+ que o Hibernate executa uma de suas ações. Isso permite a implementação de
+ certas funções genéricas, assim como permite estender as funcionalidades do
+ Hibernate
+ </para>
+
+ <sect1 id="objectstate-interceptors" revision="3">
+ <title>Interceptadores</title>
+
+ <para>
+ A interface <literal>Interceptor</literal> permite fornecer informações da session para o
+ aplicativo, permitindo ao aplicativo inspecionar e/ou manipular as propriedades de um
+ objeto persistente antes de ser salvo, atualizado, excluÃdo ou salvo. Um dos possÃveis usos é
+ gerar informações de auditoria. Por exemplo, o seguinte <literal>Interceptor</literal> seta
+ automaticamente o atributo <literal>createTimestamp</literal> quando um
+ <literal>Auditable</literal> é criada e atualiza o atributo <literal>lastUpdateTimestamp</literal>
+ quando um <literal>Auditable</literal> é atualizado.
+ </para>
+
+ <para>
+ Você pode implementar <literal>Auditable</literal> diretamente ou pode estender
+ <literal>EmptyInterceptor</literal>, sendo que a segunda é considerada a melhor opção.
+ </para>
+
+ <programlisting><![CDATA[package org.hibernate.test;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.Iterator;
+
+import org.hibernate.EmptyInterceptor;
+import org.hibernate.Transaction;
+import org.hibernate.type.Type;
+
+public class AuditInterceptor extends EmptyInterceptor {
+
+ private int updates;
+ private int creates;
+ private int loads;
+
+ public void onDelete(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+ // do nothing
+ }
+
+ public boolean onFlushDirty(Object entity,
+ Serializable id,
+ Object[] currentState,
+ Object[] previousState,
+ String[] propertyNames,
+ Type[] types) {
+
+ if ( entity instanceof Auditable ) {
+ updates++;
+ for ( int i=0; i < propertyNames.length; i++ ) {
+ if ( "lastUpdateTimestamp".equals( propertyNames[i] ) ) {
+ currentState[i] = new Date();
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public boolean onLoad(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+ if ( entity instanceof Auditable ) {
+ loads++;
+ }
+ return false;
+ }
+
+ public boolean onSave(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+
+ if ( entity instanceof Auditable ) {
+ creates++;
+ for ( int i=0; i<propertyNames.length; i++ ) {
+ if ( "createTimestamp".equals( propertyNames[i] ) ) {
+ state[i] = new Date();
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public void afterTransactionCompletion(Transaction tx) {
+ if ( tx.wasCommitted() ) {
+ System.out.println("Creations: " + creates + ", Updates: " + updates, "Loads: " + loads);
+ }
+ updates=0;
+ creates=0;
+ loads=0;
+ }
+
+}]]></programlisting>
+
+ <para>
+ Os interceptadores podem ser aplicados em dois diferentes escopos:
+ No escopo da <literal>Session</literal> e no escopo
+ <literal>SessionFactory</literal>.
+ </para>
+
+ <para>
+ Um interceptador no escopo da <literal>Session</literal> é definido quando
+ uma sessão é aberta usando o método sobrecarregado da SessionFactory.openSession()
+ que aceita um <literal>Interceptor</literal> como parâmetro.
+ </para>
+
+ <programlisting><![CDATA[Session session = sf.openSession( new AuditInterceptor() );]]></programlisting>
+
+ <para>
+ Um interceptador no escopo da <literal>SessionFactory</literal> é definido no objeto
+ <literal>Configuration</literal> antes da <literal>SessionFactory</literal> ser instanciada.
+ Nesse caso, o interceptador fornecido será aplicado para todas as sessões abertas por aquela
+ <literal>SessionFactory</literal>; Isso apenas não ocorrerá caso seja especificado um
+ interceptador no momento em que a sessão for aberta. Um interceptador no escopo de
+ <literal>SessionFactory</literal> deve ser thread safe, tomando-se o cuidado de não
+ armazenar atributos de estado especÃficos da sessão, pois, provavelmente, múltiplas
+ sessões irão utilizar esse interceptador simultaneamente.
+ </para>
+
+ <programlisting><![CDATA[new Configuration().setInterceptor( new AuditInterceptor() );]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="objectstate-events" revision="4">
+ <title>Sistema de Eventos</title>
+
+ <para>
+ Se você precisa executar uma ação em determinados eventos da camada de persistência,
+ você também pode usar a arquitetura de <emphasis>event</emphasis> do Hibernate3.
+ Um evento do sistema pode ser utilizado como complemento ou em
+ substituição a um interceptador.
+ </para>
+
+ <para>
+ Essencialmente todos os métodos da interface <literal>Session</literal> possuem um evento
+ correlacionado. Se você tiver um <literal>LoadEvent</literal>, um <literal>LoadEvent</literal>,
+ etc (consulte o DTD do XML de configuração ou o pacote <literal>org.hibernate.event</literal>
+ para a lista completa dos tipos de eventos). Quando uma requisição é feita em um desses métodos,
+ a <literal>Session</literal> do hibernate gera um evento apropriado e o envia para o listener
+ de evento correspondente àquele tipo de evento. Esses listeners implementam a mesma lógica
+ que aqueles métodos, trazendo os mesmos resultados. Entretanto, você é livre para implementar
+ uma customização de um desses listeners (isto é, o <literal>LoadEvent</literal> é processado
+ pela implementação registrada da interface <literal>LoadEventListener</literal>), então sua
+ implementação vai ficar responsável por processar qualquer requisição <literal>load()</literal>
+ feita pela <literal>Session</literal>.
+ </para>
+
+ <para>
+ Para todos os efeitos esses listeners deve ser considerados singletons; ou seja, eles são
+ compartilhados entre as requisições, e assim sendo, não devem salvar nenhum estado das
+ variáveis instanciadas.
+ </para>
+
+ <para>
+ Um listener personalizado deve implementar a interface referente ao evento a ser
+ processado e/ou deve estender a classes base equivalente (ou mesmo os listeners padrões
+ usados pelo Hibernate, eles não são declarados como finais com esse objetivo). O listener
+ personalizado pode ser registrado programaticamente no objeto <literal>Configuration</literal>,
+ ou declarativamente no XML de configuração do Hibernate (o registro do listener no propertie
+ de configuração não é suportado). Aqui temos um exemplo de como carregar um listener
+ personalizado:
+ </para>
+
+ <programlisting><![CDATA[public class MyLoadListener implements LoadEventListener {
+ // this is the single method defined by the LoadEventListener interface
+ public void onLoad(LoadEvent event, LoadEventListener.LoadType loadType)
+ throws HibernateException {
+ if ( !MySecurity.isAuthorized( event.getEntityClassName(), event.getEntityId() ) ) {
+ throw MySecurityException("Unauthorized access");
+ }
+ }
+}]]></programlisting>
+
+ <para>
+ Você também precisa adicionar uma entrada no XML de configuração do Hibernate para
+ registrar declarativamente qual listener deve se utilizado em conjunto com o listener
+ padrão:
+ </para>
+
+<programlisting><![CDATA[<hibernate-configuration>
+ <session-factory>
+ ...
+ <event type="load">
+ <listener class="com.eg.MyLoadListener"/>
+ <listener class="org.hibernate.event.def.DefaultLoadEventListener"/>
+ </event>
+ </session-factory>
+</hibernate-configuration>]]></programlisting>
+
+ <para>
+ Ou, você pode registrar o listener programaticamente:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = new Configuration();
+LoadEventListener[] stack = { new MyLoadListener(), new DefaultLoadEventListener() };
+cfg.EventListeners().setLoadEventListeners(stack);]]></programlisting>
+
+ <para>
+ Listeners registrados declarativamente não compartilham da mesma instancia. Se o mesmo
+ nome da classe é utilizado em vários elementos e <literal><listener/></literal>,
+ cada um vai resultar em uma instancia separada dessa classe. Se você tem a necessidade
+ de compartilhar uma instancia de um listener entre diversos tipos de listeners você
+ deve registrar o listener programaticamente.
+
+ </para>
+
+ <para>
+ Mas porque implementar uma interface e definir o tipo especÃfico durante a configuração?
+ Bem, um listener pode implementar vários listeners de evento. Com o tipo sendo definido
+ durante o registro, fica fácil ligar ou desligar listeners personalizados durante
+ a configuração.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-decl-security" revision="2">
+ <title>Hibernate declarative security</title>
+ <para>
+ Usually, declarative security in Hibernate applications is managed in a session facade
+ layer. Now, Hibernate3 allows certain actions to be permissioned via JACC, and authorized
+ via JAAS. This is optional functionality built on top of the event architecture.
+ </para>
+
+ <para>
+ First, you must configure the appropriate event listeners, to enable the use of JAAS
+ authorization.
+ </para>
+
+ <programlisting><![CDATA[<listener type="pre-delete" class="org.hibernate.secure.JACCPreDeleteEventListener"/>
+<listener type="pre-update" class="org.hibernate.secure.JACCPreUpdateEventListener"/>
+<listener type="pre-insert" class="org.hibernate.secure.JACCPreInsertEventListener"/>
+<listener type="pre-load" class="org.hibernate.secure.JACCPreLoadEventListener"/>]]></programlisting>
+
+ <para>
+ Note that <literal><listener type="..." class="..."/></literal> is just a shorthand
+ for <literal><event type="..."><listener class="..."/></event></literal>
+ when there is exactly one listener for a particular event type.
+ </para>
+
+ <para>
+ Next, still in <literal>hibernate.cfg.xml</literal>, bind the permissions to roles:
+ </para>
+
+ <programlisting><![CDATA[<grant role="admin" entity-name="User" actions="insert,update,read"/>
+<grant role="su" entity-name="User" actions="*"/>]]></programlisting>
+
+ <para>
+ The role names are the roles understood by your JACC provider.
+ </para>
+
+ </sect1>
+
+</chapter>
+
Property changes on: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/events.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/example_mappings.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/example_mappings.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/example_mappings.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,663 @@
+<chapter id="example-mappings">
+ <title>Exemplo: Vários Mapeamentos</title>
+
+ <para>
+ Este capitulo mostra algums mapeamentos de associações mais complexos.
+ </para>
+
+ <sect1 id="example-mappings-emp">
+ <title>Employer/Employee</title>
+
+ <para>
+ O modelo de seguinte relacionamento entre <literal>Employer</literal> e
+ <literal>Employee</literal> utiliza uma entidade de classe atual (<literal>Employment</literal>)
+ para representar a associação. Isto é feito porque pode-ser ter mais do que um perÃodo de
+ trabalho para as duas partes envolvidas. Outros Componentes são usados para modelar
+ valores monetários e os nomes do empregado.
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/EmployerEmployee.gif" format="GIF" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/EmployerEmployee.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ Abaixo o código de um possÃvel mapeamento:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Employer" table="employers">
+ <id name="id">
+ <generator class="sequence">
+ <param name="sequence">employer_id_seq</param>
+ </generator>
+ </id>
+ <property name="name"/>
+ </class>
+
+ <class name="Employment" table="employment_periods">
+
+ <id name="id">
+ <generator class="sequence">
+ <param name="sequence">employment_id_seq</param>
+ </generator>
+ </id>
+ <property name="startDate" column="start_date"/>
+ <property name="endDate" column="end_date"/>
+
+ <component name="hourlyRate" class="MonetaryAmount">
+ <property name="amount">
+ <column name="hourly_rate" sql-type="NUMERIC(12, 2)"/>
+ </property>
+ <property name="currency" length="12"/>
+ </component>
+
+ <many-to-one name="employer" column="employer_id" not-null="true"/>
+ <many-to-one name="employee" column="employee_id" not-null="true"/>
+
+ </class>
+
+ <class name="Employee" table="employees">
+ <id name="id">
+ <generator class="sequence">
+ <param name="sequence">employee_id_seq</param>
+ </generator>
+ </id>
+ <property name="taxfileNumber"/>
+ <component name="name" class="Name">
+ <property name="firstName"/>
+ <property name="initial"/>
+ <property name="lastName"/>
+ </component>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ E abaixo o esquema da tabela gerado pelo <literal>SchemaExport</literal>.
+ </para>
+
+ <programlisting><![CDATA[create table employers (
+ id BIGINT not null,
+ name VARCHAR(255),
+ primary key (id)
+)
+
+create table employment_periods (
+ id BIGINT not null,
+ hourly_rate NUMERIC(12, 2),
+ currency VARCHAR(12),
+ employee_id BIGINT not null,
+ employer_id BIGINT not null,
+ end_date TIMESTAMP,
+ start_date TIMESTAMP,
+ primary key (id)
+)
+
+create table employees (
+ id BIGINT not null,
+ firstName VARCHAR(255),
+ initial CHAR(1),
+ lastName VARCHAR(255),
+ taxfileNumber VARCHAR(255),
+ primary key (id)
+)
+
+alter table employment_periods
+ add constraint employment_periodsFK0 foreign key (employer_id) references employers
+alter table employment_periods
+ add constraint employment_periodsFK1 foreign key (employee_id) references employees
+create sequence employee_id_seq
+create sequence employment_id_seq
+create sequence employer_id_seq]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-mappings-authorwork">
+ <title>Author/Work</title>
+
+ <para>
+ Considere o seguinte modelo de relacionamento entre <literal>Work</literal>,
+ <literal>Author</literal> e <literal>Person</literal>. Nós representamos o relacionamento
+ entre <literal>Work</literal> e <literal>Author</literal> como uma associação
+ muitos-para-muitos. Nós escolhemos representar o relacionamento entre <literal>Author</literal>
+ e <literal>Person</literal> como uma associação um-para-um. Outra possibilidade seria ter
+ <literal>Author</literal> extendendo <literal>Person</literal>.
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/AuthorWork.gif" format="GIF" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/AuthorWork.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ O mapeamento do código seguinte representa corretamente estes relacionamentos:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Work" table="works" discriminator-value="W">
+
+ <id name="id" column="id">
+ <generator class="native"/>
+ </id>
+ <discriminator column="type" type="character"/>
+
+ <property name="title"/>
+ <set name="authors" table="author_work">
+ <key column name="work_id"/>
+ <many-to-many class="Author" column name="author_id"/>
+ </set>
+
+ <subclass name="Book" discriminator-value="B">
+ <property name="text"/>
+ </subclass>
+
+ <subclass name="Song" discriminator-value="S">
+ <property name="tempo"/>
+ <property name="genre"/>
+ </subclass>
+
+ </class>
+
+ <class name="Author" table="authors">
+
+ <id name="id" column="id">
+ <!-- The Author must have the same identifier as the Person -->
+ <generator class="assigned"/>
+ </id>
+
+ <property name="alias"/>
+ <one-to-one name="person" constrained="true"/>
+
+ <set name="works" table="author_work" inverse="true">
+ <key column="author_id"/>
+ <many-to-many class="Work" column="work_id"/>
+ </set>
+
+ </class>
+
+ <class name="Person" table="persons">
+ <id name="id" column="id">
+ <generator class="native"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ There are four tables in this mapping. <literal>works</literal>,
+ <literal>authors</literal> and <literal>persons</literal> hold work, author
+ and person data respectively. <literal>author_work</literal> is an association
+ table linking authors to works. Heres the table schema, as generated by
+ <literal>SchemaExport</literal>.
+ Existem quatro tabelas neste mapeamento. <literal>works</literal>,
+ <literal>authors</literal> e <literal>persons</literal> recebem os dados de work,
+ author e person, respectivamente. <literal>author_work</literal> é uma tabela de
+ associação que liga authors à works. Abaixo o esquema das tabelas, gerados pelo
+ <literal>SchemaExport</literal>.
+ </para>
+
+ <programlisting><![CDATA[create table works (
+ id BIGINT not null generated by default as identity,
+ tempo FLOAT,
+ genre VARCHAR(255),
+ text INTEGER,
+ title VARCHAR(255),
+ type CHAR(1) not null,
+ primary key (id)
+)
+
+create table author_work (
+ author_id BIGINT not null,
+ work_id BIGINT not null,
+ primary key (work_id, author_id)
+)
+
+create table authors (
+ id BIGINT not null generated by default as identity,
+ alias VARCHAR(255),
+ primary key (id)
+)
+
+create table persons (
+ id BIGINT not null generated by default as identity,
+ name VARCHAR(255),
+ primary key (id)
+)
+
+alter table authors
+ add constraint authorsFK0 foreign key (id) references persons
+alter table author_work
+ add constraint author_workFK0 foreign key (author_id) references authors
+alter table author_work
+ add constraint author_workFK1 foreign key (work_id) references works]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-mappings-customerorderproduct">
+ <title>Customer/Order/Product</title>
+
+ <para>
+ Agora considere um modelo de relacionamento entre <literal>Customer</literal>,
+ <literal>Order</literal> e <literal>LineItem</literal> e <literal>Product</literal>.
+ Existe uma associação um-para-muitos entre <literal>Customer</literal> e
+ <literal>Order</literal>, mas como devemos representar <literal>Order</literal> /
+ <literal>LineItem</literal> / <literal>Product</literal>? Eu escolhi mapear LineItem
+ como uma classe de associação representando a associação muitos-para-muitos entre
+ <literal>Order</literal> and <literal>Product</literal>. No Hibernate, isto é conhecido
+ como um elemento composto.
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/CustomerOrderProduct.gif" format="GIF" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/CustomerOrderProduct.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ O código do mapeamento:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Customer" table="customers">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="name"/>
+ <set name="orders" inverse="true">
+ <key column="customer_id"/>
+ <one-to-many class="Order"/>
+ </set>
+ </class>
+
+ <class name="Order" table="orders">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="date"/>
+ <many-to-one name="customer" column="customer_id"/>
+ <list name="lineItems" table="line_items">
+ <key column="order_id"/>
+ <list-index column="line_number"/>
+ <composite-element class="LineItem">
+ <property name="quantity"/>
+ <many-to-one name="product" column="product_id"/>
+ </composite-element>
+ </list>
+ </class>
+
+ <class name="Product" table="products">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="serialNumber"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ <literal>customers</literal>, <literal>orders</literal>, <literal>line_items</literal> e
+ <literal>products</literal> recebem os dados de customer, order, line_item e product,
+ respectivamente. <literal>line_items</literal> também atua como uma tabela de associação
+ ligando orders com products.
+ </para>
+
+ <programlisting><![CDATA[create table customers (
+ id BIGINT not null generated by default as identity,
+ name VARCHAR(255),
+ primary key (id)
+)
+
+create table orders (
+ id BIGINT not null generated by default as identity,
+ customer_id BIGINT,
+ date TIMESTAMP,
+ primary key (id)
+)
+
+create table line_items (
+ line_number INTEGER not null,
+ order_id BIGINT not null,
+ product_id BIGINT,
+ quantity INTEGER,
+ primary key (order_id, line_number)
+)
+
+create table products (
+ id BIGINT not null generated by default as identity,
+ serialNumber VARCHAR(255),
+ primary key (id)
+)
+
+alter table orders
+ add constraint ordersFK0 foreign key (customer_id) references customers
+alter table line_items
+ add constraint line_itemsFK0 foreign key (product_id) references products
+alter table line_items
+ add constraint line_itemsFK1 foreign key (order_id) references orders]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="misc">
+ <title>Exemplos variados de mapeamento</title>
+
+ <para>
+ Todos estes exemplos são retirados do conjunto de testes do Hibernate.
+ Lá, você encontrará vários outros exemplos úteis de mapeamentos.
+ Verifique o diretorio <literal>test</literal> da distribuição do Hibernate.
+ </para>
+
+ <para>TODO: put words around this stuff</para>
+
+ <sect2 id="example-mappings-typed-onetone">
+ <title>Associação um-para-um "Tipadas"</title>
+<programlisting><![CDATA[<class name="Person">
+ <id name="name"/>
+ <one-to-one name="address"
+ cascade="all">
+ <formula>name</formula>
+ <formula>'HOME'</formula>
+ </one-to-one>
+ <one-to-one name="mailingAddress"
+ cascade="all">
+ <formula>name</formula>
+ <formula>'MAILING'</formula>
+ </one-to-one>
+</class>
+
+<class name="Address" batch-size="2"
+ check="addressType in ('MAILING', 'HOME', 'BUSINESS')">
+ <composite-id>
+ <key-many-to-one name="person"
+ column="personName"/>
+ <key-property name="type"
+ column="addressType"/>
+ </composite-id>
+ <property name="street" type="text"/>
+ <property name="state"/>
+ <property name="zip"/>
+</class>]]></programlisting>
+ </sect2>
+
+ <sect2 id="example-mappings-composite-key">
+ <title>Exemplo de chave composta</title>
+<programlisting><![CDATA[<class name="Customer">
+
+ <id name="customerId"
+ length="10">
+ <generator class="assigned"/>
+ </id>
+
+ <property name="name" not-null="true" length="100"/>
+ <property name="address" not-null="true" length="200"/>
+
+ <list name="orders"
+ inverse="true"
+ cascade="save-update">
+ <key column="customerId"/>
+ <index column="orderNumber"/>
+ <one-to-many class="Order"/>
+ </list>
+
+</class>
+
+<class name="Order" table="CustomerOrder" lazy="true">
+ <synchronize table="LineItem"/>
+ <synchronize table="Product"/>
+
+ <composite-id name="id"
+ class="Order$Id">
+ <key-property name="customerId" length="10"/>
+ <key-property name="orderNumber"/>
+ </composite-id>
+
+ <property name="orderDate"
+ type="calendar_date"
+ not-null="true"/>
+
+ <property name="total">
+ <formula>
+ ( select sum(li.quantity*p.price)
+ from LineItem li, Product p
+ where li.productId = p.productId
+ and li.customerId = customerId
+ and li.orderNumber = orderNumber )
+ </formula>
+ </property>
+
+ <many-to-one name="customer"
+ column="customerId"
+ insert="false"
+ update="false"
+ not-null="true"/>
+
+ <bag name="lineItems"
+ fetch="join"
+ inverse="true"
+ cascade="save-update">
+ <key>
+ <column name="customerId"/>
+ <column name="orderNumber"/>
+ </key>
+ <one-to-many class="LineItem"/>
+ </bag>
+
+</class>
+
+<class name="LineItem">
+
+ <composite-id name="id"
+ class="LineItem$Id">
+ <key-property name="customerId" length="10"/>
+ <key-property name="orderNumber"/>
+ <key-property name="productId" length="10"/>
+ </composite-id>
+
+ <property name="quantity"/>
+
+ <many-to-one name="order"
+ insert="false"
+ update="false"
+ not-null="true">
+ <column name="customerId"/>
+ <column name="orderNumber"/>
+ </many-to-one>
+
+ <many-to-one name="product"
+ insert="false"
+ update="false"
+ not-null="true"
+ column="productId"/>
+
+</class>
+
+<class name="Product">
+ <synchronize table="LineItem"/>
+
+ <id name="productId"
+ length="10">
+ <generator class="assigned"/>
+ </id>
+
+ <property name="description"
+ not-null="true"
+ length="200"/>
+ <property name="price" length="3"/>
+ <property name="numberAvailable"/>
+
+ <property name="numberOrdered">
+ <formula>
+ ( select sum(li.quantity)
+ from LineItem li
+ where li.productId = productId )
+ </formula>
+ </property>
+
+</class>]]></programlisting>
+ </sect2>
+
+ <sect2 id="example-mappings-composite-key-manytomany">
+ <title>Mmuitos-para-muitos com atributo de chave composta compartilhada</title>
+<programlisting><![CDATA[<class name="User" table="`User`">
+ <composite-id>
+ <key-property name="name"/>
+ <key-property name="org"/>
+ </composite-id>
+ <set name="groups" table="UserGroup">
+ <key>
+ <column name="userName"/>
+ <column name="org"/>
+ </key>
+ <many-to-many class="Group">
+ <column name="groupName"/>
+ <formula>org</formula>
+ </many-to-many>
+ </set>
+</class>
+
+<class name="Group" table="`Group`">
+ <composite-id>
+ <key-property name="name"/>
+ <key-property name="org"/>
+ </composite-id>
+ <property name="description"/>
+ <set name="users" table="UserGroup" inverse="true">
+ <key>
+ <column name="groupName"/>
+ <column name="org"/>
+ </key>
+ <many-to-many class="User">
+ <column name="userName"/>
+ <formula>org</formula>
+ </many-to-many>
+ </set>
+</class>
+]]></programlisting>
+ </sect2>
+
+ <sect2 id="example-mappings-content-discrimination">
+ <title>Conteúdo baseado em descriminação</title>
+<programlisting><![CDATA[<class name="Person"
+ discriminator-value="P">
+
+ <id name="id"
+ column="person_id"
+ unsaved-value="0">
+ <generator class="native"/>
+ </id>
+
+
+ <discriminator
+ type="character">
+ <formula>
+ case
+ when title is not null then 'E'
+ when salesperson is not null then 'C'
+ else 'P'
+ end
+ </formula>
+ </discriminator>
+
+ <property name="name"
+ not-null="true"
+ length="80"/>
+
+ <property name="sex"
+ not-null="true"
+ update="false"/>
+
+ <component name="address">
+ <property name="address"/>
+ <property name="zip"/>
+ <property name="country"/>
+ </component>
+
+ <subclass name="Employee"
+ discriminator-value="E">
+ <property name="title"
+ length="20"/>
+ <property name="salary"/>
+ <many-to-one name="manager"/>
+ </subclass>
+
+ <subclass name="Customer"
+ discriminator-value="C">
+ <property name="comments"/>
+ <many-to-one name="salesperson"/>
+ </subclass>
+
+</class>]]></programlisting>
+ </sect2>
+
+ <sect2 id="example-mappings-association-alternatekeys" revision="2">
+ <title>Associações em chaves alternativas</title>
+<programlisting><![CDATA[<class name="Person">
+
+ <id name="id">
+ <generator class="hilo"/>
+ </id>
+
+ <property name="name" length="100"/>
+
+ <one-to-one name="address"
+ property-ref="person"
+ cascade="all"
+ fetch="join"/>
+
+ <set name="accounts"
+ inverse="true">
+ <key column="userId"
+ property-ref="userId"/>
+ <one-to-many class="Account"/>
+ </set>
+
+ <property name="userId" length="8"/>
+
+</class>
+
+<class name="Address">
+
+ <id name="id">
+ <generator class="hilo"/>
+ </id>
+
+ <property name="address" length="300"/>
+ <property name="zip" length="5"/>
+ <property name="country" length="25"/>
+ <many-to-one name="person" unique="true" not-null="true"/>
+
+</class>
+
+<class name="Account">
+ <id name="accountId" length="32">
+ <generator class="uuid"/>
+ </id>
+
+ <many-to-one name="user"
+ column="userId"
+ property-ref="userId"/>
+
+ <property name="type" not-null="true"/>
+
+</class>]]></programlisting>
+ </sect2>
+
+ </sect1>
+
+</chapter>
+
Property changes on: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/example_mappings.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/example_parentchild.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/example_parentchild.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/example_parentchild.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,361 @@
+<chapter id="example-parentchild">
+ <title>Example: Parent/Child</title>
+
+ <para>
+ One of the very first things that new users try to do with Hibernate is to model a parent / child type
+ relationship. There are two different approaches to this. For various reasons the most convenient
+ approach, especially for new users, is to model both <literal>Parent</literal> and <literal>Child</literal>
+ as entity classes with a <literal><one-to-many></literal> association from <literal>Parent</literal>
+ to <literal>Child</literal>. (The alternative approach is to declare the <literal>Child</literal> as a
+ <literal><composite-element></literal>.) Now, it turns out that default semantics of a one to many
+ association (in Hibernate) are much less close to the usual semantics of a parent / child relationship than
+ those of a composite element mapping. We will explain how to use a <emphasis>bidirectional one to many
+ association with cascades</emphasis> to model a parent / child relationship efficiently and elegantly.
+ It's not at all difficult!
+ </para>
+
+ <sect1 id="example-parentchild-collections">
+ <title>A note about collections</title>
+
+ <para>
+ Hibernate collections are considered to be a logical part of their owning entity; never of the
+ contained entities. This is a crucial distinction! It has the following consequences:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ When we remove / add an object from / to a collection, the version number of the collection owner
+ is incremented.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ If an object that was removed from a collection is an instance of a value type (eg, a composite
+ element), that object will cease to be persistent and its state will be completely removed from
+ the database. Likewise, adding a value type instance to the collection will cause its state to be
+ immediately persistent.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ On the other hand, if an entity is removed from a collection (a one-to-many or many-to-many
+ association), it will not be deleted, by default. This behaviour is completely consistent - a
+ change to the internal state of another entity should not cause the associated entity to vanish!
+ Likewise, adding an entity to a collection does not cause that entity to become persistent, by
+ default.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Instead, the default behaviour is that adding an entity to a collection merely creates a link between
+ the two entities, while removing it removes the link. This is very appropriate for all sorts of cases.
+ Where it is not appropriate at all is the case of a parent / child relationship, where the life of the
+ child is bound to the lifecycle of the parent.
+ </para>
+
+ </sect1>
+
+ <sect1 id="example-parentchild-bidir">
+ <title>Bidirectional one-to-many</title>
+
+ <para>
+ Suppose we start with a simple <literal><one-to-many></literal> association from
+ <literal>Parent</literal> to <literal>Child</literal>.
+ </para>
+
+ <programlisting><![CDATA[<set name="children">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ If we were to execute the following code
+ </para>
+
+ <programlisting><![CDATA[Parent p = .....;
+Child c = new Child();
+p.getChildren().add(c);
+session.save(c);
+session.flush();]]></programlisting>
+
+ <para>
+ Hibernate would issue two SQL statements:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>an <literal>INSERT</literal> to create the record for <literal>c</literal></para>
+ </listitem>
+ <listitem>
+ <para>
+ an <literal>UPDATE</literal> to create the link from <literal>p</literal> to
+ <literal>c</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ This is not only inefficient, but also violates any <literal>NOT NULL</literal> constraint on the
+ <literal>parent_id</literal> column. We can fix the nullability constraint violation by specifying
+ <literal>not-null="true"</literal> in the collection mapping:
+ </para>
+
+ <programlisting><![CDATA[<set name="children">
+ <key column="parent_id" not-null="true"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ However, this is not the recommended solution.
+ </para>
+ <para>
+ The underlying cause of this behaviour is that the link (the foreign key <literal>parent_id</literal>)
+ from <literal>p</literal> to <literal>c</literal> is not considered part of the state of the
+ <literal>Child</literal> object and is therefore not created in the <literal>INSERT</literal>. So the
+ solution is to make the link part of the <literal>Child</literal> mapping.
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="parent" column="parent_id" not-null="true"/>]]></programlisting>
+
+ <para>
+ (We also need to add the <literal>parent</literal> property to the <literal>Child</literal> class.)
+ </para>
+
+ <para>
+ Now that the <literal>Child</literal> entity is managing the state of the link, we tell the collection
+ not to update the link. We use the <literal>inverse</literal> attribute.
+ </para>
+
+ <programlisting><![CDATA[<set name="children" inverse="true">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ The following code would be used to add a new <literal>Child</literal>
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+Child c = new Child();
+c.setParent(p);
+p.getChildren().add(c);
+session.save(c);
+session.flush();]]></programlisting>
+
+ <para>
+ And now, only one SQL <literal>INSERT</literal> would be issued!
+ </para>
+
+ <para>
+ To tighten things up a bit, we could create an <literal>addChild()</literal> method of
+ <literal>Parent</literal>.
+ </para>
+
+ <programlisting><![CDATA[public void addChild(Child c) {
+ c.setParent(this);
+ children.add(c);
+}]]></programlisting>
+
+ <para>
+ Now, the code to add a <literal>Child</literal> looks like
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+Child c = new Child();
+p.addChild(c);
+session.save(c);
+session.flush();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-parentchild-cascades">
+ <title>Cascading lifecycle</title>
+
+ <para>
+ The explicit call to <literal>save()</literal> is still annoying. We will address this by
+ using cascades.
+ </para>
+
+ <programlisting><![CDATA[<set name="children" inverse="true" cascade="all">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ This simplifies the code above to
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+Child c = new Child();
+p.addChild(c);
+session.flush();]]></programlisting>
+
+ <para>
+ Similarly, we don't need to iterate over the children when saving or deleting a <literal>Parent</literal>.
+ The following removes <literal>p</literal> and all its children from the database.
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+session.delete(p);
+session.flush();]]></programlisting>
+
+ <para>
+ However, this code
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+Child c = (Child) p.getChildren().iterator().next();
+p.getChildren().remove(c);
+c.setParent(null);
+session.flush();]]></programlisting>
+
+ <para>
+ will not remove <literal>c</literal> from the database; it will ony remove the link to <literal>p</literal>
+ (and cause a <literal>NOT NULL</literal> constraint violation, in this case). You need to explicitly
+ <literal>delete()</literal> the <literal>Child</literal>.
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+Child c = (Child) p.getChildren().iterator().next();
+p.getChildren().remove(c);
+session.delete(c);
+session.flush();]]></programlisting>
+
+ <para>
+ Now, in our case, a <literal>Child</literal> can't really exist without its parent. So if we remove
+ a <literal>Child</literal> from the collection, we really do want it to be deleted. For this, we must
+ use <literal>cascade="all-delete-orphan"</literal>.
+ </para>
+
+ <programlisting><![CDATA[<set name="children" inverse="true" cascade="all-delete-orphan">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ Note: even though the collection mapping specifies <literal>inverse="true"</literal>, cascades are
+ still processed by iterating the collection elements. So if you require that an object be saved,
+ deleted or updated by cascade, you must add it to the collection. It is not enough to simply call
+ <literal>setParent()</literal>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="example-parentchild-update">
+ <title>Cascades and <literal>unsaved-value</literal></title>
+
+ <para>
+ Suppose we loaded up a <literal>Parent</literal> in one <literal>Session</literal>, made some changes
+ in a UI action and wish to persist these changes in a new session by calling <literal>update()</literal>.
+ The <literal>Parent</literal> will contain a collection of childen and, since cascading update is enabled,
+ Hibernate needs to know which children are newly instantiated and which represent existing rows in the
+ database. Lets assume that both <literal>Parent</literal> and <literal>Child</literal> have genenerated
+ identifier properties of type <literal>Long</literal>. Hibernate will use the identifier and
+ version/timestamp property value to determine which of the children are new. (See
+ <xref linkend="objectstate-saveorupdate"/>.) <emphasis>In Hibernate3, it is no longer necessary to specify
+ an <literal>unsaved-value</literal> explicitly.</emphasis>
+ </para>
+
+ <para>
+ The following code will update <literal>parent</literal> and <literal>child</literal> and insert
+ <literal>newChild</literal>.
+ </para>
+
+ <programlisting><![CDATA[//parent and child were both loaded in a previous session
+parent.addChild(child);
+Child newChild = new Child();
+parent.addChild(newChild);
+session.update(parent);
+session.flush();]]></programlisting>
+
+ <para>
+ Well, that's all very well for the case of a generated identifier, but what about assigned identifiers
+ and composite identifiers? This is more difficult, since Hibernate can't use the identifier property to
+ distinguish between a newly instantiated object (with an identifier assigned by the user) and an
+ object loaded in a previous session. In this case, Hibernate will either use the timestamp or version
+ property, or will actually query the second-level cache or, worst case, the database, to see if the
+ row exists.
+ </para>
+
+ <!-- undocumenting
+ <para>
+ There is one further possibility. The <literal>Interceptor</literal> method named
+ <literal>isUnsaved()</literal> lets the application implement its own strategy for distinguishing
+ newly instantiated objects. For example, you could define a base class for your persistent classes.
+ </para>
+
+ <programlisting><![CDATA[public class Persistent {
+ private boolean _saved = false;
+ public void onSave() {
+ _saved=true;
+ }
+ public void onLoad() {
+ _saved=true;
+ }
+ ......
+ public boolean isSaved() {
+ return _saved;
+ }
+}]]></programlisting>
+
+ <para>
+ (The <literal>saved</literal> property is non-persistent.)
+ Now implement <literal>isUnsaved()</literal>, along with <literal>onLoad()</literal>
+ and <literal>onSave()</literal> as follows.
+ </para>
+
+ <programlisting><![CDATA[public Boolean isUnsaved(Object entity) {
+ if (entity instanceof Persistent) {
+ return new Boolean( !( (Persistent) entity ).isSaved() );
+ }
+ else {
+ return null;
+ }
+}
+
+public boolean onLoad(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+
+ if (entity instanceof Persistent) ( (Persistent) entity ).onLoad();
+ return false;
+}
+
+public boolean onSave(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+
+ if (entity instanceof Persistent) ( (Persistent) entity ).onSave();
+ return false;
+}]]></programlisting>
+
+ <para>
+ Don't worry; in Hibernate3 you don't need to write any of this kind of code if you don't want to.
+ </para>
+ -->
+ </sect1>
+
+ <sect1 id="example-parentchild-conclusion">
+ <title>Conclusion</title>
+
+ <para>
+ There is quite a bit to digest here and it might look confusing first time around. However, in practice,
+ it all works out very nicely. Most Hibernate applications use the parent / child pattern in many places.
+ </para>
+
+ <para>
+ We mentioned an alternative in the first paragraph. None of the above issues exist in the case of
+ <literal><composite-element></literal> mappings, which have exactly the semantics of a parent / child
+ relationship. Unfortunately, there are two big limitations to composite element classes: composite elements
+ may not own collections, and they should not be the child of any entity other than the unique parent.
+ </para>
+
+ </sect1>
+
+</chapter>
\ No newline at end of file
Property changes on: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/example_parentchild.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/example_weblog.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/example_weblog.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/example_weblog.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,430 @@
+<chapter id="example-weblog">
+ <title>Example: Weblog Application</title>
+
+ <sect1 id="example-weblog-classes">
+ <title>Persistent Classes</title>
+
+ <para>
+ The persistent classes represent a weblog, and an item posted
+ in a weblog. They are to be modelled as a standard parent/child
+ relationship, but we will use an ordered bag, instead of a set.
+ </para>
+
+ <programlisting><![CDATA[package eg;
+
+import java.util.List;
+
+public class Blog {
+ private Long _id;
+ private String _name;
+ private List _items;
+
+ public Long getId() {
+ return _id;
+ }
+ public List getItems() {
+ return _items;
+ }
+ public String getName() {
+ return _name;
+ }
+ public void setId(Long long1) {
+ _id = long1;
+ }
+ public void setItems(List list) {
+ _items = list;
+ }
+ public void setName(String string) {
+ _name = string;
+ }
+}]]></programlisting>
+
+ <programlisting><![CDATA[package eg;
+
+import java.text.DateFormat;
+import java.util.Calendar;
+
+public class BlogItem {
+ private Long _id;
+ private Calendar _datetime;
+ private String _text;
+ private String _title;
+ private Blog _blog;
+
+ public Blog getBlog() {
+ return _blog;
+ }
+ public Calendar getDatetime() {
+ return _datetime;
+ }
+ public Long getId() {
+ return _id;
+ }
+ public String getText() {
+ return _text;
+ }
+ public String getTitle() {
+ return _title;
+ }
+ public void setBlog(Blog blog) {
+ _blog = blog;
+ }
+ public void setDatetime(Calendar calendar) {
+ _datetime = calendar;
+ }
+ public void setId(Long long1) {
+ _id = long1;
+ }
+ public void setText(String string) {
+ _text = string;
+ }
+ public void setTitle(String string) {
+ _title = string;
+ }
+}]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-weblog-mappings">
+ <title>Hibernate Mappings</title>
+
+ <para>
+ The XML mappings should now be quite straightforward.
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="eg">
+
+ <class
+ name="Blog"
+ table="BLOGS">
+
+ <id
+ name="id"
+ column="BLOG_ID">
+
+ <generator class="native"/>
+
+ </id>
+
+ <property
+ name="name"
+ column="NAME"
+ not-null="true"
+ unique="true"/>
+
+ <bag
+ name="items"
+ inverse="true"
+ order-by="DATE_TIME"
+ cascade="all">
+
+ <key column="BLOG_ID"/>
+ <one-to-many class="BlogItem"/>
+
+ </bag>
+
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="eg">
+
+ <class
+ name="BlogItem"
+ table="BLOG_ITEMS"
+ dynamic-update="true">
+
+ <id
+ name="id"
+ column="BLOG_ITEM_ID">
+
+ <generator class="native"/>
+
+ </id>
+
+ <property
+ name="title"
+ column="TITLE"
+ not-null="true"/>
+
+ <property
+ name="text"
+ column="TEXT"
+ not-null="true"/>
+
+ <property
+ name="datetime"
+ column="DATE_TIME"
+ not-null="true"/>
+
+ <many-to-one
+ name="blog"
+ column="BLOG_ID"
+ not-null="true"/>
+
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-weblog-code">
+ <title>Hibernate Code</title>
+
+ <para>
+ The following class demonstrates some of the kinds of things
+ we can do with these classes, using Hibernate.
+ </para>
+
+ <programlisting><![CDATA[package eg;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Iterator;
+import java.util.List;
+
+import org.hibernate.HibernateException;
+import org.hibernate.Query;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.tool.hbm2ddl.SchemaExport;
+
+public class BlogMain {
+
+ private SessionFactory _sessions;
+
+ public void configure() throws HibernateException {
+ _sessions = new Configuration()
+ .addClass(Blog.class)
+ .addClass(BlogItem.class)
+ .buildSessionFactory();
+ }
+
+ public void exportTables() throws HibernateException {
+ Configuration cfg = new Configuration()
+ .addClass(Blog.class)
+ .addClass(BlogItem.class);
+ new SchemaExport(cfg).create(true, true);
+ }
+
+ public Blog createBlog(String name) throws HibernateException {
+
+ Blog blog = new Blog();
+ blog.setName(name);
+ blog.setItems( new ArrayList() );
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ session.persist(blog);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return blog;
+ }
+
+ public BlogItem createBlogItem(Blog blog, String title, String text)
+ throws HibernateException {
+
+ BlogItem item = new BlogItem();
+ item.setTitle(title);
+ item.setText(text);
+ item.setBlog(blog);
+ item.setDatetime( Calendar.getInstance() );
+ blog.getItems().add(item);
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ session.update(blog);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return item;
+ }
+
+ public BlogItem createBlogItem(Long blogid, String title, String text)
+ throws HibernateException {
+
+ BlogItem item = new BlogItem();
+ item.setTitle(title);
+ item.setText(text);
+ item.setDatetime( Calendar.getInstance() );
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ Blog blog = (Blog) session.load(Blog.class, blogid);
+ item.setBlog(blog);
+ blog.getItems().add(item);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return item;
+ }
+
+ public void updateBlogItem(BlogItem item, String text)
+ throws HibernateException {
+
+ item.setText(text);
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ session.update(item);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ }
+
+ public void updateBlogItem(Long itemid, String text)
+ throws HibernateException {
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ BlogItem item = (BlogItem) session.load(BlogItem.class, itemid);
+ item.setText(text);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ }
+
+ public List listAllBlogNamesAndItemCounts(int max)
+ throws HibernateException {
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ List result = null;
+ try {
+ tx = session.beginTransaction();
+ Query q = session.createQuery(
+ "select blog.id, blog.name, count(blogItem) " +
+ "from Blog as blog " +
+ "left outer join blog.items as blogItem " +
+ "group by blog.name, blog.id " +
+ "order by max(blogItem.datetime)"
+ );
+ q.setMaxResults(max);
+ result = q.list();
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return result;
+ }
+
+ public Blog getBlogAndAllItems(Long blogid)
+ throws HibernateException {
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ Blog blog = null;
+ try {
+ tx = session.beginTransaction();
+ Query q = session.createQuery(
+ "from Blog as blog " +
+ "left outer join fetch blog.items " +
+ "where blog.id = :blogid"
+ );
+ q.setParameter("blogid", blogid);
+ blog = (Blog) q.uniqueResult();
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return blog;
+ }
+
+ public List listBlogsAndRecentItems() throws HibernateException {
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ List result = null;
+ try {
+ tx = session.beginTransaction();
+ Query q = session.createQuery(
+ "from Blog as blog " +
+ "inner join blog.items as blogItem " +
+ "where blogItem.datetime > :minDate"
+ );
+
+ Calendar cal = Calendar.getInstance();
+ cal.roll(Calendar.MONTH, false);
+ q.setCalendar("minDate", cal);
+
+ result = q.list();
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return result;
+ }
+}]]></programlisting>
+
+ </sect1>
+
+</chapter>
+
Property changes on: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/example_weblog.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/filters.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/filters.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/filters.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,128 @@
+<chapter id="filters">
+ <title>Filtrando dados</title>
+
+ <para>
+ O Hibernate3 provê um novo método inovador para manusear dados com regras de "visibilidade".
+ Um <emphasis>Filtro do Hibernate</emphasis> é um filtro global, nomeado e parametrizado que pode
+ se habilitado ou não dentro de um Session do Hibernate.
+ </para>
+
+ <sect1 id="objectstate-filters">
+ <title>Filtros do Hibernate</title>
+
+ <para>
+ O Hibernate tem a habilidade de pré definir os critérios do filtro e anexar esses filtros no
+ nÃvel da classe e no nÃvel da coleção. Um critério do filtro é a habilidade de definir uma
+ cláusula restritiva muito semelhante ao atributo "where" disponÃvel para a classe e várias
+ coleções. A não ser que essas condições de filtros podem ser parametrizadas. A aplicação
+ pode, então, fazer uma decisão em tempo de execução se os filtros definidos devem estar
+ habilitados e quais valores seus parâmetros devem ter. Os filtros podem ser usados como
+ Views de bancos de dados, mas com parametros internos à aplicação.
+ </para>
+
+ <para>
+ Para usar esses filtros, eles primeiramente devem ser definidos e anexados aos elementos do
+ mapeamento apropriados. Para definir um filtro, use o elemento <literal><filter-def/></literal>
+ dentro do elemento <literal><hibernate-mapping/></literal>:
+ </para>
+
+ <programlisting><![CDATA[<filter-def name="myFilter">
+ <filter-param name="myFilterParam" type="string"/>
+</filter-def>]]></programlisting>
+
+ <para>
+ Então esse filtro pode ser anexo à uma classe:
+ </para>
+
+ <programlisting><![CDATA[<class name="myClass" ...>
+ ...
+ <filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
+</class>]]></programlisting>
+
+ <para>
+ ou em uma coleção:
+ </para>
+
+ <programlisting><![CDATA[<set ...>
+ <filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
+</set>]]></programlisting>
+
+ <para>
+ ou mesmo para ambos (ou muitos de cada) ao mesmo tempo.
+ </para>
+
+ <para>
+ Os métodos na <literal>Session</literal> são: <literal>enableFilter(String filterName)</literal>,
+ <literal>getEnabledFilter(String filterName)</literal>, e <literal>disableFilter(String filterName)</literal>.
+ Por padrão, os filtros não são habilitados dentro de qualquer session; Eles devem ser explicitamente
+ habilitados usando o método <literal>Session.enabledFilter()</literal>, que retorna uma instância da
+ interface <literal>Filter</literal>. Usando o filtro simples definido acima, o código se pareceria
+ com o seguinte:
+ </para>
+
+ <programlisting><![CDATA[session.enableFilter("myFilter").setParameter("myFilterParam", "some-value");]]></programlisting>
+
+ <para>
+ Veja que os métodos da interface org.hibernate.Filter permite o encadeamento de funções, comum à maioria das funções do Hibernate.
+ </para>
+
+ <para>
+ Um exemplo completo, usando dados temporais com um padrão efetivo de registro de datas:
+ </para>
+
+ <programlisting><![CDATA[<filter-def name="effectiveDate">
+ <filter-param name="asOfDate" type="date"/>
+</filter-def>
+
+<class name="Employee" ...>
+...
+ <many-to-one name="department" column="dept_id" class="Department"/>
+ <property name="effectiveStartDate" type="date" column="eff_start_dt"/>
+ <property name="effectiveEndDate" type="date" column="eff_end_dt"/>
+...
+ <!--
+ Note that this assumes non-terminal records have an eff_end_dt set to
+ a max db date for simplicity-sake
+ -->
+ <filter name="effectiveDate"
+ condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
+</class>
+
+<class name="Department" ...>
+...
+ <set name="employees" lazy="true">
+ <key column="dept_id"/>
+ <one-to-many class="Employee"/>
+ <filter name="effectiveDate"
+ condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ Para garantir que você sempre tenha registro efetivos, simplesmente habilite o filtro
+ na session antes de recuperar os dados dos empregados:
+ </para>
+
+<programlisting><![CDATA[Session session = ...;
+session.enabledFilter("effectiveDate").setParameter("asOfDate", new Date());
+List results = session.createQuery("from Employee as e where e.salary > :targetSalary")
+ .setLong("targetSalary", new Long(1000000))
+ .list();
+]]></programlisting>
+
+ <para>
+ No HQL acima, mesmo que mencionamos apenas uma restrição de salário nos resultados, por causa
+ do filtro habilitado, a consulta retornará apenas os funcionários ativos cujo salário é maior
+ que um milhão de dólares.
+ </para>
+
+ <para>
+ Nota: se você planeja usar filtros com outer join (por HQL ou por load fetching) seja cuidadoso
+ na direção da expressão de condição. à mais seguro configura-lo com para um left outer join;
+ geralmente, coloque o parâmetro primeiro seguido pelo nome da coluna após o operador.
+ </para>
+
+ </sect1>
+
+</chapter>
+
Property changes on: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/filters.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/inheritance_mapping.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/inheritance_mapping.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/inheritance_mapping.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,501 @@
+<chapter id="inheritance">
+ <title>Mapeamento de Herança</title>
+
+ <sect1 id="inheritance-strategies" revision="3">
+ <title> As três estratégias</title>
+
+ <para>
+ O Hibernate suporta as três estratégias básicas de mapeamento de herança:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ tabela por hierarquia de classes
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ tabela por subclasse
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ tabela por classe concreta
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Adicionalmente, o Hibernate suporta uma quarta, um tipo levemente
+ diferente de polimorfismo:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ polimorfismo implÃcito
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ à possÃvel usar diferentes estratégias de mapeamento para diferentes
+ ramificações da mesma hierarquia de herança, e então fazer uso do
+ polimorfismo implÃcito para alcançar polimorfismo através da
+ hierarquia completa. De qualquer forma, O Hibernate não suporta a
+ mistura de mapeamentos <literal><subclass></literal>,
+ and <literal><joined-subclass></literal> e
+ <literal><union-subclass></literal> dentro do mesmo elemento
+ raiz <literal><class></literal>. à possÃvel usar junto à s estratégias
+ tabela por hierarquia e a tabela por subclasse, abaixo do mesmo elemento
+ <literal><class></literal>, combinando os elementos
+ <literal><subclass></literal> e <literal><join></literal>
+ (veja abaixo).
+
+ </para>
+
+ <para>
+ Ã possÃvel definir mapeamentos <literal>subclass</literal>, <literal>union-subclass</literal>,
+ e <literal>joined-subclass</literal> em documentos de mapeamento separados, diretamente abaixo de
+ <literal>hibernate-mapping</literal>. Isso permite a você estender uma hierarquia de classes
+ apenas adicionando um novo arquivo de mapeamento. Você deve especificar um atributo
+ <literal>extends</literal> no mapeamento da subclasse, nomeando uma superclasse previamente
+ mapeada. Nota: Anteriormente esta caracterÃstica fazia o ordenamento dos documentos de
+ mapeamento importantes. Desde o Hibernate3, o ordenamento dos arquivos de mapeamento não
+ importa quando usamos a palavra chave extends. O ordenamento dentro de um arquivo de mapeamento
+ simples ainda necessita ser definido como superclasse antes de subclasse.
+ </para>
+
+ <programlisting><![CDATA[
+ <hibernate-mapping>
+ <subclass name="DomesticCat" extends="Cat" discriminator-value="D">
+ <property name="name" type="string"/>
+ </subclass>
+ </hibernate-mapping>]]></programlisting>
+
+
+ <sect2 id="inheritance-tableperclass" >
+ <title>Tabela por hierarquia de classes</title>
+
+ <para>
+ Suponha que tenhamos uma interface <literal>Payment</literal>, com sua
+ implementação <literal>CreditCardPayment</literal>, <literal>CashPayment</literal>,
+ <literal>ChequePayment</literal>. O mapeamento da tabela por hierarquia
+ seria parecido com:
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment" table="PAYMENT">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <discriminator column="PAYMENT_TYPE" type="string"/>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <subclass name="CreditCardPayment" discriminator-value="CREDIT">
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </subclass>
+ <subclass name="CashPayment" discriminator-value="CASH">
+ ...
+ </subclass>
+ <subclass name="ChequePayment" discriminator-value="CHEQUE">
+ ...
+ </subclass>
+</class>]]></programlisting>
+
+ <para>
+ Exactly one table is required. There is one big limitation of this mapping
+ strategy: columns declared by the subclasses, such as <literal>CCTYPE</literal>,
+ may not have <literal>NOT NULL</literal> constraints.
+ </para>
+
+ </sect2>
+
+ <sect2 id="inheritance-tablepersubclass">
+ <title>Tabela por subclasse</title>
+
+ <para>
+ Um mapeamento de tabela por subclasse seria parecido com:
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment" table="PAYMENT">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <joined-subclass name="CreditCardPayment" table="CREDIT_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </joined-subclass>
+ <joined-subclass name="CashPayment" table="CASH_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ ...
+ </joined-subclass>
+ <joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ ...
+ </joined-subclass>
+</class>]]></programlisting>
+
+ <para>
+ Quatro tabelas são necessárias. As três tabelas subclasses possuem
+ associação de chave primária para a tabela de superclasse
+ (então o modelo relacional é atualmente uma associação de
+ um-para-um).
+ </para>
+
+ </sect2>
+
+ <sect2 id="inheritance-tablepersubclass-discriminator" revision="2">
+ <title>Tabela por subclasse, usando um discriminador</title>
+
+ <para>
+ Note que a implementação de tabela por subclasse do Hibernate não
+ necessita de coluna de discriminador. Outro mapeador objeto/relacional
+ usa uma implementação diferente de tabela por subclasse, que necessita
+ uma coluna com o tipo discriminador na tabela da superclasse. A
+ abordagem escolhida pelo Hibernate é muito mais difÃcil de implementar,
+ porém de forma argumentável mais correto de um ponto de vista relacional.
+ Se você deseja utilizar uma coluna discriminadora com a estratégia
+ tabela por subclasse, você pode combinar o uso de
+ <literal><subclass></literal> e <literal><join></literal>,
+ dessa maneira:
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment" table="PAYMENT">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <discriminator column="PAYMENT_TYPE" type="string"/>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <subclass name="CreditCardPayment" discriminator-value="CREDIT">
+ <join table="CREDIT_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </join>
+ </subclass>
+ <subclass name="CashPayment" discriminator-value="CASH">
+ <join table="CASH_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ ...
+ </join>
+ </subclass>
+ <subclass name="ChequePayment" discriminator-value="CHEQUE">
+ <join table="CHEQUE_PAYMENT" fetch="select">
+ <key column="PAYMENT_ID"/>
+ ...
+ </join>
+ </subclass>
+</class>]]></programlisting>
+
+ <para>
+ The optional <literal>fetch="select"</literal> declaration tells Hibernate
+ not to fetch the <literal>ChequePayment</literal> subclass data using an
+ outer join when querying the superclass.
+A declaração opcional fetch=âselectâ diz ao Hibernate para não buscar os dados da subclasse ChequePayment, quando usar um outer join pesquisando pela superclasse.
+ </para>
+
+ </sect2>
+
+ <sect2 id="inheritance-mixing-tableperclass-tablepersubclass">
+ <title>. Misturando tabela por hierarquia de classes com tabela por subclasse</title>
+
+ <para>
+ Você pode até mesmo misturar a estratégia de tabela por hierarquia e
+ tabela por subclasse usando esta abordagem:
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment" table="PAYMENT">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <discriminator column="PAYMENT_TYPE" type="string"/>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <subclass name="CreditCardPayment" discriminator-value="CREDIT">
+ <join table="CREDIT_PAYMENT">
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </join>
+ </subclass>
+ <subclass name="CashPayment" discriminator-value="CASH">
+ ...
+ </subclass>
+ <subclass name="ChequePayment" discriminator-value="CHEQUE">
+ ...
+ </subclass>
+</class>]]></programlisting>
+
+ <para>
+ Para qualquer uma dessas estratégias de mapeamento, uma associação
+ polimórfica para a classe raiz <literal>Payment</literal> deve ser
+ mapeada usando <literal><many-to-one></literal>.
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="payment" column="PAYMENT_ID" class="Payment"/>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="inheritance-tableperconcrete" revision="2">
+ <title>Tabela por classe concreta</title>
+
+ <para>
+ Existem duas formas que poderÃamos usar a respeito da estratégia de
+ mapeamento de tabela por classe concreta. A primeira é usar
+ <literal><union-subclass></literal>..
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="sequence"/>
+ </id>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <union-subclass name="CreditCardPayment" table="CREDIT_PAYMENT">
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </union-subclass>
+ <union-subclass name="CashPayment" table="CASH_PAYMENT">
+ ...
+ </union-subclass>
+ <union-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
+ ...
+ </union-subclass>
+</class>]]></programlisting>
+
+ <para>
+ Três tabelas estão envolvidas para as subclasses. Cada tabela define
+ colunas para todas as propriedades da classe, incluindo propriedades herdadas.
+ </para>
+
+ <para>
+ A limitação dessa abordagem é que se uma propriedade é mapeada na
+ superclasse, o nome da coluna deve ser o mesmo em todas as tabelas das
+ subclasses. (Nós devemos melhorar isto em um futuro release do Hibernate).
+ A estratégia do gerador de identidade não é permitida em união de
+ subclasses(union-subclass) herdadas, na verdade a fonte de chave
+ primária deve ser compartilhada através de todas subclasses unidas da
+ hierarquia.
+
+ </para>
+
+ <para>
+ Se sua superclasse é abstrata, mapeie ela com <literal>abstract="true"</literal>.
+ Claro, que se ela não for abstrata, uma tabela (padrão para <literal>PAYMENT</literal>
+ no exemplo acima) adicional é necessária para segurar as instâncias da superclasse.
+ </para>
+
+ </sect2>
+
+ <sect2 id="inheritance-tableperconcreate-polymorphism">
+ <title>Tabela por classe concreta, usando polimorfismo implÃcito</title>
+
+ <para>
+ Uma abordagem alternativa é fazer uso de polimorfismo implÃcito:
+ </para>
+
+ <programlisting><![CDATA[<class name="CreditCardPayment" table="CREDIT_PAYMENT">
+ <id name="id" type="long" column="CREDIT_PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="amount" column="CREDIT_AMOUNT"/>
+ ...
+</class>
+
+<class name="CashPayment" table="CASH_PAYMENT">
+ <id name="id" type="long" column="CASH_PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="amount" column="CASH_AMOUNT"/>
+ ...
+</class>
+
+<class name="ChequePayment" table="CHEQUE_PAYMENT">
+ <id name="id" type="long" column="CHEQUE_PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="amount" column="CHEQUE_AMOUNT"/>
+ ...
+</class>]]></programlisting>
+
+ <para>
+ Veja que em nenhum lugar mencionamos a interface <literal>Payment</literal>
+ explicitamente. Também preste atenção que propriedades de <literal>Payment</literal>
+ são mapeadas em cada uma das subclasses. Se você quer evitar duplicação,
+ considere usar entidades de XML (ex. (e.g. <literal>[ <!ENTITY allproperties
+ SYSTEM "allproperties.xml"> ]</literal> na declaração do <literal>DOCTYPE</literal>
+ e <literal>&allproperties;</literal> no mapeamento).
+ </para>
+
+ <para>
+ A desvantagem dessa abordagem é que o Hibernate não gera <literal>UNION</literal>s
+ SQL quando executa pesquisas polimórficas.
+ </para>
+
+ <para>
+ Para essa estratégia, uma associação polimórfica para <literal>Payment</literal>
+ geralmente é mapeada usando <literal><any></literal>.
+ </para>
+
+ <programlisting><![CDATA[<any name="payment" meta-type="string" id-type="long">
+ <meta-value value="CREDIT" class="CreditCardPayment"/>
+ <meta-value value="CASH" class="CashPayment"/>
+ <meta-value value="CHEQUE" class="ChequePayment"/>
+ <column name="PAYMENT_CLASS"/>
+ <column name="PAYMENT_ID"/>
+</any>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="inheritace-mixingpolymorphism">
+ <title>Misturando polimorfismo implÃcito com outros mapeamentos de herança</title>
+
+ <para>
+ Ainda existe uma coisa para ser observada com respeito a este mapeamento.
+ Desde que as subclasses sejam mapeadas em seu próprio elemento
+ <literal><class></literal> (e desde que <literal>Payment</literal> seja
+ apenas uma interface), cada uma das subclasses pode ser facilmente parte de uma
+ outra hierarquia de herança! (E você ainda pode usar pesquisas polimórficas
+ em cima da interface <literal>Payment</literal>.)
+
+ </para>
+
+ <programlisting><![CDATA[<class name="CreditCardPayment" table="CREDIT_PAYMENT">
+ <id name="id" type="long" column="CREDIT_PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <discriminator column="CREDIT_CARD" type="string"/>
+ <property name="amount" column="CREDIT_AMOUNT"/>
+ ...
+ <subclass name="MasterCardPayment" discriminator-value="MDC"/>
+ <subclass name="VisaPayment" discriminator-value="VISA"/>
+</class>
+
+<class name="NonelectronicTransaction" table="NONELECTRONIC_TXN">
+ <id name="id" type="long" column="TXN_ID">
+ <generator class="native"/>
+ </id>
+ ...
+ <joined-subclass name="CashPayment" table="CASH_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ <property name="amount" column="CASH_AMOUNT"/>
+ ...
+ </joined-subclass>
+ <joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ <property name="amount" column="CHEQUE_AMOUNT"/>
+ ...
+ </joined-subclass>
+</class>]]></programlisting>
+
+ <para>
+ Mais uma vez, nós não mencionamos <literal>Payment</literal> explicitamente.
+ Se nós executarmos uma pesquisa em cima da interface <literal>Payment</literal> â
+ por exemplo, <literal>from Payment</literal> â o Hibernate retorna
+ automaticamente instâncias de <literal>CreditCardPayment</literal> (e suas
+ subclasses, desde que elas também implementem <literal>Payment</literal>),
+ <literal>CashPayment</literal> e <literal>ChequePayment</literal> mas
+ não as instâncias de <literal>NonelectronicTransaction</literal>.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="inheritance-limitations">
+ <title>Limitações</title>
+
+ <para>
+ Existem certas limitações para a abordagem do "polimorfismo implÃcito"
+ comparada com a estratégia de mapeamento da tabela por classe concreta.
+ Existe uma limitação um tanto menos restritiva para mapeamentos
+ <literal><union-subclass></literal>.
+
+ </para>
+
+ <para>
+ A tabela seguinte demonstra as limitações do mapeamento de tabela
+ por classe concreta e do polimorfismo implÃcito no Hibernate.
+ </para>
+
+ <table frame="topbot">
+ <title>Features of inheritance mappings</title>
+ <tgroup cols='8' align='left' colsep='1' rowsep='1'>
+ <colspec colname='c1' colwidth="1*"/>
+ <colspec colname='c2' colwidth="1*"/>
+ <colspec colname='c3' colwidth="1*"/>
+ <colspec colname='c4' colwidth="1*"/>
+ <colspec colname='c5' colwidth="1*"/>
+ <colspec colname='c6' colwidth="1*"/>
+ <colspec colname='c7' colwidth="1*"/>
+ <colspec colname='c8' colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Estratégia de Herança</entry>
+ <entry>muitos-para-um Polimórfico</entry>
+ <entry>um-para-um Polimórfico</entry>
+ <entry>um-para-muitos Polimórfico</entry>
+ <entry>muitos-para-muitos Polimórfico</entry>
+ <entry><literal>load()/get()</literal> Polimórfico </entry>
+ <entry>Pesquisas Polimórficas</entry>
+ <entry>Joins polimórficos</entry>
+ <entry>Outer join fetching</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>table per class-hierarchy</entry>
+ <entry><literal><many-to-one></literal></entry>
+ <entry><literal><one-to-one></literal></entry>
+ <entry><literal><one-to-many></literal></entry>
+ <entry><literal><many-to-many></literal></entry>
+ <entry><literal>s.get(Payment.class, id)</literal></entry>
+ <entry><literal>from Payment p</literal></entry>
+ <entry><literal>from Order o join o.payment p</literal></entry>
+ <entry><emphasis>supported</emphasis></entry>
+ </row>
+ <row>
+ <entry>table per subclass</entry>
+ <entry><literal><many-to-one></literal></entry>
+ <entry><literal><one-to-one></literal></entry>
+ <entry><literal><one-to-many></literal></entry>
+ <entry><literal><many-to-many></literal></entry>
+ <entry><literal>s.get(Payment.class, id)</literal></entry>
+ <entry><literal>from Payment p</literal></entry>
+ <entry><literal>from Order o join o.payment p</literal></entry>
+ <entry><emphasis>supported</emphasis></entry>
+ </row>
+ <row>
+ <entry>table per concrete-class (union-subclass)</entry>
+ <entry><literal><many-to-one></literal></entry>
+ <entry><literal><one-to-one></literal></entry>
+ <entry><literal><one-to-many></literal> (for <literal>inverse="true"</literal> only)</entry>
+ <entry><literal><many-to-many></literal></entry>
+ <entry><literal>s.get(Payment.class, id)</literal></entry>
+ <entry><literal>from Payment p</literal></entry>
+ <entry><literal>from Order o join o.payment p</literal></entry>
+ <entry><emphasis>supported</emphasis></entry>
+ </row>
+ <row>
+ <entry>table per concrete class (implicit polymorphism)</entry>
+ <entry><literal><any></literal></entry>
+ <entry><emphasis>not supported</emphasis></entry>
+ <entry><emphasis>not supported</emphasis></entry>
+ <entry><literal><many-to-any></literal></entry>
+ <entry><literal>s.createCriteria(Payment.class).add( Restrictions.idEq(id) ).uniqueResult()</literal></entry>
+ <entry><literal>from Payment p</literal></entry>
+ <entry><emphasis>not supported</emphasis></entry>
+ <entry><emphasis>not supported</emphasis></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect1>
+
+</chapter>
Property changes on: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/inheritance_mapping.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/performance.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/performance.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/performance.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1384 @@
+<chapter id="performance">
+ <title>Aumentando a performance</title>
+
+ <sect1 id="performance-fetching" revision="2">
+ <title>Estratégias de Fetching</title>
+
+ <para>
+ Uma <emphasis>estratégia de fetching</emphasis> é a estratégia que o Hibernate
+ irá usar para buscar objetos associados se a aplicação precisar navegar pela associação.
+ Estratégias de Fetch podem ser declaradas nos metadados de mapeamento O/R, ou sobrescritos
+ por uma query HQL ou query com <literal>Criteria</literal>.
+ </para>
+
+ <para>
+ Hibernate3 define as seguintes estratégias de fetching:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Join fetching</emphasis> - o Hibernate busca o objeto
+ ou coleção associada no mesmo <literal>SELECT</literal>, usando um
+ <literal>OUTER JOIN</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Select fetching</emphasis> - um segundo <literal>SELECT</literal> é
+ usado para buscar a entidade ou coleção associada. A menos que você desabilite
+ lazy fetching especificando <literal>lazy="false"</literal>, esse segundo SELECT
+ será executado apenas quando você acessar a associação.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Subselect fetching</emphasis> - um segundo <literal>SELECT</literal> será
+ usado para buscar as coleções associadas de todas as entidades buscadas na query ou
+ fetch anterior. A menos que você desabilite lazy fetching especificando
+ <literal>lazy="false"</literal>, esse segundo SELECT será executado apenas quando
+ você acessar a associação.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Batch fetching</emphasis> - uma opção de otimização para o Select
+ Fetching â O Hibernate busca um lote de instâncias ou entidades usando um único
+ <literal>SELECT</literal>, especificando uma lista de chaves primárias ou chaves
+ estrangeiras.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ O Hibernate distingue também entre:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Immediate fetching</emphasis> - uma associação, coleção ou atributo é buscado
+ como ela é carregada (Qual SQL é usado). Não se confuda com eles! Nós usamos fetch para melhorar a performance. Nós podemos usar lazy para definir um contrato para qual dado é sempre disponÃvel em qualquer instância desanexada de uma classe qualquer.
+ imediatamente, quando o pai é carregado.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Lazy collection fetching</emphasis> - a coleção é buscada quando a
+ aplicação invoca uma operação sobre aquela coleção (Esse é o padrão para coleções)
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>"Extra-lazy" collection fetching</emphasis> - elementos individuais de uma
+ coleção são acessados do banco de dados quando preciso. O Hibernate tenta não buscar
+ a coleção inteira dentro da memória ao menos que seja absolutamente preciso.
+ (indicado para coleções muito grandes)
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Proxy fetching</emphasis> - uma associação de um valor é carregada quando um método
+ diferente do getter do identificador é invocado sobre o objeto associado.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>"No-proxy" fetching</emphasis> - uma associação de um valor é carregada quando
+ a variável da instância é carregada. Comparada com a proxy fetching, esse método é menos
+ preguiçoso (lazy)(a associação é carregada somente quando o identificador é acessada)
+ mas é mais transparente, já que não há proxies visÃveis para a aplicação.
+ Esse método requer instrumentação de bytecodes em build-time e é raramente necessário.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Lazy attribute fetching</emphasis> - um atributo ou associação de um valor é
+ carregada quanto a varÃavel da instância é acessada. Esse método requer instrumentação
+ de bytecodes em build-time e é raramente necessário.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Nós temos aqui duas noções ortogonais: <emphasis>quando</emphasis> a associação é carregada e
+ <emphasis>como </emphasis> ela é carregada (Qual SQL é usado). Não se confuda com eles! Nós
+ usamos <literal>fetch</literal> para melhorar a performance. Nós podemos usar lazy para
+ definir um contrato para qual dado é sempre disponÃvel em qualquer instância desconectada
+ de uma classe qualquer.
+ </para>
+
+ <sect2 id="performance-fetching-lazy">
+ <title>Trabalhando com associações preguiçosas (lazy)</title>
+
+ <para>
+ Por padrão, o Hibernate3 usa busca preguiçosa para coleções e busca preguiçosa
+ com proxy para associações de um valor. Esses padrões fazem sentido para quase
+ todas as associações em quase todas a aplicações.
+ </para>
+
+ <para>
+ <emphasis>Veja:</emphasis> se voce setar
+ <literal>hibernate.default_batch_fetch_size</literal>, O Hibernate irá usar otimização
+ de carregamento em lote para o carregamento preguiçoso(Essa otimização pode ser também
+ habilitada em um nÃvel mais fino).
+ </para>
+
+ <para>
+ Porém, a busca preguiçosa tem um problema que você precisar saber. Acesso a associações
+ preguiçosas fora do contexto de uma sessão aberta do Hibernate irá resultar numa exceção.
+ Por exemplo:
+ </para>
+
+ <programlisting><![CDATA[s = sessions.openSession();
+Transaction tx = s.beginTransaction();
+
+User u = (User) s.createQuery("from User u where u.name=:userName")
+ .setString("userName", userName).uniqueResult();
+Map permissions = u.getPermissions();
+
+tx.commit();
+s.close();
+
+Integer accessLevel = (Integer) permissions.get("accounts"); // Error!]]></programlisting>
+
+ <para>
+ Como a coleção de permissões não foi inicializada quando a <literal>Session</literal>
+ foi fechada, a coleção não poderá carregar o seu estado. O Hibernate não suporta
+ inicialização preguiçosa para objetos desconectados. Para consertar isso, é necessário
+ mover o código que carrega a coleção para antes da transação ser comitada.
+ </para>
+
+ <para>
+ Alternativamente, nós podemos usar uma coleção ou associação não preguiçosa,
+ especificando <literal>lazy="false"</literal> para o mapeamento da associação.
+ Porém, é pretendido que a inicialização preguiçosa seja usada por quase todas as
+ coleções e associações. Se você definir muitas associações não preguiçosas em seu
+ modelo de objetos, o Hibernate irá precisar carregar o banco de dados inteiro na memória
+ em cada transação!
+ </para>
+
+ <para>
+ Por outro lado, nós geralmente escolhemos join fetching (que é não preguiçosa por natureza)
+ ao invés de select fetching em uma transação particular. Nós iremos ver como customizar
+ a estratégoa de busca. No Hibernate3, os mecanismos para escolher a estratégia de fetching
+ são identicos para as associações simples e para coleções.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-fetching-custom" revision="4">
+ <title>Personalizando as estratégias de busca</title>
+
+ <para>
+ O select fetching (o padrão) é extremamente vunerável para N+1 problemas em select,
+ então nós iremos querer habilitar o join fetching no documento de mapeamento:
+ </para>
+
+ <programlisting><![CDATA[<set name="permissions"
+ fetch="join">
+ <key column="userId"/>
+ <one-to-many class="Permission"/>
+</set]]></programlisting>
+
+ <programlisting><![CDATA[<many-to-one name="mother" class="Cat" fetch="join"/>]]></programlisting>
+
+ <para>
+ A estratégia de <literal>fetch</literal> definida no documento de mapeamento afeta:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ recupera via <literal>get()</literal> ou <literal>load()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Recuperações que acontecem implicitamente quando navegamos por uma associação
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>Criteria</literal> queries
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ buscas por HQL se buscar por <literal>subselect</literal> for usado
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Independentemente da estratégia de busca que você usar, o grafo não preguiçoso definido
+ será garantidamente carregado na memória. Note que isso irá resultar em diversos selects
+ imediatos sendo usados em um HQL em particular.
+ </para>
+
+ <para>
+ Usualmente não usamos documentos de mapeamento para customizar as buscas. Ao invés disso,
+ nós deixamos o comportamento padrão e sobrescrevemos isso em uma transação em particular,
+ usando <literal>left join fetch</literal> no HQL. Isso diz ao Hibernate para buscar a associação
+ inteira no primeiro select, usando um outer join. Na API de busca <literal>Criteria</literal>,
+ você irá usar <literal>setFetchMode(FetchMode.JOIN)</literal>.
+ </para>
+
+ <para>
+ Se você quiser mudar a estratégia de busca usada pelo <literal>get()</literal>
+ ou <literal>load()</literal>, simplesmente use uma query <literal>Criteria</literal>,
+ por exemplo:
+ </para>
+
+ <programlisting><![CDATA[User user = (User) session.createCriteria(User.class)
+ .setFetchMode("permissions", FetchMode.JOIN)
+ .add( Restrictions.idEq(userId) )
+ .uniqueResult();]]></programlisting>
+
+ <para>
+ (Isto é o equivalente do Hibernate para o que algumas soluções ORM chamam de "plano de busca")
+ </para>
+
+ <para>
+ Um meio totalmente diferente de evitar problemas com selects N+1 é usar um cache de segundo nÃvel.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-fetching-proxies" revision="2">
+ <title>Proxies de associação single-ended</title>
+
+ <para>
+ A recuperação preguiçosa para coleções é implementada usando uma implementação
+ própria do Hibernate para coleções persistentes. Porém, um mecanismo diferente é
+ necessário para comportamento preguiçoso para associações de um lado só. A entidade
+ alvo da associação precisa usar um proxy. O Hibernate implementa proxies para inicialização
+ preguiçosa em objetos persistentes usando manipulação de bytecode
+ (via a excelente biblioteca CGLIB).
+ </para>
+
+ <para>
+ Por padrão, o Hibernate3 gera proxies (na inicialização) para todas as classes
+ persistentes que usem eles para habilitar recuperaçãopreguiçosa de associações
+ <literal>many-to-one</literal> e <literal>one-to-one</literal>.
+ </para>
+
+ <para>
+ O arquivo de mapeamento deve declaram uma interface para usar como interface de proxy
+ para aquela classe, com o atributo <literal>proxy</literal>. Por padrão, o Hibernate usa
+ uma subclasse dessa classe. <emphasis>Note que a classe a ser usada via proxy precisa
+ implementar o construtor padrão com pelo menos visibilidade de package. Nós recomendamos
+ esse construtor para todas as classes persistentes!</emphasis>
+ </para>
+
+ <para>
+ Existe alguns truques que você deve saber quando extender esse comportamento para classes
+ polimórficas, dessa maneira:
+ </para>
+
+ <programlisting><![CDATA[<class name="Cat" proxy="Cat">
+ ......
+ <subclass name="DomesticCat">
+ .....
+ </subclass>
+</class>]]></programlisting>
+
+ <para>
+ Primeiramente, instâncias de <literal>Cat</literal> nunca seráo convertidas
+ para <literal>DomesticCat</literal>, mesmo que a instância em questão seja uma
+ estância de <literal>DomesticCat</literal>:
+ </para>
+
+ <programlisting><![CDATA[Cat cat = (Cat) session.load(Cat.class, id); // instantiate a proxy (does not hit the db)
+if ( cat.isDomesticCat() ) { // hit the db to initialize the proxy
+ DomesticCat dc = (DomesticCat) cat; // Error!
+ ....
+}]]></programlisting>
+
+ <para>
+ Ã possÃvel quebrar o proxy <literal>==</literal>.
+ </para>
+
+ <programlisting><![CDATA[Cat cat = (Cat) session.load(Cat.class, id); // instantiate a Cat proxy
+DomesticCat dc =
+ (DomesticCat) session.load(DomesticCat.class, id); // acquire new DomesticCat proxy!
+System.out.println(cat==dc); // false]]></programlisting>
+
+ <para>
+ Porém a situação não é tão ruim como parece. Mesmo quando temos duas referências para
+ objetos proxies diferentes, a instância deles será o mesmo objeto
+ </para>
+
+ <programlisting><![CDATA[cat.setWeight(11.0); // hit the db to initialize the proxy
+System.out.println( dc.getWeight() ); // 11.0]]></programlisting>
+
+ <para>
+ Terceiro, Você não pode usar um proxy CGLIB em uma classe <literal>final</literal>
+ ou com qualquer método <literal>final</literal>.
+ </para>
+
+ <para>
+ Finalmente, se o seu objeto persistente adquirir qualquer recursto durante a instanciação
+ (em inicializadores ou construtor padrão), então esses recursos serão adquiridos pelo proxy
+ também. A classe de proxy é uma subclasse da classe persistente.
+ </para>
+
+ <para>
+ Esses problemas são todos devido a limitação fundamental do modelo de herança simples do Java.
+ Se você quiser evitar esse problemas em suas classes persistentes você deve imeplementar uma
+ interface que declare seus métodos de negócio. Você deve especificar essas interfaces no arquivo
+ de mapeamento. Ex:
+ </para>
+
+ <programlisting><![CDATA[<class name="CatImpl" proxy="Cat">
+ ......
+ <subclass name="DomesticCatImpl" proxy="DomesticCat">
+ .....
+ </subclass>
+</class>]]></programlisting>
+
+ <para>
+ onde <literal>CatImpl</literal> implementa a interface <literal>Cat</literal> e
+ <literal>DomesticCatImpl</literal> implementa a interface <literal>DomesticCat</literal>.
+ Então proxies para instâncias de <literal>Cat</literal> e <literal>DomesticCat</literal>
+ serão retornadas por <literal>load()</literal> ou <literal>iterate()</literal>.
+ (Note que <literal>list()</literal> geralmente não retorna proxies).
+ </para>
+
+ <programlisting><![CDATA[Cat cat = (Cat) session.load(CatImpl.class, catid);
+Iterator iter = session.iterate("from CatImpl as cat where cat.name='fritz'");
+Cat fritz = (Cat) iter.next();]]></programlisting>
+
+ <para>
+ Relacionamentos são também carregados preguiçosamente. Isso significa que você precisa declarar
+ qualquer propriedade como sendo do tipo <literal>Cat</literal>, e não <literal>CatImpl</literal>.
+ </para>
+
+ <para>
+ Algumas operações <emphasis>não</emphasis> requerem inicialização por proxy:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>equals()</literal>, se a classe persistente não sobrescrever
+ <literal>equals()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>hashCode()</literal>, se a classe persistente não sobrescrever
+ <literal>hashCode()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ O método getter do identificador
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ O Hibernate irá detectar classes persistentes que sobrescrevem
+ <literal>equals()</literal> ou <literal>hashCode()</literal>.
+ </para>
+
+ <para>
+ Escolhendo <literal>lazy="no-proxy"</literal> ao invés do padrão
+ <literal>lazy="proxy"</literal>, podemos evitar problemas associados com typecasting.
+ Porém, iremos precisar de instrumentação de bytecode em tempo de compilação e todas
+ as operações irão resultar em iniciazações de proxy imediatas.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-fetching-initialization" revision="1">
+ <title>Inicializando coleções e proxies</title>
+
+ <para>
+ Será lançada uma <literal>LazyInitializationException</literal> se uma coleção não inicializada
+ ou proxy é acessado fora do escopo da <literal>Session</literal>, isto é, quando a entidade que
+ contém a coleção ou tem a referência ao proxy estiver no estado destachado.
+ </para>
+
+ <para>
+ Algumas vezes precisamos garantir qie o proxy ou coleção é inicializado antes de se fechar a
+ <literal>Session</literal>. Claro que sempre podemos forçar a inicialização chamando
+ <literal>cat.getSex()</literal> ou <literal>cat.getKittens().size()</literal>, por exemplo.
+ Mas isto parece confuso para quem lê o código e não é conveniente para códigos genéricos.
+ </para>
+
+ <para>
+ Os métodos estáticos <literal>Hibernate.initialize()</literal> e <literal>Hibernate.isInitialized()</literal>
+ possibilitam a aplicação uma maneira conveniente de trabalhar com coleções inicializadas preguiçosamente e
+ proxies. <literal>Hibernate.initialize(cat)</literal> irá forçar a inicialização de um proxy,
+ <literal>cat</literal>, contanto que a <literal>Session</literal> esteja ainda aberta.
+ <literal>Hibernate.initialize( cat.getKittens() )</literal> tem um efeito similar para a coleção de
+ kittens.
+ </para>
+
+ <para>
+ Outra opção é manter a <literal>Session</literal> aberta até que todas as coleções e
+ proxies necessários sejam carregados. Em algumas arquiteturas de aplicações, particularmente
+ onde o código que acessa os dados usando Hibernate e o código que usa os dados estão em diferentes
+ camadas da aplicação ou diferentes processos fÃsicos, será um problema garantir que a
+ <literal>Session</literal> esteja aberta quando uma coleção for inicializada. Existem dois
+ caminhos básicos para lidar com esse problema:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ Em aplicações web, um filtro servlet pode ser usado para fechar a
+ <literal>Session</literal> somente no final da requisição do usuário, já que a
+ renderização da visão estará completa (o pattern <emphasis>Open Session In View</emphasis>).
+ Claro, que isto cria a necessidade de um correto manuseio de exceções na infraestrutura
+ de sua aplicação. à vitalmente importante que a <literal>Session</literal> esteja fechada
+ e a transação terminada antes de retornar para o usuário, mesmo que uma exceção ocorra
+ durante a renderização da view. Veja o Wiki do Hibernate para exemplos do pattern
+ "Open Session In View"
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Em uma aplicação com uma camada de negócios separada, a lógica de negócios deve
+ "preparar" todas as coleções que serão usadas pela camada web antes de retornar.
+ Isto sgnifica que a camada de negócios deve carregar todos os dados e retorná-los
+ já inicializados para a camada de apresentação. Usualmente a aplicação chama
+ <literal>Hibernate.initialize()</literal> para cada coleção que será usada pela
+ camada web (essa chamada de método deve ocorrer antes da sessão ser fechada ou
+ retornar a coleção usando uma consulta Hibernate com uma cláusula <literal>FETCH</literal>
+ ou um <literal>FetchMode.JOIN</literal> na <literal>Criteria</literal>. Fica muito
+ mais fácil se você adotar o pattern <emphasis>Command</emphasis> ao invés do
+ <emphasis>Session Facade</emphasis>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Você também pode anexar um objeto prevaimente carregado em uma nova
+ <literal>Session</literal> <literal>merge()</literal> or <literal>lock()</literal>
+ antes de acessar coleções não inicializadas (ou outros proxies). O Hibernate não faz
+ e certamente <literal>não deve</literal> isso automaticamente pois isso introduziria
+ semantica em transações ad hoc.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ As vezes você não quer inicializar uma coleção muito grande, mas precisa de algumas
+ informações (como o tamanho) ou alguns de seus dados.
+ </para>
+
+ <para>
+ Você pode usar um filtro de coleção para saber seu tamanho sem a inicializar:
+ </para>
+
+ <programlisting><![CDATA[( (Integer) s.createFilter( collection, "select count(*)" ).list().get(0) ).intValue()]]></programlisting>
+
+ <para>
+ O método <literal>createFilter()</literal> é usado também para retornar algus dados de uma
+ coleção eficientemente sem precisar inicializar a coleção inteira:
+ </para>
+
+ <programlisting><![CDATA[s.createFilter( lazyCollection, "").setFirstResult(0).setMaxResults(10).list();]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="performance-fetching-batch">
+ <title>Usando busca em lote</title>
+
+ <para>
+ O Hibernate pode fazer uso eficiente de busca em lote, isto é, o Hibernate pode carregar diversos
+ proxies não inicializados se um proxy é acessado (ou coleções. A busca em lote é uma otimização da
+ estratégia de select fetching). Existe duas maneiras em que você pode usar busca em lote: no nÃvel
+ da classe ou no nÃvel da coleção.
+ </para>
+
+ <para>
+ A recuperação em lote para classes/entidades é mais fácil de entender. Imagine que você tem a seguinte
+ situação em tempo de execução: Você tem 25 instâncias de <literal>Cat</literal> carregadas em uma
+ <literal>Session</literal>, cada <literal>Cat</literal> tem uma referência ao seu <literal>owner</literal>,
+ que é da classe <literal>Person</literal>. A classe <literal>Person</literal> é mapeada com um proxy,
+ <literal>lazy="true"</literal>. Se você iterar sobre todos os Cat's e chamar <literal>getOwner()</literal>
+ em cada, o Hibernate irá por padrão executar 25 comandos <literal>SELECT()</literal>, para buscar os
+ proxies de owners. Você pode melhorar esse comportamento especificando um <literal>batch-size</literal>
+ no mapeamento da classe <literal>Person</literal>:
+ </para>
+
+ <programlisting><![CDATA[<class name="Person" batch-size="10">...</class>]]></programlisting>
+
+ <para>
+ O Hibernate irá executar agora apenas três consultas, buscando por vez, 10, 10 e 5 Person.
+ </para>
+
+ <para>
+ Você também pode habilitar busca em lote de uma coleção. Por exemplo, se cada <literal>Person</literal>
+ tem uma coleção preguiçosa de <literal>Cat</literal>s, e 10 pessoas estão já carregados em uma
+ <literal>Sesssion</literal>, serão gerados 10 <literal>SELECT</literal>s ao se iterar todas as
+ pessoas, um para cada chamada de <literal>getCats()</literal>.. Se você habilitar busca em lote
+ para a coleção de <literal>cats</literal> no mapeamento da classe <literal>Person</literal>,
+ o Hibernate pode fazer uma pré carga das coleções:
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <set name="cats" batch-size="3">
+ ...
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ Com um <literal>batch-size</literal>de 8, o Hibernate irá carregar 3, 3, 3, 1 coleções em 4
+ <literal>SELECT</literal>s. Novamente, o valor do atributo depende do número esperado de coleções
+ não inicialiadas em determinada <literal>Session</literal>.
+ </para>
+
+ <para>
+ A busca em lote de coleções é particularmente útil quando você tem uma árvore encadeada de
+ items, ex. o tÃpico padrão bill-of-materials (Se bem que um <emphasis>conjunto encadeado</emphasis>
+ ou <emphasis>caminho materializado </emphasis> pode ser uma opção melhor para árvores com mais
+ leitura)
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-fetching-subselect">
+ <title>Usando subselect fetching</title>
+
+ <para>
+ Se uma coleção ou proxy simples precisa ser recuperado, o Hibernate carrega todos eles
+ rodando novamente a query original em um subselect. Isso funciona da mesma maneira que busca
+ em lote, sem carregar tanto.
+ </para>
+
+ <!-- TODO: Write more about this -->
+
+ </sect2>
+
+ <sect2 id="performance-fetching-lazyproperties">
+ <title>Usando busca preguiçosa de propriedade</title>
+
+ <para>
+ O Hibernate3 suporta a carga posterior de propriedades individuais. Essa técnica de otimização
+ também conhecida como <emphasis>fetch groups</emphasis>. Veja que isso é mais uma funcionalidade
+ de marketing já que na prática, é mais importante otimização nas leituras dos registros do
+ que na leitura das colunas. Porém, carregar apenas algumas propriedades de uma classe pode
+ ser útil em casos extremos, onde tabelas legadas podem ter centenas de colunas e o modelo
+ de dados não pode ser melhorado.
+ </para>
+
+ <para>
+ Para habilitar a carga posterior de propriedade, é preciso setar o atributo
+ <literal>lazy</literal> no seu mapeamento de propriedade:
+ </para>
+
+ <programlisting><![CDATA[<class name="Document">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="name" not-null="true" length="50"/>
+ <property name="summary" not-null="true" length="200" lazy="true"/>
+ <property name="text" not-null="true" length="2000" lazy="true"/>
+</class>]]></programlisting>
+
+ <para>
+ A carga posterior de propriedades requer instrumentação de bytecode! Se suas classes
+ persistentes não forem melhoradas, o Hibernate irá ignorar silenciosamente essa
+ configuração e usará busca imediatamente.
+ </para>
+
+ <para>
+ Para instrumentação de bytecode, use a seguinte tarefa do Ant:
+ </para>
+
+ <programlisting><![CDATA[<target name="instrument" depends="compile">
+ <taskdef name="instrument" classname="org.hibernate.tool.instrument.InstrumentTask">
+ <classpath path="${jar.path}"/>
+ <classpath path="${classes.dir}"/>
+ <classpath refid="lib.class.path"/>
+ </taskdef>
+
+ <instrument verbose="true">
+ <fileset dir="${testclasses.dir}/org/hibernate/auction/model">
+ <include name="*.class"/>
+ </fileset>
+ </instrument>
+</target>]]></programlisting>
+
+ <para>
+ A different (better?) way to avoid unnecessary column reads, at least for
+ read-only transactions is to use the projection features of HQL or Criteria
+ queries. This avoids the need for buildtime bytecode processing and is
+ certainly a prefered solution.
+ </para>
+
+ <para>
+ You may force the usual eager fetching of properties using <literal>fetch all
+ properties</literal> in HQL.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="performance-cache" revision="1">
+ <title>The Second Level Cache</title>
+
+ <para>
+ A Hibernate <literal>Session</literal> is a transaction-level cache of persistent data. It is
+ possible to configure a cluster or JVM-level (<literal>SessionFactory</literal>-level) cache on
+ a class-by-class and collection-by-collection basis. You may even plug in a clustered cache. Be
+ careful. Caches are never aware of changes made to the persistent store by another application
+ (though they may be configured to regularly expire cached data).
+ </para>
+
+ <para revision="1">
+ You have the option to tell Hibernate which caching implementation to use by
+ specifying the name of a class that implements <literal>org.hibernate.cache.CacheProvider</literal>
+ using the property <literal>hibernate.cache.provider_class</literal>. Hibernate
+ comes bundled with a number of built-in integrations with open-source cache providers
+ (listed below); additionally, you could implement your own and plug it in as
+ outlined above. Note that versions prior to 3.2 defaulted to use EhCache as the
+ default cache provider; that is no longer the case as of 3.2.
+ </para>
+
+ <table frame="topbot" id="cacheproviders" revision="1">
+ <title>Cache Providers</title>
+ <tgroup cols='5' align='left' colsep='1' rowsep='1'>
+ <colspec colname='c1' colwidth="1*"/>
+ <colspec colname='c2' colwidth="3*"/>
+ <colspec colname='c3' colwidth="1*"/>
+ <colspec colname='c4' colwidth="1*"/>
+ <colspec colname='c5' colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Cache</entry>
+ <entry>Provider class</entry>
+ <entry>Type</entry>
+ <entry>Cluster Safe</entry>
+ <entry>Query Cache Supported</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>Hashtable (not intended for production use)</entry>
+ <entry><literal>org.hibernate.cache.HashtableCacheProvider</literal></entry>
+ <entry>memory</entry>
+ <entry></entry>
+ <entry>yes</entry>
+ </row>
+ <row>
+ <entry>EHCache</entry>
+ <entry><literal>org.hibernate.cache.EhCacheProvider</literal></entry>
+ <entry>memory, disk</entry>
+ <entry></entry>
+ <entry>yes</entry>
+ </row>
+ <row>
+ <entry>OSCache</entry>
+ <entry><literal>org.hibernate.cache.OSCacheProvider</literal></entry>
+ <entry>memory, disk</entry>
+ <entry></entry>
+ <entry>yes</entry>
+ </row>
+ <row>
+ <entry>SwarmCache</entry>
+ <entry><literal>org.hibernate.cache.SwarmCacheProvider</literal></entry>
+ <entry>clustered (ip multicast)</entry>
+ <entry>yes (clustered invalidation)</entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>JBoss TreeCache</entry>
+ <entry><literal>org.hibernate.cache.TreeCacheProvider</literal></entry>
+ <entry>clustered (ip multicast), transactional</entry>
+ <entry>yes (replication)</entry>
+ <entry>yes (clock sync req.)</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <sect2 id="performance-cache-mapping" revision="2">
+ <title>Cache mappings</title>
+
+ <para>
+ The <literal><cache></literal> element of a class or collection mapping has the
+ following form:
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="cache1" coords="2 70"/>
+ <area id="cache2" coords="3 70"/>
+ <area id="cache3" coords="4 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<cache
+ usage="transactional|read-write|nonstrict-read-write|read-only"
+ region="RegionName"
+ include="all|non-lazy"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="cache1">
+ <para>
+ <literal>usage</literal> (required) specifies the caching strategy:
+ <literal>transactional</literal>,
+ <literal>read-write</literal>,
+ <literal>nonstrict-read-write</literal> or
+ <literal>read-only</literal>
+ </para>
+ </callout>
+ <callout arearefs="cache2">
+ <para>
+ <literal>region</literal> (optional, defaults to the class or
+ collection role name) specifies the name of the second level cache
+ region
+ </para>
+ </callout>
+ <callout arearefs="cache3">
+ <para>
+ <literal>include</literal> (optional, defaults to <literal>all</literal>)
+ <literal>non-lazy</literal> specifies that properties of the entity mapped
+ with <literal>lazy="true"</literal> may not be cached when attribute-level
+ lazy fetching is enabled
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ Alternatively (preferrably?), you may specify <literal><class-cache></literal> and
+ <literal><collection-cache></literal> elements in <literal>hibernate.cfg.xml</literal>.
+ </para>
+
+ <para>
+ The <literal>usage</literal> attribute specifies a <emphasis>cache concurrency strategy</emphasis>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-cache-readonly">
+ <title>Strategy: read only</title>
+
+ <para>
+ If your application needs to read but never modify instances of a persistent class, a
+ <literal>read-only</literal> cache may be used. This is the simplest and best performing
+ strategy. It's even perfectly safe for use in a cluster.
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Immutable" mutable="false">
+ <cache usage="read-only"/>
+ ....
+</class>]]></programlisting>
+
+ </sect2>
+
+
+ <sect2 id="performance-cache-readwrite">
+ <title>Strategy: read/write</title>
+
+ <para>
+ If the application needs to update data, a <literal>read-write</literal> cache might be appropriate.
+ This cache strategy should never be used if serializable transaction isolation level is required.
+ If the cache is used in a JTA environment, you must specify the property
+ <literal>hibernate.transaction.manager_lookup_class</literal>, naming a strategy for obtaining the
+ JTA <literal>TransactionManager</literal>. In other environments, you should ensure that the transaction
+ is completed when <literal>Session.close()</literal> or <literal>Session.disconnect()</literal> is called.
+ If you wish to use this strategy in a cluster, you should ensure that the underlying cache implementation
+ supports locking. The built-in cache providers do <emphasis>not</emphasis>.
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Cat" .... >
+ <cache usage="read-write"/>
+ ....
+ <set name="kittens" ... >
+ <cache usage="read-write"/>
+ ....
+ </set>
+</class>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="performance-cache-nonstrict">
+ <title>Strategy: nonstrict read/write</title>
+
+ <para>
+ If the application only occasionally needs to update data (ie. if it is extremely unlikely that two
+ transactions would try to update the same item simultaneously) and strict transaction isolation is
+ not required, a <literal>nonstrict-read-write</literal> cache might be appropriate. If the cache is
+ used in a JTA environment, you must specify <literal>hibernate.transaction.manager_lookup_class</literal>.
+ In other environments, you should ensure that the transaction is completed when
+ <literal>Session.close()</literal> or <literal>Session.disconnect()</literal> is called.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-cache-transactional">
+ <title>Strategy: transactional</title>
+
+ <para>
+ The <literal>transactional</literal> cache strategy provides support for fully transactional cache
+ providers such as JBoss TreeCache. Such a cache may only be used in a JTA environment and you must
+ specify <literal>hibernate.transaction.manager_lookup_class</literal>.
+ </para>
+
+ </sect2>
+
+ <para>
+ None of the cache providers support all of the cache concurrency strategies. The following table shows
+ which providers are compatible with which concurrency strategies.
+ </para>
+
+ <table frame="topbot">
+ <title>Cache Concurrency Strategy Support</title>
+ <tgroup cols='5' align='left' colsep='1' rowsep='1'>
+ <colspec colname='c1' colwidth="1*"/>
+ <colspec colname='c2' colwidth="1*"/>
+ <colspec colname='c3' colwidth="1*"/>
+ <colspec colname='c4' colwidth="1*"/>
+ <colspec colname='c5' colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Cache</entry>
+ <entry>read-only</entry>
+ <entry>nonstrict-read-write</entry>
+ <entry>read-write</entry>
+ <entry>transactional</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>Hashtable (not intended for production use)</entry>
+ <entry>yes</entry>
+ <entry>yes</entry>
+ <entry>yes</entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>EHCache</entry>
+ <entry>yes</entry>
+ <entry>yes</entry>
+ <entry>yes</entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>OSCache</entry>
+ <entry>yes</entry>
+ <entry>yes</entry>
+ <entry>yes</entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>SwarmCache</entry>
+ <entry>yes</entry>
+ <entry>yes</entry>
+ <entry></entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>JBoss TreeCache</entry>
+ <entry>yes</entry>
+ <entry></entry>
+ <entry></entry>
+ <entry>yes</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect1>
+
+ <sect1 id="performance-sessioncache" revision="2">
+ <title>Managing the caches</title>
+
+ <para>
+ Whenever you pass an object to <literal>save()</literal>, <literal>update()</literal>
+ or <literal>saveOrUpdate()</literal> and whenever you retrieve an object using
+ <literal>load()</literal>, <literal>get()</literal>, <literal>list()</literal>,
+ <literal>iterate()</literal> or <literal>scroll()</literal>, that object is added
+ to the internal cache of the <literal>Session</literal>.
+ </para>
+ <para>
+ When <literal>flush()</literal> is subsequently called, the state of that object will
+ be synchronized with the database. If you do not want this synchronization to occur or
+ if you are processing a huge number of objects and need to manage memory efficiently,
+ the <literal>evict()</literal> method may be used to remove the object and its collections
+ from the first-level cache.
+ </para>
+
+ <programlisting><![CDATA[ScrollableResult cats = sess.createQuery("from Cat as cat").scroll(); //a huge result set
+while ( cats.next() ) {
+ Cat cat = (Cat) cats.get(0);
+ doSomethingWithACat(cat);
+ sess.evict(cat);
+}]]></programlisting>
+
+ <para>
+ The <literal>Session</literal> also provides a <literal>contains()</literal> method to determine
+ if an instance belongs to the session cache.
+ </para>
+
+ <para>
+ To completely evict all objects from the session cache, call <literal>Session.clear()</literal>
+ </para>
+
+ <para>
+ For the second-level cache, there are methods defined on <literal>SessionFactory</literal> for
+ evicting the cached state of an instance, entire class, collection instance or entire collection
+ role.
+ </para>
+
+ <programlisting><![CDATA[sessionFactory.evict(Cat.class, catId); //evict a particular Cat
+sessionFactory.evict(Cat.class); //evict all Cats
+sessionFactory.evictCollection("Cat.kittens", catId); //evict a particular collection of kittens
+sessionFactory.evictCollection("Cat.kittens"); //evict all kitten collections]]></programlisting>
+
+ <para>
+ The <literal>CacheMode</literal> controls how a particular session interacts with the second-level
+ cache.
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>CacheMode.NORMAL</literal> - read items from and write items to the second-level cache
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>CacheMode.GET</literal> - read items from the second-level cache, but don't write to
+ the second-level cache except when updating data
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>CacheMode.PUT</literal> - write items to the second-level cache, but don't read from
+ the second-level cache
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>CacheMode.REFRESH</literal> - write items to the second-level cache, but don't read from
+ the second-level cache, bypass the effect of <literal>hibernate.cache.use_minimal_puts</literal>, forcing
+ a refresh of the second-level cache for all items read from the database
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ To browse the contents of a second-level or query cache region, use the <literal>Statistics</literal>
+ API:
+ </para>
+
+ <programlisting><![CDATA[Map cacheEntries = sessionFactory.getStatistics()
+ .getSecondLevelCacheStatistics(regionName)
+ .getEntries();]]></programlisting>
+
+ <para>
+ You'll need to enable statistics, and, optionally, force Hibernate to keep the cache entries in a
+ more human-understandable format:
+ </para>
+
+ <programlisting><![CDATA[hibernate.generate_statistics true
+hibernate.cache.use_structured_entries true]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="performance-querycache" revision="1">
+ <title>The Query Cache</title>
+
+ <para>
+ Query result sets may also be cached. This is only useful for queries that are run
+ frequently with the same parameters. To use the query cache you must first enable it:
+ </para>
+
+ <programlisting><![CDATA[hibernate.cache.use_query_cache true]]></programlisting>
+
+ <para>
+ This setting causes the creation of two new cache regions - one holding cached query
+ result sets (<literal>org.hibernate.cache.StandardQueryCache</literal>), the other
+ holding timestamps of the most recent updates to queryable tables
+ (<literal>org.hibernate.cache.UpdateTimestampsCache</literal>). Note that the query
+ cache does not cache the state of the actual entities in the result set; it caches
+ only identifier values and results of value type. So the query cache should always be
+ used in conjunction with the second-level cache.
+ </para>
+
+ <para>
+ Most queries do not benefit from caching, so by default queries are not cached. To
+ enable caching, call <literal>Query.setCacheable(true)</literal>. This call allows
+ the query to look for existing cache results or add its results to the cache when
+ it is executed.
+ </para>
+
+ <para>
+ If you require fine-grained control over query cache expiration policies, you may
+ specify a named cache region for a particular query by calling
+ <literal>Query.setCacheRegion()</literal>.
+ </para>
+
+ <programlisting><![CDATA[List blogs = sess.createQuery("from Blog blog where blog.blogger = :blogger")
+ .setEntity("blogger", blogger)
+ .setMaxResults(15)
+ .setCacheable(true)
+ .setCacheRegion("frontpages")
+ .list();]]></programlisting>
+
+ <para>
+ If the query should force a refresh of its query cache region, you should call
+ <literal>Query.setCacheMode(CacheMode.REFRESH)</literal>. This is particularly useful
+ in cases where underlying data may have been updated via a separate process (i.e.,
+ not modified through Hibernate) and allows the application to selectively refresh
+ particular query result sets. This is a more efficient alternative to eviction of
+ a query cache region via <literal>SessionFactory.evictQueries()</literal>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="performance-collections">
+ <title>Understanding Collection performance</title>
+
+ <para>
+ We've already spent quite some time talking about collections.
+ In this section we will highlight a couple more issues about
+ how collections behave at runtime.
+ </para>
+
+ <sect2 id="performance-collections-taxonomy">
+ <title>Taxonomy</title>
+
+ <para>Hibernate defines three basic kinds of collections:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>collections of values</para>
+ </listitem>
+ <listitem>
+ <para>one to many associations</para>
+ </listitem>
+ <listitem>
+ <para>many to many associations</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ This classification distinguishes the various table and foreign key
+ relationships but does not tell us quite everything we need to know
+ about the relational model. To fully understand the relational structure
+ and performance characteristics, we must also consider the structure of
+ the primary key that is used by Hibernate to update or delete collection
+ rows. This suggests the following classification:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>indexed collections</para>
+ </listitem>
+ <listitem>
+ <para>sets</para>
+ </listitem>
+ <listitem>
+ <para>bags</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ All indexed collections (maps, lists, arrays) have a primary key consisting
+ of the <literal><key></literal> and <literal><index></literal>
+ columns. In this case collection updates are usually extremely efficient -
+ the primary key may be efficiently indexed and a particular row may be efficiently
+ located when Hibernate tries to update or delete it.
+ </para>
+
+ <para>
+ Sets have a primary key consisting of <literal><key></literal> and element
+ columns. This may be less efficient for some types of collection element, particularly
+ composite elements or large text or binary fields; the database may not be able to index
+ a complex primary key as efficently. On the other hand, for one to many or many to many
+ associations, particularly in the case of synthetic identifiers, it is likely to be just
+ as efficient. (Side-note: if you want <literal>SchemaExport</literal> to actually create
+ the primary key of a <literal><set></literal> for you, you must declare all columns
+ as <literal>not-null="true"</literal>.)
+ </para>
+
+ <para>
+ <literal><idbag></literal> mappings define a surrogate key, so they are
+ always very efficient to update. In fact, they are the best case.
+ </para>
+
+ <para>
+ Bags are the worst case. Since a bag permits duplicate element values and has no
+ index column, no primary key may be defined. Hibernate has no way of distinguishing
+ between duplicate rows. Hibernate resolves this problem by completely removing
+ (in a single <literal>DELETE</literal>) and recreating the collection whenever it
+ changes. This might be very inefficient.
+ </para>
+
+ <para>
+ Note that for a one-to-many association, the "primary key" may not be the physical
+ primary key of the database table - but even in this case, the above classification
+ is still useful. (It still reflects how Hibernate "locates" individual rows of the
+ collection.)
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-collections-mostefficientupdate">
+ <title>Lists, maps, idbags and sets are the most efficient collections to update</title>
+
+ <para>
+ From the discussion above, it should be clear that indexed collections
+ and (usually) sets allow the most efficient operation in terms of adding,
+ removing and updating elements.
+ </para>
+
+ <para>
+ There is, arguably, one more advantage that indexed collections have over sets for
+ many to many associations or collections of values. Because of the structure of a
+ <literal>Set</literal>, Hibernate doesn't ever <literal>UPDATE</literal> a row when
+ an element is "changed". Changes to a <literal>Set</literal> always work via
+ <literal>INSERT</literal> and <literal>DELETE</literal> (of individual rows). Once
+ again, this consideration does not apply to one to many associations.
+ </para>
+
+ <para>
+ After observing that arrays cannot be lazy, we would conclude that lists, maps and
+ idbags are the most performant (non-inverse) collection types, with sets not far
+ behind. Sets are expected to be the most common kind of collection in Hibernate
+ applications. This is because the "set" semantics are most natural in the relational
+ model.
+ </para>
+
+ <para>
+ However, in well-designed Hibernate domain models, we usually see that most collections
+ are in fact one-to-many associations with <literal>inverse="true"</literal>. For these
+ associations, the update is handled by the many-to-one end of the association, and so
+ considerations of collection update performance simply do not apply.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-collections-mostefficentinverse">
+ <title>Bags and lists are the most efficient inverse collections</title>
+
+ <para>
+ Just before you ditch bags forever, there is a particular case in which bags (and also lists)
+ are much more performant than sets. For a collection with <literal>inverse="true"</literal>
+ (the standard bidirectional one-to-many relationship idiom, for example) we can add elements
+ to a bag or list without needing to initialize (fetch) the bag elements! This is because
+ <literal>Collection.add()</literal> or <literal>Collection.addAll()</literal> must always
+ return true for a bag or <literal>List</literal> (unlike a <literal>Set</literal>). This can
+ make the following common code much faster.
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) sess.load(Parent.class, id);
+Child c = new Child();
+c.setParent(p);
+p.getChildren().add(c); //no need to fetch the collection!
+sess.flush();]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="performance-collections-oneshotdelete">
+ <title>One shot delete</title>
+
+ <para>
+ Occasionally, deleting collection elements one by one can be extremely inefficient. Hibernate
+ isn't completely stupid, so it knows not to do that in the case of an newly-empty collection
+ (if you called <literal>list.clear()</literal>, for example). In this case, Hibernate will
+ issue a single <literal>DELETE</literal> and we are done!
+ </para>
+
+ <para>
+ Suppose we add a single element to a collection of size twenty and then remove two elements.
+ Hibernate will issue one <literal>INSERT</literal> statement and two <literal>DELETE</literal>
+ statements (unless the collection is a bag). This is certainly desirable.
+ </para>
+
+ <para>
+ However, suppose that we remove eighteen elements, leaving two and then add thee new elements.
+ There are two possible ways to proceed
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>delete eighteen rows one by one and then insert three rows</para>
+ </listitem>
+ <listitem>
+ <para>remove the whole collection (in one SQL <literal>DELETE</literal>) and insert
+ all five current elements (one by one)</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Hibernate isn't smart enough to know that the second option is probably quicker in this case.
+ (And it would probably be undesirable for Hibernate to be that smart; such behaviour might
+ confuse database triggers, etc.)
+ </para>
+
+ <para>
+ Fortunately, you can force this behaviour (ie. the second strategy) at any time by discarding
+ (ie. dereferencing) the original collection and returning a newly instantiated collection with
+ all the current elements. This can be very useful and powerful from time to time.
+ </para>
+
+ <para>
+ Of course, one-shot-delete does not apply to collections mapped <literal>inverse="true"</literal>.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="performance-monitoring" revision="1">
+ <title>Monitoring performance</title>
+
+ <para>
+ Optimization is not much use without monitoring and access to performance numbers.
+ Hibernate provides a full range of figures about its internal operations.
+ Statistics in Hibernate are available per <literal>SessionFactory</literal>.
+ </para>
+
+ <sect2 id="performance-monitoring-sf" revision="2">
+ <title>Monitoring a SessionFactory</title>
+
+ <para>
+ You can access <literal>SessionFactory</literal> metrics in two ways.
+ Your first option is to call <literal>sessionFactory.getStatistics()</literal> and
+ read or display the <literal>Statistics</literal> yourself.
+ </para>
+
+ <para>
+ Hibernate can also use JMX to publish metrics if you enable the
+ <literal>StatisticsService</literal> MBean. You may enable a single MBean for all your
+ <literal>SessionFactory</literal> or one per factory. See the following code for
+ minimalistic configuration examples:
+ </para>
+
+ <programlisting><![CDATA[// MBean service registration for a specific SessionFactory
+Hashtable tb = new Hashtable();
+tb.put("type", "statistics");
+tb.put("sessionFactory", "myFinancialApp");
+ObjectName on = new ObjectName("hibernate", tb); // MBean object name
+
+StatisticsService stats = new StatisticsService(); // MBean implementation
+stats.setSessionFactory(sessionFactory); // Bind the stats to a SessionFactory
+server.registerMBean(stats, on); // Register the Mbean on the server]]></programlisting>
+
+
+<programlisting><![CDATA[// MBean service registration for all SessionFactory's
+Hashtable tb = new Hashtable();
+tb.put("type", "statistics");
+tb.put("sessionFactory", "all");
+ObjectName on = new ObjectName("hibernate", tb); // MBean object name
+
+StatisticsService stats = new StatisticsService(); // MBean implementation
+server.registerMBean(stats, on); // Register the MBean on the server]]></programlisting>
+
+ <para>
+ TODO: This doesn't make sense: In the first case, we retrieve and use the MBean directly. In the second one, we must give
+ the JNDI name in which the session factory is held before using it. Use
+ <literal>hibernateStatsBean.setSessionFactoryJNDIName("my/JNDI/Name")</literal>
+ </para>
+ <para>
+ You can (de)activate the monitoring for a <literal>SessionFactory</literal>
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ at configuration time, set <literal>hibernate.generate_statistics</literal> to <literal>false</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+ <itemizedlist>
+ <listitem>
+ <para>
+ at runtime: <literal>sf.getStatistics().setStatisticsEnabled(true)</literal>
+ or <literal>hibernateStatsBean.setStatisticsEnabled(true)</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Statistics can be reset programatically using the <literal>clear()</literal> method.
+ A summary can be sent to a logger (info level) using the <literal>logSummary()</literal>
+ method.
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-monitoring-metrics" revision="1">
+ <title>Metrics</title>
+
+ <para>
+ Hibernate provides a number of metrics, from very basic to the specialized information
+ only relevant in certain scenarios. All available counters are described in the
+ <literal>Statistics</literal> interface API, in three categories:
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ Metrics related to the general <literal>Session</literal> usage, such as
+ number of open sessions, retrieved JDBC connections, etc.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Metrics related to he entities, collections, queries, and caches as a
+ whole (aka global metrics),
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Detailed metrics related to a particular entity, collection, query or
+ cache region.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ For exampl,e you can check the cache hit, miss, and put ratio of entities, collections
+ and queries, and the average time a query needs. Beware that the number of milliseconds
+ is subject to approximation in Java. Hibernate is tied to the JVM precision, on some
+ platforms this might even only be accurate to 10 seconds.
+ </para>
+
+ <para>
+ Simple getters are used to access the global metrics (i.e. not tied to a particular entity,
+ collection, cache region, etc.). You can access the metrics of a particular entity, collection
+ or cache region through its name, and through its HQL or SQL representation for queries. Please
+ refer to the <literal>Statistics</literal>, <literal>EntityStatistics</literal>,
+ <literal>CollectionStatistics</literal>, <literal>SecondLevelCacheStatistics</literal>,
+ and <literal>QueryStatistics</literal> API Javadoc for more information. The following
+ code shows a simple example:
+ </para>
+
+ <programlisting><![CDATA[Statistics stats = HibernateUtil.sessionFactory.getStatistics();
+
+double queryCacheHitCount = stats.getQueryCacheHitCount();
+double queryCacheMissCount = stats.getQueryCacheMissCount();
+double queryCacheHitRatio =
+ queryCacheHitCount / (queryCacheHitCount + queryCacheMissCount);
+
+log.info("Query Hit ratio:" + queryCacheHitRatio);
+
+EntityStatistics entityStats =
+ stats.getEntityStatistics( Cat.class.getName() );
+long changes =
+ entityStats.getInsertCount()
+ + entityStats.getUpdateCount()
+ + entityStats.getDeleteCount();
+log.info(Cat.class.getName() + " changed " + changes + "times" );]]></programlisting>
+
+ <para>
+ To work on all entities, collections, queries and region caches, you can retrieve
+ the list of names of entities, collections, queries and region caches with the
+ following methods: <literal>getQueries()</literal>, <literal>getEntityNames()</literal>,
+ <literal>getCollectionRoleNames()</literal>, and
+ <literal>getSecondLevelCacheRegionNames()</literal>.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+</chapter>
\ No newline at end of file
Property changes on: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/performance.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/persistent_classes.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/persistent_classes.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/persistent_classes.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,531 @@
+<chapter id="persistent-classes" revision="2">
+ <title>Persistent Classes</title>
+
+ <para>
+ Persistent classes are classes in an application that implement the entities
+ of the business problem (e.g. Customer and Order in an E-commerce application).
+ Not all instances of a persistent class are considered to be in the persistent
+ state - an instance may instead be transient or detached.
+ </para>
+
+ <para>
+ Hibernate works best if these classes follow some simple rules, also known
+ as the Plain Old Java Object (POJO) programming model. However, none of these
+ rules are hard requirements. Indeed, Hibernate3 assumes very little about
+ the nature of your persistent objects. You may express a domain model in other
+ ways: using trees of <literal>Map</literal> instances, for example.
+ </para>
+
+ <sect1 id="persistent-classes-pojo">
+ <title>A simple POJO example</title>
+
+ <para>
+ Most Java applications require a persistent class representing felines.
+ </para>
+
+ <programlisting><![CDATA[package eg;
+import java.util.Set;
+import java.util.Date;
+
+public class Cat {
+ private Long id; // identifier
+
+ private Date birthdate;
+ private Color color;
+ private char sex;
+ private float weight;
+ private int litterId;
+
+ private Cat mother;
+ private Set kittens = new HashSet();
+
+ private void setId(Long id) {
+ this.id=id;
+ }
+ public Long getId() {
+ return id;
+ }
+
+ void setBirthdate(Date date) {
+ birthdate = date;
+ }
+ public Date getBirthdate() {
+ return birthdate;
+ }
+
+ void setWeight(float weight) {
+ this.weight = weight;
+ }
+ public float getWeight() {
+ return weight;
+ }
+
+ public Color getColor() {
+ return color;
+ }
+ void setColor(Color color) {
+ this.color = color;
+ }
+
+ void setSex(char sex) {
+ this.sex=sex;
+ }
+ public char getSex() {
+ return sex;
+ }
+
+ void setLitterId(int id) {
+ this.litterId = id;
+ }
+ public int getLitterId() {
+ return litterId;
+ }
+
+ void setMother(Cat mother) {
+ this.mother = mother;
+ }
+ public Cat getMother() {
+ return mother;
+ }
+ void setKittens(Set kittens) {
+ this.kittens = kittens;
+ }
+ public Set getKittens() {
+ return kittens;
+ }
+
+ // addKitten not needed by Hibernate
+ public void addKitten(Cat kitten) {
+ kitten.setMother(this);
+ kitten.setLitterId( kittens.size() );
+ kittens.add(kitten);
+ }
+}]]></programlisting>
+
+ <para>
+ There are four main rules to follow here:
+ </para>
+
+
+ <sect2 id="persistent-classes-pojo-constructor" revision="1">
+ <title>Implement a no-argument constructor</title>
+
+ <para>
+ <literal>Cat</literal> has a no-argument constructor. All persistent classes must
+ have a default constructor (which may be non-public) so that Hibernate can instantiate
+ them using <literal>Constructor.newInstance()</literal>. We strongly recommend having a
+ default constructor with at least <emphasis>package</emphasis> visibility for runtime proxy
+ generation in Hibernate.
+ </para>
+ </sect2>
+
+ <sect2 id="persistent-classes-pojo-identifier" revision="2">
+ <title>Provide an identifier property (optional)</title>
+
+ <para>
+ <literal>Cat</literal> has a property called <literal>id</literal>. This property
+ maps to the primary key column of a database table. The property might have been called
+ anything, and its type might have been any primitive type, any primitive "wrapper"
+ type, <literal>java.lang.String</literal> or <literal>java.util.Date</literal>. (If
+ your legacy database table has composite keys, you can even use a user-defined class
+ with properties of these types - see the section on composite identifiers later.)
+ </para>
+
+ <para>
+ The identifier property is strictly optional. You can leave them off and let Hibernate
+ keep track of object identifiers internally. We do not recommend this, however.
+ </para>
+
+ <para>
+ In fact, some functionality is available only to classes which declare an
+ identifier property:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ Transitive reattachment for detached objects (cascade update or cascade
+ merge) - see <xref linkend="objectstate-transitive"/>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>Session.saveOrUpdate()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>Session.merge()</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ We recommend you declare consistently-named identifier properties on persistent
+ classes. We further recommend that you use a nullable (ie. non-primitive) type.
+ </para>
+ </sect2>
+
+ <sect2 id="persistent-classes-pojo-final">
+ <title>Prefer non-final classes (optional)</title>
+ <para>
+ A central feature of Hibernate, <emphasis>proxies</emphasis>, depends upon the
+ persistent class being either non-final, or the implementation of an interface
+ that declares all public methods.
+ </para>
+ <para>
+ You can persist <literal>final</literal> classes that do not implement an interface
+ with Hibernate, but you won't be able to use proxies for lazy association fetching -
+ which will limit your options for performance tuning.
+ </para>
+ <para>
+ You should also avoid declaring <literal>public final</literal> methods on the
+ non-final classes. If you want to use a class with a <literal>public final</literal>
+ method, you must explicitly disable proying by setting <literal>lazy="false"</literal>.
+ </para>
+ </sect2>
+
+ <sect2 id="persistent-classes-pojo-accessors" revision="2">
+ <title>Declare accessors and mutators for persistent fields (optional)</title>
+
+ <para>
+ <literal>Cat</literal> declares accessor methods for all its persistent fields.
+ Many other ORM tools directly persist instance variables. We believe it is
+ better to provide an indirection between the relational schema and internal
+ data structures of the class. By default, Hibernate persists JavaBeans style
+ properties, and recognizes method names of the form <literal>getFoo</literal>,
+ <literal>isFoo</literal> and <literal>setFoo</literal>. You may switch to direct
+ field access for particular properties, if needed.
+ </para>
+
+ <para>
+ Properties need <emphasis>not</emphasis> be declared public - Hibernate can
+ persist a property with a default, <literal>protected</literal> or
+ <literal>private</literal> get / set pair.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="persistent-classes-inheritance">
+ <title>Implementing inheritance</title>
+
+ <para>
+ A subclass must also observe the first and second rules. It inherits its
+ identifier property from the superclass, <literal>Cat</literal>.
+ </para>
+
+ <programlisting><![CDATA[package eg;
+
+public class DomesticCat extends Cat {
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+ protected void setName(String name) {
+ this.name=name;
+ }
+}]]></programlisting>
+ </sect1>
+
+ <sect1 id="persistent-classes-equalshashcode" revision="1">
+ <title>Implementing <literal>equals()</literal> and <literal>hashCode()</literal></title>
+
+ <para>
+ You have to override the <literal>equals()</literal> and <literal>hashCode()</literal>
+ methods if you
+ </para>
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ intend to put instances of persistent classes in a <literal>Set</literal>
+ (the recommended way to represent many-valued associations)
+ <emphasis>and</emphasis>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ intend to use reattachment of detached instances
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Hibernate guarantees equivalence of persistent identity (database row) and Java identity
+ only inside a particular session scope. So as soon as we mix instances retrieved in
+ different sessions, we must implement <literal>equals()</literal> and
+ <literal>hashCode()</literal> if we wish to have meaningful semantics for
+ <literal>Set</literal>s.
+ </para>
+
+ <para>
+ The most obvious way is to implement <literal>equals()</literal>/<literal>hashCode()</literal>
+ by comparing the identifier value of both objects. If the value is the same, both must
+ be the same database row, they are therefore equal (if both are added to a <literal>Set</literal>,
+ we will only have one element in the <literal>Set</literal>). Unfortunately, we can't use that
+ approach with generated identifiers! Hibernate will only assign identifier values to objects
+ that are persistent, a newly created instance will not have any identifier value! Furthermore,
+ if an instance is unsaved and currently in a <literal>Set</literal>, saving it will assign
+ an identifier value to the object. If <literal>equals()</literal> and <literal>hashCode()</literal>
+ are based on the identifier value, the hash code would change, breaking the contract of the
+ <literal>Set</literal>. See the Hibernate website for a full discussion of this problem. Note
+ that this is not a Hibernate issue, but normal Java semantics of object identity and equality.
+ </para>
+
+ <para>
+ We recommend implementing <literal>equals()</literal> and <literal>hashCode()</literal>
+ using <emphasis>Business key equality</emphasis>. Business key equality means that the
+ <literal>equals()</literal> method compares only the properties that form the business
+ key, a key that would identify our instance in the real world (a
+ <emphasis>natural</emphasis> candidate key):
+ </para>
+
+ <programlisting><![CDATA[public class Cat {
+
+ ...
+ public boolean equals(Object other) {
+ if (this == other) return true;
+ if ( !(other instanceof Cat) ) return false;
+
+ final Cat cat = (Cat) other;
+
+ if ( !cat.getLitterId().equals( getLitterId() ) ) return false;
+ if ( !cat.getMother().equals( getMother() ) ) return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ int result;
+ result = getMother().hashCode();
+ result = 29 * result + getLitterId();
+ return result;
+ }
+
+}]]></programlisting>
+
+ <para>
+ Note that a business key does not have to be as solid as a database
+ primary key candidate (see <xref linkend="transactions-basics-identity"/>).
+ Immutable or unique properties are usually good
+ candidates for a business key.
+ </para>
+
+ </sect1>
+
+ <sect1 id="persistent-classes-dynamicmodels">
+ <title>Dynamic models</title>
+
+ <para>
+ <emphasis>Note that the following features are currently considered
+ experimental and may change in the near future.</emphasis>
+ </para>
+
+ <para>
+ Persistent entities don't necessarily have to be represented as POJO classes
+ or as JavaBean objects at runtime. Hibernate also supports dynamic models
+ (using <literal>Map</literal>s of <literal>Map</literal>s at runtime) and the
+ representation of entities as DOM4J trees. With this approach, you don't
+ write persistent classes, only mapping files.
+ </para>
+
+ <para>
+ By default, Hibernate works in normal POJO mode. You may set a default entity
+ representation mode for a particular <literal>SessionFactory</literal> using the
+ <literal>default_entity_mode</literal> configuration option (see
+ <xref linkend="configuration-optional-properties"/>.
+ </para>
+
+ <para>
+ The following examples demonstrates the representation using <literal>Map</literal>s.
+ First, in the mapping file, an <literal>entity-name</literal> has to be declared
+ instead of (or in addition to) a class name:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class entity-name="Customer">
+
+ <id name="id"
+ type="long"
+ column="ID">
+ <generator class="sequence"/>
+ </id>
+
+ <property name="name"
+ column="NAME"
+ type="string"/>
+
+ <property name="address"
+ column="ADDRESS"
+ type="string"/>
+
+ <many-to-one name="organization"
+ column="ORGANIZATION_ID"
+ class="Organization"/>
+
+ <bag name="orders"
+ inverse="true"
+ lazy="false"
+ cascade="all">
+ <key column="CUSTOMER_ID"/>
+ <one-to-many class="Order"/>
+ </bag>
+
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+
+ Note that even though associations are declared using target class names,
+ the target type of an associations may also be a dynamic entity instead
+ of a POJO.
+ </para>
+
+ <para>
+ After setting the default entity mode to <literal>dynamic-map</literal>
+ for the <literal>SessionFactory</literal>, we can at runtime work with
+ <literal>Map</literal>s of <literal>Map</literal>s:
+ </para>
+
+ <programlisting><![CDATA[Session s = openSession();
+Transaction tx = s.beginTransaction();
+Session s = openSession();
+
+// Create a customer
+Map david = new HashMap();
+david.put("name", "David");
+
+// Create an organization
+Map foobar = new HashMap();
+foobar.put("name", "Foobar Inc.");
+
+// Link both
+david.put("organization", foobar);
+
+// Save both
+s.save("Customer", david);
+s.save("Organization", foobar);
+
+tx.commit();
+s.close();]]></programlisting>
+
+ <para>
+ The advantages of a dynamic mapping are quick turnaround time for prototyping
+ without the need for entity class implementation. However, you lose compile-time
+ type checking and will very likely deal with many exceptions at runtime. Thanks
+ to the Hibernate mapping, the database schema can easily be normalized and sound,
+ allowing to add a proper domain model implementation on top later on.
+ </para>
+
+ <para>
+ Entity representation modes can also be set on a per <literal>Session</literal>
+ basis:
+ </para>
+
+ <programlisting><![CDATA[Session dynamicSession = pojoSession.getSession(EntityMode.MAP);
+
+// Create a customer
+Map david = new HashMap();
+david.put("name", "David");
+dynamicSession.save("Customer", david);
+...
+dynamicSession.flush();
+dynamicSession.close()
+...
+// Continue on pojoSession
+]]></programlisting>
+
+
+ <para>
+ Please note that the call to <literal>getSession()</literal> using an
+ <literal>EntityMode</literal> is on the <literal>Session</literal> API, not the
+ <literal>SessionFactory</literal>. That way, the new <literal>Session</literal>
+ shares the underlying JDBC connection, transaction, and other context
+ information. This means you don't have tocall <literal>flush()</literal>
+ and <literal>close()</literal> on the secondary <literal>Session</literal>, and
+ also leave the transaction and connection handling to the primary unit of work.
+ </para>
+
+ <para>
+ More information about the XML representation capabilities can be found
+ in <xref linkend="xml"/>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="persistent-classes-tuplizers" revision="0">
+ <title>Tuplizers</title>
+
+ <para>
+ <literal>org.hibernate.tuple.Tuplizer</literal>, and its sub-interfaces, are responsible
+ for managing a particular representation of a piece of data, given that representation's
+ <literal>org.hibernate.EntityMode</literal>. If a given piece of data is thought of as
+ a data structure, then a tuplizer is the thing which knows how to create such a data structure
+ and how to extract values from and inject values into such a data structure. For example,
+ for the POJO entity mode, the correpsonding tuplizer knows how create the POJO through its
+ constructor and how to access the POJO properties using the defined property accessors.
+ There are two high-level types of Tuplizers, represented by the
+ <literal>org.hibernate.tuple.EntityTuplizer</literal> and <literal>org.hibernate.tuple.ComponentTuplizer</literal>
+ interfaces. <literal>EntityTuplizer</literal>s are responsible for managing the above mentioned
+ contracts in regards to entities, while <literal>ComponentTuplizer</literal>s do the same for
+ components.
+ </para>
+
+ <para>
+ Users may also plug in their own tuplizers. Perhaps you require that a <literal>java.util.Map</literal>
+ implementation other than <literal>java.util.HashMap</literal> be used while in the
+ dynamic-map entity-mode; or perhaps you need to define a different proxy generation strategy
+ than the one used by default. Both would be achieved by defining a custom tuplizer
+ implementation. Tuplizers definitions are attached to the entity or component mapping they
+ are meant to manage. Going back to the example of our customer entity:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+ <class entity-name="Customer">
+ <!--
+ Override the dynamic-map entity-mode
+ tuplizer for the customer entity
+ -->
+ <tuplizer entity-mode="dynamic-map"
+ class="CustomMapTuplizerImpl"/>
+
+ <id name="id" type="long" column="ID">
+ <generator class="sequence"/>
+ </id>
+
+ <!-- other properties -->
+ ...
+ </class>
+</hibernate-mapping>
+
+
+public class CustomMapTuplizerImpl
+ extends org.hibernate.tuple.DynamicMapEntityTuplizer {
+ // override the buildInstantiator() method to plug in our custom map...
+ protected final Instantiator buildInstantiator(
+ org.hibernate.mapping.PersistentClass mappingInfo) {
+ return new CustomMapInstantiator( mappingInfo );
+ }
+
+ private static final class CustomMapInstantiator
+ extends org.hibernate.tuple.DynamicMapInstantitor {
+ // override the generateMap() method to return our custom map...
+ protected final Map generateMap() {
+ return new CustomMap();
+ }
+ }
+}]]></programlisting>
+
+
+ </sect1>
+
+ <para>
+ TODO: Document user-extension framework in the property and proxy packages
+ </para>
+
+</chapter>
+
Property changes on: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/persistent_classes.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/query_criteria.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/query_criteria.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/query_criteria.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,436 @@
+<chapter id="querycriteria">
+ <title>Consultas por critérios</title>
+
+ <para>
+ O Hibernate provê uma intuitiva e extensÃvel API de critério de query.
+ </para>
+
+ <sect1 id="querycriteria-creating">
+ <title>Criando uma instancia <literal>Criteria</literal></title>
+
+ <para>
+ A interface <literal>org.hibernate.Criteria</literal> representa a query
+ ao invés de uma classe persistente particular. A sessão é uma fábrica para
+ intancias de <literal>Criteria</literal> .
+ </para>
+
+ <programlisting><![CDATA[Criteria crit = sess.createCriteria(Cat.class);
+crit.setMaxResults(50);
+List cats = crit.list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-narrowing">
+ <title>Limitando o result set</title>
+
+ <para>
+ Um critério individual de query é uma instancia da interface
+ <literal>org.hibernate.criterion.Criterion</literal>. A classe
+ <literal>org.hibernate.criterion.Restrictions</literal> define
+ os métodos da fábrica para obter certos tipos pré fabricados
+ de <literal>Criterion</literal>.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "Fritz%") )
+ .add( Restrictions.between("weight", minWeight, maxWeight) )
+ .list();]]></programlisting>
+
+ <para>
+ Restrições podem ser logicamente agrupadas.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "Fritz%") )
+ .add( Restrictions.or(
+ Restrictions.eq( "age", new Integer(0) ),
+ Restrictions.isNull("age")
+ ) )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.in( "name", new String[] { "Fritz", "Izi", "Pk" } ) )
+ .add( Restrictions.disjunction()
+ .add( Restrictions.isNull("age") )
+ .add( Restrictions.eq("age", new Integer(0) ) )
+ .add( Restrictions.eq("age", new Integer(1) ) )
+ .add( Restrictions.eq("age", new Integer(2) ) )
+ ) )
+ .list();]]></programlisting>
+
+ <para>
+ Existe um grande número de critérios pré fabricados (subclasses de
+ <literal>Restrictions</literal>), mas um é especialmente útil pois
+ permite especificar o SQL diretamente.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.sqlRestriction("lower({alias}.name) like lower(?)", "Fritz%", Hibernate.STRING) )
+ .list();]]></programlisting>
+
+ <para>
+ O parametro <literal>{alias}</literal> será substituido pelo alias da entidade procurada.
+ </para>
+
+ <para>
+ Uma maneira alternativa de obter um critério é pegá-lo de uma instancia de
+ <literal>Property</literal>. Você pode criar uma <literal>Property</literal>
+ chamando <literal>Property.forName()</literal>.
+ </para>
+
+ <programlisting><![CDATA[
+Property age = Property.forName("age");
+List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.disjunction()
+ .add( age.isNull() )
+ .add( age.eq( new Integer(0) ) )
+ .add( age.eq( new Integer(1) ) )
+ .add( age.eq( new Integer(2) ) )
+ ) )
+ .add( Property.forName("name").in( new String[] { "Fritz", "Izi", "Pk" } ) )
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-ordering">
+ <title>Ordering the results</title>
+
+ <para>
+ You may order the results using <literal>org.hibernate.criterion.Order</literal>.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "F%")
+ .addOrder( Order.asc("name") )
+ .addOrder( Order.desc("age") )
+ .setMaxResults(50)
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Property.forName("name").like("F%") )
+ .addOrder( Property.forName("name").asc() )
+ .addOrder( Property.forName("age").desc() )
+ .setMaxResults(50)
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-associations" revision="2">
+ <title>Associations</title>
+
+ <para>
+ You may easily specify constraints upon related entities by navigating
+ associations using <literal>createCriteria()</literal>.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "F%") )
+ .createCriteria("kittens")
+ .add( Restrictions.like("name", "F%") )
+ .list();]]></programlisting>
+
+ <para>
+ note that the second <literal>createCriteria()</literal> returns a new
+ instance of <literal>Criteria</literal>, which refers to the elements of
+ the <literal>kittens</literal> collection.
+ </para>
+
+ <para>
+ The following, alternate form is useful in certain circumstances.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .createAlias("kittens", "kt")
+ .createAlias("mate", "mt")
+ .add( Restrictions.eqProperty("kt.name", "mt.name") )
+ .list();]]></programlisting>
+
+ <para>
+ (<literal>createAlias()</literal> does not create a new instance of
+ <literal>Criteria</literal>.)
+ </para>
+
+ <para>
+ Note that the kittens collections held by the <literal>Cat</literal> instances
+ returned by the previous two queries are <emphasis>not</emphasis> pre-filtered
+ by the criteria! If you wish to retrieve just the kittens that match the
+ criteria, you must use a <literal>ResultTransformer</literal>.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .createCriteria("kittens", "kt")
+ .add( Restrictions.eq("name", "F%") )
+ .setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP)
+ .list();
+Iterator iter = cats.iterator();
+while ( iter.hasNext() ) {
+ Map map = (Map) iter.next();
+ Cat cat = (Cat) map.get(Criteria.ROOT_ALIAS);
+ Cat kitten = (Cat) map.get("kt");
+}]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-dynamicfetching" revision="1">
+ <title>Dynamic association fetching</title>
+
+ <para>
+ You may specify association fetching semantics at runtime using
+ <literal>setFetchMode()</literal>.
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "Fritz%") )
+ .setFetchMode("mate", FetchMode.EAGER)
+ .setFetchMode("kittens", FetchMode.EAGER)
+ .list();]]></programlisting>
+
+ <para>
+ This query will fetch both <literal>mate</literal> and <literal>kittens</literal>
+ by outer join. See <xref linkend="performance-fetching"/> for more information.
+ </para>
+
+ </sect1>
+
+ <sect1 id="querycriteria-examples">
+ <title>Example queries</title>
+
+ <para>
+ The class <literal>org.hibernate.criterion.Example</literal> allows
+ you to construct a query criterion from a given instance.
+ </para>
+
+ <programlisting><![CDATA[Cat cat = new Cat();
+cat.setSex('F');
+cat.setColor(Color.BLACK);
+List results = session.createCriteria(Cat.class)
+ .add( Example.create(cat) )
+ .list();]]></programlisting>
+
+ <para>
+ Version properties, identifiers and associations are ignored. By default,
+ null valued properties are excluded.
+ </para>
+
+ <para>
+ You can adjust how the <literal>Example</literal> is applied.
+ </para>
+
+ <programlisting><![CDATA[Example example = Example.create(cat)
+ .excludeZeroes() //exclude zero valued properties
+ .excludeProperty("color") //exclude the property named "color"
+ .ignoreCase() //perform case insensitive string comparisons
+ .enableLike(); //use like for string comparisons
+List results = session.createCriteria(Cat.class)
+ .add(example)
+ .list();]]></programlisting>
+
+ <para>
+ You can even use examples to place criteria upon associated objects.
+ </para>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .add( Example.create(cat) )
+ .createCriteria("mate")
+ .add( Example.create( cat.getMate() ) )
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-projection">
+ <title>Projections, aggregation and grouping</title>
+ <para>
+ The class <literal>org.hibernate.criterion.Projections</literal> is a
+ factory for <literal>Projection</literal> instances. We apply a
+ projection to a query by calling <literal>setProjection()</literal>.
+ </para>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.rowCount() )
+ .add( Restrictions.eq("color", Color.BLACK) )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.projectionList()
+ .add( Projections.rowCount() )
+ .add( Projections.avg("weight") )
+ .add( Projections.max("weight") )
+ .add( Projections.groupProperty("color") )
+ )
+ .list();]]></programlisting>
+
+ <para>
+ There is no explicit "group by" necessary in a criteria query. Certain
+ projection types are defined to be <emphasis>grouping projections</emphasis>,
+ which also appear in the SQL <literal>group by</literal> clause.
+ </para>
+
+ <para>
+ An alias may optionally be assigned to a projection, so that the projected value
+ may be referred to in restrictions or orderings. Here are two different ways to
+ do this:
+ </para>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.alias( Projections.groupProperty("color"), "colr" ) )
+ .addOrder( Order.asc("colr") )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.groupProperty("color").as("colr") )
+ .addOrder( Order.asc("colr") )
+ .list();]]></programlisting>
+
+ <para>
+ The <literal>alias()</literal> and <literal>as()</literal> methods simply wrap a
+ projection instance in another, aliased, instance of <literal>Projection</literal>.
+ As a shortcut, you can assign an alias when you add the projection to a
+ projection list:
+ </para>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.projectionList()
+ .add( Projections.rowCount(), "catCountByColor" )
+ .add( Projections.avg("weight"), "avgWeight" )
+ .add( Projections.max("weight"), "maxWeight" )
+ .add( Projections.groupProperty("color"), "color" )
+ )
+ .addOrder( Order.desc("catCountByColor") )
+ .addOrder( Order.desc("avgWeight") )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Domestic.class, "cat")
+ .createAlias("kittens", "kit")
+ .setProjection( Projections.projectionList()
+ .add( Projections.property("cat.name"), "catName" )
+ .add( Projections.property("kit.name"), "kitName" )
+ )
+ .addOrder( Order.asc("catName") )
+ .addOrder( Order.asc("kitName") )
+ .list();]]></programlisting>
+
+ <para>
+ You can also use <literal>Property.forName()</literal> to express projections:
+ </para>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Property.forName("name") )
+ .add( Property.forName("color").eq(Color.BLACK) )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.projectionList()
+ .add( Projections.rowCount().as("catCountByColor") )
+ .add( Property.forName("weight").avg().as("avgWeight") )
+ .add( Property.forName("weight").max().as("maxWeight") )
+ .add( Property.forName("color").group().as("color" )
+ )
+ .addOrder( Order.desc("catCountByColor") )
+ .addOrder( Order.desc("avgWeight") )
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-detachedqueries">
+ <title>Detached queries and subqueries</title>
+ <para>
+ The <literal>DetachedCriteria</literal> class lets you create a query outside the scope
+ of a session, and then later execute it using some arbitrary <literal>Session</literal>.
+ </para>
+
+ <programlisting><![CDATA[DetachedCriteria query = DetachedCriteria.forClass(Cat.class)
+ .add( Property.forName("sex").eq('F') );
+
+Session session = ....;
+Transaction txn = session.beginTransaction();
+List results = query.getExecutableCriteria(session).setMaxResults(100).list();
+txn.commit();
+session.close();]]></programlisting>
+
+ <para>
+ A <literal>DetachedCriteria</literal> may also be used to express a subquery. Criterion
+ instances involving subqueries may be obtained via <literal>Subqueries</literal> or
+ <literal>Property</literal>.
+ </para>
+
+ <programlisting><![CDATA[DetachedCriteria avgWeight = DetachedCriteria.forClass(Cat.class)
+ .setProjection( Property.forName("weight").avg() );
+session.createCriteria(Cat.class)
+ .add( Property.forName("weight).gt(avgWeight) )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[DetachedCriteria weights = DetachedCriteria.forClass(Cat.class)
+ .setProjection( Property.forName("weight") );
+session.createCriteria(Cat.class)
+ .add( Subqueries.geAll("weight", weights) )
+ .list();]]></programlisting>
+
+ <para>
+ Even correlated subqueries are possible:
+ </para>
+
+ <programlisting><![CDATA[DetachedCriteria avgWeightForSex = DetachedCriteria.forClass(Cat.class, "cat2")
+ .setProjection( Property.forName("weight").avg() )
+ .add( Property.forName("cat2.sex").eqProperty("cat.sex") );
+session.createCriteria(Cat.class, "cat")
+ .add( Property.forName("weight).gt(avgWeightForSex) )
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <!--TODO: ResultSetTransformer + aliasing. AliasToBeanTransformer allow returning arbitrary
+ user objects - similar to setResultClass in JDO2. General use of ResultTransformer
+ could also be explained. -->
+
+ <sect1 id="query-criteria-naturalid">
+ <title>Queries by natural identifier</title>
+
+ <para>
+ For most queries, including criteria queries, the query cache is not very efficient,
+ because query cache invalidation occurs too frequently. However, there is one special
+ kind of query where we can optimize the cache invalidation algorithm: lookups by a
+ constant natural key. In some applications, this kind of query occurs frequently.
+ The criteria API provides special provision for this use case.
+ </para>
+
+ <para>
+ First, you should map the natural key of your entity using
+ <literal><natural-id></literal>, and enable use of the second-level cache.
+ </para>
+
+ <programlisting><![CDATA[<class name="User">
+ <cache usage="read-write"/>
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <natural-id>
+ <property name="name"/>
+ <property name="org"/>
+ </natural-id>
+ <property name="password"/>
+</class>]]></programlisting>
+
+ <para>
+ Note that this functionality is not intended for use with entities with
+ <emphasis>mutable</emphasis> natural keys.
+ </para>
+
+ <para>
+ Next, enable the Hibernate query cache.
+ </para>
+
+ <para>
+ Now, <literal>Restrictions.naturalId()</literal> allows us to make use of
+ the more efficient cache algorithm.
+ </para>
+
+ <programlisting><![CDATA[session.createCriteria(User.class)
+ .add( Restrictions.naturalId()
+ .set("name", "gavin")
+ .set("org", "hb")
+ ).setCacheable(true)
+ .uniqueResult();]]></programlisting>
+
+ </sect1>
+
+</chapter>
Property changes on: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/query_criteria.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/query_hql.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/query_hql.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/query_hql.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1162 @@
+858
+
+<chapter id="queryhql">
+ <title>HQL: A linguagem de Queries do Hibernate</title>
+
+ <para>
+ O Hibernate vem com uma poderosa linguagem que é (intencionalmente) muito parecida
+ com o SQL. Mas não seja enganado pela sintaxe; a HQL é totalmente orientada à objetos,
+ requer conhecimentos de herança, polimorfismo e associações.
+ </para>
+
+ <sect1 id="queryhql-casesensitivity">
+ <title>Case SensitÃve</title>
+
+ <para>
+ As Queries não são case-sensitive, exceto pelo nomes das classes e propriedades Java.
+ <literal>sELEct</literal> e o mesmo que
+ <literal>SELECT</literal> mas
+ <literal>org.hibernate.eg.FOO</literal> não é
+ <literal>org.hibernate.eg.Foo</literal> e
+ <literal>foo.barSet</literal> não é
+ <literal>foo.BARSET</literal>.
+
+ </para>
+
+ <para>
+ Esse manual usa as palavras chave HQL em letras minúsculas. Alguns usuários acham que
+ com letras maiúsculas as queries ficam mais legÃveis, mas nós achamos essa convenção feia
+ dentro do código Java.
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-from">
+ <title>A clausula from</title>
+
+ <para>
+ A mais simples query possÃvel do Hibernate é a assim:
+ </para>
+
+ <programlisting><![CDATA[from eg.Cat]]></programlisting>
+
+ <para>
+ Ela irá retornar todas as instancias da classe <literal>eg.Cat</literal>.
+ Necessariamente não precisamos qualificar o nome da classe, pois é realizado
+ <literal>auto-import</literal> por padrão. Por isso na maior parte do tempos
+ nós simplesmente escrevemos:
+ </para>
+
+ <programlisting><![CDATA[from Cat]]></programlisting>
+
+ <para>
+ Na maior parte do tempo, você precisará atribuir um <emphasis>alias</emphasis>,
+ desde que você queira se referia ao <literal>Cat</literal> em outras partes da
+ query.
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat]]></programlisting>
+
+ <para>
+ Essa query atribui um alias a <literal>cat</literal> para as instancias de
+ <literal>Cat</literal>, então nós podemos usar esse alias depois na query.
+ A palavra chave as é opcional; poderÃamos escrever assim:
+ </para>
+
+ <programlisting><![CDATA[from Cat cat]]></programlisting>
+
+ <para>
+ Múltiplas classes pode ser envolvidas, resultando em um produto cartesiano ou "cross" join.
+ </para>
+
+ <programlisting><![CDATA[from Formula, Parameter]]></programlisting>
+ <programlisting><![CDATA[from Formula as form, Parameter as param]]></programlisting>
+
+ <para>
+ à considerada uma boa prática os nomes dos aliases começarem com letra minúscula,
+ aderente com os padrões Java para variáveis locais (ex: <literal>domesticCat</literal>).
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-joins" revision="2">
+ <title>Associações e joins</title>
+
+ <para>
+ Nós também podemos querer atribuir aliases em uma entidade associada, ou mesmo
+ em elementos de uma coleção de valores, usando um <literal>join</literal>.
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+ inner join cat.mate as mate
+ left outer join cat.kittens as kitten]]></programlisting>
+
+ <programlisting><![CDATA[from Cat as cat left join cat.mate.kittens as kittens]]></programlisting>
+
+ <programlisting><![CDATA[from Formula form full join form.parameter param]]></programlisting>
+
+ <para>
+ Os tipos de joins suportados foram inspirados no SQL ANSI:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>inner join</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>left outer join</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>right outer join</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>full join</literal> (geralmente não é útil)
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ The <literal>inner join</literal>, <literal>left outer join</literal> and
+ <literal>right outer join</literal> constructs may be abbreviated.
+ As construções <literal>inner join</literal>, <literal>left outer join</literal> e
+ <literal>right outer join</literal> podem ser abreviadas.
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+ join cat.mate as mate
+ left join cat.kittens as kitten]]></programlisting>
+
+ <para>
+ Você pode fornecer condições extras de join usando a palavra
+ chave do HQL <literal>with</literal>.
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+ left join cat.kittens as kitten
+ <literal>with</literal> kitten.bodyWeight > 10.0]]></programlisting>
+
+ <para>
+ Adicionalmente, um "fetch" join permite que associações ou coleções de valores
+ sejam inicializadas junto com o objeto pai, usando apenas um select. Isso é
+ muito útil no caso das coleções. Isso efetivamente sobre escreve as declarações
+ outer join e lazy do arquivo mapeamento para associações e coleções.
+ Veja a seção <xref linkend="performance-fetching"/> para mais informações.
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+ inner join <literal>fetch</literal>cat.mate
+ left join <literal>fetch</literal>cat.kittens]]></programlisting>
+
+ <para>
+ Usualmente, um <literal>fetch</literal>join não precisa atribuir um alias, pois o objeto associado não
+ deve ser usado na clausula <literal>where</literal> (ou em qualquer outra clausula).
+ Também, os objetos associados não são retornados diretamente nos resultados da query.
+ Ao invés disso, eles devem ser acessados usando o objeto pai. A única razão que nós
+ podemos necessitar de um alias é quando fazemos um fech join recursivamente em uma
+ coleção adicional:
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+ inner join <literal>fetch</literal>cat.mate
+ left join <literal>fetch</literal>cat.kittens child
+ left join <literal>fetch</literal>child.kittens]]></programlisting>
+
+ <para>
+ Observe que a construção <literal>fetch</literal> não deve ser usada em queries invocadas usando
+ <literal>iterate()</literal> (embora possa ser usado com <literal>scroll()</literal>). O
+ <literal>fetch</literal> também não deve ser usado junto com o <literal>setMaxResults()</literal> ou
+ <literal>setFirstResult()</literal> pois essas operações são baseadas nas linhas retornadas, que
+ normalmente contem duplicidade devido ao fetching das coleções, então o número de linhas pode não
+ ser o que você espera.
+
+ O <literal>fetch</literal> não deve ser usado junto com uma condição <literal>with</literal> em
+ uma condição <literal>with</literal> ad hoc. à possÃvel que seja criado um produto cartesiano pelo
+ join fetching em mais do que uma coleção em uma query, então tome cuidado nesses casos. Um join
+ fetching em varias coleções pode trazer resultados inesperados para mapeamentos do tipo bag, tome
+ cuidado na hora de formular queries como essas. Finalmente, observe o seguinte, o
+ <literal>full join fetch</literal> e <literal>right join fetch</literal> não são significativos.
+ </para>
+
+ <para>
+
+ Se está usando o nÃvel de propriedade lazy (<literal>com</literal> instrumentação de bytecode), é possÃvel
+ forçar o Hibernate a <literal>buscar</literal> as propriedades lazy imediatamente (na primeira query),
+ usando <literal>fetch all properties </literal>.
+ </para>
+
+ <programlisting><![CDATA[from Document <literal>fetch</literal>all properties order by name]]></programlisting>
+ <programlisting><![CDATA[from Document doc <literal>fetch</literal>all properties where lower(doc.name) like '%cats%']]></programlisting>
+
+ </sect1>
+
+ <sect1 id="queryhql-joins-forms">
+ <title>Formas e sintaxe de joins</title>
+
+ <para>
+ O HQL suporta duas formas de associação para união: <literal>implÃcita</literal> e <literal>explicita</literal>.
+ </para>
+
+ <para>
+ As queries apresentadas na seção anterior usam a forma <literal>explicita</literal>, onde a
+ palavra chave "join" é explicitamente usada na clausula "from". Essa é a forma recomendada.
+ </para>
+
+ <para>
+ A forma <literal>implÃcita</literal> não usa a palavra chave "join". Entretanto, as associações
+ são diferenciadas usando pontuação ("." - dotnation). Uniões implÃcitas podem aparecer em
+ qualquer das clausulas HQL. A união <literal>implÃcita</literal> resulta em declarações
+ SQL que contem inner joins.
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat where cat.mate.name like '%s%']]></programlisting>
+ </sect1>
+
+ <sect1 id="queryhql-select">
+ <title>Clausula select</title>
+
+ <para>
+ A clausula <literal>select</literal> seleciona quais obetos e propriedades retornam
+ no resultado da query. Considere:
+ </para>
+
+ <programlisting><![CDATA[select mate
+from Cat as cat
+ inner join cat.mate as mate]]></programlisting>
+
+ <para>
+ A query selecionará <literal>mate</literal>s (companheiros), de outros <literal>Cat</literal>s.
+ Atualmente, podemos expressar a query de forma mais compacta como:
+ </para>
+
+ <programlisting><![CDATA[select cat.mate from Cat cat]]></programlisting>
+
+ <para>
+ Queries podem retornar propriedades de qualquer tipo de valor, incluindo propriedades de tipo de componente:
+
+ </para>
+
+ <programlisting><![CDATA[select cat.name from DomesticCat cat
+where cat.name like 'fri%']]></programlisting>
+
+ <programlisting><![CDATA[select cust.name.firstName from Customer as cust]]></programlisting>
+
+ <para>
+ Queries podem retornar múltiplos objetos e/ou propriedades como um array do
+ tipo Object[],
+ </para>
+
+ <programlisting><![CDATA[select mother, offspr, mate.name
+from DomesticCat as mother
+ inner join mother.mate as mate
+ left outer join mother.kittens as offspr]]></programlisting>
+
+ <para>
+ ou como um <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>
+ ou como um objeto Java 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>
+ assumindo que a classe <literal>Family</literal> tenha um construtor apropriado.
+ </para>
+
+ <para>
+ Pode-se designar referencias a expressões selecionadas, <literal>as</literal>:
+ </para>
+
+ <programlisting><![CDATA[select max(bodyWeight) as max, min(bodyWeight) as min, count(*) as n
+from Cat cat]]></programlisting>
+
+ <para>
+ Isto é bem mais útil quando usado junto <literal>com</literal> <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>
+ Esta query retorna um <literal>Map</literal> de referencias para valores selecionados.
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-aggregation">
+ <title>Funções de agregação</title>
+
+ <para>
+ As queries HQL podem retornar o resultado de funções agregadas nas propriedades.
+ </para>
+
+ <programlisting><![CDATA[select avg(cat.weight), sum(cat.weight), max(cat.weight), count(cat)
+from Cat cat]]></programlisting>
+
+<!-- NO LONGER SUPPORTED
+ <para>
+ Collections may also appear inside aggregate functions in the <literal>select</literal>
+ clause.
+ </para>
+
+ <programlisting><![CDATA[select cat, count( elements(cat.kittens) )
+from Cat cat group by cat]]></programlisting>
+-->
+
+ <para>
+ As funções agregadas suportadas são:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>avg(...), sum(...), min(...), max(...)</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>count(*)</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>count(...), count(distinct ...), count(all...)</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Pode-se usar operadores aritiméticos, concatenação e funções SQL
+ reconhecidas na clausula select:
+ </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>
+ As palavras <literal>distinct</literal> e <literal>all</literal> podem ser usadas e têm
+ a mesma semântica como no SQL.
+ </para>
+
+ <programlisting><![CDATA[select distinct cat.name from Cat cat
+
+select count(distinct cat.name), count(cat) from Cat cat]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="queryhql-polymorphism">
+ <title>Queries polimórficas</title>
+
+ <para>
+ A query:
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat]]></programlisting>
+
+ <para>
+ retorna instancias não só de <literal>Cat</literal>, mas também de subclasses como
+ <literal>DomesticCat</literal>. As queries do Hibernate podem nomear qualquer classe Java
+ ou interface na clausula <literal>from</literal>. A query retornará instancias de toda classe
+ persistente que extenda a determinada classe ou implemente a determinada interface. A query
+ , a seguir, pode retornar todo objeto persistente:
+ </para>
+
+ <programlisting><![CDATA[from java.lang.Object o]]></programlisting>
+
+ <para>
+ A interface <literal>Named</literal> pode ser implementada por várias classes persistentes:
+ </para>
+
+ <programlisting><![CDATA[from Named n, Named m where n.name = m.name]]></programlisting>
+
+ <para>
+ Note que as duas últimas queries requerem mais de um SQL SELECT . Isto significa que a clausula
+ <literal>order by</literal> não ordena corretamente todo o resultado. (Isso também significa que
+ você não pode chamar essas queries usando <literal>Query.scroll()</literal>.)
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-where">
+ <title>A clausula where</title>
+
+ <para>
+ A clausula <literal>where</literal> permite estreitar a lista de instancias retornada.
+ Se não houver referencia alguma, pode-se referir a propriedades pelo nome:
+ </para>
+
+ <programlisting><![CDATA[from Cat where name='Fritz']]></programlisting>
+
+ <para>
+ Se houver uma referência, use o nome da propriedade qualificada:
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat where cat.name='Fritz']]></programlisting>
+
+ <para>
+ retorna instancias de <literal>Cat</literal> com nome âFritzâ.
+ </para>
+
+ <programlisting><![CDATA[select foo
+from Foo foo, Bar bar
+where foo.startDate = bar.date]]></programlisting>
+
+ <para>
+ retornará todas as instancias de <literal>Foo</literal>, para cada
+ um que tiver uma instancia de <literal>bar</literal> com a propriedade
+ <literal>date</literal> igual a propriedade
+ <literal>startDate</literal> de
+ <literal>Foo</literal>. Expressões de filtro compostas fazem da
+ clausula <literal>where</literal>, extremamente poderosa. Consideremos:
+ </para>
+
+ <programlisting><![CDATA[from Cat cat where cat.mate.name is not null]]></programlisting>
+
+ <para>
+ Esta query traduzida para uma query SQL <literal>com</literal> uma tabela (inner) join. Se fosse
+ escrever algo como:
+ </para>
+
+ <programlisting><![CDATA[from Foo foo
+where foo.bar.baz.customer.address.city is not null]]></programlisting>
+
+ <para>
+ Poderia-se terminar <literal>com</literal> uma query que necessitasse de join de quatro tabelas,
+ no SQL.
+ </para>
+
+ <para>
+ O operador <literal>=</literal> pode ser uasdo para comparar não apenas propriedades,
+ mas também instancias:
+ </para>
+
+ <programlisting><![CDATA[from Cat cat, Cat rival where cat.mate = rival.mate]]></programlisting>
+
+ <programlisting><![CDATA[select cat, mate
+from Cat cat, Cat mate
+where cat.mate = mate]]></programlisting>
+
+ <para>
+ A propriedade especial (lowercase) <literal>id</literal> pode ser usada para referenciar
+ o identificador único de um objeto. (Pode-se usar também o nome de sua propriedade)
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat where cat.id = 123
+
+from Cat as cat where cat.mate.id = 69]]></programlisting>
+
+ <para>
+ A Segunda query é eficiente. Nenhuma união de tabelas é necessária!
+ </para>
+
+ <para>
+ As propriedades de identificadores compostas também podem ser usadas. Suponha que
+ <literal>Person</literal> tenha um identificador composto que consiste de
+ <literal>country</literal> e <literal>medicareNumber</literal>.
+ </para>
+
+ <programlisting><![CDATA[from bank.Person person
+where person.id.country = 'AU'
+ and person.id.medicareNumber = 123456]]></programlisting>
+
+ <programlisting><![CDATA[from bank.Account account
+where account.owner.id.country = 'AU'
+ and account.owner.id.medicareNumber = 123456]]></programlisting>
+
+ <para>
+ Mais uma vez, a Segunda query não precisa de nenhum join de tabela.
+ </para>
+
+ <para>
+ Assim mesmo, a propriedade especial <literal>class</literal> acessa o valor discriminador da
+ instancia, no caso de persistência polimórfica. O nome de uma classe Java inclusa em uma
+ clausula "where", será traduzida para seu valor descriminante.
+ </para>
+
+ <programlisting><![CDATA[from Cat cat where cat.class = DomesticCat]]></programlisting>
+
+ <para>
+ Pode-se também especificar as propriedades dos components ou tipos de usuário composto
+ (e de componentes de componentes). Nunca tente usar uma expressão de filtro que termine na propriedade
+ de um tipo de componente (ao contrário de uma propriedade de um componente). Por exemplo,
+ se store.owner é uma entidade <literal>com</literal> um componente <literal>address</literal>.
+ </para>
+
+ <programlisting><![CDATA[store.owner.address.city // okay
+store.owner.address // error!]]></programlisting>
+
+ <para>
+ Um tipo "any" tem as propriedades <literal>id</literal> e <literal>class</literal> especiais,
+ nôs permitindo expressar um join da seguinte forma (onde <literal>AuditLog.item</literal> é
+ uma propriedade mapeada <literal>com</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>
+ Veja que <literal>log.item.class</literal> e <literal>payment.class</literal> podem
+ referir-se a valores de colunas de banco de dados completamente diferentes, na query acima.
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-expressions">
+ <title>Expressões</title>
+
+ <para>
+ As expressões permitidas na cláusula <literal>where</literal> inclui a maioria
+ das coisas que você poderia escrever no SQL:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ operadores matemáticos <literal>+, -, *, /</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ operadores de comparação binários <literal>=, >=, <=, <>, !=, like</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ operadores lógicos <literal>and, or, not</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ parenteses <literal>( )</literal>, indicating grouping
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>in</literal>,
+ <literal>not in</literal>,
+ <literal>between</literal>,
+ <literal>is null</literal>,
+ <literal>is not null</literal>,
+ <literal>is empty</literal>,
+ <literal>is not empty</literal>,
+ <literal>member of</literal> and
+ <literal>not member of</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ case "simples" , <literal>case ... when ... then ... else ... end</literal>, and
+ "searched" case, <literal>case when ... then ... else ... end</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ concatenação de string <literal>...||...</literal> ou <literal>concat(...,...)</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>current_date()</literal>, <literal>current_time()</literal>,
+ <literal>current_timestamp()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>second(...)</literal>, <literal>minute(...)</literal>,
+ <literal>hour(...)</literal>, <literal>day(...)</literal>,
+ <literal>month(...)</literal>, <literal>year(...)</literal>,
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ qualquer funcao ou operador definida pela EJB-QL 3.0: <literal>substring(), trim(),
+ lower(), upper(), length(), locate(), abs(), sqrt(), bit_length(), mod()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>coalesce()</literal> and <literal>nullif()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>str()</literal> para converter valores numericos ou temporais para string
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>cast(... as ...)</literal>, onde o segundo argumento é o nome do tipo
+ hibernate, e<literal>extract(... from ...)</literal> se ANSI
+ <literal>cast()</literal> e <literal>extract()</literal> é suportado pele
+ banco de dados usado
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ A função HQL <literal>index()</literal> , que se aplicam a referencias de
+ coleçôes associadas e indexadas
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ As funções hql que retornam expressões de coleções de valores:
+ <literal>size(), minelement(), maxelement(), minindex(), maxindex()</literal>,
+ <literal>junto</literal> com o elemento especial, <literal>elements()</literal>,
+ e funções de <literal>Ãndice</literal> que podem ser quantificadas usando
+ <literal>some, all, exists, any, in</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Qualquer funçâo escalar pelo bando de dados como <literal>sign()</literal>,
+ <literal>trunc()</literal>, <literal>rtrim()</literal>, <literal>sin()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Parametros posicionais ao estilo JDBC <literal>?</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Parametros nomeados <literal>:name</literal>, <literal>:start_date</literal>, <literal>:x1</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Literais SQL <literal>'foo'</literal>, <literal>69</literal>, <literal>6.66E+2</literal>,
+ <literal>'1970-01-01 10:00:01.0'</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Constantes Java <literal>public static final</literal> <literal>ex: Color.TABBY</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ <literal>in</literal> e <literal>between</literal> podem ser usadas da seguinte maneira:
+ </para>
+
+ <programlisting><![CDATA[from DomesticCat cat where cat.name between 'A' and 'B']]></programlisting>
+
+ <programlisting><![CDATA[from DomesticCat cat where cat.name in ( 'Foo', 'Bar', 'Baz' )]]></programlisting>
+
+ <para>
+ e as formas negativas podem ser escritas
+ </para>
+
+ <programlisting><![CDATA[from DomesticCat cat where cat.name not between 'A' and 'B']]></programlisting>
+
+ <programlisting><![CDATA[from DomesticCat cat where cat.name not in ( 'Foo', 'Bar', 'Baz' )]]></programlisting>
+
+ <para>
+ Likewise, <literal>is null</literal> and <literal>is not null</literal> may be used to test
+ for null values.
+ Assim mesmo, , <literal>is null</literal> e <literal>is not null</literal> podem ser usados
+ para testar valores nulos.
+ </para>
+
+ <para>
+ Booleanos podem ser facilmente usados em expressões, declarando as substituições da HQL query,
+ na configuração do Hibernate
+ </para>
+
+ <programlisting><![CDATA[<property name="hibernate.query.substitutions">true 1, false 0</property>]]></programlisting>
+
+ <para>
+ Isso irá substituir as palavras chave <literal>true</literal> e <literal>false</literal>
+ <literal>pelos</literal> literais <literal>1</literal> e <literal>0</literal> na tradução do HQL para SQL.
+ </para>
+
+ <programlisting><![CDATA[from Cat cat where cat.alive = true]]></programlisting>
+
+ <para>
+ Pode-se testar o tamanho de uma coleção <literal>com</literal> a propriedade especial <literal>size</literal>,
+ ou a função especial <literal>size()</literal>.
+ </para>
+
+ <programlisting><![CDATA[from Cat cat where cat.kittens.size > 0]]></programlisting>
+
+ <programlisting><![CDATA[from Cat cat where size(cat.kittens) > 0]]></programlisting>
+
+ <para>
+ Para coleções indexadas, você pode se referir aos Ãndices máximo e mÃnimo, usando
+ as funções <literal>minindex</literal> e <literal>maxindex</literal>. Similarmente,
+ pode-se referir aos elementos máximo e mÃnimo de uma coleção de tipos básicos usando
+ as funções <literal>minelement</literal> e <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>
+ As funções SQL <literal>any, some, all, exists, in</literal> são suportadas quando passado o
+ elemento ou o conjunto de Ãndices de uma coleção (<literal>elements</literal> e
+ <literal>indices</literal> de funções), ou o resultado de uma subquery (veja abaixo).
+ </para>
+
+ <programlisting><![CDATA[select mother from Cat as mother, Cat as kit
+where kit in elements(foo.kittens)]]></programlisting>
+
+ <programlisting><![CDATA[select p from NameList list, Person p
+where p.name = some elements(list.names)]]></programlisting>
+
+ <programlisting><![CDATA[from Cat cat where exists elements(cat.kittens)]]></programlisting>
+
+ <programlisting><![CDATA[from Player p where 3 > all elements(p.scores)]]></programlisting>
+
+ <programlisting><![CDATA[from Show show where 'fizard' in indices(show.acts)]]></programlisting>
+
+ <para>
+ Note que essas construções - <literal>size</literal>, <literal>elements</literal>,
+ <literal>indices</literal>, <literal>minindex</literal>, <literal>maxindex</literal>,
+ <literal>minelement</literal>, <literal>maxelement</literal>â
+ só podem ser usados na clausula where do Hibernate3.
+ </para>
+
+ <para>
+ Elementos de coleções com Ãndice (arrays, lists, maps), podem ser referenciadas
+ pelo Ãndice (apenas na clausula where):
+ </para>
+
+ <programlisting><![CDATA[from Order order where order.items[0].id = 1234]]></programlisting>
+
+ <programlisting><![CDATA[select person from Person person, Calendar calendar
+where calendar.holidays['national day'] = person.birthDay
+ and person.nationality.calendar = calendar]]></programlisting>
+
+ <programlisting><![CDATA[select item from Item item, Order order
+where order.items[ order.deliveredItemIndices[0] ] = item and order.id = 11]]></programlisting>
+
+ <programlisting><![CDATA[select item from Item item, Order order
+where order.items[ maxindex(order.items) ] = item and order.id = 11]]></programlisting>
+
+ <para>
+ A expressão entre colchetes <literal>[]</literal>, pode ser até uma expressão aritimética.
+ </para>
+
+ <programlisting><![CDATA[select item from Item item, Order order
+where order.items[ size(order.items) - 1 ] = item]]></programlisting>
+
+ <para>
+ O HQL também provê a função interna <literal>index()</literal>, para elementos de
+ associação um-pra-muitos ou coleção de valores.
+ </para>
+
+ <programlisting><![CDATA[select item, index(item) from Order order
+ join order.items item
+where index(item) < 5]]></programlisting>
+
+ <para>
+ Funções escalares SQL, suportadas pelo banco de dados subjacente.
+ </para>
+
+ <programlisting><![CDATA[from DomesticCat cat where upper(cat.name) like 'FRI%']]></programlisting>
+
+ <para>
+ Se ainda ainda não está totalmente convencido, pense o quão maior e menos legÃvel poderia
+ ser a query a seguir, em SQL:
+ </para>
+
+ <programlisting><![CDATA[select cust
+from Product prod,
+ Store store
+ inner join store.customers cust
+where prod.name = 'widget'
+ and store.location.name in ( 'Melbourne', 'Sydney' )
+ and prod = all elements(cust.currentOrder.lineItems)]]></programlisting>
+
+ <para>
+ <emphasis>Hint:</emphasis> something like
+ </para>
+
+ <programlisting><![CDATA[SELECT cust.name, cust.address, cust.phone, cust.id, cust.current_order
+FROM customers cust,
+ stores store,
+ locations loc,
+ store_customers sc,
+ product prod
+WHERE prod.name = 'widget'
+ AND store.loc_id = loc.id
+ AND loc.name IN ( 'Melbourne', 'Sydney' )
+ AND sc.store_id = store.id
+ AND sc.cust_id = cust.id
+ AND prod.id = ALL(
+ SELECT item.prod_id
+ FROM line_items item, orders o
+ WHERE item.order_id = o.id
+ AND cust.current_order = o.id
+ )]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="queryhql-ordering">
+ <title>A clausula order by</title>
+
+ <para>
+ A lista retornada pela query pode ser ordenada por qualquer propriedade da classe ou componente retornado:
+ </para>
+
+ <programlisting><![CDATA[from DomesticCat cat
+order by cat.name asc, cat.weight desc, cat.birthdate]]></programlisting>
+
+ <para>
+ As opções <literal>asc</literal> ou <literal>desc</literal> indicam ordem crescente ou decrescente,
+ respectivamente.
+ </para>
+ </sect1>
+
+ <sect1 id="queryhql-grouping">
+ <title>A clausula group by</title>
+
+ <para>
+ Uma query que retorne valores agregados, podem ser agrupados por qualquer propriedade de uma classe
+ ou componente retornado:
+ </para>
+
+ <programlisting><![CDATA[select cat.color, sum(cat.weight), count(cat)
+from Cat cat
+group by cat.color]]></programlisting>
+
+ <programlisting><![CDATA[select foo.id, avg(name), max(name)
+from Foo foo join foo.names name
+group by foo.id]]></programlisting>
+
+ <para>
+ Uma clausula <literal>having</literal> também é permitida.
+ </para>
+
+ <programlisting><![CDATA[select cat.color, sum(cat.weight), count(cat)
+from Cat cat
+group by cat.color
+having cat.color in (eg.Color.TABBY, eg.Color.BLACK)]]></programlisting>
+
+ <para>
+ Funções SQL e funções agregadas são permitidas nas clausulas
+ <literal>having</literal> e <literal>order by</literal>, se suportadas pelo banco
+ de dados subjacente (ex: não no MySQL).
+ </para>
+
+ <programlisting><![CDATA[select cat
+from Cat cat
+ join cat.kittens kitten
+group by cat
+having avg(kitten.weight) > 100
+order by count(kitten) asc, sum(kitten.weight) desc]]></programlisting>
+
+ <para>
+ Note que, nem a clausula <literal>group by</literal> ou
+ <literal>order by</literal>, podem conter expressões aritiméticas.
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-subqueries" revision="2">
+ <title>Subqueries</title>
+
+ <para>
+ Para bancos de dados que suportem subselects, o Hibernate suporta subqueries dentro de queries.
+ Uma subquery precisa estar entre parênteses (normalmente uma chamada de função agregada SQL).
+ Mesmo subqueries co-relacionadas (subqueries que fazem referência à alias de outras queries),
+ são aceitas.
+ </para>
+
+ <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>
+
+ <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>
+
+ <programlisting><![CDATA[select cat.id, (select max(kit.weight) from cat.kitten kit)
+from Cat as cat]]></programlisting>
+
+ <para>
+ Note que HQL subqueries podem aparecer apenas dentro de clausulas select ou where.
+ </para>
+
+ <para>
+ Para subqueries <literal>com</literal> mais de uma expressão na lista do select, pode-se usar um construtor
+ de tuplas:
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+where not ( cat.name, cat.color ) in (
+ select cat.name, cat.color from DomesticCat cat
+)]]></programlisting>
+
+ <para>
+ Veja que em alguns bancos de dados (mas não o Oracle ou HSQL), pode-se usar construtores
+ de tuplas em outros contextos. Por exemplo quando buscando componentes ou tipos de usuário composto.
+ </para>
+
+ <programlisting><![CDATA[from Person where name = ('Gavin', 'A', 'King')]]></programlisting>
+
+ <para>
+ Qual é equivalente ao mais verbalizado:
+ </para>
+
+ <programlisting><![CDATA[from Person where name.first = 'Gavin' and name.initial = 'A' and name.last = 'King')]]></programlisting>
+
+ <para>
+ Há duas razões boas que você pode não querer fazer este tipo de coisa: primeira, não
+ é completamente portável entre plataformas de banco de dados; segunda, a query agora é
+ dependente da ordem de propriedades no documento de mapeamento.
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-examples">
+ <title>Exemplos de HQL</title>
+
+ <para>
+ As queries do Hibernate, podem ser muito poderosas e complexas. De fato, o poder da linguagem de
+ querie é um dos pontos principais na distribuição do Hibernate. Aqui temos algumas queries de exemplo,
+ muito similares a queries que usei em um projeto recente. Note que a maioria das queries que você
+ irá escrever, são mais simples que estas.
+ </para>
+
+ <para>
+ A query a seguir retorna o id de order, numero de itens e o valor total do order para todos os
+ orders não pagos para um freguês particular e valor total mÃnimo dado, ordenando os resultados por
+ valor total. Ao determinar os preços, é usado o catalogo corrente. A query SQL resultante,
+ usando tabelas <literal>ORDER</literal>, <literal>ORDER_LINE</literal>, <literal>PRODUCT</literal>,
+ <literal>CATALOG</literal> e <literal>PRICE</literal>, tem quatro inner joins e um
+ (não correlacionado) subselect.
+ </para>
+
+ <programlisting><![CDATA[select order.id, sum(price.amount), count(item)
+from Order as order
+ join order.lineItems as item
+ join item.product as product,
+ Catalog as catalog
+ join catalog.prices as price
+where order.paid = false
+ and order.customer = :customer
+ and price.product = product
+ and catalog.effectiveDate < sysdate
+ and catalog.effectiveDate >= all (
+ select cat.effectiveDate
+ from Catalog as cat
+ where cat.effectiveDate < sysdate
+ )
+group by order
+having sum(price.amount) > :minAmount
+order by sum(price.amount) desc]]></programlisting>
+
+ <para>
+ Que monstro! Atualmente, na vida real, eu não sou muito afeiçoado a subqueries, então
+ minha query seria mais parecida com isto:
+ </para>
+
+ <programlisting><![CDATA[select order.id, sum(price.amount), count(item)
+from Order as order
+ join order.lineItems as item
+ join item.product as product,
+ Catalog as catalog
+ join catalog.prices as price
+where order.paid = false
+ and order.customer = :customer
+ and price.product = product
+ and catalog = :currentCatalog
+group by order
+having sum(price.amount) > :minAmount
+order by sum(price.amount) desc]]></programlisting>
+
+ <para>
+ A próxima query conta o número de pagamentos em cada status, tirando todos os pagamentos com
+ status <literal>AWAITING_APPROVAL</literal>, onde a mais recente mudança de status foi feita
+ pelo usuário corrente. Traduz-se para uma query SQL <literal>com</literal> dois inner joins e
+ um subselect correlacionado, nas tabelas <literal>PAYMENT</literal>,
+ <literal>PAYMENT_STATUS</literal> e <literal>PAYMENT_STATUS_CHANGE</literal> .
+ </para>
+
+ <programlisting><![CDATA[select count(payment), status.name
+from Payment as payment
+ join payment.currentStatus as status
+ join payment.statusChanges as statusChange
+where payment.status.name <> PaymentStatus.AWAITING_APPROVAL
+ or (
+ statusChange.timeStamp = (
+ select max(change.timeStamp)
+ from PaymentStatusChange change
+ where change.payment = payment
+ )
+ and statusChange.user <> :currentUser
+ )
+group by status.name, status.sortOrder
+order by status.sortOrder]]></programlisting>
+
+ <para>
+ Se eu tivesse mapeado a Collection <literal>statusChanges</literal> como um List, ao invés de um
+ Set, a query teria sido muito mais simples de escrever.
+ </para>
+
+ <programlisting><![CDATA[select count(payment), status.name
+from Payment as payment
+ join payment.currentStatus as status
+where payment.status.name <> PaymentStatus.AWAITING_APPROVAL
+ or payment.statusChanges[ maxIndex(payment.statusChanges) ].user <> :currentUser
+group by status.name, status.sortOrder
+order by status.sortOrder]]></programlisting>
+
+ <para>
+ A próxima query usa a função <literal>isNull()</literal> do MS SQL Server, para retornar
+ todas as contas e pagamentos não pagos para a organização, para cada usuário corrente
+ pertencente. Traduz-se para uma query SQL <literal>com</literal> três inner joins,
+ um outer join e um subselect nas tabelas <literal>ACCOUNT</literal>, <literal>PAYMENT</literal>,
+ <literal>PAYMENT_STATUS</literal>,<literal>ACCOUNT_TYPE</literal>,
+ <literal>ORGANIZATION</literal> e <literal>ORG_USER</literal> .
+ </para>
+
+ <programlisting><![CDATA[select account, payment
+from Account as account
+ left outer join account.payments as payment
+where :currentUser in elements(account.holder.users)
+ and PaymentStatus.UNPAID = isNull(payment.currentStatus.name, PaymentStatus.UNPAID)
+order by account.type.sortOrder, account.accountNumber, payment.dueDate]]></programlisting>
+
+ <para>
+ Para alguns bancos de dados, precisaremos eleminar o subselect (correlacionado).
+ </para>
+
+ <programlisting><![CDATA[select account, payment
+from Account as account
+ join account.holder.users as user
+ left outer join account.payments as payment
+where :currentUser = user
+ and PaymentStatus.UNPAID = isNull(payment.currentStatus.name, PaymentStatus.UNPAID)
+order by account.type.sortOrder, account.accountNumber, payment.dueDate]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="queryhql-bulk" revision="2">
+ <title>update e delete em lote</title>
+
+ <para>
+ Agora o HQL suporta declarações, <literal>update</literal>,
+ <literal>delete</literal> e <literal>insert ... select ...</literal>
+ Veja <xref linkend="batch-direct"/>, para mais detalhes.
+ </para>
+ </sect1>
+
+ <sect1 id="queryhql-tipstricks">
+ <title>Dicas e Truques</title>
+
+ <para>
+ Pode-se contar o número de resultados da query, sem realmente retorna-los.
+ </para>
+
+ <programlisting><![CDATA[( (Integer) session.iterate("select count(*) from ....").next() ).intValue()]]></programlisting>
+
+ <para>
+ Para ordenar um resultado pelo tamanho de uma Collection, use a query a seguir.
+ </para>
+
+ <programlisting><![CDATA[select usr.id, usr.name
+from User as usr
+ left join usr.messages as msg
+group by usr.id, usr.name
+order by count(msg)]]></programlisting>
+
+ <para>
+ Se seu banco de dados suporta subselects, pode-se colocar uma condição sobre
+ tamanho de seleção na cláusula where da sua query:
+ </para>
+
+ <programlisting><![CDATA[from User usr where size(usr.messages) >= 1]]></programlisting>
+
+ <para>
+ Se seu banco de dados não suporta subselects, use a query a seguir:
+ </para>
+
+ <programlisting><![CDATA[select usr.id, usr.name
+from User usr.name
+ join usr.messages msg
+group by usr.id, usr.name
+having count(msg) >= 1]]></programlisting>
+
+ <para>
+ Com essa solução não se pode retornar um <literal>User</literal> <literal>com</literal> sem
+ nenhuma menssagem, por causa do "inner join", a forma a seguir também é útil.
+ </para>
+
+ <programlisting><![CDATA[select usr.id, usr.name
+from User as usr
+ left join usr.messages as msg
+group by usr.id, usr.name
+having count(msg) = 0]]></programlisting>
+
+ <para>
+ As propriedades de um JavaBean podem ser limitadas à parâmetros nomeados da query:
+ </para>
+
+ <programlisting><![CDATA[Query q = s.createQuery("from foo Foo as foo where foo.name=:name and foo.size=:size");
+q.setProperties(fooBean); // fooBean has getName() and getSize()
+List foos = q.list();]]></programlisting>
+
+ <para>
+ As Collections são paginaveis, usando a interface <literal>Query</literal> <literal>com</literal> um filtro:
+ </para>
+
+ <programlisting><![CDATA[Query q = s.createFilter( collection, "" ); // the trivial filter
+q.setMaxResults(PAGE_SIZE);
+q.setFirstResult(PAGE_SIZE * pageNumber);
+List page = q.list();]]></programlisting>
+
+ <para>
+ Os elementos da Collection podem ser ordenados ou agrupados
+ usando um filtro de query:
+ </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>
+
+ <para>
+ Pode-se achar o tamanho de uma Collection sem inicializa-la:
+ </para>
+
+ <programlisting><![CDATA[( (Integer) session.iterate("select count(*) from ....").next() ).intValue();]]></programlisting>
+
+ </sect1>
+
+</chapter>
+
Property changes on: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/query_hql.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/query_sql.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/query_sql.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/query_sql.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,783 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<chapter id="querysql" revision="2">
+ <title>Native SQL</title>
+
+ <para>You may also express queries in the native SQL dialect of your
+ database. This is useful if you want to utilize database specific features
+ such as query hints or the <literal>CONNECT</literal> keyword in Oracle. It
+ also provides a clean migration path from a direct SQL/JDBC based
+ application to Hibernate.</para>
+
+ <para>Hibernate3 allows you to specify handwritten SQL (including stored
+ procedures) for all create, update, delete, and load operations.</para>
+
+ <sect1 id="querysql-creating" revision="4">
+ <title>Using a <literal>SQLQuery</literal></title>
+
+ <para>Execution of native SQL queries is controlled via the
+ <literal>SQLQuery</literal> interface, which is obtained by calling
+ <literal>Session.createSQLQuery()</literal>. The following describes how
+ to use this API for querying.</para>
+
+ <sect2>
+ <title>Scalar queries</title>
+
+ <para>The most basic SQL query is to get a list of scalars
+ (values).</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT * FROM CATS").list();
+sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS").list();
+]]></programlisting>
+
+ <para>These will both return a List of Object arrays (Object[]) with
+ scalar values for each column in the CATS table. Hibernate will use
+ ResultSetMetadata to deduce the actual order and types of the returned
+ scalar values.</para>
+
+ <para>To avoid the overhead of using
+ <literal>ResultSetMetadata</literal> or simply to be more explicit in
+ what is returned one can use <literal>addScalar()</literal>.</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT * FROM CATS")
+ .addScalar("ID", Hibernate.LONG)
+ .addScalar("NAME", Hibernate.STRING)
+ .addScalar("BIRTHDATE", Hibernate.DATE)
+]]></programlisting>
+
+ <para>This query specified:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>the SQL query string</para>
+ </listitem>
+
+ <listitem>
+ <para>the columns and types to return</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>This will still return Object arrays, but now it will not use
+ <literal>ResultSetMetdata</literal> but will instead explicitly get the
+ ID, NAME and BIRTHDATE column as respectively a Long, String and a Short
+ from the underlying resultset. This also means that only these three
+ columns will be returned, even though the query is using
+ <literal>*</literal> and could return more than the three listed
+ columns.</para>
+
+ <para>It is possible to leave out the type information for all or some
+ of the scalars.</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT * FROM CATS")
+ .addScalar("ID", Hibernate.LONG)
+ .addScalar("NAME")
+ .addScalar("BIRTHDATE")
+]]></programlisting>
+
+ <para>This is essentially the same query as before, but now
+ <literal>ResultSetMetaData</literal> is used to decide the type of NAME
+ and BIRTHDATE where as the type of ID is explicitly specified.</para>
+
+ <para>How the java.sql.Types returned from ResultSetMetaData is mapped
+ to Hibernate types is controlled by the Dialect. If a specific type is
+ not mapped or does not result in the expected type it is possible to
+ customize it via calls to <literal>registerHibernateType</literal> in
+ the Dialect.</para>
+ </sect2>
+
+ <sect2>
+ <title>Entity queries</title>
+
+ <para>The above queries were all about returning scalar values,
+ basically returning the "raw" values from the resultset. The following
+ shows how to get entity objects from a native sql query via
+ <literal>addEntity()</literal>.</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT * FROM CATS").addEntity(Cat.class);
+sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS").addEntity(Cat.class);
+]]></programlisting>
+
+ <para>This query specified:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>the SQL query string</para>
+ </listitem>
+
+ <listitem>
+ <para>the entity returned by the query</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>Assuming that Cat is mapped as a class with the columns ID, NAME
+ and BIRTHDATE the above queries will both return a List where each
+ element is a Cat entity.</para>
+
+ <para>If the entity is mapped with a <literal>many-to-one</literal> to
+ another entity it is required to also return this when performing the
+ native query, otherwise a database specific "column not found" error
+ will occur. The additional columns will automatically be returned when
+ using the * notation, but we prefer to be explicit as in the following
+ example for a <literal>many-to-one</literal> to a
+ <literal>Dog</literal>:</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE, DOG_ID FROM CATS").addEntity(Cat.class);
+]]></programlisting>
+
+ <para>This will allow cat.getDog() to function properly.</para>
+ </sect2>
+
+ <sect2>
+ <title>Handling associations and collections</title>
+
+ <para>It is possible to eagerly join in the <literal>Dog</literal> to
+ avoid the possible extra roundtrip for initializing the proxy. This is
+ done via the <literal>addJoin()</literal> method, which allows you to
+ join in an association or collection.</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT c.ID, NAME, BIRTHDATE, DOG_ID, D_ID, D_NAME FROM CATS c, DOGS d WHERE c.DOG_ID = d.D_ID")
+ .addEntity("cat", Cat.class)
+ .addJoin("cat.dog");
+]]></programlisting>
+
+ <para>In this example the returned <literal>Cat</literal>'s will have
+ their <literal>dog</literal> property fully initialized without any
+ extra roundtrip to the database. Notice that we added a alias name
+ ("cat") to be able to specify the target property path of the join. It
+ is possible to do the same eager joining for collections, e.g. if the
+ <literal>Cat</literal> had a one-to-many to <literal>Dog</literal>
+ instead.</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE, D_ID, D_NAME, CAT_ID FROM CATS c, DOGS d WHERE c.ID = d.CAT_ID")
+ .addEntity("cat", Cat.class)
+ .addJoin("cat.dogs");
+]]></programlisting>
+
+ <p>At this stage we are reaching the limits of what is possible with
+ native queries without starting to enhance the sql queries to make them
+ usable in Hibernate; the problems starts to arise when returning
+ multiple entities of the same type or when the default alias/column
+ names are not enough.</p>
+ </sect2>
+
+ <sect2>
+ <title>Returning multiple entities</title>
+
+ <para>Until now the result set column names are assumed to be the same
+ as the column names specified in the mapping document. This can be
+ problematic for SQL queries which join multiple tables, since the same
+ column names may appear in more than one table.</para>
+
+ <para>Column alias injection is needed in the following query (which
+ most likely will fail):</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT c.*, m.* FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID")
+ .addEntity("cat", Cat.class)
+ .addEntity("mother", Cat.class)
+]]></programlisting>
+
+ <para>The intention for this query is to return two Cat instances per
+ row, a cat and its mother. This will fail since there is a conflict of
+ names since they are mapped to the same column names and on some
+ databases the returned column aliases will most likely be on the form
+ "c.ID", "c.NAME", etc. which are not equal to the columns specificed in
+ the mappings ("ID" and "NAME").</para>
+
+ <para>The following form is not vulnerable to column name
+ duplication:</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT {cat.*}, {mother.*} FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID")
+ .addEntity("cat", Cat.class)
+ .addEntity("mother", Cat.class)
+]]></programlisting>
+
+ <para>This query specified:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>the SQL query string, with placeholders for Hibernate to
+ inject column aliases</para>
+ </listitem>
+
+ <listitem>
+ <para>the entities returned by the query</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>The {cat.*} and {mother.*} notation used above is a shorthand for
+ "all properties". Alternatively, you may list the columns explicity, but
+ even in this case we let Hibernate inject the SQL column aliases for
+ each property. The placeholder for a column alias is just the property
+ name qualified by the table alias. In the following example, we retrieve
+ Cats and their mothers from a different table (cat_log) to the one
+ declared in the mapping metadata. Notice that we may even use the
+ property aliases in the where clause if we like.</para>
+
+ <programlisting><![CDATA[String sql = "SELECT ID as {c.id}, NAME as {c.name}, " +
+ "BIRTHDATE as {c.birthDate}, MOTHER_ID as {c.mother}, {mother.*} " +
+ "FROM CAT_LOG c, CAT_LOG m WHERE {c.mother} = c.ID";
+
+List loggedCats = sess.createSQLQuery(sql)
+ .addEntity("cat", Cat.class)
+ .addEntity("mother", Cat.class).list()
+]]></programlisting>
+
+ <sect3 id="querysql-aliasreferences" revision="2">
+ <title>Alias and property references</title>
+
+ <para>For most cases the above alias injection is needed, but for
+ queries relating to more complex mappings like composite properties,
+ inheritance discriminators, collections etc. there are some specific
+ aliases to use to allow Hibernate to inject the proper aliases.</para>
+
+ <para>The following table shows the different possibilities of using
+ the alias injection. Note: the alias names in the result are examples,
+ each alias will have a unique and probably different name when
+ used.</para>
+
+ <table frame="topbot" id="aliasinjection-summary">
+ <title>Alias injection names</title>
+
+ <tgroup cols="3">
+ <colspec colwidth="1*" />
+
+ <colspec colwidth="1*" />
+
+ <colspec colwidth="2.5*" />
+
+ <thead>
+ <row>
+ <entry>Description</entry>
+
+ <entry>Syntax</entry>
+
+ <entry>Example</entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry>A simple property</entry>
+
+ <entry><literal>{[aliasname].[propertyname]</literal></entry>
+
+ <entry><literal>A_NAME as {item.name}</literal></entry>
+ </row>
+
+ <row>
+ <entry>A composite property</entry>
+
+ <entry><literal>{[aliasname].[componentname].[propertyname]}</literal></entry>
+
+ <entry><literal>CURRENCY as {item.amount.currency}, VALUE as
+ {item.amount.value}</literal></entry>
+ </row>
+
+ <row>
+ <entry>Discriminator of an entity</entry>
+
+ <entry><literal>{[aliasname].class}</literal></entry>
+
+ <entry><literal>DISC as {item.class}</literal></entry>
+ </row>
+
+ <row>
+ <entry>All properties of an entity</entry>
+
+ <entry><literal>{[aliasname].*}</literal></entry>
+
+ <entry><literal>{item.*}</literal></entry>
+ </row>
+
+ <row>
+ <entry>A collection key</entry>
+
+ <entry><literal>{[aliasname].key}</literal></entry>
+
+ <entry><literal>ORGID as {coll.key}</literal></entry>
+ </row>
+
+ <row>
+ <entry>The id of an collection</entry>
+
+ <entry><literal>{[aliasname].id}</literal></entry>
+
+ <entry><literal>EMPID as {coll.id}</literal></entry>
+ </row>
+
+ <row>
+ <entry>The element of an collection</entry>
+
+ <entry><literal>{[aliasname].element}</literal></entry>
+
+ <entry><literal>XID as {coll.element}</literal></entry>
+ </row>
+
+ <row>
+ <entry>roperty of the element in the collection</entry>
+
+ <entry><literal>{[aliasname].element.[propertyname]}</literal></entry>
+
+ <entry><literal>NAME as {coll.element.name}</literal></entry>
+ </row>
+
+ <row>
+ <entry>All properties of the element in the collection</entry>
+
+ <entry><literal>{[aliasname].element.*}</literal></entry>
+
+ <entry><literal>{coll.element.*}</literal></entry>
+ </row>
+
+ <row>
+ <entry>All properties of the the collection</entry>
+
+ <entry><literal>{[aliasname].*}</literal></entry>
+
+ <entry><literal>{coll.*}</literal></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect3>
+ </sect2>
+
+ <sect2>
+ <title>Returning non-managed entities</title>
+
+ <para>It is possible to apply a ResultTransformer to native sql queries. Allowing it to e.g. return non-managed entities.</para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT NAME, BIRTHDATE FROM CATS")
+ .setResultTransformer(Transformers.aliasToBean(CatDTO.class))]]></programlisting>
+
+ <para>This query specified:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>the SQL query string</para>
+ </listitem>
+
+ <listitem>
+ <para>a result transformer</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ The above query will return a list of <literal>CatDTO</literal> which has been instantiated and injected the values of NAME and BIRTHNAME into its corresponding
+ properties or fields.
+ </para>
+ </sect2>
+
+ <sect2>
+ <title>Handling inheritance</title>
+
+ <para>Native sql queries which query for entities that is mapped as part
+ of an inheritance must include all properties for the baseclass and all
+ it subclasses.</para>
+ </sect2>
+
+ <sect2>
+ <title>Parameters</title>
+
+ <para>Native sql queries support positional as well as named
+ parameters:</para>
+
+ <programlisting><![CDATA[Query query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME like ?").addEntity(Cat.class);
+List pusList = query.setString(0, "Pus%").list();
+
+query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME like :name").addEntity(Cat.class);
+List pusList = query.setString("name", "Pus%").list(); ]]></programlisting>
+ </sect2>
+
+
+
+ </sect1>
+
+ <sect1 id="querysql-namedqueries" revision="3">
+ <title>Named SQL queries</title>
+
+ <para>Named SQL queries may be defined in the mapping document and called
+ in exactly the same way as a named HQL query. In this case, we do
+ <emphasis>not</emphasis> need to call
+ <literal>addEntity()</literal>.</para>
+
+ <programlisting><![CDATA[<sql-query name="persons">
+ <return alias="person" class="eg.Person"/>
+ SELECT person.NAME AS {person.name},
+ person.AGE AS {person.age},
+ person.SEX AS {person.sex}
+ FROM PERSON person
+ WHERE person.NAME LIKE :namePattern
+</sql-query>]]></programlisting>
+
+ <programlisting><![CDATA[List people = sess.getNamedQuery("persons")
+ .setString("namePattern", namePattern)
+ .setMaxResults(50)
+ .list();]]></programlisting>
+
+ <para>The <literal><return-join></literal> and
+ <literal><load-collection></literal> elements are used to join
+ associations and define queries which initialize collections,
+ respectively.</para>
+
+ <programlisting><![CDATA[<sql-query name="personsWith">
+ <return alias="person" class="eg.Person"/>
+ <return-join alias="address" property="person.mailingAddress"/>
+ SELECT person.NAME AS {person.name},
+ person.AGE AS {person.age},
+ person.SEX AS {person.sex},
+ adddress.STREET AS {address.street},
+ adddress.CITY AS {address.city},
+ adddress.STATE AS {address.state},
+ adddress.ZIP AS {address.zip}
+ FROM PERSON person
+ JOIN ADDRESS adddress
+ ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'
+ WHERE person.NAME LIKE :namePattern
+</sql-query>]]></programlisting>
+
+ <para>A named SQL query may return a scalar value. You must declare the
+ column alias and Hibernate type using the
+ <literal><return-scalar></literal> element:</para>
+
+ <programlisting><![CDATA[<sql-query name="mySqlQuery">
+ <return-scalar column="name" type="string"/>
+ <return-scalar column="age" type="long"/>
+ SELECT p.NAME AS name,
+ p.AGE AS age,
+ FROM PERSON p WHERE p.NAME LIKE 'Hiber%'
+</sql-query>]]></programlisting>
+
+ <para>You can externalize the resultset mapping informations in a
+ <literal><resultset></literal> element to either reuse them accross
+ several named queries or through the
+ <literal>setResultSetMapping()</literal> API.</para>
+
+ <programlisting><![CDATA[<resultset name="personAddress">
+ <return alias="person" class="eg.Person"/>
+ <return-join alias="address" property="person.mailingAddress"/>
+</resultset>
+
+<sql-query name="personsWith" resultset-ref="personAddress">
+ SELECT person.NAME AS {person.name},
+ person.AGE AS {person.age},
+ person.SEX AS {person.sex},
+ adddress.STREET AS {address.street},
+ adddress.CITY AS {address.city},
+ adddress.STATE AS {address.state},
+ adddress.ZIP AS {address.zip}
+ FROM PERSON person
+ JOIN ADDRESS adddress
+ ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'
+ WHERE person.NAME LIKE :namePattern
+</sql-query>]]></programlisting>
+
+ <para>You can alternatively use the resultset mapping information in your
+ hbm files directly in java code.</para>
+
+ <programlisting><![CDATA[List cats = sess.createSQLQuery(
+ "select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id"
+ )
+ .setResultSetMapping("catAndKitten")
+ .list();]]></programlisting>
+
+ <sect2 id="propertyresults">
+ <title>Using return-property to explicitly specify column/alias
+ names</title>
+
+ <para>With <literal><return-property></literal> you can explicitly
+ tell Hibernate what column aliases to use, instead of using the
+ <literal>{}</literal>-syntax to let Hibernate inject its own
+ aliases.</para>
+
+ <programlisting><![CDATA[<sql-query name="mySqlQuery">
+ <return alias="person" class="eg.Person">
+ <return-property name="name" column="myName"/>
+ <return-property name="age" column="myAge"/>
+ <return-property name="sex" column="mySex"/>
+ </return>
+ SELECT person.NAME AS myName,
+ person.AGE AS myAge,
+ person.SEX AS mySex,
+ FROM PERSON person WHERE person.NAME LIKE :name
+</sql-query>
+]]></programlisting>
+
+ <para><literal><return-property></literal> also works with
+ multiple columns. This solves a limitation with the
+ <literal>{}</literal>-syntax which can not allow fine grained control of
+ multi-column properties.</para>
+
+ <programlisting><![CDATA[<sql-query name="organizationCurrentEmployments">
+ <return alias="emp" class="Employment">
+ <return-property name="salary">
+ <return-column name="VALUE"/>
+ <return-column name="CURRENCY"/>
+ </return-property>
+ <return-property name="endDate" column="myEndDate"/>
+ </return>
+ SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer},
+ STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},
+ REGIONCODE as {emp.regionCode}, EID AS {emp.id}, VALUE, CURRENCY
+ FROM EMPLOYMENT
+ WHERE EMPLOYER = :id AND ENDDATE IS NULL
+ ORDER BY STARTDATE ASC
+</sql-query>]]></programlisting>
+
+ <para>Notice that in this example we used
+ <literal><return-property></literal> in combination with the
+ <literal>{}</literal>-syntax for injection. Allowing users to choose how
+ they want to refer column and properties.</para>
+
+ <para>If your mapping has a discriminator you must use
+ <literal><return-discriminator></literal> to specify the
+ discriminator column.</para>
+ </sect2>
+
+ <sect2 id="sp_query" revision="1">
+ <title>Using stored procedures for querying</title>
+
+ <para>Hibernate 3 introduces support for queries via stored procedures
+ and functions. Most of the following documentation is equivalent for
+ both. The stored procedure/function must return a resultset as the first
+ out-parameter to be able to work with Hibernate. An example of such a
+ stored function in Oracle 9 and higher is as follows:</para>
+
+ <programlisting><![CDATA[CREATE OR REPLACE FUNCTION selectAllEmployments
+ RETURN SYS_REFCURSOR
+AS
+ st_cursor SYS_REFCURSOR;
+BEGIN
+ OPEN st_cursor FOR
+ SELECT EMPLOYEE, EMPLOYER,
+ STARTDATE, ENDDATE,
+ REGIONCODE, EID, VALUE, CURRENCY
+ FROM EMPLOYMENT;
+ RETURN st_cursor;
+ END;]]></programlisting>
+
+ <para>To use this query in Hibernate you need to map it via a named
+ query.</para>
+
+ <programlisting><![CDATA[<sql-query name="selectAllEmployees_SP" callable="true">
+ <return alias="emp" class="Employment">
+ <return-property name="employee" column="EMPLOYEE"/>
+ <return-property name="employer" column="EMPLOYER"/>
+ <return-property name="startDate" column="STARTDATE"/>
+ <return-property name="endDate" column="ENDDATE"/>
+ <return-property name="regionCode" column="REGIONCODE"/>
+ <return-property name="id" column="EID"/>
+ <return-property name="salary">
+ <return-column name="VALUE"/>
+ <return-column name="CURRENCY"/>
+ </return-property>
+ </return>
+ { ? = call selectAllEmployments() }
+</sql-query>]]></programlisting>
+
+ <para>Notice stored procedures currently only return scalars and
+ entities. <literal><return-join></literal> and
+ <literal><load-collection></literal> are not supported.</para>
+
+ <sect3 id="querysql-limits-storedprocedures" revision="1">
+ <title>Rules/limitations for using stored procedures</title>
+
+ <para>To use stored procedures with Hibernate the procedures/functions
+ have to follow some rules. If they do not follow those rules they are
+ not usable with Hibernate. If you still want to use these procedures
+ you have to execute them via <literal>session.connection()</literal>.
+ The rules are different for each database, since database vendors have
+ different stored procedure semantics/syntax.</para>
+
+ <para>Stored procedure queries can't be paged with
+ <literal>setFirstResult()/setMaxResults()</literal>.</para>
+
+ <para>Recommended call form is standard SQL92: <literal>{ ? = call
+ functionName(<parameters>) }</literal> or <literal>{ ? = call
+ procedureName(<parameters>}</literal>. Native call syntax is not
+ supported.</para>
+
+ <para>For Oracle the following rules apply:</para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ A funçãp deve retornar um result set. O primeiro parâmetro da procedure
+ dever ser uma <literal>SAÃDA</literal> que retorne um result set. Isto é feito
+ usando o tipo a <literal>SYS_REFCURSOR</literal> no Oracle 9 ou 10. No Oracle é
+ necessário definir o tipo <literal>REF CURSOR</literal>, veja a documentação do Oracle.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>For Sybase or MS SQL server the following rules apply:</para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ A procedure deve retornar um result set. Veja que este servidor pode retornar
+ múltiplos result sets e update counts. O Hibernate ira iterar os resultados e
+ pegar o primeiro resultado que é o valor de retorno do result set.
+ O resto será descartado.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Se você habilitar <literal>SET NOCOUNT ON</literal> na sua procedure, ela
+ provavelmente será mais eficiente. Mas, isto não é obrigatório</para>
+ </listitem>
+ </itemizedlist>
+ </sect3>
+ </sect2>
+ </sect1>
+
+ <sect1 id="querysql-cud">
+ <title>SQL customizado para create, update e delete</title>
+
+ <para>Hibernate3 can use custom SQL statements for create, update, and
+ delete operations. The class and collection persisters in Hibernate
+ already contain a set of configuration time generated strings (insertsql,
+ deletesql, updatesql etc.). The mapping tags
+ <literal><sql-insert></literal>,
+ <literal><sql-delete></literal>, and
+ <literal><sql-update></literal> override these strings:
+
+ Hibernate3 pode usar SQL customizado para operações de create, update e delete.
+ A persistência de classe e collection no hibernate já contem alguma strings
+ de configurações (insertsql, deletesql, updatesql etc.).
+ O mapaemento das tags
+ <literal><sql-insert></literal>,
+ <literal><sql-delete></literal>, e
+ <literal><sql-update></literal>
+ sobreecreve essas strings:
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true"/>
+ <sql-insert>INSERT INTO PERSON (NAME, ID) VALUES ( UPPER(?), ? )</sql-insert>
+ <sql-update>UPDATE PERSON SET NAME=UPPER(?) WHERE ID=?</sql-update>
+ <sql-delete>DELETE FROM PERSON WHERE ID=?</sql-delete>
+</class>]]></programlisting>
+
+ <para>
+ O SQL é executado diretamente no seu banco de dados, então você pode usar qualquer
+ linguagem que quiser. Isto com certeza reduzira a portabilidade do seu mapeamento se
+ você utilizar um SQL para um banco de dados especifico.
+ </para>
+
+ <para>
+ Stored Procedures são suportadas se o atributo the
+ <literal>callable</literal> estiver ativado:
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true"/>
+ <sql-insert callable="true">{call createPerson (?, ?)}</sql-insert>
+ <sql-delete callable="true">{? = call deletePerson (?)}</sql-delete>
+ <sql-update callable="true">{? = call updatePerson (?, ?)}</sql-update>
+</class>]]></programlisting>
+
+ <para>
+ A ordem de posições dos parâmetros são vitais, pois eles devem estar na mesma seqüência
+ esperada pelo Hibernate.
+ </para>
+
+ <para>Você pode ver a ordem esperada ativando o debug logging no nÃvel
+ <literal>org.hibernate.persister.entity</literal>. Com este nÃvel ativado, o Hibernate
+ irá imprimir o SQL estático que foi usado para create, update, delete, etc.
+ Entidades. (Para ver a seqüência esperada, lembre-se de não incluir seu SQL
+ customizado no arquivo de mapeamento, pois ele irá sobreecreve o SQL
+ estático gerado pelo Hibernate).
+ </para>
+
+ <para>As stored procedures são na maioria dos casos (leia: melhor não fazer) requeridas para
+ retornar o numero de linhas inseridas/atualizadas/deletadas. O hibernate tem algumas
+ verificações em tempo de execução para o sucesso da declaração. Hibernate sempre registra
+ o primeiro parâmetro da declaração como uma saÃda numérica para operações CRUD.
+ </para>
+
+ <programlisting><![CDATA[CREATE OR REPLACE FUNCTION updatePerson (uid IN NUMBER, uname IN VARCHAR2)
+ RETURN NUMBER IS
+BEGIN
+
+ update PERSON
+ set
+ NAME = uname,
+ where
+ ID = uid;
+
+ return SQL%ROWCOUNT;
+
+END updatePerson;]]>
+</programlisting>
+
+ </sect1>
+
+ <sect1 id="querysql-load">
+ <title>SQL customizado para carga</title>
+
+ <para>
+ Você pode declarar sua própria query SQL (ou HQL) para iniciar entidades:
+ </para>
+
+ <programlisting><![CDATA[<sql-query name="person">
+ <return alias="pers" class="Person" lock-mode="upgrade"/>
+ SELECT NAME AS {pers.name}, ID AS {pers.id}
+ FROM PERSON
+ WHERE ID=?
+ FOR UPDATE
+</sql-query>]]></programlisting>
+
+ <para>
+ Este é apenas uma declaração de query com nome, como discutido anteriormente.
+ Você pode referenciar esta query com nome em um mapeamento de classe:
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true"/>
+ <loader query-ref="person"/>
+</class>]]></programlisting>
+
+ <para>Isto também funciona com stored procedures.
+ </para>
+
+ <para>Você pode tembém definir uma query para iniciar collection:
+ </para>
+
+ <programlisting><![CDATA[<set name="employments" inverse="true">
+ <key/>
+ <one-to-many class="Employment"/>
+ <loader query-ref="employments"/>
+</set>]]></programlisting>
+
+ <programlisting><![CDATA[<sql-query name="employments">
+ <load-collection alias="emp" role="Person.employments"/>
+ SELECT {emp.*}
+ FROM EMPLOYMENT emp
+ WHERE EMPLOYER = :id
+ ORDER BY STARTDATE ASC, EMPLOYEE ASC
+</sql-query>]]></programlisting>
+
+ <para>You could even define an entity loader that loads a collection by
+ join fetching:</para>
+
+ <programlisting><![CDATA[<sql-query name="person">
+ <return alias="pers" class="Person"/>
+ <return-join alias="emp" property="pers.employments"/>
+ SELECT NAME AS {pers.*}, {emp.*}
+ FROM PERSON pers
+ LEFT OUTER JOIN EMPLOYMENT emp
+ ON pers.ID = emp.PERSON_ID
+ WHERE ID=?
+</sql-query>]]></programlisting>
+ </sect1>
+</chapter>
\ No newline at end of file
Property changes on: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/query_sql.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/session_api.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/session_api.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/session_api.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1248 @@
+<chapter id="objectstate">
+ <title>Trabalhando com objetos</title>
+
+ <para>
+ O Hibernate é uma solução completa de mapeamento objeto/relacional que não apenas
+ poupa o desenvolvedor dos detalhes de baixo nÃvel do sistema de gerenciamento do
+ banco de dados, mas também oferece um <emphasis>gerenciamento de estado </emphasis>
+ para objetos. Isto é, ao contrário do gerenciamento de <literal>instruções </literal>
+ SQL em camadas de persistência JDBC/SQL comuns, uma visão natural da persistência
+ orientada a objetos em aplicações Java.
+ </para>
+
+ <para>
+ Em outras palavras, desenvolvedores de aplicações Hibernate podem sempre pensar em
+ relação ao <emphasis>estado</emphasis> de seus objetos, e não necessariamente em
+ relação a execução de instruções SQL. Este parte é responsabilidade do Hibernate e
+ é relevante aos desenvolvedores de aplicações apenas quando estão ajustando
+ a performance do sistema.
+ </para>
+
+ <sect1 id="objectstate-overview">
+ <title>Estado dos objetos no Hibernate</title>
+
+ <para>
+ O Hibernate define e suporta os seguintes estados de um objetos:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Transient</emphasis> - um objeto é transiente se ele foi instanciando
+ usando apenas o operador <literal>new</literal>, e não foi associado com uma
+ <literal>Session</literal> do Hibernate. Ele não terá uma representação
+ persistente no banco de dados e nenhum identificador será atribuÃdo para ele.
+ Instâncias transientes serão destruÃdas pelo coletor de lixo se a aplicação
+ não manter sua referência. Use uma <literal>Session</literal> do Hibernate
+ para tornar o objeto persistente ( e deixe o Hibernate gerenciar as
+ instruções SQL que serão necessárias para executar esta transição).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Persistent</emphasis> -â uma instância persistente possui uma
+ representação no banco de dados e um identificador. Ele pode ter sido salvo
+ ou carregado, assim, ele está por definição no escopo de uma
+ <literal>Session</literal>. O Hibernate irá detectar qualquer mudança feita a
+ um objeto persistente e sincronizar o seu estado com o banco de dados quando
+ completar a unidade de trabalho. Desenvolvedores não executam instruções manuais
+ de <literal>UPDATE</literal>, ou instruções de <literal>DELETE</literal>
+ quando o objeto deve ser passado para transiente.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Detached</emphasis> â uma instância desaclopada é um objeto que
+ foi persistido, mas sua <literal>Session</literal> foi fechada. A referência
+ ao objeto continua válida, é claro, e a instância destacada desaclopada pode
+ ser acoplada a uma nova <literal>Session</literal> no futuro, fazendo-o
+ ( e todas as modificações sofridas) persistente novamente. Essa caracterÃstica
+ possibilita um modelo de programação para unidades de trabalho que rodam
+ durante muito tempo que requer um pensamento por tempo do usuário. Podemos
+ chamar-las de <emphasis>transações da aplicação</emphasis>, i.e. uma unidade
+ de trabalho do ponto de vista do usuário.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Agora iremos discutir os estados e suas transições ( e os métodos do Hibernate que
+ disparam uma transição) em mais detalhes.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-makingpersistent" revision="1">
+ <title>Tornando os objetos persistentes</title>
+
+ <para>
+ Instâncias recentemente instanciadas de uma classe persistente são
+ consideradas <emphasis>transientes </emphasis> pelo Hibernate.
+ Podemos tornar uma instância transiente em <emphasis>persistente</emphasis>
+ associando-a a uma sessão:
+ </para>
+
+ <programlisting><![CDATA[DomesticCat fritz = new DomesticCat();
+fritz.setColor(Color.GINGER);
+fritz.setSex('M');
+fritz.setName("Fritz");
+Long generatedId = (Long) sess.save(fritz);]]></programlisting>
+
+ <para>
+ Se <literal>Cat</literal> possui um identificador gerado, o identificador
+ é gerado e atribuÃdo a <literal>cat</literal> quando <literal>save()</literal>
+ for chamada. Se <literal>Cat</literal> possuir um identificador
+ <literal>Associado</literal>, ou uma chave composta, o identificador deve ser
+ atribuÃdo à instância de <literal>cat</literal> antes que <literal>save()</literal>
+ seja chamado. Pode-se usar também <literal>persist()</literal> ao invés de
+ <literal>save()</literal>, com a semântica definada no novo esboço do EJB3.
+ </para>
+
+ <para>
+ Alternativamente, pode-se atribuir o identificador usando uma versão
+ sobrecarregada de <literal>save()</literal>.
+ </para>
+
+<programlisting><![CDATA[DomesticCat pk = new DomesticCat();
+pk.setColor(Color.TABBY);
+pk.setSex('F');
+pk.setName("PK");
+pk.setKittens( new HashSet() );
+pk.addKitten(fritz);
+sess.save( pk, new Long(1234) );]]></programlisting>
+
+ <para>
+ Se o objeto persistido possuir objetos associados (e.g. a coleção
+ <literal>kittens</literal> no exemplo anterior), esses objetos podem ser
+ tornar persistente em qualquer ordem que se queira ao menos que se tenha uma
+ restrição <literal>NOT NULL</literal> em uma coluna de chave estrangeira.
+ Nunca há risco de violação de restrições de chave estrangeira. Assim,
+ pode-se violar uma restrição <literal>NOT NULL</literal> se
+ <literal>save()</literal> for usada nos objetos em uma ordem errada.
+ </para>
+
+ <para>
+ Geralmente você não deve se importar com esses detalhes, muito provavelmente se
+ usará a caracterÃstica de <emphasis>persistência transitiva </emphasis> do Hibernate
+ para salvar os objetos associados automaticamente. Então, enquanto uma restrição
+ <literal>NOT NULL</literal> não ocorrer â Hibernate tomará conta de tudo.
+ Persistência transitiva será discutida futuramente nesse capÃtulo.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-loading">
+ <title>Carregando o objetos</title>
+
+ <para>
+ O método <literal>load()</literal> de uma <literal> Session</literal> nos
+ fornece um meio para recuperar uma instância persistente se o identificador
+ for conhecido. <literal>load()</literal> recebe uma classe do objeto e carregará
+ o estado em uma instância mais recente dessa classe, no estado persistente.
+ </para>
+
+ <programlisting><![CDATA[Cat fritz = (Cat) sess.load(Cat.class, generatedId);]]></programlisting>
+
+<programlisting><![CDATA[// you need to wrap primitive identifiers
+long id = 1234;
+DomesticCat pk = (DomesticCat) sess.load( DomesticCat.class, new Long(id) );]]></programlisting>
+
+ <para>
+ Alternatively, you can load state into a given instance:
+Alternativamente, pode-se carregar um estado em uma instância dada:
+ </para>
+
+<programlisting><![CDATA[Cat cat = new DomesticCat();
+// load pk's state into cat
+sess.load( cat, new Long(pkId) );
+Set kittens = cat.getKittens();]]></programlisting>
+
+ <para>
+ Repare que <literal>load()</literal> irá lançar uma exceção irrecuperável
+ se não houver na tabela no banco de dados um registro que combine.
+ Se a classe for mapeada com um proxy, <literal>load()</literal>
+ simplesmente retorna um proxy não inicializado e realmente não chamará
+ o banco de dados até que um método do proxy seja invocado.
+ Esse comportamento é muito útil se deseja-se criar uma associação
+ com um objeto sem que realmente o carregue do bando de dados.
+ Isto também permite que sejam carregadas múltiplas instâncias como um
+ grupo se <literal>batch-size</literal> estiver para o mapeamento da
+ classe.
+ </para>
+
+ <para>
+ Se você não tiver certeza da existencia do registro no banco, você deve
+ usar o metodo <literal>get()</literal>, que consulta o banco
+ imediantamente e retorna um null se não existir o registro.
+ </para>
+
+ <programlisting><![CDATA[Cat cat = (Cat) sess.get(Cat.class, id);
+if (cat==null) {
+ cat = new Cat();
+ sess.save(cat, id);
+}
+return cat;]]></programlisting>
+
+ <para>
+ Também pode-se carregar um objeto usando <literal>SELECT ... FOR UPDATE</literal>,
+ usando um <literal>LockMode</literal>. Veja a documentação da API para maiores
+ informações.
+ </para>
+
+ <programlisting><![CDATA[Cat cat = (Cat) sess.get(Cat.class, id, LockMode.UPGRADE);]]></programlisting>
+
+ <para>
+ Note that any associated instances or contained collections are
+ <emphasis>not</emphasis> selected <literal>FOR UPDATE</literal>, unless you decide
+ to specify <literal>lock</literal> or <literal>all</literal> as a
+ cascade style for the association.
+ </para>
+
+ <para>
+ O recarregamento de um objeto e todas as suas coleções é possÃvel a qualquer momento,
+ usando o método <literal>refresh()</literal>. Util quando as triggers do banco de
+ dados são usados para inicializar algumas propriedades do objeto.
+ </para>
+
+ <programlisting><![CDATA[sess.save(cat);
+sess.flush(); //force the SQL INSERT
+sess.refresh(cat); //re-read the state (after the trigger executes)]]></programlisting>
+
+ <para>
+ Uma importante questão geralmente aparece neste ponto: O quanto Hibernate carrega
+ do banco de dados e quantos SQL <literal>SELECT</literal> ele irá usar? Isto
+ depende da estratégia de <emphasis>recuperação</emphasis>usada e explicada na
+ <xref linkend="performance-fetching"/>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-querying" revision="1">
+ <title>Consultando</title>
+
+ <para>
+ Se o identificador do objeto que se está buscando não for conhecido,
+ uma consulta será necessária. O Hibernate suporta uma linguagem de consulta
+ (HQL) orientada a objetos fácil mas poderosa. Para criação via programação
+ de consultas, o Hibernate suporta caracterÃsticas sofisticadas de consulta
+ por Critério e Exemplo (QBCe QBE). Pode-se também expressar a consulta
+ por meio de SQL nativa do banco de dados, com suporte opcional do Hibernate
+ para conversão do conjunto de reultados em objetos.
+ </para>
+
+ <sect2 id="objectstate-querying-executing" revision="1">
+ <title>Executando consultas</title>
+
+ <para>
+ Consultas HQL e SQL nativa são representadas por uma instância de <literal>org.hibernate.Query</literal>.
+ Esta interface oferece métodos para associação de parâmetros, tratamento de conjunto de resultados,
+ e para a execução de consultas reais. Você pode obter uma <literal>Query</literal> usando a
+ <literal>Session</literal> atual:
+ </para>
+
+ <programlisting><![CDATA[List cats = session.createQuery(
+ "from Cat as cat where cat.birthdate < ?")
+ .setDate(0, date)
+ .list();
+
+List mothers = session.createQuery(
+ "select mother from Cat as cat join cat.mother as mother where cat.name = ?")
+ .setString(0, name)
+ .list();
+
+List kittens = session.createQuery(
+ "from Cat as cat where cat.mother = ?")
+ .setEntity(0, pk)
+ .list();
+
+Cat mother = (Cat) session.createQuery(
+ "select cat.mother from Cat as cat where cat = ?")
+ .setEntity(0, izi)
+ .uniqueResult();]]
+
+Query mothersWithKittens = (Cat) session.createQuery(
+ "select mother from Cat as mother left join fetch mother.kittens");
+Set uniqueMothers = new HashSet(mothersWithKittens.list());]]></programlisting>
+
+ <para>
+ Geralmente uma consulta é executada ao invocar <literal>list()</literal>,
+ o resultado da consulta será carregado completamente em uma coleção na memória.
+ Instâncias de entidades recuperadas por uma consulta estão no estado persistente.
+ O <literal>uniqueResult()</literal> oferece um atalho se você souber de
+ previamente que a consulta retornará apenas um único objeto. Repare que consultas
+ que fazem uso de buscas de coleções de forma ansiosa (eager) geralmente retornam
+ duplicatas dos objetos raiz ( mas com suas coleções inicializadas ). Pode-se
+ filtrar estas duplicatas através de um simples <literal>Set</literal>.
+ </para>
+
+ <sect3 id="objectstate-querying-executing-iterate">
+ <title>Interagindo com resultados</title>
+
+ <para>
+ Ocasionalmente, deves-se ser capaz de atingir performances melhores com
+ a execução de consultas usando o método <literal>iterate()</literal>.
+ Geralmente isso será o caso esperado apenas se as instâncias dos entidades
+ reais retornadas pela consulta já estiverem na sessão ou no caché de segundo
+ nÃvel. Caso elas ainda não tenham sido armazenadas, <literal>iterate()</literal>
+ será mais devagar do que <literal>list()</literal> e pode ser necessário vários
+ acessos ao banco de dados para um simples consulta, geralmente <emphasis>1</emphasis>
+ para a seleção inicial que retorna apenas identificadores, e <emphasis>n</emphasis>
+ consultas adicionais para inicializar as instâncias reais.
+ </para>
+
+ <programlisting><![CDATA[// fetch ids
+Iterator iter = sess.createQuery("from eg.Qux q order by q.likeliness").iterate();
+while ( iter.hasNext() ) {
+ Qux qux = (Qux) iter.next(); // fetch the object
+ // something we couldnt express in the query
+ if ( qux.calculateComplicatedAlgorithm() ) {
+ // delete the current instance
+ iter.remove();
+ // dont need to process the rest
+ break;
+ }
+}]]></programlisting>
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-tuples">
+ <title>Consultas que retornam tuplas</title>
+
+ <para>
+ Algumas vezes as consultas do Hibernate retornam tuplas de objetos, nesse caso
+ cada tupla é retornada como um array:
+ </para>
+
+ <programlisting><![CDATA[Iterator kittensAndMothers = sess.createQuery(
+ "select kitten, mother from Cat kitten join kitten.mother mother")
+ .list()
+ .iterator();
+
+while ( kittensAndMothers.hasNext() ) {
+ Object[] tuple = (Object[]) kittensAndMothers.next();
+ Cat kitten = tuple[0];
+ Cat mother = tuple[1];
+ ....
+}]]></programlisting>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-scalar" revision="1">
+ <title>Resultados escalares</title>
+
+ <para>
+ Consultas devem especificar uma propriedade da classe na clausula
+ <literal>select</literal>. Elas também podem chamar funções SQL de agregaçãos.
+ Propriedades ou agregações são considerados resultados agregados
+ ( e não entidades no estado persistente).
+ </para>
+
+ <programlisting><![CDATA[Iterator results = sess.createQuery(
+ "select cat.color, min(cat.birthdate), count(cat) from Cat cat " +
+ "group by cat.color")
+ .list()
+ .iterator();
+
+while ( results.hasNext() ) {
+ Object[] row = (Object[]) results.next();
+ Color type = (Color) row[0];
+ Date oldest = (Date) row[1];
+ Integer count = (Integer) row[2];
+ .....
+}]]></programlisting>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-parameters">
+ <title>Bind parameters</title>
+
+ <para>
+ Methods on <literal>Query</literal> are provided for binding values to
+ named parameters or JDBC-style <literal>?</literal> parameters.
+ <emphasis>Contrary to JDBC, Hibernate numbers parameters from zero.</emphasis>
+ Named parameters are identifiers of the form <literal>:name</literal> in
+ the query string. The advantages of named parameters are:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ named parameters are insensitive to the order they occur in the
+ query string
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ they may occur multiple times in the same query
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ they are self-documenting
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <programlisting><![CDATA[//named parameter (preferred)
+Query q = sess.createQuery("from DomesticCat cat where cat.name = :name");
+q.setString("name", "Fritz");
+Iterator cats = q.iterate();]]></programlisting>
+
+ <programlisting><![CDATA[//positional parameter
+Query q = sess.createQuery("from DomesticCat cat where cat.name = ?");
+q.setString(0, "Izi");
+Iterator cats = q.iterate();]]></programlisting>
+
+ <programlisting><![CDATA[//named parameter list
+List names = new ArrayList();
+names.add("Izi");
+names.add("Fritz");
+Query q = sess.createQuery("from DomesticCat cat where cat.name in (:namesList)");
+q.setParameterList("namesList", names);
+List cats = q.list();]]></programlisting>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-pagination">
+ <title>Pagination</title>
+
+ <para>
+ If you need to specify bounds upon your result set (the maximum number of rows
+ you want to retrieve and / or the first row you want to retrieve) you should
+ use methods of the <literal>Query</literal> interface:
+ </para>
+
+ <programlisting><![CDATA[Query q = sess.createQuery("from DomesticCat cat");
+q.setFirstResult(20);
+q.setMaxResults(10);
+List cats = q.list();]]></programlisting>
+
+ <para>
+ Hibernate knows how to translate this limit query into the native
+ SQL of your DBMS.
+ </para>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-scrolling">
+ <title>Scrollable iteration</title>
+
+ <para>
+ If your JDBC driver supports scrollable <literal>ResultSet</literal>s, the
+ <literal>Query</literal> interface may be used to obtain a
+ <literal>ScrollableResults</literal> object, which allows flexible
+ navigation of the query results.
+ </para>
+
+ <programlisting><![CDATA[Query q = sess.createQuery("select cat.name, cat from DomesticCat cat " +
+ "order by cat.name");
+ScrollableResults cats = q.scroll();
+if ( cats.first() ) {
+
+ // find the first name on each page of an alphabetical list of cats by name
+ firstNamesOfPages = new ArrayList();
+ do {
+ String name = cats.getString(0);
+ firstNamesOfPages.add(name);
+ }
+ while ( cats.scroll(PAGE_SIZE) );
+
+ // Now get the first page of cats
+ pageOfCats = new ArrayList();
+ cats.beforeFirst();
+ int i=0;
+ while( ( PAGE_SIZE > i++ ) && cats.next() ) pageOfCats.add( cats.get(1) );
+
+}
+cats.close()]]></programlisting>
+
+ <para>
+ Note that an open database connection (and cursor) is required for this
+ functionality, use <literal>setMaxResult()</literal>/<literal>setFirstResult()</literal>
+ if you need offline pagination functionality.
+ </para>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-named" revision="1">
+ <title>Externalizing named queries</title>
+
+ <para>
+ You may also define named queries in the mapping document. (Remember to use a
+ <literal>CDATA</literal> section if your query contains characters that could
+ be interpreted as markup.)
+ </para>
+
+ <programlisting><![CDATA[<query name="ByNameAndMaximumWeight"><![CDATA[
+ from eg.DomesticCat as cat
+ where cat.name = ?
+ and cat.weight > ?
+] ]></query>]]></programlisting>
+
+ <para>
+ Parameter binding and executing is done programatically:
+ </para>
+
+ <programlisting><![CDATA[Query q = sess.getNamedQuery("ByNameAndMaximumWeight");
+q.setString(0, name);
+q.setInt(1, minWeight);
+List cats = q.list();]]></programlisting>
+
+ <para>
+ Note that the actual program code is independent of the query language that
+ is used, you may also define native SQL queries in metadata, or migrate
+ existing queries to Hibernate by placing them in mapping files.
+ </para>
+
+ <para>
+ Also note that a query declaration inside a <literal><hibernate-mapping></literal>
+ element requires a global unique name for the query, while a query declaration inside a
+ <literal><class></literal> element is made unique automatically by prepending the
+ fully qualified name of the class, for example
+ <literal>eg.Cat.ByNameAndMaximumWeight</literal>.
+ </para>
+
+ </sect3>
+
+ </sect2>
+
+ <sect2 id="objectstate-filtering" revision="1">
+ <title>Filtering collections</title>
+ <para>
+ A collection <emphasis>filter</emphasis> is a special type of query that may be applied to
+ a persistent collection or array. The query string may refer to <literal>this</literal>,
+ meaning the current collection element.
+ </para>
+
+ <programlisting><![CDATA[Collection blackKittens = session.createFilter(
+ pk.getKittens(),
+ "where this.color = ?")
+ .setParameter( Color.BLACK, Hibernate.custom(ColorUserType.class) )
+ .list()
+);]]></programlisting>
+
+ <para>
+ The returned collection is considered a bag, and it's a copy of the given
+ collection. The original collection is not modified (this is contrary to
+ the implication of the name "filter", but consistent with expected behavior).
+ </para>
+
+ <para>
+ Observe that filters do not require a <literal>from</literal> clause (though they may have
+ one if required). Filters are not limited to returning the collection elements themselves.
+ </para>
+
+ <programlisting><![CDATA[Collection blackKittenMates = session.createFilter(
+ pk.getKittens(),
+ "select this.mate where this.color = eg.Color.BLACK.intValue")
+ .list();]]></programlisting>
+
+ <para>
+ Even an empty filter query is useful, e.g. to load a subset of elements in a
+ huge collection:
+ </para>
+
+ <programlisting><![CDATA[Collection tenKittens = session.createFilter(
+ mother.getKittens(), "")
+ .setFirstResult(0).setMaxResults(10)
+ .list();]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="objecstate-querying-criteria" revision="1">
+ <title>Criteria queries</title>
+
+ <para>
+ HQL is extremely powerful but some developers prefer to build queries dynamically,
+ using an object-oriented API, rather than building query strings. Hibernate provides
+ an intuitive <literal>Criteria</literal> query API for these cases:
+ </para>
+
+ <programlisting><![CDATA[Criteria crit = session.createCriteria(Cat.class);
+crit.add( Expression.eq( "color", eg.Color.BLACK ) );
+crit.setMaxResults(10);
+List cats = crit.list();]]></programlisting>
+
+ <para>
+ The <literal>Criteria</literal> and the associated <literal>Example</literal>
+ API are discussed in more detail in <xref linkend="querycriteria"/>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="objectstate-querying-nativesql" revision="2">
+ <title>Queries in native SQL</title>
+
+ <para>
+ You may express a query in SQL, using <literal>createSQLQuery()</literal> and
+ let Hibernate take care of the mapping from result sets to objects. Note
+ that you may at any time call <literal>session.connection()</literal> and
+ use the JDBC <literal>Connection</literal> directly. If you chose to use the
+ Hibernate API, you must enclose SQL aliases in braces:
+ </para>
+
+ <programlisting><![CDATA[List cats = session.createSQLQuery(
+ "SELECT {cat.*} FROM CAT {cat} WHERE ROWNUM<10",
+ "cat",
+ Cat.class
+).list();]]></programlisting>
+
+ <programlisting><![CDATA[List cats = session.createSQLQuery(
+ "SELECT {cat}.ID AS {cat.id}, {cat}.SEX AS {cat.sex}, " +
+ "{cat}.MATE AS {cat.mate}, {cat}.SUBCLASS AS {cat.class}, ... " +
+ "FROM CAT {cat} WHERE ROWNUM<10",
+ "cat",
+ Cat.class
+).list()]]></programlisting>
+
+ <para>
+ SQL queries may contain named and positional parameters, just like Hibernate queries.
+ More information about native SQL queries in Hibernate can be found in
+ <xref linkend="querysql"/>.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="objectstate-modifying" revision="1">
+ <title>Modifying persistent objects</title>
+
+ <para>
+ <emphasis>Transactional persistent instances</emphasis> (ie. objects loaded, saved, created or
+ queried by the <literal>Session</literal>) may be manipulated by the application
+ and any changes to persistent state will be persisted when the <literal>Session</literal>
+ is <emphasis>flushed</emphasis> (discussed later in this chapter). There is no need
+ to call a particular method (like <literal>update()</literal>, which has a different
+ purpose) to make your modifications persistent. So the most straightforward way to update
+ the state of an object is to <literal>load()</literal> it,
+ and then manipulate it directly, while the <literal>Session</literal> is open:
+ </para>
+
+ <programlisting><![CDATA[DomesticCat cat = (DomesticCat) sess.load( Cat.class, new Long(69) );
+cat.setName("PK");
+sess.flush(); // changes to cat are automatically detected and persisted]]></programlisting>
+
+ <para>
+ Sometimes this programming model is inefficient since it would require both an SQL
+ <literal>SELECT</literal> (to load an object) and an SQL <literal>UPDATE</literal>
+ (to persist its updated state) in the same session. Therefore Hibernate offers an
+ alternate approach, using detached instances.
+ </para>
+
+ <para>
+ <emphasis>Note that Hibernate does not offer its own API for direct execution of
+ <literal>UPDATE</literal> or <literal>DELETE</literal> statements. Hibernate is a
+ <emphasis>state management</emphasis> service, you don't have to think in
+ <emphasis>statements</emphasis> to use it. JDBC is a perfect API for executing
+ SQL statements, you can get a JDBC <literal>Connection</literal> at any time
+ by calling <literal>session.connection()</literal>. Furthermore, the notion
+ of mass operations conflicts with object/relational mapping for online
+ transaction processing-oriented applications. Future versions of Hibernate
+ may however provide special mass operation functions. See <xref linkend="batch"/>
+ for some possible batch operation tricks.</emphasis>
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-detached" revision="2">
+ <title>Modifying detached objects</title>
+
+ <para>
+ Many applications need to retrieve an object in one transaction, send it to the
+ UI layer for manipulation, then save the changes in a new transaction.
+ Applications that use this kind of approach in a high-concurrency environment
+ usually use versioned data to ensure isolation for the "long" unit of work.
+ </para>
+
+ <para>
+ Hibernate supports this model by providing for reattachment of detached instances
+ using the <literal>Session.update()</literal> or <literal>Session.merge()</literal>
+ methods:
+ </para>
+
+ <programlisting><![CDATA[// in the first session
+Cat cat = (Cat) firstSession.load(Cat.class, catId);
+Cat potentialMate = new Cat();
+firstSession.save(potentialMate);
+
+// in a higher layer of the application
+cat.setMate(potentialMate);
+
+// later, in a new session
+secondSession.update(cat); // update cat
+secondSession.update(mate); // update mate]]></programlisting>
+
+ <para>
+ If the <literal>Cat</literal> with identifier <literal>catId</literal> had already
+ been loaded by <literal>secondSession</literal> when the application tried to
+ reattach it, an exception would have been thrown.
+ </para>
+
+ <para>
+ Use <literal>update()</literal> if you are sure that the session does
+ not contain an already persistent instance with the same identifier, and
+ <literal>merge()</literal> if you want to merge your modifications at any time
+ without consideration of the state of the session. In other words, <literal>update()</literal>
+ is usually the first method you would call in a fresh session, ensuring that
+ reattachment of your detached instances is the first operation that is executed.
+ </para>
+
+ <para>
+ The application should individually <literal>update()</literal> detached instances
+ reachable from the given detached instance if and <emphasis>only</emphasis> if it wants
+ their state also updated. This can be automated of course, using <emphasis>transitive
+ persistence</emphasis>, see <xref linkend="objectstate-transitive"/>.
+ </para>
+
+ <para>
+ The <literal>lock()</literal> method also allows an application to reassociate
+ an object with a new session. However, the detached instance has to be unmodified!
+ </para>
+
+ <programlisting><![CDATA[//just reassociate:
+sess.lock(fritz, LockMode.NONE);
+//do a version check, then reassociate:
+sess.lock(izi, LockMode.READ);
+//do a version check, using SELECT ... FOR UPDATE, then reassociate:
+sess.lock(pk, LockMode.UPGRADE);]]></programlisting>
+
+ <para>
+ Note that <literal>lock()</literal> can be used with various
+ <literal>LockMode</literal>s, see the API documentation and the
+ chapter on transaction handling for more information. Reattachment is not
+ the only usecase for <literal>lock()</literal>.
+ </para>
+
+ <para>
+ Other models for long units of work are discussed in <xref linkend="transactions-optimistic"/>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-saveorupdate">
+ <title>Automatic state detection</title>
+
+ <para>
+ Hibernate users have requested a general purpose method that either saves a
+ transient instance by generating a new identifier or updates/reattaches
+ the detached instances associated with its current identifier.
+ The <literal>saveOrUpdate()</literal> method implements this functionality.
+ </para>
+
+ <programlisting><![CDATA[// in the first session
+Cat cat = (Cat) firstSession.load(Cat.class, catID);
+
+// in a higher tier of the application
+Cat mate = new Cat();
+cat.setMate(mate);
+
+// later, in a new session
+secondSession.saveOrUpdate(cat); // update existing state (cat has a non-null id)
+secondSession.saveOrUpdate(mate); // save the new instance (mate has a null id)]]></programlisting>
+
+ <para>
+ The usage and semantics of <literal>saveOrUpdate()</literal> seems to be confusing
+ for new users. Firstly, so long as you are not trying to use instances from one session
+ in another new session, you should not need to use <literal>update()</literal>,
+ <literal>saveOrUpdate()</literal>, or <literal>merge()</literal>. Some whole
+ applications will never use either of these methods.
+ </para>
+
+ <para>
+ Usually <literal>update()</literal> or <literal>saveOrUpdate()</literal> are used in
+ the following scenario:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ the application loads an object in the first session
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ the object is passed up to the UI tier
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ some modifications are made to the object
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ the object is passed back down to the business logic tier
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ the application persists these modifications by calling
+ <literal>update()</literal> in a second session
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ <literal>saveOrUpdate()</literal> does the following:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ if the object is already persistent in this session, do nothing
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ if another object associated with the session has the same identifier,
+ throw an exception
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ if the object has no identifier property, <literal>save()</literal> it
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ if the object's identifier has the value assigned to a newly instantiated
+ object, <literal>save()</literal> it
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ if the object is versioned (by a <literal><version></literal> or
+ <literal><timestamp></literal>), and the version property value
+ is the same value assigned to a newly instantiated object,
+ <literal>save()</literal> it
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ otherwise <literal>update()</literal> the object
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ and <literal>merge()</literal> is very different:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ if there is a persistent instance with the same identifier currently
+ associated with the session, copy the state of the given object onto
+ the persistent instance
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ if there is no persistent instance currently associated with the session,
+ try to load it from the database, or create a new persistent instance
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ the persistent instance is returned
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ the given instance does not become associated with the session, it
+ remains detached
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect1>
+
+ <sect1 id="objectstate-deleting" revision="1">
+ <title>Deleting persistent objects</title>
+
+ <para>
+ <literal>Session.delete()</literal> will remove an object's state from the database.
+ Of course, your application might still hold a reference to a deleted object.
+ It's best to think of <literal>delete()</literal> as making a persistent instance
+ transient.
+ </para>
+
+ <programlisting><![CDATA[sess.delete(cat);]]></programlisting>
+
+ <para>
+ You may delete objects in any order you like, without risk of foreign key
+ constraint violations. It is still possible to violate a <literal>NOT
+ NULL</literal> constraint on a foreign key column by deleting objects in
+ the wrong order, e.g. if you delete the parent, but forget to delete the
+ children.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-replicating" revision="1">
+ <title>Replicating object between two different datastores</title>
+
+ <para>
+ It is occasionally useful to be able to take a graph of persistent instances
+ and make them persistent in a different datastore, without regenerating identifier
+ values.
+ </para>
+
+ <programlisting><![CDATA[//retrieve a cat from one database
+Session session1 = factory1.openSession();
+Transaction tx1 = session1.beginTransaction();
+Cat cat = session1.get(Cat.class, catId);
+tx1.commit();
+session1.close();
+
+//reconcile with a second database
+Session session2 = factory2.openSession();
+Transaction tx2 = session2.beginTransaction();
+session2.replicate(cat, ReplicationMode.LATEST_VERSION);
+tx2.commit();
+session2.close();]]></programlisting>
+
+ <para>
+ The <literal>ReplicationMode</literal> determines how <literal>replicate()</literal>
+ will deal with conflicts with existing rows in the database.
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>ReplicationMode.IGNORE</literal> - ignore the object when there is
+ an existing database row with the same identifier
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>ReplicationMode.OVERWRITE</literal> - overwrite any existing database
+ row with the same identifier
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>ReplicationMode.EXCEPTION</literal> - throw an exception if there is
+ an existing database row with the same identifier
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>ReplicationMode.LATEST_VERSION</literal> - overwrite the row if its
+ version number is earlier than the version number of the object, or ignore
+ the object otherwise
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Usecases for this feature include reconciling data entered into different database
+ instances, upgrading system configuration information during product upgrades,
+ rolling back changes made during non-ACID transactions and more.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-flushing">
+ <title>Flushing the Session</title>
+
+ <para>
+ From time to time the <literal>Session</literal> will execute the SQL statements
+ needed to synchronize the JDBC connection's state with the state of objects held in
+ memory. This process, <emphasis>flush</emphasis>, occurs by default at the following
+ points
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ before some query executions
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ from <literal>org.hibernate.Transaction.commit()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ from <literal>Session.flush()</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ The SQL statements are issued in the following order
+ </para>
+
+ <orderedlist spacing="compact">
+ <listitem>
+ <para>
+ all entity insertions, in the same order the corresponding objects
+ were saved using <literal>Session.save()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ all entity updates
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ all collection deletions
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ all collection element deletions, updates and insertions
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ all collection insertions
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ all entity deletions, in the same order the corresponding objects
+ were deleted using <literal>Session.delete()</literal>
+ </para>
+ </listitem>
+ </orderedlist>
+
+ <para>
+ (An exception is that objects using <literal>native</literal> ID generation are
+ inserted when they are saved.)
+ </para>
+
+ <para>
+ Except when you explicity <literal>flush()</literal>, there are absolutely no
+ guarantees about <emphasis>when</emphasis> the <literal>Session</literal> executes
+ the JDBC calls, only the <emphasis>order</emphasis> in which they are executed.
+ However, Hibernate does guarantee that the <literal>Query.list(..)</literal>
+ will never return stale data; nor will they return the wrong data.
+ </para>
+
+ <para>
+ It is possible to change the default behavior so that flush occurs less frequently.
+ The <literal>FlushMode</literal> class defines three different modes: only flush
+ at commit time (and only when the Hibernate <literal>Transaction</literal> API
+ is used), flush automatically using the explained routine, or never flush unless
+ <literal>flush()</literal> is called explicitly. The last mode is useful for long running
+ units of work, where a <literal>Session</literal> is kept open and disconnected for
+ a long time (see <xref linkend="transactions-optimistic-longsession"/>).
+ </para>
+
+ <programlisting><![CDATA[sess = sf.openSession();
+Transaction tx = sess.beginTransaction();
+sess.setFlushMode(FlushMode.COMMIT); // allow queries to return stale state
+
+Cat izi = (Cat) sess.load(Cat.class, id);
+izi.setName(iznizi);
+
+// might return stale data
+sess.find("from Cat as cat left outer join cat.kittens kitten");
+
+// change to izi is not flushed!
+...
+tx.commit(); // flush occurs
+sess.close();]]></programlisting>
+
+ <para>
+ During flush, an exception might occur (e.g. if a DML operation violates a constraint).
+ Since handling exceptions involves some understanding of Hibernate's transactional
+ behavior, we discuss it in <xref linkend="transactions"/>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-transitive" revision="1">
+ <title>Transitive persistence</title>
+
+ <para>
+ It is quite cumbersome to save, delete, or reattach individual objects,
+ especially if you deal with a graph of associated objects. A common case is
+ a parent/child relationship. Consider the following example:
+ </para>
+
+ <para>
+ If the children in a parent/child relationship would be value typed (e.g. a collection
+ of addresses or strings), their lifecycle would depend on the parent and no
+ further action would be required for convenient "cascading" of state changes.
+ When the parent is saved, the value-typed child objects are saved as
+ well, when the parent is deleted, the children will be deleted, etc. This
+ even works for operations such as the removal of a child from the collection;
+ Hibernate will detect this and, since value-typed objects can't have shared
+ references, delete the child from the database.
+ </para>
+
+ <para>
+ Now consider the same scenario with parent and child objects being entities,
+ not value-types (e.g. categories and items, or parent and child cats). Entities
+ have their own lifecycle, support shared references (so removing an entity from
+ the collection does not mean it can be deleted), and there is by default no
+ cascading of state from one entity to any other associated entities. Hibernate
+ does not implement <emphasis>persistence by reachability</emphasis> by default.
+ </para>
+
+ <para>
+ For each basic operation of the Hibernate session - including <literal>persist(), merge(),
+ saveOrUpdate(), delete(), lock(), refresh(), evict(), replicate()</literal> - there is a
+ corresponding cascade style. Respectively, the cascade styles are named <literal>create,
+ merge, save-update, delete, lock, refresh, evict, replicate</literal>. If you want an
+ operation to be cascaded along an association, you must indicate that in the mapping
+ document. For example:
+ </para>
+
+ <programlisting><![CDATA[<one-to-one name="person" cascade="persist"/>]]></programlisting>
+
+ <para>
+ Cascade styles my be combined:
+ </para>
+
+ <programlisting><![CDATA[<one-to-one name="person" cascade="persist,delete,lock"/>]]></programlisting>
+
+ <para>
+ You may even use <literal>cascade="all"</literal> to specify that <emphasis>all</emphasis>
+ operations should be cascaded along the association. The default <literal>cascade="none"</literal>
+ specifies that no operations are to be cascaded.
+ </para>
+
+ <para>
+ A special cascade style, <literal>delete-orphan</literal>, applies only to one-to-many
+ associations, and indicates that the <literal>delete()</literal> operation should
+ be applied to any child object that is removed from the association.
+ </para>
+
+
+ <para>
+ Recommendations:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ It doesn't usually make sense to enable cascade on a <literal><many-to-one></literal>
+ or <literal><many-to-many></literal> association. Cascade is often useful for
+ <literal><one-to-one></literal> and <literal><one-to-many></literal>
+ associations.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ If the child object's lifespan is bounded by the lifespan of the of the parent
+ object make it a <emphasis>lifecycle object</emphasis> by specifying
+ <literal>cascade="all,delete-orphan"</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Otherwise, you might not need cascade at all. But if you think that you will often be
+ working with the parent and children together in the same transaction, and you want to save
+ yourself some typing, consider using <literal>cascade="persist,merge,save-update"</literal>.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Mapping an association (either a single valued association, or a collection) with
+ <literal>cascade="all"</literal> marks the association as a
+ <emphasis>parent/child</emphasis> style relationship where save/update/delete of the
+ parent results in save/update/delete of the child or children.
+ </para>
+ <para>
+ Futhermore, a mere reference to a child from a persistent parent will result in
+ save/update of the child. This metaphor is incomplete, however. A child which becomes
+ unreferenced by its parent is <emphasis>not</emphasis> automatically deleted, except
+ in the case of a <literal><one-to-many></literal> association mapped with
+ <literal>cascade="delete-orphan"</literal>. The precise semantics of cascading
+ operations for a parent/child relationship are as follows:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ If a parent is passed to <literal>persist()</literal>, all children are passed to
+ <literal>persist()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ If a parent is passed to <literal>merge()</literal>, all children are passed to
+ <literal>merge()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ If a parent is passed to <literal>save()</literal>, <literal>update()</literal> or
+ <literal>saveOrUpdate()</literal>, all children are passed to <literal>saveOrUpdate()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ If a transient or detached child becomes referenced by a persistent parent,
+ it is passed to <literal>saveOrUpdate()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ If a parent is deleted, all children are passed to <literal>delete()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ If a child is dereferenced by a persistent parent, <emphasis>nothing
+ special happens</emphasis> - the application should explicitly delete
+ the child if necessary - unless <literal>cascade="delete-orphan"</literal>,
+ in which case the "orphaned" child is deleted.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Finally, note that cascading of operations can be applied to an object graph at
+ <emphasis>call time</emphasis> or at <emphasis>flush time</emphasis>. All operations,
+ if enabled, are cascaded to associated entities reachable when the operation is
+ executed. However, <literal>save-upate</literal> and <literal>delete-orphan</literal>
+ are transitive for all associated entities reachable during flush of the
+ <literal>Session</literal>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-metadata">
+ <title>Usando metadados</title>
+
+ <para>
+ O Hibernate requer um modelo muito rico a nÃvel de metadados de todas as entidades e tipos de
+ valores. De tempos em tempos, este modelo é muito útil à própria aplicação. Por exemplo, a
+ aplicação pode usar o metadados do Hibernate que executa um algoritmo "inteligente" que
+ compreende quais objetos podem ser copiados (por exemplo, tipos de valores mutáveis) ou
+ não (por exemplo, tipos de valores imutáveis e, possivelmente, entidades associadas).
+ </para>
+ <para>
+ O Hibernate expõe o metadados via interfaces <literal>ClassMetadata</literal>
+ e <literal>CollectionMetadata</literal> e pela hierarquia <literal>Type</literal>.
+ Instâncias das interfaces de metadados podem ser obtidas a partir do
+ <literal>SessionFactory</literal>.
+ </para>
+
+ <programlisting><![CDATA[Cat fritz = ......;
+ClassMetadata catMeta = sessionfactory.getClassMetadata(Cat.class);
+
+Object[] propertyValues = catMeta.getPropertyValues(fritz);
+String[] propertyNames = catMeta.getPropertyNames();
+Type[] propertyTypes = catMeta.getPropertyTypes();
+
+// get a Map of all properties which are not collections or associations
+Map namedValues = new HashMap();
+for ( int i=0; i<propertyNames.length; i++ ) {
+ if ( !propertyTypes[i].isEntityType() && !propertyTypes[i].isCollectionType() ) {
+ namedValues.put( propertyNames[i], propertyValues[i] );
+ }
+}]]></programlisting>
+
+ </sect1>
+
+</chapter>
+
Property changes on: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/session_api.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/toolset_guide.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/toolset_guide.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/toolset_guide.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,604 @@
+<chapter id="toolsetguide" revision="2">
+ <title>Toolset Guide</title>
+
+ <para>
+ Roundtrip engineering with Hibernate is possible using a set of Eclipse plugins,
+ commandline tools, as well as Ant tasks.
+ </para>
+
+ <para>
+ The <emphasis>Hibernate Tools</emphasis> currently include plugins for the Eclipse
+ IDE as well as Ant tasks for reverse engineering of existing databases:
+ </para>
+
+ <itemizedlist>
+ <listitem><para>
+ <emphasis>Mapping Editor:</emphasis> An editor for Hibernate XML mapping files,
+ supporting auto-completion and syntax highlighting. It also supports semantic
+ auto-completion for class names and property/field names, making it much more versatile than a normal XML editor.
+ </para></listitem>
+ <listitem><para>
+ <emphasis>Console:</emphasis> The console is a new view in Eclipse. In addition to
+ a tree overview of your console configurations, you also get an interactive view
+ of your persistent classes and their relationships. The console allows you to
+ execute HQL queries against your database and browse the result directly in
+ Eclipse.
+ </para></listitem>
+ <listitem><para>
+ <emphasis>Development Wizards:</emphasis> Several wizards are provided with the
+ Hibernate Eclipse tools; you can use a wizard to quickly generate Hibernate configuration
+ (cfg.xml) files, or you may even completely reverse engineer an existing database schema
+ into POJO source files and Hibernate mapping files. The reverse engineering wizard
+ supports customizable templates.
+ </para></listitem>
+ <listitem><para>
+ <emphasis>Ant Tasks:</emphasis>
+ </para></listitem>
+
+ </itemizedlist>
+
+ <para>
+ Please refer to the <emphasis>Hibernate Tools</emphasis> package and it's documentation
+ for more information.
+ </para>
+
+ <para>
+ However, the Hibernate main package comes bundled with an integrated tool (it can even
+ be used from "inside" Hibernate on-the-fly): <emphasis>SchemaExport</emphasis> aka
+ <literal>hbm2ddl</literal>.
+ </para>
+
+ <sect1 id="toolsetguide-s1" revision="2">
+ <title>Automatic schema generation</title>
+
+ <para>
+ DDL may be generated from your mapping files by a Hibernate utility. The generated
+ schema includes referential integrity constraints (primary and foreign keys) for
+ entity and collection tables. Tables and sequences are also created for mapped
+ identifier generators.
+ </para>
+
+ <para>
+ You <emphasis>must</emphasis> specify a SQL <literal>Dialect</literal> via the
+ <literal>hibernate.dialect</literal> property when using this tool, as DDL
+ is highly vendor specific.
+ </para>
+
+ <para>
+ First, customize your mapping files to improve the generated schema.
+ </para>
+
+ <sect2 id="toolsetguide-s1-2" revision="3">
+ <title>Customizing the schema</title>
+
+ <para>
+ Many Hibernate mapping elements define optional attributes named <literal>length</literal>,
+ <literal>precision</literal> and <literal>scale</literal>. You may set the length, precision
+ and scale of a column with this attribute.
+
+ </para>
+
+ <programlisting><![CDATA[<property name="zip" length="5"/>]]></programlisting>
+ <programlisting><![CDATA[<property name="balance" precision="12" scale="2"/>]]></programlisting>
+
+ <para>
+ Some tags also accept a <literal>not-null</literal> attribute (for generating a
+ <literal>NOT NULL</literal> constraint on table columns) and a <literal>unique</literal>
+ attribute (for generating <literal>UNIQUE</literal> constraint on table columns).
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="bar" column="barId" not-null="true"/>]]></programlisting>
+
+ <programlisting><![CDATA[<element column="serialNumber" type="long" not-null="true" unique="true"/>]]></programlisting>
+
+ <para>
+ A <literal>unique-key</literal> attribute may be used to group columns in
+ a single unique key constraint. Currently, the specified value of the
+ <literal>unique-key</literal> attribute is <emphasis>not</emphasis> used
+ to name the constraint in the generated DDL, only to group the columns in
+ the mapping file.
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="org" column="orgId" unique-key="OrgEmployeeId"/>
+<property name="employeeId" unique-key="OrgEmployee"/>]]></programlisting>
+
+ <para>
+ An <literal>index</literal> attribute specifies the name of an index that
+ will be created using the mapped column or columns. Multiple columns may be
+ grouped into the same index, simply by specifying the same index name.
+ </para>
+
+ <programlisting><![CDATA[<property name="lastName" index="CustName"/>
+<property name="firstName" index="CustName"/>]]></programlisting>
+
+ <para>
+ A <literal>foreign-key</literal> attribute may be used to override the name
+ of any generated foreign key constraint.
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="bar" column="barId" foreign-key="FKFooBar"/>]]></programlisting>
+
+ <para>
+ Many mapping elements also accept a child <literal><column></literal> element.
+ This is particularly useful for mapping multi-column types:
+ </para>
+
+ <programlisting><![CDATA[<property name="name" type="my.customtypes.Name"/>
+ <column name="last" not-null="true" index="bar_idx" length="30"/>
+ <column name="first" not-null="true" index="bar_idx" length="20"/>
+ <column name="initial"/>
+</property>]]></programlisting>
+
+ <para>
+ The <literal>default</literal> attribute lets you specify a default value for
+ a column (you should assign the same value to the mapped property before
+ saving a new instance of the mapped class).
+ </para>
+
+ <programlisting><![CDATA[<property name="credits" type="integer" insert="false">
+ <column name="credits" default="10"/>
+</property>]]></programlisting>
+
+ <programlisting><![CDATA[<version name="version" type="integer" insert="false">
+ <column name="version" default="0"/>
+</property>]]></programlisting>
+
+ <para>
+ The <literal>sql-type</literal> attribute allows the user to override the default
+ mapping of a Hibernate type to SQL datatype.
+ </para>
+
+ <programlisting><![CDATA[<property name="balance" type="float">
+ <column name="balance" sql-type="decimal(13,3)"/>
+</property>]]></programlisting>
+
+ <para>
+ The <literal>check</literal> attribute allows you to specify a check constraint.
+ </para>
+
+ <programlisting><![CDATA[<property name="foo" type="integer">
+ <column name="foo" check="foo > 10"/>
+</property>]]></programlisting>
+
+ <programlisting><![CDATA[<class name="Foo" table="foos" check="bar < 100.0">
+ ...
+ <property name="bar" type="float"/>
+</class>]]></programlisting>
+
+
+ <table frame="topbot" id="schemattributes-summary" revision="2">
+ <title>Summary</title>
+ <tgroup cols="3">
+ <colspec colwidth="1*"/>
+ <colspec colwidth="1*"/>
+ <colspec colwidth="2.5*"/>
+ <thead>
+ <row>
+ <entry>Attribute</entry>
+ <entry>Values</entry>
+ <entry>Interpretation</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>length</literal></entry>
+ <entry>number</entry>
+ <entry>column length</entry>
+ </row>
+ <row>
+ <entry><literal>precision</literal></entry>
+ <entry>number</entry>
+ <entry>column decimal precision</entry>
+ </row>
+ <row>
+ <entry><literal>scale</literal></entry>
+ <entry>number</entry>
+ <entry>column decimal scale</entry>
+ </row>
+ <row>
+ <entry><literal>not-null</literal></entry>
+ <entry><literal>true|false</literal></entry>
+ <entry>specfies that the column should be non-nullable</entry>
+ </row>
+ <row>
+ <entry><literal>unique</literal></entry>
+ <entry><literal>true|false</literal></entry>
+ <entry>specifies that the column should have a unique constraint</entry>
+ </row>
+ <row>
+ <entry><literal>index</literal></entry>
+ <entry><literal>index_name</literal></entry>
+ <entry>specifies the name of a (multi-column) index</entry>
+ </row>
+ <row>
+ <entry><literal>unique-key</literal></entry>
+ <entry><literal>unique_key_name</literal></entry>
+ <entry>specifies the name of a multi-column unique constraint</entry>
+ </row>
+ <row>
+ <entry><literal>foreign-key</literal></entry>
+ <entry><literal>foreign_key_name</literal></entry>
+ <entry>
+ specifies the name of the foreign key constraint generated
+ for an association, for a <literal><one-to-one></literal>,
+ <literal><many-to-one></literal>, <literal><key></literal>,
+ or <literal><many-to-many></literal> mapping element. Note that
+ <literal>inverse="true"</literal> sides will not be considered
+ by <literal>SchemaExport</literal>.
+ </entry>
+ </row>
+ <row>
+ <entry><literal>sql-type</literal></entry>
+ <entry><literal>SQL column type</literal></entry>
+ <entry>
+ overrides the default column type (attribute of
+ <literal><column></literal> element only)
+ </entry>
+ </row>
+ <row>
+ <entry><literal>default</literal></entry>
+ <entry>SQL expression</entry>
+ <entry>
+ specify a default value for the column
+ </entry>
+ </row>
+ <row>
+ <entry><literal>check</literal></entry>
+ <entry>SQL expression</entry>
+ <entry>
+ create an SQL check constraint on either column or table
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ The <literal><comment></literal> element allows you to specify comments
+ for the generated schema.
+ </para>
+
+ <programlisting><![CDATA[<class name="Customer" table="CurCust">
+ <comment>Current customers only</comment>
+ ...
+</class>]]></programlisting>
+
+ <programlisting><![CDATA[<property name="balance">
+ <column name="bal">
+ <comment>Balance in USD</comment>
+ </column>
+</property>]]></programlisting>
+
+ <para>
+ This results in a <literal>comment on table</literal> or
+ <literal>comment on column</literal> statement in the generated
+ DDL (where supported).
+ </para>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-3" revision="2">
+ <title>Running the tool</title>
+
+ <para>
+ The <literal>SchemaExport</literal> tool writes a DDL script to standard out and/or
+ executes the DDL statements.
+ </para>
+
+ <para>
+ <literal>java -cp </literal><emphasis>hibernate_classpaths</emphasis>
+ <literal>org.hibernate.tool.hbm2ddl.SchemaExport</literal> <emphasis>options mapping_files</emphasis>
+ </para>
+
+ <table frame="topbot">
+ <title><literal>SchemaExport</literal> Command Line Options</title>
+ <tgroup cols="2">
+ <colspec colwidth="1.5*"/>
+ <colspec colwidth="2*"/>
+ <thead>
+ <row>
+ <entry>Option</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>--quiet</literal></entry>
+ <entry>don't output the script to stdout</entry>
+ </row>
+ <row>
+ <entry><literal>--drop</literal></entry>
+ <entry>only drop the tables</entry>
+ </row>
+ <row>
+ <entry><literal>--create</literal></entry>
+ <entry>only create the tables</entry>
+ </row>
+ <row>
+ <entry><literal>--text</literal></entry>
+ <entry>don't export to the database</entry>
+ </row>
+ <row>
+ <entry><literal>--output=my_schema.ddl</literal></entry>
+ <entry>output the ddl script to a file</entry>
+ </row>
+ <row>
+ <entry><literal>--naming=eg.MyNamingStrategy</literal></entry>
+ <entry>select a <literal>NamingStrategy</literal></entry>
+ </row>
+ <row>
+ <entry><literal>--config=hibernate.cfg.xml</literal></entry>
+ <entry>read Hibernate configuration from an XML file</entry>
+ </row>
+ <row>
+ <entry><literal>--properties=hibernate.properties</literal></entry>
+ <entry>read database properties from a file</entry>
+ </row>
+ <row>
+ <entry><literal>--format</literal></entry>
+ <entry>format the generated SQL nicely in the script</entry>
+ </row>
+ <row>
+ <entry><literal>--delimiter=;</literal></entry>
+ <entry>set an end of line delimiter for the script</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ You may even embed <literal>SchemaExport</literal> in your application:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = ....;
+new SchemaExport(cfg).create(false, true);]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-4">
+ <title>Properties</title>
+
+ <para>
+ Database properties may be specified
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>as system properties with <literal>-D</literal><emphasis><property></emphasis></para>
+ </listitem>
+ <listitem>
+ <para>in <literal>hibernate.properties</literal></para>
+ </listitem>
+ <listitem>
+ <para>in a named properties file with <literal>--properties</literal></para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ The needed properties are:
+ </para>
+
+ <table frame="topbot">
+ <title>SchemaExport Connection Properties</title>
+ <tgroup cols="2">
+ <colspec colwidth="1.5*"/>
+ <colspec colwidth="2*"/>
+ <thead>
+ <row>
+ <entry>Property Name</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>hibernate.connection.driver_class</literal></entry>
+ <entry>jdbc driver class</entry>
+ </row>
+ <row>
+ <entry><literal>hibernate.connection.url</literal></entry>
+ <entry>jdbc url</entry>
+ </row>
+ <row>
+ <entry><literal>hibernate.connection.username</literal></entry>
+ <entry>database user</entry>
+ </row>
+ <row>
+ <entry><literal>hibernate.connection.password</literal></entry>
+ <entry>user password</entry>
+ </row>
+ <row>
+ <entry><literal>hibernate.dialect</literal></entry>
+ <entry>dialect</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-5">
+ <title>Using Ant</title>
+
+ <para>
+ You can call <literal>SchemaExport</literal> from your Ant build script:
+ </para>
+
+ <programlisting><![CDATA[<target name="schemaexport">
+ <taskdef name="schemaexport"
+ classname="org.hibernate.tool.hbm2ddl.SchemaExportTask"
+ classpathref="class.path"/>
+
+ <schemaexport
+ properties="hibernate.properties"
+ quiet="no"
+ text="no"
+ drop="no"
+ delimiter=";"
+ output="schema-export.sql">
+ <fileset dir="src">
+ <include name="**/*.hbm.xml"/>
+ </fileset>
+ </schemaexport>
+</target>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-6" revision="2">
+ <title>Incremental schema updates</title>
+
+ <para>
+ The <literal>SchemaUpdate</literal> tool will update an existing schema with "incremental" changes.
+ Note that <literal>SchemaUpdate</literal> depends heavily upon the JDBC metadata API, so it will
+ not work with all JDBC drivers.
+ </para>
+
+ <para>
+ <literal>java -cp </literal><emphasis>hibernate_classpaths</emphasis>
+ <literal>org.hibernate.tool.hbm2ddl.SchemaUpdate</literal> <emphasis>options mapping_files</emphasis>
+ </para>
+
+ <table frame="topbot">
+ <title><literal>SchemaUpdate</literal> Command Line Options</title>
+ <tgroup cols="2">
+ <colspec colwidth="1.5*"/>
+ <colspec colwidth="2*"/>
+ <thead>
+ <row>
+ <entry>Option</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>--quiet</literal></entry>
+ <entry>don't output the script to stdout</entry>
+ </row>
+ <row>
+ <entry><literal>--text</literal></entry>
+ <entry>don't export the script to the database</entry>
+ </row>
+ <row>
+ <entry><literal>--naming=eg.MyNamingStrategy</literal></entry>
+ <entry>select a <literal>NamingStrategy</literal></entry>
+ </row>
+ <row>
+ <entry><literal>--properties=hibernate.properties</literal></entry>
+ <entry>read database properties from a file</entry>
+ </row>
+ <row>
+ <entry><literal>--config=hibernate.cfg.xml</literal></entry>
+ <entry>specify a <literal>.cfg.xml</literal> file</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ You may embed <literal>SchemaUpdate</literal> in your application:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = ....;
+new SchemaUpdate(cfg).execute(false);]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-7">
+ <title>Using Ant for incremental schema updates</title>
+
+ <para>
+ You can call <literal>SchemaUpdate</literal> from the Ant script:
+ </para>
+
+ <programlisting><![CDATA[<target name="schemaupdate">
+ <taskdef name="schemaupdate"
+ classname="org.hibernate.tool.hbm2ddl.SchemaUpdateTask"
+ classpathref="class.path"/>
+
+ <schemaupdate
+ properties="hibernate.properties"
+ quiet="no">
+ <fileset dir="src">
+ <include name="**/*.hbm.xml"/>
+ </fileset>
+ </schemaupdate>
+</target>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-8" revision="1">
+ <title>Schema validation</title>
+
+ <para>
+ The <literal>SchemaValidator</literal> tool will validate that the existing database schema "matches"
+ your mapping documents. Note that <literal>SchemaValidator</literal> depends heavily upon the JDBC
+ metadata API, so it will not work with all JDBC drivers. This tool is extremely useful for testing.
+ </para>
+
+ <para>
+ <literal>java -cp </literal><emphasis>hibernate_classpaths</emphasis>
+ <literal>org.hibernate.tool.hbm2ddl.SchemaValidator</literal> <emphasis>options mapping_files</emphasis>
+ </para>
+
+ <table frame="topbot">
+ <title><literal>SchemaValidator</literal> Command Line Options</title>
+ <tgroup cols="2">
+ <colspec colwidth="1.5*"/>
+ <colspec colwidth="2*"/>
+ <thead>
+ <row>
+ <entry>Option</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>--naming=eg.MyNamingStrategy</literal></entry>
+ <entry>select a <literal>NamingStrategy</literal></entry>
+ </row>
+ <row>
+ <entry><literal>--properties=hibernate.properties</literal></entry>
+ <entry>read database properties from a file</entry>
+ </row>
+ <row>
+ <entry><literal>--config=hibernate.cfg.xml</literal></entry>
+ <entry>specify a <literal>.cfg.xml</literal> file</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ You may embed <literal>SchemaValidator</literal> in your application:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = ....;
+new SchemaValidator(cfg).validate();]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-9">
+ <title>Using Ant for schema validation</title>
+
+ <para>
+ You can call <literal>SchemaValidator</literal> from the Ant script:
+ </para>
+
+ <programlisting><![CDATA[<target name="schemavalidate">
+ <taskdef name="schemavalidator"
+ classname="org.hibernate.tool.hbm2ddl.SchemaValidatorTask"
+ classpathref="class.path"/>
+
+ <schemavalidator
+ properties="hibernate.properties">
+ <fileset dir="src">
+ <include name="**/*.hbm.xml"/>
+ </fileset>
+ </schemaupdate>
+</target>]]></programlisting>
+
+ </sect2>
+
+ </sect1>
+
+</chapter>
+
Property changes on: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/toolset_guide.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/transactions.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/transactions.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/transactions.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1147 @@
+<chapter id="transactions" revision="2">
+ <title>Transações e Concorrência</title>
+
+ <para>
+ O ponto o mais importante sobre o Hibernate e o controle de concorrência é que é muito
+ fácil de ser compreendido. O Hibernate usa diretamente conexões de JDBC e recursos de
+ JTA sem adicionar nenhum comportamento de bloqueio a mais. Nós altamente recomendamos
+ que você gaste algum tempo com o JDBC, o ANSI e a especificação de isolamento de transação
+ de seu sistema de gerência da base de dados.
+ </para>
+
+ <para>
+ O Hibernate não bloqueia objetos na memória. Sua aplicação pode esperar o comportamento
+ tal qual definido pelo nÃvel de isolamento de suas transações de banco de dados.
+ Note que graças ao <literal>Session</literal>, que também é um cache de escopo de
+ transação, o Hibernate fornece leituras repetÃveis para procurar por identificadores
+ e consultas de entidade (não pesquisas de relatórios que retornam valores escalares).
+ </para>
+
+ <para>
+ Além do versionamento para o controle automático de concorrência otimista, o Hibernate
+ oferece também uma API (menor) para bloqueio pessimista de linhas usando a sintaxe
+ <literal>SELECT FOR UPDATE</literal>. O controle de concorrência otimista e esta
+ API são discutidos mais tarde neste capÃtulo.
+ </para>
+
+ <para>
+ Nós começamos a discussão do controle de concorrência no Hibernate com a granularidade
+ do <literal>Configuration</literal>, <literal>SessionFactory</literal>, e
+ <literal>Session</literal>, além de transações de base de dados e conversações longas.
+ </para>
+
+ <sect1 id="transactions-basics" revision="1">
+ <title>Session e escopos de transações</title>
+
+ <para>
+ Um <literal>SessionFactory</literal> é objeto threadsafe compartilhado por
+ todas as threads da aplicação que consome muitos recursos na sua criação.
+ à criado uma unica vez no inicio da execução da aplicação a partir da
+ instância de uma <literal>Configuration</literal>.
+ </para>
+
+ <para>
+ Uma <literal>Session</literal> é um objeto de baixo custo de criação, não é threadsafe,
+ deve ser usado uma vez, para uma única requisição, uma conversação, uma única unidade
+ do trabalho e então deve ser descartado. Um <literal>Session</literal> não obterá um
+ JDBC <literal>Connection</literal> (ou um <literal>Datasource</literal>) a menos que
+ necessite, conseqüentemente não consome nenhum recurso até ser usado.
+ </para>
+
+ <para>
+ Para completar, você também tem que pensar sobre as transações de base de dados.
+ Uma transação tem que ser tão curta quanto possÃvel, para reduzir a disputa pelo
+ bloqueio na base de dados. Transações longas impedirão que sua aplicação escale a
+ carga altamente concorrente. Por isso, em um projeto raramente é para manter
+ uma transação de base de dados aberta durante o tempo que o usuário pensa,
+ até que a unidade do trabalho esteja completa.
+ </para>
+
+ <para>
+ Qual é o escopo de uma unidade de trabalho? Pode uma únicoa <literal>Session</literal>
+ do Hibernate gerenciar diversas transações ou é esta um o relacionamento um-para-um dos
+ escopos? Quando deve você abrir e fechar uma <literal>Session</literal> e como você
+ demarca os limites da transação?
+ </para>
+
+ <sect2 id="transactions-basics-uow" revision="1">
+ <title>Unidade de trabalho</title>
+
+ <para>
+ Primeiro, não use o antipattern <emphasis>sessão-por-operação</emphasis>,
+ isto é, não abra e não feche uma <literal>Session</literal> para cada simples chamada
+ ao banco de de dados em uma única thread! Naturalmente, o mesmo é verdadeiro para
+ transações. As chamadas a banco de dados em uma aplicação são feitas usando uma
+ seqüência planejada, elas são agrupadas em unidades de trabalho atômicas.
+ (Veja que isso também significa que um auto-commit depois de cada sentença SQL é
+ inútil em uma aplicação, esta modalidade é ideal para o trabalho ad hoc do console
+ do SQL. O Hibernate impede, ou espera que o servidor de aplicação impessa isso,
+ o uso da modalidade de auto-commit.) As transações nunca são opcionais, toda a
+ comunicação com um banco de dados tem que ocorrer dentro de uma transação, não
+ importa se você vai ler ou escrever dados. Como explicado, o comportamento auto-commit
+ para leitura de dados deve ser evitado, como muitas transações pequenas são
+ improváveis de executar melhor do que uma unidade claramente definida do trabalho. A
+ última opção também muito mais manutenÃvel e extensÃvel.
+ </para>
+
+ <para>
+ O pattern mais comum em uma aplicação multi-usuário cliente/servidor é
+ <emphasis>sessão-por-requisição</emphasis>. Neste modelo, uma requisição do cliente é
+ enviada ao servidor (onde a camada de persistência do Hibernate roda), uma
+ <literal>Session</literal> nova do Hibernate é aberta, e todas as operações da base de
+ dados são executadas nesta unidade do trabalho. Logo que o trabalho for completado
+ (e a resposta para o cliente for preparada), a sessão é descarregad e fechada.
+ Você usaria também uma única transação de base de dados para servir às requisições
+ dos clientes, começando e commitando-o quando você abre e fecha a <literal>Session</literal>.
+ O relacionamento entre os dois é um-para-um e este modelo é um ajuste perfeito para muitas
+ aplicações.
+ </para>
+
+ <para>
+ O desafio encontra-se na implementação. O Hibernate fornece gerência integrada da "sessão atual"
+ para simplificar este pattern. Tudo que você tem que fazer é iniciar uma transação quando uma
+ requisição tem que ser processada e termina a transação antes que a resposta seja enviada ao
+ cliente. Você pode fazer onde quiser, soluções comuns são <literal>ServletFilter</literal>,
+ interceptador AOP com um pointcut (ponto de corte) nos métodos de serviço ou em um
+ container de proxy/interceptação. Um container de EJB é uma maneira padronizada para
+ implementar aspectos cross-cutting tais como a demarcação da transação em EJB session beans,
+ declarativamente com CMT. Se você se decidir usar demarcação programática de transação,
+ de preferencia a API <literal>Transaction</literal> do Hibernate mostrada mais adiante neste
+ capÃtulo, para fácilidade no uso e portabilidade de código.
+ </para>
+
+ <para>
+ Seu código de aplicação pode acessar a "sessão atual" para processar a requisição
+ fazendo uma chamada simples a <literal>sessionFactory.getCurrentSession()</literal> em
+ qualquer lugar e com a frequencia necessária. Você sempre conseguirá uma
+ <literal>Session</literal> limitada a transação atual. Isto tem que ser configurado
+ para recurso local ou os ambientes JTA. Veja <xref linkend="architecture-current-session"/>.
+ </para>
+
+ <para>
+ Ãs vezes é conveniente estender o escopo de uma <literal>Session</literal> e de
+ uma transação do banco de dados até que a "visão esteja renderizada". à especialmente
+ útil em aplicações servlet que utilizam uma fase de rendenderização separada depois
+ que a requisição ter sido processada. Estendendo a transação até que renderização da
+ visão esteja completa é fácil de fazer se você implementar seu próprio interceptador.
+ Entretanto, não se pode fazer facilmente se você confiar em EJBs com transações
+ gerenciadas por contêiner, porque uma transação será terminada quando um método de
+ EJB retornar, antes da renderização de toda visão puder começar.
+ Veja o website e o fórum do Hibernate para dicas e exemplos em torno deste
+ pattern <emphasis>Open Session in View</emphasis>.
+
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-basics-apptx" revision="1">
+ <title>Longas conversações</title>
+
+ <para>
+ O pattern sessão-por-requisição não é o único conceito útil que você pode usar ao projetar
+ unidades de trabalho. Muitos processos de negócio requerem uma totalidade de séries de
+ interações com o usuário intercaladas com acessos a uma base de dados. Em aplicações web
+ e corporativas não é aceitável para uma transação atrapalhe uma interação do usuário.
+ Considere o seguinte exemplo:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ A primeira tela de um diálogo abre os dados carregado pelo usuário em através de
+ <literal>Session</literal> e transação particulares. O usuário está livre
+ modificar os objetos.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ O usuário clica em "Salvar" após 5 minutos e espera suas modificações serem persistidas;
+ espera também que ele era a única pessoa que edita esta informação e que nenhuma
+ modificação conflitante possa ocorrer.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Nós chamamos esta unidade de trabalho, do ponto da visão do usuário, executando uma
+ longa <emphasis>conversação</emphasis> (ou <emphasis>transação da aplicação</emphasis>).
+ Há muitas maneiras de você pode implementar em sua aplicação.
+
+ </para>
+
+ <para>
+ Uma primeira implementação simples pode manter a<literal>Session</literal> e a transação
+ aberta durante o tempo de interação do usuário, com bloqueios na base de dados para impedir
+ a modificação concorrente e para garantir o isolamento e a atomicidade. Esse é naturalmente
+ um anti-pattern, desde que a disputa do bloqueio não permitiria o escalonameneto da
+ aplicação com o número de usuários concorrentes.
+ </para>
+
+ <para>
+ Claramente, nós temos que usar diversas transações para implementar a conversação.
+ Neste caso, Manter o isolamento dos processos de negócio torna-se responsabilidade
+ parcial da camada da aplicação. Uma única conversação geralmente usa diversas transações.
+ Ela será atômica se somente uma destas transações (a última) armazenar os
+ dados atualizados, todas as outras simplesmente leram os dados (por exemplo em um
+ diálogo do estilo wizard que mede diversos ciclos de requisição/resposta). Isto é mais
+ fácil de implementar do que pode parecer, especialmente se você usar as
+ caracterÃsticas do Hibernate:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Versionamento automático</emphasis> - O Hibernate pode fazer o
+ controle automático de concorrência otimista para você, ele pode
+ automaticamente detectar se uma modificação concorrente
+ ocorreu durante o tempo de interação do usuário. Geralmente nós verificamos
+ somente no fim da conversação.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Detached Objects</emphasis>- se você se decidir usar o já discutido
+ pattern <emphasis>session-per-request</emphasis>, todas as instâncias carregadas
+ estarão no estado destacado durante o tempo em que o usuário estiver pensando.
+ O Hibernate permite que você reatache os objetos e persita as modificações,
+ esse pattern é chamado
+ <emphasis>session-per-request-with-detached-objects</emphasis>.
+ à usado versionamento automatico para isolar as modificações concorrentes.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Extended (or Long) Session</emphasis> A <literal>Session</literal>
+ do Hibernate pode ser desligada da conexão básica do JDBC depois que a
+ transação foi commitada e ser reconectado quando uma nova requisição do
+ cliente ocorrer. Este pattern é conhecido como
+ <emphasis>session-per-conversation</emphasis> e faz o reatamento uniforme
+ desnecessário. Versionamento automático é usado para isolar modificações
+ concorrentes e a <emphasis>session-per-conversation</emphasis> usualmente
+ não é permitido para ser nivelado automaticamente, e sim explicitamente.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Ambos <emphasis>session-per-request-with-detached-objects</emphasis> e
+ <emphasis>session-per-conversation</emphasis> possuem vantagens e desvantagens,
+ nos discutiremos mais tarde neste capÃtulo no contexto do controle de
+ concorrência otimista.
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-basics-identity">
+ <title>Considerando a identidade do objeto</title>
+
+ <para>
+ Uma aplicação pode acessar concorrentemente o mesmo estado persistente em duas
+ <literal>Session</literal>s diferentes. Entretanto, uma instância de uma classe
+ persistente nunca é compartilhada entre duas instâncias <literal>Session</literal>.
+ Por tanto, há duas noções diferentes da identidade:
+ </para>
+
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term>Identidade da base de dados</term>
+ <listitem>
+ <para>
+ <literal>foo.getId().equals( bar.getId() )</literal>
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Identidade da JVM</term>
+ <listitem>
+ <para>
+ <literal>foo==bar</literal>
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>
+ Então para os objetos acoplados a um <literal>Session</literal> em <literal>particular </literal>
+ (isto é no escopo de um <literal>Session</literal>), as duas noções são equivalentes e a
+ identidade da JVM para a identidade da base de dados é garantida pelo Hibernate. Entretanto,
+ quando a aplicação pode acessar concorrentemente o "mesmo" objeto do negócio (identidade
+ persistente) em duas sessões diferentes, as duas instâncias serão realmente "diferentes"
+ (identidade de JVM). Os conflitos são resolvidos usando (versionamento automático) no
+ flush/commit, usando abordagem otimista.
+
+ </para>
+
+ <para>
+ Este caminho deixa o Hibernate e o banco dedados se preocuparem com a concorrência; também
+ fornece uma escalabilidade melhor, garantindo que a identidade em unidades de trabalho
+ único-encadeadas não necessite de bloqueio dispendioso ou de outros meios de sincronização.
+ A aplicação nunca necessita sincronizar qualquer objeto de negócio tão longo que transpasse
+ uma única thread por <literal>Session</literal>. Dentro de uma <literal>Session</literal> a
+ aplicação pode usar com segurança o <literal>==</literal> para comparar objetos.
+ </para>
+
+ <para>
+ Com tudo, uma aplicação que usa <literal>==</literal> fora de uma <literal>Session</literal>,
+ pode ver resultados inesperados. Isto pode ocorrer mesmo em alguns lugares inesperados, por
+ exemplo, se você colocar duas instâncias desacopladas em um mesmo <literal>Set</literal>.
+ Ambos podem ter a mesma identidade na base de dados (isto é eles representam a mesma linha
+ em uma tabela), mas a identidade da JVM pela definição não garantida para instâncias em estado
+ desacoplado. O desenvolvedor tem que sobrescrever os métodos <literal>equals()</literal> e
+ <literal>hashCode()</literal> em classes persistentes e implementar sua própria noção da
+ igualdade do objeto. Advertência: nunca use o identificador da base de dados para implementar
+ a igualdade, use atributos de negócio, uma combinação única, geralmente imutável. O
+ identificador da base de dados mudará se um objeto transiente passar para o estado persistente.
+ Se a instância transiente (geralmente junto com instâncias desacopladas) for inserida em um
+ <literal>Set</literal>, mudar o hashcode quebra o contrato do <literal>Set</literal>.
+ Atributos para chaves de negócio não têm que ser tão estável quanto às chaves primárias
+ da base de dados, você somente tem que garantir a estabilidade durante o tempo que
+ os objetos estiverem no mesmo Set. Veja o website do Hibernate para uma discussão mais
+ completa sobre o assunto. Note também que esta não é uma caracteristica do Hibernate,
+ mas simplesmente como a identidade e a igualdade do objeto de Java têm que ser implementadas.
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-basics-issues">
+ <title>Edições comuns</title>
+
+ <para>
+ Nunca use o anti-patterns <emphasis>session-per-user-session</emphasis> ou
+ <emphasis>session-per-application</emphasis> (naturalmente, há umas exceções raras a
+ essa regra). Note que algumas das seguintes edições podem também aparecer com patterns
+ recomendados, certifique-se que tenha compreendido as implicações antes de fazer
+ uma decisão de projeto:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ Uma <literal>Session</literal> não é threadsafe. As coisas que são supostas para trabalhar
+ concorrentemente, como requisições HTTP, session beans, ou Swing, causarão condições de
+ disputa se uma instância <literal>Session</literal> for compartilhada. Se você mantiver
+ sua <literal>Session</literal> do Hibernate em seu <literal>HttpSession</literal>
+ (discutido mais tarde), você deve considerar sincronizar o acesso a sua sessão do HTTP.
+ Caso contrário, um usuário que clica em reload várias muito rapidamente pode usar o
+ mesmo <literal>Session</literal> em duas threads executando concorrentemente.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Uma exceção lançada pelo Hibernate significa que você tem que dar rollback na sua
+ transação no banco de dados e fechar a <literal>Session</literal> imediatamente
+ (discutido mais tarde em maiores detalhes). Se sua <literal>Session</literal> é
+ limitado pela aplicação, você tem que parar a aplicação. Dando rollback na
+ transação no banco de dados não põe seus objetos do negócio em um estado anterior
+ que estavam no inÃcio da transação. Isto significa que o estado da base de dados
+ e os objetos de negócio perdem a sincronização. Geralmente não é um problema
+ porque as exceções não são recuperáveis e você tem que iniciar após o
+ rollback de qualquer maneira.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ O <literal>Session</literal> guarda em cache cada objeto que está no estado persistente
+ (guardado e checado para estado "sujo" pelo Hibernate). Isto significa que ele cresce
+ infinitamente até que você obtenha uma OutOfMemoryException, se você o mantiver aberto
+ por muito tempo ou simplesmente carregar dados demais. Uma solução é chamar
+ <literal>clear()</literal> e <literal>evict()</literal> para controlar o cache
+ da <literal>Session</literal>, mas você deve considerar uma Store Procedure
+ se precisar de operações que envolvam grande volume de dados. Algumas soluções são
+ mostradas no <xref linkend="batch"/>. Manter uma <literal>Session</literal> aberta
+ durante uma sessão do usuário significa também uma probabilidade elevada de se acabar
+ com dados velhos.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="transactions-demarcation">
+ <title>Demarcação de transações de bancos de dados</title>
+
+ <para>
+ Os limites de uma transação de banco de dados (ou sistema) são sempre necessários. Nenhuma
+ comunicação com o banco de dados pode ocorrer fora de uma transação de banco de dados (isto
+ parece confundir muitos desenvolvedores que estão usados modo auto-commit). Sempre use os
+ limites desobstruÃdos da transação, até mesmo para operações somente leitura. Dependendo
+ de seu nÃvel de isolamento e capacidade da base de dados isto pode não ser requerido,
+ mas não há nenhum aspecto negativo se você demarca sempre transações explicitamente.
+ Certamente, uma única transação será melhor executada do que muitas transações pequenas,
+ até mesmo para dados de leitura.
+ </para>
+
+ <para>
+ Uma aplicação do Hibernate pode funcionar em ambientes não gerenciados (isto é aplicações standalone, Web
+ simples ou Swing) e ambientes gerenciados J2EE. Em um ambiente não gerenciado, o Hibernate é geralmente
+ responsável pelo seu próprio pool de conexões. O desenvolvedor tem que manualmente ajustar limites das
+ transaçãos, ou seja, começar, commitar, ou dar rollback nas transações ele mesmo. Um ambiente gerenciado
+ fornece transações gerenciadas por contêiner (CMT - container-managed transactions), com um conjunto
+ da transações definido declarativamente em descritores de deployment de EJB session beans, por exemplo.
+ A demarcação programática é então já não é necessário.
+ </para>
+
+ <para>
+ Entretanto, é freqüentemente desejável manter sua camada de persistência portável entre ambientes
+ de recurso locais não gerenciados e sistemas que podem confiar em JTA, mas usar BMT em vez de CMT.
+ Em ambos os casos você usaria demarcação de transação programática. O Hibernate oferece uma API
+ chamada Transaction que traduz dentro do sistema de transação nativa de seu ambiente de deployment.
+ Esta API é realmente opcional, mas nós encorajamos fortemente seu uso a menos que você estiver
+ em um CMT session bean.
+ </para>
+
+ <para>
+ Geralmente, finalizar um <literal>Session</literal>envolve quatro fases distintas:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ flush da sessão
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ commitar a transação
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ fechar a sessão
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ tratar as exceções
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ A limpeza da sessão já foi bem discutida, agora nós daremos uma olhada na demarcação da
+ transação e na manipulação de exceção em ambientes controlados e não controlados.
+ </para>
+
+
+ <sect2 id="transactions-demarcation-nonmanaged" revision="2">
+ <title>Ambiente não gerenciado</title>
+
+ <para>
+ Se uma camada de persistência do Hibernate roda em um ambiente não gerenciado, as conexões
+ do banco de dados são geralmente tratadas pelos pools de conexões simples
+ (isto é, não baseados em DataSource) dos quais o Hibernate obtém as conexões assim
+ que necessita. A maneira de se manipular uma sessão/transação é mais ou menos assim:
+ </para>
+
+ <programlisting><![CDATA[// Non-managed environment idiom
+Session sess = factory.openSession();
+Transaction tx = null;
+try {
+ tx = sess.beginTransaction();
+
+ // do some work
+ ...
+
+ tx.commit();
+}
+catch (RuntimeException e) {
+ if (tx != null) tx.rollback();
+ throw e; // or display error message
+}
+finally {
+ sess.close();
+}]]></programlisting>
+
+ <para>
+ Você não pode chamar <literal>flush()</literal> do <literal>Session()</literal>
+ explicitamente - a chamada ao <literal>commit()</literal> dispara automaticamente
+ a sincronização para a sessão (dependendo do <xref linkend="objectstate-flushing">
+ FlushMode</xref>). Uma chamada ao <literal>close()</literal> marca o fim de uma sessão.
+ A principal implicação do <literal>close()</literal> é que a conexão JDBC será abandonada
+ pela sessão. Este código Java é portável e funciona em ambientes não gerenciado e de JTA.
+ </para>
+
+ <para>
+ Uma solução muito mais flexÃvel é gerência integrada de contexto da "sessão atual"
+ do Hibernate, como descrito anteriormente:
+ </para>
+
+ <programlisting><![CDATA[// Non-managed environment idiom with getCurrentSession()
+try {
+ factory.getCurrentSession().beginTransaction();
+
+ // do some work
+ ...
+
+ factory.getCurrentSession().getTransaction().commit();
+}
+catch (RuntimeException e) {
+ factory.getCurrentSession().getTransaction().rollback();
+ throw e; // or display error message
+}]]></programlisting>
+
+ <para>
+ Você muito provavelmente nunca verá estes fragmentos de código em uma aplicação
+ regular; as exceções fatais (do sistema) devem sempre ser pegas no "alto".
+ Ou seja, o código que executa chamadas do Hibernate (na camada de persistência)
+ e o código que trata <literal>RuntimeException</literal> (e geralmente pode
+ somente limpar acima e na saÃda) estão em camadas diferentes. O gerenciamento do
+ contexto atual feito pelo Hibernate pode significativamente simplificar este
+ projeto, como tudo que você necessita é do acesso a um <literal>SessionFactory</literal>.
+ A manipulação de exceção é discutida mais tarde neste capÃtulo.
+ </para>
+
+ <para>
+ Note que você deve selecionar <literal>org.hibernate.transaction.JDBCTransactionFactory</literal>
+ (que é o padrão) e para o segundo exemplo <literal>"thread"</literal> como seu
+ <literal>hibernate.current_session_context_class</literal>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-demarcation-jta" revision="3">
+ <title>Usando JTA</title>
+
+ <para>
+ Se sua camada de persistência funcionar em um servidor de aplicação (por exemplo,
+ dentro dos EJB session beans), cada conexão do datasource obtida pelo Hibernate
+ automaticamente fará parte da transação global de JTA. Você pode também instalar uma
+ implementação standalone de JTA e usá-la sem EJB. O Hibernate oferece duas estratégias
+ para a integração de JTA.
+ </para>
+
+ <para>
+ Se você usar bean-managed transactions (BMT - transações gerenciadas por bean) o Hibernate dirá
+ ao servidor de aplicação para começar e para terminar uma transação de BMT se você usar a API
+ Transaction. Assim, o código de gerência de transação é idêntico ao ambiente não gerenciado.
+ </para>
+
+ <programlisting><![CDATA[// BMT idiom
+Session sess = factory.openSession();
+Transaction tx = null;
+try {
+ tx = sess.beginTransaction();
+
+ // do some work
+ ...
+
+ tx.commit();
+}
+catch (RuntimeException e) {
+ if (tx != null) tx.rollback();
+ throw e; // or display error message
+}
+finally {
+ sess.close();
+}]]></programlisting>
+
+ <para>
+ Se você quiser usar um <literal>Session</literal> limitada por transação, isto é,
+ a funcionalidade do <literal>getCurrentSession()</literal> para a propagação fácil
+ do contexto, você terá que usar diretamente a API JTA <literal>UserTransaction</literal>:
+ </para>
+
+ <programlisting><![CDATA[// BMT idiom with getCurrentSession()
+try {
+ UserTransaction tx = (UserTransaction)new InitialContext()
+ .lookup("java:comp/UserTransaction");
+
+ tx.begin();
+
+ // Do some work on Session bound to transaction
+ factory.getCurrentSession().load(...);
+ factory.getCurrentSession().persist(...);
+
+ tx.commit();
+}
+catch (RuntimeException e) {
+ tx.rollback();
+ throw e; // or display error message
+}]]></programlisting>
+
+ <para>
+ Com CMT, a demarcação da transação é feita em descritores de deployment do session beans,
+ não programaticamente, conseqüentemente, o código é reduzido a:
+ </para>
+
+ <programlisting><![CDATA[// CMT idiom
+ Session sess = factory.getCurrentSession();
+
+ // do some work
+ ...
+]]></programlisting>
+
+ <para>
+ Em um CMT/EJB mesmo um rollback acontece automaticamente, desde que uma exeção <literal>RuntimeException</literal>
+ não tratável seja lançada por um método de um session bean que informa ao contêiner ajustar a
+ transação global ao rollback. <emphasis>Isto significa que você não necessita usar a API
+ <literal>Transaction</literal> do Hibernate em tudo com BMT ou CMT e você obtém a propagação
+ automática do Session "atual" limitada à transação.</emphasis>
+ </para>
+
+ <para>
+ Veja que você deverá escolher <literal>org.hibernate.transaction.JTATransactionFactory</literal>
+ se você usar o JTA diretamente (BMT) e <literal>org.hibernate.transaction.CMTTransactionFactory</literal>
+ em um CMT session bean, quando você configura a fábrica de transação do Hibernate. Lembre-se também de
+ configurar o <literal>hibernate.transaction.manager_lookup_class</literal>. Além disso, certifique-se
+ que seu <literal>hibernate.current_session_context_class</literal> ou não é configurado (compatibilidade
+ com o legado) ou é definido para <literal>"jta"</literal>.
+
+ </para>
+
+ <para>
+ A operação <literal>getCurrentSession()</literal> tem um aspecto negativo em um ambiente JTA.
+ Há uma advertência para o uso do método liberado de conexão <literal>after_statement</literal>,
+ o qual é usado então por padrão. Devido a uma limitação simples da especificação JTA, não é
+ possÃvel para o Hibernate automaticamente limpar quaisquer instâncias <literal>ScrollableResults</literal>
+ ou <literal>Iterator</literal> abertas retornadas pelo <literal>scroll()</literal> ou
+ <literal>iterate()</literal>. Você <emphasis>deve</emphasis> liberar o cursor subjacente da
+ base de dados chamando <literal>ScrollableResults.close()</literal> ou
+ <literal>Hibernate.close(Iterator)</literal> explicitamente de um bloco <literal>finally</literal>.
+ (Claro que a maioria de aplicações podem facilmente evitar o uso do <literal>scroll()</literal> ou
+ do <literal>iterate()</literal> em todo código provindo do JTA ou do CMT.)
+
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-demarcation-exceptions">
+ <title>Tratamento de Exceção</title>
+
+ <para>
+ Se a <literal>Session</literal> levantar uma exceção (incluindo qualquer
+ <literal>SQLException</literal>), você deve imediatamente dar um rollback
+ na transação do banco, chamando <literal>Session.close()</literal> e descartando
+ a instância da <literal>Session</literal>. Certos métodos da <literal>Session</literal>
+ <literal>não</literal> deixarão a sessão em um estado inconsistente. Nenhuma exceção
+ lançada pelo Hibernate pode ser recuperada. Certifique-se que a <literal>Session</literal>
+ será fechada chamando <literal>close()</literal> no bloco <literal>finally</literal>.
+ </para>
+
+ <para>
+ A exceção <literal>HibernateException</literal>, a qual envolve a maioria dos erros
+ que podem ocorrer em uma camada de persistência do Hibernate, é uma exceção unchecked (
+ não estava em umas versões mais antigas de Hibernate). Em nossa opinião, nós não devemos
+ forçar o desenvolvedor a tratar uma exceção irrecuperável em uma camada mais baixa.
+ Na maioria dos sistemas, as exceções unchecked e fatais são tratadas em um dos primeiros
+ frames da pilha da chamada do método (isto é, em umas camadas mais elevadas) e uma mensagem
+ de erro é apresentada ao usuário da aplicação (ou a alguma outra ação apropriada é feita).
+ Note que Hibernate pode também lançar outras exceções unchecked que não são um
+ <literal>HibernateException</literal>. Estas, também são, irrecuperáveis e uma ação
+ apropriada deve ser tomada.
+ </para>
+
+ <para>
+ O Hibernate envolve <literal>SQLException</literal>s lançadas ao interagir com a base de dados
+ em um <literal>JDBCException</literal>. Na realidade, o Hibernate tentará converter a exceção em
+ em uma sub classe mais significativa da <literal>JDBCException</literal>. A
+ <literal>SQLException</literal> subjacente está sempre disponÃvel através de
+ <literal>JDBCException.getCause()</literal>.
+ </para>
+
+ <para>
+ O Hibernate converte a <literal>SQLException</literal> em uma sub classe
+ <literal>JDBCException</literal> apropriada usando <literal>SQLExceptionConverter</literal>
+ associado ao SessionFactory. Por padrão, o <literal>SQLExceptionConverter</literal> é definido
+ pelo dialeto configurado; entretanto, é também possÃvel conectar em uma implementação customizada
+ (veja o javadoc para mais detalhes da classe <literal>SQLExceptionConverterFactory</literal>).
+ Os subtipos padrão de <literal>JDBCException</literal> são:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>JDBCConnectionException</literal> - indica um erro com a comunicação subjacente de JDBC.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>SQLGrammarException</literal> - indica um problema da gramática ou da sintaxe com o SQL emitido.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>ConstraintViolationException</literal> - indica algum forma de violação de confinamento de integridade.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>LockAcquisitionException</literal> - indica um erro ao adquirir um nÃvel de bloqueio necessário para realizar a operação de requisição.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>GenericJDBCException</literal> - uma exceção genérica que não cai em algumas das outras categorias.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect2>
+
+ <sect2 id="transactions-demarcation-timeout">
+ <title>Timeout de Transação</title>
+
+ <para>
+ Uma caracterÃstica extremamente importante fornecida por um ambiente
+ gerenciado como EJB e que nunca é fornecido pelo código não gerenciado é o timeout
+ de transação. Timeouts de transação asseguram que nenhuma transação possa
+ reter indefinidamente recursos enquanto não retorna nenhuma resposta ao usuário.
+ Fora de um ambiente controlado (JTA), o Hibernate não pode fornecer inteiramente
+ esta funcionalidade. Entretanto, o Hibernate pode afinal controlar as operações
+ do acesso a dados, assegurando que o nÃvel de deadlocks e queries do banco de
+ dados com imensos resultados definidos sejam limitados pelo timeout. Em um ambiente
+ gerenciado, o Hibernate pode delegar o timeout da transação ao JTA. Esta funcionalidade
+ é abstraÃda pelo objeto <literal>Transaction</literal> do Hibernate.
+ </para>
+
+ <programlisting><![CDATA[
+Session sess = factory.openSession();
+try {
+ //set transaction timeout to 3 seconds
+ sess.getTransaction().setTimeout(3);
+ sess.getTransaction().begin();
+
+ // do some work
+ ...
+
+ sess.getTransaction().commit()
+}
+catch (RuntimeException e) {
+ sess.getTransaction().rollback();
+ throw e; // or display error message
+}
+finally {
+ sess.close();
+}]]></programlisting>
+
+ <para>
+ Veja que <literal>setTimeout()</literal> não pode ser chamado em um CMT bean,
+ onde os timeouts das transações devem ser definidos declarativamente.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="transactions-optimistic">
+ <title>Controle de concorrência otimista</title>
+
+ <para>
+ O único caminho que é consistente com a elevada concorrência e escalabilidade
+ é controle de concorrência otimista com versionamento. Checagem de versão usa
+ número de versão, ou timestamps, para detectar conflitos de atualizações (e para
+ impedir atualizações perdidas). O Hibernate fornece três caminhos possÃveis para
+ escrever aplicações que usam concorrência otimista. Os casos de uso que nós mostramos
+ estão no contexto de conversações longas, mas a checagem de versão também tem o
+ benefÃcio de impedir atualizações perdidas em únicas transações.
+ </para>
+
+ <sect2 id="transactions-optimistic-manual">
+ <title>Checagem de versão da aplicação</title>
+
+ <para>
+ Em uma implementação sem muita ajuda do Hibernate, cada interação com o banco de dados
+ ocorre em uma nova <literal>Session</literal> e o desenvolvedor é responsável para
+ recarregar todas as instâncias persistentes da base de dados antes de manipulá-las.
+ Este caminho força a aplicação a realizar sua própria checagem de versão para assegurar
+ a conversação do isolamento da transação. Este caminho é menos eficiente em termos de
+ acesso ao banco de dados. Ã a caminho mais similar a EJBs entity.
+ </para>
+
+ <programlisting><![CDATA[// foo is an instance loaded by a previous Session
+session = factory.openSession();
+Transaction t = session.beginTransaction();
+
+int oldVersion = foo.getVersion();
+session.load( foo, foo.getKey() ); // load the current state
+if ( oldVersion!=foo.getVersion ) throw new StaleObjectStateException();
+foo.setProperty("bar");
+
+t.commit();
+session.close();]]></programlisting>
+
+ <para>
+ A propriedade <literal>version</literal> é mapeada usando <literal><version></literal>,
+ e o Hibernate vai incrementá-lo-á automaticamente durante o flush se a entidade
+ estiver alterada.
+ </para>
+
+ <para>
+ Claro, se você se estiver operando em um ambiente de baixa concorrência de dados
+ e não requerer a checagem de versão, você pode usar este caminho e apenas saltar a
+ checagem de versão. Nesse caso, o <emphasis>ultimo commit realizdo </emphasis> é
+ a estratégia padrão para suas conversações longas. Mantenha em mente que isto pode
+ confundir os usuários da aplicação, assim como eles podem experimentar atualizações
+ perdidas sem mensagens de erro ou uma possibilidade ajustar mudanças de conflito.
+
+ </para>
+
+ <para>
+ Claro que, checagem manual da versão é somente praticável em circunstâncias triviais
+ e não para a maioria de aplicações. Freqüentemente, os grafos completos de objetos
+ modificados têm que ser verificados, não somente únicas instâncias. O Hibernate oferece
+ checagem de versão automática com uma <literal>Session</literal> estendida ou instâncias
+ desatachadas como o paradigma do projeto.
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-optimistic-longsession">
+ <title>Sessão estendida e versionamento automático</title>
+
+ <para>
+ Uma única instância de <literal>Session</literal> e suas instâncias persistentes
+ são usadas para a conversação inteira, isto é conhecido como
+ <emphasis>session-per-conversation</emphasis>. O Hibernate verifica versões da instância
+ no momento dio flush, lançando uma exceção se a modificação concorrente for detectada.
+ Até o desenvolvedor pegar e tratar essa exceção (as opções comuns são a oportunidade
+ para que o usuário intercale as mudanças ou reinicie a conversação do negócio com
+ dados não antigos).
+ </para>
+
+ <para>
+ The <literal>Session</literal> is disconnected from any underlying JDBC connection
+ when waiting for user interaction. This approach is the most efficient in terms
+ of database access. The application need not concern itself with version checking or
+ with reattaching detached instances, nor does it have to reload instances in every
+ database transaction.
+ A <literal>Session</literal> é desconectada de toda a conexão JDBC subjacente
+ enquanto espera a interação do usuário. Este caminho é a mais eficiente em termos
+ de acesso a bancos de dados. A aplicação não necessita concernir-se com a checagem
+ de versão ou com as instâncias destacadas reatadas, nem tem que recarregar instâncias
+ em cada transação.
+ </para>
+
+ <programlisting><![CDATA[// foo is an instance loaded earlier by the old session
+Transaction t = session.beginTransaction(); // Obtain a new JDBC connection, start transaction
+
+foo.setProperty("bar");
+
+session.flush(); // Only for last transaction in conversation
+t.commit(); // Also return JDBC connection
+session.close(); // Only for last transaction in conversation]]></programlisting>
+ <para>
+ O objeto <literal>foo</literal> sabe que <literal>Session</literal> já foi carregada. Começando
+ uma nova transação em uma sessão velha obtém uma conexão nova e recomeça a sessão. Commitando
+ uma transação desconecta uma sessão da conexão JDBC e retorna a conexão ao pool. Após a reconexão,
+ forçar uma checagem de versão em dados que você não está atualizando, você pode chamar
+ <literal>Session.lock()</literal> com o <literal>LockMode.READ</literal> em todos os objetos
+ que possam ter sido atualizados por uma outra transação. Você não necessita bloquear nenhum
+ dado para atualizar. Geralmente você configuraria <literal>FlushMode.NEVER</literal> em uma
+ <literal>Session</literal> estendida, de modo que somente o último ciclo da transação tenha
+ permissão de persistir todas as modificações feitas nesta conversação. Disso, somente esta última
+ transação incluiria a operação <literal>flush()</literal> e então chamar também <literal>close()</literal>
+ da sessão para terminar a conversação.
+ </para>
+
+ <para>
+ Este pattern é problemático se a <literal>Session</literal> for demasiadamente grande para
+ ser armazenado durante o tempo que usuário pensar, por exemplo um <literal>HttpSession</literal>
+ estiver mantido tão pequeno quanto possÃvel. Como o <literal>Session</literal> é também cache
+ de primeiro nÃvel (imperativo) e contém todos os objetos carregados, nós podemos provavelmente
+ usar esta estratégia somente para alguns ciclos de requisição/resposta. Você deve usar a
+ <literal>Session</literal> somente para uma única conversação, porque ela logo também
+ estará com dados velhos.
+ </para>
+
+ <para>
+ (Note que versões mais atuais de Hibernate requerem a desconexão e o reconexão explÃcitas de
+ uma <literal>Session</literal>. Estes métodos são desatualizados, como o inÃcio e término de
+ uma transação tem o mesmo efeito.)
+ </para>
+
+ <para>
+ Note também que você deve manter a <literal>Session</literal> desconectada fechada
+ para a camada de persistência. Ou seja, use um EJB stateful session bean para
+ prender a <literal>Session</literal> em um ambiente do três camadas e não o
+ transferir à camada web (ou até serializá-lo para uma camada separada)
+ para armazená-lo no <literal>HttpSession</literal>.
+
+ </para>
+
+ <para>
+ O pattern sessão estendida, ou <emphasis>session-per-conversation</emphasis>, é mais
+ difÃcil de implementar com gerenciamento automático de sessão atual. Você precisa fornecer
+ sua própria implementação do <literal>CurrentSessionContext</literal> para isto
+ (veja o Hibernate Wiki para exemplos).
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-optimistic-detached">
+ <title>Objetos destacados e versionamento automático</title>
+
+ <para>
+ Cada interação com o armazenamento persistente ocorre em uma <literal>Session</literal> nova.
+ Entretanto, as mesmas instâncias persistentes são reusadas para cada interação com o banco de dados.
+ A aplicação manipula o estado das instâncias desatachadas originalmente carregadas em um outro
+ <literal>Session</literal> e reata-os então usando <literal>Session.update()</literal>,
+ <literal>Session.saveOrUpdate()</literal> ou <literal>Session.merge()</literal>.
+ </para>
+
+ <programlisting><![CDATA[// foo is an instance loaded by a previous Session
+foo.setProperty("bar");
+session = factory.openSession();
+Transaction t = session.beginTransaction();
+session.saveOrUpdate(foo); // Use merge() if "foo" might have been loaded already
+t.commit();
+session.close();]]></programlisting>
+
+ <para>
+ Outra vez, o Hibernate verificará versões da instância durante o flush,
+ lançando uma exceção se ocorrer conflitos de atualizações.
+ </para>
+
+ <para>
+ Você pode também chamar o <literal>lock()</literal> em vez de <literal>update()</literal>
+ e usar <literal>LockMode.READ</literal> (executando uma checagem de versão, ignorando
+ todos os caches) se você estiver certo de que o objeto não foi modificado.
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-optimistic-customizing">
+ <title>Versionamento automático customizado</title>
+
+ <para>
+ Você pode desabilitar o incremento da versão automática de Hibernate para propriedades
+ e coleções particulares configurando o mapeamento do atributo <literal>optimistic-lock</literal>
+ para false. O Hibernate então não irá incrementa versões se a propriedade estiver
+ modificada.
+ </para>
+
+ <para>
+ Os esquemas da base de dados legada são freqüentemente estáticos e não podem ser modificados.
+ Ou outras aplicações puderam também acessar a mesma base de dados e não sabem tratar a
+ versão dos números ou timestamps. Em ambos os casos, o versionamento não pode confiar em uma
+ coluna particular em uma tabela. Para forçar uma checagem de versão sem uma versão ou mapeamento
+ da propriedade do timestamp com uma comparação do estado de todos os campos em uma linha,
+ configure <literal>optimistic-lock="all"</literal> no mapeamento <literal><class></literal>.
+ Note que isto conceitualmente é somente feito em trabalhos se Hibernate puder comparar o estado
+ velho e novo, isto é, se você usa um único <literal>Session</literal> longo e não
+ session-per-request-with-detached-objects.
+ </para>
+
+ <para>
+ Ãs vezes a modificação concorrente pode ser permitida tão longa quanto à s mudanças que
+ tiveram sido feitas que não sobrepuseram. Se você configurar <literal>optimistic-lock="dirty"</literal>
+ ao mapear o <literal><class></literal>, o Hibernate comparará somente campos
+ modificados durante o flush.
+ </para>
+
+ <para>
+ Em ambos os casos, com as colunas dedicadas da versão/timestamp ou com comparação do
+ campo cheio/modificados, o Hibernate usa uma única declaração UPDATE (com uma cláusula
+ WHERE apropriada ) por entidade para executar a checagem da versão e atualizar a informação.
+ Se você usa a persistência transitiva para cascatear o reatamento das entidades associadas,
+ o Hibernate pode executar atualizações desnecessárias. Isso não é geralmente um problema,
+ mas triggers <emphasis>on update</emphasis> em um banco de dados podem ser executados
+ mesmo quando nenhuma mudança foi feita nas instâncias destacadas. Você pode customizar
+ este comportamento configurando <literal>select-before-update="true"</literal> no
+ mapeamento <literal><class></literal>, forçando o Hibernate a dá um SELECT nas
+ instâncias para assegurar-se esse as mudanças ocorreram realmente, antes de atualizar
+ a linha.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="transactions-locking">
+ <title>Locking pessimista</title>
+
+ <para>
+ Não se pretende que os usuários gastam muitas horas se preocupando com suas estratégias de
+ locking. Geralmente é o bastante para especificar um nÃvel de isolamento para as conexões
+ JDBC e então deixar simplesmente o banco de dados fazer todo o trabalho. Entretanto, os
+ usuários avançados podem às vezes desejar obter locks pessimistas exclusivos, ou re-obter
+ locks no inÃcio de uma nova transação.
+ </para>
+
+ <para>
+ O Hibernate usará sempre o mecanismo de lock da base de dados, nunca trava objetos
+ na memória!
+ </para>
+
+ <para>
+ A classe <literal>LockMode</literal> define os diferentes nÃveis de lock que o Hibernate
+ pode adquirir. Um lock é obtido pelos seguintes mecanismos:
+
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>LockMode.WRITE</literal> é adquirido automaticamente quando o Hibernate atualiza
+ ou insere uma linha.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>LockMode.UPGRADE</literal> pode ser adquirido explicitamente pelo usuário
+ usando <literal>SELECT ... FOR UPDATE</literal> em um banco de dados que suporte
+ esse sintaxe.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>LockMode.UPGRADE_NOWAIT</literal> pode ser adquirido explicitamente pelo usuário
+ usando <literal>SELECT ... FOR UPDATE NOWAIT</literal> no Oracle.
+
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>LockMode.READ</literal> é adquirido automaticamente quando o Hibernate lê
+ dados em um nÃvel Repeatable Read ou Serializable isolation. Pode ser readquirido
+ explicitamente pelo usuário.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>LockMode.NONE</literal> representa a ausência do lock. Todos os objetos mudam para
+ esse estado de lock no final da <literal>Transaction</literal>. Objetos associados com a sessão
+ através do método <literal>update()</literal> ou <literal>saveOrUpdate()</literal> também são
+ inicializados com esse lock mode.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ O lock obtido "explicitamente pelo usuário" se dá em uma das seguintes maneiras:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ Uma chamada a <literal>Session.load()</literal>, especificando
+ o <literal>LockMode</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Uma chamada a <literal>Session.lock()</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Uma chamada a <literal>Query.setLockMode()</literal>.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Se uma <literal>Session.load()</literal> é invocada com <literal>UPGRADE</literal> ou
+ <literal>UPGRADE_NOWAIT</literal>, e o objeto requisitado ainda não foi carregado
+ pela sessão, o objeto é carregado usando <literal>SELECT ... FOR UPDATE</literal>.
+ Se <literal>load()</literal> for chamado para um objeto que já foi carregado
+ com um lock menos restritivo que o novo lock solicitado, o Hibernate invoca o
+ método <literal>lock()</literal> para aquele objeto.
+ </para>
+
+ <para>
+ O método <literal>Session.lock()</literal> executa uma verificação no número da versão
+ se o modo de lock especificado for <literal>READ</literal>, <literal>UPGRADE</literal> ou
+ <literal>UPGRADE_NOWAIT</literal>.. (No caso do <literal>UPGRADE</literal> ou
+ <literal>UPGRADE_NOWAIT</literal>, é usado <literal>SELECT ... FOR UPDATE</literal>.)
+ </para>
+
+ <para>
+ Se o banco de dados não suportar o lock mode solicitado, o Hibernate vai usar um modo
+ alternativo apropriado (ao invés de lançar uma exceção). Isso garante que a aplicação
+ vai ser portável.
+ </para>
+
+ </sect1>
+
+ <sect1 id="transactions-connection-release">
+ <title>Modos de liberar a Connection</title>
+
+ <para>
+ O comportamento legado do Hibernate (2.x) em consideração ao gerenciamento da conexão
+ via JDBC fez com que a <literal>Session</literal> precisasse obter uma conexão
+ quando ela precisasse pela primeira vez e depois manter a conexão enquanto
+ a sessão não fosse fechada. O Hibernate 3.x introduz a idéia de modos de liberar a
+ sessão, para informar a sessão a forma como deve manusear a sua conexão JDBC.
+ Veja que essa discussão só é pertinente para conexões fornecidas com um
+ <literal>ConnectionProvider</literal> configurado; conexões fornecidas pelo usuário
+ estão fora do escopo dessa discussão. Os diferentes modos de liberação estão definidos
+ pelos valores da enumeração
+ <literal>org.hibernate.ConnectionReleaseMode</literal>:
+
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>ON_CLOSE</literal> - essencialmente é o modo legado descrito acima. A sessão
+ do Hibernate obtêm a conexão quando precisar executar alguma operação JDBC pela
+ primeira vez e mantem enquanto a conexão não for fechada.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>AFTER_TRANSACTION</literal> â informa que a conexão deve ser
+ liberada após a conclusão de uma <literal>org.hibernate.Transaction</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>AFTER_STATEMENT</literal> (também conhecida com liberação agressiva) â informa
+ que a conexão deve ser liberada após a execução de cada statement. A liberação agressiva
+ não ocorre se o statement deixa pra trás algum recurso aberto associado com a sessão
+ obtida; atualmente, a única situação em que isso é possÃvel é com o uso de
+ <literal>org.hibernate.ScrollableResults</literal>.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ O parâmetro de configuração <literal>hibernate.connection.release_mode</literal> é usado
+ para especificar qual modo de liberação deve ser usado. Opções disponÃveis:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>auto</literal> (padrão) â essa opção delega ao modo de liberação retornado pelo
+ método <literal>org.hibernate.transaction.TransactionFactory.getDefaultReleaseMode()</literal>.
+ Para JTATransactionFactory, ele retorna ConnectionReleaseMode.AFTER_STATEMENT; para
+ JDBCTransactionFactory, ele retorna ConnectionReleaseMode.AFTER_TRANSACTION.
+ Raramente é uma boa idéia alterar padrão, como frequencia ao se fazer isso temos falhas
+ que parecem bugs e/ou suposições inválidas no código do usuário.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>on_close</literal> - indica o uso da ConnectionReleaseMode.ON_CLOSE. Essa opção
+ foi deixada para manter a compatibilidade, mas seu uso é fortemente desencorajado.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>after_transaction</literal> â indica o uso da ConnectionReleaseMode.AFTER_TRANSACTION.
+ Essa opção nada deve ser usada com ambientes JTA. Também note que no caso da
+ ConnectionReleaseMode.AFTER_TRANSACTION, se a sessão foi colocada no modo auto-commit a
+ conexão vai ser liberada de forma similar ao modo AFTER_STATEMENT.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>after_statement</literal> â indica o uso ConnectionReleaseMode.AFTER_STATEMENT.
+ Adicionalmente, o <literal>ConnectionProvider</literal> configurado é consultado para
+ verificar se suporta essa configuração ((<literal>supportsAggressiveRelease()</literal>).
+ Se não suportar, o modo de liberação é redefinido como ConnectionRelease-Mode.AFTER_TRANSACTION.
+ Essa configuração só é segura em ambientes onde podemos readquirir a mesma conexão JDBC
+ toda vez que o método <literal>ConnectionProvider.getConnection()</literal> for chamado ou
+ em um ambiente auto-commit onde não importa se nós recuperamos a mesma conexão.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect1>
+
+</chapter>
+
Property changes on: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/transactions.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/tutorial.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/tutorial.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/tutorial.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1530 @@
+<chapter id="tutorial">
+ <title>Introdução ao Hibernate</title>
+
+ <sect1 id="tutorial-intro" revision="1">
+ <title>Prefácio</title>
+
+ <para>
+ Este capítulo é um tutorial introdutório para novos usuários do Hibernate. Nós iniciaremos com uma simples linha de comando em uma aplicação usando uma base de dados em memória tornando isto um passo de fácil de compreender.
+ </para>
+
+ <para>
+ Este tutorial é voltado para novos usuários do Hibernate, mas requer um conhecimento de Java e SQL. Este tutorial é baseado no tutorial de Michael Gloegl, as bibliotecas Third Party foram nomeadas para JDK 1.4 e 5.0. Você pode precisar de outras bibliotecas para JDK 1.3.
+ </para>
+
+ <para>
+ O código fonte para o tutorial está incluÃdo no diretório da distribuição
+ <literal>doc/reference/tutorial/</literal>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="tutorial-firstapp" revision="2">
+ <title>Parte 1 â A primeira aplicação Hibernate</title>
+
+ <para>
+ Primeiro, iremos criar uma simples aplicação Hibernate baseada em console. Usaremos uma base de dados Java (HSQL DB), então não teremos que instalar nenhum servidor de base de dados.
+ </para>
+
+ <para>
+ Vamos supor que precisemos de uma aplicação com um banco de dados pequeno que possa armazenar e atender os eventos que queremos, e as informaççes sobre os hosts destes eventos.
+ </para>
+
+ <para>
+ A primeira coisa que devemos fazer é configurar nosso diretório de desenvolvimento,
+ e colocar todas as bibliotecas Java que precisamos dentro dele. Faça o download da
+ distribuição do Hibernate no site do Hibernate. Descompacte o pacote e coloque todas
+ as bibliotecas necessárias encontradas no diretório <literal>/lib</literal>, dentro do
+ diretório <literal>/lib</literal> do seu novo projeto. Você deverá ter algo parecido
+ com isso:
+ </para>
+
+ <programlisting><![CDATA[.
++lib
+ antlr.jar
+ cglib.jar
+ asm.jar
+ asm-attrs.jars
+ commons-collections.jar
+ commons-logging.jar
+ hibernate3.jar
+ jta.jar
+ dom4j.jar
+ log4j.jar ]]></programlisting>
+
+ <para>
+ Esta é a configuração mínima requerida das bibliotecas (observe que também foi copiado
+ o hibernate3.jar da pasta principal do Hibernate) para o Hibernate <emphasis>na hora do desenvolvimento</emphasis>. O Hibernate permite que você utilize mais ou menos bibliotecas.
+ Veja o arquivo <literal>README.txt</literal> no diretório <literal>lib/</literal> da distribuição
+ do Hibernate para maiores informaççes sobre bibliotecas requeridas e opcionais.
+ (Atualmente, a biblioteca Log4j não é requerida, mas é preferida por muitos desenvolvedores.)
+ </para>
+
+ <para>
+ Agora, iremos criar uma classe que representa o evento que queremos armazenar na base de dados..
+ </para>
+
+ <sect2 id="tutorial-firstapp-firstclass" revision="1">
+ <title>A primeira Classe</title>
+
+ <para>
+ Nossa primeira classe de persistência é uma simples classe JavaBean com algumas propriedades:
+ </para>
+
+ <programlisting><![CDATA[package events;
+
+import java.util.Date;
+
+public class Event {
+ private Long id;
+
+ private String title;
+ private Date date;
+
+ public Event() {}
+
+ public Long getId() {
+ return id;
+ }
+
+ private void setId(Long id) {
+ this.id = id;
+ }
+
+ public Date getDate() {
+ return date;
+ }
+
+ public void setDate(Date date) {
+ this.date = date;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+}]]></programlisting>
+
+ <para>
+ Você pode ver que esta classe usa o padrão JavaBean para o nomeamento convencional da propriedade getter e dos métodos setter, como também a visibilidade private dos campos. Este é um padrão de projeto recomendado, mas não requerido. O Hibernate pode também acessar campos diretamente, o benefÃcio para os métodos de acesso é a robustez para o Refactoring. O construtor sem argumento é requerido para instanciar um objeto desta classe com a reflexão.
+ </para>
+
+ <para>
+ A propriedade <literal>id</literal> mantém um único valor de identificação para um evento
+ particular. Todas as classes persistentes da entidade (bem como aquelas classes dependentes
+ de menos importância) precisam de uma propriedade de identificação, caso nós queiramos usar o
+ conjunto completo de caracterÃsticas do Hibernate. De fato, a maioria das aplicações
+ (esp. aplicações web) precisam destinguir os objetos pelo identificador, então você deverá
+ considerar esta, uma caracterÃstica em lugar de uma limitação. Porém, nós normalmente não
+ manipulamos a identidade de um objeto, consequentemente o método setter deverá ser privado.
+ O Hibernate somente nomeará os identificadores quando um objeto for salvo. Você pode ver como
+ o Hibernate pode acessar métodos públicos, privados, e protegidos, como também campos
+ (públicos, privados, protegidos) diretamente. A escolha está até você, e você pode combinar
+ isso para adaptar seu projeto de aplicação
+ </para>
+
+ <para>
+ O construtor sem argumentos é um requerimento para todas as classes persistentes;
+ O Hibernate tem que criar para você os objetos usando Java Reflection. O construtor
+ pode ser privado, porém, a visibilidade do pacote é requerida para a procuração da
+ geração em tempo de execução e recuperação eficiente dos dados sem a instrumentação
+ de bytecode
+ </para>
+
+ <para>
+ Coloque este fonte Java no diretório chamado <literal>src</literal> na pasta de desenvolvimento,
+ e em seu pacote correto. O diretório deverá ser parecido como este:
+ </para>
+
+ <programlisting><![CDATA[.
++lib
+ <Hibernate and third-party libraries>
++src
+ +events
+ Event.java]]></programlisting>
+
+ <para>
+ No próximo passo, iremos falar sobre as classes de persistência do Hibernate..
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-mapping" revision="1">
+ <title>O mapeamento do arquivo</title>
+
+ <para>
+ O Hibernate precisa saber como carregar e armazenar objetos da classe de
+ persistência. Isto será onde o mapeamento do arquivo do Hibernate entrará em
+ jogo. O arquivo mapeado informa ao Hibernate, qual tabela no banco de dados
+ ele deverá acessar, e quais as colunas na tabela ele deverá usar.
+ </para>
+
+ <para>
+ A estrutura básica de um arquivo de mapeamento é parecida com:
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping>
+[...]
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Note que o Hibernate DTD é muito sofisticado. Você pode usar isso para auto-conclusão
+ no mapeamento XML dos elementos e atributos no seu editor ou IDE. Você também pode
+ abrir o arquivo DTD no seu editor â é a maneira mais fácil de ter uma visão geral
+ de todos os elementos e atributos e dos padrões, como também alguns comentários.
+ Note que o Hibernate não irá carregar o arquivo DTD da web, e sim do diretório
+ da aplicação (classpath). O arquivo DTD está incluÃdo no <literal>hibernate3.jar</literal> como
+ também no diretório <literal>src/</literal> da distribuição do Hibernate.
+ </para>
+
+ <para>
+ Nós omitiremos a declaração do DTD nos exemplos futuros para encurtar o código. Isto, é claro, não é opcional.
+ </para>
+
+ <para>
+ Entre os dois tags <literal>hibernate-mapping</literal>, inclua um elemento <literal>class</literal>.
+ Todas as classes persistentes da entidade (novamente, poderá haver
+ mais tarde, dependências sobre as classes que não são classes-primárias
+ de entidades) necessitam do tal mapeamento, para uma tabela na base
+ de dados SQL
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="events.Event" table="EVENTS">
+
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Mais adiante iremos dizer ao Hibernate como fazer para persistir e carregar objetos da classe
+ <literal>Event</literal> da tabela <literal>EVENTS</literal>, cada instancia representada por
+ uma coluna na tabela. Agora, continuaremos com o mapeamento de uma única propriedade identificadora
+ para as chaves primárias da tabela. Além disso, nós não iremos se importar com esta propriedade
+ identificadora, nós iremos configurar uma estratégia de geração de idâs para uma chave primária
+ de uma surrogate key:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="events.Event" table="EVENTS">
+ <id name="id" column="EVENT_ID">
+ <generator class="native"/>
+ </id>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ O elemento <literal>id</literal> é a declaração da propriedade identificadora,
+ o <literal>name="id"</literal> declara o nome da propriedade Java â
+ o Hibernate irá usar os métodos getter e setter para acessar a propriedade.
+ O atributo da coluna informa ao Hibernate qual coluna da tabela <literal>EVENTS</literal> nós
+ iremos usar como chave primária. O elemento <literal>generator</literal> especifica
+ a estratégia de geração do identificador, neste caso usaremos <literal>native</literal>, que
+ escolhe a melhor estratégia dependendo da base de dados (dialeto) configurada.
+ O Hibernate suporta a base de dados gerada, globalmente única, bem como a atribuição
+ aos identificadores da aplicação (ou toda estratégia escrita para uma extensão).
+ </para>
+
+ <para>
+ Finalmente incluiremos as declarações para as propriedades persistentes da classe
+ no arquivo mapeado. Por default, nenhuma das propriedades da classe é considerada persistente:
+ </para>
+
+ <programlisting><![CDATA[
+<hibernate-mapping>
+
+ <class name="events.Event" table="EVENTS">
+ <id name="id" column="EVENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="date" type="timestamp" column="EVENT_DATE"/>
+ <property name="title"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Da mesma maneira que com o elemento <literal>id</literal>, o atributo <literal>name</literal> do elemento
+ <literal>property</literal> informa ao Hibernate qual método getter e setter deverá usar.
+ Assim, neste caso, o Hibernate irá procurar pelo <literal>getDate()/setDate()</literal>,
+ como também pelo <literal>getTitle()/setTitle()</literal>.
+ </para>
+
+ <para>
+ Porque fazer o mapeamento da propriedade <literal>date</literal> incluÃdo no
+ atributo <literal>column</literal>, e no title não fazer?
+ Sem o atributo <literal>column</literal> o Hibernate por padrão usa o nome
+ da propriedade como o nome da coluna. Isto trabalha muito
+ bem para o <literal>title</literal>. Entretanto o <literal>date</literal> é uma palavra-chave reservada
+ na maioria dos bancos de dados, assim nós melhoramos o mapeamentos
+ disto com um nome diferente.
+ </para>
+
+ <para>
+ A próxima coisa interessante é que mapemanto do <literal>title</literal>
+ também falta o atributo <literal>type</literal>. O tipo que declaramos e o uso nos
+ arquivos mapeados, não são como você pôde esperar, atributos de dados Java.
+ Eles não são como os tipos de base de dados SQL.
+ Esses tipos podem ser chamados de <emphasis>Tipos de mapeamento Hibernate</emphasis>, que são conversores
+ que podem traduzir tipos de dados do Java para os tipos de dados SQL e vice-versa.
+ Novamente, o Hibernate irá tentar determinar a conversão correta e mapeará o <literal>type</literal>
+ próprio, caso o tipo do atributo não estiver presente no mapeamento.
+ Em alguns casos, esta detecção automática (que usa Reflection sobre as classes Java)
+ poderá não ter padrão que você espera ou necessita.
+ Este é o caso com a propriedade <literal>date</literal>. O Hibernate não pode saber se a propriedade
+ (que é do <literal>java.util.Date</literal>) pode mapear para uma coluna do tipo <literal>date</literal>
+ do SQL, <literal>timestamp</literal>, ou <literal>time</literal> .
+ Nós preservamos a informação cheia de datas e horas pelo mapeamento da propriedade com um conversor
+ <literal>timestamp</literal>.
+ </para>
+
+ <para>
+ Este arquivo de mapeamento deve ser salvo como <literal>Event.hbm.xml</literal>,
+ corretamente no diretório próximo ao arquivo fonte da Classe Java <literal>Event</literal>.
+ O nomeamento dos arquivos de mapeamento podem ser arbitrários, porém o sufixo
+ <literal>hbm.xml</literal> é uma convenção da comunidade dos desenvolvedores do Hibernate.
+ Esta estrutura do diretório deve agora se parecer com isso:
+ </para>
+
+ <programlisting><![CDATA[.
++lib
+ <Hibernate and third-party libraries>
++src
+ +events
+ Event.java
+ Event.hbm.xml]]></programlisting>
+
+ <para>
+ Nós iremos continuar com a configuração principal do Hibernate.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-configuration" revision="2">
+ <title>Configuração do Hibernate</title>
+
+ <para>
+ Agora nós temos uma classe persistente e este arquivo de mapeamento no lugar.
+ Está na hora de configurar o Hibernate. Antes de fazermos isso, iremos precisar de uma base de dados.
+ O HSQL DB, um SQL DBMS feito em java, pode ser baixado através do site do HSQL DB.
+ Atualmente, você só precisa baixar o <literal>hsqldb.jar</literal>.
+ Coloque este arquivo no diretório da pasta de desenvolvimento <literal>lib/</literal>.
+ </para>
+
+ <para>
+ Crie um diretório chamado <literal>data</literal> no diretório root de desenvolvimento â
+ Isto será onde o HSQL DB irá armazenar arquivos de dados. Agora iremos iniciar o banco de dados
+ executando <literal>java -classpath ../lib/hsqldb.jar org.hsqldb.Server</literal> neste diretório de dados.
+ Você pode ver ele iniciando e conectando ao socket TCP/IP, isto será onde nossa aplicação irá se
+ conectar depois. Se você deseja iniciar uma nova base de dados durante este tutorial,
+ finalize o HSQL DB(pressionando o <literal>CTRL + C</literal> na janela), delete todos os
+ arquivos no diretório <literal>data/</literal>, e inicie o HSQL BD novamente.
+ </para>
+
+ <para>
+ O Hibernate é uma camada na sua aplicação na qual se conecta com a base de dados, para isso
+ necessita de informação da conexão. As conexões são feitas através de um pool de conexão JDBC,
+ na qual teremos que configurar. A distribuição do Hibernate contém diversas ferramentas de pooling
+ da conexão JDBC de fonte aberta, mas iremos usar o pool de conexão interna para este tutorial.
+ Note que você tem que copiar a biblioteca necessária em seu classpath e use configurações
+ diferentes para pooling de conexão caso você deseje utilizar um software de pooling JDBC terceirizado
+ para qualidade de produção.
+ </para>
+
+ <para>
+ Para as configurações do Hibernate, nós podemos usar um arquivo simples <literal>hibernate.properties</literal>,
+ um arquivo mais ligeiramente sofisticado <literal>hibernate.cfg.xml</literal> ou até mesmo uma
+ instalação programática completa. A maioria dos usuários preferem utilizar o arquivo de configuração XML
+ </para>
+
+ <programlisting><![CDATA[<?xml version='1.0' encoding='utf-8'?>
+<!DOCTYPE hibernate-configuration PUBLIC
+ "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
+
+<hibernate-configuration>
+
+ <session-factory>
+
+ <!-- Database connection settings -->
+ <property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
+ <property name="connection.url">jdbc:hsqldb:hsql://localhost</property>
+ <property name="connection.username">sa</property>
+ <property name="connection.password"></property>
+
+ <!-- JDBC connection pool (use the built-in) -->
+ <property name="connection.pool_size">1</property>
+
+ <!-- SQL dialect -->
+ <property name="dialect">org.hibernate.dialect.HSQLDialect</property>
+
+ <!-- Enable Hibernate's automatic session context management -->
+ <property name="current_session_context_class">thread</property>
+
+ <!-- Disable the second-level cache -->
+ <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
+
+ <!-- Echo all executed SQL to stdout -->
+ <property name="show_sql">true</property>
+
+ <!-- Drop and re-create the database schema on startup -->
+ <property name="hbm2ddl.auto">create</property>
+
+ <mapping resource="events/Event.hbm.xml"/>
+
+ </session-factory>
+
+</hibernate-configuration>]]></programlisting>
+
+ <para>
+ Note que esta configuração XML usa um diferente DTD. Nós configuraremos
+ as <literal>SessionFactory</literal> do Hibernate â uma factory global responsável
+ por uma base de dedados particular. Se você tiver diversas bases de dados,
+ use diversas configurações <literal><session-factory></literal>, geralmente
+ em diversos arquivos de configuração (para uma partida mais fácil).
+ </para>
+
+ <para>
+ As primeiras quatro <literal>propriedades</literal> do elemento contém a configuração
+ necessária para a conexão ao JDBC. A propriedade <literal>propriedade</literal> dialect
+ do elemento especifica a variante particular do SQL que o Hibernate gera.
+ O gerenciamento automático de sessão do Hibernate para contextos de persistência
+ estará disponÃvel em breve. A opção <literal>hbm2ddl.auto</literal> habilita a geração
+ automática de schemas da base de dados â diretamente na base de dados.
+ Isto também pode ser naturalmente desligado (removendo a opção de configuração) ou redirecionando
+ para um arquivo com ajuda do <literal>SchemaExport</literal> nas tarefas do Ant.
+ Finalmente, iremos adicionar os arquivos das classes de persistência mapeadas na configuração.
+ </para>
+
+ <para>
+ Copie este arquivo no diretório fonte, assim isto irá terminar na raiz (root) do
+ classpath. O Hibernate automaticamente procura por um arquivo chamado
+ <literal>hibernate.cfg.xml</literal> na raiz do classpath, no startup.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-ant" revision="1">
+ <title>Construindo com o Ant</title>
+
+ <para>
+ Nos iremos, agora, construir o tutorial com Ant. Você ira precisar o Ant instalado â
+ se encontra disponÃvel <ulink url="http://ant.apache.org/bindownload.cgi">na página de download do Ant</ulink>.
+ Como instalar o Ant, não será abordado aqui. Caso tenha alguma dúvida, por favor,
+ vá ao <ulink url="http://ant.apache.org/manual/index.html">Ant manual</ulink>.
+ Depois que tiver instalado o Ant, podemos começar a criar o arquivo de construção <literal>build.xml</literal>.
+ Este arquivo será chamado de <literal>build.xml</literal> e posto diretamente no diretório de desenvolvimento.
+ </para>
+
+ <para>
+ Um arquivo básico de build, se parece com isto:
+ </para>
+
+ <programlisting><![CDATA[<project name="hibernate-tutorial" default="compile">
+
+ <property name="sourcedir" value="${basedir}/src"/>
+ <property name="targetdir" value="${basedir}/bin"/>
+ <property name="librarydir" value="${basedir}/lib"/>
+
+ <path id="libraries">
+ <fileset dir="${librarydir}">
+ <include name="*.jar"/>
+ </fileset>
+ </path>
+
+ <target name="clean">
+ <delete dir="${targetdir}"/>
+ <mkdir dir="${targetdir}"/>
+ </target>
+
+ <target name="compile" depends="clean, copy-resources">
+ <javac srcdir="${sourcedir}"
+ destdir="${targetdir}"
+ classpathref="libraries"/>
+ </target>
+
+ <target name="copy-resources">
+ <copy todir="${targetdir}">
+ <fileset dir="${sourcedir}">
+ <exclude name="**/*.java"/>
+ </fileset>
+ </copy>
+ </target>
+
+</project>]]></programlisting>
+
+ <para>
+ Isto irá avisar ao Ant para adicionar todos os arquivos no diretório lib terminando com
+ <literal>.jar</literal>, para o classpath usado para compilação. Irá também copiar todos os
+ arquivos não-java para o diretório alvo (arquivos de configuração, mapeamento). Se você rodar
+ o ant agora, deverá ter esta saÃda.
+ </para>
+
+ <programlisting><![CDATA[C:\hibernateTutorial\>ant
+Buildfile: build.xml
+
+copy-resources:
+ [copy] Copying 2 files to C:\hibernateTutorial\bin
+
+compile:
+ [javac] Compiling 1 source file to C:\hibernateTutorial\bin
+
+BUILD SUCCESSFUL
+Total time: 1 second ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-helpers" revision="3">
+ <title>Startup and helpers</title>
+
+ <para>
+ Ã hora de carregar e arquivar alguns objetos <literal>Event</literal>, mas primeiro
+ nós temos de completar o setup com algum código de infraestrutura. Este startup
+ inclui a construção de um objeto <literal>SessionFactory</literal> global e armazenar
+ isto em algum lugar de fácil acesso para o código da aplicação.
+ Uma <literal>SessionFactory</literal> pode abrir novas <literal>Session</literal>'s.
+ Uma <literal>Session</literal> representa uma unidade single-theaded do trabalho, a
+ <literal>SessionFactory</literal> é um objeto global thread-safe, instanciado uma vez.
+ </para>
+
+ <para>
+ Nos iremos criar uma classe de ajuda <literal>HibernateUtil</literal>, que toma
+ conta do startup e faz acesso a uma <literal>SessionFactory</literal> conveniente.
+ Vamos dar uma olhada na implementação:
+ </para>
+
+ <programlisting><![CDATA[package util;
+
+import org.hibernate.*;
+import org.hibernate.cfg.*;
+
+public class HibernateUtil {
+
+ private static final SessionFactory sessionFactory;
+
+ static {
+ try {
+ // Create the SessionFactory from hibernate.cfg.xml
+ sessionFactory = new Configuration().configure().buildSessionFactory();
+ } catch (Throwable ex) {
+ // Make sure you log the exception, as it might be swallowed
+ System.err.println("Initial SessionFactory creation failed." + ex);
+ throw new ExceptionInInitializerError(ex);
+ }
+ }
+
+ public static SessionFactory getSessionFactory() {
+ return sessionFactory;
+ }
+
+}]]></programlisting>
+
+ <para>
+ Esta classe não só produz a global <literal>SessionFactory</literal> no seu static initializer
+ (chamado uma vez pela JVM quando a classe é carregada), mas também esconde o fato
+ de que isto usa um static singleton. Ela pode muito bem, enxergar a
+ <literal>SessionFactory</literal> do JNDI em um application server.
+ </para>
+
+ <para>
+ Se você der à <literal>SessionFactory</literal> um nome, no seu arquivo de configuração.
+ O Hibernate irá, de fato, tentar uni-lo ao JNDI depois que estiver construÃdo.
+ Para evitar este completamente este código, você também poderia usar JMX deployment
+ e deixar o contêiner JMX capaz, instanciar e unir um <literal>HibernateService</literal>
+ no JNDI. Essas opções avançadas são discutidas no documento de referência do Hibernate.
+ </para>
+
+ <para>
+ Coloque o <literal>HibernateUtil.java</literal> no diretório de arquivos
+ de desenvolvimento(source), em um pacote após o <literal>events</literal>:
+ </para>
+
+ <programlisting><![CDATA[.
++lib
+ <Hibernate and third-party libraries>
++src
+ +events
+ Event.java
+ Event.hbm.xml
+ +util
+ HibernateUtil.java
+ hibernate.cfg.xml
++data
+build.xml]]></programlisting>
+
+ <para>
+ Novamente, isto deve compilar sem problemas. Finalmente, nós precisamos configurar
+ um sistema de logging â o Hibernate usa commons logging e deixa você escolher entre o
+ Log4j e o logging do JDK 1.4 . A maioria dos desenvolvedores preferem o Log4j: copie
+ <literal>log4j.properties</literal> da distribuição do Hibernate (está no diretório
+ <literal>etc/</literal>), para seu diretório <literal>src</literal>,
+ depois vá em hibernate.cfg.xml. Dê uma olhada no exemplo de configuração e mude as
+ configurações se você quizer ter uma saÃda mais detalhada. Por default, apenas as
+ mensagems de startup e shwwn do Hibernate é mostrada no stdout.
+ </para>
+
+ <para>
+ O tutorial de infra-estrutura está completo - e nós já estamos preparados para algum
+ trabalho de verdade com o Hibernate.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-workingpersistence" revision="4">
+ <title>Carregando e salvando objetos</title>
+
+ <para>
+ Finalmente, nós podemos usar o Hibernate para carregar e armazenar objetos.
+ Nós escrevemos uma classe <literal>EventManager</literal> com um método main():
+ </para>
+
+ <programlisting><![CDATA[package events;
+import org.hibernate.Session;
+
+import java.util.Date;
+
+import util.HibernateUtil;
+
+public class EventManager {
+
+ public static void main(String[] args) {
+ EventManager mgr = new EventManager();
+
+ if (args[0].equals("store")) {
+ mgr.createAndStoreEvent("My Event", new Date());
+ }
+
+ HibernateUtil.getSessionFactory().close();
+ }
+
+ private void createAndStoreEvent(String title, Date theDate) {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+
+ session.beginTransaction();
+
+ Event theEvent = new Event();
+ theEvent.setTitle(title);
+ theEvent.setDate(theDate);
+
+ session.save(theEvent);
+
+ session.getTransaction().commit();
+ }
+
+}]]></programlisting>
+
+ <para>
+ Nós criamos um novo objeto <literal>Event</literal>, e passamos para o Hibernate.
+ O Hibernate sabe como tomar conta do SQL e executa <literal>INSERT</literal>s
+ no banco de dados. Vamos dar uma olhada na <literal>Session</literal> e no
+ código <literal>Transaction</literal>-handling antes de executarmos.
+ </para>
+
+ <para>
+ Um <literal>Session</literal> é uma unidade simples de trabalho. Por agora nós
+ iremos pegar coisas simples e assumir uma granularidade de um-pra-um entre uma
+ <literal>Session</literal> do Hibernate e uma transação de banco de dados.
+ Para proteger nosso código de um atual sistema subjacente de transação (nesse
+ caso puro JDBC, mas também poderia rodar com JTA), nos usamos a API
+ <literal>Transaction</literal>, que está disponÃvel na <literal>Session</literal> do Hibernate.
+ </para>
+
+ <para>
+ O que a <literal>sessionFactory.getCurrentSession()</literal> faz? Primeiro, você pode
+ chamar quantas vezes e de onde quiser, uma vez você recebe sua <literal>SessionFactory</literal>
+ (fácil graças ao <literal>HibernateUtil</literal>). O método <literal>getCurrentSession()</literal>
+ sempre retorna a unidade de trabalho "corrente". Lembra de que nós mudamos a opção
+ de configuração desse mecanismo para thread no <literal>hibernate.cfg.xml</literal>? Daqui em
+ diante, o escopo da unidade de trabalho corrente é a thread Java
+ corrente que executa nossa aplicação. Entretanto, esta não é toda a verdade. Uma
+ <literal>Session</literal> começa quando é primeiramente necessária, quando é feita a
+ primeira chamada à <literal>getCurrentSession()</literal>. à então limitado pelo Hibernate
+ para thread corrente. Quando a transação termina, tanto com commit quanto rollback,
+ o Hibernate também desune a <literal>Session</literal> da thread e fecha isso pra você.
+ Se você chamar <literal>getCurrentSession()</literal> novamente, você receberá uma nova
+ <literal>Session</literal> e pode começar uma nova unidade de trabalho. Esse modelo de
+ programação de limite de thread <emphasis>thread-bound</emphasis>, é o modo mais popular
+ de se usar o Hibernate.
+ </para>
+
+ <para>
+ Dê uma olhada no <xref linkend="transactions"/> para mais informações a
+ respeito de manipulação de transação e demarcação. Nós também pulamos qualquer
+ manipulação de erro e rollback no exemplo anterior.
+ </para>
+
+ <para>
+ Para executar esta primeira rotina, nos teremos que adicionar um ponto de chamada
+ para o arquivo de build do Ant:
+ </para>
+
+ <programlisting><![CDATA[<target name="run" depends="compile">
+ <java fork="true" classname="events.EventManager" classpathref="libraries">
+ <classpath path="${targetdir}"/>
+ <arg value="${action}"/>
+ </java>
+</target>]]></programlisting>
+
+ <para>
+ O valor do argumento <literal>action</literal>, é setado na linha de comando quando chamando esse ponto:
+ </para>
+
+ <programlisting><![CDATA[C:\hibernateTutorial\>ant run -Daction=store]]></programlisting>
+
+ <para>
+ Você deverá ver, após a compilação, o startup do Hibernate e, dependendo da sua
+ configuração, muito log de saÃda. No final você verá a seguinte linha:
+ </para>
+
+ <programlisting><![CDATA[[java] Hibernate: insert into EVENTS (EVENT_DATE, title, EVENT_ID) values (?, ?, ?)]]></programlisting>
+
+ <para>
+ Este é o <literal>INSERT</literal> executado pelo Hibernate, os pontos de interrogação
+ representam parêmetros de união do JDBC. Para ver os valores substituÃdos, ou para diminuir a
+ verbalidade do log, check seu l<literal>log4j.properties</literal>.
+ </para>
+
+ <para>
+ Agora nós gostarÃamos de listar os eventos arquivados, então nós adicionamos uma
+ opção para o método main:
+ </para>
+
+ <programlisting><![CDATA[if (args[0].equals("store")) {
+ mgr.createAndStoreEvent("My Event", new Date());
+}
+else if (args[0].equals("list")) {
+ List events = mgr.listEvents();
+ for (int i = 0; i < events.size(); i++) {
+ Event theEvent = (Event) events.get(i);
+ System.out.println("Event: " + theEvent.getTitle() +
+ " Time: " + theEvent.getDate());
+ }
+}]]></programlisting>
+
+ <para>
+ Nos também adicionamos um novo <literal>método listEvents()</literal>:
+ </para>
+
+ <programlisting><![CDATA[private List listEvents() {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+
+ session.beginTransaction();
+
+ List result = session.createQuery("from Event").list();
+
+ session.getTransaction().commit();
+
+ return result;
+}]]></programlisting>
+
+ <para>
+ O que nós fazemos aqui, é usar uma query HQL (Hibernate Query Language),
+ para carregar todos os objetos <literal>Event</literal> exitentes no banco de dados.
+ O Hibernate irá gerar o SQL apropriado, enviar para o banco de dados e popular objetos
+ <literal>Event</literal> com os dados. Você pode criar queries mais complexas com
+ HQL, claro.
+ </para>
+
+ <para>
+ Agora, para executar e testar tudo isso, siga os passos a seguir:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ Execute <literal>ant run -Daction=store</literal> para armazenar algo no banco de dados
+ e, claro, gerar o esquema do banco de dados antes pelo hbm2ddl.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Agora desabilite hbm2ddl comentando a propriedade no seu arquivo <literal>hibernate.cfg.xml</literal>.
+ Normalmente só se deixa habilitado em teste unitários contÃnuos, mas outra carga de hbm2ddl
+ pode <emphasis>remover</emphasis> tudo que você já tenha arquivado. Sa configuração
+ <literal>create</literal>, atualmente são traduzidas para "apague todas as tabelas do esquema,
+ então recrie todas quando a SessionFactory estiver pronta".
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Se você agora chamar o Ant com <literal>-Daction=list</literal>, você deverá ver os
+ eventos que você acabou de criar. Você pode também chamar a ação <literal>store</literal>
+ mais algumas vezes.
+ </para>
+
+ <para>
+ Nota: A maioria dos novos usuários do Hibernate falha nesse ponto e nós regularmente, vemos
+ questões sobre mensagens de erro de <emphasis>tabela não encontrada </emphasis> .
+ Entretanto, se você seguir os passos marcados acima, você não terá esse problema,
+ com o hbm2ddl criando o esquema do banco de dados na primeira execução, e restarts
+ subsequentes da aplicação irão usar este esquema. Se você mudar o mapeamento e/ou
+ o esquema do banco de dados, terá de re-habilitar o hbm2ddl mais uma vez.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="tutorial-associations">
+ <title>Part 2 - Mapeando associações</title>
+
+ <para>
+ Nós mapeamos uma classe de entidade de persistência para uma tabela. Agora vamos continuar
+ e adicionar algumas associações de classe. Primeiro nos iremos adicionar pessoas a nossa aplicação,
+ e armazenar os eventos de que elas participam.
+ </para>
+
+ <sect2 id="tutorial-associations-mappinguser" revision="1">
+ <title>Mapeando a classe Person</title>
+
+ <para>
+ O primeiro código da classe <literal>Person</literal> é simples:
+ </para>
+
+ <programlisting><![CDATA[package events;
+
+public class Person {
+
+ private Long id;
+ private int age;
+ private String firstname;
+ private String lastname;
+
+ public Person() {}
+
+ // Accessor methods for all properties, private setter for 'id'
+
+}]]></programlisting>
+
+ <para>
+ Crie um novo arquivo de mapeamento, chamado <literal>Person.hbm.xml</literal> (não
+ esqueça a referencia ao DTD no topo)
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="events.Person" table="PERSON">
+ <id name="id" column="PERSON_ID">
+ <generator class="native"/>
+ </id>
+ <property name="age"/>
+ <property name="firstname"/>
+ <property name="lastname"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Finalmente, adicione o novo mapeamento a configuração do Hibernate:
+ </para>
+
+ <programlisting><![CDATA[<mapping resource="events/Event.hbm.xml"/>
+<mapping resource="events/Person.hbm.xml"/>]]></programlisting>
+
+ <para>
+ Nos iremos agora criar uma associação entre estas duas entidades. Obviamente,
+ pessoas (Person) podem participar de eventos, e eventos possuem participantes.
+ As questões de design com que teremos de lidar são: direcionalidade, multiplicidade e
+ comportamento de coleção.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-unidirset" revision="3">
+ <title>Uma associação Set-based unidirectional</title>
+
+ <para>
+ Nos iremos adicionar uma coleção de eventos na classe <literal>Person</literal>. Desse jeito
+ poderemos navegar pelos eventos de uma pessoa em particular, sem executar uma query explicitamente â
+ apenas chamando <literal>aPerson.getEvents()</literal>. Nos usaremos uma coleção Java, um
+ <literal>Set</literal>, porquê a coleção não conterá elementos duplicados e a ordem não é
+ relevante para nós.
+ </para>
+
+ <para>
+ Vamos escrever o código para isto nas classes Java e então mapear:
+ </para>
+
+ <programlisting><![CDATA[public class Person {
+
+ private Set events = new HashSet();
+
+ public Set getEvents() {
+ return events;
+ }
+
+ public void setEvents(Set events) {
+ this.events = events;
+ }
+}]]></programlisting>
+
+ <para>
+ Antes de mapearmos esta associação, pense no outro lado. Claramente, poderÃamos apenas fazer isto de
+ forma unidirecional. Ou poderÃamos criar outra coleção no <literal>Event</literal>, se quisermos
+ ser capaz de navegar bidirecionalmente, i.e. um - <literal>anEvent.getParticipants()</literal>.
+ Isto não é necessário, de perspectiva funcional. Você poderia sempre executar uma query explicita
+ que retornasse os participantes de um evento em particular. Esta é uma escolha de design que cabe
+ a você, mas o que é claro nessa discussão é a multiplicidade da associação: "muitos" valores em ambos
+ os lados, nós chamamos isto uma associação <emphasis>muitos-para-muitos</emphasis>. Daqui pra frente,
+ nos usaremos o mapeamento muitos-para-muitos do Hibernate:
+ </para>
+
+ <programlisting><![CDATA[<class name="events.Person" table="PERSON">
+ <id name="id" column="PERSON_ID">
+ <generator class="native"/>
+ </id>
+ <property name="age"/>
+ <property name="firstname"/>
+ <property name="lastname"/>
+
+ <set name="events" table="PERSON_EVENT">
+ <key column="PERSON_ID"/>
+ <many-to-many column="EVENT_ID" class="events.Event"/>
+ </set>
+
+</class>]]></programlisting>
+
+ <para>
+ O Hibernate suporta todo tipo de mapeamento de coleção , sendo um <literal><set></literal> mais comum.
+ Para uma associação muitos-para-muitos (ou relacionamento de entidade <emphasis>n:m</emphasis> ),
+ uma tabela de associação é necessária. Cada linha nessa tabela representa um link entre uma pessoa e um
+ evento. O nome da tabela é configurado com o atributo <literal>table</literal> do elemento
+ <literal>set</literal>. O nome da coluna identificadora na associção, peloo lado da pessoa,
+ é definido com o elemento <literal><key></literal> , o nome da coluna pelo lado dos eventos,
+ e definido com o atributo <literal>column</literal> do <literal><many-to-many></literal>.
+ Você também precisa dizer para o Hibernate a classe dos objetos na sua coleção (a classe do outro
+ lado das coleções de referência).
+ </para>
+
+ <para>
+ O esquema de mapeamento para o banco de dados está a seguir:
+ </para>
+
+ <programlisting><![CDATA[
+ _____________ __________________
+ | | | | _____________
+ | EVENTS | | PERSON_EVENT | | |
+ |_____________| |__________________| | PERSON |
+ | | | | |_____________|
+ | *EVENT_ID | <--> | *EVENT_ID | | |
+ | EVENT_DATE | | *PERSON_ID | <--> | *PERSON_ID |
+ | TITLE | |__________________| | AGE |
+ |_____________| | FIRSTNAME |
+ | LASTNAME |
+ |_____________|
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-working" revision="1">
+ <title>Trabalhando a associação</title>
+
+ <para>
+ Vamos trazer juntos algumas pessoas e eventos em um novo método na classe <literal>EventManager</literal>::
+ </para>
+
+ <programlisting><![CDATA[private void addPersonToEvent(Long personId, Long eventId) {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+ session.beginTransaction();
+
+ Person aPerson = (Person) session.load(Person.class, personId);
+ Event anEvent = (Event) session.load(Event.class, eventId);
+
+ aPerson.getEvents().add(anEvent);
+
+ session.getTransaction().commit();
+}]]></programlisting>
+
+ <para>
+ Após carregar um <literal>Person</literal> e um <literal>Event</literal>, simplesmente
+ modifique a coleção usando os métodos normais de uma coleção. Como você pode ver, não há chamada explÃcita
+ para <literal>update()</literal> ou <literal>save()</literal>, o Hibernate detecta automaticamente
+ que a coleção foi modificada e necessita ser atualizada. Isso é chamado de <emphasis>checagem
+ suja automática</emphasis>, e você também pode usá-la modificando o nome ou a data de qualquer um dos
+ seus objetos. Assim que eles estiverem no estado <emphasis>persistent</emphasis>, ou seja,
+ limitado por uma <literal>Session</literal> do Hibernate em particular (i.e. eles foram carregados ou
+ salvos dentro de uma unidade de trabalho), o Hibernate monitora qualquer alteração e executa o SQL
+ em modo de escrita em segundo plano. O processo de sincronização do estado da memória com o banco de
+ dados, geralmente apenas no final de uma unidade de trabalho, é chamado de <emphasis>flushing</emphasis>.
+ No nosso código, a unidade de trabalho termina com o commit da transação do banco de dados â
+ como definido pela opção de configuração da <literal>thread</literal> da classe <literal>CurrentSessionContext</literal>.
+ </para>
+
+ <para>
+ Você pode também querer carregar pessoas e eventos em diferentes unidades de trabalho.
+ Ou você modifica um objeto fora de uma <literal>Session</literal>, quando não se encontra no
+ estado persistent (se já esteve neste estado anteriormente, chamamos esse estado de
+ <emphasis>detached</emphasis>). Você pode até mesmo modificar uma coleção quando esta
+ se encontrar no estado detached.
+ </para>
+
+ <programlisting><![CDATA[private void addPersonToEvent(Long personId, Long eventId) {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+ session.beginTransaction();
+
+ Person aPerson = (Person) session
+ .createQuery("select p from Person p left join fetch p.events where p.id = :pid")
+ .setParameter("pid", personId)
+ .uniqueResult(); // Eager fetch the collection so we can use it detached
+
+ Event anEvent = (Event) session.load(Event.class, eventId);
+
+ session.getTransaction().commit();
+
+ // End of first unit of work
+
+ aPerson.getEvents().add(anEvent); // aPerson (and its collection) is detached
+
+ // Begin second unit of work
+
+ Session session2 = HibernateUtil.getSessionFactory().getCurrentSession();
+ session2.beginTransaction();
+
+ session2.update(aPerson); // Reattachment of aPerson
+
+ session2.getTransaction().commit();
+}]]></programlisting>
+
+ <para>
+ A chamada <literal>update</literal> cria um objeto persistent novamente, você poderia
+ dizer que ele liga o objeto a uma nova unidade de trabalho, assim qualquer modificação
+ que você faça neste objeto enquanto estiver no estado detached pode ser salvo no banco de dados.
+ Isso inclui qualquer modificação (adição/exclusão) que você faça em uma coleção da entidade deste objeto.
+ </para>
+
+ <para>
+ Bom, isso não foi muito usado na nossa situação, porém, é um importante conceito que você
+ pode aplicar em seus aplicativos. Agora, complete este exercÃcio adicionando uma nova ação
+ ao método main( ) da classe <literal>EventManager</literal> e chame-o pela linha de comando.
+ Se você precisar dos identificadores de uma pessoa ou evento â o método <literal>save()</literal>
+ retorna estes identificadores (você poderá modificar alguns dos métodos anteriores para retornar aquele
+ identificador):
+ </para>
+
+ <programlisting><![CDATA[else if (args[0].equals("addpersontoevent")) {
+ Long eventId = mgr.createAndStoreEvent("My Event", new Date());
+ Long personId = mgr.createAndStorePerson("Foo", "Bar");
+ mgr.addPersonToEvent(personId, eventId);
+ System.out.println("Added person " + personId + " to event " + eventId);]]></programlisting>
+
+ <para>
+ Este foi um exemplo de uma associação entre duas classes igualmente importantes, duas entidades.
+ Como mencionado anteriormente, há outras classes e tipos dentro de um modelo tÃpico,
+ geralmente "menos importante". Alguns você já viu, como um <literal>int</literal> ou uma <literal>String</literal>.
+ Nós chamamos essas classes de <emphasis>value types</emphasis>, e suas instâncias <emphasis>depend</emphasis>
+ de uma entidade particular. As instâncias desses tipos não possuem sua própria identidade, nem são
+ compartilhados entre entidades (duas pessoas não referenciam o mesmo objeto <literal>firstname</literal>
+ mesmo se elas tiverem o mesmo objeto firstname). Naturalmente, os value types não são apenas encontrados
+ dentro da JDK (de fato, em um aplicativo Hibernate todas as classes JDK são consideradas como value types),
+ mas você pode também criar suas classes como, por exemplo, <literal>Address</literal> ou <literal>MonetaryAmount</literal>.
+
+ </para>
+
+ <para>
+ Você também pode criar uma coleção de value types. Isso é conceitualmente muito diferente
+ de uma coleção de referências para outras entidades, mas em Java parece ser quase a mesma coisa.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-valuecollections">
+ <title>Coleção de valores</title>
+
+ <para>
+ Nós adicionamos uma coleção de objetos de tipo de valores à entidade <literal>Person</literal>.
+ Nós querermos armazenar endereços de e-mail, para isso utilizamos o tipo <literal>String</literal>,
+ e a coleção novamente será um <literal>Set</literal>:
+ </para>
+ <programlisting><![CDATA[private Set emailAddresses = new HashSet();
+
+public Set getEmailAddresses() {
+ return emailAddresses;
+}
+
+public void setEmailAddresses(Set emailAddresses) {
+ this.emailAddresses = emailAddresses;
+}]]></programlisting>
+
+ <para>
+ O mapeamento deste <literal>Set</literal>:
+ </para>
+
+ <programlisting><![CDATA[<set name="emailAddresses" table="PERSON_EMAIL_ADDR">
+ <key column="PERSON_ID"/>
+ <element type="string" column="EMAIL_ADDR"/>
+</set>]]></programlisting>
+
+ <para>
+ A diferença comparada com o mapeamento anterior se encontra na parte <literal>element</literal>,
+ que indica ao Hibernate que a coleção não contém referências à outra entidade, mas uma coleção de
+ elementos do tipo <literal>String</literal> (a tag name em miniscula indica que se trata de um
+ mapeamento do Hibernate para conversão de tipos). Mais uma vez, o atributo <literal>table</literal>
+ do elemento <literal>set</literal> determina o nome da tabela para a coleção. O elemento
+ <literal>key</literal> define o nome da coluna de chave estrangeira na tabela de coleção.
+ O atributo <literal>column</literal> dentro do elemento <literal>element</literal> define o
+ nome da coluna onde os valores da <literal>String</literal> serão armazenados.
+ </para>
+
+ <para>
+ Dê uma olhada no esquema atualizado:
+ </para>
+
+ <programlisting><![CDATA[
+ _____________ __________________
+ | | | | _____________
+ | EVENTS | | PERSON_EVENT | | | ___________________
+ |_____________| |__________________| | PERSON | | |
+ | | | | |_____________| | PERSON_EMAIL_ADDR |
+ | *EVENT_ID | <--> | *EVENT_ID | | | |___________________|
+ | EVENT_DATE | | *PERSON_ID | <--> | *PERSON_ID | <--> | *PERSON_ID |
+ | TITLE | |__________________| | AGE | | *EMAIL_ADDR |
+ |_____________| | FIRSTNAME | |___________________|
+ | LASTNAME |
+ |_____________|
+ ]]></programlisting>
+
+ <para>
+ Você pode observar que a chave primária da tabela da coleção é de na verdade uma chave composta,
+ usando ambas colunas. Isso também implica que cada pessoa não pode ter endereços de e-mail
+ duplicados, o que é exatamente a semântica que precisamos para um set em Java.
+ </para>
+
+ <para>
+ Você pode agora tentar adicionar elementos a essa coleção, do mesmo modo que fizemos
+ anteriormente ligando pessoas e eventos. à o mesmo código em Java:
+ </para>
+
+ <programlisting><![CDATA[private void addEmailToPerson(Long personId, String emailAddress) {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+ session.beginTransaction();
+
+ Person aPerson = (Person) session.load(Person.class, personId);
+
+ // The getEmailAddresses() might trigger a lazy load of the collection
+ aPerson.getEmailAddresses().add(emailAddress);
+
+ session.getTransaction().commit();
+}]]></programlisting>
+
+ <para>
+ This time we didnt' use a <emphasis>fetch</emphasis> query to initialize the collection.
+ Hence, the call to its getter method will trigger an additional select to initialize
+ it, so we can add an element to it. Monitor the SQL log and try to optimize this with
+ an eager fetch.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-bidirectional" revision="1">
+ <title>Associações bidirecionais</title>
+
+ <para>
+ Agora iremos mapear uma associação bidirecional â fazendo a associação entre pessoas e
+ eventos, de ambos os lados, em Java. Logicamente, o esquema do banco de dados não muda,
+ nós continuamos tendo multiplicidades muitos-para-muitos. Um banco de dados é mais flexÃvel do que
+ uma linguagem de programação para redes, ele não precisa de nenhuma direção de navegação â os
+ dados podem ser acessados em qualquer caminho possÃvel.
+ </para>
+
+ <para>
+ Primeiramente, adicione uma coleção de participantes à classe <literal>Event</literal>:
+ </para>
+
+ <programlisting><![CDATA[private Set participants = new HashSet();
+
+public Set getParticipants() {
+ return participants;
+}
+
+public void setParticipants(Set participants) {
+ this.participants = participants;
+}]]></programlisting>
+
+ <para>
+ Agora mapeie este lado da associação em <literal>Event.hbm.xml</literal>.
+ </para>
+
+ <programlisting><![CDATA[<set name="participants" table="PERSON_EVENT" inverse="true">
+ <key column="EVENT_ID"/>
+ <many-to-many column="PERSON_ID" class="events.Person"/>
+</set>]]></programlisting>
+
+ <para>
+ Como você pode ver, esses é uma mapeamento normal usando <literal>set</literal> em ambos documenentos
+ de mapeamento. Observe que o nome das colunas em <literal>key</literal> e <literal>many-to-many</literal>
+ estão trocados em ambos os documentos de mapeamento. A adição mais importante feita está no atributo
+ <literal>inverse="true"</literal> no elemento set do mapeamento da coleção da classe <literal>Event</literal>.
+ </para>
+
+ <para>
+ Isso significa que o Hibernate deve pegar o outro lado â a classe <literal>Person</literal> â
+ quando necessitar encontrar informação sobre a relação entre as duas entidades. Isso será muito
+ mais facilmente compreendido quando você analisar como a relação bidirecional entre as entidades é criada.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-usingbidir">
+ <title>Trabalhando com links bidirecionais</title>
+
+ <para>
+ Primeiro tenha em mente que o Hibernate não afeta a semântica normal do Java. Como nós criamos
+ um link entre uma <literal>Person</literal> e um <literal>Event</literal> no exemplo unidirecional?
+ Nós adicionamos uma instância de <literal>Event</literal>, da coleção de referências de eventos,
+ a uma instância de <literal>Person</literal>. Então, obviamente, se nós queremos que este link funcione
+ bidirecionalmente, nós devemos fazer a mesma coisa para o outro lado â adicionando uma referência de
+ <literal>Person</literal> na coleção de um <literal>Event</literal>. Esse acerto de link de ambos
+ os lados é absolutamente necessário e você nunca deve esquecer de faze-lo.
+ </para>
+
+ <para>
+ Muitos desenvolvedores programam de maneira defensiva e criam métodos
+ gerenciador de associações que ajusta corretamente ambos os lados:
+ </para>
+
+ <programlisting><![CDATA[protected Set getEvents() {
+ return events;
+}
+
+protected void setEvents(Set events) {
+ this.events = events;
+}
+
+public void addToEvent(Event event) {
+ this.getEvents().add(event);
+ event.getParticipants().add(this);
+}
+
+public void removeFromEvent(Event event) {
+ this.getEvents().remove(event);
+ event.getParticipants().remove(this);
+}]]></programlisting>
+
+ <para>
+ Observe que os métodos set e get da a coleção estão protegidos â isso permite que classes e
+ subclasses do mesmo pacote continuem acessando os métodos, mas previne que qualquer outra classe,
+ que não esteja no mesmo pacote, acesse a coleção diretamente. Você provavelmente deve fazer a mesma
+ coisa para a coleção do outro lado.
+ </para>
+
+ <para>
+ E sobre o mapeamento do atributo <literal>inverse</literal>? Pra você, e para o Java, um link bidirecional
+ é simplesmente o fato de ajustar corretamente as referências de ambos os lados. O Hibernate, entretanto
+ não possui informação necessária para corretamente adaptar os estados <literal>INSERT</literal> e
+ <literal>UPDATE</literal> do SQL, e precisa de ajuda para manipular as propriedades das associações
+ bidirecionais. Fazer um lado da associação com o atributo <literal>inverse</literal> instrui o Hibernate
+ para basicamente ignora-lo, considerando-o uma <emphasis>cópia</emphasis> do outro lado. Isso é todo o
+ necessário para o Hibernate trabalhar com todas as possibilidades quando transformando um modelo de
+ navegação bidirecional em esquema de banco de dados do SQL. As regras que você possui para lembrar são
+ diretas: Todas associações bidirecionais necessitam que um lado possua o atributo inverse. Em uma
+ associação de um-para-muitos, o lado de "muitos" deve conter o atributo <literal>inverse</literal>,
+ já em uma associação de muitos-para-muitos você pode pegar qualquer lado, não há diferença.
+ </para>
+
+ </sect2>
+
+ <para>
+ Agora, vamos portar este exemplo para um pequeno aplicativo para internet.
+
+ </para>
+
+ </sect1>
+
+ <sect1 id="tutorial-webapp">
+ <title>EventManager um aplicativo para internet</title>
+
+ <para>
+ Um aplicativo para internet do Hibernate usa uma <literal>Session</literal> e uma <literal>Transaction</literal>
+ quase do mesmo modo que um aplicativo standalone. Entretanto, alguns patterns comuns são úteis.
+ Nós agora criaremos um <literal>EventManagerServlet</literal>. Esse servlet lista todos os eventos
+ salvos no banco de dados, e cria um formulário HTML para entrada de novos eventos.
+ </para>
+
+ <sect2 id="tutorial-webapp-servlet" revision="1">
+ <title>Criando um servlet básico</title>
+
+ <para>
+ Crie uma nova classe no seu diretório fonte, no pacote <literal>events</literal>:
+ </para>
+
+ <programlisting><![CDATA[package events;
+
+// Imports
+
+public class EventManagerServlet extends HttpServlet {
+
+ // Servlet code
+}]]></programlisting>
+
+ <para>
+ O servlet manuseia somente requisições <literal>GET</literal> do HTTP,
+ portanto o método que iremos implementar é <literal>doGet()</literal>:
+ </para>
+
+ <programlisting><![CDATA[protected void doGet(HttpServletRequest request,
+ HttpServletResponse response)
+ throws ServletException, IOException {
+
+ SimpleDateFormat dateFormatter = new SimpleDateFormat("dd.MM.yyyy");
+
+ try {
+ // Begin unit of work
+ HibernateUtil.getSessionFactory()
+ .getCurrentSession().beginTransaction();
+
+ // Process request and render page...
+
+ // End unit of work
+ HibernateUtil.getSessionFactory()
+ .getCurrentSession().getTransaction().commit();
+
+ } catch (Exception ex) {
+ HibernateUtil.getSessionFactory()
+ .getCurrentSession().getTransaction().rollback();
+ throw new ServletException(ex);
+ }
+
+}]]></programlisting>
+
+ <para>
+ O pattern que estamos aplicando neste código é chamado <emphasis>session-per-request</emphasis>.
+ Quando uma requisição chega ao servlet, uma nova <literal>Session</literal> do Hibernate é
+ aberta através da primeira chamada para <literal>getCurrentSession()</literal> em
+ <literal>SessionFactory</literal>. Então uma transação do banco de dados é inicializada -
+ todo acesso a dados deve ocorrer dentro de uma transação, não importando se o dado é de leitura ou escrita.
+ (nós não devemos usar o modo auto-commit em aplicações).
+
+ </para>
+
+ <para>
+ Agora, as possibilidades de ações de uma requisição serão processadas e uma resposta HTML será renderizada.
+ Nós já iremos chegar nesta parte.
+ </para>
+
+ <para>
+ Finalmente, a unidade de trabalho termina quando o processamento e a restituição são completados.
+ Se ocorrer algum erro durante o processamento ou a restituição, uma exceção será lançada e a
+ transação do banco de dados encerrada. Isso completa o pattern <literal>session-per-request</literal>.
+ Em vez de usar código de demarcação de transação em todo servlet você pode também criar um filtro servlet.
+ Dê uma olhada no site do Hibernate e do Wiki para maiores informações sobre esse pattern,
+ chamado <emphasis>Open Session in View</emphasis>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-webapp-processing" revision="1">
+ <title>Processando e renderizando</title>
+
+ <para>
+ Vamos implementar o processamento da requisição e a restituição da página HTML.
+ </para>
+
+<programlisting><![CDATA[// Write HTML header
+PrintWriter out = response.getWriter();
+out.println("<html><head><title>Event Manager</title></head><body>");
+
+// Handle actions
+if ( "store".equals(request.getParameter("action")) ) {
+
+ String eventTitle = request.getParameter("eventTitle");
+ String eventDate = request.getParameter("eventDate");
+
+ if ( "".equals(eventTitle) || "".equals(eventDate) ) {
+ out.println("<b><i>Please enter event title and date.</i></b>");
+ } else {
+ createAndStoreEvent(eventTitle, dateFormatter.parse(eventDate));
+ out.println("<b><i>Added event.</i></b>");
+ }
+}
+
+// Print page
+printEventForm(out);
+listEvents(out, dateFormatter);
+
+// Write HTML footer
+out.println("</body></html>");
+out.flush();
+out.close();]]></programlisting>
+
+ <para>
+ O estilo de código acima, misturando linguagem HTML e Java não será funcional em um aplicativo
+ mais complexo—tenha em mente que neste manual nós estamos apenas ilustrando conceitos
+ básicos do Hibernate. O código imprime um cabeçalho HTML e um rodapé. Dentro desta página,
+ é mostrado um formulário em HTML, para entrada de novos eventos, e uma lista de todos
+ os eventos contidos no banco de dados. O primeiro método é trivial e apenas imprime
+ uma página HTML:
+ </para>
+
+ <programlisting><![CDATA[private void printEventForm(PrintWriter out) {
+ out.println("<h2>Add new event:</h2>");
+ out.println("<form>");
+ out.println("Title: <input name='eventTitle' length='50'/><br/>");
+ out.println("Date (e.g. 24.12.2009): <input name='eventDate' length='10'/><br/>");
+ out.println("<input type='submit' name='action' value='store'/>");
+ out.println("</form>");
+}]]></programlisting>
+
+ <para>
+ O método <literal>listEvents()</literal> usa a <literal>Session</literal> do Hibernate
+ associada a thread atual para executar um query:
+ </para>
+
+ <programlisting><![CDATA[private void listEvents(PrintWriter out, SimpleDateFormat dateFormatter) {
+
+ List result = HibernateUtil.getSessionFactory()
+ .getCurrentSession().createCriteria(Event.class).list();
+ if (result.size() > 0) {
+ out.println("<h2>Events in database:</h2>");
+ out.println("<table border='1'>");
+ out.println("<tr>");
+ out.println("<th>Event title</th>");
+ out.println("<th>Event date</th>");
+ out.println("</tr>");
+ for (Iterator it = result.iterator(); it.hasNext();) {
+ Event event = (Event) it.next();
+ out.println("<tr>");
+ out.println("<td>" + event.getTitle() + "</td>");
+ out.println("<td>" + dateFormatter.format(event.getDate()) + "</td>");
+ out.println("</tr>");
+ }
+ out.println("</table>");
+ }
+}]]></programlisting>
+
+ <para>
+ Finalmente, a action <literal>store</literal> é passada pra o método
+ <literal>createAndStoreEvent()</literal>, que também usa a
+ <literal>Session</literal> da thread atual:
+ </para>
+
+ <programlisting><![CDATA[protected void createAndStoreEvent(String title, Date theDate) {
+ Event theEvent = new Event();
+ theEvent.setTitle(title);
+ theEvent.setDate(theDate);
+
+ HibernateUtil.getSessionFactory()
+ .getCurrentSession().save(theEvent);
+}]]></programlisting>
+
+ <para>
+ Pronto, o servlet está completo. Uma requisição para o servlet será processada
+ em uma <literal>Session</literal> e uma <literal>Transaction</literal> simples.
+ Como anteriormente, no aplicativo standalone, o Hibernate pode automaticamente
+ associar esses objetos a thread atual em execução. Isso possibilita a liberdade
+ de você modelar seu código e acessar o método <literal>SessionFactory</literal>
+ do jeito que achar melhor. Geralmente você irá usar um design mais sofisticado
+ e mover o código de acesso a dados para dentro de objetos de acesso a dados
+ (o patter DAO). Leia o Hibernate Wiki para maiores exemplos.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-webapp-deploy">
+ <title>Instalando e testando</title>
+
+ <para>
+ Para fazer o deploy desta aplicação você tem que criar um arquivo para web, um WAR.
+ Adicione o alvo Ant abaixo em seu <literal>build.xml</literal>:
+ </para>
+
+<programlisting><![CDATA[<target name="war" depends="compile">
+ <war destfile="hibernate-tutorial.war" webxml="web.xml">
+ <lib dir="${librarydir}">
+ <exclude name="jsdk*.jar"/>
+ </lib>
+
+ <classes dir="${targetdir}"/>
+ </war>
+</target>]]></programlisting>
+
+ <para>
+ Esta target cria um arquivo chamado <literal>hibernate-tutorial.war</literal>
+ no diretório do seu projeto. Ele empacota todas as bibliotecas e o arquivo de
+ descrição <literal>web.xml</literal>, o qual é esperado no diretório base do seu projeto:
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
+<web-app version="2.4"
+ xmlns="http://java.sun.com/xml/ns/j2ee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
+
+ <servlet>
+ <servlet-name>Event Manager</servlet-name>
+ <servlet-class>events.EventManagerServlet</servlet-class>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>Event Manager</servlet-name>
+ <url-pattern>/eventmanager</url-pattern>
+ </servlet-mapping>
+</web-app>]]></programlisting>
+
+ <para>
+ Antes de você compilar e fazer o deploy desta aplicação web, note que uma biblioteca
+ adicional é requerida: <literal>jsdk.jar</literal>. Esse é o Java servlet development kit,
+ se você não possui esta biblioteca, faça seu download na página da Sun e copie-a
+ para seu diretório de bibliotecas. Entretanto, será usado somente para a compilação e
+ excluÃdo do pacote WAR.
+ </para>
+
+ <para>
+ Para compilar e instalar execute <literal>ant war</literal> no seu diretório do projeto
+ e copie o arquivo <literal>hibernate-tutorial.war</literal> para o diretório
+ <literal>webapp</literal> do Tomcat. Se você não possui o Tomcat instalado faça
+ o download e siga as instruções de instalação. Você não precisa modificar
+ nenhuma configuração do Tomcat para rodar este aplicativo.
+ </para>
+
+ <para>
+ Uma vez feito o deploy e com Tomcat rodando, acesse o aplicativo em
+ <literal>http://localhost:8080/hibernate-tutorial/eventmanager</literal>.
+ Veja o log do Tomcat para observar a inicialização do Hibernate quando a
+ primeira requisição chega ao servlet (o inicializador estático dentro de
+ <literal>HibernateUtil</literal> é chamado) e para ter uma depuração
+ detalhada se ocorrer alguma exceção.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="tutorial-summary" revision="1">
+ <title>Sumário</title>
+
+ <para>
+ Este manual cobriu os princÃpios básicos para criação de uma aplicação simples do Hibernate
+ e uma pequena aplicação web.
+ </para>
+
+ <para>
+ Se você já se sente seguro com o Hibernate, continue navegando na documentação de referência
+ por tópicos que você acha interessante â os tópicos mais questionados são:
+ processo de transação (<xref linkend="transactions"/>), uso da API (<xref linkend="objectstate"/>)
+ e caracterÃsticas de consulta (<xref linkend="objectstate-querying"/>).
+ </para>
+
+ <para>
+ Não esqueça de visitar o site do Hibernate para obter mais tutoriais especializados.
+ </para>
+
+ </sect1>
+
+</chapter>
Property changes on: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/tutorial.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/tutorial1.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/tutorial1.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/tutorial1.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1544 @@
+<chapter id="tutorial">
+ <title>Introdução ao Hibernate</title>
+
+ <sect1 id="tutorial-intro" revision="1">
+ <title>Prefácio</title>
+
+ <para>
+ Este capítulo é um tutorial introdutório para novos usuários do Hibernate. Nós iniciaremos com uma simples linha de comando em uma aplicação usando uma base de dados em memória tornando isto um passo de fácil de compreender.
+ </para>
+
+ <para>
+ Este tutorial é voltado para novos usuários do Hibernate, mas requer um conhecimento de Java e SQL. Este tutorial é baseado no tutorial de Michael Gloegl, as bibliotecas Third Party foram nomeadas para JDK 1.4 e 5.0. Você pode precisar de outras bibliotecas para JDK 1.3.
+ </para>
+
+ <para>
+ O código fonte para o tutorial está incluÃdo no diretório da distribuição
+ <literal>doc/reference/tutorial/</literal>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="tutorial-firstapp" revision="2">
+ <title>Parte 1 â A primeira aplicação Hibernate</title>
+
+ <para>
+ Primeiro, iremos criar uma simples aplicação Hibernate baseada em console. Usaremos uma base de dados Java (HSQL DB), então não teremos que instalar nenhum servidor de base de dados.
+ </para>
+
+ <para>
+ Vamos supor que precisemos de uma aplicação com um banco de dados pequeno que possa armazenar e atender os eventos que queremos, e as informaççes sobre os hosts destes eventos.
+ </para>
+
+ <para>
+ A primeira coisa que devemos fazer é configurar nosso diretório de desenvolvimento,
+ e colocar todas as bibliotecas Java que precisamos dentro dele. Faça o download da
+ distribuição do Hibernate no site do Hibernate. Descompacte o pacote e coloque todas
+ as bibliotecas necessárias encontradas no diretório <literal>/lib</literal>, dentro do
+ diretório <literal>/lib</literal> do seu novo projeto. Você deverá ter algo parecido
+ com isso:
+ </para>
+
+ <programlisting><![CDATA[.
++lib
+ antlr.jar
+ cglib.jar
+ asm.jar
+ asm-attrs.jars
+ commons-collections.jar
+ commons-logging.jar
+ hibernate3.jar
+ jta.jar
+ dom4j.jar
+ log4j.jar ]]></programlisting>
+
+ <para>
+ Esta é a configuração mínima requerida das bibliotecas (observe que também foi copiado
+ o hibernate3.jar da pasta principal do Hibernate) para o Hibernate <emphasis>na hora do desenvolvimento</emphasis>. O Hibernate permite que você utilize mais ou menos bibliotecas.
+ Veja o arquivo <literal>README.txt</literal> no diretório <literal>lib/</literal> da distribuição
+ do Hibernate para maiores informaççes sobre bibliotecas requeridas e opcionais.
+ (Atualmente, a biblioteca Log4j não é requerida, mas é preferida por muitos desenvolvedores.)
+ </para>
+
+ <para>
+ Agora, iremos criar uma classe que representa o evento que queremos armazenar na base de dados..
+ </para>
+
+ <sect2 id="tutorial-firstapp-firstclass" revision="1">
+ <title>A primeira Classe</title>
+
+ <para>
+ Nossa primeira classe de persistência é uma simples classe JavaBean com algumas propriedades:
+ </para>
+
+ <programlisting><![CDATA[package events;
+
+import java.util.Date;
+
+public class Event {
+ private Long id;
+
+ private String title;
+ private Date date;
+
+ public Event() {}
+
+ public Long getId() {
+ return id;
+ }
+
+ private void setId(Long id) {
+ this.id = id;
+ }
+
+ public Date getDate() {
+ return date;
+ }
+
+ public void setDate(Date date) {
+ this.date = date;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+}]]></programlisting>
+
+ <para>
+ Você pode ver que esta classe usa o padrão JavaBean para o nomeamento convencional da propriedade getter e dos métodos setter, como também a visibilidade private dos campos. Este é um padrão de projeto recomendado, mas não requerido. O Hibernate pode também acessar campos diretamente, o benefÃcio para os métodos de acesso é a robustez para o Refactoring. O construtor sem argumento é requerido para instanciar um objeto desta classe com a reflexão.
+ </para>
+
+ <para>
+ A propriedade <literal>id</literal> mantém um único valor de identificação para um evento
+ particular. Todas as classes persistentes da entidade (bem como aquelas classes dependentes
+ de menos importância) precisam de uma propriedade de identificação, caso nós queiramos usar o
+ conjunto completo de caracterÃsticas do Hibernate. De fato, a maioria das aplicações
+ (esp. aplicações web) precisam destinguir os objetos pelo identificador, então você deverá
+ considerar esta, uma caracterÃstica em lugar de uma limitação. Porém, nós normalmente não
+ manipulamos a identidade de um objeto, consequentemente o método setter deverá ser privado.
+ O Hibernate somente nomeará os identificadores quando um objeto for salvo. Você pode ver como
+ o Hibernate pode acessar métodos públicos, privados, e protegidos, como também campos
+ (públicos, privados, protegidos) diretamente. A escolha está até você, e você pode combinar
+ isso para adaptar seu projeto de aplicação
+ </para>
+
+ <para>
+ O construtor sem argumentos é um requerimento para todas as classes persistentes;
+ O Hibernate tem que criar para você os objetos usando Java Reflection. O construtor
+ pode ser privado, porém, a visibilidade do pacote é requerida para a procuração da
+ geração em tempo de execução e recuperação eficiente dos dados sem a instrumentação
+ de bytecode
+ </para>
+
+ <para>
+ Coloque este fonte Java no diretório chamado <literal>src</literal> na pasta de desenvolvimento,
+ e em seu pacote correto. O diretório deverá ser parecido como este:
+ </para>
+
+ <programlisting><![CDATA[.
++lib
+ <Hibernate and third-party libraries>
++src
+ +events
+ Event.java]]></programlisting>
+
+ <para>
+ No próximo passo, iremos falar sobre as classes de persistência do Hibernate..
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-mapping" revision="1">
+ <title>O mapeamento do arquivo</title>
+
+ <para>
+ O Hibernate precisa saber como carregar e armazenar objetos da classe de
+ persistência. Isto será onde o mapeamento do arquivo do Hibernate entrará em
+ jogo. O arquivo mapeado informa ao Hibernate, qual tabela no banco de dados
+ ele deverá acessar, e quais as colunas na tabela ele deverá usar.
+ </para>
+
+ <para>
+ A estrutura básica de um arquivo de mapeamento é parecida com:
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping>
+[...]
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Note que o Hibernate DTD é muito sofisticado. Você pode usar isso para auto-conclusão
+ no mapeamento XML dos elementos e atributos no seu editor ou IDE. Você também pode
+ abrir o arquivo DTD no seu editor â é a maneira mais fácil de ter uma visão geral
+ de todos os elementos e atributos e dos padrões, como também alguns comentários.
+ Note que o Hibernate não irá carregar o arquivo DTD da web, e sim do diretório
+ da aplicação (classpath). O arquivo DTD está incluÃdo no <literal>hibernate3.jar</literal> como
+ também no diretório <literal>src/</literal> da distribuição do Hibernate.
+ </para>
+
+ <para>
+ Nós omitiremos a declaração do DTD nos exemplos futuros para encurtar o código. Isto, é claro, não é opcional.
+ </para>
+
+ <para>
+ Entre os dois tags <literal>hibernate-mapping</literal>, inclua um elemento <literal>class</literal>.
+ Todas as classes persistentes da entidade (novamente, poderá haver
+ mais tarde, dependências sobre as classes que não são classes-primárias
+ de entidades) necessitam do tal mapeamento, para uma tabela na base
+ de dados SQL
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="events.Event" table="EVENTS">
+
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Mais adiante iremos dizer ao Hibernate como fazer para persistir e carregar objetos da classe
+ <literal>Event</literal> da tabela <literal>EVENTS</literal>, cada instancia representada por
+ uma coluna na tabela. Agora, continuaremos com o mapeamento de uma única propriedade identificadora
+ para as chaves primárias da tabela. Além disso, nós não iremos se importar com esta propriedade
+ identificadora, nós iremos configurar uma estratégia de geração de idâs para uma chave primária
+ de coluna surrogate:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="events.Event" table="EVENTS">
+ <id name="id" column="EVENT_ID">
+ <generator class="native"/>
+ </id>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ O elemento <literal>id</literal> é a declaração da propriedade identificadora,
+ o <literal>name="id"</literal> declara o nome da propriedade Java â
+ o Hibernate irá usar os métodos getter e setter para acessar a propriedade.
+ O atributo da coluna informa ao Hibernate qual coluna da tabela <literal>EVENTS</literal> nós
+ iremos usar como chave primária. O elemento <literal>generator</literal> especifica
+ a estratégia de geração do identificador, neste caso usaremos <literal>native</literal>, que
+ escolhe a melhor estratégia dependendo da base de dados (dialeto) configurada.
+ O Hibernate suporta a base de dados gerada, globalmente única, bem como a atribuição
+ aos identificadores da aplicação (ou toda estratégia escrita para uma extensão).
+ </para>
+
+ <para>
+ Finalmente incluiremos as declarações para as propriedades persistentes da classe
+ no arquivo mapeado. Por default, nenhuma das propriedades da classe é considerada persistente:
+ </para>
+
+ <programlisting><![CDATA[
+<hibernate-mapping>
+
+ <class name="events.Event" table="EVENTS">
+ <id name="id" column="EVENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="date" type="timestamp" column="EVENT_DATE"/>
+ <property name="title"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Da mesma maneira que com o elemento <literal>id</literal>, o atributo <literal>name</literal> do elemento
+ <literal>property</literal> informa ao Hibernate qual método getter e setter deverá usar.
+ Assim, neste caso, o Hibernate irá procurar pelo <literal>getDate()/setDate()</literal>,
+ como também pelo <literal>getTitle()/setTitle()</literal>.
+ </para>
+
+ <para>
+ Porque fazer o mapeamento da propriedade <literal>date</literal> incluÃdo no
+ atributo <literal>column</literal>, e no title não fazer?
+ Sem o atributo <literal>column</literal> o Hibernate por padrão usa o nome
+ da propriedade como o nome da coluna. Isto trabalha muito
+ bem para o <literal>title</literal>. Entretanto o <literal>date</literal> é uma palavra-chave reservada
+ na maioria dos bancos de dados, assim nós melhoramos o mapeamentos
+ disto com um nome diferente.
+ </para>
+
+ <para>
+ A próxima coisa interessante é que mapemanto do <literal>title</literal>
+ também falta o atributo <literal>type</literal>. O tipo que declaramos e o uso nos
+ arquivos mapeados, não são como você pôde esperar, atributos de dados Java.
+ Eles não são como os tipos de base de dados SQL.
+ Esses tipos podem ser chamados de <emphasis>Tipos de mapeamento Hibernate</emphasis>, que são conversores
+ que podem traduzir tipos de dados do Java para os tipos de dados SQL e vice-versa.
+ Novamente, o Hibernate irá tentar determinar a conversão correta e mapeará o <literal>type</literal>
+ próprio, caso o tipo do atributo não estiver presente no mapeamento.
+ Em alguns casos, esta detecção automática (que usa Reflection sobre as classes Java)
+ poderá não ter padrão que você espera ou necessita.
+ Este é o caso com a propriedade <literal>date</literal>. O Hibernate não pode saber se a propriedade
+ (que é do <literal>java.util.Date</literal>) pode mapear para uma coluna do tipo <literal>date</literal>
+ do SQL, <literal>timestamp</literal>, ou <literal>time</literal> .
+ Nós preservamos a informação cheia de datas e horas pelo mapeamento da propriedade com um conversor
+ <literal>timestamp</literal>.
+ </para>
+
+ <para>
+ Este arquivo de mapeamento deve ser salvo como <literal>Event.hbm.xml</literal>,
+ corretamente no diretório próximo ao arquivo fonte da Classe Java <literal>Event</literal>.
+ O nomeamento dos arquivos de mapeamento podem ser arbitrários, porém o sufixo
+ <literal>hbm.xml</literal> é uma convenção da comunidade dos desenvolvedores do Hibernate.
+ Esta estrutura do diretório deve agora se parecer com isso:
+ </para>
+
+ <programlisting><![CDATA[.
++lib
+ <Hibernate and third-party libraries>
++src
+ +events
+ Event.java
+ Event.hbm.xml]]></programlisting>
+
+ <para>
+ Nós iremos continuar com a configuração principal do Hibernate.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-configuration" revision="2">
+ <title>Configuração do Hibernate</title>
+
+ <para>
+ Agora nós temos uma classe persistente e este arquivo de mapeamento no lugar.
+ Está na hora de configurar o Hibernate. Antes de fazermos isso, iremos precisar de uma base de dados.
+ O HSQL DB, um SQL DBMS feito em java, pode ser baixado através do site do HSQL DB.
+ Atualmente, você só precisa baixar o <literal>hsqldb.jar</literal>.
+ Coloque este arquivo no diretório da pasta de desenvolvimento <literal>lib/</literal>.
+ </para>
+
+ <para>
+ Crie um diretório chamado <literal>data</literal> no diretório root de desenvolvimento â
+ Isto será onde o HSQL DB irá armazenar arquivos de dados. Agora iremos iniciar o banco de dados
+ executando <literal>java -classpath ../lib/hsqldb.jar org.hsqldb.Server</literal> neste diretório de dados.
+ Você pode ver ele iniciando e conectando ao socket TCP/IP, isto será onde nossa aplicação irá se
+ conectar depois. Se você deseja iniciar uma nova base de dados durante este tutorial,
+ finalize o HSQL DB(pressionando o <literal>CTRL + C</literal> na janela), delete todos os
+ arquivos no diretório <literal>data/</literal>, e inicie o HSQL BD novamente.
+ </para>
+
+ <para>
+ O Hibernate é uma camada na sua aplicação na qual se conecta com a base de dados, para isso
+ necessita de informação da conexão. As conexões são feitas através de um pool de conexão JDBC,
+ na qual teremos que configurar. A distribuição do Hibernate contém diversas ferramentas de pooling
+ da conexão JDBC de fonte aberta, mas iremos usar o pool de conexão interna para este tutorial.
+ Note que você tem que copiar a biblioteca necessária em seu classpath e use configurações
+ diferentes para pooling de conexão caso você deseje utilizar um software de pooling JDBC terceirizado
+ para qualidade de produção.
+ </para>
+
+ <para>
+ Para as configurações do Hibernate, nós podemos usar um arquivo simples <literal>hibernate.properties</literal>,
+ um arquivo mais ligeiramente sofisticado <literal>hibernate.cfg.xml</literal> ou até mesmo uma
+ instalação programática completa. A maioria dos usuários preferem utilizar o arquivo de configuração XML
+ </para>
+
+ <programlisting><![CDATA[<?xml version='1.0' encoding='utf-8'?>
+<!DOCTYPE hibernate-configuration PUBLIC
+ "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
+
+<hibernate-configuration>
+
+ <session-factory>
+
+ <!-- Database connection settings -->
+ <property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
+ <property name="connection.url">jdbc:hsqldb:hsql://localhost</property>
+ <property name="connection.username">sa</property>
+ <property name="connection.password"></property>
+
+ <!-- JDBC connection pool (use the built-in) -->
+ <property name="connection.pool_size">1</property>
+
+ <!-- SQL dialect -->
+ <property name="dialect">org.hibernate.dialect.HSQLDialect</property>
+
+ <!-- Enable Hibernate's automatic session context management -->
+ <property name="current_session_context_class">thread</property>
+
+ <!-- Disable the second-level cache -->
+ <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
+
+ <!-- Echo all executed SQL to stdout -->
+ <property name="show_sql">true</property>
+
+ <!-- Drop and re-create the database schema on startup -->
+ <property name="hbm2ddl.auto">create</property>
+
+ <mapping resource="events/Event.hbm.xml"/>
+
+ </session-factory>
+
+</hibernate-configuration>]]></programlisting>
+
+ <para>
+ Note que esta configuração XML usa um diferente DTD. Nós configuraremos
+ as <literal>SessionFactory</literal> do Hibernate â uma factory global responsável
+ por uma base de dedados particular. Se você tiver diversas bases de dados,
+ use diversas configurações <literal><session-factory></literal>, geralmente
+ em diversos arquivos de configuração (para uma partida mais fácil).
+ </para>
+
+ <para>
+ As primeiras quatro <literal>propriedades</literal> do elemento contém a configuração
+ necessária para a conexão ao JDBC. A propriedade <literal>propriedade</literal> dialect
+ do elemento especifica a variante particular do SQL que o Hibernate gera.
+ O gerenciamento automático de sessão do Hibernate para contextos de persistência
+ estará disponÃvel em breve. A opção <literal>hbm2ddl.auto</literal> habilita a geração
+ automática de schemas da base de dados â diretamente na base de dados.
+ Isto também pode ser naturalmente desligado (removendo a opção de configuração) ou redirecionando
+ para um arquivo com ajuda do <literal>SchemaExport</literal> nas tarefas do Ant.
+ Finalmente, iremos adicionar os arquivos das classes de persistência mapeadas na configuração.
+ </para>
+
+ <para>
+ Copie este arquivo no diretório fonte, assim isto irá terminar na raiz (root) do
+ classpath. O Hibernate automaticamente procura por um arquivo chamado
+ <literal>hibernate.cfg.xml</literal> na raiz do classpath, no startup.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-ant" revision="1">
+ <title>Construindo com o Ant</title>
+
+ <para>
+ Nos iremos, agora, construir o tutorial com Ant. Você ira precisar o Ant instalado â
+ se encontra disponÃvel <ulink url="http://ant.apache.org/bindownload.cgi">na página de download do Ant</ulink>.
+ Como instalar o Ant, não será abordado aqui. Caso tenha alguma dúvida, por favor,
+ vá ao <ulink url="http://ant.apache.org/manual/index.html">Ant manual</ulink>.
+ Depois que tiver instalado o Ant, podemos começar a criar o arquivo de construção <literal>build.xml</literal>.
+ Este arquivo será chamado de <literal>build.xml</literal> e posto diretamente no diretório de desenvolvimento.
+ </para>
+
+ <para>
+ Um arquivo básico de build, se parece com isto:
+ </para>
+
+ <programlisting><![CDATA[<project name="hibernate-tutorial" default="compile">
+
+ <property name="sourcedir" value="${basedir}/src"/>
+ <property name="targetdir" value="${basedir}/bin"/>
+ <property name="librarydir" value="${basedir}/lib"/>
+
+ <path id="libraries">
+ <fileset dir="${librarydir}">
+ <include name="*.jar"/>
+ </fileset>
+ </path>
+
+ <target name="clean">
+ <delete dir="${targetdir}"/>
+ <mkdir dir="${targetdir}"/>
+ </target>
+
+ <target name="compile" depends="clean, copy-resources">
+ <javac srcdir="${sourcedir}"
+ destdir="${targetdir}"
+ classpathref="libraries"/>
+ </target>
+
+ <target name="copy-resources">
+ <copy todir="${targetdir}">
+ <fileset dir="${sourcedir}">
+ <exclude name="**/*.java"/>
+ </fileset>
+ </copy>
+ </target>
+
+</project>]]></programlisting>
+
+ <para>
+ Isto irá avisar ao Ant para adicionar todos os arquivos no diretório lib terminando com
+ <literal>.jar</literal>, para o classpath usado para compilação. Irá também copiar todos os
+ arquivos não-java para o diretório alvo (arquivos de configuração, mapeamento). Se você rodar
+ o ant agora, deverá ter esta saÃda.
+ </para>
+
+ <programlisting><![CDATA[C:\hibernateTutorial\>ant
+Buildfile: build.xml
+
+copy-resources:
+ [copy] Copying 2 files to C:\hibernateTutorial\bin
+
+compile:
+ [javac] Compiling 1 source file to C:\hibernateTutorial\bin
+
+BUILD SUCCESSFUL
+Total time: 1 second ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-helpers" revision="3">
+ <title>Startup and helpers</title>
+
+ <para>
+ Ã hora de carregar e arquivar alguns objetos <literal>Event</literal>, mas primeiro
+ nós temos de completar o setup com algum código de infraestrutura. Este startup
+ inclui a construção de um objeto <literal>SessionFactory</literal> global e armazenar
+ isto em algum lugar de fácil acesso para o código da aplicação.
+ Uma <literal>SessionFactory</literal> pode abrir novas <literal>Session</literal>'s.
+ Uma <literal>Session</literal> representa uma unidade single-theaded do trabalho, a
+ <literal>SessionFactory</literal> é um objeto global thread-safe, instanciado uma vez.
+ </para>
+
+ <para>
+ Nos iremos criar uma classe de ajuda <literal>HibernateUtil</literal>, que toma
+ conta do startup e faz acesso a uma <literal>SessionFactory</literal> conveniente.
+ Vamos dar uma olhada na implementação:
+ </para>
+
+ <programlisting><![CDATA[package util;
+
+import org.hibernate.*;
+import org.hibernate.cfg.*;
+
+public class HibernateUtil {
+
+ private static final SessionFactory sessionFactory;
+
+ static {
+ try {
+ // Create the SessionFactory from hibernate.cfg.xml
+ sessionFactory = new Configuration().configure().buildSessionFactory();
+ } catch (Throwable ex) {
+ // Make sure you log the exception, as it might be swallowed
+ System.err.println("Initial SessionFactory creation failed." + ex);
+ throw new ExceptionInInitializerError(ex);
+ }
+ }
+
+ public static SessionFactory getSessionFactory() {
+ return sessionFactory;
+ }
+
+}]]></programlisting>
+
+ <para>
+ Esta classe não só produz a global <literal>SessionFactory</literal> no seu static initializer
+ (chamado uma vez pela JVM quando a classe é carregada), mas também esconde o fato
+ de que isto usa um static singleton. Ela pode muito bem, enxergar a
+ <literal>SessionFactory</literal> do JNDI em um application server.
+ </para>
+
+ <para>
+ Se você der à <literal>SessionFactory</literal> um nome, no seu arquivo de configuração.
+ O Hibernate irá, de fato, tentar uni-lo ao JNDI depois que estiver construÃdo.
+ Para evitar este completamente este código, você também poderia usar JMX deployment
+ e deixar o contêiner JMX capaz, instanciar e unir um <literal>HibernateService</literal>
+ no JNDI. Essas opções avançadas são discutidas no documento de referência do Hibernate.
+ </para>
+
+ <para>
+ Coloque o Place <literal>HibernateUtil.java</literal> no diretório de arquivos
+ de desenvolvimento(source), em um pacote após o <literal>events</literal>:
+ </para>
+
+ <programlisting><![CDATA[.
++lib
+ <Hibernate and third-party libraries>
++src
+ +events
+ Event.java
+ Event.hbm.xml
+ +util
+ HibernateUtil.java
+ hibernate.cfg.xml
++data
+build.xml]]></programlisting>
+
+ <para>
+ Novamente, isto deve compilar sem problemas. Finalmente, nós precisamos configurar
+ um sistema de logging â o Hibernate usa commons logging e deixa você escolher entre o
+ Log4j e o logging do JDK 1.4 . A maioria dos desenvolvedores preferem o Log4j: copie
+ <literal>log4j.properties</literal> da distribuição do Hibernate (está no diretório
+ <literal>etc/</literal>), para seu diretório <literal>src</literal>,
+ depois vá em hibernate.cfg.xml. Dê uma olhada no exemplo de configuração e mude as
+ configurações se você quizer ter uma saÃda mais detalhada. Por default, apenas as
+ mensagems de startup e shwwn do Hibernate é mostrada no stdout.
+ </para>
+
+ <para>
+ O tutorial de infra-estrutura está completo - e nós já estamos preparados para algum
+ trabalho de verdade com o Hibernate.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-workingpersistence" revision="4">
+ <title>Carregando e salvando objetos</title>
+
+ <para>
+ Finalmente, nós podemos usar o Hibernate para carregar e armazenar objetos.
+ Nós escrevemos uma classe <literal>EventManager</literal> com um método main():
+ </para>
+
+ <programlisting><![CDATA[package events;
+import org.hibernate.Session;
+
+import java.util.Date;
+
+import util.HibernateUtil;
+
+public class EventManager {
+
+ public static void main(String[] args) {
+ EventManager mgr = new EventManager();
+
+ if (args[0].equals("store")) {
+ mgr.createAndStoreEvent("My Event", new Date());
+ }
+
+ HibernateUtil.getSessionFactory().close();
+ }
+
+ private void createAndStoreEvent(String title, Date theDate) {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+
+ session.beginTransaction();
+
+ Event theEvent = new Event();
+ theEvent.setTitle(title);
+ theEvent.setDate(theDate);
+
+ session.save(theEvent);
+
+ session.getTransaction().commit();
+ }
+
+}]]></programlisting>
+
+ <para>
+ We create a new <literal>Event</literal> object, and hand it over to Hibernate.
+ Hibernate now takes care of the SQL and executes <literal>INSERT</literal>s
+ on the database. Let's have a look at the <literal>Session</literal> and
+ <literal>Transaction</literal>-handling code before we run this.
+
+ Nós criamos um novo objeto <literal>Event</literal>, e passamos para o Hibernate.
+ O Hibernate sabe como tomar conta do SQL e executa <literal>INSERT</literal>s
+ no banco de dados. Vamos dar uma olhada na <literal>Session</literal> e no
+ código <literal>Transaction</literal>-handling antes de executarmos.
+ </para>
+
+ <para>
+ Um <literal>Session</literal> é uma unidade simples de trabalho. Por agora nós
+ iremos pegar coisas simples e assumir uma granularidade de um-pra-um entre uma
+ <literal>Session</literal> do Hibernate e uma transação de banco de dados.
+ Para proteger nosso código de um atual sistema subjacente de transação (nesse
+ caso puro JDBC, mas também poderia rodar com JTA), nos usamos a API
+ <literal>Transaction</literal>, que está disponÃvel na <literal>Session</literal> do Hibernate.
+ </para>
+
+ <para>
+ O que a <literal>sessionFactory.getCurrentSession()</literal> faz? Primeiro, você pode
+ chamar quantas vezes e de onde quiser, uma vez você recebe sua <literal>SessionFactory</literal>
+ (fácil graças ao <literal>HibernateUtil</literal>). O método <literal>getCurrentSession()</literal>
+ sempre retorna a unidade de trabalho "corrente". Lembra de que nós mudamos a opção
+ de configuração desse mecanismo para thread no <literal>hibernate.cfg.xml</literal>? Daqui em
+ diante, o escopo da unidade de trabalho corrente é a thread Java
+ corrente que executa nossa aplicação. Entretanto, esta não é toda a verdade. Uma
+ <literal>Session</literal> começa quando é primeiramente necessária, quando é feita a
+ primeira chamada à <literal>getCurrentSession()</literal>. à então limitado pelo Hibernate
+ para thread corrente. Quando a transação termina, tanto com commit quanto rollback,
+ o Hibernate também desune a <literal>Session</literal> da thread e fecha isso pra você.
+ Se você chamar <literal>getCurrentSession()</literal> novamente, você receberá uma nova
+ <literal>Session</literal> e pode começar uma nova unidade de trabalho. Esse modelo de
+ programação de limite de thread <emphasis>thread-bound</emphasis>, é o modo mais popular
+ de se usar o Hibernate.
+ </para>
+
+ <para>
+ Dê uma olhada no <xref linkend="transactions"/> para mais informações a
+ respeito de manipulação de transação e demarcação. Nós também pulamos qualquer
+ manipulação de erro e rollback no exemplo anterior.
+ </para>
+
+ <para>
+ Para executar esta primeira rotina, nos teremos que adicionar um ponto de chamada
+ para o arquivo de build do Ant:
+ </para>
+
+ <programlisting><![CDATA[<target name="run" depends="compile">
+ <java fork="true" classname="events.EventManager" classpathref="libraries">
+ <classpath path="${targetdir}"/>
+ <arg value="${action}"/>
+ </java>
+</target>]]></programlisting>
+
+ <para>
+ O valor do argumento <literal>action</literal>, é setado na linha de comando quando chamando esse ponto:
+ </para>
+
+ <programlisting><![CDATA[C:\hibernateTutorial\>ant run -Daction=store]]></programlisting>
+
+ <para>
+ Você deverá ver, após a compilação, o startup do Hibernate e, dependendo da sua
+ configuração, muito log de saÃda. No final você verá a seguinte linha:
+ </para>
+
+ <programlisting><![CDATA[[java] Hibernate: insert into EVENTS (EVENT_DATE, title, EVENT_ID) values (?, ?, ?)]]></programlisting>
+
+ <para>
+ Este é o <literal>INSERT</literal> executado pelo Hibernate, os pontos de interrogação
+ representam parêmetros de união do JDBC. Para ver os valores substituÃdos, ou para diminuir a
+ verbalidade do log, check seu l<literal>log4j.properties</literal>.
+ </para>
+
+ <para>
+ Agora nós gostarÃamos de listar os eventos arquivados, então nós adicionamos uma
+ opção para o método main:
+ </para>
+
+ <programlisting><![CDATA[if (args[0].equals("store")) {
+ mgr.createAndStoreEvent("My Event", new Date());
+}
+else if (args[0].equals("list")) {
+ List events = mgr.listEvents();
+ for (int i = 0; i < events.size(); i++) {
+ Event theEvent = (Event) events.get(i);
+ System.out.println("Event: " + theEvent.getTitle() +
+ " Time: " + theEvent.getDate());
+ }
+}]]></programlisting>
+
+ <para>
+ Nos também adicionamos um novo <literal>método listEvents()</literal>:
+ </para>
+
+ <programlisting><![CDATA[private List listEvents() {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+
+ session.beginTransaction();
+
+ List result = session.createQuery("from Event").list();
+
+ session.getTransaction().commit();
+
+ return result;
+}]]></programlisting>
+
+ <para>
+ O que nós fazemos aqui, é usar uma query HQL (Hibernate Query Language),
+ para carregar todos os objetos <literal>Event</literal> exitentes no banco de dados.
+ O Hibernate irá gerar o SQL apropriado, enviar para o banco de dados e popular objetos
+ <literal>Event</literal> com os dados. Você pode criar queries mais complexas com
+ HQL, claro.
+ </para>
+
+ <para>
+ Agora, para executar e testar tudo isso, siga os passos a seguir:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ Execute <literal>ant run -Daction=store</literal> para armazenar algo no banco de dados
+ e, claro, gerar o esquema do banco de dados antes pelo hbm2ddl.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Agora desabilite hbm2ddl comentando a propriedade no seu arquivo <literal>hibernate.cfg.xml</literal>.
+ Normalmente só se deixa habilitado em teste unitários contÃnuos, mas outra carga de hbm2ddl
+ pode <emphasis>remover</emphasis> tudo que você já tenha arquivado. Sa configuração
+ <literal>create</literal>, atualmente são traduzidas para "apague todas as tabelas do esquema,
+ então recrie todas quando a SessionFactory estiver pronta".
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Se você agora chamar o Ant com <literal>-Daction=list</literal>, você deverá ver os
+ eventos que você acabou de criar. Você pode também chamar a ação <literal>store</literal>
+ mais algumas vezes.
+ </para>
+
+ <para>
+ Nota: A maioria dos novos usuários do Hibernate falha nesse ponto e nós regularmente, vemos
+ questões sobre mensagens de erro de <emphasis>tabela não encontrada </emphasis> .
+ Entretanto, se você seguir os passos marcados acima, você não terá esse problema,
+ com o hbm2ddl criando o esquema do banco de dados na primeira execução, e restarts
+ subsequentes da aplicação irão usar este esquema. Se você mudar o mapeamento e/ou
+ o esquema do banco de dados, terá de re-habilitar o hbm2ddl mais uma vez.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="tutorial-associations">
+ <title>Part 2 - Mapeando associações</title>
+
+ <para>
+ Nós mapeamos uma classe de entidade de persistência para uma tabela. Agora vamos continuar
+ e adicionar algumas associações de classe. Primeiro nos iremos adicionar pessoas a nossa aplicação,
+ e armazenar os eventos de que elas participam.
+ </para>
+
+ <sect2 id="tutorial-associations-mappinguser" revision="1">
+ <title>Mapeando a classe Person</title>
+
+ <para>
+ O primeiro código da classe <literal>Person</literal> é simples:
+ </para>
+
+ <programlisting><![CDATA[package events;
+
+public class Person {
+
+ private Long id;
+ private int age;
+ private String firstname;
+ private String lastname;
+
+ public Person() {}
+
+ // Accessor methods for all properties, private setter for 'id'
+
+}]]></programlisting>
+
+ <para>
+ Crie um novo arquivo de mapeamento, chamado <literal>Person.hbm.xml</literal> (não
+ esqueça a referencia ao DTD no topo)
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="events.Person" table="PERSON">
+ <id name="id" column="PERSON_ID">
+ <generator class="native"/>
+ </id>
+ <property name="age"/>
+ <property name="firstname"/>
+ <property name="lastname"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ Finalmente, adicione o novo mapeamento a configuração do Hibernate:
+ </para>
+
+ <programlisting><![CDATA[<mapping resource="events/Event.hbm.xml"/>
+<mapping resource="events/Person.hbm.xml"/>]]></programlisting>
+
+ <para>
+ Nos iremos agora criar uma associação entre estas duas entidades. Obviamente,
+ pessoas (Person) podem participar de eventos, e eventos possuem participantes.
+ As questões de design com que teremos de lidar são: direcionalidade, multiplicidade e
+ comportamento de coleção.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-unidirset" revision="3">
+ <title>Uma associação Set-based unidirectional</title>
+
+ <para>
+ Nos iremos adicionar uma coleção de eventos na classe <literal>Person</literal>. Desse jeito
+ poderemos navegar pelos eventos de uma pessoa em particular, sem executar uma query explicitamente â
+ apenas chamando <literal>aPerson.getEvents()</literal>. Nos usaremos uma coleção Java, um
+ <literal>Set</literal>, porquê a coleção não conterá elementos duplicados e a ordem não é
+ relevante para nós.
+ </para>
+
+ <para>
+ Vamos escrever o código para isto nas classes Java e então mapear:
+ </para>
+
+ <programlisting><![CDATA[public class Person {
+
+ private Set events = new HashSet();
+
+ public Set getEvents() {
+ return events;
+ }
+
+ public void setEvents(Set events) {
+ this.events = events;
+ }
+}]]></programlisting>
+
+ <para>
+ Antes de mapearmos esta associação, pense no outro lado. Claramente, poderÃamos apenas fazer isto de
+ forma unidirecional. Ou poderÃamos criar outra coleção no <literal>Event</literal>, se quisermos
+ ser capaz de navegar bidirecionalmente, i.e. um - <literal>anEvent.getParticipants()</literal>.
+ Isto não é necessário, de perspectiva funcional. Você poderia sempre executar uma query explicita
+ que retornasse os participantes de um evento em particular. Esta é uma escolha de design que cabe
+ a você, mas o que é claro nessa discussão é a multiplicidade da associação: "muitos" valores em ambos
+ os lados, nós chamamos isto uma associação <emphasis>muitos-para-muitos</emphasis>. Daqui pra frente,
+ nos usaremos o mapeamento muitos-para-muitos do Hibernate:
+ </para>
+
+ <programlisting><![CDATA[<class name="events.Person" table="PERSON">
+ <id name="id" column="PERSON_ID">
+ <generator class="native"/>
+ </id>
+ <property name="age"/>
+ <property name="firstname"/>
+ <property name="lastname"/>
+
+ <set name="events" table="PERSON_EVENT">
+ <key column="PERSON_ID"/>
+ <many-to-many column="EVENT_ID" class="events.Event"/>
+ </set>
+
+</class>]]></programlisting>
+
+ <para>
+ O Hibernate suporta todo tipo de mapeamento de coleção , sendo um <literal><set></literal> mais comum.
+ Para uma associação muitos-para-muitos (ou relacionamento de entidade <emphasis>n:m</emphasis> ),
+ uma tabela de associação é necessária. Cada linha nessa tabela representa um link entre uma pessoa e um
+ evento. O nome da tabela é configurado com o atributo <literal>table</literal> do elemento
+ <literal>set</literal>. O nome da coluna identificadora na associção, peloo lado da pessoa,
+ é definido com o elemento <literal><key></literal> , o nome da coluna pelo lado dos eventos,
+ e definido com o atributo <literal>column</literal> do <literal><many-to-many></literal>.
+ Você também precisa dizer para o Hibernate a classe dos objetos na sua coleção (a classe do outro
+ lado das coleções de referência).
+ </para>
+
+ <para>
+ O esquema de mapeamento para o banco de dados está a seguir:
+ </para>
+
+ <programlisting><![CDATA[
+ _____________ __________________
+ | | | | _____________
+ | EVENTS | | PERSON_EVENT | | |
+ |_____________| |__________________| | PERSON |
+ | | | | |_____________|
+ | *EVENT_ID | <--> | *EVENT_ID | | |
+ | EVENT_DATE | | *PERSON_ID | <--> | *PERSON_ID |
+ | TITLE | |__________________| | AGE |
+ |_____________| | FIRSTNAME |
+ | LASTNAME |
+ |_____________|
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-working" revision="1">
+ <title>Trabalhando a associação</title>
+
+ <para>
+ Vamos trazer juntos algumas pessoas e eventos em um novo método na classe <literal>EventManager</literal>::
+ </para>
+
+ <programlisting><![CDATA[private void addPersonToEvent(Long personId, Long eventId) {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+ session.beginTransaction();
+
+ Person aPerson = (Person) session.load(Person.class, personId);
+ Event anEvent = (Event) session.load(Event.class, eventId);
+
+ aPerson.getEvents().add(anEvent);
+
+ session.getTransaction().commit();
+}]]></programlisting>
+
+ <para>
+ Após carregar um <literal>Person</literal> e um <literal>Event</literal>, simplesmente
+ modifique a coleção usando os métodos normais de uma coleção. Como você pode ver, não há chamada explÃcita
+ para <literal>update()</literal> ou <literal>save()</literal>, o Hibernate detecta automaticamente
+ que a coleção foi modificada e necessita ser atualizada. Isso é chamado de <emphasis>checagem
+ suja automática</emphasis>, e você também pode usá-la modificando o nome ou a data de qualquer um dos
+ seus objetos. Assim que eles estiverem no estado <emphasis>persistent</emphasis>, ou seja,
+ limitado por uma <literal>Session</literal> do Hibernate em particular (i.e. eles foram carregados ou
+ salvos dentro de uma unidade de trabalho), o Hibernate monitora qualquer alteração e executa o SQL
+ em modo de escrita em segundo plano. O processo de sincronização do estado da memória com o banco de
+ dados, geralmente apenas no final de uma unidade de trabalho, é chamado de <emphasis>flushing</emphasis>.
+ No nosso código, a unidade de trabalho termina com o commit da transação do banco de dados â
+ como definido pela opção de configuração da <literal>thread</literal> da classe <literal>CurrentSessionContext</literal>.
+ </para>
+
+ <para>
+ Você pode também querer carregar pessoas e eventos em diferentes unidades de trabalho.
+ Ou você modifica um objeto fora de uma <literal>Session</literal>, quando não se encontra no
+ estado persistent (se já esteve neste estado anteriormente, chamamos esse estado de
+ <emphasis>detached</emphasis>). Você pode até mesmo modificar uma coleção quando esta
+ se encontrar no estado detached.
+ </para>
+
+ <programlisting><![CDATA[private void addPersonToEvent(Long personId, Long eventId) {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+ session.beginTransaction();
+
+ Person aPerson = (Person) session
+ .createQuery("select p from Person p left join fetch p.events where p.id = :pid")
+ .setParameter("pid", personId)
+ .uniqueResult(); // Eager fetch the collection so we can use it detached
+
+ Event anEvent = (Event) session.load(Event.class, eventId);
+
+ session.getTransaction().commit();
+
+ // End of first unit of work
+
+ aPerson.getEvents().add(anEvent); // aPerson (and its collection) is detached
+
+ // Begin second unit of work
+
+ Session session2 = HibernateUtil.getSessionFactory().getCurrentSession();
+ session2.beginTransaction();
+
+ session2.update(aPerson); // Reattachment of aPerson
+
+ session2.getTransaction().commit();
+}]]></programlisting>
+
+ <para>
+ A chamada <literal>update</literal> cria um objeto persistent novamente, você poderia
+ dizer que ele liga o objeto a uma nova unidade de trabalho, assim qualquer modificação
+ que você faça neste objeto enquanto estiver no estado detached pode ser salvo no banco de dados.
+ Isso inclui qualquer modificação (adição/exclusão) que você faça em uma coleção da entidade deste objeto.
+ </para>
+
+ <para>
+ Bom, isso não foi muito usado na nossa situação, porém, é um importante conceito que você
+ pode aplicar em seus aplicativos. Agora, complete este exercÃcio adicionando uma nova ação
+ ao método main( ) da classe <literal>EventManager</literal> e chame-o pela linha de comando.
+ Se você precisar dos identificadores de uma pessoa ou evento â o método <literal>save()</literal>
+ retorna estes identificadores (você poderá modificar alguns dos métodos anteriores para retornar aquele
+ identificador):
+ </para>
+
+ <programlisting><![CDATA[else if (args[0].equals("addpersontoevent")) {
+ Long eventId = mgr.createAndStoreEvent("My Event", new Date());
+ Long personId = mgr.createAndStorePerson("Foo", "Bar");
+ mgr.addPersonToEvent(personId, eventId);
+ System.out.println("Added person " + personId + " to event " + eventId);]]></programlisting>
+
+ <para>
+ Este foi um exemplo de uma associação entre duas classes igualmente importantes, duas entidades.
+ Como mencionado anteriormente, há outras classes e tipos dentro de um modelo tÃpico,
+ geralmente "menos importante". Alguns você já viu, como um <literal>int</literal> ou uma <literal>String</literal>.
+ Nós chamamos essas classes de <emphasis>value types</emphasis>, e suas instâncias <emphasis>depend</emphasis>
+ de uma entidade particular. As instâncias desses tipos não possuem sua própria identidade, nem são
+ compartilhados entre entidades (duas pessoas não referenciam o mesmo objeto <literal>firstname</literal>
+ mesmo se elas tiverem o mesmo objeto firstname). Naturalmente, os value types não são apenas encontrados
+ dentro da JDK (de fato, em um aplicativo Hibernate todas as classes JDK são consideradas como value types),
+ mas você pode também criar suas classes como, por exemplo, <literal>Address</literal> ou <literal>MonetaryAmount</literal>.
+
+ </para>
+
+ <para>
+ Você também pode criar uma coleção de value types. Isso é conceitualmente muito diferente
+ de uma coleção de referências para outras entidades, mas em Java parece ser quase a mesma coisa.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-valuecollections">
+ <title>Coleção de valores</title>
+
+ <para>
+ Nós adicionamos uma coleção de objetos de tipo de valores à entidade <literal>Person</literal.
+ Nós querermos armazenar endereços de e-mail, para isso utilizamos o tipo <literal>String</literal>,
+ e a coleção novamente será um <literal>Set</literal>:
+ </para>
+ <programlisting><![CDATA[private Set emailAddresses = new HashSet();
+
+public Set getEmailAddresses() {
+ return emailAddresses;
+}
+
+public void setEmailAddresses(Set emailAddresses) {
+ this.emailAddresses = emailAddresses;
+}]]></programlisting>
+
+ <para>
+ O mapeamento deste <literal>Set</literal>:
+ </para>
+
+ <programlisting><![CDATA[<set name="emailAddresses" table="PERSON_EMAIL_ADDR">
+ <key column="PERSON_ID"/>
+ <element type="string" column="EMAIL_ADDR"/>
+</set>]]></programlisting>
+
+ <para>
+ The difference compared with the earlier mapping is the <literal>element</literal> part, which tells Hibernate that the collection
+ does not contain references to another entity, but a collection of elements of type
+ <literal>String</literal> (the lowercase name tells you it's a Hibernate mapping type/converter).
+ Once again, the <literal>table</literal> attribute of the <literal>set</literal> element determines
+ the table name for the collection. The <literal>key</literal> element defines the foreign-key column
+ name in the collection table. The <literal>column</literal> attribute in the <literal>element</literal>
+ element defines the column name where the <literal>String</literal> values will actually be stored.
+
+ A diferença comparada com o mapeamento anterior se encontra na parte <literal>element</literal>,
+ que indica ao Hibernate que a coleção não contém referências à outra entidade, mas uma coleção de
+ elementos do tipo <literal>String</literal> (a tag name em miniscula indica que se trata de um
+ mapeamento do Hibernate para conversão de tipos). Mais uma vez, o atributo <literal>table</literal>
+ do elemento <literal>set</literal> determina o nome da tabela para a coleção. O elemento
+ <literal>key</literal> define o nome da coluna de chave estrangeira na tabela de coleção.
+ O atributo <literal>column</literal> dentro do elemento <literal>element</literal> define o
+ nome da coluna onde os valores da <literal>String</literal> serão armazenados.
+ </para>
+
+ <para>
+ Dê uma olhada no esquema atualizado:
+ </para>
+
+ <programlisting><![CDATA[
+ _____________ __________________
+ | | | | _____________
+ | EVENTS | | PERSON_EVENT | | | ___________________
+ |_____________| |__________________| | PERSON | | |
+ | | | | |_____________| | PERSON_EMAIL_ADDR |
+ | *EVENT_ID | <--> | *EVENT_ID | | | |___________________|
+ | EVENT_DATE | | *PERSON_ID | <--> | *PERSON_ID | <--> | *PERSON_ID |
+ | TITLE | |__________________| | AGE | | *EMAIL_ADDR |
+ |_____________| | FIRSTNAME | |___________________|
+ | LASTNAME |
+ |_____________|
+ ]]></programlisting>
+
+ <para>
+ Você pode observar que a chave primária da tabela da coleção é de na verdade uma chave composta,
+ usando ambas colunas. Isso também implica que cada pessoa não pode ter endereços de e-mail
+ duplicados, o que é exatamente a semântica que precisamos para um set em Java.
+ </para>
+
+ <para>
+ Você pode agora tentar adicionar elementos a essa coleção, do mesmo modo que fizemos
+ anteriormente ligando pessoas e eventos. à o mesmo código em Java:
+ </para>
+
+ <programlisting><![CDATA[private void addEmailToPerson(Long personId, String emailAddress) {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+ session.beginTransaction();
+
+ Person aPerson = (Person) session.load(Person.class, personId);
+
+ // The getEmailAddresses() might trigger a lazy load of the collection
+ aPerson.getEmailAddresses().add(emailAddress);
+
+ session.getTransaction().commit();
+}]]></programlisting>
+
+ <para>
+ This time we didnt' use a <emphasis>fetch</emphasis> query to initialize the collection.
+ Hence, the call to its getter method will trigger an additional select to initialize
+ it, so we can add an element to it. Monitor the SQL log and try to optimize this with
+ an eager fetch.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-bidirectional" revision="1">
+ <title>Associações bidirecionais</title>
+
+ <para>
+ Agora iremos mapear uma associação bidirecional â fazendo a associação entre pessoas e
+ eventos, de ambos os lados, em Java. Logicamente, o esquema do banco de dados não muda,
+ nós continuamos tendo multiplicidades muitos-para-muitos. Um banco de dados é mais flexÃvel do que
+ uma linguagem de programação para redes, ele não precisa de nenhuma direção de navegação â os
+ dados podem ser acessados em qualquer caminho possÃvel.
+ </para>
+
+ <para>
+ Primeiramente, adicione uma coleção de participantes à classe <literal>Event</literal>:
+ </para>
+
+ <programlisting><![CDATA[private Set participants = new HashSet();
+
+public Set getParticipants() {
+ return participants;
+}
+
+public void setParticipants(Set participants) {
+ this.participants = participants;
+}]]></programlisting>
+
+ <para>
+ Agora mapeie este lado da associação em <literal>Event.hbm.xml</literal>.
+ </para>
+
+ <programlisting><![CDATA[<set name="participants" table="PERSON_EVENT" inverse="true">
+ <key column="EVENT_ID"/>
+ <many-to-many column="PERSON_ID" class="events.Person"/>
+</set>]]></programlisting>
+
+ <para>
+ Como você pode ver, esses é uma mapeamento normal usando <literal>set</literal> em ambos documenentos
+ de mapeamento. Observe que o nome das colunas em <literal>key</literal> e <literal>many-to-many</literal>
+ estão trocados em ambos os documentos de mapeamento. A adição mais importante feita está no atributo
+ <literal>inverse="true"</literal> no elemento set do mapeamento da coleção da classe <literal>Event</literal>.
+ </para>
+
+ <para>
+ Isso significa que o Hibernate deve pegar o outro lado â a classe <literal>Person</literal> â
+ quando necessitar encontrar informação sobre a relação entre as duas entidades. Isso será muito
+ mais facilmente compreendido quando você analisar como a relação bidirecional entre as entidades é criada.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-usingbidir">
+ <title>Trabalhando com links bidirecionais</title>
+
+ <para>
+ Primeiro tenha em mente que o Hibernate não afeta a semântica normal do Java. Como nós criamos
+ um link entre uma <literal>Person</literal> e um <literal>Event</literal> no exemplo unidirecional?
+ Nós adicionamos uma instância de <literal>Event</literal>, da coleção de referências de eventos,
+ a uma instância de <literal>Person</literal>. Então, obviamente, se nós queremos que este link funcione
+ bidirecionalmente, nós devemos fazer a mesma coisa para o outro lado â adicionando uma referência de
+ <literal>Person</literal> na coleção de um <literal>Event</literal>. Esse acerto de link de ambos
+ os lados é absolutamente necessário e você nunca deve esquecer de faze-lo.
+ </para>
+
+ <para>
+ Muitos desenvolvedores programam de maneira defensiva e criam métodos
+ gerenciador de links que ajusta corretamente ambos os lados:
+ </para>
+
+ <programlisting><![CDATA[protected Set getEvents() {
+ return events;
+}
+
+protected void setEvents(Set events) {
+ this.events = events;
+}
+
+public void addToEvent(Event event) {
+ this.getEvents().add(event);
+ event.getParticipants().add(this);
+}
+
+public void removeFromEvent(Event event) {
+ this.getEvents().remove(event);
+ event.getParticipants().remove(this);
+}]]></programlisting>
+
+ <para>
+ Notice that the get and set methods for the collection are now protected - this allows classes in the
+ same package and subclasses to still access the methods, but prevents everybody else from messing
+ with the collections directly (well, almost). You should probably do the same with the collection
+ on the other side.
+ </para>
+
+ <para>
+ What about the <literal>inverse</literal> mapping attribute? For you, and for Java, a bi-directional
+ link is simply a matter of setting the references on both sides correctly. Hibernate however doesn't
+ have enough information to correctly arrange SQL <literal>INSERT</literal> and <literal>UPDATE</literal>
+ statements (to avoid constraint violations), and needs some help to handle bi-directional associations
+ properly. Making one side of the association <literal>inverse</literal> tells Hibernate to basically
+ ignore it, to consider it a <emphasis>mirror</emphasis> of the other side. That's all that is necessary
+ for Hibernate to work out all of the issues when transformation a directional navigation model to
+ a SQL database schema. The rules you have to remember are straightforward: All bi-directional associations
+ need one side as <literal>inverse</literal>. In a one-to-many association it has to be the many-side,
+ in many-to-many association you can pick either side, there is no difference.
+ </para>
+
+ </sect2>
+
+ <para>
+ Let's turn this into a small web application.
+ </para>
+
+ </sect1>
+
+ <sect1 id="tutorial-webapp">
+ <title>Part 3 - The EventManager web application</title>
+
+ <para>
+ A Hibernate web application uses <literal>Session</literal> and <literal>Transaction</literal>
+ almost like a standalone application. However, some common patterns are useful. We now write
+ an <literal>EventManagerServlet</literal>. This servlet can list all events stored in the
+ database, and it provides an HTML form to enter new events.
+ </para>
+
+ <sect2 id="tutorial-webapp-servlet" revision="1">
+ <title>Writing the basic servlet</title>
+
+ <para>
+ Create a new class in your source directory, in the <literal>events</literal>
+ package:
+ </para>
+
+ <programlisting><![CDATA[package events;
+
+// Imports
+
+public class EventManagerServlet extends HttpServlet {
+
+ // Servlet code
+}]]></programlisting>
+
+ <para>
+ The servlet handles HTTP <literal>GET</literal> requests only, hence, the method
+ we implement is <literal>doGet()</literal>:
+ </para>
+
+ <programlisting><![CDATA[protected void doGet(HttpServletRequest request,
+ HttpServletResponse response)
+ throws ServletException, IOException {
+
+ SimpleDateFormat dateFormatter = new SimpleDateFormat("dd.MM.yyyy");
+
+ try {
+ // Begin unit of work
+ HibernateUtil.getSessionFactory()
+ .getCurrentSession().beginTransaction();
+
+ // Process request and render page...
+
+ // End unit of work
+ HibernateUtil.getSessionFactory()
+ .getCurrentSession().getTransaction().commit();
+
+ } catch (Exception ex) {
+ HibernateUtil.getSessionFactory()
+ .getCurrentSession().getTransaction().rollback();
+ throw new ServletException(ex);
+ }
+
+}]]></programlisting>
+
+ <para>
+ The pattern we are applying here is called <emphasis>session-per-request</emphasis>.
+ When a request hits the servlet, a new Hibernate <literal>Session</literal> is
+ opened through the first call to <literal>getCurrentSession()</literal> on the
+ <literal>SessionFactory</literal>. Then a database transaction is started—all
+ data access as to occur inside a transaction, no matter if data is read or written
+ (we don't use the auto-commit mode in applications).
+ </para>
+
+ <para>
+ Next, the possible actions of the request are processed and the response HTML
+ is rendered. We'll get to that part soon.
+ </para>
+
+ <para>
+ Finally, the unit of work ends when processing and rendering is complete. If any
+ problem occured during processing or rendering, an exception will be thrown
+ and the database transaction rolled back. This completes the
+ <literal>session-per-request</literal> pattern. Instead of the transaction
+ demarcation code in every servlet you could also write a servlet filter.
+ See the Hibernate website and Wiki for more information about this pattern,
+ called <emphasis>Open Session in View</emphasis>—you'll need it as soon
+ as you consider rendering your view in JSP, not in a servlet.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-webapp-processing" revision="1">
+ <title>Processing and rendering</title>
+
+ <para>
+ Let's implement the processing of the request and rendering of the page.
+ </para>
+
+<programlisting><![CDATA[// Write HTML header
+PrintWriter out = response.getWriter();
+out.println("<html><head><title>Event Manager</title></head><body>");
+
+// Handle actions
+if ( "store".equals(request.getParameter("action")) ) {
+
+ String eventTitle = request.getParameter("eventTitle");
+ String eventDate = request.getParameter("eventDate");
+
+ if ( "".equals(eventTitle) || "".equals(eventDate) ) {
+ out.println("<b><i>Please enter event title and date.</i></b>");
+ } else {
+ createAndStoreEvent(eventTitle, dateFormatter.parse(eventDate));
+ out.println("<b><i>Added event.</i></b>");
+ }
+}
+
+// Print page
+printEventForm(out);
+listEvents(out, dateFormatter);
+
+// Write HTML footer
+out.println("</body></html>");
+out.flush();
+out.close();]]></programlisting>
+
+ <para>
+ Granted, this coding style with a mix of Java and HTML would not scale
+ in a more complex application—keep in mind that we are only illustrating
+ basic Hibernate concepts in this tutorial. The code prints an HTML
+ header and a footer. Inside this page, an HTML form for event entry and
+ a list of all events in the database are printed. The first method is
+ trivial and only outputs HTML:
+ </para>
+
+ <programlisting><![CDATA[private void printEventForm(PrintWriter out) {
+ out.println("<h2>Add new event:</h2>");
+ out.println("<form>");
+ out.println("Title: <input name='eventTitle' length='50'/><br/>");
+ out.println("Date (e.g. 24.12.2009): <input name='eventDate' length='10'/><br/>");
+ out.println("<input type='submit' name='action' value='store'/>");
+ out.println("</form>");
+}]]></programlisting>
+
+ <para>
+ The <literal>listEvents()</literal> method uses the Hibernate
+ <literal>Session</literal> bound to the current thread to execute
+ a query:
+ </para>
+
+ <programlisting><![CDATA[private void listEvents(PrintWriter out, SimpleDateFormat dateFormatter) {
+
+ List result = HibernateUtil.getSessionFactory()
+ .getCurrentSession().createCriteria(Event.class).list();
+ if (result.size() > 0) {
+ out.println("<h2>Events in database:</h2>");
+ out.println("<table border='1'>");
+ out.println("<tr>");
+ out.println("<th>Event title</th>");
+ out.println("<th>Event date</th>");
+ out.println("</tr>");
+ for (Iterator it = result.iterator(); it.hasNext();) {
+ Event event = (Event) it.next();
+ out.println("<tr>");
+ out.println("<td>" + event.getTitle() + "</td>");
+ out.println("<td>" + dateFormatter.format(event.getDate()) + "</td>");
+ out.println("</tr>");
+ }
+ out.println("</table>");
+ }
+}]]></programlisting>
+
+ <para>
+ Finally, the <literal>store</literal> action is dispatched to the
+ <literal>createAndStoreEvent()</literal> method, which also uses
+ the <literal>Session</literal> of the current thread:
+ </para>
+
+ <programlisting><![CDATA[protected void createAndStoreEvent(String title, Date theDate) {
+ Event theEvent = new Event();
+ theEvent.setTitle(title);
+ theEvent.setDate(theDate);
+
+ HibernateUtil.getSessionFactory()
+ .getCurrentSession().save(theEvent);
+}]]></programlisting>
+
+ <para>
+ That's it, the servlet is complete. A request to the servlet will be processed
+ in a single <literal>Session</literal> and <literal>Transaction</literal>. As
+ earlier in the standalone application, Hibernate can automatically bind these
+ ojects to the current thread of execution. This gives you the freedom to layer
+ your code and access the <literal>SessionFactory</literal> in any way you like.
+ Usually you'd use a more sophisticated design and move the data access code
+ into data access objects (the DAO pattern). See the Hibernate Wiki for more
+ examples.
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-webapp-deploy">
+ <title>Deploying and testing</title>
+
+ <para>
+ To deploy this application you have to create a web archive, a WAR. Add the
+ following Ant target to your <literal>build.xml</literal>:
+ </para>
+
+<programlisting><![CDATA[<target name="war" depends="compile">
+ <war destfile="hibernate-tutorial.war" webxml="web.xml">
+ <lib dir="${librarydir}">
+ <exclude name="jsdk*.jar"/>
+ </lib>
+
+ <classes dir="${targetdir}"/>
+ </war>
+</target>]]></programlisting>
+
+ <para>
+ This target creates a file called <literal>hibernate-tutorial.war</literal>
+ in your project directory. It packages all libraries and the <literal>web.xml</literal>
+ descriptor, which is expected in the base directory of your project:
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
+<web-app version="2.4"
+ xmlns="http://java.sun.com/xml/ns/j2ee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
+
+ <servlet>
+ <servlet-name>Event Manager</servlet-name>
+ <servlet-class>events.EventManagerServlet</servlet-class>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>Event Manager</servlet-name>
+ <url-pattern>/eventmanager</url-pattern>
+ </servlet-mapping>
+</web-app>]]></programlisting>
+
+ <para>
+ Before you compile and deploy the web application, note that an additional library
+ is required: <literal>jsdk.jar</literal>. This is the Java servlet development kit,
+ if you don't have this library already, get it from the Sun website and copy it to
+ your library directory. However, it will be only used for compliation and excluded
+ from the WAR package.
+ </para>
+
+ <para>
+ To build and deploy call <literal>ant war</literal> in your project directory
+ and copy the <literal>hibernate-tutorial.war</literal> file into your Tomcat
+ <literal>webapp</literal> directory. If you don't have Tomcat installed, download
+ it and follow the installation instructions. You don't have to change any Tomcat
+ configuration to deploy this application though.
+ </para>
+
+ <para>
+ Once deployed and Tomcat is running, access the application at
+ <literal>http://localhost:8080/hibernate-tutorial/eventmanager</literal>. Make
+ sure you watch the Tomcat log to see Hibernate initialize when the first
+ request hits your servlet (the static initializer in <literal>HibernateUtil</literal>
+ is called) and to get the detailed output if any exceptions occurs.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="tutorial-summary" revision="1">
+ <title>Summary</title>
+
+ <para>
+ This tutorial covered the basics of writing a simple standalone Hibernate application
+ and a small web application.
+ </para>
+
+ <para>
+ If you already feel confident with Hibernate, continue browsing through the reference
+ documentation table of contents for topics you find interesting - most asked are
+ transactional processing (<xref linkend="transactions"/>), fetch
+ performance (<xref linkend="performance"/>), or the usage of the API (<xref linkend="objectstate"/>)
+ and the query features (<xref linkend="objectstate-querying"/>).
+ </para>
+
+ <para>
+ Don't forget to check the Hibernate website for more (specialized) tutorials.
+ </para>
+
+ </sect1>
+
+</chapter>
Property changes on: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/tutorial1.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/xml.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/xml.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/xml.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,289 @@
+<chapter id="xml">
+ <title>Mapeamento XML</title>
+
+ <para><emphasis>
+ Note that this is an experimental feature in Hibernate 3.0 and is under
+ extremely active development.
+ Veja que essa é uma feature experimental no Hibernate 3.0 e o
+ desenvolvimento esta bastante ativo.
+
+ </emphasis></para>
+
+ <sect1 id="xml-intro" revision="1">
+ <title>Trabalhando com dados em XML</title>
+
+ <para>
+ O Hibernate permite que se trabalhe com dados persistentes em XML quase
+ da mesma maneira como você trabalhar com POJOs persistentes. Uma árvore XML
+ parseada, pode ser imaginada como apenas uma maneira de representar os
+ dados relacionais como objetos, ao invés dos POJOs.
+ </para>
+
+ <para>
+ O Hibernate suporta a API dom4j para manipular árvores XML. Você pode escrever
+ queries que retornem árvores dom4j do banco de dados e automaticamente
+ sincronizar com o banco de dados qualquer modificação feita nessas árvores.
+ Você pode até mesmo pegar um documento XML, parsear usando o dom4j, e escrever
+ as alterações no banco de dados usando quaisquer operações básicas do Hibernate:
+ <literal>persist(), saveOrUpdate(),merge(), delete(), replicate()</literal>
+ (merging ainda não é suportado)
+ </para>
+
+ <para>
+ Essa funcionalidade tem várias aplicações incluindo importação/exportação de dados,
+ externalização de dados de entidade via JMS or SOAP e relatórios usando XSLT.
+ </para>
+
+ <para>
+ Um mapeamento simples pode ser usado para simultaneamente mapear propriedades
+ da classe e nós de um documento XML para um banco de dados ou, se não houver
+ classe para mapear, pode ser usado simplesmente para mapear o XML.
+ </para>
+
+ <sect2 id="xml-intro-mapping">
+ <title>Especificando o mapeamento de uma classe e de um arquivo XML simultaneamente</title>
+
+ <para>
+ Segue um exemplo de como mapear um POJO e um XML ao mesmo tempo:
+ </para>
+
+ <programlisting><![CDATA[<class name="Account"
+ table="ACCOUNTS"
+ node="account">
+
+ <id name="accountId"
+ column="ACCOUNT_ID"
+ node="@id"/>
+
+ <many-to-one name="customer"
+ column="CUSTOMER_ID"
+ node="customer/@id"
+ embed-xml="false"/>
+
+ <property name="balance"
+ column="BALANCE"
+ node="balance"/>
+
+ ...
+
+</class>]]></programlisting>
+ </sect2>
+
+ <sect2 id="xml-onlyxml">
+ <title>Especificando somente um mapeamento XML</title>
+
+ <para>
+ Segue um exemplo que não contém uma classe POJO:
+ </para>
+
+ <programlisting><![CDATA[<class entity-name="Account"
+ table="ACCOUNTS"
+ node="account">
+
+ <id name="id"
+ column="ACCOUNT_ID"
+ node="@id"
+ type="string"/>
+
+ <many-to-one name="customerId"
+ column="CUSTOMER_ID"
+ node="customer/@id"
+ embed-xml="false"
+ entity-name="Customer"/>
+
+ <property name="balance"
+ column="BALANCE"
+ node="balance"
+ type="big_decimal"/>
+
+ ...
+
+</class>]]></programlisting>
+
+ <para>
+ Esse mapeamento permite que você acesse os dados como uma árvore dom4j ou um
+ grafo de de pares nome de propriedade/valor (<literal>Map</literal>s do Java).
+ Os nomes de propriedades são somente construções lógicas que podem ser
+ referenciadas em consultas HQL.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="xml-mapping" revision="1">
+ <title>Mapeando metadados com XML</title>
+
+ <para>
+ Muitos elementos do mapeamento do Hibernate aceitam o atributo <literal>node</literal>.
+ Por meio dele, você pode especificar o nome de um atributo ou elemento XML que contém
+ a propriedade ou os dados da entidade. O formato do atributo <literal>node</literal>
+ deve ser o seguinte:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para><literal>"element-name"</literal> - mapeia para o elemento XML com determinado nome</para>
+ </listitem>
+ <listitem>
+ <para><literal>"@attribute-name"</literal> - mapeia para o atributo XML com determinado nome</para>
+ </listitem>
+ <listitem>
+ <para><literal>"."</literal> - mapeia para o elemento pai</para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>"element-name/@attribute-name"</literal> -
+ mapeia para para o atributo com determinado nome do elemento com determinado nome
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Para coleções e associações simples, existe o atributo adicional <literal>embed-xml</literal>.
+ Se o atributo <literal>embed-xml="true"</literal>, que é o valor padrão, a árvore XML para a
+ entidade associada (ou coleção de determinado tipo de valor) será embutida diretamente na
+ árvore XML que contém a associação. Por outro lado, se <literal>embed-xml="false"</literal>,
+ então apenas o valor do identificador referenciado irá aparecer no XML para associações
+ simples e coleções simplesmentenão irão aparecer.
+ </para>
+
+ <para>
+ Você precisa tomar cuidado em não deixar o<literal>embed-xml="true"</literal>
+ para muitas associações, pois o XML não suporta bem referências circulares.
+ </para>
+
+ <programlisting><![CDATA[<class name="Customer"
+ table="CUSTOMER"
+ node="customer">
+
+ <id name="id"
+ column="CUST_ID"
+ node="@id"/>
+
+ <map name="accounts"
+ node="."
+ embed-xml="true">
+ <key column="CUSTOMER_ID"
+ not-null="true"/>
+ <map-key column="SHORT_DESC"
+ node="@short-desc"
+ type="string"/>
+ <one-to-many entity-name="Account"
+ embed-xml="false"
+ node="account"/>
+ </map>
+
+ <component name="name"
+ node="name">
+ <property name="firstName"
+ node="first-name"/>
+ <property name="initial"
+ node="initial"/>
+ <property name="lastName"
+ node="last-name"/>
+ </component>
+
+ ...
+
+</class>]]></programlisting>
+
+ <para>
+ Nesse caso, decidimos embutir a colenção de account ids, e não os dados de accounts.
+ A query HQL a seguir:
+ </para>
+
+ <programlisting><![CDATA[from Customer c left join fetch c.accounts where c.lastName like :lastName]]></programlisting>
+
+ <para>
+ Retornaria um conjunto de dados como esse:
+ </para>
+
+ <programlisting><![CDATA[<customer id="123456789">
+ <account short-desc="Savings">987632567</account>
+ <account short-desc="Credit Card">985612323</account>
+ <name>
+ <first-name>Gavin</first-name>
+ <initial>A</initial>
+ <last-name>King</last-name>
+ </name>
+ ...
+</customer>]]></programlisting>
+
+ <para>
+ Se você setar <literal>embed-xml="true"</literal> em um mapeamento
+ <literal><one-to-many></literal>, os dados se pareceriam com o seguinte:
+ </para>
+
+ <programlisting><![CDATA[<customer id="123456789">
+ <account id="987632567" short-desc="Savings">
+ <customer id="123456789"/>
+ <balance>100.29</balance>
+ </account>
+ <account id="985612323" short-desc="Credit Card">
+ <customer id="123456789"/>
+ <balance>-2370.34</balance>
+ </account>
+ <name>
+ <first-name>Gavin</first-name>
+ <initial>A</initial>
+ <last-name>King</last-name>
+ </name>
+ ...
+</customer>]]></programlisting>
+
+ </sect1>
+
+
+ <sect1 id="xml-manipulation" revision="1">
+ <title>Manipulando dados em XML</title>
+
+ <para>
+ Vamos reler e atualizar documentos em XML em nossa aplicação. Nós fazemos isso
+ obtendo uma session do dom4j:
+ </para>
+
+ <programlisting><![CDATA[Document doc = ....;
+
+Session session = factory.openSession();
+Session dom4jSession = session.getSession(EntityMode.DOM4J);
+Transaction tx = session.beginTransaction();
+
+List results = dom4jSession
+ .createQuery("from Customer c left join fetch c.accounts where c.lastName like :lastName")
+ .list();
+for ( int i=0; i<results.size(); i++ ) {
+ //add the customer data to the XML document
+ Element customer = (Element) results.get(i);
+ doc.add(customer);
+}
+
+tx.commit();
+session.close();]]></programlisting>
+
+ <programlisting><![CDATA[Session session = factory.openSession();
+Session dom4jSession = session.getSession(EntityMode.DOM4J);
+Transaction tx = session.beginTransaction();
+
+Element cust = (Element) dom4jSession.get("Customer", customerId);
+for ( int i=0; i<results.size(); i++ ) {
+ Element customer = (Element) results.get(i);
+ //change the customer name in the XML and database
+ Element name = customer.element("name");
+ name.element("first-name").setText(firstName);
+ name.element("initial").setText(initial);
+ name.element("last-name").setText(lastName);
+}
+
+tx.commit();
+session.close();]]></programlisting>
+
+ <para>
+ à extremamente útil combinar essa funcionalidade com a operação <literal>replicate()</literal>
+ do Hibernate para implementar importação/exportação baseadas em XML.
+ </para>
+
+ </sect1>
+
+</chapter>
+
Property changes on: trunk/Hibernate3/documentation/manual/pt-BR/src/main/docbook/modules/xml.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/css/html.css
===================================================================
--- trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/css/html.css (rev 0)
+++ trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/css/html.css 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,97 @@
+A {
+ color: #003399;
+}
+
+A:active {
+ color: #003399;
+}
+
+A:visited {
+ color: #888888;
+}
+
+P, OL, UL, LI, DL, DT, DD, BLOCKQUOTE {
+ color: #000000;
+}
+
+TD, TH, SPAN {
+ color: #000000;
+}
+
+BLOCKQUOTE {
+ margin-right: 0px;
+}
+
+
+H1, H2, H3, H4, H5, H6 {
+ color: #000000;
+ font-weight:500;
+ margin-top:10px;
+ padding-top:15px;
+}
+
+TABLE {
+ border-collapse: collapse;
+ border-spacing:0;
+ border: 1px thin black;
+ empty-cells: hide;
+}
+
+TD {
+ padding: 4pt;
+}
+
+H1 { font-size: 150%; }
+H2 { font-size: 140%; }
+H3 { font-size: 110%; font-weight: bold; }
+H4 { font-size: 110%; font-weight: bold;}
+H5 { font-size: 100%; font-style: italic; }
+H6 { font-size: 100%; font-style: italic; }
+
+TT {
+font-size: 90%;
+ font-family: "Courier New", Courier, monospace;
+ color: #000000;
+}
+
+PRE {
+font-size: 100%;
+ padding: 5px;
+ border-style: solid;
+ border-width: 1px;
+ border-color: #CCCCCC;
+ background-color: #F4F4F4;
+}
+
+UL, OL, LI {
+ list-style: disc;
+}
+
+HR {
+ width: 100%;
+ height: 1px;
+ background-color: #CCCCCC;
+ border-width: 0px;
+ padding: 0px;
+ color: #CCCCCC;
+}
+
+.variablelist {
+ padding-top: 10;
+ padding-bottom:10;
+ margin:0;
+}
+
+.itemizedlist, UL {
+ padding-top: 0;
+ padding-bottom:0;
+ margin:0;
+}
+
+.term {
+ font-weight:bold;
+}
+
+
+
+
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/AuthorWork.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/AuthorWork.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/AuthorWork.zargo
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/AuthorWork.zargo
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/CustomerOrderProduct.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/CustomerOrderProduct.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/CustomerOrderProduct.zargo
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/CustomerOrderProduct.zargo
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/EmployerEmployee.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/EmployerEmployee.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/EmployerEmployee.zargo
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/EmployerEmployee.zargo
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/full_cream.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/full_cream.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/full_cream.svg
===================================================================
--- trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/full_cream.svg (rev 0)
+++ trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/full_cream.svg 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,429 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"
+[
+ <!ATTLIST svg
+ xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink">
+]>
+<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
+<svg
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="354.331"
+ height="336.614"
+ id="svg1">
+ <defs
+ id="defs3">
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop128" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="1"
+ id="stop129" />
+ </linearGradient>
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient130"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ <radialGradient
+ cx="0.5"
+ cy="0.5"
+ fx="0.5"
+ fy="0.5"
+ r="0.5"
+ id="radialGradient131"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ </defs>
+ <g
+ transform="matrix(0.823795,0,0,0.823795,0.120302,5.25349)"
+ style="font-size:12;"
+ id="g659">
+ <rect
+ width="212.257"
+ height="57.2441"
+ x="17.9576"
+ y="100.132"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect137" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ transform="matrix(0.743454,0,0,0.482981,6.46949,52.2178)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect132" />
+ </g>
+ <rect
+ width="325.86"
+ height="63.6537"
+ x="17.4083"
+ y="15.194"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect136" />
+ <rect
+ width="325.86"
+ height="63.6537"
+ x="13.6713"
+ y="12.4966"
+ style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect126" />
+ <g
+ transform="matrix(1.14345,0,0,0.729078,-1.67818,105.325)"
+ style="font-size:12;"
+ id="g164">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="222.966"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect138" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="221.002"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect133" />
+ </g>
+ <text
+ x="170.824753"
+ y="58.402939"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text183">
+ <tspan
+ x="170.824997"
+ y="58.402901"
+ id="tspan360">
+Application</tspan>
+ </text>
+ <text
+ x="178.076340"
+ y="364.281433"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text197">
+ <tspan
+ x="178.076004"
+ y="364.281006"
+ id="tspan421">
+Database</tspan>
+ </text>
+ <text
+ x="68.605331"
+ y="138.524582"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text216">
+ <tspan
+ x="68.605301"
+ y="138.524994"
+ id="tspan384">
+SessionFactory</tspan>
+ </text>
+ <rect
+ width="67.0014"
+ height="101.35"
+ x="196.927"
+ y="89.2389"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect387" />
+ <rect
+ width="67.0014"
+ height="101.35"
+ x="194.633"
+ y="86.4389"
+ style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect388" />
+ <text
+ x="249.108841"
+ y="173.885559"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text389">
+ <tspan
+ x="249.108994"
+ y="173.886002"
+ id="tspan392">
+Session</tspan>
+ </text>
+ <rect
+ width="73.0355"
+ height="101.35"
+ x="270.995"
+ y="90.0018"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect395" />
+ <rect
+ width="73.0355"
+ height="101.35"
+ x="267.869"
+ y="87.2018"
+ style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect396" />
+ <text
+ x="328.593658"
+ y="174.715622"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text397">
+ <tspan
+ x="328.593994"
+ y="174.716003"
+ id="tspan563">
+Transaction</tspan>
+ </text>
+ <g
+ transform="matrix(0.29544,0,0,0.397877,9.70533,103.96)"
+ style="font-size:12;"
+ id="g565">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect566" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect567" />
+ </g>
+ <text
+ x="25.592752"
+ y="204.497803"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:10;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text568">
+ <tspan
+ x="25.592800"
+ y="204.498001"
+ id="tspan662">
+TransactionFactory</tspan>
+ </text>
+ <g
+ transform="matrix(0.298082,0,0,0.397877,99.6898,103.96)"
+ style="font-size:12;"
+ id="g573">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect574" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect575" />
+ </g>
+ <text
+ x="134.030670"
+ y="205.532791"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:10;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text576">
+ <tspan
+ x="134.031006"
+ y="205.533005"
+ id="tspan664">
+ConnectionProvider</tspan>
+ </text>
+ <g
+ transform="matrix(1.14345,0,0,0.729078,-1.67818,38.9539)"
+ style="font-size:12;"
+ id="g587">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="222.966"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect588" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="221.002"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect589" />
+ </g>
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="25.6196"
+ y="206.028"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect594" />
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="24.4229"
+ y="204.135"
+ style="font-size:12;fill:#b3b3b3;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect595" />
+ <text
+ x="85.575645"
+ y="282.300354"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text596">
+ <tspan
+ x="85.575600"
+ y="282.299988"
+ id="tspan607">
+JNDI</tspan>
+ </text>
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="236.937"
+ y="206.791"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect610" />
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="235.741"
+ y="204.898"
+ style="font-size:12;fill:#b3b3b3;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect611" />
+ <text
+ x="342.093201"
+ y="283.226410"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text612">
+ <tspan
+ x="342.092987"
+ y="283.226013"
+ id="tspan621">
+JTA</tspan>
+ </text>
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="130.134"
+ y="206.791"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect616" />
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="128.937"
+ y="204.898"
+ style="font-size:12;fill:#b3b3b3;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect617" />
+ <text
+ x="212.445343"
+ y="283.226410"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text618">
+ <tspan
+ x="212.445007"
+ y="283.226013"
+ id="tspan623">
+JDBC</tspan>
+ </text>
+ <g
+ transform="matrix(0.823795,0,0,0.823795,0.120302,6.19341)"
+ style="font-size:12;"
+ id="g637">
+ <g
+ transform="matrix(0.499515,0,0,0.415467,-0.237339,5.61339)"
+ id="g167">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect134" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect135" />
+ </g>
+ <text
+ x="33.749969"
+ y="50.589706"
+ style="font-size:11;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text188">
+ <tspan
+ x="33.750000"
+ y="50.589699"
+ id="tspan635">
+Transient Objects</tspan>
+ </text>
+ </g>
+ <g
+ transform="matrix(0.823795,0,0,0.823795,0.120302,5.25349)"
+ style="font-size:12;"
+ id="g644">
+ <g
+ transform="matrix(0.297486,0,0,0.516482,230.251,36.9178)"
+ id="g364">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect365" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect366" />
+ </g>
+ <text
+ x="277.123230"
+ y="85.155571"
+ style="font-size:11;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text367">
+ <tspan
+ x="277.122986"
+ y="85.155602"
+ id="tspan631">
+Persistent</tspan>
+ <tspan
+ x="277.122986"
+ y="96.155602"
+ id="tspan633">
+Objects</tspan>
+ </text>
+ </g>
+</svg>
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/hibernate_logo_a.png
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/hibernate_logo_a.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/lite.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/lite.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/lite.svg
===================================================================
--- trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/lite.svg (rev 0)
+++ trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/lite.svg 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,334 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"
+[
+ <!ATTLIST svg
+ xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink">
+]>
+<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
+<svg
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="318.898"
+ height="248.031"
+ id="svg1">
+ <defs
+ id="defs3">
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop128" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="1"
+ id="stop129" />
+ </linearGradient>
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient130"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ <radialGradient
+ cx="0.5"
+ cy="0.5"
+ fx="0.5"
+ fy="0.5"
+ r="0.5"
+ id="radialGradient131"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ </defs>
+ <rect
+ width="291.837"
+ height="57.0074"
+ x="17.3169"
+ y="18.646"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect136" />
+ <rect
+ width="291.837"
+ height="57.0074"
+ x="13.9703"
+ y="16.2302"
+ style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect126" />
+ <g
+ transform="matrix(0.326107,0,0,0.765831,9.59261,8.98517)"
+ style="font-size:12;"
+ id="g161">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect137" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect132" />
+ </g>
+ <g
+ transform="matrix(1.02406,0,0,0.652953,0.223384,39.9254)"
+ style="font-size:12;"
+ id="g164">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="222.966"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect138" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="221.002"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect133" />
+ </g>
+ <g
+ transform="matrix(0.449834,0,0,0.338463,-3.15909,9.73319)"
+ style="font-size:12;"
+ id="g167">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect134" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect135" />
+ </g>
+ <text
+ x="302.277679"
+ y="65.943230"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text183">
+ <tspan
+ x="302.277954"
+ y="65.943184"
+ id="tspan360">
+Application</tspan>
+ </text>
+ <text
+ x="36.235924"
+ y="63.796055"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:14;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text188">
+ <tspan
+ x="36.235950"
+ y="63.796051"
+ id="tspan427">
+Transient Objects</tspan>
+ </text>
+ <text
+ x="180.416245"
+ y="290.543701"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text197">
+ <tspan
+ x="180.415939"
+ y="290.543549"
+ id="tspan421">
+Database</tspan>
+ </text>
+ <text
+ x="25.037701"
+ y="179.154755"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text216">
+ <tspan
+ x="25.037655"
+ y="179.154648"
+ id="tspan384">
+SessionFactory</tspan>
+ </text>
+ <g
+ transform="matrix(0.252763,0,0,0.765831,109.104,8.98517)"
+ style="font-size:12;"
+ id="g386">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect387" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect388" />
+ </g>
+ <g
+ transform="matrix(0.297394,0,0,0.572692,101.502,21.6359)"
+ style="font-size:12;"
+ id="g364">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect365" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect366" />
+ </g>
+ <text
+ x="202.746506"
+ y="102.992203"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:14;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text367">
+ <tspan
+ x="202.746948"
+ y="102.992249"
+ id="tspan423">
+Persistent</tspan>
+ <tspan
+ x="202.746948"
+ y="116.992355"
+ id="tspan425">
+Objects</tspan>
+ </text>
+ <text
+ x="174.458496"
+ y="180.080795"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text389">
+ <tspan
+ x="174.458618"
+ y="180.080338"
+ id="tspan392">
+Session</tspan>
+ </text>
+ <g
+ transform="matrix(0.127369,0,0,0.765831,188.675,8.98517)"
+ style="font-size:12;"
+ id="g394">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect395" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect396" />
+ </g>
+ <text
+ x="260.413269"
+ y="179.154739"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text397">
+ <tspan
+ x="260.412964"
+ y="179.154343"
+ id="tspan400">
+JDBC</tspan>
+ </text>
+ <g
+ transform="matrix(0.127369,0,0,0.765831,229.156,8.98517)"
+ style="font-size:12;"
+ id="g405">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect406" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect407" />
+ </g>
+ <text
+ x="320.606903"
+ y="179.154739"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text408">
+ <tspan
+ x="320.606964"
+ y="179.154343"
+ id="tspan417">
+JNDI</tspan>
+ </text>
+ <g
+ transform="matrix(0.127369,0,0,0.765831,269.281,8.98517)"
+ style="font-size:12;"
+ id="g411">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect412" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect413" />
+ </g>
+ <text
+ x="377.096313"
+ y="179.154739"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text414">
+ <tspan
+ x="377.096008"
+ y="179.154999"
+ id="tspan145">
+JTA</tspan>
+ </text>
+</svg>
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/overview.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/overview.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/overview.svg
===================================================================
--- trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/overview.svg (rev 0)
+++ trunk/Hibernate3/documentation/manual/pt-BR/src/main/resources/shared/images/overview.svg 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,250 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"
+[
+ <!ATTLIST svg
+ xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink">
+]>
+<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
+<svg
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="248.031"
+ height="248.031"
+ id="svg1">
+ <defs
+ id="defs3">
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop128" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="1"
+ id="stop129" />
+ </linearGradient>
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient130"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ <radialGradient
+ cx="0.5"
+ cy="0.5"
+ fx="0.5"
+ fy="0.5"
+ r="0.5"
+ id="radialGradient131"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ </defs>
+ <g
+ transform="matrix(0.771934,0,0,0.771934,4.36019,-3.02123)"
+ style="font-size:12;"
+ id="g158">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="17.3527"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect136" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="15.3883"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect126" />
+ </g>
+ <g
+ transform="matrix(0.771934,0,0,0.771934,4.36019,3.04452)"
+ style="font-size:12;"
+ id="g161">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect137" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect132" />
+ </g>
+ <g
+ transform="matrix(0.771934,0,0,0.771934,4.36019,8.0993)"
+ style="font-size:12;"
+ id="g164">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="222.966"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect138" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="221.002"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect133" />
+ </g>
+ <g
+ transform="matrix(0.771934,0,0,0.543505,2.59104,21.1103)"
+ style="font-size:12;"
+ id="g167">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect134" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect135" />
+ </g>
+ <text
+ x="105.392174"
+ y="56.568123"
+ transform="scale(0.771934,0.771934)"
+ style="font-size:24;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text183">
+ <tspan
+ x="105.392273"
+ y="56.568146"
+ id="tspan186">
+Application</tspan>
+ </text>
+ <text
+ x="81.820183"
+ y="103.149330"
+ transform="scale(0.771934,0.771934)"
+ style="font-size:20;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text188">
+ <tspan
+ x="81.820213"
+ y="103.149727"
+ id="tspan206">
+Persistent Objects</tspan>
+ </text>
+ <text
+ x="111.548180"
+ y="278.927887"
+ transform="scale(0.771934,0.771934)"
+ style="font-size:24;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text197">
+ <tspan
+ x="111.547874"
+ y="278.927551"
+ id="tspan200">
+Database</tspan>
+ </text>
+ <text
+ x="94.436180"
+ y="153.805740"
+ transform="scale(0.771934,0.771934)"
+ style="font-size:24;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text216">
+ <tspan
+ x="94.436180"
+ y="153.805740"
+ id="tspan221">
+HIBERNATE</tspan>
+ </text>
+ <g
+ transform="matrix(0.771934,0,0,0.771934,2.59083,1.02261)"
+ style="font-size:12;"
+ id="g254">
+ <g
+ transform="translate(4.58374,2.61928)"
+ id="g176">
+ <g
+ transform="matrix(0.571429,0,0,0.67347,-10.6174,117.093)"
+ id="g170">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect171" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect172" />
+ </g>
+ <g
+ transform="matrix(0.571429,0,0,0.67347,138.682,117.093)"
+ id="g173">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect174" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect175" />
+ </g>
+ </g>
+ <text
+ x="47.259438"
+ y="182.367538"
+ style="font-weight:bold;stroke-width:1pt;font-family:Courier;"
+ id="text191">
+ <tspan
+ x="47.259399"
+ y="182.367996"
+ id="tspan212">
+hibernate.</tspan>
+ <tspan
+ x="47.259399"
+ y="194.367996"
+ id="tspan214">
+properties</tspan>
+ </text>
+ <text
+ x="198.523010"
+ y="188.260941"
+ style="font-weight:normal;stroke-width:1pt;font-family:helvetica;"
+ id="text194">
+ <tspan
+ id="tspan195">
+XML Mapping</tspan>
+ </text>
+ </g>
+</svg>
Added: trunk/Hibernate3/documentation/manual/zh-CN/README_CHINESE_FONT.txt
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/README_CHINESE_FONT.txt (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/README_CHINESE_FONT.txt 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,6 @@
+Please be noticed that to build the pdf in Simplified Chinese, you need SimHei.ttf and SimSun.ttf from windows system, which can be found at \Windows\Fonts.
+Please copy these 2 files into fop folder before build.
+
+Thanks!
+
+Xiaogang Cao.
\ No newline at end of file
Added: trunk/Hibernate3/documentation/manual/zh-CN/pom.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/pom.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/pom.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-manual</artifactId>
+ <version>3.3.0.beta1</version>
+ <relativePath>../../../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>hibernate-manual-${translation}</artifactId>
+ <packaging>pom</packaging>
+ <name>Hibernate Manual (${translation})</name>
+
+ <properties>
+ <translation>zh-CN</translation>
+ </properties>
+
+</project>
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/glossary.txt
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/glossary.txt (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/glossary.txt 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,27 @@
+O/R Mapping 对象/å
³ç³»æ°æ®åºæ å°
+identifier property: æ è¯å±æ§
+discriminator: 辨å«æ å¿(ä¸ä½¿ç¨"é´å«å¨")
+
+join-subclass,union-subclass,join: ä¸ç¿»è¯
+
+fine-grained ç»ç²åº¦
+domain model é¢å模å
+business object ä¸å¡å¯¹è±¡(v2çæ¶åç¿»è¯ä¸º"åä¸å¯¹è±¡",被æ æ°äººçéª....)
+annotations 注解(è¿ä¸ªè¯è¿æ¯è¾æ°,ä¹è§å°æç¿»è¯ä¸º"ç¹æ®æ³¨é"/"æ 注"ç)
+
+Aggregate functions ç»è®¡å½æ° (èéå½æ°?èåå½æ°? è¿æ¯ç¿»è¯ä¸ºç»è®¡å½æ°æè½è®©äººçæ)
+Criteria Queries æ¡ä»¶æ¥è¯¢(æ åæ¥è¯¢?è¿éCriteriaæ¯"å¤æ®"çå«ä¹,èé"æ å",æè¯ä¸ºæ¡ä»¶æ¥è¯¢æ´å æç½)
+
+session flush sessionæ¸
æ´
+lazy fetching 延è¿å è½½(æå è½½,ç¿»è¯å¾ç´ç½,ä½æ¯å´ä¸æç解)
+
+对象ç¶æï¼
+ persistent class æä¹
åç±»
+ persistent object æä¹
对象 (ä½ç°è¿æ¯ä¸æ个æä¹
å设å¤èç³»ç,æçµä¹åä¸æ¶å¤±)
+ transient object ç¬æ¶å¯¹è±¡ (å
¶å«ä¹æ¯ä»
å¨å
åä¸åå¨ç,æçµä¹åæ¶å¤±,ç¿»è¯ä¸ºç¬æ¶å¯¹è±¡æ¯è¾å¥½)
+ detached object è±ç®¡å¯¹è±¡(ä¸sessionè±ç¦»ç对象)
+
+outer-join fetching å¤è¿æ¥æå (fetchä¸è¬ç¿»è¯ä¸º"è·å",v2ç¿»è¯çæ¶å,为äºçªåºå
¶ä¸»å¨è·åçç¹ç¹,ç¿»è¯ä¸º"æå")
+
+class hierarchy ç±»åå±ç»æ http://www.umlchina.com/xprogrammer/glossary1.htm
+inheritance hierarchy 继æ¿å±æ¬¡ http://www-128.ibm.com/developerworks/cn/java/j-aopwork4/?ca=dwcn-newsletter-java
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/master.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/master.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/master.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,474 @@
+<?xml version='1.0' encoding="UTF-8"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3CR3//EN"
+ "../support/docbook-dtd/docbookx.dtd"
+[
+<!ENTITY tutorial SYSTEM "modules/tutorial.xml">
+<!ENTITY architecture SYSTEM "modules/architecture.xml">
+<!ENTITY configuration SYSTEM "modules/configuration.xml">
+<!ENTITY persistent-classes SYSTEM "modules/persistent_classes.xml">
+<!ENTITY basic-mapping SYSTEM "modules/basic_mapping.xml">
+<!ENTITY collection-mapping SYSTEM "modules/collection_mapping.xml">
+<!ENTITY association-mapping SYSTEM "modules/association_mapping.xml">
+<!ENTITY component-mapping SYSTEM "modules/component_mapping.xml">
+<!ENTITY inheritance-mapping SYSTEM "modules/inheritance_mapping.xml">
+<!ENTITY session-api SYSTEM "modules/session_api.xml">
+<!ENTITY transactions SYSTEM "modules/transactions.xml">
+<!ENTITY events SYSTEM "modules/events.xml">
+<!ENTITY batch SYSTEM "modules/batch.xml">
+<!ENTITY query-hql SYSTEM "modules/query_hql.xml">
+<!ENTITY query-criteria SYSTEM "modules/query_criteria.xml">
+<!ENTITY query-sql SYSTEM "modules/query_sql.xml">
+<!ENTITY filters SYSTEM "modules/filters.xml">
+<!ENTITY xml SYSTEM "modules/xml.xml">
+<!ENTITY performance SYSTEM "modules/performance.xml">
+<!ENTITY toolset-guide SYSTEM "modules/toolset_guide.xml">
+<!ENTITY example-parentchild SYSTEM "modules/example_parentchild.xml">
+<!ENTITY example-weblog SYSTEM "modules/example_weblog.xml">
+<!ENTITY example-mappings SYSTEM "modules/example_mappings.xml">
+<!ENTITY best-practices SYSTEM "modules/best_practices.xml">
+]>
+
+
+<book lang="zh-cn">
+
+ <bookinfo>
+ <title>HIBERNATE - 符åJavaä¹ æ¯çå
³ç³»æ°æ®åºæä¹
å</title>
+
+ <subtitle>Hibernateåèææ¡£</subtitle>
+ <releaseinfo>3.2</releaseinfo>
+ </bookinfo>
+
+ <toc/>
+
+ <preface id="preface" revision="2">
+ <title>åè¨</title>
+
+ <para>
+ WARNING! This is a translated version of the English Hibernate reference
+ documentation. The translated version might not be up to date! However, the
+ differences should only be very minor. Consult the English reference
+ documentation if you are missing information or encounter a translation
+ error. If you like to contribute to a particular translation, contact us
+ on the Hibernate developer mailing list.
+ </para>
+
+ <para>
+ Translator(s): RedSaga Translate Team 满æ±çº¢ç¿»è¯å¢é <caoxg at yahoo.com>
+ </para>
+
+ <para>
+ å¨ä»æ¥çä¼ä¸ç¯å¢ä¸ï¼æé¢å对象ç软件åå
³ç³»æ°æ®åºä¸èµ·ä½¿ç¨å¯è½æ¯ç¸å½éº»ç¦ã浪费æ¶é´çãHibernateæ¯ä¸ä¸ªé¢åJavaç¯å¢ç对象/å
³ç³»æ°æ®åºæ å°å·¥å
·ã对象/å
³ç³»æ°æ®åºæ å°(object/relational mapping (ORM))è¿ä¸ªæ¯è¯è¡¨ç¤ºä¸ç§ææ¯ï¼ç¨æ¥æ对象模å表示ç对象æ å°å°åºäºSQLçå
³ç³»æ¨¡åæ°æ®ç»æä¸å»ã
+ </para>
+
+ <para>
+ Hibernateä¸ä»
ä»
管çJavaç±»å°æ°æ®åºè¡¨çæ å°ï¼å
æ¬Javaæ°æ®ç±»åå°SQLæ°æ®ç±»åçæ å°ï¼ï¼è¿æä¾æ°æ®æ¥è¯¢åè·åæ°æ®çæ¹æ³ï¼å¯ä»¥å¤§å¹
度åå°å¼åæ¶äººå·¥ä½¿ç¨SQLåJDBCå¤çæ°æ®çæ¶é´ã
+ </para>
+
+ <para>
+ Hibernateçç®æ æ¯å¯¹äºå¼åè
é常çæ°æ®æä¹
åç¸å
³çç¼ç¨ä»»å¡ï¼è§£æ¾å
¶ä¸ç95%ã对äºä»¥æ°æ®ä¸ºä¸å¿çç¨åºæ¥è¯´,å®ä»¬å¾å¾åªå¨æ°æ®åºä¸ä½¿ç¨åå¨è¿ç¨æ¥å®ç°åä¸é»è¾,Hibernateå¯è½ä¸æ¯æ好ç解å³æ¹æ¡;对äºé£äºå¨åºäºJavaçä¸é´å±åºç¨ä¸ï¼å®ä»¬å®ç°é¢å对象çä¸å¡æ¨¡åååä¸é»è¾çåºç¨ï¼Hibernateæ¯ææç¨çãä¸ç®¡ææ ·ï¼Hibernateä¸å®å¯ä»¥å¸®å©ä½ æ¶é¤æè
å
è£
é£äºé对ç¹å®ååçSQL代ç ï¼å¹¶ä¸å¸®ä½ æç»æéä»è¡¨æ ¼å¼ç表示形å¼è½¬æ¢å°ä¸ç³»åç对象å»ã
+ </para>
+
+ <para>
+ å¦æä½ å¯¹Hibernateå对象/å
³ç³»æ°æ®åºæ å°è¿æ¯ä¸ªæ°æï¼æè
çè³å¯¹Javaä¹ä¸çæï¼è¯·æç
§ä¸é¢çæ¥éª¤æ¥å¦ä¹ ã
+ </para>
+
+ <orderedlist>
+ <listitem>
+ <para>
+ é
读<xref linkend="tutorial"/>ï¼è¿æ¯ä¸ç¯å
å«è¯¦ç»çéæ¥æ导çæåãæ¬æåçæºä»£ç å
å«å¨åè¡å
ä¸ï¼ä½ å¯ä»¥å¨<literal>doc/reference/tutorial/</literal>ç®å½ä¸æ¾å°ã </para>
+ </listitem>
+ <listitem>
+ <para>
+ é
读<xref linkend="architecture"/>æ¥ç解Hibernateå¯ä»¥ä½¿ç¨çç¯å¢ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ æ¥çHibernateåè¡å
ä¸ç<literal>eg/</literal>ç®å½ï¼éé¢æä¸ä¸ªç®åçç¬ç«è¿è¡çç¨åºãæä½ çJDBC驱å¨æ·è´å°<literal>lib/</literal>ç®å½ä¸ï¼ä¿®æ¹ä¸ä¸<literal>src/hibernate.properties</literal>,æå®å
¶ä¸ä½ çæ°æ®åºçä¿¡æ¯ãè¿å
¥å½ä»¤è¡ï¼åæ¢å°ä½ çåè¡å
çç®å½ï¼è¾å
¥<literal>ant eg</literal>(使ç¨äºAntï¼ï¼æè
å¨Windowsæä½ç³»ç»ä¸ä½¿ç¨<literal>build eg</literal>ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ æè¿ä»½åèææ¡£ä½ä¸ºä½ å¦ä¹ ç主è¦ä¿¡æ¯æ¥æºã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ å¨Hibernate çç½ç«ä¸å¯ä»¥æ¾å°ç»å¸¸æé®çé®é¢ä¸è§£ç(FAQ)ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ å¨Hibernateç½ç«ä¸è¿æ第ä¸æ¹çæ¼ç¤ºã示ä¾åæç¨çé¾æ¥ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Hibernateç½ç«çâ社åº(Community Area)âæ¯è®¨è®ºå
³äºè®¾è®¡æ¨¡å¼ä»¥åå¾å¤æ´åæ¹æ¡(Tomcat, JBoss AS, Struts, EJB,çç)ç好å°æ¹ã
+ </para>
+ </listitem>
+ </orderedlist>
+
+ <para>
+ å¦æä½ æé®é¢ï¼è¯·ä½¿ç¨Hibernateç½ç«ä¸é¾æ¥çç¨æ·è®ºåãæ们ä¹æä¾ä¸ä¸ªJIRAé®é¢è¿½è¸ªç³»ç»ï¼æ¥æébugæ¥ååæ°åè½è¯·æ±ãå¦æä½ å¯¹å¼åHibernateæå
´è¶£ï¼è¯·å å
¥å¼åè
çé®ä»¶å表ãï¼Hibernateç½ç«ä¸çç¨æ·è®ºåæä¸ä¸ªä¸æçé¢ï¼JavaEyeä¹æHibernateä¸æçé¢,æ¨å¯ä»¥å¨é£é交æµé®é¢ä¸ç»éªãï¼
+ </para>
+
+ <para>
+ åä¸å¼åã产åæ¯æåHibernateå¹è®å¯ä»¥éè¿JBoss Inc.è·å¾ãï¼è¯·æ¥é
ï¼http://www.hibernate.org/SupportTraining/ï¼ã
+ Hibernateæ¯ä¸ä¸ªä¸ä¸çå¼æ¾æºä»£ç 项ç®(Professional Open Source project)ï¼ä¹æ¯JBoss Enterprise Middleware System(JEMS),JBossä¼ä¸çº§ä¸é´ä»¶ç³»ç»çä¸ä¸ªæ ¸å¿ç»ä»¶ã
+ </para>
+
+ <sect1 id="preface-translate-comments-zh-cn">
+ <title>ç¿»è¯è¯´æ</title>
+
+ <para>
+ æ¬ææ¡£çç¿»è¯æ¯å¨ç½ç»ä¸åä½è¿è¡çï¼ä¹ä¼ä¸ææ ¹æ®Hibernateçå级è¿è¡æ´æ°ãæä¾æ¤ææ¡£çç®çæ¯ä¸ºäºåç¼å¦ä¹ Hibernateçå¡åº¦ï¼èé代æ¿åææ¡£ãæ们建议æææè½åç读è
é½ç´æ¥é
读è±æåæãè¥æ¨å¯¹ç¿»è¯æå¼è®®ï¼æåç°ç¿»è¯é误ï¼æ¬è¯·ä¸åèµæï¼æ¥åå°å¦ä¸emailå°åï¼cao at redsaga.com
+ </para>
+
+ <para>
+ Hibernateçæ¬3çç¿»è¯ç±æ»¡æ±çº¢ç¿»è¯å¢é(RedSaga Translate Team)éä½è¿è¡ï¼è¿ä¹æ¯ä¸æ¬¡å¤§è§æ¨¡ç½ç»ç¿»è¯çè¯éªãå¨ä¸å°20天çæ¶é´å
ï¼æ们å®æäºä¸¤ç¾å¤é¡µææ¡£çç¿»è¯ï¼è¿ä¸æææ¯éè¿åå ä½ç½åéä½åªåå®æçãéè¿è¿æ¬¡ç¿»è¯ï¼æ们ä¹æäºä¸å¥å®æ´çæµç¨ï¼ä»åè¯ãææ¯å®¡æ ¸ä¸ç´å°æåå®¡æ ¸ãåå¸ãæ们çç¿»è¯å¢éè¿ä¼ç»§ç»å®åæ们çç¿»è¯æµç¨ï¼å¹¶ç¿»è¯å
¶ä»ä¼ç§çJavaå¼æºèµæï¼æ¬è¯·æå¾
ã
+ </para>
+
+ <table frame="topbot" id="redsaga-translate-team">
+ <title>
+ Hibernate v3ç¿»è¯å¢é
+ </title>
+ <tgroup cols="5" rowsep="1" colsep="1">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="2*"/>
+ <colspec colname="c3" colwidth="2*"/>
+ <colspec colname="c4" colwidth="2*"/>
+ <colspec colname="c5" colwidth="2*"/>
+ <thead>
+ <row>
+ <entry align="center">
+ åºå·
+ </entry>
+ <entry align="center">
+ æ é¢
+ </entry>
+ <entry align="center">
+ ä¸ææ é¢
+ </entry>
+ <entry align="center">
+ v3ç¿»è¯
+ </entry>
+ <entry align="center">
+ v3å®¡æ ¡
+ </entry>
+ <entry align="center">
+ v3.1å®¡æ ¡
+ </entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>--</entry>
+ <entry>Quickstart with Tomcat</entry>
+ <entry>å¨Tomcatä¸å¿«éä¸æ(3.1çæ¬ä¸åæ¶ï¼</entry>
+ <entry>æ¹æé¢</entry>
+ <entry>zoujm</entry>
+ <entry>--</entry>
+ </row>
+ <row>
+ <entry>#1</entry>
+ <entry>Turtotial</entry>
+ <entry>Hibernateå
¥é¨</entry>
+ <entry>Zheng Shuai</entry>
+ <entry>-</entry>
+ <entry>Sean Chan</entry>
+ </row>
+ <row>
+ <entry>#2</entry>
+ <entry>Architecture</entry>
+ <entry>ä½ç³»ç»æ</entry>
+ <entry>Hilton(BJUG)</entry>
+ <entry>åå¦åå</entry>
+ <entry>Sean Chan</entry>
+ </row>
+ <row>
+ <entry>#3</entry>
+ <entry>Configuration</entry>
+ <entry>é
ç½®</entry>
+ <entry>Goncha</entry>
+ <entry>mochow</entry>
+ <entry>zcgly</entry>
+ </row>
+ <row>
+ <entry>#4</entry>
+ <entry>Persistent Classes</entry>
+ <entry>æä¹
åç±»</entry>
+ <entry>æ¹æé¢</entry>
+ <entry>mochow</entry>
+ <entry>DigitalSonic</entry>
+ </row>
+ <row>
+ <entry>#5</entry>
+ <entry>Basic O/R Mapping</entry>
+ <entry>对象/å
³ç³»æ°æ®åºæ å°åºç¡(ä¸)</entry>
+ <entry>moxie</entry>
+ <entry>Kingfish</entry>
+ <entry>å¼ æé¢</entry>
+ </row>
+ <row>
+ <entry></entry>
+ <entry></entry>
+ <entry>对象/å
³ç³»æ°æ®åºæ å°åºç¡(ä¸)</entry>
+ <entry>inter_dudu</entry>
+ <entry>åå½é(vincent)</entry>
+ <entry>å¼ æé¢</entry>
+ </row>
+ <row>
+ <entry>#6</entry>
+ <entry>Collection Mapping</entry>
+ <entry>éåç±»æ å°</entry>
+ <entry>æ¹æé¢</entry>
+ <entry>robbin</entry>
+ <entry>--</entry>
+ </row>
+ <row>
+ <entry>#7</entry>
+ <entry>Association Mappings</entry>
+ <entry>å
³èå
³ç³»æ å°</entry>
+ <entry>Robbin</entry>
+ <entry>devils.advocate</entry>
+ <entry>--</entry>
+ </row>
+ <row>
+ <entry>#8</entry>
+ <entry>Component Mapping</entry>
+ <entry>ç»ä»¶æ å°</entry>
+ <entry>æ¹æé¢</entry>
+ <entry>Robbin</entry>
+ <entry>Song guo qiang</entry>
+ </row>
+ <row>
+ <entry>#9</entry>
+ <entry>Inheritance Mappings</entry>
+ <entry>继æ¿æ å°</entry>
+ <entry>morning(BJUG)</entry>
+ <entry>mochow</entry>
+ <entry>Liang cheng</entry>
+ </row>
+ <row>
+ <entry>#10</entry>
+ <entry>Working with objects</entry>
+ <entry>ä¸å¯¹è±¡å
±äº</entry>
+ <entry>ç¨å¹¿æ¥ </entry>
+ <entry>åå¦åå</entry>
+ <entry>--</entry>
+ </row>
+ <row>
+ <entry>#11</entry>
+ <entry>Transactions And Concurrency</entry>
+ <entry>äºå¡å并å</entry>
+ <entry>Robbin</entry>
+ <entry>mochow</entry>
+ <entry>--</entry>
+ </row>
+ <row>
+ <entry>#12</entry>
+ <entry>Interceptors and events</entry>
+ <entry>继æ¿æ å°</entry>
+ <entry>ä¸å½©ç¼(BJUG)</entry>
+ <entry>åå¦åå</entry>
+ <entry>--</entry>
+ </row>
+ <row>
+ <entry>#13</entry>
+ <entry>Batch processing</entry>
+ <entry>æ¹éå¤ç</entry>
+ <entry>Kingfish(BJUG)</entry>
+ <entry>åå¦åå</entry>
+ <entry>--</entry>
+ </row>
+ <row>
+ <entry>#14</entry>
+ <entry>HQL: The Hibernate Query Language</entry>
+ <entry>HQL: Hibernateæ¥è¯¢è¯è¨</entry>
+ <entry>é浩(BJUG)</entry>
+ <entry>Zheng Shuai</entry>
+ <entry>--</entry>
+ </row>
+ <row>
+ <entry>#15</entry>
+ <entry>Criteria Queries</entry>
+ <entry>æ¡ä»¶æ¥è¯¢</entry>
+ <entry>nemo(BJUG)</entry>
+ <entry>Zheng Shuai</entry>
+ <entry>--</entry>
+ </row>
+ <row>
+ <entry>#16</entry>
+ <entry>Native SQL</entry>
+ <entry>Native SQLæ¥è¯¢</entry>
+ <entry>似水æµå¹´</entry>
+ <entry>zoujm</entry>
+ <entry>--</entry>
+ </row>
+ <row>
+ <entry>#17</entry>
+ <entry>Filters</entry>
+ <entry>è¿æ»¤æ°æ®</entry>
+ <entry>å°äº(BJUG)</entry>
+ <entry>Goncha</entry>
+ <entry>--</entry>
+ </row>
+ <row>
+ <entry>#18</entry>
+ <entry>XML Mapping</entry>
+ <entry>XMLæ å°</entry>
+ <entry>edward(BJUG)</entry>
+ <entry>Goncha</entry>
+ <entry>huxb</entry>
+ </row>
+ <row>
+ <entry>#19</entry>
+ <entry>Improving performance</entry>
+ <entry>æ§è½æå</entry>
+ <entry>Wangjinfeng</entry>
+ <entry>Robbin</entry>
+ <entry>--</entry>
+ </row>
+ <row>
+ <entry>#20</entry>
+ <entry>Toolset Guide</entry>
+ <entry>å·¥å
·ç®±æå</entry>
+ <entry>æ¹æé¢</entry>
+ <entry>Robbin</entry>
+ <entry>--</entry>
+ </row>
+ <row>
+ <entry>#21</entry>
+ <entry>Example: Parent/Child</entry>
+ <entry>示ä¾ï¼ç¶åå
³ç³»</entry>
+ <entry>æ¹æé¢</entry>
+ <entry>devils.advocate</entry>
+ <entry>--</entry>
+ </row>
+ <row>
+ <entry>#22</entry>
+ <entry>Example: Weblog Application</entry>
+ <entry>示ä¾ï¼Weblog åºç¨ç¨åº</entry>
+ <entry>æ¹æé¢</entry>
+ <entry>devils.advocate</entry>
+ <entry>--</entry>
+ </row>
+ <row>
+ <entry>#23</entry>
+ <entry>Example: Various Mappings</entry>
+ <entry>示ä¾ï¼å¤ç§æ å°</entry>
+ <entry>shidu(BJUG)</entry>
+ <entry>å°äº</entry>
+ <entry>--</entry>
+ </row>
+ <row>
+ <entry>#24</entry>
+ <entry>Best Practices</entry>
+ <entry>æä½³å®è·µ</entry>
+ <entry>æ¹æé¢</entry>
+ <entry>å°äº</entry>
+ <entry>--</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ v3.2çæ¬å¨2006å¹´11æ份ç±æ¹æé¢æ´æ°ã
+ </para>
+
+ <para>
+ å
³äºæ们
+ </para>
+
+
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term>满æ±çº¢.å¼æº, http://www.redsaga.com</term>
+ <listitem>
+ <para>
+ ä»æç«ä¹åå°±è´åäºJavaå¼æ¾æºä»£ç å¨ä¸å½çä¼ æä¸åå±,ä¸å½å
å¤ä¸ªJavaå¢ä½ååºç社ææ·±å
¥äº¤æµãåæå°è¯´å¤åçååï¼ç®åæ两个å¢éï¼âOpenDocå¢éâä¸âç¿»è¯å¢éâï¼æ¬ç¿»è¯ææ¡£å³ä¸ºç¿»è¯å¢éä½åãOpenDocå¢éå·²ç»æ¨åºå
æ¬HibernateãiBatisãSpringãWebWorkçå¤ä»½å¼æ¾ææ¡£ï¼å¹¶äº2005å¹´5æå¨Hibernateå¼æ¾ææ¡£åºç¡ä¸æ©å
æ书ï¼åºçäºåå书ç±ï¼ãæ·±å
¥æµ
åºHibernateãï¼æ¬ä¹¦400ä½é¡µï¼éåå个å±æ¬¡çHibernateç¨æ·ã(http://www.redsaga.com/hibernate_book.html)æ¬è¯·æ¯æã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>å京Javaç¨æ·ç», http://www.bjug.org</term>
+ <listitem>
+ <para>
+ Beiing Java User Groupï¼æ°é´ææ¯äº¤æµç»ç»ï¼æç«äº2004å¹´6æã以交æµä¸å
±äº«ä¸ºå®æ¨ï¼æ¯ä¸¤å¨ä¸¾è¡ä¸æ¬¡ææ¯èä¼æ´»å¨ãBJUGçç®æ æ¯ï¼éè¿å°é¨å人çåªåï¼å½¢æä¸ä¸ªææ¯ç¤¾ç¾¤ï¼å建è¯å¥½ç交æµæ°å´ï¼å¹¶å°æ°çææ¯åææ³æ¨å¹¿å°æ´ä¸ªITçï¼è®©æ们å
±åè¿æ¥ã
+
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Javaè§çº¿, http://www.javaeye.com</term>
+ <listitem>
+ <para>
+ Javaè§çº¿å¨æ¯Hibernateä¸æ论åï¼http://www.hibernate.org.cnï¼Hibernateä¸æ论åæ¯ä¸å½ææ©çHibernateä¸ä¸ç¨æ·è®ºåï¼ä¸ºHibernateå¨ä¸å½çæ¨å¹¿ååºäºå·¨å¤§çè´¡ç®ï¼åºç¡ä¸åå±èµ·æ¥çJava深度ææ¯ç½ç«ï¼ç®æ æ¯æ为ä¸ä¸ªé«åè´¨çï¼æææ³æ·±åº¦çãååç²¾ç¥çJavaææ¯äº¤æµç½ç«ï¼ä¸ºè½¯ä»¶ä»ä¸äººåæä¾ä¸ä¸ªèªç±ç交æµææ¯ï¼äº¤æµææ³å交æµä¿¡æ¯çå¹³å°ã
+
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>è´è°¢</term>
+ <listitem>
+ <para>è¿æä¸äºæåç»æ们åæ¥äºå误ï¼å¨æ¤è´è°¢ï¼Kurapicaï¼ææ¯
ï¼ææµ·æã
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ </sect1>
+
+ <sect1 id="preface-translate-licence-zh-cn">
+ <title>çæ声æ</title>
+
+ <para>
+ Hibernateè±æææ¡£å±äºHibernateåè¡å
çä¸é¨åï¼éµå¾ªLGPLåè®®ãæ¬ç¿»è¯çæ¬åæ ·éµå¾ªLGPLåè®®ãåä¸ç¿»è¯çè¯è
ä¸è´åææ¾å¼é¤ç½²åæå¤å¯¹æ¬ç¿»è¯çæ¬çå
¶å®æå©è¦æ±ã
+ </para>
+
+ <para>
+ æ¨å¯ä»¥èªç±é¾æ¥ãä¸è½½ãä¼ ææ¤ææ¡£ï¼æè
æ¾ç½®å¨æ¨çç½ç«ä¸ï¼çè³ä½ä¸ºäº§åçä¸é¨ååè¡ãä½åææ¯å¿
é¡»ä¿è¯å
¨æå®æ´è½¬è½½ï¼å
æ¬å®æ´ççæä¿¡æ¯åä½è¯è
声æï¼å¹¶ä¸è½è¿åLGPLåè®®ãè¿éâå®æ´âçå«ä¹æ¯ï¼ä¸è½è¿è¡ä»»ä½å é¤/å¢æ·»/注解ãè¥æå é¤/å¢æ·»/注解ï¼å¿
é¡»é段æ确声æé£äºé¨å并éæ¬ææ¡£çä¸é¨åã
+ </para>
+ </sect1>
+
+ </preface>
+
+ &tutorial;
+
+ &architecture;
+
+ &configuration;
+
+ &persistent-classes;
+
+ &basic-mapping;
+ &collection-mapping;
+ &association-mapping;
+ &component-mapping;
+ &inheritance-mapping;
+
+ &session-api;
+ &transactions;
+ &events;
+ &batch;
+
+ &query-hql;
+ &query-criteria;
+ &query-sql;
+ &filters;
+ &xml;
+
+ &performance;
+
+ &toolset-guide;
+
+ &example-parentchild;
+ &example-weblog;
+ &example-mappings;
+
+ &best-practices;
+
+</book>
+
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/architecture.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/architecture.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/architecture.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,284 @@
+<chapter id="architecture">
+
+ <title>ä½ç³»ç»æ(Architecture)</title>
+ <sect1 id="architecture-overview" revision="1">
+ <title>æ¦åµ(Overview)</title>
+
+ <para>
+ ä¸ä¸ªé常ç®è¦çHibernateä½ç³»ç»æçæ¦è¦å¾ï¼
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/overview.svg" format="SVG" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/overview.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ ä»è¿ä¸ªå¾å¯ä»¥çåºï¼Hibernate使ç¨æ°æ®åºåé
置信æ¯æ¥ä¸ºåºç¨ç¨åºæä¾æä¹
åæå¡ï¼ä»¥åæä¹
ç对象ï¼ã
+ </para>
+
+ <para>
+ æ们æ¥æ´è¯¦ç»å°çä¸ä¸Hibernateè¿è¡æ¶ä½ç³»ç»æãç±äºHibernateé常çµæ´»ï¼ä¸æ¯æå¤ç§åºç¨æ¹æ¡ï¼
+ æ以æ们è¿åªæè¿°ä¸ä¸ä¸¤ç§æ端çæ
åµãâè½»åâçä½ç³»ç»ææ¹æ¡ï¼è¦æ±åºç¨ç¨åºæä¾èªå·±çJDBC
+ è¿æ¥å¹¶ç®¡çèªå·±çäºå¡ãè¿ç§æ¹æ¡ä½¿ç¨äºHibernate APIçæå°åéï¼
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/lite.svg" format="SVG" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/lite.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ âå
¨é¢è§£å³âçä½ç³»ç»ææ¹æ¡ï¼å°åºç¨å±ä»åºå±çJDBC/JTA APIä¸æ½è±¡åºæ¥ï¼è让Hibernateæ¥å¤çè¿äºç»èã
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/full_cream.svg" format="SVG" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/full_cream.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ å¾ä¸å个对象çå®ä¹å¦ä¸ï¼
+
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term>SessionFactory (<literal>org.hibernate.SessionFactory</literal>)</term>
+ <listitem>
+ <para>
+ é对å个æ°æ®åºæ å°å
³ç³»ç»è¿ç¼è¯åçå
åéåï¼æ¯çº¿ç¨å®å
¨çï¼ä¸å¯åï¼ã
+ å®æ¯çæ<literal>Session</literal>çå·¥åï¼æ¬èº«è¦ç¨å°<literal>ConnectionProvider</literal>ã
+ 该对象å¯ä»¥å¨è¿ç¨æé群ç级å«ä¸ï¼ä¸ºé£äºäºå¡ä¹é´å¯ä»¥éç¨çæ°æ®æä¾å¯éçäºçº§ç¼åã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Session (<literal>org.hibernate.Session</literal>)</term>
+ <listitem>
+ <para>
+ 表示åºç¨ç¨åºä¸æä¹
å¨åå±ä¹é´äº¤äºæä½çä¸ä¸ªå线ç¨å¯¹è±¡ï¼æ¤å¯¹è±¡çåæå¾çã
+ å
¶éèäºJDBCè¿æ¥ï¼ä¹æ¯<literal>Transaction</literal>çå·¥åã
+ å
¶ä¼ææä¸ä¸ªé对æä¹
å对象çå¿
éï¼ç¬¬ä¸çº§ï¼ç¼åï¼å¨éå对象å¾æè
æ ¹æ®æä¹
åæ è¯æ¥æ¾å¯¹è±¡æ¶ä¼ç¨å°ã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>æä¹
ç对象åå
¶éå</term>
+ <listitem>
+ <para>
+ 带ææä¹
åç¶æçãå
·æä¸å¡åè½çå线ç¨å¯¹è±¡ï¼æ¤å¯¹è±¡çåæå¾çã
+ è¿äºå¯¹è±¡å¯è½æ¯æ®éçJavaBeans/POJOï¼å¯ä¸ç¹æ®çæ¯ä»ä»¬æ£ä¸ï¼ä»
ä»
ä¸ä¸ªï¼<literal>Session</literal>ç¸å
³èã
+ ä¸æ¦è¿ä¸ª<literal>Session</literal>被å
³éï¼è¿äºå¯¹è±¡å°±ä¼è±ç¦»æä¹
åç¶æï¼è¿æ ·å°±å¯è¢«åºç¨ç¨åºçä»»ä½å±èªç±ä½¿ç¨ã
+ ï¼ä¾å¦ï¼ç¨ä½è·è¡¨ç¤ºå±æ交éçæ°æ®ä¼ è¾å¯¹è±¡ãï¼
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>ç¬æ(transient)åè±ç®¡(detached)ç对象åå
¶éå</term>
+ <listitem>
+ <para>
+ é£äºç®å没æä¸sessionå
³èçæä¹
åç±»å®ä¾ã
+ ä»ä»¬å¯è½æ¯å¨è¢«åºç¨ç¨åºå®ä¾ååï¼å°æªè¿è¡æä¹
åç对象ã
+ ä¹å¯è½æ¯å 为å®ä¾åä»ä»¬ç<literal>Session</literal>å·²ç»è¢«å
³éèè±ç¦»æä¹
åç对象ã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>äºå¡Transaction (<literal>org.hibernate.Transaction</literal>)</term>
+ <listitem>
+ <para>
+ ï¼å¯éçï¼åºç¨ç¨åºç¨æ¥æå®ååæä½åå
èå´ç对象ï¼å®æ¯å线ç¨çï¼çå½å¨æå¾çã
+ å®éè¿æ½è±¡å°åºç¨ä»åºå±å
·ä½çJDBCãJTA以åCORBAäºå¡é离å¼ã
+ æäºæ
åµä¸ï¼ä¸ä¸ª<literal>Session</literal>ä¹å
å¯è½å
å«å¤ä¸ª<literal>Transaction</literal>对象ã
+ 尽管æ¯å¦ä½¿ç¨è¯¥å¯¹è±¡æ¯å¯éçï¼ä½æ 论æ¯ä½¿ç¨åºå±çAPIè¿æ¯ä½¿ç¨<literal>Transaction</literal>对象ï¼äºå¡è¾¹ççå¼å¯ä¸å
³éæ¯å¿
ä¸å¯å°çã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>ConnectionProvider (<literal>org.hibernate.connection.ConnectionProvider</literal>)</term>
+ <listitem>
+ <para>
+ ï¼å¯éçï¼çæJDBCè¿æ¥çå·¥åï¼åæ¶ä¹èµ·å°è¿æ¥æ± çä½ç¨ï¼ã
+ å®éè¿æ½è±¡å°åºç¨ä»åºå±ç<literal>Datasource</literal>æ<literal>DriverManager</literal>é离å¼ã
+ ä»
ä¾å¼åè
æ©å±/å®ç°ç¨ï¼å¹¶ä¸æ´é²ç»åºç¨ç¨åºä½¿ç¨ã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>TransactionFactory (<literal>org.hibernate.TransactionFactory</literal>)</term>
+ <listitem>
+ <para>
+ ï¼å¯éçï¼çæ<literal>Transaction</literal>对象å®ä¾çå·¥åã
+ ä»
ä¾å¼åè
æ©å±/å®ç°ç¨ï¼å¹¶ä¸æ´é²ç»åºç¨ç¨åºä½¿ç¨ã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><emphasis>æ©å±æ¥å£</emphasis></term>
+ <listitem>
+ <para>
+ Hibernateæä¾äºå¾å¤å¯éçæ©å±æ¥å£ï¼ä½ å¯ä»¥éè¿å®ç°å®ä»¬æ¥å®å¶ä½ çæä¹
å±çè¡ä¸ºã
+ å
·ä½è¯·åèAPIææ¡£ã
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ <para>
+ å¨ç¹å®âè½»åâçä½ç³»ç»æä¸ï¼åºç¨ç¨åºå¯è½ç»è¿
+ <literal>Transaction</literal>/<literal>TransactionFactory</literal> 以å
+ <literal>ConnectionProvider</literal> çAPIç´æ¥è·JTAæJDBCæ交éã
+ </para>
+ </sect1>
+
+ <sect1 id="architecture-states" revision="1">
+ <title>å®ä¾ç¶æ</title>
+ <para>
+ ä¸ä¸ªæä¹
åç±»çå®ä¾å¯è½å¤äºä¸ç§ä¸åç¶æä¸çæä¸ç§ã
+ è¿ä¸ç§ç¶æçå®ä¹åä¸æè°ç<emphasis>æä¹
åä¸ä¸æ(persistence context)</emphasis>æå
³ã
+ Hibernateç<literal>Session</literal>对象就æ¯è¿ä¸ªæè°çæä¹
åä¸ä¸æï¼
+ </para>
+
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term>ç¬æï¼transientï¼</term>
+ <listitem>
+ <para>
+ 该å®ä¾ä»æªä¸ä»»ä½æä¹
åä¸ä¸æå
³èè¿ãå®æ²¡ææä¹
åæ è¯ï¼ç¸å½äºä¸»é®å¼ï¼ã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>æä¹
å(persistent)</term>
+ <listitem>
+ <para>
+ å®ä¾ç®åä¸æ个æä¹
åä¸ä¸ææå
³èã
+ å®æ¥ææä¹
åæ è¯ï¼ç¸å½äºä¸»é®å¼ï¼ï¼å¹¶ä¸å¯è½å¨æ°æ®åºä¸æä¸ä¸ªå¯¹åºçè¡ã
+ 对äºæä¸ä¸ªç¹å®çæä¹
åä¸ä¸æï¼Hibernate<emphasis>ä¿è¯</emphasis>æä¹
åæ è¯ä¸Javaæ è¯ï¼å
¶å¼ä»£è¡¨å¯¹è±¡å¨å
åä¸çä½ç½®ï¼çä»·ã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>è±ç®¡(detached)</term>
+ <listitem>
+ <para>
+ å®ä¾æ¾ç»ä¸æ个æä¹
åä¸ä¸æåçè¿å
³èï¼ä¸è¿é£ä¸ªä¸ä¸æ被å
³éäºï¼
+ æè
è¿ä¸ªå®ä¾æ¯è¢«åºåå(serialize)å°å¦å¤çè¿ç¨ã
+ å®æ¥ææä¹
åæ è¯ï¼å¹¶ä¸å¨æ°æ®åºä¸å¯è½åå¨ä¸ä¸ªå¯¹åºçè¡ã
+ 对äºè±ç®¡ç¶æçå®ä¾ï¼Hibernateä¸ä¿è¯ä»»ä½æä¹
åæ è¯åJavaæ è¯çå
³ç³»ã
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </sect1>
+
+ <sect1 id="architecture-jmx" revision="1">
+ <title>JMXæ´å</title>
+
+ <para>
+ JMXæ¯ç®¡çJavaç»ä»¶(Java components)çJ2EEæ åã Hibernate å¯ä»¥éè¿ä¸ä¸ªJMXæ åæå¡æ¥ç®¡çã
+ å¨è¿ä¸ªåè¡çæ¬ä¸ï¼æ们æä¾äºä¸ä¸ªMBeanæ¥å£çå®ç°,å³
+ <literal>org.hibernate.jmx.HibernateService</literal>ã
+ </para>
+
+ <para>
+ æ³è¦çå¦ä½å¨JBossåºç¨æå¡å¨ä¸å°Hibernateé¨ç½²ä¸ºä¸ä¸ªJMXæå¡çä¾åï¼æ¨å¯ä»¥åèJBossç¨æ·æåã
+ æ们ç°å¨è¯´ä¸ä¸å¨Jbossåºç¨æå¡å¨ä¸ï¼ä½¿ç¨JMXæ¥é¨ç½²Hibernateç好å¤ï¼
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Session管çï¼</emphasis> Hibernateç<literal>Session</literal>对象ççå½å¨æå¯ä»¥
+ èªå¨è·ä¸ä¸ªJTAäºå¡è¾¹çç»å®ãè¿æå³çä½ æ éæå·¥å¼å
³<literal>Session</literal>äº, è¿é¡¹
+ å·¥ä½ä¼ç±JBoss EJB æ¦æªå¨æ¥å®æãä½ åä¹ä¸ç¨æ
å¿ä½ ç代ç ä¸çäºå¡è¾¹çäº(é¤éä½ æ³å©ç¨Hibernateæä¾å¯é
+ ç<literal>Transaction</literal> APIæ¥èªå·±åä¸ä¸ªä¾¿äºç§»æ¤ççæä¹
å±)ã
+ ä½ éè¿è°ç¨<literal>HibernateContext</literal>æ¥è®¿é®<literal>Session</literal>ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>HAR é¨ç½²:</emphasis> é常æ
åµä¸ï¼ä½ ä¼ä½¿ç¨JBossçæå¡é¨ç½²æ述符ï¼å¨EARæ/åSARæ件ä¸ï¼æ¥é¨ç½²Hibernate JMXæå¡ã
+ è¿ç§é¨ç½²æ¹å¼æ¯æææ常è§çHibernate <literal>SessionFactory</literal>çé
ç½®é项ã
+ ä¸è¿ï¼ä½ ä»éå¨é¨ç½²æ述符ä¸ï¼ååºä½ ææçæ å°æ件çååãå¦æä½ ä½¿ç¨HARé¨ç½²æ¹å¼, JBoss
+ ä¼èªå¨æ¢æµåºä½ çHARæ件ä¸ææçæ å°æ件ã
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ è¿äºé项æ´å¤çæè¿°ï¼è¯·åèJBoss åºç¨ç¨åºç¨æ·æåã
+ </para>
+
+ <para>
+ å°Hibernate以é¨ç½²ä¸ºJMXæå¡çå¦ä¸ä¸ªå¥½å¤ï¼æ¯å¯ä»¥æ¥çHibernateçè¿è¡æ¶ç»è®¡ä¿¡æ¯ãåç
+ <xref linkend="configuration-optional-statistics"/>.
+ </para>
+ </sect1>
+
+ <sect1 id="architecture-jca" revision="1">
+ <title>对JCAçæ¯æ</title>
+ <para>
+ Hibernateä¹å¯ä»¥è¢«é
置为ä¸ä¸ªJCAè¿æ¥å¨ï¼JCA connectorï¼ãæ´å¤ä¿¡æ¯è¯·åçç½ç«ã
+ 请注æï¼Hibernate对JCAçæ¯æï¼ä»å¤äºå®éªæ§é¶æ®µã
+ </para>
+ </sect1>
+ <sect1 id="architecture-current-session" revision="2">
+ <title>ä¸ä¸æç¸å
³çï¼Contextualï¼Session</title>
+ <para>
+ 使ç¨Hibernateç大å¤æ°åºç¨ç¨åºéè¦æç§å½¢å¼çâä¸ä¸æç¸å
³çâ sessionï¼ç¹å®çsessionå¨æ´ä¸ªç¹å®çä¸ä¸æèå´å
å§ç»ææãç¶èï¼å¯¹ä¸åç±»åçåºç¨ç¨åºèè¨ï¼è¦ä¸ºä»ä¹æ¯ç»æè¿ç§âä¸ä¸æâä¸ä¸ä¸ªå®ä¹é常æ¯å°é¾çï¼ä¸åçä¸ä¸æ对âå½åâè¿ä¸ªæ¦å¿µå®ä¹äºä¸åçèå´ãå¨3.0çæ¬ä¹åï¼ä½¿ç¨Hibernateçç¨åºè¦ä¹éç¨èªè¡ç¼åçåºäº<literal>ThreadLocal</literal>çä¸ä¸æsessionï¼è¦ä¹éç¨<literal>HibernateUtil</literal>è¿æ ·çè¾
å©ç±»ï¼è¦ä¹éç¨ç¬¬ä¸æ¹æ¡æ¶ï¼æ¯å¦SpringæPico)ï¼å®ä»¬æä¾äºåºäºä»£ç(proxy)æè
åºäºæ¦æªå¨(interception)çä¸ä¸æç¸å
³sessionã
+ </para>
+ <para>
+ ä»3.0.1çæ¬å¼å§ï¼Hibernateå¢å äº<literal>SessionFactory.getCurrentSession()</literal>æ¹æ³ãä¸å¼å§ï¼å®åå®äºéç¨<literal>JTA</literal>äºå¡ï¼<literal>JTA</literal>äºå¡å®ä¹äºå½åsessionçèå´åä¸ä¸æ(scope and context)ãHibernateå¼åå¢éåä¿¡ï¼å 为æ好å 个ç¬ç«ç<literal>JTA TransactionManager</literal>å®ç°ç¨³å®å¯ç¨ï¼ä¸è®ºæ¯å¦è¢«é¨ç½²å°ä¸ä¸ª<literal>J2EE</literal>容å¨ä¸ï¼å¤§å¤æ°(åè¥ä¸æ¯ææçï¼åºç¨ç¨åºé½åºè¯¥éç¨<literal>JTA</literal>äºå¡ç®¡çãåºäºè¿ä¸ç¹ï¼éç¨<literal>JTA</literal>çä¸ä¸æç¸å
³sessionå¯ä»¥æ»¡è¶³ä½ ä¸åéè¦ã
+ </para>
+ <para>
+ æ´å¥½çæ¯ï¼ä»3.1å¼å§ï¼<literal>SessionFactory.getCurrentSession()</literal>çåå°å®ç°æ¯å¯ææçãå æ¤ï¼æ们å¼å
¥äºæ°çæ©å±æ¥å£(<literal>org.hibernate.context.CurrentSessionContext</literal>)åæ°çé
ç½®åæ°(<literal>hibernate.current_session_context_class</literal>)ï¼ä»¥ä¾¿å¯¹ä»ä¹æ¯âå½åsessionâçèå´åä¸ä¸æ(scope and context)çå®ä¹è¿è¡ææã
+ </para>
+ <para>
+ 请åé
<literal>org.hibernate.context.CurrentSessionContext</literal>æ¥å£çJavadoc,é£éæå
³äºå®çå¥çº¦ç详ç»è®¨è®ºãå®å®ä¹äºåä¸çæ¹æ³ï¼<literal>currentSession()</literal>ï¼ç¹å®çå®ç°ç¨å®æ¥è´è´£è·è¸ªå½åçä¸ä¸æsessionãHibernateå
ç½®äºæ¤æ¥å£çä¸ç§å®ç°ã
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>org.hibernate.context.JTASessionContext</literal> - å½åsessionæ ¹æ®<literal>JTA</literal>æ¥è·è¸ªåçå®ãè¿å以åçä»
æ¯æJTAçæ¹æ³æ¯å®å
¨ä¸æ ·çã详æ
请åé
Javadocã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>org.hibernate.context.ThreadLocalSessionContext</literal> - å½åsessionéè¿å½åæ§è¡ç线ç¨æ¥è·è¸ªåçå®ã详æ
ä¹è¯·åé
Javadocã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>org.hibernate.context.ManagedSessionContext</literal> - å½åsessionéè¿å½åæ§è¡ç线ç¨æ¥è·è¸ªåçå®ãä½æ¯ï¼ä½ éè¦è´è´£ä½¿ç¨è¿ä¸ªç±»çéææ¹æ³å°<literal>Session</literal>å®ä¾ç»å®ãæè
åæ¶ç»å®ï¼å®å¹¶ä¸ä¼æå¼(open)ãflushæè
å
³é(close)ä»»ä½<literal>Session</literal>ã
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ å两ç§å®ç°é½æä¾äºâæ¯æ°æ®åºäºå¡å¯¹åºä¸ä¸ªsessionâçç¼ç¨æ¨¡åï¼ä¹ç§°ä½<emphasis>æ¯æ¬¡è¯·æ±ä¸ä¸ªsession</emphasis>ãHibernate sessionçèµ·å§åç»ç»ç±æ°æ®åºäºå¡ççåæ¥æ§å¶ãåè¥ä½ å¨çº¯ç²¹ç Java SEä¹ä¸éç¨èªè¡ç¼å代ç æ¥ç®¡çäºå¡,èä¸ä½¿ç¨JTAï¼å»ºè®®ä½ 使ç¨Hibernate <literal>Transaction</literal> APIæ¥æåºå±äºå¡å®ç°ä»ä½ ç代ç ä¸éèæãå¦æä½ ä½¿ç¨JTAï¼è¯·ä½¿ç¨JTAåå£æ¥ç®¡çTransactionãå¦æä½ å¨æ¯æCMTçEJB容å¨ä¸æ§è¡ä»£ç ï¼äºå¡è¾¹çæ¯å£°æå¼å®ä¹çï¼ä½ ä¸éè¦å¨ä»£ç ä¸è¿è¡ä»»ä½äºå¡æsession管çæä½ã请åé
<xref linkend="transactions"/>ä¸èæ¥é
读æ´å¤çå
容å示ä¾ä»£ç ã
+ </para>
+
+ <para>
+ <literal>hibernate.current_session_context_class</literal>é
ç½®åæ°å®ä¹äºåºè¯¥éç¨åªä¸ª<literal>org.hibernate.context.CurrentSessionContext</literal>å®ç°ã注æï¼ä¸ºäºåä¸å
¼å®¹ï¼å¦ææªé
ç½®æ¤åæ°ï¼ä½æ¯åå¨<literal>org.hibernate.transaction.TransactionManagerLookup</literal>çé
ç½®ï¼Hibernateä¼éç¨<literal>org.hibernate.context.JTASessionContext</literal>ãä¸è¬èè¨ï¼æ¤åæ°çå¼ææäºè¦ä½¿ç¨çå®ç°ç±»çå
¨åï¼ä½é£ä¸ç§å
ç½®çå®ç°å¯ä»¥ä½¿ç¨ç®åï¼å³"jta"ã"thread"å"managed"ã
+
+ </para>
+
+ </sect1>
+
+</chapter>
+
+
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/association_mapping.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/association_mapping.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/association_mapping.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,579 @@
+<chapter id="associations">
+
+ <title>å
³èå
³ç³»æ å°</title>
+
+ <sect1 id="assoc-intro" revision="1">
+ <title>ä»ç»</title>
+
+ <para>
+ å
³èå
³ç³»æ å°é常æ
åµæ¯æé¾é
ç½®æ£ç¡®çãå¨è¿ä¸ªé¨åä¸ï¼æ们ä»ååå
³ç³»æ å°å¼å§ï¼ç¶åèèååå
³ç³»æ å°ï¼ç±æµ
è³æ·±è®²è¿°ä¸éå
¸åçæ¡ä¾ãå¨ææçä¾åä¸ï¼æ们é½ä½¿ç¨ <literal>Person</literal>å<literal>Address</literal>ã
+ </para>
+
+ <para>
+ æä»¬æ ¹æ®æ å°å
³ç³»æ¯å¦æ¶åè¿æ¥è¡¨ä»¥åå¤æ ·æ§æ¥ååå
³èç±»åã
+ </para>
+
+ <para>
+ å¨ä¼ ç»çæ°æ®å»ºæ¨¡ä¸ï¼å
许为Nullå¼çå¤é®è¢«è®¤ä¸ºæ¯ä¸ç§ä¸å¥½çå®è·µï¼å æ¤æ们ææçä¾åä¸é½ä½¿ç¨ä¸å
许为Nullçå¤é®ãè¿å¹¶ä¸æ¯Hibernateçè¦æ±ï¼å³ä½¿ä½ å é¤æä¸å
许为Nullç约æï¼Hibernateæ å°ä¸æ ·å¯ä»¥å·¥ä½çå¾å¥½ã
+ </para>
+
+ </sect1>
+
+ <sect1 id="assoc-unidirectional" revision="1">
+ <title>ååå
³èï¼Unidirectional associationsï¼</title>
+
+ <sect2 id="assoc-unidirectional-m21">
+ <title>å¤å¯¹ä¸(many to one)</title>
+
+ <para>
+ <emphasis>ååmany-to-oneå
³è</emphasis>æ¯æ常è§çååå
³èå
³ç³»ã
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key, addressId bigint not null )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-121">
+ <title>ä¸å¯¹ä¸ï¼one to oneï¼</title>
+
+ <para>
+ <emphasis>åºäºå¤é®å
³èçååä¸å¯¹ä¸å
³è</emphasis>å<emphasis>ååå¤å¯¹ä¸å
³è</emphasis>å ä¹æ¯ä¸æ ·çãå¯ä¸çä¸åå°±æ¯ååä¸å¯¹ä¸å
³èä¸çå¤é®å段å
·æå¯ä¸æ§çº¦æã
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <many-to-one name="address"
+ column="addressId"
+ unique="true"
+ not-null="true"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key, addressId bigint not null unique )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ <para>
+ <emphasis>åºäºä¸»é®å
³èçååä¸å¯¹ä¸å
³è</emphasis>é常使ç¨ä¸ä¸ªç¹å®çidçæå¨ãï¼è¯·æ³¨æï¼å¨è¿ä¸ªä¾åä¸æ们ææ¢äºå
³èçæ¹åãï¼
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+</class>
+
+<class name="Address">
+ <id name="id" column="personId">
+ <generator class="foreign">
+ <param name="property">person</param>
+ </generator>
+ </id>
+ <one-to-one name="person" constrained="true"/>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table Address ( personId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-12m">
+ <title>ä¸å¯¹å¤ï¼one to manyï¼</title>
+
+ <para>
+ <emphasis>åºäºå¤é®å
³èçååä¸å¯¹å¤å
³è</emphasis>æ¯ä¸ç§å¾å°è§çæ
åµï¼å¹¶ä¸æ¨è使ç¨ã
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses">
+ <key column="personId"
+ not-null="true"/>
+ <one-to-many class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table Address ( addressId bigint not null primary key, personId bigint not null )
+ ]]></programlisting>
+
+ <para>
+ æ们认为对äºè¿ç§å
³èå
³ç³»æ好使ç¨è¿æ¥è¡¨ã
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="assoc-unidirectional-join" revision="1">
+ <title>使ç¨è¿æ¥è¡¨çååå
³èï¼Unidirectional associations with join tablesï¼</title>
+
+ <sect2 id="assoc-unidirectional-join-12m">
+ <title>ä¸å¯¹å¤(one to many)</title>
+
+ <para>
+ <emphasis>åºäºè¿æ¥è¡¨çååä¸å¯¹å¤å
³è</emphasis> åºè¯¥ä¼å
被éç¨ã请注æï¼éè¿æå®<literal>unique="true"</literal>ï¼æ们å¯ä»¥æå¤æ ·æ§ä»å¤å¯¹å¤æ¹å为ä¸å¯¹å¤ã
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses" table="PersonAddress">
+ <key column="personId"/>
+ <many-to-many column="addressId"
+ unique="true"
+ class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId not null, addressId bigint not null primary key )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-join-m21">
+ <title>å¤å¯¹ä¸ï¼many to oneï¼</title>
+
+ <para>
+ <emphasis>åºäºè¿æ¥è¡¨çååå¤å¯¹ä¸å
³è</emphasis>å¨å
³èå
³ç³»å¯éçæ
åµä¸åºç¨ä¹å¾æ®éã
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ optional="true">
+ <key column="personId" unique="true"/>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"/>
+ </join>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null primary key, addressId bigint not null )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-join-121">
+ <title>ä¸å¯¹ä¸ï¼one to oneï¼</title>
+
+ <para>
+ <emphasis>åºäºè¿æ¥è¡¨çååä¸å¯¹ä¸å
³è</emphasis>é常å°è§ï¼ä½ä¹æ¯å¯è¡çã
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ optional="true">
+ <key column="personId"
+ unique="true"/>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"
+ unique="true"/>
+ </join>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-unidirectional-join-m2m">
+ <title>å¤å¯¹å¤ï¼many to manyï¼</title>
+
+ <para>
+ æåï¼è¿æ <emphasis>ååå¤å¯¹å¤å
³è</emphasis>.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses" table="PersonAddress">
+ <key column="personId"/>
+ <many-to-many column="addressId"
+ class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personId, addressId) )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="assoc-bidirectional" revision="1">
+ <title>ååå
³èï¼Bidirectional associationsï¼</title>
+
+ <sect2 id="assoc-bidirectional-m21" revision="2">
+ <title>ä¸å¯¹å¤ï¼one to many) / å¤å¯¹ä¸ï¼many to oneï¼</title>
+
+ <para>
+ <emphasis>ååå¤å¯¹ä¸å
³è</emphasis> æ¯æ常è§çå
³èå
³ç³»ãï¼è¿ä¹æ¯æ åçç¶/åå
³èå
³ç³»ãï¼
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <set name="people" inverse="true">
+ <key column="addressId"/>
+ <one-to-many class="Person"/>
+ </set>
+</class>]]></programlisting>
+
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key, addressId bigint not null )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ <para>
+ å¦æä½ ä½¿ç¨<literal>List</literal>(æè
å
¶ä»æåºéåç±»)ï¼ä½ éè¦è®¾ç½®å¤é®å¯¹åºç<literal>key</literal>å为 <literal>not null</literal>,让Hibernateæ¥ä»éå端管çå
³èï¼ç»´æ¤æ¯ä¸ªå
ç´ çç´¢å¼ï¼éè¿è®¾ç½®<literal>update="false"</literal> and <literal>insert="false"</literal>æ¥å¯¹å¦ä¸ç«¯ååæä½ï¼ã
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id"/>
+ ...
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"
+ insert="false"
+ update="false"/>
+</class>
+
+<class name="Address">
+ <id name="id"/>
+ ...
+ <list name="people">
+ <key column="addressId" not-null="true"/>
+ <list-index column="peopleIdx"/>
+ <one-to-many class="Person"/>
+ </list>
+</class>]]></programlisting>
+
+ <para>
+ åè¥éåæ å°ç<literal><key></literal>å
ç´ å¯¹åºçåºå±å¤é®å段æ¯<literal>NOT NULL</literal>çï¼é£ä¹ä¸ºè¿ä¸keyå
ç´ å®ä¹<literal>not-null="true"</literal>æ¯å¾éè¦çãä¸è¦ä»
ä»
为å¯è½çåµå¥<literal><column></literal>å
ç´ å®ä¹<literal>not-null="true"</literal>ï¼<literal><key></literal>å
ç´ ä¹æ¯éè¦çã
+ </para>
+
+ </sect2>
+
+ <sect2 id="assoc-bidirectional-121">
+ <title>ä¸å¯¹ä¸ï¼one to oneï¼</title>
+
+ <para>
+ <emphasis>åºäºå¤é®å
³èçååä¸å¯¹ä¸å
³è</emphasis>ä¹å¾å¸¸è§ã
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <many-to-one name="address"
+ column="addressId"
+ unique="true"
+ not-null="true"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <one-to-one name="person"
+ property-ref="address"/>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key, addressId bigint not null unique )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ <para>
+ <emphasis>åºäºä¸»é®å
³èçä¸å¯¹ä¸å
³è</emphasis>éè¦ä½¿ç¨ç¹å®çidçæå¨ã
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <one-to-one name="address"/>
+</class>
+
+<class name="Address">
+ <id name="id" column="personId">
+ <generator class="foreign">
+ <param name="property">person</param>
+ </generator>
+ </id>
+ <one-to-one name="person"
+ constrained="true"/>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table Address ( personId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="assoc-bidirectional-join" revision="1">
+ <title>使ç¨è¿æ¥è¡¨çååå
³èï¼Bidirectional associations with join tablesï¼</title>
+
+ <sect2 id="assoc-bidirectional-join-12m">
+ <title>ä¸å¯¹å¤ï¼one to manyï¼ /å¤å¯¹ä¸ï¼ many to oneï¼</title>
+
+ <para>
+ <emphasis>åºäºè¿æ¥è¡¨çååä¸å¯¹å¤å
³è</emphasis>ã注æ<literal>inverse="true"</literal>å¯ä»¥åºç°å¨å
³èçä»»æä¸ç«¯ï¼å³collection端æè
join端ã
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses"
+ table="PersonAddress">
+ <key column="personId"/>
+ <many-to-many column="addressId"
+ unique="true"
+ class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ inverse="true"
+ optional="true">
+ <key column="addressId"/>
+ <many-to-one name="person"
+ column="personId"
+ not-null="true"/>
+ </join>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null, addressId bigint not null primary key )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-bidirectional-join-121">
+ <title>ä¸å¯¹ä¸ï¼one to oneï¼</title>
+
+ <para>
+ <emphasis>åºäºè¿æ¥è¡¨çååä¸å¯¹ä¸å
³è</emphasis>æ为ç½è§ï¼ä½ä¹æ¯å¯è¡çã
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ optional="true">
+ <key column="personId"
+ unique="true"/>
+ <many-to-one name="address"
+ column="addressId"
+ not-null="true"
+ unique="true"/>
+ </join>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <join table="PersonAddress"
+ optional="true"
+ inverse="true">
+ <key column="addressId"
+ unique="true"/>
+ <many-to-one name="person"
+ column="personId"
+ not-null="true"
+ unique="true"/>
+ </join>
+</class>]]></programlisting>
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="assoc-bidirectional-join-m2m" revision="1">
+ <title>å¤å¯¹å¤ï¼many to manyï¼</title>
+
+ <para>
+ æåï¼è¿æ <emphasis>ååå¤å¯¹å¤å
³è</emphasis>.
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id" column="personId">
+ <generator class="native"/>
+ </id>
+ <set name="addresses" table="PersonAddress">
+ <key column="personId"/>
+ <many-to-many column="addressId"
+ class="Address"/>
+ </set>
+</class>
+
+<class name="Address">
+ <id name="id" column="addressId">
+ <generator class="native"/>
+ </id>
+ <set name="people" inverse="true" table="PersonAddress">
+ <key column="addressId"/>
+ <many-to-many column="personId"
+ class="Person"/>
+ </set>
+</class>]]></programlisting>
+
+ <programlisting><![CDATA[
+create table Person ( personId bigint not null primary key )
+create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personId, addressId) )
+create table Address ( addressId bigint not null primary key )
+ ]]></programlisting>
+
+ </sect2>
+
+ </sect1>
+ <sect1 id="assoc-complex">
+ <title>æ´å¤æçå
³èæ å°</title>
+
+ <para>
+ æ´å¤æçå
³èè¿æ¥<emphasis>æ为</emphasis>ç½è§ã
+ éè¿å¨æ å°ææ¡£ä¸åµå
¥SQLçæï¼Hibernateä¹å¯ä»¥å¤çæ´ä¸ºå¤æçæ
åµãæ¯å¦ï¼åè¥å
å«åå²å¸æ·æ°æ®ç表å®ä¹äº<literal>accountNumber</literal>, <literal>effectiveEndDate</literal> å<literal>effectiveStartDate</literal>å段ï¼æç
§ä¸é¢æ å°ï¼
+ </para>
+
+ <programlisting><![CDATA[<properties name="currentAccountKey">
+ <property name="accountNumber" type="string" not-null="true"/>
+ <property name="currentAccount" type="boolean">
+ <formula>case when effectiveEndDate is null then 1 else 0 end</formula>
+ </property>
+</properties>
+<property name="effectiveEndDate" type="date"/>
+<property name="effectiveStateDate" type="date" not-null="true"/>]]></programlisting>
+
+ <para>
+ é£ä¹æ们å¯ä»¥å¯¹<emphasis>ç®å(current)</emphasis>å®ä¾(å
¶<literal>effectiveEndDate</literal>为null)使ç¨è¿æ ·çå
³èæ å°:
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="currentAccountInfo"
+ property-ref="currentAccountKey"
+ class="AccountInfo">
+ <column name="accountNumber"/>
+ <formula>'1'</formula>
+</many-to-one>]]></programlisting>
+
+ <para>
+ æ´å¤æçä¾å,åæ³<literal>Employee</literal>å<literal>Organization</literal>ä¹é´çå
³èæ¯éè¿ä¸ä¸ª<literal>Employment</literal>ä¸é´è¡¨ç»´æ¤ç,èä¸é´è¡¨ä¸å¡«å
äºå¾å¤åå²éåæ°æ®ãé£âéåç<emphasis>ææ°</emphasis>é主âè¿ä¸ªå
³èï¼ææ°é主就æ¯<literal>startDate</literal>æåçé£ä¸ªï¼å¯ä»¥è¿æ ·æ å°ï¼
+ </para>
+
+ <programlisting><![CDATA[<join>
+ <key column="employeeId"/>
+ <subselect>
+ select employeeId, orgId
+ from Employments
+ group by orgId
+ having startDate = max(startDate)
+ </subselect>
+ <many-to-one name="mostRecentEmployer"
+ class="Organization"
+ column="orgId"/>
+</join>]]></programlisting>
+
+ <para>
+ 使ç¨è¿ä¸åè½æ¶å¯ä»¥å
满åæï¼ä½é常æ´å å®ç¨çæ¯ç¨HQLææ¡ä»¶æ¥è¯¢æ¥å¤çè¿äºæ
å½¢ã
+ </para>
+
+ </sect1>
+
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/basic_mapping.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/basic_mapping.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/basic_mapping.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,2982 @@
+<chapter id="mapping">
+ <title>对象/å
³ç³»æ°æ®åºæ å°åºç¡(Basic O/R Mapping)</title>
+
+ <sect1 id="mapping-declaration" revision="1">
+ <title>æ å°å®ä¹ï¼Mapping declarationï¼</title>
+
+ <para>
+ 对象åå
³ç³»æ°æ®åºä¹é´çæ å°é常æ¯ç¨ä¸ä¸ªXMLææ¡£(XML document)æ¥å®ä¹çãè¿ä¸ªæ å°æ档被设计为æ读çï¼
+ 并ä¸å¯ä»¥æ工修æ¹ãæ å°è¯è¨æ¯ä»¥Java为ä¸å¿ï¼è¿æå³çæ å°ææ¡£æ¯æç
§æä¹
åç±»çå®ä¹æ¥å建çï¼
+ èé表çå®ä¹ã
+ </para>
+
+
+ <para>
+ 请注æï¼è½ç¶å¾å¤Hibernateç¨æ·éæ©æåXMLæ å°ææ¡£ï¼ä½ä¹æä¸äºå·¥å
·å¯ä»¥ç¨æ¥çææ å°ææ¡£ï¼
+ å
æ¬XDoclet,MiddlegenåAndroMDAã
+ </para>
+
+ <para>
+ 让æ们ä»ä¸ä¸ªæ å°çä¾åå¼å§ï¼
+ </para>
+
+ <programlisting id="mapping-declaration-ex1" revision="1"><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="eg">
+
+ <class name="Cat"
+ table="cats"
+ discriminator-value="C">
+
+ <id name="id">
+ <generator class="native"/>
+ </id>
+
+ <discriminator column="subclass"
+ type="character"/>
+
+ <property name="weight"/>
+
+ <property name="birthdate"
+ type="date"
+ not-null="true"
+ update="false"/>
+
+ <property name="color"
+ type="eg.types.ColorUserType"
+ not-null="true"
+ update="false"/>
+
+ <property name="sex"
+ not-null="true"
+ update="false"/>
+
+ <property name="litterId"
+ column="litterId"
+ update="false"/>
+
+ <many-to-one name="mother"
+ column="mother_id"
+ update="false"/>
+
+ <set name="kittens"
+ inverse="true"
+ order-by="litter_id">
+ <key column="mother_id"/>
+ <one-to-many class="Cat"/>
+ </set>
+
+ <subclass name="DomesticCat"
+ discriminator-value="D">
+
+ <property name="name"
+ type="string"/>
+
+ </subclass>
+
+ </class>
+
+ <class name="Dog">
+ <!-- mapping for Dog could go here -->
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ æ们ç°å¨å¼å§è®¨è®ºæ å°ææ¡£çå
容ãæ们åªæè¿°Hibernateå¨è¿è¡æ¶ç¨å°çææ¡£å
ç´ åå±æ§ã
+ æ å°ææ¡£è¿å
æ¬ä¸äºé¢å¤çå¯éå±æ§åå
ç´ ï¼å®ä»¬å¨ä½¿ç¨schema导åºå·¥å
·çæ¶åä¼å½±å导åºçæ°æ®åºschemaç»æã
+ ï¼æ¯å¦ï¼<literal> not-null</literal> å±æ§ãï¼
+ </para>
+
+
+
+ <sect2 id="mapping-declaration-doctype" revision="3">
+ <title>Doctype</title>
+
+ <para>
+ ææçXMLæ å°é½éè¦å®ä¹å¦ä¸æ示çdoctypeãDTDå¯ä»¥ä»ä¸è¿°URLä¸è·åï¼
+ ä¹å¯ä»¥ä»<literal>hibernate-x.x.x/src/net/sf/hibernate</literal>ç®å½ä¸ã
+ æ<literal>hibernate.jar</literal>æ件ä¸æ¾å°ãHibernateæ»æ¯ä¼é¦å
å¨å®çclassptahä¸æç´¢DTDæ件ã
+ å¦æä½ åç°å®æ¯éè¿è¿æ¥Internetæ¥æ¾DTDæ件ï¼å°±å¯¹ç
§ä½ çclasspathç®å½æ£æ¥XMLæ件éçDTD声æã
+ </para>
+
+ <sect3 id="mapping-declaration-entity-resolution">
+ <title>EntityResolver</title>
+ <para>
+ As mentioned previously, Hibernate will first attempt to resolve DTDs in its classpath. The
+ manner in which it does this is by registering a custom <literal>org.xml.sax.EntityResolver</literal>
+ implementation with the SAXReader it uses to read in the xml files. This custom
+ <literal>EntityResolver</literal> recognizes two different systemId namespaces.
+ å¦åæè¿°,Hibernateé¦å
å¨å
¶classpathä¸æ¥æ¾DTDãå
¶è¡ä¸ºæ¯ä¾é å¨ç³»ç»ä¸æ³¨åç<literal>org.xml.sax.EntityResolver</literal>çä¸ä¸ªå
·ä½å®ç°ï¼SAXReaderä¾é å®æ¥è¯»åxmlæ件ãè¿ä¸ <literal>EntityResolver</literal> å®ç°è½è¾¨è®¤ä¸¤ç§ä¸åç systenIdå½å空é´ã
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ è¥resolveréå°äºä¸ä¸ªä»¥<literal>http://hibernate.sourceforge.net/</literal>为å¼å¤´çsystemIdï¼å®ä¼è¾¨è®¤åºæ¯<literal>hibernate namespace</literal>ï¼resolverå°±è¯å¾éè¿å è½½Hibernateç±»çclassloaderæ¥æ¥æ¾è¿äºå®ä½ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+
+ è¥resolveréå°äºä¸ä¸ªä½¿ç¨<literal>classpath://</literal>URLåè®®çsystemIdï¼å®ä¼è¾¨è®¤åºè¿æ¯<literal>user namespace</literal>,resolverè¯å¾éè¿(1)å½å线ç¨ä¸ä¸æçclassloaderå(2)å è½½Hibernate classçclassloaderæ¥æ¥æ¾è¿äºå®ä½ã
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+ 使ç¨user namespace(ç¨æ·å½å空é´)çä¾åï¼
+ </para>
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" [
+ <!ENTITY types SYSTEM "classpath://your/domain/types.xml">
+]>
+
+<hibernate-mapping package="your.domain">
+ <class name="MyEntity">
+ <id name="id" type="my-custom-id-type">
+ ...
+ </id>
+ <class>
+ &types;
+</hibernate-mapping>]]></programlisting>
+ <para>
+ <literal>types.xml</literal>æ¯<literal>your.domain</literal>å
ä¸çä¸ä¸ªèµæºï¼å®å
å«äºä¸ä¸ªå®å¶ç<xref linkend="mapping-types-custom">typedef</xref>ã
+ </para>
+
+ </sect3>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-mapping" revision="3">
+ <title>hibernate-mapping</title>
+
+ <para>
+ è¿ä¸ªå
ç´ å
æ¬ä¸äºå¯éçå±æ§ã<literal>schema</literal>å<literal>catalog</literal>å±æ§ï¼
+ ææäºè¿ä¸ªæ å°æè¿æ¥ï¼referï¼ç表æå¨çschemaå/æcatalogå称ã
+ åè¥æå®äºè¿ä¸ªå±æ§ï¼è¡¨åä¼å ä¸ææå®çschemaåcatalogçååæ©å±ä¸ºå
¨éå®åãåè¥æ²¡ææå®ï¼è¡¨åå°±ä¸ä¼ä½¿ç¨å
¨éå®åã
+ <literal>default-cascade</literal>æå®äºæªæ确注æ<literal>cascade</literal>å±æ§çJavaå±æ§å
+ éåç±»Hibernateä¼éåä»ä¹æ ·çé»è®¤çº§èé£æ ¼ã<literal>auto-import</literal>å±æ§é»è®¤è®©æ们å¨æ¥è¯¢è¯è¨ä¸å¯ä»¥ä½¿ç¨
+ éå
¨éå®åçç±»åã
+ </para>
+
+
+ <programlistingco>
+ <areaspec>
+ <area id="hm1" coords="2 55"/>
+ <area id="hm2" coords="3 55"/>
+ <area id="hm3" coords="4 55"/>
+ <area id="hm4" coords="5 55"/>
+ <area id="hm5" coords="6 55"/>
+ <area id="hm6" coords="7 55"/>
+ <area id="hm7" coords="8 55"/>
+ </areaspec>
+ <programlisting><![CDATA[<hibernate-mapping
+ schema="schemaName"
+ catalog="catalogName"
+ default-cascade="cascade_style"
+ default-access="field|property|ClassName"
+ default-lazy="true|false"
+ auto-import="true|false"
+ package="package.name"
+ />]]></programlisting>
+ <calloutlist>
+ <callout arearefs="hm1">
+ <para>
+ <literal>schema</literal> (å¯é): æ°æ®åºschemaçå称ã
+ </para>
+ </callout>
+ <callout arearefs="hm2">
+ <para>
+ <literal>catalog</literal> (å¯é): æ°æ®åºcatalogçå称ã
+ </para>
+ </callout>
+ <callout arearefs="hm3">
+ <para>
+ <literal>default-cascade</literal> (å¯é - é»è®¤ä¸º <literal>none</literal>):
+ é»è®¤ç级èé£æ ¼ã
+ </para>
+ </callout>
+ <callout arearefs="hm4">
+ <para>
+ <literal>default-access</literal> (å¯é - é»è®¤ä¸º <literal>property</literal>):
+ Hibernateç¨æ¥è®¿é®ææå±æ§ççç¥ãå¯ä»¥éè¿å®ç°<literal>PropertyAccessor</literal>æ¥å£
+ èªå®ä¹ã
+ </para>
+ </callout>
+ <callout arearefs="hm5">
+ <para>
+ <literal>default-lazy</literal> (å¯é - é»è®¤ä¸º <literal>true</literal>):
+ æå®äºæªæ确注æ<literal>lazy</literal>å±æ§çJavaå±æ§åéåç±»ï¼
+ Hibernateä¼éåä»ä¹æ ·çé»è®¤å è½½é£æ ¼ã
+ </para>
+ </callout>
+ <callout arearefs="hm6">
+ <para>
+ <literal>auto-import</literal> (å¯é - é»è®¤ä¸º <literal>true</literal>):
+ æå®æ们æ¯å¦å¯ä»¥å¨æ¥è¯¢è¯è¨ä¸ä½¿ç¨éå
¨éå®çç±»åï¼ä»
éäºæ¬æ å°æ件ä¸çç±»ï¼ã
+ </para>
+ </callout>
+ <callout arearefs="hm7">
+ <para>
+ <literal>package</literal> (å¯é): æå®ä¸ä¸ªå
åç¼ï¼å¦æå¨æ å°ææ¡£ä¸æ²¡ææå®å
¨éå®çç±»åï¼
+ 就使ç¨è¿ä¸ªä½ä¸ºå
åã
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ åè¥ä½ æ两个æä¹
åç±»ï¼å®ä»¬çéå
¨éå®åæ¯ä¸æ ·çï¼å°±æ¯ä¸¤ä¸ªç±»çååä¸æ ·ï¼æå¨çå
ä¸ä¸æ ·--è¯è
注ï¼ï¼
+ ä½ åºè¯¥è®¾ç½®<literal>auto-import="false"</literal>ãå¦æä½ æä¸ä¸ªâimportè¿âçåååæ¶å¯¹åºä¸¤ä¸ªç±»ï¼
+ Hibernateä¼æåºä¸ä¸ªå¼å¸¸ã
+ </para>
+
+ <para>
+ 注æ<literal>hibernate-mapping</literal> å
ç´ å
è®¸ä½ åµå¥å¤ä¸ªå¦ä¸æ示ç
+ <literal><class></literal>æ å°ãä½æ¯æ好çåæ³ï¼ä¹è®¸ä¸äºå·¥å
·éè¦çï¼æ¯ä¸ä¸ª
+ æä¹
åç±»ï¼æä¸ä¸ªç±»ç继æ¿å±æ¬¡ï¼å¯¹åºä¸ä¸ªæ å°æ件ï¼å¹¶ä»¥æä¹
åçè¶
ç±»å称å½åï¼ä¾å¦ï¼
+ <literal>Cat.hbm.xml</literal>ï¼
+ <literal>Dog.hbm.xml</literal>ï¼æè
å¦æ使ç¨ç»§æ¿ï¼<literal>Animal.hbm.xml</literal>ã
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-class" revision="3">
+ <title>class</title>
+
+ <para>
+ ä½ å¯ä»¥ä½¿ç¨<literal>class</literal>å
ç´ æ¥å®ä¹ä¸ä¸ªæä¹
åç±»ï¼
+ </para>
+
+
+ <programlistingco>
+ <areaspec>
+ <area id="class1" coords="2 55"/>
+ <area id="class2" coords="3 55" />
+ <area id="class3" coords="4 55"/>
+ <area id="class4" coords="5 55" />
+ <area id="class5" coords="6 55"/>
+ <area id="class6" coords="7 55" />
+ <area id="class7" coords="8 55"/>
+ <area id="class8" coords="9 55" />
+ <area id="class9" coords="10 55" />
+ <area id="class10" coords="11 55"/>
+ <area id="class11" coords="12 55"/>
+ <area id="class12" coords="13 55"/>
+ <area id="class13" coords="14 55"/>
+ <area id="class14" coords="15 55"/>
+ <area id="class15" coords="16 55"/>
+ <area id="class16" coords="17 55"/>
+ <area id="class17" coords="18 55"/>
+ <area id="class18" coords="19 55"/>
+ <area id="class19" coords="20 55"/>
+ <area id="class20" coords="21 55"/>
+ <area id="class21" coords="22 55"/>
+ </areaspec>
+ <programlisting><![CDATA[<class
+ name="ClassName"
+ table="tableName"
+ discriminator-value="discriminator_value"
+ mutable="true|false"
+ schema="owner"
+ catalog="catalog"
+ proxy="ProxyInterface"
+ dynamic-update="true|false"
+ dynamic-insert="true|false"
+ select-before-update="true|false"
+ polymorphism="implicit|explicit"
+ where="arbitrary sql where condition"
+ persister="PersisterClass"
+ batch-size="N"
+ optimistic-lock="none|version|dirty|all"
+ lazy="true|false"
+ entity-name="EntityName"
+ check="arbitrary sql check condition"
+ rowid="rowid"
+ subselect="SQL expression"
+ abstract="true|false"
+ node="element-name"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="class1">
+ <para>
+ <literal>name</literal> (å¯é): æä¹
åç±»ï¼æè
æ¥å£ï¼çJavaå
¨éå®åã
+ å¦æè¿ä¸ªå±æ§ä¸åå¨ï¼Hibernateå°åå®è¿æ¯ä¸ä¸ªéPOJOçå®ä½æ å°ã
+ </para>
+ </callout>
+ <callout arearefs="class2">
+ <para>
+ <literal>table</literal> (å¯é - é»è®¤æ¯ç±»çéå
¨éå®å): 对åºçæ°æ®åºè¡¨åã
+ </para>
+ </callout>
+ <callout arearefs="class3">
+ <para>
+ <literal>discriminator-value</literal> (å¯é - é»è®¤åç±»åä¸æ ·):
+ ä¸ä¸ªç¨äºåºåä¸åçåç±»çå¼ï¼å¨å¤æè¡ä¸ºæ¶ä½¿ç¨ãå®å¯ä»¥æ¥åçå¼å
æ¬
+ <literal>null</literal> å <literal>not null</literal>ã
+ </para>
+ </callout>
+ <callout arearefs="class4">
+ <para>
+ <literal>mutable</literal> (å¯éï¼é»è®¤å¼ä¸º<literal>true</literal>):
+ 表æ该类çå®ä¾æ¯å¯åçæè
ä¸å¯åçã
+ </para>
+ </callout>
+ <callout arearefs="class5">
+ <para>
+ <literal>schema</literal> (å¯é):
+ è¦çå¨æ ¹<literal><hibernate-mapping></literal>å
ç´ ä¸æå®çschemaååã
+ </para>
+ </callout>
+ <callout arearefs="class6">
+ <para>
+ <literal>catalog</literal> (å¯é):
+ è¦çå¨æ ¹<literal><hibernate-mapping></literal>å
ç´ ä¸æå®çcatalogååã
+ </para>
+ </callout>
+ <callout arearefs="class7">
+ <para>
+ <literal>proxy</literal> (å¯é): æå®ä¸ä¸ªæ¥å£ï¼å¨å»¶è¿è£
è½½æ¶ä½ä¸ºä»£ç使ç¨ã
+ ä½ å¯ä»¥å¨è¿é使ç¨è¯¥ç±»èªå·±çååã
+ </para>
+ </callout>
+ <callout arearefs="class8">
+ <para>
+ <literal>dynamic-update</literal> (å¯é, é»è®¤ä¸º <literal>false</literal>):
+ æå®ç¨äº<literal>UPDATE</literal> çSQLå°ä¼å¨è¿è¡æ¶å¨æçæï¼å¹¶ä¸åªæ´æ°é£äºæ¹åè¿çå段ã
+ </para>
+ </callout>
+ <callout arearefs="class9">
+ <para>
+ <literal>dynamic-insert</literal> (å¯é, é»è®¤ä¸º <literal>false</literal>):
+ æå®ç¨äº<literal>INSERT</literal>ç SQL å°ä¼å¨è¿è¡æ¶å¨æçæï¼å¹¶ä¸åªå
å«é£äºé空å¼å段ã
+ </para>
+ </callout>
+ <callout arearefs="class10">
+ <para>
+ <literal>select-before-update</literal> (å¯é, é»è®¤ä¸º <literal>false</literal>):
+ æå®Hibernateé¤éç¡®å®å¯¹è±¡çæ£è¢«ä¿®æ¹äºï¼å¦æ该å¼ä¸ºtrueï¼è¯æ³¨ï¼ï¼å¦å<emphasis>ä¸ä¼</emphasis>æ§è¡SQL
+ <literal>UPDATE</literal>æä½ãå¨ç¹å®åºåï¼å®é
ä¸ï¼å®åªå¨ä¸ä¸ªç¬æ¶å¯¹è±¡ï¼transient objectï¼å
³èå°ä¸ä¸ª
+ æ°çsessionä¸æ¶æ§è¡çupdate()ä¸çæï¼ï¼è¿è¯´æHibernateä¼å¨<literal>UPDATE</literal>
+ ä¹åæ§è¡ä¸æ¬¡é¢å¤çSQL <literal>SELECT</literal>æä½ï¼æ¥å³å®æ¯å¦åºè¯¥æ§è¡
+ <literal>UPDATE</literal>ã
+ </para>
+ </callout>
+ <callout arearefs="class11">
+ <para>
+ <literal>polymorphismï¼å¤æï¼</literal> (å¯é, é»è®¤å¼ä¸º <literal>implicit (éå¼)
+ </literal>): çå®æ¯éå¼è¿æ¯æ¾å¼ç使ç¨å¤ææ¥è¯¢ï¼è¿åªå¨Hibernateçå
·ä½è¡¨ç»§æ¿çç¥ä¸ç¨å°ï¼è¯æ³¨ï¼ã
+ </para>
+ </callout>
+ <callout arearefs="class12">
+ <para>
+ <literal>where</literal> (å¯é) æå®ä¸ä¸ªéå çSQL<literal>WHERE</literal> æ¡ä»¶ï¼
+ å¨æåè¿ä¸ªç±»ç对象æ¶ä¼ä¸ç´å¢å è¿ä¸ªæ¡ä»¶ã
+ </para>
+ </callout>
+ <callout arearefs="class13">
+ <para>
+ <literal>persister</literal> (å¯é): æå®ä¸ä¸ªå®å¶ç<literal>ClassPersister</literal>ã
+ </para>
+ </callout>
+ <callout arearefs="class14">
+ <para>
+ <literal>batch-size</literal> (å¯é,é»è®¤æ¯<literal>1</literal>) æå®ä¸ä¸ªç¨äº
+ æ ¹æ®æ è¯ç¬¦ï¼identifierï¼æåå®ä¾æ¶ä½¿ç¨ç"batch size"ï¼æ¹æ¬¡æåæ°éï¼ã
+ </para>
+ </callout>
+ <callout arearefs="class15">
+ <para>
+ <literal>optimistic-lockï¼ä¹è§éå®ï¼</literal>
+ (å¯éï¼é»è®¤æ¯<literal>version</literal>): å³å®ä¹è§éå®ççç¥ã
+ </para>
+ </callout>
+ <callout arearefs="class16">
+ <para>
+ <literal>lazy</literal> (å¯é): éè¿è®¾ç½®<literal>lazy="false"</literal>ï¼
+ ææç延è¿å è½½ï¼Lazy fetchingï¼åè½å°è¢«å
¨é¨ç¦ç¨ï¼disabledï¼ã
+ </para>
+ </callout>
+ <callout arearefs="class17">
+ <para>
+ <literal>entity-name</literal> (å¯éï¼é»è®¤ä¸ºç±»å): Hibernate3å
许ä¸ä¸ªç±»è¿è¡å¤æ¬¡æ å°ï¼
+ åææ¯æ å°å°ä¸åç表ï¼ï¼å¹¶ä¸å
许使ç¨MapsæXML代æ¿Javaå±æ¬¡çå®ä½æ å°
+ ï¼ä¹å°±æ¯å®ç°å¨æé¢å模åï¼ä¸ç¨åæä¹
åç±»ï¼è¯æ³¨ï¼ã
+ æ´å¤ä¿¡æ¯è¯·ç<xref linkend="persistent-classes-dynamicmodels"/> and <xref linkend="xml"/>ã
+ </para>
+ </callout>
+ <callout arearefs="class18">
+ <para>
+ <literal>check</literal> (å¯é): è¿æ¯ä¸ä¸ªSQL表达å¼ï¼
+ ç¨äºä¸ºèªå¨çæçschemaæ·»å å¤è¡ï¼multi-rowï¼çº¦æ<emphasis>æ£æ¥</emphasis>ã
+ </para>
+ </callout>
+ <callout arearefs="class19">
+ <para>
+ <literal>rowid</literal> (å¯é): Hibernateå¯ä»¥ä½¿ç¨æ°æ®åºæ¯æçæè°çROWIDsï¼ä¾å¦ï¼
+ Oracleæ°æ®åºï¼å¦æä½ è®¾ç½®è¿ä¸ªå¯éç<literal>rowid</literal>ï¼
+ Hibernateå¯ä»¥ä½¿ç¨é¢å¤çå段<literal>rowid</literal>å®ç°å¿«éæ´æ°ãROWIDæ¯è¿ä¸ªåè½å®ç°çéç¹ï¼
+ å®ä»£è¡¨äºä¸ä¸ªåå¨å
ç»ï¼tupleï¼çç©çä½ç½®ã
+ </para>
+ </callout>
+ <callout arearefs="class20">
+ <para>
+ <literal>subselect</literal> (å¯é): å®å°ä¸ä¸ªä¸å¯åï¼immutableï¼å¹¶ä¸åªè¯»çå®ä½æ å°å°ä¸ä¸ªæ°æ®åºç
+ åæ¥è¯¢ä¸ãå½ä½ æ³ç¨è§å¾ä»£æ¿ä¸å¼ åºæ¬è¡¨çæ¶åï¼è¿æ¯æç¨çï¼ä½æ好ä¸è¦è¿æ ·åãæ´å¤çä»ç»è¯·çä¸é¢å
容ã
+ </para>
+ </callout>
+ <callout arearefs="class21">
+ <para>
+ <literal>abstract</literal> (å¯é): ç¨äºå¨<literal><union-subclass></literal>ç继æ¿ç»æ
+ ï¼hierarchiesï¼ä¸æ è¯æ½è±¡è¶
ç±»ã
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ è¥ææçæä¹
åç±»å®é
ä¸æ¯ä¸ä¸ªæ¥å£ï¼è¿ä¹æ¯å®å
¨å¯ä»¥æ¥åçã
+ ä¹åä½ å¯ä»¥ç¨å
ç´ <literal><subclass></literal>æ¥æå®è¯¥æ¥å£çå®é
å®ç°ç±»ã
+ ä½ å¯ä»¥æä¹
åä»»ä½<emphasis>static</emphasis>ï¼éæçï¼å
é¨ç±»ã
+ ä½ åºè¯¥ä½¿ç¨æ åçç±»åæ ¼å¼æ¥æå®ç±»åï¼<literal>æ¯å¦ï¼Foo$Bar</literal>ã
+ </para>
+ <para>
+ ä¸å¯åç±»ï¼<literal>mutable="false"</literal>ä¸å¯ä»¥è¢«åºç¨ç¨åºæ´æ°æè
å é¤ã
+ è¿å¯ä»¥è®©Hibernateåä¸äºå°å°çæ§è½ä¼åã
+ </para>
+
+ <para>
+ å¯éç<literal>proxy</literal>å±æ§å
许延è¿å 载类çæä¹
åå®ä¾ã
+ Hibernateå¼å§ä¼è¿åå®ç°äºè¿ä¸ªå½åæ¥å£çCGLIB代çãå½ä»£ççæ个æ¹æ³è¢«å®é
è°ç¨çæ¶åï¼
+ çå®çæä¹
å对象æä¼è¢«è£
è½½ãåè§ä¸é¢çâç¨äºå»¶è¿è£
è½½ç代çâã
+ </para>
+
+ <para><emphasis>Implicit</emphasis> (éå¼)çå¤ææ¯æï¼å¦ææ¥è¯¢æ¶ç»åºçæ¯ä»»ä½è¶
ç±»ã该类å®ç°çæ¥å£æè
该类ç
+ ååï¼é½ä¼è¿åè¿ä¸ªç±»çå®ä¾ï¼å¦ææ¥è¯¢ä¸ç»åºçæ¯åç±»çååï¼åä¼è¿ååç±»çå®ä¾ã
+ <emphasis>Explicit</emphasis> ï¼æ¾å¼ï¼çå¤ææ¯æï¼åªæå¨æ¥è¯¢æ¶ç»åºæç¡®ç该类ååæ¶æä¼è¿åè¿ä¸ªç±»çå®ä¾ï¼
+ åæ¶åªæå¨è¿ä¸ª<literal><class></literal>çå®ä¹ä¸ä½ä¸º<literal><subclass></literal>
+ æè
<literal><joined-subclass></literal>åºç°çåç±»ï¼æä¼å¯è½è¿åã
+ å¨å¤§å¤æ°æ
åµä¸ï¼é»è®¤ç<literal>polymorphism="implicit"</literal>é½æ¯åéçã
+ æ¾å¼çå¤æå¨æ两个ä¸åçç±»æ å°å°åä¸ä¸ªè¡¨çæ¶åå¾æç¨ãï¼å
许ä¸ä¸ªâè½»åâçç±»ï¼åªå
å«é¨å表å段ï¼ã
+ </para>
+
+ <para>
+ <literal>persister</literal>å±æ§å¯ä»¥è®©ä½ å®å¶è¿ä¸ªç±»ä½¿ç¨çæä¹
åçç¥ã
+ ä½ å¯ä»¥æå®ä½ èªå·±å®ç°
+ <literal>org.hibernate.persister.EntityPersister</literal>çåç±»ï¼ä½ çè³å¯ä»¥å®å
¨ä»å¤´å¼å§ç¼åä¸ä¸ª
+ <literal>org.hibernate.persister.ClassPersister</literal>æ¥å£çå®ç°ï¼
+ æ¯å¦æ¯ç¨å¨åè¿ç¨è°ç¨ãåºååå°æ件æè
LDAPæ°æ®åºæ¥å®ç°ã
+ åé
<literal>org.hibernate.test.CustomPersister</literal>ï¼è¿æ¯ä¸ä¸ªç®åçä¾å
+ ï¼âæä¹
åâå°ä¸ä¸ª<literal>Hashtable</literal>ï¼ã
+ </para>
+
+ <para>
+ 请注æ<literal>dynamic-update</literal>å<literal>dynamic-insert</literal>ç设置并ä¸ä¼ç»§æ¿å°åç±»ï¼
+ æ以å¨<literal><subclass></literal>æè
<literal><joined-subclass></literal>å
ç´ ä¸å¯è½
+ éè¦å次设置ãè¿äºè®¾ç½®æ¯å¦è½å¤æé«æçè¦è§æ
å½¢èå®ã请ç¨ä½ çæºæ
§å³å®æ¯å¦ä½¿ç¨ã
+ </para>
+
+ <para>
+ 使ç¨<literal>select-before-update</literal>é常ä¼éä½æ§è½ãå¦æä½ éæ°è¿æ¥ä¸ä¸ªè±ç®¡ï¼detacheï¼å¯¹è±¡å®ä¾
+ å°ä¸ä¸ª<literal>Session</literal>ä¸æ¶ï¼å®å¯ä»¥é²æ¢æ°æ®åºä¸å¿
è¦ç触åupdateã
+ è¿å°±å¾æç¨äºã
+ </para>
+
+ <para>
+ å¦æä½ æå¼äº<literal>dynamic-update</literal>ï¼ä½ å¯ä»¥éæ©å ç§ä¹è§éå®ççç¥ï¼
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>versionï¼çæ¬æ£æ¥ï¼</literal> æ£æ¥version/timestampå段
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>allï¼å
¨é¨ï¼</literal> æ£æ¥å
¨é¨å段
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>dirtyï¼èæ£æ¥ï¼</literal>åªæ£å¯ä¿®æ¹è¿çå段
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>noneï¼ä¸æ£æ¥ï¼</literal>ä¸ä½¿ç¨ä¹è§éå®
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+ æ们<emphasis>é常</emphasis>强çå»ºè®®ä½ å¨Hibernateä¸ä½¿ç¨version/timestampå段æ¥è¿è¡ä¹è§éå®ã
+ 对æ§è½æ¥è¯´ï¼è¿æ¯æ好çéæ©ï¼å¹¶ä¸è¿ä¹æ¯å¯ä¸è½å¤å¤çå¨sessionå¤è¿è¡æä½ççç¥ï¼ä¾å¦ï¼
+ å¨ä½¿ç¨<literal>Session.merge()</literal>çæ¶åï¼ã
+ </para>
+
+ <para>
+ 对Hibernateæ å°æ¥è¯´è§å¾å表æ¯æ²¡æåºå«çï¼è¿æ¯å 为å®ä»¬å¨æ°æ®å±é½æ¯éæçï¼
+ 注æï¼ä¸äºæ°æ®åºä¸æ¯æè§å¾å±æ§ï¼ç¹å«æ¯æ´æ°çæ¶åï¼ãææ¶ä½ æ³ä½¿ç¨è§å¾ï¼ä½å´ä¸è½å¨æ°æ®åº
+ ä¸å建å®ï¼ä¾å¦ï¼å¨éççschemaä¸ï¼ãè¿æ ·çè¯ï¼ä½ å¯ä»¥æ å°ä¸ä¸ªä¸å¯åçï¼immutableï¼å¹¶ä¸æ¯
+ åªè¯»çå®ä½å°ä¸ä¸ªç»å®çSQLåæ¥è¯¢è¡¨è¾¾å¼ï¼
+ </para>
+
+ <programlisting><![CDATA[<class name="Summary">
+ <subselect>
+ select item.name, max(bid.amount), count(*)
+ from item
+ join bid on bid.item_id = item.id
+ group by item.name
+ </subselect>
+ <synchronize table="item"/>
+ <synchronize table="bid"/>
+ <id name="name"/>
+ ...
+</class>]]></programlisting>
+
+ <para>
+ å®ä¹è¿ä¸ªå®ä½ç¨å°ç表为åæ¥ï¼synchronizeï¼ï¼ç¡®ä¿èªå¨å·æ°ï¼auto-flushï¼æ£ç¡®æ§è¡ï¼
+ 并ä¸ä¾èµåå®ä½çæ¥è¯¢ä¸ä¼è¿åè¿ææ°æ®ã<literal><subselect></literal>å¨å±æ§å
ç´
+ åä¸ä¸ªåµå¥æ å°å
ç´ ä¸é½å¯è§ã
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-id" revision="4">
+ <title>id</title>
+
+ <para>
+ 被æ å°çç±»<emphasis>å¿
é¡»</emphasis>å®ä¹å¯¹åºæ°æ®åºè¡¨ä¸»é®å段ã大å¤æ°ç±»æä¸ä¸ªJavaBeansé£æ ¼çå±æ§ï¼
+ 为æ¯ä¸ä¸ªå®ä¾å
å«å¯ä¸çæ è¯ã<literal><id></literal> å
ç´ å®ä¹äºè¯¥å±æ§å°æ°æ®åºè¡¨ä¸»é®å段çæ å°ã
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="id1" coords="2 70"/>
+ <area id="id2" coords="3 70" />
+ <area id="id3" coords="4 70"/>
+ <area id="id4" coords="5 70" />
+ <area id="id5" coords="6 70" />
+ </areaspec>
+ <programlisting><![CDATA[<id
+ name="propertyName"
+ type="typename"
+ column="column_name"
+ unsaved-value="null|any|none|undefined|id_value"
+ access="field|property|ClassName"
+ node="element-name|@attribute-name|element/@attribute|.">
+
+ <generator class="generatorClass"/>
+</id>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="id1">
+ <para>
+ <literal>name</literal> (å¯é): æ è¯å±æ§çååã
+ </para>
+ </callout>
+ <callout arearefs="id2">
+ <para>
+ <literal>type</literal> (å¯é): æ è¯Hibernateç±»åçååã
+ </para>
+ </callout>
+ <callout arearefs="id3">
+ <para>
+ <literal>column</literal> (å¯é - é»è®¤ä¸ºå±æ§å): 主é®å段çååã
+ </para>
+ </callout>
+ <callout arearefs="id4">
+ <para>
+ <literal>unsaved-value</literal> (å¯é - é»è®¤ä¸ºä¸ä¸ªååå®é
ï¼sensibleï¼çå¼):
+ ä¸ä¸ªç¹å®çæ è¯å±æ§å¼ï¼ç¨æ¥æ å¿è¯¥å®ä¾æ¯ååå建çï¼å°æªä¿åã
+ è¿å¯ä»¥æè¿ç§å®ä¾åä»ä»¥åçsessionä¸è£
è½½è¿ï¼å¯è½ååè¿ä¿®æ¹--è¯è
注ï¼
+ ä½æªå次æä¹
åçå®ä¾åºåå¼æ¥ã
+ </para>
+ </callout>
+ <callout arearefs="id5">
+ <para>
+ <literal>access</literal> (å¯é - é»è®¤ä¸º<literal>property</literal>):
+ Hibernateç¨æ¥è®¿é®å±æ§å¼ççç¥ã
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ å¦æ <literal>name</literal>å±æ§ä¸åå¨ï¼ä¼è®¤ä¸ºè¿ä¸ªç±»æ²¡ææ è¯å±æ§ã
+ </para>
+
+ <para>
+ <literal>unsaved-value</literal> å±æ§å¨Hibernate3ä¸å ä¹ä¸åéè¦ã
+ </para>
+
+ <para>
+ è¿æä¸ä¸ªå¦å¤ç<literal><composite-id></literal>å®ä¹å¯ä»¥è®¿é®æ§å¼çå¤ä¸»é®æ°æ®ã
+ æ们强çä¸å»ºè®®ä½¿ç¨è¿ç§æ¹å¼ã
+ </para>
+
+ <sect3 id="mapping-declaration-id-generator" revision="2">
+ <title>Generator</title>
+
+ <para>
+ å¯éç<literal><generator></literal>åå
ç´ æ¯ä¸ä¸ªJavaç±»çååï¼
+ ç¨æ¥ä¸ºè¯¥æä¹
åç±»çå®ä¾çæå¯ä¸çæ è¯ãå¦æè¿ä¸ªçæå¨å®ä¾éè¦æäºé
ç½®å¼æè
åå§ååæ°ï¼
+ ç¨<literal><param></literal>å
ç´ æ¥ä¼ éã
+ </para>
+
+ <programlisting><![CDATA[<id name="id" type="long" column="cat_id">
+ <generator class="org.hibernate.id.TableHiLoGenerator">
+ <param name="table">uid_table</param>
+ <param name="column">next_hi_value_column</param>
+ </generator>
+</id>]]></programlisting>
+
+ <para>
+
+ ææççæå¨é½å®ç°<literal>org.hibernate.id.IdentifierGenerator</literal>æ¥å£ã
+ è¿æ¯ä¸ä¸ªé常ç®åçæ¥å£ï¼æäºåºç¨ç¨åºå¯ä»¥éæ©æä¾ä»ä»¬èªå·±ç¹å®çå®ç°ãå½ç¶ï¼
+ Hibernateæä¾äºå¾å¤å
ç½®çå®ç°ãä¸é¢æ¯ä¸äºå
ç½®çæå¨çå¿«æ·ååï¼
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>increment</literal></term>
+ <listitem>
+ <para>
+ ç¨äºä¸º<literal>long</literal>, <literal>short</literal>æè
<literal>int</literal>ç±»åçæ
+ å¯ä¸æ è¯ãåªæå¨æ²¡æå
¶ä»è¿ç¨å¾åä¸å¼ 表ä¸æå
¥æ°æ®æ¶æè½ä½¿ç¨ã
+ <emphasis>å¨é群ä¸ä¸è¦ä½¿ç¨ã</emphasis>
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>identity</literal></term>
+ <listitem>
+ <para>
+ 对DB2,MySQL, MS SQL Server, SybaseåHypersonicSQLçå
ç½®æ è¯å段æä¾æ¯æã
+ è¿åçæ è¯ç¬¦æ¯<literal>long</literal>, <literal>short</literal> æè
<literal>int</literal>ç±»åçã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>sequence</literal></term>
+ <listitem>
+ <para>
+ å¨DB2,PostgreSQL, Oracle, SAP DB, McKoiä¸ä½¿ç¨åºåï¼sequence)ï¼
+ èå¨Interbaseä¸ä½¿ç¨çæå¨(generator)ãè¿åçæ è¯ç¬¦æ¯<literal>long</literal>,
+ <literal>short</literal>æè
<literal>int</literal>ç±»åçã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>hilo</literal></term>
+ <listitem>
+ <para id="mapping-declaration-id-hilodescription" revision="1">
+ 使ç¨ä¸ä¸ªé«/ä½ä½ç®æ³é«æççæ<literal>long</literal>, <literal>short</literal>
+ æè
<literal>int</literal>ç±»åçæ è¯ç¬¦ãç»å®ä¸ä¸ªè¡¨åå段ï¼é»è®¤åå«æ¯
+ <literal>hibernate_unique_key</literal> å<literal>next_hi</literal>ï¼ä½ä¸ºé«ä½å¼çæ¥æºã
+ é«/ä½ä½ç®æ³çæçæ è¯ç¬¦åªå¨ä¸ä¸ªç¹å®çæ°æ®åºä¸æ¯å¯ä¸çã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>seqhilo</literal></term>
+ <listitem>
+ <para>
+ 使ç¨ä¸ä¸ªé«/ä½ä½ç®æ³æ¥é«æççæ<literal>long</literal>, <literal>short</literal>
+ æè
<literal>int</literal>ç±»åçæ è¯ç¬¦ï¼ç»å®ä¸ä¸ªæ°æ®åºåºåï¼sequence)çååã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>uuid</literal></term>
+ <listitem>
+ <para>
+ ç¨ä¸ä¸ª128-bitçUUIDç®æ³çæå符串类åçæ è¯ç¬¦ï¼
+ è¿å¨ä¸ä¸ªç½ç»ä¸æ¯å¯ä¸çï¼ä½¿ç¨äºIPå°åï¼ãUUID被ç¼ç 为ä¸ä¸ª32ä½16è¿å¶æ°åçå符串ã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>guid</literal></term>
+ <listitem>
+ <para>
+ å¨MS SQL Server å MySQL ä¸ä½¿ç¨æ°æ®åºçæçGUIDå符串ã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>native</literal></term>
+ <listitem>
+ <para>
+ æ ¹æ®åºå±æ°æ®åºçè½åéæ©<literal>identity</literal>, <literal>sequence</literal>
+ æè
<literal>hilo</literal>ä¸çä¸ä¸ªã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>assigned</literal></term>
+ <listitem>
+ <para>
+ 让åºç¨ç¨åºå¨<literal>save()</literal>ä¹å为对象åé
ä¸ä¸ªæ 示符ãè¿æ¯
+ <literal><generator></literal>å
ç´ æ²¡ææå®æ¶çé»è®¤çæçç¥ã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>select</literal></term>
+ <listitem>
+ <para>
+ éè¿æ°æ®åºè§¦åå¨éæ©ä¸äºå¯ä¸ä¸»é®çè¡å¹¶è¿å主é®å¼æ¥åé
ä¸ä¸ªä¸»é®ã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>foreign</literal></term>
+ <listitem>
+ <para>
+ 使ç¨å¦å¤ä¸ä¸ªç¸å
³èç对象çæ è¯ç¬¦ãé常å<literal><one-to-one></literal>èåèµ·æ¥ä½¿ç¨ã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>sequence-identity</literal></term>
+ <listitem>
+ <para>
+ ä¸ç§ç¹å«çåºåçæçç¥,使ç¨æ°æ®åºåºåæ¥çæå®é
å¼,ä½å°å®åJDBC3çgetGeneratedKeysç»åå¨ä¸èµ·,使å¾å¨æå
¥è¯å¥æ§è¡çæ¶åå°±è¿åçæçå¼ãç®å为æ¢åªæé¢åJDK 1.4çOracle 10g驱å¨æ¯æè¿ä¸çç¥ã注æï¼å 为Oracle驱å¨ç¨åºçä¸ä¸ªbugï¼è¿äºæå
¥è¯å¥ç注é被å
³éäºãï¼åæï¼Note comments on these insert statements are disabled due to a bug in the Oracle drivers.ï¼
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ </para>
+ </sect3>
+
+ <sect3 id="mapping-declaration-id-hilo" revision="1">
+ <title>é«/ä½ä½ç®æ³ï¼Hi/Lo Algorithmï¼</title>
+ <para>
+ <literal>hilo</literal> å <literal>seqhilo</literal>çæå¨ç»åºäºä¸¤ç§hi/loç®æ³çå®ç°ï¼
+ è¿æ¯ä¸ç§å¾ä»¤äººæ»¡æçæ è¯ç¬¦çæç®æ³ã第ä¸ç§å®ç°éè¦ä¸ä¸ªâç¹æ®âçæ°æ®åºè¡¨æ¥ä¿åä¸ä¸ä¸ªå¯ç¨çâhiâå¼ã
+ 第äºç§å®ç°ä½¿ç¨ä¸ä¸ªOracleé£æ ¼çåºåï¼å¨è¢«æ¯æçæ
åµä¸ï¼ã
+ </para>
+
+ <programlisting><![CDATA[<id name="id" type="long" column="cat_id">
+ <generator class="hilo">
+ <param name="table">hi_value</param>
+ <param name="column">next_value</param>
+ <param name="max_lo">100</param>
+ </generator>
+</id>]]></programlisting>
+
+ <programlisting><![CDATA[<id name="id" type="long" column="cat_id">
+ <generator class="seqhilo">
+ <param name="sequence">hi_value</param>
+ <param name="max_lo">100</param>
+ </generator>
+</id>]]></programlisting>
+
+ <para>
+ å¾ä¸å¹¸ï¼ä½ å¨ä¸ºHibernateèªè¡æä¾<literal>Connection</literal>æ¶æ æ³ä½¿ç¨<literal>hilo</literal>ã
+ å½Hibernate使ç¨JTAè·ååºç¨æå¡å¨çæ°æ®æºè¿æ¥æ¶,ä½ å¿
é¡»æ£ç¡®å°é
ç½®
+ <literal>hibernate.transaction.manager_lookup_class</literal>ã
+ </para>
+ </sect3>
+
+ <sect3 id="mapping-declaration-id-uuid">
+ <title>UUIDç®æ³ï¼UUID Algorithm ï¼</title>
+ <para>
+ UUIDå
å«ï¼IPå°åï¼JVMçå¯å¨æ¶é´ï¼ç²¾ç¡®å°1/4ç§ï¼ï¼ç³»ç»æ¶é´åä¸ä¸ªè®¡æ°å¨å¼ï¼å¨JVMä¸å¯ä¸ï¼ã
+ å¨Java代ç ä¸ä¸å¯è½è·å¾MACå°åæè
å
åå°åï¼æ以è¿å·²ç»æ¯æ们å¨ä¸ä½¿ç¨JNIçåæä¸çè½åçæ好å®ç°äºã
+ </para>
+ </sect3>
+
+ <sect3 id="mapping-declaration-id-sequences">
+ <title>æ è¯å段ååºåï¼Identity columns and Sequencesï¼</title>
+ <para>
+ 对äºå
é¨æ¯ææ è¯å段çæ°æ®åº(DB2,MySQL,Sybase,MS SQL)ï¼ä½ å¯ä»¥ä½¿ç¨<literal>identity</literal>å
³é®åçæã
+ 对äºå
é¨æ¯æåºåçæ°æ®åºï¼DB2,Oracle, PostgreSQL, Interbase, McKoi,SAP DB),
+ ä½ å¯ä»¥ä½¿ç¨<literal>sequence</literal>é£æ ¼çå
³é®åçæã
+ è¿ä¸¤ç§æ¹å¼å¯¹äºæå
¥ä¸ä¸ªæ°ç对象é½éè¦ä¸¤æ¬¡SQLæ¥è¯¢ã
+ </para>
+
+ <programlisting><![CDATA[<id name="id" type="long" column="person_id">
+ <generator class="sequence">
+ <param name="sequence">person_id_sequence</param>
+ </generator>
+</id>]]></programlisting>
+
+ <programlisting><![CDATA[<id name="id" type="long" column="person_id" unsaved-value="0">
+ <generator class="identity"/>
+</id>]]></programlisting>
+ <para>
+ 对äºè·¨å¹³å°å¼åï¼<literal>native</literal>çç¥ä¼ä»<literal>identity</literal>,
+ <literal>sequence</literal> å<literal>hilo</literal>ä¸è¿è¡éæ©ï¼éæ©åªä¸ä¸ªï¼è¿åå³äºåºå±æ°æ®åºçæ¯æè½åã
+ </para>
+ </sect3>
+
+ <sect3 id="mapping-declaration-id-assigned">
+ <title>ç¨åºåé
çæ è¯ç¬¦ï¼Assigned Identifiersï¼</title>
+ <para>
+ å¦æä½ éè¦åºç¨ç¨åºåé
ä¸ä¸ªæ 示符ï¼èéHibernateæ¥çæï¼ï¼ä½ å¯ä»¥ä½¿ç¨<literal>assigned</literal>
+ çæå¨ãè¿ç§ç¹æ®ççæå¨ä¼ä½¿ç¨å·²ç»åé
ç»å¯¹è±¡çæ è¯ç¬¦å±æ§çæ è¯ç¬¦å¼ã
+ è¿ä¸ªçæå¨ä½¿ç¨ä¸ä¸ªèªç¶é®ï¼natural keyï¼æåä¸æä¹çåï¼è¯æ³¨ï¼ä½ä¸ºä¸»é®ï¼èä¸æ¯ä½¿ç¨ä¸ä¸ªä»£çé®ï¼
+ surrogate keyï¼æ²¡æåä¸æä¹çåï¼è¯æ³¨ï¼ãè¿æ¯æ²¡ææå®<literal><generator></literal>å
ç´ æ¶çé»è®¤è¡ä¸º
+ </para>
+
+ <para>
+ å½éæ©<literal>assigned</literal>çæå¨æ¶ï¼é¤éæä¸ä¸ªversionætimestampå±æ§ï¼æè
ä½ å®ä¹äº
+ <literal>Interceptor.isUnsaved()</literal>ï¼å¦åéè¦è®©Hiberante使ç¨
+ <literal>unsaved-value="undefined"</literal>ï¼å¼ºå¶Hibernatetæ¥è¯¢æ°æ®åºæ¥ç¡®å®ä¸ä¸ªå®ä¾æ¯ç¬æ¶çï¼transientï¼
+ è¿æ¯è±ç®¡çï¼detachedï¼ã
+ </para>
+ </sect3>
+
+ <sect3 id="mapping-declaration-id-select">
+ <title>触åå¨å®ç°ç主é®çæå¨ï¼Primary keys assigned by triggersï¼</title>
+ <para>
+ ä»
ä»
ç¨äºéççschemaä¸ (Hibernateä¸è½ä½¿ç¨è§¦åå¨çæDDL)ã
+ </para>
+
+ <programlisting><![CDATA[<id name="id" type="long" column="person_id">
+ <generator class="select">
+ <param name="key">socialSecurityNumber</param>
+ </generator>
+</id>]]></programlisting>
+
+ <para>
+ å¨ä¸é¢çä¾åä¸ï¼ç±»å®ä¹äºä¸ä¸ªå½å为<literal>socialSecurityNumber</literal>çå¯ä¸å¼å±æ§ï¼
+ å®æ¯ä¸ä¸ªèªç¶é®ï¼natural keyï¼ï¼å½å为<literal>person_id</literal>ç代çé®ï¼surrogate keyï¼
+ çå¼ç±è§¦åå¨çæã
+ </para>
+
+ </sect3>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-compositeid" revision="3">
+ <title>composite-id</title>
+
+ <programlisting><![CDATA[<composite-id
+ name="propertyName"
+ class="ClassName"
+ mapped="true|false"
+ access="field|property|ClassName"
+ node="element-name|."
+ >
+
+ <key-property name="propertyName" type="typename" column="column_name"/>
+ <key-many-to-one name="propertyName class="ClassName" column="column_name"/>
+ ......
+</composite-id>]]></programlisting>
+
+ <para>
+ å¦æ表使ç¨èå主é®ï¼ä½ å¯ä»¥æ å°ç±»çå¤ä¸ªå±æ§ä¸ºæ è¯ç¬¦å±æ§ã
+ <literal><composite-id></literal>å
ç´ æ¥å<literal><key-property></literal>
+ å±æ§æ å°å<literal><key-many-to-one></literal>å±æ§æ å°ä½ä¸ºåå
ç´ ã
+ </para>
+
+ <programlisting><![CDATA[<composite-id>
+ <key-property name="medicareNumber"/>
+ <key-property name="dependent"/>
+</composite-id>]]></programlisting>
+
+ <para>
+ ä½ çæä¹
åç±»<emphasis>å¿
é¡»</emphasis>éè½½<literal>equals()</literal>å
+ <literal>hashCode()</literal>æ¹æ³ï¼æ¥å®ç°ç»åçæ è¯ç¬¦çç¸çå¤æã
+ å®ç°<literal>Serializable</literal>æ¥å£ä¹æ¯å¿
é¡»çã
+ </para>
+
+ <para>
+ ä¸å¹¸çæ¯ï¼è¿ç§ç»åå
³é®åçæ¹æ³æå³çä¸ä¸ªæä¹
åç±»æ¯å®èªå·±çæ è¯ãé¤äºå¯¹è±¡èªå·±ä¹å¤ï¼
+ 没æä»ä¹æ¹ä¾¿çâææâå¯ç¨ãä½ å¿
é¡»åå§åæä¹
åç±»çå®ä¾ï¼å¡«å
å®çæ è¯ç¬¦å±æ§ï¼å<literal>load()</literal>
+ ç»åå
³é®åå
³èçæä¹
ç¶æãæ们æè¿ç§æ¹æ³ç§°ä¸º<emphasis>embeddedï¼åµå
¥å¼ï¼</emphasis>çç»åæ è¯ç¬¦ï¼å¨éè¦çåºç¨ä¸ä¸é¼å±ä½¿ç¨è¿ç§ç¨æ³ã
+ </para>
+
+ <para>
+ 第äºç§æ¹æ³æ们称为<emphasis>mapped(æ å°å¼)</emphasis>ç»åæ è¯ç¬¦ (mapped composite identifier),<literal><composite-id></literal>å
ç´ ä¸ååºçæ è¯å±æ§ä¸ä½å¨æä¹
åç±»åºç°ï¼è¿å½¢æä¸ä¸ªç¬ç«çæ è¯ç¬¦ç±»ã
+ </para>
+
+ <programlisting><![CDATA[<composite-id class="MedicareId" mapped="true">
+ <key-property name="medicareNumber"/>
+ <key-property name="dependent"/>
+</composite-id>]]></programlisting>
+
+ <para>
+ å¨è¿ä¸ªä¾åä¸ï¼ç»åæ è¯ç¬¦ç±»<literal>MedicareId</literal>åå®ä½ç±»é½å«æ<literal>medicareNumber</literal>å<literal>dependent</literal>å±æ§ãæ è¯ç¬¦ç±»å¿
é¡»éè½½<literal>equals()</literal>å<literal>hashCode()</literal>并ä¸å®ç°<literal>Serializable</literal>æ¥å£ãè¿ç§æ¹æ³ç缺ç¹æ¯åºç°äºææ¾ç代ç éå¤ã
+ </para>
+
+ <para>
+ ä¸é¢ååºçå±æ§æ¯ç¨æ¥æå®ä¸ä¸ªæ å°å¼ç»åæ è¯ç¬¦çï¼
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>mapped</literal> (å¯é, é»è®¤ä¸º<literal>false</literal>):
+ ææ使ç¨ä¸ä¸ªæ å°å¼ç»åæ è¯ç¬¦ï¼å
¶å
å«çå±æ§æ å°åæ¶å¨å®ä½ç±»åç»åæ è¯ç¬¦ç±»ä¸åºç°ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>class</literal> (å¯é,ä½å¯¹æ å°å¼ç»åæ è¯ç¬¦å¿
é¡»æå®):
+ ä½ä¸ºç»åæ è¯ç¬¦ç±»ä½¿ç¨çç±»å.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ å¨<xref linkend="components-compositeid"/>ä¸èä¸,æ们ä¼æ述第ä¸ç§æ¹å¼,é£å°±æ¯æç»åæ è¯ç¬¦å®ç°ä¸ºä¸ä¸ªç»ä»¶(component)ç±»,è¿æ¯æ´æ¹ä¾¿çæ¹æ³ãä¸é¢çå±æ§ä»
对第ä¸ç§æ¹æ³ææï¼
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>name</literal> (å¯é,ä½å¯¹è¿ç§æ¹æ³èè¨å¿
é¡»): å
å«æ¤ç»ä»¶æ è¯ç¬¦çç»ä»¶ç±»åçåå (åé
第9ç« ).
+
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>access</literal> (å¯é - é»è®¤ä¸º<literal>property</literal>):
+ Hibernateåºè¯¥ä½¿ç¨ç访é®æ¤å±æ§å¼ççç¥
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>class</literal> (å¯é - é»è®¤ä¼ç¨åå°æ¥èªå¨å¤å®å±æ§ç±»å
+ ): ç¨æ¥ä½ä¸ºç»åæ è¯ç¬¦çç»ä»¶ç±»çç±»åï¼åé
ä¸ä¸èï¼
+
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ 第ä¸ç§æ¹å¼ï¼è¢«ç§°ä¸º<emphasis>identifier component(æ è¯ç¬¦ç»ä»¶)</emphasis>æ¯æ们对å ä¹ææåºç¨é½æ¨è使ç¨çæ¹å¼ã
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-discriminator" revision="3">
+ <title>é´å«å¨ï¼discriminatorï¼</title>
+
+ <para>
+ å¨"ä¸æ£µå¯¹è±¡ç»§æ¿æ 对åºä¸ä¸ªè¡¨"ççç¥ä¸,<literal><discriminator></literal>å
ç´ æ¯å¿
éç,
+ å®å®ä¹äºè¡¨çé´å«å¨å段ãé´å«å¨å段å
å«æ å¿å¼ï¼ç¨äºåç¥æä¹
åå±åºè¯¥ä¸ºæ个ç¹å®çè¡å建åªä¸ä¸ªåç±»çå®ä¾ã
+ å¦ä¸è¿äºåå°éå¶çç±»åå¯ä»¥ä½¿ç¨ï¼
+ <literal>string</literal>, <literal>character</literal>, <literal>integer</literal>,
+ <literal>byte</literal>, <literal>short</literal>, <literal>boolean</literal>,
+ <literal>yes_no</literal>, <literal>true_false</literal>.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="discriminator1" coords="2 60"/>
+ <area id="discriminator2" coords="3 60" />
+ <area id="discriminator3" coords="4 60" />
+ <area id="discriminator4" coords="5 60" />
+ <area id="discriminator5" coords="6 60" />
+ </areaspec>
+ <programlisting><![CDATA[<discriminator
+ column="discriminator_column"
+ type="discriminator_type"
+ force="true|false"
+ insert="true|false"
+ formula="arbitrary sql expression"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="discriminator1">
+ <para>
+ <literal>column</literal> (å¯é - é»è®¤ä¸º <literal>class</literal>) é´å«å¨å段çåå
+ </para>
+ </callout>
+ <callout arearefs="discriminator2">
+ <para>
+ <literal>type</literal> (å¯é - é»è®¤ä¸º <literal>string</literal>) ä¸ä¸ªHibernateå段类åçåå
+ </para>
+ </callout>
+ <callout arearefs="discriminator3">
+ <para>
+ <literal>force(强å¶)</literal> (å¯é - é»è®¤ä¸º <literal>false</literal>)
+ "强å¶"Hibernateæå®å
许çé´å«å¨å¼,å³ä½¿å½åå¾çææå®ä¾é½æ¯æ ¹ç±»çã
+ </para>
+ </callout>
+ <callout arearefs="discriminator4">
+ <para>
+ <literal>insert</literal> (å¯é - é»è®¤ä¸º<literal>true</literal>)
+ å¦æä½ çé´å«å¨å段ä¹æ¯æ å°ä¸ºå¤åæ è¯ï¼composite identifierï¼çä¸é¨åï¼åéå°
+ è¿ä¸ªå¼è®¾ä¸º<literal>false</literal>ãï¼åè¯Hibernateå¨åSQL <literal>INSERT</literal>
+ æ¶ä¸å
å«è¯¥åï¼
+ </para>
+ </callout>
+ <callout arearefs="discriminator5">
+ <para>
+ <literal>formula</literal> (å¯é)
+ ä¸ä¸ªSQL表达å¼ï¼å¨ç±»åå¤æï¼å¤ææ¯ç¶ç±»è¿æ¯å
·ä½åç±»ï¼è¯æ³¨ï¼æ¶æ§è¡ãå¯ç¨äºåºäºå
容çé´å«å¨ã
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ é´å«å¨å段çå®é
å¼æ¯æ ¹æ®<literal><class></literal>å<literal><subclass></literal>å
ç´ ä¸
+ ç<literal>discriminator-value</literal>å±æ§å¾æ¥çã
+ </para>
+
+ <para>
+ <literal>force</literal>å±æ§ä»
ä»
å¨è¿ç§æ
åµä¸æç¨çï¼è¡¨ä¸å
å«æ²¡æ被æ å°å°æä¹
åç±»çéå 辨å«å¨å¼ã
+ è¿ç§æ
åµä¸ä¼ç»å¸¸éå°ã
+ </para>
+
+ <para>
+ 使ç¨<literal>formula</literal>å±æ§ä½ å¯ä»¥å®ä¹ä¸ä¸ªSQL表达å¼ï¼ç¨æ¥å¤æä¸ä¸ªè¡æ°æ®çç±»åã
+ </para>
+
+ <programlisting><![CDATA[<discriminator
+ formula="case when CLASS_TYPE in ('a', 'b', 'c') then 0 else 1 end"
+ type="integer"/>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-version" revision="4">
+ <title>çæ¬ï¼versionï¼(å¯é)</title>
+
+ <para>
+ <literal><version></literal>å
ç´ æ¯å¯éçï¼è¡¨æ表ä¸å
å«é带çæ¬ä¿¡æ¯çæ°æ®ã
+ è¿å¨ä½ åå¤ä½¿ç¨<emphasis> é¿äºå¡ï¼long transactionsï¼</emphasis>çæ¶åç¹å«æç¨ãï¼è§åï¼
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="version1" coords="2 70"/>
+ <area id="version2" coords="3 70"/>
+ <area id="version3" coords="4 70"/>
+ <area id="version4" coords="5 70"/>
+ <area id="version5" coords="6 70"/>
+ <area id="version6" coords="7 70"/>
+ <area id="version7" coords="8 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<version
+ column="version_column"
+ name="propertyName"
+ type="typename"
+ access="field|property|ClassName"
+ unsaved-value="null|negative|undefined"
+ generated="never|always"
+ insert="true|false"
+ node="element-name|@attribute-name|element/@attribute|."
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="version1">
+ <para>
+ <literal>column</literal> (å¯é - é»è®¤ä¸ºå±æ§å): æå®ææçæ¬å·çå段åã
+ </para>
+ </callout>
+ <callout arearefs="version2">
+ <para>
+ <literal>name</literal>: æä¹
åç±»çå±æ§åã
+ </para>
+ </callout>
+ <callout arearefs="version3">
+ <para>
+ <literal>type</literal> (å¯é - é»è®¤æ¯ <literal>integer</literal>): çæ¬å·çç±»åã
+ </para>
+ </callout>
+ <callout arearefs="version4">
+ <para>
+ <literal>access</literal> (å¯é - é»è®¤æ¯ <literal>property</literal>):
+ Hibernateç¨äºè®¿é®å±æ§å¼ççç¥ã
+ </para>
+ </callout>
+ <callout arearefs="version5">
+ <para>
+ <literal>unsaved-value</literal> (å¯é - é»è®¤æ¯<literal>undefined</literal>):
+ ç¨äºæ ææ个å®ä¾æ¶åå被å®ä¾åçï¼å°æªä¿åï¼çæ¬å±æ§å¼ï¼ä¾é è¿ä¸ªå¼å°±å¯ä»¥æè¿ç§æ
åµ
+ åå·²ç»å¨å
åçsessionä¸ä¿åæè£
è½½çè±ç®¡ï¼detachedï¼å®ä¾åºåå¼æ¥ã
+ ï¼<literal>undefined</literal>ææåºè¢«ä½¿ç¨çæ è¯å±æ§å¼ãï¼
+ </para>
+ </callout>
+ <callout arearefs="version6">
+ <para>
+ <literal>generated</literal> (å¯é - é»è®¤æ¯ <literal>never</literal>):
+ 表ææ¤çæ¬å±æ§å¼æ¯å¦å®é
ä¸æ¯ç±æ°æ®åºçæçã请åé
<xref linkend="mapping-generated">generated properties</xref>é¨åç讨论ã
+ </para>
+ </callout>
+ <callout arearefs="version7">
+ <para>
+ <literal>insert</literal> (å¯é - é»è®¤æ¯ <literal>true</literal>):
+ 表ææ¤çæ¬ååºè¯¥å
å«å¨SQLæå
¥è¯å¥ä¸ãåªæå½æ°æ®åºå段æé»è®¤å¼<literal>0</literal>çæ¶åï¼æå¯ä»¥è®¾ç½®ä¸º<literal>false</literal>ã
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ çæ¬å·å¿
é¡»æ¯ä»¥ä¸ç±»åï¼<literal>long</literal>, <literal>integer</literal>,
+ <literal>short</literal>, <literal>timestamp</literal>æè
<literal>calendar</literal>ã
+ </para>
+
+ <para>
+ ä¸ä¸ªè±ç®¡ï¼detachedï¼å®ä¾çversionætimestampå±æ§ä¸è½ä¸ºç©ºï¼nullï¼ï¼å 为Hibernateä¸ç®¡
+ <literal>unsaved-value</literal>被æå®ä¸ºä½ç§çç¥ï¼å®å°ä»»ä½å±æ§ä¸ºç©ºçversionætimestamp
+ å®ä¾çä½ä¸ºç¬æ¶ï¼transientï¼å®ä¾ã
+ <emphasis>é¿å
Hibernateä¸çä¼ éééï¼transitive reattachmentï¼é®é¢çä¸ä¸ªç®åæ¹æ³æ¯
+ å®ä¹ä¸ä¸ªä¸è½ä¸ºç©ºçversionætimestampå±æ§ï¼ç¹å«æ¯å¨äººä»¬ä½¿ç¨ç¨åºåé
çæ è¯ç¬¦ï¼assigned identifiersï¼
+ æå¤å主é®æ¶é常æç¨ï¼</emphasis>
+ </para>
+ </sect2>
+
+ <sect2 id="mapping-declaration-timestamp" revision="4">
+ <title>timestamp (å¯é)</title>
+
+ <para>
+ å¯éç<literal><timestamp></literal>å
ç´ ææäºè¡¨ä¸å
å«æ¶é´æ³æ°æ®ã
+ è¿ç¨æ¥ä½ä¸ºçæ¬çæ¿ä»£ãæ¶é´æ³æ¬è´¨ä¸æ¯ä¸ç§å¯¹ä¹è§éå®çä¸ç§ä¸æ¯ç¹å«å®å
¨çå®ç°ãå½ç¶ï¼
+ ææ¶ååºç¨ç¨åºå¯è½å¨å
¶ä»æ¹é¢ä½¿ç¨æ¶é´æ³ã
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="timestamp1" coords="2 70"/>
+ <area id="timestamp2" coords="3 70" />
+ <area id="timestamp3" coords="4 70" />
+ <area id="timestamp4" coords="5 70" />
+ <area id="timestamp5" coords="6 70" />
+ <area id="timestamp6" coords="7 70" />
+ </areaspec>
+ <programlisting><![CDATA[<timestamp
+ column="timestamp_column"
+ name="propertyName"
+ access="field|property|ClassName"
+ unsaved-value="null|undefined"
+ source="vm|db"
+ generated="never|always"
+ node="element-name|@attribute-name|element/@attribute|."
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="timestamp1">
+ <para>
+ <literal>column</literal> (å¯é - é»è®¤ä¸ºå±æ§å): æææ¶é´æ³çå段åã
+ </para>
+ </callout>
+ <callout arearefs="timestamp2">
+ <para>
+ <literal>name</literal>: å¨æä¹
åç±»ä¸çJavaBeansé£æ ¼çå±æ§åï¼
+ å
¶Javaç±»åæ¯ <literal>Date</literal> æè
<literal>Timestamp</literal>çã
+ </para>
+ </callout>
+ <callout arearefs="timestamp3">
+ <para>
+ <literal>access</literal> (å¯é - é»è®¤æ¯ <literal>property</literal>):
+ Hibernateç¨äºè®¿é®å±æ§å¼ççç¥ã
+ </para>
+ </callout>
+ <callout arearefs="timestamp4">
+ <para>
+ <literal>unsaved-value</literal> (å¯é - é»è®¤æ¯<literal>null</literal>):
+ ç¨äºæ ææ个å®ä¾æ¶åå被å®ä¾åçï¼å°æªä¿åï¼çæ¬å±æ§å¼ï¼ä¾é è¿ä¸ªå¼å°±å¯ä»¥æè¿ç§æ
åµå
+ å·²ç»å¨å
åçsessionä¸ä¿åæè£
è½½çè±ç®¡ï¼detachedï¼å®ä¾åºåå¼æ¥ãï¼<literal>undefined</literal>
+ ææ使ç¨æ è¯å±æ§å¼è¿è¡è¿ç§å¤æãï¼
+ </para>
+ </callout>
+ <callout arearefs="timestamp5">
+ <para>
+ <literal>source</literal> (å¯é - é»è®¤æ¯ <literal>vm</literal>):
+ Hibernateå¦ä½æè½è·åå°æ¶é´æ³çå¼å¢ï¼ä»æ°æ®åºï¼è¿æ¯å½åJVMï¼ä»æ°æ®åºè·åä¼å¸¦æ¥ä¸äºè´æ
ï¼å 为Hibernateå¿
须访é®æ°æ®åºæ¥è·å¾âä¸ä¸ä¸ªå¼âï¼ä½æ¯å¨é群ç¯å¢ä¸ä¼æ´å®å
¨äºãè¿è¦æ³¨æï¼å¹¶ä¸æ¯ææç<literal>Dialectï¼æ¹è¨ï¼</literal>é½æ¯æè·å¾æ°æ®åºçå½åæ¶é´æ³çï¼èæ¯æçæ°æ®åºä¸åæä¸é¨åå 为精度ä¸è¶³,ç¨äºéå®æ¯ä¸å®å
¨çï¼ä¾å¦Oracle 8ï¼ã
+ </para>
+ </callout>
+ <callout arearefs="timestamp6">
+ <para>
+ <literal>generated</literal> (å¯é - é»è®¤æ¯ <literal>never</literal>):
+ æåºæ¶é´æ³å¼æ¯å¦å®é
ä¸æ¯ç±æ°æ®åºçæç.请åé
<xref linkend="mapping-generated">generated properties</xref>ç讨论ã
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ 注æï¼<literal><timestamp></literal> å<literal><version type="timestamp"></literal>æ¯çä»·çã并ä¸<literal><timestamp source="db"></literal>å<literal><version type="dbtimestamp"></literal>æ¯çä»·çã
+ </para>
+ </sect2>
+
+
+ <sect2 id="mapping-declaration-property" revision="4">
+ <title>property</title>
+
+ <para>
+ <literal><property></literal>å
ç´ ä¸ºç±»å®ä¹äºä¸ä¸ªæä¹
åç,JavaBeané£æ ¼çå±æ§ã
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="property1" coords="2 70"/>
+ <area id="property2" coords="3 70"/>
+ <area id="property3" coords="4 70"/>
+ <areaset id="property4-5" coords="">
+ <area id="property4" coords='5 70'/>
+ <area id="property5" coords='6 70'/>
+ </areaset>
+ <area id="property6" coords="7 70"/>
+ <area id="property7" coords="8 70"/>
+ <area id="property8" coords="9 70"/>
+ <area id="property9" coords="10 70"/>
+ <area id="property10" coords="11 70"/>
+ <area id="property11" coords="12 70"/>
+ <area id="property12" coords="13 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<property
+ name="propertyName"
+ column="column_name"
+ type="typename"
+ update="true|false"
+ insert="true|false"
+ formula="arbitrary SQL expression"
+ access="field|property|ClassName"
+ lazy="true|false"
+ unique="true|false"
+ not-null="true|false"
+ optimistic-lock="true|false"
+ generated="never|insert|always"
+ node="element-name|@attribute-name|element/@attribute|."
+
+ index="index_name"
+ unique_key="unique_key_id"
+ length="L"
+ precision="P"
+ scale="S"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="property1">
+ <para>
+ <literal>name</literal>: å±æ§çåå,以å°ååæ¯å¼å¤´ã
+ </para>
+ </callout>
+ <callout arearefs="property2">
+ <para>
+ <literal>column</literal> (å¯é - é»è®¤ä¸ºå±æ§åå): 对åºçæ°æ®åºå段åã
+ ä¹å¯ä»¥éè¿åµå¥ç<literal><column></literal>å
ç´ æå®ã
+ </para>
+ </callout>
+ <callout arearefs="property3">
+ <para>
+ <literal>type</literal> (å¯é): ä¸ä¸ªHibernateç±»åçååã
+ </para>
+ </callout>
+ <callout arearefs="property4-5">
+ <para>
+ <literal>update, insert</literal> (å¯é - é»è®¤ä¸º <literal>true</literal>) :
+ 表æç¨äº<literal>UPDATE</literal> å/æ <literal>INSERT</literal>
+ çSQLè¯å¥ä¸æ¯å¦å
å«è¿ä¸ªè¢«æ å°äºçå段ãè¿äºè
å¦æé½è®¾ç½®ä¸º<literal>false</literal>
+ å表æè¿æ¯ä¸ä¸ªâå¤æºæ§ï¼derivedï¼âçå±æ§ï¼å®çå¼æ¥æºäºæ å°å°åä¸ä¸ªï¼æå¤ä¸ªï¼
+ å段çæäºå
¶ä»å±æ§ï¼æè
éè¿ä¸ä¸ªtrigger(触åå¨ï¼æå
¶ä»ç¨åºçæã
+ </para>
+ </callout>
+ <callout arearefs="property6">
+ <para>
+ <literal>formula</literal> (å¯é): ä¸ä¸ªSQL表达å¼ï¼å®ä¹äºè¿ä¸ª<emphasis>计ç®
+ ï¼computedï¼</emphasis> å±æ§çå¼ã计ç®å±æ§æ²¡æåå®å¯¹åºçæ°æ®åºå段ã
+ </para>
+ </callout>
+ <callout arearefs="property7">
+ <para>
+ <literal>access</literal> (å¯é - é»è®¤å¼ä¸º <literal>property</literal>):
+ Hibernateç¨æ¥è®¿é®å±æ§å¼ççç¥ã
+ </para>
+ </callout>
+ <callout arearefs="property8">
+ <para>
+ <literal>lazy</literal> (å¯é - é»è®¤ä¸º <literal>false</literal>): æå®
+ æå®å®ä¾åé第ä¸æ¬¡è¢«è®¿é®æ¶ï¼è¿ä¸ªå±æ§æ¯å¦å»¶è¿æåï¼fetched lazilyï¼ï¼
+ éè¦è¿è¡æ¶åèç å¢å¼ºï¼ã
+ </para>
+ </callout>
+ <callout arearefs="property9">
+ <para>
+ <literal>unique</literal> (å¯é): 使ç¨DDL为该å段添å å¯ä¸ç约æã
+ åæ ·ï¼å
许å®ä½ä¸º<literal>property-ref</literal>å¼ç¨çç®æ ã
+ </para>
+ </callout>
+ <callout arearefs="property10">
+ <para>
+ <literal>not-null</literal> (å¯é): 使ç¨DDL为该å段添å å¯å¦ä¸ºç©ºï¼nullabilityï¼ç约æã
+ </para>
+ </callout>
+ <callout arearefs="property11">
+ <para>
+ <literal>optimistic-lock</literal> (å¯é - é»è®¤ä¸º <literal>true</literal>):
+ æå®è¿ä¸ªå±æ§å¨åæ´æ°æ¶æ¯å¦éè¦è·å¾ä¹è§éå®ï¼optimistic lockï¼ã
+ æ¢å¥è¯è¯´ï¼å®å³å®è¿ä¸ªå±æ§åçèæ°æ®æ¶çæ¬ï¼versionï¼çå¼æ¯å¦å¢é¿ã
+ </para>
+ </callout>
+ <callout arearefs="property12">
+ <para>
+ <literal>generated</literal> (å¯é - é»è®¤ä¸º <literal>never</literal>):
+ 表ææ¤å±æ§å¼æ¯å¦å®é
ä¸æ¯ç±æ°æ®åºçæçã请åé
<xref linkend="mapping-generated">generated properties</xref>ç讨论ã
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ <emphasis>typename</emphasis>å¯ä»¥æ¯å¦ä¸å ç§ï¼
+ </para>
+
+ <orderedlist spacing="compact">
+ <listitem>
+ <para>
+ Hibernateåºæ¬ç±»ååï¼æ¯å¦ï¼<literal>integer, string, character,date, timestamp,
+ float, binary, serializable, object, blob</literal>ï¼ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ä¸ä¸ªJavaç±»çååï¼è¿ä¸ªç±»å±äºä¸ç§é»è®¤åºç¡ç±»å
+ (æ¯å¦ï¼ <literal>int, float,char, java.lang.String, java.util.Date, java.lang.Integer,
+ java.sql.Clob</literal>)ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ä¸ä¸ªå¯ä»¥åºååçJavaç±»çååã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ä¸ä¸ªèªå®ä¹ç±»åçç±»çååãï¼æ¯å¦ï¼ <literal>com.illflow.type.MyCustomType</literal>)ã
+ </para>
+ </listitem>
+ </orderedlist>
+
+ <para>
+ å¦æä½ æ²¡ææå®ç±»åï¼Hibernarteä¼ä½¿ç¨åå°æ¥å¾å°è¿ä¸ªååçå±æ§ï¼ä»¥æ¤æ¥çæµæ£ç¡®çHibernateç±»åã
+ Hibernateä¼æç
§è§å2,3,4ç顺åºå¯¹å±æ§è¯»åå¨(getteræ¹æ³ï¼çè¿åç±»è¿è¡è§£éãç¶èï¼è¿è¿ä¸å¤ã
+ å¨æäºæ
åµä¸ä½ ä»ç¶éè¦<literal>type</literal>å±æ§ãï¼æ¯å¦ï¼ä¸ºäºåºå«<literal>Hibernate.DATE</literal>
+ å<literal>Hibernate.TIMESTAMP</literal>,æè
为äºæå®ä¸ä¸ªèªå®ä¹ç±»åãï¼
+ </para>
+
+ <para>
+ <literal>access</literal>å±æ§ç¨æ¥è®©ä½ æ§å¶Hibernateå¦ä½å¨è¿è¡æ¶è®¿é®å±æ§ãå¨é»è®¤æ
åµä¸ï¼
+ Hibernateä¼ä½¿ç¨å±æ§çget/setæ¹æ³å¯¹ï¼pairï¼ãå¦æä½ ææ<literal>access="field"</literal>,
+ Hibernateä¼å¿½ç¥get/setæ¹æ³å¯¹ï¼ç´æ¥ä½¿ç¨åå°æ¥è®¿é®æååéãä½ ä¹å¯ä»¥æå®ä½ èªå·±ççç¥ï¼
+ è¿å°±éè¦ä½ èªå·±å®ç°<literal>org.hibernate.property.PropertyAccessor</literal>æ¥å£ï¼
+ åå¨accessä¸è®¾ç½®ä½ èªå®ä¹çç¥ç±»çååã
+ </para>
+
+ <para>
+ è¡çå±æ§ï¼derive propertieï¼æ¯ä¸ä¸ªç¹å«å¼ºå¤§çç¹å¾ãè¿äºå±æ§åºè¯¥å®ä¹ä¸ºåªè¯»ï¼å±æ§å¼å¨è£
è½½æ¶è®¡ç®çæã
+ ä½ ç¨ä¸ä¸ªSQL表达å¼çæ计ç®çç»æï¼å®ä¼å¨è¿ä¸ªå®ä¾è½¬è½½æ¶ç¿»è¯æä¸ä¸ªSQLæ¥è¯¢ç<literal>SELECT</literal>
+ åæ¥è¯¢è¯å¥ã
+ </para>
+
+ <programlisting><![CDATA[
+<property name="totalPrice"
+ formula="( SELECT SUM (li.quantity*p.price) FROM LineItem li, Product p
+ WHERE li.productId = p.productId
+ AND li.customerId = customerId
+ AND li.orderNumber = orderNumber )"/>]]></programlisting>
+
+ <para>
+ 注æï¼ä½ å¯ä»¥ä½¿ç¨å®ä½èªå·±ç表ï¼èä¸ç¨ä¸ºè¿ä¸ªç¹å«çåå®ä¹å«åï¼
+ ä¸é¢ä¾åä¸ç<literal>customerId</literal>ï¼ãåæ¶æ³¨æï¼å¦æä½ ä¸å欢使ç¨å±æ§ï¼
+ ä½ å¯ä»¥ä½¿ç¨åµå¥ç<literal><formula></literal>æ å°å
ç´ ã
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-manytoone" revision="5">
+ <title>å¤å¯¹ä¸ï¼many-to-oneï¼</title>
+
+ <para>
+ éè¿<literal>many-to-one</literal>å
ç´ ,å¯ä»¥å®ä¹ä¸ç§å¸¸è§çä¸å¦ä¸ä¸ªæä¹
åç±»çå
³èã
+ è¿ç§å
³ç³»æ¨¡åæ¯å¤å¯¹ä¸å
³èï¼å®é
ä¸æ¯ä¸ä¸ªå¯¹è±¡å¼ç¨ï¼è¯æ³¨ï¼ï¼è¿ä¸ªè¡¨çä¸ä¸ªå¤é®å¼ç¨ç®æ 表ç
+ 主é®å段ã
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="manytoone1" coords="2 70"/>
+ <area id="manytoone2" coords="3 70"/>
+ <area id="manytoone3" coords="4 70"/>
+ <area id="manytoone4" coords="5 70"/>
+ <area id="manytoone5" coords="6 70"/>
+ <areaset id="manytoone6-7" coords="">
+ <area id="manytoone6" coords='7 70'/>
+ <area id="manytoone7" coords='8 70'/>
+ </areaset>
+ <area id="manytoone8" coords="9 70"/>
+ <area id="manytoone9" coords="10 70"/>
+ <area id="manytoone10" coords="11 70"/>
+ <area id="manytoone11" coords="12 70"/>
+ <area id="manytoone12" coords="13 70"/>
+ <area id="manytoone13" coords="14 70"/>
+ <area id="manytoone14" coords="15 70"/>
+ <area id="manytoone15" coords="16 70"/>
+ <area id="manytoone16" coords="17 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<many-to-one
+ name="propertyName"
+ column="column_name"
+ class="ClassName"
+ cascade="cascade_style"
+ fetch="join|select"
+ update="true|false"
+ insert="true|false"
+ property-ref="propertyNameFromAssociatedClass"
+ access="field|property|ClassName"
+ unique="true|false"
+ not-null="true|false"
+ optimistic-lock="true|false"
+ lazy="proxy|no-proxy|false"
+ not-found="ignore|exception"
+ entity-name="EntityName"
+ formula="arbitrary SQL expression"
+ node="element-name|@attribute-name|element/@attribute|."
+
+ embed-xml="true|false"
+ index="index_name"
+ unique_key="unique_key_id"
+ foreign-key="foreign_key_name"
+
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="manytoone1">
+ <para>
+ <literal>name</literal>: å±æ§åã
+ </para>
+ </callout>
+ <callout arearefs="manytoone2">
+ <para>
+ <literal>column</literal> (å¯é): å¤é´å段åãå®ä¹å¯ä»¥éè¿åµå¥ç
+ <literal><column></literal>å
ç´ æå®ã
+ </para>
+ </callout>
+ <callout arearefs="manytoone3">
+ <para>
+ <literal>class</literal> (å¯é - é»è®¤æ¯éè¿åå°å¾å°å±æ§ç±»å): å
³èçç±»çååã
+ </para>
+ </callout>
+ <callout arearefs="manytoone4">
+ <para>
+ <literal>cascadeï¼çº§èï¼</literal> (å¯é): ææåªäºæä½ä¼ä»ç¶å¯¹è±¡çº§èå°å
³èç对象ã
+ </para>
+ </callout>
+ <callout arearefs="manytoone5">
+ <para>
+ <literal>fetch</literal> (å¯é - é»è®¤ä¸º <literal>select</literal>):
+ å¨å¤è¿æ¥æåï¼outer-join fetchingï¼ååºåéæ©æåï¼sequential select fetchingï¼ä¸¤è
ä¸éæ©å
¶ä¸ã
+ </para>
+ </callout>
+ <callout arearefs="manytoone6">
+ <para>
+ <literal>update, insert</literal> (å¯é - é»è®¤ä¸º <literal>true</literal>)
+ æå®å¯¹åºçå段æ¯å¦å
å«å¨ç¨äº<literal>UPDATE</literal> å/æ <literal>INSERT</literal>
+ çSQLè¯å¥ä¸ãå¦æäºè
é½æ¯<literal>false</literal>,åè¿æ¯ä¸ä¸ªçº¯ç²¹ç
+ âå¤æºæ§ï¼derivedï¼âå
³èï¼å®çå¼æ¯éè¿æ å°å°åä¸ä¸ªï¼æå¤ä¸ªï¼å段çæäºå
¶ä»å±æ§å¾å°
+ æè
éè¿trigger(触åå¨ï¼ãæå
¶ä»ç¨åºçæã
+ </para>
+ </callout>
+ <callout arearefs="manytoone7">
+ <para>
+ <literal>property-ref</literal>: (å¯é) æå®å
³èç±»çä¸ä¸ªå±æ§ï¼è¿ä¸ªå±æ§å°ä¼åæ¬å¤é®ç¸å¯¹åºã
+ å¦æ没ææå®ï¼ä¼ä½¿ç¨å¯¹æ¹å
³èç±»ç主é®ã
+ </para>
+ </callout>
+ <callout arearefs="manytoone8">
+ <para>
+ <literal>access</literal> (å¯é - é»è®¤æ¯ <literal>property</literal>):
+ Hibernateç¨æ¥è®¿é®å±æ§ççç¥ã
+ </para>
+ </callout>
+ <callout arearefs="manytoone9">
+ <para>
+ <literal>unique</literal> (å¯é): 使ç¨DDL为å¤é®å段çæä¸ä¸ªå¯ä¸çº¦æãæ¤å¤ï¼
+ è¿ä¹å¯ä»¥ç¨ä½<literal>property-ref</literal>çç®æ å±æ§ãè¿ä½¿å
³èåæ¶å
·æ
+ ä¸å¯¹ä¸çææã
+ </para>
+ </callout>
+ <callout arearefs="manytoone10">
+ <para>
+ <literal>not-null</literal> (å¯é): 使ç¨DDL为å¤é®å段çæä¸ä¸ªé空约æã
+ </para>
+ </callout>
+ <callout arearefs="manytoone11">
+ <para>
+ <literal>optimistic-lock</literal> (å¯é - é»è®¤ä¸º <literal>true</literal>):
+ æå®è¿ä¸ªå±æ§å¨åæ´æ°æ¶æ¯å¦éè¦è·å¾ä¹è§éå®ï¼optimistic lockï¼ã
+ æ¢å¥è¯è¯´ï¼å®å³å®è¿ä¸ªå±æ§åçèæ°æ®æ¶çæ¬ï¼versionï¼çå¼æ¯å¦å¢é¿ã
+ </para>
+ </callout>
+ <callout arearefs="manytoone12">
+ <para>
+ <literal>lazy</literal> (å¯é - é»è®¤ä¸º <literal>proxy</literal>):
+ é»è®¤æ
åµä¸ï¼åç¹å
³èæ¯ç»è¿ä»£ççã<literal>lazy="no-proxy"</literal>æå®æ¤å±æ§åºè¯¥å¨å®ä¾åé第ä¸æ¬¡è¢«è®¿é®æ¶åºè¯¥å»¶è¿æåï¼fetche lazilyï¼ï¼éè¦è¿è¡æ¶åèç çå¢å¼ºï¼ã
+ <literal>lazy="false"</literal>æå®æ¤å
³èæ»æ¯è¢«é¢å
æåã
+ </para>
+ </callout>
+ <callout arearefs="manytoone13">
+ <para>
+ <literal>not-found</literal> (å¯é - é»è®¤ä¸º <literal>exception</literal>):
+ æå®å¤é®å¼ç¨çæ°æ®ä¸åå¨æ¶å¦ä½å¤çï¼
+ <literal>ignore</literal>ä¼å°è¡æ°æ®ä¸åå¨è§ä¸ºä¸ä¸ªç©ºï¼nullï¼å
³èã
+ </para>
+ </callout>
+ <callout arearefs="manytoone14">
+ <para>
+ <literal>entity-name</literal> (å¯é): 被å
³èçç±»çå®ä½åã
+ </para>
+ </callout>
+ <callout arearefs="manytoone15">
+ <para>
+ <literal>formula</literal> (å¯é):
+ SQL表达å¼ï¼ç¨äºå®ä¹<emphasis>computedï¼è®¡ç®åºçï¼</emphasis>å¤é®å¼ã
+ </para>
+ </callout>
+
+ </calloutlist>
+ </programlistingco>
+
+
+ <para>
+ <literal>cascade</literal>å±æ§è®¾ç½®ä¸ºé¤äº<literal>none</literal>以å¤ä»»ä½ææä¹çå¼ï¼
+ å®å°æç¹å®çæä½ä¼ éå°å
³è对象ä¸ãè¿ä¸ªå¼å°±ä»£è¡¨çHibernateåºæ¬æä½çå称ï¼
+ <literal>persist, merge, delete, save-update, evict, replicate, lock, refresh</literal>ï¼
+ 以åç¹å«çå¼<literal>delete-orphan</literal>å<literal>all</literal>ï¼å¹¶ä¸å¯ä»¥ç¨éå·åé符
+ æ¥ç»åè¿äºæä½ï¼ä¾å¦ï¼<literal>cascade="persist,merge,evict"</literal>æ
+ <literal>cascade="all,delete-orphan"</literal>ãæ´å
¨é¢ç解é请åè<xref linkend="objectstate-transitive"/>. 注æï¼åå¼å
³è (many-to-one å
+ one-to-one å
³è) ä¸æ¯æå é¤å¤å¿ï¼orphan deleteï¼å é¤ä¸å被å¼ç¨çå¼ï¼.
+
+
+ </para>
+
+ <para>
+ ä¸ä¸ªå
¸åçç®å<literal>many-to-one</literal>å®ä¹ä¾åï¼
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="product" class="Product" column="PRODUCT_ID"/>]]></programlisting>
+
+ <para>
+ <literal>property-ref</literal>å±æ§åªåºè¯¥ç¨æ¥å¯¹ä»éçä¸æ¥çæ°æ®åºç³»ç»ï¼
+ å¯è½æå¤é®æå对æ¹å
³è表çæ¯ä¸ªé主é®å段ï¼ä½æ¯åºè¯¥æ¯ä¸ä¸ªæä¸å
³é®åï¼çæ
åµä¸ã
+ è¿æ¯ä¸ç§ååä¸éçå
³ç³»æ¨¡åãæ¯å¦è¯´ï¼å设<literal>Product</literal>ç±»æä¸ä¸ªæä¸çåºåå·ï¼
+ å®å¹¶ä¸æ¯ä¸»é®ãï¼<literal>unique</literal>å±æ§æ§å¶Hibernateéè¿SchemaExportå·¥å
·è¿è¡çDDLçæãï¼
+ </para>
+
+
+ <programlisting><![CDATA[<property name="serialNumber" unique="true" type="string" column="SERIAL_NUMBER"/>]]></programlisting>
+
+ <para>
+ é£ä¹å
³äº<literal>OrderItem</literal> çæ å°å¯è½æ¯ï¼
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="product" property-ref="serialNumber" column="PRODUCT_SERIAL_NUMBER"/>]]></programlisting>
+
+ <para>
+ å½ç¶ï¼æ们å³ä¸é¼å±è¿ç§ç¨æ³ã
+ </para>
+
+
+ <para>
+ å¦æ被å¼ç¨çå¯ä¸ä¸»é®ç±å
³èå®ä½çå¤ä¸ªå±æ§ç»æï¼ä½ åºè¯¥å¨å称为<literal><properties></literal>çå
ç´
+ éé¢æ å°ææå
³èçå±æ§ã
+ </para>
+
+ <para>
+ åè¥è¢«å¼ç¨çå¯ä¸ä¸»é®æ¯ç»ä»¶çå±æ§ï¼ä½ å¯ä»¥æå®å±æ§è·¯å¾ï¼
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="owner" property-ref="identity.ssn" column="OWNER_SSN"/>]]></programlisting>
+
+ </sect2>
+
+<sect2 id="mapping-declaration-onetoone" revision="3">
+ <title>ä¸å¯¹ä¸</title>
+
+ <para>
+ æä¹
å对象ä¹é´ä¸å¯¹ä¸çå
³èå
³ç³»æ¯éè¿<literal>one-to-one</literal>å
ç´ å®ä¹çã
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="onetoone1" coords="2 70"/>
+ <area id="onetoone2" coords="3 70"/>
+ <area id="onetoone3" coords="4 70"/>
+ <area id="onetoone4" coords="5 70"/>
+ <area id="onetoone5" coords="6 70"/>
+ <area id="onetoone6" coords="7 70"/>
+ <area id="onetoone7" coords="8 70"/>
+ <area id="onetoone8" coords="9 70"/>
+ <area id="onetoone9" coords="10 70"/>
+ <area id="onetoone10" coords="11 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<one-to-one
+ name="propertyName"
+ class="ClassName"
+ cascade="cascade_style"
+ constrained="true|false"
+ fetch="join|select"
+ property-ref="propertyNameFromAssociatedClass"
+ access="field|property|ClassName"
+ formula="any SQL expression"
+ lazy="proxy|no-proxy|false"
+ entity-name="EntityName"
+ node="element-name|@attribute-name|element/@attribute|."
+
+ embed-xml="true|false"
+ foreign-key="foreign_key_name"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="onetoone1">
+ <para>
+ <literal>name</literal>: å±æ§çååã
+ </para>
+ </callout>
+ <callout arearefs="onetoone2">
+ <para>
+ <literal>class</literal> (å¯é - é»è®¤æ¯éè¿åå°å¾å°çå±æ§ç±»å)ï¼è¢«å
³èçç±»çååã
+ </para>
+ </callout>
+ <callout arearefs="onetoone3">
+ <para>
+ <literal>cascade(级è)</literal> (å¯é) 表ææä½æ¯å¦ä»ç¶å¯¹è±¡çº§èå°è¢«å
³èç对象ã
+ </para>
+ </callout>
+ <callout arearefs="onetoone4">
+ <para>
+ <literal>constrained(约æ)</literal> (å¯é) 表æ该类对åºç表对åºçæ°æ®åºè¡¨ï¼å被å
³èç对象æ对åºçæ°æ®åºè¡¨ä¹é´ï¼éè¿ä¸ä¸ªå¤é®å¼ç¨å¯¹ä¸»é®è¿è¡çº¦æã
+ è¿ä¸ªé项影å<literal>save()</literal>å<literal>delete()</literal>å¨çº§èæ§è¡æ¶çå
å顺åºä»¥å
+ å³å®è¯¥å
³èè½å¦è¢«å§æ(ä¹å¨schema export toolä¸è¢«ä½¿ç¨).
+ </para>
+ </callout>
+ <callout arearefs="onetoone5">
+ <para>
+ <literal>fetch</literal> (å¯é - é»è®¤è®¾ç½®ä¸º<literal>éæ©</literal>):
+ å¨å¤è¿æ¥æåæè
åºåéæ©æåéæ©å
¶ä¸.
+ </para>
+ </callout>
+ <callout arearefs="onetoone6">
+ <para>
+ <literal>property-ref</literal>: (å¯é) æå®å
³èç±»çå±æ§åï¼è¿ä¸ªå±æ§å°ä¼åæ¬ç±»ç主é®ç¸å¯¹åºãå¦æ没ææå®ï¼ä¼ä½¿ç¨å¯¹æ¹å
³èç±»ç主é®ã
+ </para>
+ </callout>
+ <callout arearefs="onetoone7">
+ <para>
+ <literal>access</literal> (å¯é - é»è®¤æ¯ <literal>property</literal>): Hibernateç¨æ¥è®¿é®å±æ§ççç¥ã
+ </para>
+ </callout>
+ <callout arearefs="onetoone8">
+ <para>
+ <literal>formula </literal> (å¯é):ç»å¤§å¤æ°ä¸å¯¹ä¸çå
³èé½æåå
¶å®ä½ç主é®ãå¨ä¸äºå°è§çæ
åµä¸ï¼ ä½ å¯è½ä¼æåå
¶ä»çä¸ä¸ªæå¤ä¸ªå段ï¼æè
æ¯ä¸ä¸ªè¡¨è¾¾å¼ï¼è¿äºæ
åµä¸ï¼ä½ å¯ä»¥ç¨ä¸ä¸ªSQLå
¬å¼æ¥è¡¨ç¤ºã ï¼å¯ä»¥å¨org.hibernate.test.onetooneformulaæ¾å°ä¾åï¼
+
+ </para>
+ </callout>
+ <callout arearefs="onetoone9">
+ <para>
+ <literal>lazy</literal> (å¯é - é»è®¤ä¸º <literal>proxy</literal>):
+ é»è®¤æ
åµä¸ï¼åç¹å
³èæ¯ç»è¿ä»£ççã<literal>lazy="no-proxy"</literal>æå®æ¤å±æ§åºè¯¥å¨å®ä¾åé第ä¸æ¬¡è¢«è®¿é®æ¶åºè¯¥å»¶è¿æåï¼fetche lazilyï¼ï¼éè¦è¿è¡æ¶åèç çå¢å¼ºï¼ã
+ <literal>lazy="false"</literal>æå®æ¤å
³èæ»æ¯è¢«é¢å
æåã<emphasis>注æï¼å¦æ<literal>constrained="false"</literal>,
+ ä¸å¯è½ä½¿ç¨ä»£çï¼Hibernateä¼éåé¢å
æåï¼</emphasis>
+ </para>
+ </callout>
+ <callout arearefs="onetoone10">
+ <para>
+ <literal>entity-name</literal> (å¯é): 被å
³èçç±»çå®ä½åã
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ æ两ç§ä¸åçä¸å¯¹ä¸å
³èï¼
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ 主é®å
³è
+ </para></listitem>
+ <listitem>
+ <para>
+ æä¸å¤é®å
³è
+ </para></listitem>
+ </itemizedlist>
+
+ <para>
+ 主é®å
³èä¸éè¦é¢å¤ç表å段ï¼å¦æ两è¡æ¯éè¿è¿ç§ä¸å¯¹ä¸å
³ç³»ç¸å
³èçï¼é£ä¹è¿ä¸¤è¡å°±å
±äº«åæ ·ç主å
³é®åå¼ãæ以å¦æä½ å¸æ两个对象éè¿ä¸»é®ä¸å¯¹ä¸å
³èï¼ä½ å¿
须确认å®ä»¬è¢«èµäºåæ ·çæ è¯å¼ï¼
+ </para>
+
+ <para>
+ æ¯å¦è¯´ï¼å¯¹ä¸é¢ç<literal>Employee</literal>å<literal>Person</literal>è¿è¡ä¸»é®ä¸å¯¹ä¸å
³è:
+ </para>
+
+
+ <programlisting><![CDATA[<one-to-one name="person" class="Person"/>]]></programlisting>
+ <programlisting><![CDATA[<one-to-one name="employee" class="Employee" constrained="true"/>]]></programlisting>
+
+ <para>
+ ç°å¨æ们å¿
须确ä¿PERSONåEMPLOYEEä¸ç¸å
³çå段æ¯ç¸ççãæ们使ç¨ä¸ä¸ªè¢«æ为<literal>foreign</literal>çç¹æ®çhibernateæ è¯ç¬¦çæçç¥ï¼
+ </para>
+
+ <programlisting><![CDATA[<class name="person" table="PERSON">
+ <id name="id" column="PERSON_ID">
+ <generator class="foreign">
+ <param name="property">employee</param>
+ </generator>
+ </id>
+ ...
+ <one-to-one name="employee"
+ class="Employee"
+ constrained="true"/>
+</class>]]></programlisting>
+
+ <para>
+ ä¸ä¸ªååä¿åç<literal>Person</literal>å®ä¾è¢«èµäºå该<literal>Person</literal>ç<literal>employee</literal>å±æ§ææåç<literal>Employee</literal>å®ä¾åæ ·çå
³é®åå¼ã
+ </para>
+
+ <para>
+ å¦ä¸ç§æ¹å¼æ¯ä¸ä¸ªå¤é®åä¸ä¸ªæä¸å
³é®å对åºï¼ä¸é¢ç<literal>Employee</literal>å<literal>Person</literal>çä¾åï¼å¦æ使ç¨è¿ç§å
³èæ¹å¼ï¼å¯ä»¥è¡¨è¾¾æï¼
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="person" class="Person" column="PERSON_ID" unique="true"/>]]></programlisting>
+
+ <para>
+ å¦æå¨<literal>Person</literal>çæ å°å å
¥ä¸é¢å å¥ï¼è¿ç§å
³èå°±æ¯ååçï¼
+ </para>
+
+ <programlisting><![CDATA[<one-to-one name"employee" class="Employee" property-ref="person"/>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-naturalid">
+ <title>èªç¶ID(natural-id)</title>
+
+ <programlisting><![CDATA[<natural-id mutable="true|false"/>
+ <property ... />
+ <many-to-one ... />
+ ......
+</natural-id>]]></programlisting>
+
+ <para>
+ æ们建议使ç¨ä»£ç¨é®ï¼é®å¼ä¸å
·å¤å®é
æä¹ï¼ä½ä¸ºä¸»é®ï¼æ们ä»ç¶åºè¯¥å°è¯ä¸ºææçå®ä½éç¨èªç¶çé®å¼ä½ä¸ºï¼éå ââè¯è
注ï¼æ 示ãèªç¶é®ï¼natural keyï¼æ¯å个æç»åå±æ§ï¼ä»ä»¬å¿
é¡»å¯ä¸ä¸é空ãå¦æå®è¿æ¯ä¸å¯åçé£å°±æ´çæ³äºãå¨<literal><natural-id></literal>å
ç´ ä¸ååºèªç¶é®çå±æ§ãHibernateä¼å¸®ä½ çæå¿
é¡»çå¯ä¸é®å¼åé空约æï¼ä½ çæ å°ä¼æ´å çææ¾ææï¼åææ¯self-documentingï¼èªæ注解ï¼ã
+ </para>
+
+ <para>
+ æ们强çå»ºè®®ä½ å®ç°<literal>equals()</literal> å<literal>hashCode()</literal>æ¹æ³,æ¥æ¯è¾å®ä½çèªç¶é®å±æ§ã
+ </para>
+
+ <para>
+ è¿ä¸æ å°ä¸æ¯ä¸ºäºæèªç¶é®ä½ä¸ºä¸»é®èåå¤çã
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>mutable</literal> (å¯é, é»è®¤ä¸º<literal>false</literal>):
+ é»è®¤æ
åµä¸ï¼èªç¶æ è¯å±æ§è¢«åå®ä¸ºä¸å¯åçï¼å¸¸éï¼ã
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect2>
+
+
+ <sect2 id="mapping-declaration-component" revision="2">
+ <title>ç»ä»¶(component), å¨æç»ä»¶(dynamic-component)</title>
+
+ <para>
+ <literal><component></literal>å
ç´ æå对象çä¸äºå
ç´ ä¸ç¶ç±»å¯¹åºç表çä¸äºå段æ å°èµ·æ¥ã ç¶åç»ä»¶å¯ä»¥å®ä¹å®ä»¬èªå·±çå±æ§ãç»ä»¶æè
éåãåè§åé¢çâComponentsâä¸ç« ã
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="component1" coords="2 45"/>
+ <area id="component2" coords="3 45"/>
+ <area id="component3" coords="4 45"/>
+ <area id="component4" coords="5 45"/>
+ <area id="component5" coords="6 45"/>
+ <area id="component6" coords="7 45"/>
+ <area id="component7" coords="8 45"/>
+ <area id="component8" coords="9 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<component
+ name="propertyName"
+ class="className"
+ insert="true|false"
+ update="true|false"
+ access="field|property|ClassName"
+ lazy="true|false"
+ optimistic-lock="true|false"
+ unique="true|false"
+ node="element-name|."
+>
+
+ <property ...../>
+ <many-to-one .... />
+ ........
+</component>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="component1">
+ <para>
+ <literal>name</literal>: å±æ§å
+ </para>
+
+ </callout>
+ <callout arearefs="component2">
+ <para>
+ <literal>class</literal> (å¯é - é»è®¤ä¸ºéè¿åå°å¾å°çå±æ§ç±»å):ç»ä»¶(å)ç±»çååã
+ </para>
+ </callout>
+ <callout arearefs="component3">
+ <para>
+ <literal>insert</literal>: 被æ å°çå段æ¯å¦åºç°å¨SQLç<literal>INSERT</literal>è¯å¥ä¸?
+ </para>
+ </callout>
+ <callout arearefs="component4">
+ <para>
+ <literal>update</literal>: 被æ å°çå段æ¯å¦åºç°å¨SQLç<literal>UPDATE</literal>è¯å¥ä¸?
+ </para>
+ </callout>
+ <callout arearefs="component5">
+ <para>
+ <literal>access</literal> (å¯é - é»è®¤æ¯ <literal>property</literal>): Hibernateç¨æ¥è®¿é®å±æ§ççç¥ã
+ </para>
+ </callout>
+ <callout arearefs="component6">
+ <para>
+ <literal>lazy</literal> (å¯é - é»è®¤æ¯ <literal>false</literal>): 表ææ¤ç»ä»¶åºå¨å®ä¾åé第ä¸æ¬¡è¢«è®¿é®çæ¶å延è¿å è½½(éè¦ç¼è¯æ¶åèç è£
ç½®å¨)
+ </para>
+ </callout>
+ <callout arearefs="component7">
+ <para>
+ <literal>optimistic-lock</literal> (å¯é - é»è®¤æ¯ <literal>true</literal>):表ææ´æ°æ¤ç»ä»¶æ¯å¦éè¦è·åä¹è§éãæ¢å¥è¯è¯´ï¼å½è¿ä¸ªå±æ§åèæ¶ï¼æ¯å¦å¢å çæ¬å·(Version)
+ </para>
+ </callout>
+ <callout arearefs="component8">
+ <para>
+ <literal>unique</literal> (å¯é - é»è®¤æ¯ <literal>false</literal>):表æç»ä»¶æ å°çææå段ä¸é½æå¯ä¸æ§çº¦æ
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ å
¶<literal><property></literal>åæ ç¾ä¸ºåç±»çä¸äºå±æ§ä¸è¡¨å段ä¹é´å»ºç«æ å°ã
+ </para>
+
+ <para>
+ <literal><component></literal>å
ç´ å
许å å
¥ä¸ä¸ª<literal><parent></literal>åå
ç´ ï¼å¨ç»ä»¶ç±»å
é¨å°±å¯ä»¥æä¸ä¸ªæåå
¶å®¹å¨çå®ä½çååå¼ç¨ã
+ </para>
+
+ <para>
+ <literal><dynamic-component></literal>å
ç´ å
许æä¸ä¸ª<literal>Map</literal>æ å°ä¸ºç»ä»¶ï¼å
¶å±æ§å对åºmapçé®å¼ã
+ åè§<xref linkend="components-dynamic"/>.
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-properties" revision="2">
+ <title>properties</title>
+
+ <para>
+ <literal><properties></literal> å
ç´ å
许å®ä¹ä¸ä¸ªå½åçé»è¾åç»(grouping)å
å«ä¸ä¸ªç±»ä¸çå¤ä¸ªå±æ§ã
+ è¿ä¸ªå
ç´ æéè¦çç¨å¤æ¯å
许å¤ä¸ªå±æ§çç»åä½ä¸º<literal>property-ref</literal>çç®æ (target)ã
+ è¿ä¹æ¯å®ä¹å¤å段å¯ä¸çº¦æçä¸ç§æ¹ä¾¿éå¾ã
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="properties1" coords="2 45"/>
+ <area id="properties2" coords="3 45"/>
+ <area id="properties3" coords="4 45"/>
+ <area id="properties4" coords="5 45"/>
+ <area id="properties5" coords="6 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<properties
+ name="logicalName"
+ insert="true|false"
+ update="true|false"
+ optimistic-lock="true|false"
+ unique="true|false"
+>
+
+ <property ...../>
+ <many-to-one .... />
+ ........
+</properties>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="properties1">
+ <para>
+ <literal>name</literal>: åç»çé»è¾å称 -
+ <emphasis>ä¸æ¯</emphasis> å®é
å±æ§çå称.
+ </para>
+ </callout>
+ <callout arearefs="properties2">
+ <para>
+ <literal>insert</literal>: 被æ å°çå段æ¯å¦åºç°å¨SQLç
+ <literal>INSERT</literal>è¯å¥ä¸?
+ </para>
+ </callout>
+ <callout arearefs="properties3">
+ <para>
+ <literal>update</literal>: 被æ å°çå段æ¯å¦åºç°å¨SQLç
+ <literal>UPDATE</literal>è¯å¥ä¸?
+ </para>
+ </callout>
+ <callout arearefs="properties4">
+ <para>
+ <literal>optimistic-lock</literal> (å¯é - é»è®¤æ¯ <literal>true</literal>):表ææ´æ°æ¤ç»ä»¶æ¯å¦éè¦è·åä¹è§éãæ¢å¥è¯è¯´ï¼å½è¿ä¸ªå±æ§åèæ¶ï¼æ¯å¦å¢å çæ¬å·(Version)
+ </para>
+ </callout>
+ <callout arearefs="properties5">
+ <para>
+ <literal>unique</literal> (å¯é - é»è®¤æ¯ <literal>false</literal>):表æç»ä»¶æ å°çææå段ä¸é½æå¯ä¸æ§çº¦æ
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ ä¾å¦ï¼å¦ææ们æå¦ä¸ç<literal><properties></literal>æ å°:
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="personNumber"/>
+ ...
+ <properties name="name"
+ unique="true" update="false">
+ <property name="firstName"/>
+ <property name="initial"/>
+ <property name="lastName"/>
+ </properties>
+</class>]]></programlisting>
+
+ <para>
+ ç¶åï¼æ们å¯è½æä¸äºéççæ°æ®å
³èï¼å¼ç¨ <literal>Person</literal>表çè¿ä¸ªå¯ä¸é®ï¼èä¸æ¯ä¸»é®ã
+ </para>
+ <programlisting><![CDATA[<many-to-one name="person"
+ class="Person" property-ref="name">
+ <column name="firstName"/>
+ <column name="initial"/>
+ <column name="lastName"/>
+</many-to-one>]]></programlisting>
+
+ <para>
+ æ们并ä¸æ¨èè¿æ ·ä½¿ç¨ï¼é¤éå¨æ å°éçæ°æ®çæ
åµä¸ã
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-subclass" revision="4">
+ <title>åç±»(subclass)</title>
+
+ <para>
+ æåï¼å¤ææä¹
åéè¦ä¸ºç¶ç±»çæ¯ä¸ªåç±»é½è¿è¡å®ä¹ã对äºâæ¯ä¸æ£µç±»ç»§æ¿æ 对åºä¸ä¸ªè¡¨âççç¥æ¥è¯´ï¼å°±éè¦ä½¿ç¨<literal><subclass></literal>å®ä¹ã
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="subclass1" coords="2 55"/>
+ <area id="subclass2" coords="3 55"/>
+ <area id="subclass3" coords="4 55"/>
+ <area id="subclass4" coords="5 55"/>
+ </areaspec>
+ <programlisting><![CDATA[<subclass
+ name="ClassName"
+ discriminator-value="discriminator_value"
+ proxy="ProxyInterface"
+ lazy="true|false"
+ dynamic-update="true|false"
+ dynamic-insert="true|false"
+ entity-name="EntityName"
+ node="element-name"
+ extends="SuperclassName">
+
+ <property .... />
+ .....
+</subclass>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="subclass1">
+ <para>
+ <literal>name</literal>: åç±»çå
¨éå®åã
+ </para>
+ </callout>
+ <callout arearefs="subclass2">
+ <para>
+ <literal>discriminator-value(辨å«æ å¿)</literal> (å¯é - é»è®¤ä¸ºç±»å):ä¸ä¸ªç¨äºåºåæ¯ä¸ªç¬ç«çåç±»çå¼ã
+ </para>
+ </callout>
+ <callout arearefs="subclass3">
+ <para>
+ <literal>proxy(代ç)</literal> (å¯é): æå®ä¸ä¸ªç±»æè
æ¥å£ï¼å¨å»¶è¿è£
è½½æ¶ä½ä¸ºä»£ç使ç¨ã
+ </para>
+ </callout>
+ <callout arearefs="subclass4">
+ <para>
+ <literal>lazy</literal> (å¯é, é»è®¤æ¯<literal>true</literal>): 设置为
+ <literal>lazy="false"</literal> ç¦æ¢ä½¿ç¨å»¶è¿æå
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ æ¯ä¸ªåç±»é½åºè¯¥å®ä¹å®èªå·±çæä¹
åå±æ§ååç±»ã
+ <literal><version></literal> å<literal><id></literal> å±æ§å¯ä»¥ä»æ ¹ç¶ç±»ç»§æ¿ä¸æ¥ãå¨ä¸æ£µç»§æ¿æ ä¸çæ¯ä¸ªåç±»é½å¿
é¡»å®ä¹ä¸ä¸ªå¯ä¸ç<literal>discriminator-value</literal>ãå¦æ没ææå®ï¼å°±ä¼ä½¿ç¨Javaç±»çå
¨éå®åã
+ </para>
+
+
+ <para>
+ æ´å¤å
³äºç»§æ¿æ å°çä¿¡æ¯, åè <xref linkend="inheritance"/>ç« è.
+ </para>
+ </sect2>
+
+ <sect2 id="mapping-declaration-joinedsubclass" revision="3">
+ <title>è¿æ¥çåç±»(joined-subclass)</title>
+
+ <para>
+ æ¤å¤ï¼æ¯ä¸ªåç±»å¯è½è¢«æ å°å°ä»èªå·±ç表ä¸(æ¯ä¸ªåç±»ä¸ä¸ªè¡¨ççç¥)ã被继æ¿çç¶æéè¿åè¶
ç±»ç表å
³èå¾å°ãæ们使ç¨<literal><joined-subclass></literal>å
ç´ ã
+ </para>
+ <programlistingco>
+ <areaspec>
+ <area id="joinedsubclass1" coords="2 45"/>
+ <area id="joinedsubclass2" coords="3 45"/>
+ <area id="joinedsubclass3" coords="4 45"/>
+ <area id="joinedsubclass4" coords="5 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<joined-subclass
+ name="ClassName"
+ table="tablename"
+ proxy="ProxyInterface"
+ lazy="true|false"
+ dynamic-update="true|false"
+ dynamic-insert="true|false"
+ schema="schema"
+ catalog="catalog"
+ extends="SuperclassName"
+ persister="ClassName"
+ subselect="SQL expression"
+ entity-name="EntityName"
+ node="element-name">
+
+ <key .... >
+
+ <property .... />
+ .....
+</joined-subclass>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="joinedsubclass1">
+ <para>
+ <literal>name</literal>: åç±»çå
¨éå®åã
+ </para>
+ </callout>
+ <callout arearefs="joinedsubclass2">
+ <para>
+ <literal>table</literal>: åç±»ç表å.
+ </para>
+ </callout>
+ <callout arearefs="joinedsubclass3">
+ <para>
+ <literal>proxy</literal> (å¯é): æå®ä¸ä¸ªç±»æè
æ¥å£ï¼å¨å»¶è¿è£
è½½æ¶ä½ä¸ºä»£ç使ç¨ã
+ </para>
+ </callout>
+ <callout arearefs="joinedsubclass4">
+ <para>
+ <literal>lazy</literal> (å¯é, é»è®¤æ¯ <literal>true</literal>): 设置为
+ <literal>lazy="false"</literal> ç¦æ¢ä½¿ç¨å»¶è¿è£
è½½ã
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ è¿ç§æ å°çç¥ä¸éè¦æå®è¾¨å«æ å¿(discriminator)å段ãä½æ¯ï¼æ¯ä¸ä¸ªåç±»é½å¿
须使ç¨<literal><key></literal>å
ç´ æå®ä¸ä¸ªè¡¨å段æ¥ææ对象çæ è¯ç¬¦ãæ¬ç« å¼å§çæ å°å¯ä»¥è¢«ç¨å¦ä¸æ¹å¼éåï¼
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="eg">
+
+ <class name="Cat" table="CATS">
+ <id name="id" column="uid" type="long">
+ <generator class="hilo"/>
+ </id>
+ <property name="birthdate" type="date"/>
+ <property name="color" not-null="true"/>
+ <property name="sex" not-null="true"/>
+ <property name="weight"/>
+ <many-to-one name="mate"/>
+ <set name="kittens">
+ <key column="MOTHER"/>
+ <one-to-many class="Cat"/>
+ </set>
+ <joined-subclass name="DomesticCat" table="DOMESTIC_CATS">
+ <key column="CAT"/>
+ <property name="name" type="string"/>
+ </joined-subclass>
+ </class>
+
+ <class name="eg.Dog">
+ <!-- mapping for Dog could go here -->
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ æ´å¤å
³äºç»§æ¿æ å°çä¿¡æ¯ï¼åè<xref linkend="inheritance"/>ã
+ </para>
+ </sect2>
+
+ <sect2 id="mapping-declaration-unionsubclass" revision="2">
+ <title>èååç±»(union-subclass)</title>
+
+ <para>
+ 第ä¸ç§éæ©æ¯ä»
ä»
æ å°ç±»ç»§æ¿æ ä¸å
·ä½ç±»é¨åå°è¡¨ä¸(æ¯ä¸ªå
·ä½ç±»ä¸å¼ 表ççç¥)ãå
¶ä¸ï¼æ¯å¼ 表å®ä¹äºç±»çæææä¹
åç¶æï¼å
æ¬ç»§æ¿çç¶æãå¨ Hibernate ä¸ï¼å¹¶ä¸éè¦å®å
¨æ¾å¼å°æ å°è¿æ ·ç继æ¿æ ãä½ å¯ä»¥ç®åå°ä½¿ç¨åç¬ç<literal><class></literal>å®ä¹æ å°æ¯ä¸ªç±»ãç¶èï¼å¦æä½ æ³ä½¿ç¨å¤æå
³è(ä¾å¦ï¼ä¸ä¸ªå¯¹ç±»ç»§æ¿æ ä¸è¶
ç±»çå
³è)ï¼ä½ éè¦ä½¿ç¨<literal><union-subclass></literal>æ å°ã
+ </para>
+ <programlistingco>
+ <areaspec>
+ <area id="unionsubclass1" coords="2 45"/>
+ <area id="unionsubclass2" coords="3 45"/>
+ <area id="unionsubclass3" coords="4 45"/>
+ <area id="unionsubclass4" coords="5 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<union-subclass
+ name="ClassName"
+ table="tablename"
+ proxy="ProxyInterface"
+ lazy="true|false"
+ dynamic-update="true|false"
+ dynamic-insert="true|false"
+ schema="schema"
+ catalog="catalog"
+ extends="SuperclassName"
+ abstract="true|false"
+ persister="ClassName"
+ subselect="SQL expression"
+ entity-name="EntityName"
+ node="element-name">
+
+ <property .... />
+ .....
+</union-subclass>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="unionsubclass1">
+ <para>
+ <literal>name</literal>: åç±»çå
¨éå®åã
+ </para>
+ </callout>
+ <callout arearefs="unionsubclass2">
+ <para>
+ <literal>table</literal>: åç±»ç表å
+ </para>
+ </callout>
+ <callout arearefs="unionsubclass3">
+ <para>
+ <literal>proxy</literal> (å¯é): æå®ä¸ä¸ªç±»æè
æ¥å£ï¼å¨å»¶è¿è£
è½½æ¶ä½ä¸ºä»£ç使ç¨ã
+ </para>
+ </callout>
+ <callout arearefs="unionsubclass4">
+ <para>
+ <literal>lazy</literal> (å¯é, é»è®¤æ¯ <literal>true</literal>): 设置为
+ <literal>lazy="false"</literal> ç¦æ¢ä½¿ç¨å»¶è¿è£
è½½ã
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ è¿ç§æ å°çç¥ä¸éè¦æå®è¾¨å«æ å¿(discriminator)å段ã
+ </para>
+ <para>
+ æ´å¤å
³äºç»§æ¿æ å°çä¿¡æ¯ï¼åè<xref linkend="inheritance"/>ã
+ </para>
+ </sect2>
+
+ <sect2 id="mapping-declaration-join" revision="3">
+ <title>è¿æ¥(join)</title>
+
+ <para>
+ ä½¿ç¨ <literal><join></literal> å
ç´ ï¼åè¥å¨è¡¨ä¹é´åå¨ä¸å¯¹ä¸å
³è,å¯ä»¥å°ä¸ä¸ªç±»çå±æ§æ å°å°å¤å¼ 表ä¸ã
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="join1" coords="2 50"/>
+ <area id="join2" coords="3 50"/>
+ <area id="join3" coords="4 50"/>
+ <area id="join4" coords="5 50"/>
+ <area id="join5" coords="6 50"/>
+ <area id="join6" coords="7 50"/>
+ </areaspec>
+ <programlisting><![CDATA[<join
+ table="tablename"
+ schema="owner"
+ catalog="catalog"
+ fetch="join|select"
+ inverse="true|false"
+ optional="true|false">
+
+ <key ... />
+
+ <property ... />
+ ...
+</join>]]></programlisting>
+
+ <calloutlist>
+ <callout arearefs="join1">
+ <para>
+ <literal>table</literal>: 被è¿æ¥è¡¨çå称ã
+ </para>
+ </callout>
+ <callout arearefs="join2">
+ <para>
+ <literal>schema</literal> (å¯é):è¦çç±æ ¹<literal><hibernate-mapping></literal>å
ç´ æå®ç模å¼å称ã
+ </para>
+ </callout>
+ <callout arearefs="join3">
+ <para>
+ <literal>catalog</literal> (å¯é): è¦çç±æ ¹ <literal><hibernate-mapping></literal>å
ç´ æå®çç®å½å称ã
+ </para>
+ </callout>
+ <callout arearefs="join4">
+ <para>
+ <literal>fetch</literal> (å¯é - é»è®¤æ¯ <literal>join</literal>):
+ å¦æ设置为é»è®¤å¼<literal>join</literal>ï¼ Hibernate å°ä½¿ç¨ä¸ä¸ªå
è¿æ¥æ¥å¾å°è¿ä¸ªç±»æå
¶è¶
ç±»å®ä¹ç<literal><join></literal>ï¼è使ç¨ä¸ä¸ªå¤è¿æ¥æ¥å¾å°å
¶åç±»å®ä¹ç<literal><join></literal>ãå¦æ设置为<literal>select</literal>ï¼å Hibernate å°ä¸ºåç±»å®ä¹ç <literal><join></literal>使ç¨é¡ºåºéæ©ãè¿ä»
å¨ä¸è¡æ°æ®è¡¨ç¤ºä¸ä¸ªåç±»ç对象çæ¶åæä¼åçã对è¿ä¸ªç±»åå
¶è¶
ç±»å®ä¹ç<literal><join></literal>ï¼ä¾ç¶ä¼ä½¿ç¨å
è¿æ¥å¾å°ã
+ </para>
+ </callout>
+ <callout arearefs="join5">
+ <para>
+ <literal>inverse</literal> (å¯é - é»è®¤æ¯ <literal>false</literal>):
+ å¦ææå¼ï¼Hibernate ä¸ä¼æå
¥æè
æ´æ°æ¤è¿æ¥å®ä¹çå±æ§ã
+ </para>
+ </callout>
+ <callout arearefs="join6">
+ <para>
+ <literal>optional</literal> (å¯é - é»è®¤æ¯ <literal>false</literal>):
+ å¦ææå¼ï¼Hibernate åªä¼å¨æ¤è¿æ¥å®ä¹çå±æ§é空æ¶æå
¥ä¸è¡æ°æ®ï¼å¹¶ä¸æ»æ¯ä½¿ç¨ä¸ä¸ªå¤è¿æ¥æ¥å¾å°è¿äºå±æ§ã
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ ä¾å¦ï¼ä¸ä¸ªäºº(person)çå°å(address)ä¿¡æ¯å¯ä»¥è¢«æ å°å°åç¬ç表ä¸(并ä¿çææå±æ§çå¼ç±»åè¯ä¹)ï¼
+ </para>
+
+ <programlisting><![CDATA[<class name="Person"
+ table="PERSON">
+
+ <id name="id" column="PERSON_ID">...</id>
+
+ <join table="ADDRESS">
+ <key column="ADDRESS_ID"/>
+ <property name="address"/>
+ <property name="zip"/>
+ <property name="country"/>
+ </join>
+ ...]]></programlisting>
+
+ <para>
+ æ¤ç¹æ§å¸¸å¸¸å¯¹éçæ°æ®æ¨¡åæç¨ï¼æ们æ¨è表个æ°æ¯ç±»ä¸ªæ°å°ï¼ä»¥åç»ç²åº¦çé¢å模åãç¶èï¼å¨åç¬ç继æ¿æ ä¸åæ¢ç»§æ¿æ å°çç¥æ¯æç¨çï¼åé¢ä¼è§£éè¿ç¹ã
+ </para>
+ </sect2>
+
+ <sect2 id="mapping-declaration-key">
+ <title>é®(key)</title>
+
+ <para>
+ æ们ç®åå·²ç»è§å°è¿<literal><key></literal>å
ç´ å¤æ¬¡äºã è¿ä¸ªå
ç´ å¨ç¶æ å°å
ç´ å®ä¹äºå¯¹æ°è¡¨çè¿æ¥ï¼å¹¶ä¸å¨è¢«è¿æ¥è¡¨ä¸å®ä¹äºä¸ä¸ªå¤é®å¼ç¨å表ç主é®çæ
åµä¸ç»å¸¸ä½¿ç¨ã
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="key1" coords="2 50"/>
+ <area id="key2" coords="3 50"/>
+ <area id="key3" coords="4 50"/>
+ <area id="key4" coords="5 50"/>
+ <area id="key5" coords="6 50"/>
+ <area id="key6" coords="7 50"/>
+ </areaspec>
+ <programlisting><![CDATA[<key
+ column="columnname"
+ on-delete="noaction|cascade"
+ property-ref="propertyName"
+ not-null="true|false"
+ update="true|false"
+ unique="true|false"
+/>]]></programlisting>
+
+ <calloutlist>
+ <callout arearefs="key1">
+ <para>
+ <literal>column</literal> (å¯é): å¤é®å段çå称ãä¹å¯ä»¥éè¿åµå¥ç <literal><column></literal>æå®ã
+ </para>
+ </callout>
+ <callout arearefs="key2">
+ <para>
+ <literal>on-delete</literal> (å¯é, é»è®¤æ¯ <literal>noaction</literal>):
+ 表æå¤é®å
³èæ¯å¦æå¼æ°æ®åºçº§å«ç级èå é¤ã
+ </para>
+ </callout>
+ <callout arearefs="key3">
+ <para>
+ <literal>property-ref</literal> (å¯é): 表æå¤é®å¼ç¨çå段ä¸æ¯å表ç主é®(æä¾ç»éçæ°æ®)ã
+ </para>
+ </callout>
+ <callout arearefs="key4">
+ <para>
+ <literal>not-null</literal> (å¯é): 表æå¤é®çå段ä¸å¯ä¸ºç©º(è¿æå³çæ 论ä½æ¶å¤é®é½æ¯ä¸»é®çä¸é¨å)ã
+ </para>
+ </callout>
+ <callout arearefs="key5">
+ <para>
+ <literal>update</literal> (å¯é): 表æå¤é®å³ä¸åºè¯¥è¢«æ´æ°(è¿æå³çæ 论ä½æ¶å¤é®é½æ¯ä¸»é®çä¸é¨å)ã
+ </para>
+ </callout>
+ <callout arearefs="key6">
+ <para>
+ <literal>unique</literal> (å¯é): 表æå¤é®åºæå¯ä¸æ§çº¦æ (è¿æå³çæ 论ä½æ¶å¤é®é½æ¯ä¸»é®çä¸é¨å)ã
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ 对é£äºçéå é¤æ§è½çç³»ç»ï¼æ们æ¨èææçé®é½åºè¯¥å®ä¹ä¸º<literal>on-delete="cascade"</literal>ï¼è¿æ · Hibernate å°ä½¿ç¨æ°æ®åºçº§ç<literal>ON CASCADE DELETE</literal>约æï¼èä¸æ¯å¤ä¸ª<literal>DELETE</literal>è¯å¥ã 注æï¼è¿ä¸ªç¹æ§ä¼ç»è¿ Hibernate é常对çæ¬æ°æ®(versioned data)éç¨çä¹è§éçç¥ã
+ </para>
+ <para>
+ <literal>not-null</literal> å <literal>update</literal> å±æ§å¨æ å°ååä¸å¯¹å¤å
³èçæ¶åæç¨ãå¦æä½ æ å°ä¸ä¸ªååä¸å¯¹å¤å
³èå°é空ç(non-nullable)å¤é®ï¼ä½ <emphasis>å¿
é¡»</emphasis> ç¨<literal><key not-null="true"></literal>å®ä¹æ¤é®å段ã
+ </para>
+ </sect2>
+
+ <sect2 id="mapping-column" revision="4">
+ <title>å段åè§åå
ç´ ï¼column and formula elementsï¼</title>
+ <para>
+ ä»»ä½æ¥å<literal>column</literal>å±æ§çæ å°å
ç´ é½å¯ä»¥éæ©æ¥å<literal><column></literal> åå
ç´ ãåæ ·çï¼<literal>formula</literal>åå
ç´ ä¹å¯ä»¥æ¿æ¢<literal><formula></literal>å±æ§ã
+ </para>
+ <programlisting><![CDATA[<column
+ name="column_name"
+ length="N"
+ precision="N"
+ scale="N"
+ not-null="true|false"
+ unique="true|false"
+ unique-key="multicolumn_unique_key_name"
+ index="index_name"
+ sql-type="sql_type_name"
+ check="SQL expression"
+ default="SQL expression"/>]]></programlisting>
+
+ <programlisting><![CDATA[<formula>SQL expression</formula>]]></programlisting>
+ <para>
+ <literal>column</literal> å <literal>formula</literal> å±æ§çè³å¯ä»¥å¨åä¸ä¸ªå±æ§æå
³èæ å°ä¸è¢«å并æ¥è¡¨è¾¾ï¼ä¾å¦ï¼ä¸äºå¥å¼çè¿æ¥æ¡ä»¶ã
+ </para>
+ <programlisting><![CDATA[<many-to-one name="homeAddress" class="Address"
+ insert="false" update="false">
+ <column name="person_id" not-null="true" length="10"/>
+ <formula>'MAILING'</formula>
+</many-to-one>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="mapping-declaration-import">
+ <title>å¼ç¨(import)</title>
+
+ <para>
+ åè®¾ä½ çåºç¨ç¨åºæ两个åæ ·ååçæä¹
åç±»ï¼ä½æ¯ä½ ä¸æ³å¨Hibernateæ¥è¯¢ä¸ä½¿ç¨ä»ä»¬çå
¨éå®åãé¤äºä¾èµ<literal>auto-import="true"</literal>以å¤ï¼ç±»ä¹å¯ä»¥è¢«æ¾å¼å°âimport(å¼ç¨)âãä½ çè³å¯ä»¥å¼ç¨æ²¡æ被æç¡®æ å°çç±»åæ¥å£ã
+ </para>
+
+ <programlisting><![CDATA[<import class="java.lang.Object" rename="Universe"/>]]></programlisting>
+
+ <programlistingco>
+ <areaspec>
+ <area id="import1" coords="2 40"/>
+ <area id="import2" coords="3 40"/>
+ </areaspec>
+ <programlisting><![CDATA[<import
+ class="ClassName"
+ rename="ShortName"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="import1">
+ <para>
+ <literal>class</literal>: ä»»ä½Javaç±»çå
¨éå®åã
+ </para>
+ </callout>
+ <callout arearefs="import2">
+ <para>
+ <literal>rename</literal> (å¯é - é»è®¤ä¸ºç±»çå
¨éå®å):
+ å¨æ¥è¯¢è¯å¥ä¸å¯ä»¥ä½¿ç¨çååã
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ </sect2>
+
+ <sect2 id="mapping-types-anymapping" revision="2">
+ <title>any</title>
+ <para>
+ è¿æ¯å±æ§æ å°çåä¸ç§ç±»åã<literal><any></literal> æ å°å
ç´ å®ä¹äºä¸ç§ä»å¤ä¸ªè¡¨å°ç±»çå¤æå
³èãè¿ç§ç±»åçæ å°å¸¸å¸¸éè¦å¤äºä¸ä¸ªå段ã第ä¸ä¸ªå段ææ被å
³èå®ä½çç±»åï¼å
¶ä»çå段æææ è¯ç¬¦ã对è¿ç§ç±»åçå
³èæ¥è¯´ï¼ä¸å¯è½æå®ä¸ä¸ªå¤é®çº¦æï¼æ以è¿å½ç¶ä¸æ¯æ å°(å¤æ)å
³èçé常çæ¹å¼ãä½ åªåºè¯¥å¨é常ç¹æ®çæ
åµä¸ä½¿ç¨å®(æ¯å¦ï¼å®¡è®¡logï¼ç¨æ·ä¼è¯æ°æ®çç)ã
+ </para>
+ <para>
+ <literal>meta-type</literal> å±æ§ä½¿å¾åºç¨ç¨åºè½æå®ä¸ä¸ªå°æ°æ®åºå段çå¼æ å°å°æä¹
åç±»çèªå®ä¹ç±»åãè¿ä¸ªæä¹
åç±»å
å«æç¨<literal>id-type</literal>æå®çæ è¯ç¬¦å±æ§ã
+ ä½ å¿
é¡»æå®ä»meta-typeçå¼å°ç±»åçæ å°ã
+ </para>
+
+ <programlisting><![CDATA[<any name="being" id-type="long" meta-type="string">
+ <meta-value value="TBL_ANIMAL" class="Animal"/>
+ <meta-value value="TBL_HUMAN" class="Human"/>
+ <meta-value value="TBL_ALIEN" class="Alien"/>
+ <column name="table_name"/>
+ <column name="id"/>
+</any>]]></programlisting>
+
+ <programlistingco>
+ <areaspec>
+ <area id="any1" coords="2 50"/>
+ <area id="any2" coords="3 50"/>
+ <area id="any3" coords="4 50"/>
+ <area id="any4" coords="5 50"/>
+ <area id="any5" coords="6 50"/>
+ <area id="any6" coords="7 50"/>
+ </areaspec>
+ <programlisting><![CDATA[<any
+ name="propertyName"
+ id-type="idtypename"
+ meta-type="metatypename"
+ cascade="cascade_style"
+ access="field|property|ClassName"
+ optimistic-lock="true|false"
+>
+ <meta-value ... />
+ <meta-value ... />
+ .....
+ <column .... />
+ <column .... />
+ .....
+</any>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="any1">
+ <para>
+ <literal>name</literal>: å±æ§å
+ </para>
+ </callout>
+ <callout arearefs="any2">
+ <para>
+ <literal>id-type</literal>: æ è¯ç¬¦ç±»å
+ </para>
+ </callout>
+ <callout arearefs="any3">
+ <para>
+ <literal>meta-type</literal> (å¯é -é»è®¤æ¯ <literal>string</literal>):
+ å
许辨å«æ å¿(discriminator)æ å°çä»»ä½ç±»å
+ </para>
+ </callout>
+ <callout arearefs="any4">
+ <para>
+ <literal>cascade</literal> (å¯é -é»è®¤æ¯<literal>none</literal>):
+ 级èçç±»å
+ </para>
+ </callout>
+ <callout arearefs="any5">
+ <para>
+ <literal>access</literal> (å¯é -é»è®¤æ¯ <literal>property</literal>): Hibernate ç¨æ¥è®¿é®å±æ§å¼ççç¥ã
+ </para>
+ </callout>
+ <callout arearefs="any6">
+ <para>
+ <literal>optimistic-lock</literal> (å¯é -é»è®¤æ¯ <literal>true</literal>): 表ææ´æ°æ¤ç»ä»¶æ¯å¦éè¦è·åä¹è§éãæ¢å¥è¯è¯´ï¼å½è¿ä¸ªå±æ§åèæ¶ï¼æ¯å¦å¢å çæ¬å·(Version)
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="mapping-types">
+ <title>Hibernate çç±»å</title>
+ <sect2 id="mapping-types-entitiesvalues" revision="1">
+ <title>å®ä½(Entities)åå¼(values)</title>
+
+ <para>
+ 为äºç解å¾å¤ä¸æä¹
åæå¡ç¸å
³çJavaè¯è¨çº§å¯¹è±¡çè¡ä¸ºï¼æ们éè¦æå®ä»¬å为两类ï¼
+ </para>
+
+ <para>
+ <emphasis>å®ä½entity</emphasis> ç¬ç«äºä»»ä½ææå®ä½å¼ç¨ç对象ãä¸é常çJava模åç¸æ¯ï¼ä¸å被å¼ç¨ç对象ä¼è¢«å½ä½åå¾æ¶éæãå®ä½å¿
须被æ¾å¼çä¿ååå é¤(é¤éä¿ååå é¤æ¯ä»ç¶å®ä½ååå®ä½å¼åç<emphasis>级è</emphasis>)ãè¿åODMG模åä¸å
³äºå¯¹è±¡éè¿å¯è§¦åä¿ææä¹
æ§æä¸äºä¸åââæ¯è¾èµ·æ¥æ´å æ¥è¿åºç¨ç¨åºå¯¹è±¡é常å¨ä¸ä¸ªå¤§ç³»ç»ä¸ç使ç¨æ¹æ³ãå®ä½æ¯æ循ç¯å¼ç¨å交åå¼ç¨ï¼å®ä»¬ä¹å¯ä»¥å ä¸çæ¬ä¿¡æ¯ã
+ </para>
+
+ <para>
+ ä¸ä¸ªå®ä½çæä¹
ç¶æå
å«æåå
¶ä»å®ä½å<emphasis>å¼</emphasis>ç±»åå®ä¾çå¼ç¨ãå¼å¯ä»¥æ¯åå§ç±»åï¼éå(ä¸æ¯éåä¸ç对象)ï¼ç»ä»¶æè
ç¹å®çä¸å¯å对象ãä¸å®ä½ä¸åï¼å¼(ç¹å«æ¯éååç»ä»¶)æ¯éè¿å¯è§¦åæ§æ¥è¿è¡æä¹
ååå é¤çãå 为å¼å¯¹è±¡(ååå§ç±»åæ°æ®)æ¯éçå
å«ä»ä»¬çå®ä½è被æä¹
ååå é¤çï¼ä»ä»¬ä¸è½è¢«ç¬ç«çå ä¸çæ¬ä¿¡æ¯ãå¼æ²¡æç¬ç«çæ è¯ï¼æ以ä»ä»¬ä¸è½è¢«ä¸¤ä¸ªå®ä½æè
éåå
±äº«ã
+ </para>
+
+ <para>
+ ç´å°ç°å¨ï¼æ们é½ä¸ç´ä½¿ç¨æ¯è¯âæä¹
ç±»â(persistent class)æ¥ä»£è¡¨å®ä½ãæ们ä»ç¶ä¼è¿ä¹åã ç¶èä¸¥æ ¼è¯´æ¥ï¼ä¸æ¯ææçç¨æ·èªå®ä¹çï¼å¸¦ææä¹
åç¶æçç±»é½æ¯å®ä½ã<emphasis>ç»ä»¶</emphasis>å°±æ¯ç¨æ·èªå®ä¹ç±»ï¼å´æ¯å¼è¯ä¹çã<literal>java.lang.String</literal>ç±»åçjavaå±æ§ä¹æ¯å¼è¯ä¹çãç»äºè¿ä¸ªå®ä¹ä»¥åï¼æ们å¯ä»¥è¯´ææJDKæä¾çç±»å(ç±»)é½æ¯å¼ç±»åçè¯ä¹ï¼èç¨äºèªå®ä¹ç±»åå¯è½è¢«æ å°ä¸ºå®ä½ç±»åæå¼ç±»åè¯ä¹ãéç¨åªç§ç±»åçè¯ä¹åå³äºå¼å人åãå¨é¢å模åä¸ï¼å¯»æ¾å®ä½ç±»çä¸ä¸ªå¥½çº¿ç´¢æ¯å
±äº«å¼ç¨æåè¿ä¸ªç±»çåä¸å®ä¾ï¼èç»åæèåé常被转å为å¼ç±»åã
+ </para>
+
+ <para>
+ æ们ä¼å¨æ¬ææ¡£ä¸éå¤ç¢°å°è¿ä¸¤ä¸ªæ¦å¿µã
+ </para>
+
+ <para>
+ ææå¨äºå°javaç±»åç³»ç»(åå¼åè
å®ä¹çå®ä½åå¼ç±»å)æ å°å° SQL/æ°æ®åºç±»åç³»ç»ãHibernateæä¾äºè¿æ¥ä¸¤ä¸ªç³»ç»ä¹é´çæ¡¥æ¢ï¼å¯¹äºå®ä½ç±»åï¼æ们使ç¨<literal><class></literal>, <literal><subclass></literal> ççã对äºå¼ç±»åï¼æä»¬ä½¿ç¨ <literal><property></literal>, <literal><component></literal> åå
¶ä»ï¼é常è·éç<literal>type</literal>å±æ§ãè¿ä¸ªå±æ§çå¼æ¯Hibernate ç<emphasis>æ å°ç±»å</emphasis>çååãHibernateæä¾äºè®¸å¤ç°æçæ å°(æ åçJDKå¼ç±»å)ãä½ ä¹å¯ä»¥ç¼åèªå·±çæ å°ç±»å并å®ç°èªå®ä¹çåæ¢çç¥ï¼éåæ们ä¼çå°è¿ç¹ã
+ </para>
+ <para>
+ ææçHibernateå
建类åï¼é¤äºcollections以å¤ï¼é½æ¯æ空(null)è¯ä¹ã
+ </para>
+ </sect2>
+
+ <sect2 id="mapping-types-basictypes" revision="3">
+ <title>åºæ¬å¼ç±»å</title>
+
+ <para>
+ å
建ç <emphasis>åºæ¬æ å°ç±»å</emphasis>å¯ä»¥å¤§è´å为
+ <variablelist>
+ <varlistentry>
+ <term><literal>integer, long, short, float, double, character, byte,
+ boolean, yes_no, true_false</literal></term>
+ <listitem>
+ <para>
+ è¿äºç±»åé½å¯¹åºJavaçåå§ç±»åæè
å
¶å°è£
ç±»ï¼æ¥ç¬¦å(ç¹å®ååç)SQL å段类åã<literal>boolean, yes_no</literal> å <literal>true_false</literal>é½æ¯Java ä¸<literal>boolean</literal> æè
<literal>java.lang.Boolean</literal>çå¦å¤è¯´æ³ã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>string</literal></term>
+ <listitem>
+ <para>
+ ä»<literal>java.lang.String</literal> å°
+ <literal>VARCHAR</literal> (æè
Oracleç <literal>VARCHAR2</literal>)çæ å°ã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>date, time, timestamp</literal></term>
+ <listitem>
+ <para>
+ ä»<literal>java.util.Date</literal>åå
¶åç±»å°SQLç±»å<literal>DATE</literal>, <literal>TIME</literal> å<literal>TIMESTAMP</literal> (æçä»·ç±»å)çæ å°ã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>calendar, calendar_date</literal></term>
+ <listitem>
+ <para>
+ ä»<literal>java.util.Calendar</literal> å°SQL ç±»å<literal>TIMESTAMP</literal>å <literal>DATE</literal>(æçä»·ç±»å)çæ å°ã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>big_decimal, big_integer</literal></term>
+ <listitem>
+ <para>
+ ä»<literal>java.math.BigDecimal</literal>å<literal>java.math.BigInteger</literal>å°<literal>NUMERIC</literal> (æè
Oracle ç<literal>NUMBER</literal>ç±»å)çæ å°ã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>locale, timezone, currency</literal></term>
+ <listitem>
+ <para>
+ ä»<literal>java.util.Locale</literal>, <literal>java.util.TimeZone</literal> å<literal>java.util.Currency</literal> å°<literal>VARCHAR</literal> (æè
Oracle ç<literal>VARCHAR2</literal>ç±»å)çæ å°.
+ <literal>Locale</literal>å <literal>Currency</literal> çå®ä¾è¢«æ å°ä¸ºå®ä»¬çISO代ç ã<literal>TimeZone</literal>çå®ä¾è¢«å½±å°ä¸ºå®ç<literal>ID</literal>ã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>class</literal></term>
+ <listitem>
+ <para>
+ ä»<literal>java.lang.Class</literal> å°
+ <literal>VARCHAR</literal> (æè
Oracle ç<literal>VARCHAR2</literal>ç±»å)çæ å°ã<literal>Class</literal>被æ å°ä¸ºå®çå
¨éå®åã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>binary</literal></term>
+ <listitem>
+ <para>
+ æåèæ°ç»(byte arrays)æ å°ä¸ºå¯¹åºç SQLäºè¿å¶ç±»åã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>text</literal></term>
+ <listitem>
+ <para>
+ æé¿Javaå符串æ å°ä¸ºSQLç<literal>CLOB</literal>æè
<literal>TEXT</literal>ç±»åã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>serializable</literal></term>
+ <listitem>
+ <para>
+ æå¯åºååçJavaç±»åæ å°å°å¯¹åºçSQLäºè¿å¶ç±»åãä½ ä¹å¯ä»¥ä¸ºä¸ä¸ªå¹¶éé»è®¤ä¸ºåºæ¬ç±»åçå¯åºååJavaç±»æè
æ¥å£æå®Hibernateç±»å<literal>serializable</literal>ã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>clob, blob</literal></term>
+ <listitem>
+ <para>
+ JDBC ç±» <literal>java.sql.Clob</literal> å <literal>java.sql.Blob</literal>çæ å°ãæäºç¨åºå¯è½ä¸éå使ç¨è¿ä¸ªç±»åï¼å 为blobåclob对象å¯è½å¨ä¸ä¸ªäºå¡ä¹å¤æ¯æ æ³éç¨çã(èä¸, 驱å¨ç¨åºå¯¹è¿ç§ç±»åçæ¯æå
满çè¡¥ä¸åååçç¾ã)
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <literal>imm_date, imm_time, imm_timestamp, imm_calendar, imm_calendar_date,
+ imm_serializable, imm_binary</literal>
+ </term>
+ <listitem>
+ <para>
+ ä¸è¬æ¥è¯´ï¼æ å°ç±»å被åå®ä¸ºæ¯å¯åçJavaç±»åï¼åªæ对ä¸å¯åJavaç±»åï¼Hibernateä¼éåç¹å®çä¼åæªæ½ï¼åºç¨ç¨åºä¼æè¿äºå¯¹è±¡ä½ä¸ºä¸å¯å对象å¤çãæ¯å¦ï¼ä½ ä¸åºè¯¥å¯¹ä½ä¸º<literal>imm_timestamp</literal>æ å°çDateæ§è¡<literal>Date.setTime()</literal>ãè¦æ¹åå±æ§çå¼ï¼å¹¶ä¸ä¿åè¿ä¸æ¹åï¼åºç¨ç¨åºå¿
须对è¿ä¸å±æ§éæ°è®¾ç½®ä¸ä¸ªæ°çï¼ä¸ä¸æ ·çï¼å¯¹è±¡ã
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ </para>
+
+ <para>
+ å®ä½åå
¶éåçå¯ä¸æ è¯å¯ä»¥æ¯é¤äº<literal>binary</literal>ã <literal>blob</literal> å <literal>clob</literal>ä¹å¤çä»»ä½åºç¡ç±»åã(èåæ è¯ä¹æ¯å
许çï¼åé¢ä¼è¯´å°ã)
+ </para>
+
+ <para>
+ å¨<literal>org.hibernate.Hibernate</literal>ä¸ï¼å®ä¹äºåºç¡ç±»å对åºç<literal>Type</literal>常éãæ¯å¦ï¼<literal>Hibernate.STRING</literal>代表<literal>string</literal> ç±»åã
+ </para>
+
+ </sect2>
+
+ <sect2 id="mapping-types-custom" revision="2">
+ <title>èªå®ä¹å¼ç±»å</title>
+
+ <para>
+ å¼åè
å建å±äºä»ä»¬èªå·±çå¼ç±»åä¹æ¯å¾å®¹æçãæ¯å¦è¯´ï¼ä½ å¯è½å¸ææä¹
å<literal>java.lang.BigInteger</literal>ç±»åçå±æ§ï¼æä¹
åæ为<literal>VARCHAR</literal>å段ãHibernate没æå
ç½®è¿æ ·ä¸ç§ç±»åãèªå®ä¹ç±»åè½å¤æ å°ä¸ä¸ªå±æ§(æéåå
ç´ )å°ä¸æ¢ä¸ä¸ªæ°æ®åºè¡¨å段ãæ¯å¦è¯´ï¼ä½ å¯è½æè¿æ ·çJavaå±æ§ï¼<literal>getName()</literal>/<literal>setName()</literal>ï¼è¿æ¯<literal>java.lang.String</literal>ç±»åçï¼å¯¹åºçæä¹
åå°ä¸ä¸ªå段ï¼<literal>FIRST_NAME</literal>, <literal>INITIAL</literal>, <literal>SURNAME</literal>ã
+ </para>
+
+ <para>
+ è¦å®ç°ä¸ä¸ªèªå®ä¹ç±»åï¼å¯ä»¥å®ç°<literal>org.hibernate.UserType</literal>æ<literal>org.hibernate.CompositeUserType</literal>ä¸çä»»ä¸ä¸ªï¼å¹¶ä¸ä½¿ç¨ç±»åçJavaå
¨éå®ç±»åæ¥å®ä¹å±æ§ã请æ¥ç<literal>org.hibernate.test.DoubleStringType</literal>è¿ä¸ªä¾åï¼ççå®æ¯æä¹åçã
+ </para>
+
+ <programlisting><![CDATA[<property name="twoStrings" type="org.hibernate.test.DoubleStringType">
+ <column name="first_string"/>
+ <column name="second_string"/>
+</property>]]></programlisting>
+
+ <para>
+ 注æ使ç¨<literal><column></literal>æ ç¾æ¥æä¸ä¸ªå±æ§æ å°å°å¤ä¸ªå段çåæ³ã
+ </para>
+
+ <para>
+ <literal>CompositeUserType</literal>, <literal>EnhancedUserType</literal>,
+ <literal>UserCollectionType</literal>, å <literal>UserVersionType</literal> æ¥å£ä¸ºæ´ç¹æ®ç使ç¨æ¹å¼æä¾æ¯æã
+ </para>
+ <para>
+ ä½ çè³å¯ä»¥å¨ä¸ä¸ªæ å°æ件ä¸æä¾åæ°ç»ä¸ä¸ª<literal>UserType</literal>ã 为äºè¿æ ·åï¼ä½ ç<literal>UserType</literal>å¿
é¡»å®ç°<literal>org.hibernate.usertype.ParameterizedType</literal>æ¥å£ã为äºç»èªå®ä¹ç±»åæä¾åæ°ï¼ä½ å¯ä»¥å¨æ å°æ件ä¸ä½¿ç¨<literal><type></literal>å
ç´ ã
+ </para>
+ <programlisting><![CDATA[<property name="priority">
+ <type name="com.mycompany.usertypes.DefaultValueIntegerType">
+ <param name="default">0</param>
+ </type>
+</property>]]></programlisting>
+
+ <para>
+ ç°å¨ï¼<literal>UserType</literal> å¯ä»¥ä»ä¼ å
¥ç<literal>Properties</literal>对象ä¸å¾å°<literal>default</literal> åæ°çå¼ã
+ </para>
+ <para>
+ å¦æä½ é常é¢ç¹å°ä½¿ç¨æä¸<literal>UserType</literal>ï¼å¯ä»¥ä¸ºä»å®ä¹ä¸ä¸ªç®ç§°ãè¿å¯ä»¥éè¿ä½¿ç¨ <literal><typedef></literal>å
ç´ æ¥å®ç°ãTypedefs为ä¸èªå®ä¹ç±»åèµäºä¸ä¸ªå称ï¼å¹¶ä¸å¦ææ¤ç±»åæ¯åæ°åçï¼è¿å¯ä»¥å
å«ä¸ç³»åé»è®¤çåæ°å¼ã
+ </para>
+ <programlisting><![CDATA[<typedef class="com.mycompany.usertypes.DefaultValueIntegerType" name="default_zero">
+ <param name="default">0</param>
+</typedef>]]></programlisting>
+
+ <programlisting><![CDATA[<property name="priority" type="default_zero"/>]]></programlisting>
+
+ <para>
+ ä¹å¯ä»¥æ ¹æ®å
·ä½æ¡ä¾éè¿å±æ§æ å°ä¸çç±»ååæ°è¦çå¨typedefä¸æä¾çåæ°ã
+ </para>
+ <para>
+ 尽管 Hibernate å
建ç丰å¯çç±»åå对ç»ä»¶çæ¯ææå³çä½ å¯è½å¾å° <emphasis>éè¦</emphasis>使ç¨èªå®ä¹ç±»åãä¸è¿ï¼ä¸ºé£äºå¨ä½ çåºç¨ä¸ç»å¸¸åºç°ç(éå®ä½)类使ç¨èªå®ä¹ç±»åä¹æ¯ä¸ä¸ªå¥½æ¹æ³ãä¾å¦ï¼ä¸ä¸ª<literal>MonetaryAmount</literal>类使ç¨<literal>CompositeUserType</literal>æ¥æ å°æ¯ä¸éçéæ©ï¼è½ç¶ä»å¯ä»¥å¾å®¹æå°è¢«æ å°æç»ä»¶ãè¿æ ·åçå¨æºä¹ä¸æ¯æ½è±¡ã使ç¨èªå®ä¹ç±»åï¼ä»¥ååè¥ä½ æ¹å表示éé¢çæ¹æ³æ¶ï¼å®å¯ä»¥ä¿è¯æ å°æ件ä¸éè¦ä¿®æ¹ã
+ </para>
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="mapping-entityname">
+ <title>å¤æ¬¡æ å°åä¸ä¸ªç±»</title>
+ <para>
+ 对ç¹å®çæä¹
åç±»ï¼æ å°å¤æ¬¡æ¯å
许çãè¿ç§æ
å½¢ä¸ï¼ä½ å¿
é¡»æå®<emphasis>entity name</emphasis>æ¥åºå«ä¸åæ å°å®ä½ç对象å®ä¾ãï¼é»è®¤æ
åµä¸ï¼å®ä½åååç±»åæ¯ç¸åçãï¼
+ Hibernateå¨æä½æä¹
å对象ãç¼åæ¥è¯¢æ¡ä»¶ï¼æè
æå
³èæ å°å°æå®å®ä½æ¶ï¼å
è®¸ä½ æå®è¿ä¸ªentity nameï¼å®ä½ååï¼ã
+ </para>
+
+ <programlisting><![CDATA[<class name="Contract" table="Contracts"
+ entity-name="CurrentContract">
+ ...
+ <set name="history" inverse="true"
+ order-by="effectiveEndDate desc">
+ <key column="currentContractId"/>
+ <one-to-many entity-name="HistoricalContract"/>
+ </set>
+</class>
+
+<class name="Contract" table="ContractHistory"
+ entity-name="HistoricalContract">
+ ...
+ <many-to-one name="currentContract"
+ column="currentContractId"
+ entity-name="CurrentContract"/>
+</class>]]></programlisting>
+
+ <para>
+ 注æè¿éå
³èæ¯å¦ä½ç¨<literal>entity-name</literal>æ¥ä»£æ¿<literal>class</literal>çã
+ </para>
+
+ </sect1>
+
+ <sect1 id="mapping-quotedidentifiers">
+ <title>SQLä¸å¼å·å
å´çæ è¯ç¬¦</title>
+ <para>
+ ä½ å¯éè¿å¨æ å°ææ¡£ä¸ä½¿ç¨ååå¼å·(`)æ表åæè
å段åå
å´èµ·æ¥ï¼ä»¥å¼ºå¶Hibernateå¨çæçSQLä¸ææ è¯ç¬¦ç¨å¼å·å
å´èµ·æ¥ãHibernateä¼ä½¿ç¨ç¸åºçSQL<literal>Dialect</literal>(æ¹è¨)æ¥ä½¿ç¨æ£ç¡®çå¼å·é£æ ¼(é常æ¯åå¼å·ï¼ä½æ¯å¨SQL Serverä¸æ¯æ¬å·ï¼MySQLä¸æ¯ååå¼å·)ã
+ </para>
+
+ <programlisting><![CDATA[<class name="LineItem" table="`Line Item`">
+ <id name="id" column="`Item Id`"/><generator class="assigned"/></id>
+ <property name="itemNumber" column="`Item #`"/>
+ ...
+</class>]]></programlisting>
+
+ </sect1>
+
+
+ <sect1 id="mapping-alternatives">
+ <title>å
¶ä»å
æ°æ®(Metadata)</title>
+
+ <para>
+ XML 并ä¸éç¨äºææ人, å æ¤æå
¶ä»å®ä¹Hibernate O/R æ å°å
æ°æ®(metadata)çæ¹æ³ã
+ </para>
+
+ <sect2 id="mapping-xdoclet">
+ <title>ä½¿ç¨ XDoclet æ è®°</title>
+
+ <para>
+ å¾å¤Hibernate使ç¨è
æ´å欢使ç¨XDoclet<literal>@hibernate.tags</literal>å°æ å°ä¿¡æ¯ç´æ¥åµå
¥å°æºä»£ç ä¸ãæ们ä¸ä¼å¨æ¬ææ¡£ä¸æ¶åè¿ä¸ªæ¹æ³ï¼å ä¸ºä¸¥æ ¼è¯´æ¥ï¼è¿å±äºXDocletçä¸é¨åãç¶èï¼æ们å
å«äºå¦ä¸ä½¿ç¨XDocletæ å°ç<literal>Cat</literal>ç±»çä¾åã
+ </para>
+ <programlisting><![CDATA[package eg;
+import java.util.Set;
+import java.util.Date;
+
+/**
+ * @hibernate.class
+ * table="CATS"
+ */
+public class Cat {
+ private Long id; // identifier
+ private Date birthdate;
+ private Cat mother;
+ private Set kittens
+ private Color color;
+ private char sex;
+ private float weight;
+
+ /*
+ * @hibernate.id
+ * generator-class="native"
+ * column="CAT_ID"
+ */
+ public Long getId() {
+ return id;
+ }
+ private void setId(Long id) {
+ this.id=id;
+ }
+
+ /**
+ * @hibernate.many-to-one
+ * column="PARENT_ID"
+ */
+ public Cat getMother() {
+ return mother;
+ }
+ void setMother(Cat mother) {
+ this.mother = mother;
+ }
+
+ /**
+ * @hibernate.property
+ * column="BIRTH_DATE"
+ */
+ public Date getBirthdate() {
+ return birthdate;
+ }
+ void setBirthdate(Date date) {
+ birthdate = date;
+ }
+ /**
+ * @hibernate.property
+ * column="WEIGHT"
+ */
+ public float getWeight() {
+ return weight;
+ }
+ void setWeight(float weight) {
+ this.weight = weight;
+ }
+
+ /**
+ * @hibernate.property
+ * column="COLOR"
+ * not-null="true"
+ */
+ public Color getColor() {
+ return color;
+ }
+ void setColor(Color color) {
+ this.color = color;
+ }
+ /**
+ * @hibernate.set
+ * inverse="true"
+ * order-by="BIRTH_DATE"
+ * @hibernate.collection-key
+ * column="PARENT_ID"
+ * @hibernate.collection-one-to-many
+ */
+ public Set getKittens() {
+ return kittens;
+ }
+ void setKittens(Set kittens) {
+ this.kittens = kittens;
+ }
+ // addKitten not needed by Hibernate
+ public void addKitten(Cat kitten) {
+ kittens.add(kitten);
+ }
+
+ /**
+ * @hibernate.property
+ * column="SEX"
+ * not-null="true"
+ * update="false"
+ */
+ public char getSex() {
+ return sex;
+ }
+ void setSex(char sex) {
+ this.sex=sex;
+ }
+}]]></programlisting>
+
+ <para>
+ åèHibernateç½ç«æ´å¤çXdocletåHibernateçä¾å
+ </para>
+ </sect2>
+
+ <sect2 id="mapping-annotations" revision="2">
+ <title>ä½¿ç¨ JDK 5.0 ç注解(Annotation)</title>
+
+ <para>
+ JDK 5.0 å¨è¯è¨çº§å«å¼å
¥äº XDoclet é£æ ¼çæ 注ï¼å¹¶ä¸æ¯ç±»åå®å
¨çï¼å¨ç¼è¯æè¿è¡æ£æ¥ãè¿ä¸æºå¶æ¯XDocletç注解æ´ä¸ºå¼ºå¤§ï¼ææ´å¥½çå·¥å
·åIDEæ¯æãä¾å¦ï¼ IntelliJ IDEAï¼æ¯æJDK 5.0注解çèªå¨å®æåè¯æ³é«äº® ãEJBè§èçæ°ä¿®è®¢ç(JSR-220)ä½¿ç¨ JDK 5.0ç注解ä½ä¸ºentity beansç主è¦å
æ°æ®(metadata)æºå¶ãHibernate 3 å®ç°äºJSR-220 (the persistence API)ç<literal>EntityManager</literal>ï¼æ¯æéè¿<emphasis>Hibernate Annotations</emphasis>å
å®ä¹æ å°å
æ°æ®ãè¿ä¸ªå
ä½ä¸ºåç¬çé¨åä¸è½½ï¼æ¯æEJB3 (JSR-220)åHibernate3çå
æ°æ®ã
+ </para>
+ <para>
+ è¿æ¯ä¸ä¸ªè¢«æ³¨è§£ä¸ºEJB entity bean çPOJOç±»çä¾å
+ </para>
+ <programlisting><![CDATA[@Entity(access = AccessType.FIELD)
+public class Customer implements Serializable {
+
+ @Id;
+ Long id;
+
+ String firstName;
+ String lastName;
+ Date birthday;
+
+ @Transient
+ Integer age;
+
+ @Embedded
+ private Address homeAddress;
+
+ @OneToMany(cascade=CascadeType.ALL)
+ @JoinColumn(name="CUSTOMER_ID")
+ Set<Order> orders;
+
+ // Getter/setter and business methods
+}]]></programlisting>
+
+ <para>
+ 注æï¼å¯¹ JDK 5.0 注解 (å JSR-220)æ¯æçå·¥ä½ä»ç¶å¨è¿è¡ä¸,并æªå®æãæ´å¤ç»è请åé
Hibernate Annotations 模åã
+ </para>
+ </sect2>
+ </sect1>
+ <sect1 id="mapping-generated" revision="1">
+ <title>æ°æ®åºçæå±æ§ï¼Generated Propertiesï¼</title>
+ <para>
+ Generated propertiesæçæ¯å
¶å¼ç±æ°æ®åºçæçå±æ§ãä¸è¬æ¥è¯´ï¼å¦æ对象æä»»ä½å±æ§ç±æ°æ®åºçæå¼ï¼Hibernateåºç¨ç¨åºéè¦è¿è¡<literal>å·æ°(refresh)</literal>ãä½å¦ææå±æ§æ æ为generatedï¼å°±å¯ä»¥è½¬ç±Hibernateæ¥è´è´£è¿ä¸ªå¨ä½ãå®é
ä¸ã对å®ä¹äºgenerated propertiesçå®ä½,æ¯å½Hibernateæ§è¡ä¸æ¡SQL INSERTæè
UPDATEè¯å¥ï¼ä¼ç«å»æ§è¡ä¸æ¡selectæ¥è·å¾çæçå¼ã
+ </para>
+ <para>
+ 被æ æ为generatedçå±æ§è¿å¿
é¡»æ¯ non-insertableå non-updateableçãåªæ<xref linkend="mapping-declaration-version">versions</xref>ï¼<xref linkend="mapping-declaration-timestamp">timestamps</xref>å<xref linkend="mapping-declaration-property">ç®åå±æ§ï¼simple propertiesï¼</xref>å¯ä»¥è¢«æ æ为generatedã
+ </para>
+ <para>
+ <literal>never</literal> (é»è®¤) æ ææ¤å±æ§å¼ä¸æ¯ä»æ°æ®åºä¸çæã
+ </para>
+ <para>
+ <literal>insert</literal> - æ ææ¤å±æ§å¼å¨insertçæ¶åçæï¼ä½æ¯ä¸ä¼å¨éåçupdateæ¶éæ°çæãæ¯å¦è¯´å建æ¥æå°±å½å±äºè¿ç±»ã注æè½ç¶<xref linkend="mapping-declaration-version">version</xref>å<xref linkend="mapping-declaration-timestamp">timestamp</xref>å±æ§å¯ä»¥è¢«æ 注为generatedï¼ä½æ¯ä¸éç¨è¿ä¸ªé项...
+ </para>
+ <para>
+ <literal>always</literal> - æ ææ¤å±æ§å¼å¨insertåupdateæ¶é½ä¼è¢«çæã
+ </para>
+ </sect1>
+
+ <sect1 id="mapping-database-object">
+ <title>è¾
å©æ°æ®åºå¯¹è±¡(Auxiliary Database Objects)</title>
+ <para>
+ Allows CREATE and DROP of arbitrary database objects, in conjunction with
+ Hibernate's schema evolution tools, to provide the ability to fully define
+ a user schema within the Hibernate mapping files. Although designed specifically
+ for creating and dropping things like triggers or stored procedures, really any
+ SQL command that can be run via a <literal>java.sql.Statement.execute()</literal>
+ method is valid here (ALTERs, INSERTS, etc). There are essentially two modes for
+ defining auxiliary database objects...
+ 帮å©CREATEåDROPä»»ææ°æ®åºå¯¹è±¡ï¼ä¸Hibernateçschema交äºå·¥å
·ç»åèµ·æ¥ï¼å¯ä»¥æä¾å¨Hibernateæ å°æ件ä¸å®å
¨å®ä¹ç¨æ·schemaçè½åãè½ç¶è¿æ¯ä¸ºå建åéæ¯trigger(触åå¨ï¼æstored procedure(åå¨è¿ç¨ï¼çç¹å«è®¾è®¡çï¼å®é
ä¸ä»»ä½å¯ä»¥å¨<literal>java.sql.Statement.execute()</literal>æ¹æ³ä¸æ§è¡çSQLå½ä»¤é½å¯ä»¥å¨æ¤ä½¿ç¨ï¼æ¯å¦ALTER, INSERTï¼ççï¼ãæ¬è´¨ä¸æ两ç§æ¨¡å¼æ¥å®ä¹è¾
å©æ°æ®åºå¯¹è±¡...
+ </para>
+ <para>
+ 第ä¸ç§æ¨¡å¼æ¯å¨æ å°æ件ä¸æ¾å¼å£°æCREATEåDROPå½ä»¤ï¼
+ </para>
+ <programlisting><![CDATA[<hibernate-mapping>
+ ...
+ <database-object>
+ <create>CREATE TRIGGER my_trigger ...</create>
+ <drop>DROP TRIGGER my_trigger</drop>
+ </database-object>
+</hibernate-mapping>]]></programlisting>
+ <para>
+ 第äºç§æ¨¡å¼æ¯æä¾ä¸ä¸ªç±»ï¼è¿ä¸ªç±»ç¥éå¦ä½ç»ç»CREATEåDROPå½ä»¤ãè¿ä¸ªç¹å«ç±»å¿
é¡»å®ç°<literal>org.hibernate.mapping.AuxiliaryDatabaseObject</literal>æ¥å£ã
+ </para>
+ <programlisting><![CDATA[<hibernate-mapping>
+ ...
+ <database-object>
+ <definition class="MyTriggerDefinition"/>
+ </database-object>
+</hibernate-mapping>]]></programlisting>
+ <para>
+ è¿æï¼è¿äºæ°æ®åºå¯¹è±¡å¯ä»¥ç¹å«æå®ä¸ºä»
å¨ç¹å®çæ¹è¨ä¸æ使ç¨ã
+ </para>
+ <programlisting><![CDATA[<hibernate-mapping>
+ ...
+ <database-object>
+ <definition class="MyTriggerDefinition"/>
+ <dialect-scope name="org.hibernate.dialect.Oracle9Dialect"/>
+ <dialect-scope name="org.hibernate.dialect.OracleDialect"/>
+ </database-object>
+</hibernate-mapping>]]></programlisting>
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/batch.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/batch.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/batch.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,282 @@
+<chapter id="batch">
+ <title>æ¹éå¤çï¼Batch processingï¼</title>
+ <para>
+ 使ç¨Hibernateå° 100 000 æ¡è®°å½æå
¥å°æ°æ®åºçä¸ä¸ªå¾èªç¶çåæ³å¯è½æ¯è¿æ ·ç
+ </para>
+
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+for ( int i=0; i<100000; i++ ) {
+ Customer customer = new Customer(.....);
+ session.save(customer);
+}
+tx.commit();
+session.close();]]></programlisting>
+
+ <para>
+ è¿æ®µç¨åºå¤§æ¦è¿è¡å° 50 000 æ¡è®°å½å·¦å³ä¼å¤±è´¥å¹¶æåº <literal>å
å溢åºå¼å¸¸ï¼OutOfMemoryExceptionï¼</literal> ã
+ è¿æ¯å 为 Hibernate ææææ°æå
¥ç <literal>客æ·ï¼Customerï¼</literal>å®ä¾å¨ session级å«çç¼ååºè¿è¡äºç¼åçç¼æ
ã
+ </para>
+
+ <para>
+ æ们ä¼å¨æ¬ç« åè¯ä½ å¦ä½é¿å
æ¤ç±»é®é¢ãé¦å
ï¼å¦æä½ è¦æ§è¡æ¹éå¤ç并ä¸æ³è¦è¾¾å°ä¸ä¸ªçæ³çæ§è½ï¼
+ é£ä¹ä½¿ç¨JDBCçæ¹éï¼batchingï¼åè½æ¯è³å
³éè¦ãå°JDBCçæ¹éæåæ°éï¼batch sizeï¼åæ°è®¾ç½®å°ä¸ä¸ªåéå¼
+ ï¼æ¯å¦ï¼10-50ä¹é´ï¼ï¼
+ </para>
+
+<programlisting><![CDATA[hibernate.jdbc.batch_size 20]]></programlisting>
+
+ <para id="disablebatching" revision="1">
+ 注æ,åè¥ä½ 使ç¨äº<literal>identiy</literal>æ è¯ç¬¦çæå¨,Hibernateå¨JDBC级å«éæçå
³éæå
¥è¯å¥çæ¹éæ§è¡ã
+ </para>
+
+ <para>
+ ä½ ä¹å¯è½æ³å¨æ§è¡æ¹éå¤çæ¶å
³éäºçº§ç¼åï¼
+ </para>
+
+<programlisting><![CDATA[hibernate.cache.use_second_level_cache false]]></programlisting>
+ <para>
+ ä½æ¯ï¼è¿ä¸æ¯ç»å¯¹å¿
é¡»çï¼å 为æ们å¯ä»¥æ¾å¼è®¾ç½®<literal>CacheMode</literal>æ¥å
³éä¸äºçº§ç¼åç交äºã
+ </para>
+
+
+ <sect1 id="batch-inserts">
+ <title>æ¹éæå
¥ï¼Batch insertsï¼</title>
+
+ <para>
+ å¦æè¦å°å¾å¤å¯¹è±¡æä¹
åï¼ä½ å¿
é¡»éè¿ç»å¸¸çè°ç¨ <literal>flush()</literal> 以åç¨åè°ç¨
+ <literal>clear()</literal> æ¥æ§å¶ç¬¬ä¸çº§ç¼åç大å°ã
+ </para>
+
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+
+for ( int i=0; i<100000; i++ ) {
+ Customer customer = new Customer(.....);
+ session.save(customer);
+ if ( i % 20 == 0 ) { //20, same as the JDBC batch size //20,ä¸JDBCæ¹é设置ç¸å
+ //flush a batch of inserts and release memory:
+ //å°æ¬æ¹æå
¥ç对象ç«å³åå
¥æ°æ®åºå¹¶éæ¾å
å
+ session.flush();
+ session.clear();
+ }
+}
+
+tx.commit();
+session.close();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="batch-update" >
+ <title>æ¹éæ´æ°ï¼Batch updatesï¼</title>
+
+ <para>
+ æ¤æ¹æ³åæ ·éç¨äºæ£ç´¢åæ´æ°æ°æ®ãæ¤å¤ï¼å¨è¿è¡ä¼è¿åå¾å¤è¡æ°æ®çæ¥è¯¢æ¶ï¼
+ ä½ éè¦ä½¿ç¨ <literal>scroll()</literal> æ¹æ³ä»¥ä¾¿å
åå©ç¨æå¡å¨ç«¯æ¸¸æ æ带æ¥ç好å¤ã
+ </para>
+
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+
+ScrollableResults customers = session.getNamedQuery("GetCustomers")
+ .setCacheMode(CacheMode.IGNORE)
+ .scroll(ScrollMode.FORWARD_ONLY);
+int count=0;
+while ( customers.next() ) {
+ Customer customer = (Customer) customers.get(0);
+ customer.updateStuff(...);
+ if ( ++count % 20 == 0 ) {
+ //flush a batch of updates and release memory:
+ session.flush();
+ session.clear();
+ }
+}
+
+tx.commit();
+session.close();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="batch-statelesssession">
+ <title>StatelessSession (æ ç¶æsession)æ¥å£</title>
+ <para>
+ ä½ä¸ºéæ©ï¼Hibernateæä¾äºåºäºå½ä»¤çAPIï¼å¯ä»¥ç¨detached objectçå½¢å¼ææ°æ®ä»¥æµçæ¹æ³å å
¥å°æ°æ®åºï¼æä»æ°æ®åºè¾åºã<literal>StatelessSession</literal>没ææä¹
åä¸ä¸æï¼ä¹ä¸æä¾å¤å°é«å±ççå½å¨æè¯ä¹ãç¹å«æ¯ï¼æ ç¶æsessionä¸å®ç°ç¬¬ä¸çº§cache,ä¹ä¸å第äºçº§ç¼åï¼æè
æ¥è¯¢ç¼å交äºãå®ä¸å®ç°äºå¡ååï¼ä¹ä¸å®ç°èæ°æ®æ£æ¥ãç¨stateless sessionè¿è¡çæä½çè³ä¸çº§èå°å
³èå®ä¾ãstateless session忽ç¥éåç±»(Collections)ãéè¿stateless sessionè¿è¡çæä½ä¸è§¦åHibernateçäºä»¶æ¨¡ååæ¦æªå¨ãæ ç¶æsession对æ°æ®çæ··æ·ç°è±¡å
ç«ï¼å 为å®æ²¡æ第ä¸çº§ç¼åãæ ç¶æsessionæ¯ä½å±çæ½è±¡ï¼åä½å±JDBCç¸å½æ¥è¿ã
+ </para>
+
+<programlisting><![CDATA[StatelessSession session = sessionFactory.openStatelessSession();
+Transaction tx = session.beginTransaction();
+
+ScrollableResults customers = session.getNamedQuery("GetCustomers")
+ .scroll(ScrollMode.FORWARD_ONLY);
+while ( customers.next() ) {
+ Customer customer = (Customer) customers.get(0);
+ customer.updateStuff(...);
+ session.update(customer);
+}
+
+tx.commit();
+session.close();]]></programlisting>
+
+ <para>
+ 注æå¨ä¸é¢çä¾åä¸ï¼æ¥è¯¢è¿åç<literal>Customer</literal>å®ä¾ç«å³è¢«è±ç®¡(detach)ãå®ä»¬ä¸ä»»ä½æä¹
åä¸ä¸æé½æ²¡æå
³ç³»ã
+ </para>
+
+ <para>
+ <literal>StatelessSession</literal> æ¥å£å®ä¹ç<literal>insert(), update()</literal> å <literal>delete()</literal>æä½æ¯ç´æ¥çæ°æ®åºè¡çº§å«æä½ï¼å
¶ç»ææ¯ç«å»æ§è¡ä¸æ¡<literal>INSERT, UPDATE</literal> æ <literal>DELETE</literal> è¯å¥ãå æ¤ï¼å®ä»¬çè¯ä¹å<literal>Session</literal> æ¥å£å®ä¹ç<literal>save(), saveOrUpdate()</literal> å<literal>delete()</literal> æä½æå¾å¤§çä¸åã
+ </para>
+
+ </sect1>
+
+ <sect1 id="batch-direct" revision="3">
+ <title>DML(æ°æ®æä½è¯è¨)é£æ ¼çæä½(DML-style operations)</title>
+
+ <para>
+ hence manipulating (using the SQL <literal>Data Manipulation Language</literal>
+ (DML) statements: <literal>INSERT</literal>, <literal>UPDATE</literal>, <literal>DELETE</literal>)
+ data directly in the database will not affect in-memory state. However, Hibernate provides methods
+ for bulk SQL-style DML statement execution which are performed through the
+ Hibernate Query Language (<xref linkend="queryhql">HQL</xref>).
+
+
+ å°±åå·²ç»è®¨è®ºçé£æ ·ï¼èªå¨åéæç 对象/å
³ç³» æ å°ï¼object/relational mappingï¼å
³æ³¨äºç®¡ç对象çç¶æã
+ è¿å°±æå³ç对象çç¶æåå¨äºå
åï¼å æ¤ç´æ¥æä½ (ä½¿ç¨ SQL <literal>Data Manipulation Language</literal>(DML,æ°æ®æä½è¯è¨ï¼è¯å¥ ï¼<literal>INSERT</literal> ,<literal>UPDATE</literal> å
+ <literal>DELETE</literal>) æ°æ®åºä¸çæ°æ®å°ä¸ä¼å½±åå
åä¸ç对象ç¶æå对象æ°æ®ã
+ ä¸è¿ï¼Hibernateæä¾éè¿Hibernateæ¥è¯¢è¯è¨ï¼<xref linkend="queryhql">HQL</xref>ï¼æ¥æ§è¡å¤§æ¹
+ éSQLé£æ ¼çDMLè¯å¥çæ¹æ³ã
+ </para>
+
+ <para>
+ <literal>UPDATE</literal> å <literal>DELETE</literal>è¯å¥çè¯æ³ä¸ºï¼
+ <literal>( UPDATE | DELETE ) FROM? EntityName (WHERE where_conditions)?</literal>
+ æå ç¹è¯´æï¼
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ å¨FROMåå¥ï¼from-clauseï¼ä¸ï¼FROMå
³é®åæ¯å¯éç
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ å¨FROMåå¥ï¼from-clauseï¼ä¸åªè½æä¸ä¸ªå®ä½åï¼å®å¯ä»¥æ¯å«åãå¦æå®ä½åæ¯å«åï¼é£ä¹ä»»ä½è¢«å¼ç¨çå±æ§é½å¿
é¡»å ä¸æ¤å«åçåç¼ï¼å¦æä¸æ¯å«åï¼é£ä¹ä»»ä½æåç¼çå±æ§å¼ç¨é½æ¯éæ³çã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ä¸è½å¨å¤§æ¹éHQLè¯å¥ä¸ä½¿ç¨<xref linkend="queryhql-joins-forms">è¿æ¥(join)</xref>ï¼æ¾å¼æè
éå¼çé½ä¸è¡ï¼ãä¸è¿å¨WHEREåå¥ä¸å¯ä»¥ä½¿ç¨åæ¥è¯¢ãå¯ä»¥å¨whereåå¥ä¸ä½¿ç¨åæ¥è¯¢ï¼åæ¥è¯¢æ¬èº«å¯ä»¥å
å«joinã
+
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ æ´ä¸ªWHEREåå¥æ¯å¯éçã
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ 举个ä¾åï¼ä½¿ç¨<literal>Query.executeUpdate()</literal>æ¹æ³æ§è¡ä¸ä¸ªHQL
+ <literal>UPDATE</literal>è¯å¥(ï¼
+ (æ¹æ³å½åæ¯æ¥æºäºJDBC's <literal>PreparedStatement.executeUpdate()</literal>):
+ </para>
+
+ <programlisting><![CDATA[Session session = sessionFactory.openSession();
+ Transaction tx = session.beginTransaction();
+
+ String hqlUpdate = "update Customer c set c.name = :newName where c.name = :oldName";
+ // or String hqlUpdate = "update Customer set name = :newName where name = :oldName";
+ int updatedEntities = s.createQuery( hqlUpdate )
+ .setString( "newName", newName )
+ .setString( "oldName", oldName )
+ .executeUpdate();
+ tx.commit();
+ session.close();]]></programlisting>
+
+ <para>
+ HQL <literal>UPDATE</literal>è¯å¥ï¼é»è®¤ä¸ä¼å½±åæ´æ°å®ä½ç<xref linkend="mapping-declaration-version">version</xref>æè
<xref linkend="mapping-declaration-timestamp">timestamp</xref>å±æ§å¼ãè¿åEJB3è§èæ¯ä¸è´çãä½æ¯ï¼éè¿ä½¿ç¨<literal>versioned update</literal>ï¼ä½ å¯ä»¥å¼ºå¶Hibernateæ£ç¡®çéç½®<literal>version</literal>æè
<literal>timestamp</literal>å±æ§å¼ãè¿éè¿å¨<literal>UPDATE</literal>å
³é®ååé¢å¢å <literal>VERSIONED</literal>å
³é®åæ¥å®ç°çã
+ </para>
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+String hqlVersionedUpdate = "update versioned Customer set name = :newName where name = :oldName";
+int updatedEntities = s.createQuery( hqlUpdate )
+ .setString( "newName", newName )
+ .setString( "oldName", oldName )
+ .executeUpdate();
+tx.commit();
+session.close();]]></programlisting>
+
+ <para>
+ 注æï¼èªå®ä¹ççæ¬ç±»å(<literal>org.hibernate.usertype.UserVersionType</literal>)ä¸å
许å<literal>update versioned</literal>è¯å¥èç¨ã
+ </para>
+
+ <para>
+ æ§è¡ä¸ä¸ªHQL <literal>DELETE</literal>ï¼åæ ·ä½¿ç¨ <literal>Query.executeUpdate()</literal> æ¹æ³:
+ </para>
+
+ <programlisting><![CDATA[Session session = sessionFactory.openSession();
+ Transaction tx = session.beginTransaction();
+
+ String hqlDelete = "delete Customer c where c.name = :oldName";
+ // or String hqlDelete = "delete Customer where name = :oldName";
+ int deletedEntities = s.createQuery( hqlDelete )
+ .setString( "oldName", oldName )
+ .executeUpdate();
+ tx.commit();
+ session.close();]]></programlisting>
+
+ <para>
+ ç±<literal>Query.executeUpdate()</literal>æ¹æ³è¿åç<literal>æ´å</literal>å¼è¡¨æäºåæ¤æä½å½±åçè®°å½æ°éã
+ 注æè¿ä¸ªæ°å¼å¯è½ä¸æ°æ®åºä¸è¢«ï¼æåä¸æ¡SQLè¯å¥ï¼å½±åäºçâè¡âæ°æå
³ï¼ä¹å¯è½æ²¡æãä¸ä¸ªå¤§æ¹éHQLæä½å¯è½å¯¼è´å¤æ¡å®é
çSQLè¯å¥è¢«æ§è¡ï¼
+ 举个ä¾åï¼å¯¹joined-subclassæ å°æ¹å¼çç±»è¿è¡çæ¤ç±»æä½ãè¿ä¸ªè¿åå¼ä»£è¡¨äºå®é
被è¯å¥å½±åäºçè®°å½æ°éãå¨é£ä¸ªjoined-subclassçä¾åä¸ï¼
+ 对ä¸ä¸ªåç±»çå é¤å®é
ä¸å¯è½ä¸ä»
ä»
ä¼å é¤åç±»æ å°å°ç表èä¸ä¼å½±åâæ ¹â表ï¼è¿æå¯è½å½±åä¸ä¹æ继æ¿å
³ç³»çjoined-subclassæ å°æ¹å¼çåç±»ç表ã
+ </para>
+
+ <para>
+ <literal>INSERT</literal>è¯å¥ç伪ç æ¯:
+ <literal>INSERT INTO EntityName properties_list select_statement</literal>.
+ è¦æ³¨æçæ¯:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ åªæ¯æINSERT INTO ... SELECT ...å½¢å¼,ä¸æ¯æINSERT INTO ... VALUES ...å½¢å¼.
+ </para>
+ <para>
+ properties_liståSQL <literal>INSERT</literal>è¯å¥ä¸ç<literal>å段å®ä¹(column speficiation)</literal>类似ã对åä¸ç»§æ¿æ æ å°çå®ä½èè¨ï¼åªæç´æ¥å®ä¹å¨ç»å®ç类级å«çå±æ§æè½ç´æ¥å¨properties_listä¸ä½¿ç¨ãè¶
ç±»çå±æ§ä¸è¢«æ¯æï¼åç±»çå±æ§æ æä¹ãæ¢å¥è¯è¯´ï¼<literal>INSERT</literal>天çä¸æ¯æå¤æã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ select_statementå¯ä»¥æ¯ä»»ä½åæ³çHQLéæ©æ¥è¯¢ï¼ä¸è¿è¦ä¿è¯è¿åç±»åå¿
é¡»åè¦æå
¥çç±»åå®å
¨å¹é
ãç®åï¼è¿ä¸æ£æ¥æ¯å¨æ¥è¯¢ç¼è¯çæ¶åè¿è¡çï¼èä¸æ¯æå®äº¤ç»æ°æ®åºã注æï¼å¨Hibernate<literal>Type</literal>é´å¦æåªæ¯<emphasis>çä»·ï¼equivalentï¼</emphasis>èé<emphasis>ç¸ç(equal)</emphasis>ï¼ä¼å¯¼è´é®é¢ãå®ä¹ä¸º<literal>org.hibernate.type.DateType</literal>å<literal>org.hibernate.type.TimestampType</literal>ç两个å±æ§å¯è½ä¼äº§çç±»åä¸å¹é
é误ï¼è½ç¶æ°æ®åºçº§å¯è½ä¸å åºåæè
å¯ä»¥å¤çè¿ç§è½¬æ¢ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ 对idå±æ§æ¥è¯´,insertè¯å¥ç»ä½ 两个éæ©ãä½ å¯ä»¥æç¡®å°å¨properties_list表ä¸æå®idå±æ§ï¼è¿æ ·å®çå¼æ¯ä»å¯¹åºçselect表达å¼ä¸è·å¾ï¼ï¼æè
å¨properties_listä¸çç¥å®ï¼æ¤æ¶ä½¿ç¨çææï¼ãåä¸ç§éæ©åªæå½ä½¿ç¨å¨æ°æ®åºä¸çæå¼çid产çå¨æ¶æè½ä½¿ç¨ï¼å¦ææ¯âå
åâä¸è®¡ç®çç±»åçæå¨ï¼å¨è§£ææ¶ä¼æåºä¸ä¸ªå¼å¸¸ã注æï¼ä¸ºäºè¯´æè¿ä¸é®é¢ï¼æ°æ®åºäº§çå¼ççæå¨æ¯<literal>org.hibernate.id.SequenceGenerator</literal>ï¼åå®çåç±»ï¼ï¼ä»¥åä»»ä½<literal>org.hibernate.id.PostInsertIdentifierGenerator</literal>æ¥å£çå®ç°ãè¿å¿æå¼å¾æ³¨æçæå¤æ¯<literal>org.hibernate.id.TableHiLoGenerator</literal>ï¼å®ä¸è½å¨æ¤ä½¿ç¨ï¼å 为å®æ²¡æå¾å°å
¶å¼çéå¾ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ 对æ å°ä¸º<literal>version</literal> æ <literal>timestamp</literal>çå±æ§æ¥è¯´ï¼insertè¯å¥ä¹ç»ä½ 两个éæ©ï¼ä½ å¯ä»¥å¨properties_list表ä¸æå®ï¼æ¤æ¶å
¶å¼ä»å¯¹åºçselect表达å¼ä¸è·å¾ï¼ï¼æè
å¨properties_listä¸çç¥å®ï¼æ¤æ¶ï¼ä½¿ç¨å¨<literal>org.hibernate.type.VersionType</literal> ä¸å®ä¹ç<literal>seed value(ç§åå¼)</literal>ï¼ã
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ æ§è¡HQL <literal>INSERT</literal>è¯å¥çä¾åå¦ä¸ï¼
+ </para>
+
+<programlisting><![CDATA[Session session = sessionFactory.openSession();
+Transaction tx = session.beginTransaction();
+
+String hqlInsert = "insert into DelinquentAccount (id, name) select c.id, c.name from Customer c where ...";
+int createdEntities = s.createQuery( hqlInsert )
+ .executeUpdate();
+tx.commit();
+session.close();]]></programlisting>
+
+ </sect1>
+
+</chapter>
\ No newline at end of file
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/best_practices.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/best_practices.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/best_practices.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,175 @@
+<chapter id="best-practices" revision="2">
+ <title>æä½³å®è·µ(Best Practices)</title>
+
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term>设计ç»é¢ç²åº¦çæä¹
类并ä¸ä½¿ç¨<literal><component></literal>æ¥å®ç°æ å°ã</term>
+ <listitem>
+ <para>
+ 使ç¨ä¸ä¸ª<literal>Address</literal>æä¹
ç±»æ¥å°è£
<literal>street</literal>,
+ <literal>suburb</literal>, <literal>state</literal>, <literal>postcode</literal>.
+ è¿å°æå©äºä»£ç éç¨åç®å代ç éæ(refactoring)çå·¥ä½ã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>对æä¹
类声ææ è¯ç¬¦å±æ§( identifier properties)ã</term>
+ <listitem>
+ <para>
+ Hibernateä¸æ è¯ç¬¦å±æ§æ¯å¯éçï¼ä¸è¿æå¾å¤åå æ¥è¯´æä½ åºè¯¥ä½¿ç¨æ è¯ç¬¦å±æ§ãæ们建议æ è¯ç¬¦åºè¯¥æ¯â人é âç(èªå¨çæï¼ä¸æ¶åä¸å¡å«ä¹)ã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>使ç¨èªç¶é®(natural keys)æ è¯</term>
+ <listitem>
+ <para>
+ 对ææçå®ä½é½æ è¯åºèªç¶é®ï¼ç¨<literal><natural-id></literal>è¿è¡æ å°ãå®ç°<literal>equals()</literal>å<literal>hashCode()</literal>ï¼å¨å
¶ä¸ç¨ç»æèªç¶é®çå±æ§è¿è¡æ¯è¾ã
+ </para>
+ </listitem>
+ Y00008051221000980 2.7ï¼89ï¼100ä¸
+ </varlistentry>
+
+ <varlistentry>
+ <term>为æ¯ä¸ªæä¹
ç±»åä¸ä¸ªæ å°æ件</term>
+ <listitem>
+ <para>
+ ä¸è¦æææçæä¹
ç±»æ å°é½åå°ä¸ä¸ªå¤§æ件ä¸ãæ <literal>com.eg.Foo</literal> æ å°å°<literal>com/eg/Foo.hbm.xml</literal>ä¸ï¼ å¨å¢éå¼åç¯å¢ä¸ï¼è¿ä¸ç¹æ¾å¾ç¹å«ææä¹ã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>ææ å°æ件ä½ä¸ºèµæºå è½½</term>
+ <listitem>
+ <para>
+ ææ å°æ件åä»ä»¬çæ å°ç±»æ¾å¨ä¸èµ·è¿è¡é¨ç½²ã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>èèææ¥è¯¢å符串æ¾å¨ç¨åºå¤é¢</term>
+ <listitem>
+ <para>
+ å¦æä½ çæ¥è¯¢ä¸è°ç¨äºéANSIæ åçSQLå½æ°ï¼é£ä¹è¿æ¡å®è·µç»éªå¯¹ä½ éç¨ãææ¥è¯¢å符串æ¾å¨æ å°æ件ä¸å¯ä»¥è®©ç¨åºå
·ææ´å¥½çå¯ç§»æ¤æ§ã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>使ç¨ç»å®åé</term>
+ <listitem>
+ <para>
+ å°±åå¨JDBCç¼ç¨ä¸ä¸æ ·ï¼åºè¯¥æ»æ¯ç¨å ä½ç¬¦"?"æ¥æ¿æ¢é常éå¼ï¼ä¸è¦å¨æ¥è¯¢ä¸ç¨å符串å¼æ¥æé é常éå¼ï¼æ´å¥½çåæ³æ¯å¨æ¥è¯¢ä¸ä½¿ç¨å½ååæ°ã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>ä¸è¦èªå·±æ¥ç®¡çJDBC connections</term>
+ <listitem>
+ <para>
+ Hibernateå
许åºç¨ç¨åºèªå·±æ¥ç®¡çJDBC connectionsï¼ä½æ¯åºè¯¥ä½ä¸ºæå没æåæ³çåæ³ãå¦æä½ ä¸è½ä½¿ç¨Hibernateå
建çconnections providersï¼é£ä¹èèå®ç°èªå·±æ¥å®ç°<literal>org.hibernate.connection.ConnectionProvider</literal>
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>èè使ç¨ç¨æ·èªå®ä¹ç±»å(custom type)</term>
+ <listitem>
+ <para>
+ åè®¾ä½ æä¸ä¸ªJavaç±»åï¼æ¥èªæäºç±»åºï¼éè¦è¢«æä¹
åï¼ä½æ¯è¯¥ç±»æ²¡ææä¾æ å°æä½éè¦çååæ¹æ³ãé£ä¹ä½ åºè¯¥èèå®ç°<literal>org.hibernate.UserType</literal>æ¥å£ãè¿ç§åæ³ä½¿ç¨åºä»£ç åèµ·æ¥æ´å èªå¦ï¼ä¸åéè¦èèç±»ä¸Hibernate typeä¹é´çç¸äºè½¬æ¢ã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>å¨æ§è½ç¶é¢çå°æ¹ä½¿ç¨ç¡¬ç¼ç çJDBC</term>
+ <listitem>
+ <para>
+ In performance-critical areas of the system, some kinds of operations might benefit from
+ direct JDBC. But please, wait until you <emphasis>know</emphasis> something is a bottleneck.
+ And don't assume that direct JDBC is necessarily faster. If you need to use direct JDBC, it might
+ be worth opening a Hibernate <literal>Session</literal> and using that JDBC connection. That
+ way you can still use the same transaction strategy and underlying connection provider.
+
+ å¨ç³»ç»ä¸å¯¹æ§è½è¦æ±å¾ä¸¥æ ¼çä¸äºé¨åï¼æäºæä½ä¹è®¸ç´æ¥ä½¿ç¨JDBCä¼æ´å¥½ãä½æ¯è¯·å
<emphasis>确认</emphasis>è¿çç¡®æ¯ä¸ä¸ªç¶é¢ï¼å¹¶ä¸ä¸è¦æ³å½ç¶è®¤ä¸ºJDBCä¸å®ä¼æ´å¿«ãå¦æç¡®å®éè¦ç´æ¥ä½¿ç¨JDBCï¼é£ä¹æ好æå¼ä¸ä¸ª
+Hibernate <literal>Session</literal> ç¶åä» <literal>Session</literal>è·å¾connectionï¼æç
§è¿ç§åæ³ä½ ä»ç¶å¯ä»¥ä½¿ç¨åæ ·çtransactionçç¥ååºå±çconnection providerã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>ç解<literal>Session</literal>æ¸
æ´ï¼ flushingï¼</term>
+ <listitem>
+ <para>
+ Sessionä¼ä¸æ¶çåæ°æ®åºåæ¥æä¹
åç¶æï¼å¦æè¿ç§æä½è¿è¡çè¿äºé¢ç¹ï¼æ§è½ä¼åå°ä¸å®çå½±åãææ¶åä½ å¯ä»¥éè¿ç¦æ¢èªå¨flushingï¼å°½éæå°åéå¿
è¦çflushingæä½ï¼æè
æ´è¿ä¸æ¥ï¼å¨ä¸ä¸ªç¹å®çtransactionä¸æ¹åæ¥è¯¢åå
¶å®æä½ç顺åºã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>å¨ä¸å±ç»æä¸ï¼èè使ç¨æ管对象ï¼detached objectï¼</term>
+ <listitem>
+ <para>
+ å½ä½¿ç¨ä¸ä¸ªservlet / session bean ç±»åçæ¶æçæ¶å, ä½ å¯ä»¥æå·²å è½½çæä¹
对象å¨session beanå±åservlet / JSP å±ä¹é´æ¥åä¼ éã使ç¨æ°çsessionæ¥ä¸ºæ¯ä¸ªè¯·æ±æå¡ï¼ä½¿ç¨ <literal>Session.merge()</literal> æè
<literal>Session.saveOrUpdate()</literal>æ¥ä¸æ°æ®åºåæ¥ã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>å¨ä¸¤å±ç»æä¸ï¼èè使ç¨é¿æä¹
ä¸ä¸æ(long persistence contexts).</term>
+ <listitem>
+ <para>
+ 为äºå¾å°æä½³çå¯ä¼¸ç¼©æ§ï¼æ°æ®åºäºå¡(Database Transaction)åºè¯¥å°½å¯è½ççãä½æ¯ï¼ç¨åºå¸¸å¸¸éè¦å®ç°é¿æ¶é´è¿è¡ç<emphasis>âåºç¨ç¨åºäºå¡(Application Transaction)â</emphasis>ï¼å
å«ä¸ä¸ªä»ç¨æ·çè§ç¹æ¥ççååæä½ãè¿ä¸ªåºç¨ç¨åºäºå¡å¯è½è·¨è¶å¤æ¬¡ä»ç¨æ·è¯·æ±å°å¾å°åé¦ç循ç¯ãç¨è±ç®¡å¯¹è±¡(ä¸sessionè±ç¦»ç对象)æ¥å®ç°åºç¨ç¨åºäºå¡æ¯å¸¸è§çãæè
ï¼å°¤å
¶å¨ä¸¤å±ç»æä¸ï¼æHibernate Sessionä»JDBCè¿æ¥ä¸è±ç¦»å¼ï¼ä¸æ¬¡éè¦ç¨çæ¶ååè¿æ¥ä¸ãç»ä¸è¦æä¸ä¸ªSessionç¨å¨å¤ä¸ªåºç¨ç¨åºäºå¡(Application Transaction)ä¸ï¼å¦åä½ çæ°æ®å¯è½ä¼è¿æ失æã
+ </para>
+
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>ä¸è¦æå¼å¸¸çæå¯æ¢å¤ç</term>
+ <listitem>
+ <para>
+ è¿ä¸ç¹çè³æ¯âæä½³å®è·µâè¿è¦éè¦ï¼è¿æ¯âå¿
å¤å¸¸è¯âãå½å¼å¸¸åççæ¶åï¼å¿
é¡»è¦åæ» <literal>Transaction</literal> ï¼å
³é<literal>Session</literal>ãå¦æä½ ä¸è¿æ ·åçè¯ï¼Hibernateæ æ³ä¿è¯å
åç¶æ精确çååºæä¹
ç¶æãå°¤å
¶ä¸è¦ä½¿ç¨<literal>Session.load()</literal>æ¥å¤æä¸ä¸ªç»å®æ è¯ç¬¦ç对象å®ä¾å¨æ°æ®åºä¸æ¯å¦åå¨ï¼åºè¯¥ä½¿ç¨<literal>Session.get()</literal>æè
è¿è¡ä¸æ¬¡æ¥è¯¢.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>对äºå
³èä¼å
èèlazy fetching </term>
+ <listitem>
+ <para>
+ è°¨æ
ç使ç¨ä¸»å¨æå(eager fetching)ã对äºå
³èæ¥è¯´ï¼è¥å
¶ç®æ æ¯æ æ³å¨ç¬¬äºçº§ç¼åä¸å®å
¨ç¼åææå®ä¾çç±»ï¼åºè¯¥ä½¿ç¨ä»£ç(proxies)ä¸/æå
·æ延è¿å è½½å±æ§çéå(lazy collections)ãè¥ç®æ æ¯å¯ä»¥è¢«ç¼åçï¼å°¤å
¶æ¯ç¼åçå½ä¸çé常é«çæ
åµä¸ï¼åºè¯¥ä½¿ç¨<literal>lazy="false"</literal>ï¼æç¡®çç¦æ¢æeager fetchingãå¦æé£äºç¹æ®çç¡®å®éå使ç¨join fetch çåºåï¼è¯·å¨æ¥è¯¢ä¸ä½¿ç¨<literal>left join fetch</literal>ã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ 使ç¨<emphasis>open session in view</emphasis>模å¼ï¼æè
æ§è¡ä¸¥æ ¼ç<emphasis>è£
é
æ(assembly phase)</emphasis>çç¥æ¥é¿å
å次æåæ°æ®å¸¦æ¥çé®é¢
+ </term>
+ <listitem>
+ <para>
+
+ Hibernate让å¼åè
们æè±äºç¹çç<emphasis>Data Transfer Objects</emphasis> (DTO)ãå¨ä¼ ç»çEJBç»æä¸ï¼DTOæåéä½ç¨ï¼é¦å
ï¼ä»ä»¬è§£å³äºentity beanæ æ³åºååçé®é¢ï¼å
¶æ¬¡ï¼ä»ä»¬éå«å°å®ä¹äºä¸ä¸ªè£
é
æï¼å¨æ¤æé´ï¼ææå¨viewå±éè¦ç¨å°çæ°æ®ï¼é½è¢«æåãéä¸å°äºDTOä¸ï¼ç¶åæ§å¶æ被è£
å°è¡¨ç¤ºå±ãHibernateç»ç»äºç¬¬ä¸ä¸ªä½ç¨ãç¶èï¼é¤éä½ å好äºå¨æ´ä¸ªæ¸²æè¿ç¨ä¸é½ç»´æ¤ä¸ä¸ªæå¼çæä¹
åä¸ä¸æ(session)çåå¤ï¼ä½ ä»ç¶éè¦ä¸ä¸ªè£
é
æï¼æ³è±¡ä¸ä¸ï¼ä½ çä¸å¡æ¹æ³ä¸ä½ ç表示å±æä¸¥æ ¼çå¥çº¦ï¼æ°æ®æ»æ¯è¢«æ¾ç½®å°æ管对象ä¸ï¼ãè¿å¹¶éæ¯Hibernateçéå¶ï¼è¿æ¯å®ç°å®å
¨çäºå¡åæ°æ®è®¿é®çåºæ¬éæ±ã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>èèæHibernate代ç ä»ä¸å¡é»è¾ä»£ç ä¸æ½è±¡åºæ¥</term>
+ <listitem>
+ <para>
+ æHibernateçæ°æ®åå代ç éèå°æ¥å£(interface)çåé¢ï¼ç»å使ç¨<emphasis>DAO</emphasis>å<emphasis>Thread Local Session</emphasis>模å¼ãéè¿Hibernateç<literal>UserType</literal>ï¼ä½ çè³å¯ä»¥ç¨ç¡¬ç¼ç çJDBCæ¥æä¹
åé£äºæ¬è¯¥è¢«Hibernateæä¹
åçç±»ã (该建议æ´éç¨äºè§æ¨¡è¶³å¤å¤§åºç¨è½¯ä»¶ä¸ï¼å¯¹äºé£äºåªæ5å¼ è¡¨çåºç¨ç¨åºå¹¶ä¸éåã)
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>ä¸è¦ç¨æªå¼çè¿æ¥æ å°</term>
+ <listitem>
+ <para>
+ å¤å¯¹å¤è¿æ¥ç¨å¾å¥½çä¾åå®é
ä¸ç¸å½å°è§ã大å¤æ°æ¶åä½ å¨âè¿æ¥è¡¨âä¸éè¦ä¿åé¢å¤çä¿¡æ¯ãè¿ç§æ
åµä¸ï¼ç¨ä¸¤ä¸ªæåä¸ä»ç±»çä¸å¯¹å¤çè¿æ¥æ¯è¾å¥½ãå®é
ä¸ï¼æ们认为ç»å¤§å¤æ°çè¿æ¥æ¯ä¸å¯¹å¤åå¤å¯¹ä¸çï¼ä½ åºè¯¥è°¨æ
使ç¨å
¶å®è¿æ¥é£æ ¼ï¼ç¨ä¹åé®èªå·±ä¸å¥ï¼æ¯å¦ççå¿
é¡»è¿ä¹åã
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>åç±ååå
³è</term>
+ <listitem>
+ <para>
+ ååå
³èæ´å é¾äºæ¥è¯¢ãå¨å¤§ååºç¨ä¸ï¼å ä¹ææçå
³èå¿
é¡»å¨æ¥è¯¢ä¸å¯ä»¥åå导èªã
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/collection_mapping.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/collection_mapping.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/collection_mapping.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1081 @@
+ <chapter id="collections">
+ <title>éåç±»(Collections)æ å°</title>
+
+
+ <sect1 id="collections-persistent" revision="3">
+ <title>æä¹
åéåç±»(Persistent collections)</title>
+
+ <para id="collections-persistent-translate-comment">
+ (è¯è
注ï¼å¨é
读æ¬ç« çæ¶åï¼ä»¥åæ´ä¸ªæåçé
读è¿ç¨ä¸ï¼æ们é½ä¼é¢ä¸´ä¸ä¸ªåè¯æ¹é¢çé®é¢ï¼é£å°±æ¯âéåâã"Collections"å"Set"å¨ä¸æé对åºé½è¢«ç¿»è¯ä¸ºâéåâï¼ä½æ¯ä»ä»¬çå«ä¹å¾ä¸ä¸æ ·ãCollectionsæ¯ä¸ä¸ªè¶
éï¼Setæ¯å
¶ä¸çä¸ç§ã大é¨åæ
åµä¸ï¼æ¬è¯ç¨¿ä¸æ³æçæªå è±æ注æçâéåâï¼é½åºå½ç解为âCollectionsâãå¨æäºäºè
åæ¶åºç°ï¼å¯è½é ææ··æ·çå°æ¹ï¼æ们ç¨âéåç±»âæ¥ç¹æâCollecionsâ,âéå(Set)âæ¥æ"Set"ï¼ä¸è¬é½ä¼å¨åé¢çæ¬å·ä¸ç»åºè±æãå¸æ大家å¨é
读æ¶èç³»ä¸ä¸æç解ï¼ä¸è¦é æ误解ã
+ ä¸æ¤åæ¶ï¼âå
ç´ âä¸è¯å¯¹åºçè±æâelementâï¼ä¹æ两个ä¸åçå«ä¹ãå
¶ä¸ä¸ºéåçå
ç´ ï¼æ¯å
åä¸çä¸ä¸ªåéï¼å¦ä¸å«ä¹åæ¯XMLææ¡£ä¸çä¸ä¸ªæ ç¾æ代表çå
ç´ ãä¹è¯·æ³¨æåºå«ã
+ æ¬ç« ä¸,ç¹å«æ¯ååé¨åæ¯éè¦åå¤é
读æè½ç解æ¸
æ¥çãå¦æéå°ä»»ä½çé®,请记ä½,è±æçæ¬çreferenceæ¯æä¸æ åçåèèµæã)
+ </para>
+
+ <para>
+ Hibernateè¦æ±æä¹
åéåå¼å段å¿
须声æ为æ¥å£ï¼æ¯å¦ï¼
+ </para>
+
+ <programlisting><![CDATA[public class Product {
+ private String serialNumber;
+ private Set parts = new HashSet();
+
+ public Set getParts() { return parts; }
+ void setParts(Set parts) { this.parts = parts; }
+ public String getSerialNumber() { return serialNumber; }
+ void setSerialNumber(String sn) { serialNumber = sn; }
+}]]></programlisting>
+
+ <para>
+ å®é
çæ¥å£å¯è½æ¯<literal>java.util.Set</literal>,
+ <literal>java.util.Collection</literal>, <literal>java.util.List</literal>,
+ <literal>java.util.Map</literal>, <literal>java.util.SortedSet</literal>,
+ <literal>java.util.SortedMap</literal> æè
...ä»»ä½ä½ å欢çç±»åï¼("ä»»ä½ä½ å欢çç±»å" ä»£è¡¨ä½ éè¦ç¼å <literal>org.hibernate.usertype.UserCollectionType</literal>çå®ç°.)
+ </para>
+
+ <para>
+ 注ææ们æ¯å¦ä½ç¨ä¸ä¸ª<literal>HashSet</literal>å®ä¾æ¥åå§åå®ä¾åéç.è¿æ¯ç¨äºåå§åæ°å建(å°æªæä¹
å)çç±»å®ä¾ä¸éåå¼å±æ§çæä½³æ¹æ³ãå½ä½ æä¹
åè¿ä¸ªå®ä¾æ¶ââæ¯å¦éè¿è°ç¨<literal>persist()</literal>ââHibernate ä¼èªå¨æ<literal>HashSet</literal>æ¿æ¢ä¸ºHibernateèªå·±ç<literal>Set</literal>å®ç°ãè§å¯ä¸é¢çé误ï¼
+ </para>
+
+
+ <programlisting><![CDATA[Cat cat = new DomesticCat();
+Cat kitten = new DomesticCat();
+....
+Set kittens = new HashSet();
+kittens.add(kitten);
+cat.setKittens(kittens);
+session.persist(cat);
+kittens = cat.getKittens(); //Okay, kittens collection is a Set
+(HashSet) cat.getKittens(); //Error!]]></programlisting>
+
+ <para>
+ æ ¹æ®ä¸åçæ¥å£ç±»åï¼è¢«Hibernate注å°çæä¹
åéåç±»ç表ç°ç±»ä¼¼<literal>HashMap</literal>, <literal>HashSet</literal>,
+ <literal>TreeMap</literal>, <literal>TreeSet</literal> or
+ <literal>ArrayList</literal>ã
+ </para>
+
+ <para>
+ éåç±»å®ä¾å
·æå¼ç±»åçé常è¡ä¸ºãå½è¢«æä¹
å对象å¼ç¨åï¼ä»ä»¬ä¼èªå¨è¢«æä¹
åï¼å½ä¸å被å¼ç¨åï¼èªå¨è¢«å é¤ãåè¥å®ä¾è¢«ä»ä¸ä¸ªæä¹
åå¯¹è±¡ä¼ éå°å¦ä¸ä¸ªï¼å®çå
ç´ å¯è½ä»ä¸ä¸ªè¡¨è½¬ç§»å°å¦ä¸ä¸ªè¡¨ã两个å®ä½ä¸è½å
±äº«åä¸ä¸ªéåç±»å®ä¾çå¼ç¨ãå 为åºå±å
³ç³»æ°æ®åºæ¨¡åçåå ï¼éåå¼å±æ§æ æ³æ¯æ空å¼è¯ä¹ï¼Hibernate对空çéåå¼ç¨å空éåä¸å åºå«ã
+ </para>
+
+ <para>
+ ä½ ä¸éè¦è¿å¤ç为æ¤æ
å¿ãå°±å¦åä½ å¹³æ¶ä½¿ç¨æ®éçJavaéåç±»ä¸æ ·æ¥ä½¿ç¨æä¹
åéåç±»ãåªæ¯è¦ç¡®è®¤ä½ ç解äºååå
³èçè¯ä¹ï¼åæ讨论ï¼ã
+ </para>
+
+ </sect1>
+
+ <sect1 id="collections-mapping" revision="4">
+ <title>éåæ å°ï¼ Collection mappings ï¼</title>
+
+ <para>
+ ç¨äºæ å°éåç±»çHibernateæ å°å
ç´ åå³äºæ¥å£çç±»åãæ¯å¦ï¼ <literal><set></literal> å
ç´ ç¨æ¥æ å°<literal>Set</literal>ç±»åçå±æ§ã
+ </para>
+
+ <programlisting><![CDATA[<class name="Product">
+ <id name="serialNumber" column="productSerialNumber"/>
+ <set name="parts">
+ <key column="productSerialNumber" not-null="true"/>
+ <one-to-many class="Part"/>
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ é¤äº<literal><set></literal>,è¿æ<literal><list></literal>, <literal><map></literal>, <literal><bag></literal>, <literal><array></literal> å
+ <literal><primitive-array></literal> æ å°å
ç´ ã<literal><map></literal>å
·æ代表æ§ï¼
+ </para>
+
+
+ <programlistingco>
+ <areaspec>
+ <area id="mappingcollection1" coords="2 65"/>
+ <area id="mappingcollection2" coords="3 65"/>
+ <area id="mappingcollection3" coords="4 65"/>
+ <area id="mappingcollection4" coords="5 65"/>
+ <area id="mappingcollection5" coords="6 65"/>
+ <area id="mappingcollection6" coords="7 65"/>
+ <area id="mappingcollection7" coords="8 65"/>
+ <area id="mappingcollection8" coords="9 65"/>
+ <area id="mappingcollection9" coords="10 65"/>
+ <area id="mappingcollection10" coords="11 65"/>
+ <area id="mappingcollection11" coords="12 65"/>
+ <area id="mappingcollection12" coords="13 65"/>
+ <area id="mappingcollection13" coords="14 65"/>
+ <area id="mappingcollection14" coords="15 65"/>
+
+ </areaspec>
+ <programlisting><![CDATA[<map
+ name="propertyName"
+ table="table_name"
+ schema="schema_name"
+ lazy="true|extra|false"
+ inverse="true|false"
+ cascade="all|none|save-update|delete|all-delete-orphan|delete-orphan"
+ sort="unsorted|natural|comparatorClass"
+ order-by="column_name asc|desc"
+ where="arbitrary sql where condition"
+ fetch="join|select|subselect"
+ batch-size="N"
+ access="field|property|ClassName"
+ optimistic-lock="true|false"
+ mutable="true|false"
+ node="element-name|."
+ embed-xml="true|false"
+>
+
+ <key .... />
+ <map-key .... />
+ <element .... />
+</map>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="mappingcollection1">
+ <para>
+ <literal>name</literal> éåå±æ§çå称
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection2">
+ <para>
+ <literal>table</literal> ï¼å¯éââé»è®¤ä¸ºå±æ§çå称ï¼è¿ä¸ªéå表çå称(ä¸è½å¨ä¸å¯¹å¤çå
³èå
³ç³»ä¸ä½¿ç¨)
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection3">
+ <para>
+ <literal>schema</literal> (å¯é) 表çschemaçå称, ä»å°è¦çå¨æ ¹å
ç´ ä¸å®ä¹çschema
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection4">
+ <para>
+ <literal>lazy</literal> (å¯é--é»è®¤ä¸ºtrue) å¯ä»¥ç¨æ¥å
³é延è¿å è½½(false)ï¼æå®ä¸ç´ä½¿ç¨é¢å
æå,æè
æå¼"extra-lazy" æåï¼æ¤æ¶å¤§å¤æ°æä½ä¸ä¼åå§åéåç±»(éç¨äºé常大çéå)
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection5">
+ <para>
+ <literal>inverse</literal> (å¯éââé»è®¤ä¸º<literal>false</literal>)
+ æ è®°è¿ä¸ªéåä½ä¸ºååå
³èå
³ç³»ä¸çæ¹åä¸ç«¯ã
+
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection6">
+ <para>
+ <literal>cascade</literal> (å¯éââé»è®¤ä¸º<literal>none</literal>)
+ 让æä½çº§èå°åå®ä½
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection7">
+ <para>
+ <literal>sort</literal>(å¯é)æå®éåçæåºé¡ºåº, å
¶å¯ä»¥ä¸ºèªç¶ç(<literal>natural</literal>)æè
ç»å®ä¸ä¸ªç¨æ¥æ¯è¾çç±»ã
+
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection8">
+ <para>
+ <literal>order-by</literal> (å¯é, ä»
ç¨äºjdk1.4) æå®è¡¨çå段(ä¸ä¸ªæå 个)åå ä¸ascæè
desc(å¯é), å®ä¹Map,SetåBagçè¿ä»£é¡ºåº
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection9">
+ <para>
+ <literal>where</literal> (å¯é) æå®ä»»æçSQL whereæ¡ä»¶, 该æ¡ä»¶å°å¨éæ°è½½å
¥æè
å é¤è¿ä¸ªéåæ¶ä½¿ç¨(å½éåä¸çæ°æ®ä»
ä»
æ¯ææå¯ç¨æ°æ®çä¸ä¸ªåéæ¶è¿ä¸ªæ¡ä»¶é常æç¨)
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection10">
+ <para>
+ <literal>fetch</literal> (å¯é, é»è®¤ä¸º<literal>select</literal>) ç¨äºå¨å¤è¿æ¥æåãéè¿åç»selectæååéè¿åç»subselectæåä¹é´éæ©ã
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection11">
+ <para>
+ <literal>batch-size</literal> (å¯é, é»è®¤ä¸º<literal>1</literal>) æå®éè¿å»¶è¿å è½½åå¾éåå®ä¾çæ¹å¤çå大å°ï¼"batch size"ï¼ã
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection12">
+ <para>
+ <literal>access</literal>(å¯é-é»è®¤ä¸ºå±æ§property):Hibernateåå¾éåå±æ§å¼æ¶ä½¿ç¨ççç¥
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection13">
+ <para>
+ <literal>ä¹è§é</literal> (å¯é - é»è®¤ä¸º <literal>true</literal>):
+ 对éåçç¶æçæ¹åä¼æ¯å¦å¯¼è´å
¶æå±çå®ä½ççæ¬å¢é¿ã (对ä¸å¯¹å¤å
³èæ¥è¯´ï¼å
³éè¿ä¸ªå±æ§å¸¸å¸¸æ¯æçç)
+ </para>
+ </callout>
+ <callout arearefs="mappingcollection14">
+ <para>
+ <literal>mutable(å¯åï¼</literal> (å¯é - é»è®¤ä¸º<literal>true</literal>):
+ è¥å¼ä¸º<literal>false</literal>,表æéåä¸çå
ç´ ä¸ä¼æ¹åï¼å¨æäºæ
åµä¸å¯ä»¥è¿è¡ä¸äºå°çæ§è½ä¼åï¼ã
+ </para>
+ </callout>
+
+ </calloutlist>
+ </programlistingco>
+
+ <sect2 id="collections-foreignkeys" >
+ <title>éåå¤é®(Collection foreign keys)</title>
+
+ <para>
+ éåå®ä¾å¨æ°æ®åºä¸ä¾é ææéåçå®ä½çå¤é®å 以辨å«ãæ¤å¤é®ä½ä¸º<emphasis>éåå
³é®å段ï¼collection key columnï¼</emphasis>ï¼æå¤ä¸ªå段ï¼å 以å¼ç¨ãéåå
³é®å段éè¿<literal><key></literal> å
ç´ æ å°ã
+ </para>
+
+ <para>
+ å¨å¤é®å段ä¸å¯è½å
·æé空约æã对äºå¤§å¤æ°éåæ¥è¯´ï¼è¿æ¯éå«çã对ååä¸å¯¹å¤å
³èæ¥è¯´ï¼å¤é®å段é»è®¤æ¯å¯ä»¥ä¸ºç©ºçï¼å æ¤ä½ å¯è½éè¦ææ <literal>not-null="true"</literal>ã
+ </para>
+
+ <programlisting><![CDATA[<key column="productSerialNumber" not-null="true"/>]]></programlisting>
+
+ <para>
+ å¤é®çº¦æå¯ä»¥ä½¿ç¨<literal>ON DELETE CASCADE</literal>ã
+ </para>
+
+ <programlisting><![CDATA[<key column="productSerialNumber" on-delete="cascade"/>]]></programlisting>
+
+ <para>
+ 对<literal><key></literal> å
ç´ çå®æ´å®ä¹ï¼è¯·åé
åé¢çç« èã
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-elements" >
+ <title>éåå
ç´ ï¼Collection elementsï¼</title>
+
+ <para>
+ éåå ä¹å¯ä»¥å
å«ä»»ä½å
¶ä»çHibernateç±»åï¼å
æ¬ææçåºæ¬ç±»åãèªå®ä¹ç±»åãç»ä»¶ï¼å½ç¶è¿æ对å
¶ä»å®ä½çå¼ç¨ãåå¨ä¸ä¸ªéè¦çåºå«ï¼ä½äºéåä¸ç对象å¯è½æ¯æ ¹æ®âå¼âè¯ä¹æ¥æä½ï¼å
¶å£°æå¨æå®å
¨ä¾èµäºéåææè
ï¼ï¼æè
å®å¯è½æ¯æåå¦ä¸ä¸ªå®ä½çå¼ç¨ï¼å
·æå
¶èªå·±ççå½å¨æãå¨åè
çæ
åµä¸ï¼è¢«ä½ä¸ºéåææçç¶æèèçï¼åªæ两个对象ä¹é´çâè¿æ¥âã
+ </para>
+
+
+ <para>
+ 被å
容çç±»å被称为<emphasis>éåå
ç´ ç±»åï¼collection element typeï¼</emphasis>ãéåå
ç´ éè¿<literal><element></literal>æ<literal><composite-element></literal>æ å°ï¼æå¨å
¶æ¯å®ä½å¼ç¨çæ¶åï¼éè¿<literal><one-to-many></literal> æ<literal><many-to-many></literal>æ å°ãå两ç§ç¨äºä½¿ç¨å¼è¯ä¹æ å°å
ç´ ï¼å两ç§ç¨äºæ å°å®ä½å
³èã
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-indexed">
+ <title>ç´¢å¼éåç±»(Indexed collections)</title>
+
+
+ <para>
+ ææçéåæ å°ï¼é¤äºsetåbagè¯ä¹ç以å¤ï¼é½éè¦æå®ä¸ä¸ªéå表ç<emphasis>ç´¢å¼å段(index column)</emphasis>ââç¨äºå¯¹åºå°æ°ç»ç´¢å¼ï¼æè
<literal>List</literal>çç´¢å¼ï¼æè
<literal>Map</literal>çå
³é®åãéè¿<literal><map-key></literal>,<literal>Map</literal> çç´¢å¼å¯ä»¥æ¯ä»»ä½åºç¡ç±»åï¼è¥éè¿<literal><map-key-many-to-many></literal>ï¼å®ä¹å¯ä»¥æ¯ä¸ä¸ªå®ä½å¼ç¨ï¼è¥éè¿<literal><composite-map-key></literal>ï¼å®è¿å¯ä»¥æ¯ä¸ä¸ªç»åç±»åãæ°ç»æå表çç´¢å¼å¿
é¡»æ¯<literal>integer</literal>ç±»åï¼å¹¶ä¸ä½¿ç¨ <literal><list-index></literal>å
ç´ å®ä¹æ å°ã被æ å°çå段å
å«æ顺åºæåçæ´æ°ï¼é»è®¤ä»0å¼å§ï¼ã
+ </para>
+
+
+ <programlistingco>
+ <areaspec>
+ <area id="mapkey1" coords="2 45"/>
+ <area id="mapkey2" coords="3 45"/>
+ <area id="mapkey3" coords="4 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<map-key
+ column="column_name"
+ formula="any SQL expression"
+ type="type_name"
+ node="@attribute-name"
+ length="N"/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="mapkey1">
+ <para>
+ <literal>column</literal>(å¯é):ä¿åéåç´¢å¼å¼çå段åã
+ </para>
+ </callout>
+ <callout arearefs="mapkey2">
+ <para>
+ <literal>formula</literal> (å¯é): ç¨äºè®¡ç®mapå
³é®åçSQLå
¬å¼
+ </para>
+ </callout>
+
+ <callout arearefs="mapkey3">
+ <para>
+ <literal>type</literal> (å¿
é¡»):æ å°é®(map key)çç±»åã
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <programlistingco>
+ <areaspec>
+ <area id="indexmanytomany1" coords="2 45"/>
+ <area id="indexmanytomany2" coords="3 45"/>
+ <area id="indexmanytomany3" coords="3 45"/>
+ </areaspec>
+ <programlisting><![CDATA[<map-key-many-to-many
+ column="column_name"
+ formula="any SQL expression"
+ class="ClassName"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="indexmanytomany1">
+ <para>
+ <literal>column</literal>(å¯é):éåç´¢å¼å¼ä¸å¤é®å段çå称
+ </para>
+ </callout>
+ <callout arearefs="indexmanytomany2">
+ <para>
+ <literal>formula</literal> (å¯é): ç¨äºè®¡ç®mapå
³é®åçå¤é®çSQLå
¬å¼
+ </para>
+ </callout>
+ <callout arearefs="indexmanytomany3">
+ <para>
+ <literal>class</literal> (å¿
é):æ å°çé®(map key)使ç¨çå®ä½ç±»ã
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ åè¥ä½ ç表没æä¸ä¸ªç´¢å¼å段,å½ä½ ä»ç¶å¸æ使ç¨<literal>List</literal>ä½ä¸ºå±æ§ç±»å,ä½ åºè¯¥ææ¤å±æ§æ å°ä¸ºHibernate <emphasis><bag></emphasis>ãä»æ°æ®åºä¸è·åçæ¶åï¼bagä¸ç»´æ¤å
¶é¡ºåºï¼ä½ä¹å¯éæ©æ§çè¿è¡æåºã
+ </para>
+
+ </sect2>
+
+
+ <para>
+ ä»éåç±»å¯ä»¥äº§çå¾å¤§ä¸é¨åæ å°ï¼è¦çäºå¾å¤å¸¸è§çå
³ç³»æ¨¡åãæä»¬å»ºè®®ä½ è¯éªschemaçæå·¥å
·ï¼æ¥ä½ä¼ä¸ä¸ä¸åçæ å°å£°ææ¯å¦ä½è¢«ç¿»è¯ä¸ºæ°æ®åºè¡¨çã
+ </para>
+
+ <sect2 id="collections-ofvalues" revision="2">
+ <title>å¼éåäºå¤å¯¹å¤å
³è(Collections of values and many-to-many associations)</title>
+
+
+ <para>
+ ä»»ä½å¼éåæè
å¤å¯¹å¤å
³èéè¦ä¸ç¨çå
·æä¸ä¸ªæå¤ä¸ªå¤é®å段ç<emphasis>collection table</emphasis>ãä¸ä¸ªæå¤ä¸ª<emphasis>collection element column</emphasis>ï¼ä»¥åè¿å¯è½æä¸ä¸ªæå¤ä¸ªç´¢å¼å段ã
+ </para>
+
+
+ <para>
+ 对äºä¸ä¸ªå¼éå, æ们使ç¨<literal><element></literal>æ ç¾ã
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="element1b" coords="2 50"/>
+ <area id="element2b" coords="3 50"/>
+ <area id="element3b" coords="4 50"/>
+ </areaspec>
+ <programlisting><![CDATA[<element
+ column="column_name"
+ formula="any SQL expression"
+ type="typename"
+ length="L"
+ precision="P"
+ scale="S"
+ not-null="true|false"
+ unique="true|false"
+ node="element-name"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="element1b">
+ <para>
+ <literal>column</literal>(å¯é):ä¿åéåå
ç´ å¼çå段åã
+ </para>
+ </callout>
+ <callout arearefs="element2b">
+ <para>
+ <literal>formula</literal> (å¯é): ç¨äºè®¡ç®å
ç´ çSQLå
¬å¼
+ </para>
+ </callout>
+ <callout arearefs="element3b">
+ <para>
+ <literal>type</literal> (å¿
é):éåå
ç´ çç±»å
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ <emphasis>å¤å¯¹å¤å
³è(many-to-many association)</emphasis> 使ç¨
+ <literal><many-to-many></literal>å
ç´ å®ä¹.
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="manytomany1" coords="2 60"/>
+ <area id="manytomany2" coords="3 60"/>
+ <area id="manytomany3" coords="4 60"/>
+ <area id="manytomany4" coords="5 60"/>
+ <area id="manytomany5" coords="6 60"/>
+ <area id="manytomany6" coords="7 60"/>
+ <area id="manytomany7" coords="8 60"/>
+ <area id="manytomany8" coords="9 60"/>
+ </areaspec>
+ <programlisting><![CDATA[<many-to-many
+ column="column_name"
+ formula="any SQL expression"
+ class="ClassName"
+ fetch="select|join"
+ unique="true|false"
+ not-found="ignore|exception"
+ entity-name="EntityName"
+ property-ref="propertyNameFromAssociatedClass"
+ node="element-name"
+ embed-xml="true|false"
+ />]]></programlisting>
+ <calloutlist>
+ <callout arearefs="manytomany1">
+ <para>
+ <literal>column</literal>(å¯é): è¿ä¸ªå
ç´ çå¤é®å
³é®å段å
+ </para>
+ </callout>
+ <callout arearefs="manytomany2">
+ <para>
+ <literal>formula</literal> (å¯é): ç¨äºè®¡ç®å
ç´ å¤é®å¼çSQLå
¬å¼.
+ </para>
+ </callout>
+
+ <callout arearefs="manytomany3">
+ <para>
+ <literal>class</literal> (å¿
é): å
³èç±»çå称
+ </para>
+ </callout>
+ <callout arearefs="manytomany3">
+ <para>
+ <literal>outer-join</literal> (å¯é - é»è®¤ä¸º<literal>auto</literal>):
+ å¨Hibernateç³»ç»åæ°ä¸<literal>hibernate.use_outer_join</literal>被æå¼çæ
åµä¸,该åæ°ç¨æ¥å
许使ç¨outer joinæ¥è½½å
¥æ¤éåçæ°æ®ã
+ </para>
+ </callout>
+ <callout arearefs="manytomany4">
+ <para>
+ 为æ¤å
³èæå¼å¤è¿æ¥æåæè
åç»selectæåãè¿æ¯ç¹æ®æ
åµï¼å¯¹äºä¸ä¸ªå®ä½åå
¶æåå
¶ä»å®ä½çå¤å¯¹å¤å
³èè¿å
¨é¢å
æåï¼ä½¿ç¨ä¸æ¡åç¬ç<literal>SELECT</literal>)ï¼ä½ ä¸ä»
éè¦å¯¹éåèªèº«æå¼<literal>join</literal>ï¼ä¹éè¦å¯¹<literal><many-to-many></literal>è¿ä¸ªå
åµå
ç´ æå¼æ¤å±æ§ã
+ </para>
+ </callout>
+ <callout arearefs="manytomany5">
+ <para>
+ 对å¤é®å段å
许DDLçæçæ¶åçæä¸ä¸ªæä¸çº¦æãè¿ä½¿å
³èåæäºä¸ä¸ªé«æçä¸å¯¹å¤å
³èãï¼æ¤å¥åçï¼åæ为This makes the association multiplicity effectively one to many.)
+ </para>
+ </callout>
+ <callout arearefs="manytomany6">
+ <para>
+ <literal>not-found</literal> (å¯é - é»è®¤ä¸º <literal>exception</literal>): ææå¼ç¨çå¤é®ä¸ç¼ºå°æäºè¡è¯¥å¦ä½å¤çï¼
+ <literal>ignore</literal> ä¼æ缺失çè¡ä½ä¸ºä¸ä¸ªç©ºå¼ç¨å¤çã
+ </para>
+
+ </callout>
+ <callout arearefs="manytomany7">
+ <para>
+ <literal>entity-name</literal> (å¯é): 被å
³èçç±»çå®ä½åï¼ä½ä¸º<literal>class</literal>çæ¿ä»£ã
+ </para>
+ </callout>
+ <callout arearefs="manytomany8">
+ <para>
+ <literal>property-ref</literal>: (å¯é) 被å
³èå°æ¤å¤é®(foreign key)çç±»ä¸ç对åºå±æ§çååãè¥æªæå®ï¼ä½¿ç¨è¢«å
³èç±»ç主é®ã
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ ä¾åï¼é¦å
, ä¸ç»å符串ï¼
+ </para>
+
+ <programlisting><![CDATA[<set name="names" table="NAMES">
+ <key column="GROUPID"/>
+ <element column="NAME" type="string"/>
+</set>]]></programlisting>
+
+ <para>
+ å
å«ä¸ç»æ´æ°çbag(è¿è®¾ç½®äº<literal>order-by</literal>åæ°æå®äºè¿ä»£ç顺åº)ï¼
+ </para>
+
+ <programlisting><![CDATA[<bag name="sizes"
+ table="item_sizes"
+ order-by="size asc">
+ <key column="item_id"/>
+ <element column="size" type="integer"/>
+</bag>]]></programlisting>
+
+ <para>
+ ä¸ä¸ªå®ä½æ°ç»,å¨è¿ä¸ªæ¡ä¾ä¸æ¯ä¸ä¸ªå¤å¯¹å¤çå
³è(注æè¿éçå®ä½æ¯èªå¨ç®¡ççå½å¨æç对象ï¼lifecycle objectsï¼,<literal>cascade="all"</literal>):
+ </para>
+
+
+ <programlisting><![CDATA[<array name="addresses"
+ table="PersonAddress"
+ cascade="persist">
+ <key column="personId"/>
+ <list-index column="sortOrder"/>
+ <many-to-many column="addressId" class="Address"/>
+</array>]]></programlisting>
+
+ <para>
+ ä¸ä¸ªmap,éè¿å符串çç´¢å¼æ¥æææ¥æï¼
+ </para>
+
+
+ <programlisting><![CDATA[<map name="holidays"
+ table="holidays"
+ schema="dbo"
+ order-by="hol_name asc">
+ <key column="id"/>
+ <map-key column="hol_name" type="string"/>
+ <element column="hol_date" type="date"/>
+</map>]]></programlisting>
+
+ <para>
+ ä¸ä¸ªç»ä»¶çå表ï¼ï¼ä¸ä¸ç« 讨论ï¼
+ </para>
+
+ <programlisting><![CDATA[<list name="carComponents"
+ table="CarComponents">
+ <key column="carId"/>
+ <list-index column="sortOrder"/>
+ <composite-element class="CarComponent">
+ <property name="price"/>
+ <property name="type"/>
+ <property name="serialNumber" column="serialNum"/>
+ </composite-element>
+</list>]]></programlisting>
+
+
+ </sect2>
+
+
+ <sect2 id="collections-onetomany">
+ <title>ä¸å¯¹å¤å
³èï¼One-to-many Associationsï¼</title>
+
+
+ <para>
+ <emphasis>ä¸å¯¹å¤å
³è</emphasis><emphasis>éè¿å¤é®</emphasis>è¿æ¥ä¸¤ä¸ªç±»å¯¹åºç表,è没æä¸é´éå表ã è¿ä¸ªå
³ç³»æ¨¡å失å»äºä¸äºJavaéåçè¯ä¹:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ ä¸ä¸ªè¢«å
å«çå®ä½çå®ä¾åªè½è¢«å
å«å¨ä¸ä¸ªéåçå®ä¾ä¸
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ä¸ä¸ªè¢«å
å«çå®ä½çå®ä¾åªè½å¯¹åºäºéåç´¢å¼çä¸ä¸ªå¼ä¸
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+ ä¸ä¸ªä»<literal>Product</literal>å°<literal>Part</literal>çå
³èéè¦å
³é®åå段,å¯è½è¿æä¸ä¸ªç´¢å¼å段æå<literal>Part</literal>æ对åºç表ã <literal><one-to-many></literal>æ è®°ææäºä¸ä¸ªä¸å¯¹å¤çå
³èã
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="onetomany1" coords="2 60"/>
+ <area id="onetomany2" coords="3 60"/>
+ <area id="onetomany3" coords="4 60"/>
+ </areaspec>
+ <programlisting><![CDATA[<one-to-many
+ class="ClassName"
+ not-found="ignore|exception"
+ entity-name="EntityName"
+ node="element-name"
+ embed-xml="true|false"
+ />]]></programlisting>
+ <calloutlist>
+ <callout arearefs="onetomany1">
+ <para>
+ <literal>class</literal>(å¿
é¡»):被å
³èç±»çå称ã
+ </para>
+ </callout>
+ <callout arearefs="onetomany2">
+ <para>
+ <literal>not-found</literal> (å¯é - é»è®¤ä¸º<literal>exception</literal>):
+ ææè¥ç¼åçæ 示å¼å
³èçè¡ç¼ºå¤±,该å¦ä½å¤ç:
+ <literal>ignore</literal> ä¼æ缺失çè¡ä½ä¸ºä¸ä¸ªç©ºå
³èå¤çã
+ </para>
+ </callout>
+ <callout arearefs="onetomany3">
+ <para>
+ <literal>entity-name</literal> (å¯é): 被å
³èçç±»çå®ä½åï¼ä½ä¸º<literal>class</literal>çæ¿ä»£ã
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+
+ <para>
+ ä¾å
+ </para>
+
+ <programlisting><![CDATA[<set name="bars">
+ <key column="foo_id"/>
+ <one-to-many class="org.hibernate.Bar"/>
+</set>]]></programlisting>
+
+ <para>
+ 注æ:<literal><one-to-many></literal>å
ç´ ä¸éè¦å®ä¹ä»»ä½å段ã ä¹ä¸éè¦æå®è¡¨åã
+
+ </para>
+
+ <para>
+ <emphasis>éè¦æ示</emphasis>:å¦æ<literal>ä¸å¯¹å¤</literal>å
³èä¸çå¤é®å段å®ä¹æ<literal>NOT NULL</literal>,ä½ å¿
é¡»æ<literal><key></literal>æ å°å£°æ为<literal>not-null="true"</literal>,æè
使ç¨<emphasis>ååå
³è</emphasis>ï¼å¹¶ä¸æ æ<literal>inverse="true"</literal>ãåé
æ¬ç« åé¢å
³äºååå
³èç讨论ã
+ </para>
+
+ <para>
+ ä¸é¢çä¾åå±ç¤ºä¸ä¸ª<literal>Part</literal>å®ä½çmap,ænameä½ä¸ºå
³é®åã( <literal>partName</literal> æ¯<literal>Part</literal>çæä¹
åå±æ§)ã注æå
¶ä¸çåºäºå
¬å¼çç´¢å¼çç¨æ³ã
+ </para>
+
+ <programlisting><![CDATA[<map name="parts"
+ cascade="all">
+ <key column="productId" not-null="true"/>
+ <map-key formula="partName"/>
+ <one-to-many class="Part"/>
+</map>]]></programlisting>
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="collections-advancedmappings">
+ <title>é«çº§éåæ å°ï¼Advanced collection mappingsï¼</title>
+
+ <sect2 id="collections-sorted" revision="2">
+ <title>æåºéåï¼Sorted collectionsï¼</title>
+
+ <para>
+ Hibernateæ¯æå®ç°<literal>java.util.SortedMap</literal>å<literal>java.util.SortedSet</literal>çéåã
+ä½ å¿
é¡»å¨æ å°æ件ä¸æå®ä¸ä¸ªæ¯è¾å¨ï¼
+ </para>
+
+ <programlisting><![CDATA[<set name="aliases"
+ table="person_aliases"
+ sort="natural">
+ <key column="person"/>
+ <element column="name" type="string"/>
+</set>
+
+<map name="holidays" sort="my.custom.HolidayComparator">
+ <key column="year_id"/>
+ <map-key column="hol_name" type="string"/>
+ <element column="hol_date" type="date"/>
+</map>]]></programlisting>
+
+ <para>
+ <literal>sort</literal>å±æ§ä¸å
许çå¼å
æ¬<literal>unsorted</literal>,<literal>natural</literal>åæ个å®ç°äº<literal>java.util.Comparator</literal>çç±»çå称ã
+ </para>
+
+ <para>
+ åç±»éåçè¡ä¸ºäºå®ä¸è±¡<literal>java.util.TreeSet</literal>æè
<literal>java.util.TreeMap</literal>ã
+ </para>
+
+ <para>
+ å¦æä½ å¸ææ°æ®åºèªå·±å¯¹éåå
ç´ æåºï¼å¯ä»¥å©ç¨<literal>set</literal>,<literal>bag</literal>æè
<literal>map</literal>æ å°ä¸ç<literal>order-by</literal>å±æ§ãè¿ä¸ªè§£å³æ¹æ¡åªè½å¨jdk1.4æè
æ´é«çjdkçæ¬ä¸æå¯ä»¥å®ç°(éè¿LinkedHashSetæè
+LinkedHashMapå®ç°)ã å®æ¯å¨SQLæ¥è¯¢ä¸å®ææåºï¼èä¸æ¯å¨å
åä¸ã
+ </para>
+
+ <programlisting><![CDATA[<set name="aliases" table="person_aliases" order-by="lower(name) asc">
+ <key column="person"/>
+ <element column="name" type="string"/>
+</set>
+
+<map name="holidays" order-by="hol_date, hol_name">
+ <key column="year_id"/>
+ <map-key column="hol_name" type="string"/>
+ <element column="hol_date" type="date"/>
+</map>]]></programlisting>
+
+ <para>
+ 注æ: è¿ä¸ª<literal>order-by</literal>å±æ§çå¼æ¯ä¸ä¸ªSQLæåºåå¥èä¸æ¯HQLçï¼
+ </para>
+
+ <para>
+ å
³èè¿å¯ä»¥å¨è¿è¡æ¶ä½¿ç¨éå<literal>filter()</literal>æ ¹æ®ä»»æçæ¡ä»¶æ¥æåºã
+ </para>
+
+ <programlisting><![CDATA[sortedUsers = s.createFilter( group.getUsers(), "order by this.name" ).list();]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="collections-bidirectional" revision="1">
+ <title>ååå
³èï¼Bidirectional associationsï¼</title>
+
+ <para>
+ <emphasis>ååå
³è</emphasis>å
许éè¿å
³èçä»»ä¸ç«¯è®¿é®å¦å¤ä¸ç«¯ãå¨Hibernateä¸, æ¯æ两ç§ç±»åçååå
³è:
+
+ <variablelist>
+ <varlistentry>
+ <term>ä¸å¯¹å¤ï¼one-to-manyï¼</term>
+ <listitem>
+ <para>
+ Setæè
bagå¼å¨ä¸ç«¯, åç¬å¼(ééå)å¨å¦å¤ä¸ç«¯
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>å¤å¯¹å¤ï¼many-to-manyï¼</term>
+ <listitem>
+ <para>
+ 两端é½æ¯setæbagå¼
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ </para>
+
+ <para>
+ è¦å»ºç«ä¸ä¸ªååçå¤å¯¹å¤å
³èï¼åªéè¦æ å°ä¸¤ä¸ªmany-to-manyå
³èå°åä¸ä¸ªæ°æ®åºè¡¨ä¸ï¼å¹¶åå®ä¹å
¶ä¸çä¸ç«¯ä¸º<emphasis>inverse</emphasis>(使ç¨åªä¸ç«¯è¦æ ¹æ®ä½ çéæ©ï¼ä½å®ä¸è½æ¯ä¸ä¸ªç´¢å¼éå)ã
+ </para>
+
+
+
+ <para>
+ è¿éæä¸ä¸ªmany-to-manyçååå
³èçä¾å;æ¯ä¸ä¸ªcategoryé½å¯ä»¥æå¾å¤items,æ¯ä¸ä¸ªitemså¯ä»¥å±äºå¾å¤categoriesï¼
+ </para>
+
+
+ <programlisting><![CDATA[<class name="Category">
+ <id name="id" column="CATEGORY_ID"/>
+ ...
+ <bag name="items" table="CATEGORY_ITEM">
+ <key column="CATEGORY_ID"/>
+ <many-to-many class="Item" column="ITEM_ID"/>
+ </bag>
+</class>
+
+<class name="Item">
+ <id name="id" column="CATEGORY_ID"/>
+ ...
+
+ <!-- inverse end -->
+ <bag name="categories" table="CATEGORY_ITEM" inverse="true">
+ <key column="ITEM_ID"/>
+ <many-to-many class="Category" column="CATEGORY_ID"/>
+ </bag>
+</class>]]></programlisting>
+
+ <para>
+ å¦æåªå¯¹å
³èçåå端è¿è¡äºæ¹åï¼è¿ä¸ªæ¹å<emphasis>ä¸ä¼</emphasis>被æä¹
åã
+ è¿è¡¨ç¤ºHibernate为æ¯ä¸ªååå
³èå¨å
åä¸åå¨ä¸¤æ¬¡è¡¨ç°,ä¸ä¸ªä»Aè¿æ¥å°B,å¦ä¸ä¸ªä»Bè¿æ¥å°Aãå¦æä½ åæ³ä¸ä¸Java对象模åï¼æ们æ¯å¦ä½å¨Javaä¸å建å¤å¯¹å¤å
³ç³»çï¼è¿å¯ä»¥è®©ä½ æ´å®¹æç解ï¼
+
+ </para>
+
+ <programlisting><![CDATA[
+category.getItems().add(item); // The category now "knows" about the relationship
+item.getCategories().add(category); // The item now "knows" about the relationship
+
+session.persist(item); // The relationship won''t be saved!
+session.persist(category); // The relationship will be saved]]></programlisting>
+
+ <para>
+ éåå端ç¨äºæå
åä¸ç表示ä¿åå°æ°æ®åºä¸ã
+ </para>
+
+ <para>
+ è¦å»ºç«ä¸ä¸ªä¸å¯¹å¤çååå
³èï¼ä½ å¯ä»¥éè¿æä¸ä¸ªä¸å¯¹å¤å
³èï¼ä½ä¸ºä¸ä¸ªå¤å¯¹ä¸å
³èæ å°å°å°åä¸å¼ 表çå段ä¸ï¼å¹¶ä¸å¨"å¤"çé£ä¸ç«¯å®ä¹<literal>inverse="true"</literal>ã
+ </para>
+
+ <programlisting><![CDATA[<class name="Parent">
+ <id name="id" column="parent_id"/>
+ ....
+ <set name="children" inverse="true">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+ </set>
+</class>
+
+<class name="Child">
+ <id name="id" column="child_id"/>
+ ....
+ <many-to-one name="parent"
+ class="Parent"
+ column="parent_id"
+ not-null="true"/>
+</class>]]></programlisting>
+
+
+
+ <para>
+ å¨âä¸âè¿ä¸ç«¯å®ä¹<literal>inverse="true"</literal>ä¸ä¼å½±å级èæä½ï¼äºè
æ¯æ£äº¤çæ¦å¿µï¼
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-indexedbidirectional">
+ <title>ååå
³èï¼æ¶åæåºéåç±»</title>
+ <para>
+ 对äºæä¸ç«¯æ¯<literal><list></literal>æè
<literal><map></literal>çååå
³èï¼éè¦å 以ç¹å«èèãåè¥åç±»ä¸çä¸ä¸ªå±æ§æ å°å°ç´¢å¼å段ï¼æ²¡é®é¢ï¼æ们ä»ç¶å¯ä»¥å¨éåç±»æ å°ä¸ä½¿ç¨<literal>inverse="true"</literal>ï¼
+ </para>
+
+ <programlisting><![CDATA[<class name="Parent">
+ <id name="id" column="parent_id"/>
+ ....
+ <map name="children" inverse="true">
+ <key column="parent_id"/>
+ <map-key column="name"
+ type="string"/>
+ <one-to-many class="Child"/>
+ </map>
+</class>
+
+<class name="Child">
+ <id name="id" column="child_id"/>
+ ....
+ <property name="name"
+ not-null="true"/>
+ <many-to-one name="parent"
+ class="Parent"
+ column="parent_id"
+ not-null="true"/>
+</class>]]></programlisting>
+
+ <para>
+ ä½æ¯ï¼åè¥åç±»ä¸æ²¡æè¿æ ·çå±æ§åå¨ï¼æ们ä¸è½è®¤ä¸ºè¿ä¸ªå
³èæ¯çæ£çååå
³èï¼ä¿¡æ¯ä¸å¯¹ç§°ï¼å¨å
³èçä¸ç«¯æä¸äºå¦å¤ä¸ç«¯æ²¡æçä¿¡æ¯ï¼ãå¨è¿ç§æ
åµä¸ï¼æ们ä¸è½ä½¿ç¨<literal>inverse="true"</literal>ãæ们éè¦è¿æ ·ç¨ï¼
+ </para>
+
+ <programlisting><![CDATA[<class name="Parent">
+ <id name="id" column="parent_id"/>
+ ....
+ <map name="children">
+ <key column="parent_id"
+ not-null="true"/>
+ <map-key column="name"
+ type="string"/>
+ <one-to-many class="Child"/>
+ </map>
+</class>
+
+<class name="Child">
+ <id name="id" column="child_id"/>
+ ....
+ <many-to-one name="parent"
+ class="Parent"
+ column="parent_id"
+ insert="false"
+ update="false"
+ not-null="true"/>
+</class>]]></programlisting>
+
+ <para>
+ 注æå¨è¿ä¸ªæ å°ä¸ï¼å
³èä¸éåç±»"å¼"ä¸ç«¯è´è´£æ¥æ´æ°å¤é®.TODO: Does this really result in some unnecessary update statements?
+ </para>
+
+ </sect2>
+
+
+ <sect2 id="collections-ternary">
+ <title>ä¸éå
³èï¼Ternary associationsï¼</title>
+
+ <para>
+ æä¸ç§å¯è½çéå¾æ¥æ å°ä¸ä¸ªä¸éå
³èã第ä¸ç§æ¯ä½¿ç¨ä¸ä¸ª<literal>Map</literal>ï¼æä¸ä¸ªå
³èä½ä¸ºå
¶ç´¢å¼ï¼
+ </para>
+
+ <programlisting><![CDATA[<map name="contracts">
+ <key column="employer_id" not-null="true"/>
+ <map-key-many-to-many column="employee_id" class="Employee"/>
+ <one-to-many class="Contract"/>
+</map>]]></programlisting>
+
+ <programlisting><![CDATA[<map name="connections">
+ <key column="incoming_node_id"/>
+ <map-key-many-to-many column="outgoing_node_id" class="Node"/>
+ <many-to-many column="connection_id" class="Connection"/>
+</map>]]></programlisting>
+
+ <para>
+ 第äºç§æ¹æ³æ¯ç®åçæå
³èéæ°å»ºæ¨¡ä¸ºä¸ä¸ªå®ä½ç±»ãè¿ä½¿æ们æç»å¸¸ä½¿ç¨çæ¹æ³ã
+ </para>
+ <para>
+ æåä¸ç§éæ©æ¯ä½¿ç¨å¤åå
ç´ ï¼æ们ä¼å¨åé¢è®¨è®º
+ </para>
+
+ </sect2>
+
+ <sect2 id="collections-idbag" revision="1">
+ <title><literal>使ç¨<idbag></literal></title>
+ <para>
+ å¦æä½ å®å
¨ä¿¡å¥æ们对äºâèå主é®ï¼composite keysï¼æ¯ä¸ªåä¸è¥¿âï¼åâå®ä½åºè¯¥ä½¿ç¨ï¼æ æºçï¼èªå·±çæç代ç¨æ è¯ç¬¦ï¼surrogate keysï¼âçè§ç¹ï¼ä¹è®¸ä½ ä¼æå°æä¸äºå¥æªï¼æ们ç®å为æ¢å±ç¤ºçå¤å¯¹å¤å
³èåå¼éåé½æ¯æ å°æ为带æèå主é®ç表çï¼ç°å¨ï¼è¿ä¸ç¹é常å¼å¾äºè¾©ï¼çä¸å»ä¸ä¸ªå纯çå
³è表并ä¸è½ä»ä»£ç¨æ è¯ç¬¦ä¸è·å¾ä»ä¹å¥½å¤ï¼è½ç¶ä½¿ç¨ç»åå¼çéå<emphasis>å¯è½</emphasis>ä¼è·å¾ä¸ç¹å¥½å¤ï¼ãä¸è¿ï¼Hibernateæä¾äºä¸ä¸ªï¼ä¸ç¹ç¹è¯éªæ§è´¨çï¼åè½ï¼è®©ä½ æå¤å¯¹å¤å
³èåå¼éååºå¾å°ä¸ä¸ªä½¿ç¨ä»£ç¨æ è¯ç¬¦ç表å»ã
+ </para>
+
+ <para>
+ <literal><idbag></literal> å±æ§è®©ä½ 使ç¨bagè¯ä¹æ¥æ å°ä¸ä¸ª<literal>List</literal> (æ<literal>Collection</literal>)ã
+ </para>
+
+<programlisting><![CDATA[<idbag name="lovers" table="LOVERS">
+ <collection-id column="ID" type="long">
+ <generator class="sequence"/>
+ </collection-id>
+ <key column="PERSON1"/>
+ <many-to-many column="PERSON2" class="Person" fetch="join"/>
+</idbag>]]></programlisting>
+
+ <para>
+ ä½ å¯ä»¥ç解ï¼<literal><idbag></literal>人工çidçæå¨ï¼å°±å¥½åæ¯å®ä½ç±»ä¸æ ·ï¼éåçæ¯ä¸è¡é½æä¸ä¸ªä¸åç人é å
³é®åãä½æ¯ï¼Hibernate没ææä¾ä»»ä½æºå¶æ¥è®©ä½ åå¾æ个ç¹å®è¡ç人é å
³é®åã
+ </para>
+
+ <para>
+ 注æ<literal><idbag></literal>çæ´æ°æ§è½è¦æ¯æ®éç<literal><bag></literal>é«å¾å¤ï¼Hibernateå¯ä»¥ææçå®ä½å°ä¸åçè¡ï¼åå«è¿è¡æ´æ°æå é¤å·¥ä½ï¼å°±å¦åå¤çä¸ä¸ªlist, mapæè
setä¸æ ·ã
+
+ </para>
+
+ <para>
+ å¨ç®åçå®ç°ä¸ï¼è¿ä¸æ¯æ使ç¨<literal>identity</literal>æ è¯ç¬¦çæå¨çç¥æ¥çæ<literal><idbag></literal>éåçæ è¯ç¬¦ã
+ </para>
+
+ </sect2>
+
+ </sect1>
+ <!--undocumenting this stuff -->
+ <!--sect1 id="collections-heterogeneous">
+ <title>å¼ç±»å
³è(Heterogeneous Associations)</title>
+
+ <para>
+ <literal><many-to-any></literal>å<literal><index-many-to-any></literal>å
ç´ æä¾çæ£çå¼ç±»å
³èãè¿äºå
ç´ å<literal><any></literal>å
ç´ å·¥ä½æ¹å¼æ¯åæ ·ç,ä»ä»¬é½åºè¯¥å¾å°ç¨å°ã
+ </para>
+ </sect1-->
+
+ <sect1 id="collections-example" revision="1">
+ <title>éåä¾åï¼Collection exampleï¼</title>
+
+ <para>
+ å¨åé¢çå ä¸ªç« èçç¡®é常令人迷æã å æ¤è®©æ们æ¥çä¸ä¸ªä¾åãè¿ä¸ªç±»ï¼
+ </para>
+
+ <programlisting><![CDATA[package eg;
+import java.util.Set;
+
+public class Parent {
+ private long id;
+ private Set children;
+
+ public long getId() { return id; }
+ private void setId(long id) { this.id=id; }
+
+ private Set getChildren() { return children; }
+ private void setChildren(Set children) { this.children=children; }
+
+ ....
+ ....
+}]]></programlisting>
+
+ <para>
+ è¿ä¸ªç±»æä¸ä¸ª<literal>Child</literal>çå®ä¾éåãå¦ææ¯ä¸ä¸ªåå®ä¾è³å¤æä¸ä¸ªç¶å®ä¾, é£ä¹æèªç¶çæ å°æ¯ä¸ä¸ªone-to-manyçå
³èå
³ç³»ï¼
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Parent">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <set name="children">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+ </set>
+ </class>
+
+ <class name="Child">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ å¨ä»¥ä¸ç表å®ä¹ä¸ååºäºè¿ä¸ªæ å°å
³ç³»ï¼
+ </para>
+
+ <programlisting><![CDATA[create table parent ( id bigint not null primary key )
+create table child ( id bigint not null primary key, name varchar(255), parent_id bigint )
+alter table child add constraint childfk0 (parent_id) references parent]]></programlisting>
+
+ <para>
+ å¦æç¶äº²æ¯<emphasis>å¿
é¡»</emphasis>ç, é£ä¹å°±å¯ä»¥ä½¿ç¨ååone-to-manyçå
³èäºï¼
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Parent">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <set name="children" inverse="true">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+ </set>
+ </class>
+
+ <class name="Child">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <property name="name"/>
+ <many-to-one name="parent" class="Parent" column="parent_id" not-null="true"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ 请注æ<literal>NOT NULL</literal>ç约æ:
+ </para>
+
+ <programlisting><![CDATA[create table parent ( id bigint not null primary key )
+create table child ( id bigint not null
+ primary key,
+ name varchar(255),
+ parent_id bigint not null )
+alter table child add constraint childfk0 (parent_id) references parent]]></programlisting>
+
+
+ <para>
+ å¦å¤ï¼å¦æä½ ç»å¯¹åæè¿ä¸ªå
³èåºè¯¥æ¯ååçï¼ä½ å¯ä»¥å¯¹<literal><key></literal>æ å°å£°æ<literal>NOT NULL</literal>约æï¼
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Parent">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <set name="children">
+ <key column="parent_id" not-null="true"/>
+ <one-to-many class="Child"/>
+ </set>
+ </class>
+
+ <class name="Child">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ å¦å¤ä¸æ¹é¢,å¦æä¸ä¸ªåå®ä¾å¯è½æå¤ä¸ªç¶å®ä¾, é£ä¹å°±åºè¯¥ä½¿ç¨many-to-manyå
³èï¼
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Parent">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <set name="children" table="childset">
+ <key column="parent_id"/>
+ <many-to-many class="Child" column="child_id"/>
+ </set>
+ </class>
+
+ <class name="Child">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+ <para>
+ 表å®ä¹ï¼
+ </para>
+
+ <programlisting><![CDATA[create table parent ( id bigint not null primary key )
+create table child ( id bigint not null primary key, name varchar(255) )
+create table childset ( parent_id bigint not null,
+ child_id bigint not null,
+ primary key ( parent_id, child_id ) )
+alter table childset add constraint childsetfk0 (parent_id) references parent
+alter table childset add constraint childsetfk1 (child_id) references child]]></programlisting>
+
+ <para>
+ æ´å¤çä¾å,以åä¸ä¸ªå®æ´çç¶/åå
³ç³»æ å°çæç»,请åé
<xref linkend="example-parentchild"/>.
+ </para>
+
+ <para>
+ çè³å¯è½åºç°æ´å å¤æçå
³èæ å°,æ们ä¼å¨ä¸ä¸ç« ä¸ååºææå¯è½æ§ã
+ </para>
+
+ </sect1>
+
+ </chapter>
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/component_mapping.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/component_mapping.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/component_mapping.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,339 @@
+<chapter id="components">
+ <title>ç»ä»¶ï¼Componentï¼æ å°</title>
+
+ <para>
+ <emphasis>ç»ä»¶</emphasis>(Component)è¿ä¸ªæ¦å¿µå¨Hibernateä¸å å¤ä¸åçå°æ¹ä¸ºäºä¸åçç®ç被éå¤ä½¿ç¨.
+ </para>
+
+ <sect1 id="components-dependentobjects" revision="2">
+ <title>ä¾èµå¯¹è±¡ï¼Dependent objectsï¼</title>
+
+ <para>
+ ç»ä»¶(Component)æ¯ä¸ä¸ªè¢«å
å«ç对象ï¼å¨æä¹
åçè¿ç¨ä¸ï¼å®è¢«å½ä½å¼ç±»åï¼è并éä¸ä¸ªå®ä½çå¼ç¨ãå¨è¿ç¯ææ¡£ä¸ï¼ç»ä»¶è¿ä¸æ¯è¯æçæ¯é¢å对象çåææ¦å¿µï¼è并ä¸æ¯ç³»ç»ææ¶å±æ¬¡ä¸çç»ä»¶çæ¦å¿µï¼ã举个ä¾å, ä½ å¯¹äºº(Person)è¿ä¸ªæ¦å¿µå¯ä»¥åä¸é¢è¿æ ·æ¥å»ºæ¨¡ï¼
+ </para>
+
+ <programlisting><![CDATA[public class Person {
+ private java.util.Date birthday;
+ private Name name;
+ private String key;
+ public String getKey() {
+ return key;
+ }
+ private void setKey(String key) {
+ this.key=key;
+ }
+ public java.util.Date getBirthday() {
+ return birthday;
+ }
+ public void setBirthday(java.util.Date birthday) {
+ this.birthday = birthday;
+ }
+ public Name getName() {
+ return name;
+ }
+ public void setName(Name name) {
+ this.name = name;
+ }
+ ......
+ ......
+}]]></programlisting>
+
+ <programlisting><![CDATA[public class Name {
+ char initial;
+ String first;
+ String last;
+ public String getFirst() {
+ return first;
+ }
+ void setFirst(String first) {
+ this.first = first;
+ }
+ public String getLast() {
+ return last;
+ }
+ void setLast(String last) {
+ this.last = last;
+ }
+ public char getInitial() {
+ return initial;
+ }
+ void setInitial(char initial) {
+ this.initial = initial;
+ }
+}]]></programlisting>
+
+ <para>
+ å¨æä¹
åçè¿ç¨ä¸,<literal>å§å(Name)</literal>å¯ä»¥ä½ä¸º<literal>人(Person)</literal>çä¸ä¸ªç»ä»¶ãéè¦æ³¨æçæ¯:ä½ åºè¯¥ä¸º<literal>å§å</literal>çæä¹
åå±æ§å®ä¹getteråsetteræ¹æ³,ä½æ¯ä½ ä¸éè¦å®ç°ä»»ä½çæ¥å£æç³ææ è¯ç¬¦å段ã
+ </para>
+
+ <para>
+ 以ä¸æ¯è¿ä¸ªä¾åçHibernateæ å°æ件:
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Person" table="person">
+ <id name="Key" column="pid" type="string">
+ <generator class="uuid"/>
+ </id>
+ <property name="birthday" type="date"/>
+ <component name="Name" class="eg.Name"> <!-- class attribute optional -->
+ <property name="initial"/>
+ <property name="first"/>
+ <property name="last"/>
+ </component>
+</class>]]></programlisting>
+
+ <para>
+ 人å(Person)表ä¸å°å
æ¬<literal>pid</literal>,
+ <literal>birthday</literal>,
+ <literal>initial</literal>,
+ <literal>first</literal>å
+ <literal>last</literal>çå段ã</para>
+
+ <para>
+ å°±åææçå¼ç±»åä¸æ ·, ç»ä»¶ä¸æ¯æå
±äº«å¼ç¨ã
+ æ¢å¥è¯è¯´ï¼ä¸¤ä¸ªäººå¯è½éåï¼ä½æ¯ä¸¤ä¸ªPerson对象åºè¯¥å
å«ä¸¤ä¸ªç¬ç«çName对象ï¼åªä¸è¿è¿ä¸¤ä¸ªName对象å
·æâåæ ·âçå¼ã
+ ç»ä»¶çå¼å¯ä»¥ä¸ºç©ºï¼å
¶å®ä¹å¦ä¸ã
+ æ¯å½Hibernateéæ°å è½½ä¸ä¸ªå
å«ç»ä»¶ç对象,å¦æ该ç»ä»¶çææå段为空ï¼Hibernateå°åå®æ´ä¸ªç»ä»¶ä¸ºç©ºã
+ å¨å¤§å¤æ°æ
åµä¸,è¿æ ·åå®åºè¯¥æ¯æ²¡æé®é¢çã
+ </para>
+
+ <para>
+ ç»ä»¶çå±æ§å¯ä»¥æ¯ä»»æä¸ç§Hibernateç±»åï¼å
æ¬éå, å¤å¯¹å¤å
³èï¼
+ 以åå
¶å®ç»ä»¶ççï¼ãåµå¥ç»ä»¶ä¸åºè¯¥è¢«å½ä½ä¸ç§ç¹æ®çåºç¨(Nested components should not be considered an
+ exotic usage)ã Hibernateå¾åäºæ¯æç»è´ç(fine-grained)对象模åã
+ </para>
+
+ <para>
+ <literal><component></literal> å
ç´ è¿å
许æ <literal><parent></literal>åå
ç´ ï¼ç¨æ¥è¡¨æcomponentç±»ä¸çä¸ä¸ªå±æ§æ¯æåå
å«å®çå®ä½çå¼ç¨ã
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Person" table="person">
+ <id name="Key" column="pid" type="string">
+ <generator class="uuid"/>
+ </id>
+ <property name="birthday" type="date">
+ <component name="Name" class="eg.Name" unique="true">
+ <parent name="namedPerson"/> <!-- reference back to the Person -->
+ <property name="initial"/>
+ <property name="first"/>
+ <property name="last"/>
+ </component>
+</class>]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="components-incollections" revision="1">
+ <title>å¨éåä¸åºç°çä¾èµå¯¹è±¡ (Collections of dependent objects)</title>
+
+ <para>
+ Hibernateæ¯æç»ä»¶çéå(ä¾å¦: ä¸ä¸ªå
ç´ æ¯å§å(Name)è¿ç§ç±»åçæ°ç»)ã
+ ä½ å¯ä»¥ä½¿ç¨<literal><composite-element></literal>æ ç¾æ¿ä»£<literal><element></literal>æ ç¾æ¥å®ä¹ä½ çç»ä»¶éåã
+ </para>
+
+ <programlisting><![CDATA[<set name="someNames" table="some_names" lazy="true">
+ <key column="id"/>
+ <composite-element class="eg.Name"> <!-- class attribute required -->
+ <property name="initial"/>
+ <property name="first"/>
+ <property name="last"/>;
+ </composite-element>
+</set>]]></programlisting>
+
+ <para>
+ 注æï¼å¦æä½ å®ä¹çSetå
å«ç»åå
ç´ (composite-element)ï¼æ£ç¡®å°å®ç°<literal>equals()</literal>å<literal>hashCode()</literal>æ¯é常éè¦çã
+ </para>
+
+ <para>
+ ç»åå
ç´ å¯ä»¥å
å«ç»ä»¶ï¼ä½æ¯ä¸è½å
å«éåãå¦æä½ çç»åå
ç´ èªèº«å
å«ç»ä»¶, ä½ å¿
须使ç¨<literal><nested-composite-element></literal>æ ç¾ãè¿æ¯ä¸ä¸ªç¸å½ç¹æ®çæ¡ä¾ - å¨ä¸ä¸ªç»ä»¶çéåéï¼é£äºç»ä»¶æ¬èº«åå¯ä»¥å
å«å
¶ä»çç»ä»¶ãè¿ä¸ªæ¶åä½ å°±åºè¯¥èèä¸ä¸ä½¿ç¨one-to-manyå
³èæ¯å¦ä¼æ´æ°å½ã
+ å°è¯å¯¹è¿ä¸ªç»åå
ç´ éæ°å»ºæ¨¡ä¸ºä¸ä¸ªå®ä½ï¼ä½æ¯éè¦æ³¨æçæ¯ï¼è½ç¶Java模ååéæ°å»ºæ¨¡åæ¯ä¸æ ·çï¼å
³ç³»æ¨¡ååæä¹
æ§è¯ä¹ä¼æç»å¾®çååã
+ </para>
+
+ <para>
+ 请注æå¦æä½ ä½¿ç¨<literal><set></literal>æ ç¾,ä¸ä¸ªç»åå
ç´ çæ å°ä¸æ¯æå¯è½ä¸ºç©ºçå±æ§. å½å é¤å¯¹è±¡æ¶ï¼ Hibernateå¿
须使ç¨æ¯ä¸ä¸ªå段çå¼æ¥ç¡®å®ä¸æ¡è®°å½(å¨ç»åå
ç´ è¡¨ä¸ï¼æ²¡æåç¬çå
³é®å段)ï¼
+ å¦ææ为nullçå段ï¼è¿æ ·åå°±ä¸å¯è½äºãä½ å¿
é¡»ä½åºä¸ä¸ªéæ©ï¼è¦ä¹å¨ç»åå
ç´ ä¸ä½¿ç¨ä¸è½ä¸ºç©ºçå±æ§ï¼è¦ä¹éæ©ä½¿ç¨<literal><list></literal>,<literal><map></literal>,<literal><bag></literal> æè
<literal><idbag></literal>èä¸æ¯ <literal><set></literal>ã
+ </para>
+
+ <para>
+ ç»åå
ç´ æ个ç¹å«çç¨æ³æ¯å®å¯ä»¥å
å«ä¸ä¸ª<literal><many-to-one></literal>å
ç´ ã类似è¿æ ·çæ å°å
è®¸ä½ å°ä¸ä¸ªmany-to-manyå
³è表æ å°ä¸ºç»åå
ç´ çéåã(A mapping like this allows you to map extra columns of a many-to-many association table to the composite element class.) æ¥ä¸æ¥ççä¾åæ¯ä»<literal>Order</literal>å°<literal>Item</literal>çä¸ä¸ªå¤å¯¹å¤çå
³èå
³ç³», å
³èå±æ§æ¯ <literal>purchaseDate</literal>, <literal>price</literal> å <literal>quantity</literal> ã
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Order" .... >
+ ....
+ <set name="purchasedItems" table="purchase_items" lazy="true">
+ <key column="order_id">
+ <composite-element class="eg.Purchase">
+ <property name="purchaseDate"/>
+ <property name="price"/>
+ <property name="quantity"/>
+ <many-to-one name="item" class="eg.Item"/> <!-- class attribute is optional -->
+ </composite-element>
+ </set>
+</class>]]></programlisting>
+ <para>
+ å½ç¶ï¼å½ä½ å®ä¹Itemæ¶ï¼ä½ æ æ³å¼ç¨è¿äºpurchaseï¼å æ¤ä½ æ æ³å®ç°ååå
³èæ¥è¯¢ãè®°ä½ç»ä»¶æ¯å¼ç±»åï¼å¹¶ä¸ä¸å
许å
±äº«å¼ç¨ãæä¸ä¸ªç¹å®ç<literal>Purchase</literal> å¯ä»¥æ¾å¨<literal>Order</literal>çéåä¸ï¼ä½å®ä¸è½åæ¶è¢«<literal>Item</literal>æå¼ç¨ã
+ </para>
+
+ <para>å
¶å®ç»åå
ç´ çè¿ä¸ªç¨æ³å¯ä»¥æ©å±å°ä¸éæå¤éå
³è:</para>
+
+ <programlisting><![CDATA[<class name="eg.Order" .... >
+ ....
+ <set name="purchasedItems" table="purchase_items" lazy="true">
+ <key column="order_id">
+ <composite-element class="eg.OrderLine">
+ <many-to-one name="purchaseDetails" class="eg.Purchase"/>
+ <many-to-one name="item" class="eg.Item"/>
+ </composite-element>
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ å¨æ¥è¯¢ä¸ï¼è¡¨è¾¾ç»åå
ç´ çè¯æ³åå
³èå°å
¶ä»å®ä½çè¯æ³æ¯ä¸æ ·çã
+ </para>
+
+ </sect1>
+
+ <sect1 id="components-asmapindex">
+ <title>ç»ä»¶ä½ä¸ºMapçç´¢å¼ï¼Components as Map indices ï¼</title>
+
+ <para>
+ <literal><composite-map-key></literal>å
ç´ å
è®¸ä½ æ å°ä¸ä¸ªç»ä»¶ç±»ä½ä¸ºä¸ä¸ª<literal>Map</literal>çkeyï¼åææ¯ä½ å¿
é¡»æ£ç¡®çå¨è¿ä¸ªç±»ä¸éåäº<literal>hashCode()</literal> å <literal>equals()</literal>æ¹æ³ã
+ </para>
+ </sect1>
+
+ <sect1 id="components-compositeid" revision="1">
+ <title>ç»ä»¶ä½ä¸ºèåæ è¯ç¬¦(Components as composite identifiers)</title>
+
+ <para>
+ ä½ å¯ä»¥ä½¿ç¨ä¸ä¸ªç»ä»¶ä½ä¸ºä¸ä¸ªå®ä½ç±»çæ è¯ç¬¦ã ä½ çç»ä»¶ç±»å¿
须满足以ä¸è¦æ±ï¼
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ å®å¿
é¡»å®ç°<literal>java.io.Serializable</literal>æ¥å£
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ å®å¿
é¡»éæ°å®ç°<literal>equals()</literal>å<literal>hashCode()</literal>æ¹æ³, å§ç»åç»åå
³é®åå¨æ°æ®åºä¸çæ¦å¿µä¿æä¸è´
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ <emphasis>注æï¼å¨Hibernate3ä¸ï¼ç¬¬äºä¸ªè¦æ±å¹¶éæ¯Hibernate强å¶å¿
é¡»çãä½æ好è¿æ ·åã</emphasis>
+ </para>
+
+ <para>
+ ä½ ä¸è½ä½¿ç¨ä¸ä¸ª<literal>IdentifierGenerator</literal>产çç»åå
³é®åãä¸ä¸ªåºç¨ç¨åºå¿
é¡»åé
å®èªå·±çæ è¯ç¬¦ã
+ </para>
+
+ <para>
+ 使ç¨<literal><composite-id></literal> æ ç¾(并ä¸å
åµ<literal><key-property></literal>å
ç´ )代æ¿é常ç<literal><id></literal>æ ç¾ãæ¯å¦,<literal>OrderLine</literal>ç±»å
·æä¸ä¸ªä¸»é®ï¼è¿ä¸ªä¸»é®ä¾èµäº<literal>Order</literal>ç(èå)主é®ã
+ </para>
+
+ <programlisting><![CDATA[<class name="OrderLine">
+
+ <composite-id name="id" class="OrderLineId">
+ <key-property name="lineId"/>
+ <key-property name="orderId"/>
+ <key-property name="customerId"/>
+ </composite-id>
+
+ <property name="name"/>
+
+ <many-to-one name="order" class="Order"
+ insert="false" update="false">
+ <column name="orderId"/>
+ <column name="customerId"/>
+ </many-to-one>
+ ....
+
+</class>]]></programlisting>
+
+ <para>
+ ç°å¨ï¼ä»»ä½æå<literal>OrderLine</literal>çå¤é®é½æ¯å¤åçãå¨ä½ çæ å°æ件ä¸ï¼å¿
须为å
¶ä»ç±»ä¹è¿æ ·å£°æãä¾å¦ï¼ä¸ä¸ªæå<literal>OrderLine</literal>çå
³èå¯è½è¢«è¿æ ·æ å°ï¼
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="orderLine" class="OrderLine">
+<!-- the "class" attribute is optional, as usual -->
+ <column name="lineId"/>
+ <column name="orderId"/>
+ <column name="customerId"/>
+</many-to-one>]]></programlisting>
+
+ <para>
+ ï¼æ³¨æå¨å个å°æ¹<literal><column></literal>æ ç¾é½æ¯<literal>column</literal>å±æ§çæ¿ä»£åæ³ãï¼
+ </para>
+
+ <para>
+ æå<literal>OrderLine</literal>ç<literal>å¤å¯¹å¤</literal>å
³èä¹ä½¿ç¨èåå¤é®:
+ </para>
+
+ <programlisting><![CDATA[<set name="undeliveredOrderLines">
+ <key column name="warehouseId"/>
+ <many-to-many class="OrderLine">
+ <column name="lineId"/>
+ <column name="orderId"/>
+ <column name="customerId"/>
+ </many-to-many>
+</set>]]></programlisting>
+
+ <para>
+ å¨<literal>Order</literal>ä¸,<literal>OrderLine</literal>çéååæ¯è¿æ ·:
+ </para>
+
+ <programlisting><![CDATA[<set name="orderLines" inverse="true">
+ <key>
+ <column name="orderId"/>
+ <column name="customerId"/>
+ </key>
+ <one-to-many class="OrderLine"/>
+</set>]]></programlisting>
+
+ <para>
+ (ä¸é常ä¸æ ·,<literal><one-to-many></literal>å
ç´ ä¸å£°æä»»ä½å.)
+ </para>
+
+ <para>
+ åè¥<literal>OrderLine</literal>æ¬èº«æ¥æä¸ä¸ªéå,å®ä¹å
·æç»åå¤é®ã
+ </para>
+
+ <programlisting><![CDATA[<class name="OrderLine">
+ ....
+ ....
+ <list name="deliveryAttempts">
+ <key> <!-- a collection inherits the composite key type -->
+ <column name="lineId"/>
+ <column name="orderId"/>
+ <column name="customerId"/>
+ </key>
+ <list-index column="attemptId" base="1"/>
+ <composite-element class="DeliveryAttempt">
+ ...
+ </composite-element>
+ </set>
+</class>]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="components-dynamic" revision="1">
+ <title>å¨æç»ä»¶ ï¼Dynamic componentsï¼</title>
+
+ <para>
+ ä½ çè³å¯ä»¥æ å°<literal>Map</literal>ç±»åçå±æ§ï¼
+ </para>
+
+ <programlisting><![CDATA[<dynamic-component name="userAttributes">
+ <property name="foo" column="FOO" type="string"/>
+ <property name="bar" column="BAR" type="integer"/>
+ <many-to-one name="baz" class="Baz" column="BAZ_ID"/>
+</dynamic-component>]]></programlisting>
+
+ <para>
+ ä»<literal><dynamic-component></literal>æ å°çè¯ä¹ä¸æ¥è®²ï¼å®å<literal><component></literal>æ¯ç¸åçã
+ è¿ç§æ å°ç±»åçä¼ç¹å¨äºéè¿ä¿®æ¹æ å°æ件ï¼å°±å¯ä»¥å
·æå¨é¨ç½²æ¶æ£æµçå®å±æ§çè½åãå©ç¨ä¸ä¸ªDOM解æå¨ï¼ä¹å¯ä»¥å¨ç¨åºè¿è¡æ¶æä½æ å°æ件ã
+ æ´å¥½çæ¯ï¼ä½ å¯ä»¥éè¿<literal>Configuration</literal>对象æ¥è®¿é®ï¼æè
ä¿®æ¹ï¼Hibernateçè¿è¡æ¶å
模åã
+ </para>
+ </sect1>
+</chapter>
\ No newline at end of file
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/configuration.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/configuration.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/configuration.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1694 @@
+<chapter id="session-configuration" revision="1">
+
+ <title>
+ é
ç½®
+ </title>
+
+ <para>
+ ç±äºHibernateæ¯ä¸ºäºè½å¨åç§ä¸åç¯å¢ä¸å·¥ä½è设计ç, å æ¤åå¨ç大éçé
ç½®åæ°. 幸è¿çæ¯å¤æ°é
ç½®åæ°é½
+ ææ¯è¾ç´è§çé»è®¤å¼, 并æéHibernateä¸åååçé
ç½®æ ·ä¾<literal>hibernate.properties</literal>
+ (ä½äº<literal>etc/</literal>)æ¥å±ç¤ºåç§é
ç½®é项. æéåçä»
ä»
æ¯å°è¿ä¸ªæ ·ä¾æ件å¤å¶å°ç±»è·¯å¾
+ (classpath)ä¸å¹¶åä¸äºèªå®ä¹çä¿®æ¹.
+ </para>
+
+ <sect1 id="configuration-programmatic" revision="1">
+ <title>
+ å¯ç¼ç¨çé
ç½®æ¹å¼
+ </title>
+
+ <para>
+ ä¸ä¸ª<literal>org.hibernate.cfg.Configuration</literal>å®ä¾ä»£è¡¨äºä¸ä¸ªåºç¨ç¨åºä¸Javaç±»å
+ å°SQLæ°æ®åºæ å°çå®æ´éå. <literal>Configuration</literal>被ç¨æ¥æ建ä¸ä¸ª(ä¸å¯åç
+ (immutable))<literal>SessionFactory</literal>. æ å°å®ä¹åç±ä¸åçXMLæ å°å®ä¹æ件ç¼è¯èæ¥.
+ </para>
+
+ <para>
+ ä½ å¯ä»¥ç´æ¥å®ä¾å<literal>Configuration</literal>æ¥è·åä¸ä¸ªå®ä¾ï¼å¹¶ä¸ºå®æå®XMLæ å°å®ä¹
+ æ件. å¦ææ å°å®
+ ä¹æ件å¨ç±»è·¯å¾(classpath)ä¸, 请使ç¨<literal>addResource()</literal>:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = new Configuration()
+ .addResource("Item.hbm.xml")
+ .addResource("Bid.hbm.xml");]]></programlisting>
+
+ <para>
+ ä¸ä¸ªæ¿ä»£æ¹æ³ï¼ææ¶æ¯æ´å¥½çéæ©ï¼æ¯ï¼æå®è¢«æ å°çç±»ï¼è®©Hibernateå¸®ä½ å¯»æ¾æ å°å®ä¹æ件:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = new Configuration()
+ .addClass(org.hibernate.auction.Item.class)
+ .addClass(org.hibernate.auction.Bid.class);]]></programlisting>
+
+ <para>
+ Hibernateå°ä¼å¨ç±»è·¯å¾(classpath)ä¸å¯»æ¾åå为
+ <literal>/org/hibernate/auction/Item.hbm.xml</literal>å
+ <literal>/org/hibernate/auction/Bid.hbm.xml</literal>æ å°å®ä¹æ件.
+ è¿ç§æ¹å¼æ¶é¤äºä»»ä½å¯¹æ件åç硬ç¼ç (hardcoded).
+ </para>
+
+ <para>
+ <literal>Configuration</literal>ä¹å
è®¸ä½ æå®é
ç½®å±æ§:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = new Configuration()
+ .addClass(org.hibernate.auction.Item.class)
+ .addClass(org.hibernate.auction.Bid.class)
+ .setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLInnoDBDialect")
+ .setProperty("hibernate.connection.datasource", "java:comp/env/jdbc/test")
+ .setProperty("hibernate.order_updates", "true");]]></programlisting>
+
+ <para>
+ å½ç¶è¿ä¸æ¯å¯ä¸çä¼ éHibernateé
ç½®å±æ§çæ¹å¼, å
¶ä»å¯éæ¹å¼è¿å
æ¬:
+ </para>
+
+ <orderedlist spacing="compact">
+ <listitem>
+ <para>
+ ä¼ ä¸ä¸ª<literal>java.util.Properties</literal>å®ä¾ç»
+ <literal>Configuration.setProperties()</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ å°<literal>hibernate.properties</literal>æ¾ç½®å¨ç±»è·¯å¾(classpath)çæ ¹ç®å½ä¸
+ (root directory).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ éè¿<literal>java -Dproperty=value</literal>æ¥è®¾ç½®ç³»ç»
+ (<literal>System</literal>)å±æ§.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ å¨<literal>hibernate.cfg.xml</literal>ä¸å å
¥å
ç´
+ <literal><property></literal> (ç¨å讨论).
+ </para>
+ </listitem>
+ </orderedlist>
+
+ <para>
+ å¦ææ³å°½å¿«ä½éªHibernate, <literal>hibernate.properties</literal>æ¯æç®åçæ¹å¼.
+ </para>
+
+ <para>
+ <literal>Configuration</literal>å®ä¾è¢«è®¾è®¡æå¯å¨æé´ï¼startup-timeï¼å¯¹è±¡,
+ ä¸æ¦<literal>SessionFactory</literal>å建å®æå®å°±è¢«ä¸¢å¼äº.
+ </para>
+
+ </sect1>
+
+ <sect1 id="configuration-sessionfactory">
+ <title>
+ è·å¾SessionFactory
+ </title>
+
+
+ <para>
+ å½æææ å°å®ä¹è¢«<literal>Configuration</literal>解æå,
+ åºç¨ç¨åºå¿
é¡»è·å¾ä¸ä¸ªç¨äºæé <literal>Session</literal>å®ä¾çå·¥å.
+ è¿ä¸ªå·¥åå°è¢«åºç¨ç¨åºçææ线ç¨å
񄧮:
+ </para>
+
+ <programlisting><![CDATA[SessionFactory sessions = cfg.buildSessionFactory();]]></programlisting>
+
+ <para>
+ Hibernateå
è®¸ä½ çåºç¨ç¨åºå建å¤ä¸ª<literal>SessionFactory</literal>å®ä¾. è¿å¯¹
+ 使ç¨å¤ä¸ªæ°æ®åºçåºç¨æ¥è¯´å¾æç¨.
+ </para>
+
+ </sect1>
+
+ <sect1 id="configuration-hibernatejdbc" revision="1">
+ <title>
+ JDBCè¿æ¥
+ </title>
+
+ <para>
+ éå¸¸ä½ å¸æ<literal>SessionFactory</literal>æ¥ä¸ºä½ å建åç¼å(pool)JDBCè¿æ¥. å¦æä½ éç¨è¿ç§æ¹å¼,
+ åªéè¦å¦ä¸ä¾æ示é£æ ·ï¼æå¼ä¸ä¸ª<literal>Session</literal>:
+ </para>
+
+ <programlisting><![CDATA[Session session = sessions.openSession(); // open a new Session]]></programlisting>
+
+ <para>
+ ä¸æ¦ä½ éè¦è¿è¡æ°æ®è®¿é®æ¶, å°±ä¼ä»è¿æ¥æ± (connection pool)è·å¾ä¸ä¸ªJDBCè¿æ¥.
+ </para>
+
+ <para>
+ 为äºä½¿è¿ç§æ¹å¼å·¥ä½èµ·æ¥, æ们éè¦åHibernateä¼ éä¸äºJDBCè¿æ¥çå±æ§.
+ ææHibernateå±æ§çåååè¯ä¹é½å¨<literal>org.hibernate.cfg.Environment</literal>ä¸å®ä¹.
+ æ们ç°å¨å°æè¿°JDBCè¿æ¥é
ç½®ä¸æéè¦ç设置.
+ </para>
+
+ <para>
+ å¦æä½ è®¾ç½®å¦ä¸å±æ§ï¼Hibernateå°ä½¿ç¨<literal>java.sql.DriverManager</literal>æ¥è·å¾(åç¼å)JDBCè¿æ¥ :
+ </para>
+
+ <table frame="topbot">
+ <title>
+ Hibernate JDBCå±æ§
+ </title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>
+ å±æ§å
+ </entry>
+ <entry>
+ ç¨é
+ </entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.connection.driver_class</literal>
+ </entry>
+ <entry>
+ <emphasis>jdbc驱å¨ç±»</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.url</literal>
+ </entry>
+ <entry>
+ <emphasis>jdbc URL</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.username</literal>
+ </entry>
+ <entry>
+ <emphasis>æ°æ®åºç¨æ·</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.password</literal>
+ </entry>
+ <entry>
+ <emphasis>æ°æ®åºç¨æ·å¯ç </emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.pool_size</literal>
+ </entry>
+ <entry>
+ <emphasis>è¿æ¥æ± 容éä¸éæ°ç®</emphasis>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ ä½Hibernateèªå¸¦çè¿æ¥æ± ç®æ³ç¸å½ä¸æç.
+ å®åªæ¯ä¸ºäºè®©ä½ å¿«äºä¸æ<emphasis>ï¼å¹¶ä¸éåç¨äºäº§åç³»ç»</emphasis>ææ§è½æµè¯ä¸ã
+ åºäºæä½³æ§è½å稳å®æ§èèä½ åºè¯¥ä½¿ç¨ç¬¬ä¸æ¹çè¿æ¥æ± ãåªéè¦ç¨ç¹å®è¿æ¥æ± ç设置æ¿æ¢
+ <literal>hibernate.connection.pool_size</literal>å³å¯ãè¿å°å
³éHibernateèªå¸¦çè¿æ¥æ± .
+ ä¾å¦, ä½ å¯è½ä¼æ³ç¨C3P0.
+ </para>
+
+ <para>
+ C3P0æ¯ä¸ä¸ªéHibernateä¸åååçå¼æºçJDBCè¿æ¥æ± , å®ä½äº<literal>lib</literal>ç®å½ä¸ã
+ å¦æä½ è®¾ç½®äº<literal>hibernate.c3p0.*</literal>ç¸å
³çå±æ§, Hibernateå°ä½¿ç¨
+ <literal>C3P0ConnectionProvider</literal>æ¥ç¼åJDBCè¿æ¥. å¦æä½ æ´åæ使ç¨Proxool, 请åèå
+ è¡å
ä¸ç<literal>hibernate.properties</literal>并å°Hibernateç½ç«è·åæ´å¤çä¿¡æ¯.
+ </para>
+
+ <para>
+ è¿æ¯ä¸ä¸ªä½¿ç¨C3P0ç<literal>hibernate.properties</literal>æ ·ä¾æ件:
+ </para>
+
+ <programlisting id="c3p0-configuration" revision="1"><![CDATA[hibernate.connection.driver_class = org.postgresql.Driver
+hibernate.connection.url = jdbc:postgresql://localhost/mydatabase
+hibernate.connection.username = myuser
+hibernate.connection.password = secret
+hibernate.c3p0.min_size=5
+hibernate.c3p0.max_size=20
+hibernate.c3p0.timeout=1800
+hibernate.c3p0.max_statements=50
+hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect]]></programlisting>
+
+ <para>
+ 为äºè½å¨åºç¨ç¨åºæå¡å¨(application server)ä¸ä½¿ç¨Hibernate, åºå½æ»æ¯å°Hibernate
+ é
ç½®æä»æ³¨åå¨JNDIä¸ç<literal>Datasource</literal>å¤è·å¾è¿æ¥ï¼ä½ è³å°éè¦è®¾ç½®ä¸åå±æ§ä¸çä¸ä¸ª:
+ </para>
+
+ <table frame="topbot">
+ <title>
+ Hibernateæ°æ®æºå±æ§
+ </title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>
+ å±æ§å
+ </entry>
+ <entry>
+ ç¨é
+ </entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.connection.datasource</literal>
+ </entry>
+ <entry>
+ <emphasis>æ°æ®æºJNDIåå</emphasis>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jndi.url</literal>
+ </entry>
+ <entry>
+ <emphasis>JNDIæä¾è
çURL</emphasis> (å¯é)
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jndi.class</literal>
+ </entry>
+ <entry>
+ <emphasis>JNDI <literal>InitialContextFactory</literal>ç±»</emphasis> (å¯é)
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.username</literal>
+ </entry>
+ <entry>
+ <emphasis>æ°æ®åºç¨æ·</emphasis> (å¯é)
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.password</literal>
+ </entry>
+ <entry>
+ <emphasis>æ°æ®åºç¨æ·å¯ç </emphasis> (å¯é)
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ è¿æ¯ä¸ä¸ªä½¿ç¨åºç¨ç¨åºæå¡å¨æä¾çJNDIæ°æ®æºç<literal>hibernate.properties</literal>æ ·ä¾æ件:
+ </para>
+
+ <programlisting><![CDATA[hibernate.connection.datasource = java:/comp/env/jdbc/test
+hibernate.transaction.factory_class = \
+ org.hibernate.transaction.JTATransactionFactory
+hibernate.transaction.manager_lookup_class = \
+ org.hibernate.transaction.JBossTransactionManagerLookup
+hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect]]></programlisting>
+
+ <para>
+ ä»JNDIæ°æ®æºè·å¾çJDBCè¿æ¥å°èªå¨åä¸å°åºç¨ç¨åºæå¡å¨ä¸å®¹å¨ç®¡ççäºå¡(container-managed transactions)ä¸å».
+ </para>
+
+ <para>
+ ä»»ä½è¿æ¥(connection)å±æ§çå±æ§åé½è¦ä»¥"<literal>hibernate.connnection</literal>"å¼å¤´.
+ ä¾å¦, ä½ å¯è½ä¼ä½¿ç¨<literal>hibernate.connection.charSet</literal>æ¥æå®å符é<literal>charSet</literal>.
+ </para>
+
+ <para>
+ éè¿å®ç°<literal>org.hibernate.connection.ConnectionProvider</literal>æ¥å£ï¼ä½ å¯ä»¥å®ä¹å±äº
+ ä½ èªå·±çè·å¾JDBCè¿æ¥çæ件çç¥ãéè¿è®¾ç½®<literal>hibernate.connection.provider_class</literal>ï¼
+ ä½ å¯ä»¥éæ©ä¸ä¸ªèªå®ä¹çå®ç°.
+ </para>
+
+ </sect1>
+
+ <sect1 id="configuration-optional" revision="1">
+ <title>
+ å¯éçé
ç½®å±æ§
+ </title>
+
+ <para>
+ æ大éå±æ§è½ç¨æ¥æ§å¶Hibernateå¨è¿è¡æçè¡ä¸º. å®ä»¬é½æ¯å¯éç, 并æ¥æéå½çé»è®¤å¼.
+ </para>
+
+ <para>
+ <emphasis>è¦å: å
¶ä¸ä¸äºå±æ§æ¯"ç³»ç»çº§(system-level)ç".</emphasis>
+ ç³»ç»çº§å±æ§åªè½éè¿<literal>java -Dproperty=value</literal>æ
+ <literal>hibernate.properties</literal>æ¥è®¾ç½®, è<emphasis>ä¸è½</emphasis>ç¨ä¸é¢æè¿°çå
¶ä»æ¹æ³æ¥è®¾ç½®.
+ </para>
+
+ <table frame="topbot" id="configuration-optional-properties" revision="8">
+ <title>
+ Hibernateé
ç½®å±æ§
+ </title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>
+ å±æ§å
+ </entry>
+ <entry>
+ ç¨é
+ </entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.dialect</literal>
+ </entry>
+ <entry>
+ ä¸ä¸ªHibernate <literal>Dialect</literal>ç±»åå
许Hibernateé对ç¹å®çå
³ç³»æ°æ®åºçæä¼åçSQL.
+ <para>
+ <emphasis role="strong">åå¼</emphasis>
+ <literal>full.classname.of.Dialect</literal>
+
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.show_sql</literal>
+ </entry>
+ <entry>
+ è¾åºææSQLè¯å¥å°æ§å¶å°.
+ æä¸ä¸ªå¦å¤çéæ©æ¯æ<literal>org.hibernate.SQL</literal>è¿ä¸ªlog category设为<literal>debug</literal>ã
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.format_sql</literal>
+ </entry>
+ <entry>
+ å¨logåconsoleä¸æå°åºæ´æ¼äº®çSQLã
+ <para>
+ <emphasis role="strong">åå¼</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.default_schema</literal>
+ </entry>
+ <entry>
+ å¨çæçSQLä¸, å°ç»å®çschema/tablespaceéå äºéå
¨éå®åç表åä¸.
+ <para>
+ <emphasis role="strong">åå¼</emphasis>
+ <literal>SCHEMA_NAME</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.default_catalog</literal>
+ </entry>
+ <entry>
+ å¨çæçSQLä¸, å°ç»å®çcatalogéå äºéå
¨éå®åç表åä¸.
+ <para>
+ <emphasis role="strong">åå¼</emphasis>
+ <literal>CATALOG_NAME</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.session_factory_name</literal>
+ </entry>
+ <entry>
+ <literal>SessionFactory</literal>å建åï¼å°èªå¨ä½¿ç¨è¿ä¸ªååç»å®å°JNDIä¸.
+ <para>
+ <emphasis role="strong">åå¼</emphasis>
+ <literal>jndi/composite/name</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.max_fetch_depth</literal>
+ </entry>
+ <entry>
+ 为ååå
³è(ä¸å¯¹ä¸, å¤å¯¹ä¸)çå¤è¿æ¥æåï¼outer join fetchï¼æ 设置æ大深度.
+ å¼ä¸º<literal>0</literal>æå³çå°å
³éé»è®¤çå¤è¿æ¥æå.
+ <para>
+ <emphasis role="strong">åå¼</emphasis>
+ 建议å¨<literal>0</literal>å°<literal>3</literal>ä¹é´åå¼
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.default_batch_fetch_size</literal>
+ </entry>
+ <entry>
+ 为Hibernateå
³èçæ¹éæå设置é»è®¤æ°é.
+ <para>
+ <emphasis role="strong">åå¼</emphasis>
+ 建议çåå¼ä¸º<literal>4</literal>, <literal>8</literal>,
+ å<literal>16</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.default_entity_mode</literal>
+ </entry>
+ <entry>
+ 为ç±è¿ä¸ª<literal>SessionFactory</literal>æå¼çææSessionæå®é»è®¤çå®ä½è¡¨ç°æ¨¡å¼.
+ <para>
+ <emphasis role="strong">åå¼</emphasis>
+ <literal>dynamic-map</literal>, <literal>dom4j</literal>,
+ <literal>pojo</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.order_updates</literal>
+ </entry>
+ <entry>
+ 强å¶Hibernateæç
§è¢«æ´æ°æ°æ®ç主é®ï¼ä¸ºSQLæ´æ°æåºãè¿ä¹åå°åå°å¨é«å¹¶åç³»ç»ä¸äºå¡çæ»éã
+ <para>
+ <emphasis role="strong">åå¼</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.generate_statistics</literal>
+ </entry>
+ <entry>
+ å¦æå¼å¯, Hibernateå°æ¶éæå©äºæ§è½è°èçç»è®¡æ°æ®.
+ <para>
+ <emphasis role="strong">åå¼</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.use_identifer_rollback</literal>
+ </entry>
+ <entry>
+ å¦æå¼å¯, å¨å¯¹è±¡è¢«å é¤æ¶çæçæ è¯å±æ§å°è¢«é设为é»è®¤å¼.
+ <para>
+ <emphasis role="strong">åå¼</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.use_sql_comments</literal>
+ </entry>
+ <entry>
+ å¦æå¼å¯, Hibernateå°å¨SQLä¸çææå©äºè°è¯ç注éä¿¡æ¯, é»è®¤å¼ä¸º<literal>false</literal>.
+ <para>
+ <emphasis role="strong">åå¼</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame="topbot" id="configuration-jdbc-properties" revision="8">
+ <title>
+ Hibernate JDBCåè¿æ¥(connection)å±æ§
+ </title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>
+ å±æ§å
+ </entry>
+ <entry>
+ ç¨é
+ </entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.fetch_size</literal>
+ </entry>
+ <entry>
+ éé¶å¼ï¼æå®JDBCæåæ°éçå¤§å° (è°ç¨<literal>Statement.setFetchSize()</literal>).
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.batch_size</literal>
+ </entry>
+ <entry>
+ éé¶å¼ï¼å
许Hibernate使ç¨JDBC2çæ¹éæ´æ°.
+ <para>
+ <emphasis role="strong">åå¼</emphasis>
+ 建议å<literal>5</literal>å°<literal>30</literal>ä¹é´çå¼
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.batch_versioned_data</literal>
+ </entry>
+ <entry>
+ å¦æä½ æ³è®©ä½ çJDBC驱å¨ä»<literal>executeBatch()</literal>è¿åæ£ç¡®çè¡è®¡æ° ,
+ é£ä¹å°æ¤å±æ§è®¾ä¸º<literal>true</literal>(å¼å¯è¿ä¸ªé项é常æ¯å®å
¨ç).
+ åæ¶ï¼Hibernateå°ä¸ºèªå¨çæ¬åçæ°æ®ä½¿ç¨æ¹éDML. é»è®¤å¼ä¸º<literal>false</literal>.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.factory_class</literal>
+ </entry>
+ <entry>
+ éæ©ä¸ä¸ªèªå®ä¹ç<literal>Batcher</literal>. å¤æ°åºç¨ç¨åºä¸éè¦è¿ä¸ªé
ç½®å±æ§.
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>classname.of.Batcher</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.use_scrollable_resultset</literal>
+ </entry>
+ <entry>
+ å
许Hibernate使ç¨JDBC2çå¯æ»å¨ç»æé.
+ åªæå¨ä½¿ç¨ç¨æ·æä¾çJDBCè¿æ¥æ¶ï¼è¿ä¸ªé项ææ¯å¿
è¦ç,
+ å¦åHibernateä¼ä½¿ç¨è¿æ¥çå
æ°æ®.
+ <para>
+ <emphasis role="strong">åå¼</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.use_streams_for_binary</literal>
+ </entry>
+ <entry>
+ å¨JDBC读å<literal>binary (äºè¿å¶)</literal>æ<literal>serializable (å¯åºåå)</literal>
+ çç±»åæ¶ä½¿ç¨æµ(stream)(ç³»ç»çº§å±æ§).
+ <para>
+ <emphasis role="strong">åå¼</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jdbc.use_get_generated_keys</literal>
+ </entry>
+ <entry>
+ å¨æ°æ®æå
¥æ°æ®åºä¹åï¼å
许使ç¨JDBC3 <literal>PreparedStatement.getGeneratedKeys()</literal>
+ æ¥è·åæ°æ®åºçæçkey(é®)ãéè¦JDBC3+驱å¨åJRE1.4+, å¦æä½ çæ°æ®åºé©±å¨å¨ä½¿ç¨Hibernateçæ
+ è¯çæå¨æ¶éå°é®é¢ï¼è¯·å°æ¤å¼è®¾ä¸ºfalse. é»è®¤æ
åµä¸å°ä½¿ç¨è¿æ¥çå
æ°æ®æ¥å¤å®é©±å¨çè½å.
+ <para>
+ <emphasis role="strong">åå¼</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.provider_class</literal>
+ </entry>
+ <entry>
+ èªå®ä¹<literal>ConnectionProvider</literal>çç±»å, æ¤ç±»ç¨æ¥åHibernateæä¾JDBCè¿æ¥.
+ <para>
+ <emphasis role="strong">åå¼</emphasis>
+ <literal>classname.of.ConnectionProvider</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.isolation</literal>
+ </entry>
+ <entry>
+ 设置JDBCäºå¡é离级å«. æ¥ç<literal>java.sql.Connection</literal>æ¥äºè§£å个å¼çå
·ä½æä¹,
+ ä½è¯·æ³¨æå¤æ°æ°æ®åºé½ä¸æ¯æææçé离级å«.
+ <para>
+ <emphasis role="strong">åå¼</emphasis>
+ <literal>1, 2, 4, 8</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.autocommit</literal>
+ </entry>
+ <entry>
+ å
许被ç¼åçJDBCè¿æ¥å¼å¯èªå¨æ交(autocommit) (ä¸å»ºè®®).
+ <para>
+ <emphasis role="strong">åå¼</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.release_mode</literal>
+ </entry>
+ <entry>
+ æå®Hibernateå¨ä½æ¶éæ¾JDBCè¿æ¥. é»è®¤æ
åµä¸,ç´å°Session被æ¾å¼å
³éæ被æå¼è¿æ¥æ¶,æä¼éæ¾JDBCè¿æ¥.
+ 对äºåºç¨ç¨åºæå¡å¨çJTAæ°æ®æº, ä½ åºå½ä½¿ç¨<literal>after_statement</literal>, è¿æ ·å¨æ¯æ¬¡JDBCè°ç¨åï¼é½ä¼ä¸»å¨çéæ¾è¿æ¥.
+ 对äºéJTAçè¿æ¥, 使ç¨<literal>after_transaction</literal>å¨æ¯ä¸ªäºå¡ç»ææ¶éæ¾è¿æ¥æ¯åçç.
+ <literal>auto</literal>å°ä¸ºJTAåCMTäºå¡çç¥éæ©<literal>after_statement</literal>,
+ 为JDBCäºå¡çç¥éæ©<literal>after_transaction</literal>.
+ <para>
+ <emphasis role="strong">åå¼</emphasis>
+ <literal>auto</literal> (é»è®¤) | <literal>on_close</literal> |
+ <literal>after_transaction</literal> | <literal>after_statement</literal>
+ </para>
+ <para>
+
+ 注æ,è¿äºè®¾ç½®ä»
对éè¿<literal>SessionFactory.openSession</literal>å¾å°ç<literal>Session</literal>èµ·ä½ç¨ã对äºéè¿<literal>SessionFactory.getCurrentSession</literal>å¾å°ç<literal>Session</literal>ï¼æé
ç½®ç<literal>CurrentSessionContext</literal>å®ç°æ§å¶è¿äº<literal>Session</literal>çè¿æ¥éæ¾æ¨¡å¼ã请åé
<xref linkend="architecture-current-session"/>ã
+ </para>
+
+
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.connection.<emphasis><propertyName></emphasis></literal>
+ </entry>
+ <entry>
+ å°JDBCå±æ§<literal>propertyName</literal>ä¼ éå°<literal>DriverManager.getConnection()</literal>ä¸å».
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.jndi.<emphasis><propertyName></emphasis></literal>
+ </entry>
+ <entry>
+ å°å±æ§<literal>propertyName</literal>ä¼ éå°JNDI <literal>InitialContextFactory</literal>ä¸å».
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame="topbot" id="configuration-cache-properties" revision="7">
+ <title>
+ Hibernateç¼åå±æ§
+ </title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>
+ å±æ§å
+ </entry>
+ <entry>
+ ç¨é
+ </entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.cache.provider_class</literal>
+ </entry>
+ <entry>
+ èªå®ä¹ç<literal>CacheProvider</literal>çç±»å.
+ <para>
+ <emphasis role="strong">åå¼</emphasis>
+ <literal>classname.of.CacheProvider</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.use_minimal_puts</literal>
+ </entry>
+ <entry>
+ 以é¢ç¹ç读æä½ä¸ºä»£ä»·, ä¼åäºçº§ç¼åæ¥æå°ååæä½. å¨Hibernate3ä¸ï¼è¿ä¸ªè®¾ç½®å¯¹çé群ç¼åé常æç¨,
+ 对é群ç¼åçå®ç°èè¨ï¼é»è®¤æ¯å¼å¯ç.
+ <para>
+ <emphasis role="strong">åå¼</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.use_query_cache</literal>
+ </entry>
+ <entry>
+ å
许æ¥è¯¢ç¼å, 个å«æ¥è¯¢ä»ç¶éè¦è¢«è®¾ç½®ä¸ºå¯ç¼åç.
+ <para>
+ <emphasis role="strong">åå¼</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.use_second_level_cache</literal>
+ </entry>
+ <entry>
+ è½ç¨æ¥å®å
¨ç¦æ¢ä½¿ç¨äºçº§ç¼å. 对é£äºå¨ç±»çæ å°å®ä¹ä¸æå®<literal><cache></literal>çç±»ï¼ä¼é»è®¤å¼å¯äºçº§ç¼å.
+ <para>
+ <emphasis role="strong">åå¼</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.query_cache_factory</literal>
+ </entry>
+ <entry>
+ èªå®ä¹å®ç°<literal>QueryCache</literal>æ¥å£çç±»å,
+ é»è®¤ä¸ºå
建ç<literal>StandardQueryCache</literal>.
+ <para>
+ <emphasis role="strong">åå¼</emphasis>
+ <literal>classname.of.QueryCache</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.region_prefix</literal>
+ </entry>
+ <entry>
+ äºçº§ç¼ååºååçåç¼.
+ <para>
+ <emphasis role="strong">åå¼</emphasis>
+ <literal>prefix</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cache.use_structured_entries</literal>
+ </entry>
+ <entry>
+ 强å¶Hibernate以æ´äººæ§åçæ ¼å¼å°æ°æ®åå
¥äºçº§ç¼å.
+ <para>
+ <emphasis role="strong">åå¼</emphasis>
+ <literal>true|false</literal>
+ </para>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame="topbot" id="configuration-transaction-properties" revision="9">
+ <title>
+ Hibernateäºå¡å±æ§
+ </title>
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>
+ å±æ§å
+ </entry>
+ <entry>
+ ç¨é
+ </entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.transaction.factory_class</literal>
+ </entry>
+ <entry>
+ ä¸ä¸ª<literal>TransactionFactory</literal>çç±»å, ç¨äºHibernate <literal>Transaction</literal> API
+ (é»è®¤ä¸º<literal>JDBCTransactionFactory</literal>).
+ <para>
+ <emphasis role="strong">åå¼</emphasis>
+ <literal>classname.of.TransactionFactory</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>jta.UserTransaction</literal>
+ </entry>
+ <entry>
+ ä¸ä¸ªJNDIååï¼è¢«<literal>JTATransactionFactory</literal>ç¨æ¥ä»åºç¨æå¡å¨è·åJTA <literal>UserTransaction</literal>.
+ <para>
+ <emphasis role="strong">åå¼</emphasis>
+ <literal>jndi/composite/name</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.transaction.manager_lookup_class</literal>
+ </entry>
+ <entry>
+ ä¸ä¸ª<literal>TransactionManagerLookup</literal>çç±»å
+ - å½ä½¿ç¨JVM级ç¼åï¼æå¨JTAç¯å¢ä¸ä½¿ç¨hiloçæå¨çæ¶åéè¦è¯¥ç±».
+ <para>
+ <emphasis role="strong">åå¼</emphasis>
+ <literal>classname.of.TransactionManagerLookup</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.transaction.flush_before_completion</literal>
+ </entry>
+ <entry>
+ å¦æå¼å¯, sessionå¨äºå¡å®æåå°è¢«èªå¨æ¸
æ´(flush)ã ç°å¨æ´å¥½çæ¹æ³æ¯ä½¿ç¨èªå¨sessionä¸ä¸æ管çã请åè§<xref linkend="architecture-current-session"/>ã
+ <para>
+ <emphasis role="strong">åå¼</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.transaction.auto_close_session</literal>
+ </entry>
+ <entry>
+ å¦æå¼å¯, sessionå¨äºå¡å®æåå°è¢«èªå¨å
³éã ç°å¨æ´å¥½çæ¹æ³æ¯ä½¿ç¨èªå¨sessionä¸ä¸æ管çã请åè§<xref linkend="architecture-current-session"/>ã
+ <para>
+ <emphasis role="strong">åå¼</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame="topbot" id="configuration-misc-properties" revision="10">
+ <title>
+ å
¶ä»å±æ§
+ </title>
+
+ <tgroup cols="2">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>
+ å±æ§å
+ </entry>
+ <entry>
+ ç¨é
+ </entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>hibernate.current_session_context_class</literal>
+ </entry>
+ <entry>
+ 为"å½å"
+ <literal>Session</literal>æå®ä¸ä¸ª(èªå®ä¹ç)çç¥ãå
³äºå
ç½®çç¥ç详æ
ï¼è¯·åè§<xref linkend="architecture-current-session"/> ã
+ <para>
+ <emphasis role="strong">eg.</emphasis>
+ <literal>jta</literal> | <literal>thread</literal> |
+ <literal>managed</literal> | <literal>custom.Class</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.query.factory_class</literal>
+ </entry>
+ <entry>
+ éæ©HQL解æå¨çå®ç°.
+ <para>
+ <emphasis role="strong">åå¼</emphasis>
+ <literal>org.hibernate.hql.ast.ASTQueryTranslatorFactory</literal> or
+ <literal>org.hibernate.hql.classic.ClassicQueryTranslatorFactory</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.query.substitutions</literal>
+ </entry>
+ <entry>
+ å°Hibernateæ¥è¯¢ä¸ç符å·æ å°å°SQLæ¥è¯¢ä¸ç符å·
+ (符å·å¯è½æ¯å½æ°åæ常éåå).
+ <para>
+ <emphasis role="strong">åå¼</emphasis>
+ <literal>hqlLiteral=SQL_LITERAL, hqlFunction=SQLFUNC</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.hbm2ddl.auto</literal>
+ </entry>
+ <entry>
+ å¨<literal>SessionFactory</literal>å建æ¶ï¼èªå¨æ£æ¥æ°æ®åºç»æï¼æè
å°æ°æ®åºschemaçDDL导åºå°æ°æ®åº. 使ç¨
+ <literal>create-drop</literal>æ¶,å¨æ¾å¼å
³é<literal>SessionFactory</literal>æ¶ï¼å°dropææ°æ®åºschema.
+ <para>
+ <emphasis role="strong">åå¼</emphasis>
+ <literal>validate</literal> | <literal>update</literal> |
+ <literal>create</literal> | <literal>create-drop</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>hibernate.cglib.use_reflection_optimizer</literal>
+ </entry>
+ <entry>
+ å¼å¯CGLIBæ¥æ¿ä»£è¿è¡æ¶åå°æºå¶(ç³»ç»çº§å±æ§). åå°æºå¶ææ¶å¨é¤éæ¶æ¯è¾æç¨.
+ 注æå³ä½¿å
³éè¿ä¸ªä¼å, Hibernateè¿æ¯éè¦CGLIB. ä½ ä¸è½å¨<literal>hibernate.cfg.xml</literal>ä¸è®¾ç½®æ¤å±æ§.
+ <para>
+ <emphasis role="strong">åå¼</emphasis>
+ <literal>true</literal> | <literal>false</literal>
+ </para>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <sect2 id="configuration-optional-dialects" revision="1">
+ <title>
+ SQLæ¹è¨
+ </title>
+
+ <para>
+ ä½ åºå½æ»æ¯ä¸ºä½ çæ°æ®åºå°<literal>hibernate.dialect</literal>å±æ§è®¾ç½®ææ£ç¡®ç
+ <literal>org.hibernate.dialect.Dialect</literal>åç±». å¦æä½ æå®ä¸ç§æ¹è¨,
+ Hibernateå°ä¸ºä¸é¢ååºçä¸äºå±æ§ä½¿ç¨åççé»è®¤å¼, ä¸ºä½ çå»äºæå·¥æå®å®ä»¬çå夫.
+ </para>
+
+ <table frame="topbot" id="sql-dialects" revision="2">
+ <title>
+ Hibernate SQLæ¹è¨ (<literal>hibernate.dialect</literal>)
+ </title>
+ <tgroup cols="2">
+ <colspec colwidth="1*"/>
+ <colspec colwidth="2.5*"/>
+ <thead>
+ <row>
+ <entry>RDBMS</entry>
+ <entry>
+ æ¹è¨
+ </entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>DB2</entry> <entry><literal>org.hibernate.dialect.DB2Dialect</literal></entry>
+ </row>
+ <row>
+ <entry>DB2 AS/400</entry> <entry><literal>org.hibernate.dialect.DB2400Dialect</literal></entry>
+ </row>
+ <row>
+ <entry>DB2 OS390</entry> <entry><literal>org.hibernate.dialect.DB2390Dialect</literal></entry>
+ </row>
+ <row>
+ <entry>PostgreSQL</entry> <entry><literal>org.hibernate.dialect.PostgreSQLDialect</literal></entry>
+ </row>
+ <row>
+ <entry>MySQL</entry> <entry><literal>org.hibernate.dialect.MySQLDialect</literal></entry>
+ </row>
+ <row>
+ <entry>MySQL with InnoDB</entry> <entry><literal>org.hibernate.dialect.MySQLInnoDBDialect</literal></entry>
+ </row>
+ <row>
+ <entry>MySQL with MyISAM</entry> <entry><literal>org.hibernate.dialect.MySQLMyISAMDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Oracle (any version)</entry> <entry><literal>org.hibernate.dialect.OracleDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Oracle 9i/10g</entry> <entry><literal>org.hibernate.dialect.Oracle9Dialect</literal></entry>
+ </row>
+ <row>
+ <entry>Sybase</entry> <entry><literal>org.hibernate.dialect.SybaseDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Sybase Anywhere</entry> <entry><literal>org.hibernate.dialect.SybaseAnywhereDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Microsoft SQL Server</entry> <entry><literal>org.hibernate.dialect.SQLServerDialect</literal></entry>
+ </row>
+ <row>
+ <entry>SAP DB</entry> <entry><literal>org.hibernate.dialect.SAPDBDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Informix</entry> <entry><literal>org.hibernate.dialect.InformixDialect</literal></entry>
+ </row>
+ <row>
+ <entry>HypersonicSQL</entry> <entry><literal>org.hibernate.dialect.HSQLDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Ingres</entry> <entry><literal>org.hibernate.dialect.IngresDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Progress</entry> <entry><literal>org.hibernate.dialect.ProgressDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Mckoi SQL</entry> <entry><literal>org.hibernate.dialect.MckoiDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Interbase</entry> <entry><literal>org.hibernate.dialect.InterbaseDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Pointbase</entry> <entry><literal>org.hibernate.dialect.PointbaseDialect</literal></entry>
+ </row>
+ <row>
+ <entry>FrontBase</entry> <entry><literal>org.hibernate.dialect.FrontbaseDialect</literal></entry>
+ </row>
+ <row>
+ <entry>Firebird</entry> <entry><literal>org.hibernate.dialect.FirebirdDialect</literal></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-outerjoin" revision="4">
+ <title>
+ å¤è¿æ¥æå(Outer Join Fetching)
+ </title>
+
+ <para>
+ å¦æä½ çæ°æ®åºæ¯æANSI, OracleæSybaseé£æ ¼çå¤è¿æ¥, <emphasis>å¤è¿æ¥æå</emphasis>é常è½éè¿éå¶å¾è¿æ°æ®åºæ¬¡æ°
+ (æ´å¤çå·¥ä½äº¤ç±æ°æ®åºèªå·±æ¥å®æ)æ¥æé«æç. å¤è¿æ¥æåå
许å¨å个<literal>SELECT</literal>SQLè¯å¥ä¸ï¼
+ éè¿many-to-one, one-to-many, many-to-manyåone-to-oneå
³èè·åè¿æ¥å¯¹è±¡çæ´ä¸ªå¯¹è±¡å¾.
+ </para>
+
+ <para>
+ å°<literal>hibernate.max_fetch_depth</literal>设为<literal>0</literal>è½å¨<emphasis>å
¨å±</emphasis>
+ èå´å
ç¦æ¢å¤è¿æ¥æå. 设为<literal>1</literal>ææ´é«å¼è½å¯ç¨one-to-oneåmany-to-oneouterå
³èçå¤è¿æ¥æå, å®ä»¬éè¿
+ <literal>fetch="join"</literal>æ¥æ å°.
+ </para>
+
+ <para>
+ åè§<xref linkend="performance-fetching"/>è·å¾æ´å¤ä¿¡æ¯.
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-binarystreams" revision="1">
+ <title>
+ äºè¿å¶æµ (Binary Streams)
+ </title>
+
+ <para>
+ Oracleéå¶é£äºéè¿JDBC驱å¨ä¼ è¾ç<literal>åè</literal>æ°ç»çæ°ç®. å¦æä½ å¸æ使ç¨<literal>äºè¿å¼ (binary)</literal>æ
+ <literal>å¯åºååç (serializable)</literal>ç±»åç大对象, ä½ åºè¯¥å¼å¯
+ <literal>hibernate.jdbc.use_streams_for_binary</literal>å±æ§.
+ <emphasis>è¿æ¯ç³»ç»çº§å±æ§.</emphasis>
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-cacheprovider" revision="2">
+ <title>
+ äºçº§ç¼åä¸æ¥è¯¢ç¼å
+ </title>
+
+ <para>
+ 以<literal>hibernate.cache</literal>为åç¼çå±æ§å
è®¸ä½ å¨Hibernateä¸ï¼ä½¿ç¨è¿ç¨æ群éèå´å
çäºçº§ç¼åç³»ç».
+ åè§<xref linkend="performance-cache"/>è·åæ´å¤ç详æ
.
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-querysubstitution">
+ <title>
+ æ¥è¯¢è¯è¨ä¸çæ¿æ¢
+ </title>
+
+ <para>
+ ä½ å¯ä»¥ä½¿ç¨<literal>hibernate.query.substitutions</literal>å¨Hibernateä¸å®ä¹æ°çæ¥è¯¢ç¬¦å·.
+ ä¾å¦:
+ </para>
+
+ <programlisting>hibernate.query.substitutions true=1, false=0</programlisting>
+
+ <para>
+ å°å¯¼è´ç¬¦å·<literal>true</literal>å<literal>false</literal>å¨çæçSQLä¸è¢«ç¿»è¯ææ´æ°å¸¸é.
+ </para>
+
+ <programlisting>hibernate.query.substitutions toLowercase=LOWER</programlisting>
+
+ <para>
+ å°å
è®¸ä½ éå½åSQLä¸ç<literal>LOWER</literal>å½æ°.
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-statistics" revision="2">
+ <title>
+ Hibernateçç»è®¡(statistics)æºå¶
+ </title>
+
+ <para>
+ å¦æä½ å¼å¯<literal>hibernate.generate_statistics</literal>, é£ä¹å½ä½ éè¿
+ <literal>SessionFactory.getStatistics()</literal>è°æ´æ£å¨è¿è¡çç³»ç»æ¶ï¼Hibernateå°å¯¼åºå¤§éæç¨çæ°æ®.
+ Hibernateçè³è½è¢«é
ç½®æéè¿JMX导åºè¿äºç»è®¡ä¿¡æ¯. åè<literal>org.hibernate.stats</literal>ä¸æ¥å£çJavadocï¼ä»¥è·å¾æ´å¤ä¿¡æ¯.
+ </para>
+
+ </sect2>
+ </sect1>
+
+ <sect1 id="configuration-logging">
+ <title>
+ æ¥å¿
+ </title>
+
+ <para>
+ Hibernate使ç¨Apache commons-loggingæ¥ä¸ºåç§äºä»¶è®°å½æ¥å¿.
+ </para>
+
+ <para>
+ commons-loggingå°ç´æ¥è¾åºå°Apache Log4j(å¦æå¨ç±»è·¯å¾ä¸å
æ¬<literal>log4j.jar</literal>)æ
+ JDK1.4 logging (å¦æè¿è¡å¨JDK1.4æ以ä¸çç¯å¢ä¸). ä½ å¯ä»¥ä»<literal>http://jakarta.apache.org</literal>
+ ä¸è½½Log4j. è¦ä½¿ç¨Log4jï¼ä½ éè¦å°<literal>log4j.properties</literal>æ件æ¾ç½®å¨ç±»è·¯å¾ä¸, éHibernate
+ ä¸åååçæ ·ä¾å±æ§æ件å¨<literal>src/</literal>ç®å½ä¸.
+ </para>
+
+ <para>
+ æ们强çå»ºè®®ä½ çæä¸ä¸Hibernateçæ¥å¿æ¶æ¯. å¨ä¸å¤±å¯è¯»æ§çåæä¸ï¼
+ æ们åäºå¾å¤å·¥ä½ï¼ä½¿Hibernateçæ¥å¿å¯è½å°è¯¦ç». è¿æ¯å¿
è¦çæ¥éå©å¨.
+ æ令人æå
´è¶£çæ¥å¿åç±»æå¦ä¸è¿äº:
+ </para>
+
+ <table frame="topbot" id="log-categories" revision="2">
+ <title>
+ Hibernateæ¥å¿ç±»å«
+ </title>
+ <tgroup cols="2">
+ <colspec colwidth="1*"/>
+ <colspec colwidth="2.5*"/>
+ <thead>
+ <row>
+ <entry>
+ ç±»å«
+ </entry>
+ <entry>
+ åè½
+ </entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>org.hibernate.SQL</literal></entry>
+ <entry>
+ å¨ææSQL DMLè¯å¥è¢«æ§è¡æ¶ä¸ºå®ä»¬è®°å½æ¥å¿
+ </entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.type</literal></entry>
+ <entry>
+ 为ææJDBCåæ°è®°å½æ¥å¿
+ </entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.tool.hbm2ddl</literal></entry>
+ <entry>
+ å¨ææSQL DDLè¯å¥æ§è¡æ¶ä¸ºå®ä»¬è®°å½æ¥å¿
+ </entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.pretty</literal></entry>
+ <entry>
+ å¨sessionæ¸
æ´(flush)æ¶ï¼ä¸ºææä¸å
¶å
³èçå®ä½(æå¤20个)çç¶æè®°å½æ¥å¿
+ </entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.cache</literal></entry>
+ <entry>
+ 为ææäºçº§ç¼åçæ´»å¨è®°å½æ¥å¿
+ </entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction</literal></entry>
+ <entry>
+ 为äºå¡ç¸å
³çæ´»å¨è®°å½æ¥å¿
+ </entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.jdbc</literal></entry>
+ <entry>
+ 为ææJDBCèµæºçè·åè®°å½æ¥å¿
+ </entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.hql.AST</literal></entry>
+ <entry>
+ å¨è§£ææ¥è¯¢çæ¶å,è®°å½HQLåSQLçASTåææ¥å¿
+ </entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.secure</literal></entry>
+ <entry>
+ 为JAAS认è¯è¯·æ±åæ¥å¿
+ </entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate</literal></entry>
+ <entry>
+ 为任ä½Hibernateç¸å
³ä¿¡æ¯åæ¥å¿ (ä¿¡æ¯éè¾å¤§, ä½å¯¹æ¥éé常æ帮å©)
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ å¨ä½¿ç¨Hibernateå¼ååºç¨ç¨åºæ¶, ä½ åºå½æ»æ¯ä¸º<literal>org.hibernate.SQL</literal>
+ å¼å¯<literal>debug</literal>级å«çæ¥å¿è®°å½,æè
å¼å¯<literal>hibernate.show_sql</literal>å±æ§ã
+ </para>
+
+ </sect1>
+
+ <sect1 id="configuration-namingstrategy">
+ <title>
+ å®ç°<literal>NamingStrategy</literal>
+ </title>
+
+ <para>
+ <literal>org.hibernate.cfg.NamingStrategy</literal>æ¥å£å
è®¸ä½ ä¸ºæ°æ®åºä¸ç对象åschema
+ å
ç´ æå®ä¸ä¸ªâå½åæ åâ.
+ </para>
+
+ <para>
+ ä½ å¯è½ä¼æä¾ä¸äºéè¿Javaæ è¯çææ°æ®åºæ è¯æå°æ å°å®ä¹æ件ä¸"é»è¾"表/ååå¤çæ"ç©ç"表/ååçè§å.
+ è¿ä¸ªç¹æ§æå©äºåå°åé¿çæ å°å®ä¹æ件.
+ </para>
+
+ <para>
+ å¨å å
¥æ å°å®ä¹åï¼ä½ å¯ä»¥è°ç¨
+ <literal>Configuration.setNamingStrategy()</literal>æå®ä¸ä¸ªä¸åçå½åçç¥:
+ </para>
+
+ <programlisting><![CDATA[SessionFactory sf = new Configuration()
+ .setNamingStrategy(ImprovedNamingStrategy.INSTANCE)
+ .addFile("Item.hbm.xml")
+ .addFile("Bid.hbm.xml")
+ .buildSessionFactory();]]></programlisting>
+
+ <para>
+ <literal>org.hibernate.cfg.ImprovedNamingStrategy</literal>æ¯ä¸ä¸ªå
建çå½åçç¥, 对
+ ä¸äºåºç¨ç¨åºèè¨ï¼å¯è½æ¯é常æç¨çèµ·ç¹.
+ </para>
+
+ </sect1>
+
+ <sect1 id="configuration-xmlconfig" revision="2">
+ <title>
+ XMLé
ç½®æ件
+ </title>
+
+ <para>
+ å¦ä¸ä¸ªé
ç½®æ¹æ³æ¯å¨<literal>hibernate.cfg.xml</literal>æ件ä¸æå®ä¸å¥å®æ´çé
ç½®.
+ è¿ä¸ªæ件å¯ä»¥å½æ<literal>hibernate.properties</literal>çæ¿ä»£ã è¥ä¸¤ä¸ªæ件åæ¶åå¨ï¼å®å°è¦çåè
çå±æ§.
+ </para>
+
+ <para>
+ XMLé
ç½®æ件被é»è®¤æ¯æ¾å¨<literal>CLASSPATH</literal>çæ ¹ç®å½ä¸. è¿æ¯ä¸ä¸ªä¾å:
+ </para>
+ <para>
+
+ </para>
+
+ <programlisting><![CDATA[<?xml version='1.0' encoding='utf-8'?>
+<!DOCTYPE hibernate-configuration PUBLIC
+ "-//Hibernate/Hibernate Configuration DTD//EN"
+ "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
+
+<hibernate-configuration>
+
+ <!-- 以/jndi/nameç»å®å°JNDIçSessionFactoryå®ä¾ -->
+ <session-factory
+ name="java:hibernate/SessionFactory">
+
+ <!-- å±æ§ -->
+ <property name="connection.datasource">java:/comp/env/jdbc/MyDB</property>
+ <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
+ <property name="show_sql">false</property>
+ <property name="transaction.factory_class">
+ org.hibernate.transaction.JTATransactionFactory
+ </property>
+ <property name="jta.UserTransaction">java:comp/UserTransaction</property>
+
+ <!-- æ å°å®ä¹æ件 -->
+ <mapping resource="org/hibernate/auction/Item.hbm.xml"/>
+ <mapping resource="org/hibernate/auction/Bid.hbm.xml"/>
+
+ <!-- ç¼å设置 -->
+ <class-cache class="org.hibernate.auction.Item" usage="read-write"/>
+ <class-cache class="org.hibernate.auction.Bid" usage="read-only"/>
+ <collection-cache collection="org.hibernate.auction.Item.bids" usage="read-write"/>
+
+ </session-factory>
+
+</hibernate-configuration>]]></programlisting>
+
+ <para>
+ å¦ä½ æè§, è¿ä¸ªæ¹æ³ä¼å¿å¨äºï¼å¨é
ç½®æ件ä¸æåºäºæ å°å®ä¹æ件çåå. ä¸æ¦ä½ éè¦è°æ´Hibernateçç¼åï¼
+ <literal>hibernate.cfg.xml</literal>ä¹æ¯æ´æ¹ä¾¿. 注æï¼ä½¿ç¨<literal>hibernate.properties</literal>è¿æ¯
+ <literal>hibernate.cfg.xml</literal>å®å
¨æ¯ç±ä½ æ¥å³å®, é¤äºä¸é¢æå°çXMLè¯æ³çä¼å¿ä¹å¤, 两è
æ¯çä»·ç.
+ </para>
+
+ <para>
+ 使ç¨XMLé
ç½®ï¼ä½¿å¾å¯å¨Hibernateåçå¼å¸¸ç®å, å¦ä¸æ示ï¼ä¸è¡ä»£ç å°±å¯ä»¥æå®ï¼
+ </para>
+
+ <programlisting><![CDATA[SessionFactory sf = new Configuration().configure().buildSessionFactory();]]></programlisting>
+
+ <para>
+ ä½ å¯ä»¥ä½¿ç¨å¦ä¸ä»£ç æ¥æ·»å ä¸ä¸ªä¸åçXMLé
ç½®æ件
+ </para>
+
+ <programlisting><![CDATA[SessionFactory sf = new Configuration()
+ .configure("catdb.cfg.xml")
+ .buildSessionFactory();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="configuration-j2ee" revision="1">
+ <title>
+ J2EEåºç¨ç¨åºæå¡å¨çéæ
+ </title>
+
+ <para>
+ é对J2EEä½ç³»,Hibernateæå¦ä¸å 个éæçæ¹é¢:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>容å¨ç®¡ççæ°æ®æº(Container-managed datasources)</emphasis>:
+ Hibernateè½ä½¿ç¨éè¿å®¹å¨ç®¡çï¼å¹¶ç±JNDIæä¾çJDBCè¿æ¥. é常, ç¹å«æ¯å½å¤çå¤ä¸ªæ°æ®æºçåå¸å¼äºå¡çæ¶å,
+ ç±ä¸ä¸ªJTAå
¼å®¹ç<literal>TransactionManager</literal>åä¸ä¸ª
+ <literal>ResourceManager</literal>æ¥å¤çäºå¡ç®¡ç(CMT, 容å¨ç®¡ççäºå¡). å½ç¶ä½ å¯ä»¥éè¿
+ ç¼ç¨æ¹å¼æ¥ååäºå¡è¾¹ç(BMT, Bean管ççäºå¡). æè
为äºä»£ç çå¯ç§»æ¤æ§ï¼ä½ ä¹ä¹è®¸ä¼æ³ä½¿ç¨å¯éç
+ Hibernate <literal>Transaction</literal> API.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>èªå¨JNDIç»å®</emphasis>: Hibernateå¯ä»¥å¨å¯å¨åå°
+ <literal>SessionFactory</literal>ç»å®å°JNDI.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>JTA Sessionç»å®:</emphasis> Hibernate <literal>Session</literal>
+ å¯ä»¥èªå¨ç»å®å°JTAäºå¡ä½ç¨çèå´. åªéç®åå°ä»JNDIæ¥æ¾<literal>SessionFactory</literal>并è·å¾å½åç
+ <literal>Session</literal>. å½JTAäºå¡å®ææ¶, 让Hibernateæ¥å¤ç
+ <literal>Session</literal>çæ¸
æ´(flush)ä¸å
³é. äºå¡çååå¯ä»¥æ¯å£°æå¼ç(CMT),ä¹å¯ä»¥æ¯ç¼ç¨å¼ç(BMT/UserTransaction).
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>JMXé¨ç½²:</emphasis> å¦æä½ ä½¿ç¨æ¯æJMXåºç¨ç¨åºæå¡å¨(å¦, JBoss AS), é£ä¹ä½ å¯ä»¥éæ©å°Hibernateé¨ç½²ææ管MBean.
+ è¿å°ä¸ºä½ çå»ä¸è¡ä»<literal>Configuration</literal>æ建<literal>SessionFactory</literal>çå¯å¨ä»£ç .
+ 容å¨å°å¯å¨ä½ ç<literal>HibernateService</literal>, 并å®ç¾å°å¤ç好æå¡é´çä¾èµå
³ç³» (å¨Hibernateå¯å¨åï¼æ°æ®æºå¿
é¡»æ¯å¯ç¨çï¼çç).
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+ å¦æåºç¨ç¨åºæå¡å¨æåº"connection containment"å¼å¸¸, æ ¹æ®ä½ çç¯å¢ï¼ä¹è®¸è¯¥å°é
ç½®å±æ§
+ <literal>hibernate.connection.release_mode</literal>设为<literal>after_statement</literal>.
+ </para>
+ <sect2 id="configuration-optional-transactionstrategy" revision="3">
+ <title>
+ äºå¡çç¥é
ç½®
+ </title>
+
+ <para>
+ å¨ä½ çæ¶æä¸ï¼Hibernateç<literal>Session</literal> APIæ¯ç¬ç«äºä»»ä½äºå¡åçç³»ç»ç.
+ å¦æä½ è®©Hibernateéè¿è¿æ¥æ± ç´æ¥ä½¿ç¨JDBC, ä½ éè¦è°ç¨JDBC APIæ¥æå¼åå
³éä½ çäºå¡.
+ å¦æä½ è¿è¡å¨J2EEåºç¨ç¨åºæå¡å¨ä¸, ä½ ä¹è®¸æ³ç¨Bean管ççäºå¡å¹¶å¨éè¦çæ¶åè°ç¨JTA APIå<literal>UserTransaction</literal>.
+ </para>
+
+ <para>
+ 为äºè®©ä½ ç代ç å¨ä¸¤ç§(æå
¶ä»)ç¯å¢ä¸å¯ä»¥ç§»æ¤ï¼æ们建议使ç¨å¯éçHibernate <literal>Transaction</literal> API,
+ å®å
è£
并éèäºåºå±ç³»ç». ä½ å¿
é¡»éè¿è®¾ç½®Hibernateé
ç½®å±æ§<literal>hibernate.transaction.factory_class</literal>æ¥æå®
+ ä¸ä¸ª<literal>Transaction</literal>å®ä¾çå·¥åç±».
+ </para>
+
+ <para>
+ æä¸ä¸ªæ å(å
建)çéæ©:
+ </para>
+
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term><literal>org.hibernate.transaction.JDBCTransactionFactory</literal></term>
+ <listitem>
+ <para>
+ å§æç»æ°æ®åº(JDBC)äºå¡ï¼é»è®¤ï¼
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>org.hibernate.transaction.JTATransactionFactory</literal></term>
+ <listitem>
+ <para>
+ å¦æå¨ä¸ä¸æç¯å¢ä¸åå¨è¿è¡ççäºå¡(å¦, EJBä¼è¯Beançæ¹æ³), åå§æç»å®¹å¨ç®¡
+ ççäºå¡, å¦åï¼å°å¯å¨ä¸ä¸ªæ°çäºå¡ï¼å¹¶ä½¿ç¨Bean管ççäºå¡.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>org.hibernate.transaction.CMTTransactionFactory</literal></term>
+ <listitem>
+ <para>
+ å§æç»å®¹å¨ç®¡ççJTAäºå¡
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>
+ ä½ ä¹å¯ä»¥å®ä¹å±äºä½ èªå·±çäºå¡çç¥ (å¦, é对CORBAçäºå¡æå¡)
+ </para>
+
+ <para>
+ Hibernateçä¸äºç¹æ§ (æ¯å¦äºçº§ç¼å, Contextual Sessions with JTAçç)éè¦è®¿é®å¨æ管ç¯å¢ä¸çJTA <literal>TransactionManager</literal>.
+ ç±äºJ2EE没ææ ååä¸ä¸ªåä¸çæºå¶,Hibernateå¨åºç¨ç¨åºæå¡å¨ä¸ï¼ä½ å¿
é¡»æå®Hibernateå¦ä½è·å¾<literal>TransactionManager</literal>çå¼ç¨:
+ </para>
+
+ <table frame="topbot" id="jtamanagerlookup" revision="1">
+ <title>JTA TransactionManagers</title>
+ <tgroup cols="2">
+ <colspec colwidth="2.5*"/>
+ <colspec colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>
+ Transactionå·¥åç±»
+ </entry>
+ <entry align="center">
+ åºç¨ç¨åºæå¡å¨
+ </entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>org.hibernate.transaction.JBossTransactionManagerLookup</literal></entry>
+ <entry align="center">JBoss</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.WeblogicTransactionManagerLookup</literal></entry>
+ <entry align="center">Weblogic</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.WebSphereTransactionManagerLookup</literal></entry>
+ <entry align="center">WebSphere</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.WebSphereExtendedJTATransactionLookup</literal></entry>
+ <entry align="center">WebSphere 6</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.OrionTransactionManagerLookup</literal></entry>
+ <entry align="center">Orion</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.ResinTransactionManagerLookup</literal></entry>
+ <entry align="center">Resin</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.JOTMTransactionManagerLookup</literal></entry>
+ <entry align="center">JOTM</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.JOnASTransactionManagerLookup</literal></entry>
+ <entry align="center">JOnAS</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.JRun4TransactionManagerLookup</literal></entry>
+ <entry align="center">JRun4</entry>
+ </row>
+ <row>
+ <entry><literal>org.hibernate.transaction.BESTransactionManagerLookup</literal></entry>
+ <entry align="center">Borland ES</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect2>
+
+ <sect2 id="configuration-optional-jndi" revision="3">
+ <title>
+ JNDIç»å®ç<literal>SessionFactory</literal>
+ </title>
+
+
+ <para>
+ ä¸JNDIç»å®çHibernateç<literal>SessionFactory</literal>è½ç®åå·¥åçæ¥è¯¢ï¼ç®åå建æ°ç<literal>Session</literal>.
+ éè¦æ³¨æçæ¯è¿ä¸JNDIç»å®<literal>Datasource</literal>没æå
³ç³», å®ä»¬åªæ¯æ°å·§ç¨äºç¸åç注å表!
+ </para>
+
+ <para>
+ å¦æä½ å¸æå°<literal>SessionFactory</literal>ç»å®å°ä¸ä¸ªJNDIçåå空é´,
+ ç¨å±æ§<literal>hibernate.session_factory_name</literal>æå®ä¸ä¸ªåå(å¦, <literal>java:hibernate/SessionFactory</literal>).
+ å¦æä¸è®¾ç½®è¿ä¸ªå±æ§, <literal>SessionFactory</literal>å°ä¸ä¼è¢«ç»å®å°JNDIä¸. (å¨ä»¥åªè¯»JNDI为é»è®¤å®ç°çç¯å¢ä¸ï¼è¿ä¸ªè®¾ç½®å°¤å
¶æç¨, å¦Tomcat.)
+ </para>
+
+ <para>
+ å¨å°<literal>SessionFactory</literal>ç»å®è³JNDIæ¶, Hibernateå°ä½¿ç¨<literal>hibernate.jndi.url</literal>,
+ å<literal>hibernate.jndi.class</literal>çå¼æ¥å®ä¾ååå§ç¯å¢(initial context).
+ å¦æå®ä»¬æ²¡æ被æå®, å°ä½¿ç¨é»è®¤ç<literal>InitialContext</literal>.
+ </para>
+
+ <para>
+ å¨ä½ è°ç¨<literal>cfg.buildSessionFactory()</literal>å, Hibernateä¼èªå¨å°<literal>SessionFactory</literal>注åå°JNDI.
+ è¿æå³è¿ä½ è³å°éè¦å¨ä½ åºç¨ç¨åºçå¯å¨ä»£ç (æå·¥å
·ç±»)ä¸å®æè¿ä¸ªè°ç¨, é¤éä½ ä½¿ç¨<literal>HibernateService</literal>æ¥åJMXé¨ç½² (è§åé¢è®¨è®º).
+ </para>
+
+ <para>
+ åè¥ä½ 使ç¨JNDI <literal>SessionFactory</literal>,EJBæè
ä»»ä½å
¶å®ç±»é½å¯ä»¥ä»JNDIä¸æ¾å°æ¤<literal>SessionFactory</literal>ã
+ </para>
+
+ <para>
+ æ们建议ï¼å¨å管ççç¯å¢ä¸ï¼æ<literal>SessionFactory</literal>ç»å®å°JNDIï¼å¨å
¶å®æ
åµä¸ï¼ä½¿ç¨ä¸ä¸ª<literal>static(éæç)</literal>singletonã为äºå¨ä½ çåºç¨ç¨åºä»£ç ä¸éèè¿äºç»èï¼æ们è¿å»ºè®®ä½ ç¨ä¸ä¸ªhelperç±»æå®é
æ¥æ¾<literal>SessionFactory</literal>ç代ç éèèµ·æ¥,æ¯å¦<literal>HibernateUtil.getSessionFactory()</literal>ã注æï¼è¿ä¸ªç±»ä¹å°±å¯ä»¥æ¹ä¾¿å°å¯å¨Hibernateï¼åè§ç¬¬ä¸ç« ã
+ </para>
+
+ </sect2>
+
+ <sect2 id="configuration-j2ee-currentsession" revision="4">
+ <title>å¨JTAç¯å¢ä¸ä½¿ç¨Current Session context (å½åsessionä¸ä¸æ)管ç</title>
+
+ <para>
+ å¨Hibernateä¸ï¼ç®¡ç<literal>Session</literal>åtransactionæ好çæ¹æ³æ¯èªå¨ç"å½å"<literal>Session</literal>管çã请åè§<xref linkend="architecture-current-session">contextual sessions</xref>ä¸èç讨论ã使ç¨<literal>"jta"</literal>sessionä¸ä¸æï¼åè¥å¨å½åJTAäºå¡ä¸è¿æ²¡æHibernate<literal>Session</literal>å
³èï¼ç¬¬ä¸æ¬¡<literal>sessionFactory.getCurrentSession()</literal>è°ç¨ä¼å¯å¨ä¸ä¸ªSession,并å
³èå°å½åçJTAäºå¡ãå¨<literal>"jta"</literal>ä¸ä¸æä¸è°ç¨<literal>getCurrentSession()</literal>è·å¾ç<literal>Session</literal>ï¼ä¼è¢«è®¾ç½®ä¸ºå¨transactionå
³éçæ¶åèªå¨flushï¼æ¸
æ´ï¼ãå¨transactionå
³éä¹åèªå¨å
³éï¼æ¯å¥è¯å¥ä¹å主å¨éæ¾JDBCè¿æ¥ãè¿å°±å¯ä»¥æ ¹æ®JTAäºå¡ççå½å¨ææ¥ç®¡çä¸ä¹å
³èç<literal>Session</literal>ï¼ç¨æ·ä»£ç ä¸å°±å¯ä»¥ä¸åèèè¿äºç®¡çãä½ ç代ç ä¹å¯ä»¥éè¿<literal>UserTransaction</literal>ç¨ç¼ç¨!
æ¹å¼ä½¿ç¨JTAï¼æè
(æ们建议ï¼ä¸ºäºä¾¿äºç§»æ¤ä»£ç ï¼ä½¿ç¨Hibernateç<literal>Transaction</literal> APIæ¥è®¾ç½®transactionè¾¹çãå¦æä½ ç代ç è¿è¡å¨EJB容å¨ä¸ï¼å»ºè®®å¯¹CMT使ç¨å£°æå¼äºå¡å£°æã
+
+ </para>
+ </sect2>
+ <sect2 id="configuration-j2ee-jmx" revision="1">
+ <title>
+ JMXé¨ç½²
+ </title>
+
+ <para>
+ 为äºå°<literal>SessionFactory</literal>注åå°JNDIä¸ï¼<literal>cfg.buildSessionFactory()</literal>è¿è¡ä»£ç ä»éå¨æå¤è¢«æ§è¡.
+ ä½ å¯å¨ä¸ä¸ª<literal>static</literal>åå§åå(å<literal>HibernateUtil</literal>ä¸çé£æ ·)ä¸æ§è¡å®æå°Hibernateé¨ç½²ä¸ºä¸ä¸ª<emphasis>æ管çæå¡</emphasis>.
+ </para>
+
+ <para>
+ 为äºé¨ç½²å¨ä¸ä¸ªæ¯æJMXçåºç¨ç¨åºæå¡å¨ä¸ï¼Hibernateå
+ <literal>org.hibernate.jmx.HibernateService</literal>ä¸åååï¼å¦Jboss ASã
+ å®é
çé¨ç½²åé
ç½®æ¯ç±åºç¨ç¨åºæå¡å¨æä¾è
æå®ç. è¿éæ¯JBoss 4.0.xç<literal>jboss-service.xml</literal>æ ·ä¾:
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<server>
+
+<mbean code="org.hibernate.jmx.HibernateService"
+ name="jboss.jca:service=HibernateFactory,name=HibernateFactory">
+
+ <!-- å¿
é¡»çæå¡ -->
+ <depends>jboss.jca:service=RARDeployer</depends>
+ <depends>jboss.jca:service=LocalTxCM,name=HsqlDS</depends>
+
+ <!-- å°Hibernateæå¡ç»å®å°JNDI -->
+ <attribute name="JndiName">java:/hibernate/SessionFactory</attribute>
+
+ <!-- æ°æ®æºè®¾ç½® -->
+ <attribute name="Datasource">java:HsqlDS</attribute>
+ <attribute name="Dialect">org.hibernate.dialect.HSQLDialect</attribute>
+
+ <!-- äºå¡éæ -->
+ <attribute name="TransactionStrategy">
+ org.hibernate.transaction.JTATransactionFactory</attribute>
+ <attribute name="TransactionManagerLookupStrategy">
+ org.hibernate.transaction.JBossTransactionManagerLookup</attribute>
+ <attribute name="FlushBeforeCompletionEnabled">true</attribute>
+ <attribute name="AutoCloseSessionEnabled">true</attribute>
+
+ <!-- æåé项 -->
+ <attribute name="MaximumFetchDepth">5</attribute>
+
+ <!-- äºçº§ç¼å -->
+ <attribute name="SecondLevelCacheEnabled">true</attribute>
+ <attribute name="CacheProviderClass">org.hibernate.cache.EhCacheProvider</attribute>
+ <attribute name="QueryCacheEnabled">true</attribute>
+
+ <!-- æ¥å¿ -->
+ <attribute name="ShowSqlEnabled">true</attribute>
+
+ <!-- æ å°å®ä¹æ件 -->
+ <attribute name="MapResources">auction/Item.hbm.xml,auction/Category.hbm.xml</attribute>
+
+</mbean>
+
+</server>]]></programlisting>
+
+ <para>
+ è¿ä¸ªæ件æ¯é¨ç½²å¨<literal>META-INF</literal>ç®å½ä¸ç, 并ä¼è¢«æå
å°ä»¥<literal>.sar</literal> (service archive)为æ©å±åçJARæ件ä¸.
+ åæ¶ï¼ä½ éè¦å°Hibernateãå®æéè¦ç第ä¸æ¹åºãä½ ç¼è¯å¥½çæä¹
å类以åä½ çæ å°å®ä¹æ件æå
è¿åä¸ä¸ªææ¡£.
+ ä½ çä¼ä¸Bean(ä¸è¬ä¸ºä¼è¯Bean)å¯è½ä¼è¢«æå
æå®ä»¬èªå·±çJARæ件, ä½ä½ ä¹è®¸ä¼å°EJB JARæ件ä¸åå
å«è¿è½ç¬ç«(ç)é¨ç½²ç主æå¡ææ¡£.
+ åèJBoss ASæ档以äºè§£æ´å¤çJMXæå¡ä¸EJBé¨ç½²çä¿¡æ¯.
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+</chapter>
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/events.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/events.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/events.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,247 @@
+<chapter id="events">
+ <title>
+ æ¦æªå¨ä¸äºä»¶(Interceptors and events)
+ </title>
+
+ <para>
+ åºç¨ç¨åºè½å¤ååºHibernateå
é¨äº§ççç¹å®äºä»¶æ¯é常æç¨çãè¿æ ·å°±å
许å®ç°æäºéç¨çåè½
+ 以åå
许对Hibernateåè½è¿è¡æ©å±ã
+ </para>
+
+ <sect1 id="objectstate-interceptors" revision="3">
+ <title>
+ æ¦æªå¨(Interceptors)
+ </title>
+
+ <para>
+ <literal>Interceptor</literal>æ¥å£æä¾äºä»ä¼è¯(session)åè°(callback)åºç¨ç¨åº(application)çæºå¶ï¼
+ è¿ç§åè°æºå¶å¯ä»¥å
许åºç¨ç¨åºå¨æä¹
å对象被ä¿åãæ´æ°ãå é¤ææ¯å è½½ä¹åï¼æ£æ¥å¹¶ï¼æï¼ä¿®æ¹å
¶
+ å±æ§ãä¸ä¸ªå¯è½çç¨éï¼å°±æ¯ç¨æ¥è·è¸ªå®¡æ ¸(auditing)ä¿¡æ¯ãä¾å¦ï¼ä¸é¢çè¿ä¸ª<literal>æ¦æªå¨</literal>ï¼ä¼å¨ä¸ä¸ªå®ç°äº
+ <literal>Auditable</literal>æ¥å£ç对象被å建æ¶èªå¨å°è®¾ç½®<literal>createTimestamp</literal>å±æ§ï¼å¹¶å¨å®ç°äº
+ <literal>Auditable</literal>æ¥å£ç对象被æ´æ°æ¶ï¼åæ¥æ´æ°<literal>lastUpdateTimestamp</literal>å±æ§ã
+ </para>
+
+ <para>
+ ä½ å¯ä»¥ç´æ¥å®ç°<literal>Interceptor</literal>æ¥å£ï¼ä¹å¯ä»¥ï¼æ好ï¼ç»§æ¿èª<literal>EmptyInterceptor</literal>ã
+ </para>
+
+ <programlisting><![CDATA[package org.hibernate.test;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.Iterator;
+
+import org.hibernate.EmptyInterceptor;
+import org.hibernate.Transaction;
+import org.hibernate.type.Type;
+
+public class AuditInterceptor extends EmptyInterceptor {
+
+ private int updates;
+ private int creates;
+ private int loads;
+
+ public void onDelete(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+ // do nothing
+ }
+
+ public boolean onFlushDirty(Object entity,
+ Serializable id,
+ Object[] currentState,
+ Object[] previousState,
+ String[] propertyNames,
+ Type[] types) {
+
+ if ( entity instanceof Auditable ) {
+ updates++;
+ for ( int i=0; i < propertyNames.length; i++ ) {
+ if ( "lastUpdateTimestamp".equals( propertyNames[i] ) ) {
+ currentState[i] = new Date();
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public boolean onLoad(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+ if ( entity instanceof Auditable ) {
+ loads++;
+ }
+ return false;
+ }
+
+ public boolean onSave(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+
+ if ( entity instanceof Auditable ) {
+ creates++;
+ for ( int i=0; i<propertyNames.length; i++ ) {
+ if ( "createTimestamp".equals( propertyNames[i] ) ) {
+ state[i] = new Date();
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public void afterTransactionCompletion(Transaction tx) {
+ if ( tx.wasCommitted() ) {
+ System.out.println("Creations: " + creates + ", Updates: " + updates, "Loads: " + loads);
+ }
+ updates=0;
+ creates=0;
+ loads=0;
+ }
+
+}]]></programlisting>
+
+
+ <para>
+ æ¦æªå¨å¯ä»¥æ两ç§:<literal>Session</literal>èå´å
çï¼å<literal>SessionFactory</literal>èå´å
çã
+ </para>
+
+ <para>
+ å½ä½¿ç¨æ个éè½½çSessionFactory.openSession()使ç¨<literal>Interceptor</literal>ä½ä¸ºåæ°è°ç¨æå¼ä¸ä¸ªsessionçæ¶åï¼å°±æå®äº<literal>Session</literal>èå´å
çæ¦æªå¨ã
+
+ </para>
+
+ <programlisting><![CDATA[Session session = sf.openSession( new AuditInterceptor() );]]></programlisting>
+
+ <para>
+ <literal>SessionFactory</literal>èå´å
çæ¦æªå¨è¦éè¿<literal>Configuration</literal>ä¸æ³¨åï¼èè¿å¿
é¡»å¨å建<literal>SessionFactory</literal>ä¹åãå¨è¿ç§æ
åµä¸ï¼ç»åºçæ¦æªå¨ä¼è¢«è¿ä¸ª<literal>SessionFactory</literal>ææå¼çææsession使ç¨äºï¼é¤ésessionæå¼æ¶æç¡®ææäºä½¿ç¨çæ¦æªå¨ã<literal>SessionFactory</literal>èå´å
çæ¦æªå¨ï¼å¿
é¡»æ¯çº¿ç¨å®å
¨çï¼å 为å¤ä¸ªsessionå¯è½å¹¶å使ç¨è¿ä¸ªæ¦æªå¨ï¼è¦å æ¤å°å¿ä¸è¦ä¿åä¸sessionç¸å
³çç¶æã
+ </para>
+
+ <programlisting><![CDATA[new Configuration().setInterceptor( new AuditInterceptor() );]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="objectstate-events" revision="4">
+ <title>
+ äºä»¶ç³»ç»(Event system)
+ </title>
+
+ <para>
+ å¦æéè¦ååºæä¹
å±çæäºç¹æ®äºä»¶ï¼ä½ ä¹å¯ä»¥ä½¿ç¨Hibernate3çäºä»¶æ¡æ¶ã
+ 该äºä»¶ç³»ç»å¯ä»¥ç¨æ¥æ¿ä»£æ¦æªå¨ï¼ä¹å¯ä»¥ä½ä¸ºæ¦æªå¨çè¡¥å
æ¥ä½¿ç¨ã
+
+ </para>
+
+ <para>
+ åºæ¬ä¸ï¼<literal>Session</literal>æ¥å£çæ¯ä¸ªæ¹æ³é½æç¸å¯¹åºçäºä»¶ãæ¯å¦
+ <literal>LoadEvent</literal>ï¼<literal>FlushEvent</literal>ï¼ççï¼æ¥é
XMLé
ç½®æ件
+ çDTDï¼ä»¥å<literal>org.hibernate.event</literal>å
æ¥è·å¾ææå·²å®ä¹çäºä»¶çå表ï¼ãå½æ个æ¹
+ æ³è¢«è°ç¨æ¶ï¼Hibernate <literal>Session</literal>ä¼çæä¸ä¸ªç¸å¯¹åºçäºä»¶å¹¶æ¿æ´»æ
+ æé
置好çäºä»¶çå¬å¨ãç³»ç»é¢è®¾ççå¬å¨å®ç°çå¤çè¿ç¨å°±æ¯è¢«çå¬çæ¹æ³è¦åçï¼è¢«çå¬çæ¹æ³æåçå
¶å®ä»
ä»
æ¯æ¿æ´»çå¬å¨ï¼
+ âå®é
âçå·¥ä½æ¯ç±çå¬å¨å®æçï¼ãä¸è¿ï¼ä½ å¯ä»¥èªç±å°éæ©å®ç°
+ ä¸ä¸ªèªå·±å®å¶ççå¬å¨ï¼æ¯å¦ï¼å®ç°å¹¶æ³¨åç¨æ¥å¤çå¤ç<literal>LoadEvent</literal>ç<literal>LoadEventListener</literal>æ¥å£ï¼ï¼
+ æ¥è´è´£å¤çææçè°ç¨<literal>Session</literal>ç<literal>load()</literal>æ¹æ³ç请æ±ã
+ </para>
+
+ <para>
+ çå¬å¨åºè¯¥è¢«çä½æ¯åä¾(singleton)对象ï¼ä¹å°±æ¯è¯´ï¼ææåç±»åçäºä»¶çå¤çå
±äº«åä¸ä¸ªçå¬å¨å®ä¾ï¼å æ¤çå¬å¨
+ ä¸åºè¯¥ä¿åä»»ä½ç¶æï¼ä¹å°±æ¯ä¸åºè¯¥ä½¿ç¨æååéï¼ã
+ </para>
+
+ <para>
+ ç¨æ·å®å¶ççå¬å¨åºè¯¥å®ç°ä¸æè¦å¤ççäºä»¶ç¸å¯¹åºçæ¥å£ï¼æè
ä»ä¸ä¸ªåéçåºç±»ç»§æ¿ï¼çè³æ¯ä»Hibernateèªå¸¦çé»è®¤äºä»¶çå¬å¨ç±»ç»§æ¿ï¼
+ 为äºæ¹ä¾¿ä½ è¿æ ·åï¼è¿äºç±»é½è¢«å£°æænon-finalçäºï¼ãç¨æ·å®å¶ççå¬å¨å¯ä»¥éè¿ç¼ç¨ä½¿ç¨<literal>Configuration</literal>对象
+ æ¥æ³¨åï¼ä¹å¯ä»¥å¨HibernateçXMLæ ¼å¼çé
ç½®æ件ä¸è¿è¡å£°æï¼ä¸æ¯æå¨Propertiesæ ¼å¼çé
ç½®æ件声æçå¬å¨ï¼ã
+ ä¸é¢æ¯ä¸ä¸ªç¨æ·å®å¶çå è½½äºä»¶(load event)ççå¬å¨ï¼
+ </para>
+
+ <programlisting><![CDATA[public class MyLoadListener implements LoadEventListener {
+ // this is the single method defined by the LoadEventListener interface
+ public void onLoad(LoadEvent event, LoadEventListener.LoadType loadType)
+ throws HibernateException {
+ if ( !MySecurity.isAuthorized( event.getEntityClassName(), event.getEntityId() ) ) {
+ throw MySecurityException("Unauthorized access");
+ }
+ }
+}]]></programlisting>
+
+ <para>
+ ä½ è¿éè¦ä¿®æ¹ä¸å¤é
ç½®ï¼æ¥åè¯Hibernateï¼é¤äºé»è®¤ççå¬å¨ï¼è¿è¦éå éå®ççå¬å¨ã
+ </para>
+
+<programlisting><![CDATA[<hibernate-configuration>
+ <session-factory>
+ ...
+ <event type="load">
+ <listener class="com.eg.MyLoadListener"/>
+ <listener class="org.hibernate.event.def.DefaultLoadEventListener"/>
+ </event>
+ </session-factory>
+</hibernate-configuration>]]></programlisting>
+
+ <para>
+ ççç¨å¦ä¸ç§æ¹å¼ï¼éè¿ç¼ç¨çæ¹å¼æ¥æ³¨åå®ã
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = new Configuration();
+LoadEventListener[] stack = { new MyLoadListener(), new DefaultLoadEventListener() };
+cfg.EventListeners().setLoadEventListeners(stack);]]></programlisting>
+
+ <para>
+ éè¿å¨XMLé
ç½®æ件声æè注åççå¬å¨ä¸è½å
±äº«å®ä¾ãå¦æå¨å¤ä¸ª<literal><listener/></literal>èç¹ä¸ä½¿ç¨
+ äºç¸åçç±»çååï¼åæ¯ä¸ä¸ªå¼ç¨é½å°ä¼äº§çä¸ä¸ªç¬ç«çå®ä¾ãå¦æä½ éè¦å¨å¤ä¸ªçå¬å¨ç±»åä¹é´å
񄧮
+ çå¬å¨çå®ä¾ï¼åä½ å¿
须使ç¨ç¼ç¨çæ¹å¼æ¥è¿è¡æ³¨åã
+ </para>
+
+ <para>
+ 为ä»ä¹æ们å®ç°äºç¹å®çå¬å¨çæ¥å£ï¼å¨æ³¨åçæ¶åè¿è¦æç¡®æåºæ们è¦æ³¨ååªä¸ªäºä»¶ççå¬å¨å¢ï¼
+ è¿æ¯å 为ä¸ä¸ªç±»å¯è½å®ç°å¤ä¸ªçå¬å¨çæ¥å£ãå¨æ³¨åçæ¶åæç¡®æå®è¦çå¬çäºä»¶ï¼å¯ä»¥è®©å¯ç¨æè
ç¦ç¨å¯¹æ个äºä»¶ççå¬çé
置工ä½ç®åäºã
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-decl-security" revision="2">
+ <title>
+ Hibernateç声æå¼å®å
¨æºå¶
+ </title>
+ <para>
+ é常ï¼Hibernateåºç¨ç¨åºç声æå¼å®å
¨æºå¶ç±ä¼è¯å¤è§å±(session facade)æ管çã
+ ç°å¨ï¼Hibernate3å
许æäºç¹å®çè¡ä¸ºç±JACCè¿è¡è®¸å¯ç®¡çï¼ç±JAASè¿è¡ææ管çã
+ æ¬åè½æ¯ä¸ä¸ªå»ºç«å¨äºä»¶æ¡æ¶ä¹ä¸çå¯éçåè½ã
+ </para>
+
+ <para>
+ é¦å
ï¼ä½ å¿
é¡»è¦é
ç½®éå½çäºä»¶çå¬å¨ï¼event listenerï¼ï¼æ¥æ¿æ´»ä½¿ç¨JAAS管çææçåè½ã
+ </para>
+
+ <programlisting><![CDATA[<listener type="pre-delete" class="org.hibernate.secure.JACCPreDeleteEventListener"/>
+<listener type="pre-update" class="org.hibernate.secure.JACCPreUpdateEventListener"/>
+<listener type="pre-insert" class="org.hibernate.secure.JACCPreInsertEventListener"/>
+<listener type="pre-load" class="org.hibernate.secure.JACCPreLoadEventListener"/>]]></programlisting>
+
+ <para>
+ 注æï¼<literal><listener type="..." class="..."/></literal>åªæ¯<literal><event type="..."><listener class="..."/></event></literal>çç®åï¼å¯¹æ¯ä¸ä¸ªäºä»¶ç±»åé½å¿
é¡»ä¸¥æ ¼çæä¸ä¸ªçå¬å¨ä¸ä¹å¯¹åºã
+ </para>
+
+ <para>
+ æ¥ä¸æ¥ï¼ä»ç¶å¨<literal>hibernate.cfg.xml</literal>æ件ä¸ï¼ç»å®è§è²çæéï¼
+ </para>
+
+ <programlisting><![CDATA[<grant role="admin" entity-name="User" actions="insert,update,read"/>
+<grant role="su" entity-name="User" actions="*"/>]]></programlisting>
+
+ <para>
+ è¿äºè§è²çååå°±æ¯ä½ çJACC provideræå®ä¹çè§è²çååã
+ </para>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/example_mappings.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/example_mappings.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/example_mappings.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,650 @@
+<chapter id="example-mappings">
+ <title>示ä¾ï¼å¤ææ å°å®ä¾</title>
+ <para>
+ æ¬ç« å±ç¤ºäºä¸äºè¾ä¸ºå¤æçå
³ç³»æ å°ã
+ </para>
+
+ <sect1 id="example-mappings-emp">
+ <title>Employerï¼é主)/Employee(éå)</title>
+
+ <para>
+ ä¸é¢å
³äº<literal>Employer</literal> å <literal>Employee</literal>çå
³ç³»æ¨¡å使ç¨äºä¸ä¸ªçå®çå®ä½ç±»
+ (<literal>Employment</literal>)æ¥è¡¨è¿°ï¼è¿æ¯å 为对äºç¸åçéååé主å¯è½ä¼æå¤ä¸ªéä½£æ¶é´æ®µã
+ 对äºéé¢åéåå§åï¼ç¨Components建模ã
+
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/EmployerEmployee.gif" format="GIF" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/EmployerEmployee.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ æ å°æ件å¯è½æ¯è¿æ ·:
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Employer" table="employers">
+ <id name="id">
+ <generator class="sequence">
+ <param name="sequence">employer_id_seq</param>
+ </generator>
+ </id>
+ <property name="name"/>
+ </class>
+
+ <class name="Employment" table="employment_periods">
+
+ <id name="id">
+ <generator class="sequence">
+ <param name="sequence">employment_id_seq</param>
+ </generator>
+ </id>
+ <property name="startDate" column="start_date"/>
+ <property name="endDate" column="end_date"/>
+
+ <component name="hourlyRate" class="MonetaryAmount">
+ <property name="amount">
+ <column name="hourly_rate" sql-type="NUMERIC(12, 2)"/>
+ </property>
+ <property name="currency" length="12"/>
+ </component>
+
+ <many-to-one name="employer" column="employer_id" not-null="true"/>
+ <many-to-one name="employee" column="employee_id" not-null="true"/>
+
+ </class>
+
+ <class name="Employee" table="employees">
+ <id name="id">
+ <generator class="sequence">
+ <param name="sequence">employee_id_seq</param>
+ </generator>
+ </id>
+ <property name="taxfileNumber"/>
+ <component name="name" class="Name">
+ <property name="firstName"/>
+ <property name="initial"/>
+ <property name="lastName"/>
+ </component>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ ç¨<literal>SchemaExport</literal>çæ表ç»æã
+ </para>
+
+ <programlisting><![CDATA[create table employers (
+ id BIGINT not null,
+ name VARCHAR(255),
+ primary key (id)
+)
+
+create table employment_periods (
+ id BIGINT not null,
+ hourly_rate NUMERIC(12, 2),
+ currency VARCHAR(12),
+ employee_id BIGINT not null,
+ employer_id BIGINT not null,
+ end_date TIMESTAMP,
+ start_date TIMESTAMP,
+ primary key (id)
+)
+
+create table employees (
+ id BIGINT not null,
+ firstName VARCHAR(255),
+ initial CHAR(1),
+ lastName VARCHAR(255),
+ taxfileNumber VARCHAR(255),
+ primary key (id)
+)
+
+alter table employment_periods
+ add constraint employment_periodsFK0 foreign key (employer_id) references employers
+alter table employment_periods
+ add constraint employment_periodsFK1 foreign key (employee_id) references employees
+create sequence employee_id_seq
+create sequence employment_id_seq
+create sequence employer_id_seq]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-mappings-authorwork">
+ <title>Author(ä½å®¶)/Work(ä½å)</title>
+
+ <para>
+
+ èèä¸é¢ç<literal>Work</literal>,<literal>Author</literal> å <literal>Person</literal>模åçå
³ç³»ã
+ æ们ç¨å¤å¯¹å¤å
³ç³»æ¥æè¿°<literal>Work</literal> å <literal>Author</literal>ï¼
+ ç¨ä¸å¯¹ä¸å
³ç³»æ¥æè¿°<literal>Author</literal> å <literal>Person</literal>ï¼
+ å¦ä¸ç§å¯è½æ§æ¯<literal>Author</literal>继æ¿<literal>Person</literal>ã
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/AuthorWork.gif" format="GIF" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/AuthorWork.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ ä¸é¢çæ å°æ件æ£ç¡®çæè¿°äºè¿äºå
³ç³»ï¼
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Work" table="works" discriminator-value="W">
+
+ <id name="id" column="id">
+ <generator class="native"/>
+ </id>
+ <discriminator column="type" type="character"/>
+
+ <property name="title"/>
+ <set name="authors" table="author_work">
+ <key column name="work_id"/>
+ <many-to-many class="Author" column name="author_id"/>
+ </set>
+
+ <subclass name="Book" discriminator-value="B">
+ <property name="text"/>
+ </subclass>
+
+ <subclass name="Song" discriminator-value="S">
+ <property name="tempo"/>
+ <property name="genre"/>
+ </subclass>
+
+ </class>
+
+ <class name="Author" table="authors">
+
+ <id name="id" column="id">
+ <!-- The Author must have the same identifier as the Person -->
+ <generator class="assigned"/>
+ </id>
+
+ <property name="alias"/>
+ <one-to-one name="person" constrained="true"/>
+
+ <set name="works" table="author_work" inverse="true">
+ <key column="author_id"/>
+ <many-to-many class="Work" column="work_id"/>
+ </set>
+
+ </class>
+
+ <class name="Person" table="persons">
+ <id name="id" column="id">
+ <generator class="native"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+
+ æ å°ä¸æ4个表ã<literal>works</literal>, <literal>authors</literal> å <literal>persons</literal>
+ åå«ä¿åçworkï¼authoråpersonçæ°æ®ã<literal>author_work</literal>æ¯authorsåworksçå
³è表ã
+ 表ç»ææ¯ç±<literal>SchemaExport</literal>çæçã
+ </para>
+
+ <programlisting><![CDATA[create table works (
+ id BIGINT not null generated by default as identity,
+ tempo FLOAT,
+ genre VARCHAR(255),
+ text INTEGER,
+ title VARCHAR(255),
+ type CHAR(1) not null,
+ primary key (id)
+)
+
+create table author_work (
+ author_id BIGINT not null,
+ work_id BIGINT not null,
+ primary key (work_id, author_id)
+)
+
+create table authors (
+ id BIGINT not null generated by default as identity,
+ alias VARCHAR(255),
+ primary key (id)
+)
+
+create table persons (
+ id BIGINT not null generated by default as identity,
+ name VARCHAR(255),
+ primary key (id)
+)
+
+alter table authors
+ add constraint authorsFK0 foreign key (id) references persons
+alter table author_work
+ add constraint author_workFK0 foreign key (author_id) references authors
+alter table author_work
+ add constraint author_workFK1 foreign key (work_id) references works]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-mappings-customerorderproduct">
+ <title>Customer(客æ·)/Order(订å)/Product(产å)</title>
+
+ <para>
+
+ ç°å¨æ¥èè<literal>Customer</literal>,<literal>Order</literal> ï¼ <literal>LineItem</literal>
+ å <literal>Product</literal>å
³ç³»ç模åã<literal>Customer</literal> å <literal>Order</literal>ä¹é´
+ æ¯ä¸å¯¹å¤çå
³ç³»ï¼ä½æ¯æ们æä¹æ¥æè¿°<literal>Order</literal> / <literal>LineItem</literal> / <literal>Product</literal>å¢ï¼
+ æå¯ä»¥æ<literal>LineItem</literal>ä½ä¸ºæè¿°<literal>Order</literal> å <literal>Product</literal>
+ å¤å¯¹å¤å
³ç³»çå
³èç±»ï¼å¨Hibernateï¼è¿å«åç»åå
ç´ ã
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/CustomerOrderProduct.gif" format="GIF" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/CustomerOrderProduct.gif" format="GIF" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ æ å°æ件å¦ä¸ï¼
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="Customer" table="customers">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="name"/>
+ <set name="orders" inverse="true">
+ <key column="customer_id"/>
+ <one-to-many class="Order"/>
+ </set>
+ </class>
+
+ <class name="Order" table="orders">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="date"/>
+ <many-to-one name="customer" column="customer_id"/>
+ <list name="lineItems" table="line_items">
+ <key column="order_id"/>
+ <list-index column="line_number"/>
+ <composite-element class="LineItem">
+ <property name="quantity"/>
+ <many-to-one name="product" column="product_id"/>
+ </composite-element>
+ </list>
+ </class>
+
+ <class name="Product" table="products">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="serialNumber"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ <literal>customers</literal>, <literal>orders</literal>, <literal>line_items</literal> å
+ <literal>products</literal> åå«ä¿åçcustomer, order, order line item å productçæ°æ®ã
+ <literal>line_items</literal>ä¹ä½ä¸ºè¿æ¥orders å productsçå
³è表ã
+ </para>
+
+ <programlisting><![CDATA[create table customers (
+ id BIGINT not null generated by default as identity,
+ name VARCHAR(255),
+ primary key (id)
+)
+
+create table orders (
+ id BIGINT not null generated by default as identity,
+ customer_id BIGINT,
+ date TIMESTAMP,
+ primary key (id)
+)
+
+create table line_items (
+ line_number INTEGER not null,
+ order_id BIGINT not null,
+ product_id BIGINT,
+ quantity INTEGER,
+ primary key (order_id, line_number)
+)
+
+create table products (
+ id BIGINT not null generated by default as identity,
+ serialNumber VARCHAR(255),
+ primary key (id)
+)
+
+alter table orders
+ add constraint ordersFK0 foreign key (customer_id) references customers
+alter table line_items
+ add constraint line_itemsFK0 foreign key (product_id) references products
+alter table line_items
+ add constraint line_itemsFK1 foreign key (order_id) references orders]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="misc">
+ <title>æä¾</title>
+
+ <para>
+ è¿äºä¾åå
¨é¨æ¥èªäºHibernateçtest suiteï¼åæ¶ä½ ä¹å¯ä»¥æ¾å°å
¶ä»æç¨çä¾åã
+ å¯ä»¥åèHibernateç<literal>test</literal>ç®å½ã
+ </para>
+
+ <para>TODO: put words around this stuff</para>
+
+ <sect2 id="example-mappings-typed-onetone">
+ <title>"Typed" one-to-one association</title>
+<programlisting><![CDATA[<class name="Person">
+ <id name="name"/>
+ <one-to-one name="address"
+ cascade="all">
+ <formula>name</formula>
+ <formula>'HOME'</formula>
+ </one-to-one>
+ <one-to-one name="mailingAddress"
+ cascade="all">
+ <formula>name</formula>
+ <formula>'MAILING'</formula>
+ </one-to-one>
+</class>
+
+<class name="Address" batch-size="2"
+ check="addressType in ('MAILING', 'HOME', 'BUSINESS')">
+ <composite-id>
+ <key-many-to-one name="person"
+ column="personName"/>
+ <key-property name="type"
+ column="addressType"/>
+ </composite-id>
+ <property name="street" type="text"/>
+ <property name="state"/>
+ <property name="zip"/>
+</class>]]></programlisting>
+ </sect2>
+
+ <sect2 id="example-mappings-composite-key">
+ <title>Composite key example</title>
+<programlisting><![CDATA[<class name="Customer">
+
+ <id name="customerId"
+ length="10">
+ <generator class="assigned"/>
+ </id>
+
+ <property name="name" not-null="true" length="100"/>
+ <property name="address" not-null="true" length="200"/>
+
+ <list name="orders"
+ inverse="true"
+ cascade="save-update">
+ <key column="customerId"/>
+ <index column="orderNumber"/>
+ <one-to-many class="Order"/>
+ </list>
+
+</class>
+
+<class name="Order" table="CustomerOrder" lazy="true">
+ <synchronize table="LineItem"/>
+ <synchronize table="Product"/>
+
+ <composite-id name="id"
+ class="Order$Id">
+ <key-property name="customerId" length="10"/>
+ <key-property name="orderNumber"/>
+ </composite-id>
+
+ <property name="orderDate"
+ type="calendar_date"
+ not-null="true"/>
+
+ <property name="total">
+ <formula>
+ ( select sum(li.quantity*p.price)
+ from LineItem li, Product p
+ where li.productId = p.productId
+ and li.customerId = customerId
+ and li.orderNumber = orderNumber )
+ </formula>
+ </property>
+
+ <many-to-one name="customer"
+ column="customerId"
+ insert="false"
+ update="false"
+ not-null="true"/>
+
+ <bag name="lineItems"
+ fetch="join"
+ inverse="true"
+ cascade="save-update">
+ <key>
+ <column name="customerId"/>
+ <column name="orderNumber"/>
+ </key>
+ <one-to-many class="LineItem"/>
+ </bag>
+
+</class>
+
+<class name="LineItem">
+
+ <composite-id name="id"
+ class="LineItem$Id">
+ <key-property name="customerId" length="10"/>
+ <key-property name="orderNumber"/>
+ <key-property name="productId" length="10"/>
+ </composite-id>
+
+ <property name="quantity"/>
+
+ <many-to-one name="order"
+ insert="false"
+ update="false"
+ not-null="true">
+ <column name="customerId"/>
+ <column name="orderNumber"/>
+ </many-to-one>
+
+ <many-to-one name="product"
+ insert="false"
+ update="false"
+ not-null="true"
+ column="productId"/>
+
+</class>
+
+<class name="Product">
+ <synchronize table="LineItem"/>
+
+ <id name="productId"
+ length="10">
+ <generator class="assigned"/>
+ </id>
+
+ <property name="description"
+ not-null="true"
+ length="200"/>
+ <property name="price" length="3"/>
+ <property name="numberAvailable"/>
+
+ <property name="numberOrdered">
+ <formula>
+ ( select sum(li.quantity)
+ from LineItem li
+ where li.productId = productId )
+ </formula>
+ </property>
+
+</class>]]></programlisting>
+ </sect2>
+
+ <sect2 id="example-mappings-composite-key-manytomany">
+ <title>å
±æç»åé®å±æ§çå¤å¯¹å¤(Many-to-many with shared composite key attribute)</title>
+
+<programlisting><![CDATA[<class name="User" table="`User`">
+ <composite-id>
+ <key-property name="name"/>
+ <key-property name="org"/>
+ </composite-id>
+ <set name="groups" table="UserGroup">
+ <key>
+ <column name="userName"/>
+ <column name="org"/>
+ </key>
+ <many-to-many class="Group">
+ <column name="groupName"/>
+ <formula>org</formula>
+ </many-to-many>
+ </set>
+</class>
+
+<class name="Group" table="`Group`">
+ <composite-id>
+ <key-property name="name"/>
+ <key-property name="org"/>
+ </composite-id>
+ <property name="description"/>
+ <set name="users" table="UserGroup" inverse="true">
+ <key>
+ <column name="groupName"/>
+ <column name="org"/>
+ </key>
+ <many-to-many class="User">
+ <column name="userName"/>
+ <formula>org</formula>
+ </many-to-many>
+ </set>
+</class>
+]]></programlisting>
+ </sect2>
+
+ <sect2 id="example-mappings-content-discrimination">
+ <title>Content based discrimination</title>
+<programlisting><![CDATA[<class name="Person"
+ discriminator-value="P">
+
+ <id name="id"
+ column="person_id"
+ unsaved-value="0">
+ <generator class="native"/>
+ </id>
+
+
+ <discriminator
+ type="character">
+ <formula>
+ case
+ when title is not null then 'E'
+ when salesperson is not null then 'C'
+ else 'P'
+ end
+ </formula>
+ </discriminator>
+
+ <property name="name"
+ not-null="true"
+ length="80"/>
+
+ <property name="sex"
+ not-null="true"
+ update="false"/>
+
+ <component name="address">
+ <property name="address"/>
+ <property name="zip"/>
+ <property name="country"/>
+ </component>
+
+ <subclass name="Employee"
+ discriminator-value="E">
+ <property name="title"
+ length="20"/>
+ <property name="salary"/>
+ <many-to-one name="manager"/>
+ </subclass>
+
+ <subclass name="Customer"
+ discriminator-value="C">
+ <property name="comments"/>
+ <many-to-one name="salesperson"/>
+ </subclass>
+
+</class>]]></programlisting>
+ </sect2>
+
+ <sect2 id="example-mappings-association-alternatekeys" revision="2">
+ <title>Associations on alternate keys</title>
+<programlisting><![CDATA[<class name="Person">
+
+ <id name="id">
+ <generator class="hilo"/>
+ </id>
+
+ <property name="name" length="100"/>
+
+ <one-to-one name="address"
+ property-ref="person"
+ cascade="all"
+ fetch="join"/>
+
+ <set name="accounts"
+ inverse="true">
+ <key column="userId"
+ property-ref="userId"/>
+ <one-to-many class="Account"/>
+ </set>
+
+ <property name="userId" length="8"/>
+
+</class>
+
+<class name="Address">
+
+ <id name="id">
+ <generator class="hilo"/>
+ </id>
+
+ <property name="address" length="300"/>
+ <property name="zip" length="5"/>
+ <property name="country" length="25"/>
+ <many-to-one name="person" unique="true" not-null="true"/>
+
+</class>
+
+<class name="Account">
+ <id name="accountId" length="32">
+ <generator class="uuid"/>
+ </id>
+
+ <many-to-one name="user"
+ column="userId"
+ property-ref="userId"/>
+
+ <property name="type" not-null="true"/>
+
+</class>]]></programlisting>
+ </sect2>
+
+ </sect1>
+
+</chapter>
\ No newline at end of file
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/example_parentchild.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/example_parentchild.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/example_parentchild.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,306 @@
+<chapter id="example-parentchild">
+ <title>示ä¾ï¼ç¶åå
³ç³»(Parent Child Relationships)</title>
+ <para>
+ ååæ¥è§¦Hibernateç人大å¤æ¯ä»ç¶åå
³ç³»ï¼parent / child type relationshipï¼ç建模å
¥æçãç¶åå
³ç³»ç建模æ两ç§æ¹æ³ãç±äºç§ç§åå ï¼ææ¹ä¾¿çæ¹æ³æ¯æ<literal>Parent</literal>å<literal>Child</literal>é½å»ºæ¨¡æå®ä½ç±»ï¼å¹¶å建ä¸ä¸ªä»<literal>Parent</literal>æå<literal>Child</literal>ç<one-to-many>å
³èï¼å¯¹æ°ææ¥è¯´å°¤å
¶å¦æ¤ãè¿æä¸ç§æ¹æ³ï¼å°±æ¯å°<literal>Child</literal>声æ为ä¸ä¸ª<literal><composite-element></literal>ï¼ç»åå
ç´ ï¼ã äºå®ä¸å¨Hibernateä¸one to manyå
³èçé»è®¤è¯ä¹è¿æ²¡æcomposite elementè´´è¿parent / childå
³ç³»çé常è¯ä¹ãä¸é¢æ们ä¼éè¿°å¦ä½ä½¿ç¨<emphasis>带æ级èçååä¸å¯¹å¤å
³è(bidirectional one to many association with cascades)</emphasis>å»å»ºç«ææãä¼ç¾çparent / childå
³ç³»ãè¿ä¸ç¹ä¹ä¸é¾ï¼
+ </para>
+
+ <sect1 id="example-parentchild-collections">
+ <title>å
³äºcollectionséè¦æ³¨æçä¸ç¹</title>
+ <para>
+ Hibernate collections被å½ä½å
¶æå±å®ä½èä¸æ¯å
¶å
å«å®ä½çä¸ä¸ªé»è¾é¨åãè¿é常éè¦ï¼å®ä¸»è¦ä½ç°ä¸ºä»¥ä¸å ç¹ï¼
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ å½å é¤æå¢å collectionä¸å¯¹è±¡çæ¶åï¼collectionæå±è
ççæ¬å¼ä¼éå¢ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ å¦æä¸ä¸ªä»collectionä¸ç§»é¤ç对象æ¯ä¸ä¸ªå¼ç±»å(value type)çå®ä¾ï¼æ¯å¦composite elementï¼é£ä¹è¿ä¸ªå¯¹è±¡çæä¹
åç¶æå°ä¼ç»æ¢ï¼å
¶å¨æ°æ®åºä¸å¯¹åºçè®°å½ä¼è¢«å é¤ãåæ ·çï¼åcollectionå¢å ä¸ä¸ªvalue typeçå®ä¾å°ä¼ä½¿ä¹ç«å³è¢«æä¹
åã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ å¦ä¸æ¹é¢ï¼å¦æä»ä¸å¯¹å¤æå¤å¯¹å¤å
³èçcollectionä¸ç§»é¤ä¸ä¸ªå®ä½ï¼å¨ç¼ºçæ
åµä¸è¿ä¸ªå¯¹è±¡å¹¶ä¸ä¼è¢«å é¤ãè¿ä¸ªè¡ä¸ºæ¯å®å
¨åä¹é»è¾çï¼ï¼æ¹åä¸ä¸ªå®ä½çå
é¨ç¶æä¸åºè¯¥ä½¿ä¸å®å
³èçå®ä½æ¶å¤±æï¼åæ ·çï¼åcollectionå¢å ä¸ä¸ªå®ä½ä¸ä¼ä½¿ä¹è¢«æä¹
åã
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ å®é
ä¸ï¼åCollectionå¢å ä¸ä¸ªå®ä½ç缺çå¨ä½åªæ¯å¨ä¸¤ä¸ªå®ä½ä¹é´å建ä¸ä¸ªè¿æ¥èå·²ï¼åæ ·ç§»é¤çæ¶åä¹åªæ¯å é¤è¿æ¥ãè¿ç§å¤ç对äºææçæ
åµé½æ¯åéçã对äºç¶åå
³ç³»åæ¯å®å
¨ä¸éåçï¼å¨è¿ç§å
³ç³»ä¸ï¼å对象ççåç»å®äºç¶å¯¹è±¡ççåå¨æã
+ </para>
+
+ </sect1>
+ <sect1 id="example-parentchild-bidir">
+ <title>ååçä¸å¯¹å¤å
³ç³»(Bidirectional one-to-many)</title>
+
+ <para>
+ å设æ们è¦å®ç°ä¸ä¸ªç®åçä»Parentå°Childç<one-to-many>å
³èã
+ </para>
+
+ <programlisting><![CDATA[<set name="children">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ å¦ææ们è¿è¡ä¸é¢ç代ç
+ </para>
+
+ <programlisting><![CDATA[Parent p = .....;
+Child c = new Child();
+p.getChildren().add(c);
+session.save(c);
+session.flush();]]></programlisting>
+
+ <para>
+ Hibernateä¼äº§ç两æ¡SQLè¯å¥ï¼
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>ä¸æ¡<literal>INSERT</literal>è¯å¥ï¼ä¸º<literal>c</literal>å建ä¸æ¡è®°å½</para>
+ </listitem>
+ <listitem>
+ <para>
+ ä¸æ¡<literal>UPDATE</literal>è¯å¥ï¼å建ä»<literal>p</literal>å°<literal>c</literal>çè¿æ¥
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ è¿æ ·åä¸ä»
æçä½ï¼èä¸è¿åäºå<literal>parent_id</literal>é空çéå¶ãæ们å¯ä»¥éè¿å¨éåç±»æ å°ä¸æå®<literal>not-null="true"</literal>æ¥è§£å³è¿åé空约æçé®é¢ï¼
+ </para>
+
+ <programlisting><![CDATA[<set name="children">
+ <key column="parent_id" not-null="true"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ ç¶èï¼è¿å¹¶éæ¯æ¨èç解å³æ¹æ³ã
+ </para>
+
+ <para> è¿ç§ç°è±¡çæ ¹æ¬åå æ¯ä»<literal>p</literal>å°<literal>c</literal>çè¿æ¥ï¼å¤é®parent_idï¼æ²¡æ被å½ä½<literal>Child</literal>对象ç¶æçä¸é¨åï¼å è没æå¨INSERTè¯å¥ä¸è¢«å建ãå æ¤è§£å³çåæ³å°±æ¯æè¿ä¸ªè¿æ¥æ·»å å°Childçæ å°ä¸ã
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="parent" column="parent_id" not-null="true"/>]]></programlisting>
+
+ <para>
+ ï¼æ们è¿éè¦ä¸ºç±»<literal>Child</literal>æ·»å <literal>parent</literal>å±æ§ï¼
+ </para>
+
+ <para>
+ ç°å¨å®ä½<literal>Child</literal>å¨ç®¡çè¿æ¥çç¶æï¼ä¸ºäºä½¿collectionä¸æ´æ°è¿æ¥ï¼æ们使ç¨<literal>inverse</literal>å±æ§ã
+ </para>
+
+ <programlisting><![CDATA[<set name="children" inverse="true">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ ä¸é¢ç代ç æ¯ç¨æ¥æ·»å ä¸ä¸ªæ°ç<literal>Child</literal>
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+Child c = new Child();
+c.setParent(p);
+p.getChildren().add(c);
+session.save(c);
+session.flush();]]></programlisting>
+
+ <para>
+ ç°å¨ï¼åªä¼æä¸æ¡<literal>INSERT</literal>è¯å¥è¢«æ§è¡ï¼
+ </para>
+
+ <para>
+ 为äºè®©äºæ
åå¾äºäºææ¡ï¼å¯ä»¥ä¸º<literal>Parent</literal>å ä¸ä¸ª<literal>addChild()</literal>æ¹æ³ã
+ </para>
+
+ <programlisting><![CDATA[public void addChild(Child c) {
+ c.setParent(this);
+ children.add(c);
+}]]></programlisting>
+
+ <para>
+ ç°å¨ï¼æ·»å <literal>Child</literal>ç代ç å°±æ¯è¿æ ·
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+Child c = new Child();
+p.addChild(c);
+session.save(c);
+session.flush();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-parentchild-cascades">
+ <title>级èçå½å¨æï¼Cascading lifecycleï¼</title>
+ <para>
+ éè¦æ¾å¼è°ç¨<literal>save()</literal>ä»ç¶å¾éº»ç¦ï¼æ们å¯ä»¥ç¨çº§èæ¥è§£å³è¿ä¸ªé®é¢ã
+ </para>
+
+ <programlisting><![CDATA[<set name="children" inverse="true" cascade="all">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ è¿æ ·ä¸é¢ç代ç å¯ä»¥ç®å为ï¼
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+Child c = new Child();
+p.addChild(c);
+session.flush();]]></programlisting>
+
+ <para>
+ åæ ·çï¼ä¿åæå é¤<literal>Parent</literal>对象çæ¶å并ä¸éè¦éåå
¶å对象ã
+ ä¸é¢ç代ç ä¼å é¤å¯¹è±¡<literal>p</literal>åå
¶ææå对象对åºçæ°æ®åºè®°å½ã
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+session.delete(p);
+session.flush();]]></programlisting>
+
+ <para>
+ ç¶èï¼è¿æ®µä»£ç
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+Child c = (Child) p.getChildren().iterator().next();
+p.getChildren().remove(c);
+c.setParent(null);
+session.flush();]]></programlisting>
+
+ <para>
+ ä¸ä¼ä»æ°æ®åºå é¤<literal>c</literal>ï¼å®åªä¼å é¤ä¸<literal>p</literal>ä¹é´çè¿æ¥ï¼å¹¶ä¸ä¼å¯¼è´è¿å<literal>NOT NULL</literal>约æï¼å¨è¿ä¸ªä¾åä¸ï¼ãä½ éè¦æ¾å¼è°ç¨<literal>delete()</literal>æ¥å é¤<literal>Child</literal>ã <!--ï¼å 为Hibernate并没æ设计åå¾åæ¶å¨ï¼ä»£ç å¦ä¸ï¼-->
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
+Child c = (Child) p.getChildren().iterator().next();
+p.getChildren().remove(c);
+session.delete(c);
+session.flush();]]></programlisting>
+
+
+ <para>
+ å¨æ们çä¾åä¸ï¼å¦æ没æç¶å¯¹è±¡ï¼å对象就ä¸åºè¯¥åå¨ï¼å¦æå°å对象ä»collectionä¸ç§»é¤ï¼å®é
ä¸æ们æ¯æ³å é¤å®ãè¦å®ç°è¿ç§è¦æ±ï¼å°±å¿
须使ç¨<literal>cascade="all-delete-orphan"</literal>ã
+ </para>
+
+
+ <programlisting><![CDATA[<set name="children" inverse="true" cascade="all-delete-orphan">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+</set>]]></programlisting>
+
+ <para>
+ 注æï¼å³ä½¿å¨collectionä¸æ¹çæ å°ä¸æå®<literal>inverse="true"</literal>ï¼çº§èä»ç¶æ¯éè¿éåcollectionä¸çå
ç´ æ¥å¤ççãå¦æä½ æ³è¦éè¿çº§èè¿è¡å对象çæå
¥ãå é¤ãæ´æ°æä½ï¼å°±å¿
é¡»æå®å å°collectionä¸ï¼åªè°ç¨<literal>setParent()</literal>æ¯ä¸å¤çã
+ </para>
+
+ </sect1>
+
+ <sect1 id="example-parentchild-update">
+ <title>级èä¸<literal>æªä¿åå¼</literal>ï¼Cascades and <literal>unsaved-value</literal>ï¼</title>
+
+ <para>
+ å设æ们ä»<literal>Session</literal>ä¸è£
å
¥äºä¸ä¸ª<literal>Parent</literal>对象ï¼ç¨æ·çé¢å¯¹å
¶è¿è¡äºä¿®æ¹ï¼ç¶åå¸æå¨ä¸ä¸ªæ°çSessionéé¢è°ç¨<literal>update()</literal>æ¥ä¿åè¿äºä¿®æ¹ã对象<literal>Parent</literal>å
å«äºå对象çéåï¼ç±äºæå¼äºçº§èæ´æ°ï¼Hibernateéè¦ç¥éåªäºChild对象æ¯æ°å®ä¾åçï¼åªäºä»£è¡¨æ°æ®åºä¸å·²ç»åå¨çè®°å½ãæ们å设<literal>Parent</literal>å<literal>Child</literal>对象çæ è¯å±æ§é½æ¯èªå¨çæçï¼ç±»å为<literal>java.lang.Long</literal>ãHibernateä¼ä½¿ç¨æ è¯å±æ§çå¼ï¼åversion æ timestamp å±æ§ï¼æ¥å¤æåªäºå对象æ¯æ°çã(åè§<xref linkend="objectstate-saveorupdate"/>.) <emphasis>å¨ Hibernate3 ä¸,æ¾å¼æå®<literal>unsaved-value</literal>ä¸åæ¯å¿
é¡»çäºã</emphasis>
+ </para>
+
+ <para>
+ ä¸é¢ç代ç ä¼æ´æ°<literal>parent</literal>å<literal>child</literal>对象ï¼å¹¶ä¸æå
¥<literal>newChild</literal>对象ã
+ </para>
+
+ <programlisting><![CDATA[//parent and child were both loaded in a previous session
+parent.addChild(child);
+Child newChild = new Child();
+parent.addChild(newChild);
+session.update(parent);
+session.flush();]]></programlisting>
+
+ <para>
+ Well, that's all very well for the case of a generated identifier, but what about assigned identifiers
+ and composite identifiers? This is more difficult, since Hibernate can't use the identifier property to
+ distinguish between a newly instantiated object (with an identifier assigned by the user) and an
+ object loaded in a previous session. In this case, Hibernate will either use the timestamp or version
+ property, or will actually query the second-level cache or, worst case, the database, to see if the
+ row exists.
+ </para>
+ <para>
+ è¿å¯¹äºèªå¨çææ è¯çæ
åµæ¯é常好çï¼ä½æ¯èªåé
çæ è¯åå¤åæ è¯æä¹åå¢ï¼è¿æ¯æç¹éº»ç¦ï¼å 为Hibernate没æåæ³åºåæ°å®ä¾åç对象ï¼æ è¯è¢«ç¨æ·æå®äºï¼ååä¸ä¸ªSessionè£
å
¥ç对象ãå¨è¿ç§æ
åµä¸ï¼Hibernateä¼ä½¿ç¨timestampæversionå±æ§ï¼æè
æ¥è¯¢ç¬¬äºçº§ç¼åï¼æè
æåçæ
åµï¼æ¥è¯¢æ°æ®åºï¼æ¥ç¡®è®¤æ¯å¦æ¤è¡åå¨ã</para>
+
+ <!-- undocumenting
+
+ <para>
+ è¿æä¸ç§å¯è½æ
åµï¼æä¸ä¸ªå为<literal>isUnsaved()</literal>ç<literal>æ°çæ¦æªå¨ï¼Interceptorï¼</literal>æ¹æ³ï¼å®å
许åºç¨ç¨åºèªå·±å®ç°æ°å®ä¾çå¤æãæ¯å¦ï¼ä½ å¯ä»¥èªå·±å®ä¹ä¸ä¸ªæä¹
ç±»çç¥å
ç±»ï¼
+ </para>
+
+ <programlisting><![CDATA[public class Persistent {
+ private boolean _saved = false;
+ public void onSave() {
+ _saved=true;
+ }
+ public void onLoad() {
+ _saved=true;
+ }
+ ......
+ public boolean isSaved() {
+ return _saved;
+ }
+}]]></programlisting>
+
+ <para>
+ ï¼<literal>saved</literal>å±æ§æ¯ä¸ä¼è¢«æä¹
åçã)
+ ç°å¨å¨<literal>onLoad()</literal>å<literal>onSave()</literal>å¤ï¼è¿è¦å®ç°<literal>isUnsaved()</literal>ã
+ </para>
+
+ <programlisting><![CDATA[public Boolean isUnsaved(Object entity) {
+ if (entity instanceof Persistent) {
+ return new Boolean( !( (Persistent) entity ).isSaved() );
+ }
+ else {
+ return null;
+ }
+}
+
+public boolean onLoad(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+
+ if (entity instanceof Persistent) ( (Persistent) entity ).onLoad();
+ return false;
+}
+
+public boolean onSave(Object entity,
+ Serializable id,
+ Object[] state,
+ String[] propertyNames,
+ Type[] types) {
+
+ if (entity instanceof Persistent) ( (Persistent) entity ).onSave();
+ return false;
+}]]></programlisting>
+
+ <para>
+ Don't worry; in Hibernate3 you don't need to write any of this kind of code if you don't want to.
+ å«æ
å¿ï¼å¨Hibernate3ä¸ï¼åè¥ä½ ä¸æ¿æï¼ä½ ä¸éè¦ç¼åä»»ä½è¿ç±»ä»£ç ã
+ </para>
+ -->
+
+ </sect1>
+
+
+ <sect1 id="example-parentchild-conclusion">
+ <title>ç»è®º</title>
+ <para> è¿éæä¸å°ä¸è¥¿éè¦èä¼è´¯éï¼å¯è½ä¼è®©æ°ææå°è¿·æãä½æ¯å¨å®è·µä¸å®ä»¬é½å·¥ä½å°é常好ã大é¨åHibernateåºç¨ç¨åºé½ä¼ç»å¸¸ç¨å°ç¶å对象模å¼ã
+ </para>
+ <para>
+ å¨ç¬¬ä¸æ®µä¸æ们æ¾ç»æå°å¦ä¸ä¸ªæ¹æ¡ãä¸é¢çè¿äºé®é¢é½ä¸ä¼åºç°å¨<literal><composite-element></literal>æ å°ä¸ï¼å®åç¡®å°è¡¨è¾¾äºç¶åå
³ç³»çè¯ä¹ãå¾ä¸å¹¸å¤åå
ç´ è¿æ两个é大éå¶:å¤åå
ç´ ä¸è½æ¥æcollectionsï¼å¹¶ä¸ï¼é¤äºç¨äºæä¸çç¶å¯¹è±¡å¤ï¼å®ä»¬ä¸è½åä½ä¸ºå
¶å®ä»»ä½å®ä½çå对象ã
+ </para>
+ </sect1>
+
+</chapter>
\ No newline at end of file
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/example_weblog.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/example_weblog.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/example_weblog.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,424 @@
+<chapter id="example-weblog">
+ <title>示ä¾ï¼Weblog åºç¨ç¨åº</title>
+
+ <sect1 id="example-weblog-classes">
+ <title>æä¹
åç±»</title>
+
+ <para> ä¸é¢çæä¹
å类表示ä¸ä¸ªweblogåå¨å
¶ä¸å¼ è´´çä¸ä¸ªè´´åãä»ä»¬æ¯æ åçç¶/åå
³ç³»æ¨¡åï¼ä½æ¯æ们ä¼ç¨ä¸ä¸ªæåºå
ï¼ordered bag)èééå(set)ã
+ </para>
+
+ <programlisting><![CDATA[package eg;
+
+import java.util.List;
+
+public class Blog {
+ private Long _id;
+ private String _name;
+ private List _items;
+
+ public Long getId() {
+ return _id;
+ }
+ public List getItems() {
+ return _items;
+ }
+ public String getName() {
+ return _name;
+ }
+ public void setId(Long long1) {
+ _id = long1;
+ }
+ public void setItems(List list) {
+ _items = list;
+ }
+ public void setName(String string) {
+ _name = string;
+ }
+}]]></programlisting>
+
+ <programlisting><![CDATA[package eg;
+
+import java.text.DateFormat;
+import java.util.Calendar;
+
+public class BlogItem {
+ private Long _id;
+ private Calendar _datetime;
+ private String _text;
+ private String _title;
+ private Blog _blog;
+
+ public Blog getBlog() {
+ return _blog;
+ }
+ public Calendar getDatetime() {
+ return _datetime;
+ }
+ public Long getId() {
+ return _id;
+ }
+ public String getText() {
+ return _text;
+ }
+ public String getTitle() {
+ return _title;
+ }
+ public void setBlog(Blog blog) {
+ _blog = blog;
+ }
+ public void setDatetime(Calendar calendar) {
+ _datetime = calendar;
+ }
+ public void setId(Long long1) {
+ _id = long1;
+ }
+ public void setText(String string) {
+ _text = string;
+ }
+ public void setTitle(String string) {
+ _title = string;
+ }
+}]]></programlisting>
+
+ </sect1>
+ <sect1 id="example-weblog-mappings">
+ <title>Hibernate æ å°</title>
+
+ <para>
+ ä¸åçXMLæ å°åºè¯¥æ¯å¾ç´ç½çã
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="eg">
+ <class
+ name="Blog"
+ table="BLOGS" >
+
+ <id
+ name="id"
+ column="BLOG_ID">
+
+ <generator class="native"/>
+
+ </id>
+
+ <property
+ name="name"
+ column="NAME"
+ not-null="true"
+ unique="true"/>
+
+ <bag
+ name="items"
+ inverse="true"
+ order-by="DATE_TIME"
+ cascade="all">
+
+ <key column="BLOG_ID"/>
+ <one-to-many class="BlogItem"/>
+
+ </bag>
+
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="eg">
+
+ <class
+ name="BlogItem"
+ table="BLOG_ITEMS"
+ dynamic-update="true">
+
+ <id
+ name="id"
+ column="BLOG_ITEM_ID">
+
+ <generator class="native"/>
+
+ </id>
+
+ <property
+ name="title"
+ column="TITLE"
+ not-null="true"/>
+
+ <property
+ name="text"
+ column="TEXT"
+ not-null="true"/>
+
+ <property
+ name="datetime"
+ column="DATE_TIME"
+ not-null="true"/>
+
+ <many-to-one
+ name="blog"
+ column="BLOG_ID"
+ not-null="true"/>
+
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="example-weblog-code">
+ <title>Hibernate 代ç </title>
+
+ <para>
+ ä¸é¢çç±»æ¼ç¤ºäºæ们å¯ä»¥ä½¿ç¨Hibernate对è¿äºç±»è¿è¡çä¸äºæä½ã
+ </para>
+
+ <programlisting><![CDATA[package eg;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Iterator;
+import java.util.List;
+
+import org.hibernate.HibernateException;
+import org.hibernate.Query;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.tool.hbm2ddl.SchemaExport;
+
+public class BlogMain {
+
+ private SessionFactory _sessions;
+
+ public void configure() throws HibernateException {
+ _sessions = new Configuration()
+ .addClass(Blog.class)
+ .addClass(BlogItem.class)
+ .buildSessionFactory();
+ }
+
+ public void exportTables() throws HibernateException {
+ Configuration cfg = new Configuration()
+ .addClass(Blog.class)
+ .addClass(BlogItem.class);
+ new SchemaExport(cfg).create(true, true);
+ }
+
+ public Blog createBlog(String name) throws HibernateException {
+
+ Blog blog = new Blog();
+ blog.setName(name);
+ blog.setItems( new ArrayList() );
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ session.persist(blog);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return blog;
+ }
+
+ public BlogItem createBlogItem(Blog blog, String title, String text)
+ throws HibernateException {
+
+ BlogItem item = new BlogItem();
+ item.setTitle(title);
+ item.setText(text);
+ item.setBlog(blog);
+ item.setDatetime( Calendar.getInstance() );
+ blog.getItems().add(item);
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ session.update(blog);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return item;
+ }
+
+ public BlogItem createBlogItem(Long blogid, String title, String text)
+ throws HibernateException {
+
+ BlogItem item = new BlogItem();
+ item.setTitle(title);
+ item.setText(text);
+ item.setDatetime( Calendar.getInstance() );
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ Blog blog = (Blog) session.load(Blog.class, blogid);
+ item.setBlog(blog);
+ blog.getItems().add(item);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return item;
+ }
+
+ public void updateBlogItem(BlogItem item, String text)
+ throws HibernateException {
+
+ item.setText(text);
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ session.update(item);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ }
+
+ public void updateBlogItem(Long itemid, String text)
+ throws HibernateException {
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ BlogItem item = (BlogItem) session.load(BlogItem.class, itemid);
+ item.setText(text);
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ }
+
+ public List listAllBlogNamesAndItemCounts(int max)
+ throws HibernateException {
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ List result = null;
+ try {
+ tx = session.beginTransaction();
+ Query q = session.createQuery(
+ "select blog.id, blog.name, count(blogItem) " +
+ "from Blog as blog " +
+ "left outer join blog.items as blogItem " +
+ "group by blog.name, blog.id " +
+ "order by max(blogItem.datetime)"
+ );
+ q.setMaxResults(max);
+ result = q.list();
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return result;
+ }
+
+ public Blog getBlogAndAllItems(Long blogid)
+ throws HibernateException {
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ Blog blog = null;
+ try {
+ tx = session.beginTransaction();
+ Query q = session.createQuery(
+ "from Blog as blog " +
+ "left outer join fetch blog.items " +
+ "where blog.id = :blogid"
+ );
+ q.setParameter("blogid", blogid);
+ blog = (Blog) q.uniqueResult();
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return blog;
+ }
+
+ public List listBlogsAndRecentItems() throws HibernateException {
+
+ Session session = _sessions.openSession();
+ Transaction tx = null;
+ List result = null;
+ try {
+ tx = session.beginTransaction();
+ Query q = session.createQuery(
+ "from Blog as blog " +
+ "inner join blog.items as blogItem " +
+ "where blogItem.datetime > :minDate"
+ );
+
+ Calendar cal = Calendar.getInstance();
+ cal.roll(Calendar.MONTH, false);
+ q.setCalendar("minDate", cal);
+
+ result = q.list();
+ tx.commit();
+ }
+ catch (HibernateException he) {
+ if (tx!=null) tx.rollback();
+ throw he;
+ }
+ finally {
+ session.close();
+ }
+ return result;
+ }
+}]]></programlisting>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/filters.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/filters.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/filters.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,140 @@
+<chapter id="filters">
+ <title>è¿æ»¤æ°æ®</title>
+ <para>
+
+ Hibernate3 æä¾äºä¸ç§åæ°çæ¹å¼æ¥å¤çå
·æâæ¾æ§(visibility)âè§åçæ°æ®ï¼é£å°±æ¯ä½¿ç¨<emphasis>Hibernate filter</emphasis>ã
+ <emphasis>Hibernate filter</emphasis>æ¯å
¨å±ææçãå
·æååãå¯ä»¥å¸¦åæ°çè¿æ»¤å¨ï¼
+ 对äºæ个ç¹å®çHibernate sessionæ¨å¯ä»¥éæ©æ¯å¦å¯ç¨ï¼æç¦ç¨ï¼æ个è¿æ»¤å¨ã
+ </para>
+
+ <sect1 id="objectstate-filters" revision="1">
+ <title>Hibernate è¿æ»¤å¨(filters)</title>
+
+ <para>
+ Hibernate3æ°å¢äºå¯¹æ个类æè
éå使ç¨é¢å
å®ä¹çè¿æ»¤å¨æ¡ä»¶(filter criteria)çåè½ãè¿æ»¤å¨æ¡ä»¶ç¸å½äºå®ä¹ä¸ä¸ª
+ é常类似äºç±»ååç§éåä¸çâwhereâå±æ§ç约æåå¥ï¼ä½æ¯è¿æ»¤å¨æ¡ä»¶å¯ä»¥å¸¦åæ°ã
+ åºç¨ç¨åºå¯ä»¥å¨è¿è¡æ¶å³å®æ¯å¦å¯ç¨ç»å®çè¿æ»¤å¨ï¼ä»¥å使ç¨ä»ä¹æ ·çåæ°å¼ã
+ è¿æ»¤å¨çç¨æ³å¾åæ°æ®åºè§å¾ï¼åªä¸è¿æ¯å¨åºç¨ç¨åºä¸ç¡®å®ä½¿ç¨ä»ä¹æ ·çåæ°çã
+
+ </para>
+
+ <para>
+
+ è¦ä½¿ç¨è¿æ»¤å¨ï¼å¿
é¡»é¦å
å¨ç¸åºçæ å°èç¹ä¸å®ä¹ãèå®ä¹ä¸ä¸ªè¿æ»¤å¨ï¼è¦ç¨å°ä½äº<literal><hibernate-mapping/></literal>
+ èç¹ä¹å
ç<literal><filter-def/></literal>èç¹ï¼
+ </para>
+
+ <programlisting><![CDATA[<filter-def name="myFilter">
+ <filter-param name="myFilterParam" type="string"/>
+</filter-def>]]></programlisting>
+
+ <para>
+ å®ä¹å¥½ä¹åï¼å°±å¯ä»¥å¨æ个类ä¸ä½¿ç¨è¿ä¸ªè¿æ»¤å¨ï¼
+ </para>
+
+ <programlisting><![CDATA[<class name="myClass" ...>
+ ...
+ <filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
+</class>]]></programlisting>
+
+ <para>
+ ä¹å¯ä»¥å¨æ个éå使ç¨å®ï¼
+ </para>
+
+ <programlisting><![CDATA[<set ...>
+ <filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
+</set>]]></programlisting>
+
+ <para>
+ å¯ä»¥å¨å¤ä¸ªç±»æéåä¸ä½¿ç¨æ个è¿æ»¤å¨ï¼æ个类æè
éåä¸ä¹å¯ä»¥ä½¿ç¨å¤ä¸ªè¿æ»¤å¨ã
+ </para>
+
+ <para>
+
+ <literal>Session</literal>对象ä¸ä¼ç¨å°çæ¹æ³æï¼<literal>enableFilter(String filterName)</literal>,
+ <literal>getEnabledFilter(String filterName)</literal>, å <literal>disableFilter(String filterName)</literal>.
+ Sessionä¸é»è®¤æ¯<emphasis>ä¸</emphasis>å¯ç¨è¿æ»¤å¨çï¼å¿
é¡»éè¿<literal>Session.enabledFilter()</literal>æ¹æ³æ¾å¼çå¯ç¨ã
+ 该æ¹æ³è¿å被å¯ç¨ç<literal>Filter</literal>çå®ä¾ã以ä¸æå®ä¹çè¿æ»¤å¨ä¸ºä¾ï¼
+ </para>
+
+ <programlisting><![CDATA[session.enableFilter("myFilter").setParameter("myFilterParam", "some-value");]]></programlisting>
+
+ <para>
+ 注æï¼org.hibernate.Filterçæ¹æ³å
许é¾å¼æ¹æ³è°ç¨ãï¼ç±»ä¼¼ä¸é¢ä¾åä¸å¯ç¨Filterä¹å设å®Filteråæ°è¿ä¸ªâæ¹æ³é¾âï¼
+ Hibernateçå
¶ä»é¨åä¹å¤§å¤æè¿ä¸ªç¹æ§ã
+ </para>
+
+ <para>
+ ä¸é¢æ¯ä¸ä¸ªæ¯è¾å®æ´çä¾åï¼ä½¿ç¨äºè®°å½çææ¥æ模å¼è¿æ»¤ææ¶æçæ°æ®ï¼
+ </para>
+
+ <programlisting><![CDATA[<filter-def name="effectiveDate">
+ <filter-param name="asOfDate" type="date"/>
+</filter-def>
+
+<class name="Employee" ...>
+...
+ <many-to-one name="department" column="dept_id" class="Department"/>
+ <property name="effectiveStartDate" type="date" column="eff_start_dt"/>
+ <property name="effectiveEndDate" type="date" column="eff_end_dt"/>
+...
+ <!--
+ Note that this assumes non-terminal records have an eff_end_dt set to
+ a max db date for simplicity-sake
+
+ 注æï¼ä¸ºäºç®åèµ·è§ï¼æ¤å¤å设éç¨å
³ç³»çææå°æªç»æçè®°å½çeff_end_dtå段çå¼çäºæ°æ®åºæ大çæ¥æ
+ -->
+ <filter name="effectiveDate"
+ condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
+</class>
+
+<class name="Department" ...>
+...
+ <set name="employees" lazy="true">
+ <key column="dept_id"/>
+ <one-to-many class="Employee"/>
+ <filter name="effectiveDate"
+ condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ å®ä¹å¥½åï¼å¦ææ³è¦ä¿è¯ååçé½æ¯ç®åå¤äºçææçè®°å½ï¼åªéå¨è·åéåæ°æ®çæä½ä¹åå
å¼å¯è¿æ»¤å¨å³å¯ï¼
+ </para>
+
+<programlisting><![CDATA[Session session = ...;
+session.enabledFilter("effectiveDate").setParameter("asOfDate", new Date());
+List results = session.createQuery("from Employee as e where e.salary > :targetSalary")
+ .setLong("targetSalary", new Long(1000000))
+ .list();
+]]></programlisting>
+
+ <para>
+
+ å¨ä¸é¢çHQLä¸ï¼è½ç¶æ们ä»
ä»
æ¾å¼ç使ç¨äºä¸ä¸ªèªæ°´æ¡ä»¶ï¼ä½å 为å¯ç¨äºè¿æ»¤å¨ï¼æ¥è¯¢å°ä»
è¿åé£äºç®åéç¨
+ å
³ç³»å¤äºçææçï¼å¹¶ä¸èªæ°´é«äºä¸ç¾ä¸ç¾åçéåçæ°æ®ã
+
+ </para>
+
+ <para>
+
+ 注æï¼å¦æä½ æç®å¨ä½¿ç¨å¤è¿æ¥ï¼æè
éè¿HQLæload fetchingï¼çåæ¶ä½¿ç¨è¿æ»¤å¨ï¼è¦æ³¨ææ¡ä»¶è¡¨è¾¾å¼çæ¹åï¼å·¦è¿æ¯å³ï¼ã
+ æå®å
¨çæ¹å¼æ¯ä½¿ç¨å·¦å¤è¿æ¥ï¼left outer joiningï¼ã并ä¸é常æ¥è¯´ï¼å
ååæ°ï¼
+ ç¶åæ¯æä½ç¬¦ï¼æååæ°æ®åºå段åã
+ </para>
+
+ <para>
+ å¨Filterå®ä¹ä¹å,å®å¯è½è¢«éå å°å¤ä¸ªå®ä½å/æéåç±»,æ¯ä¸ªé½æèªå·±çæ¡ä»¶ãåè¥è¿äºæ¡ä»¶é½æ¯ä¸æ ·çï¼æ¯æ¬¡é½è¦å®ä¹å°±æ¾å¾å¾ç¹çãå æ¤ï¼<literal><filter-def/></literal>被ç¨æ¥å®ä¹ä¸ä¸ªé»è®¤æ¡ä»¶ï¼å®å¯è½ä½ä¸ºå±æ§æè
CDATAåºç°ï¼
+ </para>
+
+ <programlisting><![CDATA[<filter-def name="myFilter" condition="abc > xyz">...</filter-def>
+<filter-def name="myOtherFilter">abc=xyz</filter-def>]]></programlisting>
+
+ <para>
+ å½è¿ä¸ªfilter被éå å°ä»»ä½ç®çå°ï¼èå没ææææ¡ä»¶æ¶ï¼è¿ä¸ªæ¡ä»¶å°±ä¼è¢«ä½¿ç¨ã注æï¼æ¢å¥è¯è¯´ï¼ä½ å¯ä»¥éè¿ç»filteréå ç¹å«çæ¡ä»¶æ¥éè½½é»è®¤æ¡ä»¶ã
+ </para>
+
+
+ </sect1>
+
+</chapter>
\ No newline at end of file
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/inheritance_mapping.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/inheritance_mapping.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/inheritance_mapping.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,467 @@
+ <chapter id="inheritance">
+ <title>继æ¿æ å°(Inheritance Mappings)</title>
+
+ <sect1 id="inheritance-strategies" revision="3">
+ <title> ä¸ç§çç¥</title>
+
+
+ <para>
+ Hibernateæ¯æä¸ç§åºæ¬ç继æ¿æ å°çç¥ï¼
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ æ¯ä¸ªç±»åå±ç»æä¸å¼ 表(table per class hierarchy)
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ æ¯ä¸ªåç±»ä¸å¼ 表(table per subclass)
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ æ¯ä¸ªå
·ä½ç±»ä¸å¼ 表(table per concrete class)
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+ æ¤å¤ï¼Hibernateè¿æ¯æ第åç§ç¨æä¸åçå¤ææ å°çç¥ï¼
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ éå¼å¤æ(implicit polymorphism)
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ 对äºåä¸ä¸ªç»§æ¿å±æ¬¡å
çä¸ååæ¯ï¼å¯ä»¥éç¨ä¸åçæ å°çç¥ï¼ç¶åç¨éå¼å¤
+ ææ¥å®æè·¨è¶æ´ä¸ªå±æ¬¡çå¤æãä½æ¯å¨åä¸ä¸ª<literal><class></literal>æ ¹å
ç´
+ ä¸ï¼Hibernateä¸æ¯ææ··åäºå
ç´ <literal><subclass></literal>ã
+ <literal><joined-subclass></literal>å<literal><union-subclass></literal>
+ çæ å°ãå¨åä¸ä¸ª<literal><class></literal>å
ç´ ä¸ï¼å¯ä»¥æ··å使ç¨
+ âæ¯ä¸ªç±»åå±ç»æä¸å¼ 表âï¼table per hierarchyï¼ åâæ¯ä¸ªåç±»ä¸å¼ 表âï¼table per subclassï¼
+ è¿ä¸¤ç§æ å°çç¥ï¼è¿æ¯éè¿ç»åå
ç´ <literal><subclass></literal>å
+ <literal><join></literal>æ¥å®ç°çï¼è§åï¼ã
+
+ </para>
+
+ <para>
+ å¨å¤ä¸ªæ å°æ件ä¸ï¼å¯ä»¥ç´æ¥å¨<literal>hibernate-mapping</literal>æ ¹ä¸å®ä¹<literal>subclass</literal>ï¼<literal>union-subclass</literal>å<literal>joined-subclass</literal>ãä¹å°±æ¯è¯´ï¼ä½ å¯ä»¥ä»
å å
¥ä¸ä¸ªæ°çæ å°æ件æ¥æ©å±ç±»å±æ¬¡ãä½ å¿
é¡»å¨subclassçæ å°ä¸ææ<literal>extends</literal>å±æ§ï¼ç»åºä¸ä¸ªä¹åå®ä¹çè¶
ç±»çååã注æï¼å¨ä»¥åï¼è¿ä¸åè½å¯¹æ å°æ件ç顺åºæä¸¥æ ¼çè¦æ±ï¼ä»Hibernate 3å¼å§ï¼ä½¿ç¨extendså
³é®åçæ¶ä¾¯ï¼å¯¹æ å°æ件ç顺åºä¸åæè¦æ±ï¼ä½å¨æ¯ä¸ªæ å°æ件éï¼è¶
ç±»å¿
é¡»å¨åç±»ä¹åå®ä¹ã
+ </para>
+
+ <programlisting><![CDATA[
+ <hibernate-mapping>
+ <subclass name="DomesticCat" extends="Cat" discriminator-value="D">
+ <property name="name" type="string"/>
+ </subclass>
+ </hibernate-mapping>]]></programlisting>
+
+ <sect2 id="inheritance-tableperclass" >
+ <title>æ¯ä¸ªç±»åå±ç»æä¸å¼ 表(Table per class hierarchy)</title>
+
+
+ <para>
+ å设æ们ææ¥å£<literal>Payment</literal>åå®çå 个å®ç°ç±»ï¼
+ <literal>CreditCardPayment</literal>, <literal>CashPayment</literal>,
+ å<literal>ChequePayment</literal>ãåâæ¯ä¸ªç±»åå±ç»æä¸å¼ 表â(Table per
+ class hierarchy)çæ å°ä»£ç å¦ä¸æ示ï¼
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment" table="PAYMENT">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <discriminator column="PAYMENT_TYPE" type="string"/>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <subclass name="CreditCardPayment" discriminator-value="CREDIT">
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </subclass>
+ <subclass name="CashPayment" discriminator-value="CASH">
+ ...
+ </subclass>
+ <subclass name="ChequePayment" discriminator-value="CHEQUE">
+ ...
+ </subclass>
+</class>]]></programlisting>
+
+ <para>
+ éç¨è¿ç§çç¥åªéè¦ä¸å¼ 表å³å¯ãå®æä¸ä¸ªå¾å¤§çéå¶ï¼è¦æ±é£äºç±åç±»å®ä¹çå段ï¼
+ å¦<literal>CCTYPE</literal>ï¼ä¸è½æ<literal>é空(NOT NULL)</literal>约æã
+ </para>
+
+ </sect2>
+
+ <sect2 id="inheritance-tablepersubclass">
+
+ <title>æ¯ä¸ªåç±»ä¸å¼ 表(Table per subclass)</title>
+
+
+ <para>
+ 对äºä¸ä¾ä¸çå 个类èè¨ï¼éç¨âæ¯ä¸ªåç±»ä¸å¼ 表âçæ å°çç¥ï¼ä»£ç å¦ä¸æ示ï¼
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment" table="PAYMENT">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <joined-subclass name="CreditCardPayment" table="CREDIT_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ ...
+ </joined-subclass>
+ <joined-subclass name="CashPayment" table="CASH_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </joined-subclass>
+ <joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ ...
+ </joined-subclass>
+</class>]]></programlisting>
+ <para>
+ éè¦åå¼ è¡¨ãä¸ä¸ªå类表éè¿ä¸»é®å
³èå°è¶
类表(å èå
³ç³»æ¨¡åå®é
ä¸æ¯ä¸å¯¹ä¸å
³è)ã
+ </para>
+
+ </sect2>
+
+ <sect2 id="inheritance-tablepersubclass-discriminator" revision="2">
+
+ <title>æ¯ä¸ªåç±»ä¸å¼ 表(Table per subclass)ï¼ä½¿ç¨è¾¨å«æ å¿(Discriminator)</title>
+
+ <para>
+ 注æï¼å¯¹âæ¯ä¸ªåç±»ä¸å¼ 表âçæ å°çç¥ï¼Hibernateçå®ç°ä¸éè¦è¾¨å«å段ï¼èå
¶ä»
+ ç对象/å
³ç³»æ å°å·¥å
·ä½¿ç¨äºä¸ç§ä¸åäºHibernateçå®ç°æ¹æ³ï¼è¯¥æ¹æ³è¦æ±å¨è¶
ç±»
+ 表ä¸æä¸ä¸ªç±»å辨å«å段(type discriminator column)ãHibernateéç¨çæ¹æ³æ´
+ é¾å®ç°ï¼ä½ä»å
³ç³»ï¼æ°æ®åºï¼çè§åº¦æ¥çï¼æç说å®æ´æ£ç¡®ãè¥ä½ æ¿æ使ç¨å¸¦æ辨å«å
+ 段çâæ¯ä¸ªåç±»ä¸å¼ 表âççç¥ï¼ä½ å¯ä»¥ç»å使ç¨<literal><subclass></literal>
+ ä¸<literal><join></literal>ï¼å¦ä¸æ示ï¼
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment" table="PAYMENT">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <discriminator column="PAYMENT_TYPE" type="string"/>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <subclass name="CreditCardPayment" discriminator-value="CREDIT">
+ <join table="CREDIT_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </join>
+ </subclass>
+ <subclass name="CashPayment" discriminator-value="CASH">
+ <join table="CASH_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ ...
+ </join>
+ </subclass>
+ <subclass name="ChequePayment" discriminator-value="CHEQUE">
+ <join table="CHEQUE_PAYMENT" fetch="select">
+ <key column="PAYMENT_ID"/>
+ ...
+ </join>
+ </subclass>
+</class>]]></programlisting>
+
+
+ <para>
+ å¯éç声æ<literal>fetch="select"</literal>ï¼æ¯ç¨æ¥åè¯Hibernateï¼å¨æ¥è¯¢è¶
ç±»æ¶ï¼
+ ä¸è¦ä½¿ç¨å¤é¨è¿æ¥(outer join)æ¥æååç±»<literal>ChequePayment</literal>çæ°æ®ã
+ </para>
+
+ </sect2>
+
+ <sect2 id="inheritance-mixing-tableperclass-tablepersubclass">
+ <title>æ··å使ç¨âæ¯ä¸ªç±»åå±ç»æä¸å¼ 表âåâæ¯ä¸ªåç±»ä¸å¼ 表â</title>
+
+ <para>
+ ä½ çè³å¯ä»¥éåå¦ä¸æ¹æ³æ··å使ç¨âæ¯ä¸ªç±»åå±ç»æä¸å¼ 表âåâæ¯ä¸ªåç±»ä¸å¼ 表âè¿ä¸¤ç§çç¥ï¼
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment" table="PAYMENT">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <discriminator column="PAYMENT_TYPE" type="string"/>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <subclass name="CreditCardPayment" discriminator-value="CREDIT">
+ <join table="CREDIT_PAYMENT">
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </join>
+ </subclass>
+ <subclass name="CashPayment" discriminator-value="CASH">
+ ...
+ </subclass>
+ <subclass name="ChequePayment" discriminator-value="CHEQUE">
+ ...
+ </subclass>
+</class>]]></programlisting>
+
+ <para>
+ 对ä¸è¿°ä»»ä½ä¸ç§æ å°çç¥èè¨ï¼æåæ ¹ç±»<literal>Payment</literal>ç
+ å
³èæ¯ä½¿ç¨<literal><many-to-one></literal>è¿è¡æ å°çã
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="payment" column="PAYMENT_ID" class="Payment"/>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="inheritance-tableperconcrete" revision="2">
+ <title>æ¯ä¸ªå
·ä½ç±»ä¸å¼ 表(Table per concrete class)</title>
+
+ <para>
+ 对äºâæ¯ä¸ªå
·ä½ç±»ä¸å¼ 表âçæ å°çç¥ï¼å¯ä»¥éç¨ä¸¤ç§æ¹æ³ã第ä¸ç§æ¹æ³æ¯ä½¿ç¨
+ <literal><union-subclass></literal>ã
+ </para>
+
+ <programlisting><![CDATA[<class name="Payment">
+ <id name="id" type="long" column="PAYMENT_ID">
+ <generator class="sequence"/>
+ </id>
+ <property name="amount" column="AMOUNT"/>
+ ...
+ <union-subclass name="CreditCardPayment" table="CREDIT_PAYMENT">
+ <property name="creditCardType" column="CCTYPE"/>
+ ...
+ </union-subclass>
+ <union-subclass name="CashPayment" table="CASH_PAYMENT">
+ ...
+ </union-subclass>
+ <union-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
+ ...
+ </union-subclass>
+</class>]]></programlisting>
+
+ <para>
+ è¿éæ¶åä¸å¼ ä¸åç±»ç¸å
³ç表ãæ¯å¼ 表为对åºç±»çææå±æ§ï¼å
æ¬ä»è¶
类继æ¿çå±æ§ï¼å®ä¹ç¸åºå段ã
+ </para>
+
+ <para>
+ è¿ç§æ¹å¼çå±éå¨äºï¼å¦æä¸ä¸ªå±æ§å¨è¶
ç±»ä¸åäºæ å°ï¼å
¶å段åå¿
é¡»ä¸ææåç±»
+ 表ä¸å®ä¹çç¸åã(æ们å¯è½ä¼å¨Hibernateçåç»åå¸çæ¬ä¸æ¾å®½æ¤éå¶ã)
+ ä¸å
许å¨èååç±»(union subclass)ç继æ¿å±æ¬¡ä¸ä½¿ç¨æ è¯çæå¨çç¥(identity generator strategy),
+ å®é
ä¸, 主é®çç§å(primary key seed)ä¸å¾ä¸ä¸ºåä¸ç»§æ¿å±æ¬¡ä¸çå
¨é¨è¢«èååç±»æå
±ç¨.
+ </para>
+
+ <para>
+ åè¥è¶
ç±»æ¯æ½è±¡ç±»ï¼è¯·ä½¿ç¨<literal>abstract="true"</literal>ãå½ç¶ï¼åè¥å®ä¸æ¯æ½è±¡çï¼éè¦ä¸ä¸ªé¢å¤ç表ï¼ä¸é¢çä¾åä¸ï¼é»è®¤æ¯<literal>PAYMENT</literal>ï¼ï¼æ¥ä¿åè¶
ç±»çå®ä¾ã
+ </para>
+
+ </sect2>
+
+ <sect2 id="inheritance-tableperconcreate-polymorphism">
+ <title>Table per concrete class, using implicit polymorphism</title>
+ <title>æ¯ä¸ªå
·ä½ç±»ä¸å¼ 表ï¼ä½¿ç¨éå¼å¤æ</title>
+
+ <para>
+ å¦ä¸ç§å¯ä¾éæ©çæ¹æ³æ¯éç¨éå¼å¤æï¼
+ </para>
+
+ <programlisting><![CDATA[<class name="CreditCardPayment" table="CREDIT_PAYMENT">
+ <id name="id" type="long" column="CREDIT_PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="amount" column="CREDIT_AMOUNT"/>
+ ...
+</class>
+
+<class name="CashPayment" table="CASH_PAYMENT">
+ <id name="id" type="long" column="CASH_PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="amount" column="CASH_AMOUNT"/>
+ ...
+</class>
+
+<class name="ChequePayment" table="CHEQUE_PAYMENT">
+ <id name="id" type="long" column="CHEQUE_PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="amount" column="CHEQUE_AMOUNT"/>
+ ...
+</class>]]></programlisting>
+
+ <para>
+ 注æï¼æ们没æå¨ä»»ä½å°æ¹æç¡®çæåæ¥å£<literal>Payment</literal>ãåæ¶æ³¨æ
+ <literal>Payment</literal>çå±æ§å¨æ¯ä¸ªåç±»ä¸é½è¿è¡äºæ å°ãå¦æä½ æ³é¿å
éå¤ï¼
+ å¯ä»¥èè使ç¨XMLå®ä½(ä¾å¦ï¼ä½äº<literal>DOCTYPE</literal>声æå
ç
+ <literal>[ <!ENTITY allproperties SYSTEM "allproperties.xml"> ]</literal>
+ åæ å°ä¸ç<literal>&allproperties;</literal>)ã
+ </para>
+
+ <para>
+ è¿ç§æ¹æ³ç缺é·å¨äºï¼å¨Hibernateæ§è¡å¤ææ¥è¯¢æ¶(polymorphic queries)æ æ³çæ带
+ <literal>UNION</literal>çSQLè¯å¥ã
+ </para>
+
+ <para>
+ 对äºè¿ç§æ å°çç¥èè¨ï¼é常ç¨<literal><any></literal>æ¥å®ç°å°
+ <literal>Payment</literal>çå¤æå
³èæ å°ã
+ </para>
+
+ <programlisting><![CDATA[<any name="payment" meta-type="string" id-type="long">
+ <meta-value value="CREDIT" class="CreditCardPayment"/>
+ <meta-value value="CASH" class="CashPayment"/>
+ <meta-value value="CHEQUE" class="ChequePayment"/>
+ <column name="PAYMENT_CLASS"/>
+ <column name="PAYMENT_ID"/>
+</any>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="inheritace-mixingpolymorphism">
+ <title>éå¼å¤æåå
¶ä»ç»§æ¿æ å°æ··å使ç¨</title>
+
+ <para>
+ 对è¿ä¸æ å°è¿æä¸ç¹éè¦æ³¨æãå 为æ¯ä¸ªåç±»é½å¨åèªç¬ç«çå
ç´ <literal><class></literal>
+ ä¸æ å°(并ä¸<literal>Payment</literal>åªæ¯ä¸ä¸ªæ¥å£)ï¼æ¯ä¸ªåç±»å¯ä»¥å¾å®¹æçæ为å¦ä¸
+ 个继æ¿ä½ç³»ä¸çä¸é¨åï¼(ä½ ä»ç¶å¯ä»¥å¯¹æ¥å£<literal>Payment</literal>使ç¨å¤ææ¥è¯¢ã)
+ </para>
+
+ <programlisting><![CDATA[<class name="CreditCardPayment" table="CREDIT_PAYMENT">
+ <id name="id" type="long" column="CREDIT_PAYMENT_ID">
+ <generator class="native"/>
+ </id>
+ <discriminator column="CREDIT_CARD" type="string"/>
+ <property name="amount" column="CREDIT_AMOUNT"/>
+ ...
+ <subclass name="MasterCardPayment" discriminator-value="MDC"/>
+ <subclass name="VisaPayment" discriminator-value="VISA"/>
+</class>
+
+<class name="NonelectronicTransaction" table="NONELECTRONIC_TXN">
+ <id name="id" type="long" column="TXN_ID">
+ <generator class="native"/>
+ </id>
+ ...
+ <joined-subclass name="CashPayment" table="CASH_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ <property name="amount" column="CASH_AMOUNT"/>
+ ...
+ </joined-subclass>
+ <joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
+ <key column="PAYMENT_ID"/>
+ <property name="amount" column="CHEQUE_AMOUNT"/>
+ ...
+ </joined-subclass>
+</class>]]></programlisting>
+
+ <para>
+ æ们è¿æ¯æ²¡ææç¡®çæå°<literal>Payment</literal>ã
+ å¦ææ们é对æ¥å£<literal>Payment</literal>æ§è¡æ¥è¯¢
+ ââå¦<literal>from Payment</literal>ââ Hibernate
+ èªå¨è¿å<literal>CreditCardPayment</literal>(åå®çåç±»ï¼å 为
+ å®ä»¬ä¹å®ç°äºæ¥å£<literal>Payment</literal>)ã
+ <literal>CashPayment</literal>å<literal>Chequepayment</literal>çå®ä¾ï¼
+ ä½ä¸è¿å<literal>NonelectronicTransaction</literal>çå®ä¾ã
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="inheritance-limitations">
+ <title>éå¶</title>
+
+ <para>
+ 对âæ¯ä¸ªå
·ä½ç±»æ å°ä¸å¼ 表âï¼table per concrete-classï¼çæ å°çç¥èè¨ï¼éå¼å¤æç
+ æ¹å¼æä¸å®çéå¶ãè<literal><union-subclass></literal>æ å°çéå¶å没æé£
+ ä¹ä¸¥æ ¼ã
+ </para>
+
+ <para>
+ ä¸é¢è¡¨æ ¼ä¸ååºäºå¨Hibernteä¸âæ¯ä¸ªå
·ä½ç±»ä¸å¼ 表âççç¥åéå¼å¤æçéå¶ã
+ </para>
+
+ <table frame="topbot">
+ <title>继æ¿æ å°ç¹æ§(Features of inheritance mappings) </title>
+ <tgroup cols='8' align='left' colsep='1' rowsep='1'>
+ <colspec colname='c1' colwidth="1*"/>
+ <colspec colname='c2' colwidth="1*"/>
+ <colspec colname='c3' colwidth="1*"/>
+ <colspec colname='c4' colwidth="1*"/>
+ <colspec colname='c5' colwidth="1*"/>
+ <colspec colname='c6' colwidth="1*"/>
+ <colspec colname='c7' colwidth="1*"/>
+ <colspec colname='c8' colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>继æ¿çç¥(Inheritance strategy)</entry>
+ <entry>å¤æå¤å¯¹ä¸</entry>
+ <entry>å¤æä¸å¯¹ä¸</entry>
+ <entry>å¤æä¸å¯¹å¤</entry>
+ <entry>å¤æå¤å¯¹å¤</entry>
+ <entry>å¤æ <literal>load()/get()</literal></entry>
+ <entry>å¤ææ¥è¯¢</entry>
+ <entry>å¤æè¿æ¥(join)</entry>
+ <entry>å¤è¿æ¥(Outer join)读å</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+
+ <entry>æ¯ä¸ªç±»åå±ç»æä¸å¼ 表</entry>
+ <entry><literal><many-to-one></literal></entry>
+ <entry><literal><one-to-one></literal></entry>
+ <entry><literal><one-to-many></literal></entry>
+ <entry><literal><many-to-many></literal></entry>
+ <entry><literal>s.get(Payment.class, id)</literal></entry>
+ <entry><literal>from Payment p</literal></entry>
+ <entry><literal>from Order o join o.payment p</literal></entry>
+ <entry><emphasis>æ¯æ</emphasis></entry>
+ </row>
+ <row>
+ <entry>æ¯ä¸ªåç±»ä¸å¼ 表</entry>
+ <entry><literal><many-to-one></literal></entry>
+ <entry><literal><one-to-one></literal></entry>
+ <entry><literal><one-to-many></literal></entry>
+ <entry><literal><many-to-many></literal></entry>
+ <entry><literal>s.get(Payment.class, id)</literal></entry>
+ <entry><literal>from Payment p</literal></entry>
+ <entry><literal>from Order o join o.payment p</literal></entry>
+ <entry><emphasis>æ¯æ</emphasis></entry>
+ </row>
+ <row>
+
+ <entry>æ¯ä¸ªå
·ä½ç±»ä¸å¼ 表(union-subclass)</entry>
+ <entry><literal><many-to-one></literal></entry>
+ <entry><literal><one-to-one></literal></entry>
+ <entry><literal><one-to-many></literal> (ä»
对äº<literal>inverse="true"</literal>çæ
åµ)</entry>
+ <entry><literal><many-to-many></literal></entry>
+ <entry><literal>s.get(Payment.class, id)</literal></entry>
+ <entry><literal>from Payment p</literal></entry>
+ <entry><literal>from Order o join o.payment p</literal></entry>
+ <entry><emphasis>æ¯æ</emphasis></entry>
+ </row>
+ <row>
+
+ <entry>æ¯ä¸ªå
·ä½ç±»ä¸å¼ 表(éå¼å¤æ)</entry>
+ <entry><literal><any></literal></entry>
+ <entry><emphasis>ä¸æ¯æ</emphasis></entry>
+ <entry><emphasis>ä¸æ¯æ</emphasis></entry>
+ <entry><literal><many-to-any></literal></entry>
+ <entry><literal>s.createCriteria(Payment.class).add( Restrictions.idEq(id) ).uniqueResult()</literal></entry>
+ <entry><literal>from Payment p</literal></entry>
+ <entry><emphasis>ä¸æ¯æ</emphasis></entry>
+ <entry><emphasis>ä¸æ¯æ</emphasis></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect1>
+
+ </chapter>
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/kicken.pl
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/kicken.pl (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/kicken.pl 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,31 @@
+#!/bin/env perl
+
+use strict;
+use warnings;
+
+
+open(F, $ARGV[0]) or die "Can't open file $ARGV[0]: $!\n";
+
+my $status = 1;
+
+READLINE:
+while (<F>) {
+ if (/\s+\+{3,}/) {
+ $status = -1;
+ next READLINE;
+ }
+ if (/\s+-{3,}/) {
+ $status = 1;
+ next READLINE;
+ }
+ if (/\s+={3,}/) {
+ next READLINE;
+ }
+
+ if ($status < 0) {
+ next READLINE;
+ }
+
+ print $_;
+}
+
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/performance.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/performance.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/performance.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1315 @@
+<chapter id="performance">
+ <title>æåæ§è½
+ </title>
+ <sect1 id="performance-fetching" revision="2">
+ <title>
+ æåçç¥(Fetching strategies)
+ </title>
+
+ <para>
+
+ <emphasis>æåçç¥ï¼fetching strategyï¼</emphasis> æ¯æï¼å½åºç¨ç¨åºéè¦å¨ï¼Hibernateå®ä½å¯¹è±¡å¾çï¼å
³èå
³ç³»é´è¿è¡å¯¼èªçæ¶åï¼
+ Hibernateå¦ä½è·åå
³è对象ççç¥ãæåçç¥å¯ä»¥å¨O/Ræ å°çå
æ°æ®ä¸å£°æï¼ä¹å¯ä»¥å¨ç¹å®çHQL
+ æ<literal>æ¡ä»¶æ¥è¯¢ï¼Criteria Queryï¼</literal>ä¸é载声æã
+ </para>
+
+ <para>
+ Hibernate3 å®ä¹äºå¦ä¸å ç§æåçç¥ï¼
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+
+ <emphasis>è¿æ¥æåï¼Join fetchingï¼</emphasis> - Hibernateéè¿
+ å¨<literal>SELECT</literal>è¯å¥ä½¿ç¨<literal>OUTER JOIN</literal>ï¼å¤è¿æ¥ï¼æ¥
+ è·å¾å¯¹è±¡çå
³èå®ä¾æè
å
³èéåã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+
+ <emphasis>æ¥è¯¢æåï¼Select fetchingï¼</emphasis> - å¦å¤åéä¸æ¡ <literal>SELECT</literal>
+ è¯å¥æåå½å对象çå
³èå®ä½æéåãé¤éä½ æ¾å¼çæå®<literal>lazy="false"</literal>ç¦æ¢
+ 延è¿æåï¼lazy fetchingï¼ï¼å¦ååªæå½ä½ çæ£è®¿é®å
³èå
³ç³»çæ¶åï¼æä¼æ§è¡ç¬¬äºæ¡selectè¯å¥ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>åæ¥è¯¢æåï¼Subselect fetchingï¼</emphasis> - å¦å¤åéä¸æ¡<literal>SELECT</literal>
+ è¯å¥æåå¨åé¢æ¥è¯¢å°ï¼æè
æåå°ï¼çææå®ä½å¯¹è±¡çå
³èéåãé¤éä½ æ¾å¼çæå®<literal>lazy="false"</literal>
+ ç¦æ¢å»¶è¿æåï¼lazy fetchingï¼ï¼å¦ååªæå½ä½ çæ£è®¿é®å
³èå
³ç³»çæ¶åï¼æä¼æ§è¡ç¬¬äºæ¡selectè¯å¥ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+
+ <emphasis>æ¹éæåï¼Batch fetchingï¼</emphasis> - 对æ¥è¯¢æåçä¼åæ¹æ¡ï¼
+ éè¿æå®ä¸ä¸ªä¸»é®æå¤é®å表ï¼Hibernate使ç¨åæ¡<literal>SELECT</literal>è¯å¥è·åä¸æ¹å¯¹è±¡å®ä¾æéåã
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Hibernateä¼åºåä¸ååç§æ
åµï¼
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>Immediate fetchingï¼ç«å³æå</emphasis> - å½å®¿ä¸»è¢«å è½½æ¶ï¼å
³èãéåæå±æ§è¢«ç«å³æåã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Lazy collection fetchingï¼å»¶è¿éåæå</emphasis>- ç´å°åºç¨ç¨åºå¯¹éåè¿è¡äºä¸æ¬¡æä½æ¶ï¼éåæ被æåãï¼å¯¹éåèè¨è¿æ¯é»è®¤è¡ä¸ºãï¼
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>"Extra-lazy" collection fetching,"Extra-lazy"éåæå</emphasis> -对éåç±»ä¸çæ¯ä¸ªå
ç´ èè¨ï¼é½æ¯ç´å°éè¦æ¶æå»è®¿é®æ°æ®åºãé¤éç»å¯¹å¿
è¦ï¼Hibernateä¸ä¼è¯å¾å»ææ´ä¸ªéåé½æåå°å
åéæ¥ï¼éç¨äºé常大çéåï¼ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Proxy fetchingï¼ä»£çæå</emphasis> - 对è¿ååå¼çå
³èèè¨ï¼å½å
¶æ个æ¹æ³è¢«è°ç¨ï¼èé对å
¶å
³é®åè¿è¡getæä½æ¶ææåã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>"No-proxy" fetching,é代çæå</emphasis> - 对è¿ååå¼çå
³èèè¨ï¼å½å®ä¾åé被访é®çæ¶åè¿è¡æåãä¸ä¸é¢ç代çæåç¸æ¯ï¼è¿ç§æ¹æ³æ²¡æé£ä¹â延è¿âå¾å害(å°±ç®åªè®¿é®æ è¯ç¬¦ï¼ä¹ä¼å¯¼è´å
³èæå)ä½æ¯æ´å éæï¼å 为对åºç¨ç¨åºæ¥è¯´ï¼ä¸åçå°proxyãè¿ç§æ¹æ³éè¦å¨ç¼è¯æé´è¿è¡åèç å¢å¼ºæä½ï¼å æ¤å¾å°éè¦ç¨å°ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Lazy attribute fetchingï¼å±æ§å»¶è¿å è½½</emphasis> - 对å±æ§æè¿ååå¼çå
³èèè¨ï¼å½å
¶å®ä¾åé被访é®çæ¶åè¿è¡æåãéè¦ç¼è¯æåèç 强åï¼å æ¤è¿ä¸æ¹æ³å¾å°æ¯å¿
è¦çã
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ è¿éæ两个æ£äº¤çæ¦å¿µï¼å
³è<emphasis>ä½æ¶</emphasis>被æåï¼ä»¥å被<emphasis>å¦ä½</emphasis>æåï¼ä¼éç¨ä»ä¹æ ·çSQLè¯å¥ï¼ãä¸è¦æ··æ·å®ä»¬ï¼æ们使ç¨<literal>æå</literal>æ¥æ¹åæ§è½ãæ们使ç¨<literal>延è¿</literal>æ¥å®ä¹ä¸äºå¥çº¦ï¼å¯¹æç¹å®ç±»çæ个è±ç®¡çå®ä¾ï¼ç¥éæåªäºæ°æ®æ¯å¯ä»¥ä½¿ç¨çã
+ </para>
+
+ <sect2 id="performance-fetching-lazy">
+ <title>æä½å»¶è¿å è½½çå
³è</title>
+
+ <para>
+ é»è®¤æ
åµä¸ï¼Hibernate 3对éå使ç¨å»¶è¿selectæåï¼å¯¹è¿ååå¼çå
³è使ç¨å»¶è¿ä»£çæåã对å ä¹æ¯ææçåºç¨èè¨ï¼å
¶ç»å¤§å¤æ°çå
³èï¼è¿ç§çç¥é½æ¯ææçã
+ </para>
+
+ <para>
+ <emphasis>注æ:</emphasis>åè¥ä½ 设置äº<literal>hibernate.default_batch_fetch_size</literal>,Hibernateä¼å¯¹å»¶è¿å è½½éåæ¹éæåä¼åæªæ½ï¼è¿ç§ä¼åä¹å¯è½ä¼å¨æ´ç»åç级å«æå¼ï¼ã
+ </para>
+
+ <para>
+ ç¶èï¼ä½ å¿
é¡»äºè§£å»¶è¿æå带æ¥çä¸ä¸ªé®é¢ãå¨ä¸ä¸ªæå¼çHibernate sessionä¸ä¸æä¹å¤è°ç¨å»¶è¿éåä¼å¯¼è´ä¸æ¬¡æå¤ãæ¯å¦ï¼
+ </para>
+
+ <programlisting><![CDATA[s = sessions.openSession();
+Transaction tx = s.beginTransaction();
+
+User u = (User) s.createQuery("from User u where u.name=:userName")
+ .setString("userName", userName).uniqueResult();
+Map permissions = u.getPermissions();
+
+tx.commit();
+s.close();
+
+Integer accessLevel = (Integer) permissions.get("accounts"); // Error!]]></programlisting>
+
+ <para>
+ å¨<literal>Session</literal>å
³éåï¼permessionséåå°æ¯æªå®ä¾åçãä¸åå¯ç¨ï¼å æ¤æ æ³æ£å¸¸è½½å
¥å
¶ç¶æã
+ <emphasis>Hibernate对è±ç®¡å¯¹è±¡ä¸æ¯æ延è¿å®ä¾å</emphasis>. è¿éçä¿®æ¹æ¹æ³æ¯ï¼å°permissions读åæ°æ®ç代ç
+ 移å°tx.commit()ä¹åã
+ </para>
+
+ <para>
+ é¤æ¤ä¹å¤ï¼éè¿å¯¹å
³èæ å°æå®<literal>lazy="false"</literal>,æ们ä¹å¯ä»¥ä½¿ç¨é延è¿çéåæå
³èãä½æ¯ï¼
+ 对ç»å¤§é¨åéåæ¥è¯´ï¼æ´æ¨è使ç¨å»¶è¿æ¹å¼æåæ°æ®ãå¦æå¨ä½ ç对象模åä¸å®ä¹äºå¤ªå¤çé延è¿å
³èï¼Hibernateæç»å ä¹éè¦å¨æ¯ä¸ªäºå¡ä¸è½½å
¥æ´ä¸ªæ°æ®åºå°å
åä¸ï¼
+ </para>
+
+ <para>
+
+ ä½æ¯ï¼å¦ä¸æ¹é¢ï¼å¨ä¸äºç¹æ®çäºå¡ä¸ï¼æ们ä¹ç»å¸¸éè¦ä½¿ç¨å°è¿æ¥æåï¼å®æ¬èº«ä¸å°±æ¯é延è¿çï¼ï¼ä»¥ä»£æ¿æ¥è¯¢æåã
+ ä¸é¢æ们å°ä¼å¾å¿«æç½å¦ä½å
·ä½çå®å¶Hibernateä¸çæåçç¥ãå¨Hibernate3ä¸ï¼å
·ä½éæ©åªç§æåçç¥çæºå¶æ¯åéæ©
+ åå¼å
³èæéåå
³èç¸ä¸è´çã
+ </para>
+ </sect2>
+
+
+ <sect2 id="performance-fetching-custom" revision="4">
+ <title>
+
+ è°æ´æåçç¥ï¼Tuning fetch strategiesï¼
+ </title>
+
+ <para>
+
+ æ¥è¯¢æåï¼é»è®¤çï¼å¨N+1æ¥è¯¢çæ
åµä¸æ¯æå
¶èå¼±çï¼å æ¤æ们å¯è½ä¼è¦æ±å¨æ å°ææ¡£ä¸å®ä¹ä½¿ç¨è¿æ¥æåï¼
+ </para>
+ <programlisting><![CDATA[<set name="permissions"
+ fetch="join">
+ <key column="userId"/>
+ <one-to-many class="Permission"/>
+</set]]></programlisting>
+
+ <programlisting><![CDATA[<many-to-one name="mother" class="Cat" fetch="join"/>]]></programlisting>
+
+ <para>
+ å¨æ å°ææ¡£ä¸å®ä¹ç<literal>æå</literal>çç¥å°ä¼å¯¹ä»¥ä¸å表æ¡ç®äº§çå½±åï¼
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+
+ éè¿<literal>get()</literal>æ<literal>load()</literal>æ¹æ³åå¾æ°æ®ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ åªæå¨å
³èä¹é´è¿è¡å¯¼èªæ¶ï¼æä¼éå¼çåå¾æ°æ®ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>æ¡ä»¶æ¥è¯¢</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ 使ç¨äº<literal>subselect</literal>æåçHQLæ¥è¯¢
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ä¸ç®¡ä½ 使ç¨åªç§æåçç¥ï¼å®ä¹ä¸ºé延è¿çç±»å¾ä¼è¢«ä¿è¯ä¸å®è£
è½½å
¥å
åã注æè¿å¯è½æå³çå¨ä¸æ¡HQLæ¥è¯¢åç´§è·çä¸ç³»åçæ¥è¯¢ã
+ </para>
+
+ <para>
+ é常æ
åµä¸ï¼æ们并ä¸ä½¿ç¨æ å°ææ¡£è¿è¡æåçç¥çå®å¶ãæ´å¤çæ¯ï¼ä¿æå
¶é»è®¤å¼ï¼ç¶åå¨ç¹å®çäºå¡ä¸ï¼
+ 使ç¨HQLç<literal>å·¦è¿æ¥æåï¼left join fetchï¼</literal> 对å
¶è¿è¡éè½½ãè¿å°éç¥
+ Hibernateå¨ç¬¬ä¸æ¬¡æ¥è¯¢ä¸ä½¿ç¨å¤é¨å
³èï¼outer joinï¼ï¼ç´æ¥å¾å°å
¶å
³èæ°æ®ã
+ å¨<literal>æ¡ä»¶æ¥è¯¢</literal> APIä¸ï¼åºè¯¥è°ç¨ <literal>setFetchMode(FetchMode.JOIN)</literal>è¯å¥ã
+ </para>
+
+ <para>
+ ä¹è®¸ä½ å欢ä»
ä»
éè¿æ¡ä»¶æ¥è¯¢ï¼å°±å¯ä»¥æ¹å<literal>get()</literal>
+ æ <literal>load()</literal>è¯å¥ä¸çæ°æ®æåçç¥ãä¾å¦ï¼
+ </para>
+
+ <programlisting><![CDATA[User user = (User) session.createCriteria(User.class)
+ .setFetchMode("permissions", FetchMode.JOIN)
+ .add( Restrictions.idEq(userId) )
+ .uniqueResult();]]></programlisting>
+
+ <para>
+
+ ï¼è¿å°±æ¯å
¶ä»ORM解å³æ¹æ¡çâæå计å(fetch plan)âå¨Hibernateä¸ççä»·ç©ãï¼
+ </para>
+
+
+ <para>
+ æªç¶ä¸åçä¸ç§é¿å
N+1次æ¥è¯¢çæ¹æ³æ¯ï¼ä½¿ç¨äºçº§ç¼åã
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-fetching-proxies" revision="2">
+ <title>å端å
³è代çï¼Single-ended association proxiesï¼
+ </title>
+
+ <para>
+ å¨Hinerbateä¸ï¼å¯¹éåç延è¿æåçéç¨äºèªå·±çå®ç°æ¹æ³ãä½æ¯ï¼å¯¹äºå端å
³èç延è¿æåï¼åéè¦éç¨
+ å
¶ä»ä¸åçæºå¶ãå端å
³èçç®æ å®ä½å¿
须使ç¨ä»£çï¼Hihernateå¨è¿è¡æäºè¿å¶çº§ï¼éè¿ä¼å¼çCGLIBåºï¼ï¼
+ 为æä¹
对象å®ç°äºå»¶è¿è½½å
¥ä»£çã
+ </para>
+
+ <para>
+ é»è®¤çï¼Hibernate3å°ä¼ä¸ºææçæä¹
对象产ç代çï¼å¨å¯å¨é¶æ®µï¼ï¼ç¶å使ç¨ä»ä»¬å®ç°
+ <literal>å¤å¯¹ä¸ï¼many-to-oneï¼</literal>å
³èå<literal>ä¸å¯¹ä¸ï¼one-to-oneï¼</literal>
+ å
³èç延è¿æåã
+ </para>
+
+ <para>
+ å¨æ å°æ件ä¸ï¼å¯ä»¥éè¿è®¾ç½®<literal>proxy</literal>å±æ§ä¸ºç®æ class声æä¸ä¸ªæ¥å£ä¾ä»£çæ¥å£ä½¿ç¨ã
+ é»è®¤çï¼Hibernateå°ä¼ä½¿ç¨è¯¥ç±»çä¸ä¸ªåç±»ã
+ <emphasis>注æï¼è¢«ä»£ççç±»å¿
é¡»å®ç°ä¸ä¸ªè³å°å
å¯è§çé»è®¤æé å½æ°ï¼æ们建议ææçæä¹
ç±»é½åºæ¥æè¿æ ·çæé å½æ°</emphasis>
+ </para>
+
+ <para>
+ å¨å¦æ¤æ¹å¼å®ä¹ä¸ä¸ªå¤æç±»çæ¶åï¼æ许å¤å¼å¾æ³¨æç常è§æ§çé®é¢ï¼ä¾å¦ï¼
+ </para>
+
+ <programlisting><![CDATA[<class name="Cat" proxy="Cat">
+ ......
+ <subclass name="DomesticCat">
+ .....
+ </subclass>
+</class>]]></programlisting>
+
+ <para>
+ é¦å
ï¼<literal>Cat</literal>å®ä¾æ°¸è¿ä¸å¯ä»¥è¢«å¼ºå¶è½¬æ¢ä¸º<literal>DomesticCat</literal>,
+ å³ä½¿å®æ¬èº«å°±æ¯<literal>DomesticCat</literal>å®ä¾ã
+ </para>
+
+ <programlisting><![CDATA[Cat cat = (Cat) session.load(Cat.class, id); // instantiate a proxy (does not hit the db)
+if ( cat.isDomesticCat() ) { // hit the db to initialize the proxy
+ DomesticCat dc = (DomesticCat) cat; // Error!
+ ....
+}]]></programlisting>
+
+ <para>
+ å
¶æ¬¡ï¼ä»£ççâ<literal>==</literal>âå¯è½ä¸åæç«ã
+ </para>
+
+ <programlisting><![CDATA[Cat cat = (Cat) session.load(Cat.class, id); // instantiate a Cat proxy
+DomesticCat dc =
+ (DomesticCat) session.load(DomesticCat.class, id); // acquire new DomesticCat proxy!
+System.out.println(cat==dc); // false]]></programlisting>
+
+ <para>
+ è½ç¶å¦æ¤ï¼ä½å®é
æ
åµå¹¶æ²¡æçä¸å»é£ä¹ç³ç³ãè½ç¶æ们ç°å¨æ两个ä¸åçå¼ç¨ï¼åå«æåè¿ä¸¤ä¸ªä¸åç代ç对象ï¼
+ ä½å®é
ä¸ï¼å
¶åºå±åºè¯¥æ¯åä¸ä¸ªå®ä¾å¯¹è±¡ï¼
+ </para>
+
+ <programlisting><![CDATA[cat.setWeight(11.0); // hit the db to initialize the proxy
+System.out.println( dc.getWeight() ); // 11.0]]></programlisting>
+
+ <para>
+ 第ä¸ï¼ä½ ä¸è½å¯¹âfinalç±»âæâå
·æfinalæ¹æ³çç±»â使ç¨CGLIB代çã
+ </para>
+
+ <para>
+ æåï¼å¦æä½ çæä¹
å对象å¨å®ä¾åæ¶éè¦æäºèµæºï¼ä¾å¦ï¼å¨å®ä¾åæ¹æ³ãé»è®¤æé æ¹æ³ä¸ï¼ï¼
+ é£ä¹ä»£ç对象ä¹åæ ·éè¦ä½¿ç¨è¿äºèµæºãå®é
ä¸ï¼ä»£çç±»æ¯æä¹
åç±»çåç±»ã
+ </para>
+
+ <para>
+
+ è¿äºé®é¢é½æºäºJavaçåæ ¹ç»§æ¿æ¨¡åç天çéå¶ãå¦æä½ å¸æé¿å
è¿äºé®é¢ï¼é£ä¹ä½ çæ¯ä¸ªæä¹
åç±»å¿
é¡»å®ç°ä¸ä¸ªæ¥å£ï¼
+ å¨æ¤æ¥å£ä¸å·²ç»å£°æäºå
¶ä¸å¡æ¹æ³ãç¶åï¼ä½ éè¦å¨æ å°ææ¡£ä¸åæå®è¿äºæ¥å£ãä¾å¦ï¼
+ </para>
+
+ <programlisting><![CDATA[<class name="CatImpl" proxy="Cat">
+ ......
+ <subclass name="DomesticCatImpl" proxy="DomesticCat">
+ .....
+ </subclass>
+</class>]]></programlisting>
+
+ <para>
+
+ è¿é<literal>CatImpl</literal>å®ç°äº<literal>Cat</literal>æ¥å£ï¼
+ <literal>DomesticCatImpl</literal>å®ç°<literal>DomesticCat</literal>æ¥å£ã
+ å¨<literal>load()</literal>ã<literal>iterate()</literal>æ¹æ³ä¸å°±ä¼è¿å
+ <literal>Cat</literal>å<literal>DomesticCat</literal>ç代ç对象ã
+ (注æ<literal>list()</literal>并ä¸ä¼è¿å代ç对象ã)
+ </para>
+
+ <programlisting><![CDATA[Cat cat = (Cat) session.load(CatImpl.class, catid);
+Iterator iter = session.iterate("from CatImpl as cat where cat.name='fritz'");
+Cat fritz = (Cat) iter.next();]]></programlisting>
+
+ <para>
+
+ è¿éï¼å¯¹è±¡ä¹é´çå
³ç³»ä¹å°è¢«å»¶è¿è½½å
¥ãè¿å°±æå³çï¼ä½ åºè¯¥å°å±æ§å£°æ为<literal>Cat</literal>ï¼èä¸æ¯<literal>CatImpl</literal>ã
+ </para>
+
+ <para>
+
+ ä½æ¯ï¼å¨æäºæ¹æ³ä¸æ¯<emphasis>ä¸éè¦</emphasis>使ç¨ä»£ççãä¾å¦ï¼
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>equals()</literal>æ¹æ³ï¼å¦ææä¹
类没æéè½½<literal>equals()</literal>æ¹æ³ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>hashCode()</literal>æ¹æ³ï¼å¦ææä¹
类没æéè½½<literal>hashCode()</literal>æ¹æ³ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ æ å¿ç¬¦çgetteræ¹æ³ã
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Hibernateå°ä¼è¯å«åºé£äºéè½½äº<literal>equals()</literal>ãæ<literal>hashCode()</literal>æ¹æ³çæä¹
åç±»ã
+ </para>
+ <para>
+ è¥éæ©<literal>lazy="no-proxy"</literal>èéé»è®¤ç<literal>lazy="proxy"</literal>ï¼æ们å¯ä»¥é¿å
ç±»å转æ¢å¸¦æ¥çé®é¢ãç¶èï¼è¿æ ·æ们就éè¦ç¼è¯æåèç å¢å¼ºï¼å¹¶ä¸ææçæä½é½ä¼å¯¼è´ç«å»è¿è¡ä»£çåå§åã
+
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-fetching-initialization" revision="1">
+ <title>å®ä¾åéåå代çï¼Initializing collections and proxiesï¼
+ </title>
+
+ <para>
+ å¨<literal>Session</literal>èå´ä¹å¤è®¿é®æªåå§åçéåæ代çï¼Hibernateå°ä¼æåº<literal>LazyInitializationException</literal>å¼å¸¸ã
+ ä¹å°±æ¯è¯´ï¼å¨å离ç¶æä¸ï¼è®¿é®ä¸ä¸ªå®ä½ææ¥æçéåï¼æè
访é®å
¶æå代ççå±æ§æ¶ï¼ä¼å¼åæ¤å¼å¸¸ã
+ </para>
+
+ <para>
+
+ ææ¶åæ们éè¦ä¿è¯æ个代çæè
éåå¨Sessionå
³éå就已ç»è¢«åå§åäºã
+ å½ç¶ï¼æ们å¯ä»¥éè¿å¼ºè¡è°ç¨<literal>cat.getSex()</literal>æè
<literal>cat.getKittens().size()</literal>ä¹ç±»çæ¹æ³æ¥ç¡®ä¿è¿ä¸ç¹ã
+ ä½æ¯è¿æ ·çç¨åºä¼é æ读è
ççæï¼ä¹ä¸ç¬¦åé常ç代ç è§èã
+ </para>
+
+ <para>
+
+ éææ¹æ³<literal>Hibernate.initialized()</literal>
+ ä¸ºä½ çåºç¨ç¨åºæä¾äºä¸ä¸ªä¾¿æ·çéå¾æ¥å»¶è¿å è½½éåæ代çã
+ åªè¦å®çSessionå¤äºopenç¶æï¼<literal>Hibernate.initialize(cat)</literal> å°ä¼ä¸ºcat强å¶å¯¹ä»£çå®ä¾åã
+ åæ ·ï¼<literal>Hibernate.initialize( cat.getKittens() )</literal> 对kittensçéåå
·æåæ ·çåè½ã
+ </para>
+
+ <para>
+
+ è¿æå¦å¤ä¸ç§éæ©ï¼å°±æ¯ä¿æ<literal>Session</literal>ä¸ç´å¤äºopenç¶æï¼ç´å°ææéè¦çéåæ代çé½è¢«è½½å
¥ã
+ å¨æäºåºç¨æ¶æä¸ï¼ç¹å«æ¯å¯¹äºé£äºä½¿ç¨Hibernateè¿è¡æ°æ®è®¿é®ç代ç ï¼ä»¥åé£äºå¨ä¸ååºç¨å±åä¸åç©çè¿ç¨ä¸ä½¿ç¨Hibernateç代ç ã
+ å¨éåå®ä¾åæ¶ï¼å¦ä½ä¿è¯<literal>Session</literal>å¤äºopenç¶æç»å¸¸ä¼æ¯ä¸ä¸ªé®é¢ãæ两ç§æ¹æ³å¯ä»¥è§£å³æ¤é®é¢ï¼
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ å¨ä¸ä¸ªåºäºWebçåºç¨ä¸ï¼å¯ä»¥å©ç¨servletè¿æ»¤å¨ï¼filterï¼ï¼å¨ç¨æ·è¯·æ±ï¼requestï¼ç»æã页é¢çæ
+ ç»ææ¶å
³é<literal>Session</literal>ï¼è¿é使ç¨äº<emphasis>å¨å±ç¤ºå±ä¿ææå¼Session模å¼ï¼Open Session in Viewï¼</emphasis>ï¼ï¼
+ å½ç¶ï¼è¿å°ä¾èµäºåºç¨æ¡æ¶ä¸å¼å¸¸éè¦è¢«æ£ç¡®çå¤çãå¨è¿åçé¢ç»ç¨æ·ä¹åï¼ä¹è³å¨çæçé¢è¿ç¨ä¸åçå¼å¸¸çæ
åµä¸ï¼
+ æ£ç¡®å
³é<literal>Session</literal>åç»æäºå¡å°æ¯é常éè¦çï¼
+ 请åè§Hibernate wikiä¸ç"Open Session in View"模å¼ï¼ä½ å¯ä»¥æ¾å°ç¤ºä¾ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+
+ å¨ä¸ä¸ªæ¥æåç¬ä¸å¡å±çåºç¨ä¸ï¼ä¸å¡å±å¿
é¡»å¨è¿åä¹åï¼ä¸ºwebå±âåå¤â好å
¶æéçæ°æ®éåãè¿å°±æå³ç
+ ä¸å¡å±åºè¯¥è½½å
¥ææ表ç°å±/webå±æéçæ°æ®ï¼å¹¶å°è¿äºå·²å®ä¾åå®æ¯çæ°æ®è¿åãé常ï¼åºç¨ç¨åºåºè¯¥
+ 为webå±æéçæ¯ä¸ªéåè°ç¨<literal>Hibernate.initialize()</literal>ï¼è¿ä¸ªè°ç¨å¿
é¡»åçå±sessionå
³éä¹åï¼ï¼
+ æè
使ç¨å¸¦æ<literal>FETCH</literal>ä»å¥ï¼æ<literal>FetchMode.JOIN</literal>çHibernateæ¥è¯¢ï¼
+ äºå
åå¾ææçæ°æ®éåãå¦æä½ å¨åºç¨ä¸ä½¿ç¨äº<emphasis>Command</emphasis>模å¼ï¼ä»£æ¿<emphasis>Session Facade</emphasis> ï¼
+ é£ä¹è¿é¡¹ä»»å¡å°ä¼åå¾ç®åçå¤ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ä½ ä¹å¯ä»¥éè¿<literal>merge()</literal>æ<literal>lock()</literal>æ¹æ³ï¼å¨è®¿é®æªå®ä¾åçéåï¼æ代çï¼ä¹åï¼
+ 为å
åè½½å
¥ç对象ç»å®ä¸ä¸ªæ°ç<literal>Session</literal>ã
+ æ¾ç¶ï¼Hibernateå°ä¸ä¼ï¼ä¹ä¸<emphasis>åºè¯¥</emphasis>èªå¨å®æè¿äºä»»å¡ï¼å 为è¿å°å¼å
¥ä¸ä¸ªç¹æ®çäºå¡è¯ä¹ã
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ææ¶åï¼ä½ 并ä¸éè¦å®å
¨å®ä¾åæ´ä¸ªå¤§çéåï¼ä»
éè¦äºè§£å®çé¨åä¿¡æ¯ï¼ä¾å¦å
¶å¤§å°ï¼ãæè
éåçé¨åå
容ã
+ </para>
+
+ <para>
+
+ ä½ å¯ä»¥ä½¿ç¨éåè¿æ»¤å¨å¾å°å
¶éåç大å°ï¼èä¸å¿
å®ä¾åæ´ä¸ªéåï¼
+ </para>
+
+ <programlisting><![CDATA[( (Integer) s.createFilter( collection, "select count(*)" ).list().get(0) ).intValue()]]></programlisting>
+
+ <para>
+ è¿éç<literal>createFilter()</literal>æ¹æ³ä¹å¯ä»¥è¢«ç¨æ¥ææçæåéåçé¨åå
容ï¼èæ éå®ä¾åæ´ä¸ªéåï¼
+ </para>
+
+ <programlisting><![CDATA[s.createFilter( lazyCollection, "").setFirstResult(0).setMaxResults(10).list();]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="performance-fetching-batch">
+ <title>使ç¨æ¹éæåï¼Using batch fetchingï¼
+ </title>
+
+ <para>
+ Hibernateå¯ä»¥å
åææç使ç¨æ¹éæåï¼ä¹å°±æ¯è¯´ï¼å¦æä»
ä¸ä¸ªè®¿é®ä»£çï¼æéåï¼ï¼é£ä¹Hibernateå°ä¸è½½å
¥å
¶ä»æªå®ä¾åç代çã
+ æ¹éæåæ¯å»¶è¿æ¥è¯¢æåçä¼åæ¹æ¡ï¼ä½ å¯ä»¥å¨ä¸¤ç§æ¹éæåæ¹æ¡ä¹é´è¿è¡éæ©ï¼å¨ç±»çº§å«åéå级å«ã
+ </para>
+
+ <para>
+
+ ç±»/å®ä½çº§å«çæ¹éæåå¾å®¹æç解ãåè®¾ä½ å¨è¿è¡æ¶å°éè¦é¢å¯¹ä¸é¢çé®é¢ï¼ä½ å¨ä¸ä¸ª<literal>Session</literal>ä¸è½½å
¥äº25个
+ <literal>Cat</literal>å®ä¾ï¼æ¯ä¸ª<literal>Cat</literal>å®ä¾é½æ¥æä¸ä¸ªå¼ç¨æå<literal>owner</literal>ï¼
+ å
¶æå<literal>Person</literal>ï¼è<literal>Person</literal>ç±»æ¯ä»£çï¼åæ¶<literal>lazy="true"</literal>ã
+ å¦æä½ å¿
é¡»éåæ´ä¸ªcatséåï¼å¯¹æ¯ä¸ªå
ç´ è°ç¨<literal>getOwner()</literal>æ¹æ³ï¼Hibernateå°ä¼é»è®¤çæ§è¡25次<literal>SELECT</literal>æ¥è¯¢ï¼
+ å¾å°å
¶ownerç代ç对象ãè¿æ¶ï¼ä½ å¯ä»¥éè¿å¨æ å°æ件ç<literal>Person</literal>å±æ§ï¼æ¾å¼å£°æ<literal>batch-size</literal>ï¼æ¹åå
¶è¡ä¸ºï¼
+
+ </para>
+
+ <programlisting><![CDATA[<class name="Person" batch-size="10">...</class>]]></programlisting>
+
+ <para>
+ éä¹ï¼Hibernateå°åªéè¦æ§è¡ä¸æ¬¡æ¥è¯¢ï¼åå«ä¸º10ã10ã 5ã
+ </para>
+
+ <para>
+ ä½ ä¹å¯ä»¥å¨éå级å«å®ä¹æ¹éæåãä¾å¦ï¼å¦ææ¯ä¸ª<literal>Person</literal>é½æ¥æä¸ä¸ªå»¶è¿è½½å
¥ç<literal>Cats</literal>éåï¼
+ ç°å¨ï¼<literal>Sesssion</literal>ä¸è½½å
¥äº10个person对象ï¼éåpersonéåå°ä¼å¼èµ·10次<literal>SELECT</literal>æ¥è¯¢ï¼
+ æ¯æ¬¡æ¥è¯¢é½ä¼è°ç¨<literal>getCats()</literal>æ¹æ³ãå¦æä½ å¨<literal>Person</literal>çæ å°å®ä¹é¨åï¼å
许对<literal>cats</literal>æ¹éæå,
+ é£ä¹ï¼Hibernateå°å¯ä»¥é¢å
æåæ´ä¸ªéåã请çä¾åï¼
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <set name="cats" batch-size="3">
+ ...
+ </set>
+</class>]]></programlisting>
+
+ <para>
+ å¦ææ´ä¸ªç<literal>batch-size</literal>æ¯3ï¼ç¬è¯¯ï¼ï¼ï¼é£ä¹Hibernateå°ä¼åå次æ§è¡<literal>SELECT</literal>æ¥è¯¢ï¼
+ æç
§3ã3ã3ã1ç大å°åå«è½½å
¥æ°æ®ãè¿éçæ¯æ¬¡è½½å
¥çæ°æ®éè¿å
·ä½ä¾èµäºå½å<literal>Session</literal>ä¸æªå®ä¾åéåç个æ°ã
+ </para>
+
+ <para>
+ å¦æä½ ç模åä¸æåµå¥çæ ç¶ç»æï¼ä¾å¦å
¸åçå¸åï¼åæç»æï¼bill-of-materials patternï¼ï¼éåçæ¹éæåæ¯é常æç¨çã
+ ï¼å°½ç®¡å¨æ´å¤æ
åµä¸å¯¹æ è¿è¡è¯»åæ¶ï¼<emphasis>åµå¥éåï¼nested setï¼</emphasis>æ<emphasis>åæè·¯å¾(materialized path)</emphasis>ï¼ÃÃï¼
+ æ¯æ´å¥½ç解å³æ¹æ³ãï¼
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-fetching-subselect">
+ <title>使ç¨åæ¥è¯¢æåï¼Using subselect fetchingï¼
+ </title>
+ <para>
+ åè¥ä¸ä¸ªå»¶è¿éåæåå¼ä»£çéè¦æåï¼Hibernateä¼ä½¿ç¨ä¸ä¸ªsubselectéæ°è¿è¡åæ¥çæ¥è¯¢ï¼ä¸æ¬¡æ§è¯»å
¥ææçå®ä¾ãè¿åæ¹éæåçå®ç°æ¹æ³æ¯ä¸æ ·çï¼ä¸ä¼æç ´ç¢çå è½½ã
+ </para>
+
+ <!-- TODO: Write more about this -->
+ </sect2>
+
+ <sect2 id="performance-fetching-lazyproperties">
+ <title>使ç¨å»¶è¿å±æ§æåï¼Using lazy property fetchingï¼
+ </title>
+
+ <para>
+ Hibernate3对åç¬çå±æ§æ¯æ延è¿æåï¼è¿é¡¹ä¼åææ¯ä¹è¢«ç§°ä¸º<emphasis>ç»æåï¼fetch groupsï¼</emphasis>ã
+ 请注æï¼è¯¥ææ¯æ´å¤çå±äºå¸åºç¹æ§ãå¨å®é
åºç¨ä¸ï¼ä¼åè¡è¯»åæ¯ä¼åå读åæ´éè¦ãä½æ¯ï¼ä»
è½½å
¥ç±»çé¨åå±æ§å¨æäºç¹å®æ
åµä¸ä¼æç¨ï¼ä¾å¦å¨åæ表ä¸æ¥æå ç¾åæ°æ®ãæ°æ®æ¨¡åæ æ³æ¹å¨çæ
åµä¸ã
+ </para>
+
+ <para>
+
+ å¯ä»¥å¨æ å°æ件ä¸å¯¹ç¹å®çå±æ§è®¾ç½®<literal>lazy</literal>ï¼å®ä¹è¯¥å±æ§ä¸ºå»¶è¿è½½å
¥ã
+ </para>
+
+ <programlisting><![CDATA[<class name="Document">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="name" not-null="true" length="50"/>
+ <property name="summary" not-null="true" length="200" lazy="true"/>
+ <property name="text" not-null="true" length="2000" lazy="true"/>
+</class>]]></programlisting>
+
+ <para>
+
+ å±æ§ç延è¿è½½å
¥è¦æ±å¨å
¶ä»£ç æ建æ¶å å
¥äºè¿å¶æ示æ令ï¼bytecode instrumentationï¼ï¼å¦æä½ çæä¹
类代ç ä¸æªå«æè¿äºæ令ï¼
+ Hibernateå°ä¼å¿½ç¥è¿äºå±æ§ç延è¿è®¾ç½®ï¼ä»ç¶å°å
¶ç´æ¥è½½å
¥ã
+ </para>
+
+ <para>
+ ä½ å¯ä»¥å¨AntçTaskä¸ï¼è¿è¡å¦ä¸å®ä¹ï¼å¯¹æä¹
类代ç å å
¥âäºè¿å¶æ令ãâ
+ </para>
+
+ <programlisting><![CDATA[<target name="instrument" depends="compile">
+ <taskdef name="instrument" classname="org.hibernate.tool.instrument.InstrumentTask">
+ <classpath path="${jar.path}"/>
+ <classpath path="${classes.dir}"/>
+ <classpath refid="lib.class.path"/>
+ </taskdef>
+
+ <instrument verbose="true">
+ <fileset dir="${testclasses.dir}/org/hibernate/auction/model">
+ <include name="*.class"/>
+ </fileset>
+ </instrument>
+</target>]]></programlisting>
+
+ <para>
+
+ è¿æä¸ç§å¯ä»¥ä¼åçæ¹æ³ï¼å®ä½¿ç¨HQLææ¡ä»¶æ¥è¯¢çæå½±ï¼projectionï¼ç¹æ§ï¼å¯ä»¥é¿å
读åéå¿
è¦çåï¼
+ è¿ä¸ç¹è³å°å¯¹åªè¯»äºå¡æ¯é常æç¨çãå®æ éå¨ä»£ç æ建æ¶âäºè¿å¶æ令âå¤çï¼å æ¤æ¯ä¸ä¸ªæ´å å¼å¾éæ©ç解å³æ¹æ³ã
+ </para>
+
+ <para>
+ ææ¶ä½ éè¦å¨HQLä¸éè¿<literal>æåææå±æ§</literal>ï¼å¼ºè¡æåææå
容ã
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="performance-cache" revision="1">
+ <title>äºçº§ç¼åï¼The Second Level Cacheï¼
+ </title>
+
+ <para>
+ Hibernateç<literal>Session</literal>å¨äºå¡çº§å«è¿è¡æä¹
åæ°æ®çç¼åæä½ã
+ å½ç¶ï¼ä¹æå¯è½åå«ä¸ºæ¯ä¸ªç±»ï¼æéå)ï¼é
ç½®é群ãæJVM级å«(<literal>SessionFactory级å«</literal>)çç¼åã
+ ä½ çè³å¯ä»¥ä¸ºä¹æå
¥ä¸ä¸ªé群çç¼åã注æï¼ç¼åæ°¸è¿ä¸ç¥éå
¶ä»åºç¨ç¨åºå¯¹æä¹
åä»åºï¼æ°æ®åºï¼å¯è½è¿è¡çä¿®æ¹
+ ï¼å³ä½¿å¯ä»¥å°ç¼åæ°æ®è®¾å®ä¸ºå®æ失æï¼ã
+ </para>
+
+ <para revision="1">
+ éè¿å¨<literal>hibernate.cache.provider_class</literal>å±æ§ä¸æå®<literal>org.hibernate.cache.CacheProvider</literal>çæ个å®ç°çç±»å,ä½ å¯ä»¥éæ©è®©Hibernate使ç¨åªä¸ªç¼åå®ç°ãHibernateæå
ä¸äºå¼æºç¼åå®ç°ï¼æä¾å¯¹å®ä»¬çå
ç½®æ¯æï¼è§ä¸è¡¨ï¼ãé¤æ¤ä¹å¤ï¼ä½ ä¹å¯ä»¥å®ç°ä½ èªå·±çå®ç°ï¼å°å®ä»¬æå
¥å°ç³»ç»ä¸ã注æï¼å¨3.2çæ¬ä¹åï¼é»è®¤ä½¿ç¨EhCache ä½ä¸ºç¼åå®ç°ï¼ä½ä»3.2èµ·å°±ä¸åè¿æ ·äºã
+ </para>
+
+ <table frame="topbot" id="cacheproviders" revision="1">
+ <title>
+ ç¼åçç¥æä¾åï¼Cache Providersï¼
+ </title>
+ <tgroup cols='5' align='left' colsep='1' rowsep='1'>
+ <colspec colname='c1' colwidth="1*"/>
+ <colspec colname='c2' colwidth="3*"/>
+ <colspec colname='c3' colwidth="1*"/>
+ <colspec colname='c4' colwidth="1*"/>
+ <colspec colname='c5' colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Cache</entry>
+ <entry>Provider class</entry>
+ <entry>Type</entry>
+ <entry>Cluster Safe</entry>
+ <entry>Query Cache Supported</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>Hashtable (not intended for production use)</entry>
+ <entry><literal>org.hibernate.cache.HashtableCacheProvider</literal></entry>
+ <entry>memory</entry>
+ <entry></entry>
+ <entry>yes</entry>
+ </row>
+ <row>
+ <entry>EHCache</entry>
+ <entry><literal>org.hibernate.cache.EhCacheProvider</literal></entry>
+ <entry>memory, disk</entry>
+ <entry></entry>
+ <entry>yes</entry>
+ </row>
+ <row>
+ <entry>OSCache</entry>
+ <entry><literal>org.hibernate.cache.OSCacheProvider</literal></entry>
+ <entry>memory, disk</entry>
+ <entry></entry>
+ <entry>yes</entry>
+ </row>
+ <row>
+ <entry>SwarmCache</entry>
+ <entry><literal>org.hibernate.cache.SwarmCacheProvider</literal></entry>
+ <entry>clustered (ip multicast)</entry>
+ <entry>yes (clustered invalidation)</entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>JBoss TreeCache</entry>
+ <entry><literal>org.hibernate.cache.TreeCacheProvider</literal></entry>
+ <entry>clustered (ip multicast), transactional</entry>
+ <entry>yes (replication)</entry>
+ <entry>yes (clock sync req.)</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <sect2 id="performance-cache-mapping" revision="2">
+ <title>ç¼åæ å°ï¼Cache mappingsï¼
+ </title>
+
+ <para>
+ ç±»æè
éåæ å°çâ<literal><cache></literal>å
ç´ âå¯ä»¥æä¸åå½¢å¼ï¼
+ </para>
+
+ <programlistingco>
+ <areaspec>
+ <area id="cache1" coords="2 70"/>
+ <area id="cache2" coords="3 70"/>
+ <area id="cache3" coords="4 70"/>
+ </areaspec>
+ <programlisting><![CDATA[<cache
+ usage="transactional|read-write|nonstrict-read-write|read-only"
+ region="RegionName"
+ include="all|non-lazy"
+/>]]></programlisting>
+ <calloutlist>
+ <callout arearefs="cache1">
+ <para>
+
+ <literal>usage</literal>(å¿
é¡»)说æäºç¼åççç¥:
+ <literal>transactional</literal>ã
+ <literal>read-write</literal>ã
+ <literal>nonstrict-read-write</literal>æ
+ <literal>read-only</literal>ã
+ </para>
+ </callout>
+ <callout arearefs="cache2">
+ <para>
+ <literal>region</literal> (å¯é, é»è®¤ä¸ºç±»æè
éåçåå(class or
+ collection role name)) æå®ç¬¬äºçº§ç¼åçåºåå(name of the second level cache
+ region)
+
+ </para>
+ </callout>
+ <callout arearefs="cache3">
+ <para>
+ <literal>include</literal> (å¯é,é»è®¤ä¸º <literal>all</literal>)
+ <literal>non-lazy</literal> å½å±æ§çº§å»¶è¿æåæå¼æ¶, æ 记为<literal>lazy="true"</literal>çå®ä½çå±æ§å¯è½æ æ³è¢«ç¼å
+ </para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+
+ <para>
+ å¦å¤(é¦é?), ä½ å¯ä»¥å¨hibernate.cfg.xmlä¸æå®<literal><class-cache></literal>å
+ <literal><collection-cache></literal> å
ç´ ã
+ </para>
+
+ <para>
+
+ è¿éç<literal>usage</literal> å±æ§ææäº<emphasis>ç¼å并åçç¥ï¼cache concurrency strategyï¼</emphasis>ã
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-cache-readonly">
+ <title>çç¥ï¼åªè¯»ç¼åï¼Strategy: read onlyï¼
+ </title>
+
+ <para>
+ å¦æä½ çåºç¨ç¨åºåªé读åä¸ä¸ªæä¹
åç±»çå®ä¾ï¼èæ é对å
¶ä¿®æ¹ï¼
+ é£ä¹å°±å¯ä»¥å¯¹å
¶è¿è¡<literal>åªè¯»</literal> ç¼åãè¿æ¯æç®åï¼ä¹æ¯å®ç¨æ§æ好çæ¹æ³ãçè³å¨é群ä¸ï¼å®ä¹è½å®ç¾å°è¿ä½ã
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Immutable" mutable="false">
+ <cache usage="read-only"/>
+ ....
+</class>]]></programlisting>
+
+ </sect2>
+
+
+ <sect2 id="performance-cache-readwrite">
+ <title>
+ çç¥:读/åç¼åï¼Strategy: read/writeï¼
+ </title>
+
+ <para>
+
+ å¦æåºç¨ç¨åºéè¦æ´æ°æ°æ®ï¼é£ä¹ä½¿ç¨<literal>读/åç¼å</literal> æ¯è¾åéã
+ å¦æåºç¨ç¨åºè¦æ±âåºååäºå¡âçé离级å«ï¼serializable transaction isolation levelï¼ï¼é£ä¹å°±å³ä¸è½ä½¿ç¨è¿ç§ç¼åçç¥ã
+ å¦æå¨JTAç¯å¢ä¸ä½¿ç¨ç¼åï¼ä½ å¿
é¡»æå®<literal>hibernate.transaction.manager_lookup_class</literal>å±æ§çå¼ï¼
+ éè¿å®ï¼Hibernateæè½ç¥é该åºç¨ç¨åºä¸JTAç<literal>TransactionManager</literal>çå
·ä½çç¥ã
+ å¨å
¶å®ç¯å¢ä¸ï¼ä½ å¿
é¡»ä¿è¯å¨<literal>Session.close()</literal>ãæ<literal>Session.disconnect()</literal>è°ç¨åï¼
+ æ´ä¸ªäºå¡å·²ç»ç»æã å¦æä½ æ³å¨é群ç¯å¢ä¸ä½¿ç¨æ¤çç¥ï¼ä½ å¿
é¡»ä¿è¯åºå±çç¼åå®ç°æ¯æéå®(locking)ãHibernateå
ç½®çç¼åçç¥å¹¶ä¸æ¯æéå®åè½ã
+ </para>
+
+ <programlisting><![CDATA[<class name="eg.Cat" .... >
+ <cache usage="read-write"/>
+ ....
+ <set name="kittens" ... >
+ <cache usage="read-write"/>
+ ....
+ </set>
+</class>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="performance-cache-nonstrict">
+ <title>
+ çç¥:éä¸¥æ ¼è¯»/åç¼åï¼Strategy: nonstrict read/writeï¼
+ </title>
+
+ <para>
+
+ å¦æåºç¨ç¨åºåªå¶å°éè¦æ´æ°æ°æ®ï¼ä¹å°±æ¯è¯´ï¼ä¸¤ä¸ªäºå¡åæ¶æ´æ°åä¸è®°å½çæ
åµå¾ä¸å¸¸è§ï¼ï¼ä¹ä¸éè¦ååä¸¥æ ¼çäºå¡é离ï¼
+ é£ä¹æ¯è¾éå使ç¨<literal>éä¸¥æ ¼è¯»/åç¼å</literal>çç¥ãå¦æå¨JTAç¯å¢ä¸ä½¿ç¨è¯¥çç¥ï¼
+ ä½ å¿
须为å
¶æå®<literal>hibernate.transaction.manager_lookup_class</literal>å±æ§çå¼ï¼
+ å¨å
¶å®ç¯å¢ä¸ï¼ä½ å¿
é¡»ä¿è¯å¨<literal>Session.close()</literal>ãæ<literal>Session.disconnect()</literal>è°ç¨åï¼
+ æ´ä¸ªäºå¡å·²ç»ç»æã
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-cache-transactional">
+ <title>
+
+ çç¥:äºå¡ç¼åï¼transactionalï¼
+ </title>
+
+ <para>
+ Hibernateç<literal>äºå¡ç¼å</literal>çç¥æä¾äºå
¨äºå¡çç¼åæ¯æï¼
+ ä¾å¦å¯¹JBoss TreeCacheçæ¯æãè¿æ ·çç¼ååªè½ç¨äºJTAç¯å¢ä¸ï¼ä½ å¿
é¡»æå®
+ 为å
¶<literal>hibernate.transaction.manager_lookup_class</literal>å±æ§ã
+ </para>
+
+ </sect2>
+
+ <para>
+ 没æä¸ç§ç¼åæä¾åè½å¤æ¯æä¸åçææç¼å并åçç¥ãä¸è¡¨ä¸ååºäºåç§æä¾å¨ãåå
¶åèªéç¨ç并åçç¥ã
+ </para>
+
+ <table frame="topbot">
+ <title>
+ åç§ç¼åæä¾å对ç¼å并åçç¥çæ¯ææ
åµï¼Cache Concurrency Strategy Supportï¼
+ </title>
+ <tgroup cols='5' align='left' colsep='1' rowsep='1'>
+ <colspec colname='c1' colwidth="1*"/>
+ <colspec colname='c2' colwidth="1*"/>
+ <colspec colname='c3' colwidth="1*"/>
+ <colspec colname='c4' colwidth="1*"/>
+ <colspec colname='c5' colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Cache</entry>
+ <entry>read-only</entry>
+ <entry>nonstrict-read-write</entry>
+ <entry>read-write</entry>
+ <entry>transactional</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>Hashtable (not intended for production use)</entry>
+ <entry>yes</entry>
+ <entry>yes</entry>
+ <entry>yes</entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>EHCache</entry>
+ <entry>yes</entry>
+ <entry>yes</entry>
+ <entry>yes</entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>OSCache</entry>
+ <entry>yes</entry>
+ <entry>yes</entry>
+ <entry>yes</entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>SwarmCache</entry>
+ <entry>yes</entry>
+ <entry>yes</entry>
+ <entry></entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>JBoss TreeCache</entry>
+ <entry>yes</entry>
+ <entry></entry>
+ <entry></entry>
+ <entry>yes</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect1>
+
+ <sect1 id="performance-sessioncache" revision="2">
+ <title>
+ 管çç¼åï¼Managing the cachesï¼
+ </title>
+
+ <para>
+ æ 论ä½æ¶ï¼å½ä½ ç»<literal>save()</literal>ã<literal>update()</literal>æ
+ <literal>saveOrUpdate()</literal>æ¹æ³ä¼ éä¸ä¸ªå¯¹è±¡æ¶ï¼æ使ç¨<literal>load()</literal>ã
+ <literal>get()</literal>ã<literal>list()</literal>ã<literal>iterate()</literal>
+ æ<literal>scroll()</literal>æ¹æ³è·å¾ä¸ä¸ªå¯¹è±¡æ¶,
+ 该对象é½å°è¢«å å
¥å°<literal>Session</literal>çå
é¨ç¼åä¸ã
+ </para>
+ <para>
+ å½éåflush()æ¹æ³è¢«è°ç¨æ¶ï¼å¯¹è±¡çç¶æä¼åæ°æ®åºåå¾åæ¥ã
+ å¦æä½ ä¸å¸ææ¤åæ¥æä½åçï¼æè
ä½ æ£å¤ç大é对象ãéè¦å¯¹ææ管çå
åæ¶ï¼ä½ å¯ä»¥è°ç¨<literal>evict()</literal>
+ æ¹æ³ï¼ä»ä¸çº§ç¼åä¸å»æè¿äºå¯¹è±¡åå
¶éåã
+ </para>
+
+ <programlisting><![CDATA[ScrollableResult cats = sess.createQuery("from Cat as cat").scroll(); //a huge result set
+while ( cats.next() ) {
+ Cat cat = (Cat) cats.get(0);
+ doSomethingWithACat(cat);
+ sess.evict(cat);
+}]]></programlisting>
+
+ <para>
+ Sessionè¿æä¾äºä¸ä¸ª<literal>contains()</literal>æ¹æ³ï¼ç¨æ¥å¤ææ个å®ä¾æ¯å¦å¤äºå½åsessionçç¼åä¸ã
+ </para>
+
+ <para>
+ å¦è¥è¦æææç对象ä»sessionç¼åä¸å½»åºæ¸
é¤ï¼åéè¦è°ç¨<literal>Session.clear()</literal>ã
+ </para>
+
+ <para>
+ 对äºäºçº§ç¼åæ¥è¯´ï¼å¨<literal>SessionFactory</literal>ä¸å®ä¹äºè®¸å¤æ¹æ³ï¼
+ æ¸
é¤ç¼åä¸å®ä¾ãæ´ä¸ªç±»ãéåå®ä¾æè
æ´ä¸ªéåã
+ </para>
+
+ <programlisting><![CDATA[sessionFactory.evict(Cat.class, catId); //evict a particular Cat
+sessionFactory.evict(Cat.class); //evict all Cats
+sessionFactory.evictCollection("Cat.kittens", catId); //evict a particular collection of kittens
+sessionFactory.evictCollection("Cat.kittens"); //evict all kitten collections]]></programlisting>
+
+ <para>
+ <literal>CacheMode</literal>åæ°ç¨äºæ§å¶å
·ä½çSessionå¦ä½ä¸äºçº§ç¼åè¿è¡äº¤äºã
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>CacheMode.NORMAL</literal> - ä»äºçº§ç¼åä¸è¯»ãåæ°æ®ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>CacheMode.GET</literal> - ä»äºçº§ç¼åä¸è¯»åæ°æ®ï¼ä»
å¨æ°æ®æ´æ°æ¶å¯¹äºçº§ç¼ååæ°æ®ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>CacheMode.PUT</literal> - ä»
åäºçº§ç¼ååæ°æ®ï¼ä½ä¸ä»äºçº§ç¼åä¸è¯»æ°æ®ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>CacheMode.REFRESH</literal> - ä»
åäºçº§ç¼ååæ°æ®ï¼ä½ä¸ä»äºçº§ç¼åä¸è¯»æ°æ®ãéè¿
+ <literal>hibernate.cache.use_minimal_puts</literal>ç设置ï¼å¼ºå¶äºçº§ç¼åä»æ°æ®åºä¸è¯»åæ°æ®ï¼å·æ°ç¼åå
容ã
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ å¦è¥éè¦æ¥çäºçº§ç¼åææ¥è¯¢ç¼ååºåçå
容ï¼ä½ å¯ä»¥ä½¿ç¨<literal>ç»è®¡ï¼Statisticsï¼</literal> APIã
+ </para>
+
+ <programlisting><![CDATA[Map cacheEntries = sessionFactory.getStatistics()
+ .getSecondLevelCacheStatistics(regionName)
+ .getEntries();]]></programlisting>
+
+ <para>
+ æ¤æ¶ï¼ä½ å¿
é¡»æå·¥æå¼ç»è®¡é项ãå¯éçï¼ä½ å¯ä»¥è®©Hibernateæ´äººå·¥å¯è¯»çæ¹å¼ç»´æ¤ç¼åå
容ã
+ </para>
+
+ <programlisting><![CDATA[hibernate.generate_statistics true
+hibernate.cache.use_structured_entries true]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="performance-querycache" revision="1">
+ <title>æ¥è¯¢ç¼åï¼The Query Cacheï¼
+ </title>
+
+ <para>
+ æ¥è¯¢çç»æéä¹å¯ä»¥è¢«ç¼åãåªæå½ç»å¸¸ä½¿ç¨åæ ·çåæ°è¿è¡æ¥è¯¢æ¶ï¼è¿æä¼æäºç¨å¤ã
+ è¦ä½¿ç¨æ¥è¯¢ç¼åï¼é¦å
ä½ å¿
é¡»æå¼å®ï¼
+ </para>
+
+ <programlisting><![CDATA[hibernate.cache.use_query_cache true]]></programlisting>
+
+ <para>
+
+ 该设置å°ä¼å建两个ç¼ååºå - ä¸ä¸ªç¨äºä¿åæ¥è¯¢ç»æé(<literal>org.hibernate.cache.StandardQueryCache</literal>)ï¼
+ å¦ä¸ä¸ªåç¨äºä¿åæè¿æ¥è¯¢çä¸ç³»å表çæ¶é´æ³(<literal>org.hibernate.cache.UpdateTimestampsCache</literal>)ã
+ 请注æï¼å¨æ¥è¯¢ç¼åä¸ï¼å®å¹¶ä¸ç¼åç»æéä¸æå
å«çå®ä½çç¡®åç¶æï¼å®åªç¼åè¿äºå®ä½çæ è¯ç¬¦å±æ§çå¼ã以ååå¼ç±»åçç»æã
+ æ以æ¥è¯¢ç¼åé常ä¼åäºçº§ç¼åä¸èµ·ä½¿ç¨ã
+ </para>
+
+ <para>
+
+ ç»å¤§å¤æ°çæ¥è¯¢å¹¶ä¸è½ä»æ¥è¯¢ç¼åä¸åçï¼æ以Hibernateé»è®¤æ¯ä¸è¿è¡æ¥è¯¢ç¼åçãå¦è¥éè¦è¿è¡ç¼åï¼è¯·è°ç¨
+ <literal>Query.setCacheable(true)</literal>æ¹æ³ãè¿ä¸ªè°ç¨ä¼è®©æ¥è¯¢å¨æ§è¡è¿ç¨ä¸æ¶å
ä»ç¼åä¸æ¥æ¾ç»æï¼
+ 并å°èªå·±çç»æéæ¾å°ç¼åä¸å»ã
+ </para>
+
+ <para>
+
+ å¦æä½ è¦å¯¹æ¥è¯¢ç¼åç失ææ¿çè¿è¡ç²¾ç¡®çæ§å¶ï¼ä½ å¿
é¡»è°ç¨<literal>Query.setCacheRegion()</literal>æ¹æ³ï¼
+ 为æ¯ä¸ªæ¥è¯¢æå®å
¶å½åçç¼ååºåã
+ </para>
+
+ <programlisting><![CDATA[List blogs = sess.createQuery("from Blog blog where blog.blogger = :blogger")
+ .setEntity("blogger", blogger)
+ .setMaxResults(15)
+ .setCacheable(true)
+ .setCacheRegion("frontpages")
+ .list();]]></programlisting>
+
+ <para>
+ å¦ææ¥è¯¢éè¦å¼ºè¡å·æ°å
¶æ¥è¯¢ç¼ååºåï¼é£ä¹ä½ åºè¯¥è°ç¨<literal>Query.setCacheMode(CacheMode.REFRESH)</literal>æ¹æ³ã
+ è¿å¯¹å¨å
¶ä»è¿ç¨ä¸ä¿®æ¹åºå±æ°æ®ï¼ä¾å¦ï¼ä¸éè¿Hibernateä¿®æ¹æ°æ®ï¼ï¼æ对é£äºéè¦éæ©æ§æ´æ°ç¹å®æ¥è¯¢ç»æéçæ
åµç¹å«æç¨ã
+ è¿æ¯å¯¹<literal>SessionFactory.evictQueries()</literal>çæ´ä¸ºææçæ¿ä»£æ¹æ¡ï¼åæ ·å¯ä»¥æ¸
é¤æ¥è¯¢ç¼ååºåã
+ </para>
+
+ </sect1>
+
+ <sect1 id="performance-collections">
+ <title>
+ ç解éåæ§è½ï¼Understanding Collection performanceï¼
+ </title>
+
+ <para>
+ åé¢æ们已ç»å¯¹éåè¿è¡äºè¶³å¤ç讨论ãæ¬æ®µä¸ï¼æ们å°çé讲述éåå¨è¿è¡æ¶çäºå®ã
+ </para>
+
+ <sect2 id="performance-collections-taxonomy">
+ <title>
+
+ åç±»ï¼Taxonomyï¼
+ </title>
+
+ <para>
+
+ Hibernateå®ä¹äºä¸ç§åºæ¬ç±»åçéåï¼
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+
+ å¼æ°æ®éå
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+
+ ä¸å¯¹å¤å
³è
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+
+ å¤å¯¹å¤å
³è
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ è¿ä¸ªåç±»æ¯åºåäºä¸åç表åå¤é®å
³ç³»ç±»åï¼ä½æ¯å®æ²¡æåè¯æ们å
³ç³»æ¨¡åçææå
容ã
+ è¦å®å
¨ç解ä»ä»¬çå
³ç³»ç»æåæ§è½ç¹ç¹ï¼æ们å¿
é¡»åæ¶èèâç¨äºHibernateæ´æ°æå é¤éåè¡æ°æ®ç主é®çç»æâã
+ å æ¤å¾å°äºå¦ä¸çåç±»ï¼
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+
+ æåºéåç±»
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+
+ éåï¼setsï¼
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+
+ å
ï¼bags)
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+
+ ææçæåºéåç±»ï¼maps, lists, arrays)é½æ¥æä¸ä¸ªç±<literal><key></literal>å
+ <literal><index></literal>ç»æç主é®ã
+ è¿ç§æ
åµä¸éåç±»çæ´æ°æ¯é常é«æçââ主é®å·²ç»è¢«ææçç´¢å¼ï¼å æ¤å½Hibernateè¯å¾æ´æ°æå é¤ä¸è¡æ¶ï¼å¯ä»¥è¿
éæ¾å°è¯¥è¡æ°æ®ã
+ </para>
+
+ <para>
+
+ éå(sets)ç主é®ç±<literal><key></literal>åå
¶ä»å
ç´ å段ææã
+ 对äºæäºå
ç´ ç±»åæ¥è¯´ï¼è¿å¾ä½æï¼ç¹å«æ¯ç»åå
ç´ æè
大ææ¬ã大äºè¿å¶å段ï¼
+ æ°æ®åºå¯è½æ æ³ææç对å¤æç主é®è¿è¡ç´¢å¼ã
+ å¦ä¸æ¹é¢ï¼å¯¹äºä¸å¯¹å¤ãå¤å¯¹å¤å
³èï¼ç¹å«æ¯åæçæ è¯ç¬¦æ¥è¯´ï¼éåä¹å¯ä»¥è¾¾å°åæ ·çé«ææ§è½ãï¼
+ é注ï¼å¦æä½ å¸æ<literal>SchemaExport</literal>ä¸ºä½ ç<literal><set></literal>å建主é®ï¼
+ ä½ å¿
é¡»æææçå段é½å£°æ为<literal>not-null="true"</literal>ãï¼
+ </para>
+
+ <para>
+
+ <literal><idbag></literal>æ å°å®ä¹äºä»£çé®ï¼å æ¤å®æ»æ¯å¯ä»¥å¾é«æç被æ´æ°ãäºå®ä¸ï¼
+ <literal><idbag></literal>æ¥æçæ好çæ§è½è¡¨ç°ã
+ </para>
+
+ <para>
+ Bagæ¯æå·®çãå 为bagå
许éå¤çå
ç´ å¼ï¼ä¹æ²¡æç´¢å¼å段ï¼å æ¤ä¸å¯è½å®ä¹ä¸»é®ã
+ Hibernateæ æ³å¤æåºéå¤çè¡ãå½è¿ç§éå被æ´æ¹æ¶ï¼Hibernateå°ä¼å
å®æ´å°ç§»é¤
+ ï¼éè¿ä¸ä¸ª(in a single <literal>DELETE</literal>)ï¼æ´ä¸ªéåï¼ç¶ååéæ°å建æ´ä¸ªéåã
+ å æ¤Bagæ¯é常ä½æçã
+ </para>
+
+ <para>
+ 请注æï¼å¯¹äºä¸å¯¹å¤å
³èæ¥è¯´ï¼â主é®âå¾å¯è½å¹¶ä¸æ¯æ°æ®åºè¡¨çç©ç主é®ã
+ ä½å°±ç®å¨æ¤æ
åµä¸ï¼ä¸é¢çåç±»ä»ç¶æ¯æç¨çãï¼å®ä»ç¶åæ äºHibernateå¨éåçåæ°æ®è¡ä¸æ¯å¦ä½è¿è¡âå®ä½âçãï¼
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-collections-mostefficientupdate">
+ <title>
+ Lists, maps åsetsç¨äºæ´æ°æçæé«
+ </title>
+
+ <para>
+
+ æ ¹æ®æ们ä¸é¢ç讨论ï¼æ¾ç¶æåºéåç±»åå大å¤æ°seté½å¯ä»¥å¨å¢å ãå é¤ãä¿®æ¹å
ç´ ä¸æ¥ææ好çæ§è½ã
+ </para>
+
+ <para>
+ å¯è®ºè¯çæ¯å¯¹äºå¤å¯¹å¤å
³èãå¼æ°æ®éåèè¨ï¼æåºéåç±»æ¯éå(set)æä¸ä¸ªå¥½å¤ãå 为<literal>Set</literal>çå
å¨ç»æï¼
+ å¦æâæ¹åâäºä¸ä¸ªå
ç´ ï¼Hibernate并ä¸ä¼<literal>æ´æ°ï¼UPDATEï¼</literal>è¿ä¸è¡ã
+ 对äº<literal>Set</literal>æ¥è¯´ï¼åªæå¨<literal>æå
¥ï¼INSERTï¼</literal>å<literal>å é¤ï¼DELETEï¼</literal>
+ æä½æ¶âæ¹åâæææãå次强è°ï¼è¿æ®µè®¨è®ºå¯¹âä¸å¯¹å¤å
³èâ并ä¸éç¨ã
+ </para>
+
+ <para>
+ 注æå°æ°ç»æ æ³å»¶è¿è½½å
¥ï¼æ们å¯ä»¥å¾åºç»è®ºï¼list, mapåidbagsæ¯æé«æçï¼éååï¼éåç±»åï¼setåç´§éå
¶åã
+ å¨Hibernateä¸ï¼setåºè¯¥æ¶æéç¨çéåç±»åï¼è¿æ¶å 为âsetâçè¯ä¹å¨å
³ç³»æ¨¡åä¸æ¯æèªç¶çã
+ </para>
+
+ <para>
+ ä½æ¯ï¼å¨è®¾è®¡è¯å¥½çHibernateé¢å模åä¸ï¼æ们é常å¯ä»¥çå°æ´å¤çéåäºå®ä¸æ¯å¸¦æ<literal>inverse="true"</literal>
+ çä¸å¯¹å¤çå
³èã对äºè¿äºå
³èï¼æ´æ°æä½å°ä¼å¨å¤å¯¹ä¸çè¿ä¸ç«¯è¿è¡å¤çãå æ¤å¯¹äºæ¤ç±»æ
åµï¼æ éèèå
¶éåçæ´æ°æ§è½ã
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-collections-mostefficentinverse">
+ <title>
+ Bagålistæ¯ååéåç±»ä¸æçæé«ç
+ </title>
+
+ <para>
+ å¨æbagæè¿æ°´æ²ä¹åï¼ä½ å¿
é¡»äºè§£ï¼å¨ä¸ç§æ
åµä¸ï¼bagçæ§è½(å
æ¬list)è¦æ¯seté«å¾å¤ï¼
+ 对äºææäº<literal>inverse="true"</literal>çéåç±»ï¼æ¯å¦è¯´ï¼æ åçååçä¸å¯¹å¤å
³èï¼ï¼
+ æ们å¯ä»¥å¨æªåå§å(fetch)å
å
ç´ çæ
åµä¸ç´æ¥åbagælistæ·»å æ°å
ç´ ï¼
+ è¿æ¯å 为<literal>Collection.add()</literal>)æè
<literal>Collection.addAll()</literal> æ¹æ³
+ 对bagæè
Listæ»æ¯è¿åtrueï¼è¿ç¹ä¸ä¸Setä¸åï¼ãå æ¤å¯¹äºä¸é¢çç¸å代ç æ¥è¯´ï¼é度ä¼å¿«å¾å¤ã
+ </para>
+
+ <programlisting><![CDATA[Parent p = (Parent) sess.load(Parent.class, id);
+ Child c = new Child();
+ c.setParent(p);
+ p.getChildren().add(c); //no need to fetch the collection!
+ sess.flush();]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="performance-collections-oneshotdelete">
+ <title>
+ ä¸æ¬¡æ§å é¤ï¼One shot deleteï¼
+ </title>
+
+ <para>
+ å¶å°çï¼é个å é¤éåç±»ä¸çå
ç´ æ¯ç¸å½ä½æçãHibernate并没é£ä¹ç¬¨ï¼
+ å¦æä½ æ³è¦ææ´ä¸ªéåé½å é¤ï¼æ¯å¦è¯´è°ç¨list.clear()ï¼ï¼Hibernateåªéè¦ä¸ä¸ªDELETEå°±æå®äºã
+ </para>
+
+ <para>
+ å设æ们å¨ä¸ä¸ªé¿åº¦ä¸º20çéåç±»ä¸æ°å¢å äºä¸ä¸ªå
ç´ ï¼ç¶ååå é¤ä¸¤ä¸ªã
+ Hibernateä¼å®æä¸æ¡<literal>INSERT</literal>è¯å¥å两æ¡<literal>DELETE</literal>è¯å¥ï¼é¤ééåç±»æ¯ä¸ä¸ªbag)ã
+ è¿å½ç¶æ¯æ¾èæè§çã
+ </para>
+
+ <para>
+ ä½æ¯ï¼å设æ们å é¤äº18个æ°æ®ï¼åªå©ä¸2个ï¼ç¶åæ°å¢3个ãåæ两ç§å¤çæ¹å¼ï¼
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ éä¸çå é¤è¿18个æ°æ®ï¼åæ°å¢ä¸ä¸ªï¼
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ å é¤æ´ä¸ªéåç±»ï¼åªç¨ä¸å¥DELETEè¯å¥ï¼ï¼ç¶åå¢å 5个æ°æ®ã
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Hibernateè¿æ²¡é£ä¹èªæï¼ç¥é第äºç§éæ©å¯è½ä¼æ¯è¾å¿«ã
+ ï¼ä¹è®¸è®©Hibernateä¸è¿ä¹èªæä¹æ¯å¥½äºï¼å¦åå¯è½ä¼å¼åæå¤çâæ°æ®åºè§¦åå¨âä¹ç±»çé®é¢ãï¼
+ </para>
+
+ <para>
+ 幸è¿çæ¯ï¼ä½ å¯ä»¥å¼ºå¶ä½¿ç¨ç¬¬äºç§çç¥ãä½ éè¦åæ¶åæ¥çæ´ä¸ªéåç±»ï¼è§£é¤å
¶å¼ç¨ï¼ï¼
+ ç¶ååè¿åä¸ä¸ªæ°çå®ä¾åçéåç±»ï¼åªå
å«éè¦çå
ç´ ãæäºæ¶åè¿æ¯é常æç¨çã
+ </para>
+
+ <para>
+ æ¾ç¶ï¼ä¸æ¬¡æ§å é¤å¹¶ä¸éç¨äºè¢«æ å°ä¸º<literal>inverse="true"</literal>çéåã
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="performance-monitoring" revision="1">
+ <title>
+ çæµæ§è½ï¼Monitoring performanceï¼
+ </title>
+
+ <para>
+ 没æçæµåæ§è½åæ°èè¿è¡ä¼åæ¯æ¯«æ æä¹çãHibernate为å
¶å
é¨æä½æä¾äºä¸ç³»åç示æå¾ï¼å æ¤å¯ä»¥ä»
+ æ¯ä¸ª<literal>SessionFactory</literal>æåå
¶ç»è®¡æ°æ®ã
+ </para>
+
+ <sect2 id="performance-monitoring-sf" revision="2">
+ <title>
+ çæµSessionFactory
+ </title>
+
+ <para>
+ ä½ å¯ä»¥æ两ç§æ¹å¼è®¿é®<literal>SessionFactory</literal>çæ°æ®è®°å½ï¼ç¬¬ä¸ç§å°±æ¯èªå·±ç´æ¥è°ç¨
+ <literal>sessionFactory.getStatistics()</literal>æ¹æ³è¯»åãæ¾ç¤º<literal>ç»è®¡</literal>æ°æ®ã
+ </para>
+
+ <para>
+ æ¤å¤ï¼å¦æä½ æå¼<literal>StatisticsService</literal> MBeané项ï¼é£ä¹Hibernateåå¯ä»¥ä½¿ç¨JMXææ¯
+ åå¸å
¶æ°æ®è®°å½ãä½ å¯ä»¥è®©åºç¨ä¸ææç<literal>SessionFactory</literal>åæ¶å
±äº«ä¸ä¸ªMBeanï¼ä¹å¯ä»¥æ¯ä¸ª
+ SessionFactoryåé
ä¸ä¸ªMBeanãä¸é¢ç代ç å³æ¯å
¶æ¼ç¤ºä»£ç ï¼
+ </para>
+
+ <programlisting><![CDATA[// MBean service registration for a specific SessionFactory
+Hashtable tb = new Hashtable();
+tb.put("type", "statistics");
+tb.put("sessionFactory", "myFinancialApp");
+ObjectName on = new ObjectName("hibernate", tb); // MBean object name
+
+StatisticsService stats = new StatisticsService(); // MBean implementation
+stats.setSessionFactory(sessionFactory); // Bind the stats to a SessionFactory
+server.registerMBean(stats, on); // Register the Mbean on the server]]></programlisting>
+
+
+<programlisting><![CDATA[// MBean service registration for all SessionFactory's
+Hashtable tb = new Hashtable();
+tb.put("type", "statistics");
+tb.put("sessionFactory", "all");
+ObjectName on = new ObjectName("hibernate", tb); // MBean object name
+
+StatisticsService stats = new StatisticsService(); // MBean implementation
+server.registerMBean(stats, on); // Register the MBean on the server]]></programlisting>
+
+ <para>
+
+ TODOï¼ä»éè¦è¯´æçæ¯ï¼å¨ç¬¬ä¸ä¸ªä¾åä¸ï¼æ们ç´æ¥å¾å°å使ç¨MBeanï¼èå¨ç¬¬äºä¸ªä¾åä¸ï¼å¨ä½¿ç¨MBeanä¹å
+ æ们åéè¦ç»åºSessionFactoryçJNDIåï¼ä½¿ç¨<literal>hibernateStatsBean.setSessionFactoryJNDIName("my/JNDI/Name")</literal>
+ å¾å°SessionFactoryï¼ç¶åå°MBeanä¿åäºå
¶ä¸ã
+ </para>
+ <para>
+ ä½ å¯ä»¥éè¿ä»¥ä¸æ¹æ³æå¼æå
³é<literal>SessionFactory</literal>ççæµåè½ï¼
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+
+ å¨é
ç½®æé´ï¼å°<literal>hibernate.generate_statistics</literal>设置为<literal>true</literal>æ<literal>false</literal>ï¼
+ </para>
+ </listitem>
+ </itemizedlist>
+ <itemizedlist>
+ <listitem>
+ <para>
+
+ å¨è¿è¡æé´ï¼åå¯ä»¥å¯ä»¥éè¿<literal>sf.getStatistics().setStatisticsEnabled(true)</literal>
+ æ<literal>hibernateStatsBean.setStatisticsEnabled(true)</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ä½ ä¹å¯ä»¥å¨ç¨åºä¸è°ç¨<literal>clear()</literal>æ¹æ³éç½®ç»è®¡æ°æ®ï¼è°ç¨<literal>logSummary()</literal>
+ å¨æ¥å¿ä¸è®°å½ï¼info级å«ï¼å
¶æ»ç»ã
+ </para>
+
+ </sect2>
+
+ <sect2 id="performance-monitoring-metrics" revision="1">
+ <title>
+ æ°æ®è®°å½ï¼Metricsï¼
+ </title>
+
+ <para>
+ Hibernateæä¾äºä¸ç³»åæ°æ®è®°å½ï¼å
¶è®°å½çå
容å
æ¬ä»æåºæ¬çä¿¡æ¯å°ä¸å
·ä½åºæ¯çç¹æ®ä¿¡æ¯ãææçæµéå¼é½å¯ä»¥ç±
+ <literal>Statistics</literal>æ¥å£è¿è¡è®¿é®ï¼ä¸»è¦å为ä¸ç±»ï¼
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ 使ç¨<literal>Session</literal>çæ®éæ°æ®è®°å½ï¼ä¾å¦æå¼çSessionç个æ°ãåå¾çJDBCçè¿æ¥æ°çï¼
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ å®ä½ãéåãæ¥è¯¢ãç¼åçå
容çç»ä¸æ°æ®è®°å½
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ åå
·ä½å®ä½ãéåãæ¥è¯¢ãç¼åç¸å
³ç详ç»æ°æ®è®°å½
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ä¾å¦ï¼ä½ å¯ä»¥æ£æ¥ç¼åçå½ä¸æå次æ°ï¼ç¼åçå½ä¸å¤±è´¥æ¬¡æ°ï¼å®ä½ãéååæ¥è¯¢ç使ç¨æ¦çï¼æ¥è¯¢çå¹³åæ¶é´çã请注æ
+ Javaä¸æ¶é´çè¿ä¼¼ç²¾åº¦æ¯æ¯«ç§ãHibernateçæ°æ®ç²¾åº¦åå
·ä½çJVMæå
³ï¼å¨æäºå¹³å°ä¸å
¶ç²¾åº¦çè³åªè½ç²¾ç¡®å°10ç§ã
+ </para>
+
+ <para>
+ ä½ å¯ä»¥ç´æ¥ä½¿ç¨getteræ¹æ³å¾å°å
¨å±æ°æ®è®°å½ï¼ä¾å¦ï¼åå
·ä½çå®ä½ãéåãç¼ååºæ å
³çæ°æ®ï¼ï¼ä½ ä¹å¯ä»¥å¨å
·ä½æ¥è¯¢ä¸éè¿æ è®°å®ä½åã
+ æHQLãSQLè¯å¥å¾å°æå®ä½çæ°æ®è®°å½ã请åè<literal>Statistics</literal>ã<literal>EntityStatistics</literal>ã
+ <literal>CollectionStatistics</literal>ã<literal>SecondLevelCacheStatistics</literal>ã
+ å<literal>QueryStatistics</literal>çAPIæ档以æåæ´å¤ä¿¡æ¯ãä¸é¢ç代ç åæ¯ä¸ªç®åçä¾åï¼
+ </para>
+
+ <programlisting><![CDATA[Statistics stats = HibernateUtil.sessionFactory.getStatistics();
+
+double queryCacheHitCount = stats.getQueryCacheHitCount();
+double queryCacheMissCount = stats.getQueryCacheMissCount();
+double queryCacheHitRatio =
+ queryCacheHitCount / (queryCacheHitCount + queryCacheMissCount);
+
+log.info("Query Hit ratio:" + queryCacheHitRatio);
+
+EntityStatistics entityStats =
+ stats.getEntityStatistics( Cat.class.getName() );
+long changes =
+ entityStats.getInsertCount()
+ + entityStats.getUpdateCount()
+ + entityStats.getDeleteCount();
+log.info(Cat.class.getName() + " changed " + changes + "times" );]]></programlisting>
+
+ <para>
+ å¦æä½ æ³å¾å°ææå®ä½ãéåãæ¥è¯¢åç¼ååºçæ°æ®ï¼ä½ å¯ä»¥éè¿ä»¥ä¸æ¹æ³è·å¾å®ä½ãéåãæ¥è¯¢åç¼ååºå表ï¼
+ <literal>getQueries()</literal>ã<literal>getEntityNames()</literal>ã
+ <literal>getCollectionRoleNames()</literal>å
+ <literal>getSecondLevelCacheRegionNames()</literal>ã
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+</chapter>
\ No newline at end of file
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/persistent_classes.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/persistent_classes.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/persistent_classes.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,493 @@
+<chapter id="persistent-classes" revision="2">
+ <title>æä¹
åç±»(Persistent Classes)</title>
+
+ <para>
+ å¨åºç¨ç¨åºä¸ï¼ç¨æ¥å®ç°ä¸å¡é®é¢å®ä½çï¼å¦ï¼å¨çµååå¡åºç¨ç¨åºä¸çCustomeråOrderï¼
+ 类就æ¯æä¹
åç±»ãä¸è½è®¤ä¸ºææçæä¹
åç±»çå®ä¾é½æ¯æä¹
çç¶æââä¸ä¸ªå®ä¾çç¶æä¹å¯è½
+ æ¯ç¬æ¶çæè±ç®¡çã
+ </para>
+
+
+
+ <para>
+ å¦æè¿äºæä¹
åç±»éµå¾ªä¸äºç®åçè§åï¼Hibernateè½å¤å·¥ä½å¾æ´å¥½ï¼è¿äºè§åä¹è¢«ç§°ä½
+ ç®åä¼ ç»Java对象(POJO:Plain Old Java Object)ç¼ç¨æ¨¡åãä½æ¯è¿äºè§å并ä¸æ¯å¿
éçã
+ å®é
ä¸ï¼Hibernate3对äºä½ çæä¹
åç±»å ä¹ä¸åä»»ä½è®¾æ³ãä½ å¯ä»¥ç¨å
¶ä»çæ¹æ³æ¥è¡¨è¾¾é¢å模åï¼
+ æ¯å¦ï¼ä½¿ç¨<literal>Map</literal>å®ä¾çæ åç»æã
+ </para>
+
+
+ <sect1 id="persistent-classes-pojo">
+ <title>ä¸ä¸ªç®åçPOJOä¾å</title>
+ <para>
+ 大å¤æ°Javaç¨åºéè¦ç¨ä¸ä¸ªæä¹
åç±»æ¥è¡¨ç¤ºç«ç§å¨ç©ã
+ </para>
+
+
+ <programlisting><![CDATA[package eg;
+import java.util.Set;
+import java.util.Date;
+
+public class Cat {
+ private Long id; // identifier
+
+ private Date birthdate;
+ private Color color;
+ private char sex;
+ private float weight;
+ private int litterId;
+
+ private Cat mother;
+ private Set kittens = new HashSet();
+
+ private void setId(Long id) {
+ this.id=id;
+ }
+ public Long getId() {
+ return id;
+ }
+
+ void setBirthdate(Date date) {
+ birthdate = date;
+ }
+ public Date getBirthdate() {
+ return birthdate;
+ }
+
+ void setWeight(float weight) {
+ this.weight = weight;
+ }
+ public float getWeight() {
+ return weight;
+ }
+
+ public Color getColor() {
+ return color;
+ }
+ void setColor(Color color) {
+ this.color = color;
+ }
+
+ void setSex(char sex) {
+ this.sex=sex;
+ }
+ public char getSex() {
+ return sex;
+ }
+
+ void setLitterId(int id) {
+ this.litterId = id;
+ }
+ public int getLitterId() {
+ return litterId;
+ }
+
+ void setMother(Cat mother) {
+ this.mother = mother;
+ }
+ public Cat getMother() {
+ return mother;
+ }
+ void setKittens(Set kittens) {
+ this.kittens = kittens;
+ }
+ public Set getKittens() {
+ return kittens;
+ }
+
+ // addKitten not needed by Hibernate
+ public void addKitten(Cat kitten) {
+ kitten.setMother(this);
+ kitten.setLitterId( kittens.size() );
+ kittens.add(kitten);
+ }
+}]]></programlisting>
+
+ <para>
+ è¿éè¦éµå¾ªåæ¡ä¸»è¦çè§åï¼
+ </para>
+
+
+ <sect2 id="persistent-classes-pojo-constructor" revision="1">
+ <title>å®ç°ä¸ä¸ªé»è®¤çï¼å³æ åæ°çï¼æé æ¹æ³ï¼constructorï¼</title>
+
+ <para>
+ <literal>Cat</literal>æä¸ä¸ªæ åæ°çæé æ¹æ³ãææçæä¹
åç±»é½å¿
é¡»æä¸ä¸ª
+ é»è®¤çæé æ¹æ³ï¼å¯ä»¥ä¸æ¯publicçï¼ï¼è¿æ ·çè¯Hibernateå°±å¯ä»¥ä½¿ç¨
+ <literal>Constructor.newInstance()</literal>æ¥å®ä¾åå®ä»¬ã
+ æ们强ç建议ï¼å¨Hibernateä¸ï¼ä¸ºäºè¿è¡æ代çççæï¼æé æ¹æ³è³å°æ¯
+ <emphasis>å
(package)</emphasis>å
å¯è§çã
+ </para>
+
+ </sect2>
+
+ <sect2 id="persistent-classes-pojo-identifier" revision="2">
+ <title>æä¾ä¸ä¸ªæ è¯å±æ§ï¼identifier propertyï¼ï¼å¯éï¼ </title>
+
+ <para>
+ <literal>Cat</literal>æä¸ä¸ªå±æ§å«å<literal>id</literal>ãè¿ä¸ªå±æ§æ å°æ°æ®åºè¡¨ç主
+ é®å段ãè¿ä¸ªå±æ§å¯ä»¥å«ä»»ä½ååï¼å
¶ç±»åå¯ä»¥æ¯ä»»ä½çåå§ç±»åãåå§ç±»åçå
è£
ç±»åã
+ <literal>java.lang.String</literal> æè
æ¯ <literal>java.util.Date</literal>ã
+ ï¼å¦æä½ çéçæ°æ®åºè¡¨æèå主é®ï¼ä½ çè³å¯ä»¥ç¨ä¸ä¸ªç¨æ·èªå®ä¹çç±»ï¼è¯¥ç±»æ¥æè¿äºç±»å
+ çå±æ§ãåè§åé¢çå
³äºèåæ è¯ç¬¦çç« èãï¼
+ </para>
+
+
+ <para>
+ æ è¯ç¬¦å±æ§æ¯å¯éçãå¯ä»¥ä¸ç¨ç®¡å®ï¼è®©Hibernateå
é¨æ¥è¿½è¸ªå¯¹è±¡çè¯å«ã
+ ä½æ¯æ们并ä¸æ¨èè¿æ ·åã
+ </para>
+
+ <para>
+ å®é
ä¸ï¼ä¸äºåè½åªå¯¹é£äºå£°æäºæ è¯ç¬¦å±æ§ç类起ä½ç¨ï¼
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+
+ <para>
+ æ管对象çä¼ ææ§åè¿æ¥ï¼çº§èæ´æ°æ级èå并ï¼
+ ââåé
<xref linkend="objectstate-transitive"/>
+ </para>
+
+ </listitem>
+ <listitem>
+ <para>
+ <literal>Session.saveOrUpdate()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>Session.merge()</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+
+ <para>
+ æä»¬å»ºè®®ä½ å¯¹æä¹
å类声æå½åä¸è´çæ è¯å±æ§ãæ们è¿å»ºè®®ä½ 使ç¨ä¸
+ 个å¯ä»¥ä¸ºç©ºï¼ä¹å°±æ¯è¯´ï¼ä¸æ¯åå§ç±»åï¼çç±»åã
+ </para>
+
+ </sect2>
+
+ <sect2 id="persistent-classes-pojo-final">
+ <title>使ç¨éfinalçç±» (å¯é)</title>
+ <para>
+ <emphasis>代çï¼proxiesï¼</emphasis>æ¯Hibernateçä¸ä¸ªéè¦çåè½ï¼å®ä¾èµçæ¡ä»¶æ¯ï¼æä¹
+ åç±»æè
æ¯éfinalçï¼æè
æ¯å®ç°äºä¸ä¸ªæææ¹æ³é½å£°æ为publicçæ¥å£ã
+ </para>
+
+
+ <para>
+ ä½ å¯ä»¥ç¨Hibernateæä¹
åä¸ä¸ªæ²¡æå®ç°ä»»ä½æ¥å£ç<literal>final</literal>ç±»ï¼ä½æ¯ä½
+ ä¸è½ä½¿ç¨ä»£çæ¥å»¶è¿å
³èå è½½ï¼è¿ä¼éå¶ä½ è¿è¡æ§è½ä¼åçéæ©ã
+ </para>
+
+
+ <para>
+ ä½ ä¹åºè¯¥é¿å
å¨éfinalç±»ä¸å£°æ <literal>public final</literal>çæ¹æ³ãå¦æä½ æ³ä½¿ç¨ä¸
+ 个æ<literal>public final</literal>æ¹æ³çç±»ï¼ä½ å¿
é¡»éè¿è®¾ç½®<literal>lazy="false"</literal>
+ æ¥æç¡®å°ç¦ç¨ä»£çã
+ </para>
+ </sect2>
+
+ <sect2 id="persistent-classes-pojo-accessors" revision="2">
+ <title>为æä¹
åå段声æ访é®å¨(accessors)åæ¯å¦å¯åçæ å¿(mutators)(å¯é)</title>
+
+ <para>
+ <literal>Cat</literal>为å®çæææä¹
åå段声æäºè®¿é®æ¹æ³ãå¾å¤å
¶ä»ORMå·¥å
·ç´æ¥å¯¹
+ å®ä¾åéè¿è¡æä¹
åãæ们ç¸ä¿¡ï¼å¨å
³ç³»æ°æ®åºschemaåç±»çå
é¨æ°æ®ç»æä¹é´å¼å
¥é´æ¥å±(åæ为"éç´æ¥"ï¼indirection)ä¼å¥½ä¸äºãé»è®¤æ
åµä¸Hibernateæä¹
åJavaBeansé£æ ¼çå±æ§ï¼è®¤å¯
+ <literal>getFoo</literal>ï¼<literal>isFoo</literal> å <literal>setFoo</literal>è¿ç§å½¢å¼çæ¹æ³åã
+ å¦æéè¦ï¼ä½ å¯ä»¥å¯¹æäºç¹å®å±æ§å®è¡ç´æ¥å段访é®ã
+ </para>
+
+
+ <para>
+ å±æ§<emphasis>ä¸éè¦</emphasis>è¦å£°æ为publicçãHibernateå¯ä»¥æä¹
åä¸ä¸ªæ
+ <literal>default</literal>ã<literal>protected</literal>æ<literal>private</literal>çget/setæ¹æ³å¯¹
+ çå±æ§è¿è¡æä¹
åã
+ </para>
+ </sect2>
+ </sect1>
+
+ <sect1 id="persistent-classes-inheritance">
+ <title>å®ç°ç»§æ¿ï¼Inheritanceï¼</title>
+
+ <para>
+ åç±»ä¹å¿
é¡»éµå®ç¬¬ä¸æ¡å第äºæ¡è§åãå®ä»è¶
ç±»<literal>Cat</literal>继æ¿äºæ è¯å±æ§ã
+ </para>
+
+ <programlisting><![CDATA[package eg;
+
+public class DomesticCat extends Cat {
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+ protected void setName(String name) {
+ this.name=name;
+ }
+}]]></programlisting>
+ </sect1>
+
+ <sect1 id="persistent-classes-equalshashcode" revision="1">
+ <title>å®ç°<literal>equals()</literal>å<literal>hashCode()</literal></title>
+
+ <para>
+ å¦æä½ æå¦ä¸éæ±ï¼ä½ å¿
é¡»éè½½
+ <literal>equals()</literal> å <literal>hashCode()</literal>æ¹æ³ï¼
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ æ³ææä¹
ç±»çå®ä¾æ¾å
¥<literal>Set</literal>ä¸ï¼å½è¡¨ç¤ºå¤å¼å
³èæ¶ï¼æ¨èè¿ä¹åï¼
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ æ³éç¨è±ç®¡å®ä¾
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Hibernateä¿è¯ï¼ä»
å¨ç¹å®ä¼è¯èå´å
ï¼æä¹
åæ è¯ï¼æ°æ®åºçè¡ï¼åJavaæ è¯æ¯çä»·çãå æ¤ï¼ä¸æ¦
+ æ们混åäºä»ä¸åä¼è¯ä¸è·åçå®ä¾ï¼å¦æå¸æ<literal>Set</literal>ææç¡®çè¯ä¹ï¼å°±å¿
+ é¡»å®ç°<literal>equals()</literal> å<literal>hashCode()</literal>ã
+ </para>
+
+ <para>
+ å®ç°<literal>equals()</literal>/<literal>hashCode()</literal>ææ¾èæè§çæ¹æ³æ¯æ¯è¾ä¸¤ä¸ªå¯¹è±¡
+ æ è¯ç¬¦çå¼ãå¦æå¼ç¸åï¼å两个对象对åºäºæ°æ®åºçåä¸è¡ï¼å æ¤å®ä»¬æ¯ç¸ççï¼å¦æé½è¢«æ·»å å°
+ <literal>Set</literal>ï¼åå¨<literal>Set</literal>ä¸åªæä¸ä¸ªå
ç´ ï¼ãä¸å¹¸çæ¯ï¼å¯¹çæçæ è¯ä¸è½
+ 使ç¨è¿ç§æ¹æ³ãHibernateä»
对é£äºæä¹
å对象èµæ è¯å¼ï¼ä¸ä¸ªæ°å建çå®ä¾å°ä¸ä¼æä»»ä½æ è¯å¼ãæ¤å¤ï¼
+ å¦æä¸ä¸ªå®ä¾æ²¡æ被ä¿å(unsaved)ï¼å¹¶ä¸å®å½åæ£å¨ä¸ä¸ª<literal>Set</literal>ä¸ï¼ä¿åå®å°ä¼ç»è¿ä¸ªå¯¹è±¡
+ èµä¸ä¸ªæ è¯å¼ãå¦æ<literal>equals()</literal> å <literal>hashCode()</literal>æ¯åºäºæ è¯å¼
+ å®ç°çï¼åå
¶åå¸ç å°ä¼æ¹åï¼è¿è¿åäº<literal>Set</literal>çå¥çº¦ã建议å»Hibernateçç«ç¹é
读å
³äºè¿ä¸ª
+ é®é¢çå
¨é¨è®¨è®ºã注æï¼è¿ä¸æ¯Hibernateçé®é¢ï¼èæ¯ä¸è¬çJava对象æ è¯åJava对象çä»·çè¯ä¹é®é¢ã
+ </para>
+ <para>
+ æ们建议使ç¨<emphasis>ä¸å¡é®å¼ç¸ç(Business key equality)</emphasis>æ¥å®ç°<literal>equals()</literal>
+ å <literal>hashCode()</literal>ãä¸å¡é®å¼ç¸ççæææ¯ï¼<literal>equals()</literal>æ¹æ³
+ ä»
ä»
æ¯è¾å½¢æä¸å¡é®çå±æ§ï¼å®è½å¨ç°å®ä¸çéæ è¯æ们çå®ä¾ï¼æ¯ä¸ä¸ª<emphasis>èªç¶ç</emphasis>åéç ï¼ã
+ </para>
+ <programlisting><![CDATA[public class Cat {
+
+ ...
+ public boolean equals(Object other) {
+ if (this == other) return true;
+ if ( !(other instanceof Cat) ) return false;
+
+ final Cat cat = (Cat) other;
+
+ if ( !cat.getLitterId().equals( getLitterId() ) ) return false;
+ if ( !cat.getMother().equals( getMother() ) ) return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ int result;
+ result = getMother().hashCode();
+ result = 29 * result + getLitterId();
+ return result;
+ }
+
+}]]></programlisting>
+
+ <para>
+ 注æï¼ä¸å¡é®ä¸å¿
åæ°æ®åºç主é®é£æ ·åºå®ä¸åï¼åè§<xref linkend="transactions-basics-identity"/>ï¼ã
+ 对ä¸å¡é®èè¨ï¼ä¸å¯åæå¯ä¸çå±æ§æ¯ä¸éçéæ©ã
+ </para>
+
+ </sect1>
+
+ <sect1 id="persistent-classes-dynamicmodels">
+ <title>å¨æ模å(Dynamic models)</title>
+ <para>
+ <emphasis>注æï¼ä»¥ä¸ç¹æ§å¨å½åå¤äºè¯éªé¶æ®µï¼å°æ¥å¯è½ä¼æååã</emphasis>
+ </para>
+
+ <para>
+ è¿è¡æçæä¹
åå®ä½æ²¡æå¿
è¦ä¸å®è¡¨ç¤ºä¸ºåPOJOç±»æJavaBean对象é£æ ·çå½¢å¼ãHibernateä¹æ¯æå¨æ模å
+ ï¼å¨è¿è¡æ使ç¨<literal>Map</literal>ç<literal>Map</literal>ï¼å象DOM4Jçæ 模åé£
+ æ ·çå®ä½è¡¨ç¤ºã使ç¨è¿ç§æ¹æ³ï¼ä½ ä¸ç¨åæä¹
åç±»ï¼åªåæ å°æ件就è¡äºã
+ </para>
+
+
+ <para>
+ Hibernateé»è®¤å·¥ä½å¨æ®éPOJO模å¼ãä½ å¯ä»¥ä½¿ç¨é
ç½®é项<literal>default_entity_mode</literal>ï¼
+ 对ç¹å®ç<literal>SessionFactory</literal>ï¼è®¾ç½®ä¸ä¸ªé»è®¤çå®ä½è¡¨ç¤ºæ¨¡å¼ã
+ ï¼åè§<xref linkend="configuration-optional-properties"/>ãï¼
+ </para>
+
+ <para>
+ ä¸é¢æ¯ç¨<literal>Map</literal>æ¥è¡¨ç¤ºçä¾åãé¦å
ï¼å¨æ å°æ件ä¸ï¼è¦å£°æ
+ <literal>entity-name</literal>æ¥ä»£æ¿ä¸ä¸ªç±»åï¼æä½ä¸ºä¸ç§éå±ï¼ã
+ </para>
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class entity-name="Customer">
+
+ <id name="id"
+ type="long"
+ column="ID">
+ <generator class="sequence"/>
+ </id>
+
+ <property name="name"
+ column="NAME"
+ type="string"/>
+
+ <property name="address"
+ column="ADDRESS"
+ type="string"/>
+
+ <many-to-one name="organization"
+ column="ORGANIZATION_ID"
+ class="Organization"/>
+
+ <bag name="orders"
+ inverse="true"
+ lazy="false"
+ cascade="all">
+ <key column="CUSTOMER_ID"/>
+ <one-to-many class="Order"/>
+ </bag>
+
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ 注æï¼è½ç¶æ¯ç¨ç®æ ç±»åæ¥å£°æå
³èçï¼ä½æ¯å
³èçç®æ ç±»åé¤äºæ¯POJOä¹å¤ï¼ä¹å¯ä»¥
+ æ¯ä¸ä¸ªå¨æçå®ä½ã
+ </para>
+
+ <para>
+ å¨ä½¿ç¨<literal>dynamic-map</literal>为<literal>SessionFactory</literal>
+ 设置äºé»è®¤çå®ä½æ¨¡å¼ä¹åï¼å¯ä»¥å¨è¿è¡æ使ç¨<literal>Map</literal>ç
+ <literal>Map</literal>ã
+ </para>
+
+ <programlisting><![CDATA[Session s = openSession();
+Transaction tx = s.beginTransaction();
+Session s = openSession();
+
+// Create a customer
+Map david = new HashMap();
+david.put("name", "David");
+
+// Create an organization
+Map foobar = new HashMap();
+foobar.put("name", "Foobar Inc.");
+
+// Link both
+david.put("organization", foobar);
+
+// Save both
+s.save("Customer", david);
+s.save("Organization", foobar);
+
+tx.commit();
+s.close();]]></programlisting>
+
+ <para>
+ å¨ææ å°ç好å¤æ¯ï¼ååæéè¦çæ¶é´å°äºï¼å 为ååä¸éè¦å®ç°å®ä½ç±»ãç¶èï¼ä½ æ æ³è¿è¡
+ ç¼è¯æçç±»åæ£æ¥ï¼å¹¶å¯è½ç±æ¤ä¼å¤çå¾å¤çè¿è¡æå¼å¸¸ã幸äºæäºHibernateæ å°ï¼å®ä½¿å¾æ°
+ æ®åºçschemaè½å®¹æçè§æ ¼åååçåï¼å¹¶å
许ç¨åå¨æ¤ä¹ä¸æ·»å åéçé¢å模åå®ç°ã
+ </para>
+
+
+ <para>
+ å®ä½è¡¨ç¤ºæ¨¡å¼ä¹è½å¨æ¯ä¸ª<literal>Session</literal>çåºç¡ä¸è®¾ç½®ï¼
+ </para>
+ <programlisting><![CDATA[Session dynamicSession = pojoSession.getSession(EntityMode.MAP);
+
+// Create a customer
+Map david = new HashMap();
+david.put("name", "David");
+dynamicSession.save("Customer", david);
+...
+dynamicSession.flush();
+dynamicSession.close()
+...
+// Continue on pojoSession
+]]></programlisting>
+
+
+
+ <para>
+ 请注æï¼ç¨<literal>EntityMode</literal>è°ç¨<literal>getSession()</literal>æ¯å¨
+ <literal>Session</literal>çAPIä¸ï¼èä¸æ¯<literal>SessionFactory</literal>ã
+ è¿æ ·ï¼æ°ç<literal>Session</literal>å
±äº«åºå±çJDBCè¿æ¥ï¼äºå¡ï¼åå
¶ä»çä¸ä¸æä¿¡
+ æ¯ãè¿æå³çï¼ä½ ä¸éè¦å¨ç¬¬äºä¸ª<literal>Session</literal>ä¸è°ç¨
+ <literal>flush()</literal>å<literal>close()</literal>ï¼åæ ·çï¼æäºå¡åè¿æ¥çå¤ç
+ 交ç»åæ¥çå·¥ä½åå
ã
+ </para>
+
+
+ <para>
+ å
³äºXML表示è½åçæ´å¤ä¿¡æ¯å¯ä»¥å¨<xref linkend="xml"/>ä¸æ¾å°ã
+ </para>
+ </sect1>
+
+ <sect1 id="persistent-classes-tuplizers" revision="1">
+ <title>å
ç»çææ å°(Tuplizers)</title>
+
+ <para>
+ <literal>org.hibernate.tuple.Tuplizer</literal>ï¼ä»¥åå
¶åæ¥å£ï¼è´è´£æ ¹æ®ç»å®ç<literal>org.hibernate.EntityMode</literal>ï¼æ¥å¤ç°çææ°æ®ãå¦æç»å®ççææ°æ®è¢«è®¤ä¸ºå
¶æ¯ä¸ç§æ°æ®ç»æï¼"tuplizer"å°±æ¯ä¸ä¸ªç¥éå¦ä½å建è¿æ ·çæ°æ®ç»æï¼ä»¥åå¦ä½ç»è¿ä¸ªæ°æ®ç»æèµå¼çä¸è¥¿ãæ¯å¦è¯´ï¼å¯¹äºPOJOè¿ç§Entity Modeï¼å¯¹åºçtuplizerç¥ééè¿å
¶æé æ¹æ³æ¥å建ä¸ä¸ªPOJOï¼åéè¿å
¶å±æ§è®¿é®å¨æ¥è®¿é®POJOå±æ§ãæ两大类é«å±Tuplizerï¼åå«æ¯<literal>org.hibernate.tuple.entity.EntityTuplizer</literal> å<literal>org.hibernate.tuple.entity.ComponentTuplizer</literal>æ¥å£ã<literal>EntityTuplizer</literal>è´è´£ç®¡çä¸é¢æå°çå®ä½çå¥çº¦ï¼è<literal>ComponentTuplizer</literal>åæ¯é对ç»ä»¶çã
+ </para>
+
+ <para>
+ ç¨æ·ä¹å¯ä»¥æå
¥å
¶èªå®ä¹çtuplizerãæ许æ¨éè¦ä¸ç§ä¸åäºdynamic-map entity-modeä¸ä½¿ç¨ç<literal>java.util.HashMap</literal>ç<literal>java.util.Map</literal>å®ç°ï¼æ许æ¨éè¦ä¸é»è®¤çç¥ä¸åç代ççæçç¥(proxy generation strategy)ãéè¿èªå®ä¹tuplizerå®ç°ï¼è¿ä¸¤ä¸ªç®æ æ¨é½å¯ä»¥è¾¾å°ãTuplizerå®ä¹è¢«éå å°å®ä»¬ææ管ççentityæè
componentæ å°ä¸ãåå°æ们çcustomer entityä¾åï¼
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+ <class entity-name="Customer">
+ <!--
+ Override the dynamic-map entity-mode
+ tuplizer for the customer entity
+ -->
+ <tuplizer entity-mode="dynamic-map"
+ class="CustomMapTuplizerImpl"/>
+
+ <id name="id" type="long" column="ID">
+ <generator class="sequence"/>
+ </id>
+
+ <!-- other properties -->
+ ...
+ </class>
+</hibernate-mapping>
+
+
+public class CustomMapTuplizerImpl
+ extends org.hibernate.tuple.entity.DynamicMapEntityTuplizer {
+ // override the buildInstantiator() method to plug in our custom map...
+ protected final Instantiator buildInstantiator(
+ org.hibernate.mapping.PersistentClass mappingInfo) {
+ return new CustomMapInstantiator( mappingInfo );
+ }
+
+ private static final class CustomMapInstantiator
+ extends org.hibernate.tuple.DynamicMapInstantitor {
+ // override the generateMap() method to return our custom map...
+ protected final Map generateMap() {
+ return new CustomMap();
+ }
+ }
+}]]></programlisting>
+
+
+ </sect1>
+
+ <para>
+ TODOï¼propertyåproxyå
éçç¨æ·æ©å±æ¡æ¶ææ¡£ã
+ </para>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/query_criteria.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/query_criteria.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/query_criteria.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,424 @@
+<chapter id="querycriteria">
+ <title>
+ æ¡ä»¶æ¥è¯¢(Criteria Queries)
+ </title>
+ <para>
+ å
·æä¸ä¸ªç´è§çãå¯æ©å±çæ¡ä»¶æ¥è¯¢APIæ¯Hibernateçç¹è²ã
+ </para>
+
+ <sect1 id="querycriteria-creating">
+ <title>å建ä¸ä¸ª<literal>Criteria</literal> å®ä¾</title>
+
+ <para>
+ <literal>org.hibernate.Criteria</literal>æ¥å£è¡¨ç¤ºç¹å®æä¹
ç±»çä¸ä¸ªæ¥è¯¢ã<literal>Session</literal>æ¯
+ <literal>Criteria</literal>å®ä¾çå·¥åã
+ </para>
+
+ <programlisting><![CDATA[Criteria crit = sess.createCriteria(Cat.class);
+crit.setMaxResults(50);
+List cats = crit.list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-narrowing">
+ <title>éå¶ç»æéå
容</title>
+
+ <para>
+ ä¸ä¸ªåç¬çæ¥è¯¢æ¡ä»¶æ¯<literal>org.hibernate.criterion.Criterion</literal>
+ æ¥å£çä¸ä¸ªå®ä¾ã<literal>org.hibernate.criterion.Restrictions</literal>ç±»
+ å®ä¹äºè·å¾æäºå
ç½®<literal>Criterion</literal>ç±»åçå·¥åæ¹æ³ã
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "Fritz%") )
+ .add( Restrictions.between("weight", minWeight, maxWeight) )
+ .list();]]></programlisting>
+
+ <para>
+ 约æå¯ä»¥æé»è¾åç»ã
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "Fritz%") )
+ .add( Restrictions.or(
+ Restrictions.eq( "age", new Integer(0) ),
+ Restrictions.isNull("age")
+ ) )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.in( "name", new String[] { "Fritz", "Izi", "Pk" } ) )
+ .add( Restrictions.disjunction()
+ .add( Restrictions.isNull("age") )
+ .add( Restrictions.eq("age", new Integer(0) ) )
+ .add( Restrictions.eq("age", new Integer(1) ) )
+ .add( Restrictions.eq("age", new Integer(2) ) )
+ ) )
+ .list();]]></programlisting>
+
+ <para>
+ Hibernateæä¾äºç¸å½å¤çå
ç½®criterionç±»å(<literal>Restrictions</literal>
+ åç±»), ä½æ¯å°¤å
¶æç¨çæ¯å¯ä»¥å
è®¸ä½ ç´æ¥ä½¿ç¨SQLã
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.sqlRestriction("lower({alias}.name) like lower(?)", "Fritz%", Hibernate.STRING) )
+ .list();]]></programlisting>
+
+ <para>
+ <literal>{alias}</literal>å ä½ç¬¦åºå½è¢«æ¿æ¢ä¸ºè¢«æ¥è¯¢å®ä½çåå«åã
+ </para>
+
+ <para>
+
+ <literal>Property</literal>å®ä¾æ¯è·å¾ä¸ä¸ªæ¡ä»¶çå¦å¤ä¸ç§éå¾ãä½ å¯ä»¥éè¿è°ç¨<literal>Property.forName()</literal>
+ å建ä¸ä¸ª<literal>Property</literal>ã
+
+ </para>
+
+ <programlisting><![CDATA[
+Property age = Property.forName("age");
+List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.disjunction()
+ .add( age.isNull() )
+ .add( age.eq( new Integer(0) ) )
+ .add( age.eq( new Integer(1) ) )
+ .add( age.eq( new Integer(2) ) )
+ ) )
+ .add( Property.forName("name").in( new String[] { "Fritz", "Izi", "Pk" } ) )
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-ordering">
+ <title>ç»æéæåº</title>
+
+ <para>
+ ä½ å¯ä»¥ä½¿ç¨<literal>org.hibernate.criterion.Order</literal>æ¥ä¸ºæ¥è¯¢ç»ææåºã
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "F%")
+ .addOrder( Order.asc("name") )
+ .addOrder( Order.desc("age") )
+ .setMaxResults(50)
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Property.forName("name").like("F%") )
+ .addOrder( Property.forName("name").asc() )
+ .addOrder( Property.forName("age").desc() )
+ .setMaxResults(50)
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-associations" revision="2">
+ <title>å
³è</title>
+
+ <para>
+ ä½ å¯ä»¥ä½¿ç¨<literal>createCriteria()</literal>é常容æçå¨äºç¸å
³èçå®ä½é´å»ºç«
+ 约æã
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "F%") )
+ .createCriteria("kittens")
+ .add( Restrictions.like("name", "F%") )
+ .list();]]></programlisting>
+
+ <para>
+ 注æ第äºä¸ª <literal>createCriteria()</literal>è¿åä¸ä¸ªæ°ç
+ <literal>Criteria</literal>å®ä¾ï¼è¯¥å®ä¾å¼ç¨<literal>kittens</literal> éåä¸çå
ç´ ã
+ </para>
+
+ <para>
+ æ¥ä¸æ¥ï¼æ¿æ¢å½¢æå¨æäºæ
åµä¸ä¹æ¯å¾æç¨çã
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .createAlias("kittens", "kt")
+ .createAlias("mate", "mt")
+ .add( Restrictions.eqProperty("kt.name", "mt.name") )
+ .list();]]></programlisting>
+
+ <para>
+
+ (<literal>createAlias()</literal>并ä¸å建ä¸ä¸ªæ°ç
+ <literal>Criteria</literal>å®ä¾ã)
+ </para>
+
+ <para>
+ <literal>Cat</literal>å®ä¾æä¿åçä¹å两次æ¥è¯¢æè¿åçkittenséåæ¯
+ <emphasis>没æ</emphasis>被æ¡ä»¶é¢è¿æ»¤çãå¦æä½ å¸æåªè·å¾ç¬¦åæ¡ä»¶çkittensï¼
+ ä½ å¿
须使ç¨<literal>ResultTransformer</literal>ã
+
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .createCriteria("kittens", "kt")
+ .add( Restrictions.eq("name", "F%") )
+ .setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP)
+ .list();
+Iterator iter = cats.iterator();
+while ( iter.hasNext() ) {
+ Map map = (Map) iter.next();
+ Cat cat = (Cat) map.get(Criteria.ROOT_ALIAS);
+ Cat kitten = (Cat) map.get("kt");
+}]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-dynamicfetching" revision="1">
+ <title>å¨æå
³èæå</title>
+
+ <para>
+ ä½ å¯ä»¥ä½¿ç¨<literal>setFetchMode()</literal>å¨è¿è¡æ¶å®ä¹å¨æå
³èæåçè¯ä¹ã
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
+ .add( Restrictions.like("name", "Fritz%") )
+ .setFetchMode("mate", FetchMode.EAGER)
+ .setFetchMode("kittens", FetchMode.EAGER)
+ .list();]]></programlisting>
+
+ <para>
+ è¿ä¸ªæ¥è¯¢å¯ä»¥éè¿å¤è¿æ¥æå<literal>mate</literal>å<literal>kittens</literal>ã
+ æ¥ç<xref linkend="performance-fetching"/>å¯ä»¥è·å¾æ´å¤ä¿¡æ¯ã
+ </para>
+
+ </sect1>
+
+ <sect1 id="querycriteria-examples">
+ <title>æ¥è¯¢ç¤ºä¾</title>
+
+ <para>
+ <literal>org.hibernate.criterion.Example</literal>ç±»å
è®¸ä½ éè¿ä¸ä¸ªç»å®å®ä¾
+ æ建ä¸ä¸ªæ¡ä»¶æ¥è¯¢ã
+ </para>
+
+ <programlisting><![CDATA[Cat cat = new Cat();
+cat.setSex('F');
+cat.setColor(Color.BLACK);
+List results = session.createCriteria(Cat.class)
+ .add( Example.create(cat) )
+ .list();]]></programlisting>
+
+ <para>
+ çæ¬å±æ§ãæ è¯ç¬¦åå
³è被忽ç¥ãé»è®¤æ
åµä¸å¼ä¸ºnullçå±æ§å°è¢«æé¤ã
+ </para>
+
+ <para>
+ ä½ å¯ä»¥èªè¡è°æ´<literal>Example</literal>使ä¹æ´å®ç¨ã
+ </para>
+
+ <programlisting><![CDATA[Example example = Example.create(cat)
+ .excludeZeroes() //exclude zero valued properties
+ .excludeProperty("color") //exclude the property named "color"
+ .ignoreCase() //perform case insensitive string comparisons
+ .enableLike(); //use like for string comparisons
+List results = session.createCriteria(Cat.class)
+ .add(example)
+ .list();]]></programlisting>
+
+ <para>
+ ä½ çè³å¯ä»¥ä½¿ç¨exampleså¨å
³è对象ä¸æ¾ç½®æ¡ä»¶ã
+ </para>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .add( Example.create(cat) )
+ .createCriteria("mate")
+ .add( Example.create( cat.getMate() ) )
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-projection">
+ <title>æå½±(Projections)ãèåï¼aggregationï¼ååç»ï¼groupingï¼</title>
+ <para>
+ <literal>org.hibernate.criterion.Projections</literal>æ¯
+ <literal>Projection</literal> çå®ä¾å·¥åãæ们éè¿è°ç¨
+ <literal>setProjection()</literal>åºç¨æå½±å°ä¸ä¸ªæ¥è¯¢ã
+ </para>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.rowCount() )
+ .add( Restrictions.eq("color", Color.BLACK) )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.projectionList()
+ .add( Projections.rowCount() )
+ .add( Projections.avg("weight") )
+ .add( Projections.max("weight") )
+ .add( Projections.groupProperty("color") )
+ )
+ .list();]]></programlisting>
+
+ <para>
+ å¨ä¸ä¸ªæ¡ä»¶æ¥è¯¢ä¸æ²¡æå¿
è¦æ¾å¼çä½¿ç¨ "group by" ãæäºæ影类åå°±æ¯è¢«å®ä¹ä¸º<emphasis>
+ åç»æå½±</emphasis>ï¼ä»ä»¬ä¹åºç°å¨SQLç<literal>group by</literal>åå¥ä¸ã
+ </para>
+
+ <para>
+ ä½ å¯ä»¥éæ©æä¸ä¸ªå«åææ´¾ç»ä¸ä¸ªæå½±ï¼è¿æ ·å¯ä»¥ä½¿æå½±å¼è¢«çº¦æææåºæå¼ç¨ãä¸é¢æ¯ä¸¤ç§ä¸åçå®ç°æ¹å¼ï¼
+ </para>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.alias( Projections.groupProperty("color"), "colr" ) )
+ .addOrder( Order.asc("colr") )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.groupProperty("color").as("colr") )
+ .addOrder( Order.asc("colr") )
+ .list();]]></programlisting>
+
+ <para>
+ <literal>alias()</literal>å<literal>as()</literal>æ¹æ³ç®ä¾¿çå°ä¸ä¸ªæå½±å®ä¾å
è£
å°å¦å¤ä¸ä¸ª
+ å«åç<literal>Projection</literal>å®ä¾ä¸ãç®èè¨ä¹ï¼å½ä½ æ·»å ä¸ä¸ªæå½±å°ä¸ä¸ªæå½±å表ä¸æ¶
+ ä½ å¯ä»¥ä¸ºå®æå®ä¸ä¸ªå«åï¼
+ </para>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.projectionList()
+ .add( Projections.rowCount(), "catCountByColor" )
+ .add( Projections.avg("weight"), "avgWeight" )
+ .add( Projections.max("weight"), "maxWeight" )
+ .add( Projections.groupProperty("color"), "color" )
+ )
+ .addOrder( Order.desc("catCountByColor") )
+ .addOrder( Order.desc("avgWeight") )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Domestic.class, "cat")
+ .createAlias("kittens", "kit")
+ .setProjection( Projections.projectionList()
+ .add( Projections.property("cat.name"), "catName" )
+ .add( Projections.property("kit.name"), "kitName" )
+ )
+ .addOrder( Order.asc("catName") )
+ .addOrder( Order.asc("kitName") )
+ .list();]]></programlisting>
+
+ <para>
+ ä½ ä¹å¯ä»¥ä½¿ç¨<literal>Property.forName()</literal>æ¥è¡¨ç¤ºæå½±ï¼
+ </para>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Property.forName("name") )
+ .add( Property.forName("color").eq(Color.BLACK) )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[List results = session.createCriteria(Cat.class)
+ .setProjection( Projections.projectionList()
+ .add( Projections.rowCount().as("catCountByColor") )
+ .add( Property.forName("weight").avg().as("avgWeight") )
+ .add( Property.forName("weight").max().as("maxWeight") )
+ .add( Property.forName("color").group().as("color" )
+ )
+ .addOrder( Order.desc("catCountByColor") )
+ .addOrder( Order.desc("avgWeight") )
+ .list();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="querycriteria-detachedqueries">
+ <title>离线(detached)æ¥è¯¢ååæ¥è¯¢</title>
+ <para>
+ <literal>DetachedCriteria</literal>ç±»ä½¿ä½ å¨ä¸ä¸ªsessionèå´ä¹å¤å建ä¸ä¸ªæ¥è¯¢ï¼å¹¶ä¸å¯ä»¥ä½¿ç¨ä»»æç
+ <literal>Session</literal>æ¥æ§è¡å®ã
+ </para>
+
+ <programlisting><![CDATA[DetachedCriteria query = DetachedCriteria.forClass(Cat.class)
+ .add( Property.forName("sex").eq('F') );
+
+Session session = ....;
+Transaction txn = session.beginTransaction();
+List results = query.getExecutableCriteria(session).setMaxResults(100).list();
+txn.commit();
+session.close();]]></programlisting>
+
+ <para>
+ <literal>DetachedCriteria</literal>ä¹å¯ä»¥ç¨ä»¥è¡¨ç¤ºåæ¥è¯¢ãæ¡ä»¶å®ä¾å
å«åæ¥è¯¢å¯ä»¥éè¿
+ <literal>Subqueries</literal>æè
<literal>Property</literal>è·å¾ã
+ </para>
+
+ <programlisting><![CDATA[DetachedCriteria avgWeight = DetachedCriteria.forClass(Cat.class)
+ .setProjection( Property.forName("weight").avg() );
+session.createCriteria(Cat.class)
+ .add( Property.forName("weight).gt(avgWeight) )
+ .list();]]></programlisting>
+
+ <programlisting><![CDATA[DetachedCriteria weights = DetachedCriteria.forClass(Cat.class)
+ .setProjection( Property.forName("weight") );
+session.createCriteria(Cat.class)
+ .add( Subqueries.geAll("weight", weights) )
+ .list();]]></programlisting>
+
+ <para>
+ çè³ç¸äºå
³èçåæ¥è¯¢ä¹æ¯æå¯è½çï¼
+ </para>
+
+ <programlisting><![CDATA[DetachedCriteria avgWeightForSex = DetachedCriteria.forClass(Cat.class, "cat2")
+ .setProjection( Property.forName("weight").avg() )
+ .add( Property.forName("cat2.sex").eqProperty("cat.sex") );
+session.createCriteria(Cat.class, "cat")
+ .add( Property.forName("weight).gt(avgWeightForSex) )
+ .list();]]></programlisting>
+
+ </sect1>
+
+
+ <!--TODO: ResultSetTransformer + aliasing. AliasToBeanTransformer allow returning arbitrary
+ user objects - similar to setResultClass in JDO2. General use of ResultTransformer
+ could also be explained. -->
+
+ <sect1 id="query-criteria-naturalid">
+ <title>æ ¹æ®èªç¶æ è¯æ¥è¯¢(Queries by natural identifier)</title>
+
+
+ <para>
+ 对大å¤æ°æ¥è¯¢ï¼å
æ¬æ¡ä»¶æ¥è¯¢èè¨ï¼å 为æ¥è¯¢ç¼åç失æ(invalidation)åçå¾å¤ªé¢ç¹ï¼æ¥è¯¢ç¼åä¸æ¯é常é«æãç¶èï¼æä¸ç§ç¹å«çæ¥è¯¢ï¼å¯ä»¥éè¿ä¸åçèªç¶é®ä¼åç¼åç失æç®æ³ãå¨æäºåºç¨ä¸ï¼è¿ç§ç±»åçæ¥è¯¢æ¯è¾å¸¸è§ãæ¡ä»¶æ¥è¯¢API对è¿ç§ç¨ä¾æä¾äºç¹å«è§çº¦ã
+ </para>
+
+ <para>
+ é¦å
ï¼ä½ åºè¯¥å¯¹ä½ çentity使ç¨<literal><natural-id></literal>æ¥æ å°èªç¶é®ï¼ç¶åæå¼ç¬¬äºçº§ç¼åã
+ </para>
+
+ <programlisting><![CDATA[<class name="User">
+ <cache usage="read-write"/>
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <natural-id>
+ <property name="name"/>
+ <property name="org"/>
+ </natural-id>
+ <property name="password"/>
+</class>]]></programlisting>
+
+ <para>
+ 注æ,æ¤åè½å¯¹å
·æ<emphasis>mutable</emphasis>èªç¶é®çentity并ä¸éç¨ã
+ </para>
+
+ <para>
+ ç¶åï¼æå¼Hibernate æ¥è¯¢ç¼åã
+ </para>
+
+ <para>
+ ç°å¨ï¼æ们å¯ä»¥ç¨<literal>Restrictions.naturalId()</literal>æ¥ä½¿ç¨æ´å é«æçç¼åç®æ³ã
+ </para>
+
+ <programlisting><![CDATA[session.createCriteria(User.class)
+ .add( Restrictions.naturalId()
+ .set("name", "gavin")
+ .set("org", "hb")
+ ).setCacheable(true)
+ .uniqueResult();]]></programlisting>
+
+ </sect1>
+
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/query_hql.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/query_hql.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/query_hql.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1088 @@
+<chapter id="queryhql">
+ <title>HQL: Hibernateæ¥è¯¢è¯è¨</title>
+ <para>
+ Hibernateé
å¤äºä¸ç§é常强大çæ¥è¯¢è¯è¨ï¼è¿ç§è¯è¨çä¸å»å¾åSQLãä½æ¯ä¸è¦è¢«è¯æ³ç»æ
+ ä¸çç¸ä¼¼æè¿·æï¼HQLæ¯é常ææè¯ç被设计为å®å
¨é¢å对象çæ¥è¯¢ï¼å®å¯ä»¥ç解å¦ç»§æ¿ãå¤æ
+ åå
³èä¹ç±»çæ¦å¿µã
+ </para>
+
+ <sect1 id="queryhql-casesensitivity">
+ <title>大å°åæææ§é®é¢</title>
+
+ <para>
+ é¤äºJavaç±»ä¸å±æ§çå称å¤ï¼æ¥è¯¢è¯å¥å¯¹å¤§å°å并ä¸ææã
+ æ以 <literal>SeLeCT</literal> ä¸
+ <literal>sELEct</literal> 以å
+ <literal>SELECT</literal> æ¯ç¸åçï¼ä½æ¯
+ <literal>org.hibernate.eg.FOO</literal> 并ä¸çä»·äº
+ <literal>org.hibernate.eg.Foo</literal> 并ä¸
+ <literal>foo.barSet</literal> ä¹ä¸çä»·äº
+ <literal>foo.BARSET</literal>ã
+ </para>
+
+ <para>
+ æ¬æåä¸çHQLå
³é®åå°ä½¿ç¨å°ååæ¯. å¾å¤ç¨æ·åç°ä½¿ç¨å®å
¨å¤§åçå
³é®åä¼ä½¿æ¥è¯¢è¯å¥
+ çå¯è¯»æ§æ´å¼º, ä½æ们åç°ï¼å½ææ¥è¯¢è¯å¥åµå
¥å°Javaè¯å¥ä¸çæ¶å使ç¨å¤§åå
³é®åæ¯è¾é¾çã
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-from">
+ <title>fromåå¥</title>
+
+ <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>å«å</emphasis>, åå æ¯ä½ å¯è½éè¦
+ å¨æ¥è¯¢è¯å¥çå
¶å®é¨åå¼ç¨å°<literal>Cat</literal>
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat]]></programlisting>
+
+ <para>
+ è¿ä¸ªè¯å¥æå«å<literal>cat</literal>æå®ç»ç±»<literal>Cat</literal>
+ çå®ä¾, è¿æ ·æ们就å¯ä»¥å¨éåçæ¥è¯¢ä¸ä½¿ç¨æ¤å«åäºã å
³é®å<literal>as</literal>
+ æ¯å¯éçï¼æ们ä¹å¯ä»¥è¿æ ·å:
+ </para>
+
+ <programlisting><![CDATA[from Cat cat]]></programlisting>
+
+ <para>
+ åå¥ä¸å¯ä»¥åæ¶åºç°å¤ä¸ªç±», å
¶æ¥è¯¢ç»ææ¯äº§çä¸ä¸ªç¬å¡å¿ç§¯æ产ç跨表çè¿æ¥ã
+ </para>
+
+ <programlisting><![CDATA[from Formula, Parameter]]></programlisting>
+ <programlisting><![CDATA[from Formula as form, Parameter as param]]></programlisting>
+
+ <para>
+ æ¥è¯¢è¯å¥ä¸å«åçå¼å¤´é¨åå°å被认为æ¯å®è·µä¸çå¥½ä¹ æ¯ï¼
+ è¿æ ·åä¸Javaåéçå½åæ åä¿æäºä¸è´
+ (æ¯å¦ï¼<literal>domesticCat</literal>)ã
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-joins" revision="2">
+ <title>å
³è(Association)ä¸è¿æ¥(Join)</title>
+
+ <para>
+ æ们ä¹å¯ä»¥ä¸ºç¸å
³èçå®ä½çè³æ¯å¯¹ä¸ä¸ªéåä¸çå
¨é¨å
ç´ æå®ä¸ä¸ªå«å, è¿æ¶è¦ä½¿ç¨å
³é®å<literal>join</literal>ã
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+ inner join cat.mate as mate
+ left outer join cat.kittens as kitten]]></programlisting>
+
+ <programlisting><![CDATA[from Cat as cat left join cat.mate.kittens as kittens]]></programlisting>
+
+ <programlisting><![CDATA[from Formula form full join form.parameter param]]></programlisting>
+
+ <para>
+ åæ¯æçè¿æ¥ç±»åæ¯ä»ANSI SQLä¸åé´æ¥çã
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>inner join</literal>ï¼å
è¿æ¥ï¼
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>left outer join</literal>ï¼å·¦å¤è¿æ¥ï¼
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>right outer join</literal>ï¼å³å¤è¿æ¥ï¼
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>full join</literal> (å
¨è¿æ¥ï¼å¹¶ä¸å¸¸ç¨)
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ è¯å¥<literal>inner join</literal>, <literal>left outer join</literal> 以å
+ <literal>right outer join</literal> å¯ä»¥ç®åã
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+ join cat.mate as mate
+ left join cat.kittens as kitten]]></programlisting>
+
+ <para>
+ éè¿HQLç<literal>with</literal>å
³é®åï¼ä½ å¯ä»¥æä¾é¢å¤çjoinæ¡ä»¶ã
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+ left join cat.kittens as kitten
+ with kitten.bodyWeight > 10.0]]></programlisting>
+
+ <para>
+ è¿æï¼ä¸ä¸ª"fetch"è¿æ¥å
许ä»
ä»
使ç¨ä¸ä¸ªéæ©è¯å¥å°±å°ç¸å
³èç对象æä¸ç»å¼çéåéçä»ä»¬çç¶å¯¹è±¡çåå§åè被åå§åï¼è¿ç§æ¹æ³å¨ä½¿ç¨å°éåçæ
åµä¸å°¤å
¶æç¨ï¼å¯¹äºå
³èåéåæ¥è¯´ï¼å®ææç代æ¿äºæ å°æ件ä¸çå¤èæ¥
+ ä¸å»¶è¿å£°æï¼lazy declarationsï¼. æ¥ç
+ <xref linkend="performance-fetching"/> 以è·å¾çå¤çä¿¡æ¯ã
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+ inner join fetch cat.mate
+ left join fetch cat.kittens]]></programlisting>
+
+ <para>
+ ä¸ä¸ªfetchè¿æ¥é常ä¸éè¦è¢«æå®å«å, å 为ç¸å
³èç对象ä¸åºå½è¢«ç¨å¨
+ <literal>where</literal> åå¥ (æå
¶å®ä»»ä½åå¥)ä¸ãåæ¶ï¼ç¸å
³èç对象
+ 并ä¸å¨æ¥è¯¢çç»æä¸ç´æ¥è¿åï¼ä½å¯ä»¥éè¿ä»ä»¬çç¶å¯¹è±¡æ¥è®¿é®å°ä»ä»¬ã
+ </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>iterate()</literal>æ¥è°ç¨æ¥è¯¢ï¼è¯·æ³¨æ<literal>fetch</literal>æé æ¯ä¸è½ä½¿ç¨ç(<literal>scroll()</literal> å¯ä»¥ä½¿ç¨)ã<literal>fetch</literal>ä¹ä¸åºè¯¥ä¸<literal>setMaxResults()</literal> æ<literal>setFirstResult()</literal>å
±ç¨ï¼è¿æ¯å 为è¿äºæä½æ¯åºäºç»æéçï¼èå¨é¢å
æåéåç±»æ¶å¯è½å
å«éå¤çæ°æ®ï¼ä¹å°±æ¯è¯´æ æ³é¢å
ç¥é精确çè¡æ°ã<literal>fetch</literal>è¿ä¸è½ä¸ç¬ç«ç <literal>with</literal>æ¡ä»¶ä¸èµ·ä½¿ç¨ãéè¿å¨ä¸æ¬¡æ¥è¯¢ä¸fetchå¤ä¸ªéåï¼å¯ä»¥å¶é åºç¬å¡å°ç§¯ï¼å æ¤è¯·å¤å 注æã对bagæ å°æ¥è¯´ï¼åæ¶join fetchå¤ä¸ªéåè§è²å¯è½å¨æäºæ
åµä¸ç»åºå¹¶éé¢æçç»æï¼ä¹è¯·å°å¿ãæå注æï¼ä½¿ç¨<literal>full join fetch</literal> ä¸ <literal>right join fetch</literal>æ¯æ²¡ææä¹çã
+ </para>
+
+ <para>
+ å¦æä½ ä½¿ç¨å±æ§çº§å«ç延è¿è·åï¼lazy fetchingï¼ï¼è¿æ¯éè¿éæ°ç¼ååèç å®ç°çï¼ï¼å¯ä»¥ä½¿ç¨ <literal>fetch
+all properties</literal>
+ æ¥å¼ºå¶Hibernateç«å³åå¾é£äºåæ¬éè¦å»¶è¿å è½½çå±æ§ï¼å¨ç¬¬ä¸ä¸ªæ¥è¯¢ä¸ï¼ã
+
+ </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>
+
+ </sect1>
+
+ <sect1 id="queryhql-joins-forms">
+ <title>join è¯æ³çå½¢å¼</title>
+
+ <para>
+ HQLæ¯æ两ç§å
³èjoinçå½¢å¼ï¼<literal>implicit(éå¼)</literal> ä¸<literal>explicitï¼æ¾å¼ï¼</literal>ã
+ </para>
+
+ <para>
+ ä¸ä¸èä¸ç»åºçæ¥è¯¢é½æ¯ä½¿ç¨<literal>explicit(æ¾å¼)</literal>å½¢å¼çï¼å
¶ä¸formåå¥ä¸æç¡®ç»åºäºjoinå
³é®åãè¿æ¯å»ºè®®ä½¿ç¨çæ¹å¼ã
+ </para>
+
+ <para>
+ <literal>implicitï¼éå¼ï¼</literal>å½¢å¼ä¸ä½¿ç¨joinå
³é®åãå
³è使ç¨"ç¹å·"æ¥è¿è¡âå¼ç¨âã<literal>implicit</literal> joinå¯ä»¥å¨ä»»ä½HQLåå¥ä¸åºç°.<literal>implicit</literal> joinå¨æç»çSQLè¯å¥ä¸ä»¥inner joinçæ¹å¼åºç°ã
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat where cat.mate.name like '%s%']]></programlisting>
+ </sect1>
+
+ <sect1 id="queryhql-select">
+ <title>selectåå¥</title>
+
+ <para>
+ <literal>select</literal> åå¥éæ©å°åªäºå¯¹è±¡ä¸å±æ§è¿
+ åå°æ¥è¯¢ç»æéä¸. èèå¦ä¸æ
åµ:
+ </para>
+
+ <programlisting><![CDATA[select mate
+from Cat as cat
+ inner join cat.mate as mate]]></programlisting>
+
+ <para>
+ 该è¯å¥å°éæ©<literal>mate</literal>s of other <literal>Cat</literal>sãï¼å
¶ä»ç«çé
å¶ï¼
+ å®é
ä¸, ä½ å¯ä»¥æ´ç®æ´çç¨ä»¥ä¸çæ¥è¯¢è¯å¥è¡¨è¾¾ç¸åçå«ä¹:
+ </para>
+
+ <programlisting><![CDATA[select cat.mate from Cat cat]]></programlisting>
+
+ <para>
+ æ¥è¯¢è¯å¥å¯ä»¥è¿åå¼ä¸ºä»»ä½ç±»åçå±æ§ï¼å
æ¬è¿åç±»å为æç§ç»ä»¶(Component)çå±æ§:
+ </para>
+
+ <programlisting><![CDATA[select cat.name from DomesticCat cat
+where cat.name like 'fri%']]></programlisting>
+
+ <programlisting><![CDATA[select cust.name.firstName from Customer as cust]]></programlisting>
+
+ <para>
+ æ¥è¯¢è¯å¥å¯ä»¥è¿åå¤ä¸ªå¯¹è±¡åï¼æï¼å±æ§ï¼åæ¾å¨
+ <literal>Object[]</literal>éåä¸,
+ </para>
+
+ <programlisting><![CDATA[select mother, offspr, mate.name
+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>
+ ä¹å¯è½ç´æ¥è¿åä¸ä¸ªå®é
çç±»åå®å
¨çJava对象,
+ </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>
+ å设类<literal>Family</literal>æä¸ä¸ªåéçæé å½æ°.
+ </para>
+
+ <para>
+ ä½ å¯ä»¥ä½¿ç¨å
³é®å<literal>as</literal>ç»â被éæ©äºç表达å¼âææ´¾å«å:
+ </para>
+
+ <programlisting><![CDATA[select max(bodyWeight) as max, min(bodyWeight) as min, count(*) as n
+from Cat cat]]></programlisting>
+
+ <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>
+ 该æ¥è¯¢è¿åäºä¸ä¸ª<literal>Map</literal>ç对象ï¼å
容æ¯å«åä¸è¢«éæ©çå¼ç»æçå-å¼æ å°ã
+ </para>
+
+ </sect1>
+
+ <sect1 id="queryhql-aggregation">
+ <title>èéå½æ°</title>
+
+ <para>
+ HQLæ¥è¯¢çè³å¯ä»¥è¿åä½ç¨äºå±æ§ä¹ä¸çèéå½æ°ç计ç®ç»æ:
+ </para>
+
+ <programlisting><![CDATA[select avg(cat.weight), sum(cat.weight), max(cat.weight), count(cat)
+from Cat cat]]></programlisting>
+
+<!-- å·²ç»ä¸å被继ç»æ¯æ
+ <para>
+ å¨<literal>select</literal>åå¥ä¸ï¼éåï¼Collectionsï¼ä¹å¯ä»¥åºç°å¨èéå½æ°çå
é¨ã
+ </para>
+
+ <programlisting><![CDATA[select cat, count( elements(cat.kittens) )
+from Cat cat group by cat]]></programlisting>
+-->
+
+ <para>
+ åæ¯æçèéå½æ°å¦ä¸ï¼
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>avg(...), sum(...), min(...), max(...)</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>count(*)</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>count(...), count(distinct ...), count(all...)</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ä½ å¯ä»¥å¨éæ©åå¥ä¸ä½¿ç¨æ°å¦æä½ç¬¦ãè¿æ¥ä»¥åç»è¿éªè¯ç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>ä¸<literal>all</literal> ä¹å¯ä»¥ä½¿ç¨ï¼å®ä»¬å
·æä¸SQLç¸åçè¯ä¹.
+ </para>
+
+ <programlisting><![CDATA[select distinct cat.name from Cat cat
+
+select count(distinct cat.name), count(cat) from Cat cat]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="queryhql-polymorphism">
+ <title>å¤ææ¥è¯¢</title>
+
+ <para>
+ ä¸ä¸ªå¦ä¸çæ¥è¯¢è¯å¥:
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat]]></programlisting>
+
+ <para>
+ ä¸ä»
è¿å<literal>Cat</literal>ç±»çå®ä¾, ä¹åæ¶è¿ååç±»
+ <literal>DomesticCat</literal>çå®ä¾. Hibernate å¯ä»¥å¨<literal>from</literal>åå¥ä¸æå®<emphasis>ä»»ä½</emphasis>
+ Java ç±»ææ¥å£. æ¥è¯¢ä¼è¿å继æ¿äºè¯¥ç±»çæææä¹
ååç±»
+ çå®ä¾æè¿å声æäºè¯¥æ¥å£çæææä¹
åç±»çå®ä¾ãä¸é¢çæ¥è¯¢è¯å¥è¿åææç被æä¹
åç对象ï¼
+ </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">
+ <title>whereåå¥</title>
+
+ <para>
+ <literal>where</literal>åå¥å
è®¸ä½ å°è¿åçå®ä¾å表çèå´ç¼©å°.
+ å¦æ没ææå®å«åï¼ä½ å¯ä»¥ä½¿ç¨å±æ§åæ¥ç´æ¥å¼ç¨å±æ§:
+ </para>
+
+ <programlisting><![CDATA[from Cat where name='Fritz']]></programlisting>
+
+ <para>
+ å¦æææ´¾äºå«åï¼éè¦ä½¿ç¨å®æ´çå±æ§å:
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat where cat.name='Fritz']]></programlisting>
+
+ <para>
+ è¿åå为ï¼å±æ§nameçäºï¼'Fritz'ç<literal>Cat</literal>ç±»çå®ä¾ã
+ </para>
+
+ <programlisting><![CDATA[select foo
+from Foo foo, Bar bar
+where foo.startDate = bar.date]]></programlisting>
+
+ <para>
+ å°è¿åææ满足ä¸é¢æ¡ä»¶ç<literal>Foo</literal>ç±»çå®ä¾ï¼
+ åå¨å¦ä¸ç<literal>bar</literal>çä¸ä¸ªå®ä¾ï¼å
¶<literal>date</literal>å±æ§çäº
+ <literal>Foo</literal>ç<literal>startDate</literal>å±æ§ã
+ å¤åè·¯å¾è¡¨è¾¾å¼ä½¿å¾<literal>where</literal>åå¥é常ç强大ï¼èèå¦ä¸æ
åµï¼
+ </para>
+
+ <programlisting><![CDATA[from Cat cat where cat.mate.name is not null]]></programlisting>
+
+ <para>
+ 该æ¥è¯¢å°è¢«ç¿»è¯æ为ä¸ä¸ªå«æ表è¿æ¥ï¼å
è¿æ¥ï¼çSQLæ¥è¯¢ãå¦æä½ æç®ååè¿æ ·çæ¥è¯¢è¯å¥
+ </para>
+
+ <programlisting><![CDATA[from Foo foo
+where foo.bar.baz.customer.address.city is not null]]></programlisting>
+
+ <para>
+ å¨SQLä¸ï¼ä½ 为达æ¤ç®çå°éè¦è¿è¡ä¸ä¸ªå表è¿æ¥çæ¥è¯¢ã
+ </para>
+
+ <para>
+ <literal>=</literal>è¿ç®ç¬¦ä¸ä»
å¯ä»¥è¢«ç¨æ¥æ¯è¾å±æ§çå¼ï¼ä¹å¯ä»¥ç¨æ¥æ¯è¾å®ä¾ï¼
+ </para>
+
+ <programlisting><![CDATA[from Cat cat, Cat rival where cat.mate = rival.mate]]></programlisting>
+
+ <programlisting><![CDATA[select cat, mate
+from Cat cat, Cat mate
+where cat.mate = mate]]></programlisting>
+
+ <para>
+ ç¹æ®å±æ§ï¼å°åï¼<literal>id</literal>å¯ä»¥ç¨æ¥è¡¨ç¤ºä¸ä¸ªå¯¹è±¡çå¯ä¸çæ è¯ç¬¦ãï¼ä½ ä¹å¯ä»¥ä½¿ç¨è¯¥å¯¹è±¡çå±æ§åãï¼
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat where cat.id = 123
+
+from Cat as cat where cat.mate.id = 69]]></programlisting>
+
+ <para>
+ 第äºä¸ªæ¥è¯¢æ¯ææçãæ¤æ¶ä¸éè¦è¿è¡è¡¨è¿æ¥ï¼
+ </para>
+
+ <para>
+ åæ ·ä¹å¯ä»¥ä½¿ç¨å¤åæ è¯ç¬¦ãæ¯å¦<literal>Person</literal>ç±»æä¸ä¸ªå¤åæ è¯ç¬¦ï¼å®ç±<literal>country</literal>å±æ§
+ ä¸<literal>medicareNumber</literal>å±æ§ç»æã
+ </para>
+
+ <programlisting><![CDATA[from bank.Person person
+where person.id.country = 'AU'
+ and person.id.medicareNumber = 123456]]></programlisting>
+
+ <programlisting><![CDATA[from bank.Account account
+where account.owner.id.country = 'AU'
+ and account.owner.id.medicareNumber = 123456]]></programlisting>
+
+ <para>
+ 第äºä¸ªæ¥è¯¢ä¹ä¸éè¦è¿è¡è¡¨è¿æ¥ã
+ </para>
+
+ <para>
+ åæ ·çï¼ç¹æ®å±æ§<literal>class</literal>å¨è¿è¡å¤ææä¹
åçæ
åµä¸è¢«ç¨æ¥ååä¸ä¸ªå®ä¾çé´å«å¼ï¼discriminator valueï¼ã
+ ä¸ä¸ªåµå
¥å°whereåå¥ä¸çJavaç±»çååå°è¢«è½¬æ¢ä¸ºè¯¥ç±»çé´å«å¼ã
+ </para>
+
+ <programlisting><![CDATA[from Cat cat where cat.class = DomesticCat]]></programlisting>
+
+ <para>
+ ä½ ä¹å¯ä»¥å£°æä¸ä¸ªå±æ§çç±»åæ¯ç»ä»¶æè
å¤åç¨æ·ç±»åï¼ä»¥åç±ç»ä»¶ææçç»ä»¶ççï¼ãæ°¸è¿ä¸è¦å°è¯ä½¿ç¨ä»¥ç»ä»¶ç±»åæ¥ç»å°¾çè·¯å¾è¡¨è¾¾å¼ï¼path-expressionï¼
+ ï¼ä¸æ¤ç¸åï¼ä½ åºå½ä½¿ç¨ç»ä»¶çä¸ä¸ªå±æ§æ¥ç»å°¾ï¼ã
+ 举ä¾æ¥è¯´ï¼å¦æ<literal>store.owner</literal>å«æä¸ä¸ªå
å«äºç»ä»¶çå®ä½<literal>address</literal>
+ </para>
+
+ <programlisting><![CDATA[store.owner.address.city // æ£ç¡®
+store.owner.address // é误!]]></programlisting>
+
+ <para>
+ ä¸ä¸ªâä»»æâç±»åæ两个ç¹æ®çå±æ§<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">
+ <title>表达å¼</title>
+
+ <para>
+ å¨<literal>where</literal>åå¥ä¸å
许使ç¨ç表达å¼å
æ¬
+ 大å¤æ°ä½ å¯ä»¥å¨SQL使ç¨ç表达å¼ç§ç±»:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ æ°å¦è¿ç®ç¬¦<literal>+, -, *, /</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ äºè¿å¶æ¯è¾è¿ç®ç¬¦<literal>=, >=, <=, <>, !=, like</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ é»è¾è¿ç®ç¬¦<literal>and, or, not</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>in</literal>,
+ <literal>not in</literal>,
+ <literal>between</literal>,
+ <literal>is null</literal>,
+ <literal>is not null</literal>,
+ <literal>is empty</literal>,
+ <literal>is not empty</literal>,
+ <literal>member of</literal> and
+ <literal>not member of</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ "ç®åç" case, <literal>case ... when ... then ... else ... end</literal>,å
+ "æç´¢" case, <literal>case when ... then ... else ... end</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ å符串è¿æ¥ç¬¦<literal>...||...</literal> or <literal>concat(...,...)</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>current_date()</literal>, <literal>current_time()</literal>,
+ <literal>current_timestamp()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>second(...)</literal>, <literal>minute(...)</literal>,
+ <literal>hour(...)</literal>, <literal>day(...)</literal>,
+ <literal>month(...)</literal>, <literal>year(...)</literal>,
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ EJB-QL 3.0å®ä¹çä»»ä½å½æ°ææä½ï¼<literal>substring(), trim(),
+ lower(), upper(), length(), locate(), abs(), sqrt(), bit_length()ï¼ mod()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>coalesce()</literal> å <literal>nullif()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>str()</literal> ææ°åæè
æ¶é´å¼è½¬æ¢ä¸ºå¯è¯»çå符串
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ <literal>cast(... as ...)</literal>, å
¶ç¬¬äºä¸ªåæ°æ¯æHibernateç±»åçååï¼ä»¥å<literal>extract(... from ...)</literal>ï¼åªè¦ANSI
+ <literal>cast()</literal> å <literal>extract()</literal> 被åºå±æ°æ®åºæ¯æ
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ HQL <literal>index()</literal> å½æ°ï¼ä½ç¨äºjoinçæåºéåçå«åã
+
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ HQLå½æ°ï¼æéåä½ä¸ºåæ°:<literal>size(), minelement(), maxelement(), minindex(), maxindex()</literal>,è¿æç¹å«ç<literal>elements()</literal> å<literal>indices</literal>å½æ°ï¼å¯ä»¥ä¸æ°éè¯å 以éå®ï¼<literal>some, all, exists, any, in</literal>ã
+
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ä»»ä½æ°æ®åºæ¯æçSQLæ éå½æ°ï¼æ¯å¦<literal>sign()</literal>,
+ <literal>trunc()</literal>, <literal>rtrim()</literal>, <literal>sin()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ JDBCé£æ ¼çåæ°ä¼ å
¥ <literal>?</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ å½ååæ°<literal>:name</literal>, <literal>:start_date</literal>, <literal>:x1</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ SQL ç´æ¥å¸¸é <literal>'foo'</literal>, <literal>69</literal>, <literal>6.66E+2</literal>, <literal>'1970-01-01 10:00:01.0'</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Java <literal>public static final</literal> ç±»åç常é <literal>eg.Color.TABBY</literal>
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ å
³é®å<literal>in</literal>ä¸<literal>between</literal>å¯æå¦ä¸æ¹æ³ä½¿ç¨:
+ </para>
+
+ <programlisting><![CDATA[from DomesticCat cat where cat.name between 'A' and 'B']]></programlisting>
+
+ <programlisting><![CDATA[from DomesticCat cat where cat.name in ( 'Foo', 'Bar', 'Baz' )]]></programlisting>
+
+ <para>
+ èä¸å¦å®çæ ¼å¼ä¹å¯ä»¥å¦ä¸ä¹¦åï¼
+ </para>
+
+ <programlisting><![CDATA[from DomesticCat cat where cat.name not between 'A' and 'B']]></programlisting>
+
+ <programlisting><![CDATA[from DomesticCat cat where cat.name not in ( 'Foo', 'Bar', 'Baz' )]]></programlisting>
+
+ <para>
+ åæ ·, åå¥<literal>is null</literal>ä¸<literal>is not null</literal>å¯ä»¥è¢«ç¨æ¥æµè¯ç©ºå¼(null).
+ </para>
+
+ <para>
+ å¨Hibernateé
ç½®æ件ä¸å£°æHQLâæ¥è¯¢æ¿ä»£ï¼query substitutionsï¼âä¹åï¼
+ å¸å°è¡¨è¾¾å¼ï¼Booleansï¼å¯ä»¥å¨å
¶ä»è¡¨è¾¾å¼ä¸è½»æ¾ç使ç¨:
+ </para>
+
+ <programlisting><![CDATA[<property name="hibernate.query.substitutions">true 1, false 0</property>]]></programlisting>
+
+ <para>
+ ç³»ç»å°è¯¥HQL转æ¢ä¸ºSQLè¯å¥æ¶ï¼è¯¥è®¾ç½®è¡¨æå°ç¨å符 <literal>1</literal> å
+<literal>0</literal> æ¥
+ å代å
³é®å<literal>true</literal> å <literal>false</literal>:
+
+ </para>
+
+ <programlisting><![CDATA[from Cat cat where cat.alive = true]]></programlisting>
+
+ <para>
+ ä½ å¯ä»¥ç¨ç¹æ®å±æ§<literal>size</literal>, ææ¯ç¹æ®å½æ°<literal>size()</literal>æµè¯ä¸ä¸ªéåç大å°ã
+ </para>
+
+ <programlisting><![CDATA[from Cat cat where cat.kittens.size > 0]]></programlisting>
+
+ <programlisting><![CDATA[from Cat cat where size(cat.kittens) > 0]]></programlisting>
+
+ <para>
+ 对äºç´¢å¼äºï¼æåºï¼çéåï¼ä½ å¯ä»¥ä½¿ç¨<literal>minindex</literal> ä¸ <literal>maxindex</literal>å½æ°æ¥å¼ç¨å°æå°ä¸æ大çç´¢å¼åºæ°ã
+ åçï¼ä½ å¯ä»¥ä½¿ç¨<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>
+å¨ä¼ éä¸ä¸ªéåçç´¢å¼éæè
æ¯å
ç´ é(<literal>elements</literal>ä¸<literal>indices</literal> å½æ°)
+ æè
ä¼ éä¸ä¸ªåæ¥è¯¢çç»æçæ¶åï¼å¯ä»¥ä½¿ç¨SQLå½æ°<literal>any, some, all, exists, in</literal>
+
+ </para>
+
+ <programlisting><![CDATA[select mother from Cat as mother, Cat as kit
+where kit in elements(foo.kittens)]]></programlisting>
+
+ <programlisting><![CDATA[select p from NameList list, Person p
+where p.name = some elements(list.names)]]></programlisting>
+
+ <programlisting><![CDATA[from Cat cat where exists elements(cat.kittens)]]></programlisting>
+
+ <programlisting><![CDATA[from Player p where 3 > all elements(p.scores)]]></programlisting>
+
+ <programlisting><![CDATA[from Show show where 'fizard' in indices(show.acts)]]></programlisting>
+
+ <para>
+ 注æï¼å¨Hibernate3ç§ï¼è¿äºç»æåé- <literal>size</literal>, <literal>elements</literal>,
+ <literal>indices</literal>, <literal>minindex</literal>, <literal>maxindex</literal>,
+ <literal>minelement</literal>, <literal>maxelement</literal> - åªè½å¨whereåå¥ä¸ä½¿ç¨ã
+ </para>
+
+ <para>
+ ä¸ä¸ªè¢«ç´¢å¼è¿çï¼æåºçï¼éåçå
ç´ (arrays, lists, maps)å¯ä»¥å¨å
¶ä»ç´¢å¼ä¸è¢«å¼ç¨ï¼åªè½å¨whereåå¥ä¸ï¼ï¼
+ </para>
+
+ <programlisting><![CDATA[from Order order where order.items[0].id = 1234]]></programlisting>
+
+ <programlisting><![CDATA[select person from Person person, Calendar calendar
+where calendar.holidays['national day'] = person.birthDay
+ and person.nationality.calendar = calendar]]></programlisting>
+
+ <programlisting><![CDATA[select item from Item item, Order order
+where order.items[ order.deliveredItemIndices[0] ] = item and order.id = 11]]></programlisting>
+
+ <programlisting><![CDATA[select item from Item item, Order order
+where order.items[ maxindex(order.items) ] = item and order.id = 11]]></programlisting>
+
+ <para>
+ å¨<literal>[]</literal>ä¸ç表达å¼çè³å¯ä»¥æ¯ä¸ä¸ªç®æ°è¡¨è¾¾å¼ã
+ </para>
+
+ <programlisting><![CDATA[select item from Item item, Order order
+where order.items[ size(order.items) - 1 ] = item]]></programlisting>
+
+ <para>
+ 对äºä¸ä¸ªä¸å¯¹å¤çå
³èï¼one-to-many associationï¼ææ¯å¼çéåä¸çå
ç´ ï¼
+ HQLä¹æä¾å
建ç<literal>index()</literal>å½æ°ï¼
+ </para>
+
+ <programlisting><![CDATA[select item, index(item) from Order order
+ join order.items item
+where index(item) < 5]]></programlisting>
+
+ <para>
+ å¦æåºå±æ°æ®åºæ¯ææ éçSQLå½æ°ï¼å®ä»¬ä¹å¯ä»¥è¢«ä½¿ç¨
+ </para>
+
+ <programlisting><![CDATA[from DomesticCat cat where upper(cat.name) like 'FRI%']]></programlisting>
+
+ <para>
+ å¦æä½ è¿ä¸è½å¯¹ææçè¿äºæ·±ä¿¡ä¸çï¼æ³æ³ä¸é¢çæ¥è¯¢ãå¦æ使ç¨SQLï¼è¯å¥é¿åº¦ä¼å¢é¿å¤å°ï¼å¯è¯»æ§ä¼ä¸éå¤å°ï¼
+ </para>
+
+ <programlisting><![CDATA[select cust
+from Product prod,
+ Store store
+ inner join store.customers cust
+where prod.name = 'widget'
+ and store.location.name in ( 'Melbourne', 'Sydney' )
+ and prod = all elements(cust.currentOrder.lineItems)]]></programlisting>
+
+ <para>
+ <emphasis>æ示:</emphasis> ä¼åå¦ä¸çè¯å¥
+ </para>
+
+ <programlisting><![CDATA[SELECT cust.name, cust.address, cust.phone, cust.id, cust.current_order
+FROM customers cust,
+ stores store,
+ locations loc,
+ store_customers sc,
+ product prod
+WHERE prod.name = 'widget'
+ AND store.loc_id = loc.id
+ AND loc.name IN ( 'Melbourne', 'Sydney' )
+ AND sc.store_id = store.id
+ AND sc.cust_id = cust.id
+ AND prod.id = ALL(
+ SELECT item.prod_id
+ FROM line_items item, orders o
+ WHERE item.order_id = o.id
+ AND cust.current_order = o.id
+ )]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="queryhql-ordering">
+ <title>order byåå¥</title>
+
+ <para>
+ æ¥è¯¢è¿åçå表(list)å¯ä»¥æç
§ä¸ä¸ªè¿åçç±»æç»ä»¶ï¼components)ä¸çä»»ä½å±æ§ï¼propertyï¼è¿è¡æåºï¼
+ </para>
+
+ <programlisting><![CDATA[from DomesticCat cat
+order by cat.name asc, cat.weight desc, cat.birthdate]]></programlisting>
+
+ <para>
+ å¯éç<literal>asc</literal>æ<literal>desc</literal>å
³é®åææäºæç
§ååºæéåºè¿è¡æåº.
+ </para>
+ </sect1>
+
+ <sect1 id="queryhql-grouping" revision="1">
+ <title>group byåå¥</title>
+
+ <para>
+ ä¸ä¸ªè¿åèéå¼(aggregate values)çæ¥è¯¢å¯ä»¥æç
§ä¸ä¸ªè¿åçç±»æç»ä»¶ï¼components)ä¸çä»»ä½å±æ§ï¼propertyï¼è¿è¡åç»ï¼
+ </para>
+
+ <programlisting><![CDATA[select cat.color, sum(cat.weight), count(cat)
+from Cat cat
+group by cat.color]]></programlisting>
+
+ <programlisting><![CDATA[select foo.id, avg(name), max(name)
+from Foo foo join foo.names name
+group by foo.id]]></programlisting>
+
+ <para>
+ <literal>having</literal>åå¥å¨è¿éä¹å
许使ç¨.
+ </para>
+
+ <programlisting><![CDATA[select cat.color, sum(cat.weight), count(cat)
+from Cat cat
+group by cat.color
+having cat.color in (eg.Color.TABBY, eg.Color.BLACK)]]></programlisting>
+
+ <para>
+ å¦æåºå±çæ°æ®åºæ¯æçè¯(ä¾å¦ä¸è½å¨MySQLä¸ä½¿ç¨)ï¼SQLçä¸è¬å½æ°ä¸èéå½æ°ä¹å¯ä»¥åºç°
+ å¨<literal>having</literal>ä¸<literal>order by</literal> åå¥ä¸ã
+ </para>
+
+ <programlisting><![CDATA[select cat
+from Cat cat
+ join cat.kittens kitten
+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>åå¥ä¸é½ä¸è½å
å«ç®æ¯è¡¨è¾¾å¼ï¼arithmetic expressionsï¼.
+
+ ä¹è¦æ³¨æHibernateç®åä¸ä¼æ©å±groupçå®ä½,å æ¤ä½ ä¸è½å<literal>group by cat</literal>,é¤é<literal>cat</literal>çææå±æ§é½ä¸æ¯èéç(non-aggregated)ãä½ å¿
é¡»æç¡®çååºææçéèéå±æ§ã
+ </para>
+
+ </sect1>
+
+
+ <sect1 id="queryhql-subqueries" revision="2">
+ <title>åæ¥è¯¢</title>
+
+ <para>
+ 对äºæ¯æåæ¥è¯¢çæ°æ®åºï¼Hibernateæ¯æå¨æ¥è¯¢ä¸ä½¿ç¨åæ¥è¯¢ãä¸ä¸ªåæ¥è¯¢å¿
须被åæ¬å·å
å´èµ·æ¥ï¼ç»å¸¸æ¯SQLèéå½æ°çåæ¬å·ï¼ã
+ çè³ç¸äºå
³èçåæ¥è¯¢ï¼å¼ç¨å°å¤é¨æ¥è¯¢ä¸çå«åçåæ¥è¯¢ï¼ä¹æ¯å
许çã
+ </para>
+
+ <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>
+
+ <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>
+
+ <programlisting><![CDATA[select cat.id, (select max(kit.weight) from cat.kitten kit)
+from Cat as cat]]></programlisting>
+
+ <para>
+ 注æï¼HQLèªæ¥è¯¢åªå¯ä»¥å¨selectæè
whereåå¥ä¸åºç°ã
+ </para>
+
+ <para>
+ å¨selectå表ä¸å
å«ä¸ä¸ªè¡¨è¾¾å¼ä»¥ä¸çåæ¥è¯¢ï¼ä½ å¯ä»¥ä½¿ç¨ä¸ä¸ªå
ç»æé 符ï¼tuple constructorsï¼ï¼
+ </para>
+
+ <programlisting><![CDATA[from Cat as cat
+where not ( cat.name, cat.color ) in (
+ select cat.name, cat.color from DomesticCat cat
+)]]></programlisting>
+
+ <para>
+ 注æå¨æäºæ°æ®åºä¸ï¼ä¸å
æ¬Oracleä¸HSQLï¼ï¼ä½ ä¹å¯ä»¥å¨å
¶ä»è¯å¢ä¸ä½¿ç¨å
ç»æé 符ï¼
+ æ¯å¦æ¥è¯¢ç¨æ·ç±»åçç»ä»¶ä¸ç»åï¼
+ </para>
+
+ <programlisting><![CDATA[from Person where name = ('Gavin', 'A', 'King')]]></programlisting>
+
+ <para>
+ 该æ¥è¯¢çä»·äºæ´å¤æçï¼
+ </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ï¼æ¡ç®çæ°éåæ»ä»·å¼ï¼
+ è¿åå¼æç
§æ»ä»·å¼çç»æè¿è¡æåºã为äºå³å®ä»·æ ¼ï¼æ¥è¯¢ä½¿ç¨äºå½åç®å½ãä½ä¸ºè½¬æ¢ç»æçSQLæ¥è¯¢ï¼ä½¿ç¨äº<literal>ORDER</literal>,
+ <literal>ORDER_LINE</literal>, <literal>PRODUCT</literal>, <literal>CATALOG</literal> å<literal>PRICE</literal>
+ åºè¡¨ã
+ </para>
+
+ <programlisting><![CDATA[select order.id, sum(price.amount), count(item)
+from Order as order
+ join order.lineItems as item
+ join item.product as product,
+ Catalog as catalog
+ join catalog.prices as price
+where order.paid = false
+ and order.customer = :customer
+ and price.product = product
+ and catalog.effectiveDate < sysdate
+ and catalog.effectiveDate >= all (
+ select cat.effectiveDate
+ from Catalog as cat
+ where cat.effectiveDate < sysdate
+ )
+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
+ join item.product as product,
+ Catalog as catalog
+ join catalog.prices as price
+where order.paid = false
+ and order.customer = :customer
+ and price.product = product
+ and catalog = :currentCatalog
+group by order
+having sum(price.amount) > :minAmount
+order by sum(price.amount) desc]]></programlisting>
+
+ <para>
+ ä¸é¢ä¸ä¸ªæ¥è¯¢è®¡ç®æ¯ä¸ç§ç¶æä¸çæ¯ä»çæ°ç®ï¼é¤å»ææå¤äº<literal>AWAITING_APPROVAL</literal>ç¶æçæ¯ä»ï¼å 为å¨è¯¥ç¶æä¸
+ å½åçç¨æ·ä½åºäºç¶æçææ°æ¹åã该æ¥è¯¢è¢«è½¬æ¢æå«æ两个å
è¿æ¥ä»¥åä¸ä¸ªç¸å
³èçåéæ©çSQLæ¥è¯¢ï¼è¯¥æ¥è¯¢ä½¿ç¨äºè¡¨
+ <literal>PAYMENT</literal>, <literal>PAYMENT_STATUS</literal> 以å
+ <literal>PAYMENT_STATUS_CHANGE</literal>ã
+ </para>
+
+ <programlisting><![CDATA[select count(payment), status.name
+from Payment as payment
+ join payment.currentStatus as status
+ join payment.statusChanges as statusChange
+where payment.status.name <> PaymentStatus.AWAITING_APPROVAL
+ or (
+ statusChange.timeStamp = (
+ select max(change.timeStamp)
+ from PaymentStatusChange change
+ where change.payment = payment
+ )
+ and statusChange.user <> :currentUser
+ )
+group by status.name, status.sortOrder
+order by status.sortOrder]]></programlisting>
+
+ <para>
+ å¦æææ<literal>statusChanges</literal>å®ä¾éæ å°ä¸ºä¸ä¸ªå表ï¼listï¼èä¸æ¯ä¸ä¸ªéåï¼setï¼,
+ 书åæ¥è¯¢è¯å¥å°æ´å ç®å.
+ </para>
+
+ <programlisting><![CDATA[select count(payment), status.name
+from Payment as payment
+ join payment.currentStatus as status
+where payment.status.name <> PaymentStatus.AWAITING_APPROVAL
+ or payment.statusChanges[ maxIndex(payment.statusChanges) ].user <> :currentUser
+group by status.name, status.sortOrder
+order by status.sortOrder]]></programlisting>
+
+ <para>
+ ä¸é¢ä¸ä¸ªæ¥è¯¢ä½¿ç¨äºMS SQL Serverç <literal>isNull()</literal>å½æ°ç¨ä»¥è¿åå½åç¨æ·æå±ç»ç»çç»ç»å¸å·åç»ç»æªæ¯ä»çè´¦ã
+ å®è¢«è½¬æ¢æä¸ä¸ªå¯¹è¡¨<literal>ACCOUNT</literal>, <literal>PAYMENT</literal>, <literal>PAYMENT_STATUS</literal>,
+ <literal>ACCOUNT_TYPE</literal>, <literal>ORGANIZATION</literal> 以å <literal>ORG_USER</literal>è¿è¡çä¸ä¸ªå
è¿æ¥ï¼
+ ä¸ä¸ªå¤è¿æ¥åä¸ä¸ªåéæ©çSQLæ¥è¯¢ã
+ </para>
+
+ <programlisting><![CDATA[select account, payment
+from Account as account
+ left outer join account.payments as payment
+where :currentUser in elements(account.holder.users)
+ and PaymentStatus.UNPAID = isNull(payment.currentStatus.name, PaymentStatus.UNPAID)
+order by account.type.sortOrder, account.accountNumber, payment.dueDate]]></programlisting>
+
+ <para>
+ 对äºä¸äºæ°æ®åºï¼æ们éè¦å¼ç¨ï¼ç¸å
³çï¼åéæ©ã
+ </para>
+
+ <programlisting><![CDATA[select account, payment
+from Account as account
+ join account.holder.users as user
+ left outer join account.payments as payment
+where :currentUser = user
+ and PaymentStatus.UNPAID = isNull(payment.currentStatus.name, PaymentStatus.UNPAID)
+order by account.type.sortOrder, account.accountNumber, payment.dueDate]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="queryhql-bulk" revision="2">
+ <title>æ¹éçUPDATEåDELETE</title>
+
+ <para>
+ HQLç°å¨æ¯æ <literal>update</literal>, <literal>delete</literal> å
+ <literal>insert ... select ...</literal>è¯å¥. æ¥é
+ <xref linkend="batch-direct"/> 以è·å¾æ´å¤ä¿¡æ¯ã
+ </para>
+ </sect1>
+
+ <sect1 id="queryhql-tipstricks">
+ <title>å°æå·§ & å°çªé¨</title>
+
+ <para>
+ ä½ å¯ä»¥ç»è®¡æ¥è¯¢ç»æçæ°ç®èä¸å¿
å®é
çè¿åä»ä»¬ï¼
+ </para>
+
+ <programlisting><![CDATA[( (Integer) session.iterate("select count(*) from ....").next() ).intValue()]]></programlisting>
+
+ <para>
+ è¥æ³æ ¹æ®ä¸ä¸ªéåç大å°æ¥è¿è¡æåºï¼å¯ä»¥ä½¿ç¨å¦ä¸çè¯å¥ï¼
+ </para>
+
+ <programlisting><![CDATA[select usr.id, usr.name
+from User as usr
+ left join usr.messages as msg
+group by usr.id, usr.name
+order by count(msg)]]></programlisting>
+
+ <para>
+ å¦æä½ çæ°æ®åºæ¯æåéæ©ï¼ä½ å¯ä»¥å¨ä½ çæ¥è¯¢çwhereåå¥ä¸ä¸ºéæ©ç大å°ï¼selection sizeï¼æå®ä¸ä¸ªæ¡ä»¶:
+ </para>
+
+ <programlisting><![CDATA[from User usr where size(usr.messages) >= 1]]></programlisting>
+
+ <para>
+ å¦æä½ çæ°æ®åºä¸æ¯æåéæ©è¯å¥ï¼ä½¿ç¨ä¸é¢çæ¥è¯¢ï¼
+ </para>
+
+ <programlisting><![CDATA[select usr.id, usr.name
+from User usr.name
+ join usr.messages msg
+group by usr.id, usr.name
+having count(msg) >= 1]]></programlisting>
+
+ <para>
+ å 为å
è¿æ¥ï¼inner joinï¼çåå ï¼è¿ä¸ªè§£å³æ¹æ¡ä¸è½è¿åå«æé¶ä¸ªä¿¡æ¯ç<literal>User</literal>
+ ç±»çå®ä¾, æ以è¿ç§æ
åµä¸ä½¿ç¨ä¸é¢çæ ¼å¼å°æ¯æ帮å©ç:
+ </para>
+
+ <programlisting><![CDATA[select usr.id, usr.name
+from User as usr
+ left join usr.messages as msg
+group by usr.id, usr.name
+having count(msg) = 0]]></programlisting>
+
+ <para>
+ JavaBeançå±æ§å¯ä»¥è¢«ç»å®å°ä¸ä¸ªå½åæ¥è¯¢ï¼named queryï¼çåæ°ä¸ï¼
+ </para>
+
+ <programlisting><![CDATA[Query q = s.createQuery("from foo Foo as foo where foo.name=:name and foo.size=:size");
+q.setProperties(fooBean); // fooBeanå
å«æ¹æ³getName()ä¸getSize()
+List foos = q.list();]]></programlisting>
+
+ <para>
+ éè¿å°æ¥å£<literal>Query</literal>ä¸ä¸ä¸ªè¿æ»¤å¨ï¼filterï¼ä¸èµ·ä½¿ç¨ï¼éåï¼Collectionsï¼æ¯å¯ä»¥å页çï¼
+ </para>
+
+ <programlisting><![CDATA[Query q = s.createFilter( collection, "" ); // ä¸ä¸ªç®åçè¿æ»¤å¨
+q.setMaxResults(PAGE_SIZE);
+q.setFirstResult(PAGE_SIZE * pageNumber);
+List page = q.list();]]></programlisting>
+
+ <para>
+ éè¿ä½¿ç¨æ¥è¯¢è¿æ»¤å¨ï¼query filterï¼å¯ä»¥å°éåï¼Collectionï¼çåç´ åç»ææåº:
+ </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>
+
+ <para>
+ ä¸ç¨éè¿åå§åï¼ä½ å°±å¯ä»¥ç¥éä¸ä¸ªéåï¼Collectionï¼ç大å°ï¼
+ </para>
+
+ <programlisting><![CDATA[( (Integer) session.iterate("select count(*) from ....").next() ).intValue();]]></programlisting>
+
+ </sect1>
+
+</chapter>
\ No newline at end of file
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/query_sql.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/query_sql.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/query_sql.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,715 @@
+<chapter id="querysql" revision="2">
+ <title>Native SQLæ¥è¯¢</title>
+ <para>
+ ä½ ä¹å¯ä»¥ä½¿ç¨ä½ çæ°æ®åºçNative SQLè¯è¨æ¥æ¥è¯¢æ°æ®ãè¿å¯¹ä½ å¨è¦ä½¿ç¨æ°æ®åºçæäºç¹æ§çæ¶å(æ¯å¦è¯´å¨æ¥è¯¢æ示æè
Oracleä¸ç
+ <literal>CONNECT</literal>å
³é®å)ï¼è¿æ¯é常æç¨çãè¿å°±è½å¤æ«æ¸
ä½ æåæ¥ç´æ¥ä½¿ç¨SQL/JDBC çç¨åºè¿ç§»å°åºäº
+ Hibernateåºç¨çéè·¯ä¸çéç¢ã
+ </para>
+
+ <para>
+ Hibernate3å
è®¸ä½ ä½¿ç¨æåçsqlæ¥å®æææçcreate,update,delete,åloadæä½ï¼å
æ¬åå¨è¿ç¨ï¼
+ </para>
+
+ <sect1 id="querysql-creating" revision="4">
+ <title>使ç¨<literal>SQLQuery</literal></title>
+
+ <para>对åçSQLæ¥è¯¢æ§è¡çæ§å¶æ¯éè¿<literal>SQLQuery</literal>æ¥å£è¿è¡çï¼éè¿æ§è¡<literal>Session.createSQLQuery()</literal>è·åè¿ä¸ªæ¥å£ãä¸é¢æ¥æè¿°å¦ä½ä½¿ç¨è¿ä¸ªAPIè¿è¡æ¥è¯¢ã</para>
+
+ <sect2>
+ <title>æ éæ¥è¯¢ï¼Scalar queriesï¼</title>
+
+ <para>
+ æåºæ¬çSQLæ¥è¯¢å°±æ¯è·å¾ä¸ä¸ªæ éï¼æ°å¼ï¼çå表ã </para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT * FROM CATS").list();
+sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS").list();
+]]></programlisting>
+
+ <para>
+ å®ä»¬é½å°è¿åä¸ä¸ªObjectæ°ç»(Object[])ç»æçListï¼æ°ç»æ¯ä¸ªå
ç´ é½æ¯CATS表çä¸ä¸ªå段å¼ãHibernateä¼ä½¿ç¨ResultSetMetadataæ¥å¤å®è¿åçæ éå¼çå®é
顺åºåç±»åã
+ </para>
+
+ <para>
+ å¦æè¦é¿å
è¿å¤ç使ç¨<literal>ResultSetMetadata</literal>,æè
åªæ¯ä¸ºäºæ´å æç¡®çæåè¿åå¼ï¼å¯ä»¥ä½¿ç¨<literal>addScalar()</literal>ã
+ </para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT * FROM CATS")
+ .addScalar("ID", Hibernate.LONG)
+ .addScalar("NAME", Hibernate.STRING)
+ .addScalar("BIRTHDATE", Hibernate.DATE)
+]]></programlisting>
+
+
+ <para>è¿ä¸ªæ¥è¯¢æå®äº:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>SQLæ¥è¯¢å符串</para>
+ </listitem>
+
+ <listitem>
+ <para>è¦è¿åçå段åç±»å</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ å®ä»ç¶ä¼è¿åObjectæ°ç»,ä½æ¯æ¤æ¶ä¸å使ç¨<literal>ResultSetMetdata</literal>,èæ¯æç¡®çå°ID,NAMEåBIRTHDATEæç
§Long,StringåShortç±»åä»resultsetä¸ååºãåæ¶ï¼ä¹ææäºå°±ç®queryæ¯ä½¿ç¨<literal>*</literal>æ¥æ¥è¯¢çï¼å¯è½è·å¾è¶
è¿ååºçè¿ä¸ä¸ªå段ï¼ä¹ä»
ä»
ä¼è¿åè¿ä¸ä¸ªå段ã
+ </para>
+
+ <para>
+ 对å
¨é¨æè
é¨åçæ éå¼ä¸è®¾ç½®ç±»åä¿¡æ¯ä¹æ¯å¯ä»¥çã
+ </para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT * FROM CATS")
+ .addScalar("ID", Hibernate.LONG)
+ .addScalar("NAME")
+ .addScalar("BIRTHDATE")
+]]></programlisting>
+
+ <para>
+ åºæ¬ä¸è¿ååé¢ä¸ä¸ªæ¥è¯¢ç¸å,åªæ¯æ¤æ¶ä½¿ç¨<literal>ResultSetMetaData</literal>æ¥å³å®NAMEåBIRTHDATEçç±»åï¼èIDçç±»åæ¯æç¡®æåºçã
+ </para>
+
+ <para>
+ å
³äºä»ResultSetMetaDataè¿åçjava.sql.Typesæ¯å¦ä½æ å°å°Hibernateç±»åï¼æ¯ç±æ¹è¨(Dialect)æ§å¶çãåè¥æ个æå®çç±»å没æ被æ å°ï¼æè
ä¸æ¯ä½ æé¢æçç±»åï¼ä½ å¯ä»¥éè¿Dialetç<literal>registerHibernateType</literal>è°ç¨èªè¡å®ä¹ã
+ </para>
+
+ </sect2>
+
+ <sect2>
+ <title>å®ä½æ¥è¯¢(Entity queries)</title>
+
+ <para>
+ ä¸é¢çæ¥è¯¢é½æ¯è¿åæ éå¼çï¼ä¹å°±æ¯ä»resultsetä¸è¿åçâ裸âæ°æ®ãä¸é¢å±ç¤ºå¦ä½éè¿<literal>addEntity()</literal>让åçæ¥è¯¢è¿åå®ä½å¯¹è±¡ã
+ </para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT * FROM CATS").addEntity(Cat.class);
+sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS").addEntity(Cat.class);
+]]></programlisting>
+
+ <para>è¿ä¸ªæ¥è¯¢æå®ï¼</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>SQLæ¥è¯¢å符串</para>
+ </listitem>
+
+ <listitem>
+ <para>è¦è¿åçå®ä½</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ å设Cat被æ å°ä¸ºæ¥æID,NAMEåBIRTHDATEä¸ä¸ªå段çç±»ï¼ä»¥ä¸ç两个æ¥è¯¢é½è¿åä¸ä¸ªListï¼æ¯ä¸ªå
ç´ é½æ¯ä¸ä¸ªCatå®ä½ã
+ </para>
+
+ <para>
+ åè¥å®ä½å¨æ å°æ¶æä¸ä¸ª<literal>many-to-one</literal>çå
³èæåå¦å¤ä¸ä¸ªå®ä½ï¼å¨æ¥è¯¢æ¶å¿
é¡»ä¹è¿åé£ä¸ªå®ä½ï¼å¦åä¼å¯¼è´åçä¸ä¸ª"column not found"çæ°æ®åºé误ãè¿äºéå çå段å¯ä»¥ä½¿ç¨*æ 注æ¥èªå¨è¿åï¼ä½æ们å¸æè¿æ¯æç¡®ææï¼çä¸é¢è¿ä¸ªå
·ææå<literal>Dog</literal>ç<literal>many-to-one</literal>çä¾åï¼
+ </para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE, DOG_ID FROM CATS").addEntity(Cat.class);
+]]></programlisting>
+
+ <para>
+ è¿æ ·cat.getDog()å°±è½æ£å¸¸è¿ä½ã
+ </para>
+ </sect2>
+
+ <sect2>
+ <title>å¤çå
³èåéåç±»(Handling associations and collections)</title>
+
+ <para>
+ éè¿æåæåå°<literal>Dog</literal>è¿æ¥è·å¾ï¼èé¿å
åå§åproxy带æ¥çé¢å¤å¼éä¹æ¯å¯è½çãè¿æ¯éè¿<literal>addJoin()</literal>æ¹æ³è¿è¡çï¼è¿ä¸ªæ¹æ³å¯ä»¥è®©ä½ å°å
³èæéåè¿æ¥è¿æ¥ã
+ </para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT c.ID, NAME, BIRTHDATE, DOG_ID, D_ID, D_NAME FROM CATS c, DOGS d WHERE c.DOG_ID = d.D_ID")
+ .addEntity("cat", Cat.class)
+ .addJoin("cat.dog");
+]]></programlisting>
+
+ <para>
+ ä¸é¢è¿ä¸ªä¾åä¸ï¼è¿åç<literal>Cat</literal>对象ï¼å
¶<literal>dog</literal>å±æ§è¢«å®å
¨åå§åäºï¼ä¸åéè¦æ°æ®åºçé¢å¤æä½ã注æï¼æ们å äºä¸ä¸ªå«å("cat")ï¼ä»¥ä¾¿ææjoinçç®æ å±æ§è·¯å¾ãéè¿åæ ·çæåè¿æ¥ä¹å¯ä»¥ä½ç¨äºéåç±»ï¼ä¾å¦ï¼åè¥<literal>Cat</literal>æä¸ä¸ªæå<literal>Dog</literal>çä¸å¯¹å¤å
³èã
+ </para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE, D_ID, D_NAME, CAT_ID FROM CATS c, DOGS d WHERE c.ID = d.CAT_ID")
+ .addEntity("cat", Cat.class)
+ .addJoin("cat.dogs");
+]]></programlisting>
+
+ <p>
+ å°æ¤ä¸ºæ¢ï¼æ们碰å°äºå¤©è±æ¿ï¼è¥ä¸å¯¹SQLæ¥è¯¢è¿è¡å¢å¼ºï¼è¿äºå·²ç»æ¯å¨Hibernateä¸ä½¿ç¨åçSQLæ¥è¯¢æè½åå°çæ大å¯è½äºãä¸é¢çé®é¢å³å°åºç°ï¼è¿åå¤ä¸ªåæ ·ç±»åçå®ä½æä¹åï¼æè
é»è®¤çå«å/å段ä¸å¤åæä¹åï¼
+
+ </p>
+ </sect2>
+
+ <sect2>
+ <title>è¿åå¤ä¸ªå®ä½(Returning multiple entities)</title>
+
+ <para>
+ å°ç®å为æ¢,ç»æéå段å被åå®ä¸ºåæ å°æ件ä¸æå®ççå段åæ¯ä¸è´çãåè¥SQLæ¥è¯¢è¿æ¥äºå¤ä¸ªè¡¨ï¼åä¸ä¸ªå段åå¯è½å¨å¤ä¸ªè¡¨ä¸åºç°å¤æ¬¡ï¼è¿å°±ä¼é æé®é¢ã
+ </para>
+
+ <para>
+ ä¸é¢çæ¥è¯¢ä¸éè¦ä½¿ç¨å段å«å注å°ï¼è¿ä¸ªä¾åæ¬èº«ä¼å¤±è´¥ï¼ï¼
+ </para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT c.*, m.* FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID")
+ .addEntity("cat", Cat.class)
+ .addEntity("mother", Cat.class)
+]]></programlisting>
+
+ <para>
+ è¿ä¸ªæ¥è¯¢çæ¬ææ¯å¸ææ¯è¡è¿å两个Catå®ä¾ï¼ä¸ä¸ªæ¯cat,å¦ä¸ä¸ªæ¯å®çå¦å¦ãä½æ¯å 为å®ä»¬çå段å被æ å°ä¸ºç¸åçï¼èä¸å¨æäºæ°æ®åºä¸ï¼è¿åçå段å«åæ¯âc.IDâ,"c.NAME"è¿æ ·çå½¢å¼ï¼èå®ä»¬åå¨æ å°æ件ä¸çååï¼"ID"å"NAME"ï¼ä¸å¹é
ï¼è¿å°±ä¼é æ失败ã
+
+ </para>
+
+ <para>
+ ä¸é¢çå½¢å¼å¯ä»¥è§£å³å段åéå¤ï¼
+ </para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT {cat.*}, {mother.*} FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID")
+ .addEntity("cat", Cat.class)
+ .addEntity("mother", Cat.class)
+]]></programlisting>
+
+ <para>è¿ä¸ªæ¥è¯¢ææï¼</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ SQLæ¥è¯¢è¯å¥ï¼å
¶ä¸å
å«å ä½éæ¥è®©Hibernate注å°å段å«å</para>
+ </listitem>
+
+ <listitem>
+ <para>
+ æ¥è¯¢è¿åçå®ä½
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ä¸é¢ä½¿ç¨ç{cat.*}å{mother.*}æ è®°æ¯ä½ä¸ºâææå±æ§âçç®åå½¢å¼åºç°çãå½ç¶ä½ ä¹å¯ä»¥æç¡®å°ç½ååºå段åï¼ä½å¨è¿ä¸ªä¾åéé¢æ们让Hibernateæ¥ä¸ºæ¯ä¸ªå±æ§æ³¨å°SQLå段å«åãå段å«åçå ä½ç¬¦æ¯å±æ§åå ä¸è¡¨å«åçåç¼ãå¨ä¸é¢çä¾åä¸ï¼æ们ä»å¦å¤ä¸ä¸ªè¡¨ï¼cat_logï¼ä¸éè¿æ å°å
æ°æ®ä¸çæå®è·åCatåå®çå¦å¦ã注æï¼è¦æ¯æ们æ¿æï¼æ们çè³å¯ä»¥å¨whereåå¥ä¸ä½¿ç¨å±æ§å«åã
+
+ </para>
+
+ <programlisting><![CDATA[String sql = "SELECT ID as {c.id}, NAME as {c.name}, " +
+ "BIRTHDATE as {c.birthDate}, MOTHER_ID as {c.mother}, {mother.*} " +
+ "FROM CAT_LOG c, CAT_LOG m WHERE {c.mother} = c.ID";
+
+List loggedCats = sess.createSQLQuery(sql)
+ .addEntity("cat", Cat.class)
+ .addEntity("mother", Cat.class).list()
+]]></programlisting>
+
+ <sect3 id="querysql-aliasreferences" revision="2">
+ <title>å«ååå±æ§å¼ç¨(Alias and property references)</title>
+
+ <para>
+ 大å¤æ°æ
åµä¸ï¼é½éè¦ä¸é¢çå±æ§æ³¨å°ï¼ä½å¨ä½¿ç¨æ´å å¤æçæ å°ï¼æ¯å¦å¤åå±æ§ãéè¿æ è¯ç¬¦æé 继æ¿æ ï¼ä»¥åéåç±»ççæ
åµä¸ï¼ä¹æä¸äºç¹å«çå«åï¼æ¥å
许Hibernate注å°åéçå«åã
+ </para>
+
+ <para>
+ ä¸è¡¨ååºäºä½¿ç¨å«å注å°åæ°çä¸åå¯è½æ§ã注æï¼ä¸é¢ç»æä¸çå«ååªæ¯ç¤ºä¾ï¼å®ç¨æ¶æ¯ä¸ªå«åéè¦å¯ä¸å¹¶ä¸ä¸åçååã
+ </para>
+
+ <table frame="topbot" id="aliasinjection-summary">
+ <title>å«å注å°(alias injection names)</title>
+
+ <tgroup cols="3">
+ <colspec colwidth="1*" />
+
+ <colspec colwidth="1*" />
+
+ <colspec colwidth="2.5*" />
+
+ <thead>
+ <row>
+ <entry>æè¿°</entry>
+
+ <entry>è¯æ³</entry>
+
+ <entry>示ä¾</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>ç®åå±æ§</entry>
+ <entry><literal>{[aliasname].[propertyname]</literal></entry>
+ <entry><literal>A_NAME as {item.name}</literal></entry>
+ </row>
+ <row>
+ <entry>å¤åå±æ§</entry>
+ <entry><literal>{[aliasname].[componentname].[propertyname]}</literal></entry>
+ <entry><literal>CURRENCY as {item.amount.currency}, VALUE as {item.amount.value}</literal></entry>
+ </row>
+ <row>
+ <entry>å®ä½è¾¨å«å¨(Discriminator of an entity)</entry>
+ <entry><literal>{[aliasname].class}</literal></entry>
+ <entry><literal>DISC as {item.class}</literal></entry>
+ </row>
+ <row>
+ <entry>å®ä½çææå±æ§</entry>
+ <entry><literal>{[aliasname].*}</literal></entry>
+ <entry><literal>{item.*}</literal></entry>
+ </row>
+ <row>
+ <entry>éåé®(collection key)</entry>
+ <entry><literal>{[aliasname].key}</literal></entry>
+ <entry><literal>ORGID as {coll.key}</literal></entry>
+ </row>
+ <row>
+ <entry>éåid</entry>
+ <entry><literal>{[aliasname].id}</literal></entry>
+ <entry><literal>EMPID as {coll.id}</literal></entry>
+ </row>
+ <row>
+ <entry>éåå
ç´ </entry>
+ <entry><literal>{[aliasname].element}</literal></entry>
+ <entry><literal>XID as {coll.element}</literal></entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry>éåå
ç´ çå±æ§</entry>
+ <entry><literal>{[aliasname].element.[propertyname]}</literal></entry>
+ <entry><literal>NAME as {coll.element.name}</literal></entry>
+ </row>
+ <row>
+ <entry>éåå
ç´ çææå±æ§</entry>
+ <entry><literal>{[aliasname].element.*}</literal></entry>
+ <entry><literal>{coll.element.*}</literal></entry>
+ </row>
+ <row>
+ <entry>éåçææå±æ§</entry>
+ <entry><literal>{[aliasname].*}</literal></entry>
+ <entry><literal>{coll.*}</literal></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+
+ </sect3>
+ </sect2>
+
+ <sect2>
+ <title>è¿åéå管å®ä½(Returning non-managed entities)</title>
+
+ <para>
+ å¯ä»¥å¯¹åçsql æ¥è¯¢ä½¿ç¨ResultTransformerãè¿ä¼è¿åä¸åHibernate管ççå®ä½ã
+
+ </para>
+
+ <programlisting><![CDATA[sess.createSQLQuery("SELECT NAME, BIRTHDATE FROM CATS")
+ .setResultTransformer(Transformers.aliasToBean(CatDTO.class))]]></programlisting>
+
+ <para>è¿ä¸ªæ¥è¯¢æå®ï¼</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>SQLæ¥è¯¢å符串</para>
+ </listitem>
+
+ <listitem>
+ <para>ç»æ转æ¢å¨(result transformer)</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ä¸é¢çæ¥è¯¢å°ä¼è¿å<literal>CatDTO</literal>çå表,å®å°è¢«å®ä¾å并ä¸å°NAMEåBIRTHDAYçå¼æ³¨å°å
¥å¯¹åºçå±æ§æè
å段ã
+ </para>
+ </sect2>
+
+ <sect2>
+ <title>å¤ç继æ¿ï¼Handling inheritanceï¼</title>
+
+ <para>
+ åçSQLæ¥è¯¢åè¥å
¶æ¥è¯¢ç»æå®ä½æ¯ç»§æ¿æ ä¸çä¸é¨åï¼å®å¿
é¡»å
å«åºç±»åææåç±»çææå±æ§ã
+ </para>
+ </sect2>
+
+ <sect2>
+ <title>åæ°ï¼Parametersï¼</title>
+
+ <para>
+ åçæ¥è¯¢æ¯æä½ç½®åæ°åå½ååæ°ï¼
+ </para>
+
+ <programlisting><![CDATA[Query query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME like ?").addEntity(Cat.class);
+List pusList = query.setString(0, "Pus%").list();
+
+query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME like :name").addEntity(Cat.class);
+List pusList = query.setString("name", "Pus%").list(); ]]></programlisting>
+ </sect2>
+
+
+
+ </sect1>
+
+
+ <sect1 id="querysql-namedqueries" revision="3">
+ <title>å½åSQLæ¥è¯¢</title>
+
+ <para>
+ å¯ä»¥å¨æ å°ææ¡£ä¸å®ä¹æ¥è¯¢çåå,ç¶åå°±å¯ä»¥è±¡è°ç¨ä¸ä¸ªå½åçHQLæ¥è¯¢ä¸æ ·ç´æ¥è°ç¨å½åSQLæ¥è¯¢.å¨è¿ç§æ
åµä¸,æ们<emphasis>ä¸</emphasis>
+ éè¦è°ç¨<literal>addEntity()</literal>æ¹æ³.
+ </para>
+
+ <programlisting><![CDATA[<sql-query name="persons">
+ <return alias="person" class="eg.Person"/>
+ SELECT person.NAME AS {person.name},
+ person.AGE AS {person.age},
+ person.SEX AS {person.sex}
+ FROM PERSON person
+ WHERE person.NAME LIKE :namePattern
+</sql-query>]]></programlisting>
+
+ <programlisting><![CDATA[List people = sess.getNamedQuery("persons")
+ .setString("namePattern", namePattern)
+ .setMaxResults(50)
+ .list();]]></programlisting>
+
+ <para><literal><return-join></literal>å
+ <literal><load-collection></literal> å
ç´ æ¯ç¨æ¥è¿æ¥å
³è以åå°æ¥è¯¢å®ä¹ä¸ºé¢å
åå§åå个éåçã</para>
+
+ <programlisting><![CDATA[<sql-query name="personsWith">
+ <return alias="person" class="eg.Person"/>
+ <return-join alias="address" property="person.mailingAddress"/>
+ SELECT person.NAME AS {person.name},
+ person.AGE AS {person.age},
+ person.SEX AS {person.sex},
+ adddress.STREET AS {address.street},
+ adddress.CITY AS {address.city},
+ adddress.STATE AS {address.state},
+ adddress.ZIP AS {address.zip}
+ FROM PERSON person
+ JOIN ADDRESS adddress
+ ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'
+ WHERE person.NAME LIKE :namePattern
+</sql-query>]]></programlisting>
+
+
+ <para>
+ ä¸ä¸ªå½åæ¥è¯¢å¯è½ä¼è¿åä¸ä¸ªæ éå¼.ä½ å¿
须使ç¨<literal><return-scalar></literal>å
ç´ æ¥æå®å段çå«åå
+ Hibernateç±»å
+ </para>
+
+ <programlisting><![CDATA[<sql-query name="mySqlQuery">
+ <return-scalar column="name" type="string"/>
+ <return-scalar column="age" type="long"/>
+ SELECT p.NAME AS name,
+ p.AGE AS age,
+ FROM PERSON p WHERE p.NAME LIKE 'Hiber%'
+</sql-query>]]></programlisting>
+
+ <para>
+ ä½ å¯ä»¥æç»æéæ å°çä¿¡æ¯æ¾å¨å¤é¨ç<literal><resultset></literal>å
ç´ ä¸ï¼è¿æ ·å°±å¯ä»¥å¨å¤ä¸ªå½åæ¥è¯¢é´ï¼æè
éè¿<literal>setResultSetMapping()</literal>APIæ¥è®¿é®ã(æ¤å¤åæå³åçãåæ为ï¼You can externalize the resultset mapping informations in a
+ <literal><resultset></literal> element to either reuse them accross
+ several named queries or through the
+ <literal>setResultSetMapping()</literal> API.)
+ </para>
+
+ <programlisting><![CDATA[<resultset name="personAddress">
+ <return alias="person" class="eg.Person"/>
+ <return-join alias="address" property="person.mailingAddress"/>
+</resultset>
+
+<sql-query name="personsWith" resultset-ref="personAddress">
+ SELECT person.NAME AS {person.name},
+ person.AGE AS {person.age},
+ person.SEX AS {person.sex},
+ adddress.STREET AS {address.street},
+ adddress.CITY AS {address.city},
+ adddress.STATE AS {address.state},
+ adddress.ZIP AS {address.zip}
+ FROM PERSON person
+ JOIN ADDRESS adddress
+ ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'
+ WHERE person.NAME LIKE :namePattern
+</sql-query>]]></programlisting>
+
+ <para>
+ å¦å¤,ä½ å¯ä»¥å¨java代ç ä¸ç´æ¥ä½¿ç¨hbmæ件ä¸çç»æéå®ä¹ä¿¡æ¯ã
+ </para>
+
+ <programlisting><![CDATA[List cats = sess.createSQLQuery(
+ "select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id"
+ )
+ .setResultSetMapping("catAndKitten")
+ .list();]]></programlisting>
+
+ <sect2 id="propertyresults">
+ <title>使ç¨return-propertyæ¥æç¡®å°æå®å段/å«å</title>
+
+ <para>
+ 使ç¨<literal><return-property></literal>ä½ å¯ä»¥æç¡®çåè¯Hibernate使ç¨åªäºå段å«å,è¿å代äºä½¿ç¨<literal>{}</literal>-è¯æ³
+ æ¥è®©Hibernate注å
¥å®èªå·±çå«å.
+ </para>
+
+ <programlisting><![CDATA[<sql-query name="mySqlQuery">
+ <return alias="person" class="eg.Person">
+ <return-property name="name" column="myName"/>
+ <return-property name="age" column="myAge"/>
+ <return-property name="sex" column="mySex"/>
+ </return>
+ SELECT person.NAME AS myName,
+ person.AGE AS myAge,
+ person.SEX AS mySex,
+ FROM PERSON person WHERE person.NAME LIKE :name
+</sql-query>
+]]></programlisting>
+ <literal><return-property></literal>ä¹å¯ç¨äºå¤ä¸ªå段,å®è§£å³äºä½¿ç¨<literal>{}</literal>-è¯æ³ä¸è½ç»ç²åº¦æ§å¶å¤ä¸ªå段çéå¶
+ <programlisting><![CDATA[<sql-query name="organizationCurrentEmployments">
+ <return alias="emp" class="Employment">
+ <return-property name="salary">
+ <return-column name="VALUE"/>
+ <return-column name="CURRENCY"/>
+ </return-property>
+ <return-property name="endDate" column="myEndDate"/>
+ </return>
+ SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer},
+ STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},
+ REGIONCODE as {emp.regionCode}, EID AS {emp.id}, VALUE, CURRENCY
+ FROM EMPLOYMENT
+ WHERE EMPLOYER = :id AND ENDDATE IS NULL
+ ORDER BY STARTDATE ASC
+</sql-query>]]></programlisting>
+
+ <para>
+ 注æå¨è¿ä¸ªä¾åä¸,æ们使ç¨äº<literal><return-property></literal>ç»å<literal>{}</literal>ç注å
¥è¯æ³.
+ å
许ç¨æ·æ¥éæ©å¦ä½å¼ç¨å段以åå±æ§.
+ </para>
+
+ <para>
+ å¦æä½ æ å°ä¸ä¸ªè¯å«å¨(discriminator),ä½ å¿
须使ç¨<literal><return-discriminator></literal> æ¥æå®è¯å«å¨å段
+ </para>
+ </sect2>
+
+ <sect2 id="sp_query" revision="1">
+ <title>使ç¨åå¨è¿ç¨æ¥æ¥è¯¢</title>
+
+ <para>
+ Hibernate 3å¼å
¥äºå¯¹åå¨è¿ç¨æ¥è¯¢(stored procedure)åå½æ°(function)çæ¯æ.以ä¸ç说æä¸ï¼è¿äºè
ä¸è¬é½éç¨ã
+
+ åå¨è¿ç¨/å½æ°å¿
é¡»è¿åä¸ä¸ªç»æé,ä½ä¸ºHibernateè½å¤ä½¿ç¨ç第ä¸ä¸ªå¤é¨åæ°.
+
+ ä¸é¢æ¯ä¸ä¸ªOracle9åæ´é«çæ¬çåå¨è¿ç¨ä¾å.</para>
+
+ <programlisting><![CDATA[CREATE OR REPLACE FUNCTION selectAllEmployments
+ RETURN SYS_REFCURSOR
+AS
+ st_cursor SYS_REFCURSOR;
+BEGIN
+ OPEN st_cursor FOR
+ SELECT EMPLOYEE, EMPLOYER,
+ STARTDATE, ENDDATE,
+ REGIONCODE, EID, VALUE, CURRENCY
+ FROM EMPLOYMENT;
+ RETURN st_cursor;
+ END;]]></programlisting>
+ <para>
+ å¨Hibernateéè¦è¦ä½¿ç¨è¿ä¸ªæ¥è¯¢,ä½ éè¦éè¿å½åæ¥è¯¢æ¥æ å°å®.
+ </para>
+
+ <programlisting><![CDATA[<sql-query name="selectAllEmployees_SP" callable="true">
+ <return alias="emp" class="Employment">
+ <return-property name="employee" column="EMPLOYEE"/>
+ <return-property name="employer" column="EMPLOYER"/>
+ <return-property name="startDate" column="STARTDATE"/>
+ <return-property name="endDate" column="ENDDATE"/>
+ <return-property name="regionCode" column="REGIONCODE"/>
+ <return-property name="id" column="EID"/>
+ <return-property name="salary">
+ <return-column name="VALUE"/>
+ <return-column name="CURRENCY"/>
+ </return-property>
+ </return>
+ { ? = call selectAllEmployments() }
+</sql-query>]]></programlisting>
+
+ <para>
+ 注æåå¨è¿ç¨å½åä»
ä»
è¿åæ éåå®ä½.ç°å¨ä¸æ¯æ<literal><return-join></literal>å<literal><load-collection></literal>
+
+ </para>
+
+ <sect3 id="querysql-limits-storedprocedures" revision="1">
+ <title>使ç¨åå¨è¿ç¨çè§ååéå¶</title>
+
+ <para>
+ 为äºå¨Hibernateä¸ä½¿ç¨åå¨è¿ç¨,ä½ å¿
é¡»éµå¾ªä¸äºè§å.ä¸éµå¾ªè¿äºè§åçåå¨è¿ç¨å°ä¸å¯ç¨.å¦æä½ ä»ç¶æ³è¦ä½¿ç¨ä»ä»¬,
+ ä½ å¿
é¡»éè¿<literal>session.connection()</literal>æ¥æ§è¡ä»ä»¬.è¿äºè§åé对äºä¸åçæ°æ®åº.å 为æ°æ®åº
+ æä¾åæåç§ä¸åçåå¨è¿ç¨è¯æ³åè¯ä¹.
+ </para>
+
+ <para>
+ 对åå¨è¿ç¨è¿è¡çæ¥è¯¢æ æ³ä½¿ç¨<literal>setFirstResult()/setMaxResults()</literal>è¿è¡å页ã
+ </para>
+
+
+ <para>建议éç¨çè°ç¨æ¹å¼æ¯æ åSQL92: <literal>{ ? = call
+ functionName(<parameters>) }</literal> æè
<literal>{ ? = call
+ procedureName(<parameters>}</literal>.åçè°ç¨è¯æ³ä¸è¢«æ¯æã</para>
+
+ <para>
+ 对äºOracleæå¦ä¸è§å:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>å½æ°å¿
é¡»è¿åä¸ä¸ªç»æéãåå¨è¿ç¨ç第ä¸ä¸ªåæ°å¿
é¡»æ¯<literal>OUT</literal>ï¼å®è¿åä¸ä¸ªç»æéãè¿æ¯éè¿Oracle 9æ10ç<literal>SYS_REFCURSOR</literal>ç±»åæ¥å®æçãå¨Oracleä¸ä½ éè¦å®ä¹ä¸ä¸ª<literal>REF CURSOR</literal>ç±»åï¼åè§Oracleçæåã</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ 对äºSybaseæè
MS SQL serveræå¦ä¸è§å:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ åå¨è¿ç¨å¿
é¡»è¿åä¸ä¸ªç»æéã.注æè¿äºserverså¯è½è¿åå¤ä¸ªç»æé以åæ´æ°çæ°ç®.Hibernateå°ååºç¬¬ä¸æ¡ç»æéä½ä¸ºå®çè¿åå¼ï¼
+ å
¶ä»å°è¢«ä¸¢å¼ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ å¦æä½ è½å¤å¨åå¨è¿ç¨é设å®<literal>SET NOCOUNT ON</literal>ï¼è¿å¯è½ä¼æçæ´é«ï¼ä½è¿ä¸æ¯å¿
éçã
+ </para>
+ </listitem>
+ </itemizedlist>
+ </sect3>
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="querysql-cud">
+ <title>å®å¶SQLç¨æ¥createï¼updateådelete</title>
+
+ <para>
+ Hibernate3è½å¤ä½¿ç¨å®å¶çSQLè¯å¥æ¥æ§è¡create,updateådeleteæä½ãå¨Hibernateä¸ï¼æä¹
åçç±»åéåå·²ç»
+ å
å«äºä¸å¥é
ç½®æ产ççè¯å¥(insertsql, deletesql, updatesqlçç)ï¼è¿äºæ å°æ è®° <literal><sql-insert></literal>,
+ <literal><sql-delete></literal>, and <literal><sql-update></literal>éè½½äº
+ è¿äºè¯å¥ã
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true"/>
+ <sql-insert>INSERT INTO PERSON (NAME, ID) VALUES ( UPPER(?), ? )</sql-insert>
+ <sql-update>UPDATE PERSON SET NAME=UPPER(?) WHERE ID=?</sql-update>
+ <sql-delete>DELETE FROM PERSON WHERE ID=?</sql-delete>
+</class>]]></programlisting>
+
+ <para>
+ è¿äºSQLç´æ¥å¨ä½ çæ°æ®åºéæ§è¡ï¼æä»¥ä½ å¯ä»¥èªç±ç使ç¨ä½ å欢çä»»æè¯æ³ãä½å¦æä½ ä½¿ç¨æ°æ®åºç¹å®çè¯æ³ï¼
+ è¿å½ç¶ä¼éä½ä½ æ å°çå¯ç§»æ¤æ§ã
+ </para>
+
+ <para>
+ å¦æ设å®<literal>callable</literal>ï¼åè½å¤æ¯æåå¨è¿ç¨äºã
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true"/>
+ <sql-insert callable="true">{call createPerson (?, ?)}</sql-insert>
+ <sql-delete callable="true">{? = call deletePerson (?)}</sql-delete>
+ <sql-update callable="true">{? = call updatePerson (?, ?)}</sql-update>
+</class>]]></programlisting>
+
+ <para>
+ åæ°çä½ç½®é¡ºåºæ¯é常éè¦çï¼ä»ä»¬å¿
é¡»åHibernateææå¾
ç顺åºç¸åã
+ </para>
+
+ <para>
+ ä½ è½å¤éè¿è®¾å®æ¥å¿è°è¯çº§å«ä¸º<literal>org.hiberante.persister.entity</literal>,æ¥æ¥çHibernateææå¾
ç顺åºãå¨è¿ä¸ªçº§å«ä¸ï¼
+ Hibernateå°ä¼æå°åºcreate,updateådeleteå®ä½çéæSQLã(å¦ææ³çå°é¢è®¡ç顺åºãè®°å¾ä¸è¦å°å®å¶SQLå
å«å¨æ å°æ件éï¼
+ å 为ä»ä»¬ä¼éè½½HibernateçæçéæSQLã)
+ </para>
+
+ <para>
+ å¨å¤§å¤æ°æ
åµä¸(æ好è¿ä¹å)ï¼åå¨è¿ç¨éè¦è¿åæå
¥/æ´æ°/å é¤çè¡æ°ï¼å 为Hibernate对è¯å¥çæåæ§è¡æäºè¿è¡æ¶çæ£æ¥ã
+ Hibernate常ä¼æè¿è¡CUDæä½çè¯å¥ç第ä¸ä¸ªåæ°æ³¨å为ä¸ä¸ªæ°å¼åè¾åºåæ°ã
+ </para>
+
+ <programlisting><![CDATA[CREATE OR REPLACE FUNCTION updatePerson (uid IN NUMBER, uname IN VARCHAR2)
+ RETURN NUMBER IS
+BEGIN
+
+ update PERSON
+ set
+ NAME = uname,
+ where
+ ID = uid;
+
+ return SQL%ROWCOUNT;
+
+END updatePerson;]]></programlisting>
+
+
+ </sect1>
+
+ <sect1 id="querysql-load">
+ <title>å®å¶è£
è½½SQL</title>
+
+ <para>
+ ä½ å¯è½éè¦å£°æä½ èªå·±çSQL(æHQL)æ¥è£
è½½å®ä½
+ </para>
+
+ <programlisting><![CDATA[<sql-query name="person">
+ <return alias="pers" class="Person" lock-mode="upgrade"/>
+ SELECT NAME AS {pers.name}, ID AS {pers.id}
+ FROM PERSON
+ WHERE ID=?
+ FOR UPDATE
+</sql-query>]]></programlisting>
+
+ <para>
+ è¿åªæ¯ä¸ä¸ªåé¢è®¨è®ºè¿çå½åæ¥è¯¢å£°æï¼ä½ å¯ä»¥å¨ç±»æ å°éå¼ç¨è¿ä¸ªå½åæ¥è¯¢ã
+ </para>
+
+ <programlisting><![CDATA[<class name="Person">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true"/>
+ <loader query-ref="person"/>
+</class>]]></programlisting>
+
+ <para>
+ è¿ä¹å¯ä»¥ç¨äºåå¨è¿ç¨
+ </para>
+
+ <para>ä½ çè³å¯ä»¥å®ä¸ä¸ªç¨äºéåè£
è½½çæ¥è¯¢:</para>
+
+ <programlisting><![CDATA[<set name="employments" inverse="true">
+ <key/>
+ <one-to-many class="Employment"/>
+ <loader query-ref="employments"/>
+</set>]]></programlisting>
+
+ <programlisting><![CDATA[<sql-query name="employments">
+ <load-collection alias="emp" role="Person.employments"/>
+ SELECT {emp.*}
+ FROM EMPLOYMENT emp
+ WHERE EMPLOYER = :id
+ ORDER BY STARTDATE ASC, EMPLOYEE ASC
+</sql-query>]]></programlisting>
+
+ <para>ä½ çè³è¿å¯ä»¥å®ä¹ä¸ä¸ªå®ä½è£
è½½å¨ï¼å®éè¿è¿æ¥æåè£
è½½ä¸ä¸ªéå:</para>
+
+ <programlisting><![CDATA[<sql-query name="person">
+ <return alias="pers" class="Person"/>
+ <return-join alias="emp" property="pers.employments"/>
+ SELECT NAME AS {pers.*}, {emp.*}
+ FROM PERSON pers
+ LEFT OUTER JOIN EMPLOYMENT emp
+ ON pers.ID = emp.PERSON_ID
+ WHERE ID=?
+</sql-query>]]></programlisting>
+ </sect1>
+
+</chapter>
\ No newline at end of file
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/quickstart.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/quickstart.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/quickstart.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,461 @@
+<chapter id="quickstart">
+ <title>å¨Tomcatä¸å¿«éä¸æ</title>
+
+ <sect1 id="quickstart-intro" revision="2">
+ <title>å¼å§Hibernateä¹æ
</title>
+
+ <para>
+ è¿ä»½æç¨æè¿°å¦ä½å¨Apache Tomcat servlet容å¨ä¸ä¸ºwebåºç¨ç¨åºé
ç½®Hibernate 3.0(æ们使ç¨Tomcat 4.1çæ¬ï¼ä¸5.0çæ¬å·®å«å¾å°)ãHibernateå¨å¤§å¤æ°ä¸»æµJ2EEåºç¨æå¡å¨
+ çè¿è¡ç¯å¢ä¸é½å¯ä»¥å·¥ä½è¯å¥½ï¼çè³ä¹å¯ä»¥å¨ç¬ç«Javaåºç¨ç¨åºä¸ä½¿ç¨ãå¨æ¬æç¨ä¸ä½¿ç¨ç示ä¾æ°æ®åºç³»ç»æ¯PostgreSQL 7.4,åªéè¦ä¿®æ¹Hibernate SQLè¯è¨é
ç½®ä¸è¿æ¥å±æ§ï¼å°±å¯ä»¥
+ å¾å®¹æçæ¯æå
¶ä»æ°æ®åºäºã
+ </para>
+
+ <para>
+ 第ä¸æ¥ï¼æ们å¿
é¡»æ·è´ææéè¦çåºæ件å°Tomcatå®è£
ç®å½ä¸ãå¨è¿ç¯æç¨ä¸ï¼æ们使ç¨ä¸ä¸ªç¬ç«çweb Contexté
ç½®ï¼<literal>webapps/quickstart</literal>ï¼ãæ们确认å
¨å±åºæ件ï¼<literal>TOMCAT/common/lib</literal>ï¼åæ¬webåºç¨ç¨åºä¸ä¸æçè·¯å¾ï¼å¯¹äºjaræ¥è¯´æ¯<literal>webapps/quickstart/WEB-INF/lib</literal>ï¼å¯¹äºclassæ件æ¥è¯´æ¯<literal>webapps/quickstart/WEB-INF/classes</literal>ï¼è½å¤è¢«ç±»è£
è½½å¨æ£ç´¢å°ãæ们æè¿ä¸¤ä¸ªç±»è£
è½½å¨çº§å«åå«ç§°åå
¨å±ç±»è·¯å¾(global classpath)åä¸ä¸æ类路å¾(context classpath)ã
+ </para>
+
+ <para>
+ ç°å¨ï¼æè¿äºåºæ件copyå°ä¸¤ä¸ªç±»è·¯å¾å»:
+ </para>
+
+ <orderedlist>
+ <listitem>
+ <para>
+ ææ°æ®åºéè¦çJDBC驱å¨æ件æ·è´å°å
¨å±ç±»è·¯å¾ï¼è¿æ¯tomcatæç»çDBCPè¿æ¥æ± æéè¦çãHibernate使ç¨JDBCè¿æ¥æ°æ®åºæ¹å¼æ§è¡SQLè¯å¥ï¼æä»¥ä½ è¦ä¹æä¾å¤é¨è¿æ¥æ± ä¸çè¿æ¥ç»Hibernateï¼æè
é
ç½®Hibernateèªå¸¦çè¿æ¥æ± ï¼C3PO,Proxoolï¼ã对äºæ¬æç¨æ¥è¯´ï¼æ<literal>pg74jdbc3.jar</literal>åºæ件ï¼æ¯æPostgreSQL 7.4åJDK 1.4)å°å
¨å±ç±»è£
载路å¾ä¸å³å¯ãå¦æä½ å¸æ使ç¨å
¶ä»çæ°æ®åºï¼æ·è´å
¶ç¸åºçJDBC 驱å¨æ件ï¼ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ æ°¸è¿ä¸è¦æ·è´ä»»ä½å
¶ä»ä¸è¥¿å°Tomcatçå
¨å±ç±»è·¯å¾ä¸ï¼å¦åä½ å¯è½å¨ä½¿ç¨å
¶ä»ä¸äºå·¥å
·ä¸éå°éº»ç¦ï¼æ¯å¦log4j, commons-loggingççã
+ ä¸å®è¦è®©æ¯ä¸ªwebåºç¨ç¨åºä½¿ç¨èªå·±çä¸ä¸æ类路å¾ï¼å°±æ¯è¯´æä½ èªå·±éè¦çç±»åºæ·è´å°<literal>WEB-INF/lib</literal>ä¸å»ï¼æé
ç½®æ件configuration/propertyçé
ç½®æ件æ·è´å°<literal>WEB-INF/classes</literal>ä¸é¢å»ãè¿ä¸¤ä¸ªç®å½é½æ¯å½åç¨åºç¼ºççä¸ä¸æ类路å¾ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Hibernateæ¬èº«æå
æä¸ä¸ªJARç±»åºãå°<literal>hibernate3.jar</literal>æ件æ·è´å°ç¨åºçä¸ä¸æ类路å¾ä¸ï¼åä½ åºç¨ç¨åºçå
¶ä»åºæ件æ¾ä¸èµ·ãå¨è¿è¡æ¶ï¼Hibernateè¿éè¦ä¸äºç¬¬ä¸æ¹ç±»åºï¼å®ä»¬å¨Hibernateåè¡å
ç<literal>lib/</literal>ç®å½ä¸ãåè§<xref linkend="3rdpartylibs"/>ãææéè¦ç第ä¸æ¹åºæ件ä¹æ·è´å°ä¸ä¸æ类路å¾ä¸ã
+ </para>
+ </listitem>
+ </orderedlist>
+
+ <table frame="topbot" id="3rdpartylibs">
+ <title>
+ Hibernate 第ä¸æ¹ç±»åº
+ </title>
+ <tgroup cols="2" rowsep="1" colsep="1">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="2*"/>
+ <thead>
+ <row>
+ <entry align="center">
+ ç±»åº
+ </entry>
+ <entry align="center">
+ æè¿°
+ </entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ antlr (å¿
é)
+ </entry>
+ <entry>
+ Hibernate使ç¨ANTLRæ¥äº§çæ¥è¯¢åæå¨ï¼è¿ä¸ªç±»åºå¨è¿è¡ç¯å¢ä¸æ¶ä¹æ¯å¿
éçã
+ </entry>
+ </row>
+ <row>
+ <entry>
+ dom4j (å¿
é)
+ </entry>
+ <entry>
+ Hibernate使ç¨dom4j解æXMLé
ç½®æ件åXMLæ å°å
æ件ã
+ </entry>
+ </row>
+ <row>
+ <entry>
+ CGLIB ,asm(å¿
é)
+ </entry>
+ <entry>
+ Hibernateå¨è¿è¡æ¶ä½¿ç¨è¿ä¸ªä»£ç çæåºå¢å¼ºç±»ï¼ä¸Javaåå°æºå¶èå使ç¨ï¼ã
+ </entry>
+ </row>
+ <row>
+ <entry>
+ Commons Collections, Commons Logging (å¿
é)
+ </entry>
+ <entry>
+ Hibernat使ç¨Apache Jakarta Commons项ç®æä¾çå¤ä¸ªå·¥å
·ç±»åºã
+ </entry>
+ </row>
+ <row>
+ <entry>
+ EHCache (å¿
é)
+ </entry>
+ <entry>
+ Hibernateå¯ä»¥ä½¿ç¨ä¸åcacheç¼åå·¥å
·ä½ä¸ºäºçº§ç¼åãEHCacheæ¯ç¼ºççcacheç¼åå·¥å
·ã
+ </entry>
+ </row>
+ <row>
+ <entry>
+ Log4j (å¯é)
+ </entry>
+ <entry>
+ Hibernate使ç¨Commons Logging API,å®ä¹å¯ä»¥ä¾æ¬¡ä½¿ç¨Log4jä½ä¸ºåºå±å®æ½logçæºå¶ãå¦æä¸ä¸æç±»ç®å½ä¸åå¨Log4jåºï¼åCommons Logging使ç¨Log4jå并å®å¨ä¸ä¸æ类路å¾ä¸å¯»æ¾ç<literal>log4j.properties</literal>æ件ãä½ å¯ä»¥ä½¿ç¨å¨Hibernateåè¡å
ä¸å
å«ä¸çé£ä¸ªç¤ºä¾Log4jçé
ç½®æ件ãè¿æ ·ï¼ælog4j.jaråå®çé
ç½®æ件ï¼ä½äº<literal>src/</literal>ç®å½ä¸ï¼æ·è´å°ä½ çä¸ä¸æ类路å¾ä¸ï¼å°±å¯ä»¥å¨åå°çå°åºç¨åºå¦ä½è¿è¡çã
+ </entry>
+ </row>
+ <row>
+ <entry>
+ å
¶ä»æ件æ¯ä¸æ¯å¿
éçï¼
+ </entry>
+ <entry>
+ 请å¯çHibernateåè¡å
ä¸ç lib/README.txtæ件ï¼è¿æ¯ä¸ä¸ªHibernateåè¡å
ä¸é带ç第ä¸æ¹ç±»åºçå表ï¼ä»ä»¬æ»æ¯ä¿æææ°çãä½ å¯ä»¥å¨é£éæ¾å°ææå¿
éæè
å¯éçç±»åº(注æï¼å
¶ä¸ç"buildtime required"æçæ¯ç¼è¯Hibernateæ¶æéè¦èéç¼è¯ä½ èªå·±çç¨åºæå¿
éçç±»åºï¼ã
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ æ¥ä¸æ¥æ们æ¥é
ç½®å¨TomcatåHibernateä¸å
±ç¨çæ°æ®åºè¿æ¥æ± ãä¹å°±æ¯è¯´Tomcatä¼æä¾ç»è¿æ± å¤ççJDBCè¿æ¥ï¼ç¨å®å
ç½®çDBCPè¿æ¥æ± ï¼ï¼Hibernateéè¿JNDIæ¹å¼æ¥è¯·æ±è·å¾JDBCè¿æ¥ãä½ä¸ºæ¿ä»£æ¹æ¡ï¼ä½ ä¹å¯ä»¥è®©Hibernateèªè¡ç®¡çè¿æ¥æ± ãTomcatæè¿æ¥æ± ç»å®å°JNDI,æ们è¦å¨Tomcatç主é
ç½®æ件ï¼<literal>TOMCAT/conf/server.xml</literal>ï¼ä¸å ä¸ä¸ªèµæºå£°æ:
+ </para>
+
+ <programlisting><![CDATA[<Context path="/quickstart" docBase="quickstart">
+ <Resource name="jdbc/quickstart" scope="Shareable" type="javax.sql.DataSource"/>
+ <ResourceParams name="jdbc/quickstart">
+ <parameter>
+ <name>factory</name>
+ <value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
+ </parameter>
+
+ <!-- DBCP database connection settings -->
+ <parameter>
+ <name>url</name>
+ <value>jdbc:postgresql://localhost/quickstart</value>
+ </parameter>
+ <parameter>
+ <name>driverClassName</name><value>org.postgresql.Driver</value>
+ </parameter>
+ <parameter>
+ <name>username</name>
+ <value>quickstart</value>
+ </parameter>
+ <parameter>
+ <name>password</name>
+ <value>secret</value>
+ </parameter>
+
+ <!-- DBCP connection pooling options -->
+ <parameter>
+ <name>maxWait</name>
+ <value>3000</value>
+ </parameter>
+ <parameter>
+ <name>maxIdle</name>
+ <value>100</value>
+ </parameter>
+ <parameter>
+ <name>maxActive</name>
+ <value>10</value>
+ </parameter>
+ </ResourceParams>
+</Context>]]></programlisting>
+
+ <para>
+ æ们å¨è¿ä¸ªä¾åä¸è¦é
ç½®çä¸ä¸æå«å<literal>quickstart</literal>ï¼å®ä½äº<literal>TOMCAT/webapp/quickstart</literal>ç®å½ä¸ãå¦æè¦è®¿é®è¿ä¸ªåºç¨ç¨åº,å¨ä½ çæµè§å¨ä¸è¾å
¥<literal>http://localhost:8080/quickstart</literal>å°±å¯ä»¥äº(å½ç¶ï¼å¨åé¢å ä¸å¨ä½ ç<literal>web.xml</literal>æ件ä¸é
ç½®å¥½ä½ çservlet)ãä½ ç°å¨å¯ä»¥å建ä¸ä¸ªåªå«æ空<literal>process()</literal>çç®åservletäºã
+ </para>
+
+ <para>
+ Tomcatç°å¨éè¿JNDIçæ¹å¼ï¼<literal>java:comp/env/jdbc/quickstart</literal>æ¥æä¾è¿æ¥ãå¦æä½ å¨é
ç½®è¿æ¥æ± éå°é®é¢ï¼è¯·æ¥é
Tomcatææ¡£ãå¦æä½ éå°äºJDBC驱å¨ææ¥çexceptionåºéä¿¡æ¯ï¼è¯·å¨æ²¡æHibernateçç¯å¢ä¸ï¼å
æµè¯JDBCè¿æ¥æ± æ¬èº«æ¯å¦é
ç½®æ£ç¡®ãTomcatåJDBCçé
ç½®æç¨å¯ä»¥å¨Webä¸æ¥å°ã
+ </para>
+
+ <para>
+ ä¸ä¸æ¥å°±æ¯é
ç½®Hibernateãé¦å
Hibernateå¿
é¡»ç¥éå®å¦ä½è·å¾JDBCè¿æ¥ï¼å¨è¿éæ们使ç¨åºäºXMLæ ¼å¼çHibernateé
ç½®æ件ãå½ç¶ä½¿ç¨propertiesæ件çè¿è¡é
ç½®ï¼ä½ç¼ºå°ä¸äºXMLè¯æ³çç¹æ§ãè¿ä¸ªXMLé
ç½®æ件å¿
é¡»æ¾å¨ä¸ä¸æ类路å¾(<literal>WEB-INF/classes</literal>)ä¸é¢ï¼å½å为<literal>hibernate.cfg.xml</literal>:
+ </para>
+
+ <programlisting><![CDATA[<?xml version='1.0' encoding='utf-8'?>
+<!DOCTYPE hibernate-configuration
+ PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
+ "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
+
+<hibernate-configuration>
+
+ <session-factory>
+
+ <property name="connection.datasource">java:comp/env/jdbc/quickstart</property>
+ <property name="show_sql">false</property>
+ <property name="dialect">org.hibernate.dialect.PostgreSQLDialect</property>
+
+ <!-- Mapping files -->
+ <mapping resource="Cat.hbm.xml"/>
+
+ </session-factory>
+
+</hibernate-configuration>]]></programlisting>
+
+ <para>
+ å¨è¿éæ们å
³éäºSQLå½ä»¤çlogï¼åæ¶åè¯Hibernate使ç¨åªç§SQLæ°æ®åºç¨è¯(Dialet)ï¼ä»¥åå¦ä½å¾å°JDBCè¿æ¥ï¼éè¿Tomcat声æç»å®çJNDIå°åï¼ãDialetæ¯å¿
éé
ç½®çï¼å 为ä¸åçæ°æ®åºé½å"SQLæ å"æä¸äºåºå
¥ãä¸ç¨æ
å¿ï¼Hibernateä¼æ¿ä½ å¤çè¿äºå·®å¼ï¼Hibernateæ¯æææ主æµçåä¸åå¼æ¾æºä»£ç æ°æ®åºã
+ </para>
+
+ <para>
+ <literal>SessionFactory</literal>æ¯Hibernateçä¸ä¸ªæ¦å¿µï¼è¡¨ç¤ºå¯¹åºä¸ä¸ªæ°æ®åå¨æºãéè¿å建å¤ä¸ªXMLé
ç½®æ件并å¨ä½ çç¨åºä¸å建å¤ä¸ª<literal>Configuration</literal>å<literal>SessionFactory</literal>对象ï¼å°±å¯ä»¥æ¯æå¤ä¸ªæ°æ®åºäºã
+ </para>
+
+ <para>
+ å¨<literal>hibernate.cfg.xml</literal>ä¸çæåä¸ä¸ªå
ç´ å£°æäº<literal>Cat.hbm.xml</literal>ï¼è¿æ¯ä¸ä¸ªHibernate XMLæ å°æ件ï¼å¯¹åºäºæä¹
åç±»<literal>Cat</literal>ãè¿ä¸ªæ件å
å«äºæ<literal>Cat</literal> POJOç±»æ å°å°æ°æ®åºè¡¨ï¼æå¤ä¸ªæ°æ®åºè¡¨ï¼çå
æ°æ®ãæ们ç¨åå°±åæ¥çè¿ä¸ªæ件ãä¸ä¸æ¥è®©æ们å
ç¼åè¿ä¸ªPOJOç±»ï¼ç¶åå¨å£°æå®çæ å°å
æ°æ®ã
+ </para>
+
+ </sect1>
+
+ <sect1 id="quickstart-persistentclass" revision="1">
+ <title>第ä¸ä¸ªæä¹
åç±»</title>
+
+ <para>
+ Hibernate使ç¨ç®åçJava对象(Plain Old Java Objects ,å°±æ¯POJOs,ææ¶åä¹ç§°ä½Plain Ordinary Java Objectsï¼è¿ç§ç¼ç¨æ¨¡åæ¥è¿è¡æä¹
åãä¸ä¸ªPOJOå¾åJavaBean,éè¿getteråsetteræ¹æ³è®¿é®å
¶å±æ§ï¼å¯¹å¤åéèäºå
é¨å®ç°çç»è(åè¥éè¦çè¯,Hibernateä¹å¯ä»¥ç´æ¥è®¿é®å
¶å±æ§å段)ã
+ </para>
+
+ <programlisting><![CDATA[package org.hibernate.examples.quickstart;
+
+public class Cat {
+
+ private String id;
+ private String name;
+ private char sex;
+ private float weight;
+
+ public Cat() {
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ private void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public char getSex() {
+ return sex;
+ }
+
+ public void setSex(char sex) {
+ this.sex = sex;
+ }
+
+ public float getWeight() {
+ return weight;
+ }
+
+ public void setWeight(float weight) {
+ this.weight = weight;
+ }
+
+}]]></programlisting>
+
+ <para>
+ Hibernate对å±æ§ä½¿ç¨çç±»åä¸å ä»»ä½éå¶ãææçJava JDKç±»åååå§ç±»åï¼æ¯å¦<literal>String</literal>,<literal>char</literal>å<literal>Date</literal>)é½å¯ä»¥è¢«æ å°ï¼ä¹å
æ¬Java éåï¼Java collections framework)ä¸çç±»ãä½ å¯ä»¥æå®ä»¬æ å°æ为å¼ï¼å¼éåï¼æè
ä¸å
¶ä»å®ä½ç±»ç¸å
³èã<literal>id</literal>æ¯ä¸ä¸ªç¹æ®çå±æ§ï¼ä»£è¡¨äºè¿ä¸ªç±»çæ°æ®åºæ è¯ç¬¦(主é®)ï¼å¯¹äºç±»ä¼¼äº<literal>Cat</literal>è¿æ ·çå®ä½ç±»æ们强ç建议使ç¨ãHibernateä¹å¯ä»¥ä½¿ç¨å
é¨æ è¯ç¬¦ï¼ä½è¿æ ·æ们ä¼å¤±å»ä¸äºç¨åºæ¶ææ¹é¢ççµæ´»æ§ã
+ </para>
+
+ <para>
+ æä¹
åç±»ä¸éè¦å®ç°ä»ä¹ç¹å«çæ¥å£ï¼ä¹ä¸éè¦ä»ä¸ä¸ªç¹å«çæä¹
åæ ¹ç±»ç»§æ¿ä¸æ¥ãHibernateä¹ä¸éè¦ä½¿ç¨ä»»ä½ç¼è¯æå¤çï¼æ¯å¦åèç å¢å¼ºæä½ï¼å®ç¬ç«ç使ç¨Javaåå°æºå¶åè¿è¡æ¶ç±»å¢å¼ºï¼éè¿CGLIBï¼ãæ以ä¸ä¾èµäºHibernateï¼æ们就å¯ä»¥æPOJOçç±»æ å°æ为æ°æ®åºè¡¨ã
+ </para>
+
+ </sect1>
+
+ <sect1 id="quickstart-mapping" revision="1">
+ <title>æ å°cat</title>
+
+ <para>
+ <literal>Cat.hbm.xml</literal>æ å°æ件å
å«äºå¯¹è±¡/å
³ç³»æ å°ï¼O/R Mappingï¼æéçå
æ°æ®ãå
æ°æ®å
å«æä¹
åç±»ç声æåå±æ§å°æ°æ®åºçæ å°ï¼æåå段åå
¶ä»å®ä½çå¤é®å
³èï¼ã
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping
+ PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping>
+
+ <class name="org.hibernate.examples.quickstart.Cat" table="CAT">
+
+ <!-- A 32 hex character is our surrogate key. It's automatically
+ generated by Hibernate with the UUID pattern. -->
+ <id name="id" type="string" unsaved-value="null" >
+ <column name="CAT_ID" sql-type="char(32)" not-null="true"/>
+ <generator class="uuid.hex"/>
+ </id>
+
+ <!-- A cat has to have a name, but it shouldn' be too long. -->
+ <property name="name">
+ <column name="NAME" length="16" not-null="true"/>
+ </property>
+
+ <property name="sex"/>
+
+ <property name="weight"/>
+
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ æ¯ä¸ªæä¹
åç±»é½åºè¯¥æä¸ä¸ªæ è¯å±æ§ï¼å®é
ä¸ï¼è¿ä¸ªç±»åªä»£è¡¨å®ä½ï¼èä¸æ¯ç¬ç«çå¼ç±»åç±»ï¼åè
ä¼è¢«æ å°ç§°ä¸ºå®ä½å¯¹è±¡ä¸çä¸ä¸ªç»ä»¶ï¼ãè¿ä¸ªå±æ§ç¨æ¥åºåæä¹
å对象ï¼å¦æ<literal>catA.getId().equals(catB.getId())</literal>ç»ææ¯trueçè¯ï¼è¿ä¸¤ä¸ªCatå°±æ¯ç¸åçãè¿ä¸ªæ¦å¿µç§°ä¸º<emphasis>æ°æ®åºæ è¯</emphasis>ãHiernateé带äºå ç§ä¸åçæ è¯ç¬¦çæå¨ï¼ç¨äºä¸åçåºåï¼å
æ¬æ°æ®åºæ¬å°ç顺åº(sequence)çæå¨ãhi/loé«ä½ä½æ è¯æ¨¡å¼ãåç¨åºèªå·±å®ä¹çæ è¯ç¬¦ï¼ãæ们å¨è¿é使ç¨UUIDçæå¨ï¼åªå¨æµè¯æ¶å»ºè®®ä½¿ç¨ï¼å¦æ使ç¨æ°æ®åºèªå·±çæçæ´æ°ç±»åçé®å¼æ´å¥½ï¼ï¼å¹¶æå®<literal>CAT</literal>表ä¸ç<literal>CAT_ID</literal>å段ï¼ä½ä¸ºè¡¨ç主é®ï¼åæ¾çæçæ è¯å¼ã
+ </para>
+
+ <para>
+ <literal>Cat</literal>çå
¶ä»å±æ§é½æ å°å°åä¸ä¸ªè¡¨çå段ã对<literal>name</literal>å±æ§æ¥è¯´ï¼æ们æå®æ¾å¼å°å£°ææ å°å°ä¸ä¸ªæ°æ®åºå段ãå¦ææ°æ®åºschemaæ¯éè¿ç±æ å°å£°æ使ç¨Hibernateç<emphasis>SchemaExport</emphasis>å·¥å
·èªå¨çæçï¼ä½ä¸ºSQL DDLæ令ï¼çè¯ï¼è¿å°±ç¹å«æç¨ãææå
¶å®çå±æ§é½ç¨Hibernateçé»è®¤å¼æ å°ï¼å¤§å¤æ°æ
åµä½ é½ä¼è¿æ ·åãæ°æ®åºä¸ç<literal>CAT</literal>表çèµ·æ¥æ¯è¿æ ·çï¼
+ </para>
+
+ <programlisting><![CDATA[ Column | Type | Modifiers
+--------+-----------------------+-----------
+ cat_id | character(32) | not null
+ name | character varying(16) | not null
+ sex | character(1) |
+ weight | real |
+Indexes: cat_pkey primary key btree (cat_id)]]></programlisting>
+
+ <para>
+ ä½ ç°å¨å¯ä»¥å¨ä½ çæ°æ®åºä¸æå·¥å建è¿ä¸ªè¡¨äºï¼å¦æä½ éè¦ä½¿ç¨<literal>hbm2ddl</literal>å·¥å
·æè¿ä¸ªæ¥éª¤èªå¨åï¼è¯·åé
<xref linkend="toolsetguide"/>ãè¿ä¸ªå·¥å
·è½å¤å建å®æ´çSQL DDLï¼å
æ¬è¡¨å®ä¹ï¼èªå®ä¹çå段类å约æï¼æä¸çº¦æåç´¢å¼ã
+ </para>
+
+ </sect1>
+
+ <sect1 id="quickstart-playingwithcats" revision="2">
+ <title>ä¸Catåä¹</title>
+
+ <para>
+ æ们ç°å¨å¯ä»¥å¼å§Hibernateç<literal>Session</literal>äºãå®æ¯ä¸ä¸ª<emphasis>æä¹
å管çå¨</emphasis>ï¼æ们éè¿å®æ¥ä»æ°æ®åºä¸åå<literal>Cat</literal>ãé¦å
ï¼æ们è¦ä»<literal>SessionFactory</literal>ä¸è·åä¸ä¸ª<literal>Session</literal>ï¼Hibernateçå·¥ä½åå
ï¼ã
+ </para>
+
+ <programlisting><![CDATA[SessionFactory sessionFactory =
+ new Configuration().configure().buildSessionFactory();]]></programlisting>
+
+ <para>
+ éè¿å¯¹<literal>configure()</literal>çè°ç¨æ¥è£
è½½<literal>hibernate.cfg.xml</literal>é
ç½®æ件,并åå§åè¿ä¸ª<literal>Configuration</literal>å®ä¾ã
+ å¨å建 <literal>SessionFactory</literal><emphasis>ä¹å</emphasis>ï¼å®æ¯ä¸å¯åçï¼ï¼ä½ å¯ä»¥è®¿é®<literal>Configuration</literal>æ¥è®¾ç½®å
¶ä»å±æ§ï¼çè³ä¿®æ¹æ å°çå
æ°æ®ï¼ãæ们åºè¯¥å¨åªå¿å建<literal>SessionFactory</literal>ï¼å¨æ们çç¨åºä¸åå¦ä½è®¿é®å®å¢ï¼
+ <literal>SessionFactory</literal>é常åªæ¯è¢«åå§åä¸æ¬¡ï¼æ¯å¦è¯´éè¿ä¸ä¸ª<emphasis>load-on-startup</emphasis> servletçæ¥åå§åãè¿æå³çä½ ä¸åºè¯¥å¨serlvetä¸æå®ä½ä¸ºä¸ä¸ªå®ä¾åéæ¥ææï¼èåºè¯¥æ¾å¨å
¶ä»å°æ¹ãè¿ä¸æ¥ç说ï¼æ们éè¦ä½¿ç¨<emphasis>åä¾ï¼Singletonï¼</emphasis>模å¼ï¼æ们æè½æ´å®¹æçå¨ç¨åºä¸è®¿é®<literal>SessionFactory</literal>ãä¸é¢çæ¹æ³å°±åæ¶è§£å³äºä¸¤ä¸ªé®é¢ï¼å¯¹<literal>SessionFactory</literal>çåå§é
ç½®ä¸ä¾¿æ·ä½¿ç¨ã
+ </para>
+
+ <para>
+ æ们å®ç°ä¸ä¸ª<literal>HibernateUtil</literal>è¾
å©ç±»ï¼
+ </para>
+
+
+ <programlisting><![CDATA[import org.hibernate.*;
+import org.hibernate.cfg.*;
+
+public class HibernateUtil {
+
+ private static Log log = LogFactory.getLog(HibernateUtil.class);
+
+ private static final SessionFactory sessionFactory;
+
+ static {
+ try {
+ // Create the SessionFactory
+ sessionFactory = new Configuration().configure().buildSessionFactory();
+ } catch (Throwable ex) {
+ // Make sure you log the exception, as it might be swallowed
+ log.error("Initial SessionFactory creation failed.", ex);
+ throw new ExceptionInInitializerError(ex);
+ }
+ }
+
+ public static final ThreadLocal session = new ThreadLocal();
+
+ public static Session currentSession() {
+ Session s = (Session) session.get();
+ // Open a new Session, if this Thread has none yet
+ if (s == null) {
+ s = sessionFactory.openSession();
+ session.set(s);
+ }
+ return s;
+ }
+
+ public static void closeSession() {
+ Session s = (Session) session.get();
+ if (s != null)
+ s.close();
+ session.set(null);
+ }
+}]]></programlisting>
+
+ <para>
+ è¿ä¸ªç±»ä¸ä½å¨å®çéæåå§å¨ä¸ä½¿ç¨äº<literal>SessionFactory</literal>ï¼è¿ä½¿ç¨äºä¸ä¸ª<literal>ThreadLocal</literal>åéæ¥ä¿å<literal>Session</literal>å为å½åå·¥ä½çº¿ç¨ãå¨ä½ 使ç¨è¿ä¸ªè¾
å©ç±»ä¹åï¼è¯·ç¡®ä¿ä½ ç解äºthread-localåéè¿ä¸ªJavaæ¦å¿µãä½ å¯ä»¥å¨<literal>CaveatEmptor</literal>(http://caveatemptor.hibernate.org/)ä¸æ¾å°ä¸ä¸ªæ´å å¤æå强大ç <literal>HibernateUtil</literal>ã
+ </para>
+
+ <para>
+ <literal>SessionFactory</literal>æ¯å®å
¨çº¿ç¨ï¼å¯ä»¥ç±å¾å¤çº¿ç¨å¹¶å访é®å¹¶è·åå°<literal>Sessions</literal>ãå个<literal>Session</literal>ä¸æ¯å®å
¨çº¿ç¨å¯¹è±¡ï¼å®åªä»£è¡¨ä¸æ°æ®åºä¹é´çä¸æ¬¡æä½ã<literal>Session</literal>éè¿<literal>SessionFactory</literal>è·å¾å¹¶å¨ææçå·¥ä½å®æåå
³éãå¨ä½ servletç<literal>process()</literal>ä¸å¯ä»¥è±¡æ¯è¿ä¹åç(çç¥äºå¼å¸¸æ
åµå¤ç):
+ </para>
+
+ <programlisting><![CDATA[Session session = HibernateUtil.currentSession();
+
+Transaction tx= session.beginTransaction();
+
+Cat princess = new Cat();
+princess.setName("Princess");
+princess.setSex('F');
+princess.setWeight(7.4f);
+
+session.save(princess);
+tx.commit();
+
+HibernateUtil.closeSession();]]></programlisting>
+
+ <para>
+ å¨ä¸ä¸ª<literal>Session</literal>ä¸ï¼æ¯ä¸ªæ°æ®åºæä½é½æ¯å¨ä¸ä¸ªäºå¡(transaction)ä¸è¿è¡çï¼è¿æ ·å°±å¯ä»¥é离å¼ä¸åçæä½ï¼çè³å
æ¬åªè¯»æä½ï¼ãæ们使ç¨Hibernateç<literal>Transaction</literal> APIæ¥ä»åºå±çäºå¡çç¥ä¸ï¼æ¬ä¾ä¸æ¯JDBCäºå¡ï¼è±èº«åºæ¥ãè¿æ ·ï¼æ们就ä¸éè¦æ´æ¹ä»»ä½æºä»£ç ï¼å°±å¯ä»¥ææ们çç¨åºé¨ç½²å°ä¸ä¸ªç±å®¹å¨ç®¡çäºå¡çç¯å¢ä¸å»ï¼ä½¿ç¨JTAï¼ã
+
+ </para>
+
+ <para>
+ è¿æ ·ä½ å°±å¯ä»¥éå¿æ欲çå¤æ¬¡è°ç¨<literal>HibernateUtil.currentSession();</literal>ï¼ä½ æ¯æ¬¡é½ä¼å¾å°åä¸ä¸ªå½å线ç¨ç<literal>Session</literal>ãä¸ç®¡æ¯å¨ä½ çservlet代ç ä¸ï¼æè
å¨servlet filterä¸è¿æ¯å¨HTTPç»æè¿åä¹åï¼ä½ é½å¿
须确ä¿è¿ä¸ª<literal>Session</literal>å¨ä½ çæ°æ®åºè®¿é®å·¥ä½å®æåå
³éãè¿æ ·åè¿æä¸ä¸ªå¥½å¤å°±æ¯å¯ä»¥å®¹æç使ç¨å»¶è¿è£
è½½ï¼lazy initializationï¼ï¼<literal>Session</literal>å¨æ¸²æviewå±çæ¶åä»ç¶æå¼ççï¼æä»¥ä½ å¨éåå½å对象å¾çæ¶åå¯ä»¥è£
è½½æéç对象ã
+ </para>
+
+ <para>
+ Hibernateæä¸åçæ¹æ³ç¨æ¥ä»æ°æ®åºä¸åå对象ãæçµæ´»çæ¹å¼å°±æ¯ä½¿ç¨Hibernateæ¥è¯¢è¯è¨(HQL),è¿æ¯ä¸ç§å®¹æå¦ä¹ çè¯è¨ï¼æ¯å¯¹SQLçé¢å对象ç强大æ©å±ã
+ </para>
+
+ <programlisting><![CDATA[Transaction tx= session.beginTransaction();
+
+Query query = session.createQuery("select c from Cat as c where c.sex = :sex");
+query.setCharacter("sex", 'F');
+for (Iterator it = query.iterate(); it.hasNext();) {
+ Cat cat = (Cat) it.next();
+ out.println("Female Cat: " + cat.getName() );
+}
+
+tx.commit();]]></programlisting>
+
+ <para>
+ Hibernateä¹æä¾ä¸ç§é¢å对象ç<emphasis>ææ¡ä»¶æ¥è¯¢</emphasis>APIï¼å¯ä»¥æ§è¡ç®æ´å®å
¨ç±»åçæ¥è¯¢ãå½ç¶ï¼Hibernateå¨ææä¸æ°æ®åºç交äºä¸é½ä½¿ç¨<literal>PrepatedStatement</literal>ååæ°ç»å®ãä½ ä¹å¯ä»¥ä½¿ç¨Hibernateçç´æ¥SQLæ¥è¯¢ç¹æ§ï¼æè
å¨ç¹æ®æ
åµä¸ä»<literal>Session</literal>è·åä¸ä¸ªåå§çJDBCè¿æ¥ã
+ </para>
+
+ </sect1>
+
+ <sect1 id="quickstart-summary" revision="1">
+ <title>ç»è¯</title>
+
+ <para>
+ å¨è¿ä¸ªçå°çæç¨ä¸ï¼æ们对Hibernateæµ
å°å³æ¢ã请注ææ们没æå¨ä¾åä¸å
å«ä»»ä½servletç¸å
³ä»£ç ãä½ å¿
é¡»èªè¡ç¼åservletï¼å¹¶æå
¥éåä½ çHibernate代ç ã
+ </para>
+
+ <para>
+ 请记ä½Hibernateä½ä¸ºä¸ä¸ªæ°æ®åºè®¿é®å±ï¼æ¯ä¸ä½ çç¨åºç´§å¯ç¸å
³çãé常æ
åµä¸ï¼ææå
¶ä»å±æ¬¡é½ä¾èµæä¹
æºå¶ãè¯·ç¡®ä¿¡ä½ ç解äºè¿ç§è®¾è®¡çå
涵ã
+ </para>
+
+ <para>
+ è¥å¸æå¦ä¹ æ´å¤æçä¾å,请åé
http://caveatemptor.hibernate.org/ ãå¨ http://www.hibernate.org/Documentation ä¹å¯ä»¥å¾å°å
¶ä»æç¨çé¾æ¥ã
+
+ </para>
+
+ </sect1>
+
+</chapter>
\ No newline at end of file
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/session_api.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/session_api.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/session_api.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1121 @@
+<chapter id="objectstate">
+
+ <title>ä¸å¯¹è±¡å
±äº</title>
+
+ <para>
+ Hibernateæ¯å®æ´ç对象/å
³ç³»æ å°è§£å³æ¹æ¡ï¼å®æä¾äºå¯¹è±¡<emphasis>ç¶æ管ç(state management)</emphasis>çåè½ï¼ä½¿å¼åè
ä¸åéè¦çä¼åºå±æ°æ®åºç³»ç»çç»èã
+ ä¹å°±æ¯è¯´ï¼ç¸å¯¹äºå¸¸è§çJDBC/SQLæä¹
å±æ¹æ¡ä¸éè¦<literal>管çSQLè¯å¥</literal>ï¼Hibernateéç¨äºæ´èªç¶çé¢å对象çè§è§æ¥æä¹
åJavaåºç¨ä¸çæ°æ®ã
+ </para>
+
+
+ <para>
+ æ¢å¥è¯è¯´ï¼ä½¿ç¨Hibernateçå¼åè
åºè¯¥æ»æ¯å
³æ³¨å¯¹è±¡ç<emphasis>ç¶æ(state)</emphasis>ï¼ä¸å¿
èèSQLè¯å¥çæ§è¡ã
+ è¿é¨åç»èå·²ç»ç±Hibernateæ管妥å½ï¼åªæå¼åè
å¨è¿è¡ç³»ç»æ§è½è°ä¼çæ¶åæéè¦è¿è¡äºè§£ã
+ </para>
+
+ <sect1 id="objectstate-overview">
+
+ <title>Hibernate对象ç¶æ(object states)</title>
+
+
+ <para>
+ Hibernateå®ä¹å¹¶æ¯æä¸å对象ç¶æ(state):
+ </para>
+
+ <itemizedlist>
+ <listitem>
+
+ <para>
+ <emphasis>ç¬æ¶(Transient)</emphasis> - ç±<literal>new</literal>æä½ç¬¦å建ï¼ä¸å°æªä¸Hibernate <literal>Session</literal>
+ å
³èç对象被认å®ä¸ºç¬æ¶(Transient)çãç¬æ¶(Transient)对象ä¸ä¼è¢«æä¹
åå°æ°æ®åºä¸ï¼ä¹ä¸ä¼è¢«èµäºæä¹
åæ è¯(identifier)ã
+ å¦æç¬æ¶(Transient)对象å¨ç¨åºä¸æ²¡æ被å¼ç¨ï¼å®ä¼è¢«åå¾åæ¶å¨(garbage collector)éæ¯ã
+ 使ç¨Hibernate <literal>Session</literal>å¯ä»¥å°å
¶å为æä¹
(Persistent)ç¶æã(Hibernateä¼èªå¨æ§è¡å¿
è¦çSQLè¯å¥)
+ </para>
+
+ </listitem>
+ <listitem>
+
+ <para>
+ <emphasis>æä¹
(Persistent)</emphasis> - æä¹
(Persistent)çå®ä¾å¨æ°æ®åºä¸æ对åºçè®°å½ï¼å¹¶æ¥æä¸ä¸ªæä¹
åæ è¯(identifier)ã
+ æä¹
(Persistent)çå®ä¾å¯è½æ¯å被ä¿åçï¼æå被å è½½çï¼æ 论åªä¸ç§ï¼æå®ä¹ï¼å®åå¨äºç¸å
³èç<literal>Session</literal>ä½ç¨èå´å
ã
+ Hibernateä¼æ£æµå°å¤äºæä¹
(Persistent)ç¶æç对象çä»»ä½æ¹å¨ï¼å¨å½åæä½åå
(unit of work)æ§è¡å®æ¯æ¶å°å¯¹è±¡æ°æ®(state)ä¸æ°æ®åºåæ¥(synchronize)ã
+ å¼åè
ä¸éè¦æå¨æ§è¡<literal>UPDATE</literal>ãå°å¯¹è±¡ä»æä¹
(Persistent)ç¶æåæç¬æ¶(Transient)ç¶æåæ ·ä¹ä¸éè¦æå¨æ§è¡<literal>DELETE</literal>è¯å¥ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>è±ç®¡(Detached)</emphasis> - ä¸æä¹
(Persistent)对象å
³èç<literal>Session</literal>被å
³éåï¼å¯¹è±¡å°±å为è±ç®¡(Detached)çã
+ 对è±ç®¡(Detached)对象çå¼ç¨ä¾ç¶ææï¼å¯¹è±¡å¯ç»§ç»è¢«ä¿®æ¹ãè±ç®¡(Detached)对象å¦æéæ°å
³èå°æ个æ°ç<literal>Session</literal>ä¸ï¼
+ ä¼å次转å为æä¹
(Persistent)ç(å¨Detachedå
¶é´çæ¹å¨å°è¢«æä¹
åå°æ°æ®åº)ã
+ è¿ä¸ªåè½ä½¿å¾ä¸ç§ç¼ç¨æ¨¡åï¼å³ä¸é´ä¼ç»ç¨æ·æèæ¶é´(user think-time)çé¿æ¶é´è¿è¡çæä½åå
(unit of work)çç¼ç¨æ¨¡åæ为å¯è½ã
+ æ们称ä¹ä¸º<emphasis>åºç¨ç¨åºäºå¡</emphasis>ï¼å³ä»ç¨æ·è§ç¹çæ¯ä¸ä¸ªæä½åå
(unit of work)ã
+ </para>
+
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ æ¥ä¸æ¥æ们æ¥ç»è´ç讨论ä¸ç¶æ(states)åç¶æé´ç转æ¢(state transitions)ï¼ä»¥å触åç¶æ转æ¢çHibernateæ¹æ³ï¼ã
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-makingpersistent" revision="1">
+ <title>使对象æä¹
å</title>
+
+ <para>
+ Hibernate认为æä¹
åç±»(persistent class)æ°å®ä¾åç对象æ¯<emphasis>ç¬æ¶(Transient)</emphasis>çã
+ æ们å¯éè¿å°ç¬æ¶(Transient)对象ä¸sessionå
³èèæå®å为<emphasis>æä¹
(Persistent)</emphasis>çã
+ </para>
+
+ <programlisting><![CDATA[DomesticCat fritz = new DomesticCat();
+fritz.setColor(Color.GINGER);
+fritz.setSex('M');
+fritz.setName("Fritz");
+Long generatedId = (Long) sess.save(fritz);]]></programlisting>
+
+ <para>
+ å¦æ<literal>Cat</literal>çæä¹
åæ è¯(identifier)æ¯<literal>generated</literal>ç±»åçï¼
+ é£ä¹è¯¥æ è¯(identifier)ä¼èªå¨å¨<literal>save()</literal>被è°ç¨æ¶äº§ç并åé
ç»<literal>cat</literal>ã
+ å¦æ<literal>Cat</literal>çæä¹
åæ è¯(identifier)æ¯<literal>assigned</literal>ç±»åçï¼ææ¯ä¸ä¸ªå¤å主é®(composite key)ï¼
+ é£ä¹è¯¥æ è¯(identifier)åºå½å¨è°ç¨<literal>save()</literal>ä¹åæå¨èµäºç»<literal>cat</literal>ã
+ ä½ ä¹å¯ä»¥æç
§EJB3 early draftä¸å®ä¹çè¯ä¹ï¼ä½¿ç¨<literal>persist()</literal>æ¿ä»£<literal>save()</literal>ã
+ </para>
+
+
+ <para>
+ æ¤å¤ï¼ä½ å¯ä»¥ç¨ä¸ä¸ªéè½½çæ¬ç<literal>save()</literal>æ¹æ³ã
+ </para>
+
+<programlisting><![CDATA[DomesticCat pk = new DomesticCat();
+pk.setColor(Color.TABBY);
+pk.setSex('F');
+pk.setName("PK");
+pk.setKittens( new HashSet() );
+pk.addKitten(fritz);
+sess.save( pk, new Long(1234) );]]></programlisting>
+
+
+ <para>
+ å¦æä½ æä¹
åç对象æå
³èç对象(associated objects)ï¼ä¾å¦ä¸ä¾ä¸ç<literal>kittens</literal>éåï¼
+ é£ä¹å¯¹è¿äºå¯¹è±¡ï¼è¯æ³¨ï¼pkåkittensï¼è¿è¡æä¹
åç顺åºæ¯ä»»æçï¼ä¹å°±æ¯è¯´å¯ä»¥å
对kittensè¿è¡æä¹
åä¹å¯ä»¥å
对pkè¿è¡æä¹
åï¼ï¼
+ é¤éä½ å¨å¤é®åä¸æ<literal>NOT NULL</literal>约æã
+ Hibernateä¸ä¼è¿åå¤é®çº¦æï¼ä½æ¯å¦æä½ ç¨é误ç顺åºæä¹
å对象ï¼è¯æ³¨ï¼å¨pkæä¹
åä¹åæä¹
åkittenï¼ï¼é£ä¹å¯è½ä¼è¿å<literal>NOT NULL</literal>约æã
+ </para>
+
+ <para>
+ éå¸¸ä½ ä¸ä¼ä¸ºè¿äºç»èç¦å¿ï¼å ä¸ºä½ å¾å¯è½ä¼ä½¿ç¨Hibernateç
+ <emphasis>ä¼ ææ§æä¹
å(transitive persistence)</emphasis>åè½èªå¨ä¿åç¸å
³èé£äºå¯¹è±¡ã
+ è¿æ ·è¿è¿å<literal>NOT NULL</literal>约æçæ
åµé½ä¸ä¼åºç°äº - Hibernateä¼ç®¡å¥½ææçäºæ
ã
+ ä¼ ææ§æä¹
å(transitive persistence)å°å¨æ¬ç« ç¨å讨论ã
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-loading">
+ <title>è£
载对象</title>
+ <para>
+ å¦æä½ ç¥éæ个å®ä¾çæä¹
åæ è¯(identifier)ï¼ä½ å°±å¯ä»¥ä½¿ç¨<literal>Session</literal>ç<literal>load()</literal>æ¹æ³
+ æ¥è·åå®ã <literal>load()</literal>çå¦ä¸ä¸ªåæ°æ¯æå®ç±»ç.class对象ã
+ æ¬æ¹æ³ä¼å建æå®ç±»çæä¹
åå®ä¾ï¼å¹¶ä»æ°æ®åºå è½½å
¶æ°æ®(state)ã
+ </para>
+
+ <programlisting><![CDATA[Cat fritz = (Cat) sess.load(Cat.class, generatedId);]]></programlisting>
+
+<programlisting><![CDATA[// you need to wrap primitive identifiers
+long id = 1234;
+DomesticCat pk = (DomesticCat) sess.load( DomesticCat.class, new Long(id) );]]></programlisting>
+
+ <para>
+ æ¤å¤, ä½ å¯ä»¥ææ°æ®(state)å è½½å°æå®ç对象å®ä¾ä¸ï¼è¦çæ该å®ä¾åæ¥çæ°æ®ï¼ã
+ </para>
+
+<programlisting><![CDATA[Cat cat = new DomesticCat();
+// load pk's state into cat
+sess.load( cat, new Long(pkId) );
+Set kittens = cat.getKittens();]]></programlisting>
+
+
+ <para>
+ 请注æå¦æ没æå¹é
çæ°æ®åºè®°å½ï¼<literal>load()</literal>æ¹æ³å¯è½æåºæ æ³æ¢å¤çå¼å¸¸(unrecoverable exception)ã
+ å¦æç±»çæ å°ä½¿ç¨äºä»£ç(proxy)ï¼<literal>load()</literal>æ¹æ³ä¼è¿åä¸ä¸ªæªåå§åç代çï¼ç´å°ä½ è°ç¨è¯¥ä»£ççææ¹æ³æ¶æä¼å»è®¿é®æ°æ®åºã
+ è¥ä½ å¸æå¨æ对象ä¸å建ä¸ä¸ªæåå¦ä¸ä¸ªå¯¹è±¡çå
³èï¼åä¸æ³å¨ä»æ°æ®åºä¸è£
载该对象æ¶åæ¶è£
è½½ç¸å
³èçé£ä¸ªå¯¹è±¡ï¼é£ä¹è¿ç§æä½æ¹å¼å°±ç¨å¾ä¸çäºã
+ å¦æ为ç¸åºç±»æ å°å
³ç³»è®¾ç½®äº<literal>batch-size</literal>ï¼
+ é£ä¹ä½¿ç¨è¿ç§æä½æ¹å¼å
许å¤ä¸ªå¯¹è±¡è¢«ä¸æ¹è£
è½½ï¼å 为è¿åçæ¯ä»£çï¼æ éä»æ°æ®åºä¸æåææ对象çæ°æ®ï¼ã
+ </para>
+
+
+ <para>
+ å¦æä½ ä¸ç¡®å®æ¯å¦æå¹é
çè¡åå¨ï¼åºè¯¥ä½¿ç¨<literal>get()</literal>æ¹æ³ï¼å®ä¼ç«å»è®¿é®æ°æ®åºï¼å¦æ没æ对åºçè®°å½ï¼ä¼è¿ånullã
+ </para>
+
+ <programlisting><![CDATA[Cat cat = (Cat) sess.get(Cat.class, id);
+if (cat==null) {
+ cat = new Cat();
+ sess.save(cat, id);
+}
+return cat;]]></programlisting>
+
+ <para>
+ ä½ çè³å¯ä»¥éç¨æ个<literal>LockMode</literal>ï¼ç¨SQLç<literal>SELECT ... FOR UPDATE</literal>è£
载对象ã
+ 请æ¥é
APIæ档以è·åæ´å¤ä¿¡æ¯ã
+ </para>
+
+ <programlisting><![CDATA[Cat cat = (Cat) sess.get(Cat.class, id, LockMode.UPGRADE);]]></programlisting>
+
+
+ <para>
+ 注æï¼ä»»ä½å
³èç对象æè
å
å«çéåé½<emphasis>ä¸ä¼</emphasis>被以<literal>FOR UPDATE</literal>æ¹å¼è¿åï¼
+ é¤éä½ æå®äº<literal>lock</literal>æè
<literal>all</literal>ä½ä¸ºå
³è(association)ç级èé£æ ¼(cascade style)ã
+ </para>
+
+
+ <para>
+ ä»»ä½æ¶åé½å¯ä»¥ä½¿ç¨<literal>refresh()</literal>æ¹æ³å¼ºè¿«è£
载对象åå®çéåãå¦æä½ ä½¿ç¨æ°æ®åºè§¦åå¨åè½æ¥å¤ç对象çæäºå±æ§ï¼è¿ä¸ªæ¹æ³å°±å¾æç¨äºã
+ </para>
+
+ <programlisting><![CDATA[sess.save(cat);
+sess.flush(); //force the SQL INSERT
+sess.refresh(cat); //re-read the state (after the trigger executes)]]></programlisting>
+
+
+ <para>
+ æ¤å¤é常ä¼åºç°ä¸ä¸ªéè¦é®é¢: Hibernateä¼ä»æ°æ®åºä¸è£
è½½å¤å°ä¸è¥¿ï¼ä¼æ§è¡å¤å°æ¡ç¸åºçSQL<literal>SELECT</literal>è¯å¥ï¼
+ è¿åå³äº<emphasis>æåçç¥(fetching strategy)</emphasis>ï¼ä¼å¨<xref linkend="performance-fetching"/>ä¸è§£éã
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-querying" revision="1">
+ <title>æ¥è¯¢</title>
+ <para>
+ å¦æä¸ç¥éæè¦å¯»æ¾ç对象çæä¹
åæ è¯ï¼é£ä¹ä½ éè¦ä½¿ç¨æ¥è¯¢ãHibernateæ¯æ强大ä¸æäºä½¿ç¨çé¢å对象æ¥è¯¢è¯è¨(HQL)ã
+ å¦æå¸æéè¿ç¼ç¨çæ¹å¼å建æ¥è¯¢ï¼Hibernateæä¾äºå®åçææ¡ä»¶(Query By Criteria, QBC)以åææ ·ä¾(Query By Example, QBE)è¿è¡æ¥è¯¢çåè½ã
+ ä½ ä¹å¯ä»¥ç¨åçSQL(native SQL)æè¿°æ¥è¯¢ï¼Hibernateé¢å¤æä¾äºå°ç»æé(result set)转å为对象çæ¯æã
+ </para>
+
+ <sect2 id="objectstate-querying-executing" revision="1">
+ <title>æ§è¡æ¥è¯¢</title>
+
+ <para>
+ HQLååçSQL(native SQL)æ¥è¯¢è¦éè¿ä¸º<literal>org.hibernate.Query</literal>çå®ä¾æ¥è¡¨è¾¾ã
+ è¿ä¸ªæ¥å£æä¾äºåæ°ç»å®ãç»æéå¤ç以åè¿è¡å®é
æ¥è¯¢çæ¹æ³ã
+ ä½ æ»æ¯å¯ä»¥éè¿å½å<literal>Session</literal>è·åä¸ä¸ª<literal>Query</literal>对象ï¼
+ </para>
+
+ <programlisting><![CDATA[List cats = session.createQuery(
+ "from Cat as cat where cat.birthdate < ?")
+ .setDate(0, date)
+ .list();
+
+List mothers = session.createQuery(
+ "select mother from Cat as cat join cat.mother as mother where cat.name = ?")
+ .setString(0, name)
+ .list();
+
+List kittens = session.createQuery(
+ "from Cat as cat where cat.mother = ?")
+ .setEntity(0, pk)
+ .list();
+
+Cat mother = (Cat) session.createQuery(
+ "select cat.mother from Cat as cat where cat = ?")
+ .setEntity(0, izi)
+ .uniqueResult();]]
+
+Query mothersWithKittens = (Cat) session.createQuery(
+ "select mother from Cat as mother left join fetch mother.kittens");
+Set uniqueMothers = new HashSet(mothersWithKittens.list());]]></programlisting>
+
+
+ <para>
+ ä¸ä¸ªæ¥è¯¢é常å¨è°ç¨<literal>list()</literal>æ¶è¢«æ§è¡ï¼æ§è¡ç»æä¼å®å
¨è£
è½½è¿å
åä¸çä¸ä¸ªéå(collection)ã
+ æ¥è¯¢è¿åç对象å¤äºæä¹
(persistent)ç¶æãå¦æä½ ç¥éçæ¥è¯¢åªä¼è¿åä¸ä¸ªå¯¹è±¡ï¼å¯ä½¿ç¨<literal>list()</literal>çå¿«æ·æ¹å¼<literal>uniqueResult()</literal>ã
+ 注æï¼ä½¿ç¨éåé¢å
æåçæ¥è¯¢å¾å¾ä¼è¿åå¤æ¬¡æ ¹å¯¹è±¡ï¼ä»ä»¬çéåç±»é½è¢«åå§åäºï¼ãä½ å¯ä»¥éè¿ä¸ä¸ªéåæ¥è¿æ»¤è¿äºéå¤å¯¹è±¡ã
+ </para>
+
+ <sect3 id="objectstate-querying-executing-iterate">
+
+ <title>è¿ä»£å¼è·åç»æ(Iterating results)</title>
+
+ <para>
+ æäºæ
åµä¸ï¼ä½ å¯ä»¥ä½¿ç¨<literal>iterate()</literal>æ¹æ³å¾å°æ´å¥½çæ§è½ã
+ è¿é常æ¯ä½ é¢æè¿åçç»æå¨sessionï¼æäºçº§ç¼å(second-level cache)ä¸å·²ç»åå¨æ¶çæ
åµã
+ å¦è¥ä¸ç¶ï¼<literal>iterate()</literal>ä¼æ¯<literal>list()</literal>æ
¢ï¼èä¸å¯è½ç®åæ¥è¯¢ä¹éè¦è¿è¡å¤æ¬¡æ°æ®åºè®¿é®ï¼
+ <literal>iterate()</literal>ä¼é¦å
使ç¨<emphasis>1</emphasis>æ¡è¯å¥å¾å°ææ对象çæä¹
åæ è¯(identifiers)ï¼åæ ¹æ®æä¹
åæ è¯æ§è¡<emphasis>n</emphasis>æ¡éå çselectè¯å¥å®ä¾åå®é
ç对象ã
+ </para>
+
+ <programlisting><![CDATA[// fetch ids
+Iterator iter = sess.createQuery("from eg.Qux q order by q.likeliness").iterate();
+while ( iter.hasNext() ) {
+ Qux qux = (Qux) iter.next(); // fetch the object
+ // something we couldnt express in the query
+ if ( qux.calculateComplicatedAlgorithm() ) {
+ // delete the current instance
+ iter.remove();
+ // dont need to process the rest
+ break;
+ }
+}]]></programlisting>
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-tuples">
+ <title>è¿åå
ç»(tuples)çæ¥è¯¢</title>
+
+ <para>
+ ï¼è¯æ³¨ï¼å
ç»(tuples)æä¸æ¡ç»æè¡å
å«å¤ä¸ªå¯¹è±¡ï¼
+ Hibernateæ¥è¯¢ææ¶è¿åå
ç»(tuples)ï¼æ¯ä¸ªå
ç»(tuples)以æ°ç»çå½¢å¼è¿å:
+ </para>
+
+ <programlisting><![CDATA[Iterator kittensAndMothers = sess.createQuery(
+ "select kitten, mother from Cat kitten join kitten.mother mother")
+ .list()
+ .iterator();
+
+while ( kittensAndMothers.hasNext() ) {
+ Object[] tuple = (Object[]) kittensAndMothers.next();
+ Cat kitten = tuple[0];
+ Cat mother = tuple[1];
+ ....
+}]]></programlisting>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-scalar" revision="1">
+ <title>æ é(Scalar)ç»æ</title>
+
+ <para>
+ æ¥è¯¢å¯å¨<literal>select</literal>ä»å¥ä¸æå®ç±»çå±æ§ï¼çè³å¯ä»¥è°ç¨SQLç»è®¡(aggregate)å½æ°ã
+ å±æ§æç»è®¡ç»æ被认å®ä¸º"æ é(Scalar)"çç»æï¼èä¸æ¯æä¹
(persistent state)çå®ä½ï¼ã
+ </para>
+
+ <programlisting><![CDATA[Iterator results = sess.createQuery(
+ "select cat.color, min(cat.birthdate), count(cat) from Cat cat " +
+ "group by cat.color")
+ .list()
+ .iterator();
+
+while ( results.hasNext() ) {
+ Object[] row = (Object[]) results.next();
+ Color type = (Color) row[0];
+ Date oldest = (Date) row[1];
+ Integer count = (Integer) row[2];
+ .....
+}]]></programlisting>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-parameters">
+ <title>ç»å®åæ°</title>
+
+ <para>
+ æ¥å£<literal>Query</literal>æä¾äºå¯¹å½ååæ°(named parameters)ãJDBCé£æ ¼ç<literal>é®å·(?)</literal>åæ°è¿è¡ç»å®çæ¹æ³ã
+ <emphasis>ä¸åäºJDBCï¼Hibernate对åæ°ä»0å¼å§è®¡æ°ã</emphasis>
+ å½ååæ°(named parameters)å¨æ¥è¯¢å符串ä¸æ¯å½¢å¦<literal>:name</literal>çæ è¯ç¬¦ã
+ å½ååæ°(named parameters)çä¼ç¹æ¯:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ å½ååæ°(named parameters)ä¸å
¶å¨æ¥è¯¢ä¸²ä¸åºç°ç顺åºæ å
³
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ å®ä»¬å¯å¨åä¸æ¥è¯¢ä¸²ä¸å¤æ¬¡åºç°
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ å®ä»¬æ¬èº«æ¯èªæ说æç
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <programlisting><![CDATA[//named parameter (preferred)
+Query q = sess.createQuery("from DomesticCat cat where cat.name = :name");
+q.setString("name", "Fritz");
+Iterator cats = q.iterate();]]></programlisting>
+
+ <programlisting><![CDATA[//positional parameter
+Query q = sess.createQuery("from DomesticCat cat where cat.name = ?");
+q.setString(0, "Izi");
+Iterator cats = q.iterate();]]></programlisting>
+
+ <programlisting><![CDATA[//named parameter list
+List names = new ArrayList();
+names.add("Izi");
+names.add("Fritz");
+Query q = sess.createQuery("from DomesticCat cat where cat.name in (:namesList)");
+q.setParameterList("namesList", names);
+List cats = q.list();]]></programlisting>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-pagination">
+ <title>å页</title>
+
+ <para>
+ å¦æä½ éè¦æå®ç»æéçèå´ï¼å¸æè¿åçæ大è¡æ°/æå¼å§çè¡æ°ï¼ï¼åºè¯¥ä½¿ç¨<literal>Query</literal>æ¥å£æä¾çæ¹æ³:
+ </para>
+
+ <programlisting><![CDATA[Query q = sess.createQuery("from DomesticCat cat");
+q.setFirstResult(20);
+q.setMaxResults(10);
+List cats = q.list();]]></programlisting>
+
+ <para>
+ Hibernate ç¥éå¦ä½å°è¿ä¸ªæéå®æ¡ä»¶çæ¥è¯¢è½¬æ¢æä½ çæ°æ®åºçåçSQL(native SQL)ã
+ </para>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-scrolling">
+ <title>å¯æ»å¨éå(Scrollable iteration)</title>
+
+
+ <para>
+ å¦æä½ çJDBC驱å¨æ¯æå¯æ»å¨ç<literal>ResuleSet</literal>ï¼<literal>Query</literal>æ¥å£å¯ä»¥ä½¿ç¨<literal>ScrollableResults</literal>ï¼å
è®¸ä½ å¨æ¥è¯¢ç»æä¸çµæ´»æ¸¸èµ°ã
+ </para>
+
+ <programlisting><![CDATA[Query q = sess.createQuery("select cat.name, cat from DomesticCat cat " +
+ "order by cat.name");
+ScrollableResults cats = q.scroll();
+if ( cats.first() ) {
+
+ // find the first name on each page of an alphabetical list of cats by name
+ firstNamesOfPages = new ArrayList();
+ do {
+ String name = cats.getString(0);
+ firstNamesOfPages.add(name);
+ }
+ while ( cats.scroll(PAGE_SIZE) );
+
+ // Now get the first page of cats
+ pageOfCats = new ArrayList();
+ cats.beforeFirst();
+ int i=0;
+ while( ( PAGE_SIZE > i++ ) && cats.next() ) pageOfCats.add( cats.get(1) );
+
+}
+cats.close()]]></programlisting>
+
+ <para>
+ 请注æï¼ä½¿ç¨æ¤åè½éè¦ä¿ææ°æ®åºè¿æ¥ï¼ä»¥å游æ (cursor)ï¼å¤äºä¸ç´æå¼ç¶æã
+ å¦æä½ éè¦æå¼è¿æ¥ä½¿ç¨å页åè½ï¼è¯·ä½¿ç¨<literal>setMaxResult()</literal>/<literal>setFirstResult()</literal>
+ </para>
+
+ </sect3>
+
+ <sect3 id="objectstate-querying-executing-named" revision="1">
+ <title>å¤ç½®å½åæ¥è¯¢(Externalizing named queries)</title>
+
+ <para>
+ ä½ å¯ä»¥å¨æ å°æ件ä¸å®ä¹å½åæ¥è¯¢(named queries)ã
+ ï¼å¦æä½ çæ¥è¯¢ä¸²ä¸å
å«å¯è½è¢«è§£é为XMLæ è®°(markup)çå符ï¼å«å¿äºç¨<literal>CDATA</literal>å
裹起æ¥ãï¼
+ </para>
+
+ <programlisting><![CDATA[<query name="ByNameAndMaximumWeight"><![CDATA[
+ from eg.DomesticCat as cat
+ where cat.name = ?
+ and cat.weight > ?
+] ]></query>]]></programlisting>
+
+
+ <para>
+ åæ°ç»å®åæ§è¡ä»¥ç¼ç¨æ¹å¼(programatically)å®æï¼
+ </para>
+
+ <programlisting><![CDATA[Query q = sess.getNamedQuery("ByNameAndMaximumWeight");
+q.setString(0, name);
+q.setInt(1, minWeight);
+List cats = q.list();]]></programlisting>
+
+
+ <para>
+ 请注æå®é
çç¨åºä»£ç ä¸æç¨çæ¥è¯¢è¯è¨æ å
³ï¼ä½ ä¹å¯å¨å
æ°æ®ä¸å®ä¹åçSQL(native SQL)æ¥è¯¢ï¼
+ æå°åæçå
¶ä»çæ¥è¯¢è¯å¥æ¾å¨é
ç½®æ件ä¸ï¼è¿æ ·å°±å¯ä»¥è®©Hibernateç»ä¸ç®¡çï¼è¾¾å°è¿ç§»çç®çã
+ </para>
+
+ <para>
+
+ ä¹è¯·æ³¨æå¨<literal><hibernate-mapping></literal>å
ç´ ä¸å£°æçæ¥è¯¢å¿
é¡»æä¸ä¸ªå
¨å±å¯ä¸çåå,èå¨<literal><class></literal>å
ç´ ä¸å£°æçæ¥è¯¢èªå¨å
·æå
¨å±å,æ¯éè¿ç±»çå
¨åå 以éå®çãæ¯å¦<literal>eg.Cat.ByNameAndMaximumWeight</literal>ã
+ </para>
+
+ </sect3>
+
+ </sect2>
+
+ <sect2 id="objectstate-filtering" revision="1">
+ <title>è¿æ»¤éå</title>
+
+
+ <para>
+ éå<emphasis>è¿æ»¤å¨(filter)</emphasis>æ¯ä¸ç§ç¨äºä¸ä¸ªæä¹
åéåæè
æ°ç»çç¹æ®çæ¥è¯¢ãæ¥è¯¢å符串ä¸å¯ä»¥ä½¿ç¨<literal>"this"</literal>æ¥å¼ç¨éåä¸çå½åå
ç´ ã
+ </para>
+
+ <programlisting><![CDATA[Collection blackKittens = session.createFilter(
+ pk.getKittens(),
+ "where this.color = ?")
+ .setParameter( Color.BLACK, Hibernate.custom(ColorUserType.class) )
+ .list()
+);]]></programlisting>
+
+
+ <para>
+ è¿åçéåå¯ä»¥è¢«è®¤ä¸ºæ¯ä¸ä¸ªå
(bag, æ 顺åºå¯éå¤çéå(collection))ï¼å®æ¯æç»éåçå¯æ¬ã
+ åæ¥çéåä¸ä¼è¢«æ¹å¨ï¼è¿ä¸âè¿æ»¤å¨(filter)âçéå«çå«ä¹ä¸ç¬¦ï¼ä¸è¿ä¸æ们æå¾
çè¡ä¸ºä¸è´ï¼ã
+ </para>
+
+ <para>
+ 请注æè¿æ»¤å¨(filter)并ä¸éè¦<literal>from</literal>åå¥ï¼å½ç¶éè¦çè¯å®ä»¬ä¹å¯ä»¥å ä¸ï¼ãè¿æ»¤å¨(filter)ä¸éå®äºåªè½è¿åéåå
ç´ æ¬èº«ã
+ </para>
+
+ <programlisting><![CDATA[Collection blackKittenMates = session.createFilter(
+ pk.getKittens(),
+ "select this.mate where this.color = eg.Color.BLACK.intValue")
+ .list();]]></programlisting>
+
+ <para>
+ å³ä½¿æ æ¡ä»¶çè¿æ»¤å¨(filter)ä¹æ¯ææä¹çãä¾å¦ï¼ç¨äºå è½½ä¸ä¸ªå¤§éåçåéï¼
+ </para>
+
+ <programlisting><![CDATA[Collection tenKittens = session.createFilter(
+ mother.getKittens(), "")
+ .setFirstResult(0).setMaxResults(10)
+ .list();]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="objecstate-querying-criteria" revision="1">
+ <title>æ¡ä»¶æ¥è¯¢(Criteria queries)</title>
+
+ <para>
+ HQLæ为强大ï¼ä½æ¯æäºäººå¸æè½å¤å¨æç使ç¨ä¸ç§é¢å对象APIå建æ¥è¯¢ï¼èéå¨ä»ä»¬çJava代ç ä¸åµå
¥å符串ã对äºé£é¨å人æ¥è¯´ï¼Hibernateæä¾äºç´è§ç<literal>Criteria</literal>æ¥è¯¢APIã
+ </para>
+
+ <programlisting><![CDATA[Criteria crit = session.createCriteria(Cat.class);
+crit.add( Expression.eq( "color", eg.Color.BLACK ) );
+crit.setMaxResults(10);
+List cats = crit.list();]]></programlisting>
+
+
+ <para>
+ <literal>Criteria</literal>以åç¸å
³ç<literal>æ ·ä¾(Example)</literal>APIå°ä¼å<xref linkend="querycriteria"/>ä¸è¯¦ç»è®¨è®ºã
+ </para>
+
+ </sect2>
+
+ <sect2 id="objectstate-querying-nativesql" revision="2">
+ <title>使ç¨åçSQLçæ¥è¯¢</title>
+
+
+ <para>
+ ä½ å¯ä»¥ä½¿ç¨<literal>createSQLQuery()</literal>æ¹æ³ï¼ç¨SQLæ¥æè¿°æ¥è¯¢ï¼å¹¶ç±Hibernateå°ç»æé转æ¢æ对象ã
+ 请注æï¼ä½ å¯ä»¥å¨ä»»ä½æ¶åè°ç¨<literal>session.connection()</literal>æ¥è·å¾å¹¶ä½¿ç¨JDBC <literal>Connection</literal>对象ã
+ å¦æä½ éæ©ä½¿ç¨HibernateçAPI, ä½ å¿
é¡»æSQLå«åç¨å¤§æ¬å·å
å´èµ·æ¥:
+ </para>
+
+ <programlisting><![CDATA[List cats = session.createSQLQuery(
+ "SELECT {cat.*} FROM CAT {cat} WHERE ROWNUM<10",
+ "cat",
+ Cat.class
+).list();]]></programlisting>
+
+ <programlisting><![CDATA[List cats = session.createSQLQuery(
+ "SELECT {cat}.ID AS {cat.id}, {cat}.SEX AS {cat.sex}, " +
+ "{cat}.MATE AS {cat.mate}, {cat}.SUBCLASS AS {cat.class}, ... " +
+ "FROM CAT {cat} WHERE ROWNUM<10",
+ "cat",
+ Cat.class
+).list()]]></programlisting>
+
+
+ <para>
+ åHibernateæ¥è¯¢ä¸æ ·ï¼SQLæ¥è¯¢ä¹å¯ä»¥å
å«å½ååæ°åå ä½åæ°ã
+ å¯ä»¥å¨<xref linkend="querysql"/>æ¾å°æ´å¤å
³äºHibernateä¸åçSQL(native SQL)çä¿¡æ¯ã
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="objectstate-modifying" revision="1">
+ <title>ä¿®æ¹æä¹
对象</title>
+
+ <para>
+ <emphasis>äºå¡ä¸çæä¹
å®ä¾</emphasis>ï¼å°±æ¯éè¿<literal>session</literal>è£
è½½ãä¿åãå建æè
æ¥è¯¢åºç对象ï¼
+ 被åºç¨ç¨åºæä½æé æçä»»ä½ä¿®æ¹é½ä¼å¨<literal>Session</literal>被<emphasis>å·åºï¼flushedï¼</emphasis>çæ¶å被æä¹
åï¼æ¬ç« åé¢ä¼è¯¦ç»è®¨è®ºï¼ã
+ è¿éä¸éè¦è°ç¨æ个ç¹å®çæ¹æ³ï¼æ¯å¦<literal>update()</literal>ï¼è®¾è®¡å®çç®çæ¯ä¸åçï¼å°ä½ çä¿®æ¹æä¹
åã
+ æ以æç´æ¥çæ´æ°ä¸ä¸ªå¯¹è±¡çæ¹æ³å°±æ¯å¨<literal>Session</literal>å¤äºæå¼ç¶ææ¶<literal>load()</literal>å®ï¼ç¶åç´æ¥ä¿®æ¹å³å¯ï¼
+ </para>
+
+ <programlisting><![CDATA[DomesticCat cat = (DomesticCat) sess.load( Cat.class, new Long(69) );
+cat.setName("PK");
+sess.flush(); // changes to cat are automatically detected and persisted]]></programlisting>
+
+ <para>
+ ææ¶è¿ç§ç¨åºæ¨¡åæçä½ä¸ï¼å 为å®å¨åä¸Sessionééè¦ä¸æ¡SQL <literal>SELECT</literal>è¯å¥ï¼ç¨äºå 载对象ï¼
+ 以åä¸æ¡SQL <literal>UPDATE</literal>è¯å¥(æä¹
åæ´æ°çç¶æ)ã
+ 为æ¤Hibernateæä¾äºå¦ä¸ç§éå¾ï¼ä½¿ç¨è±ç®¡(detached)å®ä¾ã
+ </para>
+
+
+ <para>
+ <emphasis>请注æHibernateæ¬èº«ä¸æä¾ç´æ¥æ§è¡<literal>UPDATE</literal>æ<literal>DELETE</literal>è¯å¥çAPIã
+ Hibernateæä¾çæ¯<emphasis>ç¶æ管ç(state management)</emphasis>æå¡ï¼ä½ ä¸å¿
èèè¦ä½¿ç¨ç<emphasis>è¯å¥(statements)</emphasis>ã
+ JDBCæ¯åºè²çæ§è¡SQLè¯å¥çAPIï¼ä»»ä½æ¶åè°ç¨<literal>session.connection()</literal>ä½ é½å¯ä»¥å¾å°ä¸ä¸ªJDBC <literal>Connection</literal>对象ã
+ æ¤å¤ï¼å¨èæºäºå¡å¤ç(OLTP)ç¨åºä¸ï¼å¤§éæä½(mass operations)ä¸å¯¹è±¡/å
³ç³»æ å°çè§ç¹æ¯ç¸å²çªçã
+ Hibernateçå°æ¥çæ¬å¯è½ä¼æä¾ä¸é¨çè¿è¡å¤§éæä½(mass operation)çåè½ã
+ åè<xref linkend="batch"/>ï¼å¯»æ¾ä¸äºå¯ç¨çæ¹é(batch)æä½æå·§ã</emphasis>
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-detached" revision="2">
+ <title>ä¿®æ¹è±ç®¡(Detached)对象</title>
+
+ <para>
+ å¾å¤ç¨åºéè¦å¨æ个äºå¡ä¸è·å对象ï¼ç¶åå°å¯¹è±¡åéå°çé¢å±å»æä½ï¼æåå¨ä¸ä¸ªæ°çäºå¡ä¿åæåçä¿®æ¹ã
+ å¨é«å¹¶å访é®çç¯å¢ä¸ä½¿ç¨è¿ç§æ¹å¼ï¼é常使ç¨é带çæ¬ä¿¡æ¯çæ°æ®æ¥ä¿è¯è¿äºâé¿âå·¥ä½åå
ä¹é´çé离ã
+ </para>
+
+ <para>
+ Hibernateéè¿æä¾<literal>Session.update()</literal>æ<literal>Session.merge()</literal>
+ éæ°å
³èè±ç®¡å®ä¾çåæ³æ¥æ¯æè¿ç§æ¨¡åã
+ </para>
+
+
+ <programlisting><![CDATA[// in the first session
+Cat cat = (Cat) firstSession.load(Cat.class, catId);
+Cat potentialMate = new Cat();
+firstSession.save(potentialMate);
+
+// in a higher layer of the application
+cat.setMate(potentialMate);
+
+// later, in a new session
+secondSession.update(cat); // update cat
+secondSession.update(mate); // update mate]]></programlisting>
+
+ <para>
+ å¦æå
·æ<literal>catId</literal>æä¹
åæ è¯ç<literal>Cat</literal>ä¹åå·²ç»è¢«<literal>å¦ä¸Session(secondSession)</literal>è£
è½½äºï¼
+ åºç¨ç¨åºè¿è¡éå
³èæä½(reattach)çæ¶åä¼æåºä¸ä¸ªå¼å¸¸ã
+ </para>
+
+
+ <para>
+ å¦æä½ ç¡®å®å½åsession没æå
å«ä¸ä¹å
·æç¸åæä¹
åæ è¯çæä¹
å®ä¾ï¼ä½¿ç¨<literal>update()</literal>ã
+ å¦ææ³éæ¶åå¹¶ä½ ççæ¹å¨èä¸èèsessionçç¶æï¼ä½¿ç¨<literal>merge()</literal>ã
+ æ¢å¥è¯è¯´ï¼å¨ä¸ä¸ªæ°sessionä¸é常第ä¸ä¸ªè°ç¨çæ¯<literal>update()</literal>æ¹æ³ï¼ä»¥ä¾¿ä¿è¯éæ°å
³èè±ç®¡(detached)对象çæä½é¦å
被æ§è¡ã
+ </para>
+
+
+ <para>
+ å¦æå¸æç¸å
³èçè±ç®¡å¯¹è±¡ï¼éè¿å¼ç¨âå¯å°è¾¾âçè±ç®¡å¯¹è±¡ï¼çæ°æ®ä¹è¦æ´æ°å°æ°æ®åºæ¶ï¼å¹¶ä¸ä¹<emphasis>ä»
ä»
</emphasis>å¨è¿ç§æ
åµï¼ï¼
+ å¯ä»¥å¯¹è¯¥ç¸å
³èçè±ç®¡å¯¹è±¡åç¬è°ç¨<literal>update()</literal>
+ å½ç¶è¿äºå¯ä»¥èªå¨å®æï¼å³éè¿ä½¿ç¨<emphasis>ä¼ ææ§æä¹
å(transitive persistence)</emphasis>ï¼è¯·ç<xref linkend="objectstate-transitive"/>ã
+ </para>
+
+
+ <para>
+ <literal>lock()</literal>æ¹æ³ä¹å
许ç¨åºéæ°å
³èæ个对象å°ä¸ä¸ªæ°sessionä¸ãä¸è¿ï¼è¯¥è±ç®¡(detached)ç对象å¿
é¡»æ¯æ²¡æä¿®æ¹è¿çï¼
+ </para>
+
+ <programlisting><![CDATA[//just reassociate:
+sess.lock(fritz, LockMode.NONE);
+//do a version check, then reassociate:
+sess.lock(izi, LockMode.READ);
+//do a version check, using SELECT ... FOR UPDATE, then reassociate:
+sess.lock(pk, LockMode.UPGRADE);]]></programlisting>
+
+
+ <para>
+ 请注æï¼<literal>lock()</literal>å¯ä»¥æé
å¤ç§<literal>LockMode</literal>ï¼
+ æ´å¤ä¿¡æ¯è¯·é
读APIæ档以åå
³äºäºå¡å¤ç(transaction handling)çç« èãéæ°å
³èä¸æ¯<literal>lock()</literal>çå¯ä¸ç¨éã
+ </para>
+
+
+ <para>
+ å
¶ä»ç¨äºé¿æ¶é´å·¥ä½åå
ç模åä¼å¨<xref linkend="transactions-optimistic"/>ä¸è®¨è®ºã
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-saveorupdate">
+ <title>èªå¨ç¶ææ£æµ</title>
+
+
+ <para>
+ Hibernateçç¨æ·æ¾è¦æ±ä¸ä¸ªæ¢å¯èªå¨åé
æ°æä¹
åæ è¯(identifier)ä¿åç¬æ¶(transient)对象ï¼åå¯æ´æ°/éæ°å
³èè±ç®¡(detached)å®ä¾çéç¨æ¹æ³ã
+ <literal>saveOrUpdate()</literal>æ¹æ³å®ç°äºè¿ä¸ªåè½ã
+ </para>
+
+ <programlisting><![CDATA[// in the first session
+Cat cat = (Cat) firstSession.load(Cat.class, catID);
+
+// in a higher tier of the application
+Cat mate = new Cat();
+cat.setMate(mate);
+
+// later, in a new session
+secondSession.saveOrUpdate(cat); // update existing state (cat has a non-null id)
+secondSession.saveOrUpdate(mate); // save the new instance (mate has a null id)]]></programlisting>
+
+
+ <para>
+ <literal>saveOrUpdate()</literal>ç¨éåè¯ä¹å¯è½ä¼ä½¿æ°ç¨æ·æå°è¿·æã
+ é¦å
ï¼åªè¦ä½ 没æå°è¯å¨æ个sessionä¸ä½¿ç¨æ¥èªå¦ä¸sessionçå®ä¾ï¼ä½ å°±åºè¯¥ä¸éè¦ä½¿ç¨<literal>update()</literal>ï¼
+ <literal>saveOrUpdate()</literal>ï¼æ<literal>merge()</literal>ãæäºç¨åºä»æ¥ä¸ç¨è¿äºæ¹æ³ã
+ </para>
+
+
+ <para>
+ é常ä¸é¢çåºæ¯ä¼ä½¿ç¨<literal>update()</literal>æ<literal>saveOrUpdate()</literal>ï¼
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ ç¨åºå¨ç¬¬ä¸ä¸ªsessionä¸å 载对象
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ è¯¥å¯¹è±¡è¢«ä¼ éå°è¡¨ç°å±
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ 对象åçäºä¸äºæ¹å¨
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ 该对象被è¿åå°ä¸å¡é»è¾å±
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ç¨åºè°ç¨ç¬¬äºä¸ªsessionç<literal>update()</literal>æ¹æ³æä¹
è¿äºæ¹å¨
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ <literal>saveOrUpdate()</literal>åä¸é¢çäº:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ å¦æ对象已ç»å¨æ¬sessionä¸æä¹
åäºï¼ä¸åä»»ä½äº
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ å¦æå¦ä¸ä¸ªä¸æ¬sessionå
³èç对象æ¥æç¸åçæä¹
åæ è¯(identifier)ï¼æåºä¸ä¸ªå¼å¸¸
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ å¦æ对象没ææä¹
åæ è¯(identifier)å±æ§ï¼å¯¹å
¶è°ç¨<literal>save()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ å¦æ对象çæä¹
æ è¯(identifier)表æå
¶æ¯ä¸ä¸ªæ°å®ä¾åç对象ï¼å¯¹å
¶è°ç¨<literal>save()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ å¦æ对象æ¯é带çæ¬ä¿¡æ¯çï¼éè¿<literal><version></literal>æ<literal><timestamp></literal>ï¼
+ 并ä¸çæ¬å±æ§çå¼è¡¨æå
¶æ¯ä¸ä¸ªæ°å®ä¾åç对象ï¼<literal>save()</literal>å®ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ å¦å<literal>update()</literal> è¿ä¸ªå¯¹è±¡
+ </para>
+ </listitem>
+ </itemizedlist>
+
+
+ <para>
+ <literal>merge()</literal>å¯é常ä¸å:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ å¦æsessionä¸åå¨ç¸åæä¹
åæ è¯(identifier)çå®ä¾ï¼ç¨ç¨æ·ç»åºç对象çç¶æè¦çæ§æçæä¹
å®ä¾
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ å¦æsession没æç¸åºçæä¹
å®ä¾ï¼åå°è¯ä»æ°æ®åºä¸å è½½ï¼æå建æ°çæä¹
åå®ä¾
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ æåè¿å该æä¹
å®ä¾
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ç¨æ·ç»åºçè¿ä¸ªå¯¹è±¡æ²¡æ被å
³èå°sessionä¸ï¼å®ä¾æ§æ¯è±ç®¡ç
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect1>
+
+ <sect1 id="objectstate-deleting" revision="1">
+ <title>å é¤æä¹
对象</title>
+
+ <para>
+ 使ç¨<literal>Session.delete()</literal>ä¼æ对象çç¶æä»æ°æ®åºä¸ç§»é¤ã
+ å½ç¶ï¼ä½ çåºç¨ç¨åºå¯è½ä»ç¶ææä¸ä¸ªæåå·²å é¤å¯¹è±¡çå¼ç¨ãæ以ï¼æ好è¿æ ·ç解ï¼<literal>delete()</literal>çç¨éæ¯æä¸ä¸ªæä¹
å®ä¾åæç¬æ¶(transient)å®ä¾ã
+ </para>
+
+ <programlisting><![CDATA[sess.delete(cat);]]></programlisting>
+
+
+ <para>
+ ä½ å¯ä»¥ç¨ä½ å欢çä»»ä½é¡ºåºå é¤å¯¹è±¡ï¼ä¸ç¨æ
å¿å¤é®çº¦æå²çªãå½ç¶ï¼å¦æä½ æéäºé¡ºåºï¼è¿æ¯æå¯è½å¼åå¨å¤é®å段å®ä¹ç<literal>NOT NULL</literal>约æå²çªã
+ ä¾å¦ä½ å é¤äºç¶å¯¹è±¡ï¼ä½æ¯å¿è®°å é¤å©å们ã
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-replicating" revision="1">
+ <title>å¨ä¸¤ä¸ªä¸åæ°æ®åºé´å¤å¶å¯¹è±¡</title>
+
+
+ <para>
+ å¶å°ä¼ç¨å°ä¸éæ°çææä¹
åæ è¯(identifier)ï¼å°æä¹
å®ä¾ä»¥åå
¶å
³èçå®ä¾æä¹
å°ä¸åçæ°æ®åºä¸çæä½ã
+ </para>
+
+ <programlisting><![CDATA[//retrieve a cat from one database
+Session session1 = factory1.openSession();
+Transaction tx1 = session1.beginTransaction();
+Cat cat = session1.get(Cat.class, catId);
+tx1.commit();
+session1.close();
+
+//reconcile with a second database
+Session session2 = factory2.openSession();
+Transaction tx2 = session2.beginTransaction();
+session2.replicate(cat, ReplicationMode.LATEST_VERSION);
+tx2.commit();
+session2.close();]]></programlisting>
+
+
+ <para>
+ <literal>ReplicationMode</literal>å³å®å¨åæ°æ®åºä¸å·²åå¨è®°å½ç±å²çªæ¶ï¼<literal>replicate()</literal>å¦ä½å¤çã
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>ReplicationMode.IGNORE</literal> - 忽ç¥å®
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>ReplicationMode.OVERWRITE</literal> - è¦çç¸åçè¡
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>ReplicationMode.EXCEPTION</literal> - æåºå¼å¸¸
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>ReplicationMode.LATEST_VERSION</literal> - å¦æå½åççæ¬è¾æ°ï¼åè¦çï¼å¦å忽ç¥
+ </para>
+ </listitem>
+ </itemizedlist>
+
+
+ <para>
+ è¿ä¸ªåè½çç¨éå
æ¬ä½¿å½å
¥çæ°æ®å¨ä¸åæ°æ®åºä¸ä¸è´ï¼äº§åå级æ¶å级系ç»é
置信æ¯ï¼åæ»non-ACIDäºå¡ä¸çä¿®æ¹ççã
+ ï¼è¯æ³¨ï¼non-ACIDï¼éACID;ACIDï¼Atomicï¼Consistentï¼Isolated and Durableç缩åï¼
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-flushing">
+ <title>Sessionå·åº(flush)</title>
+
+
+ <para>
+ æ¯é´éä¸æ®µæ¶é´ï¼<literal>Session</literal>ä¼æ§è¡ä¸äºå¿
éçSQLè¯å¥æ¥æå
åä¸ç对象çç¶æåæ¥å°JDBCè¿æ¥ä¸ãè¿ä¸ªè¿ç¨è¢«ç§°ä¸º<emphasis>å·åº(flush)</emphasis>ï¼é»è®¤ä¼å¨ä¸é¢çæ¶é´ç¹æ§è¡ï¼
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ å¨æäºæ¥è¯¢æ§è¡ä¹å
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ å¨è°ç¨<literal>org.hibernate.Transaction.commit()</literal>çæ¶å
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ å¨è°ç¨<literal>Session.flush()</literal>çæ¶å
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ æ¶åçSQLè¯å¥ä¼æç
§ä¸é¢ç顺åºååºæ§è¡ï¼
+ </para>
+
+ <orderedlist spacing="compact">
+ <listitem>
+ <para>
+ ææ对å®ä½è¿è¡æå
¥çè¯å¥ï¼å
¶é¡ºåºæç
§å¯¹è±¡æ§è¡<literal>Session.save()</literal>çæ¶é´é¡ºåº
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ææ对å®ä½è¿è¡æ´æ°çè¯å¥
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ææè¿è¡éåå é¤çè¯å¥
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ææ对éåå
ç´ è¿è¡å é¤ï¼æ´æ°æè
æå
¥çè¯å¥
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ææè¿è¡éåæå
¥çè¯å¥
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ææ对å®ä½è¿è¡å é¤çè¯å¥ï¼å
¶é¡ºåºæç
§å¯¹è±¡æ§è¡<literal>Session.delete()</literal>çæ¶é´é¡ºåº
+ </para>
+ </listitem>
+ </orderedlist>
+
+ <para>
+ ï¼æä¸ä¸ªä¾å¤æ¯ï¼å¦æ对象使ç¨<literal>native</literal>æ¹å¼æ¥çæIDï¼æä¹
åæ è¯ï¼çè¯ï¼å®ä»¬ä¸æ§è¡saveå°±ä¼è¢«æå
¥ãï¼
+ </para>
+
+ <para>
+ é¤éä½ æç¡®å°ååºäº<literal>flush()</literal>æ令ï¼å
³äºSession<emphasis>ä½æ¶</emphasis>ä¼æ§è¡è¿äºJDBCè°ç¨æ¯å®å
¨æ æ³ä¿è¯çï¼åªè½ä¿è¯å®ä»¬æ§è¡çåå顺åºã
+ å½ç¶ï¼Hibernateä¿è¯ï¼<literal>Query.list(..)</literal>ç»å¯¹ä¸ä¼è¿åå·²ç»å¤±æçæ°æ®ï¼ä¹ä¸ä¼è¿åé误æ°æ®ã
+ </para>
+
+
+ <para>
+ ä¹å¯ä»¥æ¹åé»è®¤ç设置ï¼æ¥è®©å·åº(flush)æä½åççä¸é£ä¹é¢ç¹ã
+ <literal>FlushMode</literal>ç±»å®ä¹äºä¸ç§ä¸åçæ¹å¼ã
+ ä»
å¨æ交æ¶å·åº(ä»
å½Hibernateç<literal>Transaction</literal> API被使ç¨æ¶ææ)ï¼
+ æç
§åæ说çæ¹å¼å·åºï¼
+ 以åé¤éæ确使ç¨<literal>flush()</literal>å¦åä»ä¸å·åºã
+ æåä¸ç§æ¨¡å¼å¯¹äºé£äºéè¦é¿æ¶é´ä¿æ<literal>Session</literal>为æå¼æè
æ线ç¶æçé¿æ¶é´è¿è¡çå·¥ä½åå
å¾æç¨ã
+ (åè§ <xref linkend="transactions-optimistic-longsession"/>).
+ </para>
+
+ <programlisting><![CDATA[sess = sf.openSession();
+Transaction tx = sess.beginTransaction();
+sess.setFlushMode(FlushMode.COMMIT); // allow queries to return stale state
+
+Cat izi = (Cat) sess.load(Cat.class, id);
+izi.setName(iznizi);
+
+// might return stale data
+sess.find("from Cat as cat left outer join cat.kittens kitten");
+
+// change to izi is not flushed!
+...
+tx.commit(); // flush occurs
+sess.close();]]></programlisting>
+
+
+ <para>
+ å·åº(flush)æé´ï¼å¯è½ä¼æåºå¼å¸¸ãï¼ä¾å¦ä¸ä¸ªDMLæä½è¿åäºçº¦æï¼
+ å¼å¸¸å¤çæ¶åå°å¯¹Hibernateäºå¡æ§è¡ä¸ºçç解ï¼å æ¤æ们å°å¨<xref linkend="transactions"/>ä¸è®¨è®ºã
+ </para>
+
+ </sect1>
+
+ <sect1 id="objectstate-transitive" revision="1">
+ <title>ä¼ ææ§æä¹
å(transitive persistence)</title>
+
+ <para>
+ 对æ¯ä¸ä¸ªå¯¹è±¡é½è¦æ§è¡ä¿åï¼å é¤æéå
³èæä½è®©äººæè§æç¹éº»ç¦ï¼å°¤å
¶æ¯å¨å¤ç许å¤å½¼æ¤å
³èç对象çæ¶åã
+ ä¸ä¸ªå¸¸è§çä¾åæ¯ç¶åå
³ç³»ãèèä¸é¢çä¾å:
+ </para>
+
+
+ <para>
+ å¦æä¸ä¸ªç¶åå
³ç³»ä¸çå对象æ¯å¼ç±»å(value typed)ï¼ä¾å¦ï¼å°åæå符串çéåï¼çï¼ä»ä»¬ççå½å¨æä¼ä¾èµäºç¶å¯¹è±¡ï¼å¯ä»¥äº«åæ¹ä¾¿ç级èæä½(Cascading)ï¼ä¸éè¦é¢å¤çå¨ä½ã
+ ç¶å¯¹è±¡è¢«ä¿åæ¶ï¼è¿äºå¼ç±»å(value typed)å对象ä¹å°è¢«ä¿åï¼ç¶å¯¹è±¡è¢«å é¤æ¶ï¼å对象ä¹å°è¢«å é¤ã
+ è¿å¯¹å°ä¸ä¸ªå对象ä»éåä¸ç§»é¤æ¯åæ ·ææï¼Hibernateä¼æ£æµå°ï¼å¹¶ä¸å 为å¼ç±»å(value typed)ç对象ä¸å¯è½è¢«å
¶ä»å¯¹è±¡å¼ç¨ï¼æ以Hibernateä¼å¨æ°æ®åºä¸å é¤è¿ä¸ªå对象ã
+ </para>
+
+
+ <para>
+ ç°å¨èèåæ ·çåºæ¯ï¼ä¸è¿ç¶å对象é½æ¯å®ä½(entities)ç±»åï¼èéå¼ç±»å(value typed)ï¼ä¾å¦ï¼ç±»å«ä¸ä¸ªä½ï¼ææ¯ç«åå°ç«ï¼ã
+ å®ä½æèªå·±ççå½æï¼å
许å
±äº«å¯¹å
¶çå¼ç¨ï¼å æ¤ä»éåä¸ç§»é¤ä¸ä¸ªå®ä½ï¼ä¸æå³çå®å¯ä»¥è¢«å é¤ï¼ï¼
+ 并ä¸å®ä½å°å
¶ä»å
³èå®ä½ä¹é´é»è®¤æ²¡æ级èæä½ç设置ã
+ Hibernateé»è®¤ä¸å®ç°æè°ç<emphasis>å¯å°è¾¾å³æä¹
åï¼persistence by reachabilityï¼</emphasis>ççç¥ã
+ </para>
+
+ <para>
+ æ¯ä¸ªHibernate sessionçåºæ¬æä½ - å
æ¬ <literal>persist(), merge(),
+ saveOrUpdate(), delete(), lock(), refresh(), evict(), replicate()</literal> - é½æ对åºç级èé£æ ¼(cascade style)ã
+ è¿äºçº§èé£æ ¼(cascade style)é£æ ¼åå«å½å为 <literal>create,
+ merge, save-update, delete, lock, refresh, evict, replicate</literal>ã
+ å¦æä½ å¸æä¸ä¸ªæä½è¢«é¡ºçå
³èå
³ç³»çº§èä¼ æï¼ä½ å¿
é¡»å¨æ å°æ件ä¸æåºè¿ä¸ç¹ãä¾å¦ï¼
+ </para>
+
+ <programlisting><![CDATA[<one-to-one name="person" cascade="persist"/>]]></programlisting>
+
+
+ <para>
+ 级èé£æ ¼(cascade style)æ¯å¯ç»åç:
+ </para>
+
+ <programlisting><![CDATA[<one-to-one name="person" cascade="persist,delete,lock"/>]]></programlisting>
+
+
+ <para>
+ ä½ å¯ä»¥ä½¿ç¨<literal>cascade="all"</literal>æ¥æå®<emphasis>å
¨é¨</emphasis>æä½é½é¡ºçå
³èå
³ç³»çº§è(cascaded)ã
+ é»è®¤å¼æ¯<literal>cascade="none"</literal>ï¼å³ä»»ä½æä½é½ä¸ä¼è¢«çº§è(cascaded)ã
+ </para>
+
+ <para>
+ 注ææä¸ä¸ªç¹æ®ç级èé£æ ¼(cascade style) <literal>delete-orphan</literal>ï¼åªåºç¨äºone-to-manyå
³èï¼è¡¨æ<literal>delete()</literal>æä½
+ åºè¯¥è¢«åºç¨äºææä»å
³èä¸å é¤ç对象ã
+ </para>
+
+
+
+ <para>
+ 建议:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ é常å¨<literal><many-to-one></literal>æ<literal><many-to-many></literal>å
³ç³»ä¸åºç¨çº§è(cascade)没ä»ä¹æä¹ã
+ 级è(cascade)éå¸¸å¨ <literal><one-to-one></literal>å<literal><one-to-many></literal>å
³ç³»ä¸æ¯è¾æç¨ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ å¦æå对象ç寿å½éå®å¨ç¶äº²å¯¹è±¡ç寿å½ä¹å
ï¼å¯éè¿æå®<literal>cascade="all,delete-orphan"</literal>å°å
¶å为<emphasis>èªå¨çå½å¨æ管çç对象(lifecycle object)</emphasis>ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ å
¶ä»æ
åµï¼ä½ å¯æ ¹æ¬ä¸éè¦çº§è(cascade)ãä½æ¯å¦æä½ è®¤ä¸ºä½ ä¼ç»å¸¸å¨æ个äºå¡ä¸åæ¶ç¨å°ç¶å¯¹è±¡ä¸å对象ï¼å¹¶ä¸ä½ å¸æå°æç¹å¿åï¼å¯ä»¥èè使ç¨<literal>cascade="persist,merge,save-update"</literal>ã
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ å¯ä»¥ä½¿ç¨<literal>cascade="all"</literal>å°ä¸ä¸ªå
³èå
³ç³»ï¼æ 论æ¯å¯¹å¼å¯¹è±¡çå
³èï¼æè
对ä¸ä¸ªéåçå
³èï¼æ 记为<emphasis>ç¶/å</emphasis>å
³ç³»çå
³èã
+ è¿æ ·å¯¹ç¶å¯¹è±¡è¿è¡save/update/deleteæä½å°±ä¼å¯¼è´å对象ä¹è¿è¡save/update/deleteæä½ã
+ </para>
+
+ <para>
+ æ¤å¤ï¼ä¸ä¸ªæä¹
çç¶å¯¹è±¡å¯¹å对象çæµ
å¼ç¨(mere reference)ä¼å¯¼è´å对象被åæ¥save/updateã
+ ä¸è¿ï¼è¿ä¸ªéå»(metaphor)ç说æ³å¹¶ä¸å®æ´ãé¤éå
³èæ¯<literal><one-to-many></literal>å
³è并ä¸è¢«æ 记为<literal>cascade="delete-orphan"</literal>ï¼
+ å¦åç¶å¯¹è±¡å¤±å»å¯¹æ个å对象çå¼ç¨<emphasis>ä¸ä¼</emphasis>导è´è¯¥å对象被èªå¨å é¤ã
+ ç¶åå
³ç³»ç级è(cascading)æä½åç¡®è¯ä¹å¦ä¸ï¼
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ å¦æç¶å¯¹è±¡è¢«<literal>persist()</literal>ï¼é£ä¹ææå对象ä¹ä¼è¢«<literal>persist()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ å¦æç¶å¯¹è±¡è¢«<literal>merge()</literal>ï¼é£ä¹ææå对象ä¹ä¼è¢«<literal>merge()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ å¦æç¶å¯¹è±¡è¢«<literal>save()</literal>ï¼<literal>update()</literal>æ
+ <literal>saveOrUpdate()</literal>ï¼é£ä¹ææå对象åä¼è¢«<literal>saveOrUpdate()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ å¦ææ个æä¹
çç¶å¯¹è±¡å¼ç¨äºç¬æ¶(transient)æè
è±ç®¡(detached)çå对象ï¼é£ä¹å对象å°ä¼è¢«<literal>saveOrUpdate()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ å¦æç¶å¯¹è±¡è¢«å é¤ï¼é£ä¹ææå对象ä¹ä¼è¢«<literal>delete()</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ é¤é被æ 记为<literal>cascade="delete-orphan"</literal>ï¼å é¤âå¤å¿â模å¼ï¼æ¤æ¶ä¸è¢«ä»»ä½ä¸ä¸ªç¶å¯¹è±¡å¼ç¨çå对象ä¼è¢«å é¤ï¼ï¼
+ å¦åå对象失æç¶å¯¹è±¡å¯¹å
¶çå¼ç¨æ¶ï¼<emphasis>ä»ä¹äºä¹ä¸ä¼åç</emphasis>ã
+ å¦ææç¹æ®éè¦ï¼åºç¨ç¨åºå¯éè¿æ¾å¼è°ç¨delete()å é¤å对象ã
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+ æåï¼æ³¨ææä½ç级èå¯è½æ¯å¨<emphasis>è°ç¨æ(call time)</emphasis>æè
<emphasis>åå
¥æ(flush time)</emphasis>ä½ç¨å°å¯¹è±¡å¾ä¸çãææçæä½ï¼å¦æå
许ï¼é½å¨æä½è¢«æ§è¡çæ¶å级èå°å¯è§¦åçå
³èå®ä½ä¸ãç¶èï¼<literal>save-upate</literal>å<literal>delete-orphan</literal>æ¯å¨<literal>Session</literal> flushçæ¶åæä½ç¨å°ææå¯è§¦åç被å
³è对象ä¸çã
+ </para>
+
+
+ </sect1>
+
+ <sect1 id="objectstate-metadata">
+ <title>使ç¨å
æ°æ®</title>
+
+ <para>
+ Hibernateä¸æä¸ä¸ªé常丰å¯çå
级å«(meta-level)ç模åï¼å«æææçå®ä½åå¼ç±»åæ°æ®çå
æ°æ®ã
+ ææ¶è¿ä¸ªæ¨¡å对åºç¨ç¨åºæ¬èº«ä¹ä¼é常æç¨ã
+ æ¯å¦è¯´ï¼åºç¨ç¨åºå¯è½å¨å®ç°ä¸ç§âæºè½âç深度æ·è´ç®æ³æ¶ï¼
+ éè¿ä½¿ç¨Hibernateçå
æ°æ®æ¥äºè§£åªäºå¯¹è±¡åºè¯¥è¢«æ·è´ï¼æ¯å¦ï¼å¯åçå¼ç±»åæ°æ®ï¼ï¼
+ é£äºä¸åºè¯¥ï¼ä¸å¯åçå¼ç±»åæ°æ®ï¼ä¹è®¸è¿ææäºè¢«å
³èçå®ä½ï¼ã
+ </para>
+
+
+ <para>
+ Hibernateæä¾äº<literal>ClassMetadata</literal>æ¥å£ï¼<literal>CollectionMetadata</literal>æ¥å£å<literal>Type</literal>å±æ¬¡ä½ç³»æ¥è®¿é®å
æ°æ®ã
+ å¯ä»¥éè¿<literal>SessionFactory</literal>è·åå
æ°æ®æ¥å£çå®ä¾ã
+ </para>
+
+ <programlisting><![CDATA[Cat fritz = ......;
+ClassMetadata catMeta = sessionfactory.getClassMetadata(Cat.class);
+
+Object[] propertyValues = catMeta.getPropertyValues(fritz);
+String[] propertyNames = catMeta.getPropertyNames();
+Type[] propertyTypes = catMeta.getPropertyTypes();
+
+// get a Map of all properties which are not collections or associations
+Map namedValues = new HashMap();
+for ( int i=0; i<propertyNames.length; i++ ) {
+ if ( !propertyTypes[i].isEntityType() && !propertyTypes[i].isCollectionType() ) {
+ namedValues.put( propertyNames[i], propertyValues[i] );
+ }
+}]]></programlisting>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/toolset_guide.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/toolset_guide.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/toolset_guide.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,564 @@
+<chapter id="toolsetguide" revision="2">
+ <title>å·¥å
·ç®±æå</title>
+
+ <para>
+ å¯ä»¥éè¿ä¸ç³»åEclipseæ件ãå½ä»¤è¡å·¥å
·åAntä»»å¡æ¥è¿è¡ä¸Hibernateå
³èç转æ¢ã
+
+ </para>
+
+ <para>
+ é¤äºAntä»»å¡å¤ï¼å½åç<emphasis>Hibernate Tools</emphasis>ä¹å
å«äºEclipse IDEçæ件ï¼ç¨äºä¸ç°åæ°æ®åºçéåå·¥ç¨ã
+ </para>
+
+ <itemizedlist>
+ <listitem><para>
+
+ <emphasis>Mapping Editor:</emphasis> Hibernate XMLæ å°æ件çç¼è¾å¨ï¼æ¯æèªå¨å®æåè¯æ³é«äº®ãå®ä¹æ¯æ对类ååå±æ§/å段åçè¯ä¹èªå¨å®æï¼æ¯é常çXMLç¼è¾å¨æ¹ä¾¿å¾å¤ã
+ </para></listitem>
+ <listitem><para>
+ <emphasis>Console:</emphasis> Consoleæ¯Eclipseçä¸ä¸ªæ°è§å¾ãé¤äºå¯¹ä½ çconsoleé
ç½®çæ ç¶æ¦è§ï¼ä½ è¿å¯ä»¥è·å¾å¯¹ä½ æä¹
åç±»åå
¶å
³èç交äºå¼è§å¾ãConsoleå
è®¸ä½ å¯¹æ°æ®åºæ§è¡HQLæ¥è¯¢ï¼å¹¶ç´æ¥å¨Eclipseä¸æµè§ç»æã
+ </para></listitem>
+ <listitem><para>
+ <emphasis>Development Wizards:</emphasis> å¨Hibernate Eclipse toolsä¸è¿æä¾äºå 个å导ï¼ä½ å¯ä»¥ç¨å导快éçæHibernate é
ç½®æ件ï¼cfg.xmlï¼ï¼ä½ çè³è¿å¯ä»¥åç°åçæ°æ®åºschemaä¸ååå·¥ç¨åºPOJOæºä»£ç ä¸Hibernate æ å°æ件ãååå·¥ç¨æ¯æå¯å®å¶ç模çã
+ </para></listitem>
+ <listitem><para>
+ <emphasis>Ant Tasks:</emphasis>
+ </para></listitem>
+
+ </itemizedlist>
+
+ <para>
+ è¦å¾å°æ´å¤ä¿¡æ¯ï¼è¯·æ¥é
<emphasis>Hibernate Tools</emphasis> å
åå
¶ææ¡£ã
+ </para>
+
+ <para>
+ åæ¶ï¼Hibernate主åè¡å
è¿é带äºä¸ä¸ªéæçå·¥å
·ï¼å®çè³å¯ä»¥å¨Hibernateâå
é¨âå¿«éè¿è¡ï¼<emphasis>SchemaExport</emphasis> ï¼ä¹å°±æ¯ <literal>hbm2ddl</literal>ã
+
+ </para>
+
+ <sect1 id="toolsetguide-s1" revision="2">
+ <title>Schemaèªå¨çæï¼Automatic schema generationï¼</title>
+
+ <para>
+ å¯ä»¥ä»ä½ çæ å°æ件使ç¨ä¸ä¸ªHibernateå·¥å
·çæDDLã çæçschemaå
å«æ对å®ä½åéå类表çå®æ´æ§å¼ç¨çº¦æï¼ä¸»é®åå¤é®ï¼ãæ¶åå°çæ 示符çæå¨æéç表åsequenceä¹ä¼åæ¶çæã
+ </para>
+
+ <para>
+ å¨ä½¿ç¨è¿ä¸ªå·¥å
·çæ¶åï¼ä½ <emphasis>å¿
é¡»</emphasis> éè¿<literal>hibernate.dialet</literal>å±æ§æå®ä¸ä¸ªSQL<literal>æ¹è¨(Dialet)</literal>ï¼å 为DDLæ¯ä¸ä¾åºåé«åº¦ç¸å
³çã
+ </para>
+
+ <para>
+ é¦å
ï¼è¦å®å¶ä½ çæ å°æ件ï¼æ¥æ¹åçæçschemaã
+ </para>
+
+ <sect2 id="toolsetguide-s1-2" revision="3">
+ <title>对schemaå®å¶å(Customizing the schema)</title>
+
+ <para>
+ å¾å¤Hibernateæ å°å
ç´ å®ä¹äºå¯éç<literal>length</literal>ã<literal>precision</literal> æè
<literal>scale</literal>å±æ§ãä½ å¯ä»¥éè¿è¿ä¸ªå±æ§è®¾ç½®å段çé¿åº¦ã精度ãå°æ°ç¹ä½æ°ã
+ </para>
+
+ <programlisting><![CDATA[<property name="zip" length="5"/>]]></programlisting>
+ <programlisting><![CDATA[<property name="balance" precision="12" scale="2"/>]]></programlisting>
+
+ <para>
+ æäºtagè¿æ¥å<literal>not-null</literal>å±æ§ï¼ç¨æ¥å¨è¡¨å段ä¸çæ<literal>NOT NULL</literal>约æï¼å<literal>unique</literal>å±æ§ï¼ç¨æ¥å¨è¡¨å段ä¸çæ<literal>UNIQUE</literal>约æï¼ã
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="bar" column="barId" not-null="true"/>]]></programlisting>
+ <programlisting><![CDATA[<element column="serialNumber" type="long" not-null="true" unique="true"/>]]></programlisting>
+
+ <para>
+ <literal>unique-key</literal>å±æ§å¯ä»¥å¯¹æç»çå段æå®ä¸ä¸ªå¯ä¸é®çº¦æ(unique key constraint)ãç®åï¼<literal>unique-key</literal>å±æ§æå®çå¼å¨çæDDLæ¶<emphasis>并ä¸ä¼</emphasis>被å½ä½è¿ä¸ªçº¦æçååï¼å®ä»¬åªæ¯å¨ç¨æ¥å¨æ å°æ件å
é¨ç¨ä½åºåçã
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="org" column="orgId" unique-key="OrgEmployeeId"/>
+<property name="employeeId" unique-key="OrgEmployee"/>]]></programlisting>
+
+ <para>
+ <literal>index</literal>å±æ§ä¼ç¨å¯¹åºçå段ï¼ä¸ä¸ªæå¤ä¸ªï¼çæä¸ä¸ªindex,å®æåºäºè¿ä¸ªindexçååãå¦æå¤ä¸ªå段对åºçindexååç¸åï¼å°±ä¼çæå
å«è¿äºå段çindexã
+ </para>
+
+ <programlisting><![CDATA[<property name="lastName" index="CustName"/>
+<property name="firstName" index="CustName"/>]]></programlisting>
+
+ <para>
+ <literal>foreign-key</literal>å±æ§å¯ä»¥ç¨æ¥è¦çä»»ä½çæçå¤é®çº¦æçååã
+ </para>
+
+ <programlisting><![CDATA[<many-to-one name="bar" column="barId" foreign-key="FKFooBar"/>]]></programlisting>
+
+ <para>
+ å¾å¤æ å°å
ç´ è¿æ¥å<literal><column></literal>åå
ç´ ãè¿å¨å®ä¹è·¨è¶å¤å段çç±»åæ¶ç¹å«æç¨ã
+ </para>
+
+ <programlisting><![CDATA[<property name="name" type="my.customtypes.Name"/>
+ <column name="last" not-null="true" index="bar_idx" length="30"/>
+ <column name="first" not-null="true" index="bar_idx" length="20"/>
+ <column name="initial"/>
+</property>]]></programlisting>
+
+ <para>
+ <literal>default</literal>å±æ§ä¸ºå段æå®ä¸ä¸ªé»è®¤å¼ (å¨ä¿å被æ å°çç±»çæ°å®ä¾ä¹åï¼ä½ åºè¯¥å°åæ ·çå¼èµäºå¯¹åºçå±æ§)ã
+ </para>
+
+ <programlisting><![CDATA[<property name="credits" type="integer" insert="false">
+ <column name="credits" default="10"/>
+</property>]]></programlisting>
+
+ <programlisting><![CDATA[<version name="version" type="integer" insert="false">
+ <column name="version" default="0"/>
+</property>]]></programlisting>
+
+ <para>
+ <literal>sql-type</literal>å±æ§å
许ç¨æ·è¦çé»è®¤çHibernateç±»åå°SQLæ°æ®ç±»åçæ å°ã
+ </para>
+
+ <programlisting><![CDATA[<property name="balance" type="float">
+ <column name="balance" sql-type="decimal(13,3)"/>
+</property>]]></programlisting>
+
+ <para>
+ <literal>check</literal>å±æ§å
许ç¨æ·æå®ä¸ä¸ªçº¦ææ£æ¥ã
+ </para>
+
+ <programlisting><![CDATA[<property name="foo" type="integer">
+ <column name="foo" check="foo > 10"/>
+</property>]]></programlisting>
+
+ <programlisting><![CDATA[<class name="Foo" table="foos" check="bar < 100.0">
+ ...
+ <property name="bar" type="float"/>
+</class>]]></programlisting>
+
+
+ <table frame="topbot" id="schemattributes-summary" revision="1">
+ <title>Summary</title>
+ <tgroup cols="2">
+ <colspec colwidth="1*"/>
+ <colspec colwidth="2.5*"/>
+ <thead>
+ <row>
+ <entry>å±æ§(Attribute)</entry>
+ <entry>å¼ï¼Valuesï¼</entry>
+ <entry>解éï¼Interpretationï¼</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>length</literal></entry>
+ <entry>æ°å</entry>
+ <entry>å段é¿åº¦</entry>
+
+ </row>
+ <row>
+ <entry><literal>precision</literal></entry>
+ <entry>æ°å</entry>
+ <entry>精度(decimal precision)</entry>
+ </row>
+ <row>
+ <entry><literal>scale</literal></entry>
+ <entry>æ°å</entry>
+ <entry>å°æ°ç¹ä½æ°(decimal scale)</entry>
+ </row>
+ <row>
+ <entry><literal>not-null</literal></entry>
+ <entry><literal>true|false</literal></entry>
+ <entry>ææå段æ¯å¦åºè¯¥æ¯é空ç</entry>
+ </row>
+ <row>
+ <entry><literal>unique</literal></entry>
+ <entry><literal>true|false</literal></entry>
+ <entry>æææ¯å¦è¯¥å段å
·ææä¸çº¦æ</entry>
+ </row>
+ <row>
+ <entry><literal>index</literal></entry>
+ <entry><literal>index_name</literal></entry>
+ <entry>ææä¸ä¸ªï¼å¤å段ï¼çç´¢å¼(index)çåå</entry>
+ </row>
+ <row>
+ <entry><literal>unique-key</literal></entry>
+ <entry><literal>unique_key_name</literal></entry>
+ <entry>ææå¤å段æä¸çº¦æçååï¼åè§ä¸é¢ç说æï¼</entry>
+ </row>
+ <row>
+ <entry><literal>foreign-key</literal></entry>
+ <entry><literal>foreign_key_name</literal></entry>
+ <entry>
+ specifies the name of the foreign key constraint generated
+ for an association, for a <literal><one-to-one></literal>,
+ <literal><many-to-one></literal>, <literal><key></literal>,
+ or <literal><many-to-many></literal> mapping element. Note that
+ <literal>inverse="true"</literal> sides will not be considered
+ by <literal>SchemaExport</literal>.
+ ææä¸ä¸ªå¤é®çååï¼å®æ¯ä¸ºå
³èçæçï¼æè
<literal><one-to-one></literal>ï¼<literal><many-to-one></literal>, <literal><key></literal>, æè
<literal><many-to-many></literal>æ å°å
ç´ ã注æ<literal>inverse="true"</literal>å¨<literal>SchemaExport</literal>æ¶ä¼è¢«å¿½ç¥ã
+
+ </entry>
+ </row>
+ <row>
+ <entry><literal>sql-type</literal></entry>
+ <entry><literal>SQL å段类å</literal></entry>
+ <entry>
+ è¦çé»è®¤çå段类å(åªè½ç¨äº<literal><column></literal>å±æ§ï¼
+ </entry>
+ </row>
+ <row>
+ <entry><literal>default</literal></entry>
+ <entry>SQL表达å¼</entry>
+ <entry>
+ 为å段æå®é»è®¤å¼
+ </entry>
+ </row>
+ <row>
+ <entry><literal>check</literal></entry>
+ <entry>SQL 表达å¼</entry>
+ <entry>
+ 对å段æ表å å
¥SQL约ææ£æ¥
+ </entry>
+ </row>
+
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ <literal><comment></literal>å
ç´ å¯ä»¥è®©ä½ å¨çæçschemaä¸å å
¥æ³¨éã
+ </para>
+
+ <programlisting><![CDATA[<class name="Customer" table="CurCust">
+ <comment>Current customers only</comment>
+ ...
+</class>]]></programlisting>
+
+ <programlisting><![CDATA[<property name="balance">
+ <column name="bal">
+ <comment>Balance in USD</comment>
+ </column>
+</property>]]></programlisting>
+
+ <para>
+ ç»ææ¯å¨çæçDDLä¸å
å«<literal>comment on table</literal> æè
+ <literal>comment on column</literal>è¯å¥(åè¥æ¯æçè¯)ã
+ </para>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-3" revision="2">
+ <title>è¿è¡è¯¥å·¥å
·</title>
+
+ <para>
+ <literal>SchemaExport</literal>å·¥å
·æDDLèæ¬åå°æ åè¾åºï¼åæ¶/æè
æ§è¡DDLè¯å¥ã
+ </para>
+
+ <para>
+ <literal>java -cp </literal><emphasis>hibernate_classpaths</emphasis>
+ <literal>org.hibernate.tool.hbm2ddl.SchemaExport</literal> <emphasis>options mapping_files</emphasis>
+ </para>
+
+ <table frame="topbot">
+ <title><literal>SchemaExport</literal>å½ä»¤è¡é项</title>
+ <tgroup cols="2">
+ <colspec colwidth="1.5*"/>
+ <colspec colwidth="2*"/>
+ <thead>
+ <row>
+ <entry>é项</entry>
+ <entry>说æ</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>--quiet</literal></entry>
+ <entry>ä¸è¦æèæ¬è¾åºå°stdout</entry>
+ </row>
+ <row>
+ <entry><literal>--drop</literal></entry>
+ <entry>åªè¿è¡drop tablesçæ¥éª¤</entry>
+ </row>
+ <row>
+ <entry><literal>--create</literal></entry>
+ <entry>åªå建表</entry>
+ </row>
+ <row>
+ <entry><literal>--text</literal></entry>
+ <entry>ä¸æ§è¡å¨æ°æ®åºä¸è¿è¡çæ¥éª¤</entry>
+ </row>
+ <row>
+ <entry><literal>--output=my_schema.ddl</literal></entry>
+ <entry>æè¾åºçddlèæ¬è¾åºå°ä¸ä¸ªæ件</entry>
+ </row>
+ <row>
+ <entry><literal>--naming=eg.MyNamingStrategy</literal></entry>
+ <entry>éæ©ä¸ä¸ªå½åçç¥(<literal>NamingStrategy</literal>)</entry>
+ </row>
+ <row>
+ <entry><literal>--config=hibernate.cfg.xml</literal></entry>
+ <entry>ä»XMLæ件读å
¥Hibernateé
ç½®</entry>
+ </row>
+ <row>
+ <entry><literal>--properties=hibernate.properties</literal></entry>
+ <entry>ä»æ件读å
¥æ°æ®åºå±æ§</entry>
+ </row>
+ <row>
+ <entry><literal>--format</literal></entry>
+ <entry>æèæ¬ä¸çSQLè¯å¥å¯¹é½åç¾å</entry>
+ </row>
+ <row>
+ <entry><literal>--delimiter=;</literal></entry>
+ <entry>为èæ¬è®¾ç½®è¡ç»æ符</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ ä½ çè³å¯ä»¥å¨ä½ çåºç¨ç¨åºä¸åµå
¥<literal>SchemaExport</literal>å·¥å
·:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = ....;
+new SchemaExport(cfg).create(false, true);]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-4">
+ <title>å±æ§(Properties)</title>
+
+ <para>
+ å¯ä»¥éè¿å¦ä¸æ¹å¼æå®æ°æ®åºå±æ§:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>éè¿<literal>-D</literal><emphasis><property></emphasis>ç³»ç»åæ°</para>
+ </listitem>
+ <listitem>
+ <para>å¨<literal>hibernate.properties</literal>æ件ä¸</para>
+ </listitem>
+ <listitem>
+ <para>ä½äºä¸ä¸ªå
¶å®ååçpropertiesæ件ä¸,ç¶åç¨ <literal>--properties</literal>åæ°æå®</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ æéçåæ°å
æ¬:
+ </para>
+
+ <table frame="topbot">
+ <title>SchemaExport è¿æ¥å±æ§</title>
+ <tgroup cols="2">
+ <colspec colwidth="1.5*"/>
+ <colspec colwidth="2*"/>
+ <thead>
+ <row>
+ <entry>å±æ§å</entry>
+ <entry>说æ</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>hibernate.connection.driver_class</literal></entry>
+ <entry>jdbc driver class</entry>
+ </row>
+ <row>
+ <entry><literal>hibernate.connection.url</literal></entry>
+ <entry>jdbc url</entry>
+ </row>
+ <row>
+ <entry><literal>hibernate.connection.username</literal></entry>
+ <entry>database user</entry>
+ </row>
+ <row>
+ <entry><literal>hibernate.connection.password</literal></entry>
+ <entry>user password</entry>
+ </row>
+ <row>
+ <entry><literal>hibernate.dialect</literal></entry>
+ <entry>æ¹è¨(dialect)</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-5">
+ <title>使ç¨Ant(Using Ant)</title>
+
+ <para>
+ ä½ å¯ä»¥å¨ä½ çAnt buildèæ¬ä¸è°ç¨<literal>SchemaExport</literal>:
+ </para>
+
+ <programlisting><![CDATA[<target name="schemaexport">
+ <taskdef name="schemaexport"
+ classname="org.hibernate.tool.hbm2ddl.SchemaExportTask"
+ classpathref="class.path"/>
+
+ <schemaexport
+ properties="hibernate.properties"
+ quiet="no"
+ text="no"
+ drop="no"
+ delimiter=";"
+ output="schema-export.sql">
+ <fileset dir="src">
+ <include name="**/*.hbm.xml"/>
+ </fileset>
+ </schemaexport>
+</target>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-6" revision="2">
+ <title>对schemaçå¢éæ´æ°(Incremental schema updates)</title>
+
+ <para>
+ <literal>SchemaUpdate</literal>å·¥å
·å¯¹å·²åå¨çschemaéç¨"å¢é"æ¹å¼è¿è¡æ´æ°ã注æ<literal>SchemaUpdate</literal>严éä¾èµäºJDBC metadata API,æ以å®å¹¶é对ææJDBC驱å¨é½ææã
+ </para>
+
+ <para>
+ <literal>java -cp </literal><emphasis>hibernate_classpaths</emphasis>
+ <literal>org.hibernate.tool.hbm2ddl.SchemaUpdate</literal> <emphasis>options mapping_files</emphasis>
+ </para>
+
+ <table frame="topbot">
+ <title><literal>SchemaUpdate</literal>å½ä»¤è¡é项</title>
+ <tgroup cols="2">
+ <colspec colwidth="1.5*"/>
+ <colspec colwidth="2*"/>
+ <thead>
+ <row>
+ <entry>é项</entry>
+ <entry>说æ</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>--quiet</literal></entry>
+ <entry>ä¸è¦æèæ¬è¾åºå°stdout</entry>
+ </row>
+ <row>
+ <entry><literal>--text</literal></entry>
+ <entry>ä¸æèæ¬è¾åºå°æ°æ®åº</entry>
+ </row>
+ <row>
+ <entry><literal>--naming=eg.MyNamingStrategy</literal></entry>
+ <entry>éæ©ä¸ä¸ªå½åçç¥ (<literal>NamingStrategy</literal>)</entry>
+ </row>
+ <row>
+ <entry><literal>--properties=hibernate.properties</literal></entry>
+ <entry>ä»æå®æ件读å
¥æ°æ®åºå±æ§</entry>
+ </row>
+ <row>
+ <entry><literal>--config=hibernate.cfg.xml</literal></entry>
+ <entry>æå®ä¸ä¸ª <literal>.cfg.xml</literal>æ件</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ ä½ å¯ä»¥å¨ä½ çåºç¨ç¨åºä¸åµå
¥<literal>SchemaUpdate</literal>å·¥å
·:
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = ....;
+new SchemaUpdate(cfg).execute(false);]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-7">
+ <title>ç¨Antæ¥å¢éæ´æ°schema(Using Ant for incremental schema updates)</title>
+
+ <para>
+ ä½ å¯ä»¥å¨Antèæ¬ä¸è°ç¨<literal>SchemaUpdate</literal>ï¼
+ </para>
+
+ <programlisting><![CDATA[<target name="schemaupdate">
+ <taskdef name="schemaupdate"
+ classname="org.hibernate.tool.hbm2ddl.SchemaUpdateTask"
+ classpathref="class.path"/>
+
+ <schemaupdate
+ properties="hibernate.properties"
+ quiet="no">
+ <fileset dir="src">
+ <include name="**/*.hbm.xml"/>
+ </fileset>
+ </schemaupdate>
+</target>]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-8" revision="1">
+ <title>Schema æ ¡éª</title>
+
+ <para>
+ <literal>SchemaValidator</literal>å·¥å
·ä¼æ¯è¾æ°æ®åºç°ç¶æ¯å¦ä¸æ å°ææ¡£âå¹é
âã注æï¼<literal>SchemaValidator</literal> 严éä¾èµäºJDBCçmetadata APIï¼å æ¤ä¸æ¯å¯¹ææçJDBC驱å¨é½éç¨ãè¿ä¸å·¥å
·å¨æµè¯çæ¶åç¹å«æç¨ã
+ </para>
+
+ <para>
+ <literal>java -cp </literal><emphasis>hibernate_classpaths</emphasis>
+ <literal>org.hibernate.tool.hbm2ddl.SchemaValidator</literal> <emphasis>options mapping_files</emphasis>
+ </para>
+
+ <table frame="topbot">
+ <title><literal>SchemaValidator</literal>å½ä»¤è¡åæ°</title>
+ <tgroup cols="2">
+ <colspec colwidth="1.5*"/>
+ <colspec colwidth="2*"/>
+ <thead>
+ <row>
+ <entry>é项</entry>
+ <entry>æè¿°</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>--naming=eg.MyNamingStrategy</literal></entry>
+ <entry>éæ©ä¸ä¸ªå½åçç¥ (<literal>NamingStrategy</literal>)</entry>
+ </row>
+ <row>
+ <entry><literal>--properties=hibernate.properties</literal></entry>
+ <entry>ä»æ件ä¸è¯»åæ°æ®åºå±æ§</entry>
+ </row>
+ <row>
+ <entry><literal>--config=hibernate.cfg.xml</literal></entry>
+ <entry>æå®ä¸ä¸ª<literal>.cfg.xml</literal>æ件</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ ä½ å¯ä»¥å¨ä½ çåºç¨ç¨åºä¸åµå
¥<literal>SchemaValidator</literal>ï¼
+ </para>
+
+ <programlisting><![CDATA[Configuration cfg = ....;
+new SchemaValidator(cfg).validate();]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="toolsetguide-s1-9">
+ <title>使ç¨Antè¿è¡schemaæ ¡éª</title>
+
+ <para>
+ ä½ å¯ä»¥å¨Antèæ¬ä¸è°ç¨<literal>SchemaValidator</literal>:
+ </para>
+
+ <programlisting><![CDATA[<target name="schemavalidate">
+ <taskdef name="schemavalidator"
+ classname="org.hibernate.tool.hbm2ddl.SchemaValidatorTask"
+ classpathref="class.path"/>
+
+ <schemavalidator
+ properties="hibernate.properties">
+ <fileset dir="src">
+ <include name="**/*.hbm.xml"/>
+ </fileset>
+ </schemaupdate>
+</target>]]></programlisting>
+
+ </sect2>
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/transactions.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/transactions.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/transactions.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,870 @@
+<chapter id="transactions" revision="2">
+ <title>äºå¡å并å</title>
+ <para>
+ Hibernateçäºå¡å并åæ§å¶å¾å®¹æææ¡ãHibernateç´æ¥ä½¿ç¨JDBCè¿æ¥åJTAèµæºï¼ä¸æ·»å ä»»ä½éå éå®
+ è¡ä¸ºãæ们强çæ¨èä½ è±ç¹æ¶é´äºè§£JDBCç¼ç¨ï¼ANSI SQLæ¥è¯¢è¯è¨åä½ ä½¿ç¨
+ çæ°æ®åºç³»ç»çäºå¡é离è§èã
+ </para>
+
+ <para>
+ Hibernateä¸éå®å
åä¸ç对象ãä½ çåºç¨ç¨åºä¼æç
§ä½ çæ°æ®åºäºå¡çé离级å«è§å®çé£æ ·è¿ä½ã幸äºæäº<literal>Session</literal>ï¼ä½¿å¾Hibernateéè¿æ è¯ç¬¦æ¥æ¾ï¼åå®ä½æ¥è¯¢ï¼ä¸æ¯è¿åæ éå¼çæ¥è¡¨æ¥è¯¢ï¼æä¾äºå¯éå¤ç读åï¼Repeatable readsï¼åè½ï¼<literal>Session</literal>åæ¶ä¹æ¯äºå¡èå´å
çç¼åï¼cacheï¼ã
+ </para>
+
+ <para>
+ é¤äºå¯¹èªå¨ä¹è§å¹¶åæ§å¶æä¾çæ¬ç®¡çï¼é对è¡çº§æ²è§éå®ï¼Hibernateä¹æä¾äºè¾
å©çï¼è¾å°ç)APIï¼å®ä½¿ç¨äº
+ <literal>SELECT FOR UPDATE</literal>çSQLè¯æ³ãæ¬ç« åé¢ä¼è®¨è®ºä¹è§å¹¶åæ§å¶åè¿ä¸ªAPIã
+ </para>
+ <para>
+ æ们ä»<literal>Configuration</literal>å±ã<literal>SessionFactory</literal>å±, å
+ <literal>Session</literal>å±å¼å§è®¨è®ºHibernateç并è¡æ§å¶ãæ°æ®åºäºå¡ååºç¨
+ ç¨åºçé¿äºå¡ã
+ </para>
+ <sect1 id="transactions-basics" revision="1">
+ <title>Sessionåäºå¡èå´(transaction scope)</title>
+ <para>
+ <literal>SessionFactory</literal>对象çå建代价å¾æè´µï¼å®æ¯çº¿ç¨å®å
¨ç对象ï¼å®ä¸ºææçåºç¨ç¨åºçº¿ç¨æå
±äº«ãå®åªå建ä¸æ¬¡ï¼é常æ¯å¨åºç¨ç¨åºå¯å¨çæ¶åï¼ç±ä¸ä¸ª<literal>Configuraion</literal>çå®ä¾æ¥å建ã
+ </para>
+ <para>
+ <literal>Session</literal>对象çå建代价æ¯è¾å°ï¼æ¯é线ç¨å®å
¨çï¼å¯¹äºå个请æ±ï¼å个ä¼è¯ãå个ç
+ å·¥ä½åå
èè¨ï¼å®åªè¢«ä½¿ç¨ä¸æ¬¡ï¼ç¶å就丢å¼ãåªæå¨éè¦çæ¶åï¼ä¸ä¸ª<literal>Session</literal>对象
+ æä¼è·åä¸ä¸ªJDBCç<literal>Connection</literal>ï¼æä¸ä¸ª<literal>Datasource</literal>ï¼
+ 对象ï¼å æ¤åè¥ä¸ä½¿ç¨çæ¶åå®ä¸æ¶è´¹ä»»ä½èµæºã
+ </para>
+ <para>
+ æ¤å¤æ们è¿è¦èèæ°æ®åºäºå¡ãæ°æ®åºäºå¡åºè¯¥å°½å¯è½ççï¼éä½æ°æ®åºä¸çéäºç¨ã
+ æ°æ®åºé¿äºå¡ä¼é»æ¢ä½ çåºç¨ç¨åºæ©å±å°é«ç并åè´è½½ãå æ¤ï¼åè¥å¨ç¨æ·æèæé´è®©æ°æ®åºäºå¡å¼çï¼ç´å°æ´ä¸ªå·¥ä½åå
å®ææå
³éè¿ä¸ªäºå¡ï¼è¿ç»ä¸æ¯ä¸ä¸ªå¥½ç设计ã
+ </para>
+ <para>
+ ä¸ä¸ªæä½åå
(Unit of work)çèå´æ¯å¤å¤§ï¼å个çHibernate <literal>Session</literal>è½è·¨è¶å¤ä¸ª
+ æ°æ®åºäºå¡åï¼è¿æ¯ä¸ä¸ª<literal>Session</literal>çä½ç¨èå´å¯¹åºä¸ä¸ªæ°æ®åºäºå¡çèå´ï¼åºè¯¥ä½æ¶æå¼
+ <literal>Session</literal>ï¼ä½æ¶å
³é<literal>Session</literal>ï¼ï¼ä½ åå¦ä½ååæ°æ®åºäºå¡çè¾¹çå¢ï¼
+ </para>
+ <sect2 id="transactions-basics-uow" revision="1">
+ <title>æä½åå
(Unit of work)</title>
+ <para>
+ é¦å
ï¼å«ç¨<emphasis>session-per-operation</emphasis>è¿ç§å模å¼äºï¼ä¹å°±æ¯è¯´ï¼å¨å个线ç¨ä¸ï¼
+ ä¸è¦å 为ä¸æ¬¡ç®åçæ°æ®åºè°ç¨ï¼å°±æå¼åå
³éä¸æ¬¡<literal>Session</literal>ï¼æ°æ®åºäºå¡ä¹æ¯å¦æ¤ã
+ åºç¨ç¨åºä¸çæ°æ®åºè°ç¨æ¯æç
§è®¡å好ç次åºï¼åç»ä¸ºååçæä½åå
ãï¼æ³¨æï¼è¿ä¹æå³çï¼åºç¨ç¨
+ åºä¸ï¼å¨å个çSQLè¯å¥åéä¹åï¼èªå¨äºå¡æ交(auto-commit)模å¼å¤±æäºãè¿ç§æ¨¡å¼ä¸é¨ä¸ºSQLæ§å¶å°æä½è®¾è®¡çã
+ Hibernateç¦æ¢ç«å³èªå¨äºå¡æ交模å¼ï¼æè
ææåºç¨æå¡å¨ç¦æ¢ç«å³èªå¨äºå¡æ交模å¼ãï¼æ°æ®åºäºå¡ç»ä¸æ¯å¯æå¯æ çï¼ä»»ä½ä¸æ°æ®åºä¹é´çé讯é½å¿
é¡»å¨æ个äºå¡ä¸è¿è¡ï¼ä¸ç®¡ä½ æ¯å¨è¯»è¿æ¯å¨åæ°æ®ã对读æ°æ®èè¨ï¼åºè¯¥é¿å
auto-commitè¡ä¸ºï¼å 为å¾å¤å°çäºå¡æ¯ä¸ä¸ªæ¸
æ°å®ä¹çå·¥ä½åå
æ§è½å·®ãåè
ä¹æ´å®¹æç»´æ¤åæ©å±ã
+ </para>
+ <para>
+ å¨å¤ç¨æ·çclient/serveråºç¨ç¨åºä¸ï¼æ常ç¨ç模å¼æ¯ <emphasis>æ¯ä¸ªè¯·æ±ä¸ä¸ªä¼è¯(session-per-request)</emphasis>ã
+ å¨è¿ç§æ¨¡å¼ä¸ï¼æ¥èªå®¢æ·ç«¯ç请æ±è¢«åéå°æå¡å¨ç«¯ï¼å³Hibernateæä¹
åå±è¿è¡çå°æ¹ï¼ï¼ä¸
+ 个æ°çHibernate <literal>Session</literal>被æå¼ï¼å¹¶ä¸æ§è¡è¿ä¸ªæä½åå
ä¸ææçæ°æ®åºæä½ã
+ ä¸æ¦æä½å®æï¼åæ¶å¯¹å®¢æ·ç«¯çååºä¹åå¤å°±ç»ªï¼ï¼session被åæ¥ï¼ç¶åå
³éãä½ ä¹å¯ä»¥ä½¿ç¨å
+ 个æ°æ®åºäºå¡æ¥å¤ç客æ·ç«¯è¯·æ±ï¼å¨ä½ æå¼<literal>Session</literal>ä¹åå¯å¨äºå¡ï¼å¨ä½ å
³é
+ <literal>Session</literal>ä¹åæ交äºå¡ãä¼è¯å请æ±ä¹é´çå
³ç³»æ¯ä¸å¯¹ä¸çå
³ç³»ï¼è¿ç§æ¨¡å¼å¯¹
+ äºå¤§å¤æ°åºç¨ç¨åºæ¥è¯´æ¯å¾æ£çã
+ </para>
+ <para>
+ å®ç°ææ¯çæ£çææãHibernateå
ç½®äºå¯¹"å½åsession(current session)" ç管çï¼ç¨äºç®åæ¤æ¨¡å¼ãä½ è¦åçä¸åå°±æ¯å¨æå¡å¨ç«¯è¦å¤ç请æ±çæ¶åï¼å¼å¯äºå¡ï¼å¨ååºåéç»å®¢æ·ä¹åç»æäºå¡ãä½ å¯ä»¥ç¨ä»»ä½æ¹å¼æ¥å®æè¿ä¸æä½ï¼é常çæ¹æ¡æ<literal>ServletFilter</literal>ï¼å¨serviceæ¹æ³ä¸è¿è¡pointcutçAOPæ¦æªå¨ï¼æè
proxy/interception容å¨ãEJB容å¨æ¯å®ç°æ¨ªå诸å¦EJB session beanä¸çäºå¡åçï¼ç¨CMT对äºå¡è¿è¡å£°æçæ¹é¢çæ åæ段ãåè¥ä½ å³å®ä½¿ç¨ç¼ç¨å¼çäºå¡åçï¼è¯·åèæ¬ç« åé¢è®²å°çHibernate <literal>Transaction</literal> APIï¼è¿å¯¹æç¨æ§å代ç å¯ç§»æ¤æ§é½æ好å¤ã
+ </para>
+
+ <para>
+ å¨ä»»ä½æ¶é´ï¼ä»»ä½å°æ¹ï¼ä½ çåºç¨ä»£ç å¯ä»¥éè¿ç®åçè°ç¨<literal>sessionFactory.getCurrentSession()</literal>æ¥è®¿é®"å½åsession"ï¼ç¨äºå¤ç请æ±ãä½ æ»æ¯ä¼å¾å°å½åæ°æ®åºäºå¡èå´å
ç<literal>Session</literal>ãå¨ä½¿ç¨æ¬å°èµæºæJTAç¯å¢æ¶ï¼å¿
é¡»é
ç½®å®ï¼è¯·åè§<xref linkend="architecture-current-session"/>ã
+ </para>
+
+ <para>
+ ææ¶ï¼å°<literal>Session</literal>åæ°æ®åºäºå¡çè¾¹ç延伸å°"å±ç¤ºå±è¢«æ¸²æå"ä¼å¸¦æ¥ä¾¿å©ãæäºserlvetåºç¨ç¨åºå¨å¯¹è¯·æ±è¿è¡å¤çåï¼æ个åç¬ç渲ææï¼è¿ç§å»¶ä¼¸å¯¹è¿ç§ç¨åºç¹å«æç¨ãåè¥ä½ å®ç°ä½ èªå·±çæ¦æªå¨ï¼æäºå¡è¾¹ç延伸å°å±ç¤ºå±æ¸²æç»æåé常容æãç¶èï¼åè¥ä½ ä¾èµæ容å¨ç®¡çäºå¡çEJBï¼è¿å°±ä¸å¤ªå®¹æäºï¼å 为äºå¡ä¼å¨EJBæ¹æ³è¿ååç»æï¼èé£æ¯å¨ä»»ä½å±ç¤ºå±æ¸²æå¼å§ä¹åã请访é®Hibernateç½ç«å论åï¼ä½ å¯ä»¥æ¾å°<emphasis>Open Session in View</emphasis>è¿ä¸æ¨¡å¼çæ示å示ä¾ã
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-basics-apptx" revision="1">
+ <title>é¿å¯¹è¯</title>
+ <para>
+ session-per-request模å¼ä¸ä»
ä»
æ¯ä¸ä¸ªå¯ä»¥ç¨æ¥è®¾è®¡æä½åå
çæç¨æ¦å¿µãå¾å¤ä¸å¡å¤çé½é
+ è¦ä¸ç³»åå®æ´çä¸ç¨æ·ä¹é´ç交äºï¼èè¿äºç¨æ·æ¯æ对æ°æ®åºæ交å访é®çç¨æ·ãå¨åºäºwebçåºç¨åä¼ä¸
+ åºç¨ä¸ï¼è·¨ç¨æ·äº¤äºçæ°æ®åºäºå¡æ¯æ æ³æ¥åçãèèä¸é¢çä¾åï¼
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ å¨çé¢ç第ä¸å±ï¼æå¼å¯¹è¯æ¡ï¼ç¨æ·æçå°çæ°æ®æ¯è¢«ä¸ä¸ªç¹å®ç <literal>Session</literal> åæ°æ®
+ åºäºå¡è½½å
¥(load)çãç¨æ·å¯ä»¥éæä¿®æ¹å¯¹è¯æ¡ä¸çæ°æ®å¯¹è±¡ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ 5åéåï¼ç¨æ·ç¹å»âä¿åâï¼æææååºçä¿®æ¹è¢«æä¹
åï¼åæ¶ä»ä¹ææèªå·±æ¯å¯ä¸ä¿®æ¹è¿ä¸ªä¿¡æ¯ç人ï¼ä¸ä¼åºç°
+ ä¿®æ¹å²çªã
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ ä»ç¨æ·çè§åº¦æ¥çï¼æ们æè¿ä¸ªæä½åå
称为é¿æ¶é´è¿è¡ç<emphasis>对è¯</emphasis>ï¼conversationï¼,æè
(or <emphasis>åºç¨äºå¡</emphasis>,application transaction)ã
+ å¨ä½ çåºç¨ç¨åºä¸ï¼å¯ä»¥æå¾å¤ç§æ¹æ³æ¥å®ç°å®ã
+ </para>
+ <para>
+ 头ä¸ä¸ªå¹¼ç¨çåæ³æ¯ï¼å¨ç¨æ·æèçè¿ç¨ä¸ï¼ä¿æ<literal>Session</literal>åæ°æ®åºäºå¡æ¯æå¼çï¼
+ ä¿ææ°æ®åºéå®ï¼ä»¥é»æ¢å¹¶åä¿®æ¹ï¼ä»èä¿è¯æ°æ®åºäºå¡é离级å«åååæä½ãè¿ç§æ¹å¼å½ç¶æ¯ä¸ä¸ªå模å¼ï¼
+ å 为éäºç¨ä¼å¯¼è´åºç¨ç¨åºæ æ³æ©å±å¹¶åç¨æ·çæ°ç®ã
+ </para>
+ <para>
+ å¾ææ¾ï¼æ们å¿
须使ç¨å¤ä¸ªæ°æ®åºäºå¡æ¥å®ç°è¿ä¸ªå¯¹è¯ãå¨è¿ä¸ªä¾åä¸ï¼ç»´æ¤ä¸å¡å¤çç
+ äºå¡é离åæäºåºç¨ç¨åºå±çé¨å责任ãä¸ä¸ªå¯¹è¯é常跨è¶å¤ä¸ªæ°æ®åºäºå¡ãå¦æä»
ä»
åªæä¸
+ 个æ°æ®åºäºå¡ï¼æåçé£ä¸ªäºå¡ï¼ä¿åæ´æ°è¿çæ°æ®ï¼èææå
¶ä»äºå¡åªæ¯å纯ç读åæ°æ®ï¼ä¾å¦å¨ä¸
+ 个跨è¶å¤ä¸ªè¯·æ±/ååºå¨æçå导é£æ ¼ç对è¯æ¡ä¸ï¼ï¼é£ä¹åºç¨ç¨åºäºå¡å°ä¿è¯å
¶ååæ§ãè¿ç§æ¹å¼æ¯å¬
+ èµ·æ¥è¿è¦å®¹æå®ç°ï¼ç¹å«æ¯å½ä½ 使ç¨äºHibernateçä¸è¿°ç¹æ§çæ¶åï¼
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis>èªå¨çæ¬å</emphasis> - Hibernateè½å¤èªå¨è¿è¡ä¹è§å¹¶åæ§å¶ ï¼å¦æå¨ç¨æ·æè
+ çè¿ç¨ä¸åç并åä¿®æ¹ï¼Hibernateè½å¤èªå¨æ£æµå°ãä¸è¬æ们åªå¨å¯¹è¯ç»ææ¶ææ£æ¥ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>è±ç®¡å¯¹è±¡</emphasis>ï¼Detached Objectsï¼- å¦æä½ å³å®éç¨åé¢å·²ç»è®¨è®ºè¿ç
+ <emphasis>session-per-request</emphasis>模å¼ï¼ææè½½å
¥çå®ä¾å¨ç¨æ·æèçè¿ç¨
+ ä¸é½å¤äºä¸Sessionè±ç¦»çç¶æãHibernateå
è®¸ä½ æä¸Sessionè±ç¦»ç对象éæ°å
³èå°Session
+ ä¸ï¼å¹¶ä¸å¯¹ä¿®æ¹è¿è¡æä¹
åï¼è¿ç§æ¨¡å¼è¢«ç§°ä¸º
+ <emphasis>session-per-request-with-detached-objects</emphasis>ãèªå¨çæ¬å被ç¨æ¥é离并åä¿®æ¹ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis>Extended (or Long) Session</emphasis> - Hibernate ç<literal>Session</literal>
+ å¯ä»¥å¨æ°æ®åºäºå¡æ交ä¹åååºå±çJDBCè¿æ¥æå¼ï¼å½ä¸ä¸ªæ°ç客æ·ç«¯è¯·æ±å°æ¥çæ¶åï¼å®åéæ°è¿æ¥ä¸åºå±ç
+ JDBCè¿æ¥ãè¿ç§æ¨¡å¼è¢«ç§°ä¹ä¸º<emphasis>session-per-conversation</emphasis>ï¼è¿ç§æ
åµå¯
+ è½ä¼é æä¸å¿
è¦çSessionåJDBCè¿æ¥çéæ°å
³èãèªå¨çæ¬å被ç¨æ¥é离并åä¿®æ¹, <literal>Session</literal>é常ä¸å
许èªå¨flush,èæ¯æç¡®flushã
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ <emphasis>session-per-request-with-detached-objects</emphasis> å
+ <emphasis>session-per-conversation</emphasis> åæä¼ç¼ºç¹ï¼æ们å¨æ¬ç« åé¢ä¹è§å¹¶å
+ æ§å¶é£é¨ååè¿è¡è®¨è®ºã
+ </para>
+ </sect2>
+
+ <sect2 id="transactions-basics-identity">
+ <title>å
³æ³¨å¯¹è±¡æ è¯(Considering object identity)</title>
+ <para>
+ åºç¨ç¨åºå¯è½å¨ä¸¤ä¸ªä¸åç<literal>Session</literal>ä¸å¹¶å访é®åä¸æä¹
åç¶æï¼ä½æ¯ï¼
+ ä¸ä¸ªæä¹
åç±»çå®ä¾æ æ³å¨ä¸¤ä¸ª <literal>Session</literal>ä¸å
±äº«ãå æ¤æ两ç§ä¸åçæ è¯è¯ä¹ï¼
+ </para>
+ <para>
+ <variablelist spacing="compact">
+ <varlistentry>
+ <term>æ°æ®åºæ è¯</term>
+ <listitem>
+ <para>
+ <literal>foo.getId().equals( bar.getId() )</literal>
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>JVM æ è¯</term>
+ <listitem>
+ <para>
+ <literal>foo==bar</literal>
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>
+ 对äºé£äºå
³èå° <emphasis>ç¹å®</emphasis><literal>Session</literal>
+ ï¼ä¹å°±æ¯å¨å个<literal>Session</literal>çèå´å
ï¼ä¸ç对象æ¥è¯´ï¼è¿
+ 两ç§æ è¯çè¯ä¹æ¯çä»·çï¼ä¸æ°æ®åºæ è¯å¯¹åºçJVMæ è¯æ¯ç±Hibernateæ¥ä¿
+ è¯çãä¸è¿ï¼å½åºç¨ç¨åºå¨ä¸¤ä¸ªä¸åçsessionä¸å¹¶å访é®å
·æåä¸æä¹
åæ
+ è¯çä¸å¡å¯¹è±¡å®ä¾çæ¶åï¼è¿ä¸ªä¸å¡å¯¹è±¡ç两个å®ä¾äºå®ä¸æ¯ä¸ç¸åçï¼ä»
+ JVMè¯å«æ¥çï¼ãè¿ç§å²çªå¯ä»¥éè¿å¨åæ¥åæ交çæ¶å使ç¨èªå¨çæ¬ååä¹
+ è§éå®æ¹æ³æ¥è§£å³ã
+ </para>
+ <para>
+ è¿ç§æ¹å¼æå
³äºå¹¶åç头ç¼é®é¢çç»äºHibernateåæ°æ®åºï¼ç±äºå¨å个线ç¨å
ï¼æä½åå
ä¸ç对象è¯å«ä¸
+ éè¦ä»£ä»·æè´µçéå®æå
¶ä»æä¹ä¸çåæ¥ï¼å æ¤å®åæ¶å¯ä»¥æä¾æ好çå¯ä¼¸ç¼©æ§ãåªè¦å¨å个线ç¨åªææä¸ä¸ª
+ <literal>Session</literal>ï¼åºç¨ç¨åºå°±ä¸éè¦åæ¥ä»»ä½ä¸å¡å¯¹è±¡ãå¨<literal>Session</literal>
+ çèå´å
ï¼åºç¨ç¨åºå¯ä»¥æ¾å¿ç使ç¨<literal>==</literal>è¿è¡å¯¹è±¡æ¯è¾ã
+ </para>
+ <para>
+ ä¸è¿ï¼åºç¨ç¨åºå¨<literal>Session</literal>çå¤é¢ä½¿ç¨<literal>==</literal>è¿è¡å¯¹è±¡æ¯è¾å¯è½ä¼
+ 导è´æ æ³é¢æçç»æãå¨ä¸äºæ æ³é¢æçåºåï¼ä¾å¦ï¼å¦æä½ æ两个è±ç®¡å¯¹è±¡å®ä¾æ¾è¿åä¸ä¸ª
+ <literal>Set</literal>çæ¶åï¼å°±å¯è½åçãè¿ä¸¤ä¸ªå¯¹è±¡å®ä¾å¯è½æåä¸ä¸ªæ°æ®åºæ è¯ï¼ä¹å°±æ¯è¯´ï¼
+ ä»ä»¬ä»£è¡¨äºè¡¨çåä¸è¡æ°æ®ï¼ï¼ä»JVMæ è¯çå®ä¹ä¸æ¥è¯´ï¼å¯¹è±ç®¡ç对象èè¨ï¼Hibernateæ æ³ä¿è¯ä»ä»¬
+ ççJVMæ è¯ä¸è´ãå¼å人åå¿
é¡»è¦çæä¹
åç±»ç<literal>equals()</literal>æ¹æ³å
+ <literal>hashCode()</literal> æ¹æ³ï¼ä»èå®ç°èªå®ä¹ç对象ç¸çè¯ä¹ãè¦åï¼ä¸è¦ä½¿ç¨æ°æ®åºæ è¯
+ æ¥å®ç°å¯¹è±¡ç¸çï¼åºè¯¥ä½¿ç¨ä¸å¡é®å¼ï¼ç±å¯ä¸çï¼é常ä¸åçå±æ§ç»æãå½ä¸ä¸ªç¬æ¶å¯¹è±¡è¢«æä¹
åçæ¶
+ åï¼å®çæ°æ®åºæ è¯ä¼åçæ¹åãå¦æä¸ä¸ªç¬æ¶å¯¹è±¡ï¼é常ä¹å
æ¬è±ç®¡å¯¹è±¡å®ä¾ï¼è¢«æ¾å
¥ä¸
+ 个<literal>Set</literal>ï¼æ¹åå®çhashcodeä¼å¯¼è´ä¸è¿ä¸ª<literal>Set</literal>çå
³ç³»ä¸æãè½
+ ç¶ä¸å¡é®å¼çå±æ§ä¸è±¡æ°æ®åºä¸»é®é£æ ·ç¨³å®ä¸åï¼ä½æ¯ä½ åªéè¦ä¿è¯å¨åä¸ä¸ª<literal>Set</literal>
+ ä¸ç对象å±æ§ç稳å®æ§å°±è¶³å¤äºã请å°Hibernateç½ç«å»å¯»æ±è¿ä¸ªé®é¢æ´å¤ç详ç»ç讨论ã请注æï¼è¿ä¸æ¯ä¸
+ 个æå
³Hibernateçé®é¢ï¼èä»
ä»
æ¯ä¸ä¸ªå
³äºJava对象æ è¯åå¤çè¡ä¸ºå¦ä½å®ç°çé®é¢ã
+ </para>
+ </para>
+ </sect2>
+
+ <sect2 id="transactions-basics-issues">
+ <title>常è§é®é¢</title>
+ <para>
+ å³ä¸è¦ä½¿ç¨å模å¼<emphasis>session-per-user-session</emphasis>æè
<emphasis>
+ session-per-application</emphasis>ï¼å½ç¶ï¼è¿ä¸ªè§å®å ä¹æ²¡æä¾å¤ï¼ã请注æï¼
+ ä¸è¿°ä¸äºé®é¢å¯è½ä¹ä¼åºç°å¨æ们æ¨èç模å¼ä¸ï¼å¨ä½ ä½åºæ个设计å³å®ä¹åï¼è¯·å¡å¿
ç解该模å¼çåºç¨åæã
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>Session</literal> 对象æ¯é线ç¨å®å
¨çãå¦æä¸ä¸ª<literal>Session</literal>
+ å®ä¾å
许å
±äº«çè¯ï¼é£äºæ¯æ并åè¿è¡çä¸ä¸ï¼ä¾å¦HTTP requestï¼session beans,æè
æ¯
+ Swing workersï¼å°ä¼å¯¼è´åºç°èµæºäºç¨ï¼race conditionï¼ãå¦æå¨<literal>HttpSession</literal>ä¸æ
+ Hibernate ç<literal>Session</literal>çè¯ï¼ç¨å讨论ï¼ï¼ä½ åºè¯¥èèåæ¥è®¿é®ä½ çHttp sessionã
+ å¦åï¼åªè¦ç¨æ·è¶³å¤å¿«çç¹å»æµè§å¨çâå·æ°âï¼å°±ä¼å¯¼è´ä¸¤ä¸ªå¹¶åè¿è¡çº¿ç¨ä½¿ç¨åä¸ä¸ª
+ <literal>Session</literal>ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ä¸ä¸ªç±Hibernateæåºçå¼å¸¸æå³çä½ å¿
é¡»ç«å³åæ»æ°æ®åºäºå¡ï¼å¹¶ç«å³å
³é<literal>Session</literal>
+ ï¼ç¨åä¼å±å¼è®¨è®ºï¼ãå¦æä½ ç<literal>Session</literal>ç»å®å°ä¸ä¸ªåºç¨ç¨åºä¸ï¼ä½ å¿
+ é¡»åæ¢è¯¥åºç¨ç¨åºãåæ»æ°æ®åºäºå¡å¹¶ä¸ä¼æä½ çä¸å¡å¯¹è±¡éåå°äºå¡å¯å¨æ¶åçç¶æãè¿
+ æå³çæ°æ®åºç¶æåä¸å¡å¯¹è±¡ç¶æä¸åæ¥ãé常æ
åµä¸ï¼è¿ä¸æ¯ä»ä¹é®é¢ï¼å 为å¼å¸¸æ¯ä¸å¯
+ æ¢å¤ç,ä½ å¿
é¡»å¨åæ»ä¹åéæ°å¼å§æ§è¡ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>Session</literal> ç¼åäºå¤äºæä¹
åç¶æçæ¯ä¸ªå¯¹è±¡ï¼Hibernateä¼çè§åæ£æ¥èæ°æ®ï¼ã
+ è¿æå³çï¼å¦æä½ è®©<literal>Session</literal>æå¼å¾é¿ä¸æ®µæ¶é´ï¼ææ¯ä»
ä»
è½½å
¥äºè¿å¤çæ°æ®ï¼
+ <literal>Session</literal>å ç¨çå
åä¼ä¸ç´å¢é¿ï¼ç´å°æåºOutOfMemoryExceptionå¼å¸¸ãè¿ä¸ª
+ é®é¢çä¸ä¸ªè§£å³æ¹æ³æ¯è°ç¨<literal>clear()</literal> å<literal>evict()</literal>æ¥ç®¡ç
+ <literal>Session</literal>çç¼åï¼ä½æ¯å¦æä½ éè¦å¤§æ¹éæ°æ®æä½çè¯ï¼æ好èè
+ 使ç¨åå¨è¿ç¨ãå¨<xref linkend="batch"/>ä¸æä¸äºè§£å³æ¹æ¡ãå¨ç¨æ·ä¼è¯æé´ä¸ç´ä¿æ
+ <literal>Session</literal>æå¼ä¹æå³çåºç°èæ°æ®çå¯è½æ§å¾é«ã
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="transactions-demarcation">
+ <title>æ°æ®åºäºå¡å£°æ</title>
+ <para>
+ æ°æ®åºï¼æè
ç³»ç»ï¼äºå¡ç声ææ»æ¯å¿
é¡»çãå¨æ°æ®åºäºå¡ä¹å¤ï¼å°±æ æ³åæ°æ®åºé讯ï¼è¿å¯è½ä¼è®©é£äºä¹ æ¯äº
+ èªå¨æ交äºå¡æ¨¡å¼çå¼å人åæå°è¿·æï¼ãæ°¸è¿ä½¿ç¨æ¸
æ°çäºå¡å£°æï¼å³ä½¿åªè¯»æä½ä¹æ¯å¦æ¤ãè¿è¡
+ æ¾å¼çäºå¡å£°æ并ä¸æ»æ¯éè¦çï¼è¿åå³äºä½ çäºå¡é离级å«åæ°æ®åºçè½åï¼ä½ä¸ç®¡æä¹è¯´ï¼å£°æäºå¡æ»å½æçæ 害ãå½ç¶ï¼ä¸ä¸ªåç¬çæ°æ®åºäºå¡æ»æ¯æ¯å¾å¤çç¢çäºå¡æ§è½æ´å¥½ï¼å³æ¶å¯¹è¯»æ°æ®èè¨ä¹æ¯ä¸æ ·ã
+ </para>
+ <para>
+ ä¸ä¸ªHibernateåºç¨ç¨åºå¯ä»¥è¿è¡å¨éæ管ç¯å¢ä¸ï¼ä¹å°±æ¯ç¬ç«è¿è¡çåºç¨ç¨åºï¼ç®åWebåºç¨ç¨åºï¼
+ æè
Swingå¾å½¢æ¡é¢åºç¨ç¨åºï¼ï¼ä¹å¯ä»¥è¿è¡å¨æ管çJ2EEç¯å¢ä¸ãå¨ä¸ä¸ªéæ管ç¯å¢ä¸ï¼Hibernate
+ é常èªå·±è´è´£ç®¡çæ°æ®åºè¿æ¥æ± ãåºç¨ç¨åºå¼å人åå¿
é¡»æ工设置äºå¡å£°æï¼æ¢å¥è¯è¯´ï¼å°±æ¯æå·¥å¯
+ å¨ï¼æ交ï¼æè
åæ»æ°æ®åºäºå¡ãä¸ä¸ªæ管çç¯å¢é常æä¾äºå®¹å¨ç®¡çäºå¡(CMT)ï¼ä¾å¦äºå¡è£
é
éè¿å¯å£°
+ æçæ¹å¼å®ä¹å¨EJB session beansçé¨ç½²æ述符ä¸ãå¯ç¼ç¨å¼äºå¡å£°æä¸åéè¦ï¼å³ä½¿æ¯
+ <literal>Session</literal> çåæ¥ä¹å¯ä»¥èªå¨å®æã
+ </para>
+ <para>
+ 让æä¹
å±å
·å¤å¯ç§»æ¤æ§æ¯äººä»¬ççæ³,è¿ç§ç§»æ¤åçå¨éæ管çæ¬å°èµæºç¯å¢ï¼ä¸ä¾èµJTAä½æ¯ä½¿ç¨BMTèéCMTçç³»ç»ä¹é´ãå¨ä¸¤ç§æ
åµä¸ä½ é½å¯ä»¥ä½¿ç¨ç¼ç¨å¼çäºå¡ç®¡çãHibernateæä¾äºä¸å¥ç§°ä¸º<literal>Transaction</literal>çå°è£
APIï¼
+ ç¨æ¥æä½ çé¨ç½²ç¯å¢ä¸çæ¬å°äºå¡ç®¡çç³»ç»è½¬æ¢å°Hibernateäºå¡ä¸ãè¿ä¸ªAPIæ¯å¯éçï¼ä½æ¯æ们强ç
+ æ¨èä½ ä½¿ç¨ï¼é¤éä½ ç¨CMT session beanã
+ </para>
+ <para>
+ é常æ
åµä¸ï¼ç»æ <literal>Session</literal> å
å«äºå个ä¸åçé¶æ®µ:
+ </para>
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ åæ¥session(flush,å·åºå°ç£çï¼
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ æ交äºå¡
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ å
³ésession
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ å¤çå¼å¸¸
+ </para>
+ </listitem>
+ </itemizedlist>
+
+
+ <para>
+ sessionçåæ¥(flush,å·åºï¼åé¢å·²ç»è®¨è®ºè¿äºï¼æ们ç°å¨è¿ä¸æ¥èå¯å¨æ管åéæ管ç¯å¢ä¸çäºå¡å£°æåå¼å¸¸å¤çã
+ </para>
+
+ <sect2 id="transactions-demarcation-nonmanaged" revision="2">
+ <title>éæ管ç¯å¢</title>
+ <para>
+ å¦æHibernatæä¹
å±è¿è¡å¨ä¸ä¸ªéæ管ç¯å¢ä¸ï¼æ°æ®åºè¿æ¥é常ç±Hibernateçç®åï¼å³éDataSource)è¿æ¥æ± æºå¶
+ æ¥å¤çãsession/transactionå¤çæ¹å¼å¦ä¸æ示ï¼
+ </para>
+ <programlisting><![CDATA[//Non-managed environment idiom
+Session sess = factory.openSession();
+Transaction tx = null;
+try {
+ tx = sess.beginTransaction();
+
+ // do some work
+ ...
+
+ tx.commit();
+}
+catch (RuntimeException e) {
+ if (tx != null) tx.rollback();
+ throw e; // or display error message
+}
+finally {
+ sess.close();
+}]]></programlisting>
+ <para>
+ ä½ ä¸éè¦æ¾å¼<literal>flush()</literal> <literal>Session</literal> -
+ 对<literal>commit()</literal>çè°ç¨ä¼èªå¨è§¦åsessionçåæ¥(åå³äºsessionç<xref linkend="objectstate-flushing">FlushMode</xref>)ãè°ç¨ <literal>close()</literal> æ å¿sessionçç»æã<literal>close()</literal>æ¹æ³éè¦çæ示æ¯ï¼<literal>session</literal>éæ¾äºJDBCè¿æ¥ãè¿æ®µJava代ç å¨éæ管ç¯å¢ä¸åJTAç¯å¢ä¸é½å¯ä»¥è¿è¡ã
+ </para>
+
+ <para>
+ æ´å çµæ´»çæ¹æ¡æ¯Hibernateå
ç½®ç"current session"ä¸ä¸æ管çï¼åæå·²ç»è®²è¿ï¼
+ </para>
+
+ <programlisting><![CDATA[// Non-managed environment idiom with getCurrentSession()
+try {
+ factory.getCurrentSession().beginTransaction();
+
+ // do some work
+ ...
+
+ factory.getCurrentSession().getTransaction().commit();
+}
+catch (RuntimeException e) {
+ factory.getCurrentSession().getTransaction().rollback();
+ throw e; // or display error message
+}]]></programlisting>
+
+ <para>
+ ä½ å¾å¯è½ä»æªå¨ä¸ä¸ªé常çåºç¨ç¨åºçä¸å¡ä»£ç ä¸è§è¿è¿æ ·ç代ç çæï¼è´å½çï¼ç³»ç»ï¼å¼å¸¸åºè¯¥æ»æ¯
+ å¨åºç¨ç¨åºâ顶å±â被æè·ãæ¢å¥è¯è¯´ï¼æ§è¡Hibernateè°ç¨ç代ç ï¼å¨æä¹
å±ï¼åå¤ç
+ <literal>RuntimeException</literal>å¼å¸¸ç代ç ï¼é常åªè½æ¸
çåéåºåºç¨ç¨åºï¼åºè¯¥å¨ä¸å
+ çåºç¨ç¨åºé»è¾å±ãHibernateçå½åä¸ä¸æ管çå¯ä»¥æ大å°ç®åè¿ä¸è®¾è®¡ï¼ä½ ææçä¸åå°±æ¯<literal>SessionFactory</literal>ã å¼å¸¸å¤çå°å¨æ¬ç« ç¨åè¿è¡è®¨è®ºã
+ </para>
+
+ <para>
+ 请注æï¼ä½ åºè¯¥éæ© <literal>org.hibernate.transaction.JDBCTransactionFactory</literal>
+ (è¿æ¯é»è®¤é项)ï¼å¯¹ç¬¬äºä¸ªä¾åæ¥è¯´ï¼<literal>hibernate.current_session_context_class</literal>åºè¯¥æ¯<literal>"thread"</literal>
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-demarcation-jta" revision="3">
+ <title>使ç¨JTA</title>
+ <para>
+ å¦æä½ çæä¹
å±è¿è¡å¨ä¸ä¸ªåºç¨æå¡å¨ä¸ï¼ä¾å¦ï¼å¨EJB session beansçåé¢ï¼ï¼Hibernateè·å
+ çæ¯ä¸ªæ°æ®æºè¿æ¥å°èªå¨æ为å
¨å±JTAäºå¡çä¸é¨åã
+ ä½ å¯ä»¥å®è£
ä¸ä¸ªç¬ç«çJTAå®ç°ï¼ä½¿ç¨å®èä¸ä½¿ç¨EJBãHibernateæä¾äºä¸¤ç§çç¥è¿è¡JTAéæã
+ </para>
+
+ <para>
+ å¦æä½ ä½¿ç¨bean管çäºå¡ï¼BMTï¼ï¼å¯ä»¥éè¿ä½¿ç¨Hibernateç <literal>Transaction</literal> APIæ¥åè¯
+ åºç¨æå¡å¨å¯å¨åç»æBMTäºå¡ãå æ¤ï¼äºå¡ç®¡ç代ç åå¨éæ管ç¯å¢ä¸æ¯ä¸æ ·çã
+ </para>
+
+ <programlisting><![CDATA[// BMT idiom
+Session sess = factory.openSession();
+Transaction tx = null;
+try {
+ tx = sess.beginTransaction();
+
+ // do some work
+ ...
+
+ tx.commit();
+}
+catch (RuntimeException e) {
+ if (tx != null) tx.rollback();
+ throw e; // or display error message
+}
+finally {
+ sess.close();
+}]]></programlisting>
+
+ <para>
+ å¦æä½ å¸æ使ç¨ä¸äºå¡ç»å®ç<literal>Session</literal>ï¼ä¹å°±æ¯ä½¿ç¨<literal>getCurrentSession()</literal>æ¥ç®åä¸ä¸æ管çï¼ä½ å°ä¸å¾ä¸ç´æ¥ä½¿ç¨JTA <literal>UserTransaction</literal>APIã
+ </para>
+
+ <programlisting><![CDATA[// BMT idiom with getCurrentSession()
+try {
+ UserTransaction tx = (UserTransaction)new InitialContext()
+ .lookup("java:comp/UserTransaction");
+
+ tx.begin();
+
+ // Do some work on Session bound to transaction
+ factory.getCurrentSession().load(...);
+ factory.getCurrentSession().persist(...);
+
+ tx.commit();
+}
+catch (RuntimeException e) {
+ tx.rollback();
+ throw e; // or display error message
+}]]></programlisting>
+
+
+ <para>
+ å¨CMTæ¹å¼ä¸ï¼äºå¡å£°ææ¯å¨session beançé¨ç½²æ述符ä¸ï¼èä¸éè¦ç¼ç¨ã
+ å æ¤ï¼ä»£ç 被ç®å为:
+ </para>
+
+ <programlisting><![CDATA[// CMT idiom
+Session sess = factory.getCurrentSession();
+
+// do some work
+...
+
+]]></programlisting>
+
+ <para>
+ å¨CMT/EJBä¸çè³ä¼èªå¨rollbackï¼å 为åè¥ææªæè·ç<literal>RuntimeException</literal>ä»session beanæ¹æ³ä¸æåºï¼è¿å°±ä¼éç¥å®¹å¨æå
¨å±äºå¡åæ»ã<emphasis>è¿å°±æå³çï¼å¨BMTæè
CMTä¸ï¼ä½ æ ¹æ¬å°±ä¸éè¦ä½¿ç¨Hibernate <literal>Transaction</literal> API ï¼ä½ èªå¨å¾å°äºç»å®å°äºå¡çâå½åâSessionã
+ </emphasis>
+ </para>
+
+ <para>
+ 注æï¼å½ä½ é
ç½®Hibernateçtransaction factoryçæ¶åï¼å¨ç´æ¥ä½¿ç¨JTAçæ¶åï¼BMTï¼ï¼ä½ åºè¯¥éæ©<literal>org.hibernate.transaction.JTATransactionFactory</literal>,å¨CMT session beanä¸éæ©<literal>org.hibernate.transaction.CMTTransactionFactory</literal>ãè®°å¾ä¹è¦è®¾ç½®<literal>hibernate.transaction.manager_lookup_class</literal>ãè¿æï¼ç¡®è®¤ä½ ç<literal>hibernate.current_session_context_class</literal>æªè®¾ç½®ï¼ä¸ºäºåä¸å
¼å®¹ï¼ï¼æè
设置为<literal>"jta"</literal>ã
+ </para>
+
+ <para>
+ <literal>getCurrentSession()</literal>å¨JTAç¯å¢ä¸æä¸ä¸ªå¼ç«¯ã对<literal>after_statement</literal>è¿æ¥éæ¾æ¹å¼æä¸ä¸ªè¦åï¼è¿æ¯è¢«é»è®¤ä½¿ç¨çãå 为JTAè§èçä¸ä¸ªå¾æè ¢çéå¶ï¼Hibernateä¸å¯è½èªå¨æ¸
çä»»ä½æªå
³éç<literal>ScrollableResults</literal> æè
<literal>Iterator</literal>ï¼å®ä»¬æ¯ç±<literal>scroll()</literal>æ<literal>iterate()</literal>产ççãä½ <emphasis>must</emphasis>éè¿å¨<literal>finally</literal>åä¸ï¼æ¾å¼è°ç¨<literal>ScrollableResults.close()</literal>æè
<literal>Hibernate.close(Iterator)</literal>æ¹æ³æ¥éæ¾åºå±æ°æ®åºæ¸¸æ ã(å½ç¶ï¼å¤§é¨åç¨åºå®å
¨å¯ä»¥å¾å®¹æçé¿å
å¨JTAæCMT代ç ä¸åºç°<literal>scroll()</literal>æ<literal>iterate()</literal>ã)
+ </para>
+
+
+ </sect2>
+
+
+
+ <sect2 id="transactions-demarcation-exceptions">
+ <title>å¼å¸¸å¤ç</title>
+ <para>
+ å¦æ <literal>Session</literal> æåºå¼å¸¸ (å
æ¬ä»»ä½<literal>SQLException</literal>),
+ ä½ åºè¯¥ç«å³åæ»æ°æ®åºäºå¡ï¼è°ç¨ <literal>Session.close()</literal> ï¼ä¸¢å¼è¯¥
+ <literal>Session</literal>å®ä¾ã<literal>Session</literal>çæäºæ¹æ³å¯è½ä¼å¯¼è´session
+ å¤äºä¸ä¸è´çç¶æãææç±Hibernateæåºçå¼å¸¸é½è§ä¸ºä¸å¯ä»¥æ¢å¤çãç¡®ä¿å¨
+ <literal>finally</literal> 代ç åä¸è°ç¨<literal>close()</literal>æ¹æ³ï¼ä»¥å
³éæ
+ <literal>Session</literal>ã
+ </para>
+ <para>
+ <literal>HibernateException</literal>æ¯ä¸ä¸ªéæ£æ¥æå¼å¸¸ï¼è¿ä¸åäºHibernateèççæ¬ï¼ï¼
+ å®å°è£
äºHibernateæä¹
å±å¯è½åºç°ç大å¤æ°é误ãæ们çè§ç¹æ¯ï¼ä¸åºè¯¥å¼ºè¿«åºç¨ç¨åºå¼å人å
+ å¨åºå±æè·æ æ³æ¢å¤çå¼å¸¸ãå¨å¤§å¤æ°è½¯ä»¶ç³»ç»ä¸ï¼éæ£æ¥æå¼å¸¸åè´å½å¼å¸¸é½æ¯å¨ç¸åºæ¹æ³è°ç¨
+ çå æ ç顶å±è¢«å¤ççï¼ä¹å°±æ¯è¯´ï¼å¨è½¯ä»¶ä¸é¢çé»è¾å±ï¼ï¼å¹¶ä¸æä¾ä¸ä¸ªé误信æ¯ç»åºç¨è½¯ä»¶çç¨æ·
+ ï¼æè
éåå
¶ä»æäºç¸åºçæä½ï¼ã请注æï¼Hibernateä¹æå¯è½æåºå
¶ä»å¹¶ä¸å±äº
+ <literal>HibernateException</literal>çéæ£æ¥æå¼å¸¸ãè¿äºå¼å¸¸åæ ·ä¹æ¯æ æ³æ¢å¤çï¼åºè¯¥
+ éåæäºç¸åºçæä½å»å¤çã
+ </para>
+ <para>
+ å¨åæ°æ®åºè¿è¡äº¤äºæ¶ï¼Hibernateææè·ç<literal>SQLException</literal>å°è£
为Hibernateç
+ <literal>JDBCException</literal>ãäºå®ä¸ï¼Hibernateå°è¯æå¼å¸¸è½¬æ¢ä¸ºæ´æå®é
å«ä¹
+ ç<literal>JDBCException</literal>å¼å¸¸çåç±»ãåºå±ç<literal>SQLException</literal>å¯ä»¥
+ éè¿<literal>JDBCException.getCause()</literal>æ¥å¾å°ãHibernateéè¿ä½¿ç¨å
³èå°
+ <literal>SessionFactory</literal>ä¸ç<literal>SQLExceptionConverter</literal>æ¥
+ æ<literal>SQLException</literal>转æ¢ä¸ºä¸ä¸ªå¯¹åºç<literal>JDBCException</literal>
+ å¼å¸¸çåç±»ãé»è®¤æ
åµä¸ï¼<literal>SQLExceptionConverter</literal>å¯ä»¥éè¿é
ç½®dialect
+ é项æå®ï¼æ¤å¤ï¼ä¹å¯ä»¥ä½¿ç¨ç¨æ·èªå®ä¹çå®ç°ç±»ï¼åèjavadocs
+ <literal>SQLExceptionConverterFactory</literal>ç±»æ¥äºè§£è¯¦æ
ï¼ãæ åç
+ <literal>JDBCException</literal>åç±»åæ¯ï¼
+ </para>
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>JDBCConnectionException</literal> - ææåºå±çJDBCé讯åºç°é误
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>SQLGrammarException</literal> - ææåéçSQLè¯å¥çè¯æ³æè
æ ¼å¼é误
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>ConstraintViolationException</literal> - æææç§ç±»åç约æè¿ä¾é误
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>LockAcquisitionException</literal> - ææäºå¨æ§è¡è¯·æ±æä½æ¶ï¼è·å
+ æéçé级å«æ¶åºç°çé误ã
+
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>GenericJDBCException</literal> - ä¸å±äºä»»ä½å
¶ä»ç§ç±»çåçå¼å¸¸
+ </para>
+ </listitem>
+ </itemizedlist>
+
+
+ </sect2>
+
+ <sect2 id="transactions-demarcation-timeout">
+ <title>äºå¡è¶
æ¶</title>
+
+ <para>
+ EJBè¿æ ·çæ管ç¯å¢æä¸é¡¹æ为éè¦çç¹æ§ï¼èå®ä»æªå¨éæ管ç¯å¢ä¸æä¾è¿ï¼é£å°±æ¯äºå¡è¶
æ¶ãå¨åºç°é误çäºå¡è¡ä¸ºçæ¶åï¼è¶
æ¶å¯ä»¥ç¡®ä¿ä¸ä¼æ éæèµ·èµæºã对ç¨æ·æ²¡æ交代ãå¨æ管(JTA)ç¯å¢ä¹å¤ï¼Hibernateæ æ³å®å
¨æä¾è¿ä¸åè½ãä½æ¯ï¼Hiberanteè³å°å¯ä»¥æ§å¶æ°æ®è®¿é®ï¼ç¡®ä¿æ°æ®åºçº§å«çæ»éï¼åè¿å巨大ç»æéçæ¥è¯¢è¢«éå®å¨ä¸ä¸ªè§å®çæ¶é´å
ãå¨æ管ç¯å¢ä¸ï¼Hibernateä¼æäºå¡è¶
æ¶è½¬äº¤ç»JTAãè¿ä¸åè½éè¿Hibernate <literal>Transaction</literal>对象è¿è¡æ½è±¡ã
+ </para>
+
+ <programlisting><![CDATA[
+Session sess = factory.openSession();
+try {
+ //set transaction timeout to 3 seconds
+ sess.getTransaction().setTimeout(3);
+ sess.getTransaction().begin();
+
+ // do some work
+ ...
+
+ sess.getTransaction().commit()
+}
+catch (RuntimeException e) {
+ sess.getTransaction().rollback();
+ throw e; // or display error message
+}
+finally {
+ sess.close();
+}]]></programlisting>
+
+ <para>
+ 注æ<literal>setTimeout()</literal>ä¸åºè¯¥å¨CMT beanä¸è°ç¨ï¼æ¤æ¶äºå¡è¶
æ¶å¼åºè¯¥æ¯è¢«å£°æå¼å®ä¹çã
+ </para>
+
+ </sect2>
+ </sect1>
+
+ <sect1 id="transactions-optimistic">
+ <title>ä¹è§å¹¶åæ§å¶(Optimistic concurrency control)</title>
+ <para>
+ å¯ä¸è½å¤åæ¶ä¿æé«å¹¶ååé«å¯ä¼¸ç¼©æ§çæ¹æ³å°±æ¯ä½¿ç¨å¸¦çæ¬åçä¹è§å¹¶åæ§å¶ãçæ¬æ£æ¥ä½¿ç¨çæ¬å·ã
+ æè
æ¶é´æ³æ¥æ£æµæ´æ°å²çªï¼å¹¶ä¸é²æ¢æ´æ°ä¸¢å¤±ï¼ãHibernate为使ç¨ä¹è§å¹¶åæ§å¶ç代ç æä¾äºä¸ç§å¯
+ è½çæ¹æ³ï¼åºç¨ç¨åºå¨ç¼åè¿äºä»£ç æ¶ï¼å¯ä»¥éç¨å®ä»¬ãæ们已ç»å¨åé¢åºç¨ç¨åºå¯¹è¯é£é¨åå±ç¤ºäº
+ ä¹è§å¹¶åæ§å¶çåºç¨åºæ¯ï¼æ¤å¤ï¼å¨å个æ°æ®åºäºå¡èå´å
ï¼çæ¬æ£æ¥ä¹æä¾äºé²æ¢æ´æ°ä¸¢å¤±ç好å¤ã
+ </para>
+
+ <sect2 id="transactions-optimistic-manual">
+ <title>åºç¨ç¨åºçº§å«ççæ¬æ£æ¥(Application version checking)</title>
+ <para>
+ æªè½å
åå©ç¨Hibernateåè½çå®ç°ä»£ç ä¸ï¼æ¯æ¬¡åæ°æ®åºäº¤äºé½éè¦ä¸ä¸ªæ°ç
+ <literal>Session</literal>ï¼èä¸å¼å人åå¿
é¡»å¨æ¾ç¤ºæ°æ®ä¹åä»æ°æ®åºä¸é
+ æ°è½½å
¥ææçæä¹
å对象å®ä¾ãè¿ç§æ¹å¼è¿«ä½¿åºç¨ç¨åºèªå·±å®ç°çæ¬æ£æ¥æ¥ç¡®ä¿
+ 对è¯äºå¡çé离ï¼ä»æ°æ®è®¿é®çè§åº¦æ¥è¯´æ¯æä½æçãè¿ç§ä½¿ç¨æ¹å¼å
+ entity EJBæç¸ä¼¼ã
+ </para>
+
+ <programlisting><![CDATA[// foo is an instance loaded by a previous Session
+session = factory.openSession();
+Transaction t = session.beginTransaction();
+
+int oldVersion = foo.getVersion();
+session.load( foo, foo.getKey() ); // load the current state
+if ( oldVersion!=foo.getVersion ) throw new StaleObjectStateException();
+foo.setProperty("bar");
+
+t.commit();
+session.close();]]></programlisting>
+
+ <para>
+ <literal>version</literal> å±æ§ä½¿ç¨ <literal><version></literal>æ¥æ å°ï¼å¦æ对象
+ æ¯èæ°æ®ï¼å¨åæ¥çæ¶åï¼Hibernateä¼èªå¨å¢å çæ¬å·ã
+ </para>
+
+ <para>
+ å½ç¶ï¼å¦æä½ çåºç¨æ¯å¨ä¸ä¸ªä½æ°æ®å¹¶åç¯å¢ä¸ï¼å¹¶ä¸éè¦çæ¬æ£æ¥çè¯ï¼ä½ ç
§æ ·å¯ä»¥ä½¿ç¨
+ è¿ç§æ¹å¼ï¼åªä¸è¿è·³è¿çæ¬æ£æ¥å°±æ¯äºãå¨è¿ç§æ
åµä¸ï¼<emphasis>æææ交çæ</emphasis>
+ ï¼<emphasis>last commit wins</emphasis>ï¼å°±æ¯ä½ çé¿å¯¹è¯çé»è®¤å¤ççç¥ã
+ 请记ä½è¿ç§çç¥å¯è½ä¼è®©åºç¨è½¯ä»¶çç¨æ·æå°å°æï¼å 为ä»ä»¬æå¯è½ä¼ç¢°ä¸æ´æ°ä¸¢å¤±æå´æ²¡
+ æåºéä¿¡æ¯ï¼æè
éè¦å并æ´æ¹å²çªçæ
åµã
+ </para>
+
+ <para>
+ å¾ææ¾ï¼æå·¥è¿è¡çæ¬æ£æ¥åªéåäºæäºè½¯ä»¶è§æ¨¡é常å°çåºç¨åºæ¯ï¼å¯¹äºå¤§å¤æ°è½¯ä»¶åºç¨åºæ¯
+ æ¥è¯´å¹¶ä¸ç°å®ãé常æ
åµä¸ï¼ä¸ä»
æ¯å个对象å®ä¾éè¦è¿è¡çæ¬æ£æ¥ï¼æ´ä¸ªè¢«ä¿®æ¹è¿çå
³
+ è对象å¾ä¹é½éè¦è¿è¡çæ¬æ£æ¥ãä½ä¸ºæ å设计èä¾ï¼Hibernate使ç¨æ©å±å¨æç
+ <literal>Session</literal>çæ¹å¼ï¼æè
è±ç®¡å¯¹è±¡å®ä¾çæ¹å¼æ¥æä¾èªå¨çæ¬æ£æ¥ã
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-optimistic-longsession">
+ <title>æ©å±å¨æçsessionåèªå¨çæ¬å</title>
+ <para>
+ å个 <literal>Session</literal>å®ä¾åå®æå
³èçæææä¹
å对象å®ä¾é½è¢«ç¨äºæ´ä¸ª
+ 对è¯ï¼è¿è¢«ç§°ä¸º<emphasis>session-per-conversation</emphasis>ãHibernateå¨åæ¥çæ¶åè¿è¡å¯¹è±¡å®ä¾ççæ¬æ£æ¥ï¼å¦ææ£æµå°å¹¶åä¿®
+ æ¹åæåºå¼å¸¸ãç±å¼å人åæ¥å³å®æ¯å¦éè¦æè·åå¤çè¿ä¸ªå¼å¸¸ï¼é常çææ©æ¯ç»ç¨æ·
+ æä¾ä¸ä¸ªå并æ´æ¹ï¼æè
å¨æ èæ°æ®æ
åµä¸éæ°è¿è¡ä¸å¡å¯¹è¯çæºä¼ï¼ã
+ </para>
+ <para>
+ å¨çå¾
ç¨æ·äº¤äºçæ¶åï¼ <literal>Session</literal> æå¼åºå±çJDBCè¿æ¥ãè¿ç§æ¹å¼
+ 以æ°æ®åºè®¿é®çè§åº¦æ¥è¯´æ¯æé«æçæ¹å¼ãåºç¨ç¨åºä¸éè¦å
³å¿çæ¬æ£æ¥æè±ç®¡å¯¹è±¡å®ä¾
+ çéæ°å
³èï¼å¨æ¯ä¸ªæ°æ®åºäºå¡ä¸ï¼åºç¨ç¨åºä¹ä¸éè¦è½½å
¥è¯»å对象å®ä¾ã
+ </para>
+
+ <programlisting><![CDATA[// foo is an instance loaded earlier by the old session
+Transaction t = session.beginTransaction(); // Obtain a new JDBC connection, start transaction
+
+foo.setProperty("bar");
+
+session.flush(); // Only for last transaction in conversation
+t.commit(); // Also return JDBC connection
+session.close(); // Only for last transaction in conversation]]></programlisting>
+
+ <para>
+ <literal>foo</literal>对象ç¥éå®æ¯å¨åªä¸ª<literal>Session</literal>ä¸è¢«è£
å
¥çãå¨ä¸ä¸ªæ§sessionä¸å¼å¯ä¸ä¸ªæ°çæ°æ®åºäºå¡ï¼ä¼å¯¼è´sessionè·åä¸ä¸ªæ°çè¿æ¥ï¼å¹¶æ¢å¤sessionçåè½ãå°æ°æ®åºäºå¡æ交ï¼ä½¿å¾sessionä»JDBCè¿æ¥æå¼ï¼å¹¶å°æ¤è¿æ¥äº¤è¿ç»è¿æ¥æ± ãå¨éæ°è¿æ¥ä¹åï¼è¦å¼ºå¶å¯¹ä½ 没ææ´æ°çæ°æ®è¿è¡ä¸æ¬¡çæ¬æ£æ¥ï¼ä½ å¯ä»¥å¯¹ææå¯è½è¢«å
¶ä»äºå¡ä¿®æ¹è¿ç对象ï¼ä½¿ç¨åæ°<literal>LockMode.READ</literal>æ¥è°ç¨<literal>Session.lock()</literal>ãä½ ä¸ç¨lockä»»ä½ä½ <emphasis>æ£å¨</emphasis>æ´æ°çæ°æ®ãä¸è¬ä½ ä¼å¨æ©å±ç<literal>Session</literal>ä¸è®¾ç½®<literal>FlushMode.NEVER</literal>ï¼å æ¤åªææåä¸ä¸ªæ°æ®åºäºå¡å¾ªç¯æä¼çæ£çå§æ´ä¸ªå¯¹è¯ä¸åççä¿®æ¹åéå°æ°æ®åºãå æ¤ï¼åªæè¿æåä¸æ¬¡æ°æ®åºäºå¡æä¼å
å«<literal>flush()</literal>æä½ï¼ç¶åå¨æ´ä¸ªå¯¹è¯ç»æåï¼è¿è¦<literal!
>close()</literal>è¿ä¸ªsessionã
+ </para>
+
+ <para>
+ å¦æå¨ç¨æ·æèçè¿ç¨ä¸ï¼<literal>Session</literal>å 为太大äºèä¸è½ä¿åï¼é£ä¹è¿ç§æ¨¡å¼æ¯æ
+ é®é¢çã举ä¾æ¥è¯´ï¼ä¸ä¸ª<literal>HttpSession</literal>åºè¯¥å°½å¯è½çå°ãç±äº
+ <literal>Session</literal>æ¯ä¸çº§ç¼åï¼å¹¶ä¸ä¿æäºææ被载å
¥è¿ç对象ï¼å æ¤
+ æ们åªåºè¯¥å¨é£äºå°éçrequest/responseæ
åµä¸ä½¿ç¨è¿ç§çç¥ãä½ åºè¯¥åªæä¸ä¸ª<literal>Session</literal>ç¨äºå个对è¯ï¼å 为å®å¾å¿«å°±ä¼åºç°èæ°æ®ã
+ </para>
+
+ <para>
+ ï¼æ³¨æï¼æ©æçHibernateçæ¬éè¦æç¡®ç对<literal>Session</literal>è¿è¡disconnecåreconnectãè¿äºæ¹æ³ç°å¨å·²ç»è¿æ¶äºï¼æå¼äºå¡åå
³éäºå¡ä¼èµ·å°åæ ·çææãï¼
+ </para>
+
+
+ <para>
+ æ¤å¤ï¼ä¹è¯·æ³¨æï¼ä½ åºè¯¥è®©ä¸æ°æ®åºè¿æ¥æå¼ç<literal>Session</literal>对æä¹
å±ä¿æ
+ å
³éç¶æãæ¢å¥è¯è¯´ï¼å¨ä¸å±ç¯å¢ä¸ï¼ä½¿ç¨æç¶æçEJB session beanæ¥ææ<literal>Session</literal>ï¼
+ èä¸è¦æå®ä¼ éå°webå±ï¼çè³æå®åºååå°ä¸ä¸ªåç¬çå±ï¼ï¼ä¿åå¨<literal>HttpSession</literal>ä¸ã
+ </para>
+
+ <para>
+ æ©å±session模å¼ï¼æè
被称为<emphasis>æ¯æ¬¡å¯¹è¯ä¸ä¸ªsession(session-per-conversation)</emphasis>, å¨ä¸èªå¨ç®¡çå½åsessionä¸ä¸æèç¨çæ¶åä¼æ´å°é¾ãä½ éè¦æä¾ä½ èªå·±ç<literal>CurrentSessionContext</literal>å®ç°ã请åé
Hibernate Wiki以è·å¾ç¤ºä¾ã
+ </para>
+
+
+ </sect2>
+
+ <sect2 id="transactions-optimistic-detached">
+ <title>è±ç®¡å¯¹è±¡(deatched object)åèªå¨çæ¬å</title>
+
+ <para>
+ è¿ç§æ¹å¼ä¸ï¼ä¸æä¹
ååå¨çæ¯æ¬¡äº¤äºé½åçå¨ä¸ä¸ªæ°ç<literal>Session</literal>ä¸ã
+ ç¶èï¼åä¸æä¹
å对象å®ä¾å¯ä»¥å¨å¤æ¬¡ä¸æ°æ®åºç交äºä¸éç¨ãåºç¨ç¨åºæ纵è±ç®¡å¯¹è±¡å®ä¾
+ çç¶æï¼è¿ä¸ªè±ç®¡å¯¹è±¡å®ä¾æåæ¯å¨å¦ä¸ä¸ª<literal>Session</literal> ä¸è½½å
¥çï¼ç¶å
+ è°ç¨ <literal>Session.update()</literal>ï¼<literal>Session.saveOrUpdate()</literal>, æè
+ <literal>Session.merge()</literal> æ¥éæ°å
³è该对象å®ä¾ã
+ </para>
+
+ <programlisting><![CDATA[// foo is an instance loaded by a previous Session
+foo.setProperty("bar");
+session = factory.openSession();
+Transaction t = session.beginTransaction();
+session.saveOrUpdate(foo); // Use merge() if "foo" might have been loaded already
+t.commit();
+session.close();]]></programlisting>
+
+ <para>
+ Hibernateä¼åä¸æ¬¡å¨åæ¥çæ¶åæ£æ¥å¯¹è±¡å®ä¾ççæ¬ï¼å¦æåçæ´æ°å²çªï¼å°±æåºå¼å¸¸ã
+ </para>
+
+ <para>
+ å¦æä½ ç¡®ä¿¡å¯¹è±¡æ²¡æ被修æ¹è¿ï¼ä½ ä¹å¯ä»¥è°ç¨<literal>lock()</literal> æ¥è®¾ç½®
+ <literal>LockMode.READ</literal>ï¼ç»è¿ææçç¼åï¼æ§è¡çæ¬æ£æ¥ï¼ï¼ä»èå
+ 代 <literal>update()</literal>æä½ã
+ </para>
+
+ </sect2>
+
+ <sect2 id="transactions-optimistic-customizing">
+ <title>å®å¶èªå¨çæ¬åè¡ä¸º</title>
+ <para>
+ 对äºç¹å®çå±æ§åéåï¼éè¿ä¸ºå®ä»¬è®¾ç½®æ å°å±æ§<literal>optimistic-lock</literal>çå¼
+ 为<literal>false</literal>ï¼æ¥ç¦æ¢Hibernateççæ¬èªå¨å¢å ãè¿æ ·çè¯ï¼å¦æ该å±æ§
+ èæ°æ®ï¼Hibernateå°ä¸åå¢å çæ¬å·ã
+ </para>
+
+ <para>
+ éçç³»ç»çæ°æ®åºSchemaé常æ¯éæçï¼ä¸å¯ä¿®æ¹çãæè
ï¼å
¶ä»åºç¨ç¨åºä¹å¯è½è®¿é®åä¸æ°æ®
+ åºï¼æ ¹æ¬æ æ³å¾ç¥å¦ä½å¤ççæ¬å·ï¼çè³æ¶é´æ³ãå¨ä»¥ä¸çææåºæ¯ä¸ï¼å®ç°çæ¬åä¸è½ä¾é
+ æ°æ®åºè¡¨çæ个ç¹å®åãå¨<literal><class></literal>çæ å°ä¸è®¾ç½®
+ <literal>optimistic-lock="all"</literal>å¯ä»¥å¨æ²¡æçæ¬æè
æ¶é´æ³å±æ§æ å°çæ
åµä¸å®ç°
+ çæ¬æ£æ¥ï¼æ¤æ¶Hibernateå°æ¯è¾ä¸è¡è®°å½çæ¯ä¸ªå段çç¶æã请注æï¼åªæå½Hibernateè½å¤æ¯
+ è¾æ°æ§ç¶æçæ
åµä¸ï¼è¿ç§æ¹å¼æè½çæï¼ä¹å°±æ¯è¯´ï¼
+ ä½ å¿
须使ç¨å个é¿çå½å¨æ<literal>Session</literal>模å¼ï¼èä¸è½ä½¿ç¨
+ session-per-request-with-detached-objects模å¼ã
+ </para>
+
+ <para>
+ æäºæ
åµä¸ï¼åªè¦æ´æ¹ä¸åç交éï¼å¹¶åä¿®æ¹ä¹æ¯å
许çãå½ä½ å¨<literal><class></literal>
+ çæ å°ä¸è®¾ç½®<literal>optimistic-lock="dirty"</literal>ï¼Hibernateå¨åæ¥çæ¶åå°åªæ¯è¾æè
+ æ°æ®çå段ã
+ </para>
+
+ <para>
+ å¨ä»¥ä¸ææåºæ¯ä¸ï¼ä¸ç®¡æ¯ä¸é¨è®¾ç½®ä¸ä¸ªçæ¬/æ¶é´æ³åï¼è¿æ¯è¿è¡å
¨é¨å段/èæ°æ®å段æ¯è¾ï¼
+ Hibernateé½ä¼é对æ¯ä¸ªå®ä½å¯¹è±¡åéä¸æ¡<literal>UPDATE</literal>ï¼å¸¦æç¸åºç
+ <literal>WHERE</literal>è¯å¥ ï¼çSQLè¯å¥æ¥æ§è¡çæ¬æ£æ¥åæ°æ®æ´æ°ãå¦æä½ å¯¹å
³èå®ä½
+ 设置级èå
³ç³»ä½¿ç¨ä¼ ææ§æä¹
åï¼transitive persistenceï¼ï¼é£ä¹Hibernateå¯è½ä¼æ§è¡ä¸å¿
+ è¦çupdateè¯å¥ãè¿é常ä¸æ¯ä¸ªé®é¢ï¼ä½æ¯æ°æ®åºéé¢å¯¹<emphasis>on update</emphasis>ç¹ç«
+ ç触åå¨å¯è½å¨è±ç®¡å¯¹è±¡æ²¡æä»»ä½æ´æ¹çæ
åµä¸è¢«è§¦åãå æ¤ï¼ä½ å¯ä»¥å¨
+ <literal><class></literal>çæ å°ä¸ï¼éè¿è®¾ç½®<literal>select-before-update="true"</literal>
+ æ¥å®å¶è¿ä¸è¡ä¸ºï¼å¼ºå¶Hibernate <literal>SELECT</literal>è¿ä¸ªå¯¹è±¡å®ä¾ï¼ä»èä¿è¯ï¼
+ å¨æ´æ°è®°å½ä¹åï¼å¯¹è±¡çç¡®æ¯è¢«ä¿®æ¹è¿ã
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="transactions-locking">
+ <title>æ²è§éå®(Pessimistic Locking)</title>
+ <para>
+ ç¨æ·å
¶å®å¹¶ä¸éè¦è±å¾å¤ç²¾åå»æ
å¿éå®çç¥çé®é¢ãé常æ
åµä¸ï¼åªè¦ä¸ºJDBCè¿æ¥æå®ä¸ä¸é
+ 离级å«ï¼ç¶å让æ°æ®åºå»æå®ä¸åå°±å¤äºãç¶èï¼é«çº§ç¨æ·ææ¶åå¸æè¿è¡ä¸ä¸ªæå®çæ²è§éå®ï¼
+ æè
å¨ä¸ä¸ªæ°çäºå¡å¯å¨çæ¶åï¼éæ°è¿è¡éå®ã
+ </para>
+
+ <para>
+ Hibernateæ»æ¯ä½¿ç¨æ°æ®åºçéå®æºå¶ï¼ä»ä¸å¨å
åä¸éå®å¯¹è±¡ï¼
+ </para>
+
+ <para>
+ ç±»<literal>LockMode</literal> å®ä¹äºHibernateæéçä¸åçéå®çº§å«ãä¸ä¸ªéå®
+ å¯ä»¥éè¿ä»¥ä¸çæºå¶æ¥è®¾ç½®:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ å½Hibernateæ´æ°æè
æå
¥ä¸è¡è®°å½çæ¶åï¼éå®çº§å«èªå¨è®¾ç½®ä¸º<literal>LockMode.WRITE</literal>ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ å½ç¨æ·æ¾å¼ç使ç¨æ°æ®åºæ¯æçSQLæ ¼å¼<literal>SELECT ... FOR UPDATE</literal>
+ åéSQLçæ¶åï¼éå®çº§å«è®¾ç½®ä¸º<literal>LockMode.UPGRADE</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ å½ç¨æ·æ¾å¼ç使ç¨Oracleæ°æ®åºçSQLè¯å¥<literal>SELECT ... FOR UPDATE NOWAIT</literal>
+ çæ¶åï¼éå®çº§å«è®¾ç½®<literal>LockMode.UPGRADE_NOWAIT</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ å½Hibernateå¨âå¯éå¤è¯»âæè
æ¯âåºååâæ°æ®åºé离级å«ä¸è¯»åæ°æ®çæ¶åï¼éå®æ¨¡å¼
+ èªå¨è®¾ç½®ä¸º<literal>LockMode.READ</literal>ãè¿ç§æ¨¡å¼ä¹å¯ä»¥éè¿ç¨æ·æ¾å¼æå®è¿è¡è®¾ç½®ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>LockMode.NONE</literal> 代表æ ééå®ãå¨<literal>Transaction</literal>ç»ææ¶ï¼
+ ææç对象é½åæ¢å°è¯¥æ¨¡å¼ä¸æ¥ãä¸sessionç¸å
³èç对象éè¿è°ç¨<literal>update()</literal>
+ æè
<literal>saveOrUpdate()</literal>è±ç¦»è¯¥æ¨¡å¼ã
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ "æ¾å¼çç¨æ·æå®"å¯ä»¥éè¿ä»¥ä¸å ç§æ¹å¼ä¹ä¸æ¥è¡¨ç¤º:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ è°ç¨ <literal>Session.load()</literal>çæ¶åæå®<literal>éå®æ¨¡å¼(LockMode)</literal>ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ è°ç¨<literal>Session.lock()</literal>ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ è°ç¨<literal>Query.setLockMode()</literal>ã
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ å¦æå¨<literal>UPGRADE</literal>æè
<literal>UPGRADE_NOWAIT</literal>éå®æ¨¡å¼ä¸è°
+ ç¨<literal>Session.load()</literal>ï¼å¹¶ä¸è¦è¯»åç对象å°æªè¢«sessionè½½å
¥è¿ï¼é£ä¹å¯¹è±¡
+ éè¿<literal>SELECT ... FOR UPDATE</literal>è¿æ ·çSQLè¯å¥è¢«è½½å
¥ãå¦æ为ä¸ä¸ªå¯¹è±¡è°ç¨
+ <literal>load()</literal>æ¹æ³æ¶ï¼è¯¥å¯¹è±¡å·²ç»å¨å¦ä¸ä¸ªè¾å°éå¶çéå®æ¨¡å¼ä¸è¢«è½½å
¥äºï¼é£
+ ä¹Hibernate就对该对象è°ç¨<literal>lock()</literal> æ¹æ³ã
+ </para>
+
+ <para>
+ å¦ææå®çéå®æ¨¡å¼æ¯<literal>READ</literal>, <literal>UPGRADE</literal> æ
+ <literal>UPGRADE_NOWAIT</literal>ï¼é£ä¹<literal>Session.lock()</literal>å°±
+ æ§è¡çæ¬å·æ£æ¥ãï¼å¨<literal>UPGRADE</literal> æè
<literal>UPGRADE_NOWAIT</literal>
+ éå®æ¨¡å¼ä¸ï¼æ§è¡<literal>SELECT ... FOR UPDATE</literal>è¿æ ·çSQLè¯å¥ãï¼
+ </para>
+
+ <para>
+ å¦ææ°æ®åºä¸æ¯æç¨æ·è®¾ç½®çéå®æ¨¡å¼ï¼Hibernateå°ä½¿ç¨éå½çæ¿ä»£æ¨¡å¼ï¼èä¸æ¯æåºå¼å¸¸ï¼ã
+ è¿ä¸ç¹å¯ä»¥ç¡®ä¿åºç¨ç¨åºçå¯ç§»æ¤æ§ã
+ </para>
+ </sect1>
+ <sect1 id="transactions-connection-release">
+ <title>è¿æ¥éæ¾æ¨¡å¼(Connection Release Modes)</title>
+
+ <para>
+ Hibernateå
³äºJDBCè¿æ¥ç®¡ççæ§(2.x)è¡ä¸ºæ¯ï¼<literal>Session</literal>å¨ç¬¬ä¸æ¬¡éè¦çæ¶åè·åä¸ä¸ªè¿æ¥ï¼å¨sessionå
³éä¹åä¸ç´ä¼ææè¿ä¸ªè¿æ¥ãHibernateå¼å
¥äºè¿æ¥éæ¾çæ¦å¿µï¼æ¥åè¯sessionå¦ä½å¤çå®çJDBCè¿æ¥ã注æï¼ä¸é¢ç讨论åªéç¨äºéç¨é
ç½®<literal>ConnectionProvider</literal>æ¥æä¾è¿æ¥çæ
åµï¼ç¨æ·èªå·±æä¾çè¿æ¥ä¸è¿éç讨论æ å
³ãéè¿<literal>org.hibernate.ConnectionReleaseMode</literal>çä¸åæ举å¼æ¥ä½¿ç¨ä¸ç¨çéæ¾æ¨¡å¼:
+
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>ON_CLOSE</literal> - åºæ¬ä¸å°±æ¯ä¸é¢æå°çèå¼è¡ä¸ºãHibernate sessionå¨ç¬¬ä¸æ¬¡éè¦è¿è¡JDBCæä½çæ¶åè·åè¿æ¥ï¼ç¶åææå®ï¼ç´å°sessionå
³éã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>AFTER_TRANSACTION</literal> - å¨<literal>org.hibernate.Transaction</literal>ç»æåéæ¾è¿æ¥ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>AFTER_STATEMENT</literal> (ä¹è¢«ç§°å积æéæ¾) - å¨æ¯ä¸æ¡è¯å¥è¢«æ§è¡åå°±éæ¾è¿æ¥ãä½åè¥è¯å¥çä¸äºä¸sessionç¸å
³çèµæºï¼é£å°±ä¸ä¼è¢«éæ¾ãç®åå¯ä¸çè¿ç§æ
形就æ¯ä½¿ç¨<literal>org.hibernate.ScrollableResults</literal>ã
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ <literal>hibernate.connection.release_mode</literal>é
ç½®åæ°ç¨æ¥æå®ä½¿ç¨åªä¸ç§éæ¾æ¨¡å¼ãå¯è½çå¼æï¼
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ <literal>auto</literal>(é»è®¤) - è¿ä¸éæ©æéæ¾æ¨¡å¼å§æ´¾ç»<literal>org.hibernate.transaction.TransactionFactory.getDefaultReleaseMode()</literal>æ¹æ³ã对JTATransactionFactoryæ¥è¯´ï¼å®ä¼è¿åConnectionReleaseMode.AFTER_STATEMENT;对JDBCTransactionFactoryæ¥è¯´ï¼åæ¯ConnectionReleaseMode.AFTER_TRANSACTIONãå¾å°éè¦ä¿®æ¹è¿ä¸é»è®¤è¡ä¸ºï¼å 为åè¥è®¾ç½®ä¸å½ï¼å°±ä¼å¸¦æ¥bugï¼æè
ç»ç¨æ·ä»£ç 带æ¥è¯¯å¯¼ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>on_close</literal> - ä½¿ç¨ ConnectionReleaseMode.ON_CLOSE. è¿ç§æ¹å¼æ¯ä¸ºäºåä¸å
¼å®¹ç,ä½æ¯å·²ç»å®å
¨ä¸è¢«é¼å±ä½¿ç¨äºã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>after_transaction</literal> - 使ç¨ConnectionReleaseMode.AFTER_TRANSACTIONãè¿ä¸è®¾ç½®ä¸åºè¯¥å¨JTAç¯å¢ä¸ä½¿ç¨ãä¹è¦æ³¨æï¼ä½¿ç¨ConnectionReleaseMode.AFTER_TRANSACTIONçæ¶åï¼åè¥session å¤äºauto-commitç¶æï¼è¿æ¥ä¼åAFTER_STATEMENTé£æ ·è¢«éæ¾ã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>after_statement</literal> - 使ç¨ConnectionReleaseMode.AFTER_STATEMENTãé¤æ¤ä¹å¤ï¼ä¼æ¥è¯¢é
ç½®ç<literal>ConnectionProvider</literal>ï¼æ¯å¦å®æ¯æè¿ä¸è®¾ç½®ï¼(<literal>supportsAggressiveRelease()</literal>)ï¼ãåè¥ä¸æ¯æï¼éæ¾æ¨¡å¼ä¼è¢«è®¾ç½®ä¸ºConnectionReleaseMode.AFTER_TRANSACTIONãåªæå¨ä½ æ¯æ¬¡è°ç¨<literal>ConnectionProvider.getConnection()</literal>è·ååºå±JDBCè¿æ¥çæ¶åï¼é½å¯ä»¥ç¡®ä¿¡è·å¾åä¸ä¸ªè¿æ¥çæ¶åï¼è¿ä¸è®¾ç½®ææ¯å®å
¨çï¼æè
å¨auto-commitç¯å¢ä¸ï¼ä½ å¯ä»¥ä¸ç®¡æ¯å¦æ¯æ¬¡é½è·å¾åä¸ä¸ªè¿æ¥çæ¶åï¼è¿ææ¯å®å
¨çã
+
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect1>
+
+</chapter>
+
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/tutorial.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/tutorial.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/tutorial.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1233 @@
+<chapter id="tutorial">
+ <title>
+ Hibernateå
¥é¨
+ </title>
+ <sect1 id="tutorial-intro" revision="1">
+ <title>
+ åè¨
+ </title>
+
+ <para>
+ æ¬ç« æ¯é¢åHibernateåå¦è
çä¸ä¸ªå
¥é¨æç¨ãæ们ä»ä¸ä¸ªä½¿ç¨é©»çå
åå¼(in-memory)æ°æ®åºçç®åå½ä»¤è¡åºç¨ç¨åºå¼å§, ç¨æäºç解çæ¹å¼éæ¥å¼åã
+
+ </para>
+
+ <para>
+ æ¬ç« é¢åHibernateåå¦è
ï¼ä½éè¦JavaåSQLç¥è¯ãå®æ¯å¨Michael Goeglæåçæåçåºç¡ä¸å®æçãå¨è¿éï¼æ们称第ä¸æ¹åºæ件æ¯æJDK 1.4å5.0ãè¥ä½¿ç¨JDK1.3ï¼ä½ å¯è½éè¦å
¶å®çåºæ件ã
+ </para>
+
+ <para>
+ æ¬ç« çæºä»£ç å·²å
å«å¨åå¸å
ä¸ï¼ä½äº<literal>doc/reference/tutorial/</literal>ç®å½ä¸ã
+ </para>
+
+ </sect1>
+
+ <sect1 id="tutorial-firstapp" revision="2">
+ <title>
+ 第ä¸é¨å ï¼ ç¬¬ä¸ä¸ªHibernateåºç¨ç¨åº
+ </title>
+ <para>
+ é¦å
æ们å°å建ä¸ä¸ªç®åçåºäºæ§å¶å°ç(console-based)Hibernateåºç¨ç¨åºãç±äºæ们使ç¨Javaæ°æ®åº(HSQL DB)ï¼æ以ä¸å¿
å®è£
ä»»ä½æ°æ®åºæå¡å¨ã
+ </para>
+
+ <para>
+ å设æ们å¸ææä¸ä¸ªå°åºç¨ç¨åºå¯ä»¥ä¿åæ们å¸æåå çæ´»å¨ï¼eventsï¼åè¿äºæ´»å¨ä¸»åæ¹çç¸å
³ä¿¡æ¯ã
+ ï¼è¯è
注ï¼å¨æ¬æç¨çåé¢é¨åï¼æ们å°ç´æ¥ä½¿ç¨eventèä¸æ¯å®çä¸æç¿»è¯âæ´»å¨âï¼ä»¥å
æ··æ·ãï¼
+ </para>
+
+ <para>
+ æ们æåç第ä¸ä»¶äºå°±æ¯å建æ们çå¼åç®å½ï¼å¹¶ä¸æææéè¦ç¨å°çJavaåºæ件æ¾è¿å»ã解å缩ä»Hibernateç½ç«ä¸è½½çHibernateåå¸å
ï¼å¹¶æ<literal>/lib</literal>ç®å½ä¸ææéè¦çåºæ件æ·å°æ们æ°å»ºå¼åç®å½ä¸ç<literal>/lib</literal>ç®å½ä¸ãçèµ·æ¥å°±åè¿æ ·ï¼
+ </para>
+
+ <programlisting><![CDATA[.
++lib
+ antlr.jar
+ cglib.jar
+ asm.jar
+ asm-attrs.jars
+ commons-collections.jar
+ commons-logging.jar
+ ehcache.jar
+ hibernate3.jar
+ jta.jar
+ dom4j.jar
+ log4j.jar ]]></programlisting>
+
+ <para>
+ <emphasis>å°ç¼åæ¬ææ¶ä¸ºæ¢</emphasis>ï¼è¿äºæ¯Hibernateè¿è¡æéè¦çæå°åºæ件éåï¼æ³¨ææ们ä¹æ·è´äº Hibernate3.jarï¼è¿ä¸ªæ¯æ主è¦çæ件ï¼ãä½ æ£ä½¿ç¨çHibernateçæ¬å¯è½éè¦æ¯è¿æ´å¤æå°ä¸äºçåºæ件ã请åè§åå¸å
ä¸ç<literal>lib/</literal>ç®å½ä¸ç<literal>README.txt</literal>ï¼ä»¥è·åæ´å¤å
³äºæéåå¯éç第ä¸æ¹åºæ件信æ¯ï¼äºå®ä¸ï¼Log4j并ä¸æ¯å¿
é¡»çåºæ件ï¼ä½è¢«è®¸å¤å¼åè
æå欢ï¼ã
+
+ </para>
+
+ <para>
+ æ¥ä¸æ¥æ们å建ä¸ä¸ªç±»ï¼ç¨æ¥ä»£è¡¨é£äºæ们å¸æå¨åå¨æ°æ®åºéçeventã
+ </para>
+
+ <sect2 id="tutorial-firstapp-firstclass" revision="1">
+ <title>
+ 第ä¸ä¸ªclass
+ </title>
+
+ <para>
+ æ们ç第ä¸ä¸ªæä¹
åç±»æ¯ä¸ä¸ªå¸¦æä¸äºå±æ§ï¼propertyï¼çç®åJavaBeanç±»ï¼
+ </para>
+
+ <programlisting><![CDATA[package events;
+
+import java.util.Date;
+
+public class Event {
+ private Long id;
+
+ private String title;
+ private Date date;
+
+ public Event() {}
+
+ public Long getId() {
+ return id;
+ }
+
+ private void setId(Long id) {
+ this.id = id;
+ }
+
+ public Date getDate() {
+ return date;
+ }
+
+ public void setDate(Date date) {
+ this.date = date;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+}]]></programlisting>
+
+ <para>
+ ä½ å¯ä»¥çå°è¿ä¸ªç±»å¯¹å±æ§çååæ¹æ³ï¼getter and setter methodï¼ä½¿ç¨äºæ åJavaBeanå½å约å®ï¼åæ¶æç±»å±æ§ï¼fieldï¼ç访é®çº§å«è®¾æç§æçï¼privateï¼ãè¿æ¯æ¨èç设计ï¼ä½å¹¶ä¸æ¯å¿
é¡»çãHibernateä¹å¯ä»¥ç´æ¥è®¿é®è¿äºfieldï¼è使ç¨è®¿é®æ¹æ³ï¼accessor methodï¼ç好å¤æ¯æä¾äºéææ¶çå¥å£®æ§ï¼robustnessï¼ã为äºéè¿åå°æºå¶ï¼Reflectionï¼æ¥å®ä¾åè¿ä¸ªç±»ç对象ï¼æ们éè¦æä¾ä¸ä¸ªæ åçæé å¨ï¼no-argument constructor)ã
+ </para>
+
+ <para>
+ 对ä¸ç¹å®çevent, <literal>id</literal> å±æ§ææå¯ä¸çæ è¯ç¬¦ï¼identifierï¼çå¼ãå¦ææ们å¸æ使ç¨Hibernateæä¾çææç¹æ§ï¼é£ä¹ææçæä¹
åå®ä½ï¼persistent entityï¼ç±»ï¼è¿éä¹å
æ¬ä¸äºæ¬¡è¦ä¾èµç±»ï¼é½éè¦ä¸ä¸ªè¿æ ·çæ è¯ç¬¦å±æ§ãèäºå®ä¸ï¼å¤§å¤æ°åºç¨ç¨åºï¼ç¹å«æ¯webåºç¨ç¨åºï¼é½éè¦éè¿æ è¯ç¬¦æ¥åºå«å¯¹è±¡ï¼æä»¥ä½ åºè¯¥èè使ç¨æ è¯ç¬¦å±æ§èä¸æ¯æå®å½ä½ä¸ç§éå¶ãç¶èï¼æ们é常ä¸ä¼æä½å¯¹è±¡çæ è¯ï¼identityï¼ï¼å æ¤å®çsetteræ¹æ³ç访é®çº§å«åºè¯¥å£°æprivateãè¿æ ·å½å¯¹è±¡è¢«ä¿åçæ¶åï¼åªæHibernateå¯ä»¥ä¸ºå®åé
æ è¯ç¬¦å¼ãä½ å¯çå°Hibernateå¯ä»¥ç´æ¥è®¿é®publicï¼privateåprotectedç访é®æ¹æ³åfieldãæ以éæ©åªç§æ¹å¼å®å
¨åå³äºä½ ï¼ä½ å¯ä»¥ä½¿ä½ çéæ©ä¸ä½ çåºç¨ç¨åºè®¾è®¡ç¸å»åã
+ </para>
+
+ <para>
+ ææçæä¹
åç±»ï¼persistent classesï¼é½è¦æ±ææ åçæé å¨ï¼å 为Hibernateå¿
须使ç¨Javaåå°æºå¶æ¥ä¸ºä½ å建对象ãæé å¨ï¼constructorï¼ç访é®çº§å«å¯ä»¥æ¯privateï¼ç¶èå½çæè¿è¡æ¶ä»£çï¼runtime proxyï¼çæ¶ååè¦æ±ä½¿ç¨è³å°æ¯package 级å«ç访é®æ§å¶ï¼è¿æ ·å¨æ²¡æåèç æ令ï¼bytecode instrumentationï¼çæ
åµä¸ï¼ä»æä¹
åç±»éè·åæ°æ®ä¼æ´ææçã
+ </para>
+
+ <para>
+ æè¿ä¸ªJavaæºä»£ç æ件æ¾å°å¼åç®å½ä¸ç<literal>src</literal>ç®å½éï¼æ³¨æå
ä½ç½®è¦æ£ç¡®ã ç°å¨è¿ä¸ªç®å½çèµ·æ¥åºè¯¥åè¿æ ·ï¼
+ </para>
+
+ <programlisting><![CDATA[.
++lib
+ <Hibernate and third-party libraries>
++src
+ +events
+ Event.java]]></programlisting>
+
+ <para>
+ ä¸ä¸æ¥ï¼æ们æè¿ä¸ªæä¹
åç±»çä¿¡æ¯åè¯Hibernateã
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-mapping" revision="1">
+ <title>
+ æ å°æ件
+ </title>
+
+ <para>
+ Hibernateéè¦ç¥éææ ·å»å è½½ï¼loadï¼ååå¨ï¼storeï¼æä¹
åç±»ç对象ãè¿æ£æ¯Hibernateæ å°æ件åæ¥ä½ç¨çå°æ¹ãæ å°æ件åè¯Hibernateå®ï¼åºè¯¥è®¿é®æ°æ®åº(database)éé¢çåªä¸ªè¡¨ï¼tableï¼ååºè¯¥ä½¿ç¨è¡¨éé¢çåªäºå段ï¼columnï¼ã
+
+ </para>
+
+ <para>
+ ä¸ä¸ªæ å°æ件çåºæ¬ç»æçèµ·æ¥åè¿æ ·ï¼
+ </para>
+
+ <programlisting><![CDATA[<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping>
+[...]
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ 注æHibernateçDTDæ¯é常å¤æçãä½ çç¼è¾å¨æè
IDEé使ç¨å®æ¥èªå¨å®æé£äºç¨æ¥æ å°çXMLå
ç´ ï¼elementï¼åå±æ§ï¼attributeï¼ãä½ ä¹å¯ä»¥å¨ææ¬ç¼è¾å¨éæå¼DTDï¼è¿æ¯æç®åçæ¹å¼æ¥æ¦è§ææçå
ç´ åattributeï¼å¹¶æ¥çå®ä»¬ç缺çå¼ä»¥å注éã注æHibernateä¸ä¼ä»webå è½½DTDæ件ï¼ä½å®ä¼é¦å
å¨åºç¨ç¨åºçclasspathä¸æ¥æ¾ãDTDæ件已å
æ¬å¨<literal>hibernate3.jar</literal>éï¼åæ¶ä¹å¨Hibernateåå¸å
ç<literal>src/</literal>ç®å½ä¸ã
+ </para>
+
+ <para>
+ 为缩ç代ç é¿åº¦ï¼å¨ä»¥åçä¾åéæ们ä¼çç¥DTDç声æãå½ç¶ï¼å¨å®é
çåºç¨ç¨åºä¸ï¼DTD声ææ¯å¿
é¡»çã
+ </para>
+
+ <para>
+ å¨<literal>hibernate-mapping</literal>æ ç¾ï¼tagï¼ä¹é´, å«æä¸ä¸ª<literal>class</literal>å
ç´ ãææçæä¹
åå®ä½ç±»ï¼å次声æï¼æ许æ¥ä¸æ¥ä¼æä¾èµç±»ï¼å°±æ¯é£äºæ¬¡è¦çå®ä½ï¼é½éè¦ä¸ä¸ªè¿æ ·çæ å°ï¼æ¥æ类对象æ å°å°SQLæ°æ®åºéç表ã
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="events.Event" table="EVENTS">
+
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ å°ç®å为æ¢ï¼æ们åè¯äºHibernateææ ·æ<literal>Events</literal>ç±»ç对象æä¹
åå°æ°æ®åºç<literal>EVENTS</literal>表éï¼ä»¥åææ ·ä»<literal>EVENTS</literal>表å è½½å°<literal>Events</literal>ç±»ç对象ãæ¯ä¸ªå®ä¾å¯¹åºçæ°æ®åºè¡¨ä¸çä¸è¡ãç°å¨æ们å°ç»§ç»è®¨è®ºæå
³å¯ä¸æ è¯ç¬¦å±æ§å°æ°æ®åºè¡¨çæ å°ãå¦å¤ï¼ç±äºæ们ä¸å
³å¿ææ ·å¤çè¿ä¸ªæ è¯ç¬¦ï¼æ们就é
ç½®ç±Hibernateçæ è¯ç¬¦çæçç¥æ¥äº§ç代ç主é®å段ã
+
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="events.Event" table="EVENTS">
+ <id name="id" column="EVENT_ID">
+ <generator class="native"/>
+ </id>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ <literal>id</literal>å
ç´ æ¯æ è¯ç¬¦å±æ§ç声æï¼<literal>name="id"</literal> 声æäºJavaå±æ§çåå ï¼ Hibernateä¼ä½¿ç¨<literal>getId()</literal>å<literal>setId()</literal>æ¥è®¿é®å®ã <literal>column</literal>å±æ§ååè¯Hibernate, æ们使ç¨<literal>EVENTS</literal>表çåªä¸ªå段ä½ä¸ºä¸»é®ãåµå¥ç<literal>generator</literal>å
ç´ æå®äºæ è¯ç¬¦çæçç¥ï¼å¨è¿éæ们æå®<literal>native</literal>ï¼å®æ ¹æ®å·²é
ç½®çæ°æ®åºï¼æ¹è¨ï¼èªå¨éæ©æä½³çæ è¯ç¬¦çæçç¥ãHibernateæ¯æç±æ°æ®åºçæï¼å
¨å±å¯ä¸æ§ï¼globally uniqueï¼ååºç¨ç¨åºæå®ï¼æè
ä½ èªå·±ä¸ºä»»ä½å·²æçç¥æåçæ©å±ï¼è¿äºçç¥æ¥çææ è¯ç¬¦ã
+ </para>
+
+ <para>
+ æåæ们å¨æ å°æ件éé¢å
å«éè¦æä¹
åå±æ§ç声æãé»è®¤æ
åµä¸ï¼ç±»éé¢çå±æ§é½è¢«è§ä¸ºéæä¹
åçï¼
+ </para>
+
+ <programlisting><![CDATA[
+<hibernate-mapping>
+
+ <class name="events.Event" table="EVENTS">
+ <id name="id" column="EVENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="date" type="timestamp" column="EVENT_DATE"/>
+ <property name="title"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ å<literal>id</literal>å
ç´ ä¸æ ·ï¼<literal>property</literal>å
ç´ ç<literal>name</literal>å±æ§åè¯Hibernate使ç¨åªä¸ªgetteråsetteræ¹æ³ãå¨æ¤ä¾ä¸ï¼Hibernateä¼å¯»æ¾<literal>getDate()/setDate()</literal>, 以å<literal>getTitle()/setTitle()</literal>ã
+ </para>
+
+ <para>
+ 为ä»ä¹<literal>date</literal>å±æ§çæ å°å«æ<literal>column</literal> attributeï¼è<literal>title</literal>å´æ²¡æï¼å½æ²¡æ设å®<literal>column</literal> attribute çæ¶åï¼Hibernate缺çå°ä½¿ç¨JavaBeançå±æ§åä½ä¸ºå段åã对äº<literal>title</literal>ï¼è¿æ ·å·¥ä½å¾å¾å¥½ãç¶èï¼<literal>date</literal>å¨å¤æ°çæ°æ®åºéï¼æ¯ä¸ä¸ªä¿çå
³é®åï¼æ以æ们æ好æå®æ å°æä¸ä¸ªä¸åçååã
+
+ </para>
+
+ <para>
+ å¦ä¸æ趣çäºæ
æ¯<literal>title</literal>å±æ§ç¼ºå°ä¸ä¸ª<literal>type</literal> attributeãæ们å¨æ å°æ件é声æ并使ç¨çç±»åï¼å´ä¸æ¯æ们ææçé£æ ·ï¼æ¯Javaæ°æ®ç±»åï¼åæ¶ä¹ä¸æ¯SQLæ°æ®åºçæ°æ®ç±»åãè¿äºç±»åå°±æ¯æè°çHibernate æ å°ç±»å<emphasis>ï¼mapping typesï¼</emphasis>ï¼å®ä»¬è½æJavaæ°æ®ç±»å转æ¢å°SQLæ°æ®ç±»åï¼åä¹äº¦ç¶ãå次éç³ï¼å¦æå¨æ å°æ件ä¸æ²¡æ设置<literal>type</literal>å±æ§çè¯ï¼Hibernateä¼èªå·±è¯çå»ç¡®å®æ£ç¡®ç转æ¢ç±»ååå®çæ å°ç±»åãå¨æäºæ
åµä¸è¿ä¸ªèªå¨æ£æµæºå¶ï¼å¨Java ç±»ä¸ä½¿ç¨åå°æºå¶ï¼ä¸ä¼äº§çä½ ææå¾
æéè¦ç缺çå¼ã<literal>date</literal>å±æ§å°±æ¯ä¸ªå¾å¥½çä¾åï¼Hibernateæ æ³ç¥éè¿ä¸ªå±æ§ï¼<literal>java.util.Date</literal>ç±»åçï¼åºè¯¥è¢«æ å°æï¼SQL <literal>date</literal>ï¼æ<literal>timestamp</literal>ï¼è¿æ¯<literal>time</literal> å段ãå¨æ¤ä¾ä¸ï¼!
æè¿ä¸ªå±æ§æ å°æ<literal>timestamp</literal> 转æ¢å¨ï¼è¿æ ·æ们é¢çäºæ¥æåæ¶é´çå
¨é¨ä¿¡æ¯ã
+ </para>
+
+ <para>
+ åºè¯¥æè¿ä¸ªæ å°æ件ä¿å为<literal>Event.hbm.xml</literal>ï¼ä¸å°±å¨<literal>Event</literal>Javaç±»çæºæ件ç®å½ä¸ãæ å°æ件å¯éæå°å½åï¼ä½<literal>hbm.xml</literal>çåç¼å·²æ为Hibernateå¼åè
社åºç约å®ãç°å¨ç®å½ç»æçèµ·æ¥åºè¯¥åè¿æ ·ï¼
+ </para>
+
+ <programlisting><![CDATA[.
++lib
+ <Hibernate and third-party libraries>
++src
+ +events
+ Event.java
+ Event.hbm.xml]]></programlisting>
+
+ <para>
+ æ们继ç»è¿è¡Hibernateç主è¦é
ç½®ã
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-configuration" revision="2">
+ <title>
+ Hibernateé
ç½®
+ </title>
+
+ <para>
+ ç°å¨æ们已ç»æäºä¸ä¸ªæä¹
åç±»åå®çæ å°æ件ï¼è¯¥æ¯é
ç½®Hibernateçæ¶åäºãå¨æ¤ä¹åï¼æ们éè¦ä¸ä¸ªæ°æ®åºã HSQL DBæ¯ç§åºäºJava çSQLæ°æ®åºç®¡çç³»ç»ï¼DBMSï¼ï¼å¯ä»¥ä»HSQL DBçç½ç«ä¸ä¸è½½ãå®é
ä¸ï¼ä½ åªéä¸è½½çå
ä¸ç<literal>hsqldb.jar</literal>æ件ï¼å¹¶æè¿ä¸ªæ件æ¾å¨å¼åæ件夹ç<literal>lib/</literal>ç®å½ä¸å³å¯ã
+
+ </para>
+
+ <para>
+ å¨å¼åçæ ¹ç®å½ä¸å建ä¸ä¸ª<literal>data</literal>ç®å½ ï¼ è¿æ¯HSQL DBåå¨æ°æ®æ件çå°æ¹ãæ¤æ¶å¨dataç®å½ä¸è¿è¡<literal>java -classpath ../lib/hsqldb.jar org.hsqldb.Server</literal>å°±å¯å¯å¨æ°æ®åºãä½ å¯ä»¥å¨logä¸çå°å®çå¯å¨ï¼åç»å®å°TCP/IPå¥ç»åï¼è¿æ£æ¯æ们çåºç¨ç¨åºç¨åä¼è¿æ¥çå°æ¹ãå¦æä½ å¸æå¨æ¬ä¾ä¸è¿è¡ä¸ä¸ªå
¨æ°çæ°æ®åºï¼å°±å¨çªå£ä¸æä¸<literal>CTRL + C</literal>æ¥å
³éHSQLæ°æ®åºï¼å¹¶å é¤<literal>data/</literal>ç®å½ä¸çæææ件ï¼åéæ°å¯å¨HSQLæ°æ®åºã
+ </para>
+
+ <para>
+ Hibernateæ¯ä½ çåºç¨ç¨åºéè¿æ¥æ°æ®åºçé£å±ï¼æ以å®éè¦è¿æ¥ç¨çä¿¡æ¯ãè¿æ¥ï¼connectionï¼æ¯éè¿ä¸ä¸ªä¹ç±æ们é
ç½®çJDBCè¿æ¥æ± ï¼connection poolï¼æ¥å®æçãHibernateçåå¸å
éå
å«äºè®¸å¤å¼æºçï¼open sourceï¼è¿æ¥æ± ï¼ä½å¨æ们ä¾åä¸ä½¿ç¨Hibernateå
ç½®çè¿æ¥æ± ã注æï¼å¦æä½ å¸æ使ç¨ä¸ä¸ªäº§å级(production-quality)ç第ä¸æ¹è¿æ¥æ± 软件ï¼ä½ å¿
é¡»æ·è´æéçåºæ件å°ä½ çclasspathä¸ï¼å¹¶ä½¿ç¨ä¸åçè¿æ¥æ± 设置ã
+ </para>
+
+ <para>
+ 为äºä¿åHibernateçé
ç½®ï¼æ们å¯ä»¥ä½¿ç¨ä¸ä¸ªç®åç<literal>hibernate.properties</literal>æ件ï¼æè
ä¸ä¸ªç¨å¾®å¤æç<literal>hibernate.cfg.xml</literal>ï¼çè³å¯ä»¥å®å
¨ä½¿ç¨ç¨åºæ¥é
ç½®Hibernateãå¤æ°ç¨æ·æ´å欢使ç¨XMLé
ç½®æ件ï¼
+ </para>
+
+ <programlisting><![CDATA[<?xml version='1.0' encoding='utf-8'?>
+<!DOCTYPE hibernate-configuration PUBLIC
+ "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
+
+<hibernate-configuration>
+
+ <session-factory>
+
+ <!-- Database connection settings -->
+ <property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
+ <property name="connection.url">jdbc:hsqldb:hsql://localhost</property>
+ <property name="connection.username">sa</property>
+ <property name="connection.password"></property>
+
+ <!-- JDBC connection pool (use the built-in) -->
+ <property name="connection.pool_size">1</property>
+
+ <!-- SQL dialect -->
+ <property name="dialect">org.hibernate.dialect.HSQLDialect</property>
+
+ <!-- Enable Hibernate's automatic session context management -->
+ <property name="current_session_context_class">thread</property>
+
+ <!-- Disable the second-level cache -->
+ <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
+
+ <!-- Echo all executed SQL to stdout -->
+ <property name="show_sql">true</property>
+
+ <!-- Drop and re-create the database schema on startup -->
+ <property name="hbm2ddl.auto">create</property>
+
+ <mapping resource="events/Event.hbm.xml"/>
+
+ </session-factory>
+
+</hibernate-configuration>]]></programlisting>
+
+ <para>
+ 注æè¿ä¸ªXMLé
置使ç¨äºä¸ä¸ªä¸åçDTDãå¨è¿éï¼æ们é
ç½®äºHibernateç<literal>SessionFactory</literal>ï¼ä¸ä¸ªå
³èäºç¹å®æ°æ®åºå
¨å±çå·¥åï¼factoryï¼ãå¦æä½ è¦ä½¿ç¨å¤ä¸ªæ°æ®åºï¼å°±è¦ç¨å¤ä¸ªç<literal><session-factory></literal>ï¼é常æå®ä»¬æ¾å¨å¤ä¸ªé
ç½®æ件ä¸ï¼ä¸ºäºæ´å®¹æå¯å¨ï¼ã
+ </para>
+
+ <para>
+ æå¼å§ç4个<literal>property</literal>å
ç´ å
å«å¿
è¦çJDBCè¿æ¥ä¿¡æ¯ãæ¹è¨ï¼dialectï¼ç<literal>property</literal>å
ç´ ææHibernate çæçç¹å®SQLåéãä½ å¾å¿«ä¼çå°ï¼Hibernate对æä¹
åä¸ä¸æçèªå¨session管çå°±ä¼æ´¾ä¸ç¨åºã æå¼<literal>hbm2ddl.auto</literal>é项å°èªå¨çææ°æ®åºæ¨¡å¼ï¼schemaï¼ï¼ ç´æ¥å å
¥æ°æ®åºä¸ãå½ç¶è¿ä¸ªé项ä¹å¯ä»¥è¢«å
³éï¼éè¿å»é¤è¿ä¸ªé
ç½®é项ï¼æè
éè¿Antä»»å¡<literal>SchemaExport</literal>ç帮å©æ¥ææ°æ®åºschemaéå®åå°æ件ä¸ãæåï¼å¨é
ç½®ä¸ä¸ºæä¹
åç±»å å
¥æ å°æ件ã
+ </para>
+
+ <para>
+ æè¿ä¸ªæ件æ·è´å°æºä»£ç ç®å½ä¸é¢ï¼è¿æ ·å®å°±ä½äºclasspathçæ ¹ç®å½çæåãHibernateå¨å¯å¨æ¶ä¼èªå¨å¨classpathçæ ¹ç®å½æ¥æ¾å为<literal>hibernate.cfg.xml</literal>çé
ç½®æ件ã
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-ant" revision="1">
+ <title>
+ ç¨Antæ建
+ </title>
+
+ <para>
+ ç°å¨æ们ç¨Antæ¥æ建åºç¨ç¨åºãä½ å¿
é¡»å
å®è£
Antï¼å¯ä»¥ä»<ulink url="http://ant.apache.org/bindownload.cgi">Ant ä¸è½½é¡µé¢</ulink>å¾å°å®ãææ ·å®è£
Antå°±ä¸å¨è¿éä»ç»äºï¼è¯·åè<ulink url="http://ant.apache.org/manual/index.html">Ant ç¨æ·æå</ulink>ãå½ä½ å®è£
å®äºAntï¼å°±å¯ä»¥å¼å§å建<literal>build.xml</literal>æ件ï¼æå®ç´æ¥æ¾å¨å¼åç®å½ä¸é¢ã
+ </para>
+ <para>
+ ä¸ä¸ªç®åçbuildæ件çèµ·æ¥åè¿æ ·ï¼
+ </para>
+
+ <programlisting><![CDATA[<project name="hibernate-tutorial" default="compile">
+
+ <property name="sourcedir" value="${basedir}/src"/>
+ <property name="targetdir" value="${basedir}/bin"/>
+ <property name="librarydir" value="${basedir}/lib"/>
+
+ <path id="libraries">
+ <fileset dir="${librarydir}">
+ <include name="*.jar"/>
+ </fileset>
+ </path>
+
+ <target name="clean">
+ <delete dir="${targetdir}"/>
+ <mkdir dir="${targetdir}"/>
+ </target>
+
+ <target name="compile" depends="clean, copy-resources">
+ <javac srcdir="${sourcedir}"
+ destdir="${targetdir}"
+ classpathref="libraries"/>
+ </target>
+
+ <target name="copy-resources">
+ <copy todir="${targetdir}">
+ <fileset dir="${sourcedir}">
+ <exclude name="**/*.java"/>
+ </fileset>
+ </copy>
+ </target>
+
+</project>]]></programlisting>
+
+ <para>
+ è¿å°åè¯Antæææå¨libç®å½ä¸ä»¥<literal>.jar</literal>ç»å°¾çæ件æ·è´å°classpathä¸ä»¥ä¾ç¼è¯ä¹ç¨ãå®ä¹æææçéJavaæºä»£ç æ件ï¼ä¾å¦é
ç½®åHibernateæ å°æ件ï¼æ·è´å°ç®æ ç®å½ãå¦æä½ ç°å¨è¿è¡Antï¼ä¼å¾å°ä»¥ä¸è¾åºï¼
+ </para>
+
+ <programlisting><![CDATA[C:\hibernateTutorial\>ant
+Buildfile: build.xml
+
+copy-resources:
+ [copy] Copying 2 files to C:\hibernateTutorial\bin
+
+compile:
+ [javac] Compiling 1 source file to C:\hibernateTutorial\bin
+
+BUILD SUCCESSFUL
+Total time: 1 second ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-helpers" revision="3">
+ <title>
+ å¯å¨åè¾
å©ç±»
+ </title>
+
+
+ <para>
+ æ¯æ¶åæ¥å è½½åå¨åä¸äº<literal>Event</literal>对象äºï¼ä½é¦å
æ们å¾ç¼åä¸äºåºç¡ç代ç 以å®æ设置ãæ们å¿
é¡»å¯å¨Hibernateï¼æ¤è¿ç¨å
æ¬å建ä¸ä¸ªå
¨å±ç<literal>SessoinFactory</literal>ï¼å¹¶æå®å¨åå¨åºç¨ç¨åºä»£ç 容æ访é®çå°æ¹ã<literal>SessionFactory</literal>å¯ä»¥å建并æå¼æ°ç<literal>Session</literal>ãä¸ä¸ª<literal>Session</literal>代表ä¸ä¸ªå线ç¨çåå
æä½ï¼<literal>SessionFactory</literal>åæ¯ä¸ªçº¿ç¨å®å
¨çå
¨å±å¯¹è±¡ï¼åªéè¦è¢«å®ä¾åä¸æ¬¡ã
+ </para>
+
+ <para>
+ æ们å°å建ä¸ä¸ª<literal>HibernateUtil</literal>è¾
å©ç±»ï¼helper classï¼æ¥è´è´£å¯å¨Hibernateåæ´æ¹ä¾¿å°æä½<literal>SessionFactory</literal>ã让æ们æ¥çä¸ä¸å®çå®ç°ï¼
+ </para>
+
+ <programlisting><![CDATA[package util;
+
+import org.hibernate.*;
+import org.hibernate.cfg.*;
+
+public class HibernateUtil {
+
+ private static final SessionFactory sessionFactory;
+
+ static {
+ try {
+ // Create the SessionFactory from hibernate.cfg.xml
+ sessionFactory = new Configuration().configure().buildSessionFactory();
+ } catch (Throwable ex) {
+ // Make sure you log the exception, as it might be swallowed
+ System.err.println("Initial SessionFactory creation failed." + ex);
+ throw new ExceptionInInitializerError(ex);
+ }
+ }
+
+ public static SessionFactory getSessionFactory() {
+ return sessionFactory;
+ }
+
+}]]></programlisting>
+
+
+ <para>
+ è¿ä¸ªç±»ä¸ä½å¨å®çéæåå§åè¿ç¨ï¼ä»
å½å è½½è¿ä¸ªç±»çæ¶å被JVMæ§è¡ä¸æ¬¡ï¼ä¸äº§çå
¨å±ç<literal>SessionFactory</literal>ï¼èä¸éèäºå®ä½¿ç¨äºéæsingletonçäºå®ãå®ä¹å¯è½å¨åºç¨ç¨åºæå¡å¨ä¸çJNDIæ¥æ¾<literal>SessionFactory</literal>ã
+ </para>
+
+ <para>
+ å¦æä½ å¨é
ç½®æ件ä¸ç»<literal>SessionFactory</literal>ä¸ä¸ªååï¼å¨<literal>SessionFactory</literal>å建åï¼Hibernateä¼è¯çæå®ç»å®å°JNDIãè¦å®å
¨é¿å
è¿æ ·ç代ç ï¼ä½ ä¹å¯ä»¥ä½¿ç¨JMXé¨ç½²ï¼è®©å
·æJMXè½åç容å¨æ¥å®ä¾å<literal>HibernateService</literal>并æå®ç»å®å°JNDIãè¿äºé«çº§å¯é项å¨åé¢çç« èä¸ä¼è®¨è®ºå°ã
+ </para>
+
+ <para>
+ æ<literal>HibernateUtil.java</literal>æ¾å¨å¼åç®å½çæºä»£ç è·¯å¾ä¸ï¼ä¸æ¾<literal>events</literal>çå
并åï¼
+ </para>
+
+ <programlisting><![CDATA[.
++lib
+ <Hibernate and third-party libraries>
++src
+ +events
+ Event.java
+ Event.hbm.xml
+ +util
+ HibernateUtil.java
+ hibernate.cfg.xml
++data
+build.xml]]></programlisting>
+
+
+ <para>
+ å次ç¼è¯è¿ä¸ªåºç¨ç¨åºåºè¯¥ä¸ä¼æé®é¢ãæåæ们éè¦é
ç½®ä¸ä¸ªæ¥å¿ï¼logging)ç³»ç» ï¼ Hibernate使ç¨éç¨æ¥å¿æ¥å£ï¼å
è®¸ä½ å¨Log4jåJDK 1.4 æ¥å¿ä¹é´è¿è¡éæ©ãå¤æ°å¼åè
æ´å欢Log4jï¼ä»Hibernateçåå¸å
ä¸ï¼å®å¨<literal>etc/</literal>ç®å½ä¸ï¼æ·è´<literal>log4j.properties</literal>å°ä½ ç<literal>src</literal>ç®å½ï¼ä¸<literal>hibernate.cfg.xml</literal>.æ¾å¨ä¸èµ·ãçä¸ä¸é
置示ä¾ï¼å¦æä½ å¸æçå°æ´å 详ç»çè¾åºä¿¡æ¯ï¼ä½ å¯ä»¥ä¿®æ¹é
ç½®ãé»è®¤æ
åµä¸ï¼åªæHibernateçå¯å¨ä¿¡æ¯æä¼æ¾ç¤ºå¨æ åè¾åºä¸ã
+ </para>
+
+ <para>
+ 示ä¾çåºæ¬æ¡æ¶å®æäº ï¼ ç°å¨æ们å¯ä»¥ç¨Hibernateæ¥åäºçæ£çå·¥ä½ã
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-firstapp-workingpersistence" revision="5">
+ <title>
+ å 载并åå¨å¯¹è±¡
+ </title>
+
+ <para>
+ æ们ç»äºå¯ä»¥ä½¿ç¨Hibernateæ¥å è½½ååå¨å¯¹è±¡äºï¼ç¼åä¸ä¸ªå¸¦æ<literal>main()</literal>æ¹æ³ç<literal>EventManager</literal>ç±»ï¼
+
+ </para>
+
+ <programlisting><![CDATA[package events;
+import org.hibernate.Session;
+
+import java.util.Date;
+
+import util.HibernateUtil;
+
+public class EventManager {
+
+ public static void main(String[] args) {
+ EventManager mgr = new EventManager();
+
+ if (args[0].equals("store")) {
+ mgr.createAndStoreEvent("My Event", new Date());
+ }
+
+ HibernateUtil.getSessionFactory().close();
+ }
+
+ private void createAndStoreEvent(String title, Date theDate) {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+
+ session.beginTransaction();
+
+ Event theEvent = new Event();
+ theEvent.setTitle(title);
+ theEvent.setDate(theDate);
+
+ session.save(theEvent);
+
+ session.getTransaction().commit();
+ }
+
+}]]></programlisting>
+
+
+ <para>
+ æ们å建äºä¸ªæ°ç<literal>Event</literal>对象并æå®ä¼ éç»Hibernateãç°å¨Hibernateè´è´£ä¸SQLæ交éï¼å¹¶æ<literal>INSERT</literal>å½ä»¤ä¼ ç»æ°æ®åºãå¨è¿è¡ä¹åï¼è®©æ们çä¸ä¸å¤ç<literal>Session</literal>å<literal>Transaction</literal>ç代ç ã
+
+ </para>
+ <para>
+ ä¸ä¸ª<literal>Session</literal>å°±æ¯ä¸ªåä¸çå·¥ä½åå
ãæ们ææ¶è®©äºæ
ç®åä¸äºï¼å¹¶å设Hibernate<literal>Session</literal>åæ°æ®åºäºå¡æ¯ä¸ä¸å¯¹åºçã为äºè®©æ们ç代ç ä»åºå±çäºå¡ç³»ç»ä¸è±ç¦»åºæ¥ï¼æ¤ä¾ä¸æ¯JDBCï¼ä½ä¹å¯è½æ¯JTAï¼ï¼æ们使ç¨Hibernate <literal>Session</literal>ä¸ç<literal>Transaction</literal> APIã
+ </para>
+
+ <para>
+ <literal>sessionFactory.getCurrentSession()</literal>æ¯å¹²ä»ä¹çå¢ï¼é¦å
ï¼åªè¦ä½ ææ<literal>SessionFactory</literal>ï¼å¹¸äºæ们æ<literal>HibernateUtil</literal>ï¼å¯ä»¥éæ¶è·å¾ï¼ï¼å¤§å¯å¨ä»»ä½æ¶åãä»»ä½å°ç¹è°ç¨è¿ä¸ªæ¹æ³ã<literal>getCurrentSession()</literal>æ¹æ³æ»ä¼è¿åâå½åçâå·¥ä½åå
ãè®°å¾æ们å¨<literal>hibernate.cfg.xml</literal>ä¸æè¿ä¸é
ç½®é项è°æ´ä¸º"thread"äºåï¼å æ¤ï¼å æ¤ï¼å½åå·¥ä½åå
被ç»å®å°å½åæ§è¡æ们åºç¨ç¨åºçJava线ç¨ãä½æ¯ï¼è¿å¹¶éæ¯å®å
¨åç¡®ç,ä½ è¿å¾èèå·¥ä½åå
ççå½å¨æèå´ (scope),å®ä½æ¶å¼å§,åä½æ¶ç»æ.
+ </para>
+
+ <para>
+ <literal>Session</literal>å¨ç¬¬ä¸æ¬¡è¢«ä½¿ç¨çæ¶å,å³ç¬¬ä¸æ¬¡è°ç¨<literal>getCurrentSession()</literal>çæ¶å,å
¶çå½å¨æå°±å¼å§ãç¶åå®è¢«Hibernateç»å®å°å½å线ç¨ãå½äºå¡ç»æçæ¶åï¼ä¸ç®¡æ¯æ交è¿æ¯åæ»ï¼Hibernateä¼èªå¨æ<literal>Session</literal>ä»å½å线ç¨å¥ç¦»ï¼å¹¶ä¸å
³éå®ãåè¥ä½ å次è°ç¨<literal>getCurrentSession()</literal>ï¼ä½ ä¼å¾å°ä¸ä¸ªæ°ç<literal>Session</literal>ï¼å¹¶ä¸å¼å§ä¸ä¸ªæ°çå·¥ä½åå
ãè¿ç§<emphasis>线ç¨ç»å®(thread-bound)</emphasis>çç¼ç¨æ¨¡åï¼modelï¼æ¯ä½¿ç¨Hibernateçæ广æ³çæ¹å¼,å 为å®æ¯æå¯¹ä½ ç代ç çµæ´»åå±(äºå¡ååå¯ä»¥åä½ çæ°æ®è®¿é®ä»£ç å离å¼æ¥,å¨æ¬æç¨çåé¢é¨åå°±ä¼è¿ä¹å)ã
+
+ </para>
+
+ <para>
+
+ åå·¥ä½åå
ççå½å¨æè¿ä¸ªè¯é¢ç¸å
³ï¼Hibernate <literal>Session</literal>æ¯å¦è¢«åºè¯¥ç¨æ¥æ§è¡å¤æ¬¡æ°æ®åºæä½ï¼ä¸é¢çä¾å对æ¯ä¸æ¬¡æä½ä½¿ç¨äºä¸ä¸ª<literal>Session</literal>ï¼è¿å®å
¨æ¯å·§åï¼è¿ä¸ªä¾åä¸æ¯å¾å¤æï¼æ æ³å±ç¤ºå
¶ä»æ¹å¼ãHibernate <literal>Session</literal>ççå½å¨æå¯ä»¥å¾çµæ´»ï¼ä½æ¯ä½ ç»ä¸è¦æä½ çåºç¨ç¨åºè®¾è®¡æ为<emphasis>æ¯ä¸æ¬¡</emphasis>æ°æ®åºæä½é½ç¨ä¸ä¸ªæ°çHibernate <literal>Session</literal>ãå æ¤å°±ç®ä¸é¢çä¾åï¼å®ä»¬é½å¾ç®åï¼ä¸ä½ å¯ä»¥çå°è¿ç§ç¨æ³ï¼è®°ä½<emphasis>æ¯æ¬¡æä½ä¸ä¸ªsession</emphasis>æ¯ä¸ä¸ªå模å¼ãå¨æ¬æç¨çåé¢ä¼å±ç¤ºä¸ä¸ªçæ£ç(web)ç¨åºã
+ </para>
+
+
+
+ <para>
+ å
³äºäºå¡å¤çåäºå¡è¾¹ççå®ç详ç»ä¿¡æ¯ï¼è¯·åç<xref linkend="transactions"/>ãå¨ä¸é¢çä¾åä¸ï¼æ们ä¹å¿½ç¥äºææçé误ä¸åæ»çå¤çã
+ </para>
+
+ <para>
+ 为第ä¸æ¬¡è¿è¡æ们çç¨åºï¼æ们å¾å¨Antçbuildæ件ä¸å¢å ä¸ä¸ªå¯ä»¥è°ç¨å¾å°çtargetã
+ </para>
+
+ <programlisting><![CDATA[<target name="run" depends="compile">
+ <java fork="true" classname="events.EventManager" classpathref="libraries">
+ <classpath path="${targetdir}"/>
+ <arg value="${action}"/>
+ </java>
+</target>]]></programlisting>
+
+ <para>
+ <literal>action</literal>åæ°ï¼argumentï¼çå¼æ¯éè¿å½ä»¤è¡è°ç¨è¿ä¸ªtargetçæ¶å设置çï¼
+ </para>
+
+ <programlisting><![CDATA[C:\hibernateTutorial\>ant run -Daction=store]]></programlisting>
+
+ <para>
+ ä½ åºè¯¥ä¼çå°ï¼ç¼è¯ä»¥åï¼Hibernateæ ¹æ®ä½ çé
ç½®å¯å¨ï¼å¹¶äº§çä¸å¤§å çè¾åºæ¥å¿ãå¨æ¥å¿æåä½ ä¼çå°ä¸é¢è¿è¡ï¼
+ </para>
+
+ <programlisting><![CDATA[[java] Hibernate: insert into EVENTS (EVENT_DATE, title, EVENT_ID) values (?, ?, ?)]]></programlisting>
+
+ <para>
+ è¿æ¯Hibernateæ§è¡ç<literal>INSERT</literal>å½ä»¤ï¼é®å·ä»£è¡¨JDBCçç»å®åæ°ãå¦ææ³è¦çå°ç»å®åæ°çå¼æè
åå°æ¥å¿çé¿åº¦ï¼å°±è¦è°æ´ä½ å¨<literal>log4j.properties</literal>æ件éç设置ã
+ </para>
+
+ <para>
+ æ们æ³è¦ååºææå·²ç»è¢«åå¨çeventsï¼å°±è¦å¢å ä¸ä¸ªæ¡ä»¶åæ¯é项å°mainæ¹æ³ä¸å»ã
+ </para>
+
+ <programlisting><![CDATA[if (args[0].equals("store")) {
+ mgr.createAndStoreEvent("My Event", new Date());
+}
+else if (args[0].equals("list")) {
+ List events = mgr.listEvents();
+ for (int i = 0; i < events.size(); i++) {
+ Event theEvent = (Event) events.get(i);
+ System.out.println("Event: " + theEvent.getTitle() +
+ " Time: " + theEvent.getDate());
+ }
+}]]></programlisting>
+
+ <para>
+ æ们ä¹å¢å ä¸ä¸ªæ°ç<literal>listEvents()</literal>æ¹æ³:
+ </para>
+
+ <programlisting><![CDATA[private List listEvents() {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+
+ session.beginTransaction();
+
+ List result = session.createQuery("from Event").list();
+
+ session.getTransaction().commit();
+
+ return result;
+}]]></programlisting>
+
+ <para>
+ æ们å¨è¿éæ¯ç¨ä¸ä¸ªHQLï¼Hibernate Query Languageï¼Hibernateæ¥è¯¢è¯è¨ï¼æ¥è¯¢è¯å¥æ¥ä»æ°æ®åºä¸å è½½ææåå¨ç<literal>Event</literal>对象ãHibernateä¼çæéå½çSQLï¼æå®åéå°æ°æ®åºï¼å¹¶æä½ä»æ¥è¯¢å¾å°æ°æ®ç<literal>Event</literal>对象ãå½ç¶ï¼ä½ å¯ä»¥ä½¿ç¨HQLæ¥å建æ´å å¤æçæ¥è¯¢ã
+ </para>
+
+ <para>
+ ç°å¨ï¼æ ¹æ®ä»¥ä¸æ¥éª¤æ¥æ§è¡å¹¶æµè¯ä»¥ä¸å项ï¼
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ è¿è¡<literal>ant run -Daction=store</literal>æ¥ä¿åä¸äºå
容å°æ°æ®åºãå½ç¶ï¼å
å¾ç¨hbm2ddlæ¥çææ°æ®åºschemaã
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ ç°å¨æ<literal>hibernate.cfg.xml</literal>æ件ä¸hbm2ddlå±æ§æ³¨éæï¼è¿æ ·æ们就åæ¶äºå¨å¯å¨æ¶ç¨hbm2ddlæ¥çææ°æ®åºschemaãé常åªæå¨ä¸æéå¤è¿è¡åå
æµè¯çæ¶åæéè¦æå¼å®ï¼ä½å次è¿è¡hbm2ddlä¼æä½ ä¿åçä¸åé½å æï¼<emphasis>drop</emphasis>ï¼ââ<literal>create</literal>é
ç½®ççå®å«ä¹æ¯ï¼âå¨å建SessionFactoryçæ¶åï¼ä»schema ä¸drop æææç表ï¼åéæ°å建å®ä»¬âã
+ </para>
+ </listitem>
+ </itemizedlist>
+
+
+ <para>
+ å¦æä½ ç°å¨ä½¿ç¨å½ä»¤è¡åæ°<literal>-Daction=list</literal>è¿è¡Antï¼ä½ ä¼çå°é£äºè³ä»ä¸ºæ¢æ们æå¨åçeventsãå½ç¶ï¼ä½ ä¹å¯ä»¥å¤è°ç¨å 次<literal>store</literal>以ä¿åæ´å¤çenventsã
+ </para>
+
+ <para>
+ 注æï¼å¾å¤Hibernateæ°æå¨è¿ä¸æ¥ä¼å¤±è´¥ï¼æ们ä¸æ¶çå°å
³äº<emphasis>Table not found</emphasis>é误信æ¯çæé®ãä½æ¯ï¼åªè¦ä½ æ ¹æ®ä¸é¢æè¿°çæ¥éª¤æ¥æ§è¡ï¼å°±ä¸ä¼æè¿ä¸ªé®é¢ï¼å 为hbm2ddlä¼å¨ç¬¬ä¸æ¬¡è¿è¡çæ¶åå建æ°æ®åºschemaï¼å继çåºç¨ç¨åºéèµ·åè¿è½ç»§ç»ä½¿ç¨è¿ä¸ªschemaãåè¥ä½ ä¿®æ¹äºæ å°ï¼æè
ä¿®æ¹äºæ°æ®åºschemaï¼ä½ å¿
é¡»æhbm2ddléæ°æå¼ä¸æ¬¡ã
+ </para>
+
+ </sect2>
+
+ </sect1>
+ <sect1 id="tutorial-associations">
+ <title>
+ 第äºé¨å ï¼ å
³èæ å°
+ </title>
+
+ <para>
+ æ们已ç»æ å°äºä¸ä¸ªæä¹
åå®ä½ç±»å°è¡¨ä¸ã让æ们å¨è¿ä¸ªåºç¡ä¸å¢å ä¸äºç±»ä¹é´çå
³èãé¦å
æ们å¾åºç¨ç¨åºéå¢å 人ï¼peopleï¼çæ¦å¿µï¼å¹¶åå¨ä»ä»¬æåä¸çä¸ä¸ªEventå表ãï¼è¯è
注ï¼ä¸Eventä¸æ ·ï¼æ们å¨åé¢å°ç´æ¥ä½¿ç¨personæ¥è¡¨ç¤ºâ人âèä¸æ¯å®çä¸æç¿»è¯ï¼
+ </para>
+
+ <sect2 id="tutorial-associations-mappinguser" revision="1">
+ <title>
+ æ å°Personç±»
+ </title>
+
+ <para>
+ æåç®åç<literal>Person</literal>ç±»ï¼
+ </para>
+
+ <programlisting><![CDATA[package events;
+
+public class Person {
+
+ private Long id;
+ private int age;
+ private String firstname;
+ private String lastname;
+
+ public Person() {}
+
+ // Accessor methods for all properties, private setter for 'id'
+
+}]]></programlisting>
+
+ <para>
+ å建ä¸ä¸ªå为<literal>Person.hbm.xml</literal>çæ°æ å°æ件ï¼å«å¿äºæä¸é¢çDTDå¼ç¨ï¼ï¼
+ </para>
+
+ <programlisting><![CDATA[<hibernate-mapping>
+
+ <class name="events.Person" table="PERSON">
+ <id name="id" column="PERSON_ID">
+ <generator class="native"/>
+ </id>
+ <property name="age"/>
+ <property name="firstname"/>
+ <property name="lastname"/>
+ </class>
+
+</hibernate-mapping>]]></programlisting>
+
+ <para>
+ æåï¼ææ°çæ å°å å
¥å°Hibernateçé
ç½®ä¸ï¼
+ </para>
+
+ <programlisting><![CDATA[<mapping resource="events/Event.hbm.xml"/>
+<mapping resource="events/Person.hbm.xml"/>]]></programlisting>
+
+ <para>
+ ç°å¨æ们å¨è¿ä¸¤ä¸ªå®ä½ä¹é´å建ä¸ä¸ªå
³èãæ¾ç¶ï¼personså¯ä»¥åä¸ä¸ç³»åeventsï¼èeventsä¹æä¸åçåå è
ï¼personsï¼ãæ们éè¦å¤çç设计é®é¢æ¯å
³èæ¹åï¼directionalityï¼ï¼é¶æ°ï¼multiplicityï¼åéåï¼collectionï¼çè¡ä¸ºã
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-unidirset" revision="3">
+ <title>
+ ååSet-basedçå
³è
+ </title>
+
+ <para>
+ æ们å°å<literal>Person</literal>ç±»å¢å ä¸è¿ä¸²çeventsãé£æ ·ï¼éè¿è°ç¨<literal>aPerson.getEvents()</literal>ï¼å°±å¯ä»¥è½»æ¾å°å¯¼èªå°ç¹å®personæåä¸çeventsï¼èä¸ç¨å»æ§è¡ä¸ä¸ªæ¾å¼çæ¥è¯¢ãæ们使ç¨Javaçéåç±»ï¼collectionï¼ï¼<literal>Set</literal>ï¼å 为set ä¸å
å«éå¤çå
ç´ åä¸æ们æ å
³çæåºã
+ </para>
+
+ <para>
+ æ们éè¦ç¨set å®ç°ä¸ä¸ªååå¤å¼å
³èã让æ们å¨Javaç±»é为è¿ä¸ªå
³èç¼ç ï¼æ¥çæ å°å®ï¼
+ </para>
+
+ <programlisting><![CDATA[public class Person {
+
+ private Set events = new HashSet();
+
+ public Set getEvents() {
+ return events;
+ }
+
+ public void setEvents(Set events) {
+ this.events = events;
+ }
+}]]></programlisting>
+
+ <para>
+ å¨æ å°è¿ä¸ªå
³èä¹åï¼å
èèä¸ä¸æ¤å
³èçå¦å¤ä¸ç«¯ãå¾æ¾ç¶ï¼æ们å¯ä»¥ä¿æè¿ä¸ªå
³èæ¯ååçãæè
ï¼æ们å¯ä»¥å¨<literal>Event</literal>éå建å¦å¤ä¸ä¸ªéåï¼å¦æå¸æè½å¤ååå°å¯¼èªï¼å¦ï¼<literal>anEvent.getParticipants()</literal>ãä»åè½çè§åº¦æ¥è¯´ï¼è¿å¹¶ä¸æ¯å¿
é¡»çãå ä¸ºä½ æ»å¯ä»¥æ¾å¼å°æ§è¡ä¸ä¸ªæ¥è¯¢ï¼ä»¥è·å¾æ个ç¹å®eventçææåä¸è
ãè¿æ¯ä¸ªå¨è®¾è®¡æ¶éè¦ååºçéæ©ï¼å®å
¨ç±ä½ æ¥å³å®ï¼ä½æ¤è®¨è®ºä¸å
³äºå
³èçé¶æ°æ¯æ¸
æ¥çï¼å³ä¸¤ç«¯é½æ¯âå¤âå¼çï¼æ们æå®å«å<emphasis>å¤å¯¹å¤(many-to-many)</emphasis>å
³èãå èï¼æ们使ç¨Hibernateçå¤å¯¹å¤æ å°ï¼
+ </para>
+
+ <programlisting><![CDATA[<class name="events.Person" table="PERSON">
+ <id name="id" column="PERSON_ID">
+ <generator class="native"/>
+ </id>
+ <property name="age"/>
+ <property name="firstname"/>
+ <property name="lastname"/>
+
+ <set name="events" table="PERSON_EVENT">
+ <key column="PERSON_ID"/>
+ <many-to-many column="EVENT_ID" class="events.Event"/>
+ </set>
+
+</class>]]></programlisting>
+
+ <para>
+ Hibernateæ¯æåç§åæ ·çéåæ å°ï¼<literal><set></literal>使ç¨çæ为æ®éã对äºå¤å¯¹å¤å
³èï¼æå«<emphasis>n:m</emphasis>å®ä½å
³ç³»ï¼, éè¦ä¸ä¸ªå
³è表ï¼association tableï¼ã<literal>表</literal>éé¢çæ¯ä¸è¡ä»£è¡¨ä»personå°eventçä¸ä¸ªå
³èã表åæ¯ç±<literal>set</literal>å
ç´ ç<literal>table</literal>å±æ§é
ç½®çãå
³èéé¢çæ è¯ç¬¦å段åï¼å¯¹äºpersonçä¸ç«¯ï¼æ¯ç±<literal><key></literal>å
ç´ å®ä¹ï¼èeventä¸ç«¯çå段åæ¯ç±<literal><many-to-many></literal>å
ç´ ç<literal>column</literal>å±æ§å®ä¹ãä½ ä¹å¿
é¡»åè¯Hibernateéåä¸å¯¹è±¡çç±»ï¼ä¹å°±æ¯ä½äºè¿ä¸ªéåæ代表çå
³èå¦å¤ä¸ç«¯çç±»ï¼ã
+ </para>
+
+ <para>
+ å èè¿ä¸ªæ å°çæ°æ®åºschemaæ¯ï¼
+ </para>
+
+ <programlisting><![CDATA[
+ _____________ __________________
+ | | | | _____________
+ | EVENTS | | PERSON_EVENT | | |
+ |_____________| |__________________| | PERSON |
+ | | | | |_____________|
+ | *EVENT_ID | <--> | *EVENT_ID | | |
+ | EVENT_DATE | | *PERSON_ID | <--> | *PERSON_ID |
+ | TITLE | |__________________| | AGE |
+ |_____________| | FIRSTNAME |
+ | LASTNAME |
+ |_____________|
+ ]]></programlisting>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-working" revision="2">
+ <title>
+ 使å
³èå·¥ä½
+ </title>
+
+ <para>
+ æ们æä¸äºpeopleåevents ä¸èµ·æ¾å°<literal>EventManager</literal>çæ°æ¹æ³ä¸ï¼
+ </para>
+
+ <programlisting><![CDATA[private void addPersonToEvent(Long personId, Long eventId) {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+ session.beginTransaction();
+
+ Person aPerson = (Person) session.load(Person.class, personId);
+ Event anEvent = (Event) session.load(Event.class, eventId);
+
+ aPerson.getEvents().add(anEvent);
+
+ session.getTransaction().commit();
+}]]></programlisting>
+
+ <para>
+ å¨å è½½ä¸<literal>Person</literal>å<literal>Event</literal>åï¼ä½¿ç¨æ®éçéåæ¹æ³å°±å¯å®¹æå°ä¿®æ¹æ们å®ä¹çéåãå¦ä½ æè§ï¼æ²¡ææ¾å¼ç<literal>update()</literal>æ<literal>save()</literal>ï¼Hibernateä¼èªå¨æ£æµå°éåå·²ç»è¢«ä¿®æ¹å¹¶éè¦æ´æ°åæ°æ®åºãè¿å«åèªå¨èæ£æ¥ï¼<emphasis>automatic dirty checking</emphasis>ï¼ï¼ä½ ä¹å¯ä»¥å°è¯ä¿®æ¹ä»»ä½å¯¹è±¡çnameæè
dateå±æ§ï¼åªè¦ä»ä»¬å¤äº<emphasis>æä¹
å</emphasis>ç¶æï¼ä¹å°±æ¯è¢«ç»å®å°æ个Hibernate ç<literal>Session</literal>ä¸ï¼å¦ï¼ä»ä»¬ååå¨ä¸ä¸ªåå
æä½è¢«å è½½æè
ä¿åï¼ï¼Hibernateçè§ä»»ä½æ¹å并å¨åå°éå¼åçæ¹å¼æ§è¡SQLãåæ¥å
åç¶æåæ°æ®åºçè¿ç¨ï¼é常åªå¨åå
æä½ç»æçæ¶ååçï¼ç§°æ¤è¿ç¨ä¸ºæ¸
çç¼å<emphasis>ï¼flushingï¼</emphasis>ãå¨æ们ç代ç ä¸ï¼å·¥ä½åå
ç±æ°æ®åºäºå¡çæ交ï¼æè
åæ»ï¼æ¥ç»æââè¿æ¯ç±<literal>Curren!
tSessionContext</literal>ç±»ç<literal>thread</literal>é
ç½®é项å®ä¹çã
+ </para>
+
+ <para>
+ å½ç¶ï¼ä½ ä¹å¯ä»¥å¨ä¸åçåå
æä½éé¢å è½½personåeventãæå¨<literal>Session</literal>以å¤ä¿®æ¹ä¸æ¯å¤å¨æä¹
åï¼persistentï¼ç¶æä¸ç对象ï¼å¦æ该对象以åæ¾ç»è¢«æä¹
åï¼é£ä¹æ们称è¿ä¸ªç¶æ为<emphasis>è±ç®¡ï¼detachedï¼</emphasis>ï¼ãä½ çè³å¯ä»¥å¨ä¸ä¸ªéå被è±ç®¡æ¶ä¿®æ¹å®ï¼
+ </para>
+
+ <programlisting><![CDATA[private void addPersonToEvent(Long personId, Long eventId) {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+ session.beginTransaction();
+
+ Person aPerson = (Person) session
+ .createQuery("select p from Person p left join fetch p.events where p.id = :pid")
+ .setParameter("pid", personId)
+ .uniqueResult(); // Eager fetch the collection so we can use it detached
+
+ Event anEvent = (Event) session.load(Event.class, eventId);
+
+ session.getTransaction().commit();
+
+ // End of first unit of work
+
+ aPerson.getEvents().add(anEvent); // aPerson (and its collection) is detached
+
+ // Begin second unit of work
+
+ Session session2 = HibernateUtil.getSessionFactory().getCurrentSession();
+ session2.beginTransaction();
+
+ session2.update(aPerson); // Reattachment of aPerson
+
+ session2.getTransaction().commit();
+}]]></programlisting>
+
+ <para>
+ 对<literal>update</literal>çè°ç¨ä½¿ä¸ä¸ªè±ç®¡å¯¹è±¡éæ°æä¹
åï¼ä½ å¯ä»¥è¯´å®è¢«ç»å®å°ä¸ä¸ªæ°çåå
æä½ä¸ï¼æ以å¨è±ç®¡ç¶æä¸å¯¹å®æåçä»»ä½ä¿®æ¹é½ä¼è¢«ä¿åå°æ°æ®åºéãè¿ä¹å
æ¬ä½ 对è¿ä¸ªå®ä½å¯¹è±¡çéåæä½çä»»ä½æ¹å¨ï¼å¢å /å é¤ï¼ã
+ </para>
+
+ <para>
+ è¿å¯¹æ们å½åçæ
å½¢ä¸æ¯å¾æç¨ï¼ä½å®æ¯é常éè¦çæ¦å¿µï¼ä½ å¯ä»¥æå®èå
¥å°ä½ èªå·±çåºç¨ç¨åºè®¾è®¡ä¸ãå¨<literal>EventManager</literal>çmainæ¹æ³ä¸æ·»å ä¸ä¸ªæ°çå¨ä½ï¼å¹¶ä»å½ä»¤è¡è¿è¡å®æ¥å®ææ们æåçç»ä¹ ãå¦æä½ éè¦personåeventçæ è¯ç¬¦ â é£å°±ç¨<literal>save()</literal>æ¹æ³è¿åå®ï¼ä½ å¯è½éè¦ä¿®æ¹åé¢çä¸äºæ¹æ³æ¥è¿åé£ä¸ªæ è¯ç¬¦ï¼ï¼
+ </para>
+
+ <programlisting><![CDATA[else if (args[0].equals("addpersontoevent")) {
+ Long eventId = mgr.createAndStoreEvent("My Event", new Date());
+ Long personId = mgr.createAndStorePerson("Foo", "Bar");
+ mgr.addPersonToEvent(personId, eventId);
+ System.out.println("Added person " + personId + " to event " + eventId);
+}]]></programlisting>
+
+ <para>
+ ä¸é¢æ¯ä¸ªå
³äºä¸¤ä¸ªåçéè¦çå®ä½ç±»é´å
³èçä¾åãååé¢ææå°çé£æ ·ï¼å¨ç¹å®ç模åä¸ä¹åå¨å
¶å®çç±»åç±»åï¼è¿äºç±»åç±»åé常æ¯â次è¦çâãä½ å·²çå°è¿å
¶ä¸çä¸äºï¼å<literal>int</literal>æ<literal>String</literal>ãæ们称è¿äºç±»ä¸º<emphasis>å¼ç±»åï¼value typeï¼</emphasis>ï¼å®ä»¬çå®ä¾<emphasis>ä¾èµï¼dependï¼</emphasis>å¨æ个ç¹å®çå®ä½ä¸ãè¿äºç±»åçå®ä¾æ²¡æå®ä»¬èªå·±çæ è¯ï¼identityï¼ï¼ä¹ä¸è½å¨å®ä½é´è¢«å
±äº«ï¼æ¯å¦ï¼ä¸¤ä¸ªpersonä¸è½å¼ç¨åä¸ä¸ª<literal>firstname</literal>对象ï¼å³ä½¿ä»ä»¬æç¸åçfirst nameï¼ãå½ç¶ï¼å¼ç±»å并ä¸ä»
ä»
å¨JDKä¸åå¨ï¼äºå®ä¸ï¼å¨ä¸ä¸ªHibernateåºç¨ç¨åºä¸ï¼ææçJDKç±»é½è¢«è§ä¸ºå¼ç±»åï¼ï¼èä¸ä½ ä¹å¯ä»¥ç¼åä½ èªå·±çä¾èµç±»ï¼ä¾å¦<literal>Address</literal>ï¼<literal>MonetaryAmount</literal>ã
+ </para>
+
+ <para>
+ ä½ ä¹å¯ä»¥è®¾è®¡ä¸ä¸ªå¼ç±»åçéåï¼è¿å¨æ¦å¿µä¸ä¸å¼ç¨å
¶å®å®ä½çéåæå¾å¤§çä¸åï¼ä½æ¯å¨Javaéé¢çèµ·æ¥å ä¹æ¯ä¸æ ·çã
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-valuecollections">
+ <title>
+ å¼ç±»åçéå
+ </title>
+
+ <para>
+ æ们æä¸ä¸ªå¼ç±»å对象çéåå å
¥<literal>Person</literal>å®ä½ä¸ãæ们å¸æä¿åemailå°åï¼æ以使ç¨<literal>String</literal>ç±»åï¼èä¸è¿æ¬¡çéåç±»ååæ¯<literal>Set</literal>ï¼
+ </para>
+ <programlisting><![CDATA[private Set emailAddresses = new HashSet();
+
+public Set getEmailAddresses() {
+ return emailAddresses;
+}
+
+public void setEmailAddresses(Set emailAddresses) {
+ this.emailAddresses = emailAddresses;
+}]]></programlisting>
+
+ <para>
+ è¿ä¸ª<literal>Set</literal>çæ å°
+ </para>
+
+ <programlisting><![CDATA[<set name="emailAddresses" table="PERSON_EMAIL_ADDR">
+ <key column="PERSON_ID"/>
+ <element type="string" column="EMAIL_ADDR"/>
+</set>]]></programlisting>
+
+ <para>
+ æ¯è¾è¿æ¬¡åæ¤åæ å°çå·®å«ï¼ä¸»è¦å¨äº<literal>element</literal>é¨åï¼è¿æ¬¡å¹¶æ²¡æå
å«å¯¹å
¶å®å®ä½å¼ç¨çéåï¼èæ¯å
ç´ ç±»å为<literal>String</literal>çéåï¼å¨æ å°ä¸ä½¿ç¨å°åçååâstringâæ¯åä½ è¡¨æå®æ¯ä¸ä¸ªHibernateçæ å°ç±»åæè
ç±»å转æ¢å¨ï¼ãåä¹åä¸æ ·ï¼<literal>set</literal>å
ç´ ç<literal>table</literal>å±æ§å³å®äºç¨äºéåç表åã<literal>key</literal>å
ç´ å®ä¹äºå¨éå表ä¸å¤é®çå段åã<literal>element</literal>å
ç´ ç<literal>column</literal>å±æ§å®ä¹ç¨äºå®é
ä¿å<literal>String</literal>å¼çå段åã
+ </para>
+
+ <para>
+ çä¸ä¸ä¿®æ¹åçæ°æ®åºschemaã
+ </para>
+
+ <programlisting><![CDATA[
+ _____________ __________________
+ | | | | _____________
+ | EVENTS | | PERSON_EVENT | | | ___________________
+ |_____________| |__________________| | PERSON | | |
+ | | | | |_____________| | PERSON_EMAIL_ADDR |
+ | *EVENT_ID | <--> | *EVENT_ID | | | |___________________|
+ | EVENT_DATE | | *PERSON_ID | <--> | *PERSON_ID | <--> | *PERSON_ID |
+ | TITLE | |__________________| | AGE | | *EMAIL_ADDR |
+ |_____________| | FIRSTNAME | |___________________|
+ | LASTNAME |
+ |_____________|
+ ]]></programlisting>
+
+ <para>
+ ä½ å¯ä»¥çå°éå表ç主é®å®é
ä¸æ¯ä¸ªå¤å主é®ï¼åæ¶ä½¿ç¨äº2个å段ãè¿ä¹æ示äºå¯¹äºåä¸ä¸ªpersonä¸è½æéå¤çemailå°åï¼è¿æ£æ¯Javaéé¢ä½¿ç¨Setæ¶åæéè¦çè¯ä¹ï¼Setéå
ç´ ä¸è½éå¤ï¼ã
+ </para>
+
+ <para>
+ ä½ ç°å¨å¯ä»¥è¯çæå
ç´ å å
¥å°è¿ä¸ªéåï¼å°±åæ们å¨ä¹åå
³èpersonåeventçé£æ ·ãå
¶å®ç°çJava代ç æ¯ç¸åçï¼
+ </para>
+ <programlisting><![CDATA[private void addEmailToPerson(Long personId, String emailAddress) {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+ session.beginTransaction();
+
+ Person aPerson = (Person) session.load(Person.class, personId);
+
+ // The getEmailAddresses() might trigger a lazy load of the collection
+ aPerson.getEmailAddresses().add(emailAddress);
+
+ session.getTransaction().commit();
+}]]></programlisting>
+
+ <para>
+ è¿æ¬¡æ们没æ使ç¨<emphasis>fetch</emphasis>æ¥è¯¢æ¥åå§åéåãå æ¤ï¼è°ç¨å
¶getteræ¹æ³ä¼è§¦åå¦ä¸éå çselectæ¥åå§åéåï¼è¿æ ·æ们æè½æå
ç´ æ·»å è¿å»ãæ£æ¥SQL logï¼è¯çéè¿é¢å
æåæ¥ä¼åå®ã
+ </para>
+
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-bidirectional" revision="1">
+ <title>
+ ååå
³è
+ </title>
+
+ <para>
+ æ¥ä¸æ¥æ们å°æ å°ååå
³èï¼bi-directional associationï¼ï¼ å¨Javaé让personåeventå¯ä»¥ä»å
³èçä»»ä½ä¸ç«¯è®¿é®å¦ä¸ç«¯ãå½ç¶ï¼æ°æ®åºschema没ææ¹åï¼æ们ä»ç¶éè¦å¤å¯¹å¤çé¶æ°ãä¸ä¸ªå
³ç³»åæ°æ®åºè¦æ¯ç½ç»ç¼ç¨è¯è¨ æ´å çµæ´»ï¼æ以å®å¹¶ä¸éè¦ä»»ä½å导èªæ¹åï¼navigation directionï¼çä¸è¥¿ ï¼ æ°æ®å¯ä»¥ç¨ä»»ä½å¯è½çæ¹å¼è¿è¡æ¥çåè·åã
+ </para>
+
+ <para>
+ é¦å
ï¼æä¸ä¸ªåä¸è
ï¼personï¼çéåå å
¥<literal>Event</literal>ç±»ä¸ï¼
+ </para>
+
+ <programlisting><![CDATA[private Set participants = new HashSet();
+
+public Set getParticipants() {
+ return participants;
+}
+
+public void setParticipants(Set participants) {
+ this.participants = participants;
+}]]></programlisting>
+
+ <para>
+ å¨<literal>Event.hbm.xml</literal>éé¢ä¹æ å°è¿ä¸ªå
³èã
+ </para>
+
+ <programlisting><![CDATA[<set name="participants" table="PERSON_EVENT" inverse="true">
+ <key column="EVENT_ID"/>
+ <many-to-many column="PERSON_ID" class="events.Person"/>
+</set>]]></programlisting>
+ <para>
+ å¦ä½ æè§ï¼ä¸¤ä¸ªæ å°æ件éé½ææ®éç<literal>set</literal>æ å°ã注æå¨ä¸¤ä¸ªæ å°æ件ä¸ï¼äºæ¢äº<literal>key</literal>å<literal>many-to-many</literal>çå段åãè¿éæéè¦çæ¯<literal>Event</literal>æ å°æ件éå¢å äº<literal>set</literal>å
ç´ ç<literal>inverse="true"</literal>å±æ§ã
+ </para>
+
+ <para>
+ è¿æå³çå¨éè¦çæ¶åï¼Hibernateè½å¨å
³èçå¦ä¸ç«¯ ï¼ <literal>Person</literal>ç±»å¾å°ä¸¤ä¸ªå®ä½é´å
³èçä¿¡æ¯ãè¿å°ä¼æ大å°å¸®å©ä½ ç解ååå
³èæ¯å¦ä½å¨ä¸¤ä¸ªå®ä½é´è¢«å建çã
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-associations-usingbidir">
+ <title>
+ 使ååè¿èµ·æ¥
+ </title>
+
+ <para>
+ é¦å
请记ä½ï¼Hibernate并ä¸å½±åé常çJavaè¯ä¹ã å¨ååå
³èçä¾åä¸ï¼æ们æ¯ææ ·å¨<literal>Person</literal>å<literal>Event</literal>ä¹é´å建èç³»çï¼æ们æ<literal>Event</literal>å®ä¾æ·»å å°<literal>Person</literal>å®ä¾å
çeventå¼ç¨éåéãå æ¤å¾æ¾ç¶ï¼å¦ææ们è¦è®©è¿ä¸ªå
³èå¯ä»¥ååå°å·¥ä½ï¼æ们éè¦å¨å¦å¤ä¸ç«¯ååæ ·çäºæ
ï¼ æ<literal>Person</literal>å®ä¾å å
¥<literal>Event</literal>ç±»å
çPersonå¼ç¨éåãè¿âå¨å
³èç两端设置èç³»âæ¯å®å
¨å¿
è¦çèä¸ä½ é½å¾è¿ä¹åã
+ </para>
+
+ <para>
+ 许å¤å¼å人åé²å¾¡å¼å°ç¼ç¨ï¼å建管çå
³èçæ¹æ³æ¥ä¿è¯æ£ç¡®ç设置äºå
³èç两端ï¼æ¯å¦å¨<literal>Person</literal>éï¼
+ </para>
+
+ <programlisting><![CDATA[protected Set getEvents() {
+ return events;
+}
+
+protected void setEvents(Set events) {
+ this.events = events;
+}
+
+public void addToEvent(Event event) {
+ this.getEvents().add(event);
+ event.getParticipants().add(this);
+}
+
+public void removeFromEvent(Event event) {
+ this.getEvents().remove(event);
+ event.getParticipants().remove(this);
+}]]></programlisting>
+
+ <para>
+ 注æç°å¨å¯¹äºéåçgetåsetæ¹æ³ç访é®çº§å«æ¯protected - è¿å
许å¨ä½äºåä¸ä¸ªå
ï¼packageï¼ä¸ç类以å继æ¿èªè¿ä¸ªç±»çåç±»å¯ä»¥è®¿é®è¿äºæ¹æ³ï¼ä½ç¦æ¢å
¶ä»ä»»ä½äººçç´æ¥è®¿é®ï¼é¿å
äºéåå
容çæ··ä¹±ãä½ åºå°½å¯è½å°å¨å¦ä¸ç«¯ä¹æéåç访é®çº§å«è®¾æprotectedã
+ </para>
+
+ <para>
+ <literal>inverse</literal>æ å°å±æ§ç©¶ç«è¡¨ç¤ºä»ä¹å¢ï¼å¯¹äºä½ åJavaæ¥è¯´ï¼ä¸ä¸ªååå
³èä»
ä»
æ¯å¨ä¸¤ç«¯ç®åå°æ£ç¡®è®¾ç½®å¼ç¨ãç¶èï¼Hibernate并没æ足å¤çä¿¡æ¯å»æ£ç¡®å°æ§è¡<literal>INSERT</literal>å<literal>UPDATE</literal>è¯å¥ï¼ä»¥é¿å
è¿åæ°æ®åºçº¦æï¼ï¼æ以å®éè¦ä¸äºå¸®å©æ¥æ£ç¡®çå¤çååå
³èãæå
³èçä¸ç«¯è®¾ç½®ä¸º<literal>inverse</literal>å°åè¯Hibernate忽ç¥å
³èçè¿ä¸ç«¯ï¼æè¿ç«¯çææ¯å¦å¤ä¸ç«¯çä¸ä¸ª<emphasis>é象ï¼mirrorï¼</emphasis>ãè¿å°±æ¯æéçå
¨é¨ä¿¡æ¯ï¼Hibernateå©ç¨è¿äºä¿¡æ¯æ¥å¤çæä¸ä¸ªæå导èªæ¨¡å转移å°æ°æ®åºschemaæ¶çææé®é¢ãä½ åªéè¦è®°ä½è¿ä¸ªç´è§çè§åï¼ææçååå
³èéè¦æä¸ç«¯è¢«è®¾ç½®ä¸º<literal>inverse</literal>ãå¨ä¸å¯¹å¤å
³èä¸å®å¿
é¡»æ¯ä»£è¡¨å¤ï¼manyï¼çé£ç«¯ãèå¨å¤å¯¹å¤ï¼many-to-manyï¼å
³èä¸ï¼ä½ å¯ä»¥ä»»æéåä¸ç«¯ï¼å 为两端ä¹é´å¹¶æ²¡æå·®å«ã
+ </para>
+ </sect2>
+ <para>
+ 让æ们æè¿å
¥ä¸ä¸ªå°åçwebåºç¨ç¨åºã
+ </para>
+
+ </sect1>
+
+ <sect1 id="tutorial-webapp">
+ <title>第ä¸é¨å - EventManager webåºç¨ç¨åº</title>
+
+ <para>
+ Hibernate webåºç¨ç¨åºä½¿ç¨<literal>Session</literal> å<literal>Transaction</literal>çæ¹å¼å ä¹åç¬ç«åºç¨ç¨åºæ¯ä¸æ ·çãä½æ¯ï¼æä¸äºå¸¸è§ç模å¼ï¼patternï¼é常æç¨ãç°å¨æ们ç¼åä¸ä¸ª<literal>EventManagerServlet</literal>ãè¿ä¸ªservletå¯ä»¥ååºæ°æ®åºä¸ä¿åçææçeventsï¼è¿æä¾ä¸ä¸ªHTML表åæ¥å¢å æ°çeventsã
+ </para>
+
+ <sect2 id="tutorial-webapp-servlet" revision="2">
+ <title>ç¼ååºæ¬çservlet</title>
+
+ <para>
+ å¨ä½ çæºä»£ç ç®å½ç<literal>events</literal>å
ä¸å建ä¸ä¸ªæ°çç±»ï¼
+ </para>
+
+ <programlisting><![CDATA[package events;
+
+// Imports
+
+public class EventManagerServlet extends HttpServlet {
+
+ // Servlet code
+}]]></programlisting>
+
+ <para>
+ æ们åé¢ä¼ç¨å°<literal>dateFormatter</literal> çå·¥å
·ï¼ å®æ<literal>Date</literal>对象转æ¢ä¸ºå符串ãåªè¦ä¸ä¸ªformatterä½ä¸ºservletçæåå°±å¯ä»¥äºã
+ </para>
+
+ <para>
+ è¿ä¸ªservletåªå¤ç HTTP <literal>GET</literal> 请æ±ï¼å æ¤ï¼æ们è¦å®ç°çæ¯<literal>doGet()</literal>æ¹æ³ï¼
+ </para>
+
+ <programlisting><![CDATA[protected void doGet(HttpServletRequest request,
+ HttpServletResponse response)
+ throws ServletException, IOException {
+
+ SimpleDateFormat dateFormatter = new SimpleDateFormat("dd.MM.yyyy");
+
+ try {
+ // Begin unit of work
+ HibernateUtil.getSessionFactory()
+ .getCurrentSession().beginTransaction();
+
+ // Process request and render page...
+
+ // End unit of work
+ HibernateUtil.getSessionFactory()
+ .getCurrentSession().getTransaction().commit();
+
+ } catch (Exception ex) {
+ HibernateUtil.getSessionFactory()
+ .getCurrentSession().getTransaction().rollback();
+ throw new ServletException(ex);
+ }
+
+}]]></programlisting>
+ <para>
+æ们称è¿éåºç¨ç模å¼ä¸ºæ¯æ¬¡è¯·æ±ä¸ä¸ªsession<emphasis>(session-per-request)</emphasis>ãå½æ请æ±å°è¾¾è¿ä¸ªservletçæ¶åï¼éè¿å¯¹<literal>SessionFactory</literal>ç第ä¸æ¬¡è°ç¨ï¼æå¼ä¸ä¸ªæ°çHibernate <literal>Session</literal>ãç¶åå¯å¨ä¸ä¸ªæ°æ®åºäºå¡—ææçæ°æ®è®¿é®é½æ¯å¨äºå¡ä¸è¿è¡ï¼ä¸ç®¡æ¯è¯»è¿æ¯åï¼æ们å¨åºç¨ç¨åºä¸ä¸ä½¿ç¨auto-commit模å¼ï¼ã
+ </para>
+
+ <para>
+
+ <emphasis>ä¸è¦</emphasis>为æ¯æ¬¡æ°æ®åºæä½é½ä½¿ç¨ä¸ä¸ªæ°çHibernate <literal>Session</literal>ãå°Hibernate <literal>Session</literal>çèå´è®¾ç½®ä¸ºæ´ä¸ªè¯·æ±ãè¦ç¨<literal>getCurrentSession()</literal>ï¼è¿æ ·å®èªå¨ä¼ç»å®å°å½åJava线ç¨ã
+ </para>
+
+ <para>
+ ä¸ä¸æ¥ï¼å¯¹è¯·æ±çå¯è½å¨ä½è¿è¡å¤çï¼æ¸²æåºåé¦çHTMLãæ们å¾å¿«å°±ä¼æ¶åå°é£é¨åã
+ </para>
+
+ <para>
+ æåï¼å½å¤çä¸æ¸²æé½ç»æçæ¶åï¼è¿ä¸ªå·¥ä½åå
å°±ç»æäºãåè¥å¨å¤çæ渲æçæ¶åæä»»ä½é误åçï¼ä¼æåºä¸ä¸ªå¼å¸¸ï¼åæ»æ°æ®åºäºå¡ãè¿æ ·ï¼<literal>session-per-request</literal>模å¼å°±å®æäºã为äºé¿å
å¨æ¯ä¸ªservletä¸é½ç¼åäºå¡è¾¹ççå®ç代ç ï¼å¯ä»¥èèåä¸ä¸ªservlet è¿æ»¤å¨ï¼filterï¼æ¥æ´å¥½å°è§£å³ãå
³äºè¿ä¸æ¨¡å¼çæ´å¤ä¿¡æ¯ï¼è¯·åé
Hibernateç½ç«åWikiï¼è¿ä¸æ¨¡å¼å«å<emphasis>Open Session in View</emphasis>—åªè¦ä½ èèç¨JSPæ¥æ¸²æä½ çè§å¾ï¼viewï¼ï¼èä¸æ¯å¨servletä¸ï¼ä½ å°±ä¼å¾å¿«ç¨å°å®ã
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-webapp-processing" revision="1">
+ <title>å¤çä¸æ¸²æ</title>
+
+ <para>
+ æ们æ¥å®ç°å¤ç请æ±ä»¥å渲æ页é¢çå·¥ä½ã
+ </para>
+
+<programlisting><![CDATA[// Write HTML header
+PrintWriter out = response.getWriter();
+out.println("<html><head><title>Event Manager</title></head><body>");
+
+// Handle actions
+if ( "store".equals(request.getParameter("action")) ) {
+
+ String eventTitle = request.getParameter("eventTitle");
+ String eventDate = request.getParameter("eventDate");
+
+ if ( "".equals(eventTitle) || "".equals(eventDate) ) {
+ out.println("<b><i>Please enter event title and date.</i></b>");
+ } else {
+ createAndStoreEvent(eventTitle, dateFormatter.parse(eventDate));
+ out.println("<b><i>Added event.</i></b>");
+ }
+}
+
+// Print page
+printEventForm(out);
+listEvents(out, dateFormatter);
+
+// Write HTML footer
+out.println("</body></html>");
+out.flush();
+out.close();]]></programlisting>
+ <para>
+ <literal>listEvents()</literal>æ¹æ³ä½¿ç¨ç»å®å°å½å线ç¨çHibernate <literal>Session</literal>æ¥æ§è¡æ¥è¯¢ï¼
+ </para>
+
+ <programlisting><![CDATA[private void listEvents(PrintWriter out, SimpleDateFormat dateFormatter) {
+
+ List result = HibernateUtil.getSessionFactory()
+ .getCurrentSession().createCriteria(Event.class).list();
+ if (result.size() > 0) {
+ out.println("<h2>Events in database:</h2>");
+ out.println("<table border='1'>");
+ out.println("<tr>");
+ out.println("<th>Event title</th>");
+ out.println("<th>Event date</th>");
+ out.println("</tr>");
+ for (Iterator it = result.iterator(); it.hasNext();) {
+ Event event = (Event) it.next();
+ out.println("<tr>");
+ out.println("<td>" + event.getTitle() + "</td>");
+ out.println("<td>" + dateFormatter.format(event.getDate()) + "</td>");
+ out.println("</tr>");
+ }
+ out.println("</table>");
+ }
+}]]></programlisting>
+
+ <para>
+ æåï¼<literal>store</literal>å¨ä½ä¼è¢«å¯¼åå°<literal>createAndStoreEvent()</literal>æ¹æ³ï¼å®ä¹ä½¿ç¨å½å线ç¨ç<literal>Session</literal>:
+ </para>
+
+ <programlisting><![CDATA[protected void createAndStoreEvent(String title, Date theDate) {
+ Event theEvent = new Event();
+ theEvent.setTitle(title);
+ theEvent.setDate(theDate);
+
+ HibernateUtil.getSessionFactory()
+ .getCurrentSession().save(theEvent);
+}]]></programlisting>
+
+ <para>
+ 大ååæï¼è¿ä¸ªservletåå®äºãHibernateä¼å¨åä¸ç<literal>Session</literal> å<literal>Transaction</literal>ä¸å¤çå°è¾¾çservlet请æ±ãå¦åå¨åé¢çç¬ç«åºç¨ç¨åºä¸é£æ ·ï¼Hibernateå¯ä»¥èªå¨çæè¿äºå¯¹è±¡ç»å®å°å½åè¿è¡ç线ç¨ä¸ãè¿ç»äºä½ ç¨ä»»ä½ä½ å欢çæ¹å¼æ¥å¯¹ä»£ç åå±å访é®<literal>SessionFactory</literal>çèªç±ãé常ï¼ä½ ä¼ç¨æ´å å®å¤ç设计ï¼ææ°æ®è®¿é®ä»£ç 转移å°æ°æ®è®¿é®å¯¹è±¡ä¸(DAO模å¼ï¼ã请åè§Hibernate Wikiï¼é£éææ´å¤çä¾åã
+ </para>
+
+ </sect2>
+
+ <sect2 id="tutorial-webapp-deploy">
+ <title>é¨ç½²ä¸æµè¯</title>
+
+ <para>
+ è¦åå¸è¿ä¸ªç¨åºï¼ä½ å¾æå®ææwebåå¸å
ï¼WARæ件ãæä¸é¢çèæ¬å å
¥å°ä½ ç<literal>build.xml</literal>ä¸ï¼
+ </para>
+
+<programlisting><![CDATA[<target name="war" depends="compile">
+ <war destfile="hibernate-tutorial.war" webxml="web.xml">
+ <lib dir="${librarydir}">
+ <exclude name="jsdk*.jar"/>
+ </lib>
+
+ <classes dir="${targetdir}"/>
+ </war>
+</target>]]></programlisting>
+
+ <para>
+ è¿æ®µä»£ç å¨ä½ çå¼åç®å½ä¸å建ä¸ä¸ª<literal>hibernate-tutorial.war</literal>çæ件ãå®æææçç±»åºå<literal>web.xml</literal>æè¿°æ件é½æå
è¿å»ï¼web.xml æ件åºè¯¥ä½äºä½ çå¼åæ ¹ç®å½ä¸ï¼
+ </para>
+
+<programlisting><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
+<web-app version="2.4"
+ xmlns="http://java.sun.com/xml/ns/j2ee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
+
+ <servlet>
+ <servlet-name>Event Manager</servlet-name>
+ <servlet-class>events.EventManagerServlet</servlet-class>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>Event Manager</servlet-name>
+ <url-pattern>/eventmanager</url-pattern>
+ </servlet-mapping>
+</web-app>]]></programlisting>
+
+ <para>
+ 请注æå¨ä½ ç¼è¯åé¨ç½²webåºç¨ç¨ä¹åï¼éè¦ä¸ä¸ªéå çç±»åºï¼<literal>jsdk.jar</literal>ãè¿æ¯Java Servletå¼åå
ï¼åè¥ä½ è¿æ²¡æï¼å¯ä»¥ä»Sunç½ç«ä¸ä¸è½½ï¼æå®copyå°ä½ çlibç®å½ãä½æ¯ï¼å®ä»
ä»
æ¯å¨ç¼è¯æ¶éè¦ï¼ä¸ä¼è¢«æå
¥WARå
ã
+ </para>
+
+ <para>
+ å¨ä½ çå¼åç®å½ä¸ï¼è°ç¨<literal>ant war</literal>æ¥æ建ãæå
ï¼ç¶åæ<literal>hibernate-tutorial.war</literal>æ件æ·è´å°ä½ çtomcatç<literal>webapps</literal>ç®å½ä¸ãåè¥ä½ è¿æ²¡å®è£
Tomcatï¼å°±å»ä¸è½½ä¸ä¸ªï¼æç
§æåæ¥å®è£
ã对æ¤åºç¨çåå¸ï¼ä½ ä¸éè¦ä¿®æ¹ä»»ä½Tomcatçé
ç½®ã
+ </para>
+
+ <para>
+ å¨é¨ç½²å®ï¼å¯å¨Tomcatä¹åï¼éè¿<literal>http://localhost:8080/hibernate-tutorial/eventmanager</literal>è¿è¡è®¿é®ä½ çåºç¨ï¼å¨ç¬¬ä¸æ¬¡servlet 请æ±åçæ¶ï¼è¯·å¨Tomcat logä¸ç¡®è®¤ä½ çå°Hibernate被åå§åäºï¼<literal>HibernateUtil</literal>çéæåå§åå¨è¢«è°ç¨ï¼ï¼åè¥æä»»ä½å¼å¸¸æåºï¼ä¹å¯ä»¥çå°è¯¦ç»çè¾åºã
+ </para>
+
+ </sect2>
+
+ </sect1>
+ <sect1 id="tutorial-summary">
+ <title>
+ æ»ç»
+ </title>
+
+ <para>
+ æ¬ç« è¦çäºå¦ä½ç¼åä¸ä¸ªç®åç¬ç«çHibernateå½ä»¤è¡åºç¨ç¨åºåå°åçHibernate webåºç¨ç¨åºçåºæ¬è¦ç´ ã
+ </para>
+
+ <para>
+ å¦æä½ å·²ç»å¯¹Hibernateæå°èªä¿¡ï¼éè¿å¼åæåç®å½ï¼ç»§ç»æµè§ä½ æå
´è¶£çå
容ï¼é£äºä¼è¢«é®å°çé®é¢å¤§å¤æ¯äºå¡å¤ç (<xref linkend="transactions"/>)ï¼æåï¼fetchï¼çæç (<xref linkend="performance"/>)ï¼æè
APIçä½¿ç¨ (<xref linkend="objectstate"/>)åæ¥è¯¢çç¹æ§(<xref linkend="objectstate-querying"/>)ã
+ </para>
+
+ <para>
+ å«å¿äºå»Hibernateçç½ç«æ¥çæ´å¤ï¼æé对æ§çï¼ç¤ºä¾ã
+ </para>
+
+ </sect1>
+</chapter>
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/xml.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/xml.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/main/docbook/modules/xml.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,270 @@
+<chapter id="xml">
+ <title>XMLæ å°</title>
+ <para><emphasis>
+ 注æè¿æ¯Hibernate 3.0çä¸ä¸ªå®éªæ§çç¹æ§ãè¿ä¸ç¹æ§ä»å¨ç§¯æå¼åä¸ã
+ </emphasis></para>
+
+ <sect1 id="xml-intro" revision="1">
+ <title>ç¨XMLæ°æ®è¿è¡å·¥ä½</title>
+
+ <para>
+ Hibernate使å¾ä½ å¯ä»¥ç¨XMLæ°æ®æ¥è¿è¡å·¥ä½ï¼æ°å¦ä½ ç¨æä¹
åçPOJOè¿è¡å·¥ä½é£æ ·ã解æè¿çXMLæ
+ å¯ä»¥è¢«è®¤ä¸ºæ¯ä»£æ¿POJOçå¦å¤ä¸ç§å¨å¯¹è±¡å±é¢ä¸è¡¨ç¤ºå
³ç³»åæ°æ®çéå¾.
+ </para>
+
+ <para>
+ Hibernateæ¯æéç¨dom4jä½ä¸ºæä½XMLæ çAPIãä½ å¯ä»¥åä¸äºæ¥è¯¢ä»æ°æ®åºä¸æ£ç´¢åº
+ dom4jæ ï¼éåä½ å¯¹è¿é¢æ åçä»»ä½ä¿®æ¹é½å°èªå¨åæ¥åæ°æ®åºãä½ çè³å¯ä»¥ç¨dom4j解æ
+ ä¸ç¯XMLææ¡£ï¼ç¶å使ç¨Hibernateçä»»ä¸åºæ¬æä½å°å®åå
¥æ°æ®åºï¼
+ <literal>persist(), saveOrUpdate(), merge(), delete(), replicate()</literal>
+ (å并æä½merge()ç®åè¿ä¸æ¯æ)ã
+ </para>
+
+ <para>
+ è¿ä¸ç¹æ§å¯ä»¥åºç¨å¨å¾å¤åºåï¼å
æ¬æ°æ®å¯¼å
¥å¯¼åºï¼éè¿JMSæSOAPå
·ä½åå®ä½æ°æ®ä»¥å
+ åºäºXSLTçæ¥è¡¨ã
+ </para>
+
+ <para>
+ ä¸ä¸ªåä¸çæ å°å°±å¯ä»¥å°ç±»çå±æ§åXMLææ¡£çèç¹åæ¶æ å°å°æ°æ®åºãå¦æä¸éè¦æ å°ç±»ï¼
+ å®ä¹å¯ä»¥ç¨æ¥åªæ å°XMLææ¡£ã
+ </para>
+
+ <sect2 id="xml-intro-mapping">
+ <title>æå®åæ¶æ å°XMLåç±»</title>
+
+ <para>
+ è¿æ¯ä¸ä¸ªåæ¶æ å°POJOåXMLçä¾åï¼
+ </para>
+
+ <programlisting><![CDATA[<class name="Account"
+ table="ACCOUNTS"
+ node="account">
+
+ <id name="accountId"
+ column="ACCOUNT_ID"
+ node="@id"/>
+
+ <many-to-one name="customer"
+ column="CUSTOMER_ID"
+ node="customer/@id"
+ embed-xml="false"/>
+
+ <property name="balance"
+ column="BALANCE"
+ node="balance"/>
+
+ ...
+
+</class>]]></programlisting>
+ </sect2>
+
+ <sect2 id="xml-onlyxml">
+ <title>åªå®ä¹XMLæ å°</title>
+
+ <para>
+ è¿æ¯ä¸ä¸ªä¸æ å°POJOçä¾åï¼
+ </para>
+
+ <programlisting><![CDATA[<class entity-name="Account"
+ table="ACCOUNTS"
+ node="account">
+
+ <id name="id"
+ column="ACCOUNT_ID"
+ node="@id"
+ type="string"/>
+
+ <many-to-one name="customerId"
+ column="CUSTOMER_ID"
+ node="customer/@id"
+ embed-xml="false"
+ entity-name="Customer"/>
+
+ <property name="balance"
+ column="BALANCE"
+ node="balance"
+ type="big_decimal"/>
+
+ ...
+
+</class>]]></programlisting>
+
+ <para>
+ è¿ä¸ªæ å°ä½¿å¾ä½ æ¢å¯ä»¥ææ°æ®ä½ä¸ºä¸æ£µdom4jæ é£æ ·è®¿é®ï¼åå¯ä»¥ä½ä¸ºç±å±æ§é®å¼å¯¹(java <literal>Map</literal>s)
+ ç»æçå¾é£æ ·è®¿é®ãå±æ§åå纯粹æ¯é»è¾ä¸çç»æï¼ä½ å¯ä»¥å¨HQLæ¥è¯¢ä¸å¼ç¨å®ã
+ </para>
+
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="xml-mapping" revision="1">
+ <title>XMLæ å°å
æ°æ®</title>
+
+ <para>
+ 许å¤Hibernateæ å°å
ç´ å
·æ<literal>node</literal>å±æ§ãè¿ä½¿ä½ å¯ä»¥æå®ç¨æ¥ä¿å
+ å±æ§æå®ä½æ°æ®çXMLå±æ§æå
ç´ ã<literal>node</literal>å±æ§å¿
é¡»æ¯ä¸åæ ¼å¼ä¹ä¸ï¼
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para><literal>"element-name"</literal> - æ å°ä¸ºæå®çXMLå
ç´ </para>
+ </listitem>
+ <listitem>
+ <para><literal>"@attribute-name"</literal> - æ å°ä¸ºæå®çXMLå±æ§</para>
+ </listitem>
+ <listitem>
+ <para><literal>"."</literal> - æ å°ä¸ºç¶å
ç´ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>"element-name/@attribute-name"</literal> -
+ æ å°ä¸ºæå®å
ç´ çæå®å±æ§
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ 对äºéåååå¼çå
³èï¼æä¸ä¸ªé¢å¤ç<literal>embed-xml</literal>å±æ§å¯ç¨ã
+ è¿ä¸ªå±æ§ç缺çå¼æ¯ç(<literal>embed-xml="true"</literal>)ãå¦æ<literal>embed-xml="true"</literal>ï¼
+ å对åºäºè¢«å
³èå®ä½æå¼ç±»åçéåçXMLæ å°ç´æ¥åµå
¥æ¥æè¿äºå
³èçå®ä½çXMLæ ä¸ã
+ å¦åï¼å¦æ<literal>embed-xml="false"</literal>ï¼é£ä¹å¯¹äºåå¼çå
³èï¼ä»
被å¼ç¨çå®ä½çæ è¯ç¬¦åºç°å¨
+ XMLæ ä¸(被å¼ç¨å®ä½æ¬èº«ä¸åºç°)ï¼èéååæ ¹æ¬ä¸åºç°ã
+ </para>
+
+ <para>
+ ä½ åºè¯¥å°å¿ï¼ä¸è¦è®©å¤ªå¤å
³èçembed-xmlå±æ§ä¸ºç(<literal>embed-xml="true"</literal>)ï¼å 为XMLä¸è½å¾å¥½å°å¤ç
+ 循ç¯å¼ç¨!
+ </para>
+
+ <programlisting><![CDATA[<class name="Customer"
+ table="CUSTOMER"
+ node="customer">
+
+ <id name="id"
+ column="CUST_ID"
+ node="@id"/>
+
+ <map name="accounts"
+ node="."
+ embed-xml="true">
+ <key column="CUSTOMER_ID"
+ not-null="true"/>
+ <map-key column="SHORT_DESC"
+ node="@short-desc"
+ type="string"/>
+ <one-to-many entity-name="Account"
+ embed-xml="false"
+ node="account"/>
+ </map>
+
+ <component name="name"
+ node="name">
+ <property name="firstName"
+ node="first-name"/>
+ <property name="initial"
+ node="initial"/>
+ <property name="lastName"
+ node="last-name"/>
+ </component>
+
+ ...
+
+</class>]]></programlisting>
+
+ <para>
+ å¨è¿ä¸ªä¾åä¸ï¼æ们å³å®åµå
¥å¸ç®å·ç (account id)çéåï¼ä½ä¸åµå
¥å®é
çå¸ç®æ°æ®ãä¸é¢çHQLæ¥è¯¢ï¼
+ </para>
+
+ <programlisting><![CDATA[from Customer c left join fetch c.accounts where c.lastName like :lastName]]></programlisting>
+
+ <para>
+ è¿åçæ°æ®éå°æ¯è¿æ ·ï¼
+ </para>
+
+ <programlisting><![CDATA[<customer id="123456789">
+ <account id="987632567" short-desc="Savings"/>
+ <account id="985612323" short-desc="Credit Card"/>
+ <name>
+ <first-name>Gavin</first-name>
+ <initial>A</initial>
+ <last-name>King</last-name>
+ </name>
+ ...
+</customer>]]></programlisting>
+
+ <para>
+ å¦æä½ æä¸å¯¹å¤æ å°<literal><one-to-many></literal>çembed-xmlå±æ§ç½®ä¸ºç(<literal>embed-xml="true"</literal>)ï¼
+ åæ°æ®çä¸å»å°±åè¿æ ·ï¼
+ </para>
+
+ <programlisting><![CDATA[<customer id="123456789">
+ <account id="987632567" short-desc="Savings">
+ <customer id="123456789"/>
+ <balance>100.29</balance>
+ </account>
+ <account id="985612323" short-desc="Credit Card">
+ <customer id="123456789"/>
+ <balance>-2370.34</balance>
+ </account>
+ <name>
+ <first-name>Gavin</first-name>
+ <initial>A</initial>
+ <last-name>King</last-name>
+ </name>
+ ...
+</customer>]]></programlisting>
+
+ </sect1>
+
+
+ <sect1 id="xml-manipulation" revision="1">
+ <title>æä½XMLæ°æ®</title>
+
+ <para>
+ 让æ们æ¥è¯»å
¥åæ´æ°åºç¨ç¨åºä¸çXMLææ¡£ãéè¿è·åä¸ä¸ªdom4jä¼è¯å¯ä»¥åå°è¿ä¸ç¹ï¼
+ </para>
+
+ <programlisting><![CDATA[Document doc = ....;
+
+Session session = factory.openSession();
+Session dom4jSession = session.getSession(EntityMode.DOM4J);
+Transaction tx = session.beginTransaction();
+
+List results = dom4jSession
+ .createQuery("from Customer c left join fetch c.accounts where c.lastName like :lastName")
+ .list();
+for ( int i=0; i<results.size(); i++ ) {
+ //add the customer data to the XML document
+ Element customer = (Element) results.get(i);
+ doc.add(customer);
+}
+
+tx.commit();
+session.close();]]></programlisting>
+
+ <programlisting><![CDATA[Session session = factory.openSession();
+Session dom4jSession = session.getSession(EntityMode.DOM4J);
+Transaction tx = session.beginTransaction();
+
+Element cust = (Element) dom4jSession.get("Customer", customerId);
+for ( int i=0; i<results.size(); i++ ) {
+ Element customer = (Element) results.get(i);
+ //change the customer name in the XML and database
+ Element name = customer.element("name");
+ name.element("first-name").setText(firstName);
+ name.element("initial").setText(initial);
+ name.element("last-name").setText(lastName);
+}
+
+tx.commit();
+session.close();]]></programlisting>
+
+ <para>
+ å°è¿ä¸ç¹è²ä¸Hibernateç<literal>replicate()</literal>æä½ç»åèµ·æ¥å¯¹äºå®ç°çåºäºXMLçæ°æ®å¯¼å
¥/导åºå°é常æç¨.
+ </para>
+
+ </sect1>
+
+</chapter>
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/css/html.css
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/css/html.css (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/css/html.css 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,97 @@
+A {
+ color: #003399;
+}
+
+A:active {
+ color: #003399;
+}
+
+A:visited {
+ color: #888888;
+}
+
+P, OL, UL, LI, DL, DT, DD, BLOCKQUOTE {
+ color: #000000;
+}
+
+TD, TH, SPAN {
+ color: #000000;
+}
+
+BLOCKQUOTE {
+ margin-right: 0px;
+}
+
+
+H1, H2, H3, H4, H5, H6 {
+ color: #000000;
+ font-weight:500;
+ margin-top:10px;
+ padding-top:15px;
+}
+
+TABLE {
+ border-collapse: collapse;
+ border-spacing:0;
+ border: 1px thin black;
+ empty-cells: hide;
+}
+
+TD {
+ padding: 4pt;
+}
+
+H1 { font-size: 150%; }
+H2 { font-size: 140%; }
+H3 { font-size: 110%; font-weight: bold; }
+H4 { font-size: 110%; font-weight: bold;}
+H5 { font-size: 100%; font-style: italic; }
+H6 { font-size: 100%; font-style: italic; }
+
+TT {
+font-size: 90%;
+ font-family: "Courier New", Courier, monospace;
+ color: #000000;
+}
+
+PRE {
+font-size: 100%;
+ padding: 5px;
+ border-style: solid;
+ border-width: 1px;
+ border-color: #CCCCCC;
+ background-color: #F4F4F4;
+}
+
+UL, OL, LI {
+ list-style: disc;
+}
+
+HR {
+ width: 100%;
+ height: 1px;
+ background-color: #CCCCCC;
+ border-width: 0px;
+ padding: 0px;
+ color: #CCCCCC;
+}
+
+.variablelist {
+ padding-top: 10;
+ padding-bottom:10;
+ margin:0;
+}
+
+.itemizedlist, UL {
+ padding-top: 0;
+ padding-bottom:0;
+ margin:0;
+}
+
+.term {
+ font-weight:bold;
+}
+
+
+
+
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/AuthorWork.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/AuthorWork.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/AuthorWork.zargo
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/AuthorWork.zargo
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/CustomerOrderProduct.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/CustomerOrderProduct.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/CustomerOrderProduct.zargo
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/CustomerOrderProduct.zargo
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/EmployerEmployee.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/EmployerEmployee.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/EmployerEmployee.zargo
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/EmployerEmployee.zargo
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/full_cream.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/full_cream.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/full_cream.svg
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/full_cream.svg (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/full_cream.svg 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,429 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"
+[
+ <!ATTLIST svg
+ xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink">
+]>
+<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
+<svg
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="354.331"
+ height="336.614"
+ id="svg1">
+ <defs
+ id="defs3">
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop128" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="1"
+ id="stop129" />
+ </linearGradient>
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient130"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ <radialGradient
+ cx="0.5"
+ cy="0.5"
+ fx="0.5"
+ fy="0.5"
+ r="0.5"
+ id="radialGradient131"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ </defs>
+ <g
+ transform="matrix(0.823795,0,0,0.823795,0.120302,5.25349)"
+ style="font-size:12;"
+ id="g659">
+ <rect
+ width="212.257"
+ height="57.2441"
+ x="17.9576"
+ y="100.132"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect137" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ transform="matrix(0.743454,0,0,0.482981,6.46949,52.2178)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect132" />
+ </g>
+ <rect
+ width="325.86"
+ height="63.6537"
+ x="17.4083"
+ y="15.194"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect136" />
+ <rect
+ width="325.86"
+ height="63.6537"
+ x="13.6713"
+ y="12.4966"
+ style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect126" />
+ <g
+ transform="matrix(1.14345,0,0,0.729078,-1.67818,105.325)"
+ style="font-size:12;"
+ id="g164">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="222.966"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect138" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="221.002"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect133" />
+ </g>
+ <text
+ x="170.824753"
+ y="58.402939"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text183">
+ <tspan
+ x="170.824997"
+ y="58.402901"
+ id="tspan360">
+Application</tspan>
+ </text>
+ <text
+ x="178.076340"
+ y="364.281433"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text197">
+ <tspan
+ x="178.076004"
+ y="364.281006"
+ id="tspan421">
+Database</tspan>
+ </text>
+ <text
+ x="68.605331"
+ y="138.524582"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text216">
+ <tspan
+ x="68.605301"
+ y="138.524994"
+ id="tspan384">
+SessionFactory</tspan>
+ </text>
+ <rect
+ width="67.0014"
+ height="101.35"
+ x="196.927"
+ y="89.2389"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect387" />
+ <rect
+ width="67.0014"
+ height="101.35"
+ x="194.633"
+ y="86.4389"
+ style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect388" />
+ <text
+ x="249.108841"
+ y="173.885559"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text389">
+ <tspan
+ x="249.108994"
+ y="173.886002"
+ id="tspan392">
+Session</tspan>
+ </text>
+ <rect
+ width="73.0355"
+ height="101.35"
+ x="270.995"
+ y="90.0018"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect395" />
+ <rect
+ width="73.0355"
+ height="101.35"
+ x="267.869"
+ y="87.2018"
+ style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect396" />
+ <text
+ x="328.593658"
+ y="174.715622"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text397">
+ <tspan
+ x="328.593994"
+ y="174.716003"
+ id="tspan563">
+Transaction</tspan>
+ </text>
+ <g
+ transform="matrix(0.29544,0,0,0.397877,9.70533,103.96)"
+ style="font-size:12;"
+ id="g565">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect566" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect567" />
+ </g>
+ <text
+ x="25.592752"
+ y="204.497803"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:10;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text568">
+ <tspan
+ x="25.592800"
+ y="204.498001"
+ id="tspan662">
+TransactionFactory</tspan>
+ </text>
+ <g
+ transform="matrix(0.298082,0,0,0.397877,99.6898,103.96)"
+ style="font-size:12;"
+ id="g573">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect574" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect575" />
+ </g>
+ <text
+ x="134.030670"
+ y="205.532791"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:10;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text576">
+ <tspan
+ x="134.031006"
+ y="205.533005"
+ id="tspan664">
+ConnectionProvider</tspan>
+ </text>
+ <g
+ transform="matrix(1.14345,0,0,0.729078,-1.67818,38.9539)"
+ style="font-size:12;"
+ id="g587">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="222.966"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect588" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="221.002"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect589" />
+ </g>
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="25.6196"
+ y="206.028"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect594" />
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="24.4229"
+ y="204.135"
+ style="font-size:12;fill:#b3b3b3;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect595" />
+ <text
+ x="85.575645"
+ y="282.300354"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text596">
+ <tspan
+ x="85.575600"
+ y="282.299988"
+ id="tspan607">
+JNDI</tspan>
+ </text>
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="236.937"
+ y="206.791"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect610" />
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="235.741"
+ y="204.898"
+ style="font-size:12;fill:#b3b3b3;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect611" />
+ <text
+ x="342.093201"
+ y="283.226410"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text612">
+ <tspan
+ x="342.092987"
+ y="283.226013"
+ id="tspan621">
+JTA</tspan>
+ </text>
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="130.134"
+ y="206.791"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect616" />
+ <rect
+ width="90.951"
+ height="44.4829"
+ x="128.937"
+ y="204.898"
+ style="font-size:12;fill:#b3b3b3;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect617" />
+ <text
+ x="212.445343"
+ y="283.226410"
+ transform="scale(0.823795,0.823795)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text618">
+ <tspan
+ x="212.445007"
+ y="283.226013"
+ id="tspan623">
+JDBC</tspan>
+ </text>
+ <g
+ transform="matrix(0.823795,0,0,0.823795,0.120302,6.19341)"
+ style="font-size:12;"
+ id="g637">
+ <g
+ transform="matrix(0.499515,0,0,0.415467,-0.237339,5.61339)"
+ id="g167">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect134" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect135" />
+ </g>
+ <text
+ x="33.749969"
+ y="50.589706"
+ style="font-size:11;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text188">
+ <tspan
+ x="33.750000"
+ y="50.589699"
+ id="tspan635">
+Transient Objects</tspan>
+ </text>
+ </g>
+ <g
+ transform="matrix(0.823795,0,0,0.823795,0.120302,5.25349)"
+ style="font-size:12;"
+ id="g644">
+ <g
+ transform="matrix(0.297486,0,0,0.516482,230.251,36.9178)"
+ id="g364">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect365" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect366" />
+ </g>
+ <text
+ x="277.123230"
+ y="85.155571"
+ style="font-size:11;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text367">
+ <tspan
+ x="277.122986"
+ y="85.155602"
+ id="tspan631">
+Persistent</tspan>
+ <tspan
+ x="277.122986"
+ y="96.155602"
+ id="tspan633">
+Objects</tspan>
+ </text>
+ </g>
+</svg>
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/hibernate_logo_a.png
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/hibernate_logo_a.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/lite.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/lite.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/lite.svg
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/lite.svg (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/lite.svg 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,334 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"
+[
+ <!ATTLIST svg
+ xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink">
+]>
+<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
+<svg
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="318.898"
+ height="248.031"
+ id="svg1">
+ <defs
+ id="defs3">
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop128" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="1"
+ id="stop129" />
+ </linearGradient>
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient130"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ <radialGradient
+ cx="0.5"
+ cy="0.5"
+ fx="0.5"
+ fy="0.5"
+ r="0.5"
+ id="radialGradient131"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ </defs>
+ <rect
+ width="291.837"
+ height="57.0074"
+ x="17.3169"
+ y="18.646"
+ style="font-size:12;fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect136" />
+ <rect
+ width="291.837"
+ height="57.0074"
+ x="13.9703"
+ y="16.2302"
+ style="font-size:12;fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect126" />
+ <g
+ transform="matrix(0.326107,0,0,0.765831,9.59261,8.98517)"
+ style="font-size:12;"
+ id="g161">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect137" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect132" />
+ </g>
+ <g
+ transform="matrix(1.02406,0,0,0.652953,0.223384,39.9254)"
+ style="font-size:12;"
+ id="g164">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="222.966"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect138" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="221.002"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect133" />
+ </g>
+ <g
+ transform="matrix(0.449834,0,0,0.338463,-3.15909,9.73319)"
+ style="font-size:12;"
+ id="g167">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect134" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect135" />
+ </g>
+ <text
+ x="302.277679"
+ y="65.943230"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text183">
+ <tspan
+ x="302.277954"
+ y="65.943184"
+ id="tspan360">
+Application</tspan>
+ </text>
+ <text
+ x="36.235924"
+ y="63.796055"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:14;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text188">
+ <tspan
+ x="36.235950"
+ y="63.796051"
+ id="tspan427">
+Transient Objects</tspan>
+ </text>
+ <text
+ x="180.416245"
+ y="290.543701"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:18;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text197">
+ <tspan
+ x="180.415939"
+ y="290.543549"
+ id="tspan421">
+Database</tspan>
+ </text>
+ <text
+ x="25.037701"
+ y="179.154755"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text216">
+ <tspan
+ x="25.037655"
+ y="179.154648"
+ id="tspan384">
+SessionFactory</tspan>
+ </text>
+ <g
+ transform="matrix(0.252763,0,0,0.765831,109.104,8.98517)"
+ style="font-size:12;"
+ id="g386">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect387" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect388" />
+ </g>
+ <g
+ transform="matrix(0.297394,0,0,0.572692,101.502,21.6359)"
+ style="font-size:12;"
+ id="g364">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect365" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect366" />
+ </g>
+ <text
+ x="202.746506"
+ y="102.992203"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:14;font-weight:normal;stroke-width:1pt;font-family:Helvetica;text-anchor:middle;"
+ id="text367">
+ <tspan
+ x="202.746948"
+ y="102.992249"
+ id="tspan423">
+Persistent</tspan>
+ <tspan
+ x="202.746948"
+ y="116.992355"
+ id="tspan425">
+Objects</tspan>
+ </text>
+ <text
+ x="174.458496"
+ y="180.080795"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text389">
+ <tspan
+ x="174.458618"
+ y="180.080338"
+ id="tspan392">
+Session</tspan>
+ </text>
+ <g
+ transform="matrix(0.127369,0,0,0.765831,188.675,8.98517)"
+ style="font-size:12;"
+ id="g394">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect395" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect396" />
+ </g>
+ <text
+ x="260.413269"
+ y="179.154739"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text397">
+ <tspan
+ x="260.412964"
+ y="179.154343"
+ id="tspan400">
+JDBC</tspan>
+ </text>
+ <g
+ transform="matrix(0.127369,0,0,0.765831,229.156,8.98517)"
+ style="font-size:12;"
+ id="g405">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect406" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect407" />
+ </g>
+ <text
+ x="320.606903"
+ y="179.154739"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text408">
+ <tspan
+ x="320.606964"
+ y="179.154343"
+ id="tspan417">
+JNDI</tspan>
+ </text>
+ <g
+ transform="matrix(0.127369,0,0,0.765831,269.281,8.98517)"
+ style="font-size:12;"
+ id="g411">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect412" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect413" />
+ </g>
+ <text
+ x="377.096313"
+ y="179.154739"
+ transform="scale(0.73778,0.73778)"
+ style="font-size:16;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text414">
+ <tspan
+ x="377.096008"
+ y="179.154999"
+ id="tspan145">
+JTA</tspan>
+ </text>
+</svg>
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/overview.gif
===================================================================
(Binary files differ)
Property changes on: trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/overview.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/overview.svg
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/overview.svg (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/main/resources/shared/images/overview.svg 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,250 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"
+[
+ <!ATTLIST svg
+ xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink">
+]>
+<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
+<svg
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="248.031"
+ height="248.031"
+ id="svg1">
+ <defs
+ id="defs3">
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop128" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="1"
+ id="stop129" />
+ </linearGradient>
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ id="linearGradient130"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ <radialGradient
+ cx="0.5"
+ cy="0.5"
+ fx="0.5"
+ fy="0.5"
+ r="0.5"
+ id="radialGradient131"
+ xlink:href="#linearGradient127"
+ gradientUnits="objectBoundingBox"
+ spreadMethod="pad" />
+ </defs>
+ <g
+ transform="matrix(0.771934,0,0,0.771934,4.36019,-3.02123)"
+ style="font-size:12;"
+ id="g158">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="17.3527"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect136" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="15.3883"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect126" />
+ </g>
+ <g
+ transform="matrix(0.771934,0,0,0.771934,4.36019,3.04452)"
+ style="font-size:12;"
+ id="g161">
+ <rect
+ width="285.502"
+ height="118.523"
+ x="16.6979"
+ y="99.2053"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect137" />
+ <rect
+ width="285.502"
+ height="118.523"
+ x="13.4238"
+ y="95.9309"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect132" />
+ </g>
+ <g
+ transform="matrix(0.771934,0,0,0.771934,4.36019,8.0993)"
+ style="font-size:12;"
+ id="g164">
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="16.6979"
+ y="222.966"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect138" />
+ <rect
+ width="285.502"
+ height="77.2688"
+ x="14.7335"
+ y="221.002"
+ transform="translate(-1.30962,-1.30992)"
+ style="fill:#d2d2d2;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect133" />
+ </g>
+ <g
+ transform="matrix(0.771934,0,0,0.543505,2.59104,21.1103)"
+ style="font-size:12;"
+ id="g167">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect134" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect135" />
+ </g>
+ <text
+ x="105.392174"
+ y="56.568123"
+ transform="scale(0.771934,0.771934)"
+ style="font-size:24;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text183">
+ <tspan
+ x="105.392273"
+ y="56.568146"
+ id="tspan186">
+Application</tspan>
+ </text>
+ <text
+ x="81.820183"
+ y="103.149330"
+ transform="scale(0.771934,0.771934)"
+ style="font-size:20;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text188">
+ <tspan
+ x="81.820213"
+ y="103.149727"
+ id="tspan206">
+Persistent Objects</tspan>
+ </text>
+ <text
+ x="111.548180"
+ y="278.927887"
+ transform="scale(0.771934,0.771934)"
+ style="font-size:24;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text197">
+ <tspan
+ x="111.547874"
+ y="278.927551"
+ id="tspan200">
+Database</tspan>
+ </text>
+ <text
+ x="94.436180"
+ y="153.805740"
+ transform="scale(0.771934,0.771934)"
+ style="font-size:24;font-weight:normal;stroke-width:1pt;font-family:Helvetica;"
+ id="text216">
+ <tspan
+ x="94.436180"
+ y="153.805740"
+ id="tspan221">
+HIBERNATE</tspan>
+ </text>
+ <g
+ transform="matrix(0.771934,0,0,0.771934,2.59083,1.02261)"
+ style="font-size:12;"
+ id="g254">
+ <g
+ transform="translate(4.58374,2.61928)"
+ id="g176">
+ <g
+ transform="matrix(0.571429,0,0,0.67347,-10.6174,117.093)"
+ id="g170">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect171" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect172" />
+ </g>
+ <g
+ transform="matrix(0.571429,0,0,0.67347,138.682,117.093)"
+ id="g173">
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="61.8805"
+ y="68.4288"
+ style="fill:#757575;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect174" />
+ <rect
+ width="199.065"
+ height="61.5532"
+ x="59.2613"
+ y="65.8095"
+ style="fill:#e0e0e0;fill-rule:evenodd;stroke-width:1pt;"
+ id="rect175" />
+ </g>
+ </g>
+ <text
+ x="47.259438"
+ y="182.367538"
+ style="font-weight:bold;stroke-width:1pt;font-family:Courier;"
+ id="text191">
+ <tspan
+ x="47.259399"
+ y="182.367996"
+ id="tspan212">
+hibernate.</tspan>
+ <tspan
+ x="47.259399"
+ y="194.367996"
+ id="tspan214">
+properties</tspan>
+ </text>
+ <text
+ x="198.523010"
+ y="188.260941"
+ style="font-weight:normal;stroke-width:1pt;font-family:helvetica;"
+ id="text194">
+ <tspan
+ id="tspan195">
+XML Mapping</tspan>
+ </text>
+ </g>
+</svg>
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/no-idea-what-to-do-with-these-yet/fop/simhei.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/no-idea-what-to-do-with-these-yet/fop/simhei.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/no-idea-what-to-do-with-these-yet/fop/simhei.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<font-metrics type="TYPE0"><font-name>SimHei</font-name><embed/><cap-height>0</cap-height><x-height>0</x-height><ascender>859</ascender><descender>-140</descender><bbox><left>0</left><bottom>-140</bottom><right>996</right><top>855</top></bbox><flags>33</flags><stemv>0</stemv><italicangle>0</italicangle><subtype>TYPE0</subtype><multibyte-extras><cid-type>CIDFontType2</cid-type><default-width>0</default-width><bfranges><bf us="32" ue="126" gi="3"/><bf us="127" ue="127" gi="2"/><bf us="164" ue="164" gi="98"/><bf us="167" ue="168" gi="99"/><bf us="176" ue="177" gi="101"/><bf us="183" ue="183" gi="103"/><bf us="215" ue="215" gi="104"/><bf us="224" ue="225" gi="105"/><bf us="232" ue="234" gi="107"/><bf us="236" ue="237" gi="110"/><bf us="242" ue="243" gi="112"/><bf us="247" ue="247" gi="114"/><bf us="249" ue="250" gi="115"/><bf us="252" ue="252" gi="117"/><bf us="257" ue="257" gi="118"/><bf us="275" ue="275" gi="119"/><bf us="283" ue="283" gi="120"/><bf us="299" ue="299" gi="121"!
/><bf us="324" ue="324" gi="122"/><bf us="328" ue="328" gi="123"/><bf us="333" ue="333" gi="124"/><bf us="363" ue="363" gi="125"/><bf us="462" ue="462" gi="126"/><bf us="464" ue="464" gi="127"/><bf us="466" ue="466" gi="128"/><bf us="468" ue="468" gi="129"/><bf us="470" ue="470" gi="130"/><bf us="472" ue="472" gi="131"/><bf us="474" ue="474" gi="132"/><bf us="476" ue="476" gi="133"/><bf us="505" ue="505" gi="687"/><bf us="593" ue="593" gi="134"/><bf us="609" ue="609" gi="135"/><bf us="711" ue="711" gi="136"/><bf us="713" ue="713" gi="137"/><bf us="714" ue="715" gi="799"/><bf us="729" ue="729" gi="801"/><bf us="913" ue="929" gi="138"/><bf us="931" ue="937" gi="155"/><bf us="945" ue="961" gi="162"/><bf us="963" ue="969" gi="179"/><bf us="1025" ue="1025" gi="186"/><bf us="1040" ue="1103" gi="187"/><bf us="1105" ue="1105" gi="251"/><bf us="8208" ue="8208" gi="971"/><bf us="8211" ue="8211" gi="802"/><bf us="8212" ue="8212" gi="252"/><bf us="8213" ue="8213" gi="803"/><bf us="8214!
" ue="8214" gi="253"/><bf us="8216" ue="8217" gi="254"/><bf us="8220"
ue="8221" gi="256"/><bf us="8229" ue="8229" gi="804"/><bf us="8230" ue="8230" gi="258"/><bf us="8240" ue="8240" gi="259"/><bf us="8242" ue="8243" gi="260"/><bf us="8245" ue="8245" gi="805"/><bf us="8251" ue="8251" gi="262"/><bf us="8451" ue="8451" gi="263"/><bf us="8453" ue="8453" gi="806"/><bf us="8457" ue="8457" gi="807"/><bf us="8470" ue="8470" gi="264"/><bf us="8481" ue="8481" gi="969"/><bf us="8544" ue="8555" gi="265"/><bf us="8560" ue="8569" gi="957"/><bf us="8592" ue="8595" gi="277"/><bf us="8598" ue="8601" gi="808"/><bf us="8712" ue="8712" gi="281"/><bf us="8719" ue="8719" gi="282"/><bf us="8721" ue="8721" gi="283"/><bf us="8725" ue="8725" gi="812"/><bf us="8730" ue="8730" gi="284"/><bf us="8733" ue="8734" gi="285"/><bf us="8735" ue="8735" gi="813"/><bf us="8736" ue="8736" gi="287"/><bf us="8739" ue="8739" gi="814"/><bf us="8741" ue="8741" gi="288"/><bf us="8743" ue="8747" gi="289"/><bf us="8750" ue="8750" gi="294"/><bf us="8756" ue="8759" gi="295"/><bf us="8765" ue=!
"8765" gi="299"/><bf us="8776" ue="8776" gi="300"/><bf us="8780" ue="8780" gi="301"/><bf us="8786" ue="8786" gi="815"/><bf us="8800" ue="8801" gi="302"/><bf us="8804" ue="8805" gi="304"/><bf us="8806" ue="8807" gi="816"/><bf us="8814" ue="8815" gi="306"/><bf us="8853" ue="8853" gi="880"/><bf us="8857" ue="8857" gi="308"/><bf us="8869" ue="8869" gi="309"/><bf us="8895" ue="8895" gi="818"/><bf us="8978" ue="8978" gi="310"/><bf us="9312" ue="9321" gi="311"/><bf us="9332" ue="9371" gi="321"/><bf us="9472" ue="9547" gi="361"/><bf us="9552" ue="9587" gi="819"/><bf us="9601" ue="9615" gi="855"/><bf us="9619" ue="9621" gi="870"/><bf us="9632" ue="9633" gi="437"/><bf us="9650" ue="9651" gi="439"/><bf us="9660" ue="9661" gi="873"/><bf us="9670" ue="9671" gi="441"/><bf us="9675" ue="9675" gi="443"/><bf us="9678" ue="9679" gi="444"/><bf us="9698" ue="9701" gi="875"/><bf us="9733" ue="9734" gi="446"/><bf us="9737" ue="9737" gi="879"/><bf us="9792" ue="9792" gi="448"/><bf us="9794" ue="9!
794" gi="449"/><bf us="11905" ue="11905" gi="21903"/><bf us="11908" ue
="11908" gi="21907"/><bf us="11912" ue="11912" gi="21910"/><bf us="11915" ue="11915" gi="21911"/><bf us="11916" ue="11916" gi="21916"/><bf us="11943" ue="11943" gi="21930"/><bf us="11946" ue="11946" gi="21933"/><bf us="11950" ue="11950" gi="21936"/><bf us="11955" ue="11955" gi="21938"/><bf us="11958" ue="11959" gi="21939"/><bf us="11963" ue="11963" gi="21944"/><bf us="11978" ue="11978" gi="21954"/><bf us="12272" ue="12283" gi="689"/><bf us="12288" ue="12291" gi="450"/><bf us="12293" ue="12293" gi="454"/><bf us="12294" ue="12294" gi="977"/><bf us="12295" ue="12295" gi="21983"/><bf us="12296" ue="12305" gi="455"/><bf us="12306" ue="12306" gi="881"/><bf us="12307" ue="12311" gi="465"/><bf us="12317" ue="12318" gi="882"/><bf us="12321" ue="12329" gi="884"/><bf us="12353" ue="12435" gi="470"/><bf us="12443" ue="12444" gi="973"/><bf us="12445" ue="12446" gi="978"/><bf us="12449" ue="12534" gi="553"/><bf us="12540" ue="12540" gi="972"/><bf us="12541" ue="12542" gi="975"/><bf us="12!
549" ue="12585" gi="639"/><bf us="12832" ue="12841" gi="676"/><bf us="12849" ue="12849" gi="970"/><bf us="12963" ue="12963" gi="893"/><bf us="13198" ue="13199" gi="894"/><bf us="13212" ue="13214" gi="896"/><bf us="13217" ue="13217" gi="899"/><bf us="13252" ue="13252" gi="900"/><bf us="13262" ue="13262" gi="901"/><bf us="13265" ue="13266" gi="902"/><bf us="13269" ue="13269" gi="904"/><bf us="13383" ue="13383" gi="21909"/><bf us="13427" ue="13427" gi="21908"/><bf us="13726" ue="13726" gi="21913"/><bf us="13838" ue="13838" gi="21915"/><bf us="13850" ue="13850" gi="21914"/><bf us="14616" ue="14616" gi="21919"/><bf us="14702" ue="14702" gi="21918"/><bf us="14799" ue="14799" gi="21921"/><bf us="14800" ue="14800" gi="21924"/><bf us="14815" ue="14815" gi="21922"/><bf us="14963" ue="14963" gi="21923"/><bf us="15182" ue="15182" gi="21927"/><bf us="15470" ue="15470" gi="21928"/><bf us="15584" ue="15584" gi="21929"/><bf us="16470" ue="16470" gi="21934"/><bf us="16735" ue="16735" gi="21!
935"/><bf us="17207" ue="17207" gi="21937"/><bf us="17324" ue="17324"
gi="21943"/><bf us="17329" ue="17329" gi="21942"/><bf us="17373" ue="17373" gi="21945"/><bf us="17622" ue="17622" gi="21946"/><bf us="17996" ue="17996" gi="21948"/><bf us="18017" ue="18017" gi="21947"/><bf us="18211" ue="18211" gi="21950"/><bf us="18217" ue="18217" gi="21951"/><bf us="18300" ue="18300" gi="21952"/><bf us="18317" ue="18317" gi="21953"/><bf us="18759" ue="18759" gi="21955"/><bf us="18810" ue="18810" gi="21956"/><bf us="18813" ue="18813" gi="21957"/><bf us="18818" ue="18819" gi="21958"/><bf us="18821" ue="18822" gi="21960"/><bf us="18843" ue="18843" gi="21963"/><bf us="18847" ue="18847" gi="21962"/><bf us="18870" ue="18870" gi="21965"/><bf us="18871" ue="18871" gi="21964"/><bf us="19575" ue="19575" gi="21972"/><bf us="19615" ue="19615" gi="21969"/><bf us="19617" ue="19617" gi="21971"/><bf us="19618" ue="19618" gi="21973"/><bf us="19619" ue="19619" gi="21968"/><bf us="19629" ue="19629" gi="21970"/><bf us="19731" ue="19737" gi="21974"/><bf us="19886" ue="19886" g!
i="21981"/><bf us="19968" ue="40869" gi="980"/><bf us="59335" ue="59336" gi="686"/><bf us="59367" ue="59379" gi="688"/><bf us="59413" ue="59492" gi="21903"/><bf us="63788" ue="63788" gi="21882"/><bf us="63865" ue="63865" gi="21883"/><bf us="63893" ue="63893" gi="21884"/><bf us="63975" ue="63975" gi="21885"/><bf us="63985" ue="63985" gi="21886"/><bf us="64012" ue="64015" gi="21887"/><bf us="64017" ue="64017" gi="21891"/><bf us="64019" ue="64020" gi="21892"/><bf us="64024" ue="64024" gi="21894"/><bf us="64031" ue="64033" gi="21895"/><bf us="64035" ue="64036" gi="21898"/><bf us="64039" ue="64041" gi="21900"/><bf us="65072" ue="65073" gi="905"/><bf us="65075" ue="65092" gi="907"/><bf us="65097" ue="65106" gi="925"/><bf us="65108" ue="65111" gi="935"/><bf us="65113" ue="65126" gi="939"/><bf us="65128" ue="65131" gi="953"/><bf us="65281" ue="65374" gi="701"/><bf us="65504" ue="65505" gi="795"/><bf us="65506" ue="65506" gi="967"/><bf us="65507" ue="65507" gi="797"/><bf us="65508" !
ue="65508" gi="968"/><bf us="65509" ue="65509" gi="798"/><bf us="65535
" ue="65535" gi="0"/></bfranges><cid-widths start-index="0"><wx w="1000"/><wx w="0"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="!
500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/></cid-widths></multibyte-extras></font-metrics>
\ No newline at end of file
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/no-idea-what-to-do-with-these-yet/fop/simsun.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/no-idea-what-to-do-with-these-yet/fop/simsun.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/no-idea-what-to-do-with-these-yet/fop/simsun.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<font-metrics type="TYPE0"><font-name>SimSun</font-name><embed/><cap-height>0</cap-height><x-height>0</x-height><ascender>859</ascender><descender>-140</descender><bbox><left>0</left><bottom>-140</bottom><right>996</right><top>855</top></bbox><flags>33</flags><stemv>0</stemv><italicangle>0</italicangle><ttc-name>SimSun</ttc-name><subtype>TYPE0</subtype><multibyte-extras><cid-type>CIDFontType2</cid-type><default-width>0</default-width><bfranges><bf us="32" ue="126" gi="3"/><bf us="127" ue="127" gi="2"/><bf us="160" ue="163" gi="22021"/><bf us="164" ue="164" gi="98"/><bf us="165" ue="166" gi="22025"/><bf us="167" ue="168" gi="99"/><bf us="169" ue="175" gi="22027"/><bf us="176" ue="177" gi="101"/><bf us="178" ue="182" gi="22034"/><bf us="183" ue="183" gi="103"/><bf us="184" ue="214" gi="22039"/><bf us="215" ue="215" gi="104"/><bf us="216" ue="223" gi="22070"/><bf us="224" ue="225" gi="105"/><bf us="226" ue="231" gi="22078"/><bf us="232" ue="234" gi="107"/><bf us="235" ue="235"!
gi="22084"/><bf us="236" ue="237" gi="110"/><bf us="238" ue="241" gi="22085"/><bf us="242" ue="243" gi="112"/><bf us="244" ue="246" gi="22089"/><bf us="247" ue="247" gi="114"/><bf us="248" ue="248" gi="22092"/><bf us="249" ue="250" gi="115"/><bf us="251" ue="251" gi="22093"/><bf us="252" ue="252" gi="117"/><bf us="253" ue="255" gi="22094"/><bf us="257" ue="257" gi="118"/><bf us="275" ue="275" gi="119"/><bf us="283" ue="283" gi="120"/><bf us="299" ue="299" gi="121"/><bf us="324" ue="324" gi="122"/><bf us="328" ue="328" gi="123"/><bf us="333" ue="333" gi="124"/><bf us="338" ue="339" gi="22097"/><bf us="352" ue="353" gi="22099"/><bf us="363" ue="363" gi="125"/><bf us="376" ue="376" gi="22101"/><bf us="402" ue="402" gi="22102"/><bf us="462" ue="462" gi="126"/><bf us="464" ue="464" gi="127"/><bf us="466" ue="466" gi="128"/><bf us="468" ue="468" gi="129"/><bf us="470" ue="470" gi="130"/><bf us="472" ue="472" gi="131"/><bf us="474" ue="474" gi="132"/><bf us="476" ue="476" gi="133!
"/><bf us="505" ue="505" gi="687"/><bf us="593" ue="593" gi="134"/><bf
us="609" ue="609" gi="135"/><bf us="710" ue="710" gi="22103"/><bf us="711" ue="711" gi="136"/><bf us="713" ue="713" gi="137"/><bf us="714" ue="715" gi="799"/><bf us="729" ue="729" gi="801"/><bf us="732" ue="732" gi="22104"/><bf us="913" ue="929" gi="138"/><bf us="931" ue="937" gi="155"/><bf us="945" ue="961" gi="162"/><bf us="963" ue="969" gi="179"/><bf us="1025" ue="1025" gi="186"/><bf us="1040" ue="1103" gi="187"/><bf us="1105" ue="1105" gi="251"/><bf us="8208" ue="8208" gi="971"/><bf us="8211" ue="8211" gi="802"/><bf us="8212" ue="8212" gi="252"/><bf us="8213" ue="8213" gi="803"/><bf us="8214" ue="8214" gi="253"/><bf us="8216" ue="8217" gi="254"/><bf us="8218" ue="8218" gi="22105"/><bf us="8220" ue="8221" gi="256"/><bf us="8222" ue="8222" gi="22106"/><bf us="8224" ue="8226" gi="22107"/><bf us="8229" ue="8229" gi="804"/><bf us="8230" ue="8230" gi="258"/><bf us="8240" ue="8240" gi="259"/><bf us="8242" ue="8243" gi="260"/><bf us="8245" ue="8245" gi="805"/><bf us="8249" ue="!
8250" gi="22110"/><bf us="8251" ue="8251" gi="262"/><bf us="8364" ue="8364" gi="22112"/><bf us="8451" ue="8451" gi="263"/><bf us="8453" ue="8453" gi="806"/><bf us="8457" ue="8457" gi="807"/><bf us="8470" ue="8470" gi="264"/><bf us="8481" ue="8481" gi="969"/><bf us="8482" ue="8482" gi="22113"/><bf us="8544" ue="8555" gi="265"/><bf us="8560" ue="8569" gi="957"/><bf us="8592" ue="8595" gi="277"/><bf us="8598" ue="8601" gi="808"/><bf us="8712" ue="8712" gi="281"/><bf us="8719" ue="8719" gi="282"/><bf us="8721" ue="8721" gi="283"/><bf us="8725" ue="8725" gi="812"/><bf us="8730" ue="8730" gi="284"/><bf us="8733" ue="8734" gi="285"/><bf us="8735" ue="8735" gi="813"/><bf us="8736" ue="8736" gi="287"/><bf us="8739" ue="8739" gi="814"/><bf us="8741" ue="8741" gi="288"/><bf us="8743" ue="8747" gi="289"/><bf us="8750" ue="8750" gi="294"/><bf us="8756" ue="8759" gi="295"/><bf us="8765" ue="8765" gi="299"/><bf us="8776" ue="8776" gi="300"/><bf us="8780" ue="8780" gi="301"/><bf us="8786" !
ue="8786" gi="815"/><bf us="8800" ue="8801" gi="302"/><bf us="8804" ue
="8805" gi="304"/><bf us="8806" ue="8807" gi="816"/><bf us="8814" ue="8815" gi="306"/><bf us="8853" ue="8853" gi="880"/><bf us="8857" ue="8857" gi="308"/><bf us="8869" ue="8869" gi="309"/><bf us="8895" ue="8895" gi="818"/><bf us="8978" ue="8978" gi="310"/><bf us="9312" ue="9321" gi="311"/><bf us="9332" ue="9371" gi="321"/><bf us="9472" ue="9547" gi="361"/><bf us="9552" ue="9587" gi="819"/><bf us="9601" ue="9615" gi="855"/><bf us="9619" ue="9621" gi="870"/><bf us="9632" ue="9633" gi="437"/><bf us="9650" ue="9651" gi="439"/><bf us="9660" ue="9661" gi="873"/><bf us="9670" ue="9671" gi="441"/><bf us="9675" ue="9675" gi="443"/><bf us="9678" ue="9679" gi="444"/><bf us="9698" ue="9701" gi="875"/><bf us="9733" ue="9734" gi="446"/><bf us="9737" ue="9737" gi="879"/><bf us="9792" ue="9792" gi="448"/><bf us="9794" ue="9794" gi="449"/><bf us="11905" ue="11905" gi="21903"/><bf us="11908" ue="11908" gi="21907"/><bf us="11912" ue="11912" gi="21910"/><bf us="11915" ue="11915" gi="21911"/><bf!
us="11916" ue="11916" gi="21916"/><bf us="11943" ue="11943" gi="21930"/><bf us="11946" ue="11946" gi="21933"/><bf us="11950" ue="11950" gi="21936"/><bf us="11955" ue="11955" gi="21938"/><bf us="11958" ue="11959" gi="21939"/><bf us="11963" ue="11963" gi="21944"/><bf us="11978" ue="11978" gi="21954"/><bf us="12272" ue="12283" gi="689"/><bf us="12288" ue="12291" gi="450"/><bf us="12293" ue="12293" gi="454"/><bf us="12294" ue="12294" gi="977"/><bf us="12295" ue="12295" gi="21983"/><bf us="12296" ue="12305" gi="455"/><bf us="12306" ue="12306" gi="881"/><bf us="12307" ue="12311" gi="465"/><bf us="12317" ue="12318" gi="882"/><bf us="12321" ue="12329" gi="884"/><bf us="12353" ue="12435" gi="470"/><bf us="12443" ue="12444" gi="973"/><bf us="12445" ue="12446" gi="978"/><bf us="12449" ue="12534" gi="553"/><bf us="12540" ue="12540" gi="972"/><bf us="12541" ue="12542" gi="975"/><bf us="12549" ue="12585" gi="639"/><bf us="12832" ue="12841" gi="676"/><bf us="12849" ue="12849" gi="970"/><!
bf us="12963" ue="12963" gi="893"/><bf us="13198" ue="13199" gi="894"/
><bf us="13212" ue="13214" gi="896"/><bf us="13217" ue="13217" gi="899"/><bf us="13252" ue="13252" gi="900"/><bf us="13262" ue="13262" gi="901"/><bf us="13265" ue="13266" gi="902"/><bf us="13269" ue="13269" gi="904"/><bf us="13383" ue="13383" gi="21909"/><bf us="13427" ue="13427" gi="21908"/><bf us="13726" ue="13726" gi="21913"/><bf us="13838" ue="13838" gi="21915"/><bf us="13850" ue="13850" gi="21914"/><bf us="14616" ue="14616" gi="21919"/><bf us="14702" ue="14702" gi="21918"/><bf us="14799" ue="14799" gi="21921"/><bf us="14800" ue="14800" gi="21924"/><bf us="14815" ue="14815" gi="21922"/><bf us="14963" ue="14963" gi="21923"/><bf us="15182" ue="15182" gi="21927"/><bf us="15470" ue="15470" gi="21928"/><bf us="15584" ue="15584" gi="21929"/><bf us="16470" ue="16470" gi="21934"/><bf us="16735" ue="16735" gi="21935"/><bf us="17207" ue="17207" gi="21937"/><bf us="17324" ue="17324" gi="21943"/><bf us="17329" ue="17329" gi="21942"/><bf us="17373" ue="17373" gi="21945"/><bf us="1762!
2" ue="17622" gi="21946"/><bf us="17996" ue="17996" gi="21948"/><bf us="18017" ue="18017" gi="21947"/><bf us="18211" ue="18211" gi="21950"/><bf us="18217" ue="18217" gi="21951"/><bf us="18300" ue="18300" gi="21952"/><bf us="18317" ue="18317" gi="21953"/><bf us="18759" ue="18759" gi="21955"/><bf us="18810" ue="18810" gi="21956"/><bf us="18813" ue="18813" gi="21957"/><bf us="18818" ue="18819" gi="21958"/><bf us="18821" ue="18822" gi="21960"/><bf us="18843" ue="18843" gi="21963"/><bf us="18847" ue="18847" gi="21962"/><bf us="18870" ue="18870" gi="21965"/><bf us="18871" ue="18871" gi="21964"/><bf us="19575" ue="19575" gi="21972"/><bf us="19615" ue="19615" gi="21969"/><bf us="19617" ue="19617" gi="21971"/><bf us="19618" ue="19618" gi="21973"/><bf us="19619" ue="19619" gi="21968"/><bf us="19629" ue="19629" gi="21970"/><bf us="19731" ue="19737" gi="21974"/><bf us="19886" ue="19886" gi="21981"/><bf us="19968" ue="40869" gi="980"/><bf us="59335" ue="59336" gi="686"/><bf us="59367" u!
e="59379" gi="688"/><bf us="59413" ue="59492" gi="21903"/><bf us="6378
8" ue="63788" gi="21882"/><bf us="63865" ue="63865" gi="21883"/><bf us="63893" ue="63893" gi="21884"/><bf us="63975" ue="63975" gi="21885"/><bf us="63985" ue="63985" gi="21886"/><bf us="64012" ue="64015" gi="21887"/><bf us="64017" ue="64017" gi="21891"/><bf us="64019" ue="64020" gi="21892"/><bf us="64024" ue="64024" gi="21894"/><bf us="64031" ue="64033" gi="21895"/><bf us="64035" ue="64036" gi="21898"/><bf us="64039" ue="64041" gi="21900"/><bf us="65072" ue="65073" gi="905"/><bf us="65075" ue="65092" gi="907"/><bf us="65097" ue="65106" gi="925"/><bf us="65108" ue="65111" gi="935"/><bf us="65113" ue="65126" gi="939"/><bf us="65128" ue="65131" gi="953"/><bf us="65281" ue="65374" gi="701"/><bf us="65504" ue="65505" gi="795"/><bf us="65506" ue="65506" gi="967"/><bf us="65507" ue="65507" gi="797"/><bf us="65508" ue="65508" gi="968"/><bf us="65509" ue="65509" gi="798"/><bf us="65535" ue="65535" gi="0"/></bfranges><cid-widths start-index="0"><wx w="1000"/><wx w="0"/><wx w="500"/><w!
x w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><w!
x w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="
500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1!
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1
000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000!
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000
"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>!
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/>
<wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx!
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=!
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w=
"1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10!
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="10
00"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"!
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"
/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><!
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><
wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx !
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx
w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="!
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="
1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><w!
x w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100!
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="100
0"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w!
="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/!
><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/
><wx w="1000"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/!
><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="500"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/><wx w="1000"/></cid-widths></multibyte-extras></font-metrics>
\ No newline at end of file
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/no-idea-what-to-do-with-these-yet/fop/userconfig.xml
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/no-idea-what-to-do-with-these-yet/fop/userconfig.xml (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/no-idea-what-to-do-with-these-yet/fop/userconfig.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,114 @@
+<!--<!DOCTYPE configuration SYSTEM "config.dtd">-->
+<!--
+ this file contains templates which allow an user easy
+ configuration of Fop. Actually normally you don't need this configuration
+ file, but if you need to change configuration, you should
+ always use this file and *not* config.xml.
+ Usage: java org.apache.fop.apps.Fop -c userconfig.xml -fo fo-file -pdf pdf-file
+-->
+
+
+<configuration>
+
+<!--
+baseDir: normally the base directory is the directory where the fo file is
+ located. if you want to specify your own, uncomment this entry.
+ This value can also be a URL. Actually, the value is converted to
+ a URL.
+-->
+<!--
+ <entry>
+ <key>baseDir</key>
+ <value></value>
+ </entry>
+-->
+
+<!--
+fontBaseDir: Similar to baseDir, except that this value is used for fonts. If
+ it isn't specified, the value from baseDir is used.
+-->
+<!--
+ <entry>
+ <key>fontBaseDir</key>
+ <value></value>
+ </entry>
+-->
+
+<!--
+************************************************************************
+ HYPHENATION
+************************************************************************
+-->
+
+<!--
+ hyphenation directory
+ if you want to specify your own directory with hyphenation pattern
+ then uncomment the next entry and add the directory name
+-->
+
+<!--
+ <entry>
+ <key>hyphenation-dir</key>
+ <value>/java/xml-fop/hyph</value>
+ </entry>
+-->
+
+<!--
+************************************************************************
+ Add fonts here
+************************************************************************
+-->
+
+<fonts>
+ <!-- example -->
+ <!--
+ <font metrics-file="arial.xml" kerning="yes" embed-file="arial.ttf">
+ <font-triplet name="Arial" style="normal" weight="normal"/>
+ <font-triplet name="ArialMT" style="normal" weight="normal"/>
+ </font>
+ <font metrics-file="arialb.xml" kerning="yes" embed-file="arialb.ttf">
+ <font-triplet name="Arial" style="normal" weight="bold"/>
+ <font-triplet name="ArialMT" style="normal" weight="bold"/>
+ </font>
+ <font metrics-file="ariali.xml" kerning="yes" embed-file="ariali.ttf">
+ <font-triplet name="Arial" style="italic" weight="normal"/>
+ <font-triplet name="ArialMT" style="italic" weight="normal"/>
+ </font>
+ <font metrics-file="arialbi.xml" kerning="yes" embed-file="arialbi.ttf">
+ <font-triplet name="Arial" style="italic" weight="bold"/>
+ <font-triplet name="ArialMT" style="italic" weight="bold"/>
+ </font>
+ -->
+ <!-- Example Japanese fonts
+ <font metrics-file="msgothic.xml" embed-file="D:\winnt\font\msgothic.ttc" kerning="yes">
+ <font-triplet name="Gothic" style="normal" weight="normal"/>
+ <font-triplet name="Gothic" style="normal" weight="bold"/>
+ <font-triplet name="Gothic" style="italic" weight="normal"/>
+ <font-triplet name="Gothic" style="italic" weight="bold"/>
+ </font>
+ <font metrics-file="msmincho.xml" embed-file="Cyberbit.ttf" kerning="yes">
+ <font-triplet name="Mincho" style="normal" weight="normal"/>
+ <font-triplet name="Mincho" style="normal" weight="bold"/>
+ <font-triplet name="Mincho" style="italic" weight="normal"/>
+ <font-triplet name="Mincho" style="italic" weight="bold"/>
+ </font>
+ -->
+ <font metrics-file="simhei.xml" embed-file="simhei.ttf" kerning="yes">
+ <font-triplet name="simhei" style="normal" weight="normal"/>
+ <font-triplet name="simhei" style="normal" weight="bold"/>
+ <font-triplet name="simhei" style="italic" weight="normal"/>
+ <font-triplet name="simhei" style="italic" weight="bold"/>
+ </font>
+ <font metrics-file="simsun.xml" embed-file="simsun.ttc" kerning="yes">
+ <font-triplet name="simsun" style="normal" weight="normal"/>
+ <font-triplet name="simsun" style="normal" weight="bold"/>
+ <font-triplet name="simsun" style="italic" weight="normal"/>
+ <font-triplet name="simsun" style="italic" weight="bold"/>
+ </font>
+</fonts>
+
+
+</configuration>
+
+
+
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/no-idea-what-to-do-with-these-yet/styles/fopdf.xsl
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/no-idea-what-to-do-with-these-yet/styles/fopdf.xsl (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/no-idea-what-to-do-with-these-yet/styles/fopdf.xsl 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,509 @@
+<?xml version="1.0"?>
+
+<!--
+
+ This is the XSL FO configuration file for the Hibernate
+ Reference Documentation. It defines a custom titlepage and
+ the parameters for the A4 sized PDF printable output.
+
+ It took me days to figure out this stuff and fix most of
+ the obvious bugs in the DocBook XSL distribution. Some of
+ the workarounds might not be appropriate with a newer version
+ of DocBook XSL. This file is released as part of Hibernate,
+ hence LGPL licensed.
+
+ christian at hibernate.org
+
+-->
+
+<!DOCTYPE xsl:stylesheet [
+ <!ENTITY db_xsl_path "../../support/docbook-xsl/">
+]>
+
+<xsl:stylesheet
+ version="1.0"
+ xmlns="http://www.w3.org/TR/xhtml1/transitional"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:fo="http://www.w3.org/1999/XSL/Format"
+ exclude-result-prefixes="#default">
+
+ <xsl:import href="&db_xsl_path;/fo/docbook.xsl"/>
+
+ <!--###################################################
+ Custom Title Page
+ ################################################### -->
+
+ <xsl:template name="book.titlepage.recto">
+ <fo:block>
+ <fo:table table-layout="fixed" width="175mm">
+ <fo:table-column column-width="175mm"/>
+ <fo:table-body>
+ <fo:table-row>
+ <fo:table-cell text-align="center">
+ <fo:block>
+ <fo:external-graphic src="file:images/hibernate_logo_a.png"/>
+ </fo:block>
+ <fo:block font-family="Helvetica" font-size="22pt" padding-before="10mm">
+ <xsl:value-of select="bookinfo/subtitle"/>
+ </fo:block>
+ <fo:block font-family="Helvetica" font-size="12pt" padding="10mm">
+ Version:
+ <xsl:value-of select="bookinfo/releaseinfo"/>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ </fo:table-body>
+ </fo:table>
+ </fo:block>
+ </xsl:template>
+
+ <!-- Prevent blank pages in output -->
+ <xsl:template name="book.titlepage.before.verso">
+ </xsl:template>
+ <xsl:template name="book.titlepage.verso">
+ </xsl:template>
+ <xsl:template name="book.titlepage.separator">
+ </xsl:template>
+
+ <!--###################################################
+ Header
+ ################################################### -->
+
+ <!-- More space in the center header for long text -->
+ <xsl:attribute-set name="header.content.properties">
+ <xsl:attribute name="font-family">
+ <xsl:value-of select="$body.font.family"/>
+ </xsl:attribute>
+ <xsl:attribute name="margin-left">-5em</xsl:attribute>
+ <xsl:attribute name="margin-right">-5em</xsl:attribute>
+ </xsl:attribute-set>
+
+ <!--###################################################
+ Custom Footer
+ ################################################### -->
+
+ <!-- This footer prints the Hibernate version number on the left side -->
+ <xsl:template name="footer.content">
+ <xsl:param name="pageclass" select="''"/>
+ <xsl:param name="sequence" select="''"/>
+ <xsl:param name="position" select="''"/>
+ <xsl:param name="gentext-key" select="''"/>
+
+ <xsl:variable name="Version">
+ <xsl:choose>
+ <xsl:when test="//releaseinfo">
+ <xsl:text>Hibernate </xsl:text>
+ <xsl:value-of select="//releaseinfo"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- nop -->
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:choose>
+ <xsl:when test="$sequence='blank'">
+ <xsl:choose>
+ <xsl:when test="$double.sided != 0 and $position = 'left'">
+ <xsl:value-of select="$Version"/>
+ </xsl:when>
+
+ <xsl:when test="$double.sided = 0 and $position = 'center'">
+ <!-- nop -->
+ </xsl:when>
+
+ <xsl:otherwise>
+ <fo:page-number/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+
+ <xsl:when test="$pageclass='titlepage'">
+ <!-- nop: other titlepage sequences have no footer -->
+ </xsl:when>
+
+ <xsl:when test="$double.sided != 0 and $sequence = 'even' and $position='left'">
+ <fo:page-number/>
+ </xsl:when>
+
+ <xsl:when test="$double.sided != 0 and $sequence = 'odd' and $position='right'">
+ <fo:page-number/>
+ </xsl:when>
+
+ <xsl:when test="$double.sided = 0 and $position='right'">
+ <fo:page-number/>
+ </xsl:when>
+
+ <xsl:when test="$double.sided != 0 and $sequence = 'odd' and $position='left'">
+ <xsl:value-of select="$Version"/>
+ </xsl:when>
+
+ <xsl:when test="$double.sided != 0 and $sequence = 'even' and $position='right'">
+ <xsl:value-of select="$Version"/>
+ </xsl:when>
+
+ <xsl:when test="$double.sided = 0 and $position='left'">
+ <xsl:value-of select="$Version"/>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <!-- nop -->
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!--###################################################
+ Custom Toc Line
+ ################################################### -->
+
+ <!-- Improve the TOC. -->
+ <xsl:template name="toc.line">
+ <xsl:variable name="id">
+ <xsl:call-template name="object.id"/>
+ </xsl:variable>
+
+ <xsl:variable name="label">
+ <xsl:apply-templates select="." mode="label.markup"/>
+ </xsl:variable>
+
+ <fo:block text-align-last="justify"
+ end-indent="{$toc.indent.width}pt"
+ last-line-end-indent="-{$toc.indent.width}pt">
+ <fo:inline keep-with-next.within-line="always">
+ <fo:basic-link internal-destination="{$id}">
+
+ <!-- Chapter titles should be bold. -->
+ <xsl:choose>
+ <xsl:when test="local-name(.) = 'chapter'">
+ <xsl:attribute name="font-weight">bold</xsl:attribute>
+ </xsl:when>
+ </xsl:choose>
+
+ <xsl:if test="$label != ''">
+ <xsl:copy-of select="$label"/>
+ <xsl:value-of select="$autotoc.label.separator"/>
+ </xsl:if>
+ <xsl:apply-templates select="." mode="titleabbrev.markup"/>
+ </fo:basic-link>
+ </fo:inline>
+ <fo:inline keep-together.within-line="always">
+ <xsl:text> </xsl:text>
+ <fo:leader leader-pattern="dots"
+ leader-pattern-width="3pt"
+ leader-alignment="reference-area"
+ keep-with-next.within-line="always"/>
+ <xsl:text> </xsl:text>
+ <fo:basic-link internal-destination="{$id}">
+ <fo:page-number-citation ref-id="{$id}"/>
+ </fo:basic-link>
+ </fo:inline>
+ </fo:block>
+ </xsl:template>
+
+ <!--###################################################
+ Extensions
+ ################################################### -->
+
+ <!-- These extensions are required for table printing and other stuff -->
+ <xsl:param name="use.extensions">1</xsl:param>
+ <xsl:param name="tablecolumns.extension">0</xsl:param>
+ <!-- FOP provide only PDF Bookmarks at the moment -->
+ <xsl:param name="fop.extensions">1</xsl:param>
+
+ <!--###################################################
+ Table Of Contents
+ ################################################### -->
+
+ <!-- Generate the TOCs for named components only -->
+ <xsl:param name="generate.toc">
+ book toc
+ </xsl:param>
+
+ <!-- Show only Sections up to level 3 in the TOCs -->
+ <xsl:param name="toc.section.depth">3</xsl:param>
+
+ <!-- Dot and Whitespace as separator in TOC between Label and Title-->
+ <xsl:param name="autotoc.label.separator" select="'. '"/>
+
+
+ <!--###################################################
+ Paper & Page Size
+ ################################################### -->
+
+ <!-- Paper type, no headers on blank pages, no double sided printing -->
+ <xsl:param name="paper.type" select="'A4'"/>
+ <xsl:param name="double.sided">0</xsl:param>
+ <xsl:param name="headers.on.blank.pages">0</xsl:param>
+ <xsl:param name="footers.on.blank.pages">0</xsl:param>
+
+ <!-- Space between paper border and content (chaotic stuff, don't touch) -->
+ <xsl:param name="page.margin.top">5mm</xsl:param>
+ <xsl:param name="region.before.extent">10mm</xsl:param>
+ <xsl:param name="body.margin.top">10mm</xsl:param>
+
+ <xsl:param name="body.margin.bottom">15mm</xsl:param>
+ <xsl:param name="region.after.extent">10mm</xsl:param>
+ <xsl:param name="page.margin.bottom">0mm</xsl:param>
+
+ <xsl:param name="page.margin.outer">18mm</xsl:param>
+ <xsl:param name="page.margin.inner">18mm</xsl:param>
+
+ <!-- No intendation of Titles -->
+ <xsl:param name="title.margin.left">0pc</xsl:param>
+
+ <!--###################################################
+ Fonts & Styles
+ ################################################### -->
+
+ <!-- Default Font size -->
+ <xsl:param name="body.font.master">11</xsl:param>
+
+ <!-- Line height in body text -->
+ <xsl:param name="line-height">1.4</xsl:param>
+
+ <!-- Monospaced fonts are smaller than regular text -->
+ <xsl:attribute-set name="monospace.properties">
+ <xsl:attribute name="font-family">
+ <xsl:value-of select="$monospace.font.family"/>
+ </xsl:attribute>
+ <xsl:attribute name="font-size">0.8em</xsl:attribute>
+ </xsl:attribute-set>
+
+
+ <!--###################################################
+ Tables
+ ################################################### -->
+
+ <!-- The table width should be adapted to the paper size -->
+ <xsl:param name="default.table.width">17.4cm</xsl:param>
+
+ <!-- Some padding inside tables -->
+ <xsl:attribute-set name="table.cell.padding">
+ <xsl:attribute name="padding-left">4pt</xsl:attribute>
+ <xsl:attribute name="padding-right">4pt</xsl:attribute>
+ <xsl:attribute name="padding-top">4pt</xsl:attribute>
+ <xsl:attribute name="padding-bottom">4pt</xsl:attribute>
+ </xsl:attribute-set>
+
+ <!-- Only hairlines as frame and cell borders in tables -->
+ <xsl:param name="table.frame.border.thickness">0.1pt</xsl:param>
+ <xsl:param name="table.cell.border.thickness">0.1pt</xsl:param>
+
+ <!--###################################################
+ Labels
+ ################################################### -->
+
+ <!-- Label Chapters and Sections (numbering) -->
+ <xsl:param name="chapter.autolabel">1</xsl:param>
+ <xsl:param name="section.autolabel" select="1"/>
+ <xsl:param name="section.label.includes.component.label" select="1"/>
+
+ <!--###################################################
+ Titles
+ ################################################### -->
+
+ <!-- Chapter title size -->
+ <xsl:attribute-set name="chapter.titlepage.recto.style">
+ <xsl:attribute name="text-align">left</xsl:attribute>
+ <xsl:attribute name="font-weight">bold</xsl:attribute>
+ <xsl:attribute name="font-size">
+ <xsl:value-of select="$body.font.master * 1.8"/>
+ <xsl:text>pt</xsl:text>
+ </xsl:attribute>
+ </xsl:attribute-set>
+
+ <!-- Why is the font-size for chapters hardcoded in the XSL FO templates?
+ Let's remove it, so this sucker can use our attribute-set only... -->
+ <xsl:template match="title" mode="chapter.titlepage.recto.auto.mode">
+ <fo:block xmlns:fo="http://www.w3.org/1999/XSL/Format"
+ xsl:use-attribute-sets="chapter.titlepage.recto.style">
+ <xsl:call-template name="component.title">
+ <xsl:with-param name="node" select="ancestor-or-self::chapter[1]"/>
+ </xsl:call-template>
+ </fo:block>
+ </xsl:template>
+
+ <!-- Sections 1, 2 and 3 titles have a small bump factor and padding -->
+ <xsl:attribute-set name="section.title.level1.properties">
+ <xsl:attribute name="space-before.optimum">0.8em</xsl:attribute>
+ <xsl:attribute name="space-before.minimum">0.8em</xsl:attribute>
+ <xsl:attribute name="space-before.maximum">0.8em</xsl:attribute>
+ <xsl:attribute name="font-size">
+ <xsl:value-of select="$body.font.master * 1.5"/>
+ <xsl:text>pt</xsl:text>
+ </xsl:attribute>
+ <xsl:attribute name="space-after.optimum">0.1em</xsl:attribute>
+ <xsl:attribute name="space-after.minimum">0.1em</xsl:attribute>
+ <xsl:attribute name="space-after.maximum">0.1em</xsl:attribute>
+ </xsl:attribute-set>
+ <xsl:attribute-set name="section.title.level2.properties">
+ <xsl:attribute name="space-before.optimum">0.6em</xsl:attribute>
+ <xsl:attribute name="space-before.minimum">0.6em</xsl:attribute>
+ <xsl:attribute name="space-before.maximum">0.6em</xsl:attribute>
+ <xsl:attribute name="font-size">
+ <xsl:value-of select="$body.font.master * 1.25"/>
+ <xsl:text>pt</xsl:text>
+ </xsl:attribute>
+ <xsl:attribute name="space-after.optimum">0.1em</xsl:attribute>
+ <xsl:attribute name="space-after.minimum">0.1em</xsl:attribute>
+ <xsl:attribute name="space-after.maximum">0.1em</xsl:attribute>
+ </xsl:attribute-set>
+ <xsl:attribute-set name="section.title.level3.properties">
+ <xsl:attribute name="space-before.optimum">0.4em</xsl:attribute>
+ <xsl:attribute name="space-before.minimum">0.4em</xsl:attribute>
+ <xsl:attribute name="space-before.maximum">0.4em</xsl:attribute>
+ <xsl:attribute name="font-size">
+ <xsl:value-of select="$body.font.master * 1.0"/>
+ <xsl:text>pt</xsl:text>
+ </xsl:attribute>
+ <xsl:attribute name="space-after.optimum">0.1em</xsl:attribute>
+ <xsl:attribute name="space-after.minimum">0.1em</xsl:attribute>
+ <xsl:attribute name="space-after.maximum">0.1em</xsl:attribute>
+ </xsl:attribute-set>
+
+ <!-- Titles of formal objects (tables, examples, ...) -->
+ <xsl:attribute-set name="formal.title.properties" use-attribute-sets="normal.para.spacing">
+ <xsl:attribute name="font-weight">bold</xsl:attribute>
+ <xsl:attribute name="font-size">
+ <xsl:value-of select="$body.font.master"/>
+ <xsl:text>pt</xsl:text>
+ </xsl:attribute>
+ <xsl:attribute name="hyphenate">false</xsl:attribute>
+ <xsl:attribute name="space-after.minimum">0.4em</xsl:attribute>
+ <xsl:attribute name="space-after.optimum">0.6em</xsl:attribute>
+ <xsl:attribute name="space-after.maximum">0.8em</xsl:attribute>
+ </xsl:attribute-set>
+
+ <!--###################################################
+ Programlistings
+ ################################################### -->
+
+ <!-- Verbatim text formatting (programlistings) -->
+ <xsl:attribute-set name="verbatim.properties">
+ <xsl:attribute name="space-before.minimum">1em</xsl:attribute>
+ <xsl:attribute name="space-before.optimum">1em</xsl:attribute>
+ <xsl:attribute name="space-before.maximum">1em</xsl:attribute>
+ <xsl:attribute name="space-after.minimum">0.1em</xsl:attribute>
+ <xsl:attribute name="space-after.optimum">0.1em</xsl:attribute>
+ <xsl:attribute name="space-after.maximum">0.1em</xsl:attribute>
+ <xsl:attribute name="border-color">#444444</xsl:attribute>
+ <xsl:attribute name="border-style">solid</xsl:attribute>
+ <xsl:attribute name="border-width">0.1pt</xsl:attribute>
+ <xsl:attribute name="padding-top">0.5em</xsl:attribute>
+ <xsl:attribute name="padding-left">0.5em</xsl:attribute>
+ <xsl:attribute name="padding-right">0.5em</xsl:attribute>
+ <xsl:attribute name="padding-bottom">0.5em</xsl:attribute>
+ <xsl:attribute name="margin-left">0.5em</xsl:attribute>
+ <xsl:attribute name="margin-right">0.5em</xsl:attribute>
+ </xsl:attribute-set>
+
+ <!-- Shade (background) programlistings -->
+ <xsl:param name="shade.verbatim">1</xsl:param>
+ <xsl:attribute-set name="shade.verbatim.style">
+ <xsl:attribute name="background-color">#F0F0F0</xsl:attribute>
+ </xsl:attribute-set>
+
+ <!--###################################################
+ Callouts
+ ################################################### -->
+
+ <!-- We want to use callouts... -->
+ <xsl:param name="callout.extensions">1</xsl:param>
+
+ <!-- Place callout bullets at this column in programmlisting.-->
+ <xsl:param name="callout.defaultcolumn">90</xsl:param>
+
+ <!--
+ No, don't use crappy graphics for the callout bullets. This setting
+ enables some weird Unicode rendering for some fancy bullet points
+ in callouts. By default, this can only count to 10 and produces
+ strange results if you ever have more than 10 callouts for one
+ programlisting. We will fix that next.
+ -->
+ <xsl:param name="callout.graphics">0</xsl:param>
+
+ <!--
+ Again, fun with DocBook XSL: The callout bullets are rendered in
+ two places: In the programlisting itself and in the list below
+ the listing, with the actual callout text. The rendering in the
+ programlisting is some XSL transformer extension (e.g. a Saxon
+ extension), so we can't change that without messing with the
+ extensions. We only can turn it off by setting this limit to
+ zero, then, a simple bracket style like "(3)" and "(4)" will
+ be used in the programlisting.
+ -->
+ <xsl:param name="callout.unicode.number.limit" select="'0'"></xsl:param>
+
+ <!--
+ The callout bullets in the actual callout list will be rendered
+ with an XSL FO template. The default template is broken: limited to 10
+ nice looking Unicode bullet points and then it doesn't print anything,
+ the fallback doesn't work. We implement our own template, which is not
+ as complicated, more ugly, but works. As always, function is more
+ important than form.
+ -->
+ <xsl:template name="callout-bug">
+ <xsl:param name="conum" select='1'/>
+ <fo:inline
+ color="black"
+ padding-top="0.1em"
+ padding-bottom="0.1em"
+ padding-start="0.2em"
+ padding-end="0.2em"
+ baseline-shift="0.1em"
+ font-family="{$monospace.font.family}"
+ font-weight="bold"
+ font-size="75%">
+ <xsl:text>(</xsl:text>
+ <xsl:value-of select="$conum"/>
+ <xsl:text>)</xsl:text>
+ </fo:inline>
+
+ </xsl:template>
+
+ <!--###################################################
+ Misc
+ ################################################### -->
+
+ <!-- Correct placement of titles for figures and examples. -->
+ <xsl:param name="formal.title.placement">
+ figure after
+ example before
+ equation before
+ table before
+ procedure before
+ </xsl:param>
+
+ <!-- Format Variable Lists as Blocks (prevents horizontal overflow). -->
+ <xsl:param name="variablelist.as.blocks">1</xsl:param>
+
+ <!-- The horrible list spacing problems, this is much better. -->
+ <xsl:attribute-set name="list.block.spacing">
+ <xsl:attribute name="space-before.optimum">0.8em</xsl:attribute>
+ <xsl:attribute name="space-before.minimum">0.8em</xsl:attribute>
+ <xsl:attribute name="space-before.maximum">0.8em</xsl:attribute>
+ <xsl:attribute name="space-after.optimum">0.1em</xsl:attribute>
+ <xsl:attribute name="space-after.minimum">0.1em</xsl:attribute>
+ <xsl:attribute name="space-after.maximum">0.1em</xsl:attribute>
+ </xsl:attribute-set>
+
+ <!-- Newer DocBook XSL apparently thinks that some sections are by
+ default "draft" status, and this idiotic thing is by default
+ also set to "maybe", so it spits out a lot of errors with the
+ latest FOP as the XSL/FO styles have references to some draft
+ watermarks, which you actually don't want in the first place.
+ Turn this crap off. If you have to work with the "status"
+ attribute, don't.
+ -->
+ <xsl:param name="draft.mode" select="'no'"/>
+
+ <!-- Simplified Chinese related Settings -->
+ <xsl:param name="hyphenate">false</xsl:param>
+ <xsl:param name="body.font.family">simsun</xsl:param>
+ <xsl:param name="monospace.font.family">simsun</xsl:param>
+ <xsl:param name="title.font.family">simhei</xsl:param>
+ <xsl:param name="saxon.character.representation" select="native"/>
+ <xsl:param name="callout.unicode" select="1"/>
+ <xsl:param name="callout.unicode.start.character" select="10102"/>
+ <xsl:param name="l10n.gentext.default.language" select="zh-cn"/>
+</xsl:stylesheet>
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/no-idea-what-to-do-with-these-yet/styles/html.css
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/no-idea-what-to-do-with-these-yet/styles/html.css (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/no-idea-what-to-do-with-these-yet/styles/html.css 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,97 @@
+A {
+ color: #003399;
+}
+
+A:active {
+ color: #003399;
+}
+
+A:visited {
+ color: #888888;
+}
+
+P, OL, UL, LI, DL, DT, DD, BLOCKQUOTE {
+ color: #000000;
+}
+
+TD, TH, SPAN {
+ color: #000000;
+}
+
+BLOCKQUOTE {
+ margin-right: 0px;
+}
+
+
+H1, H2, H3, H4, H5, H6 {
+ color: #000000;
+ font-weight:500;
+ margin-top:10px;
+ padding-top:15px;
+}
+
+TABLE {
+ border-collapse: collapse;
+ border-spacing:0;
+ border: 1px thin black;
+ empty-cells: hide;
+}
+
+TD {
+ padding: 4pt;
+}
+
+H1 { font-size: 150%; }
+H2 { font-size: 140%; }
+H3 { font-size: 110%; font-weight: bold; }
+H4 { font-size: 110%; font-weight: bold;}
+H5 { font-size: 100%; font-style: italic; }
+H6 { font-size: 100%; font-style: italic; }
+
+TT {
+font-size: 90%;
+ font-family: "Courier New", Courier, monospace;
+ color: #000000;
+}
+
+PRE {
+font-size: 100%;
+ padding: 5px;
+ border-style: solid;
+ border-width: 1px;
+ border-color: #CCCCCC;
+ background-color: #F4F4F4;
+}
+
+UL, OL, LI {
+ list-style: disc;
+}
+
+HR {
+ width: 100%;
+ height: 1px;
+ background-color: #CCCCCC;
+ border-width: 0px;
+ padding: 0px;
+ color: #CCCCCC;
+}
+
+.variablelist {
+ padding-top: 10;
+ padding-bottom:10;
+ margin:0;
+}
+
+.itemizedlist, UL {
+ padding-top: 0;
+ padding-bottom:0;
+ margin:0;
+}
+
+.term {
+ font-weight:bold;
+}
+
+
+
+
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/no-idea-what-to-do-with-these-yet/styles/html.xsl
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/no-idea-what-to-do-with-these-yet/styles/html.xsl (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/no-idea-what-to-do-with-these-yet/styles/html.xsl 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,84 @@
+<?xml version="1.0"?>
+
+<!--
+
+ This is the XSL HTML configuration file for the Hibernate
+ Reference Documentation.
+
+ It took me days to figure out this stuff and fix most of
+ the obvious bugs in the DocBook XSL distribution. Some of
+ the workarounds might not be appropriate with a newer version
+ of DocBook XSL. This file is released as part of Hibernate,
+ hence LGPL licensed.
+
+ christian at hibernate.org
+-->
+
+<!DOCTYPE xsl:stylesheet [
+ <!ENTITY db_xsl_path "../../support/docbook-xsl/">
+]>
+
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ version="1.0"
+ xmlns="http://www.w3.org/TR/xhtml1/transitional"
+ exclude-result-prefixes="#default">
+
+<xsl:import href="&db_xsl_path;/html/docbook.xsl"/>
+
+<!--###################################################
+ HTML Settings
+ ################################################### -->
+
+ <xsl:param name="html.stylesheet">../shared/css/html.css</xsl:param>
+
+ <!-- These extensions are required for table printing and other stuff -->
+ <xsl:param name="use.extensions">1</xsl:param>
+ <xsl:param name="tablecolumns.extension">0</xsl:param>
+ <xsl:param name="callout.extensions">1</xsl:param>
+ <xsl:param name="graphicsize.extension">0</xsl:param>
+
+<!--###################################################
+ Table Of Contents
+ ################################################### -->
+
+ <!-- Generate the TOCs for named components only -->
+ <xsl:param name="generate.toc">
+ book toc
+ </xsl:param>
+
+ <!-- Show only Sections up to level 3 in the TOCs -->
+ <xsl:param name="toc.section.depth">3</xsl:param>
+
+<!--###################################################
+ Labels
+ ################################################### -->
+
+ <!-- Label Chapters and Sections (numbering) -->
+ <xsl:param name="chapter.autolabel">1</xsl:param>
+ <xsl:param name="section.autolabel" select="1"/>
+ <xsl:param name="section.label.includes.component.label" select="1"/>
+
+<!--###################################################
+ Callouts
+ ################################################### -->
+
+ <!-- Don't use graphics, use a simple number style -->
+ <xsl:param name="callout.graphics">0</xsl:param>
+
+ <!-- Place callout marks at this column in annotated areas -->
+ <xsl:param name="callout.defaultcolumn">90</xsl:param>
+
+<!--###################################################
+ Misc
+ ################################################### -->
+
+ <!-- Placement of titles -->
+ <xsl:param name="formal.title.placement">
+ figure after
+ example before
+ equation before
+ table before
+ procedure before
+ </xsl:param>
+
+</xsl:stylesheet>
Added: trunk/Hibernate3/documentation/manual/zh-CN/src/no-idea-what-to-do-with-these-yet/styles/html_chunk.xsl
===================================================================
--- trunk/Hibernate3/documentation/manual/zh-CN/src/no-idea-what-to-do-with-these-yet/styles/html_chunk.xsl (rev 0)
+++ trunk/Hibernate3/documentation/manual/zh-CN/src/no-idea-what-to-do-with-these-yet/styles/html_chunk.xsl 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,86 @@
+<?xml version="1.0"?>
+
+<!--
+
+ This is the XSL HTML configuration file for the Hibernate
+ Reference Documentation.
+
+ It took me days to figure out this stuff and fix most of
+ the obvious bugs in the DocBook XSL distribution. Some of
+ the workarounds might not be appropriate with a newer version
+ of DocBook XSL. This file is released as part of Hibernate,
+ hence LGPL licensed.
+
+ christian at hibernate.org
+-->
+
+<!DOCTYPE xsl:stylesheet [
+ <!ENTITY db_xsl_path "../../support/docbook-xsl/">
+]>
+
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ version="1.0"
+ xmlns="http://www.w3.org/TR/xhtml1/transitional"
+ exclude-result-prefixes="#default">
+
+<xsl:import href="&db_xsl_path;/html/chunk.xsl"/>
+
+<!--###################################################
+ HTML Settings
+ ################################################### -->
+
+ <xsl:param name="chunk.section.depth">'5'</xsl:param>
+ <xsl:param name="use.id.as.filename">'1'</xsl:param>
+ <xsl:param name="html.stylesheet">../shared/css/html.css</xsl:param>
+
+ <!-- These extensions are required for table printing and other stuff -->
+ <xsl:param name="use.extensions">1</xsl:param>
+ <xsl:param name="tablecolumns.extension">0</xsl:param>
+ <xsl:param name="callout.extensions">1</xsl:param>
+ <xsl:param name="graphicsize.extension">0</xsl:param>
+
+<!--###################################################
+ Table Of Contents
+ ################################################### -->
+
+ <!-- Generate the TOCs for named components only -->
+ <xsl:param name="generate.toc">
+ book toc
+ </xsl:param>
+
+ <!-- Show only Sections up to level 3 in the TOCs -->
+ <xsl:param name="toc.section.depth">3</xsl:param>
+
+<!--###################################################
+ Labels
+ ################################################### -->
+
+ <!-- Label Chapters and Sections (numbering) -->
+ <xsl:param name="chapter.autolabel">1</xsl:param>
+ <xsl:param name="section.autolabel" select="1"/>
+ <xsl:param name="section.label.includes.component.label" select="1"/>
+
+<!--###################################################
+ Callouts
+ ################################################### -->
+
+ <!-- Don't use graphics, use a simple number style -->
+ <xsl:param name="callout.graphics">0</xsl:param>
+
+ <!-- Place callout marks at this column in annotated areas -->
+ <xsl:param name="callout.defaultcolumn">90</xsl:param>
+
+<!--###################################################
+ Misc
+ ################################################### -->
+
+ <!-- Placement of titles -->
+ <xsl:param name="formal.title.placement">
+ figure after
+ example before
+ equation before
+ table before
+ procedure before
+ </xsl:param>
+
+</xsl:stylesheet>
Added: trunk/Hibernate3/documentation/pom.xml
===================================================================
--- trunk/Hibernate3/documentation/pom.xml (rev 0)
+++ trunk/Hibernate3/documentation/pom.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,24 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-core-project</artifactId>
+ <version>3.3.0.beta1</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>hibernate-documentation</artifactId>
+ <packaging>pom</packaging>
+ <name>Hibernate Documentation Project</name>
+ <description>The grouping project for Hibernate documentation</description>
+
+ <modules>
+ <module>manual</module>
+ <module>tutorial</module>
+ </modules>
+
+</project>
Added: trunk/Hibernate3/documentation/tutorial/pom.xml
===================================================================
--- trunk/Hibernate3/documentation/tutorial/pom.xml (rev 0)
+++ trunk/Hibernate3/documentation/tutorial/pom.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,27 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-core-project</artifactId>
+ <version>3.3.0.beta1</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-tutorial</artifactId>
+ <packaging>jar</packaging>
+ <name>Hibernate Tutorial</name>
+ <description>A tutorial project showcasing Hibernate usage</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-core</artifactId>
+ </dependency>
+ </dependencies>
+
+</project>
Added: trunk/Hibernate3/documentation/tutorial/src/main/java/events/Event.hbm.xml
===================================================================
--- trunk/Hibernate3/documentation/tutorial/src/main/java/events/Event.hbm.xml (rev 0)
+++ trunk/Hibernate3/documentation/tutorial/src/main/java/events/Event.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping>
+
+ <class name="events.Event" table="EVENTS">
+ <id name="id" column="EVENT_ID">
+ <generator class="native"/>
+ </id>
+ <property name="date" type="timestamp" column="EVENT_DATE"/>
+ <property name="title"/>
+
+ <set name="participants" table="PERSON_EVENT" inverse="true">
+ <key column="EVENT_ID"/>
+ <many-to-many column="PERSON_ID" class="events.Person"/>
+ </set>
+
+ </class>
+
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/documentation/tutorial/src/main/java/events/Event.java
===================================================================
--- trunk/Hibernate3/documentation/tutorial/src/main/java/events/Event.java (rev 0)
+++ trunk/Hibernate3/documentation/tutorial/src/main/java/events/Event.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,48 @@
+package events;
+
+import java.util.*;
+
+public class Event {
+ private Long id;
+
+ private String title;
+ private Date date;
+
+ public Event() {}
+
+ public Long getId() {
+ return id;
+ }
+
+ private void setId(Long id) {
+ this.id = id;
+ }
+
+ public Date getDate() {
+ return date;
+ }
+
+ public void setDate(Date date) {
+ this.date = date;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+
+ private Set participants = new HashSet();
+
+ public Set getParticipants() {
+ return participants;
+ }
+
+ public void setParticipants(Set participants) {
+ this.participants = participants;
+ }
+
+}
\ No newline at end of file
Added: trunk/Hibernate3/documentation/tutorial/src/main/java/events/EventManager.java
===================================================================
--- trunk/Hibernate3/documentation/tutorial/src/main/java/events/EventManager.java (rev 0)
+++ trunk/Hibernate3/documentation/tutorial/src/main/java/events/EventManager.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,136 @@
+package events;
+import org.hibernate.*;
+import org.hibernate.criterion.Expression;
+
+import java.util.*;
+
+import util.HibernateUtil;
+
+public class EventManager {
+
+ public static void main(String[] args) {
+ EventManager mgr = new EventManager();
+
+ if (args[0].equals("store")) {
+ mgr.createAndStoreEvent("My Event", new Date());
+ }
+ else if (args[0].equals("list")) {
+ List events = mgr.listEvents();
+ for (int i = 0; i < events.size(); i++) {
+ Event theEvent = (Event) events.get(i);
+ System.out.println("Event: " + theEvent.getTitle() +
+ " Time: " + theEvent.getDate());
+ }
+ }
+ else if (args[0].equals("addpersontoevent")) {
+ Long eventId = mgr.createAndStoreEvent("My Event", new Date());
+ Long personId = mgr.createAndStorePerson("Foo", "Bar");
+ mgr.addPersonToEvent(personId, eventId);
+ System.out.println("Added person " + personId + " to event " + eventId);
+ }
+ else if (args[0].equals("addemailtoperson")) {
+ Long personId = mgr.createAndStorePerson("Foozy", "Beary");
+ mgr.addEmailToPerson(personId, "foo at bar");
+ mgr.addEmailToPerson(personId, "bar at foo");
+ System.out.println("Added two email addresses (value typed objects) to person entity : " + personId);
+ }
+
+ HibernateUtil.getSessionFactory().close();
+ }
+
+ private Long createAndStoreEvent(String title, Date theDate) {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+ session.beginTransaction();
+
+ Event theEvent = new Event();
+ theEvent.setTitle(title);
+ theEvent.setDate(theDate);
+
+ session.save(theEvent);
+
+ session.getTransaction().commit();
+
+ return theEvent.getId();
+ }
+
+ private Long createAndStorePerson(String firstname, String lastname) {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+ session.beginTransaction();
+
+ Person thePerson = new Person();
+ thePerson.setFirstname(firstname);
+ thePerson.setLastname(lastname);
+
+ session.save(thePerson);
+
+ session.getTransaction().commit();
+
+ return thePerson.getId();
+ }
+
+
+ private List listEvents() {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+ session.beginTransaction();
+
+ List result = session.createQuery("from Event").list();
+
+ session.getTransaction().commit();
+
+ return result;
+ }
+
+ private void addPersonToEvent(Long personId, Long eventId) {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+ session.beginTransaction();
+
+ Person aPerson = (Person) session
+ .createQuery("select p from Person p left join fetch p.events where p.id = :pid")
+ .setParameter("pid", personId)
+ .uniqueResult(); // Eager fetch the collection so we can use it detached
+
+ Event anEvent = (Event) session.load(Event.class, eventId);
+ // If we want to handle it bidirectional and detached, we also need to load this
+ // collection with an eager outer-join fetch, this time with Criteria and not HQL:
+ /*
+ Event anEvent = (Event) session
+ .createCriteria(Event.class).setFetchMode("participants", FetchMode.JOIN)
+ .add( Expression.eq("id", eventId) )
+ .uniqueResult(); // Eager fetch the colleciton so we can use it detached
+ */
+
+ session.getTransaction().commit();
+
+ // End of first unit of work
+
+ aPerson.getEvents().add(anEvent); // aPerson is detached
+ // or bidirectional safety method, setting both sides: aPerson.addToEvent(anEvent);
+
+ // Begin second unit of work
+
+ Session session2 = HibernateUtil.getSessionFactory().getCurrentSession();
+ session2.beginTransaction();
+
+ session2.update(aPerson); // Reattachment of aPerson
+
+ session2.getTransaction().commit();
+ }
+
+ private void addEmailToPerson(Long personId, String emailAddress) {
+
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+ session.beginTransaction();
+
+ Person aPerson = (Person) session.load(Person.class, personId);
+
+ // The getEmailAddresses() might trigger a lazy load of the collection
+ aPerson.getEmailAddresses().add(emailAddress);
+
+ session.getTransaction().commit();
+ }
+
+}
\ No newline at end of file
Added: trunk/Hibernate3/documentation/tutorial/src/main/java/events/EventManagerServlet.java
===================================================================
--- trunk/Hibernate3/documentation/tutorial/src/main/java/events/EventManagerServlet.java (rev 0)
+++ trunk/Hibernate3/documentation/tutorial/src/main/java/events/EventManagerServlet.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,101 @@
+package events;
+
+import util.HibernateUtil;
+
+import javax.servlet.http.*;
+import javax.servlet.ServletException;
+import java.io.*;
+import java.util.*;
+import java.text.SimpleDateFormat;
+
+public class EventManagerServlet extends HttpServlet {
+
+ protected void doGet(HttpServletRequest request,
+ HttpServletResponse response)
+ throws ServletException, IOException {
+
+ SimpleDateFormat dateFormatter = new SimpleDateFormat("dd.MM.yyyy");
+
+ try {
+ // Begin unit of work
+ HibernateUtil.getSessionFactory()
+ .getCurrentSession().beginTransaction();
+
+ // Write HTML header
+ PrintWriter out = response.getWriter();
+ out.println("<html><head><title>Event Manager</title></head><body>");
+
+ // Handle actions
+ if ( "store".equals(request.getParameter("action")) ) {
+
+ String eventTitle = request.getParameter("eventTitle");
+ String eventDate = request.getParameter("eventDate");
+
+ if ( "".equals(eventTitle) || "".equals(eventDate) ) {
+ out.println("<b><i>Please enter event title and date.</i></b>");
+ } else {
+ createAndStoreEvent(eventTitle, dateFormatter.parse(eventDate));
+ out.println("<b><i>Added event.</i></b>");
+ }
+ }
+
+ // Print page
+ printEventForm(out);
+ listEvents(out, dateFormatter);
+
+ // Write HTML footer
+ out.println("</body></html>");
+ out.flush();
+ out.close();
+
+ // End unit of work
+ HibernateUtil.getSessionFactory()
+ .getCurrentSession().getTransaction().commit();
+
+ } catch (Exception ex) {
+ HibernateUtil.getSessionFactory()
+ .getCurrentSession().getTransaction().rollback();
+ throw new ServletException(ex);
+ }
+ }
+
+ private void printEventForm(PrintWriter out) {
+ out.println("<h2>Add new event:</h2>");
+ out.println("<form>");
+ out.println("Title: <input name='eventTitle' length='50'/><br/>");
+ out.println("Date (e.g. 24.12.2009): <input name='eventDate' length='10'/><br/>");
+ out.println("<input type='submit' name='action' value='store'/>");
+ out.println("</form>");
+ }
+
+ private void listEvents(PrintWriter out, SimpleDateFormat dateFormatter) {
+ List result = HibernateUtil.getSessionFactory()
+ .getCurrentSession().createCriteria(Event.class).list();
+ if (result.size() > 0) {
+ out.println("<h2>Events in database:</h2>");
+ out.println("<table border='1'>");
+ out.println("<tr>");
+ out.println("<th>Event title</th>");
+ out.println("<th>Event date</th>");
+ out.println("</tr>");
+ for (Iterator it = result.iterator(); it.hasNext();) {
+ Event event = (Event) it.next();
+ out.println("<tr>");
+ out.println("<td>" + event.getTitle() + "</td>");
+ out.println("<td>" + dateFormatter.format(event.getDate()) + "</td>");
+ out.println("</tr>");
+ }
+ out.println("</table>");
+ }
+ }
+
+ protected void createAndStoreEvent(String title, Date theDate) {
+ Event theEvent = new Event();
+ theEvent.setTitle(title);
+ theEvent.setDate(theDate);
+
+ HibernateUtil.getSessionFactory()
+ .getCurrentSession().save(theEvent);
+ }
+
+}
Added: trunk/Hibernate3/documentation/tutorial/src/main/java/events/Person.hbm.xml
===================================================================
--- trunk/Hibernate3/documentation/tutorial/src/main/java/events/Person.hbm.xml (rev 0)
+++ trunk/Hibernate3/documentation/tutorial/src/main/java/events/Person.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -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">
+
+<hibernate-mapping>
+
+ <class name="events.Person" table="PERSON">
+ <id name="id" column="PERSON_ID">
+ <generator class="native"/>
+ </id>
+ <property name="age"/>
+ <property name="firstname"/>
+ <property name="lastname"/>
+
+ <set name="events" table="PERSON_EVENT">
+ <key column="PERSON_ID"/>
+ <many-to-many column="EVENT_ID" class="events.Event"/>
+ </set>
+
+ <set name="emailAddresses" table="PERSON_EMAIL_ADDR">
+ <key column="PERSON_ID"/>
+ <element type="string" column="EMAIL_ADDR"/>
+ </set>
+
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/documentation/tutorial/src/main/java/events/Person.java
===================================================================
--- trunk/Hibernate3/documentation/tutorial/src/main/java/events/Person.java (rev 0)
+++ trunk/Hibernate3/documentation/tutorial/src/main/java/events/Person.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,80 @@
+package events;
+
+import java.util.*;
+
+public class Person {
+
+ private Long id;
+ private int age;
+ private String firstname;
+ private String lastname;
+
+ public Person() {}
+
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public void setAge(int age) {
+ this.age = age;
+ }
+
+ public String getFirstname() {
+ return firstname;
+ }
+
+ public void setFirstname(String firstname) {
+ this.firstname = firstname;
+ }
+
+ public String getLastname() {
+ return lastname;
+ }
+
+ public void setLastname(String lastname) {
+ this.lastname = lastname;
+ }
+
+
+ private Set emailAddresses = new HashSet();
+
+ public Set getEmailAddresses() {
+ return emailAddresses;
+ }
+
+ public void setEmailAddresses(Set emailAddresses) {
+ this.emailAddresses = emailAddresses;
+ }
+
+
+ private Set events = new HashSet();
+
+ // Defensive, convenience methods
+ protected Set getEvents() {
+ return events;
+ }
+
+ protected void setEvents(Set events) {
+ this.events = events;
+ }
+
+ public void addToEvent(Event event) {
+ this.getEvents().add(event);
+ event.getParticipants().add(this);
+ }
+
+ public void removeFromEvent(Event event) {
+ this.getEvents().remove(event);
+ event.getParticipants().remove(this);
+ }
+
+}
\ No newline at end of file
Added: trunk/Hibernate3/documentation/tutorial/src/main/java/util/HibernateUtil.java
===================================================================
--- trunk/Hibernate3/documentation/tutorial/src/main/java/util/HibernateUtil.java (rev 0)
+++ trunk/Hibernate3/documentation/tutorial/src/main/java/util/HibernateUtil.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,25 @@
+package util;
+
+import org.hibernate.*;
+import org.hibernate.cfg.*;
+
+public class HibernateUtil {
+
+ private static final SessionFactory sessionFactory;
+
+ static {
+ try {
+ // Create the SessionFactory from hibernate.cfg.xml
+ sessionFactory = new Configuration().configure().buildSessionFactory();
+ } catch (Throwable ex) {
+ // Make sure you log the exception, as it might be swallowed
+ System.err.println("Initial SessionFactory creation failed." + ex);
+ throw new ExceptionInInitializerError(ex);
+ }
+ }
+
+ public static SessionFactory getSessionFactory() {
+ return sessionFactory;
+ }
+
+}
\ No newline at end of file
Added: trunk/Hibernate3/documentation/tutorial/src/main/resources/hibernate.cfg.xml
===================================================================
--- trunk/Hibernate3/documentation/tutorial/src/main/resources/hibernate.cfg.xml (rev 0)
+++ trunk/Hibernate3/documentation/tutorial/src/main/resources/hibernate.cfg.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,40 @@
+<?xml version='1.0' encoding='utf-8'?>
+<!DOCTYPE hibernate-configuration PUBLIC
+ "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
+
+<hibernate-configuration>
+
+ <session-factory>
+
+ <!-- Database connection settings -->
+ <property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
+ <property name="connection.url">jdbc:hsqldb:hsql://localhost</property>
+ <property name="connection.username">sa</property>
+ <property name="connection.password"></property>
+
+ <!-- JDBC connection pool (use the built-in) -->
+ <property name="connection.pool_size">1</property>
+
+ <!-- SQL dialect -->
+ <property name="dialect">org.hibernate.dialect.HSQLDialect</property>
+
+ <!-- Enable Hibernate's automatic session context management -->
+ <property name="current_session_context_class">thread</property>
+
+ <!-- Disable the second-level cache -->
+ <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
+
+ <!-- Echo all executed SQL to stdout -->
+ <property name="show_sql">true</property>
+
+ <!-- Drop and re-create the database schema on startup -->
+ <property name="hbm2ddl.auto">create</property>
+
+ <mapping resource="events/Event.hbm.xml"/>
+ <mapping resource="events/Person.hbm.xml"/>
+
+ </session-factory>
+
+</hibernate-configuration>
+
Added: trunk/Hibernate3/documentation/tutorial/src/main/resources/log4j.properties
===================================================================
--- trunk/Hibernate3/documentation/tutorial/src/main/resources/log4j.properties (rev 0)
+++ trunk/Hibernate3/documentation/tutorial/src/main/resources/log4j.properties 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,47 @@
+### direct log messages to stdout ###
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
+
+### direct messages to file hibernate.log ###
+#log4j.appender.file=org.apache.log4j.FileAppender
+#log4j.appender.file.File=hibernate.log
+#log4j.appender.file.layout=org.apache.log4j.PatternLayout
+#log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
+
+### set log levels - for more verbose logging change 'info' to 'debug' ###
+
+log4j.rootLogger=warn, stdout
+
+log4j.logger.org.hibernate=info
+#log4j.logger.org.hibernate=debug
+
+### log HQL query parser activity
+#log4j.logger.org.hibernate.hql.ast.AST=debug
+
+### log just the SQL
+#log4j.logger.org.hibernate.SQL=debug
+
+### log JDBC bind parameters ###
+log4j.logger.org.hibernate.type=info
+#log4j.logger.org.hibernate.type=debug
+
+### log schema export/update ###
+log4j.logger.org.hibernate.tool.hbm2ddl=debug
+
+### log HQL parse trees
+#log4j.logger.org.hibernate.hql=debug
+
+### log cache activity ###
+#log4j.logger.org.hibernate.cache=debug
+
+### log transaction activity
+#log4j.logger.org.hibernate.transaction=debug
+
+### log JDBC resource acquisition
+#log4j.logger.org.hibernate.jdbc=debug
+
+### enable the following line if you want to track down connection ###
+### leakages when using DriverManagerConnectionProvider ###
+#log4j.logger.org.hibernate.connection.DriverManagerConnectionProvider=trace
Added: trunk/Hibernate3/documentation/tutorial/src/main/scripts/runCleanDatabase.sh
===================================================================
--- trunk/Hibernate3/documentation/tutorial/src/main/scripts/runCleanDatabase.sh (rev 0)
+++ trunk/Hibernate3/documentation/tutorial/src/main/scripts/runCleanDatabase.sh 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,13 @@
+# Delete database and recreate/start it
+if test ! -d data
+then
+ mkdir data
+fi
+if test -d data
+then
+ echo Removing database files...
+ rm -r data/test.*
+fi
+echo Starting database engine...
+cd data/
+java -classpath ../lib/hsqldb.jar org.hsqldb.Server
Added: trunk/Hibernate3/documentation/tutorial/src/main/webapp/WEB-INF/web.xml
===================================================================
--- trunk/Hibernate3/documentation/tutorial/src/main/webapp/WEB-INF/web.xml (rev 0)
+++ trunk/Hibernate3/documentation/tutorial/src/main/webapp/WEB-INF/web.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app version="2.4"
+ xmlns="http://java.sun.com/xml/ns/j2ee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
+
+ <servlet>
+ <servlet-name>Event Manager</servlet-name>
+ <servlet-class>events.EventManagerServlet</servlet-class>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>Event Manager</servlet-name>
+ <url-pattern>/eventmanager</url-pattern>
+ </servlet-mapping>
+</web-app>
Added: trunk/Hibernate3/eg/pom.xml
===================================================================
--- trunk/Hibernate3/eg/pom.xml (rev 0)
+++ trunk/Hibernate3/eg/pom.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,27 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-core-project</artifactId>
+ <version>3.3.0.beta1</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-eg</artifactId>
+ <packaging>jar</packaging>
+ <name>Hibernate Example</name>
+ <description>A simple example of Hibernate functionality</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-core</artifactId>
+ </dependency>
+ </dependencies>
+
+</project>
Added: trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/AuctionInfo.java
===================================================================
--- trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/AuctionInfo.java (rev 0)
+++ trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/AuctionInfo.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,37 @@
+//$Id: AuctionInfo.java 3890 2004-06-03 16:31:32Z steveebersole $
+package org.hibernate.auction;
+
+import java.util.Date;
+
+/**
+ * @author Gavin King
+ */
+public class AuctionInfo {
+ private long id;
+ private String description;
+ private Date ends;
+ private Float maxAmount;
+ public String getDescription() {
+ return description;
+ }
+
+ public Date getEnds() {
+ return ends;
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public Float getMaxAmount() {
+ return maxAmount;
+ }
+
+ public AuctionInfo(long id, String description, Date ends, Float maxAmount) {
+ this.id = id;
+ this.description = description;
+ this.ends = ends;
+ this.maxAmount = maxAmount;
+ }
+
+}
Added: trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/AuctionItem.hbm.xml
===================================================================
--- trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/AuctionItem.hbm.xml (rev 0)
+++ trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/AuctionItem.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,42 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping
+ package="org.hibernate.auction">
+
+ <import class="AuctionInfo"/>
+
+ <class name="AuctionItem">
+ <comment>An item that is being auctioned.</comment>
+
+ <id name="id">
+ <generator class="native"/>
+ </id>
+
+ <natural-id>
+ <many-to-one name="seller"/>
+ <property name="shortDescription"
+ length="200"/>
+ </natural-id>
+
+ <property name="description"
+ length="1000"/>
+
+ <property name="ends"/>
+
+ <property name="condition" column="`CONDITION`"/>
+
+ <many-to-one name="successfulBid"
+ outer-join="false"/>
+
+ <bag name="bids"
+ inverse="true"
+ cascade="all">
+ <key column="item"/>
+ <one-to-many class="Bid"/>
+ </bag>
+
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/AuctionItem.java
===================================================================
--- trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/AuctionItem.java (rev 0)
+++ trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/AuctionItem.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,78 @@
+//$Id: AuctionItem.java 7369 2005-07-04 03:18:34Z oneovthafew $
+package org.hibernate.auction;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author Gavin King
+ */
+public class AuctionItem extends Persistent {
+ private String description;
+ private String shortDescription;
+ private List bids;
+ private Bid successfulBid;
+ private User seller;
+ private Date ends;
+ private int condition;
+ public List getBids() {
+ return bids;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public User getSeller() {
+ return seller;
+ }
+
+ public Bid getSuccessfulBid() {
+ return successfulBid;
+ }
+
+ public void setBids(List bids) {
+ this.bids = bids;
+ }
+
+ public void setDescription(String string) {
+ description = string;
+ }
+
+ public void setSeller(User user) {
+ seller = user;
+ }
+
+ public void setSuccessfulBid(Bid bid) {
+ successfulBid = bid;
+ }
+
+ public Date getEnds() {
+ return ends;
+ }
+
+ public void setEnds(Date date) {
+ ends = date;
+ }
+
+ public int getCondition() {
+ return condition;
+ }
+
+ public void setCondition(int i) {
+ condition = i;
+ }
+
+ public String toString() {
+ return shortDescription + " (" + description + ": " + condition + "/10)";
+ }
+
+ public String getShortDescription() {
+ return shortDescription;
+ }
+
+ public void setShortDescription(String shortDescription) {
+ this.shortDescription = shortDescription;
+ }
+
+}
Added: trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/Bid.hbm.xml
===================================================================
--- trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/Bid.hbm.xml (rev 0)
+++ trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/Bid.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,39 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping
+ package="org.hibernate.auction">
+
+ <class name="Bid"
+ discriminator-value="N">
+ <comment>A bid or "buy now" for an item.</comment>
+
+ <id name="id">
+ <generator class="native"/>
+ </id>
+
+ <discriminator type="char">
+ <column name="isBuyNow">
+ <comment>Y if a "buy now", N if a regular bid.</comment>
+ </column>
+ </discriminator>
+
+ <natural-id>
+ <many-to-one name="item"/>
+ <property name="amount"/>
+ </natural-id>
+
+ <property name="datetime"
+ not-null="true"
+ column="`datetime`"/>
+
+ <many-to-one name="bidder"
+ not-null="true"/>
+
+ <subclass name="BuyNow"
+ discriminator-value="Y"/>
+
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/Bid.java
===================================================================
--- trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/Bid.java (rev 0)
+++ trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/Bid.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,55 @@
+//$Id: Bid.java 3890 2004-06-03 16:31:32Z steveebersole $
+package org.hibernate.auction;
+
+import java.util.Date;
+
+/**
+ * @author Gavin King
+ */
+public class Bid extends Persistent {
+ private AuctionItem item;
+ private float amount;
+ private Date datetime;
+ private User bidder;
+
+ public AuctionItem getItem() {
+ return item;
+ }
+
+ public void setItem(AuctionItem item) {
+ this.item = item;
+ }
+
+ public float getAmount() {
+ return amount;
+ }
+
+ public Date getDatetime() {
+ return datetime;
+ }
+
+ public void setAmount(float f) {
+ amount = f;
+ }
+
+ public void setDatetime(Date date) {
+ datetime = date;
+ }
+
+ public User getBidder() {
+ return bidder;
+ }
+
+ public void setBidder(User user) {
+ bidder = user;
+ }
+
+ public String toString() {
+ return bidder.getUserName() + " $" + amount;
+ }
+
+ public boolean isBuyNow() {
+ return false;
+ }
+
+}
Added: trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/BuyNow.java
===================================================================
--- trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/BuyNow.java (rev 0)
+++ trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/BuyNow.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,14 @@
+//$Id: BuyNow.java 3890 2004-06-03 16:31:32Z steveebersole $
+package org.hibernate.auction;
+
+/**
+ * @author Gavin King
+ */
+public class BuyNow extends Bid {
+ public boolean isBuyNow() {
+ return true;
+ }
+ public String toString() {
+ return super.toString() + " (buy now)";
+ }
+}
Added: trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/Main.java
===================================================================
--- trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/Main.java (rev 0)
+++ trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/Main.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,390 @@
+//$Id: Main.java 7369 2005-07-04 03:18:34Z oneovthafew $
+package org.hibernate.auction;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+import org.hibernate.FetchMode;
+import org.hibernate.FlushMode;
+import org.hibernate.LockMode;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.criterion.Example;
+import org.hibernate.criterion.Expression;
+import org.hibernate.criterion.MatchMode;
+
+
+/**
+ * Demonstrate some useful features of Hibernate.
+ *
+ * @author Gavin King
+ */
+public class Main {
+
+ private SessionFactory factory;
+
+ /**
+ * Demonstrates HQL projection/aggregation
+ */
+ public void viewAllAuctionsFast() throws Exception {
+ System.out.println("Viewing all auction item info");
+
+ Session s = factory.openSession();
+ Transaction tx=null;
+ try {
+ tx = s.beginTransaction();
+
+ List auctions = s.createQuery(
+ "select new AuctionInfo( item.id, item.description, item.ends, max(bid.amount) ) "
+ + "from AuctionItem item "
+ + "left join item.bids bid "
+ + "group by item.id, item.description, item.ends "
+ + "order by item.ends desc"
+ )
+ .setMaxResults(100)
+ .list();
+
+ Iterator iter = auctions.iterator();
+ while ( iter.hasNext() ) {
+ AuctionInfo ai = (AuctionInfo) iter.next();
+ System.out.println(
+ "Auction: " + ai.getId() + " - " + ai.getDescription() +
+ ", ends: " + ai.getEnds() +
+ ", highest bid: " + ai.getMaxAmount()
+ );
+ }
+ System.out.println();
+
+ tx.commit();
+ }
+ catch (Exception e) {
+ if (tx!=null) tx.rollback();
+ throw e;
+ }
+ finally {
+ s.close();
+ }
+ }
+
+ /**
+ * Demonstrates HQL with runtime fetch strategy
+ */
+ public void viewAllAuctionsSlow() throws Exception {
+ System.out.println("Viewing all auction item objects");
+
+ Session s = factory.openSession();
+ Transaction tx=null;
+ try {
+ s.setFlushMode(FlushMode.NEVER); //entirely optional!!
+ tx = s.beginTransaction();
+
+ List auctions = s.createQuery(
+ "from AuctionItem item "
+ + "left join fetch item.bids bid left join fetch bid.bidder "
+ + "order by item.ends desc"
+ )
+ .setMaxResults(100)
+ .list();
+
+ Iterator iter = new HashSet(auctions).iterator();
+ while ( iter.hasNext() ) {
+ AuctionItem auction = (AuctionItem) iter.next();
+ System.out.println(
+ "Auction: " + auction.getId() + " - " + auction.getDescription() +
+ ", ends: " + auction.getEnds() +
+ ", bids: " + auction.getBids()
+ );
+ }
+ System.out.println();
+
+ tx.commit();
+ }
+ catch (Exception e) {
+ if (tx!=null) tx.rollback();
+ throw e;
+ }
+ finally {
+ s.close();
+ }
+ }
+
+ /**
+ * Demonstrates transitive persistence with detached object support
+ */
+ public void bidOnAuction(User bidder, AuctionItem item, float amount) throws Exception {
+ System.out.println("Creating a new bid for auction item: " + item.getId() + " by user: " + bidder.getId() );
+
+ Session s = factory.openSession();
+ Transaction tx=null;
+ try {
+ tx = s.beginTransaction();
+
+ s.lock(item, LockMode.NONE);
+ s.lock(bidder, LockMode.NONE);
+
+ Bid bid = new Bid();
+ bid.setBidder(bidder);
+ bid.setDatetime( new Date() );
+ bid.setAmount(amount);
+ bid.setItem(item);
+ bidder.getBids().add(bid);
+ item.getBids().add(bid);
+
+ tx.commit();
+ }
+ catch (Exception e) {
+ if (tx!=null) tx.rollback();
+ throw e;
+ }
+ finally {
+ s.close();
+ }
+ }
+
+ /**
+ * Demonstrates detached object support
+ */
+ public void changeUserDetails(User user) throws Exception {
+ System.out.println("Changing user details for: " + user.getId() );
+
+ Session s = factory.openSession();
+ Transaction tx=null;
+ try {
+ tx = s.beginTransaction();
+
+ s.merge(user);
+
+ tx.commit();
+ }
+ catch (Exception e) {
+ if (tx!=null) tx.rollback();
+ throw e;
+ }
+ finally {
+ s.close();
+ }
+ }
+
+ /**
+ * Demonstrates automatic dirty checking
+ */
+ public void changeItemDescription(Long itemId, String description) throws Exception {
+ System.out.println("Changing auction item description for: " + itemId );
+
+ Session s = factory.openSession();
+ Transaction tx=null;
+ try {
+ tx = s.beginTransaction();
+
+ AuctionItem item = (AuctionItem) s.get(AuctionItem.class, itemId);
+ if (item==null) throw new IllegalArgumentException("No item for the given id: " + itemId);
+ item.setDescription(description);
+
+ tx.commit();
+ }
+ catch (Exception e) {
+ if (tx!=null) tx.rollback();
+ throw e;
+ }
+ finally {
+ s.close();
+ }
+ }
+
+ /**
+ * Demonstrates query by criteria with runtime fetch strategy
+ */
+ public void viewUserAuctions(Long sellerId) throws Exception {
+ System.out.println("Viewing user and auctions: " + sellerId);
+
+ Session s = factory.openSession();
+ Transaction tx=null;
+ try {
+ tx = s.beginTransaction();
+
+ List list = s.createCriteria(User.class)
+ .add( Expression.eq("id", sellerId) )
+ .setFetchMode("auctions", FetchMode.JOIN)
+ .list();
+
+ if (list.size()==0) throw new IllegalArgumentException("No user for the given id: " + sellerId);
+ User user = (User) list.get(0);
+ System.out.println(
+ "User: " + user.getId() + " - " + user.getName() +
+ ", email: " + user.getEmail() +
+ ", auctions: " + user.getAuctions()
+ );
+
+ tx.commit();
+ }
+ catch (Exception e) {
+ if (tx!=null) tx.rollback();
+ throw e;
+ }
+ finally {
+ s.close();
+ }
+ }
+
+ /**
+ * Demonstrates query by example
+ */
+ public void viewAuctionsByDescription(String description, int condition) throws Exception {
+ String msg = "Viewing auctions containing: " + description;
+ if (condition>0) msg += " with condition: " + condition + "/10";
+
+ AuctionItem item = new AuctionItem();
+ item.setDescription(description);
+ item.setCondition(condition);
+
+ Session s = factory.openSession();
+ Transaction tx=null;
+ try {
+ tx = s.beginTransaction();
+
+ Iterator iter = s.createCriteria(AuctionItem.class)
+ .add( Example.create(item)
+ .enableLike(MatchMode.ANYWHERE)
+ .ignoreCase()
+ .excludeZeroes()
+ )
+ .list()
+ .iterator();
+
+ System.out.println(msg);
+ while ( iter.hasNext() ) {
+ item = (AuctionItem) iter.next();
+ System.out.println("Item: " + item.getId() + " - " + item.getDescription() );
+ }
+ System.out.println();
+
+ tx.commit();
+ }
+ catch (Exception e) {
+ if (tx!=null) tx.rollback();
+ throw e;
+ }
+ finally {
+ s.close();
+ }
+ }
+
+ /**
+ * Demonstrates transitive persistence
+ */
+ public void createTestAuctions() throws Exception {
+ System.out.println("Setting up some test data");
+
+ Session s = factory.openSession();
+ Transaction tx = s.beginTransaction();
+
+ User seller = new User();
+ seller.setUserName("xam");
+ seller.setName( new Name("Max", new Character('R'), "Andersen") );
+ seller.setEmail("max at hibernate.org");
+ seller.setPassword("******");
+ seller.setAuctions( new ArrayList() );
+ s.save(seller);
+ User bidder1 = new User();
+ bidder1.setUserName("1E1");
+ bidder1.setName( new Name( "Gavin", new Character('A'), "King") );
+ bidder1.setEmail("gavin at hibernate.org");
+ bidder1.setPassword("******");
+ bidder1.setBids( new ArrayList() );
+ s.save(bidder1);
+ User bidder2 = new User();
+ bidder2.setUserName("steve");
+ bidder2.setName( new Name("Steve", null, "Ebersole") );
+ bidder2.setEmail("steve at hibernate.org");
+ bidder2.setPassword("******");
+ bidder2.setBids( new ArrayList() );
+ s.save(bidder2);
+
+ for ( int i=0; i<3; i++ ) {
+ AuctionItem item = new AuctionItem();
+ item.setShortDescription("Auction " + i);
+ item.setDescription("the auction item number " + i);
+ item.setEnds( new Date() );
+ item.setBids( new ArrayList() );
+ item.setSeller(seller);
+ item.setCondition(i*3 + 2);
+ for ( int j=0; j<i; j++ ) {
+
+ Bid bid = new Bid();
+ bid.setBidder(bidder1);
+ bid.setAmount(j);
+ bid.setDatetime( new Date() );
+ bid.setItem(item);
+ item.getBids().add(bid);
+ bidder1.getBids().add(bid);
+
+ Bid bid2 = new Bid();
+ bid2.setBidder(bidder2);
+ bid2.setAmount( j + 0.5f);
+ bid2.setDatetime( new Date() );
+ bid2.setItem(item);
+ item.getBids().add(bid2);
+ bidder2.getBids().add(bid2);
+ }
+ seller.getAuctions().add(item);
+ mainItem = item;
+ }
+ mainBidder = bidder2;
+ mainSeller = seller;
+
+ BuyNow buyNow = new BuyNow();
+ buyNow.setAmount(1.2f);
+ buyNow.setDatetime( new Date() );
+ buyNow.setBidder(mainBidder);
+ buyNow.setItem(mainItem);
+ mainBidder.getBids().add(buyNow);
+ mainItem.getBids().add(buyNow);
+
+ tx.commit();
+ s.close();
+ }
+
+ static AuctionItem mainItem;
+ static User mainBidder;
+ static User mainSeller;
+
+ public static void main(String[] args) throws Exception {
+
+ final Main test = new Main();
+
+ Configuration cfg = new Configuration()
+ .addClass(AuctionItem.class)
+ .addClass(Bid.class)
+ .addClass(User.class)
+ .setProperty(Environment.HBM2DDL_AUTO, "create");
+ //cfg.setProperty("hibernate.show_sql", "true");
+
+ test.factory = cfg.buildSessionFactory();
+
+ test.createTestAuctions();
+ test.viewAllAuctionsSlow();
+
+ test.viewAllAuctionsFast();
+ test.bidOnAuction(mainBidder, mainItem, 5.5f);
+ test.viewAllAuctionsFast();
+
+ test.viewUserAuctions( mainSeller.getId() );
+ mainSeller.setEmail("max at jboss.org");
+ test.changeUserDetails(mainSeller);
+ test.changeItemDescription(mainItem.getId(), "new description");
+ test.viewUserAuctions( mainSeller.getId() );
+
+ test.viewAuctionsByDescription("It", 0);
+ test.viewAuctionsByDescription("DESC", 3);
+ test.viewAuctionsByDescription("DESC", 8);
+
+ test.factory.close();
+
+ }
+}
Added: trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/Name.java
===================================================================
--- trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/Name.java (rev 0)
+++ trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/Name.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,51 @@
+//$Id: Name.java 3890 2004-06-03 16:31:32Z steveebersole $
+package org.hibernate.auction;
+
+/**
+ * @author Gavin King
+ */
+public class Name {
+ private String firstName;
+ private String lastName;
+ private Character initial;
+ private Name() {}
+ public Name(String first, Character middle, String last) {
+ firstName = first;
+ initial = middle;
+ lastName = last;
+ }
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+ public Character getInitial() {
+ return initial;
+ }
+
+ public void setInitial(Character initial) {
+ this.initial = initial;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
+ public String toString() {
+ StringBuffer buf = new StringBuffer()
+ .append(firstName)
+ .append(' ');
+ if (initial!=null) buf.append(initial)
+ .append(' ');
+ return buf.append(lastName)
+ .toString();
+ }
+
+}
Added: trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/Persistent.java
===================================================================
--- trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/Persistent.java (rev 0)
+++ trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/Persistent.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,17 @@
+//$Id: Persistent.java 3890 2004-06-03 16:31:32Z steveebersole $
+package org.hibernate.auction;
+
+/**
+ * @author Gavin King
+ */
+public class Persistent {
+ private Long id;
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long long1) {
+ id = long1;
+ }
+
+}
Added: trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/User.hbm.xml
===================================================================
--- trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/User.hbm.xml (rev 0)
+++ trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/User.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,54 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping
+ package="org.hibernate.auction">
+
+ <class name="User" table="AuctionUser" lazy="true">
+ <comment>Users may bid for or sell auction items.</comment>
+
+ <id name="id">
+ <generator class="native"/>
+ </id>
+
+ <natural-id mutable="true">
+ <property name="userName"
+ length="10"/>
+ </natural-id>
+
+ <property name="password"
+ not-null="true"
+ length="15"
+ column="`password`"/>
+
+ <property name="email"/>
+
+ <component name="name">
+ <property name="firstName"
+ length="50"
+ not-null="true"/>
+ <property name="initial"
+ column="`initial`"/>
+ <property name="lastName"
+ length="50"
+ not-null="true"/>
+ </component>
+
+ <bag name="bids"
+ inverse="true"
+ cascade="save-update,lock">
+ <key column="bidder"/>
+ <one-to-many class="Bid"/>
+ </bag>
+
+ <bag name="auctions"
+ inverse="true"
+ cascade="save-update,lock">
+ <key column="seller"/>
+ <one-to-many class="AuctionItem"/>
+ </bag>
+
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/User.java
===================================================================
--- trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/User.java (rev 0)
+++ trunk/Hibernate3/eg/src/main/java/org/hibernate/auction/User.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,69 @@
+//$Id: User.java 3890 2004-06-03 16:31:32Z steveebersole $
+package org.hibernate.auction;
+
+import java.util.List;
+
+/**
+ * @author Gavin King
+ */
+public class User extends Persistent {
+ private String userName;
+ private String password;
+ private String email;
+ private Name name;
+ private List bids;
+ private List auctions;
+
+ public String getEmail() {
+ return email;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public String getUserName() {
+ return userName;
+ }
+
+ public void setEmail(String string) {
+ email = string;
+ }
+
+ public void setPassword(String string) {
+ password = string;
+ }
+
+ public void setUserName(String string) {
+ userName = string;
+ }
+
+ public List getAuctions() {
+ return auctions;
+ }
+
+ public List getBids() {
+ return bids;
+ }
+
+ public void setAuctions(List list) {
+ auctions = list;
+ }
+
+ public void setBids(List list) {
+ bids = list;
+ }
+
+ public String toString() {
+ return userName;
+ }
+
+ public Name getName() {
+ return name;
+ }
+
+ public void setName(Name name) {
+ this.name = name;
+ }
+
+}
Added: trunk/Hibernate3/eg/src/main/resources/org/hibernate/auction/AuctionItem.hbm.xml
===================================================================
--- trunk/Hibernate3/eg/src/main/resources/org/hibernate/auction/AuctionItem.hbm.xml (rev 0)
+++ trunk/Hibernate3/eg/src/main/resources/org/hibernate/auction/AuctionItem.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,42 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping
+ package="org.hibernate.auction">
+
+ <import class="AuctionInfo"/>
+
+ <class name="AuctionItem">
+ <comment>An item that is being auctioned.</comment>
+
+ <id name="id">
+ <generator class="native"/>
+ </id>
+
+ <natural-id>
+ <many-to-one name="seller"/>
+ <property name="shortDescription"
+ length="200"/>
+ </natural-id>
+
+ <property name="description"
+ length="1000"/>
+
+ <property name="ends"/>
+
+ <property name="condition" column="`CONDITION`"/>
+
+ <many-to-one name="successfulBid"
+ outer-join="false"/>
+
+ <bag name="bids"
+ inverse="true"
+ cascade="all">
+ <key column="item"/>
+ <one-to-many class="Bid"/>
+ </bag>
+
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/eg/src/main/resources/org/hibernate/auction/Bid.hbm.xml
===================================================================
--- trunk/Hibernate3/eg/src/main/resources/org/hibernate/auction/Bid.hbm.xml (rev 0)
+++ trunk/Hibernate3/eg/src/main/resources/org/hibernate/auction/Bid.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,39 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping
+ package="org.hibernate.auction">
+
+ <class name="Bid"
+ discriminator-value="N">
+ <comment>A bid or "buy now" for an item.</comment>
+
+ <id name="id">
+ <generator class="native"/>
+ </id>
+
+ <discriminator type="char">
+ <column name="isBuyNow">
+ <comment>Y if a "buy now", N if a regular bid.</comment>
+ </column>
+ </discriminator>
+
+ <natural-id>
+ <many-to-one name="item"/>
+ <property name="amount"/>
+ </natural-id>
+
+ <property name="datetime"
+ not-null="true"
+ column="`datetime`"/>
+
+ <many-to-one name="bidder"
+ not-null="true"/>
+
+ <subclass name="BuyNow"
+ discriminator-value="Y"/>
+
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/eg/src/main/resources/org/hibernate/auction/User.hbm.xml
===================================================================
--- trunk/Hibernate3/eg/src/main/resources/org/hibernate/auction/User.hbm.xml (rev 0)
+++ trunk/Hibernate3/eg/src/main/resources/org/hibernate/auction/User.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,54 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping
+ package="org.hibernate.auction">
+
+ <class name="User" table="AuctionUser" lazy="true">
+ <comment>Users may bid for or sell auction items.</comment>
+
+ <id name="id">
+ <generator class="native"/>
+ </id>
+
+ <natural-id mutable="true">
+ <property name="userName"
+ length="10"/>
+ </natural-id>
+
+ <property name="password"
+ not-null="true"
+ length="15"
+ column="`password`"/>
+
+ <property name="email"/>
+
+ <component name="name">
+ <property name="firstName"
+ length="50"
+ not-null="true"/>
+ <property name="initial"
+ column="`initial`"/>
+ <property name="lastName"
+ length="50"
+ not-null="true"/>
+ </component>
+
+ <bag name="bids"
+ inverse="true"
+ cascade="save-update,lock">
+ <key column="bidder"/>
+ <one-to-many class="Bid"/>
+ </bag>
+
+ <bag name="auctions"
+ inverse="true"
+ cascade="save-update,lock">
+ <key column="seller"/>
+ <one-to-many class="AuctionItem"/>
+ </bag>
+
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Deleted: trunk/Hibernate3/indent.py
===================================================================
--- trunk/Hibernate3/indent.py 2007-05-24 19:06:06 UTC (rev 11562)
+++ trunk/Hibernate3/indent.py 2007-05-25 20:19:29 UTC (rev 11563)
@@ -1,45 +0,0 @@
-import re
-import sys
-sys.argv.pop(0)
-for fname in sys.argv:
- file = open(fname)
- inlines = file.read().split('\n')
- file.close()
- out= []
- tabcount = 0
- extratab=0
- for ln in inlines:
- code = ln.lstrip('\t ').rstrip('\t ')
- clen = len(code)
- javadoc = clen > 0 and code[0]=='*'
- if javadoc:
- code = ' ' + code
- else:
- begincb = clen > 0 and ( code[0]=='}' or code[0]==')' )
- tabcount -= begincb
-
- extratab = extratab or ( len(code)>0 and code[0]=='.' )
-
- tabs = '\t' * (tabcount + extratab)
-
- extratab = clen>0 and ( code[clen-1]==':' or code[clen-1]=='?' )
-
- if clen>5 and code[0:6] == '} else':
- code = '}\n' + tabs + 'else' + code[6:]
- if clen>6 and code[0:7] == '} catch':
- code = '}\n' + tabs + 'catch' + code[7:]
-
- out.append( tabs + code + '\n' )
-
- if not javadoc:
- uncommented = code.split('//')[0].rstrip()
- clen = len(uncommented)
- endob = clen > 0 and ( code[clen-1]=='{' or code[clen-1]=='(' )
- tabcount += endob
-
- file = open( fname, 'w' )
- for ln in out:
- file.write(ln)
- file.close()
-
-
Added: trunk/Hibernate3/jmx/pom.xml
===================================================================
--- trunk/Hibernate3/jmx/pom.xml (rev 0)
+++ trunk/Hibernate3/jmx/pom.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,27 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-core-project</artifactId>
+ <version>3.3.0.beta1</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-jmx</artifactId>
+ <packaging>jar</packaging>
+ <name>Hibernate JMX Module</name>
+ <url>http://hibernate.org</url>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-core</artifactId>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
Added: trunk/Hibernate3/jmx/src/main/java/org/hibernate/jmx/HibernateService.java
===================================================================
--- trunk/Hibernate3/jmx/src/main/java/org/hibernate/jmx/HibernateService.java (rev 0)
+++ trunk/Hibernate3/jmx/src/main/java/org/hibernate/jmx/HibernateService.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,168 @@
+//$Id: HibernateService.java 6100 2005-03-17 10:48:03Z turin42 $
+package org.hibernate.jmx;
+
+import java.util.Properties;
+import java.util.Map;
+
+import javax.naming.InitialContext;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.hibernate.HibernateException;
+import org.hibernate.SessionFactory;
+import org.hibernate.cfg.Environment;
+import org.hibernate.tool.hbm2ddl.SchemaExport;
+import org.hibernate.util.NamingHelper;
+import org.hibernate.util.ExternalSessionFactoryConfig;
+
+
+/**
+ * Implementation of <tt>HibernateServiceMBean</tt>. Creates a
+ * <tt>SessionFactory</tt> and binds it to the specified JNDI name.<br>
+ * <br>
+ * All mapping documents are loaded as resources by the MBean.
+ * @see HibernateServiceMBean
+ * @see org.hibernate.SessionFactory
+ * @author John Urberg, Gavin King
+ */
+public class HibernateService extends ExternalSessionFactoryConfig implements HibernateServiceMBean {
+
+ private static final Log log = LogFactory.getLog(HibernateServiceMBean.class);
+
+ private String boundName;
+ private Properties properties = new Properties();
+
+
+ public void start() throws HibernateException {
+ boundName = getJndiName();
+ try {
+ buildSessionFactory();
+ }
+ catch (HibernateException he) {
+ log.info( "Could not build SessionFactory using the MBean classpath - will try again using client classpath: " + he.getMessage() );
+ log.debug("Error was", he);
+ new SessionFactoryStub(this);
+ }
+ }
+
+ public void stop() {
+ log.info("stopping service");
+ try {
+ InitialContext context = NamingHelper.getInitialContext( buildProperties() );
+ ( (SessionFactory) context.lookup(boundName) ).close();
+ //context.unbind(boundName);
+ }
+ catch (Exception e) {
+ log.warn("exception while stopping service", e);
+ }
+ }
+
+ SessionFactory buildSessionFactory() throws HibernateException {
+ log.info( "starting service at JNDI name: " + boundName );
+ log.info( "service properties: " + properties );
+ return buildConfiguration().buildSessionFactory();
+ }
+
+ protected Map getExtraProperties() {
+ return properties;
+ }
+
+ public String getTransactionStrategy() {
+ return getProperty(Environment.TRANSACTION_STRATEGY);
+ }
+
+ public void setTransactionStrategy(String txnStrategy) {
+ setProperty(Environment.TRANSACTION_STRATEGY, txnStrategy);
+ }
+
+ public String getUserTransactionName() {
+ return getProperty(Environment.USER_TRANSACTION);
+ }
+
+ public void setUserTransactionName(String utName) {
+ setProperty(Environment.USER_TRANSACTION, utName);
+ }
+
+ public String getTransactionManagerLookupStrategy() {
+ return getProperty(Environment.TRANSACTION_MANAGER_STRATEGY);
+ }
+
+ public void setTransactionManagerLookupStrategy(String lkpStrategy) {
+ setProperty(Environment.TRANSACTION_MANAGER_STRATEGY, lkpStrategy);
+ }
+
+ public String getPropertyList() {
+ return buildProperties().toString();
+ }
+
+ public String getProperty(String property) {
+ return properties.getProperty(property);
+ }
+
+ public void setProperty(String property, String value) {
+ properties.setProperty(property, value);
+ }
+
+ public void dropSchema() {
+ new SchemaExport( buildConfiguration() ).drop(false, true);
+ }
+
+ public void createSchema() {
+ new SchemaExport( buildConfiguration() ).create(false, true);
+ } public String getName() {
+ return getProperty(Environment.SESSION_FACTORY_NAME);
+ }
+
+ public String getDatasource() {
+ return getProperty(Environment.DATASOURCE);
+ }
+
+ public void setDatasource(String datasource) {
+ setProperty(Environment.DATASOURCE, datasource);
+ }
+
+ public String getJndiName() {
+ return getProperty(Environment.SESSION_FACTORY_NAME);
+ }
+
+ public void setJndiName(String jndiName) {
+ setProperty(Environment.SESSION_FACTORY_NAME, jndiName);
+ }
+
+ public String getUserName() {
+ return getProperty(Environment.USER);
+ }
+
+ public void setUserName(String userName) {
+ setProperty(Environment.USER, userName);
+ }
+
+ public String getPassword() {
+ return getProperty(Environment.PASS);
+ }
+
+ public void setPassword(String password) {
+ setProperty(Environment.PASS, password);
+ }
+
+ public void setFlushBeforeCompletionEnabled(String enabled) {
+ setProperty(Environment.FLUSH_BEFORE_COMPLETION, enabled);
+ }
+
+ public String getFlushBeforeCompletionEnabled() {
+ return getProperty(Environment.FLUSH_BEFORE_COMPLETION);
+ }
+
+ public void setAutoCloseSessionEnabled(String enabled) {
+ setProperty(Environment.AUTO_CLOSE_SESSION, enabled);
+ }
+
+ public String getAutoCloseSessionEnabled() {
+ return getProperty(Environment.AUTO_CLOSE_SESSION);
+ }
+
+ public Properties getProperties() {
+ return buildProperties();
+ }
+}
Added: trunk/Hibernate3/jmx/src/main/java/org/hibernate/jmx/HibernateServiceMBean.java
===================================================================
--- trunk/Hibernate3/jmx/src/main/java/org/hibernate/jmx/HibernateServiceMBean.java (rev 0)
+++ trunk/Hibernate3/jmx/src/main/java/org/hibernate/jmx/HibernateServiceMBean.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,333 @@
+//$Id: HibernateServiceMBean.java 10860 2006-11-22 00:02:55Z steve.ebersole at jboss.com $
+package org.hibernate.jmx;
+
+import org.hibernate.HibernateException;
+
+/**
+ * Hibernate JMX Management API
+ * @see HibernateService
+ * @author John Urberg, Gavin King
+ */
+public interface HibernateServiceMBean {
+
+ /**
+ * The Hibernate mapping files (might be overridden by subclasses
+ * that want to specify the mapping files by some other mechanism)
+ * @return String
+ */
+ public String getMapResources();
+ /**
+ * Specify the Hibernate mapping files
+ * @param mappingFiles
+ */
+ public void setMapResources(String mappingFiles);
+ /**
+ * Add a mapping file
+ * @param mapResource
+ */
+ public void addMapResource(String mapResource);
+
+ /**
+ * Set a property
+ * @param property the property name
+ * @param value the property value
+ */
+ public void setProperty(String property, String value);
+
+ /**
+ * Get a property
+ * @param property the property name
+ * @return the property value
+ */
+ public String getProperty(String property);
+
+ /**
+ * Display the properties
+ * @return a list of property names and values
+ */
+ public String getPropertyList();
+
+ /**
+ * The JNDI name of the datasource to use in this <tt>SessionFactory</tt>
+ * @return String
+ */
+ public String getDatasource();
+ /**
+ * Set the JNDI name of the datasource to use in this <tt>SessionFactory</tt>
+ * @param datasource
+ */
+ public void setDatasource(String datasource);
+
+ /**
+ * Log into the database with this name
+ * @return String
+ */
+ public String getUserName();
+ /**
+ * Log into the database with this name
+ * @param userName
+ */
+ public void setUserName(String userName);
+
+ /**
+ * Log into the database with this password
+ * @return String
+ */
+ public String getPassword();
+ /**
+ * Log into the database with this password
+ * @param password
+ */
+ public void setPassword(String password);
+
+ /**
+ * The JNDI name of the dialect class to use in this <tt>SessionFactory</tt>
+ * @return String
+ */
+ public String getDialect();
+ /**
+ * The name of the dialect class to use in this <tt>SessionFactory</tt>
+ * @param dialect fully qualified class name of <tt>Dialect</tt> subclass
+ * @see org.hibernate.dialect.Dialect
+ */
+ public void setDialect(String dialect);
+
+ /**
+ * The JNDI name to bind to the <tt>SessionFactory</tt>
+ * @return String
+ */
+ public String getJndiName();
+ /**
+ * The JNDI name to bind to the <tt>SessionFactory</tt>
+ * @param jndiName
+ */
+ public void setJndiName(String jndiName);
+
+ /**
+ * The fully qualified class name of the Hibernate <tt>TransactionFactory</tt> implementation
+ * @return the class name
+ * @see org.hibernate.transaction.TransactionFactory
+ */
+ public String getTransactionStrategy();
+
+ /**
+ * Set the fully qualified class name of the Hibernate <tt>TransactionFactory</tt> implementation
+ * @param txnStrategy the class name
+ * @see org.hibernate.transaction.TransactionFactory
+ */
+ public void setTransactionStrategy(String txnStrategy);
+
+ /**
+ * The JNDI name of the JTA UserTransaction object (used only be <tt>JTATransaction</tt>).
+ * @return the JNDI name
+ * @see org.hibernate.transaction.JTATransaction
+ */
+ public String getUserTransactionName();
+ /**
+ * Set the JNDI name of the JTA UserTransaction object (used only by <tt>JTATransaction</tt>).
+ * @param utName the JNDI name
+ * @see org.hibernate.transaction.JTATransaction
+ */
+ public void setUserTransactionName(String utName);
+
+ /**
+ * Get the strategy for obtaining the JTA <tt>TransactionManager</tt>
+ * @return the class name
+ * @see org.hibernate.transaction.TransactionManagerLookup
+ */
+ public String getTransactionManagerLookupStrategy();
+ /**
+ * Set the strategy for obtaining the JTA <tt>TransactionManager</tt>
+ * @param lkpStrategy the class name
+ * @see org.hibernate.transaction.TransactionManagerLookup
+ */
+ public void setTransactionManagerLookupStrategy(String lkpStrategy);
+
+ /**
+ * Is SQL logging enabled?
+ */
+ public String getShowSqlEnabled();
+ /**
+ * Enable logging of SQL to console
+ */
+ public void setShowSqlEnabled(String showSql);
+ /**
+ * Get the maximum outer join fetch depth
+ */
+ public String getMaximumFetchDepth();
+ /**
+ * Set the maximum outer join fetch depth
+ */
+ public void setMaximumFetchDepth(String fetchDepth);
+ /**
+ * Get the maximum JDBC batch size
+ */
+ public String getJdbcBatchSize();
+ /**
+ * Set the maximum JDBC batch size
+ */
+ public void setJdbcBatchSize(String batchSize);
+ /**
+ * Get the JDBC fetch size
+ */
+ public String getJdbcFetchSize();
+ /**
+ * Set the JDBC fetch size
+ */
+ public void setJdbcFetchSize(String fetchSize);
+ /**
+ * Get the query language substitutions
+ */
+ public String getQuerySubstitutions();
+ /**
+ * Set the query language substitutions
+ */
+ public void setQuerySubstitutions(String querySubstitutions);
+ /**
+ * Get the default schema
+ */
+ public String getDefaultSchema();
+ /**
+ * Set the default schema
+ */
+ public void setDefaultSchema(String schema);
+ /**
+ * Get the default catalog
+ */
+ public String getDefaultCatalog();
+ /**
+ * Set the default catalog
+ */
+ public void setDefaultCatalog(String catalog);
+ /**
+ * Is use of scrollable resultsets enabled?
+ */
+ public String getJdbcScrollableResultSetEnabled();
+ /**
+ * Enable or disable the use of scrollable resultsets
+ */
+ public void setJdbcScrollableResultSetEnabled(String enabled);
+ /**
+ * Is use of JDBC3 <tt>getGeneratedKeys()</tt> enabled?
+ */
+ public String getGetGeneratedKeysEnabled();
+ /**
+ * Enable or disable the use <tt>getGeneratedKeys()</tt>
+ */
+ public void setGetGeneratedKeysEnabled(String enabled);
+ /**
+ * Get the second-level cache provider class name
+ */
+ public String getCacheProviderClass();
+ /**
+ * Set the second-level cache provider class name
+ */
+ public void setCacheProviderClass(String providerClassName);
+ /**
+ * For cache providers which support this setting, get the
+ * provider's specific configuration resource.
+ */
+ public String getCacheProviderConfig();
+ /**
+ * For cache providers which support this setting, specify the
+ * provider's specific configuration resource.
+ */
+ public void setCacheProviderConfig(String cacheProviderConfig);
+ /**
+ * Is the query cache enabled?
+ */
+ public String getQueryCacheEnabled();
+ /**
+ * Enable or disable the query cache
+ */
+ public void setQueryCacheEnabled(String enabled);
+ /**
+ * Is the second-level cache enabled?
+ */
+ public String getSecondLevelCacheEnabled();
+ /**
+ * Enable or disable the second-level cache
+ */
+ public void setSecondLevelCacheEnabled(String enabled);
+ /**
+ * Get the cache region prefix
+ */
+ public String getCacheRegionPrefix();
+ /**
+ * Set the cache region prefix
+ */
+ public void setCacheRegionPrefix(String prefix);
+ /**
+ * Is the second-level cache optimized for miminal puts?
+ */
+ public String getMinimalPutsEnabled();
+ /**
+ * Enable or disable optimization of second-level cache
+ * for minimal puts
+ */
+ public void setMinimalPutsEnabled(String enabled);
+ /**
+ * Are SQL comments enabled?
+ */
+ public String getCommentsEnabled();
+ /**
+ * Enable or disable the inclusion of comments in
+ * generated SQL
+ */
+ public void setCommentsEnabled(String enabled);
+ /**
+ * Is JDBC batch update for versioned entities enabled?
+ */
+ public String getBatchVersionedDataEnabled();
+ /**
+ * Enable or disable the use of batch updates for
+ * versioned entities
+ */
+ public void setBatchVersionedDataEnabled(String enabled);
+
+ /**
+ * Enable automatic flushing of the Session when JTA transaction ends.
+ */
+ public void setFlushBeforeCompletionEnabled(String enabled);
+ /**
+ * Is automatic Session flusing enabled?
+ */
+ public String getFlushBeforeCompletionEnabled();
+
+ /**
+ * Enable automatic closing of Session when JTA transaction ends.
+ */
+ public void setAutoCloseSessionEnabled(String enabled);
+ /**
+ * Is automatic Session closing enabled?
+ */
+ public String getAutoCloseSessionEnabled();
+
+ /**
+ * Export the <tt>CREATE</tt> DDL to the database
+ * @throws HibernateException
+ */
+ public void createSchema() throws HibernateException;
+ /**
+ * Export the <tt>DROP</tt> DDL to the database
+ * @throws HibernateException
+ */
+ public void dropSchema() throws HibernateException;
+
+
+ /**
+ * Create the <tt>SessionFactory</tt> and bind to the jndi name on startup
+ */
+ public void start() throws HibernateException;
+ /**
+ * Unbind the <tt>SessionFactory</tt> or stub from JNDI
+ */
+ public void stop();
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/jmx/src/main/java/org/hibernate/jmx/SessionFactoryStub.java
===================================================================
--- trunk/Hibernate3/jmx/src/main/java/org/hibernate/jmx/SessionFactoryStub.java (rev 0)
+++ trunk/Hibernate3/jmx/src/main/java/org/hibernate/jmx/SessionFactoryStub.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,201 @@
+//$Id: SessionFactoryStub.java 8754 2005-12-05 23:36:59Z steveebersole $
+package org.hibernate.jmx;
+
+import java.io.InvalidObjectException;
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.sql.Connection;
+import java.util.Map;
+import java.util.Set;
+
+import javax.naming.NamingException;
+import javax.naming.Reference;
+import javax.naming.StringRefAddr;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.AssertionFailure;
+import org.hibernate.HibernateException;
+import org.hibernate.Interceptor;
+import org.hibernate.SessionFactory;
+import org.hibernate.StatelessSession;
+import org.hibernate.engine.FilterDefinition;
+import org.hibernate.id.IdentifierGenerator;
+import org.hibernate.id.UUIDHexGenerator;
+import org.hibernate.impl.SessionFactoryObjectFactory;
+import org.hibernate.metadata.ClassMetadata;
+import org.hibernate.metadata.CollectionMetadata;
+import org.hibernate.stat.Statistics;
+
+/**
+ * A flyweight for <tt>SessionFactory</tt>. If the MBean itself does not
+ * have classpath to the persistent classes, then a stub will be registered
+ * with JNDI and the actual <tt>SessionFactoryImpl</tt> built upon first
+ * access.
+ * @author Gavin King
+ */
+public class SessionFactoryStub implements SessionFactory {
+
+ private static final Log log = LogFactory.getLog(SessionFactoryStub.class);
+
+ private static final IdentifierGenerator UUID_GENERATOR = new UUIDHexGenerator();
+
+ private transient SessionFactory impl;
+ private transient HibernateService service;
+ private String uuid;
+ private String name;
+
+ SessionFactoryStub(HibernateService service) {
+ this.service = service;
+ this.name = service.getJndiName();
+ try {
+ uuid = (String) UUID_GENERATOR.generate(null, null);
+ }
+ catch (Exception e) {
+ throw new AssertionFailure("Could not generate UUID");
+ }
+
+ SessionFactoryObjectFactory.addInstance( uuid, name, this, service.getProperties() );
+ }
+
+ public org.hibernate.classic.Session openSession(Connection connection, Interceptor interceptor) {
+ return getImpl().openSession(connection, interceptor);
+ }
+
+ public org.hibernate.classic.Session openSession(Interceptor interceptor) throws HibernateException {
+ return getImpl().openSession(interceptor);
+ }
+
+ public org.hibernate.classic.Session openSession() throws HibernateException {
+ return getImpl().openSession();
+ }
+
+ public org.hibernate.classic.Session openSession(Connection conn) {
+ return getImpl().openSession(conn);
+ }
+
+ public org.hibernate.classic.Session getCurrentSession() {
+ return getImpl().getCurrentSession();
+ }
+
+ private synchronized SessionFactory getImpl() {
+ if (impl==null) impl = service.buildSessionFactory();
+ return impl;
+ }
+
+ //readResolveObject
+ private Object readResolve() throws ObjectStreamException {
+ // look for the instance by uuid
+ Object result = SessionFactoryObjectFactory.getInstance(uuid);
+ if (result==null) {
+ // in case we were deserialized in a different JVM, look for an instance with the same name
+ // (alternatively we could do an actual JNDI lookup here....)
+ result = SessionFactoryObjectFactory.getNamedInstance(name);
+ if (result==null) {
+ throw new InvalidObjectException("Could not find a stub SessionFactory named: " + name);
+ }
+ else {
+ log.debug("resolved stub SessionFactory by name");
+ }
+ }
+ else {
+ log.debug("resolved stub SessionFactory by uid");
+ }
+ return result;
+ }
+
+ /**
+ * @see javax.naming.Referenceable#getReference()
+ */
+ public Reference getReference() throws NamingException {
+ return new Reference(
+ SessionFactoryStub.class.getName(),
+ new StringRefAddr("uuid", uuid),
+ SessionFactoryObjectFactory.class.getName(),
+ null
+ );
+ }
+
+ public ClassMetadata getClassMetadata(Class persistentClass) throws HibernateException {
+ return getImpl().getClassMetadata(persistentClass);
+ }
+
+ public ClassMetadata getClassMetadata(String entityName)
+ throws HibernateException {
+ return getImpl().getClassMetadata(entityName);
+ }
+
+ public CollectionMetadata getCollectionMetadata(String roleName) throws HibernateException {
+ return getImpl().getCollectionMetadata(roleName);
+ }
+
+ public Map getAllClassMetadata() throws HibernateException {
+ return getImpl().getAllClassMetadata();
+ }
+
+ public Map getAllCollectionMetadata() throws HibernateException {
+ return getImpl().getAllCollectionMetadata();
+ }
+
+ public void close() throws HibernateException {
+ }
+
+ public boolean isClosed() {
+ return false;
+ }
+
+ public void evict(Class persistentClass, Serializable id)
+ throws HibernateException {
+ getImpl().evict(persistentClass, id);
+ }
+
+ public void evict(Class persistentClass) throws HibernateException {
+ getImpl().evict(persistentClass);
+ }
+
+ public void evictEntity(String entityName, Serializable id)
+ throws HibernateException {
+ getImpl().evictEntity(entityName, id);
+ }
+
+ public void evictEntity(String entityName) throws HibernateException {
+ getImpl().evictEntity(entityName);
+ }
+
+ public void evictCollection(String roleName, Serializable id)
+ throws HibernateException {
+ getImpl().evictCollection(roleName, id);
+ }
+
+ public void evictCollection(String roleName) throws HibernateException {
+ getImpl().evictCollection(roleName);
+ }
+
+ public void evictQueries() throws HibernateException {
+ getImpl().evictQueries();
+ }
+
+ public void evictQueries(String cacheRegion) throws HibernateException {
+ getImpl().evictQueries(cacheRegion);
+ }
+
+ public Statistics getStatistics() {
+ return getImpl().getStatistics();
+ }
+
+ public StatelessSession openStatelessSession() {
+ return getImpl().openStatelessSession();
+ }
+
+ public StatelessSession openStatelessSession(Connection conn) {
+ return getImpl().openStatelessSession(conn);
+ }
+
+ public Set getDefinedFilterNames() {
+ return getImpl().getDefinedFilterNames();
+ }
+
+ public FilterDefinition getFilterDefinition(String filterName) throws HibernateException {
+ return getImpl().getFilterDefinition( filterName );
+ }
+}
Added: trunk/Hibernate3/jmx/src/main/java/org/hibernate/jmx/StatisticsService.java
===================================================================
--- trunk/Hibernate3/jmx/src/main/java/org/hibernate/jmx/StatisticsService.java (rev 0)
+++ trunk/Hibernate3/jmx/src/main/java/org/hibernate/jmx/StatisticsService.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,317 @@
+//$Id: StatisticsService.java 8262 2005-09-30 07:48:53Z oneovthafew $
+package org.hibernate.jmx;
+
+import javax.naming.InitialContext;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+import javax.naming.Reference;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.SessionFactory;
+import org.hibernate.impl.SessionFactoryObjectFactory;
+import org.hibernate.stat.CollectionStatistics;
+import org.hibernate.stat.EntityStatistics;
+import org.hibernate.stat.QueryStatistics;
+import org.hibernate.stat.SecondLevelCacheStatistics;
+import org.hibernate.stat.Statistics;
+import org.hibernate.stat.StatisticsImpl;
+
+/**
+ * JMX service for Hibernate statistics<br>
+ * <br>
+ * Register this MBean in your JMX server for a specific session factory
+ * <pre>
+ * //build the ObjectName you want
+ * Hashtable tb = new Hashtable();
+ * tb.put("type", "statistics");
+ * tb.put("sessionFactory", "myFinancialApp");
+ * ObjectName on = new ObjectName("hibernate", tb);
+ * StatisticsService stats = new StatisticsService();
+ * stats.setSessionFactory(sessionFactory);
+ * server.registerMBean(stats, on);
+ * </pre>
+ * And call the MBean the way you want<br>
+ * <br>
+ * Register this MBean in your JMX server with no specific session factory
+ * <pre>
+ * //build the ObjectName you want
+ * Hashtable tb = new Hashtable();
+ * tb.put("type", "statistics");
+ * tb.put("sessionFactory", "myFinancialApp");
+ * ObjectName on = new ObjectName("hibernate", tb);
+ * StatisticsService stats = new StatisticsService();
+ * server.registerMBean(stats, on);
+ * </pre>
+ * And call the MBean by providing the <code>SessionFactoryJNDIName</code> first.
+ * Then the session factory will be retrieved from JNDI and the statistics
+ * loaded.
+ *
+ * @author Emmanuel Bernard
+ */
+public class StatisticsService implements StatisticsServiceMBean {
+
+ //TODO: We probably should have a StatisticsNotPublishedException, to make it clean
+
+ SessionFactory sf;
+ String sfJNDIName;
+ Log log = LogFactory.getLog(StatisticsService.class);
+ Statistics stats = new StatisticsImpl();
+
+ /**
+ * @see StatisticsServiceMBean#setSessionFactoryJNDIName(java.lang.String)
+ */
+ public void setSessionFactoryJNDIName(String sfJNDIName) {
+ this.sfJNDIName = sfJNDIName;
+ try {
+ Object obj = new InitialContext().lookup(sfJNDIName);
+ if (obj instanceof Reference) {
+ Reference ref = (Reference) obj;
+ setSessionFactory( (SessionFactory) SessionFactoryObjectFactory.getInstance( (String) ref.get(0).getContent() ) );
+ }
+ else {
+ setSessionFactory( (SessionFactory) obj );
+ }
+ }
+ catch (NameNotFoundException e) {
+ log.error("No session factory with JNDI name " + sfJNDIName, e);
+ setSessionFactory(null);
+ }
+ catch (NamingException e) {
+ log.error("Error while accessing session factory with JNDI name " + sfJNDIName, e);
+ setSessionFactory(null);
+ }
+ catch (ClassCastException e) {
+ log.error("JNDI name " + sfJNDIName + " does not handle a session factory reference", e);
+ setSessionFactory(null);
+ }
+ }
+
+ /**
+ * Useful to init this MBean wo a JNDI session factory name
+ *
+ * @param sf session factory to register
+ */
+ public void setSessionFactory(SessionFactory sf) {
+ this.sf = sf;
+ if (sf == null) {
+ stats = new StatisticsImpl();
+ }
+ else {
+ stats = sf.getStatistics();
+ }
+
+ }
+ /**
+ * @see StatisticsServiceMBean#clear()
+ */
+ public void clear() {
+ stats.clear();
+ }
+ /**
+ * @see StatisticsServiceMBean#getEntityStatistics(java.lang.String)
+ */
+ public EntityStatistics getEntityStatistics(String entityName) {
+ return stats.getEntityStatistics(entityName);
+ }
+ /**
+ * @see StatisticsServiceMBean#getCollectionStatistics(java.lang.String)
+ */
+ public CollectionStatistics getCollectionStatistics(String role) {
+ return stats.getCollectionStatistics(role);
+ }
+ /**
+ * @see StatisticsServiceMBean#getSecondLevelCacheStatistics(java.lang.String)
+ */
+ public SecondLevelCacheStatistics getSecondLevelCacheStatistics(String regionName) {
+ return stats.getSecondLevelCacheStatistics(regionName);
+ }
+ /**
+ * @see StatisticsServiceMBean#getQueryStatistics(java.lang.String)
+ */
+ public QueryStatistics getQueryStatistics(String hql) {
+ return stats.getQueryStatistics(hql);
+ }
+ /**
+ * @see StatisticsServiceMBean#getEntityDeleteCount()
+ */
+ public long getEntityDeleteCount() {
+ return stats.getEntityDeleteCount();
+ }
+ /**
+ * @see StatisticsServiceMBean#getEntityInsertCount()
+ */
+ public long getEntityInsertCount() {
+ return stats.getEntityInsertCount();
+ }
+ /**
+ * @see StatisticsServiceMBean#getEntityLoadCount()
+ */
+ public long getEntityLoadCount() {
+ return stats.getEntityLoadCount();
+ }
+ /**
+ * @see StatisticsServiceMBean#getEntityFetchCount()
+ */
+ public long getEntityFetchCount() {
+ return stats.getEntityFetchCount();
+ }
+ /**
+ * @see StatisticsServiceMBean#getEntityUpdateCount()
+ */
+ public long getEntityUpdateCount() {
+ return stats.getEntityUpdateCount();
+ }
+ /**
+ * @see StatisticsServiceMBean#getQueryExecutionCount()
+ */
+ public long getQueryExecutionCount() {
+ return stats.getQueryExecutionCount();
+ }
+ public long getQueryCacheHitCount() {
+ return stats.getQueryCacheHitCount();
+ }
+ public long getQueryExecutionMaxTime() {
+ return stats.getQueryExecutionMaxTime();
+ }
+ public long getQueryCacheMissCount() {
+ return stats.getQueryCacheMissCount();
+ }
+ public long getQueryCachePutCount() {
+ return stats.getQueryCachePutCount();
+ }
+ /**
+ * @see StatisticsServiceMBean#getFlushCount()
+ */
+ public long getFlushCount() {
+ return stats.getFlushCount();
+ }
+ /**
+ * @see StatisticsServiceMBean#getConnectCount()
+ */
+ public long getConnectCount() {
+ return stats.getConnectCount();
+ }
+ /**
+ * @see StatisticsServiceMBean#getSecondLevelCacheHitCount()
+ */
+ public long getSecondLevelCacheHitCount() {
+ return stats.getSecondLevelCacheHitCount();
+ }
+ /**
+ * @see StatisticsServiceMBean#getSecondLevelCacheMissCount()
+ */
+ public long getSecondLevelCacheMissCount() {
+ return stats.getSecondLevelCacheMissCount();
+ }
+ /**
+ * @see StatisticsServiceMBean#getSecondLevelCachePutCount()
+ */
+ public long getSecondLevelCachePutCount() {
+ return stats.getSecondLevelCachePutCount();
+ }
+ /**
+ * @see StatisticsServiceMBean#getSessionCloseCount()
+ */
+ public long getSessionCloseCount() {
+ return stats.getSessionCloseCount();
+ }
+ /**
+ * @see StatisticsServiceMBean#getSessionOpenCount()
+ */
+ public long getSessionOpenCount() {
+ return stats.getSessionOpenCount();
+ }
+ /**
+ * @see StatisticsServiceMBean#getCollectionLoadCount()
+ */
+ public long getCollectionLoadCount() {
+ return stats.getCollectionLoadCount();
+ }
+ /**
+ * @see StatisticsServiceMBean#getCollectionFetchCount()
+ */
+ public long getCollectionFetchCount() {
+ return stats.getCollectionFetchCount();
+ }
+ /**
+ * @see StatisticsServiceMBean#getCollectionUpdateCount()
+ */
+ public long getCollectionUpdateCount() {
+ return stats.getCollectionUpdateCount();
+ }
+ /**
+ * @see StatisticsServiceMBean#getCollectionRemoveCount()
+ */
+ public long getCollectionRemoveCount() {
+ return stats.getCollectionRemoveCount();
+ }
+ /**
+ * @see StatisticsServiceMBean#getCollectionRecreateCount()
+ */
+ public long getCollectionRecreateCount() {
+ return stats.getCollectionRecreateCount();
+ }
+ /**
+ * @see StatisticsServiceMBean#getStartTime()
+ */
+ public long getStartTime() {
+ return stats.getStartTime();
+ }
+
+ /**
+ * @see StatisticsServiceMBean#isStatisticsEnabled()
+ */
+ public boolean isStatisticsEnabled() {
+ return stats.isStatisticsEnabled();
+ }
+
+ /**
+ * @see StatisticsServiceMBean#setStatisticsEnabled(boolean)
+ */
+ public void setStatisticsEnabled(boolean enable) {
+ stats.setStatisticsEnabled(enable);
+ }
+
+ public void logSummary() {
+ stats.logSummary();
+ }
+
+ public String[] getCollectionRoleNames() {
+ return stats.getCollectionRoleNames();
+ }
+
+ public String[] getEntityNames() {
+ return stats.getEntityNames();
+ }
+
+ public String[] getQueries() {
+ return stats.getQueries();
+ }
+
+ public String[] getSecondLevelCacheRegionNames() {
+ return stats.getSecondLevelCacheRegionNames();
+ }
+
+ public long getSuccessfulTransactionCount() {
+ return stats.getSuccessfulTransactionCount();
+ }
+ public long getTransactionCount() {
+ return stats.getTransactionCount();
+ }
+
+ public long getCloseStatementCount() {
+ return stats.getCloseStatementCount();
+ }
+ public long getPrepareStatementCount() {
+ return stats.getPrepareStatementCount();
+ }
+
+ public long getOptimisticFailureCount() {
+ return stats.getOptimisticFailureCount();
+ }
+
+ public String getQueryExecutionMaxTimeQueryString() {
+ return stats.getQueryExecutionMaxTimeQueryString();
+ }
+}
Added: trunk/Hibernate3/jmx/src/main/java/org/hibernate/jmx/StatisticsServiceMBean.java
===================================================================
--- trunk/Hibernate3/jmx/src/main/java/org/hibernate/jmx/StatisticsServiceMBean.java (rev 0)
+++ trunk/Hibernate3/jmx/src/main/java/org/hibernate/jmx/StatisticsServiceMBean.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,19 @@
+//$Id: StatisticsServiceMBean.java 4332 2004-08-15 12:55:28Z oneovthafew $
+package org.hibernate.jmx;
+
+import org.hibernate.stat.Statistics;
+
+/**
+ * MBean exposing Session Factory statistics
+ *
+ * @see org.hibernate.stat.Statistics
+ * @author Emmanuel Bernard
+ */
+public interface StatisticsServiceMBean extends Statistics {
+ /**
+ * Publish the statistics of a session factory bound to
+ * the default JNDI context
+ * @param sfJNDIName session factory jndi name
+ */
+ public abstract void setSessionFactoryJNDIName(String sfJNDIName);
+}
\ No newline at end of file
Added: trunk/Hibernate3/jmx/src/main/java/org/hibernate/jmx/package.html
===================================================================
--- trunk/Hibernate3/jmx/src/main/java/org/hibernate/jmx/package.html (rev 0)
+++ trunk/Hibernate3/jmx/src/main/java/org/hibernate/jmx/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,14 @@
+<html>
+<head></head>
+<body>
+<p>
+ This package exposes a Hibernate instance via JMX.
+</p>
+<p>
+ <tt>HibernateService</tt> allows configuration and management
+ of the Hibernate runtime. <tt>StatisticsService</tt>
+ reports information that might be useful for performance
+ tuning.
+</p>
+</body>
+</html>
Property changes on: trunk/Hibernate3/jmx/src/main/java/org/hibernate/jmx/package.html
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testing/pom.xml
===================================================================
--- trunk/Hibernate3/testing/pom.xml (rev 0)
+++ trunk/Hibernate3/testing/pom.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,32 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-core-project</artifactId>
+ <version>3.3.0.beta1</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-testing</artifactId>
+ <packaging>jar</packaging>
+ <name>Hibernate Testing</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>3.8.1</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-core</artifactId>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
Added: trunk/Hibernate3/testing/src/main/java/org/hibernate/junit/AbstractClassLoaderIsolatedTestCase.java
===================================================================
--- trunk/Hibernate3/testing/src/main/java/org/hibernate/junit/AbstractClassLoaderIsolatedTestCase.java (rev 0)
+++ trunk/Hibernate3/testing/src/main/java/org/hibernate/junit/AbstractClassLoaderIsolatedTestCase.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,34 @@
+package org.hibernate.junit;
+
+/**
+ * A specialized TestCase for running tests in an isolated class-loader
+ *
+ * @author Steve Ebersole
+ */
+public abstract class AbstractClassLoaderIsolatedTestCase extends UnitTestCase {
+ private ClassLoader parentLoader;
+ private ClassLoader isolatedLoader;
+
+ public AbstractClassLoaderIsolatedTestCase(String string) {
+ super( string );
+ }
+
+ protected void setUp() throws Exception {
+ parentLoader = Thread.currentThread().getContextClassLoader();
+ isolatedLoader = buildIsolatedClassLoader( parentLoader );
+ Thread.currentThread().setContextClassLoader( isolatedLoader );
+ super.setUp();
+ }
+
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ Thread.currentThread().setContextClassLoader( parentLoader );
+ releaseIsolatedClassLoader( isolatedLoader );
+ parentLoader = null;
+ isolatedLoader = null;
+ }
+
+ protected abstract ClassLoader buildIsolatedClassLoader(ClassLoader parent);
+
+ protected abstract void releaseIsolatedClassLoader(ClassLoader isolatedLoader);
+}
Added: trunk/Hibernate3/testing/src/main/java/org/hibernate/junit/SkipLog.java
===================================================================
--- trunk/Hibernate3/testing/src/main/java/org/hibernate/junit/SkipLog.java (rev 0)
+++ trunk/Hibernate3/testing/src/main/java/org/hibernate/junit/SkipLog.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,13 @@
+package org.hibernate.junit;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Well-known-location lookup for the test-skip log...
+ *
+ * @author Steve Ebersole
+ */
+public class SkipLog {
+ public static final Log LOG = LogFactory.getLog( "org.hibernate.test.SKIPPED" );
+}
Added: trunk/Hibernate3/testing/src/main/java/org/hibernate/junit/TestSuiteVisitor.java
===================================================================
--- trunk/Hibernate3/testing/src/main/java/org/hibernate/junit/TestSuiteVisitor.java (rev 0)
+++ trunk/Hibernate3/testing/src/main/java/org/hibernate/junit/TestSuiteVisitor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,42 @@
+package org.hibernate.junit;
+
+import java.util.Enumeration;
+
+import junit.framework.TestSuite;
+import junit.framework.Test;
+
+/**
+ * Handles walking a TestSuite hierarchy for recognition of individual tests.
+ *
+ * @author Steve Ebersole
+ */
+public class TestSuiteVisitor {
+
+ private final TestSuiteVisitor.Handler handler;
+
+ public TestSuiteVisitor(TestSuiteVisitor.Handler handler) {
+ this.handler = handler;
+ }
+
+ public void visit(TestSuite testSuite) {
+ handler.startingTestSuite( testSuite );
+ Enumeration tests = testSuite.tests();
+ while ( tests.hasMoreElements() ) {
+ Test test = ( Test ) tests.nextElement();
+ if ( test instanceof TestSuite ) {
+ visit( ( TestSuite ) test );
+ }
+ else {
+ handler.handleTestCase( test );
+ }
+ }
+ handler.completedTestSuite( testSuite );
+ }
+
+ public static interface Handler {
+ public void handleTestCase(Test test);
+ public void startingTestSuite(TestSuite suite);
+ public void completedTestSuite(TestSuite suite);
+ }
+
+}
Added: trunk/Hibernate3/testing/src/main/java/org/hibernate/junit/UnitTestCase.java
===================================================================
--- trunk/Hibernate3/testing/src/main/java/org/hibernate/junit/UnitTestCase.java (rev 0)
+++ trunk/Hibernate3/testing/src/main/java/org/hibernate/junit/UnitTestCase.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,84 @@
+package org.hibernate.junit;
+
+import java.util.Iterator;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import junit.framework.AssertionFailedError;
+
+
+/**
+ * A basic JUnit {@link junit.framework.TestCase} subclass for
+ * adding some Hibernate specific behavior and functionality.
+ *
+ * @author Steve Ebersole
+ */
+public abstract class UnitTestCase extends junit.framework.TestCase {
+
+ private static final Log log = LogFactory.getLog( UnitTestCase.class );
+
+ public UnitTestCase(String string) {
+ super( string );
+ }
+
+ /**
+ * runBare overridden in order to apply FailureExpected validations
+ * as well as start/complete logging
+ *
+ * @throws Throwable
+ */
+ public void runBare() throws Throwable {
+ final boolean doValidate = getName().endsWith( "FailureExpected" ) && Boolean.getBoolean( "hibernate.test.validatefailureexpected" );
+ try {
+ log.info( "Starting test [" + fullTestName() + "]" );
+ super.runBare();
+ if ( doValidate ) {
+ fail( "Test marked as FailureExpected, but did not fail!" );
+ }
+ }
+ catch( Throwable t ) {
+ if ( doValidate ) {
+ skipExpectedFailure( t );
+ }
+ else {
+ throw t;
+ }
+ }
+ finally {
+ log.info( "Completed test [" + fullTestName() + "]" );
+ }
+ }
+
+ protected void skipExpectedFailure(Throwable error) {
+ reportSkip( "ignoring *FailuredExpected methods", "Failed with: " + error.toString() );
+ }
+
+ // additional assertions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public static void assertElementTypeAssignability(java.util.Collection collection, Class clazz) throws AssertionFailedError {
+ Iterator itr = collection.iterator();
+ while ( itr.hasNext() ) {
+ assertClassAssignability( itr.next().getClass(), clazz );
+ }
+ }
+
+ public static void assertClassAssignability(Class source, Class target) throws AssertionFailedError {
+ if ( !target.isAssignableFrom( source ) ) {
+ throw new AssertionFailedError(
+ "Classes were not assignment-compatible : source<" + source.getName() +
+ "> target<" + target.getName() + ">"
+ );
+ }
+ }
+
+
+ // test skipping ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public String fullTestName() {
+ return this.getClass().getName() + "#" + this.getName();
+ }
+
+ protected void reportSkip(String reason, String testDescription) {
+ SkipLog.LOG.warn( "*** skipping [" + fullTestName() + "] - " + testDescription + " : " + reason, new Exception() );
+ }
+}
Added: trunk/Hibernate3/testing/src/main/java/org/hibernate/junit/functional/DatabaseSpecificFunctionalTestCase.java
===================================================================
--- trunk/Hibernate3/testing/src/main/java/org/hibernate/junit/functional/DatabaseSpecificFunctionalTestCase.java (rev 0)
+++ trunk/Hibernate3/testing/src/main/java/org/hibernate/junit/functional/DatabaseSpecificFunctionalTestCase.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,27 @@
+package org.hibernate.junit.functional;
+
+import org.hibernate.junit.SkipLog;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public abstract class DatabaseSpecificFunctionalTestCase extends FunctionalTestCase {
+ public DatabaseSpecificFunctionalTestCase(String string) {
+ super( string );
+ }
+
+ protected void runTest() throws Throwable {
+ // Note: this protection comes into play when running
+ // tests individually. The suite as a whole is already
+ // "protected" by the fact that these tests are actually
+ // filtered out of the suite
+ if ( appliesTo( getDialect() ) ) {
+ super.runTest();
+ }
+ else {
+ SkipLog.LOG.warn( "skipping database-specific test [" + fullTestName() + "] for dialect [" + getDialect().getClass().getName() + "]" );
+ }
+ }
+}
Added: trunk/Hibernate3/testing/src/main/java/org/hibernate/junit/functional/ExecutionEnvironment.java
===================================================================
--- trunk/Hibernate3/testing/src/main/java/org/hibernate/junit/functional/ExecutionEnvironment.java (rev 0)
+++ trunk/Hibernate3/testing/src/main/java/org/hibernate/junit/functional/ExecutionEnvironment.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,161 @@
+package org.hibernate.junit.functional;
+
+import java.util.Iterator;
+import java.sql.Blob;
+import java.sql.Clob;
+
+import org.hibernate.dialect.Dialect;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.cfg.Mappings;
+import org.hibernate.SessionFactory;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Property;
+import org.hibernate.mapping.SimpleValue;
+import org.hibernate.mapping.Collection;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class ExecutionEnvironment {
+
+ public static final Dialect DIALECT = Dialect.getDialect();
+
+ private final ExecutionEnvironment.Settings settings;
+
+ private Configuration configuration;
+ private SessionFactory sessionFactory;
+ private boolean allowRebuild;
+
+ public ExecutionEnvironment(ExecutionEnvironment.Settings settings) {
+ this.settings = settings;
+ }
+
+ public boolean isAllowRebuild() {
+ return allowRebuild;
+ }
+
+ public void setAllowRebuild(boolean allowRebuild) {
+ this.allowRebuild = allowRebuild;
+ }
+
+ public Dialect getDialect() {
+ return DIALECT;
+ }
+
+ public Configuration getConfiguration() {
+ return configuration;
+ }
+
+ public SessionFactory getSessionFactory() {
+ return sessionFactory;
+ }
+
+ public void initialize() {
+ if ( sessionFactory != null ) {
+ throw new IllegalStateException( "attempt to initialize already initialized ExecutionEnvironment" );
+ }
+ if ( ! settings.appliesTo( getDialect() ) ) {
+ return;
+ }
+
+ Configuration configuration = new Configuration();
+ configuration.setProperty( Environment.CACHE_PROVIDER, "org.hibernate.cache.HashtableCacheProvider" );
+
+ settings.configure( configuration );
+
+ applyMappings( configuration );
+ applyCacheSettings( configuration );
+
+
+ if ( settings.createSchema() ) {
+ configuration.setProperty( Environment.HBM2DDL_AUTO, "create-drop" );
+ }
+
+ // make sure we use the same dialect...
+ configuration.setProperty( Environment.DIALECT, getDialect().getClass().getName() );
+
+ configuration.buildMappings();
+ settings.afterConfigurationBuilt( configuration.createMappings(), getDialect() );
+
+ SessionFactory sessionFactory = configuration.buildSessionFactory();
+ this.configuration = configuration;
+ this.sessionFactory = sessionFactory;
+
+ settings.afterSessionFactoryBuilt( ( SessionFactoryImplementor ) sessionFactory );
+ }
+
+ private void applyMappings(Configuration configuration) {
+ String[] mappings = settings.getMappings();
+ for ( int i = 0; i < mappings.length; i++ ) {
+ configuration.addResource( settings.getBaseForMappings() + mappings[i], ExecutionEnvironment.class.getClassLoader() );
+ }
+ }
+
+ private void applyCacheSettings(Configuration configuration) {
+ if ( settings.getCacheConcurrencyStrategy() != null ) {
+ Iterator iter = configuration.getClassMappings();
+ while ( iter.hasNext() ) {
+ PersistentClass clazz = (PersistentClass) iter.next();
+ Iterator props = clazz.getPropertyClosureIterator();
+ boolean hasLob = false;
+ while ( props.hasNext() ) {
+ Property prop = (Property) props.next();
+ if ( prop.getValue().isSimpleValue() ) {
+ String type = ( ( SimpleValue ) prop.getValue() ).getTypeName();
+ if ( "blob".equals(type) || "clob".equals(type) ) {
+ hasLob = true;
+ }
+ if ( Blob.class.getName().equals(type) || Clob.class.getName().equals(type) ) {
+ hasLob = true;
+ }
+ }
+ }
+ if ( !hasLob && !clazz.isInherited() && settings.overrideCacheStrategy() ) {
+ configuration.setCacheConcurrencyStrategy( clazz.getEntityName(), settings.getCacheConcurrencyStrategy() );
+ }
+ }
+ iter = configuration.getCollectionMappings();
+ while ( iter.hasNext() ) {
+ Collection coll = (Collection) iter.next();
+ configuration.setCollectionCacheConcurrencyStrategy( coll.getRole(), settings.getCacheConcurrencyStrategy() );
+ }
+ }
+ }
+
+ public void rebuild() {
+ if ( !allowRebuild ) {
+ return;
+ }
+ if ( sessionFactory != null ) {
+ sessionFactory.close();
+ sessionFactory = null;
+ }
+ sessionFactory = configuration.buildSessionFactory();
+ settings.afterSessionFactoryBuilt( ( SessionFactoryImplementor ) sessionFactory );
+ }
+
+ public void complete() {
+ if ( sessionFactory != null ) {
+ sessionFactory.close();
+ sessionFactory = null;
+ }
+ configuration = null;
+ }
+
+ public static interface Settings {
+ public String[] getMappings();
+ public String getBaseForMappings();
+ public boolean createSchema();
+ public boolean recreateSchemaAfterFailure();
+ public void configure(Configuration cfg);
+ public boolean overrideCacheStrategy();
+ public String getCacheConcurrencyStrategy();
+ public void afterSessionFactoryBuilt(SessionFactoryImplementor sfi);
+ public void afterConfigurationBuilt(Mappings mappings, Dialect dialect);
+ public boolean appliesTo(Dialect dialect);
+ }
+}
Added: trunk/Hibernate3/testing/src/main/java/org/hibernate/junit/functional/FunctionalTestCase.java
===================================================================
--- trunk/Hibernate3/testing/src/main/java/org/hibernate/junit/functional/FunctionalTestCase.java (rev 0)
+++ trunk/Hibernate3/testing/src/main/java/org/hibernate/junit/functional/FunctionalTestCase.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,505 @@
+package org.hibernate.junit.functional;
+
+import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Mappings;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.DB2Dialect;
+import org.hibernate.dialect.DerbyDialect;
+import org.hibernate.SessionFactory;
+import org.hibernate.HibernateException;
+import org.hibernate.Interceptor;
+import org.hibernate.Session;
+import org.hibernate.junit.UnitTestCase;
+import org.hibernate.engine.SessionFactoryImplementor;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Most of the Hibernate test suite in fact is a series of functional tests, not
+ * unit tests. Here is a base class for these functional tests.
+ *
+ * @author Steve Ebersole
+ */
+public abstract class FunctionalTestCase extends UnitTestCase implements ExecutionEnvironment.Settings {
+
+ private static final Log log = LogFactory.getLog( FunctionalTestCase.class );
+
+ private ExecutionEnvironment environment;
+ private boolean isEnvironmentLocallyManaged;
+
+ private org.hibernate.classic.Session session;
+
+ public FunctionalTestCase(String string) {
+ super( string );
+ }
+
+ public ExecutionEnvironment getEnvironment() {
+ return environment;
+ }
+
+ public void setEnvironment(ExecutionEnvironment environment) {
+ this.environment = environment;
+ }
+
+ protected void prepareTest() throws Exception {
+ }
+
+ protected void cleanupTest() throws Exception {
+ }
+
+ // JUnit hooks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Override {@link junit.framework.TestCase#setUp()} to check if we need
+ * to build a locally managed execution environment.
+ *
+ * @throws Exception
+ */
+ protected final void setUp() throws Exception {
+ if ( environment == null ) {
+ log.info( "Building locally managed execution env" );
+ isEnvironmentLocallyManaged = true;
+ environment = new ExecutionEnvironment( this );
+ environment.initialize();
+ }
+ prepareTest();
+ }
+
+ /**
+ * Override {@link junit.framework.TestCase#tearDown()} to tear down
+ * the execution environment if it is locally managed.
+ *
+ * @throws Exception
+ */
+ protected final void tearDown() throws Exception {
+ cleanupTest();
+ if ( isEnvironmentLocallyManaged ) {
+ log.info( "Destroying locally managed execution env" );
+ environment.complete();
+ environment = null;
+ }
+ }
+
+ /**
+ * runTest is overridden in order to apply session closure assertions.
+ *
+ * @throws Throwable
+ */
+ protected void runTest() throws Throwable {
+ final boolean stats = sfi().getStatistics().isStatisticsEnabled();
+ try {
+ if ( stats ) {
+ sfi().getStatistics().clear();
+ }
+
+ super.runTest();
+
+ if ( stats ) {
+ sfi().getStatistics().logSummary();
+ }
+
+ if ( session != null && session.isOpen() ) {
+ if ( session.isConnected() ) {
+ session.connection().rollback();
+ }
+ session.close();
+ session = null;
+ fail( "unclosed session" );
+ }
+ else {
+ session = null;
+ }
+ assertAllDataRemoved();
+ }
+ catch ( Throwable e ) {
+ log.trace( "test run resulted in error; attempting to cleanup", e );
+ try {
+ if ( session != null && session.isOpen() ) {
+ if ( session.isConnected() ) {
+ session.connection().rollback();
+ }
+ session.close();
+ }
+ }
+ catch ( Exception ignore ) {
+ }
+ try {
+ if ( recreateSchemaAfterFailure() && environment != null ) {
+ environment.rebuild();
+ }
+ }
+ catch ( Exception ignore ) {
+ }
+ throw e;
+ }
+ }
+
+ protected void assertAllDataRemoved() {
+ if ( !createSchema() ) {
+ return; // no tables were created...
+ }
+ if ( !Boolean.getBoolean( "hibernate.test.validateDataCleanup" ) ) {
+ return;
+ }
+
+ Session tmpSession = getSessions().openSession();
+ try {
+ List list = tmpSession.createQuery( "select o from java.lang.Object o" ).list();
+
+ Map items = new HashMap();
+ if ( !list.isEmpty() ) {
+ for ( Iterator iter = list.iterator(); iter.hasNext(); ) {
+ Object element = iter.next();
+ Integer l = ( Integer ) items.get( tmpSession.getEntityName( element ) );
+ if ( l == null ) {
+ l = new Integer( 0 );
+ }
+ l = new Integer( l.intValue() + 1 );
+ items.put( tmpSession.getEntityName( element ), l );
+ System.out.println( "Data left: " + element );
+ }
+ fail( "Data is left in the database: " + items.toString() );
+ }
+ }
+ finally {
+ try {
+ tmpSession.close();
+ }
+ catch( Throwable t ) {
+ // intentionally empty
+ }
+ }
+ }
+
+ protected void skipExpectedFailure(Throwable error) {
+ super.skipExpectedFailure( error );
+ try {
+ if ( recreateSchemaAfterFailure() && environment != null ) {
+ environment.rebuild();
+ }
+ }
+ catch ( Exception ignore ) {
+ }
+ }
+
+ // ExecutionEnvironment.Settings implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public String getBaseForMappings() {
+ return "org/hibernate/test/";
+ }
+
+ public boolean createSchema() {
+ return true;
+ }
+
+ public boolean recreateSchemaAfterFailure() {
+ return true;
+ }
+
+ public void configure(Configuration cfg) {
+ }
+
+ public boolean overrideCacheStrategy() {
+ return true;
+ }
+
+ public String getCacheConcurrencyStrategy() {
+ return "nonstrict-read-write";
+ }
+
+ public void afterSessionFactoryBuilt(SessionFactoryImplementor sfi) {
+ }
+
+ public void afterConfigurationBuilt(Mappings mappings, Dialect dialect) {
+ }
+
+ /**
+ * Intended to indicate that this test class as a whole is intended for
+ * a dialect or series of dialects. Skips here (appliesTo = false) therefore
+ * simply indicate that the given tests target a particular feature of the
+ * checked database and none of the tests on this class should be run for the
+ * checked dialect.
+ *
+ * @param dialect The dialect to be checked.
+ * @return False if the test class as a whole is specifically targetting
+ * a dialect (or series of dialects) other than the indicated dialect
+ * and the test should therefore be skipped in its entirety;
+ * true otherwise.
+ */
+ public boolean appliesTo(Dialect dialect) {
+ return true;
+ }
+
+
+ // methods for subclasses to access environment ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Get the factory for this test environment.
+ *
+ * @return The factory.
+ */
+ protected SessionFactory getSessions() {
+ return environment.getSessionFactory();
+ }
+
+ /**
+ * Get the factory for this test environment, casted to {@link org.hibernate.engine.SessionFactoryImplementor}.
+ * <p/>
+ * Shorthand for ( {@link org.hibernate.engine.SessionFactoryImplementor} ) {@link #getSessions()}...
+ *
+ * @return The factory
+ */
+ protected SessionFactoryImplementor sfi() {
+ return ( SessionFactoryImplementor ) getSessions();
+ }
+
+ protected Dialect getDialect() {
+ return ExecutionEnvironment.DIALECT;
+ }
+
+ protected Configuration getCfg() {
+ return environment.getConfiguration();
+ }
+
+ public org.hibernate.classic.Session openSession() throws HibernateException {
+ session = getSessions().openSession();
+ return session;
+ }
+
+ public org.hibernate.classic.Session openSession(Interceptor interceptor) throws HibernateException {
+ session = getSessions().openSession(interceptor);
+ return session;
+ }
+
+
+
+
+ /**
+ * Is connection at least read committed?
+ * <p/>
+ * Not, that this skip check relies on the JDBC driver reporting
+ * the true isolation level correctly. HSQLDB, for example, will
+ * report whatever you specify as the isolation
+ * (Connection.setTransationIsolation()), even though it only supports
+ * read-uncommitted.
+ *
+ * @param scenario text description of the scenario being tested.
+ * @return true if read-committed isolation is maintained.
+ */
+ protected boolean readCommittedIsolationMaintained(String scenario) {
+ int isolation = java.sql.Connection.TRANSACTION_READ_UNCOMMITTED;
+ Session testSession = null;
+ try {
+ testSession = openSession();
+ isolation = testSession.connection().getTransactionIsolation();
+ }
+ catch( Throwable ignore ) {
+ }
+ finally {
+ if ( testSession != null ) {
+ try {
+ testSession.close();
+ }
+ catch( Throwable ignore ) {
+ }
+ }
+ }
+ if ( isolation < java.sql.Connection.TRANSACTION_READ_COMMITTED ) {
+ reportSkip( "environment does not support at least read committed isolation", scenario );
+ return false;
+ }
+ else {
+ return true;
+ }
+ }
+
+ /**
+ * Does the db/dialect support using a column's physical name in the order-by clause
+ * even after it has been aliased in the select clause. This is not actually
+ * required by the SQL spec, although virtually ever DB in the world supports this
+ * (the most glaring omission here being IBM-variant DBs ala DB2 and Derby).
+ *
+ * @param testDescription description of the scenario being tested.
+ * @return true if is allowed
+ */
+ protected boolean allowsPhysicalColumnNameInOrderby(String testDescription) {
+ if ( DB2Dialect.class.isInstance( getDialect() ) ) {
+ // https://issues.apache.org/jira/browse/DERBY-1624
+ reportSkip( "Dialect does not support physical column name in order-by clause after it is aliased", testDescription );
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Does the db/dialect support using a column's physical name in the having clause
+ * even after it has been aliased in the select/group-by clause. This is not actually
+ * required by the SQL spec, although virtually ever DB in the world supports this.
+ *
+ * @param testDescription description of the scenario being tested.
+ * @return true if is allowed
+ */
+ protected boolean allowsPhysicalColumnNameInHaving(String testDescription) {
+ // I only *know* of this being a limitation on Derby, although I highly suspect
+ // it is a limitation on any IBM/DB2 variant
+ if ( DerbyDialect.class.isInstance( getDialect() ) ) {
+ // https://issues.apache.org/jira/browse/DERBY-1624
+ reportSkip( "Dialect does not support physical column name in having clause after it is aliased", testDescription );
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Does the db/dialect support empty lists in the IN operator?
+ * <p/>
+ * For example, is "... a.b IN () ..." supported?
+ *
+ * @param testDescription description of the scenario being tested.
+ * @return true if is allowed
+ */
+ protected boolean dialectSupportsEmptyInList(String testDescription) {
+ if ( ! getDialect().supportsEmptyInList() ) {
+ reportSkip( "Dialect does not support SQL empty in list : x in ()", testDescription );
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Is the db/dialect sensitive in terms of string comparisons?
+ * @param testDescription description of the scenario being tested.
+ * @return true if sensitive
+ */
+ protected boolean dialectIsCaseSensitive(String testDescription) {
+ if ( ! getDialect().areStringComparisonsCaseInsensitive() ) {
+ reportSkip( "Dialect is case sensitive. ", testDescription );
+ return true;
+ }
+ return false;
+ }
+
+ protected boolean supportsRowValueConstructorSyntaxInInList() {
+ if ( ! getDialect().supportsRowValueConstructorSyntaxInInList() ) {
+ reportSkip( "Dialect does not support 'tuple' syntax as part of an IN value list", "query support" );
+ return false;
+ }
+ return true;
+ }
+
+ protected boolean supportsResultSetPositionQueryMethodsOnForwardOnlyCursor() {
+ if ( ! getDialect().supportsResultSetPositionQueryMethodsOnForwardOnlyCursor() ) {
+ reportSkip( "Driver does not support 'position query' methods on forward-only cursors", "query support" );
+ return false;
+ }
+ return true;
+ }
+
+ protected boolean supportsCircularCascadeDelete() {
+ if ( ! getDialect().supportsCircularCascadeDeleteConstraints() ) {
+ reportSkip( "db/dialect does not support 'circular' cascade delete constraints", "cascade delete constraint support" );
+ return false;
+ }
+ return true;
+ }
+
+ protected boolean supportsSubselectOnLeftSideIn() {
+ if ( ! getDialect().supportsSubselectAsInPredicateLHS() ) {
+ reportSkip( "Database does not support (<subselect>) in ( ... ) ", "query support" );
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Expected LOB usage pattern is such that I can perform an insert
+ * via prepared statement with a parameter binding for a LOB value
+ * without crazy casting to JDBC driver implementation-specific classes...
+ * <p/>
+ * Part of the trickiness here is the fact that this is largely
+ * driver dependent. For Oracle, which is notoriously bad with
+ * LOB support in their drivers actually does a pretty good job with
+ * LOB support as of the 10.2.x versions of their drivers...
+ *
+ * @return True if expected usage pattern is support; false otherwise.
+ */
+ protected boolean supportsExpectedLobUsagePattern() {
+ if ( ! getDialect().supportsExpectedLobUsagePattern() ) {
+ reportSkip( "database/driver does not support expected LOB usage pattern", "LOB support" );
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Does the current dialect support propogating changes to LOB
+ * values back to the database? Talking about mutating the
+ * underlying value as opposed to supplying a new
+ * LOB instance...
+ *
+ * @return True if the changes are propogated back to the
+ * database; false otherwise.
+ */
+ protected boolean supportsLobValueChangePropogation() {
+ if ( ! getDialect().supportsLobValueChangePropogation() ) {
+ reportSkip( "database/driver does not support propogating LOB value change back to database", "LOB support" );
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Is it supported to materialize a LOB locator outside the transaction in
+ * which it was created?
+ * <p/>
+ * Again, part of the trickiness here is the fact that this is largely
+ * driver dependent.
+ * <p/>
+ * NOTE: all database I have tested which {@link #supportsExpectedLobUsagePattern()}
+ * also support the ability to materialize a LOB outside the owning transaction...
+ *
+ * @return True if unbounded materialization is supported; false otherwise.
+ */
+ protected boolean supportsUnboundedLobLocatorMaterialization() {
+ if ( !getDialect().supportsUnboundedLobLocatorMaterialization() ) {
+ reportSkip( "database/driver does not support materializing a LOB locator outside the 'owning' transaction", "LOB support" );
+ return false;
+ }
+ return true;
+ }
+
+ protected boolean supportsSubqueryOnMutatingTable() {
+ if ( !getDialect().supportsSubqueryOnMutatingTable() ) {
+ reportSkip( "database/driver does not support referencing mutating table in subquery", "bulk DML support" );
+ return false;
+ }
+ return true;
+ }
+
+ protected boolean dialectIs(Class dialectClass) {
+ return dialectClass.isInstance( getDialect() );
+ }
+
+ protected boolean dialectIsOneOf(Class[] dialectClasses) {
+ for ( int i = 0; i < dialectClasses.length; i++ ) {
+ if ( dialectClasses[i].isInstance( getDialect() ) ) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ protected boolean dialectIsNot(Class dialectClass) {
+ return ! dialectIs( dialectClass );
+ }
+
+ protected boolean dialectIsNot(Class[] dialectClasses) {
+ return ! dialectIsOneOf( dialectClasses );
+ }
+}
Added: trunk/Hibernate3/testing/src/main/java/org/hibernate/junit/functional/FunctionalTestClassTestSuite.java
===================================================================
--- trunk/Hibernate3/testing/src/main/java/org/hibernate/junit/functional/FunctionalTestClassTestSuite.java (rev 0)
+++ trunk/Hibernate3/testing/src/main/java/org/hibernate/junit/functional/FunctionalTestClassTestSuite.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,129 @@
+package org.hibernate.junit.functional;
+
+import junit.framework.TestSuite;
+import junit.framework.Test;
+import junit.framework.TestResult;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * A specialized {@link junit.framework.TestSuite} implementation intended
+ * for use as an aggregate for a single test class specifically for the purpose
+ * of maintaing a single {@link org.hibernate.SessionFactory} for executings all
+ * tests defined as part of the given functional test class.
+ *
+ * @author Steve Ebersole
+ */
+public class FunctionalTestClassTestSuite extends TestSuite {
+
+ private static final Log log = LogFactory.getLog( FunctionalTestClassTestSuite.class );
+
+ private ExecutionEnvironment.Settings settings;
+ private ExecutionEnvironment environment;
+ private Throwable environmentSetupError;
+ private int testCount;
+ private int testPosition;
+
+ public FunctionalTestClassTestSuite(Class testClass, String name) {
+ super( testClass, name );
+ }
+
+ public FunctionalTestClassTestSuite(Class testClass) {
+ this( testClass, testClass.getName() );
+ }
+
+ /**
+ * Constructor form used during {@link org.hibernate.test.AllTests} filtering...
+ *
+ * @param name The name.
+ */
+ private FunctionalTestClassTestSuite(String name) {
+ super( name );
+ }
+
+ public void addTest(Test test) {
+ log.trace( "adding test [" + test + "]" );
+ if ( settings == null ) {
+ if ( test instanceof ExecutionEnvironment.Settings ) {
+ settings = ( ExecutionEnvironment.Settings ) test;
+ // todo : we could also centralize the skipping of "database specific" tests here
+ // instead of duplicating this notion in AllTests and DatabaseSpecificFunctionalTestCase.
+ // as a test gets added, simply check to see if we should really add it via
+ // DatabaseSpecificFunctionalTestCase.appliesTo( ExecutionEnvironment.DIALECT )...
+ }
+ }
+ testCount++;
+ super.addTest( test );
+ }
+
+ public void run(TestResult testResult) {
+ if ( testCount == 0 ) {
+ // might be zero if database-specific...
+ return;
+ }
+ try {
+ log.info( "Starting test-suite [" + getName() + "]" );
+ setUp();
+ testPosition = 0;
+ super.run( testResult );
+ }
+ finally {
+ try {
+ tearDown();
+ }
+ catch( Throwable ignore ) {
+ }
+ log.info( "Completed test-suite [" + getName() + "]" );
+ }
+ }
+
+ public void runTest(Test test, TestResult testResult) {
+ testPosition++;
+ if ( environmentSetupError != null ) {
+ testResult.startTest( test );
+ testResult.addError( test, environmentSetupError );
+ testResult.endTest( test );
+ return;
+ }
+ if ( ! ( test instanceof FunctionalTestCase ) ) {
+ super.runTest( test, testResult );
+ }
+ else {
+ FunctionalTestCase functionalTest = ( ( FunctionalTestCase ) test );
+ try {
+ // disallow rebuilding the schema because this is the last test
+ // in this suite, thus it is about to get dropped immediately
+ // afterwards anyway...
+ environment.setAllowRebuild( testPosition < testCount );
+ functionalTest.setEnvironment( environment );
+ super.runTest( functionalTest, testResult );
+ }
+ finally {
+ functionalTest.setEnvironment( null );
+ }
+ }
+ }
+
+ protected void setUp() {
+ if ( settings == null ) {
+ return;
+ }
+ log.info( "Building aggregated execution environment" );
+ try {
+ environment = new ExecutionEnvironment( settings );
+ environment.initialize();
+ }
+ catch( Throwable t ) {
+ environmentSetupError = t;
+ }
+ }
+
+ protected void tearDown() {
+ if ( environment != null ) {
+ log.info( "Destroying aggregated execution environment" );
+ environment.complete();
+ this.environment = null;
+ }
+ }
+}
Added: trunk/Hibernate3/testsuite/pom.xml
===================================================================
--- trunk/Hibernate3/testsuite/pom.xml (rev 0)
+++ trunk/Hibernate3/testsuite/pom.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,48 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-core-project</artifactId>
+ <version>3.3.0.beta1</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-testsuite</artifactId>
+ <packaging>jar</packaging>
+ <name>Hibernate Testsuite</name>
+ <description>The testsuite of Hibernate functionality</description>
+ <url>http://hibernate.org</url>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-testing</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-ehcache</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-jbosscache</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-oscache</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-swarmcache</artifactId>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/AllTests.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/AllTests.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/AllTests.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,445 @@
+//$Id: AllTests.java 11496 2007-05-09 03:54:06Z steve.ebersole at jboss.com $
+package org.hibernate.test;
+
+import java.lang.reflect.Constructor;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import junit.textui.TestRunner;
+
+import org.hibernate.dialect.Dialect;
+import org.hibernate.junit.TestSuiteVisitor;
+import org.hibernate.test.abstractembeddedcomponents.cid.AbstractCompositeIdTest;
+import org.hibernate.test.abstractembeddedcomponents.propertyref.AbstractComponentPropertyRefTest;
+import org.hibernate.test.any.AnyTypeTest;
+import org.hibernate.test.array.ArrayTest;
+import org.hibernate.test.ast.ASTIteratorTest;
+import org.hibernate.test.ast.ASTUtilTest;
+import org.hibernate.test.batchfetch.BatchFetchTest;
+import org.hibernate.test.bidi.AuctionTest;
+import org.hibernate.test.bidi.AuctionTest2;
+import org.hibernate.test.bytecode.BytecodeSuite;
+import org.hibernate.test.cache.CacheSuite;
+import org.hibernate.test.cascade.RefreshTest;
+import org.hibernate.test.cid.CompositeIdTest;
+import org.hibernate.test.collection.CollectionSuite;
+import org.hibernate.test.component.ComponentSuite;
+import org.hibernate.test.compositeelement.CompositeElementTest;
+import org.hibernate.test.connections.ConnectionsSuite;
+import org.hibernate.test.criteria.CriteriaQueryTest;
+import org.hibernate.test.cuk.CompositePropertyRefTest;
+import org.hibernate.test.cut.CompositeUserTypeTest;
+import org.hibernate.test.deletetransient.DeleteTransientEntityTest;
+import org.hibernate.test.dialect.functional.DialectFunctionalTestsSuite;
+import org.hibernate.test.dialect.unit.DialectUnitTestsSuite;
+import org.hibernate.test.discriminator.DiscriminatorTest;
+import org.hibernate.test.dynamicentity.interceptor.InterceptorDynamicEntityTest;
+import org.hibernate.test.dynamicentity.tuplizer.TuplizerDynamicEntityTest;
+import org.hibernate.test.ecid.EmbeddedCompositeIdTest;
+import org.hibernate.test.entitymode.EntityModeSuite;
+import org.hibernate.test.exception.SQLExceptionConversionTest;
+import org.hibernate.test.extralazy.ExtraLazyTest;
+import org.hibernate.test.filter.DynamicFilterTest;
+import org.hibernate.test.formulajoin.FormulaJoinTest;
+import org.hibernate.test.generated.GeneratedPropertySuite;
+import org.hibernate.test.generatedkeys.GeneratedKeysSuite;
+import org.hibernate.test.hql.HQLSuite;
+import org.hibernate.test.id.MultipleHiLoPerTableGeneratorTest;
+import org.hibernate.test.idbag.IdBagTest;
+import org.hibernate.test.idclass.IdClassTest;
+import org.hibernate.test.idprops.IdentifierPropertyReferencesTest;
+import org.hibernate.test.immutable.ImmutableTest;
+import org.hibernate.test.instrument.buildtime.InstrumentTest;
+import org.hibernate.test.instrument.runtime.CGLIBInstrumentationTest;
+import org.hibernate.test.instrument.runtime.JavassistInstrumentationTest;
+import org.hibernate.test.interceptor.InterceptorTest;
+import org.hibernate.test.interfaceproxy.InterfaceProxyTest;
+import org.hibernate.test.iterate.IterateTest;
+import org.hibernate.test.join.JoinTest;
+import org.hibernate.test.joinedsubclass.JoinedSubclassTest;
+import org.hibernate.test.joinfetch.JoinFetchTest;
+import org.hibernate.test.jpa.JPAComplianceSuite;
+import org.hibernate.test.keymanytoone.KeyManyToOneSuite;
+import org.hibernate.test.lazycache.InstrumentCacheTest;
+import org.hibernate.test.lazycache.InstrumentCacheTest2;
+import org.hibernate.test.lazyonetoone.LazyOneToOneTest;
+import org.hibernate.test.legacy.ABCProxyTest;
+import org.hibernate.test.legacy.ABCTest;
+import org.hibernate.test.legacy.CacheTest;
+import org.hibernate.test.legacy.ComponentNotNullTest;
+import org.hibernate.test.legacy.ConfigurationPerformanceTest;
+import org.hibernate.test.legacy.FooBarTest;
+import org.hibernate.test.legacy.FumTest;
+import org.hibernate.test.legacy.IJ2Test;
+import org.hibernate.test.legacy.IJTest;
+import org.hibernate.test.legacy.MapTest;
+import org.hibernate.test.legacy.MasterDetailTest;
+import org.hibernate.test.legacy.MultiTableTest;
+import org.hibernate.test.legacy.NonReflectiveBinderTest;
+import org.hibernate.test.legacy.OneToOneCacheTest;
+import org.hibernate.test.legacy.ParentChildTest;
+import org.hibernate.test.legacy.QueryByExampleTest;
+import org.hibernate.test.legacy.SQLFunctionsTest;
+import org.hibernate.test.legacy.SQLLoaderTest;
+import org.hibernate.test.legacy.StatisticsTest;
+import org.hibernate.test.lob.LobSuite;
+import org.hibernate.test.manytomany.ManyToManyTest;
+import org.hibernate.test.map.MapIndexFormulaTest;
+import org.hibernate.test.mapcompelem.MapCompositeElementTest;
+import org.hibernate.test.mapelemformula.MapElementFormulaTest;
+import org.hibernate.test.mapping.PersistentClassVisitorTest;
+import org.hibernate.test.mapping.ValueVisitorTest;
+import org.hibernate.test.mappingexception.MappingExceptionTest;
+import org.hibernate.test.mixed.MixedTest;
+import org.hibernate.test.naturalid.NaturalIdTest;
+import org.hibernate.test.ondelete.OnDeleteTest;
+import org.hibernate.test.onetomany.OneToManyTest;
+import org.hibernate.test.onetoone.OneToOneSuite;
+import org.hibernate.test.ops.OpsSuite;
+import org.hibernate.test.optlock.OptimisticLockTest;
+import org.hibernate.test.ordered.OrderByTest;
+import org.hibernate.test.orphan.OrphanSuite;
+import org.hibernate.test.pagination.PaginationTest;
+import org.hibernate.test.propertyref.PropertyRefSuite;
+import org.hibernate.test.proxy.ProxyTest;
+import org.hibernate.test.querycache.QueryCacheTest;
+import org.hibernate.test.readonly.ReadOnlyTest;
+import org.hibernate.test.rowid.RowIdTest;
+import org.hibernate.test.sorted.SortTest;
+import org.hibernate.test.sql.NativeSqlSupportSuite;
+import org.hibernate.test.stats.SessionStatsTest;
+import org.hibernate.test.stats.StatsTest;
+import org.hibernate.test.subclassfilter.DiscrimSubclassFilterTest;
+import org.hibernate.test.subclassfilter.JoinedSubclassFilterTest;
+import org.hibernate.test.subclassfilter.UnionSubclassFilterTest;
+import org.hibernate.test.subselect.SubselectTest;
+import org.hibernate.test.subselectfetch.SubselectFetchTest;
+import org.hibernate.test.ternary.TernaryTest;
+import org.hibernate.test.timestamp.TimestampTest;
+import org.hibernate.test.tm.CMTTest;
+import org.hibernate.test.typedmanytoone.TypedManyToOneTest;
+import org.hibernate.test.typedonetoone.TypedOneToOneTest;
+import org.hibernate.test.typeparameters.TypeParameterTest;
+import org.hibernate.test.unconstrained.UnconstrainedTest;
+import org.hibernate.test.unidir.BackrefTest;
+import org.hibernate.test.unionsubclass.UnionSubclassTest;
+import org.hibernate.test.usercollection.UserCollectionTypeSuite;
+import org.hibernate.test.util.UtilSuite;
+import org.hibernate.test.version.VersionTest;
+import org.hibernate.test.version.db.DbVersionTest;
+import org.hibernate.test.version.sybase.SybaseTimestampVersioningTest;
+import org.hibernate.test.where.WhereTest;
+import org.hibernate.test.insertordering.InsertOrderingTest;
+import org.hibernate.test.reattachment.ReattachmentSuite;
+
+/**
+ * @author Gavin King
+ */
+public class AllTests {
+
+ /**
+ * Returns the entire test suite (both legacy and new
+ *
+ * @return the entire test suite
+ */
+ public static Test suite() {
+ TestSuite suite = new TestSuite();
+ suite.addTest( NewTests.suite() );
+ suite.addTest( LegacyTests.suite() );
+ return suite;
+ }
+
+ /**
+ * Returns the entire test suite (both legacy and new) w/o filtering
+ *
+ * @return the entire test suite
+ */
+ public static Test unfilteredSuite() {
+ TestSuite suite = new TestSuite();
+ suite.addTest( NewTests.unfilteredSuite() );
+ suite.addTest( LegacyTests.unfilteredSuite() );
+ return suite;
+ }
+
+ /**
+ * Runs the entire test suite.
+ *
+ * @param args n/a
+ * @see #suite
+ */
+ public static void main(String args[]) {
+ TestRunner.run( suite() );
+ }
+
+ /**
+ * An inner class representing the new test suite.
+ */
+ public static class NewTests {
+
+ /**
+ * Returns the new test suite (filtered)
+ *
+ * @return the new test suite
+ */
+ public static Test suite() {
+ return filter( ( TestSuite ) unfilteredSuite() );
+ }
+
+ /**
+ * Returns the new test suite (unfiltered)
+ *
+ * @return the new test suite
+ */
+ public static Test unfilteredSuite() {
+ TestSuite suite = new TestSuite("New tests suite");
+ suite.addTest( OpsSuite.suite() );
+ suite.addTest( NaturalIdTest.suite() );
+ suite.addTest( ComponentSuite.suite() );
+ suite.addTest( ProxyTest.suite() );
+ suite.addTest( VersionTest.suite() );
+ suite.addTest( TimestampTest.suite() );
+ suite.addTest( InterceptorTest.suite() );
+ suite.addTest( EmbeddedCompositeIdTest.suite() );
+ suite.addTest( ImmutableTest.suite() );
+ suite.addTest( ReadOnlyTest.suite() );
+ suite.addTest( IdClassTest.suite() );
+ suite.addTest( ArrayTest.suite() );
+ suite.addTest( TernaryTest.suite() );
+ suite.addTest( CollectionSuite.suite() );
+ suite.addTest( IdBagTest.suite() );
+ suite.addTest( MapCompositeElementTest.suite() );
+ suite.addTest( MapIndexFormulaTest.suite() );
+ suite.addTest( MapElementFormulaTest.suite() );
+ suite.addTest( BackrefTest.suite() );
+ suite.addTest( BatchFetchTest.suite() );
+ suite.addTest( CompositeIdTest.suite() );
+ suite.addTest( CompositeElementTest.suite() );
+ suite.addTest( CompositePropertyRefTest.suite() );
+ suite.addTest( FormulaJoinTest.suite() );
+ suite.addTest( DiscriminatorTest.suite() );
+ suite.addTest( EntityModeSuite.suite() );
+ suite.addTest( DynamicFilterTest.suite() );
+ suite.addTest( InterfaceProxyTest.suite() );
+ suite.addTest( OrphanSuite.suite() );
+ suite.addTest( JoinTest.suite() );
+ suite.addTest( JoinedSubclassTest.suite() );
+ suite.addTest( org.hibernate.test.unionsubclass2.UnionSubclassTest.suite() );
+ suite.addTest( MixedTest.suite() );
+ suite.addTest( OneToManyTest.suite() );
+ suite.addTest( ManyToManyTest.suite() );
+ suite.addTest( OneToOneSuite.suite() );
+ suite.addTest( OptimisticLockTest.suite() );
+ suite.addTest( PropertyRefSuite.suite() );
+ suite.addTest( NativeSqlSupportSuite.suite() );
+ suite.addTest( CriteriaQueryTest.suite() );
+ suite.addTest( SubselectTest.suite() );
+ suite.addTest( SubselectFetchTest.suite() );
+ suite.addTest( JoinFetchTest.suite() );
+ suite.addTest( UnionSubclassTest.suite() );
+ suite.addTest( ASTIteratorTest.suite() );
+ suite.addTest( HQLSuite.suite() );
+ suite.addTest( ASTUtilTest.suite() );
+ suite.addTest( CacheSuite.suite() );
+ suite.addTest( QueryCacheTest.suite() );
+ suite.addTest( CompositeUserTypeTest.suite() );
+ suite.addTest( TypeParameterTest.suite() );
+ suite.addTest( TypedOneToOneTest.suite() );
+ suite.addTest( TypedManyToOneTest.suite() );
+ suite.addTest( CMTTest.suite() );
+ suite.addTest( MultipleHiLoPerTableGeneratorTest.suite() );
+ suite.addTest( UnionSubclassFilterTest.suite() );
+ suite.addTest( JoinedSubclassFilterTest.suite() );
+ suite.addTest( DiscrimSubclassFilterTest.suite() );
+ suite.addTest( UnconstrainedTest.suite() );
+ suite.addTest( RowIdTest.suite() );
+ suite.addTest( OnDeleteTest.suite() );
+ suite.addTest( OrderByTest.suite() );
+ suite.addTest( SortTest.suite() );
+ suite.addTest( WhereTest.suite() );
+ suite.addTest( IterateTest.suite() );
+ suite.addTest( RefreshTest.suite() );
+ suite.addTest( ExtraLazyTest.suite() );
+ suite.addTest( StatsTest.suite() );
+ suite.addTest( SessionStatsTest.suite() );
+ suite.addTest( ConnectionsSuite.suite() );
+ suite.addTest( SQLExceptionConversionTest.suite() );
+ suite.addTest( ValueVisitorTest.suite() );
+ suite.addTest( PersistentClassVisitorTest.suite() );
+ suite.addTest( AuctionTest.suite() );
+ suite.addTest( AuctionTest2.suite() );
+ suite.addTest( PaginationTest.suite() );
+ suite.addTest( MappingExceptionTest.suite() );
+ if ( InstrumentTest.isRunnable() ) {
+ suite.addTest( InstrumentTest.suite() );
+ }
+ if ( LazyOneToOneTest.isRunnable() ) {
+ suite.addTest( LazyOneToOneTest.suite() );
+ }
+ if ( InstrumentCacheTest.isRunnable() ) {
+ suite.addTest( InstrumentCacheTest.suite() );
+ }
+ if ( InstrumentCacheTest2.isRunnable() ) {
+ suite.addTest( InstrumentCacheTest2.suite() );
+ }
+ suite.addTest( CGLIBInstrumentationTest.suite() );
+ suite.addTest( JavassistInstrumentationTest.suite() );
+ suite.addTest( SybaseTimestampVersioningTest.suite() );
+ suite.addTest( DbVersionTest.suite() );
+ suite.addTest( GeneratedPropertySuite.suite() );
+ suite.addTest( GeneratedKeysSuite.suite() );
+ suite.addTest( InterceptorDynamicEntityTest.suite() );
+ suite.addTest( TuplizerDynamicEntityTest.suite() );
+ suite.addTest( BytecodeSuite.suite() );
+ suite.addTest( JPAComplianceSuite.suite() );
+ suite.addTest( AbstractComponentPropertyRefTest.suite() );
+ suite.addTest( AbstractCompositeIdTest.suite() );
+ suite.addTest( UtilSuite.suite() );
+ suite.addTest( AnyTypeTest.suite() );
+ suite.addTest( LobSuite.suite() );
+ suite.addTest( IdentifierPropertyReferencesTest.suite() );
+ suite.addTest( DeleteTransientEntityTest.suite() );
+ suite.addTest( UserCollectionTypeSuite.suite() );
+ suite.addTest( KeyManyToOneSuite.suite() );
+ suite.addTest( DialectFunctionalTestsSuite.suite() );
+ suite.addTest( DialectUnitTestsSuite.suite() );
+ suite.addTest( InsertOrderingTest.suite() );
+ suite.addTest( ReattachmentSuite.suite() );
+
+ return suite;
+ }
+
+ /**
+ * Runs the new test suite
+ *
+ * @param args n/a
+ */
+ public static void main(String[] args) {
+ TestRunner.run( suite() );
+ }
+ }
+
+ /**
+ * An inner class representing the legacy test suite.
+ */
+ public static class LegacyTests {
+
+ /**
+ * Returns the legacy test suite
+ *
+ * @return the legacy test suite
+ */
+ public static Test suite() {
+ return filter( ( TestSuite ) unfilteredSuite() );
+ }
+
+ /**
+ * Returns the legacy test suite
+ *
+ * @return the legacy test suite
+ */
+ public static Test unfilteredSuite() {
+ TestSuite suite = new TestSuite("Legacy tests suite");
+ suite.addTest( FumTest.suite() );
+ suite.addTest( MasterDetailTest.suite() );
+ suite.addTest( ParentChildTest.suite() );
+ suite.addTest( ABCTest.suite() );
+ suite.addTest( ABCProxyTest.suite() );
+ suite.addTest( SQLFunctionsTest.suite() );
+ suite.addTest( SQLLoaderTest.suite() );
+ suite.addTest( MultiTableTest.suite() );
+ suite.addTest( MapTest.suite() );
+ suite.addTest( QueryByExampleTest.suite() );
+ suite.addTest( ComponentNotNullTest.suite() );
+ suite.addTest( IJTest.suite() );
+ suite.addTest( IJ2Test.suite() );
+ suite.addTest( FooBarTest.suite() );
+ suite.addTest( StatisticsTest.suite() );
+ suite.addTest( CacheTest.suite() );
+ suite.addTest( OneToOneCacheTest.suite() );
+ suite.addTest( NonReflectiveBinderTest.suite() );
+ suite.addTest( ConfigurationPerformanceTest.suite() ); // Added to ensure we can utilize the recommended performance tips ;)
+ return suite;
+ }
+
+ /**
+ * Run the legacy test suite
+ *
+ * @param args n/a
+ */
+ public static void main(String[] args) {
+ TestRunner.run( suite() );
+ }
+ }
+
+ private static TestSuite filter(TestSuite testSuite) {
+ FilterHandler handler = new FilterHandler();
+ TestSuiteVisitor visitor = new TestSuiteVisitor( handler );
+ visitor.visit( testSuite );
+ return handler.getFilteredTestSuite();
+ }
+
+ private static class TestSuiteStackEntry {
+ public final TestSuite testSuite;
+ public final TestSuiteStackEntry parentEntry;
+
+ public TestSuiteStackEntry(TestSuite testSuite, TestSuiteStackEntry parentEntry) {
+ this.testSuite = testSuite;
+ this.parentEntry = parentEntry;
+ if ( parentEntry != null ) {
+ parentEntry.testSuite.addTest( testSuite );
+ }
+ }
+ }
+
+ private static class FilterHandler implements TestSuiteVisitor.Handler {
+ private TestSuiteStackEntry topStackElement;
+ private TestSuiteStackEntry currentStackElement;
+ private Dialect dialect = Dialect.getDialect();
+
+ public void handleTestCase(Test test) {
+ if ( test instanceof TestCase ) {
+ TestCase hibernateTestCase = ( TestCase ) test;
+ if ( ! hibernateTestCase.appliesTo( dialect ) ) {
+ System.out.println( "skipping test [" + hibernateTestCase.fullTestName() + "] for dialect [" + dialect.getClass().getName() + "]" );
+ }
+ else {
+ currentStackElement.testSuite.addTest( test );
+ }
+ }
+ else {
+ currentStackElement.testSuite.addTest( test );
+ }
+ }
+
+ public void startingTestSuite(TestSuite suite) {
+ currentStackElement = new TestSuiteStackEntry( instantiateCopy( suite ), currentStackElement );
+ if ( topStackElement == null ) {
+ topStackElement = currentStackElement;
+ }
+ }
+
+ public void completedTestSuite(TestSuite suite) {
+ if ( currentStackElement != null ) {
+ currentStackElement = currentStackElement.parentEntry;
+ }
+ }
+
+ public TestSuite getFilteredTestSuite() {
+ return topStackElement.testSuite;
+ }
+
+ private static final Class[] EXPECTED_CTOR_SIG = new Class[] { String.class };
+
+ private TestSuite instantiateCopy(TestSuite suite) {
+ try {
+ Class testSuiteClass = suite.getClass();
+ Constructor ctor = testSuiteClass.getDeclaredConstructor( EXPECTED_CTOR_SIG );
+ ctor.setAccessible( true );
+ return ( TestSuite ) ctor.newInstance( new Object[] { suite.getName() } );
+ }
+ catch ( Throwable t ) {
+ throw new RuntimeException( "Unable to build test suite copy [" + suite + "]", t );
+ }
+ }
+ }
+}
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/FailureExpectedCollector.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/FailureExpectedCollector.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/FailureExpectedCollector.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,50 @@
+package org.hibernate.junit;
+
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Iterator;
+
+import junit.framework.TestSuite;
+import junit.framework.Test;
+import junit.framework.TestCase;
+
+import org.hibernate.test.AllTests;
+
+/**
+ * A simple class to collect the names of "failure expected" tests...
+ *
+ * @author Steve Ebersole
+ */
+public class FailureExpectedCollector {
+
+ public static void main(String[] args) {
+ Set testNames = collectAllFailureExpectedTestNames();
+ Iterator itr = testNames.iterator();
+ int i = 0;
+ while ( itr.hasNext() ) {
+ i++;
+ System.out.println( i + ") " + itr.next() );
+ }
+ }
+
+ public static Set collectAllFailureExpectedTestNames() {
+ Set names = new HashSet();
+ collectFailureExpectedTestNames( names, ( TestSuite ) AllTests.unfilteredSuite() );
+ return names;
+ }
+
+ public static void collectFailureExpectedTestNames(final Set names, TestSuite suite) {
+ TestSuiteVisitor.Handler handler = new TestSuiteVisitor.Handler() {
+ public void handleTestCase(Test test) {
+ TestCase testCase = ( TestCase ) test;
+ if ( testCase.getName().endsWith( "FailureExpected" ) ) {
+ names.add( testCase.getClass().getName() + "#" + testCase.getName() );
+ }
+ }
+ public void startingTestSuite(TestSuite suite) {}
+ public void completedTestSuite(TestSuite suite) {}
+ };
+ TestSuiteVisitor visitor = new TestSuiteVisitor( handler );
+ visitor.visit( suite );
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/TestCase.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/TestCase.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/TestCase.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,616 @@
+//$Id: TestCase.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test;
+
+import java.sql.Blob;
+import java.sql.Clob;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.AssertionFailedError;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.HibernateException;
+import org.hibernate.Interceptor;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.dialect.DB2Dialect;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.DerbyDialect;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.mapping.Collection;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Property;
+import org.hibernate.mapping.SimpleValue;
+
+public abstract class TestCase extends junit.framework.TestCase {
+
+ private static SessionFactory sessions;
+ private static Configuration cfg;
+ private static Dialect dialect;
+ private static Class lastTestClass;
+ private org.hibernate.classic.Session session;
+
+ public TestCase(String name) {
+ super( name );
+ }
+
+
+ // methods for subclasses to change test environment ~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Get the mapping resources to be used to build the configuration.
+ * <p/>
+ * Resources should be relative to {@link #getBaseForMappings()}
+ *
+ * @return The mapping resources
+ */
+ protected abstract String[] getMappings();
+
+ /**
+ * The base name for relative mapping resources. The default is
+ * <tt>org/hibernate/test/</tt>
+ *
+ * @return the mapping resource base
+ */
+ protected String getBaseForMappings() {
+ return "org/hibernate/test/";
+ }
+
+ /**
+ * Should the database schema be (re)created
+ *
+ * @return True for auto export (including recreation on test failure).
+ */
+ protected boolean recreateSchema() {
+ return true;
+ }
+
+ protected boolean dropAfterFailure() {
+ return true;
+ }
+
+ /**
+ * Apply any test-specific configuration prior to building the factory.
+ *
+ * @param cfg The configuration which will be used to construct the factory.
+ */
+ protected void configure(Configuration cfg) {
+ }
+
+ protected boolean overrideCacheStrategy() {
+ return true;
+ }
+
+ protected String getCacheConcurrencyStrategy() {
+ return "nonstrict-read-write";
+ }
+
+
+ // methods for subclasses to access environment ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Get the factory for this test environment.
+ *
+ * @return The factory.
+ */
+ protected SessionFactory getSessions() {
+ return sessions;
+ }
+
+ /**
+ * Get the factory for this test environment, casted to {@link SessionFactoryImplementor}.
+ * <p/>
+ * Shorthand for ( {@link SessionFactoryImplementor} ) {@link #getSessions()}...
+ *
+ * @return The factory
+ */
+ protected SessionFactoryImplementor sfi() {
+ return ( SessionFactoryImplementor ) getSessions();
+ }
+
+ protected Dialect getDialect() {
+ if ( dialect == null ) {
+ dialect = Dialect.getDialect();
+ }
+ return dialect;
+ }
+
+ protected Configuration getCfg() {
+ return cfg;
+ }
+
+ public org.hibernate.classic.Session openSession() throws HibernateException {
+ session = getSessions().openSession();
+ return session;
+ }
+
+ public org.hibernate.classic.Session openSession(Interceptor interceptor)
+ throws HibernateException {
+ session = getSessions().openSession(interceptor);
+ return session;
+ }
+
+
+ // JUnit hooks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * The Hibernate test suite tries to only build the db schema once
+ * per test class (not test case which = instance) hence all the
+ * static vars.
+ * <p/>
+ * Here is the crux of that attempt. We only build a factory when one was
+ * not previously built, or when we start a new test class.
+ *
+ * @throws Exception
+ */
+ protected void setUp() throws Exception {
+ if ( getSessions() == null || lastTestClass != getClass() ) {
+ buildSessionFactory();
+ lastTestClass = getClass();
+ }
+ }
+
+
+ private void buildSessionFactory() throws Exception {
+ if ( getSessions()!=null ) {
+ getSessions().close();
+ }
+
+ TestCase.dialect = Dialect.getDialect();
+ if ( ! appliesTo( getDialect() ) ) {
+ return;
+ }
+
+ try {
+
+ TestCase.cfg = new Configuration();
+ cfg.setProperty( Environment.CACHE_PROVIDER, "org.hibernate.cache.HashtableCacheProvider" );
+ if( recreateSchema() ) {
+ cfg.setProperty( Environment.HBM2DDL_AUTO, "create-drop" );
+ }
+ addMappings( getMappings(), cfg );
+ configure( cfg );
+
+ if ( getCacheConcurrencyStrategy() != null ) {
+ Iterator iter = cfg.getClassMappings();
+ while ( iter.hasNext() ) {
+ PersistentClass clazz = (PersistentClass) iter.next();
+ Iterator props = clazz.getPropertyClosureIterator();
+ boolean hasLob = false;
+ while ( props.hasNext() ) {
+ Property prop = (Property) props.next();
+ if ( prop.getValue().isSimpleValue() ) {
+ String type = ( (SimpleValue) prop.getValue() ).getTypeName();
+ if ( "blob".equals(type) || "clob".equals(type) ) hasLob = true;
+ if ( Blob.class.getName().equals(type) || Clob.class.getName().equals(type) ) hasLob = true;
+ }
+ }
+ if ( !hasLob && !clazz.isInherited() && overrideCacheStrategy() ) {
+ cfg.setCacheConcurrencyStrategy(
+ clazz.getEntityName(),
+ getCacheConcurrencyStrategy()
+ );
+ }
+ }
+ iter = cfg.getCollectionMappings();
+ while ( iter.hasNext() ) {
+ Collection coll = (Collection) iter.next();
+ cfg.setCollectionCacheConcurrencyStrategy(
+ coll.getRole(),
+ getCacheConcurrencyStrategy()
+ );
+ }
+ }
+
+ // make sure we use the same dialect...
+ cfg.setProperty( Environment.DIALECT, TestCase.dialect.getClass().getName() );
+ TestCase.sessions = cfg.buildSessionFactory();
+ afterSessionFactoryBuilt();
+ }
+ catch ( Exception e ) {
+ e.printStackTrace();
+ throw e;
+ }
+ }
+
+ protected void addMappings(String[] files, Configuration cfg) {
+ for ( int i = 0; i < files.length; i++ ) {
+ if ( !files[i].startsWith( "net/" ) ) {
+ files[i] = getBaseForMappings() + files[i];
+ }
+ cfg.addResource( files[i], TestCase.class.getClassLoader() );
+ }
+ }
+
+ protected void afterSessionFactoryBuilt() throws Exception {
+ // for subclasses to override in order to perform extra "stuff" only
+ // when SF (re)built...
+ }
+
+ protected void runTest() throws Throwable {
+ final boolean stats = sessions.getStatistics().isStatisticsEnabled();
+ try {
+ if ( stats ) {
+ sessions.getStatistics().clear();
+ }
+
+ super.runTest();
+
+ if ( stats ) {
+ sessions.getStatistics().logSummary();
+ }
+
+ if ( session != null && session.isOpen() ) {
+ if ( session.isConnected() ) {
+ session.connection().rollback();
+ }
+ session.close();
+ session = null;
+ fail( "unclosed session" );
+ }
+ else {
+ session = null;
+ }
+ assertAllDataRemoved();
+ }
+ catch ( Throwable e ) {
+ try {
+ if ( session != null && session.isOpen() ) {
+ if ( session.isConnected() ) {
+ session.connection().rollback();
+ }
+ session.close();
+ }
+ }
+ catch ( Exception ignore ) {
+ }
+ try {
+ if ( dropAfterFailure() && sessions != null ) {
+ sessions.close();
+ sessions = null;
+ }
+ }
+ catch ( Exception ignore ) {
+ }
+ throw e;
+ }
+ }
+
+ public void runBare() throws Throwable {
+ String sysPropName = "hibernate.test.validatefailureexpected";
+ assertNotNull( getName() );
+ if ( Boolean.getBoolean( sysPropName ) ) {
+ if ( getName().endsWith( "FailureExpected" ) ) {
+ Throwable t = null;
+ try {
+ super.runBare();
+ }
+ catch ( Throwable afe ) {
+ t = afe;
+ }
+ if ( t == null ) {
+ fail( "Test where marked as FailureExpected, but did not fail!" );
+ }
+ else {
+ reportSkip( "ignoring *FailuredExpected methods", "Failed with: " + t.toString() );
+ }
+ }
+ else {
+ super.runBare();
+ }
+ }
+ else {
+ super.runBare();
+ }
+ }
+
+ protected void assertAllDataRemoved() {
+ if ( !recreateSchema() ) {
+ return; // no tables were created...
+ }
+ if ( !Boolean.getBoolean( "hibernate.test.validateDataCleanup" ) ) {
+ return;
+ }
+
+ Session tmpSession = sessions.openSession();
+ try {
+ List list = tmpSession.createQuery( "select o from java.lang.Object o" ).list();
+
+ Map items = new HashMap();
+ if ( !list.isEmpty() ) {
+ for ( Iterator iter = list.iterator(); iter.hasNext(); ) {
+ Object element = iter.next();
+ Integer l = ( Integer ) items.get( tmpSession.getEntityName( element ) );
+ if ( l == null ) {
+ l = new Integer( 0 );
+ }
+ l = new Integer( l.intValue() + 1 );
+ items.put( tmpSession.getEntityName( element ), l );
+ System.out.println( "Data left: " + element );
+ }
+ fail( "Data is left in the database: " + items.toString() );
+ }
+ }
+ finally {
+ try {
+ tmpSession.close();
+ }
+ catch( Throwable t ) {
+ // intentionally empty
+ }
+ }
+ }
+
+ public static void assertElementTypeAssignability(java.util.Collection collection, Class clazz) throws AssertionFailedError {
+ Iterator itr = collection.iterator();
+ while ( itr.hasNext() ) {
+ assertClassAssignability( itr.next().getClass(), clazz );
+ }
+ }
+
+ public static void assertClassAssignability(Class source, Class target) throws AssertionFailedError {
+ if ( !target.isAssignableFrom( source ) ) {
+ throw new AssertionFailedError(
+ "Classes were not assignment-compatible : source<" + source.getName() +
+ "> target<" + target.getName() + ">"
+ );
+ }
+ }
+
+
+ // test skipping ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ protected static final Log SKIP_LOG = LogFactory.getLog("org.hibernate.test.SKIPPED");
+
+ public String fullTestName() {
+ return this.getName() + " (" + this.getClass().getName() + ")";
+ }
+
+ protected void reportSkip(String reason, String testDescription) {
+ SKIP_LOG.warn( "*** skipping [" + fullTestName() + "] - " + testDescription + " : " + reason, new Exception() );
+ }
+
+ /**
+ * Intended to indicate that this test class as a whole is intended for
+ * a dialect or series of dialects. Skips here (appliesTo = false) therefore
+ * simply indicate that the given tests target a particular feature of the
+ * checked database and none of the tests on this class should be run for the
+ * checked dialect.
+ *
+ * @param dialect The dialect to be checked.
+ * @return True if all the tests on this class apply to the given dialect (and
+ * therefore should be run); false otherwise.
+ */
+ public boolean appliesTo(Dialect dialect) {
+ return true;
+ }
+
+ /**
+ * Is connection at least read committed?
+ * <p/>
+ * Not, that this skip check relies on the JDBC driver reporting
+ * the true isolation level correctly. HSQLDB, for example, will
+ * report whatever you specify as the isolation
+ * (Connection.setTransationIsolation()), even though it only supports
+ * read-uncommitted.
+ *
+ * @param scenario text description of the scenario being tested.
+ * @return true if read-committed isolation is maintained.
+ */
+ protected boolean readCommittedIsolationMaintained(String scenario) {
+ int isolation = java.sql.Connection.TRANSACTION_READ_UNCOMMITTED;
+ Session testSession = null;
+ try {
+ testSession = openSession();
+ isolation = testSession.connection().getTransactionIsolation();
+ }
+ catch( Throwable ignore ) {
+ }
+ finally {
+ if ( testSession != null ) {
+ try {
+ testSession.close();
+ }
+ catch( Throwable ignore ) {
+ }
+ }
+ }
+ if ( isolation < java.sql.Connection.TRANSACTION_READ_COMMITTED ) {
+ reportSkip( "environment does not support at least read committed isolation", scenario );
+ return false;
+ }
+ else {
+ return true;
+ }
+ }
+
+ /**
+ * Does the db/dialect support using a column's physical name in the order-by clause
+ * even after it has been aliased in the select clause. This is not actually
+ * required by the SQL spec, although virtually ever DB in the world supports this
+ * (the most glaring omission here being IBM-variant DBs ala DB2 and Derby).
+ *
+ * @param testDescription description of the scenario being tested.
+ * @return true if is allowed
+ */
+ protected boolean allowsPhysicalColumnNameInOrderby(String testDescription) {
+ if ( DB2Dialect.class.isInstance( getDialect() ) ) {
+ // https://issues.apache.org/jira/browse/DERBY-1624
+ reportSkip( "Dialect does not support physical column name in order-by clause after it is aliased", testDescription );
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Does the db/dialect support using a column's physical name in the having clause
+ * even after it has been aliased in the select/group-by clause. This is not actually
+ * required by the SQL spec, although virtually ever DB in the world supports this.
+ *
+ * @param testDescription description of the scenario being tested.
+ * @return true if is allowed
+ */
+ protected boolean allowsPhysicalColumnNameInHaving(String testDescription) {
+ // I only *know* of this being a limitation on Derby, although I highly suspect
+ // it is a limitation on any IBM/DB2 variant
+ if ( DerbyDialect.class.isInstance( getDialect() ) ) {
+ // https://issues.apache.org/jira/browse/DERBY-1624
+ reportSkip( "Dialect does not support physical column name in having clause after it is aliased", testDescription );
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Does the db/dialect support empty lists in the IN operator?
+ * <p/>
+ * For example, is "... a.b IN () ..." supported?
+ *
+ * @param testDescription description of the scenario being tested.
+ * @return true if is allowed
+ */
+ protected boolean dialectSupportsEmptyInList(String testDescription) {
+ if ( ! getDialect().supportsEmptyInList() ) {
+ reportSkip( "Dialect does not support SQL empty in list : x in ()", testDescription );
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Is the db/dialect sensitive in terms of string comparisons?
+ * @param testDescription description of the scenario being tested.
+ * @return true if sensitive
+ */
+ protected boolean dialectIsCaseSensitive(String testDescription) {
+ if ( getDialect().areStringComparisonsCaseInsensitive() ) {
+ reportSkip( "Dialect is case sensitive. ", testDescription );
+ return true;
+ }
+ return false;
+ }
+
+ protected boolean supportsRowValueConstructorSyntaxInInList() {
+ if ( ! getDialect().supportsRowValueConstructorSyntaxInInList() ) {
+ reportSkip( "Dialect does not support 'tuple' syntax as part of an IN value list", "query support" );
+ return false;
+ }
+ return true;
+ }
+
+ protected boolean supportsResultSetPositionQueryMethodsOnForwardOnlyCursor() {
+ if ( ! getDialect().supportsResultSetPositionQueryMethodsOnForwardOnlyCursor() ) {
+ reportSkip( "Driver does not support 'position query' methods on forward-only cursors", "query support" );
+ return false;
+ }
+ return true;
+ }
+
+ protected boolean supportsCircularCascadeDelete() {
+ if ( ! getDialect().supportsCircularCascadeDeleteConstraints() ) {
+ reportSkip( "db/dialect does not support 'circular' cascade delete constraints", "cascade delete constraint support" );
+ return false;
+ }
+ return true;
+ }
+
+ protected boolean supportsSubselectOnLeftSideIn() {
+ if ( ! getDialect().supportsSubselectAsInPredicateLHS() ) {
+ reportSkip( "Database does not support (<subselect>) in ( ... ) ", "query support" );
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Expected LOB usage pattern is such that I can perform an insert
+ * via prepared statement with a parameter binding for a LOB value
+ * without crazy casting to JDBC driver implementation-specific classes...
+ * <p/>
+ * Part of the trickiness here is the fact that this is largely
+ * driver dependent. For Oracle, which is notoriously bad with
+ * LOB support in their drivers actually does a pretty good job with
+ * LOB support as of the 10.2.x versions of their drivers...
+ *
+ * @return True if expected usage pattern is support; false otherwise.
+ */
+ protected boolean supportsExpectedLobUsagePattern() {
+ if ( ! getDialect().supportsExpectedLobUsagePattern() ) {
+ reportSkip( "database/driver does not support expected LOB usage pattern", "LOB support" );
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Does the current dialect support propogating changes to LOB
+ * values back to the database? Talking about mutating the
+ * underlying value as opposed to supplying a new
+ * LOB instance...
+ *
+ * @return True if the changes are propogated back to the
+ * database; false otherwise.
+ */
+ protected boolean supportsLobValueChangePropogation() {
+ if ( ! getDialect().supportsLobValueChangePropogation() ) {
+ reportSkip( "database/driver does not support propogating LOB value change back to database", "LOB support" );
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Is it supported to materialize a LOB locator outside the transaction in
+ * which it was created?
+ * <p/>
+ * Again, part of the trickiness here is the fact that this is largely
+ * driver dependent.
+ * <p/>
+ * NOTE: all database I have tested which {@link #supportsExpectedLobUsagePattern()}
+ * also support the ability to materialize a LOB outside the owning transaction...
+ *
+ * @return True if unbounded materialization is supported; false otherwise.
+ */
+ protected boolean supportsUnboundedLobLocatorMaterialization() {
+ if ( !getDialect().supportsUnboundedLobLocatorMaterialization() ) {
+ reportSkip( "database/driver does not support materializing a LOB locator outside the 'owning' transaction", "LOB support" );
+ return false;
+ }
+ return true;
+ }
+
+ protected boolean supportsSubqueryOnMutatingTable() {
+ if ( !getDialect().supportsSubqueryOnMutatingTable() ) {
+ reportSkip( "database/driver does not support referencing mutating table in subquery", "bulk DML support" );
+ return false;
+ }
+ return true;
+ }
+
+ protected boolean dialectIs(Class dialectClass) {
+ return dialectClass.isInstance( getDialect() );
+ }
+
+ protected boolean dialectIsOneOf(Class[] dialectClasses) {
+ for ( int i = 0; i < dialectClasses.length; i++ ) {
+ if ( dialectClasses[i].isInstance( getDialect() ) ) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ protected boolean dialectIsNot(Class dialectClass) {
+ return ! dialectIs( dialectClass );
+ }
+
+ protected boolean dialectIsNot(Class[] dialectClasses) {
+ return ! dialectIsOneOf( dialectClasses );
+ }
+
+}
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/TestSelector.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/TestSelector.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/TestSelector.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,64 @@
+package org.hibernate.test;
+
+import java.util.Set;
+import java.util.HashSet;
+import java.io.File;
+
+import org.apache.tools.ant.types.selectors.FileSelector;
+import org.apache.tools.ant.BuildException;
+
+import org.hibernate.junit.TestSuiteVisitor;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * A custom Ant FileSelector used to limit the tests run from the Ant
+ * build script to only those defined in the {@link org.hibernate.test.AllTests} suite.
+ * <p/>
+ * {@link org.hibernate.test.AllTests} is used/maintained by the developers to easily
+ * run the test suite in all IDEs. It represents all the tests
+ * which should actually be run and included in test results.
+ *
+ * @author Steve Ebersole
+ */
+public class TestSelector implements FileSelector {
+
+ private final Set allTestClassNames = new HashSet();
+
+ public TestSelector() {
+ TestSuiteVisitor.Handler handler = new TestSuiteVisitor.Handler() {
+ public void handleTestCase(Test test) {
+ allTestClassNames.add( test.getClass().getName() );
+ }
+ public void startingTestSuite(TestSuite suite) {}
+ public void completedTestSuite(TestSuite suite) {}
+ };
+ TestSuiteVisitor visitor = new TestSuiteVisitor( handler );
+ visitor.visit( ( TestSuite ) AllTests.suite() );
+ }
+
+
+ // FileSelector impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public boolean isSelected(File dir, String fileFromDir, File fullFile) throws BuildException {
+ String correspondingClassName = determineClassName( fileFromDir );
+ return allTestClassNames.contains( correspondingClassName );
+ }
+
+ private String determineClassName(String file) {
+ if ( file.endsWith( ".class" ) ) {
+ file = file.substring( 0, file.length() - 6 );
+ }
+ else if ( file.endsWith( ".java" ) ) {
+ file = file.substring( 0, file.length() - 5 );
+ }
+ else {
+ return null;
+ }
+ file = file.replace( '\\', '.' );
+ file = file.replace( '/', '.' );
+ return file;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/cid/AbstractCompositeIdTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/cid/AbstractCompositeIdTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/cid/AbstractCompositeIdTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,44 @@
+package org.hibernate.test.abstractembeddedcomponents.cid;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+import junit.framework.Test;
+
+/**
+ * @author Steve Ebersole
+ */
+public class AbstractCompositeIdTest extends FunctionalTestCase {
+ public AbstractCompositeIdTest(String x) {
+ super( x );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( AbstractCompositeIdTest.class );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "abstractembeddedcomponents/cid/Mappings.hbm.xml" };
+ }
+
+ public void testEmbeddedCompositeIdentifierOnAbstractClass() {
+ MyInterfaceImpl myInterface = new MyInterfaceImpl();
+ myInterface.setKey1( "key1" );
+ myInterface.setKey2( "key2" );
+ myInterface.setName( "test" );
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.save( myInterface );
+ s.flush();
+
+ s.createQuery( "from MyInterface" ).list();
+
+ s.delete( myInterface );
+ t.commit();
+ s.close();
+
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/cid/Mappings.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/cid/Mappings.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/cid/Mappings.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.abstractembeddedcomponents.cid">
+
+ <class name="MyInterface" table="MY_INTF" proxy="MyInterface">
+ <composite-id>
+ <key-property name="key1" type="string"/>
+ <key-property name="key2" type="string"/>
+ </composite-id>
+ <discriminator column="TYPE" type="string" length="10"/>
+ <property name="name" type="string"/>
+ </class>
+
+ <subclass name="MyInterfaceImpl" extends="MyInterface" discriminator-value="1" proxy="MyInterface">
+ </subclass>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/cid/MyInterface.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/cid/MyInterface.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/cid/MyInterface.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,15 @@
+package org.hibernate.test.abstractembeddedcomponents.cid;
+
+import java.io.Serializable;
+
+/**
+ * @author Steve Ebersole
+ */
+public interface MyInterface extends Serializable {
+ public String getKey1();
+ public void setKey1(String key1);
+ public String getKey2();
+ public void setKey2(String key2);
+ public String getName();
+ public void setName(String name);
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/cid/MyInterfaceImpl.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/cid/MyInterfaceImpl.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/cid/MyInterfaceImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,36 @@
+package org.hibernate.test.abstractembeddedcomponents.cid;
+
+import org.hibernate.test.abstractembeddedcomponents.cid.MyInterface;
+
+/**
+ * @author Steve Ebersole
+ */
+public class MyInterfaceImpl implements MyInterface {
+ private String key1;
+ private String key2;
+ private String name;
+
+ public String getKey1() {
+ return key1;
+ }
+
+ public void setKey1(String key1) {
+ this.key1 = key1;
+ }
+
+ public String getKey2() {
+ return key2;
+ }
+
+ public void setKey2(String key2) {
+ this.key2 = key2;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/propertyref/AbstractComponentPropertyRefTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/propertyref/AbstractComponentPropertyRefTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/propertyref/AbstractComponentPropertyRefTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,49 @@
+package org.hibernate.test.abstractembeddedcomponents.propertyref;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+import junit.framework.Test;
+
+/**
+ * @author Steve Ebersole
+ */
+public class AbstractComponentPropertyRefTest extends FunctionalTestCase {
+ public AbstractComponentPropertyRefTest(String name) {
+ super( name );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( AbstractComponentPropertyRefTest.class );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "abstractembeddedcomponents/propertyref/Mappings.hbm.xml" };
+ }
+
+ public void testPropertiesRefCascades() {
+ Session session = openSession();
+ Transaction trans = session.beginTransaction();
+ ServerImpl server = new ServerImpl();
+ session.save( server );
+ AddressImpl address = new AddressImpl();
+ server.setAddress( address );
+ address.setServer( server );
+ session.flush();
+ session.createQuery( "from Server s join fetch s.address" ).list();
+ trans.commit();
+ session.close();
+
+ assertNotNull( server.getId() );
+ assertNotNull( address.getId() );
+
+ session = openSession();
+ trans = session.beginTransaction();
+ session.delete( address );
+ session.delete( server );
+ trans.commit();
+ session.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/propertyref/Address.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/propertyref/Address.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/propertyref/Address.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,13 @@
+package org.hibernate.test.abstractembeddedcomponents.propertyref;
+
+/**
+ * @author Steve Ebersole
+ */
+public interface Address {
+ public Long getId();
+ public void setId(Long id);
+ public String getAddressType();
+ public void setAddressType(String addressType);
+ public Server getServer();
+ public void setServer(Server server);
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/propertyref/AddressImpl.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/propertyref/AddressImpl.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/propertyref/AddressImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,36 @@
+package org.hibernate.test.abstractembeddedcomponents.propertyref;
+
+import org.hibernate.test.abstractembeddedcomponents.propertyref.Address;
+
+/**
+ * @author Steve Ebersole
+ */
+public class AddressImpl implements Address {
+ private Long id;
+ private String addressType;
+ private Server server;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getAddressType() {
+ return addressType;
+ }
+
+ public void setAddressType(String addressType) {
+ this.addressType = addressType;
+ }
+
+ public Server getServer() {
+ return server;
+ }
+
+ public void setServer(Server server) {
+ this.server = server;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/propertyref/Mappings.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/propertyref/Mappings.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/propertyref/Mappings.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,43 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.abstractembeddedcomponents.propertyref">
+
+ <class name="Address" table="ADDRESS" proxy="Address">
+ <id name="id" type="long" column="ADDRESS_ID">
+ <generator class="native"/>
+ </id>
+ <discriminator column="ADDRESS_TYPE" type="string" length="30"/>
+ <properties name="uniqueAddress">
+ <property name="addressType" column="ADDRESS_TYPE" type="string" insert="false" update="false" length="30"/>
+ <many-to-one name="server" column="SERVER_ID" class="Server" not-null="true"/>
+ </properties>
+ </class>
+
+ <subclass name="AddressImpl" extends="Address" discriminator-value="2" proxy="Address">
+ </subclass>
+
+ <class name="Server" table="SERVER" proxy="Server">
+ <id name="id" type="long" column="SERVER_ID">
+ <generator class="native"/>
+ </id>
+ <discriminator column="SERVER_TYPE" type="string" length="10"/>
+ <property name="serverType" type="string" column="SERVER_TYPE" length="10" update="false" insert="false"/>
+ </class>
+
+ <subclass name="ServerImpl" extends="Server" discriminator-value="1" proxy="Server">
+ <many-to-one name="address"
+ class="AddressImpl"
+ property-ref="uniqueAddress"
+ cascade="all"
+ unique="true"
+ update="false"
+ insert="false">
+ <column name="ADDRESS_TYPE"/>
+ <column name="SERVER_ID"/>
+ </many-to-one>
+ </subclass>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/propertyref/Server.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/propertyref/Server.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/propertyref/Server.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,15 @@
+package org.hibernate.test.abstractembeddedcomponents.propertyref;
+
+import org.hibernate.test.abstractembeddedcomponents.propertyref.Address;
+
+/**
+ * @author Steve Ebersole
+ */
+public interface Server {
+ public Long getId();
+ public void setId(Long id);
+ public String getServerType();
+ public void setServerType(String serverType);
+ public Address getAddress();
+ public void setAddress(Address address);
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/propertyref/ServerImpl.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/propertyref/ServerImpl.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/abstractembeddedcomponents/propertyref/ServerImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,37 @@
+package org.hibernate.test.abstractembeddedcomponents.propertyref;
+
+import org.hibernate.test.abstractembeddedcomponents.propertyref.Address;
+import org.hibernate.test.abstractembeddedcomponents.propertyref.Server;
+
+/**
+ * @author Steve Ebersole
+ */
+public class ServerImpl implements Server {
+ private Long id;
+ private String serverType;
+ private Address address;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getServerType() {
+ return serverType;
+ }
+
+ public void setServerType(String serverType) {
+ this.serverType = serverType;
+ }
+
+ public Address getAddress() {
+ return address;
+ }
+
+ public void setAddress(Address address) {
+ this.address = address;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/Address.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/Address.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/Address.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,30 @@
+package org.hibernate.test.any;
+
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * todo: describe Address
+ *
+ * @author Steve Ebersole
+ */
+public class Address {
+ private Long id;
+ private Set lines = new HashSet();
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public Set getLines() {
+ return lines;
+ }
+
+ public void setLines(Set lines) {
+ this.lines = lines;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/AnyTypeTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/AnyTypeTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/AnyTypeTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,59 @@
+package org.hibernate.test.any;
+
+import org.hibernate.Session;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.junit.functional.FunctionalTestCase;
+
+import junit.framework.Test;
+
+/**
+ * todo: describe AnyTypeTest
+ *
+ * @author Steve Ebersole
+ */
+public class AnyTypeTest extends FunctionalTestCase {
+ public AnyTypeTest(String name) {
+ super( name );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "any/Person.hbm.xml" };
+ }
+
+ public String getCacheConcurrencyStrategy() {
+ // having second level cache causes a condition whereby the original test case would not fail...
+ return null;
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( AnyTypeTest.class );
+ }
+
+ /**
+ * Specific test for HHH-1663...
+ */
+ public void testFlushProcessing() {
+ Session session = openSession();
+ session.beginTransaction();
+ Person person = new Person();
+ Address address = new Address();
+ person.setData( address );
+ session.saveOrUpdate(person);
+ session.saveOrUpdate(address);
+ session.getTransaction().commit();
+ session.close();
+
+ session = openSession();
+ session.beginTransaction();
+ person = (Person) session.load( Person.class, person.getId() );
+ person.setName("makingpersondirty");
+ session.getTransaction().commit();
+ session.close();
+
+ session = openSession();
+ session.beginTransaction();
+ session.delete( person );
+ session.getTransaction().commit();
+ session.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/ComplexPropertyValue.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/ComplexPropertyValue.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/ComplexPropertyValue.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,47 @@
+package org.hibernate.test.any;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ * todo: describe ${NAME}
+ *
+ * @author Steve Ebersole
+ */
+public class ComplexPropertyValue implements PropertyValue {
+ private Long id;
+ private Map subProperties = new HashMap();
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public Map getSubProperties() {
+ return subProperties;
+ }
+
+ public void setSubProperties(Map subProperties) {
+ this.subProperties = subProperties;
+ }
+
+ public String asString() {
+ return "complex[" + keyString() + "]";
+ }
+
+ private String keyString() {
+ StringBuffer buff = new StringBuffer();
+ Iterator itr = subProperties.keySet().iterator();
+ while ( itr.hasNext() ) {
+ buff.append( itr.next() );
+ if ( itr.hasNext() ) {
+ buff.append( ", " );
+ }
+ }
+ return buff.toString();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/IntegerPropertyValue.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/IntegerPropertyValue.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/IntegerPropertyValue.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,38 @@
+package org.hibernate.test.any;
+
+/**
+ * todo: describe IntegerPropertyValue
+ *
+ * @author Steve Ebersole
+ */
+public class IntegerPropertyValue implements PropertyValue {
+ private Long id;
+ private int value;
+
+ public IntegerPropertyValue() {
+ }
+
+ public IntegerPropertyValue(int value) {
+ this.value = value;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ public void setValue(int value) {
+ this.value = value;
+ }
+
+ public String asString() {
+ return Integer.toString( value );
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/Person.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/Person.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/Person.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.any">
+
+ <class name="Person" table="T_ANY_PERSON">
+ <id name="id" column="ID_">
+ <generator class="increment" />
+ </id>
+ <property name="name" />
+ <any name="data" id-type="long" cascade="none">
+ <meta-value value="A" class="Address"/>
+ <column name="DATATYPE_"/>
+ <column name="DATAID_"/>
+ </any>
+ </class>
+
+ <class name="Address" table="T_ANY_ADDRESS">
+ <id name="id" column="ID_">
+ <generator class="increment" />
+ </id>
+ <set name="lines" table="LINE">
+ <key column="ADDRESS" />
+ <element type="string" />
+ </set>
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/Person.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/Person.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/Person.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,37 @@
+package org.hibernate.test.any;
+
+/**
+ * todo: describe Person
+ *
+ * @author Steve Ebersole
+ */
+public class Person {
+ private Long id;
+ private String name;
+ private Object data;
+
+
+ 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 Object getData() {
+ return data;
+ }
+
+ public void setData(Object data) {
+ this.data = data;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/Properties.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/Properties.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/Properties.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,56 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.any">
+
+ <class name="PropertySet" table="T_PROP_SET">
+ <id name="id" column="ID" type="long">
+ <generator class="increment"/>
+ </id>
+ <property name="name" column="NAME" type="string"/>
+ <any name="someSpecificProperty" id-type="long" meta-type="string" cascade="all">
+ <meta-value value="I" class="IntegerPropertyValue"/>
+ <meta-value value="S" class="StringPropertyValue"/>
+ <meta-value value="C" class="ComplexPropertyValue" />
+ <column name="S_S_PROP_TYPE"/>
+ <column name="S_S_PROP_ID"/>
+ </any>
+ <map name="generalProperties" table="T_GEN_PROPS" lazy="true" cascade="all">
+ <key column="PROP_SET_ID"/>
+ <map-key type="string" column="GEN_PROP_NAME"/>
+ <many-to-any id-type="long" meta-type="string">
+ <meta-value value="I" class="IntegerPropertyValue"/>
+ <meta-value value="S" class="StringPropertyValue"/>
+ <column name="PROP_TYPE"/>
+ <column name="PROP_ID"/>
+ </many-to-any>
+ </map>
+ </class>
+
+ <class name="StringPropertyValue" table="T_CHAR_PROP">
+ <id name="id" column="ID" type="long">
+ <generator class="increment"/>
+ </id>
+ <property name="value" column="VAL" not-null="true" type="string"/>
+ </class>
+
+ <class name="IntegerPropertyValue" table="T_NUM_PROP">
+ <id name="id" column="ID" type="long">
+ <generator class="increment"/>
+ </id>
+ <property name="value" column="VAL" not-null="true" type="integer"/>
+ </class>
+
+ <class name="ComplexPropertyValue" table="T_COMPLEX_PROP">
+ <id name="id" column="ID" type="long">
+ <generator class="increment"/>
+ </id>
+ <map name="subProperties" table="T_COMPLEX_SUB_PROPS" lazy="true">
+ <key column="PROP_ID" />
+ <map-key type="string" column="SUB_PROP_NAME" />
+ <element type="string" column="SUB_PROP_VAL" />
+ </map>
+ </class>
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/PropertySet.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/PropertySet.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/PropertySet.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,55 @@
+package org.hibernate.test.any;
+
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * todo: describe PropertySet
+ *
+ * @author Steve Ebersole
+ */
+public class PropertySet {
+ private Long id;
+ private String name;
+ private PropertyValue someSpecificProperty;
+ private Map generalProperties = new HashMap();
+
+ public PropertySet() {
+ }
+
+ public PropertySet(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;
+ }
+
+ public PropertyValue getSomeSpecificProperty() {
+ return someSpecificProperty;
+ }
+
+ public void setSomeSpecificProperty(PropertyValue someSpecificProperty) {
+ this.someSpecificProperty = someSpecificProperty;
+ }
+
+ public Map getGeneralProperties() {
+ return generalProperties;
+ }
+
+ public void setGeneralProperties(Map generalProperties) {
+ this.generalProperties = generalProperties;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/PropertyValue.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/PropertyValue.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/PropertyValue.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,10 @@
+package org.hibernate.test.any;
+
+/**
+ * todo: describe PropertyValue
+ *
+ * @author Steve Ebersole
+ */
+public interface PropertyValue {
+ public String asString();
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/StringPropertyValue.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/StringPropertyValue.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/any/StringPropertyValue.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,38 @@
+package org.hibernate.test.any;
+
+/**
+ * todo: describe StringPropertyValue
+ *
+ * @author Steve Ebersole
+ */
+public class StringPropertyValue implements PropertyValue {
+ private Long id;
+ private String value;
+
+ public StringPropertyValue() {
+ }
+
+ public StringPropertyValue(String value) {
+ this.value = value;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public String asString() {
+ return value;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/array/A.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/array/A.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/array/A.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+ This mapping demonstrates content-based discrimination for the
+ table-per-hierarchy mapping strategy, using a formula
+ discriminator.
+
+-->
+
+<hibernate-mapping
+ package="org.hibernate.test.array">
+
+ <class name="A" lazy="true" table="aaa">
+
+ <id name="id">
+ <generator class="native"/>
+ </id>
+
+ <array name="bs" cascade="all" fetch="join">
+ <key column="a_id"/>
+ <list-index column="idx"/>
+ <one-to-many class="B"/>
+ </array>
+
+ </class>
+
+ <class name="B" lazy="true" table="bbb">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ </class>
+
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/array/A.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/array/A.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/array/A.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/array/A.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,26 @@
+//$Id: A.java 6527 2005-04-26 16:58:52Z oneovthafew $
+package org.hibernate.test.array;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class A {
+ private Integer id;
+ private B[] bs;
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public B[] getBs() {
+ return bs;
+ }
+
+ public void setBs(B[] bs) {
+ this.bs = bs;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/array/A.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/array/ArrayTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/array/ArrayTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/array/ArrayTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,53 @@
+//$Id: ArrayTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.array;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class ArrayTest extends FunctionalTestCase {
+
+ public ArrayTest(String x) {
+ super( x );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "array/A.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( ArrayTest.class );
+ }
+
+ public void testArrayJoinFetch() throws Exception {
+ Session s;
+ Transaction tx;
+ s = openSession();
+ tx = s.beginTransaction();
+ A a = new A();
+ B b = new B();
+ a.setBs( new B[] {b} );
+ s.persist( a );
+ tx.commit();
+ s.close();
+
+ s = openSession();
+ tx = s.beginTransaction();
+ a = (A) s.get( A.class, a.getId() );
+ assertNotNull( a );
+ assertNotNull( a.getBs() );
+ assertEquals( a.getBs().length, 1 );
+ assertNotNull( a.getBs()[0] );
+
+ s.delete(a);
+ s.delete(a.getBs()[0]);
+ tx.commit();
+ s.close();
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/array/ArrayTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/array/B.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/array/B.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/array/B.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,17 @@
+//$Id: B.java 6527 2005-04-26 16:58:52Z oneovthafew $
+package org.hibernate.test.array;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class B {
+ private Integer id;
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/array/B.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ast/ASTIteratorTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ast/ASTIteratorTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ast/ASTIteratorTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,120 @@
+// $Id: ASTIteratorTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.ast;
+
+import java.io.PrintWriter;
+
+import antlr.ASTFactory;
+import antlr.collections.AST;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.hibernate.hql.antlr.HqlTokenTypes;
+import org.hibernate.hql.ast.HqlParser;
+import org.hibernate.hql.ast.util.ASTIterator;
+import org.hibernate.hql.ast.util.ASTParentsFirstIterator;
+import org.hibernate.hql.ast.util.ASTPrinter;
+import org.hibernate.hql.ast.util.ASTUtil;
+import org.hibernate.junit.UnitTestCase;
+
+/**
+ * Test ASTIterator.
+ */
+public class ASTIteratorTest extends UnitTestCase {
+ private ASTFactory factory;
+
+ /**
+ * Standard JUnit test case constructor.
+ *
+ * @param name The name of the test case.
+ */
+ public ASTIteratorTest(String name) {
+ super( name );
+ }
+
+ public static Test suite() {
+ return new TestSuite( ASTIteratorTest.class );
+ }
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ factory = new ASTFactory();
+ }
+
+ /**
+ * Test a simple tree, make sure the iterator encounters every node.
+ */
+ public void testSimpleTree() throws Exception {
+ String input = "select foo from foo in class org.hibernate.test.Foo, fee in class org.hibernate.test.Fee where foo.dependent = fee order by foo.string desc, foo.component.count asc, fee.id";
+ HqlParser parser = HqlParser.getInstance( input );
+ parser.statement();
+ AST ast = parser.getAST();
+ ASTPrinter printer = new ASTPrinter( HqlTokenTypes.class );
+ printer.showAst( ast, new PrintWriter( System.out ) );
+ ASTIterator iterator = new ASTIterator( ast );
+ int count = 0;
+ while ( iterator.hasNext() ) {
+ assertTrue( iterator.next() instanceof AST );
+ count++;
+ }
+ assertEquals( 43, count );
+
+ UnsupportedOperationException uoe = null;
+ try {
+ iterator.remove();
+ }
+ catch ( UnsupportedOperationException e ) {
+ uoe = e;
+ }
+ assertNotNull( uoe );
+ }
+
+ public void testParentsFirstIterator() throws Exception {
+ AST[] tree = new AST[4];
+ AST grandparent = tree[0] = ASTUtil.create( factory, 1, "grandparent" );
+ AST parent = tree[1] = ASTUtil.create( factory, 2, "parent" );
+ AST child = tree[2] = ASTUtil.create( factory, 3, "child" );
+ AST baby = tree[3] = ASTUtil.create( factory, 4, "baby" );
+ AST t = ASTUtil.createTree( factory, tree );
+ AST brother = ASTUtil.create( factory, 10, "brother" );
+ child.setNextSibling( brother );
+ AST sister = ASTUtil.create( factory, 11, "sister" );
+ brother.setNextSibling( sister );
+ AST uncle = factory.make( new AST[]{
+ factory.create( 20, "uncle" ),
+ factory.create( 21, "cousin1" ),
+ factory.create( 22, "cousin2" ),
+ factory.create( 23, "cousin3" )} );
+ parent.setNextSibling( uncle );
+ System.out.println( t.toStringTree() );
+
+ System.out.println( "--- ASTParentsFirstIterator ---" );
+ ASTParentsFirstIterator iter = new ASTParentsFirstIterator( t );
+ int count = 0;
+ while ( iter.hasNext() ) {
+ AST n = iter.nextNode();
+ count++;
+ System.out.println( n );
+ }
+ assertEquals( 10, count );
+
+ System.out.println( "--- ASTIterator ---" );
+ ASTIterator iter2 = new ASTIterator( t );
+ int count2 = 0;
+ while ( iter2.hasNext() ) {
+ AST n = iter2.nextNode();
+ count2++;
+ System.out.println( n );
+ }
+ assertEquals( 10, count2 );
+
+ System.out.println( "--- ASTParentsFirstIterator (parent) ---" );
+ ASTParentsFirstIterator iter3 = new ASTParentsFirstIterator( parent );
+ int count3 = 0;
+ while ( iter3.hasNext() ) {
+ AST n = iter3.nextNode();
+ count3++;
+ System.out.println( n );
+ }
+ assertEquals( 5, count3 );
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ast/ASTUtilTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ast/ASTUtilTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ast/ASTUtilTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,77 @@
+// $Id: ASTUtilTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.ast;
+
+import antlr.ASTFactory;
+import antlr.collections.AST;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.hibernate.hql.ast.util.ASTUtil;
+import org.hibernate.junit.UnitTestCase;
+
+/**
+ * Unit test for ASTUtil.
+ */
+public class ASTUtilTest extends UnitTestCase {
+ private ASTFactory factory;
+
+ /**
+ * Standard JUnit test case constructor.
+ *
+ * @param name The name of the test case.
+ */
+ public ASTUtilTest(String name) {
+ super( name );
+ }
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ factory = new ASTFactory();
+ }
+
+ public void testCreate() throws Exception {
+ AST n = ASTUtil.create( factory, 1, "one");
+ assertNull( n.getFirstChild() );
+ assertEquals("one",n.getText());
+ assertEquals(1,n.getType());
+ }
+ /**
+ * Test adding a tree of children.
+ */
+ public void testCreateTree() throws Exception {
+ AST[] tree = new AST[4];
+ AST grandparent = tree[0] = ASTUtil.create(factory, 1, "grandparent");
+ AST parent = tree[1] = ASTUtil.create(factory,2,"parent");
+ AST child = tree[2] = ASTUtil.create(factory,3,"child");
+ AST baby = tree[3] = ASTUtil.create(factory,4,"baby");
+ AST t = ASTUtil.createTree( factory, tree);
+ assertSame(t,grandparent);
+ assertSame(parent,t.getFirstChild());
+ assertSame(child,t.getFirstChild().getFirstChild());
+ assertSame(baby,t.getFirstChild().getFirstChild().getFirstChild());
+ }
+
+ public void testFindPreviousSibling() throws Exception {
+ AST child1 = ASTUtil.create(factory,2, "child1");
+ AST child2 = ASTUtil.create(factory,3, "child2");
+ AST n = factory.make( new AST[] {
+ ASTUtil.create(factory, 1, "parent"),
+ child1,
+ child2,
+ });
+ assertSame(child1,ASTUtil.findPreviousSibling( n,child2));
+ Exception e = null;
+ try {
+ ASTUtil.findPreviousSibling(child1,null);
+ }
+ catch (Exception x) {
+ e = x;
+ }
+ assertNotNull(e);
+ }
+
+ public static Test suite() {
+ return new TestSuite( ASTUtilTest.class );
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/batch/BatchTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/batch/BatchTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/batch/BatchTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,89 @@
+//$Id: BatchTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.batch;
+
+import java.math.BigDecimal;
+
+import junit.framework.Test;
+
+import org.hibernate.CacheMode;
+import org.hibernate.ScrollMode;
+import org.hibernate.ScrollableResults;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+
+/**
+ * This is how to do batch processing in Hibernate.
+ * Remember to enable JDBC batch updates, or this
+ * test will take a Very Long Time!
+ *
+ * @author Gavin King
+ */
+public class BatchTest extends FunctionalTestCase {
+
+ public BatchTest(String str) {
+ super( str );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "batch/DataPoint.hbm.xml" };
+ }
+
+ public String getCacheConcurrencyStrategy() {
+ return null;
+ }
+
+ public void configure(Configuration cfg) {
+ cfg.setProperty( Environment.STATEMENT_BATCH_SIZE, "20" );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( BatchTest.class );
+ }
+
+ public void testBatchInsertUpdate() {
+ long start = System.currentTimeMillis();
+ final int N = 5000; //26 secs with batch flush, 26 without
+ //final int N = 100000; //53 secs with batch flush, OOME without
+ //final int N = 250000; //137 secs with batch flush, OOME without
+
+ Session s = openSession();
+ s.setCacheMode( CacheMode.IGNORE );
+ Transaction t = s.beginTransaction();
+ for ( int i = 0; i < N; i++ ) {
+ DataPoint dp = new DataPoint();
+ dp.setX( new BigDecimal( i * 0.1d ).setScale( 19, BigDecimal.ROUND_DOWN ) );
+ dp.setY( new BigDecimal( Math.cos( dp.getX().doubleValue() ) ).setScale( 19, BigDecimal.ROUND_DOWN ) );
+ s.save( dp );
+ if ( i % 20 == 0 ) {
+ s.flush();
+ s.clear();
+ }
+ }
+ t.commit();
+ s.close();
+
+ s = openSession();
+ s.setCacheMode( CacheMode.IGNORE );
+ t = s.beginTransaction();
+ int i = 0;
+ ScrollableResults sr = s.createQuery( "from DataPoint dp order by dp.x asc" )
+ .scroll( ScrollMode.FORWARD_ONLY );
+ while ( sr.next() ) {
+ DataPoint dp = ( DataPoint ) sr.get( 0 );
+ dp.setDescription( "done!" );
+ if ( ++i % 20 == 0 ) {
+ s.flush();
+ s.clear();
+ }
+ }
+ t.commit();
+ s.close();
+ System.out.println( System.currentTimeMillis() - start );
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/batch/BatchTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/batch/DataPoint.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/batch/DataPoint.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/batch/DataPoint.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping
+ package="org.hibernate.test.batch">
+
+ <class name="DataPoint"
+ dynamic-update="true">
+ <!--rowid="rowid"--> <!-- remove this if not oracle -->
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="x">
+ <column name="xval" not-null="true" length="4" unique-key="xy"/>
+ </property>
+ <property name="y">
+ <column name="yval" not-null="true" length="4" unique-key="xy"/>
+ </property>
+ <property name="description"/>
+ </class>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/batch/DataPoint.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/batch/DataPoint.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/batch/DataPoint.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/batch/DataPoint.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,62 @@
+//$Id: DataPoint.java 4343 2004-08-16 11:46:19Z oneovthafew $
+package org.hibernate.test.batch;
+
+import java.math.BigDecimal;
+
+/**
+ * @author Gavin King
+ */
+public class DataPoint {
+ private long id;
+ private BigDecimal x;
+ private BigDecimal y;
+ private String description;
+ /**
+ * @return Returns the description.
+ */
+ public String getDescription() {
+ return description;
+ }
+ /**
+ * @param description The description to set.
+ */
+ public void setDescription(String description) {
+ this.description = description;
+ }
+ /**
+ * @return Returns the id.
+ */
+ public long getId() {
+ return id;
+ }
+ /**
+ * @param id The id to set.
+ */
+ public void setId(long id) {
+ this.id = id;
+ }
+ /**
+ * @return Returns the x.
+ */
+ public BigDecimal getX() {
+ return x;
+ }
+ /**
+ * @param x The x to set.
+ */
+ public void setX(BigDecimal x) {
+ this.x = x;
+ }
+ /**
+ * @return Returns the y.
+ */
+ public BigDecimal getY() {
+ return y;
+ }
+ /**
+ * @param y The y to set.
+ */
+ public void setY(BigDecimal y) {
+ this.y = y;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/batch/DataPoint.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/batchfetch/BatchFetchTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/batchfetch/BatchFetchTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/batchfetch/BatchFetchTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,122 @@
+//$Id: BatchFetchTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.batchfetch;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class BatchFetchTest extends FunctionalTestCase {
+
+ public BatchFetchTest(String str) {
+ super( str );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "batchfetch/ProductLine.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( BatchFetchTest.class );
+ }
+
+ public void testBatchFetch() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ ProductLine cars = new ProductLine();
+ cars.setDescription( "Cars" );
+ Model monaro = new Model( cars );
+ monaro.setName( "monaro" );
+ monaro.setDescription( "Holden Monaro" );
+ Model hsv = new Model( cars );
+ hsv.setName( "hsv" );
+ hsv.setDescription( "Holden Commodore HSV" );
+ s.save( cars );
+
+ ProductLine oss = new ProductLine();
+ oss.setDescription( "OSS" );
+ Model jboss = new Model( oss );
+ jboss.setName( "JBoss" );
+ jboss.setDescription( "JBoss Application Server" );
+ Model hibernate = new Model( oss );
+ hibernate.setName( "Hibernate" );
+ hibernate.setDescription( "Hibernate" );
+ Model cache = new Model( oss );
+ cache.setName( "JBossCache" );
+ cache.setDescription( "JBoss TreeCache" );
+ s.save( oss );
+
+ t.commit();
+ s.close();
+
+ s.getSessionFactory().evict( Model.class );
+ s.getSessionFactory().evict( ProductLine.class );
+
+ s = openSession();
+ t = s.beginTransaction();
+
+ List list = s.createQuery( "from ProductLine pl order by pl.description" ).list();
+ cars = ( ProductLine ) list.get( 0 );
+ oss = ( ProductLine ) list.get( 1 );
+ assertFalse( Hibernate.isInitialized( cars.getModels() ) );
+ assertFalse( Hibernate.isInitialized( oss.getModels() ) );
+ assertEquals( cars.getModels().size(), 2 ); //fetch both collections
+ assertTrue( Hibernate.isInitialized( cars.getModels() ) );
+ assertTrue( Hibernate.isInitialized( oss.getModels() ) );
+
+ s.clear();
+
+ list = s.createQuery( "from Model m" ).list();
+ hibernate = ( Model ) s.get( Model.class, hibernate.getId() );
+ hibernate.getProductLine().getId();
+ for ( Iterator i = list.iterator(); i.hasNext(); ) {
+ assertFalse( Hibernate.isInitialized( ( ( Model ) i.next() ).getProductLine() ) );
+ }
+ assertEquals( hibernate.getProductLine().getDescription(), "OSS" ); //fetch both productlines
+
+ s.clear();
+
+ Iterator iter = s.createQuery( "from Model" ).iterate();
+ list = new ArrayList();
+ while ( iter.hasNext() ) {
+ list.add( iter.next() );
+ }
+ Model m = ( Model ) list.get( 0 );
+ m.getDescription(); //fetch a batch of 4
+
+ s.clear();
+
+ list = s.createQuery( "from ProductLine" ).list();
+ ProductLine pl = ( ProductLine ) list.get( 0 );
+ ProductLine pl2 = ( ProductLine ) list.get( 1 );
+ s.evict( pl2 );
+ pl.getModels().size(); //fetch just one collection! (how can we write an assertion for that??)
+
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ list = s.createQuery( "from ProductLine pl order by pl.description" ).list();
+ cars = ( ProductLine ) list.get( 0 );
+ oss = ( ProductLine ) list.get( 1 );
+ assertEquals( cars.getModels().size(), 2 );
+ assertEquals( oss.getModels().size(), 3 );
+ s.delete( cars );
+ s.delete( oss );
+ t.commit();
+ s.close();
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/batchfetch/BatchFetchTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/batchfetch/Model.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/batchfetch/Model.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/batchfetch/Model.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,44 @@
+//$Id: Model.java 4460 2004-08-29 12:04:14Z oneovthafew $
+package org.hibernate.test.batchfetch;
+
+/**
+ * @author Gavin King
+ */
+public class Model {
+ private String id;
+ private String name;
+ private String description;
+ private ProductLine productLine;
+
+ Model() {}
+
+ public Model(ProductLine pl) {
+ this.productLine = pl;
+ pl.getModels().add(this);
+ }
+
+ public String getDescription() {
+ return description;
+ }
+ public void setDescription(String description) {
+ this.description = description;
+ }
+ public String getId() {
+ return id;
+ }
+ public void setId(String id) {
+ this.id = id;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public ProductLine getProductLine() {
+ return productLine;
+ }
+ public void setProductLine(ProductLine productLine) {
+ this.productLine = productLine;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/batchfetch/Model.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/batchfetch/ProductLine.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/batchfetch/ProductLine.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/batchfetch/ProductLine.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,61 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.batchfetch">
+
+<!--
+
+ This mapping demonstrates the use of batch fetching
+ for collections and entities.
+
+-->
+
+ <class name="ProductLine"
+ batch-size="64">
+
+ <id name="id"
+ column="productId"
+ length="32">
+ <generator class="uuid.hex"/>
+ </id>
+
+ <property name="description"
+ not-null="true"
+ length="200"/>
+
+ <set name="models"
+ batch-size="64"
+ cascade="all"
+ inverse="true">
+ <key column="productId"/>
+ <one-to-many class="Model"/>
+ </set>
+
+ </class>
+
+ <class name="Model"
+ batch-size="64">
+
+ <id name="id"
+ column="modelId"
+ length="32">
+ <generator class="uuid.hex"/>
+ </id>
+
+ <property name="name"
+ not-null="true"
+ length="25"/>
+
+ <property name="description"
+ not-null="true"
+ length="200"/>
+
+ <many-to-one name="productLine"
+ column="productId"
+ not-null="true"/>
+
+ </class>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/batchfetch/ProductLine.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/batchfetch/ProductLine.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/batchfetch/ProductLine.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/batchfetch/ProductLine.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,34 @@
+//$Id: ProductLine.java 4460 2004-08-29 12:04:14Z oneovthafew $
+package org.hibernate.test.batchfetch;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author Gavin King
+ */
+public class ProductLine {
+
+ private String id;
+ private String description;
+ private Set models = new HashSet();
+
+ public String getDescription() {
+ return description;
+ }
+ public void setDescription(String description) {
+ this.description = description;
+ }
+ public String getId() {
+ return id;
+ }
+ public void setId(String id) {
+ this.id = id;
+ }
+ public Set getModels() {
+ return models;
+ }
+ public void setModels(Set models) {
+ this.models = models;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/batchfetch/ProductLine.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bidi/Auction.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bidi/Auction.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bidi/Auction.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,45 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping
+ package="org.hibernate.test.bidi">
+
+ <class name="Auction" table="TAuction">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="description"/>
+ <property name="end" column="endDatetime"/>
+ <bag name="bids" inverse="true"
+ cascade="persist">
+ <key column="auctionId"/>
+ <one-to-many class="Bid"/>
+ </bag>
+ <one-to-one name="successfulBid"
+ property-ref="abc">
+ <formula>id</formula>
+ <formula>1</formula>
+ </one-to-one>
+ </class>
+
+ <class name="Bid" table="TBid">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="amount"
+ scale="19"
+ precision="31" />
+ <property name="datetime"
+ column="createdDatetime"/>
+ <properties name="abc">
+ <many-to-one name="item"
+ column="auctionId"
+ cascade="persist"/>
+ <property name="successful"
+ column="success"/>
+ </properties>
+ </class>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bidi/Auction.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bidi/Auction.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bidi/Auction.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bidi/Auction.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,48 @@
+//$Id: Auction.java 5733 2005-02-14 15:56:06Z oneovthafew $
+package org.hibernate.test.bidi;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author Gavin King
+ */
+public class Auction {
+ private Long id;
+ private String description;
+ private List bids = new ArrayList();
+ private Bid successfulBid;
+ private Date end;
+
+ public Date getEnd() {
+ return end;
+ }
+ public void setEnd(Date end) {
+ this.end = end;
+ }
+ public List getBids() {
+ return bids;
+ }
+ public void setBids(List bids) {
+ this.bids = bids;
+ }
+ public String getDescription() {
+ return description;
+ }
+ public void setDescription(String description) {
+ this.description = description;
+ }
+ public Long getId() {
+ return id;
+ }
+ public void setId(Long id) {
+ this.id = id;
+ }
+ public Bid getSuccessfulBid() {
+ return successfulBid;
+ }
+ public void setSuccessfulBid(Bid successfulBid) {
+ this.successfulBid = successfulBid;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bidi/Auction.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bidi/Auction2.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bidi/Auction2.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bidi/Auction2.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping
+ package="org.hibernate.test.bidi">
+
+ <class name="Auction" table="TAuction2">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="description"/>
+ <property name="end" column="endDatetime"/>
+ <bag name="bids" inverse="true"
+ cascade="persist">
+ <key column="auctionId"/>
+ <one-to-many class="Bid"/>
+ </bag>
+ <many-to-one name="successfulBid"/>
+ </class>
+
+ <class name="Bid" table="TBid2">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="amount" scale="19" precision="31"/>
+ <property name="datetime"
+ column="createdDatetime"/>
+ <many-to-one name="item"
+ column="auctionId"
+ cascade="persist"/>
+ <property name="successful">
+ <formula>exists(select a.id from TAuction2 a where a.successfulBid=id)</formula>
+ </property>
+ </class>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bidi/Auction2.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bidi/AuctionTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bidi/AuctionTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bidi/AuctionTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,119 @@
+//$Id: AuctionTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.bidi;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.dialect.PostgreSQLDialect;
+
+/**
+ * @author Gavin King
+ */
+public class AuctionTest extends FunctionalTestCase {
+
+ public AuctionTest(String str) {
+ super( str );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "bidi/Auction.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( AuctionTest.class );
+ }
+
+ public void testLazy() {
+ if ( getDialect() instanceof PostgreSQLDialect ) {
+ return; //doesn't like boolean=1
+ }
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Auction a = new Auction();
+ a.setDescription( "an auction for something" );
+ a.setEnd( new Date() );
+ Bid b = new Bid();
+ b.setAmount( new BigDecimal( 123.34 ).setScale( 19, BigDecimal.ROUND_DOWN ) );
+ b.setSuccessful( true );
+ b.setDatetime( new Date() );
+ b.setItem( a );
+ a.getBids().add( b );
+ a.setSuccessfulBid( b );
+ s.persist( b );
+ t.commit();
+ s.close();
+
+ Long aid = a.getId();
+ Long bid = b.getId();
+
+ s = openSession();
+ t = s.beginTransaction();
+ b = ( Bid ) s.load( Bid.class, bid );
+ assertFalse( Hibernate.isInitialized( b ) );
+ a = ( Auction ) s.get( Auction.class, aid );
+ assertFalse( Hibernate.isInitialized( a.getBids() ) );
+ assertTrue( Hibernate.isInitialized( a.getSuccessfulBid() ) );
+ assertSame( a.getBids().iterator().next(), b );
+ assertSame( b, a.getSuccessfulBid() );
+ assertTrue( Hibernate.isInitialized( b ) );
+ assertTrue( b.isSuccessful() );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ b = ( Bid ) s.load( Bid.class, bid );
+ assertFalse( Hibernate.isInitialized( b ) );
+ a = ( Auction ) s.createQuery( "from Auction a left join fetch a.bids" ).uniqueResult();
+ assertTrue( Hibernate.isInitialized( b ) );
+ assertTrue( Hibernate.isInitialized( a.getBids() ) );
+ assertSame( b, a.getSuccessfulBid() );
+ assertSame( a.getBids().iterator().next(), b );
+ assertTrue( b.isSuccessful() );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ b = ( Bid ) s.load( Bid.class, bid );
+ a = ( Auction ) s.load( Auction.class, aid );
+ assertFalse( Hibernate.isInitialized( b ) );
+ assertFalse( Hibernate.isInitialized( a ) );
+ s.createQuery( "from Auction a left join fetch a.successfulBid" ).list();
+ assertTrue( Hibernate.isInitialized( b ) );
+ assertTrue( Hibernate.isInitialized( a ) );
+ assertSame( b, a.getSuccessfulBid() );
+ assertFalse( Hibernate.isInitialized( a.getBids() ) );
+ assertSame( a.getBids().iterator().next(), b );
+ assertTrue( b.isSuccessful() );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ b = ( Bid ) s.load( Bid.class, bid );
+ a = ( Auction ) s.load( Auction.class, aid );
+ assertFalse( Hibernate.isInitialized( b ) );
+ assertFalse( Hibernate.isInitialized( a ) );
+ assertSame( s.get( Bid.class, bid ), b );
+ assertTrue( Hibernate.isInitialized( b ) );
+ assertSame( s.get( Auction.class, aid ), a );
+ assertTrue( Hibernate.isInitialized( a ) );
+ assertSame( b, a.getSuccessfulBid() );
+ assertFalse( Hibernate.isInitialized( a.getBids() ) );
+ assertSame( a.getBids().iterator().next(), b );
+ assertTrue( b.isSuccessful() );
+ t.commit();
+ s.close();
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bidi/AuctionTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bidi/AuctionTest2.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bidi/AuctionTest2.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bidi/AuctionTest2.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,122 @@
+//$Id: AuctionTest2.java 10981 2006-12-13 00:14:17Z steve.ebersole at jboss.com $
+package org.hibernate.test.bidi;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+import junit.framework.Test;
+
+/**
+ * @author Gavin King
+ */
+public class AuctionTest2 extends FunctionalTestCase {
+
+ public AuctionTest2(String str) {
+ super( str );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "bidi/Auction2.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( AuctionTest2.class );
+ }
+
+ public boolean createSchema() {
+ return getDialect().supportsExistsInSelect();
+ }
+
+ public void testLazy() {
+ if ( ! getDialect().supportsExistsInSelect() ) {
+ reportSkip( "dialect does not support exist fragments in the select clause", "bidi support" );
+ return;
+ }
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Auction a = new Auction();
+ a.setDescription( "an auction for something" );
+ a.setEnd( new Date() );
+ Bid b = new Bid();
+ b.setAmount( new BigDecimal( 123.34 ).setScale( 19, BigDecimal.ROUND_DOWN ) );
+ b.setSuccessful( true );
+ b.setDatetime( new Date() );
+ b.setItem( a );
+ a.getBids().add( b );
+ a.setSuccessfulBid( b );
+ s.persist( b );
+ t.commit();
+ s.close();
+
+ Long aid = a.getId();
+ Long bid = b.getId();
+
+ s = openSession();
+ t = s.beginTransaction();
+ b = ( Bid ) s.load( Bid.class, bid );
+ assertFalse( Hibernate.isInitialized( b ) );
+ a = ( Auction ) s.get( Auction.class, aid );
+ assertFalse( Hibernate.isInitialized( a.getBids() ) );
+ assertFalse( Hibernate.isInitialized( a.getSuccessfulBid() ) );
+ assertSame( a.getBids().iterator().next(), b );
+ assertSame( b, a.getSuccessfulBid() );
+ assertTrue( Hibernate.isInitialized( b ) );
+ assertTrue( b.isSuccessful() );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ b = ( Bid ) s.load( Bid.class, bid );
+ assertFalse( Hibernate.isInitialized( b ) );
+ a = ( Auction ) s.createQuery( "from Auction a left join fetch a.bids" ).uniqueResult();
+ assertTrue( Hibernate.isInitialized( b ) );
+ assertTrue( Hibernate.isInitialized( a.getBids() ) );
+ assertSame( b, a.getSuccessfulBid() );
+ assertSame( a.getBids().iterator().next(), b );
+ assertTrue( b.isSuccessful() );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ b = ( Bid ) s.load( Bid.class, bid );
+ a = ( Auction ) s.load( Auction.class, aid );
+ assertFalse( Hibernate.isInitialized( b ) );
+ assertFalse( Hibernate.isInitialized( a ) );
+ s.createQuery( "from Auction a left join fetch a.successfulBid" ).list();
+ assertTrue( Hibernate.isInitialized( b ) );
+ assertTrue( Hibernate.isInitialized( a ) );
+ assertSame( b, a.getSuccessfulBid() );
+ assertFalse( Hibernate.isInitialized( a.getBids() ) );
+ assertSame( a.getBids().iterator().next(), b );
+ assertTrue( b.isSuccessful() );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ b = ( Bid ) s.load( Bid.class, bid );
+ a = ( Auction ) s.load( Auction.class, aid );
+ assertFalse( Hibernate.isInitialized( b ) );
+ assertFalse( Hibernate.isInitialized( a ) );
+ assertSame( s.get( Bid.class, bid ), b );
+ assertTrue( Hibernate.isInitialized( b ) );
+ assertSame( s.get( Auction.class, aid ), a );
+ assertTrue( Hibernate.isInitialized( a ) );
+ assertSame( b, a.getSuccessfulBid() );
+ assertFalse( Hibernate.isInitialized( a.getBids() ) );
+ assertSame( a.getBids().iterator().next(), b );
+ assertTrue( b.isSuccessful() );
+ t.commit();
+ s.close();
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bidi/AuctionTest2.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bidi/Bid.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bidi/Bid.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bidi/Bid.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,47 @@
+//$Id: Bid.java 5733 2005-02-14 15:56:06Z oneovthafew $
+package org.hibernate.test.bidi;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * @author Gavin King
+ */
+public class Bid {
+ private Long id;
+ private Auction item;
+ private BigDecimal amount;
+ private boolean successful;
+ private Date datetime;
+
+ public Long getId() {
+ return id;
+ }
+ public void setId(Long id) {
+ this.id = id;
+ }
+ public BigDecimal getAmount() {
+ return amount;
+ }
+ public void setAmount(BigDecimal amount) {
+ this.amount = amount;
+ }
+ public Auction getItem() {
+ return item;
+ }
+ public void setItem(Auction item) {
+ this.item = item;
+ }
+ public boolean isSuccessful() {
+ return successful;
+ }
+ public void setSuccessful(boolean successful) {
+ this.successful = successful;
+ }
+ public Date getDatetime() {
+ return datetime;
+ }
+ public void setDatetime(Date datetime) {
+ this.datetime = datetime;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bidi/Bid.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/Bean.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/Bean.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/Bean.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -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">
+
+<hibernate-mapping package="org.hibernate.test.bytecode">
+
+ <class name="Bean">
+ <id name="someString" type="string">
+ <generator class="assigned"/>
+ </id>
+ <property name="someLong" type="long" column="S_LONG_1" />
+ <property name="someInteger" type="int" column="S_INT_1" />
+ <property name="someDate" type="timestamp" column="S_DATE_1" />
+ <property name="somelong" type="long" column="S_LONG_2" />
+ <property name="someint" type="int" column="S_INT_2" />
+ <property name="someObject" type="serializable" column="S_OBJ_1" />
+ </class>
+
+
+ <class name="ProxyBean" lazy="true">
+ <id name="someString" type="string">
+ <generator class="assigned"/>
+ </id>
+ <property name="someLong" type="long" column="S_LONG_1" />
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/Bean.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/Bean.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/Bean.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,78 @@
+package org.hibernate.test.bytecode;
+
+import java.util.Date;
+import java.text.ParseException;
+
+/**
+ * @author Steve Ebersole
+ */
+public class Bean {
+ private String someString;
+ private Long someLong;
+ private Integer someInteger;
+ private Date someDate;
+ private long somelong;
+ private int someint;
+ private Object someObject;
+
+
+ public String getSomeString() {
+ return someString;
+ }
+
+ public void setSomeString(String someString) {
+ this.someString = someString;
+ }
+
+ public Long getSomeLong() {
+ return someLong;
+ }
+
+ public void setSomeLong(Long someLong) {
+ this.someLong = someLong;
+ }
+
+ public Integer getSomeInteger() {
+ return someInteger;
+ }
+
+ public void setSomeInteger(Integer someInteger) {
+ this.someInteger = someInteger;
+ }
+
+ public Date getSomeDate() {
+ return someDate;
+ }
+
+ public void setSomeDate(Date someDate) {
+ this.someDate = someDate;
+ }
+
+ public long getSomelong() {
+ return somelong;
+ }
+
+ public void setSomelong(long somelong) {
+ this.somelong = somelong;
+ }
+
+ public int getSomeint() {
+ return someint;
+ }
+
+ public void setSomeint(int someint) {
+ this.someint = someint;
+ }
+
+ public Object getSomeObject() {
+ return someObject;
+ }
+
+ public void setSomeObject(Object someObject) {
+ this.someObject = someObject;
+ }
+
+ public void throwException() throws ParseException {
+ throw new ParseException( "you asked for it...", 0 );
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/BeanReflectionHelper.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/BeanReflectionHelper.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/BeanReflectionHelper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,78 @@
+package org.hibernate.test.bytecode;
+
+import org.hibernate.property.BasicPropertyAccessor;
+import org.hibernate.property.Getter;
+import org.hibernate.property.Setter;
+
+import java.util.Date;
+
+/**
+ * @author Steve Ebersole
+ */
+public class BeanReflectionHelper {
+
+ public static final Object[] TEST_VALUES = new Object[] {
+ "hello", new Long(1), new Integer(1), new Date(), new Long(1), new Integer(1), new Object()
+ };
+
+ private static final String[] getterNames = new String[7];
+ private static final String[] setterNames = new String[7];
+ private static final Class[] types = new Class[7];
+
+ static {
+ BasicPropertyAccessor propertyAccessor = new BasicPropertyAccessor();
+ Getter getter = propertyAccessor.getGetter( Bean.class, "someString" );
+ Setter setter = propertyAccessor.getSetter( Bean.class, "someString" );
+ getterNames[0] = getter.getMethodName();
+ types[0] = getter.getReturnType();
+ setterNames[0] = setter.getMethodName();
+
+ getter = propertyAccessor.getGetter( Bean.class, "someLong" );
+ setter = propertyAccessor.getSetter( Bean.class, "someLong" );
+ getterNames[1] = getter.getMethodName();
+ types[1] = getter.getReturnType();
+ setterNames[1] = setter.getMethodName();
+
+ getter = propertyAccessor.getGetter( Bean.class, "someInteger" );
+ setter = propertyAccessor.getSetter( Bean.class, "someInteger" );
+ getterNames[2] = getter.getMethodName();
+ types[2] = getter.getReturnType();
+ setterNames[2] = setter.getMethodName();
+
+ getter = propertyAccessor.getGetter( Bean.class, "someDate" );
+ setter = propertyAccessor.getSetter( Bean.class, "someDate" );
+ getterNames[3] = getter.getMethodName();
+ types[3] = getter.getReturnType();
+ setterNames[3] = setter.getMethodName();
+
+ getter = propertyAccessor.getGetter( Bean.class, "somelong" );
+ setter = propertyAccessor.getSetter( Bean.class, "somelong" );
+ getterNames[4] = getter.getMethodName();
+ types[4] = getter.getReturnType();
+ setterNames[4] = setter.getMethodName();
+
+ getter = propertyAccessor.getGetter( Bean.class, "someint" );
+ setter = propertyAccessor.getSetter( Bean.class, "someint" );
+ getterNames[5] = getter.getMethodName();
+ types[5] = getter.getReturnType();
+ setterNames[5] = setter.getMethodName();
+
+ getter = propertyAccessor.getGetter( Bean.class, "someObject" );
+ setter = propertyAccessor.getSetter( Bean.class, "someObject" );
+ getterNames[6] = getter.getMethodName();
+ types[6] = getter.getReturnType();
+ setterNames[6] = setter.getMethodName();
+ }
+
+ public static String[] getGetterNames() {
+ return getterNames;
+ }
+
+ public static String[] getSetterNames() {
+ return setterNames;
+ }
+
+ public static Class[] getTypes() {
+ return types;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/BytecodeSuite.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/BytecodeSuite.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/BytecodeSuite.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,21 @@
+package org.hibernate.test.bytecode;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * todo: describe BytecodeSuite
+ *
+ * @author Steve Ebersole
+ */
+public class BytecodeSuite {
+ public static Test suite() {
+ TestSuite suite = new TestSuite( "BytecodeProvider tests" );
+ suite.addTest( org.hibernate.test.bytecode.cglib.ReflectionOptimizerTest.suite() );
+ suite.addTest( org.hibernate.test.bytecode.cglib.InvocationTargetExceptionTest.suite() );
+ suite.addTest( org.hibernate.test.bytecode.cglib.CGLIBThreadLocalTest.suite() );
+ suite.addTest( org.hibernate.test.bytecode.javassist.ReflectionOptimizerTest.suite() );
+ suite.addTest( org.hibernate.test.bytecode.javassist.InvocationTargetExceptionTest.suite() );
+ return suite;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/ProxyBean.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/ProxyBean.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/ProxyBean.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,31 @@
+package org.hibernate.test.bytecode;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: Paul
+ * Date: Mar 9, 2007
+ * Time: 11:31:40 AM
+ * To change this template use File | Settings | File Templates.
+ */
+public class ProxyBean {
+ private String someString;
+ private long someLong;
+
+
+ public String getSomeString() {
+ return someString;
+ }
+
+ public void setSomeString(String someString) {
+ this.someString = someString;
+ }
+
+
+ public long getSomeLong() {
+ return someLong;
+ }
+
+ public void setSomeLong(long someLong) {
+ this.someLong = someLong;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/cglib/CGLIBThreadLocalTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/cglib/CGLIBThreadLocalTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/cglib/CGLIBThreadLocalTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,73 @@
+package org.hibernate.test.bytecode.cglib;
+
+import org.hibernate.junit.functional.*;
+import org.hibernate.cfg.*;
+import org.hibernate.*;
+import org.hibernate.proxy.*;
+import org.hibernate.test.bytecode.*;
+import junit.framework.*;
+
+import java.text.*;
+import java.lang.reflect.*;
+
+import net.sf.cglib.proxy.*;
+
+/**
+ * Test that the static thread local callback object is cleared out of the proxy class after instantiated.
+ * This tests that the memory leak reported by HHH-2481 hasn't been re-introduced.
+ *
+ * @author Paul Malolepsy
+ */
+public class CGLIBThreadLocalTest extends FunctionalTestCase {
+ public CGLIBThreadLocalTest(String name) {
+ super(name);
+ }
+
+ public String[] getMappings() {
+ return new String[]{"bytecode/Bean.hbm.xml"};
+ }
+
+ public static TestSuite suite() {
+ return new FunctionalTestClassTestSuite(CGLIBThreadLocalTest.class);
+ }
+
+ public void testCglibClearing() {
+ if (!(Environment.getBytecodeProvider() instanceof org.hibernate.bytecode.cglib.BytecodeProviderImpl)) {
+ // because of the scoping :(
+ reportSkip("env not configured for cglib provider", "cglib thread local callback clearing");
+ return;
+ }
+
+ //create the object for the test
+ Session s = openSession();
+ s.beginTransaction();
+ ProxyBean proxyBean = new ProxyBean();
+ proxyBean.setSomeString("my-bean");
+ proxyBean.setSomeLong(1234);
+ s.save(proxyBean);
+ s.getTransaction().commit();
+ s.close();
+
+ // read the object as a proxy
+ s = openSession();
+ s.beginTransaction();
+ proxyBean = (ProxyBean) s.load(ProxyBean.class, proxyBean.getSomeString());
+ assertTrue(proxyBean instanceof HibernateProxy);
+ try {
+ //check that the static thread callbacks thread local has been cleared out
+ Field field = proxyBean.getClass().getDeclaredField("CGLIB$THREAD_CALLBACKS");
+ field.setAccessible(true);
+ ThreadLocal threadCallbacksThreadLocal = (ThreadLocal) field.get(null);
+ assertTrue(threadCallbacksThreadLocal.get() == null);
+ } catch (NoSuchFieldException e1) {
+ fail("unable to find CGLIB$THREAD_CALLBACKS field in proxy.");
+ } catch (Throwable t) {
+ fail("unexpected exception type : " + t);
+ } finally {
+ //clean up
+ s.delete(proxyBean);
+ s.getTransaction().commit();
+ s.close();
+ }
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/cglib/InvocationTargetExceptionTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/cglib/InvocationTargetExceptionTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/cglib/InvocationTargetExceptionTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,67 @@
+package org.hibernate.test.bytecode.cglib;
+
+import org.hibernate.test.TestCase;
+import org.hibernate.test.bytecode.Bean;
+import org.hibernate.Session;
+import org.hibernate.Hibernate;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.cfg.Environment;
+import junit.framework.TestSuite;
+
+import java.text.ParseException;
+
+/**
+ * Test that the Javassist-based lazy initializer properly handles
+ * InvocationTargetExceptions
+ *
+ * @author Steve Ebersole
+ */
+public class InvocationTargetExceptionTest extends FunctionalTestCase {
+ public InvocationTargetExceptionTest(String name) {
+ super( name );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "bytecode/Bean.hbm.xml" };
+ }
+
+ public static TestSuite suite() {
+ return new FunctionalTestClassTestSuite( InvocationTargetExceptionTest.class );
+ }
+
+ public void testProxiedInvocationException() {
+ if ( ! ( Environment.getBytecodeProvider() instanceof org.hibernate.bytecode.cglib.BytecodeProviderImpl ) ) {
+ // because of the scoping :(
+ reportSkip( "env not configured for cglib provider", "bytecode-provider InvocationTargetException handling" );
+ return;
+ }
+ Session s = openSession();
+ s.beginTransaction();
+ Bean bean = new Bean();
+ bean.setSomeString( "my-bean" );
+ s.save( bean );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ bean = ( Bean ) s.load( Bean.class, bean.getSomeString() );
+ assertFalse( Hibernate.isInitialized( bean ) );
+ try {
+ bean.throwException();
+ fail( "exception not thrown" );
+ }
+ catch ( ParseException e ) {
+ // expected behavior
+ }
+ catch( Throwable t ) {
+ fail( "unexpected exception type : " + t );
+ }
+
+ s.delete( bean );
+ s.getTransaction().commit();
+ s.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/cglib/ReflectionOptimizerTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/cglib/ReflectionOptimizerTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/cglib/ReflectionOptimizerTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,52 @@
+package org.hibernate.test.bytecode.cglib;
+
+import junit.framework.TestSuite;
+
+import org.hibernate.bytecode.ReflectionOptimizer;
+import org.hibernate.bytecode.cglib.BytecodeProviderImpl;
+import org.hibernate.junit.UnitTestCase;
+import org.hibernate.test.bytecode.Bean;
+import org.hibernate.test.bytecode.BeanReflectionHelper;
+
+/**
+ * @author Steve Ebersole
+ */
+public class ReflectionOptimizerTest extends UnitTestCase {
+
+ public ReflectionOptimizerTest(String string) {
+ super( string );
+ }
+
+ public void testReflectionOptimization() {
+ BytecodeProviderImpl provider = new BytecodeProviderImpl();
+ ReflectionOptimizer optimizer = provider.getReflectionOptimizer(
+ Bean.class,
+ BeanReflectionHelper.getGetterNames(),
+ BeanReflectionHelper.getSetterNames(),
+ BeanReflectionHelper.getTypes()
+ );
+ assertNotNull( optimizer );
+ assertNotNull( optimizer.getInstantiationOptimizer() );
+ assertNotNull( optimizer.getAccessOptimizer() );
+
+ Object instance = optimizer.getInstantiationOptimizer().newInstance();
+ assertEquals( instance.getClass(), Bean.class );
+ Bean bean = ( Bean ) instance;
+
+ optimizer.getAccessOptimizer().setPropertyValues( bean, BeanReflectionHelper.TEST_VALUES );
+ assertEquals( bean.getSomeString(), BeanReflectionHelper.TEST_VALUES[0] );
+ Object[] values = optimizer.getAccessOptimizer().getPropertyValues( bean );
+ assertEquivalent( values, BeanReflectionHelper.TEST_VALUES );
+ }
+
+ private void assertEquivalent(Object[] checkValues, Object[] values) {
+ assertEquals( "Different lengths", checkValues.length, values.length );
+ for ( int i = 0; i < checkValues.length; i++ ) {
+ assertEquals( "different values at index [" + i + "]", checkValues[i], values[i] );
+ }
+ }
+
+ public static TestSuite suite() {
+ return new TestSuite( ReflectionOptimizerTest.class );
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/javassist/InvocationTargetExceptionTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/javassist/InvocationTargetExceptionTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/javassist/InvocationTargetExceptionTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,68 @@
+package org.hibernate.test.bytecode.javassist;
+
+import java.text.ParseException;
+
+import junit.framework.TestSuite;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.cfg.Environment;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.test.bytecode.Bean;
+
+/**
+ * Test that the Javassist-based lazy initializer properly handles
+ * InvocationTargetExceptions
+ *
+ * @author Steve Ebersole
+ */
+public class InvocationTargetExceptionTest extends FunctionalTestCase {
+ public InvocationTargetExceptionTest(String name) {
+ super( name );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "bytecode/Bean.hbm.xml" };
+ }
+
+ public static TestSuite suite() {
+ return new FunctionalTestClassTestSuite( InvocationTargetExceptionTest.class );
+ }
+
+ public void testProxiedInvocationException() {
+ if ( !( Environment.getBytecodeProvider() instanceof org.hibernate.bytecode.javassist.BytecodeProviderImpl ) ) {
+ // because of the scoping :(
+ reportSkip(
+ "env not configured for javassist provider", "bytecode-provider InvocationTargetException handling"
+ );
+ return;
+ }
+ Session s = openSession();
+ s.beginTransaction();
+ Bean bean = new Bean();
+ bean.setSomeString( "my-bean" );
+ s.save( bean );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ bean = ( Bean ) s.load( Bean.class, bean.getSomeString() );
+ assertFalse( Hibernate.isInitialized( bean ) );
+ try {
+ bean.throwException();
+ fail( "exception not thrown" );
+ }
+ catch ( ParseException e ) {
+ // expected behavior
+ }
+ catch ( Throwable t ) {
+ fail( "unexpected exception type : " + t );
+ }
+
+ s.delete( bean );
+ s.getTransaction().commit();
+ s.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/javassist/ReflectionOptimizerTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/javassist/ReflectionOptimizerTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/bytecode/javassist/ReflectionOptimizerTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,52 @@
+package org.hibernate.test.bytecode.javassist;
+
+import junit.framework.TestSuite;
+
+import org.hibernate.bytecode.ReflectionOptimizer;
+import org.hibernate.bytecode.javassist.BytecodeProviderImpl;
+import org.hibernate.junit.UnitTestCase;
+import org.hibernate.test.bytecode.Bean;
+import org.hibernate.test.bytecode.BeanReflectionHelper;
+
+/**
+ * @author Steve Ebersole
+ */
+public class ReflectionOptimizerTest extends UnitTestCase {
+
+ public ReflectionOptimizerTest(String string) {
+ super( string );
+ }
+
+ public void testReflectionOptimization() {
+ BytecodeProviderImpl provider = new BytecodeProviderImpl();
+ ReflectionOptimizer optimizer = provider.getReflectionOptimizer(
+ Bean.class,
+ BeanReflectionHelper.getGetterNames(),
+ BeanReflectionHelper.getSetterNames(),
+ BeanReflectionHelper.getTypes()
+ );
+ assertNotNull( optimizer );
+ assertNotNull( optimizer.getInstantiationOptimizer() );
+ assertNotNull( optimizer.getAccessOptimizer() );
+
+ Object instance = optimizer.getInstantiationOptimizer().newInstance();
+ assertEquals( instance.getClass(), Bean.class );
+ Bean bean = ( Bean ) instance;
+
+ optimizer.getAccessOptimizer().setPropertyValues( bean, BeanReflectionHelper.TEST_VALUES );
+ assertEquals( bean.getSomeString(), BeanReflectionHelper.TEST_VALUES[0] );
+ Object[] values = optimizer.getAccessOptimizer().getPropertyValues( bean );
+ assertEquivalent( values, BeanReflectionHelper.TEST_VALUES );
+ }
+
+ private void assertEquivalent(Object[] checkValues, Object[] values) {
+ assertEquals( "Different lengths", checkValues.length, values.length );
+ for ( int i = 0; i < checkValues.length; i++ ) {
+ assertEquals( "different values at index [" + i + "]", checkValues[i], values[i] );
+ }
+ }
+
+ public static TestSuite suite() {
+ return new TestSuite( ReflectionOptimizerTest.class );
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/BaseCacheProviderTestCase.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/BaseCacheProviderTestCase.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/BaseCacheProviderTestCase.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,216 @@
+package org.hibernate.test.cache;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.cache.ReadWriteCache;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.stat.SecondLevelCacheStatistics;
+import org.hibernate.stat.Statistics;
+import org.hibernate.test.tm.DummyConnectionProvider;
+import org.hibernate.test.tm.DummyTransactionManagerLookup;
+import org.hibernate.transaction.JDBCTransactionFactory;
+
+/**
+ * Common requirement testing for each {@link org.hibernate.cache.CacheProvider} impl.
+ *
+ * @author Steve Ebersole
+ */
+public abstract class BaseCacheProviderTestCase extends FunctionalTestCase {
+
+ // note that a lot of the fucntionality here is intended to be used
+ // in creating specific tests for each CacheProvider that would extend
+ // from a base test case (this) for common requirement testing...
+
+ public BaseCacheProviderTestCase(String x) {
+ super( x );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "cache/Item.hbm.xml" };
+ }
+
+ public void configure(Configuration cfg) {
+ super.configure( cfg );
+ cfg.setProperty( Environment.CACHE_REGION_PREFIX, "" );
+ cfg.setProperty( Environment.USE_SECOND_LEVEL_CACHE, "true" );
+ cfg.setProperty( Environment.GENERATE_STATISTICS, "true" );
+ cfg.setProperty( Environment.USE_STRUCTURED_CACHE, "true" );
+ cfg.setProperty( Environment.CACHE_PROVIDER, getCacheProvider().getName() );
+
+ if ( getConfigResourceKey() != null ) {
+ cfg.setProperty( getConfigResourceKey(), getConfigResourceLocation() );
+ }
+
+ if ( useTransactionManager() ) {
+ cfg.setProperty( Environment.CONNECTION_PROVIDER, DummyConnectionProvider.class.getName() );
+ cfg.setProperty( Environment.TRANSACTION_MANAGER_STRATEGY, DummyTransactionManagerLookup.class.getName() );
+ }
+ else {
+ cfg.setProperty( Environment.TRANSACTION_STRATEGY, JDBCTransactionFactory.class.getName() );
+ }
+ }
+
+ /**
+ * The cache provider to be tested.
+ *
+ * @return The cache provider.
+ */
+ protected abstract Class getCacheProvider();
+
+ /**
+ * For provider-specific configuration, the name of the property key the
+ * provider expects.
+ *
+ * @return The provider-specific config key.
+ */
+ protected abstract String getConfigResourceKey();
+
+ /**
+ * For provider-specific configuration, the resource location of that
+ * config resource.
+ *
+ * @return The config resource location.
+ */
+ protected abstract String getConfigResourceLocation();
+
+ /**
+ * Should we use a transaction manager for transaction management.
+ *
+ * @return True if we should use a RM; false otherwise.
+ */
+ protected abstract boolean useTransactionManager();
+
+
+ public void testQueryCacheInvalidation() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Item i = new Item();
+ i.setName("widget");
+ i.setDescription("A really top-quality, full-featured widget.");
+ s.persist(i);
+ t.commit();
+ s.close();
+
+ SecondLevelCacheStatistics slcs = s.getSessionFactory().getStatistics()
+ .getSecondLevelCacheStatistics( Item.class.getName() );
+
+ assertEquals( slcs.getPutCount(), 1 );
+ assertEquals( slcs.getElementCountInMemory(), 1 );
+ assertEquals( slcs.getEntries().size(), 1 );
+
+ s = openSession();
+ t = s.beginTransaction();
+ i = (Item) s.get( Item.class, i.getId() );
+
+ assertEquals( slcs.getHitCount(), 1 );
+ assertEquals( slcs.getMissCount(), 0 );
+
+ i.setDescription("A bog standard item");
+
+ t.commit();
+ s.close();
+
+ assertEquals( slcs.getPutCount(), 2 );
+
+ Object entry = slcs.getEntries().get( i.getId() );
+ Map map;
+ if ( entry instanceof ReadWriteCache.Item ) {
+ map = (Map) ( (ReadWriteCache.Item) entry ).getValue();
+ }
+ else {
+ map = (Map) entry;
+ }
+ assertTrue( map.get("description").equals("A bog standard item") );
+ assertTrue( map.get("name").equals("widget") );
+
+ // cleanup
+ s = openSession();
+ t = s.beginTransaction();
+ s.delete( i );
+ t.commit();
+ s.close();
+ }
+
+ public void testEmptySecondLevelCacheEntry() throws Exception {
+ getSessions().evictEntity( Item.class.getName() );
+ Statistics stats = getSessions().getStatistics();
+ stats.clear();
+ SecondLevelCacheStatistics statistics = stats.getSecondLevelCacheStatistics( Item.class.getName() );
+ Map cacheEntries = statistics.getEntries();
+ assertEquals( 0, cacheEntries.size() );
+ }
+
+ public void testStaleWritesLeaveCacheConsistent() {
+ Session s = openSession();
+ Transaction txn = s.beginTransaction();
+ VersionedItem item = new VersionedItem();
+ item.setName( "steve" );
+ item.setDescription( "steve's item" );
+ s.save( item );
+ txn.commit();
+ s.close();
+
+ Long initialVersion = item.getVersion();
+
+ // manually revert the version property
+ item.setVersion( new Long( item.getVersion().longValue() - 1 ) );
+
+ try {
+ s = openSession();
+ txn = s.beginTransaction();
+ s.update( item );
+ txn.commit();
+ s.close();
+ fail( "expected stale write to fail" );
+ }
+ catch( Throwable expected ) {
+ // expected behavior here
+ if ( txn != null ) {
+ try {
+ txn.rollback();
+ }
+ catch( Throwable ignore ) {
+ }
+ }
+ }
+ finally {
+ if ( s != null && s.isOpen() ) {
+ try {
+ s.close();
+ }
+ catch( Throwable ignore ) {
+ }
+ }
+ }
+
+ // check the version value in the cache...
+ SecondLevelCacheStatistics slcs = sfi().getStatistics()
+ .getSecondLevelCacheStatistics( VersionedItem.class.getName() );
+
+ Object entry = slcs.getEntries().get( item.getId() );
+ Long cachedVersionValue;
+ if ( entry instanceof ReadWriteCache.Lock ) {
+ //FIXME don't know what to test here
+ cachedVersionValue = new Long( ( (ReadWriteCache.Lock) entry).getUnlockTimestamp() );
+ }
+ else {
+ cachedVersionValue = ( Long ) ( (Map) entry ).get( "_version" );
+ assertEquals( initialVersion.longValue(), cachedVersionValue.longValue() );
+ }
+
+
+ // cleanup
+ s = openSession();
+ txn = s.beginTransaction();
+ item = ( VersionedItem ) s.load( VersionedItem.class, item.getId() );
+ s.delete( item );
+ txn.commit();
+ s.close();
+
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/CacheSuite.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/CacheSuite.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/CacheSuite.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,21 @@
+package org.hibernate.test.cache;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.hibernate.test.cache.treecache.optimistic.OptimisticTreeCacheTest;
+import org.hibernate.test.cache.treecache.pessimistic.TreeCacheTest;
+import org.hibernate.test.cache.ehcache.EhCacheTest;
+
+/**
+ * @author Steve Ebersole
+ */
+public class CacheSuite {
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite( "CacheProvider tests");
+ suite.addTest( OptimisticTreeCacheTest.suite() );
+ suite.addTest( TreeCacheTest.suite() );
+ suite.addTest( EhCacheTest.suite() );
+ return suite;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/Item.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/Item.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/Item.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -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">
+
+<hibernate-mapping
+ package="org.hibernate.test.cache">
+
+ <class name="Item" table="Items">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true"/>
+ <property name="description" not-null="true"/>
+ </class>
+
+ <class name="VersionedItem" table="VersionedItems">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <version name="version" type="long"/>
+ <property name="name" not-null="true"/>
+ <property name="description" not-null="true"/>
+ </class>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/Item.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/Item.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/Item.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/Item.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,31 @@
+//$Id: Item.java 5686 2005-02-12 07:27:32Z steveebersole $
+package org.hibernate.test.cache;
+
+
+/**
+ * @author Gavin King
+ */
+public class Item {
+ private Long id;
+ private String name;
+ private String description;
+
+ public String getDescription() {
+ return description;
+ }
+ public void setDescription(String description) {
+ this.description = description;
+ }
+ 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;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/Item.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/VersionedItem.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/VersionedItem.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/VersionedItem.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,16 @@
+package org.hibernate.test.cache;
+
+/**
+ * @author Steve Ebersole
+ */
+public class VersionedItem extends Item {
+ private Long version;
+
+ public Long getVersion() {
+ return version;
+ }
+
+ public void setVersion(Long version) {
+ this.version = version;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/ehcache/EhCacheTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/ehcache/EhCacheTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/ehcache/EhCacheTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,48 @@
+//$Id: $
+package org.hibernate.test.cache.ehcache;
+
+import junit.framework.Test;
+
+import org.hibernate.cache.EhCacheProvider;
+import org.hibernate.cfg.Environment;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.test.cache.BaseCacheProviderTestCase;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class EhCacheTest extends BaseCacheProviderTestCase {
+
+ // note that a lot of the fucntionality here is intended to be used
+ // in creating specific tests for each CacheProvider that would extend
+ // from a base test case (this) for common requirement testing...
+
+ public EhCacheTest(String x) {
+ super( x );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( EhCacheTest.class );
+ }
+
+ public String getCacheConcurrencyStrategy() {
+ return "read-write";
+ }
+
+ protected Class getCacheProvider() {
+ return EhCacheProvider.class;
+ }
+
+ protected String getConfigResourceKey() {
+ return Environment.CACHE_PROVIDER_CONFIG;
+ }
+
+ protected String getConfigResourceLocation() {
+ return "org/hibernate/test/cache/ehcache/ehcache.xml";
+ }
+
+ protected boolean useTransactionManager() {
+ return false;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/ehcache/ehcache.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/ehcache/ehcache.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/ehcache/ehcache.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,88 @@
+<ehcache>
+
+ <!-- Sets the path to the directory where cache .data files are created.
+
+ If the path is a Java System Property it is replaced by
+ its value in the running VM.
+
+ The following properties are translated:
+ user.home - User's home directory
+ user.dir - User's current working directory
+ java.io.tmpdir - Default temp file path -->
+ <diskStore path="java.io.tmpdir"/>
+
+
+ <!--Default Cache configuration. These will applied to caches programmatically created through
+ the CacheManager.
+
+ The following attributes are required for defaultCache:
+
+ maxInMemory - Sets the maximum number of objects that will be created in memory
+ eternal - Sets whether elements are eternal. If eternal, timeouts are ignored and the element
+ is never expired.
+ timeToIdleSeconds - Sets the time to idle for an element before it expires. Is only used
+ if the element is not eternal. Idle time is now - last accessed time
+ timeToLiveSeconds - Sets the time to live for an element before it expires. Is only used
+ if the element is not eternal. TTL is now - creation time
+ overflowToDisk - Sets whether elements can overflow to disk when the in-memory cache
+ has reached the maxInMemory limit.
+
+ -->
+ <defaultCache
+ maxElementsInMemory="10000"
+ eternal="false"
+ timeToIdleSeconds="120"
+ timeToLiveSeconds="120"
+ overflowToDisk="true"
+ />
+
+ <!--Predefined caches. Add your cache configuration settings here.
+ If you do not have a configuration for your cache a WARNING will be issued when the
+ CacheManager starts
+
+ The following attributes are required for defaultCache:
+
+ name - Sets the name of the cache. This is used to identify the cache. It must be unique.
+ maxInMemory - Sets the maximum number of objects that will be created in memory
+ eternal - Sets whether elements are eternal. If eternal, timeouts are ignored and the element
+ is never expired.
+ timeToIdleSeconds - Sets the time to idle for an element before it expires. Is only used
+ if the element is not eternal. Idle time is now - last accessed time
+ timeToLiveSeconds - Sets the time to live for an element before it expires. Is only used
+ if the element is not eternal. TTL is now - creation time
+ overflowToDisk - Sets whether elements can overflow to disk when the in-memory cache
+ has reached the maxInMemory limit.
+
+ -->
+
+ <!-- Sample cache named sampleCache1
+ This cache contains a maximum in memory of 10000 elements, and will expire
+ an element if it is idle for more than 5 minutes and lives for more than
+ 10 minutes.
+
+ If there are more than 10000 elements it will overflow to the
+ disk cache, which in this configuration will go to wherever java.io.tmp is
+ defined on your system. On a standard Linux system this will be /tmp"
+ -->
+ <cache name="sampleCache1"
+ maxElementsInMemory="10000"
+ eternal="false"
+ timeToIdleSeconds="300"
+ timeToLiveSeconds="600"
+ overflowToDisk="true"
+ />
+
+ <!-- Sample cache named sampleCache2
+ This cache contains 1000 elements. Elements will always be held in memory.
+ They are not expired. -->
+ <cache name="sampleCache2"
+ maxElementsInMemory="1000"
+ eternal="true"
+ timeToIdleSeconds="0"
+ timeToLiveSeconds="0"
+ overflowToDisk="false"
+ /> -->
+
+ <!-- Place configuration for your caches following -->
+
+</ehcache>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/treecache/optimistic/OptimisticTreeCacheTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/treecache/optimistic/OptimisticTreeCacheTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/treecache/optimistic/OptimisticTreeCacheTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,113 @@
+package org.hibernate.test.cache.treecache.optimistic;
+
+import junit.framework.Test;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.TreeCache;
+import org.jboss.cache.config.Option;
+import org.jboss.cache.optimistic.DataVersion;
+
+import org.hibernate.cache.OptimisticTreeCacheProvider;
+import org.hibernate.cache.impl.bridge.RegionFactoryCacheProviderBridge;
+import org.hibernate.cfg.Environment;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.test.cache.BaseCacheProviderTestCase;
+import org.hibernate.test.tm.DummyTransactionManager;
+
+/**
+ * @author Steve Ebersole
+ */
+public class OptimisticTreeCacheTest extends BaseCacheProviderTestCase {
+
+ // note that a lot of the fucntionality here is intended to be used
+ // in creating specific tests for each CacheProvider that would extend
+ // from a base test case (this) for common requirement testing...
+
+ public OptimisticTreeCacheTest(String x) {
+ super( x );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( OptimisticTreeCacheTest.class );
+ }
+
+ public String getCacheConcurrencyStrategy() {
+ return "transactional";
+ }
+
+ protected Class getCacheProvider() {
+ return OptimisticTreeCacheProvider.class;
+ }
+
+ protected String getConfigResourceKey() {
+ return Environment.CACHE_PROVIDER_CONFIG;
+ }
+
+ protected String getConfigResourceLocation() {
+ return "org/hibernate/test/cache/treecache/optimistic/treecache.xml";
+ }
+
+ protected boolean useTransactionManager() {
+ return true;
+ }
+
+ public void testCacheLevelStaleWritesFail() throws Throwable {
+ Fqn fqn = new Fqn( "whatever" );
+ TreeCache treeCache = ( ( OptimisticTreeCacheProvider ) ( ( RegionFactoryCacheProviderBridge ) sfi().getSettings().getRegionFactory() ).getCacheProvider() ).getUnderlyingCache();
+
+ Long long1 = new Long(1);
+ Long long2 = new Long(2);
+
+ try {
+ System.out.println( "****************************************************************" );
+ DummyTransactionManager.INSTANCE.begin();
+ treeCache.put( fqn, "ITEM", long1, ManualDataVersion.gen( 1 ) );
+ DummyTransactionManager.INSTANCE.commit();
+
+ System.out.println( "****************************************************************" );
+ DummyTransactionManager.INSTANCE.begin();
+ treeCache.put( fqn, "ITEM", long2, ManualDataVersion.gen( 2 ) );
+ DummyTransactionManager.INSTANCE.commit();
+
+ try {
+ System.out.println( "****************************************************************" );
+ DummyTransactionManager.INSTANCE.begin();
+ treeCache.put( fqn, "ITEM", long1, ManualDataVersion.gen( 1 ) );
+ DummyTransactionManager.INSTANCE.commit();
+ fail( "stale write allowed" );
+ }
+ catch( Throwable ignore ) {
+ // expected behavior
+ DummyTransactionManager.INSTANCE.rollback();
+ }
+
+ Long current = ( Long ) treeCache.get( fqn, "ITEM" );
+ assertEquals( "unexpected current value", 2, current.longValue() );
+ }
+ finally {
+ try {
+ treeCache.remove( fqn, "ITEM" );
+ }
+ catch( Throwable ignore ) {
+ }
+ }
+ }
+
+ private static class ManualDataVersion implements DataVersion {
+ private final int version;
+
+ public ManualDataVersion(int version) {
+ this.version = version;
+ }
+
+ public boolean newerThan(DataVersion dataVersion) {
+ return this.version > ( ( ManualDataVersion ) dataVersion ).version;
+ }
+
+ public static Option gen(int version) {
+ ManualDataVersion mdv = new ManualDataVersion( version );
+ Option option = new Option();
+ option.setDataVersion( mdv );
+ return option;
+ }
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/treecache/optimistic/treecache.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/treecache/optimistic/treecache.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/treecache/optimistic/treecache.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- ===================================================================== -->
+<!-- -->
+<!-- Sample TreeCache Service Configuration -->
+<!-- Recommended for use as Hibernate's 2nd Level Cache -->
+<!-- For use with JBossCache >= 1.3.0 ONLY!!! -->
+<!-- -->
+<!-- ===================================================================== -->
+
+<server>
+
+ <classpath codebase="./lib" archives="jboss-cache.jar, jgroups.jar"/>
+
+
+ <!-- ==================================================================== -->
+ <!-- Defines TreeCache configuration -->
+ <!-- ==================================================================== -->
+
+ <mbean code="org.jboss.cache.TreeCache"
+ name="jboss.cache:service=TreeCache">
+
+ <depends>jboss:service=Naming</depends>
+ <depends>jboss:service=TransactionManager</depends>
+
+ <!--
+ Configure the TransactionManager : no matter since Hibernate will plug in
+ an "adapter" to its own TransactionManagerLookup strategy here
+ -->
+ <attribute name="TransactionManagerLookupClass">org.jboss.cache.GenericTransactionManagerLookup</attribute>
+
+
+ <!--
+ Node locking scheme:
+ OPTIMISTIC
+ PESSIMISTIC (default)
+ -->
+ <attribute name="NodeLockingScheme">OPTIMISTIC</attribute>
+
+ <!--
+ Note that this attribute is IGNORED if your NodeLockingScheme above is OPTIMISTIC.
+
+ Isolation level : SERIALIZABLE
+ REPEATABLE_READ (default)
+ READ_COMMITTED
+ READ_UNCOMMITTED
+ NONE
+ -->
+ <attribute name="IsolationLevel">REPEATABLE_READ</attribute>
+
+ <!--
+ Valid modes are LOCAL
+ REPL_ASYNC
+ REPL_SYNC
+ INVALIDATION_ASYNC
+ INVALIDATION_SYNC
+
+ INVALIDATION_ASYNC is highly recommended as the mode for use
+ with clustered second-level caches.
+ -->
+ <attribute name="CacheMode">LOCAL</attribute>
+
+ <!--
+ Just used for async repl: use a replication queue
+ -->
+ <attribute name="UseReplQueue">false</attribute>
+
+ <!--
+ Replication interval for replication queue (in ms)
+ -->
+ <attribute name="ReplQueueInterval">0</attribute>
+
+ <!--
+ Max number of elements which trigger replication
+ -->
+ <attribute name="ReplQueueMaxElements">0</attribute>
+
+ <!-- Name of cluster. Needs to be the same for all clusters, in order
+ to find each other
+ -->
+ <attribute name="ClusterName">TreeCache-Cluster</attribute>
+
+ <!-- JGroups protocol stack properties. Can also be a URL,
+ e.g. file:/home/bela/default.xml
+ <attribute name="ClusterProperties"></attribute>
+ -->
+
+ <attribute name="ClusterConfig">
+ <config>
+ <!-- UDP: if you have a multihomed machine,
+ set the bind_addr attribute to the appropriate NIC IP address -->
+ <!-- UDP: On Windows machines, because of the media sense feature
+ being broken with multicast (even after disabling media sense)
+ set the loopback attribute to true -->
+ <UDP mcast_addr="228.1.2.3" mcast_port="48866"
+ ip_ttl="64" ip_mcast="true"
+ mcast_send_buf_size="150000" mcast_recv_buf_size="80000"
+ ucast_send_buf_size="150000" ucast_recv_buf_size="80000"
+ loopback="false"/>
+ <PING timeout="2000" num_initial_members="3"
+ up_thread="false" down_thread="false"/>
+ <MERGE2 min_interval="10000" max_interval="20000"/>
+ <!-- <FD shun="true" up_thread="true" down_thread="true" />-->
+ <FD_SOCK/>
+ <VERIFY_SUSPECT timeout="1500"
+ up_thread="false" down_thread="false"/>
+ <pbcast.NAKACK gc_lag="50" retransmit_timeout="600,1200,2400,4800"
+ max_xmit_size="8192" up_thread="false" down_thread="false"/>
+ <UNICAST timeout="600,1200,2400" window_size="100" min_threshold="10"
+ down_thread="false"/>
+ <pbcast.STABLE desired_avg_gossip="20000"
+ up_thread="false" down_thread="false"/>
+ <FRAG frag_size="8192"
+ down_thread="false" up_thread="false"/>
+ <pbcast.GMS join_timeout="5000" join_retry_timeout="2000"
+ shun="true" print_local_addr="true"/>
+ <pbcast.STATE_TRANSFER up_thread="true" down_thread="true"/>
+ </config>
+ </attribute>
+
+ <!--
+ Whether or not to fetch state on joining a cluster
+ NOTE this used to be called FetchStateOnStartup and has been renamed to be more descriptive.
+ -->
+ <attribute name="FetchInMemoryState">false</attribute>
+
+ <!--
+ Number of milliseconds to wait until all responses for a
+ synchronous call have been received.
+ -->
+ <attribute name="SyncReplTimeout">20000</attribute>
+
+ <!-- Max number of milliseconds to wait for a lock acquisition -->
+ <attribute name="LockAcquisitionTimeout">15000</attribute>
+
+
+ <!-- Name of the eviction policy class. -->
+ <attribute name="EvictionPolicyClass"></attribute>
+
+ <!--
+ Indicate whether to use marshalling or not. Set this to true if you are running under a scoped
+ class loader, e.g., inside an application server. Default is "false".
+ -->
+ <attribute name="UseMarshalling">false</attribute>
+
+ </mbean>
+</server>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/treecache/pessimistic/TreeCacheTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/treecache/pessimistic/TreeCacheTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/treecache/pessimistic/TreeCacheTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,47 @@
+package org.hibernate.test.cache.treecache.pessimistic;
+
+import junit.framework.Test;
+
+import org.hibernate.cache.TreeCacheProvider;
+import org.hibernate.cfg.Environment;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.test.cache.BaseCacheProviderTestCase;
+
+/**
+ * @author Steve Ebersole
+ */
+public class TreeCacheTest extends BaseCacheProviderTestCase {
+
+ // note that a lot of the fucntionality here is intended to be used
+ // in creating specific tests for each CacheProvider that would extend
+ // from a base test case (this) for common requirement testing...
+
+ public TreeCacheTest(String x) {
+ super( x );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( TreeCacheTest.class );
+ }
+
+ public String getCacheConcurrencyStrategy() {
+ return "transactional";
+ }
+
+ protected Class getCacheProvider() {
+ return TreeCacheProvider.class;
+ }
+
+ protected String getConfigResourceKey() {
+ return Environment.CACHE_PROVIDER_CONFIG;
+ }
+
+ protected String getConfigResourceLocation() {
+ return "org/hibernate/test/cache/treecache/pessimistic/treecache.xml";
+ }
+
+ protected boolean useTransactionManager() {
+ return true;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/treecache/pessimistic/treecache.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/treecache/pessimistic/treecache.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cache/treecache/pessimistic/treecache.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- ===================================================================== -->
+<!-- -->
+<!-- Sample TreeCache Service Configuration -->
+<!-- -->
+<!-- ===================================================================== -->
+
+<server>
+
+ <classpath codebase="./lib" archives="jboss-cache.jar, jgroups.jar"/>
+
+
+ <!-- ==================================================================== -->
+ <!-- Defines TreeCache configuration -->
+ <!-- ==================================================================== -->
+
+ <mbean code="org.jboss.cache.TreeCache"
+ name="jboss.cache:service=TreeCache">
+
+ <depends>jboss:service=Naming</depends>
+ <depends>jboss:service=TransactionManager</depends>
+
+ <!--
+ TransactionManager configuration not required for Hibernate!
+ -->
+
+
+ <!--
+ Node isolation level : SERIALIZABLE
+ REPEATABLE_READ (default)
+ READ_COMMITTED
+ READ_UNCOMMITTED
+ NONE
+ -->
+ <attribute name="IsolationLevel">REPEATABLE_READ</attribute>
+
+ <!--
+ Valid modes are LOCAL
+ REPL_ASYNC
+ REPL_SYNC
+ -->
+ <attribute name="CacheMode">LOCAL</attribute>
+
+ <!-- Name of cluster. Needs to be the same for all clusters, in order
+ to find each other
+ -->
+ <attribute name="ClusterName">TreeCache-Cluster</attribute>
+
+ <!-- JGroups protocol stack properties. Can also be a URL,
+ e.g. file:/home/bela/default.xml
+ <attribute name="ClusterProperties"></attribute>
+ -->
+
+ <attribute name="ClusterConfig">
+ <config>
+ <!-- UDP: if you have a multihomed machine,
+ set the bind_addr attribute to the appropriate NIC IP address -->
+ <!-- UDP: On Windows machines, because of the media sense feature
+ being broken with multicast (even after disabling media sense)
+ set the loopback attribute to true -->
+ <UDP mcast_addr="228.1.2.3" mcast_port="45566"
+ ip_ttl="64" ip_mcast="true"
+ mcast_send_buf_size="150000" mcast_recv_buf_size="80000"
+ ucast_send_buf_size="150000" ucast_recv_buf_size="80000"
+ loopback="false"/>
+ <PING timeout="2000" num_initial_members="3"
+ up_thread="false" down_thread="false"/>
+ <MERGE2 min_interval="10000" max_interval="20000"/>
+ <FD shun="true" up_thread="true" down_thread="true"/>
+ <VERIFY_SUSPECT timeout="1500"
+ up_thread="false" down_thread="false"/>
+ <pbcast.NAKACK gc_lag="50" retransmit_timeout="600,1200,2400,4800"
+ up_thread="false" down_thread="false"/>
+ <pbcast.STABLE desired_avg_gossip="20000"
+ up_thread="false" down_thread="false"/>
+ <UNICAST timeout="600,1200,2400" window_size="100" min_threshold="10"
+ down_thread="false"/>
+ <FRAG frag_size="8192"
+ down_thread="false" up_thread="false"/>
+ <pbcast.GMS join_timeout="5000" join_retry_timeout="2000"
+ shun="true" print_local_addr="true"/>
+ <pbcast.STATE_TRANSFER up_thread="false" down_thread="false"/>
+ </config>
+ </attribute>
+
+ <!--
+ Max number of entries in the cache. If this is exceeded, the
+ eviction policy will kick some entries out in order to make
+ more room
+ -->
+ <attribute name="MaxCapacity">20000</attribute>
+
+ <!--
+ The max amount of time (in milliseconds) we wait until the
+ initial state (ie. the contents of the cache) are retrieved from
+ existing members in a clustered environment
+ -->
+ <attribute name="InitialStateRetrievalTimeout">20000</attribute>
+
+ <!--
+ Number of milliseconds to wait until all responses for a
+ synchronous call have been received.
+ -->
+ <attribute name="SyncReplTimeout">10000</attribute>
+
+ <!-- Max number of milliseconds to wait for a lock acquisition -->
+ <attribute name="LockAcquisitionTimeout">15000</attribute>
+
+ <!-- Max number of milliseconds we hold a lock (not currently
+ implemented) -->
+ <attribute name="LockLeaseTimeout">60000</attribute>
+
+ <!-- Name of the eviction policy class. Not supported now. -->
+ <attribute name="EvictionPolicyClass"></attribute>
+ </mbean>
+
+
+</server>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cascade/Job.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cascade/Job.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cascade/Job.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping SYSTEM "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
+
+<hibernate-mapping package="org.hibernate.test.cascade">
+
+ <class name="Job" table="T_JOB">
+ <id name="id" column="JOB_ID">
+ <generator class="native"/>
+ </id>
+
+ <many-to-one name="batch" class="JobBatch" cascade="none" column="BATCH_ID"/>
+
+ <property name="status" type="int" column="JOB_STATUS" not-null="true"/>
+ <property name="processingInstructions" type="string" column="PI" not-null="true"/>
+
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cascade/Job.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cascade/Job.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cascade/Job.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,53 @@
+// $Id: Job.java 6663 2005-05-03 20:55:31Z steveebersole $
+package org.hibernate.test.cascade;
+
+/**
+ * Implementation of Job.
+ *
+ * @author Steve Ebersole
+ */
+public class Job {
+ private Long id;
+ private JobBatch batch;
+ private String processingInstructions;
+ private int status;
+
+ /** GCLIB constructor */
+ Job() {}
+
+ protected Job(JobBatch batch) {
+ this.batch = batch;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ /*package*/ void setId(Long id) {
+ this.id = id;
+ }
+
+ public JobBatch getBatch() {
+ return batch;
+ }
+
+ /*package*/ void setBatch(JobBatch batch) {
+ this.batch = batch;
+ }
+
+ public String getProcessingInstructions() {
+ return processingInstructions;
+ }
+
+ public void setProcessingInstructions(String processingInstructions) {
+ this.processingInstructions = processingInstructions;
+ }
+
+ public int getStatus() {
+ return status;
+ }
+
+ public void setStatus(int status) {
+ this.status = status;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cascade/JobBatch.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cascade/JobBatch.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cascade/JobBatch.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping SYSTEM "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
+
+<hibernate-mapping package="org.hibernate.test.cascade">
+
+ <class name="JobBatch" table="T_JOB_BATCH">
+ <id name="id" column="BATCH_ID">
+ <generator class="native"/>
+ </id>
+
+ <property name="batchDate" type="timestamp" column="BATCH_DATE" not-null="true"/>
+
+ <set name="jobs" inverse="true" fetch="select" lazy="true" cascade="all, refresh">
+ <key column="BATCH_ID"/>
+ <one-to-many class="Job"/>
+ </set>
+
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cascade/JobBatch.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cascade/JobBatch.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cascade/JobBatch.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,54 @@
+// $Id: JobBatch.java 6663 2005-05-03 20:55:31Z steveebersole $
+package org.hibernate.test.cascade;
+
+import java.util.Date;
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * Implementation of JobBatch.
+ *
+ * @author Steve Ebersole
+ */
+public class JobBatch {
+ private Long id;
+ private Date batchDate;
+ private Set jobs = new HashSet();
+
+ /** CGLIB constructor */
+ JobBatch() {}
+
+ public JobBatch(Date batchDate) {
+ this.batchDate = batchDate;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public Date getBatchDate() {
+ return batchDate;
+ }
+
+ public void setBatchDate(Date batchDate) {
+ this.batchDate = batchDate;
+ }
+
+ public Set getJobs() {
+ return jobs;
+ }
+
+ public void setJobs(Set jobs) {
+ this.jobs = jobs;
+ }
+
+ public Job createJob() {
+ Job job = new Job( this );
+ jobs.add( job );
+ return job;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cascade/RefreshTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cascade/RefreshTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cascade/RefreshTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,79 @@
+// $Id: RefreshTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.cascade;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.util.Date;
+import java.util.Iterator;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * Implementation of RefreshTest.
+ *
+ * @author Steve Ebersole
+ */
+public class RefreshTest extends FunctionalTestCase {
+
+ public RefreshTest(String name) {
+ super( name );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "cascade/Job.hbm.xml", "cascade/JobBatch.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( RefreshTest.class );
+ }
+
+ public void testRefreshCascade() throws Throwable {
+ Session session = openSession();
+ Transaction txn = session.beginTransaction();
+
+ JobBatch batch = new JobBatch( new Date() );
+ batch.createJob().setProcessingInstructions( "Just do it!" );
+ batch.createJob().setProcessingInstructions( "I know you can do it!" );
+
+ // write the stuff to the database; at this stage all job.status values are zero
+ session.persist( batch );
+ session.flush();
+
+ // behind the session's back, let's modify the statuses
+ updateStatuses( session.connection() );
+
+ // Now lets refresh the persistent batch, and see if the refresh cascaded to the jobs collection elements
+ session.refresh( batch );
+
+ Iterator itr = batch.getJobs().iterator();
+ while( itr.hasNext() ) {
+ Job job = ( Job ) itr.next();
+ assertEquals( "Jobs not refreshed!", 1, job.getStatus() );
+ }
+
+ txn.rollback();
+ session.close();
+ }
+
+ private void updateStatuses(Connection connection) throws Throwable {
+ PreparedStatement stmnt = null;
+ try {
+ stmnt = connection.prepareStatement( "UPDATE T_JOB SET JOB_STATUS = 1" );
+ stmnt.executeUpdate();
+ }
+ finally {
+ if ( stmnt != null ) {
+ try {
+ stmnt.close();
+ }
+ catch( Throwable ignore ) {
+ }
+ }
+ }
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cfg/Cacheable.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cfg/Cacheable.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cfg/Cacheable.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,15 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping>
+
+ <class entity-name="Entity">
+ <id name="id" type="long" column="ID">
+ <generator class="increment"/>
+ </id>
+ <property name="name" type="string" column="NAME"/>
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cfg/CacheableFileTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cfg/CacheableFileTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cfg/CacheableFileTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,44 @@
+package org.hibernate.test.cfg;
+
+import java.io.File;
+
+import org.hibernate.cfg.Configuration;
+import org.hibernate.junit.UnitTestCase;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class CacheableFileTest extends UnitTestCase {
+
+ public static final String MAPPING = "org/hibernate/test/cfg/Cacheable.hbm.xml";
+
+ private File mappingFile;
+
+ public CacheableFileTest(String string) {
+ super( string );
+ }
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ mappingFile = new File( getClass().getClassLoader().getResource( MAPPING ).getFile() );
+ assertTrue( mappingFile.exists() );
+ File cached = new File( mappingFile.getParentFile(), mappingFile.getName() + ".bin" );
+ if ( cached.exists() ) {
+ cached.delete();
+ }
+ }
+
+ protected void tearDown() throws Exception {
+ mappingFile = null;
+ super.tearDown();
+ }
+
+ public void testCachedFiles() {
+ Configuration cfg = new Configuration();
+ cfg.addCacheableFile( mappingFile );
+ Configuration cfg2 = new Configuration();
+ cfg2.addCacheableFile( mappingFile );
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/CompositeIdTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/CompositeIdTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/CompositeIdTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,269 @@
+//$Id: CompositeIdTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.cid;
+
+import java.math.BigDecimal;
+import java.util.Calendar;
+import java.util.Iterator;
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class CompositeIdTest extends FunctionalTestCase {
+
+ public CompositeIdTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "cid/Customer.hbm.xml", "cid/Order.hbm.xml", "cid/LineItem.hbm.xml", "cid/Product.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite(CompositeIdTest.class);
+ }
+
+ public void testQuery() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.createQuery("from LineItem ol where ol.order.id.customerId = 'C111'").list();
+ t.commit();
+ s.close();
+ }
+
+ public void testCompositeIds() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ Product p = new Product();
+ p.setProductId("A123");
+ p.setDescription("nipple ring");
+ p.setPrice( new BigDecimal(1.0) );
+ p.setNumberAvailable(1004);
+ s.persist(p);
+
+ Product p2 = new Product();
+ p2.setProductId("X525");
+ p2.setDescription("nose stud");
+ p2.setPrice( new BigDecimal(3.0) );
+ p2.setNumberAvailable(105);
+ s.persist(p2);
+
+ Customer c = new Customer();
+ c.setAddress("St Kilda Rd, MEL, 3000");
+ c.setName("Virginia");
+ c.setCustomerId("C111");
+ s.persist(c);
+
+ Order o = new Order(c);
+ o.setOrderDate( Calendar.getInstance() );
+ LineItem li = new LineItem(o, p);
+ li.setQuantity(2);
+
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ o = (Order) s.get( Order.class, new Order.Id("C111", 0) );
+ assertEquals( o.getTotal().intValue(), 2 );
+ o.getCustomer().getName();
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.createQuery("from Customer c left join fetch c.orders o left join fetch o.lineItems li left join fetch li.product p").list();
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.createQuery("from Order o left join fetch o.lineItems li left join fetch li.product p").list();
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Iterator iter = s.createQuery("select o.id, li.id from Order o join o.lineItems li").list().iterator();
+ while ( iter.hasNext() ) {
+ Object[] stuff = (Object[]) iter.next();
+ assertTrue(stuff.length==2);
+ }
+ iter = s.createQuery("from Order o join o.lineItems li").iterate();
+ while ( iter.hasNext() ) {
+ Object[] stuff = (Object[]) iter.next();
+ assertTrue(stuff.length==2);
+ }
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Customer) s.get( Customer.class, "C111" );
+ Order o2 = new Order(c);
+ o2.setOrderDate( Calendar.getInstance() );
+ s.flush();
+ LineItem li2 = new LineItem(o2, p2);
+ li2.setQuantity(5);
+ List bigOrders = s.createQuery("from Order o where o.total>10.0").list();
+ assertEquals( bigOrders.size(), 1 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.createQuery("delete from LineItem").executeUpdate();
+ s.createQuery("delete from Order").executeUpdate();
+ s.createQuery("delete from Customer").executeUpdate();
+ s.createQuery("delete from Product").executeUpdate();
+ t.commit();
+ s.close();
+ }
+
+
+ public void testNonLazyFetch() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ Product p = new Product();
+ p.setProductId("A123");
+ p.setDescription("nipple ring");
+ p.setPrice( new BigDecimal(1.0) );
+ p.setNumberAvailable(1004);
+ s.persist(p);
+
+ Product p2 = new Product();
+ p2.setProductId("X525");
+ p2.setDescription("nose stud");
+ p2.setPrice( new BigDecimal(3.0) );
+ p2.setNumberAvailable(105);
+ s.persist(p2);
+
+ Customer c = new Customer();
+ c.setAddress("St Kilda Rd, MEL, 3000");
+ c.setName("Virginia");
+ c.setCustomerId("C111");
+ s.persist(c);
+
+ Order o = new Order(c);
+ o.setOrderDate( Calendar.getInstance() );
+ LineItem li = new LineItem(o, p);
+ li.setQuantity(2);
+
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ o = (Order) s.get( Order.class, new Order.Id("C111", 0) );
+ assertEquals( o.getTotal().intValue(), 2 );
+ o.getCustomer().getName();
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ o = (Order) s.createQuery("from Order o left join fetch o.lineItems li left join fetch li.product p").uniqueResult();
+ assertTrue( Hibernate.isInitialized( o.getLineItems() ) );
+ li = (LineItem) o.getLineItems().iterator().next();
+ assertTrue( Hibernate.isInitialized( li ) );
+ assertTrue( Hibernate.isInitialized( li.getProduct() ) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ o = (Order) s.createQuery("from Order o").uniqueResult();
+ assertTrue( Hibernate.isInitialized( o.getLineItems() ) );
+ li = (LineItem) o.getLineItems().iterator().next();
+ assertTrue( Hibernate.isInitialized( li ) );
+ assertFalse( Hibernate.isInitialized( li.getProduct() ) );
+ t.commit();
+ s.close();
+
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.createQuery("delete from LineItem").executeUpdate();
+ s.createQuery("delete from Order").executeUpdate();
+ s.createQuery("delete from Customer").executeUpdate();
+ s.createQuery("delete from Product").executeUpdate();
+ t.commit();
+ s.close();
+
+ }
+
+ public void testMultipleCollectionFetch() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ Product p = new Product();
+ p.setProductId("A123");
+ p.setDescription("nipple ring");
+ p.setPrice( new BigDecimal(1.0) );
+ p.setNumberAvailable(1004);
+ s.persist(p);
+
+ Product p2 = new Product();
+ p2.setProductId("X525");
+ p2.setDescription("nose stud");
+ p2.setPrice( new BigDecimal(3.0) );
+ p2.setNumberAvailable(105);
+ s.persist(p2);
+
+ Customer c = new Customer();
+ c.setAddress("St Kilda Rd, MEL, 3000");
+ c.setName("Virginia");
+ c.setCustomerId("C111");
+ s.persist(c);
+
+ Order o = new Order(c);
+ o.setOrderDate( Calendar.getInstance() );
+ LineItem li = new LineItem(o, p);
+ li.setQuantity(2);
+ LineItem li2 = new LineItem(o, p2);
+ li2.setQuantity(3);
+
+ Order o2 = new Order(c);
+ o2.setOrderDate( Calendar.getInstance() );
+ LineItem li3 = new LineItem(o2, p);
+ li3.setQuantity(1);
+ LineItem li4 = new LineItem(o2, p2);
+ li4.setQuantity(1);
+
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Customer) s.createQuery("from Customer c left join fetch c.orders o left join fetch o.lineItems li left join fetch li.product p").uniqueResult();
+ assertTrue( Hibernate.isInitialized( c.getOrders() ) );
+ assertEquals( c.getOrders().size(), 2 );
+ assertTrue( Hibernate.isInitialized( ( (Order) c.getOrders().get(0) ).getLineItems() ) );
+ assertTrue( Hibernate.isInitialized( ( (Order) c.getOrders().get(1) ).getLineItems() ) );
+ assertEquals( ( (Order) c.getOrders().get(0) ).getLineItems().size(), 2 );
+ assertEquals( ( (Order) c.getOrders().get(1) ).getLineItems().size(), 2 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.createQuery("delete from LineItem").executeUpdate();
+ s.createQuery("delete from Order").executeUpdate();
+ s.createQuery("delete from Customer").executeUpdate();
+ s.createQuery("delete from Product").executeUpdate();
+ t.commit();
+ s.close();
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/CompositeIdTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/Customer.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/Customer.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/Customer.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,37 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+ This mapping demonstrates how to map a collection
+ <key> to one of the primary key columns of an
+ associated child class with a composite key. This
+ is very useful for legacy data!
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.cid">
+
+ <class name="Customer">
+
+ <id name="customerId"
+ length="10">
+ <generator class="assigned"/>
+ </id>
+
+ <property name="name" not-null="true" length="100"/>
+ <property name="address" not-null="true" length="200"/>
+
+ <list name="orders"
+ inverse="true"
+ cascade="save-update">
+ <key column="customerId"/>
+ <index column="orderNumber"/>
+ <one-to-many class="Order"/>
+ </list>
+
+ </class>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/Customer.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/Customer.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/Customer.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/Customer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,73 @@
+//$Id: Customer.java 4806 2004-11-25 14:37:00Z steveebersole $
+package org.hibernate.test.cid;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.GregorianCalendar;
+import java.math.BigDecimal;
+
+/**
+ * @author Gavin King
+ */
+public class Customer {
+ private String customerId;
+ private String name;
+ private String address;
+ private List orders = new ArrayList();
+ /**
+ * @return Returns the address.
+ */
+ public String getAddress() {
+ return address;
+ }
+ /**
+ * @param address The address to set.
+ */
+ public void setAddress(String address) {
+ this.address = address;
+ }
+ /**
+ * @return Returns the customerId.
+ */
+ public String getCustomerId() {
+ return customerId;
+ }
+ /**
+ * @param customerId The customerId to set.
+ */
+ public void setCustomerId(String customerId) {
+ this.customerId = customerId;
+ }
+ /**
+ * @return Returns the name.
+ */
+ public String getName() {
+ return name;
+ }
+ /**
+ * @param name The name to set.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+ /**
+ * @return Returns the orders.
+ */
+ public List getOrders() {
+ return orders;
+ }
+ /**
+ * @param orders The orders to set.
+ */
+ public void setOrders(List orders) {
+ this.orders = orders;
+ }
+
+ public Order generateNewOrder(BigDecimal total) {
+ Order order = new Order(this);
+ order.setOrderDate( new GregorianCalendar() );
+ order.setTotal( total );
+
+ return order;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/Customer.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/LineItem.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/LineItem.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/LineItem.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,49 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+ This mapping demonstrates
+
+ (1) composite keys and many-to-one associations on
+ composite keys
+
+ (2) use of insert="false" update="false" on an
+ association mapping, when the foreign key is
+ also part of the primary key
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.cid">
+
+ <class name="LineItem">
+
+ <composite-id name="id"
+ class="LineItem$Id">
+ <key-property name="customerId" length="10"/>
+ <key-property name="orderNumber"/>
+ <key-property name="productId" length="10"/>
+ </composite-id>
+
+ <property name="quantity"/>
+
+ <many-to-one name="order"
+ insert="false"
+ update="false"
+ not-null="true">
+ <column name="customerId"/>
+ <column name="orderNumber"/>
+ </many-to-one>
+
+ <many-to-one name="product"
+ insert="false"
+ update="false"
+ not-null="true"
+ column="productId"/>
+
+ </class>
+
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/LineItem.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/LineItem.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/LineItem.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/LineItem.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,137 @@
+//$Id: LineItem.java 4806 2004-11-25 14:37:00Z steveebersole $
+package org.hibernate.test.cid;
+
+import java.io.Serializable;
+
+/**
+ * @author Gavin King
+ */
+public class LineItem {
+ public static class Id implements Serializable {
+ private String customerId;
+ private int orderNumber;
+ private String productId;
+
+ public Id(String customerId, int orderNumber, String productId) {
+ this.customerId = customerId;
+ this.orderNumber = orderNumber;
+ this.productId = productId;
+ }
+ public Id() {}
+
+ /**
+ * @return Returns the customerId.
+ */
+ public String getCustomerId() {
+ return customerId;
+ }
+ /**
+ * @param customerId The customerId to set.
+ */
+ public void setCustomerId(String customerId) {
+ this.customerId = customerId;
+ }
+ /**
+ * @return Returns the productId.
+ */
+ public String getProductId() {
+ return productId;
+ }
+ /**
+ * @param productId The productId to set.
+ */
+ public void setProductId(String productId) {
+ this.productId = productId;
+ }
+ /**
+ * @return Returns the orderNumber.
+ */
+ public int getOrderNumber() {
+ return orderNumber;
+ }
+ /**
+ * @param orderNumber The orderNumber to set.
+ */
+ public void setOrderNumber(int orderNumber) {
+ this.orderNumber = orderNumber;
+ }
+ public int hashCode() {
+ return customerId.hashCode() + orderNumber + productId.hashCode();
+ }
+ public boolean equals(Object other) {
+ if (other instanceof Id) {
+ Id that = (Id) other;
+ return that.customerId.equals(this.customerId) &&
+ that.productId.equals(this.productId) &&
+ that.orderNumber == this.orderNumber;
+ }
+ else {
+ return false;
+ }
+ }
+ }
+
+ private Id id = new Id();
+ private int quantity;
+ private Order order;
+ private Product product;
+
+ public LineItem(Order o, Product p) {
+ this.order = o;
+ this.id.orderNumber = o.getId().getOrderNumber();
+ this.id.customerId = o.getId().getCustomerId();
+ this.id.productId = p.getProductId();
+ o.getLineItems().add(this);
+ }
+
+ public LineItem() {}
+
+ /**
+ * @return Returns the order.
+ */
+ public Order getOrder() {
+ return order;
+ }
+ /**
+ * @param order The order to set.
+ */
+ public void setOrder(Order order) {
+ this.order = order;
+ }
+ /**
+ * @return Returns the product.
+ */
+ public Product getProduct() {
+ return product;
+ }
+ /**
+ * @param product The product to set.
+ */
+ public void setProduct(Product product) {
+ this.product = product;
+ }
+ /**
+ * @return Returns the quantity.
+ */
+ public int getQuantity() {
+ return quantity;
+ }
+ /**
+ * @param quantity The quantity to set.
+ */
+ public void setQuantity(int quantity) {
+ this.quantity = quantity;
+ }
+ /**
+ * @return Returns the id.
+ */
+ public Id getId() {
+ return id;
+ }
+ /**
+ * @param id The id to set.
+ */
+ public void setId(Id id) {
+ this.id = id;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/LineItem.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/Order.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/Order.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/Order.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,66 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.cid">
+
+<!--
+
+ This mapping demonstrates
+
+ (1) composite keys and one-to-many associations on
+ composite keys
+
+ (2) use of insert="false" update="false" on an
+ association mapping, when the foreign key is
+ also part of the primary key
+
+ (3) use of a derived property which performs a
+ subselect against associated tables
+
+ (4) use of <synchronize/> to ensure that auto-flush
+ works correctly for an entity with a property
+ derived from other tables
+
+
+-->
+
+ <class name="Order" table="CustomerOrder">
+ <synchronize table="LineItem"/>
+ <synchronize table="Product"/>
+
+ <composite-id name="id"
+ class="Order$Id">
+ <key-property name="customerId" length="10"/>
+ <key-property name="orderNumber"/>
+ </composite-id>
+
+ <property name="orderDate"
+ type="calendar_date"
+ not-null="true"/>
+
+ <property name="total"
+ formula="( select sum(li.quantity*p.cost) from LineItem li, Product p where li.productId = p.productId and li.customerId = customerId and li.orderNumber = orderNumber )"/>
+
+ <many-to-one name="customer"
+ column="customerId"
+ insert="false"
+ update="false"
+ not-null="true"/>
+
+ <bag name="lineItems"
+ fetch="join"
+ lazy="false"
+ inverse="true"
+ cascade="save-update">
+ <key>
+ <column name="customerId"/>
+ <column name="orderNumber"/>
+ </key>
+ <one-to-many class="LineItem"/>
+ </bag>
+
+ </class>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/Order.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/Order.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/Order.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/Order.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,145 @@
+//$Id: Order.java 4806 2004-11-25 14:37:00Z steveebersole $
+package org.hibernate.test.cid;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collection;
+
+/**
+ * @author Gavin King
+ */
+public class Order {
+ public static class Id implements Serializable {
+ private String customerId;
+ private int orderNumber;
+
+ public Id(String customerId, int orderNumber) {
+ this.customerId = customerId;
+ this.orderNumber = orderNumber;
+ }
+ public Id() {}
+
+ /**
+ * @return Returns the customerId.
+ */
+ public String getCustomerId() {
+ return customerId;
+ }
+ /**
+ * @param customerId The customerId to set.
+ */
+ public void setCustomerId(String customerId) {
+ this.customerId = customerId;
+ }
+ /**
+ * @return Returns the orderNumber.
+ */
+ public int getOrderNumber() {
+ return orderNumber;
+ }
+ /**
+ * @param orderNumber The orderNumber to set.
+ */
+ public void setOrderNumber(int orderNumber) {
+ this.orderNumber = orderNumber;
+ }
+ public int hashCode() {
+ return customerId.hashCode() + orderNumber;
+ }
+ public boolean equals(Object other) {
+ if (other instanceof Id) {
+ Id that = (Id) other;
+ return that.customerId.equals(this.customerId) &&
+ that.orderNumber == this.orderNumber;
+ }
+ else {
+ return false;
+ }
+ }
+ }
+
+ private Id id = new Id();
+ private Calendar orderDate;
+ private Customer customer;
+ private Collection lineItems = new ArrayList();
+ private BigDecimal total;
+
+ public Order(Customer customer) {
+ this.customer = customer;
+ this.id.customerId = customer.getCustomerId();
+ this.id.orderNumber = customer.getOrders().size();
+ customer.getOrders().add(this);
+ }
+
+ public Order() {}
+
+ /**
+ * @return Returns the customer.
+ */
+ public Customer getCustomer() {
+ return customer;
+ }
+ /**
+ * @param customer The customer to set.
+ */
+ public void setCustomer(Customer customer) {
+ this.customer = customer;
+ }
+ /**
+ * @return Returns the lineItems.
+ */
+ public Collection getLineItems() {
+ return lineItems;
+ }
+ /**
+ * @param lineItems The lineItems to set.
+ */
+ public void setLineItems(Collection lineItems) {
+ this.lineItems = lineItems;
+ }
+ /**
+ * @return Returns the orderDate.
+ */
+ public Calendar getOrderDate() {
+ return orderDate;
+ }
+ /**
+ * @param orderDate The orderDate to set.
+ */
+ public void setOrderDate(Calendar orderDate) {
+ this.orderDate = orderDate;
+ }
+ /**
+ * @return Returns the total.
+ */
+ public BigDecimal getTotal() {
+ return total;
+ }
+ /**
+ * @param total The total to set.
+ */
+ public void setTotal(BigDecimal total) {
+ this.total = total;
+ }
+ /**
+ * @return Returns the id.
+ */
+ public Id getId() {
+ return id;
+ }
+ /**
+ * @param id The id to set.
+ */
+ public void setId(Id id) {
+ this.id = id;
+ }
+
+ public LineItem generateLineItem( Product product, int quantity ) {
+ LineItem li = new LineItem( this, product );
+ li.setQuantity( quantity );
+ lineItems.add( li );
+ return li;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/Order.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/Product.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/Product.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/Product.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,40 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.cid">
+
+<!--
+
+ This mapping demonstrates
+
+ (1) use of a derived property which performs a
+ subselect against an associated table
+
+ (2) use of <synchronize/> to ensure that auto-flush
+ works correctly for an entity with a property
+ derived from another table
+
+
+-->
+
+ <class name="Product">
+ <synchronize table="LineItem"/>
+
+ <id name="productId"
+ length="10">
+ <generator class="assigned"/>
+ </id>
+
+ <property name="description"
+ not-null="true"
+ length="200"/>
+ <property name="price" length="3" column="cost"/>
+ <property name="numberAvailable"/>
+
+ <property name="numberOrdered"
+ formula="( select sum(li.quantity) from LineItem li where li.productId = productId )"/>
+ </class>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/Product.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/Product.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/Product.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/Product.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,75 @@
+//$Id: Product.java 4806 2004-11-25 14:37:00Z steveebersole $
+package org.hibernate.test.cid;
+
+import java.math.BigDecimal;
+
+/**
+ * @author Gavin King
+ */
+public class Product {
+ private String productId;
+ private String description;
+ private BigDecimal price;
+ private int numberAvailable;
+ private int numberOrdered;
+ /**
+ * @return Returns the description.
+ */
+ public String getDescription() {
+ return description;
+ }
+ /**
+ * @param description The description to set.
+ */
+ public void setDescription(String description) {
+ this.description = description;
+ }
+ /**
+ * @return Returns the numberAvailable.
+ */
+ public int getNumberAvailable() {
+ return numberAvailable;
+ }
+ /**
+ * @param numberAvailable The numberAvailable to set.
+ */
+ public void setNumberAvailable(int numberAvailable) {
+ this.numberAvailable = numberAvailable;
+ }
+ /**
+ * @return Returns the numberOrdered.
+ */
+ public int getNumberOrdered() {
+ return numberOrdered;
+ }
+ /**
+ * @param numberOrdered The numberOrdered to set.
+ */
+ public void setNumberOrdered(int numberOrdered) {
+ this.numberOrdered = numberOrdered;
+ }
+ /**
+ * @return Returns the productId.
+ */
+ public String getProductId() {
+ return productId;
+ }
+ /**
+ * @param productId The productId to set.
+ */
+ public void setProductId(String productId) {
+ this.productId = productId;
+ }
+ /**
+ * @return Returns the price.
+ */
+ public BigDecimal getPrice() {
+ return price;
+ }
+ /**
+ * @param price The price to set.
+ */
+ public void setPrice(BigDecimal price) {
+ this.price = price;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cid/Product.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/CollectionSuite.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/CollectionSuite.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/CollectionSuite.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,31 @@
+package org.hibernate.test.collection;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.hibernate.test.collection.bag.PersistentBagTest;
+import org.hibernate.test.collection.idbag.PersistentIdBagTest;
+import org.hibernate.test.collection.list.PersistentListTest;
+import org.hibernate.test.collection.map.PersistentMapTest;
+import org.hibernate.test.collection.original.CollectionTest;
+import org.hibernate.test.collection.set.PersistentSetTest;
+
+/**
+ * Suite of collection (i.e. PersistentCollection) related tests
+ *
+ * @author Steve Ebersole
+ */
+public class CollectionSuite {
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite( "Collection-related tests" );
+ suite.addTest( PersistentBagTest.suite() );
+ suite.addTest( PersistentIdBagTest.suite() );
+ suite.addTest( PersistentListTest.suite() );
+ suite.addTest( PersistentMapTest.suite() );
+ suite.addTest( CollectionTest.suite() );
+ suite.addTest( PersistentSetTest.suite() );
+ return suite;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/bag/BagOwner.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/bag/BagOwner.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/bag/BagOwner.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,46 @@
+package org.hibernate.test.collection.bag;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class BagOwner {
+ private String name;
+ private BagOwner parent;
+ private List children = new ArrayList();
+
+ public BagOwner() {
+ }
+
+ public BagOwner(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public BagOwner getParent() {
+ return parent;
+ }
+
+ public void setParent(BagOwner parent) {
+ this.parent = parent;
+ }
+
+ public List getChildren() {
+ return children;
+ }
+
+ public void setChildren(List children) {
+ this.children = children;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/bag/Mappings.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/bag/Mappings.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/bag/Mappings.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+
+<hibernate-mapping package="org.hibernate.test.collection.bag">
+
+ <class name="BagOwner">
+ <id name="name" column="NAME" type="string" />
+
+ <many-to-one name="parent" class="BagOwner" cascade="none" />
+
+ <bag name="children" inverse="true" cascade="all">
+ <key column="PARENT" />
+ <one-to-many class="BagOwner" />
+ </bag>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/bag/PersistentBagTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/bag/PersistentBagTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/bag/PersistentBagTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,70 @@
+package org.hibernate.test.collection.bag;
+
+import java.util.ArrayList;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.collection.PersistentBag;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * Tests related to operations on a PersistentBag.
+ *
+ * @author Steve Ebersole
+ */
+public class PersistentBagTest extends FunctionalTestCase {
+ public PersistentBagTest(String name) {
+ super( name );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "collection/bag/Mappings.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( PersistentBagTest.class );
+ }
+
+ public void testWriteMethodDirtying() {
+ BagOwner parent = new BagOwner( "root" );
+ BagOwner child = new BagOwner( "c1" );
+ parent.getChildren().add( child );
+ child.setParent( parent );
+ BagOwner otherChild = new BagOwner( "c2" );
+
+ Session session = openSession();
+ session.beginTransaction();
+ session.save( parent );
+ session.flush();
+ // at this point, the list on parent has now been replaced with a PersistentBag...
+ PersistentBag children = ( PersistentBag ) parent.getChildren();
+
+ assertFalse( children.remove( otherChild ) );
+ assertFalse( children.isDirty() );
+
+ ArrayList otherCollection = new ArrayList();
+ otherCollection.add( child );
+ assertFalse( children.retainAll( otherCollection ) );
+ assertFalse( children.isDirty() );
+
+ otherCollection = new ArrayList();
+ otherCollection.add( otherChild );
+ assertFalse( children.removeAll( otherCollection ) );
+ assertFalse( children.isDirty() );
+
+ children.clear();
+ session.delete( child );
+ assertTrue( children.isDirty() );
+
+ session.flush();
+
+ children.clear();
+ assertFalse( children.isDirty() );
+
+ session.delete( parent );
+ session.getTransaction().commit();
+ session.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/idbag/IdbagOwner.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/idbag/IdbagOwner.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/idbag/IdbagOwner.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,37 @@
+package org.hibernate.test.collection.idbag;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class IdbagOwner {
+ private String name;
+ private List children = new ArrayList();
+
+ public IdbagOwner() {
+ }
+
+ public IdbagOwner(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public List getChildren() {
+ return children;
+ }
+
+ public void setChildren(List children) {
+ this.children = children;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/idbag/Mappings.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/idbag/Mappings.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/idbag/Mappings.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+
+<hibernate-mapping package="org.hibernate.test.collection.idbag">
+
+ <class name="IdbagOwner">
+ <id name="name" column="NAME" type="string" />
+
+ <idbag name="children" cascade="all" table="idbag_owner_children">
+ <collection-id column="CHILD" type="long">
+ <generator class="increment"/>
+ </collection-id>
+ <key column="PARENT_FK" />
+ <many-to-many column="CHILD_FK" class="IdbagOwner" />
+ </idbag>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/idbag/PersistentIdBagTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/idbag/PersistentIdBagTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/idbag/PersistentIdBagTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,69 @@
+package org.hibernate.test.collection.idbag;
+
+import java.util.ArrayList;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.collection.PersistentIdentifierBag;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * Tests related to operations on a PersistentIdentifierBag
+ *
+ * @author Steve Ebersole
+ */
+public class PersistentIdBagTest extends FunctionalTestCase {
+ public PersistentIdBagTest(String name) {
+ super( name );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "collection/idbag/Mappings.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( PersistentIdBagTest.class );
+ }
+
+ public void testWriteMethodDirtying() {
+ IdbagOwner parent = new IdbagOwner( "root" );
+ IdbagOwner child = new IdbagOwner( "c1" );
+ parent.getChildren().add( child );
+ IdbagOwner otherChild = new IdbagOwner( "c2" );
+
+ Session session = openSession();
+ session.beginTransaction();
+ session.save( parent );
+ session.flush();
+ // at this point, the list on parent has now been replaced with a PersistentBag...
+ PersistentIdentifierBag children = ( PersistentIdentifierBag ) parent.getChildren();
+
+ assertFalse( children.remove( otherChild ) );
+ assertFalse( children.isDirty() );
+
+ ArrayList otherCollection = new ArrayList();
+ otherCollection.add( child );
+ assertFalse( children.retainAll( otherCollection ) );
+ assertFalse( children.isDirty() );
+
+ otherCollection = new ArrayList();
+ otherCollection.add( otherChild );
+ assertFalse( children.removeAll( otherCollection ) );
+ assertFalse( children.isDirty() );
+
+ children.clear();
+ session.delete( child );
+ assertTrue( children.isDirty() );
+
+ session.flush();
+
+ children.clear();
+ assertFalse( children.isDirty() );
+
+ session.delete( parent );
+ session.getTransaction().commit();
+ session.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/list/ListOwner.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/list/ListOwner.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/list/ListOwner.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,46 @@
+package org.hibernate.test.collection.list;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class ListOwner {
+ private String name;
+ private ListOwner parent;
+ private List children = new ArrayList();
+
+ public ListOwner() {
+ }
+
+ public ListOwner(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public ListOwner getParent() {
+ return parent;
+ }
+
+ public void setParent(ListOwner parent) {
+ this.parent = parent;
+ }
+
+ public List getChildren() {
+ return children;
+ }
+
+ public void setChildren(List children) {
+ this.children = children;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/list/Mappings.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/list/Mappings.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/list/Mappings.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+
+<hibernate-mapping package="org.hibernate.test.collection.list">
+
+ <class name="ListOwner">
+ <id name="name" column="NAME" type="string" />
+
+ <many-to-one name="parent" class="ListOwner" cascade="none" />
+
+ <list name="children" inverse="true" cascade="all">
+ <key column="PARENT" />
+ <list-index column="LIST_INDEX"/>
+ <one-to-many class="ListOwner" />
+ </list>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/list/PersistentListTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/list/PersistentListTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/list/PersistentListTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,70 @@
+package org.hibernate.test.collection.list;
+
+import java.util.ArrayList;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.collection.PersistentList;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * Tests related to operations on a PersistentList
+ *
+ * @author Steve Ebersole
+ */
+public class PersistentListTest extends FunctionalTestCase {
+ public PersistentListTest(String name) {
+ super( name );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "collection/list/Mappings.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( PersistentListTest.class );
+ }
+
+ public void testWriteMethodDirtying() {
+ ListOwner parent = new ListOwner( "root" );
+ ListOwner child = new ListOwner( "c1" );
+ parent.getChildren().add( child );
+ child.setParent( parent );
+ ListOwner otherChild = new ListOwner( "c2" );
+
+ Session session = openSession();
+ session.beginTransaction();
+ session.save( parent );
+ session.flush();
+ // at this point, the list on parent has now been replaced with a PersistentList...
+ PersistentList children = ( PersistentList ) parent.getChildren();
+
+ assertFalse( children.remove( otherChild ) );
+ assertFalse( children.isDirty() );
+
+ ArrayList otherCollection = new ArrayList();
+ otherCollection.add( child );
+ assertFalse( children.retainAll( otherCollection ) );
+ assertFalse( children.isDirty() );
+
+ otherCollection = new ArrayList();
+ otherCollection.add( otherChild );
+ assertFalse( children.removeAll( otherCollection ) );
+ assertFalse( children.isDirty() );
+
+ children.clear();
+ session.delete( child );
+ assertTrue( children.isDirty() );
+
+ session.flush();
+
+ children.clear();
+ assertFalse( children.isDirty() );
+
+ session.delete( parent );
+ session.getTransaction().commit();
+ session.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/map/Child.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/map/Child.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/map/Child.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,34 @@
+package org.hibernate.test.collection.map;
+
+/**
+ * todo: describe Child
+ *
+ * @author Steve Ebersole
+ */
+public class Child {
+ private String name;
+ private Parent parent;
+
+ public Child() {
+ }
+
+ public Child(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Parent getParent() {
+ return parent;
+ }
+
+ public void setParent(Parent parent) {
+ this.parent = parent;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/map/Mappings.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/map/Mappings.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/map/Mappings.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+
+<hibernate-mapping package="org.hibernate.test.collection.map">
+
+ <class name="Parent">
+ <id name="name" column="NAME" type="string" />
+
+ <map name="children" inverse="true" cascade="all">
+ <key column="PARENT" />
+ <map-key type="string" />
+ <one-to-many class="Child" />
+ </map>
+ </class>
+
+ <class name="Child">
+ <id name="name" column="NAME" type="string"/>
+ <many-to-one name="parent" class="Parent" cascade="none" />
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/map/Parent.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/map/Parent.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/map/Parent.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,48 @@
+package org.hibernate.test.collection.map;
+
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * todo: describe Parent
+ *
+ * @author Steve Ebersole
+ */
+public class Parent {
+ private String name;
+ private Map children = new HashMap();
+
+ public Parent() {
+ }
+
+ public Parent(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Map getChildren() {
+ return children;
+ }
+
+ 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 );
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/map/PersistentMapTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/map/PersistentMapTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/map/PersistentMapTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,102 @@
+package org.hibernate.test.collection.map;
+
+import java.util.HashMap;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.collection.PersistentMap;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * todo: describe PersistentMapTest
+ *
+ * @author Steve Ebersole
+ */
+public class PersistentMapTest extends FunctionalTestCase {
+ public PersistentMapTest(String name) {
+ super( name );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "collection/map/Mappings.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( PersistentMapTest.class );
+ }
+
+ public void testWriteMethodDirtying() {
+ Parent parent = new Parent( "p1" );
+ Child child = new Child( "c1" );
+ parent.getChildren().put( child.getName(), child );
+ child.setParent( parent );
+ Child otherChild = new Child( "c2" );
+
+ Session session = openSession();
+ session.beginTransaction();
+ session.save( parent );
+ session.flush();
+ // at this point, the set on parent has now been replaced with a PersistentSet...
+ PersistentMap children = ( PersistentMap ) parent.getChildren();
+
+ Object old = children.put( child.getName(), child );
+ assertTrue( old == child );
+ assertFalse( children.isDirty() );
+
+ old = children.remove( otherChild.getName() );
+ assertNull( old );
+ assertFalse( children.isDirty() );
+
+ HashMap otherMap = new HashMap();
+ otherMap.put( child.getName(), child );
+ children.putAll( otherMap );
+ assertFalse( children.isDirty() );
+
+ otherMap = new HashMap();
+ otherMap.put( otherChild.getName(), otherChild );
+ children.putAll( otherMap );
+ assertTrue( children.isDirty() );
+
+ children.clearDirty();
+ session.delete( child );
+ children.clear();
+ assertTrue( children.isDirty() );
+ session.flush();
+
+ children.clear();
+ assertFalse( children.isDirty() );
+
+ session.delete( parent );
+ 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();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/original/CollectionTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/original/CollectionTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/original/CollectionTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,228 @@
+//$Id: CollectionTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.collection.original;
+
+import java.sql.SQLException;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class CollectionTest extends FunctionalTestCase {
+
+ public CollectionTest(String str) {
+ super( str );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "collection/original/UserPermissions.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( CollectionTest.class );
+ }
+
+ public void testExtraLazy() throws HibernateException, SQLException {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ User u = new User( "gavin" );
+ u.getPermissions().add( new Permission( "obnoxiousness" ) );
+ u.getPermissions().add( new Permission( "pigheadedness" ) );
+ u.getSessionData().put( "foo", "foo value" );
+ s.persist( u );
+ t.commit();
+ s.close();
+ s = openSession();
+ t = s.beginTransaction();
+ u = ( User ) s.get( User.class, "gavin" );
+
+ assertFalse( Hibernate.isInitialized( u.getPermissions() ) );
+ assertEquals( u.getPermissions().size(), 2 );
+ assertTrue( u.getPermissions().contains( new Permission( "obnoxiousness" ) ) );
+ assertFalse( u.getPermissions().contains( new Permission( "silliness" ) ) );
+ assertNotNull( u.getPermissions().get( 1 ) );
+ assertNull( u.getPermissions().get( 3 ) );
+ assertFalse( Hibernate.isInitialized( u.getPermissions() ) );
+
+ assertFalse( Hibernate.isInitialized( u.getSessionData() ) );
+ assertEquals( u.getSessionData().size(), 1 );
+ assertTrue( u.getSessionData().containsKey( "foo" ) );
+ assertFalse( u.getSessionData().containsKey( "bar" ) );
+ assertTrue( u.getSessionData().containsValue( "foo value" ) );
+ assertFalse( u.getSessionData().containsValue( "bar" ) );
+ assertEquals( "foo value", u.getSessionData().get( "foo" ) );
+ assertNull( u.getSessionData().get( "bar" ) );
+ assertFalse( Hibernate.isInitialized( u.getSessionData() ) );
+
+ assertFalse( Hibernate.isInitialized( u.getSessionData() ) );
+ u.getSessionData().put( "bar", "bar value" );
+ u.getSessionAttributeNames().add( "bar" );
+ assertFalse( Hibernate.isInitialized( u.getSessionAttributeNames() ) );
+ assertTrue( Hibernate.isInitialized( u.getSessionData() ) );
+
+ s.delete( u );
+ t.commit();
+ s.close();
+ }
+
+ public void testMerge() throws HibernateException, SQLException {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ User u = new User( "gavin" );
+ u.getPermissions().add( new Permission( "obnoxiousness" ) );
+ u.getPermissions().add( new Permission( "pigheadedness" ) );
+ s.persist( u );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ User u2 = ( User ) s.createCriteria( User.class ).uniqueResult();
+ u2.setPermissions( null ); //forces one shot delete
+ s.merge( u );
+ t.commit();
+ s.close();
+
+ u.getPermissions().add( new Permission( "silliness" ) );
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.merge( u );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ u2 = ( User ) s.createCriteria( User.class ).uniqueResult();
+ assertEquals( u2.getPermissions().size(), 3 );
+ assertEquals( ( ( Permission ) u2.getPermissions().get( 0 ) ).getType(), "obnoxiousness" );
+ assertEquals( ( ( Permission ) u2.getPermissions().get( 2 ) ).getType(), "silliness" );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.delete( u2 );
+ s.flush();
+ t.commit();
+ s.close();
+
+ }
+
+ public void testFetch() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ User u = new User( "gavin" );
+ u.getPermissions().add( new Permission( "obnoxiousness" ) );
+ u.getPermissions().add( new Permission( "pigheadedness" ) );
+ u.getEmailAddresses().add( new Email( "gavin at hibernate.org" ) );
+ u.getEmailAddresses().add( new Email( "gavin.king at jboss.com" ) );
+ s.persist( u );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ User u2 = ( User ) s.createCriteria( User.class ).uniqueResult();
+ assertTrue( Hibernate.isInitialized( u2.getEmailAddresses() ) );
+ assertFalse( Hibernate.isInitialized( u2.getPermissions() ) );
+ assertEquals( u2.getEmailAddresses().size(), 2 );
+ s.delete( u2 );
+ t.commit();
+ s.close();
+ }
+
+ public void testUpdateOrder() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ User u = new User( "gavin" );
+ u.getSessionData().put( "foo", "foo value" );
+ u.getSessionData().put( "bar", "bar value" );
+ u.getEmailAddresses().add( new Email( "gavin.king at jboss.com" ) );
+ u.getEmailAddresses().add( new Email( "gavin at hibernate.org" ) );
+ u.getEmailAddresses().add( new Email( "gavin at illflow.com" ) );
+ u.getEmailAddresses().add( new Email( "gavin at nospam.com" ) );
+ s.persist( u );
+ t.commit();
+ s.close();
+
+ u.getSessionData().clear();
+ u.getSessionData().put( "baz", "baz value" );
+ u.getSessionData().put( "bar", "bar value" );
+ u.getEmailAddresses().remove( 0 );
+ u.getEmailAddresses().remove( 2 );
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.update( u );
+ t.commit();
+ s.close();
+
+ u.getSessionData().clear();
+ u.getEmailAddresses().add( 0, new Email( "gavin at nospam.com" ) );
+ u.getEmailAddresses().add( new Email( "gavin.king at jboss.com" ) );
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.update( u );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.delete( u );
+ t.commit();
+ s.close();
+
+ }
+
+ public void testValueMap() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ User u = new User( "gavin" );
+ u.getSessionData().put( "foo", "foo value" );
+ u.getSessionData().put( "bar", null );
+ u.getEmailAddresses().add( null );
+ u.getEmailAddresses().add( new Email( "gavin.king at jboss.com" ) );
+ u.getEmailAddresses().add( null );
+ u.getEmailAddresses().add( null );
+ s.persist( u );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ User u2 = ( User ) s.createCriteria( User.class ).uniqueResult();
+ assertFalse( Hibernate.isInitialized( u2.getSessionData() ) );
+ assertEquals( u2.getSessionData().size(), 1 );
+ assertEquals( u2.getEmailAddresses().size(), 2 );
+ u2.getSessionData().put( "foo", "new foo value" );
+ u2.getEmailAddresses().set( 1, new Email( "gavin at hibernate.org" ) );
+ //u2.getEmailAddresses().remove(3);
+ //u2.getEmailAddresses().remove(2);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ u2 = ( User ) s.createCriteria( User.class ).uniqueResult();
+ assertFalse( Hibernate.isInitialized( u2.getSessionData() ) );
+ assertEquals( u2.getSessionData().size(), 1 );
+ assertEquals( u2.getEmailAddresses().size(), 2 );
+ assertEquals( u2.getSessionData().get( "foo" ), "new foo value" );
+ assertEquals( ( ( Email ) u2.getEmailAddresses().get( 1 ) ).getAddress(), "gavin at hibernate.org" );
+ s.delete( u2 );
+ t.commit();
+ s.close();
+ }
+
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/original/CollectionTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/original/Email.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/original/Email.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/original/Email.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,27 @@
+//$Id: Email.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.collection.original;
+
+/**
+ * @author Gavin King
+ */
+public class Email {
+ private String address;
+ Email() {}
+ public String getAddress() {
+ return address;
+ }
+ public void setAddress(String type) {
+ this.address = type;
+ }
+ public Email(String type) {
+ this.address = type;
+ }
+ public boolean equals(Object that) {
+ if ( !(that instanceof Email) ) return false;
+ Email p = (Email) that;
+ return this.address.equals(p.address);
+ }
+ public int hashCode() {
+ return address.hashCode();
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/original/Email.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/original/Permission.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/original/Permission.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/original/Permission.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,27 @@
+//$Id: Permission.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.collection.original;
+
+/**
+ * @author Gavin King
+ */
+public class Permission {
+ private String type;
+ Permission() {}
+ public String getType() {
+ return type;
+ }
+ public void setType(String type) {
+ this.type = type;
+ }
+ public Permission(String type) {
+ this.type = type;
+ }
+ public boolean equals(Object that) {
+ if ( !(that instanceof Permission) ) return false;
+ Permission p = (Permission) that;
+ return this.type.equals(p.type);
+ }
+ public int hashCode() {
+ return type.hashCode();
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/original/Permission.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/original/User.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/original/User.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/original/User.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,55 @@
+//$Id: User.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.collection.original;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author Gavin King
+ */
+public class User {
+ private String userName;
+ private List permissions = new ArrayList();
+ private List emailAddresses = new ArrayList();
+ private Map sessionData = new HashMap();
+ private Set sessionAttributeNames = new HashSet();
+
+ User() {}
+ public User(String name) {
+ userName = name;
+ }
+ public List getPermissions() {
+ return permissions;
+ }
+ public void setPermissions(List permissions) {
+ this.permissions = permissions;
+ }
+ public String getUserName() {
+ return userName;
+ }
+ public void setUserName(String userName) {
+ this.userName = userName;
+ }
+ public List getEmailAddresses() {
+ return emailAddresses;
+ }
+ public void setEmailAddresses(List emailAddresses) {
+ this.emailAddresses = emailAddresses;
+ }
+ public Map getSessionData() {
+ return sessionData;
+ }
+ public void setSessionData(Map sessionData) {
+ this.sessionData = sessionData;
+ }
+ public Set getSessionAttributeNames() {
+ return sessionAttributeNames;
+ }
+ public void setSessionAttributeNames(Set sessionAttributeNames) {
+ this.sessionAttributeNames = sessionAttributeNames;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/original/User.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/original/UserPermissions.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/original/UserPermissions.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/original/UserPermissions.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,53 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+ This mapping demonstrates how to use a composite
+ element mapping to model a parent/child association.
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.collection.original">
+
+ <import class="Permission"/>
+
+ <class name="User" table="`Users`">
+ <id name="userName"/>
+ <list name="permissions" lazy="extra">
+ <key column="userName"/>
+ <list-index column="displayOrder" base="1"/>
+ <composite-element class="Permission">
+ <property name="type" column="permissionType"/>
+ </composite-element>
+ </list>
+ <list name="emailAddresses" fetch="join">
+ <key column="userName"/>
+ <list-index column="displayOrder" base="1"/>
+ <composite-element class="Email">
+ <property name="address"/>
+ </composite-element>
+ </list>
+ <map name="sessionData"
+ order-by="lower(`attributeName`) asc"
+ lazy="extra">
+ <key column="userName"/>
+ <map-key column="`attributeName`"
+ type="string"/>
+ <element column="`attributeValue`"
+ type="serializable"
+ not-null="true"/>
+ </map>
+ <set name="sessionAttributeNames"
+ lazy="extra"
+ inverse="true">
+ <key column="userName"/>
+ <element column="`attributeName`"
+ type="string"/>
+ </set>
+ </class>
+
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/original/UserPermissions.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/set/Child.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/set/Child.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/set/Child.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,34 @@
+package org.hibernate.test.collection.set;
+
+/**
+ * todo: describe Child
+ *
+ * @author Steve Ebersole
+ */
+public class Child {
+ private String name;
+ private Parent parent;
+
+ public Child() {
+ }
+
+ public Child(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Parent getParent() {
+ return parent;
+ }
+
+ public void setParent(Parent parent) {
+ this.parent = parent;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/set/Mappings.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/set/Mappings.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/set/Mappings.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+
+<hibernate-mapping package="org.hibernate.test.collection.set">
+
+ <class name="Parent">
+ <id name="name" column="NAME" type="string" />
+
+ <set name="children" inverse="true" cascade="all">
+ <key column="PARENT" />
+ <one-to-many class="Child" />
+ </set>
+ </class>
+
+ <class name="Child">
+ <id name="name" column="NAME" type="string"/>
+ <many-to-one name="parent" class="Parent" cascade="none" />
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/set/Parent.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/set/Parent.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/set/Parent.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,37 @@
+package org.hibernate.test.collection.set;
+
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * todo: describe Parent
+ *
+ * @author Steve Ebersole
+ */
+public class Parent {
+ private String name;
+ private Set children = new HashSet();
+
+ public Parent() {
+ }
+
+ public Parent(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Set getChildren() {
+ return children;
+ }
+
+ public void setChildren(Set children) {
+ this.children = children;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/set/PersistentSetTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/set/PersistentSetTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/collection/set/PersistentSetTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,80 @@
+package org.hibernate.test.collection.set;
+
+import java.util.HashSet;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.collection.PersistentSet;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * todo: describe PersistentSetTest
+ *
+ * @author Steve Ebersole
+ */
+public class PersistentSetTest extends FunctionalTestCase {
+ public PersistentSetTest(String name) {
+ super( name );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "collection/set/Mappings.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( PersistentSetTest.class );
+ }
+
+ public void testWriteMethodDirtying() {
+ Parent parent = new Parent( "p1" );
+ Child child = new Child( "c1" );
+ parent.getChildren().add( child );
+ child.setParent( parent );
+ Child otherChild = new Child( "c2" );
+
+ Session session = openSession();
+ session.beginTransaction();
+ session.save( parent );
+ session.flush();
+ // at this point, the set on parent has now been replaced with a PersistentSet...
+ PersistentSet children = ( PersistentSet ) parent.getChildren();
+
+ assertFalse( children.add( child ) );
+ assertFalse( children.isDirty() );
+
+ assertFalse( children.remove( otherChild ) );
+ assertFalse( children.isDirty() );
+
+ HashSet otherSet = new HashSet();
+ otherSet.add( child );
+ assertFalse( children.addAll( otherSet ) );
+ assertFalse( children.isDirty() );
+
+ assertFalse( children.retainAll( otherSet ) );
+ assertFalse( children.isDirty() );
+
+ otherSet = new HashSet();
+ otherSet.add( otherChild );
+ assertFalse( children.removeAll( otherSet ) );
+ assertFalse( children.isDirty() );
+
+ assertTrue( children.retainAll( otherSet ));
+ assertTrue( children.isDirty() );
+ assertTrue( children.isEmpty() );
+
+ children.clear();
+ session.delete( child );
+ assertTrue( children.isDirty() );
+
+ session.flush();
+
+ children.clear();
+ assertFalse( children.isDirty() );
+
+ session.delete( parent );
+ session.getTransaction().commit();
+ session.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/ComponentSuite.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/ComponentSuite.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/ComponentSuite.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,23 @@
+package org.hibernate.test.component;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.hibernate.test.component.basic.ComponentTest;
+import org.hibernate.test.component.cascading.collection.CascadeToComponentCollectionTest;
+import org.hibernate.test.component.cascading.toone.CascadeToComponentAssociationTest;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class ComponentSuite {
+ public static Test suite() {
+ TestSuite suite = new TestSuite( "component test suite" );
+ suite.addTest( ComponentTest.suite() );
+ suite.addTest( CascadeToComponentCollectionTest.suite() );
+ suite.addTest( CascadeToComponentAssociationTest.suite() );
+ return suite;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/basic/ComponentTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/basic/ComponentTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/basic/ComponentTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,194 @@
+//$Id: ComponentTest.java 11349 2007-03-28 15:37:21Z steve.ebersole at jboss.com $
+package org.hibernate.test.component.basic;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.cfg.Mappings;
+import org.hibernate.criterion.Property;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.function.SQLFunction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.mapping.Component;
+import org.hibernate.mapping.Formula;
+import org.hibernate.mapping.PersistentClass;
+
+/**
+ * @author Gavin King
+ */
+public class ComponentTest extends FunctionalTestCase {
+
+ public ComponentTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "component/basic/User.hbm.xml" };
+ }
+
+ public void configure(Configuration cfg) {
+ cfg.setProperty( Environment.GENERATE_STATISTICS, "true" );
+ }
+
+ public void afterConfigurationBuilt(Mappings mappings, Dialect dialect) {
+ super.afterConfigurationBuilt( mappings, dialect );
+ // Oracle and Postgres do not have year() functions, so we need to
+ // redefine the 'User.person.yob' formula
+ //
+ // consider temporary until we add the capability to define
+ // mapping foprmulas which can use dialect-registered functions...
+ PersistentClass user = mappings.getClass( User.class.getName() );
+ org.hibernate.mapping.Property personProperty = user.getProperty( "person" );
+ Component component = ( Component ) personProperty.getValue();
+ Formula f = ( Formula ) component.getProperty( "yob" ).getValue().getColumnIterator().next();
+
+ SQLFunction yearFunction = ( SQLFunction ) dialect.getFunctions().get( "year" );
+ if ( yearFunction == null ) {
+ // the dialect not know to support a year() function, so rely on the
+ // ANSI SQL extract function
+ f.setFormula( "extract( year from dob )");
+ }
+ else {
+ List args = new ArrayList();
+ args.add( "dob" );
+ f.setFormula( yearFunction.render( args, null ) );
+ }
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite(ComponentTest.class);
+ }
+
+ public void testUpdateFalse() {
+ getSessions().getStatistics().clear();
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ User u = new User( "gavin", "secret", new Person("Gavin King", new Date(), "Karbarook Ave") );
+ s.persist(u);
+ s.flush();
+ u.getPerson().setName("XXXXYYYYY");
+ t.commit();
+ s.close();
+
+ assertEquals( 1, getSessions().getStatistics().getEntityInsertCount() );
+ assertEquals( 0, getSessions().getStatistics().getEntityUpdateCount() );
+
+ s = openSession();
+ t = s.beginTransaction();
+ u = (User) s.get(User.class, "gavin");
+ assertEquals( u.getPerson().getName(), "Gavin King" );
+ s.delete(u);
+ t.commit();
+ s.close();
+
+ assertEquals( 1, getSessions().getStatistics().getEntityDeleteCount() );
+ }
+
+ public void testComponent() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ User u = new User( "gavin", "secret", new Person("Gavin King", new Date(), "Karbarook Ave") );
+ s.persist(u);
+ s.flush();
+ u.getPerson().changeAddress("Phipps Place");
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ u = (User) s.get(User.class, "gavin");
+ assertEquals( u.getPerson().getAddress(), "Phipps Place" );
+ assertEquals( u.getPerson().getPreviousAddress(), "Karbarook Ave" );
+ assertEquals( u.getPerson().getYob(), u.getPerson().getDob().getYear()+1900 );
+ u.setPassword("$ecret");
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ u = (User) s.get(User.class, "gavin");
+ assertEquals( u.getPerson().getAddress(), "Phipps Place" );
+ assertEquals( u.getPerson().getPreviousAddress(), "Karbarook Ave" );
+ assertEquals( u.getPassword(), "$ecret" );
+ s.delete(u);
+ t.commit();
+ s.close();
+ }
+
+ public void testComponentStateChangeAndDirtiness() {
+ // test for HHH-2366
+ Session s = openSession();
+ s.beginTransaction();
+ User u = new User( "steve", "hibernater", new Person( "Steve Ebersole", new Date(), "Main St") );
+ s.persist( u );
+ s.flush();
+ long intialUpdateCount = sfi().getStatistics().getEntityUpdateCount();
+ u.getPerson().setAddress( "Austin" );
+ s.flush();
+ assertEquals( intialUpdateCount + 1, sfi().getStatistics().getEntityUpdateCount() );
+ intialUpdateCount = sfi().getStatistics().getEntityUpdateCount();
+ u.getPerson().setAddress( "Cedar Park" );
+ s.flush();
+ assertEquals( intialUpdateCount + 1, sfi().getStatistics().getEntityUpdateCount() );
+ s.delete( u );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testComponentQueries() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Employee emp = new Employee();
+ emp.setHireDate( new Date() );
+ emp.setPerson( new Person() );
+ emp.getPerson().setName( "steve" );
+ emp.getPerson().setDob( new Date() );
+ s.save( emp );
+
+ s.createQuery( "from Employee e where e.person = :p and 1 = 1 and 2=2" ).setParameter( "p", emp.getPerson() ).list();
+ s.createQuery( "from Employee e where :p = e.person" ).setParameter( "p", emp.getPerson() ).list();
+ s.createQuery( "from Employee e where e.person = ('steve', current_timestamp)" ).list();
+
+ s.delete( emp );
+ t.commit();
+ s.close();
+ }
+
+ public void testComponentFormulaQuery() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.createQuery("from User u where u.person.yob = 1999").list();
+ s.createCriteria(User.class)
+ .add( Property.forName("person.yob").between( new Integer(1999), new Integer(2002) ) )
+ .list();
+ if ( getDialect().supportsRowValueConstructorSyntax() ) {
+ s.createQuery("from User u where u.person = ('gavin', :dob, 'Peachtree Rd', 'Karbarook Ave', 1974, 'Peachtree Rd')")
+ .setDate("dob", new Date("March 25, 1974")).list();
+ s.createQuery("from User where person = ('gavin', :dob, 'Peachtree Rd', 'Karbarook Ave', 1974, 'Peachtree Rd')")
+ .setDate("dob", new Date("March 25, 1974")).list();
+ }
+ t.commit();
+ s.close();
+ }
+
+ public void testNamedQuery() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.getNamedQuery("userNameIn")
+ .setParameterList( "nameList", new Object[] {"1ovthafew", "turin", "xam"} )
+ .list();
+ t.commit();
+ s.close();
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/basic/ComponentTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/basic/Employee.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/basic/Employee.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/basic/Employee.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,38 @@
+package org.hibernate.test.component.basic;
+
+import java.util.Date;
+
+/**
+ * todo: describe Employee
+ *
+ * @author Steve Ebersole
+ */
+public class Employee {
+ private Long id;
+ private Person person;
+ private Date hireDate;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public Person getPerson() {
+ return person;
+ }
+
+ public void setPerson(Person person) {
+ this.person = person;
+ }
+
+ public Date getHireDate() {
+ return hireDate;
+ }
+
+ public void setHireDate(Date hireDate) {
+ this.hireDate = hireDate;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/basic/Person.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/basic/Person.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/basic/Person.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,63 @@
+//$Id: Person.java 11345 2007-03-26 17:24:20Z steve.ebersole at jboss.com $
+package org.hibernate.test.component.basic;
+
+import java.util.Date;
+
+/**
+ * @author Gavin King
+ */
+public class Person {
+ private String name;
+ private Date dob;
+ private String address;
+ private String currentAddress;
+ private String previousAddress;
+ private int yob;
+ Person() {}
+ public Person(String name, Date dob, String address) {
+ this.name = name;
+ this.dob = dob;
+ this.address = address;
+ this.currentAddress = address;
+ }
+ public int getYob() {
+ return yob;
+ }
+ public void setYob(int age) {
+ this.yob = age;
+ }
+ public String getAddress() {
+ return address;
+ }
+ public void setAddress(String address) {
+ this.address = address;
+ }
+ public Date getDob() {
+ return dob;
+ }
+ public void setDob(Date dob) {
+ this.dob = dob;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public String getPreviousAddress() {
+ return previousAddress;
+ }
+ public void setPreviousAddress(String previousAddress) {
+ this.previousAddress = previousAddress;
+ }
+ public void changeAddress(String add) {
+ setPreviousAddress( getAddress() );
+ setAddress(add);
+ }
+ public String getCurrentAddress() {
+ return currentAddress;
+ }
+ public void setCurrentAddress(String currentAddress) {
+ this.currentAddress = currentAddress;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/basic/Person.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/basic/User.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/basic/User.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/basic/User.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,42 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.component.basic">
+
+ <class name="User" table="T_USER">
+ <id name="userName"/>
+ <timestamp name="lastModified"/>
+ <property name="password" not-null="true" optimistic-lock="false"/>
+ <component name="person">
+ <property name="name" update="false" not-null="true"/>
+ <property name="dob" update="false" not-null="true"/>
+ <property name="address"/>
+ <property name="previousAddress" insert="false"/>
+ <property name="yob" formula="year(dob)"/>
+ <property name="currentAddress"
+ column="address"
+ insert="false"
+ update="false"/>
+ </component>
+ </class>
+
+ <class name="Employee" table="T_EMP">
+ <id name="id" type="long" column="ID">
+ <generator class="increment"/>
+ </id>
+ <property name="hireDate" type="date" column="HIRE_DATE"/>
+ <component name="person">
+ <property name="name" update="false" not-null="true"/>
+ <property name="dob" update="false" not-null="true"/>
+ </component>
+ </class>
+
+ <query name="userNameIn"><![CDATA[from User where person.name in (:nameList) or userName in (:nameList)]]></query>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/basic/User.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/basic/User.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/basic/User.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/basic/User.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,44 @@
+//$Id: User.java 11345 2007-03-26 17:24:20Z steve.ebersole at jboss.com $
+package org.hibernate.test.component.basic;
+
+import java.util.Date;
+
+/**
+ * @author Gavin King
+ */
+public class User {
+ private String userName;
+ private String password;
+ private Person person;
+ private Date lastModified;
+ User() {}
+ public User(String id, String pw, Person person) {
+ this.userName = id;
+ this.password = pw;
+ this.person = person;
+ }
+ public Date getLastModified() {
+ return lastModified;
+ }
+ public void setLastModified(Date lastModified) {
+ this.lastModified = lastModified;
+ }
+ public String getPassword() {
+ return password;
+ }
+ public void setPassword(String password) {
+ this.password = password;
+ }
+ public Person getPerson() {
+ return person;
+ }
+ public void setPerson(Person person) {
+ this.person = person;
+ }
+ public String getUserName() {
+ return userName;
+ }
+ public void setUserName(String userName) {
+ this.userName = userName;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/basic/User.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/collection/CascadeToComponentCollectionTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/collection/CascadeToComponentCollectionTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/collection/CascadeToComponentCollectionTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,116 @@
+package org.hibernate.test.component.cascading.collection;
+
+import java.util.Iterator;
+import java.util.Locale;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class CascadeToComponentCollectionTest extends FunctionalTestCase {
+
+ public CascadeToComponentCollectionTest(String string) {
+ super( string );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "component/cascading/collection/Mappings.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( CascadeToComponentCollectionTest.class );
+ }
+
+ public void testMerging() {
+ // step1, we create a definition with one value
+ Session session = openSession();
+ session.beginTransaction();
+ Definition definition = new Definition();
+ Value value1 = new Value( definition );
+ value1.getLocalizedStrings().addString( new Locale( "en_US" ), "hello" );
+ session.persist( definition );
+ session.getTransaction().commit();
+ session.close();
+
+ // step2, we verify that the definition has one value; then we detach it
+ session = openSession();
+ session.beginTransaction();
+ definition = ( Definition ) session.get( Definition.class, definition.getId() );
+ assertEquals( 1, definition.getValues().size() );
+ session.getTransaction().commit();
+ session.close();
+
+ // step3, we add a new value during detachment
+ Value value2 = new Value( definition );
+ value2.getLocalizedStrings().addString( new Locale( "es" ), "hola" );
+
+ // step4 we merge the definition
+ session = openSession();
+ session.beginTransaction();
+ session.merge( definition );
+ session.getTransaction().commit();
+ session.close();
+
+ // step5, final test
+ session = openSession();
+ session.beginTransaction();
+ definition = ( Definition ) session.get( Definition.class, definition.getId() );
+ assertEquals( 2, definition.getValues().size() );
+ Iterator values = definition.getValues().iterator();
+ while ( values.hasNext() ) {
+ assertEquals( 1, ( ( Value ) values.next() ).getLocalizedStrings().getStringsCopy().size() );
+ }
+ session.getTransaction().commit();
+ session.close();
+ }
+
+ public void testMergingOriginallyNullComponent() {
+ // step1, we create a definition with one value, but with a null component
+ Session session = openSession();
+ session.beginTransaction();
+ Definition definition = new Definition();
+ Value value1 = new Value( definition );
+ session.persist( definition );
+ session.getTransaction().commit();
+ session.close();
+
+ // step2, we verify that the definition has one value; then we detach it
+ session = openSession();
+ session.beginTransaction();
+ definition = ( Definition ) session.get( Definition.class, definition.getId() );
+ assertEquals( 1, definition.getValues().size() );
+ session.getTransaction().commit();
+ session.close();
+
+ // step3, we add a new value during detachment
+ ( ( Value ) definition.getValues().iterator().next() ).getLocalizedStrings().addString( new Locale( "en_US" ), "hello" );
+ Value value2 = new Value( definition );
+ value2.getLocalizedStrings().addString( new Locale( "es" ), "hola" );
+
+ // step4 we merge the definition
+ session = openSession();
+ session.beginTransaction();
+ session.merge( definition );
+ session.getTransaction().commit();
+ session.close();
+
+ // step5, final test
+ session = openSession();
+ session.beginTransaction();
+ definition = ( Definition ) session.get( Definition.class, definition.getId() );
+ assertEquals( 2, definition.getValues().size() );
+ Iterator values = definition.getValues().iterator();
+ while ( values.hasNext() ) {
+ assertEquals( 1, ( ( Value ) values.next() ).getLocalizedStrings().getStringsCopy().size() );
+ }
+ session.getTransaction().commit();
+ session.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/collection/Definition.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/collection/Definition.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/collection/Definition.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,30 @@
+package org.hibernate.test.component.cascading.collection;
+
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class Definition {
+ private Long id;
+ private Set values = new HashSet();
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public Set getValues() {
+ return values;
+ }
+
+ public void setValues(Set values) {
+ this.values = values;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/collection/LocalizedStrings.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/collection/LocalizedStrings.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/collection/LocalizedStrings.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,26 @@
+package org.hibernate.test.component.cascading.collection;
+
+import java.util.Locale;
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class LocalizedStrings {
+ private Map strings = new HashMap();
+
+ public void addString(Locale locale, String value) {
+ strings.put( locale, value );
+ }
+
+ public String getString(Locale locale) {
+ return ( String ) strings.get( locale );
+ }
+
+ public Map getStringsCopy() {
+ return java.util.Collections.unmodifiableMap( strings );
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/collection/Mappings.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/collection/Mappings.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/collection/Mappings.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.component.cascading.collection">
+
+ <class name="Definition" >
+ <id name="id" type="long" column="ID">
+ <generator class="increment"/>
+ </id>
+ <set name="values" cascade="all-delete-orphan,merge" lazy="false" inverse="true">
+ <key column="DEF_ID" />
+ <one-to-many class="Value"/>
+ </set>
+ </class>
+
+ <class name="Value" >
+ <id name="id" type="long" column="ID">
+ <generator class="increment"/>
+ </id>
+
+ <many-to-one name="definition" class="Definition" column="DEF_ID"/>
+
+ <component name="localizedStrings" class="LocalizedStrings">
+ <map name="strings" access="field" cascade="persist,merge" lazy="false">
+ <key column="VAL_ID" />
+ <map-key type="locale" column="LOC" />
+ <element type="string" column="STR_VAL" />
+ </map>
+ </component>
+
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/collection/Value.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/collection/Value.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/collection/Value.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,45 @@
+package org.hibernate.test.component.cascading.collection;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class Value {
+ private Long id;
+ private Definition definition;
+ private LocalizedStrings localizedStrings = new LocalizedStrings();
+
+ private Value() {
+ }
+
+ public Value(Definition definition) {
+ this();
+ this.definition = definition;
+ definition.getValues().add( this );
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public Definition getDefinition() {
+ return definition;
+ }
+
+ public void setDefinition(Definition definition) {
+ this.definition = definition;
+ }
+
+ public LocalizedStrings getLocalizedStrings() {
+ return localizedStrings;
+ }
+
+ public void setLocalizedStrings(LocalizedStrings localizedStrings) {
+ this.localizedStrings = localizedStrings;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/toone/Address.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/toone/Address.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/toone/Address.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,63 @@
+package org.hibernate.test.component.cascading.toone;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class Address {
+ private Long id;
+ private String street1;
+ private String street2;
+ private String city;
+ private String state;
+ private String zipCode;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getStreet1() {
+ return street1;
+ }
+
+ public void setStreet1(String street1) {
+ this.street1 = street1;
+ }
+
+ public String getStreet2() {
+ return street2;
+ }
+
+ public void setStreet2(String street2) {
+ this.street2 = street2;
+ }
+
+ public String getCity() {
+ return city;
+ }
+
+ public void setCity(String city) {
+ this.city = city;
+ }
+
+ public String getState() {
+ return state;
+ }
+
+ public void setState(String state) {
+ this.state = state;
+ }
+
+ public String getZipCode() {
+ return zipCode;
+ }
+
+ public void setZipCode(String zipCode) {
+ this.zipCode = zipCode;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/toone/CascadeToComponentAssociationTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/toone/CascadeToComponentAssociationTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/toone/CascadeToComponentAssociationTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,71 @@
+package org.hibernate.test.component.cascading.toone;
+
+import junit.framework.Test;
+
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.Session;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class CascadeToComponentAssociationTest extends FunctionalTestCase {
+ public CascadeToComponentAssociationTest(String string) {
+ super( string );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "component/cascading/toone/Mappings.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( CascadeToComponentAssociationTest.class );
+ }
+
+ public void testMerging() {
+ // step1, we create a document with owner
+ Session session = openSession();
+ session.beginTransaction();
+ User user = new User();
+ Document document = new Document();
+ document.setOwner( user );
+ session.persist( document );
+ session.getTransaction().commit();
+ session.close();
+
+ // step2, we verify that the document has owner and that owner has no personal-info; then we detach
+ session = openSession();
+ session.beginTransaction();
+ document = ( Document ) session.get( Document.class, document.getId() );
+ assertNotNull( document.getOwner() );
+ assertNull( document.getOwner().getPersonalInfo() );
+ session.getTransaction().commit();
+ session.close();
+
+ // step3, try to specify the personal-info during detachment
+ Address addr = new Address();
+ addr.setStreet1( "123 6th St" );
+ addr.setCity( "Austin" );
+ addr.setState( "TX" );
+ document.getOwner().setPersonalInfo( new PersonalInfo( addr ) );
+
+ // step4 we merge the document
+ session = openSession();
+ session.beginTransaction();
+ session.merge( document );
+ session.getTransaction().commit();
+ session.close();
+
+ // step5, final test
+ session = openSession();
+ session.beginTransaction();
+ document = ( Document ) session.get( Document.class, document.getId() );
+ assertNotNull( document.getOwner() );
+ assertNotNull( document.getOwner().getPersonalInfo() );
+ assertNotNull( document.getOwner().getPersonalInfo().getHomeAddress() );
+ session.getTransaction().commit();
+ session.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/toone/Document.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/toone/Document.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/toone/Document.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,36 @@
+package org.hibernate.test.component.cascading.toone;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class Document {
+ private Long id;
+ private String location;
+ private User owner;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getLocation() {
+ return location;
+ }
+
+ public void setLocation(String location) {
+ this.location = location;
+ }
+
+ public User getOwner() {
+ return owner;
+ }
+
+ public void setOwner(User owner) {
+ this.owner = owner;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/toone/Mappings.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/toone/Mappings.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/toone/Mappings.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.component.cascading.toone">
+
+ <class name="Document" table="COMP_CASC_TO1_DOC">
+ <id name="id" type="long" column="ID">
+ <generator class="increment"/>
+ </id>
+ <many-to-one name="owner" class="User" cascade="persist,merge,delete"/>
+ </class>
+
+ <class name="User" table="COMP_CASC_TO1_USER">
+ <id name="id" type="long" column="ID">
+ <generator class="increment"/>
+ </id>
+ <component name="personalInfo" class="PersonalInfo">
+ <many-to-one name="homeAddress" class="Address" cascade="persist,merge,delete"/>
+ </component>
+ </class>
+
+ <class name="Address" table="COMP_CASC_TO1_ADDR">
+ <id name="id" type="long" column="ID">
+ <generator class="increment"/>
+ </id>
+ <property name="street1" type="string" column="STREET1" />
+ <property name="street2" type="string" column="STREET2" />
+ <property name="city" type="string" column="CITY" />
+ <property name="state" type="string" column="STATE" />
+ <property name="zipCode" type="string" column="ZIP_CODE" />
+ </class>
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/toone/PersonalInfo.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/toone/PersonalInfo.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/toone/PersonalInfo.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,25 @@
+package org.hibernate.test.component.cascading.toone;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class PersonalInfo {
+ private Address homeAddress = new Address();
+
+ public PersonalInfo() {
+ }
+
+ public PersonalInfo(Address homeAddress) {
+ this.homeAddress = homeAddress;
+ }
+
+ public Address getHomeAddress() {
+ return homeAddress;
+ }
+
+ public void setHomeAddress(Address homeAddress) {
+ this.homeAddress = homeAddress;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/toone/User.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/toone/User.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/component/cascading/toone/User.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,27 @@
+package org.hibernate.test.component.cascading.toone;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class User {
+ private Long id;
+ private PersonalInfo personalInfo;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public PersonalInfo getPersonalInfo() {
+ return personalInfo;
+ }
+
+ public void setPersonalInfo(PersonalInfo personalInfo) {
+ this.personalInfo = personalInfo;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/compositeelement/Child.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/compositeelement/Child.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/compositeelement/Child.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,60 @@
+//$Id: Child.java 6978 2005-06-01 03:29:27Z oneovthafew $
+package org.hibernate.test.compositeelement;
+
+/**
+ * @author gavin
+ */
+public class Child {
+ private String name;
+ private String bio;
+ private Parent parent;
+ private int bioLength;
+ Child() {}
+ public Child(String name) {
+ this.name = name;
+ }
+ /**
+ * @return Returns the name.
+ */
+ public String getName() {
+ return name;
+ }
+ /**
+ * @param name The name to set.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+ /**
+ * @return Returns the parent.
+ */
+ public Parent getParent() {
+ return parent;
+ }
+ /**
+ * @param parent The parent to set.
+ */
+ public void setParent(Parent parent) {
+ this.parent = parent;
+ }
+ public String getBio() {
+ return bio;
+ }
+ public void setBio(String bio) {
+ this.bio = bio;
+ }
+ public int hashCode() {
+ return name.hashCode();
+ }
+ public boolean equals(Object other) {
+ Child c = (Child) other;
+ return c.parent.getId().equals(parent.getId())
+ && c.name.equals(name);
+ }
+ public int getBioLength() {
+ return bioLength;
+ }
+ public void setBioLength(Integer bioLength) {
+ this.bioLength = bioLength==null ? 0 : bioLength.intValue();
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/compositeelement/Child.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/compositeelement/CompositeElementTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/compositeelement/CompositeElementTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/compositeelement/CompositeElementTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,90 @@
+//$Id: CompositeElementTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.compositeelement;
+
+import java.util.ArrayList;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Mappings;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.function.SQLFunction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.mapping.Collection;
+import org.hibernate.mapping.Component;
+import org.hibernate.mapping.Formula;
+
+/**
+ * @author Gavin King
+ */
+public class CompositeElementTest extends FunctionalTestCase {
+
+ public CompositeElementTest(String str) {
+ super( str );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "compositeelement/Parent.hbm.xml" };
+ }
+
+ public void afterConfigurationBuilt(Mappings mappings, Dialect dialect) {
+ super.afterConfigurationBuilt( mappings, dialect );
+ Collection children = mappings.getCollection( Parent.class.getName() + ".children" );
+ Component childComponents = ( Component ) children.getElement();
+ Formula f = ( Formula ) childComponents.getProperty( "bioLength" ).getValue().getColumnIterator().next();
+
+ SQLFunction lengthFunction = ( SQLFunction ) dialect.getFunctions().get( "length" );
+ if ( lengthFunction != null ) {
+ ArrayList args = new ArrayList();
+ args.add( "bio" );
+ f.setFormula( lengthFunction.render( args, null ) );
+ }
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( CompositeElementTest.class );
+ }
+
+ public void testHandSQL() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Child c = new Child( "Child One" );
+ Parent p = new Parent( "Parent" );
+ p.getChildren().add( c );
+ c.setParent( p );
+ s.save( p );
+ s.flush();
+
+ p.getChildren().remove( c );
+ c.setParent( null );
+ s.flush();
+
+ p.getChildren().add( c );
+ c.setParent( p );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.createQuery( "select distinct p from Parent p join p.children c where c.name like 'Child%'" ).uniqueResult();
+ s.clear();
+ s.createQuery( "select new Child(c.name) from Parent p left outer join p.children c where c.name like 'Child%'" )
+ .uniqueResult();
+ s.clear();
+ //s.createQuery("select c from Parent p left outer join p.children c where c.name like 'Child%'").uniqueResult(); //we really need to be able to do this!
+ s.clear();
+ p = ( Parent ) s.createQuery( "from Parent p left join fetch p.children" ).uniqueResult();
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.delete( p );
+ t.commit();
+ s.close();
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/compositeelement/CompositeElementTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/compositeelement/Parent.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/compositeelement/Parent.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/compositeelement/Parent.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,36 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+ This mapping demonstrates how to use a composite
+ element mapping to model a parent/child association.
+
+-->
+
+<hibernate-mapping
+ package="org.hibernate.test.compositeelement">
+
+ <import class="Child"/>
+
+ <class name="Parent">
+ <id name="id"
+ column="parent_id">
+ <generator class="increment"/>
+ </id>
+ <property name="name"/>
+ <set name="children" table="ParentChild">
+ <key column="parent_id"/>
+ <composite-element class="Child">
+ <parent name="parent"/>
+ <property name="name" not-null="true"/>
+ <property name="bio"/>
+ <property name="bioLength" formula="length(bio)"/>
+ </composite-element>
+ </set>
+ </class>
+
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/compositeelement/Parent.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/compositeelement/Parent.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/compositeelement/Parent.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/compositeelement/Parent.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,54 @@
+//$Id: Parent.java 4478 2004-09-02 02:30:28Z oneovthafew $
+package org.hibernate.test.compositeelement;
+
+import java.util.Collection;
+import java.util.HashSet;
+
+/**
+ * @author gavin
+ */
+public class Parent {
+ private Long id;
+ private String name;
+ private Collection children = new HashSet();
+ Parent() {}
+ public Parent(String name) {
+ this.name = name;
+ }
+ /**
+ * @return Returns the children.
+ */
+ public Collection getChildren() {
+ return children;
+ }
+ /**
+ * @param children The children to set.
+ */
+ public void setChildren(Collection children) {
+ this.children = children;
+ }
+ /**
+ * @return Returns the id.
+ */
+ public Long getId() {
+ return id;
+ }
+ /**
+ * @param id The id to set.
+ */
+ public void setId(Long id) {
+ this.id = id;
+ }
+ /**
+ * @return Returns the name.
+ */
+ public String getName() {
+ return name;
+ }
+ /**
+ * @param name The name to set.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/compositeelement/Parent.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/AggressiveReleaseTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/AggressiveReleaseTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/AggressiveReleaseTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,249 @@
+// $Id: AggressiveReleaseTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.connections;
+
+import java.sql.Connection;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.hibernate.ConnectionReleaseMode;
+import org.hibernate.Hibernate;
+import org.hibernate.ScrollableResults;
+import org.hibernate.Session;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.impl.SessionImpl;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.test.tm.DummyConnectionProvider;
+import org.hibernate.test.tm.DummyTransactionManager;
+import org.hibernate.test.tm.DummyTransactionManagerLookup;
+import org.hibernate.transaction.CMTTransactionFactory;
+import org.hibernate.util.SerializationHelper;
+
+/**
+ * Implementation of AggressiveReleaseTest.
+ *
+ * @author Steve Ebersole
+ */
+public class AggressiveReleaseTest extends ConnectionManagementTestCase {
+
+ public AggressiveReleaseTest(String name) {
+ super( name );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( AggressiveReleaseTest.class );
+ }
+
+ public void configure(Configuration cfg) {
+ super.configure( cfg );
+ cfg.setProperty( Environment.RELEASE_CONNECTIONS, ConnectionReleaseMode.AFTER_STATEMENT.toString() );
+ cfg.setProperty( Environment.CONNECTION_PROVIDER, DummyConnectionProvider.class.getName() );
+ cfg.setProperty( Environment.TRANSACTION_STRATEGY, CMTTransactionFactory.class.getName() );
+ cfg.setProperty( Environment.TRANSACTION_MANAGER_STRATEGY, DummyTransactionManagerLookup.class.getName() );
+ cfg.setProperty( Environment.GENERATE_STATISTICS, "true" );
+ cfg.setProperty( Environment.STATEMENT_BATCH_SIZE, "0" );
+ }
+
+ protected Session getSessionUnderTest() throws Throwable {
+ return openSession();
+ }
+
+ protected void reconnect(Session session) {
+ session.reconnect();
+ }
+
+ protected void prepare() throws Throwable {
+ DummyTransactionManager.INSTANCE.begin();
+ }
+
+ protected void done() throws Throwable {
+ DummyTransactionManager.INSTANCE.commit();
+ }
+
+ // Some additional tests specifically for the aggressive-release functionality...
+
+ public void testSerializationOnAfterStatementAggressiveRelease() throws Throwable {
+ prepare();
+ Session s = getSessionUnderTest();
+ Silly silly = new Silly( "silly" );
+ s.save( silly );
+
+ // this should cause the CM to obtain a connection, and then release it
+ s.flush();
+
+ // We should be able to serialize the session at this point...
+ SerializationHelper.serialize( s );
+
+ s.delete( silly );
+ s.flush();
+
+ release( s );
+ done();
+ }
+
+ public void testSerializationFailsOnAfterStatementAggressiveReleaseWithOpenResources() throws Throwable {
+ prepare();
+ Session s = getSessionUnderTest();
+
+ Silly silly = new Silly( "silly" );
+ s.save( silly );
+
+ // this should cause the CM to obtain a connection, and then release it
+ s.flush();
+
+ // both scroll() and iterate() cause the batcher to hold on
+ // to resources, which should make aggresive-release not release
+ // the connection (and thus cause serialization to fail)
+ ScrollableResults sr = s.createQuery( "from Silly" ).scroll();
+
+ try {
+ SerializationHelper.serialize( s );
+ fail( "Serialization allowed on connected session; or aggressive release released connection with open resources" );
+ }
+ catch( IllegalStateException e ) {
+ // expected behavior
+ }
+
+ // Closing the ScrollableResults does currently force the batcher to
+ // aggressively release the connection
+ sr.close();
+ SerializationHelper.serialize( s );
+
+ s.delete( silly );
+ s.flush();
+
+ release( s );
+ done();
+ }
+
+ public void testQueryIteration() throws Throwable {
+ prepare();
+ Session s = getSessionUnderTest();
+ Silly silly = new Silly( "silly" );
+ s.save( silly );
+ s.flush();
+
+ Iterator itr = s.createQuery( "from Silly" ).iterate();
+ assertTrue( itr.hasNext() );
+ Silly silly2 = ( Silly ) itr.next();
+ assertEquals( silly, silly2 );
+ Hibernate.close( itr );
+
+ itr = s.createQuery( "from Silly" ).iterate();
+ Iterator itr2 = s.createQuery( "from Silly where name = 'silly'" ).iterate();
+
+ assertTrue( itr.hasNext() );
+ assertEquals( silly, itr.next() );
+ assertTrue( itr2.hasNext() );
+ assertEquals( silly, itr2.next() );
+
+ Hibernate.close( itr );
+ Hibernate.close( itr2 );
+
+ s.delete( silly );
+ s.flush();
+
+ release( s );
+ done();
+ }
+
+ public void testQueryScrolling() throws Throwable {
+ prepare();
+ Session s = getSessionUnderTest();
+ Silly silly = new Silly( "silly" );
+ s.save( silly );
+ s.flush();
+
+ ScrollableResults sr = s.createQuery( "from Silly" ).scroll();
+ assertTrue( sr.next() );
+ Silly silly2 = ( Silly ) sr.get( 0 );
+ assertEquals( silly, silly2 );
+ sr.close();
+
+ sr = s.createQuery( "from Silly" ).scroll();
+ ScrollableResults sr2 = s.createQuery( "from Silly where name = 'silly'" ).scroll();
+
+ assertTrue( sr.next() );
+ assertEquals( silly, sr.get( 0 ) );
+ assertTrue( sr2.next() );
+ assertEquals( silly, sr2.get( 0 ) );
+
+ sr.close();
+ sr2.close();
+
+ s.delete( silly );
+ s.flush();
+
+ release( s );
+ done();
+ }
+
+ public void testSuppliedConnection() throws Throwable {
+ prepare();
+
+ Connection originalConnection = DummyTransactionManager.INSTANCE.getCurrent().getConnection();
+ Session session = getSessions().openSession( originalConnection );
+
+ Silly silly = new Silly( "silly" );
+ session.save( silly );
+
+ // this will cause the connection manager to cycle through the aggressive release logic;
+ // it should not release the connection since we explicitly suplied it ourselves.
+ session.flush();
+
+ assertTrue( "Different connections", originalConnection == session.connection() );
+
+ session.delete( silly );
+ session.flush();
+
+ release( session );
+ done();
+ }
+
+ public void testBorrowedConnections() throws Throwable {
+ prepare();
+ Session s = getSessionUnderTest();
+
+ Connection conn = s.connection();
+ assertTrue( ( ( SessionImpl ) s ).getJDBCContext().getConnectionManager().hasBorrowedConnection() );
+ conn.close();
+ assertFalse( ( ( SessionImpl ) s ).getJDBCContext().getConnectionManager().hasBorrowedConnection() );
+
+ release( s );
+ done();
+ }
+
+ public void testConnectionMaintanenceDuringFlush() throws Throwable {
+ prepare();
+ Session s = getSessionUnderTest();
+ s.beginTransaction();
+
+ List entities = new ArrayList();
+ for ( int i = 0; i < 10; i++ ) {
+ Other other = new Other( "other-" + i );
+ Silly silly = new Silly( "silly-" + i, other );
+ entities.add( silly );
+ s.save( silly );
+ }
+ s.flush();
+
+ Iterator itr = entities.iterator();
+ while ( itr.hasNext() ) {
+ Silly silly = ( Silly ) itr.next();
+ silly.setName( "new-" + silly.getName() );
+ silly.getOther().setName( "new-" + silly.getOther().getName() );
+ }
+ long initialCount = getSessions().getStatistics().getConnectCount();
+ s.flush();
+ assertEquals( "connection not maintained through flush", initialCount + 1, getSessions().getStatistics().getConnectCount() );
+
+ s.createQuery( "delete from Silly" ).executeUpdate();
+ s.createQuery( "delete from Other" ).executeUpdate();
+ s.getTransaction().commit();
+ release( s );
+ done();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/BasicConnectionProviderTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/BasicConnectionProviderTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/BasicConnectionProviderTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,39 @@
+// $Id: BasicConnectionProviderTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.connections;
+
+import junit.framework.Test;
+
+import org.hibernate.ConnectionReleaseMode;
+import org.hibernate.Session;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * Implementation of BasicConnectionProviderTest.
+ *
+ * @author Steve Ebersole
+ */
+public class BasicConnectionProviderTest extends ConnectionManagementTestCase {
+
+ public BasicConnectionProviderTest(String name) {
+ super( name );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( BasicConnectionProviderTest.class );
+ }
+
+ protected Session getSessionUnderTest() {
+ return openSession();
+ }
+
+ protected void reconnect(Session session) {
+ session.reconnect();
+ }
+
+ public void configure(Configuration cfg) {
+ super.configure( cfg );
+ cfg.setProperty( Environment.RELEASE_CONNECTIONS, ConnectionReleaseMode.ON_CLOSE.toString() );
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/ConnectionManagementTestCase.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/ConnectionManagementTestCase.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/ConnectionManagementTestCase.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,307 @@
+// $Id: ConnectionManagementTestCase.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.connections;
+
+import org.hibernate.test.TestCase;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.util.SerializationHelper;
+
+/**
+ * Common test cases relating to session management and how the sessions
+ * manages its underlying jdbc connection across different config
+ * scenarios. The different config scenarios are controlled by the
+ * individual test subclasses.
+ * <p/>
+ * In general, all the tests required are defined here in templated fashion.
+ * Subclassed then override various hook methods specific to their given
+ * scneario being tested.
+ *
+ * @author Steve Ebersole
+ */
+public abstract class ConnectionManagementTestCase extends FunctionalTestCase {
+
+ public ConnectionManagementTestCase(String name) {
+ super( name );
+ }
+
+ public final String[] getMappings() {
+ return new String[] { "connections/Silly.hbm.xml" };
+ }
+
+
+ // hooks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Used to prepare the environment for testing (e.g., starting a
+ * JTA transaction or obtaining a user-supplied connection).
+ *
+ * @throws Throwable indicates problems preparing
+ */
+ protected void prepare() throws Throwable {
+ }
+
+ /**
+ * Used to cleanup the environment after testing (e.g., ending a JTA
+ * transaction or closing a user-supplied connection).
+ *
+ * @throws Throwable indicates problems cleaning up
+ */
+ protected void done() throws Throwable {
+ }
+
+ /**
+ * Used to get a session configured based on the config scenario being
+ * tested.
+ *
+ * @return The session to be used in testing.
+ * @throws Throwable Indicates problems building a test session fixture.
+ */
+ protected abstract Session getSessionUnderTest() throws Throwable;
+
+ /**
+ * Used to release a {@link #getSessionUnderTest fixture session}.
+ * Overridden to perform session releasing/testing specific to the given
+ * config scenario being tested.
+ *
+ * @param session The session to be released.
+ */
+ protected void release(Session session) {
+ if ( session != null && session.isOpen() ) {
+ try {
+ session.close();
+ }
+ catch( Throwable ignore ) {
+ }
+ }
+ }
+
+ /**
+ * Perform any steps needed to reconnect a fixture session.
+ *
+ * @param session The fixture session to be reconnected.
+ * @throws Throwable Indicates problems reconnecting.
+ */
+ protected abstract void reconnect(Session session) throws Throwable;
+
+ /**
+ * Check the state of a fixture session after serialization, as well
+ * as validate the environmental state after session serialization.
+ *
+ * @param session The fixture session that was serialized.
+ */
+ protected void checkSerializedState(Session session) {
+ }
+
+ /**
+ * Check the state of a fixture session after deserialization, as well
+ * as validate the environmental state after session deserialization.
+ *
+ * @param session The fixture session that was deserialized.
+ */
+ protected void checkDeserializedState(Session session) {
+ }
+
+
+ // tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Tests to validate that a session holding JDBC resources will not
+ * be allowed to serialize.
+ *
+ * @throws Throwable
+ */
+ public final void testConnectedSerialization() throws Throwable {
+ prepare();
+ Session sessionUnderTest = getSessionUnderTest();
+
+ // force the connection to be retained
+ sessionUnderTest.createQuery( "from Silly" ).scroll();
+
+ try {
+ SerializationHelper.serialize( sessionUnderTest );
+
+ fail( "Serialization of connected session allowed!" );
+ }
+ catch( IllegalStateException e ) {
+ // expected behaviour
+ }
+ finally {
+ release( sessionUnderTest );
+ done();
+ }
+ }
+
+ /**
+ * Test that a session which has been manually disconnected will be allowed
+ * to serialize.
+ *
+ * @throws Throwable
+ */
+ public final void testManualDisconnectedSerialization() throws Throwable {
+ prepare();
+ Session sessionUnderTest = getSessionUnderTest();
+
+ sessionUnderTest.disconnect();
+
+ SerializationHelper.serialize( sessionUnderTest );
+ checkSerializedState( sessionUnderTest );
+
+ release( sessionUnderTest );
+ done();
+ }
+
+ /**
+ * Test that the legacy manual disconnect()/reconnect() chain works as
+ * expected in the given environment.
+ *
+ * @throws Throwable
+ */
+ public final void testManualDisconnectChain() throws Throwable {
+ prepare();
+ Session sessionUnderTest = getSessionUnderTest();
+
+ sessionUnderTest.disconnect();
+
+ byte[] bytes = SerializationHelper.serialize( sessionUnderTest );
+ checkSerializedState( sessionUnderTest );
+ Session s2 = ( Session ) SerializationHelper.deserialize( bytes );
+ checkDeserializedState( s2 );
+
+ reconnect( s2 );
+
+ s2.disconnect();
+ reconnect( s2 );
+
+ release( sessionUnderTest );
+ release( s2 );
+ done();
+ }
+
+ /**
+ * Test that the legacy manual disconnect()/reconnect() chain works as
+ * expected in the given environment. Similiar to {@link #testManualDisconnectChain}
+ * expect that here we force the session to acquire and hold JDBC resources
+ * prior to disconnecting.
+ *
+ * @throws Throwable
+ */
+ public final void testManualDisconnectWithOpenResources() throws Throwable {
+ prepare();
+ Session sessionUnderTest = getSessionUnderTest();
+
+ Silly silly = new Silly( "tester" );
+ sessionUnderTest.save( silly );
+ sessionUnderTest.flush();
+
+ sessionUnderTest.createQuery( "from Silly" ).iterate();
+
+ sessionUnderTest.disconnect();
+ SerializationHelper.serialize( sessionUnderTest );
+ checkSerializedState( sessionUnderTest );
+
+ reconnect( sessionUnderTest );
+ sessionUnderTest.createQuery( "from Silly" ).scroll();
+
+ sessionUnderTest.disconnect();
+ SerializationHelper.serialize( sessionUnderTest );
+ checkSerializedState( sessionUnderTest );
+
+ reconnect( sessionUnderTest );
+ sessionUnderTest.delete( silly );
+ sessionUnderTest.flush();
+
+ release( sessionUnderTest );
+ done();
+ }
+
+ /**
+ * Test that the basic session usage template works in all environment
+ * scenarios.
+ *
+ * @throws Throwable
+ */
+ public void testBasicSessionUsage() throws Throwable {
+ prepare();
+ Session s = null;
+ Transaction txn = null;
+ try {
+ s = getSessionUnderTest();
+ txn = s.beginTransaction();
+ s.createQuery( "from Silly" ).list();
+ txn.commit();
+ }
+ catch( Throwable t ) {
+ if ( txn != null ) {
+ try {
+ txn.rollback();
+ }
+ catch( Throwable ignore ) {
+ }
+ }
+ }
+ finally {
+ if ( s != null && s.isOpen() ) {
+ try {
+ s.close();
+ }
+ catch( Throwable ignore ) {
+ }
+ }
+ }
+ done();
+ }
+
+ /**
+ * Test that session-closed protections work properly in all environments.
+ *
+ * @throws Throwable
+ */
+ public void testSessionClosedProtections() throws Throwable {
+ prepare();
+ Session s = getSessionUnderTest();
+ release( s );
+ done();
+ assertFalse( s.isOpen() );
+ assertFalse( s.isConnected() );
+ assertNotNull( s.getStatistics() );
+ assertNotNull( s.toString() );
+
+ try {
+ s.createQuery( "from Silly" ).list();
+ fail( "allowed to create query on closed session" );
+ }
+ catch( Throwable ignore ) {
+ }
+
+ try {
+ s.getTransaction();
+ fail( "allowed to access transaction on closed session" );
+ }
+ catch( Throwable ignore ) {
+ }
+
+ try {
+ s.connection();
+ fail( "allowed to access connection on closed session" );
+ }
+ catch( Throwable ignore ) {
+ }
+
+ try {
+ s.close();
+ fail( "allowed to close already closed session" );
+ }
+ catch( Throwable ignore ) {
+ }
+
+ try {
+ s.isDirty();
+ fail( "allowed to check dirtiness of closed session" );
+ }
+ catch( Throwable ignore ) {
+ }
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/ConnectionsSuite.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/ConnectionsSuite.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/ConnectionsSuite.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,23 @@
+// $Id: ConnectionsSuite.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.connections;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * Implementation of ConnectionsSuite.
+ *
+ * @author Steve Ebersole
+ */
+public class ConnectionsSuite {
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite( "Connection-management tests");
+ suite.addTest( AggressiveReleaseTest.suite() );
+ suite.addTest( BasicConnectionProviderTest.suite() );
+ suite.addTest( CurrentSessionConnectionTest.suite() );
+ suite.addTest( SuppliedConnectionTest.suite() );
+ suite.addTest( ThreadLocalCurrentSessionTest.suite() );
+ return suite;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/CurrentSessionConnectionTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/CurrentSessionConnectionTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/CurrentSessionConnectionTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,31 @@
+// $Id: CurrentSessionConnectionTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.connections;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * Implementation of CurrentSessionConnectionTest.
+ *
+ * @author Steve Ebersole
+ */
+public class CurrentSessionConnectionTest extends AggressiveReleaseTest {
+
+ public CurrentSessionConnectionTest(String name) {
+ super( name );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( CurrentSessionConnectionTest.class );
+ }
+
+ protected Session getSessionUnderTest() throws Throwable {
+ return getSessions().getCurrentSession();
+ }
+
+ protected void release(Session session) {
+ // do nothing, txn synch should release session as part of current-session definition
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/Other.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/Other.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/Other.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,32 @@
+package org.hibernate.test.connections;
+
+/**
+ * @author Steve Ebersole
+ */
+public class Other {
+ private Long id;
+ private String name;
+
+ public Other() {
+ }
+
+ public Other(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/testsuite/src/test/java/org/hibernate/test/connections/Silly.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/Silly.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/Silly.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.connections">
+
+ <class name="Silly">
+ <id name="id" type="long">
+ <generator class="native"/>
+ </id>
+ <property name="name"/>
+ <many-to-one name="other" class="Other" cascade="all"/>
+ </class>
+
+ <class name="Other">
+ <id name="id" type="long">
+ <generator class="native"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/Silly.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/Silly.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/Silly.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,51 @@
+// $Id: Silly.java 9595 2006-03-10 18:14:21Z steve.ebersole at jboss.com $
+package org.hibernate.test.connections;
+
+import java.io.Serializable;
+
+/**
+ * Implementation of Silly.
+ *
+ * @author Steve Ebersole
+ */
+public class Silly implements Serializable {
+ private Long id;
+ private String name;
+ private Other other;
+
+ public Silly() {
+ }
+
+ public Silly(String name) {
+ this.name = name;
+ }
+
+ public Silly(String name, Other other) {
+ this.name = name;
+ this.other = other;
+ }
+
+ 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 Other getOther() {
+ return other;
+ }
+
+ public void setOther(Other other) {
+ this.other = other;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/SuppliedConnectionTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/SuppliedConnectionTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/SuppliedConnectionTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,121 @@
+// $Id: SuppliedConnectionTest.java 11332 2007-03-22 17:34:55Z steve.ebersole at jboss.com $
+package org.hibernate.test.connections;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+
+import junit.framework.Test;
+
+import org.hibernate.ConnectionReleaseMode;
+import org.hibernate.Session;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.connection.ConnectionProvider;
+import org.hibernate.connection.ConnectionProviderFactory;
+import org.hibernate.connection.UserSuppliedConnectionProvider;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.tool.hbm2ddl.SchemaExport;
+
+/**
+ * Implementation of SuppliedConnectionTest.
+ *
+ * @author Steve Ebersole
+ */
+public class SuppliedConnectionTest extends ConnectionManagementTestCase {
+
+ private ConnectionProvider cp = ConnectionProviderFactory.newConnectionProvider();
+ private Connection connectionUnderTest;
+
+ public SuppliedConnectionTest(String name) {
+ super( name );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( SuppliedConnectionTest.class );
+ }
+
+ protected Session getSessionUnderTest() throws Throwable {
+ connectionUnderTest = cp.getConnection();
+ return getSessions().openSession( connectionUnderTest );
+ }
+
+ protected void reconnect(Session session) {
+ session.reconnect( connectionUnderTest );
+ }
+
+ protected void done() throws Throwable {
+ cp.closeConnection( connectionUnderTest );
+ }
+
+ public void configure(Configuration cfg) {
+ super.configure( cfg );
+ cfg.setProperty( Environment.RELEASE_CONNECTIONS, ConnectionReleaseMode.ON_CLOSE.toString() );
+ cfg.setProperty( Environment.CONNECTION_PROVIDER, UserSuppliedConnectionProvider.class.getName() );
+ boolean supportsScroll = true;
+ Connection conn = null;
+ try {
+ conn = cp.getConnection();
+ supportsScroll = conn.getMetaData().supportsResultSetType(ResultSet.TYPE_SCROLL_INSENSITIVE);
+ }
+ catch( Throwable ignore ) {
+ }
+ finally {
+ if ( conn != null ) {
+ try {
+ conn.close();
+ }
+ catch( Throwable ignore ) {
+ // ignore it...
+ }
+ }
+ }
+ cfg.setProperty( Environment.USE_SCROLLABLE_RESULTSET, "" + supportsScroll );
+ }
+
+ public boolean createSchema() {
+ return false;
+ }
+
+ public boolean recreateSchemaAfterFailure() {
+ return false;
+ }
+
+ protected void prepareTest() throws Exception {
+ super.prepareTest();
+ Connection conn = cp.getConnection();
+ try {
+ new SchemaExport( getCfg(), conn ).create( false, true );
+ }
+ finally {
+ if ( conn != null ) {
+ try {
+ cp.closeConnection( conn );
+ }
+ catch( Throwable ignore ) {
+ }
+ }
+ }
+ }
+
+ protected void cleanupTest() throws Exception {
+ Connection conn = cp.getConnection();
+ try {
+ new SchemaExport( getCfg(), conn ).drop( false, true );
+ }
+ finally {
+ if ( conn != null ) {
+ try {
+ cp.closeConnection( conn );
+ }
+ catch( Throwable ignore ) {
+ }
+ }
+ }
+ try {
+ cp.close();
+ }
+ catch( Throwable ignore ) {
+ }
+ super.cleanupTest();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/ThreadLocalCurrentSessionTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/ThreadLocalCurrentSessionTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/connections/ThreadLocalCurrentSessionTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,102 @@
+package org.hibernate.test.connections;
+
+import junit.framework.Test;
+
+import org.hibernate.HibernateException;
+import org.hibernate.Session;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.context.ThreadLocalSessionContext;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Steve Ebersole
+ */
+public class ThreadLocalCurrentSessionTest extends ConnectionManagementTestCase {
+
+ public ThreadLocalCurrentSessionTest(String name) {
+ super( name );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( ThreadLocalCurrentSessionTest.class );
+ }
+
+ public void configure(Configuration cfg) {
+ super.configure( cfg );
+ cfg.setProperty( Environment.CURRENT_SESSION_CONTEXT_CLASS, TestableThreadLocalContext.class.getName() );
+ cfg.setProperty( Environment.GENERATE_STATISTICS, "true" );
+ }
+
+ protected Session getSessionUnderTest() throws Throwable {
+ Session session = getSessions().getCurrentSession();
+ session.beginTransaction();
+ return session;
+ }
+
+ protected void release(Session session) {
+ long initialCount = getSessions().getStatistics().getSessionCloseCount();
+ session.getTransaction().commit();
+ long subsequentCount = getSessions().getStatistics().getSessionCloseCount();
+ assertEquals( "Session still open after commit", initialCount + 1, subsequentCount );
+ // also make sure it was cleaned up from the internal ThreadLocal...
+ assertFalse( "session still bound to internal ThreadLocal", TestableThreadLocalContext.hasBind() );
+ }
+
+ protected void reconnect(Session session) throws Throwable {
+// session.reconnect();
+ session.beginTransaction();
+ }
+
+ protected void checkSerializedState(Session session) {
+ assertFalse( "session still bound after serialize", TestableThreadLocalContext.isSessionBound( session ) );
+ }
+
+ protected void checkDeserializedState(Session session) {
+ assertTrue( "session not bound after deserialize", TestableThreadLocalContext.isSessionBound( session ) );
+ }
+
+ public void testTransactionProtection() {
+ Session session = getSessions().getCurrentSession();
+ try {
+ session.createQuery( "from Silly" );
+ fail( "method other than beginTransaction{} allowed" );
+ }
+ catch ( HibernateException e ) {
+ // ok
+ }
+ }
+
+ public void testContextCleanup() {
+ Session session = getSessions().getCurrentSession();
+ session.beginTransaction();
+ session.getTransaction().commit();
+ assertFalse( "session open after txn completion", session.isOpen() );
+ assertFalse( "session still bound after txn completion", TestableThreadLocalContext.isSessionBound( session ) );
+
+ Session session2 = getSessions().getCurrentSession();
+ assertFalse( "same session returned after txn completion", session == session2 );
+ session2.close();
+ assertFalse( "session open after closing", session2.isOpen() );
+ assertFalse( "session still bound after closing", TestableThreadLocalContext.isSessionBound( session2 ) );
+ }
+
+ public static class TestableThreadLocalContext extends ThreadLocalSessionContext {
+ private static TestableThreadLocalContext me;
+
+ public TestableThreadLocalContext(SessionFactoryImplementor factory) {
+ super( factory );
+ me = this;
+ }
+
+ public static boolean isSessionBound(Session session) {
+ return sessionMap() != null && sessionMap().containsKey( me.factory )
+ && sessionMap().get( me.factory ) == session;
+ }
+
+ public static boolean hasBind() {
+ return sessionMap() != null && sessionMap().containsKey( me.factory );
+ }
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/criteria/Course.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/criteria/Course.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/criteria/Course.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+//$Id: Course.java 5686 2005-02-12 07:27:32Z steveebersole $
+package org.hibernate.test.criteria;
+
+/**
+ * @author Gavin King
+ */
+public class Course {
+ private String courseCode;
+ private String description;
+ public String getCourseCode() {
+ return courseCode;
+ }
+ public void setCourseCode(String courseCode) {
+ this.courseCode = courseCode;
+ }
+ public String getDescription() {
+ return description;
+ }
+ public void setDescription(String description) {
+ this.description = description;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/criteria/Course.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/criteria/CriteriaQueryTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/criteria/CriteriaQueryTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/criteria/CriteriaQueryTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,800 @@
+//$Id: CriteriaQueryTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.criteria;
+
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.Test;
+
+import org.hibernate.Criteria;
+import org.hibernate.FetchMode;
+import org.hibernate.Hibernate;
+import org.hibernate.ScrollableResults;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.criterion.DetachedCriteria;
+import org.hibernate.criterion.Example;
+import org.hibernate.criterion.Expression;
+import org.hibernate.criterion.MatchMode;
+import org.hibernate.criterion.Order;
+import org.hibernate.criterion.Projection;
+import org.hibernate.criterion.Projections;
+import org.hibernate.criterion.Property;
+import org.hibernate.criterion.Restrictions;
+import org.hibernate.criterion.Subqueries;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.test.hql.Animal;
+import org.hibernate.test.hql.Reptile;
+import org.hibernate.transform.Transformers;
+import org.hibernate.type.Type;
+import org.hibernate.util.SerializationHelper;
+
+/**
+ * @author Gavin King
+ */
+public class CriteriaQueryTest extends FunctionalTestCase {
+
+ public CriteriaQueryTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "criteria/Enrolment.hbm.xml", "hql/Animal.hbm.xml" };
+ }
+
+ public void configure(Configuration cfg) {
+ super.configure( cfg );
+ cfg.setProperty( Environment.USE_QUERY_CACHE, "true" );
+ cfg.setProperty( Environment.CACHE_REGION_PREFIX, "criteriaquerytest" );
+ cfg.setProperty( Environment.USE_SECOND_LEVEL_CACHE, "true" );
+ cfg.setProperty( Environment.GENERATE_STATISTICS, "true" );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( CriteriaQueryTest.class );
+ }
+
+ public void testEscapeCharacter() {
+ Session session = openSession();
+ Transaction t = session.beginTransaction();
+ Course c1 = new Course();
+ c1.setCourseCode( "course-1" );
+ c1.setDescription( "%1" );
+ Course c2 = new Course();
+ c2.setCourseCode( "course-2" );
+ c2.setDescription( "%2" );
+ Course c3 = new Course();
+ c3.setCourseCode( "course-3" );
+ c3.setDescription( "control" );
+ session.persist( c1 );
+ session.persist( c2 );
+ session.persist( c3 );
+ session.flush();
+ session.clear();
+
+ // finds all courses which have a description equal to '%1'
+ Course example = new Course();
+ example.setDescription( "&%1" );
+ List result = session.createCriteria( Course.class )
+ .add( Example.create( example ).ignoreCase().enableLike().setEscapeCharacter( new Character( '&' ) ) )
+ .list();
+ assertEquals( 1, result.size() );
+ // finds all courses which contain '%' as the first char in the description
+ example.setDescription( "&%%" );
+ result = session.createCriteria( Course.class )
+ .add( Example.create( example ).ignoreCase().enableLike().setEscapeCharacter( new Character( '&' ) ) )
+ .list();
+ assertEquals( 2, result.size() );
+
+ session.createQuery( "delete Course" ).executeUpdate();
+ t.commit();
+ session.close();
+ }
+
+ public void testScrollCriteria() {
+ Session session = openSession();
+ Transaction t = session.beginTransaction();
+
+ Course course = new Course();
+ course.setCourseCode("HIB");
+ course.setDescription("Hibernate Training");
+ session.persist(course);
+ session.flush();
+ session.clear();
+ ScrollableResults sr = session.createCriteria(Course.class).scroll();
+ assertTrue( sr.next() );
+ course = (Course) sr.get(0);
+ assertNotNull(course);
+ sr.close();
+ session.delete(course);
+
+ t.commit();
+ session.close();
+
+ }
+
+ public void testSubselect() {
+
+ Session session = openSession();
+ Transaction t = session.beginTransaction();
+
+ Course course = new Course();
+ course.setCourseCode("HIB");
+ course.setDescription("Hibernate Training");
+ session.persist(course);
+
+ Student gavin = new Student();
+ gavin.setName("Gavin King");
+ gavin.setStudentNumber(232);
+ session.persist(gavin);
+
+ Enrolment enrolment2 = new Enrolment();
+ enrolment2.setCourse(course);
+ enrolment2.setCourseCode(course.getCourseCode());
+ enrolment2.setSemester((short) 3);
+ enrolment2.setYear((short) 1998);
+ enrolment2.setStudent(gavin);
+ enrolment2.setStudentNumber(gavin.getStudentNumber());
+ gavin.getEnrolments().add(enrolment2);
+ session.persist(enrolment2);
+
+ DetachedCriteria dc = DetachedCriteria.forClass(Student.class)
+ .add( Property.forName("studentNumber").eq( new Long(232) ) )
+ .setProjection( Property.forName("name") );
+
+ session.createCriteria(Student.class)
+ .add( Subqueries.propertyEqAll("name", dc) )
+ .list();
+
+ session.createCriteria(Student.class)
+ .add( Subqueries.exists(dc) )
+ .list();
+
+ session.createCriteria(Student.class)
+ .add( Property.forName("name").eqAll(dc) )
+ .list();
+
+ session.createCriteria(Student.class)
+ .add( Subqueries.in("Gavin King", dc) )
+ .list();
+
+ DetachedCriteria dc2 = DetachedCriteria.forClass(Student.class, "st")
+ .add( Property.forName("st.studentNumber").eqProperty("e.studentNumber") )
+ .setProjection( Property.forName("name") );
+
+ session.createCriteria(Enrolment.class, "e")
+ .add( Subqueries.eq("Gavin King", dc2) )
+ .list();
+
+ //TODO: join in subselect: HHH-952
+ /*DetachedCriteria dc3 = DetachedCriteria.forClass(Student.class, "st")
+ .createCriteria("enrolments")
+ .createCriteria("course")
+ .add( Property.forName("description").eq("Hibernate Training") )
+ .setProjection( Property.forName("st.name") );
+
+ session.createCriteria(Enrolment.class, "e")
+ .add( Subqueries.eq("Gavin King", dc3) )
+ .list();*/
+
+ session.delete(enrolment2);
+ session.delete(gavin);
+ session.delete(course);
+ t.commit();
+ session.close();
+
+ }
+
+ public void testDetachedCriteria() {
+
+ DetachedCriteria dc = DetachedCriteria.forClass(Student.class)
+ .add( Property.forName("name").eq("Gavin King") )
+ .addOrder( Order.asc("studentNumber") )
+ .setProjection( Property.forName("studentNumber") );
+
+ byte[] bytes = SerializationHelper.serialize(dc);
+
+ dc = (DetachedCriteria) SerializationHelper.deserialize(bytes);
+
+ Session session = openSession();
+ Transaction t = session.beginTransaction();
+
+ Student gavin = new Student();
+ gavin.setName("Gavin King");
+ gavin.setStudentNumber(232);
+ Student bizarroGavin = new Student();
+ bizarroGavin.setName("Gavin King");
+ bizarroGavin.setStudentNumber(666);
+ session.persist(bizarroGavin);
+ session.persist(gavin);
+
+ List result = dc.getExecutableCriteria(session)
+ .setMaxResults(3)
+ .list();
+
+ assertEquals( result.size(), 2 );
+ assertEquals( result.get(0), new Long(232) );
+ assertEquals( result.get(1), new Long(666) );
+
+ session.delete(gavin);
+ session.delete(bizarroGavin);
+ t.commit();
+ session.close();
+ }
+
+ public void testProjectionCache() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ Course course = new Course();
+ course.setCourseCode("HIB");
+ course.setDescription("Hibernate Training");
+ s.save(course);
+
+ Student gavin = new Student();
+ gavin.setName("Gavin King");
+ gavin.setStudentNumber(666);
+ s.save(gavin);
+
+ Student xam = new Student();
+ xam.setName("Max Rydahl Andersen");
+ xam.setStudentNumber(101);
+ s.save(xam);
+
+ Enrolment enrolment1 = new Enrolment();
+ enrolment1.setCourse(course);
+ enrolment1.setCourseCode(course.getCourseCode());
+ enrolment1.setSemester((short) 1);
+ enrolment1.setYear((short) 1999);
+ enrolment1.setStudent(xam);
+ enrolment1.setStudentNumber(xam.getStudentNumber());
+ xam.getEnrolments().add(enrolment1);
+ s.save(enrolment1);
+
+ Enrolment enrolment2 = new Enrolment();
+ enrolment2.setCourse(course);
+ enrolment2.setCourseCode(course.getCourseCode());
+ enrolment2.setSemester((short) 3);
+ enrolment2.setYear((short) 1998);
+ enrolment2.setStudent(gavin);
+ enrolment2.setStudentNumber(gavin.getStudentNumber());
+ gavin.getEnrolments().add(enrolment2);
+ s.save(enrolment2);
+
+ List list = s.createCriteria(Enrolment.class)
+ .createAlias("student", "s")
+ .createAlias("course", "c")
+ .add( Restrictions.isNotEmpty("s.enrolments") )
+ .setProjection( Projections.projectionList()
+ .add( Projections.property("s.name") )
+ .add( Projections.property("c.description") )
+ )
+ .setCacheable(true)
+ .list();
+
+ assertEquals( list.size(), 2 );
+ assertEquals( ( (Object[]) list.get(0) ).length, 2 );
+ assertEquals( ( (Object[]) list.get(1) ).length, 2 );
+
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+
+ s.createCriteria(Enrolment.class)
+ .createAlias("student", "s")
+ .createAlias("course", "c")
+ .add( Restrictions.isNotEmpty("s.enrolments") )
+ .setProjection( Projections.projectionList()
+ .add( Projections.property("s.name") )
+ .add( Projections.property("c.description") )
+ )
+ .setCacheable(true)
+ .list();
+
+ assertEquals( list.size(), 2 );
+ assertEquals( ( (Object[]) list.get(0) ).length, 2 );
+ assertEquals( ( (Object[]) list.get(1) ).length, 2 );
+
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+
+ s.createCriteria(Enrolment.class)
+ .createAlias("student", "s")
+ .createAlias("course", "c")
+ .add( Restrictions.isNotEmpty("s.enrolments") )
+ .setProjection( Projections.projectionList()
+ .add( Projections.property("s.name") )
+ .add( Projections.property("c.description") )
+ )
+ .setCacheable(true)
+ .list();
+
+ assertEquals( list.size(), 2 );
+ assertEquals( ( (Object[]) list.get(0) ).length, 2 );
+ assertEquals( ( (Object[]) list.get(1) ).length, 2 );
+
+ s.delete(enrolment1);
+ s.delete(enrolment2);
+ s.delete(course);
+ s.delete(gavin);
+ s.delete(xam);
+
+ t.commit();
+ s.close();
+ }
+
+ public void testProjections() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ Course course = new Course();
+ course.setCourseCode("HIB");
+ course.setDescription("Hibernate Training");
+ s.save(course);
+
+ Student gavin = new Student();
+ gavin.setName("Gavin King");
+ gavin.setStudentNumber(667);
+ s.save(gavin);
+
+ Student xam = new Student();
+ xam.setName("Max Rydahl Andersen");
+ xam.setStudentNumber(101);
+ s.save(xam);
+
+ Enrolment enrolment = new Enrolment();
+ enrolment.setCourse(course);
+ enrolment.setCourseCode(course.getCourseCode());
+ enrolment.setSemester((short) 1);
+ enrolment.setYear((short) 1999);
+ enrolment.setStudent(xam);
+ enrolment.setStudentNumber(xam.getStudentNumber());
+ xam.getEnrolments().add(enrolment);
+ s.save(enrolment);
+
+ enrolment = new Enrolment();
+ enrolment.setCourse(course);
+ enrolment.setCourseCode(course.getCourseCode());
+ enrolment.setSemester((short) 3);
+ enrolment.setYear((short) 1998);
+ enrolment.setStudent(gavin);
+ enrolment.setStudentNumber(gavin.getStudentNumber());
+ gavin.getEnrolments().add(enrolment);
+ s.save(enrolment);
+
+ //s.flush();
+
+ Integer count = (Integer) s.createCriteria(Enrolment.class)
+ .setProjection( Projections.count("studentNumber").setDistinct() )
+ .uniqueResult();
+ assertEquals(count, new Integer(2));
+
+ Object object = s.createCriteria(Enrolment.class)
+ .setProjection( Projections.projectionList()
+ .add( Projections.count("studentNumber") )
+ .add( Projections.max("studentNumber") )
+ .add( Projections.min("studentNumber") )
+ .add( Projections.avg("studentNumber") )
+ )
+ .uniqueResult();
+ Object[] result = (Object[])object;
+
+ assertEquals(new Integer(2),result[0]);
+ assertEquals(new Long(667),result[1]);
+ assertEquals(new Long(101),result[2]);
+ assertEquals( 384.0, ( (Double) result[3] ).doubleValue(), 0.01 );
+
+
+ List resultWithMaps = s.createCriteria(Enrolment.class)
+ .setProjection( Projections.distinct( Projections.projectionList()
+ .add( Projections.property("studentNumber"), "stNumber" )
+ .add( Projections.property("courseCode"), "cCode" ) )
+ )
+ .add( Expression.gt( "studentNumber", new Long(665) ) )
+ .add( Expression.lt( "studentNumber", new Long(668) ) )
+ .addOrder( Order.asc("stNumber") )
+ .setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP)
+ .list();
+
+ assertEquals(1, resultWithMaps.size());
+ Map m1 = (Map) resultWithMaps.get(0);
+
+ assertEquals(new Long(667), m1.get("stNumber"));
+ assertEquals(course.getCourseCode(), m1.get("cCode"));
+
+ resultWithMaps = s.createCriteria(Enrolment.class)
+ .setProjection( Projections.property("studentNumber").as("stNumber") )
+ .addOrder( Order.desc("stNumber") )
+ .setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP)
+ .list();
+
+ assertEquals(2, resultWithMaps.size());
+ Map m0 = (Map) resultWithMaps.get(0);
+ m1 = (Map) resultWithMaps.get(1);
+
+ assertEquals(new Long(101), m1.get("stNumber"));
+ assertEquals(new Long(667), m0.get("stNumber"));
+
+
+ List resultWithAliasedBean = s.createCriteria(Enrolment.class)
+ .createAlias("student", "st")
+ .createAlias("course", "co")
+ .setProjection( Projections.projectionList()
+ .add( Projections.property("st.name"), "studentName" )
+ .add( Projections.property("co.description"), "courseDescription" )
+ )
+ .addOrder( Order.desc("studentName") )
+ .setResultTransformer( Transformers.aliasToBean(StudentDTO.class) )
+ .list();
+
+ assertEquals(2, resultWithAliasedBean.size());
+
+ StudentDTO dto = (StudentDTO) resultWithAliasedBean.get(0);
+ assertNotNull(dto.getDescription());
+ assertNotNull(dto.getName());
+
+ s.createCriteria(Student.class)
+ .add( Restrictions.like("name", "Gavin", MatchMode.START) )
+ .addOrder( Order.asc("name") )
+ .createCriteria("enrolments", "e")
+ .addOrder( Order.desc("year") )
+ .addOrder( Order.desc("semester") )
+ .createCriteria("course","c")
+ .addOrder( Order.asc("description") )
+ .setProjection( Projections.projectionList()
+ .add( Projections.property("this.name") )
+ .add( Projections.property("e.year") )
+ .add( Projections.property("e.semester") )
+ .add( Projections.property("c.courseCode") )
+ .add( Projections.property("c.description") )
+ )
+ .uniqueResult();
+
+ Projection p1 = Projections.projectionList()
+ .add( Projections.count("studentNumber") )
+ .add( Projections.max("studentNumber") )
+ .add( Projections.rowCount() );
+
+ Projection p2 = Projections.projectionList()
+ .add( Projections.min("studentNumber") )
+ .add( Projections.avg("studentNumber") )
+ .add( Projections.sqlProjection(
+ "1 as constOne, count(*) as countStar",
+ new String[] { "constOne", "countStar" },
+ new Type[] { Hibernate.INTEGER, Hibernate.INTEGER }
+ ) );
+
+ Object[] array = (Object[]) s.createCriteria(Enrolment.class)
+ .setProjection( Projections.projectionList().add(p1).add(p2) )
+ .uniqueResult();
+
+ assertEquals( array.length, 7 );
+
+ List list = s.createCriteria(Enrolment.class)
+ .createAlias("student", "st")
+ .createAlias("course", "co")
+ .setProjection( Projections.projectionList()
+ .add( Projections.groupProperty("co.courseCode") )
+ .add( Projections.count("st.studentNumber").setDistinct() )
+ .add( Projections.groupProperty("year") )
+ )
+ .list();
+
+ assertEquals( list.size(), 2 );
+
+ Object g = s.createCriteria(Student.class)
+ .add( Restrictions.idEq( new Long(667) ) )
+ .setFetchMode("enrolments", FetchMode.JOIN)
+ //.setFetchMode("enrolments.course", FetchMode.JOIN) //TODO: would love to make that work...
+ .uniqueResult();
+ assertSame(g, gavin);
+
+ s.delete(gavin);
+ s.delete(xam);
+ s.delete(course);
+
+ t.commit();
+ s.close();
+ }
+
+ public void testProjectionsUsingProperty() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ Course course = new Course();
+ course.setCourseCode("HIB");
+ course.setDescription("Hibernate Training");
+ s.save(course);
+
+ Student gavin = new Student();
+ gavin.setName("Gavin King");
+ gavin.setStudentNumber(667);
+ s.save(gavin);
+
+ Student xam = new Student();
+ xam.setName("Max Rydahl Andersen");
+ xam.setStudentNumber(101);
+ s.save(xam);
+
+ Enrolment enrolment = new Enrolment();
+ enrolment.setCourse(course);
+ enrolment.setCourseCode(course.getCourseCode());
+ enrolment.setSemester((short) 1);
+ enrolment.setYear((short) 1999);
+ enrolment.setStudent(xam);
+ enrolment.setStudentNumber(xam.getStudentNumber());
+ xam.getEnrolments().add(enrolment);
+ s.save(enrolment);
+
+ enrolment = new Enrolment();
+ enrolment.setCourse(course);
+ enrolment.setCourseCode(course.getCourseCode());
+ enrolment.setSemester((short) 3);
+ enrolment.setYear((short) 1998);
+ enrolment.setStudent(gavin);
+ enrolment.setStudentNumber(gavin.getStudentNumber());
+ gavin.getEnrolments().add(enrolment);
+ s.save(enrolment);
+
+ s.flush();
+
+ Integer count = (Integer) s.createCriteria(Enrolment.class)
+ .setProjection( Property.forName("studentNumber").count().setDistinct() )
+ .uniqueResult();
+ assertEquals(count, new Integer(2));
+
+ Object object = s.createCriteria(Enrolment.class)
+ .setProjection( Projections.projectionList()
+ .add( Property.forName("studentNumber").count() )
+ .add( Property.forName("studentNumber").max() )
+ .add( Property.forName("studentNumber").min() )
+ .add( Property.forName("studentNumber").avg() )
+ )
+ .uniqueResult();
+ Object[] result = (Object[])object;
+
+ assertEquals(new Integer(2),result[0]);
+ assertEquals(new Long(667),result[1]);
+ assertEquals(new Long(101),result[2]);
+ assertEquals(384.0, ( (Double) result[3] ).doubleValue(), 0.01);
+
+
+ s.createCriteria(Enrolment.class)
+ .add( Property.forName("studentNumber").gt( new Long(665) ) )
+ .add( Property.forName("studentNumber").lt( new Long(668) ) )
+ .add( Property.forName("courseCode").like("HIB", MatchMode.START) )
+ .add( Property.forName("year").eq( new Short( (short) 1999 ) ) )
+ .addOrder( Property.forName("studentNumber").asc() )
+ .uniqueResult();
+
+ List resultWithMaps = s.createCriteria(Enrolment.class)
+ .setProjection( Projections.projectionList()
+ .add( Property.forName("studentNumber").as("stNumber") )
+ .add( Property.forName("courseCode").as("cCode") )
+ )
+ .add( Property.forName("studentNumber").gt( new Long(665) ) )
+ .add( Property.forName("studentNumber").lt( new Long(668) ) )
+ .addOrder( Property.forName("studentNumber").asc() )
+ .setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP)
+ .list();
+
+ assertEquals(1, resultWithMaps.size());
+ Map m1 = (Map) resultWithMaps.get(0);
+
+ assertEquals(new Long(667), m1.get("stNumber"));
+ assertEquals(course.getCourseCode(), m1.get("cCode"));
+
+ resultWithMaps = s.createCriteria(Enrolment.class)
+ .setProjection( Property.forName("studentNumber").as("stNumber") )
+ .addOrder( Order.desc("stNumber") )
+ .setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP)
+ .list();
+
+ assertEquals(2, resultWithMaps.size());
+ Map m0 = (Map) resultWithMaps.get(0);
+ m1 = (Map) resultWithMaps.get(1);
+
+ assertEquals(new Long(101), m1.get("stNumber"));
+ assertEquals(new Long(667), m0.get("stNumber"));
+
+
+ List resultWithAliasedBean = s.createCriteria(Enrolment.class)
+ .createAlias("student", "st")
+ .createAlias("course", "co")
+ .setProjection( Projections.projectionList()
+ .add( Property.forName("st.name").as("studentName") )
+ .add( Property.forName("co.description").as("courseDescription") )
+ )
+ .addOrder( Order.desc("studentName") )
+ .setResultTransformer( Transformers.aliasToBean(StudentDTO.class) )
+ .list();
+
+ assertEquals(2, resultWithAliasedBean.size());
+
+ StudentDTO dto = (StudentDTO) resultWithAliasedBean.get(0);
+ assertNotNull(dto.getDescription());
+ assertNotNull(dto.getName());
+
+ s.createCriteria(Student.class)
+ .add( Restrictions.like("name", "Gavin", MatchMode.START) )
+ .addOrder( Order.asc("name") )
+ .createCriteria("enrolments", "e")
+ .addOrder( Order.desc("year") )
+ .addOrder( Order.desc("semester") )
+ .createCriteria("course","c")
+ .addOrder( Order.asc("description") )
+ .setProjection( Projections.projectionList()
+ .add( Property.forName("this.name") )
+ .add( Property.forName("e.year") )
+ .add( Property.forName("e.semester") )
+ .add( Property.forName("c.courseCode") )
+ .add( Property.forName("c.description") )
+ )
+ .uniqueResult();
+
+ Projection p1 = Projections.projectionList()
+ .add( Property.forName("studentNumber").count() )
+ .add( Property.forName("studentNumber").max() )
+ .add( Projections.rowCount() );
+
+ Projection p2 = Projections.projectionList()
+ .add( Property.forName("studentNumber").min() )
+ .add( Property.forName("studentNumber").avg() )
+ .add( Projections.sqlProjection(
+ "1 as constOne, count(*) as countStar",
+ new String[] { "constOne", "countStar" },
+ new Type[] { Hibernate.INTEGER, Hibernate.INTEGER }
+ ) );
+
+ Object[] array = (Object[]) s.createCriteria(Enrolment.class)
+ .setProjection( Projections.projectionList().add(p1).add(p2) )
+ .uniqueResult();
+
+ assertEquals( array.length, 7 );
+
+ List list = s.createCriteria(Enrolment.class)
+ .createAlias("student", "st")
+ .createAlias("course", "co")
+ .setProjection( Projections.projectionList()
+ .add( Property.forName("co.courseCode").group() )
+ .add( Property.forName("st.studentNumber").count().setDistinct() )
+ .add( Property.forName("year").group() )
+ )
+ .list();
+
+ assertEquals( list.size(), 2 );
+
+ s.delete(gavin);
+ s.delete(xam);
+ s.delete(course);
+
+ t.commit();
+ s.close();
+ }
+
+ public void testRestrictionOnSubclassCollection() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ s.createCriteria( Reptile.class )
+ .add( Restrictions.isEmpty( "offspring" ) )
+ .list();
+
+ s.createCriteria( Reptile.class )
+ .add( Restrictions.isNotEmpty( "offspring" ) )
+ .list();
+
+ t.rollback();
+ s.close();
+ }
+
+ public void testClassProperty() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ // HQL: from Animal a where a.mother.class = Reptile
+ Criteria c = s.createCriteria(Animal.class,"a")
+ .createAlias("mother","m")
+ .add( Property.forName("m.class").eq(Reptile.class) );
+ c.list();
+ t.rollback();
+ s.close();
+ }
+
+ public void testProjectedId() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.createCriteria(Course.class).setProjection( Projections.property("courseCode") ).list();
+ s.createCriteria(Course.class).setProjection( Projections.id() ).list();
+ t.rollback();
+ s.close();
+ }
+
+ public void testSubcriteriaJoinTypes() {
+ Session session = openSession();
+ Transaction t = session.beginTransaction();
+
+ Course courseA = new Course();
+ courseA.setCourseCode("HIB-A");
+ courseA.setDescription("Hibernate Training A");
+ session.persist(courseA);
+
+ Course courseB = new Course();
+ courseB.setCourseCode("HIB-B");
+ courseB.setDescription("Hibernate Training B");
+ session.persist(courseB);
+
+ Student gavin = new Student();
+ gavin.setName("Gavin King");
+ gavin.setStudentNumber(232);
+ gavin.setPreferredCourse(courseA);
+ session.persist(gavin);
+
+ Student leonardo = new Student();
+ leonardo.setName("Leonardo Quijano");
+ leonardo.setStudentNumber(233);
+ leonardo.setPreferredCourse(courseB);
+ session.persist(leonardo);
+
+ Student johnDoe = new Student();
+ johnDoe.setName("John Doe");
+ johnDoe.setStudentNumber(235);
+ johnDoe.setPreferredCourse(null);
+ session.persist(johnDoe);
+
+ List result = session.createCriteria( Student.class )
+ .setProjection( Property.forName("preferredCourse.courseCode") )
+ .createCriteria( "preferredCourse", Criteria.LEFT_JOIN )
+ .addOrder( Order.asc( "courseCode" ) )
+ .list();
+ assertEquals( 3, result.size() );
+ // can't be sure of NULL comparison ordering aside from they should
+ // either come first or last
+ if ( result.get( 0 ) == null ) {
+ assertEquals( "HIB-A", result.get(1) );
+ assertEquals( "HIB-B", result.get(2) );
+ }
+ else {
+ assertNull( result.get(2) );
+ assertEquals( "HIB-A", result.get(0) );
+ assertEquals( "HIB-B", result.get(1) );
+ }
+
+ result = session.createCriteria( Student.class )
+ .setFetchMode( "preferredCourse", FetchMode.JOIN )
+ .createCriteria( "preferredCourse", Criteria.LEFT_JOIN )
+ .addOrder( Order.asc( "courseCode" ) )
+ .list();
+ assertEquals( 3, result.size() );
+ assertNotNull( result.get(0) );
+ assertNotNull( result.get(1) );
+ assertNotNull( result.get(2) );
+
+ result = session.createCriteria( Student.class )
+ .setFetchMode( "preferredCourse", FetchMode.JOIN )
+ .createAlias( "preferredCourse", "pc", Criteria.LEFT_JOIN )
+ .addOrder( Order.asc( "pc.courseCode" ) )
+ .list();
+ assertEquals( 3, result.size() );
+ assertNotNull( result.get(0) );
+ assertNotNull( result.get(1) );
+ assertNotNull( result.get(2) );
+
+ session.delete(gavin);
+ session.delete(leonardo);
+ session.delete(johnDoe);
+ session.delete(courseA);
+ session.delete(courseB);
+ t.commit();
+ session.close();
+ }
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/criteria/CriteriaQueryTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/criteria/Enrolment.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/criteria/Enrolment.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/criteria/Enrolment.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,45 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.criteria">
+
+ <class name="Course">
+ <id name="courseCode">
+ <generator class="assigned"/>
+ </id>
+ <property name="description"/>
+ </class>
+
+ <class name="Student">
+ <id name="studentNumber">
+ <column name="studentId"/>
+ <generator class="assigned"/>
+ </id>
+ <property name="name" not-null="true"/>
+ <set name="enrolments" inverse="true" cascade="delete">
+ <key column="studentId"/>
+ <one-to-many class="Enrolment"/>
+ </set>
+ <many-to-one name="preferredCourse" column="preferredCourseCode"/>
+ </class>
+
+ <class name="Enrolment">
+ <composite-id>
+ <key-property name="studentNumber">
+ <column name="studentId"/>
+ </key-property>
+ <key-property name="courseCode"/>
+ </composite-id>
+ <many-to-one name="student" insert="false" update="false">
+ <column name="studentId"/>
+ </many-to-one>
+ <many-to-one name="course" insert="false" update="false">
+ <column name="courseCode"/>
+ </many-to-one>
+ <property name="semester" not-null="true"/>
+ <property name="year" not-null="true"/>
+ </class>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/criteria/Enrolment.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/criteria/Enrolment.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/criteria/Enrolment.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/criteria/Enrolment.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,63 @@
+//$Id: Enrolment.java 6970 2005-05-31 20:24:41Z oneovthafew $
+package org.hibernate.test.criteria;
+
+import java.io.Serializable;
+
+/**
+ * @author Gavin King
+ */
+public class Enrolment implements Serializable {
+ private Student student;
+ private Course course;
+ private long studentNumber;
+ private String courseCode;
+ private short year;
+ private short semester;
+ public String getCourseCode() {
+ return courseCode;
+ }
+ public void setCourseCode(String courseId) {
+ this.courseCode = courseId;
+ }
+ public long getStudentNumber() {
+ return studentNumber;
+ }
+ public void setStudentNumber(long studentId) {
+ this.studentNumber = studentId;
+ }
+ public Course getCourse() {
+ return course;
+ }
+ public void setCourse(Course course) {
+ this.course = course;
+ }
+ public Student getStudent() {
+ return student;
+ }
+ public void setStudent(Student student) {
+ this.student = student;
+ }
+ public short getSemester() {
+ return semester;
+ }
+ public void setSemester(short semester) {
+ this.semester = semester;
+ }
+ public short getYear() {
+ return year;
+ }
+ public void setYear(short year) {
+ this.year = year;
+ }
+
+ public boolean equals(Object other) {
+ if ( !(other instanceof Enrolment) ) return false;
+ Enrolment that = (Enrolment) other;
+ return studentNumber==that.studentNumber &&
+ courseCode.equals(that.courseCode);
+ }
+
+ public int hashCode() {
+ return courseCode.hashCode();
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/criteria/Enrolment.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/criteria/Student.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/criteria/Student.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/criteria/Student.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,47 @@
+//$Id: Student.java 9116 2006-01-23 21:21:01Z steveebersole $
+package org.hibernate.test.criteria;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author Gavin King
+ */
+public class Student {
+ private long studentNumber;
+ private String name;
+ private Course preferredCourse;
+ private Set enrolments = new HashSet();
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public long getStudentNumber() {
+ return studentNumber;
+ }
+
+ public void setStudentNumber(long studentNumber) {
+ this.studentNumber = studentNumber;
+ }
+
+ public Course getPreferredCourse() {
+ return preferredCourse;
+ }
+
+ public void setPreferredCourse(Course preferredCourse) {
+ this.preferredCourse = preferredCourse;
+ }
+
+ public Set getEnrolments() {
+ return enrolments;
+ }
+
+ public void setEnrolments(Set employments) {
+ this.enrolments = employments;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/criteria/Student.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/criteria/StudentDTO.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/criteria/StudentDTO.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/criteria/StudentDTO.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,26 @@
+/*
+ * Created on 28-Jan-2005
+ *
+ */
+package org.hibernate.test.criteria;
+
+/**
+ * @author max
+ *
+ */
+public class StudentDTO {
+
+ private String studentName;
+ private String courseDescription;
+
+ public StudentDTO() { }
+
+ public String getName() {
+ return studentName;
+ }
+
+ public String getDescription() {
+ return courseDescription;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cuk/Account.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cuk/Account.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cuk/Account.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,50 @@
+//$Id: Account.java 4592 2004-09-26 00:39:43Z oneovthafew $
+package org.hibernate.test.cuk;
+
+import java.io.Serializable;
+
+/**
+ * @author Gavin King
+ */
+public class Account implements Serializable {
+ private String accountId;
+ private Person user;
+ private char type;
+ /**
+ * @return Returns the user.
+ */
+ public Person getUser() {
+ return user;
+ }
+ /**
+ * @param user The user to set.
+ */
+ public void setUser(Person user) {
+ this.user = user;
+ }
+ /**
+ * @return Returns the accountId.
+ */
+ public String getAccountId() {
+ return accountId;
+ }
+ /**
+ * @param accountId The accountId to set.
+ */
+ public void setAccountId(String accountId) {
+ this.accountId = accountId;
+ }
+ /**
+ * @return Returns the type.
+ */
+ public char getType() {
+ return type;
+ }
+ /**
+ * @param type The type to set.
+ */
+ public void setType(char type) {
+ this.type = type;
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cuk/Account.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cuk/Address.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cuk/Address.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cuk/Address.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,75 @@
+//$Id: Address.java 4592 2004-09-26 00:39:43Z oneovthafew $
+package org.hibernate.test.cuk;
+
+import java.io.Serializable;
+
+/**
+ * @author gavin
+ */
+public class Address implements Serializable {
+ private Long id;
+ private String address;
+ private String zip;
+ private String country;
+ private Person person;
+ /**
+ * @return Returns the id.
+ */
+ public Long getId() {
+ return id;
+ }
+ /**
+ * @param id The id to set.
+ */
+ public void setId(Long id) {
+ this.id = id;
+ }
+ /**
+ * @return Returns the person.
+ */
+ public Person getPerson() {
+ return person;
+ }
+ /**
+ * @param person The person to set.
+ */
+ public void setPerson(Person person) {
+ this.person = person;
+ }
+ /**
+ * @return Returns the address.
+ */
+ public String getAddress() {
+ return address;
+ }
+ /**
+ * @param address The address to set.
+ */
+ public void setAddress(String address) {
+ this.address = address;
+ }
+ /**
+ * @return Returns the country.
+ */
+ public String getCountry() {
+ return country;
+ }
+ /**
+ * @param country The country to set.
+ */
+ public void setCountry(String country) {
+ this.country = country;
+ }
+ /**
+ * @return Returns the zip.
+ */
+ public String getZip() {
+ return zip;
+ }
+ /**
+ * @param zip The zip to set.
+ */
+ public void setZip(String zip) {
+ this.zip = zip;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cuk/Address.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cuk/CompositePropertyRefTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cuk/CompositePropertyRefTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cuk/CompositePropertyRefTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,124 @@
+//$Id: CompositePropertyRefTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.cuk;
+
+import java.util.List;
+import java.util.Set;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class CompositePropertyRefTest extends FunctionalTestCase {
+
+ public CompositePropertyRefTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "cuk/Person.hbm.xml" };
+ }
+
+ public void configure(Configuration cfg) {
+ cfg.setProperty(Environment.DEFAULT_BATCH_FETCH_SIZE, "1");
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( CompositePropertyRefTest.class );
+ }
+
+ public void testOneToOnePropertyRef() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Person p = new Person();
+ p.setName("Steve");
+ p.setUserId("steve");
+ Address a = new Address();
+ a.setAddress("Texas");
+ a.setCountry("USA");
+ p.setAddress(a);
+ a.setPerson(p);
+ s.save(p);
+ Person p2 = new Person();
+ p2.setName("Max");
+ p2.setUserId("max");
+ s.save(p2);
+ Account act = new Account();
+ act.setType('c');
+ act.setUser(p2);
+ p2.getAccounts().add(act);
+ s.save(act);
+ s.flush();
+ s.clear();
+
+ p = (Person) s.get( Person.class, p.getId() ); //get address reference by outer join
+ p2 = (Person) s.get( Person.class, p2.getId() ); //get null address reference by outer join
+ assertNull( p2.getAddress() );
+ assertNotNull( p.getAddress() );
+ List l = s.createQuery("from Person").list(); //pull address references for cache
+ assertEquals( l.size(), 2 );
+ assertTrue( l.contains(p) && l.contains(p2) );
+ s.clear();
+
+ l = s.createQuery("from Person p order by p.name").list(); //get address references by sequential selects
+ assertEquals( l.size(), 2 );
+ assertNull( ( (Person) l.get(0) ).getAddress() );
+ assertNotNull( ( (Person) l.get(1) ).getAddress() );
+ s.clear();
+
+ l = s.createQuery("from Person p left join fetch p.address a order by a.country").list(); //get em by outer join
+ assertEquals( l.size(), 2 );
+ if ( ( (Person) l.get(0) ).getName().equals("Max") ) {
+ assertNull( ( (Person) l.get(0) ).getAddress() );
+ assertNotNull( ( (Person) l.get(1) ).getAddress() );
+ }
+ else {
+ assertNull( ( (Person) l.get(1) ).getAddress() );
+ assertNotNull( ( (Person) l.get(0) ).getAddress() );
+ }
+ s.clear();
+
+ l = s.createQuery("from Person p left join p.accounts").list();
+ for ( int i=0; i<2; i++ ) {
+ Object[] row = (Object[]) l.get(i);
+ Person px = (Person) row[0];
+ Set accounts = px.getAccounts();
+ assertFalse( Hibernate.isInitialized(accounts) );
+ assertTrue( px.getAccounts().size()>0 || row[1]==null );
+ }
+ s.clear();
+
+ l = s.createQuery("from Person p left join fetch p.accounts a order by p.name").list();
+ Person p0 = (Person) l.get(0);
+ assertTrue( Hibernate.isInitialized( p0.getAccounts() ) );
+ assertEquals( p0.getAccounts().size(), 1 );
+ assertSame( ( (Account) p0.getAccounts().iterator().next() ).getUser(), p0 );
+ Person p1 = (Person) l.get(1);
+ assertTrue( Hibernate.isInitialized( p1.getAccounts() ) );
+ assertEquals( p1.getAccounts().size(), 0 );
+ s.clear();
+
+ l = s.createQuery("from Account a join fetch a.user").list();
+
+ s.clear();
+
+ l = s.createQuery("from Person p left join fetch p.address").list();
+
+ s.clear();
+ s.createQuery( "delete Address" ).executeUpdate();
+ s.createQuery( "delete Account" ).executeUpdate();
+ s.createQuery( "delete Person" ).executeUpdate();
+ t.commit();
+ s.close();
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cuk/CompositePropertyRefTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cuk/Person.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cuk/Person.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cuk/Person.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,63 @@
+<?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 the use of property-ref to map legacy data where
+ foreign keys reference something other than the primary key of
+ the associated entity. Here we show:
+
+ (1) A one-to-one foreign key association (prefer primary key
+ associations)
+
+ (2) A bidirectional one-to-many association on a key that is
+ comprised of several properties of the associated entity
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.cuk">
+
+ <class name="Person">
+ <id name="id">
+ <generator class="hilo"/>
+ </id>
+ <property name="name" length="100"/>
+ <one-to-one name="address" property-ref="person" cascade="all" fetch="join"/>
+ <set name="accounts" inverse="true">
+ <key property-ref="userIdAndDeleted">
+ <column name="userId"/>
+ <column name="userDeleted"/>
+ </key>
+ <one-to-many class="Account"/>
+ </set>
+ <properties name="userIdAndDeleted" update="false" unique="true">
+ <property name="userId" length="8"/>
+ <property name="deleted"/>
+ </properties>
+
+ </class>
+
+ <class name="Address">
+ <id name="id">
+ <generator class="hilo"/>
+ </id>
+ <property name="address" length="300"/>
+ <property name="zip" length="5"/>
+ <property name="country" length="25"/>
+ <many-to-one name="person" unique="true" not-null="true"/>
+ </class>
+
+ <class name="Account">
+ <id name="accountId" length="32">
+ <generator class="uuid.hex"/>
+ </id>
+ <many-to-one name="user" property-ref="userIdAndDeleted">
+ <column name="userId"/>
+ <column name="userDeleted"/>
+ </many-to-one>
+ <property name="type" not-null="true"/>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cuk/Person.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cuk/Person.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cuk/Person.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cuk/Person.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,100 @@
+//$Id: Person.java 4592 2004-09-26 00:39:43Z oneovthafew $
+package org.hibernate.test.cuk;
+
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author gavin
+ */
+public class Person implements Serializable {
+ private Long id;
+ private String name;
+ private Address address;
+ private String userId;
+ private boolean deleted;
+ private Set accounts = new HashSet();
+ /**
+ * @return Returns the userId.
+ */
+ public String getUserId() {
+ return userId;
+ }
+ /**
+ * @param userId The userId to set.
+ */
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
+ /**
+ * @return Returns the address.
+ */
+ public Address getAddress() {
+ return address;
+ }
+ /**
+ * @param address The address to set.
+ */
+ public void setAddress(Address address) {
+ this.address = address;
+ }
+ /**
+ * @return Returns the id.
+ */
+ public Long getId() {
+ return id;
+ }
+ /**
+ * @param id The id to set.
+ */
+ public void setId(Long id) {
+ this.id = id;
+ }
+ /**
+ * @return Returns the name.
+ */
+ public String getName() {
+ return name;
+ }
+ /**
+ * @param name The name to set.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+ /**
+ * @return Returns the accounts.
+ */
+ public Set getAccounts() {
+ return accounts;
+ }
+ /**
+ * @param accounts The accounts to set.
+ */
+ public void setAccounts(Set accounts) {
+ this.accounts = accounts;
+ }
+
+ public boolean isDeleted() {
+ return deleted;
+ }
+
+ public void setDeleted(boolean deleted) {
+ this.deleted = deleted;
+ }
+
+ public boolean equals(Object other) {
+ if (other instanceof Person) {
+ Person that = (Person) other;
+ return that.isDeleted() == deleted && that.getUserId().equals(userId);
+ }
+ else {
+ return false;
+ }
+ }
+
+ public int hashCode() {
+ return userId.hashCode();
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cuk/Person.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cut/CompositeUserTypeTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cut/CompositeUserTypeTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cut/CompositeUserTypeTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,63 @@
+//$Id: CompositeUserTypeTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.cut;
+
+import java.math.BigDecimal;
+import java.util.Currency;
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.dialect.HSQLDialect;
+import org.hibernate.dialect.Oracle9Dialect;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class CompositeUserTypeTest extends FunctionalTestCase {
+
+ public CompositeUserTypeTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "cut/types.hbm.xml", "cut/Transaction.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( CompositeUserTypeTest.class );
+ }
+
+ public void testCompositeUserType() {
+ Session s = openSession();
+ org.hibernate.Transaction t = s.beginTransaction();
+
+ Transaction tran = new Transaction();
+ tran.setDescription("a small transaction");
+ tran.setValue( new MonetoryAmount( new BigDecimal(1.5), Currency.getInstance("USD") ) );
+ s.persist(tran);
+
+ List result = s.createQuery("from Transaction tran where tran.value.amount > 1.0 and tran.value.currency = 'USD'").list();
+ assertEquals( result.size(), 1 );
+ tran.getValue().setCurrency( Currency.getInstance("AUD") );
+ result = s.createQuery("from Transaction tran where tran.value.amount > 1.0 and tran.value.currency = 'AUD'").list();
+ assertEquals( result.size(), 1 );
+
+ if ( !(getDialect() instanceof HSQLDialect) && ! (getDialect() instanceof Oracle9Dialect) ) {
+
+ result = s.createQuery("from Transaction txn where txn.value = (1.5, 'AUD')").list();
+ assertEquals( result.size(), 1 );
+ result = s.createQuery("from Transaction where value = (1.5, 'AUD')").list();
+ assertEquals( result.size(), 1 );
+
+ }
+
+ s.delete(tran);
+ t.commit();
+ s.close();
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cut/CompositeUserTypeTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cut/MonetoryAmount.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cut/MonetoryAmount.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cut/MonetoryAmount.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,37 @@
+//$Id: MonetoryAmount.java 6234 2005-03-29 03:07:30Z oneovthafew $
+package org.hibernate.test.cut;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Currency;
+
+/**
+ * @author Gavin King
+ */
+public class MonetoryAmount implements Serializable {
+
+ private BigDecimal amount;
+ private Currency currency;
+
+ public MonetoryAmount(BigDecimal amount, Currency currency) {
+ this.amount = amount;
+ this.currency = currency;
+ }
+
+ public BigDecimal getAmount() {
+ return amount;
+ }
+
+ public void setAmount(BigDecimal amount) {
+ this.amount = amount;
+ }
+
+ public Currency getCurrency() {
+ return currency;
+ }
+
+ public void setCurrency(Currency currency) {
+ this.currency = currency;
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cut/MonetoryAmount.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cut/MonetoryAmountUserType.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cut/MonetoryAmountUserType.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cut/MonetoryAmountUserType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,104 @@
+//$Id: MonetoryAmountUserType.java 6235 2005-03-29 03:17:49Z oneovthafew $
+package org.hibernate.test.cut;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Currency;
+
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.type.Type;
+import org.hibernate.usertype.CompositeUserType;
+
+/**
+ * @author Gavin King
+ */
+public class MonetoryAmountUserType implements CompositeUserType {
+
+ public String[] getPropertyNames() {
+ return new String[] { "amount", "currency" };
+ }
+
+ public Type[] getPropertyTypes() {
+ return new Type[] { Hibernate.BIG_DECIMAL, Hibernate.CURRENCY };
+ }
+
+ public Object getPropertyValue(Object component, int property) throws HibernateException {
+ MonetoryAmount ma = (MonetoryAmount) component;
+ return property==0 ? (Object) ma.getAmount() : (Object) ma.getCurrency();
+ }
+
+ public void setPropertyValue(Object component, int property, Object value)
+ throws HibernateException {
+ MonetoryAmount ma = (MonetoryAmount) component;
+ if ( property==0 ) {
+ ma.setAmount( (BigDecimal) value );
+ }
+ else {
+ ma.setCurrency( (Currency) value );
+ }
+ }
+
+ public Class returnedClass() {
+ return MonetoryAmount.class;
+ }
+
+ public boolean equals(Object x, Object y) throws HibernateException {
+ if (x==y) return true;
+ if (x==null || y==null) return false;
+ MonetoryAmount mx = (MonetoryAmount) x;
+ MonetoryAmount my = (MonetoryAmount) y;
+ return mx.getAmount().equals( my.getAmount() ) &&
+ mx.getCurrency().equals( my.getCurrency() );
+ }
+
+ public int hashCode(Object x) throws HibernateException {
+ return ( (MonetoryAmount) x ).getAmount().hashCode();
+ }
+
+ public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner)
+ throws HibernateException, SQLException {
+ BigDecimal amt = (BigDecimal) Hibernate.BIG_DECIMAL.nullSafeGet( rs, names[0] );
+ Currency cur = (Currency) Hibernate.CURRENCY.nullSafeGet( rs, names[1] );
+ if (amt==null) return null;
+ return new MonetoryAmount(amt, cur);
+ }
+
+ public void nullSafeSet(PreparedStatement st, Object value, int index,
+ SessionImplementor session) throws HibernateException, SQLException {
+ MonetoryAmount ma = (MonetoryAmount) value;
+ BigDecimal amt = ma == null ? null : ma.getAmount();
+ Currency cur = ma == null ? null : ma.getCurrency();
+ Hibernate.BIG_DECIMAL.nullSafeSet(st, amt, index);
+ Hibernate.CURRENCY.nullSafeSet(st, cur, index+1);
+ }
+
+ public Object deepCopy(Object value) throws HibernateException {
+ MonetoryAmount ma = (MonetoryAmount) value;
+ return new MonetoryAmount( ma.getAmount(), ma.getCurrency() );
+ }
+
+ public boolean isMutable() {
+ return true;
+ }
+
+ public Serializable disassemble(Object value, SessionImplementor session)
+ throws HibernateException {
+ return (Serializable) deepCopy(value);
+ }
+
+ public Object assemble(Serializable cached, SessionImplementor session, Object owner)
+ throws HibernateException {
+ return deepCopy(cached);
+ }
+
+ public Object replace(Object original, Object target, SessionImplementor session, Object owner)
+ throws HibernateException {
+ return deepCopy(original); //TODO: improve
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cut/MonetoryAmountUserType.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cut/Transaction.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cut/Transaction.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cut/Transaction.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,25 @@
+<?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 the use of a CompositeUserType.
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.cut">
+
+ <class name="Transaction" table="Trnsctn">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="description" length="100" not-null="true"/>
+ <property name="value" type="money">
+ <column name="amount" not-null="true"/>
+ <column name="currency" not-null="true"/>
+ </property>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cut/Transaction.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cut/Transaction.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cut/Transaction.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cut/Transaction.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,37 @@
+//$Id: Transaction.java 6234 2005-03-29 03:07:30Z oneovthafew $
+package org.hibernate.test.cut;
+
+/**
+ * @author Gavin King
+ */
+public class Transaction {
+
+ private Long id;
+ private String description;
+ private MonetoryAmount value;
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public MonetoryAmount getValue() {
+ return value;
+ }
+
+ public void setValue(MonetoryAmount value) {
+ this.value = value;
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cut/Transaction.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cut/types.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cut/types.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/cut/types.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.cut">
+ <typedef name="money" class="org.hibernate.test.cut.MonetoryAmountUserType"/>
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/deletetransient/Address.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/deletetransient/Address.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/deletetransient/Address.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,34 @@
+package org.hibernate.test.deletetransient;
+
+/**
+ * todo: describe Address
+ *
+ * @author Steve Ebersole
+ */
+public class Address {
+ private Long id;
+ private String info;
+
+ public Address() {
+ }
+
+ public Address(String info) {
+ this.info = info;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getInfo() {
+ return info;
+ }
+
+ public void setInfo(String info) {
+ this.info = info;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/deletetransient/DeleteTransientEntityTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/deletetransient/DeleteTransientEntityTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/deletetransient/DeleteTransientEntityTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,108 @@
+package org.hibernate.test.deletetransient;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * todo: describe DeleteTransientEntityTest
+ *
+ * @author Steve Ebersole
+ */
+public class DeleteTransientEntityTest extends FunctionalTestCase {
+ public DeleteTransientEntityTest(String name) {
+ super( name );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "deletetransient/Person.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( DeleteTransientEntityTest.class );
+ }
+
+ public void testTransientEntityDeletionNoCascades() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.delete( new Address() );
+ t.commit();
+ s.close();
+ }
+
+ public void testTransientEntityDeletionCascadingToTransientAssociation() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Person p = new Person();
+ p.getAddresses().add( new Address() );
+ s.delete( p );
+ t.commit();
+ s.close();
+ }
+
+ public void testTransientEntityDeleteCascadingToCircularity() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Person p1 = new Person();
+ Person p2 = new Person();
+ p1.getFriends().add( p2 );
+ p2.getFriends().add( p1 );
+ s.delete( p1 );
+ t.commit();
+ s.close();
+ }
+
+ public void testTransientEntityDeletionCascadingToDetachedAssociation() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Address address = new Address();
+ address.setInfo( "123 Main St." );
+ s.save( address );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Person p = new Person();
+ p.getAddresses().add( address );
+ s.delete( p );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Long count = ( Long ) s.createQuery( "select count(*) from Address" ).list().get( 0 );
+ assertEquals( "delete not cascaded properly across transient entity", 0, count.longValue() );
+ t.commit();
+ s.close();
+ }
+
+ public void testTransientEntityDeletionCascadingToPersistentAssociation() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Address address = new Address();
+ address.setInfo( "123 Main St." );
+ s.save( address );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ address = ( Address ) s.get( Address.class, address.getId() );
+ Person p = new Person();
+ p.getAddresses().add( address );
+ s.delete( p );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Long count = ( Long ) s.createQuery( "select count(*) from Address" ).list().get( 0 );
+ assertEquals( "delete not cascaded properly across transient entity", 0, count.longValue() );
+ t.commit();
+ s.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/deletetransient/Person.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/deletetransient/Person.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/deletetransient/Person.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.deletetransient">
+
+ <class name="Person" table="T_PERSON">
+ <id name="id" type="long">
+ <generator class="increment"/>
+ </id>
+ <property name="name" type="string"/>
+ <set name="addresses" lazy="true" inverse="false" cascade="all">
+ <key column="PERSON_ID"/>
+ <one-to-many class="Address"/>
+ </set>
+ <bag name="friends" lazy="true" inverse="false" cascade="all" table="T_FRIENDS">
+ <key column="FRIEND_ID_1"/>
+ <many-to-many class="Person" column="FRIEND_ID_2"/>
+ </bag>
+ </class>
+
+ <class name="Address" table="T_ADDRESS">
+ <id name="id" type="long">
+ <generator class="increment"/>
+ </id>
+ <property name="info" type="string"/>
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/deletetransient/Person.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/deletetransient/Person.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/deletetransient/Person.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,57 @@
+package org.hibernate.test.deletetransient;
+
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Collection;
+import java.util.ArrayList;
+
+/**
+ * todo: describe Person
+ *
+ * @author Steve Ebersole
+ */
+public class Person {
+ private Long id;
+ private String name;
+ private Set addresses = new HashSet();
+ private Collection friends = new ArrayList();
+
+ public Person() {
+ }
+
+ public Person(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;
+ }
+
+ public Set getAddresses() {
+ return addresses;
+ }
+
+ public void setAddresses(Set addresses) {
+ this.addresses = addresses;
+ }
+
+ public Collection getFriends() {
+ return friends;
+ }
+
+ public void setFriends(Collection friends) {
+ this.friends = friends;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dialect/functional/DialectFunctionalTestsSuite.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dialect/functional/DialectFunctionalTestsSuite.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dialect/functional/DialectFunctionalTestsSuite.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,18 @@
+package org.hibernate.test.dialect.functional;
+
+import junit.framework.TestSuite;
+
+import org.hibernate.test.dialect.functional.cache.SQLFunctionsInterSystemsTest;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class DialectFunctionalTestsSuite {
+ public static TestSuite suite() {
+ TestSuite suite = new TestSuite( "Dialect tests" );
+ suite.addTest( SQLFunctionsInterSystemsTest.suite() );
+ return suite;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dialect/functional/cache/SQLFunctionsInterSystemsTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dialect/functional/cache/SQLFunctionsInterSystemsTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dialect/functional/cache/SQLFunctionsInterSystemsTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,740 @@
+package org.hibernate.test.dialect.functional.cache;
+
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.Test;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.hibernate.Hibernate;
+import org.hibernate.LockMode;
+import org.hibernate.Query;
+import org.hibernate.ScrollableResults;
+import org.hibernate.Transaction;
+import org.hibernate.classic.Session;
+import org.hibernate.dialect.Cache71Dialect;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.HSQLDialect;
+import org.hibernate.dialect.InterbaseDialect;
+import org.hibernate.dialect.MckoiDialect;
+import org.hibernate.dialect.MySQLDialect;
+import org.hibernate.dialect.Oracle9Dialect;
+import org.hibernate.dialect.SybaseDialect;
+import org.hibernate.dialect.TimesTenDialect;
+import org.hibernate.dialect.function.SQLFunction;
+import org.hibernate.junit.functional.DatabaseSpecificFunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.test.legacy.Blobber;
+import org.hibernate.test.legacy.Broken;
+import org.hibernate.test.legacy.Fixed;
+import org.hibernate.test.legacy.Simple;
+import org.hibernate.test.legacy.Single;
+
+/**
+ * Tests for function support on CacheSQL...
+ *
+ * @author Jonathan Levinson
+ */
+public class SQLFunctionsInterSystemsTest extends DatabaseSpecificFunctionalTestCase {
+
+ private static final Log log = LogFactory.getLog(SQLFunctionsInterSystemsTest.class);
+
+ public SQLFunctionsInterSystemsTest(String name) {
+ super(name);
+ }
+
+ public String[] getMappings() {
+ return new String[] {
+ "legacy/AltSimple.hbm.xml",
+ "legacy/Broken.hbm.xml",
+ "legacy/Blobber.hbm.xml",
+ "dialect/cache/TestInterSystemsFunctionsClass.hbm.xml"
+ };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( SQLFunctionsInterSystemsTest.class );
+ }
+
+ public boolean appliesTo(Dialect dialect) {
+ // all these test case apply only to testing InterSystems' CacheSQL dialect
+ return dialect instanceof Cache71Dialect;
+ }
+
+ public void testDialectSQLFunctions() throws Exception {
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ Iterator iter = s.iterate("select max(s.count) from Simple s");
+
+ if ( getDialect() instanceof MySQLDialect ) assertTrue( iter.hasNext() && iter.next()==null );
+
+ Simple simple = new Simple();
+ simple.setName("Simple Dialect Function Test");
+ simple.setAddress("Simple Address");
+ simple.setPay(new Float(45.8));
+ simple.setCount(2);
+ s.save(simple, new Long(10) );
+
+ // Test to make sure allocating an specified object operates correctly.
+ assertTrue(
+ s.find("select new org.hibernate.test.legacy.S(s.count, s.address) from Simple s").size() == 1
+ );
+
+ // Quick check the base dialect functions operate correctly
+ assertTrue(
+ s.find("select max(s.count) from Simple s").size() == 1
+ );
+ assertTrue(
+ s.find("select count(*) from Simple s").size() == 1
+ );
+
+ if ( getDialect() instanceof Cache71Dialect) {
+ // Check Oracle Dialect mix of dialect functions - no args (no parenthesis and single arg functions
+ java.util.List rset = s.find("select s.name, sysdate, floor(s.pay), round(s.pay,0) from Simple s");
+ assertNotNull("Name string should have been returned",(((Object[])rset.get(0))[0]));
+ assertNotNull("Todays Date should have been returned",(((Object[])rset.get(0))[1]));
+ assertEquals("floor(45.8) result was incorrect ", new Integer(45), ( (Object[]) rset.get(0) )[2] );
+ assertEquals("round(45.8) result was incorrect ", new Float(46), ( (Object[]) rset.get(0) )[3] );
+
+ simple.setPay(new Float(-45.8));
+ s.update(simple);
+
+ // Test type conversions while using nested functions (Float to Int).
+ rset = s.find("select abs(round(s.pay,0)) from Simple s");
+ assertEquals("abs(round(-45.8)) result was incorrect ", new Float(46), rset.get(0));
+
+ // Test a larger depth 3 function example - Not a useful combo other than for testing
+ assertTrue(
+ s.find("select floor(round(sysdate,1)) from Simple s").size() == 1
+ );
+
+ // Test the oracle standard NVL funtion as a test of multi-param functions...
+ simple.setPay(null);
+ s.update(simple);
+ Double value = (Double) s.createQuery("select mod( nvl(s.pay, 5000), 2 ) from Simple as s where s.id = 10").list().get(0);
+ assertTrue( 0 == value.intValue() );
+ }
+
+ if ( (getDialect() instanceof Cache71Dialect) ) {
+ // Test the hsql standard MOD funtion as a test of multi-param functions...
+ Double value = (Double) s.find("select MOD(s.count, 2) from Simple as s where s.id = 10" ).get(0);
+ assertTrue( 0 == value.intValue() );
+ }
+
+ /*
+ if ( (getDialect() instanceof Cache71Dialect) ) {
+ // Test the hsql standard MOD funtion as a test of multi-param functions...
+ Date value = (Date) s.find("select sysdate from Simple as s where nvl(cast(null as date), sysdate)=sysdate" ).get(0);
+ assertTrue( value.equals(new java.sql.Date(System.currentTimeMillis())));
+ }
+ */
+
+ s.delete(simple);
+ t.commit();
+ s.close();
+ }
+
+ public void testSetProperties() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Simple simple = new Simple();
+ simple.setName("Simple 1");
+ s.save(simple, new Long(10) );
+ Query q = s.createQuery("from Simple s where s.name=:name and s.count=:count");
+ q.setProperties(simple);
+ assertTrue( q.list().get(0)==simple );
+ //misuse of "Single" as a propertyobject, but it was the first testclass i found with a collection ;)
+ Single single = new Single() { // trivial hack to test properties with arrays.
+ String[] getStuff() { return (String[]) getSeveral().toArray(new String[getSeveral().size()]); }
+ };
+
+ List l = new ArrayList();
+ l.add("Simple 1");
+ l.add("Slimeball");
+ single.setSeveral(l);
+ q = s.createQuery("from Simple s where s.name in (:several)");
+ q.setProperties(single);
+ assertTrue( q.list().get(0)==simple );
+
+
+ q = s.createQuery("from Simple s where s.name in (:stuff)");
+ q.setProperties(single);
+ assertTrue( q.list().get(0)==simple );
+ s.delete(simple);
+ t.commit();
+ s.close();
+ }
+
+ public void testBroken() throws Exception {
+ if (getDialect() instanceof Oracle9Dialect) return;
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Broken b = new Fixed();
+ b.setId( new Long(123));
+ b.setOtherId("foobar");
+ s.save(b);
+ s.flush();
+ b.setTimestamp( new Date() );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.update(b);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ b = (Broken) s.load( Broken.class, b );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.delete(b);
+ t.commit();
+ s.close();
+ }
+
+ public void testNothinToUpdate() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Simple simple = new Simple();
+ simple.setName("Simple 1");
+ s.save( simple, new Long(10) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.update( simple, new Long(10) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.update( simple, new Long(10) );
+ s.delete(simple);
+ t.commit();
+ s.close();
+ }
+
+ public void testCachedQuery() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Simple simple = new Simple();
+ simple.setName("Simple 1");
+ s.save( simple, new Long(10) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Query q = s.createQuery("from Simple s where s.name=?");
+ q.setCacheable(true);
+ q.setString(0, "Simple 1");
+ assertTrue( q.list().size()==1 );
+ assertTrue( q.list().size()==1 );
+ assertTrue( q.list().size()==1 );
+ q = s.createQuery("from Simple s where s.name=:name");
+ q.setCacheable(true);
+ q.setString("name", "Simple 1");
+ assertTrue( q.list().size()==1 );
+ simple = (Simple) q.list().get(0);
+
+ q.setString("name", "Simple 2");
+ assertTrue( q.list().size()==0 );
+ assertTrue( q.list().size()==0 );
+ simple.setName("Simple 2");
+ assertTrue( q.list().size()==1 );
+ assertTrue( q.list().size()==1 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ q = s.createQuery("from Simple s where s.name=:name");
+ q.setString("name", "Simple 2");
+ q.setCacheable(true);
+ assertTrue( q.list().size()==1 );
+ assertTrue( q.list().size()==1 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.update( simple, new Long(10) );
+ s.delete(simple);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ q = s.createQuery("from Simple s where s.name=?");
+ q.setCacheable(true);
+ q.setString(0, "Simple 1");
+ assertTrue( q.list().size()==0 );
+ assertTrue( q.list().size()==0 );
+ t.commit();
+ s.close();
+ }
+
+ public void testCachedQueryRegion() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Simple simple = new Simple();
+ simple.setName("Simple 1");
+ s.save( simple, new Long(10) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Query q = s.createQuery("from Simple s where s.name=?");
+ q.setCacheRegion("foo");
+ q.setCacheable(true);
+ q.setString(0, "Simple 1");
+ assertTrue( q.list().size()==1 );
+ assertTrue( q.list().size()==1 );
+ assertTrue( q.list().size()==1 );
+ q = s.createQuery("from Simple s where s.name=:name");
+ q.setCacheRegion("foo");
+ q.setCacheable(true);
+ q.setString("name", "Simple 1");
+ assertTrue( q.list().size()==1 );
+ simple = (Simple) q.list().get(0);
+
+ q.setString("name", "Simple 2");
+ assertTrue( q.list().size()==0 );
+ assertTrue( q.list().size()==0 );
+ simple.setName("Simple 2");
+ assertTrue( q.list().size()==1 );
+ assertTrue( q.list().size()==1 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.update( simple, new Long(10) );
+ s.delete(simple);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ q = s.createQuery("from Simple s where s.name=?");
+ q.setCacheRegion("foo");
+ q.setCacheable(true);
+ q.setString(0, "Simple 1");
+ assertTrue( q.list().size()==0 );
+ assertTrue( q.list().size()==0 );
+ t.commit();
+ s.close();
+ }
+
+ public void testSQLFunctions() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Simple simple = new Simple();
+ simple.setName("Simple 1");
+ s.save(simple, new Long(10) );
+
+ if ( getDialect() instanceof Cache71Dialect) {
+ s.find("from Simple s where repeat('foo', 3) = 'foofoofoo'");
+ s.find("from Simple s where repeat(s.name, 3) = 'foofoofoo'");
+ s.find("from Simple s where repeat( lower(s.name), (3 + (1-1)) / 2) = 'foofoofoo'");
+ }
+
+ assertTrue(
+ s.find("from Simple s where upper( s.name ) ='SIMPLE 1'").size()==1
+ );
+ if ( !(getDialect() instanceof HSQLDialect) ) {
+ assertTrue(
+ s.find("from Simple s where not( upper( s.name ) ='yada' or 1=2 or 'foo'='bar' or not('foo'='foo') or 'foo' like 'bar' )").size()==1
+ );
+ }
+ if ( !(getDialect() instanceof MySQLDialect) && !(getDialect() instanceof SybaseDialect) && !(getDialect() instanceof MckoiDialect) && !(getDialect() instanceof InterbaseDialect) && !(getDialect() instanceof TimesTenDialect) ) { //My SQL has a funny concatenation operator
+ assertTrue(
+ s.find("from Simple s where lower( s.name || ' foo' ) ='simple 1 foo'").size()==1
+ );
+ }
+ /* + is not concat in Cache
+ if ( (getDialect() instanceof Cache71Dialect) ) {
+ assertTrue(
+ s.find("from Simple s where lower( cons.name ' foo' ) ='simple 1 foo'").size()==1
+ );
+ }
+ */
+ if ( (getDialect() instanceof Cache71Dialect) ) {
+ assertTrue(
+ s.find("from Simple s where lower( concat(s.name, ' foo') ) ='simple 1 foo'").size()==1
+ );
+ }
+
+ Simple other = new Simple();
+ other.setName("Simple 2");
+ other.setCount(12);
+ simple.setOther(other);
+ s.save( other, new Long(20) );
+ //s.find("from Simple s where s.name ## 'cat|rat|bag'");
+ assertTrue(
+ s.find("from Simple s where upper( s.other.name ) ='SIMPLE 2'").size()==1
+ );
+ assertTrue(
+ s.find("from Simple s where not ( upper( s.other.name ) ='SIMPLE 2' )").size()==0
+ );
+ assertTrue(
+ s.find("select distinct s from Simple s where ( ( s.other.count + 3 ) = (15*2)/2 and s.count = 69) or ( ( s.other.count + 2 ) / 7 ) = 2").size()==1
+ );
+ assertTrue(
+ s.find("select s from Simple s where ( ( s.other.count + 3 ) = (15*2)/2 and s.count = 69) or ( ( s.other.count + 2 ) / 7 ) = 2 order by s.other.count").size()==1
+ );
+ Simple min = new Simple();
+ min.setCount(-1);
+ s.save(min, new Long(30) );
+ if ( ! (getDialect() instanceof MySQLDialect) && ! (getDialect() instanceof HSQLDialect) ) { //My SQL has no subqueries
+ assertTrue(
+ s.find("from Simple s where s.count > ( select min(sim.count) from Simple sim )").size()==2
+ );
+ t.commit();
+ t = s.beginTransaction();
+ assertTrue(
+ s.find("from Simple s where s = some( select sim from Simple sim where sim.count>=0 ) and s.count >= 0").size()==2
+ );
+ assertTrue(
+ s.find("from Simple s where s = some( select sim from Simple sim where sim.other.count=s.other.count ) and s.other.count > 0").size()==1
+ );
+ }
+
+ Iterator iter = s.iterate("select sum(s.count) from Simple s group by s.count having sum(s.count) > 10");
+ assertTrue( iter.hasNext() );
+ assertEquals( new Long(12), iter.next() );
+ assertTrue( !iter.hasNext() );
+ if ( ! (getDialect() instanceof MySQLDialect) ) {
+ iter = s.iterate("select s.count from Simple s group by s.count having s.count = 12");
+ assertTrue( iter.hasNext() );
+ }
+
+ s.iterate("select s.id, s.count, count(t), max(t.date) from Simple s, Simple t where s.count = t.count group by s.id, s.count order by s.count");
+
+ Query q = s.createQuery("from Simple s");
+ q.setMaxResults(10);
+ assertTrue( q.list().size()==3 );
+ q = s.createQuery("from Simple s");
+ q.setMaxResults(1);
+ assertTrue( q.list().size()==1 );
+ q = s.createQuery("from Simple s");
+ assertTrue( q.list().size()==3 );
+ q = s.createQuery("from Simple s where s.name = ?");
+ q.setString(0, "Simple 1");
+ assertTrue( q.list().size()==1 );
+ q = s.createQuery("from Simple s where s.name = ? and upper(s.name) = ?");
+ q.setString(1, "SIMPLE 1");
+ q.setString(0, "Simple 1");
+ q.setFirstResult(0);
+ assertTrue( q.iterate().hasNext() );
+ q = s.createQuery("from Simple s where s.name = :foo and upper(s.name) = :bar or s.count=:count or s.count=:count + 1");
+ q.setParameter("bar", "SIMPLE 1");
+ q.setString("foo", "Simple 1");
+ q.setInteger("count", 69);
+ q.setFirstResult(0);
+ assertTrue( q.iterate().hasNext() );
+ q = s.createQuery("select s.id from Simple s");
+ q.setFirstResult(1);
+ q.setMaxResults(2);
+ iter = q.iterate();
+ int i=0;
+ while ( iter.hasNext() ) {
+ assertTrue( iter.next() instanceof Long );
+ i++;
+ }
+ assertTrue(i==2);
+ q = s.createQuery("select all s, s.other from Simple s where s = :s");
+ q.setParameter("s", simple);
+ assertTrue( q.list().size()==1 );
+
+
+ q = s.createQuery("from Simple s where s.name in (:name_list) and s.count > :count");
+ HashSet set = new HashSet();
+ set.add("Simple 1"); set.add("foo");
+ q.setParameterList( "name_list", set );
+ q.setParameter("count", new Integer(-1) );
+ assertTrue( q.list().size()==1 );
+
+ ScrollableResults sr = s.createQuery("from Simple s").scroll();
+ sr.next();
+ sr.get(0);
+ sr.close();
+
+ s.delete(other);
+ s.delete(simple);
+ s.delete(min);
+ t.commit();
+ s.close();
+
+ }
+
+ public void testBlobClob() throws Exception {
+
+ Session s = openSession();
+ Blobber b = new Blobber();
+ b.setBlob( Hibernate.createBlob( "foo/bar/baz".getBytes() ) );
+ b.setClob( Hibernate.createClob("foo/bar/baz") );
+ s.save(b);
+ //s.refresh(b);
+ //assertTrue( b.getClob() instanceof ClobImpl );
+ s.flush();
+ s.refresh(b);
+ //b.getBlob().setBytes( 2, "abc".getBytes() );
+ log.debug("levinson: just bfore b.getClob()");
+ b.getClob().getSubString(2, 3);
+ //b.getClob().setString(2, "abc");
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ b = (Blobber) s.load( Blobber.class, new Integer( b.getId() ) );
+ Blobber b2 = new Blobber();
+ s.save(b2);
+ b2.setBlob( b.getBlob() );
+ b.setBlob(null);
+ //assertTrue( b.getClob().getSubString(1, 3).equals("fab") );
+ b.getClob().getSubString(1, 6);
+ //b.getClob().setString(1, "qwerty");
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ b = (Blobber) s.load( Blobber.class, new Integer( b.getId() ) );
+ b.setClob( Hibernate.createClob("xcvfxvc xcvbx cvbx cvbx cvbxcvbxcvbxcvb") );
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ b = (Blobber) s.load( Blobber.class, new Integer( b.getId() ) );
+ assertTrue( b.getClob().getSubString(1, 7).equals("xcvfxvc") );
+ //b.getClob().setString(5, "1234567890");
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+
+ /*InputStream is = getClass().getClassLoader().getResourceAsStream("jdbc20.pdf");
+ s = sessionsopenSession();
+ b = (Blobber) s.load( Blobber.class, new Integer( b.getId() ) );
+ System.out.println( is.available() );
+ int size = is.available();
+ b.setBlob( Hibernate.createBlob( is, is.available() ) );
+ s.flush();
+ s.connection().commit();
+ ResultSet rs = s.connection().createStatement().executeQuery("select datalength(blob_) from blobber where id=" + b.getId() );
+ rs.next();
+ assertTrue( size==rs.getInt(1) );
+ rs.close();
+ s.close();
+
+ s = sessionsopenSession();
+ b = (Blobber) s.load( Blobber.class, new Integer( b.getId() ) );
+ File f = new File("C:/foo.pdf");
+ f.createNewFile();
+ FileOutputStream fos = new FileOutputStream(f);
+ Blob blob = b.getBlob();
+ byte[] bytes = blob.getBytes( 1, (int) blob.length() );
+ System.out.println( bytes.length );
+ fos.write(bytes);
+ fos.flush();
+ fos.close();
+ s.close();*/
+
+ }
+
+ public void testSqlFunctionAsAlias() throws Exception {
+ String functionName = locateAppropriateDialectFunctionNameForAliasTest();
+ if (functionName == null) {
+ log.info("Dialect does not list any no-arg functions");
+ return;
+ }
+
+ log.info("Using function named [" + functionName + "] for 'function as alias' test");
+ String query = "select " + functionName + " from Simple as " + functionName + " where " + functionName + ".id = 10";
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Simple simple = new Simple();
+ simple.setName("Simple 1");
+ s.save( simple, new Long(10) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ List result = s.find(query);
+ assertTrue( result.size() == 1 );
+ assertTrue(result.get(0) instanceof Simple);
+ s.delete( result.get(0) );
+ t.commit();
+ s.close();
+ }
+
+ private String locateAppropriateDialectFunctionNameForAliasTest() {
+ for (Iterator itr = getDialect().getFunctions().entrySet().iterator(); itr.hasNext(); ) {
+ final Map.Entry entry = (Map.Entry) itr.next();
+ final SQLFunction function = (SQLFunction) entry.getValue();
+ if ( !function.hasArguments() && !function.hasParenthesesIfNoArguments() ) {
+ return (String) entry.getKey();
+ }
+ }
+ return null;
+ }
+
+ public void testCachedQueryOnInsert() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Simple simple = new Simple();
+ simple.setName("Simple 1");
+ s.save( simple, new Long(10) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Query q = s.createQuery("from Simple s");
+ List list = q.setCacheable(true).list();
+ assertTrue( list.size()==1 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ q = s.createQuery("from Simple s");
+ list = q.setCacheable(true).list();
+ assertTrue( list.size()==1 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Simple simple2 = new Simple();
+ simple2.setCount(133);
+ s.save( simple2, new Long(12) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ q = s.createQuery("from Simple s");
+ list = q.setCacheable(true).list();
+ assertTrue( list.size()==2 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ q = s.createQuery("from Simple s");
+ list = q.setCacheable(true).list();
+ assertTrue( list.size()==2 );
+ Iterator i = list.iterator();
+ while ( i.hasNext() ) s.delete( i.next() );
+ t.commit();
+ s.close();
+
+ }
+
+ public void testInterSystemsFunctions() throws Exception {
+ Calendar cal = new GregorianCalendar();
+ cal.set(1977,6,3,0,0,0);
+ java.sql.Timestamp testvalue = new java.sql.Timestamp(cal.getTimeInMillis());
+ testvalue.setNanos(0);
+ Calendar cal3 = new GregorianCalendar();
+ cal3.set(1976,2,3,0,0,0);
+ java.sql.Timestamp testvalue3 = new java.sql.Timestamp(cal3.getTimeInMillis());
+ testvalue3.setNanos(0);
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ try {
+ Statement stmt = s.connection().createStatement();
+ stmt.executeUpdate("DROP FUNCTION spLock FROM TestInterSystemsFunctionsClass");
+ t.commit();
+ }
+ catch (Exception ex) {
+ System.out.println("as we expected stored procedure sp does not exist when we drop it");
+
+ }
+ t = s.beginTransaction();
+ Statement stmt = s.connection().createStatement();
+ String create_function = "CREATE FUNCTION SQLUser.TestInterSystemsFunctionsClass_spLock\n" +
+ " ( INOUT pHandle %SQLProcContext, \n" +
+ " ROWID INTEGER \n" +
+ " )\n" +
+ " FOR User.TestInterSystemsFunctionsClass " +
+ " PROCEDURE\n" +
+ " RETURNS INTEGER\n" +
+ " LANGUAGE OBJECTSCRIPT\n" +
+ " {\n" +
+ " q 0\n" +
+ " }";
+ stmt.executeUpdate(create_function);
+ t.commit();
+ t = s.beginTransaction();
+
+ TestInterSystemsFunctionsClass object = new TestInterSystemsFunctionsClass();
+ object.setDateText("1977-07-03");
+ object.setDate1(testvalue);
+ object.setDate3(testvalue3);
+ s.save( object, new Long(10));
+ t.commit();
+ s.close();
+ s = openSession();
+ s.clear();
+ t = s.beginTransaction();
+ TestInterSystemsFunctionsClass test = (TestInterSystemsFunctionsClass) s.get(TestInterSystemsFunctionsClass.class, new Long(10));
+ assertTrue( test.getDate1().equals(testvalue));
+ test = (TestInterSystemsFunctionsClass) s.get(TestInterSystemsFunctionsClass.class, new Long(10), LockMode.UPGRADE);
+ assertTrue( test.getDate1().equals(testvalue));
+ Date value = (Date) s.find("select nvl(o.date,o.dateText) from TestInterSystemsFunctionsClass as o" ).get(0);
+ assertTrue( value.equals(testvalue));
+ Object nv = s.find("select nullif(o.dateText,o.dateText) from TestInterSystemsFunctionsClass as o" ).get(0);
+ assertTrue( nv == null);
+ String dateText = (String) s.find("select nvl(o.dateText,o.date) from TestInterSystemsFunctionsClass as o" ).get(0);
+ assertTrue( dateText.equals("1977-07-03"));
+ value = (Date) s.find("select ifnull(o.date,o.date1) from TestInterSystemsFunctionsClass as o" ).get(0);
+ assertTrue( value.equals(testvalue));
+ value = (Date) s.find("select ifnull(o.date3,o.date,o.date1) from TestInterSystemsFunctionsClass as o" ).get(0);
+ assertTrue( value.equals(testvalue));
+ Integer pos = (Integer) s.find("select position('07', o.dateText) from TestInterSystemsFunctionsClass as o" ).get(0);
+ assertTrue(pos.intValue() == 6);
+ String st = (String) s.find("select convert(o.date1, SQL_TIME) from TestInterSystemsFunctionsClass as o" ).get(0);
+ assertTrue( st.equals("00:00:00"));
+ java.sql.Time tm = (java.sql.Time) s.find("select cast(o.date1, time) from TestInterSystemsFunctionsClass as o" ).get(0);
+ assertTrue( tm.toString().equals("00:00:00"));
+ Double diff = (Double)s.find("select timestampdiff(SQL_TSI_FRAC_SECOND, o.date3, o.date1) from TestInterSystemsFunctionsClass as o" ).get(0);
+ assertTrue(diff.doubleValue() != 0.0);
+ diff = (Double)s.find("select timestampdiff(SQL_TSI_MONTH, o.date3, o.date1) from TestInterSystemsFunctionsClass as o" ).get(0);
+ assertTrue(diff.doubleValue() == 16.0);
+ diff = (Double)s.find("select timestampdiff(SQL_TSI_WEEK, o.date3, o.date1) from TestInterSystemsFunctionsClass as o" ).get(0);
+ assertTrue(diff.doubleValue() >= 16*4);
+ diff = (Double)s.find("select timestampdiff(SQL_TSI_YEAR, o.date3, o.date1) from TestInterSystemsFunctionsClass as o" ).get(0);
+ assertTrue(diff.doubleValue() == 1.0);
+
+ t.commit();
+ s.close();
+
+
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dialect/functional/cache/TestInterSystemsFunctionsClass.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dialect/functional/cache/TestInterSystemsFunctionsClass.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dialect/functional/cache/TestInterSystemsFunctionsClass.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.dialect.functional.cache" >
+
+ <class name="TestInterSystemsFunctionsClass" table="SQLUser.TestInterSystemsFunctionsClass">
+ <id type="long" column="id_">
+ <generator class="assigned"/>
+ </id>
+ <property name="date" column="date_"/>
+ <property name="date1" column="date1_"/>
+ <property name="date3" column="date3_"/>
+ <property name="dateText" column="dateText_"/>
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dialect/functional/cache/TestInterSystemsFunctionsClass.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dialect/functional/cache/TestInterSystemsFunctionsClass.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dialect/functional/cache/TestInterSystemsFunctionsClass.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,51 @@
+package org.hibernate.test.dialect.functional.cache;
+
+import java.util.Date;
+
+/**
+ * Entity for testing function support of InterSystems' CacheSQL...
+ *
+ * @author Jonathan Levinson
+ */
+public class TestInterSystemsFunctionsClass {
+ private java.util.Date date3;
+ private java.util.Date date1;
+ private java.util.Date date;
+ private String dateText;
+
+ public Date getDate() {
+ return date;
+ }
+
+ public void setDate(Date date) {
+ this.date = date;
+ }
+
+
+ public String getDateText() {
+ return dateText;
+ }
+
+ public void setDateText(String dateText) {
+ this.dateText = dateText;
+ }
+
+
+ public Date getDate1() {
+ return date1;
+ }
+
+ public void setDate1(Date date1) {
+ this.date1 = date1;
+ }
+
+
+ public Date getDate3() {
+ return date3;
+ }
+
+ public void setDate3(Date date3) {
+ this.date3 = date3;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dialect/unit/DialectUnitTestsSuite.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dialect/unit/DialectUnitTestsSuite.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dialect/unit/DialectUnitTestsSuite.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,20 @@
+package org.hibernate.test.dialect.unit;
+
+import junit.framework.TestSuite;
+
+import org.hibernate.test.dialect.unit.lockhint.SybaseLockHintsTest;
+import org.hibernate.test.dialect.unit.lockhint.SQLServerLockHintsTest;
+
+/**
+ * Suite of all unit tests of the Dialect(s).
+ *
+ * @author Steve Ebersole
+ */
+public class DialectUnitTestsSuite {
+ public static TestSuite suite() {
+ TestSuite suite = new TestSuite( "Dialect unit-tests" );
+ suite.addTest( SybaseLockHintsTest.suite() );
+ suite.addTest( SQLServerLockHintsTest.suite() );
+ return suite;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dialect/unit/lockhint/AbstractLockHintTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dialect/unit/lockhint/AbstractLockHintTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dialect/unit/lockhint/AbstractLockHintTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,64 @@
+package org.hibernate.test.dialect.unit.lockhint;
+
+import java.util.HashMap;
+import java.util.Collections;
+
+import org.hibernate.junit.UnitTestCase;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.util.StringHelper;
+import org.hibernate.LockMode;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public abstract class AbstractLockHintTest extends UnitTestCase {
+ public AbstractLockHintTest(String string) {
+ super( string );
+ }
+
+ private Dialect dialect;
+
+ protected abstract String getLockHintUsed();
+ protected abstract Dialect getDialectUnderTest();
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ this.dialect = getDialectUnderTest();
+ }
+
+ protected void tearDown() throws Exception {
+ this.dialect = null;
+ super.tearDown();
+ }
+
+ public void testBasicLocking() {
+ new SyntaxChecker( "select xyz from ABC $HOLDER$", "a" ).verify();
+ new SyntaxChecker( "select xyz from ABC $HOLDER$ join DEF d", "a" ).verify();
+ new SyntaxChecker( "select xyz from ABC $HOLDER$, DEF d", "a" ).verify();
+ }
+
+ protected class SyntaxChecker {
+ private final String aliasToLock;
+ private final String rawSql;
+ private final String expectedProcessedSql;
+
+ public SyntaxChecker(String template) {
+ this( template, "" );
+ }
+
+ public SyntaxChecker(String template, String aliasToLock) {
+ this.aliasToLock = aliasToLock;
+ rawSql = StringHelper.replace( template, "$HOLDER$", aliasToLock );
+ expectedProcessedSql = StringHelper.replace( template, "$HOLDER$", aliasToLock + " " + getLockHintUsed() );
+ }
+
+ public void verify() {
+ HashMap lockModes = new HashMap();
+ lockModes.put( aliasToLock, LockMode.UPGRADE );
+ String actualProcessedSql = dialect.applyLocksToSql( rawSql, lockModes, Collections.EMPTY_MAP );
+ assertEquals( expectedProcessedSql, actualProcessedSql );
+ }
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dialect/unit/lockhint/SQLServerLockHintsTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dialect/unit/lockhint/SQLServerLockHintsTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dialect/unit/lockhint/SQLServerLockHintsTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,31 @@
+package org.hibernate.test.dialect.unit.lockhint;
+
+import junit.framework.TestSuite;
+
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.SQLServerDialect;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class SQLServerLockHintsTest extends AbstractLockHintTest {
+ public static final Dialect DIALECT = new SQLServerDialect();
+
+ public SQLServerLockHintsTest(String string) {
+ super( string );
+ }
+
+ protected String getLockHintUsed() {
+ return "with (updlock, rowlock)";
+ }
+
+ protected Dialect getDialectUnderTest() {
+ return DIALECT;
+ }
+
+ public static TestSuite suite() {
+ return new TestSuite( SQLServerLockHintsTest.class );
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dialect/unit/lockhint/SybaseLockHintsTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dialect/unit/lockhint/SybaseLockHintsTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dialect/unit/lockhint/SybaseLockHintsTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,31 @@
+package org.hibernate.test.dialect.unit.lockhint;
+
+import junit.framework.TestSuite;
+
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.SybaseDialect;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class SybaseLockHintsTest extends AbstractLockHintTest {
+ public static final Dialect DIALECT = new SybaseDialect();
+
+ public SybaseLockHintsTest(String string) {
+ super( string );
+ }
+
+ protected String getLockHintUsed() {
+ return "holdlock";
+ }
+
+ protected Dialect getDialectUnderTest() {
+ return DIALECT;
+ }
+
+ public static TestSuite suite() {
+ return new TestSuite( SybaseLockHintsTest.class );
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/discriminator/Address.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/discriminator/Address.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/discriminator/Address.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,11 @@
+//$Id: Address.java 4373 2004-08-18 09:18:34Z oneovthafew $
+package org.hibernate.test.discriminator;
+
+/**
+ * @author Gavin King
+ */
+public class Address {
+ public String address;
+ public String zip;
+ public String country;
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/discriminator/Address.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/discriminator/Customer.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/discriminator/Customer.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/discriminator/Customer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,35 @@
+//$Id: Customer.java 4373 2004-08-18 09:18:34Z oneovthafew $
+package org.hibernate.test.discriminator;
+
+/**
+ * @author Gavin King
+ */
+public class Customer extends Person {
+ private Employee salesperson;
+ private String comments;
+
+ /**
+ * @return Returns the salesperson.
+ */
+ public Employee getSalesperson() {
+ return salesperson;
+ }
+ /**
+ * @param salesperson The salesperson to set.
+ */
+ public void setSalesperson(Employee salesperson) {
+ this.salesperson = salesperson;
+ }
+ /**
+ * @return Returns the comments.
+ */
+ public String getComments() {
+ return comments;
+ }
+ /**
+ * @param comments The comments to set.
+ */
+ public void setComments(String comments) {
+ this.comments = comments;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/discriminator/Customer.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/discriminator/DiscriminatorTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/discriminator/DiscriminatorTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/discriminator/DiscriminatorTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,176 @@
+//$Id: DiscriminatorTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.discriminator;
+
+import java.math.BigDecimal;
+import java.util.Iterator;
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.criterion.Property;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class DiscriminatorTest extends FunctionalTestCase {
+
+ public DiscriminatorTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "discriminator/Person.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( DiscriminatorTest.class );
+ }
+
+ public void testDiscriminatorSubclass() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ Employee mark = new Employee();
+ mark.setName("Mark");
+ mark.setTitle("internal sales");
+ mark.setSex('M');
+ mark.setAddress("buckhead");
+ mark.setZip("30305");
+ mark.setCountry("USA");
+
+ Customer joe = new Customer();
+ joe.setName("Joe");
+ joe.setAddress("San Francisco");
+ joe.setZip("XXXXX");
+ joe.setCountry("USA");
+ joe.setComments("Very demanding");
+ joe.setSex('M');
+ joe.setSalesperson(mark);
+
+ Person yomomma = new Person();
+ yomomma.setName("mum");
+ yomomma.setSex('F');
+
+ s.save(yomomma);
+ s.save(mark);
+ s.save(joe);
+
+ assertEquals( s.createQuery("from java.io.Serializable").list().size(), 0 );
+
+ assertEquals( s.createQuery("from Person").list().size(), 3 );
+ assertEquals( s.createQuery("from Person p where p.class = Person").list().size(), 1 );
+ assertEquals( s.createQuery("from Person p where p.class = Customer").list().size(), 1 );
+ s.clear();
+
+ List customers = s.createQuery("from Customer c left join fetch c.salesperson").list();
+ for ( Iterator iter = customers.iterator(); iter.hasNext(); ) {
+ Customer c = (Customer) iter.next();
+ assertTrue( Hibernate.isInitialized( c.getSalesperson() ) );
+ assertEquals( c.getSalesperson().getName(), "Mark" );
+ }
+ assertEquals( customers.size(), 1 );
+ s.clear();
+
+ customers = s.createQuery("from Customer").list();
+ for ( Iterator iter = customers.iterator(); iter.hasNext(); ) {
+ Customer c = (Customer) iter.next();
+ assertFalse( Hibernate.isInitialized( c.getSalesperson() ) );
+ assertEquals( c.getSalesperson().getName(), "Mark" );
+ }
+ assertEquals( customers.size(), 1 );
+ s.clear();
+
+
+ mark = (Employee) s.get( Employee.class, new Long( mark.getId() ) );
+ joe = (Customer) s.get( Customer.class, new Long( joe.getId() ) );
+
+ mark.setZip("30306");
+ assertEquals( s.createQuery("from Person p where p.address.zip = '30306'").list().size(), 1 );
+ s.delete(mark);
+ s.delete(joe);
+ s.delete(yomomma);
+ assertTrue( s.createQuery("from Person").list().isEmpty() );
+ t.commit();
+ s.close();
+ }
+
+ public void testAccessAsIncorrectSubclass() {
+ Session s = openSession();
+ s.beginTransaction();
+ Employee e = new Employee();
+ e.setName( "Steve" );
+ e.setSex( 'M' );
+ e.setTitle( "grand poobah" );
+ s.save( e );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ Customer c = ( Customer ) s.get( Customer.class, new Long( e.getId() ) );
+ s.getTransaction().commit();
+ s.close();
+ assertNull( c );
+
+ s = openSession();
+ s.beginTransaction();
+ e = ( Employee ) s.get( Employee.class, new Long( e.getId() ) );
+ c = ( Customer ) s.get( Customer.class, new Long( e.getId() ) );
+ s.getTransaction().commit();
+ s.close();
+ assertNotNull( e );
+ assertNull( c );
+
+ s = openSession();
+ s.beginTransaction();
+ s.delete( e );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testQuerySubclassAttribute() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Person p = new Person();
+ p.setName("Emmanuel");
+ p.setSex('M');
+ s.persist(p);
+ Employee q = new Employee();
+ q.setName("Steve");
+ q.setSex('M');
+ q.setTitle("Mr");
+ q.setSalary( new BigDecimal(1000) );
+ s.persist(q);
+
+ List result = s.createQuery("from Person where salary > 100").list();
+ assertEquals( result.size(), 1 );
+ assertSame( result.get(0), q );
+
+ result = s.createQuery("from Person where salary > 100 or name like 'E%'").list();
+ assertEquals( result.size(), 2 );
+
+ result = s.createCriteria(Person.class)
+ .add( Property.forName("salary").gt( new BigDecimal(100) ) )
+ .list();
+ assertEquals( result.size(), 1 );
+ assertSame( result.get(0), q );
+
+ //TODO: make this work:
+ /*result = s.createQuery("select salary from Person where salary > 100").list();
+ assertEquals( result.size(), 1 );
+ assertEquals( result.get(0), new BigDecimal(1000) );*/
+
+ s.delete(p);
+ s.delete(q);
+ t.commit();
+ s.close();
+ }
+
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/discriminator/DiscriminatorTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/discriminator/Employee.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/discriminator/Employee.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/discriminator/Employee.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,49 @@
+//$Id: Employee.java 4373 2004-08-18 09:18:34Z oneovthafew $
+package org.hibernate.test.discriminator;
+
+import java.math.BigDecimal;
+
+/**
+ * @author Gavin King
+ */
+public class Employee extends Person {
+ private String title;
+ private BigDecimal salary;
+ private Employee manager;
+ /**
+ * @return Returns the title.
+ */
+ public String getTitle() {
+ return title;
+ }
+ /**
+ * @param title The title to set.
+ */
+ public void setTitle(String title) {
+ this.title = title;
+ }
+ /**
+ * @return Returns the manager.
+ */
+ public Employee getManager() {
+ return manager;
+ }
+ /**
+ * @param manager The manager to set.
+ */
+ public void setManager(Employee manager) {
+ this.manager = manager;
+ }
+ /**
+ * @return Returns the salary.
+ */
+ public BigDecimal getSalary() {
+ return salary;
+ }
+ /**
+ * @param salary The salary to set.
+ */
+ public void setSalary(BigDecimal salary) {
+ this.salary = salary;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/discriminator/Employee.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/discriminator/Person.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/discriminator/Person.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/discriminator/Person.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,64 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+ This mapping demonstrates content-based discrimination for the
+ table-per-hierarchy mapping strategy, using a formula
+ discriminator.
+
+-->
+
+<hibernate-mapping
+ package="org.hibernate.test.discriminator"
+ default-access="field">
+
+ <class name="Person"
+ discriminator-value="P">
+
+ <id name="id"
+ column="person_id"
+ unsaved-value="0">
+ <generator class="native"/>
+ </id>
+
+
+ <discriminator
+ type="character"
+ formula="case when title is not null then 'E' when salesperson is not null then 'C' else 'P' end"/>
+
+ <property name="name"
+ not-null="true"
+ length="80"/>
+
+ <property name="sex"
+ not-null="true"
+ update="false"/>
+
+ <component name="address">
+ <property name="address"/>
+ <property name="zip"/>
+ <property name="country"/>
+ </component>
+
+ <subclass name="Employee"
+ discriminator-value="E">
+ <property name="title"
+ length="20"/>
+ <property name="salary"
+ length="0"/>
+ <many-to-one name="manager"/>
+ </subclass>
+
+ <subclass name="Customer"
+ discriminator-value="C">
+ <property name="comments"/>
+ <many-to-one name="salesperson"/>
+ </subclass>
+
+ </class>
+
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/discriminator/Person.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/discriminator/Person.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/discriminator/Person.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/discriminator/Person.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,70 @@
+//$Id: Person.java 4373 2004-08-18 09:18:34Z oneovthafew $
+package org.hibernate.test.discriminator;
+
+
+/**
+ * @author Gavin King
+ */
+public class Person {
+ private long id;
+ private String name;
+ private char sex;
+ private Address address = new Address();
+ /**
+ * @return Returns the address.
+ */
+ public Address getAddress() {
+ return address;
+ }
+
+ public void setAddress(String string) {
+ this.address.address = string;
+ }
+
+ public void setZip(String string) {
+ this.address.zip = string;
+ }
+
+ public void setCountry(String string) {
+ this.address.country = string;
+ }
+
+
+ /**
+ * @return Returns the sex.
+ */
+ public char getSex() {
+ return sex;
+ }
+ /**
+ * @param sex The sex to set.
+ */
+ public void setSex(char sex) {
+ this.sex = sex;
+ }
+ /**
+ * @return Returns the id.
+ */
+ public long getId() {
+ return id;
+ }
+ /**
+ * @param id The id to set.
+ */
+ public void setId(long id) {
+ this.id = id;
+ }
+ /**
+ * @return Returns the identity.
+ */
+ public String getName() {
+ return name;
+ }
+ /**
+ * @param identity The identity to set.
+ */
+ public void setName(String identity) {
+ this.name = identity;
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/discriminator/Person.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/Address.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/Address.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/Address.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,18 @@
+package org.hibernate.test.dynamicentity;
+
+/**
+ * @author <a href="mailto:steve at hibernate.org">Steve Ebersole </a>
+ */
+public interface Address {
+ public Long getId();
+ public void setId(Long id);
+
+ public String getStreet();
+ public void setStreet(String street);
+
+ public String getCity();
+ public void setCity(String city);
+
+ public String getPostalCode();
+ public void setPostalCode(String postalCode);
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/Company.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/Company.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/Company.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,11 @@
+package org.hibernate.test.dynamicentity;
+
+/**
+ * @author <a href="mailto:steve at hibernate.org">Steve Ebersole </a>
+ */
+public interface Company {
+ public Long getId();
+ public void setId(Long id);
+ public String getName();
+ public void setName(String name);
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/Customer.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/Customer.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/Customer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,9 @@
+package org.hibernate.test.dynamicentity;
+
+/**
+ * @author <a href="mailto:steve at hibernate.org">Steve Ebersole </a>
+ */
+public interface Customer extends Person {
+ public Company getCompany();
+ public void setCompany(Company company);
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/DataProxyHandler.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/DataProxyHandler.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/DataProxyHandler.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,52 @@
+package org.hibernate.test.dynamicentity;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.io.Serializable;
+
+/**
+ * A simple {@link InvocationHandler} to act as the handler for our generated
+ * {@link java.lang.reflect.Proxy}-based entity instances.
+ * <p/>
+ * This is a trivial impl which simply keeps the property values into
+ * a Map.
+ *
+ * @author <a href="mailto:steve at hibernate.org">Steve Ebersole </a>
+ */
+public final class DataProxyHandler implements InvocationHandler {
+ private String entityName;
+ private HashMap data = new HashMap();
+
+ public DataProxyHandler(String entityName, Serializable id) {
+ this.entityName = entityName;
+ data.put( "Id", id );
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ String methodName = method.getName();
+ if ( methodName.startsWith( "set" ) ) {
+ String propertyName = methodName.substring( 3 );
+ data.put( propertyName, args[0] );
+ }
+ else if ( methodName.startsWith( "get" ) ) {
+ String propertyName = methodName.substring( 3 );
+ return data.get( propertyName );
+ }
+ else if ( "toString".equals( methodName ) ) {
+ return entityName + "#" + data.get( "Id" );
+ }
+ else if ( "hashCode".equals( methodName ) ) {
+ return new Integer( this.hashCode() );
+ }
+ return null;
+ }
+
+ public String getEntityName() {
+ return entityName;
+ }
+
+ public HashMap getData() {
+ return data;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/DynamicEntitySuite.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/DynamicEntitySuite.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/DynamicEntitySuite.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,21 @@
+package org.hibernate.test.dynamicentity;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.hibernate.test.dynamicentity.interceptor.InterceptorDynamicEntityTest;
+import org.hibernate.test.dynamicentity.tuplizer.TuplizerDynamicEntityTest;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class DynamicEntitySuite {
+ public static Test suite() {
+ TestSuite suite = new TestSuite( "dynamic entity suite" );
+ suite.addTest( InterceptorDynamicEntityTest.suite() );
+ suite.addTest( TuplizerDynamicEntityTest.suite() );
+ return suite;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/Person.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/Person.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/Person.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,17 @@
+package org.hibernate.test.dynamicentity;
+
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:steve at hibernate.org">Steve Ebersole </a>
+ */
+public interface Person {
+ public Long getId();
+ public void setId(Long id);
+ public String getName();
+ public void setName(String name);
+ public Address getAddress();
+ public void setAddress(Address address);
+ public Set getFamily();
+ public void setFamily(Set family);
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/ProxyHelper.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/ProxyHelper.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/ProxyHelper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,73 @@
+package org.hibernate.test.dynamicentity;
+
+import java.io.Serializable;
+import java.lang.reflect.Proxy;
+import java.lang.reflect.InvocationHandler;
+
+/**
+ * @author <a href="mailto:steve at hibernate.org">Steve Ebersole </a>
+ */
+public class ProxyHelper {
+
+ public static Person newPersonProxy() {
+ return newPersonProxy( null );
+ }
+
+ public static Person newPersonProxy(Serializable id) {
+ return ( Person ) Proxy.newProxyInstance(
+ Person.class.getClassLoader(),
+ new Class[] {Person.class},
+ new DataProxyHandler( Person.class.getName(), id )
+ );
+ }
+
+ public static Customer newCustomerProxy() {
+ return newCustomerProxy( null );
+ }
+
+ public static Customer newCustomerProxy(Serializable id) {
+ return ( Customer ) Proxy.newProxyInstance(
+ Customer.class.getClassLoader(),
+ new Class[] {Customer.class},
+ new DataProxyHandler( Customer.class.getName(), id )
+ );
+ }
+
+ public static Company newCompanyProxy() {
+ return newCompanyProxy( null );
+ }
+
+ public static Company newCompanyProxy(Serializable id) {
+ return ( Company ) Proxy.newProxyInstance(
+ Company.class.getClassLoader(),
+ new Class[] {Company.class},
+ new DataProxyHandler( Company.class.getName(), id )
+ );
+ }
+
+ public static Address newAddressProxy() {
+ return newAddressProxy( null );
+ }
+
+ public static Address newAddressProxy(Serializable id) {
+ return ( Address ) Proxy.newProxyInstance(
+ Address.class.getClassLoader(),
+ new Class[] {Address.class},
+ new DataProxyHandler( Address.class.getName(), id )
+ );
+ }
+
+ public static String extractEntityName(Object object) {
+ // Our custom java.lang.reflect.Proxy instances actually bundle
+ // their appropriate entity name, so we simply extract it from there
+ // if this represents one of our proxies; otherwise, we return null
+ if ( Proxy.isProxyClass( object.getClass() ) ) {
+ InvocationHandler handler = Proxy.getInvocationHandler( object );
+ if ( DataProxyHandler.class.isAssignableFrom( handler.getClass() ) ) {
+ DataProxyHandler myHandler = ( DataProxyHandler ) handler;
+ return myHandler.getEntityName();
+ }
+ }
+ return null;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/interceptor/Customer.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/interceptor/Customer.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/interceptor/Customer.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,51 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.dynamicentity">
+
+ <!--
+ Mapping the Customer and Company interfaces. Our custom Interceptor
+ will be responsible for: a) creating instances representing these
+ interfaces; b) determining the appropriate entity-name (i.e., which
+ entity mapping to use) given an instance of one of these proxies.
+ -->
+ <class name="Person" table="t_person" abstract="false">
+ <!-- <class name="Person" table="t_person" discriminator-value="person"> -->
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <discriminator force="false"/>
+ <property name="name"/>
+
+ <many-to-one name="address" cascade="all" column="addr_id"/>
+
+ <set name="family" lazy="true" cascade="all">
+ <key column="pers_id"/>
+ <one-to-many class="Person"/>
+ </set>
+
+ <subclass name="Customer" discriminator-value="customer" abstract="false">
+ <many-to-one name="company" cascade="none" column="comp_id"/>
+ </subclass>
+ </class>
+
+ <!-- Company interface mapping -->
+ <class name="Company" table="t_company" abstract="false">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+ <class name="Address" table="t_address" abstract="false">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="street"/>
+ <property name="city"/>
+ <property name="postalCode"/>
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/interceptor/InterceptorDynamicEntityTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/interceptor/InterceptorDynamicEntityTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/interceptor/InterceptorDynamicEntityTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,112 @@
+package org.hibernate.test.dynamicentity.interceptor;
+
+import junit.framework.TestSuite;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.test.dynamicentity.Company;
+import org.hibernate.test.dynamicentity.Customer;
+import org.hibernate.test.dynamicentity.ProxyHelper;
+
+/**
+ * Demonstrates custom interpretation of entity-name through
+ * an Interceptor.
+ * <p/>
+ * Here, we are generating dynamic
+ * {@link java.lang.reflect.Proxy proxies} on the fly to represent
+ * our entities. Because of this, Hibernate would not be able to
+ * determine the appropriate entity mapping to use given one of
+ * these proxies (they are named like $Proxy1, or such). Thus, we
+ * plug a custom Interceptor into the session to perform this
+ * entity-name interpretation.
+ *
+ * @see ProxyInterceptor
+ *
+ * @author Steve Ebersole
+ */
+public class InterceptorDynamicEntityTest extends FunctionalTestCase {
+ public InterceptorDynamicEntityTest(String x) {
+ super( x );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "dynamicentity/interceptor/Customer.hbm.xml" };
+ }
+
+ public void configure(Configuration cfg) {
+ cfg.setInterceptor( new ProxyInterceptor() );
+ }
+
+ public static TestSuite suite() {
+ return new FunctionalTestClassTestSuite( InterceptorDynamicEntityTest.class );
+ }
+
+ public void testIt() {
+ // Test saving these dyna-proxies
+ Session session = openSession();
+ session.beginTransaction();
+ Company company = ProxyHelper.newCompanyProxy();
+ company.setName( "acme" );
+ session.save( company );
+ Customer customer = ProxyHelper.newCustomerProxy();
+ customer.setName( "Steve" );
+ customer.setCompany( company );
+ session.save( customer );
+ session.getTransaction().commit();
+ session.close();
+
+ assertNotNull( "company id not assigned", company.getId() );
+ assertNotNull( "customer id not assigned", customer.getId() );
+
+ // Test loading these dyna-proxies, along with flush processing
+ session = openSession();
+ session.beginTransaction();
+ customer = ( Customer ) session.load( Customer.class, customer.getId() );
+ assertFalse( "should-be-proxy was initialized", Hibernate.isInitialized( customer ) );
+
+ customer.setName( "other" );
+ session.flush();
+ assertFalse( "should-be-proxy was initialized", Hibernate.isInitialized( customer.getCompany() ) );
+
+ session.refresh( customer );
+ assertEquals( "name not updated", "other", customer.getName() );
+ assertEquals( "company association not correct", "acme", customer.getCompany().getName() );
+
+ session.getTransaction().commit();
+ session.close();
+
+ // Test detached entity re-attachment with these dyna-proxies
+ customer.setName( "Steve" );
+ session = openSession();
+ session.beginTransaction();
+ session.update( customer );
+ session.flush();
+ session.refresh( customer );
+ assertEquals( "name not updated", "Steve", customer.getName() );
+ session.getTransaction().commit();
+ session.close();
+
+ // Test querying
+ session = openSession();
+ session.beginTransaction();
+ int count = session.createQuery( "from Customer" ).list().size();
+ assertEquals( "querying dynamic entity", 1, count );
+ session.clear();
+ count = session.createQuery( "from Person" ).list().size();
+ assertEquals( "querying dynamic entity", 1, count );
+ session.getTransaction().commit();
+ session.close();
+
+ // test deleteing
+ session = openSession();
+ session.beginTransaction();
+ session.delete( company );
+ session.delete( customer );
+ session.getTransaction().commit();
+ session.close();
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/interceptor/ProxyInterceptor.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/interceptor/ProxyInterceptor.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/interceptor/ProxyInterceptor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,61 @@
+package org.hibernate.test.dynamicentity.interceptor;
+
+import org.hibernate.EmptyInterceptor;
+import org.hibernate.EntityMode;
+import org.hibernate.test.dynamicentity.Company;
+import org.hibernate.test.dynamicentity.Customer;
+import org.hibernate.test.dynamicentity.ProxyHelper;
+import org.hibernate.test.dynamicentity.DataProxyHandler;
+
+import java.io.Serializable;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Proxy;
+
+/**
+ * Our custom {@link org.hibernate.Interceptor} impl which performs the
+ * interpretation of entity-name -> proxy instance and vice-versa.
+ *
+ * @author <a href="mailto:steve at hibernate.org">Steve Ebersole </a>
+ */
+public class ProxyInterceptor extends EmptyInterceptor {
+
+ /**
+ * The callback from Hibernate to determine the entity name given
+ * a presumed entity instance.
+ *
+ * @param object The presumed entity instance.
+ * @return The entity name (pointing to the proper entity mapping).
+ */
+ public String getEntityName(Object object) {
+ String entityName = ProxyHelper.extractEntityName( object );
+ if ( entityName == null ) {
+ entityName = super.getEntityName( object );
+ }
+ return entityName;
+ }
+
+ /**
+ * The callback from Hibernate in order to build an instance of the
+ * entity represented by the given entity name. Here, we build a
+ * {@link Proxy} representing the entity.
+ *
+ * @param entityName The entity name for which to create an instance. In our setup,
+ * this is the interface name.
+ * @param entityMode The entity mode in which to create an instance. Here, we are only
+ * interestes in custom behavior for the POJO entity mode.
+ * @param id The identifier value for the given entity.
+ * @return The instantiated instance.
+ */
+ public Object instantiate(String entityName, EntityMode entityMode, Serializable id) {
+ if ( entityMode == EntityMode.POJO ) {
+ if ( Customer.class.getName().equals( entityName ) ) {
+ return ProxyHelper.newCustomerProxy( id );
+ }
+ else if ( Company.class.getName().equals( entityName ) ) {
+ return ProxyHelper.newCompanyProxy( id );
+ }
+ }
+ return super.instantiate( entityName, entityMode, id );
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/package.html
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/package.html (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,29 @@
+<html>
+<head></head>
+
+<body>
+<p>
+ Demonstration of different ways to use Hibernate to represent your domain
+ model as a series of JDK dynamic proxies. We map the interfaces and then
+ use one of two approaches to get Hibernate to recognize these proxies as
+ domain entities. Really this is demonstrating various "entity representation"
+ capabilities built in to Hibernate3.
+</p>
+<p>
+ First we use an interceptor-based approach where we use a custom Interceptor
+ implementation for interpret incoming proxies (and resolve them to the correct
+ mappings) and to help Hibernate instantiate these proxy instances. This is the
+ quick-and-dirty approach. It is fully expected that this approach will
+ encounter certain limitations.
+</p>
+<p>
+ Next we explore the notion of a Tuplizer and plug in custom Tuplizers to
+ help achieve the same results. Currently, Tuplizers do not have a chance
+ to influence the resolution of entity-name given a potential entity, so we
+ also use an Interceptor here and supply its getEntityName() impl. This is
+ simply so we do not need to supply the entity name explicitly to the
+ Hibernate Session calls.
+</p>
+</body>
+
+</html>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/tuplizer/Customer.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/tuplizer/Customer.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/tuplizer/Customer.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,54 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.dynamicentity">
+
+ <!--
+ Mapping the Customer and Company interfaces. Our custom Interceptor
+ will be responsible for: a) creating instances representing these
+ interfaces; b) determining the appropriate entity-name (i.e., which
+ entity mapping to use) given an instance of one of these proxies.
+ -->
+ <class name="Person" table="t_person" discriminator-value="person" abstract="false">
+ <tuplizer class="org.hibernate.test.dynamicentity.tuplizer.MyEntityTuplizer" entity-mode="pojo"/>
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <discriminator force="false"/>
+ <property name="name"/>
+
+ <many-to-one name="address" cascade="all" column="addr_id"/>
+
+ <set name="family" lazy="true" cascade="all">
+ <key column="pers_id"/>
+ <one-to-many class="Person"/>
+ </set>
+
+ <subclass name="Customer" discriminator-value="customer" abstract="false">
+ <tuplizer class="org.hibernate.test.dynamicentity.tuplizer.MyEntityTuplizer" entity-mode="pojo"/>
+ <many-to-one name="company" cascade="none" column="comp_id"/>
+ </subclass>
+ </class>
+
+ <!-- Company interface mapping -->
+ <class name="Company" table="t_company" abstract="false">
+ <tuplizer class="org.hibernate.test.dynamicentity.tuplizer.MyEntityTuplizer" entity-mode="pojo"/>
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+ <class name="Address" table="t_address" abstract="false">
+ <tuplizer class="org.hibernate.test.dynamicentity.tuplizer.MyEntityTuplizer" entity-mode="pojo"/>
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="street"/>
+ <property name="city"/>
+ <property name="postalCode"/>
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/tuplizer/EntityNameInterceptor.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/tuplizer/EntityNameInterceptor.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/tuplizer/EntityNameInterceptor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,24 @@
+package org.hibernate.test.dynamicentity.tuplizer;
+
+import org.hibernate.EmptyInterceptor;
+import org.hibernate.test.dynamicentity.ProxyHelper;
+
+/**
+ * @author <a href="mailto:steve at hibernate.org">Steve Ebersole </a>
+ */
+public class EntityNameInterceptor extends EmptyInterceptor {
+ /**
+ * The callback from Hibernate to determine the entity name given
+ * a presumed entity instance.
+ *
+ * @param object The presumed entity instance.
+ * @return The entity name (pointing to the proper entity mapping).
+ */
+ public String getEntityName(Object object) {
+ String entityName = ProxyHelper.extractEntityName( object );
+ if ( entityName == null ) {
+ entityName = super.getEntityName( object );
+ }
+ return entityName;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/tuplizer/MyEntityInstantiator.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/tuplizer/MyEntityInstantiator.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/tuplizer/MyEntityInstantiator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,67 @@
+package org.hibernate.test.dynamicentity.tuplizer;
+
+import org.hibernate.tuple.Instantiator;
+import org.hibernate.test.dynamicentity.Customer;
+import org.hibernate.test.dynamicentity.ProxyHelper;
+import org.hibernate.test.dynamicentity.Company;
+import org.hibernate.test.dynamicentity.DataProxyHandler;
+import org.hibernate.test.dynamicentity.Address;
+import org.hibernate.test.dynamicentity.Person;
+import org.hibernate.util.ReflectHelper;
+import org.hibernate.HibernateException;
+
+import java.io.Serializable;
+import java.lang.reflect.Proxy;
+import java.lang.reflect.InvocationHandler;
+
+/**
+ * @author <a href="mailto:steve at hibernate.org">Steve Ebersole </a>
+ */
+public class MyEntityInstantiator implements Instantiator {
+ private final String entityName;
+
+ public MyEntityInstantiator(String entityName) {
+ this.entityName = entityName;
+ }
+
+ public Object instantiate(Serializable id) {
+ if ( Person.class.getName().equals( entityName ) ) {
+ return ProxyHelper.newPersonProxy( id );
+ }
+ if ( Customer.class.getName().equals( entityName ) ) {
+ return ProxyHelper.newCustomerProxy( id );
+ }
+ else if ( Company.class.getName().equals( entityName ) ) {
+ return ProxyHelper.newCompanyProxy( id );
+ }
+ else if ( Address.class.getName().equals( entityName ) ) {
+ return ProxyHelper.newAddressProxy( id );
+ }
+ else {
+ throw new IllegalArgumentException( "unknown entity for instantiation [" + entityName + "]" );
+ }
+ }
+
+ public Object instantiate() {
+ return instantiate( null );
+ }
+
+ public boolean isInstance(Object object) {
+ String resolvedEntityName = null;
+ if ( Proxy.isProxyClass( object.getClass() ) ) {
+ InvocationHandler handler = Proxy.getInvocationHandler( object );
+ if ( DataProxyHandler.class.isAssignableFrom( handler.getClass() ) ) {
+ DataProxyHandler myHandler = ( DataProxyHandler ) handler;
+ resolvedEntityName = myHandler.getEntityName();
+ }
+ }
+ try {
+ return ReflectHelper.classForName( entityName ).isInstance( object );
+ }
+ catch( Throwable t ) {
+ throw new HibernateException( "could not get handle to entity-name as interface : " + t );
+ }
+
+// return entityName.equals( resolvedEntityName );
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/tuplizer/MyEntityTuplizer.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/tuplizer/MyEntityTuplizer.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/tuplizer/MyEntityTuplizer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,31 @@
+package org.hibernate.test.dynamicentity.tuplizer;
+
+import org.hibernate.tuple.entity.PojoEntityTuplizer;
+import org.hibernate.tuple.entity.EntityMetamodel;
+import org.hibernate.tuple.Instantiator;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.proxy.ProxyFactory;
+import org.hibernate.property.Getter;
+import org.hibernate.property.Setter;
+
+/**
+ * @author Steve Ebersole
+ */
+public class MyEntityTuplizer extends PojoEntityTuplizer {
+
+ public MyEntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappedEntity) {
+ super( entityMetamodel, mappedEntity );
+ }
+
+ protected Instantiator buildInstantiator(PersistentClass persistentClass) {
+ return new MyEntityInstantiator( persistentClass.getEntityName() );
+ }
+
+ protected ProxyFactory buildProxyFactory(PersistentClass persistentClass, Getter idGetter, Setter idSetter) {
+ // allows defining a custom proxy factory, which is responsible for
+ // generating lazy proxies for a given entity.
+ //
+ // Here we simply use the default...
+ return super.buildProxyFactory( persistentClass, idGetter, idSetter );
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/tuplizer/TuplizerDynamicEntityTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/tuplizer/TuplizerDynamicEntityTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/dynamicentity/tuplizer/TuplizerDynamicEntityTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,127 @@
+package org.hibernate.test.dynamicentity.tuplizer;
+
+import org.hibernate.test.TestCase;
+import org.hibernate.test.dynamicentity.Company;
+import org.hibernate.test.dynamicentity.ProxyHelper;
+import org.hibernate.test.dynamicentity.Customer;
+import org.hibernate.test.dynamicentity.Address;
+import org.hibernate.test.dynamicentity.Person;
+import org.hibernate.Session;
+import org.hibernate.Hibernate;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+import junit.framework.TestSuite;
+
+import java.util.HashSet;
+
+/**
+ * Demonstrates use of Tuplizers to allow the use of JDK
+ * {@link java.lang.reflect.Proxy dynamic proxies} as our
+ * domain model.
+ * <p/>
+ * Here we plug a custom Interceptor into the session simply to
+ * allow us to not have to explicitly supply the appropriate entity
+ * name to the Session calls.
+ *
+ * @author Steve Ebersole
+ */
+public class TuplizerDynamicEntityTest extends FunctionalTestCase {
+ public TuplizerDynamicEntityTest(String x) {
+ super( x );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "dynamicentity/tuplizer/Customer.hbm.xml" };
+ }
+
+ public void configure(Configuration cfg) {
+ super.configure( cfg );
+ cfg.setInterceptor( new EntityNameInterceptor() );
+ }
+
+ public static TestSuite suite() {
+ return new FunctionalTestClassTestSuite( TuplizerDynamicEntityTest.class );
+ }
+
+ public void testIt() {
+ // Test saving these dyna-proxies
+ Session session = openSession();
+ session.beginTransaction();
+ Company company = ProxyHelper.newCompanyProxy();
+ company.setName( "acme" );
+ session.save( company );
+ Customer customer = ProxyHelper.newCustomerProxy();
+ customer.setName( "Steve" );
+ customer.setCompany( company );
+ Address address = ProxyHelper.newAddressProxy();
+ address.setStreet( "somewhere over the rainbow" );
+ address.setCity( "lawerence, kansas" );
+ address.setPostalCode( "toto");
+ customer.setAddress( address );
+ customer.setFamily( new HashSet() );
+ Person son = ProxyHelper.newPersonProxy();
+ son.setName( "son" );
+ customer.getFamily().add( son );
+ Person wife = ProxyHelper.newPersonProxy();
+ wife.setName( "wife" );
+ customer.getFamily().add( wife );
+ session.save( customer );
+ session.getTransaction().commit();
+ session.close();
+
+ assertNotNull( "company id not assigned", company.getId() );
+ assertNotNull( "customer id not assigned", customer.getId() );
+ assertNotNull( "address id not assigned", address.getId() );
+ assertNotNull( "son:Person id not assigned", son.getId() );
+ assertNotNull( "wife:Person id not assigned", wife.getId() );
+
+ // Test loading these dyna-proxies, along with flush processing
+ session = openSession();
+ session.beginTransaction();
+ customer = ( Customer ) session.load( Customer.class, customer.getId() );
+ assertFalse( "should-be-proxy was initialized", Hibernate.isInitialized( customer ) );
+
+ customer.setName( "other" );
+ session.flush();
+ assertFalse( "should-be-proxy was initialized", Hibernate.isInitialized( customer.getCompany() ) );
+
+ session.refresh( customer );
+ assertEquals( "name not updated", "other", customer.getName() );
+ assertEquals( "company association not correct", "acme", customer.getCompany().getName() );
+
+ session.getTransaction().commit();
+ session.close();
+
+ // Test detached entity re-attachment with these dyna-proxies
+ customer.setName( "Steve" );
+ session = openSession();
+ session.beginTransaction();
+ session.update( customer );
+ session.flush();
+ session.refresh( customer );
+ assertEquals( "name not updated", "Steve", customer.getName() );
+ session.getTransaction().commit();
+ session.close();
+
+ // Test querying
+ session = openSession();
+ session.beginTransaction();
+ int count = session.createQuery( "from Customer" ).list().size();
+ assertEquals( "querying dynamic entity", 1, count );
+ session.clear();
+ count = session.createQuery( "from Person" ).list().size();
+ assertEquals( "querying dynamic entity", 3, count );
+ session.getTransaction().commit();
+ session.close();
+
+ // test deleteing
+ session = openSession();
+ session.beginTransaction();
+ session.delete( company );
+ session.delete( customer );
+ session.getTransaction().commit();
+ session.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ecid/Course.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ecid/Course.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ecid/Course.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,29 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping
+ package="org.hibernate.test.ecid"
+ default-access="field">
+
+<!--
+
+ This mapping demonstrates inheritance with embedded composite ids.
+
+-->
+
+ <class name="Course" table="SchoolCourses"
+ discriminator-value="secondary/primary">
+ <composite-id>
+ <key-property name="courseCode"/>
+ <key-property name="org"/>
+ </composite-id>
+ <discriminator column="schoolLevel" length="20"/>
+ <property name="description" not-null="true"/>
+ <subclass name="UniversityCourse" discriminator-value="tertiary">
+ <property name="semester"/>
+ </subclass>
+ </class>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ecid/Course.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ecid/Course.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ecid/Course.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ecid/Course.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,38 @@
+//$Id: Course.java 6913 2005-05-25 17:37:51Z oneovthafew $
+package org.hibernate.test.ecid;
+
+import java.io.Serializable;
+
+/**
+ * @author Gavin King
+ */
+public class Course implements Serializable {
+
+ private String courseCode;
+ private String org;
+ private String description;
+
+ Course() {}
+ Course(String courseCode, String org, String description) {
+ this.courseCode = courseCode;
+ this.org = org;
+ this.description = description;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getCourseCode() {
+ return courseCode;
+ }
+
+ public String getOrg() {
+ return org;
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ecid/Course.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ecid/EmbeddedCompositeIdTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ecid/EmbeddedCompositeIdTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ecid/EmbeddedCompositeIdTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,162 @@
+//$Id: EmbeddedCompositeIdTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.ecid;
+
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.proxy.HibernateProxy;
+
+/**
+ * @author Gavin King
+ */
+public class EmbeddedCompositeIdTest extends FunctionalTestCase {
+
+ public EmbeddedCompositeIdTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "ecid/Course.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( EmbeddedCompositeIdTest.class );
+ }
+
+ public void testMerge() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Course uc = new UniversityCourse("mat2000", "Monash", "second year maths", 0);
+ Course c = new Course("eng5000", "BHS", "grade 5 english");
+ s.persist(uc);
+ s.persist(c);
+ t.commit();
+ s.close();
+
+ c.setDescription("Grade 5 English");
+ uc.setDescription("Second year mathematics");
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.merge(c);
+ s.merge(uc);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.delete(c);
+ s.delete(uc);
+ t.commit();
+ s.close();
+ }
+
+ public void testMerging() {
+ // Test HHH-799
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Course course = new Course( "EN-101", "BA", "preparatory english" );
+ s.persist( course );
+ t.commit();
+ s.close();
+
+ String newDesc = "basic preparatory english";
+ course.setDescription( newDesc );
+
+ s = openSession();
+ t = s.beginTransaction();
+ Course c = (Course) s.merge( course );
+ assertEquals( "description not merged", newDesc, c.getDescription() );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Course cid = new Course( "EN-101", "BA", null );
+ course = ( Course ) s.get( Course.class, cid );
+ assertEquals( "description not merged", newDesc, course.getDescription() );
+ s.delete( course );
+ t.commit();
+ s.close();
+ }
+
+ public void testPolymorphism() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Course uc = new UniversityCourse("mat2000", "Monash", "second year maths", 0);
+ Course c = new Course("eng5000", "BHS", "grade 5 english");
+ s.persist(uc);
+ s.persist(c);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Course ucid = new Course("mat2000", "Monash", null);
+ Course cid = new Course("eng5000", "BHS", null);
+ Course luc = (Course) s.load(Course.class, ucid);
+ Course lc = (Course) s.load(Course.class, cid);
+ assertFalse( Hibernate.isInitialized(luc) );
+ assertFalse( Hibernate.isInitialized(lc) );
+ assertEquals( UniversityCourse.class, Hibernate.getClass(luc) );
+ assertEquals( Course.class, Hibernate.getClass(lc) );
+ assertSame( ( (HibernateProxy) lc ).getHibernateLazyInitializer().getImplementation(), cid );
+ assertEquals( c.getCourseCode(), "eng5000" );
+ assertEquals( uc.getCourseCode(), "mat2000" );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ ucid = new Course("mat2000", "Monash", null);
+ cid = new Course("eng5000", "BHS", null);
+ luc = (Course) s.get(Course.class, ucid);
+ lc = (Course) s.get(Course.class, cid);
+ assertTrue( Hibernate.isInitialized(luc) );
+ assertTrue( Hibernate.isInitialized(lc) );
+ assertEquals( UniversityCourse.class, Hibernate.getClass(luc) );
+ assertEquals( Course.class, Hibernate.getClass(lc) );
+ assertSame( lc, cid );
+ assertEquals( c.getCourseCode(), "eng5000" );
+ assertEquals( uc.getCourseCode(), "mat2000" );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ List list = s.createQuery("from Course order by courseCode").list();
+ assertTrue( list.get(0) instanceof Course );
+ assertTrue( list.get(1) instanceof UniversityCourse );
+ c = (Course) list.get(0);
+ uc = (UniversityCourse) list.get(1);
+ assertEquals( c.getCourseCode(), "eng5000" );
+ assertEquals( uc.getCourseCode(), "mat2000" );
+ t.commit();
+ s.close();
+
+ c.setDescription("Grade 5 English");
+ uc.setDescription("Second year mathematics");
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.saveOrUpdate(c);
+ s.saveOrUpdate(uc);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.delete(c);
+ s.delete(uc);
+ t.commit();
+ s.close();
+
+ }
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ecid/EmbeddedCompositeIdTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ecid/UniversityCourse.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ecid/UniversityCourse.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ecid/UniversityCourse.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+//$Id: UniversityCourse.java 6913 2005-05-25 17:37:51Z oneovthafew $
+package org.hibernate.test.ecid;
+
+/**
+ * @author Gavin King
+ */
+public class UniversityCourse extends Course {
+
+ private int semester;
+
+ UniversityCourse() {}
+
+ public UniversityCourse(String courseCode, String org, String description, int semester) {
+ super( courseCode, org, description );
+ this.semester = semester;
+ }
+
+ public int getSemester() {
+ return semester;
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ecid/UniversityCourse.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/EntityModeSuite.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/EntityModeSuite.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/EntityModeSuite.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,23 @@
+package org.hibernate.test.entitymode;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.hibernate.test.entitymode.dom4j.Dom4jSuite;
+import org.hibernate.test.entitymode.map.MapSuite;
+import org.hibernate.test.entitymode.multi.MultiRepresentationTest;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class EntityModeSuite {
+ public static Test suite() {
+ TestSuite suite = new TestSuite( "entity-mode tests" );
+ suite.addTest( Dom4jSuite.suite() );
+ suite.addTest( MapSuite.suite() );
+ suite.addTest( MultiRepresentationTest.suite() );
+ return suite;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/Dom4jSuite.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/Dom4jSuite.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/Dom4jSuite.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,23 @@
+package org.hibernate.test.entitymode.dom4j;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.hibernate.test.entitymode.dom4j.accessors.Dom4jAccessorTest;
+import org.hibernate.test.entitymode.dom4j.basic.Dom4jTest;
+import org.hibernate.test.entitymode.dom4j.many2one.Dom4jManyToOneTest;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class Dom4jSuite {
+ public static Test suite() {
+ TestSuite suite = new TestSuite( "dom4j entity-mode suite" );
+ suite.addTest( Dom4jAccessorTest.suite() );
+ suite.addTest( Dom4jTest.suite() );
+ suite.addTest( Dom4jManyToOneTest.suite() );
+ return suite;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/accessors/Dom4jAccessorTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/accessors/Dom4jAccessorTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/accessors/Dom4jAccessorTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,157 @@
+// $Id: Dom4jAccessorTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.entitymode.dom4j.accessors;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.dom4j.DocumentFactory;
+import org.dom4j.Element;
+import org.dom4j.util.NodeComparator;
+
+import org.hibernate.EntityMode;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.mapping.Property;
+import org.hibernate.mapping.SimpleValue;
+import org.hibernate.property.Getter;
+import org.hibernate.property.PropertyAccessorFactory;
+import org.hibernate.property.Setter;
+
+/**
+ * Unit test of dom4j-based accessors
+ *
+ * @author Steve Ebersole
+ */
+public class Dom4jAccessorTest extends TestCase {
+
+ public static final Element DOM = generateTestElement();
+
+ public Dom4jAccessorTest(String name) {
+ super( name );
+ }
+
+ public void testStringElementExtraction() throws Throwable {
+ Property property = generateNameProperty();
+ Getter getter = PropertyAccessorFactory.getPropertyAccessor( property, EntityMode.DOM4J )
+ .getGetter( null, null );
+ String name = ( String ) getter.get( DOM );
+ assertEquals( "Not equals", "JBoss", name );
+ }
+
+ public void testStringTextExtraction() throws Throwable {
+ Property property = generateTextProperty();
+ Getter getter = PropertyAccessorFactory.getPropertyAccessor( property, EntityMode.DOM4J )
+ .getGetter( null, null );
+ String name = ( String ) getter.get( DOM );
+ assertEquals( "Not equals", "description...", name );
+ }
+
+ public void testLongAttributeExtraction() throws Throwable {
+ Property property = generateIdProperty();
+ Getter getter = PropertyAccessorFactory.getPropertyAccessor( property, EntityMode.DOM4J )
+ .getGetter( null, null );
+ Long id = ( Long ) getter.get( DOM );
+ assertEquals( "Not equals", new Long( 123 ), id );
+ }
+
+ public void testLongElementAttributeExtraction() throws Throwable {
+ Property property = generateAccountIdProperty();
+ Getter getter = PropertyAccessorFactory.getPropertyAccessor( property, EntityMode.DOM4J )
+ .getGetter( null, null );
+ Long id = ( Long ) getter.get( DOM );
+ assertEquals( "Not equals", new Long( 456 ), id );
+ }
+
+ public void testCompanyElementGeneration() throws Throwable {
+ Setter idSetter = PropertyAccessorFactory.getPropertyAccessor( generateIdProperty(), EntityMode.DOM4J )
+ .getSetter( null, null );
+ Setter nameSetter = PropertyAccessorFactory.getPropertyAccessor( generateNameProperty(), EntityMode.DOM4J )
+ .getSetter( null, null );
+ Setter textSetter = PropertyAccessorFactory.getPropertyAccessor( generateTextProperty(), EntityMode.DOM4J )
+ .getSetter( null, null );
+ Setter accountIdSetter = PropertyAccessorFactory.getPropertyAccessor(
+ generateAccountIdProperty(), EntityMode.DOM4J
+ )
+ .getSetter( null, null );
+
+ Element root = generateRootTestElement();
+
+ idSetter.set( root, new Long( 123 ), getSFI() );
+ textSetter.set( root, "description...", getSFI() );
+ nameSetter.set( root, "JBoss", getSFI() );
+ accountIdSetter.set( root, new Long( 456 ), getSFI() );
+
+ assertTrue( "DOMs not equal", new NodeComparator().compare( DOM, root ) == 0 );
+ }
+
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ private static Element generateTestElement() {
+ Element company = generateRootTestElement();
+ company.addAttribute( "id", "123" );
+ company.setText( "description..." );
+ company.addElement( "name" ).setText( "JBoss" );
+ company.addElement( "account" ).addAttribute( "num", "456" );
+
+ return company;
+ }
+
+ private static Element generateRootTestElement() {
+ return DocumentFactory.getInstance().createElement( "company" );
+ }
+
+ public static Test suite() {
+ return new TestSuite( Dom4jAccessorTest.class );
+ }
+
+ private SessionFactoryImplementor getSFI() {
+ return null;
+ }
+
+ private Property generateIdProperty() {
+ SimpleValue value = new SimpleValue();
+ value.setTypeName( "long" );
+
+ Property property = new Property();
+ property.setName( "id" );
+ property.setNodeName( "@id" );
+ property.setValue( value );
+
+ return property;
+ }
+
+ private Property generateTextProperty() {
+ SimpleValue value = new SimpleValue();
+ value.setTypeName( "string" );
+
+ Property property = new Property();
+ property.setName( "text" );
+ property.setNodeName( "." );
+ property.setValue( value );
+
+ return property;
+ }
+
+ private Property generateAccountIdProperty() {
+ SimpleValue value = new SimpleValue();
+ value.setTypeName( "long" );
+
+ Property property = new Property();
+ property.setName( "number" );
+ property.setNodeName( "account/@num" );
+ property.setValue( value );
+
+ return property;
+ }
+
+ private Property generateNameProperty() {
+ SimpleValue value = new SimpleValue();
+ value.setTypeName( "string" );
+
+ Property property = new Property();
+ property.setName( "name" );
+ property.setNodeName( "name" );
+ property.setValue( value );
+
+ return property;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/basic/AB.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/basic/AB.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/basic/AB.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -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">
+
+<hibernate-mapping >
+
+ <class entity-name="A" table="AX" node="a">
+ <id name="aId" type="int" column="aId" node="@id"/>
+ <property name="x" type="string"/>
+ <set name="bs" node="." embed-xml="true" cascade="all" inverse="true">
+ <key column="aId"/>
+ <one-to-many class="B"/>
+ </set>
+ </class>
+
+
+ <class entity-name="B" table="BX" node="b">
+ <composite-id>
+ <key-property name="bId" column="bId" type="int" node="@bId"/>
+ <key-property name="aId" column="aId" type="int" node="@aId"/>
+ </composite-id>
+ <property name="y" type="string" node="."/>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/basic/AB.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/basic/Account.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/basic/Account.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/basic/Account.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,51 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping>
+
+ <class entity-name="Customer" node="customer">
+ <id name="customerId" type="string" node="@id"/>
+ <component name="name">
+ <property name="first" type="string"/>
+ <property name="last" type="string"/>
+ </component>
+ <property name="address" type="string" node="address"/>
+ <map name="stuff">
+ <key column="customerId"/>
+ <map-key type="string" column="bar" node="@bar"/>
+ <element type="string" node="foo" column="foo"/>
+ </map>
+ <bag name="morestuff" node=".">
+ <key column="customerId"/>
+ <element type="integer" node="amount" column="amount"/>
+ </bag>
+ <list name="accounts" cascade="all">
+ <key column="customerId2"/>
+ <list-index column="acctno" base="1"/>
+ <one-to-many entity-name="Account" node="account"/>
+ </list>
+ <many-to-one name="location" node="location/@id" entity-name="Location" embed-xml="false"/>
+ <property name="description" node="." type="string"/>
+ <set name="unembedded" embed-xml="false">
+ <key column="x"/>
+ <element type="string" column="y" not-null="true"/>
+ </set>
+ </class>
+
+ <class entity-name="Account" node="account">
+ <id name="accountId" type="string" node="@id"/>
+ <many-to-one name="customer" column="customerId" entity-name="Customer" cascade="all" embed-xml="true" />
+ <!--not-null="true"-->
+ <property name="balance" type="big_decimal" node="balance" precision="10" scale="0" />
+ </class>
+
+ <class entity-name="Location" node="location">
+ <id name="id" node="@id" type="long">
+ <generator class="increment"/>
+ </id>
+ <property name="address" type="string"/>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/basic/Account.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/basic/Dom4jTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/basic/Dom4jTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/basic/Dom4jTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,360 @@
+// $Id: Dom4jTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.entitymode.dom4j.basic;
+
+import java.util.Map;
+
+import junit.framework.Test;
+import org.dom4j.DocumentFactory;
+import org.dom4j.Element;
+
+import org.hibernate.EntityMode;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.criterion.Example;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.transform.Transformers;
+import org.hibernate.util.XMLHelper;
+
+/**
+ * @author Gavin King
+ */
+public class Dom4jTest extends FunctionalTestCase {
+
+ public Dom4jTest(String str) {
+ super( str );
+ }
+
+ public String[] getMappings() {
+ return new String[] {
+ "entitymode/dom4j/basic/Account.hbm.xml",
+ "entitymode/dom4j/basic/AB.hbm.xml",
+ "entitymode/dom4j/basic/Employer.hbm.xml"
+ };
+ }
+
+ public void configure(Configuration cfg) {
+ cfg.setProperty( Environment.DEFAULT_ENTITY_MODE, EntityMode.DOM4J.toString() );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( Dom4jTest.class );
+ }
+
+// TODO : still need to figure out inheritence support within the DOM4J entity-mode
+//
+// public void testSubtyping() throws Exception {
+// Element employer = DocumentFactory.getInstance().createElement( "employer" );
+// employer.addAttribute( "name", "JBoss" );
+// Element gavin = employer.addElement( "techie" );
+// gavin.addAttribute( "name", "Gavin" );
+// Element ben = employer.addElement( "sales-dude" );
+// ben.addAttribute( "name", "Ben" );
+// print( employer );
+//
+// Session s = openSession();
+// Transaction t = s.beginTransaction();
+// s.persist( "Employer", employer );
+// Long eid = new Long( employer.attributeValue( "id" ) );
+// t.commit();
+// s.close();
+//
+// s = openSession();
+// t = s.beginTransaction();
+// employer = (Element) s.get( "Employer", eid );
+// print( employer );
+// s.delete( "Employer", employer );
+// t.commit();
+// s.close();
+//
+// Element dept = DocumentFactory.getInstance().createElement( "department" );
+// dept.addAttribute( "name", "engineering" );
+// Element steve = dept.addElement( "manager" ).addElement( "techie" );
+// steve.addAttribute( "name", "Steve" );
+// print( dept );
+//
+// s = openSession();
+// t = s.beginTransaction();
+// s.persist( "Department", dept );
+// Long did = new Long( dept.attributeValue( "id" ) );
+// t.commit();
+// s.close();
+//
+// s = openSession();
+// t = s.beginTransaction();
+// dept = ( Element ) s.load( "Department", did );
+// print( dept );
+// s.delete( "Department", dept );
+// t.commit();
+// s.close();
+// }
+
+ public void testCompositeId() throws Exception {
+ Element a = DocumentFactory.getInstance().createElement( "a" );
+ a.addAttribute("id", "1");
+ a.addElement("x").setText("foo bar");
+ //Element bs = a.addElement("bs");
+ Element b = a.addElement("b");
+ //b.addElement("bId").setText("1");
+ //b.addElement("aId").setText("1");
+ b.addAttribute("bId", "1");
+ b.addAttribute("aId", "1");
+ b.setText("foo foo");
+ b = a.addElement("b");
+ //b.addElement("bId").setText("2");
+ //b.addElement("aId").setText("1");
+ b.addAttribute("bId", "2");
+ b.addAttribute("aId", "1");
+ b.setText("bar bar");
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.persist("A", a);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ a = (Element) s.createCriteria("A").uniqueResult();
+ assertEquals( a.elements("b").size(), 2 );
+ print(a);
+ s.delete("A", a);
+ t.commit();
+ s.close();
+ }
+
+ public void testDom4j() throws Exception {
+ Element acct = DocumentFactory.getInstance().createElement( "account" );
+ acct.addAttribute( "id", "abc123" );
+ acct.addElement( "balance" ).setText( "123.45" );
+ Element cust = acct.addElement( "customer" );
+ cust.addAttribute( "id", "xyz123" );
+ Element foo1 = cust.addElement( "stuff" ).addElement( "foo" );
+ foo1.setText( "foo" );
+ foo1.addAttribute("bar", "x");
+ Element foo2 = cust.element( "stuff" ).addElement( "foo" );
+ foo2.setText( "bar" );
+ foo2.addAttribute("bar", "y");
+ cust.addElement( "amount" ).setText( "45" );
+ cust.setText( "An example customer" );
+ Element name = cust.addElement( "name" );
+ name.addElement( "first" ).setText( "Gavin" );
+ name.addElement( "last" ).setText( "King" );
+
+ Element loc = DocumentFactory.getInstance().createElement( "location" );
+ loc.addElement( "address" ).setText( "Karbarook Avenue" );
+
+ print( acct );
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.persist( "Location", loc );
+ cust.addElement( "location" ).addAttribute( "id", loc.attributeValue( "id" ) );
+ s.persist( "Account", acct );
+ t.commit();
+ s.close();
+
+ print( loc );
+
+ s = openSession();
+ t = s.beginTransaction();
+ cust = (Element) s.get( "Customer", "xyz123" );
+ print( cust );
+ acct = (Element) s.get( "Account", "abc123" );
+ print( acct );
+ assertEquals( acct.element( "customer" ), cust );
+ cust.element( "name" ).element( "first" ).setText( "Gavin A" );
+ Element foo3 = cust.element("stuff").addElement("foo");
+ foo3.setText("baz");
+ foo3.addAttribute("bar", "z");
+ cust.element("amount").setText("3");
+ cust.addElement("amount").setText("56");
+ t.commit();
+ s.close();
+
+ System.out.println();
+
+ acct.element( "balance" ).setText( "3456.12" );
+ cust.addElement( "address" ).setText( "Karbarook Ave" );
+
+ assertEquals( acct.element( "customer" ), cust );
+
+ cust.setText( "Still the same example!" );
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.saveOrUpdate( "Account", acct );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ cust = (Element) s.get( "Customer", "xyz123" );
+ print( cust );
+ acct = (Element) s.get( "Account", "abc123" );
+ print( acct );
+ assertEquals( acct.element( "customer" ), cust );
+ t.commit();
+ s.close();
+
+ System.out.println();
+
+ s = openSession();
+ t = s.beginTransaction();
+ cust = (Element) s.createCriteria( "Customer" )
+ .add( Example.create( cust ) )
+ .uniqueResult();
+ print( cust );
+ t.commit();
+ s.close();
+
+ System.out.println();
+
+ s = openSession();
+ t = s.beginTransaction();
+ acct = (Element) s.createQuery( "from Account a left join fetch a.customer" )
+ .uniqueResult();
+ print( acct );
+ t.commit();
+ s.close();
+
+ System.out.println();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Map m = (Map) s.createQuery( "select a as acc from Account a left join fetch a.customer" )
+ .setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).uniqueResult();
+ acct = (Element)m.get("acc");
+ print( acct );
+ t.commit();
+ s.close();
+
+ System.out.println();
+
+ s = openSession();
+ t = s.beginTransaction();
+ acct = (Element) s.createQuery( "from Account" ).uniqueResult();
+ print( acct );
+ t.commit();
+ s.close();
+
+ System.out.println();
+
+ s = openSession();
+ t = s.beginTransaction();
+ cust = (Element) s.createQuery( "from Customer c left join fetch c.stuff" ).uniqueResult();
+ print( cust );
+ t.commit();
+ s.close();
+
+ System.out.println();
+
+ s = openSession();
+ t = s.beginTransaction();
+ cust = (Element) s.createQuery( "from Customer c left join fetch c.morestuff" ).uniqueResult();
+ print( cust );
+ t.commit();
+ s.close();
+
+ System.out.println();
+
+ s = openSession();
+ t = s.beginTransaction();
+ cust = (Element) s.createQuery( "from Customer c left join fetch c.morestuff" ).uniqueResult();
+ print( cust );
+ cust = (Element) s.createQuery( "from Customer c left join fetch c.stuff" ).uniqueResult();
+ print( cust );
+ t.commit();
+ s.close();
+
+ System.out.println();
+
+ s = openSession();
+ t = s.beginTransaction();
+ cust = (Element) s.createQuery( "from Customer c left join fetch c.accounts" ).uniqueResult();
+ Element a1 = cust.element( "accounts" ).addElement( "account" );
+ a1.addElement( "balance" ).setText( "12.67" );
+ a1.addAttribute( "id", "lkj345" );
+ a1.addAttribute("acnum", "0");
+ Element a2 = cust.element( "accounts" ).addElement( "account" );
+ a2.addElement( "balance" ).setText( "10000.00" );
+ a2.addAttribute( "id", "hsh987" );
+ a2.addAttribute("acnum", "1");
+ print( cust );
+ t.commit();
+ s.close();
+
+ System.out.println();
+
+ s = openSession();
+ t = s.beginTransaction();
+ cust = (Element) s.createQuery( "from Customer c left join fetch c.accounts" ).uniqueResult();
+ print( cust );
+ t.commit();
+ s.close();
+
+ // clean up
+ s = openSession();
+ t = s.beginTransaction();
+ s.delete( "Account", acct );
+ s.delete( "Location", loc );
+ s.createQuery( "delete from Account" ).executeUpdate();
+ t.commit();
+ s.close();
+ }
+
+ public void testMapIndexEmision() throws Throwable {
+ Element acct = DocumentFactory.getInstance().createElement( "account" );
+ acct.addAttribute( "id", "abc123" );
+ acct.addElement( "balance" ).setText( "123.45" );
+ Element cust = acct.addElement( "customer" );
+ cust.addAttribute( "id", "xyz123" );
+ Element foo1 = cust.addElement( "stuff" ).addElement( "foo" );
+ foo1.setText( "foo" );
+ foo1.addAttribute("bar", "x");
+ Element foo2 = cust.element( "stuff" ).addElement( "foo" );
+ foo2.setText( "bar" );
+ foo2.addAttribute("bar", "y");
+ cust.addElement( "amount" ).setText( "45" );
+ cust.setText( "An example customer" );
+ Element name = cust.addElement( "name" );
+ name.addElement( "first" ).setText( "Gavin" );
+ name.addElement( "last" ).setText( "King" );
+
+ print( acct );
+
+ Element loc = DocumentFactory.getInstance().createElement( "location" );
+ loc.addElement( "address" ).setText( "Karbarook Avenue" );
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.persist( "Location", loc );
+ cust.addElement( "location" ).addAttribute( "id", loc.attributeValue( "id" ) );
+ s.persist( "Account", acct );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ cust = ( Element ) s.get( "Customer", "xyz123" );
+ print( cust );
+ assertEquals( "Incorrect stuff-map size", 2, cust.element( "stuff" ).elements( "foo" ).size() );
+ Element stuffElement = ( Element ) cust.element( "stuff" ).elements( "foo" ).get( 0 );
+ assertNotNull( "No map-key value present", stuffElement.attribute( "bar" ) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.delete( "Account", acct );
+ s.delete( "Location", loc );
+ t.commit();
+ s.close();
+ }
+
+ public static void print(Element elt) throws Exception {
+ XMLHelper.dump( elt );
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/basic/Dom4jTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/basic/Employer.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/basic/Employer.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/basic/Employer.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,36 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping>
+
+ <class entity-name="Employer" node="employer">
+ <id name="id" node="@id" type="long">
+ <generator class="increment"/>
+ </id>
+ <property name="name" node="@name" type="string"/>
+ <set name="employees" node="." cascade="all,delete-orphan" fetch="join" lazy="false">
+ <key not-null="true" column="employerId"/>
+ <one-to-many entity-name="Employee" />
+ </set>
+ </class>
+
+ <class entity-name="Employee" node="employee">
+ <id name="id" node="@id" type="long">
+ <generator class="increment"/>
+ </id>
+ <discriminator column="role" type="string" length="10"/>
+ <property name="name" node="@name" type="string"/>
+ <subclass entity-name="Techie" node="techie" />
+ <subclass entity-name="Salesdude" node="sales-dude"/>
+ </class>
+
+ <class entity-name="Department" node="department">
+ <id name="id" node="@id" type="long">
+ <generator class="increment"/>
+ </id>
+ <property name="name" node="@name" type="string"/>
+ <many-to-one name="manager" entity-name="Employee" cascade="all" fetch="join" lazy="false" embed-xml="true" node="manager" />
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/basic/Employer.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/many2one/Car.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/many2one/Car.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/many2one/Car.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.entitymode.dom4j.many2one">
+
+ <class name="Car" lazy="false" node="car">
+ <id name="id" node="@id" type="long">
+ <generator class="increment"/>
+ </id>
+ <property name="model" type="string" node="model"/>
+ <many-to-one name="carType" node="carType" class="CarType"/>
+ <set name="carParts" node="." cascade="all">
+ <key column="car" not-null="true"/>
+ <one-to-many class="CarPart" node="carPart" embed-xml="false"/>
+ </set>
+ </class>
+
+ <class name="CarType" lazy="true" node="carType">
+ <id name="id" node="@id" type="long">
+ <generator class="increment"/>
+ </id>
+ <property name="typeName" type="string" node="typeName"/>
+ </class>
+
+ <class name="CarPart" node="carPart">
+ <id name="id" node="@id" type="long">
+ <generator class="increment"/>
+ </id>
+ <property name="partName" type="string" node="partName"/>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/many2one/Car.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/many2one/Car.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/many2one/Car.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/many2one/Car.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,58 @@
+package org.hibernate.test.entitymode.dom4j.many2one;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author Paco Hernández
+ */
+public class Car implements java.io.Serializable {
+
+ private long id;
+ private String model;
+ private CarType carType;
+ private Set carParts = new HashSet();
+
+ /**
+ * @return Returns the carType.
+ */
+ public CarType getCarType() {
+ return carType;
+ }
+ /**
+ * @param carType The carType to set.
+ */
+ public void setCarType(CarType carType) {
+ this.carType = carType;
+ }
+ /**
+ * @return Returns the id.
+ */
+ public long getId() {
+ return id;
+ }
+ /**
+ * @param id The id to set.
+ */
+ public void setId(long id) {
+ this.id = id;
+ }
+ /**
+ * @return Returns the model.
+ */
+ public String getModel() {
+ return model;
+ }
+ /**
+ * @param model The model to set.
+ */
+ public void setModel(String model) {
+ this.model = model;
+ }
+ public Set getCarParts() {
+ return carParts;
+ }
+ public void setCarParts(Set carParts) {
+ this.carParts = carParts;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/many2one/Car.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/many2one/CarPart.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/many2one/CarPart.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/many2one/CarPart.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,35 @@
+package org.hibernate.test.entitymode.dom4j.many2one;
+
+/**
+ * @author Paco Hernández
+ */
+public class CarPart implements java.io.Serializable {
+
+ private long id;
+ private String partName;
+
+ /**
+ * @return Returns the id.
+ */
+ public long getId() {
+ return id;
+ }
+ /**
+ * @param id The id to set.
+ */
+ public void setId(long id) {
+ this.id = id;
+ }
+ /**
+ * @return Returns the typeName.
+ */
+ public String getPartName() {
+ return partName;
+ }
+ /**
+ * @param typeName The typeName to set.
+ */
+ public void setPartName(String typeName) {
+ this.partName = typeName;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/many2one/CarPart.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/many2one/CarType.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/many2one/CarType.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/many2one/CarType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,35 @@
+package org.hibernate.test.entitymode.dom4j.many2one;
+
+/**
+ * @author Paco Hernández
+ */
+public class CarType implements java.io.Serializable {
+
+ private long id;
+ private String typeName;
+
+ /**
+ * @return Returns the id.
+ */
+ public long getId() {
+ return id;
+ }
+ /**
+ * @param id The id to set.
+ */
+ public void setId(long id) {
+ this.id = id;
+ }
+ /**
+ * @return Returns the typeName.
+ */
+ public String getTypeName() {
+ return typeName;
+ }
+ /**
+ * @param typeName The typeName to set.
+ */
+ public void setTypeName(String typeName) {
+ this.typeName = typeName;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/many2one/CarType.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/many2one/Dom4jManyToOneTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/many2one/Dom4jManyToOneTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/many2one/Dom4jManyToOneTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,141 @@
+package org.hibernate.test.entitymode.dom4j.many2one;
+
+import java.util.List;
+
+import junit.framework.Test;
+import org.dom4j.Element;
+import org.dom4j.io.OutputFormat;
+import org.dom4j.io.XMLWriter;
+
+import org.hibernate.EntityMode;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Paco Hernández
+ */
+public class Dom4jManyToOneTest extends FunctionalTestCase {
+
+ public Dom4jManyToOneTest(String str) {
+ super( str );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "entitymode/dom4j/many2one/Car.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( Dom4jManyToOneTest.class );
+ }
+
+ public void testDom4jOneToMany() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ CarType carType = new CarType();
+ carType.setTypeName("Type 1");
+ s.save(carType);
+
+ Car car = new Car();
+ car.setCarType(carType);
+ car.setModel("Model 1");
+ s.save(car);
+
+ CarPart carPart1 = new CarPart();
+ carPart1.setPartName("chassis");
+ car.getCarParts().add(carPart1);
+
+ t.commit();
+ s.close();
+
+ s = openSession();
+ Session dom4jSession = s.getSession( EntityMode.DOM4J );
+ t = s.beginTransaction();
+
+ Element element = (Element) dom4jSession.createQuery( "from Car c join fetch c.carParts" ).uniqueResult();
+
+ String expectedResult = "<car id=\"" +
+ car.getId() +
+ "\"><carPart>" +
+ carPart1.getId() +
+ "</carPart><model>Model 1</model><carType id=\"" +
+ carType.getId() +
+ "\"><typeName>Type 1</typeName></carType></car>";
+
+ print(element);
+ assertTrue(element.asXML().equals(expectedResult));
+
+ s.createQuery("delete from CarPart").executeUpdate();
+ s.createQuery("delete from Car").executeUpdate();
+ s.createQuery("delete from CarType").executeUpdate();
+
+ t.commit();
+ s.close();
+ }
+
+ public void testDom4jManyToOne() throws Exception {
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ CarType carType = new CarType();
+ carType.setTypeName("Type 1");
+ s.save(carType);
+
+ Car car1 = new Car();
+ car1.setCarType(carType);
+ car1.setModel("Model 1");
+ s.save(car1);
+
+ Car car2 = new Car();
+ car2.setCarType(carType);
+ car2.setModel("Model 2");
+ s.save(car2);
+
+ t.commit();
+ s.close();
+
+ s = openSession();
+ Session dom4jSession = s.getSession( EntityMode.DOM4J );
+ t = s.beginTransaction();
+
+ List list = dom4jSession.createQuery( "from Car c join fetch c.carType order by c.model asc" ).list();
+
+ String[] expectedResults = new String[] {
+ "<car id=\"" +
+ car1.getId() +
+ "\"><model>Model 1</model><carType id=\"" +
+ carType.getId() +
+ "\"><typeName>Type 1</typeName></carType></car>",
+ "<car id=\"" +
+ car2.getId() +
+ "\"><model>Model 2</model><carType id=\"" +
+ carType.getId() +
+ "\"><typeName>Type 1</typeName></carType></car>"
+ };
+
+ for (int i = 0; i < list.size(); i++) {
+ Element element = (Element) list.get(i);
+
+ print(element);
+ assertTrue(element.asXML().equals(expectedResults[i]));
+ }
+
+ s.createQuery("delete from Car").executeUpdate();
+ s.createQuery("delete from CarType").executeUpdate();
+
+ t.commit();
+ s.close();
+ }
+
+ public static void print(Element elt) throws Exception {
+ OutputFormat outformat = OutputFormat.createPrettyPrint();
+ // outformat.setEncoding(aEncodingScheme);
+ XMLWriter writer = new XMLWriter( System.out, outformat );
+ writer.write( elt );
+ writer.flush();
+ // System.out.println( elt.asXML() );
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/dom4j/many2one/Dom4jManyToOneTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/map/MapSuite.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/map/MapSuite.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/map/MapSuite.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,19 @@
+package org.hibernate.test.entitymode.map;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.hibernate.test.entitymode.map.basic.DynamicClassTest;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class MapSuite {
+ public static Test suite() {
+ TestSuite suite = new TestSuite( "map entity-mode suite");
+ suite.addTest( DynamicClassTest.suite() );
+ return suite;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/map/basic/DynamicClassTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/map/basic/DynamicClassTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/map/basic/DynamicClassTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,109 @@
+//$Id: DynamicClassTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.entitymode.map.basic;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.Test;
+
+import org.hibernate.EntityMode;
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class DynamicClassTest extends FunctionalTestCase {
+
+ public DynamicClassTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "entitymode/map/basic/ProductLine.hbm.xml" };
+ }
+
+ public void configure(Configuration cfg) {
+ cfg.setProperty(Environment.DEFAULT_ENTITY_MODE, EntityMode.MAP.toString());
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( DynamicClassTest.class );
+ }
+
+ public void testLazyDynamicClass() {
+ Session s = openSession();
+ assertTrue( "Incorrectly handled default_entity_mode", s.getEntityMode() == EntityMode.MAP );
+ Session other = s.getSession( EntityMode.MAP );
+ assertEquals( "openSession() using same entity-mode returned new session", s, other );
+
+ other = s.getSession( EntityMode.POJO );
+ other.close();
+ assertTrue( !other.isOpen() );
+// this is no longer allowed since the session does much more up-front closed checking
+// assertTrue( other.isConnected() ); // because it is linked to the "root" session's connection
+
+ s.close();
+
+ s = openSession();
+ Transaction t = s.beginTransaction();
+
+ Map cars = new HashMap();
+ cars.put("description", "Cars");
+ Map monaro = new HashMap();
+ monaro.put("productLine", cars);
+ monaro.put("name", "monaro");
+ monaro.put("description", "Holden Monaro");
+ Map hsv = new HashMap();
+ hsv.put("productLine", cars);
+ hsv.put("name", "hsv");
+ hsv.put("description", "Holden Commodore HSV");
+ List models = new ArrayList();
+ cars.put("models", models);
+ models.add(hsv);
+ models.add(monaro);
+ s.save("ProductLine", cars);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+
+ cars = (Map) s.createQuery("from ProductLine pl order by pl.description").uniqueResult();
+ models = (List) cars.get("models");
+ assertFalse( Hibernate.isInitialized(models) );
+ assertEquals( models.size(), 2);
+ assertTrue( Hibernate.isInitialized(models) );
+
+ s.clear();
+
+ List list = s.createQuery("from Model m").list();
+ for ( Iterator i=list.iterator(); i.hasNext(); ) {
+ assertFalse( Hibernate.isInitialized( ( (Map) i.next() ).get("productLine") ) );
+ }
+ Map model = (Map) list.get(0);
+ assertTrue( ( (List) ( (Map) model.get("productLine") ).get("models") ).contains(model) );
+ s.clear();
+
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ cars = (Map) s.createQuery("from ProductLine pl order by pl.description").uniqueResult();
+ s.delete(cars);
+ t.commit();
+ s.close();
+ }
+
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/map/basic/DynamicClassTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/map/basic/ProductLine.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/map/basic/ProductLine.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/map/basic/ProductLine.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping>
+
+<!--
+ This mapping demonstrates "dynamic" entities.
+-->
+
+ <class entity-name="ProductLine">
+ <id name="id" column="productId" length="32" type="string">
+ <generator class="uuid.hex"/>
+ </id>
+
+ <property name="description" not-null="true" length="200" type="string"/>
+
+ <!-- don't use sets for associations, unless you want stack overflows! -->
+ <bag name="models" cascade="all" inverse="true">
+ <key column="productId"/>
+ <one-to-many class="Model"/>
+ </bag>
+
+ </class>
+
+ <class entity-name="Model">
+ <id name="id" column="modelId" length="32" type="string">
+ <generator class="uuid.hex"/>
+ </id>
+
+ <property name="name" not-null="true" length="25" type="string"/>
+ <property name="description" not-null="true" length="200" type="string"/>
+ <many-to-one name="productLine" column="productId" not-null="true" class="ProductLine"/>
+
+ </class>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/map/basic/ProductLine.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/multi/MultiRepresentationTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/multi/MultiRepresentationTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/multi/MultiRepresentationTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,194 @@
+// $Id: MultiRepresentationTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.entitymode.multi;
+
+import java.sql.Date;
+import java.util.Iterator;
+import java.util.List;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.dom4j.DocumentFactory;
+import org.dom4j.Element;
+import org.dom4j.io.OutputFormat;
+import org.dom4j.io.XMLWriter;
+
+import org.hibernate.EntityMode;
+import org.hibernate.Transaction;
+import org.hibernate.classic.Session;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * Implementation of MultiRepresentationTest.
+ *
+ * @author Steve Ebersole
+ */
+public class MultiRepresentationTest extends FunctionalTestCase {
+
+
+ public MultiRepresentationTest(String name) {
+ super( name );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "entitymode/multi/Stock.hbm.xml", "entitymode/multi/Valuation.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( MultiRepresentationTest.class );
+ }
+
+ public void testPojoRetreival() {
+ TestData testData = new TestData();
+ testData.create();
+
+ Session session = openSession();
+ Transaction txn = session.beginTransaction();
+
+ Stock stock = ( Stock ) session.get( Stock.class, new Long( 1 ) );
+ assertEquals( "Something wrong!", new Long( 1 ), stock.getId() );
+
+ txn.commit();
+ session.close();
+
+ testData.destroy();
+ }
+
+ public void testDom4jRetreival() {
+ TestData testData = new TestData();
+ testData.create();
+
+ Session session = openSession();
+ Transaction txn = session.beginTransaction();
+ org.hibernate.Session dom4j = session.getSession( EntityMode.DOM4J );
+
+ Object rtn = dom4j.get( Stock.class.getName(), testData.stockId );
+ Element element = ( Element ) rtn;
+
+ assertEquals( "Something wrong!", testData.stockId, Long.valueOf( element.attributeValue( "id" ) ) );
+
+ System.out.println( "**** XML: ****************************************************" );
+ prettyPrint( element );
+ System.out.println( "**************************************************************" );
+
+ Element currVal = element.element( "currentValuation" );
+
+ System.out.println( "**** XML: ****************************************************" );
+ prettyPrint( currVal );
+ System.out.println( "**************************************************************" );
+
+ txn.rollback();
+ session.close();
+
+ testData.destroy();
+ }
+
+ public void testDom4jSave() {
+ TestData testData = new TestData();
+ testData.create();
+
+ Session pojos = openSession();
+ Transaction txn = pojos.beginTransaction();
+ org.hibernate.Session dom4j = pojos.getSession( EntityMode.DOM4J );
+
+ Element stock = DocumentFactory.getInstance().createElement( "stock" );
+ stock.addElement( "tradeSymbol" ).setText( "IBM" );
+
+ Element val = stock.addElement( "currentValuation" ).addElement( "valuation" );
+ val.appendContent( stock );
+ val.addElement( "valuationDate" ).setText( new java.util.Date().toString() );
+ val.addElement( "value" ).setText( "121.00" );
+
+ dom4j.save( Stock.class.getName(), stock );
+ dom4j.flush();
+
+ txn.rollback();
+ pojos.close();
+
+ assertTrue( !pojos.isOpen() );
+ assertTrue( !dom4j.isOpen() );
+
+ prettyPrint( stock );
+
+ testData.destroy();
+ }
+
+ public void testDom4jHQL() {
+ TestData testData = new TestData();
+ testData.create();
+
+ Session session = openSession();
+ Transaction txn = session.beginTransaction();
+ org.hibernate.Session dom4j = session.getSession( EntityMode.DOM4J );
+
+ List result = dom4j.createQuery( "from Stock" ).list();
+
+ assertEquals( "Incorrect result size", 1, result.size() );
+ Element element = ( Element ) result.get( 0 );
+ assertEquals( "Something wrong!", testData.stockId, Long.valueOf( element.attributeValue( "id" ) ) );
+
+ System.out.println( "**** XML: ****************************************************" );
+ prettyPrint( element );
+ System.out.println( "**************************************************************" );
+
+ txn.rollback();
+ session.close();
+
+ testData.destroy();
+ }
+
+ private class TestData {
+ private Long stockId;
+
+ private void create() {
+ Session session = getSessions().openSession();
+ session.beginTransaction();
+ Stock stock = new Stock();
+ stock.setTradeSymbol( "JBOSS" );
+ Valuation valuation = new Valuation();
+ valuation.setStock( stock );
+ valuation.setValuationDate( new Date( new java.util.Date().getTime() ) );
+ valuation.setValue( new Double( 200.0 ) );
+ stock.setCurrentValuation( valuation );
+ stock.getValuations().add( valuation );
+
+ session.save( stock );
+ session.save( valuation );
+
+ session.getTransaction().commit();
+ session.close();
+
+ stockId = stock.getId();
+ }
+
+ private void destroy() {
+ Session session = getSessions().openSession();
+ session.beginTransaction();
+ Iterator stocks = session.createQuery( "from Stock" ).list().iterator();
+ while ( stocks.hasNext() ) {
+ final Stock stock = ( Stock ) stocks.next();
+ stock.setCurrentValuation( null );
+ session.flush();
+ Iterator valuations = stock.getValuations().iterator();
+ while ( valuations.hasNext() ) {
+ session.delete( valuations.next() );
+ }
+ session.delete( stock );
+ }
+ session.getTransaction().commit();
+ session.close();
+ }
+ }
+
+ private void prettyPrint(Element element) {
+ //System.out.println( element.asXML() );
+ try {
+ OutputFormat format = OutputFormat.createPrettyPrint();
+ new XMLWriter( System.out, format ).write( element );
+ System.out.println();
+ }
+ catch ( Throwable t ) {
+ System.err.println( "Unable to pretty print element : " + t );
+ }
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/multi/Stock.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/multi/Stock.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/multi/Stock.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping
+ SYSTEM
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
+
+<hibernate-mapping package="org.hibernate.test.entitymode.multi">
+
+ <class table="STOCK" name="Stock" node="stock">
+
+ <id name="id" column="STOCK_ID" node="@id">
+ <generator class="increment"/>
+ </id>
+
+ <property name="tradeSymbol" type="string" column="SYMBOL"/>
+
+ <many-to-one name="currentValuation" class="Valuation" column="CURR_VAL_ID" cascade="all" />
+
+ <set name="valuations" cascade="all" lazy="true">
+ <key column="STOCK_ID"/>
+ <one-to-many class="Valuation"/>
+ </set>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/multi/Stock.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/multi/Stock.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/multi/Stock.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,49 @@
+// $Id: Stock.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.entitymode.multi;
+
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * POJO implementation of Stock entity.
+ *
+ * @author Steve Ebersole
+ */
+public class Stock {
+ private Long id;
+ private String tradeSymbol;
+ private Valuation currentValuation;
+ private Set valuations = new HashSet();
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getTradeSymbol() {
+ return tradeSymbol;
+ }
+
+ public void setTradeSymbol(String tradeSymbol) {
+ this.tradeSymbol = tradeSymbol;
+ }
+
+ public Valuation getCurrentValuation() {
+ return currentValuation;
+ }
+
+ public void setCurrentValuation(Valuation currentValuation) {
+ this.currentValuation = currentValuation;
+ }
+
+ public Set getValuations() {
+ return valuations;
+ }
+
+ public void setValuations(Set valuations) {
+ this.valuations = valuations;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/multi/Valuation.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/multi/Valuation.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/multi/Valuation.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping
+ SYSTEM
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
+
+<hibernate-mapping package="org.hibernate.test.entitymode.multi">
+
+ <class table="STOCK_VAL" name="Valuation" node="valuation">
+
+ <id name="id" column="VAL_ID" node="@id">
+ <generator class="increment"/>
+ </id>
+
+ <many-to-one name="stock" embed-xml="false"
+ class="Stock" column="STOCK_ID" cascade="none" />
+
+ <property name="valuationDate" type="date" column="DT"/>
+ <property name="value" type="double" column="VAL"/>
+
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/multi/Valuation.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/multi/Valuation.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/entitymode/multi/Valuation.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,48 @@
+// $Id: Valuation.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.entitymode.multi;
+
+import java.util.Date;
+
+/**
+ * Implementation of Valuation.
+ *
+ * @author Steve Ebersole
+ */
+public class Valuation {
+ private Long id;
+ private Stock stock;
+ private Date valuationDate;
+ private Double value;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public Stock getStock() {
+ return stock;
+ }
+
+ public void setStock(Stock stock) {
+ this.stock = stock;
+ }
+
+ public Date getValuationDate() {
+ return valuationDate;
+ }
+
+ public void setValuationDate(Date valuationDate) {
+ this.valuationDate = valuationDate;
+ }
+
+ public Double getValue() {
+ return value;
+ }
+
+ public void setValue(Double value) {
+ this.value = value;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/exception/Group.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/exception/Group.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/exception/Group.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,15 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping SYSTEM "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
+
+<hibernate-mapping package="org.hibernate.test.exception" >
+ <class name="Group" table="T_GROUP" >
+ <id name="id" unsaved-value="null" column="group_id" >
+ <generator class="native"/>
+ </id>
+ <property name="name" type="string" column="name" />
+ <set name="members" inverse="true" table="T_MEMBERSHIP" cascade="none">
+ <key column="group_id"/>
+ <many-to-many class="User" column="user_id"/>
+ </set>
+ </class>
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/exception/Group.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/exception/Group.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/exception/Group.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,48 @@
+// $Id: Group.java 4746 2004-11-11 20:57:28Z steveebersole $
+package org.hibernate.test.exception;
+
+import java.util.Set;
+
+/**
+ * Implementation of Group.
+ *
+ * @author Steve Ebersole
+ */
+public class Group {
+ private Long id;
+ private String name;
+ private Set members;
+
+ 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 Set getMembers() {
+ return members;
+ }
+
+ public void setMembers(Set members) {
+ this.members = members;
+ }
+
+ public void addMember(User member) {
+ if (member == null) {
+ throw new IllegalArgumentException("Member to add cannot be null");
+ }
+
+ this.members.add(member);
+ member.getMemberships().add(this);
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/exception/SQLExceptionConversionTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/exception/SQLExceptionConversionTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/exception/SQLExceptionConversionTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,112 @@
+// $Id: SQLExceptionConversionTest.java 11339 2007-03-23 12:51:38Z steve.ebersole at jboss.com $
+package org.hibernate.test.exception;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+import junit.framework.Test;
+
+import org.hibernate.JDBCException;
+import org.hibernate.Session;
+import org.hibernate.dialect.MySQLMyISAMDialect;
+import org.hibernate.exception.ConstraintViolationException;
+import org.hibernate.exception.SQLExceptionConverter;
+import org.hibernate.exception.SQLGrammarException;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.util.JDBCExceptionReporter;
+
+/**
+ * Implementation of SQLExceptionConversionTest.
+ *
+ * @author Steve Ebersole
+ */
+public class SQLExceptionConversionTest extends FunctionalTestCase {
+
+ public SQLExceptionConversionTest(String name) {
+ super(name);
+ }
+
+ public String[] getMappings() {
+ return new String[] {"exception/User.hbm.xml", "exception/Group.hbm.xml"};
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite(SQLExceptionConversionTest.class);
+ }
+
+ public void testIntegrityViolation() throws Exception {
+ if ( getDialect() instanceof MySQLMyISAMDialect ) {
+ reportSkip( "MySQL (ISAM) does not support FK violation checking", "exception conversion" );
+ return;
+ }
+
+ SQLExceptionConverter converter = getDialect().buildSQLExceptionConverter();
+
+ Session session = openSession();
+ Connection connection = session.connection();
+
+ // Attempt to insert some bad values into the T_MEMBERSHIP table that should
+ // result in a constraint violation
+ PreparedStatement ps = null;
+ try {
+ ps = connection.prepareStatement("INSERT INTO T_MEMBERSHIP (user_id, group_id) VALUES (?, ?)");
+ ps.setLong(1, 52134241); // Non-existent user_id
+ ps.setLong(2, 5342); // Non-existent group_id
+ ps.executeUpdate();
+
+ fail("INSERT should have failed");
+ }
+ catch(SQLException sqle) {
+ JDBCExceptionReporter.logExceptions(sqle, "Just output!!!!");
+ JDBCException jdbcException = converter.convert(sqle, null, null);
+ assertEquals( "Bad conversion [" + sqle.getMessage() + "]", ConstraintViolationException.class , jdbcException.getClass() );
+ ConstraintViolationException ex = (ConstraintViolationException) jdbcException;
+ System.out.println("Violated constraint name: " + ex.getConstraintName());
+ }
+ finally {
+ if ( ps != null ) {
+ try {
+ ps.close();
+ }
+ catch( Throwable ignore ) {
+ // ignore...
+ }
+ }
+ }
+
+ session.close();
+ }
+
+ public void testBadGrammar() throws Exception {
+ SQLExceptionConverter converter = getDialect().buildSQLExceptionConverter();
+
+ Session session = openSession();
+ Connection connection = session.connection();
+
+ // prepare a query against a non-existent table
+ PreparedStatement ps = null;
+ try {
+ ps = connection.prepareStatement("SELECT user_id, user_name FROM tbl_user");
+ ps.executeQuery();
+
+ fail("SQL compilation should have failed");
+ }
+ catch( SQLException sqle ) {
+ assertEquals( "Bad conversion [" + sqle.getMessage() + "]", SQLGrammarException.class, converter.convert(sqle, null, null).getClass() );
+ }
+ finally {
+ if ( ps != null ) {
+ try {
+ ps.close();
+ }
+ catch( Throwable ignore ) {
+ // ignore...
+ }
+ }
+ }
+
+ session.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/exception/User.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/exception/User.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/exception/User.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,15 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping SYSTEM "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
+
+<hibernate-mapping package="org.hibernate.test.exception" >
+ <class name="User" table="T_USER" >
+ <id name="id" unsaved-value="null" column="user_id" >
+ <generator class="native"/>
+ </id>
+ <property name="username" type="string" column="user_name" />
+ <set name="memberships" inverse="false" table="T_MEMBERSHIP" cascade="none">
+ <key column="user_id"/>
+ <many-to-many class="Group" column="group_id"/>
+ </set>
+ </class>
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/exception/User.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/exception/User.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/exception/User.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,49 @@
+// $Id: User.java 4746 2004-11-11 20:57:28Z steveebersole $
+package org.hibernate.test.exception;
+
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * Implementation of User.
+ *
+ * @author Steve Ebersole
+ */
+public class User {
+ private Long id;
+ private String username;
+ private Set memberships = new HashSet();
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public Set getMemberships() {
+ return memberships;
+ }
+
+ public void setMemberships(Set memberships) {
+ this.memberships = memberships;
+ }
+
+ public void addMembership(Group membership) {
+ if (membership == null) {
+ throw new IllegalArgumentException("Membership to add cannot be null");
+ }
+
+ this.memberships.add(membership);
+ membership.getMembers().add(this);
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/Customer.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/Customer.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/Customer.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+ This mapping demonstrates
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.extendshbm">
+ <subclass name="Customer" extends="Person">
+ <property name="comments"/>
+ <many-to-one name="salesperson"/>
+ </subclass>
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/Customer.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/Customer.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/Customer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,35 @@
+//$Id: Customer.java 5011 2004-12-19 22:01:25Z maxcsaucdk $
+package org.hibernate.test.extendshbm;
+
+/**
+ * @author Gavin King
+ */
+public class Customer extends Person {
+ private Employee salesperson;
+ private String comments;
+
+ /**
+ * @return Returns the salesperson.
+ */
+ public Employee getSalesperson() {
+ return salesperson;
+ }
+ /**
+ * @param salesperson The salesperson to set.
+ */
+ public void setSalesperson(Employee salesperson) {
+ this.salesperson = salesperson;
+ }
+ /**
+ * @return Returns the comments.
+ */
+ public String getComments() {
+ return comments;
+ }
+ /**
+ * @param comments The comments to set.
+ */
+ public void setComments(String comments) {
+ this.comments = comments;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/Employee.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/Employee.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/Employee.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+ This mapping demonstrates
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.extendshbm">
+ <subclass name="Employee" extends="Person">
+ <property name="title"
+ length="20"/>
+ <property name="salary"
+ length="0"/>
+ <many-to-one name="manager"/>
+ </subclass>
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/Employee.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/Employee.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/Employee.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,49 @@
+//$Id: Employee.java 5011 2004-12-19 22:01:25Z maxcsaucdk $
+package org.hibernate.test.extendshbm;
+
+import java.math.BigDecimal;
+
+/**
+ * @author Gavin King
+ */
+public class Employee extends Person {
+ private String title;
+ private BigDecimal salary;
+ private Employee manager;
+ /**
+ * @return Returns the title.
+ */
+ public String getTitle() {
+ return title;
+ }
+ /**
+ * @param title The title to set.
+ */
+ public void setTitle(String title) {
+ this.title = title;
+ }
+ /**
+ * @return Returns the manager.
+ */
+ public Employee getManager() {
+ return manager;
+ }
+ /**
+ * @param manager The manager to set.
+ */
+ public void setManager(Employee manager) {
+ this.manager = manager;
+ }
+ /**
+ * @return Returns the salary.
+ */
+ public BigDecimal getSalary() {
+ return salary;
+ }
+ /**
+ * @param salary The salary to set.
+ */
+ public void setSalary(BigDecimal salary) {
+ this.salary = salary;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/ExtendsTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/ExtendsTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/ExtendsTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,192 @@
+//$Id: ExtendsTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.extendshbm;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.hibernate.HibernateException;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.junit.UnitTestCase;
+
+/**
+ * @author Gavin King
+ */
+public class ExtendsTest extends UnitTestCase {
+
+ public ExtendsTest(String str) {
+ super( str );
+ }
+
+ public static Test suite() {
+ return new TestSuite( ExtendsTest.class );
+ }
+
+ private String getBaseForMappings() {
+ return "org/hibernate/test/";
+ }
+
+ public void testAllInOne() {
+ Configuration cfg = new Configuration();
+
+ cfg.addResource( getBaseForMappings() + "extendshbm/allinone.hbm.xml" );
+ assertNotNull( cfg.getClassMapping( "org.hibernate.test.extendshbm.Customer" ) );
+ assertNotNull( cfg.getClassMapping( "org.hibernate.test.extendshbm.Person" ) );
+ assertNotNull( cfg.getClassMapping( "org.hibernate.test.extendshbm.Employee" ) );
+ }
+
+ public void testOutOfOrder() {
+ Configuration cfg = new Configuration();
+
+ try {
+ cfg.addResource( getBaseForMappings() + "extendshbm/Customer.hbm.xml" );
+ assertNull(
+ "cannot be in the configuration yet!",
+ cfg.getClassMapping( "org.hibernate.test.extendshbm.Customer" )
+ );
+ cfg.addResource( getBaseForMappings() + "extendshbm/Person.hbm.xml" );
+ cfg.addResource( getBaseForMappings() + "extendshbm/Employee.hbm.xml" );
+
+ cfg.buildSessionFactory();
+
+ assertNotNull( cfg.getClassMapping( "org.hibernate.test.extendshbm.Customer" ) );
+ assertNotNull( cfg.getClassMapping( "org.hibernate.test.extendshbm.Person" ) );
+ assertNotNull( cfg.getClassMapping( "org.hibernate.test.extendshbm.Employee" ) );
+
+ }
+ catch ( HibernateException e ) {
+ fail( "should not fail with exception! " + e );
+ }
+
+ }
+
+ public void testNwaitingForSuper() {
+ Configuration cfg = new Configuration();
+
+ try {
+ cfg.addResource( getBaseForMappings() + "extendshbm/Customer.hbm.xml" );
+ assertNull(
+ "cannot be in the configuration yet!",
+ cfg.getClassMapping( "org.hibernate.test.extendshbm.Customer" )
+ );
+ cfg.addResource( getBaseForMappings() + "extendshbm/Employee.hbm.xml" );
+ assertNull(
+ "cannot be in the configuration yet!",
+ cfg.getClassMapping( "org.hibernate.test.extendshbm.Employee" )
+ );
+ cfg.addResource( getBaseForMappings() + "extendshbm/Person.hbm.xml" );
+
+ cfg.buildMappings();
+
+ assertNotNull( cfg.getClassMapping( "org.hibernate.test.extendshbm.Person" ) );
+ assertNotNull( cfg.getClassMapping( "org.hibernate.test.extendshbm.Employee" ) );
+ assertNotNull( cfg.getClassMapping( "org.hibernate.test.extendshbm.Customer" ) );
+
+
+ }
+ catch ( HibernateException e ) {
+ e.printStackTrace();
+ fail( "should not fail with exception! " + e );
+
+ }
+
+ }
+
+ public void testMissingSuper() {
+ Configuration cfg = new Configuration();
+
+ try {
+ cfg.addResource( getBaseForMappings() + "extendshbm/Customer.hbm.xml" );
+ assertNull(
+ "cannot be in the configuration yet!",
+ cfg.getClassMapping( "org.hibernate.test.extendshbm.Customer" )
+ );
+ cfg.addResource( getBaseForMappings() + "extendshbm/Employee.hbm.xml" );
+
+ cfg.buildSessionFactory();
+
+ fail( "Should not be able to build sessionfactory without a Person" );
+ }
+ catch ( HibernateException e ) {
+
+ }
+
+ }
+
+ public void testAllSeparateInOne() {
+ Configuration cfg = new Configuration();
+
+ try {
+ cfg.addResource( getBaseForMappings() + "extendshbm/allseparateinone.hbm.xml" );
+
+ cfg.buildSessionFactory();
+
+ assertNotNull( cfg.getClassMapping( "org.hibernate.test.extendshbm.Customer" ) );
+ assertNotNull( cfg.getClassMapping( "org.hibernate.test.extendshbm.Person" ) );
+ assertNotNull( cfg.getClassMapping( "org.hibernate.test.extendshbm.Employee" ) );
+
+ }
+ catch ( HibernateException e ) {
+ fail( "should not fail with exception! " + e );
+ }
+
+ }
+
+ public void testJoinedSubclassAndEntityNamesOnly() {
+ Configuration cfg = new Configuration();
+
+ try {
+ cfg.addResource( getBaseForMappings() + "extendshbm/entitynames.hbm.xml" );
+
+ cfg.buildMappings();
+
+ assertNotNull( cfg.getClassMapping( "EntityHasName" ) );
+ assertNotNull( cfg.getClassMapping( "EntityCompany" ) );
+
+ }
+ catch ( HibernateException e ) {
+ e.printStackTrace();
+ fail( "should not fail with exception! " + e );
+
+ }
+ }
+
+ public void testEntityNamesWithPackageFailureExpected() {
+ Configuration cfg = new Configuration();
+ try {
+ cfg.addResource( getBaseForMappings() + "extendshbm/packageentitynames.hbm.xml" );
+
+ cfg.buildMappings();
+
+ assertNotNull( cfg.getClassMapping( "EntityHasName" ) );
+ assertNotNull( cfg.getClassMapping( "EntityCompany" ) );
+
+ }
+ catch ( HibernateException e ) {
+ e.printStackTrace();
+ fail( "should not fail with exception! " + e );
+
+ }
+ }
+
+
+ public void testUnionSubclass() {
+ Configuration cfg = new Configuration();
+
+ try {
+ cfg.addResource( getBaseForMappings() + "extendshbm/unionsubclass.hbm.xml" );
+
+ cfg.buildMappings();
+
+ assertNotNull( cfg.getClassMapping( "org.hibernate.test.extendshbm.Person" ) );
+ assertNotNull( cfg.getClassMapping( "org.hibernate.test.extendshbm.Customer" ) );
+
+ }
+ catch ( HibernateException e ) {
+ e.printStackTrace();
+ fail( "should not fail with exception! " + e );
+
+ }
+ }
+
+}
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/Person.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/Person.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/Person.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+ This mapping demonstrates
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.extendshbm">
+ <class name="Person">
+
+ <id name="id"
+ column="person_id"
+ unsaved-value="0">
+ <generator class="native"/>
+ </id>
+
+ <discriminator
+ type="string"
+ />
+
+ <property name="name"
+ not-null="true"
+ length="80"/>
+
+ </class>
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/Person.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/Person.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/Person.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,50 @@
+//$Id: Person.java 5011 2004-12-19 22:01:25Z maxcsaucdk $
+package org.hibernate.test.extendshbm;
+
+
+/**
+ * @author Gavin King
+ */
+public class Person {
+ private long id;
+ private String name;
+ private char sex;
+
+ /**
+ * @return Returns the sex.
+ */
+ public char getSex() {
+ return sex;
+ }
+ /**
+ * @param sex The sex to set.
+ */
+ public void setSex(char sex) {
+ this.sex = sex;
+ }
+ /**
+ * @return Returns the id.
+ */
+ public long getId() {
+ return id;
+ }
+ /**
+ * @param id The id to set.
+ */
+ public void setId(long id) {
+ this.id = id;
+ }
+ /**
+ * @return Returns the identity.
+ */
+ public String getName() {
+ return name;
+ }
+ /**
+ * @param identity The identity to set.
+ */
+ public void setName(String identity) {
+ this.name = identity;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/allinone.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/allinone.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/allinone.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,46 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+ This mapping demonstrates
+
+-->
+
+<hibernate-mapping
+ package="org.hibernate.test.extendshbm">
+
+ <class name="Person">
+
+ <id name="id"
+ column="person_id"
+ unsaved-value="0">
+ <generator class="native"/>
+ </id>
+
+ <discriminator
+ type="string"/>
+
+ <property name="name"
+ not-null="true"
+ length="80"/>
+
+ <subclass name="Employee">
+ <property name="title"
+ length="20"/>
+ <property name="salary"
+ length="0"/>
+ <many-to-one name="manager"/>
+ </subclass>
+
+ <subclass name="Customer">
+ <property name="comments"/>
+ <many-to-one name="salesperson"/>
+ </subclass>
+
+ </class>
+
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/allseparateinone.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/allseparateinone.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/allseparateinone.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,49 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+ This mapping demonstrates
+
+-->
+
+<hibernate-mapping
+ package="org.hibernate.test.extendshbm">
+
+ <class name="Person">
+
+ <id name="id"
+ column="person_id"
+ unsaved-value="0">
+ <generator class="native"/>
+ </id>
+
+ <discriminator
+ type="string"/>
+
+ <property name="name"
+ not-null="true"
+ length="80"/>
+
+ <subclass name="Employee">
+ <property name="title"
+ length="20"/>
+ <property name="salary"
+ length="0"/>
+ <many-to-one name="manager"/>
+ </subclass>
+
+
+
+ </class>
+
+
+ <subclass name="Customer" extends="Person">
+ <property name="comments"/>
+ <many-to-one name="salesperson"/>
+ </subclass>
+
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/entitynames.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/entitynames.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/entitynames.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,18 @@
+<!DOCTYPE hibernate-mapping
+ PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping>
+ <class lazy="true" entity-name="EntityHasName" table="F5_ENTITY_HASNAME">
+ <id name="id" column="ID" type="long">
+ <generator class="native"/>
+ </id>
+ <property name="attrName" type="string">
+ <column name="NAME"/>
+ </property>
+ </class>
+ <joined-subclass lazy="true" entity-name="EntityCompany" table="F5_ENTITY_COMPANY" extends="EntityHasName">
+ <key column="REF_ID"/>
+ <many-to-one name="parent" entity-name="EntityHasName"/>
+ </joined-subclass>
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/packageentitynames.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/packageentitynames.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/packageentitynames.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,18 @@
+<!DOCTYPE hibernate-mapping
+ PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.extendshbm">
+ <class lazy="true" entity-name="EntityHasName" table="F5_ENTITY_HASNAME">
+ <id name="id" column="ID" type="long">
+ <generator class="native"/>
+ </id>
+ <property name="attrName" type="string">
+ <column name="NAME"/>
+ </property>
+ </class>
+ <joined-subclass lazy="true" entity-name="EntityCompany" table="F5_ENTITY_COMPANY" extends="EntityHasName">
+ <key column="REF_ID"/>
+ <many-to-one name="parent" entity-name="EntityHasName"/>
+ </joined-subclass>
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/unionsubclass.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/unionsubclass.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extendshbm/unionsubclass.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,46 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+ This mapping demonstrates
+
+-->
+
+<hibernate-mapping
+ package="org.hibernate.test.extendshbm">
+
+ <class name="Person">
+
+ <id name="id"
+ column="person_id"
+ unsaved-value="0">
+ <generator class="native"/>
+ </id>
+
+ <discriminator
+ type="string"/>
+
+ <property name="name"
+ not-null="true"
+ length="80"/>
+
+ </class>
+
+
+ <union-subclass name="Customer" extends="Person">
+ <property name="comments"/>
+ <many-to-one name="salesperson"/>
+ </union-subclass>
+
+ <subclass name="Employee" extends="Person">
+ <property name="title"
+ length="20"/>
+ <property name="salary"
+ length="0"/>
+ <many-to-one name="manager"/>
+ </subclass>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extralazy/Document.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extralazy/Document.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extralazy/Document.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,38 @@
+//$Id: Document.java 7635 2005-07-24 23:04:30Z oneovthafew $
+package org.hibernate.test.extralazy;
+
+public class Document {
+
+ private String title;
+ private String content;
+ private User owner;
+
+ Document() {}
+
+ public Document(String title, String content, User owner) {
+ this.content = content;
+ this.owner = owner;
+ this.title = title;
+ owner.getDocuments().add(this);
+ }
+
+ public String getContent() {
+ return content;
+ }
+ public void setContent(String content) {
+ this.content = content;
+ }
+ public User getOwner() {
+ return owner;
+ }
+ public void setOwner(User owner) {
+ this.owner = owner;
+ }
+ public String getTitle() {
+ return title;
+ }
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extralazy/Document.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extralazy/ExtraLazyTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extralazy/ExtraLazyTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extralazy/ExtraLazyTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,221 @@
+//$Id: ExtraLazyTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.extralazy;
+
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class ExtraLazyTest extends FunctionalTestCase {
+
+ public ExtraLazyTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "extralazy/UserGroup.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( ExtraLazyTest.class );
+ }
+
+ public void testOrphanDelete() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ User gavin = new User("gavin", "secret");
+ Document hia = new Document("HiA", "blah blah blah", gavin);
+ Document hia2 = new Document("HiA2", "blah blah blah blah", gavin);
+ s.persist(gavin);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ gavin = (User) s.get(User.class, "gavin");
+ assertEquals( 2, gavin.getDocuments().size() );
+ gavin.getDocuments().remove(hia2);
+ assertFalse( gavin.getDocuments().contains(hia2) );
+ assertTrue( gavin.getDocuments().contains(hia) );
+ assertEquals( 1, gavin.getDocuments().size() );
+ assertFalse( Hibernate.isInitialized( gavin.getDocuments() ) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ gavin = (User) s.get(User.class, "gavin");
+ assertEquals( 1, gavin.getDocuments().size() );
+ assertFalse( gavin.getDocuments().contains(hia2) );
+ assertTrue( gavin.getDocuments().contains(hia) );
+ assertFalse( Hibernate.isInitialized( gavin.getDocuments() ) );
+ assertNull( s.get(Document.class, "HiA2") );
+ gavin.getDocuments().clear();
+ assertTrue( Hibernate.isInitialized( gavin.getDocuments() ) );
+ s.delete(gavin);
+ t.commit();
+ s.close();
+ }
+
+ public void testGet() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ User gavin = new User("gavin", "secret");
+ User turin = new User("turin", "tiger");
+ Group g = new Group("developers");
+ g.getUsers().put("gavin", gavin);
+ g.getUsers().put("turin", turin);
+ s.persist(g);
+ gavin.getSession().put( "foo", new SessionAttribute("foo", "foo bar baz") );
+ gavin.getSession().put( "bar", new SessionAttribute("bar", "foo bar baz 2") );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ g = (Group) s.get(Group.class, "developers");
+ gavin = (User) g.getUsers().get("gavin");
+ turin = (User) g.getUsers().get("turin");
+ assertNotNull(gavin);
+ assertNotNull(turin);
+ assertNull( g.getUsers().get("emmanuel") );
+ assertFalse( Hibernate.isInitialized( g.getUsers() ) );
+ assertNotNull( gavin.getSession().get("foo") );
+ assertNull( turin.getSession().get("foo") );
+ assertFalse( Hibernate.isInitialized( gavin.getSession() ) );
+ assertFalse( Hibernate.isInitialized( turin.getSession() ) );
+ s.delete(gavin);
+ s.delete(turin);
+ s.delete(g);
+ t.commit();
+ s.close();
+ }
+
+ public void testRemoveClear() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ User gavin = new User("gavin", "secret");
+ User turin = new User("turin", "tiger");
+ Group g = new Group("developers");
+ g.getUsers().put("gavin", gavin);
+ g.getUsers().put("turin", turin);
+ s.persist(g);
+ gavin.getSession().put( "foo", new SessionAttribute("foo", "foo bar baz") );
+ gavin.getSession().put( "bar", new SessionAttribute("bar", "foo bar baz 2") );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ g = (Group) s.get(Group.class, "developers");
+ gavin = (User) g.getUsers().get("gavin");
+ turin = (User) g.getUsers().get("turin");
+ assertFalse( Hibernate.isInitialized( g.getUsers() ) );
+ g.getUsers().clear();
+ gavin.getSession().remove("foo");
+ assertTrue( Hibernate.isInitialized( g.getUsers() ) );
+ assertTrue( Hibernate.isInitialized( gavin.getSession() ) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ g = (Group) s.get(Group.class, "developers");
+ assertTrue( g.getUsers().isEmpty() );
+ assertFalse( Hibernate.isInitialized( g.getUsers() ) );
+ gavin = (User) s.get(User.class, "gavin");
+ assertFalse( gavin.getSession().containsKey("foo") );
+ assertFalse( Hibernate.isInitialized( gavin.getSession() ) );
+ s.delete(gavin);
+ s.delete(turin);
+ s.delete(g);
+ t.commit();
+ s.close();
+ }
+
+ public void testIndexFormulaMap() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ User gavin = new User("gavin", "secret");
+ User turin = new User("turin", "tiger");
+ Group g = new Group("developers");
+ g.getUsers().put("gavin", gavin);
+ g.getUsers().put("turin", turin);
+ s.persist(g);
+ gavin.getSession().put( "foo", new SessionAttribute("foo", "foo bar baz") );
+ gavin.getSession().put( "bar", new SessionAttribute("bar", "foo bar baz 2") );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ g = (Group) s.get(Group.class, "developers");
+ assertEquals( g.getUsers().size(), 2 );
+ g.getUsers().remove("turin");
+ Map smap = ( (User) g.getUsers().get("gavin") ).getSession();
+ assertEquals(smap.size(), 2);
+ smap.remove("bar");
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ g = (Group) s.get(Group.class, "developers");
+ assertEquals( g.getUsers().size(), 1 );
+ smap = ( (User) g.getUsers().get("gavin") ).getSession();
+ assertEquals(smap.size(), 1);
+ gavin = (User) g.getUsers().put("gavin", turin);
+ s.delete(gavin);
+ assertEquals( s.createQuery("select count(*) from SessionAttribute").uniqueResult(), new Long(0) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ g = (Group) s.get(Group.class, "developers");
+ assertEquals( g.getUsers().size(), 1 );
+ turin = (User) g.getUsers().get("turin");
+ smap = turin.getSession();
+ assertEquals(smap.size(), 0);
+ assertEquals( s.createQuery("select count(*) from User").uniqueResult(), new Long(1) );
+ s.delete(g);
+ s.delete(turin);
+ assertEquals( s.createQuery("select count(*) from User").uniqueResult(), new Long(0) );
+ t.commit();
+ s.close();
+ }
+
+ public void testSQLQuery() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ User gavin = new User("gavin", "secret");
+ User turin = new User("turin", "tiger");
+ gavin.getSession().put( "foo", new SessionAttribute("foo", "foo bar baz") );
+ gavin.getSession().put( "bar", new SessionAttribute("bar", "foo bar baz 2") );
+ s.persist(gavin);
+ s.persist(turin);
+ s.flush();
+ s.clear();
+ List results = s.getNamedQuery("userSessionData").setParameter("uname", "%in").list();
+ assertEquals( results.size(), 2 );
+ gavin = (User) ( (Object[]) results.get(0) )[0];
+ assertEquals( gavin.getName(), "gavin" );
+ assertEquals( gavin.getSession().size(), 2 );
+ s.createQuery("delete SessionAttribute").executeUpdate();
+ s.createQuery("delete User").executeUpdate();
+ t.commit();
+ s.close();
+
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extralazy/ExtraLazyTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extralazy/Group.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extralazy/Group.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extralazy/Group.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,29 @@
+//$Id: Group.java 7628 2005-07-24 06:55:01Z oneovthafew $
+package org.hibernate.test.extralazy;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Gavin King
+ */
+public class Group {
+ private String name;
+ private Map users = new HashMap();
+ Group() {}
+ public Group(String n) {
+ name = n;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public Map getUsers() {
+ return users;
+ }
+ public void setUsers(Map users) {
+ this.users = users;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extralazy/Group.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extralazy/SessionAttribute.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extralazy/SessionAttribute.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extralazy/SessionAttribute.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,41 @@
+//$Id: SessionAttribute.java 7628 2005-07-24 06:55:01Z oneovthafew $
+package org.hibernate.test.extralazy;
+
+import java.io.Serializable;
+
+/**
+ * @author Gavin King
+ */
+public class SessionAttribute {
+ private Long id;
+ private String name;
+ private String stringData;
+ private Serializable objectData;
+ SessionAttribute() {}
+ public SessionAttribute(String name, Serializable obj) {
+ this.name = name;
+ this.objectData = obj;
+ }
+ public SessionAttribute(String name, String str) {
+ this.name = name;
+ this.stringData = str;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public Serializable getObjectData() {
+ return objectData;
+ }
+ public void setObjectData(Serializable objectData) {
+ this.objectData = objectData;
+ }
+ public String getStringData() {
+ return stringData;
+ }
+ public void setStringData(String stringData) {
+ this.stringData = stringData;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extralazy/SessionAttribute.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extralazy/User.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extralazy/User.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extralazy/User.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,46 @@
+//$Id: User.java 7635 2005-07-24 23:04:30Z oneovthafew $
+package org.hibernate.test.extralazy;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author Gavin King
+ */
+public class User {
+ private String name;
+ private String password;
+ private Map session = new HashMap();
+ private Set documents = new HashSet();
+ User() {}
+ public User(String n, String pw) {
+ name=n;
+ password = pw;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public String getPassword() {
+ return password;
+ }
+ public void setPassword(String password) {
+ this.password = password;
+ }
+ public Map getSession() {
+ return session;
+ }
+ public void setSession(Map session) {
+ this.session = session;
+ }
+ public Set getDocuments() {
+ return documents;
+ }
+ public void setDocuments(Set documents) {
+ this.documents = documents;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extralazy/User.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extralazy/UserGroup.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extralazy/UserGroup.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extralazy/UserGroup.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,67 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.extralazy">
+
+ <class name="Group" table="groups">
+ <id name="name"/>
+ <map name="users" cascade="persist"
+ table="group_user" lazy="extra">
+ <key column="groupName"/>
+ <map-key formula="lower(personName)" type="string"/>
+ <many-to-many column="personName" class="User"/>
+ </map>
+ </class>
+
+ <class name="User" table="users">
+ <id name="name"/>
+ <property name="password"/>
+ <map name="session" lazy="extra"
+ cascade="persist,save-update,delete,delete-orphan">
+ <key column="userName" not-null="true"/>
+ <map-key column="name" type="string"/>
+ <one-to-many class="SessionAttribute"/>
+ </map>
+ <set name="documents" inverse="true"
+ lazy="extra" cascade="all,delete-orphan">
+ <key column="owner"/>
+ <one-to-many class="Document"/>
+ </set>
+ </class>
+
+ <class name="Document" table="documents">
+ <id name="title"/>
+ <property name="content" type="text"/>
+ <many-to-one name="owner" not-null="true"/>
+ </class>
+
+ <class name="SessionAttribute" table="session_attributes">
+ <id name="id" access="field">
+ <generator class="native"/>
+ </id>
+ <property name="name" not-null="true"
+ insert="false" update="false"/>
+ <property name="stringData"/>
+ <property name="objectData"/>
+ </class>
+
+ <sql-query name="userSessionData">
+ <return alias="u" class="User"/>
+ <return-join alias="s" property="u.session"/>
+ select
+ lower(u.name) as {u.name}, lower(u.password) as {u.password},
+ lower(s.userName) as {s.key}, lower(s.name) as {s.index}, s.id as {s.element},
+ {s.element.*}
+ from users u
+ join session_attributes s on lower(s.userName) = lower(u.name)
+ where u.name like :uname
+ </sql-query>
+
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/extralazy/UserGroup.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Category.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Category.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Category.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping
+ SYSTEM
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
+
+<hibernate-mapping package="org.hibernate.test.filter">
+
+ <class name="Category" table="CATEGORY">
+ <id name="id" column="CAT_ID" >
+ <generator class="native"/>
+ </id>
+
+ <property name="name" type="string"/>
+ <property name="effectiveStartDate" column="eff_start_dt" type="java.util.Date"/>
+ <property name="effectiveEndDate" column="eff_end_dt" type="java.util.Date"/>
+
+ <set cascade="none" inverse="true" name="products" table="PROD_CAT">
+ <key column="CAT_ID"/>
+ <many-to-many column="PROD_ID" class="Product"/>
+ </set>
+
+ <filter name="effectiveDate" condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
+
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Category.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Category.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Category.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,104 @@
+// $Id: Category.java 6507 2005-04-25 16:57:32Z steveebersole $
+package org.hibernate.test.filter;
+
+import java.util.Date;
+import java.util.Set;
+
+/**
+ * Implementation of Category.
+ *
+ * @author Steve Ebersole
+ */
+public class Category {
+ private Long id;
+ private String name;
+ private Date effectiveStartDate;
+ private Date effectiveEndDate;
+ private Set products;
+
+ public Category() {
+ }
+
+ public Category(String name) {
+ this.name = name;
+ }
+
+ public Category(String name, Date effectiveStartDate, Date effectiveEndDate) {
+ this.name = name;
+ this.effectiveStartDate = effectiveStartDate;
+ this.effectiveEndDate = effectiveEndDate;
+ }
+
+ 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 Date getEffectiveStartDate() {
+ return effectiveStartDate;
+ }
+
+ public void setEffectiveStartDate(Date effectiveStartDate) {
+ this.effectiveStartDate = effectiveStartDate;
+ }
+
+ public Date getEffectiveEndDate() {
+ return effectiveEndDate;
+ }
+
+ public void setEffectiveEndDate(Date effectiveEndDate) {
+ this.effectiveEndDate = effectiveEndDate;
+ }
+
+ public Set getProducts() {
+ return products;
+ }
+
+ public void setProducts(Set products) {
+ this.products = products;
+ }
+
+ public boolean equals(Object o) {
+ if ( this == o ) return true;
+ if ( !( o instanceof Category ) ) return false;
+
+ final Category category = ( Category ) o;
+
+ if ( !name.equals( category.name ) ) {
+ return false;
+ }
+
+ if ( effectiveEndDate != null ?
+ !effectiveEndDate.equals( category.effectiveEndDate ) :
+ category.effectiveEndDate != null ) {
+ return false;
+ }
+
+ if ( effectiveStartDate != null ?
+ !effectiveStartDate.equals( category.effectiveStartDate ) :
+ category.effectiveStartDate != null ) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public int hashCode() {
+ int result;
+ result = name.hashCode();
+ result = 29 * result + ( effectiveStartDate != null ? effectiveStartDate.hashCode() : 0 );
+ result = 29 * result + ( effectiveEndDate != null ? effectiveEndDate.hashCode() : 0 );
+ return result;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Department.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Department.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Department.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping
+ SYSTEM
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
+
+<hibernate-mapping package="org.hibernate.test.filter">
+
+ <class name="Department" table="DEPARTMENT">
+ <id name="id" column="DEPT_ID" type="long">
+ <generator class="native"/>
+ </id>
+
+ <property name="name" column="REG" type="string"/>
+
+ <set name="salespersons" cascade="save-update" inverse="true">
+ <key column="DEPT_ID"/>
+ <one-to-many class="Salesperson"/>
+ <filter name="seniorSalespersons"><![CDATA[`hire dt` < :asOfDate]]></filter>
+ </set>
+
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Department.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Department.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Department.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,40 @@
+// $Id: Department.java 4448 2004-08-28 02:29:05Z steveebersole $
+package org.hibernate.test.filter;
+
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * Implementation of Department.
+ *
+ * @author Steve
+ */
+public class Department {
+ private Long id;
+ private String name;
+ private Set salespersons = new HashSet();
+
+ 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 Set getSalespersons() {
+ return salespersons;
+ }
+
+ public void setSalespersons(Set salespersons) {
+ this.salespersons = salespersons;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/DynamicFilterTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/DynamicFilterTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/DynamicFilterTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,583 @@
+// $Id: DynamicFilterTest.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.test.filter;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import junit.framework.Test;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.EntityMode;
+import org.hibernate.FetchMode;
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.cache.CacheKey;
+import org.hibernate.cache.entry.CollectionCacheEntry;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.criterion.Expression;
+import org.hibernate.impl.SessionFactoryImpl;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.transform.DistinctRootEntityResultTransformer;
+
+/**
+ * Implementation of DynamicFilterTest.
+ *
+ * @author Steve
+ */
+public class DynamicFilterTest extends FunctionalTestCase {
+
+ private Log log = LogFactory.getLog( DynamicFilterTest.class );
+
+ public DynamicFilterTest(String testName) {
+ super( testName );
+ }
+
+ public String[] getMappings() {
+ return new String[]{
+ "filter/defs.hbm.xml",
+ "filter/LineItem.hbm.xml",
+ "filter/Order.hbm.xml",
+ "filter/Product.hbm.xml",
+ "filter/Salesperson.hbm.xml",
+ "filter/Department.hbm.xml",
+ "filter/Category.hbm.xml"
+ };
+ }
+
+ public void configure(Configuration cfg) {
+ cfg.setProperty( Environment.MAX_FETCH_DEPTH, "1" );
+ cfg.setProperty( Environment.GENERATE_STATISTICS, "true" );
+ cfg.setProperty( Environment.USE_QUERY_CACHE, "true" );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( DynamicFilterTest.class );
+ }
+
+ public void testSecondLevelCachedCollectionsFiltering() {
+ TestData testData = new TestData();
+ testData.prepare();
+
+ Session session = openSession();
+ long ts = ( ( SessionImplementor ) session ).getTimestamp();
+
+ // Force a collection into the second level cache, with its non-filtered elements
+ Salesperson sp = ( Salesperson ) session.load( Salesperson.class, testData.steveId );
+ Hibernate.initialize( sp.getOrders() );
+ CollectionPersister persister = ( ( SessionFactoryImpl ) getSessions() )
+ .getCollectionPersister( Salesperson.class.getName() + ".orders" );
+ assertTrue( "No cache for collection", persister.hasCache() );
+ CollectionCacheEntry cachedData = ( CollectionCacheEntry ) persister.getCacheAccessStrategy()
+ .get( new CacheKey( testData.steveId, persister.getKeyType(), persister.getRole(), EntityMode.POJO, sfi() ), ts );
+ assertNotNull( "collection was not in cache", cachedData );
+
+ session.close();
+
+ session = openSession();
+ ts = ( ( SessionImplementor ) session ).getTimestamp();
+ session.enableFilter( "fulfilledOrders" ).setParameter( "asOfDate", testData.lastMonth.getTime() );
+ sp = ( Salesperson ) session.createQuery( "from Salesperson as s where s.id = :id" )
+ .setLong( "id", testData.steveId.longValue() )
+ .uniqueResult();
+ assertEquals( "Filtered-collection not bypassing 2L-cache", 1, sp.getOrders().size() );
+
+ CollectionCacheEntry cachedData2 = ( CollectionCacheEntry ) persister.getCacheAccessStrategy()
+ .get( new CacheKey( testData.steveId, persister.getKeyType(), persister.getRole(), EntityMode.POJO, sfi() ), ts );
+ assertNotNull( "collection no longer in cache!", cachedData2 );
+ assertSame( "Different cache values!", cachedData, cachedData2 );
+
+ session.close();
+
+ session = openSession();
+ session.enableFilter( "fulfilledOrders" ).setParameter( "asOfDate", testData.lastMonth.getTime() );
+ sp = ( Salesperson ) session.load( Salesperson.class, testData.steveId );
+ assertEquals( "Filtered-collection not bypassing 2L-cache", 1, sp.getOrders().size() );
+
+ session.close();
+
+ // Finally, make sure that the original cached version did not get over-written
+ session = openSession();
+ sp = ( Salesperson ) session.load( Salesperson.class, testData.steveId );
+ assertEquals( "Actual cached version got over-written", 2, sp.getOrders().size() );
+
+ session.close();
+ testData.release();
+ }
+
+ public void testCombinedClassAndCollectionFiltersEnabled() {
+ TestData testData = new TestData();
+ testData.prepare();
+
+ Session session = openSession();
+ session.enableFilter( "regionlist" ).setParameterList( "regions", new String[]{"LA", "APAC"} );
+ session.enableFilter( "fulfilledOrders" ).setParameter( "asOfDate", testData.lastMonth.getTime() );
+
+ // test retreival through hql with the collection as non-eager
+ List salespersons = session.createQuery( "select s from Salesperson as s" ).list();
+ assertEquals( "Incorrect salesperson count", 1, salespersons.size() );
+ Salesperson sp = ( Salesperson ) salespersons.get( 0 );
+ assertEquals( "Incorrect order count", 1, sp.getOrders().size() );
+
+ session.clear();
+
+ // test retreival through hql with the collection join fetched
+ salespersons = session.createQuery( "select s from Salesperson as s left join fetch s.orders" ).list();
+ assertEquals( "Incorrect salesperson count", 1, salespersons.size() );
+ sp = ( Salesperson ) salespersons.get( 0 );
+ assertEquals( "Incorrect order count", 1, sp.getOrders().size() );
+
+ session.close();
+ testData.release();
+ }
+
+ public void testHqlFilters() {
+ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ // HQL test
+ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ log.info( "Starting HQL filter tests" );
+ TestData testData = new TestData();
+ testData.prepare();
+
+ Session session = openSession();
+ session.enableFilter( "region" ).setParameter( "region", "APAC" );
+
+ session.enableFilter( "effectiveDate" )
+ .setParameter( "asOfDate", testData.lastMonth.getTime() );
+
+ log.info( "HQL against Salesperson..." );
+ List results = session.createQuery( "select s from Salesperson as s left join fetch s.orders" ).list();
+ assertTrue( "Incorrect filtered HQL result count [" + results.size() + "]", results.size() == 1 );
+ Salesperson result = ( Salesperson ) results.get( 0 );
+ assertTrue( "Incorrect collectionfilter count", result.getOrders().size() == 1 );
+
+ log.info( "HQL against Product..." );
+ results = session.createQuery( "from Product as p where p.stockNumber = ?" ).setInteger( 0, 124 ).list();
+ assertTrue( results.size() == 1 );
+
+ session.close();
+ testData.release();
+ }
+
+ public void testCriteriaQueryFilters() {
+ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ // Criteria-query test
+ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ log.info( "Starting Criteria-query filter tests" );
+ TestData testData = new TestData();
+ testData.prepare();
+
+ Session session = openSession();
+ session.enableFilter( "region" ).setParameter( "region", "APAC" );
+
+ session.enableFilter( "fulfilledOrders" )
+ .setParameter( "asOfDate", testData.lastMonth.getTime() );
+
+ session.enableFilter( "effectiveDate" )
+ .setParameter( "asOfDate", testData.lastMonth.getTime() );
+
+ log.info( "Criteria query against Salesperson..." );
+ List salespersons = session.createCriteria( Salesperson.class )
+ .setFetchMode( "orders", FetchMode.JOIN )
+ .list();
+ assertEquals( "Incorrect salesperson count", 1, salespersons.size() );
+ assertEquals( "Incorrect order count", 1, ( ( Salesperson ) salespersons.get( 0 ) ).getOrders().size() );
+
+ log.info( "Criteria query against Product..." );
+ List products = session.createCriteria( Product.class )
+ .add( Expression.eq( "stockNumber", new Integer( 124 ) ) )
+ .list();
+ assertEquals( "Incorrect product count", 1, products.size() );
+
+ session.close();
+ testData.release();
+ }
+
+ public void testGetFilters() {
+ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ // Get() test
+ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ log.info( "Starting get() filter tests (eager assoc. fetching)." );
+ TestData testData = new TestData();
+ testData.prepare();
+
+ Session session = openSession();
+ session.enableFilter( "region" ).setParameter( "region", "APAC" );
+
+ log.info( "Performing get()..." );
+ Salesperson salesperson = ( Salesperson ) session.get( Salesperson.class, testData.steveId );
+ assertNotNull( salesperson );
+ assertEquals( "Incorrect order count", 1, salesperson.getOrders().size() );
+
+ session.close();
+ testData.release();
+ }
+
+ public void testOneToManyFilters() {
+ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ // one-to-many loading tests
+ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ log.info( "Starting one-to-many collection loader filter tests." );
+ TestData testData = new TestData();
+ testData.prepare();
+
+ Session session = openSession();
+ session.enableFilter( "seniorSalespersons" )
+ .setParameter( "asOfDate", testData.lastMonth.getTime() );
+
+ log.info( "Performing load of Department..." );
+ Department department = ( Department ) session.load( Department.class, testData.deptId );
+ Set salespersons = department.getSalespersons();
+ assertEquals( "Incorrect salesperson count", 1, salespersons.size() );
+
+ session.close();
+ testData.release();
+ }
+
+ public void testInStyleFilterParameter() {
+ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ // one-to-many loading tests
+ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ log.info( "Starting one-to-many collection loader filter tests." );
+ TestData testData = new TestData();
+ testData.prepare();
+
+ Session session = openSession();
+ session.enableFilter( "regionlist" )
+ .setParameterList( "regions", new String[]{"LA", "APAC"} );
+
+ log.debug( "Performing query of Salespersons" );
+ List salespersons = session.createQuery( "from Salesperson" ).list();
+ assertEquals( "Incorrect salesperson count", 1, salespersons.size() );
+
+ session.close();
+ testData.release();
+ }
+
+ public void testManyToManyFilterOnCriteria() {
+ TestData testData = new TestData();
+ testData.prepare();
+
+ Session session = openSession();
+ session.enableFilter( "effectiveDate" ).setParameter( "asOfDate", new Date() );
+
+ Product prod = ( Product ) session.createCriteria( Product.class )
+ .setResultTransformer( new DistinctRootEntityResultTransformer() )
+ .add( Expression.eq( "id", testData.prod1Id ) )
+ .uniqueResult();
+
+ assertNotNull( prod );
+ assertEquals( "Incorrect Product.categories count for filter", 1, prod.getCategories().size() );
+
+ session.close();
+ testData.release();
+ }
+
+ public void testManyToManyFilterOnLoad() {
+ TestData testData = new TestData();
+ testData.prepare();
+
+ Session session = openSession();
+ session.enableFilter( "effectiveDate" ).setParameter( "asOfDate", new Date() );
+
+ Product prod = ( Product ) session.get( Product.class, testData.prod1Id );
+
+ long initLoadCount = getSessions().getStatistics().getCollectionLoadCount();
+ long initFetchCount = getSessions().getStatistics().getCollectionFetchCount();
+
+ // should already have been initialized...
+ int size = prod.getCategories().size();
+ assertEquals( "Incorrect filtered collection count", 1, size );
+
+ long currLoadCount = getSessions().getStatistics().getCollectionLoadCount();
+ long currFetchCount = getSessions().getStatistics().getCollectionFetchCount();
+
+ assertTrue(
+ "load with join fetch of many-to-many did not trigger join fetch",
+ ( initLoadCount == currLoadCount ) && ( initFetchCount == currFetchCount )
+ );
+
+ // make sure we did not get back a collection of proxies
+ long initEntityLoadCount = getSessions().getStatistics().getEntityLoadCount();
+ Iterator itr = prod.getCategories().iterator();
+ while ( itr.hasNext() ) {
+ Category cat = ( Category ) itr.next();
+ System.out.println( " ===> " + cat.getName() );
+ }
+ long currEntityLoadCount = getSessions().getStatistics().getEntityLoadCount();
+
+ assertTrue(
+ "load with join fetch of many-to-many did not trigger *complete* join fetch",
+ ( initEntityLoadCount == currEntityLoadCount )
+ );
+
+ session.close();
+ testData.release();
+ }
+
+ public void testManyToManyOnCollectionLoadAfterHQL() {
+ TestData testData = new TestData();
+ testData.prepare();
+
+ Session session = openSession();
+ session.enableFilter( "effectiveDate" ).setParameter( "asOfDate", new Date() );
+
+ // Force the categories to not get initialized here
+ List result = session.createQuery( "from Product as p where p.id = :id" )
+ .setLong( "id", testData.prod1Id.longValue() )
+ .list();
+ assertTrue( "No products returned from HQL", !result.isEmpty() );
+
+ Product prod = ( Product ) result.get( 0 );
+ assertNotNull( prod );
+ assertEquals( "Incorrect Product.categories count for filter on collection load", 1, prod.getCategories().size() );
+
+ session.close();
+ testData.release();
+ }
+
+ public void testManyToManyFilterOnQuery() {
+ TestData testData = new TestData();
+ testData.prepare();
+
+ Session session = openSession();
+ session.enableFilter( "effectiveDate" ).setParameter( "asOfDate", new Date() );
+
+ List result = session.createQuery( "from Product p inner join fetch p.categories" ).list();
+ assertTrue( "No products returned from HQL many-to-many filter case", !result.isEmpty() );
+
+ Product prod = ( Product ) result.get( 0 );
+
+ assertNotNull( prod );
+ assertEquals( "Incorrect Product.categories count for filter with HQL", 1, prod.getCategories().size() );
+
+ session.close();
+ testData.release();
+ }
+
+ public void testManyToManyBase() {
+ TestData testData = new TestData();
+ testData.prepare();
+
+ Session session = openSession();
+
+ Product prod = ( Product ) session.get( Product.class, testData.prod1Id );
+
+ long initLoadCount = getSessions().getStatistics().getCollectionLoadCount();
+ long initFetchCount = getSessions().getStatistics().getCollectionFetchCount();
+
+ // should already have been initialized...
+ int size = prod.getCategories().size();
+ assertEquals( "Incorrect non-filtered collection count", 2, size );
+
+ long currLoadCount = getSessions().getStatistics().getCollectionLoadCount();
+ long currFetchCount = getSessions().getStatistics().getCollectionFetchCount();
+
+ assertTrue(
+ "load with join fetch of many-to-many did not trigger join fetch",
+ ( initLoadCount == currLoadCount ) && ( initFetchCount == currFetchCount )
+ );
+
+ // make sure we did not get back a collection of proxies
+ long initEntityLoadCount = getSessions().getStatistics().getEntityLoadCount();
+ Iterator itr = prod.getCategories().iterator();
+ while ( itr.hasNext() ) {
+ Category cat = ( Category ) itr.next();
+ System.out.println( " ===> " + cat.getName() );
+ }
+ long currEntityLoadCount = getSessions().getStatistics().getEntityLoadCount();
+
+ assertTrue(
+ "load with join fetch of many-to-many did not trigger *complete* join fetch",
+ ( initEntityLoadCount == currEntityLoadCount )
+ );
+
+ session.close();
+ testData.release();
+ }
+
+ public void testManyToManyBaseThruCriteria() {
+ TestData testData = new TestData();
+ testData.prepare();
+
+ Session session = openSession();
+
+ List result = session.createCriteria( Product.class )
+ .add( Expression.eq( "id", testData.prod1Id ) )
+ .list();
+
+ Product prod = ( Product ) result.get( 0 );
+
+ long initLoadCount = getSessions().getStatistics().getCollectionLoadCount();
+ long initFetchCount = getSessions().getStatistics().getCollectionFetchCount();
+
+ // should already have been initialized...
+ int size = prod.getCategories().size();
+ assertEquals( "Incorrect non-filtered collection count", 2, size );
+
+ long currLoadCount = getSessions().getStatistics().getCollectionLoadCount();
+ long currFetchCount = getSessions().getStatistics().getCollectionFetchCount();
+
+ assertTrue(
+ "load with join fetch of many-to-many did not trigger join fetch",
+ ( initLoadCount == currLoadCount ) && ( initFetchCount == currFetchCount )
+ );
+
+ // make sure we did not get back a collection of proxies
+ long initEntityLoadCount = getSessions().getStatistics().getEntityLoadCount();
+ Iterator itr = prod.getCategories().iterator();
+ while ( itr.hasNext() ) {
+ Category cat = ( Category ) itr.next();
+ System.out.println( " ===> " + cat.getName() );
+ }
+ long currEntityLoadCount = getSessions().getStatistics().getEntityLoadCount();
+
+ assertTrue(
+ "load with join fetch of many-to-many did not trigger *complete* join fetch",
+ ( initEntityLoadCount == currEntityLoadCount )
+ );
+
+ session.close();
+ testData.release();
+ }
+
+ private class TestData {
+ private Long steveId;
+ private Long deptId;
+ private Long prod1Id;
+ private Calendar lastMonth;
+ private Calendar nextMonth;
+ private Calendar sixMonthsAgo;
+ private Calendar fourMonthsAgo;
+
+ private List entitiesToCleanUp = new ArrayList();
+
+ private void prepare() {
+ Session session = openSession();
+ Transaction transaction = session.beginTransaction();
+
+ lastMonth = new GregorianCalendar();
+ lastMonth.add( Calendar.MONTH, -1 );
+
+ nextMonth = new GregorianCalendar();
+ nextMonth.add( Calendar.MONTH, 1 );
+
+ sixMonthsAgo = new GregorianCalendar();
+ sixMonthsAgo.add( Calendar.MONTH, -6 );
+
+ fourMonthsAgo = new GregorianCalendar();
+ fourMonthsAgo.add( Calendar.MONTH, -4 );
+
+ Department dept = new Department();
+ dept.setName( "Sales" );
+
+ session.save( dept );
+ deptId = dept.getId();
+ entitiesToCleanUp.add( dept );
+
+ Salesperson steve = new Salesperson();
+ steve.setName( "steve" );
+ steve.setRegion( "APAC" );
+ steve.setHireDate( sixMonthsAgo.getTime() );
+
+ steve.setDepartment( dept );
+ dept.getSalespersons().add( steve );
+
+ Salesperson max = new Salesperson();
+ max.setName( "max" );
+ max.setRegion( "EMEA" );
+ max.setHireDate( nextMonth.getTime() );
+
+ max.setDepartment( dept );
+ dept.getSalespersons().add( max );
+
+ session.save( steve );
+ session.save( max );
+ entitiesToCleanUp.add( steve );
+ entitiesToCleanUp.add( max );
+
+ steveId = steve.getId();
+
+ Category cat1 = new Category( "test cat 1", lastMonth.getTime(), nextMonth.getTime() );
+ Category cat2 = new Category( "test cat 2", sixMonthsAgo.getTime(), fourMonthsAgo.getTime() );
+
+ Product product1 = new Product();
+ product1.setName( "Acme Hair Gel" );
+ product1.setStockNumber( 123 );
+ product1.setEffectiveStartDate( lastMonth.getTime() );
+ product1.setEffectiveEndDate( nextMonth.getTime() );
+
+ product1.addCategory( cat1 );
+ product1.addCategory( cat2 );
+
+ session.save( product1 );
+ entitiesToCleanUp.add( product1 );
+ prod1Id = product1.getId();
+
+ Order order1 = new Order();
+ order1.setBuyer( "gavin" );
+ order1.setRegion( "APAC" );
+ order1.setPlacementDate( sixMonthsAgo.getTime() );
+ order1.setFulfillmentDate( fourMonthsAgo.getTime() );
+ order1.setSalesperson( steve );
+ order1.addLineItem( product1, 500 );
+
+ session.save( order1 );
+ entitiesToCleanUp.add( order1 );
+
+ Product product2 = new Product();
+ product2.setName( "Acme Super-Duper DTO Factory" );
+ product2.setStockNumber( 124 );
+ product2.setEffectiveStartDate( sixMonthsAgo.getTime() );
+ product2.setEffectiveEndDate( new Date() );
+
+ Category cat3 = new Category( "test cat 2", sixMonthsAgo.getTime(), new Date() );
+ product2.addCategory( cat3 );
+
+ session.save( product2 );
+ entitiesToCleanUp.add( product2 );
+
+ // An uncategorized product
+ Product product3 = new Product();
+ product3.setName( "Uncategorized product" );
+ session.save( product3 );
+ entitiesToCleanUp.add( product3 );
+
+ Order order2 = new Order();
+ order2.setBuyer( "christian" );
+ order2.setRegion( "EMEA" );
+ order2.setPlacementDate( lastMonth.getTime() );
+ order2.setSalesperson( steve );
+ order2.addLineItem( product2, -1 );
+
+ session.save( order2 );
+ entitiesToCleanUp.add( order2 );
+
+ transaction.commit();
+ session.close();
+ }
+
+ private void release() {
+ Session session = openSession();
+ Transaction transaction = session.beginTransaction();
+
+ Iterator itr = entitiesToCleanUp.iterator();
+ while ( itr.hasNext() ) {
+ session.delete( itr.next() );
+ }
+
+ transaction.commit();
+ session.close();
+ }
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/LineItem.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/LineItem.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/LineItem.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping
+ SYSTEM
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
+
+<hibernate-mapping package="org.hibernate.test.filter">
+
+ <class name="LineItem" table="ORDER_ITEM">
+ <id name="id" column="ITEM_ID" >
+ <generator class="native"/>
+ </id>
+
+ <many-to-one name="order" class="Order" column="ORDER_ID" cascade="none"/>
+ <property name="sequence" column="SEQ" type="int"/>
+ <many-to-one name="product" class="Product" column="PRODUCT_ID" cascade="none"/>
+ <property name="quantity" column="AMT" type="long"/>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/LineItem.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/LineItem.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/LineItem.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,67 @@
+// $Id: LineItem.java 4046 2004-07-20 04:07:40Z steveebersole $
+package org.hibernate.test.filter;
+
+/**
+ * Implementation of LineItem.
+ *
+ * @author Steve
+ */
+public class LineItem {
+ private Long id;
+ private Order order;
+ private int sequence;
+ private Product product;
+ private long quantity;
+
+ /*package*/ LineItem() {}
+
+ public static LineItem generate(Order order, int sequence, Product product, long quantity) {
+ LineItem item = new LineItem();
+ item.order = order;
+ item.sequence = sequence;
+ item.product = product;
+ item.quantity = quantity;
+ item.order.getLineItems().add(sequence, item);
+ return item;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public Order getOrder() {
+ return order;
+ }
+
+ public void setOrder(Order order) {
+ this.order = order;
+ }
+
+ public int getSequence() {
+ return sequence;
+ }
+
+ public void setSequence(int sequence) {
+ this.sequence = sequence;
+ }
+
+ public Product getProduct() {
+ return product;
+ }
+
+ public void setProduct(Product product) {
+ this.product = product;
+ }
+
+ public long getQuantity() {
+ return quantity;
+ }
+
+ public void setQuantity(long quantity) {
+ this.quantity = quantity;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Order.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Order.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Order.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping
+ SYSTEM
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
+
+<hibernate-mapping package="org.hibernate.test.filter">
+
+ <class name="Order" table="T_ORDER">
+ <id name="id" column="ORDER_ID" type="long">
+ <generator class="native"/>
+ </id>
+
+ <property name="region" column="REG" type="string"/>
+ <property name="buyer" column="BUYER" type="string"/>
+ <property name="placementDate" column="`ord dt`" type="java.util.Date"/>
+ <property name="fulfillmentDate" column="`close dt`" type="java.util.Date"/>
+
+ <many-to-one name="salesperson" class="Salesperson" column="SALES_PERSON_ID"/>
+
+ <list cascade="all-delete-orphan" inverse="true" name="lineItems">
+ <key column="ORDER_ID"/>
+ <index column="SEQ"/>
+ <one-to-many class="LineItem"/>
+ </list>
+
+ <filter name="region" condition="REG = :region"/>
+ <filter name="fulfilledOrders"><![CDATA[`close dt` < :asOfDate]]></filter>
+
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Order.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Order.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Order.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,87 @@
+// $Id: Order.java 4222 2004-08-10 05:19:46Z steveebersole $
+package org.hibernate.test.filter;
+
+import java.util.Date;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * @author Steve Ebersole
+ */
+public class Order {
+ private Long id;
+ private String region;
+ private Date placementDate;
+ private Date fulfillmentDate;
+ private Salesperson salesperson;
+ private String buyer;
+ private List lineItems = new ArrayList();
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getRegion() {
+ return region;
+ }
+
+ public void setRegion(String region) {
+ this.region = region;
+ }
+
+ public Date getPlacementDate() {
+ return placementDate;
+ }
+
+ public void setPlacementDate(Date placementDate) {
+ this.placementDate = placementDate;
+ }
+
+ public Date getFulfillmentDate() {
+ return fulfillmentDate;
+ }
+
+ public void setFulfillmentDate(Date fulfillmentDate) {
+ this.fulfillmentDate = fulfillmentDate;
+ }
+
+ public Salesperson getSalesperson() {
+ return salesperson;
+ }
+
+ public void setSalesperson(Salesperson salesperson) {
+ this.salesperson = salesperson;
+ }
+
+ public String getBuyer() {
+ return buyer;
+ }
+
+ public void setBuyer(String buyer) {
+ this.buyer = buyer;
+ }
+
+ public List getLineItems() {
+ return lineItems;
+ }
+
+ protected void setLineItems(List lineItems) {
+ this.lineItems = lineItems;
+ }
+
+ public LineItem addLineItem(Product product, long quantity) {
+ return LineItem.generate(this, getLineItems().size(), product, quantity);
+ }
+
+ public void removeLineItem(LineItem item) {
+ removeLineItem( item.getSequence() );
+ }
+
+ public void removeLineItem(int sequence) {
+ getLineItems().remove(sequence);
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Product.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Product.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Product.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,36 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping
+ SYSTEM
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
+
+<hibernate-mapping package="org.hibernate.test.filter">
+
+ <class name="Product" table="PRODUCT">
+ <id name="id" column="PROD_ID" >
+ <generator class="native"/>
+ </id>
+
+ <property name="name" type="string"/>
+ <property name="stockNumber" column="STOCK_NUM" type="int"/>
+
+ <property name="effectiveStartDate" column="eff_start_dt" type="java.util.Date"/>
+ <property name="effectiveEndDate" column="eff_end_dt" type="java.util.Date"/>
+
+ <set cascade="none" inverse="true" name="orderLineItems">
+ <key column="PROD_ID"/>
+ <one-to-many class="LineItem"/>
+ </set>
+
+ <set cascade="all" inverse="false" name="categories" fetch="join" table="PROD_CAT" >
+ <key column="PROD_ID"/>
+ <many-to-many class="Category" column="CAT_ID" fetch="join" >
+ <filter name="effectiveDate" condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
+ <filter name="cat" condition="CAT_ID = :catId"/>
+ </many-to-many>
+ </set>
+
+ <filter name="effectiveDate" condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
+
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Product.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Product.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Product.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,99 @@
+// $Id: Product.java 6507 2005-04-25 16:57:32Z steveebersole $
+package org.hibernate.test.filter;
+
+import java.util.Set;
+import java.util.Date;
+import java.util.HashSet;
+
+/**
+ * @author Steve Ebersole
+ */
+public class Product {
+ private Long id;
+ private String name;
+ private int stockNumber; // int for ease of hashCode() impl
+ private Date effectiveStartDate;
+ private Date effectiveEndDate;
+ private Set orderLineItems;
+ private Set categories;
+
+ 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 Set getOrderLineItems() {
+ return orderLineItems;
+ }
+
+ public void setOrderLineItems(Set orderLineItems) {
+ this.orderLineItems = orderLineItems;
+ }
+
+ public int getStockNumber() {
+ return stockNumber;
+ }
+
+ public void setStockNumber(int stockNumber) {
+ this.stockNumber = stockNumber;
+ }
+
+ public int hashCode() {
+ return stockNumber;
+ }
+
+ public boolean equals(Object obj) {
+ return ( (Product) obj ).stockNumber == this.stockNumber;
+ }
+
+ public Date getEffectiveStartDate() {
+ return effectiveStartDate;
+ }
+
+ public void setEffectiveStartDate(Date effectiveStartDate) {
+ this.effectiveStartDate = effectiveStartDate;
+ }
+
+ public Date getEffectiveEndDate() {
+ return effectiveEndDate;
+ }
+
+ public void setEffectiveEndDate(Date effectiveEndDate) {
+ this.effectiveEndDate = effectiveEndDate;
+ }
+
+ public Set getCategories() {
+ return categories;
+ }
+
+ public void setCategories(Set categories) {
+ this.categories = categories;
+ }
+
+ public void addCategory(Category category) {
+ if ( category == null ) {
+ return;
+ }
+
+ if ( categories == null ) {
+ categories = new HashSet();
+ }
+
+ categories.add( category );
+ if ( category.getProducts() == null ) {
+ category.setProducts( new HashSet() );
+ }
+ category.getProducts().add( this );
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Salesperson.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Salesperson.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Salesperson.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,32 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping
+ SYSTEM
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
+
+<hibernate-mapping package="org.hibernate.test.filter">
+
+ <class name="Salesperson" table="SALES_PERSON">
+ <id name="id" column="SALES_PERSON_ID" >
+ <generator class="native"/>
+ </id>
+
+ <property name="name" column="NAME" type="string"/>
+ <property name="region" column="REG" type="string"/>
+
+ <many-to-one name="department" cascade="none" class="Department" column="DEPT_ID"/>
+
+ <property name="hireDate" column="`hire dt`" type="java.util.Date"/>
+
+ <set name="orders" cascade="none" inverse="true" fetch="join">
+ <key column="SALES_PERSON_ID"/>
+ <one-to-many class="Order"/>
+ <filter name="region" condition="REG = :region"/>
+ <filter name="fulfilledOrders"><![CDATA[`close dt` < :asOfDate]]></filter>
+ </set>
+
+ <filter name="region" condition="REG = :region"/>
+ <filter name="regionlist" condition="REG IN (:regions)"/>
+
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Salesperson.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Salesperson.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/Salesperson.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,68 @@
+// $Id: Salesperson.java 4448 2004-08-28 02:29:05Z steveebersole $
+package org.hibernate.test.filter;
+
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Date;
+
+/**
+ * Implementation of Salesperson.
+ *
+ * @author Steve
+ */
+public class Salesperson {
+ private Long id;
+ private String name;
+ private String region;
+ private Date hireDate;
+ private Department department;
+ private Set orders = new HashSet();
+
+ 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 String getRegion() {
+ return region;
+ }
+
+ public void setRegion(String region) {
+ this.region = region;
+ }
+
+ public Set getOrders() {
+ return orders;
+ }
+
+ public void setOrders(Set orders) {
+ this.orders = orders;
+ }
+
+ public Date getHireDate() {
+ return hireDate;
+ }
+
+ public void setHireDate(Date hireDate) {
+ this.hireDate = hireDate;
+ }
+
+ public Department getDepartment() {
+ return department;
+ }
+
+ public void setDepartment(Department department) {
+ this.department = department;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/defs.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/defs.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/filter/defs.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping
+ SYSTEM
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
+
+<hibernate-mapping>
+ <filter-def name="region">
+ <filter-param name="region" type="string"/>
+ </filter-def>
+
+ <filter-def name="regionlist">
+ <filter-param name="regions" type="string"/>
+ </filter-def>
+
+ <filter-def name="fulfilledOrders">
+ <filter-param name="asOfDate" type="timestamp"/>
+ </filter-def>
+
+ <filter-def name="effectiveDate">
+ <filter-param name="asOfDate" type="timestamp"/>
+ </filter-def>
+
+ <filter-def name="seniorSalespersons">
+ <filter-param name="asOfDate" type="timestamp"/>
+ </filter-def>
+
+ <filter-def name="cat">
+ <filter-param name="catId" type="long"/>
+ </filter-def>
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/formulajoin/Detail.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/formulajoin/Detail.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/formulajoin/Detail.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,46 @@
+//$Id: Detail.java 4602 2004-09-26 11:42:47Z oneovthafew $
+package org.hibernate.test.formulajoin;
+
+import java.io.Serializable;
+
+/**
+ * @author Gavin King
+ */
+public class Detail implements Serializable {
+ private Long id;
+ private Master master;
+ private int version;
+ private String details;
+ private boolean currentVersion;
+
+ public boolean isCurrentVersion() {
+ return currentVersion;
+ }
+ public void setCurrentVersion(boolean currentVersion) {
+ this.currentVersion = currentVersion;
+ }
+ public String getDetails() {
+ return details;
+ }
+ public void setDetails(String details) {
+ this.details = details;
+ }
+ public Long getId() {
+ return id;
+ }
+ public void setId(Long id) {
+ this.id = id;
+ }
+ public Master getMaster() {
+ return master;
+ }
+ public void setMaster(Master master) {
+ this.master = master;
+ }
+ public int getVersion() {
+ return version;
+ }
+ public void setVersion(int version) {
+ this.version = version;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/formulajoin/Detail.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/formulajoin/FormulaJoinTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/formulajoin/FormulaJoinTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/formulajoin/FormulaJoinTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,100 @@
+//$Id: FormulaJoinTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.formulajoin;
+
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.dialect.PostgreSQLDialect;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class FormulaJoinTest extends FunctionalTestCase {
+
+ public FormulaJoinTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "formulajoin/Master.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( FormulaJoinTest.class );
+ }
+
+ public void testFormulaJoin() {
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ Master master = new Master();
+ master.setName("master 1");
+ Detail current = new Detail();
+ current.setCurrentVersion(true);
+ current.setVersion(2);
+ current.setDetails("details of master 1 blah blah");
+ current.setMaster(master);
+ master.setDetail(current);
+ Detail past = new Detail();
+ past.setCurrentVersion(false);
+ past.setVersion(1);
+ past.setDetails("old details of master 1 yada yada");
+ past.setMaster(master);
+ s.persist(master);
+ s.persist(past);
+ s.persist(current);
+ tx.commit();
+ s.close();
+
+ if ( getDialect() instanceof PostgreSQLDialect ) return;
+
+ s = openSession();
+ tx = s.beginTransaction();
+ List l = s.createQuery("from Master m left join m.detail d").list();
+ assertEquals( l.size(), 1 );
+ tx.commit();
+ s.close();
+
+ s = openSession();
+ tx = s.beginTransaction();
+ l = s.createQuery("from Master m left join fetch m.detail").list();
+ assertEquals( l.size(), 1 );
+ Master m = (Master) l.get(0);
+ assertEquals( "master 1", m.getDetail().getMaster().getName() );
+ assertTrue( m==m.getDetail().getMaster() );
+ tx.commit();
+ s.close();
+
+ s = openSession();
+ tx = s.beginTransaction();
+ l = s.createQuery("from Master m join fetch m.detail").list();
+ assertEquals( l.size(), 1 );
+ tx.commit();
+ s.close();
+
+ s = openSession();
+ tx = s.beginTransaction();
+ l = s.createQuery("from Detail d join fetch d.currentMaster.master").list();
+ assertEquals( l.size(), 2 );
+ tx.commit();
+ s.close();
+
+ s = openSession();
+ tx = s.beginTransaction();
+ l = s.createQuery("from Detail d join fetch d.currentMaster.master m join fetch m.detail").list();
+ assertEquals( l.size(), 2 );
+
+ s.createQuery("delete from Detail").executeUpdate();
+ s.createQuery("delete from Master").executeUpdate();
+
+ tx.commit();
+ s.close();
+
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/formulajoin/FormulaJoinTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/formulajoin/Master.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/formulajoin/Master.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/formulajoin/Master.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,65 @@
+<?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 an association "filtered" using a formula
+ mapping in the foreign key.
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.formulajoin">
+
+ <class name="Master" table="t_masters">
+
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+
+ <property name="name"
+ not-null="true"
+ length="100"
+ update="false"/>
+
+ <many-to-one name="detail"
+ property-ref="currentMaster"
+ insert="false"
+ update="false">
+ <column name="id"/>
+ <formula>1</formula>
+ </many-to-one>
+
+ </class>
+
+ <class name="Detail" table="t_details">
+
+ <id name="id">
+ <generator class="hilo"/>
+ </id>
+
+ <property name="details"
+ length="300"
+ update="false"
+ not-null="true"/>
+
+ <property name="version" update="false">
+ <column name="version"
+ not-null="true"
+ unique-key="masterVersion"/>
+ </property>
+
+ <properties name="currentMaster">
+ <many-to-one name="master">
+ <column name="cur_master"
+ unique-key="masterVersion"
+ not-null="true"/>
+ </many-to-one>
+ <property name="currentVersion"
+ not-null="true"/>
+ </properties>
+
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/formulajoin/Master.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/formulajoin/Master.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/formulajoin/Master.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/formulajoin/Master.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,31 @@
+//$Id: Master.java 4602 2004-09-26 11:42:47Z oneovthafew $
+package org.hibernate.test.formulajoin;
+
+import java.io.Serializable;
+
+/**
+ * @author Gavin King
+ */
+public class Master implements Serializable {
+ private Long id;
+ private String name;
+ private Detail detail;
+ public Detail getDetail() {
+ return detail;
+ }
+ public void setDetail(Detail detail) {
+ this.detail = detail;
+ }
+ 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;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/formulajoin/Master.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/AbstractGeneratedPropertyTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/AbstractGeneratedPropertyTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/AbstractGeneratedPropertyTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,47 @@
+// $Id: AbstractGeneratedPropertyTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.generated;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.Hibernate;
+import org.hibernate.junit.functional.DatabaseSpecificFunctionalTestCase;
+
+/**
+ * Implementation of AbstractGeneratedPropertyTest.
+ *
+ * @author Steve Ebersole
+ */
+public abstract class AbstractGeneratedPropertyTest extends DatabaseSpecificFunctionalTestCase {
+ public AbstractGeneratedPropertyTest(String x) {
+ super( x );
+ }
+
+ public final void testGeneratedProperty() {
+ GeneratedPropertyEntity entity = new GeneratedPropertyEntity();
+ entity.setName( "entity-1" );
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.save( entity );
+ s.flush();
+ assertNotNull( "no timestamp retrieved", entity.getLastModified() );
+ t.commit();
+ s.close();
+
+ byte[] bytes = entity.getLastModified();
+
+ s = openSession();
+ t = s.beginTransaction();
+ entity = ( GeneratedPropertyEntity ) s.get( GeneratedPropertyEntity.class, entity.getId() );
+ assertTrue( Hibernate.BINARY.isEqual( bytes, entity.getLastModified() ) );
+ t.commit();
+ s.close();
+
+ assertTrue( Hibernate.BINARY.isEqual( bytes, entity.getLastModified() ) );
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.delete( entity );
+ t.commit();
+ s.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/ComponentOwner.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/ComponentOwner.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/ComponentOwner.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.generated">
+
+ <class name="ComponentOwner" table="part_gen_comp">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="name" />
+ <component name="component" class="ComponentOwner$Component">
+ <property name="generated" type="int" generated="always" column="GENED"/>
+ </component>
+ </class>
+
+ <database-object>
+ <create>
+ <![CDATA[CREATE OR REPLACE TRIGGER t_iu_part_gen_comp
+ BEFORE INSERT OR UPDATE ON part_gen_comp
+ FOR EACH ROW
+ BEGIN
+ IF INSERTING THEN
+ :new.gened := 1;
+ ELSE
+ :new.gened := :old.gened + 1;
+ END IF;
+ END;]]>
+ </create>
+ <drop>
+ <![CDATA[DROP TRIGGER t_iu_part_gen_comp]]>
+ </drop>
+ <dialect-scope name="org.hibernate.dialect.Oracle9Dialect"/>
+ <dialect-scope name="org.hibernate.dialect.OracleDialect"/>
+ </database-object>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/ComponentOwner.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/ComponentOwner.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/ComponentOwner.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,55 @@
+package org.hibernate.test.generated;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class ComponentOwner {
+ private Long id;
+ private String name;
+ private Component component;
+
+ public ComponentOwner() {
+ }
+
+ public ComponentOwner(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;
+ }
+
+ public Component getComponent() {
+ return component;
+ }
+
+ public void setComponent(Component component) {
+ this.component = component;
+ }
+
+ public static class Component {
+ private int generated;
+
+ public int getGenerated() {
+ return generated;
+ }
+
+ public void setGenerated(int generated) {
+ this.generated = generated;
+ }
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/GeneratedPropertyEntity.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/GeneratedPropertyEntity.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/GeneratedPropertyEntity.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,42 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.generated">
+
+ <class name="GeneratedPropertyEntity" table="gen_prop">
+
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="name"/>
+ <property name="lastModified" generated="always" type="binary">
+ <column name="lastModified" length="8"/>
+ </property>
+
+ </class>
+
+ <database-object>
+ <create>
+ <![CDATA[CREATE OR REPLACE TRIGGER t_iu_gen_prop
+ BEFORE INSERT OR UPDATE ON gen_prop
+ FOR EACH ROW
+ BEGIN
+ IF INSERTING THEN
+ :new.lastModified := HEXTORAW( '1' );
+ ELSE
+ :new.lastModified := HEXTORAW(
+ TO_NUMBER( RAWTOHEX( :old.lastModified ) ) + 1
+ );
+ END IF;
+ END;]]>
+ </create>
+ <drop>
+ <![CDATA[DROP TRIGGER t_iu_gen_prop]]>
+ </drop>
+ <dialect-scope name="org.hibernate.dialect.Oracle9Dialect"/>
+ <dialect-scope name="org.hibernate.dialect.OracleDialect"/>
+ </database-object>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/GeneratedPropertyEntity.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/GeneratedPropertyEntity.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/GeneratedPropertyEntity.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,37 @@
+// $Id: GeneratedPropertyEntity.java 7800 2005-08-10 12:13:00Z steveebersole $
+package org.hibernate.test.generated;
+
+/**
+ * Implementation of GeneratedPropertyEntity.
+ *
+ * @author Steve Ebersole
+ */
+public class GeneratedPropertyEntity {
+ private Long id;
+ private String name;
+ private byte[] lastModified;
+
+ 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 byte[] getLastModified() {
+ return lastModified;
+ }
+
+ public void setLastModified(byte[] lastModified) {
+ this.lastModified = lastModified;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/GeneratedPropertySuite.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/GeneratedPropertySuite.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/GeneratedPropertySuite.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,20 @@
+package org.hibernate.test.generated;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class GeneratedPropertySuite {
+ public static Test suite() {
+ TestSuite suite = new TestSuite( "generated property suite" );
+ suite.addTest( TimestampGeneratedValuesWithCachingTest.suite() );
+ suite.addTest( TriggerGeneratedValuesWithCachingTest.suite() );
+ suite.addTest( TriggerGeneratedValuesWithoutCachingTest.suite() );
+ suite.addTest( PartiallyGeneratedComponentTest.suite() );
+ return suite;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/MSSQLGeneratedPropertyEntity.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/MSSQLGeneratedPropertyEntity.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/MSSQLGeneratedPropertyEntity.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.generated">
+
+ <class name="GeneratedPropertyEntity" table="gen_prop">
+
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="name"/>
+ <property name="lastModified" generated="always" type="binary">
+ <column name="lastModified" length="8" sql-type="TIMESTAMP"/>
+ </property>
+
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/PartiallyGeneratedComponentTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/PartiallyGeneratedComponentTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/PartiallyGeneratedComponentTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,64 @@
+package org.hibernate.test.generated;
+
+import junit.framework.Test;
+
+import org.hibernate.junit.functional.DatabaseSpecificFunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.Oracle9Dialect;
+import org.hibernate.Session;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class PartiallyGeneratedComponentTest extends DatabaseSpecificFunctionalTestCase {
+ public PartiallyGeneratedComponentTest(String string) {
+ super( string );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "generated/ComponentOwner.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( PartiallyGeneratedComponentTest.class );
+ }
+
+ public boolean appliesTo(Dialect dialect) {
+ return dialect instanceof Oracle9Dialect;
+ }
+
+ public void testPartialComponentGeneration() {
+ ComponentOwner owner = new ComponentOwner( "initial" );
+ Session s = openSession();
+ s.beginTransaction();
+ s.save( owner );
+ s.getTransaction().commit();
+ s.close();
+
+ assertNotNull( "expecting insert value generation", owner.getComponent() );
+ int previousValue = owner.getComponent().getGenerated();
+ assertFalse( "expecting insert value generation", 0 == previousValue );
+
+ s = openSession();
+ s.beginTransaction();
+ owner = ( ComponentOwner ) s.get( ComponentOwner.class, owner.getId() );
+ assertEquals( "expecting insert value generation", previousValue, owner.getComponent().getGenerated() );
+ owner.setName( "subsequent" );
+ s.getTransaction().commit();
+ s.close();
+
+ assertNotNull( owner.getComponent() );
+ previousValue = owner.getComponent().getGenerated();
+
+ s = openSession();
+ s.beginTransaction();
+ owner = ( ComponentOwner ) s.get( ComponentOwner.class, owner.getId() );
+ assertEquals( "expecting update value generation", previousValue, owner.getComponent().getGenerated() );
+ s.delete( owner );
+ s.getTransaction().commit();
+ s.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/TimestampGeneratedValuesWithCachingTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/TimestampGeneratedValuesWithCachingTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/TimestampGeneratedValuesWithCachingTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,34 @@
+// $Id: TimestampGeneratedValuesWithCachingTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.generated;
+
+import junit.framework.Test;
+
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.SybaseDialect;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * Implementation of TimestampGeneratedValuesWithCachingTest.
+ *
+ * @author Steve Ebersole
+ */
+public class TimestampGeneratedValuesWithCachingTest extends AbstractGeneratedPropertyTest {
+
+ public TimestampGeneratedValuesWithCachingTest(String x) {
+ super( x );
+ }
+
+ public final String[] getMappings() {
+ return new String[] { "generated/MSSQLGeneratedPropertyEntity.hbm.xml" };
+ }
+
+ public boolean appliesTo(Dialect dialect) {
+ // this test is specific to Sybase/SQLServer as it is testing support
+ // for their TIMESTAMP datatype...
+ return ( dialect instanceof SybaseDialect );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( TimestampGeneratedValuesWithCachingTest.class );
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/TriggerGeneratedValuesWithCachingTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/TriggerGeneratedValuesWithCachingTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/TriggerGeneratedValuesWithCachingTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,35 @@
+// $Id: TriggerGeneratedValuesWithCachingTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.generated;
+
+import org.hibernate.dialect.Oracle9Dialect;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * Implementation of TriggerGeneratedValuesWithoutCachingTest.
+ *
+ * @author Steve Ebersole
+ */
+public class TriggerGeneratedValuesWithCachingTest extends AbstractGeneratedPropertyTest {
+
+ public TriggerGeneratedValuesWithCachingTest(String x) {
+ super( x );
+ }
+
+ public final String[] getMappings() {
+ return new String[] { "generated/GeneratedPropertyEntity.hbm.xml" };
+ }
+
+ public boolean appliesTo(Dialect dialect) {
+ // currently have only defined triggers for oracle...
+ // TODO : add more triggers for dialects which allow mods in triggers...
+ return ( dialect instanceof Oracle9Dialect );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( TriggerGeneratedValuesWithCachingTest.class );
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/TriggerGeneratedValuesWithoutCachingTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/TriggerGeneratedValuesWithoutCachingTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generated/TriggerGeneratedValuesWithoutCachingTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,38 @@
+// $Id: TriggerGeneratedValuesWithoutCachingTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.generated;
+
+import junit.framework.Test;
+
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.Oracle9Dialect;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * Implementation of TriggerGeneratedValuesWithoutCachingTest.
+ *
+ * @author Steve Ebersole
+ */
+public class TriggerGeneratedValuesWithoutCachingTest extends AbstractGeneratedPropertyTest {
+
+ public TriggerGeneratedValuesWithoutCachingTest(String x) {
+ super( x );
+ }
+
+ public final String[] getMappings() {
+ return new String[] { "generated/GeneratedPropertyEntity.hbm.xml" };
+ }
+
+ public boolean appliesTo(Dialect dialect) {
+ // currently have only defined triggers for oracle...
+ // TODO : add more triggers for dialects which allow mods in triggers...
+ return ( dialect instanceof Oracle9Dialect );
+ }
+
+ public String getCacheConcurrencyStrategy() {
+ return null;
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( TriggerGeneratedValuesWithoutCachingTest.class );
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/GeneratedKeysSuite.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/GeneratedKeysSuite.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/GeneratedKeysSuite.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,23 @@
+package org.hibernate.test.generatedkeys;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.hibernate.test.generatedkeys.identity.IdentityGeneratedKeysTest;
+import org.hibernate.test.generatedkeys.select.SelectGeneratorTest;
+import org.hibernate.test.generatedkeys.seqidentity.SequenceIdentityTest;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class GeneratedKeysSuite {
+ public static Test suite() {
+ TestSuite suite = new TestSuite( "generated keys suite" );
+ suite.addTest( IdentityGeneratedKeysTest.suite() );
+ suite.addTest( SelectGeneratorTest.suite() );
+ suite.addTest( SequenceIdentityTest.suite() );
+ return suite;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/identity/IdentityGeneratedKeysTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/identity/IdentityGeneratedKeysTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/identity/IdentityGeneratedKeysTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,160 @@
+package org.hibernate.test.generatedkeys.identity;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.junit.functional.DatabaseSpecificFunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Steve Ebersole
+ */
+public class IdentityGeneratedKeysTest extends DatabaseSpecificFunctionalTestCase {
+ public IdentityGeneratedKeysTest(String name) {
+ super( name );
+ }
+
+ public void configure(Configuration cfg) {
+ super.configure( cfg );
+ cfg.setProperty( Environment.GENERATE_STATISTICS, "true" );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "generatedkeys/identity/MyEntity.hbm.xml" };
+ }
+
+ public boolean appliesTo(Dialect dialect) {
+ return dialect.supportsIdentityColumns();
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( IdentityGeneratedKeysTest.class );
+ }
+
+ public void testIdentityColumnGeneratedIds() {
+ Session s = openSession();
+ s.beginTransaction();
+ MyEntity myEntity = new MyEntity( "test" );
+ Long id = ( Long ) s.save( myEntity );
+ assertNotNull( "identity column did not force immediate insert", id );
+ assertEquals( id, myEntity.getId() );
+ s.delete( myEntity );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testPersistOutsideTransaction() {
+ Session s = openSession();
+
+ // first test save() which should force an immediate insert...
+ MyEntity myEntity1 = new MyEntity( "test-save" );
+ Long id = ( Long ) s.save( myEntity1 );
+ assertNotNull( "identity column did not force immediate insert", id );
+ assertEquals( id, myEntity1.getId() );
+
+ // next test persist() which should cause a delayed insert...
+ long initialInsertCount = sfi().getStatistics().getEntityInsertCount();
+ MyEntity myEntity2 = new MyEntity( "test-persist");
+ s.persist( myEntity2 );
+ assertEquals( "persist on identity column not delayed", initialInsertCount, sfi().getStatistics().getEntityInsertCount() );
+ assertNull( myEntity2.getId() );
+
+ // an explicit flush should cause execution of the delayed insertion
+ s.flush();
+ assertEquals( "delayed persist insert not executed on flush", initialInsertCount + 1, sfi().getStatistics().getEntityInsertCount() );
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ s.delete( myEntity1 );
+ s.delete( myEntity2 );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testPersistOutsideTransactionCascadedToNonInverseCollection() {
+ long initialInsertCount = sfi().getStatistics().getEntityInsertCount();
+ Session s = openSession();
+ MyEntity myEntity = new MyEntity( "test-persist");
+ myEntity.getNonInverseChildren().add( new MyChild( "test-child-persist-non-inverse" ) );
+ s.persist( myEntity );
+ assertEquals( "persist on identity column not delayed", initialInsertCount, sfi().getStatistics().getEntityInsertCount() );
+ assertNull( myEntity.getId() );
+ s.flush();
+ assertEquals( "delayed persist insert not executed on flush", initialInsertCount + 2, sfi().getStatistics().getEntityInsertCount() );
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ s.createQuery( "delete MyChild" ).executeUpdate();
+ s.createQuery( "delete MyEntity" ).executeUpdate();
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testPersistOutsideTransactionCascadedToInverseCollection() {
+ long initialInsertCount = sfi().getStatistics().getEntityInsertCount();
+ Session s = openSession();
+ MyEntity myEntity2 = new MyEntity( "test-persist-2");
+ MyChild child = new MyChild( "test-child-persist-inverse" );
+ myEntity2.getInverseChildren().add( child );
+ child.setInverseParent( myEntity2 );
+ s.persist( myEntity2 );
+ assertEquals( "persist on identity column not delayed", initialInsertCount, sfi().getStatistics().getEntityInsertCount() );
+ assertNull( myEntity2.getId() );
+ s.flush();
+ assertEquals( "delayed persist insert not executed on flush", initialInsertCount + 2, sfi().getStatistics().getEntityInsertCount() );
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ s.createQuery( "delete MyChild" ).executeUpdate();
+ s.createQuery( "delete MyEntity" ).executeUpdate();
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testPersistOutsideTransactionCascadedToManyToOne() {
+ long initialInsertCount = sfi().getStatistics().getEntityInsertCount();
+ Session s = openSession();
+ MyEntity myEntity = new MyEntity( "test-persist");
+ myEntity.setSibling( new MySibling( "test-persist-sibling-out" ) );
+ s.persist( myEntity );
+ assertEquals( "persist on identity column not delayed", initialInsertCount, sfi().getStatistics().getEntityInsertCount() );
+ assertNull( myEntity.getId() );
+ s.flush();
+ assertEquals( "delayed persist insert not executed on flush", initialInsertCount + 2, sfi().getStatistics().getEntityInsertCount() );
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ s.createQuery( "delete MyEntity" ).executeUpdate();
+ s.createQuery( "delete MySibling" ).executeUpdate();
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testPersistOutsideTransactionCascadedFromManyToOne() {
+ long initialInsertCount = sfi().getStatistics().getEntityInsertCount();
+ Session s = openSession();
+ MyEntity myEntity2 = new MyEntity( "test-persist-2");
+ MySibling sibling = new MySibling( "test-persist-sibling-in" );
+ sibling.setEntity( myEntity2 );
+ s.persist( sibling );
+ assertEquals( "persist on identity column not delayed", initialInsertCount, sfi().getStatistics().getEntityInsertCount() );
+ assertNull( myEntity2.getId() );
+ s.flush();
+ assertEquals( "delayed persist insert not executed on flush", initialInsertCount + 2, sfi().getStatistics().getEntityInsertCount() );
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ s.createQuery( "delete MySibling" ).executeUpdate();
+ s.createQuery( "delete MyEntity" ).executeUpdate();
+ s.getTransaction().commit();
+ s.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/identity/MyChild.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/identity/MyChild.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/identity/MyChild.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,41 @@
+package org.hibernate.test.generatedkeys.identity;
+
+/**
+ * @author Steve Ebersole
+ */
+public class MyChild {
+ private Long id;
+ private String name;
+ private MyEntity inverseParent;
+
+ public MyChild() {
+ }
+
+ public MyChild(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;
+ }
+
+ public MyEntity getInverseParent() {
+ return inverseParent;
+ }
+
+ public void setInverseParent(MyEntity inverseParent) {
+ this.inverseParent = inverseParent;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/identity/MyEntity.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/identity/MyEntity.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/identity/MyEntity.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,48 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.generatedkeys.identity" default-access="field">
+
+ <class name="MyEntity" table="my_entity">
+ <id name="id">
+ <generator class="identity"/>
+ </id>
+ <property name="name"/>
+
+ <!-- used to test cascades "out" to a many-to-one association -->
+ <many-to-one name="sibling" class="MySibling" cascade="persist, merge"/>
+
+ <!-- used to test cascades "out" to non-inverse collections -->
+ <set name="nonInverseChildren" inverse="false" cascade="persist, merge">
+ <key column="non_inv_parent_id"/>
+ <one-to-many class="MyChild"/>
+ </set>
+
+ <!-- used to test cascades "out" to inverse collections -->
+ <set name="inverseChildren" inverse="true" cascade="persist, merge">
+ <key column="inv_parent_id"/>
+ <one-to-many class="MyChild"/>
+ </set>
+ </class>
+
+
+ <class name="MySibling" table="my_sibling">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="name"/>
+ <many-to-one name="entity" class="MyEntity" cascade="persist, merge"/>
+ </class>
+
+
+ <class name="MyChild" table="my_child">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="name"/>
+ <many-to-one name="inverseParent" column="inv_parent_id" class="MyEntity"/>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/identity/MyEntity.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/identity/MyEntity.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/identity/MyEntity.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,62 @@
+package org.hibernate.test.generatedkeys.identity;
+
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * @author Steve Ebersole
+ */
+public class MyEntity {
+ private Long id;
+ private String name;
+ private MySibling sibling;
+ private Set nonInverseChildren = new HashSet();
+ private Set inverseChildren = new HashSet();
+
+ public MyEntity() {
+ }
+
+ public MyEntity(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;
+ }
+
+ public MySibling getSibling() {
+ return sibling;
+ }
+
+ public void setSibling(MySibling sibling) {
+ this.sibling = sibling;
+ }
+
+ public Set getNonInverseChildren() {
+ return nonInverseChildren;
+ }
+
+ public void setNonInverseChildren(Set nonInverseChildren) {
+ this.nonInverseChildren = nonInverseChildren;
+ }
+
+ public Set getInverseChildren() {
+ return inverseChildren;
+ }
+
+ public void setInverseChildren(Set inverseChildren) {
+ this.inverseChildren = inverseChildren;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/identity/MySibling.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/identity/MySibling.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/identity/MySibling.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,41 @@
+package org.hibernate.test.generatedkeys.identity;
+
+/**
+ * @author Steve Ebersole
+ */
+public class MySibling {
+ private Long id;
+ private String name;
+ private MyEntity entity;
+
+ public MySibling() {
+ }
+
+ public MySibling(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;
+ }
+
+ public MyEntity getEntity() {
+ return entity;
+ }
+
+ public void setEntity(MyEntity entity) {
+ this.entity = entity;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/select/MyEntity.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/select/MyEntity.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/select/MyEntity.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,36 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.generatedkeys.select" default-access="field">
+
+ <class name="MyEntity" table="my_entity">
+
+ <id name="id">
+ <generator class="select"/>
+ </id>
+ <natural-id>
+ <property name="name"/>
+ </natural-id>
+ </class>
+
+ <database-object>
+ <create>
+ <![CDATA[CREATE OR REPLACE TRIGGER t_i_my_entity
+ BEFORE INSERT ON my_entity
+ FOR EACH ROW
+ BEGIN
+ select nvl( max(id), 0 ) + 1
+ into :new.id
+ from my_entity;
+ END;]]>
+ </create>
+ <drop>
+ <![CDATA[DROP TRIGGER t_i_my_entity]]>
+ </drop>
+ <dialect-scope name="org.hibernate.dialect.Oracle9Dialect"/>
+ <dialect-scope name="org.hibernate.dialect.OracleDialect"/>
+ </database-object>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/select/MyEntity.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/select/MyEntity.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/select/MyEntity.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,28 @@
+package org.hibernate.test.generatedkeys.select;
+
+/**
+ * @author <a href="mailto:steve at hibernate.org">Steve Ebersole </a>
+ */
+public class MyEntity {
+ private Long id;
+ private String name;
+
+ public MyEntity() {
+ }
+
+ public MyEntity(String name) {
+ this.name = name;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/select/SelectGeneratorTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/select/SelectGeneratorTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/select/SelectGeneratorTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,52 @@
+package org.hibernate.test.generatedkeys.select;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.dialect.DataDirectOracle9Dialect;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.Oracle9Dialect;
+import org.hibernate.junit.functional.DatabaseSpecificFunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Steve Ebersole
+ */
+public class SelectGeneratorTest extends DatabaseSpecificFunctionalTestCase {
+ public SelectGeneratorTest(String x) {
+ super( x );
+ }
+
+ // TODO : need to determine appropriate physical generation strategies for select-generator testing on other databases...
+
+ public String[] getMappings() {
+ return new String[] { "generatedkeys/select/MyEntity.hbm.xml" };
+ }
+
+ public boolean appliesTo(Dialect dialect) {
+ return ( dialect instanceof Oracle9Dialect );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( SelectGeneratorTest.class );
+ }
+
+ public void testJDBC3GetGeneratedKeysSupportOnOracle() {
+ if ( getDialect() instanceof DataDirectOracle9Dialect ) {
+ reportSkip( "DataDirect drivers known to not support JDBC3 getGeneratedKeys for Oracle", "oracle getGeneratedKeys support" );
+ return;
+ }
+ Session session = openSession();
+ session.beginTransaction();
+
+ MyEntity e = new MyEntity( "entity-1" );
+ session.save( e );
+
+ // this insert should happen immediately!
+ assertEquals( "id not generated through forced insertion", new Long(1), e.getId() );
+
+ session.delete( e );
+ session.getTransaction().commit();
+ session.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/seqidentity/MyEntity.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/seqidentity/MyEntity.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/seqidentity/MyEntity.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.generatedkeys.seqidentity" default-access="field">
+
+ <class name="MyEntity" table="my_entity">
+
+ <id name="id">
+ <generator class="sequence-identity"/>
+ </id>
+ <natural-id>
+ <property name="name"/>
+ </natural-id>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/seqidentity/MyEntity.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/seqidentity/MyEntity.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/seqidentity/MyEntity.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,28 @@
+package org.hibernate.test.generatedkeys.seqidentity;
+
+/**
+ * @author <a href="mailto:steve at hibernate.org">Steve Ebersole </a>
+ */
+public class MyEntity {
+ private Long id;
+ private String name;
+
+ public MyEntity() {
+ }
+
+ public MyEntity(String name) {
+ this.name = name;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/seqidentity/SequenceIdentityTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/seqidentity/SequenceIdentityTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/generatedkeys/seqidentity/SequenceIdentityTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,53 @@
+package org.hibernate.test.generatedkeys.seqidentity;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.dialect.DataDirectOracle9Dialect;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.Oracle9Dialect;
+import org.hibernate.junit.functional.DatabaseSpecificFunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Steve Ebersole
+ */
+public class SequenceIdentityTest extends DatabaseSpecificFunctionalTestCase {
+ public SequenceIdentityTest(String x) {
+ super( x );
+ }
+
+ public void configure(Configuration cfg) {
+ super.configure( cfg );
+ }
+
+ public boolean appliesTo(Dialect dialect) {
+ // the DataDirect driver for Oracle known to not support
+ // JDBC3 getGeneratedKeys...
+ return ( dialect instanceof Oracle9Dialect ) && ( ! ( dialect instanceof DataDirectOracle9Dialect ) ) ;
+ }
+
+ public String[] getMappings() {
+ return new String[] { "generatedkeys/seqidentity/MyEntity.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( SequenceIdentityTest.class );
+ }
+
+ public void testSequenceIdentityGenerator() {
+ Session session = openSession();
+ session.beginTransaction();
+
+ MyEntity e = new MyEntity( "entity-1" );
+ session.save( e );
+
+ // this insert should happen immediately!
+ assertEquals( "id not generated through forced insertion", new Long(1), e.getId() );
+
+ session.delete( e );
+ session.getTransaction().commit();
+ session.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/ASTParserLoadingTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/ASTParserLoadingTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/ASTParserLoadingTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1925 @@
+// $Id: ASTParserLoadingTest.java 11373 2007-03-29 19:09:07Z steve.ebersole at jboss.com $
+package org.hibernate.test.hql;
+
+import java.math.BigDecimal;
+import java.sql.Date;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.Query;
+import org.hibernate.QueryException;
+import org.hibernate.ScrollableResults;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.TypeMismatchException;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.dialect.DB2Dialect;
+import org.hibernate.dialect.HSQLDialect;
+import org.hibernate.dialect.MySQLDialect;
+import org.hibernate.dialect.Oracle9Dialect;
+import org.hibernate.dialect.PostgreSQLDialect;
+import org.hibernate.dialect.SQLServerDialect;
+import org.hibernate.dialect.SybaseDialect;
+import org.hibernate.hql.ast.ASTQueryTranslatorFactory;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.stat.QueryStatistics;
+import org.hibernate.test.any.IntegerPropertyValue;
+import org.hibernate.test.any.PropertySet;
+import org.hibernate.test.any.PropertyValue;
+import org.hibernate.test.any.StringPropertyValue;
+import org.hibernate.test.cid.Customer;
+import org.hibernate.test.cid.LineItem;
+import org.hibernate.test.cid.Order;
+import org.hibernate.test.cid.Product;
+import org.hibernate.transform.DistinctRootEntityResultTransformer;
+import org.hibernate.transform.Transformers;
+import org.hibernate.type.ComponentType;
+import org.hibernate.type.ManyToOneType;
+import org.hibernate.type.Type;
+import org.hibernate.util.StringHelper;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Tests the integration of the new AST parser into the loading of query results using
+ * the Hibernate persisters and loaders.
+ * <p/>
+ * Also used to test the syntax of the resulting sql against the underlying
+ * database, specifically for functionality not supported by the classic
+ * parser.
+ *
+ * @author Steve
+ */
+public class ASTParserLoadingTest extends FunctionalTestCase {
+
+ private static final Log log = LogFactory.getLog( ASTParserLoadingTest.class );
+
+ private List createdAnimalIds = new ArrayList();
+
+ public ASTParserLoadingTest(String name) {
+ super( name );
+ }
+
+ public String[] getMappings() {
+ return new String[] {
+ "hql/Animal.hbm.xml",
+ "hql/FooBarCopy.hbm.xml",
+ "hql/SimpleEntityWithAssociation.hbm.xml",
+ "hql/CrazyIdFieldNames.hbm.xml",
+ "batchfetch/ProductLine.hbm.xml",
+ "cid/Customer.hbm.xml",
+ "cid/Order.hbm.xml",
+ "cid/LineItem.hbm.xml",
+ "cid/Product.hbm.xml",
+ "any/Properties.hbm.xml",
+ "legacy/Commento.hbm.xml",
+ "legacy/Marelo.hbm.xml"
+ };
+ }
+
+ public void configure(Configuration cfg) {
+ super.configure( cfg );
+ cfg.setProperty( Environment.USE_QUERY_CACHE, "true" );
+ cfg.setProperty( Environment.GENERATE_STATISTICS, "true" );
+ cfg.setProperty( Environment.QUERY_TRANSLATOR, ASTQueryTranslatorFactory.class.getName() );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( ASTParserLoadingTest.class );
+ }
+
+ public void testInvalidCollectionDereferencesFail() {
+ Session s = openSession();
+ s.beginTransaction();
+
+ // control group...
+ s.createQuery( "from Animal a join a.offspring o where o.description = 'xyz'" ).list();
+ s.createQuery( "from Animal a join a.offspring o where o.father.description = 'xyz'" ).list();
+ s.createQuery( "from Animal a join a.offspring o order by o.description" ).list();
+ s.createQuery( "from Animal a join a.offspring o order by o.father.description" ).list();
+
+ try {
+ s.createQuery( "from Animal a where a.offspring.description = 'xyz'" ).list();
+ fail( "illegal collection dereference semantic did not cause failure" );
+ }
+ catch( QueryException qe ) {
+ log.trace( "expected failure...", qe );
+ }
+
+ try {
+ s.createQuery( "from Animal a where a.offspring.father.description = 'xyz'" ).list();
+ fail( "illegal collection dereference semantic did not cause failure" );
+ }
+ catch( QueryException qe ) {
+ log.trace( "expected failure...", qe );
+ }
+
+ try {
+ s.createQuery( "from Animal a order by a.offspring.description" ).list();
+ fail( "illegal collection dereference semantic did not cause failure" );
+ }
+ catch( QueryException qe ) {
+ log.trace( "expected failure...", qe );
+ }
+
+ try {
+ s.createQuery( "from Animal a order by a.offspring.father.description" ).list();
+ fail( "illegal collection dereference semantic did not cause failure" );
+ }
+ catch( QueryException qe ) {
+ log.trace( "expected failure...", qe );
+ }
+
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ /**
+ * Copied from {@link HQLTest#testConcatenation}
+ */
+ public void testConcatenation() {
+ // simple syntax checking...
+ Session s = openSession();
+ s.beginTransaction();
+ s.createQuery( "from Human h where h.nickName = '1' || 'ov' || 'tha' || 'few'" ).list();
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ /**
+ * Copied from {@link HQLTest#testExpressionWithParamInFunction}
+ */
+ public void testExpressionWithParamInFunction() {
+ Session s = openSession();
+ s.beginTransaction();
+ s.createQuery( "from Animal a where abs(a.bodyWeight-:param) < 2.0" ).setLong( "param", 1 ).list();
+ s.createQuery( "from Animal a where abs(:param - a.bodyWeight) < 2.0" ).setLong( "param", 1 ).list();
+ if ( ! ( getDialect() instanceof HSQLDialect ) ) {
+ // HSQLDB does not like the abs(? - ?) syntax...
+ s.createQuery( "from Animal where abs(:x - :y) < 2.0" ).setLong( "x", 1 ).setLong( "y", 1 ).list();
+ }
+ s.createQuery( "from Animal where lower(upper(:foo)) like 'f%'" ).setString( "foo", "foo" ).list();
+ s.createQuery( "from Animal a where abs(abs(a.bodyWeight - 1.0 + :param) * abs(length('ffobar')-3)) = 3.0" ).setLong( "param", 1 ).list();
+ s.createQuery( "from Animal where lower(upper('foo') || upper(:bar)) like 'f%'" ).setString( "bar", "xyz" ).list();
+ if ( ! ( getDialect() instanceof PostgreSQLDialect || getDialect() instanceof MySQLDialect ) ) {
+ s.createQuery( "from Animal where abs(cast(1 as float) - cast(:param as float)) = 1.0" ).setLong( "param", 1 ).list();
+ }
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testCrazyIdFieldNames() {
+ MoreCrazyIdFieldNameStuffEntity top = new MoreCrazyIdFieldNameStuffEntity( "top" );
+ HeresAnotherCrazyIdFieldName next = new HeresAnotherCrazyIdFieldName( "next" );
+ top.setHeresAnotherCrazyIdFieldName( next );
+ MoreCrazyIdFieldNameStuffEntity other = new MoreCrazyIdFieldNameStuffEntity( "other" );
+ Session s = openSession();
+ s.beginTransaction();
+ s.save( next );
+ s.save( top );
+ s.save( other );
+ s.flush();
+
+ List results = s.createQuery( "select e.heresAnotherCrazyIdFieldName from MoreCrazyIdFieldNameStuffEntity e where e.heresAnotherCrazyIdFieldName is not null" ).list();
+ assertEquals( 1, results.size() );
+ Object result = results.get( 0 );
+ assertClassAssignability( HeresAnotherCrazyIdFieldName.class, result.getClass() );
+ assertSame( next, result );
+
+ results = s.createQuery( "select e.heresAnotherCrazyIdFieldName.heresAnotherCrazyIdFieldName from MoreCrazyIdFieldNameStuffEntity e where e.heresAnotherCrazyIdFieldName is not null" ).list();
+ assertEquals( 1, results.size() );
+ result = results.get( 0 );
+ assertClassAssignability( Long.class, result.getClass() );
+ assertEquals( next.getHeresAnotherCrazyIdFieldName(), result );
+
+ results = s.createQuery( "select e.heresAnotherCrazyIdFieldName from MoreCrazyIdFieldNameStuffEntity e" ).list();
+ assertEquals( 1, results.size() );
+ Iterator itr = s.createQuery( "select e.heresAnotherCrazyIdFieldName from MoreCrazyIdFieldNameStuffEntity e" ).iterate();
+ assertTrue( itr.hasNext() ); itr.next(); assertFalse( itr.hasNext() );
+
+ s.delete( top );
+ s.delete( next );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testImplicitJoinsInDifferentClauses() {
+ // HHH-2257 :
+ // both the classic and ast translators output the same syntactically valid sql
+ // for all of these cases; the issue is that shallow (iterate) and
+ // non-shallow (list/scroll) queries return different results because the
+ // shallow skips the inner join which "weeds out" results from the non-shallow queries.
+ // The results were initially different depending upon the clause(s) in which the
+ // implicit join occurred
+ Session s = openSession();
+ s.beginTransaction();
+ SimpleEntityWithAssociation owner = new SimpleEntityWithAssociation( "owner" );
+ SimpleAssociatedEntity e1 = new SimpleAssociatedEntity( "thing one", owner );
+ SimpleAssociatedEntity e2 = new SimpleAssociatedEntity( "thing two" );
+ s.save( e1 );
+ s.save( e2 );
+ s.save( owner );
+ s.getTransaction().commit();
+ s.close();
+
+ checkCounts( "select e.owner from SimpleAssociatedEntity e", 1, "implicit-join in select clause" );
+ checkCounts( "select e.id, e.owner from SimpleAssociatedEntity e", 1, "implicit-join in select clause" );
+
+ // resolved to a "id short cut" when part of the order by clause -> no inner join = no weeding out...
+ checkCounts( "from SimpleAssociatedEntity e order by e.owner", 2, "implicit-join in order-by clause" );
+ // resolved to a "id short cut" when part of the group by clause -> no inner join = no weeding out...
+ checkCounts( "select e.owner.id, count(*) from SimpleAssociatedEntity e group by e.owner", 2, "implicit-join in select and group-by clauses" );
+
+ s = openSession();
+ s.beginTransaction();
+ s.delete( e1 );
+ s.delete( e2 );
+ s.delete( owner );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ private void checkCounts(String hql, int expected, String testCondition) {
+ Session s = openSession();
+ s.beginTransaction();
+ int count = determineCount( s.createQuery( hql ).list().iterator() );
+ assertEquals( "list() [" + testCondition + "]", expected, count );
+ count = determineCount( s.createQuery( hql ).iterate() );
+ assertEquals( "iterate() [" + testCondition + "]", expected, count );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testImplicitSelectEntityAssociationInShallowQuery() {
+ // HHH-2257 :
+ // both the classic and ast translators output the same syntactically valid sql.
+ // the issue is that shallow and non-shallow queries return different
+ // results because the shallow skips the inner join which "weeds out" results
+ // from the non-shallow queries...
+ Session s = openSession();
+ s.beginTransaction();
+ SimpleEntityWithAssociation owner = new SimpleEntityWithAssociation( "owner" );
+ SimpleAssociatedEntity e1 = new SimpleAssociatedEntity( "thing one", owner );
+ SimpleAssociatedEntity e2 = new SimpleAssociatedEntity( "thing two" );
+ s.save( e1 );
+ s.save( e2 );
+ s.save( owner );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ int count = determineCount( s.createQuery( "select e.id, e.owner from SimpleAssociatedEntity e" ).list().iterator() );
+ assertEquals( 1, count ); // thing two would be removed from the result due to the inner join
+ count = determineCount( s.createQuery( "select e.id, e.owner from SimpleAssociatedEntity e" ).iterate() );
+ assertEquals( 1, count );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ s.delete( e1 );
+ s.delete( e2 );
+ s.delete( owner );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ private int determineCount(Iterator iterator) {
+ int count = 0;
+ while( iterator.hasNext() ) {
+ count++;
+ iterator.next();
+ }
+ return count;
+ }
+
+ public void testNestedComponentIsNull() {
+ // (1) From MapTest originally...
+ // (2) Was then moved into HQLTest...
+ // (3) However, a bug fix to EntityType#getIdentifierOrUniqueKeyType (HHH-2138)
+ // caused the classic parser to suddenly start throwing exceptions on
+ // this query, apparently relying on the buggy behavior somehow; thus
+ // moved here to at least get some syntax checking...
+ //
+ // fyi... found and fixed the problem in the classic parser; still
+ // leaving here for syntax checking
+ new SyntaxChecker( "from Commento c where c.marelo.commento.mcompr is null" ).checkAll();
+ }
+
+ public void testSpecialClassPropertyReference() {
+ // this is a long standing bug in Hibernate when applied to joined-subclasses;
+ // see HHH-939 for details and history
+ new SyntaxChecker( "from Zoo zoo where zoo.class = PettingZoo" ).checkAll();
+ new SyntaxChecker( "select a.description from Animal a where a.class = Mammal" ).checkAll();
+ new SyntaxChecker( "select a.class from Animal a" ).checkAll();
+ new SyntaxChecker( "from DomesticAnimal an where an.class = Dog" ).checkAll();
+ 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
+ //
+ // (1) pregnant is defined as a property of the class (Mammal) itself
+ // (2) description is defined as a property of the superclass (Animal)
+ // (3) name is defined as a property of a particular subclass (Human)
+
+ new SyntaxChecker( "from Zoo z join z.mammals as m where m.name.first = 'John'" ).checkIterate();
+
+ new SyntaxChecker( "from Zoo z join z.mammals as m where m.pregnant = false" ).checkAll();
+ new SyntaxChecker( "select m.pregnant from Zoo z join z.mammals as m where m.pregnant = false" ).checkAll();
+
+ new SyntaxChecker( "from Zoo z join z.mammals as m where m.description = 'tabby'" ).checkAll();
+ new SyntaxChecker( "select m.description from Zoo z join z.mammals as m where m.description = 'tabby'" ).checkAll();
+
+ new SyntaxChecker( "from Zoo z join z.mammals as m where m.name.first = 'John'" ).checkAll();
+ new SyntaxChecker( "select m.name from Zoo z join z.mammals as m where m.name.first = 'John'" ).checkAll();
+
+ new SyntaxChecker( "select m.pregnant from Zoo z join z.mammals as m" ).checkAll();
+ new SyntaxChecker( "select m.description from Zoo z join z.mammals as m" ).checkAll();
+ new SyntaxChecker( "select m.name from Zoo z join z.mammals as m" ).checkAll();
+
+ new SyntaxChecker( "from DomesticAnimal da join da.owner as o where o.nickName = 'Gavin'" ).checkAll();
+ new SyntaxChecker( "select da.father from DomesticAnimal da join da.owner as o where o.nickName = 'Gavin'" ).checkAll();
+ }
+
+ public void testSimpleSelectWithLimitAndOffset() throws Exception {
+ if ( ! ( getDialect().supportsLimit() && getDialect().supportsLimitOffset() ) ) {
+ reportSkip( "dialect does not support offset and limit combo", "limit and offset combination" );
+ return;
+ }
+
+ // just checking correctness of param binding code...
+ Session session = openSession();
+ session.createQuery( "from Animal" )
+ .setFirstResult( 2 )
+ .setMaxResults( 1 )
+ .list();
+ session.close();
+ }
+
+ public void testJPAPositionalParameterList() {
+ Session s = openSession();
+ s.beginTransaction();
+ ArrayList params = new ArrayList();
+ params.add( "Doe" );
+ params.add( "Public" );
+ s.createQuery( "from Human where name.last in (?1)" )
+ .setParameterList( "1", params )
+ .list();
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testComponentQueries() {
+ Session s = openSession();
+ s.beginTransaction();
+
+ Type[] types = s.createQuery( "select h.name from Human h" ).getReturnTypes();
+ assertEquals( 1, types.length );
+ assertTrue( types[0] instanceof ComponentType );
+
+ // Test the ability to perform comparisions between component values
+ s.createQuery( "from Human h where h.name = h.name" ).list();
+ s.createQuery( "from Human h where h.name = :name" ).setParameter( "name", new Name() ).list();
+ s.createQuery( "from Human where name = :name" ).setParameter( "name", new Name() ).list();
+ s.createQuery( "from Human h where :name = h.name" ).setParameter( "name", new Name() ).list();
+ s.createQuery( "from Human h where :name <> h.name" ).setParameter( "name", new Name() ).list();
+
+ // Test the ability to perform comparisions between a component and an explicit row-value
+ s.createQuery( "from Human h where h.name = ('John', 'X', 'Doe')" ).list();
+ s.createQuery( "from Human h where ('John', 'X', 'Doe') = h.name" ).list();
+ s.createQuery( "from Human h where ('John', 'X', 'Doe') <> h.name" ).list();
+ s.createQuery( "from Human h where ('John', 'X', 'Doe') >= h.name" ).list();
+
+ s.createQuery( "from Human h order by h.name" ).list();
+
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testComponentParameterBinding() {
+ // HHH-1774 : parameters are bound incorrectly with component parameters...
+ Session s = openSession();
+ s.beginTransaction();
+
+ Order.Id oId = new Order.Id( "1234", 1 );
+
+ // control
+ s.createQuery("from Order o where o.customer.name =:name and o.id = :id")
+ .setParameter( "name", "oracle" )
+ .setParameter( "id", oId )
+ .list();
+
+ // this is the form that caused problems in the original case...
+ s.createQuery("from Order o where o.id = :id and o.customer.name =:name ")
+ .setParameter( "id", oId )
+ .setParameter( "name", "oracle" )
+ .list();
+
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testAnyMappingReference() {
+ Session s = openSession();
+ s.beginTransaction();
+
+ PropertyValue redValue = new StringPropertyValue( "red" );
+ PropertyValue lonliestNumberValue = new IntegerPropertyValue( 1 );
+
+ Long id;
+ PropertySet ps = new PropertySet( "my properties" );
+ ps.setSomeSpecificProperty( redValue );
+ ps.getGeneralProperties().put( "the lonliest number", lonliestNumberValue );
+ ps.getGeneralProperties().put( "i like", new StringPropertyValue( "pina coladas" ) );
+ ps.getGeneralProperties().put( "i also like", new StringPropertyValue( "getting caught in the rain" ) );
+ s.save( ps );
+
+ s.getTransaction().commit();
+ id = ps.getId();
+ s.clear();
+ s.beginTransaction();
+
+ // TODO : setEntity() currently will not work here, but that would be *very* nice
+ // does not work because the corresponding EntityType is then used as the "bind type" rather
+ // than the "discovered" AnyType...
+ s.createQuery( "from PropertySet p where p.someSpecificProperty = :ssp" ).setParameter( "ssp", redValue ).list();
+
+ s.createQuery( "from PropertySet p where p.someSpecificProperty.id is not null" ).list();
+
+ s.createQuery( "from PropertySet p join p.generalProperties gp where gp.id is not null" ).list();
+
+ s.delete( s.load( PropertySet.class, id ) );
+
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testJdkEnumStyleEnumConstant() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+
+ s.createQuery( "from Zoo z where z.classification = org.hibernate.test.hql.Classification.LAME" ).list();
+
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testParameterTypeMismatchFailureExpected() {
+ Session s = openSession();
+ s.beginTransaction();
+
+ Query query = s.createQuery( "from Animal a where a.description = :nonstring" )
+ .setParameter( "nonstring", new Integer(1) );
+ try {
+ query.list();
+ fail( "query execution should have failed" );
+ }
+ catch( TypeMismatchException tme ) {
+ // expected behavior
+ }
+
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testMultipleBagFetchesFail() {
+ Session s = openSession();
+ s.beginTransaction();
+ try {
+ s.createQuery( "from Human h join fetch h.friends f join fetch f.friends fof" ).list();
+ fail( "failure expected" );
+ }
+ catch( HibernateException e ) {
+ assertTrue( "unexpected failure reason : " + e, e.getMessage().indexOf( "multiple bags" ) > 0 );
+ }
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testCollectionJoinsInSubselect() {
+ // HHH-1248 : initially FromElementFactory treated any explicit join
+ // as an implied join so that theta-style joins would always be used.
+ // This was because correlated subqueries cannot use ANSI-style joins
+ // for the correlation. However, this special treatment was not limited
+ // to only correlated subqueries; it was applied to any subqueries ->
+ // which in-and-of-itself is not necessarily bad. But somewhere later
+ // the choices made there caused joins to be dropped.
+ Session s = openSession();
+ String qryString =
+ "select a.id, a.description" +
+ " from Animal a" +
+ " left join a.offspring" +
+ " where a in (" +
+ " select a1 from Animal a1" +
+ " left join a1.offspring o" +
+ " where a1.id=1" +
+ ")";
+ s.createQuery( qryString ).list();
+ qryString =
+ "select h.id, h.description" +
+ " from Human h" +
+ " left join h.friends" +
+ " where h in (" +
+ " select h1" +
+ " from Human h1" +
+ " left join h1.friends f" +
+ " where h1.id=1" +
+ ")";
+ s.createQuery( qryString ).list();
+ qryString =
+ "select h.id, h.description" +
+ " from Human h" +
+ " left join h.friends f" +
+ " where f in (" +
+ " select h1" +
+ " from Human h1" +
+ " left join h1.friends f1" +
+ " where h = f1" +
+ ")";
+ s.createQuery( qryString ).list();
+ s.close();
+ }
+
+ public void testCollectionFetchWithDistinctionAndLimit() {
+ // create some test data...
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ int parentCount = 30;
+ for ( int i = 0; i < parentCount; i++ ) {
+ Animal child1 = new Animal();
+ child1.setDescription( "collection fetch distinction (child1 - parent" + i + ")" );
+ s.persist( child1 );
+ Animal child2 = new Animal();
+ child2.setDescription( "collection fetch distinction (child2 - parent " + i + ")" );
+ s.persist( child2 );
+ Animal parent = new Animal();
+ parent.setDescription( "collection fetch distinction (parent" + i + ")" );
+ parent.setSerialNumber( "123-" + i );
+ parent.addOffspring( child1 );
+ parent.addOffspring( child2 );
+ s.persist( parent );
+ }
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ // Test simple distinction
+ List results;
+ results = s.createQuery( "select distinct p from Animal p inner join fetch p.offspring" ).list();
+ assertEquals( "duplicate list() returns", 30, results.size() );
+ // Test first/max
+ results = s.createQuery( "select p from Animal p inner join fetch p.offspring order by p.id" )
+ .setFirstResult( 5 )
+ .setMaxResults( 20 )
+ .list();
+ assertEquals( "duplicate returns", 20, results.size() );
+ Animal firstReturn = ( Animal ) results.get( 0 );
+ assertEquals( "firstResult not applied correctly", "123-5", firstReturn.getSerialNumber() );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.createQuery( "delete Animal where mother is not null" ).executeUpdate();
+ s.createQuery( "delete Animal" ).executeUpdate();
+ t.commit();
+ s.close();
+ }
+
+ public void testFetchInSubqueryFails() {
+ Session s = openSession();
+ try {
+ s.createQuery( "from Animal a where a.mother in (select m from Animal a1 inner join a1.mother as m join fetch m.mother)" ).list();
+ fail( "fetch join allowed in subquery" );
+ }
+ catch( QueryException expected ) {
+ // expected behavior
+ }
+ s.close();
+ }
+
+ public void testQueryMetadataRetrievalWithFetching() {
+ // HHH-1464 : there was a problem due to the fact they we polled
+ // the shallow version of the query plan to get the metadata.
+ Session s = openSession();
+ Query query = s.createQuery( "from Animal a inner join fetch a.mother" );
+ assertEquals( 1, query.getReturnTypes().length );
+ assertNull( query.getReturnAliases() );
+ s.close();
+ }
+
+ public void testSuperclassPropertyReferenceAfterCollectionIndexedAccess() {
+ // note: simply performing syntax checking in the db
+ // test for HHH-429
+ Session s = openSession();
+ s.beginTransaction();
+ Mammal tiger = new Mammal();
+ tiger.setDescription( "Tiger" );
+ s.persist( tiger );
+ Mammal mother = new Mammal();
+ mother.setDescription( "Tiger's mother" );
+ mother.setBodyWeight( 4.0f );
+ mother.addOffspring( tiger );
+ s.persist( mother );
+ Zoo zoo = new Zoo();
+ zoo.setName( "Austin Zoo" );
+ zoo.setMammals( new HashMap() );
+ zoo.getMammals().put( "tiger", tiger );
+ s.persist( zoo );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ List results = s.createQuery( "from Zoo zoo where zoo.mammals['tiger'].mother.bodyWeight > 3.0f" ).list();
+ assertEquals( 1, results.size() );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ s.delete( tiger );
+ s.delete( mother );
+ s.delete( zoo );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testJoinFetchCollectionOfValues() {
+ // note: simply performing syntax checking in the db
+ Session s = openSession();
+ s.beginTransaction();
+ s.createQuery( "select h from Human as h join fetch h.nickNames" ).list();
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testIntegerLiterals() {
+ // note: simply performing syntax checking in the db
+ Session s = openSession();
+ s.beginTransaction();
+ s.createQuery( "from Foo where long = 1" ).list();
+ s.createQuery( "from Foo where long = " + Integer.MIN_VALUE ).list();
+ s.createQuery( "from Foo where long = " + Integer.MAX_VALUE ).list();
+ s.createQuery( "from Foo where long = 1L" ).list();
+ s.createQuery( "from Foo where long = " + (Long.MIN_VALUE + 1) + "L" ).list();
+ s.createQuery( "from Foo where long = " + Long.MAX_VALUE + "L" ).list();
+ s.createQuery( "from Foo where integer = " + (Long.MIN_VALUE + 1) ).list();
+// currently fails due to HHH-1387
+// s.createQuery( "from Foo where long = " + Long.MIN_VALUE ).list();
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testDecimalLiterals() {
+ // note: simply performing syntax checking in the db
+ Session s = openSession();
+ s.beginTransaction();
+ s.createQuery( "from Animal where bodyWeight > 100.0e-10" ).list();
+ s.createQuery( "from Animal where bodyWeight > 100.0E-10" ).list();
+ s.createQuery( "from Animal where bodyWeight > 100.001f" ).list();
+ s.createQuery( "from Animal where bodyWeight > 100.001F" ).list();
+ s.createQuery( "from Animal where bodyWeight > 100.001d" ).list();
+ s.createQuery( "from Animal where bodyWeight > 100.001D" ).list();
+ s.createQuery( "from Animal where bodyWeight > .001f" ).list();
+ s.createQuery( "from Animal where bodyWeight > 100e-10" ).list();
+ s.createQuery( "from Animal where bodyWeight > .01E-10" ).list();
+ s.createQuery( "from Animal where bodyWeight > 1e-38" ).list();
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testNakedPropertyRef() {
+ // note: simply performing syntax and column/table resolution checking in the db
+ Session s = openSession();
+ s.beginTransaction();
+ s.createQuery( "from Animal where bodyWeight = bodyWeight" ).list();
+ s.createQuery( "select bodyWeight from Animal" ).list();
+ s.createQuery( "select max(bodyWeight) from Animal" ).list();
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testNakedComponentPropertyRef() {
+ // note: simply performing syntax and column/table resolution checking in the db
+ Session s = openSession();
+ s.beginTransaction();
+ s.createQuery( "from Human where name.first = 'Gavin'" ).list();
+ s.createQuery( "select name from Human" ).list();
+ s.createQuery( "select upper(h.name.first) from Human as h" ).list();
+ s.createQuery( "select upper(name.first) from Human" ).list();
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testNakedImplicitJoins() {
+ // note: simply performing syntax and column/table resolution checking in the db
+ Session s = openSession();
+ s.beginTransaction();
+ s.createQuery( "from Animal where mother.father.id = 1" ).list();
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testNakedEntityAssociationReference() {
+ // note: simply performing syntax and column/table resolution checking in the db
+ Session s = openSession();
+ s.beginTransaction();
+ s.createQuery( "from Animal where mother = :mother" ).setParameter( "mother", null ).list();
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testNakedMapIndex() throws Exception {
+ // note: simply performing syntax and column/table resolution checking in the db
+ Session s = openSession();
+ s.beginTransaction();
+ s.createQuery( "from Zoo where mammals['dog'].description like '%black%'" ).list();
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testInvalidFetchSemantics() {
+ Session s = openSession();
+ s.beginTransaction();
+
+ try {
+ s.createQuery( "select mother from Human a left join fetch a.mother mother" ).list();
+ fail( "invalid fetch semantic allowed!" );
+ }
+ catch( QueryException e ) {
+ }
+
+ try {
+ s.createQuery( "select mother from Human a left join fetch a.mother mother" ).list();
+ fail( "invalid fetch semantic allowed!" );
+ }
+ catch( QueryException e ) {
+ }
+
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testArithmetic() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Zoo zoo = new Zoo();
+ zoo.setName("Melbourne Zoo");
+ s.persist(zoo);
+ s.createQuery("select 2*2*2*2*(2*2) from Zoo").uniqueResult();
+ s.createQuery("select 2 / (1+1) from Zoo").uniqueResult();
+ int result0 = ( (Integer) s.createQuery("select 2 - (1+1) from Zoo").uniqueResult() ).intValue();
+ int result1 = ( (Integer) s.createQuery("select 2 - 1 + 1 from Zoo").uniqueResult() ).intValue();
+ int result2 = ( (Integer) s.createQuery("select 2 * (1-1) from Zoo").uniqueResult() ).intValue();
+ int result3 = ( (Integer) s.createQuery("select 4 / (2 * 2) from Zoo").uniqueResult() ).intValue();
+ int result4 = ( (Integer) s.createQuery("select 4 / 2 * 2 from Zoo").uniqueResult() ).intValue();
+ int result5 = ( (Integer) s.createQuery("select 2 * (2/2) from Zoo").uniqueResult() ).intValue();
+ int result6 = ( (Integer) s.createQuery("select 2 * (2/2+1) from Zoo").uniqueResult() ).intValue();
+ assertEquals(result0, 0);
+ assertEquals(result1, 2);
+ assertEquals(result2, 0);
+ assertEquals(result3, 1);
+ assertEquals(result4, 4);
+ assertEquals(result5, 2);
+ assertEquals(result6, 4);
+ s.delete(zoo);
+ t.commit();
+ s.close();
+ }
+
+ public void testNestedCollectionFetch() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.createQuery("from Animal a left join fetch a.offspring o left join fetch o.offspring where a.mother.id = 1 order by a.description").list();
+ s.createQuery("from Zoo z left join fetch z.animals a left join fetch a.offspring where z.name ='MZ' order by a.description").list();
+ s.createQuery("from Human h left join fetch h.pets a left join fetch a.offspring where h.name.first ='Gavin' order by a.description").list();
+ t.commit();
+ s.close();
+ }
+
+ public void testSelectClauseSubselect() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Zoo zoo = new Zoo();
+ zoo.setName("Melbourne Zoo");
+ zoo.setMammals( new HashMap() );
+ zoo.setAnimals( new HashMap() );
+ Mammal plat = new Mammal();
+ plat.setBodyWeight( 11f );
+ plat.setDescription( "Platypus" );
+ plat.setZoo(zoo);
+ plat.setSerialNumber("plat123");
+ zoo.getMammals().put("Platypus", plat);
+ zoo.getAnimals().put("plat123", plat);
+ s.persist( plat );
+ s.persist(zoo);
+
+ s.createQuery("select (select max(z.id) from a.zoo z) from Animal a").list();
+ s.createQuery("select (select max(z.id) from a.zoo z where z.name=:name) from Animal a")
+ .setParameter("name", "Melbourne Zoo").list();
+
+ s.delete(plat);
+ s.delete(zoo);
+ t.commit();
+ s.close();
+ }
+
+ public void testInitProxy() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Mammal plat = new Mammal();
+ plat.setBodyWeight( 11f );
+ plat.setDescription( "Platypus" );
+ s.persist( plat );
+ s.flush();
+ s.clear();
+ plat = (Mammal) s.load(Mammal.class, plat.getId() );
+ assertFalse( Hibernate.isInitialized(plat) );
+ Object plat2 = s.createQuery("from Animal a").uniqueResult();
+ assertSame(plat, plat2);
+ assertTrue( Hibernate.isInitialized(plat) );
+ s.delete(plat);
+ t.commit();
+ s.close();
+ }
+
+ public void testSelectClauseImplicitJoin() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Zoo zoo = new Zoo();
+ zoo.setName("The Zoo");
+ zoo.setMammals( new HashMap() );
+ zoo.setAnimals( new HashMap() );
+ Mammal plat = new Mammal();
+ plat.setBodyWeight( 11f );
+ plat.setDescription( "Platypus" );
+ plat.setZoo(zoo);
+ plat.setSerialNumber("plat123");
+ zoo.getMammals().put("Platypus", plat);
+ zoo.getAnimals().put("plat123", plat);
+ s.persist( plat );
+ s.persist(zoo);
+ s.flush();
+ s.clear();
+ Query q = s.createQuery("select distinct a.zoo from Animal a where a.zoo is not null");
+ Type type = q.getReturnTypes()[0];
+ assertTrue( type instanceof ManyToOneType );
+ assertEquals( ( (ManyToOneType) type ).getAssociatedEntityName(), "org.hibernate.test.hql.Zoo" );
+ zoo = (Zoo) q.list().get(0);
+ assertEquals( zoo.getMammals().size(), 1 );
+ assertEquals( zoo.getAnimals().size(), 1 );
+ s.clear();
+ s.delete(plat);
+ s.delete(zoo);
+ t.commit();
+ s.close();
+ }
+
+ public void testSelectClauseImplicitJoinWithIterate() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Zoo zoo = new Zoo();
+ zoo.setName("The Zoo");
+ zoo.setMammals( new HashMap() );
+ zoo.setAnimals( new HashMap() );
+ Mammal plat = new Mammal();
+ plat.setBodyWeight( 11f );
+ plat.setDescription( "Platypus" );
+ plat.setZoo(zoo);
+ plat.setSerialNumber("plat123");
+ zoo.getMammals().put("Platypus", plat);
+ zoo.getAnimals().put("plat123", plat);
+ s.persist( plat );
+ s.persist(zoo);
+ s.flush();
+ s.clear();
+ Query q = s.createQuery("select distinct a.zoo from Animal a where a.zoo is not null");
+ Type type = q.getReturnTypes()[0];
+ assertTrue( type instanceof ManyToOneType );
+ assertEquals( ( (ManyToOneType) type ).getAssociatedEntityName(), "org.hibernate.test.hql.Zoo" );
+ zoo = (Zoo) q
+ .iterate().next();
+ assertEquals( zoo.getMammals().size(), 1 );
+ assertEquals( zoo.getAnimals().size(), 1 );
+ s.clear();
+ s.delete(plat);
+ s.delete(zoo);
+ t.commit();
+ s.close();
+ }
+
+ public void testComponentOrderBy() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ Long id1 = ( Long ) s.save( genSimpleHuman( "John", "Jacob" ) );
+ Long id2 = ( Long ) s.save( genSimpleHuman( "Jingleheimer", "Schmidt" ) );
+
+ s.flush();
+
+ // the component is defined with the firstName column first...
+ List results = s.createQuery( "from Human as h order by h.name" ).list();
+ assertEquals( "Incorrect return count", 2, results.size() );
+
+ Human h1 = ( Human ) results.get( 0 );
+ Human h2 = ( Human ) results.get( 1 );
+
+ assertEquals( "Incorrect ordering", id2, h1.getId() );
+ assertEquals( "Incorrect ordering", id1, h2.getId() );
+
+ s.delete( h1 );
+ s.delete( h2 );
+
+ t.commit();
+ s.close();
+ }
+
+ private Human genSimpleHuman(String fName, String lName) {
+ Human h = new Human();
+ h.setName( new Name() );
+ h.getName().setFirst( fName );
+ h.getName().setLast( lName );
+ h.getName().setInitial('X');
+
+ return h;
+ }
+
+ public void testCastInSelect() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Animal a = new Animal();
+ a.setBodyWeight(12.4f);
+ a.setDescription("an animal");
+ s.persist(a);
+ Integer bw = (Integer) s.createQuery("select cast(bodyWeight as integer) from Animal").uniqueResult();
+ bw = (Integer) s.createQuery("select cast(a.bodyWeight as integer) from Animal a").uniqueResult();
+ bw.toString();
+ s.delete(a);
+ t.commit();
+ s.close();
+ }
+
+ public void testAliases() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Animal a = new Animal();
+ a.setBodyWeight(12.4f);
+ a.setDescription("an animal");
+ s.persist(a);
+ String[] aliases1 = s.createQuery("select a.bodyWeight as abw, a.description from Animal a").getReturnAliases();
+ assertEquals(aliases1[0], "abw");
+ assertEquals(aliases1[1], "1");
+ String[] aliases2 = s.createQuery("select count(*), avg(a.bodyWeight) as avg from Animal a").getReturnAliases();
+ assertEquals(aliases2[0], "0");
+ assertEquals(aliases2[1], "avg");
+ s.delete(a);
+ t.commit();
+ s.close();
+ }
+
+ public void testParameterMixing() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.createQuery( "from Animal a where a.description = ? and a.bodyWeight = ? or a.bodyWeight = :bw" )
+ .setString( 0, "something" )
+ .setFloat( 1, 12345f )
+ .setFloat( "bw", 123f )
+ .list();
+ t.commit();
+ s.close();
+ }
+
+ public void testOrdinalParameters() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.createQuery( "from Animal a where a.description = ? and a.bodyWeight = ?" )
+ .setString( 0, "something" )
+ .setFloat( 1, 123f )
+ .list();
+ s.createQuery( "from Animal a where a.bodyWeight in (?, ?)" )
+ .setFloat( 0, 999f )
+ .setFloat( 1, 123f )
+ .list();
+ t.commit();
+ s.close();
+ }
+
+ public void testIndexParams() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.createQuery("from Zoo zoo where zoo.mammals[:name] = :id")
+ .setParameter("name", "Walrus")
+ .setParameter("id", new Long(123))
+ .list();
+ s.createQuery("from Zoo zoo where zoo.mammals[:name].bodyWeight > :w")
+ .setParameter("name", "Walrus")
+ .setParameter("w", new Float(123.32))
+ .list();
+ s.createQuery("from Zoo zoo where zoo.animals[:sn].mother.bodyWeight < :mw")
+ .setParameter("sn", "ant-123")
+ .setParameter("mw", new Float(23.32))
+ .list();
+ /*s.createQuery("from Zoo zoo where zoo.animals[:sn].description like :desc and zoo.animals[:sn].bodyWeight > :wmin and zoo.animals[:sn].bodyWeight < :wmax")
+ .setParameter("sn", "ant-123")
+ .setParameter("desc", "%big%")
+ .setParameter("wmin", new Float(123.32))
+ .setParameter("wmax", new Float(167.89))
+ .list();*/
+ /*s.createQuery("from Human where addresses[:type].city = :city and addresses[:type].country = :country")
+ .setParameter("type", "home")
+ .setParameter("city", "Melbourne")
+ .setParameter("country", "Australia")
+ .list();*/
+ t.commit();
+ s.close();
+ }
+
+ public void testAggregation() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Human h = new Human();
+ h.setBodyWeight( (float) 74.0 );
+ h.setHeight(120.5);
+ h.setDescription("Me");
+ h.setName( new Name("Gavin", 'A', "King") );
+ h.setNickName("Oney");
+ s.persist(h);
+ Double sum = (Double) s.createQuery("select sum(h.bodyWeight) from Human h").uniqueResult();
+ Double avg = (Double) s.createQuery("select avg(h.height) from Human h").uniqueResult();
+ assertEquals(sum.floatValue(), 74.0, 0.01);
+ assertEquals(avg.doubleValue(), 120.5, 0.01);
+ Long id = (Long) s.createQuery("select max(a.id) from Animal a").uniqueResult();
+ s.delete(h);
+ t.commit();
+ s.close();
+ }
+
+ public void testSelectClauseCase() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Human h = new Human();
+ h.setBodyWeight( (float) 74.0 );
+ h.setHeight(120.5);
+ h.setDescription("Me");
+ h.setName( new Name("Gavin", 'A', "King") );
+ h.setNickName("Oney");
+ s.persist(h);
+ String name = (String) s.createQuery("select case nickName when 'Oney' then 'gavin' when 'Turin' then 'christian' else nickName end from Human").uniqueResult();
+ assertEquals(name, "gavin");
+ String result = (String) s.createQuery("select case when bodyWeight > 100 then 'fat' else 'skinny' end from Human").uniqueResult();
+ assertEquals(result, "skinny");
+ s.delete(h);
+ t.commit();
+ s.close();
+ }
+
+ public void testImplicitPolymorphism() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ Product product = new Product();
+ product.setDescription( "My Product" );
+ product.setNumberAvailable( 10 );
+ product.setPrice( new BigDecimal( 123 ) );
+ product.setProductId( "4321" );
+ s.save( product );
+
+ List list = s.createQuery("from java.lang.Comparable").list();
+ assertEquals( list.size(), 0 );
+
+ list = s.createQuery("from java.lang.Object").list();
+ assertEquals( list.size(), 1 );
+
+ s.delete(product);
+
+ list = s.createQuery("from java.lang.Object").list();
+ assertEquals( list.size(), 0 );
+
+ t.commit();
+ s.close();
+ }
+
+ public void testCoalesce() {
+ Session session = openSession();
+ Transaction txn = session.beginTransaction();
+ session.createQuery("from Human h where coalesce(h.nickName, h.name.first, h.name.last) = 'max'").list();
+ session.createQuery("select nullif(nickName, '1e1') from Human").list();
+ txn.commit();
+ session.close();
+ }
+
+ public void testStr() {
+ Session session = openSession();
+ Transaction txn = session.beginTransaction();
+ Animal an = new Animal();
+ an.setBodyWeight(123.45f);
+ session.persist(an);
+ String str = (String) session.createQuery("select str(an.bodyWeight) from Animal an where str(an.bodyWeight) like '123%' or str(an.bodyWeight) like '1.23%'").uniqueResult();
+ if ( getDialect() instanceof DB2Dialect ) {
+ assertTrue( str.startsWith("1.234") );
+ }
+ else if ( getDialect() instanceof SQLServerDialect ) {
+ // no assertion as SQLServer always returns nulls here; even trying directly against the
+ // database, it seems to have problems with str() in the where clause...
+ }
+ else {
+ assertTrue( str.startsWith("123.4") );
+ }
+ if ( ! ( getDialect() instanceof SybaseDialect ) ) {
+ // In TransactSQL (the variant spoken by Sybase and SQLServer), the str() function
+ // is explicitly intended for numeric values only...
+ String dateStr1 = (String) session.createQuery("select str(current_date) from Animal").uniqueResult();
+ String dateStr2 = (String) session.createQuery("select str(year(current_date))||'-'||str(month(current_date))||'-'||str(day(current_date)) from Animal").uniqueResult();
+ System.out.println(dateStr1 + '=' + dateStr2);
+ if ( ! ( getDialect() instanceof Oracle9Dialect ) ) { //Oracle renders the name of the month :(
+ String[] dp1 = StringHelper.split("-", dateStr1);
+ String[] dp2 = StringHelper.split("-", dateStr2);
+ for (int i=0; i<3; i++) {
+ if ( dp1[i].startsWith( "0" ) ) {
+ dp1[i] = dp1[i].substring( 1 );
+ }
+ assertEquals( dp1[i], dp2[i] );
+ }
+ }
+ }
+ session.delete(an);
+ txn.commit();
+ session.close();
+ }
+
+ public void testCast() {
+ if ( ( getDialect() instanceof MySQLDialect ) || ( getDialect() instanceof DB2Dialect ) ) {
+ return;
+ }
+ Session session = openSession();
+ Transaction txn = session.beginTransaction();
+ session.createQuery("from Human h where h.nickName like 'G%'").list();
+ session.createQuery("from Animal a where cast(a.bodyWeight as string) like '1.%'").list();
+ session.createQuery("from Animal a where cast(a.bodyWeight as integer) = 1").list();
+ txn.commit();
+ session.close();
+ }
+
+ public void testExtract() {
+ Session session = openSession();
+ Transaction txn = session.beginTransaction();
+ session.createQuery("select second(current_timestamp()), minute(current_timestamp()), hour(current_timestamp()) from Mammal m").list();
+ session.createQuery("select day(m.birthdate), month(m.birthdate), year(m.birthdate) from Mammal m").list();
+ if ( !(getDialect() instanceof DB2Dialect) ) { //no ANSI extract
+ session.createQuery("select extract(second from current_timestamp()), extract(minute from current_timestamp()), extract(hour from current_timestamp()) from Mammal m").list();
+ session.createQuery("select extract(day from m.birthdate), extract(month from m.birthdate), extract(year from m.birthdate) from Mammal m").list();
+ }
+ txn.commit();
+ session.close();
+ }
+
+ public void testOneToManyFilter() throws Throwable {
+ Session session = openSession();
+ Transaction txn = session.beginTransaction();
+
+ Product product = new Product();
+ product.setDescription( "My Product" );
+ product.setNumberAvailable( 10 );
+ product.setPrice( new BigDecimal( 123 ) );
+ product.setProductId( "4321" );
+ session.save( product );
+
+ Customer customer = new Customer();
+ customer.setCustomerId( "123456789" );
+ customer.setName( "My customer" );
+ customer.setAddress( "somewhere" );
+ session.save( customer );
+
+ Order order = customer.generateNewOrder( new BigDecimal( 1234 ) );
+ session.save( order );
+
+ LineItem li = order.generateLineItem( product, 5 );
+ session.save( li );
+
+ session.flush();
+
+ assertEquals( session.createFilter( customer.getOrders(), "" ).list().size(), 1 );
+
+ assertEquals( session.createFilter( order.getLineItems(), "" ).list().size(), 1 );
+ assertEquals( session.createFilter( order.getLineItems(), "where this.quantity > :quantity" ).setInteger( "quantity", 5 ).list().size(), 0 );
+
+ session.delete(li);
+ session.delete(order);
+ session.delete(product);
+ session.delete(customer);
+ txn.commit();
+ session.close();
+ }
+
+ public void testManyToManyFilter() throws Throwable {
+ Session session = openSession();
+ Transaction txn = session.beginTransaction();
+
+ Human human = new Human();
+ human.setName( new Name() );
+ human.getName().setFirst( "Steve" );
+ human.getName().setInitial( 'L' );
+ human.getName().setLast( "Ebersole" );
+ session.save( human );
+
+ Human friend = new Human();
+ friend.setName( new Name() );
+ friend.getName().setFirst( "John" );
+ friend.getName().setInitial( 'Q' );
+ friend.getName().setLast( "Doe" );
+ friend.setBodyWeight( 11.0f );
+ session.save( friend );
+
+ human.setFriends( new ArrayList() );
+ friend.setFriends( new ArrayList() );
+ human.getFriends().add( friend );
+ friend.getFriends().add( human );
+
+ session.flush();
+
+ assertEquals( session.createFilter( human.getFriends(), "" ).list().size(), 1 );
+ assertEquals( session.createFilter( human.getFriends(), "where this.bodyWeight > ?" ).setFloat( 0, 10f ).list().size(), 1 );
+ assertEquals( session.createFilter( human.getFriends(), "where this.bodyWeight < ?" ).setFloat( 0, 10f ).list().size(), 0 );
+
+ session.delete(human);
+ session.delete(friend);
+
+ txn.commit();
+ session.close();
+ }
+
+ public void testSelectExpressions() {
+ createTestBaseData();
+ Session session = openSession();
+ Transaction txn = session.beginTransaction();
+ Human h = new Human();
+ h.setName( new Name("Gavin", 'A', "King") );
+ h.setNickName("Oney");
+ h.setBodyWeight(1.0f);
+ session.persist(h);
+ List results = session.createQuery("select 'found', lower(h.name.first) from Human h where lower(h.name.first) = 'gavin'").list();
+ results = session.createQuery("select 'found', lower(h.name.first) from Human h where concat(h.name.first, ' ', h.name.initial, ' ', h.name.last) = 'Gavin A King'").list();
+ results = session.createQuery("select 'found', lower(h.name.first) from Human h where h.name.first||' '||h.name.initial||' '||h.name.last = 'Gavin A King'").list();
+ results = session.createQuery("select a.bodyWeight + m.bodyWeight from Animal a join a.mother m").list();
+ results = session.createQuery("select 2.0 * (a.bodyWeight + m.bodyWeight) from Animal a join a.mother m").list();
+ results = session.createQuery("select sum(a.bodyWeight + m.bodyWeight) from Animal a join a.mother m").list();
+ results = session.createQuery("select sum(a.mother.bodyWeight * 2.0) from Animal a").list();
+ results = session.createQuery("select concat(h.name.first, ' ', h.name.initial, ' ', h.name.last) from Human h").list();
+ results = session.createQuery("select h.name.first||' '||h.name.initial||' '||h.name.last from Human h").list();
+ results = session.createQuery("select nickName from Human").list();
+ results = session.createQuery("select lower(nickName) from Human").list();
+ results = session.createQuery("select abs(bodyWeight*-1) from Human").list();
+ results = session.createQuery("select upper(h.name.first||' ('||h.nickName||')') from Human h").list();
+ results = session.createQuery("select abs(a.bodyWeight-:param) from Animal a").setParameter("param", new Float(2.0)).list();
+ results = session.createQuery("select abs(:param - a.bodyWeight) from Animal a").setParameter("param", new Float(2.0)).list();
+ results = session.createQuery("select lower(upper('foo')) from Animal").list();
+ results = session.createQuery("select lower(upper('foo') || upper('bar')) from Animal").list();
+ results = session.createQuery("select sum(abs(bodyWeight - 1.0) * abs(length('ffobar')-3)) from Animal").list();
+ session.delete(h);
+ txn.commit();
+ session.close();
+ destroyTestBaseData();
+ }
+
+ private void createTestBaseData() {
+ Session session = openSession();
+ Transaction txn = session.beginTransaction();
+
+ Mammal m1 = new Mammal();
+ m1.setBodyWeight( 11f );
+ m1.setDescription( "Mammal #1" );
+
+ session.save( m1 );
+
+ Mammal m2 = new Mammal();
+ m2.setBodyWeight( 9f );
+ m2.setDescription( "Mammal #2" );
+ m2.setMother( m1 );
+
+ session.save( m2 );
+
+ txn.commit();
+ session.close();
+
+ createdAnimalIds.add( m1.getId() );
+ createdAnimalIds.add( m2.getId() );
+ }
+
+ private void destroyTestBaseData() {
+ Session session = openSession();
+ Transaction txn = session.beginTransaction();
+
+ for ( int i = 0; i < createdAnimalIds.size(); i++ ) {
+ Animal animal = ( Animal ) session.load( Animal.class, ( Long ) createdAnimalIds.get( i ) );
+ session.delete( animal );
+ }
+
+ txn.commit();
+ session.close();
+ }
+
+ public void testImplicitJoin() throws Exception {
+ Session session = openSession();
+ Transaction t = session.beginTransaction();
+ Animal a = new Animal();
+ a.setBodyWeight(0.5f);
+ a.setBodyWeight(1.5f);
+ Animal b = new Animal();
+ Animal mother = new Animal();
+ mother.setBodyWeight(10.0f);
+ mother.addOffspring(a);
+ mother.addOffspring(b);
+ session.persist(a);
+ session.persist(b);
+ session.persist(mother);
+ List list = session.createQuery("from Animal a where a.mother.bodyWeight < 2.0 or a.mother.bodyWeight > 9.0").list();
+ assertEquals( list.size(), 2 );
+ list = session.createQuery("from Animal a where a.mother.bodyWeight > 2.0 and a.mother.bodyWeight > 9.0").list();
+ assertEquals( list.size(), 2 );
+ session.delete(b);
+ session.delete(a);
+ session.delete(mother);
+ t.commit();
+ session.close();
+ }
+
+ public void testFromOnly() throws Exception {
+
+ createTestBaseData();
+
+ Session session = openSession();
+
+ List results = session.createQuery( "from Animal" ).list();
+ assertEquals( "Incorrect result size", 2, results.size() );
+ assertTrue( "Incorrect result return type", results.get( 0 ) instanceof Animal );
+
+ session.close();
+
+ destroyTestBaseData();
+ }
+
+ public void testSimpleSelect() throws Exception {
+
+ createTestBaseData();
+
+ Session session = openSession();
+
+ List results = session.createQuery( "select a from Animal as a" ).list();
+ assertEquals( "Incorrect result size", 2, results.size() );
+ assertTrue( "Incorrect result return type", results.get( 0 ) instanceof Animal );
+
+ session.close();
+
+ destroyTestBaseData();
+ }
+
+ public void testEntityPropertySelect() throws Exception {
+
+ createTestBaseData();
+
+ Session session = openSession();
+
+ List results = session.createQuery( "select a.mother from Animal as a" ).list();
+// assertEquals("Incorrect result size", 2, results.size());
+ assertTrue( "Incorrect result return type", results.get( 0 ) instanceof Animal );
+
+ session.close();
+
+ destroyTestBaseData();
+ }
+
+ public void testWhere() throws Exception {
+
+ createTestBaseData();
+
+ Session session = openSession();
+ List results = null;
+
+ results = session.createQuery( "from Animal an where an.bodyWeight > 10" ).list();
+ assertEquals( "Incorrect result size", 1, results.size() );
+
+ results = session.createQuery( "from Animal an where not an.bodyWeight > 10" ).list();
+ assertEquals( "Incorrect result size", 1, results.size() );
+
+ results = session.createQuery( "from Animal an where an.bodyWeight between 0 and 10" ).list();
+ assertEquals( "Incorrect result size", 1, results.size() );
+
+ results = session.createQuery( "from Animal an where an.bodyWeight not between 0 and 10" ).list();
+ assertEquals( "Incorrect result size", 1, results.size() );
+
+ results = session.createQuery( "from Animal an where sqrt(an.bodyWeight)/2 > 10" ).list();
+ assertEquals( "Incorrect result size", 0, results.size() );
+
+ results = session.createQuery( "from Animal an where (an.bodyWeight > 10 and an.bodyWeight < 100) or an.bodyWeight is null" ).list();
+ assertEquals( "Incorrect result size", 1, results.size() );
+
+ session.close();
+
+ destroyTestBaseData();
+ }
+
+ public void testEntityFetching() throws Exception {
+
+ createTestBaseData();
+
+ Session session = openSession();
+
+ List results = session.createQuery( "from Animal an join fetch an.mother" ).list();
+ assertEquals( "Incorrect result size", 1, results.size() );
+ assertTrue( "Incorrect result return type", results.get( 0 ) instanceof Animal );
+ Animal mother = ( ( Animal ) results.get( 0 ) ).getMother();
+ assertTrue( "fetch uninitialized", mother != null && Hibernate.isInitialized( mother ) );
+
+ results = session.createQuery( "select an from Animal an join fetch an.mother" ).list();
+ assertEquals( "Incorrect result size", 1, results.size() );
+ assertTrue( "Incorrect result return type", results.get( 0 ) instanceof Animal );
+ mother = ( ( Animal ) results.get( 0 ) ).getMother();
+ assertTrue( "fetch uninitialized", mother != null && Hibernate.isInitialized( mother ) );
+
+ session.close();
+
+ destroyTestBaseData();
+ }
+
+ public void testCollectionFetching() throws Exception {
+
+ createTestBaseData();
+
+ Session session = openSession();
+ List results = session.createQuery( "from Animal an join fetch an.offspring" ).list();
+ assertEquals( "Incorrect result size", 1, results.size() );
+ assertTrue( "Incorrect result return type", results.get( 0 ) instanceof Animal );
+ Collection os = ( ( Animal ) results.get( 0 ) ).getOffspring();
+ assertTrue( "fetch uninitialized", os != null && Hibernate.isInitialized( os ) && os.size() == 1 );
+
+ results = session.createQuery( "select an from Animal an join fetch an.offspring" ).list();
+ assertEquals( "Incorrect result size", 1, results.size() );
+ assertTrue( "Incorrect result return type", results.get( 0 ) instanceof Animal );
+ os = ( ( Animal ) results.get( 0 ) ).getOffspring();
+ assertTrue( "fetch uninitialized", os != null && Hibernate.isInitialized( os ) && os.size() == 1 );
+
+ session.close();
+
+ destroyTestBaseData();
+ }
+
+ public void testProjectionQueries() throws Exception {
+
+ createTestBaseData();
+
+ Session session = openSession();
+
+ List results = session.createQuery( "select an.mother.id, max(an.bodyWeight) from Animal an group by an.mother.id" ).list();
+ // mysql returns nulls in this group by
+ assertEquals( "Incorrect result size", 2, results.size() );
+ assertTrue( "Incorrect return type", results.get( 0 ) instanceof Object[] );
+ assertEquals( "Incorrect return dimensions", 2, ( ( Object[] ) results.get( 0 ) ).length );
+
+ session.close();
+
+ destroyTestBaseData();
+
+ }
+
+ public void testStandardFunctions() throws Exception {
+ Session session = openSession();
+ Transaction t = session.beginTransaction();
+ Product p = new Product();
+ p.setDescription("a product");
+ p.setPrice( new BigDecimal(1.0) );
+ p.setProductId("abc123");
+ session.persist(p);
+ Object[] result = (Object[]) session
+ .createQuery("select current_time(), current_date(), current_timestamp() from Product")
+ .uniqueResult();
+ assertTrue( result[0] instanceof Time );
+ assertTrue( result[1] instanceof Date );
+ assertTrue( result[2] instanceof Timestamp );
+ assertNotNull( result[0] );
+ assertNotNull( result[1] );
+ assertNotNull( result[2] );
+ session.delete(p);
+ t.commit();
+ session.close();
+
+ }
+
+ public void testDynamicInstantiationQueries() throws Exception {
+
+ createTestBaseData();
+
+ Session session = openSession();
+
+ List results = session.createQuery( "select new Animal(an.description, an.bodyWeight) from Animal an" ).list();
+ assertEquals( "Incorrect result size", 2, results.size() );
+ assertClassAssignability( results.get( 0 ).getClass(), Animal.class );
+
+ Iterator iter = session.createQuery( "select new Animal(an.description, an.bodyWeight) from Animal an" ).iterate();
+ assertTrue( "Incorrect result size", iter.hasNext() );
+ assertTrue( "Incorrect return type", iter.next() instanceof Animal );
+
+ results = session.createQuery( "select new list(an.description, an.bodyWeight) from Animal an" ).list();
+ assertEquals( "Incorrect result size", 2, results.size() );
+ assertTrue( "Incorrect return type", results.get( 0 ) instanceof List );
+ assertEquals( "Incorrect return type", ( (List) results.get( 0 ) ).size(), 2 );
+
+ results = session.createQuery( "select new list(an.description, an.bodyWeight) from Animal an" ).list();
+ assertEquals( "Incorrect result size", 2, results.size() );
+ assertTrue( "Incorrect return type", results.get( 0 ) instanceof List );
+ assertEquals( "Incorrect return type", ( (List) results.get( 0 ) ).size(), 2 );
+
+ iter = session.createQuery( "select new list(an.description, an.bodyWeight) from Animal an" ).iterate();
+ assertTrue( "Incorrect result size", iter.hasNext() );
+ Object obj = iter.next();
+ assertTrue( "Incorrect return type", obj instanceof List );
+ assertEquals( "Incorrect return type", ( (List) obj ).size(), 2 );
+
+ iter = ((org.hibernate.classic.Session)session).iterate( "select new list(an.description, an.bodyWeight) from Animal an" );
+ assertTrue( "Incorrect result size", iter.hasNext() );
+ obj = iter.next();
+ assertTrue( "Incorrect return type", obj instanceof List );
+ assertEquals( "Incorrect return type", ( (List) obj ).size(), 2 );
+
+ results = session.createQuery( "select new map(an.description, an.bodyWeight) from Animal an" ).list();
+ assertEquals( "Incorrect result size", 2, results.size() );
+ assertTrue( "Incorrect return type", results.get( 0 ) instanceof Map );
+ assertEquals( "Incorrect return type", ( (Map) results.get( 0 ) ).size(), 2 );
+ assertTrue( ( (Map) results.get( 0 ) ).containsKey("0") );
+ assertTrue( ( (Map) results.get( 0 ) ).containsKey("1") );
+
+ results = session.createQuery( "select new map(an.description as descr, an.bodyWeight as bw) from Animal an" ).list();
+ assertEquals( "Incorrect result size", 2, results.size() );
+ assertTrue( "Incorrect return type", results.get( 0 ) instanceof Map );
+ assertEquals( "Incorrect return type", ( (Map) results.get( 0 ) ).size(), 2 );
+ assertTrue( ( (Map) results.get( 0 ) ).containsKey("descr") );
+ assertTrue( ( (Map) results.get( 0 ) ).containsKey("bw") );
+
+ iter = session.createQuery( "select new map(an.description, an.bodyWeight) from Animal an" ).iterate();
+ assertTrue( "Incorrect result size", iter.hasNext() );
+ obj = iter.next();
+ assertTrue( "Incorrect return type", obj instanceof Map );
+ assertEquals( "Incorrect return type", ( (Map) obj ).size(), 2 );
+
+ ScrollableResults sr = session.createQuery( "select new map(an.description, an.bodyWeight) from Animal an" ).scroll();
+ assertTrue( "Incorrect result size", sr.next() );
+ obj = sr.get(0);
+ assertTrue( "Incorrect return type", obj instanceof Map );
+ assertEquals( "Incorrect return type", ( (Map) obj ).size(), 2 );
+ sr.close();
+
+ sr = session.createQuery( "select new Animal(an.description, an.bodyWeight) from Animal an" ).scroll();
+ assertTrue( "Incorrect result size", sr.next() );
+ assertTrue( "Incorrect return type", sr.get(0) instanceof Animal );
+ sr.close();
+
+ // caching...
+ QueryStatistics stats = getSessions().getStatistics().getQueryStatistics( "select new Animal(an.description, an.bodyWeight) from Animal an" );
+ results = session.createQuery( "select new Animal(an.description, an.bodyWeight) from Animal an" )
+ .setCacheable( true )
+ .list();
+ assertEquals( "incorrect result size", 2, results.size() );
+ assertClassAssignability( Animal.class, results.get( 0 ).getClass() );
+ long initCacheHits = stats.getCacheHitCount();
+ results = session.createQuery( "select new Animal(an.description, an.bodyWeight) from Animal an" )
+ .setCacheable( true )
+ .list();
+ assertEquals( "dynamic intantiation query not served from cache", initCacheHits + 1, stats.getCacheHitCount() );
+ assertEquals( "incorrect result size", 2, results.size() );
+ assertClassAssignability( Animal.class, results.get( 0 ).getClass() );
+
+ session.close();
+
+ destroyTestBaseData();
+ }
+
+ public void testIllegalMixedTransformerQueries() {
+ Session session = openSession();
+
+ try {
+ getSelectNewQuery( session ).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();
+ fail("'select new' together with a resulttransformer should result in error!");
+ } catch(QueryException he) {
+ assertTrue(he.getMessage().indexOf("ResultTransformer")==0);
+ }
+
+ try {
+ getSelectNewQuery( session ).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).iterate();
+ fail("'select new' together with a resulttransformer should result in error!");
+ } catch(HibernateException he) {
+ assertTrue(he.getMessage().indexOf("ResultTransformer")==0);
+ }
+
+ try {
+ getSelectNewQuery( session ).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).scroll();
+ fail("'select new' together with a resulttransformer should result in error!");
+ } catch(HibernateException he) {
+ assertTrue(he.getMessage().indexOf("ResultTransformer")==0);
+ }
+
+ session.close();
+ }
+
+ private Query getSelectNewQuery(Session session) {
+ return session.createQuery( "select new Animal(an.description, an.bodyWeight) from Animal an" );
+ }
+ public void testResultTransformerScalarQueries() throws Exception {
+
+ createTestBaseData();
+
+ String query = "select an.description as description, an.bodyWeight as bodyWeight from Animal an order by bodyWeight desc";
+
+ Session session = openSession();
+
+ List results = session.createQuery( query )
+ .setResultTransformer(Transformers.aliasToBean(Animal.class)).list();
+ assertEquals( "Incorrect result size", results.size(), 2 );
+ assertTrue( "Incorrect return type", results.get(0) instanceof Animal );
+ Animal firstAnimal = (Animal) results.get(0);
+ Animal secondAnimal = (Animal) results.get(1);
+ assertEquals("Mammal #1", firstAnimal.getDescription());
+ assertEquals("Mammal #2", secondAnimal.getDescription());
+ assertFalse(session.contains(firstAnimal));
+ session.close();
+
+ session = openSession();
+
+ Iterator iter = session.createQuery( query )
+ .setResultTransformer(Transformers.aliasToBean(Animal.class)).iterate();
+ assertTrue( "Incorrect result size", iter.hasNext() );
+ assertTrue( "Incorrect return type", iter.next() instanceof Animal );
+
+ session.close();
+
+ session = openSession();
+
+ ScrollableResults sr = session.createQuery( query )
+ .setResultTransformer(Transformers.aliasToBean(Animal.class)).scroll();
+ assertTrue( "Incorrect result size", sr.next() );
+ assertTrue( "Incorrect return type", sr.get(0) instanceof Animal );
+ assertFalse(session.contains(sr.get(0)));
+ sr.close();
+
+ session.close();
+
+ session = openSession();
+
+ results = session.createQuery( "select a from Animal a, Animal b order by a.id" )
+ .setResultTransformer(new DistinctRootEntityResultTransformer())
+ .list();
+ assertEquals( "Incorrect result size", 2, results.size());
+ assertTrue( "Incorrect return type", results.get(0) instanceof Animal );
+ firstAnimal = (Animal) results.get(0);
+ secondAnimal = (Animal) results.get(1);
+ assertEquals("Mammal #1", firstAnimal.getDescription());
+ assertEquals("Mammal #2", secondAnimal.getDescription());
+
+ session.close();
+
+ destroyTestBaseData();
+ }
+
+ public void testResultTransformerEntityQueries() throws Exception {
+
+ createTestBaseData();
+
+ String query = "select an as an from Animal an order by bodyWeight desc";
+
+ Session session = openSession();
+
+ List results = session.createQuery( query )
+ .setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();
+ assertEquals( "Incorrect result size", results.size(), 2 );
+ assertTrue( "Incorrect return type", results.get(0) instanceof Map );
+ Map map = ((Map) results.get(0));
+ assertEquals(1, map.size());
+ Animal firstAnimal = (Animal) map.get("an");
+ map = ((Map) results.get(1));
+ Animal secondAnimal = (Animal) map.get("an");
+ assertEquals("Mammal #1", firstAnimal.getDescription());
+ assertEquals("Mammal #2", secondAnimal.getDescription());
+ assertTrue(session.contains(firstAnimal));
+ assertSame(firstAnimal, session.get(Animal.class,firstAnimal.getId()));
+ session.close();
+
+ session = openSession();
+
+ Iterator iter = session.createQuery( query )
+ .setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).iterate();
+ assertTrue( "Incorrect result size", iter.hasNext() );
+ map = (Map) iter.next();
+ firstAnimal = (Animal) map.get("an");
+ assertEquals("Mammal #1", firstAnimal.getDescription());
+ assertTrue( "Incorrect result size", iter.hasNext() );
+
+ session.close();
+
+ session = openSession();
+
+ ScrollableResults sr = session.createQuery( query )
+ .setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).scroll();
+ assertTrue( "Incorrect result size", sr.next() );
+ assertTrue( "Incorrect return type", sr.get(0) instanceof Map );
+ assertFalse(session.contains(sr.get(0)));
+ sr.close();
+
+ session.close();
+
+ destroyTestBaseData();
+ }
+
+ public void testEJBQLFunctions() throws Exception {
+ Session session = openSession();
+
+ String hql = "from Animal a where a.description = concat('1', concat('2','3'), '4'||'5')||'0'";
+ session.createQuery(hql).list();
+
+ hql = "from Animal a where substring(a.description, 1, 3) = 'cat'";
+ session.createQuery(hql).list();
+
+ hql = "select substring(a.description, 1, 3) from Animal a";
+ session.createQuery(hql).list();
+
+ hql = "from Animal a where lower(a.description) = 'cat'";
+ session.createQuery(hql).list();
+
+ hql = "select lower(a.description) from Animal a";
+ session.createQuery(hql).list();
+
+ hql = "from Animal a where upper(a.description) = 'CAT'";
+ session.createQuery(hql).list();
+
+ hql = "select upper(a.description) from Animal a";
+ session.createQuery(hql).list();
+
+ hql = "from Animal a where length(a.description) = 5";
+ session.createQuery(hql).list();
+
+ hql = "select length(a.description) from Animal a";
+ session.createQuery(hql).list();
+
+ //note: postgres and db2 don't have a 3-arg form, it gets transformed to 2-args
+ hql = "from Animal a where locate('abc', a.description, 2) = 2";
+ session.createQuery(hql).list();
+
+ hql = "from Animal a where locate('abc', a.description) = 2";
+ session.createQuery(hql).list();
+
+ hql = "select locate('cat', a.description, 2) from Animal a";
+ session.createQuery(hql).list();
+
+ if ( !( getDialect() instanceof DB2Dialect ) ) {
+ hql = "from Animal a where trim(trailing '_' from a.description) = 'cat'";
+ session.createQuery(hql).list();
+
+ hql = "select trim(trailing '_' from a.description) from Animal a";
+ session.createQuery(hql).list();
+
+ hql = "from Animal a where trim(leading '_' from a.description) = 'cat'";
+ session.createQuery(hql).list();
+
+ hql = "from Animal a where trim(both from a.description) = 'cat'";
+ session.createQuery(hql).list();
+ }
+
+ if ( !(getDialect() instanceof HSQLDialect) ) { //HSQL doesn't like trim() without specification
+ hql = "from Animal a where trim(a.description) = 'cat'";
+ session.createQuery(hql).list();
+ }
+
+ hql = "from Animal a where abs(a.bodyWeight) = sqrt(a.bodyWeight)";
+ session.createQuery(hql).list();
+
+ hql = "from Animal a where mod(16, 4) = 4";
+ session.createQuery(hql).list();
+
+ hql = "from Animal a where bit_length(a.bodyWeight) = 24";
+ session.createQuery(hql).list();
+
+ hql = "select bit_length(a.bodyWeight) from Animal a";
+ session.createQuery(hql).list();
+
+ /*hql = "select object(a) from Animal a where CURRENT_DATE = :p1 or CURRENT_TIME = :p2 or CURRENT_TIMESTAMP = :p3";
+ session.createQuery(hql).list();*/
+
+ // todo the following is not supported
+ //hql = "select CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP from Animal a";
+ //parse(hql, true);
+ //System.out.println("sql: " + toSql(hql));
+
+ hql = "from Animal a where a.description like '%a%'";
+ session.createQuery(hql).list();
+
+ hql = "from Animal a where a.description not like '%a%'";
+ session.createQuery(hql).list();
+
+ hql = "from Animal a where a.description like 'x%ax%' escape 'x'";
+ session.createQuery(hql).list();
+
+ session.close();
+ }
+
+ public void testSubselectBetween() {
+ if ( supportsSubselectOnLeftSideIn() ) {
+ assertResultSize( "from Animal x where (select max(a.bodyWeight) from Animal a) in (1,2,3)", 0 );
+ assertResultSize( "from Animal x where (select max(a.bodyWeight) from Animal a) between 0 and 100", 0 );
+ assertResultSize( "from Animal x where (select max(a.description) from Animal a) like 'big%'", 0 );
+ assertResultSize( "from Animal x where (select max(a.bodyWeight) from Animal a) is not null", 0 );
+ }
+ assertResultSize( "from Animal x where exists (select max(a.bodyWeight) from Animal a)", 0 );
+ }
+
+ private void assertResultSize(String hql, int size) {
+ Session session = openSession();
+ Transaction txn = session.beginTransaction();
+ assertEquals( size, session.createQuery(hql).list().size() );
+ txn.commit();
+ session.close();
+ }
+
+ private interface QueryPreparer {
+ public void prepare(Query query);
+ }
+
+ private static final QueryPreparer DEFAULT_PREPARER = new QueryPreparer() {
+ public void prepare(Query query) {
+ }
+ };
+
+ private class SyntaxChecker {
+ private final String hql;
+ private final QueryPreparer preparer;
+
+ public SyntaxChecker(String hql) {
+ this( hql, DEFAULT_PREPARER );
+ }
+
+ public SyntaxChecker(String hql, QueryPreparer preparer) {
+ this.hql = hql;
+ this.preparer = preparer;
+ }
+
+ public void checkAll() {
+ checkList();
+ checkIterate();
+ checkScroll();
+ }
+
+ public SyntaxChecker checkList() {
+ Session s = openSession();
+ s.beginTransaction();
+ Query query = s.createQuery( hql );
+ preparer.prepare( query );
+ query.list();
+ s.getTransaction().commit();
+ s.close();
+ return this;
+ }
+
+ public SyntaxChecker checkScroll() {
+ Session s = openSession();
+ s.beginTransaction();
+ Query query = s.createQuery( hql );
+ preparer.prepare( query );
+ query.scroll();
+ s.getTransaction().commit();
+ s.close();
+ return this;
+ }
+
+ public SyntaxChecker checkIterate() {
+ Session s = openSession();
+ s.beginTransaction();
+ Query query = s.createQuery( hql );
+ preparer.prepare( query );
+ query.iterate();
+ s.getTransaction().commit();
+ s.close();
+ return this;
+ }
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/ASTQueryTranslatorTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/ASTQueryTranslatorTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/ASTQueryTranslatorTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,176 @@
+// $Id: ASTQueryTranslatorTest.java 8889 2005-12-20 17:35:54Z steveebersole $
+package org.hibernate.test.hql;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * Tests cases where the AST based QueryTranslator does not generate identical SQL.
+ *
+ * @author josh Dec 6, 2004 9:07:58 AM
+ */
+public class ASTQueryTranslatorTest extends QueryTranslatorTestCase {
+
+ public ASTQueryTranslatorTest(String x) {
+ super( x );
+ }
+
+ public static Test suite() {
+ return new TestSuite( ASTQueryTranslatorTest.class );
+ }
+
+ protected boolean dropAfterFailure() {
+ return false;
+ }
+
+ // ##### TESTS THAT DON'T PASS BECAUSE THEY GENERATE DIFFERENT, POSSIBLY VALID SQL #####
+
+ public void testSelectManyToOne() {
+ assertTranslation("select distinct a.zoo from Animal a where a.zoo is not null");
+ assertTranslation("select a.zoo from Animal a");
+ }
+
+ public void testSelectExpression() {
+ //old qt cant handle select-clause expressions
+ assertTranslation("select a.bodyWeight + m.bodyWeight from Animal a join a.mother m");
+ }
+
+ public void testFetchProperties() {
+ //not implemented in old qt
+ assertTranslation("from Animal a fetch all properties join a.offspring o fetch all properties");
+ }
+
+ public void testOldSyntax() {
+ //generates ANSI join instead of theta join
+ assertTranslation("from a in class Animal, o in elements(a.offspring), h in class Human");
+ }
+
+ public void testImplicitJoinInsideOutsideSubselect() {
+ // new qt re-uses the joins defined by 's.other.count' path when referenced in the subquery,
+ // whereas the old qt reuses only the "root table" (not the already defined join to 'other'):
+ // OLD SQL :
+ // select simple0_.id_ as col_0_0_
+ // from Simple simple0_,
+ // Simple simple1_
+ // where (simple1_.count_>0
+ // and simple0_.other=simple1_.id_)
+ // and (simple0_.id_=some(
+ // select simple2_.id_
+ // from Simple simple2_,
+ // Simple simple3_,
+ // Simple simple4_
+ // where (simple3_.count_=simple4_.count_
+ // and simple2_.other=simple3_.id_
+ // and simple0_.other=simple4_.id_)
+ // ))
+ // NEW SQL :
+ // select simple0_.id_ as col_0_0_
+ // from Simple simple0_,
+ // Simple simple1_
+ // where (simple1_.count_>0
+ // and simple0_.id_=some(
+ // select simple2_.id_
+ // from Simple simple2_,
+ // Simple simple3_
+ // where (simple3_.count_=simple1_.count_
+ // and simple2_.other=simple3_.id_)
+ // )
+ // and simple0_.other=simple1_.id_)
+ assertTranslation( "from Simple s where s = some( from Simple sim where sim.other.count=s.other.count ) and s.other.count > 0" );
+ assertTranslation( "from Simple s where s.other.count > 0 and s = some( from Simple sim where sim.other.count=s.other.count )" );
+ }
+
+ public void testNakedPropertyRef() {
+ // this is needed for ejb3 selects and bulk statements
+ // Note: these all fail because the old parser did not have this
+ // feature, it just "passes the tokens through" to the SQL.
+ assertTranslation( "from Animal where bodyWeight = bodyWeight" );
+ assertTranslation( "select bodyWeight from Animal" );
+ assertTranslation( "select max(bodyWeight) from Animal" );
+ }
+
+ public void testNakedComponentPropertyRef() {
+ // this is needed for ejb3 selects and bulk statements
+ // Note: these all fail because the old parser did not have this
+ // feature, it just "passes the tokens through" to the SQL.
+ assertTranslation( "from Human where name.first = 'Gavin'" );
+ assertTranslation( "select name from Human" );
+ assertTranslation( "select upper(h.name.first) from Human as h" );
+ assertTranslation( "select upper(name.first) from Human" );
+ }
+
+ public void testNakedMapIndex() throws Exception {
+ assertTranslation( "from Zoo where mammals['dog'].description like '%black%'" );
+ }
+
+ public void testNakedImplicitJoins() {
+ assertTranslation( "from Animal where mother.father = ?" );
+ }
+
+ public void testDuplicateImplicitJoinInWhere() {
+ //new qt has more organized joins
+ assertTranslation("from Human h where h.mother.bodyWeight>10 and h.mother.bodyWeight<10");
+ }
+
+ public void testWhereExpressions() {
+ assertTranslation("from User u where u.userName='gavin' and u.human.name.first='Gavin'");
+ //new qt has more organized joins
+ assertTranslation("from User u where u.human.name.last='King' and u.human.name.first='Gavin'");
+ assertTranslation("from Bar bar where bar.baz.name='josh'");
+ assertTranslation("from Bar bar where bar.baz.name='josh' and not bar.baz.name='gavin'");
+ }
+
+ public void testImplicitJoinInSelect() {
+ //slightly diff select clause, both correct
+ assertTranslation("select foo.long, foo.foo from Foo foo");
+ }
+
+ public void testSelectStandardFunctions() throws Exception {
+ //old parser throws an exception
+ assertTranslation( "select current_date(), current_time(), current_timestamp() from Animal" );
+ }
+
+ public void testSelectClauseImplicitJoin() throws Exception {
+ //the old query translator has a bug which results in the wrong return type!
+ assertTranslation( "select d.owner.mother from Dog d" );
+ }
+
+ public void testComplexWhereExpression() throws Exception {
+ // classic QT generates lots of extra parens and some extra theta joins.
+ assertTranslation( "select distinct s from Simple s\n" +
+ "where ( ( s.other.count + 3 ) = (15*2)/2 and s.count = 69) or ( ( s.other.count + 2 ) / 7 ) = 2" );
+ }
+
+ public void testDuplicateImplicitJoin() throws Exception {
+ // old qt generates an extra theta join
+ assertTranslation( "from Animal an where an.mother.bodyWeight > 10 and an.mother.bodyWeight < 20" );
+ }
+
+ public void testKeywordClassNameAndAlias() throws Exception {
+ // The old QT throws an exception, the new one doesn't, which is better
+ assertTranslation( "from Order order" );
+ }
+
+ public void testComponent3() throws Exception {
+ // The old translator generates duplicate inner joins *and* duplicate theta join clauses in the where statement in this case.
+ assertTranslation( "from Dog dog where dog.owner.name.first = 'Gavin' and dog.owner.name.last='King' and dog.owner.bodyWeight<70" );
+ }
+
+ public void testUncorrelatedSubselectWithJoin() throws Exception {
+ // The old translator generates unnecessary inner joins for the Animal subclass of zoo.mammals
+ // The new one is working fine now!
+ assertTranslation( "from Animal a where a in (select mam from Zoo zoo join zoo.mammals mam)" );
+ }
+
+ public void testFetch() throws Exception {
+ //SQL is correct, new qt is not throwing an exception when it should be (minor issue)
+ assertTranslation("from Customer cust left join fetch cust.billingAddress where cust.customerId='abc123'");
+ }
+
+ public void testImplicitJoin() throws Exception {
+ //old qt generates an exception, the new one doesnt
+ //this is actually invalid HQL, an implicit join on a many-valued association
+ assertTranslation( "from Animal an where an.offspring.mother.bodyWeight > 10" );
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Address.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Address.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Address.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,55 @@
+// $Id: Address.java 7996 2005-08-22 14:49:57Z steveebersole $
+package org.hibernate.test.hql;
+
+/**
+ * Implementation of Address.
+ *
+ * @author Steve Ebersole
+ */
+public class Address {
+ private String street;
+ private String city;
+ private String postalCode;
+ private String country;
+ private StateProvince stateProvince;
+
+ public String getStreet() {
+ return street;
+ }
+
+ public void setStreet(String street) {
+ this.street = street;
+ }
+
+ public String getCity() {
+ return city;
+ }
+
+ public void setCity(String city) {
+ this.city = city;
+ }
+
+ public String getPostalCode() {
+ return postalCode;
+ }
+
+ public void setPostalCode(String postalCode) {
+ this.postalCode = postalCode;
+ }
+
+ public String getCountry() {
+ return country;
+ }
+
+ public void setCountry(String country) {
+ this.country = country;
+ }
+
+ public StateProvince getStateProvince() {
+ return stateProvince;
+ }
+
+ public void setStateProvince(StateProvince stateProvince) {
+ this.stateProvince = stateProvince;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Animal.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Animal.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Animal.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,150 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping SYSTEM "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
+<hibernate-mapping
+ package="org.hibernate.test.hql"
+ default-access="field">
+
+ <class name="Animal">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="description"/>
+ <property name="bodyWeight" column="body_weight"/>
+ <many-to-one name="mother" column="mother_id"/>
+ <many-to-one name="father" column="father_id"/>
+ <many-to-one name="zoo" column="zoo_id"/>
+ <property name="serialNumber"/>
+ <set name="offspring" order-by="father_id">
+ <key column="mother_id"/>
+ <one-to-many class="Animal"/>
+ </set>
+ <joined-subclass name="Reptile">
+ <key column="animal"/>
+ <property name="bodyTemperature"/>
+ <joined-subclass name="Lizard">
+ <key column="reptile"/>
+ </joined-subclass>
+ </joined-subclass>
+ <joined-subclass name="Mammal">
+ <key column="animal"/>
+ <property name="pregnant"/>
+ <property name="birthdate" type="date"/>
+ <joined-subclass name="DomesticAnimal">
+ <key column="mammal"/>
+ <many-to-one name="owner"/>
+ <joined-subclass name="Cat">
+ <key column="mammal"/>
+ </joined-subclass>
+ <joined-subclass name="Dog">
+ <key column="mammal"/>
+ </joined-subclass>
+ </joined-subclass>
+ <joined-subclass name="Human">
+ <key column="mammal"/>
+ <component name="name">
+ <property name="first" column="name_first"/>
+ <property name="initial" column="name_initial"/>
+ <property name="last" column="name_last"/>
+ </component>
+ <property name="nickName"/>
+ <property name="height"/>
+
+ <property name="intValue"/>
+ <property name="floatValue"/>
+ <property name="bigDecimalValue"/>
+ <property name="bigIntegerValue"/>
+
+ <bag name="friends">
+ <key column="human1"/>
+ <many-to-many column="human2" class="Human"/>
+ </bag>
+ <map name="family">
+ <key column="human1"/>
+ <map-key column="relationship" type="string"/>
+ <many-to-many column="human2" class="Human"/>
+ </map>
+ <bag name="pets" inverse="true">
+ <key column="owner"/>
+ <one-to-many class="DomesticAnimal"/>
+ </bag>
+ <set name="nickNames" lazy="false" table="human_nick_names" sort="natural">
+ <key column="human"/>
+ <element column="nick_name" type="string" not-null="true"/>
+ </set>
+ <map name="addresses" table="addresses">
+ <key column="human"/>
+ <map-key type="string" column="type"/>
+ <composite-element class="Address">
+ <property name="street"/>
+ <property name="city"/>
+ <property name="postalCode"/>
+ <property name="country"/>
+ <many-to-one name="stateProvince" column="state_prov_id" class="StateProvince"/>
+ </composite-element>
+ </map>
+ </joined-subclass>
+ </joined-subclass>
+ </class>
+
+ <class name="User" table="`User`">
+ <id name="id">
+ <generator class="foreign">
+ <param name="property">human</param>
+ </generator>
+ </id>
+ <property name="userName"/>
+ <one-to-one name="human" constrained="true"/>
+ <list name="permissions">
+ <key column="userId"/>
+ <list-index column="permissionId"/>
+ <element type="string" column="permissionName"/>
+ </list>
+ </class>
+
+ <class name="Zoo" discriminator-value="Z">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <discriminator column="zooType" type="character"/>
+ <property name="name" type="string"/>
+ <property name="classification" type="org.hibernate.test.hql.ClassificationType"/>
+ <map name="mammals">
+ <key column="mammalZoo_id"/>
+ <index type="string" column="name"/>
+ <one-to-many class="Mammal"/>
+ </map>
+ <map name="animals" inverse="true">
+ <key column="zoo_id"/>
+ <index type="string" column="serialNumber"/>
+ <one-to-many class="Animal"/>
+ </map>
+ <component name="address" class="Address">
+ <property name="street"/>
+ <property name="city"/>
+ <property name="postalCode"/>
+ <property name="country"/>
+ <many-to-one name="stateProvince" column="state_prov_id" class="StateProvince"/>
+ </component>
+ <subclass name="PettingZoo" discriminator-value="P"/>
+ </class>
+
+ <class name="StateProvince">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="name"/>
+ <property name="isoCode"/>
+ </class>
+
+ <class name="Joiner">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="name"/>
+ <join table="JOINED">
+ <key column="ID"/>
+ <property name="joinedName"/>
+ </join>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Animal.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Animal.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Animal.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,99 @@
+//$Id: Animal.java 7445 2005-07-10 16:51:17Z oneovthafew $
+package org.hibernate.test.hql;
+
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * @author Gavin King
+ */
+public class Animal {
+ private Long id;
+ private float bodyWeight;
+ private Set offspring;
+ private Animal mother;
+ private Animal father;
+ private String description;
+ private Zoo zoo;
+ private String serialNumber;
+
+ public Animal() {
+ }
+
+ public Animal(String description, float bodyWeight) {
+ this.description = description;
+ this.bodyWeight = bodyWeight;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public float getBodyWeight() {
+ return bodyWeight;
+ }
+
+ public void setBodyWeight(float bodyWeight) {
+ this.bodyWeight = bodyWeight;
+ }
+
+ public Set getOffspring() {
+ return offspring;
+ }
+
+ public void addOffspring(Animal offspring) {
+ if ( this.offspring == null ) {
+ this.offspring = new HashSet();
+ }
+
+ this.offspring.add( offspring );
+ }
+
+ public void setOffspring(Set offspring) {
+ this.offspring = offspring;
+ }
+
+ public Animal getMother() {
+ return mother;
+ }
+
+ public void setMother(Animal mother) {
+ this.mother = mother;
+ }
+
+ public Animal getFather() {
+ return father;
+ }
+
+ public void setFather(Animal father) {
+ this.father = father;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public Zoo getZoo() {
+ return zoo;
+ }
+
+ public void setZoo(Zoo zoo) {
+ this.zoo = zoo;
+ }
+
+ public String getSerialNumber() {
+ return serialNumber;
+ }
+
+ public void setSerialNumber(String serialNumber) {
+ this.serialNumber = serialNumber;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/BooleanLiteralEntity.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/BooleanLiteralEntity.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/BooleanLiteralEntity.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,15 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping SYSTEM "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
+
+<hibernate-mapping package="org.hibernate.test.hql" default-access="field">
+
+ <class name="BooleanLiteralEntity">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="yesNoBoolean" column="Y_N_BOOL" type="yes_no"/>
+ <property name="trueFalseBoolean" column="T_F_BOOL" type="true_false"/>
+ <property name="zeroOneBoolean" column="NUM_BOOL" type="boolean"/>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/BooleanLiteralEntity.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/BooleanLiteralEntity.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/BooleanLiteralEntity.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,45 @@
+package org.hibernate.test.hql;
+
+/**
+ * todo: describe BooleanLiteralEntity
+ *
+ * @author Steve Ebersole
+ */
+public class BooleanLiteralEntity {
+ private Long id;
+ private boolean yesNoBoolean;
+ private boolean trueFalseBoolean;
+ private boolean zeroOneBoolean;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public boolean isYesNoBoolean() {
+ return yesNoBoolean;
+ }
+
+ public void setYesNoBoolean(boolean yesNoBoolean) {
+ this.yesNoBoolean = yesNoBoolean;
+ }
+
+ public boolean isTrueFalseBoolean() {
+ return trueFalseBoolean;
+ }
+
+ public void setTrueFalseBoolean(boolean trueFalseBoolean) {
+ this.trueFalseBoolean = trueFalseBoolean;
+ }
+
+ public boolean isZeroOneBoolean() {
+ return zeroOneBoolean;
+ }
+
+ public void setZeroOneBoolean(boolean zeroOneBoolean) {
+ this.zeroOneBoolean = zeroOneBoolean;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/BulkManipulationTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/BulkManipulationTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/BulkManipulationTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1211 @@
+// $Id: BulkManipulationTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.hql;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.hibernate.QueryException;
+import org.hibernate.Transaction;
+import org.hibernate.classic.Session;
+import org.hibernate.dialect.MySQLDialect;
+import org.hibernate.hql.ast.HqlSqlWalker;
+import org.hibernate.id.IdentifierGenerator;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.persister.entity.EntityPersister;
+
+
+/**
+ * Tests execution of bulk UPDATE/DELETE statements through the new AST parser.
+ *
+ * @author Steve Ebersole
+ */
+public class BulkManipulationTest extends FunctionalTestCase {
+
+ public BulkManipulationTest(String name) {
+ super( name );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( BulkManipulationTest.class );
+ }
+
+ public String[] getMappings() {
+ return new String[] {
+ "hql/Animal.hbm.xml",
+ "hql/Vehicle.hbm.xml",
+ "hql/KeyManyToOneEntity.hbm.xml",
+ "hql/Versions.hbm.xml",
+ "hql/FooBarCopy.hbm.xml",
+ "legacy/Multi.hbm.xml",
+ "hql/EntityWithCrazyCompositeKey.hbm.xml",
+ "hql/SimpleEntityWithAssociation.hbm.xml",
+ "hql/BooleanLiteralEntity.hbm.xml"
+ };
+ }
+
+
+ // Non-exists ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public void testDeleteNonExistentEntity() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ try {
+ s.createQuery( "delete NonExistentEntity" ).executeUpdate();
+ fail( "no exception thrown" );
+ }
+ catch( QueryException e ) {
+ System.out.println( "Caught expected error type : " + e.getMessage() );
+ }
+
+ t.commit();
+ s.close();
+ }
+
+ public void testUpdateNonExistentEntity() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ try {
+ s.createQuery( "update NonExistentEntity e set e.someProp = ?" ).executeUpdate();
+ fail( "no exception thrown" );
+ }
+ catch( QueryException e ) {
+ System.out.println( "Caught expected error type : " + e.getMessage() );
+ }
+
+ t.commit();
+ s.close();
+ }
+
+ public void testTempTableGenerationIsolation() throws Throwable{
+ Session s = openSession();
+ s.beginTransaction();
+
+ Truck truck = new Truck();
+ truck.setVin( "123t" );
+ truck.setOwner( "Steve" );
+ s.save( truck );
+
+ // manually flush the session to ensure the insert happens
+ s.flush();
+
+ // now issue a bulk delete against Car which should force the temp table to be
+ // created. we need to test to ensure that this does not cause the transaction
+ // to be committed...
+ s.createQuery( "delete from Vehicle" ).executeUpdate();
+
+ s.getTransaction().rollback();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ List list = s.createQuery( "from Car" ).list();
+ assertEquals( "temp table gen caused premature commit", 0, list.size() );
+ s.createQuery( "delete from Car" ).executeUpdate();
+ s.getTransaction().rollback();
+ s.close();
+ }
+
+
+ // BOOLEAN HANDLING ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public void testBooleanHandling() {
+ TestData data = new TestData();
+ data.prepare();
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ // currently, we need the three different binds because they are different underlying types...
+ int count = s.createQuery( "update BooleanLiteralEntity set yesNoBoolean = :b1, trueFalseBoolean = :b2, zeroOneBoolean = :b3" )
+ .setBoolean( "b1", true )
+ .setBoolean( "b2", true )
+ .setBoolean( "b3", true )
+ .executeUpdate();
+ assertEquals( 1, count );
+ BooleanLiteralEntity entity = ( BooleanLiteralEntity ) s.createQuery( "from BooleanLiteralEntity" ).uniqueResult();
+ assertTrue( entity.isYesNoBoolean() );
+ assertTrue( entity.isTrueFalseBoolean() );
+ assertTrue( entity.isZeroOneBoolean() );
+ s.clear();
+
+ count = s.createQuery( "update BooleanLiteralEntity set yesNoBoolean = true, trueFalseBoolean = true, zeroOneBoolean = true" )
+ .executeUpdate();
+ assertEquals( 1, count );
+ entity = ( BooleanLiteralEntity ) s.createQuery( "from BooleanLiteralEntity" ).uniqueResult();
+ assertTrue( entity.isYesNoBoolean() );
+ assertTrue( entity.isTrueFalseBoolean() );
+ assertTrue( entity.isZeroOneBoolean() );
+
+ t.commit();
+ s.close();
+
+ data.cleanup();
+ }
+
+
+ // INSERTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public void testSimpleInsert() {
+ TestData data = new TestData();
+ data.prepare();
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ s.createQuery( "insert into Pickup (id, vin, owner) select id, vin, owner from Car" ).executeUpdate();
+
+ t.commit();
+ t = s.beginTransaction();
+
+ s.createQuery( "delete Vehicle" ).executeUpdate();
+
+ t.commit();
+ s.close();
+
+ data.cleanup();
+ }
+
+ public void testSimpleNativeSQLInsert() {
+ TestData data = new TestData();
+ data.prepare();
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ List l = s.createQuery("from Vehicle").list();
+ assertEquals(l.size(),4);
+
+ s.createSQLQuery( "insert into PICKUP (id, vin, owner) select id, vin, owner from Car" ).executeUpdate();
+
+ l = s.createQuery("from Vehicle").list();
+ assertEquals(l.size(),5);
+
+ t.commit();
+ t = s.beginTransaction();
+
+ s.createSQLQuery( "delete from TRUCK" ).executeUpdate();
+
+ l = s.createQuery("from Vehicle").list();
+ assertEquals(l.size(),4);
+
+ Car c = (Car) s.createQuery( "from Car where owner = 'Kirsten'" ).uniqueResult();
+ c.setOwner("NotKirsten");
+ assertEquals(0,s.getNamedQuery( "native-delete-car" ).setString( 0, "Kirsten" ).executeUpdate());
+ assertEquals(1,s.getNamedQuery( "native-delete-car" ).setString( 0, "NotKirsten" ).executeUpdate());
+
+
+ assertEquals(0,s.createSQLQuery( "delete from SUV where owner = :owner" ).setString( "owner", "NotThere" ).executeUpdate());
+ assertEquals(1,s.createSQLQuery( "delete from SUV where owner = :owner" ).setString( "owner", "Joe" ).executeUpdate());
+ s.createSQLQuery( "delete from PICKUP" ).executeUpdate();
+
+ l = s.createQuery("from Vehicle").list();
+ assertEquals(l.size(),0);
+
+
+ t.commit();
+ s.close();
+
+
+ data.cleanup();
+ }
+
+ public void testInsertWithManyToOne() {
+ TestData data = new TestData();
+ data.prepare();
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ s.createQuery( "insert into Animal (description, bodyWeight, mother) select description, bodyWeight, mother from Human" ).executeUpdate();
+
+ t.commit();
+ t = s.beginTransaction();
+
+ t.commit();
+ s.close();
+
+ data.cleanup();
+ }
+
+ public void testInsertWithMismatchedTypes() {
+ TestData data = new TestData();
+ data.prepare();
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ try {
+ s.createQuery( "insert into Pickup (owner, vin, id) select id, vin, owner from Car" ).executeUpdate();
+ fail( "mismatched types did not error" );
+ }
+ catch( QueryException e ) {
+ // expected result
+ }
+
+ t.commit();
+ t = s.beginTransaction();
+
+ s.createQuery( "delete Vehicle" ).executeUpdate();
+
+ t.commit();
+ s.close();
+
+ data.cleanup();
+ }
+
+ public void testInsertIntoSuperclassPropertiesFails() {
+ TestData data = new TestData();
+ data.prepare();
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ try {
+ s.createQuery( "insert into Human (id, bodyWeight) select id, bodyWeight from Lizard" ).executeUpdate();
+ fail( "superclass prop insertion did not error" );
+ }
+ catch( QueryException e ) {
+ // expected result
+ }
+
+ t.commit();
+ t = s.beginTransaction();
+
+ s.createQuery( "delete Animal where mother is not null" ).executeUpdate();
+ s.createQuery( "delete Animal where father is not null" ).executeUpdate();
+ s.createQuery( "delete Animal" ).executeUpdate();
+
+ t.commit();
+ s.close();
+
+ data.cleanup();
+ }
+
+ public void testInsertAcrossMappedJoinFails() {
+ TestData data = new TestData();
+ data.prepare();
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ try {
+ s.createQuery( "insert into Joiner (name, joinedName) select vin, owner from Car" ).executeUpdate();
+ fail( "mapped-join insertion did not error" );
+ }
+ catch( QueryException e ) {
+ // expected result
+ }
+
+ t.commit();
+ t = s.beginTransaction();
+
+ s.createQuery( "delete Joiner" ).executeUpdate();
+ s.createQuery( "delete Vehicle" ).executeUpdate();
+
+ t.commit();
+ s.close();
+
+ data.cleanup();
+ }
+
+ public void testInsertWithGeneratedId() {
+ // Make sure the env supports bulk inserts with generated ids...
+ EntityPersister persister = sfi().getEntityPersister( PettingZoo.class.getName() );
+ IdentifierGenerator generator = persister.getIdentifierGenerator();
+ if ( !HqlSqlWalker.supportsIdGenWithBulkInsertion( generator ) ) {
+ return;
+ }
+
+ // create a Zoo
+ Zoo zoo = new Zoo();
+ zoo.setName( "zoo" );
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.save( zoo );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ int count = s.createQuery( "insert into PettingZoo (name) select name from Zoo" ).executeUpdate();
+ t.commit();
+ s.close();
+
+ assertEquals( "unexpected insertion count", 1, count );
+
+ s = openSession();
+ t = s.beginTransaction();
+ PettingZoo pz = ( PettingZoo ) s.createQuery( "from PettingZoo" ).uniqueResult();
+ t.commit();
+ s.close();
+
+ assertEquals( zoo.getName(), pz.getName() );
+ assertTrue( zoo.getId() != pz.getId() );
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.createQuery( "delete Zoo" ).executeUpdate();
+ t.commit();
+ s.close();
+ }
+
+ public void testInsertWithGeneratedVersionAndId() {
+ // Make sure the env supports bulk inserts with generated ids...
+ EntityPersister persister = sfi().getEntityPersister( IntegerVersioned.class.getName() );
+ IdentifierGenerator generator = persister.getIdentifierGenerator();
+ if ( !HqlSqlWalker.supportsIdGenWithBulkInsertion( generator ) ) {
+ return;
+ }
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ IntegerVersioned entity = new IntegerVersioned( "int-vers" );
+ s.save( entity );
+ s.createQuery( "select id, name, version from IntegerVersioned" ).list();
+ t.commit();
+ s.close();
+
+ Long initialId = entity.getId();
+ int initialVersion = entity.getVersion();
+
+ s = openSession();
+ t = s.beginTransaction();
+ int count = s.createQuery( "insert into IntegerVersioned ( name ) select name from IntegerVersioned" ).executeUpdate();
+ t.commit();
+ s.close();
+
+ assertEquals( "unexpected insertion count", 1, count );
+
+ s = openSession();
+ t = s.beginTransaction();
+ IntegerVersioned created = ( IntegerVersioned ) s.createQuery( "from IntegerVersioned where id <> :initialId" )
+ .setLong( "initialId", initialId.longValue() )
+ .uniqueResult();
+ t.commit();
+ s.close();
+
+ assertEquals( "version was not seeded", initialVersion, created.getVersion() );
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.createQuery( "delete IntegerVersioned" ).executeUpdate();
+ t.commit();
+ s.close();
+ }
+
+ public void testInsertWithGeneratedTimestampVersion() {
+ // Make sure the env supports bulk inserts with generated ids...
+ EntityPersister persister = sfi().getEntityPersister( TimestampVersioned.class.getName() );
+ IdentifierGenerator generator = persister.getIdentifierGenerator();
+ if ( !HqlSqlWalker.supportsIdGenWithBulkInsertion( generator ) ) {
+ return;
+ }
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ TimestampVersioned entity = new TimestampVersioned( "int-vers" );
+ s.save( entity );
+ s.createQuery( "select id, name, version from TimestampVersioned" ).list();
+ t.commit();
+ s.close();
+
+ Long initialId = entity.getId();
+ //Date initialVersion = entity.getVersion();
+
+ s = openSession();
+ t = s.beginTransaction();
+ int count = s.createQuery( "insert into TimestampVersioned ( name ) select name from TimestampVersioned" ).executeUpdate();
+ t.commit();
+ s.close();
+
+ assertEquals( "unexpected insertion count", 1, count );
+
+ s = openSession();
+ t = s.beginTransaction();
+ TimestampVersioned created = ( TimestampVersioned ) s.createQuery( "from TimestampVersioned where id <> :initialId" )
+ .setLong( "initialId", initialId.longValue() )
+ .uniqueResult();
+ t.commit();
+ s.close();
+
+ assertNotNull( created.getVersion() );
+ //assertEquals( "version was not seeded", initialVersion, created.getVersion() );
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.createQuery( "delete TimestampVersioned" ).executeUpdate();
+ t.commit();
+ s.close();
+ }
+
+ // UPDATES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public void testIncorrectSyntax() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ try {
+ s.createQuery( "update Human set Human.description = 'xyz' where Human.id = 1 and Human.description is null" );
+ fail( "expected failure" );
+ }
+ catch( QueryException expected ) {
+ // ignore : expected behavior
+ }
+ t.commit();
+ s.close();
+ }
+
+ public void testUpdateWithWhereExistsSubquery() {
+ // multi-table ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Human joe = new Human();
+ joe.setName( new Name( "Joe", 'Q', "Public" ) );
+ s.save( joe );
+ Human doll = new Human();
+ doll.setName( new Name( "Kyu", 'P', "Doll" ) );
+ doll.setFriends( new ArrayList() );
+ doll.getFriends().add( joe );
+ s.save( doll );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ String updateQryString = "update Human h " +
+ "set h.description = 'updated' " +
+ "where exists (" +
+ " select f.id " +
+ " from h.friends f " +
+ " where f.name.last = 'Public' " +
+ ")";
+ int count = s.createQuery( updateQryString ).executeUpdate();
+ assertEquals( 1, count );
+ s.delete( doll );
+ s.delete( joe );
+ t.commit();
+ s.close();
+
+ // single-table (one-to-many & many-to-many) ~~~~~~~~~~~~~~~~~~~~~~~~~~
+ s = openSession();
+ t = s.beginTransaction();
+ SimpleEntityWithAssociation entity = new SimpleEntityWithAssociation();
+ SimpleEntityWithAssociation other = new SimpleEntityWithAssociation();
+ entity.setName( "main" );
+ other.setName( "many-to-many-association" );
+ entity.getManyToManyAssociatedEntities().add( other );
+ entity.addAssociation( "one-to-many-association" );
+ s.save( entity );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ // one-to-many test
+ updateQryString = "update SimpleEntityWithAssociation e " +
+ "set e.name = 'updated' " +
+ "where exists (" +
+ " select a.id " +
+ " from e.associatedEntities a " +
+ " where a.name = 'one-to-many-association' " +
+ ")";
+ count = s.createQuery( updateQryString ).executeUpdate();
+ assertEquals( 1, count );
+ // many-to-many test
+ if ( supportsSubqueryOnMutatingTable() ) {
+ updateQryString = "update SimpleEntityWithAssociation e " +
+ "set e.name = 'updated' " +
+ "where exists (" +
+ " select a.id " +
+ " from e.manyToManyAssociatedEntities a " +
+ " where a.name = 'many-to-many-association' " +
+ ")";
+ count = s.createQuery( updateQryString ).executeUpdate();
+ assertEquals( 1, count );
+ }
+ s.delete( entity.getManyToManyAssociatedEntities().iterator().next() );
+ s.delete( entity );
+ t.commit();
+ s.close();
+ }
+
+ public void testIncrementCounterVersion() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ IntegerVersioned entity = new IntegerVersioned( "int-vers" );
+ s.save( entity );
+ t.commit();
+ s.close();
+
+ int initialVersion = entity.getVersion();
+
+ s = openSession();
+ t = s.beginTransaction();
+ int count = s.createQuery( "update versioned IntegerVersioned set name = name" ).executeUpdate();
+ assertEquals( "incorrect exec count", 1, count );
+ t.commit();
+
+ t = s.beginTransaction();
+ entity = ( IntegerVersioned ) s.load( IntegerVersioned.class, entity.getId() );
+ assertEquals( "version not incremented", initialVersion + 1, entity.getVersion() );
+
+ s.delete( entity );
+ t.commit();
+ s.close();
+ }
+
+ public void testIncrementTimestampVersion() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ TimestampVersioned entity = new TimestampVersioned( "ts-vers" );
+ s.save( entity );
+ t.commit();
+ s.close();
+
+ Date initialVersion = entity.getVersion();
+
+ synchronized (this) {
+ try {
+ wait(1500);
+ }
+ catch (InterruptedException ie) {}
+ }
+
+ s = openSession();
+ t = s.beginTransaction();
+ int count = s.createQuery( "update versioned TimestampVersioned set name = name" ).executeUpdate();
+ assertEquals( "incorrect exec count", 1, count );
+ t.commit();
+
+ t = s.beginTransaction();
+ entity = ( TimestampVersioned ) s.load( TimestampVersioned.class, entity.getId() );
+ assertTrue( "version not incremented", entity.getVersion().after( initialVersion ) );
+
+ s.delete( entity );
+ t.commit();
+ s.close();
+ }
+
+ public void testUpdateOnComponent() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ Human human = new Human();
+ human.setName( new Name() );
+ human.getName().setFirst("Stevee");
+ human.getName().setInitial('X');
+ human.getName().setLast("Ebersole");
+
+ s.save( human );
+ s.flush();
+
+ t.commit();
+
+ String correctName = "Steve";
+
+ t = s.beginTransaction();
+
+ int count = s.createQuery( "update Human set name.first = :correction where id = :id" )
+ .setString( "correction", correctName )
+ .setLong( "id", human.getId().longValue() )
+ .executeUpdate();
+
+ assertEquals( "Incorrect update count", 1, count );
+
+ t.commit();
+
+ t = s.beginTransaction();
+
+ s.refresh( human );
+
+ assertEquals( "Update did not execute properly", correctName, human.getName().getFirst() );
+
+ s.createQuery( "delete Human" ).executeUpdate();
+ t.commit();
+
+ s.close();
+ }
+
+ public void testUpdateOnManyToOne() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ s.createQuery( "update Animal a set a.mother = null where a.id = 2" ).executeUpdate();
+ if ( ! ( getDialect() instanceof MySQLDialect ) ) {
+ // MySQL does not support (even un-correlated) subqueries against the update-mutating table
+ s.createQuery( "update Animal a set a.mother = (from Animal where id = 1) where a.id = 2" ).executeUpdate();
+ }
+
+ t.commit();
+ s.close();
+ }
+
+ public void testUpdateOnImplicitJoinFails() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ Human human = new Human();
+ human.setName( new Name() );
+ human.getName().setFirst( "Steve" );
+ human.getName().setInitial('E');
+
+ Human mother = new Human();
+ mother.setName( new Name() );
+ mother.getName().setFirst( "Jane" );
+ mother.getName().setInitial('E');
+ human.setMother( mother );
+
+ s.save( human );
+ s.save( mother );
+ s.flush();
+
+ t.commit();
+
+ t = s.beginTransaction();
+ try {
+ s.createQuery( "update Human set mother.name.initial = :initial" ).setString( "initial", "F" ).executeUpdate();
+ fail( "update allowed across implicit join" );
+ }
+ catch( QueryException e ) {
+ System.out.println( "TEST (OK) : " + e.getMessage() );
+ // expected condition
+ }
+
+ s.createQuery( "delete Human where mother is not null" ).executeUpdate();
+ s.createQuery( "delete Human" ).executeUpdate();
+ t.commit();
+ s.close();
+ }
+
+ public void testUpdateOnDiscriminatorSubclass() {
+ TestData data = new TestData();
+ data.prepare();
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ int count = s.createQuery( "update PettingZoo set name = name" ).executeUpdate();
+ assertEquals( "Incorrect discrim subclass update count", 1, count );
+
+ t.rollback();
+ t = s.beginTransaction();
+
+ count = s.createQuery( "update PettingZoo pz set pz.name = pz.name where pz.id = :id" )
+ .setLong( "id", data.pettingZoo.getId().longValue() )
+ .executeUpdate();
+ assertEquals( "Incorrect discrim subclass update count", 1, count );
+
+ t.rollback();
+ t = s.beginTransaction();
+
+ count = s.createQuery( "update Zoo as z set z.name = z.name" ).executeUpdate();
+ assertEquals( "Incorrect discrim subclass update count", 2, count );
+
+ t.rollback();
+ t = s.beginTransaction();
+
+ // TODO : not so sure this should be allowed. Seems to me that if they specify an alias,
+ // property-refs should be required to be qualified.
+ count = s.createQuery( "update Zoo as z set name = name where id = :id" )
+ .setLong( "id", data.zoo.getId().longValue() )
+ .executeUpdate();
+ assertEquals( "Incorrect discrim subclass update count", 1, count );
+
+ t.commit();
+ s.close();
+
+ data.cleanup();
+ }
+
+ public void testUpdateOnAnimal() {
+ TestData data = new TestData();
+ data.prepare();
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ int count = s.createQuery( "update Animal set description = description where description = :desc" )
+ .setString( "desc", data.frog.getDescription() )
+ .executeUpdate();
+ assertEquals( "Incorrect entity-updated count", 1, count );
+
+ count = s.createQuery( "update Animal set description = :newDesc where description = :desc" )
+ .setString( "desc", data.polliwog.getDescription() )
+ .setString( "newDesc", "Tadpole" )
+ .executeUpdate();
+ assertEquals( "Incorrect entity-updated count", 1, count );
+
+ Animal tadpole = ( Animal ) s.load( Animal.class, data.polliwog.getId() );
+ assertEquals( "Update did not take effect", "Tadpole", tadpole.getDescription() );
+
+ count = s.createQuery( "update Animal set bodyWeight = bodyWeight + :w1 + :w2" )
+ .setDouble( "w1", 1 )
+ .setDouble( "w2", 2 )
+ .executeUpdate();
+ assertEquals( "incorrect count on 'complex' update assignment", count, 6 );
+
+ if ( ! ( getDialect() instanceof MySQLDialect ) ) {
+ // MySQL does not support (even un-correlated) subqueries against the update-mutating table
+ s.createQuery( "update Animal set bodyWeight = ( select max(bodyWeight) from Animal )" )
+ .executeUpdate();
+ }
+
+ t.commit();
+ s.close();
+
+ data.cleanup();
+ }
+
+ public void testUpdateOnMammal() {
+ TestData data = new TestData();
+ data.prepare();
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ int count = s.createQuery( "update Mammal set description = description" ).executeUpdate();
+ assertEquals( "incorrect update count against 'middle' of joined-subclass hierarchy", 2, count );
+
+ count = s.createQuery( "update Mammal set bodyWeight = 25" ).executeUpdate();
+ assertEquals( "incorrect update count against 'middle' of joined-subclass hierarchy", 2, count );
+
+ if ( ! ( getDialect() instanceof MySQLDialect ) ) {
+ // MySQL does not support (even un-correlated) subqueries against the update-mutating table
+ count = s.createQuery( "update Mammal set bodyWeight = ( select max(bodyWeight) from Animal )" ).executeUpdate();
+ assertEquals( "incorrect update count against 'middle' of joined-subclass hierarchy", 2, count );
+ }
+
+ t.commit();
+ s.close();
+
+ data.cleanup();
+ }
+
+ public void testUpdateSetNullUnionSubclass() {
+ TestData data = new TestData();
+ data.prepare();
+
+ // These should reach out into *all* subclass tables...
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ int count = s.createQuery( "update Vehicle set owner = 'Steve'" ).executeUpdate();
+ assertEquals( "incorrect restricted update count", 4, count );
+ count = s.createQuery( "update Vehicle set owner = null where owner = 'Steve'" ).executeUpdate();
+ assertEquals( "incorrect restricted update count", 4, count );
+
+ count = s.createQuery( "delete Vehicle where owner is null" ).executeUpdate();
+ assertEquals( "incorrect restricted update count", 4, count );
+
+ t.commit();
+ s.close();
+
+ data.cleanup();
+ }
+
+ public void testUpdateSetNullOnDiscriminatorSubclass() {
+ TestData data = new TestData();
+ data.prepare();
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ int count = s.createQuery( "update PettingZoo set address.city = null" ).executeUpdate();
+ assertEquals( "Incorrect discrim subclass delete count", 1, count );
+ count = s.createQuery( "delete Zoo where address.city is null" ).executeUpdate();
+ assertEquals( "Incorrect discrim subclass delete count", 1, count );
+
+ count = s.createQuery( "update Zoo set address.city = null" ).executeUpdate();
+ assertEquals( "Incorrect discrim subclass delete count", 1, count );
+ count = s.createQuery( "delete Zoo where address.city is null" ).executeUpdate();
+ assertEquals( "Incorrect discrim subclass delete count", 1, count );
+
+ t.commit();
+ s.close();
+
+ data.cleanup();
+ }
+
+ public void testUpdateSetNullOnJoinedSubclass() {
+ TestData data = new TestData();
+ data.prepare();
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ int count = s.createQuery( "update Mammal set bodyWeight = null" ).executeUpdate();
+ assertEquals( "Incorrect deletion count on joined subclass", 2, count );
+
+ count = s.createQuery( "delete Animal where bodyWeight = null" ).executeUpdate();
+ assertEquals( "Incorrect deletion count on joined subclass", 2, count );
+
+ t.commit();
+ s.close();
+
+ data.cleanup();
+ }
+
+
+ // DELETES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public void testDeleteWithSubquery() {
+ // setup the test data...
+ Session s = openSession();
+ s.beginTransaction();
+ SimpleEntityWithAssociation owner = new SimpleEntityWithAssociation( "myEntity-1" );
+ owner.addAssociation( "assoc-1" );
+ owner.addAssociation( "assoc-2" );
+ owner.addAssociation( "assoc-3" );
+ s.save( owner );
+ SimpleEntityWithAssociation owner2 = new SimpleEntityWithAssociation( "myEntity-2" );
+ owner2.addAssociation( "assoc-1" );
+ owner2.addAssociation( "assoc-2" );
+ owner2.addAssociation( "assoc-3" );
+ owner2.addAssociation( "assoc-4" );
+ s.save( owner2 );
+ SimpleEntityWithAssociation owner3 = new SimpleEntityWithAssociation( "myEntity-3" );
+ s.save( owner3 );
+ s.getTransaction().commit();
+ s.close();
+
+ // now try the bulk delete
+ s = openSession();
+ s.beginTransaction();
+ int count = s.createQuery( "delete SimpleEntityWithAssociation e where size( e.associatedEntities ) = 0 and e.name like '%'" ).executeUpdate();
+ assertEquals( "incorrect delete count", 1, count );
+ s.getTransaction().commit();
+ s.close();
+
+ // finally, clean up
+ s = openSession();
+ s.beginTransaction();
+ s.createQuery( "delete SimpleAssociatedEntity" ).executeUpdate();
+ s.createQuery( "delete SimpleEntityWithAssociation" ).executeUpdate();
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testSimpleDeleteOnAnimal() {
+ if ( getDialect().hasSelfReferentialForeignKeyBug() ) {
+ reportSkip( "self referential FK bug", "HQL delete testing" );
+ return;
+ }
+
+ TestData data = new TestData();
+ data.prepare();
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ int count = s.createQuery( "delete from Animal as a where a.id = :id" )
+ .setLong( "id", data.polliwog.getId().longValue() )
+ .executeUpdate();
+ assertEquals( "Incorrect delete count", 1, count );
+
+ count = s.createQuery( "delete Animal where id = :id" )
+ .setLong( "id", data.catepillar.getId().longValue() )
+ .executeUpdate();
+ assertEquals( "incorrect delete count", 1, count );
+
+ // HHH-873...
+ if ( supportsSubqueryOnMutatingTable() ) {
+ count = s.createQuery( "delete from User u where u not in (select u from User u)" ).executeUpdate();
+ assertEquals( 0, count );
+ }
+
+ count = s.createQuery( "delete Animal a" ).executeUpdate();
+ assertEquals( "Incorrect delete count", 4, count );
+
+ List list = s.createQuery( "select a from Animal as a" ).list();
+ assertTrue( "table not empty", list.isEmpty() );
+
+ t.commit();
+ s.close();
+ data.cleanup();
+ }
+
+ public void testDeleteOnDiscriminatorSubclass() {
+ TestData data = new TestData();
+ data.prepare();
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ int count = s.createQuery( "delete PettingZoo" ).executeUpdate();
+ assertEquals( "Incorrect discrim subclass delete count", 1, count );
+
+ count = s.createQuery( "delete Zoo" ).executeUpdate();
+ assertEquals( "Incorrect discrim subclass delete count", 1, count );
+
+ t.commit();
+ s.close();
+
+ data.cleanup();
+ }
+
+ public void testDeleteOnJoinedSubclass() {
+ TestData data = new TestData();
+ data.prepare();
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ int count = s.createQuery( "delete Mammal where bodyWeight > 150" ).executeUpdate();
+ assertEquals( "Incorrect deletion count on joined subclass", 1, count );
+
+ count = s.createQuery( "delete Mammal" ).executeUpdate();
+ assertEquals( "Incorrect deletion count on joined subclass", 1, count );
+
+ count = s.createQuery( "delete SubMulti" ).executeUpdate();
+ assertEquals( "Incorrect deletion count on joined subclass", 0, count );
+
+ t.commit();
+ s.close();
+
+ data.cleanup();
+ }
+
+ public void testDeleteOnMappedJoin() {
+ TestData data = new TestData();
+ data.prepare();
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ int count = s.createQuery( "delete Joiner where joinedName = :joinedName" ).setString( "joinedName", "joined-name" ).executeUpdate();
+ assertEquals( "Incorrect deletion count on joined subclass", 1, count );
+
+ t.commit();
+ s.close();
+
+ data.cleanup();
+ }
+
+ public void testDeleteUnionSubclassAbstractRoot() {
+ TestData data = new TestData();
+ data.prepare();
+
+ // These should reach out into *all* subclass tables...
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ int count = s.createQuery( "delete Vehicle where owner = :owner" ).setString( "owner", "Steve" ).executeUpdate();
+ assertEquals( "incorrect restricted update count", 1, count );
+
+ count = s.createQuery( "delete Vehicle" ).executeUpdate();
+ assertEquals( "incorrect update count", 3, count );
+ t.commit();
+ s.close();
+
+ data.cleanup();
+ }
+
+ public void testDeleteUnionSubclassConcreteSubclass() {
+ TestData data = new TestData();
+ data.prepare();
+
+ // These should only affect the given table
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ int count = s.createQuery( "delete Truck where owner = :owner" ).setString( "owner", "Steve" ).executeUpdate();
+ assertEquals( "incorrect restricted update count", 1, count );
+
+ count = s.createQuery( "delete Truck" ).executeUpdate();
+ assertEquals( "incorrect update count", 2, count );
+ t.commit();
+ s.close();
+
+ data.cleanup();
+ }
+
+ public void testDeleteUnionSubclassLeafSubclass() {
+ TestData data = new TestData();
+ data.prepare();
+
+ // These should only affect the given table
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ int count = s.createQuery( "delete Car where owner = :owner" ).setString( "owner", "Kirsten" ).executeUpdate();
+ assertEquals( "incorrect restricted update count", 1, count );
+
+ count = s.createQuery( "delete Car" ).executeUpdate();
+ assertEquals( "incorrect update count", 0, count );
+ t.commit();
+ s.close();
+
+ data.cleanup();
+ }
+
+ public void testDeleteWithMetadataWhereFragments() throws Throwable {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ // Note: we are just checking the syntax here...
+ s.createQuery("delete from Bar").executeUpdate();
+ s.createQuery("delete from Bar where barString = 's'").executeUpdate();
+
+ t.commit();
+ s.close();
+ }
+
+ public void testDeleteRestrictedOnManyToOne() {
+ TestData data = new TestData();
+ data.prepare();
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ int count = s.createQuery( "delete Animal where mother = :mother" )
+ .setEntity( "mother", data.butterfly )
+ .executeUpdate();
+ assertEquals( 1, count );
+
+ t.commit();
+ s.close();
+
+ data.cleanup();
+ }
+
+ public void testDeleteSyntaxWithCompositeId() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ s.createQuery( "delete EntityWithCrazyCompositeKey where id.id = 1 and id.otherId = 2" ).executeUpdate();
+ s.createQuery( "delete from EntityWithCrazyCompositeKey where id.id = 1 and id.otherId = 2" ).executeUpdate();
+ s.createQuery( "delete from EntityWithCrazyCompositeKey e where e.id.id = 1 and e.id.otherId = 2" ).executeUpdate();
+
+ t.commit();
+ s.close();
+ }
+
+ private class TestData {
+
+ private Animal polliwog;
+ private Animal catepillar;
+ private Animal frog;
+ private Animal butterfly;
+
+ private Zoo zoo;
+ private Zoo pettingZoo;
+
+ private void prepare() {
+ Session s = openSession();
+ Transaction txn = s.beginTransaction();
+
+ polliwog = new Animal();
+ polliwog.setBodyWeight( 12 );
+ polliwog.setDescription( "Polliwog" );
+
+ catepillar = new Animal();
+ catepillar.setBodyWeight( 10 );
+ catepillar.setDescription( "Catepillar" );
+
+ frog = new Animal();
+ frog.setBodyWeight( 34 );
+ frog.setDescription( "Frog" );
+
+ polliwog.setFather( frog );
+ frog.addOffspring( polliwog );
+
+ butterfly = new Animal();
+ butterfly.setBodyWeight( 9 );
+ butterfly.setDescription( "Butterfly" );
+
+ catepillar.setMother( butterfly );
+ butterfly.addOffspring( catepillar );
+
+ s.save( frog );
+ s.save( polliwog );
+ s.save( butterfly );
+ s.save( catepillar );
+
+ Dog dog = new Dog();
+ dog.setBodyWeight( 200 );
+ dog.setDescription( "dog" );
+ s.save( dog );
+
+ Cat cat = new Cat();
+ cat.setBodyWeight( 100 );
+ cat.setDescription( "cat" );
+ s.save( cat );
+
+ zoo = new Zoo();
+ zoo.setName( "Zoo" );
+ Address add = new Address();
+ add.setCity("MEL");
+ add.setCountry("AU");
+ add.setStreet("Main st");
+ add.setPostalCode("3000");
+ zoo.setAddress(add);
+
+ pettingZoo = new PettingZoo();
+ pettingZoo.setName( "Petting Zoo" );
+ Address addr = new Address();
+ addr.setCity("Sydney");
+ addr.setCountry("AU");
+ addr.setStreet("High st");
+ addr.setPostalCode("2000");
+ pettingZoo.setAddress(addr);
+
+ s.save( zoo );
+ s.save( pettingZoo );
+
+ Joiner joiner = new Joiner();
+ joiner.setJoinedName( "joined-name" );
+ joiner.setName( "name" );
+ s.save( joiner );
+
+ Car car = new Car();
+ car.setVin( "123c" );
+ car.setOwner( "Kirsten" );
+ s.save( car );
+
+ Truck truck = new Truck();
+ truck.setVin( "123t" );
+ truck.setOwner( "Steve" );
+ s.save( truck );
+
+ SUV suv = new SUV();
+ suv.setVin( "123s" );
+ suv.setOwner( "Joe" );
+ s.save( suv );
+
+ Pickup pickup = new Pickup();
+ pickup.setVin( "123p" );
+ pickup.setOwner( "Cecelia" );
+ s.save( pickup );
+
+ BooleanLiteralEntity bool = new BooleanLiteralEntity();
+ s.save( bool );
+
+ txn.commit();
+ s.close();
+ }
+
+ private void cleanup() {
+ Session s = openSession();
+ Transaction txn = s.beginTransaction();
+
+ // workaround awesome HSQLDB "feature"
+ s.createQuery( "delete from Animal where mother is not null or father is not null" ).executeUpdate();
+ s.createQuery( "delete from Animal" ).executeUpdate();
+ s.createQuery( "delete from Zoo" ).executeUpdate();
+ s.createQuery( "delete from Joiner" ).executeUpdate();
+ s.createQuery( "delete from Vehicle" ).executeUpdate();
+ s.createQuery( "delete from BooleanLiteralEntity" ).executeUpdate();
+
+ txn.commit();
+ s.close();
+ }
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Car.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Car.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Car.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,10 @@
+// $Id: Car.java 7087 2005-06-08 18:23:44Z steveebersole $
+package org.hibernate.test.hql;
+
+/**
+ * Implementation of Car.
+ *
+ * @author Steve Ebersole
+ */
+public class Car extends Vehicle {
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Cat.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Cat.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Cat.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,9 @@
+//$Id: Cat.java 4476 2004-09-02 02:28:13Z oneovthafew $
+package org.hibernate.test.hql;
+
+/**
+ * @author Gavin King
+ */
+public class Cat extends DomesticAnimal {
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/ClassicTranslatorTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/ClassicTranslatorTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/ClassicTranslatorTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,64 @@
+package org.hibernate.test.hql;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.hql.classic.ClassicQueryTranslatorFactory;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * Some simple test queries using the classic translator explicitly
+ * to ensure that code is not broken in changes for the new translator.
+ * <p/>
+ * Only really checking translation and syntax, not results.
+ *
+ * @author Steve Ebersole
+ */
+public class ClassicTranslatorTest extends QueryTranslatorTestCase {
+
+ public ClassicTranslatorTest(String x) {
+ super( x );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( ClassicTranslatorTest.class );
+ }
+
+ public void configure(Configuration cfg) {
+ super.configure( cfg );
+ cfg.setProperty( Environment.QUERY_TRANSLATOR, ClassicQueryTranslatorFactory.class.getName() );
+ }
+
+
+ public boolean createSchema() {
+ return true;
+ }
+
+ public boolean recreateSchemaAfterFailure() {
+ return true;
+ }
+
+ public void testQueries() {
+ Session session = openSession();
+ session.beginTransaction();
+
+ session.createQuery( "from Animal" ).list();
+
+ session.createQuery( "select a from Animal as a" ).list();
+ session.createQuery( "select a.mother from Animal as a" ).list();
+ session.createQuery( "select m from Animal as a inner join a.mother as m" ).list();
+ session.createQuery( "select a from Animal as a inner join fetch a.mother" ).list();
+
+ session.createQuery( "from Animal as a where a.description = ?" ).setString( 0, "jj" ).list();
+ session.createQuery( "from Animal as a where a.description = :desc" ).setString( "desc", "jr" ).list();
+ session.createQuery( "from Animal as a where a.description = ? or a.description = :desc" )
+ .setString( 0, "jj" )
+ .setString( "desc", "jr" )
+ .list();
+
+ session.getTransaction().commit();
+ session.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Classification.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Classification.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Classification.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,78 @@
+package org.hibernate.test.hql;
+
+import java.io.Serializable;
+import java.util.HashMap;
+
+/**
+ * Mimic a JDK 5 enum.
+ *
+ * @author Steve Ebersole
+ */
+public class Classification implements Serializable, Comparable {
+
+ public static final Classification COOL = new Classification( "COOL", 0 );
+ public static final Classification LAME = new Classification( "LAME", 1 );
+
+ private static final HashMap INSTANCES = new HashMap();
+ static {
+ INSTANCES.put( COOL.name, COOL );
+ INSTANCES.put( LAME.name, LAME );
+ }
+
+ private final String name;
+ private final int ordinal;
+ private final int hashCode;
+
+ private Classification(String name, int ordinal) {
+ this.name = name;
+ this.ordinal = ordinal;
+
+ int hashCode = name.hashCode();
+ hashCode = 29 * hashCode + ordinal;
+ this.hashCode = hashCode;
+ }
+
+ public String name() {
+ return name;
+ }
+
+ public int ordinal() {
+ return ordinal;
+ }
+
+ public boolean equals(Object obj) {
+ return compareTo( obj ) == 0;
+ }
+
+ public int compareTo(Object o) {
+ int otherOrdinal = ( ( Classification ) o ).ordinal;
+ if ( ordinal == otherOrdinal ) {
+ return 0;
+ }
+ else if ( ordinal > otherOrdinal ) {
+ return 1;
+ }
+ else {
+ return -1;
+ }
+ }
+
+ public int hashCode() {
+ return hashCode;
+ }
+
+ public static Classification valueOf(String name) {
+ return ( Classification ) INSTANCES.get( name );
+ }
+
+ public static Classification valueOf(Integer ordinal) {
+ if ( ordinal == null ) {
+ return null;
+ }
+ switch ( ordinal.intValue() ) {
+ case 0: return COOL;
+ case 1: return LAME;
+ default: throw new IllegalArgumentException( "unknown classification ordinal [" + ordinal + "]" );
+ }
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/ClassificationType.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/ClassificationType.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/ClassificationType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,101 @@
+package org.hibernate.test.hql;
+
+import org.hibernate.usertype.EnhancedUserType;
+import org.hibernate.HibernateException;
+import org.hibernate.Hibernate;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.PreparedStatement;
+import java.sql.Types;
+import java.io.Serializable;
+
+/**
+ * A custom type for mapping {@link org.hibernate.test.hql.Classification} instances
+ * to the respective db column.
+ * </p>
+ * THis is largely intended to mimic JDK5 enum support in JPA. Here we are
+ * using the approach of storing the ordinal values, rather than the names.
+ *
+ * @author Steve Ebersole
+ */
+public class ClassificationType implements EnhancedUserType {
+
+ public int[] sqlTypes() {
+ return new int[] { Types.TINYINT };
+ }
+
+ public Class returnedClass() {
+ return Classification.class;
+ }
+
+ public boolean equals(Object x, Object y) throws HibernateException {
+ if ( x == null && y == null ) {
+ return false;
+ }
+ else if ( x != null ) {
+ return x.equals( y );
+ }
+ else {
+ return y.equals( x );
+ }
+ }
+
+ public int hashCode(Object x) throws HibernateException {
+ return x.hashCode();
+ }
+
+ public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException {
+ Integer ordinal = ( Integer ) Hibernate.INTEGER.nullSafeGet( rs, names[0] );
+ return Classification.valueOf( ordinal );
+ }
+
+ public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException {
+ Integer ordinal = value == null ? null : new Integer( ( ( Classification ) value ).ordinal() );
+ Hibernate.INTEGER.nullSafeSet( st, ordinal, index );
+ }
+
+ public Object deepCopy(Object value) throws HibernateException {
+ return value;
+ }
+
+ public boolean isMutable() {
+ return false;
+ }
+
+ public Serializable disassemble(Object value) throws HibernateException {
+ return ( Classification ) value;
+ }
+
+ public Object assemble(Serializable cached, Object owner) throws HibernateException {
+ return cached;
+ }
+
+ public Object replace(Object original, Object target, Object owner) throws HibernateException {
+ return original;
+ }
+
+ public String objectToSQLString(Object value) {
+ return extractOrdinalString( value );
+ }
+
+ public String toXMLString(Object value) {
+ return extractName( value );
+ }
+
+ public Object fromXMLString(String xmlValue) {
+ return Classification.valueOf( xmlValue );
+ }
+
+ private String extractName(Object obj) {
+ return ( ( Classification ) obj ).name();
+ }
+
+ private int extractOrdinal(Object value) {
+ return ( ( Classification ) value ).ordinal();
+ }
+
+ private String extractOrdinalString(Object value) {
+ return Integer.toString( extractOrdinal( value ) );
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/ComponentContainer.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/ComponentContainer.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/ComponentContainer.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping SYSTEM "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
+
+<hibernate-mapping package="org.hibernate.test.hql" default-access="field">
+
+ <class name="ComponentContainer" table="HQL_COMP_CONT">
+ <id name="id" type="long" column="ID">
+ <generator class="increment" />
+ </id>
+ <component name="address" class="ComponentContainer$Address">
+ <property name="street" type="string" column="STREET_ADDR" />
+ <property name="city" type="string" column="CITY_ADDR" />
+ <property name="state" type="string" column="STATE_ADDR" />
+ <component name="zip" class="ComponentContainer$Address$Zip">
+ <property name="code" type="int" column="ZIP_CODE_ADDR" />
+ <property name="plus4" type="int" column="ZIP_PLUS4_ADDR" />
+ </component>
+ </component>
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/ComponentContainer.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/ComponentContainer.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/ComponentContainer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,107 @@
+package org.hibernate.test.hql;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class ComponentContainer {
+
+ private Long id;
+ private ComponentContainer.Address address;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public ComponentContainer.Address getAddress() {
+ return address;
+ }
+
+ public void setAddress(ComponentContainer.Address address) {
+ this.address = address;
+ }
+
+ public static class Address {
+ private String street;
+ private String city;
+ private String state;
+ private ComponentContainer.Address.Zip zip;
+
+ public Address() {
+ }
+
+ public Address(String street, String city, String state, ComponentContainer.Address.Zip zip) {
+ this.street = street;
+ this.city = city;
+ this.state = state;
+ this.zip = zip;
+ }
+
+ public String getStreet() {
+ return street;
+ }
+
+ public void setStreet(String street) {
+ this.street = street;
+ }
+
+ public String getCity() {
+ return city;
+ }
+
+ public void setCity(String city) {
+ this.city = city;
+ }
+
+ public String getState() {
+ return state;
+ }
+
+ public void setState(String state) {
+ this.state = state;
+ }
+
+ public ComponentContainer.Address.Zip getZip() {
+ return zip;
+ }
+
+ public void setZip(ComponentContainer.Address.Zip zip) {
+ this.zip = zip;
+ }
+
+ public static class Zip {
+ private int code;
+ private int plus4;
+
+ public Zip() {
+ }
+
+ public Zip(int code, int plus4) {
+ this.code = code;
+ this.plus4 = plus4;
+ }
+
+ public int getCode() {
+ return code;
+ }
+
+ public void setCode(int code) {
+ this.code = code;
+ }
+
+ public int getPlus4() {
+ return plus4;
+ }
+
+ public void setPlus4(int plus4) {
+ this.plus4 = plus4;
+ }
+ }
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/CrazyCompositeKey.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/CrazyCompositeKey.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/CrazyCompositeKey.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,40 @@
+// $Id: CrazyCompositeKey.java 6970 2005-05-31 20:24:41Z oneovthafew $
+package org.hibernate.test.hql;
+
+import java.io.Serializable;
+
+/**
+ * Implementation of CrazyCompositeKey.
+ *
+ * @author Steve Ebersole
+ */
+public class CrazyCompositeKey implements Serializable {
+ private Long id;
+ private Long otherId;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public Long getOtherId() {
+ return otherId;
+ }
+
+ public void setOtherId(Long otherId) {
+ this.otherId = otherId;
+ }
+
+ public boolean equals(Object that) {
+ CrazyCompositeKey cck = (CrazyCompositeKey) that;
+ return cck.id.longValue() == id.longValue()
+ && cck.otherId.longValue() == otherId.longValue();
+ }
+
+ public int hashCode() {
+ return id.hashCode() + otherId.hashCode();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/CrazyIdFieldNames.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/CrazyIdFieldNames.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/CrazyIdFieldNames.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+
+<!DOCTYPE hibernate-mapping SYSTEM "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
+
+<hibernate-mapping package="org.hibernate.test.hql">
+
+ <class name="HeresAnotherCrazyIdFieldName" table="CRAZY_ID_NODE">
+ <id name="heresAnotherCrazyIdFieldName" column="ID" type="long">
+ <generator class="increment"/>
+ </id>
+ <property name="name" type="string"/>
+ </class>
+
+ <class name="MoreCrazyIdFieldNameStuffEntity" table="CRAZY_ID_TOP">
+ <id name="moreCrazyIdFieldNameStuffEntity" column="ID" type="long">
+ <generator class="increment"/>
+ </id>
+ <property name="name" type="string" />
+ <many-to-one name="heresAnotherCrazyIdFieldName" class="HeresAnotherCrazyIdFieldName"/>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/CriteriaClassicAggregationReturnTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/CriteriaClassicAggregationReturnTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/CriteriaClassicAggregationReturnTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,107 @@
+package org.hibernate.test.hql;
+
+import java.util.Collections;
+
+import junit.framework.Test;
+
+import org.hibernate.cfg.Configuration;
+import org.hibernate.dialect.function.ClassicCountFunction;
+import org.hibernate.dialect.function.ClassicAvgFunction;
+import org.hibernate.dialect.function.ClassicSumFunction;
+import org.hibernate.hql.ast.QueryTranslatorImpl;
+import org.hibernate.hql.QueryTranslator;
+import org.hibernate.hql.QueryTranslatorFactory;
+import org.hibernate.hql.classic.ClassicQueryTranslatorFactory;
+import org.hibernate.Hibernate;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class CriteriaClassicAggregationReturnTest extends QueryTranslatorTestCase {
+
+ public CriteriaClassicAggregationReturnTest(String x) {
+ super( x );
+ }
+
+ public void configure(Configuration cfg) {
+ super.configure( cfg );
+ cfg.addSqlFunction( "count", new ClassicCountFunction() );
+ cfg.addSqlFunction( "avg", new ClassicAvgFunction() );
+ cfg.addSqlFunction( "sum", new ClassicSumFunction() );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( CriteriaClassicAggregationReturnTest.class );
+ }
+
+ public void testClassicHQLAggregationReturnTypes() {
+ // EJB3: COUNT returns Long
+ QueryTranslatorImpl translator = createNewQueryTranslator( "select count(*) from Human h", sfi() );
+ assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
+ assertEquals( "incorrect return type", Hibernate.INTEGER, translator.getReturnTypes()[0] );
+
+ translator = createNewQueryTranslator( "select count(h.height) from Human h", sfi() );
+ assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
+ assertEquals( "incorrect return type", Hibernate.INTEGER, translator.getReturnTypes()[0] );
+
+ // MAX, MIN return the type of the state-field to which they are applied.
+ translator = createNewQueryTranslator( "select max(h.height) from Human h", sfi() );
+ assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
+ assertEquals( "incorrect return type", Hibernate.DOUBLE, translator.getReturnTypes()[0] );
+
+ translator = createNewQueryTranslator( "select max(h.id) from Human h", sfi() );
+ assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
+ assertEquals( "incorrect return type", Hibernate.LONG, translator.getReturnTypes()[0] );
+
+ // AVG returns Float integrals, and otherwise the field type.
+ translator = createNewQueryTranslator( "select avg(h.height) from Human h", sfi() );
+ assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
+ assertEquals( "incorrect return type", Hibernate.DOUBLE, translator.getReturnTypes()[0] );
+
+ translator = createNewQueryTranslator( "select avg(h.id) from Human h", sfi() );
+ assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
+ assertEquals( "incorrect return type", Hibernate.FLOAT, translator.getReturnTypes()[0] );
+
+ translator = createNewQueryTranslator( "select avg(h.bigIntegerValue) from Human h", sfi() );
+ assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
+ assertEquals( "incorrect return type", Hibernate.BIG_INTEGER, translator.getReturnTypes()[0] );
+
+ // SUM returns underlying type sum
+ translator = createNewQueryTranslator( "select sum(h.id) from Human h", sfi() );
+ assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
+ assertEquals( "incorrect return type", Hibernate.LONG, translator.getReturnTypes()[0] );
+
+ translator = createNewQueryTranslator( "select sum(h.intValue) from Human h", sfi() );
+ assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
+ assertEquals( "incorrect return type", Hibernate.INTEGER, translator.getReturnTypes()[0] );
+
+ translator = createNewQueryTranslator( "select sum(h.height) from Human h", sfi() );
+ assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
+ assertEquals( "incorrect return type", Hibernate.DOUBLE, translator.getReturnTypes()[0] );
+
+ translator = createNewQueryTranslator( "select sum(h.floatValue) from Human h", sfi() );
+ assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
+ assertEquals( "incorrect return type", Hibernate.FLOAT, translator.getReturnTypes()[0] );
+
+ translator = createNewQueryTranslator( "select sum(h.bigIntegerValue) from Human h", sfi() );
+ assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
+ assertEquals( "incorrect return type", Hibernate.BIG_INTEGER, translator.getReturnTypes()[0] );
+
+ translator = createNewQueryTranslator( "select sum(h.bigDecimalValue) from Human h", sfi() );
+ assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
+ assertEquals( "incorrect return type", Hibernate.BIG_DECIMAL, translator.getReturnTypes()[0] );
+
+ // special case to test classicquery special case handling of count(*)
+ QueryTranslator oldQueryTranslator = null;
+ String hql = "select count(*) from Human h";
+ QueryTranslatorFactory classic = new ClassicQueryTranslatorFactory();
+ oldQueryTranslator = classic.createQueryTranslator( hql, hql, Collections.EMPTY_MAP, sfi() );
+ oldQueryTranslator.compile( Collections.EMPTY_MAP, true);
+ assertEquals( "incorrect return type count", 1, oldQueryTranslator.getReturnTypes().length );
+ assertEquals( "incorrect return type", Hibernate.INTEGER, oldQueryTranslator.getReturnTypes()[0] );
+
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/CriteriaHQLAlignmentTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/CriteriaHQLAlignmentTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/CriteriaHQLAlignmentTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,173 @@
+//$Id: HQLTest.java 9873 2006-05-04 13:42:48Z max.andersen at jboss.com $
+package org.hibernate.test.hql;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Collections;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.classic.Session;
+import org.hibernate.criterion.Projections;
+import org.hibernate.hql.QueryTranslator;
+import org.hibernate.hql.QueryTranslatorFactory;
+import org.hibernate.hql.ast.QueryTranslatorImpl;
+import org.hibernate.hql.ast.tree.SelectClause;
+import org.hibernate.hql.classic.ClassicQueryTranslatorFactory;
+
+/**
+ * Tests cases for ensuring alignment between HQL and Criteria behavior.
+ *
+ * @author Max Rydahl Andersen
+ */
+public class CriteriaHQLAlignmentTest extends QueryTranslatorTestCase {
+
+ public CriteriaHQLAlignmentTest(String x) {
+ super( x );
+ SelectClause.VERSION2_SQL = true;
+ }
+
+ public boolean createSchema() {
+ return true; // needed for the Criteria return type test
+ }
+
+ public boolean recreateSchemaAfterFailure() {
+ return true;
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( CriteriaHQLAlignmentTest.class );
+ }
+
+ public void testHQLAggregationReturnType() {
+ // EJB3: COUNT returns Long
+ QueryTranslatorImpl translator = createNewQueryTranslator( "select count(*) from Human h" );
+ assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
+ assertEquals( "incorrect return type", Hibernate.LONG, translator.getReturnTypes()[0] );
+
+ translator = createNewQueryTranslator( "select count(h.height) from Human h" );
+ assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
+ assertEquals( "incorrect return type", Hibernate.LONG, translator.getReturnTypes()[0] );
+
+ // MAX, MIN return the type of the state-field to which they are applied.
+ translator = createNewQueryTranslator( "select max(h.height) from Human h" );
+ assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
+ assertEquals( "incorrect return type", Hibernate.DOUBLE, translator.getReturnTypes()[0] );
+
+ translator = createNewQueryTranslator( "select max(h.id) from Human h" );
+ assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
+ assertEquals( "incorrect return type", Hibernate.LONG, translator.getReturnTypes()[0] );
+
+ // AVG returns Double.
+ translator = createNewQueryTranslator( "select avg(h.height) from Human h" );
+ assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
+ assertEquals( "incorrect return type", Hibernate.DOUBLE, translator.getReturnTypes()[0] );
+
+ translator = createNewQueryTranslator( "select avg(h.id) from Human h" );
+ assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
+ assertEquals( "incorrect return type", Hibernate.DOUBLE, translator.getReturnTypes()[0] );
+
+ translator = createNewQueryTranslator( "select avg(h.bigIntegerValue) from Human h" );
+ assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
+ assertEquals( "incorrect return type", Hibernate.DOUBLE, translator.getReturnTypes()[0] );
+
+ // SUM returns Long when applied to state-fields of integral types (other than BigInteger);
+ translator = createNewQueryTranslator( "select sum(h.id) from Human h" );
+ assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
+ assertEquals( "incorrect return type", Hibernate.LONG, translator.getReturnTypes()[0] );
+
+ translator = createNewQueryTranslator( "select sum(h.intValue) from Human h" );
+ assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
+ assertEquals( "incorrect return type", Hibernate.LONG, translator.getReturnTypes()[0] );
+
+ // SUM returns Double when applied to state-fields of floating point types;
+ translator = createNewQueryTranslator( "select sum(h.height) from Human h" );
+ assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
+ assertEquals( "incorrect return type", Hibernate.DOUBLE, translator.getReturnTypes()[0] );
+
+ translator = createNewQueryTranslator( "select sum(h.floatValue) from Human h" );
+ assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
+ assertEquals( "incorrect return type", Hibernate.DOUBLE, translator.getReturnTypes()[0] );
+
+ // SUM returns BigInteger when applied to state-fields of type BigInteger
+ translator = createNewQueryTranslator( "select sum(h.bigIntegerValue) from Human h" );
+ assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
+ assertEquals( "incorrect return type", Hibernate.BIG_INTEGER, translator.getReturnTypes()[0] );
+
+ // SUM and BigDecimal when applied to state-fields of type BigDecimal.
+ translator = createNewQueryTranslator( "select sum(h.bigDecimalValue) from Human h" );
+ assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
+ assertEquals( "incorrect return type", Hibernate.BIG_DECIMAL, translator.getReturnTypes()[0] );
+
+ // special case to test classicquery special case handling of count(*)
+ QueryTranslator oldQueryTranslator = null;
+ String hql = "select count(*) from Human h";
+ QueryTranslatorFactory classic = new ClassicQueryTranslatorFactory();
+ oldQueryTranslator = classic.createQueryTranslator( hql, hql, Collections.EMPTY_MAP, getSessionFactoryImplementor() );
+ oldQueryTranslator.compile( Collections.EMPTY_MAP, true);
+ assertEquals( "incorrect return type count", 1, oldQueryTranslator.getReturnTypes().length );
+ assertEquals( "incorrect return type", Hibernate.LONG, oldQueryTranslator.getReturnTypes()[0] );
+
+ }
+
+ // HHH-1724 Align Criteria with HQL aggregation return types.
+ public void testCriteriaAggregationReturnTypeFailureExpected() {
+ Session s = openSession();
+ Human human = new Human();
+ human.setBigIntegerValue( new BigInteger("42") );
+ human.setBigDecimalValue( new BigDecimal(45) );
+ s.save(human);
+
+ // EJB3: COUNT returns Long
+ Long longValue = (Long) s.createCriteria( Human.class ).setProjection( Projections.rowCount()).uniqueResult();
+ assertEquals(longValue, new Long(1));
+ longValue = (Long) s.createCriteria( Human.class ).setProjection( Projections.count("height")).uniqueResult();
+ assertEquals(longValue, new Long(1));
+
+ // MAX, MIN return the type of the state-field to which they are applied.
+ Double dblValue = (Double) s.createCriteria( Human.class ).setProjection( Projections.max( "height" )).uniqueResult();
+ assertNotNull(dblValue);
+
+ longValue = (Long) s.createCriteria( Human.class ).setProjection( Projections.max( "id" )).uniqueResult();
+ assertNotNull(longValue);
+
+ // AVG returns Double.
+ dblValue = (Double) s.createCriteria( Human.class ).setProjection( Projections.avg( "height" )).uniqueResult();
+ assertNotNull(dblValue);
+
+ dblValue = (Double) s.createCriteria( Human.class ).setProjection( Projections.avg( "id" )).uniqueResult();
+ assertNotNull(dblValue);
+
+ dblValue = (Double) s.createCriteria( Human.class ).setProjection( Projections.avg( "bigIntegerValue" )).uniqueResult();
+ assertNotNull(dblValue);
+
+ // SUM returns Long when applied to state-fields of integral types (other than BigInteger);
+ longValue = (Long) s.createCriteria( Human.class ).setProjection( Projections.sum( "id" )).uniqueResult();
+ assertNotNull(longValue);
+
+ longValue = (Long) s.createCriteria( Human.class ).setProjection( Projections.sum( "intValue" )).uniqueResult();
+ assertNotNull(longValue);
+
+ // SUM returns Double when applied to state-fields of floating point types;
+ dblValue = (Double) s.createCriteria( Human.class ).setProjection( Projections.sum( "height" )).uniqueResult();
+ assertNotNull(dblValue);
+
+ dblValue = (Double) s.createCriteria( Human.class ).setProjection( Projections.sum( "floatValue" )).uniqueResult();
+ assertNotNull(dblValue);
+
+ // SUM returns BigInteger when applied to state-fields of type BigInteger
+ BigInteger bigIValue = (BigInteger) s.createCriteria( Human.class ).setProjection( Projections.sum( "bigIntegerValue" )).uniqueResult();
+ assertNotNull(bigIValue);
+
+ // SUM and BigDecimal when applied to state-fields of type BigDecimal.
+ BigDecimal bigDValue = (BigDecimal) s.createCriteria( Human.class ).setProjection( Projections.sum( "bigDecimalValue" )).uniqueResult();
+ assertNotNull(bigDValue);
+
+ s.delete( human );
+ s.flush();
+ s.close();
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Dog.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Dog.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Dog.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,9 @@
+//$Id: Dog.java 4476 2004-09-02 02:28:13Z oneovthafew $
+package org.hibernate.test.hql;
+
+/**
+ * @author Gavin King
+ */
+public class Dog extends DomesticAnimal {
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/DomesticAnimal.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/DomesticAnimal.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/DomesticAnimal.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,17 @@
+//$Id: DomesticAnimal.java 4899 2004-12-06 14:17:24Z pgmjsd $
+package org.hibernate.test.hql;
+
+/**
+ * @author Gavin King
+ */
+public class DomesticAnimal extends Mammal {
+ private Human owner;
+
+ public Human getOwner() {
+ return owner;
+ }
+
+ public void setOwner(Human owner) {
+ this.owner = owner;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/DomesticAnimal.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/EJBQLTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/EJBQLTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/EJBQLTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,311 @@
+//$Id: EJBQLTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.hql;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.util.Collections;
+import java.util.List;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.hql.QueryTranslator;
+import org.hibernate.hql.QueryTranslatorFactory;
+import org.hibernate.hql.antlr.HqlSqlTokenTypes;
+import org.hibernate.hql.ast.ASTQueryTranslatorFactory;
+import org.hibernate.hql.ast.HqlParser;
+import org.hibernate.hql.ast.QueryTranslatorImpl;
+import org.hibernate.hql.ast.util.ASTUtil;
+import org.hibernate.test.TestCase;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+import antlr.RecognitionException;
+import antlr.TokenStreamException;
+import antlr.collections.AST;
+
+
+/**
+ * @author <a href="mailto:alex at jboss.org">Alexey Loubyansky</a>
+ */
+public class EJBQLTest extends FunctionalTestCase {
+
+ public EJBQLTest(String x) {
+ super( x );
+ }
+
+ public String[] getMappings() {
+ return new String[]{
+ "hql/Animal.hbm.xml",
+ "batchfetch/ProductLine.hbm.xml",
+ "cid/Customer.hbm.xml",
+ "cid/Order.hbm.xml",
+ "cid/LineItem.hbm.xml",
+ "cid/Product.hbm.xml",
+ "legacy/Glarch.hbm.xml",
+ "legacy/Fee.hbm.xml",
+ "legacy/Qux.hbm.xml",
+ "legacy/Fum.hbm.xml",
+ "legacy/Holder.hbm.xml",
+ "legacy/One.hbm.xml",
+ "legacy/FooBar.hbm.xml",
+ "legacy/Many.hbm.xml",
+ "legacy/Baz.hbm.xml",
+ "legacy/Simple.hbm.xml",
+ "legacy/Middle.hbm.xml",
+ "legacy/Category.hbm.xml",
+ "legacy/Multi.hbm.xml",
+ "legacy/Commento.hbm.xml",
+ "legacy/Marelo.hbm.xml",
+ "compositeelement/Parent.hbm.xml",
+ "legacy/Container.hbm.xml",
+ };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( EJBQLTest.class );
+ }
+
+
+ public boolean createSchema() {
+ return false;
+ }
+
+ public void testEjb3PositionalParameters() throws Exception {
+ QueryTranslatorImpl qt = compile( "from Animal a where a.bodyWeight = ?1" );
+ AST ast = ( AST ) qt.getSqlAST();
+
+ // make certain that the ejb3-positional param got recognized as a named param
+ List namedParams = ASTUtil.collectChildren(
+ ast,
+ new ASTUtil.FilterPredicate() {
+ public boolean exclude(AST n) {
+ return n.getType() != HqlSqlTokenTypes.NAMED_PARAM;
+ }
+ }
+ );
+ assertTrue( "ejb3 positional param not recognized as a named param", namedParams.size() > 0 );
+ }
+
+ /**
+ * SELECT OBJECT(identifier)
+ */
+ public void testSelectObjectClause() throws Exception {
+ //parse("select object(m) from Model m");
+ assertEjbqlEqualsHql( "select object(m) from Model m", "from Model m" );
+ }
+
+ /**
+ * IN(collection_valued_path) identifier
+ */
+ public void testCollectionMemberDeclaration() throws Exception {
+ String hql = "select o from Animal a inner join a.offspring o";
+ String ejbql = "select object(o) from Animal a, in(a.offspring) o";
+ //parse(hql);
+ //parse(ejbql);
+ assertEjbqlEqualsHql( ejbql, hql );
+ }
+
+ /**
+ * collection_valued_path IS [NOT] EMPTY
+ */
+ public void testIsEmpty() throws Exception {
+ //String hql = "from Animal a where not exists (from a.offspring)";
+ String hql = "from Animal a where not exists elements(a.offspring)";
+ String ejbql = "select object(a) from Animal a where a.offspring is empty";
+ //parse(hql);
+ //parse(ejbql);
+ assertEjbqlEqualsHql(ejbql, hql);
+
+ hql = "from Animal a where exists (from a.mother.father.offspring)";
+ ejbql = "select object(a) from Animal a where a.mother.father.offspring is not empty";
+ assertEjbqlEqualsHql( ejbql, hql );
+ }
+
+ /**
+ * [NOT] MEMBER OF
+ */
+ public void testMemberOf() throws Exception {
+ String hql = "from Animal a where a.mother in (from a.offspring)";
+ //String hql = "from Animal a where a.mother in elements(a.offspring)";
+ String ejbql = "select object(a) from Animal a where a.mother member of a.offspring";
+ //parse(hql);
+ //parse(ejbql);
+ assertEjbqlEqualsHql( ejbql, hql );
+
+ hql = "from Animal a where a.mother not in (from a.offspring)";
+ //hql = "from Animal a where a.mother not in elements(a.offspring)";
+ ejbql = "select object(a) from Animal a where a.mother not member of a.offspring";
+ //parse(hql);
+ //parse(ejbql);
+ assertEjbqlEqualsHql( ejbql, hql );
+ }
+
+ /**
+ * Various functions.
+ * Tests just parsing for now which means it doesn't guarantee that the generated SQL is as expected or even valid.
+ */
+ public void testEJBQLFunctions() throws Exception {
+ String hql = "select object(a) from Animal a where a.description = concat('1', concat('2','3'), '4'||'5')||0";
+ parse( hql, false );
+ System.out.println( "sql: " + toSql( hql ) );
+
+ hql = "from Animal a where substring(a.description, 1, 3) = :p1";
+ parse( hql, false );
+ System.out.println( "sql: " + toSql( hql ) );
+
+ hql = "select substring(a.description, 1, 3) from Animal a";
+ parse( hql, false );
+ System.out.println( "sql: " + toSql( hql ) );
+
+ hql = "from Animal a where lower(a.description) = :p1";
+ parse( hql, false );
+ System.out.println( "sql: " + toSql( hql ) );
+
+ hql = "select lower(a.description) from Animal a";
+ parse( hql, false );
+ System.out.println( "sql: " + toSql( hql ) );
+
+ hql = "from Animal a where upper(a.description) = :p1";
+ parse( hql, false );
+ System.out.println( "sql: " + toSql( hql ) );
+
+ hql = "select upper(a.description) from Animal a";
+ parse( hql, false );
+ System.out.println( "sql: " + toSql( hql ) );
+
+ hql = "from Animal a where length(a.description) = :p1";
+ parse( hql, false );
+ System.out.println( "sql: " + toSql( hql ) );
+
+ hql = "select length(a.description) from Animal a";
+ parse( hql, false );
+ System.out.println( "sql: " + toSql( hql ) );
+
+ hql = "from Animal a where locate(a.description, 'abc', 2) = :p1";
+ parse( hql, false );
+ System.out.println( "sql: " + toSql( hql ) );
+
+ hql = "select locate(a.description, :p1, 2) from Animal a";
+ parse( hql, false );
+ System.out.println( "sql: " + toSql( hql ) );
+
+ hql = "select object(a) from Animal a where trim(trailing '_' from a.description) = :p1";
+ parse( hql, false );
+ System.out.println( "sql: " + toSql( hql ) );
+
+ hql = "select trim(trailing '_' from a.description) from Animal a";
+ parse( hql, false );
+ System.out.println( "sql: " + toSql( hql ) );
+
+ hql = "select object(a) from Animal a where trim(leading '_' from a.description) = :p1";
+ parse( hql, false );
+ System.out.println( "sql: " + toSql( hql ) );
+
+ hql = "select object(a) from Animal a where trim(both a.description) = :p1";
+ parse( hql, false );
+ System.out.println( "sql: " + toSql( hql ) );
+
+ hql = "select object(a) from Animal a where trim(a.description) = :p1";
+ parse( hql, false );
+ System.out.println( "sql: " + toSql( hql ) );
+
+ hql = "select object(a) from Animal a where abs(a.bodyWeight) = sqrt(a.bodyWeight)";
+ parse( hql, false );
+ System.out.println( "sql: " + toSql( hql ) );
+
+ hql = "select object(a) from Animal a where mod(a.bodyWeight, a.mother.bodyWeight) = :p1";
+ parse( hql, false );
+ System.out.println( "sql: " + toSql( hql ) );
+
+ hql = "select object(a) from Animal a where BIT_LENGTH(a.bodyWeight) = :p1";
+ parse( hql, false );
+ System.out.println( "sql: " + toSql( hql ) );
+
+ hql = "select BIT_LENGTH(a.bodyWeight) from Animal a";
+ parse( hql, false );
+ System.out.println( "sql: " + toSql( hql ) );
+
+ hql = "select object(a) from Animal a where CURRENT_DATE = :p1 or CURRENT_TIME = :p2 or CURRENT_TIMESTAMP = :p3";
+ parse( hql, false );
+ System.out.println( "sql: " + toSql( hql ) );
+
+ // todo the following is not supported
+ //hql = "select CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP from Animal a";
+ //parse(hql, true);
+ //System.out.println("sql: " + toSql(hql));
+
+ hql = "select object(a) from Animal a where a.bodyWeight like '%a%'";
+ parse( hql, false );
+ System.out.println( "sql: " + toSql( hql ) );
+
+ hql = "select object(a) from Animal a where a.bodyWeight not like '%a%'";
+ parse( hql, false );
+ System.out.println( "sql: " + toSql( hql ) );
+
+ hql = "select object(a) from Animal a where a.bodyWeight like '%a%' escape '%'";
+ parse( hql, false );
+ System.out.println( "sql: " + toSql( hql ) );
+ }
+
+ public void testTrueFalse() throws Exception {
+ assertEjbqlEqualsHql( "from Human h where h.pregnant is true", "from Human h where h.pregnant = true" );
+ assertEjbqlEqualsHql( "from Human h where h.pregnant is false", "from Human h where h.pregnant = false" );
+ assertEjbqlEqualsHql( "from Human h where not(h.pregnant is true)", "from Human h where not( h.pregnant=true )" );
+ }
+
+
+ // Private
+
+ private void assertEjbqlEqualsHql(String ejbql, String hql) {
+ QueryTranslatorFactory ast = new ASTQueryTranslatorFactory();
+
+ QueryTranslator queryTranslator = ast.createQueryTranslator( hql, hql, Collections.EMPTY_MAP, sfi() );
+ queryTranslator.compile( Collections.EMPTY_MAP, true );
+ String hqlSql = queryTranslator.getSQLString();
+
+ queryTranslator = ast.createQueryTranslator( ejbql, ejbql, Collections.EMPTY_MAP, sfi() );
+ queryTranslator.compile( Collections.EMPTY_MAP, true );
+ String ejbqlSql = queryTranslator.getSQLString();
+
+ assertEquals( hqlSql, ejbqlSql );
+ }
+
+ private QueryTranslatorImpl compile(String input) {
+ QueryTranslatorFactory ast = new ASTQueryTranslatorFactory();
+ QueryTranslator queryTranslator = ast.createQueryTranslator( input, input, Collections.EMPTY_MAP, sfi() );
+ queryTranslator.compile( Collections.EMPTY_MAP, true );
+
+ return ( QueryTranslatorImpl ) queryTranslator;
+ }
+
+ private AST parse(String input, boolean logging) throws RecognitionException, TokenStreamException {
+ if ( logging ) {
+ System.out.println( "input: ->" + input + "<-" );
+ }
+
+ HqlParser parser = HqlParser.getInstance( input );
+ parser.setFilter( false );
+ parser.statement();
+ AST ast = parser.getAST();
+
+ if ( logging ) {
+ System.out.println( "AST : " + ast.toStringTree() + "" );
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ parser.showAst( ast, new PrintStream( baos ) );
+ System.out.println( baos.toString() );
+ }
+
+ assertEquals( "At least one error occurred during parsing!", 0, parser.getParseErrorHandler().getErrorCount() );
+
+ return ast;
+ }
+
+ private String toSql(String hql) {
+ QueryTranslatorFactory ast = new ASTQueryTranslatorFactory();
+ QueryTranslator queryTranslator = ast.createQueryTranslator( hql, hql, Collections.EMPTY_MAP, sfi() );
+ queryTranslator.compile( Collections.EMPTY_MAP, true );
+ return queryTranslator.getSQLString();
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/EJBQLTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/EntityWithCrazyCompositeKey.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/EntityWithCrazyCompositeKey.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/EntityWithCrazyCompositeKey.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping SYSTEM "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
+
+<hibernate-mapping package="org.hibernate.test.hql">
+
+ <class name="EntityWithCrazyCompositeKey">
+ <composite-id name="id" class="CrazyCompositeKey">
+ <key-property name="id" column="id"/>
+ <key-property name="otherId" column="other_id"/>
+ </composite-id>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/EntityWithCrazyCompositeKey.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/EntityWithCrazyCompositeKey.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/EntityWithCrazyCompositeKey.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,28 @@
+// $Id: EntityWithCrazyCompositeKey.java 6567 2005-04-27 17:41:57Z steveebersole $
+package org.hibernate.test.hql;
+
+/**
+ * Implementation of EntityWithCrazyCompositeKey.
+ *
+ * @author Steve Ebersole
+ */
+public class EntityWithCrazyCompositeKey {
+ private CrazyCompositeKey id;
+ private String name;
+
+ public CrazyCompositeKey getId() {
+ return id;
+ }
+
+ public void setId(CrazyCompositeKey id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/FooBarCopy.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/FooBarCopy.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/FooBarCopy.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,137 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping default-lazy="false" package="org.hibernate.test.legacy">
+
+ <!-- a slightly modified copy of FooBar.hbm.xml from the legacy test package -->
+
+ <class
+ name="Foo"
+ table="`foos`"
+ proxy="FooProxy"
+ discriminator-value="F"
+ batch-size="4"
+ dynamic-insert="true"
+ dynamic-update="true"
+ select-before-update="true">
+
+ <id name="key" type="string">
+ <column name="`foo_idcolumnname123`" length="36"/>
+ <generator class="uuid.hex">
+ <param name="seperator">:</param>
+ </generator>
+ </id>
+
+ <discriminator column="`foo_subclass_1234`" type="character" force="true"/>
+ <version name="version"/>
+
+ <many-to-one name="foo" class="Foo">
+ <column name="foo" length="36" index="fbmtoidx"/>
+ </many-to-one>
+
+ <property name="long">
+ <column name="long_" index="fbmtoidx" unique-key="abc" not-null="true"/>
+ </property>
+ <property name="integer">
+ <column name="`integer__`" unique-key="abc" not-null="true"/>
+ </property>
+ <property name="float">
+ <column name="float_" unique-key="abc" not-null="true" check="float_ > 0.0"/>
+ </property>
+ <property name="x"/>
+ <property name="double" column="double_"/>
+
+ <primitive-array name="bytes" table="foobytes">
+ <key column="id"/>
+ <index column="i"/>
+ <element column="byte_" type="byte"/>
+ </primitive-array>
+
+ <property name="date" type="date" column="date_"/>
+ <property name="timestamp" type="timestamp" column="timestamp_"/>
+ <property name="boolean" column="boolean_"/>
+ <property name="bool" column="bool_"/>
+ <property name="null" column="null_"/>
+ <property name="short" column="short_"/>
+ <property name="char" column="char_"/>
+ <property name="zero" column="zero_"/>
+ <property name="int" column="int_"/>
+ <property name="string">
+ <column name="string_" length="48" index="fbstridx"/>
+ </property>
+ <property name="byte" column="byte_"/>
+ <property name="yesno" type="yes_no"/>
+ <property name="blob" type="org.hibernate.test.legacy.Foo$Struct" column="blobb_"/>
+ <property name="nullBlob" type="serializable"/>
+ <property name="binary" column="bin_"/>
+ <property name="theLocale" access="field" column="`localeayzabc123`"/>
+
+ <property name="formula" formula="int_/2"/>
+
+ <property name="custom" type="org.hibernate.test.legacy.DoubleStringType" access="field">
+ <column name="first_name" length="66"/>
+ <column name="surname" length="66"/>
+ </property>
+ <component name="nullComponent">
+ <property name="name" column="null_cmpnt_"/>
+ </component>
+
+ <join table="jointable">
+ <key column="fooid" on-delete="cascade"/>
+ <property name="joinedProp"/>
+ </join>
+
+ <subclass
+ name="Trivial"
+ proxy="FooProxy"
+ discriminator-value="T"/>
+
+ <subclass
+ name="Abstract"
+ proxy="AbstractProxy"
+ discriminator-value="null">
+ <set name="abstracts" batch-size="2">
+ <key column="abstract_id"/>
+ <one-to-many class="Abstract"/>
+ </set>
+ <property name="time" column="the_time"/>
+
+ <subclass
+ name="Bar"
+ proxy="BarProxy"
+ discriminator-value="B">
+ <property name="barString">
+ <column name="bar_string" length="24"/>
+ </property>
+ <any name="object" meta-type="character" id-type="long" cascade="all">
+ <meta-value value="O" class="One"/>
+ <meta-value value="M" class="Many"/>
+ <column name="clazz" length="100"/>
+ <column name="gen_id"/>
+ </any>
+ <join table="bar_join_table">
+ <key column="bar_id"/>
+ <property name="name" column="name_name"/>
+ </join>
+ </subclass>
+ </subclass>
+ </class>
+
+ <class name="One" table="one">
+ <id name="key" column="one_key">
+ <generator class="native" />
+ </id>
+ <property name="x"/>
+ <property column="one_value" name="value"/>
+ </class>
+
+ <class name="Many" table="many">
+ <id name="key" column="many_key">
+ <generator class="native" />
+ </id>
+ <property name="x"/>
+ </class>
+
+</hibernate-mapping>
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/HQLSuite.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/HQLSuite.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/HQLSuite.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,29 @@
+// $Id: HQLSuite.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.hql;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * The full suite of tests against the Antlr grammar
+ *
+ * @author Steve Ebersole
+ */
+public class HQLSuite {
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite( "Antlr HQL grammar tests" );
+ suite.addTest( HQLTest.suite() );
+ suite.addTest( ASTParserLoadingTest.suite() );
+ suite.addTest( BulkManipulationTest.suite() );
+ suite.addTest( WithClauseTest.suite() );
+// suite.addTest( ASTQueryTranslatorTest.suite() );
+ suite.addTest( EJBQLTest.suite() );
+ suite.addTest( HqlParserTest.suite() );
+ suite.addTest( ScrollableCollectionFetchingTest.suite() );
+ suite.addTest( ClassicTranslatorTest.suite() );
+ suite.addTest( CriteriaHQLAlignmentTest.suite() );
+ suite.addTest( CriteriaClassicAggregationReturnTest.suite() );
+ return suite;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/HQLTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/HQLTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/HQLTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1342 @@
+//$Id: HQLTest.java 11374 2007-03-29 19:09:18Z steve.ebersole at jboss.com $
+package org.hibernate.test.hql;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import antlr.RecognitionException;
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.QueryException;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.dialect.DB2Dialect;
+import org.hibernate.dialect.HSQLDialect;
+import org.hibernate.dialect.MySQLDialect;
+import org.hibernate.dialect.Oracle9Dialect;
+import org.hibernate.dialect.PostgreSQLDialect;
+import org.hibernate.dialect.SybaseDialect;
+import org.hibernate.dialect.function.SQLFunction;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.query.HQLQueryPlan;
+import org.hibernate.engine.query.ReturnMetadata;
+import org.hibernate.hql.QueryTranslator;
+import org.hibernate.hql.QueryTranslatorFactory;
+import org.hibernate.hql.ast.ASTQueryTranslatorFactory;
+import org.hibernate.hql.ast.DetailedSemanticException;
+import org.hibernate.hql.ast.QuerySyntaxException;
+import org.hibernate.hql.ast.QueryTranslatorImpl;
+import org.hibernate.hql.ast.tree.ConstructorNode;
+import org.hibernate.hql.ast.tree.DotNode;
+import org.hibernate.hql.ast.tree.IndexNode;
+import org.hibernate.hql.ast.tree.SelectClause;
+import org.hibernate.hql.ast.tree.FromReferenceNode;
+
+/**
+ * Tests cases where the AST based query translator and the 'classic' query translator generate identical SQL.
+ *
+ * @author Gavin King
+ */
+public class HQLTest extends QueryTranslatorTestCase {
+
+ public HQLTest(String x) {
+ super( x );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( HQLTest.class );
+ }
+
+ public boolean createSchema() {
+ return false;
+ }
+
+ public boolean recreateSchemaAfterFailure() {
+ return false;
+ }
+
+ protected void prepareTest() throws Exception {
+ super.prepareTest();
+ SelectClause.VERSION2_SQL = true;
+ DotNode.REGRESSION_STYLE_JOIN_SUPPRESSION = true;
+ DotNode.ILLEGAL_COLL_DEREF_EXCP_BUILDER = new DotNode.IllegalCollectionDereferenceExceptionBuilder() {
+ public QueryException buildIllegalCollectionDereferenceException(String propertyName, FromReferenceNode lhs) {
+ throw new QueryException( "illegal syntax near collection: " + propertyName );
+ }
+ };
+ }
+
+ protected void cleanupTest() throws Exception {
+ SelectClause.VERSION2_SQL = false;
+ DotNode.REGRESSION_STYLE_JOIN_SUPPRESSION = false;
+ DotNode.ILLEGAL_COLL_DEREF_EXCP_BUILDER = DotNode.DEF_ILLEGAL_COLL_DEREF_EXCP_BUILDER;
+ super.cleanupTest();
+ }
+
+ public void testInvalidCollectionDereferencesFail() {
+ // should fail with the same exceptions (because of the DotNode.ILLEGAL_COLL_DEREF_EXCP_BUILDER injection)
+ assertTranslation( "from Animal a where a.offspring.description = 'xyz'" );
+ assertTranslation( "from Animal a where a.offspring.father.description = 'xyz'" );
+ }
+
+ public void testSubComponentReferences() {
+ assertTranslation( "select c.address.zip.code from ComponentContainer c" );
+ assertTranslation( "select c.address.zip from ComponentContainer c" );
+ assertTranslation( "select c.address from ComponentContainer c" );
+ }
+
+ public void testManyToAnyReferences() {
+ assertTranslation( "from PropertySet p where p.someSpecificProperty.id is not null" );
+ assertTranslation( "from PropertySet p join p.generalProperties gp where gp.id is not null" );
+ }
+
+ public void testJoinFetchCollectionOfValues() {
+ assertTranslation( "select h from Human as h join fetch h.nickNames" );
+ }
+
+ public void testCollectionJoinsInSubselect() {
+ // caused by some goofiness in FromElementFactory that tries to
+ // handle correlated subqueries (but fails miserably) even though this
+ // is not a correlated subquery. HHH-1248
+ assertTranslation(
+ "select a.id, a.description" +
+ " from Animal a" +
+ " left join a.offspring" +
+ " where a in (" +
+ " select a1 from Animal a1" +
+ " left join a1.offspring o" +
+ " where a1.id=1" +
+ ")"
+ );
+ assertTranslation(
+ "select h.id, h.description" +
+ " from Human h" +
+ " left join h.friends" +
+ " where h in (" +
+ " select h1" +
+ " from Human h1" +
+ " left join h1.friends f" +
+ " where h1.id=1" +
+ ")"
+ );
+ }
+
+ public void testEmptyInListFailureExpected() {
+ assertTranslation( "select a from Animal a where a.description in ()" );
+ }
+
+ public void testDateTimeArithmeticReturnTypesAndParameterGuessing() {
+ QueryTranslatorImpl translator = createNewQueryTranslator( "select o.orderDate - o.orderDate from Order o" );
+ assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
+ assertEquals( "incorrect return type", Hibernate.DOUBLE, translator.getReturnTypes()[0] );
+ translator = createNewQueryTranslator( "select o.orderDate + 2 from Order o" );
+ assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
+ assertEquals( "incorrect return type", Hibernate.CALENDAR_DATE, translator.getReturnTypes()[0] );
+ translator = createNewQueryTranslator( "select o.orderDate -2 from Order o" );
+ assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
+ assertEquals( "incorrect return type", Hibernate.CALENDAR_DATE, translator.getReturnTypes()[0] );
+
+ translator = createNewQueryTranslator( "from Order o where o.orderDate > ?" );
+ assertEquals( "incorrect expected param type", Hibernate.CALENDAR_DATE, translator.getParameterTranslations().getOrdinalParameterExpectedType( 1 ) );
+
+ translator = createNewQueryTranslator( "select o.orderDate + ? from Order o" );
+ assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
+ assertEquals( "incorrect return type", Hibernate.CALENDAR_DATE, translator.getReturnTypes()[0] );
+ assertEquals( "incorrect expected param type", Hibernate.DOUBLE, translator.getParameterTranslations().getOrdinalParameterExpectedType( 1 ) );
+
+ }
+
+ public void testReturnMetadata() {
+ HQLQueryPlan plan = createQueryPlan( "from Animal a" );
+ check( plan.getReturnMetadata(), false, true );
+
+ plan = createQueryPlan( "select a as animal from Animal a" );
+ check( plan.getReturnMetadata(), false, false );
+
+ plan = createQueryPlan( "from java.lang.Object" );
+ check( plan.getReturnMetadata(), true, true );
+
+ plan = createQueryPlan( "select o as entity from java.lang.Object o" );
+ check( plan.getReturnMetadata(), true, false );
+ }
+
+ private void check(
+ ReturnMetadata returnMetadata,
+ boolean expectingEmptyTypes,
+ boolean expectingEmptyAliases) {
+ assertNotNull( "null return metadata", returnMetadata );
+ assertNotNull( "null return metadata - types", returnMetadata );
+ assertEquals( "unexpected return size", 1, returnMetadata.getReturnTypes().length );
+ if ( expectingEmptyTypes ) {
+ assertNull( "non-empty types", returnMetadata.getReturnTypes()[0] );
+ }
+ else {
+ assertNotNull( "empty types", returnMetadata.getReturnTypes()[0] );
+ }
+ if ( expectingEmptyAliases ) {
+ assertNull( "non-empty aliases", returnMetadata.getReturnAliases() );
+ }
+ else {
+ assertNotNull( "empty aliases", returnMetadata.getReturnAliases() );
+ assertNotNull( "empty aliases", returnMetadata.getReturnAliases()[0] );
+ }
+ }
+
+ public void testImplicitJoinsAlongWithCartesianProduct() {
+ DotNode.useThetaStyleImplicitJoins = true;
+ assertTranslation( "select foo.foo from Foo foo, Foo foo2" );
+ assertTranslation( "select foo.foo.foo from Foo foo, Foo foo2" );
+ DotNode.useThetaStyleImplicitJoins = false;
+ }
+
+ public void testSubselectBetween() {
+ assertTranslation("from Animal x where (select max(a.bodyWeight) from Animal a) between :min and :max");
+ assertTranslation("from Animal x where (select max(a.description) from Animal a) like 'big%'");
+ assertTranslation("from Animal x where (select max(a.bodyWeight) from Animal a) is not null");
+ assertTranslation("from Animal x where exists (select max(a.bodyWeight) from Animal a)");
+ assertTranslation("from Animal x where (select max(a.bodyWeight) from Animal a) in (1,2,3)");
+ }
+
+ public void testFetchOrderBy() {
+ assertTranslation("from Animal a left outer join fetch a.offspring where a.mother.id = :mid order by a.description");
+ }
+
+ public void testCollectionOrderBy() {
+ assertTranslation("from Animal a join a.offspring o order by a.description");
+ assertTranslation("from Animal a join fetch a.offspring order by a.description");
+ assertTranslation("from Animal a join fetch a.offspring o order by o.description");
+ assertTranslation("from Animal a join a.offspring o order by a.description, o.description");
+ }
+
+ public void testExpressionWithParamInFunction() {
+ assertTranslation("from Animal a where abs(a.bodyWeight-:param) < 2.0");
+ assertTranslation("from Animal a where abs(:param - a.bodyWeight) < 2.0");
+ assertTranslation("from Animal where abs(:x - :y) < 2.0");
+ assertTranslation("from Animal where lower(upper(:foo)) like 'f%'");
+ if ( ! ( getDialect() instanceof SybaseDialect ) ) {
+ // SybaseDialect maps the length function -> len; classic translator does not consider that *when nested*
+ assertTranslation("from Animal a where abs(abs(a.bodyWeight - 1.0 + :param) * abs(length('ffobar')-3)) = 3.0");
+ }
+ if ( !( getDialect() instanceof MySQLDialect || getDialect() instanceof SybaseDialect ) ) {
+ assertTranslation("from Animal where lower(upper('foo') || upper(:bar)) like 'f%'");
+ }
+ if ( getDialect() instanceof PostgreSQLDialect ) {
+ return;
+ }
+ assertTranslation("from Animal where abs(cast(1 as float) - cast(:param as float)) = 1.0");
+ }
+
+ public void testCompositeKeysWithPropertyNamedId() {
+ assertTranslation( "select e.id.id from EntityWithCrazyCompositeKey e" );
+ assertTranslation( "select max(e.id.id) from EntityWithCrazyCompositeKey e" );
+ }
+
+ public void testMaxindexHqlFunctionInElementAccessorFailureExpected() {
+ //TODO: broken SQL
+ // steve (2005.10.06) - this is perfect SQL, but fairly different from the old parser
+ // tested : HSQLDB (1.8), Oracle8i
+ assertTranslation( "select c from ContainerX c where c.manyToMany[ maxindex(c.manyToMany) ].count = 2" );
+ assertTranslation( "select c from Container c where c.manyToMany[ maxIndex(c.manyToMany) ].count = 2" );
+ }
+
+ public void testMultipleElementAccessorOperatorsFailureExpected() throws Exception {
+ //TODO: broken SQL
+ // steve (2005.10.06) - Yes, this is all hosed ;)
+ assertTranslation( "select c from ContainerX c where c.oneToMany[ c.manyToMany[0].count ].name = 's'" );
+ assertTranslation( "select c from ContainerX c where c.manyToMany[ c.oneToMany[0].count ].name = 's'" );
+ }
+
+ /*public void testSelectMaxElements() throws Exception {
+ //TODO: this is almost correct, but missing a select-clause column alias!
+ assertTranslation("select max( elements(one.manies) ) from org.hibernate.test.legacy.One one");
+ }*/
+
+ public void testKeyManyToOneJoinFailureExpected() {
+ //TODO: new parser generates unnecessary joins (though the query results are correct)
+ assertTranslation( "from Order o left join fetch o.lineItems li left join fetch li.product p" );
+ assertTranslation( "from Outer o where o.id.master.id.sup.dudu is not null" );
+ assertTranslation( "from Outer o where o.id.master.id.sup.dudu is not null" );
+ }
+
+ public void testDuplicateExplicitJoinFailureExpected() throws Exception {
+ //very minor issue with select clause:
+ assertTranslation( "from Animal a join a.mother m1 join a.mother m2" );
+ assertTranslation( "from Zoo zoo join zoo.animals an join zoo.mammals m" );
+ assertTranslation( "from Zoo zoo join zoo.mammals an join zoo.mammals m" );
+ }
+
+ // TESTS THAT FAIL ONLY ON DIALECTS WITH THETA-STYLE OUTERJOINS:
+
+ public void testIndexWithExplicitJoin() throws Exception {
+ //TODO: broken on dialects with theta-style outerjoins:
+ // steve (2005.10.06) - this works perfectly for me on Oracle8i
+ assertTranslation( "from Zoo zoo join zoo.animals an where zoo.mammals[ index(an) ] = an" );
+ assertTranslation( "from Zoo zoo join zoo.mammals dog where zoo.mammals[ index(dog) ] = dog" );
+ assertTranslation( "from Zoo zoo join zoo.mammals dog where dog = zoo.mammals[ index(dog) ]" );
+ }
+
+ public void testOneToManyMapIndex() throws Exception {
+ //TODO: this breaks on dialects with theta-style outerjoins:
+ // steve (2005.10.06) - this works perfectly for me on Oracle8i
+ assertTranslation( "from Zoo zoo where zoo.mammals['dog'].description like '%black%'" );
+ assertTranslation( "from Zoo zoo where zoo.mammals['dog'].father.description like '%black%'" );
+ assertTranslation( "from Zoo zoo where zoo.mammals['dog'].father.id = 1234" );
+ assertTranslation( "from Zoo zoo where zoo.animals['1234'].description like '%black%'" );
+ }
+
+ public void testExplicitJoinMapIndex() throws Exception {
+ //TODO: this breaks on dialects with theta-style outerjoins:
+ // steve (2005.10.06) - this works perfectly for me on Oracle8i
+ assertTranslation( "from Zoo zoo, Dog dog where zoo.mammals['dog'] = dog" );
+ assertTranslation( "from Zoo zoo join zoo.mammals dog where zoo.mammals['dog'] = dog" );
+ }
+
+ public void testIndexFunction() throws Exception {
+ // Instead of doing the pre-processor trick like the existing QueryTranslator, this
+ // is handled by MethodNode.
+ // steve (2005.10.06) - this works perfectly for me on Oracle8i
+ //TODO: broken on dialects with theta-style outerjoins:
+ assertTranslation( "from Zoo zoo join zoo.mammals dog where index(dog) = 'dog'" );
+ assertTranslation( "from Zoo zoo join zoo.animals an where index(an) = '1234'" );
+ }
+
+ public void testSelectCollectionOfValues() throws Exception {
+ //TODO: broken on dialects with theta-style joins
+ ///old parser had a bug where the collection element was not included in return types!
+ // steve (2005.10.06) - this works perfectly for me on Oracle8i
+ assertTranslation( "select baz, date from Baz baz join baz.stringDateMap date where index(date) = 'foo'" );
+ }
+
+ public void testCollectionOfValues() throws Exception {
+ //old parser had a bug where the collection element was not returned!
+ //TODO: broken on dialects with theta-style joins
+ // steve (2005.10.06) - this works perfectly for me on Oracle8i
+ assertTranslation( "from Baz baz join baz.stringDateMap date where index(date) = 'foo'" );
+ }
+
+ public void testHHH719() throws Exception {
+ assertTranslation("from Baz b order by org.bazco.SpecialFunction(b.id)");
+ assertTranslation("from Baz b order by anypackage.anyFunction(b.id)");
+ }
+
+
+ //PASSING TESTS:
+
+ public void testParameterListExpansion() {
+ assertTranslation( "from Animal as animal where animal.id in (:idList_1, :idList_2)" );
+ }
+
+ public void testComponentManyToOneDereferenceShortcut() {
+ assertTranslation( "from Zoo z where z.address.stateProvince.id is null" );
+ }
+
+ public void testNestedCollectionImplicitJoins() {
+ // HHH-770
+ assertTranslation( "select h.friends.offspring from Human h" );
+ }
+
+ public void testExplicitJoinsInSubquery() {
+ // test for HHH-557,
+ // TODO : this passes regardless because the only difference between the two sqls is one extra comma
+ // (commas are eaten by the tokenizer during asserTranslation when building the token maps).
+ assertTranslation(
+ "from org.hibernate.test.hql.Animal as animal " +
+ "where animal.id in (" +
+ " select a.id " +
+ " from org.hibernate.test.hql.Animal as a " +
+ " left join a.mother as mo" +
+ ")"
+ );
+ }
+
+ public void testImplicitJoinsInGroupBy() {
+ assertTranslation(
+ "select o.mother.bodyWeight, count(distinct o) " +
+ "from Animal an " +
+ " join an.offspring as o " +
+ "group by o.mother.bodyWeight"
+ );
+ }
+
+ public void testCrazyIdFieldNames() {
+ DotNode.useThetaStyleImplicitJoins = true;
+ // only regress against non-scalar forms as there appears to be a bug in the classic translator
+ // in regards to this issue also. Specifically, it interprets the wrong return type, though it gets
+ // the sql "correct" :/
+
+ String hql = "select e.heresAnotherCrazyIdFieldName from MoreCrazyIdFieldNameStuffEntity e where e.heresAnotherCrazyIdFieldName is not null";
+ assertTranslation( hql, new HashMap(), false, ( String ) null );
+
+ hql = "select e.heresAnotherCrazyIdFieldName.heresAnotherCrazyIdFieldName from MoreCrazyIdFieldNameStuffEntity e where e.heresAnotherCrazyIdFieldName is not null";
+ assertTranslation( hql, new HashMap(), false, ( String ) null );
+
+ DotNode.useThetaStyleImplicitJoins = false;
+ }
+
+ public void testSizeFunctionAndProperty() {
+ assertTranslation("from Animal a where a.offspring.size > 0");
+ assertTranslation("from Animal a join a.offspring where a.offspring.size > 1");
+ assertTranslation("from Animal a where size(a.offspring) > 0");
+ assertTranslation("from Animal a join a.offspring o where size(a.offspring) > 1");
+ assertTranslation("from Animal a where size(a.offspring) > 1 and size(a.offspring) < 100");
+
+ assertTranslation("from Human a where a.family.size > 0");
+ assertTranslation("from Human a join a.family where a.family.size > 1");
+ assertTranslation("from Human a where size(a.family) > 0");
+ assertTranslation("from Human a join a.family o where size(a.family) > 1");
+ assertTranslation("from Human a where a.family.size > 0 and a.family.size < 100");
+}
+
+ // Do the simplest test first!
+ public void testFromOnly() throws Exception {
+ // 2004-06-21 [jsd] This test now works with the new AST based QueryTranslatorImpl.
+ assertTranslation( "from Animal" );
+ assertTranslation( "from Model" );
+ }
+
+ public void testJoinPathEndingInValueCollection() {
+ assertTranslation( "select h from Human as h join h.nickNames as nn where h.nickName=:nn1 and (nn=:nn2 or nn=:nn3)" );
+ }
+
+ public void testSerialJoinPathEndingInValueCollection() {
+ // HHH-242
+ assertTranslation( "select h from Human as h join h.friends as f join f.nickNames as nn where h.nickName=:nn1 and (nn=:nn2 or nn=:nn3)" );
+ }
+
+ public void testImplicitJoinContainedByCollectionFunction() {
+ // HHH-281 : Implied joins in a collection function (i.e., indices or elements)
+ assertTranslation( "from Human as h where 'shipping' in indices(h.father.addresses)" );
+ assertTranslation( "from Human as h where 'shipping' in indices(h.father.father.addresses)" );
+ assertTranslation( "from Human as h where 'sparky' in elements(h.father.nickNames)" );
+ assertTranslation( "from Human as h where 'sparky' in elements(h.father.father.nickNames)" );
+ }
+
+
+ public void testImpliedJoinInSubselectFrom() {
+ // HHH-276 : Implied joins in a from in a subselect.
+ assertTranslation( "from Animal a where exists( from a.mother.offspring )" );
+ }
+
+ public void testSubselectImplicitJoins() {
+ // HHH-276 : Implied joins in a from in a subselect.
+ assertTranslation( "from Simple s where s = some( select sim from Simple sim where sim.other.count=s.other.count )" );
+ }
+
+
+ public void testCollectionOfValuesSize() throws Exception {
+ //SQL *was* missing a comma
+ assertTranslation( "select size(baz.stringDateMap) from org.hibernate.test.legacy.Baz baz" );
+ }
+
+ public void testCollectionFunctions() throws Exception {
+ //these are both broken, a join that belongs in the subselect finds its way into the main query
+ assertTranslation( "from Zoo zoo where size(zoo.animals) > 100" );
+ assertTranslation( "from Zoo zoo where maxindex(zoo.mammals) = 'dog'" );
+ }
+
+ public void testImplicitJoinInExplicitJoin() throws Exception {
+ assertTranslation( "from Animal an inner join an.mother.mother gm" );
+ assertTranslation( "from Animal an inner join an.mother.mother.mother ggm" );
+ assertTranslation( "from Animal an inner join an.mother.mother.mother.mother gggm" );
+ }
+
+ public void testImpliedManyToManyProperty() throws Exception {
+ //missing a table join (SQL correct for a one-to-many, not for a many-to-many)
+ assertTranslation( "select c from ContainerX c where c.manyToMany[0].name = 's'" );
+ }
+
+ public void testCollectionSize() throws Exception {
+ //SQL is correct, query spaces *was* missing a table
+ assertTranslation( "select size(zoo.animals) from Zoo zoo" );
+ }
+
+ /*public void testCollectionIndexFunctionsInSelect() throws Exception {
+ assertTranslation("select maxindex(zoo.animals) from Zoo zoo");
+ assertTranslation("select minindex(zoo.animals) from Zoo zoo");
+ assertTranslation("select indices(zoo.animals) from Zoo zoo");
+ }
+
+ public void testCollectionElementFunctionsInSelect() throws Exception {
+ assertTranslation("select maxelement(zoo.animals) from Zoo zoo");
+ assertTranslation("select minelement(zoo.animals) from Zoo zoo");
+ assertTranslation("select elements(zoo.animals) from Zoo zoo");
+ }*/
+
+ public void testFetchCollectionOfValues() throws Exception {
+ assertTranslation( "from Baz baz left join fetch baz.stringSet" );
+ }
+
+ public void testFetchList() throws Exception {
+ assertTranslation( "from User u join fetch u.permissions" );
+ }
+
+ public void testCollectionFetchWithExplicitThetaJoin() {
+ assertTranslation( "select m from Master m1, Master m left join fetch m.details where m.name=m1.name" );
+ }
+
+ /*public void testListElementFunctionInSelect() throws Exception {
+ //wrong pk column in select clause! (easy fix?)
+ assertTranslation("select maxelement(u.permissions) from User u");
+ assertTranslation("select elements(u.permissions) from User u");
+ }*/
+
+ public void testListElementFunctionInWhere() throws Exception {
+ assertTranslation( "from User u where 'read' in elements(u.permissions)" );
+ assertTranslation( "from User u where 'write' <> all elements(u.permissions)" );
+ }
+
+ /*public void testManyToManyElementFunctionInSelect() throws Exception {
+ assertTranslation("select maxelement(human.friends) from Human human");
+ assertTranslation("select elements(human.friends) from Human human");
+ }*/
+
+ public void testManyToManyMaxElementFunctionInWhere() throws Exception {
+ //completely broken!!
+ assertTranslation( "from Human human where 5 = maxelement(human.friends)" );
+ }
+
+ public void testCollectionIndexFunctionsInWhere() throws Exception {
+ assertTranslation( "from Zoo zoo where 4 = maxindex(zoo.animals)" );
+ assertTranslation( "from Zoo zoo where 2 = minindex(zoo.animals)" );
+ }
+
+ public void testCollectionIndicesInWhere() throws Exception {
+ assertTranslation( "from Zoo zoo where 4 > some indices(zoo.animals)" );
+ assertTranslation( "from Zoo zoo where 4 > all indices(zoo.animals)" );
+ }
+
+ public void testIndicesInWhere() throws Exception {
+ assertTranslation( "from Zoo zoo where 4 in indices(zoo.animals)" );
+ assertTranslation( "from Zoo zoo where exists indices(zoo.animals)" );
+ }
+
+ public void testCollectionElementInWhere() throws Exception {
+ assertTranslation( "from Zoo zoo where 4 > some elements(zoo.animals)" );
+ assertTranslation( "from Zoo zoo where 4 > all elements(zoo.animals)" );
+ }
+
+ public void testElementsInWhere() throws Exception {
+ assertTranslation( "from Zoo zoo where 4 in elements(zoo.animals)" );
+ assertTranslation( "from Zoo zoo where exists elements(zoo.animals)" );
+ }
+
+ public void testNull() throws Exception {
+ assertTranslation( "from Human h where h.nickName is null" );
+ assertTranslation( "from Human h where h.nickName is not null" );
+ }
+
+ public void testSubstitutions() throws Exception {
+ Map replacements = buildTrueFalseReplacementMapForDialect();
+ replacements.put("yes", "'Y'");
+ assertTranslation( "from Human h where h.pregnant = true", replacements );
+ assertTranslation( "from Human h where h.pregnant = yes", replacements );
+ assertTranslation( "from Human h where h.pregnant = foo", replacements );
+ }
+
+ public void testWhere() throws Exception {
+ assertTranslation( "from Animal an where an.bodyWeight > 10" );
+ // 2004-06-26 [jsd] This one requires NOT GT => LE transform.
+ assertTranslation( "from Animal an where not an.bodyWeight > 10" );
+ assertTranslation( "from Animal an where an.bodyWeight between 0 and 10" );
+ assertTranslation( "from Animal an where an.bodyWeight not between 0 and 10" );
+ assertTranslation( "from Animal an where sqrt(an.bodyWeight)/2 > 10" );
+ // 2004-06-27 [jsd] Recognize 'is null' properly. Generate 'and' and 'or' as well.
+ assertTranslation( "from Animal an where (an.bodyWeight > 10 and an.bodyWeight < 100) or an.bodyWeight is null" );
+ }
+
+ public void testEscapedQuote() throws Exception {
+ assertTranslation( "from Human h where h.nickName='1 ov''tha''few'");
+ }
+
+ public void testCaseWhenElse() {
+ assertTranslation( "from Human h where case when h.nickName='1ovthafew' then 'Gavin' when h.nickName='turin' then 'Christian' else h.nickName end = h.name.first" );
+ }
+
+ public void testCaseExprWhenElse() {
+ assertTranslation( "from Human h where case h.nickName when '1ovthafew' then 'Gavin' when 'turin' then 'Christian' else h.nickName end = h.name.first" );
+ }
+
+ public void testInvalidHql() throws Exception {
+ Exception newException = compileBadHql( "from Animal foo where an.bodyWeight > 10", false );
+ assertTrue( "Wrong exception type!", newException instanceof QuerySyntaxException );
+ newException = compileBadHql( "select an.name from Animal foo", false );
+ assertTrue( "Wrong exception type!", newException instanceof QuerySyntaxException );
+ newException = compileBadHql( "from Animal foo where an.verybogus > 10", false );
+ assertTrue( "Wrong exception type!", newException instanceof QuerySyntaxException );
+ newException = compileBadHql( "select an.boguspropertyname from Animal foo", false );
+ assertTrue( "Wrong exception type!", newException instanceof QuerySyntaxException );
+ newException = compileBadHql( "select an.name", false );
+ assertTrue( "Wrong exception type!", newException instanceof QuerySyntaxException );
+ newException = compileBadHql( "from Animal an where (((an.bodyWeight > 10 and an.bodyWeight < 100)) or an.bodyWeight is null", false );
+ assertTrue( "Wrong exception type!", newException instanceof QuerySyntaxException );
+ newException = compileBadHql( "from Animal an where an.bodyWeight is null where an.bodyWeight is null", false );
+ assertTrue( "Wrong exception type!", newException instanceof QuerySyntaxException );
+ newException = compileBadHql( "from where name='foo'", false );
+ assertTrue( "Wrong exception type!", newException instanceof QuerySyntaxException );
+ newException = compileBadHql( "from NonexistentClass where name='foo'", false );
+ assertTrue( "Wrong exception type!", newException instanceof QuerySyntaxException );
+ newException = compileBadHql( "select new FOO_BOGUS_Animal(an.description, an.bodyWeight) from Animal an", false );
+ assertTrue( "Wrong exception type!", newException instanceof QuerySyntaxException );
+ newException = compileBadHql( "select new Animal(an.description, an.bodyWeight, 666) from Animal an", false );
+ assertTrue( "Wrong exception type!", newException instanceof QuerySyntaxException );
+
+ }
+
+ public void testWhereBetween() throws Exception {
+ // 2004-08-31 [jsd] This "just worked"! Woohoo!
+ assertTranslation( "from Animal an where an.bodyWeight between 1 and 10" );
+ }
+
+ public void testConcatenation() {
+ if ( getDialect() instanceof MySQLDialect || getDialect() instanceof SybaseDialect ) {
+ // MySQL uses concat(x, y, z)
+ // SQL Server replaces '||' with '+'
+ //
+ // this is syntax checked in {@link ASTParserLoadingTest#testConcatenation}
+ return;
+ }
+ assertTranslation("from Human h where h.nickName = '1' || 'ov' || 'tha' || 'few'");
+ }
+
+ public void testWhereLike() throws Exception {
+ assertTranslation( "from Animal a where a.description like '%black%'" );
+ assertTranslation( "from Animal an where an.description like '%fat%'" );
+ assertTranslation( "from Animal an where lower(an.description) like '%fat%'" );
+ }
+
+ public void testWhereIn() throws Exception {
+ assertTranslation( "from Animal an where an.description in ('fat', 'skinny')" );
+ }
+
+ public void testLiteralInFunction() throws Exception {
+ assertTranslation( "from Animal an where an.bodyWeight > abs(5)" );
+ assertTranslation( "from Animal an where an.bodyWeight > abs(-5)" );
+ }
+
+ public void testExpressionInFunction() throws Exception {
+ assertTranslation( "from Animal an where an.bodyWeight > abs(3-5)" );
+ assertTranslation( "from Animal an where an.bodyWeight > abs(3/5)" );
+ assertTranslation( "from Animal an where an.bodyWeight > abs(3+5)" );
+ assertTranslation( "from Animal an where an.bodyWeight > abs(3*5)" );
+ SQLFunction concat = getSessionFactoryImplementor().getSqlFunctionRegistry().findSQLFunction( "concat");
+ List list = new ArrayList(); list.add("'fat'"); list.add("'skinny'");
+ assertTranslation( "from Animal an where an.description = " + concat.render(list, getSessionFactoryImplementor()) );
+ }
+
+ public void testNotOrWhereClause() {
+ assertTranslation( "from Simple s where 'foo'='bar' or not 'foo'='foo'" );
+ assertTranslation( "from Simple s where 'foo'='bar' or not ('foo'='foo')" );
+ assertTranslation( "from Simple s where not ( 'foo'='bar' or 'foo'='foo' )" );
+ assertTranslation( "from Simple s where not ( 'foo'='bar' and 'foo'='foo' )" );
+ assertTranslation( "from Simple s where not ( 'foo'='bar' and 'foo'='foo' ) or not ('x'='y')" );
+ assertTranslation( "from Simple s where not ( 'foo'='bar' or 'foo'='foo' ) and not ('x'='y')" );
+ assertTranslation( "from Simple s where not ( 'foo'='bar' or 'foo'='foo' ) and 'x'='y'" );
+ assertTranslation( "from Simple s where not ( 'foo'='bar' and 'foo'='foo' ) or 'x'='y'" );
+ assertTranslation( "from Simple s where 'foo'='bar' and 'foo'='foo' or not 'x'='y'" );
+ assertTranslation( "from Simple s where 'foo'='bar' or 'foo'='foo' and not 'x'='y'" );
+ assertTranslation( "from Simple s where ('foo'='bar' and 'foo'='foo') or 'x'='y'" );
+ assertTranslation( "from Simple s where ('foo'='bar' or 'foo'='foo') and 'x'='y'" );
+ assertTranslation( "from Simple s where not( upper( s.name ) ='yada' or 1=2 or 'foo'='bar' or not('foo'='foo') or 'foo' like 'bar' )" );
+ }
+
+ public void testComplexExpressionInFunction() throws Exception {
+ assertTranslation( "from Animal an where an.bodyWeight > abs((3-5)/4)" );
+ }
+
+ public void testStandardFunctions() throws Exception {
+ assertTranslation( "from Animal where current_date = current_time" );
+ assertTranslation( "from Animal a where upper(a.description) = 'FAT'" );
+ assertTranslation( "select lower(a.description) from Animal a" );
+ }
+
+ public void testOrderBy() throws Exception {
+ assertTranslation( "from Animal an order by an.bodyWeight" );
+ assertTranslation( "from Animal an order by an.bodyWeight asc" );
+ assertTranslation( "from Animal an order by an.bodyWeight desc" );
+ assertTranslation( "from Animal an order by sqrt(an.bodyWeight*4)/2" );
+ assertTranslation( "from Animal an order by an.mother.bodyWeight" );
+ assertTranslation( "from Animal an order by an.bodyWeight, an.description" );
+ assertTranslation( "from Animal an order by an.bodyWeight asc, an.description desc" );
+ if ( getDialect() instanceof HSQLDialect || getDialect() instanceof DB2Dialect ) {
+ assertTranslation( "from Human h order by sqrt(h.bodyWeight), year(h.birthdate)" );
+ }
+ }
+
+ public void testGroupByFunction() {
+ if ( getDialect() instanceof Oracle9Dialect ) return;
+ if ( getDialect() instanceof PostgreSQLDialect ) return;
+ assertTranslation( "select count(*) from Human h group by year(h.birthdate)" );
+ assertTranslation( "select count(*) from Human h group by trunc( sqrt(h.bodyWeight*4)/2 )" );
+ assertTranslation( "select count(*) from Human h group by year(sysdate)" );
+ }
+
+
+ public void testPolymorphism() throws Exception {
+ Map replacements = buildTrueFalseReplacementMapForDialect();
+ assertTranslation( "from Mammal" );
+ assertTranslation( "from Dog" );
+ assertTranslation( "from Mammal m where m.pregnant = false and m.bodyWeight > 10", replacements );
+ assertTranslation( "from Dog d where d.pregnant = false and d.bodyWeight > 10", replacements );
+ }
+
+ private Map buildTrueFalseReplacementMapForDialect() {
+ HashMap replacements = new HashMap();
+ try {
+ String dialectTrueRepresentation = getDialect().toBooleanValueString( true );
+ // if this call succeeds, then the dialect is saying to represent true/false as int values...
+ Integer.parseInt( dialectTrueRepresentation );
+ replacements.put( "true", "1" );
+ replacements.put( "false", "0" );
+ }
+ catch( NumberFormatException nfe ) {
+ // the Integer#parseInt call failed...
+ }
+ return replacements;
+ }
+
+ public void testTokenReplacement() throws Exception {
+ Map replacements = buildTrueFalseReplacementMapForDialect();
+ assertTranslation( "from Mammal m where m.pregnant = false and m.bodyWeight > 10", replacements );
+ }
+
+ public void testProduct() throws Exception {
+ Map replacements = buildTrueFalseReplacementMapForDialect();
+ assertTranslation( "from Animal, Animal" );
+ assertTranslation( "from Animal x, Animal y where x.bodyWeight = y.bodyWeight" );
+ assertTranslation( "from Animal x, Mammal y where x.bodyWeight = y.bodyWeight and not y.pregnant = true", replacements );
+ assertTranslation( "from Mammal, Mammal" );
+ }
+
+ public void testJoinedSubclassProduct() throws Exception {
+ assertTranslation( "from PettingZoo, PettingZoo" ); //product of two subclasses
+ }
+
+ public void testProjectProduct() throws Exception {
+ assertTranslation( "select x from Human x, Human y where x.nickName = y.nickName" );
+ assertTranslation( "select x, y from Human x, Human y where x.nickName = y.nickName" );
+ }
+
+ public void testExplicitEntityJoins() throws Exception {
+ assertTranslation( "from Animal an inner join an.mother mo" );
+ assertTranslation( "from Animal an left outer join an.mother mo" );
+ assertTranslation( "from Animal an left outer join fetch an.mother" );
+ }
+
+ public void testMultipleExplicitEntityJoins() throws Exception {
+ assertTranslation( "from Animal an inner join an.mother mo inner join mo.mother gm" );
+ assertTranslation( "from Animal an left outer join an.mother mo left outer join mo.mother gm" );
+ assertTranslation( "from Animal an inner join an.mother m inner join an.father f" );
+ assertTranslation( "from Animal an left join fetch an.mother m left join fetch an.father f" );
+ }
+
+ public void testMultipleExplicitJoins() throws Exception {
+ assertTranslation( "from Animal an inner join an.mother mo inner join an.offspring os" );
+ assertTranslation( "from Animal an left outer join an.mother mo left outer join an.offspring os" );
+ }
+
+ public void testExplicitEntityJoinsWithRestriction() throws Exception {
+ assertTranslation( "from Animal an inner join an.mother mo where an.bodyWeight < mo.bodyWeight" );
+ }
+
+ public void testIdProperty() throws Exception {
+ assertTranslation( "from Animal a where a.mother.id = 12" );
+ }
+
+ public void testSubclassAssociation() throws Exception {
+ assertTranslation( "from DomesticAnimal da join da.owner o where o.nickName = 'Gavin'" );
+ assertTranslation( "from DomesticAnimal da left join fetch da.owner" );
+ assertTranslation( "from Human h join h.pets p where p.pregnant = 1" );
+ assertTranslation( "from Human h join h.pets p where p.bodyWeight > 100" );
+ assertTranslation( "from Human h left join fetch h.pets" );
+ }
+
+ public void testExplicitCollectionJoins() throws Exception {
+ assertTranslation( "from Animal an inner join an.offspring os" );
+ assertTranslation( "from Animal an left outer join an.offspring os" );
+ }
+
+ public void testExplicitOuterJoinFetch() throws Exception {
+ assertTranslation( "from Animal an left outer join fetch an.offspring" );
+ }
+
+ public void testExplicitOuterJoinFetchWithSelect() throws Exception {
+ assertTranslation( "select an from Animal an left outer join fetch an.offspring" );
+ }
+
+ public void testExplicitJoins() throws Exception {
+ Map replacements = buildTrueFalseReplacementMapForDialect();
+ assertTranslation( "from Zoo zoo join zoo.mammals mam where mam.pregnant = true and mam.description like '%white%'", replacements );
+ assertTranslation( "from Zoo zoo join zoo.animals an where an.description like '%white%'" );
+ }
+
+ /**
+ * Test for HHH-559
+ */
+ public void testMultibyteCharacterConstant() throws Exception {
+ assertTranslation( "from Zoo zoo join zoo.animals an where an.description like '%\u4e2d%'" );
+ }
+
+ public void testImplicitJoins() throws Exception {
+ // Two dots...
+ assertTranslation( "from Animal an where an.mother.bodyWeight > ?" );
+ assertTranslation( "from Animal an where an.mother.bodyWeight > 10" );
+ assertTranslation( "from Dog dog where dog.mother.bodyWeight > 10" );
+ // Three dots...
+ assertTranslation( "from Animal an where an.mother.mother.bodyWeight > 10" );
+ // The new QT doesn't throw an exception here, so this belongs in ASTQueryTranslator test. [jsd]
+// assertTranslation( "from Animal an where an.offspring.mother.bodyWeight > 10" );
+ // Is not null (unary postfix operator)
+ assertTranslation( "from Animal an where an.mother is not null" );
+ // ID property shortut (no implicit join)
+ assertTranslation( "from Animal an where an.mother.id = 123" );
+ }
+
+ public void testImplicitJoinInSelect() {
+ assertTranslation( "select foo, foo.long from Foo foo" );
+ DotNode.useThetaStyleImplicitJoins = true;
+ assertTranslation( "select foo.foo from Foo foo" );
+ assertTranslation( "select foo, foo.foo from Foo foo" );
+ assertTranslation( "select foo.foo from Foo foo where foo.foo is not null" );
+ DotNode.useThetaStyleImplicitJoins = false;
+ }
+
+ public void testSelectExpressions() {
+ DotNode.useThetaStyleImplicitJoins = true;
+ assertTranslation( "select an.mother.mother from Animal an" );
+ assertTranslation( "select an.mother.mother.mother from Animal an" );
+ assertTranslation( "select an.mother.mother.bodyWeight from Animal an" );
+ assertTranslation( "select an.mother.zoo.id from Animal an" );
+ assertTranslation( "select user.human.zoo.id from User user" );
+ assertTranslation( "select u.userName, u.human.name.first from User u" );
+ assertTranslation( "select u.human.name.last, u.human.name.first from User u" );
+ assertTranslation( "select bar.baz.name from Bar bar" );
+ assertTranslation( "select bar.baz.name, bar.baz.count from Bar bar" );
+ DotNode.useThetaStyleImplicitJoins = false;
+ }
+
+ public void testSelectStandardFunctionsNoParens() throws Exception {
+ assertTranslation( "select current_date, current_time, current_timestamp from Animal" );
+ }
+
+ public void testMapIndex() throws Exception {
+ assertTranslation( "from User u where u.permissions['hibernate']='read'" );
+ }
+
+ /*public void testCollectionFunctionsInSelect() {
+ //sql is correct, just different order in select clause
+ assertTranslation("select baz, size(baz.stringSet), count( distinct elements(baz.stringSet) ), max( elements(baz.stringSet) ) from Baz baz group by baz");
+ }
+
+ public void testSelectElements() throws Exception {
+ assertTranslation( "select elements(fum1.friends) from org.hibernate.test.legacy.Fum fum1" );
+ assertTranslation( "select elements(one.manies) from org.hibernate.test.legacy.One one" );
+ }*/
+
+ public void testNamedParameters() throws Exception {
+ assertTranslation( "from Animal an where an.mother.bodyWeight > :weight" );
+ }
+
+ // Second set of examples....
+
+ public void testClassProperty() throws Exception {
+ assertTranslation( "from Animal a where a.mother.class = Reptile" );
+ }
+
+ public void testComponent() throws Exception {
+ assertTranslation( "from Human h where h.name.first = 'Gavin'" );
+ }
+
+ public void testSelectEntity() throws Exception {
+ assertTranslation( "select an from Animal an inner join an.mother mo where an.bodyWeight < mo.bodyWeight" );
+ assertTranslation( "select mo, an from Animal an inner join an.mother mo where an.bodyWeight < mo.bodyWeight" );
+ }
+
+ public void testValueAggregate() {
+ assertTranslation( "select max(p), min(p) from User u join u.permissions p" );
+ }
+
+ public void testAggregation() throws Exception {
+ assertTranslation( "select count(an) from Animal an" );
+ assertTranslation( "select count(*) from Animal an" );
+ assertTranslation( "select count(distinct an) from Animal an" );
+ assertTranslation( "select count(distinct an.id) from Animal an" );
+ assertTranslation( "select count(all an.id) from Animal an" );
+ }
+
+ public void testSelectProperty() throws Exception {
+ assertTranslation( "select an.bodyWeight, mo.bodyWeight from Animal an inner join an.mother mo where an.bodyWeight < mo.bodyWeight" );
+ }
+
+ public void testSelectEntityProperty() throws Exception {
+ DotNode.useThetaStyleImplicitJoins = true;
+ assertTranslation( "select an.mother from Animal an" );
+ assertTranslation( "select an, an.mother from Animal an" );
+ DotNode.useThetaStyleImplicitJoins = false;
+ }
+
+ public void testSelectDistinctAll() throws Exception {
+ assertTranslation( "select distinct an.description, an.bodyWeight from Animal an" );
+ assertTranslation( "select all an from Animal an" );
+ }
+
+ public void testSelectAssociatedEntityId() throws Exception {
+ assertTranslation( "select an.mother.id from Animal an" );
+ }
+
+ public void testGroupBy() throws Exception {
+ assertTranslation( "select an.mother.id, max(an.bodyWeight) from Animal an group by an.mother.id" );
+ assertTranslation( "select an.mother.id, max(an.bodyWeight) from Animal an group by an.mother.id having max(an.bodyWeight)>1.0" );
+ }
+
+ public void testGroupByMultiple() throws Exception {
+ assertTranslation( "select s.id, s.count, count(t), max(t.date) from org.hibernate.test.legacy.Simple s, org.hibernate.test.legacy.Simple t where s.count = t.count group by s.id, s.count order by s.count" );
+ }
+
+ public void testManyToMany() throws Exception {
+ assertTranslation( "from Human h join h.friends f where f.nickName = 'Gavin'" );
+ assertTranslation( "from Human h join h.friends f where f.bodyWeight > 100" );
+ }
+
+ public void testManyToManyElementFunctionInWhere() throws Exception {
+ assertTranslation( "from Human human where human in elements(human.friends)" );
+ assertTranslation( "from Human human where human = some elements(human.friends)" );
+ }
+
+ public void testManyToManyElementFunctionInWhere2() throws Exception {
+ assertTranslation( "from Human h1, Human h2 where h2 in elements(h1.family)" );
+ assertTranslation( "from Human h1, Human h2 where 'father' in indices(h1.family)" );
+ }
+
+ public void testManyToManyFetch() throws Exception {
+ assertTranslation( "from Human h left join fetch h.friends" );
+ }
+
+ public void testManyToManyIndexAccessor() throws Exception {
+ // From ParentChildTest.testCollectionQuery()
+ assertTranslation( "select c from ContainerX c, Simple s where c.manyToMany[2] = s" );
+ assertTranslation( "select s from ContainerX c, Simple s where c.manyToMany[2] = s" );
+ assertTranslation( "from ContainerX c, Simple s where c.manyToMany[2] = s" );
+ //would be nice to have:
+ //assertTranslation( "select c.manyToMany[2] from ContainerX c" );
+ }
+
+ public void testSelectNew() throws Exception {
+ assertTranslation( "select new Animal(an.description, an.bodyWeight) from Animal an" );
+ assertTranslation( "select new org.hibernate.test.hql.Animal(an.description, an.bodyWeight) from Animal an" );
+ }
+
+ public void testSimpleCorrelatedSubselect() throws Exception {
+ assertTranslation( "from Animal a where a.bodyWeight = (select o.bodyWeight from a.offspring o)" );
+ assertTranslation( "from Animal a where a = (from a.offspring o)" );
+ }
+
+ public void testSimpleUncorrelatedSubselect() throws Exception {
+ assertTranslation( "from Animal a where a.bodyWeight = (select an.bodyWeight from Animal an)" );
+ assertTranslation( "from Animal a where a = (from Animal an)" );
+ }
+
+ public void testSimpleCorrelatedSubselect2() throws Exception {
+ assertTranslation( "from Animal a where a = (select o from a.offspring o)" );
+ assertTranslation( "from Animal a where a in (select o from a.offspring o)" );
+ }
+
+ public void testSimpleUncorrelatedSubselect2() throws Exception {
+ assertTranslation( "from Animal a where a = (select an from Animal an)" );
+ assertTranslation( "from Animal a where a in (select an from Animal an)" );
+ }
+
+ public void testUncorrelatedSubselect2() throws Exception {
+ assertTranslation( "from Animal a where a.bodyWeight = (select max(an.bodyWeight) from Animal an)" );
+ }
+
+ public void testCorrelatedSubselect2() throws Exception {
+ assertTranslation( "from Animal a where a.bodyWeight > (select max(o.bodyWeight) from a.offspring o)" );
+ }
+
+ public void testManyToManyJoinInSubselect() throws Exception {
+ DotNode.useThetaStyleImplicitJoins = true;
+ assertTranslation( "select foo from Foo foo where foo in (select elt from Baz baz join baz.fooArray elt)" );
+ DotNode.useThetaStyleImplicitJoins = false;
+ }
+
+ public void testImplicitJoinInSubselect() throws Exception {
+ assertTranslation( "from Animal a where a = (select an.mother from Animal an)" );
+ assertTranslation( "from Animal a where a.id = (select an.mother.id from Animal an)" );
+ }
+
+ public void testManyToOneSubselect() {
+ //TODO: the join in the subselect also shows up in the outer query!
+ assertTranslation( "from Animal a where 'foo' in (select m.description from a.mother m)" );
+ }
+
+ public void testPositionalParameters() throws Exception {
+ assertTranslation( "from Animal an where an.bodyWeight > ?" );
+ }
+
+ public void testKeywordPropertyName() throws Exception {
+ assertTranslation( "from Glarch g order by g.order asc" );
+ assertTranslation( "select g.order from Glarch g where g.order = 3" );
+ }
+
+ public void testJavaConstant() throws Exception {
+ assertTranslation( "from org.hibernate.test.legacy.Category c where c.name = org.hibernate.test.legacy.Category.ROOT_CATEGORY" );
+ assertTranslation( "from org.hibernate.test.legacy.Category c where c.id = org.hibernate.test.legacy.Category.ROOT_ID" );
+ // todo : additional desired functionality
+ //assertTranslation( "from Category c where c.name = Category.ROOT_CATEGORY" );
+ //assertTranslation( "select c.name, Category.ROOT_ID from Category as c");
+ }
+
+ public void testClassName() throws Exception {
+ // The Zoo reference is OK; Zoo is discriminator-based;
+ // the old parser could handle these correctly
+ //
+ // However, the Animal one ares not; Animal is joined subclassing;
+ // the old parser does not handle thee correctly. The new parser
+ // previously did not handle them correctly in that same way. So they
+ // used to pass regression even though the output was bogus SQL...
+ //
+ // I have moved the Animal ones (plus duplicating the Zoo one)
+ // to ASTParserLoadingTest for syntax checking.
+ assertTranslation( "from Zoo zoo where zoo.class = PettingZoo" );
+// assertTranslation( "from DomesticAnimal an where an.class = Dog" );
+// assertTranslation( "from Animal an where an.class = Dog" );
+ }
+
+ public void testSelectDialectFunction() throws Exception {
+ // From SQLFunctionsTest.testDialectSQLFunctions...
+ if ( getDialect() instanceof HSQLDialect ) {
+ assertTranslation( "select mod(s.count, 2) from org.hibernate.test.legacy.Simple as s where s.id = 10" );
+ //assertTranslation( "from org.hibernate.test.legacy.Simple as s where mod(s.count, 2) = 0" );
+ }
+ assertTranslation( "select upper(human.name.first) from Human human" );
+ assertTranslation( "from Human human where lower(human.name.first) like 'gav%'" );
+ assertTranslation( "select upper(a.description) from Animal a" );
+ assertTranslation( "select max(a.bodyWeight) from Animal a" );
+ }
+
+ public void testTwoJoins() throws Exception {
+ assertTranslation( "from Human human join human.friends, Human h join h.mother" );
+ assertTranslation( "from Human human join human.friends f, Animal an join an.mother m where f=m" );
+ assertTranslation( "from Baz baz left join baz.fooToGlarch, Bar bar join bar.foo" );
+ }
+
+ public void testToOneToManyManyJoinSequence() throws Exception {
+ assertTranslation( "from Dog d join d.owner h join h.friends f where f.name.first like 'joe%'" );
+ }
+
+ public void testToOneToManyJoinSequence() throws Exception {
+ assertTranslation( "from Animal a join a.mother m join m.offspring" );
+ assertTranslation( "from Dog d join d.owner m join m.offspring" );
+ assertTranslation( "from Animal a join a.mother m join m.offspring o where o.bodyWeight > a.bodyWeight" );
+ }
+
+ public void testSubclassExplicitJoin() throws Exception {
+ assertTranslation( "from DomesticAnimal da join da.owner o where o.nickName = 'gavin'" );
+ assertTranslation( "from DomesticAnimal da join da.owner o where o.bodyWeight > 0" );
+ }
+
+ public void testMultipleExplicitCollectionJoins() throws Exception {
+ assertTranslation( "from Animal an inner join an.offspring os join os.offspring gc" );
+ assertTranslation( "from Animal an left outer join an.offspring os left outer join os.offspring gc" );
+ }
+
+ public void testSelectDistinctComposite() throws Exception {
+ // This is from CompositeElementTest.testHandSQL.
+ assertTranslation( "select distinct p from org.hibernate.test.compositeelement.Parent p join p.children c where c.name like 'Child%'" );
+ }
+
+ public void testDotComponent() throws Exception {
+ // from FumTest.testListIdentifiers()
+ assertTranslation( "select fum.id from org.hibernate.test.legacy.Fum as fum where not fum.fum='FRIEND'" );
+ }
+
+ public void testOrderByCount() throws Exception {
+ assertTranslation( "from Animal an group by an.zoo.id order by an.zoo.id, count(*)" );
+ }
+
+ public void testHavingCount() throws Exception {
+ assertTranslation( "from Animal an group by an.zoo.id having count(an.zoo.id) > 1" );
+ }
+
+ public void selectWhereElements() throws Exception {
+ assertTranslation( "select foo from Foo foo, Baz baz where foo in elements(baz.fooArray)" );
+ }
+
+ public void testCollectionOfComponents() throws Exception {
+ assertTranslation( "from Baz baz inner join baz.components comp where comp.name='foo'" );
+ }
+
+ public void testNestedComponentIsNull() {
+ // From MapTest...
+ assertTranslation( "from Commento c where c.marelo.commento.mcompr is null" );
+ }
+
+ public void testOneToOneJoinedFetch() throws Exception {
+ // From OneToOneTest.testOneToOneOnSubclass
+ assertTranslation( "from org.hibernate.test.onetoone.joined.Person p join fetch p.address left join fetch p.mailingAddress" );
+ }
+
+ public void testSubclassImplicitJoin() throws Exception {
+ assertTranslation( "from DomesticAnimal da where da.owner.nickName like 'Gavin%'" );
+ assertTranslation( "from DomesticAnimal da where da.owner.nickName = 'gavin'" );
+ assertTranslation( "from DomesticAnimal da where da.owner.bodyWeight > 0" );
+ }
+
+ public void testComponent2() throws Exception {
+ assertTranslation( "from Dog dog where dog.owner.name.first = 'Gavin'" );
+ }
+
+ public void testOneToOne() throws Exception {
+ assertTranslation( "from User u where u.human.nickName='Steve'" );
+ assertTranslation( "from User u where u.human.name.first='Steve'" );
+ }
+
+ public void testSelectClauseImplicitJoin() throws Exception {
+ //assertTranslation( "select d.owner.mother from Dog d" ); //bug in old qt
+ assertTranslation( "select d.owner.mother.description from Dog d" );
+ //assertTranslation( "select d.owner.mother from Dog d, Dog h" );
+ }
+
+ public void testFromClauseImplicitJoin() throws Exception {
+ assertTranslation( "from DomesticAnimal da join da.owner.mother m where m.bodyWeight > 10" );
+ }
+
+ public void testJoinedSubclassWithOrCondition() {
+ assertTranslation( "from Animal an where (an.bodyWeight > 10 and an.bodyWeight < 100) or an.bodyWeight is null" );
+ }
+
+ public void testImplicitJoinInFrom() {
+ assertTranslation( "from Human h join h.mother.mother.offspring o" );
+ }
+
+ public void testDuplicateImplicitJoinInSelect() {
+ // This test causes failures on theta-join dialects because the SQL is different. The old parser
+ // duplicates the condition, whereas the new parser does not. The queries are semantically the
+ // same however.
+ assertTranslation( "select an.mother.bodyWeight from Animal an join an.mother m where an.mother.bodyWeight > 10" );
+ assertTranslation( "select an.mother.bodyWeight from Animal an where an.mother.bodyWeight > 10" );
+ //assertTranslation("select an.mother from Animal an where an.mother.bodyWeight is not null");
+ assertTranslation( "select an.mother.bodyWeight from Animal an order by an.mother.bodyWeight" );
+ }
+
+ public void testConstructorNode() throws Exception {
+ ConstructorNode n = new ConstructorNode();
+ assertNull( n.getFromElement() );
+ assertFalse( n.isReturnableEntity() );
+ }
+
+ public void testIndexNode() throws Exception {
+ IndexNode n = new IndexNode();
+ Exception ex = null;
+ try {
+ n.setScalarColumnText( 0 );
+ }
+ catch ( UnsupportedOperationException e ) {
+ ex = e;
+ }
+ assertNotNull( ex );
+ }
+
+ public void testExceptions() throws Exception {
+ DetailedSemanticException dse = new DetailedSemanticException( "test" );
+ dse.printStackTrace();
+ dse.printStackTrace( new PrintWriter( new StringWriter() ) );
+ QuerySyntaxException qse = QuerySyntaxException.convert( new RecognitionException( "test" ), "from bozo b where b.clown = true" );
+ assertNotNull( qse.getMessage() );
+ }
+
+ public void testSelectProperty2() throws Exception {
+ assertTranslation( "select an, mo.bodyWeight from Animal an inner join an.mother mo where an.bodyWeight < mo.bodyWeight" );
+ assertTranslation( "select an, mo, an.bodyWeight, mo.bodyWeight from Animal an inner join an.mother mo where an.bodyWeight < mo.bodyWeight" );
+ }
+
+ public void testSubclassWhere() throws Exception {
+ // TODO: The classic QT generates lots of extra parens, etc.
+ assertTranslation( "from PettingZoo pz1, PettingZoo pz2 where pz1.id = pz2.id" );
+ assertTranslation( "from PettingZoo pz1, PettingZoo pz2 where pz1.id = pz2" );
+ assertTranslation( "from PettingZoo pz where pz.id > 0 " );
+ }
+
+ public void testNestedImplicitJoinsInSelect() throws Exception {
+ // NOTE: This test is not likely to generate the exact SQL because of the where clause. The synthetic
+ // theta style joins come out differently in the new QT.
+ // From FooBarTest.testQuery()
+ // Missing the foo2_ join, and foo3_ should include subclasses, but it doesn't.
+// assertTranslation("select foo.foo.foo.foo.string from org.hibernate.test.legacy.Foo foo where foo.foo.foo = 'bar'");
+ assertTranslation( "select foo.foo.foo.foo.string from org.hibernate.test.legacy.Foo foo" );
+ }
+
+ public void testNestedComponent() throws Exception {
+ // From FooBarTest.testQuery()
+ //an extra set of parens in new SQL
+ assertTranslation( "from org.hibernate.test.legacy.Foo foo where foo.component.subcomponent.name='bar'" );
+ }
+
+ public void testNull2() throws Exception {
+ //old parser generates useless extra parens
+ assertTranslation( "from Human h where not( h.nickName is null )" );
+ assertTranslation( "from Human h where not( h.nickName is not null )" );
+ }
+
+ public void testUnknownFailureFromMultiTableTest() {
+ assertTranslation( "from Lower s where s.yetanother.name='name'" );
+ }
+
+ public void testJoinInSubselect() throws Exception {
+ //new parser uses ANSI-style inner join syntax
+ DotNode.useThetaStyleImplicitJoins = true;
+ assertTranslation( "from Animal a where a in (select m from Animal an join an.mother m)" );
+ assertTranslation( "from Animal a where a in (select o from Animal an join an.offspring o)" );
+ DotNode.useThetaStyleImplicitJoins = false;
+ }
+
+ public void testJoinedSubclassImplicitJoin() throws Exception {
+ // From MultiTableTest.testQueries()
+ // TODO: This produces the proper from clause now, but the parens in the where clause are different.
+ assertTranslation( "from org.hibernate.test.legacy.Lower s where s.yetanother.name='name'" );
+ }
+
+ public void testProjectProductJoinedSubclass() throws Exception {
+ // TODO: The old QT generates the discriminator and the theta join in a strange order, and with two extra sets of parens, this is okay, right?
+ assertTranslation( "select zoo from Zoo zoo, PettingZoo pz where zoo=pz" );
+ assertTranslation( "select zoo, pz from Zoo zoo, PettingZoo pz where zoo=pz" );
+ }
+
+ public void testCorrelatedSubselect1() throws Exception {
+ // The old translator generates the theta join before the condition in the sub query.
+ // TODO: Decide if we want to bother generating the theta join in the same order (non simple).
+ assertTranslation( "from Animal a where exists (from a.offspring o where o.bodyWeight>10)" );
+ }
+
+ public void testOuterAliasInSubselect() {
+ assertTranslation( "from Human h where h = (from Animal an where an = h)" );
+ }
+
+ public void testFetch() throws Exception {
+ assertTranslation( "from Zoo zoo left join zoo.mammals" );
+ assertTranslation( "from Zoo zoo left join fetch zoo.mammals" );
+ }
+
+ public void testOneToManyElementFunctionInWhere() throws Exception {
+ assertTranslation( "from Zoo zoo where 'dog' in indices(zoo.mammals)" );
+ assertTranslation( "from Zoo zoo, Dog dog where dog in elements(zoo.mammals)" );
+ }
+
+ /*public void testManyToManyElementFunctionInSelect() throws Exception {
+ assertTranslation("select elements(zoo.mammals) from Zoo zoo");
+ assertTranslation("select indices(zoo.mammals) from Zoo zoo");
+ }*/
+
+ public void testManyToManyInJoin() throws Exception {
+ assertTranslation( "select x.id from Human h1 join h1.family x" );
+ //assertTranslation("select index(h2) from Human h1 join h1.family h2");
+ }
+
+ public void testManyToManyInSubselect() throws Exception {
+ assertTranslation( "from Human h1, Human h2 where h2 in (select x.id from h1.family x)" );
+ assertTranslation( "from Human h1, Human h2 where 'father' in indices(h1.family)" );
+ }
+
+ public void testOneToManyIndexAccess() throws Exception {
+ assertTranslation( "from Zoo zoo where zoo.mammals['dog'] is not null" );
+ }
+
+ public void testImpliedSelect() throws Exception {
+ assertTranslation( "select zoo from Zoo zoo" );
+ assertTranslation( "from Zoo zoo" );
+ assertTranslation( "from Zoo zoo join zoo.mammals m" );
+ assertTranslation( "from Zoo" );
+ assertTranslation( "from Zoo zoo join zoo.mammals" );
+ }
+
+ public void testVectorSubselect() {
+ assertTranslation( "from Animal a where ('foo', 'bar') in (select m.description, m.bodyWeight from a.mother m)" );
+ }
+
+ public void testWierdSubselectImplicitJoinStuff() {
+ //note that the new qt used to eliminate unnecessary join, but no more
+ assertTranslation("from Simple s where s = some( select sim from Simple sim where sim.other.count=s.other.count ) and s.other.count > 0");
+ }
+
+ /*public void testSelectElementsOfCollectionOfValues() throws Exception {
+ // From FooBarTest.testQuery()
+ // TODO: This produces the where clause in a different order, but it seems okay.
+ assertTranslation("select foo.component.name, elements(foo.component.importantDates) from org.hibernate.test.legacy.Foo foo where foo.foo.id=?");
+ }*/
+
+ //public void testMultiTableElements() throws Exception {
+ /*
+ HQL : select elements(ls.bag), elements(ls.set) from org.hibernate.test.legacy.Lower ls
+ OLD SQL:
+ select top2_.id1_ as col_0_0_, top4_.id1_ as col_1_0_
+ from leafsubclass lower0_ inner join rootclass lower0_1_ on lower0_.id__=lower0_1_.id1_, simple_simple bag1_, rootclass top2_, rootclass set3_, rootclass top4_
+ where lower0_1_.id1_ is not null and lower0_.id__=bag1_.simple1 and bag1_.simple2=top2_.id1_ and lower0_.id__=set3_.parent and set3_.id1_=top4_.id1_
+ */
+
+ //assertTranslation("select elements(ls.bag), elements(ls.set) from org.hibernate.test.legacy.Lower ls");
+ //}
+
+ public void testCollectionsInSelect2() throws Exception {
+ // This one looks okay now, it just generates extra parens in the where clause.
+ assertTranslation( "select foo.string from Bar bar left join bar.baz.fooArray foo where bar.string = foo.string" );
+ }
+
+
+ //public void testCollectionsInSelect() throws Exception {
+ // From FooBarTest.testCollectionsInSelect
+ /*
+ HQL : select baz, baz.stringSet.size, count( distinct elements(baz.stringSet) ), max( elements(baz.stringSet) ) from org.hibernate.test.legacy.Baz baz group by baz
+ OLD SQL:
+ select
+ baz0_.baz_id_column_ as baz_id_c1_, baz0_.count_count as count_co2_37_, baz0_.name_b as name_b37_, baz0_.foo as foo37_, baz0_.superBaz as superBaz37_, baz0_.str as str37_, baz0_.baz_id_column_ as col_0_0_,
+ count(*) as col_1_0_,
+ count(distinct stringset2_.element) as col_2_0_, max(stringset3_.element) as col_3_0_
+ from baz baz0_, stringSet stringset1_, stringSet stringset2_, stringSet stringset3_
+ where baz0_.baz_id_column_=stringset1_.id_ and baz0_.baz_id_column_=stringset2_.id_ and baz0_.baz_id_column_=stringset3_.id_
+ group by baz0_.baz_id_column_
+
+ NEW SQL:
+ select
+ // TODO: Remove the extra 'id' column select.
+ baz0_.baz_id_column_ as col_0_0_,
+ // TODO: Figure out how the classic translator knows to use count(*)
+ (select count(*) from stringSet stringset1_ where baz0_.baz_id_column_=stringset1_.id_) as col_1_0_,
+ // This is also correct.
+ count(distinct stringset2_.element) as col_2_0_, max(stringset3_.element) as col_3_0_,
+ // The properties of baz are correct, they're just in the wrong place.
+ baz0_.baz_id_column_ as baz_id_c1_, baz0_.count_count as count_co2_37_, baz0_.name_b as name_b37_, baz0_.foo as foo37_, baz0_.superBaz as superBaz37_, baz0_.str as str37_
+// FROM is okay.
+ from baz baz0_ stringSet stringset1_, stringSet stringset3_, stringSet stringset2_
+// WHERE is okay.
+ where (baz0_.baz_id_column_=stringset1_.id_ and baz0_.baz_id_column_=stringset2_.id_ baz0_.baz_id_column_=stringset3_.id_)
+// GROUP BY is okay.
+ group by baz0_.baz_id_column_
+ */
+ //assertTranslation( "select baz, size(baz.stringSet), count( distinct elements(baz.stringSet) ), max( elements(baz.stringSet) ) from org.hibernate.test.legacy.Baz baz group by baz");
+
+ //}
+
+ public void testAssociationPropertyWithoutAlias() throws Exception {
+ // The classic translator doesn't do this right, so don't bother asserting.
+ compileWithAstQueryTranslator("from Animal where zoo is null", false);
+ }
+
+ private void compileWithAstQueryTranslator(String hql, boolean scalar) {
+ Map replacements = new HashMap();
+ QueryTranslatorFactory ast = new ASTQueryTranslatorFactory();
+ SessionFactoryImplementor factory = getSessionFactoryImplementor();
+ QueryTranslator newQueryTranslator = ast.createQueryTranslator( hql, hql, Collections.EMPTY_MAP, factory );
+ newQueryTranslator.compile( replacements, scalar );
+ }
+
+ public void testComponentNoAlias() throws Exception {
+ // The classic translator doesn't do this right, so don't bother asserting.
+ compileWithAstQueryTranslator( "from Human where name.first = 'Gavin'", false);
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/HeresAnotherCrazyIdFieldName.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/HeresAnotherCrazyIdFieldName.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/HeresAnotherCrazyIdFieldName.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,35 @@
+// $Id: HeresAnotherCrazyIdFieldName.java 7471 2005-07-14 14:58:28Z steveebersole $
+package org.hibernate.test.hql;
+
+/**
+ * Implementation of HeresAnotherCrazyIdFieldName.
+ *
+ * @author Steve Ebersole
+ */
+public class HeresAnotherCrazyIdFieldName {
+ private Long heresAnotherCrazyIdFieldName;
+ private String name;
+
+ public HeresAnotherCrazyIdFieldName() {
+ }
+
+ public HeresAnotherCrazyIdFieldName(String name) {
+ this.name = name;
+ }
+
+ public Long getHeresAnotherCrazyIdFieldName() {
+ return heresAnotherCrazyIdFieldName;
+ }
+
+ public void setHeresAnotherCrazyIdFieldName(Long heresAnotherCrazyIdFieldName) {
+ this.heresAnotherCrazyIdFieldName = heresAnotherCrazyIdFieldName;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/HqlParserTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/HqlParserTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/HqlParserTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1081 @@
+// $Id: HqlParserTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.hql;
+
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import antlr.RecognitionException;
+import antlr.TokenStreamException;
+import antlr.collections.AST;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.hibernate.hql.ast.HqlParser;
+import org.hibernate.hql.ast.tree.Node;
+import org.hibernate.hql.ast.util.ASTIterator;
+import org.hibernate.hql.ast.util.ASTPrinter;
+
+/**
+ * Tests the HQL parser on various inputs, just makes sure that the first phase of the parser
+ * works properly (i.e. no unexpected syntax errors).
+ */
+public class HqlParserTest extends TestCase {
+
+ /**
+ * Standard JUnit test case constructor.
+ *
+ * @param name The name of the test case.
+ */
+ public HqlParserTest(String name) {
+ super( name );
+ }
+
+ public void testUnion() throws Exception {
+ parse("from Animal a where a in (from Cat union from Dog) ");
+ }
+
+ /**
+ * Section 9.2 - from *
+ */
+ public void testDocoExamples92() throws Exception {
+ parse( "from eg.Cat" );
+ parse( "from eg.Cat as cat" );
+ parse( "from eg.Cat cat" );
+ parse( "from Formula, Parameter" );
+ parse( "from Formula as form, Parameter as param" );
+ }
+
+ /**
+ * Section 9.3 - Associations and joins *
+ */
+ public void testDocoExamples93() throws Exception {
+ parse( "from eg.Cat as cat inner join cat.mate as mate left outer join cat.kittens as kitten" );
+ parse( "from eg.Cat as cat left join cat.mate.kittens as kittens" );
+ parse( "from Formula form full join form.parameter param" );
+ parse( "from eg.Cat as cat join cat.mate as mate left join cat.kittens as kitten" );
+ parse( "from eg.Cat as cat\ninner join fetch cat.mate\nleft join fetch cat.kittens" );
+ }
+
+ /**
+ * Section 9.4 - Select *
+ */
+ public void testDocoExamples94() throws Exception {
+ parse( "select mate from eg.Cat as cat inner join cat.mate as mate" );
+ parse( "select cat.mate from eg.Cat cat" );
+ parse( "select elements(cat.kittens) from eg.Cat cat" );
+ parse( "select cat.name from eg.DomesticCat cat where cat.name like 'fri%'" );
+ parse( "select cust.name.firstName from Customer as cust" );
+ parse( "select mother, offspr, mate.name from eg.DomesticCat\n"
+ + " as mother inner join mother.mate as mate left outer join\n"
+ + "mother.kittens as offspr" );
+ parse( "select new Family(mother, mate, offspr)\n"
+ + "from eg.DomesticCat as mother\n"
+ + "join mother.mate as mate\n"
+ + "left join mother.kittens as offspr\n" );
+ }
+
+ /**
+ * Section 9.5 - Aggregate functions *
+ */
+ public void testDocoExamples95() throws Exception {
+ parse( "select avg(cat.weight), sum(cat.weight), max(cat.weight), count(cat)\n"
+ + "from eg.Cat cat" );
+ parse( "select cat, count( elements(cat.kittens) )\n"
+ + " from eg.Cat cat group by cat" );
+ parse( "select distinct cat.name from eg.Cat cat" );
+ parse( "select count(distinct cat.name), count(cat) from eg.Cat cat" );
+ }
+
+ /**
+ * Section 9.6 - Polymorphism *
+ */
+ public void testDocoExamples96() throws Exception {
+ parse( "from eg.Cat as cat" );
+ parse( "from java.lang.Object o" );
+ parse( "from eg.Named n, eg.Named m where n.name = m.name" );
+ }
+
+ /**
+ * Section 9.7 - Where *
+ */
+ public void testDocoExamples97() throws Exception {
+ parse( "from eg.Cat as cat where cat.name='Fritz'" );
+ parse( "select foo\n"
+ + "from eg.Foo foo, eg.Bar bar\n"
+ + "where foo.startDate = bar.date\n" );
+ parse( "from eg.Cat cat where cat.mate.name is not null" );
+ parse( "from eg.Cat cat, eg.Cat rival where cat.mate = rival.mate" );
+ parse( "select cat, mate\n"
+ + "from eg.Cat cat, eg.Cat mate\n"
+ + "where cat.mate = mate" );
+ parse( "from eg.Cat as cat where cat.id = 123" );
+ parse( "from eg.Cat as cat where cat.mate.id = 69" );
+ parse( "from bank.Person person\n"
+ + "where person.id.country = 'AU'\n"
+ + "and person.id.medicareNumber = 123456" );
+ parse( "from bank.Account account\n"
+ + "where account.owner.id.country = 'AU'\n"
+ + "and account.owner.id.medicareNumber = 123456" );
+ parse( "from eg.Cat cat where cat.class = eg.DomesticCat" );
+ parse( "from eg.AuditLog log, eg.Payment payment\n"
+ + "where log.item.class = 'eg.Payment' and log.item.id = payment.id" );
+ }
+
+ /**
+ * Section 9.8 - Expressions *
+ */
+ public void testDocoExamples98() throws Exception {
+ parse( "from eg.DomesticCat cat where cat.name between 'A' and 'B'" );
+ parse( "from eg.DomesticCat cat where cat.name in ( 'Foo', 'Bar', 'Baz' )" );
+ parse( "from eg.DomesticCat cat where cat.name not between 'A' and 'B'" );
+ parse( "from eg.DomesticCat cat where cat.name not in ( 'Foo', 'Bar', 'Baz' )" );
+ parse( "from eg.Cat cat where cat.kittens.size > 0" );
+ parse( "from eg.Cat cat where size(cat.kittens) > 0" );
+// This is a little odd. I'm not sure whether 'current' is a keyword.
+// parse("from Calendar cal where cal.holidays.maxElement > current date");
+// Using the token 'order' as both a keyword and an identifier works now, but
+// the second instance causes some problems because order is valid in the second instance.
+// parse("from Order order where maxindex(order.items) > 100");
+// parse("from Order order where minelement(order.items) > 10000");
+ parse( "from Order ord where maxindex(ord.items) > 100" );
+ parse( "from Order ord where minelement(ord.items) > 10000" );
+
+ parse( "select mother from eg.Cat as mother, eg.Cat as kit\n"
+ + "where kit in elements(foo.kittens)" );
+ parse( "select p from eg.NameList list, eg.Person p\n"
+ + "where p.name = some elements(list.names)" );
+ parse( "from eg.Cat cat where exists elements(cat.kittens)" );
+ parse( "from eg.Player p where 3 > all elements(p.scores)" );
+ parse( "from eg.Show show where 'fizard' in indices(show.acts)" );
+
+ // Yet another example of the pathological 'order' token.
+// parse("from Order order where order.items[0].id = 1234");
+// parse("select person from Person person, Calendar calendar\n"
+// + "where calendar.holidays['national day'] = person.birthDay\n"
+// + "and person.nationality.calendar = calendar");
+// parse("select item from Item item, Order order\n"
+// + "where order.items[ order.deliveredItemIndices[0] ] = item and order.id = 11");
+// parse("select item from Item item, Order order\n"
+// + "where order.items[ maxindex(order.items) ] = item and order.id = 11");
+
+ parse( "from Order ord where ord.items[0].id = 1234" );
+ parse( "select person from Person person, Calendar calendar\n"
+ + "where calendar.holidays['national day'] = person.birthDay\n"
+ + "and person.nationality.calendar = calendar" );
+ parse( "select item from Item item, Order ord\n"
+ + "where ord.items[ ord.deliveredItemIndices[0] ] = item and ord.id = 11" );
+ parse( "select item from Item item, Order ord\n"
+ + "where ord.items[ maxindex(ord.items) ] = item and ord.id = 11" );
+
+ parse( "select item from Item item, Order ord\n"
+ + "where ord.items[ size(ord.items) - 1 ] = item" );
+
+ parse( "from eg.DomesticCat cat where upper(cat.name) like 'FRI%'" );
+
+ parse( "select cust from Product prod, Store store\n"
+ + "inner join store.customers cust\n"
+ + "where prod.name = 'widget'\n"
+ + "and store.location.name in ( 'Melbourne', 'Sydney' )\n"
+ + "and prod = all elements(cust.currentOrder.lineItems)" );
+
+ }
+
+ public void testDocoExamples99() throws Exception {
+ parse( "from eg.DomesticCat cat\n"
+ + "order by cat.name asc, cat.weight desc, cat.birthdate" );
+ }
+
+ public void testDocoExamples910() throws Exception {
+ parse( "select cat.color, sum(cat.weight), count(cat)\n"
+ + "from eg.Cat cat group by cat.color" );
+ parse( "select foo.id, avg( elements(foo.names) ), max( indices(foo.names) )\n"
+ + "from eg.Foo foo group by foo.id" );
+ parse( "select cat.color, sum(cat.weight), count(cat)\n"
+ + "from eg.Cat cat group by cat.color\n"
+ + "having cat.color in (eg.Color.TABBY, eg.Color.BLACK)" );
+ parse( "select cat from eg.Cat cat join cat.kittens kitten\n"
+ + "group by cat having avg(kitten.weight) > 100\n"
+ + "order by count(kitten) asc, sum(kitten.weight) desc" );
+ }
+
+ public void testDocoExamples911() throws Exception {
+ parse( "from eg.Cat as fatcat where fatcat.weight > (\n"
+ + "select avg(cat.weight) from eg.DomesticCat cat)" );
+ parse( "from eg.DomesticCat as cat where cat.name = some (\n"
+ + "select name.nickName from eg.Name as name)\n" );
+ parse( "from eg.Cat as cat where not exists (\n"
+ + "from eg.Cat as mate where mate.mate = cat)" );
+ parse( "from eg.DomesticCat as cat where cat.name not in (\n"
+ + "select name.nickName from eg.Name as name)" );
+ }
+
+ public void testDocoExamples912() throws Exception {
+ parse( "select ord.id, sum(price.amount), count(item)\n"
+ + "from Order as ord join ord.lineItems as item\n"
+ + "join item.product as product, Catalog as catalog\n"
+ + "join catalog.prices as price\n"
+ + "where ord.paid = false\n"
+ + "and ord.customer = :customer\n"
+ + "and price.product = product\n"
+ + "and catalog.effectiveDate < sysdate\n"
+ + "and catalog.effectiveDate >= all (\n"
+ + "select cat.effectiveDate from Catalog as cat where cat.effectiveDate < sysdate)\n"
+ + "group by ord\n"
+ + "having sum(price.amount) > :minAmount\n"
+ + "order by sum(price.amount) desc" );
+
+ parse( "select ord.id, sum(price.amount), count(item)\n"
+ + "from Order as ord join ord.lineItems as item join item.product as product,\n"
+ + "Catalog as catalog join catalog.prices as price\n"
+ + "where ord.paid = false and ord.customer = :customer\n"
+ + "and price.product = product and catalog = :currentCatalog\n"
+ + "group by ord having sum(price.amount) > :minAmount\n"
+ + "order by sum(price.amount) desc" );
+
+ parse( "select count(payment), status.name \n"
+ + "from Payment as payment \n"
+ + " join payment.currentStatus as status\n"
+ + " join payment.statusChanges as statusChange\n"
+ + "where payment.status.name <> PaymentStatus.AWAITING_APPROVAL\n"
+ + " or (\n"
+ + " statusChange.timeStamp = ( \n"
+ + " select max(change.timeStamp) \n"
+ + " from PaymentStatusChange change \n"
+ + " where change.payment = payment\n"
+ + " )\n"
+ + " and statusChange.user <> :currentUser\n"
+ + " )\n"
+ + "group by status.name, status.sortOrder\n"
+ + "order by status.sortOrder" );
+ parse( "select count(payment), status.name \n"
+ + "from Payment as payment\n"
+ + " join payment.currentStatus as status\n"
+ + "where payment.status.name <> PaymentStatus.AWAITING_APPROVAL\n"
+ + " or payment.statusChanges[ maxIndex(payment.statusChanges) ].user <> :currentUser\n"
+ + "group by status.name, status.sortOrder\n"
+ + "order by status.sortOrder" );
+ parse( "select account, payment\n"
+ + "from Account as account\n"
+ + " left outer join account.payments as payment\n"
+ + "where :currentUser in elements(account.holder.users)\n"
+ + " and PaymentStatus.UNPAID = isNull(payment.currentStatus.name, PaymentStatus.UNPAID)\n"
+ + "order by account.type.sortOrder, account.accountNumber, payment.dueDate" );
+ parse( "select account, payment\n"
+ + "from Account as account\n"
+ + " join account.holder.users as user\n"
+ + " left outer join account.payments as payment\n"
+ + "where :currentUser = user\n"
+ + " and PaymentStatus.UNPAID = isNull(payment.currentStatus.name, PaymentStatus.UNPAID)\n"
+ + "order by account.type.sortOrder, account.accountNumber, payment.dueDate" );
+ }
+
+ public void testExamples1() throws Exception {
+ parse( "select new org.hibernate.test.S(s.count, s.address)\n"
+ + "from s in class Simple" );
+ parse( "select s.name, sysdate, trunc(s.pay), round(s.pay) from s in class Simple" );
+ parse( "select round(s.pay, 2) from s" );
+ parse( "select abs(round(s.pay)) from s in class Simple" );
+ parse( "select trunc(round(sysdate)) from s in class Simple" );
+ }
+
+ public void testArrayExpr() throws Exception {
+ parse( "from Order ord where ord.items[0].id = 1234" );
+ }
+
+ public void testMultipleActualParameters() throws Exception {
+ parse( "select round(s.pay, 2) from s" );
+ }
+
+ public void testMultipleFromClasses() throws Exception {
+ parse( "FROM eg.mypackage.Cat qat, com.toadstool.Foo f" );
+ parse( "FROM eg.mypackage.Cat qat, org.jabberwocky.Dipstick" );
+ }
+
+ public void testFromWithJoin() throws Exception {
+ parse( "FROM eg.mypackage.Cat qat, com.toadstool.Foo f join net.sf.blurb.Blurb" );
+ parse( "FROM eg.mypackage.Cat qat left join com.multijoin.JoinORama , com.toadstool.Foo f join net.sf.blurb.Blurb" );
+ }
+
+ public void testSelect() throws Exception {
+ parse( "SELECT f FROM eg.mypackage.Cat qat, com.toadstool.Foo f join net.sf.blurb.Blurb" );
+ parse( "SELECT DISTINCT bar FROM eg.mypackage.Cat qat left join com.multijoin.JoinORama as bar, com.toadstool.Foo f join net.sf.blurb.Blurb" );
+ parse( "SELECT count(*) FROM eg.mypackage.Cat qat" );
+ parse( "SELECT avg(qat.weight) FROM eg.mypackage.Cat qat" );
+ }
+
+ public void testWhere() throws Exception {
+ parse( "FROM eg.mypackage.Cat qat where qat.name like '%fluffy%' or qat.toes > 5" );
+ parse( "FROM eg.mypackage.Cat qat where not qat.name like '%fluffy%' or qat.toes > 5" );
+ parse( "FROM eg.mypackage.Cat qat where not qat.name not like '%fluffy%'" );
+ parse( "FROM eg.mypackage.Cat qat where qat.name in ('crater','bean','fluffy')" );
+ parse( "FROM eg.mypackage.Cat qat where qat.name not in ('crater','bean','fluffy')" );
+ parse( "from Animal an where sqrt(an.bodyWeight)/2 > 10" );
+ parse( "from Animal an where (an.bodyWeight > 10 and an.bodyWeight < 100) or an.bodyWeight is null" );
+ }
+
+ public void testGroupBy() throws Exception {
+ parse( "FROM eg.mypackage.Cat qat group by qat.breed" );
+ parse( "FROM eg.mypackage.Cat qat group by qat.breed, qat.eyecolor" );
+ }
+
+ public void testOrderBy() throws Exception {
+ parse( "FROM eg.mypackage.Cat qat order by avg(qat.toes)" );
+ parse( "from Animal an order by sqrt(an.bodyWeight)/2" );
+ }
+
+ public void testDoubleLiteral() throws Exception {
+ parse( "from eg.Cat as tinycat where fatcat.weight < 3.1415" );
+ parse( "from eg.Cat as enormouscat where fatcat.weight > 3.1415e3" );
+ }
+
+ public void testComplexConstructor() throws Exception {
+ parse( "select new Foo(count(bar)) from bar" );
+ parse( "select new Foo(count(bar),(select count(*) from doofus d where d.gob = 'fat' )) from bar" );
+ }
+
+
+ public void testInNotIn() throws Exception {
+ parse( "from foo where foo.bar in ('a' , 'b', 'c')" );
+ parse( "from foo where foo.bar not in ('a' , 'b', 'c')" );
+ }
+
+ public void testOperatorPrecedence() throws Exception {
+ parse( "from foo where foo.bar = 123 + foo.baz * foo.not" );
+ parse( "from foo where foo.bar like 'testzzz' || foo.baz or foo.bar in ('duh', 'gob')" );
+ }
+
+ /**
+ * Tests HQL generated by the other unit tests.
+ *
+ * @throws Exception if the HQL could not be parsed.
+ */
+ public void testUnitTestHql() throws Exception {
+ parse( "select foo from foo in class org.hibernate.test.Foo, fee in class org.hibernate.test.Fee where foo.dependent = fee order by foo.string desc, foo.component.count asc, fee.id" );
+ parse( "select foo.foo, foo.dependent from foo in class org.hibernate.test.Foo order by foo.foo.string desc, foo.component.count asc, foo.dependent.id" );
+ parse( "select foo from foo in class org.hibernate.test.Foo order by foo.dependent.id, foo.dependent.fi" );
+ parse( "SELECT one FROM one IN CLASS org.hibernate.test.One ORDER BY one.value ASC" );
+ parse( "SELECT many.one FROM many IN CLASS org.hibernate.test.Many ORDER BY many.one.value ASC, many.one.id" );
+ parse( "select foo.id from org.hibernate.test.Foo foo where foo.joinedProp = 'foo'" );
+ parse( "from org.hibernate.test.Foo foo inner join fetch foo.foo" );
+ parse( "from org.hibernate.test.Baz baz left outer join fetch baz.fooToGlarch" );
+ parse( "select foo.foo.foo.string from foo in class org.hibernate.test.Foo where foo.foo = 'bar'" );
+ parse( "select foo.foo.foo.foo.string from foo in class org.hibernate.test.Foo where foo.foo.foo = 'bar'" );
+ parse( "select foo.foo.foo.string from foo in class org.hibernate.test.Foo where foo.foo.foo.foo.string = 'bar'" );
+ parse( "select foo.string from foo in class org.hibernate.test.Foo where foo.foo.foo = 'bar' and foo.foo.foo.foo = 'baz'" );
+ parse( "select foo.string from foo in class org.hibernate.test.Foo where foo.foo.foo.foo.string = 'a' and foo.foo.string = 'b'" );
+ parse( "from org.hibernate.test.Foo as foo where foo.component.glarch.name is not null" );
+ parse( "from org.hibernate.test.Foo as foo left outer join foo.component.glarch as glarch where glarch.name = 'foo'" );
+ parse( "from org.hibernate.test.Foo" );
+ parse( "from org.hibernate.test.Foo foo left outer join foo.foo" );
+ parse( "from org.hibernate.test.Foo, org.hibernate.test.Bar" );
+ parse( "from org.hibernate.test.Baz baz left join baz.fooToGlarch, org.hibernate.test.Bar bar join bar.foo" );
+ parse( "from org.hibernate.test.Baz baz left join baz.fooToGlarch join baz.fooSet" );
+ parse( "from org.hibernate.test.Baz baz left join baz.fooToGlarch join fetch baz.fooSet foo left join fetch foo.foo" );
+ parse( "from foo in class org.hibernate.test.Foo where foo.string='osama bin laden' and foo.boolean = true order by foo.string asc, foo.component.count desc" );
+ parse( "from foo in class org.hibernate.test.Foo where foo.string='osama bin laden' order by foo.string asc, foo.component.count desc" );
+ parse( "select foo.foo from foo in class org.hibernate.test.Foo" );
+ parse( "from foo in class org.hibernate.test.Foo where foo.component.count is null order by foo.component.count" );
+ parse( "from foo in class org.hibernate.test.Foo where foo.component.name='foo'" );
+ parse( "select distinct foo.component.name, foo.component.name from foo in class org.hibernate.test.Foo where foo.component.name='foo'" );
+ parse( "select distinct foo.component.name, foo.id from foo in class org.hibernate.test.Foo where foo.component.name='foo'" );
+ parse( "from foo in class org.hibernate.test.Foo where foo.id=?" );
+ parse( "from foo in class org.hibernate.test.Foo where foo.key=?" );
+ parse( "select foo.foo from foo in class org.hibernate.test.Foo where foo.string='fizard'" );
+ parse( "from foo in class org.hibernate.test.Foo where foo.component.subcomponent.name='bar'" );
+ parse( "select foo.foo from foo in class org.hibernate.test.Foo where foo.foo.id=?" );
+ parse( "from foo in class org.hibernate.test.Foo where foo.foo = ?" );
+ parse( "from bar in class org.hibernate.test.Bar where bar.string='a string' or bar.string='a string'" );
+ parse( "select foo.component.name, elements(foo.component.importantDates) from foo in class org.hibernate.test.Foo where foo.foo.id=?" );
+ parse( "select max(elements(foo.component.importantDates)) from foo in class org.hibernate.test.Foo group by foo.id" );
+ parse( "select foo.foo.foo.foo from foo in class org.hibernate.test.Foo, foo2 in class org.hibernate.test.Foo where foo = foo2.foo and not not ( not foo.string='fizard' ) and foo2.string between 'a' and (foo.foo.string) and ( foo2.string in ( 'fiz', 'blah') or 1=1 )" );
+ parse( "from foo in class org.hibernate.test.Foo where foo.string='from BoogieDown -tinsel town =!@#$^&*())'" );
+ parse( "from foo in class org.hibernate.test.Foo where not foo.string='foo''bar'" ); // Added quote quote is an escape
+ parse( "from foo in class org.hibernate.test.Foo where foo.component.glarch.next is null" );
+ parse( " from bar in class org.hibernate.test.Bar where bar.baz.count=667 and bar.baz.count!=123 and not bar.baz.name='1-E-1'" );
+ parse( " from i in class org.hibernate.test.Bar where i.baz.name='Bazza'" );
+ parse( "select count(distinct foo.foo) from foo in class org.hibernate.test.Foo" );
+ parse( "select count(foo.foo.boolean) from foo in class org.hibernate.test.Foo" );
+ parse( "select count(*), foo.int from foo in class org.hibernate.test.Foo group by foo.int" );
+ parse( "select sum(foo.foo.int) from foo in class org.hibernate.test.Foo" );
+ parse( "select count(foo) from foo in class org.hibernate.test.Foo where foo.id=?" );
+ parse( "from foo in class org.hibernate.test.Foo where foo.boolean = ?" );
+ parse( "select new Foo(fo.x) from org.hibernate.test.Fo fo" );
+ parse( "select new Foo(fo.integer) from org.hibernate.test.Foo fo" );
+ parse( "select new Foo(fo.x) from org.hibernate.test.Foo fo" );
+ parse( "select foo.long, foo.component.name, foo, foo.foo from foo in class org.hibernate.test.Foo" );
+ parse( "select avg(foo.float), max(foo.component.name), count(distinct foo.id) from foo in class org.hibernate.test.Foo" );
+ parse( "select foo.long, foo.component, foo, foo.foo from foo in class org.hibernate.test.Foo" );
+ parse( "from o in class org.hibernate.test.MoreStuff" );
+ parse( "from o in class org.hibernate.test.Many" );
+ parse( "from o in class org.hibernate.test.Fee" );
+ parse( "from o in class org.hibernate.test.Qux" );
+ parse( "from o in class org.hibernate.test.Y" );
+ parse( "from o in class org.hibernate.test.Fumm" );
+ parse( "from o in class org.hibernate.test.X" );
+ parse( "from o in class org.hibernate.test.Simple" );
+ parse( "from o in class org.hibernate.test.Location" );
+ parse( "from o in class org.hibernate.test.Holder" );
+ parse( "from o in class org.hibernate.test.Part" );
+ parse( "from o in class org.hibernate.test.Baz" );
+ parse( "from o in class org.hibernate.test.Vetoer" );
+ parse( "from o in class org.hibernate.test.Sortable" );
+ parse( "from o in class org.hibernate.test.Contained" );
+ parse( "from o in class org.hibernate.test.Stuff" );
+ parse( "from o in class org.hibernate.test.Immutable" );
+ parse( "from o in class org.hibernate.test.Container" );
+ parse( "from o in class org.hibernate.test.X$XX" );
+ parse( "from o in class org.hibernate.test.One" );
+ parse( "from o in class org.hibernate.test.Foo" );
+ parse( "from o in class org.hibernate.test.Fo" );
+ parse( "from o in class org.hibernate.test.Glarch" );
+ parse( "from o in class org.hibernate.test.Fum" );
+ parse( "from n in class org.hibernate.test.Holder" );
+ parse( "from n in class org.hibernate.test.Baz" );
+ parse( "from n in class org.hibernate.test.Bar" );
+ parse( "from n in class org.hibernate.test.Glarch" );
+ parse( "from n in class org.hibernate.test.Holder where n.name is not null" );
+ parse( "from n in class org.hibernate.test.Baz where n.name is not null" );
+ parse( "from n in class org.hibernate.test.Bar where n.name is not null" );
+ parse( "from n in class org.hibernate.test.Glarch where n.name is not null" );
+ parse( "from n in class org.hibernate.test.Holder" );
+ parse( "from n in class org.hibernate.test.Baz" );
+ parse( "from n in class org.hibernate.test.Bar" );
+ parse( "from n in class org.hibernate.test.Glarch" );
+ parse( "from n0 in class org.hibernate.test.Holder, n1 in class org.hibernate.test.Holder where n0.name = n1.name" );
+ parse( "from n0 in class org.hibernate.test.Baz, n1 in class org.hibernate.test.Holder where n0.name = n1.name" );
+ parse( "from n0 in class org.hibernate.test.Bar, n1 in class org.hibernate.test.Holder where n0.name = n1.name" );
+ parse( "from n0 in class org.hibernate.test.Glarch, n1 in class org.hibernate.test.Holder where n0.name = n1.name" );
+ parse( "from n0 in class org.hibernate.test.Holder, n1 in class org.hibernate.test.Baz where n0.name = n1.name" );
+ parse( "from n0 in class org.hibernate.test.Baz, n1 in class org.hibernate.test.Baz where n0.name = n1.name" );
+ parse( "from n0 in class org.hibernate.test.Bar, n1 in class org.hibernate.test.Baz where n0.name = n1.name" );
+ parse( "from n0 in class org.hibernate.test.Glarch, n1 in class org.hibernate.test.Baz where n0.name = n1.name" );
+ parse( "from n0 in class org.hibernate.test.Holder, n1 in class org.hibernate.test.Bar where n0.name = n1.name" );
+ parse( "from n0 in class org.hibernate.test.Baz, n1 in class org.hibernate.test.Bar where n0.name = n1.name" );
+ parse( "from n0 in class org.hibernate.test.Bar, n1 in class org.hibernate.test.Bar where n0.name = n1.name" );
+ parse( "from n0 in class org.hibernate.test.Glarch, n1 in class org.hibernate.test.Bar where n0.name = n1.name" );
+ parse( "from n0 in class org.hibernate.test.Holder, n1 in class org.hibernate.test.Glarch where n0.name = n1.name" );
+ parse( "from n0 in class org.hibernate.test.Baz, n1 in class org.hibernate.test.Glarch where n0.name = n1.name" );
+ parse( "from n0 in class org.hibernate.test.Bar, n1 in class org.hibernate.test.Glarch where n0.name = n1.name" );
+ parse( "from n0 in class org.hibernate.test.Glarch, n1 in class org.hibernate.test.Glarch where n0.name = n1.name" );
+ parse( "from n in class org.hibernate.test.Holder where n.name = :name" );
+ parse( "from o in class org.hibernate.test.MoreStuff" );
+ parse( "from o in class org.hibernate.test.Many" );
+ parse( "from o in class org.hibernate.test.Fee" );
+ parse( "from o in class org.hibernate.test.Qux" );
+ parse( "from o in class org.hibernate.test.Y" );
+ parse( "from o in class org.hibernate.test.Fumm" );
+ parse( "from o in class org.hibernate.test.X" );
+ parse( "from o in class org.hibernate.test.Simple" );
+ parse( "from o in class org.hibernate.test.Location" );
+ parse( "from o in class org.hibernate.test.Holder" );
+ parse( "from o in class org.hibernate.test.Part" );
+ parse( "from o in class org.hibernate.test.Baz" );
+ parse( "from o in class org.hibernate.test.Vetoer" );
+ parse( "from o in class org.hibernate.test.Sortable" );
+ parse( "from o in class org.hibernate.test.Contained" );
+ parse( "from o in class org.hibernate.test.Stuff" );
+ parse( "from o in class org.hibernate.test.Immutable" );
+ parse( "from o in class org.hibernate.test.Container" );
+ parse( "from o in class org.hibernate.test.X$XX" );
+ parse( "from o in class org.hibernate.test.One" );
+ parse( "from o in class org.hibernate.test.Foo" );
+ parse( "from o in class org.hibernate.test.Fo" );
+ parse( "from o in class org.hibernate.test.Glarch" );
+ parse( "from o in class org.hibernate.test.Fum" );
+ parse( "select baz.code, min(baz.count) from baz in class org.hibernate.test.Baz group by baz.code" );
+ parse( "selecT baz from baz in class org.hibernate.test.Baz where baz.stringDateMap['foo'] is not null or baz.stringDateMap['bar'] = ?" );
+ parse( "select baz from baz in class org.hibernate.test.Baz where baz.stringDateMap['now'] is not null" );
+ parse( "select baz from baz in class org.hibernate.test.Baz where baz.stringDateMap['now'] is not null and baz.stringDateMap['big bang'] < baz.stringDateMap['now']" );
+ parse( "select index(date) from org.hibernate.test.Baz baz join baz.stringDateMap date" );
+ parse( "from foo in class org.hibernate.test.Foo where foo.integer not between 1 and 5 and foo.string not in ('cde', 'abc') and foo.string is not null and foo.integer<=3" );
+ parse( "from org.hibernate.test.Baz baz inner join baz.collectionComponent.nested.foos foo where foo.string is null" );
+ parse( "from org.hibernate.test.Baz baz inner join baz.fooSet where '1' in (from baz.fooSet foo where foo.string is not null)" );
+ parse( "from org.hibernate.test.Baz baz where 'a' in elements(baz.collectionComponent.nested.foos) and 1.0 in elements(baz.collectionComponent.nested.floats)" );
+ parse( "from org.hibernate.test.Foo foo join foo.foo where foo.foo in ('1','2','3')" );
+ parse( "select foo.foo from org.hibernate.test.Foo foo where foo.foo in ('1','2','3')" );
+ parse( "select foo.foo.string from org.hibernate.test.Foo foo where foo.foo in ('1','2','3')" );
+ parse( "select foo.foo.string from org.hibernate.test.Foo foo where foo.foo.string in ('1','2','3')" );
+ parse( "select foo.foo.long from org.hibernate.test.Foo foo where foo.foo.string in ('1','2','3')" );
+ parse( "select count(*) from org.hibernate.test.Foo foo where foo.foo.string in ('1','2','3') or foo.foo.long in (1,2,3)" );
+ parse( "select count(*) from org.hibernate.test.Foo foo where foo.foo.string in ('1','2','3') group by foo.foo.long" );
+ parse( "from org.hibernate.test.Foo foo1 left join foo1.foo foo2 left join foo2.foo where foo1.string is not null" );
+ parse( "from org.hibernate.test.Foo foo1 left join foo1.foo.foo where foo1.string is not null" );
+ parse( "from org.hibernate.test.Foo foo1 left join foo1.foo foo2 left join foo1.foo.foo foo3 where foo1.string is not null" );
+ parse( "select foo.formula from org.hibernate.test.Foo foo where foo.formula > 0" );
+ parse( "from org.hibernate.test.Foo as foo join foo.foo as foo2 where foo2.id >'a' or foo2.id <'a'" );
+ parse( "from org.hibernate.test.Holder" );
+ parse( "from org.hibernate.test.Baz baz left outer join fetch baz.manyToAny" );
+ parse( "from org.hibernate.test.Baz baz join baz.manyToAny" );
+ parse( "select baz from org.hibernate.test.Baz baz join baz.manyToAny a where index(a) = 0" );
+ parse( "select bar from org.hibernate.test.Bar bar where bar.baz.stringDateMap['now'] is not null" );
+ parse( "select bar from org.hibernate.test.Bar bar join bar.baz b where b.stringDateMap['big bang'] < b.stringDateMap['now'] and b.stringDateMap['now'] is not null" );
+ parse( "select bar from org.hibernate.test.Bar bar where bar.baz.stringDateMap['big bang'] < bar.baz.stringDateMap['now'] and bar.baz.stringDateMap['now'] is not null" );
+ parse( "select foo.string, foo.component, foo.id from org.hibernate.test.Bar foo" );
+ parse( "select elements(baz.components) from org.hibernate.test.Baz baz" );
+ parse( "select bc.name from org.hibernate.test.Baz baz join baz.components bc" );
+ parse( "from org.hibernate.test.Foo foo where foo.integer < 10 order by foo.string" );
+ parse( "from org.hibernate.test.Fee" );
+ parse( "from org.hibernate.test.Holder h join h.otherHolder oh where h.otherHolder.name = 'bar'" );
+ parse( "from org.hibernate.test.Baz baz join baz.fooSet foo join foo.foo.foo foo2 where foo2.string = 'foo'" );
+ parse( "from org.hibernate.test.Baz baz join baz.fooArray foo join foo.foo.foo foo2 where foo2.string = 'foo'" );
+ parse( "from org.hibernate.test.Baz baz join baz.stringDateMap date where index(date) = 'foo'" );
+ parse( "from org.hibernate.test.Baz baz join baz.topGlarchez g where index(g) = 'A'" );
+ parse( "select index(g) from org.hibernate.test.Baz baz join baz.topGlarchez g" );
+ parse( "from org.hibernate.test.Baz baz left join baz.stringSet" );
+ parse( "from org.hibernate.test.Baz baz join baz.stringSet str where str='foo'" );
+ parse( "from org.hibernate.test.Baz baz left join fetch baz.stringSet" );
+ parse( "from org.hibernate.test.Baz baz join baz.stringSet string where string='foo'" );
+ parse( "from org.hibernate.test.Baz baz inner join baz.components comp where comp.name='foo'" );
+ parse( "from org.hibernate.test.Glarch g inner join g.fooComponents comp where comp.fee is not null" );
+ parse( "from org.hibernate.test.Glarch g inner join g.fooComponents comp join comp.fee fee where fee.count > 0" );
+ parse( "from org.hibernate.test.Glarch g inner join g.fooComponents comp where comp.fee.count is not null" );
+ parse( "from org.hibernate.test.Baz baz left join fetch baz.fooBag" );
+ parse( "from org.hibernate.test.Glarch" );
+ parse( "from org.hibernate.test.Fee" );
+ parse( "from org.hibernate.test.Baz baz left join fetch baz.sortablez order by baz.name asc" );
+ parse( "from org.hibernate.test.Baz baz order by baz.name asc" );
+ parse( "from org.hibernate.test.Foo foo, org.hibernate.test.Baz baz left join fetch baz.fees" );
+ parse( "from org.hibernate.test.Foo foo, org.hibernate.test.Bar bar" );
+ parse( "from org.hibernate.test.Foo foo" );
+ parse( "from org.hibernate.test.Foo foo, org.hibernate.test.Bar bar, org.hibernate.test.Bar bar2" );
+ parse( "from org.hibernate.test.X x" );
+ parse( "from org.hibernate.test.Foo foo" );
+ parse( "select distinct foo from org.hibernate.test.Foo foo" );
+ parse( "from org.hibernate.test.Glarch g where g.multiple.glarch=g and g.multiple.count=12" );
+ parse( "from org.hibernate.test.Bar bar left join bar.baz baz left join baz.cascadingBars b where bar.name like 'Bar %'" );
+ parse( "select bar, b from org.hibernate.test.Bar bar left join bar.baz baz left join baz.cascadingBars b where bar.name like 'Bar%'" );
+ parse( "select bar, b from org.hibernate.test.Bar bar left join bar.baz baz left join baz.cascadingBars b where ( bar.name in (:nameList0_, :nameList1_, :nameList2_) or bar.name in (:nameList0_, :nameList1_, :nameList2_) ) and bar.string = :stringVal" );
+ parse( "select bar, b from org.hibernate.test.Bar bar inner join bar.baz baz inner join baz.cascadingBars b where bar.name like 'Bar%'" );
+ parse( "select bar, b from org.hibernate.test.Bar bar left join bar.baz baz left join baz.cascadingBars b where bar.name like :name and b.name like :name" );
+ parse( "select bar from org.hibernate.test.Bar as bar where bar.x > ? or bar.short = 1 or bar.string = 'ff ? bb'" );
+ parse( "select bar from org.hibernate.test.Bar as bar where bar.string = ' ? ' or bar.string = '?'" );
+ parse( "from org.hibernate.test.Baz baz, baz.fooArray foo" );
+ parse( "from s in class org.hibernate.test.Stuff where s.foo.id = ? and s.id.id = ? and s.moreStuff.id.intId = ? and s.moreStuff.id.stringId = ?" );
+ parse( "from s in class org.hibernate.test.Stuff where s.foo.id = ? and s.id.id = ? and s.moreStuff.name = ?" );
+ parse( "from s in class org.hibernate.test.Stuff where s.foo.string is not null" );
+ parse( "from s in class org.hibernate.test.Stuff where s.foo > '0' order by s.foo" );
+ parse( "from ms in class org.hibernate.test.MoreStuff" );
+ parse( "from foo in class org.hibernate.test.Foo" );
+ parse( "from fee in class org.hibernate.test.Fee" );
+ parse( "select new Result(foo.string, foo.long, foo.integer) from foo in class org.hibernate.test.Foo" );
+ parse( "select new Result( baz.name, foo.long, count(elements(baz.fooArray)) ) from org.hibernate.test.Baz baz join baz.fooArray foo group by baz.name, foo.long" );
+ parse( "select new Result( baz.name, max(foo.long), count(foo) ) from org.hibernate.test.Baz baz join baz.fooArray foo group by baz.name" );
+ parse( "select max( elements(bar.baz.fooArray) ) from org.hibernate.test.Bar as bar" );
+ parse( "from org.hibernate.test.Baz baz left join baz.fooToGlarch join fetch baz.fooArray foo left join fetch foo.foo" );
+ parse( "select baz.name from org.hibernate.test.Bar bar inner join bar.baz baz inner join baz.fooSet foo where baz.name = bar.string" );
+ parse( "SELECT baz.name FROM org.hibernate.test.Bar AS bar INNER JOIN bar.baz AS baz INNER JOIN baz.fooSet AS foo WHERE baz.name = bar.string" );
+ parse( "select baz.name from org.hibernate.test.Bar bar join bar.baz baz left outer join baz.fooSet foo where baz.name = bar.string" );
+ parse( "select baz.name from org.hibernate.test.Bar bar, bar.baz baz, baz.fooSet foo where baz.name = bar.string" );
+ parse( "SELECT baz.name FROM org.hibernate.test.Bar AS bar, bar.baz AS baz, baz.fooSet AS foo WHERE baz.name = bar.string" );
+ parse( "select baz.name from org.hibernate.test.Bar bar left join bar.baz baz left join baz.fooSet foo where baz.name = bar.string" );
+ parse( "select foo.string from org.hibernate.test.Bar bar left join bar.baz.fooSet foo where bar.string = foo.string" );
+ parse( "select baz.name from org.hibernate.test.Bar bar left join bar.baz baz left join baz.fooArray foo where baz.name = bar.string" );
+ parse( "select foo.string from org.hibernate.test.Bar bar left join bar.baz.fooArray foo where bar.string = foo.string" );
+ parse( "select foo from bar in class org.hibernate.test.Bar inner join bar.baz as baz inner join baz.fooSet as foo" );
+ parse( "select foo from bar in class org.hibernate.test.Bar inner join bar.baz.fooSet as foo" );
+ parse( "select foo from bar in class org.hibernate.test.Bar, bar.baz as baz, baz.fooSet as foo" );
+ parse( "select foo from bar in class org.hibernate.test.Bar, bar.baz.fooSet as foo" );
+ parse( "from org.hibernate.test.Bar bar join bar.baz.fooArray foo" );
+ parse( "from bar in class org.hibernate.test.Bar, foo in elements( bar.baz.fooArray )" );
+ parse( "select one.id, elements(one.manies) from one in class org.hibernate.test.One" );
+ parse( "select max( elements(one.manies) ) from one in class org.hibernate.test.One" );
+ parse( "select one, elements(one.manies) from one in class org.hibernate.test.One" );
+ parse( "select one, max(elements(one.manies)) from one in class org.hibernate.test.One group by one" );
+ parse( "select elements(baz.fooArray) from baz in class org.hibernate.test.Baz where baz.id=?" );
+ parse( "select elements(baz.fooArray) from baz in class org.hibernate.test.Baz where baz.id=?" );
+ parse( "select indices(baz.fooArray) from baz in class org.hibernate.test.Baz where baz.id=?" );
+ parse( "select baz, max(elements(baz.timeArray)) from baz in class org.hibernate.test.Baz group by baz" );
+ parse( "select baz, baz.stringSet.size, count(distinct elements(baz.stringSet)), max(elements(baz.stringSet)) from baz in class org.hibernate.test.Baz group by baz" );
+ parse( "select max( elements(baz.timeArray) ) from baz in class org.hibernate.test.Baz where baz.id=?" );
+ parse( "select max(elements(baz.stringSet)) from baz in class org.hibernate.test.Baz where baz.id=?" );
+ parse( "select size(baz.stringSet) from baz in class org.hibernate.test.Baz where baz.id=?" );
+ parse( "from org.hibernate.test.Foo foo where foo.component.glarch.id is not null" );
+ parse( "from baz in class org.hibernate.test.Baz" );
+ parse( "select elements(baz.stringArray) from baz in class org.hibernate.test.Baz" );
+ parse( "from foo in class org.hibernate.test.Foo" );
+ parse( "select elements(baz.stringList) from baz in class org.hibernate.test.Baz" );
+ parse( "select count(*) from org.hibernate.test.Bar" );
+ parse( "select count(*) from b in class org.hibernate.test.Bar" );
+ parse( "from g in class org.hibernate.test.Glarch" );
+ parse( "select baz, baz from baz in class org.hibernate.test.Baz" );
+ parse( "select baz from baz in class org.hibernate.test.Baz order by baz" );
+ parse( "from bar in class org.hibernate.test.Bar" );
+ parse( "from g in class org.hibernate.test.Glarch" );
+ parse( "from f in class org.hibernate.test.Foo" );
+ parse( "from o in class org.hibernate.test.One" );
+ parse( "from q in class org.hibernate.test.Qux" );
+ parse( "select foo from foo in class org.hibernate.test.Foo where foo.string='foo bar'" );
+ parse( "from foo in class org.hibernate.test.Foo order by foo.string, foo.date" );
+ parse( "from foo in class org.hibernate.test.Foo where foo.class='B'" );
+ parse( "from foo in class org.hibernate.test.Foo where foo.class=Bar" );
+ parse( "select bar from bar in class org.hibernate.test.Bar, foo in class org.hibernate.test.Foo where bar.string = foo.string and not bar=foo" );
+ parse( "from foo in class org.hibernate.test.Foo where foo.string='foo bar'" );
+ parse( "select foo from foo in class org.hibernate.test.Foo" );
+ parse( "from bar in class org.hibernate.test.Bar where bar.barString='bar bar'" );
+ parse( "from t in class org.hibernate.test.Trivial" );
+ parse( "from foo in class org.hibernate.test.Foo where foo.date = ?" );
+ parse( "from o in class org.hibernate.test.MoreStuff" );
+ parse( "from o in class org.hibernate.test.Many" );
+ parse( "from o in class org.hibernate.test.Fee" );
+ parse( "from o in class org.hibernate.test.Qux" );
+ parse( "from o in class org.hibernate.test.Y" );
+ parse( "from o in class org.hibernate.test.Fumm" );
+ parse( "from o in class org.hibernate.test.X" );
+ parse( "from o in class org.hibernate.test.Simple" );
+ parse( "from o in class org.hibernate.test.Location" );
+ parse( "from o in class org.hibernate.test.Holder" );
+ parse( "from o in class org.hibernate.test.Part" );
+ parse( "from o in class org.hibernate.test.Baz" );
+ parse( "from o in class org.hibernate.test.Vetoer" );
+ parse( "from o in class org.hibernate.test.Sortable" );
+ parse( "from o in class org.hibernate.test.Contained" );
+ parse( "from o in class org.hibernate.test.Stuff" );
+ parse( "from o in class org.hibernate.test.Immutable" );
+ parse( "from o in class org.hibernate.test.Container" );
+ parse( "from o in class org.hibernate.test.X$XX" );
+ parse( "from o in class org.hibernate.test.One" );
+ parse( "from o in class org.hibernate.test.Foo" );
+ parse( "from o in class org.hibernate.test.Fo" );
+ parse( "from o in class org.hibernate.test.Glarch" );
+ parse( "from o in class org.hibernate.test.Fum" );
+ parse( "from q in class org.hibernate.test.Qux where q.stuff is null" );
+ parse( "from q in class org.hibernate.test.Qux where q.stuff=?" );
+ parse( "from q in class org.hibernate.test.Qux" );
+ parse( "from g in class org.hibernate.test.Glarch where g.version=2" );
+ parse( "from g in class org.hibernate.test.Glarch where g.next is not null" );
+ parse( "from g in class org.hibernate.test.Glarch order by g.order asc" );
+ parse( "from foo in class org.hibernate.test.Foo order by foo.string asc" );
+ parse( "select parent, child from parent in class org.hibernate.test.Foo, child in class org.hibernate.test.Foo where parent.foo = child" );
+ parse( "select count(distinct child.id), count(distinct parent.id) from parent in class org.hibernate.test.Foo, child in class org.hibernate.test.Foo where parent.foo = child" );
+ parse( "select child.id, parent.id, child.long from parent in class org.hibernate.test.Foo, child in class org.hibernate.test.Foo where parent.foo = child" );
+ parse( "select child.id, parent.id, child.long, child, parent.foo from parent in class org.hibernate.test.Foo, child in class org.hibernate.test.Foo where parent.foo = child" );
+ parse( "select parent, child from parent in class org.hibernate.test.Foo, child in class org.hibernate.test.Foo where parent.foo = child and parent.string='a string'" );
+ parse( "from fee in class org.hibernate.test.Fee" );
+ parse( "from org.hibernate.test.Foo foo where foo.custom.s1 = 'one'" );
+ parse( "from im in class org.hibernate.test.Immutable where im = ?" );
+ parse( "from foo in class org.hibernate.test.Foo" );
+ parse( "from foo in class org.hibernate.test.Foo where foo.char='X'" );
+ parse( "select elements(baz.stringArray) from baz in class org.hibernate.test.Baz" );
+ parse( "select distinct elements(baz.stringArray) from baz in class org.hibernate.test.Baz" );
+ parse( "select elements(baz.fooArray) from baz in class org.hibernate.test.Baz" );
+ parse( "from foo in class org.hibernate.test.Fo" );
+ parse( "from foo in class org.hibernate.test.Foo where foo.dependent.qux.foo.string = 'foo2'" );
+ parse( "from org.hibernate.test.Bar bar where bar.object.id = ? and bar.object.class = ?" );
+ parse( "select one from org.hibernate.test.One one, org.hibernate.test.Bar bar where bar.object.id = one.id and bar.object.class = 'O'" );
+ parse( "from l in class org.hibernate.test.Location where l.countryCode = 'AU' and l.description='foo bar'" );
+ parse( "from org.hibernate.test.Bar bar" );
+ parse( "From org.hibernate.test.Bar bar" );
+ parse( "From org.hibernate.test.Foo foo" );
+ parse( "from o in class org.hibernate.test.Baz" );
+ parse( "from o in class org.hibernate.test.Foo" );
+ parse( "from f in class org.hibernate.test.Foo" );
+ parse( "select fum.id from fum in class org.hibernate.test.Fum where not fum.fum='FRIEND'" );
+ parse( "select fum.id from fum in class org.hibernate.test.Fum where not fum.fum='FRIEND'" );
+ parse( "from fum in class org.hibernate.test.Fum where not fum.fum='FRIEND'" );
+ parse( "from fo in class org.hibernate.test.Fo where fo.id.string like 'an instance of fo'" );
+ parse( "from org.hibernate.test.Inner" );
+ parse( "from org.hibernate.test.Outer o where o.id.detailId = ?" );
+ parse( "from org.hibernate.test.Outer o where o.id.master.id.sup.dudu is not null" );
+ parse( "from org.hibernate.test.Outer o where o.id.master.id.sup.id.akey is not null" );
+ parse( "select o.id.master.id.sup.dudu from org.hibernate.test.Outer o where o.id.master.id.sup.dudu is not null" );
+ parse( "select o.id.master.id.sup.id.akey from org.hibernate.test.Outer o where o.id.master.id.sup.id.akey is not null" );
+ parse( "from org.hibernate.test.Outer o where o.id.master.bla = ''" );
+ parse( "from org.hibernate.test.Outer o where o.id.master.id.one = ''" );
+ parse( "from org.hibernate.test.Inner inn where inn.id.bkey is not null and inn.backOut.id.master.id.sup.id.akey > 'a'" );
+ parse( "from org.hibernate.test.Outer as o left join o.id.master m left join m.id.sup where o.bubu is not null" );
+ parse( "from org.hibernate.test.Outer as o left join o.id.master.id.sup s where o.bubu is not null" );
+ parse( "from org.hibernate.test.Outer as o left join o.id.master m left join o.id.master.id.sup s where o.bubu is not null" );
+ parse( "select fum1.fo from fum1 in class org.hibernate.test.Fum where fum1.fo.fum is not null" );
+ parse( "from fum1 in class org.hibernate.test.Fum where fum1.fo.fum is not null order by fum1.fo.fum" );
+ parse( "select elements(fum1.friends) from fum1 in class org.hibernate.test.Fum" );
+ parse( "from fum1 in class org.hibernate.test.Fum, fr in elements( fum1.friends )" );
+ parse( "select new Jay(eye) from org.hibernate.test.Eye eye" );
+ parse( "from org.hibernate.test.Category cat where cat.name='new foo'" );
+ parse( "from org.hibernate.test.Category cat where cat.name='new sub'" );
+ parse( "from org.hibernate.test.Up up order by up.id2 asc" );
+ parse( "from org.hibernate.test.Down down" );
+ parse( "from org.hibernate.test.Up up" );
+ parse( "from m in class org.hibernate.test.Master" );
+ parse( "from s in class org.hibernate.test.Several" );
+ parse( "from s in class org.hibernate.test.Single" );
+ parse( "\n" +
+ " from d in class \n" +
+ " org.hibernate.test.Detail\n" +
+ " " );
+ parse( "from c in class org.hibernate.test.Category where c.name = org.hibernate.test.Category.ROOT_CATEGORY" );
+ parse( "select c from c in class org.hibernate.test.Container, s in class org.hibernate.test.Simple where c.oneToMany[2] = s" );
+ parse( "select c from c in class org.hibernate.test.Container, s in class org.hibernate.test.Simple where c.manyToMany[2] = s" );
+ parse( "select c from c in class org.hibernate.test.Container, s in class org.hibernate.test.Simple where s = c.oneToMany[2]" );
+ parse( "select c from c in class org.hibernate.test.Container, s in class org.hibernate.test.Simple where s = c.manyToMany[2]" );
+ parse( "select c from c in class org.hibernate.test.Container where c.oneToMany[0].name = 's'" );
+ parse( "select c from c in class org.hibernate.test.Container where c.manyToMany[0].name = 's'" );
+ parse( "select c from c in class org.hibernate.test.Container where 's' = c.oneToMany[2 - 2].name" );
+ parse( "select c from c in class org.hibernate.test.Container where 's' = c.manyToMany[(3+1)/4-1].name" );
+ parse( "select c from c in class org.hibernate.test.Container where c.manyToMany[ maxindex(c.manyToMany) ].count = 2" );
+ parse( "select c from c in class org.hibernate.test.Container where c.oneToMany[ c.manyToMany[0].count ].name = 's'" );
+ parse( "select c from org.hibernate.test.Container c where c.manyToMany[ c.oneToMany[0].count ].name = 's'" );
+ parse( "select count(comp.name) from org.hibernate.test.Container c join c.components comp" );
+ parse( "from org.hibernate.test.Parent p left join fetch p.child" );
+ parse( "from org.hibernate.test.Parent p join p.child c where c.x > 0" );
+ parse( "from org.hibernate.test.Child c join c.parent p where p.x > 0" );
+ parse( "from org.hibernate.test.Child" );
+ parse( "from org.hibernate.test.MoreStuff" );
+ parse( "from org.hibernate.test.Many" );
+ parse( "from org.hibernate.test.Fee" );
+ parse( "from org.hibernate.test.Qux" );
+ parse( "from org.hibernate.test.Fumm" );
+ parse( "from org.hibernate.test.Parent" );
+ parse( "from org.hibernate.test.Simple" );
+ parse( "from org.hibernate.test.Holder" );
+ parse( "from org.hibernate.test.Part" );
+ parse( "from org.hibernate.test.Baz" );
+ parse( "from org.hibernate.test.Vetoer" );
+ parse( "from org.hibernate.test.Sortable" );
+ parse( "from org.hibernate.test.Contained" );
+ parse( "from org.hibernate.test.Circular" );
+ parse( "from org.hibernate.test.Stuff" );
+ parse( "from org.hibernate.test.Immutable" );
+ parse( "from org.hibernate.test.Container" );
+ parse( "from org.hibernate.test.One" );
+ parse( "from org.hibernate.test.Foo" );
+ parse( "from org.hibernate.test.Fo" );
+ parse( "from org.hibernate.test.Glarch" );
+ parse( "from org.hibernate.test.Fum" );
+ parse( "from org.hibernate.test.Glarch g" );
+ parse( "from org.hibernate.test.Part" );
+ parse( "from org.hibernate.test.Baz baz join baz.parts" );
+ parse( "from c in class org.hibernate.test.Child where c.parent.count=66" );
+ parse( "from org.hibernate.test.Parent p join p.child c where p.count=66" );
+ parse( "select c, c.parent from c in class org.hibernate.test.Child order by c.parent.count" );
+ parse( "select c, c.parent from c in class org.hibernate.test.Child where c.parent.count=66 order by c.parent.count" );
+ parse( "select c, c.parent, c.parent.count from c in class org.hibernate.test.Child order by c.parent.count" );
+ parse( "FROM p IN CLASS org.hibernate.test.Parent WHERE p.count = ?" );
+ parse( "select count(*) from org.hibernate.test.Container as c join c.components as ce join ce.simple as s where ce.name='foo'" );
+ parse( "select c, s from org.hibernate.test.Container as c join c.components as ce join ce.simple as s where ce.name='foo'" );
+ parse( "from s in class org.hibernate.test.Simple" );
+ parse( "from m in class org.hibernate.test.Many" );
+ parse( "from o in class org.hibernate.test.One" );
+ parse( "from c in class org.hibernate.test.Container" );
+ parse( "from o in class org.hibernate.test.Child" );
+ parse( "from o in class org.hibernate.test.MoreStuff" );
+ parse( "from o in class org.hibernate.test.Many" );
+ parse( "from o in class org.hibernate.test.Fee" );
+ parse( "from o in class org.hibernate.test.Qux" );
+ parse( "from o in class org.hibernate.test.Fumm" );
+ parse( "from o in class org.hibernate.test.Parent" );
+ parse( "from o in class org.hibernate.test.Simple" );
+ parse( "from o in class org.hibernate.test.Holder" );
+ parse( "from o in class org.hibernate.test.Part" );
+ parse( "from o in class org.hibernate.test.Baz" );
+ parse( "from o in class org.hibernate.test.Vetoer" );
+ parse( "from o in class org.hibernate.test.Sortable" );
+ parse( "from o in class org.hibernate.test.Contained" );
+ parse( "from o in class org.hibernate.test.Circular" );
+ parse( "from o in class org.hibernate.test.Stuff" );
+ parse( "from o in class org.hibernate.test.Immutable" );
+ parse( "from o in class org.hibernate.test.Container" );
+ parse( "from o in class org.hibernate.test.One" );
+ parse( "from o in class org.hibernate.test.Foo" );
+ parse( "from o in class org.hibernate.test.Fo" );
+ parse( "from o in class org.hibernate.test.Glarch" );
+ parse( "from o in class org.hibernate.test.Fum" );
+ parse( "from c in class org.hibernate.test.C2 where 1=1 or 1=1" );
+ parse( "from b in class org.hibernate.test.B" );
+ parse( "from a in class org.hibernate.test.A" );
+ parse( "from b in class org.hibernate.test.B" );
+ parse( "from org.hibernate.test.E e join e.reverse as b where b.count=1" );
+ parse( "from org.hibernate.test.E e join e.as as b where b.count=1" );
+ parse( "from org.hibernate.test.B" );
+ parse( "from org.hibernate.test.C1" );
+ parse( "from org.hibernate.test.C2" );
+ parse( "from org.hibernate.test.E e, org.hibernate.test.A a where e.reverse = a.forward and a = ?" );
+ parse( "from org.hibernate.test.E e join fetch e.reverse" );
+ parse( "from org.hibernate.test.E e" );
+ parse( "select max(s.count) from s in class org.hibernate.test.Simple" );
+ parse( "select new org.hibernate.test.S(s.count, s.address) from s in class org.hibernate.test.Simple" );
+ parse( "select max(s.count) from s in class org.hibernate.test.Simple" );
+ parse( "select count(*) from s in class org.hibernate.test.Simple" );
+ parse( "from s in class org.hibernate.test.Simple where s.name=:name and s.count=:count" );
+ parse( "from s in class org.hibernate.test.Simple where s.name in (:several0_, :several1_)" );
+ parse( "from s in class org.hibernate.test.Simple where s.name in (:stuff0_, :stuff1_)" );
+ parse( "from org.hibernate.test.Simple s where s.name=?" );
+ parse( "from org.hibernate.test.Simple s where s.name=:name" );
+ parse( "from s in class org.hibernate.test.Simple where upper( s.name ) ='SIMPLE 1'" );
+ parse( "from s in class org.hibernate.test.Simple where not( upper( s.name ) ='yada' or 1=2 or 'foo'='bar' or not('foo'='foo') or 'foo' like 'bar' )" );
+ parse( "from s in class org.hibernate.test.Simple where lower( s.name || ' foo' ) ='simple 1 foo'" );
+ parse( "from s in class org.hibernate.test.Simple where upper( s.other.name ) ='SIMPLE 2'" );
+ parse( "from s in class org.hibernate.test.Simple where not ( upper( s.other.name ) ='SIMPLE 2' )" );
+ parse( "select distinct s from s in class org.hibernate.test.Simple where ( ( s.other.count + 3 ) = (15*2)/2 and s.count = 69) or ( ( s.other.count + 2 ) / 7 ) = 2" );
+ parse( "select s from s in class org.hibernate.test.Simple where ( ( s.other.count + 3 ) = (15*2)/2 and s.count = 69) or ( ( s.other.count + 2 ) / 7 ) = 2 order by s.other.count" );
+ parse( "select sum(s.count) from s in class org.hibernate.test.Simple group by s.count having sum(s.count) > 10" );
+ parse( "select s.count from s in class org.hibernate.test.Simple group by s.count having s.count = 12" );
+ parse( "select s.id, s.count, count(t), max(t.date) from s in class org.hibernate.test.Simple, t in class org.hibernate.test.Simple where s.count = t.count group by s.id, s.count order by s.count" );
+ parse( "from s in class org.hibernate.test.Simple" );
+ parse( "from s in class org.hibernate.test.Simple where s.name = ?" );
+ parse( "from s in class org.hibernate.test.Simple where s.name = ? and upper(s.name) = ?" );
+ parse( "from s in class org.hibernate.test.Simple where s.name = :foo and upper(s.name) = :bar or s.count=:count or s.count=:count + 1" );
+ parse( "select s.id from s in class org.hibernate.test.Simple" );
+ parse( "select all s, s.other from s in class org.hibernate.test.Simple where s = :s" );
+ parse( "from s in class org.hibernate.test.Simple where s.name in (:name_list0_, :name_list1_) and s.count > :count" );
+ parse( "from org.hibernate.test.Simple s" );
+ parse( "from org.hibernate.test.Simple s" );
+ parse( "from org.hibernate.test.Assignable" );
+ parse( "from org.hibernate.test.Category" );
+ parse( "from org.hibernate.test.Simple" );
+ parse( "from org.hibernate.test.A" );
+ parse( "from foo in class org.hibernate.test.Foo where foo.string=?" );
+ parse( "from foo in class org.hibernate.test.Foo" );
+ parse( "from org.hibernate.test.Po po, org.hibernate.test.Lower low where low.mypo = po" );
+ parse( "from org.hibernate.test.Po po join po.set as sm where sm.amount > 0" );
+ parse( "from org.hibernate.test.Po po join po.top as low where low.foo = 'po'" );
+ parse( "from org.hibernate.test.SubMulti sm join sm.children smc where smc.name > 'a'" );
+ parse( "select s, ya from org.hibernate.test.Lower s join s.yetanother ya" );
+ parse( "from org.hibernate.test.Lower s1 join s1.bag s2" );
+ parse( "from org.hibernate.test.Lower s1 left join s1.bag s2" );
+ parse( "select s, a from org.hibernate.test.Lower s join s.another a" );
+ parse( "select s, a from org.hibernate.test.Lower s left join s.another a" );
+ parse( "from org.hibernate.test.Top s, org.hibernate.test.Lower ls" );
+ parse( "from org.hibernate.test.Lower ls join ls.set s where s.name > 'a'" );
+ parse( "from org.hibernate.test.Po po join po.list sm where sm.name > 'a'" );
+ parse( "from org.hibernate.test.Lower ls inner join ls.another s where s.name is not null" );
+ parse( "from org.hibernate.test.Lower ls where ls.other.another.name is not null" );
+ parse( "from org.hibernate.test.Multi m where m.derived like 'F%'" );
+ parse( "from org.hibernate.test.SubMulti m where m.derived like 'F%'" );
+ parse( "select s from org.hibernate.test.SubMulti as sm join sm.children as s where s.amount>-1 and s.name is null" );
+ parse( "select elements(sm.children) from org.hibernate.test.SubMulti as sm" );
+ parse( "select distinct sm from org.hibernate.test.SubMulti as sm join sm.children as s where s.amount>-1 and s.name is null" );
+ parse( "select distinct s from s in class org.hibernate.test.SubMulti where s.moreChildren[1].amount < 1.0" );
+ parse( "from s in class org.hibernate.test.TrivialClass where s.id = 2" );
+ parse( "select s.count from s in class org.hibernate.test.Top" );
+ parse( "from s in class org.hibernate.test.Lower where s.another.name='name'" );
+ parse( "from s in class org.hibernate.test.Lower where s.yetanother.name='name'" );
+ parse( "from s in class org.hibernate.test.Lower where s.yetanother.name='name' and s.yetanother.foo is null" );
+ parse( "from s in class org.hibernate.test.Top where s.count=1" );
+ parse( "select s.count from s in class org.hibernate.test.Top, ls in class org.hibernate.test.Lower where ls.another=s" );
+ parse( "select elements(ls.bag), elements(ls.set) from ls in class org.hibernate.test.Lower" );
+ parse( "from s in class org.hibernate.test.Lower" );
+ parse( "from s in class org.hibernate.test.Top" );
+ parse( "from sm in class org.hibernate.test.SubMulti" );
+ parse( "select\n" +
+ "\n" +
+ "s from s in class org.hibernate.test.Top where s.count>0" );
+ parse( "from m in class org.hibernate.test.Multi where m.count>0 and m.extraProp is not null" );
+ parse( "from m in class org.hibernate.test.Top where m.count>0 and m.name is not null" );
+ parse( "from m in class org.hibernate.test.Lower where m.other is not null" );
+ parse( "from m in class org.hibernate.test.Multi where m.other.id = 1" );
+ parse( "from m in class org.hibernate.test.SubMulti where m.amount > 0.0" );
+ parse( "from m in class org.hibernate.test.Multi" );
+ parse( "from m in class org.hibernate.test.Multi where m.class = SubMulti" );
+ parse( "from m in class org.hibernate.test.Top where m.class = Multi" );
+ parse( "from s in class org.hibernate.test.Top" );
+ parse( "from ls in class org.hibernate.test.Lower" );
+ parse( "from ls in class org.hibernate.test.Lower, s in elements(ls.bag) where s.id is not null" );
+ parse( "from ls in class org.hibernate.test.Lower, s in elements(ls.set) where s.id is not null" );
+ parse( "from o in class org.hibernate.test.Top" );
+ parse( "from o in class org.hibernate.test.Po" );
+ parse( "from ChildMap cm where cm.parent is not null" );
+ parse( "from ParentMap cm where cm.child is not null" );
+ parse( "from org.hibernate.test.Componentizable" );
+ }
+
+ public void testUnnamedParameter() throws Exception {
+ parse( "select foo, bar from org.hibernate.test.Foo foo left outer join foo.foo bar where foo = ?" ); // Added '?' as a valid expression.
+ }
+
+ public void testInElements() throws Exception {
+ parse( "from bar in class org.hibernate.test.Bar, foo in elements(bar.baz.fooArray)" ); // Added collectionExpr as a valid 'in' clause.
+ }
+
+ public void testDotElements() throws Exception {
+ parse( "select distinct foo from baz in class org.hibernate.test.Baz, foo in elements(baz.fooArray)" );
+ parse( "select foo from baz in class org.hibernate.test.Baz, foo in elements(baz.fooSet)" );
+ parse( "select foo from baz in class org.hibernate.test.Baz, foo in elements(baz.fooArray)" );
+ parse( "from org.hibernate.test.Baz baz where 'b' in elements(baz.collectionComponent.nested.foos) and 1.0 in elements(baz.collectionComponent.nested.floats)" );
+ }
+
+ public void testSelectAll() throws Exception {
+ parse( "select all s, s.other from s in class org.hibernate.test.Simple where s = :s" );
+ }
+
+ public void testNot() throws Exception {
+ // Cover NOT optimization in HqlParser
+ parse( "from eg.Cat cat where not ( cat.kittens.size < 1 )" );
+ parse( "from eg.Cat cat where not ( cat.kittens.size > 1 )" );
+ parse( "from eg.Cat cat where not ( cat.kittens.size >= 1 )" );
+ parse( "from eg.Cat cat where not ( cat.kittens.size <= 1 )" );
+ parse( "from eg.DomesticCat cat where not ( cat.name between 'A' and 'B' ) " );
+ parse( "from eg.DomesticCat cat where not ( cat.name not between 'A' and 'B' ) " );
+ parse( "from eg.Cat cat where not ( not cat.kittens.size <= 1 )" );
+ parse( "from eg.Cat cat where not not ( not cat.kittens.size <= 1 )" );
+ }
+
+ public void testOtherSyntax() throws Exception {
+ parse( "select bar from org.hibernate.test.Bar bar order by ((bar.x - :valueX)*(bar.x - :valueX))" );
+ parse( "from bar in class org.hibernate.test.Bar, foo in elements(bar.baz.fooSet)" );
+ parse( "from one in class org.hibernate.test.One, many in elements(one.manies) where one.id = 1 and many.id = 1" );
+ parse( "from org.hibernate.test.Inner _inner join _inner.middles middle" );
+ parse( "FROM m IN CLASS org.hibernate.test.Master WHERE NOT EXISTS ( FROM d IN elements(m.details) WHERE NOT d.i=5 )" );
+ parse( "FROM m IN CLASS org.hibernate.test.Master WHERE NOT 5 IN ( SELECT d.i FROM d IN elements(m.details) )" );
+ parse( "SELECT m FROM m IN CLASS org.hibernate.test.Master, d IN elements(m.details) WHERE d.i=5" );
+ parse( "SELECT m FROM m IN CLASS org.hibernate.test.Master, d IN elements(m.details) WHERE d.i=5" );
+ parse( "SELECT m.id FROM m IN CLASS org.hibernate.test.Master, d IN elements(m.details) WHERE d.i=5" );
+ // I'm not sure about these... [jsd]
+// parse("select bar.string, foo.string from bar in class org.hibernate.test.Bar inner join bar.baz as baz inner join elements(baz.fooSet) as foo where baz.name = 'name'");
+// parse("select bar.string, foo.string from bar in class org.hibernate.test.Bar, bar.baz as baz, elements(baz.fooSet) as foo where baz.name = 'name'");
+// parse("select count(*) where this.amount>-1 and this.name is null");
+// parse("from sm in class org.hibernate.test.SubMulti where exists sm.children.elements");
+ }
+
+ public void testEjbqlExtensions() throws Exception {
+ parse( "select object(a) from Animal a where a.mother member of a.offspring" );
+ parse( "select object(a) from Animal a where a.mother member a.offspring" ); //no member of
+ parse( "select object(a) from Animal a where a.offspring is empty" );
+ }
+
+ public void testEmptyFilter() throws Exception {
+ parseFilter( "" ); // Blank is a legitimate filter.
+ }
+
+ public void testOrderByFilter() throws Exception {
+ parseFilter( "order by this.id" );
+ }
+
+ public void testRestrictionFilter() throws Exception {
+ parseFilter( "where this.name = ?" );
+ }
+
+ public void testNoFrom() throws Exception {
+ System.out.println( "***** This test ensures that an error is detected ERROR MESSAGES ARE OKAY! *****" );
+ HqlParser parser = HqlParser.getInstance( "" );
+ parser.setFilter( false );
+ parser.statement();
+ assertEquals( "Parser allowed no FROM clause!", 1, parser.getParseErrorHandler().getErrorCount() );
+ System.out.println( "***** END OF ERROR TEST *****" );
+ }
+
+ public void testHB1042() throws Exception {
+ parse( "select x from fmc_web.pool.Pool x left join x.containers c0 where (upper(x.name) = upper(':') and c0.id = 1)" );
+ }
+
+ public void testKeywordInPath() throws Exception {
+ // The keyword 'order' used as a property name.
+ parse( "from Customer c where c.order.status = 'argh'" );
+ // The keyword 'order' and 'count' used as a property name.
+ parse( "from Customer c where c.order.count > 3" );
+ // The keywords 'where', 'order' and 'count' used as a property name.
+ parse( "select c.where from Customer c where c.order.count > 3" );
+ parse( "from Interval i where i.end <:end" );
+ parse( "from Letter l where l.case = :case" );
+ }
+
+ public void testPathologicalKeywordAsIdentifier() throws Exception {
+ // Super evil badness... a legitimate keyword!
+ parse( "from Order order" );
+ //parse( "from Order order join order.group" );
+ parse( "from X x order by x.group.by.from" );
+ parse( "from Order x order by x.order.group.by.from" );
+ parse( "select order.id from Order order" );
+ parse( "select order from Order order" );
+ parse( "from Order order where order.group.by.from is not null" );
+ parse( "from Order order order by order.group.by.from" );
+ // Okay, now this is getting silly.
+ parse( "from Group as group group by group.by.from" );
+ }
+
+ public void testHHH354() throws Exception {
+ parse( "from Foo f where f.full = 'yep'");
+ }
+
+ public void testWhereAsIdentifier() throws Exception {
+ // 'where' as a package name
+ parse( "from where.Order" );
+ }
+
+ public void testEjbqlKeywordsAsIdentifier() throws Exception {
+ parse( "from org.hibernate.test.Bar bar where bar.object.id = ? and bar.object.class = ?" );
+ }
+
+ public void testConstructorIn() throws Exception {
+ parse( "from org.hibernate.test.Bar bar where (b.x, b.y, b.z) in (select foo, bar, baz from org.hibernate.test.Foo)" );
+ }
+
+ public void testMultiByteCharacters() throws Exception {
+ parse ("from User user where user.name like '%nn\u4e2dnn%'");
+ // Test for HHH-558
+ parse ("from User user where user.\u432d like '%\u4e2d%'");
+ parse ("from \u432d \u432d where \u432d.name like '%fred%'");
+ }
+
+ public void testHHH719() throws Exception {
+ // Some SQLs have function names with package qualifiers.
+ parse("from Foo f order by com.fooco.SpecialFunction(f.id)");
+ }
+
+ public void testHHH1107() throws Exception {
+ parse("from Animal where zoo.address.street = '123 Bogus St.'");
+ }
+
+
+ public void testHHH1247() throws Exception {
+ parse("select distinct user.party from com.itf.iceclaims.domain.party.user.UserImpl user inner join user.party.$RelatedWorkgroups relatedWorkgroups where relatedWorkgroups.workgroup.id = :workgroup and relatedWorkgroups.effectiveTime.start <= :datesnow and relatedWorkgroups.effectiveTime.end > :dateenow ");
+ }
+ public void testLineAndColumnNumber() throws Exception {
+ AST ast = doParse("from Foo f\nwhere f.name = 'fred'",false);
+ // Find some of the nodes and check line and column values.
+ ASTIterator iter = new ASTIterator(ast);
+ boolean foundFoo = false;
+ boolean foundName = false;
+ while (iter.hasNext())
+ {
+ AST n = iter.nextNode();
+ if ("Foo".equals(n.getText()))
+ {
+ if (foundFoo)
+ fail("Already found 'Foo'!");
+ foundFoo = true;
+ Node node = (Node)n;
+ assertEquals(1,node.getLine());
+ assertEquals(6,node.getColumn());
+ }
+ else if ("name".equals(n.getText()))
+ {
+ if (foundName)
+ fail("Already found 'name'!");
+ foundName = true;
+ Node node = (Node)n;
+ assertEquals(2,node.getLine());
+ assertEquals(9,node.getColumn());
+ }
+ }
+ assertTrue(foundFoo);
+ assertTrue(foundName);
+ }
+
+ private void parseFilter(String input) throws TokenStreamException, RecognitionException {
+ doParse( input, true );
+ }
+
+ private void parse(String input) throws RecognitionException, TokenStreamException {
+ doParse( input, false );
+ }
+
+ private AST doParse(String input, boolean filter) throws RecognitionException, TokenStreamException {
+ System.out.println( "input: ->" + ASTPrinter.escapeMultibyteChars(input) + "<-" );
+ HqlParser parser = HqlParser.getInstance( input );
+ parser.setFilter( filter );
+ parser.statement();
+ AST ast = parser.getAST();
+ System.out.println( "AST : " + ast.toStringTree() + "" );
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ parser.showAst( ast, new PrintStream( baos ) );
+ System.out.println( baos.toString() );
+ assertEquals( "At least one error occurred during parsing!", 0, parser.getParseErrorHandler().getErrorCount() );
+ return ast;
+ }
+
+ public static Test suite() {
+ return new TestSuite( HqlParserTest.class );
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Human.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Human.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Human.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,126 @@
+//$Id: Human.java 9873 2006-05-04 13:42:48Z max.andersen at jboss.com $
+package org.hibernate.test.hql;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author Gavin King
+ */
+public class Human extends Mammal {
+ private Name name;
+ private String nickName;
+ private Collection friends;
+ private Collection pets;
+ private Map family;
+ private double height;
+
+ private BigInteger bigIntegerValue;
+ private BigDecimal bigDecimalValue;
+ private int intValue;
+ private float floatValue;
+
+ private Set nickNames;
+ private Map addresses;
+
+ public Collection getFriends() {
+ return friends;
+ }
+
+ public void setFriends(Collection friends) {
+ this.friends = friends;
+ }
+
+ public Collection getPets() {
+ return pets;
+ }
+
+ public void setPets(Collection pets) {
+ this.pets = pets;
+ }
+
+ public Name getName() {
+ return name;
+ }
+
+ public void setName(Name name) {
+ this.name = name;
+ }
+
+ public String getNickName() {
+ return nickName;
+ }
+
+ public void setNickName(String nickName) {
+ this.nickName = nickName;
+ }
+
+ public double getHeight() {
+ return height;
+ }
+ public void setHeight(double height) {
+ this.height = height;
+ }
+
+ public Map getFamily() {
+ return family;
+ }
+
+
+ public void setFamily(Map family) {
+ this.family = family;
+ }
+
+ public Set getNickNames() {
+ return nickNames;
+ }
+
+ public void setNickNames(Set nickNames) {
+ this.nickNames = nickNames;
+ }
+
+ public Map getAddresses() {
+ return addresses;
+ }
+
+ public void setAddresses(Map addresses) {
+ this.addresses = addresses;
+ }
+
+ public BigDecimal getBigDecimalValue() {
+ return bigDecimalValue;
+ }
+
+ public void setBigDecimalValue(BigDecimal bigDecimalValue) {
+ this.bigDecimalValue = bigDecimalValue;
+ }
+
+ public BigInteger getBigIntegerValue() {
+ return bigIntegerValue;
+ }
+
+ public void setBigIntegerValue(BigInteger bigIntegerValue) {
+ this.bigIntegerValue = bigIntegerValue;
+ }
+
+ public float getFloatValue() {
+ return floatValue;
+ }
+
+ public void setFloatValue(float floatValue) {
+ this.floatValue = floatValue;
+ }
+
+ public int getIntValue() {
+ return intValue;
+ }
+
+ public void setIntValue(int intValue) {
+ this.intValue = intValue;
+ }
+
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Human.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/IntegerVersioned.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/IntegerVersioned.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/IntegerVersioned.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,36 @@
+// $Id: IntegerVersioned.java 7384 2005-07-06 17:04:45Z steveebersole $
+package org.hibernate.test.hql;
+
+/**
+ * Implementation of IntegerVersioned.
+ *
+ * @author Steve Ebersole
+ */
+public class IntegerVersioned {
+ private Long id;
+ private int version = -1;
+ private String name;
+
+ public IntegerVersioned() {
+ }
+
+ public IntegerVersioned(String name) {
+ this.name = name;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public int getVersion() {
+ return version;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Joiner.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Joiner.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Joiner.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,37 @@
+// $Id: Joiner.java 6021 2005-03-06 02:02:30Z steveebersole $
+package org.hibernate.test.hql;
+
+/**
+ * Implementation of Joiner.
+ *
+ * @author Steve Ebersole
+ */
+public class Joiner {
+ private Long id;
+ private String name;
+ private String joinedName;
+
+ 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 String getJoinedName() {
+ return joinedName;
+ }
+
+ public void setJoinedName(String joinedName) {
+ this.joinedName = joinedName;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/KeyManyToOneEntity.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/KeyManyToOneEntity.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/KeyManyToOneEntity.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.hql">
+
+ <class name="KeyManyToOneKeyEntity">
+ <id name="id" type="long" access="field" >
+ <generator class="native"/>
+ </id>
+ <property name="name" type="string"/>
+ </class>
+
+ <class name="KeyManyToOneEntity">
+ <composite-id name="id" access="field" class="KeyManyToOneEntity$Id" unsaved-value="undefined" >
+ <key-many-to-one name="key1" class="KeyManyToOneKeyEntity" />
+ <key-property name="key2" type="string"/>
+ </composite-id>
+ <property name="name" type="string"/>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/KeyManyToOneEntity.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/KeyManyToOneEntity.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/KeyManyToOneEntity.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,52 @@
+// $Id: KeyManyToOneEntity.java 7091 2005-06-08 19:31:26Z steveebersole $
+package org.hibernate.test.hql;
+
+import java.io.Serializable;
+
+/**
+ * Implementation of KeyManyToOneEntity.
+ *
+ * @author Steve Ebersole
+ */
+public class KeyManyToOneEntity {
+ private Id id;
+ private String name;
+
+ public KeyManyToOneEntity(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public static class Id implements Serializable {
+ private KeyManyToOneKeyEntity key1;
+ private String key2;
+
+ public Id(KeyManyToOneKeyEntity key1, String key2) {
+ this.key1 = key1;
+ this.key2 = key2;
+ }
+
+ public KeyManyToOneKeyEntity getKey1() {
+ return key1;
+ }
+
+ public void setKey1(KeyManyToOneKeyEntity key1) {
+ this.key1 = key1;
+ }
+
+ public String getKey2() {
+ return key2;
+ }
+
+ public void setKey2(String key2) {
+ this.key2 = key2;
+ }
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/KeyManyToOneKeyEntity.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/KeyManyToOneKeyEntity.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/KeyManyToOneKeyEntity.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,24 @@
+// $Id: KeyManyToOneKeyEntity.java 7091 2005-06-08 19:31:26Z steveebersole $
+package org.hibernate.test.hql;
+
+/**
+ * Implementation of KeyManyToOneKeyEntity.
+ *
+ * @author Steve Ebersole
+ */
+public class KeyManyToOneKeyEntity {
+ private Long id;
+ private String name;
+
+ public KeyManyToOneKeyEntity(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Lizard.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Lizard.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Lizard.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,9 @@
+//$Id: Lizard.java 3890 2004-06-03 16:31:32Z steveebersole $
+package org.hibernate.test.hql;
+
+/**
+ * @author Gavin King
+ */
+public class Lizard extends Reptile {
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Mammal.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Mammal.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Mammal.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,30 @@
+//$Id: Mammal.java 6005 2005-03-04 11:41:11Z oneovthafew $
+package org.hibernate.test.hql;
+
+import java.util.Date;
+
+/**
+ * @author Gavin King
+ */
+public class Mammal extends Animal {
+ private boolean pregnant;
+ private Date birthdate;
+
+ public boolean isPregnant() {
+ return pregnant;
+ }
+
+ public void setPregnant(boolean pregnant) {
+ this.pregnant = pregnant;
+ }
+
+ public Date getBirthdate() {
+ return birthdate;
+ }
+
+
+ public void setBirthdate(Date birthdate) {
+ this.birthdate = birthdate;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/MoreCrazyIdFieldNameStuffEntity.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/MoreCrazyIdFieldNameStuffEntity.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/MoreCrazyIdFieldNameStuffEntity.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,44 @@
+// $Id: MoreCrazyIdFieldNameStuffEntity.java 7471 2005-07-14 14:58:28Z steveebersole $
+package org.hibernate.test.hql;
+
+/**
+ * Implementation of MoreCrazyIdFieldNameStuffEntity.
+ *
+ * @author Steve Ebersole
+ */
+public class MoreCrazyIdFieldNameStuffEntity {
+ private Long moreCrazyIdFieldNameStuffEntity;
+ private HeresAnotherCrazyIdFieldName heresAnotherCrazyIdFieldName; // silly ain't it ;)
+ private String name;
+
+ public MoreCrazyIdFieldNameStuffEntity() {
+ }
+
+ public MoreCrazyIdFieldNameStuffEntity(String name) {
+ this.name = name;
+ }
+
+ public Long getMoreCrazyIdFieldNameStuffEntity() {
+ return moreCrazyIdFieldNameStuffEntity;
+ }
+
+ public void setMoreCrazyIdFieldNameStuffEntity(Long moreCrazyIdFieldNameStuffEntity) {
+ this.moreCrazyIdFieldNameStuffEntity = moreCrazyIdFieldNameStuffEntity;
+ }
+
+ public HeresAnotherCrazyIdFieldName getHeresAnotherCrazyIdFieldName() {
+ return heresAnotherCrazyIdFieldName;
+ }
+
+ public void setHeresAnotherCrazyIdFieldName(HeresAnotherCrazyIdFieldName heresAnotherCrazyIdFieldName) {
+ this.heresAnotherCrazyIdFieldName = heresAnotherCrazyIdFieldName;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Name.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Name.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Name.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,43 @@
+//$Id: Name.java 5686 2005-02-12 07:27:32Z steveebersole $
+package org.hibernate.test.hql;
+
+/**
+ * @author Gavin King
+ */
+public class Name {
+ private String first;
+ private char initial;
+ private String last;
+
+ protected Name() {}
+
+ public Name(String first, char initial, String last) {
+ this.first = first;
+ this.initial = initial;
+ this.last = last;
+ }
+
+ public String getFirst() {
+ return first;
+ }
+
+ public void setFirst(String first) {
+ this.first = first;
+ }
+
+ public char getInitial() {
+ return initial;
+ }
+
+ public void setInitial(char initial) {
+ this.initial = initial;
+ }
+
+ public String getLast() {
+ return last;
+ }
+
+ public void setLast(String last) {
+ this.last = last;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Name.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/PettingZoo.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/PettingZoo.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/PettingZoo.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,9 @@
+//$Id: PettingZoo.java 4857 2004-12-02 16:28:57Z oneovthafew $
+package org.hibernate.test.hql;
+
+/**
+ * @author Gavin King
+ */
+public class PettingZoo extends Zoo {
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/PettingZoo.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Pickup.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Pickup.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Pickup.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,10 @@
+// $Id: Pickup.java 7087 2005-06-08 18:23:44Z steveebersole $
+package org.hibernate.test.hql;
+
+/**
+ * Implementation of Pickup.
+ *
+ * @author Steve Ebersole
+ */
+public class Pickup extends Truck {
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/QueryTranslatorTestCase.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/QueryTranslatorTestCase.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/QueryTranslatorTestCase.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,546 @@
+// $Id: QueryTranslatorTestCase.java 11361 2007-03-29 12:48:35Z steve.ebersole at jboss.com $
+package org.hibernate.test.hql;
+
+import java.sql.Connection;
+import java.sql.ParameterMetaData;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.TreeMap;
+
+import junit.framework.ComparisonFailure;
+
+import org.hibernate.EntityMode;
+import org.hibernate.MappingException;
+import org.hibernate.QueryException;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.classic.Session;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.query.HQLQueryPlan;
+import org.hibernate.hql.QueryTranslator;
+import org.hibernate.hql.QueryTranslatorFactory;
+import org.hibernate.hql.ast.ASTQueryTranslatorFactory;
+import org.hibernate.hql.ast.HqlToken;
+import org.hibernate.hql.ast.QueryTranslatorImpl;
+import org.hibernate.hql.ast.util.ASTPrinter;
+import org.hibernate.hql.classic.ClassicQueryTranslatorFactory;
+import org.hibernate.type.Type;
+import org.hibernate.type.TypeFactory;
+import org.hibernate.util.StringHelper;
+
+/**
+ * Test case superclass for testing QueryTranslator implementations.
+ *
+ * @author josh Dec 6, 2004 8:21:21 AM
+ */
+public abstract class QueryTranslatorTestCase extends FunctionalTestCase {
+
+ public QueryTranslatorTestCase(String x) {
+ super( x );
+ // Create an instance of HqlToken, so that it will have an entry point outside the package. This
+ // will stop IDEA's code inspector from suggesting that HqlToken should be package local.
+ new HqlToken();
+ }
+
+ public String[] getMappings() {
+ return new String[] {
+ "hql/Animal.hbm.xml",
+ "hql/EntityWithCrazyCompositeKey.hbm.xml",
+ "hql/CrazyIdFieldNames.hbm.xml",
+ "hql/SimpleEntityWithAssociation.hbm.xml",
+ "hql/ComponentContainer.hbm.xml",
+ "batchfetch/ProductLine.hbm.xml",
+ "cid/Customer.hbm.xml",
+ "cid/Order.hbm.xml",
+ "cid/LineItem.hbm.xml",
+ "cid/Product.hbm.xml",
+ "legacy/Baz.hbm.xml",
+ "legacy/Category.hbm.xml",
+ "legacy/Commento.hbm.xml",
+ "legacy/Container.hbm.xml",
+ "legacy/Custom.hbm.xml",
+ "legacy/Eye.hbm.xml",
+ "legacy/Fee.hbm.xml",
+ "legacy/FooBar.hbm.xml",
+ "legacy/Fum.hbm.xml",
+ "legacy/Glarch.hbm.xml",
+ "legacy/Holder.hbm.xml",
+ "legacy/Many.hbm.xml",
+ "legacy/Marelo.hbm.xml",
+ "legacy/MasterDetail.hbm.xml",
+ "legacy/Middle.hbm.xml",
+ "legacy/Multi.hbm.xml",
+ "legacy/Nameable.hbm.xml",
+ "legacy/One.hbm.xml",
+ "legacy/Qux.hbm.xml",
+ "legacy/Simple.hbm.xml",
+ "legacy/SingleSeveral.hbm.xml",
+ "legacy/WZ.hbm.xml",
+ "legacy/UpDown.hbm.xml",
+ "compositeelement/Parent.hbm.xml",
+ "onetoone/joined/Person.hbm.xml",
+ "any/Properties.hbm.xml"
+ };
+ }
+
+ public boolean createSchema() {
+ return false;
+ }
+
+ public boolean recreateSchemaAfterFailure() {
+ return false;
+ }
+
+ public void assertTranslation(String hql) throws QueryException, MappingException {
+ assertTranslation( hql, null );
+ }
+
+ public void assertTranslation(String hql, boolean scalar) throws QueryException, MappingException {
+ assertTranslation( hql, null, scalar, null );
+ }
+
+ protected void assertTranslation(String hql, Map replacements) {
+ ComparisonFailure cf = null;
+ try {
+ assertTranslation( hql, replacements, false, null );
+ }
+ catch ( ComparisonFailure e ) {
+ e.printStackTrace();
+ cf = e;
+ }
+ if ("false".equals(System.getProperty("org.hibernate.test.hql.SkipScalarQuery","false"))) {
+ // Run the scalar translation anyway, even if there was a comparison failure.
+ assertTranslation( hql, replacements, true, null );
+ }
+ if (cf != null)
+ throw cf;
+ }
+
+ protected void runClassicTranslator(String hql) throws Exception {
+ SessionFactoryImplementor factory = getSessionFactoryImplementor();
+ Map replacements = new HashMap();
+ QueryTranslator oldQueryTranslator = null;
+ try {
+ QueryTranslatorFactory classic = new ClassicQueryTranslatorFactory();
+ oldQueryTranslator = classic.createQueryTranslator( hql, hql, Collections.EMPTY_MAP, factory );
+ oldQueryTranslator.compile( replacements, false );
+ }
+ catch ( Exception e ) {
+ e.printStackTrace();
+ throw e;
+ }
+ String oldsql = oldQueryTranslator.getSQLString();
+ System.out.println( "HQL : " + hql );
+ System.out.println( "OLD SQL: " + oldsql );
+ }
+
+ protected void assertTranslation(String hql, Map replacements, boolean scalar, String sql) {
+ SessionFactoryImplementor factory = getSessionFactoryImplementor();
+
+ // Create an empty replacements map if we don't have one.
+ if ( replacements == null ) {
+ replacements = new HashMap();
+ }
+
+ // steve -> note that the empty maps here represent the currently enabled filters...
+ QueryTranslator oldQueryTranslator = null;
+ Exception oldException = null;
+ try {
+ System.out.println("Compiling with classic QueryTranslator...");
+ QueryTranslatorFactory classic = new ClassicQueryTranslatorFactory();
+ oldQueryTranslator = classic.createQueryTranslator( hql, hql, Collections.EMPTY_MAP, factory );
+ oldQueryTranslator.compile( replacements, scalar );
+ }
+ catch ( QueryException e ) {
+ oldException = e;
+ }
+ catch ( MappingException e ) {
+ oldException = e;
+ }
+
+ QueryTranslator newQueryTranslator = null;
+ Exception newException = null;
+ try {
+ System.out.println("Compiling with AST QueryTranslator...");
+ newQueryTranslator = createNewQueryTranslator( hql, replacements, scalar );
+ }
+ catch ( QueryException e ) {
+ newException = e;
+ }
+ catch ( MappingException e ) {
+ newException = e;
+ }
+
+ // If the old QT threw an exception, the new one should too.
+ if ( oldException != null ) {
+// oldException.printStackTrace();
+ assertNotNull( "New query translator did *NOT* throw an exception, the old one did : " + oldException, newException );
+ assertEquals( oldException.getMessage(), newException.getMessage() );
+ return; // Don't bother with the rest of the assertions.
+ }
+ else if ( newException != null ) {
+ newException.printStackTrace();
+ assertNull( "Old query translator did not throw an exception, the new one did", newException );
+ }
+
+ // -- check all of the outputs --
+ checkSql( oldQueryTranslator, newQueryTranslator, hql, scalar, sql );
+ checkQuerySpaces( oldQueryTranslator, newQueryTranslator );
+ checkReturnedTypes( oldQueryTranslator, newQueryTranslator );
+ checkColumnNames( oldQueryTranslator, newQueryTranslator );
+
+ }
+
+ protected QueryTranslatorImpl createNewQueryTranslator(String hql, Map replacements, boolean scalar) {
+ SessionFactoryImplementor factory = getSessionFactoryImplementor();
+ return createNewQueryTranslator( hql, replacements, scalar, factory );
+ }
+
+ private QueryTranslatorImpl createNewQueryTranslator(String hql, Map replacements, boolean scalar, SessionFactoryImplementor factory) {
+ QueryTranslatorFactory ast = new ASTQueryTranslatorFactory();
+ QueryTranslatorImpl newQueryTranslator = ( QueryTranslatorImpl ) ast.createQueryTranslator( hql, hql, Collections.EMPTY_MAP, factory );
+ newQueryTranslator.compile( replacements, scalar );
+ return newQueryTranslator;
+ }
+
+ protected QueryTranslatorImpl createNewQueryTranslator(String hql) {
+ return createNewQueryTranslator( hql, new HashMap(), false );
+ }
+
+ protected QueryTranslatorImpl createNewQueryTranslator(String hql, SessionFactoryImplementor sfimpl) {
+ return createNewQueryTranslator( hql, new HashMap(), false, sfimpl );
+ }
+
+ protected HQLQueryPlan createQueryPlan(String hql, boolean scalar) {
+ return new HQLQueryPlan( hql, scalar, Collections.EMPTY_MAP, getSessionFactoryImplementor() );
+ }
+
+ protected HQLQueryPlan createQueryPlan(String hql) {
+ return createQueryPlan( hql, false );
+ }
+
+ protected SessionFactoryImplementor getSessionFactoryImplementor() {
+ SessionFactoryImplementor factory = ( SessionFactoryImplementor ) getSessions();
+ if ( factory == null ) {
+ throw new NullPointerException( "Unable to create factory!" );
+ }
+ return factory;
+ }
+
+ private void checkColumnNames(QueryTranslator oldQueryTranslator, QueryTranslator newQueryTranslator) {
+ // Check the column names.
+
+ String[][] oldColumnNames = oldQueryTranslator.getColumnNames();
+ String[][] newColumnNames = newQueryTranslator.getColumnNames();
+ /*assertEquals( "Column name array is not the right length!", oldColumnNames.length, newColumnNames.length );
+ for ( int i = 0; i < oldColumnNames.length; i++ ) {
+ assertEquals( "Column name array [" + i + "] is not the right length!", oldColumnNames[i].length, newColumnNames[i].length );
+ for ( int j = 0; j < oldColumnNames[i].length; j++ ) {
+ assertEquals( "Column name [" + i + "," + j + "]", oldColumnNames[i][j], newColumnNames[i][j] );
+ }
+ }*/
+ }
+
+ private void checkReturnedTypes(QueryTranslator oldQueryTranslator, QueryTranslator newQueryTranslator) {
+ // Check the returned types for a regression.
+ Type[] oldReturnTypes = oldQueryTranslator.getReturnTypes();
+ Type[] returnTypes = newQueryTranslator.getReturnTypes();
+ assertEquals( "Return types array is not the right length!", oldReturnTypes.length, returnTypes.length );
+ for ( int i = 0; i < returnTypes.length; i++ ) {
+ assertNotNull( returnTypes[i] );
+ assertNotNull( oldReturnTypes[i] );
+ assertEquals( "Returned types did not match!", oldReturnTypes[i].getReturnedClass(), returnTypes[i].getReturnedClass() );
+ System.out.println("returnedType[" + i + "] = " + returnTypes[i] + " oldReturnTypes[" + i + "] = " + oldReturnTypes[i]);
+ }
+ }
+
+ private void checkQuerySpaces(QueryTranslator oldQueryTranslator, QueryTranslator newQueryTranslator) {
+ // Check the query spaces for a regression.
+ Set oldQuerySpaces = oldQueryTranslator.getQuerySpaces();
+ Set querySpaces = newQueryTranslator.getQuerySpaces();
+ assertEquals( "Query spaces is not the right size!", oldQuerySpaces.size(), querySpaces.size() );
+ for ( Iterator iterator = oldQuerySpaces.iterator(); iterator.hasNext(); ) {
+ Object o = iterator.next();
+ assertTrue( "New query space does not contain " + o + "!", querySpaces.contains( o ) );
+ }
+ }
+
+ protected Exception compileBadHql(String hql, boolean scalar) {
+ QueryTranslator newQueryTranslator;
+ Map replacements = null;
+ Exception newException = null;
+ SessionFactoryImplementor factory = getSessionFactoryImplementor();
+ try {
+ QueryTranslatorFactory ast = new ASTQueryTranslatorFactory();
+ newQueryTranslator = ast.createQueryTranslator( hql, hql, Collections.EMPTY_MAP, factory );
+ newQueryTranslator.compile( replacements, scalar );
+ }
+ catch ( QueryException e ) {
+ newException = e;
+ }
+ catch ( MappingException e ) {
+ newException = e;
+ }
+ assertNotNull( "Expected exception from compilation of '" + hql + "'!", newException );
+ return newException;
+ }
+
+ private void checkSql(QueryTranslator oldQueryTranslator, QueryTranslator newQueryTranslator, String hql, boolean scalar, String sql) {
+
+ String oldsql = oldQueryTranslator.getSQLString();
+ String newsql = newQueryTranslator.getSQLString();
+ System.out.println( "HQL : " + ASTPrinter.escapeMultibyteChars(hql) );
+ System.out.println( "OLD SQL: " + ASTPrinter.escapeMultibyteChars(oldsql) );
+ System.out.println( "NEW SQL: " + ASTPrinter.escapeMultibyteChars(newsql) );
+ if ( sql == null ) {
+ // Check the generated SQL. ASTPrinter.escapeMultibyteChars(
+ assertSQLEquals( "SQL is not the same as the old SQL (scalar=" + scalar + ")", oldsql, newsql );
+ }
+ else {
+ assertSQLEquals( "SQL is not the same as the expected SQL (scalar=" + scalar + ")", sql, newsql );
+ }
+ }
+
+ private void assertSQLEquals(String message, String oldsql, String newsql) {
+ Map oldMap = getTokens(oldsql);
+ Map newMap = getTokens(newsql);
+ if ( !oldMap.equals(newMap) ) {
+ assertEquals(message, oldsql, newsql);
+ }
+
+ //String oldsqlStripped = stripExtraSpaces( oldsql );
+ //String newsqlStripped = stripExtraSpaces( newsql );
+ //assertEquals( message, oldsqlStripped, newsqlStripped );
+ }
+
+
+ private Map getTokens(String sql) {
+ Map result = new TreeMap();
+ if (sql==null) return result;
+ result.put( "=", new Integer( StringHelper.countUnquoted(sql, '=') ) );
+ StringTokenizer tokenizer = new StringTokenizer( sql, "(),= " );
+ while ( tokenizer.hasMoreTokens() ) {
+ String fragment = tokenizer.nextToken();
+ /*if ( "on".equals(fragment) ) fragment = "and";
+ if ( "join".equals(fragment) || "inner".equals(fragment) ) continue;*/
+ Integer count = (Integer) result.get(fragment);
+ if ( count==null ) {
+ count = new Integer(1);
+ }
+ else {
+ count = new Integer( count.intValue() + 1 );
+ }
+ result.put(fragment, count);
+ }
+ return result;
+ }
+
+ private String stripExtraSpaces(String string) {
+ if ( string == null ) {
+ return null;
+ }
+
+ StringBuffer buffer = new StringBuffer( string.length() );
+ char[] chars = string.toCharArray();
+ int length = chars.length;
+ boolean wasSpace = false;
+ for ( int i = 0; i < length; i++ ) {
+ boolean isSpace = chars[i] == ' ';
+ if ( wasSpace && isSpace ) {
+ continue;
+ }
+ else {
+ buffer.append( chars[i] );
+ }
+
+ wasSpace = isSpace;
+ }
+// StringTokenizer tokenizer = new StringTokenizer( string.trim(), " " );
+// while ( tokenizer.hasMoreTokens() ) {
+// final String fragment = tokenizer.nextToken();
+// buffer.append( fragment );
+// buffer.append( " " );
+// }
+//
+ return buffer.toString();
+ }
+
+ private void checkSqlByResultSet(
+ QueryTranslator oldQueryTranslator,
+ QueryTranslator newQueryTranslator,
+ Object[] binds
+ ) {
+ String oldsql = oldQueryTranslator.getSQLString();
+ String newsql = newQueryTranslator.getSQLString();
+
+ Session session = openSession();
+ Connection connection = session.connection();
+
+ PreparedStatement oldps = null;
+ PreparedStatement newps = null;
+ ResultSet oldrs = null;
+ ResultSet newrs = null;
+
+ try {
+ try {
+ oldps = connection.prepareStatement( oldsql );
+ }
+ catch( Throwable t ) {
+ fail( "Unable to prepare sql generated by old parser : " + t );
+ }
+ try {
+ newps = connection.prepareStatement( newsql );
+ }
+ catch( Throwable t ) {
+ fail( "Unable to prepare sql generated by new parser : " + t );
+ }
+
+ checkBinds(oldps, newps, binds);
+
+ try {
+ oldrs = executeQuery( oldps, binds );
+ }
+ catch( Throwable t ) {
+ fail( "Unable to execute sql generated by old parser : " + t );
+ }
+
+ try {
+ newrs = executeQuery( newps, binds );
+ }
+ catch( Throwable t ) {
+ fail( "Unable to execute sql generated by new parser : " + t );
+ }
+
+ checkResults( oldrs, newrs );
+ }
+ finally {
+ // make *sure* the sql resources get cleaned up
+ release(oldrs);
+ release(newrs);
+ release(oldps);
+ release(newps);
+ release(session);
+ }
+ }
+
+ private void checkBinds(PreparedStatement oldps, PreparedStatement newps, Object[] binds) {
+ // Make sure the binds "feel" ok
+ try {
+ ParameterMetaData oldBindMetaData = oldps.getParameterMetaData();
+ ParameterMetaData newBindMetaData = newps.getParameterMetaData();
+
+ assertEquals( "Different bind parameter count", oldBindMetaData.getParameterCount(), newBindMetaData.getParameterCount() );
+ assertEquals( "Incorrect number of binds passed in", oldBindMetaData.getParameterCount(), binds == null ? 0 : binds.length );
+
+ for ( int i = 0, max = oldBindMetaData.getParameterCount(); i < max; i++ ) {
+ assertEquals( "Different bind types", oldBindMetaData.getParameterType(i), newBindMetaData.getParameterType(i) );
+ }
+ }
+ catch( Throwable t ) {
+ fail( "SQLException comparing binds : " + t );
+ }
+ }
+
+ private ResultSet executeQuery(PreparedStatement ps, Object[] binds) throws SQLException {
+ if ( binds != null ) {
+ for ( int i = 0, max = binds.length; i < max; i++ ) {
+ ps.setObject( i, binds[i] );
+ }
+ }
+
+ return ps.executeQuery();
+ }
+
+ private void checkResults(ResultSet oldrs, ResultSet newrs) {
+ ResultSetMetaData oldmeta = null;
+ ResultSetMetaData newmeta = null;
+ int colCount = 0;
+ Type[] types = null;
+
+ // first compare the metadata from the two results
+ try {
+ oldmeta = oldrs.getMetaData();
+ newmeta = newrs.getMetaData();
+ assertEquals( "Different column counts", oldmeta.getColumnCount(), newmeta.getColumnCount() );
+
+ colCount = oldmeta.getColumnCount();
+ types = new Type[colCount];
+
+ for ( int i = 1, max = colCount; i < max; i++ ) {
+ assertEquals( "Column names were different", oldmeta.getColumnName(i), newmeta.getColumnName(i) );
+ assertEquals( "Column types were different", oldmeta.getColumnType(i), newmeta.getColumnType(i) );
+ assertEquals( "Java types were different", oldmeta.getColumnClassName(i), newmeta.getColumnClassName(i) );
+ types[i] = TypeFactory.basic( oldmeta.getColumnClassName(i) );
+ }
+ }
+ catch( Throwable t ) {
+ fail( "Error comparing result set metadata" );
+ }
+
+ // Then compare the actual results
+ try {
+ while ( oldrs.next() & newrs.next() ) {
+ for ( int i = 1; i < colCount; i++ ) {
+ Object oldval = oldrs.getObject(i);
+ if ( oldrs.wasNull() ) oldval = null;
+ Object newval = newrs.getObject(i);
+ if ( newrs.wasNull() ) newval = null;
+ checkLogicalEquality( oldval, newval, types[i] );
+ }
+ }
+
+ // for "better reporting" purposes, make sure both result sets are fully exhausted
+ while ( oldrs.next() );
+ while ( newrs.next() );
+
+ assertEquals( "Different row counts", oldrs.getRow(), newrs.getRow() );
+ }
+ catch( Throwable t ) {
+ fail( "Error comparing result set structure" );
+ }
+ }
+
+ private void checkLogicalEquality(Object oldval, Object newval, Type type) {
+ if ( oldval == null && newval == null ) {
+ // two nulls are logically equivalent here...
+ return;
+ }
+ else {
+ assertTrue( "Different result values", type.isEqual(oldval, newval, EntityMode.POJO) );
+ }
+ }
+
+ private void release(PreparedStatement ps) {
+ if ( ps != null ) {
+ try {
+ ps.close();
+ }
+ catch( Throwable t ) {}
+ }
+ }
+
+ private void release(ResultSet rs) {
+ if ( rs != null ) {
+ try {
+ rs.close();
+ }
+ catch( Throwable t ) {}
+ }
+ }
+
+ private void release(Session session) {
+ if ( session != null ) {
+ try {
+ session.close();
+ }
+ catch( Throwable t ) {}
+ }
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Reptile.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Reptile.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Reptile.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,15 @@
+//$Id: Reptile.java 5686 2005-02-12 07:27:32Z steveebersole $
+package org.hibernate.test.hql;
+
+/**
+ * @author Gavin King
+ */
+public class Reptile extends Animal {
+ private float bodyTemperature;
+ public float getBodyTemperature() {
+ return bodyTemperature;
+ }
+ public void setBodyTemperature(float bodyTemperature) {
+ this.bodyTemperature = bodyTemperature;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/SUV.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/SUV.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/SUV.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,10 @@
+// $Id: SUV.java 7087 2005-06-08 18:23:44Z steveebersole $
+package org.hibernate.test.hql;
+
+/**
+ * Implementation of SUV.
+ *
+ * @author Steve Ebersole
+ */
+public class SUV extends Truck {
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/ScrollableCollectionFetchingTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/ScrollableCollectionFetchingTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/ScrollableCollectionFetchingTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,220 @@
+// $Id: ScrollableCollectionFetchingTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.hql;
+
+import junit.framework.Test;
+
+import org.hibernate.HibernateException;
+import org.hibernate.ScrollMode;
+import org.hibernate.ScrollableResults;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * Tests the new functionality of allowing scrolling of results which
+ * contain collection fetches.
+ *
+ * @author Steve Ebersole
+ */
+public class ScrollableCollectionFetchingTest extends FunctionalTestCase {
+
+ public ScrollableCollectionFetchingTest(String name) {
+ super( name );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( ScrollableCollectionFetchingTest.class );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "hql/Animal.hbm.xml" };
+ }
+
+ public void testTupleReturnFails() {
+ Session s = openSession();
+ Transaction txn = s.beginTransaction();
+
+ try {
+ s.createQuery( "select a, a.weight from Animal a inner join fetch a.offspring" ).scroll();
+ fail( "scroll allowed with collection fetch and reurning tuples" );
+ }
+ catch( HibernateException e ) {
+ // expected result...
+ }
+
+ txn.commit();
+ s.close();
+ }
+
+ public void testScrollingJoinFetchesForward() {
+ if ( ! supportsResultSetPositionQueryMethodsOnForwardOnlyCursor() ) {
+ return;
+ }
+
+ TestData data = new TestData();
+ data.prepare();
+
+ Session s = openSession();
+ Transaction txn = s.beginTransaction();
+
+ ScrollableResults results = s
+ .createQuery( "from Animal a left join fetch a.offspring where a.description like :desc order by a.id" )
+ .setString( "desc", "root%" )
+ .scroll( ScrollMode.FORWARD_ONLY );
+
+ int counter = 0;
+ while ( results.next() ) {
+ counter++;
+ Animal animal = ( Animal ) results.get( 0 );
+ checkResult( animal );
+ }
+ assertEquals( "unexpected result count", 2, counter );
+
+ txn.commit();
+ s.close();
+
+ data.cleanup();
+ }
+
+ public void testScrollingJoinFetchesReverse() {
+ TestData data = new TestData();
+ data.prepare();
+
+ Session s = openSession();
+ Transaction txn = s.beginTransaction();
+
+ ScrollableResults results = s
+ .createQuery( "from Animal a left join fetch a.offspring where a.description like :desc order by a.id" )
+ .setString( "desc", "root%" )
+ .scroll();
+
+ results.afterLast();
+
+ int counter = 0;
+ while ( results.previous() ) {
+ counter++;
+ Animal animal = ( Animal ) results.get( 0 );
+ checkResult( animal );
+ }
+ assertEquals( "unexpected result count", 2, counter );
+
+ txn.commit();
+ s.close();
+
+ data.cleanup();
+ }
+
+ public void testScrollingJoinFetchesPositioning() {
+ TestData data = new TestData();
+ data.prepare();
+
+ Session s = openSession();
+ Transaction txn = s.beginTransaction();
+
+ ScrollableResults results = s
+ .createQuery( "from Animal a left join fetch a.offspring where a.description like :desc order by a.id" )
+ .setString( "desc", "root%" )
+ .scroll();
+
+ results.first();
+ Animal animal = ( Animal ) results.get( 0 );
+ assertEquals( "first() did not return expected row", data.root1Id, animal.getId() );
+
+ results.scroll( 1 );
+ animal = ( Animal ) results.get( 0 );
+ assertEquals( "scroll(1) did not return expected row", data.root2Id, animal.getId() );
+
+ results.scroll( -1 );
+ animal = ( Animal ) results.get( 0 );
+ assertEquals( "scroll(-1) did not return expected row", data.root1Id, animal.getId() );
+
+ results.setRowNumber( 1 );
+ animal = ( Animal ) results.get( 0 );
+ assertEquals( "setRowNumber(1) did not return expected row", data.root1Id, animal.getId() );
+
+ results.setRowNumber( 2 );
+ animal = ( Animal ) results.get( 0 );
+ assertEquals( "setRowNumber(2) did not return expected row", data.root2Id, animal.getId() );
+
+ txn.commit();
+ s.close();
+
+ data.cleanup();
+ }
+
+ private void checkResult(Animal animal) {
+ if ( "root-1".equals( animal.getDescription() ) ) {
+ assertEquals( "root-1 did not contain both children", 2, animal.getOffspring().size() );
+ }
+ else if ( "root-2".equals( animal.getDescription() ) ) {
+ assertEquals( "root-2 did not contain zero children", 0, animal.getOffspring().size() );
+ }
+ }
+
+ private class TestData {
+
+ private Long root1Id;
+ private Long root2Id;
+
+ private void prepare() {
+ Session s = openSession();
+ Transaction txn = s.beginTransaction();
+
+ Animal mother = new Animal();
+ mother.setDescription( "root-1" );
+
+ Animal another = new Animal();
+ another.setDescription( "root-2" );
+
+ Animal son = new Animal();
+ son.setDescription( "son");
+
+ Animal daughter = new Animal();
+ daughter.setDescription( "daughter" );
+
+ Animal grandson = new Animal();
+ grandson.setDescription( "grandson" );
+
+ Animal grandDaughter = new Animal();
+ grandDaughter.setDescription( "granddaughter" );
+
+ son.setMother( mother );
+ mother.addOffspring( son );
+
+ daughter.setMother( mother );
+ mother.addOffspring( daughter );
+
+ grandson.setMother( daughter );
+ daughter.addOffspring( grandson );
+
+ grandDaughter.setMother( daughter );
+ daughter.addOffspring( grandDaughter );
+
+ s.save( mother );
+ s.save( another );
+ s.save( son );
+ s.save( daughter );
+ s.save( grandson );
+ s.save( grandDaughter );
+
+ txn.commit();
+ s.close();
+
+ root1Id = mother.getId();
+ root2Id = another.getId();
+ }
+
+ private void cleanup() {
+ Session s = openSession();
+ Transaction txn = s.beginTransaction();
+
+ s.createQuery( "delete Animal where description like 'grand%'" ).executeUpdate();
+ s.createQuery( "delete Animal where not description like 'root%'" ).executeUpdate();
+ s.createQuery( "delete Animal" ).executeUpdate();
+
+ txn.commit();
+ s.close();
+ }
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/SimpleAssociatedEntity.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/SimpleAssociatedEntity.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/SimpleAssociatedEntity.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,63 @@
+package org.hibernate.test.hql;
+
+/**
+ * @author Steve Ebersole
+ */
+public class SimpleAssociatedEntity {
+ private Long id;
+ private String name;
+ private SimpleEntityWithAssociation owner;
+
+ public SimpleAssociatedEntity() {
+ }
+
+ public SimpleAssociatedEntity(String name) {
+ this.name = name;
+ }
+
+ public SimpleAssociatedEntity(String name, SimpleEntityWithAssociation owner) {
+ this( name );
+ bindToOwner( owner );
+ }
+
+ 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 SimpleEntityWithAssociation getOwner() {
+ return owner;
+ }
+
+ public void setOwner(SimpleEntityWithAssociation owner) {
+ this.owner = owner;
+ }
+
+ public void bindToOwner(SimpleEntityWithAssociation owner) {
+ if ( owner != this.owner ) {
+ unbindFromCurrentOwner();
+ if ( owner != null ) {
+ owner.getAssociatedEntities().add( this );
+ }
+ }
+ this.owner = owner;
+ }
+
+ public void unbindFromCurrentOwner() {
+ if ( this.owner != null ) {
+ this.owner.getAssociatedEntities().remove( this );
+ this.owner = null;
+ }
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/SimpleEntityWithAssociation.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/SimpleEntityWithAssociation.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/SimpleEntityWithAssociation.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,32 @@
+<?xml version="1.0"?>
+
+<!DOCTYPE hibernate-mapping SYSTEM "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
+
+<hibernate-mapping package="org.hibernate.test.hql">
+
+ <!-- *Very* important for the test cases that these entities have identically named columns! -->
+
+ <class name="SimpleEntityWithAssociation" table="SIMPLE_1">
+ <id name="id" column="ID" type="long">
+ <generator class="native"/>
+ </id>
+ <property name="name" column="NAME" type="string"/>
+ <set name="associatedEntities" cascade="all" inverse="true" lazy="true">
+ <key column="SIMPLE_1_ID"/>
+ <one-to-many class="SimpleAssociatedEntity"/>
+ </set>
+ <set name="manyToManyAssociatedEntities" cascade="save-update" inverse="false" lazy="true" table="MANY_TO_MANY">
+ <key column="IN_ID"/>
+ <many-to-many class="SimpleEntityWithAssociation" column="OUT_ID"/>
+ </set>
+ </class>
+
+ <class name="SimpleAssociatedEntity" table="SIMPLE_2">
+ <id name="id" column="ID" type="long">
+ <generator class="native"/>
+ </id>
+ <property name="name" column="NAME" type="string" />
+ <many-to-one name="owner" class="SimpleEntityWithAssociation" column="SIMPLE_1_ID"/>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/SimpleEntityWithAssociation.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/SimpleEntityWithAssociation.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/SimpleEntityWithAssociation.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,70 @@
+package org.hibernate.test.hql;
+
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * @author Steve Ebersole
+ */
+public class SimpleEntityWithAssociation {
+ private Long id;
+ private String name;
+ private Set associatedEntities = new HashSet();
+ private Set manyToManyAssociatedEntities = new HashSet();
+
+ public SimpleEntityWithAssociation() {
+ }
+
+ public SimpleEntityWithAssociation(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;
+ }
+
+ public Set getAssociatedEntities() {
+ return associatedEntities;
+ }
+
+ public void setAssociatedEntities(Set associatedEntities) {
+ this.associatedEntities = associatedEntities;
+ }
+
+ public SimpleAssociatedEntity addAssociation(String name) {
+ return new SimpleAssociatedEntity( name, this );
+ }
+
+ public void addAssociation(SimpleAssociatedEntity association) {
+ association.bindToOwner( this );
+ }
+
+ public void removeAssociation(SimpleAssociatedEntity association) {
+ if ( getAssociatedEntities().contains( association ) ) {
+ association.unbindFromCurrentOwner();
+ }
+ else {
+ throw new IllegalArgumentException( "SimpleAssociatedEntity [" + association + "] not currently bound to this [" + this + "]" );
+ }
+ }
+
+ public Set getManyToManyAssociatedEntities() {
+ return manyToManyAssociatedEntities;
+ }
+
+ public void setManyToManyAssociatedEntities(Set manyToManyAssociatedEntities) {
+ this.manyToManyAssociatedEntities = manyToManyAssociatedEntities;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/StateProvince.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/StateProvince.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/StateProvince.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,37 @@
+// $Id: StateProvince.java 7996 2005-08-22 14:49:57Z steveebersole $
+package org.hibernate.test.hql;
+
+/**
+ * Implementation of StateProvince.
+ *
+ * @author Steve Ebersole
+ */
+public class StateProvince {
+ private Long id;
+ private String name;
+ private String isoCode;
+
+ 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 String getIsoCode() {
+ return isoCode;
+ }
+
+ public void setIsoCode(String isoCode) {
+ this.isoCode = isoCode;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/TimestampVersioned.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/TimestampVersioned.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/TimestampVersioned.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,38 @@
+// $Id: TimestampVersioned.java 7384 2005-07-06 17:04:45Z steveebersole $
+package org.hibernate.test.hql;
+
+import java.util.Date;
+
+/**
+ * Implementation of TimestampVersioned.
+ *
+ * @author Steve Ebersole
+ */
+public class TimestampVersioned {
+ private Long id;
+ private Date version;
+ private String name;
+
+ public TimestampVersioned() {
+ }
+
+ public TimestampVersioned(String name) {
+ this.name = name;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public Date getVersion() {
+ return version;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Truck.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Truck.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Truck.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,10 @@
+// $Id: Truck.java 7087 2005-06-08 18:23:44Z steveebersole $
+package org.hibernate.test.hql;
+
+/**
+ * Implementation of Truck.
+ *
+ * @author Steve Ebersole
+ */
+public class Truck extends Vehicle {
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/User.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/User.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/User.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,48 @@
+//$Id: User.java 5891 2005-02-24 01:18:15Z oneovthafew $
+package org.hibernate.test.hql;
+
+import java.util.List;
+
+/**
+ * @author Gavin King
+ */
+public class User {
+ private Long id;
+ private String userName;
+ private Human human;
+ private List permissions;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getUserName() {
+ return userName;
+ }
+
+ public void setUserName(String userName) {
+ this.userName = userName;
+ }
+
+ public Human getHuman() {
+ return human;
+ }
+
+ public void setHuman(Human human) {
+ this.human = human;
+ }
+
+ public List getPermissions() {
+ return permissions;
+ }
+
+
+ public void setPermissions(List permissions) {
+ this.permissions = permissions;
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/User.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Vehicle.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Vehicle.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Vehicle.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.hql">
+
+ <!-- Vehicle represents an abstract root of a union-subclass hierarchy -->
+ <class name="Vehicle" abstract="true">
+ <id name="id" access="field" type="long">
+ <generator class="increment"/>
+ </id>
+ <property name="vin" type="string"/>
+ <property name="owner" type="string"/>
+
+ <!-- Car represents a concrete leaf of a union-subclass hierarchy with no concrete super -->
+ <union-subclass name="Car"/>
+
+ <!-- Truck represents a concrete subclass in a union-subclass hierarchy with concrete subclasses and no concrete super -->
+ <union-subclass name="Truck">
+ <!-- Both SUV and Pickup represent concrete leaf of a union-subclass hierarchy (like Car), but with a concrete super -->
+ <union-subclass name="SUV"/>
+ <union-subclass name="Pickup"/>
+ </union-subclass>
+ </class>
+
+ <sql-query name="native-delete-car">
+ <synchronize table="Car"/>
+ delete from CAR where owner = ?
+ </sql-query>
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Vehicle.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Vehicle.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Vehicle.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,29 @@
+// $Id: Vehicle.java 7087 2005-06-08 18:23:44Z steveebersole $
+package org.hibernate.test.hql;
+
+/**
+ * Implementation of Vehicle.
+ *
+ * @author Steve Ebersole
+ */
+public abstract class Vehicle {
+ private Long id;
+ private String vin;
+ private String owner;
+
+ public String getVin() {
+ return vin;
+ }
+
+ public void setVin(String vin) {
+ this.vin = vin;
+ }
+
+ public String getOwner() {
+ return owner;
+ }
+
+ public void setOwner(String owner) {
+ this.owner = owner;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Versions.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Versions.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Versions.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping SYSTEM "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
+
+<hibernate-mapping package="org.hibernate.test.hql" default-access="field">
+
+ <class name="IntegerVersioned">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <version name="version" column="vers"/>
+ <property name="name"/>
+ </class>
+
+ <class name="TimestampVersioned">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <timestamp name="version" column="vers"/>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/WithClauseTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/WithClauseTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/WithClauseTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,196 @@
+// $Id: WithClauseTest.java 10945 2006-12-07 14:50:42Z steve.ebersole at jboss.com $
+package org.hibernate.test.hql;
+
+import org.hibernate.test.TestCase;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.HibernateException;
+import org.hibernate.QueryException;
+import org.hibernate.hql.ast.InvalidWithClauseException;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * Implementation of WithClauseTest.
+ *
+ * @author Steve Ebersole
+ */
+public class WithClauseTest extends TestCase {
+
+ public WithClauseTest(String name) {
+ super( name );
+ }
+
+ protected String[] getMappings() {
+ return new String[] { "hql/Animal.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new TestSuite( WithClauseTest.class );
+ }
+
+ protected void configure(Configuration cfg) {
+ super.configure( cfg );
+ }
+
+ public void testWithClauseFailsWithFetch() {
+ TestData data = new TestData();
+ data.prepare();
+
+ Session s = openSession();
+ Transaction txn = s.beginTransaction();
+
+ try {
+ s.createQuery( "from Animal a inner join fetch a.offspring as o with o.bodyWeight = :someLimit" )
+ .setDouble( "someLimit", 1 )
+ .list();
+ fail( "ad-hoc on clause allowed with fetched association" );
+ }
+ catch ( HibernateException e ) {
+ // the expected response...
+ }
+
+ txn.commit();
+ s.close();
+
+ data.cleanup();
+ }
+
+ public void testInvalidWithSemantics() {
+ Session s = openSession();
+ Transaction txn = s.beginTransaction();
+
+ try {
+ // PROBLEM : f.bodyWeight is a reference to a column on the Animal table; however, the 'f'
+ // alias relates to the Human.friends collection which the aonther Human entity. The issue
+ // here is the way JoinSequence and Joinable (the persister) interact to generate the
+ // joins relating to the sublcass/superclass tables
+ s.createQuery( "from Human h inner join h.friends as f with f.bodyWeight < :someLimit" )
+ .setDouble( "someLimit", 1 )
+ .list();
+ fail( "failure expected" );
+ }
+ catch( InvalidWithClauseException expected ) {
+ }
+
+ try {
+ s.createQuery( "from Animal a inner join a.offspring o inner join o.mother as m inner join m.father as f with o.bodyWeight > 1" )
+ .list();
+ fail( "failure expected" );
+ }
+ catch( InvalidWithClauseException expected ) {
+ }
+
+ try {
+ s.createQuery( "from Human h inner join h.offspring o with o.mother.father = :cousin" )
+ .setEntity( "cousin", s.load( Human.class, new Long(123) ) )
+ .list();
+ fail( "failure expected" );
+ }
+ catch( InvalidWithClauseException expected ) {
+ }
+
+ txn.commit();
+ s.close();
+ }
+
+ public void testWithClause() {
+ TestData data = new TestData();
+ data.prepare();
+
+ Session s = openSession();
+ Transaction txn = s.beginTransaction();
+
+ // one-to-many
+ List list = s.createQuery( "from Human h inner join h.offspring as o with o.bodyWeight < :someLimit" )
+ .setDouble( "someLimit", 1 )
+ .list();
+ assertTrue( "ad-hoc on did not take effect", list.isEmpty() );
+
+ // many-to-one
+ list = s.createQuery( "from Animal a inner join a.mother as m with m.bodyWeight < :someLimit" )
+ .setDouble( "someLimit", 1 )
+ .list();
+ assertTrue( "ad-hoc on did not take effect", list.isEmpty() );
+
+ // many-to-many
+ list = s.createQuery( "from Human h inner join h.friends as f with f.nickName like 'bubba'" )
+ .list();
+ assertTrue( "ad-hoc on did not take effect", list.isEmpty() );
+
+ txn.commit();
+ s.close();
+
+ data.cleanup();
+ }
+
+ private class TestData {
+ public void prepare() {
+ Session session = openSession();
+ Transaction txn = session.beginTransaction();
+
+ Human mother = new Human();
+ mother.setBodyWeight( 10 );
+ mother.setDescription( "mother" );
+
+ Human father = new Human();
+ father.setBodyWeight( 15 );
+ father.setDescription( "father" );
+
+ Human child1 = new Human();
+ child1.setBodyWeight( 5 );
+ child1.setDescription( "child1" );
+
+ Human child2 = new Human();
+ child2.setBodyWeight( 6 );
+ child2.setDescription( "child2" );
+
+ Human friend = new Human();
+ friend.setBodyWeight( 20 );
+ friend.setDescription( "friend" );
+
+ child1.setMother( mother );
+ child1.setFather( father );
+ mother.addOffspring( child1 );
+ father.addOffspring( child1 );
+
+ child2.setMother( mother );
+ child2.setFather( father );
+ mother.addOffspring( child2 );
+ father.addOffspring( child2 );
+
+ father.setFriends( new ArrayList() );
+ father.getFriends().add( friend );
+
+ session.save( mother );
+ session.save( father );
+ session.save( child1 );
+ session.save( child2 );
+ session.save( friend );
+
+ txn.commit();
+ session.close();
+ }
+
+ public void cleanup() {
+ Session session = openSession();
+ Transaction txn = session.beginTransaction();
+ session.createQuery( "delete Animal where mother is not null" ).executeUpdate();
+ List humansWithFriends = session.createQuery( "from Human h where exists(from h.friends)" ).list();
+ Iterator itr = humansWithFriends.iterator();
+ while ( itr.hasNext() ) {
+ session.delete( itr.next() );
+ }
+ session.createQuery( "delete Animal" ).executeUpdate();
+ txn.commit();
+ session.close();
+ }
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Zoo.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Zoo.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/hql/Zoo.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,64 @@
+//$Id: Zoo.java 10653 2006-10-26 13:38:50Z steve.ebersole at jboss.com $
+package org.hibernate.test.hql;
+
+import java.util.Map;
+
+/**
+ * @author Gavin King
+ */
+public class Zoo {
+ private Long id;
+ private String name;
+ private Classification classification;
+ private Map animals;
+ private Map mammals;
+ 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 Map getMammals() {
+ return mammals;
+ }
+
+ public void setMammals(Map mammals) {
+ this.mammals = mammals;
+ }
+
+ public Map getAnimals() {
+ return animals;
+ }
+
+ public void setAnimals(Map animals) {
+ this.animals = animals;
+ }
+
+ public Address getAddress() {
+ return address;
+ }
+
+ public void setAddress(Address address) {
+ this.address = address;
+ }
+
+ public Classification getClassification() {
+ return classification;
+ }
+
+ public void setClassification(Classification classification) {
+ this.classification = classification;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/Car.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/Car.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/Car.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+ This mapping demonstrates the use of
+ the EJB3 compliant table hilo generator
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.id">
+
+ <class name="Car">
+
+ <id name="id">
+ <generator class="org.hibernate.id.MultipleHiLoPerTableGenerator">
+ <param name="max_lo">0</param>
+ </generator>
+ </id>
+ <property name="color"/>
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/Car.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/Car.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/Car.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,26 @@
+//$Id: Car.java 5686 2005-02-12 07:27:32Z steveebersole $
+package org.hibernate.test.id;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class Car {
+ private Long id;
+ private String color;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getColor() {
+ return color;
+ }
+
+ public void setColor(String color) {
+ this.color = color;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/MultipleHiLoPerTableGeneratorTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/MultipleHiLoPerTableGeneratorTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/MultipleHiLoPerTableGeneratorTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,107 @@
+//$Id: MultipleHiLoPerTableGeneratorTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.id;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class MultipleHiLoPerTableGeneratorTest extends FunctionalTestCase {
+ public MultipleHiLoPerTableGeneratorTest(String x) {
+ super(x);
+ }
+
+ public String[] getMappings() {
+ return new String[]{ "id/Car.hbm.xml", "id/Plane.hbm.xml", "id/Radio.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( MultipleHiLoPerTableGeneratorTest.class );
+ }
+
+ public void testDistinctId() throws Exception {
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ int testLength = 8;
+ Car[] cars = new Car[testLength];
+ Plane[] planes = new Plane[testLength];
+ for (int i = 0; i < testLength ; i++) {
+ cars[i] = new Car();
+ cars[i].setColor("Color" + i);
+ planes[i] = new Plane();
+ planes[i].setNbrOfSeats(i);
+ s.persist(cars[i]);
+ //s.persist(planes[i]);
+ }
+ tx.commit();
+ s.close();
+ for (int i = 0; i < testLength ; i++) {
+ assertEquals(i+1, cars[i].getId().intValue());
+ //assertEquals(i+1, planes[i].getId().intValue());
+ }
+
+ s = openSession();
+ tx = s.beginTransaction();
+ s.createQuery( "delete from Car" ).executeUpdate();
+ tx.commit();
+ s.close();
+ }
+
+ public void testRollingBack() throws Throwable {
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ int testLength = 3;
+ Long lastId = null;
+ for (int i = 0; i < testLength ; i++) {
+ Car car = new Car();
+ car.setColor( "color " + i );
+ s.save( car );
+ lastId = car.getId();
+ }
+ tx.rollback();
+ s.close();
+
+ s = openSession();
+ tx = s.beginTransaction();
+ Car car = new Car();
+ car.setColor( "blue" );
+ s.save( car );
+ s.flush();
+ tx.commit();
+ s.close();
+
+ assertEquals( "id generation was rolled back", lastId.longValue() + 1, car.getId().longValue() );
+
+ s = openSession();
+ tx = s.beginTransaction();
+ s.createQuery( "delete Car" ).executeUpdate();
+ tx.commit();
+ s.close();
+ }
+
+ public void testAllParams() throws Exception {
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ Radio radio = new Radio();
+ radio.setFrequency("32 MHz");
+ s.persist(radio);
+ assertEquals( new Integer(1), radio.getId() );
+ radio = new Radio();
+ radio.setFrequency("32 MHz");
+ s.persist(radio);
+ assertEquals( new Integer(2), radio.getId() );
+ tx.commit();
+ s.close();
+
+ s = openSession();
+ tx = s.beginTransaction();
+ s.createQuery( "delete from Radio" ).executeUpdate();
+ tx.commit();
+ s.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/Plane.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/Plane.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/Plane.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+ This mapping demonstrates the use of
+ the EJB3 compliant table hilo generator
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.id">
+
+ <class name="Plane">
+
+ <id name="id">
+ <generator class="org.hibernate.id.MultipleHiLoPerTableGenerator">
+ <param name="max_lo">2</param>
+ </generator>
+ </id>
+ <property name="nbrOfSeats"/>
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/Plane.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/Plane.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/Plane.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,26 @@
+//$Id: Plane.java 5686 2005-02-12 07:27:32Z steveebersole $
+package org.hibernate.test.id;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class Plane {
+ private Long id;
+ private int nbrOfSeats;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public int getNbrOfSeats() {
+ return nbrOfSeats;
+ }
+
+ public void setNbrOfSeats(int nbrOfSeats) {
+ this.nbrOfSeats = nbrOfSeats;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/Product.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/Product.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/Product.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.id">
+
+ <class name="Product" table="t_product">
+ <id name="name">
+ <generator class="uuid"/>
+ </id>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/Product.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/Product.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/Product.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,17 @@
+//$Id: $
+package org.hibernate.test.id;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class Product {
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/Radio.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/Radio.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/Radio.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+ This mapping demonstrates the use of
+ the EJB3 compliant table hilo generator
+ with all the parameters
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.id">
+
+ <class name="Radio">
+
+ <id name="id">
+ <generator class="org.hibernate.id.MultipleHiLoPerTableGenerator">
+ <param name="table">sequences</param>
+ <param name="value_column">hi_value</param>
+ <param name="primary_key_column">name</param>
+ <param name="primary_key_length">50</param>
+ <param name="max_lo">2</param>
+ <param name="primary_key_value">Radio</param>
+ </generator>
+ </id>
+ <property name="frequency"/>
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/Radio.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/Radio.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/Radio.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,26 @@
+//$Id: Radio.java 5686 2005-02-12 07:27:32Z steveebersole $
+package org.hibernate.test.id;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class Radio {
+ private Integer id;
+ private String frequency;
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getFrequency() {
+ return frequency;
+ }
+
+ public void setFrequency(String frequency) {
+ this.frequency = frequency;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/UseIdentifierRollbackTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/UseIdentifierRollbackTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/id/UseIdentifierRollbackTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,44 @@
+//$Id: $
+package org.hibernate.test.id;
+
+import org.hibernate.test.TestCase;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import junit.framework.Test;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class UseIdentifierRollbackTest extends TestCase {
+ public UseIdentifierRollbackTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "id/Product.hbm.xml" };
+ }
+
+ public void configure(Configuration cfg) {
+ cfg.setProperty( Environment.USE_IDENTIFIER_ROLLBACK, "true");
+ super.configure( cfg );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( UseIdentifierRollbackTest.class );
+ }
+
+ public void testSimpleRollback() {
+ Session session = openSession();
+ Transaction t = session.beginTransaction();
+ Product prod = new Product();
+ assertNull( prod.getName() );
+ session.persist(prod);
+ session.flush();
+ assertNotNull( prod.getName() );
+ t.rollback();
+ session.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idbag/Group.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idbag/Group.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idbag/Group.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,24 @@
+//$Id: Group.java 6058 2005-03-11 17:05:19Z oneovthafew $
+package org.hibernate.test.idbag;
+
+/**
+ * @author Gavin King
+ */
+public class Group {
+ private String name;
+
+ Group() {}
+
+ public Group(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ void setName(String name) {
+ this.name = name;
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idbag/Group.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idbag/IdBagTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idbag/IdBagTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idbag/IdBagTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,101 @@
+//$Id: IdBagTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.idbag;
+
+import java.sql.SQLException;
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class IdBagTest extends FunctionalTestCase {
+
+ public IdBagTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "idbag/UserGroup.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( IdBagTest.class );
+ }
+
+ public void testUpdateIdBag() throws HibernateException, SQLException {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ User gavin = new User("gavin");
+ Group admins = new Group("admins");
+ Group plebs = new Group("plebs");
+ Group moderators = new Group("moderators");
+ Group banned = new Group("banned");
+ gavin.getGroups().add(plebs);
+ //gavin.getGroups().add(moderators);
+ s.persist(gavin);
+ s.persist(plebs);
+ s.persist(admins);
+ s.persist(moderators);
+ s.persist(banned);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ gavin = (User) s.createCriteria(User.class).uniqueResult();
+ admins = (Group) s.load(Group.class, "admins");
+ plebs = (Group) s.load(Group.class, "plebs");
+ banned = (Group) s.load(Group.class, "banned");
+ gavin.getGroups().add(admins);
+ gavin.getGroups().remove(plebs);
+ //gavin.getGroups().add(banned);
+
+ s.delete(plebs);
+ s.delete(banned);
+ s.delete(moderators);
+ s.delete(admins);
+ s.delete(gavin);
+
+ t.commit();
+ s.close();
+ }
+
+ public void testJoin() throws HibernateException, SQLException {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ User gavin = new User("gavin");
+ Group admins = new Group("admins");
+ Group plebs = new Group("plebs");
+ gavin.getGroups().add(plebs);
+ gavin.getGroups().add(admins);
+ s.persist(gavin);
+ s.persist(plebs);
+ s.persist(admins);
+
+ List l = s.createQuery("from User u join u.groups g").list();
+ assertEquals( l.size(), 2 );
+ s.clear();
+
+ gavin = (User) s.createQuery("from User u join fetch u.groups").uniqueResult();
+ assertTrue( Hibernate.isInitialized( gavin.getGroups() ) );
+ assertEquals( gavin.getGroups().size(), 2 );
+ assertEquals( ( (Group) gavin.getGroups().get(0) ).getName(), "admins" );
+
+ s.delete( gavin.getGroups().get(0) );
+ s.delete( gavin.getGroups().get(1) );
+ s.delete(gavin);
+
+ t.commit();
+ s.close();
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idbag/IdBagTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idbag/User.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idbag/User.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idbag/User.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,37 @@
+//$Id: User.java 6058 2005-03-11 17:05:19Z oneovthafew $
+package org.hibernate.test.idbag;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Gavin King
+ */
+public class User {
+ private String name;
+ private List groups = new ArrayList();
+
+ User() {}
+
+ public User(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+
+ void setName(String name) {
+ this.name = name;
+ }
+
+ public List getGroups() {
+ return groups;
+ }
+
+ void setGroups(List groups) {
+ this.groups = groups;
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idbag/User.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idbag/UserGroup.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idbag/UserGroup.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idbag/UserGroup.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+ This mapping demonstrates how to use an idbag to represent
+ a many-to-many association where the association table has
+ a surrogate key.
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.idbag">
+
+ <class name="User" table="`Users`">
+ <id name="name"/>
+
+ <idbag name="groups"
+ order-by="groupName asc"
+ table="`UserGroups`">
+ <collection-id column="userGroupId"
+ type="long">
+ <generator class="increment"/>
+ </collection-id>
+ <key column="userName"/>
+ <many-to-many column="groupName"
+ class="Group"/>
+ </idbag>
+
+ </class>
+
+ <class name="Group" table="`Groups`">
+ <id name="name"/>
+ </class>
+
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idbag/UserGroup.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idclass/Customer.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idclass/Customer.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idclass/Customer.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -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">
+
+<!--
+
+ This mapping demonstrates how to use an id class not
+ embedded in the entity. Only the property values are
+ shared between the 2. This mimic the EJB2.1 and EJB3
+ semantic.
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.idclass">
+
+ <class name="Customer">
+ <composite-id class="CustomerId" mapped="true">
+ <key-property name="orgName" column="org_name"/>
+ <key-property name="customerName" column="cust_name"/>
+ </composite-id>
+ <discriminator column="disc"/>
+ <property name="address"/>
+ <subclass name="FavoriteCustomer"/>
+ </class>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idclass/Customer.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idclass/Customer.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idclass/Customer.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idclass/Customer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,90 @@
+//$Id: Customer.java 7858 2005-08-11 21:46:58Z epbernard $
+
+package org.hibernate.test.idclass;
+
+
+
+
+
+public class Customer {
+
+
+
+ public Customer() {
+
+ super();
+
+ }
+
+
+
+ public Customer(String orgName, String custName, String add) {
+
+ this.orgName = orgName;
+
+ this.customerName = custName;
+
+ this.address = add;
+
+ }
+
+
+
+ private String orgName;
+
+ private String customerName;
+
+ private String address;
+
+
+
+ public String getAddress() {
+
+ return address;
+
+ }
+
+
+
+ public void setAddress(String address) {
+
+ this.address = address;
+
+ }
+
+
+
+ public String getCustomerName() {
+
+ return customerName;
+
+ }
+
+
+
+ public void setCustomerName(String customerName) {
+
+ this.customerName = customerName;
+
+ }
+
+
+
+ public String getOrgName() {
+
+ return orgName;
+
+ }
+
+
+
+ public void setOrgName(String orgName) {
+
+ this.orgName = orgName;
+
+ }
+
+
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idclass/Customer.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idclass/CustomerId.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idclass/CustomerId.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idclass/CustomerId.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,36 @@
+//$Id: CustomerId.java 7239 2005-06-20 09:44:54Z oneovthafew $
+package org.hibernate.test.idclass;
+
+import java.io.Serializable;
+
+public class CustomerId implements Serializable {
+
+ private String orgName;
+ private String customerName;
+
+ public CustomerId() {
+ super();
+ }
+
+ public CustomerId(String orgName, String custName) {
+ this.orgName = orgName;
+ this.customerName = custName;
+ }
+
+ public String getCustomerName() {
+ return customerName;
+ }
+
+ public void setCustomerName(String customerName) {
+ this.customerName = customerName;
+ }
+
+ public String getOrgName() {
+ return orgName;
+ }
+
+ public void setOrgName(String orgName) {
+ this.orgName = orgName;
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idclass/CustomerId.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idclass/FavoriteCustomer.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idclass/FavoriteCustomer.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idclass/FavoriteCustomer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,14 @@
+//$Id: FavoriteCustomer.java 7858 2005-08-11 21:46:58Z epbernard $
+package org.hibernate.test.idclass;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class FavoriteCustomer extends Customer {
+ public FavoriteCustomer() {
+ }
+
+ public FavoriteCustomer(String orgName, String custName, String add) {
+ super( orgName, custName, add );
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idclass/IdClassTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idclass/IdClassTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idclass/IdClassTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,69 @@
+//$Id: IdClassTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.idclass;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class IdClassTest extends FunctionalTestCase {
+
+ public IdClassTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "idclass/Customer.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( IdClassTest.class );
+ }
+
+ public void testIdClass() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Customer cust = new FavoriteCustomer("JBoss", "RouteOne", "Detroit");
+ s.persist(cust);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ CustomerId custId = new CustomerId("JBoss", "RouteOne");
+ t = s.beginTransaction();
+ cust = (Customer) s.get(Customer.class, custId);
+ assertEquals( "Detroit", cust.getAddress() );
+ assertEquals( cust.getCustomerName(), custId.getCustomerName() );
+ assertEquals( cust.getOrgName(), custId.getOrgName() );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ cust = (Customer) s.createQuery("from Customer where id.customerName = 'RouteOne'").uniqueResult();
+ assertEquals( "Detroit", cust.getAddress() );
+ assertEquals( cust.getCustomerName(), custId.getCustomerName() );
+ assertEquals( cust.getOrgName(), custId.getOrgName() );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ cust = (Customer) s.createQuery("from Customer where customerName = 'RouteOne'").uniqueResult();
+ assertEquals( "Detroit", cust.getAddress() );
+ assertEquals( cust.getCustomerName(), custId.getCustomerName() );
+ assertEquals( cust.getOrgName(), custId.getOrgName() );
+
+ s.createQuery( "delete from Customer" ).executeUpdate();
+
+ t.commit();
+ s.close();
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idclass/IdClassTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/IdGenSuite.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/IdGenSuite.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/IdGenSuite.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,44 @@
+package org.hibernate.test.idgen;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.hibernate.test.idgen.enhanced.OptimizerUnitTest;
+import org.hibernate.test.idgen.enhanced.SequenceStyleConfigUnitTest;
+import org.hibernate.test.idgen.enhanced.forcedtable.BasicForcedTableSequenceTest;
+import org.hibernate.test.idgen.enhanced.forcedtable.HiLoForcedTableSequenceTest;
+import org.hibernate.test.idgen.enhanced.forcedtable.PooledForcedTableSequenceTest;
+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;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class IdGenSuite {
+ public static Test suite() {
+ TestSuite suite = new TestSuite( "enhanced id generators" );
+
+ suite.addTest( OptimizerUnitTest.suite() );
+ suite.addTest( SequenceStyleConfigUnitTest.suite() );
+
+ suite.addTest( BasicForcedTableSequenceTest.suite() );
+ suite.addTest( HiLoForcedTableSequenceTest.suite() );
+ suite.addTest( PooledForcedTableSequenceTest.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/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/OptimizerUnitTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/OptimizerUnitTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/OptimizerUnitTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -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/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/SequenceStyleConfigUnitTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/SequenceStyleConfigUnitTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/SequenceStyleConfigUnitTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -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/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/Basic.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/Basic.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/Basic.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -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.forcedtable">
+
+ <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/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/BasicForcedTableSequenceTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/BasicForcedTableSequenceTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/BasicForcedTableSequenceTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,57 @@
+package org.hibernate.test.idgen.enhanced.forcedtable;
+
+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 BasicForcedTableSequenceTest extends DatabaseSpecificFunctionalTestCase {
+ public BasicForcedTableSequenceTest(String string) {
+ super( string );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "idgen/enhanced/forcedtable/Basic.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( BasicForcedTableSequenceTest.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/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/Entity.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/Entity.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/Entity.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,34 @@
+package org.hibernate.test.idgen.enhanced.forcedtable;
+
+/**
+ * {@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/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/HiLo.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/HiLo.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/HiLo.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -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.forcedtable">
+
+ <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/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/HiLoForcedTableSequenceTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/HiLoForcedTableSequenceTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/HiLoForcedTableSequenceTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,69 @@
+package org.hibernate.test.idgen.enhanced.forcedtable;
+
+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 HiLoForcedTableSequenceTest extends DatabaseSpecificFunctionalTestCase {
+ public HiLoForcedTableSequenceTest(String string) {
+ super( string );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "idgen/enhanced/forcedtable/HiLo.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( HiLoForcedTableSequenceTest.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/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/Pooled.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/Pooled.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/Pooled.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -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.forcedtable">
+
+ <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/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/PooledForcedTableSequenceTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/PooledForcedTableSequenceTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/forcedtable/PooledForcedTableSequenceTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,69 @@
+package org.hibernate.test.idgen.enhanced.forcedtable;
+
+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 PooledForcedTableSequenceTest extends DatabaseSpecificFunctionalTestCase {
+ public PooledForcedTableSequenceTest(String string) {
+ super( string );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "idgen/enhanced/forcedtable/Pooled.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( PooledForcedTableSequenceTest.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();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/sequence/Basic.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/sequence/Basic.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/sequence/Basic.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -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/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/sequence/BasicSequenceTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/sequence/BasicSequenceTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/sequence/BasicSequenceTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -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/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/sequence/Entity.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/sequence/Entity.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/sequence/Entity.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -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/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/sequence/HiLo.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/sequence/HiLo.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/sequence/HiLo.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -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/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/sequence/HiLoSequenceTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/sequence/HiLoSequenceTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/sequence/HiLoSequenceTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -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/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/sequence/Pooled.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/sequence/Pooled.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/sequence/Pooled.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -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/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/sequence/PooledSequenceTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/sequence/PooledSequenceTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/sequence/PooledSequenceTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -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/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/table/Basic.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/table/Basic.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/table/Basic.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.idgen.enhanced.table">
+
+ <class name="Entity" table="ID_TBL_BSC_ENTITY">
+ <id name="id" column="ID" type="long">
+ <generator class="org.hibernate.id.enhanced.TableGenerator">
+ <param name="table_name">ID_TBL_BSC_TBL</param>
+ <param name="segment_value">test</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/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/table/BasicTableTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/table/BasicTableTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/table/BasicTableTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,57 @@
+package org.hibernate.test.idgen.enhanced.table;
+
+import junit.framework.Test;
+
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.id.enhanced.TableGenerator;
+import org.hibernate.Session;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class BasicTableTest extends FunctionalTestCase {
+ 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( TableGenerator.class, persister.getIdentifierGenerator().getClass() );
+ TableGenerator generator = ( TableGenerator ) 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.getTableAccessCount() );
+ 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/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/table/Entity.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/table/Entity.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/table/Entity.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -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/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/table/HiLo.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/table/HiLo.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/table/HiLo.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.idgen.enhanced.table">
+
+ <class name="Entity" table="ID_TBL_HILO_ENTITY">
+ <id name="id" column="ID" type="long">
+ <generator class="org.hibernate.id.enhanced.TableGenerator">
+ <param name="table_name">ID_TBL_HILO_TBL</param>
+ <param name="segment_value">test</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/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/table/HiLoTableTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/table/HiLoTableTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/table/HiLoTableTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,67 @@
+package org.hibernate.test.idgen.enhanced.table;
+
+import junit.framework.Test;
+
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.id.enhanced.OptimizerFactory;
+import org.hibernate.id.enhanced.TableGenerator;
+import org.hibernate.Session;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class HiLoTableTest extends FunctionalTestCase {
+ 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( TableGenerator.class, persister.getIdentifierGenerator().getClass() );
+ TableGenerator generator = ( TableGenerator ) 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.getTableAccessCount() ); // 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.getTableAccessCount() ); // 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/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/table/Pooled.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/table/Pooled.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/table/Pooled.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -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 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.table">
+
+ <class name="Entity" table="ID_TBL_POOL_ENTITY">
+ <id name="id" column="ID" type="long">
+ <generator class="org.hibernate.id.enhanced.TableGenerator">
+ <param name="table_name">ID_TBL_POOL_TBL</param>
+ <param name="segment_value">test</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/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/table/PooledTableTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/table/PooledTableTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idgen/enhanced/table/PooledTableTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,65 @@
+package org.hibernate.test.idgen.enhanced.table;
+
+import junit.framework.Test;
+
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.id.enhanced.OptimizerFactory;
+import org.hibernate.id.enhanced.TableGenerator;
+import org.hibernate.Session;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class PooledTableTest extends FunctionalTestCase {
+ 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( TableGenerator.class, persister.getIdentifierGenerator().getClass() );
+ TableGenerator generator = ( TableGenerator ) 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.getTableAccessCount() ); // 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.getTableAccessCount() ); // 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/testsuite/src/test/java/org/hibernate/test/idprops/IdentifierPropertyReferencesTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idprops/IdentifierPropertyReferencesTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idprops/IdentifierPropertyReferencesTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,187 @@
+package org.hibernate.test.idprops;
+
+import junit.framework.Test;
+
+import org.hibernate.Criteria;
+import org.hibernate.Query;
+import org.hibernate.Session;
+import org.hibernate.criterion.Projections;
+import org.hibernate.criterion.Restrictions;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class IdentifierPropertyReferencesTest extends FunctionalTestCase {
+ public IdentifierPropertyReferencesTest(String name) {
+ super( name );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "idprops/Mapping.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( IdentifierPropertyReferencesTest.class );
+ }
+
+ public void testHqlIdPropertyReferences() {
+ Session s = openSession();
+ s.beginTransaction();
+ Person p = new Person( new Long(1), "steve", 123 );
+ s.save( p );
+ Order o = new Order( new Long(1), p );
+ LineItem l = new LineItem( o, "my-product", 2 );
+ l.setId( "456" );
+ s.save( o );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+
+ long count = extractCount( s, "select count(*) from Person p where p.id = 123" );
+ assertEquals( "Person by id prop (non-identifier)", 1, count );
+ count = extractCount( s, "select count(*) from Person p where p.pk = 1" );
+ assertEquals( "Person by pk prop (identifier)", 1, count );
+
+ count = extractCount( s, "select count(*) from Order o where o.id = 1" );
+ assertEquals( "Order by number prop (named identifier)", 1, count );
+ count = extractCount( s, "select count(*) from Order o where o.number = 1" );
+ assertEquals( "Order by id prop (virtual identifier)", 1, count );
+
+ count = extractCount( s, "select count(*) from LineItem l where l.id = '456'" );
+ assertEquals( "LineItem by id prop (non-identifier", 1, count );
+
+ if ( getDialect().supportsRowValueConstructorSyntax() ) {
+ Query q = s.createQuery( "select count(*) from LineItem l where l.pk = (:order, :product)" )
+ .setEntity( "order", o )
+ .setString( "product", "my-product" );
+ count = extractCount( q );
+ assertEquals( "LineItem by pk prop (named composite identifier", 1, count );
+ }
+
+ count = extractCount( s, "select count(*) from Order o where o.orderee.id = 1" );
+ assertEquals( 0, count );
+ count = extractCount( s, "select count(*) from Order o where o.orderee.pk = 1" );
+ assertEquals( 1, count );
+ count = extractCount( s, "select count(*) from Order o where o.orderee.id = 123" );
+ assertEquals( 1, count );
+
+ count = extractCount( s, "select count(*) from LineItem l where l.pk.order.id = 1" );
+ assertEquals( 1, count );
+ count = extractCount( s, "select count(*) from LineItem l where l.pk.order.number = 1" );
+ assertEquals( 1, count );
+ count = extractCount( s, "select count(*) from LineItem l where l.pk.order.orderee.pk = 1" );
+ assertEquals( 1, count );
+
+ s.delete( o );
+ s.delete( p );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testCriteriaIdPropertyReferences() {
+ Session s = openSession();
+ s.beginTransaction();
+ Person p = new Person( new Long(1), "steve", 123 );
+ s.save( p );
+ Order o = new Order( new Long(1), p );
+ LineItem l = new LineItem( o, "my-product", 2 );
+ l.setId( "456" );
+ s.save( o );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+
+ Criteria crit = s.createCriteria( Person.class );
+ crit.setProjection( Projections.rowCount() );
+ crit.add( Restrictions.eq( "id", new Integer(123) ) );
+ long count = extractCount( crit );
+ assertEquals( "Person by id prop (non-identifier)", 1, count );
+
+ crit = s.createCriteria( Person.class );
+ crit.setProjection( Projections.rowCount() );
+ crit.add( Restrictions.eq( "pk", new Long(1) ) );
+ count = extractCount( crit );
+ assertEquals( "Person by pk prop (identifier)", 1, count );
+
+ crit = s.createCriteria( Order.class );
+ crit.setProjection( Projections.rowCount() );
+ crit.add( Restrictions.eq( "number", new Long(1) ) );
+ count = extractCount( crit );
+ assertEquals( "Order by number prop (named identifier)", 1, count );
+
+ crit = s.createCriteria( Order.class );
+ crit.setProjection( Projections.rowCount() );
+ crit.add( Restrictions.eq( "id", new Long(1) ) );
+ count = extractCount( crit );
+ assertEquals( "Order by id prop (virtual identifier)", 1, count );
+
+ crit = s.createCriteria( LineItem.class );
+ crit.setProjection( Projections.rowCount() );
+ crit.add( Restrictions.eq( "id", "456" ) );
+ count = extractCount( crit );
+ assertEquals( "LineItem by id prop (non-identifier", 1, count );
+
+ if ( getDialect().supportsRowValueConstructorSyntax() ) {
+ crit = s.createCriteria( LineItem.class );
+ crit.setProjection( Projections.rowCount() );
+ crit.add( Restrictions.eq( "pk", new LineItemPK( o, "my-product" ) ) );
+ count = extractCount( crit );
+ assertEquals( "LineItem by pk prop (named composite identifier)", 1, count );
+ }
+
+ crit = s.createCriteria( Order.class );
+ crit.setProjection( Projections.rowCount() );
+ crit.createAlias( "orderee", "p" ).add( Restrictions.eq( "p.id", new Integer(1) ) );
+ count = extractCount( crit );
+ assertEquals( 0, count );
+
+ crit = s.createCriteria( Order.class );
+ crit.setProjection( Projections.rowCount() );
+ crit.createAlias( "orderee", "p" ).add( Restrictions.eq( "p.pk", new Long(1) ) );
+ count = extractCount( crit );
+ assertEquals( 1, count );
+
+ crit = s.createCriteria( Order.class );
+ crit.setProjection( Projections.rowCount() );
+ crit.createAlias( "orderee", "p" ).add( Restrictions.eq( "p.id", new Integer(123) ) );
+ count = extractCount( crit );
+ assertEquals( 1, count );
+
+ crit = s.createCriteria( LineItem.class );
+ crit.setProjection( Projections.rowCount() );
+ crit.add( Restrictions.eq( "pk.order.id", new Long(1) ) );
+ count = extractCount( crit );
+ assertEquals( 1, count );
+
+ crit = s.createCriteria( LineItem.class );
+ crit.setProjection( Projections.rowCount() );
+ crit.add( Restrictions.eq( "pk.order.number", new Long(1) ) );
+ count = extractCount( crit );
+ assertEquals( 1, count );
+
+ s.delete( o );
+ s.delete( p );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ private long extractCount(Session s, String hql) {
+ return extractCount( s.createQuery( hql ) );
+ }
+
+ private long extractCount(Query query) {
+ return ( ( Long ) query.list().get( 0 ) ).longValue();
+ }
+
+ private long extractCount(Criteria crit) {
+ return ( ( Integer ) crit.list().get( 0 ) ).intValue();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idprops/LineItem.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idprops/LineItem.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idprops/LineItem.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,49 @@
+package org.hibernate.test.idprops;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class LineItem {
+ private LineItemPK pk;
+ private int quantity;
+ private String id;
+
+ public LineItem() {
+ }
+
+ public LineItem(LineItemPK pk, int quantity) {
+ this.pk = pk;
+ this.quantity = quantity;
+ this.pk.getOrder().getLineItems().add( this );
+ }
+
+ public LineItem(Order order, String productCode, int quantity) {
+ this( new LineItemPK( order, productCode ), quantity );
+ }
+
+ public LineItemPK getPk() {
+ return pk;
+ }
+
+ public void setPk(LineItemPK pk) {
+ this.pk = pk;
+ }
+
+ public int getQuantity() {
+ return quantity;
+ }
+
+ public void setQuantity(int quantity) {
+ this.quantity = quantity;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idprops/LineItemPK.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idprops/LineItemPK.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idprops/LineItemPK.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,64 @@
+package org.hibernate.test.idprops;
+
+import java.io.Serializable;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class LineItemPK implements Serializable {
+ private Order order;
+ private String productCode;
+
+ public LineItemPK() {
+ }
+
+ public LineItemPK(Order order, String productCode) {
+ this.order = order;
+ this.productCode = productCode;
+ }
+
+ public Order getOrder() {
+ return order;
+ }
+
+ public void setOrder(Order order) {
+ this.order = order;
+ }
+
+ public String getProductCode() {
+ return productCode;
+ }
+
+ public void setProductCode(String productCode) {
+ this.productCode = productCode;
+ }
+
+ public boolean equals(Object o) {
+ if ( this == o ) {
+ return true;
+ }
+ if ( o == null || getClass() != o.getClass() ) {
+ return false;
+ }
+
+ LineItemPK that = ( LineItemPK ) o;
+
+ if ( !order.equals( that.order ) ) {
+ return false;
+ }
+ if ( !productCode.equals( that.productCode ) ) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public int hashCode() {
+ int result;
+ result = order.hashCode();
+ result = 31 * result + productCode.hashCode();
+ return result;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idprops/Mapping.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idprops/Mapping.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idprops/Mapping.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,48 @@
+<?xml version="1.0"?>
+
+<!DOCTYPE hibernate-mapping SYSTEM "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
+
+<hibernate-mapping package="org.hibernate.test.idprops">
+
+ <!--
+ Person has an identitifer property named something other than 'id';
+ additionally, it has a non-identitifer property named 'id'
+ -->
+ <class name="Person" table="T_ID_PERSON">
+ <id name="pk" column="PK" type="long">
+ <generator class="assigned"/>
+ </id>
+ <property name="name" column="NAME" type="string"/>
+ <property name="id" column="ID_NON_ID" type="int" />
+ </class>
+
+ <!--
+ Order has an identitifer property named something other than 'id';
+ it has no non-identitifer property named 'id'
+ -->
+ <class name="Order" table="T_ID_ORDER">
+ <id name="number" column="PK" type="long">
+ <generator class="assigned"/>
+ </id>
+ <property name="placed" column="ORDR_DT" type="timestamp"/>
+ <many-to-one name="orderee" class="Person" column="ORDEREE"/>
+ <set name="lineItems" cascade="all-delete-orphan" lazy="true" inverse="true">
+ <key column="ORDR_ID"/>
+ <one-to-many class="LineItem"/>
+ </set>
+ </class>
+
+ <!--
+ LineItem has a composite identitifer property named something other than 'id';
+ additionally, it has a non-identitifer property named 'id'
+ -->
+ <class name="LineItem" table="T_ID_LINE_ITEM">
+ <composite-id class="LineItemPK" name="pk">
+ <key-many-to-one name="order" class="Order" column="ORDR_ID" />
+ <key-property name="productCode" column="PROD_CODE" type="string"/>
+ </composite-id>
+ <property name="quantity" column="QTY" type="int"/>
+ <property name="id" column="ID_NON_ID2" type="string"/>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idprops/Order.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idprops/Order.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idprops/Order.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,60 @@
+package org.hibernate.test.idprops;
+
+import java.util.Date;
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class Order {
+ private Long number;
+ private Date placed;
+ private Person orderee;
+
+ private Set lineItems = new HashSet();
+
+ public Order() {
+ }
+
+ public Order(Long number, Person orderee) {
+ this.number = number;
+ this.orderee = orderee;
+ this.placed = new Date();
+ }
+
+ public Long getNumber() {
+ return number;
+ }
+
+ public void setNumber(Long number) {
+ this.number = number;
+ }
+
+ public Date getPlaced() {
+ return placed;
+ }
+
+ public void setPlaced(Date placed) {
+ this.placed = placed;
+ }
+
+ public Person getOrderee() {
+ return orderee;
+ }
+
+ public void setOrderee(Person orderee) {
+ this.orderee = orderee;
+ }
+
+
+ public Set getLineItems() {
+ return lineItems;
+ }
+
+ public void setLineItems(Set lineItems) {
+ this.lineItems = lineItems;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idprops/Person.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idprops/Person.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/idprops/Person.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,45 @@
+package org.hibernate.test.idprops;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class Person {
+ private Long pk;
+ private String name;
+ private int id;
+
+ public Person() {
+ }
+
+ public Person(Long pk, String name, int id) {
+ this.pk = pk;
+ this.name = name;
+ this.id = id;
+ }
+
+ public Long getPk() {
+ return pk;
+ }
+
+ public void setPk(Long pk) {
+ this.pk = pk;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/immutable/Contract.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/immutable/Contract.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/immutable/Contract.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,57 @@
+//$Id: Contract.java 7222 2005-06-19 17:22:01Z oneovthafew $
+package org.hibernate.test.immutable;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+public class Contract implements Serializable {
+
+ private long id;
+ private String customerName;
+ private String type;
+ private List variations;
+
+ public Contract() {
+ super();
+ }
+
+ public Contract(String customerName, String type) {
+ this.customerName = customerName;
+ this.type = type;
+ variations = new ArrayList();
+ }
+
+ public String getCustomerName() {
+ return customerName;
+ }
+
+ public void setCustomerName(String customerName) {
+ this.customerName = customerName;
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public List getVariations() {
+ return variations;
+ }
+
+ public void setVariations(List variations) {
+ this.variations = variations;
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/immutable/Contract.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/immutable/ContractVariation.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/immutable/ContractVariation.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/immutable/ContractVariation.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,35 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+ Test for immutable classes/collections.
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.immutable">
+
+ <class name="Contract" mutable="false">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="customerName" not-null="true"/>
+ <property name="type" not-null="true"/>
+ <bag name="variations" inverse="true" order-by="version asc"
+ mutable="false" cascade="all" fetch="join">
+ <key column="contract"/>
+ <one-to-many class="ContractVariation"/>
+ </bag>
+ </class>
+
+ <class name="ContractVariation" mutable="false">
+ <composite-id>
+ <key-many-to-one name="contract"/>
+ <key-property name="version"/>
+ </composite-id>
+ <property name="text" type="text"/>
+ </class>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/immutable/ContractVariation.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/immutable/ContractVariation.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/immutable/ContractVariation.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/immutable/ContractVariation.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,45 @@
+//$Id: ContractVariation.java 7222 2005-06-19 17:22:01Z oneovthafew $
+package org.hibernate.test.immutable;
+
+import java.io.Serializable;
+
+public class ContractVariation implements Serializable {
+
+ private int version;
+ private Contract contract;
+ private String text;
+
+ public Contract getContract() {
+ return contract;
+ }
+
+ public void setContract(Contract contract) {
+ this.contract = contract;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ public int getVersion() {
+ return version;
+ }
+
+ public void setVersion(int version) {
+ this.version = version;
+ }
+
+ public ContractVariation() {
+ super();
+ }
+
+ public ContractVariation(int version, Contract contract) {
+ this.contract = contract;
+ this.version = version;
+ contract.getVariations().add(this);
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/immutable/ContractVariation.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/immutable/ImmutableTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/immutable/ImmutableTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/immutable/ImmutableTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,66 @@
+//$Id: ImmutableTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.immutable;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.criterion.Projections;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class ImmutableTest extends FunctionalTestCase {
+
+ public ImmutableTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "immutable/ContractVariation.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( ImmutableTest.class );
+ }
+
+ public void testImmutable() {
+ Contract c = new Contract("gavin", "phone");
+ ContractVariation cv1 = new ContractVariation(1, c);
+ cv1.setText("expensive");
+ ContractVariation cv2 = new ContractVariation(2, c);
+ cv2.setText("more expensive");
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.persist(c);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Contract) s.createCriteria(Contract.class).uniqueResult();
+ c.setCustomerName("foo bar");
+ c.getVariations().add( new ContractVariation(3, c) );
+ cv1 = (ContractVariation) c.getVariations().iterator().next();
+ cv1.setText("blah blah");
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Contract) s.createCriteria(Contract.class).uniqueResult();
+ assertEquals( c.getCustomerName(), "gavin" );
+ assertEquals( c.getVariations().size(), 2 );
+ cv1 = (ContractVariation) c.getVariations().iterator().next();
+ assertEquals( cv1.getText(), "expensive" );
+ s.delete(c);
+ assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) );
+ t.commit();
+ s.close();
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/immutable/ImmutableTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/insertordering/Group.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/insertordering/Group.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/insertordering/Group.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,29 @@
+package org.hibernate.test.insertordering;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class Group {
+ private Long id;
+ private String name;
+
+ /**
+ * for persistence
+ */
+ Group() {
+ }
+
+ public Group(String name) {
+ this.name = name;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/insertordering/InsertOrderingTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/insertordering/InsertOrderingTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/insertordering/InsertOrderingTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,127 @@
+package org.hibernate.test.insertordering;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.sql.SQLException;
+import java.sql.PreparedStatement;
+
+import junit.framework.Test;
+
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.Session;
+import org.hibernate.Interceptor;
+import org.hibernate.HibernateException;
+import org.hibernate.jdbc.BatchingBatcher;
+import org.hibernate.jdbc.ConnectionManager;
+import org.hibernate.jdbc.Expectation;
+import org.hibernate.jdbc.BatcherFactory;
+import org.hibernate.jdbc.Batcher;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class InsertOrderingTest extends FunctionalTestCase {
+ public InsertOrderingTest(String string) {
+ super( string );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( InsertOrderingTest.class );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "insertordering/Mapping.hbm.xml" };
+ }
+
+ public void configure(Configuration cfg) {
+ super.configure( cfg );
+ cfg.setProperty( Environment.ORDER_INSERTS, "true" );
+ cfg.setProperty( Environment.STATEMENT_BATCH_SIZE, "10" );
+ cfg.setProperty( Environment.BATCH_STRATEGY, StatsBatcherFactory.class.getName() );
+ }
+
+ public void testBatchOrdering() {
+ Session s = openSession();
+ s.beginTransaction();
+ int iterations = 12;
+ for ( int i = 0; i < iterations; i++ ) {
+ User user = new User( "user-" + i );
+ Group group = new Group( "group-" + i );
+ s.save( user );
+ s.save( group );
+ user.addMembership( group );
+ }
+ StatsBatcher.reset();
+ s.getTransaction().commit();
+ s.close();
+
+ assertEquals( 6, StatsBatcher.batchSizes.size() ); // 2 batches of each insert statement
+
+ s = openSession();
+ s.beginTransaction();
+ Iterator users = s.createQuery( "from User u left join fetch u.memberships m left join fetch m.group" ).list().iterator();
+ while ( users.hasNext() ) {
+ s.delete( users.next() );
+ }
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public static class Counter {
+ public int count = 0;
+ }
+
+ public static class StatsBatcher extends BatchingBatcher {
+ private static String batchSQL;
+ private static List batchSizes = new ArrayList();
+ private static int currentBatch = -1;
+
+ public StatsBatcher(ConnectionManager connectionManager, Interceptor interceptor) {
+ super( connectionManager, interceptor );
+ }
+
+ static void reset() {
+ batchSizes = new ArrayList();
+ currentBatch = -1;
+ batchSQL = null;
+ }
+
+ public PreparedStatement prepareBatchStatement(String sql) throws SQLException {
+ PreparedStatement rtn = super.prepareBatchStatement( sql );
+ if ( batchSQL == null || !batchSQL.equals( sql ) ) {
+ currentBatch++;
+ batchSQL = sql;
+ batchSizes.add( currentBatch, new Counter() );
+ System.out.println( "--------------------------------------------------------" );
+ System.out.println( "Preparing statement [" + sql + "]" );
+ }
+ return rtn;
+ }
+
+ public void addToBatch(Expectation expectation) throws SQLException, HibernateException {
+ Counter counter = ( Counter ) batchSizes.get( currentBatch );
+ counter.count++;
+ System.out.println( "Adding to batch [" + batchSQL + "]" );
+ super.addToBatch( expectation );
+ }
+
+ protected void doExecuteBatch(PreparedStatement ps) throws SQLException, HibernateException {
+ System.out.println( "executing batch [" + batchSQL + "]" );
+ System.out.println( "--------------------------------------------------------" );
+ batchSQL = null;
+ super.doExecuteBatch( ps );
+ }
+ }
+
+ public static class StatsBatcherFactory implements BatcherFactory {
+ public Batcher createBatcher(ConnectionManager connectionManager, Interceptor interceptor) {
+ return new StatsBatcher( connectionManager, interceptor );
+ }
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/insertordering/Mapping.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/insertordering/Mapping.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/insertordering/Mapping.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,35 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.insertordering" default-access="field">
+
+ <class name="User" table="INS_ORD_USR">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="username" column="USR_NM" />
+ <set name="memberships" fetch="select" lazy="true" inverse="true" cascade="all">
+ <key column="USR_ID"/>
+ <one-to-many class="Membership"/>
+ </set>
+ </class>
+
+ <class name="Group" table="INS_ORD_GRP">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+ <class name="Membership" table="INS_ORD_MEM">
+ <id name="id">
+ <generator class="increment" />
+ </id>
+ <many-to-one name="user" class="User" column="USR_ID" cascade="all"/>
+ <many-to-one name="group" class="Group" column="GRP_ID" cascade="all"/>
+ <property name="activationDate" type="timestamp" column="JN_DT"/>
+ </class>
+</hibernate-mapping>
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/insertordering/Membership.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/insertordering/Membership.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/insertordering/Membership.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,47 @@
+package org.hibernate.test.insertordering;
+
+import java.util.Date;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class Membership {
+ private Long id;
+ private User user;
+ private Group group;
+ private Date activationDate;
+
+ /**
+ * For persistence
+ */
+ Membership() {
+ }
+
+ public Membership(User user, Group group) {
+ this( user, group, new Date() );
+ }
+
+ public Membership(User user, Group group, Date activationDate) {
+ this.user = user;
+ this.group = group;
+ this.activationDate = activationDate;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public User getUser() {
+ return user;
+ }
+
+ public Group getGroup() {
+ return group;
+ }
+
+ public Date getActivationDate() {
+ return activationDate;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/insertordering/User.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/insertordering/User.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/insertordering/User.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,44 @@
+package org.hibernate.test.insertordering;
+
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Iterator;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class User {
+ private Long id;
+ private String username;
+ private Set memberships = new HashSet();
+
+ /**
+ * for persistence
+ */
+ User() {
+ }
+
+ public User(String username) {
+ this.username = username;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public Iterator getMemberships() {
+ return memberships.iterator();
+ }
+
+ public Membership addMembership(Group group) {
+ Membership membership = new Membership( this, group );
+ memberships.add( membership );
+ return membership;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/buildtime/InstrumentTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/buildtime/InstrumentTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/buildtime/InstrumentTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,88 @@
+//$Id: InstrumentTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.instrument.buildtime;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.hibernate.intercept.FieldInterceptionHelper;
+import org.hibernate.test.instrument.domain.Document;
+import org.hibernate.test.instrument.cases.TestDirtyCheckExecutable;
+import org.hibernate.test.instrument.cases.TestFetchAllExecutable;
+import org.hibernate.test.instrument.cases.TestLazyExecutable;
+import org.hibernate.test.instrument.cases.TestLazyManyToOneExecutable;
+import org.hibernate.test.instrument.cases.TestInjectFieldInterceptorExecutable;
+import org.hibernate.test.instrument.cases.TestIsPropertyInitializedExecutable;
+import org.hibernate.test.instrument.cases.TestLazyPropertyCustomTypeExecutable;
+import org.hibernate.test.instrument.cases.TestManyToOneProxyExecutable;
+import org.hibernate.test.instrument.cases.Executable;
+import org.hibernate.junit.UnitTestCase;
+
+/**
+ * @author Gavin King
+ */
+public class InstrumentTest extends UnitTestCase {
+
+ public InstrumentTest(String str) {
+ super(str);
+ }
+
+ public static Test suite() {
+ return new TestSuite( InstrumentTest.class );
+ }
+
+ public void testDirtyCheck() {
+ execute( new TestDirtyCheckExecutable() );
+ }
+
+ public void testFetchAll() throws Exception {
+ execute( new TestFetchAllExecutable() );
+ }
+
+ public void testLazy() throws Exception {
+ execute( new TestLazyExecutable() );
+ }
+
+ public void testLazyManyToOne() {
+ execute( new TestLazyManyToOneExecutable() );
+ }
+
+ public void testSetFieldInterceptor() {
+ execute( new TestInjectFieldInterceptorExecutable() );
+ }
+
+ public void testPropertyInitialized() {
+ execute( new TestIsPropertyInitializedExecutable() );
+ }
+
+ public void testManyToOneProxy() {
+ execute( new TestManyToOneProxyExecutable() );
+ }
+
+ public void testLazyPropertyCustomTypeExecutable() {
+ execute( new TestLazyPropertyCustomTypeExecutable() );
+ }
+
+ private void execute(Executable executable) {
+ executable.prepare();
+ try {
+ executable.execute();
+ }
+ finally {
+ executable.complete();
+ }
+ }
+
+ protected void runTest() throws Throwable {
+ if ( isRunnable() ) {
+ super.runTest();
+ }
+ else {
+ reportSkip( "domain classes not instrumented", "build-time instrumentation" );
+ }
+ }
+
+ public static boolean isRunnable() {
+ return FieldInterceptionHelper.isInstrumented( new Document() );
+ }
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/buildtime/InstrumentTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/AbstractExecutable.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/AbstractExecutable.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/AbstractExecutable.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,42 @@
+package org.hibernate.test.instrument.cases;
+
+import org.hibernate.SessionFactory;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+
+/**
+ * @author Steve Ebersole
+ */
+public abstract class AbstractExecutable implements Executable {
+
+ private SessionFactory factory;
+
+ public final void prepare() {
+ Configuration cfg = new Configuration().setProperty( Environment.HBM2DDL_AUTO, "create-drop" );
+ String[] resources = getResources();
+ for ( int i = 0; i < resources.length; i++ ) {
+ cfg.addResource( resources[i] );
+ }
+ factory = cfg.buildSessionFactory();
+ }
+
+ public final void complete() {
+ try {
+ cleanup();
+ }
+ finally {
+ factory.close();
+ }
+ }
+
+ protected SessionFactory getFactory() {
+ return factory;
+ }
+
+ protected void cleanup() {
+ }
+
+ protected String[] getResources() {
+ return new String[] { "org/hibernate/test/instrument/domain/Documents.hbm.xml" };
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/Executable.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/Executable.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/Executable.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,10 @@
+package org.hibernate.test.instrument.cases;
+
+/**
+ * @author Steve Ebersole
+ */
+public interface Executable {
+ public void prepare();
+ public void execute();
+ public void complete();
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/TestDirtyCheckExecutable.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/TestDirtyCheckExecutable.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/TestDirtyCheckExecutable.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,49 @@
+package org.hibernate.test.instrument.cases;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.test.instrument.domain.Folder;
+
+import java.util.List;
+import java.util.Iterator;
+
+import junit.framework.Assert;
+
+/**
+ * @author Steve Ebersole
+ */
+public class TestDirtyCheckExecutable extends AbstractExecutable {
+ public void execute() {
+ Session s = getFactory().openSession();
+ Transaction t = s.beginTransaction();
+ Folder pics = new Folder();
+ pics.setName("pics");
+ Folder docs = new Folder();
+ docs.setName("docs");
+ s.persist(docs);
+ s.persist(pics);
+ t.commit();
+ s.close();
+
+ s = getFactory().openSession();
+ t = s.beginTransaction();
+ List list = s.createCriteria(Folder.class).list();
+ for ( Iterator iter = list.iterator(); iter.hasNext(); ) {
+ Folder f = (Folder) iter.next();
+ Assert.assertFalse( f.nameWasread );
+ }
+ t.commit();
+ s.close();
+
+ for ( Iterator iter = list.iterator(); iter.hasNext(); ) {
+ Folder f = (Folder) iter.next();
+ Assert.assertFalse( f.nameWasread );
+ }
+
+ s = getFactory().openSession();
+ t = s.beginTransaction();
+ s.createQuery("delete from Folder").executeUpdate();
+ t.commit();
+ s.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/TestFetchAllExecutable.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/TestFetchAllExecutable.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/TestFetchAllExecutable.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,47 @@
+package org.hibernate.test.instrument.cases;
+
+import org.hibernate.Session;
+import org.hibernate.Hibernate;
+import org.hibernate.Transaction;
+import org.hibernate.test.instrument.domain.Owner;
+import org.hibernate.test.instrument.domain.Document;
+import org.hibernate.test.instrument.domain.Folder;
+import junit.framework.Assert;
+
+/**
+ * @author Steve Ebersole
+ */
+public class TestFetchAllExecutable extends AbstractExecutable {
+ public void execute() {
+ Session s = getFactory().openSession();
+ Transaction t = s.beginTransaction();
+ Owner o = new Owner();
+ Document doc = new Document();
+ Folder fol = new Folder();
+ o.setName("gavin");
+ doc.setName("Hibernate in Action");
+ doc.setSummary("blah");
+ doc.updateText("blah blah");
+ fol.setName("books");
+ doc.setOwner(o);
+ doc.setFolder(fol);
+ fol.getDocuments().add(doc);
+ s.persist(o);
+ s.persist(fol);
+ t.commit();
+ s.close();
+
+ s = getFactory().openSession();
+ t = s.beginTransaction();
+ doc = (Document) s.createQuery("from Document fetch all properties").uniqueResult();
+ Assert.assertTrue( Hibernate.isPropertyInitialized( doc, "summary" ) );
+ Assert.assertTrue( Hibernate.isPropertyInitialized( doc, "upperCaseName" ) );
+ Assert.assertTrue( Hibernate.isPropertyInitialized( doc, "owner" ) );
+ Assert.assertEquals( doc.getSummary(), "blah" );
+ s.delete(doc);
+ s.delete( doc.getOwner() );
+ s.delete( doc.getFolder() );
+ t.commit();
+ s.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/TestInjectFieldInterceptorExecutable.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/TestInjectFieldInterceptorExecutable.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/TestInjectFieldInterceptorExecutable.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,17 @@
+package org.hibernate.test.instrument.cases;
+
+import org.hibernate.test.instrument.domain.Document;
+import org.hibernate.intercept.FieldInterceptionHelper;
+
+import java.util.HashSet;
+
+/**
+ * @author Steve Ebersole
+ */
+public class TestInjectFieldInterceptorExecutable extends AbstractExecutable {
+ public void execute() {
+ Document doc = new Document();
+ FieldInterceptionHelper.injectFieldInterceptor( doc, "Document", new HashSet(), null );
+ doc.getId();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/TestIsPropertyInitializedExecutable.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/TestIsPropertyInitializedExecutable.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/TestIsPropertyInitializedExecutable.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,48 @@
+//$Id: $
+package org.hibernate.test.instrument.cases;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.Hibernate;
+import org.hibernate.test.instrument.domain.Owner;
+import org.hibernate.test.instrument.domain.Document;
+import org.hibernate.test.instrument.domain.Folder;
+import junit.framework.Assert;
+
+/**
+ * @author Steve Ebersole
+ */
+public class TestIsPropertyInitializedExecutable extends AbstractExecutable {
+ public void execute() {
+ Session s = getFactory().openSession();
+ Transaction t = s.beginTransaction();
+ Owner o = new Owner();
+ Document doc = new Document();
+ Folder fol = new Folder();
+ o.setName("gavin");
+ doc.setName("Hibernate in Action");
+ doc.setSummary("blah");
+ doc.updateText("blah blah");
+ fol.setName("books");
+ doc.setOwner(o);
+ doc.setFolder(fol);
+ fol.getDocuments().add(doc);
+ Assert.assertTrue( Hibernate.isPropertyInitialized( doc, "summary" ) );
+ s.persist(o);
+ s.persist(fol);
+ t.commit();
+ s.close();
+
+ s = getFactory().openSession();
+ t = s.beginTransaction();
+ doc = (Document) s.get( Document.class, doc.getId() );
+ Assert.assertFalse( Hibernate.isPropertyInitialized( doc, "summary" ) );
+ Assert.assertFalse( Hibernate.isPropertyInitialized( doc, "upperCaseName" ) );
+ Assert.assertFalse( Hibernate.isPropertyInitialized( doc, "owner" ) );
+ s.delete(doc);
+ s.delete( doc.getOwner() );
+ s.delete( doc.getFolder() );
+ t.commit();
+ s.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/TestLazyExecutable.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/TestLazyExecutable.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/TestLazyExecutable.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,197 @@
+package org.hibernate.test.instrument.cases;
+
+import org.hibernate.test.TestCase;
+import org.hibernate.test.instrument.cases.AbstractExecutable;
+import org.hibernate.test.instrument.domain.Owner;
+import org.hibernate.test.instrument.domain.Document;
+import org.hibernate.test.instrument.domain.Folder;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.Hibernate;
+import org.hibernate.LockMode;
+import org.hibernate.CacheMode;
+import org.hibernate.SessionFactory;
+
+/**
+ * @author Steve Ebersole
+ */
+public class TestLazyExecutable extends AbstractExecutable {
+ public void execute() {
+ SessionFactory factory = getFactory();
+ Session s = factory.openSession();
+ Transaction t = s.beginTransaction();
+ Owner o = new Owner();
+ Document doc = new Document();
+ Folder fol = new Folder();
+ o.setName("gavin");
+ doc.setName("Hibernate in Action");
+ doc.setSummary("blah");
+ doc.updateText("blah blah");
+ fol.setName("books");
+ doc.setOwner(o);
+ doc.setFolder(fol);
+ fol.getDocuments().add(doc);
+ s.save(o);
+ s.save(fol);
+ t.commit();
+ s.close();
+
+ s = factory.openSession();
+ s.setCacheMode( CacheMode.IGNORE );
+ t = s.beginTransaction();
+ doc = ( Document ) s.get( Document.class, doc.getId() );
+ TestCase.assertTrue( Hibernate.isPropertyInitialized(doc, "weirdProperty"));
+ TestCase.assertTrue(Hibernate.isPropertyInitialized(doc, "name"));
+ TestCase.assertFalse(Hibernate.isPropertyInitialized(doc, "text"));
+ TestCase.assertFalse(Hibernate.isPropertyInitialized(doc, "upperCaseName"));
+ TestCase.assertFalse(Hibernate.isPropertyInitialized(doc, "folder"));
+ TestCase.assertFalse(Hibernate.isPropertyInitialized(doc, "owner"));
+ doc.getUpperCaseName(); // should force initialization
+ TestCase.assertTrue(Hibernate.isPropertyInitialized(doc, "text"));
+ TestCase.assertTrue(Hibernate.isPropertyInitialized(doc, "weirdProperty"));
+ TestCase.assertTrue(Hibernate.isPropertyInitialized(doc, "upperCaseName"));
+ TestCase.assertTrue(Hibernate.isPropertyInitialized(doc, "folder"));
+ TestCase.assertTrue(Hibernate.isPropertyInitialized(doc, "owner"));
+ t.commit();
+ s.close();
+
+ s = factory.openSession();
+ s.setCacheMode( CacheMode.IGNORE );
+ t = s.beginTransaction();
+ doc = (Document) s.createQuery("from Document").uniqueResult();
+ doc.getName();
+ TestCase.assertEquals( doc.getText(), "blah blah" );
+ t.commit();
+ s.close();
+
+ s = factory.openSession();
+ s.setCacheMode( CacheMode.IGNORE );
+ t = s.beginTransaction();
+ doc = (Document) s.createQuery("from Document").uniqueResult();
+ doc.getName();
+ TestCase.assertFalse(Hibernate.isPropertyInitialized(doc, "text"));
+ TestCase.assertFalse(Hibernate.isPropertyInitialized(doc, "summary"));
+ TestCase.assertEquals( doc.getText(), "blah blah" );
+ TestCase.assertTrue(Hibernate.isPropertyInitialized(doc, "text"));
+ TestCase.assertTrue(Hibernate.isPropertyInitialized(doc, "summary"));
+ t.commit();
+ s.close();
+
+ s = factory.openSession();
+ s.setCacheMode( CacheMode.IGNORE );
+ t = s.beginTransaction();
+ doc = (Document) s.createQuery("from Document").uniqueResult();
+ doc.setName("HiA");
+ t.commit();
+ s.close();
+
+ s = factory.openSession();
+ s.setCacheMode( CacheMode.IGNORE );
+ t = s.beginTransaction();
+ doc = (Document) s.createQuery("from Document").uniqueResult();
+ TestCase.assertEquals( doc.getName(), "HiA" );
+ TestCase.assertEquals( doc.getText(), "blah blah" );
+ t.commit();
+ s.close();
+
+ s = factory.openSession();
+ s.setCacheMode( CacheMode.IGNORE );
+ t = s.beginTransaction();
+ doc = (Document) s.createQuery("from Document").uniqueResult();
+ doc.getText();
+ doc.setName("HiA second edition");
+ t.commit();
+ s.close();
+
+ s = factory.openSession();
+ s.setCacheMode( CacheMode.IGNORE );
+ t = s.beginTransaction();
+ doc = (Document) s.createQuery("from Document").uniqueResult();
+ TestCase.assertTrue(Hibernate.isPropertyInitialized(doc, "weirdProperty"));
+ TestCase.assertTrue(Hibernate.isPropertyInitialized(doc, "name"));
+ TestCase.assertFalse(Hibernate.isPropertyInitialized(doc, "text"));
+ TestCase.assertFalse(Hibernate.isPropertyInitialized(doc, "upperCaseName"));
+ TestCase.assertFalse(Hibernate.isPropertyInitialized(doc, "owner"));
+ TestCase.assertEquals( doc.getName(), "HiA second edition" );
+ TestCase.assertEquals( doc.getText(), "blah blah" );
+ TestCase.assertEquals( doc.getUpperCaseName(), "HIA SECOND EDITION" );
+ TestCase.assertTrue(Hibernate.isPropertyInitialized(doc, "text"));
+ TestCase.assertTrue(Hibernate.isPropertyInitialized(doc, "weirdProperty"));
+ TestCase.assertTrue(Hibernate.isPropertyInitialized(doc, "upperCaseName"));
+ t.commit();
+ s.close();
+
+ s = factory.openSession();
+ s.setCacheMode( CacheMode.IGNORE );
+ t = s.beginTransaction();
+ doc = (Document) s.createQuery("from Document").uniqueResult();
+ t.commit();
+ s.close();
+
+ TestCase.assertFalse(Hibernate.isPropertyInitialized(doc, "text"));
+
+ s = factory.openSession();
+ s.setCacheMode( CacheMode.IGNORE );
+ t = s.beginTransaction();
+ s.lock(doc, LockMode.NONE);
+ TestCase.assertFalse(Hibernate.isPropertyInitialized(doc, "text"));
+ TestCase.assertEquals( doc.getText(), "blah blah" );
+ TestCase.assertTrue(Hibernate.isPropertyInitialized(doc, "text"));
+ t.commit();
+ s.close();
+
+ s = factory.openSession();
+ s.setCacheMode( CacheMode.IGNORE );
+ t = s.beginTransaction();
+ doc = (Document) s.createQuery("from Document").uniqueResult();
+ t.commit();
+ s.close();
+
+ doc.setName("HiA2");
+
+ TestCase.assertFalse(Hibernate.isPropertyInitialized(doc, "text"));
+
+ s = factory.openSession();
+ s.setCacheMode( CacheMode.IGNORE );
+ t = s.beginTransaction();
+ s.saveOrUpdate(doc);
+ s.flush();
+ TestCase.assertFalse(Hibernate.isPropertyInitialized(doc, "text"));
+ TestCase.assertEquals( doc.getText(), "blah blah" );
+ TestCase.assertTrue(Hibernate.isPropertyInitialized(doc, "text"));
+ doc.updateText("blah blah blah blah");
+ t.commit();
+ s.close();
+
+ s = factory.openSession();
+ s.setCacheMode( CacheMode.IGNORE );
+ t = s.beginTransaction();
+ doc = ( Document ) s.createQuery("from Document").uniqueResult();
+ TestCase.assertEquals( doc.getName(), "HiA2" );
+ TestCase.assertEquals( doc.getText(), "blah blah blah blah" );
+ t.commit();
+ s.close();
+
+ s = factory.openSession();
+ s.setCacheMode( CacheMode.IGNORE );
+ t = s.beginTransaction();
+ doc = (Document) s.load( Document.class, doc.getId() );
+ doc.getName();
+ TestCase.assertFalse(Hibernate.isPropertyInitialized(doc, "text"));
+ TestCase.assertFalse(Hibernate.isPropertyInitialized(doc, "summary"));
+ t.commit();
+ s.close();
+
+ s = factory.openSession();
+ s.setCacheMode( CacheMode.IGNORE );
+ t = s.beginTransaction();
+ doc = (Document) s.createQuery("from Document").uniqueResult();
+ //s.delete(doc);
+ s.delete( doc.getFolder() );
+ s.delete( doc.getOwner() );
+ s.flush();
+ t.commit();
+ s.close();
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/TestLazyManyToOneExecutable.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/TestLazyManyToOneExecutable.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/TestLazyManyToOneExecutable.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,74 @@
+package org.hibernate.test.instrument.cases;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.Hibernate;
+import org.hibernate.test.instrument.domain.Owner;
+import org.hibernate.test.instrument.domain.Document;
+import org.hibernate.test.instrument.domain.Folder;
+import org.hibernate.test.instrument.cases.AbstractExecutable;
+import junit.framework.Assert;
+
+/**
+ * @author Steve Ebersole
+ */
+public class TestLazyManyToOneExecutable extends AbstractExecutable {
+ public void execute() {
+ Session s = getFactory().openSession();
+ Transaction t = s.beginTransaction();
+ Owner gavin = new Owner();
+ Document hia = new Document();
+ Folder fol = new Folder();
+ gavin.setName("gavin");
+ hia.setName("Hibernate in Action");
+ hia.setSummary("blah");
+ hia.updateText("blah blah");
+ fol.setName("books");
+ hia.setOwner(gavin);
+ hia.setFolder(fol);
+ fol.getDocuments().add(hia);
+ s.persist(gavin);
+ s.persist(fol);
+ t.commit();
+ s.close();
+
+ s = getFactory().openSession();
+ t = s.beginTransaction();
+ hia = (Document) s.createCriteria(Document.class).uniqueResult();
+ Assert.assertEquals( hia.getFolder().getClass(), Folder.class);
+ fol = hia.getFolder();
+ Assert.assertTrue( Hibernate.isInitialized(fol) );
+ t.commit();
+ s.close();
+
+ s = getFactory().openSession();
+ t = s.beginTransaction();
+ hia = (Document) s.createCriteria(Document.class).uniqueResult();
+ Assert.assertSame( hia.getFolder(), s.load(Folder.class, fol.getId()) );
+ Assert.assertTrue( Hibernate.isInitialized( hia.getFolder() ) );
+ t.commit();
+ s.close();
+
+ s = getFactory().openSession();
+ t = s.beginTransaction();
+ fol = (Folder) s.get(Folder.class, fol.getId());
+ hia = (Document) s.createCriteria(Document.class).uniqueResult();
+ Assert.assertSame( fol, hia.getFolder() );
+ fol = hia.getFolder();
+ Assert.assertTrue( Hibernate.isInitialized(fol) );
+ t.commit();
+ s.close();
+
+ s = getFactory().openSession();
+ t = s.beginTransaction();
+ fol = (Folder) s.load(Folder.class, fol.getId());
+ hia = (Document) s.createCriteria(Document.class).uniqueResult();
+ Assert.assertNotSame( fol, hia.getFolder() );
+ fol = hia.getFolder();
+ Assert.assertTrue( Hibernate.isInitialized(fol) );
+ s.delete(hia.getFolder());
+ s.delete(hia.getOwner());
+ t.commit();
+ s.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/TestLazyPropertyCustomTypeExecutable.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/TestLazyPropertyCustomTypeExecutable.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/TestLazyPropertyCustomTypeExecutable.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,68 @@
+package org.hibernate.test.instrument.cases;
+
+import java.util.Iterator;
+
+import junit.framework.Assert;
+
+import org.hibernate.Session;
+import org.hibernate.intercept.FieldInterceptionHelper;
+import org.hibernate.test.instrument.domain.Problematic;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class TestLazyPropertyCustomTypeExecutable extends AbstractExecutable {
+
+ protected String[] getResources() {
+ return new String[] { "org/hibernate/test/instrument/domain/Problematic.hbm.xml" };
+ }
+
+ public void execute() {
+ Session s = getFactory().openSession();
+ s.beginTransaction();
+ Problematic p = new Problematic();
+ p.setName( "whatever" );
+ p.setBytes( new byte[] { 1, 0, 1, 1, 0 } );
+ s.save( p );
+ s.getTransaction().commit();
+ s.close();
+
+ // this access should be ok because p1 is not a lazy proxy
+ s = getFactory().openSession();
+ s.beginTransaction();
+ Problematic p1 = (Problematic) s.get( Problematic.class, p.getId() );
+ Assert.assertTrue( FieldInterceptionHelper.isInstrumented( p1 ) );
+ p1.getRepresentation();
+ s.getTransaction().commit();
+ s.close();
+
+ s = getFactory().openSession();
+ s.beginTransaction();
+ p1 = (Problematic) s.createQuery( "from Problematic" ).setReadOnly(true ).list().get( 0 );
+ p1.getRepresentation();
+ s.getTransaction().commit();
+ s.close();
+
+ s = getFactory().openSession();
+ s.beginTransaction();
+ p1 = (Problematic) s.load( Problematic.class, p.getId() );
+ Assert.assertFalse( FieldInterceptionHelper.isInstrumented( p1 ) );
+ p1.setRepresentation( p.getRepresentation() );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ protected void cleanup() {
+ Session s = getFactory().openSession();
+ s.beginTransaction();
+ Iterator itr = s.createQuery( "from Problematic" ).list().iterator();
+ while ( itr.hasNext() ) {
+ Problematic p = (Problematic) itr.next();
+ s.delete( p );
+ }
+ s.getTransaction().commit();
+ s.close();
+ }
+}
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/TestManyToOneProxyExecutable.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/TestManyToOneProxyExecutable.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/cases/TestManyToOneProxyExecutable.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,67 @@
+package org.hibernate.test.instrument.cases;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.Hibernate;
+import org.hibernate.test.instrument.domain.Entity;
+import junit.framework.Assert;
+
+/**
+ *
+ * @author Steve Ebersole
+ */
+public class TestManyToOneProxyExecutable extends AbstractExecutable {
+ public void execute() {
+ Session s = getFactory().openSession();
+ Transaction t = s.beginTransaction();
+ Entity root = new Entity( "root" );
+ Entity child1 = new Entity( "child1" );
+ Entity child2 = new Entity( "child2" );
+ root.setChild( child1 );
+ child1.setSibling( child2 );
+ Entity gChild1 = new Entity( "grandchild 1" );
+ Entity gChild2 = new Entity( "grandchild 2" );
+ child1.setChild( gChild1 );
+ gChild1.setSibling( gChild2 );
+ s.save( root );
+ t.commit();
+ s.close();
+
+ // NOTE : child is mapped with lazy="proxy"; sibling with lazy="no-proxy"...
+
+ s = getFactory().openSession();
+ t = s.beginTransaction();
+ // load root
+ root = ( Entity ) s.get( Entity.class, root.getId() );
+ Assert.assertFalse( Hibernate.isPropertyInitialized( root, "name" ) );
+ Assert.assertFalse( Hibernate.isPropertyInitialized( root, "sibling" ) );
+ Assert.assertTrue( Hibernate.isPropertyInitialized( root, "child" ) );
+
+ // get a handle to the child1 proxy reference (and make certain that
+ // this does not force the lazy properties of the root entity
+ // to get initialized.
+ child1 = root.getChild();
+ Assert.assertFalse( Hibernate.isInitialized( child1 ) );
+ Assert.assertFalse( Hibernate.isPropertyInitialized( root, "name" ) );
+ Assert.assertFalse( Hibernate.isPropertyInitialized( root, "sibling" ) );
+ Assert.assertFalse( Hibernate.isPropertyInitialized( child1, "name" ) );
+ Assert.assertFalse( Hibernate.isPropertyInitialized( child1, "sibling" ) );
+ Assert.assertFalse( Hibernate.isPropertyInitialized( child1, "child" ) );
+
+ child1.getName();
+ Assert.assertFalse( Hibernate.isPropertyInitialized( root, "name" ) );
+ Assert.assertFalse( Hibernate.isPropertyInitialized( root, "sibling" ) );
+ Assert.assertTrue( Hibernate.isPropertyInitialized( child1, "name" ) );
+ Assert.assertTrue( Hibernate.isPropertyInitialized( child1, "sibling" ) );
+ Assert.assertTrue( Hibernate.isPropertyInitialized( child1, "child" ) );
+
+ gChild1 = child1.getChild();
+ Assert.assertFalse( Hibernate.isInitialized( gChild1 ) );
+ Assert.assertFalse( Hibernate.isPropertyInitialized( root, "name" ) );
+ Assert.assertFalse( Hibernate.isPropertyInitialized( root, "sibling" ) );
+
+ s.delete( root );
+ t.commit();
+ s.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/domain/CustomBlobType.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/domain/CustomBlobType.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/domain/CustomBlobType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,109 @@
+package org.hibernate.test.instrument.domain;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.Arrays;
+
+import org.hibernate.HibernateException;
+import org.hibernate.Hibernate;
+import org.hibernate.usertype.UserType;
+
+/**
+ * A simple byte[]-based custom type.
+ */
+public class CustomBlobType implements UserType {
+ /**
+ * {@inheritDoc}
+ */
+ public Object nullSafeGet(ResultSet rs, String names[], Object owner) throws SQLException {
+ // cast just to make sure...
+ return ( byte[] ) Hibernate.BINARY.nullSafeGet( rs, names );
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void nullSafeSet(PreparedStatement ps, Object value, int index) throws SQLException, HibernateException {
+ // cast just to make sure...
+ Hibernate.BINARY.nullSafeSet( ps, ( byte[] ) value, index );
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Object deepCopy(Object value) {
+ byte result[] = null;
+
+ if ( value != null ) {
+ byte bytes[] = ( byte[] ) value;
+
+ result = new byte[bytes.length];
+ System.arraycopy( bytes, 0, result, 0, bytes.length );
+ }
+
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isMutable() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int[] sqlTypes() {
+ return new int[] { Types.VARBINARY };
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Class returnedClass() {
+ return byte[].class;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean equals(Object x, Object y) {
+ return Arrays.equals( ( byte[] ) x, ( byte[] ) y );
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Object assemble(Serializable arg0, Object arg1)
+ throws HibernateException {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Serializable disassemble(Object arg0)
+ throws HibernateException {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int hashCode(Object arg0)
+ throws HibernateException {
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Object replace(Object arg0, Object arg1, Object arg2)
+ throws HibernateException {
+ return null;
+ }
+}
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/domain/Document.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/domain/Document.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/domain/Document.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,110 @@
+//$Id: Document.java 9538 2006-03-04 00:17:57Z steve.ebersole at jboss.com $
+package org.hibernate.test.instrument.domain;
+
+import java.util.Date;
+
+/**
+ * @author Gavin King
+ */
+public class Document {
+ private Long id;
+ private String name;
+ private String upperCaseName;
+ private String summary;
+ private String text;
+ private Owner owner;
+ private Folder folder;
+ private Date lastTextModification = new Date();
+ /**
+ * @return Returns the folder.
+ */
+ public Folder getFolder() {
+ return folder;
+ }
+ /**
+ * @param folder The folder to set.
+ */
+ public void setFolder(Folder folder) {
+ this.folder = folder;
+ }
+ /**
+ * @return Returns the owner.
+ */
+ public Owner getOwner() {
+ return owner;
+ }
+ /**
+ * @param owner The owner to set.
+ */
+ public void setOwner(Owner owner) {
+ this.owner = owner;
+ }
+ /**
+ * @return Returns the id.
+ */
+ public Long getId() {
+ return id;
+ }
+ /**
+ * @param id The id to set.
+ */
+ public void setId(Long id) {
+ this.id = id;
+ }
+ /**
+ * @return Returns the name.
+ */
+ public String getName() {
+ return name;
+ }
+ /**
+ * @param name The name to set.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+ /**
+ * @return Returns the summary.
+ */
+ public String getSummary() {
+ return summary;
+ }
+ /**
+ * @param summary The summary to set.
+ */
+ public void setSummary(String summary) {
+ this.summary = summary;
+ }
+ /**
+ * @return Returns the text.
+ */
+ public String getText() {
+ return text;
+ }
+ /**
+ * @param text The text to set.
+ */
+ private void setText(String text) {
+ this.text = text;
+ }
+ /**
+ * @return Returns the upperCaseName.
+ */
+ public String getUpperCaseName() {
+ return upperCaseName;
+ }
+ /**
+ * @param upperCaseName The upperCaseName to set.
+ */
+ public void setUpperCaseName(String upperCaseName) {
+ this.upperCaseName = upperCaseName;
+ }
+
+ public void updateText(String newText) {
+ if ( !newText.equals(text) ) {
+ this.text = newText;
+ lastTextModification = new Date();
+ }
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/domain/Document.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/domain/Documents.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/domain/Documents.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/domain/Documents.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,67 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+ This mapping demonstrates
+
+ (1) use of lazy properties - this feature requires buildtime
+ bytecode instrumentation; we don't think this is a very
+ necessary feature, but provide it for completeleness; if
+ Hibernate encounters uninstrumented classes, lazy property
+ fetching will be silently disabled, to enable testing
+
+ (2) use of a formula to define a "derived property"
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.instrument.domain" default-access="field">
+
+ <class name="Folder" table="folders">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true" length="50"/>
+ <many-to-one name="parent"/>
+ <bag name="subfolders" inverse="true" cascade="save-update">
+ <key column="parent"/>
+ <one-to-many class="Folder"/>
+ </bag>
+ <bag name="documents" inverse="true" cascade="all-delete-orphan">
+ <key column="folder"/>
+ <one-to-many class="Document"/>
+ </bag>
+ </class>
+
+ <class name="Owner" table="owners" lazy="false">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true" length="50"/>
+ </class>
+
+ <class name="Document" table="documents">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true" length="50"/>
+ <property name="upperCaseName" formula="upper(name)" lazy="true"/>
+ <property name="summary" not-null="true" length="200" lazy="true"/>
+ <many-to-one name="folder" not-null="true" lazy="no-proxy"/>
+ <many-to-one name="owner" not-null="true" lazy="no-proxy" fetch="select"/>
+ <property name="text" not-null="true" length="2000" lazy="true"/>
+ <property name="lastTextModification" not-null="true" lazy="true" access="field"/>
+ </class>
+
+ <class name="Entity" table="entity">
+ <id name="id" column="ID" type="long">
+ <generator class="increment"/>
+ </id>
+ <property name="name" column="NAME" type="string" lazy="true"/>
+ <many-to-one name="child" column="PRNT_ID" class="Entity" lazy="proxy" cascade="all" />
+ <many-to-one name="sibling" column="RIGHT_ID" class="Entity" lazy="no-proxy" cascade="all" />
+ </class>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/domain/Documents.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/domain/Entity.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/domain/Entity.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/domain/Entity.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,52 @@
+package org.hibernate.test.instrument.domain;
+
+/**
+ * todo: describe Entity
+ *
+ * @author Steve Ebersole
+ */
+public class Entity {
+ private Long id;
+ private String name;
+ private Entity child;
+ private Entity sibling;
+
+ 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;
+ }
+
+ public Entity getChild() {
+ return child;
+ }
+
+ public void setChild(Entity child) {
+ this.child = child;
+ }
+
+ public Entity getSibling() {
+ return sibling;
+ }
+
+ public void setSibling(Entity sibling) {
+ this.sibling = sibling;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/domain/Folder.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/domain/Folder.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/domain/Folder.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,80 @@
+//$Id: Folder.java 9538 2006-03-04 00:17:57Z steve.ebersole at jboss.com $
+package org.hibernate.test.instrument.domain;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * @author Gavin King
+ */
+public class Folder {
+ private Long id;
+ private String name;
+ private Folder parent;
+ private Collection subfolders = new ArrayList();
+ private Collection documents = new ArrayList();
+
+ public boolean nameWasread;
+
+ /**
+ * @return Returns the id.
+ */
+ public Long getId() {
+ return id;
+ }
+ /**
+ * @param id The id to set.
+ */
+ public void setId(Long id) {
+ this.id = id;
+ }
+ /**
+ * @return Returns the name.
+ */
+ public String getName() {
+ nameWasread = true;
+ return name;
+ }
+ /**
+ * @param name The name to set.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+ /**
+ * @return Returns the documents.
+ */
+ public Collection getDocuments() {
+ return documents;
+ }
+ /**
+ * @param documents The documents to set.
+ */
+ public void setDocuments(Collection documents) {
+ this.documents = documents;
+ }
+ /**
+ * @return Returns the parent.
+ */
+ public Folder getParent() {
+ return parent;
+ }
+ /**
+ * @param parent The parent to set.
+ */
+ public void setParent(Folder parent) {
+ this.parent = parent;
+ }
+ /**
+ * @return Returns the subfolders.
+ */
+ public Collection getSubfolders() {
+ return subfolders;
+ }
+ /**
+ * @param subfolders The subfolders to set.
+ */
+ public void setSubfolders(Collection subfolders) {
+ this.subfolders = subfolders;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/domain/Folder.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/domain/Owner.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/domain/Owner.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/domain/Owner.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,34 @@
+//$Id: Owner.java 9538 2006-03-04 00:17:57Z steve.ebersole at jboss.com $
+package org.hibernate.test.instrument.domain;
+
+/**
+ * @author Gavin King
+ */
+public class Owner {
+ private Long id;
+ private String name;
+ /**
+ * @return Returns the id.
+ */
+ public Long getId() {
+ return id;
+ }
+ /**
+ * @param id The id to set.
+ */
+ public void setId(Long id) {
+ this.id = id;
+ }
+ /**
+ * @return Returns the name.
+ */
+ public String getName() {
+ return name;
+ }
+ /**
+ * @param name The name to set.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/domain/Owner.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/domain/Problematic.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/domain/Problematic.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/domain/Problematic.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.instrument.domain">
+ <class name="Problematic">
+ <id name="id" type="long" column="ID">
+ <generator class="increment" />
+ </id>
+ <property name="name" type="string" column="NAME" />
+ <property name="bytes" type="org.hibernate.test.instrument.domain.CustomBlobType" column="DATA" lazy="true" />
+ </class>
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/domain/Problematic.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/domain/Problematic.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/domain/Problematic.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,69 @@
+package org.hibernate.test.instrument.domain;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class Problematic {
+ private Long id;
+ private String name;
+ private byte[] bytes;
+
+ private Representation representation;
+
+ 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 byte[] getBytes() {
+ return bytes;
+ }
+
+ public void setBytes(byte[] bytes) {
+ this.bytes = bytes;
+ }
+
+ public Representation getRepresentation() {
+ if ( representation == null ) {
+ representation = ( ( bytes == null ) ? null : new Representation( bytes ) );
+ }
+ return representation;
+ }
+
+ public void setRepresentation(Representation rep) {
+ bytes = rep.getBytes();
+ }
+
+ public static class Representation {
+ private byte[] bytes;
+
+ public Representation(byte[] bytes) {
+ this.bytes = bytes;
+ }
+
+ public byte[] getBytes() {
+ return bytes;
+ }
+
+ public String toString() {
+ String result = "";
+ for ( int i = 0; i < bytes.length; i++ ) {
+ result += bytes[i];
+ }
+ return result;
+ }
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/runtime/AbstractTransformingClassLoaderInstrumentTestCase.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/runtime/AbstractTransformingClassLoaderInstrumentTestCase.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/runtime/AbstractTransformingClassLoaderInstrumentTestCase.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,119 @@
+package org.hibernate.test.instrument.runtime;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.hibernate.HibernateException;
+import org.hibernate.bytecode.BytecodeProvider;
+import org.hibernate.bytecode.InstrumentedClassLoader;
+import org.hibernate.bytecode.util.BasicClassFilter;
+import org.hibernate.bytecode.util.FieldFilter;
+import org.hibernate.junit.AbstractClassLoaderIsolatedTestCase;
+
+/**
+ * @author Steve Ebersole
+ */
+public abstract class AbstractTransformingClassLoaderInstrumentTestCase extends AbstractClassLoaderIsolatedTestCase {
+
+ public AbstractTransformingClassLoaderInstrumentTestCase(String string) {
+ super( string );
+ }
+
+ protected ClassLoader buildIsolatedClassLoader(ClassLoader parent) {
+ BytecodeProvider provider = buildBytecodeProvider();
+ return new InstrumentedClassLoader(
+ parent,
+ provider.getTransformer(
+ new BasicClassFilter( new String[] { "org.hibernate.test.instrument" }, null ),
+ new FieldFilter() {
+ public boolean shouldInstrumentField(String className, String fieldName) {
+ return className.startsWith( "org.hibernate.test.instrument.domain" );
+ }
+ public boolean shouldTransformFieldAccess(String transformingClassName, String fieldOwnerClassName, String fieldName) {
+ return fieldOwnerClassName.startsWith( "org.hibernate.test.instrument.domain" )
+ && transformingClassName.equals( fieldOwnerClassName );
+ }
+ }
+ )
+ );
+
+ }
+
+ protected void releaseIsolatedClassLoader(ClassLoader isolatedLoader) {
+ }
+
+ protected abstract BytecodeProvider buildBytecodeProvider();
+
+
+ // the tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public void testSetFieldInterceptor() {
+ executeExecutable( "org.hibernate.test.instrument.cases.TestInjectFieldInterceptorExecutable" );
+ }
+
+ public void testDirtyCheck() {
+ executeExecutable( "org.hibernate.test.instrument.cases.TestDirtyCheckExecutable" );
+ }
+
+ public void testFetchAll() throws Exception {
+ executeExecutable( "org.hibernate.test.instrument.cases.TestFetchAllExecutable" );
+ }
+
+ public void testLazy() {
+ executeExecutable( "org.hibernate.test.instrument.cases.TestLazyExecutable" );
+ }
+
+ public void testLazyManyToOne() {
+ executeExecutable( "org.hibernate.test.instrument.cases.TestLazyManyToOneExecutable" );
+ }
+
+ public void testPropertyInitialized() {
+ executeExecutable( "org.hibernate.test.instrument.cases.TestIsPropertyInitializedExecutable" );
+ }
+
+ public void testManyToOneProxy() {
+ executeExecutable( "org.hibernate.test.instrument.cases.TestManyToOneProxyExecutable" );
+ }
+
+ public void testLazyPropertyCustomType() {
+ executeExecutable( "org.hibernate.test.instrument.cases.TestLazyPropertyCustomTypeExecutable" );
+ }
+
+
+
+ // reflection code to ensure isolation into the created classloader ~~~~~~~
+
+ private static final Class[] SIG = new Class[] {};
+ private static final Object[] ARGS = new Object[] {};
+
+ public void executeExecutable(String name) {
+ Class execClass = null;
+ Object executable = null;
+ try {
+ execClass = Thread.currentThread().getContextClassLoader().loadClass( name );
+ executable = execClass.newInstance();
+ }
+ catch( Throwable t ) {
+ throw new HibernateException( "could not load executable", t );
+ }
+ try {
+ execClass.getMethod( "prepare", SIG ).invoke( executable, ARGS );
+ execClass.getMethod( "execute", SIG ).invoke( executable, ARGS );
+ }
+ catch ( NoSuchMethodException e ) {
+ throw new HibernateException( "could not exeucte executable", e );
+ }
+ catch ( IllegalAccessException e ) {
+ throw new HibernateException( "could not exeucte executable", e );
+ }
+ catch ( InvocationTargetException e ) {
+ throw new HibernateException( "could not exeucte executable", e.getTargetException() );
+ }
+ finally {
+ try {
+ execClass.getMethod( "complete", SIG ).invoke( executable, ARGS );
+ }
+ catch ( Throwable ignore ) {
+ }
+ }
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/runtime/CGLIBInstrumentationTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/runtime/CGLIBInstrumentationTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/runtime/CGLIBInstrumentationTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,51 @@
+package org.hibernate.test.instrument.runtime;
+
+import org.hibernate.bytecode.BytecodeProvider;
+import org.hibernate.bytecode.cglib.BytecodeProviderImpl;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * @author Steve Ebersole
+ */
+public class CGLIBInstrumentationTest extends AbstractTransformingClassLoaderInstrumentTestCase {
+ public CGLIBInstrumentationTest(String string) {
+ super( string );
+ }
+
+ protected BytecodeProvider buildBytecodeProvider() {
+ return new BytecodeProviderImpl();
+ }
+
+ public static Test suite() {
+ return new TestSuite( CGLIBInstrumentationTest.class );
+ }
+
+ public void testSetFieldInterceptor() {
+ super.testSetFieldInterceptor(); //To change body of overridden methods use File | Settings | File Templates.
+ }
+
+ public void testDirtyCheck() {
+ super.testDirtyCheck(); //To change body of overridden methods use File | Settings | File Templates.
+ }
+
+ public void testFetchAll() throws Exception {
+ super.testFetchAll(); //To change body of overridden methods use File | Settings | File Templates.
+ }
+
+ public void testLazy() {
+ super.testLazy(); //To change body of overridden methods use File | Settings | File Templates.
+ }
+
+ public void testLazyManyToOne() {
+ super.testLazyManyToOne(); //To change body of overridden methods use File | Settings | File Templates.
+ }
+
+ public void testPropertyInitialized() {
+ super.testPropertyInitialized(); //To change body of overridden methods use File | Settings | File Templates.
+ }
+
+ public void testManyToOneProxy() {
+ super.testManyToOneProxy(); //To change body of overridden methods use File | Settings | File Templates.
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/runtime/JavassistInstrumentationTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/runtime/JavassistInstrumentationTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/instrument/runtime/JavassistInstrumentationTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,52 @@
+//$Id: $
+package org.hibernate.test.instrument.runtime;
+
+import org.hibernate.bytecode.BytecodeProvider;
+import org.hibernate.bytecode.javassist.BytecodeProviderImpl;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * @author Steve Ebersole
+ */
+public class JavassistInstrumentationTest extends AbstractTransformingClassLoaderInstrumentTestCase {
+ public JavassistInstrumentationTest(String string) {
+ super( string );
+ }
+
+ protected BytecodeProvider buildBytecodeProvider() {
+ return new BytecodeProviderImpl();
+ }
+
+ public static Test suite() {
+ return new TestSuite( JavassistInstrumentationTest.class );
+ }
+
+ public void testSetFieldInterceptor() {
+ super.testSetFieldInterceptor();
+ }
+
+ public void testDirtyCheck() {
+ super.testDirtyCheck();
+ }
+
+ public void testFetchAll() throws Exception {
+ super.testFetchAll();
+ }
+
+ public void testLazy() {
+ super.testLazy();
+ }
+
+ public void testLazyManyToOne() {
+ super.testLazyManyToOne();
+ }
+
+ public void testPropertyInitialized() {
+ super.testPropertyInitialized();
+ }
+
+ public void testManyToOneProxy() {
+ super.testManyToOneProxy();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/CollectionInterceptor.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/CollectionInterceptor.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/CollectionInterceptor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,21 @@
+//$Id: CollectionInterceptor.java 7700 2005-07-30 05:02:47Z oneovthafew $
+package org.hibernate.test.interceptor;
+
+import java.io.Serializable;
+
+import org.hibernate.EmptyInterceptor;
+import org.hibernate.type.Type;
+
+public class CollectionInterceptor extends EmptyInterceptor {
+
+ public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) {
+ ( (User) entity ).getActions().add("updated");
+ return false;
+ }
+
+ public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
+ ( (User) entity ).getActions().add("created");
+ return false;
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/CollectionInterceptor.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/Image.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/Image.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/Image.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
+
+<hibernate-mapping package="org.hibernate.test.interceptor">
+
+ <class name="Image" table="image" abstract="false" select-before-update="true" >
+ <id name="id" type="java.lang.Long" column="id">
+ <generator class="native"/>
+ </id>
+ <component name="details" class="Image$Details">
+ <property name="perm1" not-null="true" type="long" column="permissions"/>
+ <property name="comment" type="string" column="comment_txt"/>
+ </component>
+ <property name="name" type="java.lang.String" column="name" not-null="true"/>
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/Image.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/Image.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/Image.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,63 @@
+package org.hibernate.test.interceptor;
+
+public class Image {
+
+ private Long id;
+ private String name;
+ private Details details;
+
+ public Details getDetails() {
+ return details;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setDetails(Details details) {
+ this.details = details;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String toString() {
+ return "Image/" + ( details == null ? "no details" : details.toString() );
+ }
+
+ public static class Details {
+ private long perm1 = -1; // all bits turned on.
+ private String comment;
+
+ protected long getPerm1() {
+ return this.perm1;
+ }
+
+ protected void setPerm1(long value) {
+ this.perm1 = value;
+ }
+
+ public String getComment() {
+ return comment;
+ }
+
+ public void setComment(String comment) {
+ this.comment = comment;
+ }
+
+ public String toString() {
+ return "Details=" + perm1;
+ }
+ }
+
+}
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/InterceptorTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/InterceptorTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/InterceptorTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,168 @@
+//$Id: InterceptorTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.interceptor;
+
+import java.io.Serializable;
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.hibernate.EmptyInterceptor;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.type.Type;
+
+/**
+ * @author Gavin King
+ */
+public class InterceptorTest extends FunctionalTestCase {
+
+ public InterceptorTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "interceptor/User.hbm.xml", "interceptor/Image.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( InterceptorTest.class );
+ }
+
+ public void testCollectionIntercept() {
+ Session s = openSession( new CollectionInterceptor() );
+ Transaction t = s.beginTransaction();
+ User u = new User("Gavin", "nivag");
+ s.persist(u);
+ u.setPassword("vagni");
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ u = (User) s.get(User.class, "Gavin");
+ assertEquals( 2, u.getActions().size() );
+ s.delete(u);
+ t.commit();
+ s.close();
+ }
+
+ public void testPropertyIntercept() {
+ Session s = openSession( new PropertyInterceptor() );
+ Transaction t = s.beginTransaction();
+ User u = new User("Gavin", "nivag");
+ s.persist(u);
+ u.setPassword("vagni");
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ u = (User) s.get(User.class, "Gavin");
+ assertNotNull( u.getCreated() );
+ assertNotNull( u.getLastUpdated() );
+ s.delete(u);
+ t.commit();
+ s.close();
+ }
+
+ /**
+ * Test case from HHH-1921. Here the interceptor resets the
+ * current-state to the same thing as the current db state; this
+ * causes EntityPersister.findDirty() to return no dirty properties.
+ */
+ public void testPropertyIntercept2() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ User u = new User("Josh", "test");
+ s.persist( u );
+ t.commit();
+ s.close();
+
+ s = openSession(
+ new EmptyInterceptor() {
+ public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) {
+ currentState[0] = "test";
+ return true;
+ }
+ }
+ );
+ t = s.beginTransaction();
+ u = ( User ) s.get( User.class, u.getName() );
+ u.setPassword( "nottest" );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ u = (User) s.get(User.class, "Josh");
+ assertEquals("test", u.getPassword());
+ s.delete(u);
+ t.commit();
+ s.close();
+
+ }
+
+ public void testComponentInterceptor() {
+ final int checkPerm = 500;
+ final String checkComment = "generated from interceptor";
+
+ Session s = openSession(
+ new EmptyInterceptor() {
+ public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
+ if ( state[0] == null ) {
+ Image.Details detail = new Image.Details();
+ detail.setPerm1( checkPerm );
+ detail.setComment( checkComment );
+ state[0] = detail;
+ }
+ return true;
+ }
+ }
+ );
+ s.beginTransaction();
+ Image i = new Image();
+ i.setName( "compincomp" );
+ i = ( Image ) s.merge( i );
+ assertNotNull( i.getDetails() );
+ assertEquals( checkPerm, i.getDetails().getPerm1() );
+ assertEquals( checkComment, i.getDetails().getComment() );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ i = ( Image ) s.get( Image.class, i.getId() );
+ assertNotNull( i.getDetails() );
+ assertEquals( checkPerm, i.getDetails().getPerm1() );
+ assertEquals( checkComment, i.getDetails().getComment() );
+ s.delete( i );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testStatefulIntercept() {
+ final StatefulInterceptor statefulInterceptor = new StatefulInterceptor();
+ Session s = openSession( statefulInterceptor );
+ statefulInterceptor.setSession(s);
+
+ Transaction t = s.beginTransaction();
+ User u = new User("Gavin", "nivag");
+ s.persist(u);
+ u.setPassword("vagni");
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ List logs = s.createCriteria(Log.class).list();
+ assertEquals( 2, logs.size() );
+ s.delete(u);
+ s.createQuery( "delete from Log" ).executeUpdate();
+ t.commit();
+ s.close();
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/InterceptorTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/Log.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/Log.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/Log.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,53 @@
+//$Id: Log.java 7700 2005-07-30 05:02:47Z oneovthafew $
+package org.hibernate.test.interceptor;
+
+import java.util.Calendar;
+
+public class Log {
+ private Long id;
+ private String entityName;
+ private String entityId;
+ private String action;
+ private Calendar time;
+
+ public Log(String action, String id, String name) {
+ super();
+ this.action = action;
+ entityId = id;
+ entityName = name;
+ time = Calendar.getInstance();
+ }
+ public Log() {
+ super();
+ }
+ public String getAction() {
+ return action;
+ }
+ public void setAction(String action) {
+ this.action = action;
+ }
+ public String getEntityId() {
+ return entityId;
+ }
+ public void setEntityId(String entityId) {
+ this.entityId = entityId;
+ }
+ public String getEntityName() {
+ return entityName;
+ }
+ public void setEntityName(String entityName) {
+ this.entityName = entityName;
+ }
+ public Long getId() {
+ return id;
+ }
+ public void setId(Long id) {
+ this.id = id;
+ }
+ public Calendar getTime() {
+ return time;
+ }
+ public void setTime(Calendar time) {
+ this.time = time;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/Log.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/PropertyInterceptor.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/PropertyInterceptor.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/PropertyInterceptor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+//$Id: PropertyInterceptor.java 7700 2005-07-30 05:02:47Z oneovthafew $
+package org.hibernate.test.interceptor;
+
+import java.io.Serializable;
+import java.util.Calendar;
+
+import org.hibernate.EmptyInterceptor;
+import org.hibernate.type.Type;
+
+public class PropertyInterceptor extends EmptyInterceptor {
+
+ public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) {
+ currentState[1] = Calendar.getInstance();
+ return true;
+ }
+
+ public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
+ state[2] = Calendar.getInstance();
+ return true;
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/PropertyInterceptor.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/StatefulInterceptor.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/StatefulInterceptor.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/StatefulInterceptor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,47 @@
+//$Id: StatefulInterceptor.java 7701 2005-07-30 05:07:01Z oneovthafew $
+package org.hibernate.test.interceptor;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.hibernate.EmptyInterceptor;
+import org.hibernate.Session;
+import org.hibernate.type.Type;
+
+public class StatefulInterceptor extends EmptyInterceptor {
+
+ private Session session;
+
+ private List list = new ArrayList();
+
+ public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
+ if ( !(entity instanceof Log) ) {
+ list.add( new Log( "insert", (String) id, entity.getClass().getName() ) );
+ }
+ return false;
+ }
+
+ public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) {
+ if ( !(entity instanceof Log) ) {
+ list.add( new Log( "update", (String) id, entity.getClass().getName() ) );
+ }
+ return false;
+ }
+
+ public void postFlush(Iterator entities) {
+ if ( list.size()>0 ) {
+ for ( Iterator iter = list.iterator(); iter.hasNext(); ) {
+ session.persist( iter.next() );
+ }
+ list.clear();
+ session.flush();
+ }
+ }
+
+ public void setSession(Session s) {
+ session = s;
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/StatefulInterceptor.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/User.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/User.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/User.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,35 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+
+-->
+
+<hibernate-mapping
+ package="org.hibernate.test.interceptor">
+
+ <class name="User" table="users">
+ <id name="name"/>
+ <property name="password"/>
+ <property name="lastUpdated"/>
+ <property name="created"/>
+ <set name="actions" lazy="false" fetch="join">
+ <key column="user_name"/>
+ <element column="action" type="string"/>
+ </set>
+ </class>
+
+ <class name="Log" table="log_entries">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="entityName" column="entity_name"/>
+ <property name="entityId" column="entity_id"/>
+ <property name="action"/>
+ <property name="time" column="action_time"/>
+ </class>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/User.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/User.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/User.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/User.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,53 @@
+//$Id: User.java 7700 2005-07-30 05:02:47Z oneovthafew $
+package org.hibernate.test.interceptor;
+
+import java.util.Calendar;
+import java.util.HashSet;
+import java.util.Set;
+
+public class User {
+ private String name;
+ private String password;
+ private Set actions = new HashSet();
+ private Calendar lastUpdated;
+ private Calendar created;
+
+ public User(String name, String password) {
+ super();
+ this.name = name;
+ this.password = password;
+ }
+ public User() {
+ super();
+ }
+ public Calendar getLastUpdated() {
+ return lastUpdated;
+ }
+ public void setLastUpdated(Calendar lastUpdated) {
+ this.lastUpdated = lastUpdated;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public String getPassword() {
+ return password;
+ }
+ public void setPassword(String password) {
+ this.password = password;
+ }
+ public Set getActions() {
+ return actions;
+ }
+ public void setActions(Set actions) {
+ this.actions = actions;
+ }
+ public Calendar getCreated() {
+ return created;
+ }
+ public void setCreated(Calendar created) {
+ this.created = created;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interceptor/User.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/Document.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/Document.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/Document.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,24 @@
+//$Id: Document.java 4407 2004-08-22 01:20:08Z oneovthafew $
+package org.hibernate.test.interfaceproxy;
+
+import java.sql.Blob;
+import java.util.Calendar;
+
+/**
+ * @author Gavin King
+ */
+public interface Document extends Item {
+ /**
+ * @return Returns the content.
+ */
+ public Blob getContent();
+
+ /**
+ * @param content The content to set.
+ */
+ public void setContent(Blob content);
+
+ public Calendar getCreated();
+
+ public Calendar getModified();
+}
\ No newline at end of file
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/Document.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/DocumentImpl.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/DocumentImpl.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/DocumentImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,50 @@
+//$Id: DocumentImpl.java 4407 2004-08-22 01:20:08Z oneovthafew $
+package org.hibernate.test.interfaceproxy;
+
+import java.sql.Blob;
+import java.util.Calendar;
+
+/**
+ * @author Gavin King
+ */
+public class DocumentImpl extends ItemImpl implements Document {
+ private Blob content;
+ private Calendar modified;
+ private Calendar created;
+ /**
+ * @return Returns the created.
+ */
+ public Calendar getCreated() {
+ return created;
+ }
+ /**
+ * @param created The created to set.
+ */
+ public void setCreated(Calendar created) {
+ this.created = created;
+ }
+ /**
+ * @return Returns the modified.
+ */
+ public Calendar getModified() {
+ return modified;
+ }
+ /**
+ * @param modified The modified to set.
+ */
+ public void setModified(Calendar modified) {
+ this.modified = modified;
+ }
+ /**
+ * @return Returns the content.
+ */
+ public Blob getContent() {
+ return content;
+ }
+ /**
+ * @param content The content to set.
+ */
+ public void setContent(Blob content) {
+ this.content = content;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/DocumentImpl.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/DocumentInterceptor.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/DocumentInterceptor.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/DocumentInterceptor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,95 @@
+//$Id: DocumentInterceptor.java 7860 2005-08-11 21:58:23Z oneovthafew $
+package org.hibernate.test.interfaceproxy;
+
+import java.io.Serializable;
+import java.util.Calendar;
+import java.util.Iterator;
+
+import org.hibernate.CallbackException;
+import org.hibernate.Interceptor;
+import org.hibernate.Transaction;
+import org.hibernate.EntityMode;
+import org.hibernate.type.Type;
+
+/**
+ * @author Gavin King
+ */
+public class DocumentInterceptor implements Interceptor {
+
+
+ public boolean onLoad(Object entity, Serializable id, Object[] state,
+ String[] propertyNames, Type[] types) throws CallbackException {
+ return false;
+ }
+
+ public boolean onFlushDirty(Object entity, Serializable id,
+ Object[] currentState, Object[] previousState,
+ String[] propertyNames, Type[] types) throws CallbackException {
+ if ( entity instanceof Document ) {
+ currentState[2] = Calendar.getInstance();
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ public boolean onSave(Object entity, Serializable id, Object[] state,
+ String[] propertyNames, Type[] types) throws CallbackException {
+ if ( entity instanceof Document ) {
+ state[3] = state[2] = Calendar.getInstance();
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ public void onDelete(Object entity, Serializable id, Object[] state,
+ String[] propertyNames, Type[] types) throws CallbackException {
+
+ }
+
+ public void preFlush(Iterator entities) throws CallbackException {
+
+ }
+
+ public void postFlush(Iterator entities) throws CallbackException {
+
+ }
+
+ public Boolean isTransient(Object entity) {
+ return null;
+ }
+
+ public int[] findDirty(Object entity, Serializable id,
+ Object[] currentState, Object[] previousState,
+ String[] propertyNames, Type[] types) {
+ return null;
+ }
+
+ public Object instantiate(String entityName, EntityMode entityMode, Serializable id) throws CallbackException {
+ return null;
+ }
+
+ public String getEntityName(Object object) throws CallbackException {
+ return null;
+ }
+
+ public Object getEntity(String entityName, Serializable id)
+ throws CallbackException {
+ return null;
+ }
+
+ public void afterTransactionBegin(Transaction tx) {}
+ public void afterTransactionCompletion(Transaction tx) {}
+ public void beforeTransactionCompletion(Transaction tx) {}
+
+ public String onPrepareStatement(String sql) {
+ return sql;
+ }
+
+ public void onCollectionRecreate(Object collection, Serializable key) throws CallbackException {}
+ public void onCollectionRemove(Object collection, Serializable key) throws CallbackException {}
+ public void onCollectionUpdate(Object collection, Serializable key) throws CallbackException {}
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/DocumentInterceptor.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/Folder.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/Folder.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/Folder.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,17 @@
+//$Id: Folder.java 4407 2004-08-22 01:20:08Z oneovthafew $
+package org.hibernate.test.interfaceproxy;
+
+/**
+ * @author Gavin King
+ */
+public interface Folder extends Item {
+ /**
+ * @return Returns the parent.
+ */
+ public Folder getParent();
+
+ /**
+ * @param parent The parent to set.
+ */
+ public void setParent(Folder parent);
+}
\ No newline at end of file
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/Folder.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/FolderImpl.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/FolderImpl.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/FolderImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,21 @@
+//$Id: FolderImpl.java 4407 2004-08-22 01:20:08Z oneovthafew $
+package org.hibernate.test.interfaceproxy;
+
+/**
+ * @author Gavin King
+ */
+public class FolderImpl extends ItemImpl implements Folder {
+ private Folder parent;
+ /**
+ * @return Returns the parent.
+ */
+ public Folder getParent() {
+ return parent;
+ }
+ /**
+ * @param parent The parent to set.
+ */
+ public void setParent(Folder parent) {
+ this.parent = parent;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/FolderImpl.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/InterfaceProxyTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/InterfaceProxyTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/InterfaceProxyTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,101 @@
+//$Id: InterfaceProxyTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.interfaceproxy;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.dialect.PostgreSQLDialect;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class InterfaceProxyTest extends FunctionalTestCase {
+
+ public InterfaceProxyTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "interfaceproxy/Item.hbm.xml" };
+ }
+
+ public String getCacheConcurrencyStrategy() {
+ return null;
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( InterfaceProxyTest.class );
+ }
+
+ public void testInterfaceProxies() {
+
+ if ( getDialect() instanceof PostgreSQLDialect ) {
+ // TODO : why?
+ return;
+ }
+
+ Session s = openSession( new DocumentInterceptor() );
+ Transaction t = s.beginTransaction();
+ Document d = new DocumentImpl();
+ d.setName("Hibernate in Action");
+ d.setContent( Hibernate.createBlob( "blah blah blah".getBytes() ) );
+ Long did = (Long) s.save(d);
+ SecureDocument d2 = new SecureDocumentImpl();
+ d2.setName("Secret");
+ d2.setContent( Hibernate.createBlob( "wxyz wxyz".getBytes() ) );
+ d2.setPermissionBits( (byte) 664 );
+ d2.setOwner("gavin");
+ Long d2id = (Long) s.save(d2);
+ t.commit();
+ s.close();
+
+ s = openSession( new DocumentInterceptor() );
+ t = s.beginTransaction();
+ d = (Document) s.load(ItemImpl.class, did);
+ assertEquals( did, d.getId() );
+ assertEquals( "Hibernate in Action", d.getName() );
+ assertNotNull( d.getContent() );
+
+ d2 = (SecureDocument) s.load(ItemImpl.class, d2id);
+ assertEquals( d2id, d2.getId() );
+ assertEquals( "Secret", d2.getName() );
+ assertNotNull( d2.getContent() );
+
+ s.clear();
+
+ d = (Document) s.load(DocumentImpl.class, did);
+ assertEquals( did, d.getId() );
+ assertEquals( "Hibernate in Action", d.getName() );
+ assertNotNull( d.getContent() );
+
+ d2 = (SecureDocument) s.load(SecureDocumentImpl.class, d2id);
+ assertEquals( d2id, d2.getId() );
+ assertEquals( "Secret", d2.getName() );
+ assertNotNull( d2.getContent() );
+ assertEquals( "gavin", d2.getOwner() );
+
+ //s.clear();
+
+ d2 = (SecureDocument) s.load(SecureDocumentImpl.class, did);
+ assertEquals( did, d2.getId() );
+ assertEquals( "Hibernate in Action", d2.getName() );
+ assertNotNull( d2.getContent() );
+
+ try {
+ d2.getOwner(); //CCE
+ assertFalse(true);
+ }
+ catch (ClassCastException cce) {
+ //correct
+ }
+
+ s.createQuery( "delete ItemImpl" ).executeUpdate();
+ t.commit();
+ s.close();
+ }
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/InterfaceProxyTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/Item.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/Item.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/Item.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,70 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+ This mapping demonstrates the use of interface proxies,
+ and fields which are initialized by an interceptor.
+
+-->
+
+<hibernate-mapping
+ package="org.hibernate.test.interfaceproxy">
+
+ <class name="ItemImpl"
+ table="Items"
+ proxy="Item">
+
+ <id name="id">
+ <generator class="native"/>
+ </id>
+
+ <property name="name"
+ not-null="true"/>
+
+ <joined-subclass name="DocumentImpl"
+ table="Documents"
+ proxy="Document">
+
+ <key column="id"/>
+
+ <property name="content"
+ not-null="true"/>
+
+ <!-- managed by DocumentInterceptor -->
+ <property name="modified"
+ not-null="true"/>
+ <property name="created"
+ not-null="true"/>
+
+ <joined-subclass name="SecureDocumentImpl"
+ table="SecureDocuments"
+ proxy="SecureDocument">
+
+ <key column="documentId"/>
+
+ <property name="permissionBits"
+ not-null="true"/>
+ <property name="owner"
+ not-null="true"/>
+
+ </joined-subclass>
+
+ </joined-subclass>
+
+ <joined-subclass name="FolderImpl"
+ table="Folders"
+ proxy="Folder">
+
+ <key column="id"/>
+
+ <many-to-one name="parent"
+ class="FolderImpl"/>
+
+ </joined-subclass>
+
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/Item.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/Item.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/Item.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/Item.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+//$Id: Item.java 4407 2004-08-22 01:20:08Z oneovthafew $
+package org.hibernate.test.interfaceproxy;
+
+/**
+ * @author Gavin King
+ */
+public interface Item {
+ /**
+ * @return Returns the id.
+ */
+ public Long getId();
+
+ /**
+ * @return Returns the name.
+ */
+ public String getName();
+
+ /**
+ * @param name The name to set.
+ */
+ public void setName(String name);
+}
\ No newline at end of file
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/Item.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/ItemImpl.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/ItemImpl.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/ItemImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,34 @@
+//$Id: ItemImpl.java 4407 2004-08-22 01:20:08Z oneovthafew $
+package org.hibernate.test.interfaceproxy;
+
+/**
+ * @author Gavin King
+ */
+public abstract class ItemImpl implements Item {
+ private Long id;
+ private String name;
+ /**
+ * @return Returns the id.
+ */
+ public Long getId() {
+ return id;
+ }
+ /**
+ * @param id The id to set.
+ */
+ public void setId(Long id) {
+ this.id = id;
+ }
+ /**
+ * @return Returns the name.
+ */
+ public String getName() {
+ return name;
+ }
+ /**
+ * @param name The name to set.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/ItemImpl.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/SecureDocument.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/SecureDocument.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/SecureDocument.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,27 @@
+//$Id: SecureDocument.java 4407 2004-08-22 01:20:08Z oneovthafew $
+package org.hibernate.test.interfaceproxy;
+
+/**
+ * @author Gavin King
+ */
+public interface SecureDocument extends Document {
+ /**
+ * @return Returns the owner.
+ */
+ public String getOwner();
+
+ /**
+ * @param owner The owner to set.
+ */
+ public void setOwner(String owner);
+
+ /**
+ * @return Returns the permissionBits.
+ */
+ public byte getPermissionBits();
+
+ /**
+ * @param permissionBits The permissionBits to set.
+ */
+ public void setPermissionBits(byte permissionBits);
+}
\ No newline at end of file
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/SecureDocument.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/SecureDocumentImpl.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/SecureDocumentImpl.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/SecureDocumentImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,34 @@
+//$Id: SecureDocumentImpl.java 4407 2004-08-22 01:20:08Z oneovthafew $
+package org.hibernate.test.interfaceproxy;
+
+/**
+ * @author Gavin King
+ */
+public class SecureDocumentImpl extends DocumentImpl implements SecureDocument {
+ private byte permissionBits;
+ private String owner;
+ /**
+ * @return Returns the owner.
+ */
+ public String getOwner() {
+ return owner;
+ }
+ /**
+ * @param owner The owner to set.
+ */
+ public void setOwner(String owner) {
+ this.owner = owner;
+ }
+ /**
+ * @return Returns the permissionBits.
+ */
+ public byte getPermissionBits() {
+ return permissionBits;
+ }
+ /**
+ * @param permissionBits The permissionBits to set.
+ */
+ public void setPermissionBits(byte permissionBits) {
+ this.permissionBits = permissionBits;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/interfaceproxy/SecureDocumentImpl.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/iterate/Item.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/iterate/Item.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/iterate/Item.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping
+ package="org.hibernate.test.iterate">
+
+ <class name="Item" table="NamedItem" entity-name="Item">
+ <id name="name"/>
+ <query name="nameDesc">from Item order by name desc</query>
+ </class>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/iterate/Item.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/iterate/Item.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/iterate/Item.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/iterate/Item.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,20 @@
+//$Id: Item.java 6593 2005-04-28 15:52:26Z oneovthafew $
+package org.hibernate.test.iterate;
+
+/**
+ * @author Gavin King
+ */
+public class Item {
+ private String name;
+ Item() {}
+ public Item(String n) {
+ name = n;
+ }
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/iterate/Item.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/iterate/IterateTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/iterate/IterateTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/iterate/IterateTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,105 @@
+//$Id: IterateTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.iterate;
+
+import java.util.Iterator;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.ScrollableResults;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class IterateTest extends FunctionalTestCase {
+
+ public IterateTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "iterate/Item.hbm.xml" };
+ }
+
+ public void configure(Configuration cfg) {
+ super.configure( cfg );
+ cfg.setProperty( Environment.USE_QUERY_CACHE, "true" );
+ cfg.setProperty( Environment.CACHE_REGION_PREFIX, "foo" );
+ cfg.setProperty( Environment.USE_SECOND_LEVEL_CACHE, "true" );
+ cfg.setProperty( Environment.GENERATE_STATISTICS, "true" );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( IterateTest.class );
+ }
+
+ public void testIterate() throws Exception {
+ getSessions().getStatistics().clear();
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Item i1 = new Item("foo");
+ Item i2 = new Item("bar");
+ s.persist("Item", i1);
+ s.persist("Item", i2);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Iterator iter = s.getNamedQuery("Item.nameDesc").iterate();
+ i1 = (Item) iter.next();
+ i2 = (Item) iter.next();
+ assertFalse( Hibernate.isInitialized(i1) );
+ assertFalse( Hibernate.isInitialized(i2) );
+ i1.getName();
+ i2.getName();
+ assertFalse( Hibernate.isInitialized(i1) );
+ assertFalse( Hibernate.isInitialized(i2) );
+ assertEquals( i1.getName(), "foo" );
+ assertEquals( i2.getName(), "bar" );
+ Hibernate.initialize(i1);
+ assertFalse( iter.hasNext() );
+ s.delete(i1);
+ s.delete(i2);
+ t.commit();
+ s.close();
+ assertEquals( getSessions().getStatistics().getEntityFetchCount(), 2 );
+ }
+
+ public void testScroll() throws Exception {
+ getSessions().getStatistics().clear();
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Item i1 = new Item("foo");
+ Item i2 = new Item("bar");
+ s.persist("Item", i1);
+ s.persist("Item", i2);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ ScrollableResults sr = s.getNamedQuery("Item.nameDesc").scroll();
+ assertTrue( sr.next() );
+ i1 = (Item) sr.get(0);
+ assertTrue( sr.next() );
+ i2 = (Item) sr.get(0);
+ assertTrue( Hibernate.isInitialized(i1) );
+ assertTrue( Hibernate.isInitialized(i2) );
+ assertEquals( i1.getName(), "foo" );
+ assertEquals( i2.getName(), "bar" );
+ assertFalse( sr.next() );
+ s.delete(i1);
+ s.delete(i2);
+ t.commit();
+ s.close();
+ assertEquals( getSessions().getStatistics().getEntityFetchCount(), 0 );
+ }
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/iterate/IterateTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/join/Customer.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/join/Customer.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/join/Customer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,35 @@
+//$Id: Customer.java 4364 2004-08-17 12:10:32Z oneovthafew $
+package org.hibernate.test.join;
+
+/**
+ * @author Gavin King
+ */
+public class Customer extends Person {
+ private Employee salesperson;
+ private String comments;
+
+ /**
+ * @return Returns the salesperson.
+ */
+ public Employee getSalesperson() {
+ return salesperson;
+ }
+ /**
+ * @param salesperson The salesperson to set.
+ */
+ public void setSalesperson(Employee salesperson) {
+ this.salesperson = salesperson;
+ }
+ /**
+ * @return Returns the comments.
+ */
+ public String getComments() {
+ return comments;
+ }
+ /**
+ * @param comments The comments to set.
+ */
+ public void setComments(String comments) {
+ this.comments = comments;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/join/Customer.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/join/Employee.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/join/Employee.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/join/Employee.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,49 @@
+//$Id: Employee.java 4364 2004-08-17 12:10:32Z oneovthafew $
+package org.hibernate.test.join;
+
+import java.math.BigDecimal;
+
+/**
+ * @author Gavin King
+ */
+public class Employee extends Person {
+ private String title;
+ private BigDecimal salary;
+ private Employee manager;
+ /**
+ * @return Returns the title.
+ */
+ public String getTitle() {
+ return title;
+ }
+ /**
+ * @param title The title to set.
+ */
+ public void setTitle(String title) {
+ this.title = title;
+ }
+ /**
+ * @return Returns the manager.
+ */
+ public Employee getManager() {
+ return manager;
+ }
+ /**
+ * @param manager The manager to set.
+ */
+ public void setManager(Employee manager) {
+ this.manager = manager;
+ }
+ /**
+ * @return Returns the salary.
+ */
+ public BigDecimal getSalary() {
+ return salary;
+ }
+ /**
+ * @param salary The salary to set.
+ */
+ public void setSalary(BigDecimal salary) {
+ this.salary = salary;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/join/Employee.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/join/JoinTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/join/JoinTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/join/JoinTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,135 @@
+//$Id: JoinTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.join;
+
+import java.util.Iterator;
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class JoinTest extends FunctionalTestCase {
+
+ public JoinTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "join/Person.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( JoinTest.class );
+ }
+
+ public void testSequentialSelects() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ Employee mark = new Employee();
+ mark.setName("Mark");
+ mark.setTitle("internal sales");
+ mark.setSex('M');
+ mark.setAddress("buckhead");
+ mark.setZip("30305");
+ mark.setCountry("USA");
+
+ Customer joe = new Customer();
+ joe.setName("Joe");
+ joe.setAddress("San Francisco");
+ joe.setZip("XXXXX");
+ joe.setCountry("USA");
+ joe.setComments("Very demanding");
+ joe.setSex('M');
+ joe.setSalesperson(mark);
+
+ Person yomomma = new Person();
+ yomomma.setName("mum");
+ yomomma.setSex('F');
+
+ s.save(yomomma);
+ s.save(mark);
+ s.save(joe);
+
+ assertEquals( s.createQuery("from java.io.Serializable").list().size(), 0 );
+
+ assertEquals( s.createQuery("from Person").list().size(), 3 );
+ assertEquals( s.createQuery("from Person p where p.class is null").list().size(), 1 );
+ assertEquals( s.createQuery("from Person p where p.class = Customer").list().size(), 1 );
+ assertTrue(s.createQuery("from Customer c").list().size()==1);
+ s.clear();
+
+ List customers = s.createQuery("from Customer c left join fetch c.salesperson").list();
+ for ( Iterator iter = customers.iterator(); iter.hasNext(); ) {
+ Customer c = (Customer) iter.next();
+ assertTrue( Hibernate.isInitialized( c.getSalesperson() ) );
+ assertEquals( c.getSalesperson().getName(), "Mark" );
+ }
+ assertEquals( customers.size(), 1 );
+ s.clear();
+
+ customers = s.createQuery("from Customer").list();
+ for ( Iterator iter = customers.iterator(); iter.hasNext(); ) {
+ Customer c = (Customer) iter.next();
+ assertFalse( Hibernate.isInitialized( c.getSalesperson() ) );
+ assertEquals( c.getSalesperson().getName(), "Mark" );
+ }
+ assertEquals( customers.size(), 1 );
+ s.clear();
+
+
+ mark = (Employee) s.get( Employee.class, new Long( mark.getId() ) );
+ joe = (Customer) s.get( Customer.class, new Long( joe.getId() ) );
+
+ mark.setZip("30306");
+ assertEquals( s.createQuery("from Person p where p.zip = '30306'").list().size(), 1 );
+ s.delete(mark);
+ s.delete(joe);
+ s.delete(yomomma);
+ assertTrue( s.createQuery("from Person").list().isEmpty() );
+ t.commit();
+ s.close();
+ }
+
+ public void testSequentialSelectsOptionalData() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ User jesus = new User();
+ jesus.setName("Jesus Olvera y Martinez");
+ jesus.setSex('M');
+
+ s.save(jesus);
+
+ assertEquals( s.createQuery("from java.io.Serializable").list().size(), 0 );
+
+ assertEquals( s.createQuery("from Person").list().size(), 1 );
+ assertEquals( s.createQuery("from Person p where p.class is null").list().size(), 0 );
+ assertEquals( s.createQuery("from Person p where p.class = User").list().size(), 1 );
+ assertTrue(s.createQuery("from User u").list().size()==1);
+ s.clear();
+
+ // Remove the optional row from the join table and requery the User obj
+ s.connection().prepareStatement("delete from t_user").execute();
+ s.clear();
+
+ jesus = (User) s.get( Person.class, new Long( jesus.getId() ) );
+ s.clear();
+
+ // Cleanup the test data
+ s.delete(jesus);
+
+ assertTrue( s.createQuery("from Person").list().isEmpty() );
+ t.commit();
+ s.close();
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/join/JoinTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/join/Person.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/join/Person.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/join/Person.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,78 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+ This mapping demonstrates
+
+ (1) a table-per-subclass mapping strategy where subclass data is
+ retrieved by sequential select instead of a join (we do NOT
+ recommend you do this, since it is very vulnerable to N+1
+ problems; users porting applications from TopLink might find
+ this useful for reproducing old behavior)
+
+ (2) spreading data from a single class across multiple tables
+ (again, we do not recommend you do this; use finer grained
+ classes instead)
+
+ (3) use of null as a discriminator value
+
+ (4) recursive associations withing an inheritance tree
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.join" default-access="field">
+
+ <class name="Person" table="person" lazy="true" discriminator-value="null">
+
+ <id name="id" column="person_id" unsaved-value="0">
+ <generator class="native"/>
+ </id>
+
+ <!-- force is unnecessary, in case we had other unknown discriminator values -->
+ <discriminator column="person_type" type="string" length="1" not-null="false" force="true"/>
+
+ <property name="name" not-null="true" length="80"/>
+ <property name="sex" not-null="true" update="false"/>
+
+ <join table="address">
+ <key column="address_id"/>
+ <property name="address"/>
+ <property name="zip"/>
+ <property name="country"/>
+ </join>
+
+ <subclass name="Employee" lazy="true" discriminator-value="E">
+ <join table="employee" fetch="select">
+ <key column="person_id"/>
+ <property name="title" not-null="true" length="20"/>
+ <property name="salary" length="0"/>
+ <many-to-one name="manager"/>
+ </join>
+ </subclass>
+
+ <subclass name="Customer" lazy="true" discriminator-value="C">
+ <join table="customer" fetch="select">
+ <key column="person_id"/>
+ <property name="comments"/>
+ <many-to-one name="salesperson"/>
+ </join>
+ </subclass>
+
+ <subclass name="User" lazy="true" discriminator-value="U">
+ <join table="t_user" fetch="select" optional="true">
+ <key column="person_id"/>
+ <property name="login" column="u_login"/>
+ </join>
+ <join table="t_silly" fetch="select" optional="true">
+ <key column="person_id"/>
+ <property name="silly"/>
+ </join>
+ </subclass>
+
+ </class>
+
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/join/Person.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/join/Person.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/join/Person.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/join/Person.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,90 @@
+//$Id: Person.java 7203 2005-06-19 02:01:05Z oneovthafew $
+package org.hibernate.test.join;
+
+
+/**
+ * @author Gavin King
+ */
+public class Person {
+ private long id;
+ private String name;
+ private String address;
+ private String zip;
+ private String country;
+ private char sex;
+
+ /**
+ * @return Returns the sex.
+ */
+ public char getSex() {
+ return sex;
+ }
+ /**
+ * @param sex The sex to set.
+ */
+ public void setSex(char sex) {
+ this.sex = sex;
+ }
+ /**
+ * @return Returns the id.
+ */
+ public long getId() {
+ return id;
+ }
+ /**
+ * @param id The id to set.
+ */
+ public void setId(long id) {
+ this.id = id;
+ }
+ /**
+ * @return Returns the identity.
+ */
+ public String getName() {
+ return name;
+ }
+ /**
+ * @param identity The identity to set.
+ */
+ public void setName(String identity) {
+ this.name = identity;
+ }
+ public String getSpecies() {
+ return null;
+ }
+
+ /**
+ * @return Returns the country.
+ */
+ public String getCountry() {
+ return country;
+ }
+ /**
+ * @param country The country to set.
+ */
+ public void setCountry(String country) {
+ this.country = country;
+ }
+ /**
+ * @return Returns the zip.
+ */
+ public String getZip() {
+ return zip;
+ }
+ /**
+ * @param zip The zip to set.
+ */
+ public void setZip(String zip) {
+ this.zip = zip;
+ }
+ /**
+ * @param address The address to set.
+ */
+ public void setAddress(String address) {
+ this.address = address;
+ }
+
+ public String getAddress() {
+ return address;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/join/Person.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/join/User.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/join/User.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/join/User.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,23 @@
+//$Id$
+package org.hibernate.test.join;
+
+/**
+ * @author Mike Dillon
+ */
+public class User extends Person {
+ private String login;
+ private String silly;
+
+ /**
+ * @return Returns the login.
+ */
+ public String getLogin() {
+ return login;
+ }
+ /**
+ * @param login The login to set.
+ */
+ public void setLogin(String login) {
+ this.login = login;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinedsubclass/Address.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinedsubclass/Address.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinedsubclass/Address.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,11 @@
+//$Id: Address.java 4364 2004-08-17 12:10:32Z oneovthafew $
+package org.hibernate.test.joinedsubclass;
+
+/**
+ * @author Gavin King
+ */
+public class Address {
+ public String address;
+ public String zip;
+ public String country;
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinedsubclass/Address.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinedsubclass/Customer.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinedsubclass/Customer.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinedsubclass/Customer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,35 @@
+//$Id: Customer.java 4364 2004-08-17 12:10:32Z oneovthafew $
+package org.hibernate.test.joinedsubclass;
+
+/**
+ * @author Gavin King
+ */
+public class Customer extends Person {
+ private Employee salesperson;
+ private String comments;
+
+ /**
+ * @return Returns the salesperson.
+ */
+ public Employee getSalesperson() {
+ return salesperson;
+ }
+ /**
+ * @param salesperson The salesperson to set.
+ */
+ public void setSalesperson(Employee salesperson) {
+ this.salesperson = salesperson;
+ }
+ /**
+ * @return Returns the comments.
+ */
+ public String getComments() {
+ return comments;
+ }
+ /**
+ * @param comments The comments to set.
+ */
+ public void setComments(String comments) {
+ this.comments = comments;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinedsubclass/Customer.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinedsubclass/Employee.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinedsubclass/Employee.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinedsubclass/Employee.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,49 @@
+//$Id: Employee.java 4364 2004-08-17 12:10:32Z oneovthafew $
+package org.hibernate.test.joinedsubclass;
+
+import java.math.BigDecimal;
+
+/**
+ * @author Gavin King
+ */
+public class Employee extends Person {
+ private String title;
+ private BigDecimal salary;
+ private Employee manager;
+ /**
+ * @return Returns the title.
+ */
+ public String getTitle() {
+ return title;
+ }
+ /**
+ * @param title The title to set.
+ */
+ public void setTitle(String title) {
+ this.title = title;
+ }
+ /**
+ * @return Returns the manager.
+ */
+ public Employee getManager() {
+ return manager;
+ }
+ /**
+ * @param manager The manager to set.
+ */
+ public void setManager(Employee manager) {
+ this.manager = manager;
+ }
+ /**
+ * @return Returns the salary.
+ */
+ public BigDecimal getSalary() {
+ return salary;
+ }
+ /**
+ * @param salary The salary to set.
+ */
+ public void setSalary(BigDecimal salary) {
+ this.salary = salary;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinedsubclass/Employee.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinedsubclass/JoinedSubclassTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinedsubclass/JoinedSubclassTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinedsubclass/JoinedSubclassTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,211 @@
+//$Id: JoinedSubclassTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.joinedsubclass;
+
+import java.math.BigDecimal;
+import java.util.Iterator;
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.LockMode;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.criterion.Expression;
+import org.hibernate.criterion.Property;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class JoinedSubclassTest extends FunctionalTestCase {
+
+ public JoinedSubclassTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "joinedsubclass/Person.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( JoinedSubclassTest.class );
+ }
+
+ public void testJoinedSubclass() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ Employee mark = new Employee();
+ mark.setName("Mark");
+ mark.setTitle("internal sales");
+ mark.setSex('M');
+ mark.setAddress("buckhead");
+ mark.setZip("30305");
+ mark.setCountry("USA");
+
+ Customer joe = new Customer();
+ joe.setName("Joe");
+ joe.setAddress("San Francisco");
+ joe.setZip("XXXXX");
+ joe.setCountry("USA");
+ joe.setComments("Very demanding");
+ joe.setSex('M');
+ joe.setSalesperson(mark);
+
+ Person yomomma = new Person();
+ yomomma.setName("mum");
+ yomomma.setSex('F');
+
+ s.save(yomomma);
+ s.save(mark);
+ s.save(joe);
+
+ assertEquals( s.createQuery("from java.io.Serializable").list().size(), 0 );
+
+ assertEquals( s.createQuery("from Person").list().size(), 3 );
+ assertEquals( s.createQuery("from Person p where p.class = Customer").list().size(), 1 );
+ assertEquals( s.createQuery("from Person p where p.class = Person").list().size(), 1 );
+ s.clear();
+
+ List customers = s.createQuery("from Customer c left join fetch c.salesperson").list();
+ for ( Iterator iter = customers.iterator(); iter.hasNext(); ) {
+ Customer c = (Customer) iter.next();
+ assertTrue( Hibernate.isInitialized( c.getSalesperson() ) );
+ assertEquals( c.getSalesperson().getName(), "Mark" );
+ }
+ assertEquals( customers.size(), 1 );
+ s.clear();
+
+ customers = s.createQuery("from Customer").list();
+ for ( Iterator iter = customers.iterator(); iter.hasNext(); ) {
+ Customer c = (Customer) iter.next();
+ assertFalse( Hibernate.isInitialized( c.getSalesperson() ) );
+ assertEquals( c.getSalesperson().getName(), "Mark" );
+ }
+ assertEquals( customers.size(), 1 );
+ s.clear();
+
+
+ mark = (Employee) s.get( Employee.class, new Long( mark.getId() ) );
+ joe = (Customer) s.get( Customer.class, new Long( joe.getId() ) );
+
+ mark.setZip("30306");
+ assertEquals( s.createQuery("from Person p where p.address.zip = '30306'").list().size(), 1 );
+
+ if ( supportsRowValueConstructorSyntaxInInList() ) {
+ s.createCriteria(Person.class).add(
+ Expression.in("address", new Address[] { mark.getAddress(), joe.getAddress() } )
+ ).list();
+ }
+
+ s.delete(mark);
+ s.delete(joe);
+ s.delete(yomomma);
+ assertTrue( s.createQuery("from Person").list().isEmpty() );
+ t.commit();
+ s.close();
+ }
+
+ public void testAccessAsIncorrectSubclass() {
+ Session s = openSession();
+ s.beginTransaction();
+ Employee e = new Employee();
+ e.setName( "Steve" );
+ e.setSex( 'M' );
+ e.setTitle( "grand poobah" );
+ s.save( e );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ Customer c = ( Customer ) s.get( Customer.class, new Long( e.getId() ) );
+ s.getTransaction().commit();
+ s.close();
+ assertNull( c );
+
+ s = openSession();
+ s.beginTransaction();
+ e = ( Employee ) s.get( Employee.class, new Long( e.getId() ) );
+ c = ( Customer ) s.get( Customer.class, new Long( e.getId() ) );
+ s.getTransaction().commit();
+ s.close();
+ assertNotNull( e );
+ assertNull( c );
+
+ s = openSession();
+ s.beginTransaction();
+ s.delete( e );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testQuerySubclassAttribute() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Person p = new Person();
+ p.setName("Emmanuel");
+ p.setSex('M');
+ s.persist(p);
+ Employee q = new Employee();
+ q.setName("Steve");
+ q.setSex('M');
+ q.setTitle("Mr");
+ q.setSalary( new BigDecimal(1000) );
+ s.persist(q);
+
+ List result = s.createQuery("from Person where salary > 100").list();
+ assertEquals( result.size(), 1 );
+ assertSame( result.get(0), q );
+
+ result = s.createQuery("from Person where salary > 100 or name like 'E%'").list();
+ assertEquals( result.size(), 2 );
+
+ result = s.createCriteria(Person.class)
+ .add( Property.forName("salary").gt( new BigDecimal(100) ) )
+ .list();
+ assertEquals( result.size(), 1 );
+ assertSame( result.get(0), q );
+
+ //TODO: make this work:
+ /*result = s.createQuery("select salary from Person where salary > 100").list();
+ assertEquals( result.size(), 1 );
+ assertEquals( result.get(0), new BigDecimal(1000) );*/
+
+ s.delete(p);
+ s.delete(q);
+ t.commit();
+ s.close();
+ }
+
+ public void testLockingJoinedSubclass() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Person p = new Person();
+ p.setName("Emmanuel");
+ p.setSex('M');
+ s.persist(p);
+ Employee q = new Employee();
+ q.setName("Steve");
+ q.setSex('M');
+ q.setTitle("Mr");
+ q.setSalary( new BigDecimal(1000) );
+ s.persist(q);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.lock( p, LockMode.UPGRADE );
+ s.lock( q, LockMode.UPGRADE );
+ s.delete( p );
+ s.delete( q );
+ t.commit();
+ s.close();
+
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinedsubclass/JoinedSubclassTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinedsubclass/Person.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinedsubclass/Person.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinedsubclass/Person.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,64 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+ This mapping demonstrates
+
+ (1) a table-per-subclass mapping strategy
+
+ (2) a simple component mapping
+
+ (3) recursive associations withing an inheritance tree
+
+-->
+
+<hibernate-mapping
+ package="org.hibernate.test.joinedsubclass"
+ default-access="field">
+
+ <class name="Person" table="JPerson">
+
+ <id name="id"
+ column="person_id"
+ unsaved-value="0">
+ <generator class="native"/>
+ </id>
+
+ <version name="version" column="version" type="int"/>
+
+ <property name="name"
+ not-null="true"
+ length="80"/>
+ <property name="sex"
+ not-null="true"
+ update="false"/>
+
+ <component name="address">
+ <property name="address"/>
+ <property name="zip"/>
+ <property name="country"/>
+ </component>
+
+ <joined-subclass name="Employee" table="JEmployee">
+ <key column="person_id"/>
+ <property name="title"
+ not-null="true"
+ length="20"/>
+ <property name="salary"
+ length="0"/>
+ <many-to-one name="manager"/>
+ </joined-subclass>
+
+ <joined-subclass name="Customer" table="JManager">
+ <key column="person_id"/>
+ <property name="comments"/>
+ <many-to-one name="salesperson"/>
+ </joined-subclass>
+
+ </class>
+
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinedsubclass/Person.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinedsubclass/Person.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinedsubclass/Person.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinedsubclass/Person.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,78 @@
+//$Id: Person.java 10218 2006-08-04 18:24:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.joinedsubclass;
+
+
+/**
+ * @author Gavin King
+ */
+public class Person {
+ private long id;
+ private String name;
+ private char sex;
+ private int version;
+ private Address address = new Address();
+ /**
+ * @return Returns the address.
+ */
+ public Address getAddress() {
+ return address;
+ }
+
+ public void setAddress(String string) {
+ this.address.address = string;
+ }
+
+ public void setZip(String string) {
+ this.address.zip = string;
+ }
+
+ public void setCountry(String string) {
+ this.address.country = string;
+ }
+
+
+ /**
+ * @return Returns the sex.
+ */
+ public char getSex() {
+ return sex;
+ }
+ /**
+ * @param sex The sex to set.
+ */
+ public void setSex(char sex) {
+ this.sex = sex;
+ }
+ /**
+ * @return Returns the id.
+ */
+ public long getId() {
+ return id;
+ }
+ /**
+ * @param id The id to set.
+ */
+ public void setId(long id) {
+ this.id = id;
+ }
+ /**
+ * @return Returns the identity.
+ */
+ public String getName() {
+ return name;
+ }
+ /**
+ * @param identity The identity to set.
+ */
+ public void setName(String identity) {
+ this.name = identity;
+ }
+
+ public int getVersion() {
+ return version;
+ }
+
+ public void setVersion(int version) {
+ this.version = version;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinedsubclass/Person.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/Bid.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/Bid.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/Bid.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,49 @@
+//$Id: Bid.java 6793 2005-05-16 05:46:47Z oneovthafew $
+package org.hibernate.test.joinfetch;
+
+import java.util.Calendar;
+
+/**
+ * @author Gavin King
+ */
+public class Bid {
+
+ private float amount;
+ private Item item;
+ private Calendar timestamp;
+ private Long id;
+
+ public float getAmount() {
+ return amount;
+ }
+ public void setAmount(float amount) {
+ this.amount = amount;
+ }
+ public Item getItem() {
+ return item;
+ }
+ public void setItem(Item item) {
+ this.item = item;
+ }
+ public Calendar getTimestamp() {
+ return timestamp;
+ }
+ public void setTimestamp(Calendar timestamp) {
+ this.timestamp = timestamp;
+ }
+
+ Bid() {}
+ public Bid(Item item, float amount) {
+ this.amount = amount;
+ this.item = item;
+ item.getBids().add(this);
+ this.timestamp = Calendar.getInstance();
+ }
+ public Long getId() {
+ return id;
+ }
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/Bid.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/Category.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/Category.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/Category.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,20 @@
+//$Id: Category.java 6957 2005-05-31 04:21:58Z oneovthafew $
+package org.hibernate.test.joinfetch;
+
+/**
+ * @author Gavin King
+ */
+public class Category {
+
+ private String name;
+
+ Category() {}
+
+ public Category(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/Category.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/Comment.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/Comment.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/Comment.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,49 @@
+//$Id: Comment.java 6797 2005-05-16 12:08:03Z oneovthafew $
+package org.hibernate.test.joinfetch;
+
+import java.util.Calendar;
+
+/**
+ * @author Gavin King
+ */
+public class Comment {
+
+ private String text;
+ private Item item;
+ private Calendar timestamp;
+ private Long id;
+
+ public Item getItem() {
+ return item;
+ }
+ public void setItem(Item item) {
+ this.item = item;
+ }
+ public Calendar getTimestamp() {
+ return timestamp;
+ }
+ public void setTimestamp(Calendar timestamp) {
+ this.timestamp = timestamp;
+ }
+
+ Comment() {}
+ public Comment(Item item, String comment) {
+ this.text = comment;
+ this.item = item;
+ item.getComments().add(this);
+ this.timestamp = Calendar.getInstance();
+ }
+ public Long getId() {
+ return id;
+ }
+ public void setId(Long id) {
+ this.id = id;
+ }
+ public String getText() {
+ return text;
+ }
+ public void setText(String text) {
+ this.text = text;
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/Comment.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/Group.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/Group.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/Group.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,33 @@
+//$Id: Group.java 7175 2005-06-17 05:23:15Z oneovthafew $
+package org.hibernate.test.joinfetch;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class Group {
+ private String name;
+ private Map users = new HashMap();
+
+ public Group(String name) {
+ this.name = name;
+ }
+
+ Group() {}
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Map getUsers() {
+ return users;
+ }
+
+ public void setUsers(Map users) {
+ this.users = users;
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/Group.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/Item.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/Item.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/Item.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,64 @@
+//$Id: Item.java 6957 2005-05-31 04:21:58Z oneovthafew $
+package org.hibernate.test.joinfetch;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author Gavin King
+ */
+public class Item {
+
+ private String description;
+ private Long id;
+ private Category category;
+ private Set bids = new HashSet();
+ private Set comments = new HashSet();
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ Item() {}
+ public Item(Category cat, String desc) {
+ description = desc;
+ category = cat;
+ }
+
+ public Set getBids() {
+ return bids;
+ }
+
+ public void setBids(Set bids) {
+ this.bids = bids;
+ }
+
+ public Set getComments() {
+ return comments;
+ }
+
+ public void setComments(Set comments) {
+ this.comments = comments;
+ }
+
+ public Category getCategory() {
+ return category;
+ }
+
+ public void setCategory(Category category) {
+ this.category = category;
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/Item.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/ItemBid.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/ItemBid.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/ItemBid.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,64 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+
+-->
+
+<hibernate-mapping
+ package="org.hibernate.test.joinfetch"
+ default-access="field">
+
+ <class name="Category" table="AuctionCategories">
+ <id name="name"/>
+ </class>
+
+ <class name="Item" table="AuctionItems">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="description" not-null="true"/>
+ <many-to-one name="category" not-null="true"/>
+ <set name="bids" cascade="all" fetch="join" inverse="true" order-by="`timestamp`">
+ <key column="item"/>
+ <one-to-many class="Bid"/>
+ </set>
+ <set name="comments" cascade="all" fetch="join" inverse="true" order-by="`timestamp`">
+ <key column="item"/>
+ <one-to-many class="Comment"/>
+ </set>
+
+ <sql-query name="all">
+ <return alias="item" class="Item"/>
+ <return-join alias="bid" property="item.bids"/>
+ <return-join alias="commnt" property="item.comments"/>
+ select {item.*}, {bid.*}, {commnt.*}
+ from AuctionItems item
+ left outer join AuctionBids bid on bid.item = item.id
+ left outer join AuctionComments commnt on commnt.item = item.id
+ </sql-query>
+
+ </class>
+
+ <class name="Bid" table="AuctionBids">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="amount" not-null="true"/>
+ <property name="timestamp" column="`timestamp`" not-null="true"/>
+ <many-to-one name="item" fetch="join" not-null="true"/>
+ </class>
+
+ <class name="Comment" table="AuctionComments">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="text" not-null="true"/>
+ <property name="timestamp" column="`timestamp`" not-null="true"/>
+ <many-to-one name="item" fetch="join" not-null="true"/>
+ </class>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/ItemBid.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/JoinFetchTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/JoinFetchTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/JoinFetchTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,267 @@
+//$Id: JoinFetchTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.joinfetch;
+
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.hibernate.FetchMode;
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.criterion.Projections;
+import org.hibernate.criterion.Restrictions;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class JoinFetchTest extends FunctionalTestCase {
+
+ public JoinFetchTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "joinfetch/ItemBid.hbm.xml", "joinfetch/UserGroup.hbm.xml" };
+ }
+
+ public void configure(Configuration cfg) {
+ cfg.setProperty(Environment.MAX_FETCH_DEPTH, "10");
+ cfg.setProperty(Environment.USE_SECOND_LEVEL_CACHE, "false");
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( JoinFetchTest.class );
+ }
+
+ public void testProjection() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.createCriteria(Item.class).setProjection( Projections.rowCount() ).uniqueResult();
+ s.createCriteria(Item.class).uniqueResult();
+ t.commit();
+ s.close();
+ }
+
+ public void testJoinFetch() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.createQuery( "delete from Bid" ).executeUpdate();
+ s.createQuery( "delete from Comment" ).executeUpdate();
+ s.createQuery( "delete from Item" ).executeUpdate();
+ t.commit();
+ s.close();
+
+ Category cat = new Category("Photography");
+ Item i = new Item(cat, "Camera");
+ Bid b = new Bid(i, 100.0f);
+ new Bid(i, 105.0f);
+ new Comment(i, "This looks like a really good deal");
+ new Comment(i, "Is it the latest version?");
+ new Comment(i, "<comment deleted>");
+ System.out.println( b.getTimestamp() );
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.persist(cat);
+ s.persist(i);
+ t.commit();
+ s.close();
+
+ getSessions().evict(Item.class);
+
+ s = openSession();
+ t = s.beginTransaction();
+ i = (Item) s.get( Item.class, i.getId() );
+ assertTrue( Hibernate.isInitialized( i.getBids() ) );
+ assertEquals( i.getBids().size(), 2 );
+ assertTrue( Hibernate.isInitialized( i.getComments() ) );
+ assertEquals( i.getComments().size(), 3 );
+ t.commit();
+ s.close();
+
+ getSessions().evict(Bid.class);
+
+ s = openSession();
+ t = s.beginTransaction();
+ b = (Bid) s.get( Bid.class, b.getId() );
+ assertTrue( Hibernate.isInitialized( b.getItem() ) );
+ assertTrue( Hibernate.isInitialized( b.getItem().getComments() ) );
+ assertEquals( b.getItem().getComments().size(), 3 );
+ System.out.println( b.getTimestamp() );
+ t.commit();
+ s.close();
+
+ getSessions().evictCollection(Item.class.getName() + ".bids");
+
+ s = openSession();
+ t = s.beginTransaction();
+ i = (Item) s.createCriteria( Item.class )
+ .setFetchMode("bids", FetchMode.SELECT)
+ .setFetchMode("comments", FetchMode.SELECT)
+ .uniqueResult();
+ assertFalse( Hibernate.isInitialized( i.getBids() ) );
+ assertFalse( Hibernate.isInitialized( i.getComments() ) );
+ b = (Bid) i.getBids().iterator().next();
+ assertTrue( Hibernate.isInitialized( b.getItem() ) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ i = (Item) s.createQuery("from Item i left join fetch i.bids left join fetch i.comments").uniqueResult();
+ assertTrue( Hibernate.isInitialized( i.getBids() ) );
+ assertTrue( Hibernate.isInitialized( i.getComments() ) );
+ assertEquals( i.getComments().size(), 3 );
+ assertEquals( i.getBids().size(), 2 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Object[] row = (Object[]) s.getNamedQuery(Item.class.getName() + ".all").list().get(0);
+ i = (Item) row[0];
+ assertTrue( Hibernate.isInitialized( i.getBids() ) );
+ assertTrue( Hibernate.isInitialized( i.getComments() ) );
+ assertEquals( i.getComments().size(), 3 );
+ assertEquals( i.getBids().size(), 2 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ i = (Item) s.createCriteria(Item.class).uniqueResult();
+ assertTrue( Hibernate.isInitialized( i.getBids() ) );
+ assertTrue( Hibernate.isInitialized( i.getComments() ) );
+ assertEquals( i.getComments().size(), 3 );
+ assertEquals( i.getBids().size(), 2 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ List bids = s.createQuery("from Bid b left join fetch b.item i left join fetch i.category").list();
+ Bid bid = (Bid) bids.get(0);
+ assertTrue( Hibernate.isInitialized( bid.getItem() ) );
+ assertTrue( Hibernate.isInitialized( bid.getItem().getCategory() ) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ List pairs = s.createQuery("from Item i left join i.bids b left join fetch i.category").list();
+ Item item = (Item) ( (Object[]) pairs.get(0) )[0];
+ assertFalse( Hibernate.isInitialized( item.getBids() ) );
+ assertTrue( Hibernate.isInitialized( item.getCategory() ) );
+ s.clear();
+ pairs = s.createQuery("from Item i left join i.bids b left join i.category").list();
+ item = (Item) ( (Object[]) pairs.get(0) )[0];
+ assertFalse( Hibernate.isInitialized( item.getBids() ) );
+ assertTrue( Hibernate.isInitialized( item.getCategory() ) );
+ s.clear();
+ pairs = s.createQuery("from Bid b left join b.item i left join fetch i.category").list();
+ bid = (Bid) ( (Object[]) pairs.get(0) )[0];
+ assertTrue( Hibernate.isInitialized( bid.getItem() ) );
+ assertTrue( Hibernate.isInitialized( bid.getItem().getCategory() ) );
+ s.clear();
+ pairs = s.createQuery("from Bid b left join b.item i left join i.category").list();
+ bid = (Bid) ( (Object[]) pairs.get(0) )[0];
+ assertTrue( Hibernate.isInitialized( bid.getItem() ) );
+ assertTrue( Hibernate.isInitialized( bid.getItem().getCategory() ) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.createQuery( "delete from Bid" ).executeUpdate();
+ s.createQuery( "delete from Comment" ).executeUpdate();
+ s.createQuery( "delete from Item" ).executeUpdate();
+ s.createQuery( "delete from Category" ).executeUpdate();
+ t.commit();
+ s.close();
+
+ }
+
+ public void testCollectionFilter() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Group hb = new Group("hibernate");
+ User gavin = new User("gavin");
+ User max = new User("max");
+ hb.getUsers().put("gavin", gavin);
+ hb.getUsers().put("max", max);
+ gavin.getGroups().put("hibernate", hb);
+ max.getGroups().put("hibernate", hb);
+ s.persist(hb);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ hb = (Group) s.createCriteria(Group.class)
+ .setFetchMode("users", FetchMode.SELECT)
+ .add( Restrictions.idEq("hibernate") )
+ .uniqueResult();
+ assertFalse( Hibernate.isInitialized( hb.getUsers() ) );
+ //gavin = (User) s.createFilter( hb.getUsers(), "where index(this) = 'gavin'" ).uniqueResult();
+ Long size = (Long) s.createFilter( hb.getUsers(), "select count(*)" ).uniqueResult();
+ assertEquals( new Long(2), size );
+ assertFalse( Hibernate.isInitialized( hb.getUsers() ) );
+ s.delete(hb);
+ t.commit();
+ s.close();
+
+ }
+
+ public void testJoinFetchManyToMany() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Group hb = new Group("hibernate");
+ User gavin = new User("gavin");
+ User max = new User("max");
+ hb.getUsers().put("gavin", gavin);
+ hb.getUsers().put("max", max);
+ gavin.getGroups().put("hibernate", hb);
+ max.getGroups().put("hibernate", hb);
+ s.persist(hb);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ hb = (Group) s.get(Group.class, "hibernate");
+ assertTrue( Hibernate.isInitialized( hb.getUsers() ) );
+ gavin = (User) hb.getUsers().get("gavin");
+ assertFalse( Hibernate.isInitialized( gavin.getGroups() ) );
+ max = (User) s.get(User.class, "max");
+ assertFalse( Hibernate.isInitialized( max.getGroups() ) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ hb = (Group) s.createCriteria(Group.class)
+ .setFetchMode("users", FetchMode.JOIN)
+ .setFetchMode("users.groups", FetchMode.JOIN)
+ .uniqueResult();
+ assertTrue( Hibernate.isInitialized( hb.getUsers() ) );
+ gavin = (User) hb.getUsers().get("gavin");
+ assertTrue( Hibernate.isInitialized( gavin.getGroups() ) );
+ max = (User) s.get(User.class, "max");
+ assertTrue( Hibernate.isInitialized( max.getGroups() ) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.delete(hb);
+ t.commit();
+ s.close();
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/JoinFetchTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/User.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/User.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/User.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,33 @@
+//$Id: User.java 7175 2005-06-17 05:23:15Z oneovthafew $
+package org.hibernate.test.joinfetch;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class User {
+ private String name;
+ private Map groups = new HashMap();
+
+ public User(String name) {
+ this.name = name;
+ }
+
+ User() {}
+
+ public Map getGroups() {
+ return groups;
+ }
+
+ public void setGroups(Map groups) {
+ this.groups = groups;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/User.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/UserGroup.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/UserGroup.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/UserGroup.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+
+-->
+
+<hibernate-mapping
+ package="org.hibernate.test.joinfetch"
+ default-access="field">
+
+ <class name="User" table="AuctionUsers">
+ <id name="name"/>
+ <map name="groups" table="AuctionUsersGroups" fetch="join" order-by="groupName">
+ <key column="`userName`"/>
+ <map-key formula="groupName" type="string"/>
+ <many-to-many column="groupName" class="Group"/>
+ </map>
+ </class>
+
+ <class name="Group" table="AuctionGroups">
+ <id name="name"/>
+ <map name="users" table="AuctionUsersGroups" fetch="join" order-by="`userName`" inverse="true" cascade="all">
+ <key column="groupName"/>
+ <map-key formula="`userName`" type="string"/>
+ <many-to-many column="`userName`" class="User"/>
+ </map>
+ </class>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/joinfetch/UserGroup.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/AbstractJPATest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/AbstractJPATest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/AbstractJPATest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,149 @@
+package org.hibernate.test.jpa;
+
+import org.hibernate.test.TestCase;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.proxy.EntityNotFoundDelegate;
+import org.hibernate.event.def.DefaultPersistEventListener;
+import org.hibernate.event.def.DefaultAutoFlushEventListener;
+import org.hibernate.event.def.DefaultFlushEventListener;
+import org.hibernate.event.def.DefaultFlushEntityEventListener;
+import org.hibernate.event.AutoFlushEventListener;
+import org.hibernate.event.FlushEventListener;
+import org.hibernate.event.PersistEventListener;
+import org.hibernate.event.FlushEntityEventListener;
+import org.hibernate.engine.CascadingAction;
+import org.hibernate.util.IdentityMap;
+import org.hibernate.junit.functional.FunctionalTestCase;
+
+import java.io.Serializable;
+
+/**
+ * An abstract test for all JPA spec related tests.
+ *
+ * @author Steve Ebersole
+ */
+public class AbstractJPATest extends FunctionalTestCase {
+ public AbstractJPATest(String name) {
+ super( name );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "jpa/Part.hbm.xml", "jpa/Item.hbm.xml", "jpa/MyEntity.hbm.xml" };
+ }
+
+ public void configure(Configuration cfg) {
+ super.configure( cfg );
+ cfg.setProperty( Environment.JPAQL_STRICT_COMPLIANCE, "true" );
+ cfg.setProperty( Environment.USE_SECOND_LEVEL_CACHE, "false" );
+ cfg.setEntityNotFoundDelegate( new JPAEntityNotFoundDelegate() );
+ cfg.getEventListeners().setPersistEventListeners( buildPersistEventListeners() );
+ cfg.getEventListeners().setPersistOnFlushEventListeners( buildPersisOnFlushEventListeners() );
+ cfg.getEventListeners().setAutoFlushEventListeners( buildAutoFlushEventListeners() );
+ cfg.getEventListeners().setFlushEventListeners( buildFlushEventListeners() );
+ cfg.getEventListeners().setFlushEntityEventListeners( buildFlushEntityEventListeners() );
+ }
+
+ public String getCacheConcurrencyStrategy() {
+ // no second level caching
+ return null;
+ }
+
+
+ // mimic specific exception aspects of the JPA environment ~~~~~~~~~~~~~~~~
+
+ private static class JPAEntityNotFoundDelegate implements EntityNotFoundDelegate {
+ public void handleEntityNotFound(String entityName, Serializable id) {
+ throw new EntityNotFoundException( entityName, id );
+ }
+ }
+
+ /**
+ * Mimic the JPA EntityNotFoundException.
+ */
+ public static class EntityNotFoundException extends RuntimeException {
+ private final String entityName;
+ private final Serializable id;
+
+ public EntityNotFoundException(String entityName, Serializable id) {
+ this( "unable to locate specified entity", entityName, id );
+ }
+
+ public EntityNotFoundException(String message, String entityName, Serializable id) {
+ super( message );
+ this.entityName = entityName;
+ this.id = id;
+ }
+
+ public String getEntityName() {
+ return entityName;
+ }
+
+ public Serializable getId() {
+ return id;
+ }
+ }
+
+
+ // mimic specific event aspects of the JPA environment ~~~~~~~~~~~~~~~~~~~~
+
+ protected PersistEventListener[] buildPersistEventListeners() {
+ return new PersistEventListener[] { new JPAPersistEventListener() };
+ }
+
+ protected PersistEventListener[] buildPersisOnFlushEventListeners() {
+ return new PersistEventListener[] { new JPAPersistOnFlushEventListener() };
+ }
+
+ protected AutoFlushEventListener[] buildAutoFlushEventListeners() {
+ return new AutoFlushEventListener[] { JPAAutoFlushEventListener.INSTANCE };
+ }
+
+ protected FlushEventListener[] buildFlushEventListeners() {
+ return new FlushEventListener[] { JPAFlushEventListener.INSTANCE };
+ }
+
+ protected FlushEntityEventListener[] buildFlushEntityEventListeners() {
+ return new FlushEntityEventListener[] { new JPAFlushEntityEventListener() };
+ }
+
+ public static class JPAPersistEventListener extends DefaultPersistEventListener {
+ // overridden in JPA impl for entity callbacks...
+ }
+
+ public static class JPAPersistOnFlushEventListener extends JPAPersistEventListener {
+ protected CascadingAction getCascadeAction() {
+ return CascadingAction.PERSIST_ON_FLUSH;
+ }
+ }
+
+ public static class JPAAutoFlushEventListener extends DefaultAutoFlushEventListener {
+ // not sure why EM code has this ...
+ public static final AutoFlushEventListener INSTANCE = new JPAAutoFlushEventListener();
+
+ protected CascadingAction getCascadingAction() {
+ return CascadingAction.PERSIST_ON_FLUSH;
+ }
+
+ protected Object getAnything() {
+ return IdentityMap.instantiate( 10 );
+ }
+ }
+
+ public static class JPAFlushEventListener extends DefaultFlushEventListener {
+ // not sure why EM code has this ...
+ public static final FlushEventListener INSTANCE = new JPAFlushEventListener();
+
+ protected CascadingAction getCascadingAction() {
+ return CascadingAction.PERSIST_ON_FLUSH;
+ }
+
+ protected Object getAnything() {
+ return IdentityMap.instantiate( 10 );
+ }
+ }
+
+ public static class JPAFlushEntityEventListener extends DefaultFlushEntityEventListener {
+ // in JPA, used mainly for preUpdate callbacks...
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/Item.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/Item.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/Item.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.jpa">
+
+ <class name="Item" table="EJB3_ITEM">
+ <id name="id" column="ITEM_ID" type="long">
+ <generator class="increment"/>
+ </id>
+ <version name="version" column="VERS" type="long"/>
+ <property name="name" column="NAME" not-null="true"/>
+ <!-- modeled as many-to-one even though, yes, in real life would normally be many-to-many -->
+ <set name="parts" cascade="all" fetch="subselect" inverse="true">
+ <key column="ITEM_ID"/>
+ <one-to-many class="Part"/>
+ </set>
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/Item.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/Item.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/Item.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,53 @@
+package org.hibernate.test.jpa;
+
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * @author Steve Ebersole
+ */
+public class Item {
+ private Long id;
+ private String name;
+ private long version;
+ private Set parts = new HashSet();
+
+ public Item() {
+ }
+
+ public Item(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;
+ }
+
+ public long getVersion() {
+ return version;
+ }
+
+ public void setVersion(long version) {
+ this.version = version;
+ }
+
+ public Set getParts() {
+ return parts;
+ }
+
+ public void setParts(Set parts) {
+ this.parts = parts;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/JPAComplianceSuite.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/JPAComplianceSuite.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/JPAComplianceSuite.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,30 @@
+package org.hibernate.test.jpa;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.hibernate.test.jpa.lock.JPALockTest;
+import org.hibernate.test.jpa.lock.RepeatableReadTest;
+import org.hibernate.test.jpa.removed.RemovedEntityTest;
+import org.hibernate.test.jpa.proxy.JPAProxyTest;
+import org.hibernate.test.jpa.fetch.FetchingTest;
+import org.hibernate.test.jpa.ql.JPAQLComplianceTest;
+import org.hibernate.test.jpa.ql.NativeQueryTest;
+import org.hibernate.test.jpa.cascade.CascadeTest;
+
+/**
+ * @author Steve Ebersole
+ */
+public class JPAComplianceSuite {
+ public static Test suite() {
+ TestSuite suite = new TestSuite( "JPA-compliance tests");
+ suite.addTest( CascadeTest.suite() );
+ suite.addTest( FetchingTest.suite() );
+ suite.addTest( JPALockTest.suite() );
+ suite.addTest( RepeatableReadTest.suite() );
+ suite.addTest( JPAProxyTest.suite() );
+ suite.addTest( JPAQLComplianceTest.suite() );
+ suite.addTest( NativeQueryTest.suite() );
+ suite.addTest( RemovedEntityTest.suite() );
+ return suite;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/MyEntity.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/MyEntity.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/MyEntity.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+
+<!DOCTYPE hibernate-mapping SYSTEM "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
+
+<hibernate-mapping package="org.hibernate.test.jpa">
+
+ <class name="MyEntity" table="JPA_MYENTITY" discriminator-value="E">
+ <id name="id" column="ID" type="long">
+ <generator class="increment"/>
+ </id>
+ <discriminator column="TYPE" />
+ <property name="name" type="string"/>
+ <many-to-one name="other" class="MyEntity" />
+ <subclass name="MySubclassEntity" discriminator-value="S">
+ <property name="someSubProperty"/>
+ </subclass>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/MyEntity.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/MyEntity.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/MyEntity.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,36 @@
+package org.hibernate.test.jpa;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class MyEntity {
+ private Long id;
+ private String name;
+ private MyEntity other;
+
+ 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 MyEntity getOther() {
+ return other;
+ }
+
+ public void setOther(MyEntity other) {
+ this.other = other;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/MySubclassEntity.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/MySubclassEntity.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/MySubclassEntity.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,18 @@
+package org.hibernate.test.jpa;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class MySubclassEntity extends MyEntity {
+ private String someSubProperty;
+
+ public String getSomeSubProperty() {
+ return someSubProperty;
+ }
+
+ public void setSomeSubProperty(String someSubProperty) {
+ this.someSubProperty = someSubProperty;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/Part.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/Part.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/Part.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.jpa">
+
+ <class name="Part" table="EJB3_PART">
+ <id name="id" column="PART_ID" type="long">
+ <generator class="increment"/>
+ </id>
+ <many-to-one name="item" class="Item" column="ITEM_ID" cascade="save-update, lock" not-null="true"/>
+ <property name="name" column="NAME" not-null="true" type="string"/>
+ <property name="stockNumber" column="STOCK_NUM" not-null="true" type="string"/>
+ <property name="unitPrice" column="UNIT_PRICE" not-null="true" type="big_decimal"/>
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/Part.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/Part.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/Part.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,66 @@
+package org.hibernate.test.jpa;
+
+import java.math.BigDecimal;
+
+/**
+ * @author Steve Ebersole
+ */
+public class Part {
+ private Long id;
+ private Item item;
+ private String name;
+ private String stockNumber;
+ private BigDecimal unitPrice;
+
+ public Part() {
+ }
+
+ public Part(Item item, String name, String stockNumber, BigDecimal unitPrice) {
+ this.item = item;
+ this.name = name;
+ this.stockNumber = stockNumber;
+ this.unitPrice = unitPrice;
+
+ this.item.getParts().add( this );
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ private void setId(Long id) {
+ this.id = id;
+ }
+
+ public Item getItem() {
+ return item;
+ }
+
+ private void setItem(Item item) {
+ this.item = item;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getStockNumber() {
+ return stockNumber;
+ }
+
+ public void setStockNumber(String stockNumber) {
+ this.stockNumber = stockNumber;
+ }
+
+ public BigDecimal getUnitPrice() {
+ return unitPrice;
+ }
+
+ public void setUnitPrice(BigDecimal unitPrice) {
+ this.unitPrice = unitPrice;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/CascadeTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/CascadeTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/CascadeTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,321 @@
+package org.hibernate.test.jpa.cascade;
+
+import org.hibernate.test.jpa.AbstractJPATest;
+import org.hibernate.Session;
+import org.hibernate.TransientObjectException;
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.engine.Status;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.event.FlushEntityEventListener;
+import org.hibernate.event.FlushEntityEvent;
+import org.hibernate.cfg.Configuration;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import junit.framework.Test;
+
+/**
+ * According to the JPA spec, persist()ing an entity should throw an exception
+ * when said entity contains a reference to a transient entity through a mapped
+ * association where that association is not marked for cascading the persist
+ * operation.
+ * <p/>
+ * This test-case tests that requirement in the various association style
+ * scenarios such as many-to-one, one-to-one, many-to-one (property-ref),
+ * one-to-one (property-ref). Additionally, it performs each of these tests
+ * in both generated and assigned identifier usages...
+ *
+ * @author Steve Ebersole
+ */
+public class CascadeTest extends AbstractJPATest {
+
+ public static final Log log = LogFactory.getLog( CascadeTest.class );
+
+ public CascadeTest(String name) {
+ super( name );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "jpa/cascade/ParentChild.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( CascadeTest.class );
+ }
+
+ public void testManyToOneGeneratedIdsOnSave() {
+ // NOTES: Child defines a many-to-one back to its Parent. This
+ // association does not define persist cascading (which is natural;
+ // a child should not be able to create its parent).
+ try {
+ Session s = openSession();
+ s.beginTransaction();
+ Parent p = new Parent( "parent" );
+ Child c = new Child( "child" );
+ c.setParent( p );
+ s.save( c );
+ try {
+ s.getTransaction().commit();
+ fail( "expecting TransientObjectException on flush" );
+ }
+ catch( TransientObjectException e ) {
+ // expected result
+ log.trace( "handled expected exception", e );
+ s.getTransaction().rollback();
+ }
+ finally {
+ s.close();
+ }
+ }
+ finally {
+ cleanupData();
+ }
+ }
+
+ public void testManyToOneGeneratedIds() {
+ // NOTES: Child defines a many-to-one back to its Parent. This
+ // association does not define persist cascading (which is natural;
+ // a child should not be able to create its parent).
+ try {
+ Session s = openSession();
+ s.beginTransaction();
+ Parent p = new Parent( "parent" );
+ Child c = new Child( "child" );
+ c.setParent( p );
+ s.persist( c );
+ try {
+ s.getTransaction().commit();
+ fail( "expecting TransientObjectException on flush" );
+ }
+ catch( TransientObjectException e ) {
+ // expected result
+ log.trace( "handled expected exception", e );
+ s.getTransaction().rollback();
+ }
+ finally {
+ s.close();
+ }
+ }
+ finally {
+ cleanupData();
+ }
+ }
+
+ public void testManyToOneAssignedIds() {
+ // NOTES: Child defines a many-to-one back to its Parent. This
+ // association does not define persist cascading (which is natural;
+ // a child should not be able to create its parent).
+ try {
+ Session s = openSession();
+ s.beginTransaction();
+ ParentAssigned p = new ParentAssigned( new Long( 1 ), "parent" );
+ ChildAssigned c = new ChildAssigned( new Long( 2 ), "child" );
+ c.setParent( p );
+ s.persist( c );
+ try {
+ s.getTransaction().commit();
+ fail( "expecting TransientObjectException on flush" );
+ }
+ catch( TransientObjectException e ) {
+ // expected result
+ log.trace( "handled expected exception", e );
+ s.getTransaction().rollback();
+ }
+ finally {
+ s.close();
+ }
+ }
+ finally {
+ cleanupData();
+ }
+ }
+
+ public void testOneToOneGeneratedIds() {
+ try {
+ Session s = openSession();
+ s.beginTransaction();
+ Parent p = new Parent( "parent" );
+ ParentInfo info = new ParentInfo( "xyz" );
+ p.setInfo( info );
+ info.setOwner( p );
+ s.persist( p );
+ try {
+ s.getTransaction().commit();
+ fail( "expecting TransientObjectException on flush" );
+ }
+ catch( TransientObjectException e ) {
+ // expected result
+ log.trace( "handled expected exception", e );
+ s.getTransaction().rollback();
+ }
+ finally {
+ s.close();
+ }
+ }
+ finally {
+ cleanupData();
+ }
+ }
+
+ public void testOneToOneAssignedIds() {
+ try {
+ Session s = openSession();
+ s.beginTransaction();
+ ParentAssigned p = new ParentAssigned( new Long( 1 ), "parent" );
+ ParentInfoAssigned info = new ParentInfoAssigned( "something secret" );
+ p.setInfo( info );
+ info.setOwner( p );
+ s.persist( p );
+ try {
+ s.getTransaction().commit();
+ fail( "expecting TransientObjectException on flush" );
+ }
+ catch( TransientObjectException e ) {
+ // expected result
+ log.trace( "handled expected exception", e );
+ s.getTransaction().rollback();
+ }
+ finally {
+ s.close();
+ }
+ }
+ finally {
+ cleanupData();
+ }
+ }
+
+ public void testManyToOnePropertyRefGeneratedIds() {
+ try {
+ Session s = openSession();
+ s.beginTransaction();
+ Parent p = new Parent( "parent" );
+ Other other = new Other();
+ other.setOwner( p );
+ s.persist( other );
+ try {
+ s.getTransaction().commit();
+ fail( "expecting TransientObjectException on flush" );
+ }
+ catch( TransientObjectException e ) {
+ // expected result
+ log.trace( "handled expected exception", e );
+ s.getTransaction().rollback();
+ }
+ finally {
+ s.close();
+ }
+ }
+ finally {
+ cleanupData();
+ }
+ }
+
+ public void testManyToOnePropertyRefAssignedIds() {
+ try {
+ Session s = openSession();
+ s.beginTransaction();
+ ParentAssigned p = new ParentAssigned( new Long( 1 ), "parent" );
+ OtherAssigned other = new OtherAssigned( new Long( 2 ) );
+ other.setOwner( p );
+ s.persist( other );
+ try {
+ s.getTransaction().commit();
+ fail( "expecting TransientObjectException on flush" );
+ }
+ catch( TransientObjectException e ) {
+ // expected result
+ log.trace( "handled expected exception", e );
+ s.getTransaction().rollback();
+ }
+ finally {
+ s.close();
+ }
+ }
+ finally {
+ cleanupData();
+ }
+ }
+
+ public void testOneToOnePropertyRefGeneratedIds() {
+ try {
+ Session s = openSession();
+ s.beginTransaction();
+ Child c2 = new Child( "c2" );
+ ChildInfo info = new ChildInfo( "blah blah blah" );
+ c2.setInfo( info );
+ info.setOwner( c2 );
+ s.persist( c2 );
+ try {
+ s.getTransaction().commit();
+ fail( "expecting TransientObjectException on flush" );
+ }
+ catch( TransientObjectException e ) {
+ // expected result
+ log.trace( "handled expected exception : " + e );
+ s.getTransaction().rollback();
+ }
+ finally {
+ s.close();
+ }
+ }
+ finally {
+ cleanupData();
+ }
+ }
+
+ public void testOneToOnePropertyRefAssignedIds() {
+ try {
+ Session s = openSession();
+ s.beginTransaction();
+ ChildAssigned c2 = new ChildAssigned( new Long( 3 ), "c3" );
+ ChildInfoAssigned info = new ChildInfoAssigned( new Long( 4 ), "blah blah blah" );
+ c2.setInfo( info );
+ info.setOwner( c2 );
+ s.persist( c2 );
+ try {
+ s.getTransaction().commit();
+ fail( "expecting TransientObjectException on flush" );
+ }
+ catch( TransientObjectException e ) {
+ // expected result
+ log.trace( "handled expected exception : " + e );
+ s.getTransaction().rollback();
+ }
+ finally {
+ s.close();
+ }
+ }
+ finally {
+ cleanupData();
+ }
+ }
+
+
+ private void cleanupData() {
+ Session s = null;
+ try {
+ s = openSession();
+ s.beginTransaction();
+ s.createQuery( "delete ChildInfoAssigned" ).executeUpdate();
+ s.createQuery( "delete ChildAssigned" ).executeUpdate();
+ s.createQuery( "delete ParentAssigned" ).executeUpdate();
+ s.createQuery( "delete ChildInfoAssigned" ).executeUpdate();
+ s.createQuery( "delete ChildAssigned" ).executeUpdate();
+ s.createQuery( "delete ParentAssigned" ).executeUpdate();
+ s.getTransaction().commit();
+ }
+ catch( Throwable t ) {
+ log.warn( "unable to cleanup test data [" + fullTestName() + "] : " + t );
+ }
+ finally {
+ if ( s != null ) {
+ try {
+ s.close();
+ }
+ catch( Throwable ignore ) {
+ }
+ }
+ }
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/Child.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/Child.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/Child.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,52 @@
+package org.hibernate.test.jpa.cascade;
+
+/**
+ * todo: describe Child
+ *
+ * @author Steve Ebersole
+ */
+public class Child {
+ private Long id;
+ private String name;
+ private Parent parent;
+ private ChildInfo info;
+
+ public Child() {
+ }
+
+ public Child(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;
+ }
+
+ public Parent getParent() {
+ return parent;
+ }
+
+ public void setParent(Parent parent) {
+ this.parent = parent;
+ }
+
+ public ChildInfo getInfo() {
+ return info;
+ }
+
+ public void setInfo(ChildInfo info) {
+ this.info = info;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/ChildAssigned.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/ChildAssigned.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/ChildAssigned.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,49 @@
+package org.hibernate.test.jpa.cascade;
+
+/**
+ * Child, but with an assigned identifier.
+ *
+ * @author Steve Ebersole
+ */
+public class ChildAssigned {
+ private Long id;
+ private String name;
+ private ParentAssigned parent;
+ private ChildInfoAssigned info;
+
+ public ChildAssigned() {
+ }
+
+ public ChildAssigned(Long id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public ParentAssigned getParent() {
+ return parent;
+ }
+
+ public void setParent(ParentAssigned parent) {
+ this.parent = parent;
+ }
+
+ public ChildInfoAssigned getInfo() {
+ return info;
+ }
+
+ public void setInfo(ChildInfoAssigned info) {
+ this.info = info;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/ChildInfo.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/ChildInfo.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/ChildInfo.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,39 @@
+package org.hibernate.test.jpa.cascade;
+
+/**
+ * todo: describe ChildInfo
+ *
+ * @author Steve Ebersole
+ */
+public class ChildInfo {
+ private Long id;
+ private Child owner;
+ private String info;
+
+ public ChildInfo() {
+ }
+
+ public ChildInfo(String info) {
+ this.info = info;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public Child getOwner() {
+ return owner;
+ }
+
+ public void setOwner(Child owner) {
+ this.owner = owner;
+ }
+
+ public String getInfo() {
+ return info;
+ }
+
+ public void setInfo(String info) {
+ this.info = info;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/ChildInfoAssigned.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/ChildInfoAssigned.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/ChildInfoAssigned.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,40 @@
+package org.hibernate.test.jpa.cascade;
+
+/**
+ * todo: describe ChildInfo
+ *
+ * @author Steve Ebersole
+ */
+public class ChildInfoAssigned {
+ private Long id;
+ private ChildAssigned owner;
+ private String info;
+
+ public ChildInfoAssigned() {
+ }
+
+ public ChildInfoAssigned(Long id, String info) {
+ this.id = id;
+ this.info = info;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public ChildAssigned getOwner() {
+ return owner;
+ }
+
+ public void setOwner(ChildAssigned owner) {
+ this.owner = owner;
+ }
+
+ public String getInfo() {
+ return info;
+ }
+
+ public void setInfo(String info) {
+ this.info = info;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/Other.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/Other.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/Other.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,23 @@
+package org.hibernate.test.jpa.cascade;
+
+/**
+ * todo: describe Other
+ *
+ * @author Steve Ebersole
+ */
+public class Other {
+ private Long id;
+ private Parent owner;
+
+ public Long getId() {
+ return id;
+ }
+
+ public Parent getOwner() {
+ return owner;
+ }
+
+ public void setOwner(Parent owner) {
+ this.owner = owner;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/OtherAssigned.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/OtherAssigned.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/OtherAssigned.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,30 @@
+package org.hibernate.test.jpa.cascade;
+
+/**
+ * todo: describe Other
+ *
+ * @author Steve Ebersole
+ */
+public class OtherAssigned {
+ private Long id;
+ private ParentAssigned owner;
+
+ public OtherAssigned() {
+ }
+
+ public OtherAssigned(Long id) {
+ this.id = id;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public ParentAssigned getOwner() {
+ return owner;
+ }
+
+ public void setOwner(ParentAssigned owner) {
+ this.owner = owner;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/Parent.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/Parent.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/Parent.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,43 @@
+package org.hibernate.test.jpa.cascade;
+
+/**
+ * todo: describe Parent
+ *
+ * @author Steve Ebersole
+ */
+public class Parent {
+ private Long id;
+ private String name;
+ private ParentInfo info;
+
+ public Parent() {
+ }
+
+ public Parent(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;
+ }
+
+ public ParentInfo getInfo() {
+ return info;
+ }
+
+ public void setInfo(ParentInfo info) {
+ this.info = info;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/ParentAssigned.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/ParentAssigned.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/ParentAssigned.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,40 @@
+package org.hibernate.test.jpa.cascade;
+
+/**
+ * Parent, but with an assigned identifier.
+ *
+ * @author Steve Ebersole
+ */
+public class ParentAssigned {
+ private Long id;
+ private String name;
+ private ParentInfoAssigned info;
+
+ public ParentAssigned() {
+ }
+
+ public ParentAssigned(Long id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public ParentInfoAssigned getInfo() {
+ return info;
+ }
+
+ public void setInfo(ParentInfoAssigned info) {
+ this.info = info;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/ParentChild.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/ParentChild.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/ParentChild.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,97 @@
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.jpa.cascade" default-access="field">
+
+ <!-- +++++++++++++ Generated ids ++++++++++++++++++++++ -->
+
+ <class name="Parent" table="PARENT">
+ <id name="id" column="ID" type="long">
+ <generator class="increment"/>
+ </id>
+ <property name="name" type="string" unique="true"/>
+ <one-to-one name="info" class="ParentInfo" cascade="none"/>
+ </class>
+
+ <class name="Child" table="CHILD">
+ <id name="id" column="ID" type="long">
+ <generator class="increment"/>
+ </id>
+ <property name="name" type="string"/>
+ <many-to-one name="parent" class="Parent" cascade="none"/>
+ <one-to-one name="info" property-ref="owner" class="ChildInfo" cascade="none"/>
+ </class>
+
+ <class name="ParentInfo" table="P_INFO">
+ <id name="id" column="ID" type="long">
+ <generator class="foreign">
+ <param name="property">owner</param>
+ </generator>
+ </id>
+ <property name="info" column="INFO" type="string"/>
+ <one-to-one name="owner" class="Parent" constrained="true" cascade="none"/>
+ </class>
+
+ <class name="ChildInfo" table="C_INFO">
+ <id name="id" column="ID" type="long">
+ <generator class="increment"/>
+ </id>
+ <property name="info" column="INFO" type="string"/>
+ <many-to-one name="owner" class="Child" column="CHILD_ID" cascade="none"/>
+ </class>
+
+ <class name="Other" table="OTHER">
+ <id name="id" column="ID" type="long">
+ <generator class="increment"/>
+ </id>
+ <many-to-one name="owner" column="OWNER_NAME" class="Parent" property-ref="name" cascade="none"/>
+ </class>
+
+
+
+ <!-- +++++++++++++ Assigned ids ++++++++++++++++++++++ -->
+
+ <class name="ParentAssigned" table="PARENT_A">
+ <id name="id" column="ID" type="long">
+ <generator class="assigned"/>
+ </id>
+ <property name="name" type="string" unique="true"/>
+ <one-to-one name="info" class="ParentInfoAssigned"/>
+ </class>
+
+ <class name="ChildAssigned" table="CHILD_A">
+ <id name="id" column="ID" type="long">
+ <generator class="assigned"/>
+ </id>
+ <property name="name" type="string"/>
+ <many-to-one name="parent" class="ParentAssigned" cascade="none"/>
+ <one-to-one name="info" property-ref="owner" class="ChildInfoAssigned" cascade="none"/>
+ </class>
+
+ <class name="ParentInfoAssigned" table="P_INFO_A">
+ <id name="id" column="ID" type="long">
+ <generator class="foreign">
+ <param name="property">owner</param>
+ </generator>
+ </id>
+ <property name="info" column="INFO" type="string"/>
+ <one-to-one name="owner" class="ParentAssigned" constrained="true"/>
+ </class>
+
+ <class name="ChildInfoAssigned" table="C_INFO_A">
+ <id name="id" column="ID" type="long">
+ <generator class="assigned"/>
+ </id>
+ <property name="info" column="INFO" type="string"/>
+ <many-to-one name="owner" class="ChildAssigned" column="CHILD_ID" cascade="none"/>
+ </class>
+
+ <class name="OtherAssigned" table="OTHER_A">
+ <id name="id" column="ID" type="long">
+ <generator class="assigned"/>
+ </id>
+ <many-to-one name="owner" column="OWNER_NAME" class="ParentAssigned" property-ref="name" cascade="none"/>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/ParentInfo.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/ParentInfo.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/ParentInfo.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,39 @@
+package org.hibernate.test.jpa.cascade;
+
+/**
+ * todo: describe ChildInfo
+ *
+ * @author Steve Ebersole
+ */
+public class ParentInfo {
+ private Long id;
+ private Parent owner;
+ private String info;
+
+ public ParentInfo() {
+ }
+
+ public ParentInfo(String info) {
+ this.info = info;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public Parent getOwner() {
+ return owner;
+ }
+
+ public void setOwner(Parent owner) {
+ this.owner = owner;
+ }
+
+ public String getInfo() {
+ return info;
+ }
+
+ public void setInfo(String info) {
+ this.info = info;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/ParentInfoAssigned.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/ParentInfoAssigned.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/cascade/ParentInfoAssigned.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,39 @@
+package org.hibernate.test.jpa.cascade;
+
+/**
+ * todo: describe ChildInfo
+ *
+ * @author Steve Ebersole
+ */
+public class ParentInfoAssigned {
+ private Long id;
+ private ParentAssigned owner;
+ private String info;
+
+ public ParentInfoAssigned() {
+ }
+
+ public ParentInfoAssigned(String info) {
+ this.info = info;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public ParentAssigned getOwner() {
+ return owner;
+ }
+
+ public void setOwner(ParentAssigned owner) {
+ this.owner = owner;
+ }
+
+ public String getInfo() {
+ return info;
+ }
+
+ public void setInfo(String info) {
+ this.info = info;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/fetch/FetchingTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/fetch/FetchingTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/fetch/FetchingTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,84 @@
+package org.hibernate.test.jpa.fetch;
+
+import java.util.Date;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.test.jpa.AbstractJPATest;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class FetchingTest extends AbstractJPATest {
+
+ public FetchingTest(String x) {
+ super( x );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "jpa/fetch/Person.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( FetchingTest.class );
+ }
+
+ public void testLazy() throws Exception {
+ Session s;
+ Transaction tx;
+ s = openSession();
+ tx = s.beginTransaction();
+ Person p = new Person( "Gavin", "King", "JBoss Inc" );
+ Stay stay = new Stay( p, new Date(), new Date(), "A380", "Blah", "Blah" );
+ p.addStay( stay );
+ s.persist( p );
+ tx.commit();
+ s.clear();
+ tx = s.beginTransaction();
+ p = (Person) s.createQuery( "select p from Person p where p.firstName = :name" )
+ .setParameter( "name", "Gavin" ).uniqueResult();
+ assertFalse( Hibernate.isInitialized( p.getStays() ) );
+ s.delete( p );
+ tx.commit();
+ s.close();
+ }
+
+ public void testHibernateFetchingLazy() throws Exception {
+ Session s;
+ Transaction tx;
+ s = openSession();
+ tx = s.beginTransaction();
+ Person p = new Person( "Gavin", "King", "JBoss Inc" );
+ Stay stay = new Stay( null, new Date(), new Date(), "A380", "Blah", "Blah" );
+ Stay stay2 = new Stay( null, new Date(), new Date(), "A320", "Blah", "Blah" );
+ Stay stay3 = new Stay( null, new Date(), new Date(), "A340", "Blah", "Blah" );
+ stay.setOldPerson( p );
+ stay2.setVeryOldPerson( p );
+ stay3.setVeryOldPerson( p );
+ p.addOldStay( stay );
+ p.addVeryOldStay( stay2 );
+ p.addVeryOldStay( stay3 );
+ s.persist( p );
+ tx.commit();
+ s.clear();
+ tx = s.beginTransaction();
+ p = (Person) s.createQuery( "select p from Person p where p.firstName = :name" )
+ .setParameter( "name", "Gavin" ).uniqueResult();
+ assertFalse( Hibernate.isInitialized( p.getOldStays() ) );
+ assertEquals( 1, p.getOldStays().size() );
+ assertFalse( "lazy extra is failing", Hibernate.isInitialized( p.getOldStays() ) );
+ s.clear();
+ stay = (Stay) s.get( Stay.class, stay.getId() );
+ assertTrue( ! Hibernate.isInitialized( stay.getOldPerson() ) );
+ s.clear();
+ stay3 = (Stay) s.get( Stay.class, stay3.getId() );
+ assertTrue( "FetchMode.JOIN should overrides lazy options", Hibernate.isInitialized( stay3.getVeryOldPerson() ) );
+ s.delete( stay3.getVeryOldPerson() );
+ tx.commit();
+ s.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/fetch/Person.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/fetch/Person.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/fetch/Person.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,48 @@
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.jpa.fetch">
+
+ <class name="Person" table="PERSON">
+ <id name="id" column="ID" type="long">
+ <generator class="increment"/>
+ </id>
+ <property name="firstName"/>
+ <property name="lastName"/>
+ <property name="companyName"/>
+
+ <bag name="stays" cascade="all" lazy="true" inverse="true">
+ <key column="PERS_ID"/>
+ <one-to-many class="Stay"/>
+ </bag>
+
+ <bag name="oldStays" cascade="all" lazy="extra" fetch="subselect" inverse="true">
+ <key column="OLD_PERS_ID"/>
+ <one-to-many class="Stay"/>
+ </bag>
+
+ <bag name="veryOldStays" cascade="all" lazy="true" fetch="select" inverse="true">
+ <key column="VERY_OLD_PERS_ID"/>
+ <one-to-many class="Stay"/>
+ </bag>
+ </class>
+
+ <class name="Stay" table="STAY">
+ <id name="id" column="ID" type="long">
+ <generator class="increment"/>
+ </id>
+
+ <property name="startDate"/>
+ <property name="endDate"/>
+ <property name="vessel"/>
+ <property name="authoriser"/>
+ <property name="comments"/>
+
+ <many-to-one name="person" column="PERS_ID" class="Person" cascade="all"/>
+ <many-to-one name="oldPerson" column="OLD_PERS_ID" class="Person" cascade="all" fetch="select"/>
+ <many-to-one name="veryOldPerson" column="VERY_OLD_PERS_ID" class="Person" cascade="all" fetch="join"/>
+
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/fetch/Person.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/fetch/Person.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/fetch/Person.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,136 @@
+package org.hibernate.test.jpa.fetch;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Date;
+import java.util.ArrayList;
+
+/**
+ * Copied over from annotations test suite...
+ *
+ * @author Emmanuel Bernard
+ */
+public class Person implements Serializable {
+
+ // member declaration
+ private Long id;
+ private String firstName;
+ private String lastName;
+ private String companyName;
+ private Collection stays;
+ private Collection oldStays;
+ private Collection veryOldStays;
+
+ // constructors
+ public Person() {
+ }
+
+ public Person(String firstName, String lastName, String companyName) {
+ this.firstName = firstName;
+ this.lastName = lastName;
+ this.companyName = companyName;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
+ public String getCompanyName() {
+ return companyName;
+ }
+
+ public void setCompanyName(String companyName) {
+ this.companyName = companyName;
+ }
+
+ public Collection getStays() {
+ return stays;
+ }
+
+ public void setStays(Collection stays) {
+ this.stays = stays;
+ }
+
+ public Collection getOldStays() {
+ return oldStays;
+ }
+
+ public void setOldStays(Collection oldStays) {
+ this.oldStays = oldStays;
+ }
+
+ public Collection getVeryOldStays() {
+ return veryOldStays;
+ }
+
+ public void setVeryOldStays(Collection veryOldStays) {
+ this.veryOldStays = veryOldStays;
+ }
+
+
+ // business logic
+ public void addStay(Date startDate, Date endDate, String vessel, String authoriser, String comments) {
+ Stay stay = new Stay( this, startDate, endDate, vessel, authoriser, comments );
+ addStay( stay );
+ }
+
+ public void addStay(Stay stay) {
+ Collection stays = getStays();
+ if ( stays == null ) {
+ stays = new ArrayList();
+ }
+ stays.add( stay );
+
+ this.stays = stays;
+ }
+
+ public void addOldStay(Date startDate, Date endDate, String vessel, String authoriser, String comments) {
+ Stay stay = new Stay( this, startDate, endDate, vessel, authoriser, comments );
+ addOldStay( stay );
+ }
+
+ public void addOldStay(Stay stay) {
+ Collection stays = getOldStays();
+ if ( stays == null ) {
+ stays = new ArrayList();
+ }
+ stays.add( stay );
+
+ this.oldStays = stays;
+ }
+
+ public void addVeryOldStay(Date startDate, Date endDate, String vessel, String authoriser, String comments) {
+ Stay stay = new Stay( this, startDate, endDate, vessel, authoriser, comments );
+ addVeryOldStay( stay );
+ }
+
+ public void addVeryOldStay(Stay stay) {
+ Collection stays = getVeryOldStays();
+ if ( stays == null ) {
+ stays = new ArrayList();
+ }
+ stays.add( stay );
+
+ this.veryOldStays = stays;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/fetch/Stay.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/fetch/Stay.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/fetch/Stay.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,107 @@
+package org.hibernate.test.jpa.fetch;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class Stay implements Serializable {
+
+ // member declaration
+ private Long id;
+ private Person person;
+ private Person oldPerson;
+ private Person veryOldPerson;
+ private Date startDate;
+ private Date endDate;
+ private String vessel;
+ private String authoriser;
+ private String comments;
+
+
+ // constructors
+ public Stay() {
+ }
+
+ public Stay(Person person, Date startDate, Date endDate, String vessel, String authoriser, String comments) {
+ this.authoriser = authoriser;
+ this.endDate = endDate;
+ this.person = person;
+ this.startDate = startDate;
+ this.vessel = vessel;
+ this.comments = comments;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public Person getPerson() {
+ return person;
+ }
+
+ public void setPerson(Person person) {
+ this.person = person;
+ }
+
+ public Person getOldPerson() {
+ return oldPerson;
+ }
+
+ public void setOldPerson(Person oldPerson) {
+ this.oldPerson = oldPerson;
+ }
+
+ public Person getVeryOldPerson() {
+ return veryOldPerson;
+ }
+
+ public void setVeryOldPerson(Person veryOldPerson) {
+ this.veryOldPerson = veryOldPerson;
+ }
+
+ public Date getStartDate() {
+ return startDate;
+ }
+
+ public void setStartDate(Date startDate) {
+ this.startDate = startDate;
+ }
+
+ public Date getEndDate() {
+ return endDate;
+ }
+
+ public void setEndDate(Date endDate) {
+ this.endDate = endDate;
+ }
+
+ public String getVessel() {
+ return vessel;
+ }
+
+ public void setVessel(String vessel) {
+ this.vessel = vessel;
+ }
+
+ public String getAuthoriser() {
+ return authoriser;
+ }
+
+ public void setAuthoriser(String authoriser) {
+ this.authoriser = authoriser;
+ }
+
+ public String getComments() {
+ return comments;
+ }
+
+ public void setComments(String comments) {
+ this.comments = comments;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/lock/JPALockTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/lock/JPALockTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/lock/JPALockTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,197 @@
+package org.hibernate.test.jpa.lock;
+
+import junit.framework.Test;
+
+import org.hibernate.LockMode;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.test.jpa.AbstractJPATest;
+import org.hibernate.test.jpa.Item;
+import org.hibernate.test.jpa.MyEntity;
+
+/**
+ * Tests specifically relating to section 3.3.5.3 [Lock Modes] of the
+ * JPA persistence specification (as of the <i>Proposed Final Draft</i>).
+ *
+ * @author Steve Ebersole
+ */
+public class JPALockTest extends AbstractJPATest {
+ public JPALockTest(String name) {
+ super( name );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( JPALockTest.class );
+ }
+
+ /**
+ * Test the equivalent of EJB3 LockModeType.READ
+ * <p/>
+ * From the spec:
+ * <p/>
+ * If transaction T1 calls lock(entity, LockModeType.READ) on a versioned object, the entity
+ * manager must ensure that neither of the following phenomena can occur:<ul>
+ * <li>P1 (Dirty read): Transaction T1 modifies a row. Another transaction T2 then reads that row and
+ * obtains the modified value, before T1 has committed or rolled back. Transaction T2 eventually
+ * commits successfully; it does not matter whether T1 commits or rolls back and whether it does
+ * so before or after T2 commits.
+ * <li>P2 (Non-repeatable read): Transaction T1 reads a row. Another transaction T2 then modifies or
+ * deletes that row, before T1 has committed. Both transactions eventually commit successfully.
+ * <p/>
+ * This will generally be achieved by the entity manager acquiring a lock on the underlying database row.
+ * Any such lock may be obtained immediately (so long as it is retained until commit completes), or the
+ * lock may be deferred until commit time (although even then it must be retained until the commit completes).
+ * Any implementation that supports repeatable reads in a way that prevents the above phenomena
+ * is permissible.
+ * <p/>
+ * The persistence implementation is not required to support calling lock(entity, LockMode-Type.READ)
+ * on a non-versioned object. When it cannot support such a lock call, it must throw the
+ * PersistenceException. When supported, whether for versioned or non-versioned objects, LockMode-Type.READ
+ * must always prevent the phenomena P1 and P2. Applications that call lock(entity, LockModeType.READ)
+ * on non-versioned objects will not be portable.
+ * <p/>
+ * Odd as it may sound, EJB3 LockModeType.READ actually maps to the Hibernate LockMode.UPGRADE
+ */
+ public void testLockModeTypeRead() {
+ if ( !readCommittedIsolationMaintained( "ejb3 lock tests" ) ) {
+ return;
+ }
+ if ( getDialect().doesReadCommittedCauseWritersToBlockReaders() ) {
+ reportSkip( "deadlock", "jpa read locking" );
+ return;
+ }
+
+ final String initialName = "lock test";
+ // set up some test data
+ Session s1 = getSessions().openSession();
+ Transaction t1 = s1.beginTransaction();
+ Item item = new Item();
+ item.setName( initialName );
+ s1.save( item );
+ t1.commit();
+ s1.close();
+
+ Long itemId = item.getId();
+
+ // perform the isolated update
+ s1 = getSessions().openSession();
+ t1 = s1.beginTransaction();
+ item = (Item) s1.get( Item.class, itemId );
+ s1.lock( item, LockMode.UPGRADE );
+ item.setName( "updated" );
+ s1.flush();
+
+ Session s2 = getSessions().openSession();
+ Transaction t2 = s2.beginTransaction();
+ Item item2 = (Item) s2.get( Item.class, itemId );
+ assertEquals( "isolation not maintained", initialName, item2.getName() );
+
+ t1.commit();
+ s1.close();
+
+ item2 = (Item) s2.get( Item.class, itemId );
+ assertEquals( "repeatable read not maintained", initialName, item2.getName() );
+ t2.commit();
+ s2.close();
+
+ s1 = getSessions().openSession();
+ t1 = s1.beginTransaction();
+ s1.delete( item );
+ t1.commit();
+ s1.close();
+ }
+
+ /**
+ * Test the equivalent of EJB3 LockModeType.WRITE
+ * <p/>
+ * From the spec:
+ * <p/>
+ * If transaction T1 calls lock(entity, LockModeType.WRITE) on a versioned object, the entity
+ * manager must avoid the phenomena P1 and P2 (as with LockModeType.READ) and must also force
+ * an update (increment) to the entity's version column. A forced version update may be performed immediately,
+ * or may be deferred until a flush or commit. If an entity is removed before a deferred version
+ * update was to have been applied, the forced version update is omitted, since the underlying database
+ * row no longer exists.
+ * <p/>
+ * The persistence implementation is not required to support calling lock(entity, LockMode-Type.WRITE)
+ * on a non-versioned object. When it cannot support a such lock call, it must throw the
+ * PersistenceException. When supported, whether for versioned or non-versioned objects, LockMode-Type.WRITE
+ * must always prevent the phenomena P1 and P2. For non-versioned objects, whether or
+ * not LockModeType.WRITE has any additional behaviour is vendor-specific. Applications that call
+ * lock(entity, LockModeType.WRITE) on non-versioned objects will not be portable.
+ * <p/>
+ * Due to the requirement that LockModeType.WRITE needs to force a version increment,
+ * a new Hibernate LockMode was added to support this behavior: {@link org.hibernate.LockMode#FORCE}.
+ */
+ public void testLockModeTypeWrite() {
+ if ( !readCommittedIsolationMaintained( "ejb3 lock tests" ) ) {
+ return;
+ }
+ if ( getDialect().doesReadCommittedCauseWritersToBlockReaders() ) {
+ reportSkip( "deadlock", "jpa write locking" );
+ return;
+ }
+ final String initialName = "lock test";
+ // set up some test data
+ Session s1 = getSessions().openSession();
+ Transaction t1 = s1.beginTransaction();
+ Item item = new Item();
+ item.setName( initialName );
+ s1.save( item );
+ MyEntity myEntity = new MyEntity();
+ myEntity.setName( "Test" );
+ s1.save( myEntity );
+ t1.commit();
+ s1.close();
+
+ Long itemId = item.getId();
+ long initialVersion = item.getVersion();
+
+ s1 = getSessions().openSession();
+ t1 = s1.beginTransaction();
+ item = (Item) s1.get( Item.class, itemId );
+ s1.lock( item, LockMode.FORCE );
+ assertEquals( "no forced version increment", initialVersion + 1, item.getVersion() );
+
+ myEntity = (MyEntity) s1.get( MyEntity.class, myEntity.getId() );
+ s1.lock( myEntity, LockMode.FORCE );
+ assertTrue( "LockMode.FORCE on a unversioned entity should degrade nicely to UPGRADE", true );
+
+ s1.lock( item, LockMode.FORCE );
+ assertEquals( "subsequent LockMode.FORCE did not no-op", initialVersion + 1, item.getVersion() );
+
+ Session s2 = getSessions().openSession();
+ Transaction t2 = s2.beginTransaction();
+ Item item2 = (Item) s2.get( Item.class, itemId );
+ assertEquals( "isolation not maintained", initialName, item2.getName() );
+
+ item.setName( "updated-1" );
+ s1.flush();
+ // currently an unfortunate side effect...
+ assertEquals( initialVersion + 2, item.getVersion() );
+
+ t1.commit();
+ s1.close();
+
+ item2.setName( "updated" );
+ try {
+ t2.commit();
+ fail( "optimisitc lock should have failed" );
+ }
+ catch (Throwable ignore) {
+ // expected behavior
+ t2.rollback();
+ }
+ finally {
+ s2.close();
+ }
+
+ s1 = getSessions().openSession();
+ t1 = s1.beginTransaction();
+ s1.delete( item );
+ s1.delete( myEntity );
+ t1.commit();
+ s1.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/lock/RepeatableReadTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/lock/RepeatableReadTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/lock/RepeatableReadTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,275 @@
+package org.hibernate.test.jpa.lock;
+
+import java.math.BigDecimal;
+
+import junit.framework.Test;
+
+import org.hibernate.LockMode;
+import org.hibernate.Session;
+import org.hibernate.StaleObjectStateException;
+import org.hibernate.Transaction;
+import org.hibernate.dialect.SQLServerDialect;
+import org.hibernate.exception.SQLGrammarException;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.test.jpa.AbstractJPATest;
+import org.hibernate.test.jpa.Item;
+import org.hibernate.test.jpa.Part;
+
+/**
+ * Test that the Hibernate Session complies with REPEATABLE_READ isolation
+ * semantics.
+ *
+ * @author Steve Ebersole
+ */
+public class RepeatableReadTest extends AbstractJPATest {
+
+ public RepeatableReadTest(String name) {
+ super( name );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( RepeatableReadTest.class );
+ }
+
+
+ // versioned entity tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public void testStaleVersionedInstanceFoundInQueryResult() {
+ if ( getDialect().doesReadCommittedCauseWritersToBlockReaders()) {
+ reportSkip( "lock blocking", "stale versioned instance" );
+ return;
+ }
+ String check = "EJB3 Specification";
+ Session s1 = getSessions().openSession();
+ Transaction t1 = s1.beginTransaction();
+ Item item = new Item( check );
+ s1.save( item );
+ t1.commit();
+ s1.close();
+
+ Long itemId = item.getId();
+ long initialVersion = item.getVersion();
+
+ // Now, open a new Session and re-load the item...
+ s1 = getSessions().openSession();
+ t1 = s1.beginTransaction();
+ item = ( Item ) s1.get( Item.class, itemId );
+
+ // now that the item is associated with the persistence-context of that session,
+ // open a new session and modify it "behind the back" of the first session
+ Session s2 = getSessions().openSession();
+ Transaction t2 = s2.beginTransaction();
+ Item item2 = ( Item ) s2.get( Item.class, itemId );
+ item2.setName( "EJB3 Persistence Spec" );
+ t2.commit();
+ s2.close();
+
+ // at this point, s1 now contains stale data, so try an hql query which
+ // returns said item and make sure we get the previously associated state
+ // (i.e., the old name and the old version)
+ item2 = ( Item ) s1.createQuery( "select i from Item i" ).list().get( 0 );
+ assertTrue( item == item2 );
+ assertEquals( "encountered non-repeatable read", check, item2.getName() );
+ assertEquals( "encountered non-repeatable read", initialVersion, item2.getVersion() );
+
+ t1.commit();
+ s1.close();
+
+ // clean up
+ s1 = getSessions().openSession();
+ t1 = s1.beginTransaction();
+ s1.createQuery( "delete Item" ).executeUpdate();
+ t1.commit();
+ s1.close();
+ }
+
+ public void testStaleVersionedInstanceFoundOnLock() {
+ if ( ! readCommittedIsolationMaintained( "repeatable read tests" ) ) {
+ return;
+ }
+ if ( getDialect().doesReadCommittedCauseWritersToBlockReaders()) {
+ reportSkip( "lock blocking", "stale versioned instance" );
+ return;
+ }
+ String check = "EJB3 Specification";
+ Session s1 = getSessions().openSession();
+ Transaction t1 = s1.beginTransaction();
+ Item item = new Item( check );
+ s1.save( item );
+ t1.commit();
+ s1.close();
+
+ Long itemId = item.getId();
+ long initialVersion = item.getVersion();
+
+ // Now, open a new Session and re-load the item...
+ s1 = getSessions().openSession();
+ t1 = s1.beginTransaction();
+ item = ( Item ) s1.get( Item.class, itemId );
+
+ // now that the item is associated with the persistence-context of that session,
+ // open a new session and modify it "behind the back" of the first session
+ Session s2 = getSessions().openSession();
+ Transaction t2 = s2.beginTransaction();
+ Item item2 = ( Item ) s2.get( Item.class, itemId );
+ item2.setName( "EJB3 Persistence Spec" );
+ t2.commit();
+ s2.close();
+
+ // at this point, s1 now contains stale data, so acquire a READ lock
+ // and make sure we get the already associated state (i.e., the old
+ // name and the old version)
+ s1.lock( item, LockMode.READ );
+ item2 = ( Item ) s1.get( Item.class, itemId );
+ assertTrue( item == item2 );
+ assertEquals( "encountered non-repeatable read", check, item2.getName() );
+ assertEquals( "encountered non-repeatable read", initialVersion, item2.getVersion() );
+
+ // attempt to acquire an UPGRADE lock; this should fail
+ try {
+ s1.lock( item, LockMode.UPGRADE );
+ fail( "expected UPGRADE lock failure" );
+ }
+ catch( StaleObjectStateException expected ) {
+ // this is the expected behavior
+ }
+ catch( SQLGrammarException t ) {
+ if ( getDialect() instanceof SQLServerDialect ) {
+ // sql-server (using snapshot isolation) reports this as a grammar exception /:)
+ //
+ // not to mention that it seems to "lose track" of the transaction in this scenario...
+ t1.rollback();
+ t1 = s1.beginTransaction();
+ }
+ else {
+ throw t;
+ }
+ }
+
+ t1.commit();
+ s1.close();
+
+ // clean up
+ s1 = getSessions().openSession();
+ t1 = s1.beginTransaction();
+ s1.createQuery( "delete Item" ).executeUpdate();
+ t1.commit();
+ s1.close();
+ }
+
+
+ // non-versioned entity tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public void testStaleNonVersionedInstanceFoundInQueryResult() {
+ if ( getDialect().doesReadCommittedCauseWritersToBlockReaders()) {
+ reportSkip( "lock blocking", "stale versioned instance" );
+ return;
+ }
+ String check = "Lock Modes";
+ Session s1 = getSessions().openSession();
+ Transaction t1 = s1.beginTransaction();
+ Part part = new Part( new Item( "EJB3 Specification" ), check, "3.3.5.3", new BigDecimal( 0.0 ) );
+ s1.save( part );
+ t1.commit();
+ s1.close();
+
+ Long partId = part.getId();
+
+ // Now, open a new Session and re-load the part...
+ s1 = getSessions().openSession();
+ t1 = s1.beginTransaction();
+ part = ( Part ) s1.get( Part.class, partId );
+
+ // now that the item is associated with the persistence-context of that session,
+ // open a new session and modify it "behind the back" of the first session
+ Session s2 = getSessions().openSession();
+ Transaction t2 = s2.beginTransaction();
+ Part part2 = ( Part ) s2.get( Part.class, partId );
+ part2.setName( "Lock Mode Types" );
+ t2.commit();
+ s2.close();
+
+ // at this point, s1 now contains stale data, so try an hql query which
+ // returns said part and make sure we get the previously associated state
+ // (i.e., the old name)
+ part2 = ( Part ) s1.createQuery( "select p from Part p" ).list().get( 0 );
+ assertTrue( part == part2 );
+ assertEquals( "encountered non-repeatable read", check, part2.getName() );
+
+ t1.commit();
+ s1.close();
+
+ // clean up
+ s1 = getSessions().openSession();
+ t1 = s1.beginTransaction();
+ s1.delete( part2 );
+ s1.delete( part2.getItem() );
+ t1.commit();
+ s1.close();
+ }
+
+ public void testStaleNonVersionedInstanceFoundOnLock() {
+ if ( ! readCommittedIsolationMaintained( "repeatable read tests" ) ) {
+ return;
+ }
+ if ( getDialect().doesReadCommittedCauseWritersToBlockReaders()) {
+ reportSkip( "lock blocking", "stale versioned instance" );
+ return;
+ }
+ String check = "Lock Modes";
+ Session s1 = getSessions().openSession();
+ Transaction t1 = s1.beginTransaction();
+ Part part = new Part( new Item( "EJB3 Specification" ), check, "3.3.5.3", new BigDecimal( 0.0 ) );
+ s1.save( part );
+ t1.commit();
+ s1.close();
+
+ Long partId = part.getId();
+
+ // Now, open a new Session and re-load the part...
+ s1 = getSessions().openSession();
+ t1 = s1.beginTransaction();
+ part = ( Part ) s1.get( Part.class, partId );
+
+ // now that the item is associated with the persistence-context of that session,
+ // open a new session and modify it "behind the back" of the first session
+ Session s2 = getSessions().openSession();
+ Transaction t2 = s2.beginTransaction();
+ Part part2 = ( Part ) s2.get( Part.class, partId );
+ part2.setName( "Lock Mode Types" );
+ t2.commit();
+ s2.close();
+
+ // at this point, s1 now contains stale data, so acquire a READ lock
+ // and make sure we get the already associated state (i.e., the old
+ // name and the old version)
+ s1.lock( part, LockMode.READ );
+ part2 = ( Part ) s1.get( Part.class, partId );
+ assertTrue( part == part2 );
+ assertEquals( "encountered non-repeatable read", check, part2.getName() );
+
+ // then acquire an UPGRADE lock; this should fail
+ try {
+ s1.lock( part, LockMode.UPGRADE );
+ }
+ catch( Throwable t ) {
+ // SQLServer, for example, immediately throws an exception here...
+ t1.rollback();
+ t1 = s1.beginTransaction();
+ }
+ part2 = ( Part ) s1.get( Part.class, partId );
+ assertTrue( part == part2 );
+ assertEquals( "encountered non-repeatable read", check, part2.getName() );
+
+ t1.commit();
+ s1.close();
+
+ // clean up
+ s1 = getSessions().openSession();
+ t1 = s1.beginTransaction();
+ s1.delete( part );
+ s1.delete( part.getItem() );
+ t1.commit();
+ s1.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/package.html
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/package.html (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/package.html 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,9 @@
+<html>
+ <head></head>
+ <body>
+ <p>
+ Tests for any JPA-specific behavior for which we need to ensure
+ compliance.
+ </p>
+ </body>
+</html>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/proxy/JPAProxyTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/proxy/JPAProxyTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/proxy/JPAProxyTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,99 @@
+package org.hibernate.test.jpa.proxy;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.test.jpa.AbstractJPATest;
+import org.hibernate.test.jpa.Item;
+
+/**
+ * Test relation between proxies and get()/load() processing
+ * and make sure the interactions match the ejb3 expectations
+ *
+ * @author Steve Ebersole
+ */
+public class JPAProxyTest extends AbstractJPATest {
+ public JPAProxyTest(String name) {
+ super( name );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( JPAProxyTest.class );
+ }
+
+ public void testEjb3ProxyUsage() {
+ Session s = openSession();
+ Transaction txn = s.beginTransaction();
+
+ Item item = ( Item ) s.load( Item.class, new Long(-1) );
+ assertFalse( Hibernate.isInitialized( item ) );
+ try {
+ Hibernate.initialize( item );
+ fail( "proxy access did not fail on non-existent proxy" );
+ }
+ catch ( EntityNotFoundException e ) {
+ // expected behavior
+ }
+ catch ( Throwable t ) {
+ fail( "unexpected exception type on non-existent proxy access : " + t );
+ }
+
+ s.clear();
+
+ Item item2 = ( Item ) s.load( Item.class, new Long(-1) );
+ assertFalse( Hibernate.isInitialized( item2 ) );
+ assertFalse( item == item2 );
+ try {
+ item2.getName();
+ fail( "proxy access did not fail on non-existent proxy" );
+ }
+ catch ( EntityNotFoundException e ) {
+ // expected behavior
+ }
+ catch ( Throwable t ) {
+ fail( "unexpected exception type on non-existent proxy access : " + t );
+ }
+
+ txn.commit();
+ s.close();
+ }
+
+ /**
+ * The ejb3 find() method maps to the Hibernate get() method
+ */
+ public void testGetSemantics() {
+ Long nonExistentId = new Long( -1 );
+ Session s = openSession();
+ Transaction txn = s.beginTransaction();
+ Item item = ( Item ) s.get( Item.class, nonExistentId );
+ assertNull( "get() of non-existent entity did not return null", item );
+ txn.commit();
+ s.close();
+
+ s = openSession();
+ txn = s.beginTransaction();
+ // first load() it to generate a proxy...
+ item = ( Item ) s.load( Item.class, nonExistentId );
+ assertFalse( Hibernate.isInitialized( item ) );
+ // then try to get() it to make sure we get an exception
+ try {
+ s.get( Item.class, nonExistentId );
+ fail( "force load did not fail on non-existent entity" );
+ }
+ catch ( EntityNotFoundException e ) {
+ // expected behavior
+ }
+ catch( AssertionFailedError e ) {
+ throw e;
+ }
+ catch ( Throwable t ) {
+ fail( "unexpected exception type on non-existent entity force load : " + t );
+ }
+ txn.commit();
+ s.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/ql/JPAQLComplianceTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/ql/JPAQLComplianceTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/ql/JPAQLComplianceTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,49 @@
+package org.hibernate.test.jpa.ql;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.test.jpa.AbstractJPATest;
+
+/**
+ * Tests for various JPAQL compliance issues
+ *
+ * @author Steve Ebersole
+ */
+public class JPAQLComplianceTest extends AbstractJPATest {
+ public JPAQLComplianceTest(String name) {
+ super( name );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( JPAQLComplianceTest.class );
+ }
+
+ public void testAliasNameSameAsUnqualifiedEntityName() {
+ Session s = openSession();
+ s.beginTransaction();
+ s.createQuery( "select item from Item item" ).list();
+ s.createQuery( "select item from Item item where item.name = 'a'" ).list();
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testIdentifierCaseSensitive() throws Exception {
+ Session s = openSession( );
+ // a control test (a user reported that the JPA 'case insensitivity' support
+ // caused problems with the "discriminator resolution" code; unable to reproduce)...
+ s.createQuery( "from MyEntity e where e.class = MySubclassEntity" );
+ s.createQuery( "from MyEntity e where e.other.class = MySubclassEntity" );
+ s.createQuery( "from MyEntity where other.class = MySubclassEntity" );
+
+ s.createQuery( "select object(I) from Item i").list();
+ s.close();
+ }
+
+ public void testGeneratedSubquery() {
+ Session s = openSession();
+ s.createQuery( "select c FROM Item c WHERE c.parts IS EMPTY" ).list();
+ s.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/ql/NativeQueryTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/ql/NativeQueryTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/ql/NativeQueryTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,30 @@
+package org.hibernate.test.jpa.ql;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.test.jpa.AbstractJPATest;
+
+/**
+ * todo: describe NativeQueryTest
+ *
+ * @author Steve Ebersole
+ */
+public class NativeQueryTest extends AbstractJPATest {
+ public NativeQueryTest(String name) {
+ super( name );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( NativeQueryTest.class );
+ }
+
+ public void testJpaStylePositionalParametersInNativeSql() {
+ Session s = openSession();
+ s.beginTransaction();
+ s.createSQLQuery( "select NAME from EJB3_ITEM where ITEM_ID = ?1" ).setParameter( "1", "123" ).list();
+ s.getTransaction().commit();
+ s.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/removed/RemovedEntityTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/removed/RemovedEntityTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/jpa/removed/RemovedEntityTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,62 @@
+package org.hibernate.test.jpa.removed;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.test.jpa.AbstractJPATest;
+import org.hibernate.test.jpa.Item;
+
+/**
+ *
+ * @author Steve Ebersole
+ */
+public class RemovedEntityTest extends AbstractJPATest {
+ public RemovedEntityTest(String name) {
+ super( name );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( RemovedEntityTest.class );
+ }
+
+ public void testRemoveThenContains() {
+ Session s = openSession();
+ s.beginTransaction();
+ Item item = new Item();
+ item.setName( "dummy" );
+ s.persist( item );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ s.delete( item );
+ boolean contains = s.contains( item );
+ s.getTransaction().commit();
+ s.close();
+
+ assertFalse( "expecting removed entity to not be contained", contains );
+ }
+
+ public void testRemoveThenGet() {
+ Session s = openSession();
+ s.beginTransaction();
+ Item item = new Item();
+ item.setName( "dummy" );
+ s.persist( item );
+ s.getTransaction().commit();
+ s.close();
+
+ Long id = item.getId();
+
+ s = openSession();
+ s.beginTransaction();
+ s.delete( item );
+ item = ( Item ) s.get( Item.class, id );
+ s.getTransaction().commit();
+ s.close();
+
+ assertNull( "expecting removed entity to be returned as null from get()", item );
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/KeyManyToOneSuite.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/KeyManyToOneSuite.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/KeyManyToOneSuite.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,23 @@
+package org.hibernate.test.keymanytoone;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.hibernate.test.keymanytoone.bidir.embedded.KeyManyToOneTest;
+import org.hibernate.test.keymanytoone.bidir.component.LazyKeyManyToOneTest;
+import org.hibernate.test.keymanytoone.bidir.component.EagerKeyManyToOneTest;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class KeyManyToOneSuite {
+ public static Test suite() {
+ TestSuite suite = new TestSuite( "key-many-to-one mappings" );
+ suite.addTest( KeyManyToOneTest.suite() );
+ suite.addTest( LazyKeyManyToOneTest.suite() );
+ suite.addTest( EagerKeyManyToOneTest.suite() );
+ return suite;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/component/Customer.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/component/Customer.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/component/Customer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,46 @@
+package org.hibernate.test.keymanytoone.bidir.component;
+
+import java.util.Collection;
+import java.util.ArrayList;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class Customer {
+ private Long id;
+ private String name;
+ private Collection orders = new ArrayList();
+
+ public Customer() {
+ }
+
+ public Customer(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;
+ }
+
+ public Collection getOrders() {
+ return orders;
+ }
+
+ public void setOrders(Collection orders) {
+ this.orders = orders;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/component/EagerKeyManyToOneTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/component/EagerKeyManyToOneTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/component/EagerKeyManyToOneTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,157 @@
+package org.hibernate.test.keymanytoone.bidir.component;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.HibernateException;
+import org.hibernate.event.def.DefaultLoadEventListener;
+import org.hibernate.event.LoadEvent;
+import org.hibernate.event.LoadEventListener;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.test.TestCase;
+
+/**
+ * @author Steve Ebersole
+ */
+public class EagerKeyManyToOneTest extends TestCase {
+
+ public EagerKeyManyToOneTest(String name) {
+ super( name );
+ }
+
+ protected String[] getMappings() {
+ return new String[] { "keymanytoone/bidir/component/EagerMapping.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( EagerKeyManyToOneTest.class );
+ }
+
+ protected void configure(Configuration cfg) {
+ super.configure( cfg );
+ cfg.setProperty( Environment.GENERATE_STATISTICS, "true" );
+ LoadEventListener[] baseListeners = cfg.getEventListeners().getLoadEventListeners();
+ int baseLength = baseListeners.length;
+ LoadEventListener[] expandedListeners = new LoadEventListener[ baseLength + 1 ];
+ expandedListeners[ 0 ] = new CustomLoadListener();
+ System.arraycopy( baseListeners, 0, expandedListeners, 1, baseLength );
+ cfg.getEventListeners().setLoadEventListeners( expandedListeners );
+ }
+
+ public void testSaveCascadedToKeyManyToOne() {
+ // test cascading a save to an association with a key-many-to-one which refers to a
+ // just saved entity
+ Session s = openSession();
+ s.beginTransaction();
+ Customer cust = new Customer( "Acme, Inc." );
+ Order order = new Order( new Order.Id( cust, 1 ) );
+ cust.getOrders().add( order );
+ s.save( cust );
+ s.flush();
+ assertEquals( 2, sfi().getStatistics().getEntityInsertCount() );
+ s.delete( cust );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testLoadingStrategies() {
+ Session s = openSession();
+ s.beginTransaction();
+ Customer cust = new Customer( "Acme, Inc." );
+ Order order = new Order( new Order.Id( cust, 1 ) );
+ cust.getOrders().add( order );
+ s.save( cust );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+
+// Here is an example of HHH-2277
+// essentially we have a bidirectional association where one side of the
+// association is actually part of a composite PK
+//
+// See #testLoadEntityWithEagerFetchingToKeyManyToOneReferenceBackToSelfFailureExpected() below...
+//
+// The way these are mapped causes the problem because both sides
+// are defined as eager which leads to the infinite loop; if only
+// one side is marked as eager, then all is ok...
+// cust = ( Customer ) s.get( Customer.class, cust.getId() );
+// assertEquals( 1, cust.getOrders().size() );
+// s.clear();
+
+ cust = ( Customer ) s.createQuery( "from Customer" ).uniqueResult();
+ assertEquals( 1, cust.getOrders().size() );
+ s.clear();
+
+ cust = ( Customer ) s.createQuery( "from Customer c join fetch c.orders" ).uniqueResult();
+ assertEquals( 1, cust.getOrders().size() );
+ s.clear();
+
+ cust = ( Customer ) s.createQuery( "from Customer c join fetch c.orders as o join fetch o.id.customer" ).uniqueResult();
+ assertEquals( 1, cust.getOrders().size() );
+ s.clear();
+
+ cust = ( Customer ) s.createCriteria( Customer.class ).uniqueResult();
+ assertEquals( 1, cust.getOrders().size() );
+ s.clear();
+
+ s.delete( cust );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testLoadEntityWithEagerFetchingToKeyManyToOneReferenceBackToSelfFailureExpected() {
+ // long winded method name to say that this is a test specifically for HHH-2277 ;)
+ // essentially we have a bidirectional association where one side of the
+ // association is actually part of a composite PK.
+ //
+ // The way these are mapped causes the problem because both sides
+ // are defined as eager which leads to the infinite loop; if only
+ // one side is marked as eager, then all is ok. In other words the
+ // problem arises when both pieces of instance data are coming from
+ // the same result set. This is because no "entry" can be placed
+ // into the persistence context for the association with the
+ // composite key because we are in the process of trying to build
+ // the composite-id instance
+ Session s = openSession();
+ s.beginTransaction();
+ Customer cust = new Customer( "Acme, Inc." );
+ Order order = new Order( new Order.Id( cust, 1 ) );
+ cust.getOrders().add( order );
+ s.save( cust );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ try {
+ cust = ( Customer ) s.get( Customer.class, cust.getId() );
+ }
+ catch( OverflowCondition overflow ) {
+ fail( "get()/load() caused overflow condition" );
+ }
+ s.delete( cust );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ private static class OverflowCondition extends RuntimeException {
+ }
+
+ private static class CustomLoadListener extends DefaultLoadEventListener {
+ private int internalLoadCount = 0;
+ public void onLoad(LoadEvent event, LoadType loadType) throws HibernateException {
+ if ( LoadEventListener.INTERNAL_LOAD_EAGER.getName().equals( loadType.getName() ) ) {
+ internalLoadCount++;
+ if ( internalLoadCount > 10 ) {
+ throw new OverflowCondition();
+ }
+ }
+ super.onLoad( event, loadType );
+ internalLoadCount--;
+ }
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/component/EagerMapping.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/component/EagerMapping.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/component/EagerMapping.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,42 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+ This mapping demonstrates the use of composite ids with the
+ key-many-to-one feature. Essentially a composite id where part
+ of the composition is a foreign-key to another entity.
+
+ Here, specifically, we map the key-many-to-one as a lazy
+ association.
+-->
+
+<hibernate-mapping package="org.hibernate.test.keymanytoone.bidir.component">
+
+ <class name="Customer" table="COMP_LAZY_KM2O_CUST">
+ <id name="id" column="ID" type="long">
+ <generator class="increment" />
+ </id>
+ <property name="name" column="NAME" type="string" />
+ <bag name="orders" inverse="true" cascade="all" lazy="false" fetch="join">
+ <key column="CUST_ID" />
+ <one-to-many class="Order" />
+ </bag>
+ </class>
+
+ <class name="Order" table="COMP_LAZY_KM2O_ORDR">
+ <composite-id name="id" class="Order$Id">
+ <key-many-to-one name="customer" class="Customer" column="CUST_ID" lazy="false"/>
+ <key-property name="number" column="ORDR_NUM" type="long" />
+ </composite-id>
+ <set name="items" table="COMP_LAZY_KM2O_ITEM">
+ <key>
+ <column name="CUST_ID"/>
+ <column name="ORDER_NUM"/>
+ </key>
+ <element type="string" column="ITEM_DESC" />
+ </set>
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/component/LazyKeyManyToOneTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/component/LazyKeyManyToOneTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/component/LazyKeyManyToOneTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,77 @@
+package org.hibernate.test.keymanytoone.bidir.component;
+
+import junit.framework.Test;
+
+import org.hibernate.test.TestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.Session;
+
+/**
+ * @author Steve Ebersole
+ */
+public class LazyKeyManyToOneTest extends TestCase {
+ public LazyKeyManyToOneTest(String name) {
+ super( name );
+ }
+
+ protected String[] getMappings() {
+ return new String[] { "keymanytoone/bidir/component/LazyMapping.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( LazyKeyManyToOneTest.class );
+ }
+
+ protected void configure(Configuration cfg) {
+ super.configure( cfg );
+ cfg.setProperty( Environment.GENERATE_STATISTICS, "true" );
+ }
+
+ public void testSaveCascadedToKeyManyToOne() {
+ // test cascading a save to an association with a key-many-to-one which refers to a
+ // just saved entity
+ Session s = openSession();
+ s.beginTransaction();
+ Customer cust = new Customer( "Acme, Inc." );
+ Order order = new Order( new Order.Id( cust, 1 ) );
+ cust.getOrders().add( order );
+ s.save( cust );
+ s.flush();
+ assertEquals( 2, sfi().getStatistics().getEntityInsertCount() );
+ s.delete( cust );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testLoadingStrategies() {
+ Session s = openSession();
+ s.beginTransaction();
+ Customer cust = new Customer( "Acme, Inc." );
+ Order order = new Order( new Order.Id( cust, 1 ) );
+ cust.getOrders().add( order );
+ s.save( cust );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+
+ cust = ( Customer ) s.get( Customer.class, cust.getId() );
+ assertEquals( 1, cust.getOrders().size() );
+ s.clear();
+
+ cust = ( Customer ) s.createQuery( "from Customer" ).uniqueResult();
+ assertEquals( 1, cust.getOrders().size() );
+ s.clear();
+
+ cust = ( Customer ) s.createQuery( "from Customer c join fetch c.orders" ).uniqueResult();
+ assertEquals( 1, cust.getOrders().size() );
+ s.clear();
+
+ s.delete( cust );
+ s.getTransaction().commit();
+ s.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/component/LazyMapping.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/component/LazyMapping.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/component/LazyMapping.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,42 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+ This mapping demonstrates the use of composite ids with the
+ key-many-to-one feature. Essentially a composite id where part
+ of the composition is a foreign-key to another entity.
+
+ Here, specifically, we map the key-many-to-one as a lazy
+ association.
+-->
+
+<hibernate-mapping package="org.hibernate.test.keymanytoone.bidir.component">
+
+ <class name="Customer" table="COMP_LAZY_KM2O_CUST">
+ <id name="id" column="ID" type="long">
+ <generator class="increment" />
+ </id>
+ <property name="name" column="NAME" type="string" />
+ <bag name="orders" inverse="true" cascade="all">
+ <key column="CUST_ID" />
+ <one-to-many class="Order" />
+ </bag>
+ </class>
+
+ <class name="Order" table="COMP_LAZY_KM2O_ORDR">
+ <composite-id name="id" class="Order$Id">
+ <key-many-to-one name="customer" class="Customer" column="CUST_ID" lazy="proxy"/>
+ <key-property name="number" column="ORDR_NUM" type="long" />
+ </composite-id>
+ <set name="items" table="COMP_LAZY_KM2O_ITEM">
+ <key>
+ <column name="CUST_ID"/>
+ <column name="ORDER_NUM"/>
+ </key>
+ <element type="string" column="ITEM_DESC" />
+ </set>
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/component/Order.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/component/Order.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/component/Order.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,86 @@
+package org.hibernate.test.keymanytoone.bidir.component;
+
+import java.io.Serializable;
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class Order {
+ private Id id;
+ private Set items = new HashSet();
+
+ public Order() {
+ }
+
+ public Order(Id id) {
+ this.id = id;
+ }
+
+ public Id getId() {
+ return id;
+ }
+
+ public void setId(Id id) {
+ this.id = id;
+ }
+
+ public Set getItems() {
+ return items;
+ }
+
+ public void setItems(Set items) {
+ this.items = items;
+ }
+
+ public static class Id implements Serializable {
+ private Customer customer;
+ private long number;
+
+ public Id() {
+ }
+
+ public Id(Customer customer, long number) {
+ this.customer = customer;
+ this.number = number;
+ }
+
+ public Customer getCustomer() {
+ return customer;
+ }
+
+ public void setCustomer(Customer customer) {
+ this.customer = customer;
+ }
+
+ public long getNumber() {
+ return number;
+ }
+
+ public void setNumber(long number) {
+ this.number = number;
+ }
+
+ public boolean equals(Object o) {
+ if ( this == o ) {
+ return true;
+ }
+ if ( o == null || getClass() != o.getClass() ) {
+ return false;
+ }
+
+ Id id = ( Id ) o;
+ return number == id.number && customer.equals( id.customer );
+ }
+
+ public int hashCode() {
+ int result;
+ result = customer.hashCode();
+ result = 31 * result + ( int ) ( number ^ ( number >>> 32 ) );
+ return result;
+ }
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/embedded/Customer.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/embedded/Customer.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/embedded/Customer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,46 @@
+package org.hibernate.test.keymanytoone.bidir.embedded;
+
+import java.util.Collection;
+import java.util.ArrayList;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class Customer {
+ private Long id;
+ private String name;
+ private Collection orders = new ArrayList();
+
+ public Customer() {
+ }
+
+ public Customer(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;
+ }
+
+ public Collection getOrders() {
+ return orders;
+ }
+
+ public void setOrders(Collection orders) {
+ this.orders = orders;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/embedded/KeyManyToOneTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/embedded/KeyManyToOneTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/embedded/KeyManyToOneTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,77 @@
+package org.hibernate.test.keymanytoone.bidir.embedded;
+
+import junit.framework.Test;
+
+import org.hibernate.test.TestCase;
+import org.hibernate.Session;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+
+/**
+ * @author Steve Ebersole
+ */
+public class KeyManyToOneTest extends TestCase {
+ public KeyManyToOneTest(String name) {
+ super( name );
+ }
+
+ protected String[] getMappings() {
+ return new String[] { "keymanytoone/bidir/embedded/Mapping.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( KeyManyToOneTest.class );
+ }
+
+ protected void configure(Configuration cfg) {
+ super.configure( cfg );
+ cfg.setProperty( Environment.GENERATE_STATISTICS, "true" );
+ }
+
+ public void testSaveCascadedToKeyManyToOne() {
+ // test cascading a save to an association with a key-many-to-one which refers to a
+ // just saved entity
+ Session s = openSession();
+ s.beginTransaction();
+ Customer cust = new Customer( "Acme, Inc." );
+ Order order = new Order( cust, 1 );
+ cust.getOrders().add( order );
+ s.save( cust );
+ s.flush();
+ assertEquals( 2, sfi().getStatistics().getEntityInsertCount() );
+ s.delete( cust );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testLoadingStrategies() {
+ Session s = openSession();
+ s.beginTransaction();
+ Customer cust = new Customer( "Acme, Inc." );
+ Order order = new Order( cust, 1 );
+ cust.getOrders().add( order );
+ s.save( cust );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+
+ cust = ( Customer ) s.get( Customer.class, cust.getId() );
+ assertEquals( 1, cust.getOrders().size() );
+ s.clear();
+
+ cust = ( Customer ) s.createQuery( "from Customer" ).uniqueResult();
+ assertEquals( 1, cust.getOrders().size() );
+ s.clear();
+
+ cust = ( Customer ) s.createQuery( "from Customer c join fetch c.orders" ).uniqueResult();
+ assertEquals( 1, cust.getOrders().size() );
+ s.clear();
+
+ s.delete( cust );
+ s.getTransaction().commit();
+ s.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/embedded/Mapping.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/embedded/Mapping.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/embedded/Mapping.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,39 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+ This mapping demonstrates the use of composite ids with the
+ key-many-to-one feature where the composite-id is an embedded form.
+ Essentially a composite id where part of the composition is a
+ foreign-key to another entity.
+-->
+
+<hibernate-mapping package="org.hibernate.test.keymanytoone.bidir.embedded">
+
+ <class name="Customer" table="EMBD_KM2O_CUST">
+ <id name="id" column="ID" type="long">
+ <generator class="increment" />
+ </id>
+ <property name="name" column="NAME" type="string" />
+ <bag name="orders" inverse="true" cascade="all">
+ <key column="CUST_ID" />
+ <one-to-many class="Order" />
+ </bag>
+ </class>
+
+ <class name="Order" table="EMBD_KM2O_ORDR">
+ <composite-id mapped="false">
+ <key-many-to-one name="customer" class="Customer" column="CUST_ID" lazy="false"/>
+ <key-property name="number" column="ORDR_NUM" type="long" />
+ </composite-id>
+ <set name="items" table="EMBD_KM2O_ITEM">
+ <key>
+ <column name="CUST_ID"/>
+ <column name="ORDER_NUM"/>
+ </key>
+ <element type="string" column="ITEM_DESC" />
+ </set>
+ </class>
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/embedded/Order.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/embedded/Order.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/keymanytoone/bidir/embedded/Order.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,48 @@
+package org.hibernate.test.keymanytoone.bidir.embedded;
+
+import java.util.Set;
+import java.util.HashSet;
+import java.io.Serializable;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class Order implements Serializable {
+ private Customer customer;
+ private long number;
+ private Set items = new HashSet();
+
+ public Order() {
+ }
+
+ public Order(Customer customer, long number) {
+ this.customer = customer;
+ this.number = number;
+ }
+
+ public Customer getCustomer() {
+ return customer;
+ }
+
+ public void setCustomer(Customer customer) {
+ this.customer = customer;
+ }
+
+ public long getNumber() {
+ return number;
+ }
+
+ public void setNumber(long number) {
+ this.number = number;
+ }
+
+ public Set getItems() {
+ return items;
+ }
+
+ public void setItems(Set items) {
+ this.items = items;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazycache/Document.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazycache/Document.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazycache/Document.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,100 @@
+//$Id: Document.java 7772 2005-08-05 23:03:46Z oneovthafew $
+package org.hibernate.test.lazycache;
+
+import java.util.Date;
+
+/**
+ * @author Gavin King
+ */
+public class Document {
+
+ private Long id;
+ private String name;
+ private String upperCaseName;
+ private String summary;
+ private String text;
+ private Date lastTextModification;
+
+ public Document(String name, String summary, String text) {
+ lastTextModification = new Date();
+ this.name = name;
+ upperCaseName = name.toUpperCase();
+ this.summary = summary;
+ this.text = text;
+ }
+
+ Document() {}
+
+ public Date getLastTextModification() {
+ return lastTextModification;
+ }
+
+ /**
+ * @return Returns the id.
+ */
+ public Long getId() {
+ return id;
+ }
+ /**
+ * @param id The id to set.
+ */
+ public void setId(Long id) {
+ this.id = id;
+ }
+ /**
+ * @return Returns the name.
+ */
+ public String getName() {
+ return name;
+ }
+ /**
+ * @param name The name to set.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+ /**
+ * @return Returns the summary.
+ */
+ public String getSummary() {
+ return summary;
+ }
+ /**
+ * @param summary The summary to set.
+ */
+ public void setSummary(String summary) {
+ this.summary = summary;
+ }
+ /**
+ * @return Returns the text.
+ */
+ public String getText() {
+ return text;
+ }
+ /**
+ * @param text The text to set.
+ */
+ private void setText(String text) {
+ this.text = text;
+ }
+ /**
+ * @return Returns the upperCaseName.
+ */
+ public String getUpperCaseName() {
+ return upperCaseName;
+ }
+ /**
+ * @param upperCaseName The upperCaseName to set.
+ */
+ public void setUpperCaseName(String upperCaseName) {
+ this.upperCaseName = upperCaseName;
+ }
+
+ public void updateText(String newText) {
+ if ( !newText.equals(text) ) {
+ this.text = newText;
+ lastTextModification = new Date();
+ }
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazycache/Document.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazycache/Documents.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazycache/Documents.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazycache/Documents.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,37 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+ This mapping demonstrates
+
+ (1) use of lazy properties - this feature requires buildtime
+ bytecode instrumentation; we don't think this is a very
+ necessary feature, but provide it for completeleness; if
+ Hibernate encounters uninstrumented classes, lazy property
+ fetching will be silently disabled, to enable testing
+
+ (2) use of a formula to define a "derived property"
+
+-->
+
+<hibernate-mapping
+ package="org.hibernate.test.lazycache"
+ default-access="field">
+
+ <class name="Document" table="documents">
+ <cache usage="nonstrict-read-write" include="non-lazy" region="foo"/>
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="name" not-null="true" length="50"/>
+ <property name="upperCaseName" formula="upper(name)" lazy="true"/>
+ <property name="summary" not-null="true" length="200" lazy="true"/>
+ <property name="text" not-null="true" length="2000" lazy="true"/>
+ <property name="lastTextModification" not-null="true" lazy="true" access="field"/>
+ </class>
+
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazycache/Documents.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazycache/InstrumentCacheTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazycache/InstrumentCacheTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazycache/InstrumentCacheTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,86 @@
+//$Id: InstrumentCacheTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.lazycache;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.intercept.FieldInterceptionHelper;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class InstrumentCacheTest extends FunctionalTestCase {
+
+ public InstrumentCacheTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "lazycache/Documents.hbm.xml" };
+ }
+
+ public void configure(Configuration cfg) {
+ cfg.setProperty(Environment.GENERATE_STATISTICS, "true");
+ }
+
+ public boolean overrideCacheStrategy() {
+ return false;
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( InstrumentCacheTest.class );
+ }
+
+ public static boolean isRunnable() {
+ // TODO : this could be handled via appliesTo()...
+ return FieldInterceptionHelper.isInstrumented( new Document() );
+ }
+
+ public void testInitFromCache() {
+ Session s;
+ Transaction tx;
+
+ s = getSessions().openSession();
+ tx = s.beginTransaction();
+ s.persist( new Document("HiA", "Hibernate book", "Hibernate is....") );
+ tx.commit();
+ s.close();
+
+ s = getSessions().openSession();
+ tx = s.beginTransaction();
+ s.createQuery("from Document fetch all properties").uniqueResult();
+ tx.commit();
+ s.close();
+
+ getSessions().getStatistics().clear();
+
+ s = getSessions().openSession();
+ tx = s.beginTransaction();
+ Document d = (Document) s.createCriteria(Document.class).uniqueResult();
+ assertFalse( Hibernate.isPropertyInitialized(d, "text") );
+ assertFalse( Hibernate.isPropertyInitialized(d, "summary") );
+ assertEquals( "Hibernate is....", d.getText() );
+ assertTrue( Hibernate.isPropertyInitialized(d, "text") );
+ assertTrue( Hibernate.isPropertyInitialized(d, "summary") );
+ tx.commit();
+ s.close();
+
+ assertEquals( 2, getSessions().getStatistics().getPrepareStatementCount() );
+
+ s = getSessions().openSession();
+ tx = s.beginTransaction();
+ d = (Document) s.get(Document.class, d.getId());
+ assertFalse( Hibernate.isPropertyInitialized(d, "text") );
+ assertFalse( Hibernate.isPropertyInitialized(d, "summary") );
+ tx.commit();
+ s.close();
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazycache/InstrumentCacheTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazycache/InstrumentCacheTest2.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazycache/InstrumentCacheTest2.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazycache/InstrumentCacheTest2.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,81 @@
+//$Id: InstrumentCacheTest2.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.lazycache;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.intercept.FieldInterceptionHelper;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class InstrumentCacheTest2 extends FunctionalTestCase {
+
+ public InstrumentCacheTest2(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "lazycache/Documents.hbm.xml" };
+ }
+
+ public void configure(Configuration cfg) {
+ cfg.setProperty(Environment.GENERATE_STATISTICS, "true");
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( InstrumentCacheTest2.class );
+ }
+
+ public static boolean isRunnable() {
+ return FieldInterceptionHelper.isInstrumented( new Document() );
+ }
+
+ public void testInitFromCache() {
+ Session s;
+ Transaction tx;
+
+ s = getSessions().openSession();
+ tx = s.beginTransaction();
+ s.persist( new Document("HiA", "Hibernate book", "Hibernate is....") );
+ tx.commit();
+ s.close();
+
+ s = getSessions().openSession();
+ tx = s.beginTransaction();
+ s.createQuery("from Document fetch all properties").uniqueResult();
+ tx.commit();
+ s.close();
+
+ getSessions().getStatistics().clear();
+
+ s = getSessions().openSession();
+ tx = s.beginTransaction();
+ Document d = (Document) s.createCriteria(Document.class).uniqueResult();
+ assertFalse( Hibernate.isPropertyInitialized(d, "text") );
+ assertFalse( Hibernate.isPropertyInitialized(d, "summary") );
+ assertEquals( "Hibernate is....", d.getText() );
+ assertTrue( Hibernate.isPropertyInitialized(d, "text") );
+ assertTrue( Hibernate.isPropertyInitialized(d, "summary") );
+ tx.commit();
+ s.close();
+
+ assertEquals( 1, getSessions().getStatistics().getPrepareStatementCount() );
+
+ s = getSessions().openSession();
+ tx = s.beginTransaction();
+ d = (Document) s.get(Document.class, d.getId());
+ assertTrue( Hibernate.isPropertyInitialized(d, "text") );
+ assertTrue( Hibernate.isPropertyInitialized(d, "summary") );
+ tx.commit();
+ s.close();
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazycache/InstrumentCacheTest2.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazyonetoone/Employee.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazyonetoone/Employee.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazyonetoone/Employee.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,38 @@
+//$Id: Employee.java 5686 2005-02-12 07:27:32Z steveebersole $
+package org.hibernate.test.lazyonetoone;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * @author Gavin King
+ */
+public class Employee {
+ private String personName;
+ private Person person;
+ private Collection employments = new ArrayList();
+ Employee() {}
+ public Employee(Person p) {
+ this.person = p;
+ this.personName = p.getName();
+ p.setEmployee(this);
+ }
+ public Person getPerson() {
+ return person;
+ }
+ public void setPerson(Person person) {
+ this.person = person;
+ }
+ public String getPersonName() {
+ return personName;
+ }
+ public void setPersonName(String personName) {
+ this.personName = personName;
+ }
+ public Collection getEmployments() {
+ return employments;
+ }
+ public void setEmployments(Collection employments) {
+ this.employments = employments;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazyonetoone/Employee.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazyonetoone/Employment.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazyonetoone/Employment.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazyonetoone/Employment.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,46 @@
+//$Id: Employment.java 5686 2005-02-12 07:27:32Z steveebersole $
+package org.hibernate.test.lazyonetoone;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * @author Gavin King
+ */
+public class Employment implements Serializable {
+ private String personName;
+ private String organizationName;
+ private Date startDate;
+ private Date endDate;
+ Employment() {}
+ public Employment(Employee e, String org) {
+ this.personName = e.getPersonName();
+ this.organizationName = org;
+ startDate = new Date();
+ e.getEmployments().add(this);
+ }
+ public String getOrganizationName() {
+ return organizationName;
+ }
+ public void setOrganizationName(String organizationName) {
+ this.organizationName = organizationName;
+ }
+ public String getPersonName() {
+ return personName;
+ }
+ public void setPersonName(String personName) {
+ this.personName = personName;
+ }
+ public Date getEndDate() {
+ return endDate;
+ }
+ public void setEndDate(Date endDate) {
+ this.endDate = endDate;
+ }
+ public Date getStartDate() {
+ return startDate;
+ }
+ public void setStartDate(Date startDate) {
+ this.startDate = startDate;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazyonetoone/Employment.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazyonetoone/LazyOneToOneTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazyonetoone/LazyOneToOneTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazyonetoone/LazyOneToOneTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,85 @@
+//$Id: LazyOneToOneTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.lazyonetoone;
+
+import java.util.Date;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.intercept.FieldInterceptionHelper;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class LazyOneToOneTest extends FunctionalTestCase {
+
+ public LazyOneToOneTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "lazyonetoone/Person.hbm.xml" };
+ }
+
+ public void configure(Configuration cfg) {
+ cfg.setProperty(Environment.MAX_FETCH_DEPTH, "2");
+ cfg.setProperty(Environment.USE_SECOND_LEVEL_CACHE, "false");
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( LazyOneToOneTest.class );
+ }
+
+ public static boolean isRunnable() {
+ return FieldInterceptionHelper.isInstrumented( new Person() );
+ }
+
+ public void testLazy() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Person p = new Person("Gavin");
+ Person p2 = new Person("Emmanuel");
+ Employee e = new Employee(p);
+ new Employment(e, "JBoss");
+ Employment old = new Employment(e, "IFA");
+ old.setEndDate( new Date() );
+ s.persist(p);
+ s.persist(p2);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ p = (Person) s.createQuery("from Person where name='Gavin'").uniqueResult();
+ //assertFalse( Hibernate.isPropertyInitialized(p, "employee") );
+ assertSame( p.getEmployee().getPerson(), p );
+ assertTrue( Hibernate.isInitialized( p.getEmployee().getEmployments() ) );
+ assertEquals( p.getEmployee().getEmployments().size(), 1 );
+ p2 = (Person) s.createQuery("from Person where name='Emmanuel'").uniqueResult();
+ assertNull( p2.getEmployee() );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ p = (Person) s.get(Person.class, "Gavin");
+ //assertFalse( Hibernate.isPropertyInitialized(p, "employee") );
+ assertSame( p.getEmployee().getPerson(), p );
+ assertTrue( Hibernate.isInitialized( p.getEmployee().getEmployments() ) );
+ assertEquals( p.getEmployee().getEmployments().size(), 1 );
+ p2 = (Person) s.get(Person.class, "Emmanuel");
+ assertNull( p2.getEmployee() );
+ s.delete(p2);
+ s.delete(old);
+ s.delete(p);
+ t.commit();
+ s.close();
+ }
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazyonetoone/LazyOneToOneTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazyonetoone/Person.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazyonetoone/Person.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazyonetoone/Person.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,36 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.lazyonetoone">
+
+ <class name="Person">
+ <id name="name"/>
+ <one-to-one name="employee" lazy="no-proxy" cascade="persist,delete"/>
+ </class>
+
+ <class name="Employee">
+ <id name="personName"/>
+ <one-to-one name="person" lazy="no-proxy"/>
+ <bag name="employments"
+ inverse="true"
+ fetch="join"
+ lazy="false"
+ where="endDate is null"
+ cascade="persist,delete">
+ <key column="personName"/>
+ <one-to-many class="Employment"/>
+ </bag>
+ </class>
+
+ <class name="Employment">
+ <composite-id>
+ <key-property name="personName"/>
+ <key-property name="organizationName"/>
+ </composite-id>
+ <property name="startDate" update="false"/>
+ <property name="endDate"/>
+ </class>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazyonetoone/Person.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazyonetoone/Person.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazyonetoone/Person.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazyonetoone/Person.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,26 @@
+//$Id: Person.java 5686 2005-02-12 07:27:32Z steveebersole $
+package org.hibernate.test.lazyonetoone;
+
+/**
+ * @author Gavin King
+ */
+public class Person {
+ private String name;
+ private Employee employee;
+ Person() {}
+ public Person(String name) {
+ this.name = name;
+ }
+ public Employee getEmployee() {
+ return employee;
+ }
+ public void setEmployee(Employee employee) {
+ this.employee = employee;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lazyonetoone/Person.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/.cvsignore
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/.cvsignore (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/.cvsignore 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1 @@
+*.bin
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/A.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/A.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/A.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,55 @@
+package org.hibernate.test.legacy;
+
+
+public class A {
+ private Long id;
+ private String name;
+ private E forward;
+
+ /**
+ * Returns the id.
+ * @return Long
+ */
+ public Long getId() {
+ return id;
+ }
+
+ /**
+ * Returns the name.
+ * @return String
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the id.
+ * @param id The id to set
+ */
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ /**
+ * Sets the name.
+ * @param name The name to set
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public E getForward() {
+ return forward;
+ }
+
+ public void setForward(E e) {
+ forward = e;
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ABC.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ABC.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ABC.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,55 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping default-lazy="false">
+
+ <class name="org.hibernate.test.legacy.A" discriminator-value="0" lazy="true" table="TA">
+ <id name = "id" column="identifier_column" unsaved-value = "null">
+ <generator class="increment"/>
+ </id>
+ <discriminator column="clazz_discriminata" type="integer" not-null="false"/>
+
+ <property name="name" index="indx_a_name">
+ <column name="name" index="indx_a_name2"/>
+ </property>
+
+ <subclass name="org.hibernate.test.legacy.B" discriminator-value="-1" lazy="true">
+ <property name="count" column="count_"/>
+ <join fetch="select" table="B2">
+ <key column="bid"/>
+ <property name="BName" not-null="true"/>
+ </join>
+ <subclass name="org.hibernate.test.legacy.C1" discriminator-value="null" lazy="true">
+ <property name="address" column="c1"/>
+ <one-to-one name="d"/>
+ <join table="cmore" fetch="select">
+ <key column="c1" on-delete="cascade"/>
+ <property name="C1Name"/>
+ </join>
+ </subclass>
+ </subclass>
+ </class>
+
+ <class name="org.hibernate.test.legacy.D" discriminator-value="0" proxy="org.hibernate.test.legacy.D" table="TD">
+ <id name = "id" unsaved-value = "null">
+ <generator class="assigned"/>
+ </id>
+ <property name="amount"/>
+ <many-to-one name="reverse" insert="false" update="false" outer-join="true">
+ <formula>(id)</formula>
+ </many-to-one>
+ <many-to-one name="inverse" access="field" insert="false" update="false" formula = "(id)" outer-join="true"/>
+ <!--many-to-one name="reverse" formula = "(select a.id from TA a where a.id = id)"/-->
+ </class>
+
+ <sql-query name="propertyResultDiscriminator">
+ <return alias="a" class="org.hibernate.test.legacy.A">
+ <return-discriminator column="clazz"/>
+ <return-property name="id" column="identifier_column"/>
+ <return-property name="name" column="name"/>
+ <return-property name="count" column="count_"/>
+ </return>
+ select identifier_column, clazz_discriminata as clazz, name, count_ from TA s
+ </sql-query>
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ABCExtends.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ABCExtends.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ABCExtends.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,16 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping default-lazy="false">
+
+ <subclass name="org.hibernate.test.legacy.C2" discriminator-value="2" extends="org.hibernate.test.legacy.B" lazy="true">
+ <property name="address" column="c2"/>
+ <join table="c2more" fetch="select">
+ <key column="c2"/>
+ <property name="C2Name"/>
+ </join>
+ </subclass>
+
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ABCProxy.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ABCProxy.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ABCProxy.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,62 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping default-lazy="false">
+
+ <class name="org.hibernate.test.legacy.A" discriminator-value="0" lazy="true" table="AP">
+ <id name="id" column="identifier_column" unsaved-value = "null">
+ <generator class="increment"/>
+ </id>
+ <discriminator column="clazz_discriminata" type="integer" force="true" not-null="false"/>
+ <property name="name"/>
+ <many-to-one name="forward" class="org.hibernate.test.legacy.E" cascade="save-update" />
+ <subclass name="org.hibernate.test.legacy.B" discriminator-value="null" lazy="true">
+ <property name="count" column="count_"/>
+
+ <map name="map">
+ <key column="BID"/>
+ <index column="MAPKEY" type="string"/>
+ <element column="MAPVAL" type="int"/>
+ </map>
+
+ <subclass name="org.hibernate.test.legacy.C1" discriminator-value="1" lazy="true">
+ <property name="address" column="c1"/>
+ <one-to-one name="d"/>
+ <many-to-one name="c2" column="c" class="org.hibernate.test.legacy.C2"/>
+ <bag name="c2s" inverse="true" where="identifier_column is not null"> <!--where="clazz_discriminata=2"-->
+ <key column="c"/>
+ <one-to-many class="org.hibernate.test.legacy.C2"/>
+ </bag>
+ </subclass>
+ <subclass name="org.hibernate.test.legacy.C2" discriminator-value="2" lazy="true">
+ <property name="address" column="c2"/>
+ <many-to-one name="c1" column="c" class="org.hibernate.test.legacy.C1"/>
+ <bag name="c1s" inverse="true" > <!--where="clazz_discriminata=1"-->
+ <key column="c"/>
+ <one-to-many class="org.hibernate.test.legacy.C1"/>
+ </bag>
+ </subclass>
+ </subclass>
+ </class>
+
+ <class name="org.hibernate.test.legacy.D" discriminator-value="0" lazy="true" table="DP">
+ <id name = "id" unsaved-value = "null">
+ <generator class="assigned"/>
+ </id>
+ <property name="amount"/>
+ </class>
+
+ <class name="org.hibernate.test.legacy.E" discriminator-value="0" lazy="true" table="EP">
+ <id name = "id" unsaved-value = "null">
+ <generator class="increment"/>
+ </id>
+ <property name="amount"/>
+ <one-to-one name="reverse" class="org.hibernate.test.legacy.A" property-ref="forward" cascade="save-update"/>
+ <set name="as" lazy="true">
+ <key column="e"/>
+ <one-to-many class="org.hibernate.test.legacy.A"/>
+ </set>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ABCProxyTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ABCProxyTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ABCProxyTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,304 @@
+//$Id: ABCProxyTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.Test;
+import junit.textui.TestRunner;
+
+import org.hibernate.Hibernate;
+import org.hibernate.LockMode;
+import org.hibernate.Transaction;
+import org.hibernate.classic.Session;
+import org.hibernate.dialect.HSQLDialect;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+
+public class ABCProxyTest extends LegacyTestCase {
+
+ public ABCProxyTest(String arg0) {
+ super(arg0);
+ }
+
+ public void testDiscriminatorFiltering() throws Exception {
+ if ( ( getDialect() instanceof HSQLDialect ) ) return;
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.createQuery("from C1 c1 left join c1.c2s c2").list();
+ s.createCriteria(C1.class).createCriteria("c2s").list();
+ t.commit();
+ s.close();
+ }
+
+ public void testNarrow() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.createQuery("from E e join e.reverse as b where b.count=1").list();
+ s.createQuery("from E e join e.as as b where b.count=1").list();
+ t.commit();
+ s.close();
+ }
+
+ public void testSharedColumn() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ C1 c1 = new C1();
+ C2 c2 = new C2();
+ c1.setC2(c2);
+ c2.setC1(c1);
+ s.save(c1); s.save(c2);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ List list = s.find("from B");
+ assertTrue( list.size()==2 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c1 = (C1) s.createQuery("from C1").uniqueResult();
+ c2 = (C2) s.createQuery("from C2").uniqueResult();
+ assertTrue( c1.getC2()==c2 );
+ assertTrue( c2.getC1()==c1 );
+ assertTrue( c1.getC2s().contains(c2) );
+ assertTrue( c2.getC1s().contains(c1) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c1 = (C1) s.get( A.class, c1.getId() );
+ c2 = (C2) s.get( A.class, c2.getId() );
+ assertTrue( c1.getC2()==c2 );
+ assertTrue( c2.getC1()==c1 );
+ assertTrue( c1.getC2s().contains(c2) );
+ assertTrue( c2.getC1s().contains(c1) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.delete(c1); s.delete(c2);
+ t.commit();
+ s.close();
+
+ }
+
+ public void testSubclassing() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ C1 c1 = new C1();
+ D d = new D();
+ d.setAmount(213.34f);
+ c1.setAddress("foo bar");
+ c1.setCount(23432);
+ c1.setName("c1");
+ c1.setD(d);
+ s.save(c1);
+ d.setId( c1.getId() );
+ s.save(d);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ A c1a = (A) s.load( A.class, c1.getId() );
+ assertFalse( Hibernate.isInitialized(c1a) );
+ assertTrue( c1a.getName().equals("c1") );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ B c1b = (B) s.load( B.class, c1.getId() );
+ assertTrue(
+ (c1b.getCount()==23432) &&
+ c1b.getName().equals("c1")
+ );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c1 = (C1) s.load( C1.class, c1.getId() );
+ assertTrue(
+ c1.getAddress().equals("foo bar") &&
+ (c1.getCount()==23432) &&
+ c1.getName().equals("c1") &&
+ c1.getD().getAmount()>213.3f
+ );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c1a = (A) s.load( A.class, c1.getId() );
+ assertTrue( c1a.getName().equals("c1") );
+ c1 = (C1) s.load( C1.class, c1.getId() );
+ assertTrue(
+ c1.getAddress().equals("foo bar") &&
+ (c1.getCount()==23432) &&
+ c1.getName().equals("c1") &&
+ c1.getD().getAmount()>213.3f
+ );
+ c1b = (B) s.load( B.class, c1.getId() );
+ assertTrue(
+ (c1b.getCount()==23432) &&
+ c1b.getName().equals("c1")
+ );
+ assertTrue( c1a.getName().equals("c1") );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c1a = (A) s.load( A.class, c1.getId() );
+ assertTrue( c1a.getName().equals("c1") );
+ c1 = (C1) s.load( C1.class, c1.getId(), LockMode.UPGRADE );
+ assertTrue(
+ c1.getAddress().equals("foo bar") &&
+ (c1.getCount()==23432) &&
+ c1.getName().equals("c1") &&
+ c1.getD().getAmount()>213.3f
+ );
+ c1b = (B) s.load( B.class, c1.getId(), LockMode.UPGRADE );
+ assertTrue(
+ (c1b.getCount()==23432) &&
+ c1b.getName().equals("c1")
+ );
+ assertTrue( c1a.getName().equals("c1") );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c1a = (A) s.load( A.class, c1.getId() );
+ c1 = (C1) s.load( C1.class, c1.getId() );
+ c1b = (B) s.load( B.class, c1.getId() );
+ assertTrue( c1a.getName().equals("c1") );
+ assertTrue(
+ c1.getAddress().equals("foo bar") &&
+ (c1.getCount()==23432) &&
+ c1.getName().equals("c1") &&
+ c1.getD().getAmount()>213.3f
+ );
+ assertTrue(
+ (c1b.getCount()==23432) &&
+ c1b.getName().equals("c1")
+ );
+ System.out.println( s.delete("from A") );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.save( new B() );
+ s.save( new A() );
+ assertTrue( s.find("from B").size()==1 );
+ assertTrue( s.find("from A").size()==2 );
+ s.delete("from A");
+ t.commit();
+ s.close();
+ }
+
+ public void testSubclassMap() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ B b = new B();
+ s.save(b);
+ Map map = new HashMap();
+ map.put("3", new Integer(1) );
+ b.setMap(map);
+ s.flush();
+ s.delete(b);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ map = new HashMap();
+ map.put("3", new Integer(1) );
+ b = new B();
+ b.setMap(map);
+ s.save(b);
+ s.flush();
+ s.delete(b);
+ t.commit();
+ s.close();
+ }
+
+ public void testOneToOne() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ A a = new A();
+ E d1 = new E();
+ C1 c = new C1();
+ E d2 = new E();
+ a.setForward(d1);
+ d1.setReverse(a);
+ c.setForward(d2);
+ d2.setReverse(c);
+ Serializable aid = s.save(a);
+ Serializable d2id = s.save(d2);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ List l = s.find( "from E e, A a where e.reverse = a.forward and a = ?", a, Hibernate.entity(A.class) );
+ assertTrue( l.size()==1 );
+ l = s.find( "from E e join fetch e.reverse" );
+ assertTrue( l.size()==2 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ l = s.find( "from E e" );
+ assertTrue( l.size()==2 );
+ E e = (E) l.get(0);
+ assertTrue( e==e.getReverse().getForward() );
+ e = (E) l.get(1);
+ assertTrue( e==e.getReverse().getForward() );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ a = (A) s.load(A.class, aid);
+ d2 = (E) s.load(E.class, d2id);
+ assertTrue( a==a.getForward().getReverse() );
+ assertTrue( d2==d2.getReverse().getForward() );
+ s.delete(a);
+ s.delete( a.getForward() );
+ s.delete(d2);
+ s.delete( d2.getReverse() );
+ t.commit();
+
+ s = openSession();
+ t = s.beginTransaction();
+ l = s.find( "from E e" );
+ assertTrue( l.size()==0 );
+ t.commit();
+ s.close();
+ }
+
+ public String[] getMappings() {
+ return new String[] { "legacy/ABCProxy.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( ABCProxyTest.class );
+ }
+
+ public static void main(String[] args) throws Exception {
+ TestRunner.run( suite() );
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ABCTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ABCTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ABCTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,165 @@
+//$Id: ABCTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.legacy;
+
+import java.util.List;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import junit.textui.TestRunner;
+import org.hibernate.classic.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+
+public class ABCTest extends LegacyTestCase {
+
+ public ABCTest(String arg0) {
+ super(arg0);
+ }
+
+ public void testFormulaAssociation() throws Throwable {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ D d = new D();
+ Long did = new Long(12);
+ s.save(d, did);
+ A a = new A();
+ a.setName("a");
+ s.save(a, did);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ d = (D) s.get(D.class, did);
+ assertTrue(d.getReverse().getId().equals(did));
+ s.clear();
+ getSessions().evict(D.class);
+ getSessions().evict(A.class);
+ d = (D) s.get(D.class, did);
+ assertTrue(d.inverse.getId().equals(did));
+ assertTrue(d.inverse.getName().equals("a"));
+ s.clear();
+ getSessions().evict(D.class);
+ getSessions().evict(A.class);
+ assertTrue( s.find("from D d join d.reverse r join d.inverse i where i = r").size()==1 );
+ t.commit();
+ s.close();
+ }
+
+ public void testHigherLevelIndexDefinition() throws Throwable {
+ String[] commands = getCfg().generateSchemaCreationScript( getDialect() );
+ int max = commands.length;
+ boolean found = false;
+ for (int indx = 0; indx < max; indx++) {
+ System.out.println("Checking command : " + commands[indx]);
+ found = commands[indx].indexOf("create index indx_a_name") >= 0;
+ if (found)
+ break;
+ }
+ assertTrue("Unable to locate indx_a_name index creation", found);
+ }
+
+ public void testSubclassing() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ C1 c1 = new C1();
+ D d = new D();
+ d.setAmount(213.34f);
+ c1.setAddress("foo bar");
+ c1.setCount(23432);
+ c1.setName("c1");
+ c1.setBName("a funny name");
+ c1.setD(d);
+ s.save(c1);
+ d.setId( c1.getId() );
+ s.save(d);
+
+ assertTrue( s.find("from C2 c where 1=1 or 1=1").size()==0 );
+
+ t.commit();
+ s.close();
+
+ getSessions().evict(A.class);
+
+ s = openSession();
+ t = s.beginTransaction();
+ c1 = (C1) s.get( A.class, c1.getId() );
+ assertTrue(
+ c1.getAddress().equals("foo bar") &&
+ (c1.getCount()==23432) &&
+ c1.getName().equals("c1") &&
+ c1.getD().getAmount()>213.3f
+ );
+ assertEquals( "a funny name", c1.getBName() );
+ t.commit();
+ s.close();
+
+ getSessions().evict(A.class);
+
+ s = openSession();
+ t = s.beginTransaction();
+ c1 = (C1) s.get( B.class, c1.getId() );
+ assertTrue(
+ c1.getAddress().equals("foo bar") &&
+ (c1.getCount()==23432) &&
+ c1.getName().equals("c1") &&
+ c1.getD().getAmount()>213.3f
+ );
+ assertEquals( "a funny name", c1.getBName() );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c1 = (C1) s.load( C1.class, c1.getId() );
+ assertTrue(
+ c1.getAddress().equals("foo bar") &&
+ (c1.getCount()==23432) &&
+ c1.getName().equals("c1") &&
+ c1.getD().getAmount()>213.3f
+ );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ List bs = s.createQuery("from B").list();
+ for (int i=0; i<bs.size(); i++) {
+ C1 b = (C1) bs.get(i);
+ s.delete(b);
+ s.delete( b.getD() );
+ }
+ t.commit();
+ s.close();
+ }
+
+ public void testGetSave() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ assertNull( s.get( D.class, new Long(1) ) );
+ D d = new D();
+ d.setId( new Long(1) );
+ s.save(d);
+ s.flush();
+ assertNotNull( s.get( D.class, new Long(1) ) );
+ s.delete(d);
+ s.flush();
+ t.commit();
+ s.close();
+ }
+
+ public String[] getMappings() {
+ return new String[] { "legacy/ABC.hbm.xml", "legacy/ABCExtends.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( ABCTest.class );
+ }
+
+ public static void main(String[] args) throws Exception {
+ TestRunner.run( suite() );
+ }
+
+}
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Abstract.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Abstract.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Abstract.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,33 @@
+//$Id: Abstract.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.util.Set;
+
+public abstract class Abstract extends Foo implements AbstractProxy {
+
+ private java.sql.Time time;
+ private Set abstracts;
+
+ public java.sql.Time getTime() {
+ return time;
+ }
+
+ public void setTime(java.sql.Time time) {
+ this.time = time;
+ }
+
+ public Set getAbstracts() {
+ return abstracts;
+ }
+
+ public void setAbstracts(Set abstracts) {
+ this.abstracts = abstracts;
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/AbstractProxy.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/AbstractProxy.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/AbstractProxy.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,13 @@
+package org.hibernate.test.legacy;
+
+public interface AbstractProxy extends FooProxy {
+ public void setAbstracts(java.util.Set arg0);
+ public java.util.Set getAbstracts();
+ public void setTime(java.sql.Time arg0);
+ public java.sql.Time getTime();
+}
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/AltSimple.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/AltSimple.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/AltSimple.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping default-lazy="false">
+
+ <class name="org.hibernate.test.legacy.Simple" table="SIMP">
+ <id type="long" column="id_">
+ <generator class="assigned"/>
+ </id>
+ <property name="name"/>
+ <property name="address"/>
+ <property name="count" column="count_" not-null="true" unique="true"/>
+ <property name="date" column="date_"/>
+ <property name="pay"/>
+ <many-to-one name="other"/>
+ </class>
+
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Assignable.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Assignable.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Assignable.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,28 @@
+package org.hibernate.test.legacy;
+
+import java.util.Collection;
+
+/**
+ * @author Administrator
+ */
+public class Assignable {
+ private String id;
+ private Collection categories;
+
+ public Collection getCategories() {
+ return categories;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setCategories(Collection collection) {
+ categories = collection;
+ }
+
+ public void setId(String string) {
+ id = string;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/B.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/B.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/B.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,40 @@
+package org.hibernate.test.legacy;
+
+import java.util.Map;
+
+
+public class B extends A {
+ private int count;
+ private Map map;
+ private String bName = "B Name";
+
+ public int getCount() {
+ return count;
+ }
+
+ public void setCount(int count) {
+ this.count = count;
+ }
+
+ public Map getMap() {
+ return map;
+ }
+
+ public void setMap(Map map) {
+ this.map = map;
+ }
+
+ public String getBName() {
+ return bName;
+ }
+
+ public void setBName(String name) {
+ bName = name;
+ }
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Bar.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Bar.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Bar.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,67 @@
+//$Id: Bar.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+public class Bar extends Abstract implements BarProxy, Named {
+ private String barString;
+ private FooComponent barComponent = new FooComponent("bar", 69, null, null);
+ private Baz baz;
+ private int x;
+ private Object object;
+
+ public int getX() {
+ return x;
+ }
+ public void setX(int x) {
+ this.x = x;
+ }
+
+ public String getBarString() {
+ return barString;
+ }
+
+ void setBarString(String barString) {
+ this.barString = barString;
+ }
+
+ public FooComponent getBarComponent() {
+ return barComponent;
+ }
+
+ public void setBarComponent(FooComponent barComponent) {
+ this.barComponent = barComponent;
+ }
+
+ public Baz getBaz() {
+ return baz;
+ }
+
+ public void setBaz(Baz baz) {
+ this.baz = baz;
+ }
+
+ private String name = "bar";
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Object getObject() {
+ return object;
+ }
+
+ public void setObject(Object object) {
+ this.object = object;
+ }
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/BarProxy.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/BarProxy.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/BarProxy.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,18 @@
+//$Id: BarProxy.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+public interface BarProxy extends AbstractProxy {
+ public void setBaz(Baz arg0);
+ public Baz getBaz();
+ public void setBarComponent(FooComponent arg0);
+ public FooComponent getBarComponent();
+ //public void setBarString(String arg0);
+ public String getBarString();
+ public Object getObject();
+ public void setObject(Object o);
+}
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/BasicNameable.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/BasicNameable.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/BasicNameable.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,48 @@
+//$Id: BasicNameable.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+/**
+ * @author administrator
+ *
+ *
+ */
+public class BasicNameable implements Nameable {
+
+ private String name;
+ private Long id;
+
+ /**
+ * @see Nameable#getName()
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @see Nameable#setName()
+ */
+ public void setName(String n) {
+ name = n;
+ }
+
+ /**
+ * @see Nameable#getKey()
+ */
+ public Long getKey() {
+ return id;
+ }
+
+ /**
+ * @see Nameable#setKey()
+ */
+ public void setKey(Long k) {
+ id = k;
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Baz.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Baz.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Baz.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,292 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping default-lazy="false">
+
+ <class name="org.hibernate.test.legacy.Baz"
+ table="baz"
+ check="count_count > -666">
+ <id name="code" type="string">
+ <column name="baz_id_column_" length="32"/>
+ <generator class="uuid.hex"/>
+ </id>
+ <property name="count" column="count_count"/>
+ <property name="name" column="name_b"/>
+ <!--property name="text" type="text" column="text_"/-->
+ <many-to-one name="foo" outer-join="true" class="org.hibernate.test.legacy.Foo"/>
+
+ <list name="stringList" table="string_list" batch-size="12">
+ <key>
+ <column name="id" length="16"/>
+ </key>
+ <index column="`i`"/>
+ <element column="element" type="string"/>
+ </list>
+ <primitive-array name="intArray" table="int_array">
+ <key>
+ <column name="id" length="16"/>
+ </key>
+ <index column="i"/>
+ <element column="j" type="int"/>
+ </primitive-array>
+ <set name="fooSet" lazy="true" order-by="string_" batch-size="12">
+ <key>
+ <column name="baz_id" length="16"/>
+ </key>
+ <one-to-many class="org.hibernate.test.legacy.Foo"/>
+ </set>
+ <array name="components" table="bazcomponents">
+ <key>
+ <column name="baz_id" length="16"/>
+ </key>
+ <index column="i"/>
+ <composite-element class="org.hibernate.test.legacy.FooComponent">
+ <parent name="baz"/>
+ <property name="name">
+ <column name="name" length="56"/>
+ </property>
+ <property name="count" column="count_"/>
+ <nested-composite-element name="subcomponent" class="org.hibernate.test.legacy.FooComponent">
+ <property name="name" column="x_"/>
+ <property name="count" column="y_"/>
+ </nested-composite-element>
+ </composite-element>
+ </array>
+ <array name="timeArray">
+ <key>
+ <column name="baz_id" length="16"/>
+ </key>
+ <index column="j"/>
+ <element column="the_time" type="time"/>
+ </array>
+ <bag name="bag" order-by="`name_`" table="`bxaxg`">
+ <key>
+ <column name="`baz_id_`" length="16"/>
+ </key>
+ <element column="`name_`" type="string"/>
+ </bag>
+ <map name="fooToGlarch" lazy="false">
+ <key>
+ <column name="baz_id" length="16"/>
+ </key>
+ <index-many-to-many column="foo_id" class="org.hibernate.test.legacy.Foo"/>
+ <many-to-many column="glarch_id" class="org.hibernate.test.legacy.Glarch"/>
+ </map>
+ <map name="fooComponentToFoo">
+ <!--cache-->
+ <key>
+ <column name="baz_id" length="16"/>
+ </key>
+ <composite-index class="org.hibernate.test.legacy.FooComponent">
+ <key-property name="name" length="32"/>
+ <key-property name="count" column="count_"/>
+ </composite-index>
+ <many-to-many column="foo_id" class="org.hibernate.test.legacy.Foo" outer-join="true"/>
+ </map>
+ <map name="glarchToFoo">
+ <key>
+ <column name="gtf_baz_id" length="16"/>
+ </key>
+ <index-many-to-many column="gtf_foo_id" class="org.hibernate.test.legacy.Foo"/>
+ <one-to-many class="org.hibernate.test.legacy.Glarch"/>
+ </map>
+
+ <set name="stringSet" lazy="true" sort="org.hibernate.test.legacy.StringComparator" batch-size="12">
+ <!--<cache usage="read-write"/>-->
+ <key column="id_"/>
+ <element column="element" type="string" not-null="true" length="32"/>
+ </set>
+
+ <map name="stringDateMap" lazy="true" sort="org.hibernate.test.legacy.ReverseComparator">
+ <key column="id_"/>
+ <index column="map_key" type="string" length="32"/>
+ <element column="map_value" type="date"/>
+ </map>
+
+ <array name="fooArray" element-class="org.hibernate.test.legacy.FooProxy" where="i<8" check="i>=0">
+ <!--cache-->
+ <key column="id_"/>
+ <index column="i"/>
+ <many-to-many class="org.hibernate.test.legacy.Foo" fetch="select">
+ <column name="foo" length="36"/>
+ </many-to-many>
+ </array>
+
+ <bag name="fooBag" lazy="true" table="baz_foo" cascade="all-delete-orphan" inverse="false">
+ <key column="baz"/>
+ <many-to-many class="org.hibernate.test.legacy.Foo" column="foo" outer-join="true"/>
+ </bag>
+
+ <idbag name="idFooBag" lazy="true" table="baz_id_foo" cascade="all">
+ <collection-id column="pkid" type="long">
+ <generator class="hilo"/>
+ </collection-id>
+ <key column="baz"/>
+ <many-to-many class="org.hibernate.test.legacy.Foo" column="foo" outer-join="true"/>
+ </idbag>
+
+ <idbag name="byteBag" lazy="true" table="baz_byte_bag">
+ <collection-id column="pkid" type="long">
+ <generator class="hilo"/>
+ </collection-id>
+ <key column="baz"/>
+ <element type="binary" column="bytez" not-null="true"/>
+ </idbag>
+
+ <array name="stringArray">
+ <key column="id_"/>
+ <index column="i"/>
+ <element column="name" type="string"/>
+ </array>
+
+ <list name="fees" lazy="true" cascade="all" check="bazind is null or (bazind>=0 and bazind<10)">
+ <key column="bazid"/>
+ <index column="bazind"/>
+ <one-to-many class="org.hibernate.test.legacy.Fee"/>
+ </list>
+
+ <list name="customs">
+ <key column="id_"/>
+ <index column="indx"/>
+ <element type="org.hibernate.test.legacy.DoubleStringType">
+ <column name="first_"/>
+ <column name="second_"/>
+ </element>
+ </list>
+
+ <list name="topComponents" table="topcomponents">
+ <!--cache-->
+ <key column="id_"/>
+ <index column="i"/>
+ <composite-element class="org.hibernate.test.legacy.FooComponent">
+ <property name="name"/>
+ <property name="count" column="count_"/>
+ </composite-element>
+ </list>
+
+ <set name="topFoos">
+ <key column="idtopbar" />
+ <one-to-many class = "org.hibernate.test.legacy.Bar"/>
+ </set>
+
+ <set name="cascadingBars" cascade="all-delete-orphan">
+ <key column="idofbaz" />
+ <one-to-many class = "org.hibernate.test.legacy.Bar"/>
+ </set>
+
+ <map name="topGlarchez" outer-join="true">
+ <key column="idtopglarch" />
+ <index type="character">
+ <column name="mapkey" sql-type="char(1)"/>
+ </index>
+ <one-to-many class = "org.hibernate.test.legacy.Glarch"/>
+ </map>
+
+ <set name="cached" sort="natural" table="cached_set">
+ <!--cache-->
+ <key column="baz"/>
+ <composite-element class="org.hibernate.test.legacy.CompositeElement">
+ <property name="foo" not-null="true"/>
+ <property name="bar" not-null="true"/>
+ </composite-element>
+ </set>
+
+ <map name="cachedMap" sort="natural" table="cached_map">
+ <!--cache-->
+ <key column="baz"/>
+ <index-many-to-many column="another_baz" class="org.hibernate.test.legacy.Baz"/>
+ <composite-element class="org.hibernate.test.legacy.CompositeElement">
+ <property name="foo"/>
+ <property name="bar"/>
+ </composite-element>
+ </map>
+
+ <map name="stringGlarchMap" where="baz_map_index > 'a' and tha_key is not null" cascade="all">
+ <key column="baz_map_id"/>
+ <index column="baz_map_index" type="string"/>
+ <one-to-many class="org.hibernate.test.legacy.Glarch"/>
+ </map>
+
+ <map name="anyToAny" lazy="true">
+ <key column="baz"/>
+ <index-many-to-any id-type="long">
+ <column name="ind_clazz_"/>
+ <column name="ind_id_"/>
+ </index-many-to-any>
+ <many-to-any id-type="long">
+ <column name="el_clazz_"/>
+ <column name="el_id_"/>
+ </many-to-any>
+ </map>
+
+ <list name="manyToAny" lazy="true">
+ <key column="baz"/>
+ <index column="ind"/>
+ <many-to-any id-type="string">
+ <column name="el_clazz_"/>
+ <column name="el_id_"/>
+ </many-to-any>
+ </list>
+
+ <bag name="bazez" lazy="true" order-by="name_b desc" cascade="all">
+ <key column="`baz-id`"/>
+ <one-to-many class="org.hibernate.test.legacy.Baz"/>
+ </bag>
+
+ <set name="sortablez" sort="natural" cascade="all">
+ <key column="baz"/>
+ <one-to-many class="org.hibernate.test.legacy.Sortable"/>
+ </set>
+
+ <bag name="parts" lazy="true" cascade="save-update">
+ <key column="baz"/>
+ <one-to-many class="org.hibernate.test.legacy.Part"/>
+ </bag>
+
+ <bag name="moreParts" lazy="true" cascade="save-update">
+ <key column="baz"/>
+ <many-to-many column="part" class="org.hibernate.test.legacy.Part"/>
+ </bag>
+
+ <bag name="subs" inverse="true" cascade="all" access="field">
+ <key column="superBaz" on-delete="cascade"/>
+ <one-to-many class="org.hibernate.test.legacy.Baz"/>
+ </bag>
+
+ <many-to-one name="superBaz" access="field"/>
+
+ <component name="collectionComponent">
+ <component name="nested">
+ <property name="str"/>
+ <bag name="foos" cascade="all">
+ <key column="baz_compon_id"/>
+ <one-to-many class="org.hibernate.test.legacy.Foo"/>
+ </bag>
+ <bag name="floats" table="baz_floats_bag">
+ <key column="baz_compon_id"/>
+ <element type="float" column="float_value"/>
+ </bag>
+ </component>
+ </component>
+
+ </class>
+
+ <class name="org.hibernate.test.legacy.Sortable">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+ <class name="org.hibernate.test.legacy.Part" where="description like 'x%'">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <discriminator column="clazz"/>
+ <property name="description"/>
+ <subclass name="org.hibernate.test.legacy.Part$SpecialPart"/>
+ </class>
+
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Baz.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Baz.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Baz.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,451 @@
+//$Id: Baz.java 4688 2004-10-26 09:10:50Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+public class Baz implements Named, Serializable, Comparable {
+ private SortedSet stringSet;
+ private Map stringDateMap;
+ private List stringList;
+ private int[] intArray;
+ private FooProxy[] fooArray;
+ private String[] stringArray;
+ private String code;
+ private List customs;
+ private List topComponents;
+ private Set fooSet;
+ private FooComponent[] components;
+ private Date[] timeArray;
+ private int count;
+ private String name;
+ private Collection bag;
+ private Set topFoos;
+ private Map topGlarchez;
+ private Set cascadingBars;
+ private Map fooToGlarch;
+ private Map fooComponentToFoo;
+ private Map glarchToFoo;
+ private List fees;
+ private Collection fooBag;
+ private Set cached;
+ private Map cachedMap;
+ private Map stringGlarchMap;
+ private Map anyToAny;
+ private List manyToAny;
+ private Collection idFooBag;
+ private Collection byteBag;
+ private FooProxy foo;
+ private List bazez;
+ private SortedSet sortablez;
+ private NestingComponent collectionComponent;
+ private String text;
+ private List parts;
+ private List moreParts;
+ public List subs;
+ public Baz superBaz;
+
+ Baz() {}
+
+ public SortedSet getStringSet() {
+ return stringSet;
+ }
+ public void setStringSet(SortedSet stringSet) {
+ this.stringSet = stringSet;
+ }
+ public Map getStringDateMap() {
+ return stringDateMap;
+ }
+ public void setStringDateMap(Map stringDateMap) {
+ this.stringDateMap = stringDateMap;
+ }
+ public List getStringList() {
+ return stringList;
+ }
+ public void setStringList(List stringList) {
+ this.stringList = stringList;
+ }
+ public int[] getIntArray() {
+ return intArray;
+ }
+ public void setIntArray(int[] intArray) {
+ this.intArray = intArray;
+ }
+ public FooProxy[] getFooArray() {
+ return fooArray;
+ }
+ public void setFooArray(FooProxy[] fooArray) {
+ this.fooArray = fooArray;
+ }
+ public String[] getStringArray() {
+ return stringArray;
+ }
+ public void setStringArray(String[] stringArray) {
+ this.stringArray = stringArray;
+ }
+
+ public String getCode() {
+ return code;
+ }
+ public void setCode(String code) {
+ this.code = code;
+ }
+
+ public void setDefaults() {
+ SortedSet set = new TreeSet();
+ set.add("foo"); set.add("bar"); set.add("baz");
+ setStringSet(set);
+ Map map = new TreeMap();
+ map.put( "now", new Date() );
+ map.put( "never", null );
+ map.put( "big bang", new Date(0) );
+ setStringDateMap(map);
+ List list = new ArrayList();
+ list.addAll(set);
+ setStringList(list);
+ setIntArray( new int[] { 1,3,3,7 } );
+ setFooArray( new Foo[0] );
+ setStringArray( (String[]) list.toArray( new String[0] ) );
+ customs = new ArrayList();
+ customs.add( new String[] { "foo", "bar" } );
+ customs.add( new String[] { "A", "B" } );
+ customs.add( new String[] { "1", "2" } );
+
+ fooSet = new HashSet();
+ components = new FooComponent[] {
+ new FooComponent("foo", 42, null, null),
+ new FooComponent("bar", 88, null, new FooComponent("sub", 69, null, null) )
+ };
+ timeArray = new Date[] { new Date(), new Date(), null, new Date(0) };
+ TreeSet x = new TreeSet();
+ x.add("w"); x.add("x"); x.add("y"); x.add("z");
+ TreeSet a = new TreeSet();
+ a.add("a"); a.add("b"); a.add("d"); a.add("c");
+
+ count = 667;
+ name="Bazza";
+ topComponents = new ArrayList();
+ topComponents.add( new FooComponent("foo", 11, new Date[] { new Date(), new Date(123) }, null) );
+ topComponents.add( new FooComponent("bar", 22, new Date[] { new Date(7), new Date(456) }, null) );
+ topComponents.add( null );
+ bag = new ArrayList();
+ bag.add("duplicate");
+ bag.add("duplicate");
+ bag.add("duplicate");
+ bag.add("unique");
+ cached = new TreeSet();
+ CompositeElement ce = new CompositeElement();
+ ce.setFoo("foo");
+ ce.setBar("bar");
+ CompositeElement ce2 = new CompositeElement();
+ ce2.setFoo("fooxxx");
+ ce2.setBar("barxxx");
+ cached.add(ce);
+ cached.add(ce2);
+ cachedMap = new TreeMap();
+ cachedMap.put(this, ce);
+
+ text="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
+ for (int i=0; i<10; i++) text+=text;
+
+ }
+
+ public List getCustoms() {
+ return customs;
+ }
+ public void setCustoms(List customs) {
+ this.customs = customs;
+ }
+
+ public Set getFooSet() {
+ return fooSet;
+ }
+ public void setFooSet(Set fooSet) {
+ this.fooSet = fooSet;
+ }
+
+ public FooComponent[] getComponents() {
+ return components;
+ }
+ public void setComponents(FooComponent[] components) {
+ this.components = components;
+ }
+
+ public Date[] getTimeArray() {
+ return timeArray;
+ }
+
+ public void setTimeArray(Date[] timeArray) {
+ this.timeArray = timeArray;
+ }
+
+ public int getCount() {
+ return count;
+ }
+
+ public void setCount(int count) {
+ this.count = count;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public List getTopComponents() {
+ return topComponents;
+ }
+
+ public void setTopComponents(List topComponents) {
+ this.topComponents = topComponents;
+ }
+
+ public Collection getBag() {
+ return bag;
+ }
+
+ public void setBag(Collection bag) {
+ this.bag = bag;
+ }
+
+ public Set getTopFoos() {
+ return topFoos;
+ }
+
+ public void setTopFoos(Set topFoos) {
+ this.topFoos = topFoos;
+ }
+
+
+ public Map getTopGlarchez() {
+ return topGlarchez;
+ }
+
+ public void setTopGlarchez(Map topGlarchez) {
+ this.topGlarchez = topGlarchez;
+ }
+
+ public Set getCascadingBars() {
+ return cascadingBars;
+ }
+
+ public void setCascadingBars(Set cascadingBars) {
+ this.cascadingBars = cascadingBars;
+ }
+
+ public Map getFooToGlarch() {
+ return fooToGlarch;
+ }
+
+ public void setFooToGlarch(Map fooToGlarch) {
+ this.fooToGlarch = fooToGlarch;
+ }
+
+ public Map getFooComponentToFoo() {
+ return fooComponentToFoo;
+ }
+
+ public void setFooComponentToFoo(Map fooComponentToFoo) {
+ this.fooComponentToFoo = fooComponentToFoo;
+ }
+
+ public Map getGlarchToFoo() {
+ return glarchToFoo;
+ }
+
+ public void setGlarchToFoo(Map glarchToFoo) {
+ this.glarchToFoo = glarchToFoo;
+ }
+
+ public List getFees() {
+ return fees;
+ }
+
+ public void setFees(List fees) {
+ this.fees = fees;
+ }
+
+ public Collection getFooBag() {
+ return fooBag;
+ }
+
+ public void setFooBag(Collection fooBag) {
+ this.fooBag = fooBag;
+ }
+
+ /**
+ * Returns the cached.
+ * @return Set
+ */
+ public Set getCached() {
+ return cached;
+ }
+
+ /**
+ * Sets the cached.
+ * @param cached The cached to set
+ */
+ public void setCached(Set cached) {
+ this.cached = cached;
+ }
+
+ /**
+ * Returns the cachedMap.
+ * @return Map
+ */
+ public Map getCachedMap() {
+ return cachedMap;
+ }
+
+ /**
+ * Sets the cachedMap.
+ * @param cachedMap The cachedMap to set
+ */
+ public void setCachedMap(Map cachedMap) {
+ this.cachedMap = cachedMap;
+ }
+
+ /**
+ * @see java.lang.Comparable#compareTo(java.lang.Object)
+ */
+ public int compareTo(Object o) {
+ return ( (Baz) o ).code.compareTo(code);
+ }
+
+ /**
+ * Returns the stringGlarchMap.
+ * @return Map
+ */
+ public Map getStringGlarchMap() {
+ return stringGlarchMap;
+ }
+
+ /**
+ * Sets the stringGlarchMap.
+ * @param stringGlarchMap The stringGlarchMap to set
+ */
+ public void setStringGlarchMap(Map stringGlarchMap) {
+ this.stringGlarchMap = stringGlarchMap;
+ }
+
+ /**
+ * Returns the anyToAny.
+ * @return Map
+ */
+ public Map getAnyToAny() {
+ return anyToAny;
+ }
+
+ /**
+ * Sets the anyToAny.
+ * @param anyToAny The anyToAny to set
+ */
+ public void setAnyToAny(Map anyToAny) {
+ this.anyToAny = anyToAny;
+ }
+
+ public Collection getIdFooBag() {
+ return idFooBag;
+ }
+
+ public void setIdFooBag(Collection collection) {
+ idFooBag = collection;
+ }
+
+ public Collection getByteBag() {
+ return byteBag;
+ }
+
+ public void setByteBag(Collection list) {
+ byteBag = list;
+ }
+
+ public FooProxy getFoo() {
+ return foo;
+ }
+
+ public void setFoo(FooProxy foo) {
+ this.foo = foo;
+ }
+
+ public List getBazez() {
+ return bazez;
+ }
+
+ public void setBazez(List list) {
+ bazez = list;
+ }
+
+ public SortedSet getSortablez() {
+ return sortablez;
+ }
+
+ public void setSortablez(SortedSet set) {
+ sortablez = set;
+ }
+
+
+ public NestingComponent getCollectionComponent() {
+ return collectionComponent;
+ }
+
+ public void setCollectionComponent(NestingComponent collection) {
+ collectionComponent = collection;
+ }
+
+ /**
+ * @return
+ */
+ public String getText() {
+ return text;
+ }
+
+ /**
+ * @param string
+ */
+ public void setText(String string) {
+ text = string;
+ }
+
+ public List getParts() {
+ return parts;
+ }
+
+ public void setParts(List list) {
+ parts = list;
+ }
+
+ public List getManyToAny() {
+ return manyToAny;
+ }
+
+ public void setManyToAny(List manyToAny) {
+ this.manyToAny = manyToAny;
+ }
+
+ public List getMoreParts() {
+ return moreParts;
+ }
+ public void setMoreParts(List moreParts) {
+ this.moreParts = moreParts;
+ }
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Blobber.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Blobber.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Blobber.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping default-lazy="false">
+ <class name="org.hibernate.test.legacy.Blobber" dynamic-update="true">
+ <id name="id">
+ <generator class="hilo"/>
+ </id>
+ <property name="blob" column="blob_"/>
+ <property name="clob" column="clob_"/>
+ </class>
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Blobber.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Blobber.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Blobber.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,58 @@
+package org.hibernate.test.legacy;
+
+import java.sql.Blob;
+import java.sql.Clob;
+
+public class Blobber {
+ private int id;
+ private Blob blob;
+ private Clob clob;
+ /**
+ * Returns the blob.
+ * @return Blob
+ */
+ public Blob getBlob() {
+ return blob;
+ }
+
+ /**
+ * Returns the clob.
+ * @return Clob
+ */
+ public Clob getClob() {
+ return clob;
+ }
+
+ /**
+ * Returns the id.
+ * @return int
+ */
+ public int getId() {
+ return id;
+ }
+
+ /**
+ * Sets the blob.
+ * @param blob The blob to set
+ */
+ public void setBlob(Blob blob) {
+ this.blob = blob;
+ }
+
+ /**
+ * Sets the clob.
+ * @param clob The clob to set
+ */
+ public void setClob(Clob clob) {
+ this.clob = clob;
+ }
+
+ /**
+ * Sets the id.
+ * @param id The id to set
+ */
+ public void setId(int id) {
+ this.id = id;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Broken.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Broken.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Broken.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping default-lazy="false">
+
+ <class name="org.hibernate.test.legacy.Broken" table="BROKE">
+ <!--id name="id" type="long" column="id_">
+ <generator class="hilo"/>
+ </id-->
+ <composite-id>
+ <key-property name="id"/>
+ <key-property name="otherId"/>
+ </composite-id>
+ <timestamp name="timestamp" column="`timestamp`"/>
+ <joined-subclass name="org.hibernate.test.legacy.Fixed" table="FIX">
+ <key>
+ <column name="BROKE_ID"/>
+ <column name="OTHER_BROKE_ID"/>
+ </key>
+ <set name="set" lazy="true" inverse="true">
+ <key>
+ <column name="FIX_ID"/>
+ <column name="OTHER_FIX_ID"/>
+ </key>
+ <one-to-many class="org.hibernate.test.legacy.Broken"/>
+ </set>
+ <property name="list" type="serializable"/>
+ </joined-subclass>
+ </class>
+
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Broken.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Broken.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Broken.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,48 @@
+//$Id: Broken.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * @author Gavin King
+ */
+public class Broken implements Serializable {
+ private Long id;
+ private String otherId;
+ private Date timestamp;
+ public Long getId() {
+ return id;
+ }
+
+ public Date getTimestamp() {
+ return timestamp;
+ }
+
+ public void setId(Long long1) {
+ id = long1;
+ }
+
+ public void setTimestamp(Date date) {
+ timestamp = date;
+ }
+
+ public String getOtherId() {
+ return otherId;
+ }
+
+ public void setOtherId(String string) {
+ otherId = string;
+ }
+
+ public boolean equals(Object other) {
+ if ( !(other instanceof Broken) ) return false;
+ Broken that = (Broken) other;
+ return this.id.equals(that.id) && this.otherId.equals(that.otherId);
+ }
+
+ public int hashCode() {
+ return 1;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/C1.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/C1.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/C1.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,91 @@
+//$Id: C1.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+public class C1 extends B{
+ private String address;
+ private String c1Name;
+ private C2 c2;
+ private D d;
+ private Collection c2s = new ArrayList();
+ /**
+ * Returns the address.
+ * @return String
+ */
+ public String getAddress() {
+ return address;
+ }
+
+ /**
+ * Sets the address.
+ * @param address The address to set
+ */
+ public void setAddress(String address) {
+ this.address = address;
+ }
+
+ /**
+ * Returns the d.
+ * @return D
+ */
+ public D getD() {
+ return d;
+ }
+
+ /**
+ * Sets the d.
+ * @param d The d to set
+ */
+ public void setD(D d) {
+ this.d = d;
+ }
+
+ /**
+ * @return Returns the c.
+ */
+ public C2 getC2() {
+ return c2;
+ }
+
+ /**
+ * @param c The c to set.
+ */
+ public void setC2(C2 c) {
+ this.c2 = c;
+ }
+
+ /**
+ * @return Returns the cs.
+ */
+ public Collection getC2s() {
+ return c2s;
+ }
+
+ /**
+ * @param cs The cs to set.
+ */
+ public void setC2s(Collection cs) {
+ this.c2s = cs;
+ }
+
+ /**
+ * @return Returns the c1Name.
+ */
+ public String getC1Name() {
+ return c1Name;
+ }
+ /**
+ * @param name The c1Name to set.
+ */
+ public void setC1Name(String name) {
+ c1Name = name;
+ }
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/C2.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/C2.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/C2.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,69 @@
+//$Id: C2.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+public class C2 extends B {
+ private String address;
+ private C1 c1;
+ private String c2Name;
+ private Collection c1s = new ArrayList();
+ /**
+ * Returns the address.
+ * @return String
+ */
+ public String getAddress() {
+ return address;
+ }
+
+ /**
+ * Sets the address.
+ * @param address The address to set
+ */
+ public void setAddress(String address) {
+ this.address = address;
+ }
+
+ /**
+ * @return Returns the c.
+ */
+ public C1 getC1() {
+ return c1;
+ }
+
+ /**
+ * @param c The c to set.
+ */
+ public void setC1(C1 c) {
+ this.c1 = c;
+ }
+
+ /**
+ * @return Returns the cs.
+ */
+ public Collection getC1s() {
+ return c1s;
+ }
+
+ /**
+ * @param cs The cs to set.
+ */
+ public void setC1s(Collection cs) {
+ this.c1s = cs;
+ }
+
+ public String getC2Name() {
+ return c2Name;
+ }
+
+ public void setC2Name(String name) {
+ c2Name = name;
+ }
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/CacheTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/CacheTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/CacheTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,143 @@
+//$Id: CacheTest.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.test.legacy;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.hibernate.cache.Cache;
+import org.hibernate.cache.CacheConcurrencyStrategy;
+import org.hibernate.cache.CacheProvider;
+import org.hibernate.cache.ReadWriteCache;
+import org.hibernate.cache.HashtableCacheProvider;
+import org.hibernate.cache.access.SoftLock;
+import org.hibernate.junit.UnitTestCase;
+
+public class CacheTest extends UnitTestCase {
+
+ public CacheTest(String name) {
+ super( name );
+ }
+
+ public static Test suite() {
+ return new TestSuite( CacheTest.class );
+ }
+
+ public void testCaches() throws Exception {
+ doTestCache( new HashtableCacheProvider() );
+ }
+
+ public void doTestCache(CacheProvider cacheProvider) throws Exception {
+
+ Cache cache = cacheProvider.buildCache( String.class.getName(), System.getProperties() );
+
+ long longBefore = cache.nextTimestamp();
+
+ Thread.sleep(15);
+
+ long before = cache.nextTimestamp();
+
+ Thread.sleep(15);
+
+ //cache.setTimeout(1000);
+ CacheConcurrencyStrategy ccs = new ReadWriteCache();
+ ccs.setCache(cache);
+
+ // cache something
+
+ assertTrue( ccs.put("foo", "foo", before, null, null, false) );
+
+ Thread.sleep(15);
+
+ long after = cache.nextTimestamp();
+
+ assertTrue( ccs.get("foo", longBefore)==null );
+ assertTrue( ccs.get("foo", after).equals("foo") );
+
+ assertTrue( !ccs.put("foo", "foo", before, null, null, false) );
+
+ // update it:
+
+ SoftLock lock = ccs.lock("foo", null);
+
+ assertTrue( ccs.get("foo", after)==null );
+ assertTrue( ccs.get("foo", longBefore)==null );
+
+ assertTrue( !ccs.put("foo", "foo", before, null, null, false) );
+
+ Thread.sleep(15);
+
+ long whileLocked = cache.nextTimestamp();
+
+ assertTrue( !ccs.put("foo", "foo", whileLocked, null, null, false) );
+
+ Thread.sleep(15);
+
+ ccs.release("foo", lock);
+
+ assertTrue( ccs.get("foo", after)==null );
+ assertTrue( ccs.get("foo", longBefore)==null );
+
+ assertTrue( !ccs.put("foo", "bar", whileLocked, null, null, false) );
+ assertTrue( !ccs.put("foo", "bar", after, null, null, false) );
+
+ Thread.sleep(15);
+
+ long longAfter = cache.nextTimestamp();
+
+ assertTrue( ccs.put("foo", "baz", longAfter, null, null, false) );
+
+ assertTrue( ccs.get("foo", after)==null );
+ assertTrue( ccs.get("foo", whileLocked)==null );
+
+ Thread.sleep(15);
+
+ long longLongAfter = cache.nextTimestamp();
+
+ assertTrue( ccs.get("foo", longLongAfter).equals("baz") );
+
+ // update it again, with multiple locks:
+
+ SoftLock lock1 = ccs.lock("foo", null);
+ SoftLock lock2 = ccs.lock("foo", null);
+
+ assertTrue( ccs.get("foo", longLongAfter)==null );
+
+ Thread.sleep(15);
+
+ whileLocked = cache.nextTimestamp();
+
+ assertTrue( !ccs.put("foo", "foo", whileLocked, null, null, false) );
+
+ Thread.sleep(15);
+
+ ccs.release("foo", lock2);
+
+ Thread.sleep(15);
+
+ long betweenReleases = cache.nextTimestamp();
+
+ assertTrue( !ccs.put("foo", "bar", betweenReleases, null, null, false) );
+ assertTrue( ccs.get("foo", betweenReleases)==null );
+
+ Thread.sleep(15);
+
+ ccs.release("foo", lock1);
+
+ assertTrue( !ccs.put("foo", "bar", whileLocked, null, null, false) );
+
+ Thread.sleep(15);
+
+ longAfter = cache.nextTimestamp();
+
+ assertTrue( ccs.put("foo", "baz", longAfter, null, null, false) );
+ assertTrue( ccs.get("foo", whileLocked)==null );
+
+ Thread.sleep(15);
+
+ longLongAfter = cache.nextTimestamp();
+
+ assertTrue( ccs.get("foo", longLongAfter).equals("baz") );
+
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Category.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Category.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Category.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,54 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping default-lazy="false">
+
+ <class name="org.hibernate.test.legacy.Category"
+ table="category"
+ dynamic-update="true"
+ dynamic-insert="true">
+
+ <id name="id" column="category_key_col">
+ <generator class="native"/>
+ </id>
+
+ <list name="subcategories" lazy="true" cascade="save-update,merge,delete,lock">
+ <!--cache-->
+ <key column="parent"/>
+ <index column="ord"/>
+ <one-to-many class="org.hibernate.test.legacy.Category"/>
+ </list>
+
+ <property name="name"/>
+ <many-to-one name="assignable" column="`assign-able-id`"/>
+
+ </class>
+
+ <class name="org.hibernate.test.legacy.Assignable" table="`assign-able`">
+ <id name="id">
+ <generator class="assigned"/>
+ </id>
+
+ <bag name="categories" inverse="true" cascade="all" lazy="true">
+ <key column="`assign-able-id`"/>
+ <one-to-many class="org.hibernate.test.legacy.Category"/>
+ </bag>
+ </class>
+
+ <sql-query name="namedsql">
+ <return alias="category" class="org.hibernate.test.legacy.Category"/>
+ <return alias="assignable" class="org.hibernate.test.legacy.Assignable"/>
+ select {category.*}, {assignable.*} from category {category}, "assign-able" {assignable}
+ </sql-query>
+
+ <sql-query name="nonaliasedsql">
+ <return alias="category" class="org.hibernate.test.legacy.Category">
+ <return-property name="id" column="category_key_col"/>
+ <return-property name="name" column="name"/>
+ <return-property name="assignable" column="assignid"/>
+ </return>
+ select category_key_col, name, "assign-able-id" as assignid from category
+ </sql-query>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Category.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Category.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Category.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,85 @@
+//$Id: Category.java 5686 2005-02-12 07:27:32Z steveebersole $
+package org.hibernate.test.legacy;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ *
+ */
+public class Category {
+
+ public static final String ROOT_CATEGORY = "/";
+ public static final int ROOT_ID = 42;
+
+ private long id;
+ private String name;
+ private List subcategories = new ArrayList();
+ private Assignable assignable;
+ /**
+ * Returns the id.
+ * @return long
+ */
+ public long getId() {
+ return id;
+ }
+
+ /**
+ * Sets the id.
+ * @param id The id to set
+ */
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ /**
+ * Returns the subcategories.
+ * @return List
+ */
+ public List getSubcategories() {
+ return subcategories;
+ }
+
+ /**
+ * Sets the subcategories.
+ * @param subcategories The subcategories to set
+ */
+ public void setSubcategories(List subcategories) {
+ this.subcategories = subcategories;
+ }
+
+ /**
+ * Returns the name.
+ * @return String
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the name.
+ * @param name The name to set
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Assignable getAssignable() {
+ return assignable;
+ }
+
+ public void setAssignable(Assignable assignable) {
+ this.assignable = assignable;
+ }
+
+ public String toString() {
+ return id + ":" + name;
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Child.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Child.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Child.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,48 @@
+//$Id: Child.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+public class Child {
+ private Parent parent;
+ private int count;
+ private int x;
+
+ public int getX() {
+ return x;
+ }
+ public void setX(int x) {
+ this.x = x;
+ }
+
+ public Parent getParent() {
+ return parent;
+ }
+
+
+ public void setParent(Parent parent) {
+ this.parent = parent;
+ }
+
+
+ public int getCount() {
+ return count;
+ }
+
+
+ public void setCount(int count) {
+ this.count = count;
+ }
+
+ public long getId() {
+ return parent.getId();
+ }
+ private void setId(long id) {
+ }
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Circular.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Circular.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Circular.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping default-lazy="false" default-cascade="save-update">
+
+ <class name="org.hibernate.test.legacy.Circular" dynamic-update="true">
+ <id name="id" column="id_" length="64" unsaved-value="null">
+ <generator class="uuid"/>
+ </id>
+ <property name="clazz"/>
+ <many-to-one name="other"/>
+ <property name="anyEntity">
+ <column name="classname"/>
+ <column name="any_id"/>
+ </property>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Circular.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Circular.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Circular.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,88 @@
+//$Id: Circular.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+public class Circular {
+
+ private String id;
+ private Class clazz;
+ private Circular other;
+ private Object anyEntity;
+
+ /**
+ * Constructor for Circular.
+ */
+ public Circular() {
+ super();
+ }
+
+ /**
+ * Returns the clazz.
+ * @return Class
+ */
+ public Class getClazz() {
+ return clazz;
+ }
+
+ /**
+ * Returns the id.
+ * @return String
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Sets the clazz.
+ * @param clazz The clazz to set
+ */
+ public void setClazz(Class clazz) {
+ this.clazz = clazz;
+ }
+
+ /**
+ * Sets the id.
+ * @param id The id to set
+ */
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ /**
+ * Returns the other.
+ * @return Circular
+ */
+ public Circular getOther() {
+ return other;
+ }
+
+ /**
+ * Sets the other.
+ * @param other The other to set
+ */
+ public void setOther(Circular other) {
+ this.other = other;
+ }
+
+ /**
+ * Returns the anyEntity.
+ * @return Object
+ */
+ public Object getAnyEntity() {
+ return anyEntity;
+ }
+
+ /**
+ * Sets the anyEntity.
+ * @param anyEntity The anyEntity to set
+ */
+ public void setAnyEntity(Object anyEntity) {
+ this.anyEntity = anyEntity;
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Commento.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Commento.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Commento.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -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">
+<hibernate-mapping default-lazy="false">
+<!--
+ Created by the Middlegen Hibernate plugin 2.1
+
+ http://boss.bekk.no/boss/middlegen/
+ http://hibernate.sourceforge.net/
+-->
+
+<class entity-name="Commento" table="MARECM">
+ <id name="mclink" type="long" column="MCLINK">
+ <generator class="assigned"/>
+ </id>
+
+ <property name="mcompr" type="java.lang.String" column="MCOMPR" length="1"/>
+ <property name="mcomme" type="java.lang.String" column="MCOMME" length="30"/>
+
+ <!-- Associations -->
+ <!--many-to-one name="marelo" class="Marelo" column="MCLINK" unique="true"/-->
+ <one-to-one name="marelo" entity-name="Marelo" outer-join="auto" property-ref="commento"/>
+
+</class>
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Company.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Company.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Company.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,29 @@
+package org.hibernate.test.legacy;
+
+/**
+ * @author hbm2java
+ */
+public class Company extends org.hibernate.test.legacy.Party {
+
+ java.lang.String id;
+ java.lang.String president;
+
+
+ java.lang.String getId() {
+ return id;
+ }
+
+ void setId(java.lang.String newValue) {
+ id = newValue;
+ }
+
+ java.lang.String getPresident() {
+ return president;
+ }
+
+ void setPresident(java.lang.String newValue) {
+ president = newValue;
+ }
+
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Component.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Component.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Component.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,42 @@
+//$Id: Component.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+/**
+ * Component
+ *
+ * @author Emmanuel Bernard
+ */
+public class Component {
+ private String _name;
+
+ private SubComponent _subComponent;
+
+ /**
+ * @return
+ */
+ public String getName() {
+ return _name;
+ }
+
+ /**
+ * @param string
+ */
+ public void setName(String string) {
+ _name = string;
+ }
+
+ /**
+ * @return
+ */
+ public SubComponent getSubComponent() {
+ return _subComponent;
+ }
+
+ /**
+ * @param component
+ */
+ public void setSubComponent(SubComponent component) {
+ _subComponent = component;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ComponentCollection.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ComponentCollection.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ComponentCollection.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,35 @@
+//$Id: ComponentCollection.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+import java.util.List;
+
+public class ComponentCollection implements Serializable {
+ private String str;
+ private List foos;
+ private List floats;
+ public List getFoos() {
+ return foos;
+ }
+
+ public String getStr() {
+ return str;
+ }
+
+ public void setFoos(List list) {
+ foos = list;
+ }
+
+ public void setStr(String string) {
+ str = string;
+ }
+
+ public List getFloats() {
+ return floats;
+ }
+
+ public void setFloats(List list) {
+ floats = list;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ComponentNotNull.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ComponentNotNull.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ComponentNotNull.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,90 @@
+//$Id: ComponentNotNull.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+/**
+ * Component used to check not-null sub properties management
+ *
+ * @author Emmanuel Bernard
+ */
+public class ComponentNotNull {
+ /*
+ * I've flatten several components in one class, this is kind of ugly but
+ * I don't have to write tons of classes
+ */
+ private String prop1Nullable;
+ private String prop2Nullable;
+ private ComponentNotNull supercomp;
+ private ComponentNotNull subcomp;
+ private String prop1Subcomp;
+
+ /**
+ * @return
+ */
+ public String getProp1Nullable() {
+ return prop1Nullable;
+ }
+
+ /**
+ * @return
+ */
+ public String getProp1Subcomp() {
+ return prop1Subcomp;
+ }
+
+ /**
+ * @return
+ */
+ public String getProp2Nullable() {
+ return prop2Nullable;
+ }
+
+ /**
+ * @return
+ */
+ public ComponentNotNull getSubcomp() {
+ return subcomp;
+ }
+
+ /**
+ * @return
+ */
+ public ComponentNotNull getSupercomp() {
+ return supercomp;
+ }
+
+ /**
+ * @param string
+ */
+ public void setProp1Nullable(String string) {
+ prop1Nullable = string;
+ }
+
+ /**
+ * @param string
+ */
+ public void setProp1Subcomp(String string) {
+ prop1Subcomp = string;
+ }
+
+ /**
+ * @param string
+ */
+ public void setProp2Nullable(String string) {
+ prop2Nullable = string;
+ }
+
+ /**
+ * @param null1
+ */
+ public void setSubcomp(ComponentNotNull null1) {
+ subcomp = null1;
+ }
+
+ /**
+ * @param null1
+ */
+ public void setSupercomp(ComponentNotNull null1) {
+ supercomp = null1;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ComponentNotNullMaster.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ComponentNotNullMaster.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ComponentNotNullMaster.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,64 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping default-lazy="false">
+
+ <class name="org.hibernate.test.legacy.ComponentNotNullMaster"> <!-- discriminator-value="A"> -->
+ <id name="id" column="container_id">
+ <generator class="native" />
+ </id>
+ <property name="test"/>
+
+ <component name="nullable">
+ <property name="prop1Nullable" />
+ <property name="prop2Nullable"/>
+ </component>
+
+ <component name="supercomp">
+ <component name="subcomp">
+ <property name="prop1Subcomp" not-null="true"/>
+ </component>
+ </component>
+
+ <list name="components" lazy="true">
+ <key column = "container_id"/>
+ <index column = "list_index"/>
+ <composite-element class="org.hibernate.test.legacy.ComponentNotNullMaster$ContainerInnerClass">
+ <property name="name"/>
+ <property name="count" column="count_"/>
+ <many-to-one name="simple" not-null="true" cascade="save-update"/>
+ <many-to-one name="one" cascade="save-update"/>
+ <many-to-one name="many" cascade="save-update"/>
+ </composite-element>
+ </list>
+
+
+ <list name="componentsImplicit" lazy="true">
+ <key column = "container_id"/>
+ <index column = "list_index3"/>
+ <composite-element class="org.hibernate.test.legacy.ComponentNotNullMaster$ContainerInnerClass">
+ <property name="name"/>
+ <property name="count" column="count_"/>
+ <many-to-one name="simple" cascade="save-update"/>
+ <many-to-one name="one" cascade="save-update"/>
+ <nested-composite-element name="nested" class="org.hibernate.test.legacy.ComponentNotNullMaster$ContainerInnerClass">
+ <property name="nestedproperty" not-null="true"/>
+ </nested-composite-element>
+ </composite-element>
+ </list>
+ <!-- subclass name="org.hibernate.test.legacy.ExtendedComponentNotNull" discriminator-value="B">
+ <component name="subclassComp" not-null="true">
+ <property name="subclass" />
+ </component>
+ </subclass -->
+ <!-- joined-subclass name="org.hibernate.test.legacy.ExtendedComponentNotNull">
+ <key column="id"/>
+ <component name="subclassComp" not-null="true">
+ <property name="subclass" />
+ </component>
+ </joined-subclass -->
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ComponentNotNullMaster.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ComponentNotNullMaster.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ComponentNotNullMaster.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,142 @@
+//$Id: ComponentNotNullMaster.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.util.List;
+
+/**
+ * Entity containing components for not-null testing
+ *
+ * @author Emmanuel Bernard
+ */
+public class ComponentNotNullMaster {
+
+ private int id;
+ private String test;
+ private ComponentNotNull nullable;
+ private ComponentNotNull supercomp;
+ private List components;
+ private List componentsImplicit;
+
+ public int getId() {
+ return id;
+ }
+
+ public ComponentNotNull getNullable() {
+ return nullable;
+ }
+
+ public void setId(int i) {
+ id = i;
+ }
+
+ public void setNullable(ComponentNotNull component) {
+ nullable = component;
+ }
+
+ public static final class ContainerInnerClass {
+ private Simple simple;
+ private String name;
+ private One one;
+ private Many many;
+ private int count;
+ private ContainerInnerClass nested;
+ private String nestedproperty;
+
+ public void setSimple(Simple simple) {
+ this.simple = simple;
+ }
+
+ public Simple getSimple() {
+ return simple;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String toString() {
+ return name + " = " + simple.getCount() +
+ "/" + ( one==null ? "nil" : one.getKey().toString() ) +
+ "/" + ( many==null ? "nil" : many.getKey().toString() );
+ }
+
+ public One getOne() {
+ return one;
+ }
+
+ public void setOne(One one) {
+ this.one = one;
+ }
+
+ public Many getMany() {
+ return many;
+ }
+
+ public void setMany(Many many) {
+ this.many = many;
+ }
+
+ public int getCount() {
+ return count;
+ }
+
+ public void setCount(int count) {
+ this.count = count;
+ }
+
+ public ContainerInnerClass getNested() {
+ return nested;
+ }
+
+ public void setNested(ContainerInnerClass class1) {
+ nested = class1;
+ }
+
+ public String getNestedproperty() {
+ return nestedproperty;
+ }
+
+ public void setNestedproperty(String string) {
+ nestedproperty = string;
+ }
+
+ }
+
+ public List getComponents() {
+ return components;
+ }
+
+ public void setComponents(List list) {
+ components = list;
+ }
+
+ public List getComponentsImplicit() {
+ return componentsImplicit;
+ }
+
+ public void setComponentsImplicit(List list) {
+ componentsImplicit = list;
+ }
+
+ public ComponentNotNull getSupercomp() {
+ return supercomp;
+ }
+
+ public void setSupercomp(ComponentNotNull component) {
+ supercomp = component;
+ }
+
+ public String getTest() {
+ return test;
+ }
+
+ public void setTest(String string) {
+ test = string;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ComponentNotNullTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ComponentNotNullTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ComponentNotNullTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,206 @@
+//{Id:}
+package org.hibernate.test.legacy;
+
+import java.util.ArrayList;
+
+import junit.framework.Test;
+import junit.textui.TestRunner;
+
+import org.hibernate.PropertyValueException;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * Test some cases of not-null properties inside components.
+ *
+ * @author Emmanuel Bernard
+ */
+public class ComponentNotNullTest extends LegacyTestCase {
+
+ public ComponentNotNullTest(String x) {
+ super(x);
+ }
+
+ public String[] getMappings() {
+ return new String[] {
+ "legacy/ComponentNotNullMaster.hbm.xml",
+ "legacy/One.hbm.xml",
+ "legacy/Many.hbm.xml",
+ "legacy/Simple.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( ComponentNotNullTest.class );
+ }
+
+ public static void main(String[] args) throws Exception {
+ TestRunner.run(suite());
+ }
+
+ public void testComponentNotNull() throws Exception {
+
+ //everything not null
+ //
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ ComponentNotNullMaster master = new ComponentNotNullMaster();
+ ComponentNotNull nullable = new ComponentNotNull();
+ ComponentNotNull supercomp = new ComponentNotNull();
+ ComponentNotNull subcomp = new ComponentNotNull();
+
+ master.setNullable(nullable);
+ subcomp.setProp1Subcomp("test");
+ supercomp.setSubcomp(subcomp);
+ master.setSupercomp(supercomp);
+ s.save(master);
+ t.commit();
+ s.close();
+
+ //null prop of a subcomp
+ //
+ s = openSession();
+ t = s.beginTransaction();
+
+ master = new ComponentNotNullMaster();
+ nullable = new ComponentNotNull();
+ supercomp = new ComponentNotNull();
+ subcomp = new ComponentNotNull();
+
+ master.setNullable(nullable);
+ // do not set property
+ //subcomp.setProp1Subcomp("test");
+ supercomp.setSubcomp(subcomp);
+ master.setSupercomp(supercomp);
+
+
+ try {
+ s.save(master);
+ t.commit();
+ fail("Inserting not-null null property should fail");
+ } catch (PropertyValueException e) {
+ //succeed
+ }
+ t.rollback();
+ s.close();
+
+ //null component having not-null column
+ //
+ s = openSession();
+ t = s.beginTransaction();
+
+ master = new ComponentNotNullMaster();
+ nullable = new ComponentNotNull();
+ supercomp = new ComponentNotNull();
+ subcomp = new ComponentNotNull();
+
+ master.setNullable(nullable);
+ // do not set supercomp for master
+ //subcomp.setProp1Subcomp("test");
+ //supercomp.setSubcomp(subcomp);
+ //master.setSupercomp(supercomp);
+
+
+ try {
+ s.save(master);
+ t.commit();
+ fail("Inserting not-null null property should fail");
+ } catch (PropertyValueException e) {
+ //succeed
+ }
+ t.rollback();
+ s.close();
+ }
+
+ public void testCompositeElement() throws Exception {
+ //composite-element nullable
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ ComponentNotNullMaster master = new ComponentNotNullMaster();
+ ComponentNotNull nullable = new ComponentNotNull();
+ ComponentNotNull supercomp = new ComponentNotNull();
+ ComponentNotNull subcomp = new ComponentNotNull();
+
+ master.setNullable(nullable);
+ subcomp.setProp1Subcomp("test");
+ supercomp.setSubcomp(subcomp);
+ master.setSupercomp(supercomp);
+
+ master.setComponents(new ArrayList());
+ ComponentNotNullMaster.ContainerInnerClass cc =
+ new ComponentNotNullMaster.ContainerInnerClass();
+ master.getComponents().add(cc);
+
+ try {
+ s.save(master);
+ t.commit();
+ fail("Inserting not-null many-to-one should fail");
+ } catch (PropertyValueException e) {
+ //success
+ }
+ t.rollback();
+ s.close();
+
+ //null nested component having not-null column
+ //
+ s = openSession();
+ t = s.beginTransaction();
+
+ master = new ComponentNotNullMaster();
+ nullable = new ComponentNotNull();
+ supercomp = new ComponentNotNull();
+ subcomp = new ComponentNotNull();
+
+ master.setNullable(nullable);
+ subcomp.setProp1Subcomp("test");
+ supercomp.setSubcomp(subcomp);
+ master.setSupercomp(supercomp);
+
+ master.setComponentsImplicit(new ArrayList());
+ ComponentNotNullMaster.ContainerInnerClass nestedCc =
+ new ComponentNotNullMaster.ContainerInnerClass();
+ cc =
+ new ComponentNotNullMaster.ContainerInnerClass();
+ cc.setNested(nestedCc);
+ master.getComponentsImplicit().add(cc);
+
+ try {
+ s.save(master);
+ t.commit();
+ fail("Inserting not-null null property should fail");
+ } catch (PropertyValueException e) {
+ //succeed
+ }
+ t.rollback();
+ s.close();
+
+ //nested component having not-null column
+ //
+ s = openSession();
+ t = s.beginTransaction();
+
+ master = new ComponentNotNullMaster();
+ nullable = new ComponentNotNull();
+ supercomp = new ComponentNotNull();
+ subcomp = new ComponentNotNull();
+
+ master.setNullable(nullable);
+ subcomp.setProp1Subcomp("test");
+ supercomp.setSubcomp(subcomp);
+ master.setSupercomp(supercomp);
+
+ master.setComponentsImplicit(new ArrayList());
+ nestedCc =
+ new ComponentNotNullMaster.ContainerInnerClass();
+ cc =
+ new ComponentNotNullMaster.ContainerInnerClass();
+ cc.setNested(nestedCc);
+ nestedCc.setNestedproperty("test");
+ master.getComponentsImplicit().add(cc);
+
+ s.save(master);
+ t.commit();
+ s.close();
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Componentizable.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Componentizable.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Componentizable.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping default-lazy="false">
+ <class name="org.hibernate.test.legacy.Componentizable">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="nickName"/>
+ <component name="component" access="property" class="org.hibernate.test.legacy.Component">
+ <property name="name"/>
+ <component name="subComponent" class="org.hibernate.test.legacy.SubComponent">
+ <property name="subName"/>
+ <property name="subName1"/>
+ </component>
+ </component>
+ </class>
+
+ <sql-query name="queryComponentWithOtherColumn">
+ <return class="org.hibernate.test.legacy.Componentizable">
+ <return-property name="nickName" column="n2"/>
+ <return-property name="component.subComponent.subName">
+ <return-column name="otherSubName"/>
+ </return-property>
+ <return-property name="component.subComponent.subName1">
+ <return-column name="subName1"/>
+ </return-property>
+ <return-property name="component.name">
+ <return-column name="name"/>
+ </return-property>
+ </return>
+ select id, nickName as n2, name, subName as otherSubName, subName1 from Componentizable
+ </sql-query>
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Componentizable.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Componentizable.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Componentizable.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,60 @@
+//$Id: Componentizable.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+/**
+ * contains components
+ *
+ * @author emmanuel
+ */
+public class Componentizable {
+ /** surrogate id */
+ private Integer _id;
+
+ public String _nickName;
+
+ /** component */
+ private Component _component;
+
+ /**
+ * @return
+ */
+ public Integer getId() {
+ return _id;
+ }
+
+ /**
+ * @param integer
+ */
+ public void setId(Integer integer) {
+ _id = integer;
+ }
+
+ /**
+ * @return
+ */
+ public Component getComponent() {
+ return _component;
+ }
+
+ /**
+ * @param component
+ */
+ public void setComponent(Component component) {
+ _component = component;
+ }
+
+ /**
+ * @return
+ */
+ public String getNickName() {
+ return _nickName;
+ }
+
+ /**
+ * @param string
+ */
+ public void setNickName(String string) {
+ _nickName = string;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/CompositeElement.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/CompositeElement.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/CompositeElement.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,57 @@
+//$Id: CompositeElement.java 6844 2005-05-21 14:22:16Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+
+public class CompositeElement implements Comparable, Serializable {
+ private String foo;
+ private String bar;
+ /**
+ * Returns the bar.
+ * @return String
+ */
+ public String getBar() {
+ return bar;
+ }
+
+ /**
+ * Returns the foo.
+ * @return String
+ */
+ public String getFoo() {
+ return foo;
+ }
+
+ /**
+ * Sets the bar.
+ * @param bar The bar to set
+ */
+ public void setBar(String bar) {
+ this.bar = bar;
+ }
+
+ /**
+ * Sets the foo.
+ * @param foo The foo to set
+ */
+ public void setFoo(String foo) {
+ this.foo = foo;
+ }
+
+ /**
+ * @see java.lang.Comparable#compareTo(java.lang.Object)
+ */
+ public int compareTo(Object o) {
+ return ( (CompositeElement) o ).foo.compareTo(foo);
+ }
+
+ public int hashCode() {
+ return foo.hashCode() + bar.hashCode();
+ }
+
+ public boolean equals(Object that) {
+ CompositeElement ce = (CompositeElement) that;
+ return ce.bar.equals(bar) && ce.foo.equals(foo);
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/CompositeIdId.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/CompositeIdId.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/CompositeIdId.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping package="org.hibernate.test.legacy">
+ <class name="CompositeIdId">
+ <composite-id>
+ <key-property name="system"/>
+ <key-property name="id"/>
+ </composite-id>
+ <property name="name"/>
+
+ <component name="composite">
+ <property name="foo"/>
+ <property name="bar"/>
+ </component>
+ <loader query-ref="loadC"/>
+ </class>
+
+ <sql-query name="loadC">
+ <return alias="c" class="CompositeIdId"/>
+ select system as {c.system}, id as {c.id}, name as {c.name}, foo as {c.composite.foo}, bar as {c.composite.bar} from CompositeIdId where system=? and id=?
+ </sql-query>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/CompositeIdId.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/CompositeIdId.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/CompositeIdId.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,55 @@
+/*
+ * Created on 20-Dec-2004
+ *
+ */
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+
+/**
+ * @author max
+ *
+ */
+public class CompositeIdId implements Serializable {
+
+ String system;
+ String id;
+ String name;
+ CompositeElement composite;
+
+
+ public String getId() {
+ return id;
+ }
+ public void setId(String id) {
+ this.id = id;
+ }
+ public String getSystem() {
+ return system;
+ }
+ public void setSystem(String system) {
+ this.system = system;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ public boolean equals(Object obj) {
+ // not totally NP or type safe equals, but enough for the unittests.
+ CompositeIdId o = (CompositeIdId) obj;
+ if(o==null) return false;
+ return o.getSystem().equals(getSystem()) && o.getId().equals(getId());
+ }
+ public CompositeElement getComposite() {
+ return composite;
+ }
+ public void setComposite(CompositeElement composite) {
+ this.composite = composite;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ConfigurationPerformanceTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ConfigurationPerformanceTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ConfigurationPerformanceTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,255 @@
+/*
+ * Created on 01-05-2004
+ *
+ */
+package org.hibernate.test.legacy;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.PrintWriter;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import junit.textui.TestRunner;
+
+import org.hibernate.HibernateException;
+import org.hibernate.SessionFactory;
+import org.hibernate.junit.UnitTestCase;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.classic.Session;
+
+/**
+ * @author MAX
+ *
+ */
+public class ConfigurationPerformanceTest extends UnitTestCase {
+
+ String[] files = new String[] {
+ "legacy/ABC.hbm.xml",
+ "legacy/ABCExtends.hbm.xml",
+ "legacy/Baz.hbm.xml",
+ "legacy/Blobber.hbm.xml",
+ "legacy/Broken.hbm.xml",
+ "legacy/Category.hbm.xml",
+ "legacy/Circular.hbm.xml",
+ "legacy/Commento.hbm.xml",
+ "legacy/ComponentNotNullMaster.hbm.xml",
+ "legacy/Componentizable.hbm.xml",
+ "legacy/Container.hbm.xml",
+ "legacy/Custom.hbm.xml",
+ "legacy/CustomSQL.hbm.xml",
+ "legacy/Eye.hbm.xml",
+ "legacy/Fee.hbm.xml",
+ "legacy/Fo.hbm.xml",
+ "legacy/FooBar.hbm.xml",
+ "legacy/Fum.hbm.xml",
+ "legacy/Fumm.hbm.xml",
+ "legacy/Glarch.hbm.xml",
+ "legacy/Holder.hbm.xml",
+ "legacy/IJ2.hbm.xml",
+ "legacy/Immutable.hbm.xml",
+ "legacy/Location.hbm.xml",
+ "legacy/Many.hbm.xml",
+ "legacy/Map.hbm.xml",
+ "legacy/Marelo.hbm.xml",
+ "legacy/MasterDetail.hbm.xml",
+ "legacy/Middle.hbm.xml",
+ "legacy/Multi.hbm.xml",
+ "legacy/MultiExtends.hbm.xml",
+ "legacy/Nameable.hbm.xml",
+ "legacy/One.hbm.xml",
+ "legacy/ParentChild.hbm.xml",
+ "legacy/Qux.hbm.xml",
+ "legacy/Simple.hbm.xml",
+ "legacy/SingleSeveral.hbm.xml",
+ "legacy/Stuff.hbm.xml",
+ "legacy/UpDown.hbm.xml",
+ "legacy/Vetoer.hbm.xml",
+ "legacy/WZ.hbm.xml",
+ };
+
+ boolean keepFilesAround = false ; // set to true to be able to "coldstart" start.
+
+ public ConfigurationPerformanceTest(String string) {
+ super( string );
+ }
+
+ public void testLoadingAndSerializationOfConfiguration() throws HibernateException, FileNotFoundException, IOException, ClassNotFoundException {
+
+ String prefix = "./test/org/hibernate/test/";
+ try {
+ // first time
+ System.err.println("###FIRST SAVELOAD###");
+ saveAndLoad(prefix,files, "hibernate.cfg.bin");
+ // second time to validate
+ System.err.println("###SECOND SAVELOAD###");
+ saveAndLoad(prefix,files, "hibernate.cfg.bin");
+ }
+ finally {
+ System.err.println( "###CLEANING UP###" );
+ if(!keepFilesAround) {
+ File file = null;
+ try {
+ // clean up
+ file = new File("hibernate.cfg.bin");
+ file.delete();
+ }
+ catch( Throwable t ) {
+ System.err.println( "Unable to cleanup [" + file + "] : " + t );
+ }
+
+ for (int i = 0; i < files.length; i++) {
+ try {
+ String fileName = files[i];
+ file = new File( prefix, fileName + ".bin" );
+ file.delete();
+ }
+ catch( Throwable t ) {
+ System.err.println( "Unable to cleanup [" + file + "] : " + t );
+ }
+ }
+ }
+ }
+
+ }
+
+ // this method requires generation of test files (can be done with generateTestFiles) + their compile
+ public void xtestSessionFactoryCreationTime() throws FileNotFoundException, IOException, ClassNotFoundException {
+ File perfs = new File("perfsrc");
+ generateTestFiles(perfs, "perftest");
+ if(perfs.exists()) {
+ SessionFactory factory = saveAndLoad("perfsrc/perftest/", new File(perfs, "perftest").list(new FilenameFilter() {
+
+ public boolean accept(File dir, String name) {
+ return name.endsWith(".hbm.xml");
+ }
+
+ }), "hibernateperftest.cfg.bin");
+
+ Session session = factory.openSession();
+ Object o = session.load("perftest.Test1", new Long(42));
+ System.out.println(o);
+ } else {
+ System.err.println(perfs.getAbsoluteFile() + " not found");
+ }
+ }
+
+ private SessionFactory saveAndLoad(String prefix, String[] files, String cfgName) throws IOException, FileNotFoundException, ClassNotFoundException {
+ long start = System.currentTimeMillis();
+
+ Configuration cfg = new Configuration();
+ System.err.println("Created configuration: " + (System.currentTimeMillis() - start) / 1000.0 + " sec.");
+
+ System.err.println("saveAndLoad from " + prefix + " with cfg = " + cfgName);
+ if(!new File(cfgName).exists()) {
+ start = System.currentTimeMillis();
+ /*for (int i=0; i<files.length; i++) {
+ if ( !files[i].startsWith("net/") ) files[i] = "test/org/hibernate/test/" + files[i];
+ cfg.addFile(files[i]);
+ //cfg.addLazyFile(files[i]);
+ }*/
+
+ for (int i = 0; i < files.length; i++) {
+ String file = files[i];
+ cfg.addCacheableFile(new File(prefix + file));
+ }
+
+ System.err.println("Added " + (files.length) + " resources: " + (System.currentTimeMillis() - start) / 1000.0 + " sec.");
+
+ ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(cfgName));
+ os.writeObject(cfg); // need to serialize Configuration *before* building sf since it would require non-mappings and cfg types to be serializable
+ os.flush();
+ os.close();
+
+ } else {
+ start = System.currentTimeMillis();
+ ObjectInputStream is = new ObjectInputStream(new FileInputStream(cfgName));
+ cfg = (Configuration) is.readObject();
+ is.close();
+ System.err.println("Loaded serializable configuration:" + (System.currentTimeMillis() - start) / 1000.0 + " sec.");
+ }
+ start = System.currentTimeMillis();
+ System.err.println("Start build of session factory");
+ SessionFactory factory = cfg.buildSessionFactory();
+ System.err.println("Build session factory:" + (System.currentTimeMillis() - start) / 1000.0 + " sec.");
+ return factory;
+ }
+
+
+ public static Test suite() {
+ return new TestSuite(ConfigurationPerformanceTest.class);
+ }
+
+ public static void main(String[] args) throws Exception {
+ TestRunner.run( suite() );
+ }
+
+ public void generateTestFiles(File basedir, String pkgName) throws IOException {
+
+ for(int count=0;count<100;count++) {
+ String name = "Test" + count;
+ File javaFile = new File(new File(basedir, pkgName), name + ".java");
+ File hbmFile = new File(new File(basedir, pkgName), name + ".hbm.xml");
+
+ javaFile.getParentFile().mkdirs();
+ hbmFile.getParentFile().mkdirs();
+
+ System.out.println("Generating " + javaFile.getAbsolutePath());
+ PrintWriter javaWriter = null;
+ PrintWriter hbmWriter = null;
+ try {
+ javaWriter = new PrintWriter(new FileWriter(javaFile));
+ hbmWriter = new PrintWriter(new FileWriter(hbmFile));
+
+ javaWriter.println("package " + pkgName + ";");
+ hbmWriter.println("<?xml version=\"1.0\"?>\r\n" +
+ "<!DOCTYPE hibernate-mapping PUBLIC \r\n" +
+ " \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\"\r\n" +
+ " \"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd\">\r\n");
+
+ hbmWriter.println("<hibernate-mapping package=\"" + pkgName + "\">");
+
+ javaWriter.println("public class " + name + " {");
+ javaWriter.println(" static { System.out.println(\"" + name + " initialized!\"); }");
+ hbmWriter.println("<class name=\"" + name + "\">");
+
+ hbmWriter.println("<id type=\"long\"><generator class=\"assigned\"/></id>");
+ for(int propCount=0;propCount<100;propCount++) {
+ String propName = "Prop" + propCount;
+
+ writeJavaProperty(javaWriter, propName);
+
+ hbmWriter.println("<property name=\"" + propName + "\" type=\"string\"/>");
+
+ }
+ hbmWriter.println("</class>");
+ javaWriter.println("}");
+ hbmWriter.println("</hibernate-mapping>");
+ } finally {
+ if(javaWriter!=null) {
+ javaWriter.flush();
+ javaWriter.close();
+ }
+ if(hbmWriter!=null) {
+ hbmWriter.flush();
+ hbmWriter.close();
+ }
+ }
+ }
+
+ }
+
+
+ private void writeJavaProperty(PrintWriter javaWriter, String propName) {
+ javaWriter.println(" String " + propName + ";");
+ javaWriter.println(" String get" + propName + "() { return " + propName + "; }");
+ javaWriter.println(" void set" + propName + "(String newVal) { " + propName + "=newVal; }");
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Contained.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Contained.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Contained.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,90 @@
+//$Id: Contained.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+public class Contained {
+ private Container container;
+ private long id;
+ private Collection bag = new ArrayList();
+ private Collection lazyBag = new ArrayList();
+
+ public boolean equals(Object other) {
+ return id==( (Contained) other ).getId();
+ }
+ public int hashCode() {
+ return new Long(id).hashCode();
+ }
+
+ /**
+ * Returns the container.
+ * @return Container
+ */
+ public Container getContainer() {
+ return container;
+ }
+
+ /**
+ * Returns the id.
+ * @return long
+ */
+ public long getId() {
+ return id;
+ }
+
+ /**
+ * Sets the container.
+ * @param container The container to set
+ */
+ public void setContainer(Container container) {
+ this.container = container;
+ }
+
+ /**
+ * Sets the id.
+ * @param id The id to set
+ */
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ /**
+ * Returns the bag.
+ * @return Collection
+ */
+ public Collection getBag() {
+ return bag;
+ }
+
+ /**
+ * Sets the bag.
+ * @param bag The bag to set
+ */
+ public void setBag(Collection bag) {
+ this.bag = bag;
+ }
+
+ /**
+ * Returns the lazyBag.
+ * @return Collection
+ */
+ public Collection getLazyBag() {
+ return lazyBag;
+ }
+
+ /**
+ * Sets the lazyBag.
+ * @param lazyBag The lazyBag to set
+ */
+ public void setLazyBag(Collection lazyBag) {
+ this.lazyBag = lazyBag;
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Container.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Container.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Container.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,98 @@
+<?xml version="1.0"?>
+
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping default-lazy="false" auto-import="false">
+
+ <import class="org.hibernate.test.legacy.Container" rename="ContainerX"/>
+ <import class="org.hibernate.test.legacy.Contained"/>
+ <import class="java.lang.Object" rename="Universe"/>
+
+ <class name="org.hibernate.test.legacy.Container" proxy="org.hibernate.test.legacy.Container">
+ <id name="id" column="container_id" unsaved-value="0">
+ <generator class="native" />
+ </id>
+ <list name="oneToMany" lazy="true">
+ <key column = "container_id1"/>
+ <index column = "list_index"/>
+ <one-to-many class="org.hibernate.test.legacy.Simple"/>
+ </list>
+ <list name="manyToMany" lazy="true">
+ <key column = "container_id2"/>
+ <index column = "list_index"/>
+ <many-to-many class="org.hibernate.test.legacy.Simple"/>
+ </list>
+ <list name="components" lazy="true">
+ <key column = "container_id"/>
+ <index column = "list_index"/>
+ <composite-element class="org.hibernate.test.legacy.Container$ContainerInnerClass">
+ <property name="name"/>
+ <property name="count" column="count_"/>
+ <many-to-one name="simple"/>
+ <many-to-one name="one"/>
+ <many-to-one name="many"/>
+ </composite-element>
+ </list>
+ <set name="composites" lazy="true">
+ <key column = "container_id"/>
+ <composite-element class="org.hibernate.test.legacy.Container$ContainerInnerClass">
+ <property name="name"/>
+ <many-to-one name="simple"/>
+ <many-to-one name="one"/>
+ <many-to-one name="many"/>
+ </composite-element>
+ </set>
+ <bag name="cascades" cascade="all" table="abcd">
+ <key column="container_id"/>
+ <composite-element class="org.hibernate.test.legacy.Container$ContainerInnerClass">
+ <property name="name"/>
+ <many-to-one name="simple" cascade="all"/>
+ <many-to-one name="one" cascade="all" not-null="true"/>
+ <many-to-one name="many" cascade="all" not-null="true"/>
+ </composite-element>
+ </bag>
+ <bag name="bag" inverse="true" cascade="save-update" table="CCBAG">
+ <key column="container_id"/>
+ <many-to-many column="contained_id" class="org.hibernate.test.legacy.Contained" outer-join="true"/>
+ </bag>
+ <bag name="lazyBag" inverse="true" lazy="true" table="LCCBAG" cascade="save-update">
+ <!--cache usage="read-write"-->
+ <key column="container_id"/>
+ <many-to-many column="contained_id" class="org.hibernate.test.legacy.Contained"/>
+ </bag>
+ <map name="ternaryMap">
+ <key column="container_id"/>
+ <index column="idx" type="string" length="32"/>
+ <composite-element class="org.hibernate.test.legacy.Container$Ternary">
+ <property name="name"/>
+ <many-to-one name="foo"/>
+ <many-to-one name="glarch"/>
+ </composite-element>
+ </map>
+ <set name="ternarySet">
+ <key column="container_id"/>
+ <composite-element class="org.hibernate.test.legacy.Container$Ternary">
+ <property name="name" not-null="true"/>
+ <many-to-one name="foo" not-null="true"/>
+ <many-to-one name="glarch" not-null="true"/>
+ </composite-element>
+ </set>
+ </class>
+
+ <class name="org.hibernate.test.legacy.Contained" proxy="org.hibernate.test.legacy.Contained">
+ <id name="id" column="container_id" unsaved-value="0">
+ <generator class="native" />
+ </id>
+ <bag name="bag" table="CCBAG">
+ <key column="contained_id"/>
+ <many-to-many column="container_id" class="org.hibernate.test.legacy.Container" outer-join="false"/>
+ </bag>
+ <bag name="lazyBag" lazy="true" table="LCCBAG">
+ <key column="contained_id"/>
+ <many-to-many column="container_id" class="org.hibernate.test.legacy.Container"/>
+ </bag>
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Container.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Container.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Container.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,351 @@
+//$Id: Container.java 6844 2005-05-21 14:22:16Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class Container {
+
+ public static final class ContainerInnerClass {
+ private Simple simple;
+ private String name;
+ private One one;
+ private Many many;
+ private int count;
+
+ public void setSimple(Simple simple) {
+ this.simple = simple;
+ }
+
+ public Simple getSimple() {
+ return simple;
+ }
+ /**
+ * Returns the name.
+ * @return String
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the name.
+ * @param name The name to set
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String toString() {
+ return name + " = "
+ + (simple==null ? "nil" : Integer.toString( simple.getCount() ) )
+ + "/" + ( one==null ? "nil" : one.getKey().toString() )
+ + "/" + ( many==null ? "nil" : many.getKey().toString() );
+ }
+
+
+ /**
+ * Returns the one.
+ * @return One
+ */
+ public One getOne() {
+ return one;
+ }
+
+ /**
+ * Sets the one.
+ * @param one The one to set
+ */
+ public void setOne(One one) {
+ this.one = one;
+ }
+
+ /**
+ * Returns the many.
+ * @return Many
+ */
+ public Many getMany() {
+ return many;
+ }
+
+ /**
+ * Sets the many.
+ * @param many The many to set
+ */
+ public void setMany(Many many) {
+ this.many = many;
+ }
+
+ /**
+ * Returns the count.
+ * @return int
+ */
+ public int getCount() {
+ return count;
+ }
+
+ /**
+ * Sets the count.
+ * @param count The count to set
+ */
+ public void setCount(int count) {
+ this.count = count;
+ }
+
+ public int hashCode() {
+ return count + name.hashCode();
+ }
+
+ public boolean equals(Object other) {
+ ContainerInnerClass cic = (ContainerInnerClass) other;
+ return cic.name.equals(name)
+ && cic.count==count
+ && cic.one.getKey().equals(one.getKey())
+ && cic.many.getKey().equals(many.getKey())
+ && cic.simple.getCount()==simple.getCount();
+ }
+
+ }
+
+ private List oneToMany;
+ private List manyToMany;
+ private List components;
+ private Set composites;
+ private Collection cascades;
+ private long id;
+ private Collection bag;
+ private Collection lazyBag = new ArrayList();
+ private Map ternaryMap;
+ private Set ternarySet;
+
+ /**
+ * Constructor for Container.
+ */
+ public Container() {
+ super();
+ }
+
+ /**
+ * Returns the components.
+ * @return List
+ */
+ public List getComponents() {
+ return components;
+ }
+
+ /**
+ * Returns the manyToMany.
+ * @return List
+ */
+ public List getManyToMany() {
+ return manyToMany;
+ }
+
+ /**
+ * Returns the oneToMany.
+ * @return List
+ */
+ public List getOneToMany() {
+ return oneToMany;
+ }
+
+ /**
+ * Sets the components.
+ * @param components The components to set
+ */
+ public void setComponents(List components) {
+ this.components = components;
+ }
+
+ /**
+ * Sets the manyToMany.
+ * @param manyToMany The manyToMany to set
+ */
+ public void setManyToMany(List manyToMany) {
+ this.manyToMany = manyToMany;
+ }
+
+ /**
+ * Sets the oneToMany.
+ * @param oneToMany The oneToMany to set
+ */
+ public void setOneToMany(List oneToMany) {
+ this.oneToMany = oneToMany;
+ }
+
+ /**
+ * Returns the id.
+ * @return long
+ */
+ public long getId() {
+ return id;
+ }
+
+ /**
+ * Sets the id.
+ * @param id The id to set
+ */
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ /**
+ * Gets the composites.
+ * @return Returns a Set
+ */
+ public Set getComposites() {
+ return composites;
+ }
+
+ /**
+ * Sets the composites.
+ * @param composites The composites to set
+ */
+ public void setComposites(Set composites) {
+ this.composites = composites;
+ }
+
+ /**
+ * Returns the bag.
+ * @return Collection
+ */
+ public Collection getBag() {
+ return bag;
+ }
+
+ /**
+ * Sets the bag.
+ * @param bag The bag to set
+ */
+ public void setBag(Collection bag) {
+ this.bag = bag;
+ }
+
+ /**
+ * Returns the ternary.
+ * @return Map
+ */
+ public Map getTernaryMap() {
+ return ternaryMap;
+ }
+
+ /**
+ * Sets the ternary.
+ * @param ternary The ternary to set
+ */
+ public void setTernaryMap(Map ternary) {
+ this.ternaryMap = ternary;
+ }
+
+ public static final class Ternary {
+ private String name;
+ private Foo foo;
+ private Glarch glarch;
+ /**
+ * Returns the foo.
+ * @return Foo
+ */
+ public Foo getFoo() {
+ return foo;
+ }
+
+ /**
+ * Returns the glarch.
+ * @return Glarch
+ */
+ public Glarch getGlarch() {
+ return glarch;
+ }
+
+ /**
+ * Returns the name.
+ * @return String
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the foo.
+ * @param foo The foo to set
+ */
+ public void setFoo(Foo foo) {
+ this.foo = foo;
+ }
+
+ /**
+ * Sets the glarch.
+ * @param glarch The glarch to set
+ */
+ public void setGlarch(Glarch glarch) {
+ this.glarch = glarch;
+ }
+
+ /**
+ * Sets the name.
+ * @param name The name to set
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ }
+
+ /**
+ * Returns the ternarySet.
+ * @return Set
+ */
+ public Set getTernarySet() {
+ return ternarySet;
+ }
+
+ /**
+ * Sets the ternarySet.
+ * @param ternarySet The ternarySet to set
+ */
+ public void setTernarySet(Set ternarySet) {
+ this.ternarySet = ternarySet;
+ }
+
+ /**
+ * Returns the lazyBag.
+ * @return Collection
+ */
+ public Collection getLazyBag() {
+ return lazyBag;
+ }
+
+ /**
+ * Sets the lazyBag.
+ * @param lazyBag The lazyBag to set
+ */
+ public void setLazyBag(Collection lazyBag) {
+ this.lazyBag = lazyBag;
+ }
+
+ /**
+ * Returns the cascades.
+ * @return Collection
+ */
+ public Collection getCascades() {
+ return cascades;
+ }
+
+ /**
+ * Sets the cascades.
+ * @param cascades The cascades to set
+ */
+ public void setCascades(Collection cascades) {
+ this.cascades = cascades;
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Custom.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Custom.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Custom.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping default-lazy="false">
+
+ <class name="org.hibernate.test.legacy.Custom" persister="org.hibernate.test.legacy.CustomPersister">
+ <id type="string" name="id" column="id_" length="64" unsaved-value="null">
+ <generator class="uuid.hex"/>
+ </id>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Custom.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Custom.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Custom.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,32 @@
+//$Id: Custom.java 7275 2005-06-22 18:58:16Z oneovthafew $
+package org.hibernate.test.legacy;
+
+
+public class Custom implements Cloneable {
+ String id;
+ private String name;
+
+ public Object clone() {
+ try {
+ return super.clone();
+ }
+ catch (CloneNotSupportedException cnse) {
+ throw new RuntimeException();
+ }
+ }
+
+ void setName(String name) {
+ this.name = name;
+ }
+
+ String getName() {
+ return name;
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/CustomPersister.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/CustomPersister.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/CustomPersister.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,618 @@
+//$Id: CustomPersister.java 11398 2007-04-10 14:54:07Z steve.ebersole at jboss.com $
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Comparator;
+
+import org.hibernate.EntityMode;
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.LockMode;
+import org.hibernate.MappingException;
+import org.hibernate.tuple.entity.EntityMetamodel;
+import org.hibernate.cache.CacheConcurrencyStrategy;
+import org.hibernate.cache.access.EntityRegionAccessStrategy;
+import org.hibernate.cache.entry.CacheEntryStructure;
+import org.hibernate.cache.entry.UnstructuredCacheEntry;
+import org.hibernate.engine.CascadeStyle;
+import org.hibernate.engine.EntityKey;
+import org.hibernate.engine.Mapping;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.TwoPhaseLoad;
+import org.hibernate.engine.ValueInclusion;
+import org.hibernate.event.EventSource;
+import org.hibernate.event.PostLoadEvent;
+import org.hibernate.event.PreLoadEvent;
+import org.hibernate.id.IdentifierGenerator;
+import org.hibernate.id.UUIDHexGenerator;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.metadata.ClassMetadata;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.sql.QuerySelect;
+import org.hibernate.sql.Select;
+import org.hibernate.type.Type;
+import org.hibernate.type.VersionType;
+import org.hibernate.util.EqualsHelper;
+
+public class CustomPersister implements EntityPersister {
+
+ private static final Hashtable INSTANCES = new Hashtable();
+ private static final IdentifierGenerator GENERATOR = new UUIDHexGenerator();
+
+ private SessionFactoryImplementor factory;
+
+ public CustomPersister(
+ PersistentClass model,
+ EntityRegionAccessStrategy cacheAccessStrategy,
+ SessionFactoryImplementor factory,
+ Mapping mapping) {
+ this.factory = factory;
+ }
+
+ public boolean hasLazyProperties() {
+ return false;
+ }
+
+ private void checkEntityMode(EntityMode entityMode) {
+ if ( EntityMode.POJO != entityMode ) {
+ throw new IllegalArgumentException( "Unhandled EntityMode : " + entityMode );
+ }
+ }
+
+ public boolean isInherited() {
+ return false;
+ }
+
+ public SessionFactoryImplementor getFactory() {
+ return factory;
+ }
+
+ public Class getMappedClass() {
+ return Custom.class;
+ }
+
+ public void postInstantiate() throws MappingException {}
+
+ public String getEntityName() {
+ return Custom.class.getName();
+ }
+
+ public boolean isSubclassEntityName(String entityName) {
+ return Custom.class.getName().equals(entityName);
+ }
+
+ public boolean hasProxy() {
+ return false;
+ }
+
+ public boolean hasCollections() {
+ return false;
+ }
+
+ public boolean hasCascades() {
+ return false;
+ }
+
+ public boolean isMutable() {
+ return true;
+ }
+
+ public boolean isSelectBeforeUpdateRequired() {
+ return false;
+ }
+
+ public boolean isIdentifierAssignedByInsert() {
+ return false;
+ }
+
+ public Boolean isTransient(Object object, SessionImplementor session) {
+ return new Boolean( ( (Custom) object ).id==null );
+ }
+
+ public Object[] getPropertyValuesToInsert(Object object, Map mergeMap, SessionImplementor session)
+ throws HibernateException {
+ return getPropertyValues( object, session.getEntityMode() );
+ }
+
+ public void processInsertGeneratedProperties(Serializable id, Object entity, Object[] state, SessionImplementor session) {
+ }
+
+ public void processUpdateGeneratedProperties(Serializable id, Object entity, Object[] state, SessionImplementor session) {
+ }
+
+ public void retrieveGeneratedProperties(Serializable id, Object entity, Object[] state, SessionImplementor session) {
+ throw new UnsupportedOperationException();
+ }
+
+ public Class getMappedClass(EntityMode entityMode) {
+ checkEntityMode( entityMode );
+ return Custom.class;
+ }
+
+ public boolean implementsLifecycle(EntityMode entityMode) {
+ checkEntityMode( entityMode );
+ return false;
+ }
+
+ public boolean implementsValidatable(EntityMode entityMode) {
+ checkEntityMode( entityMode );
+ return false;
+ }
+
+ public Class getConcreteProxyClass(EntityMode entityMode) {
+ checkEntityMode( entityMode );
+ return Custom.class;
+ }
+
+ public void setPropertyValues(Object object, Object[] values, EntityMode entityMode) throws HibernateException {
+ checkEntityMode( entityMode );
+ setPropertyValue( object, 0, values[0], entityMode );
+ }
+
+ public void setPropertyValue(Object object, int i, Object value, EntityMode entityMode) throws HibernateException {
+ checkEntityMode( entityMode );
+ ( (Custom) object ).setName( (String) value );
+ }
+
+ public Object[] getPropertyValues(Object object, EntityMode entityMode) throws HibernateException {
+ checkEntityMode( entityMode );
+ Custom c = (Custom) object;
+ return new Object[] { c.getName() };
+ }
+
+ public Object getPropertyValue(Object object, int i, EntityMode entityMode) throws HibernateException {
+ checkEntityMode( entityMode );
+ return ( (Custom) object ).getName();
+ }
+
+ public Object getPropertyValue(Object object, String propertyName, EntityMode entityMode) throws HibernateException {
+ checkEntityMode( entityMode );
+ return ( (Custom) object ).getName();
+ }
+
+ public Serializable getIdentifier(Object object, EntityMode entityMode) throws HibernateException {
+ checkEntityMode( entityMode );
+ return ( (Custom) object ).id;
+ }
+
+ public void setIdentifier(Object object, Serializable id, EntityMode entityMode) throws HibernateException {
+ checkEntityMode( entityMode );
+ ( (Custom) object ).id = (String) id;
+ }
+
+ public Object getVersion(Object object, EntityMode entityMode) throws HibernateException {
+ checkEntityMode( entityMode );
+ return null;
+ }
+
+ public Object instantiate(Serializable id, EntityMode entityMode) throws HibernateException {
+ checkEntityMode( entityMode );
+ Custom c = new Custom();
+ c.id = (String) id;
+ return c;
+ }
+
+ public boolean isInstance(Object object, EntityMode entityMode) {
+ checkEntityMode( entityMode );
+ return object instanceof Custom;
+ }
+
+ public boolean hasUninitializedLazyProperties(Object object, EntityMode entityMode) {
+ checkEntityMode( entityMode );
+ return false;
+ }
+
+ public void resetIdentifier(Object entity, Serializable currentId, Object currentVersion, EntityMode entityMode) {
+ checkEntityMode( entityMode );
+ ( ( Custom ) entity ).id = ( String ) currentId;
+ }
+
+ public EntityPersister getSubclassEntityPersister(Object instance, SessionFactoryImplementor factory, EntityMode entityMode) {
+ checkEntityMode( entityMode );
+ return this;
+ }
+
+ public int[] findDirty(
+ Object[] x,
+ Object[] y,
+ Object owner,
+ SessionImplementor session
+ ) throws HibernateException {
+ if ( !EqualsHelper.equals( x[0], y[0] ) ) {
+ return new int[] { 0 };
+ }
+ else {
+ return null;
+ }
+ }
+
+ public int[] findModified(
+ Object[] x,
+ Object[] y,
+ Object owner,
+ SessionImplementor session
+ ) throws HibernateException {
+ if ( !EqualsHelper.equals( x[0], y[0] ) ) {
+ return new int[] { 0 };
+ }
+ else {
+ return null;
+ }
+ }
+
+ /**
+ * @see EntityPersister#hasIdentifierProperty()
+ */
+ public boolean hasIdentifierProperty() {
+ return true;
+ }
+
+ /**
+ * @see EntityPersister#isVersioned()
+ */
+ public boolean isVersioned() {
+ return false;
+ }
+
+ /**
+ * @see EntityPersister#getVersionType()
+ */
+ public VersionType getVersionType() {
+ return null;
+ }
+
+ /**
+ * @see EntityPersister#getVersionProperty()
+ */
+ public int getVersionProperty() {
+ return 0;
+ }
+
+ /**
+ * @see EntityPersister#getIdentifierGenerator()
+ */
+ public IdentifierGenerator getIdentifierGenerator()
+ throws HibernateException {
+ return GENERATOR;
+ }
+
+ /**
+ * @see EntityPersister#load(Serializable, Object, LockMode, SessionImplementor)
+ */
+ public Object load(
+ Serializable id,
+ Object optionalObject,
+ LockMode lockMode,
+ SessionImplementor session
+ ) throws HibernateException {
+
+ // fails when optional object is supplied
+
+ Custom clone = null;
+ Custom obj = (Custom) INSTANCES.get(id);
+ if (obj!=null) {
+ clone = (Custom) obj.clone();
+ TwoPhaseLoad.addUninitializedEntity(
+ new EntityKey( id, this, session.getEntityMode() ),
+ clone,
+ this,
+ LockMode.NONE,
+ false,
+ session
+ );
+ TwoPhaseLoad.postHydrate(
+ this, id,
+ new String[] { obj.getName() },
+ null,
+ clone,
+ LockMode.NONE,
+ false,
+ session
+ );
+ TwoPhaseLoad.initializeEntity(
+ clone,
+ false,
+ session,
+ new PreLoadEvent( (EventSource) session ),
+ new PostLoadEvent( (EventSource) session )
+ );
+ }
+ return clone;
+ }
+
+ /**
+ * @see EntityPersister#lock(Serializable, Object, Object, LockMode, SessionImplementor)
+ */
+ public void lock(
+ Serializable id,
+ Object version,
+ Object object,
+ LockMode lockMode,
+ SessionImplementor session
+ ) throws HibernateException {
+
+ throw new UnsupportedOperationException();
+ }
+
+ public void insert(
+ Serializable id,
+ Object[] fields,
+ Object object,
+ SessionImplementor session
+ ) throws HibernateException {
+
+ INSTANCES.put(id, ( (Custom) object ).clone() );
+ }
+
+ public Serializable insert(Object[] fields, Object object, SessionImplementor session)
+ throws HibernateException {
+
+ throw new UnsupportedOperationException();
+ }
+
+ public void delete(
+ Serializable id,
+ Object version,
+ Object object,
+ SessionImplementor session
+ ) throws HibernateException {
+
+ INSTANCES.remove(id);
+ }
+
+ /**
+ * @see EntityPersister
+ */
+ public void update(
+ Serializable id,
+ Object[] fields,
+ int[] dirtyFields,
+ boolean hasDirtyCollection,
+ Object[] oldFields,
+ Object oldVersion,
+ Object object,
+ Object rowId,
+ SessionImplementor session
+ ) throws HibernateException {
+
+ INSTANCES.put( id, ( (Custom) object ).clone() );
+
+ }
+
+ private static final Type[] TYPES = new Type[] { Hibernate.STRING };
+ private static final String[] NAMES = new String[] { "name" };
+ private static final boolean[] MUTABILITY = new boolean[] { true };
+ private static final boolean[] GENERATION = new boolean[] { false };
+
+ /**
+ * @see EntityPersister#getPropertyTypes()
+ */
+ public Type[] getPropertyTypes() {
+ return TYPES;
+ }
+
+ /**
+ * @see EntityPersister#getPropertyNames()
+ */
+ public String[] getPropertyNames() {
+ return NAMES;
+ }
+
+ /**
+ * @see EntityPersister#getPropertyCascadeStyles()
+ */
+ public CascadeStyle[] getPropertyCascadeStyles() {
+ return null;
+ }
+
+ /**
+ * @see EntityPersister#getIdentifierType()
+ */
+ public Type getIdentifierType() {
+ return Hibernate.STRING;
+ }
+
+ /**
+ * @see EntityPersister#getIdentifierPropertyName()
+ */
+ public String getIdentifierPropertyName() {
+ return "id";
+ }
+
+ public boolean hasCache() {
+ return false;
+ }
+
+ public EntityRegionAccessStrategy getCacheAccessStrategy() {
+ return null;
+ }
+
+ public String getRootEntityName() {
+ return "CUSTOMS";
+ }
+
+ public Serializable[] getPropertySpaces() {
+ return new String[] { "CUSTOMS" };
+ }
+
+ public Serializable[] getQuerySpaces() {
+ return new String[] { "CUSTOMS" };
+ }
+
+ /**
+ * @see EntityPersister#getClassMetadata()
+ */
+ public ClassMetadata getClassMetadata() {
+ return null;
+ }
+
+ public boolean[] getPropertyUpdateability() {
+ return MUTABILITY;
+ }
+
+ public boolean[] getPropertyCheckability() {
+ return MUTABILITY;
+ }
+
+ /**
+ * @see EntityPersister#getPropertyInsertability()
+ */
+ public boolean[] getPropertyInsertability() {
+ return MUTABILITY;
+ }
+
+ public ValueInclusion[] getPropertyInsertGenerationInclusions() {
+ return new ValueInclusion[0];
+ }
+
+ public ValueInclusion[] getPropertyUpdateGenerationInclusions() {
+ return new ValueInclusion[0];
+ }
+
+
+ public boolean canExtractIdOutOfEntity() {
+ return true;
+ }
+
+ public boolean isBatchLoadable() {
+ return false;
+ }
+
+ public Type getPropertyType(String propertyName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public Object getPropertyValue(Object object, String propertyName)
+ throws HibernateException {
+ throw new UnsupportedOperationException();
+ }
+
+ public Object createProxy(Serializable id, SessionImplementor session)
+ throws HibernateException {
+ throw new UnsupportedOperationException("no proxy for this class");
+ }
+
+ public Object getCurrentVersion(
+ Serializable id,
+ SessionImplementor session)
+ throws HibernateException {
+
+ return INSTANCES.get(id);
+ }
+
+ public Object forceVersionIncrement(Serializable id, Object currentVersion, SessionImplementor session)
+ throws HibernateException {
+ return null;
+ }
+
+ public EntityMode guessEntityMode(Object object) {
+ if ( !isInstance(object, EntityMode.POJO) ) {
+ return null;
+ }
+ else {
+ return EntityMode.POJO;
+ }
+ }
+
+ public boolean[] getPropertyNullability() {
+ return MUTABILITY;
+ }
+
+ public boolean isDynamic() {
+ return false;
+ }
+
+ public boolean isCacheInvalidationRequired() {
+ return false;
+ }
+
+ public void applyFilters(QuerySelect select, String alias, Map filters) {
+ }
+
+ public void applyFilters(Select select, String alias, Map filters) {
+ }
+
+
+ public void afterInitialize(Object entity, boolean fetched, SessionImplementor session) {
+ }
+
+ public void afterReassociate(Object entity, SessionImplementor session) {
+ }
+
+ public Object[] getDatabaseSnapshot(Serializable id, SessionImplementor session)
+ throws HibernateException {
+ return null;
+ }
+
+ public boolean[] getPropertyVersionability() {
+ return MUTABILITY;
+ }
+
+ public CacheEntryStructure getCacheEntryStructure() {
+ return new UnstructuredCacheEntry();
+ }
+
+ public boolean hasSubselectLoadableCollections() {
+ return false;
+ }
+
+ public int[] getNaturalIdentifierProperties() {
+ return null;
+ }
+
+ public Type[] getNaturalIdentifierTypes() {
+ return null;
+ }
+
+ public boolean hasNaturalIdentifier() {
+ return false;
+ }
+
+ public boolean hasMutableProperties() {
+ return false;
+ }
+
+ public boolean isInstrumented(EntityMode entityMode) {
+ return false;
+ }
+
+ public boolean hasInsertGeneratedProperties() {
+ return false;
+ }
+
+ public boolean hasUpdateGeneratedProperties() {
+ return false;
+ }
+
+ public boolean[] getPropertyLaziness() {
+ return null;
+ }
+
+ public boolean isLazyPropertiesCacheable() {
+ return true;
+ }
+
+ public boolean hasGeneratedProperties() {
+ return false;
+ }
+
+ public boolean isVersionPropertyGenerated() {
+ return false;
+ }
+
+ public Object[] getNaturalIdentifierSnapshot(Serializable id, SessionImplementor session) throws HibernateException {
+ return null;
+ }
+
+ public Comparator getVersionComparator() {
+ return null;
+ }
+
+ public EntityMetamodel getEntityMetamodel() {
+ return null;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/CustomSQL.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/CustomSQL.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/CustomSQL.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,104 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping default-lazy="false" package="org.hibernate.test.legacy">
+ <class name="Role">
+
+ <id name="id" type="long">
+ <generator class="native"/>
+ </id>
+
+ <property name="name" type="string"/>
+
+ <set name="interventions" lazy="true" cascade="all">
+ <key column="role_id"/>
+ <one-to-many class="Intervention"/>
+ <sql-insert callable="false">/* max comment */
+ update Intervention set role_id=? where id=?</sql-insert>
+ <sql-delete callable="false">update Intervention set role_id=null where role_id=? and id=?</sql-delete>
+ <sql-delete-all callable="false">update Intervention set role_id=null where role_id=?</sql-delete-all>
+ </set>
+
+ <list name="bunchOfStrings">
+ <key column="GROUPID"/>
+ <index column="posn"/>
+ <element column="NAME" type="string"/>
+ <sql-insert callable="true">{ ? = call createRoleBunchOfStrings(?, ?, ?)}</sql-insert>
+ <sql-update callable="true">{ ? = call updateRoleBunchOfStrings(?, ?, ?)}</sql-update>
+ <sql-delete callable="true">{ ? = call deleteRoleBunchOfString(?, ?)}</sql-delete>
+ <sql-delete-all callable="true">{ ? = call deleteAllRoleBunchOfString(?)}</sql-delete-all>
+ </list>
+
+<!-- <sql-load callable="true">{ call loadPatient (?)}</sql-load>
+ <sql-insert callable="true">{call createPatient (?, ?, ?, ?)}</sql-insert>
+ <sql-delete callable="true">{? = call deletePatient (?)}</sql-delete>
+ <sql-update callable="true">{? = call updatePatient (?, ?, ?, ?)}</sql-update> -->
+<!-- <sql-insert callable="true">insert </sql-insert> -->
+<!-- <sql-delete>delete from Role where values (?, upper(?)) /** i did this */</sql-insert> -->
+ <sql-insert>insert into Role (name, id) values (?, upper(?)) /** i did this */</sql-insert>
+<!-- <sql-update>update</sql-update>-->
+ <sql-delete>delete from Role where id=?</sql-delete>
+
+ </class>
+
+ <class name="Resource" table="ecruoser">
+ <id name="id" type="string">
+ <generator class="uuid.hex"/>
+ </id>
+
+ <discriminator column="discriminator" type="string"/>
+
+ <property name="name" type="string"/>
+ <property name="userCode" type="string"/>
+
+ <subclass name="Drug">
+
+ </subclass>
+ </class>
+
+ <class name="Party">
+ <id name="id" type="string">
+ <generator class="uuid.hex"/>
+ </id>
+ <discriminator column="discriminator" type="string"/>
+ <join table="partyjointable">
+ <key column="partyid"/>
+ <property name="name" column="xname" type="string"/>
+ <property name="address" type="string"/>
+ <sql-insert callable="true">{ call createJoinTable(?, ?, ?) }</sql-insert>
+ <sql-update callable="true">{ ? = call updateJoinTable(?, ?, ?) }</sql-update> <!-- xname, address, partyid -->
+ <sql-delete callable="true">{ ? = call deleteJoinTable(?) }</sql-delete> <!-- partyid -->
+ </join>
+
+ <subclass name="Person">
+ <property name="givenName" type="string"/>
+ <property name="lastName" type="string"/>
+ <property name="nationalID" unique="true" type="string"/>
+ </subclass>
+
+ <subclass name="Company">
+ <property name="president" type="string"/>
+ </subclass>
+ </class>
+
+ <class name="Intervention">
+ <id name="id" type="string">
+ <generator class="uuid.hex"/>
+ </id>
+
+ <version name="version" type="long"/>
+
+ <property name="description" type="string"/>
+
+ <joined-subclass name="Medication">
+ <key column="interventionid"/>
+ <many-to-one name="prescribedDrug" class="org.hibernate.test.legacy.Drug"/>
+ <sql-insert>insert into /** put weird comments here */ Medication (prescribedDrug, interventionid) values (?, ?)</sql-insert>
+ </joined-subclass>
+
+ </class>
+
+
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/CustomSQLTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/CustomSQLTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/CustomSQLTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,212 @@
+//$Id: CustomSQLTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.legacy;
+
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.hibernate.HibernateException;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.classic.Session;
+import org.hibernate.dialect.HSQLDialect;
+import org.hibernate.dialect.MySQLDialect;
+
+/**
+ * @author MAX
+ *
+ */
+public class CustomSQLTest extends LegacyTestCase {
+
+ public CustomSQLTest(String name) {
+ super(name);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "legacy/CustomSQL.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( CustomSQLTest.class );
+ }
+
+ public void testInsert() throws HibernateException, SQLException {
+
+ if ( getDialect() instanceof HSQLDialect ) return;
+ if ( getDialect() instanceof MySQLDialect ) return;
+
+ Role p = new Role();
+
+ p.setName("Patient");
+
+ Session s = openSession();
+
+ s.save(p);
+ s.flush();
+
+ s.connection().commit();
+ s.close();
+
+ getSessions().evict(Role.class);
+ s = openSession();
+
+ Role p2 = (Role) s.get(Role.class, new Long(p.getId()));
+ assertNotSame(p, p2);
+ assertEquals(p2.getId(),p.getId());
+ assertTrue(p2.getName().equalsIgnoreCase(p.getName()));
+ s.delete(p2);
+ s.flush();
+
+
+ s.connection().commit();
+ s.close();
+
+
+ }
+
+ public void testJoinedSubclass() throws HibernateException, SQLException {
+ Medication m = new Medication();
+
+ m.setPrescribedDrug(new Drug());
+
+ m.getPrescribedDrug().setName("Morphine");
+
+
+ Session s = openSession();
+
+ s.save(m.getPrescribedDrug());
+ s.save(m);
+
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+
+ Medication m2 = (Medication) s.get(Medication.class, m.getId());
+ assertNotSame(m, m2);
+
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ }
+
+ public void testCollectionCUD() throws HibernateException, SQLException {
+
+ if ( getDialect() instanceof HSQLDialect ) return;
+ if ( getDialect() instanceof MySQLDialect ) return;
+
+ Role role = new Role();
+
+ role.setName("Jim Flanders");
+
+ Intervention iv = new Medication();
+ iv.setDescription("JF medical intervention");
+
+ role.getInterventions().add(iv);
+
+ List sx = new ArrayList();
+ sx.add("somewhere");
+ sx.add("somehow");
+ sx.add("whatever");
+ role.setBunchOfStrings(sx);
+
+ Session s = openSession();
+
+ s.save(role);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+
+ Role r = (Role) s.get(Role.class,new Long(role.getId()));
+ assertNotSame(role,r);
+
+ assertEquals(1,r.getInterventions().size());
+
+ assertEquals(3, r.getBunchOfStrings().size());
+
+ r.getBunchOfStrings().set(1, "replacement");
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+
+ r = (Role) s.get(Role.class,new Long(role.getId()));
+ assertNotSame(role,r);
+
+ assertEquals(r.getBunchOfStrings().get(1),"replacement");
+ assertEquals(3, r.getBunchOfStrings().size());
+
+ r.getBunchOfStrings().set(1, "replacement");
+
+ r.getBunchOfStrings().remove(1);
+ s.flush();
+
+ r.getBunchOfStrings().clear();
+ s.flush();
+
+ s.connection().commit();
+ s.close();
+
+ }
+
+ public void testCRUD() throws HibernateException, SQLException {
+
+ if ( getDialect() instanceof HSQLDialect ) return;
+ if ( getDialect() instanceof MySQLDialect ) return;
+
+ Person p = new Person();
+
+ p.setName("Max");
+ p.setLastName("Andersen");
+ p.setNationalID("110974XYZÅ");
+ p.setAddress("P. P. Street 8");
+
+ Session s = openSession();
+
+ s.save(p);
+ s.flush();
+
+ s.connection().commit();
+ s.close();
+
+ getSessions().evict(Person.class);
+ s = openSession();
+
+ Person p2 = (Person) s.get(Person.class, p.getId());
+ assertNotSame(p, p2);
+ assertEquals(p2.getId(),p.getId());
+ assertEquals(p2.getLastName(),p.getLastName());
+ s.flush();
+
+ List list = s.find("select p from Party as p");
+ assertTrue(list.size() == 1);
+
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+
+ list = s.find("select p from Person as p where p.address = 'Lærkevænget 1'");
+ assertTrue(list.size() == 0);
+ p.setAddress("Lærkevænget 1");
+ s.update(p);
+ list = s.find("select p from Person as p where p.address = 'Lærkevænget 1'");
+ assertTrue(list.size() == 1);
+ list = s.find("select p from Party as p where p.address = 'P. P. Street 8'");
+ assertTrue(list.size() == 0);
+
+ s.delete(p);
+ list = s.find("select p from Person as p");
+ assertTrue(list.size() == 0);
+
+ s.connection().commit();
+ s.close();
+
+
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/D.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/D.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/D.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,61 @@
+//$Id: D.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+public class D {
+ private Long id;
+ private float amount;
+ private A reverse;
+ public A inverse;
+
+ public D() {
+ // try to induce an infinite loop in the lazy-loading machinery
+ setAmount(100.0f);
+ getAmount();
+ }
+ /**
+ * Returns the amount.
+ * @return float
+ */
+ public float getAmount() {
+ return amount;
+ }
+
+ /**
+ * Returns the id.
+ * @return long
+ */
+ public Long getId() {
+ return id;
+ }
+
+ /**
+ * Sets the amount.
+ * @param amount The amount to set
+ */
+ public void setAmount(float amount) {
+ this.amount = amount;
+ }
+
+ /**
+ * Sets the id.
+ * @param id The id to set
+ */
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public A getReverse() {
+ return reverse;
+ }
+
+ public void setReverse(A a) {
+ reverse = a;
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Detail.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Detail.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Detail.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,60 @@
+//$Id: Detail.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Set;
+
+public class Detail implements Serializable {
+
+ private Master master;
+ private int i;
+ private Set details = new HashSet();
+ private int x;
+
+ public int getX() {
+ return x;
+ }
+ public void setX(int x) {
+ this.x = x;
+ }
+
+ public Master getMaster() {
+ return master;
+ }
+
+ public void setMaster(Master master) {
+ this.master = master;
+ }
+
+ public int getI() {
+ return i;
+ }
+
+ public void setI(int i) {
+ this.i = i;
+ }
+
+ /**
+ * Returns the details.
+ * @return Set
+ */
+ public Set getSubDetails() {
+ return details;
+ }
+
+ /**
+ * Sets the details.
+ * @param details The details to set
+ */
+ public void setSubDetails(Set details) {
+ this.details = details;
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/DoubleStringType.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/DoubleStringType.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/DoubleStringType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,112 @@
+//$Id: DoubleStringType.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.type.Type;
+import org.hibernate.usertype.CompositeUserType;
+
+public class DoubleStringType implements CompositeUserType {
+
+ private static final int[] TYPES = { Types.VARCHAR, Types.VARCHAR };
+
+ public int[] sqlTypes() {
+ return TYPES;
+ }
+
+ public Class returnedClass() {
+ return String[].class;
+ }
+
+ public boolean equals(Object x, Object y) {
+ if (x==y) return true;
+ if (x==null || y==null) return false;
+ return ( (String[]) x )[0].equals( ( (String[]) y )[0] ) && ( (String[]) x )[1].equals( ( (String[]) y )[1] );
+ }
+
+ public int hashCode(Object x) throws HibernateException {
+ String[] a = (String[]) x;
+ return a[0].hashCode() + 31 * a[1].hashCode();
+ }
+
+ public Object deepCopy(Object x) {
+ if (x==null) return null;
+ String[] result = new String[2];
+ String[] input = (String[]) x;
+ result[0] = input[0];
+ result[1] = input[1];
+ return result;
+ }
+
+ public boolean isMutable() { return true; }
+
+ public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner)
+ throws HibernateException, SQLException {
+
+ String first = (String) Hibernate.STRING.nullSafeGet(rs, names[0]);
+ String second = (String) Hibernate.STRING.nullSafeGet(rs, names[1]);
+
+ return ( first==null && second==null ) ? null : new String[] { first, second };
+ }
+
+ public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session)
+ throws HibernateException, SQLException {
+
+ String[] strings = (value==null) ? new String[2] : (String[]) value;
+
+ Hibernate.STRING.nullSafeSet(st, strings[0], index);
+ Hibernate.STRING.nullSafeSet(st, strings[1], index+1);
+ }
+
+ public String[] getPropertyNames() {
+ return new String[] { "s1", "s2" };
+ }
+
+ public Type[] getPropertyTypes() {
+ return new Type[] { Hibernate.STRING, Hibernate.STRING };
+ }
+
+ public Object getPropertyValue(Object component, int property) {
+ return ( (String[]) component )[property];
+ }
+
+ public void setPropertyValue(
+ Object component,
+ int property,
+ Object value) {
+
+ ( (String[]) component )[property] = (String) value;
+ }
+
+ public Object assemble(
+ Serializable cached,
+ SessionImplementor session,
+ Object owner) {
+
+ return deepCopy(cached);
+ }
+
+ public Serializable disassemble(Object value, SessionImplementor session) {
+ return (Serializable) deepCopy(value);
+ }
+
+ public Object replace(Object original, Object target, SessionImplementor session, Object owner)
+ throws HibernateException {
+ return original;
+ }
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Down.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Down.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Down.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,20 @@
+//$Id: Down.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+
+/**
+ * @author Gavin King
+ */
+public class Down extends Up {
+
+ private long value;
+
+ public long getValue() {
+ return value;
+ }
+
+ public void setValue(long l) {
+ value = l;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Drug.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Drug.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Drug.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,20 @@
+package org.hibernate.test.legacy;
+
+/**
+ * @author hbm2java
+ */
+public class Drug extends org.hibernate.test.legacy.Resource {
+
+ java.lang.String id;
+
+
+ java.lang.String getId() {
+ return id;
+ }
+
+ void setId(java.lang.String newValue) {
+ id = newValue;
+ }
+
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/E.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/E.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/E.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,71 @@
+//$Id: E.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.util.Set;
+
+public class E {
+ private Long id;
+ private float amount;
+ private A reverse;
+ private Set as;
+ /**
+ * Returns the amount.
+ * @return float
+ */
+ public float getAmount() {
+ return amount;
+ }
+
+ /**
+ * Returns the id.
+ * @return long
+ */
+ public Long getId() {
+ return id;
+ }
+
+ /**
+ * Sets the amount.
+ * @param amount The amount to set
+ */
+ public void setAmount(float amount) {
+ this.amount = amount;
+ }
+
+ /**
+ * Sets the id.
+ * @param id The id to set
+ */
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public A getReverse() {
+ return reverse;
+ }
+
+ public void setReverse(A a) {
+ reverse = a;
+ }
+
+ /**
+ * @return Returns the as.
+ */
+ public Set getAs() {
+ return as;
+ }
+
+ /**
+ * @param as The as to set.
+ */
+ public void setAs(Set as) {
+ this.as = as;
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Eye.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Eye.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Eye.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -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">
+<hibernate-mapping default-lazy="false"
+ default-cascade="save-update"
+ package="org.hibernate.test.legacy">
+
+ <class name="Eye" lazy="true">
+ <id name="id" unsaved-value="0">
+ <generator class="hilo"/>
+ </id>
+ <property name="name"/>
+ <set name="jays" inverse="true" outer-join="true" cascade="all">
+ <key column="eye"/>
+ <one-to-many class="Jay"/>
+ </set>
+ <many-to-one name="jay" outer-join="true"/>
+ </class>
+
+ <class name="Jay" lazy="true">
+ <id name="id" unsaved-value="0">
+ <generator class="hilo"/>
+ </id>
+ <many-to-one name="eye" outer-join="true"/>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Eye.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Eye.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Eye.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,72 @@
+//$Id: Eye.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author Gavin King
+ */
+public class Eye {
+ private long id;
+ private String name;
+ private Jay jay;
+ private Set jays = new HashSet();
+
+ /**
+ * @return Returns the id.
+ */
+ public long getId() {
+ return id;
+ }
+
+ /**
+ * @param id The id to set.
+ */
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ /**
+ * @return Returns the jay.
+ */
+ public Jay getJay() {
+ return jay;
+ }
+
+ /**
+ * @param jay The jay to set.
+ */
+ public void setJay(Jay jay) {
+ this.jay = jay;
+ }
+
+ /**
+ * @return Returns the jays.
+ */
+ public Set getJays() {
+ return jays;
+ }
+
+ /**
+ * @param jays The jays to set.
+ */
+ public void setJays(Set jays) {
+ this.jays = jays;
+ }
+
+ /**
+ * @return Returns the name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @param name The name to set.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Fee.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Fee.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Fee.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -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">
+<hibernate-mapping default-lazy="false">
+
+ <class name="org.hibernate.test.legacy.Fee" table="`the fees`">
+ <id type="string" name="key" column="id_" length="64" unsaved-value="null">
+ <generator class="uuid"/>
+ </id>
+ <property name="fi"/>
+ <many-to-one name="fee"/>
+ <many-to-one name="anotherFee"/>
+ <many-to-one name="qux" cascade="all"/>
+ <property name="count" column="count_"/>
+ <set name="fees">
+ <key column="fee_id"/>
+ <element column="str_" type="string" not-null="true"/>
+ </set>
+ <component name="compon" update="false">
+ <property name="name"/>
+ <property name="null" column="null_prop"/>
+ </component>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Fee.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Fee.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Fee.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,98 @@
+//$Id: Fee.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+import java.util.Set;
+
+public class Fee implements Serializable {
+ public Fee fee;
+ public Fee anotherFee;
+ public String fi;
+ public String key;
+ public Set fees;
+ private Qux qux;
+ private FooComponent compon;
+ private int count;
+
+ public Fee() {
+ }
+
+ public Fee getFee() {
+ return fee;
+ }
+
+ public void setFee(Fee fee) {
+ this.fee = fee;
+ }
+
+ public String getFi() {
+ return fi;
+ }
+
+ public void setFi(String fi) {
+ this.fi = fi;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ public Set getFees() {
+ return fees;
+ }
+
+ public void setFees(Set fees) {
+ this.fees = fees;
+ }
+
+ public Fee getAnotherFee() {
+ return anotherFee;
+ }
+
+ public void setAnotherFee(Fee anotherFee) {
+ this.anotherFee = anotherFee;
+ }
+
+ public Qux getQux() {
+ return qux;
+ }
+
+ public void setQux(Qux qux) {
+ this.qux = qux;
+ }
+
+ public FooComponent getCompon() {
+ return compon;
+ }
+
+ public void setCompon(FooComponent compon) {
+ this.compon = compon;
+ }
+
+ /**
+ * Returns the count.
+ * @return int
+ */
+ public int getCount() {
+ return count;
+ }
+
+ /**
+ * Sets the count.
+ * @param count The count to set
+ */
+ public void setCount(int count) {
+ this.count = count;
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Fixed.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Fixed.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Fixed.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,31 @@
+//$Id: Fixed.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author Gavin King
+ */
+public class Fixed extends Broken {
+ private Set set;
+ private List list = new ArrayList();
+
+ public Set getSet() {
+ return set;
+ }
+
+ public void setSet(Set set) {
+ this.set = set;
+ }
+
+ public List getList() {
+ return list;
+ }
+
+ public void setList(List list) {
+ this.list = list;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Fo.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Fo.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Fo.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping default-lazy="false">
+
+ <class name="org.hibernate.test.legacy.Fo" table="foes">
+ <composite-id class="org.hibernate.test.legacy.FumCompositeID">
+ <key-property name="string">
+ <column name="string_" length="20"/>
+ </key-property>
+ <key-property name="short" column="short_"/>
+ <key-property name="date" column="date_" type="date"/>
+ </composite-id>
+ <version name="version" type="long"/>
+ <property name="serial" column="serial_"/>
+ <property name="buf"/>
+ <property name="x"/>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Fo.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Fo.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Fo.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,60 @@
+//$Id: Fo.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+
+public final class Fo {
+
+ public static Fo newFo() {
+ return new Fo();
+ }
+
+ private Fo() {}
+
+ private byte[] buf;
+ private Serializable serial;
+ private long version;
+ private int x;
+
+ public int getX() {
+ return x;
+ }
+ public void setX(int x) {
+ this.x = x;
+ }
+
+ public byte[] getBuf() {
+ return buf;
+ }
+
+
+ public Serializable getSerial() {
+ return serial;
+ }
+
+
+ public void setBuf(byte[] buf) {
+ this.buf = buf;
+ }
+
+
+ public void setSerial(Serializable serial) {
+ this.serial = serial;
+ }
+
+ public long getVersion() {
+ return version;
+ }
+
+ public void setVersion(long version) {
+ this.version = version;
+ }
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Foo.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Foo.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Foo.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,437 @@
+//$Id: Foo.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+import java.sql.Timestamp;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Locale;
+
+import org.hibernate.CallbackException;
+import org.hibernate.Session;
+import org.hibernate.classic.Lifecycle;
+
+public class Foo implements Lifecycle, FooProxy, Serializable {
+
+ private static int count=0;
+
+ public static class Struct implements java.io.Serializable {
+ String name;
+ int count;
+ public boolean equals(Object other) {
+ Struct s = (Struct) other;
+ return ( s.name==name || s.name.equals(name) ) && s.count==count;
+ }
+ public int hashCode() {
+ return count;
+ }
+ }
+
+ /*public boolean equals(Object other) {
+ FooProxy otherFoo = (FooProxy) other;
+ return this.key.equals( otherFoo.getKey() ) && this._string.equals( otherFoo.getString() );
+ }
+
+ public int hashCode() {
+ return key.hashCode() - _string.hashCode();
+ }*/
+
+ String key;
+ FooProxy _foo;
+ String _string;
+ Date _date;
+ Date _timestamp;
+ Integer _integer;
+ Long _long;
+ Short _short;
+ Float _float;
+ Double _double;
+ Boolean _boolean;
+ Byte _byte;
+ Integer _null;
+ int _int;
+ boolean _bool;
+ float _zero;
+ byte[] _bytes;
+ boolean yesno;
+ java.io.Serializable blob;
+ java.io.Serializable nullBlob;
+ byte[] binary;
+ String[] custom;
+ FooComponent component;
+ char _char;
+ Fee dependent;
+ Locale theLocale;
+ private int version;
+ private Timestamp versionTimestamp;
+ private Calendar versionCalendar;
+ private float formula;
+ private String joinedProp;
+
+ private int x;
+
+ public int getX() {
+ return x;
+ }
+ public void setX(int x) {
+ this.x = x;
+ }
+
+ public Foo() {
+ }
+
+ public Foo(int x) {
+ this.x=x;
+ }
+
+ public boolean onSave(Session db) throws CallbackException {
+ _string = "a string";
+ _date = new Date(123);
+ _timestamp = new Date( System.currentTimeMillis() );
+ _integer = new Integer( -666 );
+ _long = new Long( 696969696969696969l - count++ );
+ _short = new Short("42");
+ _float = new Float( 6666.66f );
+ //_double = new Double( 1.33e-69 ); // this double is too big for the sap db jdbc driver
+ _double = new Double( 1.12e-36 );
+ _boolean = new Boolean(true);
+ _byte = new Byte( (byte) 127 );
+ _int = 2;
+ _char = '@';
+ _bytes = _string.getBytes();
+ Struct s = new Struct();
+ s.name="name";
+ s.count = 69;
+ blob = s;
+ binary = ( _string + "yada yada yada" ).getBytes();
+ custom = new String[] { "foo", "bar" };
+ component = new FooComponent("foo", 12, new Date[] { _date, _timestamp, null, new Date() }, new FooComponent("bar", 666, new Date[] { new Date(123456l), null }, null ) );
+ component.setGlarch( new Glarch() );
+ dependent = new Fee();
+ dependent.setFi( "belongs to foo # " + getKey() );
+ theLocale = Locale.getDefault();
+ return NO_VETO;
+ }
+
+ public boolean onDelete(Session db) throws CallbackException {
+ return NO_VETO;
+ }
+ public boolean onUpdate(Session db) throws CallbackException {
+ return NO_VETO;
+ }
+
+ public void onLoad(Session db, Serializable id) {
+ }
+
+ public String getKey() {
+ return key;
+ }
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ public FooProxy getFoo() {
+ return _foo;
+ }
+ public void setFoo(FooProxy foo) {
+ _foo = foo;
+ }
+
+ public String getString() {
+ return _string;
+ }
+ public void setString(String string) {
+ _string = string;
+ //if (_foo!=null) _foo.setString(string);
+ }
+
+ public java.util.Date getDate() {
+ return _date;
+ }
+ public void setDate(java.util.Date date) {
+ _date = date;
+ }
+
+ public java.util.Date getTimestamp() {
+ return _timestamp;
+ }
+ public void setTimestamp(java.util.Date timestamp) {
+ _timestamp = timestamp;
+ }
+
+ public Integer getInteger() {
+ return _integer;
+ }
+ public void setInteger(Integer iinteger) {
+ _integer = iinteger;
+ }
+ public Long getLong() {
+ return _long;
+ }
+ public void setLong(Long llong) {
+ _long = llong;
+ }
+
+
+ public Short getShort() {
+ return _short;
+ }
+ public void setShort(Short sshort) {
+ _short = sshort;
+ }
+ public Float getFloat() {
+ return _float;
+ }
+ public void setFloat(Float ffloat) {
+ _float = ffloat;
+ }
+ public Double getDouble() {
+ return _double;
+ }
+ public void setDouble(Double ddouble) {
+ _double = ddouble;
+ }
+ public Boolean getBoolean() {
+ return _boolean;
+ }
+ public void setBoolean(Boolean bboolean) {
+ _boolean = bboolean;
+ }
+ public byte[] getBytes() {
+ return _bytes;
+ }
+ public void setBytes(byte[] bytes) {
+ _bytes = bytes;
+ }
+ public float getZero() {
+ return _zero;
+ }
+ public void setZero(float zero) {
+ _zero = zero;
+ }
+ public boolean getBool() {
+ return _bool;
+ }
+ public void setBool(boolean bool) {
+ _bool = bool;
+ }
+
+ public int getInt() {
+ return _int;
+ }
+ public void setInt(int iint) {
+ _int = iint;
+ }
+
+ public Integer getNull() {
+ return _null;
+ }
+ public void setNull(Integer nnull) {
+ _null = nnull;
+ }
+
+ public Byte getByte() {
+ return _byte;
+ }
+
+ public void setByte(Byte bbyte) {
+ _byte = bbyte;
+ }
+
+ public String toString() {
+ return this.getClass().getName() + ": " + key;
+ }
+
+ public void disconnect() {
+ if ( _foo!=null) _foo.disconnect();
+ _foo=null;
+ }
+
+ public boolean equalsFoo(Foo other) {
+ if ( _bytes!=other._bytes ) {
+ if ( _bytes==null || other._bytes==null ) return false;
+ if ( _bytes.length!=other._bytes.length ) return false;
+ for ( int i=0; i< _bytes.length; i++) {
+ if ( _bytes[i] != other._bytes[i] ) return false;
+ }
+ }
+
+ return ( this._bool == other._bool )
+ && ( ( this._boolean == other._boolean ) || ( this._boolean.equals(other._boolean) ) )
+ && ( ( this._byte == other._byte ) || ( this._byte.equals(other._byte) ) )
+ //&& ( ( this._date == other._date ) || ( this._date.getDate() == other._date.getDate() && this._date.getMonth() == other._date.getMonth() && this._date.getYear() == other._date.getYear() ) )
+ && ( ( this._double == other._double ) || ( this._double.equals(other._double) ) )
+ && ( ( this._float == other._float ) || ( this._float.equals(other._float) ) )
+ && ( this._int == other._int )
+ && ( ( this._integer == other._integer ) || ( this._integer.equals(other._integer) ) )
+ && ( ( this._long == other._long ) || ( this._long.equals(other._long) ) )
+ && ( this._null == other._null )
+ && ( ( this._short == other._short ) || ( this._short.equals(other._short) ) )
+ && ( ( this._string == other._string) || ( this._string.equals(other._string) ) )
+ //&& ( ( this._timestamp==other._timestamp) || ( this._timestamp.getDate() == other._timestamp.getDate() && this._timestamp.getYear() == other._timestamp.getYear() && this._timestamp.getMonth() == other._timestamp.getMonth() ) )
+ && ( this._zero == other._zero )
+ && ( ( this._foo == other._foo ) || ( this._foo.getKey().equals( other._foo.getKey() ) ) )
+ && ( ( this.blob == other.blob ) || ( this.blob.equals(other.blob) ) )
+ && ( this.yesno == other.yesno )
+ && ( ( this.binary == other.binary ) || java.util.Arrays.equals(this.binary, other.binary) )
+ && ( this.key.equals(other.key) )
+ && ( this.theLocale.equals(other.theLocale) )
+ && ( ( this.custom == other.custom ) || ( this.custom[0].equals(other.custom[0]) && this.custom[1].equals(other.custom[1]) ) );
+
+ }
+
+ public boolean getYesno() {
+ return yesno;
+ }
+
+ public void setYesno(boolean yesno) {
+ this.yesno = yesno;
+ }
+
+ public java.io.Serializable getBlob() {
+ return blob;
+ }
+
+ public void setBlob(java.io.Serializable blob) {
+ this.blob = blob;
+ }
+
+ public java.io.Serializable getNullBlob() {
+ return nullBlob;
+ }
+
+ public void setNullBlob(java.io.Serializable nullBlob) {
+ this.nullBlob = nullBlob;
+ }
+
+ public byte[] getBinary() {
+ return binary;
+ }
+ public void setBinary(byte[] binary) {
+ this.binary = binary;
+ }
+
+ public String[] getCustom() {
+ return custom;
+ }
+
+ public void setCustom(String[] custom) {
+ this.custom = custom;
+ }
+
+ public FooComponent getComponent() {
+ return component;
+ }
+ public void setComponent(FooComponent component) {
+ this.component = component;
+ }
+
+ public FooComponent getNullComponent() {
+ return null;
+ }
+ public void setNullComponent(FooComponent fc) throws Exception {
+ if (fc!=null) throw new Exception("Null component");
+ }
+
+ public Character getChar() {
+ return new Character(_char);
+ }
+
+ public void setChar(Character _char) {
+ this._char = _char.charValue();
+ }
+
+ public Fee getDependent() {
+ return dependent;
+ }
+
+ public void setDependent(Fee dependent) {
+ this.dependent = dependent;
+ }
+
+ /**
+ * Returns the locale.
+ * @return Locale
+ */
+ public Locale getLocale() {
+ return theLocale;
+ }
+
+ /**
+ * Sets the locale.
+ * @param locale The locale to set
+ */
+ public void setLocale(Locale locale) {
+ this.theLocale = locale;
+ }
+
+ /**
+ * Returns the version.
+ * @return int
+ */
+ public int getVersion() {
+ return version;
+ }
+
+ /**
+ * Sets the version.
+ * @param version The version to set
+ */
+ public void setVersion(int version) {
+ this.version = version;
+ }
+
+ /**
+ * Returns the versionTimestamp.
+ * @return Timestamp
+ */
+ public Timestamp getVersionTimestamp() {
+ return versionTimestamp;
+ }
+
+ /**
+ * Sets the versionTimestamp.
+ * @param versionTimestamp The versionTimestamp to set
+ */
+ public void setVersionTimestamp(Timestamp versionTimestamp) {
+ this.versionTimestamp = versionTimestamp;
+ }
+
+ public void finalize() { }
+
+ public Calendar getVersionCalendar() {
+ return versionCalendar;
+ }
+
+ public void setVersionCalendar(Calendar calendar) {
+ versionCalendar = calendar;
+ }
+
+ public float getFormula() {
+ return formula;
+ }
+
+ public void setFormula(float f) {
+ formula = f;
+ }
+
+ /**
+ * @return Returns the joinedProp.
+ */
+ public String getJoinedProp() {
+ return joinedProp;
+ }
+
+ /**
+ * @param joinedProp The joinedProp to set.
+ */
+ public void setJoinedProp(String joinedProp) {
+ this.joinedProp = joinedProp;
+ }
+
+}
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/FooBar.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/FooBar.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/FooBar.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,178 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping default-lazy="false" package="org.hibernate.test.legacy">
+
+ <import class="Result"/>
+ <import class="Named"/>
+
+ <class
+ name="Foo"
+ table="`foos`"
+ proxy="FooProxy"
+ discriminator-value="F"
+ batch-size="4"
+ dynamic-insert="true"
+ dynamic-update="true"
+ select-before-update="true">
+
+ <!--cache-->
+
+ <id name="key" type="string">
+ <column name="`foo_idcolumnname123`" length="36"/>
+ <generator class="uuid.hex">
+ <param name="seperator">:</param>
+ </generator>
+ </id>
+ <discriminator column="`foo_subclass_1234`" type="character" force="true"/>
+ <version name="version"/>
+ <!--<version name="versionCalendar" type="calendar"/>-->
+ <!--<timestamp name="versionTimestamp"/>-->
+ <many-to-one name="foo" class="Foo">
+ <column name="foo" length="36" index="fbmtoidx"/>
+ </many-to-one>
+ <property name="long">
+ <column name="long_" index="fbmtoidx" unique-key="abc" not-null="true"/>
+ </property>
+ <property name="integer">
+ <column name="`integer__`" unique-key="abc" not-null="true"/>
+ </property>
+ <property name="float">
+ <column name="float_" unique-key="abc" not-null="true" check="float_ > 0.0"/>
+ </property>
+ <property name="x"/>
+ <property name="double" column="double_"/>
+
+ <primitive-array name="bytes" table="foobytes">
+ <key column="id"/>
+ <index column="i"/>
+ <element column="byte_" type="byte"/>
+ </primitive-array>
+
+ <property name="date" type="date" column="date_"/>
+ <property name="timestamp" type="timestamp" column="timestamp_"/>
+ <property name="boolean" column="boolean_"/>
+ <property name="bool" column="bool_"/>
+ <property name="null" column="null_"/>
+ <property name="short" column="short_"/>
+ <property name="char" column="char_"/>
+ <property name="zero" column="zero_"/>
+ <property name="int" column="int_"/>
+ <property name="string">
+ <column name="string_" length="48" index="fbstridx"/>
+ </property>
+ <property name="byte" column="byte_"/>
+ <property name="yesno" type="yes_no"/>
+ <property name="blob" type="org.hibernate.test.legacy.Foo$Struct" column="blobb_"/>
+ <property name="nullBlob" type="serializable"/>
+ <property name="binary" column="bin_"/>
+ <property name="theLocale" access="field" column="`localeayzabc123`"/>
+
+ <property name="formula" formula="int_/2"/>
+
+ <property name="custom" type="org.hibernate.test.legacy.DoubleStringType" access="field">
+ <column name="first_name" length="66"/>
+ <column name="surname" length="66"/>
+ </property>
+
+ <component name="component">
+ <property name="count" column="count_" type="int" not-null="true"/>
+ <property name="name">
+ <column name="name_" length="32" not-null="true"/>
+ </property>
+ <many-to-one name="glarch"
+ column="g__"
+ cascade="all"
+ class="org.hibernate.test.legacy.Glarch"
+ lazy="proxy"
+ outer-join="true"/>
+ <property name="null" column="cmpnt_null_"/>
+ <component name="subcomponent">
+ <!--property name="count" column="subcount"/-->
+ <property name="name" column="subname"/>
+ <array name="importantDates" table="foo_times">
+ <key column="foo_id"/>
+ <index column="i"/>
+ <element column="date_" type="time"/>
+ </array>
+ <many-to-one name="fee"
+ column="fee_sub"
+ cascade="all"
+ class="Fee"
+ outer-join="false"
+ access="field"/>
+ </component>
+ <array name="importantDates" table="foo_dates">
+ <key column="foo_id"/>
+ <index column="i"/>
+ <element column="date_" type="date"/>
+ </array>
+ </component>
+ <component name="nullComponent">
+ <property name="name" column="null_cmpnt_"/>
+ </component>
+
+ <join table="jointable">
+ <key column="fooid" on-delete="cascade"/>
+ <property name="joinedProp"/>
+ </join>
+
+ <join table="foo_dep_table">
+ <key column="fooid"/>
+ <many-to-one name="dependent"
+ class="org.hibernate.test.legacy.Fee"
+ cascade="all"
+ not-null="true"/>
+ </join>
+
+ <subclass
+ name="Trivial"
+ proxy="FooProxy"
+ discriminator-value="T"/>
+
+ <subclass
+ name="Abstract"
+ proxy="AbstractProxy"
+ discriminator-value="null">
+ <set name="abstracts" batch-size="2">
+ <key column="abstract_id"/>
+ <one-to-many class="Abstract"/>
+ </set>
+ <property name="time" column="the_time"/>
+
+ <subclass
+ name="Bar"
+ proxy="BarProxy"
+ discriminator-value="B">
+ <many-to-one name="baz"/>
+ <property name="barString">
+ <column name="bar_string" length="24"/>
+ </property>
+ <component name="barComponent" class="FooComponent">
+ <parent name="parent"/>
+ <property name="count" column="bar_count"/>
+ <property name="name" length="64"/>
+ <array name ="importantDates">
+ <key column="id" />
+ <index column="i"/>
+ <element column="date_" type="date"/>
+ </array>
+ </component>
+ <any name="object" meta-type="character" id-type="long" cascade="all">
+ <meta-value value="O" class="One"/>
+ <meta-value value="M" class="Many"/>
+ <column name="clazz" length="100"/>
+ <column name="gen_id"/>
+ </any>
+ <join table="bar_join_table">
+ <key column="bar_id"/>
+ <property name="name" column="name_name"/>
+ </join>
+ </subclass>
+ </subclass>
+ </class>
+
+
+</hibernate-mapping>
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/FooBarTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/FooBarTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/FooBarTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,4781 @@
+//$Id: FooBarTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+import java.sql.Connection;
+import java.sql.Time;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TimeZone;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import junit.framework.Test;
+import junit.textui.TestRunner;
+
+import org.hibernate.Criteria;
+import org.hibernate.FetchMode;
+import org.hibernate.FlushMode;
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.LazyInitializationException;
+import org.hibernate.LockMode;
+import org.hibernate.ObjectNotFoundException;
+import org.hibernate.Query;
+import org.hibernate.QueryException;
+import org.hibernate.ScrollableResults;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.classic.Session;
+import org.hibernate.connection.ConnectionProvider;
+import org.hibernate.connection.DriverManagerConnectionProvider;
+import org.hibernate.criterion.Example;
+import org.hibernate.criterion.Expression;
+import org.hibernate.criterion.MatchMode;
+import org.hibernate.criterion.Order;
+import org.hibernate.dialect.DB2Dialect;
+import org.hibernate.dialect.DerbyDialect;
+import org.hibernate.dialect.HSQLDialect;
+import org.hibernate.dialect.InterbaseDialect;
+import org.hibernate.dialect.MckoiDialect;
+import org.hibernate.dialect.MySQLDialect;
+import org.hibernate.dialect.Oracle9Dialect;
+import org.hibernate.dialect.OracleDialect;
+import org.hibernate.dialect.PointbaseDialect;
+import org.hibernate.dialect.PostgreSQLDialect;
+import org.hibernate.dialect.SAPDBDialect;
+import org.hibernate.dialect.SybaseDialect;
+import org.hibernate.dialect.TimesTenDialect;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.jmx.HibernateService;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.mapping.RootClass;
+import org.hibernate.proxy.HibernateProxy;
+import org.hibernate.type.Type;
+import org.hibernate.util.JoinedIterator;
+import org.hibernate.util.SerializationHelper;
+
+
+public class FooBarTest extends LegacyTestCase {
+
+ public FooBarTest(String arg) {
+ super(arg);
+ }
+
+ public String[] getMappings() {
+ return new String[] {
+ "legacy/FooBar.hbm.xml",
+ "legacy/Baz.hbm.xml",
+ "legacy/Qux.hbm.xml",
+ "legacy/Glarch.hbm.xml",
+ "legacy/Fum.hbm.xml",
+ "legacy/Fumm.hbm.xml",
+ "legacy/Fo.hbm.xml",
+ "legacy/One.hbm.xml",
+ "legacy/Many.hbm.xml",
+ "legacy/Immutable.hbm.xml",
+ "legacy/Fee.hbm.xml",
+ "legacy/Vetoer.hbm.xml",
+ "legacy/Holder.hbm.xml",
+ "legacy/Location.hbm.xml",
+ "legacy/Stuff.hbm.xml",
+ "legacy/Container.hbm.xml",
+ "legacy/Simple.hbm.xml",
+ "legacy/XY.hbm.xml"
+ };
+ }
+
+ public void configure(Configuration cfg) {
+ super.configure( cfg );
+ if ( Dialect.getDialect() instanceof OracleDialect ) {
+ ( (RootClass) cfg.getClassMapping("org.hibernate.test.legacy.Foo") ).setForceDiscriminator(false);
+ }
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( FooBarTest.class );
+ }
+
+ public static void main(String[] args) throws Exception {
+ TestRunner.run( suite() );
+ }
+
+ public void testSaveOrUpdateCopyAny() throws Exception {
+ Session s = openSession();
+ Bar bar = new Bar();
+ One one = new One();
+ bar.setObject(one);
+ s.save(bar);
+ GlarchProxy g = bar.getComponent().getGlarch();
+ bar.getComponent().setGlarch(null);
+ s.delete(g);
+ s.flush();
+ assertTrue( s.contains(one) );
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ Bar bar2 = (Bar) s.saveOrUpdateCopy(bar);
+ s.flush();
+ s.delete(bar2);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testRefreshProxy() throws Exception {
+ Session s = openSession();
+ Glarch g = new Glarch();
+ Serializable gid = s.save(g);
+ s.flush();
+ s.clear();
+ GlarchProxy gp = (GlarchProxy) s.load(Glarch.class, gid);
+ gp.getName(); //force init
+ s.refresh(gp);
+ s.delete(gp);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testOnCascadeDelete() throws Exception {
+
+ if ( ! supportsCircularCascadeDelete() ) {
+ return;
+ }
+
+ Session s = openSession();
+ Baz baz = new Baz();
+ baz.subs = new ArrayList();
+ Baz sub = new Baz();
+ sub.superBaz = baz;
+ baz.subs.add(sub);
+ s.save(baz);
+ s.flush();
+ assertTrue( s.createQuery("from Baz").list().size()==2 );
+ s.connection().commit();
+ s.delete(baz);
+ s.flush();
+ s.connection().commit();
+ assertTrue( s.createQuery("from Baz").list().size()==0 );
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testRemoveFromIdbag() throws Exception {
+ Session s = openSession();
+ Baz baz = new Baz();
+ baz.setByteBag( new ArrayList() );
+ byte[] bytes = { 12, 13 };
+ baz.getByteBag().add( new byte[] { 10, 45 } );
+ baz.getByteBag().add(bytes);
+ baz.getByteBag().add( new byte[] { 1, 11 } );
+ baz.getByteBag().add( new byte[] { 12 } );
+ s.save(baz);
+ s.flush();
+ baz.getByteBag().remove(bytes);
+ s.flush();
+ baz.getByteBag().add(bytes);
+ s.flush();
+ s.delete(baz);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testLoad() throws Exception {
+ Session s = openSession();
+ Qux q = new Qux();
+ s.save(q);
+ BarProxy b = new Bar();
+ s.save(b);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ q = (Qux) s.load(Qux.class, q.getKey() );
+ b = (BarProxy) s.load( Foo.class, b.getKey() );
+ b.getKey();
+ assertFalse( Hibernate.isInitialized(b) );
+ b.getBarString();
+ assertTrue( Hibernate.isInitialized(b) );
+ BarProxy b2 = (BarProxy) s.load( Bar.class, new String( b.getKey() ) );
+ Qux q2 = (Qux) s.load( Qux.class, q.getKey() );
+ assertTrue( "loaded same object", q==q2 );
+ assertTrue( "loaded same object", b==b2 );
+ assertTrue( Math.round( b.getFormula() ) == b.getInt()/2 );
+ s.delete(q2);
+ s.delete(b2);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testJoin() throws Exception {
+ Session s = openSession();
+ Foo foo = new Foo();
+ foo.setJoinedProp("foo");
+ s.save(foo);
+ s.flush();
+ foo.setJoinedProp("bar");
+ s.flush();
+ String fid = foo.getKey();
+ s.delete(foo);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ Foo foo2 = new Foo();
+ foo2.setJoinedProp("foo");
+ s.save(foo2);
+ s.find("select foo.id from Foo foo where foo.joinedProp = 'foo'");
+ assertNull( s.get(Foo.class, fid) );
+ s.delete(foo2);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ }
+
+ public void testDereferenceLazyCollection() throws Exception {
+ Session s = openSession();
+ Baz baz = new Baz();
+ baz.setFooSet( new HashSet() );
+ Foo foo = new Foo();
+ baz.getFooSet().add(foo);
+ s.save(foo);
+ s.save(baz);
+ foo.setBytes( "foobar".getBytes() );
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ foo = (Foo) s.get( Foo.class, foo.getKey() );
+ assertTrue( Hibernate.isInitialized( foo.getBytes() ) );
+ assertTrue( foo.getBytes().length==6 );
+ baz = (Baz) s.get( Baz.class, baz.getCode() );
+ assertTrue( baz.getFooSet().size()==1 );
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ getSessions().evictCollection("org.hibernate.test.legacy.Baz.fooSet");
+
+ s = openSession();
+ baz = (Baz) s.get( Baz.class, baz.getCode() );
+ assertFalse( Hibernate.isInitialized( baz.getFooSet() ) );
+ baz.setFooSet(null);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ foo = (Foo) s.get( Foo.class, foo.getKey() );
+ assertTrue( foo.getBytes().length==6 );
+ baz = (Baz) s.get( Baz.class, baz.getCode() );
+ assertFalse( Hibernate.isInitialized( baz.getFooSet() ) );
+ assertTrue( baz.getFooSet().size()==0 );
+ s.delete(baz);
+ s.delete(foo);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testMoveLazyCollection() throws Exception {
+ Session s = openSession();
+ Baz baz = new Baz();
+ Baz baz2 = new Baz();
+ baz.setFooSet( new HashSet() );
+ Foo foo = new Foo();
+ baz.getFooSet().add(foo);
+ s.save(foo);
+ s.save(baz);
+ s.save(baz2);
+ foo.setBytes( "foobar".getBytes() );
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ foo = (Foo) s.get( Foo.class, foo.getKey() );
+ assertTrue( Hibernate.isInitialized( foo.getBytes() ) );
+ assertTrue( foo.getBytes().length==6 );
+ baz = (Baz) s.get( Baz.class, baz.getCode() );
+ assertTrue( baz.getFooSet().size()==1 );
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ getSessions().evictCollection("org.hibernate.test.legacy.Baz.fooSet");
+
+ s = openSession();
+ baz = (Baz) s.get( Baz.class, baz.getCode() );
+ assertFalse( Hibernate.isInitialized( baz.getFooSet() ) );
+ baz2 = (Baz) s.get( Baz.class, baz2.getCode() );
+ baz2.setFooSet( baz.getFooSet() );
+ baz.setFooSet(null);
+ assertFalse( Hibernate.isInitialized( baz2.getFooSet() ) );
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ foo = (Foo) s.get( Foo.class, foo.getKey() );
+ assertTrue( foo.getBytes().length==6 );
+ baz = (Baz) s.get( Baz.class, baz.getCode() );
+ baz2 = (Baz) s.get( Baz.class, baz2.getCode() );
+ assertFalse( Hibernate.isInitialized( baz.getFooSet() ) );
+ assertTrue( baz.getFooSet().size()==0 );
+ assertTrue( Hibernate.isInitialized( baz2.getFooSet() ) ); //fooSet has batching enabled
+ assertTrue( baz2.getFooSet().size()==1 );
+ s.delete(baz);
+ s.delete(baz2);
+ s.delete(foo);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testCriteriaCollection() throws Exception {
+ Session s = openSession();
+ Baz bb = (Baz) s.createCriteria(Baz.class).uniqueResult();
+ assertTrue(bb==null);
+ Baz baz = new Baz();
+ s.save(baz);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ Baz b = (Baz) s.createCriteria(Baz.class).uniqueResult();
+ assertTrue( Hibernate.isInitialized( b.getTopGlarchez() ) );
+ assertTrue( b.getTopGlarchez().size()==0 );
+ s.delete(b);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testQuery() throws Exception {
+ Session s = openSession();
+ Transaction txn = s.beginTransaction();
+ Foo foo = new Foo();
+ s.save(foo);
+ Foo foo2 = new Foo();
+ s.save(foo2);
+ foo.setFoo(foo2);
+
+ List list = s.find("from Foo foo inner join fetch foo.foo");
+ Foo foof = (Foo) list.get(0);
+ assertTrue( Hibernate.isInitialized( foof.getFoo() ) );
+
+ list = s.find("from Baz baz left outer join fetch baz.fooToGlarch");
+
+ list = s.find(
+ "select foo, bar from Foo foo left outer join foo.foo bar where foo = ?",
+ foo,
+ Hibernate.entity(Foo.class)
+ );
+ Object[] row1 = (Object[]) list.get(0);
+ assertTrue( row1[0]==foo && row1[1]==foo2 );
+
+ s.find("select foo.foo.foo.string from Foo foo where foo.foo = 'bar'");
+ s.find("select foo.foo.foo.foo.string from Foo foo where foo.foo = 'bar'");
+ s.find("select foo from Foo foo where foo.foo.foo = 'bar'");
+ s.find("select foo.foo.foo.foo.string from Foo foo where foo.foo.foo = 'bar'");
+ s.find("select foo.foo.foo.string from Foo foo where foo.foo.foo.foo.string = 'bar'");
+ if ( ! (getDialect() instanceof HSQLDialect) ) s.find("select foo.string from Foo foo where foo.foo.foo.foo = foo.foo.foo");
+ s.find("select foo.string from Foo foo where foo.foo.foo = 'bar' and foo.foo.foo.foo = 'baz'");
+ s.find("select foo.string from Foo foo where foo.foo.foo.foo.string = 'a' and foo.foo.string = 'b'");
+
+ s.find("from Bar bar, foo in elements(bar.baz.fooArray)");
+
+ //s.find("from Baz as baz where baz.topComponents[baz].name = 'bazzz'");
+
+ if ( (getDialect() instanceof DB2Dialect) && !(getDialect() instanceof DerbyDialect) ) {
+ s.find("from Foo foo where lower( foo.foo.string ) = 'foo'");
+ s.find("from Foo foo where lower( (foo.foo.string || 'foo') || 'bar' ) = 'foo'");
+ s.find("from Foo foo where repeat( (foo.foo.string || 'foo') || 'bar', 2 ) = 'foo'");
+ s.find("from Bar foo where foo.foo.integer is not null and repeat( (foo.foo.string || 'foo') || 'bar', (5+5)/2 ) = 'foo'");
+ s.find("from Bar foo where foo.foo.integer is not null or repeat( (foo.foo.string || 'foo') || 'bar', (5+5)/2 ) = 'foo'");
+ }
+ if (getDialect() instanceof SybaseDialect) {
+ s.iterate("select baz from Baz as baz join baz.fooArray foo group by baz order by sum(foo.float)");
+ }
+
+ s.find("from Foo as foo where foo.component.glarch.name is not null");
+ s.find("from Foo as foo left outer join foo.component.glarch as glarch where glarch.name = 'foo'");
+
+ list = s.find("from Foo");
+ assertTrue( list.size()==2 && list.get(0) instanceof FooProxy );
+ list = s.find("from Foo foo left outer join foo.foo");
+ assertTrue( list.size()==2 && ( (Object[]) list.get(0) )[0] instanceof FooProxy );
+
+ s.createQuery("from Bar, Bar").list();
+ s.createQuery("from Foo, Bar").list();
+ s.find("from Baz baz left join baz.fooToGlarch, Bar bar join bar.foo");
+ s.find("from Baz baz left join baz.fooToGlarch join baz.fooSet");
+ s.find("from Baz baz left join baz.fooToGlarch join fetch baz.fooSet foo left join fetch foo.foo");
+
+ list = s.find("from Foo foo where foo.string='osama bin laden' and foo.boolean = true order by foo.string asc, foo.component.count desc");
+ assertTrue( "empty query", list.size()==0 );
+ Iterator iter = s.iterate("from Foo foo where foo.string='osama bin laden' order by foo.string asc, foo.component.count desc");
+ assertTrue( "empty iterator", !iter.hasNext() );
+
+ list = s.find("select foo.foo from Foo foo");
+ assertTrue( "query", list.size()==1 );
+ assertTrue( "returned object", list.get(0)==foo.getFoo() );
+ foo.getFoo().setFoo(foo);
+ foo.setString("fizard");
+ //The following test is disabled for databases with no subselects...also for Interbase (not sure why).
+ if (
+ !(getDialect() instanceof MySQLDialect) &&
+ !(getDialect() instanceof HSQLDialect) &&
+ !(getDialect() instanceof MckoiDialect) &&
+ !(getDialect() instanceof SAPDBDialect) &&
+ !(getDialect() instanceof PointbaseDialect) &&
+ !(getDialect() instanceof DerbyDialect)
+ ) {
+ // && !db.equals("weblogic") {
+ if ( !( getDialect() instanceof InterbaseDialect ) ) {
+ list = s.find("from Foo foo where ? = some elements(foo.component.importantDates)", new Date(), Hibernate.DATE);
+ assertTrue( "component query", list.size()==2 );
+ }
+ if( !( getDialect() instanceof TimesTenDialect)) {
+ list = s.find("from Foo foo where size(foo.component.importantDates) = 3"); //WAS: 4
+ assertTrue( "component query", list.size()==2 );
+ list = s.find("from Foo foo where 0 = size(foo.component.importantDates)");
+ assertTrue( "component query", list.size()==0 );
+ }
+ list = s.find("from Foo foo where exists elements(foo.component.importantDates)");
+ assertTrue( "component query", list.size()==2 );
+ s.find("from Foo foo where not exists (from Bar bar where bar.id = foo.id)");
+
+ s.find("select foo.foo from Foo foo where foo = some(select x from Foo x where x.long > foo.foo.long)");
+ s.find("select foo.foo from Foo foo where foo = some(from Foo x where (x.long > foo.foo.long))");
+ if ( !( getDialect() instanceof TimesTenDialect)) {
+ s.find("select foo.foo from Foo foo where foo.long = some( select max(x.long) from Foo x where (x.long > foo.foo.long) group by x.foo )");
+ }
+ s.find("from Foo foo where foo = some(select x from Foo x where x.long > foo.foo.long) and foo.foo.string='baz'");
+ s.find("from Foo foo where foo.foo.string='baz' and foo = some(select x from Foo x where x.long > foo.foo.long)");
+ s.find("from Foo foo where foo = some(select x from Foo x where x.long > foo.foo.long)");
+
+ s.iterate("select foo.string, foo.date, foo.foo.string, foo.id from Foo foo, Baz baz where foo in elements(baz.fooArray) and foo.string like 'foo'");
+ }
+ list = s.find("from Foo foo where foo.component.count is null order by foo.component.count");
+ assertTrue( "component query", list.size()==0 );
+ list = s.find("from Foo foo where foo.component.name='foo'");
+ assertTrue( "component query", list.size()==2 );
+ list = s.find("select distinct foo.component.name, foo.component.name from Foo foo where foo.component.name='foo'");
+ assertTrue( "component query", list.size()==1 );
+ list = s.find("select distinct foo.component.name, foo.id from Foo foo where foo.component.name='foo'");
+ assertTrue( "component query", list.size()==2 );
+ list = s.find("select foo.foo from Foo foo");
+ assertTrue( "query", list.size()==2 );
+ list = s.find("from Foo foo where foo.id=?", foo.getKey(), Hibernate.STRING);
+ assertTrue( "id query", list.size()==1 );
+ list = s.find("from Foo foo where foo.key=?", foo.getKey(), Hibernate.STRING);
+ assertTrue( "named id query", list.size()==1 );
+ assertTrue( "id query", list.get(0)==foo );
+ list = s.find("select foo.foo from Foo foo where foo.string='fizard'");
+ assertTrue( "query", list.size()==1 );
+ assertTrue( "returned object", list.get(0)==foo.getFoo() );
+ list = s.find("from Foo foo where foo.component.subcomponent.name='bar'");
+ assertTrue( "components of components", list.size()==2 );
+ list = s.find("select foo.foo from Foo foo where foo.foo.id=?", foo.getFoo().getKey(), Hibernate.STRING);
+ assertTrue( "by id query", list.size()==1 );
+ assertTrue( "by id returned object", list.get(0)==foo.getFoo() );
+
+ s.find( "from Foo foo where foo.foo = ?", foo.getFoo(), Hibernate.entity(Foo.class) );
+
+ assertTrue( !s.iterate("from Bar bar where bar.string='a string' or bar.string='a string'").hasNext() );
+
+ iter = s.iterate(
+ "select foo.component.name, elements(foo.component.importantDates) from Foo foo where foo.foo.id=?",
+ foo.getFoo().getKey(),
+ Hibernate.STRING
+ );
+ int i=0;
+ while ( iter.hasNext() ) {
+ i++;
+ Object[] row = (Object[]) iter.next();
+ assertTrue( row[0] instanceof String && ( row[1]==null || row[1] instanceof Date ) );
+ }
+ assertTrue(i==3); //WAS: 4
+ iter = s.iterate(
+ "select max( elements(foo.component.importantDates) ) from Foo foo group by foo.id"
+ );
+ assertTrue( iter.next() instanceof Date );
+
+ list = s.find(
+ "select foo.foo.foo.foo from Foo foo, Foo foo2 where"
+ + " foo = foo2.foo and not not ( not foo.string='fizard' )"
+ + " and foo2.string between 'a' and (foo.foo.string)"
+ + ( ( getDialect() instanceof HSQLDialect || getDialect() instanceof InterbaseDialect || getDialect() instanceof TimesTenDialect)?
+ " and ( foo2.string in ( 'fiz', 'blah') or 1=1 )"
+ :
+ " and ( foo2.string in ( 'fiz', 'blah', foo.foo.string, foo.string, foo2.string ) )"
+ )
+ );
+ assertTrue( "complex query", list.size()==1 );
+ assertTrue( "returned object", list.get(0)==foo );
+ foo.setString("from BoogieDown -tinsel town =!@#$^&*())");
+ list = s.find("from Foo foo where foo.string='from BoogieDown -tinsel town =!@#$^&*())'");
+ assertTrue( "single quotes", list.size()==1 );
+ list = s.find("from Foo foo where not foo.string='foo''bar'");
+ assertTrue( "single quotes", list.size()==2 );
+ list = s.find("from Foo foo where foo.component.glarch.next is null");
+ assertTrue( "query association in component", list.size()==2 );
+ Bar bar = new Bar();
+ Baz baz = new Baz();
+ baz.setDefaults();
+ bar.setBaz(baz);
+ baz.setManyToAny( new ArrayList() );
+ baz.getManyToAny().add(bar);
+ baz.getManyToAny().add(foo);
+ s.save(bar);
+ s.save(baz);
+ list = s.find(" from Bar bar where bar.baz.count=667 and bar.baz.count!=123 and not bar.baz.name='1-E-1'");
+ assertTrue( "query many-to-one", list.size()==1 );
+ list = s.find(" from Bar i where i.baz.name='Bazza'");
+ assertTrue( "query many-to-one", list.size()==1 );
+
+ Iterator rs = s.iterate("select count(distinct foo.foo) from Foo foo");
+ assertTrue( "count", ( (Long) rs.next() ).longValue()==2 );
+ assertTrue( !rs.hasNext() );
+ rs = s.iterate("select count(foo.foo.boolean) from Foo foo");
+ assertTrue( "count", ( (Long) rs.next() ).longValue()==2 );
+ assertTrue( !rs.hasNext() );
+ rs = s.iterate("select count(*), foo.int from Foo foo group by foo.int");
+ assertTrue( "count(*) group by", ( (Object[]) rs.next() )[0].equals( new Long(3) ) );
+ assertTrue( !rs.hasNext() );
+ rs = s.iterate("select sum(foo.foo.int) from Foo foo");
+ assertTrue( "sum", ( (Long) rs.next() ).longValue()==4 );
+ assertTrue( !rs.hasNext() );
+ rs = s.iterate("select count(foo) from Foo foo where foo.id=?", foo.getKey(), Hibernate.STRING);
+ assertTrue( "id query count", ( (Long) rs.next() ).longValue()==1 );
+ assertTrue( !rs.hasNext() );
+
+ list = s.find( "from Foo foo where foo.boolean = ?", new Boolean(true), Hibernate.BOOLEAN );
+
+ list = s.find("select new Foo(fo.x) from Fo fo");
+ list = s.find("select new Foo(fo.integer) from Foo fo");
+
+ list = s.createQuery("select new Foo(fo.x) from Foo fo")
+ //.setComment("projection test")
+ .setCacheable(true)
+ .list();
+ assertTrue(list.size()==3);
+ list = s.createQuery("select new Foo(fo.x) from Foo fo")
+ //.setComment("projection test 2")
+ .setCacheable(true)
+ .list();
+ assertTrue(list.size()==3);
+
+ rs = s.iterate("select new Foo(fo.x) from Foo fo");
+ assertTrue( "projection iterate (results)", rs.hasNext() );
+ assertTrue( "projection iterate (return check)", Foo.class.isAssignableFrom( rs.next().getClass() ) );
+
+ ScrollableResults sr = s.createQuery("select new Foo(fo.x) from Foo fo").scroll();
+ assertTrue( "projection scroll (results)", sr.next() );
+ assertTrue( "projection scroll (return check)", Foo.class.isAssignableFrom( sr.get(0).getClass() ) );
+
+ list = s.find("select foo.long, foo.component.name, foo, foo.foo from Foo foo");
+ rs = list.iterator();
+ int count=0;
+ while ( rs.hasNext() ) {
+ count++;
+ Object[] row = (Object[]) rs.next();
+ assertTrue( row[0] instanceof Long );
+ assertTrue( row[1] instanceof String );
+ assertTrue( row[2] instanceof Foo );
+ assertTrue( row[3] instanceof Foo );
+ }
+ assertTrue(count!=0);
+ list = s.find("select avg(foo.float), max(foo.component.name), count(distinct foo.id) from Foo foo");
+ rs = list.iterator();
+ count=0;
+ while ( rs.hasNext() ) {
+ count++;
+ Object[] row = (Object[]) rs.next();
+ assertTrue( row[0] instanceof Double );
+ assertTrue( row[1] instanceof String );
+ assertTrue( row[2] instanceof Long );
+ }
+ assertTrue(count!=0);
+ list = s.find("select foo.long, foo.component, foo, foo.foo from Foo foo");
+ rs = list.iterator();
+ count=0;
+ while ( rs.hasNext() ) {
+ count++;
+ Object[] row = (Object[]) rs.next();
+ assertTrue( row[0] instanceof Long );
+ assertTrue( row[1] instanceof FooComponent );
+ assertTrue( row[2] instanceof Foo );
+ assertTrue( row[3] instanceof Foo );
+ }
+ assertTrue(count!=0);
+
+ s.save( new Holder("ice T") );
+ s.save( new Holder("ice cube") );
+
+ assertTrue( s.find("from java.lang.Object as o").size()==15 );
+ assertTrue( s.find("from Named").size()==7 );
+ assertTrue( s.find("from Named n where n.name is not null").size()==4 );
+ iter = s.iterate("from Named n");
+ while ( iter.hasNext() ) {
+ assertTrue( iter.next() instanceof Named );
+ }
+
+ s.save( new Holder("bar") );
+ iter = s.iterate("from Named n0, Named n1 where n0.name = n1.name");
+ int cnt = 0;
+ while ( iter.hasNext() ) {
+ Object[] row = (Object[]) iter.next();
+ if ( row[0]!=row[1] ) cnt++;
+ }
+ if ( !(getDialect() instanceof HSQLDialect) ) {
+ assertTrue(cnt==2);
+ assertTrue( s.find("from Named n0, Named n1 where n0.name = n1.name").size()==7 );
+ }
+
+ Query qu = s.createQuery("from Named n where n.name = :name");
+ qu.getReturnTypes();
+ qu.getNamedParameters();
+
+ iter = s.iterate("from java.lang.Object");
+ int c = 0;
+ while ( iter.hasNext() ) {
+ iter.next();
+ c++;
+ }
+ assertTrue(c==16);
+
+ s.iterate("select baz.code, min(baz.count) from Baz baz group by baz.code");
+
+ iter = s.iterate("selecT baz from Baz baz where baz.stringDateMap['foo'] is not null or baz.stringDateMap['bar'] = ?", new Date(), Hibernate.DATE);
+ assertFalse( iter.hasNext() );
+ list = s.find("select baz from Baz baz where baz.stringDateMap['now'] is not null");
+ assertTrue( list.size()==1 );
+ list = s.find("select baz from Baz baz where baz.stringDateMap['now'] is not null and baz.stringDateMap['big bang'] < baz.stringDateMap['now']");
+ assertTrue( list.size()==1 );
+ list = s.find("select index(date) from Baz baz join baz.stringDateMap date");
+ System.out.println(list);
+ assertTrue( list.size()==2 );
+
+ s.find("from Foo foo where foo.integer not between 1 and 5 and foo.string not in ('cde', 'abc') and foo.string is not null and foo.integer<=3");
+
+ s.find("from Baz baz inner join baz.collectionComponent.nested.foos foo where foo.string is null");
+ if ( !(getDialect() instanceof MySQLDialect) && !(getDialect() instanceof MckoiDialect) && !(getDialect() instanceof SAPDBDialect) && !(getDialect() instanceof PointbaseDialect) /*&& !(dialect instanceof Oracle9Dialect)*/ ) {
+ s.find("from Baz baz inner join baz.fooSet where '1' in (from baz.fooSet foo where foo.string is not null)");
+ s.find("from Baz baz where 'a' in elements(baz.collectionComponent.nested.foos) and 1.0 in elements(baz.collectionComponent.nested.floats)");
+ s.find("from Baz baz where 'b' in elements(baz.collectionComponent.nested.foos) and 1.0 in elements(baz.collectionComponent.nested.floats)");
+ }
+
+ s.find("from Foo foo join foo.foo where foo.foo in ('1','2','3')");
+ if ( !(getDialect() instanceof HSQLDialect) ) s.find("from Foo foo left join foo.foo where foo.foo in ('1','2','3')");
+ s.find("select foo.foo from Foo foo where foo.foo in ('1','2','3')");
+ s.find("select foo.foo.string from Foo foo where foo.foo in ('1','2','3')");
+ s.find("select foo.foo.string from Foo foo where foo.foo.string in ('1','2','3')");
+ s.find("select foo.foo.long from Foo foo where foo.foo.string in ('1','2','3')");
+ s.find("select count(*) from Foo foo where foo.foo.string in ('1','2','3') or foo.foo.long in (1,2,3)");
+ s.find("select count(*) from Foo foo where foo.foo.string in ('1','2','3') group by foo.foo.long");
+
+ s.find("from Foo foo1 left join foo1.foo foo2 left join foo2.foo where foo1.string is not null");
+ s.find("from Foo foo1 left join foo1.foo.foo where foo1.string is not null");
+ s.find("from Foo foo1 left join foo1.foo foo2 left join foo1.foo.foo foo3 where foo1.string is not null");
+
+ s.find("select foo.formula from Foo foo where foo.formula > 0");
+
+ int len = s.find("from Foo as foo join foo.foo as foo2 where foo2.id >'a' or foo2.id <'a'").size();
+ assertTrue(len==2);
+
+ s.delete("from Holder");
+ txn.commit();
+ s.close();
+
+ s = openSession();
+ txn = s.beginTransaction();
+ baz = (Baz) s.createQuery("from Baz baz left outer join fetch baz.manyToAny").uniqueResult();
+ assertTrue( Hibernate.isInitialized( baz.getManyToAny() ) );
+ assertTrue( baz.getManyToAny().size()==2 );
+ BarProxy barp = (BarProxy) baz.getManyToAny().get(0);
+ s.find("from Baz baz join baz.manyToAny");
+ assertTrue( s.find("select baz from Baz baz join baz.manyToAny a where index(a) = 0").size()==1 );
+
+ FooProxy foop = (FooProxy) s.get( Foo.class, foo.getKey() );
+ assertTrue( foop == baz.getManyToAny().get(1) );
+
+ barp.setBaz(baz);
+ assertTrue( s.find("select bar from Bar bar where bar.baz.stringDateMap['now'] is not null").size()==1 );
+ assertTrue( s.find("select bar from Bar bar join bar.baz b where b.stringDateMap['big bang'] < b.stringDateMap['now'] and b.stringDateMap['now'] is not null").size()==1 );
+ assertTrue( s.find("select bar from Bar bar where bar.baz.stringDateMap['big bang'] < bar.baz.stringDateMap['now'] and bar.baz.stringDateMap['now'] is not null").size()==1 );
+
+ list = s.find("select foo.string, foo.component, foo.id from Bar foo");
+ assertTrue ( ( (FooComponent) ( (Object[]) list.get(0) )[1] ).getName().equals("foo") );
+ list = s.find("select elements(baz.components) from Baz baz");
+ assertTrue( list.size()==2 );
+ list = s.find("select bc.name from Baz baz join baz.components bc");
+ assertTrue( list.size()==2 );
+ //list = s.find("select bc from Baz baz join baz.components bc");
+
+ s.createQuery("from Foo foo where foo.integer < 10 order by foo.string").setMaxResults(12).list();
+
+ s.delete(barp);
+ s.delete(baz);
+ s.delete( foop.getFoo() );
+ s.delete(foop);
+ txn.commit();
+ s.close();
+ }
+
+ public void testCascadeDeleteDetached() throws Exception {
+ Session s = openSession();
+ Baz baz = new Baz();
+ List list = new ArrayList();
+ list.add( new Fee() );
+ baz.setFees(list);
+ s.save(baz);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ baz = (Baz) s.get( Baz.class, baz.getCode() );
+ s.connection().commit();
+ s.close();
+
+ assertFalse( Hibernate.isInitialized( baz.getFees() ) );
+
+ s = openSession();
+ s.delete(baz);
+ s.flush();
+ assertFalse( s.iterate("from Fee").hasNext() );
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ baz = new Baz();
+ list = new ArrayList();
+ list.add( new Fee() );
+ list.add( new Fee() );
+ baz.setFees(list);
+ s.save(baz);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ baz = (Baz) s.get( Baz.class, baz.getCode() );
+ Hibernate.initialize( baz.getFees() );
+ s.connection().commit();
+ s.close();
+
+ assertTrue( baz.getFees().size()==2 );
+
+ s = openSession();
+ s.delete(baz);
+ s.flush();
+ assertFalse( s.iterate("from Fee").hasNext() );
+ s.connection().commit();
+ s.close();
+
+ }
+
+ public void testForeignKeys() throws Exception {
+ Session s = openSession();
+ Baz baz = new Baz();
+ Foo foo = new Foo();
+ List bag = new ArrayList();
+ bag.add(foo);
+ baz.setIdFooBag(bag);
+ baz.setFoo(foo);
+ s.save(baz);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ baz = (Baz) s.load( Baz.class, baz.getCode() );
+ s.delete(baz);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testNonlazyCollection() throws Exception {
+ Session s = openSession();
+ Baz baz = new Baz();
+ s.save(baz);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ baz = (Baz) s.createCriteria(Baz.class)
+ //.setComment("criteria test")
+ .setFetchMode("stringDateMap", FetchMode.EAGER)
+ .uniqueResult();
+ assertTrue( Hibernate.isInitialized( baz.getFooToGlarch() ) );
+ assertTrue( Hibernate.isInitialized( baz.getFooComponentToFoo() ) );
+ assertTrue( !Hibernate.isInitialized( baz.getStringSet() ) );
+ assertTrue( Hibernate.isInitialized( baz.getStringDateMap() ) );
+ s.delete(baz);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ }
+
+ public void testReuseDeletedCollection() throws Exception {
+ Session s = openSession();
+ Baz baz = new Baz();
+ baz.setDefaults();
+ s.save(baz);
+ s.flush();
+ s.delete(baz);
+ Baz baz2 = new Baz();
+ baz2.setStringArray( new String[] {"x-y-z"} );
+ s.save(baz2);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ baz2.setStringSet( baz.getStringSet() );
+ baz2.setStringArray( baz.getStringArray() );
+ baz2.setFooArray( baz.getFooArray() );
+
+ s = openSession();
+ s.update(baz2);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ baz2 = (Baz) s.load( Baz.class, baz2.getCode() );
+ assertTrue( baz2.getStringArray().length==3 );
+ assertTrue( baz2.getStringSet().size()==3 );
+ s.delete(baz2);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+
+ }
+
+ public void testPropertyRef() throws Exception {
+ Session s = openSession();
+ Holder h = new Holder();
+ h.setName("foo");
+ Holder h2 = new Holder();
+ h2.setName("bar");
+ h.setOtherHolder(h2);
+ Serializable hid = s.save(h);
+ Qux q = new Qux();
+ q.setHolder(h2);
+ Serializable qid = s.save(q);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ h = (Holder) s.load(Holder.class, hid);
+ assertEquals( h.getName(), "foo");
+ assertEquals( h.getOtherHolder().getName(), "bar");
+ Object[] res = (Object[]) s.find("from Holder h join h.otherHolder oh where h.otherHolder.name = 'bar'").get(0);
+ assertTrue( res[0]==h );
+ q = (Qux) s.get(Qux.class, qid);
+ assertTrue( q.getHolder() == h.getOtherHolder() );
+ s.delete(h);
+ s.delete(q);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testQueryCollectionOfValues() throws Exception {
+ Session s = openSession();
+ Baz baz = new Baz();
+ baz.setDefaults();
+ s.save(baz);
+ Glarch g = new Glarch();
+ Serializable gid = s.save(g);
+
+ if ( !(getDialect() instanceof MySQLDialect) && !(getDialect() instanceof HSQLDialect) /*&& !(dialect instanceof MckoiDialect)*/ && !(getDialect() instanceof SAPDBDialect) && !(getDialect() instanceof PointbaseDialect) && !(getDialect() instanceof TimesTenDialect) ) {
+ s.filter( baz.getFooArray(), "where size(this.bytes) > 0");
+ s.filter( baz.getFooArray(), "where 0 in elements(this.bytes)");
+ }
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ //s.find("from Baz baz where baz.fooSet.string = 'foo'");
+ //s.find("from Baz baz where baz.fooArray.string = 'foo'");
+ //s.find("from Baz baz where baz.fooSet.foo.string = 'foo'");
+ //s.find("from Baz baz join baz.fooSet.foo foo where foo.string = 'foo'");
+ s.find("from Baz baz join baz.fooSet foo join foo.foo.foo foo2 where foo2.string = 'foo'");
+ s.find("from Baz baz join baz.fooArray foo join foo.foo.foo foo2 where foo2.string = 'foo'");
+ s.find("from Baz baz join baz.stringDateMap date where index(date) = 'foo'");
+ s.find("from Baz baz join baz.topGlarchez g where index(g) = 'A'");
+ s.find("select index(g) from Baz baz join baz.topGlarchez g");
+
+ assertTrue( s.find("from Baz baz left join baz.stringSet").size()==3 );
+ baz = (Baz) s.find("from Baz baz join baz.stringSet str where str='foo'").get(0);
+ assertTrue( !Hibernate.isInitialized( baz.getStringSet() ) );
+ baz = (Baz) s.find("from Baz baz left join fetch baz.stringSet").get(0);
+ assertTrue( Hibernate.isInitialized( baz.getStringSet() ) );
+ assertTrue( s.find("from Baz baz join baz.stringSet string where string='foo'").size()==1 );
+ assertTrue( s.find("from Baz baz inner join baz.components comp where comp.name='foo'").size()==1 );
+ //List bss = s.find("select baz, ss from Baz baz inner join baz.stringSet ss");
+ s.find("from Glarch g inner join g.fooComponents comp where comp.fee is not null");
+ s.find("from Glarch g inner join g.fooComponents comp join comp.fee fee where fee.count > 0");
+ s.find("from Glarch g inner join g.fooComponents comp where comp.fee.count is not null");
+
+ s.delete(baz);
+ //s.delete("from Glarch g");
+ s.delete( s.get(Glarch.class, gid) );
+ s.flush();
+
+ s.connection().commit();
+ s.close();
+
+ }
+
+ public void testBatchLoad() throws Exception {
+ Session s = openSession();
+ Baz baz = new Baz();
+ SortedSet stringSet = new TreeSet();
+ stringSet.add("foo");
+ stringSet.add("bar");
+ Set fooSet = new HashSet();
+ for (int i=0; i<3; i++) {
+ Foo foo = new Foo();
+ s.save(foo);
+ fooSet.add(foo);
+ }
+ baz.setFooSet(fooSet);
+ baz.setStringSet(stringSet);
+ s.save(baz);
+ Baz baz2 = new Baz();
+ fooSet = new HashSet();
+ for (int i=0; i<2; i++) {
+ Foo foo = new Foo();
+ s.save(foo);
+ fooSet.add(foo);
+ }
+ baz2.setFooSet(fooSet);
+ s.save(baz2);
+ Baz baz3 = new Baz();
+ stringSet = new TreeSet();
+ stringSet.add("foo");
+ stringSet.add("baz");
+ baz3.setStringSet(stringSet);
+ s.save(baz3);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ baz = (Baz) s.load( Baz.class, baz.getCode() );
+ baz2 = (Baz) s.load( Baz.class, baz2.getCode() );
+ baz3 = (Baz) s.load( Baz.class, baz3.getCode() );
+ assertFalse( Hibernate.isInitialized(baz.getFooSet()) || Hibernate.isInitialized(baz2.getFooSet()) || Hibernate.isInitialized(baz3.getFooSet()) );
+ assertFalse( Hibernate.isInitialized(baz.getStringSet()) || Hibernate.isInitialized(baz2.getStringSet()) || Hibernate.isInitialized(baz3.getStringSet()) );
+ assertTrue( baz.getFooSet().size()==3 );
+ assertTrue( Hibernate.isInitialized(baz.getFooSet()) && Hibernate.isInitialized(baz2.getFooSet()) && Hibernate.isInitialized(baz3.getFooSet()));
+ assertTrue( baz2.getFooSet().size()==2 );
+ assertTrue( baz3.getStringSet().contains("baz") );
+ assertTrue( Hibernate.isInitialized(baz.getStringSet()) && Hibernate.isInitialized(baz2.getStringSet()) && Hibernate.isInitialized(baz3.getStringSet()));
+ assertTrue( baz.getStringSet().size()==2 && baz2.getStringSet().size()==0 );
+ s.delete(baz);
+ s.delete(baz2);
+ s.delete(baz3);
+ Iterator iter = new JoinedIterator( new Iterator[] { baz.getFooSet().iterator(), baz2.getFooSet().iterator() } );
+ while ( iter.hasNext() ) s.delete( iter.next() );
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ }
+
+ public void testFetchInitializedCollection() throws Exception {
+ Session s = openSession();
+ Baz baz = new Baz();
+ Collection fooBag = new ArrayList();
+ fooBag.add( new Foo() );
+ fooBag.add( new Foo() );
+ baz.setFooBag(fooBag);
+ s.save(baz);
+ s.flush();
+ fooBag = baz.getFooBag();
+ s.find("from Baz baz left join fetch baz.fooBag");
+ assertTrue( fooBag==baz.getFooBag() );
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ baz = (Baz) s.load( Baz.class, baz.getCode() );
+ Object bag = baz.getFooBag();
+ assertFalse( Hibernate.isInitialized(bag) );
+ s.find("from Baz baz left join fetch baz.fooBag");
+ assertTrue( bag==baz.getFooBag() );
+ assertTrue( baz.getFooBag().size()==2 );
+ s.delete(baz);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testLateCollectionAdd() throws Exception {
+ Session s = openSession();
+ Baz baz = new Baz();
+ List l = new ArrayList();
+ baz.setStringList(l);
+ l.add("foo");
+ Serializable id = s.save(baz);
+ l.add("bar");
+ s.flush();
+ l.add("baz");
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ baz = (Baz) s.load(Baz.class, id);
+ assertTrue( baz.getStringList().size()==3 && baz.getStringList().contains("bar") );
+ s.delete(baz);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ }
+
+ public void testUpdate() throws Exception {
+ Session s = openSession();
+ Foo foo = new Foo();
+ s.save(foo);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ foo = (Foo) SerializationHelper.deserialize( SerializationHelper.serialize(foo) );
+
+ s = openSession();
+ FooProxy foo2 = (FooProxy) s.load( Foo.class, foo.getKey() );
+ foo2.setString("dirty");
+ foo2.setBoolean( new Boolean(false) );
+ foo2.setBytes( new byte[] { 1,2,3} );
+ foo2.setDate(null);
+ foo2.setShort( new Short("69") );
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ foo2.setString("dirty again");
+ s.update(foo2);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ foo2.setString("dirty again 2");
+ s.update(foo2);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ Foo foo3 = new Foo();
+ s.load( foo3, foo.getKey() );
+ // There is an interbase bug that causes null integers to return as 0, also numeric precision is <= 15
+ assertTrue( "update", foo2.equalsFoo(foo3) );
+ s.delete(foo3);
+ s.delete("from Glarch");
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ }
+
+ public void testListRemove() throws Exception {
+ Session s = openSession();
+ Baz b = new Baz();
+ List stringList = new ArrayList();
+ List feeList = new ArrayList();
+ b.setFees(feeList);
+ b.setStringList(stringList);
+ feeList.add( new Fee() );
+ feeList.add( new Fee() );
+ feeList.add( new Fee() );
+ feeList.add( new Fee() );
+ stringList.add("foo");
+ stringList.add("bar");
+ stringList.add("baz");
+ stringList.add("glarch");
+ s.save(b);
+ s.flush();
+ stringList.remove(1);
+ feeList.remove(1);
+ s.flush();
+ s.evict(b);
+ s.refresh(b);
+ assertTrue( b.getFees().size()==3 );
+ stringList = b.getStringList();
+ assertTrue(
+ stringList.size()==3 &&
+ "baz".equals( stringList.get(1) ) &&
+ "foo".equals( stringList.get(0) )
+ );
+ s.delete(b);
+ s.delete("from Fee");
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testFetchInitializedCollectionDupe() throws Exception {
+ Session s = openSession();
+ Baz baz = new Baz();
+ Collection fooBag = new ArrayList();
+ fooBag.add( new Foo() );
+ fooBag.add( new Foo() );
+ baz.setFooBag(fooBag);
+ s.save(baz);
+ s.flush();
+ fooBag = baz.getFooBag();
+ s.find("from Baz baz left join fetch baz.fooBag");
+ assertTrue( Hibernate.isInitialized(fooBag) );
+ assertTrue( fooBag==baz.getFooBag() );
+ assertTrue( baz.getFooBag().size()==2 );
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ baz = (Baz) s.load( Baz.class, baz.getCode() );
+ Object bag = baz.getFooBag();
+ assertFalse( Hibernate.isInitialized(bag) );
+ s.find("from Baz baz left join fetch baz.fooBag");
+ assertTrue( Hibernate.isInitialized(bag) );
+ assertTrue( bag==baz.getFooBag() );
+ assertTrue( baz.getFooBag().size()==2 );
+ s.delete(baz);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testSortables() throws Exception {
+ Session s = openSession();
+ Baz b = new Baz();
+ b.setName("name");
+ SortedSet ss = new TreeSet();
+ ss.add( new Sortable("foo") );
+ ss.add( new Sortable("bar") );
+ ss.add( new Sortable("baz") );
+ b.setSortablez(ss);
+ s.save(b);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ Criteria cr = s.createCriteria(Baz.class);
+ cr.setFetchMode("topGlarchez", FetchMode.LAZY);
+ List result = cr
+ .addOrder( Order.asc("name") )
+ .list();
+ assertTrue( result.size()==1 );
+ b = (Baz) result.get(0);
+ assertTrue( b.getSortablez().size()==3 );
+ assertEquals( ( (Sortable) b.getSortablez().iterator().next() ).getName(), "bar" );
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ result = s.createQuery("from Baz baz left join fetch baz.sortablez order by baz.name asc")
+ .list();
+ b = (Baz) result.get(0);
+ assertTrue( b.getSortablez().size()==3 );
+ assertEquals( ( (Sortable) b.getSortablez().iterator().next() ).getName(), "bar" );
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ result = s.createQuery("from Baz baz order by baz.name asc")
+ .list();
+ b = (Baz) result.get(0);
+ assertTrue( b.getSortablez().size()==3 );
+ assertEquals( ( (Sortable) b.getSortablez().iterator().next() ).getName(), "bar" );
+ s.delete(b);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ }
+
+ public void testFetchList() throws Exception {
+ Session s = openSession();
+ Baz baz = new Baz();
+ s.save(baz);
+ Foo foo = new Foo();
+ s.save(foo);
+ Foo foo2 = new Foo();
+ s.save(foo2);
+ s.flush();
+ List list = new ArrayList();
+ for ( int i=0; i<5; i++ ) {
+ Fee fee = new Fee();
+ list.add(fee);
+ }
+ baz.setFees(list);
+ list = s.find("from Foo foo, Baz baz left join fetch baz.fees");
+ assertTrue( Hibernate.isInitialized( ( (Baz) ( (Object[]) list.get(0) )[1] ).getFees() ) );
+ s.delete(foo);
+ s.delete(foo2);
+ s.delete(baz);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testBagOneToMany() throws Exception {
+ Session s = openSession();
+ Baz baz = new Baz();
+ List list = new ArrayList();
+ baz.setBazez(list);
+ list.add( new Baz() );
+ s.save(baz);
+ s.flush();
+ list.add( new Baz() );
+ s.flush();
+ list.add( 0, new Baz() );
+ s.flush();
+ s.delete( list.remove(1) );
+ s.flush();
+ s.delete(baz);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testQueryLockMode() throws Exception {
+
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ Bar bar = new Bar();
+ s.save(bar);
+ s.flush();
+ bar.setString("changed");
+ Baz baz = new Baz();
+ baz.setFoo(bar);
+ s.save(baz);
+ Query q = s.createQuery("from Foo foo, Bar bar");
+ if ( !(getDialect() instanceof DB2Dialect) ) {
+ q.setLockMode("bar", LockMode.UPGRADE);
+ }
+ Object[] result = (Object[]) q.uniqueResult();
+ Object b = result[0];
+ assertTrue( s.getCurrentLockMode(b)==LockMode.WRITE && s.getCurrentLockMode( result[1] )==LockMode.WRITE );
+ tx.commit();
+ s.disconnect();
+
+ s.reconnect();
+ tx = s.beginTransaction();
+ assertTrue( s.getCurrentLockMode(b)==LockMode.NONE );
+ s.find("from Foo foo");
+ assertTrue( s.getCurrentLockMode(b)==LockMode.NONE );
+ q = s.createQuery("from Foo foo");
+ q.setLockMode("foo", LockMode.READ);
+ q.list();
+ assertTrue( s.getCurrentLockMode(b)==LockMode.READ);
+ s.evict(baz);
+ tx.commit();
+ s.disconnect();
+
+ s.reconnect();
+ tx = s.beginTransaction();
+ assertTrue( s.getCurrentLockMode(b)==LockMode.NONE );
+ s.delete( s.load( Baz.class, baz.getCode() ) );
+ assertTrue( s.getCurrentLockMode(b)==LockMode.NONE );
+ tx.commit();
+ s.close();
+
+ s = openSession();
+ tx = s.beginTransaction();
+ q = s.createQuery("from Foo foo, Bar bar, Bar bar2");
+ if ( !(getDialect() instanceof DB2Dialect) ) {
+ q.setLockMode("bar", LockMode.UPGRADE);
+ }
+ q.setLockMode("bar2", LockMode.READ);
+ result = (Object[]) q.list().get(0);
+ if ( !(getDialect() instanceof DB2Dialect) ) {
+ assertTrue( s.getCurrentLockMode( result[0] )==LockMode.UPGRADE && s.getCurrentLockMode( result[1] )==LockMode.UPGRADE );
+ }
+ s.delete( result[0] );
+ tx.commit();
+ s.close();
+ }
+
+ public void testManyToManyBag() throws Exception {
+
+ Session s = openSession();
+ Baz baz = new Baz();
+ Serializable id = s.save(baz);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ baz = (Baz) s.load(Baz.class, id);
+ baz.getFooBag().add( new Foo() );
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ baz = (Baz) s.load(Baz.class, id);
+ assertTrue( !Hibernate.isInitialized( baz.getFooBag() ) );
+ assertTrue( baz.getFooBag().size()==1 );
+ if ( !(getDialect() instanceof HSQLDialect) ) assertTrue( Hibernate.isInitialized( baz.getFooBag().iterator().next() ) );
+ s.delete(baz);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testIdBag() throws Exception {
+ Session s = openSession();
+ Baz baz = new Baz();
+ s.save(baz);
+ List l = new ArrayList();
+ List l2 = new ArrayList();
+ baz.setIdFooBag(l);
+ baz.setByteBag(l2);
+ l.add( new Foo() );
+ l.add( new Bar() );
+ byte[] bytes = "ffo".getBytes();
+ l2.add(bytes);
+ l2.add( "foo".getBytes() );
+ s.flush();
+ l.add( new Foo() );
+ l.add( new Bar() );
+ l2.add( "bar".getBytes() );
+ s.flush();
+ s.delete( l.remove(3) );
+ bytes[1]='o';
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ baz = (Baz) s.load(Baz.class, baz.getCode());
+ assertTrue( baz.getIdFooBag().size()==3 );
+ assertTrue( baz.getByteBag().size()==3 );
+ bytes = "foobar".getBytes();
+ Iterator iter = baz.getIdFooBag().iterator();
+ while ( iter.hasNext() ) s.delete( iter.next() );
+ baz.setIdFooBag(null);
+ baz.getByteBag().add(bytes);
+ baz.getByteBag().add(bytes);
+ assertTrue( baz.getByteBag().size()==5 );
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ baz = (Baz) s.load(Baz.class, baz.getCode());
+ assertTrue( baz.getIdFooBag().size()==0 );
+ assertTrue( baz.getByteBag().size()==5 );
+ baz.getIdFooBag().add( new Foo() );
+ iter = baz.getByteBag().iterator();
+ iter.next();
+ iter.remove();
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ baz = (Baz) s.load(Baz.class, baz.getCode());
+ assertTrue( baz.getIdFooBag().size()==1 );
+ assertTrue( baz.getByteBag().size()==4 );
+ s.delete(baz);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ private boolean isOuterJoinFetchingDisabled() {
+ return new Integer(0).equals( ( (SessionFactoryImplementor) getSessions() ).getSettings().getMaximumFetchDepth() );
+ }
+
+ public void testForceOuterJoin() throws Exception {
+
+ if ( isOuterJoinFetchingDisabled() ) return;
+
+ Session s = openSession();
+ Glarch g = new Glarch();
+ FooComponent fc = new FooComponent();
+ fc.setGlarch(g);
+ FooProxy f = new Foo();
+ FooProxy f2 = new Foo();
+ f.setComponent(fc);
+ f.setFoo(f2);
+ s.save(f2);
+ Serializable id = s.save(f);
+ Serializable gid = s.getIdentifier( f.getComponent().getGlarch() );
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ getSessions().evict(Foo.class);
+
+ s = openSession();
+ f = (FooProxy) s.load(Foo.class, id);
+ assertFalse( Hibernate.isInitialized(f) );
+ assertTrue( Hibernate.isInitialized( f.getComponent().getGlarch() ) ); //outer-join="true"
+ assertFalse( Hibernate.isInitialized( f.getFoo() ) ); //outer-join="auto"
+ assertEquals( s.getIdentifier( f.getComponent().getGlarch() ), gid );
+ s.delete(f);
+ s.delete( f.getFoo() );
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testEmptyCollection() throws Exception {
+ Session s = openSession();
+ Serializable id = s.save( new Baz() );
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ Baz baz = (Baz) s.load(Baz.class, id);
+ Set foos = baz.getFooSet();
+ assertTrue( foos.size()==0 );
+ Foo foo = new Foo();
+ foos.add(foo);
+ s.save(foo);
+ s.flush();
+ s.delete(foo);
+ s.delete(baz);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testOneToOneGenerator() throws Exception {
+ Session s = openSession();
+ X x = new X();
+ Y y = new Y();
+ x.setY(y);
+ y.setTheX(x);
+ x.getXxs().add( new X.XX(x) );
+ x.getXxs().add( new X.XX(x) );
+ Serializable id = s.save(y);
+ assertEquals( id, s.save(x) );
+ s.flush();
+ assertTrue( s.contains(y) && s.contains(x) );
+ s.connection().commit();
+ s.close();
+ assertEquals( new Long(x.getId()), y.getId() );
+
+ s = openSession();
+ x = new X();
+ y = new Y();
+ x.setY(y);
+ y.setTheX(x);
+ x.getXxs().add( new X.XX(x) );
+ s.save(y);
+ s.flush();
+ assertTrue( s.contains(y) && s.contains(x) );
+ s.connection().commit();
+ s.close();
+ assertEquals( new Long(x.getId()), y.getId() );
+
+ s = openSession();
+ x = new X();
+ y = new Y();
+ x.setY(y);
+ y.setTheX(x);
+ x.getXxs().add( new X.XX(x) );
+ x.getXxs().add( new X.XX(x) );
+ id = s.save(x);
+ assertEquals( id, y.getId() );
+ assertEquals( id, new Long( x.getId() ) );
+ s.flush();
+ assertTrue( s.contains(y) && s.contains(x) );
+ s.delete("from X x");
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ }
+
+ public void testLimit() throws Exception {
+ Session s = openSession();
+ Transaction txn = s.beginTransaction();
+ for ( int i=0; i<10; i++ ) s.save( new Foo() );
+ Iterator iter = s.createQuery("from Foo foo")
+ .setMaxResults(4)
+ .setFirstResult(2)
+ .iterate();
+ int count=0;
+ while ( iter.hasNext() ) {
+ iter.next();
+ count++;
+ }
+ assertTrue(count==4);
+ iter = s.createQuery("select distinct foo from Foo foo")
+ .setMaxResults(2)
+ .setFirstResult(2)
+ .list()
+ .iterator();
+ count=0;
+ while ( iter.hasNext() ) {
+ iter.next();
+ count++;
+ }
+ assertTrue(count==2);
+ iter = s.createQuery("select distinct foo from Foo foo")
+ .setMaxResults(3)
+ .list()
+ .iterator();
+ count=0;
+ while ( iter.hasNext() ) {
+ iter.next();
+ count++;
+ }
+ assertTrue(count==3);
+ assertTrue( s.delete("from Foo foo")==10 );
+ txn.commit();
+ s.close();
+ }
+
+ public void testCustom() throws Exception {
+ GlarchProxy g = new Glarch();
+ Multiplicity m = new Multiplicity();
+ m.count = 12;
+ m.glarch = (Glarch) g;
+ g.setMultiple(m);
+ Session s = openSession();
+ Serializable gid = s.save(g);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ g = (Glarch) s.find("from Glarch g where g.multiple.count=12").get(0);
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ g = (Glarch) s.find("from Glarch g where g.multiple.glarch=g and g.multiple.count=12").get(0);
+ assertTrue( g.getMultiple()!=null );
+ assertEquals( g.getMultiple().count, 12 );
+ assertSame(g.getMultiple().glarch, g);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ g = (GlarchProxy) s.load(Glarch.class, gid);
+ assertTrue( g.getMultiple()!=null );
+ assertEquals( g.getMultiple().count, 12 );
+ assertSame(g.getMultiple().glarch, g);
+ s.delete(g);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testSaveAddDelete() throws Exception {
+ Session s = openSession();
+ Baz baz = new Baz();
+ Set bars = new HashSet();
+ baz.setCascadingBars(bars);
+ s.save(baz);
+ s.flush();
+ baz.getCascadingBars().add( new Bar() );
+ s.delete(baz);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testNamedParams() throws Exception {
+ Bar bar = new Bar();
+ Bar bar2 = new Bar();
+ bar.setName("Bar");
+ bar2.setName("Bar Two");
+ bar.setX(10);
+ bar2.setX(1000);Baz baz = new Baz();
+ baz.setCascadingBars( new HashSet() );
+ baz.getCascadingBars().add(bar);
+ bar.setBaz(baz);
+
+ Session s = openSession();
+ Transaction txn = s.beginTransaction();
+ s.save(baz);
+ s.save(bar2);
+
+ List list = s.find("from Bar bar left join bar.baz baz left join baz.cascadingBars b where bar.name like 'Bar %'");
+ Object row = list.iterator().next();
+ assertTrue( row instanceof Object[] && ( (Object[]) row ).length==3 );
+
+ Query q = s.createQuery("select bar, b from Bar bar left join bar.baz baz left join baz.cascadingBars b where bar.name like 'Bar%'");
+ list = q.list();
+ if ( !(getDialect() instanceof SAPDBDialect) ) assertTrue( list.size()==2 );
+
+ q = s.createQuery("select bar, b from Bar bar left join bar.baz baz left join baz.cascadingBars b where ( bar.name in (:nameList) or bar.name in (:nameList) ) and bar.string = :stringVal");
+ HashSet nameList = new HashSet();
+ nameList.add("bar");
+ nameList.add("Bar");
+ nameList.add("Bar Two");
+ q.setParameterList("nameList", nameList);
+ q.setParameter("stringVal", "a string");
+ list = q.list();
+ if ( !(getDialect() instanceof SAPDBDialect) ) assertTrue( list.size()==2 );
+
+ try {
+ q.setParameterList("nameList", (Collection)null);
+ fail("Should throw an queryexception when passing a null!");
+ } catch (QueryException qe) {
+ //should happen
+ }
+
+
+ if (dialectSupportsEmptyInList("HQL 'x in (:name)' with EMPTY_LIST.")) {
+ q.setParameterList("nameList", Collections.EMPTY_LIST);
+ list = q.list();
+ assertTrue( list.size()==0 );
+ }
+
+ q = s.createQuery("select bar, b from Bar bar inner join bar.baz baz inner join baz.cascadingBars b where bar.name like 'Bar%'");
+ Object result = q.uniqueResult();
+ assertTrue( result!=null );
+ q = s.createQuery("select bar, b from Bar bar left join bar.baz baz left join baz.cascadingBars b where bar.name like :name and b.name like :name");
+ q.setString("name", "Bar%");
+ list = q.list();
+ assertTrue( list.size()==1 );
+
+
+ // This test added for issue HB-297 - there is an named parameter in the Order By clause
+ q = s.createQuery("select bar from Bar bar order by ((bar.x - :valueX)*(bar.x - :valueX))");
+ q.setInteger("valueX", bar.getX()+1);
+ list = q.list();
+ assertTrue( ((Bar)list.get(0)).getX() == bar.getX());
+ q.setInteger("valueX", bar2.getX()+1);
+ list = q.list();
+ assertTrue( ((Bar)list.get(0)).getX() == bar2.getX());
+
+ s.delete(baz);
+ s.delete(bar2);
+ txn.commit();
+ s.close();
+ }
+
+ public void testParameterCheck() throws HibernateException {
+ Session s = openSession();
+ try {
+ Query q = s.createQuery("select bar from Bar as bar where bar.x > :myX");
+ q.list();
+ fail("Should throw QueryException for missing myX");
+ }
+ catch (QueryException iae) {
+ // should happen
+ }
+ finally {
+ s.close();
+ }
+
+ s = openSession();
+ try {
+ Query q = s.createQuery("select bar from Bar as bar where bar.x > ?");
+ q.list();
+ fail("Should throw QueryException for missing ?");
+ }
+ catch (QueryException iae) {
+ // should happen
+ }
+ finally {
+ s.close();
+ }
+
+ s = openSession();
+ try {
+ Query q = s.createQuery("select bar from Bar as bar where bar.x > ? or bar.short = 1 or bar.string = 'ff ? bb'");
+ q.setInteger(0, 1);
+ q.list();
+ }
+ catch (QueryException iae) {
+ fail("Should not throw QueryException for missing ?");
+ }
+ finally {
+ s.close();
+ }
+
+ s = openSession();
+ try {
+ Query q = s.createQuery("select bar from Bar as bar where bar.string = ' ? ' or bar.string = '?'");
+ q.list();
+ }
+ catch (QueryException iae) {
+ fail("Should not throw QueryException for ? in quotes");
+ }
+ finally {
+ s.close();
+ }
+
+ s = openSession();
+ try {
+ Query q = s.createQuery("select bar from Bar as bar where bar.string = ? or bar.string = ? or bar.string = ?");
+ q.setParameter(0, "bull");
+ q.setParameter(2, "shit");
+ q.list();
+ fail("should throw exception telling me i have not set parameter 1");
+ }
+ catch (QueryException iae) {
+ // should happen!
+ }
+ finally {
+ s.close();
+ }
+ }
+ public void testDyna() throws Exception {
+ Session s = openSession();
+ GlarchProxy g = new Glarch();
+ g.setName("G");
+ Serializable id = s.save(g);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ g = (GlarchProxy) s.load(Glarch.class, id);
+ assertTrue( g.getName().equals("G") );
+ assertTrue( g.getDynaBean().get("foo").equals("foo") && g.getDynaBean().get("bar").equals( new Integer(66) ) );
+ assertTrue( ! (g instanceof Glarch) );
+ g.getDynaBean().put("foo", "bar");
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ g = (GlarchProxy) s.load(Glarch.class, id);
+ assertTrue( g.getDynaBean().get("foo").equals("bar") && g.getDynaBean().get("bar").equals( new Integer(66) ) );
+ g.setDynaBean(null);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ g = (GlarchProxy) s.load(Glarch.class, id);
+ assertTrue( g.getDynaBean()==null );
+ s.delete(g);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testFindByCriteria() throws Exception {
+ if ( getDialect() instanceof DB2Dialect ) return;
+ Session s = openSession();
+ Transaction txn = s.beginTransaction();
+ Foo f = new Foo();
+ s.save(f);
+ s.flush();
+
+ List list = s.createCriteria(Foo.class)
+ .add( Expression.eq( "integer", f.getInteger() ) )
+ .add( Expression.eqProperty("integer", "integer") )
+ .add( Expression.like( "string", f.getString().toUpperCase() ).ignoreCase() )
+ .add( Expression.in( "boolean", new Boolean[] { f.getBoolean(), f.getBoolean() } ) )
+ .setFetchMode("foo", FetchMode.JOIN)
+ .setFetchMode("baz", FetchMode.SELECT)
+ .setFetchMode("abstracts", FetchMode.JOIN)
+ .list();
+ assertTrue( list.size()==1 && list.get(0)==f );
+
+ list = s.createCriteria(Foo.class).add(
+ Expression.disjunction()
+ .add( Expression.eq( "integer", f.getInteger() ) )
+ .add( Expression.like( "string", f.getString() ) )
+ .add( Expression.eq( "boolean", f.getBoolean() ) )
+ )
+ .add( Expression.isNotNull("boolean") )
+ .list();
+ assertTrue( list.size()==1 && list.get(0)==f );
+
+ Foo example = new Foo();
+ example.setString("a STRing");
+ list = s.createCriteria(Foo.class).add(
+ Example.create(example)
+ .excludeZeroes()
+ .ignoreCase()
+ .excludeProperty("bool")
+ .excludeProperty("char")
+ .excludeProperty("yesno")
+ )
+ .list();
+ assertTrue( "Example API without like did not work correctly, size was " + list.size(), list.size()==1 && list.get(0)==f );
+ example.setString("rin");
+
+ list = s.createCriteria(Foo.class).add(
+ Example.create(example)
+ .excludeZeroes()
+ .enableLike(MatchMode.ANYWHERE)
+ .excludeProperty("bool")
+ .excludeProperty("char")
+ .excludeProperty("yesno")
+ )
+ .list();
+ assertTrue( "Example API without like did not work correctly, size was " + list.size(), list.size()==1 && list.get(0)==f );
+
+ list = s.createCriteria(Foo.class)
+ .add( Expression.or(
+ Expression.and(
+ Expression.eq( "integer", f.getInteger() ),
+ Expression.like( "string", f.getString() )
+ ),
+ Expression.eq( "boolean", f.getBoolean() )
+ ) )
+ .list();
+ assertTrue( list.size()==1 && list.get(0)==f );
+ list = s.createCriteria(Foo.class)
+ .setMaxResults(5)
+ .addOrder( Order.asc("date") )
+ .list();
+ assertTrue( list.size()==1 && list.get(0)==f );
+ if(!(getDialect() instanceof TimesTenDialect)) {
+ list = s.createCriteria(Foo.class).setMaxResults(0).list();
+ assertTrue( list.size()==0 );
+ }
+ list = s.createCriteria(Foo.class)
+ .setFirstResult(1)
+ .addOrder( Order.asc("date") )
+ .addOrder( Order.desc("string") )
+ .list();
+ assertTrue( list.size()==0 );
+ list = s.createCriteria(Foo.class)
+ .setFetchMode("component.importantDates", FetchMode.EAGER)
+ .list();
+ assertTrue( list.size()==3 );
+
+ list = s.createCriteria(Foo.class)
+ .setFetchMode("component.importantDates", FetchMode.EAGER)
+ .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
+ .list();
+ assertTrue( list.size()==1 );
+
+ f.setFoo( new Foo() );
+ s.save( f.getFoo() );
+ txn.commit();
+ s.close();
+
+ s = openSession();
+ txn = s.beginTransaction();
+ list = s.createCriteria(Foo.class)
+ .add( Expression.eq( "integer", f.getInteger() ) )
+ .add( Expression.like( "string", f.getString() ) )
+ .add( Expression.in( "boolean", new Boolean[] { f.getBoolean(), f.getBoolean() } ) )
+ .add( Expression.isNotNull("foo") )
+ .setFetchMode("foo", FetchMode.EAGER)
+ .setFetchMode("baz", FetchMode.LAZY)
+ .setFetchMode("component.glarch", FetchMode.LAZY)
+ .setFetchMode("foo.baz", FetchMode.LAZY)
+ .setFetchMode("foo.component.glarch", FetchMode.LAZY)
+ .list();
+ f = (Foo) list.get(0);
+ assertTrue( Hibernate.isInitialized( f.getFoo() ) );
+ assertTrue( !Hibernate.isInitialized( f.getComponent().getGlarch() ) );
+
+ s.save( new Bar() );
+ list = s.createCriteria(Bar.class)
+ .list();
+ assertTrue( list.size()==1 );
+ assertTrue( s.createCriteria(Foo.class).list().size()==3 );
+ s.delete( list.get(0) );
+
+ s.delete( f.getFoo() );
+ s.delete(f);
+ txn.commit();
+ s.close();
+ }
+
+ public void testAfterDelete() throws Exception {
+ Session s = openSession();
+ Foo foo = new Foo();
+ s.save(foo);
+ s.flush();
+ s.delete(foo);
+ s.save(foo);
+ s.delete(foo);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testCollectionWhere() throws Exception {
+ Foo foo1 = new Foo();
+ Foo foo2 = new Foo();
+ Baz baz = new Baz();
+ Foo[] arr = new Foo[10];
+ arr[0] = foo1;
+ arr[9] = foo2;
+ Session s = openSession();
+ s.save(foo1);
+ s.save(foo2);
+ baz.setFooArray(arr);
+ s.save(baz);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ baz = (Baz) s.load( Baz.class, baz.getCode() );
+ assertTrue( baz.getFooArray().length==1 );
+ assertTrue( s.find("from Baz baz join baz.fooArray foo").size()==1 );
+ assertTrue( s.find("from Foo foo").size()==2 );
+ assertTrue( s.filter( baz.getFooArray(), "" ).size()==1 );
+ //assertTrue( s.delete("from java.lang.Object o")==9 );
+ s.delete("from Foo foo");
+ String bazid = baz.getCode();
+ s.delete(baz);
+ int rows=s.connection().createStatement().executeUpdate(
+ "delete from fooArray where id_='" + bazid + "' and i>=8"
+ );
+ assertTrue(rows==1);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testComponentParent() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ BarProxy bar = new Bar();
+ bar.setBarComponent( new FooComponent() );
+ Baz baz = new Baz();
+ baz.setComponents( new FooComponent[] { new FooComponent(), new FooComponent() } );
+ s.save(bar);
+ s.save(baz);
+ t.commit();
+ s.close();
+ s = openSession();
+ t = s.beginTransaction();
+ bar = (BarProxy) s.load(Bar.class, bar.getKey());
+ s.load(baz, baz.getCode());
+ assertTrue( bar.getBarComponent().getParent()==bar );
+ assertTrue( baz.getComponents()[0].getBaz()==baz && baz.getComponents()[1].getBaz()==baz );
+ s.delete(baz);
+ s.delete(bar);
+ t.commit();
+ s.close();
+ }
+
+ public void testCollectionCache() throws Exception {
+ Session s = openSession();
+ Baz baz = new Baz();
+ baz.setDefaults();
+ s.save(baz);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ s.load( Baz.class, baz.getCode() );
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ baz = (Baz) s.load( Baz.class, baz.getCode() );
+ s.delete(baz);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void ntestAssociationId() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Bar bar = new Bar();
+ String id = (String) s.save(bar);
+ MoreStuff more = new MoreStuff();
+ more.setName("More Stuff");
+ more.setIntId(12);
+ more.setStringId("id");
+ Stuff stuf = new Stuff();
+ stuf.setMoreStuff(more);
+ more.setStuffs( new ArrayList() );
+ more.getStuffs().add(stuf);
+ stuf.setFoo(bar);
+ stuf.setId(1234);
+ stuf.setProperty( TimeZone.getDefault() );
+ s.save(more);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ assertTrue( s.find(
+ "from Stuff as s where s.foo.id = ? and s.id.id = ? and s.moreStuff.id.intId = ? and s.moreStuff.id.stringId = ?",
+ new Object[] { bar, new Long(1234), new Integer(12), "id" },
+ new Type[] { Hibernate.entity(Foo.class), Hibernate.LONG, Hibernate.INTEGER, Hibernate.STRING }
+ ).size()==1 );
+ assertTrue( s.find(
+ "from Stuff as s where s.foo.id = ? and s.id.id = ? and s.moreStuff.name = ?",
+ new Object[] { bar, new Long(1234), "More Stuff" },
+ new Type[] { Hibernate.entity(Foo.class), Hibernate.LONG, Hibernate.STRING }
+ ).size()==1 );
+ s.find("from Stuff as s where s.foo.string is not null");
+ assertTrue(
+ s.find("from Stuff as s where s.foo > '0' order by s.foo").size()==1
+ );
+ //s.createCriteria(Stuff.class).createCriteria("id.foo").add( Expression.isNull("foo") ).list();
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ FooProxy foo = (FooProxy) s.load(Foo.class, id);
+ s.load(more, more);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Stuff stuff = new Stuff();
+ stuff.setFoo(foo);
+ stuff.setId(1234);
+ stuff.setMoreStuff(more);
+ s.load(stuff, stuff);
+ assertTrue( stuff.getProperty().equals( TimeZone.getDefault() ) );
+ assertTrue( stuff.getMoreStuff().getName().equals("More Stuff") );
+ s.delete("from MoreStuff");
+ s.delete("from Foo foo");
+ t.commit();
+ s.close();
+ }
+
+ public void testCascadeSave() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Baz baz = new Baz();
+ List list = new ArrayList();
+ list.add( new Fee() );
+ list.add( new Fee() );
+ baz.setFees(list);
+ s.save(baz);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ baz = (Baz) s.load( Baz.class, baz.getCode() );
+ assertTrue( baz.getFees().size()==2 );
+ s.delete(baz);
+ assertTrue( !s.iterate("from Fee fee").hasNext() );
+ t.commit();
+ s.close();
+
+ }
+
+ public void testCollectionsInSelect() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Foo[] foos = new Foo[] { null, new Foo() };
+ s.save( foos[1] );
+ Baz baz = new Baz();
+ baz.setDefaults();
+ baz.setFooArray(foos);
+ s.save(baz);
+ Baz baz2 = new Baz();
+ baz2.setDefaults();
+ s.save(baz2);
+
+ Bar bar = new Bar();
+ bar.setBaz(baz);
+ s.save(bar);
+
+ List list = s.find("select new Result(foo.string, foo.long, foo.integer) from Foo foo");
+ assertTrue( list.size()==2 && ( list.get(0) instanceof Result ) && ( list.get(1) instanceof Result ) );
+ /*list = s.find("select new Result( baz.name, foo.long, count(elements(baz.fooArray)) ) from Baz baz join baz.fooArray foo group by baz.name, foo.long");
+ assertTrue( list.size()==1 && ( list.get(0) instanceof Result ) );
+ Result r = ((Result) list.get(0) );
+ assertEquals( r.getName(), baz.getName() );
+ assertEquals( r.getCount(), 1 );
+ assertEquals( r.getAmount(), foos[1].getLong().longValue() );*/
+ list = s.find("select new Result( baz.name, max(foo.long), count(foo) ) from Baz baz join baz.fooArray foo group by baz.name");
+ assertTrue( list.size()==1 && ( list.get(0) instanceof Result ) );
+ Result r = ((Result) list.get(0) );
+ assertEquals( r.getName(), baz.getName() );
+ assertEquals( r.getCount(), 1 );
+ assertTrue( r.getAmount() > 696969696969696000l );
+
+
+ //s.find("select max( elements(bar.baz.fooArray) ) from Bar as bar");
+ //The following test is disabled for databases with no subselects...also for Interbase (not sure why).
+ if ( !(getDialect() instanceof MySQLDialect) && !(getDialect() instanceof HSQLDialect) /*&& !(dialect instanceof MckoiDialect)*/ && !(getDialect() instanceof SAPDBDialect) && !(getDialect() instanceof PointbaseDialect) ) {
+ s.find("select count(*) from Baz as baz where 1 in indices(baz.fooArray)");
+ s.find("select count(*) from Bar as bar where 'abc' in elements(bar.baz.fooArray)");
+ s.find("select count(*) from Bar as bar where 1 in indices(bar.baz.fooArray)");
+ if ( !(getDialect() instanceof DB2Dialect) && !(getDialect() instanceof Oracle9Dialect) ) {
+ s.find("select count(*) from Bar as bar, bar.component.glarch.proxyArray as g where g.id in indices(bar.baz.fooArray)");
+ s.find("select max( elements(bar.baz.fooArray) ) from Bar as bar, bar.component.glarch.proxyArray as g where g.id in indices(bar.baz.fooArray)");
+ }
+ s.find("select count(*) from Bar as bar where '1' in (from bar.component.glarch.proxyArray g where g.name='foo')");
+ s.find("select count(*) from Bar as bar where '1' in (from bar.component.glarch.proxyArray g where g.name='foo')");
+ s.find("select count(*) from Bar as bar left outer join bar.component.glarch.proxyArray as pg where '1' in (from bar.component.glarch.proxyArray)");
+ }
+
+ list = s.find("from Baz baz left join baz.fooToGlarch join fetch baz.fooArray foo left join fetch foo.foo");
+ assertTrue( list.size()==1 && ( (Object[]) list.get(0) ).length==2 );
+
+ s.find("select baz.name from Bar bar inner join bar.baz baz inner join baz.fooSet foo where baz.name = bar.string");
+ s.find("SELECT baz.name FROM Bar AS bar INNER JOIN bar.baz AS baz INNER JOIN baz.fooSet AS foo WHERE baz.name = bar.string");
+
+ if ( !( getDialect() instanceof HSQLDialect ) ) s.find("select baz.name from Bar bar join bar.baz baz left outer join baz.fooSet foo where baz.name = bar.string");
+
+ s.find("select baz.name from Bar bar join bar.baz baz join baz.fooSet foo where baz.name = bar.string");
+ s.find("SELECT baz.name FROM Bar AS bar JOIN bar.baz AS baz JOIN baz.fooSet AS foo WHERE baz.name = bar.string");
+
+ if ( !( getDialect() instanceof HSQLDialect ) ) {
+ s.find("select baz.name from Bar bar left join bar.baz baz left join baz.fooSet foo where baz.name = bar.string");
+ s.find("select foo.string from Bar bar left join bar.baz.fooSet foo where bar.string = foo.string");
+ }
+
+ s.find("select baz.name from Bar bar left join bar.baz baz left join baz.fooArray foo where baz.name = bar.string");
+ s.find("select foo.string from Bar bar left join bar.baz.fooArray foo where bar.string = foo.string");
+
+ s.find("select bar.string, foo.string from Bar bar inner join bar.baz as baz inner join baz.fooSet as foo where baz.name = 'name'");
+ s.find("select foo from Bar bar inner join bar.baz as baz inner join baz.fooSet as foo");
+ s.find("select foo from Bar bar inner join bar.baz.fooSet as foo");
+
+ s.find("select bar.string, foo.string from Bar bar join bar.baz as baz join baz.fooSet as foo where baz.name = 'name'");
+ s.find("select foo from Bar bar join bar.baz as baz join baz.fooSet as foo");
+ s.find("select foo from Bar bar join bar.baz.fooSet as foo");
+
+ assertTrue( s.find("from Bar bar join bar.baz.fooArray foo").size()==1 );
+
+ assertTrue( s.find("from Bar bar join bar.baz.fooSet foo").size()==0 );
+ assertTrue( s.find("from Bar bar join bar.baz.fooArray foo").size()==1 );
+
+ s.delete(bar);
+
+ if ( getDialect() instanceof DB2Dialect || getDialect() instanceof PostgreSQLDialect ) {
+ s.iterate("select one from One one join one.manies many group by one order by count(many)");
+ s.iterate("select one from One one join one.manies many group by one having count(many) < 5");
+ }
+
+ s.find("from One one join one.manies many where one.id = 1 and many.id = 1");
+ s.iterate("select one.id, elements(one.manies) from One one");
+ s.iterate("select max( elements(one.manies) ) from One one");
+ s.find("select one, elements(one.manies) from One one");
+ //s.iterate("select one, max( elements(one.manies) ) from One one group by one");
+ Iterator iter = s.iterate("select elements(baz.fooArray) from Baz baz where baz.id=?", baz.getCode(), Hibernate.STRING);
+ //WAS: assertTrue( iter.next()==null && iter.next()==foos[1] && !iter.hasNext() );
+ assertTrue( iter.next()==foos[1] && !iter.hasNext() );
+ list = s.find("select elements(baz.fooArray) from Baz baz where baz.id=?", baz.getCode(), Hibernate.STRING);
+ //WAS: assertTrue( list.size()==2 );
+ assertTrue( list.size()==1 );
+ iter = s.iterate("select indices(baz.fooArray) from Baz baz where baz.id=?", baz.getCode(), Hibernate.STRING);
+ //WAS: assertTrue( iter.next().equals( new Integer(0) ) && iter.next().equals( new Integer(1) ) && !iter.hasNext() );
+ assertTrue( iter.next().equals( new Integer(1) ) && !iter.hasNext() );
+
+ //assertTrue( s.iterate("select max( elements(baz.timeArray) ) from Baz baz where baz.id=?", baz.getCode(), Hibernate.STRING).next() instanceof Time );
+ //assertTrue( s.iterate("select max( elements(baz.stringSet) ) from Baz baz where baz.id=?", baz.getCode(), Hibernate.STRING).next().equals("foo") );
+ assertTrue( s.iterate("select size(baz.stringSet) from Baz baz where baz.id=?", baz.getCode(), Hibernate.STRING).next().equals( new Integer(3) ) );
+ //s.find("from One one where sum one.manies.elements =0 or 1 = min one.manies.elements");
+
+ s.find("from Foo foo where foo.component.glarch.id is not null");
+
+ //iter = s.iterate("select baz, max( elements(baz.timeArray) ) from Baz baz group by baz");
+ //while ( iter.hasNext() ) { Object[] arr = (Object[]) iter.next(); System.out.println( arr[0] + " " + arr[1] ); }
+ iter = s.iterate("select baz, size(baz.stringSet), count( distinct elements(baz.stringSet) ), max( elements(baz.stringSet) ) from Baz baz group by baz");
+ while ( iter.hasNext() ) { Object[] arr = (Object[]) iter.next(); System.out.println( arr[0] + " " + arr[1] + " " + arr[2] + " " + arr[3] ); }
+
+ s.delete(baz);
+ s.delete(baz2);
+ s.delete( foos[1] );
+ t.commit();
+ s.close();
+ }
+
+ public void testNewFlushing() throws Exception {
+ Session s = openSession();
+ Transaction txn = s.beginTransaction();
+ Baz baz = new Baz();
+ baz.setDefaults();
+ s.save(baz);
+ s.flush();
+ baz.getStringArray()[0] = "a new value";
+ Iterator iter = s.iterate("from Baz baz");//no flush
+ assertTrue( iter.next()==baz );
+ iter = s.iterate("select elements(baz.stringArray) from Baz baz");
+ boolean found = false;
+ while ( iter.hasNext() ) {
+ if ( iter.next().equals("a new value") ) found = true;
+ }
+ assertTrue(found);
+ baz.setStringArray(null);
+ s.iterate("from Baz baz"); //no flush
+ iter = s.iterate("select elements(baz.stringArray) from Baz baz");
+ assertTrue( !iter.hasNext() );
+ baz.getStringList().add("1E1");
+ iter = s.iterate("from Foo foo");//no flush
+ assertTrue( !iter.hasNext() );
+ iter = s.iterate("select elements(baz.stringList) from Baz baz");
+ found = false;
+ while ( iter.hasNext() ) {
+ if ( iter.next().equals("1E1") ) found = true;
+ }
+ assertTrue(found);
+ baz.getStringList().remove("1E1");
+ iter = s.iterate("select elements(baz.stringArray) from Baz baz"); //no flush
+ iter = s.iterate("select elements(baz.stringList) from Baz baz");
+ found = false;
+ while ( iter.hasNext() ) {
+ if ( iter.next().equals("1E1") ) found = true;
+ }
+ assertTrue(!found);
+
+ List newList = new ArrayList();
+ newList.add("value");
+ baz.setStringList(newList);
+ iter = s.iterate("from Foo foo");//no flush
+ baz.setStringList(null);
+ iter = s.iterate("select elements(baz.stringList) from Baz baz");
+ assertTrue( !iter.hasNext() );
+
+ baz.setStringList(newList);
+ iter = s.iterate("from Foo foo");//no flush
+ iter = s.iterate("select elements(baz.stringList) from Baz baz");
+ assertTrue( iter.hasNext() );
+
+ s.delete(baz);
+ txn.commit();
+ s.close();
+ }
+
+ public void testPersistCollections() throws Exception {
+ Session s = openSession();
+ Transaction txn = s.beginTransaction();
+ assertTrue( ( (Long) s.iterate("select count(*) from Bar").next() ).longValue()==0 );
+ assertTrue( s.iterate("select count(*) from Bar b").next().equals( new Long(0) ) );
+ assertFalse( s.iterate("from Glarch g").hasNext() );
+
+ Baz baz = new Baz();
+ s.save(baz);
+ baz.setDefaults();
+ baz.setStringArray( new String[] { "stuff" } );
+ Set bars = new HashSet();
+ bars.add( new Bar() );
+ baz.setCascadingBars(bars);
+ HashMap sgm = new HashMap();
+ sgm.put( "a", new Glarch() );
+ sgm.put( "b", new Glarch() );
+ baz.setStringGlarchMap(sgm);
+ //System.out.println( s.print(baz) );
+ txn.commit();
+ s.close();
+
+ s = openSession();
+ txn = s.beginTransaction();
+ assertTrue( ( (Long) s.iterate("select count(*) from Bar").next() ).longValue()==1 );
+ baz = (Baz) ( (Object[]) s.find("select baz, baz from Baz baz").get(0) )[1];
+ assertTrue( baz.getCascadingBars().size()==1 );
+ //System.out.println( s.print(baz) );
+ Foo foo = new Foo();
+ s.save(foo);
+ Foo foo2 = new Foo() ;
+ s.save(foo2);
+ baz.setFooArray( new Foo[] { foo, foo, null, foo2 } );
+ baz.getFooSet().add(foo);
+ baz.getCustoms().add( new String[] { "new", "custom" } );
+ baz.setStringArray(null);
+ baz.getStringList().set(0, "new value");
+ baz.setStringSet( new TreeSet() );
+ Time time = new java.sql.Time(12345);
+ baz.getTimeArray()[2] = time;
+ //System.out.println(time);
+
+ assertTrue( baz.getStringGlarchMap().size()==1 );
+
+ //The following test is disabled databases with no subselects
+ if ( !(getDialect() instanceof MySQLDialect) && !(getDialect() instanceof HSQLDialect) && !(getDialect() instanceof PointbaseDialect) ) {
+ List list = s.find("select foo from Foo foo, Baz baz where foo in elements(baz.fooArray) and 3 = some elements(baz.intArray) and 4 > all indices(baz.intArray)");
+ assertTrue( "collection.elements find", list.size()==2 );
+ }
+ if (!(getDialect() instanceof SAPDBDialect) ) { // SAPDB doesn't like distinct with binary type
+ List list = s.find("select distinct foo from Baz baz join baz.fooArray foo");
+ assertTrue( "collection.elements find", list.size()==2 );
+ }
+
+ List list = s.find("select foo from Baz baz join baz.fooSet foo");
+ assertTrue( "association.elements find", list.size()==1 );
+ txn.commit();
+ s.close();
+
+ s = openSession();
+ txn = s.beginTransaction();
+ assertTrue( ( (Long) s.iterate("select count(*) from Bar").next() ).longValue()==1 );
+ baz = (Baz) s.find("select baz from Baz baz order by baz").get(0);
+ assertTrue( "collection of custom types - added element", baz.getCustoms().size()==4 && baz.getCustoms().get(0)!=null );
+ assertTrue ( "component of component in collection", baz.getComponents()[1].getSubcomponent()!=null );
+ assertTrue( baz.getComponents()[1].getBaz()==baz );
+ assertTrue( "set of objects", ( (FooProxy) baz.getFooSet().iterator().next() ).getKey().equals( foo.getKey() ));
+ assertTrue( "collection removed", baz.getStringArray().length==0 );
+ assertTrue( "changed element", baz.getStringList().get(0).equals("new value"));
+ assertTrue( "replaced set", baz.getStringSet().size()==0 );
+ assertTrue( "array element change", baz.getTimeArray()[2]!=null );
+ assertTrue( baz.getCascadingBars().size()==1 );
+ //System.out.println( s.print(baz) );
+ baz.getStringSet().add("two");
+ baz.getStringSet().add("one");
+ baz.getBag().add("three");
+ txn.commit();
+ s.close();
+
+ s = openSession();
+ txn = s.beginTransaction();
+ baz = (Baz) s.find("select baz from Baz baz order by baz").get(0);
+ assertTrue( baz.getStringSet().size()==2 );
+ assertTrue( baz.getStringSet().first().equals("one") );
+ assertTrue( baz.getStringSet().last().equals("two") );
+ assertTrue( baz.getBag().size()==5 );
+ baz.getStringSet().remove("two");
+ baz.getBag().remove("duplicate");
+ txn.commit();
+ s.close();
+
+ s = openSession();
+ txn = s.beginTransaction();
+ assertTrue( ( (Long) s.iterate("select count(*) from Bar").next() ).longValue()==1 );
+ baz = (Baz) s.load(Baz.class, baz.getCode());
+ assertTrue( baz.getCascadingBars().size()==1 );
+ Bar bar = new Bar();
+ Bar bar2 = new Bar();
+ s.save(bar); s.save(bar2);
+ baz.setTopFoos( new HashSet() );
+ baz.getTopFoos().add(bar);
+ baz.getTopFoos().add(bar2);
+ assertTrue( baz.getCascadingBars().size()==1 );
+ baz.setTopGlarchez( new TreeMap() );
+ GlarchProxy g = new Glarch();
+ s.save(g);
+ baz.getTopGlarchez().put( new Character('G'), g );
+ HashMap map = new HashMap();
+ map.put(bar, g);
+ map.put(bar2, g);
+ baz.setFooToGlarch(map);
+ map = new HashMap();
+ map.put( new FooComponent("name", 123, null, null), bar );
+ map.put( new FooComponent("nameName", 12, null, null), bar );
+ baz.setFooComponentToFoo(map);
+ map = new HashMap();
+ map.put(bar, g);
+ baz.setGlarchToFoo(map);
+ txn.commit();
+ s.close();
+
+ s = openSession();
+ txn = s.beginTransaction();
+ baz = (Baz) s.find("select baz from Baz baz order by baz").get(0);
+ assertTrue( baz.getCascadingBars().size()==1 );
+
+ Session s2 = openSession();
+ Transaction txn2 = s2.beginTransaction();
+ assertTrue( ( (Long) s2.iterate("select count(*) from Bar").next() ).longValue()==3 );
+ Baz baz2 = (Baz) s2.find("select baz from Baz baz order by baz").get(0);
+ Object o = baz2.getFooComponentToFoo().get( new FooComponent("name", 123, null, null) );
+ assertTrue(
+ o==baz2.getFooComponentToFoo().get( new FooComponent("nameName", 12, null, null) ) && o!=null
+ );
+ txn2.commit();
+ s2.close();
+
+ assertTrue( Hibernate.isInitialized( baz.getFooToGlarch() ) );
+ assertTrue( baz.getTopFoos().size()==2 );
+ assertTrue( baz.getTopGlarchez().size()==1 );
+ assertTrue( baz.getTopFoos().iterator().next()!=null );
+ assertTrue( baz.getStringSet().size()==1 );
+ assertTrue( baz.getBag().size()==4 );
+ assertTrue( baz.getFooToGlarch().size()==2 );
+ assertTrue( baz.getFooComponentToFoo().size()==2 );
+ assertTrue( baz.getGlarchToFoo().size()==1 );
+ Iterator iter = baz.getFooToGlarch().keySet().iterator();
+ for (int i=0; i<2; i++ ) assertTrue( iter.next() instanceof BarProxy );
+ FooComponent fooComp = (FooComponent) baz.getFooComponentToFoo().keySet().iterator().next();
+ assertTrue(
+ ( (fooComp.getCount()==123 && fooComp.getName().equals("name"))
+ || (fooComp.getCount()==12 && fooComp.getName().equals("nameName")) )
+ && ( baz.getFooComponentToFoo().get(fooComp) instanceof BarProxy )
+ );
+ Glarch g2 = new Glarch();
+ s.save(g2);
+ g = (GlarchProxy) baz.getTopGlarchez().get( new Character('G') );
+ baz.getTopGlarchez().put( new Character('H'), g );
+ baz.getTopGlarchez().put( new Character('G'), g2 );
+ txn.commit();
+ s.close();
+
+ s = openSession();
+ txn = s.beginTransaction();
+ baz = (Baz) s.load(Baz.class, baz.getCode());
+ assertTrue( baz.getTopGlarchez().size()==2 );
+ assertTrue( baz.getCascadingBars().size()==1 );
+ txn.commit();
+ s.close();
+
+ s = openSession();
+ txn = s.beginTransaction();
+ assertTrue( ( (Long) s.iterate("select count(*) from Bar").next() ).longValue()==3 );
+ baz = (Baz) s.find("select baz from Baz baz order by baz").get(0);
+ assertTrue( baz.getTopGlarchez().size()==2 );
+ assertTrue( baz.getCascadingBars().size()==1 );
+ txn.commit();
+
+ s.disconnect();
+
+ s2 = (Session) SerializationHelper.deserialize( SerializationHelper.serialize(s) );
+ s.close();
+
+ s2.reconnect();
+ txn2 = s2.beginTransaction();
+ baz = (Baz) s2.load(Baz.class, baz.getCode());
+ assertTrue( ( (Long) s2.iterate("select count(*) from Bar").next() ).longValue()==3 );
+ s2.delete(baz);
+ s2.delete( baz.getTopGlarchez().get( new Character('G') ) );
+ s2.delete( baz.getTopGlarchez().get( new Character('H') ) );
+ int rows = s2.connection().createStatement().executeUpdate("update " + getDialect().openQuote() + "glarchez" + getDialect().closeQuote() + " set baz_map_id=null where baz_map_index='a'");
+ assertTrue(rows==1);
+ assertTrue( s2.delete("from Bar bar")==2 );
+ FooProxy[] arr = baz.getFooArray();
+ assertTrue( "new array of objects", arr.length==4 && arr[1].getKey().equals( foo.getKey() ) );
+ for ( int i=1; i<arr.length; i++ ) {
+ if ( arr[i]!=null) s2.delete(arr[i]);
+ }
+
+ s2.load( Qux.class, new Long(666) ); //nonexistent
+
+ assertTrue( s2.delete("from Glarch g")==1 );
+ txn2.commit();
+
+ s2.disconnect();
+
+ Session s3 = (Session) SerializationHelper.deserialize( SerializationHelper.serialize(s2) );
+ s2.close();
+ //s3.reconnect();
+ assertTrue( s3.load( Qux.class, new Long(666) )!=null ); //nonexistent
+ //s3.disconnect();
+ s3.close();
+ }
+
+ public void testSaveFlush() throws Exception {
+ Session s = openSession();
+ Fee fee = new Fee();
+ s.save( fee, "key" );
+ fee.setFi("blah");
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ fee = (Fee) s.load( Fee.class, fee.getKey() );
+ assertTrue( "blah".equals( fee.getFi() ) );
+ assertTrue( "key".equals( fee.getKey() ) );
+ s.delete(fee);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ }
+
+ public void testCreateUpdate() throws Exception {
+ Session s = openSession();
+ Foo foo = new Foo();
+ s.save(foo);
+ foo.setString("dirty");
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ Foo foo2 = new Foo();
+ s.load( foo2, foo.getKey() );
+ // There is an interbase bug that causes null integers to return as 0, also numeric precision is <= 15
+ assertTrue( "create-update", foo.equalsFoo(foo2) );
+ //System.out.println( s.print(foo2) );
+ s.delete(foo2);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ foo = new Foo();
+ s.save(foo, "assignedid");
+ foo.setString("dirty");
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ s.load(foo2, "assignedid");
+ // There is an interbase bug that causes null integers to return as 0, also numeric precision is <= 15
+ assertTrue( "create-update", foo.equalsFoo(foo2) );
+ //System.out.println( s.print(foo2) );
+ s.delete(foo2);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testUpdateCollections() throws Exception {
+ Session s = openSession();
+ Holder baz = new Holder();
+ baz.setName("123");
+ Foo f1 = new Foo();
+ Foo f2 = new Foo();
+ Foo f3 = new Foo();
+ One o = new One();
+ baz.setOnes( new ArrayList() );
+ baz.getOnes().add(o);
+ Foo[] foos = new Foo[] { f1, null, f2 };
+ baz.setFooArray(foos);
+ baz.setFoos( new HashSet() );
+ baz.getFoos().add(f1);
+ s.save(f1);
+ s.save(f2);
+ s.save(f3);
+ s.save(o);
+ s.save(baz);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ baz.getOnes().set(0, null);
+ baz.getOnes().add(o);
+ baz.getFoos().add(f2);
+ foos[0] = f3;
+ foos[1] = f1;
+
+ s = openSession();
+ s.saveOrUpdate(baz);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ Holder h = (Holder) s.load(Holder.class, baz.getId());
+ assertTrue( h.getOnes().get(0)==null );
+ assertTrue( h.getOnes().get(1)!=null );
+ assertTrue( h.getFooArray()[0]!=null);
+ assertTrue( h.getFooArray()[1]!=null);
+ assertTrue( h.getFooArray()[2]!=null);
+ assertTrue( h.getFoos().size()==2 );
+ s.connection().commit();
+ s.close();
+
+ baz.getFoos().remove(f1);
+ baz.getFoos().remove(f2);
+ baz.getFooArray()[0]=null;
+ baz.getFooArray()[0]=null;
+ baz.getFooArray()[0]=null;
+ s = openSession();
+ s.saveOrUpdate(baz);
+ s.delete("from Foo");
+ baz.getOnes().remove(o);
+ s.delete("from One");
+ s.delete(baz);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ }
+
+ public void testCreate() throws Exception {
+ Session s = openSession();
+ Foo foo = new Foo();
+ s.save(foo);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ Foo foo2 = new Foo();
+ s.load( foo2, foo.getKey() );
+ // There is an interbase bug that causes null integers to return as 0, also numeric precision is <= 15
+ assertTrue( "create", foo.equalsFoo(foo2) );
+ s.delete(foo2);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testCallback() throws Exception {
+ Session s = openSession();
+ Qux q = new Qux("0");
+ s.save(q);
+ q.setChild( new Qux("1") );
+ s.save( q.getChild() );
+ Qux q2 = new Qux("2");
+ q2.setChild( q.getChild() );
+ Qux q3 = new Qux("3");
+ q.getChild().setChild(q3);
+ s.save(q3);
+ Qux q4 = new Qux("4");
+ q4.setChild(q3);
+ s.save(q4);
+ s.save(q2);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ List l = s.find("from Qux");
+ assertTrue( "", l.size()==5);
+ s.delete( l.get(0) );
+ s.delete( l.get(1) );
+ s.delete( l.get(2) );
+ s.delete( l.get(3) );
+ s.delete( l.get(4) );
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testPolymorphism() throws Exception {
+ Session s = openSession();
+ Bar bar = new Bar();
+ s.save(bar);
+ bar.setBarString("bar bar");
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ FooProxy foo = (FooProxy) s.load( Foo.class, bar.getKey() );
+ assertTrue( "polymorphic", foo instanceof BarProxy );
+ assertTrue( "subclass property", ( (BarProxy) foo ).getBarString().equals( bar.getBarString() ) );
+ //System.out.println( s.print(foo) );
+ s.delete(foo);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testRemoveContains() throws Exception {
+ Session s = openSession();
+ Baz baz = new Baz();
+ baz.setDefaults();
+ s.save(baz);
+ s.flush();
+ assertTrue( s.contains(baz) );
+ s.evict(baz);
+ assertFalse( s.contains(baz) );
+ Baz baz2 = (Baz) s.load( Baz.class, baz.getCode() );
+ assertFalse(baz==baz2);
+ s.delete(baz2);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testCollectionOfSelf() throws Exception {
+
+ Session s = openSession();
+ Bar bar = new Bar();
+ s.save(bar);
+ bar.setAbstracts( new HashSet() );
+ bar.getAbstracts().add(bar);
+ Bar bar2 = new Bar();
+ bar.getAbstracts().add(bar2);
+ bar.setFoo(bar);
+ s.save(bar2);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ bar.setAbstracts(null);
+ s = openSession();
+ s.load( bar, bar.getKey() );
+ assertTrue( "collection contains self", bar.getAbstracts().size()==2 && bar.getAbstracts().contains(bar) );
+ assertTrue( "association to self", bar.getFoo()==bar );
+ Iterator iter = bar.getAbstracts().iterator();
+ while ( iter.hasNext() ) {
+ s.delete( iter.next() );
+ }
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testFind() throws Exception {
+ Session s = openSession();
+ Transaction txn = s.beginTransaction();
+
+ Bar bar = new Bar();
+ s.save(bar);
+ bar.setBarString("bar bar");
+ bar.setString("xxx");
+ Foo foo = new Foo();
+ s.save(foo);
+ foo.setString("foo bar");
+ s.save( new Foo() );
+ s.save( new Bar() );
+ List list1 = s.find("select foo from Foo foo where foo.string='foo bar'");
+ assertTrue( "find size", list1.size()==1 );
+ assertTrue( "find ==", list1.get(0)==foo );
+ List list2 = s.find("from Foo foo order by foo.string, foo.date");
+ assertTrue( "find size", list2.size()==4 );
+
+ list1 = s.find("from Foo foo where foo.class='B'");
+ assertTrue( "class special property", list1.size()==2);
+ list1 = s.find("from Foo foo where foo.class=Bar");
+ assertTrue( "class special property", list1.size()==2);
+ list1 = s.find("from Foo foo where foo.class=Bar");
+ list2 = s.find("select bar from Bar bar, Foo foo where bar.string = foo.string and not bar=foo");
+ assertTrue( "class special property", list1.size()==2);
+ assertTrue( "select from a subclass", list2.size()==1);
+ Trivial t = new Trivial();
+ s.save(t);
+ txn.commit();
+ s.close();
+
+ s = openSession();
+ txn = s.beginTransaction();
+ list1 = s.find("from Foo foo where foo.string='foo bar'");
+ assertTrue( "find size", list1.size()==1 );
+ // There is an interbase bug that causes null integers to return as 0, also numeric precision is <= 15
+ assertTrue( "find equals", ( (Foo) list1.get(0) ).equalsFoo(foo) );
+ list2 = s.find("select foo from Foo foo");
+ assertTrue( "find size", list2.size()==5 );
+ List list3 = s.find("from Bar bar where bar.barString='bar bar'");
+ assertTrue( "find size", list3.size()==1 );
+ assertTrue( "find same instance", list2.contains( list1.get(0) ) && list2.contains( list2.get(0) ) );
+ assertTrue( s.find("from Trivial").size()==1 );
+ s.delete("from Trivial");
+
+ list2 = s.find("from Foo foo where foo.date = ?", new java.sql.Date(123), Hibernate.DATE);
+ assertTrue ( "find by date", list2.size()==4 );
+ Iterator iter = list2.iterator();
+ while ( iter.hasNext() ) {
+ s.delete( iter.next() );
+ }
+ list2 = s.find("from Foo foo");
+ assertTrue( "find deleted", list2.size()==0);
+ txn.commit();
+ s.close();
+ }
+
+ public void testDeleteRecursive() throws Exception {
+ Session s = openSession();
+ Foo x = new Foo();
+ Foo y = new Foo();
+ x.setFoo(y);
+ y.setFoo(x);
+ s.save(x);
+ s.save(y);
+ s.flush();
+ s.delete(y);
+ s.delete(x);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ /*public void testSubcollections() throws Exception {
+ Session s = sessionsopenSession();
+ Baz baz = new Baz();
+ s.save(baz);
+ baz.setDefaults();
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = sessionsopenSession();
+ baz = (Baz) s.load( Baz.class, baz.getCode() );
+ Set[] setArray = baz.getSetArray();
+ baz.setSetArray(null);
+ baz.setAnotherSetArray(setArray);
+ baz.setAnotherSetList( baz.getSetList() );
+ baz.setSetList(null);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = sessionsopenSession();
+ baz = (Baz) s.load( Baz.class, baz.getCode() );
+ assertTrue( baz.getAnotherSetArray().length==2 && baz.getAnotherSetArray()[0]!=null, "subcollection moved property");
+ assertTrue( baz.getSetArray()==null, "subcollection moved property");
+ assertTrue( baz.getAnotherSetList().size()==4 && baz.getAnotherSetList().get(2)!=null, "subcollection moved role");
+ assertTrue( baz.getSetList()==null, "subcollection moved role");
+ s.delete(baz);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }*/
+
+
+ public void testReachability() throws Exception {
+ //first for unkeyed collections
+ Session s = openSession();
+ Baz baz1 = new Baz();
+ s.save(baz1);
+ Baz baz2 = new Baz();
+ s.save(baz2);
+ baz1.setIntArray( new int[] {1 ,2, 3, 4} );
+ baz1.setFooSet( new HashSet() );
+ Foo foo = new Foo();
+ s.save(foo);
+ baz1.getFooSet().add(foo);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ baz2 = (Baz) s.load( Baz.class, baz2.getCode() );
+ baz1 = (Baz) s.load( Baz.class, baz1.getCode() );
+ baz2.setFooSet( baz1.getFooSet() ); baz1.setFooSet(null);
+ baz2.setIntArray( baz1.getIntArray() ); baz1.setIntArray(null);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ baz2 = (Baz) s.load( Baz.class, baz2.getCode() );
+ baz1 = (Baz) s.load( Baz.class, baz1.getCode() );
+ assertTrue( "unkeyed reachability", baz2.getIntArray().length==4 );
+ assertTrue( "unkeyed reachability", baz2.getFooSet().size()==1 );
+ assertTrue( "unkeyed reachability", baz1.getIntArray().length==0 );
+ assertTrue( "unkeyed reachability", baz1.getFooSet().size()==0 );
+ //System.out.println( s.print(baz1) + s.print(baz2) );
+ FooProxy fp = (FooProxy) baz2.getFooSet().iterator().next();
+ s.delete(fp);
+ s.delete(baz1);
+ s.delete(baz2);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ //now for collections of collections
+ s = openSession();
+ baz1 = new Baz();
+ s.save(baz1);
+ baz2 = new Baz();
+ s.save(baz2);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ baz2 = (Baz) s.load( Baz.class, baz2.getCode() );
+ baz1 = (Baz) s.load( Baz.class, baz1.getCode() );
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ baz2 = (Baz) s.load( Baz.class, baz2.getCode() );
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ baz2 = (Baz) s.load( Baz.class, baz2.getCode() );
+ baz1 = (Baz) s.load( Baz.class, baz1.getCode() );
+ //System.out.println( s.print(baz1) + s.print(baz2) );
+ //System.out.println( s.print(baz1) + s.print(baz2) );
+ s.delete(baz1);
+ s.delete(baz2);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ //now for keyed collections
+ s = openSession();
+ baz1 = new Baz();
+ s.save(baz1);
+ baz2 = new Baz();
+ s.save(baz2);
+ Foo foo1 = new Foo();
+ Foo foo2 = new Foo();
+ s.save(foo1); s.save(foo2);
+ baz1.setFooArray( new Foo[] { foo1, null, foo2 } );
+ baz1.setStringDateMap( new TreeMap() );
+ baz1.getStringDateMap().put("today", new Date( System.currentTimeMillis() ) );
+ baz1.getStringDateMap().put("tomorrow", new Date( System.currentTimeMillis() + 86400000 ) );
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ baz2 = (Baz) s.load( Baz.class, baz2.getCode() );
+ baz1 = (Baz) s.load( Baz.class, baz1.getCode() );
+ baz2.setFooArray( baz1.getFooArray() ); baz1.setFooArray(null);
+ baz2.setStringDateMap( baz1.getStringDateMap() ); baz1.setStringDateMap(null);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ baz2 = (Baz) s.load( Baz.class, baz2.getCode() );
+ baz1 = (Baz) s.load( Baz.class, baz1.getCode() );
+ assertTrue( "reachability", baz2.getStringDateMap().size()==2 );
+ assertTrue( "reachability", baz2.getFooArray().length==3 );
+ assertTrue( "reachability", baz1.getStringDateMap().size()==0 );
+ assertTrue( "reachability", baz1.getFooArray().length==0 );
+ //System.out.println( s.print(baz1) + s.print(baz2) );
+ assertTrue( "null element", baz2.getFooArray()[1]==null );
+ assertTrue( "non-null element", baz2.getStringDateMap().get("today")!=null );
+ assertTrue( "non-null element", baz2.getStringDateMap().get("tomorrow")!=null );
+ assertTrue( "null element", baz2.getStringDateMap().get("foo")==null );
+ s.delete( baz2.getFooArray()[0] );
+ s.delete( baz2.getFooArray()[2] );
+ s.delete(baz1);
+ s.delete(baz2);
+ s.flush();
+ assertTrue( s.find("from java.lang.Object").size()==0 );
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testPersistentLifecycle() throws Exception {
+ Session s = openSession();
+ Qux q = new Qux();
+ s.save(q);
+ q.setStuff("foo bar baz qux");
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ q = (Qux) s.load( Qux.class, q.getKey() );
+ assertTrue( "lifecycle create", q.getCreated() );
+ assertTrue( "lifecycle load", q.getLoaded() );
+ assertTrue( "lifecycle subobject", q.getFoo()!=null );
+ s.delete(q);
+ assertTrue( "lifecycle delete", q.getDeleted() );
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ assertTrue( "subdeletion", s.find("from Foo foo").size()==0);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testIterators() throws Exception {
+ Session s = openSession();
+ for ( int i=0; i<10; i++ ) {
+ Qux q = new Qux();
+ Object qid = s.save(q);
+ assertTrue("not null", qid!=null);
+ }
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ Iterator iter = s.iterate("from Qux q where q.stuff is null");
+ int count=0;
+ while ( iter.hasNext() ) {
+ Qux q = (Qux) iter.next();
+ q.setStuff("foo");
+ if (count==0 || count==5) iter.remove();
+ count++;
+ }
+ assertTrue("iterate", count==10);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ assertTrue(
+ "delete by query",
+ s.delete("from Qux q where q.stuff=?", "foo", Hibernate.STRING)==8
+ );
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ iter = s.iterate("from Qux q");
+ assertTrue( "empty iterator", !iter.hasNext() );
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testVersioning() throws Exception {
+ Session s = openSession();
+ Transaction txn = s.beginTransaction();
+ GlarchProxy g = new Glarch();
+ s.save(g);
+ GlarchProxy g2 = new Glarch();
+ s.save(g2);
+ Serializable gid = s.getIdentifier(g);
+ Serializable g2id = s.getIdentifier(g2);
+ g.setName("glarch");
+ txn.commit();
+ s.close();
+
+ getSessions().evict(Glarch.class);
+
+ s = openSession();
+ txn = s.beginTransaction();
+ g = (GlarchProxy) s.load( Glarch.class, gid );
+ s.lock(g, LockMode.UPGRADE);
+ g2 = (GlarchProxy) s.load( Glarch.class, g2id );
+ assertTrue( "version", g.getVersion()==1 );
+ assertTrue( "version", g.getDerivedVersion()==1 );
+ assertTrue( "version", g2.getVersion()==0 );
+ g.setName("foo");
+ assertTrue(
+ "find by version",
+ s.find("from Glarch g where g.version=2").size()==1
+ );
+ g.setName("bar");
+ txn.commit();
+ s.close();
+
+ getSessions().evict(Glarch.class);
+
+ s = openSession();
+ txn = s.beginTransaction();
+ g = (GlarchProxy) s.load( Glarch.class, gid );
+ g2 = (GlarchProxy) s.load( Glarch.class, g2id );
+ assertTrue( "version", g.getVersion()==3 );
+ assertTrue( "version", g.getDerivedVersion()==3 );
+ assertTrue( "version", g2.getVersion()==0 );
+ g.setNext(null);
+ g2.setNext(g);
+ s.delete(g2);
+ s.delete(g);
+ txn.commit();
+ s.close();
+ }
+
+ public void testVersionedCollections() throws Exception {
+ Session s = openSession();
+ GlarchProxy g = new Glarch();
+ s.save(g);
+ g.setProxyArray( new GlarchProxy[] { g } );
+ String gid = (String) s.getIdentifier(g);
+ ArrayList list = new ArrayList();
+ list.add("foo");
+ g.setStrings(list);
+ HashSet set = new HashSet();
+ set.add(g);
+ g.setProxySet(set);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ g = (GlarchProxy) s.load(Glarch.class, gid);
+ assertTrue( g.getStrings().size()==1 );
+ assertTrue( g.getProxyArray().length==1 );
+ assertTrue( g.getProxySet().size()==1 );
+ assertTrue( "versioned collection before", g.getVersion()==1 );
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ g = (GlarchProxy) s.load(Glarch.class, gid);
+ assertTrue( g.getStrings().get(0).equals("foo") );
+ assertTrue( g.getProxyArray()[0]==g );
+ assertTrue( g.getProxySet().iterator().next()==g );
+ assertTrue( "versioned collection before", g.getVersion()==1 );
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ g = (GlarchProxy) s.load(Glarch.class, gid);
+ assertTrue( "versioned collection before", g.getVersion()==1 );
+ g.getStrings().add("bar");
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ g = (GlarchProxy) s.load(Glarch.class, gid);
+ assertTrue( "versioned collection after", g.getVersion()==2 );
+ assertTrue( "versioned collection after", g.getStrings().size()==2 );
+ g.setProxyArray(null);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ g = (GlarchProxy) s.load(Glarch.class, gid);
+ assertTrue( "versioned collection after", g.getVersion()==3 );
+ assertTrue( "versioned collection after", g.getProxyArray().length==0 );
+ g.setFooComponents( new ArrayList() );
+ g.setProxyArray(null);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ g = (GlarchProxy) s.load(Glarch.class, gid);
+ assertTrue( "versioned collection after", g.getVersion()==4 );
+ s.delete(g);
+ s.flush();
+ assertTrue( s.find("from java.lang.Object").size()==0 );
+ s.connection().commit();
+ s.close();
+ }
+
+ /*public void testVersionedSubcollections() throws Exception {
+ Session s = sessionsopenSession();
+
+ assertTrue( !s.iterate("from Fee fee").hasNext() );
+
+ GlarchProxy g = new Glarch();
+ s.save(g);
+ String gid = (String) s.getIdentifier(g);
+ HashMap map = new HashMap();
+ HashSet subSet = new HashSet();
+ map.put("xxx", subSet);
+ subSet.add("foo"); subSet.add("bar");
+ g.setStringSets(map);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = sessionsopenSession();
+ g = (GlarchProxy) s.load(Glarch.class, gid);
+ assertTrue( g.getVersion()==1, "versioned collection before" );
+ //System.out.println( g.getStringSets().get("xxx") );
+ assertTrue( ( (Set) g.getStringSets().get("xxx") ).size()==2, "versioned collection before" );
+ ( (Set) g.getStringSets().get("xxx") ).add("baz");
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = sessionsopenSession();
+ g = (GlarchProxy) s.load(Glarch.class, gid);
+ assertTrue( g.getVersion()==2, "versioned collection after" );
+ //System.out.println( g.getStringSets().get("xxx") );
+ assertTrue( ( (Set) g.getStringSets().get("xxx") ).size()==3, "versioned collection after" );
+ s.delete(g);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ //with components! (note: this also tests some stuff testPersistCollections misses)
+ s = sessionsopenSession();
+ g = new Glarch(); //(GlarchProxy) s.create(Glarch.class);
+ List list = new ArrayList();
+ Date[] dates = new Date[] { null, null, new Date(), new Date(0) };
+ list.add(null);
+ list.add( new FooComponent("foo", 69, dates, new FooComponent("bar", 96, null, null, new Fee() ) ) );
+ g.setFooComponents(list);
+ String EIGHT_CHARS = "abcdefgh";
+ s.save(g, EIGHT_CHARS + EIGHT_CHARS + EIGHT_CHARS + EIGHT_CHARS);
+ gid = (String) s.getIdentifier(g);
+ assertTrue( s.iterate("from Fee fee").hasNext() );
+ g.getFooComponents().add( new FooComponent("bar", 96, null, null) );
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = sessionsopenSession();
+ g = (GlarchProxy) s.load(Glarch.class, gid);
+ assertTrue( g.getVersion()==2, "versioned collection before" );
+ ( (FooComponent) g.getFooComponents().get(1) ).getImportantDates()[0] = new Date(123567890);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = sessionsopenSession();
+ g = (GlarchProxy) s.load(Glarch.class, gid);
+ assertTrue( g.getVersion()==3, "versioned collection after" );
+ ( (FooComponent) g.getFooComponents().get(1) ).getSubcomponent().setName("new name");
+ assertTrue( ( (FooComponent) g.getFooComponents().get(1) ).getImportantDates()[0]!=null, "versioned collection after" );
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = sessionsopenSession();
+ g = (GlarchProxy) s.load(Glarch.class, gid);
+ assertTrue( ( (FooComponent) g.getFooComponents().get(1) ).getSubcomponent().getName().equals("new name"), "versioned collection after" );
+ assertTrue( g.getVersion()==4, "versioned collection after" );
+ s.delete(g);
+ assertTrue( !s.iterate("from Fee fee").hasNext() );
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+
+ }*/
+
+ public void testRecursiveLoad() throws Exception {
+ //Non polymorphic class (there is an implementation optimization
+ //being tested here)
+ Session s = openSession();
+ Transaction txn = s.beginTransaction();
+ GlarchProxy last = new Glarch();
+ s.save(last);
+ last.setOrder( (short) 0 );
+ for (int i=0; i<5; i++) {
+ GlarchProxy next = new Glarch();
+ s.save(next);
+ last.setNext(next);
+ last = next;
+ last.setOrder( (short) (i+1) );
+ }
+ Iterator iter = s.iterate("from Glarch g");
+ while ( iter.hasNext() ) {
+ iter.next();
+ }
+ List list = s.find("from Glarch g");
+ assertTrue( "recursive find", list.size()==6 );
+ txn.commit();
+ s.close();
+
+ s = openSession();
+ txn = s.beginTransaction();
+ list = s.find("from Glarch g");
+ assertTrue( "recursive iter", list.size()==6 );
+ list = s.find("from Glarch g where g.next is not null");
+ assertTrue( "recursive iter", list.size()==5 );
+ txn.commit();
+ s.close();
+
+ s = openSession();
+ txn = s.beginTransaction();
+ iter = s.iterate("from Glarch g order by g.order asc");
+ while ( iter.hasNext() ) {
+ GlarchProxy g = (GlarchProxy) iter.next();
+ assertTrue( "not null", g!=null );
+ iter.remove();
+ }
+ txn.commit();
+ s.close();
+
+ //Same thing but using polymorphic class (no optimisation possible):
+ s = openSession();
+ txn = s.beginTransaction();
+ FooProxy flast = new Bar();
+ s.save(flast);
+ flast.setString( "foo0" );
+ for (int i=0; i<5; i++) {
+ FooProxy foo = new Bar();
+ s.save(foo);
+ flast.setFoo(foo);
+ flast = flast.getFoo();
+ flast.setString( "foo" + (i+1) );
+ }
+ iter = s.iterate("from Foo foo");
+ while ( iter.hasNext() ) {
+ iter.next();
+ }
+ list = s.find("from Foo foo");
+ assertTrue( "recursive find", list.size()==6 );
+ txn.commit();
+ s.close();
+
+ s = openSession();
+ txn = s.beginTransaction();
+ list = s.find("from Foo foo");
+ assertTrue( "recursive iter", list.size()==6 );
+ iter = list.iterator();
+ while ( iter.hasNext() ) {
+ assertTrue( "polymorphic recursive load", iter.next() instanceof BarProxy );
+ }
+ txn.commit();
+ s.close();
+
+ s = openSession();
+ txn = s.beginTransaction();
+ iter = s.iterate("from Foo foo order by foo.string asc");
+ while ( iter.hasNext() ) {
+ BarProxy bar = (BarProxy) iter.next();
+ assertTrue( "not null", bar!=null );
+ iter.remove();
+ }
+ txn.commit();
+ s.close();
+ }
+
+ public void testScrollableIterator() throws Exception {
+ if ( getDialect() instanceof DB2Dialect || getDialect() instanceof OracleDialect || getDialect() instanceof SybaseDialect || getDialect() instanceof HSQLDialect ) {
+ Session s = openSession();
+ Transaction txn = s.beginTransaction();
+ s.save( new Foo() );
+ s.save( new Foo() );
+ s.save( new Foo() );
+ s.save( new Bar() );
+ Query query = s.createQuery("select f, f.integer from Foo f");
+ assertTrue( query.getReturnTypes().length==2 );
+ ScrollableResults iter = query.scroll();
+ assertTrue( iter.next() );
+ assertTrue( iter.scroll(1) );
+ FooProxy f2 = (FooProxy) iter.get()[0];
+ assertTrue( f2!=null );
+ assertTrue( iter.scroll(-1) );
+ Object f1 = iter.get(0);
+ iter.next();
+ assertTrue( f1!=null && iter.get(0)==f2 );
+ iter.getInteger(1);
+
+ assertTrue( !iter.scroll(100) );
+ assertTrue( iter.first() );
+ assertTrue( iter.scroll(3) );
+ Object f4 = iter.get(0);
+ assertTrue( f4!=null );
+ assertTrue( !iter.next() );
+ assertTrue( iter.first() );
+ assertTrue( iter.get(0)==f1 );
+ assertTrue( iter.last() );
+ assertTrue( iter.get(0)==f4 );
+ assertTrue( iter.previous() );
+ txn.commit();
+ s.close();
+
+ s = openSession();
+ txn = s.beginTransaction();
+ query = s.createQuery("select f, f.integer from Foo f");
+ assertTrue( query.getReturnTypes().length==2 );
+ iter = query.scroll();
+ assertTrue( iter.next() );
+ assertTrue( iter.scroll(1) );
+ f2 = (FooProxy) iter.get()[0];
+ assertTrue( f2!=null );
+ assertTrue( f2.getString()!=null && f2.getComponent().getImportantDates().length > 0 );
+ assertTrue( iter.scroll(-1) );
+ f1 = iter.get(0);
+ iter.next();
+ assertTrue( f1!=null && iter.get(0)==f2 );
+ iter.getInteger(1);
+
+ assertTrue( !iter.scroll(100) );
+ assertTrue( iter.first() );
+ assertTrue( iter.scroll(3) );
+ f4 = iter.get(0);
+ assertTrue( f4!=null );
+ assertTrue( !iter.next() );
+ assertTrue( iter.first() );
+ assertTrue( iter.get(0)==f1 );
+ assertTrue( iter.last() );
+ assertTrue( iter.get(0)==f4 );
+ assertTrue( iter.previous() );
+ assertTrue( s.delete("from Foo")==4 );
+ s.flush();
+ assertTrue( s.find("from java.lang.Object").size()==0 );
+ txn.commit();
+ s.close();
+ }
+ }
+
+ public void testMultiColumnQueries() throws Exception {
+ Session s = openSession();
+ Transaction txn = s.beginTransaction();
+ Foo foo = new Foo();
+ s.save(foo);
+ Foo foo1 = new Foo();
+ s.save(foo1);
+ foo.setFoo(foo1);
+ List l = s.find("select parent, child from Foo parent, Foo child where parent.foo = child");
+ assertTrue( "multi-column find", l.size()==1 );
+
+ Iterator rs = s.iterate("select count(distinct child.id), count(distinct parent.id) from Foo parent, Foo child where parent.foo = child");
+ Object[] row = (Object[]) rs.next();
+ assertTrue( "multi-column count", ( (Long) row[0] ).intValue()==1 );
+ assertTrue( "multi-column count", ( (Long) row[1] ).intValue()==1 );
+ assertTrue( !rs.hasNext() );
+
+ rs = s.iterate("select child.id, parent.id, child.long from Foo parent, Foo child where parent.foo = child");
+ row = (Object[]) rs.next();
+ assertTrue( "multi-column id", row[0].equals( foo.getFoo().getKey() ) );
+ assertTrue( "multi-column id", row[1].equals( foo.getKey() ) );
+ assertTrue( "multi-column property", row[2].equals( foo.getFoo().getLong() ) );
+ assertTrue( !rs.hasNext() );
+
+ rs = s.iterate("select child.id, parent.id, child.long, child, parent.foo from Foo parent, Foo child where parent.foo = child");
+ row = (Object[]) rs.next();
+ assertTrue(
+ foo.getFoo().getKey().equals( row[0] ) &&
+ foo.getKey().equals( row[1] ) &&
+ foo.getFoo().getLong().equals( row[2] ) &&
+ row[3] == foo.getFoo() &&
+ row[3]==row[4]
+ );
+ assertTrue( !rs.hasNext() );
+
+ row = (Object[]) l.get(0);
+ assertTrue( "multi-column find", row[0]==foo && row[1]==foo.getFoo() );
+ txn.commit();
+ s.close();
+
+ s = openSession();
+ txn = s.beginTransaction();
+ Iterator iter = s.iterate("select parent, child from Foo parent, Foo child where parent.foo = child and parent.string='a string'");
+ int deletions=0;
+ while ( iter.hasNext() ) {
+ Object[] pnc = (Object[]) iter.next();
+ s.delete( pnc[0] );
+ s.delete( pnc[1] );
+ deletions++;
+ }
+ assertTrue("multi-column iterate", deletions==1);
+ txn.commit();
+ s.close();
+ }
+
+ public void testDeleteTransient() throws Exception {
+ Fee fee = new Fee();
+ Fee fee2 = new Fee();
+ fee2.setAnotherFee(fee);
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ s.save(fee);
+ s.save(fee2);
+ s.flush();
+ fee.setCount(123);
+ tx.commit();
+ s.close();
+ s = openSession();
+ tx = s.beginTransaction();
+ s.delete(fee);
+ s.delete(fee2);
+ //foo.setAnotherFee(null);
+ tx.commit();
+ s.close();
+ s = openSession();
+ tx = s.beginTransaction();
+ assertTrue( s.find("from Fee fee").size()==0 );
+ tx.commit();
+ s.close();
+ }
+
+ public void testDeleteUpdatedTransient() throws Exception {
+ Fee fee = new Fee();
+ Fee fee2 = new Fee();
+ fee2.setAnotherFee(fee);
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ s.save(fee);
+ s.save(fee2);
+ s.flush();
+ fee.setCount(123);
+ tx.commit();
+ s.close();
+ s = openSession();
+ tx = s.beginTransaction();
+ s.update(fee);
+ //fee2.setAnotherFee(null);
+ s.update(fee2);
+ s.delete(fee);
+ s.delete(fee2);
+ tx.commit();
+ s.close();
+ s = openSession();
+ tx = s.beginTransaction();
+ assertTrue( s.find("from Fee fee").size()==0 );
+ tx.commit();
+ s.close();
+ }
+
+ public void testUpdateOrder() throws Exception {
+ Session s = openSession();
+ Fee fee1 = new Fee();
+ s.save(fee1);
+ Fee fee2 = new Fee();
+ fee1.setFee(fee2);
+ fee2.setFee(fee1);
+ fee2.setFees( new HashSet() );
+ Fee fee3 = new Fee();
+ fee3.setFee(fee1);
+ fee3.setAnotherFee(fee2);
+ fee2.setAnotherFee(fee3);
+ s.save(fee3);
+ s.save(fee2);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ fee1.setCount(10);
+ fee2.setCount(20);
+ fee3.setCount(30);
+ s.update(fee1);
+ s.update(fee2);
+ s.update(fee3);
+ s.flush();
+ s.delete(fee1);
+ s.delete(fee2);
+ s.delete(fee3);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ Transaction tx = s.beginTransaction();
+ assertTrue( s.find("from Fee fee").size()==0 );
+ tx.commit();
+ s.close();
+ }
+
+ public void testUpdateFromTransient() throws Exception {
+ Session s = openSession();
+ Fee fee1 = new Fee();
+ s.save(fee1);
+ Fee fee2 = new Fee();
+ fee1.setFee(fee2);
+ fee2.setFee(fee1);
+ fee2.setFees( new HashSet() );
+ Fee fee3 = new Fee();
+ fee3.setFee(fee1);
+ fee3.setAnotherFee(fee2);
+ fee2.setAnotherFee(fee3);
+ s.save(fee3);
+ s.save(fee2);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ fee1.setFi("changed");
+ s = openSession();
+ s.saveOrUpdate(fee1);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ Qux q = new Qux("quxxy");
+ q.setTheKey(0);
+ fee1.setQux(q);
+ s = openSession();
+ s.saveOrUpdate(fee1);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+
+ s = openSession();
+ fee1 = (Fee) s.load( Fee.class, fee1.getKey() );
+ assertTrue( "updated from transient", fee1.getFi().equals("changed") );
+ assertTrue( "unsaved value", fee1.getQux()!=null );
+ s.delete( fee1.getQux() );
+ fee1.setQux(null);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ fee2.setFi("CHANGED");
+ fee2.getFees().add("an element");
+ fee1.setFi("changed again");
+ s = openSession();
+ s.saveOrUpdate(fee2);
+ s.update( fee1, fee1.getKey() );
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ Fee fee = new Fee();
+ s.load( fee, fee2.getKey() );
+ fee1 = (Fee) s.load( Fee.class, fee1.getKey() );
+ assertTrue( "updated from transient", fee1.getFi().equals("changed again") );
+ assertTrue( "updated from transient", fee.getFi().equals("CHANGED") );
+ assertTrue( "updated collection", fee.getFees().contains("an element") );
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ fee.getFees().clear();
+ fee.getFees().add("new element");
+ fee1.setFee(null);
+ s = openSession();
+ s.saveOrUpdate(fee);
+ s.saveOrUpdate(fee1);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ s.load( fee, fee.getKey() );
+ assertTrue( "update", fee.getAnotherFee()!=null );
+ assertTrue( "update", fee.getFee()!=null );
+ assertTrue( "update", fee.getAnotherFee().getFee()==fee.getFee() );
+ assertTrue( "updated collection", fee.getFees().contains("new element") );
+ assertTrue( "updated collection", !fee.getFees().contains("an element") );
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ fee.setQux( new Qux("quxy") );
+ s = openSession();
+ s.saveOrUpdate(fee);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ fee.getQux().setStuff("xxx");
+ s = openSession();
+ s.saveOrUpdate(fee);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ s.load( fee, fee.getKey() );
+ assertTrue( "cascade update", fee.getQux()!=null );
+ assertTrue( "cascade update", fee.getQux().getStuff().equals("xxx") );
+ assertTrue( "update", fee.getAnotherFee()!=null );
+ assertTrue( "update", fee.getFee()!=null );
+ assertTrue( "update", fee.getAnotherFee().getFee()==fee.getFee() );
+ fee.getAnotherFee().setAnotherFee(null);
+ s.delete(fee);
+ s.delete("from Fee fee");
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ Transaction tx = s.beginTransaction();
+ assertTrue( s.find("from Fee fee").size()==0 );
+ tx.commit();
+ s.close();
+ }
+
+ public void testArraysOfTimes() throws Exception {
+ Session s = openSession();
+ Baz baz = new Baz() ;
+ s.save(baz);
+ baz.setDefaults();
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+
+ baz.getTimeArray()[2] = new Date(123);
+ baz.getTimeArray()[3] = new java.sql.Time(1234);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ baz = (Baz) s.load( Baz.class, baz.getCode() );
+ s.delete(baz);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testComponents() throws Exception {
+ Session s = openSession();
+ Transaction txn = s.beginTransaction();
+ Foo foo = new Foo();
+ foo.setComponent( new FooComponent("foo", 69, null, new FooComponent("bar", 96, null, null) ) );
+ s.save(foo);
+ foo.getComponent().setName("IFA");
+ txn.commit();
+ s.close();
+
+ foo.setComponent(null);
+
+ s = openSession();
+ txn = s.beginTransaction();
+ s.load( foo, foo.getKey() );
+ assertTrue(
+ "save components",
+ foo.getComponent().getName().equals("IFA") &&
+ foo.getComponent().getSubcomponent().getName().equals("bar")
+ );
+ assertTrue( "cascade save via component", foo.getComponent().getGlarch()!=null);
+ foo.getComponent().getSubcomponent().setName("baz");
+ txn.commit();
+ s.close();
+
+ foo.setComponent(null);
+
+ s = openSession();
+ txn = s.beginTransaction();
+ s.load( foo, foo.getKey() );
+ assertTrue(
+ "update components",
+ foo.getComponent().getName().equals("IFA") &&
+ foo.getComponent().getSubcomponent().getName().equals("baz")
+ );
+ s.delete(foo);
+ txn.commit();
+ s.close();
+
+ s = openSession();
+ txn = s.beginTransaction();
+ foo = new Foo();
+ s.save(foo);
+ foo.setCustom( new String[] { "one", "two" } );
+ assertTrue( s.find("from Foo foo where foo.custom.s1 = 'one'").get(0)==foo );
+ s.delete(foo);
+ txn.commit();
+ s.close();
+
+ }
+
+ public void testNoForeignKeyViolations() throws Exception {
+ Session s = openSession();
+ Glarch g1 = new Glarch();
+ Glarch g2 = new Glarch();
+ g1.setNext(g2);
+ g2.setNext(g1);
+ s.save(g1);
+ s.save(g2);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ List l = s.find("from Glarch g where g.next is not null");
+ s.delete( l.get(0) );
+ s.delete( l.get(1) );
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testLazyCollections() throws Exception {
+ Session s = openSession();
+ Qux q = new Qux();
+ s.save(q);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ q = (Qux) s.load( Qux.class, q.getKey() );
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ System.out.println("Two exceptions are supposed to occur:");
+ boolean ok = false;
+ try {
+ q.getMoreFums().isEmpty();
+ }
+ catch (LazyInitializationException e) {
+ ok = true;
+ }
+ assertTrue( "lazy collection with one-to-many", ok );
+
+ ok = false;
+ try {
+ q.getFums().isEmpty();
+ }
+ catch (LazyInitializationException e) {
+ ok = true;
+ }
+ assertTrue( "lazy collection with many-to-many", ok );
+
+ s = openSession();
+ q = (Qux) s.load( Qux.class, q.getKey() );
+ s.delete(q);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testNewSessionLifecycle() throws Exception {
+ Session s = openSession();
+ Serializable fid = null;
+ try {
+ Foo f = new Foo();
+ s.save(f);
+ fid = s.getIdentifier(f);
+ s.flush();
+ s.connection().commit();
+ }
+ catch (Exception e) {
+ s.connection().rollback();
+ throw e;
+ }
+ finally {
+ s.close();
+ }
+ s = openSession();
+ try {
+ Foo f = new Foo();
+ s.delete(f);
+ s.flush();
+ s.connection().commit();
+ }
+ catch (Exception e) {
+ s.connection().rollback();
+ }
+ finally {
+ s.close();
+ }
+ s = openSession();
+ try {
+ Foo f = (Foo) s.load(Foo.class, fid, LockMode.UPGRADE);
+ s.delete(f);
+ s.flush();
+ s.connection().commit();
+ }
+ catch (Exception e) {
+ s.connection().rollback();
+ throw e;
+ }
+ finally {
+ assertTrue( s.close()==null );
+ }
+ }
+
+ public void testDisconnect() throws Exception {
+ Session s = openSession();
+ Foo foo = new Foo();
+ Foo foo2 = new Foo();
+ s.save(foo);
+ s.save(foo2);
+ foo2.setFoo(foo);
+ s.flush();
+ s.connection().commit();
+ s.disconnect();
+ s.reconnect();
+ s.delete(foo);
+ foo2.setFoo(null);
+ s.flush();
+ s.connection().commit();
+ s.disconnect();
+ s.reconnect();
+ s.delete(foo2);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+
+
+ public void testOrderBy() throws Exception {
+
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ Foo foo = new Foo();
+ s.save(foo);
+ List list = s.find("select foo from Foo foo, Fee fee where foo.dependent = fee order by foo.string desc, foo.component.count asc, fee.id");
+ assertTrue( "order by", list.size()==1 );
+ Foo foo2 = new Foo();
+ s.save(foo2);
+ foo.setFoo(foo2);
+ list = s.find("select foo.foo, foo.dependent from Foo foo order by foo.foo.string desc, foo.component.count asc, foo.dependent.id");
+ assertTrue( "order by", list.size()==1 );
+ list = s.find("select foo from Foo foo order by foo.dependent.id, foo.dependent.fi");
+ assertTrue( "order by", list.size()==2 );
+ s.delete(foo);
+ s.delete(foo2);
+ tx.commit();
+ s.close();
+
+ s = openSession();
+ Many manyB = new Many();
+ s.save(manyB);
+ One oneB = new One();
+ s.save(oneB);
+ oneB.setValue("b");
+ manyB.setOne(oneB);
+ Many manyA = new Many();
+ s.save(manyA);
+ One oneA = new One();
+ s.save(oneA);
+ oneA.setValue("a");
+ manyA.setOne(oneA);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ Iterator it = s.iterate(
+ "SELECT one FROM " +
+ One.class.getName() +
+ " one ORDER BY one.value ASC"
+ );
+ int count = 0;
+ while ( it.hasNext() ) {
+ One one = (One)it.next();
+ switch (count) {
+ case 0:
+ assertTrue("ordering failed", "a".equals(one.getValue()));
+ break;
+ case 1:
+ assertTrue("ordering failed", "b".equals(one.getValue()));
+ break;
+ default:
+ assertTrue("more than two elements", false);
+ break;
+ }
+ count ++;
+ }
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ it = s.iterate(
+ "SELECT many.one FROM " +
+ Many.class.getName() +
+ " many ORDER BY many.one.value ASC, many.one.id"
+ );
+ count = 0;
+ while ( it.hasNext() ) {
+ One one = (One)it.next();
+ switch (count) {
+ case 0:
+ assertTrue("'a' isn't first element", "a".equals(one.getValue()));
+ break;
+ case 1:
+ assertTrue("'b' isn't second element", "b".equals(one.getValue()));
+ break;
+ default:
+ assertTrue("more than two elements", false);
+ break;
+ }
+ count ++;
+ }
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ oneA = (One)s.load(One.class, oneA.getKey());
+ manyA = (Many)s.load(Many.class, manyA.getKey());
+ oneB = (One)s.load(One.class, oneB.getKey());
+ manyB = (Many)s.load(Many.class, manyB.getKey());
+ s.delete(manyA);
+ s.delete(oneA);
+ s.delete(manyB);
+ s.delete(oneB);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testManyToOne() throws Exception {
+ Session s = openSession();
+ One one = new One();
+ s.save(one);
+ one.setValue("yada");
+ Many many = new Many();
+ many.setOne(one);
+ s.save(many);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ one = (One) s.load( One.class, one.getKey() );
+ one.getManies().size();
+ s.connection().commit();
+ s.close();
+
+
+ s = openSession();
+ many = (Many) s.load( Many.class, many.getKey() );
+ assertTrue( "many-to-one assoc", many.getOne()!=null );
+ s.delete( many.getOne() );
+ s.delete(many);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testSaveDelete() throws Exception {
+ Session s = openSession();
+ Foo f = new Foo();
+ s.save(f);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ s.delete( s.load( Foo.class, f.getKey() ) );
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ /*public void testIdNotFound() throws Exception {
+ // The following test is only enabled for MySQL which has no foreign key constraints.
+ // I disabled this test cos it didn't clean up after itself so other tests failed
+ if (db.equals("mysql")) {
+ Session s = sessionsopenSession();
+ Glarch g = (Glarch) s.create(Glarch.class);
+ Glarch g2 = (Glarch) s.create(Glarch.class);
+ g.setNext(g2);
+ Serializable gid = s.getID(g);
+ Serializable g2id = s.getID(g2);
+ s.commit();
+
+ s = sessionsopenSession();
+ g2 = (Glarch) s.load( Glarch.class, g2id );
+ s.delete(g2);
+ s.commit();
+
+ s = sessionsopenSession();
+ boolean ok = false;
+ try {
+ g = (Glarch) s.load( Glarch.class, gid );
+ }
+ catch (HibernateException e) {
+ ok = "id not found or provided object was wrong class".equals(e.getMessage());
+ }
+ catch (java.lang.StackOverflowError soe) {
+ ok = false;
+ }
+ assertTrue( ok, "id not found");
+ s.cancel();
+ }
+ }*/
+
+ public void testProxyArray() throws Exception {
+ Session s = openSession();
+ GlarchProxy g = new Glarch();
+ Glarch g1 = new Glarch();
+ Glarch g2 = new Glarch();
+ g.setProxyArray( new GlarchProxy[] { g1, g2 } );
+ Glarch g3 = new Glarch();
+ s.save(g3);
+ g2.setProxyArray( new GlarchProxy[] {null, g3, g} );
+ Set set = new HashSet();
+ set.add(g1);
+ set.add(g2);
+ g.setProxySet(set);
+ s.save(g);
+ s.save(g1);
+ s.save(g2);
+ Serializable id = s.getIdentifier(g);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ g = (GlarchProxy) s.load(Glarch.class, id);
+ assertTrue( "array of proxies", g.getProxyArray().length==2 );
+ assertTrue( "array of proxies", g.getProxyArray()[0]!=null );
+ assertTrue("deferred load test",g.getProxyArray()[1].getProxyArray()[0]==null );
+ assertTrue("deferred load test",g.getProxyArray()[1].getProxyArray()[2]==g );
+ assertTrue( "set of proxies", g.getProxySet().size()==2 );
+ Iterator iter = s.iterate("from Glarch g");
+ while ( iter.hasNext() ) {
+ iter.next();
+ iter.remove();
+ }
+
+ s.flush();
+ s.connection().commit();
+ s.disconnect();
+ SerializationHelper.deserialize( SerializationHelper.serialize(s) );
+ s.close();
+ }
+
+ public void testCache() throws Exception {
+ Session s = openSession();
+ Immutable im = new Immutable();
+ s.save(im);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ s.load( im, im.getId() );
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ s.load( im, im.getId() );
+ assertTrue(
+ "cached object identity",
+ s.find(
+ "from Immutable im where im = ?",
+ im,
+ Hibernate.entity(Immutable.class)
+ ).get(0)==im &&
+ im == s.load( Immutable.class, im.getId() )
+ );
+ s.connection().createStatement().executeUpdate("delete from immut");
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testFindLoad() throws Exception {
+ Session s = openSession();
+ FooProxy foo = new Foo();
+ s.save(foo);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ foo = (FooProxy) s.find("from Foo foo").get(0);
+ FooProxy foo2 = (FooProxy) s.load( Foo.class, foo.getKey() );
+ assertTrue("find returns same object as load", foo==foo2);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ foo2 = (FooProxy) s.load( Foo.class, foo.getKey() );
+ foo = (FooProxy) s.find("from Foo foo").get(0);
+ assertTrue("find returns same object as load", foo==foo2);
+ s.delete("from Foo foo");
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testRefresh() throws Exception {
+ Session s = openSession();
+ Foo foo = new Foo();
+ s.save(foo);
+ s.flush();
+ s.connection().createStatement().executeUpdate("update "+getDialect().openQuote()+"foos"+getDialect().closeQuote()+" set long_ = -3");
+ s.refresh(foo);
+ assertTrue( foo.getLong().longValue()==-3l );
+ assertTrue( s.getCurrentLockMode(foo)==LockMode.READ );
+ s.refresh(foo, LockMode.UPGRADE);
+ if ( getDialect().supportsOuterJoinForUpdate() ) {
+ assertTrue( s.getCurrentLockMode(foo)==LockMode.UPGRADE );
+ }
+ s.delete(foo);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testAutoFlush() throws Exception {
+ Session s = openSession();
+ Transaction txn = s.beginTransaction();
+ FooProxy foo = new Foo();
+ s.save(foo);
+ assertTrue( "autoflush create", s.find("from Foo foo").size()==1 );
+ foo.setChar( new Character('X') );
+ assertTrue( "autoflush update", s.find("from Foo foo where foo.char='X'").size()==1 );
+ txn.commit();
+ s.close();
+
+ s = openSession();
+ txn = s.beginTransaction();
+ foo = (FooProxy) s.load( Foo.class, foo.getKey() );
+ //s.update( new Foo(), foo.getKey() );
+ //assertTrue( s.find("from Foo foo where not foo.char='X'").size()==1, "autoflush update" );
+ if ( !(getDialect() instanceof MySQLDialect) && !(getDialect() instanceof HSQLDialect) && !(getDialect() instanceof PointbaseDialect) ) {
+ foo.setBytes( "osama".getBytes() );
+ assertTrue( "autoflush collection update", s.find("from Foo foo where 111 in elements(foo.bytes)").size()==1 );
+ foo.getBytes()[0] = 69;
+ assertTrue( "autoflush collection update", s.find("from Foo foo where 69 in elements(foo.bytes)").size()==1 );
+ }
+ s.delete(foo);
+ assertTrue( "autoflush delete", s.find("from Foo foo").size()==0 );
+ txn.commit();
+ s.close();
+ }
+
+ public void testVeto() throws Exception {
+ Session s = openSession();
+ Vetoer v = new Vetoer();
+ s.save(v); Serializable id = s.save(v);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ s.update(v, id); s.update(v, id);
+ s.delete(v); s.delete(v);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testSerializableType() throws Exception {
+ Session s = openSession();
+ Vetoer v = new Vetoer();
+ v.setStrings( new String[] { "foo", "bar", "baz" } );
+ s.save(v); Serializable id = s.save(v);
+ v.getStrings()[1] = "osama";
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ v = (Vetoer) s.load(Vetoer.class, id);
+ assertTrue( "serializable type", v.getStrings()[1].equals("osama") );
+ s.delete(v); s.delete(v);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testAutoFlushCollections() throws Exception {
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ Baz baz = new Baz();
+ baz.setDefaults();
+ s.save(baz);
+ tx.commit();
+ s.close();
+
+ s = openSession();
+ tx = s.beginTransaction();
+ baz = (Baz) s.load(Baz.class, baz.getCode());
+ baz.getStringArray()[0] = "bark";
+ Iterator i = s.iterate("select elements(baz.stringArray) from Baz baz");
+ boolean found = false;
+ while ( i.hasNext() ) {
+ if ( "bark".equals( i.next() ) ) found = true;
+ }
+ assertTrue(found);
+ baz.setStringArray(null);
+ i = s.iterate("select distinct elements(baz.stringArray) from Baz baz");
+ assertTrue( !i.hasNext() );
+ baz.setStringArray( new String[] { "foo", "bar" } );
+ i = s.iterate("select elements(baz.stringArray) from Baz baz");
+ assertTrue( i.hasNext() );
+
+ Foo foo = new Foo();
+ s.save(foo);
+ s.flush();
+ baz.setFooArray( new Foo[] {foo} );
+
+ i = s.iterate("select foo from Baz baz join baz.fooArray foo");
+ found = false;
+ while ( i.hasNext() ) {
+ if ( foo==i.next() ) found = true;
+ }
+ assertTrue(found);
+
+ baz.getFooArray()[0] = null;
+ i = s.iterate("select foo from Baz baz join baz.fooArray foo");
+ assertTrue( !i.hasNext() );
+ baz.getFooArray()[0] = foo;
+ i = s.iterate("select elements(baz.fooArray) from Baz baz");
+ assertTrue( i.hasNext() );
+
+ if ( !(getDialect() instanceof MySQLDialect) && !(getDialect() instanceof HSQLDialect) && !(getDialect() instanceof InterbaseDialect) && !(getDialect() instanceof PointbaseDialect) && !(getDialect() instanceof SAPDBDialect) ) {
+ baz.getFooArray()[0] = null;
+ i = s.iterate(
+ "from Baz baz where ? in elements(baz.fooArray)",
+ foo, Hibernate.entity(Foo.class)
+ );
+ assertTrue( !i.hasNext() );
+ baz.getFooArray()[0] = foo;
+ i = s.iterate(
+ "select foo from Foo foo where foo in "
+ + "(select elt from Baz baz join baz.fooArray elt)"
+ );
+ assertTrue( i.hasNext() );
+ }
+ s.delete(foo);
+ s.delete(baz);
+ tx.commit();
+ s.close();
+
+ }
+
+ public void testUserProvidedConnection() throws Exception {
+ ConnectionProvider dcp = new DriverManagerConnectionProvider();
+ dcp.configure( Environment.getProperties() );
+ Session s = getSessions().openSession( dcp.getConnection() );
+ Transaction tx = s.beginTransaction();
+ s.find("from Fo");
+ tx.commit();
+ Connection c = s.disconnect();
+ assertTrue( c!=null );
+ s.reconnect(c);
+ tx = s.beginTransaction();
+ s.find("from Fo");
+ tx.commit();
+ assertTrue( s.close()==c );
+ c.close();
+ }
+
+ public void testCachedCollection() throws Exception {
+ Session s = openSession();
+ Baz baz = new Baz();
+ baz.setDefaults();
+ s.save(baz);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ baz = (Baz) s.load( Baz.class, baz.getCode() );
+ ( (FooComponent) baz.getTopComponents().get(0) ).setCount(99);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ baz = (Baz) s.load( Baz.class, baz.getCode() );
+ assertTrue( ( (FooComponent) baz.getTopComponents().get(0) ).getCount()==99 );
+ s.delete(baz);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testComplicatedQuery() throws Exception {
+ Session s = openSession();
+ Transaction txn = s.beginTransaction();
+ Foo foo = new Foo();
+ Serializable id = s.save(foo);
+ assertTrue(id!=null);
+ Qux q = new Qux("q");
+ foo.getDependent().setQux(q);
+ s.save(q);
+ q.getFoo().setString("foo2");
+ //s.flush();
+ //s.connection().commit();
+ assertTrue(
+ s.iterate("from Foo foo where foo.dependent.qux.foo.string = 'foo2'").hasNext()
+ );
+ s.delete(foo);
+ txn.commit();
+ s.close();
+ }
+
+ public void testLoadAfterDelete() throws Exception {
+ Session s = openSession();
+ Foo foo = new Foo();
+ Serializable id = s.save(foo);
+ s.flush();
+ s.delete(foo);
+ boolean err=false;
+ try {
+ s.load(Foo.class, id);
+ }
+ catch (ObjectNotFoundException ode) {
+ err=true;
+ }
+ assertTrue(err);
+ s.flush();
+ err=false;
+ try {
+ ( (FooProxy) s.load(Foo.class, id) ).getBool();
+ }
+ catch (ObjectNotFoundException onfe) {
+ err=true;
+ }
+ assertTrue(err);
+ Fo fo = Fo.newFo();
+ id = new FumTest("").fumKey("abc"); //yuck!!
+ s.save(fo, id);
+ s.flush();
+ s.delete(fo);
+ err=false;
+ try {
+ s.load(Fo.class, id);
+ }
+ catch (ObjectNotFoundException ode) {
+ err=true;
+ }
+ assertTrue(err);
+ s.flush();
+ err=false;
+ try {
+ s.load(Fo.class, id);
+ }
+ catch (ObjectNotFoundException onfe) {
+ err=true;
+ }
+ assertTrue(err);
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testObjectType() throws Exception {
+ Session s = openSession();
+ GlarchProxy g = new Glarch();
+ Foo foo = new Foo();
+ g.setAny(foo);
+ Serializable gid = s.save(g);
+ s.save(foo);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ g = (GlarchProxy) s.load(Glarch.class, gid);
+ assertTrue( g.getAny()!=null && g.getAny() instanceof FooProxy );
+ s.delete( g.getAny() );
+ s.delete(g);
+ //s.delete( g.getAny() );
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+
+ public void testAny() throws Exception {
+ Session s = openSession();
+ One one = new One();
+ BarProxy foo = new Bar();
+ foo.setObject(one);
+ //Serializable oid = s.save(one);
+ Serializable fid = s.save(foo);
+ Serializable oid = one.getKey();
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ assertTrue( s.find(
+ "from Bar bar where bar.object.id = ? and bar.object.class = ?",
+ new Object[] { oid, new Character('O') },
+ new Type[] { Hibernate.LONG, Hibernate.CHARACTER }
+ ).size()==1 );
+ assertTrue( s.find(
+ "select one from One one, Bar bar where bar.object.id = one.id and bar.object.class = 'O'"
+ ).size()==1 );
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ foo = (BarProxy) s.load(Foo.class, fid);
+ assertTrue( foo.getObject()!=null && foo.getObject() instanceof One && s.getIdentifier( foo.getObject() ).equals(oid) );
+ //s.delete( foo.getObject() );
+ s.delete(foo);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testEmbeddedCompositeID() throws Exception {
+ Session s = openSession();
+ Location l = new Location();
+ l.setCountryCode("AU");
+ l.setDescription("foo bar");
+ l.setLocale( Locale.getDefault() );
+ l.setStreetName("Brunswick Rd");
+ l.setStreetNumber(300);
+ l.setCity("Melbourne");
+ s.save(l);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ s.setFlushMode(FlushMode.NEVER);
+ l = (Location) s.find("from Location l where l.countryCode = 'AU' and l.description='foo bar'").get(0);
+ assertTrue( l.getCountryCode().equals("AU") );
+ assertTrue( l.getCity().equals("Melbourne") );
+ assertTrue( l.getLocale().equals( Locale.getDefault() ) );
+ assertTrue( s.createCriteria(Location.class).add( Expression.eq( "streetNumber", new Integer(300) ) ).list().size()==1 );
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ l.setDescription("sick're");
+ s.update(l);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ l = new Location();
+ l.setCountryCode("AU");
+ l.setDescription("foo bar");
+ l.setLocale(Locale.ENGLISH);
+ l.setStreetName("Brunswick Rd");
+ l.setStreetNumber(300);
+ l.setCity("Melbourne");
+ assertTrue( l==s.load(Location.class, l) );
+ assertTrue( l.getLocale().equals( Locale.getDefault() ) );
+ s.delete(l);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testAutosaveChildren() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Baz baz = new Baz();
+ Set bars = new HashSet();
+ baz.setCascadingBars(bars);
+ s.save(baz);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ baz = (Baz) s.load( Baz.class, baz.getCode() );
+ baz.getCascadingBars().add( new Bar() );
+ baz.getCascadingBars().add( new Bar() );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ baz = (Baz) s.load( Baz.class, baz.getCode() );
+ assertTrue( baz.getCascadingBars().size()==2 );
+ assertTrue( baz.getCascadingBars().iterator().next()!=null );
+ baz.getCascadingBars().clear(); //test all-delete-orphan;
+ s.flush();
+ assertTrue( s.find("from Bar bar").size()==0 );
+ s.delete(baz);
+ t.commit();
+ s.close();
+ }
+
+ public void testOrphanDelete() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Baz baz = new Baz();
+ Set bars = new HashSet();
+ baz.setCascadingBars(bars);
+ bars.add( new Bar() );
+ bars.add( new Bar() );
+ bars.add( new Bar() );
+ bars.add( new Bar() );
+ s.save(baz);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ baz = (Baz) s.load( Baz.class, baz.getCode() );
+ bars = baz.getCascadingBars();
+ assertEquals( 4, bars.size() );
+ bars.remove( bars.iterator().next() );
+ assertEquals( 3, s.find("From Bar bar").size() );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ baz = (Baz) s.load( Baz.class, baz.getCode() );
+ bars = baz.getCascadingBars();
+ assertEquals( 3, bars.size() );
+ bars.remove( bars.iterator().next() );
+ s.delete(baz);
+ bars.remove( bars.iterator().next() );
+ assertEquals( 0, s.find("From Bar bar").size() );
+ t.commit();
+ s.close();
+
+ }
+
+ public void testTransientOrphanDelete() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Baz baz = new Baz();
+ Set bars = new HashSet();
+ baz.setCascadingBars(bars);
+ bars.add( new Bar() );
+ bars.add( new Bar() );
+ bars.add( new Bar() );
+ List foos = new ArrayList();
+ foos.add( new Foo() );
+ foos.add( new Foo() );
+ baz.setFooBag(foos);
+ s.save(baz);
+ Iterator i = new JoinedIterator( new Iterator[] {foos.iterator(), bars.iterator()} );
+ while ( i.hasNext() ) {
+ FooComponent cmp = ( (Foo) i.next() ).getComponent();
+ s.delete( cmp.getGlarch() );
+ cmp.setGlarch(null);
+ }
+ t.commit();
+ s.close();
+
+ bars.remove( bars.iterator().next() );
+ foos.remove(1);
+ s = openSession();
+ t = s.beginTransaction();
+ s.update(baz);
+ assertEquals( 2, s.find("From Bar bar").size() );
+ assertEquals( 3, s.find("From Foo foo").size() );
+ t.commit();
+ s.close();
+
+ foos.remove(0);
+ s = openSession();
+ t = s.beginTransaction();
+ s.update(baz);
+ bars.remove( bars.iterator().next() );
+ assertEquals( 1, s.find("From Foo foo").size() );
+ s.delete(baz);
+ //s.flush();
+ assertEquals( 0, s.find("From Foo foo").size() );
+ t.commit();
+ s.close();
+
+ }
+
+ public void testProxiesInCollections() throws Exception {
+ Session s = openSession();
+ Baz baz = new Baz();
+ Bar bar = new Bar();
+ Bar bar2 = new Bar();
+ s.save(bar);
+ Serializable bar2id = s.save(bar2);
+ baz.setFooArray( new Foo[] { bar, bar2 } );
+ HashSet set = new HashSet();
+ bar = new Bar();
+ s.save(bar);
+ set.add(bar);
+ baz.setFooSet(set);
+ set = new HashSet();
+ set.add( new Bar() );
+ set.add( new Bar() );
+ baz.setCascadingBars(set);
+ ArrayList list = new ArrayList();
+ list.add( new Foo() );
+ baz.setFooBag(list);
+ Serializable id = s.save(baz);
+ Serializable bid = ( (Bar) baz.getCascadingBars().iterator().next() ).getKey();
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ BarProxy barprox = (BarProxy) s.load(Bar.class, bid);
+ BarProxy bar2prox = (BarProxy) s.load(Bar.class, bar2id);
+ assertTrue(bar2prox instanceof HibernateProxy);
+ assertTrue(barprox instanceof HibernateProxy);
+ baz = (Baz) s.load(Baz.class, id);
+ Iterator i = baz.getCascadingBars().iterator();
+ BarProxy b1 = (BarProxy) i.next();
+ BarProxy b2 = (BarProxy) i.next();
+ assertTrue( ( b1==barprox && !(b2 instanceof HibernateProxy) ) || ( b2==barprox && !(b1 instanceof HibernateProxy) ) ); //one-to-many
+ assertTrue( baz.getFooArray()[0] instanceof HibernateProxy ); //many-to-many
+ assertTrue( baz.getFooArray()[1]==bar2prox );
+ if ( !isOuterJoinFetchingDisabled() ) assertTrue( !(baz.getFooBag().iterator().next() instanceof HibernateProxy) ); //many-to-many outer-join="true"
+ assertTrue( !(baz.getFooSet().iterator().next() instanceof HibernateProxy) ); //one-to-many
+ s.delete("from Baz");
+ s.delete("from Foo");
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testService() throws Exception {
+ HibernateService hs = new HibernateService();
+ hs.setJndiName("SessionFactory");
+ hs.setMapResources("net/sf/hibernate/test/Simple.hbm.xml, net/sf/hibernate/test/Blobber.hbm.xml");
+ hs.setShowSqlEnabled("true");
+ hs.start();
+ hs.stop();
+ hs.setProperty("foo", "bar");
+ hs.start();
+ hs.stop();
+ }
+
+ public void testPSCache() throws Exception {
+ Session s = openSession();
+ Transaction txn = s.beginTransaction();
+ for ( int i=0; i<10; i++ ) s.save( new Foo() );
+ Query q = s.createQuery("from Foo");
+ q.setMaxResults(2);
+ q.setFirstResult(5);
+ assertTrue( q.list().size()==2 );
+ q = s.createQuery("from Foo");
+ assertTrue( q.list().size()==10 );
+ assertTrue( q.list().size()==10 );
+ q.setMaxResults(3);
+ q.setFirstResult(3);
+ assertTrue( q.list().size()==3 );
+ q = s.createQuery("from Foo");
+ assertTrue( q.list().size()==10 );
+ txn.commit();
+ s.close();
+
+ s = openSession();
+ txn = s.beginTransaction();
+ q = s.createQuery("from Foo");
+ assertTrue( q.list().size()==10 );
+ q.setMaxResults(5);
+ assertTrue( q.list().size()==5 );
+ s.delete("from Foo");
+ txn.commit();
+ s.close();
+
+ }
+
+ public void testForCertain() throws Exception {
+ Glarch g = new Glarch();
+ Glarch g2 = new Glarch();
+ List set = new ArrayList();
+ set.add("foo");
+ g2.setStrings(set);
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Serializable gid = (Serializable) s.save(g);
+ Serializable g2id = (Serializable) s.save(g2);
+ t.commit();
+ assertTrue( g.getVersion()==0 );
+ assertTrue( g2.getVersion()==0 );
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ g = (Glarch) s.get(Glarch.class, gid);
+ g2 = (Glarch) s.get(Glarch.class, g2id);
+ assertTrue( g2.getStrings().size()==1 );
+ s.delete(g);
+ s.delete(g2);
+ t.commit();
+ s.close();
+
+ }
+
+ public void testBagMultipleElements() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Baz baz = new Baz();
+ baz.setBag( new ArrayList() );
+ baz.setByteBag( new ArrayList() );
+ s.save(baz);
+ baz.getBag().add("foo");
+ baz.getBag().add("bar");
+ baz.getByteBag().add( "foo".getBytes() );
+ baz.getByteBag().add( "bar".getBytes() );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ //put in cache
+ baz = (Baz) s.get( Baz.class, baz.getCode() );
+ assertTrue( baz.getBag().size()==2 );
+ assertTrue( baz.getByteBag().size()==2 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ baz = (Baz) s.get( Baz.class, baz.getCode() );
+ assertTrue( baz.getBag().size()==2 );
+ assertTrue( baz.getByteBag().size()==2 );
+ baz.getBag().remove("bar");
+ baz.getBag().add("foo");
+ baz.getByteBag().add( "bar".getBytes() );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ baz = (Baz) s.get( Baz.class, baz.getCode() );
+ assertTrue( baz.getBag().size()==2 );
+ assertTrue( baz.getByteBag().size()==3 );
+ s.delete(baz);
+ t.commit();
+ s.close();
+ }
+
+ public void testWierdSession() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Serializable id = s.save( new Foo() );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ s.setFlushMode(FlushMode.NEVER);
+ t = s.beginTransaction();
+ Foo foo = (Foo) s.get(Foo.class, id);
+ t.commit();
+ s.disconnect();
+
+ s.reconnect();
+ t = s.beginTransaction();
+ s.flush();
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ foo = (Foo) s.get(Foo.class, id);
+ s.delete(foo);
+ t.commit();
+ s.close();
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/FooComponent.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/FooComponent.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/FooComponent.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,133 @@
+//$Id: FooComponent.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+import java.util.Date;
+
+public class FooComponent implements Serializable {
+
+ int count;
+ String name;
+ Date[] importantDates;
+ FooComponent subcomponent;
+ Fee fee = new Fee();
+ GlarchProxy glarch;
+ private FooProxy parent;
+ private Baz baz;
+
+ public boolean equals(Object that) {
+ FooComponent fc = (FooComponent) that;
+ return count==fc.count;
+ }
+
+ public int hashCode() {
+ return count;
+ }
+
+ public String toString() {
+ String result = "FooComponent: " + name + "=" + count;
+ result+="; dates=[";
+ if ( importantDates!=null) {
+ for ( int i=0; i<importantDates.length; i++ ) {
+ result+=(i==0 ?"":", ") + importantDates[i];
+ }
+ }
+ result+="]";
+ if ( subcomponent!=null ) {
+ result+= " (" + subcomponent + ")";
+ }
+ return result;
+ }
+
+ public FooComponent() {}
+
+ FooComponent(String name, int count, Date[] dates, FooComponent subcomponent) {
+ this.name = name;
+ this.count = count;
+ this.importantDates = dates;
+ this.subcomponent = subcomponent;
+ }
+
+ FooComponent(String name, int count, Date[] dates, FooComponent subcomponent, Fee fee) {
+ this.name = name;
+ this.count = count;
+ this.importantDates = dates;
+ this.subcomponent = subcomponent;
+ this.fee = fee;
+ }
+
+ public int getCount() {
+ return count;
+ }
+ public void setCount(int count) {
+ this.count = count;
+ }
+
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Date[] getImportantDates() {
+ return importantDates;
+ }
+ public void setImportantDates(Date[] importantDates) {
+ this.importantDates = importantDates;
+ }
+
+ public FooComponent getSubcomponent() {
+ return subcomponent;
+ }
+ public void setSubcomponent(FooComponent subcomponent) {
+ this.subcomponent = subcomponent;
+ }
+
+ private String getNull() {
+ return null;
+ }
+ private void setNull(String str) throws Exception {
+ if (str!=null) throw new Exception("null component property");
+ }
+ public Fee getFee() {
+ return fee;
+ }
+
+ public void setFee(Fee fee) {
+ this.fee = fee;
+ }
+
+ public GlarchProxy getGlarch() {
+ return glarch;
+ }
+
+ public void setGlarch(GlarchProxy glarch) {
+ this.glarch = glarch;
+ }
+
+ public FooProxy getParent() {
+ return parent;
+ }
+
+ public void setParent(FooProxy parent) {
+ //if (parent==null) throw new RuntimeException("null parent set");
+ this.parent = parent;
+ }
+
+ public Baz getBaz() {
+ return baz;
+ }
+
+ public void setBaz(Baz baz) {
+ this.baz = baz;
+ }
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/FooProxy.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/FooProxy.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/FooProxy.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,64 @@
+package org.hibernate.test.legacy;
+
+public interface FooProxy {
+ public void setNullComponent(FooComponent arg0) throws Exception;
+ public FooComponent getNullComponent();
+ public void setComponent(FooComponent arg0);
+ public FooComponent getComponent();
+ public void setCustom(String[] arg0);
+ public String[] getCustom();
+ public void setBinary(byte[] arg0);
+ public byte[] getBinary();
+ public void setNullBlob(java.io.Serializable arg0);
+ public java.io.Serializable getNullBlob();
+ public void setBlob(java.io.Serializable arg0);
+ public java.io.Serializable getBlob();
+ public void setYesno(boolean arg0);
+ public boolean getYesno();
+ public boolean equalsFoo(Foo arg0);
+ public void disconnect();
+ public String toString();
+ public void setByte(Byte arg0);
+ public Byte getByte();
+ public void setNull(Integer arg0);
+ public Integer getNull();
+ public void setInt(int arg0);
+ public int getInt();
+ public void setBool(boolean arg0);
+ public boolean getBool();
+ public void setZero(float arg0);
+ public float getZero();
+ public void setBytes(byte[] arg0);
+ public byte[] getBytes();
+ public void setBoolean(Boolean arg0);
+ public Boolean getBoolean();
+ public void setDouble(Double arg0);
+ public Double getDouble();
+ public void setFloat(Float arg0);
+ public Float getFloat();
+ public void setShort(Short arg0);
+ public Short getShort();
+ public Character getChar();
+ public void setChar(Character _char);
+ public Long getLong();
+ public void setInteger(Integer arg0);
+ public Integer getInteger();
+ public void setTimestamp(java.util.Date arg0);
+ public java.util.Date getTimestamp();
+ public void setDate(java.util.Date arg0);
+ public java.util.Date getDate();
+ public void setString(String arg0);
+ public String getString();
+ public void setFoo(FooProxy arg0);
+ public FooProxy getFoo();
+ public void setKey(String arg0);
+ public String getKey();
+ public Fee getDependent();
+ public float getFormula();
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Fum.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Fum.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Fum.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,71 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping default-lazy="false">
+
+ <class name="org.hibernate.test.legacy.Fum" dynamic-update="true">
+ <!--cache-->
+ <composite-id name="id" unsaved-value="any">
+ <meta attribute="fum">fum</meta>
+ <key-property name="string">
+ <column name="string_" length="10"/>
+ </key-property>
+ <key-property name="short" column="short_"/>
+ <key-property name="date" column="date_" type="date"/>
+ </composite-id>
+ <version name="vid" type="short" access="field"/>
+ <!--version name="lastUpdated" type="calendar"/-->
+ <property name="fum" not-null="true"/>
+ <property name="TString"/>
+ <many-to-one name="fo">
+ <column name="fo_string" length="10"/>
+ <column name="fo_short"/>
+ <column name="fo_date"/>
+ </many-to-one>
+ <set name="friends">
+ <key>
+ <column name="fr_string_" length="10"/>
+ <column name="fr_short_"/>
+ <column name="fr_date_"/>
+ </key>
+ <one-to-many class="org.hibernate.test.legacy.Fum"/>
+ </set>
+ <array name="quxArray">
+ <key>
+ <column name="fum_str_" length="10"/>
+ <column name="fum_sho_"/>
+ <column name="fum_dat_"/>
+ </key>
+ <index column="i"/>
+ <one-to-many class="org.hibernate.test.legacy.Qux"/>
+ </array>
+ <property name="dupe"
+ column="short_"
+ insert="false"
+ update="false"
+ not-null="true"/>
+ <component name="mapComponent">
+ <property name="count" column="count_"/>
+ <map name="fummap" lazy="true">
+ <key>
+ <column name="fum_str_" length="10"/>
+ <column name="fum_sho_"/>
+ <column name="fum_dat_"/>
+ </key>
+ <index column="fummapindex" type="string"/>
+ <one-to-many class="org.hibernate.test.legacy.Fum"/>
+ </map>
+ <map name="stringmap" outer-join="true">
+ <key>
+ <column name="fum_str_" length="10"/>
+ <column name="fum_sho_"/>
+ <column name="fum_dat_"/>
+ </key>
+ <index column="mapindex" type="string"/>
+ <element column="mapelement" type="string"/>
+ </map>
+ </component>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Fum.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Fum.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Fum.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,187 @@
+//$Id: Fum.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+import java.sql.SQLException;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.hibernate.CallbackException;
+import org.hibernate.HibernateException;
+import org.hibernate.Session;
+import org.hibernate.classic.Lifecycle;
+
+public class Fum implements Lifecycle, Serializable {
+ private String fum;
+ private FumCompositeID id;
+ private Fum fo;
+ private Qux[] quxArray;
+ private Set friends;
+ private Calendar lastUpdated;
+ private String tString;
+ private short vid;
+ private short dupe;
+ private MapComponent mapComponent = new MapComponent();
+
+ public Fum() {}
+ public Fum(FumCompositeID id) throws SQLException, HibernateException {
+ this.id = id;
+ friends = new HashSet();
+ FumCompositeID fid = new FumCompositeID();
+ fid.setDate( new Date() );
+ fid.setShort( (short) ( id.short_ + 33 ) );
+ fid.setString( id.string_ + "dd" );
+ Fum f = new Fum();
+ f.id = fid;
+ f.fum="FRIEND";
+ friends.add(f);
+ }
+ public String getFum() {
+ return fum;
+ }
+ public void setFum(String fum) {
+ this.fum = fum;
+ }
+
+ public FumCompositeID getId() {
+ return id;
+ }
+ private void setId(FumCompositeID id) {
+ this.id = id;
+ }
+ public Fum getFo() {
+ return fo;
+ }
+ public void setFo(Fum fo) {
+ this.fo = fo;
+ }
+
+ public Qux[] getQuxArray() {
+ return quxArray;
+ }
+ public void setQuxArray(Qux[] quxArray) {
+ this.quxArray = quxArray;
+ }
+
+ public Set getFriends() {
+ return friends;
+ }
+
+ public void setFriends(Set friends) {
+ this.friends = friends;
+ }
+
+
+ public boolean onDelete(Session s) throws CallbackException {
+ if (friends==null) return false;
+ try {
+ Iterator iter = friends.iterator();
+ while ( iter.hasNext() ) {
+ s.delete( iter.next() );
+ }
+ }
+ catch (Exception e) {
+ throw new CallbackException(e);
+ }
+ return false;
+ }
+
+
+ public void onLoad(Session s, Serializable id) {
+ }
+
+
+ public boolean onSave(Session s) throws CallbackException {
+ if (friends==null) return false;
+ try {
+ Iterator iter = friends.iterator();
+ while ( iter.hasNext() ) {
+ s.save( iter.next() );
+ }
+ }
+ catch (Exception e) {
+ throw new CallbackException(e);
+ }
+ return false;
+ }
+
+
+ public boolean onUpdate(Session s) throws CallbackException {
+ return false;
+ }
+
+ public Calendar getLastUpdated() {
+ return lastUpdated;
+ }
+
+ public void setLastUpdated(Calendar calendar) {
+ lastUpdated = calendar;
+ }
+
+ public String getTString() {
+ return tString;
+ }
+
+ public void setTString(String string) {
+ tString = string;
+ }
+
+ public short getDupe() {
+ return dupe;
+ }
+
+ public void setDupe(short s) {
+ dupe = s;
+ }
+
+ public static final class MapComponent implements Serializable {
+ private Map fummap = new HashMap();
+ private Map stringmap = new HashMap();
+ private int count;
+ public Map getFummap() {
+ return fummap;
+ }
+
+ public void setFummap(Map mapcomponent) {
+ this.fummap = mapcomponent;
+ }
+
+ public int getCount() {
+ return count;
+ }
+
+ public void setCount(int count) {
+ this.count = count;
+ }
+
+ public Map getStringmap() {
+ return stringmap;
+ }
+
+ public void setStringmap(Map stringmap) {
+ this.stringmap = stringmap;
+ }
+
+ }
+
+ public MapComponent getMapComponent() {
+ return mapComponent;
+ }
+
+ public void setMapComponent(MapComponent mapComponent) {
+ this.mapComponent = mapComponent;
+ }
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/FumCompositeID.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/FumCompositeID.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/FumCompositeID.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,40 @@
+//$Id: FumCompositeID.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+public class FumCompositeID implements java.io.Serializable {
+ String string_;
+ java.util.Date date_;
+ short short_;
+ public boolean equals(Object other) {
+ FumCompositeID that = (FumCompositeID) other;
+ return this.string_.equals(that.string_) && this.short_==that.short_;
+ }
+ public int hashCode() {
+ return string_.hashCode();
+ }
+ public String getString() {
+ return string_;
+ }
+ public void setString(String string_) {
+ this.string_ = string_;
+ }
+ public java.util.Date getDate() {
+ return date_;
+ }
+ public void setDate(java.util.Date date_) {
+ this.date_ = date_;
+ }
+ public short getShort() {
+ return short_;
+ }
+ public void setShort(short short_) {
+ this.short_ = short_;
+ }
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/FumTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/FumTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/FumTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,882 @@
+//$Id: FumTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.legacy;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import junit.framework.Test;
+import junit.textui.TestRunner;
+
+import org.hibernate.Criteria;
+import org.hibernate.FetchMode;
+import org.hibernate.FlushMode;
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.LockMode;
+import org.hibernate.Query;
+import org.hibernate.Transaction;
+import org.hibernate.classic.Session;
+import org.hibernate.criterion.Expression;
+import org.hibernate.criterion.MatchMode;
+import org.hibernate.dialect.HSQLDialect;
+import org.hibernate.dialect.MckoiDialect;
+import org.hibernate.dialect.MySQLDialect;
+import org.hibernate.dialect.PointbaseDialect;
+import org.hibernate.dialect.TimesTenDialect;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.transform.Transformers;
+import org.hibernate.type.DateType;
+import org.hibernate.type.EntityType;
+import org.hibernate.type.StringType;
+import org.hibernate.type.Type;
+
+public class FumTest extends LegacyTestCase {
+
+ private static short fumKeyShort = 1;
+
+ public FumTest(String arg) {
+ super(arg);
+ }
+
+ public String[] getMappings() {
+ return new String[] {
+ "legacy/FooBar.hbm.xml",
+ "legacy/Baz.hbm.xml",
+ "legacy/Qux.hbm.xml",
+ "legacy/Glarch.hbm.xml",
+ "legacy/Fum.hbm.xml",
+ "legacy/Fumm.hbm.xml",
+ "legacy/Fo.hbm.xml",
+ "legacy/One.hbm.xml",
+ "legacy/Many.hbm.xml",
+ "legacy/Immutable.hbm.xml",
+ "legacy/Fee.hbm.xml",
+ "legacy/Vetoer.hbm.xml",
+ "legacy/Holder.hbm.xml",
+ "legacy/Location.hbm.xml",
+ "legacy/Stuff.hbm.xml",
+ "legacy/Container.hbm.xml",
+ "legacy/Simple.hbm.xml",
+ "legacy/Middle.hbm.xml"
+ };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( FumTest.class );
+ }
+
+ public static void main(String[] args) throws Exception {
+ TestRunner.run( suite() );
+ }
+
+ public void testQuery() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.createQuery("from Fum fum where fum.fo.id.string = 'x'").list();
+ t.commit();
+ s.close();
+ }
+
+ public void testCriteriaCollection() throws Exception {
+ Session s = openSession();
+ Fum fum = new Fum( fumKey("fum") );
+ fum.setFum("a value");
+ fum.getMapComponent().getFummap().put("self", fum);
+ fum.getMapComponent().getStringmap().put("string", "a staring");
+ fum.getMapComponent().getStringmap().put("string2", "a notha staring");
+ fum.getMapComponent().setCount(1);
+ s.save(fum);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ Fum b = (Fum) s.createCriteria(Fum.class).add(
+ Expression.in("fum", new String[] { "a value", "no value" } )
+ )
+ .uniqueResult();
+ //assertTrue( Hibernate.isInitialized( b.getMapComponent().getFummap() ) );
+ assertTrue( Hibernate.isInitialized( b.getMapComponent().getStringmap() ) );
+ assertTrue( b.getMapComponent().getFummap().size()==1 );
+ assertTrue( b.getMapComponent().getStringmap().size()==2 );
+ s.delete(b);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testCriteria() throws Exception {
+ Session s = openSession();
+ Transaction txn = s.beginTransaction();
+ Fum fum = new Fum( fumKey("fum") );
+ fum.setFo( new Fum( fumKey("fo") ) );
+ fum.setFum("fo fee fi");
+ fum.getFo().setFum("stuff");
+ Fum fr = new Fum( fumKey("fr") );
+ fr.setFum("goo");
+ Fum fr2 = new Fum( fumKey("fr2") );
+ fr2.setFum("soo");
+ fum.setFriends( new HashSet() );
+ fum.getFriends().add(fr);
+ fum.getFriends().add(fr2);
+ s.save(fr);
+ s.save(fr2);
+ s.save( fum.getFo() );
+ s.save(fum);
+
+ Criteria base = s.createCriteria(Fum.class)
+ .add( Expression.like("fum", "f", MatchMode.START) );
+ base.createCriteria("fo")
+ .add( Expression.isNotNull("fum") );
+ base.createCriteria("friends")
+ .add( Expression.like("fum", "g%") );
+ List list = base.list();
+ assertTrue( list.size()==1 && list.get(0)==fum );
+
+ base = s.createCriteria(Fum.class)
+ .add( Expression.like("fum", "f%") )
+ .setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP);
+ base.createCriteria("fo", "fo")
+ .add( Expression.isNotNull("fum") );
+ base.createCriteria("friends", "fum")
+ .add( Expression.like("fum", "g", MatchMode.START) );
+ Map map = (Map) base.uniqueResult();
+
+ assertTrue(
+ map.get("this")==fum &&
+ map.get("fo")==fum.getFo() &&
+ fum.getFriends().contains( map.get("fum") ) &&
+ map.size()==3
+ );
+
+ base = s.createCriteria(Fum.class)
+ .add( Expression.like("fum", "f%") )
+ .setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP)
+ .setFetchMode("friends", FetchMode.EAGER);
+ base.createCriteria("fo", "fo")
+ .add( Expression.eq( "fum", fum.getFo().getFum() ) );
+ map = (Map) base.list().get(0);
+
+ assertTrue(
+ map.get("this")==fum &&
+ map.get("fo")==fum.getFo() &&
+ map.size()==2
+ );
+
+ list = s.createCriteria(Fum.class)
+ .createAlias("friends", "fr")
+ .createAlias("fo", "fo")
+ .add( Expression.like("fum", "f%") )
+ .add( Expression.isNotNull("fo") )
+ .add( Expression.isNotNull("fo.fum") )
+ .add( Expression.like("fr.fum", "g%") )
+ .add( Expression.eqProperty("fr.id.short", "id.short") )
+ .list();
+ assertTrue( list.size()==1 && list.get(0)==fum );
+ txn.commit();
+ s.close();
+
+ s = openSession();
+ txn = s.beginTransaction();
+ base = s.createCriteria(Fum.class)
+ .add( Expression.like("fum", "f%") );
+ base.createCriteria("fo")
+ .add( Expression.isNotNull("fum") );
+ base.createCriteria("friends")
+ .add( Expression.like("fum", "g%") );
+ fum = (Fum) base.list().get(0);
+ assertTrue( fum.getFriends().size()==2 );
+ s.delete(fum);
+ s.delete( fum.getFo() );
+ Iterator iter = fum.getFriends().iterator();
+ while ( iter.hasNext() ) s.delete( iter.next() );
+ txn.commit();
+ s.close();
+ }
+
+ static public class ABean {
+ public Fum fum;
+ public Fum fo;
+ public Fum getFo() {
+ return fo;
+ }
+ public void setFo(Fum fo) {
+ this.fo = fo;
+ }
+ public Fum getFum() {
+ return fum;
+ }
+ public void setFum(Fum fum) {
+ this.fum = fum;
+ }
+ }
+
+ public void testBeanResultTransformer() throws HibernateException, SQLException {
+
+ Session s = openSession();
+ Transaction transaction = s.beginTransaction();
+ Fum fum = new Fum( fumKey("fum") );
+ fum.setFo( new Fum( fumKey("fo") ) );
+ fum.setFum("fo fee fi");
+ fum.getFo().setFum("stuff");
+ Fum fr = new Fum( fumKey("fr") );
+ fr.setFum("goo");
+ Fum fr2 = new Fum( fumKey("fr2") );
+ fr2.setFum("soo");
+ fum.setFriends( new HashSet() );
+ fum.getFriends().add(fr);
+ fum.getFriends().add(fr2);
+ s.save(fr);
+ s.save(fr2);
+ s.save( fum.getFo() );
+ s.save(fum);
+
+ Criteria test = s.createCriteria(Fum.class, "xam")
+ .createCriteria("fo", "fo")
+ .setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP);
+
+ Map fc = (Map) test.list().get(0);
+ assertNotNull(fc.get("xam"));
+
+ Criteria base = s.createCriteria(Fum.class, "fum")
+ .add( Expression.like("fum", "f%") )
+ .setResultTransformer(Transformers.aliasToBean(ABean.class))
+ .setFetchMode("friends", FetchMode.JOIN);
+ base.createCriteria("fo", "fo")
+ .add( Expression.eq( "fum", fum.getFo().getFum() ) );
+ ABean map = (ABean) base.list().get(0);
+
+ assertTrue(
+ map.getFum()==fum &&
+ map.getFo()==fum.getFo() );
+
+ s.delete(fr);
+ s.delete(fr2);
+ s.delete(fum);
+ s.delete(fum.getFo());
+ s.flush();
+ transaction.commit();
+ s.close();
+
+ }
+
+
+ public void testListIdentifiers() throws Exception {
+ Session s = openSession();
+ Transaction txn = s.beginTransaction();
+ Fum fum = new Fum( fumKey("fum") );
+ fum.setFum("fo fee fi");
+ s.save(fum);
+ fum = new Fum( fumKey("fi") );
+ fum.setFum("fee fi fo");
+ s.save(fum);
+ List list = s.find("select fum.id from Fum as fum where not fum.fum='FRIEND'");
+ assertTrue( "list identifiers", list.size()==2);
+ Iterator iter = s.iterate("select fum.id from Fum fum where not fum.fum='FRIEND'");
+ int i=0;
+ while ( iter.hasNext() ) {
+ assertTrue( "iterate identifiers", iter.next() instanceof FumCompositeID);
+ i++;
+ }
+ assertTrue(i==2);
+
+ s.delete( s.load(Fum.class, (Serializable) list.get(0) ) );
+ s.delete( s.load(Fum.class, (Serializable) list.get(1) ) );
+ txn.commit();
+ s.close();
+ }
+
+
+ public FumCompositeID fumKey(String str) {
+
+ return fumKey(str,false);
+ }
+
+ private FumCompositeID fumKey(String str, boolean aCompositeQueryTest) {
+ FumCompositeID id = new FumCompositeID();
+ if ( Dialect.getDialect() instanceof MckoiDialect ) {
+ GregorianCalendar now = new GregorianCalendar();
+ GregorianCalendar cal = new GregorianCalendar(
+ now.get(java.util.Calendar.YEAR),
+ now.get(java.util.Calendar.MONTH),
+ now.get(java.util.Calendar.DATE)
+ );
+ id.setDate( cal.getTime() );
+ }
+ else {
+ id.setDate( new Date() );
+ }
+ id.setString( new String(str) );
+
+ if (aCompositeQueryTest) {
+ id.setShort( fumKeyShort++ );
+ }
+ else {
+ id.setShort( (short) 12 );
+ }
+
+ return id;
+ }
+
+ public void testCompositeID() throws Exception {
+ Session s = openSession();
+ Transaction txn = s.beginTransaction();
+ Fum fum = new Fum( fumKey("fum") );
+ fum.setFum("fee fi fo");
+ s.save(fum);
+ assertTrue( "load by composite key", fum==s.load( Fum.class, fumKey("fum") ) );
+ txn.commit();
+ s.close();
+
+ s = openSession();
+ txn = s.beginTransaction();
+ fum = (Fum) s.load( Fum.class, fumKey("fum"), LockMode.UPGRADE );
+ assertTrue( "load by composite key", fum!=null );
+
+ Fum fum2 = new Fum( fumKey("fi") );
+ fum2.setFum("fee fo fi");
+ fum.setFo(fum2);
+ s.save(fum2);
+ assertTrue(
+ "find composite keyed objects",
+ s.find("from Fum fum where not fum.fum='FRIEND'").size()==2
+ );
+ assertTrue(
+ "find composite keyed object",
+ s.find("select fum from Fum fum where fum.fum='fee fi fo'").get(0)==fum
+ );
+ fum.setFo(null);
+ txn.commit();
+ s.close();
+
+ s = openSession();
+ txn = s.beginTransaction();
+ Iterator iter = s.iterate("from Fum fum where not fum.fum='FRIEND'");
+ int i = 0;
+ while ( iter.hasNext() ) {
+ fum = (Fum) iter.next();
+ //iter.remove();
+ s.delete(fum);
+ i++;
+ }
+ assertTrue( "iterate on composite key", i==2 );
+ txn.commit();
+ s.close();
+ }
+
+ public void testCompositeIDOneToOne() throws Exception {
+ Session s = openSession();
+ Transaction txn = s.beginTransaction();
+ Fum fum = new Fum( fumKey("fum") );
+ fum.setFum("fee fi fo");
+ //s.save(fum);
+ Fumm fumm = new Fumm();
+ fumm.setFum(fum);
+ s.save(fumm);
+ txn.commit();
+ s.close();
+
+ s = openSession();
+ txn = s.beginTransaction();
+ fumm = (Fumm) s.load( Fumm.class, fumKey("fum") );
+ //s.delete( fumm.getFum() );
+ s.delete(fumm);
+ txn.commit();
+ s.close();
+ }
+
+ public void testCompositeIDQuery() throws Exception {
+ Session s = openSession();
+ Fum fee = new Fum( fumKey("fee",true) );
+ fee.setFum("fee");
+ s.save(fee);
+ Fum fi = new Fum( fumKey("fi",true) );
+ fi.setFum("fi");
+ short fiShort = fi.getId().getShort();
+ s.save(fi);
+ Fum fo = new Fum( fumKey("fo",true) );
+ fo.setFum("fo");
+ s.save(fo);
+ Fum fum = new Fum( fumKey("fum",true) );
+ fum.setFum("fum");
+ s.save(fum);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ // Try to find the Fum object "fo" that we inserted searching by the string in the id
+ List vList = s.find("from Fum fum where fum.id.string='fo'" );
+ assertTrue( "find by composite key query (find fo object)", vList.size() == 1 );
+ fum = (Fum)vList.get(0);
+ assertTrue( "find by composite key query (check fo object)", fum.getId().getString().equals("fo") );
+
+ // Try to find the Fum object "fi" that we inserted searching by the date in the id
+ vList = s.find("from Fum fum where fum.id.short = ?",new Short(fiShort),Hibernate.SHORT);
+ assertTrue( "find by composite key query (find fi object)", vList.size() == 1 );
+ fi = (Fum)vList.get(0);
+ assertTrue( "find by composite key query (check fi object)", fi.getId().getString().equals("fi") );
+
+ // Make sure we can return all of the objects by searching by the date id
+ assertTrue(
+ "find by composite key query with arguments",
+ s.find("from Fum fum where fum.id.date <= ? and not fum.fum='FRIEND'",new Date(),Hibernate.DATE).size()==4
+ );
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ assertTrue(
+ s.iterate("select fum.id.short, fum.id.date, fum.id.string from Fum fum").hasNext()
+ );
+ assertTrue(
+ s.iterate("select fum.id from Fum fum").hasNext()
+ );
+ Query qu = s.createQuery("select fum.fum, fum , fum.fum, fum.id.date from Fum fum");
+ Type[] types = qu.getReturnTypes();
+ assertTrue(types.length==4);
+ for ( int k=0; k<types.length; k++) {
+ assertTrue( types[k]!=null );
+ }
+ assertTrue(types[0] instanceof StringType);
+ assertTrue(types[1] instanceof EntityType);
+ assertTrue(types[2] instanceof StringType);
+ assertTrue(types[3] instanceof DateType);
+ Iterator iter = qu.iterate();
+ int j = 0;
+ while ( iter.hasNext() ) {
+ j++;
+ assertTrue( ( (Object[]) iter.next() )[1] instanceof Fum );
+ }
+ assertTrue( "iterate on composite key", j==8 );
+
+ fum = (Fum) s.load( Fum.class, fum.getId() );
+ s.filter( fum.getQuxArray(), "where this.foo is null" );
+ s.filter( fum.getQuxArray(), "where this.foo.id = ?", "fooid", Hibernate.STRING );
+ Query f = s.createFilter( fum.getQuxArray(), "where this.foo.id = :fooId" );
+ f.setString("fooId", "abc");
+ assertFalse( f.iterate().hasNext() );
+
+ iter = s.iterate("from Fum fum where not fum.fum='FRIEND'");
+ int i = 0;
+ while ( iter.hasNext() ) {
+ fum = (Fum) iter.next();
+ //iter.remove();
+ s.delete(fum);
+ i++;
+ }
+ assertTrue( "iterate on composite key", i==4 );
+ s.flush();
+
+ s.iterate("from Fum fu, Fum fo where fu.fo.id.string = fo.id.string and fo.fum is not null");
+
+ s.find("from Fumm f1 inner join f1.fum f2");
+
+ s.connection().commit();
+ s.close();
+ }
+
+
+ public void testCompositeIDCollections() throws Exception {
+ Session s = openSession();
+ Fum fum1 = new Fum( fumKey("fum1") );
+ Fum fum2 = new Fum( fumKey("fum2") );
+ fum1.setFum("fee fo fi");
+ fum2.setFum("fee fo fi");
+ s.save(fum1);
+ s.save(fum2);
+ Qux q = new Qux();
+ s.save(q);
+ Set set = new HashSet();
+ List list = new ArrayList();
+ set.add(fum1); set.add(fum2);
+ list.add(fum1);
+ q.setFums(set);
+ q.setMoreFums(list);
+ fum1.setQuxArray( new Qux[] {q} );
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ q = (Qux) s.load( Qux.class, q.getKey() );
+ assertTrue( "collection of fums", q.getFums().size()==2 );
+ assertTrue( "collection of fums", q.getMoreFums().size()==1 );
+ assertTrue( "unkeyed composite id collection", ( (Fum) q.getMoreFums().get(0) ).getQuxArray()[0]==q );
+ Iterator iter = q.getFums().iterator();
+ iter.hasNext();
+ Fum f = (Fum) iter.next();
+ s.delete(f);
+ iter.hasNext();
+ f = (Fum) iter.next();
+ s.delete(f);
+ s.delete(q);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+
+ public void testDeleteOwner() throws Exception {
+ Session s = openSession();
+ Qux q = new Qux();
+ s.save(q);
+ Fum f1 = new Fum( fumKey("f1") );
+ Fum f2 = new Fum( fumKey("f2") );
+ Set set = new HashSet();
+ set.add(f1);
+ set.add(f2);
+ List list = new LinkedList();
+ list.add(f1);
+ list.add(f2);
+ f1.setFum("f1");
+ f2.setFum("f2");
+ q.setFums(set);
+ q.setMoreFums(list);
+ s.save(f1);
+ s.save(f2);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ q = (Qux) s.load( Qux.class, q.getKey(), LockMode.UPGRADE );
+ s.lock( q, LockMode.UPGRADE );
+ s.delete(q);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ list = s.find("from Fum fum where not fum.fum='FRIEND'");
+ assertTrue( "deleted owner", list.size()==2 );
+ s.lock( list.get(0), LockMode.UPGRADE );
+ s.lock( list.get(1), LockMode.UPGRADE );
+ Iterator iter = list.iterator();
+ while ( iter.hasNext() ) {
+ s.delete( iter.next() );
+ }
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+
+ public void testCompositeIDs() throws Exception {
+ Session s = openSession();
+ Fo fo = Fo.newFo();
+ Properties props = new Properties();
+ props.setProperty("foo", "bar");
+ props.setProperty("bar", "foo");
+ fo.setSerial(props);
+ fo.setBuf( "abcdefghij1`23%$*^*$*\n\t".getBytes() );
+ s.save( fo, fumKey("an instance of fo") );
+ s.flush();
+ props.setProperty("x", "y");
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ fo = (Fo) s.load( Fo.class, fumKey("an instance of fo") );
+ props = (Properties) fo.getSerial();
+ assertTrue( props.getProperty("foo").equals("bar") );
+ //assertTrue( props.contains("x") );
+ assertTrue( props.getProperty("x").equals("y") );
+ assertTrue( fo.getBuf()[0]=='a' );
+ fo.getBuf()[1]=(byte)126;
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ fo = (Fo) s.load( Fo.class, fumKey("an instance of fo") );
+ assertTrue( fo.getBuf()[1]==126 );
+ assertTrue(
+ s.iterate("from Fo fo where fo.id.string like 'an instance of fo'").next()==fo
+ );
+ s.delete(fo);
+ s.flush();
+ try {
+ s.save( Fo.newFo() );
+ assertTrue(false);
+ }
+ catch (Exception e) {
+ //System.out.println( e.getMessage() );
+ }
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testKeyManyToOne() throws Exception {
+ Session s = openSession();
+ Inner sup = new Inner();
+ InnerKey sid = new InnerKey();
+ sup.setDudu("dudu");
+ sid.setAkey("a");
+ sid.setBkey("b");
+ sup.setId(sid);
+ Middle m = new Middle();
+ MiddleKey mid = new MiddleKey();
+ mid.setOne("one");
+ mid.setTwo("two");
+ mid.setSup(sup);
+ m.setId(mid);
+ m.setBla("bla");
+ Outer d = new Outer();
+ OuterKey did = new OuterKey();
+ did.setMaster(m);
+ did.setDetailId("detail");
+ d.setId(did);
+ d.setBubu("bubu");
+ s.save(sup);
+ s.save(m);
+ s.save(d);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ Inner in = (Inner) s.find("from Inner").get(0);
+ assertTrue( in.getMiddles().size()==1 );
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ assertTrue( s.find("from Inner _inner join _inner.middles middle").size()==1 );
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ d = (Outer) s.load(Outer.class, did);
+ assertTrue( d.getId().getMaster().getId().getSup().getDudu().equals("dudu") );
+ s.delete(d);
+ s.delete( d.getId().getMaster() );
+ s.save( d.getId().getMaster() );
+ s.save(d);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ d = (Outer) s.find("from Outer o where o.id.detailId = ?", d.getId().getDetailId(), Hibernate.STRING ).get(0);
+ s.find("from Outer o where o.id.master.id.sup.dudu is not null");
+ s.find("from Outer o where o.id.master.id.sup.id.akey is not null");
+ s.find("from Inner i where i.backOut.id.master.id.sup.id.akey = i.id.bkey");
+ List l = s.find("select o.id.master.id.sup.dudu from Outer o where o.id.master.id.sup.dudu is not null");
+ assertTrue(l.size()==1);
+ l = s.find("select o.id.master.id.sup.id.akey from Outer o where o.id.master.id.sup.id.akey is not null");
+ assertTrue(l.size()==1);
+ s.find("select i.backOut.id.master.id.sup.id.akey from Inner i where i.backOut.id.master.id.sup.id.akey = i.id.bkey");
+ s.find("from Outer o where o.id.master.bla = ''");
+ s.find("from Outer o where o.id.master.id.one = ''");
+ s.find("from Inner inn where inn.id.bkey is not null and inn.backOut.id.master.id.sup.id.akey > 'a'");
+ s.find("from Outer as o left join o.id.master m left join m.id.sup where o.bubu is not null");
+ s.find("from Outer as o left join o.id.master.id.sup s where o.bubu is not null");
+ s.find("from Outer as o left join o.id.master m left join o.id.master.id.sup s where o.bubu is not null");
+ s.delete(d);
+ s.delete( d.getId().getMaster() );
+ s.delete( d.getId().getMaster().getId().getSup() );
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testCompositeKeyPathExpressions() throws Exception {
+ Session s = openSession();
+ s.find("select fum1.fo from Fum fum1 where fum1.fo.fum is not null");
+ s.find("from Fum fum1 where fum1.fo.fum is not null order by fum1.fo.fum");
+ if ( !(getDialect() instanceof MySQLDialect) && !(getDialect() instanceof HSQLDialect) && !(getDialect() instanceof MckoiDialect) && !(getDialect() instanceof PointbaseDialect) ) {
+ s.find("from Fum fum1 where exists elements(fum1.friends)");
+ if(!(getDialect() instanceof TimesTenDialect)) { // can't execute because TimesTen can't do subqueries combined with aggreations
+ s.find("from Fum fum1 where size(fum1.friends) = 0");
+ }
+ }
+ s.find("select elements(fum1.friends) from Fum fum1");
+ s.find("from Fum fum1, fr in elements( fum1.friends )");
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testUnflushedSessionSerialization() throws Exception {
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Test insertions across serializations
+ Session s = getSessions().openSession();
+ s.setFlushMode(FlushMode.NEVER);
+
+ Simple simple = new Simple();
+ simple.setAddress("123 Main St. Anytown USA");
+ simple.setCount(1);
+ simple.setDate( new Date() );
+ simple.setName("My UnflushedSessionSerialization Simple");
+ simple.setPay( new Float(5000) );
+ s.save( simple, new Long(10) );
+
+ // Now, try to serialize session without flushing...
+ s.disconnect();
+ Session s2 = spoofSerialization(s);
+ s.close();
+ s = s2;
+ s.reconnect();
+
+ simple = (Simple) s.load( Simple.class, new Long(10) );
+ Simple other = new Simple();
+ other.init();
+ s.save( other, new Long(11) );
+
+ simple.setOther(other);
+ s.flush();
+
+ s.connection().commit();
+ s.close();
+ Simple check = simple;
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Test updates across serializations
+ s = getSessions().openSession();
+ s.setFlushMode(FlushMode.NEVER);
+
+ simple = (Simple) s.get( Simple.class, new Long(10) );
+ assertTrue("Not same parent instances", check.getName().equals( simple.getName() ) );
+ assertTrue("Not same child instances", check.getOther().getName().equals( other.getName() ) );
+
+ simple.setName("My updated name");
+
+ s.disconnect();
+ s2 = spoofSerialization(s);
+ s.close();
+ s = s2;
+ s.reconnect();
+ s.flush();
+
+ s.connection().commit();
+ s.close();
+ check = simple;
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Test deletions across serializations
+ s = getSessions().openSession();
+ s.setFlushMode(FlushMode.NEVER);
+
+ simple = (Simple) s.get( Simple.class, new Long(10) );
+ assertTrue("Not same parent instances", check.getName().equals( simple.getName() ) );
+ assertTrue("Not same child instances", check.getOther().getName().equals( other.getName() ) );
+
+ // Now, lets delete across serialization...
+ s.delete(simple);
+
+ s.disconnect();
+ s2 = spoofSerialization(s);
+ s.close();
+ s = s2;
+ s.reconnect();
+ s.flush();
+
+ s.connection().commit();
+ s.close();
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Test collection actions across serializations
+ s = getSessions().openSession();
+ s.setFlushMode(FlushMode.NEVER);
+
+ Fum fum = new Fum( fumKey("uss-fum") );
+ fum.setFo( new Fum( fumKey("uss-fo") ) );
+ fum.setFum("fo fee fi");
+ fum.getFo().setFum("stuff");
+ Fum fr = new Fum( fumKey("uss-fr") );
+ fr.setFum("goo");
+ Fum fr2 = new Fum( fumKey("uss-fr2") );
+ fr2.setFum("soo");
+ fum.setFriends( new HashSet() );
+ fum.getFriends().add(fr);
+ fum.getFriends().add(fr2);
+ s.save(fr);
+ s.save(fr2);
+ s.save( fum.getFo() );
+ s.save(fum);
+
+ s.disconnect();
+ s2 = spoofSerialization(s);
+ s.close();
+ s = s2;
+ s.reconnect();
+ s.flush();
+
+ s.connection().commit();
+ s.close();
+
+ s = getSessions().openSession();
+ s.setFlushMode(FlushMode.NEVER);
+ fum = (Fum) s.load( Fum.class, fum.getId() );
+
+ assertTrue("the Fum.friends did not get saved", fum.getFriends().size() == 2);
+
+ fum.setFriends(null);
+ s.disconnect();
+ s2 = spoofSerialization(s);
+ s.close();
+
+ s = s2;
+ s.reconnect();
+ s.flush();
+
+ s.connection().commit();
+ s.close();
+
+ s = getSessions().openSession();
+ s.setFlushMode(FlushMode.NEVER);
+ fum = (Fum) s.load( Fum.class, fum.getId() );
+ assertTrue("the Fum.friends is not empty", fum.getFriends() == null || fum.getFriends().size() == 0);
+ s.connection().commit();
+ s.close();
+ }
+
+ private Session spoofSerialization(Session session) throws IOException {
+ try {
+ // Serialize the incoming out to memory
+ ByteArrayOutputStream serBaOut = new ByteArrayOutputStream();
+ ObjectOutputStream serOut = new ObjectOutputStream(serBaOut);
+
+ serOut.writeObject(session);
+
+ // Now, re-constitute the model from memory
+ ByteArrayInputStream serBaIn =
+ new ByteArrayInputStream(serBaOut.toByteArray());
+ ObjectInputStream serIn = new ObjectInputStream(serBaIn);
+
+ Session outgoing = (Session) serIn.readObject();
+
+ return outgoing;
+ }
+ catch (ClassNotFoundException cnfe) {
+ throw new IOException("Unable to locate class on reconstruction");
+ }
+ }
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Fumm.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Fumm.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Fumm.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping default-lazy="false">
+
+ <class name="org.hibernate.test.legacy.Fumm">
+ <composite-id name="id">
+ <key-property name="string">
+ <column name="string_" length="10"/>
+ </key-property>
+ <key-property name="short" column="short_"/>
+ <key-property name="date" column="date_" type="date"/>
+ </composite-id>
+ <property name="locale"/>
+ <one-to-one name="fum" cascade="all" constrained="true"/>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Fumm.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Fumm.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Fumm.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,59 @@
+//$Id: Fumm.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.util.Locale;
+
+public class Fumm {
+
+ private Locale locale;
+ private Fum fum;
+
+ public FumCompositeID getId() {
+ return fum.getId();
+ }
+ public void setId(FumCompositeID id) {
+ }
+
+ public Fumm() {
+ super();
+ }
+
+ /**
+ * Returns the fum.
+ * @return Fum
+ */
+ public Fum getFum() {
+ return fum;
+ }
+
+ /**
+ * Returns the locale.
+ * @return Locale
+ */
+ public Locale getLocale() {
+ return locale;
+ }
+
+ /**
+ * Sets the fum.
+ * @param fum The fum to set
+ */
+ public void setFum(Fum fum) {
+ this.fum = fum;
+ }
+
+ /**
+ * Sets the locale.
+ * @param locale The locale to set
+ */
+ public void setLocale(Locale locale) {
+ this.locale = locale;
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Glarch.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Glarch.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Glarch.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,65 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping default-lazy="false" package="org.hibernate.test.legacy">
+
+ <class name="Glarch"
+ table="`glarchez`"
+ proxy="GlarchProxy"
+ dynamic-update="true">
+
+ <!--cache-->
+ <id type="string" column="tha_key" length="32">
+ <generator class="uuid"/>
+ </id>
+ <version name="version"/>
+ <property name="name" column="namecvbnmasdf"/>
+ <!-- <property name="currency"/> -->
+ <many-to-one name="next" column="next_" class="Glarch"/>
+ <property name="order" column="order_"/>
+ <dynamic-component name="dynaBean">
+ <property name="foo" type="string"/>
+ <property name="bar" type="integer"/>
+ </dynamic-component>
+ <property name="x"/>
+ <list name="strings">
+ <key column="glarch_key"/>
+ <index column="`indx_`"/>
+ <element type="string" column="`tha_stryng`"/>
+ </list>
+ <list name="fooComponents" lazy="true" cascade="all">
+ <key column="glarch_key"/>
+ <index column="tha_indecks"/>
+ <composite-element class="FooComponent">
+ <property name="name" column="name_"/>
+ <property name="count" column="count_"/>
+ <nested-composite-element name="subcomponent" class="FooComponent">
+ <property name="name" column="x_"/>
+ <property name="count" column="y_"/>
+ </nested-composite-element>
+ <many-to-one name="fee" cascade="all" outer-join="true"/>
+ </composite-element>
+ </list>
+ <array name="proxyArray" element-class="GlarchProxy">
+ <key column="array_key"/>
+ <index column="array_indecks"/>
+ <one-to-many class="org.hibernate.test.legacy.Glarch"/>
+ </array>
+ <set name="proxySet">
+ <key column="set_key"/>
+ <one-to-many class="Glarch"/>
+ </set>
+ <property name="immutable" update="false" not-null="true"/>
+ <property name="derivedVersion" insert="false" update="false" column="version"/>
+ <property name="any" type="object">
+ <column name="`any_id_of_object`"/>
+ <column name="`any_class_of_object`"/>
+ </property>
+ <property name="multiple" type="org.hibernate.test.legacy.MultiplicityType">
+ <column name="count_"/>
+ <column name="glarch_"/>
+ </property>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Glarch.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Glarch.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Glarch.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,216 @@
+//$Id: Glarch.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.hibernate.CallbackException;
+import org.hibernate.Session;
+import org.hibernate.classic.Lifecycle;
+
+public class Glarch extends Super implements GlarchProxy, Lifecycle, Named, Serializable {
+
+ private int version;
+ private GlarchProxy next;
+ private short order;
+ private List strings;
+ private Map stringSets;
+ private List fooComponents;
+ private GlarchProxy[] proxyArray;
+ private Set proxySet;
+ //private Currency currency = Currency.getInstance( Locale.getDefault() );
+ private transient Map dynaBean;
+ private String immutable;
+ private int derivedVersion;
+ private Object any;
+ private int x;
+ private Multiplicity multiple;
+
+ public int getX() {
+ return x;
+ }
+ public void setX(int x) {
+ this.x = x;
+ }
+
+ public int getVersion() {
+ return version;
+ }
+
+ public void setVersion(int version) {
+ this.version = version;
+ }
+
+ public GlarchProxy getNext() {
+ return next;
+ }
+ public void setNext(GlarchProxy next) {
+ this.next = next;
+ }
+
+ public short getOrder() {
+ return order;
+ }
+ public void setOrder(short order) {
+ this.order = order;
+ }
+
+ public List getStrings() {
+ return strings;
+ }
+
+ public void setStrings(List strings) {
+ this.strings = strings;
+ }
+
+ public Map getStringSets() {
+ return stringSets;
+ }
+
+ public void setStringSets(Map stringSets) {
+ this.stringSets = stringSets;
+ }
+
+ public List getFooComponents() {
+ return fooComponents;
+ }
+
+ public void setFooComponents(List fooComponents) {
+ this.fooComponents = fooComponents;
+ }
+
+ public GlarchProxy[] getProxyArray() {
+ return proxyArray;
+ }
+ public void setProxyArray(GlarchProxy[] proxyArray) {
+ this.proxyArray = proxyArray;
+ }
+ public Set getProxySet() {
+ return proxySet;
+ }
+
+ public void setProxySet(Set proxySet) {
+ this.proxySet = proxySet;
+ }
+
+ public boolean onDelete(Session s) throws CallbackException {
+ return NO_VETO;
+ }
+
+ public void onLoad(Session s, Serializable id) {
+ if ( ! ( ( (String) id ).length()==32 ) ) throw new RuntimeException("id problem");
+ }
+
+ public boolean onSave(Session s) throws CallbackException {
+ dynaBean = new HashMap();
+ dynaBean.put("foo", "foo");
+ dynaBean.put("bar", new Integer(66));
+ immutable="never changes!";
+ return NO_VETO;
+ }
+
+ public boolean onUpdate(Session s) throws CallbackException {
+ return NO_VETO;
+ }
+
+ /*public Currency getCurrency() {
+ return currency;
+ }
+
+ public void setCurrency(Currency currency) {
+ this.currency = currency;
+ }*/
+
+ /**
+ * Returns the dynaBean.
+ * @return DynaBean
+ */
+ public Map getDynaBean() {
+ return dynaBean;
+ }
+
+ /**
+ * Sets the dynaBean.
+ * @param dynaBean The dynaBean to set
+ */
+ public void setDynaBean(Map dynaBean) {
+ this.dynaBean = dynaBean;
+ }
+
+ /**
+ * Returns the immutable.
+ * @return String
+ */
+ public String getImmutable() {
+ return immutable;
+ }
+
+ /**
+ * Sets the immutable.
+ * @param immutable The immutable to set
+ */
+ public void setImmutable(String immutable) {
+ this.immutable = immutable;
+ }
+
+ /**
+ * Returns the derivedVersion.
+ * @return int
+ */
+ public int getDerivedVersion() {
+ return derivedVersion;
+ }
+
+ /**
+ * Sets the derivedVersion.
+ * @param derivedVersion The derivedVersion to set
+ */
+ public void setDerivedVersion(int derivedVersion) {
+ this.derivedVersion = derivedVersion;
+ }
+
+ /**
+ * Returns the any.
+ * @return Object
+ */
+ public Object getAny() {
+ return any;
+ }
+
+ /**
+ * Sets the any.
+ * @param any The any to set
+ */
+ public void setAny(Object any) {
+ this.any = any;
+ }
+
+ /**
+ * @return
+ */
+ public Multiplicity getMultiple() {
+ return multiple;
+ }
+
+ /**
+ * @param multiplicity
+ */
+ public void setMultiple(Multiplicity multiplicity) {
+ multiple = multiplicity;
+ }
+
+ public String getName() {
+ return super.getName();
+ }
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/GlarchProxy.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/GlarchProxy.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/GlarchProxy.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,52 @@
+package org.hibernate.test.legacy;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public interface GlarchProxy {
+
+ public int getVersion();
+ public int getDerivedVersion();
+ public void setVersion(int version);
+
+ public String getName();
+ public void setName(String name);
+
+ public GlarchProxy getNext();
+ public void setNext(GlarchProxy next);
+
+ public short getOrder();
+ public void setOrder(short order);
+
+ public List getStrings();
+ public void setStrings(List strings);
+
+ public Map getDynaBean();
+ public void setDynaBean(Map bean);
+
+ public Map getStringSets();
+ public void setStringSets(Map stringSets);
+
+ public List getFooComponents();
+ public void setFooComponents(List fooComponents);
+
+ public GlarchProxy[] getProxyArray();
+ public void setProxyArray(GlarchProxy[] proxyArray);
+
+ public Set getProxySet();
+ public void setProxySet(Set proxySet);
+
+ public Multiplicity getMultiple();
+ public void setMultiple(Multiplicity m);
+
+ public Object getAny();
+ public void setAny(Object any);
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Holder.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Holder.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Holder.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -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">
+<hibernate-mapping default-lazy="false">
+ <class name="org.hibernate.test.legacy.Holder">
+ <id name="id" column="id_" type="string" length="32" unsaved-value="null">
+ <generator class="uuid.hex"/>
+ </id>
+ <property name="name" unique="true" not-null="true" length="36"/>
+ <list name="ones">
+ <key column="holder"/>
+ <index column="i"/>
+ <one-to-many class="org.hibernate.test.legacy.One"/>
+ </list>
+ <array name="fooArray">
+ <key column="holder1"/>
+ <index column="j1"/>
+ <one-to-many class="org.hibernate.test.legacy.Foo"/>
+ </array>
+ <set name="foos">
+ <key column="holder2"/>
+ <one-to-many class="org.hibernate.test.legacy.Foo"/>
+ </set>
+ <many-to-one name="otherHolder" property-ref="name" cascade="all"/>
+ </class>
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Holder.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Holder.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Holder.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,115 @@
+//$Id: Holder.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.util.List;
+import java.util.Set;
+
+public class Holder implements Named {
+ private String id;
+ private List ones;
+ private Foo[] fooArray;
+ private Set foos;
+ private String name;
+ private Holder otherHolder;
+
+ public Holder() {
+ }
+ public Holder(String name) {
+ this.name=name;
+ }
+
+ /**
+ * Returns the fooArray.
+ * @return Foo[]
+ */
+ public Foo[] getFooArray() {
+ return fooArray;
+ }
+
+ /**
+ * Returns the foos.
+ * @return Set
+ */
+ public Set getFoos() {
+ return foos;
+ }
+
+ /**
+ * Sets the fooArray.
+ * @param fooArray The fooArray to set
+ */
+ public void setFooArray(Foo[] fooArray) {
+ this.fooArray = fooArray;
+ }
+
+ /**
+ * Sets the foos.
+ * @param foos The foos to set
+ */
+ public void setFoos(Set foos) {
+ this.foos = foos;
+ }
+
+ /**
+ * Returns the id.
+ * @return String
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Sets the id.
+ * @param id The id to set
+ */
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ /**
+ * Returns the name.
+ * @return String
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the name.
+ * @param name The name to set
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Returns the ones.
+ * @return List
+ */
+ public List getOnes() {
+ return ones;
+ }
+
+ /**
+ * Sets the ones.
+ * @param ones The ones to set
+ */
+ public void setOnes(List ones) {
+ this.ones = ones;
+ }
+
+ public Holder getOtherHolder() {
+ return otherHolder;
+ }
+
+ public void setOtherHolder(Holder holder) {
+ otherHolder = holder;
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/I.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/I.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/I.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,36 @@
+//$Id: I.java 7203 2005-06-19 02:01:05Z oneovthafew $
+package org.hibernate.test.legacy;
+
+/**
+ * @author Gavin King
+ */
+public class I {
+ private Long id;
+ private String name;
+ private char type;
+ private K parent;
+
+ public K getParent() {
+ return parent;
+ }
+
+ public void setParent(K parent) {
+ this.parent = parent;
+ }
+
+ void setType(char type) {
+ this.type = type;
+ }
+
+ char getType() {
+ return type;
+ }
+
+ void setName(String name) {
+ this.name = name;
+ }
+
+ String getName() {
+ return name;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/IJ.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/IJ.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/IJ.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping default-lazy="false"
+ package="org.hibernate.test.legacy"
+ default-access="field">
+
+ <class name="I" discriminator-value="0">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+
+ <discriminator formula="case when type_ in ('a', 'b', 'c') then 0 else 1 end" type="integer"/>
+
+ <property name="name" unique="true" not-null="true"/>
+ <property name="type" column="type_" not-null="true"/>
+
+ <subclass name="J" discriminator-value="1">
+ <property name="amount"/>
+ </subclass>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/IJ2.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/IJ2.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/IJ2.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping default-lazy="false"
+ package="org.hibernate.test.legacy"
+ default-access="field">
+
+ <class name="I">
+ <id name="id">
+ <generator class="hilo"/>
+ </id>
+ <property name="name" unique="true" not-null="true"/>
+ <property name="type" column="type_"/>
+ <many-to-one name="parent" cascade="save-update"/>
+ <union-subclass name="J">
+ <property name="amount"/>
+ </union-subclass>
+ </class>
+
+ <class name="K">
+ <id name="id">
+ <generator class="hilo"/>
+ </id>
+ <set name="is" inverse="true">
+ <key column="parent"/>
+ <one-to-many class="I"/>
+ </set>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/IJ2Test.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/IJ2Test.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/IJ2Test.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,129 @@
+//$Id: IJ2Test.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+
+import junit.framework.Test;
+
+import org.hibernate.LockMode;
+import org.hibernate.classic.Session;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class IJ2Test extends LegacyTestCase {
+
+ public IJ2Test(String x) {
+ super(x);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "legacy/IJ2.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( IJ2Test.class );
+ }
+
+ public void testUnionSubclass() throws Exception {
+ Session s = getSessions().openSession();
+ I i = new I();
+ i.setName( "i" );
+ i.setType( 'a' );
+ J j = new J();
+ j.setName( "j" );
+ j.setType( 'x' );
+ j.setAmount( 1.0f );
+ Serializable iid = s.save(i);
+ Serializable jid = s.save(j);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ getSessions().evict(I.class);
+
+ s = getSessions().openSession();
+ j = (J) s.get(I.class, jid);
+ j = (J) s.get(J.class, jid);
+ i = (I) s.get(I.class, iid);
+ assertTrue( i.getClass()==I.class );
+ j.setAmount( 0.5f );
+ s.lock(i, LockMode.UPGRADE);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ getSessions().evict(I.class);
+
+ s = getSessions().openSession();
+ j = (J) s.get(J.class, jid);
+ j = (J) s.get(I.class, jid);
+ i = (I) s.get(I.class, iid);
+ assertTrue( i.getClass()==I.class );
+ j.setAmount( 0.5f );
+ s.lock(i, LockMode.UPGRADE);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ getSessions().evict(I.class);
+
+ s = getSessions().openSession();
+ assertTrue( s.find("from I").size()==2 );
+ assertTrue( s.find("from J").size()==1 );
+ assertTrue( s.find("from J j where j.amount > 0 and j.name is not null").size()==1 );
+ assertTrue( s.find("from I i where i.class = org.hibernate.test.legacy.I").size()==1 );
+ assertTrue( s.find("from I i where i.class = J").size()==1 );
+ s.connection().commit();
+ s.close();
+
+ getSessions().evict(I.class);
+
+ s = getSessions().openSession();
+ j = (J) s.get(J.class, jid);
+ i = (I) s.get(I.class, iid);
+ K k = new K();
+ Serializable kid = s.save(k);
+ i.setParent(k);
+ j.setParent(k);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ getSessions().evict(I.class);
+
+ s = getSessions().openSession();
+ j = (J) s.get(J.class, jid);
+ i = (I) s.get(I.class, iid);
+ k = (K) s.get(K.class, kid);
+ System.out.println(k + "=" + i.getParent());
+ assertTrue( i.getParent()==k );
+ assertTrue( j.getParent()==k );
+ assertTrue( k.getIs().size()==2 );
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ getSessions().evict(I.class);
+
+ s = getSessions().openSession();
+ assertTrue( s.find("from K k inner join k.is i where i.name = 'j'").size()==1 );
+ assertTrue( s.find("from K k inner join k.is i where i.name = 'i'").size()==1 );
+ assertTrue( s.find("from K k left join fetch k.is").size()==2 );
+ s.connection().commit();
+ s.close();
+
+ s = getSessions().openSession();
+ j = (J) s.get(J.class, jid);
+ i = (I) s.get(I.class, iid);
+ k = (K) s.get(K.class, kid);
+ s.delete(k);
+ s.delete(j);
+ s.delete(i);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/IJTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/IJTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/IJTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,84 @@
+//$Id: IJTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+
+import junit.framework.Test;
+
+import org.hibernate.LockMode;
+import org.hibernate.classic.Session;
+import org.hibernate.dialect.HSQLDialect;
+import org.hibernate.dialect.OracleDialect;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class IJTest extends LegacyTestCase {
+
+ public IJTest(String x) {
+ super(x);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "legacy/IJ.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( IJTest.class );
+ }
+
+ public void testFormulaDiscriminator() throws Exception {
+ if ( ( getDialect() instanceof OracleDialect ) || ( getDialect() instanceof HSQLDialect ) ) return;
+ Session s = getSessions().openSession();
+ I i = new I();
+ i.setName( "i" );
+ i.setType( 'a' );
+ J j = new J();
+ j.setName( "j" );
+ j.setType( 'x' );
+ j.setAmount( 1.0f );
+ Serializable iid = s.save(i);
+ Serializable jid = s.save(j);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ getSessions().evict(I.class);
+
+ s = getSessions().openSession();
+ j = (J) s.get(I.class, jid);
+ i = (I) s.get(I.class, iid);
+ assertTrue( i.getClass()==I.class );
+ j.setAmount( 0.5f );
+ s.lock(i, LockMode.UPGRADE);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = getSessions().openSession();
+ j = (J) s.get(I.class, jid, LockMode.UPGRADE);
+ i = (I) s.get(I.class, iid, LockMode.UPGRADE);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = getSessions().openSession();
+ assertTrue( s.find("from I").size()==2 );
+ assertTrue( s.find("from J").size()==1 );
+ assertTrue( s.find("from I i where i.class = 0").size()==1 );
+ assertTrue( s.find("from I i where i.class = 1").size()==1 );
+ s.connection().commit();
+ s.close();
+
+ s = getSessions().openSession();
+ j = (J) s.get(J.class, jid);
+ i = (I) s.get(I.class, iid);
+ s.delete(j);
+ s.delete(i);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Immutable.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Immutable.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Immutable.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,16 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping default-lazy="false">
+
+ <class name="org.hibernate.test.legacy.Immutable" table="immut" mutable="false">
+ <!--cache-->
+ <id name="id" column="id_" length="64">
+ <generator class="uuid"/>
+ </id>
+ <property name="foo"/>
+ <property name="bar"/>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Immutable.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Immutable.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Immutable.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,39 @@
+//$Id: Immutable.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+public class Immutable {
+ private String foo;
+ private String bar;
+ private String id;
+
+ public String getFoo() {
+ return foo;
+ }
+
+ public void setFoo(String foo) {
+ this.foo = foo;
+ }
+
+ public String getBar() {
+ return bar;
+ }
+
+ public void setBar(String bar) {
+ this.bar = bar;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Inner.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Inner.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Inner.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,61 @@
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @author Stefano Travelli
+ */
+public class Inner implements Serializable {
+ private InnerKey id;
+ private String dudu;
+ private List middles;
+ private Outer backOut;
+
+ public InnerKey getId() {
+ return id;
+ }
+
+ public void setId(InnerKey id) {
+ this.id = id;
+ }
+
+ public String getDudu() {
+ return dudu;
+ }
+
+ public void setDudu(String dudu) {
+ this.dudu = dudu;
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof Inner)) return false;
+
+ final Inner cidSuper = (Inner) o;
+
+ if (id != null ? !id.equals(cidSuper.id) : cidSuper.id != null) return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ return (id != null ? id.hashCode() : 0);
+ }
+ public List getMiddles() {
+ return middles;
+ }
+
+ public void setMiddles(List list) {
+ middles = list;
+ }
+
+ public Outer getBackOut() {
+ return backOut;
+ }
+
+ public void setBackOut(Outer outer) {
+ backOut = outer;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/InnerKey.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/InnerKey.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/InnerKey.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,46 @@
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+
+/**
+ * @author Stefano Travelli
+ */
+public class InnerKey implements Serializable {
+ private String akey;
+ private String bkey;
+
+ public String getAkey() {
+ return akey;
+ }
+
+ public void setAkey(String akey) {
+ this.akey = akey;
+ }
+
+ public String getBkey() {
+ return bkey;
+ }
+
+ public void setBkey(String bkey) {
+ this.bkey = bkey;
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof InnerKey)) return false;
+
+ final InnerKey cidSuperID = (InnerKey) o;
+
+ if (akey != null ? !akey.equals(cidSuperID.akey) : cidSuperID.akey != null) return false;
+ if (bkey != null ? !bkey.equals(cidSuperID.bkey) : cidSuperID.bkey != null) return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ int result;
+ result = (akey != null ? akey.hashCode() : 0);
+ result = 29 * result + (bkey != null ? bkey.hashCode() : 0);
+ return result;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Intervention.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Intervention.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Intervention.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,36 @@
+package org.hibernate.test.legacy;
+
+/**
+ * @author hbm2java
+ */
+public class Intervention {
+
+ java.lang.String id;
+ long version;
+
+ String description;
+
+ java.lang.String getId() {
+ return id;
+ }
+
+ void setId(java.lang.String newValue) {
+ id = newValue;
+ }
+
+ long getVersion() {
+ return version;
+ }
+
+ void setVersion(long newValue) {
+ version = newValue;
+ }
+
+
+public String getDescription() {
+ return description;
+}
+public void setDescription(String description) {
+ this.description = description;
+}
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/J.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/J.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/J.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,17 @@
+//$Id: J.java 7203 2005-06-19 02:01:05Z oneovthafew $
+package org.hibernate.test.legacy;
+
+/**
+ * @author Gavin King
+ */
+public class J extends I {
+ private float amount;
+
+ void setAmount(float amount) {
+ this.amount = amount;
+ }
+
+ float getAmount() {
+ return amount;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Jay.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Jay.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Jay.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,45 @@
+//$Id: Jay.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+/**
+ * @author Gavin King
+ */
+public class Jay {
+ private long id;
+ private Eye eye;
+ /**
+ * @return Returns the eye.
+ */
+ public Eye getEye() {
+ return eye;
+ }
+
+ /**
+ * @param eye The eye to set.
+ */
+ public void setEye(Eye eye) {
+ this.eye = eye;
+ }
+
+ /**
+ * @return Returns the id.
+ */
+ public long getId() {
+ return id;
+ }
+
+ /**
+ * @param id The id to set.
+ */
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public Jay() {}
+
+ public Jay(Eye eye) {
+ eye.getJays().add(this);
+ this.eye = eye;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/K.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/K.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/K.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,18 @@
+//$Id: K.java 7203 2005-06-19 02:01:05Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.util.Set;
+
+/**
+ * @author Gavin King
+ */
+public class K {
+ private Long id;
+ private Set is;
+ void setIs(Set is) {
+ this.is = is;
+ }
+ Set getIs() {
+ return is;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/LegacyTestCase.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/LegacyTestCase.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/LegacyTestCase.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,57 @@
+package org.hibernate.test.legacy;
+
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.hql.classic.ClassicQueryTranslatorFactory;
+import org.hibernate.util.StringHelper;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.dialect.Dialect;
+
+/**
+ * @author Steve Ebersole
+ */
+public abstract class LegacyTestCase extends FunctionalTestCase {
+
+ public static final String USE_ANTLR_PARSER_PROP = "legacy.use_antlr_hql_parser";
+ private final boolean useAntlrParser;
+
+ public LegacyTestCase(String x) {
+ super( x );
+ useAntlrParser = Boolean.valueOf( extractFromSystem( USE_ANTLR_PARSER_PROP ) ).booleanValue();
+ }
+
+ protected static String extractFromSystem(String systemPropertyName) {
+ try {
+ return System.getProperty( systemPropertyName );
+ }
+ catch( Throwable t ) {
+ return null;
+ }
+ }
+
+ public void configure(Configuration cfg) {
+ super.configure( cfg );
+ if ( !useAntlrParser ) {
+ cfg.setProperty( Environment.QUERY_TRANSLATOR, ClassicQueryTranslatorFactory.class.getName() );
+ try {
+ String dialectTrueRepresentation = Dialect.getDialect().toBooleanValueString( true );
+ // if this call succeeds, then the dialect is saying to represent true/false as int values...
+ Integer.parseInt( dialectTrueRepresentation );
+ String subs = cfg.getProperties().getProperty( Environment.QUERY_SUBSTITUTIONS );
+ if ( subs == null ) {
+ subs = "";
+ }
+ if ( StringHelper.isEmpty( subs ) ) {
+ subs = "true=1, false=0";
+ }
+ else {
+ subs += ", true=1, false=0";
+ }
+ cfg.getProperties().setProperty( Environment.QUERY_SUBSTITUTIONS, subs );
+ }
+ catch( NumberFormatException nfe ) {
+ // the Integer#parseInt call failed...
+ }
+ }
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Location.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Location.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Location.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping default-lazy="false">
+
+ <class name="org.hibernate.test.legacy.Location">
+ <composite-id>
+ <key-property name="streetNumber"/>
+ <key-property name="streetName" length="20"/>
+ <key-property name="city" length="20"/>
+ <key-property name="countryCode" length="2"/>
+ </composite-id>
+ <property name="locale"/>
+ <property name="description"/>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Location.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Location.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Location.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,125 @@
+//$Id: Location.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+import java.util.Locale;
+
+public class Location implements Serializable {
+ private int streetNumber;
+ private String city;
+ private String streetName;
+ private String countryCode;
+ private Locale locale;
+ private String description;
+
+ /**
+ * Returns the countryCode.
+ * @return String
+ */
+ public String getCountryCode() {
+ return countryCode;
+ }
+
+ /**
+ * Returns the description.
+ * @return String
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * Returns the locale.
+ * @return Locale
+ */
+ public Locale getLocale() {
+ return locale;
+ }
+
+ /**
+ * Returns the streetName.
+ * @return String
+ */
+ public String getStreetName() {
+ return streetName;
+ }
+
+ /**
+ * Returns the streetNumber.
+ * @return int
+ */
+ public int getStreetNumber() {
+ return streetNumber;
+ }
+
+ /**
+ * Sets the countryCode.
+ * @param countryCode The countryCode to set
+ */
+ public void setCountryCode(String countryCode) {
+ this.countryCode = countryCode;
+ }
+
+ /**
+ * Sets the description.
+ * @param description The description to set
+ */
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ /**
+ * Sets the locale.
+ * @param locale The locale to set
+ */
+ public void setLocale(Locale locale) {
+ this.locale = locale;
+ }
+
+ /**
+ * Sets the streetName.
+ * @param streetName The streetName to set
+ */
+ public void setStreetName(String streetName) {
+ this.streetName = streetName;
+ }
+
+ /**
+ * Sets the streetNumber.
+ * @param streetNumber The streetNumber to set
+ */
+ public void setStreetNumber(int streetNumber) {
+ this.streetNumber = streetNumber;
+ }
+
+ /**
+ * Returns the city.
+ * @return String
+ */
+ public String getCity() {
+ return city;
+ }
+
+ /**
+ * Sets the city.
+ * @param city The city to set
+ */
+ public void setCity(String city) {
+ this.city = city;
+ }
+
+ public boolean equals(Object other) {
+ Location l = (Location) other;
+ return l.getCity().equals(city) && l.getStreetName().equals(streetName) && l.getCountryCode().equals(countryCode) && l.getStreetNumber()==streetNumber;
+ }
+ public int hashCode() {
+ return streetName.hashCode();
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Lower.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Lower.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Lower.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,134 @@
+//$Id: Lower.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.util.List;
+import java.util.Set;
+
+
+public class Lower extends Top {
+ private int intprop;
+ private String foo;
+ private Set set;
+ private List bag;
+ private Top another;
+ private Lower yetanother;
+ private Po mypo;
+ /**
+ * Returns the intprop.
+ * @return int
+ */
+ public int getIntprop() {
+ return intprop;
+ }
+
+ /**
+ * Sets the intprop.
+ * @param intprop The intprop to set
+ */
+ public void setIntprop(int intprop) {
+ this.intprop = intprop;
+ }
+
+ /**
+ * Returns the foo.
+ * @return String
+ */
+ public String getFoo() {
+ return foo;
+ }
+
+ /**
+ * Sets the foo.
+ * @param foo The foo to set
+ */
+ public void setFoo(String foo) {
+ this.foo = foo;
+ }
+
+ /**
+ * Returns the set.
+ * @return Set
+ */
+ public Set getSet() {
+ return set;
+ }
+
+ /**
+ * Sets the set.
+ * @param set The set to set
+ */
+ public void setSet(Set set) {
+ this.set = set;
+ }
+
+ /**
+ * Returns the another.
+ * @return Simple
+ */
+ public Top getAnother() {
+ return another;
+ }
+
+ /**
+ * Returns the yetanother.
+ * @return LessSimple
+ */
+ public Lower getYetanother() {
+ return yetanother;
+ }
+
+ /**
+ * Sets the another.
+ * @param another The another to set
+ */
+ public void setAnother(Top another) {
+ this.another = another;
+ }
+
+ /**
+ * Sets the yetanother.
+ * @param yetanother The yetanother to set
+ */
+ public void setYetanother(Lower yetanother) {
+ this.yetanother = yetanother;
+ }
+
+ /**
+ * Returns the bag.
+ * @return List
+ */
+ public List getBag() {
+ return bag;
+ }
+
+ /**
+ * Sets the bag.
+ * @param bag The bag to set
+ */
+ public void setBag(List bag) {
+ this.bag = bag;
+ }
+
+ /**
+ * Returns the mypo.
+ * @return Po
+ */
+ public Po getMypo() {
+ return mypo;
+ }
+
+ /**
+ * Sets the mypo.
+ * @param mypo The mypo to set
+ */
+ public void setMypo(Po mypo) {
+ this.mypo = mypo;
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MainObject.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MainObject.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MainObject.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,50 @@
+<?xml version="1.0"?>
+
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping default-lazy="false">
+ <class
+ name="org.hibernate.test.legacy.MainObject"
+ table="mainobject"
+ dynamic-update="false"
+ dynamic-insert="false"
+ >
+ <cache usage="read-write" />
+
+ <id
+ name="id"
+ column="id"
+ type="java.lang.Long"
+ >
+ <generator class="native">
+ <param name="sequence">seq_mainobj</param>
+ </generator>
+ </id>
+
+ <one-to-one
+ name="obj2"
+ class="org.hibernate.test.legacy.Object2"
+ cascade="all"
+ outer-join="auto"
+ constrained="false"
+ />
+
+ <property
+ name="description"
+ type="java.lang.String"
+ update="true"
+ insert="true"
+ column="description"
+ />
+
+ <!--
+ To add non XDoclet property mappings, create a file named
+ hibernate-properties-MainObject.xml
+ containing the additional properties and place it in your merge dir.
+ -->
+
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MainObject.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MainObject.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MainObject.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,38 @@
+package org.hibernate.test.legacy;
+
+/**
+ * @author Wolfgang Voelkl
+ */
+public class MainObject {
+ private Long id;
+ private String description;
+ private Object2 obj2;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public Object2 getObj2() {
+ return obj2;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String string) {
+ description = string;
+ }
+
+ public void setObj2(Object2 object2) {
+ this.obj2 = object2;
+ if (object2 != null) {
+ object2.setBelongsToMainObj(this);
+ }
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Many.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Many.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Many.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,16 @@
+<?xml version="1.0"?>
+
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping default-lazy="false">
+ <class name="org.hibernate.test.legacy.Many" table="many">
+ <id name="key" column="many_key">
+ <generator class="native" />
+ </id>
+ <property name="x"/>
+ <many-to-one name="one" column="one_key" class="org.hibernate.test.legacy.One"/>
+ </class>
+</hibernate-mapping>
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Many.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Many.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Many.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,37 @@
+//$Id: Many.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+public class Many {
+ Long key;
+ One one;
+ private int x;
+
+ public int getX() {
+ return x;
+ }
+ public void setX(int x) {
+ this.x = x;
+ }
+
+ public void setKey(Long key) {
+ this.key = key;
+ }
+
+ public Long getKey() {
+ return this.key;
+ }
+
+ public void setOne(One one) {
+ this.one = one;
+ }
+
+ public One getOne() {
+ return this.one;
+ }
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Map.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Map.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Map.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,57 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping default-lazy="false">
+
+ <class entity-name="TestMap">
+
+ <id name="id" type="long" column="id_">
+ <generator class="hilo"/>
+ </id>
+
+ <property name="name" type="string"/>
+ <property name="address" type="string"/>
+
+ <many-to-one name="parent" entity-name="TestMap"/>
+
+ <bag name="children" inverse="true" lazy="false">
+ <key column="parent"/>
+ <one-to-many entity-name="TestMap"/>
+ </bag>
+
+ <dynamic-component name="cmp">
+ <property name="a" type="integer"/>
+ <property name="b" type="float"/>
+ </dynamic-component>
+ </class>
+
+ <class entity-name="ParentMap">
+
+ <id name="parentId" column="ParentId" type="long" unsaved-value="null">
+ <generator class="hilo">
+ <param name="sequence">parent_sequence</param>
+ </generator>
+ </id>
+
+ <version name="version" type="java.lang.Integer" column="version"/>
+ <property name="field" type="string" column="Field" length="50"/>
+
+ <one-to-one name="child" entity-name="ChildMap" cascade="all" outer-join="false" constrained="false" property-ref="parent"/>
+ </class>
+
+ <class entity-name="ChildMap">
+
+ <id name="childId" column="ChildId" type="long" unsaved-value="null">
+ <generator class="hilo">
+ <param name="sequence">child_sequence</param>
+ </generator>
+ </id>
+
+ <property name="field" type="string" column="Field" length="50"/>
+
+ <many-to-one name="parent" entity-name="ParentMap" cascade="all" outer-join="false" column="ParentId" not-null="true"/>
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MapTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MapTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MapTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,164 @@
+//$Id: MapTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.Test;
+
+import org.hibernate.EntityMode;
+import org.hibernate.Session;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.criterion.Example;
+import org.hibernate.criterion.Expression;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+
+public class MapTest extends LegacyTestCase {
+
+ public MapTest(String x) {
+ super(x);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "legacy/Map.hbm.xml", "legacy/Commento.hbm.xml", "legacy/Marelo.hbm.xml" };
+ }
+
+ public void configure(Configuration cfg) {
+ super.configure( cfg );
+ cfg.setProperty( Environment.DEFAULT_ENTITY_MODE, EntityMode.MAP.toString() );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( MapTest.class );
+ }
+
+ public void testMap() throws Exception {
+
+ Session s = openSession().getSession(EntityMode.MAP);
+ Map map = new HashMap();
+ map.put("$type$", "TestMap");
+ map.put("name", "foo");
+ map.put("address", "bar");
+ Map cmp = new HashMap();
+ cmp.put( "a", new Integer(1) );
+ cmp.put( "b", new Float(1.0) );
+ map.put("cmp", cmp);
+ s.save(map);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession().getSession(EntityMode.MAP);
+ map = (Map) s.get( "TestMap", (Serializable) map.get("id") );
+ assertTrue( map!=null && "foo".equals( map.get("name") ) );
+ assertTrue( map.get("$type$").equals("TestMap") );
+
+ int size = s.createCriteria("TestMap").add( Example.create(map) ).list().size();
+ assertTrue(size==1);
+ s.connection().commit();
+ s.close();
+
+ s = openSession().getSession(EntityMode.MAP);
+ List list = s.createQuery("from TestMap").list();
+ map = (Map) list.get(0);
+ assertTrue( "foo".equals( map.get("name") ) );
+ assertTrue( "bar".equals( map.get("address") ) );
+ cmp = (Map) map.get("cmp");
+ assertTrue( new Integer(1).equals( cmp.get("a") ) && new Float(1.0).equals( cmp.get("b") ) );
+ assertTrue( null==map.get("parent") );
+ map.put("name", "foobar");
+ map.put("parent", map);
+ List bag = (List) map.get("children");
+ bag.add(map);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ list = s.createQuery("from TestMap tm where tm.address = 'bar'").list();
+ map = (Map) list.get(0);
+ assertTrue( "foobar".equals( map.get("name") ) );
+ assertTrue( "bar".equals( map.get("address") ) );
+ assertTrue( map==map.get("parent") );
+ bag = (List) map.get("children");
+ assertTrue( bag.size()==1 );
+
+ size = s.createCriteria("TestMap")
+ .add( Expression.eq("address", "bar") )
+ .createCriteria("parent")
+ .add( Expression.eq("name", "foobar") )
+ .list()
+ .size();
+ assertTrue(size==1);
+
+ s.delete(map);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ }
+
+ public void testMapOneToOne() throws Exception {
+ Map child = new HashMap();
+ Map parent = new HashMap();
+ Session s = openSession();
+ child.put("parent", parent);
+ child.put("$type$", "ChildMap");
+ parent.put("child", child);
+ parent.put("$type$", "ParentMap");
+ s.save(parent);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ Map cm = (Map) s.createQuery("from ChildMap cm where cm.parent is not null").uniqueResult();
+ s.delete(cm);
+ s.delete( cm.get("parent") );
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ child = new HashMap();
+ parent = new HashMap();
+ s = openSession();
+ child.put("parent", parent);
+ child.put("$type$", "ChildMap");
+ parent.put("child", child);
+ parent.put("$type$", "ParentMap");
+ s.save(child);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ Map pm = (Map) s.createQuery("from ParentMap cm where cm.child is not null").uniqueResult();
+ s.delete(pm);
+ s.delete( pm.get("child") );
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ }
+
+ public void testOneToOnePropertyRef() throws Exception {
+ Session s = openSession();
+ s.createQuery("from Commento c where c.marelo.mlmag = 0").list();
+ s.createQuery("from Commento c where c.marelo.commento.mcompr is null").list();
+ s.createQuery("from Commento c where c.marelo.mlink = 0").list();
+ s.createQuery("from Commento c where c.marelo.commento = c").list();
+ s.createQuery("from Commento c where c.marelo.id.mlmag = 0").list();
+ s.createQuery("from Commento c where c.marelo.commento.id = c.id").list();
+ s.createQuery("from Commento c where c.marelo.commento.mclink = c.mclink").list();
+ s.createQuery("from Marelo m where m.commento.id > 0").list();
+ s.createQuery("from Marelo m where m.commento.marelo.commento.marelo.mlmag is not null").list();
+ s.connection().commit();
+ s.close();
+ }
+
+}
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Marelo.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Marelo.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Marelo.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,41 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping default-lazy="false">
+<!--
+ Created by the Middlegen Hibernate plugin 2.1
+
+ http://boss.bekk.no/boss/middlegen/
+ http://hibernate.sourceforge.net/
+-->
+
+<class entity-name="Marelo" table="MARELO">
+
+ <composite-id>
+ <key-property name="mlmag" column="MLMAG" type="int" length="5"/>
+ <key-property name="mloca" column="MLOCA" type="java.lang.String" length="11"/>
+ </composite-id>
+
+ <property name="mlink" type="long" column="MLINK" length="10"/>
+ <property name="mlart" type="java.lang.String" column="MLART" length="13"/>
+ <property name="mlido" type="long" column="MLIDO" length="10"/>
+ <property name="mldtc" type="java.sql.Timestamp" column="MLDTC" length="7"/>
+ <property name="mltop" type="java.lang.String" column="MLTOP" length="10"/>
+ <property name="mlrag" type="java.lang.String" column="MLRAG" length="2"/>
+ <property name="mlrop" type="short" column="MLROP" length="4"/>
+ <property name="mlcom" type="long" column="MLCOM" length="10"/>
+ <property name="matri" type="java.lang.String" column="MATRI" length="14"/>
+ <property name="socod" type="java.lang.String" column="SOCOD" length="10"/>
+
+ <!-- Associations -->
+
+ <!-- bi-directional one-to-one association to Commento -->
+ <many-to-one name="commento" entity-name="Commento" unique="false" insert="false" update="false">
+ <column name="MLCOM"/>
+ </many-to-one>
+
+
+</class>
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Master.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Master.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Master.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,143 @@
+//$Id: Master.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+
+public class Master implements Serializable, Named {
+ private Master otherMaster;
+ private Set details = new HashSet();
+ private Set moreDetails = new HashSet();
+ private Set incoming = new HashSet();
+ private Set outgoing = new HashSet();
+ private String name="master";
+ private Date stamp;
+ private int version;
+ private BigDecimal bigDecimal = new BigDecimal("1234.123");
+ private int x;
+ private Collection allDetails;
+
+ public int getX() {
+ return x;
+ }
+ public void setX(int x) {
+ this.x = x;
+ }
+
+ public Set getDetails() {
+ return details;
+ }
+
+ private void setDetails(Set details) {
+ this.details = details;
+ }
+
+ public void addDetail(Detail d) {
+ details.add(d);
+ }
+
+ public void removeDetail(Detail d) {
+ details.remove(d);
+ }
+
+ public void addIncoming(Master m) {
+ incoming.add(m);
+ }
+
+ public void removeIncoming(Master m) {
+ incoming.remove(m);
+ }
+
+ public void addOutgoing(Master m) {
+ outgoing.add(m);
+ }
+
+ public void removeOutgoing(Master m) {
+ outgoing.remove(m);
+ }
+
+ public Set getIncoming() {
+ return incoming;
+ }
+
+ public void setIncoming(Set incoming) {
+ this.incoming = incoming;
+ }
+
+ public Set getOutgoing() {
+ return outgoing;
+ }
+
+ public void setOutgoing(Set outgoing) {
+ this.outgoing = outgoing;
+ }
+
+ public Set getMoreDetails() {
+ return moreDetails;
+ }
+
+ public void setMoreDetails(Set moreDetails) {
+ this.moreDetails = moreDetails;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Date getStamp() {
+ return stamp;
+ }
+
+ public void setStamp(Date stamp) {
+ this.stamp = stamp;
+ }
+
+ public BigDecimal getBigDecimal() {
+ return bigDecimal;
+ }
+
+ public void setBigDecimal(BigDecimal bigDecimal) {
+ this.bigDecimal = bigDecimal;
+ }
+
+ /**
+ * @return
+ */
+ public Master getOtherMaster() {
+ return otherMaster;
+ }
+
+ /**
+ * @param master
+ */
+ public void setOtherMaster(Master master) {
+ otherMaster = master;
+ }
+
+ /**
+ * @return Returns the allDetails.
+ */
+ public Collection getAllDetails() {
+ return allDetails;
+ }
+ /**
+ * @param allDetails The allDetails to set.
+ */
+ public void setAllDetails(Collection allDetails) {
+ this.allDetails = allDetails;
+ }
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MasterDetail.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MasterDetail.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MasterDetail.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,78 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping default-lazy="false">
+
+ <class name="org.hibernate.test.legacy.Master" table="`master`">
+ <meta attribute="foo">foo</meta>
+ <id column="master_key_column" type="long">
+ <generator class="native"/>
+ </id>
+ <version name="version" access="field"/>
+ <!--version type="timestamp" name="stamp" column="ts_"/-->
+ <!--timestamp name="stamp" column="ts_"/-->
+ <property name="name">
+ <meta attribute="bar">bar</meta>
+ <meta attribute="bar">bar2</meta>
+ </property>
+ <property name="x"/>
+ <property name="bigDecimal" column="big_dec" precision="7" scale="3"/>
+ <set name="details" inverse="true" lazy="true" order-by="i asc">
+ <key column="master_key"/>
+ <one-to-many class="org.hibernate.test.legacy.Detail"/>
+ </set>
+ <set name="moreDetails" cascade="all" lazy="true" order-by="i desc">
+ <key column="more_master_key"/>
+ <one-to-many class="org.hibernate.test.legacy.Detail"/>
+ </set>
+ <set name="incoming" lazy="true" table="mtm">
+ <key foreign-key="AA" column="outgoingabcdef"/>
+ <many-to-many class="org.hibernate.test.legacy.Master" foreign-key="BB">
+ <column name="incomingabcdef" index="IBB"/>
+ </many-to-many>
+ </set>
+ <set name="outgoing" lazy="true" table="mtm" inverse="true">
+ <key column="incomingabcdef"/>
+ <many-to-many class="org.hibernate.test.legacy.Master" column="outgoingabcdef"/>
+ </set>
+ <many-to-one name="otherMaster"/>
+ <!--query-list name="allDetails" query-ref="all_details"/-->
+ </class>
+
+ <class name="org.hibernate.test.legacy.Detail">
+ <id column="detail_key_column" type="long">
+ <generator class="native"/>
+ </id>
+ <property name="x">
+ <column name="x" unique-key="uk" not-null="true"/>
+ </property>
+ <property name="i">
+ <column name="i" unique-key="uk" not-null="true"/>
+ </property>
+ <many-to-one name="master" foreign-key="CC"> <!--not-null="true"-->
+ <column name="master_key" unique-key="uk" not-null="true"/>
+ </many-to-one>
+ <!-- <cache timeout="10000" type="readwrite"/> -->
+ <set name="subDetails" lazy="true" cascade="all">
+ <key column="detail_key"/>
+ <one-to-many class="org.hibernate.test.legacy.SubDetail"/>
+ </set>
+ </class>
+
+ <class name="org.hibernate.test.legacy.SubDetail">
+ <id column="subdetail_key_col" type="long">
+ <generator class="native">
+ <param name="sequence">fooseq</param>
+ </generator>
+ </id>
+ <property name="name"/>
+ </class>
+
+ <query name="all_details" cacheable="true" fetch-size="10"><![CDATA[
+ from
+ Detail as d
+ ]]></query>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MasterDetailTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MasterDetailTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MasterDetailTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1036 @@
+//$Id: MasterDetailTest.java 10981 2006-12-13 00:14:17Z steve.ebersole at jboss.com $
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+import java.sql.Connection;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.LockMode;
+import org.hibernate.ObjectNotFoundException;
+import org.hibernate.Query;
+import org.hibernate.Transaction;
+import org.hibernate.classic.Session;
+import org.hibernate.criterion.Example;
+import org.hibernate.criterion.Expression;
+import org.hibernate.dialect.HSQLDialect;
+import org.hibernate.dialect.MckoiDialect;
+import org.hibernate.dialect.MySQLDialect;
+import org.hibernate.dialect.Oracle9Dialect;
+import org.hibernate.dialect.SAPDBDialect;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.mapping.MetaAttribute;
+import org.hibernate.mapping.PersistentClass;
+
+
+public class MasterDetailTest extends LegacyTestCase {
+
+ public MasterDetailTest(String arg) {
+ super(arg);
+ }
+
+ public String[] getMappings() {
+ return new String[] {
+ "legacy/MasterDetail.hbm.xml",
+ "legacy/Custom.hbm.xml",
+ "legacy/Category.hbm.xml",
+ "legacy/Nameable.hbm.xml",
+ "legacy/SingleSeveral.hbm.xml",
+ "legacy/WZ.hbm.xml",
+ "legacy/UpDown.hbm.xml",
+ "legacy/Eye.hbm.xml"
+ };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( MasterDetailTest.class );
+ }
+
+ public void testOuterJoin() throws Exception {
+ Session s = openSession();
+ Eye e = new Eye();
+ e.setName("Eye Eye");
+ Jay jay = new Jay(e);
+ e.setJay(jay);
+ s.saveOrUpdate(e);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ e = (Eye) s.createCriteria(Eye.class).uniqueResult();
+ assertTrue( Hibernate.isInitialized( e.getJay() ) );
+ assertTrue( Hibernate.isInitialized( e.getJays() ) );
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ jay = (Jay) s.createQuery("select new Jay(eye) from Eye eye").uniqueResult();
+ assertTrue( "Eye Eye".equals( jay.getEye().getName() ) );
+ s.delete( jay.getEye() );
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testMeta() throws Exception {
+ PersistentClass clazz = getCfg().getClassMapping( Master.class.getName() );
+ MetaAttribute meta = clazz.getMetaAttribute("foo");
+ assertTrue( "foo".equals( meta.getValue() ) );
+ meta = clazz.getProperty("name").getMetaAttribute("bar");
+ assertTrue( meta.isMultiValued() );
+ }
+
+ public void testCopy() throws Exception {
+ Category catWA = new Category();
+ catWA.setName("HSQL workaround");
+ Category cat = new Category();
+ cat.setName("foo");
+ Category subCatBar = new Category();
+ subCatBar.setName("bar");
+ Category subCatBaz = new Category();
+ subCatBaz.setName("baz");
+ cat.getSubcategories().add(subCatBar);
+ cat.getSubcategories().add(subCatBaz);
+ Session s = openSession();
+ s.save(catWA);
+ s.save(cat);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ cat.setName("new foo");
+ subCatBar.setName("new bar");
+ cat.getSubcategories().remove(subCatBaz);
+ Category newCat = new Category();
+ newCat.setName("new");
+ cat.getSubcategories().add(newCat);
+ Category newSubCat = new Category();
+ newSubCat.setName("new sub");
+ newCat.getSubcategories().add(newSubCat);
+
+ s = openSession();
+ Category copiedCat = (Category) s.saveOrUpdateCopy(cat);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ assertFalse( copiedCat==cat );
+ //assertFalse( copiedCat.getSubcategories().contains(newCat) );
+ assertTrue( cat.getSubcategories().contains(newCat) );
+
+ s = openSession();
+ cat = (Category) s.createQuery("from Category cat where cat.name='new foo'").uniqueResult();
+ newSubCat = (Category) s.createQuery("from Category cat left join fetch cat.subcategories where cat.name='new sub'").uniqueResult();
+ assertTrue( newSubCat.getName().equals("new sub") );
+ s.close();
+
+ newSubCat.getSubcategories().add(cat);
+ cat.setName("new new foo");
+
+ s = openSession();
+ newSubCat = (Category) s.saveOrUpdateCopy( newSubCat, new Long( newSubCat.getId() ) );
+ assertTrue( newSubCat.getName().equals("new sub") );
+ assertTrue( newSubCat.getSubcategories().size()==1 );
+ cat = (Category) newSubCat.getSubcategories().get(0);
+ assertTrue( cat.getName().equals("new new foo") );
+ newSubCat.getSubcategories().remove(cat);
+ s.delete(cat);
+ s.delete(subCatBaz);
+ s.delete(catWA);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ }
+
+ public void testNotNullDiscriminator() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Up up = new Up();
+ up.setId1("foo");
+ up.setId2(123l);
+ Down down = new Down();
+ down.setId1("foo");
+ down.setId2(321l);
+ down.setValue(12312312l);
+ s.save(up);
+ s.save(down);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ List list = s.find("from Up up order by up.id2 asc");
+ assertTrue( list.size()==2 );
+ assertFalse( list.get(0) instanceof Down );
+ assertTrue( list.get(1) instanceof Down );
+ list = s.find("from Down down");
+ assertTrue( list.size()==1 );
+ assertTrue( list.get(0) instanceof Down );
+ //list = s.find("from Up down where down.class = Down");
+ assertTrue( list.size()==1 );
+ assertTrue( list.get(0) instanceof Down );
+ s.delete("from Up up");
+ t.commit();
+ s.close();
+
+ }
+
+ public void testSelfManyToOne() throws Exception {
+
+ //if (dialect instanceof HSQLDialect) return;
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Master m = new Master();
+ m.setOtherMaster(m);
+ s.save(m);
+ t.commit();
+ s.close();
+ s = openSession();
+ t = s.beginTransaction();
+ Iterator i = s.iterate("from Master");
+ m = (Master) i.next();
+ assertTrue( m.getOtherMaster()==m );
+ if (getDialect() instanceof HSQLDialect) { m.setOtherMaster(null); s.flush(); }
+ s.delete(m);
+ t.commit();
+ s.close();
+ }
+
+ public void testExample() throws Exception {
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Master m = new Master();
+ m.setName("name");
+ m.setX(5);
+ m.setOtherMaster(m);
+ s.save(m);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Master m1 = (Master) s.createCriteria(Master.class)
+ .add( Example.create(m).enableLike().ignoreCase().excludeProperty("bigDecimal") )
+ .uniqueResult();
+ assertTrue( m1.getOtherMaster()==m1 );
+ m1 = (Master) s.createCriteria(Master.class)
+ .add( Expression.eq("name", "foobar") )
+ .uniqueResult();
+ assertTrue( m1==null );
+ m1 = (Master) s.createCriteria(Master.class)
+ .add( Example.create(m).excludeProperty("bigDecimal") )
+ .createCriteria("otherMaster")
+ .add( Example.create(m).excludeZeroes().excludeProperty("bigDecimal") )
+ .uniqueResult();
+ assertTrue( m1.getOtherMaster()==m1 );
+ Master m2 = (Master) s.createCriteria(Master.class)
+ .add( Example.create(m).excludeNone().excludeProperty("bigDecimal") )
+ .uniqueResult();
+ assertTrue( m2==m1 );
+ m.setName(null);
+ m2 = (Master) s.createCriteria(Master.class)
+ .add( Example.create(m).excludeNone().excludeProperty("bigDecimal") )
+ .uniqueResult();
+ assertTrue( null==m2 );
+ if (getDialect() instanceof HSQLDialect) { m1.setOtherMaster(null); s.flush(); }
+ s.delete(m1);
+ t.commit();
+ s.close();
+ }
+
+ public void testNonLazyBidirectional() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Single sin = new Single();
+ sin.setId("asdfds");
+ sin.setString("adsa asdfasd");
+ Several sev = new Several();
+ sev.setId("asdfasdfasd");
+ sev.setString("asd ddd");
+ sin.getSeveral().add(sev);
+ sev.setSingle(sin);
+ s.save(sin);
+ t.commit();
+ s.close();
+ s = openSession();
+ t = s.beginTransaction();
+ sin = (Single) s.load( Single.class, sin );
+ t.commit();
+ s.close();
+ s = openSession();
+ t = s.beginTransaction();
+ sev = (Several) s.load( Several.class, sev );
+ t.commit();
+ s.close();
+ s = openSession();
+ t = s.beginTransaction();
+ s.find("from Several");
+ t.commit();
+ s.close();
+ s = openSession();
+ t = s.beginTransaction();
+ s.delete("from Single");
+ t.commit();
+ s.close();
+ }
+
+ public void testCollectionQuery() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ if ( !(getDialect() instanceof MySQLDialect) && !(getDialect() instanceof SAPDBDialect) && !(getDialect() instanceof MckoiDialect) ) {
+ s.iterate("FROM Master m WHERE NOT EXISTS ( FROM m.details d WHERE NOT d.i=5 )");
+ s.iterate("FROM Master m WHERE NOT 5 IN ( SELECT d.i FROM m.details AS d )");
+ }
+ s.iterate("SELECT m FROM Master m JOIN m.details d WHERE d.i=5");
+ s.find("SELECT m FROM Master m JOIN m.details d WHERE d.i=5");
+ s.find("SELECT m.id FROM Master AS m JOIN m.details AS d WHERE d.i=5");
+ t.commit();
+ s.close();
+ }
+
+ public void testMasterDetail() throws Exception {
+
+ if (getDialect() instanceof HSQLDialect) return;
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Master master = new Master();
+ assertTrue( "save returned native id", s.save(master)!=null );
+ Serializable mid = s.getIdentifier(master);
+ Detail d1 = new Detail();
+ d1.setMaster(master);
+ Serializable did = s.save(d1);
+ Detail d2 = new Detail();
+ d2.setI(12);
+ d2.setMaster(master);
+ assertTrue( "generated id returned", s.save(d2)!=null);
+ master.addDetail(d1);
+ master.addDetail(d2);
+ if ( !(getDialect() instanceof MySQLDialect) && !(getDialect() instanceof Oracle9Dialect) && !(getDialect() instanceof SAPDBDialect) && !(getDialect() instanceof MckoiDialect) && !(getDialect() instanceof org.hibernate.dialect.TimesTenDialect)) {
+ assertTrue(
+ "query",
+ s.find("from Detail d, Master m where m = d.master and size(m.outgoing) = 0 and size(m.incoming) = 0").size()==2
+ );
+ }
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ master = new Master();
+ s.load(master, mid);
+ assertTrue( master.getDetails().size()==2 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ master = (Master) s.load(Master.class, mid);
+ Iterator iter = master.getDetails().iterator();
+ int i=0;
+ while ( iter.hasNext() ) {
+ Detail d = (Detail) iter.next();
+ assertTrue( "master-detail", d.getMaster()==master );
+ i++;
+ }
+ assertTrue( "master-detail", i==2 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ assertTrue( s.find("select elements(master.details) from Master master").size()==2 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ List list = s.find("from Master m left join fetch m.details");
+ Master m = (Master) list.get(0);
+ assertTrue( Hibernate.isInitialized( m.getDetails() ) );
+ assertTrue( m.getDetails().size()==2 );
+ list = s.find("from Detail d inner join fetch d.master");
+ Detail dt = (Detail) list.get(0);
+ Serializable dtid = s.getIdentifier(dt);
+ assertTrue( dt.getMaster()==m );
+
+ //assertTrue(m.getAllDetails().size()==2);
+
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ list = s.find("select m from Master m1, Master m left join fetch m.details where m.name=m1.name");
+ assertTrue( Hibernate.isInitialized( ( (Master) list.get(0) ).getDetails() ) );
+ dt = (Detail) s.load(Detail.class, dtid);
+ assertTrue( ( (Master) list.get(0) ).getDetails().contains(dt) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ list = s.find("select m, m1.name from Master m1, Master m left join fetch m.details where m.name=m1.name");
+ assertTrue( Hibernate.isInitialized( ( (Master) ( (Object[]) list.get(0) )[0] ).getDetails() ) );
+ dt = (Detail) s.load(Detail.class, dtid);
+ assertTrue( ( (Master) ( (Object[]) list.get(0) )[0] ).getDetails().contains(dt) );
+ //list = s.find("select m from Master m, Master m2 left join fetch m.details");
+// depracted syntax
+// list = s.find("select m.id from Master m inner join fetch m.details");
+ t.commit();
+ s.close();
+
+
+ s = openSession();
+ t = s.beginTransaction();
+ Detail dd = (Detail) s.load(Detail.class, did);
+ master = dd.getMaster();
+ assertTrue( "detail-master", master.getDetails().contains(dd) );
+ assertTrue( s.filter( master.getDetails(), "order by this.i desc").size()==2 );
+ assertTrue( s.filter( master.getDetails(), "select this where this.id > -1").size()==2 );
+ Query q = s.createFilter( master.getDetails(), "where this.id > :id" );
+ q.setInteger("id", -1);
+ assertTrue( q.list().size()==2 );
+ q = s.createFilter( master.getDetails(), "where this.id > :id1 and this.id < :id2" );
+ q.setInteger("id1", -1);
+ q.setInteger("id2", 99999999);
+ assertTrue( q.list().size()==2 );
+ q.setInteger("id2", -1);
+ assertTrue( q.list().size()==0 );
+ q = s.createFilter( master.getDetails(), "where this.id in (:ids)" );
+ list = new ArrayList();
+ list.add(did);
+ list.add( new Long(-1) );
+ q.setParameterList("ids", list);
+ assertTrue( q.list().size()==1 );
+ assertTrue( q.iterate().hasNext() );
+ assertTrue( s.filter( master.getDetails(), "where this.id > -1").size()==2 );
+ assertTrue( s.filter( master.getDetails(), "select this.master where this.id > -1").size()==2 );
+ assertTrue( s.filter( master.getDetails(), "select m from Master m where this.id > -1 and this.master=m").size()==2 );
+ assertTrue( s.filter( master.getIncoming(), "where this.id > -1 and this.name is not null").size()==0 );
+
+ assertTrue( s.createFilter( master.getDetails(), "select max(this.i)" ).iterate().next() instanceof Integer );
+ assertTrue( s.createFilter( master.getDetails(), "select max(this.i) group by this.id" ).iterate().next() instanceof Integer );
+ assertTrue( s.createFilter( master.getDetails(), "select count(*)" ).iterate().next() instanceof Long );
+
+ assertTrue( s.createFilter( master.getDetails(), "select this.master" ).list().size()==2 );
+ assertTrue( s.filter( master.getMoreDetails(), "" ).size()==0 );
+ assertTrue( s.filter( master.getIncoming(), "" ).size()==0 );
+
+ Query f = s.createFilter( master.getDetails(), "select max(this.i) where this.i < :top and this.i>=:bottom" );
+ f.setInteger("top", 100);
+ f.setInteger("bottom", 0);
+ assertEquals( f.iterate().next(), new Integer(12) );
+ f.setInteger("top", 2);
+ assertEquals( f.iterate().next(), new Integer(0) );
+
+ f = s.createFilter( master.getDetails(), "select max(this.i) where this.i not in (:list)" );
+ Collection coll = new ArrayList();
+ coll.add( new Integer(-666) );
+ coll.add( new Integer(22) );
+ coll.add( new Integer(0) );
+ f.setParameterList("list", coll);
+ assertEquals( f.iterate().next(), new Integer(12) );
+
+ f = s.createFilter( master.getDetails(), "select max(this.i) where this.i not in (:list) and this.master.name = :listy2" );
+ f.setParameterList("list", coll);
+ f.setParameter( "listy2", master.getName() );
+ assertEquals( f.iterate().next(), new Integer(12) );
+
+ iter = master.getDetails().iterator();
+ i=0;
+ while ( iter.hasNext() ) {
+ Detail d = (Detail) iter.next();
+ assertTrue( "master-detail", d.getMaster()==master );
+ s.delete(d);
+ i++;
+ }
+ assertTrue( "master-detail", i==2 );
+ s.delete(master);
+ t.commit();
+ s.close();
+ }
+
+ public void testIncomingOutgoing() throws Exception {
+
+ Session s = openSession();
+ Master master1 = new Master();
+ Master master2 = new Master();
+ Master master3 = new Master();
+ s.save(master1);
+ s.save(master2);
+ s.save(master3);
+ master1.addIncoming(master2);
+ master2.addOutgoing(master1);
+ master1.addIncoming(master3);
+ master3.addOutgoing(master1);
+ Serializable m1id = s.getIdentifier(master1);
+ assertTrue( s.filter( master1.getIncoming(), "where this.id > 0 and this.name is not null").size()==2 );
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ master1 = (Master) s.load(Master.class, m1id);
+ Iterator iter = master1.getIncoming().iterator();
+ int i=0;
+ while ( iter.hasNext() ) {
+ Master m = (Master) iter.next();
+ assertTrue( "outgoing", m.getOutgoing().size()==1 );
+ assertTrue( "outgoing", m.getOutgoing().contains(master1) );
+ s.delete(m);
+ i++;
+ }
+ assertTrue( "incoming-outgoing", i==2 );
+ s.delete(master1);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testCascading() throws Exception {
+
+ Session s = openSession();
+ Detail d1 = new Detail();
+ Detail d2 = new Detail();
+ d2.setI(22);
+ Master m = new Master();
+ Master m0 = new Master();
+ Serializable m0id = s.save(m0);
+ m0.addDetail(d1); m0.addDetail(d2);
+ d1.setMaster(m0); d2.setMaster(m0);
+ m.getMoreDetails().add(d1);
+ m.getMoreDetails().add(d2);
+ Serializable mid = s.save(m);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ m = (Master) s.load(Master.class, mid);
+ assertTrue( "cascade save", m.getMoreDetails().size()==2 );
+ assertTrue( "cascade save", ( (Detail) m.getMoreDetails().iterator().next() ).getMaster().getDetails().size()==2 );
+
+ s.delete(m);
+ s.delete( s.load(Master.class, m0id) );
+
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testNamedQuery() throws Exception {
+ Session s = openSession();
+ Query q = s.getNamedQuery("all_details");
+ q.list();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testUpdateLazyCollections() throws Exception {
+ Session s = openSession();
+ Master m = new Master();
+ Serializable mid = s.save(m);
+ Detail d1 = new Detail();
+ Detail d2 = new Detail();
+ d2.setX(14);
+ d1.setMaster(m);
+ d2.setMaster(m);
+ s.save(d1);
+ s.save(d2);
+ m.addDetail(d1);
+ m.addDetail(d2);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ m = (Master) s.load(Master.class, mid);
+ s.connection().commit();
+ s.close();
+ m.setName("New Name");
+ s = openSession();
+ s.update(m, mid);
+ Iterator iter = m.getDetails().iterator();
+ int i=0;
+ while ( iter.hasNext() ) {
+ assertTrue( iter.next()!=null );
+ i++;
+ }
+ assertTrue(i==2);
+ iter = m.getDetails().iterator();
+ while ( iter.hasNext() ) s.delete( iter.next() );
+ s.delete(m);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testMultiLevelCascade() throws Exception {
+ Session s = openSession();
+ Transaction txn = s.beginTransaction();
+ Detail detail = new Detail();
+ SubDetail subdetail = new SubDetail();
+ Master m = new Master();
+ Master m0 = new Master();
+ Serializable m0id = s.save(m0);
+ m0.addDetail(detail);
+ detail.setMaster(m0);
+ m.getMoreDetails().add(detail);
+ detail.setSubDetails( new HashSet() );
+ detail.getSubDetails().add(subdetail);
+ Serializable mid = s.save(m);
+ txn.commit();
+ s.close();
+
+ s = openSession();
+ txn = s.beginTransaction();
+ m = (Master) s.load( Master.class, mid );
+ assertTrue( ( (Detail) m.getMoreDetails().iterator().next() ).getSubDetails().size()!=0 );
+ s.delete(m);
+ assertTrue( s.find("from SubDetail").size()==0 );
+ assertTrue( s.find("from Detail d").size()==0 );
+ s.delete( s.load(Master.class, m0id) );
+ txn.commit();
+ s.close();
+ }
+
+ public void testMixNativeAssigned() throws Exception {
+
+ Session s = openSession();
+ Category c = new Category();
+ c.setName("NAME");
+ Assignable assn = new Assignable();
+ assn.setId("i.d.");
+ List l = new ArrayList();
+ l.add(c);
+ assn.setCategories(l);
+ c.setAssignable(assn);
+ s.save(assn);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ s.delete(assn);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testCollectionReplaceOnUpdate() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Category c = new Category();
+ List list = new ArrayList();
+ c.setSubcategories(list);
+ list.add( new Category() );
+ s.save(c);
+ t.commit();
+ s.close();
+ c.setSubcategories(list);
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.update(c);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Category) s.load( Category.class, new Long( c.getId() ), LockMode.UPGRADE );
+ List list2 = c.getSubcategories();
+ t.commit();
+ s.close();
+
+ assertTrue( !Hibernate.isInitialized( c.getSubcategories() ) );
+
+ c.setSubcategories(list2);
+ s = openSession();
+ t = s.beginTransaction();
+ s.update(c);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Category) s.load( Category.class, new Long( c.getId() ), LockMode.UPGRADE );
+ assertTrue( c.getSubcategories().size()==1 );
+ s.delete(c);
+ t.commit();
+ s.close();
+ }
+
+ public void testCollectionReplace2() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Category c = new Category();
+ List list = new ArrayList();
+ c.setSubcategories(list);
+ list.add( new Category() );
+ Category c2 = new Category();
+ s.save(c2);
+ s.save(c);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Category) s.load( Category.class, new Long( c.getId() ), LockMode.UPGRADE );
+ List list2 = c.getSubcategories();
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c2 = (Category) s.load( Category.class, new Long( c2.getId() ), LockMode.UPGRADE );
+ c2.setSubcategories(list2);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c2 = (Category) s.load( Category.class, new Long( c2.getId() ), LockMode.UPGRADE );
+ assertTrue( c2.getSubcategories().size()==1 );
+ s.delete(c2);
+ s.delete( s.load( Category.class, new Long( c.getId() ) ) );
+ t.commit();
+ s.close();
+ }
+
+ public void testCollectionReplace() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Category c = new Category();
+ List list = new ArrayList();
+ c.setSubcategories(list);
+ list.add( new Category() );
+ s.save(c);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Category) s.load( Category.class, new Long( c.getId() ), LockMode.UPGRADE );
+ c.setSubcategories(list);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Category) s.load( Category.class, new Long( c.getId() ), LockMode.UPGRADE );
+ List list2 = c.getSubcategories();
+ t.commit();
+ s.close();
+
+ assertTrue( !Hibernate.isInitialized( c.getSubcategories() ) );
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Category) s.load( Category.class, new Long( c.getId() ), LockMode.UPGRADE );
+ c.setSubcategories(list2);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Category) s.load( Category.class, new Long( c.getId() ), LockMode.UPGRADE );
+ assertTrue( c.getSubcategories().size()==1 );
+ s.delete(c);
+ t.commit();
+ s.close();
+ }
+
+ public void testCategories() throws Exception {
+ Session s = openSession();
+ Category c = new Category();
+ c.setName(Category.ROOT_CATEGORY);
+ Category c1 = new Category();
+ Category c2 = new Category();
+ Category c3 = new Category();
+ c.getSubcategories().add(c1);
+ c.getSubcategories().add(c2);
+ c2.getSubcategories().add(null);
+ c2.getSubcategories().add(c3);
+ s.save(c);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ Transaction tx = s.beginTransaction();
+ s.lock(c, LockMode.UPGRADE);
+ Category loaded = (Category) s.load( Category.class, new Long( c3.getId() ) );
+ assertTrue( s.contains(c3) );
+ assertTrue(loaded==c3);
+ assertTrue( s.getCurrentLockMode(c3)==LockMode.NONE );
+ assertTrue( s.getCurrentLockMode(c)==LockMode.UPGRADE );
+ s.flush();
+ tx.commit();
+ s.close();
+
+ s = openSession();
+ loaded = (Category) s.load( Category.class, new Long( c.getId() ) );
+ assertFalse( Hibernate.isInitialized( loaded.getSubcategories() ) );
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ s.lock(loaded, LockMode.NONE);
+ assertTrue( loaded.getSubcategories().size()==2 );
+ s.connection().commit();
+ s.close();
+
+
+ s = openSession();
+ c = (Category) s.load( Category.class, new Long( c.getId() ) );
+ System.out.println( c.getSubcategories() );
+ assertTrue( c.getSubcategories().get(0)!=null && c.getSubcategories().get(1)!=null );
+ List list = ( (Category) c.getSubcategories().get(1) ).getSubcategories();
+ assertTrue( list.get(1)!=null && list.get(0)==null );
+
+ assertTrue(
+ s.iterate("from Category c where c.name = org.hibernate.test.legacy.Category.ROOT_CATEGORY").hasNext()
+ );
+ s.delete(c);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ }
+
+ public void testCollectionRefresh() throws Exception {
+ Session s = openSession();
+ Category c = new Category();
+ List list = new ArrayList();
+ c.setSubcategories(list);
+ list.add( new Category() );
+ c.setName("root");
+ Serializable id = s.save(c);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ c = (Category) s.load(Category.class, id);
+ s.refresh(c);
+ s.flush();
+ assertTrue( c.getSubcategories().size()==1 );
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ c = (Category) s.load(Category.class, id);
+ assertTrue( c.getSubcategories().size()==1 );
+ s.delete(c);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testCachedCollectionRefresh() throws Exception {
+ Session s = openSession();
+ Category c = new Category();
+ List list = new ArrayList();
+ c.setSubcategories(list);
+ list.add( new Category() );
+ c.setName("root");
+ Serializable id = s.save(c);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ c = (Category) s.load(Category.class, id);
+ c.getSubcategories().size(); //force load and cache
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ if ( (getDialect() instanceof MySQLDialect) ) s.connection().setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
+ c = (Category) s.load(Category.class, id);
+ c.getSubcategories().size(); //force load
+
+ Session ss = openSession();
+ Category c2 = (Category) ss.load(Category.class, id);
+ ss.delete( c2.getSubcategories().get(0) );
+ c2.getSubcategories().clear();
+ ss.flush();
+ ss.connection().commit();
+ ss.close();
+
+ s.refresh(c);
+ assertTrue( c.getSubcategories().size()==0 );
+
+ ss = openSession();
+ c2 = (Category) ss.load(Category.class, id);
+ c2.getSubcategories().add( new Category() );
+ c2.getSubcategories().add( new Category() );
+ ss.flush();
+ ss.connection().commit();
+ ss.close();
+
+ s.refresh(c);
+ assertEquals( 2, c.getSubcategories().size() );
+
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ c = (Category) s.load(Category.class, id);
+ assertEquals( 2, c.getSubcategories().size() );
+ s.delete(c);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testCustomPersister() throws Exception {
+ Session s = openSession();
+ Custom c = new Custom();
+ c.setName( "foo" );
+ c.id="100";
+ String id = (String) s.save(c);
+ assertTrue( c==s.load(Custom.class, id) );
+ s.flush();
+ s.close();
+ s = openSession();
+ c = (Custom) s.load(Custom.class, id);
+ assertTrue( c.getName().equals("foo") );
+ c.setName( "bar" );
+ s.flush();
+ s.close();
+ s = openSession();
+ c = (Custom) s.load(Custom.class, id);
+ assertTrue( c.getName().equals("bar") );
+ s.delete(c);
+ s.flush();
+ s.close();
+ s = openSession();
+ boolean none = false;
+ try {
+ s.load(Custom.class, id);
+ }
+ catch (ObjectNotFoundException onfe) {
+ none=true;
+ }
+ assertTrue(none);
+ s.close();
+
+ }
+
+ public void testInterface() throws Exception {
+ Session s = openSession();
+ Serializable id = s.save( new BasicNameable() );
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ Nameable n = (Nameable) s.load(Nameable.class, id);
+ s.delete(n);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testNoUpdateManyToOne() throws Exception {
+ Session s = openSession();
+ W w1 = new W();
+ W w2 = new W();
+ Z z = new Z();
+ z.setW(w1);
+ s.save(z);
+ s.flush();
+ z.setW(w2);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ s.update(z);
+ s.flush();
+ s.delete(z);
+ s.delete("from W");
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testQueuedBagAdds() throws Exception {
+ Session s = openSession();
+ Assignable a = new Assignable();
+ a.setId("foo");
+ a.setCategories( new ArrayList() );
+ Category c = new Category();
+ c.setAssignable(a);
+ a.getCategories().add(c);
+ s.save(a);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ getSessions().evictCollection("org.hibernate.test.legacy.Assignable.categories");
+
+ s = openSession();
+ a = (Assignable) s.get(Assignable.class, "foo");
+ c = new Category();
+ c.setAssignable(a);
+ a.getCategories().add(c);
+ assertFalse( Hibernate.isInitialized( a.getCategories() ) );
+ assertTrue( a.getCategories().size()==2 );
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ getSessions().evictCollection("org.hibernate.test.legacy.Assignable.categories");
+
+ s = openSession();
+ a = (Assignable) s.get(Assignable.class, "foo");
+ c = new Category();
+ c.setAssignable(a);
+ a.getCategories().add(c);
+ assertFalse( Hibernate.isInitialized( a.getCategories() ) );
+ s.flush();
+ assertFalse( Hibernate.isInitialized( a.getCategories() ) );
+ assertTrue( a.getCategories().size()==3 );
+ s.connection().commit();
+ s.close();
+
+ getSessions().evictCollection("org.hibernate.test.legacy.Assignable.categories");
+
+ s = openSession();
+ a = (Assignable) s.get(Assignable.class, "foo");
+ assertTrue( a.getCategories().size()==3 );
+ s.delete(a);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ }
+
+ public void testPolymorphicCriteria() throws Exception {
+ Session s = openSession();
+ Transaction txn = s.beginTransaction();
+ Category f = new Category();
+ Single b = new Single();
+ b.setId("asdfa");
+ b.setString("asdfasdf");
+ s.save(f);
+ s.save(b);
+ List list = s.createCriteria(Object.class).list();
+ assertTrue( list.size()==2 );
+ assertTrue( list.contains(f) && list.contains(b) );
+ s.delete(f);
+ s.delete(b);
+ txn.commit();
+ s.close();
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Medication.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Medication.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Medication.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,20 @@
+package org.hibernate.test.legacy;
+
+/**
+ * @author hbm2java
+ */
+public class Medication extends org.hibernate.test.legacy.Intervention {
+
+ org.hibernate.test.legacy.Drug prescribedDrug;
+
+
+ org.hibernate.test.legacy.Drug getPrescribedDrug() {
+ return prescribedDrug;
+ }
+
+ void setPrescribedDrug(org.hibernate.test.legacy.Drug newValue) {
+ prescribedDrug = newValue;
+ }
+
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Middle.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Middle.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Middle.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping default-lazy="false">
+
+ <class name="org.hibernate.test.legacy.Inner" table="CID_IN">
+ <composite-id name="id">
+ <key-property name="akey" type="string" length="10" column="AKEY"/>
+ <key-property name="bkey" type="string" length="10" column="BKEY"/>
+ </composite-id>
+ <property name="dudu" length="10" column="DUDU" not-null="true" type="string"/>
+ <bag name="middles" lazy="true" inverse="true">
+ <key>
+ <column length="10" name="AKEY"/>
+ <column length="10" name="BKEY"/>
+ </key>
+ <one-to-many class="org.hibernate.test.legacy.Middle"/>
+ </bag>
+ <many-to-one name="backOut">
+ <column name="BO_AKEY"/>
+ <column name="BO_BKEY"/>
+ <column name="BO_ONE"/>
+ <column name="BO_TWO"/>
+ <column name="BO_DET"/>
+ </many-to-one>
+ </class>
+
+ <class name="org.hibernate.test.legacy.Middle" table="CID_MID">
+ <composite-id name="id">
+ <key-many-to-one name="sup">
+ <column length="10" name="AKEY"/>
+ <column length="10" name="BKEY"/>
+ </key-many-to-one>
+ <key-property name="one" type="string" length="10" column="ONE"/>
+ <key-property name="two" type="string" length="10" column="TWO"/>
+ </composite-id>
+ <property column="BLA" length="10" name="bla" not-null="true" type="string"/>
+ </class>
+
+ <class name="org.hibernate.test.legacy.Outer" table="CID_OUT">
+ <composite-id name="id">
+ <key-many-to-one name="master">
+ <column length="10" name="AKEY"/>
+ <column length="10" name="BKEY"/>
+ <column length="10" name="ONE"/>
+ <column length="10" name="TWO"/>
+ </key-many-to-one>
+ <key-property name="detailId" type="string" column="ID" length="10"/>
+ </composite-id>
+ <property name="bubu" column="BUBU" length="10" type="string"/>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Middle.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Middle.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Middle.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,42 @@
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+
+/**
+ * @author Stefano Travelli
+ */
+public class Middle implements Serializable {
+ private MiddleKey id;
+ private String bla;
+
+ public MiddleKey getId() {
+ return id;
+ }
+
+ public void setId(MiddleKey id) {
+ this.id = id;
+ }
+
+ public String getBla() {
+ return bla;
+ }
+
+ public void setBla(String bla) {
+ this.bla = bla;
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof Middle)) return false;
+
+ final Middle cidMaster = (Middle) o;
+
+ if (id != null ? !id.equals(cidMaster.id) : cidMaster.id != null) return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ return (id != null ? id.hashCode() : 0);
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MiddleKey.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MiddleKey.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MiddleKey.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,57 @@
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+
+/**
+ * @author Stefano Travelli
+ */
+public class MiddleKey implements Serializable {
+ private Inner sup;
+ private String one;
+ private String two;
+
+ public String getOne() {
+ return one;
+ }
+
+ public void setOne(String one) {
+ this.one = one;
+ }
+
+ public String getTwo() {
+ return two;
+ }
+
+ public void setTwo(String two) {
+ this.two = two;
+ }
+
+ public Inner getSup() {
+ return sup;
+ }
+
+ public void setSup(Inner sup) {
+ this.sup = sup;
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof MiddleKey)) return false;
+
+ final MiddleKey cidMasterID = (MiddleKey) o;
+
+ if (one != null ? !one.equals(cidMasterID.one) : cidMasterID.one != null) return false;
+ if (sup != null ? !sup.equals(cidMasterID.sup) : cidMasterID.sup != null) return false;
+ if (two != null ? !two.equals(cidMasterID.two) : cidMasterID.two != null) return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ int result;
+ result = (sup != null ? sup.hashCode() : 0);
+ result = 29 * result + (one != null ? one.hashCode() : 0);
+ result = 29 * result + (two != null ? two.hashCode() : 0);
+ return result;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Mono.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Mono.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Mono.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,42 @@
+//$Id: Mono.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.util.Set;
+
+
+public class Mono extends Top {
+
+ private Set strings;
+
+ /**
+ * Constructor for Mono.
+ * @param c
+ */
+ public Mono(int c) {
+ super(c);
+ }
+
+ /**
+ * Constructor for Mono.
+ */
+ public Mono() {
+ super();
+ }
+
+ /**
+ * Returns the strings.
+ * @return Set
+ */
+ public Set getStrings() {
+ return strings;
+ }
+
+ /**
+ * Sets the strings.
+ * @param strings The strings to set
+ */
+ public void setStrings(Set strings) {
+ this.strings = strings;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MoreStuff.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MoreStuff.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MoreStuff.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,92 @@
+//$Id: MoreStuff.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+import java.util.Collection;
+
+
+public class MoreStuff implements Serializable {
+ private String stringId;
+ private int intId;
+ private Collection stuffs;
+ private String name;
+
+ public boolean equals(Object other) {
+ return ( (MoreStuff) other ).getIntId()==intId && ( (MoreStuff) other ).getStringId().equals(stringId);
+ }
+
+ public int hashCode() {
+ return stringId.hashCode();
+ }
+
+ /**
+ * Returns the stuffs.
+ * @return Collection
+ */
+ public Collection getStuffs() {
+ return stuffs;
+ }
+
+ /**
+ * Sets the stuffs.
+ * @param stuffs The stuffs to set
+ */
+ public void setStuffs(Collection stuffs) {
+ this.stuffs = stuffs;
+ }
+
+ /**
+ * Returns the name.
+ * @return String
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the name.
+ * @param name The name to set
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Returns the intId.
+ * @return int
+ */
+ public int getIntId() {
+ return intId;
+ }
+
+ /**
+ * Returns the stringId.
+ * @return String
+ */
+ public String getStringId() {
+ return stringId;
+ }
+
+ /**
+ * Sets the intId.
+ * @param intId The intId to set
+ */
+ public void setIntId(int intId) {
+ this.intId = intId;
+ }
+
+ /**
+ * Sets the stringId.
+ * @param stringId The stringId to set
+ */
+ public void setStringId(String stringId) {
+ this.stringId = stringId;
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Multi.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Multi.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Multi.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,101 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping default-lazy="false">
+
+ <class
+ name="org.hibernate.test.legacy.Top"
+ table="rootclass"
+ dynamic-insert="true"
+ dynamic-update="true"
+ select-before-update="true"
+ where="id1_ is not null">
+ <!--cache-->
+ <id name="id" type="long" column="id1_" unsaved-value="0">
+ <generator class="native"/>
+ </id>
+ <version name="count" column="count_"/>
+ <property name="name"/>
+ <property name="address"/>
+ <property name="date" column="date_"/>
+ <many-to-one name="top" access="field" column="top_"/>
+
+ <joined-subclass name="org.hibernate.test.legacy.TrivialClass">
+ <key column="tcid"/>
+ </joined-subclass>
+
+ <joined-subclass
+ name="org.hibernate.test.legacy.Lower"
+ table="leafsubclass"
+ dynamic-insert="true"
+ dynamic-update="true">
+ <key column="id__"/>
+ <property name="intprop" not-null="true"/>
+ <one-to-one name="other" class="org.hibernate.test.legacy.Lower"/>
+ <many-to-one name="another" column="other1" class="org.hibernate.test.legacy.Top"/>
+ <many-to-one name="yetanother" column="other3" class="org.hibernate.test.legacy.Lower"/>
+ <property name="foo"/>
+ <set name="set" lazy="false">
+ <key column="parent"/>
+ <one-to-many class="org.hibernate.test.legacy.Top"/>
+ </set>
+ <bag name="bag" lazy="true" table="simple_simple">
+ <key column="simple1"/>
+ <many-to-many column="simple2" class="org.hibernate.test.legacy.Top"/>
+ </bag>
+ <one-to-one name="mypo"/>
+ </joined-subclass>
+
+ <joined-subclass name="org.hibernate.test.legacy.Multi" table="nonleafsubclass">
+ <key column="sid" on-delete="cascade"/>
+ <property name="extraProp" column="dupe"/>
+ <many-to-one name="other" column="other2" class="org.hibernate.test.legacy.Multi" />
+ <many-to-one name="po"/>
+ <property name="derived" formula="upper(dupe)"/>
+
+ <component name="comp" class="org.hibernate.test.legacy.Multi$Component">
+ <property name="cal"/>
+ <property name="floaty"/>
+ </component>
+
+ <joined-subclass name="org.hibernate.test.legacy.SubMulti" table="leafsubsubclass">
+ <key column="sid"/>
+ <property name="amount" column="dupe"/>
+ <bag name="children" lazy="true" inverse="true">
+ <key column="parent"/>
+ <one-to-many class="org.hibernate.test.legacy.SubMulti"/>
+ </bag>
+ <many-to-one name="parent"/>
+ <list name="moreChildren" lazy="true">
+ <key column="another_parent"/>
+ <index column="list_ind"/>
+ <one-to-many class="org.hibernate.test.legacy.SubMulti"/>
+ </list>
+ </joined-subclass>
+
+ </joined-subclass>
+
+ </class>
+
+ <class
+ name="org.hibernate.test.legacy.Po"
+ dynamic-insert="true">
+ <id type="long" column="id_">
+ <generator class="native"/>
+ </id>
+ <property name="value" column="value_"/>
+ <list name="list" cascade="all">
+ <key column="list_po"/>
+ <index column="i"/>
+ <one-to-many class="org.hibernate.test.legacy.SubMulti"/>
+ </list>
+ <set name="set" inverse="true" cascade="all">
+ <key column="po"/>
+ <one-to-many class="org.hibernate.test.legacy.Multi"/>
+ </set>
+ <many-to-one name="top" column="tl" insert="false" update="false"/>
+ <many-to-one name="lower" column="tl"/>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Multi.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Multi.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Multi.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,120 @@
+//$Id: Multi.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.util.Calendar;
+
+
+public class Multi extends Top {
+
+ /**
+ * Constructor for Multi.
+ */
+ public Multi() {
+ super();
+ }
+
+ private String extraProp;
+ private String derived;
+ private Component comp;
+ private Po po;
+
+ /**
+ * Returns the extraProp.
+ * @return String
+ */
+ public String getExtraProp() {
+ return extraProp;
+ }
+
+ /**
+ * Sets the extraProp.
+ * @param extraProp The extraProp to set
+ */
+ public void setExtraProp(String extraProp) {
+ this.extraProp = extraProp;
+ }
+
+ public static final class Component {
+ private Calendar cal;
+ private Float floaty;
+ /**
+ * Returns the cal.
+ * @return Calendar
+ */
+ public Calendar getCal() {
+ return cal;
+ }
+
+ /**
+ * Sets the cal.
+ * @param cal The cal to set
+ */
+ public void setCal(Calendar cal) {
+ this.cal = cal;
+ }
+
+ /**
+ * Returns the floaty.
+ * @return Float
+ */
+ public Float getFloaty() {
+ return floaty;
+ }
+
+ /**
+ * Sets the floaty.
+ * @param floaty The floaty to set
+ */
+ public void setFloaty(Float floaty) {
+ this.floaty = floaty;
+ }
+
+ }
+
+ /**
+ * Returns the comp.
+ * @return Component
+ */
+ public Component getComp() {
+ return comp;
+ }
+
+ /**
+ * Sets the comp.
+ * @param comp The comp to set
+ */
+ public void setComp(Component comp) {
+ this.comp = comp;
+ }
+
+ /**
+ * Returns the po.
+ * @return Po
+ */
+ public Po getPo() {
+ return po;
+ }
+
+ /**
+ * Sets the po.
+ * @param po The po to set
+ */
+ public void setPo(Po po) {
+ this.po = po;
+ }
+
+ public String getDerived() {
+ return derived;
+ }
+
+ public void setDerived(String derived) {
+ this.derived = derived;
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MultiExtends.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MultiExtends.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MultiExtends.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping default-lazy="false">
+
+ <joined-subclass
+ name="org.hibernate.test.legacy.Mono"
+ extends="org.hibernate.test.legacy.Top"
+ table="mono"
+ dynamic-insert="true"
+ dynamic-update="true">
+ <key column="superid"/>
+ <set name="strings" table="monostrings">
+ <key column="monoid_"/>
+ <element type="string" column="str_" not-null="true"/>
+ </set>
+ </joined-subclass>
+
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MultiTableTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MultiTableTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MultiTableTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,622 @@
+//$Id: MultiTableTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import junit.framework.Test;
+
+import org.hibernate.Criteria;
+import org.hibernate.FetchMode;
+import org.hibernate.LockMode;
+import org.hibernate.Transaction;
+import org.hibernate.classic.Session;
+import org.hibernate.criterion.Expression;
+import org.hibernate.dialect.MySQLDialect;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+
+public class MultiTableTest extends LegacyTestCase {
+
+ public MultiTableTest(String arg0) {
+ super(arg0);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "legacy/Multi.hbm.xml", "legacy/MultiExtends.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( MultiTableTest.class );
+ }
+
+ public void testCriteria() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Lower l = new Lower();
+ s.save(l);
+ assertTrue( l==s.createCriteria(Top.class).uniqueResult() );
+ s.delete(l);
+ s.flush();
+ Criteria c = s.createCriteria(Lower.class);
+ c.createCriteria("yetanother")
+ .add( Expression.isNotNull("id") )
+ .createCriteria("another");
+ c.createCriteria("another").add( Expression.isNotNull("id") );
+ c.list();
+ t.commit();
+ s.close();
+ }
+
+ public void testFetchOneToMany() throws Exception {
+ Session s = openSession();
+ s.createCriteria(Po.class).setFetchMode("set", FetchMode.EAGER).list();
+ s.createCriteria(Po.class).setFetchMode("list", FetchMode.EAGER).list();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testNarrow() throws Exception {
+ Session s = openSession();
+ s.find("from Po po, Lower low where low.mypo = po");
+ s.find("from Po po join po.set as sm where sm.amount > 0");
+ s.find("from Po po join po.top as low where low.foo = 'po'");
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testJoins() throws Exception {
+ Session s = openSession();
+ s.find("from Lower l join l.yetanother l2 where lower(l2.name) > 'a'");
+ s.find("from Lower l where lower(l.yetanother.top.name) > 'a'");
+ s.find("from SubMulti sm join sm.children smc where smc.name > 'a'");
+ s.find("select s, ya from Lower s join s.yetanother ya");
+ s.find("from Lower s1 join s1.bag s2");
+ s.find("from Lower s1 left join s1.bag s2");
+ s.find("select s, a from Lower s join s.another a");
+ s.find("select s, a from Lower s left join s.another a");
+ s.find("from Top s, Lower ls");
+ s.find("from Lower ls join ls.set s where s.name > 'a'");
+ s.find("from Po po join po.list sm where sm.name > 'a'");
+ s.find("from Lower ls inner join ls.another s where s.name is not null");
+ s.find("from Lower ls where ls.other.another.name is not null");
+ s.find("from Multi m where m.derived like 'F%'");
+ s.find("from SubMulti m where m.derived like 'F%'");
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testSubclassCollection() throws Exception {
+ //if ( getDialect() instanceof HSQLDialect ) return; //TODO: figure out why!?
+ Session s = openSession();
+ SubMulti sm = new SubMulti();
+ SubMulti sm1 = new SubMulti();
+ SubMulti sm2 = new SubMulti();
+ ArrayList list = new ArrayList();
+ ArrayList anotherList = new ArrayList();
+ sm.setChildren(list);
+ sm.setMoreChildren(anotherList);
+ sm.setExtraProp("foo");
+ list.add(sm1);
+ list.add(sm2);
+ anotherList.add(sm1);
+ anotherList.add(sm2);
+ sm1.setParent(sm);
+ sm2.setParent(sm);
+ Serializable id = s.save(sm);
+ s.save(sm1);
+ s.save(sm2);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ getSessions().evict(SubMulti.class);
+
+ s = openSession();
+ s.connection().createStatement().executeQuery(
+ "select * from leafsubsubclass sm, nonleafsubclass m, rootclass s where sm.sid=m.sid and sm.sid=s.id1_ and sm.sid=1"
+ ).next();
+ assertTrue( s.find("select s from SubMulti as sm join sm.children as s where s.amount>-1 and s.name is null").size()==2 );
+ s.find("select c from SubMulti sm join sm.children c");
+ assertTrue( s.find("select elements(sm.children) from SubMulti as sm").size()==2 );
+ assertTrue( s.find("select distinct sm from SubMulti as sm join sm.children as s where s.amount>-1 and s.name is null").size()==1 );
+ sm = (SubMulti) s.load(SubMulti.class, id);
+ assertTrue( sm.getChildren().size()==2 );
+ assertEquals(
+ s.filter( sm.getMoreChildren(), "select count(*) where this.amount>-1 and this.name is null" ).iterator().next(),
+ new Long(2)
+ );
+ assertEquals( "FOO", sm.getDerived() );
+ assertSame(
+ s.iterate("select distinct s from SubMulti s where s.moreChildren[1].amount < 1.0").next(),
+ sm
+ );
+ assertTrue( sm.getMoreChildren().size()==2 );
+ s.delete(sm);
+ Iterator iter = sm.getChildren().iterator();
+ while ( iter.hasNext() ) s.delete( iter.next() );
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ }
+
+ public void testCollectionOnly() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Mono m = new Mono();
+ Long id = (Long) s.save(m);
+ t.commit();
+ s.close();
+ s = openSession();
+ t = s.beginTransaction();
+ s.update(m, id);
+ s.flush();
+ m.setAddress("foo bar");
+ s.flush();
+ s.delete(m);
+ t.commit();
+ s.close();
+ }
+
+ public void testQueries() throws Exception {
+ Session s = openSession();
+ Long id = ( Long ) s.save( new TrivialClass() );
+
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ TrivialClass tc = (TrivialClass) s.load(TrivialClass.class, id);
+ s.find("from TrivialClass s where s.id = 2");
+ s.find("select t.count from Top t");
+ s.find("from Lower s where s.another.name='name'");
+ s.find("from Lower s where s.yetanother.name='name'");
+ s.find("from Lower s where s.yetanother.name='name' and s.yetanother.foo is null");
+ s.find("from Top s where s.count=1");
+ s.find("select s.count from Top s, Lower ls where ls.another=s");
+ s.find("select elements(ls.bag), elements(ls.set) from Lower ls");
+ s.iterate("from Lower");
+ s.iterate("from Top");
+ s.delete(tc);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testConstraints() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ SubMulti sm = new SubMulti();
+ sm.setAmount(66.5f);
+ s.save( sm );
+ t.commit();
+ s.close();
+ s = openSession();
+ s.delete( "from SubMulti" );
+ t = s.beginTransaction();
+ t.commit();
+ s.close();
+ }
+
+ public void testMultiTable() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Multi multi = new Multi();
+ multi.setExtraProp("extra");
+ multi.setName("name");
+ Top simp = new Top();
+ simp.setDate( new Date() );
+ simp.setName("simp");
+
+ Serializable mid = s.save(multi);
+ Serializable sid = s.save(simp);
+
+ SubMulti sm = new SubMulti();
+ sm.setAmount(66.5f);
+ Serializable smid = s.save(sm);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ multi.setExtraProp( multi.getExtraProp() + "2" );
+ //multi.setCount( multi.getCount() + 1 );
+ multi.setName("new name");
+ s.update(multi, mid);
+ simp.setName("new name");
+ s.update(simp, sid);
+ sm.setAmount(456.7f);
+ s.update(sm, smid);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ multi = (Multi) s.load(Multi.class, mid);
+ assertTrue( multi.getExtraProp().equals("extra2") );
+ multi.setExtraProp( multi.getExtraProp() + "3" );
+ //multi.setCount( multi.getCount() + 1 );
+ assertTrue( multi.getName().equals("new name") );
+ multi.setName("newer name");
+ sm = (SubMulti) s.load(SubMulti.class, smid);
+ assertTrue( sm.getAmount()==456.7f );
+ sm.setAmount(23423f);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ multi = (Multi) s.load(Top.class, mid);
+ simp = (Top) s.load(Top.class, sid);
+ assertTrue( ! (simp instanceof Multi) );
+ assertTrue( multi.getExtraProp().equals("extra23") );
+ //multi.setCount( multi.getCount() + 1 );
+ assertTrue( multi.getName().equals("newer name") );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Iterator iter = s.iterate("select\n\nt from Top t where t.count>0");
+ boolean foundSimp = false;
+ boolean foundMulti = false;
+ boolean foundSubMulti = false;
+ while ( iter.hasNext() ) {
+ Object o = iter.next();
+ if ( ( o instanceof Top ) && !( o instanceof Multi) ) foundSimp = true;
+ if ( o instanceof Multi && !(o instanceof SubMulti) ) foundMulti = true;
+ if ( o instanceof SubMulti ) foundSubMulti = true;
+ }
+ assertTrue( foundSimp&&foundMulti&&foundSubMulti );
+ s.find("from Multi m where m.count>0 and m.extraProp is not null");
+ s.find("from Top m where m.count>0 and m.name is not null");
+ s.find("from Lower m where m.other is not null");
+ s.find("from Multi m where m.other.id = 1");
+ s.find("from SubMulti m where m.amount > 0.0");
+
+ assertTrue(
+ s.find("from Multi").size()==2
+ );
+ assertTrue(
+ s.find("from Multi m where m.class = SubMulti").size()==1
+ );
+ assertTrue(
+ s.find("from Top m where m.class = Multi").size()==1
+ );
+ assertTrue(
+ s.find("from Top").size()==3
+ );
+ assertTrue(
+ s.find("from Lower").size()==0
+ );
+ assertTrue(
+ s.find("from SubMulti").size()==1
+ );
+
+ s.find("from Lower ls join ls.bag s where s.id is not null");
+ s.find("from Lower ls join ls.set s where s.id is not null");
+ if ( !(getDialect() instanceof MySQLDialect) ) s.find("from SubMulti sm where exists elements(sm.children)");
+
+ List l = s.createCriteria(Top.class).list();
+ assertTrue( l.size()==3 );
+ assertTrue( s.createCriteria(SubMulti.class).list().size()==1 );
+ assertTrue(
+ s.createCriteria(SubMulti.class)
+ .add( Expression.lt("amount", new Float(0)) )
+ .list()
+ .size()==0
+ );
+ assertTrue(
+ s.createCriteria(SubMulti.class)
+ .add( Expression.ge("amount", new Float(0)) )
+ .list()
+ .size()==1
+ );
+
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ multi = (Multi) s.load(Top.class, mid, LockMode.UPGRADE);
+ simp = (Top) s.load(Top.class, sid);
+ s.lock(simp, LockMode.UPGRADE_NOWAIT);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.update(multi, mid);
+ s.delete(multi);
+ assertTrue( s.delete("from Top")==2);
+ t.commit();
+ s.close();
+
+ }
+
+ public void testMultiTableGeneratedId() throws Exception {
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Multi multi = new Multi();
+ multi.setExtraProp("extra");
+ //multi.setCount(666);
+ multi.setName("name");
+ Top simp = new Top();
+ simp.setDate( new Date() );
+ simp.setName("simp");
+ //simp.setCount(132);
+ Serializable multiId = s.save( multi );
+ Serializable simpId = s.save( simp );
+ SubMulti sm = new SubMulti();
+ sm.setAmount(66.5f);
+ Serializable smId = s.save( sm );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ multi.setExtraProp( multi.getExtraProp() + "2" );
+ //multi.setCount( multi.getCount() + 1 );
+ multi.setName("new name");
+ s.update( multi, multiId );
+ simp.setName("new name");
+ s.update( simp, simpId );
+ sm.setAmount(456.7f);
+ s.update( sm, smId );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ multi = (Multi) s.load( Multi.class, multiId );
+ assertTrue( multi.getExtraProp().equals("extra2") );
+ multi.setExtraProp( multi.getExtraProp() + "3" );
+ //multi.setCount( multi.getCount() + 1 );
+ assertTrue( multi.getName().equals("new name") );
+ multi.setName("newer name");
+ sm = (SubMulti) s.load( SubMulti.class, smId );
+ assertTrue( sm.getAmount()==456.7f );
+ sm.setAmount(23423f);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ multi = (Multi) s.load( Top.class, multiId );
+ simp = (Top) s.load( Top.class, simpId );
+ assertTrue( ! (simp instanceof Multi) );
+ assertTrue( multi.getExtraProp().equals("extra23") );
+ //multi.setCount( multi.getCount() + 1 );
+ assertTrue( multi.getName().equals("newer name") );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Iterator iter = s.iterate("select\n\nt from Top t where t.count>0");
+ boolean foundSimp = false;
+ boolean foundMulti = false;
+ boolean foundSubMulti = false;
+ while ( iter.hasNext() ) {
+ Object o = iter.next();
+ if ( ( o instanceof Top ) && !( o instanceof Multi) ) foundSimp = true;
+ if ( o instanceof Multi && !(o instanceof SubMulti) ) foundMulti = true;
+ if ( o instanceof SubMulti ) foundSubMulti = true;
+ }
+ assertTrue( foundSimp&&foundMulti&&foundSubMulti );
+ s.find("from Multi m where m.count>0 and m.extraProp is not null");
+ s.find("from Top m where m.count>0 and m.name is not null");
+ s.find("from Lower m where m.other is not null");
+ s.find("from Multi m where m.other.id = 1");
+ s.find("from SubMulti m where m.amount > 0.0");
+
+ assertTrue(
+ s.find("from Multi").size()==2
+ );
+ /*assertTrue(
+ s.find("from m in class Multi where m.class = Multi").size()==1
+ );*/
+ assertTrue(
+ s.find("from Top").size()==3
+ );
+ assertTrue(
+ s.find("from Lower").size()==0
+ );
+ assertTrue(
+ s.find("from SubMulti").size()==1
+ );
+
+ s.find("from Lower ls join ls.bag s where s.id is not null");
+ if ( !(getDialect() instanceof MySQLDialect) ) s.find("from SubMulti sm where exists elements(sm.children)");
+
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ multi = (Multi) s.load( Top.class, multiId, LockMode.UPGRADE );
+ simp = (Top) s.load( Top.class, simpId );
+ s.lock(simp, LockMode.UPGRADE_NOWAIT);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.update( multi, multiId );
+ s.delete(multi);
+ assertTrue( s.delete("from Top")==2);
+ t.commit();
+ s.close();
+
+ }
+
+ public void testMultiTableCollections() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ assertTrue( s.find("from Top").size()==0 );
+ Multi multi = new Multi();
+ multi.setExtraProp("extra");
+ multi.setName("name");
+ Top simp = new Top();
+ simp.setDate( new Date() );
+ simp.setName("simp");
+
+ s.save(multi);
+ s.save(simp);
+
+ Lower ls = new Lower();
+ ls.setOther(ls);
+ ls.setAnother(ls);
+ ls.setYetanother(ls);
+ ls.setName("Less Simple");
+ Set set = new HashSet();
+ ls.setSet(set);
+ set.add(multi);
+ set.add(simp);
+ Serializable id = s.save(ls);
+ t.commit();
+ s.close();
+ assertTrue( ls.getOther()==ls && ls.getAnother()==ls && ls.getYetanother()==ls );
+
+ s = openSession();
+ t = s.beginTransaction();
+ ls = (Lower) s.load(Lower.class, id);
+ assertTrue( ls.getOther()==ls && ls.getAnother()==ls && ls.getYetanother()==ls );
+ assertTrue( ls.getSet().size()==2 );
+ Iterator iter = ls.getSet().iterator();
+ int foundMulti = 0;
+ int foundSimple = 0;
+ while ( iter.hasNext() ) {
+ Object o = iter.next();
+ if ( o instanceof Top ) foundSimple++;
+ if ( o instanceof Multi ) foundMulti++;
+ }
+ assertTrue( foundSimple==2 && foundMulti==1 );
+ assertTrue( s.delete("from Top")==3 );
+ t.commit();
+ s.close();
+ }
+
+ public void testMultiTableManyToOne() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ assertTrue( s.find("from Top").size()==0 );
+ Multi multi = new Multi();
+ multi.setExtraProp("extra");
+ multi.setName("name");
+ Top simp = new Top();
+ simp.setDate( new Date() );
+ simp.setName("simp");
+ s.save(multi);
+ Lower ls = new Lower();
+ ls.setOther(ls);
+ ls.setAnother(multi);
+ ls.setYetanother(ls);
+ ls.setName("Less Simple");
+ Serializable id = s.save(ls);
+ t.commit();
+ s.close();
+ assertTrue( ls.getOther()==ls && ls.getAnother()==multi && ls.getYetanother()==ls );
+
+ s = openSession();
+ t = s.beginTransaction();
+ ls = (Lower) s.load(Lower.class, id);
+ assertTrue( ls.getOther()==ls && ls.getYetanother()==ls );
+ assertTrue( ls.getAnother().getName().equals("name") && ls.getAnother() instanceof Multi );
+ s.delete(ls);
+ s.delete( ls.getAnother() );
+ t.commit();
+ s.close();
+ }
+
+ public void testMultiTableNativeId() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Multi multi = new Multi();
+ multi.setExtraProp("extra");
+ Long id = (Long) s.save(multi);
+ assertTrue( id!=null );
+ s.delete(multi);
+ t.commit();
+ s.close();
+ }
+
+ public void testCollection() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Multi multi1 = new Multi();
+ multi1.setExtraProp("extra1");
+ Multi multi2 = new Multi();
+ multi2.setExtraProp("extra2");
+ Po po = new Po();
+ multi1.setPo(po); multi2.setPo(po);
+ po.setSet( new HashSet() );
+ po.getSet().add(multi1);
+ po.getSet().add(multi2);
+ po.setList( new ArrayList() );
+ //po.getList().add(null);
+ po.getList().add( new SubMulti() );
+ Serializable id = s.save(po);
+ assertTrue( id!=null );
+ t.commit();
+ s.close();
+ s = openSession();
+ t = s.beginTransaction();
+ po = (Po) s.load(Po.class, id);
+ assertTrue( po.getSet().size()==2 );
+ assertTrue( po.getList().size()==1 );
+ s.delete(po);
+ assertTrue( s.find("from Top").size()==0 );
+ t.commit();
+ s.close();
+ }
+
+ public void testOneToOne() throws Exception {
+ Session s = openSession();
+ Lower ls = new Lower();
+ Serializable id = s.save(ls);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ s.load(Lower.class, id);
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ s.delete( s.load(Lower.class, id) );
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testCollectionPointer() throws Exception {
+ Session sess = openSession();
+ Lower ls = new Lower();
+ List list = new ArrayList();
+ ls.setBag(list);
+ Top s = new Top();
+ Serializable id = sess.save(ls);
+ sess.save(s);
+ sess.flush();
+ list.add(s);
+ sess.flush();
+ sess.connection().commit();
+ sess.close();
+
+ sess = openSession();
+ ls = (Lower) sess.load(Lower.class, id);
+ assertTrue( ls.getBag().size()==1 );
+ sess.delete("from java.lang.Object");
+ sess.flush();
+ sess.connection().commit();
+ sess.close();
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Multiplicity.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Multiplicity.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Multiplicity.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,9 @@
+//$Id: Multiplicity.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+
+public class Multiplicity implements Serializable {
+ public int count;
+ public GlarchProxy glarch;
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MultiplicityType.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MultiplicityType.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/MultiplicityType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,150 @@
+//$Id: MultiplicityType.java 6592 2005-04-28 15:44:16Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.engine.ForeignKeys;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.type.Type;
+import org.hibernate.usertype.CompositeUserType;
+
+public class MultiplicityType implements CompositeUserType {
+
+ private static final String[] PROP_NAMES = new String[] {
+ "count", "glarch"
+ };
+ private static final int[] SQL_TYPES = new int[] {
+ Hibernate.INTEGER.sqlType(), Hibernate.STRING.sqlType()
+ };
+ private static final Type[] TYPES = new Type[] {
+ Hibernate.INTEGER, Hibernate.entity(Glarch.class)
+ };
+
+ public String[] getPropertyNames() {
+ return PROP_NAMES;
+ }
+
+ public Type[] getPropertyTypes() {
+ return TYPES;
+ }
+
+ public int hashCode(Object x) throws HibernateException {
+ Multiplicity o = (Multiplicity) x;
+ return o.count + o.glarch.hashCode();
+ }
+
+ public Object getPropertyValue(Object component, int property) {
+ Multiplicity o = (Multiplicity) component;
+ return property==0 ?
+ (Object) new Integer(o.count) :
+ (Object) o.glarch;
+ }
+
+ public void setPropertyValue(
+ Object component,
+ int property,
+ Object value) {
+
+ Multiplicity o = (Multiplicity) component;
+ if (property==0) {
+ o.count = ( (Integer) value ).intValue();
+ }
+ else {
+ o.glarch = (Glarch) value;
+ }
+ }
+
+ public int[] sqlTypes() {
+ return SQL_TYPES;
+ }
+
+ public Class returnedClass() {
+ return Multiplicity.class;
+ }
+
+ public boolean equals(Object x, Object y) {
+ Multiplicity mx = (Multiplicity) x;
+ Multiplicity my = (Multiplicity) y;
+ if (mx==my) return true;
+ if (mx==null || my==null) return false;
+ return mx.count==my.count && mx.glarch==my.glarch;
+ }
+
+ public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner)
+ throws HibernateException, SQLException {
+
+ Integer c = (Integer) Hibernate.INTEGER.nullSafeGet( rs, names[0] );
+ GlarchProxy g = (GlarchProxy) Hibernate.entity(Glarch.class).nullSafeGet(rs, names[1], session, owner);
+ Multiplicity m = new Multiplicity();
+ m.count = c==null ? 0 : c.intValue();
+ m.glarch = g;
+ return m;
+ }
+
+ public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session)
+ throws HibernateException, SQLException {
+
+ Multiplicity o = (Multiplicity) value;
+ GlarchProxy g;
+ Integer c;
+ if (o==null) {
+ g=null;
+ c=new Integer(0);
+ }
+ else {
+ g = o.glarch;
+ c = new Integer(o.count);
+ }
+ Hibernate.INTEGER.nullSafeSet(st, c, index, session);
+ Hibernate.entity(Glarch.class).nullSafeSet(st, g, index+1, session);
+
+ }
+
+ public Object deepCopy(Object value) {
+ if (value==null) return null;
+ Multiplicity v = (Multiplicity) value;
+ Multiplicity m = new Multiplicity();
+ m.count = v.count;
+ m.glarch = v.glarch;
+ return m;
+ }
+
+ public boolean isMutable() {
+ return true;
+ }
+
+ public Object assemble(
+ Serializable cached,
+ SessionImplementor session,
+ Object owner) throws HibernateException {
+ if (cached==null) return null;
+ Serializable[] o = (Serializable[]) cached;
+ Multiplicity m = new Multiplicity();
+ m.count = ( (Integer) o[0] ).intValue();
+ m.glarch = o[1]==null ?
+ null :
+ (GlarchProxy) session.internalLoad( Glarch.class.getName(), o[1], false, false );
+ return m;
+ }
+
+ public Serializable disassemble(Object value, SessionImplementor session)
+ throws HibernateException {
+ if (value==null) return null;
+ Multiplicity m = (Multiplicity) value;
+ return new Serializable[] {
+ new Integer(m.count),
+ ForeignKeys.getEntityIdentifierIfNotUnsaved( Glarch.class.getName(), m.glarch, session )
+ };
+ }
+
+ public Object replace(Object original, Object target, SessionImplementor session, Object owner)
+ throws HibernateException {
+ return assemble( disassemble(original, session), session, owner);
+ }
+
+}
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Nameable.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Nameable.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Nameable.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,16 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping default-lazy="false">
+
+ <class name="org.hibernate.test.legacy.Nameable">
+ <id name="key" column="key_">
+ <generator class="native"/>
+ </id>
+ <discriminator column="clazz"/>
+ <property name="name"/>
+ <subclass name="org.hibernate.test.legacy.BasicNameable"/>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Nameable.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Nameable.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Nameable.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,18 @@
+//$Id: Nameable.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+/**
+ *
+ */
+public interface Nameable {
+ public String getName();
+ public void setName(String name);
+ public Long getKey();
+ public void setKey(Long key);
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Named.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Named.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Named.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,13 @@
+//$Id: Named.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+
+public interface Named {
+ public String getName();
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/NestingComponent.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/NestingComponent.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/NestingComponent.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,15 @@
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+
+public class NestingComponent implements Serializable {
+ private ComponentCollection nested;
+ public ComponentCollection getNested() {
+ return nested;
+ }
+
+ public void setNested(ComponentCollection collection) {
+ nested = collection;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/NewPerformanceTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/NewPerformanceTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/NewPerformanceTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,92 @@
+//$Id: NewPerformanceTest.java 5718 2005-02-13 23:01:34Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+import java.util.List;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import junit.textui.TestRunner;
+import org.hibernate.classic.Session;
+import org.hibernate.test.TestCase;
+
+public class NewPerformanceTest extends TestCase {
+
+ public NewPerformanceTest(String arg0) {
+ super(arg0);
+ }
+
+ public void testPerformance() throws Exception {
+
+ for ( int n=2; n<4000; n*=2 ) {
+
+ Simple[] simples = new Simple[n];
+ Serializable[] ids = new Serializable[n];
+ for ( int i=0; i<n; i++ ) {
+ simples[i] = new Simple();
+ simples[i].init();
+ simples[i].setCount(i);
+ ids[i] = new Long(i);
+ }
+
+ Session s = openSession();
+ prepare(s, simples, ids, n);
+ s.close();
+
+ long find = 0;
+ long flush = 0;
+
+ for ( int i=0; i<100; i++ ) {
+
+ s = openSession();
+ long time = System.currentTimeMillis();
+ List list = s.createQuery("from Simple s where not s.name='osama bin laden' and s.other is null").list();
+ find += System.currentTimeMillis() - time;
+ assertTrue( list.size()==n );
+ time = System.currentTimeMillis();
+ s.flush();
+ flush += System.currentTimeMillis() - time;
+ time = System.currentTimeMillis();
+ s.connection().commit();
+ find += System.currentTimeMillis() - time;
+ s.close();
+
+ }
+
+ System.out.println( "Objects: " + n + " - find(): " + find + "ms / flush(): " + flush + "ms / Ratio: " + ( (float) flush )/find );
+ System.out.println( "Objects: " + n + " flush time per object: " + flush / 100.0 / n );
+ System.out.println( "Objects: " + n + " load time per object: " + find / 100.0 / n );
+ s = openSession();
+ delete(s);
+ s.close();
+
+ }
+ }
+
+ private void prepare(Session s, Simple[] simples, Serializable[] ids, int N) throws Exception {
+ for ( int i=0; i<N; i++ ) {
+ s.save( simples[i], ids[i] );
+ }
+ s.flush();
+ s.connection().commit();
+ }
+
+ private void delete(Session s) throws Exception {
+ s.delete("from Simple s");
+ s.flush();
+ s.connection().commit();
+ }
+
+ public String[] getMappings() {
+ return new String[] { "legacy/Simple.hbm.xml" };
+ }
+
+ public static Test suite() throws Exception {
+ return new TestSuite(NewPerformanceTest.class);
+ }
+
+ public static void main(String[] args) throws Exception {
+ TestRunner.run( suite() );
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/NewerPerformanceTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/NewerPerformanceTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/NewerPerformanceTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,402 @@
+//$Id: NewerPerformanceTest.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import junit.textui.TestRunner;
+import org.hibernate.classic.Session;
+import org.hibernate.cfg.Environment;
+import org.hibernate.connection.ConnectionProvider;
+import org.hibernate.connection.ConnectionProviderFactory;
+import org.hibernate.test.TestCase;
+
+public class NewerPerformanceTest extends TestCase {
+
+ public NewerPerformanceTest(String arg0) {
+ super(arg0);
+ }
+
+ public void testMany() throws Exception {
+
+ ConnectionProvider cp = ConnectionProviderFactory.newConnectionProvider( Environment.getProperties() );
+
+ long hiber=0;
+ long jdbc=0;
+
+ for ( int n=0; n<20; n++ ) {
+
+ Session s = openSession();
+ s.delete("from Simple");
+ s.flush();
+ Simple[] simples = new Simple[n];
+ Serializable[] ids = new Serializable[n];
+ for ( int i=0; i<n; i++ ) {
+ simples[i] = new Simple();
+ simples[i].init();
+ simples[i].setCount(i);
+ ids[i] = new Long(i);
+ s.save(simples[i], ids[i]);
+ }
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ //allow cache to settle
+
+ s = openSession();
+ hibernate(s, simples, ids, n, "h0");
+ s.close();
+
+ Connection c = cp.getConnection();
+ directJDBC( c, simples, ids, n, "j0" );
+ cp.closeConnection(c);
+
+ s = openSession();
+ hibernate(s, simples, ids, n, "h0");
+ s.close();
+
+ c = cp.getConnection();
+ directJDBC( c, simples, ids, n, "j0" );
+ cp.closeConnection(c);
+
+ //Now do timings
+
+ int N=30;
+
+ long time = System.currentTimeMillis();
+ for (int i=0; i<N; i++) {
+ s = openSession();
+ hibernate(s, simples, ids, n, "h1");
+ s.close();
+ }
+ hiber += System.currentTimeMillis() - time;
+
+ time = System.currentTimeMillis();
+ for (int i=0; i<N; i++) {
+ c = cp.getConnection();
+ directJDBC( c, simples, ids, n, "j1" );
+ cp.closeConnection(c);
+ }
+ jdbc += System.currentTimeMillis() - time;
+
+ time = System.currentTimeMillis();
+ for (int i=0; i<N; i++) {
+ s = openSession();
+ hibernate(s, simples, ids, n, "h2");
+ s.close();
+ }
+ hiber += System.currentTimeMillis() - time;
+
+ time = System.currentTimeMillis();
+ for (int i=0; i<N; i++) {
+ c = cp.getConnection();
+ directJDBC( c, simples, ids, n, "j2" );
+ cp.closeConnection(c);
+ }
+ jdbc += System.currentTimeMillis() - time;
+
+ time = System.currentTimeMillis();
+ for (int i=0; i<N; i++) {
+ s = openSession();
+ hibernate(s, simples, ids, n, "h1");
+ s.close();
+ }
+ hiber += System.currentTimeMillis() - time;
+
+ time = System.currentTimeMillis();
+ for (int i=0; i<N; i++) {
+ c = cp.getConnection();
+ directJDBC( c, simples, ids, n, "j1" );
+ cp.closeConnection(c);
+ }
+ jdbc += System.currentTimeMillis() - time;
+
+ }
+
+ System.out.println( "Hibernate: " + hiber + "ms / Direct JDBC: " + jdbc + "ms = Ratio: " + ( (float) hiber )/jdbc );
+
+ cp.close();
+ System.gc();
+ }
+
+ public void testSimultaneous() throws Exception {
+
+ ConnectionProvider cp = ConnectionProviderFactory.newConnectionProvider( Environment.getProperties() );
+
+ for ( int n=2; n<4000; n*=2 ) {
+
+ Session s = openSession();
+ s.delete("from Simple");
+ s.flush();
+ Simple[] simples = new Simple[n];
+ Serializable[] ids = new Serializable[n];
+ for ( int i=0; i<n; i++ ) {
+ simples[i] = new Simple();
+ simples[i].init();
+ simples[i].setCount(i);
+ ids[i] = new Long(i);
+ s.save(simples[i], ids[i]);
+ }
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ //allow cache to settle
+
+ s = openSession();
+ hibernate(s, simples, ids, n, "h0");
+ s.close();
+
+ Connection c = cp.getConnection();
+ directJDBC( c, simples, ids, n, "j0" );
+ cp.closeConnection(c);
+
+ s = openSession();
+ hibernate(s, simples, ids, n, "h0");
+ s.close();
+
+ c = cp.getConnection();
+ directJDBC( c, simples, ids, n, "j0" );
+ cp.closeConnection(c);
+
+ //Now do timings
+
+ s = openSession();
+ long time = System.currentTimeMillis();
+ hibernate(s, simples, ids, n, "h1");
+ long hiber = System.currentTimeMillis() - time;
+ s.close();
+
+ c = cp.getConnection();
+ time = System.currentTimeMillis();
+ directJDBC( c, simples, ids, n, "j1" );
+ long jdbc = System.currentTimeMillis() - time;
+ cp.closeConnection(c);
+
+ s = openSession();
+ time = System.currentTimeMillis();
+ hibernate(s, simples, ids, n, "h2");
+ hiber += System.currentTimeMillis() - time;
+ s.close();
+
+ c = cp.getConnection();
+ time = System.currentTimeMillis();
+ directJDBC( c, simples, ids, n, "j2" );
+ jdbc += System.currentTimeMillis() - time;
+ cp.closeConnection(c);
+
+ s = openSession();
+ time = System.currentTimeMillis();
+ hibernate(s, simples, ids, n, "h2");
+ hiber += System.currentTimeMillis() - time;
+ s.close();
+
+ c = cp.getConnection();
+ time = System.currentTimeMillis();
+ directJDBC( c, simples, ids, n, "j2" );
+ jdbc += System.currentTimeMillis() - time;
+ cp.closeConnection(c);
+
+ System.out.println( "Objects: " + n + " - Hibernate: " + hiber + "ms / Direct JDBC: " + jdbc + "ms = Ratio: " + ( (float) hiber )/jdbc );
+
+ }
+
+ cp.close();
+ System.gc();
+ }
+
+ public void testHibernateOnly() throws Exception {
+
+ for ( int n=2; n<4000; n*=2 ) {
+
+ Session s = openSession();
+ Simple[] simples = new Simple[n];
+ s.delete("from Simple");
+ s.flush();
+ Serializable[] ids = new Serializable[n];
+ for ( int i=0; i<n; i++ ) {
+ simples[i] = new Simple();
+ simples[i].init();
+ simples[i].setCount(i);
+ ids[i] = new Long(i);
+ s.save(simples[i], ids[i]);
+ }
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ //Now do timings
+
+ s = openSession();
+ long time = System.currentTimeMillis();
+ hibernate(s, simples, ids, n, "h1");
+ long hiber = System.currentTimeMillis() - time;
+ s.close();
+
+ s = openSession();
+ time = System.currentTimeMillis();
+ hibernate(s, simples, ids, n, "h2");
+ hiber += System.currentTimeMillis() - time;
+ s.close();
+
+ s = openSession();
+ time = System.currentTimeMillis();
+ hibernate(s, simples, ids, n, "h2");
+ hiber += System.currentTimeMillis() - time;
+ s.close();
+
+ System.out.println( "Objects: " + n + " - Hibernate: " + hiber );
+
+ }
+
+ System.gc();
+ }
+
+ public void testJdbcOnly() throws Exception {
+
+ ConnectionProvider cp = ConnectionProviderFactory.newConnectionProvider( Environment.getProperties() );
+
+ for ( int n=2; n<4000; n*=2 ) {
+
+ Session s = openSession();
+ Simple[] simples = new Simple[n];
+ s.delete("from Simple");
+ s.flush();
+ Serializable[] ids = new Serializable[n];
+ for ( int i=0; i<n; i++ ) {
+ simples[i] = new Simple();
+ simples[i].init();
+ simples[i].setCount(i);
+ ids[i] = new Long(i);
+ s.save(simples[i], ids[i]);
+ }
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+
+ //Now do timings
+
+ Connection c = cp.getConnection();
+ long time = System.currentTimeMillis();
+ directJDBC( c, simples, ids, n, "j1" );
+ long jdbc = System.currentTimeMillis() - time;
+ cp.closeConnection(c);
+
+ c = cp.getConnection();
+ time = System.currentTimeMillis();
+ directJDBC( c, simples, ids, n, "j2" );
+ jdbc += System.currentTimeMillis() - time;
+ cp.closeConnection(c);
+
+ c = cp.getConnection();
+ time = System.currentTimeMillis();
+ directJDBC( c, simples, ids, n, "j2" );
+ jdbc += System.currentTimeMillis() - time;
+ cp.closeConnection(c);
+
+ System.out.println( "Objects: " + n + " Direct JDBC: " + jdbc );
+
+ }
+
+ cp.close();
+ System.gc();
+ }
+
+ /*private void hibernate(Session s, Simple[] simples, Serializable[] ids, int N, String runname) throws Exception {
+ for ( int i=0; i<N; i++ ) {
+ s.get( Simple.class, ids[i] );
+ //s.find("from Simple s where s.id = ?", ids[i], Hibernate.LONG);
+ }
+ //s.flush();
+ s.connection().commit();
+ }
+
+ private void directJDBC(Connection c, Simple[] simples, Serializable[] ids, int N, String runname) throws SQLException {
+
+ for ( int i=0; i<N; i++ ) {
+ PreparedStatement select = c.prepareStatement("SELECT s.id_, s.name, s.address, s.count_, s.date_, s.pay, s.other FROM Simple s where s.id_=?");
+ select.setLong( 1, ( (Long) ids[i] ).longValue() );
+ ResultSet rs = select.executeQuery();
+ rs.next();
+ /*new Long( rs.getLong(1) );
+ rs.getString(2);
+ rs.getString(3);
+ rs.getInt(4);
+ rs.getDate(5);
+ rs.getFloat(6);
+ rs.getLong(7);*/
+ /*Simple s = new Simple();
+ new Long( rs.getLong("id_") ); rs.wasNull();
+ s.setName( rs.getString("name") ); rs.wasNull();
+ s.setAddress( rs.getString("address") ); rs.wasNull();
+ s.setCount( rs.getInt("count_") ); rs.wasNull();
+ s.setDate( rs.getTimestamp("date_") ); rs.wasNull();
+ s.setPay( new Float( rs.getFloat("pay") ) ); rs.wasNull();
+ rs.getLong("other"); rs.wasNull(); s.setOther(null);
+ rs.close();
+ select.close();
+ }
+ c.commit();
+ }*/
+
+ private void hibernate(Session s, Simple[] simples, Serializable[] ids, int N, String runname) throws Exception {
+ s.createQuery("from Simple s").list();
+ s.connection().commit();
+ }
+
+ private void directJDBC(Connection c, Simple[] simples, Serializable[] ids, int N, String runname) throws SQLException {
+ List result=new ArrayList();
+ PreparedStatement select = c.prepareStatement("SELECT s.id_ as id_, s.name as name, s.address as address, s.count_ as count_, s.date_ as date_, s.pay as pay, s.other as other FROM Simple s");
+ ResultSet rs = select.executeQuery();
+ while ( rs.next() ) {
+ /*new Long( rs.getLong(1) );
+ rs.getString(2);
+ rs.getString(3);
+ rs.getInt(4);
+ rs.getDate(5);
+ rs.getFloat(6);
+ rs.getLong(7);*/
+ Simple s = new Simple();
+ new Long( rs.getLong("id_") ); rs.wasNull();
+ s.setName( rs.getString("name") ); rs.wasNull();
+ s.setAddress( rs.getString("address") ); rs.wasNull();
+ s.setCount( rs.getInt("count_") ); rs.wasNull();
+ s.setDate( rs.getTimestamp("date_") ); rs.wasNull();
+ s.setPay( new Float( rs.getFloat("pay") ) ); rs.wasNull();
+ rs.getLong("other"); rs.wasNull(); s.setOther(null);
+ result.add(s);
+ }
+ rs.close();
+ select.close();
+ c.commit();
+ }
+
+ public String[] getMappings() {
+ return new String[] { "legacy/Simple.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new TestSuite(NewerPerformanceTest.class);
+ }
+
+ public static void main(String[] args) throws Exception {
+ TestRunner.run( suite() );
+ }
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/NonReflectiveBinderTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/NonReflectiveBinderTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/NonReflectiveBinderTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,189 @@
+//$Id: NonReflectiveBinderTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.legacy;
+
+import java.util.Iterator;
+import java.util.Map;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.hibernate.cfg.Configuration;
+import org.hibernate.mapping.Bag;
+import org.hibernate.mapping.Collection;
+import org.hibernate.mapping.Component;
+import org.hibernate.mapping.MetaAttribute;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Property;
+import org.hibernate.junit.UnitTestCase;
+
+
+public class NonReflectiveBinderTest extends UnitTestCase {
+
+ private Configuration cfg;
+
+ public NonReflectiveBinderTest(String x) {
+ super( x );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "legacy/Wicked.hbm.xml"};
+ }
+
+ public static Test suite() {
+ return new TestSuite( NonReflectiveBinderTest.class );
+ }
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ cfg = new Configuration()
+ .addResource( "org/hibernate/test/legacy/Wicked.hbm.xml" );
+ cfg.buildMappings();
+ }
+
+ protected void tearDown() throws Exception {
+ cfg = null;
+ super.tearDown();
+ }
+
+ public void testMetaInheritance() {
+ PersistentClass cm = cfg.getClassMapping("org.hibernate.test.legacy.Wicked");
+ Map m = cm.getMetaAttributes();
+ assertNotNull(m);
+ assertNotNull(cm.getMetaAttribute("global"));
+ assertNull(cm.getMetaAttribute("globalnoinherit"));
+
+ MetaAttribute metaAttribute = cm.getMetaAttribute("implements");
+ assertNotNull(metaAttribute);
+ assertEquals("implements", metaAttribute.getName());
+ assertTrue(metaAttribute.isMultiValued());
+ assertEquals(3, metaAttribute.getValues().size());
+ assertEquals("java.lang.Observer",metaAttribute.getValues().get(0));
+ assertEquals("java.lang.Observer",metaAttribute.getValues().get(1));
+ assertEquals("org.foo.BogusVisitor",metaAttribute.getValues().get(2));
+
+ /*Property property = cm.getIdentifierProperty();
+ property.getMetaAttribute(null);*/
+
+ Iterator propertyIterator = cm.getPropertyIterator();
+ while (propertyIterator.hasNext()) {
+ Property element = (Property) propertyIterator.next();
+ System.out.println(element);
+ Map ma = element.getMetaAttributes();
+ assertNotNull(ma);
+ assertNotNull(element.getMetaAttribute("global"));
+ MetaAttribute metaAttribute2 = element.getMetaAttribute("implements");
+ assertNotNull(metaAttribute2);
+ assertNull(element.getMetaAttribute("globalnoinherit"));
+
+ }
+
+ Property element = cm.getProperty("component");
+ Map ma = element.getMetaAttributes();
+ assertNotNull(ma);
+ assertNotNull(element.getMetaAttribute("global"));
+ assertNotNull(element.getMetaAttribute("componentonly"));
+ assertNotNull(element.getMetaAttribute("allcomponent"));
+ assertNull(element.getMetaAttribute("globalnoinherit"));
+
+ MetaAttribute compimplements = element.getMetaAttribute("implements");
+ assertNotNull(compimplements);
+ assertEquals(compimplements.getValue(), "AnotherInterface");
+
+ Property xp = ((Component)element.getValue()).getProperty( "x" );
+ MetaAttribute propximplements = xp.getMetaAttribute( "implements" );
+ assertNotNull(propximplements);
+ assertEquals(propximplements.getValue(), "AnotherInterface");
+
+
+ }
+
+ // HBX-718
+ public void testNonMutatedInheritance() {
+ PersistentClass cm = cfg.getClassMapping("org.hibernate.test.legacy.Wicked");
+ MetaAttribute metaAttribute = cm.getMetaAttribute( "globalmutated" );
+
+ assertNotNull(metaAttribute);
+ /*assertEquals( metaAttribute.getValues().size(), 2 );
+ assertEquals( "top level", metaAttribute.getValues().get(0) );*/
+ assertEquals( "wicked level", metaAttribute.getValue() );
+
+ Property property = cm.getProperty( "component" );
+ MetaAttribute propertyAttribute = property.getMetaAttribute( "globalmutated" );
+
+ assertNotNull(propertyAttribute);
+ /*assertEquals( propertyAttribute.getValues().size(), 3 );
+ assertEquals( "top level", propertyAttribute.getValues().get(0) );
+ assertEquals( "wicked level", propertyAttribute.getValues().get(1) );*/
+ assertEquals( "monetaryamount level", propertyAttribute.getValue() );
+
+ org.hibernate.mapping.Component component = (Component)property.getValue();
+ property = component.getProperty( "x" );
+ propertyAttribute = property.getMetaAttribute( "globalmutated" );
+
+ assertNotNull(propertyAttribute);
+ /*assertEquals( propertyAttribute.getValues().size(), 4 );
+ assertEquals( "top level", propertyAttribute.getValues().get(0) );
+ assertEquals( "wicked level", propertyAttribute.getValues().get(1) );
+ assertEquals( "monetaryamount level", propertyAttribute.getValues().get(2) );*/
+ assertEquals( "monetaryamount x level", propertyAttribute.getValue() );
+
+ property = cm.getProperty( "sortedEmployee" );
+ propertyAttribute = property.getMetaAttribute( "globalmutated" );
+
+ assertNotNull(propertyAttribute);
+ /*assertEquals( propertyAttribute.getValues().size(), 3 );
+ assertEquals( "top level", propertyAttribute.getValues().get(0) );
+ assertEquals( "wicked level", propertyAttribute.getValues().get(1) );*/
+ assertEquals( "sortedemployee level", propertyAttribute.getValue() );
+
+ property = cm.getProperty( "anotherSet" );
+ propertyAttribute = property.getMetaAttribute( "globalmutated" );
+
+ assertNotNull(propertyAttribute);
+ /*assertEquals( propertyAttribute.getValues().size(), 2 );
+ assertEquals( "top level", propertyAttribute.getValues().get(0) );*/
+ assertEquals( "wicked level", propertyAttribute.getValue() );
+
+ Bag bag = (Bag) property.getValue();
+ component = (Component)bag.getElement();
+
+ assertEquals(4,component.getMetaAttributes().size());
+
+ metaAttribute = component.getMetaAttribute( "globalmutated" );
+ /*assertEquals( metaAttribute.getValues().size(), 3 );
+ assertEquals( "top level", metaAttribute.getValues().get(0) );
+ assertEquals( "wicked level", metaAttribute.getValues().get(1) );*/
+ assertEquals( "monetaryamount anotherSet composite level", metaAttribute.getValue() );
+
+ property = component.getProperty( "emp" );
+ propertyAttribute = property.getMetaAttribute( "globalmutated" );
+
+ assertNotNull(propertyAttribute);
+ /*assertEquals( propertyAttribute.getValues().size(), 4 );
+ assertEquals( "top level", propertyAttribute.getValues().get(0) );
+ assertEquals( "wicked level", propertyAttribute.getValues().get(1) );
+ assertEquals( "monetaryamount anotherSet composite level", propertyAttribute.getValues().get(2) );*/
+ assertEquals( "monetaryamount anotherSet composite property emp level", propertyAttribute.getValue() );
+
+
+ property = component.getProperty( "empinone" );
+ propertyAttribute = property.getMetaAttribute( "globalmutated" );
+
+ assertNotNull(propertyAttribute);
+ /*assertEquals( propertyAttribute.getValues().size(), 4 );
+ assertEquals( "top level", propertyAttribute.getValues().get(0) );
+ assertEquals( "wicked level", propertyAttribute.getValues().get(1) );
+ assertEquals( "monetaryamount anotherSet composite level", propertyAttribute.getValues().get(2) );*/
+ assertEquals( "monetaryamount anotherSet composite property empinone level", propertyAttribute.getValue() );
+
+
+ }
+
+ public void testComparator() {
+ PersistentClass cm = cfg.getClassMapping("org.hibernate.test.legacy.Wicked");
+
+ Property property = cm.getProperty("sortedEmployee");
+ Collection col = (Collection) property.getValue();
+ assertEquals(col.getComparatorClassName(),"org.hibernate.test.legacy.NonExistingComparator");
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Object2.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Object2.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Object2.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,50 @@
+<?xml version="1.0"?>
+
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping default-lazy="false">
+ <class
+ name="org.hibernate.test.legacy.Object2"
+ table="object2"
+ dynamic-update="false"
+ dynamic-insert="false"
+ >
+ <cache usage="read-write" />
+
+ <id
+ name="id"
+ column="id"
+ type="java.lang.Long"
+ >
+ <generator class="foreign">
+ <param name="property">belongsToMainObj</param>
+ </generator>
+ </id>
+
+ <property
+ name="dummy"
+ type="java.lang.String"
+ update="true"
+ insert="true"
+ column="xdummy"
+ />
+
+ <one-to-one
+ name="belongsToMainObj"
+ class="org.hibernate.test.legacy.MainObject"
+ cascade="none"
+ outer-join="auto"
+ constrained="true"
+ />
+
+ <!--
+ To add non XDoclet property mappings, create a file named
+ hibernate-properties-Object2.xml
+ containing the additional properties and place it in your merge dir.
+ -->
+
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Object2.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Object2.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Object2.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,37 @@
+package org.hibernate.test.legacy;
+
+/**
+ *
+ * @author Wolfgang Voelkl
+ *
+ */
+public class Object2 {
+ private Long id;
+ private String dummy;
+ private MainObject belongsToMainObj;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long l) {
+ this.id = l;
+ }
+
+ public String getDummy() {
+ return dummy;
+ }
+
+ public void setDummy(String string) {
+ dummy = string;
+ }
+
+ public MainObject getBelongsToMainObj() {
+ return belongsToMainObj;
+ }
+
+ public void setBelongsToMainObj(MainObject object) {
+ belongsToMainObj = object;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/One.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/One.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/One.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping default-lazy="false">
+ <class name="org.hibernate.test.legacy.One" table="one">
+ <id name="key" column="one_key">
+ <generator class="native" />
+ </id>
+ <property name="x"/>
+ <property column="one_value" name="value"/>
+ <set name="manies" inverse="true" lazy="true">
+ <key column="one_key"/>
+ <one-to-many class="org.hibernate.test.legacy.Many"/>
+ </set>
+ </class>
+</hibernate-mapping>
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/One.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/One.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/One.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,49 @@
+//$Id: One.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.util.Set;
+
+public class One {
+ Long key;
+ String value;
+ Set manies;
+ private int x;
+
+ public int getX() {
+ return x;
+ }
+ public void setX(int x) {
+ this.x = x;
+ }
+
+ public void setKey(Long key) {
+ this.key = key;
+ }
+
+ public Long getKey() {
+ return this.key;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return this.value;
+ }
+
+ public Set getManies() {
+ return manies;
+ }
+
+ public void setManies(Set manies) {
+ this.manies = manies;
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/OneToOneCacheTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/OneToOneCacheTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/OneToOneCacheTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,120 @@
+//$Id: OneToOneCacheTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+
+import junit.framework.Test;
+
+import org.hibernate.HibernateException;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * Simple testcase to illustrate HB-992
+ *
+ * @author Wolfgang Voelkl, michael
+ */
+public class OneToOneCacheTest extends LegacyTestCase {
+
+ private Serializable generatedId;
+
+ public OneToOneCacheTest(String x) {
+ super( x );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "legacy/Object2.hbm.xml", "legacy/MainObject.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( OneToOneCacheTest.class );
+ }
+
+ public void testOneToOneCache() throws HibernateException {
+
+ //create a new MainObject
+ createMainObject();
+ // load the MainObject
+ readMainObject();
+
+ //create and add Ojbect2
+ addObject2();
+
+ //here the newly created Object2 is written to the database
+ //but the MainObject does not know it yet
+ MainObject mainObject = readMainObject();
+
+ assertNotNull( mainObject.getObj2() );
+
+ // after evicting, it works.
+ getSessions().evict( MainObject.class );
+
+ mainObject = readMainObject();
+
+ assertNotNull( mainObject.getObj2() );
+
+ }
+
+ /**
+ * creates a new MainObject
+ * <p/>
+ * one hibernate transaction !
+ */
+ private void createMainObject() throws HibernateException {
+ Session session = openSession();
+ Transaction tx = session.beginTransaction();
+
+ MainObject mo = new MainObject();
+ mo.setDescription( "Main Test" );
+
+ generatedId = session.save( mo );
+
+ tx.commit();
+ session.close();
+ }
+
+ /**
+ * loads the newly created MainObject
+ * and adds a new Object2 to it
+ * <p/>
+ * one hibernate transaction
+ */
+ private void addObject2() throws HibernateException {
+ Session session = openSession();
+ Transaction tx = session.beginTransaction();
+
+ MainObject mo =
+ ( MainObject ) session.load( MainObject.class, generatedId );
+
+ Object2 toAdd = new Object2();
+ toAdd.setDummy( "test" );
+
+ //toAdd should now be saved by cascade
+ mo.setObj2( toAdd );
+
+ tx.commit();
+ session.close();
+ }
+
+ /**
+ * reads the newly created MainObject
+ * and its Object2 if it exists
+ * <p/>
+ * one hibernate transaction
+ */
+ private MainObject readMainObject() throws HibernateException {
+ Long returnId = null;
+ Session session = openSession();
+ Transaction tx = session.beginTransaction();
+
+ Serializable id = generatedId;
+
+ MainObject mo = ( MainObject ) session.load( MainObject.class, id );
+
+ tx.commit();
+ session.close();
+
+ return mo;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Outer.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Outer.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Outer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,42 @@
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+
+/**
+ * @author Stefano Travelli
+ */
+public class Outer implements Serializable {
+ private OuterKey id;
+ private String bubu;
+
+ public OuterKey getId() {
+ return id;
+ }
+
+ public void setId(OuterKey id) {
+ this.id = id;
+ }
+
+ public String getBubu() {
+ return bubu;
+ }
+
+ public void setBubu(String bubu) {
+ this.bubu = bubu;
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof Outer)) return false;
+
+ final Outer cidDetail = (Outer) o;
+
+ if (id != null ? !id.equals(cidDetail.id) : cidDetail.id != null) return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ return (id != null ? id.hashCode() : 0);
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/OuterKey.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/OuterKey.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/OuterKey.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,46 @@
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+
+/**
+ * @author Stefano Travelli
+ */
+public class OuterKey implements Serializable {
+ private Middle master;
+ private String detailId;
+
+ public Middle getMaster() {
+ return master;
+ }
+
+ public void setMaster(Middle master) {
+ this.master = master;
+ }
+
+ public String getDetailId() {
+ return detailId;
+ }
+
+ public void setDetailId(String detailId) {
+ this.detailId = detailId;
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof OuterKey)) return false;
+
+ final OuterKey cidDetailID = (OuterKey) o;
+
+ if (detailId != null ? !detailId.equals(cidDetailID.detailId) : cidDetailID.detailId != null) return false;
+ if (master != null ? !master.equals(cidDetailID.master) : cidDetailID.master != null) return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ int result;
+ result = (master != null ? master.hashCode() : 0);
+ result = 29 * result + (detailId != null ? detailId.hashCode() : 0);
+ return result;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Parent.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Parent.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Parent.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,62 @@
+//$Id: Parent.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+public class Parent {
+ private long id;
+ private int count;
+ private Child child;
+ private Object any;
+ private int x;
+
+ public int getX() {
+ return x;
+ }
+ public void setX(int x) {
+ this.x = x;
+ }
+
+ public int getCount() {
+ return count;
+ }
+
+
+ public long getId() {
+ return id;
+ }
+
+
+ public void setCount(int count) {
+ this.count = count;
+ }
+
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+
+ public Child getChild() {
+ return child;
+ }
+
+
+ public void setChild(Child child) {
+ this.child = child;
+ }
+
+ public Object getAny() {
+ return any;
+ }
+
+ public void setAny(Object any) {
+ this.any = any;
+ }
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ParentChild.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ParentChild.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ParentChild.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping default-lazy="false">
+
+ <class name="org.hibernate.test.legacy.Parent">
+ <id name="id" type="long">
+ <generator class="native"> <!--seqhilo-->
+ <!--<param>foo_seq</param>-->
+ </generator>
+ </id>
+ <property name="x"/>
+ <property name="count" column="count_"/>
+ <one-to-one name="child"/>
+ <property name="any" type="object">
+ <column name="any_id"/>
+ <column name="any_class"/>
+ </property>
+ </class>
+
+ <class name="org.hibernate.test.legacy.Child">
+ <id name="id" type="long">
+ <generator class="assigned"/>
+ </id>
+ <property name="x"/>
+ <one-to-one name="parent" class="org.hibernate.test.legacy.Parent" constrained="true" foreign-key="parentfk"/>
+ <property name="count" column="count_"/>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ParentChildTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ParentChildTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ParentChildTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,1083 @@
+//$Id: ParentChildTest.java 11089 2007-01-24 14:34:22Z max.andersen at jboss.com $
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.Test;
+
+import org.hibernate.Criteria;
+import org.hibernate.FetchMode;
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.LockMode;
+import org.hibernate.ObjectNotFoundException;
+import org.hibernate.ReplicationMode;
+import org.hibernate.Transaction;
+import org.hibernate.classic.Session;
+import org.hibernate.criterion.Expression;
+import org.hibernate.dialect.DB2Dialect;
+import org.hibernate.dialect.HSQLDialect;
+import org.hibernate.dialect.MySQLDialect;
+import org.hibernate.engine.EntityEntry;
+import org.hibernate.impl.SessionImpl;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+
+public class ParentChildTest extends LegacyTestCase {
+
+ public ParentChildTest(String x) {
+ super(x);
+ }
+
+ public String[] getMappings() {
+ return new String[] {
+ "legacy/ParentChild.hbm.xml",
+ "legacy/FooBar.hbm.xml",
+ "legacy/Baz.hbm.xml",
+ "legacy/Qux.hbm.xml",
+ "legacy/Glarch.hbm.xml",
+ "legacy/Fum.hbm.xml",
+ "legacy/Fumm.hbm.xml",
+ "legacy/Fo.hbm.xml",
+ "legacy/One.hbm.xml",
+ "legacy/Many.hbm.xml",
+ "legacy/Immutable.hbm.xml",
+ "legacy/Fee.hbm.xml",
+ "legacy/Vetoer.hbm.xml",
+ "legacy/Holder.hbm.xml",
+ "legacy/Simple.hbm.xml",
+ "legacy/Container.hbm.xml",
+ "legacy/Circular.hbm.xml",
+ "legacy/Stuff.hbm.xml"
+ };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( ParentChildTest.class );
+ }
+
+ public void testReplicate() throws Exception {
+ Session s = openSession();
+ Container baz = new Container();
+ Contained f = new Contained();
+ List list = new ArrayList();
+ list.add(baz);
+ f.setBag(list);
+ List list2 = new ArrayList();
+ list2.add(f);
+ baz.setBag(list2);
+ s.save(f);
+ s.save(baz);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ s.replicate(baz, ReplicationMode.OVERWRITE);
+
+ // HHH-2378
+ SessionImpl x = (SessionImpl)s;
+ EntityEntry entry = x.getPersistenceContext().getEntry( baz );
+ assertNull(entry.getVersion());
+
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ s.replicate(baz, ReplicationMode.IGNORE);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ s.delete(baz);
+ s.delete(f);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testQueryOneToOne() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Serializable id = s.save( new Parent() );
+ assertTrue( s.find("from Parent p left join fetch p.child").size()==1 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Parent p = (Parent) s.createQuery("from Parent p left join fetch p.child").uniqueResult();
+ assertTrue( p.getChild()==null );
+ s.find("from Parent p join p.child c where c.x > 0");
+ s.find("from Child c join c.parent p where p.x > 0");
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.delete( s.get(Parent.class, id) );
+ t.commit();
+ s.close();
+ }
+
+ public void testProxyReuse() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ FooProxy foo = new Foo();
+ FooProxy foo2 = new Foo();
+ Serializable id = s.save(foo);
+ Serializable id2 = s.save(foo2);
+ foo2.setInt(1234567);
+ foo.setInt(1234);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ foo = (FooProxy) s.load(Foo.class, id);
+ foo2 = (FooProxy) s.load(Foo.class, id2);
+ assertFalse( Hibernate.isInitialized(foo) );
+ Hibernate.initialize(foo2);
+ Hibernate.initialize(foo);
+ assertTrue( foo.getComponent().getImportantDates().length==4 );
+ assertTrue( foo2.getComponent().getImportantDates().length==4 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ foo.setFloat( new Float(1.2f) );
+ foo2.setFloat( new Float(1.3f) );
+ foo2.getDependent().setKey(null);
+ foo2.getComponent().getSubcomponent().getFee().setKey(null);
+ assertFalse( foo2.getKey().equals(id) );
+ s.save(foo, "xyzid");
+ s.update(foo2, id); //intentionally id, not id2!
+ assertEquals( foo2.getKey(), id );
+ assertTrue( foo2.getInt()==1234567 );
+ assertEquals( foo.getKey(), "xyzid" );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ foo = (FooProxy) s.load(Foo.class, id);
+ assertTrue( foo.getInt()==1234567 );
+ assertTrue( foo.getComponent().getImportantDates().length==4 );
+ String feekey = foo.getDependent().getKey();
+ String fookey = foo.getKey();
+ s.delete(foo);
+ s.delete( s.get(Foo.class, id2) );
+ s.delete( s.get(Foo.class, "xyzid") );
+ assertTrue( s.delete("from java.lang.Object")==3 );
+ t.commit();
+ s.close();
+
+ //to account for new id rollback shit
+ foo.setKey(fookey);
+ foo.getDependent().setKey(feekey);
+ foo.getComponent().setGlarch(null);
+ foo.getComponent().setSubcomponent(null);
+
+ s = openSession();
+ t = s.beginTransaction();
+ //foo.getComponent().setGlarch(null); //no id property!
+ s.replicate(foo, ReplicationMode.OVERWRITE);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Foo refoo = (Foo) s.get(Foo.class, id);
+ assertEquals( feekey, refoo.getDependent().getKey() );
+ s.delete(refoo);
+ t.commit();
+ s.close();
+ }
+
+ public void testComplexCriteria() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Baz baz = new Baz();
+ s.save(baz);
+ baz.setDefaults();
+ Map topGlarchez = new HashMap();
+ baz.setTopGlarchez(topGlarchez);
+ Glarch g1 = new Glarch();
+ g1.setName("g1");
+ s.save(g1);
+ Glarch g2 = new Glarch();
+ g2.setName("g2");
+ s.save(g2);
+ g1.setProxyArray( new GlarchProxy[] {g2} );
+ topGlarchez.put( new Character('1'),g1 );
+ topGlarchez.put( new Character('2'), g2);
+ Foo foo1 = new Foo();
+ Foo foo2 = new Foo();
+ s.save(foo1);
+ s.save(foo2);
+ baz.getFooSet().add(foo1);
+ baz.getFooSet().add(foo2);
+ baz.setFooArray( new FooProxy[] { foo1 } );
+
+ LockMode lockMode = (getDialect() instanceof DB2Dialect) ? LockMode.READ : LockMode.UPGRADE;
+
+ Criteria crit = s.createCriteria(Baz.class);
+ crit.createCriteria("topGlarchez")
+ .add( Expression.isNotNull("name") )
+ .createCriteria("proxyArray")
+ .add( Expression.eqProperty("name", "name") )
+ .add( Expression.eq("name", "g2") )
+ .add( Expression.gt("x", new Integer(-666) ) );
+ crit.createCriteria("fooSet")
+ .add( Expression.isNull("null") )
+ .add( Expression.eq("string", "a string") )
+ .add( Expression.lt("integer", new Integer(-665) ) );
+ crit.createCriteria("fooArray")
+ .add( Expression.eq("string", "a string") )
+ .setLockMode(lockMode);
+
+ List list = crit.list();
+ assertTrue( list.size()==2 );
+
+ s.createCriteria(Glarch.class).setLockMode(LockMode.UPGRADE).list();
+ s.createCriteria(Glarch.class).setLockMode(Criteria.ROOT_ALIAS, LockMode.UPGRADE).list();
+
+ g2.setName(null);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+
+ list = s.createCriteria(Baz.class).add( Expression.isEmpty("fooSet") ).list();
+ assertEquals( list.size(), 0 );
+
+ list = s.createCriteria(Baz.class).add( Expression.isNotEmpty("fooSet") ).list();
+ assertEquals( new HashSet(list).size(), 1 );
+
+ list = s.createCriteria(Baz.class).add( Expression.sizeEq("fooSet", 2) ).list();
+ assertEquals( new HashSet(list).size(), 1 );
+
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+
+ crit = s.createCriteria(Baz.class)
+ .setLockMode(lockMode);
+ crit.createCriteria("topGlarchez")
+ .add( Expression.gt( "x", new Integer(-666) ) );
+ crit.createCriteria("fooSet")
+ .add( Expression.isNull("null") );
+ list = crit.list();
+
+ assertTrue( list.size()==4 );
+ baz = (Baz) crit.uniqueResult();
+ assertTrue( Hibernate.isInitialized(baz.getTopGlarchez()) ); //cos it is nonlazy
+ assertTrue( !Hibernate.isInitialized(baz.getFooSet()) );
+
+ /*list = s.createCriteria(Baz.class)
+ .createCriteria("fooSet.foo.component.glarch")
+ .add( Expression.eq("name", "xxx") )
+ .add( Expression.eq("fooSet.foo.component.glarch.name", "xxx") )
+ .list();
+ assertTrue( list.size()==0 );*/
+ list = s.createCriteria(Baz.class)
+ .createCriteria("fooSet")
+ .createCriteria("foo")
+ .createCriteria("component.glarch")
+ .add( Expression.eq("name", "xxx") )
+ .list();
+ assertTrue( list.size()==0 );
+
+ list = s.createCriteria(Baz.class)
+ .createAlias("fooSet", "foo")
+ .createAlias("foo.foo", "foo2")
+ .setLockMode("foo2", lockMode)
+ .add( Expression.isNull("foo2.component.glarch") )
+ .createCriteria("foo2.component.glarch")
+ .add( Expression.eq("name", "xxx") )
+ .list();
+ assertTrue( list.size()==0 );
+
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+
+ crit = s.createCriteria(Baz.class);
+ crit.createCriteria("topGlarchez")
+ .add( Expression.isNotNull("name") );
+ crit.createCriteria("fooSet")
+ .add( Expression.isNull("null") );
+
+ list = crit.list();
+ assertTrue( list.size()==2 );
+ baz = (Baz) crit.uniqueResult();
+ assertTrue( Hibernate.isInitialized(baz.getTopGlarchez()) ); //cos it is nonlazy
+ assertTrue( !Hibernate.isInitialized(baz.getFooSet()) );
+
+
+ list = s.createCriteria(Child.class).setFetchMode("parent", FetchMode.JOIN).list();
+
+ s.delete("from Glarch g");
+ s.delete( s.get(Foo.class, foo1.getKey() ) );
+ s.delete( s.get(Foo.class, foo2.getKey() ) );
+ s.delete(baz);
+ t.commit();
+ s.close();
+ }
+
+ public void testClassWhere() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Baz baz = new Baz();
+ baz.setParts( new ArrayList() );
+ Part p1 = new Part();
+ p1.setDescription("xyz");
+ Part p2 = new Part();
+ p2.setDescription("abc");
+ baz.getParts().add(p1);
+ baz.getParts().add(p2);
+ s.save(baz);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ assertTrue( s.createCriteria(Part.class).list().size()==1 ); //there is a where condition on Part mapping
+ assertTrue( s.createCriteria(Part.class).add( Expression.eq( "id", p1.getId() ) ).list().size()==1 );
+ assertTrue( s.createQuery("from Part").list().size()==1 );
+ assertTrue( s.createQuery("from Baz baz join baz.parts").list().size()==2 );
+ baz = (Baz) s.createCriteria(Baz.class).uniqueResult();
+ assertTrue( s.createFilter( baz.getParts(), "" ).list().size()==2 );
+ //assertTrue( baz.getParts().size()==1 );
+ s.delete( s.get( Part.class, p1.getId() ));
+ s.delete( s.get( Part.class, p2.getId() ));
+ s.delete(baz);
+ t.commit();
+ s.close();
+ }
+
+ public void testClassWhereManyToMany() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Baz baz = new Baz();
+ baz.setMoreParts( new ArrayList() );
+ Part p1 = new Part();
+ p1.setDescription("xyz");
+ Part p2 = new Part();
+ p2.setDescription("abc");
+ baz.getMoreParts().add(p1);
+ baz.getMoreParts().add(p2);
+ s.save(baz);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ assertTrue( s.createCriteria(Part.class).list().size()==1 ); //there is a where condition on Part mapping
+ assertTrue( s.createCriteria(Part.class).add( Expression.eq( "id", p1.getId() ) ).list().size()==1 );
+ assertTrue( s.createQuery("from Part").list().size()==1 );
+ assertTrue( s.createQuery("from Baz baz join baz.moreParts").list().size()==2 );
+ baz = (Baz) s.createCriteria(Baz.class).uniqueResult();
+ assertTrue( s.createFilter( baz.getMoreParts(), "" ).list().size()==2 );
+ //assertTrue( baz.getParts().size()==1 );
+ s.delete( s.get( Part.class, p1.getId() ));
+ s.delete( s.get( Part.class, p2.getId() ));
+ s.delete(baz);
+ t.commit();
+ s.close();
+ }
+
+ public void testCollectionQuery() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ Simple s1 = new Simple();
+ s1.setName("s");
+ s1.setCount(0);
+ Simple s2 = new Simple();
+ s2.setCount(2);
+ Simple s3 = new Simple();
+ s3.setCount(3);
+ s.save( s1, new Long(1) ); s.save( s2, new Long(2) ); s.save( s3, new Long(3) );
+ Container c = new Container();
+ Contained cd = new Contained();
+ List bag = new ArrayList();
+ bag.add(cd);
+ c.setBag(bag);
+ List l = new ArrayList();
+ l.add(s1);
+ l.add(s3);
+ l.add(s2);
+ c.setOneToMany(l);
+ l = new ArrayList();
+ l.add(s1);
+ l.add(null);
+ l.add(s2);
+ c.setManyToMany(l);
+ s.save(c);
+ Container cx = new Container();
+ s.save(cx);
+ Simple sx = new Simple();
+ sx.setCount(5);
+ sx.setName("s");
+ s.save( sx, new Long(5) );
+ assertTrue(
+ s.find("select c from ContainerX c, Simple s where c.oneToMany[2] = s")
+ .size() == 1
+ );
+ assertTrue(
+ s.find("select c from ContainerX c, Simple s where c.manyToMany[2] = s")
+ .size() == 1
+ );
+ assertTrue(
+ s.find("select c from ContainerX c, Simple s where s = c.oneToMany[2]")
+ .size() == 1
+ );
+ assertTrue(
+ s.find("select c from ContainerX c, Simple s where s = c.manyToMany[2]")
+ .size() == 1
+ );
+ assertTrue(
+ s.find("select c from ContainerX c where c.oneToMany[0].name = 's'")
+ .size() == 1
+ );
+ assertTrue(
+ s.find("select c from ContainerX c where c.manyToMany[0].name = 's'")
+ .size() == 1
+ );
+ assertTrue(
+ s.find("select c from ContainerX c where 's' = c.oneToMany[2 - 2].name")
+ .size() == 1
+ );
+ assertTrue(
+ s.find("select c from ContainerX c where 's' = c.manyToMany[(3+1)/4-1].name")
+ .size() == 1
+ );
+ assertTrue(
+ s.find("select c from ContainerX c where c.oneToMany[ c.manyToMany[0].count ].name = 's'")
+ .size() == 1
+ );
+ assertTrue(
+ s.find("select c from ContainerX c where c.manyToMany[ c.oneToMany[0].count ].name = 's'")
+ .size() == 1
+ );
+ if ( ! ( getDialect() instanceof MySQLDialect ) && !(getDialect() instanceof org.hibernate.dialect.TimesTenDialect) ) {
+ assertTrue(
+ s.find("select c from ContainerX c where c.manyToMany[ maxindex(c.manyToMany) ].count = 2")
+ .size() == 1
+ );
+ }
+ assertTrue( s.contains(cd) );
+ if ( !(getDialect() instanceof MySQLDialect) && !(getDialect() instanceof HSQLDialect) ) {
+ s.filter( c.getBag(), "where 0 in elements(this.bag)" );
+ s.filter( c.getBag(), "where 0 in elements(this.lazyBag)" );
+ }
+ s.find("select count(comp.name) from ContainerX c join c.components comp");
+ s.delete(cd);
+ s.delete(c);
+ s.delete(s1);
+ s.delete(s2);
+ s.delete(s3);
+ s.delete(cx);
+ s.delete(sx);
+ t.commit();
+ s.close();
+ }
+
+ public void testParentChild() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Parent p = new Parent();
+ Child c = new Child();
+ c.setParent(p);
+ p.setChild(c);
+ s.save(p);
+ s.save(c);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Child) s.load( Child.class, new Long( c.getId() ) );
+ p = c.getParent();
+ assertTrue( "1-1 parent", p!=null );
+ c.setCount(32);
+ p.setCount(66);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Child) s.load( Child.class, new Long( c.getId() ) );
+ p = c.getParent();
+ assertTrue( "1-1 update", p.getCount()==66 );
+ assertTrue( "1-1 update", c.getCount()==32 );
+ assertTrue(
+ "1-1 query",
+ s.find("from Child c where c.parent.count=66").size()==1
+ );
+ assertTrue(
+ "1-1 query",
+ ( (Object[]) s.find("from Parent p join p.child c where p.count=66").get(0) ).length==2
+ );
+ s.find("select c, c.parent from Child c order by c.parent.count");
+ s.find("select c, c.parent from Child c where c.parent.count=66 order by c.parent.count");
+ s.iterate("select c, c.parent, c.parent.count from Child c order by c.parent.count");
+ assertTrue(
+ "1-1 query",
+ s.find("FROM Parent AS p WHERE p.count = ?", new Integer(66), Hibernate.INTEGER).size()==1
+ );
+ s.delete(c); s.delete(p);
+ t.commit();
+ s.close();
+ }
+
+ public void testParentNullChild() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Parent p = new Parent();
+ s.save(p);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ p = (Parent) s.load( Parent.class, new Long( p.getId() ) );
+ assertTrue( p.getChild()==null );
+ p.setCount(66);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ p = (Parent) s.load( Parent.class, new Long( p.getId() ) );
+ assertTrue( "null 1-1 update", p.getCount()==66 );
+ assertTrue( p.getChild()==null );
+ s.delete(p);
+ t.commit();
+ s.close();
+ }
+
+ public void testManyToMany() throws Exception {
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Container c = new Container();
+ c.setManyToMany( new ArrayList() );
+ c.setBag( new ArrayList() );
+ Simple s1 = new Simple();
+ Simple s2 = new Simple();
+ s1.setCount(123); s2.setCount(654);
+ Contained c1 = new Contained();
+ c1.setBag( new ArrayList() );
+ c1.getBag().add(c);
+ c.getBag().add(c1);
+ c.getManyToMany().add(s1);
+ c.getManyToMany().add(s2);
+ Serializable cid = s.save(c); //s.save(c1);
+ s.save(s1, new Long(12) ); s.save(s2, new Long(-1) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Container) s.load(Container.class, cid);
+ assertTrue( c.getBag().size()==1 );
+ assertTrue( c.getManyToMany().size()==2 );
+ c1 = (Contained) c.getBag().iterator().next();
+ assertTrue( c.getBag().size()==1 );
+ c.getBag().remove(c1);
+ c1.getBag().remove(c);
+ assertTrue( c.getManyToMany().remove(0)!=null );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Container) s.load(Container.class, cid);
+ assertTrue( c.getBag().size()==0 );
+ assertTrue( c.getManyToMany().size()==1 );
+ c1 = (Contained) s.load( Contained.class, new Long(c1.getId()) );
+ assertTrue( c1.getBag().size()==0 );
+ assertTrue( s.delete("from ContainerX c")==1 );
+ assertTrue( s.delete("from Contained")==1 );
+ assertTrue( s.delete("from Simple")==2 );
+ t.commit();
+ s.close();
+ }
+
+ public void testContainer() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Container c = new Container();
+ Simple x = new Simple(); x.setCount(123);
+ Simple y = new Simple(); y.setCount(456);
+ s.save( x, new Long(1) ); s.save( y, new Long(0) );
+ List o2m = new ArrayList();
+ o2m.add(x); o2m.add(null); o2m.add(y);
+ List m2m = new ArrayList();
+ m2m.add(x); m2m.add(null); m2m.add(y);
+ c.setOneToMany(o2m); c.setManyToMany(m2m);
+ List comps = new ArrayList();
+ Container.ContainerInnerClass ccic = new Container.ContainerInnerClass();
+ ccic.setName("foo");
+ ccic.setSimple(x);
+ comps.add(ccic);
+ comps.add(null);
+ ccic = new Container.ContainerInnerClass();
+ ccic.setName("bar");
+ ccic.setSimple(y);
+ comps.add(ccic);
+ HashSet compos = new HashSet();
+ compos.add(ccic);
+ c.setComposites(compos);
+ c.setComponents(comps);
+ One one = new One();
+ Many many = new Many();
+ HashSet manies = new HashSet();
+ manies.add(many);
+ one.setManies(manies);
+ many.setOne(one);
+ ccic.setMany(many);
+ ccic.setOne(one);
+ s.save(one);
+ s.save(many);
+ s.save(c);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Long count = (Long) s.createQuery("select count(*) from ContainerX as c join c.components as ce join ce.simple as s where ce.name='foo'").uniqueResult();
+ assertTrue( count.intValue()==1 );
+ List res = s.find("select c, s from ContainerX as c join c.components as ce join ce.simple as s where ce.name='foo'");
+ assertTrue(res.size()==1);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Container) s.load( Container.class, new Long( c.getId() ) );
+ System.out.println( c.getOneToMany() );
+ System.out.println( c.getManyToMany() );
+ System.out.println( c.getComponents() );
+ System.out.println( c.getComposites() );
+ ccic = (Container.ContainerInnerClass) c.getComponents().get(2);
+ assertTrue( ccic.getMany().getOne()==ccic.getOne() );
+ assertTrue( c.getComponents().size()==3 );
+ assertTrue( c.getComposites().size()==1 );
+ assertTrue( c.getOneToMany().size()==3 );
+ assertTrue( c.getManyToMany().size()==3 );
+ assertTrue( c.getOneToMany().get(0)!=null );
+ assertTrue( c.getOneToMany().get(2)!=null );
+ for ( int i=0; i<3; i++ ) {
+ assertTrue( c.getManyToMany().get(i) == c.getOneToMany().get(i) );
+ }
+ Object o1 = c.getOneToMany().get(0);
+ Object o2 = c.getOneToMany().remove(2);
+ c.getOneToMany().set(0, o2);
+ c.getOneToMany().set(1, o1);
+ o1 = c.getComponents().remove(2);
+ c.getComponents().set(0, o1);
+ c.getManyToMany().set( 0, c.getManyToMany().get(2) );
+ Container.ContainerInnerClass ccic2 = new Container.ContainerInnerClass();
+ ccic2.setName("foo");
+ ccic2.setOne(one);
+ ccic2.setMany(many);
+ ccic2.setSimple( (Simple) s.load(Simple.class, new Long(0) ) );
+ c.getComposites().add(ccic2);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Container) s.load( Container.class, new Long( c.getId() ) );
+ System.out.println( c.getOneToMany() );
+ System.out.println( c.getManyToMany() );
+ System.out.println( c.getComponents() );
+ System.out.println( c.getComposites() );
+ assertTrue( c.getComponents().size()==1 ); //WAS: 2
+ assertTrue( c.getComposites().size()==2 );
+ assertTrue( c.getOneToMany().size()==2 );
+ assertTrue( c.getManyToMany().size()==3 );
+ assertTrue( c.getOneToMany().get(0)!=null );
+ assertTrue( c.getOneToMany().get(1)!=null );
+ ( (Container.ContainerInnerClass) c.getComponents().get(0) ).setName("a different name");
+ ( (Container.ContainerInnerClass) c.getComposites().iterator().next() ).setName("once again");
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Container) s.load( Container.class, new Long( c.getId() ) );
+ System.out.println( c.getOneToMany() );
+ System.out.println( c.getManyToMany() );
+ System.out.println( c.getComponents() );
+ System.out.println( c.getComposites() );
+ assertTrue( c.getComponents().size()==1 ); //WAS: 2
+ assertTrue( c.getComposites().size()==2 );
+ assertTrue( ( (Container.ContainerInnerClass) c.getComponents().get(0) ).getName().equals("a different name") );
+ Iterator iter = c.getComposites().iterator();
+ boolean found = false;
+ while ( iter.hasNext() ) {
+ if ( ( (Container.ContainerInnerClass) iter.next() ).getName().equals("once again") ) found = true;
+ }
+ assertTrue(found);
+ c.getOneToMany().clear();
+ c.getManyToMany().clear();
+ c.getComposites().clear();
+ c.getComponents().clear();
+ s.delete("from Simple");
+ s.delete("from Many");
+ s.delete("from One");
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Container) s.load( Container.class, new Long( c.getId() ) );
+ assertTrue( c.getComponents().size()==0 );
+ assertTrue( c.getComposites().size()==0 );
+ assertTrue( c.getOneToMany().size()==0 );
+ assertTrue( c.getManyToMany().size()==0 );
+ s.delete(c);
+ t.commit();
+ s.close();
+ }
+
+ public void testCascadeCompositeElements() throws Exception {
+ Container c = new Container();
+ List list = new ArrayList();
+ c.setCascades(list);
+ Container.ContainerInnerClass cic = new Container.ContainerInnerClass();
+ cic.setMany( new Many() );
+ cic.setOne( new One() );
+ list.add(cic);
+ Session s = openSession();
+ s.save(c);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s=openSession();
+ c = (Container) s.iterate("from ContainerX c").next();
+ cic = (Container.ContainerInnerClass) c.getCascades().iterator().next();
+ assertTrue( cic.getMany()!=null && cic.getOne()!=null );
+ assertTrue( c.getCascades().size()==1 );
+ s.delete(c);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ c = new Container();
+ s = openSession();
+ s.save(c);
+ list = new ArrayList();
+ c.setCascades(list);
+ cic = new Container.ContainerInnerClass();
+ cic.setMany( new Many() );
+ cic.setOne( new One() );
+ list.add(cic);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s=openSession();
+ c = (Container) s.iterate("from ContainerX c").next();
+ cic = (Container.ContainerInnerClass) c.getCascades().iterator().next();
+ assertTrue( cic.getMany()!=null && cic.getOne()!=null );
+ assertTrue( c.getCascades().size()==1 );
+ s.delete(c);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testBag() throws Exception {
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Container c = new Container();
+ Contained c1 = new Contained();
+ Contained c2 = new Contained();
+ c.setBag( new ArrayList() );
+ c.getBag().add(c1);
+ c.getBag().add(c2);
+ c1.getBag().add(c);
+ c2.getBag().add(c);
+ s.save(c);
+ c.getBag().add(c2);
+ c2.getBag().add(c);
+ c.getLazyBag().add(c1);
+ c1.getLazyBag().add(c);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Container) s.find("from ContainerX c").get(0);
+ c.getLazyBag().size();
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Container) s.find("from ContainerX c").get(0);
+ Contained c3 = new Contained();
+ //c.getBag().add(c3);
+ //c3.getBag().add(c);
+ c.getLazyBag().add(c3);
+ c3.getLazyBag().add(c);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Container) s.find("from ContainerX c").get(0);
+ Contained c4 = new Contained();
+ c.getLazyBag().add(c4);
+ c4.getLazyBag().add(c);
+ assertTrue( c.getLazyBag().size()==3 ); //forces initialization
+ //s.save(c4);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Container) s.find("from ContainerX c").get(0);
+ Iterator i = c.getBag().iterator();
+ int j=0;
+ while ( i.hasNext() ) {
+ assertTrue( i.next()!=null );
+ j++;
+ }
+ assertTrue(j==3);
+ assertTrue( c.getLazyBag().size()==3 );
+ s.delete(c);
+ c.getBag().remove(c2);
+ Iterator iter = c.getBag().iterator();
+ j=0;
+ while ( iter.hasNext() ) {
+ j++;
+ s.delete( iter.next() );
+ }
+ assertTrue(j==2);
+ s.delete( s.load(Contained.class, new Long( c4.getId() ) ) );
+ s.delete( s.load(Contained.class, new Long( c3.getId() ) ) );
+ t.commit();
+ s.close();
+
+ }
+
+ public void testCircularCascade() throws Exception {
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ Circular c = new Circular();
+ c.setClazz(Circular.class);
+ c.setOther( new Circular() );
+ c.getOther().setOther( new Circular() );
+ c.getOther().getOther().setOther(c);
+ c.setAnyEntity( c.getOther() );
+ String id = (String) s.save(c);
+ tx.commit();
+ s.close();
+ s = openSession();
+ tx = s.beginTransaction();
+ c = (Circular) s.load(Circular.class, id);
+ c.getOther().getOther().setClazz(Foo.class);
+ tx.commit();
+ s.close();
+ c.getOther().setClazz(Qux.class);
+ s = openSession();
+ tx = s.beginTransaction();
+ s.saveOrUpdate(c);
+ tx.commit();
+ s.close();
+ c.getOther().getOther().setClazz(Bar.class);
+ s = openSession();
+ tx = s.beginTransaction();
+ s.saveOrUpdate(c);
+ tx.commit();
+ s.close();
+ s = openSession();
+ tx = s.beginTransaction();
+ c = (Circular) s.load(Circular.class, id);
+ assertTrue( c.getOther().getOther().getClazz()==Bar.class);
+ assertTrue( c.getOther().getClazz()==Qux.class);
+ assertTrue( c.getOther().getOther().getOther()==c);
+ assertTrue( c.getAnyEntity()==c.getOther() );
+ assertTrue( s.delete("from Universe")==3 );
+ tx.commit();
+ s.close();
+ }
+
+ public void testDeleteEmpty() throws Exception {
+ Session s = openSession();
+ assertTrue( s.delete("from Simple")==0 );
+ assertTrue( s.delete("from Universe")==0 );
+ s.close();
+ }
+
+ public void testLocking() throws Exception {
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ Simple s1 = new Simple(); s1.setCount(1);
+ Simple s2 = new Simple(); s2.setCount(2);
+ Simple s3 = new Simple(); s3.setCount(3);
+ Simple s4 = new Simple(); s4.setCount(4);
+ s.save(s1, new Long(1) );
+ s.save(s2, new Long(2) );
+ s.save(s3, new Long(3) );
+ s.save(s4, new Long(4) );
+ assertTrue( s.getCurrentLockMode(s1)==LockMode.WRITE );
+ tx.commit();
+ s.close();
+
+ s = openSession();
+ tx = s.beginTransaction();
+ s1 = (Simple) s.load(Simple.class, new Long(1), LockMode.NONE);
+ assertTrue( s.getCurrentLockMode(s1)==LockMode.READ || s.getCurrentLockMode(s1)==LockMode.NONE ); //depends if cache is enabled
+ s2 = (Simple) s.load(Simple.class, new Long(2), LockMode.READ);
+ assertTrue( s.getCurrentLockMode(s2)==LockMode.READ );
+ s3 = (Simple) s.load(Simple.class, new Long(3), LockMode.UPGRADE);
+ assertTrue( s.getCurrentLockMode(s3)==LockMode.UPGRADE );
+ s4 = (Simple) s.get(Simple.class, new Long(4), LockMode.UPGRADE_NOWAIT);
+ assertTrue( s.getCurrentLockMode(s4)==LockMode.UPGRADE_NOWAIT );
+
+ s1 = (Simple) s.load(Simple.class, new Long(1), LockMode.UPGRADE); //upgrade
+ assertTrue( s.getCurrentLockMode(s1)==LockMode.UPGRADE );
+ s2 = (Simple) s.load(Simple.class, new Long(2), LockMode.NONE);
+ assertTrue( s.getCurrentLockMode(s2)==LockMode.READ );
+ s3 = (Simple) s.load(Simple.class, new Long(3), LockMode.READ);
+ assertTrue( s.getCurrentLockMode(s3)==LockMode.UPGRADE );
+ s4 = (Simple) s.load(Simple.class, new Long(4), LockMode.UPGRADE);
+ assertTrue( s.getCurrentLockMode(s4)==LockMode.UPGRADE_NOWAIT );
+
+ s.lock(s2, LockMode.UPGRADE); //upgrade
+ assertTrue( s.getCurrentLockMode(s2)==LockMode.UPGRADE );
+ s.lock(s3, LockMode.UPGRADE);
+ assertTrue( s.getCurrentLockMode(s3)==LockMode.UPGRADE );
+ s.lock(s1, LockMode.UPGRADE_NOWAIT);
+ s.lock(s4, LockMode.NONE);
+ assertTrue( s.getCurrentLockMode(s4)==LockMode.UPGRADE_NOWAIT );
+
+ tx.commit();
+ tx = s.beginTransaction();
+
+ assertTrue( s.getCurrentLockMode(s3)==LockMode.NONE );
+ assertTrue( s.getCurrentLockMode(s1)==LockMode.NONE );
+ assertTrue( s.getCurrentLockMode(s2)==LockMode.NONE );
+ assertTrue( s.getCurrentLockMode(s4)==LockMode.NONE );
+
+ s.lock(s1, LockMode.READ); //upgrade
+ assertTrue( s.getCurrentLockMode(s1)==LockMode.READ );
+ s.lock(s2, LockMode.UPGRADE); //upgrade
+ assertTrue( s.getCurrentLockMode(s2)==LockMode.UPGRADE );
+ s.lock(s3, LockMode.UPGRADE_NOWAIT); //upgrade
+ assertTrue( s.getCurrentLockMode(s3)==LockMode.UPGRADE_NOWAIT );
+ s.lock(s4, LockMode.NONE);
+ assertTrue( s.getCurrentLockMode(s4)==LockMode.NONE );
+
+ s4.setName("s4");
+ s.flush();
+ assertTrue( s.getCurrentLockMode(s4)==LockMode.WRITE );
+ tx.commit();
+
+ tx = s.beginTransaction();
+
+ assertTrue( s.getCurrentLockMode(s3)==LockMode.NONE );
+ assertTrue( s.getCurrentLockMode(s1)==LockMode.NONE );
+ assertTrue( s.getCurrentLockMode(s2)==LockMode.NONE );
+ assertTrue( s.getCurrentLockMode(s4)==LockMode.NONE );
+
+ s.delete(s1); s.delete(s2); s.delete(s3); s.delete(s4);
+ tx.commit();
+ s.close();
+ }
+
+ public void testObjectType() throws Exception {
+ Session s = openSession();
+ Parent g = new Parent();
+ Foo foo = new Foo();
+ g.setAny(foo);
+ s.save(g);
+ s.save(foo);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ g = (Parent) s.load( Parent.class, new Long( g.getId() ) );
+ assertTrue( g.getAny()!=null && g.getAny() instanceof FooProxy );
+ s.delete( g.getAny() );
+ s.delete(g);
+ s.flush();
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testLoadAfterNonExists() throws HibernateException, SQLException {
+
+ Session session = openSession();
+ if ( (getDialect() instanceof MySQLDialect) ) {
+ session.connection().setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
+ }
+
+ // First, prime the fixture session to think the entity does not exist
+ try {
+ session.load( Simple.class, new Long(-1) );
+ fail();
+ }
+ catch(ObjectNotFoundException onfe) {
+ // this is correct
+ }
+
+ // Next, lets create that entity under the covers
+ Session anotherSession = getSessions().openSession();
+ Simple myNewSimple = new Simple();
+ myNewSimple.setName("My under the radar Simple entity");
+ myNewSimple.setAddress("SessionCacheTest.testLoadAfterNonExists");
+ myNewSimple.setCount(1);
+ myNewSimple.setDate( new Date() );
+ myNewSimple.setPay( new Float(100000000) );
+ anotherSession.save( myNewSimple, new Long(-1) );
+ anotherSession.flush();
+ anotherSession.connection().commit();
+ anotherSession.close();
+
+ // Verify that the original session is still able to see the new entry...
+ //try {
+ session.load( Simple.class, new Long(-1) );
+ /*fail();
+ }
+ catch(ObjectNotFoundException onfe) {
+ }*/
+
+ // Now, lets clear the original session at which point it should be able to see
+ // the new entity
+ session.clear();
+ try {
+ Simple dummy = (Simple) session.load( Simple.class, new Long(-1) );
+ assertNotNull("Unable to locate entity Simple with id = -1", dummy);
+ }
+ catch(ObjectNotFoundException onfe) {
+ fail("Unable to locate entity Simple with id = -1");
+ }
+ session.connection().commit();
+ session.close();
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Part.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Part.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Part.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,30 @@
+//$Id: Part.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+/**
+ * @author Gavin King
+ */
+public class Part {
+
+ private Long id;
+ private String description;
+
+ public String getDescription() {
+ return description;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setDescription(String string) {
+ description = string;
+ }
+
+ public void setId(Long long1) {
+ id = long1;
+ }
+
+ public static class SpecialPart extends Part {}
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Party.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Party.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Party.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,38 @@
+package org.hibernate.test.legacy;
+
+/**
+ * @author hbm2java
+ */
+public class Party {
+
+ java.lang.String id;
+ java.lang.String name;
+ java.lang.String address;
+
+
+ java.lang.String getId() {
+ return id;
+ }
+
+ void setId(java.lang.String newValue) {
+ id = newValue;
+ }
+
+ java.lang.String getName() {
+ return name;
+ }
+
+ void setName(java.lang.String newValue) {
+ name = newValue;
+ }
+
+ java.lang.String getAddress() {
+ return address;
+ }
+
+ void setAddress(java.lang.String newValue) {
+ address = newValue;
+ }
+
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/PerformanceTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/PerformanceTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/PerformanceTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,356 @@
+//$Id: PerformanceTest.java 6900 2005-05-25 01:24:22Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Types;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import junit.textui.TestRunner;
+import org.hibernate.classic.Session;
+import org.hibernate.cfg.Environment;
+import org.hibernate.connection.ConnectionProvider;
+import org.hibernate.connection.ConnectionProviderFactory;
+import org.hibernate.test.TestCase;
+
+public class PerformanceTest extends TestCase {
+
+ public PerformanceTest(String arg0) {
+ super(arg0);
+ }
+
+ public void testMany() throws Exception {
+
+ ConnectionProvider cp = ConnectionProviderFactory.newConnectionProvider( Environment.getProperties() );
+
+ long hiber=0;
+ long jdbc=0;
+
+ for ( int n=0; n<20; n++ ) {
+
+ Simple[] simples = new Simple[n];
+ Serializable[] ids = new Serializable[n];
+ for ( int i=0; i<n; i++ ) {
+ simples[i] = new Simple();
+ simples[i].init();
+ simples[i].setCount(i);
+ ids[i] = new Long(i);
+ }
+
+ //allow cache to settle
+
+ Session s = openSession();
+ hibernate(s, simples, ids, n, "h0");
+ s.close();
+
+ Connection c = cp.getConnection();
+ directJDBC( c, simples, ids, n, "j0" );
+ cp.closeConnection(c);
+
+ s = openSession();
+ hibernate(s, simples, ids, n, "h0");
+ s.close();
+
+ c = cp.getConnection();
+ directJDBC( c, simples, ids, n, "j0" );
+ cp.closeConnection(c);
+
+ //Now do timings
+
+ int N=30;
+
+ long time = System.currentTimeMillis();
+ for (int i=0; i<N; i++) {
+ s = openSession();
+ hibernate(s, simples, ids, n, "h1");
+ s.close();
+ }
+ hiber += System.currentTimeMillis() - time;
+
+ time = System.currentTimeMillis();
+ for (int i=0; i<N; i++) {
+ c = cp.getConnection();
+ directJDBC( c, simples, ids, n, "j1" );
+ cp.closeConnection(c);
+ }
+ jdbc += System.currentTimeMillis() - time;
+
+ time = System.currentTimeMillis();
+ for (int i=0; i<N; i++) {
+ s = openSession();
+ hibernate(s, simples, ids, n, "h2");
+ s.close();
+ }
+ hiber += System.currentTimeMillis() - time;
+
+ time = System.currentTimeMillis();
+ for (int i=0; i<N; i++) {
+ c = cp.getConnection();
+ directJDBC( c, simples, ids, n, "j2" );
+ cp.closeConnection(c);
+ }
+ jdbc += System.currentTimeMillis() - time;
+
+ time = System.currentTimeMillis();
+ for (int i=0; i<N; i++) {
+ s = openSession();
+ hibernate(s, simples, ids, n, "h1");
+ s.close();
+ }
+ hiber += System.currentTimeMillis() - time;
+
+ time = System.currentTimeMillis();
+ for (int i=0; i<N; i++) {
+ c = cp.getConnection();
+ directJDBC( c, simples, ids, n, "j1" );
+ cp.closeConnection(c);
+ }
+ jdbc += System.currentTimeMillis() - time;
+
+ }
+
+ System.out.println( "Hibernate: " + hiber + "ms / Direct JDBC: " + jdbc + "ms = Ratio: " + ( (float) hiber )/jdbc );
+
+ cp.close();
+ System.gc();
+ }
+
+ public void testSimultaneous() throws Exception {
+
+ ConnectionProvider cp = ConnectionProviderFactory.newConnectionProvider( Environment.getProperties() );
+
+ for ( int n=2; n<4000; n*=2 ) {
+
+ Simple[] simples = new Simple[n];
+ Serializable[] ids = new Serializable[n];
+ for ( int i=0; i<n; i++ ) {
+ simples[i] = new Simple();
+ simples[i].init();
+ simples[i].setCount(i);
+ ids[i] = new Long(i);
+ }
+
+ //allow cache to settle
+
+ Session s = openSession();
+ hibernate(s, simples, ids, n, "h0");
+ s.close();
+
+ Connection c = cp.getConnection();
+ directJDBC( c, simples, ids, n, "j0" );
+ cp.closeConnection(c);
+
+ s = openSession();
+ hibernate(s, simples, ids, n, "h0");
+ s.close();
+
+ c = cp.getConnection();
+ directJDBC( c, simples, ids, n, "j0" );
+ cp.closeConnection(c);
+
+ //Now do timings
+
+ s = openSession();
+ long time = System.currentTimeMillis();
+ hibernate(s, simples, ids, n, "h1");
+ long hiber = System.currentTimeMillis() - time;
+ s.close();
+
+ c = cp.getConnection();
+ time = System.currentTimeMillis();
+ directJDBC( c, simples, ids, n, "j1" );
+ long jdbc = System.currentTimeMillis() - time;
+ cp.closeConnection(c);
+
+ s = openSession();
+ time = System.currentTimeMillis();
+ hibernate(s, simples, ids, n, "h2");
+ hiber += System.currentTimeMillis() - time;
+ s.close();
+
+ c = cp.getConnection();
+ time = System.currentTimeMillis();
+ directJDBC( c, simples, ids, n, "j2" );
+ jdbc += System.currentTimeMillis() - time;
+ cp.closeConnection(c);
+
+ s = openSession();
+ time = System.currentTimeMillis();
+ hibernate(s, simples, ids, n, "h2");
+ hiber += System.currentTimeMillis() - time;
+ s.close();
+
+ c = cp.getConnection();
+ time = System.currentTimeMillis();
+ directJDBC( c, simples, ids, n, "j2" );
+ jdbc += System.currentTimeMillis() - time;
+ cp.closeConnection(c);
+
+ System.out.println( "Objects: " + n + " - Hibernate: " + hiber + "ms / Direct JDBC: " + jdbc + "ms = Ratio: " + ( (float) hiber )/jdbc );
+
+ }
+
+ cp.close();
+ System.gc();
+ }
+
+ public void testHibernateOnly() throws Exception {
+
+ for ( int n=2; n<4000; n*=2 ) {
+
+ Simple[] simples = new Simple[n];
+ Serializable[] ids = new Serializable[n];
+ for ( int i=0; i<n; i++ ) {
+ simples[i] = new Simple();
+ simples[i].init();
+ simples[i].setCount(i);
+ ids[i] = new Long(i);
+ }
+
+ //Now do timings
+
+ Session s = openSession();
+ long time = System.currentTimeMillis();
+ hibernate(s, simples, ids, n, "h1");
+ long hiber = System.currentTimeMillis() - time;
+ s.close();
+
+ s = openSession();
+ time = System.currentTimeMillis();
+ hibernate(s, simples, ids, n, "h2");
+ hiber += System.currentTimeMillis() - time;
+ s.close();
+
+ s = openSession();
+ time = System.currentTimeMillis();
+ hibernate(s, simples, ids, n, "h2");
+ hiber += System.currentTimeMillis() - time;
+ s.close();
+
+ System.out.println( "Objects: " + n + " - Hibernate: " + hiber );
+
+ }
+
+ System.gc();
+ }
+
+ public void testJdbcOnly() throws Exception {
+
+ ConnectionProvider cp = ConnectionProviderFactory.newConnectionProvider( Environment.getProperties() );
+
+ for ( int n=2; n<4000; n*=2 ) {
+
+ Simple[] simples = new Simple[n];
+ Serializable[] ids = new Serializable[n];
+ for ( int i=0; i<n; i++ ) {
+ simples[i] = new Simple();
+ simples[i].init();
+ simples[i].setCount(i);
+ ids[i] = new Long(i);
+ }
+
+ //Now do timings
+
+ Connection c = cp.getConnection();
+ long time = System.currentTimeMillis();
+ directJDBC( c, simples, ids, n, "j1" );
+ long jdbc = System.currentTimeMillis() - time;
+ cp.closeConnection(c);
+
+ c = cp.getConnection();
+ time = System.currentTimeMillis();
+ directJDBC( c, simples, ids, n, "j2" );
+ jdbc += System.currentTimeMillis() - time;
+ cp.closeConnection(c);
+
+ c = cp.getConnection();
+ time = System.currentTimeMillis();
+ directJDBC( c, simples, ids, n, "j2" );
+ jdbc += System.currentTimeMillis() - time;
+ cp.closeConnection(c);
+
+ System.out.println( "Objects: " + n + " Direct JDBC: " + jdbc );
+
+ }
+
+ cp.close();
+ System.gc();
+ }
+
+ private void hibernate(Session s, Simple[] simples, Serializable[] ids, int N, String runname) throws Exception {
+ for ( int i=0; i<N; i++ ) {
+ s.save( simples[i], ids[i] );
+ }
+ for ( int i=0; i<N; i++ ) {
+ simples[0].setName("A Different Name!" + i + N + runname);
+ }
+ //s.flush();
+ // the results of this test are highly dependent upon
+ // how many times we flush!
+ assertTrue( "assertion", s.delete("from Simple s")==N );
+ s.flush();
+ s.connection().commit();
+ }
+
+ private void directJDBC(Connection c, Simple[] simples, Serializable[] ids, int N, String runname) throws SQLException {
+
+ PreparedStatement insert = c.prepareStatement("insert into Simple ( name, address, count_, date_, other, id_ ) values ( ?, ?, ?, ?, ?, ? )");
+ PreparedStatement delete = c.prepareStatement("delete from Simple where id_ = ?");
+ PreparedStatement select = c.prepareStatement("SELECT s.id_, s.name, s.address, s.count_, s.date_, s.other FROM Simple s");
+ PreparedStatement update = c.prepareStatement("update Simple set name = ?, address = ?, count_ = ?, date_ = ?, other = ? where id_ = ?");
+ for ( int i=0; i<N; i++ ) {
+ insert.setString(1, simples[i].getName() );
+ insert.setString(2, simples[i].getAddress() );
+ insert.setInt(3, simples[i].getCount() );
+ insert.setDate( 4, (java.sql.Date) simples[i].getDate() );
+ insert.setNull(5, Types.BIGINT);
+ insert.setLong( 6, ( (Long) ids[i] ).longValue() );
+ insert.executeUpdate();
+ }
+ insert.close();
+ for ( int i=0; i<N; i++ ) {
+ update.setString(1, "A Different Name!" + i + N + runname );
+ update.setString(2, simples[i].getAddress() );
+ update.setInt(3, simples[i].getCount() );
+ update.setDate( 4, (java.sql.Date) simples[i].getDate() );
+ update.setNull(5, Types.BIGINT);
+ update.setLong( 6, ( (Long) ids[i] ).longValue() );
+ update.executeUpdate();
+ }
+ update.close();
+ java.sql.ResultSet rs = select.executeQuery();
+ Long[] keys = new Long[N];
+ int j=0;
+ while ( rs.next() ) {
+ keys[j++] = new Long( rs.getLong(1) );
+ rs.getString(2);
+ rs.getString(3);
+ rs.getInt(4);
+ rs.getDate(5);
+ rs.getLong(6);
+ }
+ rs.close();
+ select.close();
+ for ( int i=0; i<N; i++ ) {
+ delete.setLong(1, keys[i].longValue() );
+ delete.executeUpdate();
+ }
+ delete.close();
+ c.commit();
+ }
+
+ public String[] getMappings() {
+ return new String[] { "legacy/Simple.hbm.xml" };
+ }
+
+ public static Test suite() throws Exception {
+ return new TestSuite(PerformanceTest.class);
+ }
+
+ public static void main(String[] args) throws Exception {
+ TestRunner.run( suite() );
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Person.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Person.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Person.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,47 @@
+package org.hibernate.test.legacy;
+
+/**
+ * @author hbm2java
+ */
+public class Person extends org.hibernate.test.legacy.Party {
+
+ java.lang.String id;
+ java.lang.String givenName;
+ java.lang.String lastName;
+ java.lang.String nationalID;
+
+
+ java.lang.String getId() {
+ return id;
+ }
+
+ void setId(java.lang.String newValue) {
+ id = newValue;
+ }
+
+ java.lang.String getGivenName() {
+ return givenName;
+ }
+
+ void setGivenName(java.lang.String newValue) {
+ givenName = newValue;
+ }
+
+ java.lang.String getLastName() {
+ return lastName;
+ }
+
+ void setLastName(java.lang.String newValue) {
+ lastName = newValue;
+ }
+
+ java.lang.String getNationalID() {
+ return nationalID;
+ }
+
+ void setNationalID(java.lang.String newValue) {
+ nationalID = newValue;
+ }
+
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Po.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Po.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Po.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,104 @@
+//$Id: Po.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.util.List;
+import java.util.Set;
+
+
+/**
+ *
+ */
+public class Po {
+ private long id;
+ private String value;
+ private Set set;
+ private List list;
+ private Top top;
+ private Lower lower;
+ /**
+ * Returns the id.
+ * @return long
+ */
+ public long getId() {
+ return id;
+ }
+
+ /**
+ * Returns the value.
+ * @return String
+ */
+ public String getValue() {
+ return value;
+ }
+
+ /**
+ * Sets the id.
+ * @param id The id to set
+ */
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ /**
+ * Sets the value.
+ * @param value The value to set
+ */
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ /**
+ * Returns the set.
+ * @return Set
+ */
+ public Set getSet() {
+ return set;
+ }
+
+ /**
+ * Sets the set.
+ * @param set The set to set
+ */
+ public void setSet(Set set) {
+ this.set = set;
+ }
+
+ /**
+ * Returns the list.
+ * @return List
+ */
+ public List getList() {
+ return list;
+ }
+
+ /**
+ * Sets the list.
+ * @param list The list to set
+ */
+ public void setList(List list) {
+ this.list = list;
+ }
+
+ public Lower getLower() {
+ return lower;
+ }
+
+ public Top getTop() {
+ return top;
+ }
+
+ public void setLower(Lower lower) {
+ this.lower = lower;
+ }
+
+ public void setTop(Top top) {
+ this.top = top;
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/QueryByExampleTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/QueryByExampleTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/QueryByExampleTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,136 @@
+//$Id: QueryByExampleTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.legacy;
+
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.hibernate.Criteria;
+import org.hibernate.Transaction;
+import org.hibernate.classic.Session;
+import org.hibernate.criterion.Example;
+import org.hibernate.criterion.Expression;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * Query by example test to allow nested components
+ *
+ * @author Emmanuel Bernard
+ */
+public class QueryByExampleTest extends LegacyTestCase {
+
+ public QueryByExampleTest(String name) {
+ super(name);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "legacy/Componentizable.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( QueryByExampleTest.class );
+ }
+
+ public void testSimpleQBE() throws Exception {
+ deleteData();
+ initData();
+
+ Session s = openSession();
+
+ Transaction t = s.beginTransaction();
+ Componentizable master = getMaster("hibernate", "open sourc%", "open source1");
+ Criteria crit = s.createCriteria(Componentizable.class);
+ Example ex = Example.create(master).enableLike();
+ crit.add(ex);
+ List result = crit.list();
+ assertNotNull(result);
+ assertEquals(1, result.size());
+
+ t.commit();
+ s.close();
+ }
+
+ public void testJunctionNotExpressionQBE() throws Exception {
+ deleteData();
+ initData();
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Componentizable master = getMaster("hibernate", null, "ope%");
+ Criteria crit = s.createCriteria(Componentizable.class);
+ Example ex = Example.create(master).enableLike();
+
+ crit.add(Expression.or(Expression.not(ex), ex));
+
+ List result = crit.list();
+ assertNotNull(result);
+ assertEquals(2, result.size());
+ t.commit();
+ s.close();
+
+ }
+
+ public void testExcludingQBE() throws Exception {
+ deleteData();
+ initData();
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Componentizable master = getMaster("hibernate", null, "ope%");
+ Criteria crit = s.createCriteria(Componentizable.class);
+ Example ex = Example.create(master).enableLike()
+ .excludeProperty("component.subComponent");
+ crit.add(ex);
+ List result = crit.list();
+ assertNotNull(result);
+ assertEquals(3, result.size());
+
+ master = getMaster("hibernate", "ORM tool", "fake stuff");
+ crit = s.createCriteria(Componentizable.class);
+ ex = Example.create(master).enableLike()
+ .excludeProperty("component.subComponent.subName1");
+ crit.add(ex);
+ result = crit.list();
+ assertNotNull(result);
+ assertEquals(1, result.size());
+ t.commit();
+ s.close();
+
+
+ }
+
+ private void initData() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Componentizable master = getMaster("hibernate", "ORM tool", "ORM tool1");
+ s.saveOrUpdate(master);
+ master = getMaster("hibernate", "open source", "open source1");
+ s.saveOrUpdate(master);
+ master = getMaster("hibernate", null, null);
+ s.saveOrUpdate(master);
+ t.commit();
+ s.close();
+ }
+
+ private void deleteData() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.delete("from Componentizable");
+ t.commit();
+ s.close();
+ }
+
+ private Componentizable getMaster(String name, String subname, String subname1) {
+ Componentizable master = new Componentizable();
+ if (name != null) {
+ Component masterComp = new Component();
+ masterComp.setName(name);
+ if (subname != null || subname1 != null) {
+ SubComponent subComponent = new SubComponent();
+ subComponent.setSubName(subname);
+ subComponent.setSubName1(subname1);
+ masterComp.setSubComponent(subComponent);
+ }
+ master.setComponent(masterComp);
+ }
+ return master;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Qux.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Qux.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Qux.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,36 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping default-lazy="false">
+
+ <class name="org.hibernate.test.legacy.Qux" table="quux" proxy="org.hibernate.test.legacy.Qux"> <!---->
+ <id name="key" column="qux_key" unsaved-value="0">
+ <generator class="hilo"/>
+ </id>
+ <many-to-one name="foo" class="org.hibernate.test.legacy.Foo">
+ <column name="foo" length="36"/>
+ </many-to-one>
+ <property name="deleted"/>
+ <property name="loaded"/>
+ <property name="stored"/>
+ <property name="created"/>
+ <property name="childKey" type="long"/>
+ <property name="stuff"/>
+ <set name="fums" lazy="true">
+ <key column="qux_id"/>
+ <many-to-many class="org.hibernate.test.legacy.Fum">
+ <column name="fum_string" length="10" not-null="true"/>
+ <column name="fum_short" not-null="true"/>
+ <column name="fum_date" not-null="true"/>
+ </many-to-many>
+ </set>
+ <list name="moreFums" lazy="true">
+ <key column="qux_id"/>
+ <index column="posn"/>
+ <one-to-many class="org.hibernate.test.legacy.Fum"/>
+ </list>
+ <many-to-one name="holder" column="HOLDER_NAME" property-ref="name"/>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Qux.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Qux.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Qux.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,180 @@
+//$Id: Qux.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+import java.sql.SQLException;
+import java.util.List;
+import java.util.Set;
+
+import org.hibernate.CallbackException;
+import org.hibernate.HibernateException;
+import org.hibernate.Session;
+import org.hibernate.classic.Lifecycle;
+
+public class Qux implements Lifecycle {
+
+ boolean created;
+ boolean deleted;
+ boolean loaded;
+ boolean stored;
+ private Long key;
+ private String stuff;
+ private Set fums;
+ private List moreFums;
+ private Qux child;
+ private Session session;
+ private Long childKey;
+ private Holder holder;
+
+ private FooProxy foo;
+
+ public Qux() { }
+
+ public Qux(String s) {
+ stuff=s;
+ }
+
+ public boolean onSave(Session session) throws CallbackException {
+ created=true;
+ try {
+ foo = new Foo();
+ session.save(foo);
+ }
+ catch (Exception e) {
+ throw new CallbackException(e);
+ }
+ foo.setString("child of a qux");
+ return NO_VETO;
+ }
+
+ public boolean onDelete(Session session) throws CallbackException {
+ deleted=true;
+ try {
+ session.delete(foo);
+ }
+ catch (Exception e) {
+ throw new CallbackException(e);
+ }
+ //if (child!=null) session.delete(child);
+ return NO_VETO;
+ }
+
+ public void onLoad(Session session, Serializable id) {
+ loaded=true;
+ this.session=session;
+ }
+
+ public void store() {
+ }
+
+ public FooProxy getFoo() {
+ return foo;
+ }
+ public void setFoo(FooProxy foo) {
+ this.foo = foo;
+ }
+
+ public boolean getCreated() {
+ return created;
+ }
+ private void setCreated(boolean created) {
+ this.created = created;
+ }
+
+ public boolean getDeleted() {
+ return deleted;
+ }
+
+ private void setDeleted(boolean deleted) {
+ this.deleted = deleted;
+ }
+
+ public boolean getLoaded() {
+ return loaded;
+ }
+ private void setLoaded(boolean loaded) {
+ this.loaded = loaded;
+ }
+
+ public boolean getStored() {
+ return stored;
+ }
+ private void setStored(boolean stored) {
+ this.stored = stored;
+ }
+
+ public Long getKey() {
+ return key;
+ }
+
+ private void setKey(long key) {
+ this.key = new Long(key);
+ }
+
+ public void setTheKey(long key) {
+ this.key = new Long(key);
+ }
+
+ public String getStuff() {
+ return stuff;
+ }
+ public void setStuff(String stuff) {
+ this.stuff = stuff;
+ }
+
+ public Set getFums() {
+ return fums;
+ }
+
+ public void setFums(Set fums) {
+ this.fums = fums;
+ }
+
+ public List getMoreFums() {
+ return moreFums;
+ }
+ public void setMoreFums(List moreFums) {
+ this.moreFums = moreFums;
+ }
+
+ public Qux getChild() throws HibernateException, SQLException {
+ stored=true;
+ this.childKey = child==null ? null : child.getKey();
+ if (childKey!=null && child==null) child = (Qux) session.load(Qux.class, childKey);
+ return child;
+ }
+
+ public void setChild(Qux child) {
+ this.child = child;
+ }
+
+ private Long getChildKey() {
+ return childKey;
+ }
+
+ private void setChildKey(Long childKey) {
+ this.childKey = childKey;
+ }
+
+ public boolean onUpdate(Session s) throws CallbackException {
+ return NO_VETO;
+ }
+
+ protected void finalize() { }
+
+ public Holder getHolder() {
+ return holder;
+ }
+
+ public void setHolder(Holder holder) {
+ this.holder = holder;
+ }
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Resource.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Resource.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Resource.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,38 @@
+package org.hibernate.test.legacy;
+
+/**
+ * @author hbm2java
+ */
+public class Resource {
+
+ java.lang.String id;
+ java.lang.String name;
+ java.lang.String userCode;
+
+
+ java.lang.String getId() {
+ return id;
+ }
+
+ void setId(java.lang.String newValue) {
+ id = newValue;
+ }
+
+ java.lang.String getName() {
+ return name;
+ }
+
+ void setName(java.lang.String newValue) {
+ name = newValue;
+ }
+
+ java.lang.String getUserCode() {
+ return userCode;
+ }
+
+ void setUserCode(java.lang.String newValue) {
+ userCode = newValue;
+ }
+
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Result.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Result.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Result.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,67 @@
+package org.hibernate.test.legacy;
+
+
+public class Result {
+ private String name;
+ private long amount;
+ private long count;
+ /**
+ * Returns the amount.
+ * @return long
+ */
+ public long getAmount() {
+ return amount;
+ }
+
+ /**
+ * Returns the count.
+ * @return int
+ */
+ public long getCount() {
+ return count;
+ }
+
+ /**
+ * Returns the name.
+ * @return String
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the amount.
+ * @param amount The amount to set
+ */
+ public void setAmount(long amount) {
+ this.amount = amount;
+ }
+
+ /**
+ * Sets the count.
+ * @param count The count to set
+ */
+ public void setCount(long count) {
+ this.count = count;
+ }
+
+ /**
+ * Sets the name.
+ * @param name The name to set
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Result(String n, long a, int c) {
+ name = n;
+ amount = a;
+ count = c;
+ }
+
+ public Result(String n, long a, long c) {
+ name = n;
+ amount = a;
+ count = c;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ReverseComparator.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ReverseComparator.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/ReverseComparator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,26 @@
+//$Id: ReverseComparator.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+import java.util.Comparator;
+
+public final class ReverseComparator implements Comparator, Serializable {
+ public int compare(Object x, Object y) {
+ return - ( (Comparable) x ).compareTo(y);
+ }
+
+ public boolean equals(Object obj) {
+ return obj instanceof ReverseComparator;
+ }
+
+ public int hashCode() {
+ return 0;
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Role.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Role.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Role.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,48 @@
+package org.hibernate.test.legacy;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author hbm2java
+ */
+public class Role {
+
+ long id;
+ java.lang.String name;
+ Set interventions = new HashSet();
+private List bunchOfStrings;
+
+ long getId() {
+ return id;
+ }
+
+ void setId(long newValue) {
+ id = newValue;
+ }
+
+ java.lang.String getName() {
+ return name;
+ }
+
+ void setName(java.lang.String newValue) {
+ name = newValue;
+ }
+
+ public Set getInterventions() {
+ return interventions;
+ }
+
+ public void setInterventions(Set iv) {
+ interventions = iv;
+ }
+
+ List getBunchOfStrings() {
+ return bunchOfStrings;
+ }
+
+ void setBunchOfStrings(List s) {
+ bunchOfStrings = s;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/S.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/S.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/S.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,42 @@
+//$Id: S.java 4599 2004-09-26 05:18:27Z oneovthafew $
+
+package org.hibernate.test.legacy;
+
+public class S {
+ private String address;
+ private int count;
+
+ public S(int countArg, String addressArg) {
+ count = countArg;
+ address = addressArg;
+ }
+
+ /**
+ * Gets the address
+ * @return Returns a String
+ */
+ public String getAddress() {
+ return address;
+ }
+ /**
+ * Sets the address
+ * @param address The address to set
+ */
+ public void setAddress(String address) {
+ this.address = address;
+ }
+ /**
+ * Gets the count
+ * @return Returns a int
+ */
+ public int getCount() {
+ return count;
+ }
+ /**
+ * Sets the count
+ * @param count The count to set
+ */
+ public void setCount(int count) {
+ this.count = count;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/SQLFunctionsTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/SQLFunctionsTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/SQLFunctionsTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,659 @@
+//$Id: SQLFunctionsTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.legacy;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.Test;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Query;
+import org.hibernate.ScrollableResults;
+import org.hibernate.Transaction;
+import org.hibernate.classic.Session;
+import org.hibernate.dialect.DB2Dialect;
+import org.hibernate.dialect.HSQLDialect;
+import org.hibernate.dialect.InterbaseDialect;
+import org.hibernate.dialect.MckoiDialect;
+import org.hibernate.dialect.MySQLDialect;
+import org.hibernate.dialect.Oracle9Dialect;
+import org.hibernate.dialect.OracleDialect;
+import org.hibernate.dialect.SybaseDialect;
+import org.hibernate.dialect.TimesTenDialect;
+import org.hibernate.dialect.function.SQLFunction;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+
+public class SQLFunctionsTest extends LegacyTestCase {
+
+ private static final Log log = LogFactory.getLog(SQLFunctionsTest.class);
+
+ public SQLFunctionsTest(String name) {
+ super(name);
+ }
+
+ public String[] getMappings() {
+ return new String[] {
+ "legacy/AltSimple.hbm.xml",
+ "legacy/Broken.hbm.xml",
+ "legacy/Blobber.hbm.xml"
+ };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( SQLFunctionsTest.class );
+ }
+
+ public void testDialectSQLFunctions() throws Exception {
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ Iterator iter = s.iterate("select max(s.count) from Simple s");
+
+ if ( getDialect() instanceof MySQLDialect ) assertTrue( iter.hasNext() && iter.next()==null );
+
+ Simple simple = new Simple();
+ simple.setName("Simple Dialect Function Test");
+ simple.setAddress("Simple Address");
+ simple.setPay(new Float(45.8));
+ simple.setCount(2);
+ s.save(simple, new Long(10) );
+
+ // Test to make sure allocating an specified object operates correctly.
+ assertTrue(
+ s.find("select new org.hibernate.test.legacy.S(s.count, s.address) from Simple s").size() == 1
+ );
+
+ // Quick check the base dialect functions operate correctly
+ assertTrue(
+ s.find("select max(s.count) from Simple s").size() == 1
+ );
+ assertTrue(
+ s.find("select count(*) from Simple s").size() == 1
+ );
+
+ if ( getDialect() instanceof OracleDialect) {
+ // Check Oracle Dialect mix of dialect functions - no args (no parenthesis and single arg functions
+ java.util.List rset = s.find("select s.name, sysdate(), trunc(s.pay), round(s.pay) from Simple s");
+ assertNotNull("Name string should have been returned",(((Object[])rset.get(0))[0]));
+ assertNotNull("Todays Date should have been returned",(((Object[])rset.get(0))[1]));
+ assertEquals("trunc(45.8) result was incorrect ", new Float(45), ( (Object[]) rset.get(0) )[2] );
+ assertEquals("round(45.8) result was incorrect ", new Float(46), ( (Object[]) rset.get(0) )[3] );
+
+ simple.setPay(new Float(-45.8));
+ s.update(simple);
+
+ // Test type conversions while using nested functions (Float to Int).
+ rset = s.find("select abs(round(s.pay)) from Simple s");
+ assertEquals("abs(round(-45.8)) result was incorrect ", new Float(46), rset.get(0));
+
+ // Test a larger depth 3 function example - Not a useful combo other than for testing
+ assertTrue(
+ s.find("select trunc(round(sysdate())) from Simple s").size() == 1
+ );
+
+ // Test the oracle standard NVL funtion as a test of multi-param functions...
+ simple.setPay(null);
+ s.update(simple);
+ Integer value = (Integer) s.find("select MOD( NVL(s.pay, 5000), 2 ) from Simple as s where s.id = 10").get(0);
+ assertTrue( 0 == value.intValue() );
+ }
+
+ if ( (getDialect() instanceof HSQLDialect) ) {
+ // Test the hsql standard MOD funtion as a test of multi-param functions...
+ Integer value = (Integer) s.find("select MOD(s.count, 2) from Simple as s where s.id = 10" ).get(0);
+ assertTrue( 0 == value.intValue() );
+ }
+
+ s.delete(simple);
+ t.commit();
+ s.close();
+ }
+
+ public void testSetProperties() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Simple simple = new Simple();
+ simple.setName("Simple 1");
+ s.save(simple, new Long(10) );
+ Query q = s.createQuery("from Simple s where s.name=:name and s.count=:count");
+ q.setProperties(simple);
+ assertTrue( q.list().get(0)==simple );
+ //misuse of "Single" as a propertyobject, but it was the first testclass i found with a collection ;)
+ Single single = new Single() { // trivial hack to test properties with arrays.
+ String[] getStuff() { return (String[]) getSeveral().toArray(new String[getSeveral().size()]); }
+ };
+
+ List l = new ArrayList();
+ l.add("Simple 1");
+ l.add("Slimeball");
+ single.setSeveral(l);
+ q = s.createQuery("from Simple s where s.name in (:several)");
+ q.setProperties(single);
+ assertTrue( q.list().get(0)==simple );
+
+
+ q = s.createQuery("from Simple s where s.name in (:stuff)");
+ q.setProperties(single);
+ assertTrue( q.list().get(0)==simple );
+ s.delete(simple);
+ t.commit();
+ s.close();
+ }
+
+ public void testSetPropertiesMap() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Simple simple = new Simple();
+ simple.setName("Simple 1");
+ s.save(simple, new Long(10) );
+ Map parameters = new HashMap();
+ parameters.put("name", simple.getName());
+ parameters.put("count", new Integer(simple.getCount()));
+
+ Query q = s.createQuery("from Simple s where s.name=:name and s.count=:count");
+ q.setProperties(((Map)parameters));
+ assertTrue( q.list().get(0)==simple );
+
+ List l = new ArrayList();
+ l.add("Simple 1");
+ l.add("Slimeball");
+ parameters.put("several", l);
+ q = s.createQuery("from Simple s where s.name in (:several)");
+ q.setProperties(parameters);
+ assertTrue( q.list().get(0)==simple );
+
+
+ parameters.put("stuff", l.toArray(new String[0]));
+ q = s.createQuery("from Simple s where s.name in (:stuff)");
+ q.setProperties(parameters);
+ assertTrue( q.list().get(0)==simple );
+ s.delete(simple);
+ t.commit();
+ s.close();
+ }
+ public void testBroken() throws Exception {
+ if (getDialect() instanceof Oracle9Dialect) return;
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Broken b = new Fixed();
+ b.setId( new Long(123));
+ b.setOtherId("foobar");
+ s.save(b);
+ s.flush();
+ b.setTimestamp( new Date() );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.update(b);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ b = (Broken) s.load( Broken.class, b );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.delete(b);
+ t.commit();
+ s.close();
+ }
+
+ public void testNothinToUpdate() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Simple simple = new Simple();
+ simple.setName("Simple 1");
+ s.save( simple, new Long(10) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.update( simple, new Long(10) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.update( simple, new Long(10) );
+ s.delete(simple);
+ t.commit();
+ s.close();
+ }
+
+ public void testCachedQuery() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Simple simple = new Simple();
+ simple.setName("Simple 1");
+ s.save( simple, new Long(10) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Query q = s.createQuery("from Simple s where s.name=?");
+ q.setCacheable(true);
+ q.setString(0, "Simple 1");
+ assertTrue( q.list().size()==1 );
+ assertTrue( q.list().size()==1 );
+ assertTrue( q.list().size()==1 );
+ q = s.createQuery("from Simple s where s.name=:name");
+ q.setCacheable(true);
+ q.setString("name", "Simple 1");
+ assertTrue( q.list().size()==1 );
+ simple = (Simple) q.list().get(0);
+
+ q.setString("name", "Simple 2");
+ assertTrue( q.list().size()==0 );
+ assertTrue( q.list().size()==0 );
+ simple.setName("Simple 2");
+ assertTrue( q.list().size()==1 );
+ assertTrue( q.list().size()==1 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ q = s.createQuery("from Simple s where s.name=:name");
+ q.setString("name", "Simple 2");
+ q.setCacheable(true);
+ assertTrue( q.list().size()==1 );
+ assertTrue( q.list().size()==1 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.update( simple, new Long(10) );
+ s.delete(simple);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ q = s.createQuery("from Simple s where s.name=?");
+ q.setCacheable(true);
+ q.setString(0, "Simple 1");
+ assertTrue( q.list().size()==0 );
+ assertTrue( q.list().size()==0 );
+ t.commit();
+ s.close();
+ }
+
+ public void testCachedQueryRegion() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Simple simple = new Simple();
+ simple.setName("Simple 1");
+ s.save( simple, new Long(10) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Query q = s.createQuery("from Simple s where s.name=?");
+ q.setCacheRegion("foo");
+ q.setCacheable(true);
+ q.setString(0, "Simple 1");
+ assertTrue( q.list().size()==1 );
+ assertTrue( q.list().size()==1 );
+ assertTrue( q.list().size()==1 );
+ q = s.createQuery("from Simple s where s.name=:name");
+ q.setCacheRegion("foo");
+ q.setCacheable(true);
+ q.setString("name", "Simple 1");
+ assertTrue( q.list().size()==1 );
+ simple = (Simple) q.list().get(0);
+
+ q.setString("name", "Simple 2");
+ assertTrue( q.list().size()==0 );
+ assertTrue( q.list().size()==0 );
+ simple.setName("Simple 2");
+ assertTrue( q.list().size()==1 );
+ assertTrue( q.list().size()==1 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.update( simple, new Long(10) );
+ s.delete(simple);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ q = s.createQuery("from Simple s where s.name=?");
+ q.setCacheRegion("foo");
+ q.setCacheable(true);
+ q.setString(0, "Simple 1");
+ assertTrue( q.list().size()==0 );
+ assertTrue( q.list().size()==0 );
+ t.commit();
+ s.close();
+ }
+
+ public void testSQLFunctions() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Simple simple = new Simple();
+ simple.setName("Simple 1");
+ s.save(simple, new Long(10) );
+
+ if ( getDialect() instanceof DB2Dialect) {
+ s.find("from Simple s where repeat('foo', 3) = 'foofoofoo'");
+ s.find("from Simple s where repeat(s.name, 3) = 'foofoofoo'");
+ s.find("from Simple s where repeat( lower(s.name), 3 + (1-1) / 2) = 'foofoofoo'");
+ }
+
+ assertTrue(
+ s.find("from Simple s where upper( s.name ) ='SIMPLE 1'").size()==1
+ );
+ if ( !(getDialect() instanceof HSQLDialect) ) {
+ assertTrue(
+ s.find("from Simple s where not( upper( s.name ) ='yada' or 1=2 or 'foo'='bar' or not('foo'='foo') or 'foo' like 'bar' )").size()==1
+ );
+ }
+ if ( !(getDialect() instanceof MySQLDialect) && !(getDialect() instanceof SybaseDialect) && !(getDialect() instanceof MckoiDialect) && !(getDialect() instanceof InterbaseDialect) && !(getDialect() instanceof TimesTenDialect) ) { //My SQL has a funny concatenation operator
+ assertTrue(
+ s.find("from Simple s where lower( s.name || ' foo' ) ='simple 1 foo'").size()==1
+ );
+ }
+ if ( (getDialect() instanceof SybaseDialect) ) {
+ assertTrue(
+ s.find("from Simple s where lower( s.name + ' foo' ) ='simple 1 foo'").size()==1
+ );
+ }
+ if ( (getDialect() instanceof MckoiDialect) || (getDialect() instanceof TimesTenDialect)) {
+ assertTrue(
+ s.find("from Simple s where lower( concat(s.name, ' foo') ) ='simple 1 foo'").size()==1
+ );
+ }
+
+ Simple other = new Simple();
+ other.setName("Simple 2");
+ other.setCount(12);
+ simple.setOther(other);
+ s.save( other, new Long(20) );
+ //s.find("from Simple s where s.name ## 'cat|rat|bag'");
+ assertTrue(
+ s.find("from Simple s where upper( s.other.name ) ='SIMPLE 2'").size()==1
+ );
+ assertTrue(
+ s.find("from Simple s where not ( upper( s.other.name ) ='SIMPLE 2' )").size()==0
+ );
+ assertTrue(
+ s.find("select distinct s from Simple s where ( ( s.other.count + 3 ) = (15*2)/2 and s.count = 69) or ( ( s.other.count + 2 ) / 7 ) = 2").size()==1
+ );
+ assertTrue(
+ s.find("select s from Simple s where ( ( s.other.count + 3 ) = (15*2)/2 and s.count = 69) or ( ( s.other.count + 2 ) / 7 ) = 2 order by s.other.count").size()==1
+ );
+ Simple min = new Simple();
+ min.setCount(-1);
+ s.save(min, new Long(30) );
+ if ( ! (getDialect() instanceof MySQLDialect) && ! (getDialect() instanceof HSQLDialect) ) { //My SQL has no subqueries
+ assertTrue(
+ s.find("from Simple s where s.count > ( select min(sim.count) from Simple sim )").size()==2
+ );
+ t.commit();
+ t = s.beginTransaction();
+ assertTrue(
+ s.find("from Simple s where s = some( select sim from Simple sim where sim.count>=0 ) and s.count >= 0").size()==2
+ );
+ assertTrue(
+ s.find("from Simple s where s = some( select sim from Simple sim where sim.other.count=s.other.count ) and s.other.count > 0").size()==1
+ );
+ }
+
+ Iterator iter = s.iterate("select sum(s.count) from Simple s group by s.count having sum(s.count) > 10");
+ assertTrue( iter.hasNext() );
+ assertEquals( new Long(12), iter.next() );
+ assertTrue( !iter.hasNext() );
+ if ( ! (getDialect() instanceof MySQLDialect) ) {
+ iter = s.iterate("select s.count from Simple s group by s.count having s.count = 12");
+ assertTrue( iter.hasNext() );
+ }
+
+ s.iterate("select s.id, s.count, count(t), max(t.date) from Simple s, Simple t where s.count = t.count group by s.id, s.count order by s.count");
+
+ Query q = s.createQuery("from Simple s");
+ q.setMaxResults(10);
+ assertTrue( q.list().size()==3 );
+ q = s.createQuery("from Simple s");
+ q.setMaxResults(1);
+ assertTrue( q.list().size()==1 );
+ q = s.createQuery("from Simple s");
+ assertTrue( q.list().size()==3 );
+ q = s.createQuery("from Simple s where s.name = ?");
+ q.setString(0, "Simple 1");
+ assertTrue( q.list().size()==1 );
+ q = s.createQuery("from Simple s where s.name = ? and upper(s.name) = ?");
+ q.setString(1, "SIMPLE 1");
+ q.setString(0, "Simple 1");
+ q.setFirstResult(0);
+ assertTrue( q.iterate().hasNext() );
+ q = s.createQuery("from Simple s where s.name = :foo and upper(s.name) = :bar or s.count=:count or s.count=:count + 1");
+ q.setParameter("bar", "SIMPLE 1");
+ q.setString("foo", "Simple 1");
+ q.setInteger("count", 69);
+ q.setFirstResult(0);
+ assertTrue( q.iterate().hasNext() );
+ q = s.createQuery("select s.id from Simple s");
+ q.setFirstResult(1);
+ q.setMaxResults(2);
+ iter = q.iterate();
+ int i=0;
+ while ( iter.hasNext() ) {
+ assertTrue( iter.next() instanceof Long );
+ i++;
+ }
+ assertTrue(i==2);
+ q = s.createQuery("select all s, s.other from Simple s where s = :s");
+ q.setParameter("s", simple);
+ assertTrue( q.list().size()==1 );
+
+
+ q = s.createQuery("from Simple s where s.name in (:name_list) and s.count > :count");
+ HashSet set = new HashSet();
+ set.add("Simple 1"); set.add("foo");
+ q.setParameterList( "name_list", set );
+ q.setParameter("count", new Integer(-1) );
+ assertTrue( q.list().size()==1 );
+
+ ScrollableResults sr = s.createQuery("from Simple s").scroll();
+ sr.next();
+ sr.get(0);
+ sr.close();
+
+ s.delete(other);
+ s.delete(simple);
+ s.delete(min);
+ t.commit();
+ s.close();
+
+ }
+
+ public void testBlobClob() throws Exception {
+
+ Session s = openSession();
+ Blobber b = new Blobber();
+ b.setBlob( Hibernate.createBlob( "foo/bar/baz".getBytes() ) );
+ b.setClob( Hibernate.createClob("foo/bar/baz") );
+ s.save(b);
+ //s.refresh(b);
+ //assertTrue( b.getClob() instanceof ClobImpl );
+ s.flush();
+ s.refresh(b);
+ //b.getBlob().setBytes( 2, "abc".getBytes() );
+ b.getClob().getSubString(2, 3);
+ //b.getClob().setString(2, "abc");
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ b = (Blobber) s.load( Blobber.class, new Integer( b.getId() ) );
+ Blobber b2 = new Blobber();
+ s.save(b2);
+ b2.setBlob( b.getBlob() );
+ b.setBlob(null);
+ //assertTrue( b.getClob().getSubString(1, 3).equals("fab") );
+ b.getClob().getSubString(1, 6);
+ //b.getClob().setString(1, "qwerty");
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ b = (Blobber) s.load( Blobber.class, new Integer( b.getId() ) );
+ b.setClob( Hibernate.createClob("xcvfxvc xcvbx cvbx cvbx cvbxcvbxcvbxcvb") );
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ b = (Blobber) s.load( Blobber.class, new Integer( b.getId() ) );
+ assertTrue( b.getClob().getSubString(1, 7).equals("xcvfxvc") );
+ //b.getClob().setString(5, "1234567890");
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+
+ /*InputStream is = getClass().getClassLoader().getResourceAsStream("jdbc20.pdf");
+ s = sessionsopenSession();
+ b = (Blobber) s.load( Blobber.class, new Integer( b.getId() ) );
+ System.out.println( is.available() );
+ int size = is.available();
+ b.setBlob( Hibernate.createBlob( is, is.available() ) );
+ s.flush();
+ s.connection().commit();
+ ResultSet rs = s.connection().createStatement().executeQuery("select datalength(blob_) from blobber where id=" + b.getId() );
+ rs.next();
+ assertTrue( size==rs.getInt(1) );
+ rs.close();
+ s.close();
+
+ s = sessionsopenSession();
+ b = (Blobber) s.load( Blobber.class, new Integer( b.getId() ) );
+ File f = new File("C:/foo.pdf");
+ f.createNewFile();
+ FileOutputStream fos = new FileOutputStream(f);
+ Blob blob = b.getBlob();
+ byte[] bytes = blob.getBytes( 1, (int) blob.length() );
+ System.out.println( bytes.length );
+ fos.write(bytes);
+ fos.flush();
+ fos.close();
+ s.close();*/
+
+ }
+
+ public void testSqlFunctionAsAlias() throws Exception {
+ String functionName = locateAppropriateDialectFunctionNameForAliasTest();
+ if (functionName == null) {
+ log.info("Dialect does not list any no-arg functions");
+ return;
+ }
+
+ log.info("Using function named [" + functionName + "] for 'function as alias' test");
+ String query = "select " + functionName + " from Simple as " + functionName + " where " + functionName + ".id = 10";
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Simple simple = new Simple();
+ simple.setName("Simple 1");
+ s.save( simple, new Long(10) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ List result = s.find(query);
+ assertTrue( result.size() == 1 );
+ assertTrue(result.get(0) instanceof Simple);
+ s.delete( result.get(0) );
+ t.commit();
+ s.close();
+ }
+
+ private String locateAppropriateDialectFunctionNameForAliasTest() {
+ for (Iterator itr = getDialect().getFunctions().entrySet().iterator(); itr.hasNext(); ) {
+ final Map.Entry entry = (Map.Entry) itr.next();
+ final SQLFunction function = (SQLFunction) entry.getValue();
+ if ( !function.hasArguments() && !function.hasParenthesesIfNoArguments() ) {
+ return (String) entry.getKey();
+ }
+ }
+ return null;
+ }
+
+ public void testCachedQueryOnInsert() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Simple simple = new Simple();
+ simple.setName("Simple 1");
+ s.save( simple, new Long(10) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Query q = s.createQuery("from Simple s");
+ List list = q.setCacheable(true).list();
+ assertTrue( list.size()==1 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ q = s.createQuery("from Simple s");
+ list = q.setCacheable(true).list();
+ assertTrue( list.size()==1 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Simple simple2 = new Simple();
+ simple2.setCount(133);
+ s.save( simple2, new Long(12) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ q = s.createQuery("from Simple s");
+ list = q.setCacheable(true).list();
+ assertTrue( list.size()==2 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ q = s.createQuery("from Simple s");
+ list = q.setCacheable(true).list();
+ assertTrue( list.size()==2 );
+ Iterator i = list.iterator();
+ while ( i.hasNext() ) s.delete( i.next() );
+ t.commit();
+ s.close();
+
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/SQLLoaderTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/SQLLoaderTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/SQLLoaderTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,669 @@
+//$Id: SQLLoaderTest.java 11383 2007-04-02 15:34:02Z steve.ebersole at jboss.com $
+package org.hibernate.test.legacy;
+
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.hibernate.HibernateException;
+import org.hibernate.Query;
+import org.hibernate.Transaction;
+import org.hibernate.classic.Session;
+import org.hibernate.dialect.HSQLDialect;
+import org.hibernate.dialect.MySQLDialect;
+import org.hibernate.dialect.Oracle9Dialect;
+import org.hibernate.dialect.OracleDialect;
+import org.hibernate.dialect.PostgreSQLDialect;
+import org.hibernate.dialect.TimesTenDialect;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+
+public class SQLLoaderTest extends LegacyTestCase {
+
+ static int nextInt = 1;
+ static long nextLong = 1;
+
+ public SQLLoaderTest(String arg) {
+ super(arg);
+ }
+
+ public String[] getMappings() {
+ return new String[] {
+ "legacy/ABC.hbm.xml",
+ "legacy/Category.hbm.xml",
+ "legacy/Simple.hbm.xml",
+ "legacy/Fo.hbm.xml",
+ "legacy/SingleSeveral.hbm.xml",
+ "legacy/Componentizable.hbm.xml",
+ "legacy/CompositeIdId.hbm.xml"
+ };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( SQLLoaderTest.class );
+ }
+
+ public void testTS() throws Exception {
+ if (getDialect() instanceof Oracle9Dialect) return;
+ Session session = openSession();
+ Transaction txn = session.beginTransaction();
+ Simple sim = new Simple();
+ sim.setDate( new Date() );
+ session.save( sim, new Long(1) );
+ Query q = session.createSQLQuery("select {sim.*} from Simple {sim} where {sim}.date_ = ?", "sim", Simple.class);
+ q.setTimestamp( 0, sim.getDate() );
+ assertTrue ( q.list().size()==1 );
+ session.delete(sim);
+ txn.commit();
+ session.close();
+ }
+
+
+ public void testFindBySQLStar() throws HibernateException, SQLException {
+ Session session = openSession();
+ session.delete("from Assignable");
+ session.delete("from Category");
+ session.delete("from Simple");
+ session.delete("from A");
+
+ Category s = new Category();
+ s.setName(String.valueOf(nextLong++));
+ session.save(s);
+
+ Simple simple = new Simple();
+ simple.init();
+ session.save(simple, new Long(nextLong++));
+
+ A a = new A();
+ session.save(a);
+
+ B b = new B();
+ session.save(b);
+ session.flush();
+
+ session.createSQLQuery("select {category.*} from category {category}", "category", Category.class).list();
+ session.createSQLQuery("select {simple.*} from Simple {simple}", "simple", Simple.class).list();
+ session.createSQLQuery("select {a.*} from TA {a}", "a", A.class).list();
+
+ session.connection().commit();
+ session.close();
+
+ }
+
+ public void testFindBySQLProperties() throws HibernateException, SQLException {
+ Session session = openSession();
+ session.delete("from Category");
+
+ Category s = new Category();
+ s.setName(String.valueOf(nextLong++));
+ session.save(s);
+
+ s = new Category();
+ s.setName("WannaBeFound");
+ session.flush();
+
+ Query query = session.createSQLQuery("select {category.*} from category {category} where {category}.name = :name", "category", Category.class);
+
+ query.setProperties(s);
+ //query.setParameter("name", s.getName());
+
+ query.list();
+
+ query = session.createSQLQuery("select {category.*} from category {category} where {category}.name in (:names)", "category", Category.class);
+ String[] str = new String[] { "WannaBeFound", "NotThere" };
+ query.setParameterList("names", str);
+
+ query.uniqueResult();
+
+ session.connection().commit();
+ session.close();
+
+
+
+ }
+
+ public void testFindBySQLAssociatedObjects() throws HibernateException, SQLException {
+ Session s = openSession();
+ s.delete("from Assignable");
+ s.delete("from Category");
+
+ Category c = new Category();
+ c.setName("NAME");
+ Assignable assn = new Assignable();
+ assn.setId("i.d.");
+ List l = new ArrayList();
+ l.add(c);
+ assn.setCategories(l);
+ c.setAssignable(assn);
+ s.save(assn);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ List list = s.createSQLQuery("select {category.*} from category {category}", "category", Category.class).list();
+ list.get(0);
+ s.connection().commit();
+ s.close();
+
+ if ( getDialect() instanceof MySQLDialect ) return;
+ if ( getDialect() instanceof OracleDialect ) return; // todo : this fails on Oracle8 also
+
+ s = openSession();
+
+ Query query = s.getNamedQuery("namedsql");
+ assertNotNull(query);
+ list = query.list();
+ assertNotNull(list);
+
+ Object[] values = (Object[]) list.get(0);
+ assertNotNull(values[0]);
+ assertNotNull(values[1]);
+ assertTrue("wrong type: " + values[0].getClass(), values[0] instanceof Category);
+ assertTrue("wrong type: " + values[1].getClass(), values[1] instanceof Assignable);
+
+ s.connection().commit();
+ s.close();
+
+ }
+
+ public void testPropertyResultSQL() throws HibernateException, SQLException {
+
+ if ( getDialect() instanceof MySQLDialect ) return;
+
+ Session s = openSession();
+ s.delete("from Assignable");
+ s.delete("from Category");
+
+ Category c = new Category();
+ c.setName("NAME");
+ Assignable assn = new Assignable();
+ assn.setId("i.d.");
+ List l = new ArrayList();
+ l.add(c);
+ assn.setCategories(l);
+ c.setAssignable(assn);
+ s.save(assn);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+
+ Query query = s.getNamedQuery("nonaliasedsql");
+ assertNotNull(query);
+ List list = query.list();
+ assertNotNull(list);
+
+ assertTrue(list.get(0) instanceof Category);
+
+ s.connection().commit();
+ s.close();
+
+ }
+
+ public void testFindBySQLMultipleObject() throws HibernateException, SQLException {
+ Session s = openSession();
+ s.delete("from Assignable");
+ s.delete("from Category");
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ Category c = new Category();
+ c.setName("NAME");
+ Assignable assn = new Assignable();
+ assn.setId("i.d.");
+ List l = new ArrayList();
+ l.add(c);
+ assn.setCategories(l);
+ c.setAssignable(assn);
+ s.save(assn);
+ s.flush();
+ c = new Category();
+ c.setName("NAME2");
+ assn = new Assignable();
+ assn.setId("i.d.2");
+ l = new ArrayList();
+ l.add(c);
+ assn.setCategories(l);
+ c.setAssignable(assn);
+ s.save(assn);
+ s.flush();
+
+ assn = new Assignable();
+ assn.setId("i.d.3");
+ s.save(assn);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ if ( getDialect() instanceof MySQLDialect ) return;
+
+ s = openSession();
+ List list = s.createSQLQuery("select {category.*}, {assignable.*} from category {category}, \"assign-able\" {assignable}", new String[] { "category", "assignable" }, new Class[] { Category.class, Assignable.class }).list();
+
+ assertTrue(list.size() == 6); // crossproduct of 2 categories x 3 assignables
+ assertTrue(list.get(0) instanceof Object[]);
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testFindBySQLParameters() throws HibernateException, SQLException {
+ Session s = openSession();
+ s.delete("from Assignable");
+ s.delete("from Category");
+ s.flush();
+ s.connection().commit();
+ s.close();
+ s = openSession();
+ Category c = new Category();
+ c.setName("Good");
+ Assignable assn = new Assignable();
+ assn.setId("i.d.");
+ List l = new ArrayList();
+ l.add(c);
+ assn.setCategories(l);
+ c.setAssignable(assn);
+ s.save(assn);
+ s.flush();
+ c = new Category();
+ c.setName("Best");
+ assn = new Assignable();
+ assn.setId("i.d.2");
+ l = new ArrayList();
+ l.add(c);
+ assn.setCategories(l);
+ c.setAssignable(assn);
+ s.save(assn);
+ s.flush();
+ c = new Category();
+ c.setName("Better");
+ assn = new Assignable();
+ assn.setId("i.d.7");
+ l = new ArrayList();
+ l.add(c);
+ assn.setCategories(l);
+ c.setAssignable(assn);
+ s.save(assn);
+ s.flush();
+
+ assn = new Assignable();
+ assn.setId("i.d.3");
+ s.save(assn);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ Query basicParam = s.createSQLQuery("select {category.*} from category {category} where {category}.name = 'Best'", "category", Category.class);
+ List list = basicParam.list();
+ assertEquals(1, list.size());
+
+ Query unnamedParam = s.createSQLQuery("select {category.*} from category {category} where {category}.name = ? or {category}.name = ?", "category", Category.class);
+ unnamedParam.setString(0, "Good");
+ unnamedParam.setString(1, "Best");
+ list = unnamedParam.list();
+ assertEquals(2, list.size());
+
+ Query namedParam = s.createSQLQuery("select {category.*} from category {category} where ({category}.name=:firstCat or {category}.name=:secondCat)", "category", Category.class);
+ namedParam.setString("firstCat", "Better");
+ namedParam.setString("secondCat", "Best");
+ list = namedParam.list();
+ assertEquals(2, list.size());
+
+ s.connection().commit();
+ s.close();
+ }
+
+ public void testEscapedJDBC() throws HibernateException, SQLException {
+ if (
+ getDialect() instanceof HSQLDialect ||
+ getDialect() instanceof PostgreSQLDialect
+ ) return;
+
+ Session session = openSession();
+ session.delete("from A");
+ A savedA = new A();
+ savedA.setName("Max");
+ session.save(savedA);
+
+ B savedB = new B();
+ session.save(savedB);
+ session.flush();
+
+ int count = session.createQuery("from A").list().size();
+ session.close();
+
+ session = openSession();
+
+ Query query;
+ if ( getDialect() instanceof OracleDialect ) {
+ // Oracle8 does not support X/Open extension functions :)
+ query = session.createSQLQuery("select identifier_column as {a.id}, clazz_discriminata as {a.class}, count_ as {a.count}, name as {a.name} from TA where upper(name) like upper('max')", "a", A.class);
+ }
+ else if( getDialect() instanceof TimesTenDialect) {
+ // TimesTen does not permit general expressions (like UPPER) in the second part of a LIKE expression,
+ // so we execute a similar test
+ query = session.createSQLQuery("select identifier_column as {a.id}, clazz_discriminata as {a.class}, count_ as {a.count}, name as {a.name} from TA where {fn ucase(name)} like 'MAX'", "a", A.class);
+ } else {
+ query = session.createSQLQuery("select identifier_column as {a.id}, clazz_discriminata as {a.class}, count_ as {a.count}, name as {a.name} from TA where {fn ucase(name)} like {fn ucase('max')}", "a", A.class);
+ }
+ List list = query.list();
+
+ assertNotNull(list);
+ assertEquals(1, list.size());
+ session.connection().commit();
+ session.close();
+ }
+
+ public void testDoubleAliasing() throws HibernateException, SQLException {
+
+ Session session = openSession();
+ session.delete("from A");
+ A savedA = new A();
+ savedA.setName("Max");
+ session.save(savedA);
+
+ B savedB = new B();
+ session.save(savedB);
+ session.flush();
+
+ int count = session.createQuery("from A").list().size();
+ session.close();
+
+ session = openSession();
+
+ Query query = session.createSQLQuery("select a.identifier_column as {a1.id}, a.clazz_discriminata as {a1.class}, a.count_ as {a1.count}, a.name as {a1.name} " +
+ ", b.identifier_column as {a2.id}, b.clazz_discriminata as {a2.class}, b.count_ as {a2.count}, b.name as {a2.name} " +
+ " from TA a, TA b" +
+ " where a.identifier_column = b.identifier_column", new String[] {"a1", "a2" }, new Class[] {A.class, A.class});
+ List list = query.list();
+
+ assertNotNull(list);
+ assertEquals(2, list.size());
+ session.connection().commit();
+ session.close();
+ }
+
+ // TODO: compositeid's - how ? (SingleSeveral.hbm.xml test)
+ public void testEmbeddedCompositeProperties() throws HibernateException, SQLException {
+ Session session = openSession();
+
+ Single s = new Single();
+ s.setId("my id");
+ s.setString("string 1");
+ session.save(s);
+ session.flush();
+ session.connection().commit();
+
+ session.clear();
+
+ Query query = session.createSQLQuery("select {sing.*} from Single {sing}", "sing", Single.class);
+
+ List list = query.list();
+
+ assertTrue(list.size()==1);
+
+ session.clear();
+
+ query = session.createSQLQuery("select {sing.*} from Single {sing} where sing.id = ?", "sing", Single.class);
+ query.setString(0, "my id");
+ list = query.list();
+
+ assertTrue(list.size()==1);
+
+ session.clear();
+
+ query = session.createSQLQuery("select s.id as {sing.id}, s.string_ as {sing.string}, s.prop as {sing.prop} from Single s where s.id = ?", "sing", Single.class);
+ query.setString(0, "my id");
+ list = query.list();
+
+ assertTrue(list.size()==1);
+
+ session.clear();
+
+ query = session.createSQLQuery("select s.id as {sing.id}, s.string_ as {sing.string}, s.prop as {sing.prop} from Single s where s.id = ?", "sing", Single.class);
+ query.setString(0, "my id");
+ list = query.list();
+
+ assertTrue(list.size()==1);
+
+ session.connection().commit();
+ session.close();
+
+ }
+
+ public void testReturnPropertyComponentRenameFailureExpected() throws HibernateException, SQLException {
+ // failure expected because this was a regression introduced previously which needs to get tracked down.
+ Session session = openSession();
+ Componentizable componentizable = setupComponentData(session);
+
+ Query namedQuery = session.getNamedQuery("queryComponentWithOtherColumn");
+ List list = namedQuery.list();
+
+ assertEquals(1, list.size());
+ assertEquals( "flakky comp", ( (Componentizable) list.get(0) ).getComponent().getName() );
+
+ session.clear();
+
+ session.delete(componentizable);
+ session.flush();
+
+ session.connection().commit();
+ session.close();
+ }
+
+ public void testComponentStar() throws HibernateException, SQLException {
+ componentTest("select {comp.*} from Componentizable comp");
+ }
+
+ public void testComponentNoStar() throws HibernateException, SQLException {
+ componentTest("select comp.id as {comp.id}, comp.nickName as {comp.nickName}, comp.name as {comp.component.name}, comp.subName as {comp.component.subComponent.subName}, comp.subName1 as {comp.component.subComponent.subName1} from Componentizable comp");
+ }
+
+
+ private void componentTest(String sql) throws SQLException {
+ Session session = openSession();
+
+ Componentizable c = setupComponentData( session );
+
+ Query q = session.createSQLQuery(sql, "comp", Componentizable.class);
+ List list = q.list();
+
+ assertEquals(list.size(),1);
+
+ Componentizable co = (Componentizable) list.get(0);
+
+ assertEquals(c.getNickName(), co.getNickName());
+ assertEquals(c.getComponent().getName(), co.getComponent().getName());
+ assertEquals(c.getComponent().getSubComponent().getSubName(), co.getComponent().getSubComponent().getSubName());
+
+ session.delete(co);
+ session.flush();
+ session.connection().commit();
+ session.close();
+ }
+
+ private Componentizable setupComponentData(Session session) throws SQLException {
+ Componentizable c = new Componentizable();
+ c.setNickName("Flacky");
+ Component component = new Component();
+ component.setName("flakky comp");
+ SubComponent subComponent = new SubComponent();
+ subComponent.setSubName("subway");
+ component.setSubComponent(subComponent);
+
+ c.setComponent(component);
+
+ session.save(c);
+
+ session.flush();
+ session.connection().commit();
+
+ session.clear();
+ return c;
+ }
+
+ public void testFindSimpleBySQL() throws Exception {
+ if ( getDialect() instanceof MySQLDialect ) return;
+ Session session = openSession();
+ Category s = new Category();
+ s.setName(String.valueOf(nextLong++));
+ session.save(s);
+ session.flush();
+
+ Query query = session.createSQLQuery("select s.category_key_col as {category.id}, s.name as {category.name}, s.\"assign-able-id\" as {category.assignable} from {category} s", "category", Category.class);
+ List list = query.list();
+
+ assertNotNull(list);
+ assertTrue(list.size() > 0);
+ assertTrue(list.get(0) instanceof Category);
+ session.connection().commit();
+ session.close();
+ // How do we handle objects with composite id's ? (such as Single)
+ }
+
+ public void testFindBySQLSimpleByDiffSessions() throws Exception {
+ Session session = openSession();
+ Category s = new Category();
+ s.setName(String.valueOf(nextLong++));
+ session.save(s);
+ session.flush();
+ session.connection().commit();
+ session.close();
+
+ if ( getDialect() instanceof MySQLDialect ) return;
+
+ session = openSession();
+
+ Query query = session.createSQLQuery("select s.category_key_col as {category.id}, s.name as {category.name}, s.\"assign-able-id\" as {category.assignable} from {category} s", "category", Category.class);
+ List list = query.list();
+
+ assertNotNull(list);
+ assertTrue(list.size() > 0);
+ assertTrue(list.get(0) instanceof Category);
+
+ // How do we handle objects that does not have id property (such as Simple ?)
+ // How do we handle objects with composite id's ? (such as Single)
+ session.connection().commit();
+ session.close();
+ }
+
+ public void testFindBySQLDiscriminatedSameSession() throws Exception {
+ Session session = openSession();
+ session.delete("from A");
+ A savedA = new A();
+ session.save(savedA);
+
+ B savedB = new B();
+ session.save(savedB);
+ session.flush();
+
+ Query query = session.createSQLQuery("select identifier_column as {a.id}, clazz_discriminata as {a.class}, name as {a.name}, count_ as {a.count} from TA {a}", "a", A.class);
+ List list = query.list();
+
+ assertNotNull(list);
+ assertEquals(2, list.size());
+
+ A a1 = (A) list.get(0);
+ A a2 = (A) list.get(1);
+
+ assertTrue((a2 instanceof B) || (a1 instanceof B));
+ assertFalse(a1 instanceof B && a2 instanceof B);
+
+ if (a1 instanceof B) {
+ assertSame(a1, savedB);
+ assertSame(a2, savedA);
+ }
+ else {
+ assertSame(a2, savedB);
+ assertSame(a1, savedA);
+ }
+
+ session.clear();
+ List list2 = session.getNamedQuery("propertyResultDiscriminator").list();
+ assertEquals(2, list2.size());
+
+ session.connection().commit();
+ session.close();
+ }
+
+ public void testFindBySQLDiscriminatedDiffSession() throws Exception {
+ Session session = openSession();
+ session.delete("from A");
+ A savedA = new A();
+ session.save(savedA);
+
+ B savedB = new B();
+ session.save(savedB);
+ session.flush();
+
+ int count = session.createQuery("from A").list().size();
+ session.close();
+
+ session = openSession();
+
+ Query query = session.createSQLQuery("select identifier_column as {a.id}, clazz_discriminata as {a.class}, count_ as {a.count}, name as {a.name} from TA", "a", A.class);
+ List list = query.list();
+
+ assertNotNull(list);
+ assertEquals(count, list.size());
+ session.connection().commit();
+ session.close();
+ }
+
+
+ public void testCompositeIdId() throws HibernateException, SQLException {
+ // issue HHH-21
+ Session s = openSession();
+
+ CompositeIdId id = new CompositeIdId();
+ id.setName("Max");
+ id.setSystem("c64");
+ id.setId("games");
+
+ s.save(id);
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ // having a composite id with one property named id works since the map used by sqlloader to map names to properties handles it.
+ Query query = s.createSQLQuery("select system as {c.system}, id as {c.id}, name as {c.name}, foo as {c.composite.foo}, bar as {c.composite.bar} from CompositeIdId where system=? and id=?", "c", CompositeIdId.class);
+ query.setString(0, "c64");
+ query.setString(1, "games");
+
+ CompositeIdId id2 = (CompositeIdId) query.uniqueResult();
+ check(id, id2);
+
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+
+ CompositeIdId useForGet = new CompositeIdId();
+ useForGet.setSystem("c64");
+ useForGet.setId("games");
+ // this doesn't work since the verification does not take column span into respect!
+ CompositeIdId getted = (CompositeIdId) s.get(CompositeIdId.class, useForGet);
+ check(id,getted);
+
+
+ s.connection().commit();
+ s.close();
+
+ }
+
+ private void check(CompositeIdId id, CompositeIdId id2) {
+ assertEquals(id,id2);
+ assertEquals(id.getName(), id2.getName());
+ assertEquals(id.getId(), id2.getId());
+ assertEquals(id.getSystem(), id2.getSystem());
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Several.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Several.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Several.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,90 @@
+//$Id: Several.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+
+
+public class Several implements Serializable {
+ private String id;
+ private String prop;
+ private Single single;
+ private String string;
+ /**
+ * Returns the id.
+ * @return String
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Returns the prop.
+ * @return String
+ */
+ public String getProp() {
+ return prop;
+ }
+
+ /**
+ * Returns the single.
+ * @return Single
+ */
+ public Single getSingle() {
+ return single;
+ }
+
+ /**
+ * Sets the id.
+ * @param id The id to set
+ */
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ /**
+ * Sets the prop.
+ * @param prop The prop to set
+ */
+ public void setProp(String prop) {
+ this.prop = prop;
+ }
+
+ /**
+ * Sets the single.
+ * @param single The single to set
+ */
+ public void setSingle(Single single) {
+ this.single = single;
+ }
+
+ /**
+ * Returns the string.
+ * @return String
+ */
+ public String getString() {
+ return string;
+ }
+
+ /**
+ * Sets the string.
+ * @param string The string to set
+ */
+ public void setString(String string) {
+ this.string = string;
+ }
+
+ /*public boolean equals(Object other) {
+ return ( (Several) other ).id.equals(id) && ( (Several) other ).string.equals(string);
+ }
+
+ public int hashCode() {
+ return id.hashCode();
+ }*/
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Simple.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Simple.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Simple.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping default-lazy="false">
+
+ <class name="org.hibernate.test.legacy.Simple">
+ <id type="long" column="id_">
+ <generator class="assigned"/>
+ </id>
+ <property name="name"/>
+ <property name="address"/>
+ <property name="count" column="count_" not-null="true" unique="true"/>
+ <property name="date" column="date_"/>
+ <property name="pay"/>
+ <many-to-one name="other" access="field"/>
+ </class>
+
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Simple.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Simple.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Simple.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,127 @@
+//$Id: Simple.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+
+public class Simple implements Serializable {
+ private String name;
+ private String address;
+ private int count;
+ private java.util.Date date;
+ private Float number;
+ private Simple other;
+
+ private Long parent;
+
+ public Simple(int c) {
+ count=c;
+ }
+ public Simple() {}
+
+ public void init() {
+ name="Someone With Along Name";
+ address="1234 Some Street, Some City, Victoria, 3000, Austraya";
+ count=69;
+ date=new java.sql.Date(666);
+ number=new Float(55.8);
+ }
+ /**
+ * Gets the name
+ * @return Returns a String
+ */
+ public String getName() {
+ return name;
+ }
+ /**
+ * Sets the name
+ * @param name The name to set
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Gets the address
+ * @return Returns a String
+ */
+ public String getAddress() {
+ return address;
+ }
+ /**
+ * Sets the address
+ * @param address The address to set
+ */
+ public void setAddress(String address) {
+ this.address = address;
+ }
+
+ /**
+ * Gets the count
+ * @return Returns a int
+ */
+ public int getCount() {
+ return count;
+ }
+ /**
+ * Sets the count
+ * @param count The count to set
+ */
+ public void setCount(int count) {
+ this.count = count;
+ }
+
+ /**
+ * Gets the date
+ * @return Returns a java.util.Date
+ */
+ public java.util.Date getDate() {
+ return date;
+ }
+ /**
+ * Sets the date
+ * @param date The date to set
+ */
+ public void setDate(java.util.Date date) {
+ this.date = date;
+ }
+
+ /**
+ * Gets the pay number
+ * @return Returns a Float
+ */
+ public Float getPay() {
+ return number;
+ }
+
+ /**
+ * Sets the pay number
+ * @param number The Pay to set
+ */
+ public void setPay(Float number) {
+ this.number = number;
+ }
+
+ /**
+ * Returns the other.
+ * @return Simple
+ */
+ public Simple getOther() {
+ return other;
+ }
+
+ /**
+ * Sets the other.
+ * @param other The other to set
+ */
+ public void setOther(Simple other) {
+ this.other = other;
+ }
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Single.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Single.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Single.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,93 @@
+//$Id: Single.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.HashSet;
+
+
+public class Single implements Serializable {
+ private String id;
+ private String prop;
+ private String string;
+ private Collection several = new HashSet();
+ /**
+ * Returns the id.
+ * @return String
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Returns the prop.
+ * @return String
+ */
+ public String getProp() {
+ return prop;
+ }
+
+ /**
+ * Returns the several.
+ * @return Set
+ */
+ public Collection getSeveral() {
+ return several;
+ }
+
+ /**
+ * Sets the id.
+ * @param id The id to set
+ */
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ /**
+ * Sets the prop.
+ * @param prop The prop to set
+ */
+ public void setProp(String prop) {
+ this.prop = prop;
+ }
+
+ /**
+ * Sets the several.
+ * @param several The several to set
+ */
+ public void setSeveral(Collection several) {
+ this.several = several;
+ }
+
+ /**
+ * Returns the string.
+ * @return String
+ */
+ public String getString() {
+ return string;
+ }
+
+ /**
+ * Sets the string.
+ * @param string The string to set
+ */
+ public void setString(String string) {
+ this.string = string;
+ }
+
+ /*public boolean equals(Object other) {
+ if ( !(other instanceof Single) ) return false;
+ return ( (Single) other ).id.equals(id) && ( (Single) other ).string.equals(string);
+ }
+
+ public int hashCode() {
+ return id.hashCode();
+ }*/
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/SingleSeveral.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/SingleSeveral.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/SingleSeveral.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,35 @@
+<?xml version="1.0"?>
+
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping default-lazy="false">
+
+ <class name="org.hibernate.test.legacy.Single">
+ <composite-id>
+ <key-property name="id" length="32"/>
+ <key-property name="string" length="32" column="string_"/>
+ </composite-id>
+ <property name="prop"/>
+ <bag name="several" inverse="true" lazy="false" cascade="all"> <!--important: test for bidirectional with lazy="false" -->
+ <key>
+ <column name="single_id" not-null="true"/>
+ <column name="single_string" not-null="true"/>
+ </key>
+ <one-to-many class="org.hibernate.test.legacy.Several"/>
+ </bag>
+ </class>
+
+ <class name="org.hibernate.test.legacy.Several">
+ <composite-id unsaved-value="any">
+ <key-property name="id" length="32"/>
+ <key-property name="string" length="32" column="string_"/>
+ </composite-id>
+ <many-to-one name="single">
+ <column name="single_id" length="32"/>
+ <column name="single_string" length="32"/>
+ </many-to-one>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Sortable.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Sortable.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Sortable.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,33 @@
+package org.hibernate.test.legacy;
+
+public class Sortable implements Comparable {
+
+ private int id;
+ private String name;
+
+ private Sortable() {}
+ Sortable(String name) {
+ this.name = name;
+ }
+
+ public int compareTo(Object o) {
+ return name.compareTo( ( (Sortable) o).name );
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setId(int i) {
+ id = i;
+ }
+
+ public void setName(String string) {
+ name = string;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/StatisticsTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/StatisticsTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/StatisticsTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,63 @@
+//$Id: StatisticsTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.legacy;
+
+import junit.framework.Test;
+
+import org.hibernate.Query;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.stat.Statistics;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class StatisticsTest extends LegacyTestCase {
+
+ public StatisticsTest(String x) {
+ super(x);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "legacy/ABC.hbm.xml", "legacy/ABCExtends.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( StatisticsTest.class );
+ }
+
+ public void testSessionStats() throws Exception {
+
+ SessionFactory sf = getSessions();
+ Statistics stats = sf.getStatistics();
+ boolean isStats = stats.isStatisticsEnabled();
+ stats.clear();
+ stats.setStatisticsEnabled(true);
+ Session s = sf.openSession();
+ assertEquals( 1, stats.getSessionOpenCount() );
+ s.close();
+ assertEquals( 1, stats.getSessionCloseCount() );
+ s = sf.openSession();
+ Transaction tx = s.beginTransaction();
+ A a = new A();
+ a.setName("mya");
+ s.save(a);
+ a.setName("b");
+ tx.commit();
+ s.close();
+ assertEquals( 1, stats.getFlushCount() );
+ s = sf.openSession();
+ tx = s.beginTransaction();
+ String hql = "from " + A.class.getName();
+ Query q = s.createQuery(hql);
+ q.list();
+ tx.commit();
+ s.close();
+ assertEquals(1, stats.getQueryExecutionCount() );
+ assertEquals(1, stats.getQueryStatistics(hql).getExecutionCount() );
+
+ stats.setStatisticsEnabled(isStats);
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/StringComparator.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/StringComparator.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/StringComparator.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,12 @@
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+import java.util.Comparator;
+
+public class StringComparator implements Comparator, Serializable {
+
+ public int compare(Object o1, Object o2) {
+ return ( (String) o1 ).toLowerCase().compareTo( ( (String) o2 ).toLowerCase() );
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Stuff.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Stuff.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Stuff.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,35 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping default-lazy="false">
+
+ <class name="org.hibernate.test.legacy.MoreStuff">
+ <composite-id>
+ <key-property name="intId"/>
+ <key-property name="stringId" length="32"/>
+ </composite-id>
+ <property name="name"/>
+ <bag name="stuffs" inverse="true" lazy="false" cascade="all">
+ <key>
+ <!--unfortunately have to specify not-null here because of limitation in schema export....-->
+ <column name="moreInt" not-null="true"/>
+ <column name="moreString" not-null="true" length="32"/>
+ </key>
+ <one-to-many class="org.hibernate.test.legacy.Stuff"/>
+ </bag>
+ </class>
+
+ <class name="org.hibernate.test.legacy.Stuff">
+ <composite-id unsaved-value="any">
+ <key-property name="id"/>
+ <key-many-to-one name="foo" class="org.hibernate.test.legacy.Foo"/>
+ <key-many-to-one name="moreStuff" class="org.hibernate.test.legacy.MoreStuff">
+ <column name="moreInt"/>
+ <column name="moreString" length="32"/>
+ </key-many-to-one>
+ </composite-id>
+ <property name="property"/>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Stuff.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Stuff.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Stuff.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,93 @@
+//$Id: Stuff.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+import java.util.TimeZone;
+
+public class Stuff implements Serializable {
+
+ public int hashCode() {
+ return new Long(id).hashCode();
+ }
+
+ public boolean equals(Object other) {
+ if ( ! (other instanceof Stuff) ) return false;
+ Stuff otherStuff = (Stuff) other;
+ return otherStuff.getId()==id && otherStuff.getFoo().getKey().equals( foo.getKey() ) && otherStuff.getMoreStuff().equals(moreStuff);
+ }
+
+ private long id;
+ private FooProxy foo;
+ private MoreStuff moreStuff;
+ private TimeZone property;
+ /**
+ * Returns the foo.
+ * @return Foo
+ */
+ public FooProxy getFoo() {
+ return foo;
+ }
+
+ /**
+ * Returns the id.
+ * @return long
+ */
+ public long getId() {
+ return id;
+ }
+
+ /**
+ * Returns the property.
+ * @return TimeZone
+ */
+ public TimeZone getProperty() {
+ return property;
+ }
+
+ /**
+ * Sets the foo.
+ * @param foo The foo to set
+ */
+ public void setFoo(FooProxy foo) {
+ this.foo = foo;
+ }
+
+ /**
+ * Sets the id.
+ * @param id The id to set
+ */
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ /**
+ * Sets the property.
+ * @param property The property to set
+ */
+ public void setProperty(TimeZone property) {
+ this.property = property;
+ }
+
+ /**
+ * Returns the moreStuff.
+ * @return MoreStuff
+ */
+ public MoreStuff getMoreStuff() {
+ return moreStuff;
+ }
+
+ /**
+ * Sets the moreStuff.
+ * @param moreStuff The moreStuff to set
+ */
+ public void setMoreStuff(MoreStuff moreStuff) {
+ this.moreStuff = moreStuff;
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/SubComponent.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/SubComponent.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/SubComponent.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,41 @@
+//$Id: SubComponent.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+/**
+ * Sub component
+ *
+ * @author emmanuel
+ */
+public class SubComponent {
+ private String _subName;
+
+ private String _subName1;
+
+ /**
+ * @return
+ */
+ public String getSubName() {
+ return _subName;
+ }
+
+ /**
+ * @param string
+ */
+ public void setSubName(String string) {
+ _subName = string;
+ }
+
+ /**
+ * @return
+ */
+ public String getSubName1() {
+ return _subName1;
+ }
+
+ /**
+ * @param string
+ */
+ public void setSubName1(String string) {
+ _subName1 = string;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/SubDetail.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/SubDetail.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/SubDetail.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,45 @@
+//$Id: SubDetail.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+public class SubDetail {
+ private String name;
+ private long id;
+ /**
+ * Returns the id.
+ * @return long
+ */
+ public long getId() {
+ return id;
+ }
+
+ /**
+ * Returns the name.
+ * @return String
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the id.
+ * @param id The id to set
+ */
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ /**
+ * Sets the name.
+ * @param name The name to set
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/SubMulti.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/SubMulti.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/SubMulti.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,81 @@
+//$Id: SubMulti.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.util.List;
+
+public class SubMulti extends Multi {
+ private float amount;
+ private SubMulti parent;
+ private List children;
+ private List moreChildren;
+ /**
+ * Returns the amount.
+ * @return float
+ */
+ public float getAmount() {
+ return amount;
+ }
+
+ /**
+ * Sets the amount.
+ * @param amount The amount to set
+ */
+ public void setAmount(float amount) {
+ this.amount = amount;
+ }
+
+ /**
+ * Returns the childen.
+ * @return List
+ */
+ public List getChildren() {
+ return children;
+ }
+
+ /**
+ * Returns the parent.
+ * @return SubMulti
+ */
+ public SubMulti getParent() {
+ return parent;
+ }
+
+ /**
+ * Sets the childen.
+ * @param childen The childen to set
+ */
+ public void setChildren(List children) {
+ this.children = children;
+ }
+
+ /**
+ * Sets the parent.
+ * @param parent The parent to set
+ */
+ public void setParent(SubMulti parent) {
+ this.parent = parent;
+ }
+
+ /**
+ * Returns the moreChildren.
+ * @return List
+ */
+ public List getMoreChildren() {
+ return moreChildren;
+ }
+
+ /**
+ * Sets the moreChildren.
+ * @param moreChildren The moreChildren to set
+ */
+ public void setMoreChildren(List moreChildren) {
+ this.moreChildren = moreChildren;
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Super.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Super.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Super.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,15 @@
+package org.hibernate.test.legacy;
+
+public class Super {
+
+ protected String name;
+
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Top.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Top.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Top.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,115 @@
+//$Id: Top.java 5686 2005-02-12 07:27:32Z steveebersole $
+package org.hibernate.test.legacy;
+
+public class Top {
+ private long id;
+ private String name;
+ private String address;
+ private int count;
+ private java.util.Date date;
+ private Top other;
+ private Top top;
+
+ public Top(int c) {
+ count=c;
+ }
+ public Top() {}
+
+ public void init() {
+ name="Someone With Along Name";
+ address="1234 Some Street, Some City, Victoria, 3000, Austraya";
+ count=69;
+ date=new java.sql.Date(666);
+ }
+ /**
+ * Gets the name
+ * @return Returns a String
+ */
+ public String getName() {
+ return name;
+ }
+ /**
+ * Sets the name
+ * @param name The name to set
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Gets the address
+ * @return Returns a String
+ */
+ public String getAddress() {
+ return address;
+ }
+ /**
+ * Sets the address
+ * @param address The address to set
+ */
+ public void setAddress(String address) {
+ this.address = address;
+ }
+
+ /**
+ * Gets the count
+ * @return Returns a int
+ */
+ public int getCount() {
+ return count;
+ }
+ /**
+ * Sets the count
+ * @param count The count to set
+ */
+ public void setCount(int count) {
+ this.count = count;
+ }
+
+ /**
+ * Gets the date
+ * @return Returns a java.util.Date
+ */
+ public java.util.Date getDate() {
+ return date;
+ }
+ /**
+ * Sets the date
+ * @param date The date to set
+ */
+ public void setDate(java.util.Date date) {
+ this.date = date;
+ }
+
+ /**
+ * Returns the other.
+ * @return Simple
+ */
+ public Top getOther() {
+ return other;
+ }
+
+ /**
+ * Sets the other.
+ * @param other The other to set
+ */
+ public void setOther(Top other) {
+ this.other = other;
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long l) {
+ id = l;
+ }
+
+}
+
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Trivial.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Trivial.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Trivial.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,13 @@
+//$Id: Trivial.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+
+public class Trivial extends Foo {
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/TrivialClass.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/TrivialClass.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/TrivialClass.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,6 @@
+//$Id: TrivialClass.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+public class TrivialClass extends Top {
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Up.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Up.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Up.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,39 @@
+//$Id: Up.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+
+/**
+ * @author Gavin King
+ */
+public class Up implements Serializable {
+
+ private String id1;
+ private long id2;
+
+ public String getId1() {
+ return id1;
+ }
+
+ public long getId2() {
+ return id2;
+ }
+
+ public void setId1(String string) {
+ id1 = string;
+ }
+
+ public void setId2(long l) {
+ id2 = l;
+ }
+
+ public boolean equals(Object other) {
+ if ( !(other instanceof Up) ) return false;
+ Up that = (Up) other;
+ return this.id1.equals(that.id1) && this.id2==that.id2;
+ }
+ public int hashCode() {
+ return id1.hashCode();
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/UpDown.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/UpDown.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/UpDown.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping default-lazy="false">
+ <class name="org.hibernate.test.legacy.Up" discriminator-value="null">
+ <composite-id>
+ <key-property name="id1"/>
+ <key-property name="id2"/>
+ </composite-id>
+ <discriminator column="value_" type="long" not-null="false" insert="false"/>
+
+ <subclass name="org.hibernate.test.legacy.Down" discriminator-value="not null">
+ <property name="value" column="value_" type="long"/>
+ </subclass>
+ </class>
+</hibernate-mapping>
+
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Vetoer.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Vetoer.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Vetoer.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,15 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping default-lazy="false">
+
+ <class name="org.hibernate.test.legacy.Vetoer">
+ <id type="string" column="id_" length="32">
+ <generator class="uuid.hex"/>
+ </id>
+ <property name="name"/>
+ <property name="strings"/>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Vetoer.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Vetoer.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Vetoer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,61 @@
+//$Id: Vetoer.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.io.Serializable;
+
+import org.hibernate.CallbackException;
+import org.hibernate.Session;
+import org.hibernate.classic.Lifecycle;
+
+public class Vetoer implements Lifecycle {
+
+ boolean onSaveCalled;
+ boolean onUpdateCalled;
+ boolean onDeleteCalled;
+
+ private String name;
+ private String[] strings;
+
+ public boolean onSave(Session s) throws CallbackException {
+ boolean result = !onSaveCalled;
+ onSaveCalled = true;
+ return result;
+ }
+
+ public boolean onUpdate(Session s) throws CallbackException {
+ boolean result = !onUpdateCalled;
+ onUpdateCalled = true;
+ return result;
+ }
+
+ public boolean onDelete(Session s) throws CallbackException {
+ boolean result = !onDeleteCalled;
+ onDeleteCalled = true;
+ return result;
+ }
+
+ public void onLoad(Session s, Serializable id) {}
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String[] getStrings() {
+ return strings;
+ }
+
+ public void setStrings(String[] strings) {
+ this.strings = strings;
+ }
+
+}
+
+
+
+
+
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/W.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/W.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/W.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,44 @@
+package org.hibernate.test.legacy;
+
+import java.util.Set;
+
+public class W {
+
+ private long id;
+ private Set zeds;
+
+ /**
+ *
+ */
+ public W() {
+ }
+
+ /**
+ * @return
+ */
+ public long getId() {
+ return id;
+ }
+
+ /**
+ * @return
+ */
+ public Set getZeds() {
+ return zeds;
+ }
+
+ /**
+ * @param l
+ */
+ public void setId(long l) {
+ id = l;
+ }
+
+ /**
+ * @param set
+ */
+ public void setZeds(Set set) {
+ zeds = set;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/WZ.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/WZ.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/WZ.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,32 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping default-lazy="false">
+
+ <class name="org.hibernate.test.legacy.Z">
+ <id name="id" unsaved-value="0" column="zid">
+ <generator class="hilo"/>
+ </id>
+ <many-to-one name="w"
+ cascade="save-update"
+ class="org.hibernate.test.legacy.W"
+ insert="true"
+ update="false"
+ not-null="true"/>
+ </class>
+
+ <class name="org.hibernate.test.legacy.W"
+ discriminator-value="0"
+ proxy="org.hibernate.test.legacy.W">
+
+ <id name="id" unsaved-value="0" column="wid">
+ <generator class="hilo"/>
+ </id>
+ <!--<set name="zeds" lazy="true">
+ <key column="w"/>
+ <one-to-many class="org.hibernate.test.legacy.Z"/>
+ </set>-->
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Wicked.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Wicked.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Wicked.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,71 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!-- Mapping document mainly used for testing non-reflective Binder + meta inheritance -->
+<hibernate-mapping default-lazy="false">
+ <meta attribute="global">global value</meta>
+ <meta attribute="globalnoinherit" inherit="false">only visible at top level</meta>
+ <meta attribute="globalmutated">top level</meta>
+
+ <class name="org.hibernate.test.legacy.Wicked"
+ table="WICKED"
+ schema="HR">
+ <meta attribute="implements">java.lang.Observer</meta>
+ <meta attribute="implements">java.lang.Observer</meta>
+ <meta attribute="implements" inherit="false">org.foo.BogusVisitor</meta>
+ <meta attribute="extends">AuditInfo</meta>
+ <meta attribute="globalmutated">wicked level</meta>
+ <id name="id"
+ type="long"
+ column="EMPLOYEE_ID">
+ <generator class="assigned"/>
+ </id>
+ <version name="versionProp" type="long"/>
+ <property name="stringProp" type="string"/>
+ <property name="doubleProp" type="double"/>
+ <property name="objectDoubleProp" type="java.lang.Double"/>
+ <property name="booleanProp" type="boolean"/>
+ <property name="objectBooleanProp" type="java.lang.Boolean"/>
+ <property name="binaryProp" type="binary"/>
+ <many-to-one name="objectManyToOne" class="org.hibernate.test.legacy.Employee" column="MANAGER_ID"/>
+ <component name="component" class="net.sf.hibern8ide.test.MonetaryAmount">
+ <meta attribute="componentonly" inherit="true"/>
+ <meta attribute="implements">AnotherInterface</meta>
+ <meta attribute="allcomponent"/>
+ <meta attribute="globalmutated">monetaryamount level</meta>
+ <property name="x" type="string">
+ <meta attribute="globalmutated">monetaryamount x level</meta>
+ </property>
+ </component>
+
+ <set name="sortedEmployee" sort="org.hibernate.test.legacy.NonExistingComparator">
+ <meta attribute="globalmutated">sortedemployee level</meta>
+ <key column="attrb_id"/>
+ <many-to-many class="org.hibernate.test.legacy.Employee" column="id"/>
+ </set>
+
+ <bag name="anotherSet">
+ <key column="attrb2_id"/>
+ <composite-element class="org.hibernate.test.legacy.Employee">
+ <meta attribute="globalmutated">monetaryamount anotherSet composite level</meta>
+ <property name="emp" type="string">
+ <meta attribute="globalmutated">monetaryamount anotherSet composite property emp level</meta>
+ </property>
+ <many-to-one name="empinone" class="org.hibernate.test.legacy.Employee">
+ <meta attribute="globalmutated">monetaryamount anotherSet composite property empinone level</meta>
+ </many-to-one>
+ </composite-element>
+ </bag>
+
+ </class>
+
+ <class name="org.hibernate.test.legacy.Employee">
+ <composite-id class="X">
+ <key-property name="comp" type="string"/>
+ </composite-id>
+
+ </class>
+</hibernate-mapping>
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/X.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/X.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/X.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,79 @@
+//$Id: X.java 4599 2004-09-26 05:18:27Z oneovthafew $
+package org.hibernate.test.legacy;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class X {
+
+ private long id;
+ private Y y;
+ private List xxs = new ArrayList();
+
+ /**
+ * Returns the id.
+ * @return long
+ */
+ public long getId() {
+ return id;
+ }
+
+ /**
+ * Returns the y.
+ * @return Y
+ */
+ public Y getY() {
+ return y;
+ }
+
+ /**
+ * Sets the id.
+ * @param id The id to set
+ */
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ /**
+ * Sets the y.
+ * @param y The y to set
+ */
+ public void setY(Y y) {
+ this.y = y;
+ }
+
+ public List getXxs() {
+ return xxs;
+ }
+
+ public void setXxs(List xxs) {
+ this.xxs = xxs;
+ }
+
+ public static class XX {
+ private Long id;
+ private X x;
+ private XX() {}
+ public XX(X x) {
+ this.x = x;
+ }
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public X getX() {
+ return x;
+ }
+
+ public void setX(X x) {
+ this.x = x;
+ }
+
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/XY.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/XY.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/XY.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping default-lazy="false">
+ <class name="org.hibernate.test.legacy.X">
+ <id name="id" unsaved-value="0" column="xid">
+ <generator class="foreign">
+ <param name="property">y</param>
+ </generator>
+ </id>
+ <one-to-one name="y" constrained="true" cascade="all"/>
+ <bag name="xxs" inverse="true" cascade="all">
+ <key column="x"/>
+ <one-to-many class="org.hibernate.test.legacy.X$XX"/>
+ </bag>
+ </class>
+ <class name="org.hibernate.test.legacy.Y">
+ <id name="id" column="yid">
+ <generator class="hilo"/>
+ </id>
+ <property name="x"/>
+ <one-to-one name="theX" cascade="save-update"/>
+ </class>
+ <class name="org.hibernate.test.legacy.X$XX" table="xxx">
+ <id name="id">
+ <generator class="hilo"/>
+ </id>
+ <many-to-one name="x" not-null="true"/>
+ </class>
+</hibernate-mapping>
+
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Y.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Y.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Y.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,55 @@
+package org.hibernate.test.legacy;
+
+
+public class Y {
+
+ private Long id;
+ private String x;
+ private X theX;
+ /**
+ * Returns the id.
+ * @return Long
+ */
+ public Long getId() {
+ return id;
+ }
+
+ /**
+ * Returns the x.
+ * @return String
+ */
+ public String getX() {
+ return x;
+ }
+
+ /**
+ * Sets the id.
+ * @param id The id to set
+ */
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ /**
+ * Sets the x.
+ * @param x The x to set
+ */
+ public void setX(String x) {
+ this.x = x;
+ }
+
+ /**
+ * @return
+ */
+ public X getTheX() {
+ return theX;
+ }
+
+ /**
+ * @param x
+ */
+ public void setTheX(X x) {
+ theX = x;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Z.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Z.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/legacy/Z.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,43 @@
+package org.hibernate.test.legacy;
+
+public class Z {
+
+ private long id;
+ private W w;
+
+ /**
+ *
+ */
+ public Z() {
+ }
+
+
+ /**
+ * @return
+ */
+ public long getId() {
+ return id;
+ }
+
+ /**
+ * @return
+ */
+ public W getW() {
+ return w;
+ }
+
+ /**
+ * @param l
+ */
+ public void setId(long l) {
+ id = l;
+ }
+
+ /**
+ * @param w
+ */
+ public void setW(W w) {
+ this.w = w;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lob/BlobTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lob/BlobTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lob/BlobTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,200 @@
+package org.hibernate.test.lob;
+
+import java.sql.Blob;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.LockMode;
+import org.hibernate.Session;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.junit.functional.DatabaseSpecificFunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.util.ArrayHelper;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class BlobTest extends DatabaseSpecificFunctionalTestCase {
+ private static final int BLOB_SIZE = 10000;
+
+ public BlobTest(String name) {
+ super( name );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "lob/LobMappings.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( BlobTest.class );
+ }
+
+ public boolean appliesTo(Dialect dialect) {
+ if ( ! dialect.supportsExpectedLobUsagePattern() ) {
+ reportSkip( "database/driver does not support expected LOB usage pattern", "LOB support" );
+ return false;
+ }
+ return true;
+ }
+
+ public void testBoundedMaterializedBlobAccess() {
+ byte[] original = buildRecursively( BLOB_SIZE, true );
+ byte[] changed = buildRecursively( BLOB_SIZE, false );
+
+ Session s = openSession();
+ s.beginTransaction();
+ LobHolder entity = new LobHolder();
+ entity.setMaterializedBlob( original );
+ s.save( entity );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ entity = ( LobHolder ) s.get( LobHolder.class, entity.getId() );
+ assertEquals( BLOB_SIZE, entity.getMaterializedBlob().length );
+ assertEquals( original, entity.getMaterializedBlob() );
+ entity.setMaterializedBlob( changed );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ entity = ( LobHolder ) s.get( LobHolder.class, entity.getId() );
+ assertEquals( BLOB_SIZE, entity.getMaterializedBlob().length );
+ assertEquals( changed, entity.getMaterializedBlob() );
+ s.delete( entity );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testBoundedBlobLocatorAccess() throws Throwable {
+ byte[] original = buildRecursively( BLOB_SIZE, true );
+ byte[] changed = buildRecursively( BLOB_SIZE, false );
+
+ Session s = openSession();
+ s.beginTransaction();
+ LobHolder entity = new LobHolder();
+ entity.setBlobLocator( Hibernate.createBlob( original ) );
+ s.save( entity );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ entity = ( LobHolder ) s.get( LobHolder.class, entity.getId() );
+ assertEquals( BLOB_SIZE, entity.getBlobLocator().length() );
+ assertEquals( original, extractData( entity.getBlobLocator() ) );
+ s.getTransaction().commit();
+ s.close();
+
+ // test mutation via setting the new clob data...
+ if ( supportsLobValueChangePropogation() ) {
+ s = openSession();
+ s.beginTransaction();
+ entity = ( LobHolder ) s.get( LobHolder.class, entity.getId(), LockMode.UPGRADE );
+ entity.getBlobLocator().truncate( 1 );
+ entity.getBlobLocator().setBytes( 1, changed );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ entity = ( LobHolder ) s.get( LobHolder.class, entity.getId(), LockMode.UPGRADE );
+ assertNotNull( entity.getBlobLocator() );
+ assertEquals( BLOB_SIZE, entity.getBlobLocator().length() );
+ assertEquals( changed, extractData( entity.getBlobLocator() ) );
+ entity.getBlobLocator().truncate( 1 );
+ entity.getBlobLocator().setBytes( 1, original );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ // test mutation via supplying a new clob locator instance...
+ s = openSession();
+ s.beginTransaction();
+ entity = ( LobHolder ) s.get( LobHolder.class, entity.getId(), LockMode.UPGRADE );
+ assertNotNull( entity.getBlobLocator() );
+ assertEquals( BLOB_SIZE, entity.getBlobLocator().length() );
+ assertEquals( original, extractData( entity.getBlobLocator() ) );
+ entity.setBlobLocator( Hibernate.createBlob( changed ) );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ entity = ( LobHolder ) s.get( LobHolder.class, entity.getId() );
+ assertEquals( BLOB_SIZE, entity.getBlobLocator().length() );
+ assertEquals( changed, extractData( entity.getBlobLocator() ) );
+ s.delete( entity );
+ s.getTransaction().commit();
+ s.close();
+
+ }
+
+ public void testUnboundedBlobLocatorAccess() throws Throwable {
+ if ( ! supportsUnboundedLobLocatorMaterialization() ) {
+ return;
+ }
+
+ // Note: unbounded mutation of the underlying lob data is completely
+ // unsupported; most databases would not allow such a construct anyway.
+ // Thus here we are only testing materialization...
+
+ byte[] original = buildRecursively( BLOB_SIZE, true );
+
+ Session s = openSession();
+ s.beginTransaction();
+ LobHolder entity = new LobHolder();
+ entity.setBlobLocator( Hibernate.createBlob( original ) );
+ s.save( entity );
+ s.getTransaction().commit();
+ s.close();
+
+ // load the entity with the clob locator, and close the session/transaction;
+ // at that point it is unbounded...
+ s = openSession();
+ s.beginTransaction();
+ entity = ( LobHolder ) s.get( LobHolder.class, entity.getId() );
+ s.getTransaction().commit();
+ s.close();
+
+ assertEquals( BLOB_SIZE, entity.getBlobLocator().length() );
+ assertEquals( original, extractData( entity.getBlobLocator() ) );
+
+ s = openSession();
+ s.beginTransaction();
+ s.delete( entity );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ private byte[] extractData(Blob blob) throws Throwable {
+ return blob.getBytes( 1, ( int ) blob.length() );
+ }
+
+
+ private byte[] buildRecursively(int size, boolean on) {
+ byte[] data = new byte[size];
+ data[0] = mask( on );
+ for ( int i = 0; i < size; i++ ) {
+ data[i] = mask( on );
+ on = !on;
+ }
+ return data;
+ }
+
+ private byte mask(boolean on) {
+ return on ? ( byte ) 1 : ( byte ) 0;
+ }
+
+ public static void assertEquals(byte[] val1, byte[] val2) {
+ if ( !ArrayHelper.isEquals( val1, val2 ) ) {
+ throw new AssertionFailedError( "byte arrays did not match" );
+ }
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lob/ClobTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lob/ClobTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lob/ClobTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,196 @@
+package org.hibernate.test.lob;
+
+import java.sql.Clob;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.LockMode;
+import org.hibernate.Session;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.H2Dialect;
+import org.hibernate.junit.functional.DatabaseSpecificFunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * Test various access scenarios for eager and lazy materialization
+ * of CLOB data, as well as bounded and unbounded materialization
+ * and mutation.
+ *
+ * @author Steve Ebersole
+ */
+public class ClobTest extends DatabaseSpecificFunctionalTestCase {
+ private static final int CLOB_SIZE = 10000;
+
+ public ClobTest(String name) {
+ super( name );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "lob/LobMappings.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( ClobTest.class );
+ }
+
+ public boolean appliesTo(Dialect dialect) {
+ if ( ! dialect.supportsExpectedLobUsagePattern() ) {
+ reportSkip( "database/driver does not support expected LOB usage pattern", "LOB support" );
+ return false;
+ }
+ return true;
+ }
+
+ public void testBoundedMaterializedClobAccess() {
+ String original = buildRecursively( CLOB_SIZE, 'x' );
+ String changed = buildRecursively( CLOB_SIZE, 'y' );
+
+ Session s = openSession();
+ s.beginTransaction();
+ LobHolder entity = new LobHolder();
+ entity.setMaterializedClob( original );
+ s.save( entity );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ entity = ( LobHolder ) s.get( LobHolder.class, entity.getId() );
+ assertEquals( CLOB_SIZE, entity.getMaterializedClob().length() );
+ assertEquals( original, entity.getMaterializedClob() );
+ entity.setMaterializedClob( changed );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ entity = ( LobHolder ) s.get( LobHolder.class, entity.getId() );
+ assertEquals( CLOB_SIZE, entity.getMaterializedClob().length() );
+ assertEquals( changed, entity.getMaterializedClob() );
+ s.delete( entity );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testBoundedClobLocatorAccess() throws Throwable {
+ String original = buildRecursively( CLOB_SIZE, 'x' );
+ String changed = buildRecursively( CLOB_SIZE, 'y' );
+
+ Session s = openSession();
+ s.beginTransaction();
+ LobHolder entity = new LobHolder();
+ entity.setClobLocator( Hibernate.createClob( original ) );
+ s.save( entity );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ entity = ( LobHolder ) s.get( LobHolder.class, entity.getId() );
+ assertEquals( CLOB_SIZE, entity.getClobLocator().length() );
+ assertEquals( original, extractData( entity.getClobLocator() ) );
+ s.getTransaction().commit();
+ s.close();
+
+ // test mutation via setting the new clob data...
+ if ( supportsLobValueChangePropogation() ) {
+ s = openSession();
+ s.beginTransaction();
+ entity = ( LobHolder ) s.get( LobHolder.class, entity.getId(), LockMode.UPGRADE );
+ entity.getClobLocator().truncate( 1 );
+ entity.getClobLocator().setString( 1, changed );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ entity = ( LobHolder ) s.get( LobHolder.class, entity.getId(), LockMode.UPGRADE );
+ assertNotNull( entity.getClobLocator() );
+ assertEquals( CLOB_SIZE, entity.getClobLocator().length() );
+ assertEquals( changed, extractData( entity.getClobLocator() ) );
+ entity.getClobLocator().truncate( 1 );
+ entity.getClobLocator().setString( 1, original );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ // test mutation via supplying a new clob locator instance...
+ s = openSession();
+ s.beginTransaction();
+ entity = ( LobHolder ) s.get( LobHolder.class, entity.getId(), LockMode.UPGRADE );
+ assertNotNull( entity.getClobLocator() );
+ assertEquals( CLOB_SIZE, entity.getClobLocator().length() );
+ assertEquals( original, extractData( entity.getClobLocator() ) );
+ entity.setClobLocator( Hibernate.createClob( changed ) );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ entity = ( LobHolder ) s.get( LobHolder.class, entity.getId() );
+ assertEquals( CLOB_SIZE, entity.getClobLocator().length() );
+ assertEquals( changed, extractData( entity.getClobLocator() ) );
+ s.delete( entity );
+ s.getTransaction().commit();
+ s.close();
+
+ }
+
+ public void testUnboundedClobLocatorAccess() throws Throwable {
+ if ( ! supportsUnboundedLobLocatorMaterialization() ) {
+ return;
+ }
+
+ // Note: unbounded mutation of the underlying lob data is completely
+ // unsupported; most databases would not allow such a construct anyway.
+ // Thus here we are only testing materialization...
+
+ String original = buildRecursively( CLOB_SIZE, 'x' );
+
+ Session s = openSession();
+ s.beginTransaction();
+ LobHolder entity = new LobHolder();
+ entity.setClobLocator( Hibernate.createClob( original ) );
+ s.save( entity );
+ s.getTransaction().commit();
+ s.close();
+
+ // load the entity with the clob locator, and close the session/transaction;
+ // at that point it is unbounded...
+ s = openSession();
+ s.beginTransaction();
+ entity = ( LobHolder ) s.get( LobHolder.class, entity.getId() );
+ s.getTransaction().commit();
+ s.close();
+
+ assertEquals( CLOB_SIZE, entity.getClobLocator().length() );
+ assertEquals( original, extractData( entity.getClobLocator() ) );
+
+ s = openSession();
+ s.beginTransaction();
+ s.delete( entity );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ private String extractData(Clob clob) throws Throwable {
+ if ( getDialect() instanceof H2Dialect ) {
+ return clob.getSubString( 1, ( int ) clob.length() );
+ }
+ else {
+ char[] data = new char[ (int) clob.length() ];
+ clob.getCharacterStream().read( data );
+ return new String( data );
+ }
+ }
+
+
+ private String buildRecursively(int size, char baseChar) {
+ StringBuffer buff = new StringBuffer();
+ for( int i = 0; i < size; i++ ) {
+ buff.append( baseChar );
+ }
+ return buff.toString();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lob/LobHolder.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lob/LobHolder.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lob/LobHolder.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,90 @@
+package org.hibernate.test.lob;
+
+import java.io.Serializable;
+import java.sql.Clob;
+import java.sql.Blob;
+
+/**
+ * An entity containing all kinds of good LOB-type data...
+ * <p/>
+ * {@link #serialData} is used to hold general serializable data which is
+ * mapped via the {@link org.hibernate.type.SerializableType}.
+ * <p/>
+ * {@link #materializedClob} is used to hold CLOB data that is materialized
+ * into a String immediately; it is mapped via the
+ * {@link org.hibernate.type.TextType}.
+ * <p/>
+ * {@link #clobLocator} is used to hold CLOB data that is materialized lazily
+ * via a JDBC CLOB locator; it is mapped via the
+ * {@link org.hibernate.type.ClobType}
+ * <p/>
+ * {@link #materializedBlob} is used to hold BLOB data that is materialized
+ * into a byte array immediately; it is mapped via the
+ * {@link org.hibernate.test.lob.MaterializedBlobType}.
+ * <p/>
+ * {@link #blobLocator} is used to hold BLOB data that is materialized lazily
+ * via a JDBC BLOB locator; it is mapped via the
+ * {@link org.hibernate.type.BlobType}
+ *
+ *
+ * @author Steve Ebersole
+ */
+public class LobHolder {
+ private Long id;
+
+ private Serializable serialData;
+
+ private String materializedClob;
+ private Clob clobLocator;
+
+ private byte[] materializedBlob;
+ private Blob blobLocator;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public Serializable getSerialData() {
+ return serialData;
+ }
+
+ public void setSerialData(Serializable serialData) {
+ this.serialData = serialData;
+ }
+
+ public String getMaterializedClob() {
+ return materializedClob;
+ }
+
+ public void setMaterializedClob(String materializedClob) {
+ this.materializedClob = materializedClob;
+ }
+
+ public Clob getClobLocator() {
+ return clobLocator;
+ }
+
+ public void setClobLocator(Clob clobLocator) {
+ this.clobLocator = clobLocator;
+ }
+
+ public byte[] getMaterializedBlob() {
+ return materializedBlob;
+ }
+
+ public void setMaterializedBlob(byte[] materializedBlob) {
+ this.materializedBlob = materializedBlob;
+ }
+
+ public Blob getBlobLocator() {
+ return blobLocator;
+ }
+
+ public void setBlobLocator(Blob blobLocator) {
+ this.blobLocator = blobLocator;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lob/LobMappings.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lob/LobMappings.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lob/LobMappings.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 2.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.lob">
+
+ <class name="LobHolder" table="LOB_ENTITY">
+ <id name="id" type="long" column="ID">
+ <generator class="increment"/>
+ </id>
+
+ <property name="serialData" column="SER_DATA" type="serializable"/>
+
+ <property name="materializedClob" column="MAT_CLOB_DATA" type="text" length="15000"/>
+ <property name="clobLocator" column="CLOB_DATA" type="clob" length="15000"/>
+
+ <property name="materializedBlob" column="MAT_BLOB_DATA" type="org.hibernate.test.lob.MaterializedBlobType" length="15000"/>
+ <property name="blobLocator" column="BLOB_DATA" type="blob" length="15000"/>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lob/LobSuite.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lob/LobSuite.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lob/LobSuite.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,19 @@
+package org.hibernate.test.lob;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class LobSuite {
+ public static Test suite() {
+ TestSuite suite = new TestSuite( "LOB handling tests" );
+ suite.addTest( SerializableTypeTest.suite() );
+ suite.addTest( BlobTest.suite() );
+ suite.addTest( ClobTest.suite() );
+ return suite;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lob/MaterializedBlobType.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lob/MaterializedBlobType.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lob/MaterializedBlobType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,33 @@
+package org.hibernate.test.lob;
+
+import java.sql.Types;
+
+import org.hibernate.type.AbstractBynaryType;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class MaterializedBlobType extends AbstractBynaryType {
+
+ public int sqlType() {
+ return Types.BLOB;
+ }
+
+ public String getName() {
+ return "materialized-blob";
+ }
+
+ public Class getReturnedClass() {
+ return byte[].class;
+ }
+
+ protected Object toExternalFormat(byte[] bytes) {
+ return bytes;
+ }
+
+ protected byte[] toInternalFormat(Object bytes) {
+ return ( byte[] ) bytes;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lob/SerializableData.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lob/SerializableData.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lob/SerializableData.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,29 @@
+// $Id: SerializableData.java 4704 2004-11-04 21:59:22Z steveebersole $
+package org.hibernate.test.lob;
+
+import java.io.Serializable;
+
+/**
+ * Implementation of SerializableData.
+ *
+ * @author Steve
+ */
+public class SerializableData implements Serializable
+{
+ private String payload;
+
+ public SerializableData(String payload)
+ {
+ this.payload = payload;
+ }
+
+ public String getPayload()
+ {
+ return payload;
+ }
+
+ public void setPayload(String payload)
+ {
+ this.payload = payload;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lob/SerializableTypeTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lob/SerializableTypeTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/lob/SerializableTypeTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,55 @@
+// $Id: SerializableTypeTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.lob;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * Tests of {@link org.hibernate.type.SerializableType}
+ *
+ * @author Steve Ebersole
+ */
+public class SerializableTypeTest extends FunctionalTestCase {
+
+ public SerializableTypeTest(String testName) {
+ super( testName );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "lob/LobMappings.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( SerializableTypeTest.class );
+ }
+
+ public String getCacheConcurrencyStrategy() {
+ return null;
+ }
+
+ public void testNewSerializableType() {
+ final String payloadText = "Initial payload";
+
+ Session s = openSession();
+ s.beginTransaction();
+ LobHolder holder = new LobHolder();
+ holder.setSerialData( new SerializableData( payloadText ) );
+ s.save( holder );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ holder = ( LobHolder ) s.get( LobHolder.class, holder.getId() );
+ SerializableData serialData = ( SerializableData ) holder.getSerialData();
+ assertEquals( payloadText, serialData.getPayload() );
+ s.delete( holder );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/manytomany/Group.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/manytomany/Group.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/manytomany/Group.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,56 @@
+//$Id: Group.java 7085 2005-06-08 17:59:47Z oneovthafew $
+package org.hibernate.test.manytomany;
+
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Set;
+
+public class Group implements Serializable {
+
+ private String org;
+ private String name;
+ private String description;
+
+ private Set users = new HashSet();
+
+ public Group(String name, String org) {
+ this.org = org;
+ this.name = name;
+ }
+
+ public Group() {
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getOrg() {
+ return org;
+ }
+
+ public void setOrg(String org) {
+ this.org = org;
+ }
+
+ public Set getUsers() {
+ return users;
+ }
+
+ public void setUsers(Set users) {
+ this.users = users;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/manytomany/Group.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/manytomany/ManyToManyTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/manytomany/ManyToManyTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/manytomany/ManyToManyTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,127 @@
+//$Id: ManyToManyTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.manytomany;
+
+import junit.framework.Test;
+
+import org.hibernate.FetchMode;
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class ManyToManyTest extends FunctionalTestCase {
+
+ public ManyToManyTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "manytomany/UserGroup.hbm.xml" };
+ }
+
+ public void configure(Configuration cfg) {
+ cfg.setProperty(Environment.USE_SECOND_LEVEL_CACHE, "false");
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( ManyToManyTest.class );
+ }
+
+ public void testManyToManyWithFormula() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ User gavin = new User("gavin", "jboss");
+ Group seam = new Group("seam", "jboss");
+ Group hb = new Group("hibernate", "jboss");
+ gavin.getGroups().add(seam);
+ gavin.getGroups().add(hb);
+ seam.getUsers().add(gavin);
+ hb.getUsers().add(gavin);
+ s.persist(gavin);
+ s.persist(seam);
+ s.persist(hb);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ gavin = (User) s.get(User.class, gavin);
+ assertFalse( Hibernate.isInitialized( gavin.getGroups() ) );
+ assertEquals( 2, gavin.getGroups().size() );
+ hb = (Group) s.get(Group.class, hb);
+ assertFalse( Hibernate.isInitialized( hb.getUsers() ) );
+ assertEquals( 1, hb.getUsers().size() );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ gavin = (User) s.createCriteria(User.class)
+ .setFetchMode("groups", FetchMode.JOIN)
+ .uniqueResult();
+ assertTrue( Hibernate.isInitialized( gavin.getGroups() ) );
+ assertEquals( 2, gavin.getGroups().size() );
+ Group group = (Group) gavin.getGroups().iterator().next();
+ assertFalse( Hibernate.isInitialized( group.getUsers() ) );
+ assertEquals( 1, group.getUsers().size() );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ gavin = (User) s.createCriteria(User.class)
+ .setFetchMode("groups", FetchMode.JOIN)
+ .setFetchMode("groups.users", FetchMode.JOIN)
+ .uniqueResult();
+ assertTrue( Hibernate.isInitialized( gavin.getGroups() ) );
+ assertEquals( 2, gavin.getGroups().size() );
+ group = (Group) gavin.getGroups().iterator().next();
+ assertTrue( Hibernate.isInitialized( group.getUsers() ) );
+ assertEquals( 1, group.getUsers().size() );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ gavin = (User) s.createQuery("from User u join fetch u.groups g join fetch g.users").uniqueResult();
+ assertTrue( Hibernate.isInitialized( gavin.getGroups() ) );
+ assertEquals( 2, gavin.getGroups().size() );
+ group = (Group) gavin.getGroups().iterator().next();
+ assertTrue( Hibernate.isInitialized( group.getUsers() ) );
+ assertEquals( 1, group.getUsers().size() );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ gavin = (User) s.get(User.class, gavin);
+ hb = (Group) s.get(Group.class, hb);
+ gavin.getGroups().remove(hb);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ gavin = (User) s.get(User.class, gavin);
+ assertEquals( gavin.getGroups().size(), 1 );
+ hb = (Group) s.get(Group.class, hb);
+ assertEquals( hb.getUsers().size(), 0 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.delete(gavin);
+ s.flush();
+ s.createQuery("delete from Group").executeUpdate();
+ t.commit();
+ s.close();
+ }
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/manytomany/ManyToManyTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/manytomany/User.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/manytomany/User.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/manytomany/User.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,46 @@
+//$Id: User.java 7085 2005-06-08 17:59:47Z oneovthafew $
+package org.hibernate.test.manytomany;
+
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Set;
+
+public class User implements Serializable {
+
+ private String org;
+ private String name;
+ private Set groups = new HashSet();
+
+ public User(String name, String org) {
+ this.org = org;
+ this.name = name;
+ }
+
+ public User() {
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getOrg() {
+ return org;
+ }
+
+ public void setOrg(String org) {
+ this.org = org;
+ }
+
+ public Set getGroups() {
+ return groups;
+ }
+
+ public void setGroups(Set groups) {
+ this.groups = groups;
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/manytomany/User.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/manytomany/UserGroup.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/manytomany/UserGroup.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/manytomany/UserGroup.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,52 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+ This mapping demonstrates how to map a many-to-many
+ association with a shared attribute in the primary keys
+ of the associated entities.
+
+-->
+
+<hibernate-mapping
+ package="org.hibernate.test.manytomany">
+
+ <class name="User" table="`User`">
+ <composite-id>
+ <key-property name="name"/>
+ <key-property name="org"/>
+ </composite-id>
+ <set name="groups" table="UserGroup">
+ <key>
+ <column name="userName"/>
+ <column name="org"/>
+ </key>
+ <many-to-many class="Group">
+ <column name="groupName"/>
+ <formula>org</formula>
+ </many-to-many>
+ </set>
+ </class>
+
+ <class name="Group" table="`Group`">
+ <composite-id>
+ <key-property name="name"/>
+ <key-property name="org"/>
+ </composite-id>
+ <property name="description"/>
+ <set name="users" table="UserGroup" inverse="true">
+ <key>
+ <column name="groupName"/>
+ <column name="org"/>
+ </key>
+ <many-to-many class="User">
+ <column name="userName"/>
+ <formula>org</formula>
+ </many-to-many>
+ </set>
+ </class>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/manytomany/UserGroup.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/manytomany/ordered/Group.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/manytomany/ordered/Group.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/manytomany/ordered/Group.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,69 @@
+package org.hibernate.test.manytomany.ordered;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.ArrayList;
+
+public class Group implements Serializable {
+
+ private Long id;
+ private String org;
+ private String name;
+ private String description;
+
+ private List users = new ArrayList();
+
+ public Group() {
+ }
+
+ public Group(String name, String org) {
+ this.org = org;
+ 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;
+ }
+
+ public String getOrg() {
+ return org;
+ }
+
+ public void setOrg(String org) {
+ this.org = org;
+ }
+
+ public List getUsers() {
+ return users;
+ }
+
+ public void setUsers(List users) {
+ this.users = users;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public void addUser(User user) {
+ if ( user.getGroups().add( this ) ) {
+ getUsers().add( user );
+ }
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/manytomany/ordered/OrderedManyToManyTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/manytomany/ordered/OrderedManyToManyTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/manytomany/ordered/OrderedManyToManyTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,139 @@
+package org.hibernate.test.manytomany.ordered;
+
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.hibernate.Criteria;
+import org.hibernate.FetchMode;
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class OrderedManyToManyTest extends FunctionalTestCase {
+
+ public OrderedManyToManyTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "manytomany/ordered/UserGroup.hbm.xml" };
+ }
+
+ public void configure(Configuration cfg) {
+ cfg.setProperty( Environment.USE_SECOND_LEVEL_CACHE, "false");
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( OrderedManyToManyTest.class );
+ }
+
+ public void testManyToManyOrdering() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ User gavin = new User( "gavin", "jboss" );
+ User steve = new User( "steve", "jboss" );
+ User max = new User( "max", "jboss" );
+ User emmanuel = new User( "emmanuel", "jboss" );
+ s.persist( gavin );
+ s.persist( steve );
+ s.persist( max );
+ s.persist( emmanuel );
+ Group hibernate = new Group( "hibernate", "jboss" );
+ hibernate.addUser( gavin );
+ hibernate.addUser( steve );
+ hibernate.addUser( max );
+ hibernate.addUser( emmanuel );
+ s.persist( hibernate );
+ t.commit();
+ s.close();
+
+ // delayed collection load...
+ s = openSession();
+ t = s.beginTransaction();
+ hibernate = ( Group ) s.get( Group.class, hibernate.getId() );
+ assertFalse( Hibernate.isInitialized( hibernate.getUsers() ) );
+ assertEquals( 4, hibernate.getUsers().size() );
+ assertOrdering( hibernate.getUsers() );
+ t.commit();
+ s.close();
+
+ // HQL (non eager)
+ s = openSession();
+ t = s.beginTransaction();
+ hibernate = ( Group ) s.createQuery( "from Group" ).uniqueResult();
+ assertFalse( Hibernate.isInitialized( hibernate.getUsers() ) );
+ assertEquals( 4, hibernate.getUsers().size() );
+ assertOrdering( hibernate.getUsers() );
+ t.commit();
+ s.close();
+
+ // HQL (eager)
+ s = openSession();
+ t = s.beginTransaction();
+ hibernate = ( Group ) s.createQuery( "from Group g inner join fetch g.users" ).uniqueResult();
+ assertTrue( Hibernate.isInitialized( hibernate.getUsers() ) );
+ assertEquals( 4, hibernate.getUsers().size() );
+ assertOrdering( hibernate.getUsers() );
+ t.commit();
+ s.close();
+
+ // criteria load (forced eager fetch)
+ s = openSession();
+ t = s.beginTransaction();
+ Criteria criteria = s.createCriteria( Group.class );
+ criteria.setFetchMode( "users", FetchMode.JOIN );
+ hibernate = ( Group ) criteria.uniqueResult();
+ assertTrue( Hibernate.isInitialized( hibernate.getUsers() ) );
+ assertEquals( 4, hibernate.getUsers().size() );
+ assertOrdering( hibernate.getUsers() );
+ t.commit();
+ s.close();
+
+ // criteria load (forced non eager fetch)
+ s = openSession();
+ t = s.beginTransaction();
+ criteria = s.createCriteria( Group.class );
+ criteria.setFetchMode( "users", FetchMode.SELECT );
+ hibernate = ( Group ) criteria.uniqueResult();
+ assertFalse( Hibernate.isInitialized( hibernate.getUsers() ) );
+ assertEquals( 4, hibernate.getUsers().size() );
+ assertOrdering( hibernate.getUsers() );
+ t.commit();
+ s.close();
+
+ // clean up
+ s = openSession();
+ t = s.beginTransaction();
+ s.delete( gavin );
+ s.delete( steve );
+ s.delete( max );
+ s.delete( emmanuel );
+ s.delete( hibernate );
+ t.commit();
+ s.close();
+ }
+
+ private void assertOrdering(List users) {
+ User user = extractUser( users, 0 );
+ assertTrue( "many-to-many ordering not applied", user.getName().equals( "emmanuel" ) );
+ user = extractUser( users, 1 );
+ assertTrue( "many-to-many ordering not applied", user.getName().equals( "gavin" ) );
+ user = extractUser( users, 2 );
+ assertTrue( "many-to-many ordering not applied", user.getName().equals( "max" ) );
+ user = extractUser( users, 3 );
+ assertTrue( "many-to-many ordering not applied", user.getName().equals( "steve" ) );
+ }
+
+ private User extractUser(List users, int position) {
+ return ( User ) users.get( position );
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/manytomany/ordered/User.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/manytomany/ordered/User.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/manytomany/ordered/User.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,54 @@
+package org.hibernate.test.manytomany.ordered;
+
+import java.io.Serializable;
+import java.util.Set;
+import java.util.HashSet;
+
+public class User implements Serializable {
+
+ private Long id;
+ private String org;
+ private String name;
+ private Set groups = new HashSet();
+
+ public User() {
+ }
+
+ public User(String name, String org) {
+ this.org = org;
+ 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;
+ }
+
+ public String getOrg() {
+ return org;
+ }
+
+ public void setOrg(String org) {
+ this.org = org;
+ }
+
+ public Set getGroups() {
+ return groups;
+ }
+
+ public void setGroups(Set groups) {
+ this.groups = groups;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/manytomany/ordered/UserGroup.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/manytomany/ordered/UserGroup.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/manytomany/ordered/UserGroup.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,42 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+ This mapping demonstrates how to map a many-to-many
+ association with a shared attribute in the primary keys
+ of the associated entities.
+
+-->
+
+<hibernate-mapping
+ package="org.hibernate.test.manytomany.ordered">
+
+
+ <class name="User" table="`User`">
+ <id name="id" column="ID" type="long">
+ <generator class="native"/>
+ </id>
+ <property name="name" column="NAME"/>
+ <property name="org" column="ORG"/>
+ <set name="groups" table="UserGroup">
+ <key column="USER_ID"/>
+ <many-to-many column="GROUP_ID" class="Group"/>
+ </set>
+ </class>
+
+ <class name="Group" table="`Group`">
+ <id name="id" column="ID">
+ <generator class="native"/>
+ </id>
+ <property name="name"/>
+ <property name="description"/>
+ <bag name="users" table="UserGroup" inverse="true">
+ <key column="GROUP_ID"/>
+ <many-to-many column="USER_ID" class="User" order-by="NAME"/>
+ </bag>
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/map/Group.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/map/Group.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/map/Group.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,29 @@
+//$Id: Group.java 5686 2005-02-12 07:27:32Z steveebersole $
+package org.hibernate.test.map;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Gavin King
+ */
+public class Group {
+ private String name;
+ private Map users = new HashMap();
+ Group() {}
+ public Group(String n) {
+ name = n;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public Map getUsers() {
+ return users;
+ }
+ public void setUsers(Map users) {
+ this.users = users;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/map/Group.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/map/MapIndexFormulaTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/map/MapIndexFormulaTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/map/MapIndexFormulaTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,107 @@
+//$Id: MapIndexFormulaTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.map;
+
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class MapIndexFormulaTest extends FunctionalTestCase {
+
+ public MapIndexFormulaTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "map/UserGroup.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( MapIndexFormulaTest.class );
+ }
+
+ public void testIndexFormulaMap() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ User gavin = new User("gavin", "secret");
+ User turin = new User("turin", "tiger");
+ Group g = new Group("developers");
+ g.getUsers().put("gavin", gavin);
+ g.getUsers().put("turin", turin);
+ s.persist(g);
+ gavin.getSession().put( "foo", new SessionAttribute("foo", "foo bar baz") );
+ gavin.getSession().put( "bar", new SessionAttribute("bar", "foo bar baz 2") );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ g = (Group) s.get(Group.class, "developers");
+ assertEquals( g.getUsers().size(), 2 );
+ g.getUsers().remove("turin");
+ Map smap = ( (User) g.getUsers().get("gavin") ).getSession();
+ assertEquals(smap.size(), 2);
+ smap.remove("bar");
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ g = (Group) s.get(Group.class, "developers");
+ assertEquals( g.getUsers().size(), 1 );
+ smap = ( (User) g.getUsers().get("gavin") ).getSession();
+ assertEquals(smap.size(), 1);
+ gavin = (User) g.getUsers().put("gavin", turin);
+ s.delete(gavin);
+ assertEquals( s.createQuery("select count(*) from SessionAttribute").uniqueResult(), new Long(0) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ g = (Group) s.get(Group.class, "developers");
+ assertEquals( g.getUsers().size(), 1 );
+ turin = (User) g.getUsers().get("turin");
+ smap = turin.getSession();
+ assertEquals(smap.size(), 0);
+ assertEquals( s.createQuery("select count(*) from User").uniqueResult(), new Long(1) );
+ s.delete(g);
+ s.delete(turin);
+ assertEquals( s.createQuery("select count(*) from User").uniqueResult(), new Long(0) );
+ t.commit();
+ s.close();
+ }
+
+ public void testSQLQuery() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ User gavin = new User("gavin", "secret");
+ User turin = new User("turin", "tiger");
+ gavin.getSession().put( "foo", new SessionAttribute("foo", "foo bar baz") );
+ gavin.getSession().put( "bar", new SessionAttribute("bar", "foo bar baz 2") );
+ s.persist(gavin);
+ s.persist(turin);
+ s.flush();
+ s.clear();
+ List results = s.getNamedQuery("userSessionData").setParameter("uname", "%in").list();
+ assertEquals( results.size(), 2 );
+ gavin = (User) ( (Object[]) results.get(0) )[0];
+ assertEquals( gavin.getName(), "gavin" );
+ assertEquals( gavin.getSession().size(), 2 );
+ s.createQuery("delete SessionAttribute").executeUpdate();
+ s.createQuery("delete User").executeUpdate();
+ t.commit();
+ s.close();
+
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/map/MapIndexFormulaTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/map/SessionAttribute.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/map/SessionAttribute.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/map/SessionAttribute.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,41 @@
+//$Id: SessionAttribute.java 5686 2005-02-12 07:27:32Z steveebersole $
+package org.hibernate.test.map;
+
+import java.io.Serializable;
+
+/**
+ * @author Gavin King
+ */
+public class SessionAttribute {
+ private Long id;
+ private String name;
+ private String stringData;
+ private Serializable objectData;
+ SessionAttribute() {}
+ public SessionAttribute(String name, Serializable obj) {
+ this.name = name;
+ this.objectData = obj;
+ }
+ public SessionAttribute(String name, String str) {
+ this.name = name;
+ this.stringData = str;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public Serializable getObjectData() {
+ return objectData;
+ }
+ public void setObjectData(Serializable objectData) {
+ this.objectData = objectData;
+ }
+ public String getStringData() {
+ return stringData;
+ }
+ public void setStringData(String stringData) {
+ this.stringData = stringData;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/map/SessionAttribute.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/map/User.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/map/User.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/map/User.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,37 @@
+//$Id: User.java 5686 2005-02-12 07:27:32Z steveebersole $
+package org.hibernate.test.map;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Gavin King
+ */
+public class User {
+ private String name;
+ private String password;
+ private Map session = new HashMap();
+ User() {}
+ public User(String n, String pw) {
+ name=n;
+ password = pw;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public String getPassword() {
+ return password;
+ }
+ public void setPassword(String password) {
+ this.password = password;
+ }
+ public Map getSession() {
+ return session;
+ }
+ public void setSession(Map session) {
+ this.session = session;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/map/User.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/map/UserGroup.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/map/UserGroup.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/map/UserGroup.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,53 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.map">
+
+ <class name="Group" table="groups">
+ <id name="name"/>
+ <map name="users" cascade="persist" table="group_user">
+ <key column="groupName"/>
+ <map-key formula="lower(personName)" type="string"/>
+ <many-to-many column="personName" class="User"/>
+ </map>
+ </class>
+
+ <class name="User" table="users">
+ <id name="name"/>
+ <property name="password"/>
+ <map name="session" cascade="persist,save-update,delete,delete-orphan">
+ <key column="userName" not-null="true"/>
+ <map-key formula="lower(name)" type="string"/>
+ <one-to-many class="SessionAttribute"/>
+ </map>
+ </class>
+
+ <class name="SessionAttribute" table="session_attributes">
+ <id name="id" access="field">
+ <generator class="native"/>
+ </id>
+ <property name="name" not-null="true" update="false"/>
+ <property name="stringData"/>
+ <property name="objectData"/>
+ </class>
+
+ <sql-query name="userSessionData">
+ <return alias="u" class="User"/>
+ <return-join alias="s" property="u.session"/>
+ select
+ lower(u.name) as {u.name}, lower(u.password) as {u.password},
+ lower(s.userName) as {s.key}, lower(s.name) as {s.index}, s.id as {s.element},
+ {s.element.*}
+ from users u
+ join session_attributes s on lower(s.userName) = lower(u.name)
+ where u.name like :uname
+ </sql-query>
+
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/map/UserGroup.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapcompelem/Item.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapcompelem/Item.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapcompelem/Item.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,35 @@
+//$Id: Item.java 6236 2005-03-29 03:20:23Z oneovthafew $
+package org.hibernate.test.mapcompelem;
+
+/**
+ * @author Gavin King
+ */
+public class Item {
+
+ private String code;
+ private Product product;
+
+
+ Item() {}
+ public Item(String code, Product p) {
+ this.code = code;
+ this.product = p;
+ }
+
+ public String getCode() {
+ return code;
+ }
+
+ public void setCode(String code) {
+ this.code = code;
+ }
+
+ public Product getProduct() {
+ return product;
+ }
+
+ public void setProduct(Product product) {
+ this.product = product;
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapcompelem/Item.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapcompelem/MapCompositeElementTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapcompelem/MapCompositeElementTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapcompelem/MapCompositeElementTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,116 @@
+//$Id: MapCompositeElementTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.mapcompelem;
+
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class MapCompositeElementTest extends FunctionalTestCase {
+
+ public MapCompositeElementTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "mapcompelem/ProductPart.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( MapCompositeElementTest.class );
+ }
+
+ public void testMapCompositeElementWithFormula() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Part top = new Part("top", "The top part");
+ Part bottom = new Part("bottom", "The bottom part");
+ Product prod = new Product("Some Thing");
+ prod.getParts().put("Top", top);
+ prod.getParts().put("Bottom", bottom);
+ s.persist(prod);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ prod = (Product) s.get(Product.class, "Some Thing");
+ assertEquals( prod.getParts().size(), 2 );
+ prod.getParts().remove("Bottom");
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ prod = (Product) s.get(Product.class, "Some Thing");
+ assertEquals( prod.getParts().size(), 1 );
+ prod.getParts().put("Top", new Part("top", "The brand new top part"));
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ prod = (Product) s.get(Product.class, "Some Thing");
+ assertEquals( prod.getParts().size(), 1 );
+ assertEquals( ( (Part) prod.getParts().get("Top") ).getDescription(), "The brand new top part");
+ s.delete(prod);
+ t.commit();
+ s.close();
+ }
+
+ public void testQueryMapCompositeElement() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ Part top = new Part("top", "The top part");
+ Part bottom = new Part("bottom", "The bottom part");
+ Product prod = new Product("Some Thing");
+ prod.getParts().put("Top", top);
+ prod.getParts().put("Bottom", bottom);
+ s.persist(prod);
+
+ Item item = new Item("123456", prod);
+ s.persist(item);
+
+ List list = s.createQuery("select new Part( part.name, part.description ) from Product prod join prod.parts part order by part.name desc").list();
+ assertEquals( list.size(), 2 );
+ assertTrue( list.get(0) instanceof Part );
+ assertTrue( list.get(1) instanceof Part );
+ Part part = (Part) list.get(0);
+ assertEquals( part.getName(), "top" );
+ assertEquals( part.getDescription(), "The top part" );
+
+ list = s.createQuery("select new Part( part.name, part.description ) from Product prod join prod.parts part where index(part) = 'Top'").list();
+ assertEquals( list.size(), 1 );
+ assertTrue( list.get(0) instanceof Part );
+ part = (Part) list.get(0);
+ assertEquals( part.getName(), "top" );
+ assertEquals( part.getDescription(), "The top part" );
+
+ list = s.createQuery("from Product p where 'Top' in indices(p.parts)").list();
+ assertEquals( list.size(), 1 );
+ assertSame( list.get(0), prod );
+
+ list = s.createQuery("select i from Item i join i.product p where 'Top' in indices(p.parts)").list();
+ assertEquals( list.size(), 1 );
+ assertSame( list.get(0), item );
+
+ list = s.createQuery("from Item i where 'Top' in indices(i.product.parts)").list();
+ assertEquals( list.size(), 1 );
+ assertSame( list.get(0), item );
+
+ s.delete(item);
+ s.delete(prod);
+ t.commit();
+ s.close();
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapcompelem/MapCompositeElementTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapcompelem/Part.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapcompelem/Part.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapcompelem/Part.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,37 @@
+//$Id: Part.java 5686 2005-02-12 07:27:32Z steveebersole $
+package org.hibernate.test.mapcompelem;
+
+
+/**
+ * @author Gavin King
+ */
+public class Part {
+ private String name;
+ private String description;
+ Part() {}
+ public Part(String n, String pw) {
+ name=n;
+ description = pw;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public String getDescription() {
+ return description;
+ }
+ public void setDescription(String password) {
+ this.description = password;
+ }
+ public boolean equals(Object that) {
+ return ( (Part) that ).getName().equals(name);
+ }
+ public int hashCode() {
+ return name.hashCode();
+ }
+ public String toString() {
+ return name + ":" + description;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapcompelem/Part.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapcompelem/Product.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapcompelem/Product.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapcompelem/Product.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,29 @@
+//$Id: Product.java 5686 2005-02-12 07:27:32Z steveebersole $
+package org.hibernate.test.mapcompelem;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Gavin King
+ */
+public class Product {
+ private String name;
+ private Map parts = new HashMap();
+ Product() {}
+ public Product(String n) {
+ name = n;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public Map getParts() {
+ return parts;
+ }
+ public void setParts(Map users) {
+ this.parts = users;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapcompelem/Product.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapcompelem/ProductPart.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapcompelem/ProductPart.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapcompelem/ProductPart.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.mapcompelem">
+
+ <import class="Part"/>
+
+ <class name="Product" table="Products">
+ <id name="name" column="productName"/>
+ <map name="parts" table="Parts">
+ <key column="productName"/>
+ <map-key column="partName" type="string"/>
+ <composite-element class="Part">
+ <property name="name" formula="lower(partName)"/>
+ <property name="description" column="partDescription"/>
+ </composite-element>
+ </map>
+ </class>
+
+ <class name="Item" table="Items">
+ <id name="code"/>
+ <many-to-one name="product" column="productName"/>
+ </class>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapcompelem/ProductPart.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapelemformula/Group.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapelemformula/Group.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapelemformula/Group.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,29 @@
+//$Id: Group.java 5686 2005-02-12 07:27:32Z steveebersole $
+package org.hibernate.test.mapelemformula;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Gavin King
+ */
+public class Group {
+ private String name;
+ private Map users = new HashMap();
+ Group() {}
+ public Group(String n) {
+ name = n;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public Map getUsers() {
+ return users;
+ }
+ public void setUsers(Map users) {
+ this.users = users;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapelemformula/Group.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapelemformula/MapElementFormulaTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapelemformula/MapElementFormulaTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapelemformula/MapElementFormulaTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,60 @@
+//$Id: MapElementFormulaTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.mapelemformula;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class MapElementFormulaTest extends FunctionalTestCase {
+
+ public MapElementFormulaTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "mapelemformula/UserGroup.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( MapElementFormulaTest.class );
+ }
+
+ public void testManyToManyFormula() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ User gavin = new User("gavin", "secret");
+ User turin = new User("turin", "tiger");
+ Group g = new Group("users");
+ g.getUsers().put("Gavin", gavin);
+ g.getUsers().put("Turin", turin);
+ s.persist(g);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ g = (Group) s.get(Group.class, "users");
+ assertEquals( g.getUsers().size(), 2 );
+ g.getUsers().remove("Turin");
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ g = (Group) s.get(Group.class, "users");
+ assertEquals( g.getUsers().size(), 1 );
+ s.delete(g);
+ s.delete( g.getUsers().get("Gavin") );
+ s.delete( s.get(User.class, "turin") );
+ t.commit();
+ s.close();
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapelemformula/MapElementFormulaTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapelemformula/User.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapelemformula/User.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapelemformula/User.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,28 @@
+//$Id: User.java 5686 2005-02-12 07:27:32Z steveebersole $
+package org.hibernate.test.mapelemformula;
+
+
+/**
+ * @author Gavin King
+ */
+public class User {
+ private String name;
+ private String password;
+ User() {}
+ public User(String n, String pw) {
+ name=n;
+ password = pw;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public String getPassword() {
+ return password;
+ }
+ public void setPassword(String password) {
+ this.password = password;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapelemformula/User.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapelemformula/UserGroup.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapelemformula/UserGroup.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapelemformula/UserGroup.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -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">
+
+<!--
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.mapelemformula">
+
+ <class name="Group" table="groups">
+ <id name="name"/>
+ <map name="users" cascade="persist" table="group_user">
+ <key column="groupName"/>
+ <map-key column="personName" type="string"/>
+ <many-to-many formula="lower(personName)" class="User" fetch="select"/>
+ </map>
+ </class>
+
+ <class name="User" table="users">
+ <id name="name"/>
+ <property name="password"/>
+ </class>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapelemformula/UserGroup.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapping/PersistentClassVisitorTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapping/PersistentClassVisitorTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapping/PersistentClassVisitorTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,78 @@
+/*
+* Created on 06-Dec-2004
+*
+*/
+package org.hibernate.test.mapping;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.hibernate.mapping.JoinedSubclass;
+import org.hibernate.mapping.PersistentClassVisitor;
+import org.hibernate.mapping.RootClass;
+import org.hibernate.mapping.SingleTableSubclass;
+import org.hibernate.mapping.Subclass;
+import org.hibernate.mapping.UnionSubclass;
+import org.hibernate.junit.UnitTestCase;
+
+/**
+ * @author max
+ *
+ */
+public class PersistentClassVisitorTest extends UnitTestCase {
+
+ public PersistentClassVisitorTest(String string) {
+ super( string );
+ }
+
+ static public class PersistentClassVisitorValidator implements PersistentClassVisitor {
+
+ private Object validate(Class expectedClass, Object visitee) {
+ if (!visitee.getClass().getName().equals(expectedClass.getName())) {
+ throw new IllegalStateException(visitee.getClass().getName()
+ + " did not call proper accept method. Was "
+ + expectedClass.getName());
+ }
+ return null;
+ }
+
+ public Object accept(RootClass class1) {
+ return validate(RootClass.class, class1);
+ }
+
+ public Object accept(UnionSubclass subclass) {
+ return validate(UnionSubclass.class, subclass);
+ }
+
+ public Object accept(SingleTableSubclass subclass) {
+ return validate(SingleTableSubclass.class, subclass);
+ }
+
+ public Object accept(JoinedSubclass subclass) {
+ return validate(JoinedSubclass.class, subclass);
+ }
+
+ public Object accept(Subclass subclass) {
+ return validate(Subclass.class, subclass);
+ }
+
+
+ };
+
+ public void testProperCallbacks() {
+
+ PersistentClassVisitorValidator vv = new PersistentClassVisitorValidator();
+
+ new RootClass().accept(vv);
+ new Subclass(new RootClass()).accept(vv);
+ new JoinedSubclass(new RootClass()).accept(vv);
+ new SingleTableSubclass(new RootClass()).accept(vv);
+ new UnionSubclass(new RootClass()).accept(vv);
+
+ }
+
+ public static Test suite() {
+ return new TestSuite(PersistentClassVisitorTest.class);
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapping/ValueVisitorTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapping/ValueVisitorTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mapping/ValueVisitorTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,199 @@
+/*
+ * Created on 06-Dec-2004
+ *
+ */
+package org.hibernate.test.mapping;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.hibernate.mapping.Any;
+import org.hibernate.mapping.Array;
+import org.hibernate.mapping.Bag;
+import org.hibernate.mapping.Component;
+import org.hibernate.mapping.DependantValue;
+import org.hibernate.mapping.IdentifierBag;
+import org.hibernate.mapping.List;
+import org.hibernate.mapping.ManyToOne;
+import org.hibernate.mapping.Map;
+import org.hibernate.mapping.OneToMany;
+import org.hibernate.mapping.OneToOne;
+import org.hibernate.mapping.PrimitiveArray;
+import org.hibernate.mapping.RootClass;
+import org.hibernate.mapping.Set;
+import org.hibernate.mapping.SimpleValue;
+import org.hibernate.mapping.Table;
+import org.hibernate.mapping.ValueVisitor;
+import org.hibernate.junit.UnitTestCase;
+
+/**
+ * @author max
+ *
+ */
+public class ValueVisitorTest extends UnitTestCase {
+
+ public ValueVisitorTest(String string) {
+ super( string );
+ }
+
+ static public class ValueVisitorValidator implements ValueVisitor {
+
+ /* (non-Javadoc)
+ * @see org.hibernate.mapping.ValueVisitor#accept(org.hibernate.mapping.PrimitiveArray)
+ */
+ public Object accept(PrimitiveArray primitiveArray) {
+ return validate(PrimitiveArray.class,primitiveArray);
+ }
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.hibernate.mapping.ValueVisitor#accept(org.hibernate.mapping.Bag)
+ */
+ public Object accept(Bag bag) {
+ return validate(Bag.class, bag);
+ }
+
+ /* (non-Javadoc)
+ * @see org.hibernate.mapping.ValueVisitor#accept(org.hibernate.mapping.DependantValue)
+ */
+ public Object accept(DependantValue value) {
+ return validate(DependantValue.class, value);
+ }
+ /**
+ * @param expectedClass
+ * @param visitee
+ */
+ private Object validate(Class expectedClass, Object visitee) {
+ if (!visitee.getClass().getName().equals(expectedClass.getName())) {
+ throw new IllegalStateException(visitee.getClass().getName()
+ + " did not call proper accept method. Was "
+ + expectedClass.getName());
+ }
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.hibernate.mapping.ValueVisitor#accept(org.hibernate.mapping.IdentifierBag)
+ */
+ public Object accept(IdentifierBag bag) {
+ return validate(IdentifierBag.class, bag);
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.hibernate.mapping.ValueVisitor#accept(org.hibernate.mapping.List)
+ */
+ public Object accept(List list) {
+ return validate(List.class, list);
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.hibernate.mapping.ValueVisitor#accept(org.hibernate.mapping.Map)
+ */
+ public Object accept(Map map) {
+ return validate(Map.class, map);
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.hibernate.mapping.ValueVisitor#accept(org.hibernate.mapping.Array)
+ */
+ public Object accept(Array list) {
+ return validate(Array.class, list);
+ }
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.hibernate.mapping.ValueVisitor#accept(org.hibernate.mapping.OneToMany)
+ */
+ public Object accept(OneToMany many) {
+ return validate(OneToMany.class, many);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.hibernate.mapping.ValueVisitor#accept(org.hibernate.mapping.Set)
+ */
+ public Object accept(Set set) {
+ return validate(Set.class, set);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.hibernate.mapping.ValueVisitor#accept(org.hibernate.mapping.Any)
+ */
+ public Object accept(Any any) {
+ return validate(Any.class, any);
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.hibernate.mapping.ValueVisitor#accept(org.hibernate.mapping.SimpleValue)
+ */
+ public Object accept(SimpleValue value) {
+ return validate(SimpleValue.class, value);
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.hibernate.mapping.ValueVisitor#accept(org.hibernate.mapping.Component)
+ */
+ public Object accept(Component component) {
+ return validate(Component.class, component);
+ }
+
+ /* (non-Javadoc)
+ * @see org.hibernate.mapping.ValueVisitor#accept(org.hibernate.mapping.ManyToOne)
+ */
+ public Object accept(ManyToOne mto) {
+ return validate(ManyToOne.class, mto);
+ }
+
+ /* (non-Javadoc)
+ * @see org.hibernate.mapping.ValueVisitor#accept(org.hibernate.mapping.OneToOne)
+ */
+ public Object accept(OneToOne oto) {
+ return validate(OneToOne.class, oto);
+ }
+
+ };
+
+ public void testProperCallbacks() {
+
+ ValueVisitor vv = new ValueVisitorValidator();
+
+ new Any(new Table()).accept(vv);
+ new Array(new RootClass()).accept(vv);
+ new Bag(new RootClass()).accept(vv);
+ new Component(new RootClass()).accept(vv);
+ new DependantValue(null,null).accept(vv);
+ new IdentifierBag(null).accept(vv);
+ new List(null).accept(vv);
+ new ManyToOne(null).accept(vv);
+ new Map(null).accept(vv);
+ new OneToMany(null).accept(vv);
+ new OneToOne(null, new RootClass() ).accept(vv);
+ new PrimitiveArray(null).accept(vv);
+ new Set(null).accept(vv);
+ new SimpleValue().accept(vv);
+
+
+ }
+
+ public static Test suite() {
+ return new TestSuite(ValueVisitorTest.class);
+ }
+
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mappingexception/InvalidMapping.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mappingexception/InvalidMapping.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mappingexception/InvalidMapping.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,4 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping SYSTEM "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
+
+This File Intentionally Left Blank
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mappingexception/InvalidMapping.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mappingexception/InvalidMapping.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mappingexception/InvalidMapping.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,5 @@
+package org.hibernate.test.mappingexception;
+
+public class InvalidMapping {
+// This Class Intentionally Left Blank
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mappingexception/MappingExceptionTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mappingexception/MappingExceptionTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mappingexception/MappingExceptionTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,253 @@
+// $Id: SQLExceptionConversionTest.java 6847 2005-05-21 15:46:41Z oneovthafew $
+package org.hibernate.test.mappingexception;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.hibernate.DuplicateMappingException;
+import org.hibernate.Hibernate;
+import org.hibernate.InvalidMappingException;
+import org.hibernate.MappingException;
+import org.hibernate.MappingNotFoundException;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.junit.UnitTestCase;
+import org.hibernate.util.ConfigHelper;
+
+/**
+ * Test for various mapping exceptions thrown when mappings are not found or invalid.
+ *
+ * @author Max Rydahl Andersen
+ */
+public class MappingExceptionTest extends UnitTestCase {
+
+ public MappingExceptionTest(String name) {
+ super( name );
+ }
+
+ public static Test suite() {
+ return new TestSuite( MappingExceptionTest.class );
+ }
+
+ public void testNotFound() throws MappingException, MalformedURLException {
+ Configuration cfg = new Configuration();
+
+ try {
+ cfg.addCacheableFile( "completelybogus.hbm.xml" );
+ fail();
+ }
+ catch ( MappingNotFoundException e ) {
+ assertEquals( e.getType(), "file" );
+ assertEquals( e.getPath(), "completelybogus.hbm.xml" );
+ }
+
+ try {
+ cfg.addCacheableFile( new File( "completelybogus.hbm.xml" ) );
+ fail();
+ }
+ catch ( MappingNotFoundException e ) {
+ assertEquals( e.getType(), "file" );
+ assertEquals( e.getPath(), "completelybogus.hbm.xml" );
+ }
+
+ try {
+ cfg.addClass( Hibernate.class ); // TODO: String.class result in npe, because no classloader exists for it
+ fail();
+ }
+ catch ( MappingNotFoundException inv ) {
+ assertEquals( inv.getType(), "resource" );
+ assertEquals( inv.getPath(), "org/hibernate/Hibernate.hbm.xml" );
+ }
+
+ try {
+ cfg.addFile( "completelybogus.hbm.xml" );
+ fail();
+ }
+ catch ( MappingNotFoundException e ) {
+ assertEquals( e.getType(), "file" );
+ assertEquals( e.getPath(), "completelybogus.hbm.xml" );
+ }
+
+ try {
+ cfg.addFile( new File( "completelybogus.hbm.xml" ) );
+ fail();
+ }
+ catch ( MappingNotFoundException inv ) {
+ assertEquals( inv.getType(), "file" );
+ assertEquals( inv.getPath(), "completelybogus.hbm.xml" );
+ }
+
+ try {
+ cfg.addInputStream( new ByteArrayInputStream( new byte[0] ) );
+ fail();
+ }
+ catch ( InvalidMappingException inv ) {
+ assertEquals( inv.getType(), "input stream" );
+ assertEquals( inv.getPath(), null );
+ }
+
+ try {
+ cfg.addResource( "nothere" );
+ fail();
+ }
+ catch ( MappingNotFoundException inv ) {
+ assertEquals( inv.getType(), "resource" );
+ assertEquals( inv.getPath(), "nothere" );
+ }
+
+ try {
+ cfg.addResource( "nothere", getClass().getClassLoader() );
+ fail();
+ }
+ catch ( MappingNotFoundException inv ) {
+ assertEquals( inv.getType(), "resource" );
+ assertEquals( inv.getPath(), "nothere" );
+ }
+
+ try {
+ cfg.addURL( new URL( "file://nothere" ) );
+ fail();
+ }
+ catch ( InvalidMappingException inv ) {
+ assertEquals( inv.getType(), "URL" );
+ assertEquals( inv.getPath(), "file://nothere" );
+ }
+ }
+
+ public void testDuplicateMapping() {
+ String resourceName = "org/hibernate/test/mappingexception/User.hbm.xml";
+ Configuration cfg = new Configuration();
+ cfg.addResource( resourceName );
+ try {
+ cfg.addResource( resourceName );
+ fail();
+ }
+ catch ( InvalidMappingException inv ) {
+ assertEquals( inv.getType(), "resource" );
+ assertEquals( inv.getPath(), resourceName );
+ assertClassAssignability( inv.getCause().getClass(), DuplicateMappingException.class );
+ }
+ }
+
+ public void testInvalidMapping() throws MappingException, IOException {
+ String resourceName = "org/hibernate/test/mappingexception/InvalidMapping.hbm.xml";
+ File file = File.createTempFile( "TempInvalidMapping", ".hbm.xml" );
+ file.deleteOnExit();
+ copy( ConfigHelper.getConfigStream( resourceName ), file );
+
+ Configuration cfg = new Configuration();
+ try {
+ cfg.addCacheableFile( file.getAbsolutePath() );
+ fail();
+ }
+ catch ( InvalidMappingException inv ) {
+ assertEquals( inv.getType(), "file" );
+ assertNotNull( inv.getPath() );
+ assertTrue( inv.getPath().endsWith( ".hbm.xml" ) );
+ assertTrue( !( inv.getCause() instanceof MappingNotFoundException ) );
+ }
+
+ try {
+ cfg.addCacheableFile( file );
+ fail();
+ }
+ catch ( InvalidMappingException inv ) {
+ assertEquals( inv.getType(), "file" );
+ assertNotNull( inv.getPath() );
+ assertTrue( inv.getPath().endsWith( ".hbm.xml" ) );
+ assertTrue( !( inv.getCause() instanceof MappingNotFoundException ) );
+ }
+
+ try {
+ cfg.addClass( InvalidMapping.class );
+ fail();
+ }
+ catch ( InvalidMappingException inv ) {
+ assertEquals( inv.getType(), "resource" );
+ assertEquals( inv.getPath(), "org/hibernate/test/mappingexception/InvalidMapping.hbm.xml" );
+ assertTrue( !( inv.getCause() instanceof MappingNotFoundException ) );
+ }
+
+ try {
+ cfg.addFile( file.getAbsolutePath() );
+ fail();
+ }
+ catch ( InvalidMappingException inv ) {
+ assertEquals( inv.getType(), "file" );
+ assertEquals( inv.getPath(), file.getPath() );
+ assertTrue( !( inv.getCause() instanceof MappingNotFoundException ) );
+ }
+
+ try {
+ cfg.addFile( file );
+ fail();
+ }
+ catch ( InvalidMappingException inv ) {
+ assertEquals( inv.getType(), "file" );
+ assertEquals( inv.getPath(), file.getPath() );
+ assertTrue( !( inv.getCause() instanceof MappingNotFoundException ) );
+ }
+
+
+ try {
+ cfg.addInputStream( ConfigHelper.getResourceAsStream( resourceName ) );
+ fail();
+ }
+ catch ( InvalidMappingException inv ) {
+ assertEquals( inv.getType(), "input stream" );
+ assertEquals( inv.getPath(), null );
+ assertTrue( !( inv.getCause() instanceof MappingNotFoundException ) );
+ }
+
+ try {
+ cfg.addResource( resourceName );
+ fail();
+ }
+ catch ( InvalidMappingException inv ) {
+ assertEquals( inv.getType(), "resource" );
+ assertEquals( inv.getPath(), resourceName );
+ assertTrue( !( inv.getCause() instanceof MappingNotFoundException ) );
+ }
+
+ try {
+ cfg.addResource( resourceName, getClass().getClassLoader() );
+ fail();
+ }
+ catch ( InvalidMappingException inv ) {
+ assertEquals( inv.getType(), "resource" );
+ assertEquals( inv.getPath(), resourceName );
+ assertTrue( !( inv.getCause() instanceof MappingNotFoundException ) );
+ }
+
+ try {
+ cfg.addURL( ConfigHelper.findAsResource( resourceName ) );
+ fail();
+ }
+ catch ( InvalidMappingException inv ) {
+ assertEquals( inv.getType(), "URL" );
+ assertTrue( inv.getPath().endsWith( "InvalidMapping.hbm.xml" ) );
+ assertTrue( !( inv.getCause() instanceof MappingNotFoundException ) );
+ }
+ }
+
+ void copy(InputStream in, File dst) throws IOException {
+ OutputStream out = new FileOutputStream( dst );
+
+ // Transfer bytes from in to out
+ byte[] buf = new byte[1024];
+ int len;
+ while ( ( len = in.read( buf ) ) > 0 ) {
+ out.write( buf, 0, len );
+ }
+ in.close();
+ out.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mappingexception/User.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mappingexception/User.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mappingexception/User.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping SYSTEM "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
+
+<hibernate-mapping package="org.hibernate.test.exception" >
+ <class name="User" table="T_USER" >
+ <id name="id" unsaved-value="null" column="user_id" >
+ <generator class="native"/>
+ </id>
+ <property name="username" type="string" column="user_name" />
+ </class>
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mappingexception/User.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mappingexception/User.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mappingexception/User.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,31 @@
+// $Id: User.java 4746 2004-11-11 20:57:28Z steveebersole $
+package org.hibernate.test.mappingexception;
+
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ *
+ *
+ * @author Max Rydahl Andersen
+ */
+public class User {
+ private Long id;
+ private String username;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mixed/Document.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mixed/Document.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mixed/Document.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,83 @@
+//$Id: Document.java 8670 2005-11-25 17:36:29Z epbernard $
+
+package org.hibernate.test.mixed;
+
+
+import java.sql.Blob;
+import java.util.Calendar;
+
+
+/**
+ * @author Gavin King
+ */
+
+public class Document extends Item {
+
+ private Blob content;
+
+ private Calendar modified;
+
+ private Calendar created;
+
+ /**
+ * @return Returns the created.
+ */
+
+ public Calendar getCreated() {
+
+ return created;
+
+ }
+
+ /**
+ * @param created The created to set.
+ */
+
+ public void setCreated(Calendar created) {
+
+ this.created = created;
+
+ }
+
+ /**
+ * @return Returns the modified.
+ */
+
+ public Calendar getModified() {
+
+ return modified;
+
+ }
+
+ /**
+ * @param modified The modified to set.
+ */
+
+ public void setModified(Calendar modified) {
+
+ this.modified = modified;
+
+ }
+
+ /**
+ * @return Returns the content.
+ */
+
+ public Blob getContent() {
+
+ return content;
+
+ }
+
+ /**
+ * @param content The content to set.
+ */
+
+ public void setContent(Blob content) {
+
+ this.content = content;
+
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mixed/Document.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mixed/DocumentInterceptor.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mixed/DocumentInterceptor.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mixed/DocumentInterceptor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,116 @@
+//$Id: DocumentInterceptor.java 8670 2005-11-25 17:36:29Z epbernard $
+package org.hibernate.test.mixed;
+
+import java.io.Serializable;
+import java.util.Calendar;
+import java.util.Iterator;
+
+import org.hibernate.CallbackException;
+import org.hibernate.EntityMode;
+import org.hibernate.Interceptor;
+import org.hibernate.Transaction;
+import org.hibernate.type.Type;
+
+/**
+ * @author Gavin King
+ */
+public class DocumentInterceptor implements Interceptor {
+
+
+ public boolean onLoad(
+ Object entity, Serializable id, Object[] state,
+ String[] propertyNames, Type[] types
+ ) throws CallbackException {
+ return false;
+ }
+
+ public boolean onFlushDirty(
+ Object entity, Serializable id,
+ Object[] currentState, Object[] previousState,
+ String[] propertyNames, Type[] types
+ ) throws CallbackException {
+ if ( entity instanceof Document ) {
+ currentState[3] = Calendar.getInstance();
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ public boolean onSave(
+ Object entity, Serializable id, Object[] state,
+ String[] propertyNames, Type[] types
+ ) throws CallbackException {
+ if ( entity instanceof Document ) {
+ state[4] = state[3] = Calendar.getInstance();
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ public void onDelete(
+ Object entity, Serializable id, Object[] state,
+ String[] propertyNames, Type[] types
+ ) throws CallbackException {
+
+ }
+
+ public void preFlush(Iterator entities) throws CallbackException {
+
+ }
+
+ public void postFlush(Iterator entities) throws CallbackException {
+
+ }
+
+ public Boolean isTransient(Object entity) {
+ return null;
+ }
+
+ public int[] findDirty(
+ Object entity, Serializable id,
+ Object[] currentState, Object[] previousState,
+ String[] propertyNames, Type[] types
+ ) {
+ return null;
+ }
+
+ public Object instantiate(String entityName, EntityMode entityMode, Serializable id) throws CallbackException {
+ return null;
+ }
+
+ public String getEntityName(Object object) throws CallbackException {
+ return null;
+ }
+
+ public Object getEntity(String entityName, Serializable id)
+ throws CallbackException {
+ return null;
+ }
+
+ public void afterTransactionBegin(Transaction tx) {
+ }
+
+ public void afterTransactionCompletion(Transaction tx) {
+ }
+
+ public void beforeTransactionCompletion(Transaction tx) {
+ }
+
+ public String onPrepareStatement(String sql) {
+ return sql;
+ }
+
+ public void onCollectionRecreate(Object collection, Serializable key) throws CallbackException {
+ }
+
+ public void onCollectionRemove(Object collection, Serializable key) throws CallbackException {
+ }
+
+ public void onCollectionUpdate(Object collection, Serializable key) throws CallbackException {
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mixed/DocumentInterceptor.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mixed/Folder.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mixed/Folder.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mixed/Folder.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,13 @@
+//$Id: Folder.java 8670 2005-11-25 17:36:29Z epbernard $
+
+package org.hibernate.test.mixed;
+
+
+/**
+ * @author Gavin King
+ */
+
+public class Folder extends Item {
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mixed/Folder.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mixed/Item.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mixed/Item.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mixed/Item.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,135 @@
+<?xml version="1.0"?>
+
+<!DOCTYPE hibernate-mapping PUBLIC
+
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+
+<!--
+
+
+
+ This mapping demonstrates a mixed inheritance strategy,
+
+ with one subclass acting like a joined-subclass via
+
+ use of a <join>. Also notice the fields which are initialized
+
+ by an interceptor.
+
+
+
+-->
+
+
+<hibernate-mapping
+
+ package="org.hibernate.test.mixed">
+
+
+ <class name="Item"
+
+ table="Files"
+
+ discriminator-value="I">
+
+
+ <id name="id">
+
+ <generator class="native"/>
+
+ </id>
+
+
+ <discriminator type="character"
+
+ column="itemType"/>
+
+
+ <property name="name">
+
+ <column name="name"
+
+ not-null="true"
+
+ />
+ <!--unique-key="folder-parent"-->
+
+ </property>
+
+
+ <many-to-one name="parent"
+
+ class="Folder">
+
+ <column name="parentFolderId"
+
+ />
+ <!--unique-key="folder-parent"-->
+
+ </many-to-one>
+
+
+ <subclass name="Document"
+
+ discriminator-value="D"
+
+ lazy="true"
+
+ dynamic-update="true">
+
+
+ <property name="content"/>
+
+
+ <!-- managed by DocumentInterceptor -->
+
+ <property name="modified"/>
+
+ <property name="created"/>
+
+
+ <subclass name="SecureDocument"
+
+ discriminator-value="S"
+
+ dynamic-update="true"
+
+ lazy="true">
+
+
+ <join table="SecureDocument">
+
+
+ <key column="documentId"/>
+
+
+ <property name="permissionBits"
+
+ not-null="true"/>
+
+ <property name="owner"
+
+ not-null="true"/>
+
+
+ </join>
+
+
+ </subclass>
+
+
+ </subclass>
+
+
+ <subclass name="Folder"
+
+ discriminator-value="F"/>
+
+
+ </class>
+
+
+</hibernate-mapping>
\ No newline at end of file
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mixed/Item.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mixed/Item.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mixed/Item.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mixed/Item.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,79 @@
+//$Id: Item.java 8670 2005-11-25 17:36:29Z epbernard $
+
+package org.hibernate.test.mixed;
+
+
+/**
+ * @author Gavin King
+ */
+
+public abstract class Item {
+
+ private Long id;
+
+ private String name;
+
+ private Folder parent;
+
+ /**
+ * @return Returns the parent.
+ */
+
+ public Folder getParent() {
+
+ return parent;
+
+ }
+
+ /**
+ * @param parent The parent to set.
+ */
+
+ public void setParent(Folder parent) {
+
+ this.parent = parent;
+
+ }
+
+ /**
+ * @return Returns the id.
+ */
+
+ public Long getId() {
+
+ return id;
+
+ }
+
+ /**
+ * @param id The id to set.
+ */
+
+ public void setId(Long id) {
+
+ this.id = id;
+
+ }
+
+ /**
+ * @return Returns the name.
+ */
+
+ public String getName() {
+
+ return name;
+
+ }
+
+ /**
+ * @param name The name to set.
+ */
+
+ public void setName(String name) {
+
+ this.name = name;
+
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mixed/Item.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mixed/MixedTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mixed/MixedTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mixed/MixedTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,107 @@
+//$Id: MixedTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.mixed;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.dialect.PostgreSQLDialect;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class MixedTest extends FunctionalTestCase {
+
+ public MixedTest(String str) {
+ super( str );
+ }
+
+ public String[] getMappings() {
+ return new String[]{"mixed/Item.hbm.xml"};
+ }
+
+ public String getCacheConcurrencyStrategy() {
+ return null;
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( MixedTest.class );
+ }
+
+ public void testMixedInheritance() {
+ Session s = openSession( new DocumentInterceptor() );
+ Transaction t = s.beginTransaction();
+ Folder f = new Folder();
+ f.setName( "/" );
+ s.save( f );
+
+ Document d = new Document();
+ d.setName( "Hibernate in Action" );
+ d.setContent( Hibernate.createBlob( "blah blah blah".getBytes() ) );
+ d.setParent( f );
+ Long did = (Long) s.save( d );
+
+ SecureDocument d2 = new SecureDocument();
+ d2.setName( "Secret" );
+ d2.setContent( Hibernate.createBlob( "wxyz wxyz".getBytes() ) );
+ d2.setPermissionBits( (byte) 664 );
+ d2.setOwner( "gavin" );
+ d2.setParent( f );
+ Long d2id = (Long) s.save( d2 );
+
+ t.commit();
+ s.close();
+
+ if ( getDialect() instanceof PostgreSQLDialect ) return;
+
+ s = openSession( new DocumentInterceptor() );
+ t = s.beginTransaction();
+ Item id = (Item) s.load( Item.class, did );
+ assertEquals( did, id.getId() );
+ assertEquals( "Hibernate in Action", id.getName() );
+ assertEquals( "/", id.getParent().getName() );
+
+ Item id2 = (Item) s.load( Item.class, d2id );
+ assertEquals( d2id, id2.getId() );
+ assertEquals( "Secret", id2.getName() );
+ assertEquals( "/", id2.getParent().getName() );
+
+ id.setName( "HiA" );
+
+ d2 = (SecureDocument) s.load( SecureDocument.class, d2id );
+ d2.setOwner( "max" );
+
+ s.flush();
+
+ s.clear();
+
+ d = (Document) s.load( Document.class, did );
+ assertEquals( did, d.getId() );
+ assertEquals( "HiA", d.getName() );
+ assertNotNull( d.getContent() );
+ assertEquals( "/", d.getParent().getName() );
+ assertNotNull( d.getCreated() );
+ assertNotNull( d.getModified() );
+
+ d2 = (SecureDocument) s.load( SecureDocument.class, d2id );
+ assertEquals( d2id, d2.getId() );
+ assertEquals( "Secret", d2.getName() );
+ assertNotNull( d2.getContent() );
+ assertEquals( "max", d2.getOwner() );
+ assertEquals( "/", d2.getParent().getName() );
+ assertEquals( (byte) 664, d2.getPermissionBits() );
+ assertNotNull( d2.getCreated() );
+ assertNotNull( d2.getModified() );
+
+ s.delete( d.getParent() );
+ s.delete( d );
+ s.delete( d2 );
+
+ t.commit();
+ s.close();
+ }
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mixed/MixedTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mixed/SecureDocument.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mixed/SecureDocument.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mixed/SecureDocument.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,57 @@
+//$Id: SecureDocument.java 8670 2005-11-25 17:36:29Z epbernard $
+
+package org.hibernate.test.mixed;
+
+
+/**
+ * @author Gavin King
+ */
+
+public class SecureDocument extends Document {
+
+ private byte permissionBits;
+
+ private String owner;
+
+ /**
+ * @return Returns the owner.
+ */
+
+ public String getOwner() {
+
+ return owner;
+
+ }
+
+ /**
+ * @param owner The owner to set.
+ */
+
+ public void setOwner(String owner) {
+
+ this.owner = owner;
+
+ }
+
+ /**
+ * @return Returns the permissionBits.
+ */
+
+ public byte getPermissionBits() {
+
+ return permissionBits;
+
+ }
+
+ /**
+ * @param permissionBits The permissionBits to set.
+ */
+
+ public void setPermissionBits(byte permissionBits) {
+
+ this.permissionBits = permissionBits;
+
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/mixed/SecureDocument.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/naturalid/NaturalIdTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/naturalid/NaturalIdTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/naturalid/NaturalIdTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,248 @@
+//$Id: NaturalIdTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.naturalid;
+
+import java.lang.reflect.Field;
+
+import junit.framework.Test;
+
+import org.hibernate.HibernateException;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.criterion.Restrictions;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class NaturalIdTest extends FunctionalTestCase {
+
+ public NaturalIdTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "naturalid/User.hbm.xml" };
+ }
+
+ public void configure(Configuration cfg) {
+ cfg.setProperty(Environment.USE_SECOND_LEVEL_CACHE, "true");
+ cfg.setProperty(Environment.USE_QUERY_CACHE, "true");
+ cfg.setProperty(Environment.GENERATE_STATISTICS, "true");
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( NaturalIdTest.class );
+ }
+
+ public void testNaturalIdCheck() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ User u = new User("gavin", "hb", "secret");
+ s.persist(u);
+ Field name = u.getClass().getDeclaredField("name");
+ name.setAccessible(true);
+ name.set(u, "Gavin");
+ try {
+ s.flush();
+ fail();
+ }
+ catch (HibernateException he) {}
+ name.set(u, "gavin");
+ s.delete(u);
+ t.commit();
+ s.close();
+ }
+
+ public void testNonexistentNaturalIdCache() {
+ getSessions().getStatistics().clear();
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ Object nullUser = s.createCriteria(User.class)
+ .add( Restrictions.naturalId()
+ .set("name", "gavin")
+ .set("org", "hb")
+ )
+ .setCacheable(true)
+ .uniqueResult();
+
+ assertNull(nullUser);
+
+ t.commit();
+ s.close();
+
+ assertEquals( getSessions().getStatistics().getQueryExecutionCount(), 1 );
+ assertEquals( getSessions().getStatistics().getQueryCacheHitCount(), 0 );
+ assertEquals( getSessions().getStatistics().getQueryCachePutCount(), 0 );
+
+ s = openSession();
+ t = s.beginTransaction();
+
+ User u = new User("gavin", "hb", "secret");
+ s.persist(u);
+
+ t.commit();
+ s.close();
+
+ getSessions().getStatistics().clear();
+
+ s = openSession();
+ t = s.beginTransaction();
+
+ u = (User) s.createCriteria(User.class)
+ .add( Restrictions.naturalId()
+ .set("name", "gavin")
+ .set("org", "hb")
+ )
+ .setCacheable(true)
+ .uniqueResult();
+
+ assertNotNull(u);
+
+ t.commit();
+ s.close();
+
+ assertEquals( getSessions().getStatistics().getQueryExecutionCount(), 1 );
+ assertEquals( getSessions().getStatistics().getQueryCacheHitCount(), 0 );
+ assertEquals( getSessions().getStatistics().getQueryCachePutCount(), 1 );
+
+ getSessions().getStatistics().clear();
+
+ s = openSession();
+ t = s.beginTransaction();
+
+ u = (User) s.createCriteria(User.class)
+ .add( Restrictions.naturalId()
+ .set("name", "gavin")
+ .set("org", "hb")
+ ).setCacheable(true)
+ .uniqueResult();
+
+ s.delete(u);
+
+ t.commit();
+ s.close();
+
+ assertEquals( getSessions().getStatistics().getQueryExecutionCount(), 0 );
+ assertEquals( getSessions().getStatistics().getQueryCacheHitCount(), 1 );
+
+ getSessions().getStatistics().clear();
+
+ s = openSession();
+ t = s.beginTransaction();
+
+ nullUser = s.createCriteria(User.class)
+ .add( Restrictions.naturalId()
+ .set("name", "gavin")
+ .set("org", "hb")
+ )
+ .setCacheable(true)
+ .uniqueResult();
+
+ assertNull(nullUser);
+
+ t.commit();
+ s.close();
+
+ assertEquals( getSessions().getStatistics().getQueryExecutionCount(), 1 );
+ assertEquals( getSessions().getStatistics().getQueryCacheHitCount(), 0 );
+ assertEquals( getSessions().getStatistics().getQueryCachePutCount(), 0 );
+
+ }
+
+ public void testNaturalIdCache() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ User u = new User("gavin", "hb", "secret");
+ s.persist(u);
+
+ t.commit();
+ s.close();
+
+ getSessions().getStatistics().clear();
+
+ s = openSession();
+ t = s.beginTransaction();
+
+ u = (User) s.createCriteria(User.class)
+ .add( Restrictions.naturalId()
+ .set("name", "gavin")
+ .set("org", "hb")
+ )
+ .setCacheable(true)
+ .uniqueResult();
+
+ assertNotNull(u);
+
+ t.commit();
+ s.close();
+
+ assertEquals( getSessions().getStatistics().getQueryExecutionCount(), 1 );
+ assertEquals( getSessions().getStatistics().getQueryCacheHitCount(), 0 );
+ assertEquals( getSessions().getStatistics().getQueryCachePutCount(), 1 );
+
+ s = openSession();
+ t = s.beginTransaction();
+
+ User v = new User("xam", "hb", "foobar");
+ s.persist(v);
+
+ t.commit();
+ s.close();
+
+ getSessions().getStatistics().clear();
+
+ s = openSession();
+ t = s.beginTransaction();
+
+ u = (User) s.createCriteria(User.class)
+ .add( Restrictions.naturalId()
+ .set("name", "gavin")
+ .set("org", "hb")
+ ).setCacheable(true)
+ .uniqueResult();
+
+ assertNotNull(u);
+
+ t.commit();
+ s.close();
+
+ assertEquals( getSessions().getStatistics().getQueryExecutionCount(), 0 );
+ assertEquals( getSessions().getStatistics().getQueryCacheHitCount(), 1 );
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.createQuery("delete User").executeUpdate();
+ t.commit();
+ s.close();
+ }
+
+ public void testQuerying() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ User u = new User("emmanuel", "hb", "bh");
+ s.persist(u);
+
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+
+ u = (User) s.createQuery( "from User u where u.name = :name" )
+ .setParameter( "name", "emmanuel" ).uniqueResult();
+ assertEquals( "emmanuel", u.getName() );
+ s.delete( u );
+
+ t.commit();
+ s.close();
+ }
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/naturalid/NaturalIdTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/naturalid/User.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/naturalid/User.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/naturalid/User.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -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">
+
+<!--
+
+ This mapping illustrates use of <natural-id>.
+
+-->
+
+<hibernate-mapping
+ package="org.hibernate.test.naturalid"
+ default-access="field">
+
+ <class name="User" table="SystemUserInfo">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <natural-id>
+ <property name="name"/>
+ <property name="org"/>
+ </natural-id>
+ <property name="password"/>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/naturalid/User.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/naturalid/User.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/naturalid/User.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/naturalid/User.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,34 @@
+//$Id: User.java 6900 2005-05-25 01:24:22Z oneovthafew $
+package org.hibernate.test.naturalid;
+
+/**
+ * @author Gavin King
+ */
+public class User {
+
+ private Long id;
+ private String name;
+ private String org;
+ private String password;
+
+ User() {}
+
+ public User(String name, String org, String password) {
+ this.name = name;
+ this.org = org;
+ this.password = password;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public String getOrg() {
+ return org;
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/naturalid/User.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ondelete/Address.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ondelete/Address.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ondelete/Address.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,11 @@
+//$Id: Address.java 7119 2005-06-12 22:03:30Z oneovthafew $
+package org.hibernate.test.ondelete;
+
+/**
+ * @author Gavin King
+ */
+public class Address {
+ public String address;
+ public String zip;
+ public String country;
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ondelete/Address.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ondelete/Employee.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ondelete/Employee.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ondelete/Employee.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,49 @@
+//$Id: Employee.java 7119 2005-06-12 22:03:30Z oneovthafew $
+package org.hibernate.test.ondelete;
+
+import java.math.BigDecimal;
+
+/**
+ * @author Gavin King
+ */
+public class Employee extends Person {
+ private String title;
+ private BigDecimal salary;
+ private Employee manager;
+ /**
+ * @return Returns the title.
+ */
+ public String getTitle() {
+ return title;
+ }
+ /**
+ * @param title The title to set.
+ */
+ public void setTitle(String title) {
+ this.title = title;
+ }
+ /**
+ * @return Returns the manager.
+ */
+ public Employee getManager() {
+ return manager;
+ }
+ /**
+ * @param manager The manager to set.
+ */
+ public void setManager(Employee manager) {
+ this.manager = manager;
+ }
+ /**
+ * @return Returns the salary.
+ */
+ public BigDecimal getSalary() {
+ return salary;
+ }
+ /**
+ * @param salary The salary to set.
+ */
+ public void setSalary(BigDecimal salary) {
+ this.salary = salary;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ondelete/Employee.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ondelete/OnDeleteTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ondelete/OnDeleteTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ondelete/OnDeleteTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,94 @@
+//$Id: OnDeleteTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.ondelete;
+
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.dialect.MySQLDialect;
+import org.hibernate.dialect.MySQLInnoDBDialect;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.stat.Statistics;
+
+/**
+ * @author Gavin King
+ */
+public class OnDeleteTest extends FunctionalTestCase {
+
+ public OnDeleteTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "ondelete/Person.hbm.xml" };
+ }
+
+ public void configure(Configuration cfg) {
+ cfg.setProperty(Environment.GENERATE_STATISTICS, "true");
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( OnDeleteTest.class );
+ }
+
+ public void testJoinedSubclass() {
+ if ( ! supportsCircularCascadeDelete() ) {
+ return;
+ }
+
+ Statistics statistics = getSessions().getStatistics();
+ statistics.clear();
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ Salesperson mark = new Salesperson();
+ mark.setName("Mark");
+ mark.setTitle("internal sales");
+ mark.setSex('M');
+ mark.setAddress("buckhead");
+ mark.setZip("30305");
+ mark.setCountry("USA");
+
+ Person joe = new Person();
+ joe.setName("Joe");
+ joe.setAddress("San Francisco");
+ joe.setZip("XXXXX");
+ joe.setCountry("USA");
+ joe.setSex('M');
+ joe.setSalesperson(mark);
+ mark.getCustomers().add(joe);
+
+ s.save(mark);
+
+ t.commit();
+
+ assertEquals( statistics.getEntityInsertCount(), 2 );
+ assertEquals( statistics.getPrepareStatementCount(), 5 );
+
+ statistics.clear();
+
+ t = s.beginTransaction();
+ s.delete(mark);
+ t.commit();
+
+ assertEquals( statistics.getEntityDeleteCount(), 2 );
+ if ( !(getDialect() instanceof MySQLDialect) || (getDialect() instanceof MySQLInnoDBDialect) ) {
+ assertEquals( statistics.getPrepareStatementCount(), 1 );
+ }
+
+ t = s.beginTransaction();
+ List names = s.createQuery("select name from Person").list();
+ assertTrue( names.isEmpty() );
+ t.commit();
+
+ s.close();
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ondelete/OnDeleteTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ondelete/Person.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ondelete/Person.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ondelete/Person.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,67 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+ This mapping demonstrates the use of database-level
+ cascade delete.
+
+-->
+
+<hibernate-mapping
+ package="org.hibernate.test.ondelete"
+ default-access="field">
+
+ <class name="Person" table="ODPerson">
+
+ <id name="id"
+ column="person_id">
+ <generator class="increment"/>
+ </id>
+
+ <property name="name"
+ not-null="true"
+ length="80"/>
+ <property name="sex"
+ not-null="true"
+ update="false"/>
+
+ <component name="address">
+ <property name="address"/>
+ <property name="zip"/>
+ <property name="country"/>
+ </component>
+
+ <many-to-one name="salesperson"
+ column="salesperson_id"/>
+
+ <joined-subclass name="Employee" table="ODEmployee">
+ <key column="person_id"
+ on-delete="cascade"/>
+ <property name="title"
+ not-null="true"
+ length="20"/>
+ <property name="salary"
+ length="0"/>
+ <many-to-one name="manager"/>
+
+ <joined-subclass name="Salesperson" table="ODSalesperson">
+ <key column="employee_id"
+ on-delete="cascade"/>
+ <set name="customers"
+ inverse="true"
+ cascade="all">
+ <key column="salesperson_id"
+ on-delete="cascade"/>
+ <one-to-many class="Person"/>
+ </set>
+ </joined-subclass>
+
+ </joined-subclass>
+
+ </class>
+
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ondelete/Person.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ondelete/Person.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ondelete/Person.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ondelete/Person.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,79 @@
+//$Id: Person.java 7119 2005-06-12 22:03:30Z oneovthafew $
+package org.hibernate.test.ondelete;
+
+
+/**
+ * @author Gavin King
+ */
+public class Person {
+ private long id;
+ private String name;
+ private char sex;
+ private Address address = new Address();
+ private Salesperson salesperson;
+ /**
+ * @return Returns the address.
+ */
+ public Address getAddress() {
+ return address;
+ }
+
+ public void setAddress(String string) {
+ this.address.address = string;
+ }
+
+ public void setZip(String string) {
+ this.address.zip = string;
+ }
+
+ public void setCountry(String string) {
+ this.address.country = string;
+ }
+
+
+ /**
+ * @return Returns the sex.
+ */
+ public char getSex() {
+ return sex;
+ }
+ /**
+ * @param sex The sex to set.
+ */
+ public void setSex(char sex) {
+ this.sex = sex;
+ }
+ /**
+ * @return Returns the id.
+ */
+ public long getId() {
+ return id;
+ }
+ /**
+ * @param id The id to set.
+ */
+ public void setId(long id) {
+ this.id = id;
+ }
+ /**
+ * @return Returns the identity.
+ */
+ public String getName() {
+ return name;
+ }
+ /**
+ * @param identity The identity to set.
+ */
+ public void setName(String identity) {
+ this.name = identity;
+ }
+
+ public Salesperson getSalesperson() {
+ return salesperson;
+ }
+
+ public void setSalesperson(Salesperson salesperson) {
+ this.salesperson = salesperson;
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ondelete/Person.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ondelete/Salesperson.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ondelete/Salesperson.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ondelete/Salesperson.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,18 @@
+//$Id: Salesperson.java 7119 2005-06-12 22:03:30Z oneovthafew $
+package org.hibernate.test.ondelete;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class Salesperson extends Employee {
+ private Set customers = new HashSet();
+
+ public Set getCustomers() {
+ return customers;
+ }
+
+ public void setCustomers(Set customers) {
+ this.customers = customers;
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ondelete/Salesperson.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetomany/Child.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetomany/Child.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetomany/Child.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,47 @@
+//$Id: Child.java 4378 2004-08-19 10:12:11Z oneovthafew $
+package org.hibernate.test.onetomany;
+
+/**
+ * @author gavin
+ */
+public class Child {
+ private Long id;
+ private String name;
+ private Parent parent;
+ /**
+ * @return Returns the id.
+ */
+ public Long getId() {
+ return id;
+ }
+ /**
+ * @param id The id to set.
+ */
+ public void setId(Long id) {
+ this.id = id;
+ }
+ /**
+ * @return Returns the name.
+ */
+ public String getName() {
+ return name;
+ }
+ /**
+ * @param name The name to set.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+ /**
+ * @return Returns the parent.
+ */
+ public Parent getParent() {
+ return parent;
+ }
+ /**
+ * @param parent The parent to set.
+ */
+ public void setParent(Parent parent) {
+ this.parent = parent;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetomany/Child.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetomany/OneToManyTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetomany/OneToManyTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetomany/OneToManyTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,94 @@
+//$Id: OneToManyTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.onetomany;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class OneToManyTest extends FunctionalTestCase {
+
+ public OneToManyTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "onetomany/Parent.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( OneToManyTest.class );
+ }
+
+ public void testOneToManyLinkTable() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Child c = new Child();
+ c.setName("Child One");
+ Parent p = new Parent();
+ p.setName("Parent");
+ p.getChildren().add(c);
+ c.setParent(p);
+ s.save(p);
+ s.flush();
+
+ p.getChildren().remove(c);
+ c.setParent(null);
+ s.flush();
+
+ p.getChildren().add(c);
+ c.setParent(p);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c.setParent(null);
+ s.update(c);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c.setParent(p);
+ s.update(c);
+ t.commit();
+ s.close();
+
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Child) s.createQuery("from Child").uniqueResult();
+ s.createQuery("from Child c left join fetch c.parent").list();
+ s.createQuery("from Child c inner join fetch c.parent").list();
+ s.clear();
+ p = (Parent) s.createQuery("from Parent p left join fetch p.children").uniqueResult();
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.createQuery("delete from Child").executeUpdate();
+ s.createQuery("delete from Parent").executeUpdate();
+ t.commit();
+ s.close();
+
+ }
+
+ public void testManyToManySize() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ //this test raise an exception right now HHH-570
+ assertEquals( 0, s.createQuery("from Parent p where size(p.children) = 0").list().size() );
+ assertEquals( 0, s.createQuery("from Parent p where p.children.size = 0").list().size() );
+ t.commit();
+ s.close();
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetomany/OneToManyTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetomany/Parent.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetomany/Parent.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetomany/Parent.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,49 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+ This mapping demonstrates how to use an "inverse" join
+ to map an association table as an association with
+ one-to-many multiplicity at the level of the object
+ model.
+
+-->
+
+<hibernate-mapping
+ package="org.hibernate.test.onetomany">
+
+ <class name="Parent">
+ <id name="id"
+ column="parent_id">
+ <generator class="increment"/>
+ </id>
+ <property name="name"/>
+ <set name="children"
+ table="ParentChild"
+ cascade="all"
+ inverse="true">
+ <key column="parent_id"/>
+ <many-to-many column="child_id"
+ class="Child"/>
+ </set>
+ </class>
+
+ <class name="Child">
+ <id name="id"
+ column="child_id">
+ <generator class="increment"/>
+ </id>
+ <property name="name"/>
+ <join table="ParentChild"
+ optional="true">
+ <key column="child_id"/>
+ <many-to-one name="parent"
+ column="parent_id"
+ not-null="true"/>
+ </join>
+ </class>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetomany/Parent.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetomany/Parent.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetomany/Parent.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetomany/Parent.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,50 @@
+//$Id: Parent.java 4378 2004-08-19 10:12:11Z oneovthafew $
+package org.hibernate.test.onetomany;
+
+import java.util.Collection;
+import java.util.HashSet;
+
+/**
+ * @author gavin
+ */
+public class Parent {
+ private Long id;
+ private String name;
+ private Collection children = new HashSet();
+ /**
+ * @return Returns the children.
+ */
+ public Collection getChildren() {
+ return children;
+ }
+ /**
+ * @param children The children to set.
+ */
+ public void setChildren(Collection children) {
+ this.children = children;
+ }
+ /**
+ * @return Returns the id.
+ */
+ public Long getId() {
+ return id;
+ }
+ /**
+ * @param id The id to set.
+ */
+ public void setId(Long id) {
+ this.id = id;
+ }
+ /**
+ * @return Returns the name.
+ */
+ public String getName() {
+ return name;
+ }
+ /**
+ * @param name The name to set.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetomany/Parent.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/OneToOneSuite.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/OneToOneSuite.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/OneToOneSuite.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,29 @@
+package org.hibernate.test.onetoone;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.hibernate.test.onetoone.formula.OneToOneFormulaTest;
+import org.hibernate.test.onetoone.joined.JoinedSubclassOneToOneTest;
+import org.hibernate.test.onetoone.link.OneToOneLinkTest;
+import org.hibernate.test.onetoone.nopojo.DynamicMapOneToOneTest;
+import org.hibernate.test.onetoone.optional.OptionalOneToOneTest;
+import org.hibernate.test.onetoone.singletable.DiscrimSubclassOneToOneTest;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class OneToOneSuite {
+ public static Test suite() {
+ TestSuite suite = new TestSuite( "one-to-one suite" );
+ suite.addTest( OneToOneFormulaTest.suite() );
+ suite.addTest( JoinedSubclassOneToOneTest.suite() );
+ suite.addTest( OneToOneLinkTest.suite() );
+ suite.addTest( DynamicMapOneToOneTest.suite() );
+ suite.addTest( OptionalOneToOneTest.suite() );
+ suite.addTest( DiscrimSubclassOneToOneTest.suite() );
+ return suite;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/formula/Address.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/formula/Address.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/formula/Address.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,57 @@
+//$Id: Address.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.onetoone.formula;
+
+import java.io.Serializable;
+
+/**
+ * @author Gavin King
+ */
+public class Address implements Serializable {
+ private Person person;
+ private String type;
+ private String street;
+ private String state;
+ private String zip;
+
+ public Person getPerson() {
+ return person;
+ }
+ public void setPerson(Person person) {
+ this.person = person;
+ }
+ public String getState() {
+ return state;
+ }
+ public void setState(String state) {
+ this.state = state;
+ }
+ public String getStreet() {
+ return street;
+ }
+ public void setStreet(String street) {
+ this.street = street;
+ }
+ public String getType() {
+ return type;
+ }
+ public void setType(String type) {
+ this.type = type;
+ }
+ public String getZip() {
+ return zip;
+ }
+ public void setZip(String zip) {
+ this.zip = zip;
+ }
+
+ public boolean equals(Object that) {
+ if ( !(that instanceof Address) ) return false;
+ Address address = (Address) that;
+ return address.getType().equals(type) &&
+ address.getPerson().getName().equals( person.getName() );
+ }
+
+ public int hashCode() {
+ return person.getName().hashCode() + type.hashCode();
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/formula/Address.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/formula/OneToOneFormulaTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/formula/OneToOneFormulaTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/formula/OneToOneFormulaTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,164 @@
+//$Id: OneToOneFormulaTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.onetoone.formula;
+
+import junit.framework.Test;
+
+import org.hibernate.FetchMode;
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.criterion.Property;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class OneToOneFormulaTest extends FunctionalTestCase {
+
+ public OneToOneFormulaTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "onetoone/formula/Person.hbm.xml" };
+ }
+
+ public void configure(Configuration cfg) {
+ cfg.setProperty(Environment.USE_SECOND_LEVEL_CACHE, "false");
+ cfg.setProperty(Environment.GENERATE_STATISTICS, "true");
+ cfg.setProperty(Environment.DEFAULT_BATCH_FETCH_SIZE, "2");
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( OneToOneFormulaTest.class );
+ }
+
+ public void testOneToOneFormula() {
+ Person p = new Person();
+ p.setName("Gavin King");
+ Address a = new Address();
+ a.setPerson(p);
+ a.setType("HOME");
+ a.setZip("3181");
+ a.setState("VIC");
+ a.setStreet("Karbarook Ave");
+ p.setAddress(a);
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.persist(p);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ p = (Person) s.createQuery("from Person").uniqueResult();
+
+ assertNotNull( p.getAddress() );
+ assertTrue( Hibernate.isInitialized( p.getAddress() ) );
+ assertNull( p.getMailingAddress() );
+
+ s.clear();
+
+ p = (Person) s.createQuery("from Person p left join fetch p.mailingAddress left join fetch p.address").uniqueResult();
+
+ assertNotNull( p.getAddress() );
+ assertTrue( Hibernate.isInitialized( p.getAddress() ) );
+ assertNull( p.getMailingAddress() );
+
+ s.clear();
+
+ p = (Person) s.createQuery("from Person p left join fetch p.address").uniqueResult();
+
+ assertNotNull( p.getAddress() );
+ assertTrue( Hibernate.isInitialized( p.getAddress() ) );
+ assertNull( p.getMailingAddress() );
+
+ s.clear();
+
+ p = (Person) s.createCriteria(Person.class)
+ .createCriteria("address")
+ .add( Property.forName("zip").eq("3181") )
+ .uniqueResult();
+ assertNotNull(p);
+
+ s.clear();
+
+ p = (Person) s.createCriteria(Person.class)
+ .setFetchMode("address", FetchMode.JOIN)
+ .uniqueResult();
+
+ assertNotNull( p.getAddress() );
+ assertTrue( Hibernate.isInitialized( p.getAddress() ) );
+ assertNull( p.getMailingAddress() );
+
+ s.clear();
+
+ p = (Person) s.createCriteria(Person.class)
+ .setFetchMode("mailingAddress", FetchMode.JOIN)
+ .uniqueResult();
+
+ assertNotNull( p.getAddress() );
+ assertTrue( Hibernate.isInitialized( p.getAddress() ) );
+ assertNull( p.getMailingAddress() );
+
+ s.delete(p);
+
+ t.commit();
+ s.close();
+
+ }
+
+ public void testOneToOneEmbeddedCompositeKey() {
+ Person p = new Person();
+ p.setName("Gavin King");
+ Address a = new Address();
+ a.setPerson(p);
+ a.setType("HOME");
+ a.setZip("3181");
+ a.setState("VIC");
+ a.setStreet("Karbarook Ave");
+ p.setAddress(a);
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.persist(p);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+
+ a = new Address();
+ a.setType("HOME");
+ a.setPerson(p);
+ a = (Address) s.load(Address.class, a);
+ assertFalse( Hibernate.isInitialized(a) );
+ a.getPerson();
+ a.getType();
+ assertFalse( Hibernate.isInitialized(a) );
+ assertEquals(a.getZip(), "3181");
+
+ s.clear();
+
+ a = new Address();
+ a.setType("HOME");
+ a.setPerson(p);
+ Address a2 = (Address) s.get(Address.class, a);
+ assertTrue( Hibernate.isInitialized(a) );
+ assertSame(a2, a);
+ assertSame(a2.getPerson(), p); //this is a little bit desirable
+ assertEquals(a.getZip(), "3181");
+
+ s.delete(a2);
+ s.delete( s.get( Person.class, p.getName() ) ); //this is certainly undesirable! oh well...
+
+ t.commit();
+ s.close();
+
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/formula/OneToOneFormulaTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/formula/Person.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/formula/Person.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/formula/Person.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.onetoone.formula">
+
+ <class name="Person">
+ <id name="name"/>
+ <one-to-one name="address" cascade="all" constrained="false">
+ <formula>name</formula>
+ <formula>'HOME'</formula>
+ </one-to-one>
+ <one-to-one name="mailingAddress" constrained="false">
+ <formula>name</formula>
+ <formula>'MAILING'</formula>
+ </one-to-one>
+ </class>
+
+ <class name="Address" batch-size="2"
+ check="addressType in ('MAILING', 'HOME', 'BUSINESS')">
+ <composite-id>
+ <key-many-to-one name="person"
+ column="personName"/>
+ <key-property name="type"
+ column="addressType"/>
+ </composite-id>
+ <property name="street" type="text"/>
+ <property name="state"/>
+ <property name="zip"/>
+ </class>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/formula/Person.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/formula/Person.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/formula/Person.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/formula/Person.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,42 @@
+//$Id: Person.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.onetoone.formula;
+
+import java.io.Serializable;
+
+/**
+ * @author Gavin King
+ */
+public class Person implements Serializable {
+ private String name;
+ private Address address;
+ private Address mailingAddress;
+
+ public Address getAddress() {
+ return address;
+ }
+ public void setAddress(Address address) {
+ this.address = address;
+ }
+ public Address getMailingAddress() {
+ return mailingAddress;
+ }
+ public void setMailingAddress(Address mailingAddress) {
+ this.mailingAddress = mailingAddress;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public boolean equals(Object that) {
+ if ( !(that instanceof Person) ) return false;
+ Person person = (Person) that;
+ return person.getName().equals(name);
+ }
+
+ public int hashCode() {
+ return name.hashCode();
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/formula/Person.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/joined/Address.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/joined/Address.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/joined/Address.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,16 @@
+//$Id: Address.java 9914 2006-05-09 09:37:18Z max.andersen at jboss.com $
+package org.hibernate.test.onetoone.joined;
+
+/**
+ * @author Gavin King
+ */
+public class Address {
+ public String entityName;
+ public String street;
+ public String state;
+ public String zip;
+
+ public String toString() {
+ return this.getClass() + ":" + street;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/joined/Address.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/joined/Entity.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/joined/Entity.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/joined/Entity.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,9 @@
+//$Id: Entity.java 5686 2005-02-12 07:27:32Z steveebersole $
+package org.hibernate.test.onetoone.joined;
+
+/**
+ * @author Gavin King
+ */
+public class Entity {
+ public String name;
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/joined/Entity.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/joined/JoinedSubclassOneToOneTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/joined/JoinedSubclassOneToOneTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/joined/JoinedSubclassOneToOneTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,159 @@
+//$Id: JoinedSubclassOneToOneTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.onetoone.joined;
+
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.stat.EntityStatistics;
+
+/**
+ * @author Gavin King
+ */
+public class JoinedSubclassOneToOneTest extends FunctionalTestCase {
+
+ public JoinedSubclassOneToOneTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "onetoone/joined/Person.hbm.xml" };
+ }
+
+ public void configure(Configuration cfg) {
+ cfg.setProperty(Environment.USE_SECOND_LEVEL_CACHE, "false");
+ cfg.setProperty(Environment.GENERATE_STATISTICS, "true");
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( JoinedSubclassOneToOneTest.class );
+ }
+
+ public void testOneToOneOnSubclass() {
+ Person p = new Person();
+ p.name = "Gavin";
+ Address a = new Address();
+ a.entityName = "Gavin";
+ a.zip = "3181";
+ a.state = "VIC";
+ a.street = "Karbarook Ave";
+ p.address = a;
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.persist(p);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+
+ EntityStatistics addressStats = getSessions().getStatistics().getEntityStatistics( Address.class.getName() );
+ EntityStatistics mailingAddressStats = getSessions().getStatistics().getEntityStatistics("MailingAddress");
+
+ p = (Person) s.createQuery("from Person p join fetch p.address left join fetch p.mailingAddress").uniqueResult();
+ assertNotNull(p.address); assertNull(p.mailingAddress);
+ s.clear();
+
+ p = (Person) s.createQuery("select p from Person p join fetch p.address left join fetch p.mailingAddress").uniqueResult();
+ assertNotNull(p.address); assertNull(p.mailingAddress);
+ s.clear();
+
+ Object[] stuff = (Object[]) s.createQuery("select p.name, p from Person p join fetch p.address left join fetch p.mailingAddress").uniqueResult();
+ assertEquals(stuff.length, 2);
+ p = (Person) stuff[1];
+ assertNotNull(p.address); assertNull(p.mailingAddress);
+ s.clear();
+
+ assertEquals( addressStats.getFetchCount(), 0 );
+ assertEquals( mailingAddressStats.getFetchCount(), 0 );
+
+ p = (Person) s.createQuery("from Person p join fetch p.address").uniqueResult();
+ assertNotNull(p.address); assertNull(p.mailingAddress);
+ s.clear();
+
+ assertEquals( addressStats.getFetchCount(), 0 );
+ assertEquals( mailingAddressStats.getFetchCount(), 1 );
+
+ p = (Person) s.createQuery("from Person").uniqueResult();
+ assertNotNull(p.address); assertNull(p.mailingAddress);
+ s.clear();
+
+ assertEquals( addressStats.getFetchCount(), 0 );
+ assertEquals( mailingAddressStats.getFetchCount(), 2 );
+
+ p = (Person) s.createQuery("from Entity").uniqueResult();
+ assertNotNull(p.address); assertNull(p.mailingAddress);
+ s.clear();
+
+ assertEquals( addressStats.getFetchCount(), 0 );
+ assertEquals( mailingAddressStats.getFetchCount(), 3 );
+
+ //note that in here join fetch is used for the nullable
+ //one-to-one, due to a very special case of default
+ p = (Person) s.get(Person.class, "Gavin");
+ assertNotNull(p.address); assertNull(p.mailingAddress);
+ s.clear();
+
+ assertEquals( addressStats.getFetchCount(), 0 );
+ assertEquals( mailingAddressStats.getFetchCount(), 3 );
+
+ p = (Person) s.get(Entity.class, "Gavin");
+ assertNotNull(p.address); assertNull(p.mailingAddress);
+ s.clear();
+
+ assertEquals( addressStats.getFetchCount(), 0 );
+ assertEquals( mailingAddressStats.getFetchCount(), 3 );
+
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Org org = new Org();
+ org.name = "IFA";
+ Address a2 = new Address();
+ a2.entityName = "IFA";
+ a2.zip = "3181";
+ a2.state = "VIC";
+ a2.street = "Orrong Rd";
+ s.persist(org);
+ s.persist(a2);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ org = (Org) s.get(Entity.class, "IFA");
+ s.clear();
+
+ List list = s.createQuery("from Entity e order by e.name").list();
+ p = (Person) list.get(0);
+ assertNotNull(p.address); assertNull(p.mailingAddress);
+ org = (Org) list.get(1);
+ s.clear();
+
+ list = s.createQuery("from Entity e left join fetch e.address left join fetch e.mailingAddress order by e.name").list();
+ p = (Person) list.get(0);
+ org = (Org) list.get(1);
+ assertNotNull(p.address); assertNull(p.mailingAddress);
+
+ s.clear();
+ s.delete(p);
+ s.delete( p.address );
+ s.delete( org );
+ s.delete( a2 );
+ s.flush();
+ t.commit();
+ s.close();
+
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/joined/JoinedSubclassOneToOneTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/joined/Org.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/joined/Org.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/joined/Org.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,9 @@
+//$Id: Org.java 5686 2005-02-12 07:27:32Z steveebersole $
+package org.hibernate.test.onetoone.joined;
+
+
+/**
+ * @author Gavin King
+ */
+public class Org extends Entity {
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/joined/Org.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/joined/Person.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/joined/Person.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/joined/Person.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,40 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping
+ package="org.hibernate.test.onetoone.joined"
+ default-access="field">
+
+ <class name="Entity">
+ <id name="name"/>
+ <joined-subclass name="Person">
+ <key column="entityName"/>
+ <one-to-one name="address"
+ cascade="all"
+ constrained="true"/>
+ <one-to-one name="mailingAddress"
+ cascade="all"
+ entity-name="MailingAddress"/>
+ </joined-subclass>
+ <joined-subclass name="Org">
+ <key column="entityName"/>
+ </joined-subclass>
+ </class>
+
+ <class name="Address">
+ <id name="entityName"/>
+ <property name="street"/>
+ <property name="state"/>
+ <property name="zip"/>
+ </class>
+
+ <class name="Address" entity-name="MailingAddress">
+ <id name="entityName"/>
+ <property name="street"/>
+ <property name="state"/>
+ <property name="zip"/>
+ </class>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/joined/Person.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/joined/Person.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/joined/Person.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/joined/Person.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,10 @@
+//$Id: Person.java 5686 2005-02-12 07:27:32Z steveebersole $
+package org.hibernate.test.onetoone.joined;
+
+/**
+ * @author Gavin King
+ */
+public class Person extends Entity {
+ public Address address;
+ public Address mailingAddress;
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/joined/Person.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/link/Customer.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/link/Customer.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/link/Customer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+//$Id: Customer.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.onetoone.link;
+
+/**
+ * @author Gavin King
+ */
+public class Customer {
+ private Long id;
+ private Person person;
+ public Person getPerson() {
+ return person;
+ }
+ public void setPerson(Person person) {
+ this.person = person;
+ }
+ public Long getId() {
+ return id;
+ }
+ public void setId(Long id) {
+ this.id = id;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/link/Customer.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/link/Employee.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/link/Employee.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/link/Employee.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+//$Id: Employee.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.onetoone.link;
+
+/**
+ * @author Gavin King
+ */
+public class Employee {
+ private Long id;
+ private Person person;
+ public Person getPerson() {
+ return person;
+ }
+ public void setPerson(Person person) {
+ this.person = person;
+ }
+ public Long getId() {
+ return id;
+ }
+ public void setId(Long id) {
+ this.id = id;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/link/Employee.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/link/OneToOneLinkTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/link/OneToOneLinkTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/link/OneToOneLinkTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,91 @@
+//$Id: OneToOneLinkTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.onetoone.link;
+
+import java.util.Date;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class OneToOneLinkTest extends FunctionalTestCase {
+
+ public OneToOneLinkTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "onetoone/link/Person.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( OneToOneLinkTest.class );
+ }
+
+ public void testOneToOneViaAssociationTable() {
+ Person p = new Person();
+ p.setName("Gavin King");
+ p.setDob( new Date() );
+ Employee e = new Employee();
+ p.setEmployee(e);
+ e.setPerson(p);
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.persist(p);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ e = (Employee) s.createQuery("from Employee e where e.person.name like 'Gavin%'").uniqueResult();
+ assertEquals( e.getPerson().getName(), "Gavin King" );
+ assertFalse( Hibernate.isInitialized( e.getPerson() ) );
+ assertNull( e.getPerson().getCustomer() );
+ s.clear();
+
+ e = (Employee) s.createQuery("from Employee e where e.person.dob = :date")
+ .setDate("date", new Date() )
+ .uniqueResult();
+ assertEquals( e.getPerson().getName(), "Gavin King" );
+ assertFalse( Hibernate.isInitialized( e.getPerson() ) );
+ assertNull( e.getPerson().getCustomer() );
+ s.clear();
+
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+
+ e = (Employee) s.createQuery("from Employee e join fetch e.person p left join fetch p.customer").uniqueResult();
+ assertTrue( Hibernate.isInitialized( e.getPerson() ) );
+ assertNull( e.getPerson().getCustomer() );
+ Customer c = new Customer();
+ e.getPerson().setCustomer(c);
+ c.setPerson( e.getPerson() );
+
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+
+ e = (Employee) s.createQuery("from Employee e join fetch e.person p left join fetch p.customer").uniqueResult();
+ assertTrue( Hibernate.isInitialized( e.getPerson() ) );
+ assertTrue( Hibernate.isInitialized( e.getPerson().getCustomer() ) );
+ assertNotNull( e.getPerson().getCustomer() );
+ s.delete(e);
+ t.commit();
+ s.close();
+
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/link/OneToOneLinkTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/link/Person.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/link/Person.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/link/Person.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,69 @@
+<?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 the mapping of two subtyping one-to-one relationships
+ to association tables using <many-to-one> nested inside <join>.
+ Note that the <join> elements specify optional="true", and that
+ there is an inverse="true" side of both joins.
+
+ -->
+
+<hibernate-mapping package="org.hibernate.test.onetoone.link">
+
+ <class name="Person">
+ <id name="name"/>
+ <property name="dob" type="date"/>
+ <join table="employeePerson"
+ inverse="true"
+ optional="true">
+ <key column="personId" unique="true"/>
+ <many-to-one name="employee"
+ column="employeeId"
+ cascade="all"
+ not-null="true"/>
+ </join>
+ <join table="customerPerson"
+ inverse="true"
+ optional="true">
+ <key column="personId" unique="true"/>
+ <many-to-one name="customer"
+ column="customerId"
+ cascade="all"
+ not-null="true"/>
+ </join>
+ </class>
+
+ <class name="Employee">
+ <id name="id" column="employeeId">
+ <generator class="native"/>
+ </id>
+ <join table="employeePerson"
+ optional="true">
+ <key column="employeeId"/>
+ <many-to-one name="person"
+ column="personId"
+ cascade="all"
+ not-null="true"
+ unique="true"/>
+ </join>
+ </class>
+
+ <class name="Customer">
+ <id name="id" column="customerId">
+ <generator class="native"/>
+ </id>
+ <join table="customerPerson" optional="true">
+ <key column="customerId"/>
+ <many-to-one name="person"
+ column="personId"
+ cascade="all"
+ not-null="true"
+ unique="true"/>
+ </join>
+ </class>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/link/Person.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/link/Person.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/link/Person.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/link/Person.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,38 @@
+//$Id: Person.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.onetoone.link;
+
+import java.util.Date;
+
+/**
+ * @author Gavin King
+ */
+public class Person {
+ private String name;
+ private Date dob;
+ private Employee employee;
+ private Customer customer;
+ public Customer getCustomer() {
+ return customer;
+ }
+ public void setCustomer(Customer customer) {
+ this.customer = customer;
+ }
+ public Employee getEmployee() {
+ return employee;
+ }
+ public void setEmployee(Employee employee) {
+ this.employee = employee;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public Date getDob() {
+ return dob;
+ }
+ public void setDob(Date dob) {
+ this.dob = dob;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/link/Person.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/nopojo/DynamicMapOneToOneTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/nopojo/DynamicMapOneToOneTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/nopojo/DynamicMapOneToOneTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,82 @@
+//$Id: DynamicMapOneToOneTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.onetoone.nopojo;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.Test;
+
+import org.hibernate.EntityMode;
+import org.hibernate.Session;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.stat.EntityStatistics;
+
+/**
+ * @author Gavin King
+ */
+public class DynamicMapOneToOneTest extends FunctionalTestCase {
+
+ public DynamicMapOneToOneTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "onetoone/nopojo/Person.hbm.xml" };
+ }
+
+ public void configure(Configuration cfg) {
+ cfg.setProperty(Environment.USE_SECOND_LEVEL_CACHE, "false");
+ cfg.setProperty(Environment.GENERATE_STATISTICS, "true");
+ cfg.setProperty( Environment.DEFAULT_ENTITY_MODE, EntityMode.MAP.toString() );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( DynamicMapOneToOneTest.class );
+ }
+
+ public void testOneToOneOnSubclass() {
+ Map person = new HashMap();
+ person.put( "name", "Steve" );
+ Map address = new HashMap();
+ address.put( "zip", "12345" );
+ address.put( "state", "TX" );
+ address.put( "street", "123 Main St" );
+
+ person.put( "address", address );
+ address.put( "owner", person );
+
+ Session s = openSession();
+ s.beginTransaction();
+ s.persist( "Person", person );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+
+ EntityStatistics addressStats = getSessions().getStatistics().getEntityStatistics( "Address" );
+
+ person = ( Map ) s.createQuery( "from Person p join fetch p.address" ).uniqueResult();
+ assertNotNull( "could not locate person", person );
+ assertNotNull( "could not locate persons address", person.get( "address" ) );
+ s.clear();
+
+ Object[] tuple = ( Object[] ) s.createQuery( "select p.name, p from Person p join fetch p.address" ).uniqueResult();
+ assertEquals( tuple.length, 2 );
+ person = ( Map ) tuple[1];
+ assertNotNull( "could not locate person", person );
+ assertNotNull( "could not locate persons address", person.get( "address" ) );
+
+ s.delete( "Person", person );
+
+ s.getTransaction().commit();
+ s.close();
+
+ assertEquals( addressStats.getFetchCount(), 0 );
+ }
+
+}
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/nopojo/Person.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/nopojo/Person.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/nopojo/Person.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -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">
+
+<hibernate-mapping package="org.hibernate.test.onetoone.nopojo">
+
+ <class entity-name="Person">
+ <id name="id" type="long">
+ <generator class="foreign">
+ <param name="property">address</param>
+ </generator>
+ </id>
+ <property name="name" type="string"/>
+ <one-to-one name="address" cascade="all" entity-name="Address" constrained="true"/>
+ </class>
+
+ <class entity-name="Address">
+ <id name="id" type="long">
+ <generator class="native"/>
+ </id>
+ <property name="street" type="string"/>
+ <property name="state" type="string"/>
+ <property name="zip" type="string"/>
+ <one-to-one name="owner" entity-name="Person" />
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/optional/Address.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/optional/Address.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/optional/Address.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,15 @@
+package org.hibernate.test.onetoone.optional;
+
+/**
+ * @author Gavin King
+ */
+public class Address {
+ public String entityName;
+ public String street;
+ public String state;
+ public String zip;
+
+ public String toString() {
+ return this.getClass() + ":" + street;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/optional/Entity.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/optional/Entity.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/optional/Entity.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,8 @@
+package org.hibernate.test.onetoone.optional;
+
+/**
+ * @author Gavin King
+ */
+public class Entity {
+ public String name;
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/optional/OptionalOneToOneTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/optional/OptionalOneToOneTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/optional/OptionalOneToOneTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,50 @@
+package org.hibernate.test.onetoone.optional;
+
+import junit.framework.Test;
+
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.Session;
+
+/**
+ * @author Gavin King
+ */
+public class OptionalOneToOneTest extends FunctionalTestCase {
+
+ public OptionalOneToOneTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "onetoone/optional/Person.hbm.xml" };
+ }
+
+ public void configure(Configuration cfg) {
+ cfg.setProperty( Environment.USE_SECOND_LEVEL_CACHE, "false");
+ cfg.setProperty(Environment.GENERATE_STATISTICS, "true");
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( OptionalOneToOneTest.class );
+ }
+
+ public void testOptionalOneToOneRetrieval() {
+ Session s = openSession();
+ s.beginTransaction();
+ Person me = new Person();
+ me.name = "Steve";
+ s.save( me );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ me = ( Person ) s.load( Person.class, me.name );
+ assertNull( me.address );
+ s.delete( me );
+ s.getTransaction().commit();
+ s.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/optional/Org.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/optional/Org.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/optional/Org.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,8 @@
+package org.hibernate.test.onetoone.optional;
+
+
+/**
+ * @author Gavin King
+ */
+public class Org extends Entity {
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/optional/Person.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/optional/Person.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/optional/Person.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,38 @@
+<?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 mapping an "optional" one-to-one association. Basically
+ a (zero or one)-to-one.
+
+ Note that this is only conceptually possible on the non-constrained
+ side of the association (the side without the FK).
+
+ Also, it is impossible that the optional side be lazy; we must hit the
+ target table to determine whether a matching row actually exists or not.
+ This is so we can properly determine whether to use null or some value
+ for the association property's value.
+-->
+<hibernate-mapping package="org.hibernate.test.onetoone.optional" default-access="field">
+
+ <class name="Entity">
+ <id name="name"/>
+ <joined-subclass name="Person">
+ <key column="entityName"/>
+ <one-to-one name="address" cascade="all" constrained="false" outer-join="false" lazy="proxy"/>
+ </joined-subclass>
+ <joined-subclass name="Org">
+ <key column="entityName"/>
+ </joined-subclass>
+ </class>
+
+ <class name="Address">
+ <id name="entityName"/>
+ <property name="street"/>
+ <property name="state"/>
+ <property name="zip"/>
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/optional/Person.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/optional/Person.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/optional/Person.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,9 @@
+package org.hibernate.test.onetoone.optional;
+
+/**
+ * @author Gavin King
+ */
+public class Person extends Entity {
+ public Address address;
+ public Address mailingAddress;
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/singletable/Address.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/singletable/Address.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/singletable/Address.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,12 @@
+//$Id: Address.java 5686 2005-02-12 07:27:32Z steveebersole $
+package org.hibernate.test.onetoone.singletable;
+
+/**
+ * @author Gavin King
+ */
+public class Address {
+ public String entityName;
+ public String street;
+ public String state;
+ public String zip;
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/singletable/Address.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/singletable/DiscrimSubclassOneToOneTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/singletable/DiscrimSubclassOneToOneTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/singletable/DiscrimSubclassOneToOneTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,148 @@
+//$Id: DiscrimSubclassOneToOneTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.onetoone.singletable;
+
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.stat.EntityStatistics;
+
+/**
+ * @author Gavin King
+ */
+public class DiscrimSubclassOneToOneTest extends FunctionalTestCase {
+
+ public DiscrimSubclassOneToOneTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "onetoone/singletable/Person.hbm.xml" };
+ }
+
+ public void configure(Configuration cfg) {
+ cfg.setProperty(Environment.USE_SECOND_LEVEL_CACHE, "false");
+ cfg.setProperty(Environment.GENERATE_STATISTICS, "true");
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( DiscrimSubclassOneToOneTest.class );
+ }
+
+ public void testOneToOneOnSubclass() {
+ Person p = new Person();
+ p.name = "Gavin";
+ Address a = new Address();
+ a.entityName = "Gavin";
+ a.zip = "3181";
+ a.state = "VIC";
+ a.street = "Karbarook Ave";
+ p.address = a;
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.persist(p);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+
+ EntityStatistics addressStats = getSessions().getStatistics().getEntityStatistics( Address.class.getName() );
+ EntityStatistics mailingAddressStats = getSessions().getStatistics().getEntityStatistics("MailingAddress");
+
+ p = (Person) s.createQuery("from Person p join fetch p.address left join fetch p.mailingAddress").uniqueResult();
+ assertNotNull(p.address); assertNull(p.mailingAddress);
+ s.clear();
+
+ assertEquals( addressStats.getFetchCount(), 0 );
+ assertEquals( mailingAddressStats.getFetchCount(), 0 );
+
+ p = (Person) s.createQuery("from Person p join fetch p.address").uniqueResult();
+ assertNotNull(p.address); assertNull(p.mailingAddress);
+ s.clear();
+
+ assertEquals( addressStats.getFetchCount(), 0 );
+ assertEquals( mailingAddressStats.getFetchCount(), 1 );
+
+ p = (Person) s.createQuery("from Person").uniqueResult();
+ assertNotNull(p.address); assertNull(p.mailingAddress);
+ s.clear();
+
+ assertEquals( addressStats.getFetchCount(), 1 );
+ assertEquals( mailingAddressStats.getFetchCount(), 2 );
+
+ p = (Person) s.createQuery("from Entity").uniqueResult();
+ assertNotNull(p.address); assertNull(p.mailingAddress);
+ s.clear();
+
+ assertEquals( addressStats.getFetchCount(), 2 );
+ assertEquals( mailingAddressStats.getFetchCount(), 3 );
+
+ //note that in here join fetch is used for the nullable
+ //one-to-one, due to a very special case of default
+ p = (Person) s.get(Person.class, "Gavin");
+ assertNotNull(p.address); assertNull(p.mailingAddress);
+ s.clear();
+
+ assertEquals( addressStats.getFetchCount(), 2 );
+ assertEquals( mailingAddressStats.getFetchCount(), 3 );
+
+ p = (Person) s.get(Entity.class, "Gavin");
+ assertNotNull(p.address); assertNull(p.mailingAddress);
+ s.clear();
+
+ assertEquals( addressStats.getFetchCount(), 2 );
+ assertEquals( mailingAddressStats.getFetchCount(), 3 );
+
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Org org = new Org();
+ org.name = "IFA";
+ Address a2 = new Address();
+ a2.entityName = "IFA";
+ a2.zip = "3181";
+ a2.state = "VIC";
+ a2.street = "Orrong Rd";
+ org.addresses.add(a2);
+ s.persist(org);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ org = (Org) s.get(Entity.class, "IFA");
+ s.clear();
+
+ List list = s.createQuery("from Entity e order by e.name").list();
+ p = (Person) list.get(0);
+ assertNotNull(p.address); assertNull(p.mailingAddress);
+ org = (Org) list.get(1);
+ assertEquals( org.addresses.size(), 1 );
+ s.clear();
+
+ list = s.createQuery("from Entity e left join fetch e.address left join fetch e.mailingAddress order by e.name").list();
+ p = (Person) list.get(0);
+ org = (Org) list.get(1);
+ assertNotNull(p.address); assertNull(p.mailingAddress);
+ assertEquals( org.addresses.size(), 1 );
+
+ s.delete(p);
+ s.delete(org);
+
+ t.commit();
+ s.close();
+
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/singletable/DiscrimSubclassOneToOneTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/singletable/Entity.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/singletable/Entity.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/singletable/Entity.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,9 @@
+//$Id: Entity.java 5686 2005-02-12 07:27:32Z steveebersole $
+package org.hibernate.test.onetoone.singletable;
+
+/**
+ * @author Gavin King
+ */
+public class Entity {
+ public String name;
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/singletable/Entity.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/singletable/Org.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/singletable/Org.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/singletable/Org.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,12 @@
+//$Id: Org.java 5686 2005-02-12 07:27:32Z steveebersole $
+package org.hibernate.test.onetoone.singletable;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author Gavin King
+ */
+public class Org extends Entity {
+ public Set addresses = new HashSet();
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/singletable/Org.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/singletable/Person.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/singletable/Person.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/singletable/Person.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,42 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping
+ package="org.hibernate.test.onetoone.singletable"
+ default-access="field">
+
+ <class name="Entity">
+ <id name="name"/>
+ <discriminator type="string" column="subtype"/>
+ <subclass name="Person">
+ <one-to-one name="address" cascade="all"/>
+ <one-to-one name="mailingAddress"
+ cascade="all"
+ entity-name="MailingAddress"/>
+ </subclass>
+ <subclass name="Org">
+ <set name="addresses" inverse="true" cascade="all-delete-orphan">
+ <key column="entityName"/>
+ <one-to-many class="Address"/>
+ </set>
+ </subclass>
+ </class>
+
+ <class name="Address">
+ <id name="entityName"/>
+
+ <property name="street"/>
+ <property name="state"/>
+ <property name="zip"/>
+ </class>
+
+ <class name="Address" entity-name="MailingAddress">
+ <id name="entityName"/>
+ <property name="street"/>
+ <property name="state"/>
+ <property name="zip"/>
+ </class>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/singletable/Person.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/singletable/Person.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/singletable/Person.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/singletable/Person.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,10 @@
+//$Id: Person.java 5686 2005-02-12 07:27:32Z steveebersole $
+package org.hibernate.test.onetoone.singletable;
+
+/**
+ * @author Gavin King
+ */
+public class Person extends Entity {
+ public Address address;
+ public Address mailingAddress;
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/onetoone/singletable/Person.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/AbstractOperationTestCase.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/AbstractOperationTestCase.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/AbstractOperationTestCase.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,48 @@
+package org.hibernate.test.ops;
+
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.junit.functional.FunctionalTestCase;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public abstract class AbstractOperationTestCase extends FunctionalTestCase {
+ public AbstractOperationTestCase(String name) {
+ super( name );
+ }
+
+ public void configure(Configuration cfg) {
+ cfg.setProperty( Environment.GENERATE_STATISTICS, "true");
+ cfg.setProperty( Environment.STATEMENT_BATCH_SIZE, "0" );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "ops/Node.hbm.xml", "ops/Employer.hbm.xml", "ops/OptLockEntity.hbm.xml", "ops/OneToOne.hbm.xml", "ops/Competition.hbm.xml" };
+ }
+
+ public String getCacheConcurrencyStrategy() {
+ return null;
+ }
+
+ protected void clearCounts() {
+ getSessions().getStatistics().clear();
+ }
+
+ protected void assertInsertCount(int expected) {
+ int inserts = ( int ) getSessions().getStatistics().getEntityInsertCount();
+ assertEquals( "unexpected insert count", expected, inserts );
+ }
+
+ protected void assertUpdateCount(int expected) {
+ int updates = ( int ) getSessions().getStatistics().getEntityUpdateCount();
+ assertEquals( "unexpected update counts", expected, updates );
+ }
+
+ protected void assertDeleteCount(int expected) {
+ int deletes = ( int ) getSessions().getStatistics().getEntityDeleteCount();
+ assertEquals( "unexpected delete counts", expected, deletes );
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Address.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Address.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Address.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,65 @@
+package org.hibernate.test.ops;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class Address {
+ private Long id;
+ private String streetAddress;
+ private String city;
+ private String country;
+ private Person resident;
+
+ public Address() {
+ }
+
+ public Address(String streetAddress, String city, String country, Person resident) {
+ this.streetAddress = streetAddress;
+ this.city = city;
+ this.country = country;
+ this.resident = resident;
+ resident.setAddress( this );
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getStreetAddress() {
+ return streetAddress;
+ }
+
+ public void setStreetAddress(String streetAddress) {
+ this.streetAddress = streetAddress;
+ }
+
+ public String getCity() {
+ return city;
+ }
+
+ public void setCity(String city) {
+ this.city = city;
+ }
+
+ public String getCountry() {
+ return country;
+ }
+
+ public void setCountry(String country) {
+ this.country = country;
+ }
+
+ public Person getResident() {
+ return resident;
+ }
+
+ public void setResident(Person resident) {
+ this.resident = resident;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Competition.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Competition.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Competition.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.ops">
+
+ <class name="Competition">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <list name="competitors" table="COMPET_ION_OR" cascade="persist,merge,delete">
+ <key column="TION_ID"/>
+ <list-index column="INDEX_COL"/>
+ <many-to-many class="Competitor" column="TOR_ID" />
+ </list>
+ </class>
+
+ <class name="Competitor">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping>
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Competition.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Competition.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Competition.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,31 @@
+//$Id: $
+package org.hibernate.test.ops;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class Competition {
+ private Integer id;
+
+ private List competitors = new ArrayList();
+
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public List getCompetitors() {
+ return competitors;
+ }
+
+ public void setCompetitors(List competitors) {
+ this.competitors = competitors;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Competitor.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Competitor.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Competitor.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,34 @@
+//$Id: $
+package org.hibernate.test.ops;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class Competitor {
+ public Integer id;
+ private String name;
+
+
+ public Competitor() {
+ }
+
+ public Competitor(String name) {
+ this.name = name;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/CreateTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/CreateTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/CreateTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,218 @@
+//$Id: CreateTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.ops;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import junit.framework.Test;
+
+import org.hibernate.PersistentObjectException;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.exception.ConstraintViolationException;
+
+/**
+ * @author Gavin King
+ */
+public class CreateTest extends AbstractOperationTestCase {
+
+ public CreateTest(String str) {
+ super( str );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( CreateTest.class );
+ }
+
+ public void testNoUpdatesOnCreateVersionedWithCollection() {
+ clearCounts();
+
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ VersionedEntity root = new VersionedEntity( "root", "root" );
+ VersionedEntity child = new VersionedEntity( "c1", "child-1" );
+ root.getChildren().add( child );
+ child.setParent( root );
+ s.save(root);
+ tx.commit();
+ s.close();
+
+ assertInsertCount( 2 );
+ assertUpdateCount( 0 );
+ assertDeleteCount( 0 );
+
+ s = openSession();
+ tx = s.beginTransaction();
+ s.delete( root );
+ tx.commit();
+ s.close();
+
+ assertUpdateCount( 0 );
+ assertDeleteCount( 2 );
+ }
+
+ public void testCreateTree() {
+
+ clearCounts();
+
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ Node root = new Node("root");
+ Node child = new Node("child");
+ root.addChild(child);
+ s.persist(root);
+ tx.commit();
+ s.close();
+
+ assertInsertCount(2);
+ assertUpdateCount(0);
+
+ s = openSession();
+ tx = s.beginTransaction();
+ System.out.println("getting");
+ root = (Node) s.get(Node.class, "root");
+ Node child2 = new Node("child2");
+ root.addChild(child2);
+ System.out.println("committing");
+ tx.commit();
+ s.close();
+
+ assertInsertCount(3);
+ assertUpdateCount(0);
+ }
+
+ public void testCreateTreeWithGeneratedId() {
+
+ clearCounts();
+
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ NumberedNode root = new NumberedNode("root");
+ NumberedNode child = new NumberedNode("child");
+ root.addChild(child);
+ s.persist(root);
+ tx.commit();
+ s.close();
+
+ assertInsertCount(2);
+ assertUpdateCount(0);
+
+ s = openSession();
+ tx = s.beginTransaction();
+ root = (NumberedNode) s.get( NumberedNode.class, new Long( root.getId() ) );
+ NumberedNode child2 = new NumberedNode("child2");
+ root.addChild(child2);
+ tx.commit();
+ s.close();
+
+ assertInsertCount(3);
+ assertUpdateCount(0);
+ }
+
+ public void testCreateException() {
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ Node dupe = new Node("dupe");
+ s.persist(dupe);
+ s.persist(dupe);
+ tx.commit();
+ s.close();
+
+ s = openSession();
+ tx = s.beginTransaction();
+ s.persist(dupe);
+ try {
+ tx.commit();
+ assertFalse(true);
+ }
+ catch (ConstraintViolationException cve) {
+ //verify that an exception is thrown!
+ }
+ tx.rollback();
+ s.close();
+
+ Node nondupe = new Node("nondupe");
+ nondupe.addChild(dupe);
+
+ s = openSession();
+ tx = s.beginTransaction();
+ s.persist(nondupe);
+ try {
+ tx.commit();
+ assertFalse(true);
+ }
+ catch (ConstraintViolationException cve) {
+ //verify that an exception is thrown!
+ }
+ tx.rollback();
+ s.close();
+ }
+
+ public void testCreateExceptionWithGeneratedId() {
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ NumberedNode dupe = new NumberedNode("dupe");
+ s.persist(dupe);
+ s.persist(dupe);
+ tx.commit();
+ s.close();
+
+ s = openSession();
+ tx = s.beginTransaction();
+ try {
+ s.persist(dupe);
+ assertFalse(true);
+ }
+ catch (PersistentObjectException poe) {
+ //verify that an exception is thrown!
+ }
+ tx.rollback();
+ s.close();
+
+ NumberedNode nondupe = new NumberedNode("nondupe");
+ nondupe.addChild(dupe);
+
+ s = openSession();
+ tx = s.beginTransaction();
+ try {
+ s.persist(nondupe);
+ assertFalse(true);
+ }
+ catch (PersistentObjectException poe) {
+ //verify that an exception is thrown!
+ }
+ tx.rollback();
+ s.close();
+ }
+
+ public void testBasic() throws Exception {
+ Session s;
+ Transaction tx;
+ s = openSession();
+ tx = s.beginTransaction();
+ Employer er = new Employer();
+ Employee ee = new Employee();
+ s.persist(ee);
+ Collection erColl = new ArrayList();
+ Collection eeColl = new ArrayList();
+ erColl.add(ee);
+ eeColl.add(er);
+ er.setEmployees(erColl);
+ ee.setEmployers(eeColl);
+ tx.commit();
+ s.close();
+
+ s = openSession();
+ tx = s.beginTransaction();
+ er = (Employer) s.load(Employer.class, er.getId() );
+ assertNotNull(er);
+ assertNotNull( er.getEmployees() );
+ assertEquals( 1, er.getEmployees().size() );
+ Employee eeFromDb = (Employee) er.getEmployees().iterator().next();
+ assertEquals( ee.getId(), eeFromDb.getId() );
+ tx.commit();
+ s.close();
+ }
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/CreateTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/DeleteTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/DeleteTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/DeleteTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,91 @@
+package org.hibernate.test.ops;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class DeleteTest extends AbstractOperationTestCase {
+ public DeleteTest(String name) {
+ super( name );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( DeleteTest.class );
+ }
+
+ public void testDeleteVersionedWithCollectionNoUpdate() {
+ // test adapted from HHH-1564...
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedEntity c = new VersionedEntity( "c1", "child-1" );
+ VersionedEntity p = new VersionedEntity( "root", "root");
+ p.getChildren().add( c );
+ c.setParent( p );
+ s.save( p );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ s = openSession();
+ s.beginTransaction();
+ VersionedEntity loadedParent = ( VersionedEntity ) s.get( VersionedEntity.class, "root" );
+ s.delete( loadedParent );
+ s.getTransaction().commit();
+ s.close();
+
+ assertInsertCount( 0 );
+ assertUpdateCount( 0 );
+ assertDeleteCount( 2 );
+ }
+
+ public void testNoUpdateOnDelete() {
+ Session s = openSession();
+ s.beginTransaction();
+ Node node = new Node( "test" );
+ s.persist( node );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ s = openSession();
+ s.beginTransaction();
+ s.delete( node );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 0 );
+ }
+
+ public void testNoUpdateOnDeleteWithCollection() {
+ Session s = openSession();
+ s.beginTransaction();
+ Node parent = new Node( "parent" );
+ Node child = new Node( "child" );
+ parent.getCascadingChildren().add( child );
+ s.persist( parent );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ s = openSession();
+ s.beginTransaction();
+ parent = ( Node ) s.get( Node.class, "parent" );
+ s.delete( parent );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 0 );
+ assertDeleteCount( 2 );
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Employee.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Employee.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Employee.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,35 @@
+//$Id: Employee.java 5686 2005-02-12 07:27:32Z steveebersole $
+package org.hibernate.test.ops;
+
+import java.util.Collection;
+import java.io.Serializable;
+
+
+/**
+ * Employee in an Employer-Employee relationship
+ *
+ * @author Emmanuel Bernard
+ */
+
+public class Employee implements Serializable {
+ private Integer id;
+ private Collection employers;
+
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer integer) {
+ id = integer;
+ }
+
+
+ public Collection getEmployers() {
+ return employers;
+ }
+
+ public void setEmployers(Collection employers) {
+ this.employers = employers;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Employer.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Employer.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Employer.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,39 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.ops">
+
+ <class name="Employer" polymorphism="explicit">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <version column="vers" name="vers"/>
+ <bag name="employees"
+ cascade="persist,merge"
+ table="EMPLOYER_EMPLOYEE">
+ <key column="EMPER_ID"/>
+ <many-to-many class="Employee" column="EMPEE_ID" />
+ </bag>
+ </class>
+
+ <class name="Employee" polymorphism="explicit">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <bag name="employers"
+ inverse="true"
+ cascade="persist,merge,save-update"
+ table="EMPLOYER_EMPLOYEE">
+ <key column="EMPEE_ID"/>
+ <many-to-many class="Employer" column="EMPER_ID" />
+ </bag>
+ </class>
+
+</hibernate-mapping>
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Employer.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Employer.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Employer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,44 @@
+//$Id: Employer.java 8670 2005-11-25 17:36:29Z epbernard $
+package org.hibernate.test.ops;
+
+import java.util.Collection;
+import java.io.Serializable;
+
+
+/**
+ * Employer in a employer-Employee relationship
+ *
+ * @author Emmanuel Bernard
+ */
+
+public class Employer implements Serializable {
+ private Integer id;
+ private Collection employees;
+ private Integer vers;
+
+ public Integer getVers() {
+ return vers;
+ }
+
+ public void setVers(Integer vers) {
+ this.vers = vers;
+ }
+
+
+ public Collection getEmployees() {
+ return employees;
+ }
+
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setEmployees(Collection set) {
+ employees = set;
+ }
+
+ public void setId(Integer integer) {
+ id = integer;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/GetLoadTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/GetLoadTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/GetLoadTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,113 @@
+//$Id: GetLoadTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.ops;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+
+
+/**
+ * @author Gavin King
+ */
+public class GetLoadTest extends FunctionalTestCase {
+
+ public GetLoadTest(String str) {
+ super(str);
+ }
+
+ public void testGetLoad() {
+ clearCounts();
+
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ Employer emp = new Employer();
+ s.persist(emp);
+ Node node = new Node("foo");
+ Node parent = new Node("bar");
+ parent.addChild(node);
+ s.persist(parent);
+ tx.commit();
+ s.close();
+
+ s = openSession();
+ tx = s.beginTransaction();
+ emp = (Employer) s.get(Employer.class, emp.getId());
+ assertTrue( Hibernate.isInitialized(emp) );
+ assertFalse( Hibernate.isInitialized(emp.getEmployees()) );
+ node = (Node) s.get(Node.class, node.getName());
+ assertTrue( Hibernate.isInitialized(node) );
+ assertFalse( Hibernate.isInitialized(node.getChildren()) );
+ assertFalse( Hibernate.isInitialized(node.getParent()) );
+ assertNull( s.get(Node.class, "xyz") );
+ tx.commit();
+ s.close();
+
+ s = openSession();
+ tx = s.beginTransaction();
+ emp = (Employer) s.load(Employer.class, emp.getId());
+ emp.getId();
+ assertFalse( Hibernate.isInitialized(emp) );
+ node = (Node) s.load(Node.class, node.getName());
+ assertEquals( node.getName(), "foo" );
+ assertFalse( Hibernate.isInitialized(node) );
+ tx.commit();
+ s.close();
+
+ s = openSession();
+ tx = s.beginTransaction();
+ emp = (Employer) s.get("org.hibernate.test.ops.Employer", emp.getId());
+ assertTrue( Hibernate.isInitialized(emp) );
+ node = (Node) s.get("org.hibernate.test.ops.Node", node.getName());
+ assertTrue( Hibernate.isInitialized(node) );
+ tx.commit();
+ s.close();
+
+ s = openSession();
+ tx = s.beginTransaction();
+ emp = (Employer) s.load("org.hibernate.test.ops.Employer", emp.getId());
+ emp.getId();
+ assertFalse( Hibernate.isInitialized(emp) );
+ node = (Node) s.load("org.hibernate.test.ops.Node", node.getName());
+ assertEquals( node.getName(), "foo" );
+ assertFalse( Hibernate.isInitialized(node) );
+ tx.commit();
+ s.close();
+
+ assertFetchCount(0);
+ }
+
+ private void clearCounts() {
+ getSessions().getStatistics().clear();
+ }
+
+ private void assertFetchCount(int count) {
+ int fetches = (int) getSessions().getStatistics().getEntityFetchCount();
+ assertEquals(count, fetches);
+ }
+
+ public void configure(Configuration cfg) {
+ cfg.setProperty(Environment.GENERATE_STATISTICS, "true");
+ cfg.setProperty(Environment.STATEMENT_BATCH_SIZE, "0");
+ }
+
+ public String[] getMappings() {
+ return new String[] { "ops/Node.hbm.xml", "ops/Employer.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite(GetLoadTest.class);
+ }
+
+ public String getCacheConcurrencyStrategy() {
+ return null;
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/GetLoadTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/MergeTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/MergeTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/MergeTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,642 @@
+//$Id: MergeTest.java 11037 2007-01-09 16:04:16Z steve.ebersole at jboss.com $
+package org.hibernate.test.ops;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.NonUniqueObjectException;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.criterion.Projections;
+
+/**
+ * @author Gavin King
+ */
+public class MergeTest extends AbstractOperationTestCase {
+
+ public MergeTest(String str) {
+ super( str );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( MergeTest.class );
+ }
+
+ public void testMergeBidiPrimayKeyOneToOne() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ Person p = new Person( "steve" );
+ new PersonalDetails( "I have big feet", p );
+ s.persist( p );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ p.getDetails().setSomePersonalDetail( p.getDetails().getSomePersonalDetail() + " and big hands too" );
+ s = openSession();
+ s.beginTransaction();
+ p = ( Person ) s.merge( p );
+ s.getTransaction().commit();
+ s.close();
+
+ assertInsertCount( 0 );
+ assertUpdateCount( 1 );
+ assertDeleteCount( 0 );
+
+ s = openSession();
+ s.beginTransaction();
+ s.delete( p );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testMergeBidiForeignKeyOneToOne() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ Person p = new Person( "steve" );
+ Address a = new Address( "123 Main", "Austin", "US", p );
+ s.persist( a );
+ s.persist( p );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ p.getAddress().setStreetAddress( "321 Main" );
+ s = openSession();
+ s.beginTransaction();
+ p = ( Person ) s.merge( p );
+ s.getTransaction().commit();
+ s.close();
+
+ assertInsertCount( 0 );
+ assertUpdateCount( 0 ); // no cascade
+ assertDeleteCount( 0 );
+
+ s = openSession();
+ s.beginTransaction();
+ s.delete( a );
+ s.delete( p );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testNoExtraUpdatesOnMerge() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ Node node = new Node( "test" );
+ s.persist( node );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ // node is now detached, but we have made no changes. so attempt to merge it
+ // into this new session; this should cause no updates...
+ s = openSession();
+ s.beginTransaction();
+ node = ( Node ) s.merge( node );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 0 );
+
+ ///////////////////////////////////////////////////////////////////////
+ // as a control measure, now update the node while it is detached and
+ // make sure we get an update as a result...
+ node.setDescription( "new description" );
+ s = openSession();
+ s.beginTransaction();
+ node = ( Node ) s.merge( node );
+ s.getTransaction().commit();
+ s.close();
+ assertUpdateCount( 1 );
+ assertInsertCount( 0 );
+ ///////////////////////////////////////////////////////////////////////
+
+ cleanup();
+ }
+
+ public void testNoExtraUpdatesOnMergeWithCollection() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ Node parent = new Node( "parent" );
+ Node child = new Node( "child" );
+ parent.getChildren().add( child );
+ child.setParent( parent );
+ s.persist( parent );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ // parent is now detached, but we have made no changes. so attempt to merge it
+ // into this new session; this should cause no updates...
+ s = openSession();
+ s.beginTransaction();
+ parent = ( Node ) s.merge( parent );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 0 );
+
+ ///////////////////////////////////////////////////////////////////////
+ // as a control measure, now update the node while it is detached and
+ // make sure we get an update as a result...
+ ( ( Node ) parent.getChildren().iterator().next() ).setDescription( "child's new description" );
+ parent.getChildren().add( new Node( "second child" ) );
+ s = openSession();
+ s.beginTransaction();
+ parent = ( Node ) s.merge( parent );
+ s.getTransaction().commit();
+ s.close();
+ assertUpdateCount( 1 );
+ assertInsertCount( 1 );
+ ///////////////////////////////////////////////////////////////////////
+
+ cleanup();
+ }
+
+ public void testNoExtraUpdatesOnMergeVersioned() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedEntity entity = new VersionedEntity( "entity", "entity" );
+ s.persist( entity );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ // entity is now detached, but we have made no changes. so attempt to merge it
+ // into this new session; this should cause no updates...
+ s = openSession();
+ s.beginTransaction();
+ VersionedEntity mergedEntity = ( VersionedEntity ) s.merge( entity );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 0 );
+ assertEquals( "unexpected version increment", entity.getVersion(), mergedEntity.getVersion() );
+
+
+ ///////////////////////////////////////////////////////////////////////
+ // as a control measure, now update the node while it is detached and
+ // make sure we get an update as a result...
+ entity.setName( "new name" );
+ s = openSession();
+ s.beginTransaction();
+ entity = ( VersionedEntity ) s.merge( entity );
+ s.getTransaction().commit();
+ s.close();
+ assertUpdateCount( 1 );
+ assertInsertCount( 0 );
+ ///////////////////////////////////////////////////////////////////////
+
+ cleanup();
+ }
+
+ public void testNoExtraUpdatesOnMergeVersionedWithCollection() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedEntity parent = new VersionedEntity( "parent", "parent" );
+ VersionedEntity child = new VersionedEntity( "child", "child" );
+ parent.getChildren().add( child );
+ child.setParent( parent );
+ s.persist( parent );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ // parent is now detached, but we have made no changes. so attempt to merge it
+ // into this new session; this should cause no updates...
+ s = openSession();
+ s.beginTransaction();
+ VersionedEntity mergedParent = ( VersionedEntity ) s.merge( parent );
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 0 );
+ assertEquals( "unexpected parent version increment", parent.getVersion(), mergedParent.getVersion() );
+ VersionedEntity mergedChild = ( VersionedEntity ) mergedParent.getChildren().iterator().next();
+ assertEquals( "unexpected child version increment", child.getVersion(), mergedChild.getVersion() );
+
+ ///////////////////////////////////////////////////////////////////////
+ // as a control measure, now update the node while it is detached and
+ // make sure we get an update as a result...
+ mergedParent.setName( "new name" );
+ mergedParent.getChildren().add( new VersionedEntity( "child2", "new child" ) );
+ s = openSession();
+ s.beginTransaction();
+ parent = ( VersionedEntity ) s.merge( mergedParent );
+ s.getTransaction().commit();
+ s.close();
+ assertUpdateCount( 1 );
+ assertInsertCount( 1 );
+ ///////////////////////////////////////////////////////////////////////
+
+ cleanup();
+ }
+
+ public void testPersistThenMergeInSameTxnWithVersion() {
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ VersionedEntity entity = new VersionedEntity( "test", "test" );
+ s.persist( entity );
+ s.merge( new VersionedEntity( "test", "test-2" ) );
+
+ try {
+ // control operation...
+ s.saveOrUpdate( new VersionedEntity( "test", "test-3" ) );
+ fail( "saveOrUpdate() should fail here" );
+ }
+ catch( NonUniqueObjectException expected ) {
+ // expected behavior
+ }
+
+ tx.commit();
+ s.close();
+
+ cleanup();
+ }
+
+ public void testPersistThenMergeInSameTxnWithTimestamp() {
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ TimestampedEntity entity = new TimestampedEntity( "test", "test" );
+ s.persist( entity );
+ s.merge( new TimestampedEntity( "test", "test-2" ) );
+
+ try {
+ // control operation...
+ s.saveOrUpdate( new TimestampedEntity( "test", "test-3" ) );
+ fail( "saveOrUpdate() should fail here" );
+ }
+ catch( NonUniqueObjectException expected ) {
+ // expected behavior
+ }
+
+ tx.commit();
+ s.close();
+
+ cleanup();
+ }
+
+ public void testMergeDeepTree() {
+
+ clearCounts();
+
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ Node root = new Node("root");
+ Node child = new Node("child");
+ Node grandchild = new Node("grandchild");
+ root.addChild(child);
+ child.addChild(grandchild);
+ s.merge(root);
+ tx.commit();
+ s.close();
+
+ assertInsertCount(3);
+ assertUpdateCount(0);
+ clearCounts();
+
+ grandchild.setDescription("the grand child");
+ Node grandchild2 = new Node("grandchild2");
+ child.addChild( grandchild2 );
+
+ s = openSession();
+ tx = s.beginTransaction();
+ s.merge(root);
+ tx.commit();
+ s.close();
+
+ assertInsertCount(1);
+ assertUpdateCount(1);
+ clearCounts();
+
+ Node child2 = new Node("child2");
+ Node grandchild3 = new Node("grandchild3");
+ child2.addChild( grandchild3 );
+ root.addChild(child2);
+
+ s = openSession();
+ tx = s.beginTransaction();
+ s.merge(root);
+ tx.commit();
+ s.close();
+
+ assertInsertCount(2);
+ assertUpdateCount(0);
+ clearCounts();
+
+ s = openSession();
+ tx = s.beginTransaction();
+ s.delete(grandchild);
+ s.delete(grandchild2);
+ s.delete(grandchild3);
+ s.delete(child);
+ s.delete(child2);
+ s.delete(root);
+ tx.commit();
+ s.close();
+
+ }
+
+ public void testMergeDeepTreeWithGeneratedId() {
+
+ clearCounts();
+
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ NumberedNode root = new NumberedNode("root");
+ NumberedNode child = new NumberedNode("child");
+ NumberedNode grandchild = new NumberedNode("grandchild");
+ root.addChild(child);
+ child.addChild(grandchild);
+ root = (NumberedNode) s.merge(root);
+ tx.commit();
+ s.close();
+
+ assertInsertCount(3);
+ assertUpdateCount(0);
+ clearCounts();
+
+ child = (NumberedNode) root.getChildren().iterator().next();
+ grandchild = (NumberedNode) child.getChildren().iterator().next();
+ grandchild.setDescription("the grand child");
+ NumberedNode grandchild2 = new NumberedNode("grandchild2");
+ child.addChild( grandchild2 );
+
+ s = openSession();
+ tx = s.beginTransaction();
+ root = (NumberedNode) s.merge(root);
+ tx.commit();
+ s.close();
+
+ assertInsertCount(1);
+ assertUpdateCount(1);
+ clearCounts();
+
+ getSessions().evict(NumberedNode.class);
+
+ NumberedNode child2 = new NumberedNode("child2");
+ NumberedNode grandchild3 = new NumberedNode("grandchild3");
+ child2.addChild( grandchild3 );
+ root.addChild(child2);
+
+ s = openSession();
+ tx = s.beginTransaction();
+ root = (NumberedNode) s.merge(root);
+ tx.commit();
+ s.close();
+
+ assertInsertCount(2);
+ assertUpdateCount(0);
+ clearCounts();
+
+ s = openSession();
+ tx = s.beginTransaction();
+ s.createQuery("delete from NumberedNode where name like 'grand%'").executeUpdate();
+ s.createQuery("delete from NumberedNode where name like 'child%'").executeUpdate();
+ s.createQuery("delete from NumberedNode").executeUpdate();
+ tx.commit();
+ s.close();
+
+ }
+
+ public void testMergeTree() {
+
+ clearCounts();
+
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ Node root = new Node("root");
+ Node child = new Node("child");
+ root.addChild(child);
+ s.persist(root);
+ tx.commit();
+ s.close();
+
+ assertInsertCount(2);
+ clearCounts();
+
+ root.setDescription("The root node");
+ child.setDescription("The child node");
+
+ Node secondChild = new Node("second child");
+
+ root.addChild(secondChild);
+
+ s = openSession();
+ tx = s.beginTransaction();
+ s.merge(root);
+ tx.commit();
+ s.close();
+
+ assertInsertCount(1);
+ assertUpdateCount(2);
+
+ cleanup();
+ }
+
+ public void testMergeTreeWithGeneratedId() {
+
+ clearCounts();
+
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ NumberedNode root = new NumberedNode("root");
+ NumberedNode child = new NumberedNode("child");
+ root.addChild(child);
+ s.persist(root);
+ tx.commit();
+ s.close();
+
+ assertInsertCount(2);
+ clearCounts();
+
+ root.setDescription("The root node");
+ child.setDescription("The child node");
+
+ NumberedNode secondChild = new NumberedNode("second child");
+
+ root.addChild(secondChild);
+
+ s = openSession();
+ tx = s.beginTransaction();
+ s.merge(root);
+ tx.commit();
+ s.close();
+
+ assertInsertCount(1);
+ assertUpdateCount(2);
+
+ cleanup();
+ }
+
+ public void testMergeManaged() {
+
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ NumberedNode root = new NumberedNode("root");
+ s.persist(root);
+ tx.commit();
+
+ clearCounts();
+
+ tx = s.beginTransaction();
+ NumberedNode child = new NumberedNode("child");
+ root.addChild(child);
+ assertSame( root, s.merge(root) );
+ Object mergedChild = root.getChildren().iterator().next();
+ assertNotSame( mergedChild, child );
+ assertTrue( s.contains(mergedChild) );
+ assertFalse( s.contains(child) );
+ assertEquals( root.getChildren().size(), 1 );
+ assertTrue( root.getChildren().contains(mergedChild) );
+ //assertNotSame( mergedChild, s.merge(child) ); //yucky :(
+ tx.commit();
+
+ assertInsertCount(1);
+ assertUpdateCount(0);
+
+ assertEquals( root.getChildren().size(), 1 );
+ assertTrue( root.getChildren().contains(mergedChild) );
+
+ tx = s.beginTransaction();
+ assertEquals(
+ s.createCriteria(NumberedNode.class)
+ .setProjection( Projections.rowCount() )
+ .uniqueResult(),
+ new Integer(2)
+ );
+
+ s.close();
+
+ cleanup();
+ }
+
+ public void testRecursiveMergeTransient() {
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ Employer jboss = new Employer();
+ Employee gavin = new Employee();
+ jboss.setEmployees( new ArrayList() );
+ jboss.getEmployees().add(gavin);
+ s.merge(jboss);
+ s.flush();
+ jboss = (Employer) s.createQuery("from Employer e join fetch e.employees").uniqueResult();
+ assertTrue( Hibernate.isInitialized( jboss.getEmployees() ) );
+ assertEquals( 1, jboss.getEmployees().size() );
+ s.clear();
+ s.merge( jboss.getEmployees().iterator().next() );
+ tx.commit();
+ s.close();
+
+ cleanup();
+ }
+
+ public void testDeleteAndMerge() throws Exception {
+ Session s = openSession();
+ s.getTransaction().begin();
+ Employer jboss = new Employer();
+ s.persist( jboss );
+ s.getTransaction().commit();
+ s.clear();
+
+ s.getTransaction().begin();
+ Employer otherJboss;
+ otherJboss = (Employer) s.get( Employer.class, jboss.getId() );
+ s.delete( otherJboss );
+ s.getTransaction().commit();
+ s.clear();
+ jboss.setVers( new Integer(1) );
+ s.getTransaction().begin();
+ s.merge( jboss );
+ s.getTransaction().commit();
+ s.close();
+
+ cleanup();
+ }
+
+ public void testMergeManyToManyWithCollectionDeference() throws Exception {
+ // setup base data...
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ Competition competition = new Competition();
+ competition.getCompetitors().add( new Competitor( "Name" ) );
+ competition.getCompetitors().add( new Competitor() );
+ competition.getCompetitors().add( new Competitor() );
+ s.persist( competition );
+ tx.commit();
+ s.close();
+
+ // the competition graph is now detached:
+ // 1) create a new List reference to represent the competitors
+ s = openSession();
+ tx = s.beginTransaction();
+ List newComp = new ArrayList();
+ Competitor originalCompetitor = ( Competitor ) competition.getCompetitors().get( 0 );
+ originalCompetitor.setName( "Name2" );
+ newComp.add( originalCompetitor );
+ newComp.add( new Competitor() );
+ // 2) set that new List reference unto the Competition reference
+ competition.setCompetitors( newComp );
+ // 3) attempt the merge
+ Competition competition2 = ( Competition ) s.merge( competition );
+ tx.commit();
+ s.close();
+
+ assertFalse( competition == competition2 );
+ assertFalse( competition.getCompetitors() == competition2.getCompetitors() );
+ assertEquals( 2, competition2.getCompetitors().size() );
+
+ s = openSession();
+ tx = s.beginTransaction();
+ competition = ( Competition ) s.get( Competition.class, competition.getId() );
+ assertEquals( 2, competition.getCompetitors().size() );
+ s.delete( competition );
+ tx.commit();
+ s.close();
+
+ cleanup();
+ }
+
+ private void cleanup() {
+ Session s = openSession();
+ s.beginTransaction();
+ s.createQuery( "delete from NumberedNode where parent is not null" ).executeUpdate();
+ s.createQuery( "delete from NumberedNode" ).executeUpdate();
+
+ s.createQuery( "delete from Node where parent is not null" ).executeUpdate();
+ s.createQuery( "delete from Node" ).executeUpdate();
+
+ s.createQuery( "delete from VersionedEntity where parent is not null" ).executeUpdate();
+ s.createQuery( "delete from VersionedEntity" ).executeUpdate();
+ s.createQuery( "delete from TimestampedEntity" ).executeUpdate();
+
+ s.createQuery( "delete from Competitor" ).executeUpdate();
+ s.createQuery( "delete from Competition" ).executeUpdate();
+
+ Iterator itr = s.createQuery( "from Employer" ).list().iterator();
+ while ( itr.hasNext() ) {
+ final Employer employer = ( Employer ) itr.next();
+ s.delete( employer );
+ }
+
+ s.getTransaction().commit();
+ s.close();
+ }
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/MergeTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Node.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Node.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Node.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,51 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.ops">
+
+ <class name="Node" polymorphism="explicit">
+ <id name="name">
+ <generator class="assigned"/>
+ </id>
+ <property name="description"/>
+ <many-to-one name="parent"/>
+ <property name="created" not-null="true"/>
+ <set name="children"
+ inverse="true"
+ cascade="persist,merge,save-update,evict">
+ <key column="parent"/>
+ <one-to-many class="Node"/>
+ </set>
+ <set name="cascadingChildren" inverse="false" cascade="persist,merge,save-update,evict,delete">
+ <key column="CASC_PARENT"/>
+ <one-to-many class="Node"/>
+ </set>
+ </class>
+
+ <class name="NumberedNode" polymorphism="explicit">
+ <id name="id" unsaved-value="0">
+ <generator class="native"/>
+ </id>
+ <property name="name">
+ <column name="name" index="iname" not-null="true"/>
+ </property>
+ <property name="description"/>
+ <property name="created" not-null="true"
+ type="imm_date"/>
+ <many-to-one name="parent" class="NumberedNode"/>
+ <set name="children"
+ inverse="true"
+ cascade="persist,merge,save-update">
+ <key column="parent"/>
+ <one-to-many class="NumberedNode"/>
+ </set>
+ </class>
+
+</hibernate-mapping>
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Node.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Node.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Node.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Node.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,88 @@
+//$Id: Node.java 10759 2006-11-08 00:00:53Z steve.ebersole at jboss.com $
+package org.hibernate.test.ops;
+
+import java.sql.Date;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author Gavin King
+ */
+public class Node {
+
+ private String name;
+ private String description;
+ private Date created;
+ private Node parent;
+ private Set children = new HashSet();
+ private Set cascadingChildren = new HashSet();
+
+ public Node() {
+ }
+
+ public Node(String name) {
+ this.name = name;
+ created = generateCurrentDate();
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public Date getCreated() {
+ return created;
+ }
+
+ public void setCreated(Date created) {
+ this.created = created;
+ }
+
+ public Node getParent() {
+ return parent;
+ }
+
+ public void setParent(Node parent) {
+ this.parent = parent;
+ }
+
+ public Set getChildren() {
+ return children;
+ }
+
+ public void setChildren(Set children) {
+ this.children = children;
+ }
+
+ public Node addChild(Node child) {
+ children.add( child );
+ child.setParent( this );
+ return this;
+ }
+
+ public Set getCascadingChildren() {
+ return cascadingChildren;
+ }
+
+ public void setCascadingChildren(Set cascadingChildren) {
+ this.cascadingChildren = cascadingChildren;
+ }
+
+ private Date generateCurrentDate() {
+ // Note : done as java.sql.Date mainly to work around issue with
+ // MySQL and its lack of milli-second precision on its DATETIME
+ // and TIMESTAMP datatypes.
+ return new Date( new java.util.Date().getTime() );
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Node.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/NumberedNode.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/NumberedNode.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/NumberedNode.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,75 @@
+//$Id: NumberedNode.java 7236 2005-06-20 03:19:34Z oneovthafew $
+package org.hibernate.test.ops;
+
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author Gavin King
+ */
+public class NumberedNode {
+
+ private long id;
+ private String name;
+ private NumberedNode parent;
+ private Set children = new HashSet();
+ private String description;
+ private Date created;
+
+ public NumberedNode() {
+ super();
+ }
+
+ public NumberedNode(String name) {
+ this.name = name;
+ created = new Date();
+ }
+
+ public long getId() {
+ return id;
+ }
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public Set getChildren() {
+ return children;
+ }
+ public void setChildren(Set children) {
+ this.children = children;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public NumberedNode getParent() {
+ return parent;
+ }
+ public void setParent(NumberedNode parent) {
+ this.parent = parent;
+ }
+
+ public NumberedNode addChild(NumberedNode child) {
+ children.add(child);
+ child.setParent(this);
+ return this;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public Date getCreated() {
+ return created;
+ }
+
+ public void setCreated(Date created) {
+ this.created = created;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/NumberedNode.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/OneToOne.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/OneToOne.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/OneToOne.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,43 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+ Mappings demonstrating bidirectional one-to-one mappings for testing
+ with various operations.
+
+ Person -> Address is modeled as a bidirectional one to one based on FK.
+ Person -> Details is modeled as a bidirectional one to one based on PK.
+-->
+
+<hibernate-mapping package="org.hibernate.test.ops">
+
+ <class name="Person" table="OPS_PERSON">
+ <id name="id" column="ID" type="long">
+ <generator class="increment"/>
+ </id>
+ <property name="name" column="NAME" type="string"/>
+ <one-to-one name="address" class="Address" property-ref="resident" />
+ <one-to-one name="details" class="PersonalDetails" cascade="all" />
+ </class>
+
+ <class name="Address" table="OPS_ADDRESS">
+ <id name="id" column="ID" type="long">
+ <generator class="increment"/>
+ </id>
+ <property name="streetAddress" column="STREET" type="string" />
+ <property name="city" column="CITY" type="string" />
+ <property name="country" column="CTRY" type="string" />
+ <many-to-one name="resident" column="RESIDENT" class="Person" />
+ </class>
+
+ <class name="PersonalDetails" table="OPS_PERS_DETAIL">
+ <id name="id" column="ID" type="long">
+ <generator class="increment"/>
+ </id>
+ <property name="somePersonalDetail" column="SOME_DETAIL" type="string"/>
+ <one-to-one name="person" class="Person" cascade="none" constrained="true" />
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/OpsSuite.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/OpsSuite.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/OpsSuite.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,21 @@
+package org.hibernate.test.ops;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class OpsSuite {
+ public static Test suite() {
+ TestSuite suite = new TestSuite( "Operations tests" );
+ suite.addTest( CreateTest.suite() );
+ suite.addTest( DeleteTest.suite() );
+ suite.addTest( GetLoadTest.suite() );
+ suite.addTest( MergeTest.suite() );
+ suite.addTest( SaveOrUpdateTest.suite() );
+ return suite;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/OptLockEntity.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/OptLockEntity.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/OptLockEntity.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,36 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.ops">
+
+ <class name="VersionedEntity" table="V_ENTITY">
+ <id name="id" column="ID" type="string">
+ <generator class="assigned"/>
+ </id>
+ <version name="version" column="VERS" type="long" />
+ <property name="name" column="NAME" type="string" />
+ <many-to-one name="parent" class="VersionedEntity"/>
+ <set name="children"
+ inverse="true"
+ cascade="persist,merge,save-update,evict,delete">
+ <key column="parent"/>
+ <one-to-many class="VersionedEntity"/>
+ </set>
+ </class>
+
+ <class name="TimestampedEntity" table="T_ENTITY">
+ <id name="id" column="ID" type="string">
+ <generator class="assigned"/>
+ </id>
+ <timestamp name="timestamp" column="TS" />
+ <property name="name" column="NAME" type="string" />
+ </class>
+
+</hibernate-mapping>
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Person.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Person.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/Person.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,52 @@
+package org.hibernate.test.ops;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class Person {
+ private Long id;
+ private String name;
+ private Address address;
+ private PersonalDetails details;
+
+ public Person() {
+ }
+
+ public Person(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;
+ }
+
+ public Address getAddress() {
+ return address;
+ }
+
+ public void setAddress(Address address) {
+ this.address = address;
+ }
+
+ public PersonalDetails getDetails() {
+ return details;
+ }
+
+ public void setDetails(PersonalDetails details) {
+ this.details = details;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/PersonalDetails.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/PersonalDetails.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/PersonalDetails.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,45 @@
+package org.hibernate.test.ops;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class PersonalDetails {
+ private Long id;
+ private String somePersonalDetail;
+ private Person person;
+
+ public PersonalDetails() {
+ }
+
+ public PersonalDetails(String somePersonalDetail, Person person) {
+ this.somePersonalDetail = somePersonalDetail;
+ this.person = person;
+ person.setDetails( this );
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getSomePersonalDetail() {
+ return somePersonalDetail;
+ }
+
+ public void setSomePersonalDetail(String somePersonalDetail) {
+ this.somePersonalDetail = somePersonalDetail;
+ }
+
+ public Person getPerson() {
+ return person;
+ }
+
+ public void setPerson(Person person) {
+ this.person = person;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/SaveOrUpdateTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/SaveOrUpdateTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/SaveOrUpdateTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,448 @@
+//$Id: SaveOrUpdateTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.ops;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.intercept.FieldInterceptionHelper;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.criterion.Projections;
+
+/**
+ * @author Gavin King
+ */
+public class SaveOrUpdateTest extends FunctionalTestCase {
+
+ public SaveOrUpdateTest(String str) {
+ super( str );
+ }
+
+ public void testSaveOrUpdateDeepTree() {
+ clearCounts();
+
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ Node root = new Node( "root" );
+ Node child = new Node( "child" );
+ Node grandchild = new Node( "grandchild" );
+ root.addChild( child );
+ child.addChild( grandchild );
+ s.saveOrUpdate( root );
+ tx.commit();
+ s.close();
+
+ assertInsertCount( 3 );
+ assertUpdateCount( 0 );
+ clearCounts();
+
+ grandchild.setDescription( "the grand child" );
+ Node grandchild2 = new Node( "grandchild2" );
+ child.addChild( grandchild2 );
+
+ s = openSession();
+ tx = s.beginTransaction();
+ s.saveOrUpdate( root );
+ tx.commit();
+ s.close();
+
+ assertInsertCount( 1 );
+ assertUpdateCount( 1 );
+ clearCounts();
+
+ Node child2 = new Node( "child2" );
+ Node grandchild3 = new Node( "grandchild3" );
+ child2.addChild( grandchild3 );
+ root.addChild( child2 );
+
+ s = openSession();
+ tx = s.beginTransaction();
+ s.saveOrUpdate( root );
+ tx.commit();
+ s.close();
+
+ assertInsertCount( 2 );
+ assertUpdateCount( 0 );
+ clearCounts();
+
+ s = openSession();
+ tx = s.beginTransaction();
+ s.delete( grandchild );
+ s.delete( grandchild2 );
+ s.delete( grandchild3 );
+ s.delete( child );
+ s.delete( child2 );
+ s.delete( root );
+ tx.commit();
+ s.close();
+ }
+
+ public void testSaveOrUpdateDeepTreeWithGeneratedId() {
+ boolean instrumented = FieldInterceptionHelper.isInstrumented( new NumberedNode() );
+ clearCounts();
+
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ NumberedNode root = new NumberedNode( "root" );
+ NumberedNode child = new NumberedNode( "child" );
+ NumberedNode grandchild = new NumberedNode( "grandchild" );
+ root.addChild( child );
+ child.addChild( grandchild );
+ s.saveOrUpdate( root );
+ tx.commit();
+ s.close();
+
+ assertInsertCount( 3 );
+ assertUpdateCount( 0 );
+ clearCounts();
+
+ child = ( NumberedNode ) root.getChildren().iterator().next();
+ grandchild = ( NumberedNode ) child.getChildren().iterator().next();
+ grandchild.setDescription( "the grand child" );
+ NumberedNode grandchild2 = new NumberedNode( "grandchild2" );
+ child.addChild( grandchild2 );
+
+ s = openSession();
+ tx = s.beginTransaction();
+ s.saveOrUpdate( root );
+ tx.commit();
+ s.close();
+
+ assertInsertCount( 1 );
+ assertUpdateCount( instrumented ? 1 : 3 );
+ clearCounts();
+
+ NumberedNode child2 = new NumberedNode( "child2" );
+ NumberedNode grandchild3 = new NumberedNode( "grandchild3" );
+ child2.addChild( grandchild3 );
+ root.addChild( child2 );
+
+ s = openSession();
+ tx = s.beginTransaction();
+ s.saveOrUpdate( root );
+ tx.commit();
+ s.close();
+
+ assertInsertCount( 2 );
+ assertUpdateCount( instrumented ? 0 : 4 );
+ clearCounts();
+
+ s = openSession();
+ tx = s.beginTransaction();
+ s.createQuery( "delete from NumberedNode where name like 'grand%'" ).executeUpdate();
+ s.createQuery( "delete from NumberedNode where name like 'child%'" ).executeUpdate();
+ s.createQuery( "delete from NumberedNode" ).executeUpdate();
+ tx.commit();
+ s.close();
+ }
+
+ public void testSaveOrUpdateTree() {
+ clearCounts();
+
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ Node root = new Node( "root" );
+ Node child = new Node( "child" );
+ root.addChild( child );
+ s.saveOrUpdate( root );
+ tx.commit();
+ s.close();
+
+ assertInsertCount( 2 );
+ clearCounts();
+
+ root.setDescription( "The root node" );
+ child.setDescription( "The child node" );
+
+ Node secondChild = new Node( "second child" );
+
+ root.addChild( secondChild );
+
+ s = openSession();
+ tx = s.beginTransaction();
+ s.saveOrUpdate( root );
+ tx.commit();
+ s.close();
+
+ assertInsertCount( 1 );
+ assertUpdateCount( 2 );
+
+ s = openSession();
+ tx = s.beginTransaction();
+ s.createQuery( "delete from Node where parent is not null" ).executeUpdate();
+ s.createQuery( "delete from Node" ).executeUpdate();
+ tx.commit();
+ s.close();
+ }
+
+ public void testSaveOrUpdateTreeWithGeneratedId() {
+ clearCounts();
+
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ NumberedNode root = new NumberedNode( "root" );
+ NumberedNode child = new NumberedNode( "child" );
+ root.addChild( child );
+ s.saveOrUpdate( root );
+ tx.commit();
+ s.close();
+
+ assertInsertCount( 2 );
+ clearCounts();
+
+ root.setDescription( "The root node" );
+ child.setDescription( "The child node" );
+
+ NumberedNode secondChild = new NumberedNode( "second child" );
+
+ root.addChild( secondChild );
+
+ s = openSession();
+ tx = s.beginTransaction();
+ s.saveOrUpdate( root );
+ tx.commit();
+ s.close();
+
+ assertInsertCount( 1 );
+ assertUpdateCount( 2 );
+
+ s = openSession();
+ tx = s.beginTransaction();
+ s.createQuery( "delete from NumberedNode where parent is not null" ).executeUpdate();
+ s.createQuery( "delete from NumberedNode" ).executeUpdate();
+ tx.commit();
+ s.close();
+ }
+
+ public void testSaveOrUpdateManaged() {
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ NumberedNode root = new NumberedNode( "root" );
+ s.saveOrUpdate( root );
+ tx.commit();
+
+ tx = s.beginTransaction();
+ NumberedNode child = new NumberedNode( "child" );
+ root.addChild( child );
+ s.saveOrUpdate( root );
+ assertFalse( s.contains( child ) );
+ s.flush();
+ assertTrue( s.contains( child ) );
+ tx.commit();
+
+ assertTrue( root.getChildren().contains( child ) );
+ assertEquals( root.getChildren().size(), 1 );
+
+ tx = s.beginTransaction();
+ assertEquals(
+ s.createCriteria( NumberedNode.class )
+ .setProjection( Projections.rowCount() )
+ .uniqueResult(),
+ new Integer( 2 )
+ );
+ s.delete( root );
+ s.delete( child );
+ tx.commit();
+ s.close();
+ }
+
+
+ public void testSaveOrUpdateGot() {
+ boolean instrumented = FieldInterceptionHelper.isInstrumented( new NumberedNode() );
+
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ NumberedNode root = new NumberedNode( "root" );
+ s.saveOrUpdate( root );
+ tx.commit();
+ s.close();
+
+ assertInsertCount( 1 );
+ assertUpdateCount( 0 );
+ clearCounts();
+
+ s = openSession();
+ tx = s.beginTransaction();
+ s.saveOrUpdate( root );
+ tx.commit();
+ s.close();
+
+ assertInsertCount( 0 );
+ assertUpdateCount( instrumented ? 0 : 1 );
+
+ s = openSession();
+ tx = s.beginTransaction();
+ root = ( NumberedNode ) s.get( NumberedNode.class, new Long( root.getId() ) );
+ Hibernate.initialize( root.getChildren() );
+ tx.commit();
+ s.close();
+
+ clearCounts();
+
+ s = openSession();
+ tx = s.beginTransaction();
+ NumberedNode child = new NumberedNode( "child" );
+ root.addChild( child );
+ s.saveOrUpdate( root );
+ assertTrue( s.contains( child ) );
+ tx.commit();
+
+ assertInsertCount( 1 );
+ assertUpdateCount( instrumented ? 0 : 1 );
+
+ tx = s.beginTransaction();
+ assertEquals(
+ s.createCriteria( NumberedNode.class )
+ .setProjection( Projections.rowCount() )
+ .uniqueResult(),
+ new Integer( 2 )
+ );
+ s.delete( root );
+ s.delete( child );
+ tx.commit();
+ s.close();
+ }
+
+ public void testSaveOrUpdateGotWithMutableProp() {
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ Node root = new Node( "root" );
+ s.saveOrUpdate( root );
+ tx.commit();
+ s.close();
+
+ assertInsertCount( 1 );
+ assertUpdateCount( 0 );
+ clearCounts();
+
+ s = openSession();
+ tx = s.beginTransaction();
+ s.saveOrUpdate( root );
+ tx.commit();
+ s.close();
+
+ assertInsertCount( 0 );
+ assertUpdateCount( 0 );
+
+ s = openSession();
+ tx = s.beginTransaction();
+ root = ( Node ) s.get( Node.class, "root" );
+ Hibernate.initialize( root.getChildren() );
+ tx.commit();
+ s.close();
+
+ clearCounts();
+
+ s = openSession();
+ tx = s.beginTransaction();
+ Node child = new Node( "child" );
+ root.addChild( child );
+ s.saveOrUpdate( root );
+ assertTrue( s.contains( child ) );
+ tx.commit();
+
+ assertInsertCount( 1 );
+ assertUpdateCount( 1 ); //note: will fail here if no second-level cache
+
+ tx = s.beginTransaction();
+ assertEquals(
+ s.createCriteria( Node.class )
+ .setProjection( Projections.rowCount() )
+ .uniqueResult(),
+ new Integer( 2 )
+ );
+ s.delete( root );
+ s.delete( child );
+ tx.commit();
+ s.close();
+ }
+
+ public void testEvictThenSaveOrUpdate() {
+ Session s = openSession();
+ s.getTransaction().begin();
+ Node parent = new Node( "1:parent" );
+ Node child = new Node( "2:child" );
+ Node grandchild = new Node( "3:grandchild" );
+ parent.addChild( child );
+ child.addChild( grandchild );
+ s.saveOrUpdate( parent );
+ s.getTransaction().commit();
+ s.close();
+
+ Session s1 = openSession();
+ s1.getTransaction().begin();
+ child = ( Node ) s1.load( Node.class, "2:child" );
+ assertTrue( s1.contains( child ) );
+ assertFalse( Hibernate.isInitialized( child ) );
+ assertTrue( s1.contains( child.getParent() ) );
+ assertTrue( Hibernate.isInitialized( child ) );
+ assertFalse( Hibernate.isInitialized( child.getChildren() ) );
+ assertFalse( Hibernate.isInitialized( child.getParent() ) );
+ assertTrue( s1.contains( child ) );
+ s1.evict( child );
+ assertFalse( s1.contains( child ) );
+ assertTrue( s1.contains( child.getParent() ) );
+
+ Session s2 = openSession();
+ s2.getTransaction().begin();
+ s2.saveOrUpdate( child );
+ assertTrue( s2.contains( child ) );
+ assertFalse( s1.contains( child ) );
+ assertTrue( s2.contains( child.getParent() ) );
+ assertFalse( s1.contains( child.getParent() ) );
+ assertFalse( Hibernate.isInitialized( child.getChildren() ) );
+ assertFalse( Hibernate.isInitialized( child.getParent() ) );
+ assertEquals( 1, child.getChildren().size() );
+ assertEquals( "1:parent", child.getParent().getName() );
+ assertTrue( Hibernate.isInitialized( child.getChildren() ) );
+ assertFalse( Hibernate.isInitialized( child.getParent() ) );
+ assertNull( child.getParent().getDescription() );
+ assertTrue( Hibernate.isInitialized( child.getParent() ) );
+
+ s1.getTransaction().commit();
+ s2.getTransaction().commit();
+ s1.close();
+ s2.close();
+
+ s = openSession();
+ s.beginTransaction();
+ s.delete( s.get( Node.class, "3:grandchild" ) );
+ s.delete( s.get( Node.class, "2:child" ) );
+ s.delete( s.get( Node.class, "1:parent" ) );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ private void clearCounts() {
+ getSessions().getStatistics().clear();
+ }
+
+ private void assertInsertCount(int count) {
+ int inserts = ( int ) getSessions().getStatistics().getEntityInsertCount();
+ assertEquals( count, inserts );
+ }
+
+ private void assertUpdateCount(int count) {
+ int updates = ( int ) getSessions().getStatistics().getEntityUpdateCount();
+ assertEquals( count, updates );
+ }
+
+ public void configure(Configuration cfg) {
+ cfg.setProperty( Environment.GENERATE_STATISTICS, "true" );
+ cfg.setProperty( Environment.STATEMENT_BATCH_SIZE, "0" );
+ }
+
+ public String[] getMappings() {
+ return new String[] {"ops/Node.hbm.xml"};
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( SaveOrUpdateTest.class );
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/SaveOrUpdateTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/TimestampedEntity.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/TimestampedEntity.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/TimestampedEntity.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,47 @@
+package org.hibernate.test.ops;
+
+import java.util.Date;
+
+/**
+ * todo: describe TimestampedEntity
+ *
+ * @author Steve Ebersole
+ */
+public class TimestampedEntity {
+ private String id;
+ private String name;
+ private Date timestamp;
+
+ public TimestampedEntity() {
+ }
+
+ public TimestampedEntity(String id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Date getTimestamp() {
+ return timestamp;
+ }
+
+ public void setTimestamp(Date timestamp) {
+ this.timestamp = timestamp;
+ }
+}
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/VersionedEntity.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/VersionedEntity.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ops/VersionedEntity.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,66 @@
+package org.hibernate.test.ops;
+
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * VersionedEntity
+ *
+ * @author Steve Ebersole
+ */
+public class VersionedEntity {
+ private String id;
+ private String name;
+ private long version;
+
+ private VersionedEntity parent;
+ private Set children = new HashSet();
+
+ public VersionedEntity() {
+ }
+
+ public VersionedEntity(String id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public long getVersion() {
+ return version;
+ }
+
+ public void setVersion(long version) {
+ this.version = version;
+ }
+
+ public VersionedEntity getParent() {
+ return parent;
+ }
+
+ public void setParent(VersionedEntity parent) {
+ this.parent = parent;
+ }
+
+ public Set getChildren() {
+ return children;
+ }
+
+ public void setChildren(Set children) {
+ this.children = children;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/optlock/Document.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/optlock/Document.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/optlock/Document.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,47 @@
+<?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 the use of dirty-property optimistic locking.
+ We do not recommend this approach, since it does not work
+ for detached entities. Use version-based optimistic locking.
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.optlock">
+
+ <class name="Document" entity-name="LockDirty" table="Document" optimistic-lock="dirty" dynamic-update="true">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="title"/>
+ <property name="author"/>
+ <component name="pubDate">
+ <property name="year" not-null="true"/>
+ <property name="month"/>
+ </component>
+ <property name="summary"/>
+ <property name="totalSales" optimistic-lock="false"/>
+ <property name="text" column="`text`"/>
+ </class>
+
+ <class name="Document" entity-name="LockAll" table="Document" optimistic-lock="all" dynamic-update="true">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="title"/>
+ <property name="author"/>
+ <component name="pubDate">
+ <property name="year" not-null="true"/>
+ <property name="month"/>
+ </component>
+ <property name="summary"/>
+ <property name="totalSales" optimistic-lock="false"/>
+ <property name="text" column="`text`"/>
+ </class>
+
+</hibernate-mapping>
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/optlock/Document.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/optlock/Document.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/optlock/Document.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/optlock/Document.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,87 @@
+//$Id: Document.java 7552 2005-07-19 18:51:24Z oneovthafew $
+package org.hibernate.test.optlock;
+
+/**
+ * @author Gavin King
+ */
+public class Document {
+ private Long id;
+ private String author;
+ private String title;
+ private String summary;
+ private String text;
+ private PublicationDate pubDate;
+ private int totalSales;
+ /**
+ * @return Returns the author.
+ */
+ public String getAuthor() {
+ return author;
+ }
+ /**
+ * @param author The author to set.
+ */
+ public void setAuthor(String author) {
+ this.author = author;
+ }
+ /**
+ * @return Returns the id.
+ */
+ public Long getId() {
+ return id;
+ }
+ /**
+ * @param id The id to set.
+ */
+ public void setId(Long id) {
+ this.id = id;
+ }
+ /**
+ * @return Returns the summary.
+ */
+ public String getSummary() {
+ return summary;
+ }
+ /**
+ * @param summary The summary to set.
+ */
+ public void setSummary(String summary) {
+ this.summary = summary;
+ }
+ /**
+ * @return Returns the text.
+ */
+ public String getText() {
+ return text;
+ }
+ /**
+ * @param text The text to set.
+ */
+ public void setText(String text) {
+ this.text = text;
+ }
+ /**
+ * @return Returns the title.
+ */
+ public String getTitle() {
+ return title;
+ }
+ /**
+ * @param title The title to set.
+ */
+ public void setTitle(String title) {
+ this.title = title;
+ }
+ public PublicationDate getPubDate() {
+ return pubDate;
+ }
+ public void setPubDate(PublicationDate pubDate) {
+ this.pubDate = pubDate;
+ }
+ public int getTotalSales() {
+ return totalSales;
+ }
+ public void setTotalSales(int totalSales) {
+ this.totalSales = totalSales;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/optlock/Document.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/optlock/OptimisticLockTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/optlock/OptimisticLockTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/optlock/OptimisticLockTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,183 @@
+//$Id: OptimisticLockTest.java 11303 2007-03-19 22:06:14Z steve.ebersole at jboss.com $
+package org.hibernate.test.optlock;
+
+import junit.framework.Test;
+
+import org.hibernate.JDBCException;
+import org.hibernate.Session;
+import org.hibernate.StaleObjectStateException;
+import org.hibernate.StaleStateException;
+import org.hibernate.dialect.SQLServerDialect;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * Tests relating to the optimisitc-lock mapping option.
+ *
+ * @author Gavin King
+ * @author Steve Ebersole
+ */
+public class OptimisticLockTest extends FunctionalTestCase {
+
+ public OptimisticLockTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "optlock/Document.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( OptimisticLockTest.class );
+ }
+
+ public void testOptimisticLockDirty() {
+ testUpdateOptimisticLockFailure( "LockDirty" );
+ }
+
+ public void testOptimisticLockAll() {
+ testUpdateOptimisticLockFailure( "LockAll" );
+ }
+
+ public void testOptimisticLockDirtyDelete() {
+ testDeleteOptimisticLockFailure( "LockDirty" );
+ }
+
+ public void testOptimisticLockAllDelete() {
+ testDeleteOptimisticLockFailure( "LockAll" );
+ }
+
+ private void testUpdateOptimisticLockFailure(String entityName) {
+ if ( getDialect().doesRepeatableReadCauseReadersToBlockWriters() ) {
+ reportSkip( "deadlock", "update optimistic locking" );
+ return;
+ }
+ Session mainSession = openSession();
+ mainSession.beginTransaction();
+ Document doc = new Document();
+ doc.setTitle( "Hibernate in Action" );
+ doc.setAuthor( "Bauer et al" );
+ doc.setSummary( "Very boring book about persistence" );
+ doc.setText( "blah blah yada yada yada" );
+ doc.setPubDate( new PublicationDate( 2004 ) );
+ mainSession.save( entityName, doc );
+ mainSession.getTransaction().commit();
+ mainSession.close();
+
+ mainSession = openSession();
+ mainSession.beginTransaction();
+ doc = ( Document ) mainSession.get( entityName, doc.getId() );
+
+ Session otherSession = getSessions().openSession();
+ otherSession.beginTransaction();
+ Document otherDoc = ( Document ) otherSession.get( entityName, doc.getId() );
+ otherDoc.setSummary( "A modern classic" );
+ otherSession.getTransaction().commit();
+ otherSession.close();
+
+ try {
+ doc.setSummary( "A machiavelian achievement of epic proportions" );
+ mainSession.flush();
+ fail( "expecting opt lock failure" );
+ }
+ catch ( StaleObjectStateException expected ) {
+ // expected result...
+ }
+ catch( StaleStateException expected ) {
+ // expected result (if using versioned batching)...
+ }
+ catch( JDBCException e ) {
+ // SQLServer will report this condition via a SQLException
+ // when using its SNAPSHOT transaction isolation...
+ if ( ! ( getDialect() instanceof SQLServerDialect && e.getErrorCode() == 3960 ) ) {
+ throw e;
+ }
+ else {
+ // it seems to "lose track" of the transaction as well...
+ mainSession.getTransaction().rollback();
+ mainSession.beginTransaction();
+ }
+ }
+ mainSession.clear();
+ mainSession.getTransaction().commit();
+ mainSession.close();
+
+ mainSession = openSession();
+ mainSession.beginTransaction();
+ doc = ( Document ) mainSession.load( entityName, doc.getId() );
+ mainSession.delete( entityName, doc );
+ mainSession.getTransaction().commit();
+ mainSession.close();
+ }
+
+ private void testDeleteOptimisticLockFailure(String entityName) {
+ if ( getDialect().doesRepeatableReadCauseReadersToBlockWriters() ) {
+ reportSkip( "deadlock", "delete optimistic locking" );
+ return;
+ }
+ Session mainSession = openSession();
+ mainSession.beginTransaction();
+ Document doc = new Document();
+ doc.setTitle( "Hibernate in Action" );
+ doc.setAuthor( "Bauer et al" );
+ doc.setSummary( "Very boring book about persistence" );
+ doc.setText( "blah blah yada yada yada" );
+ doc.setPubDate( new PublicationDate( 2004 ) );
+ mainSession.save( entityName, doc );
+ mainSession.flush();
+ doc.setSummary( "A modern classic" );
+ mainSession.flush();
+ doc.getPubDate().setMonth( new Integer( 3 ) );
+ mainSession.flush();
+ mainSession.getTransaction().commit();
+ mainSession.close();
+
+ mainSession = openSession();
+ mainSession.beginTransaction();
+ doc = ( Document ) mainSession.get( entityName, doc.getId() );
+
+ Session otherSession = openSession();
+ otherSession.beginTransaction();
+ Document otherDoc = ( Document ) otherSession.get( entityName, doc.getId() );
+ otherDoc.setSummary( "my other summary" );
+ otherSession.flush();
+ otherSession.getTransaction().commit();
+ otherSession.close();
+
+ try {
+ mainSession.delete( doc );
+ mainSession.flush();
+ fail( "expecting opt lock failure" );
+ }
+ catch ( StaleObjectStateException e ) {
+ // expected
+ }
+ catch( StaleStateException expected ) {
+ // expected result (if using versioned batching)...
+ }
+ catch( JDBCException e ) {
+ // SQLServer will report this condition via a SQLException
+ // when using its SNAPSHOT transaction isolation...
+ if ( ! ( getDialect() instanceof SQLServerDialect && e.getErrorCode() == 3960 ) ) {
+ throw e;
+ }
+ else {
+ // it seems to "lose track" of the transaction as well...
+ mainSession.getTransaction().rollback();
+ mainSession.beginTransaction();
+ }
+ }
+ mainSession.clear();
+ mainSession.getTransaction().commit();
+ mainSession.close();
+
+ mainSession = openSession();
+ mainSession.beginTransaction();
+ doc = ( Document ) mainSession.load( entityName, doc.getId() );
+ mainSession.delete( entityName, doc );
+ mainSession.getTransaction().commit();
+ mainSession.close();
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/optlock/OptimisticLockTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/optlock/PublicationDate.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/optlock/PublicationDate.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/optlock/PublicationDate.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,26 @@
+//$Id: PublicationDate.java 7556 2005-07-19 23:22:27Z oneovthafew $
+package org.hibernate.test.optlock;
+
+public class PublicationDate {
+ private int year;
+ private Integer month;
+
+ public PublicationDate(int year) {
+ this.year = year;
+ }
+
+ PublicationDate() {}
+
+ public Integer getMonth() {
+ return month;
+ }
+ public void setMonth(Integer month) {
+ this.month = month;
+ }
+ public int getYear() {
+ return year;
+ }
+ public void setYear(int year) {
+ this.year = year;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/optlock/PublicationDate.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ordered/OrderByTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ordered/OrderByTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ordered/OrderByTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,88 @@
+//$Id: OrderByTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.ordered;
+
+import java.util.Iterator;
+
+import junit.framework.Test;
+
+import org.hibernate.FetchMode;
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class OrderByTest extends FunctionalTestCase {
+
+ public OrderByTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "ordered/Search.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( OrderByTest.class );
+ }
+
+ public void testOrderBy() {
+ Search s = new Search("Hibernate");
+ s.getSearchResults().add("jboss.com");
+ s.getSearchResults().add("hibernate.org");
+ s.getSearchResults().add("HiA");
+
+ Session sess = openSession();
+ Transaction tx = sess.beginTransaction();
+ sess.persist(s);
+ sess.flush();
+
+ sess.clear();
+ s = (Search) sess.createCriteria(Search.class).uniqueResult();
+ assertFalse( Hibernate.isInitialized( s.getSearchResults() ) );
+ Iterator iter = s.getSearchResults().iterator();
+ assertEquals( iter.next(), "HiA" );
+ assertEquals( iter.next(), "hibernate.org" );
+ assertEquals( iter.next(), "jboss.com" );
+ assertFalse( iter.hasNext() );
+
+ sess.clear();
+ s = (Search) sess.createCriteria(Search.class)
+ .setFetchMode("searchResults", FetchMode.JOIN)
+ .uniqueResult();
+ assertTrue( Hibernate.isInitialized( s.getSearchResults() ) );
+ iter = s.getSearchResults().iterator();
+ assertEquals( iter.next(), "HiA" );
+ assertEquals( iter.next(), "hibernate.org" );
+ assertEquals( iter.next(), "jboss.com" );
+ assertFalse( iter.hasNext() );
+
+ sess.clear();
+ s = (Search) sess.createQuery("from Search s left join fetch s.searchResults")
+ .uniqueResult();
+ assertTrue( Hibernate.isInitialized( s.getSearchResults() ) );
+ iter = s.getSearchResults().iterator();
+ assertEquals( iter.next(), "HiA" );
+ assertEquals( iter.next(), "hibernate.org" );
+ assertEquals( iter.next(), "jboss.com" );
+ assertFalse( iter.hasNext() );
+
+ /*sess.clear();
+ s = (Search) sess.createCriteria(Search.class).uniqueResult();
+ assertFalse( Hibernate.isInitialized( s.getSearchResults() ) );
+ iter = sess.createFilter( s.getSearchResults(), "").iterate();
+ assertEquals( iter.next(), "HiA" );
+ assertEquals( iter.next(), "hibernate.org" );
+ assertEquals( iter.next(), "jboss.com" );
+ assertFalse( iter.hasNext() );*/
+
+ sess.delete(s);
+ tx.commit();
+ sess.close();
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ordered/OrderByTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ordered/Search.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ordered/Search.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ordered/Search.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.ordered">
+
+ <class name="Search">
+ <id name="searchString"/>
+ <set name="searchResults" order-by="lower(text) asc">
+ <key column="searchString"/>
+ <element column="text" type="string"/>
+ </set>
+ </class>
+
+</hibernate-mapping>
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ordered/Search.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ordered/Search.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ordered/Search.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ordered/Search.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,29 @@
+//$Id: Search.java 7772 2005-08-05 23:03:46Z oneovthafew $
+package org.hibernate.test.ordered;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class Search {
+ private String searchString;
+ private Set searchResults = new HashSet();
+
+ Search() {}
+
+ public Search(String string) {
+ searchString = string;
+ }
+
+ public Set getSearchResults() {
+ return searchResults;
+ }
+ public void setSearchResults(Set searchResults) {
+ this.searchResults = searchResults;
+ }
+ public String getSearchString() {
+ return searchString;
+ }
+ public void setSearchString(String searchString) {
+ this.searchString = searchString;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ordered/Search.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/Mail.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/Mail.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/Mail.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE hibernate-mapping PUBLIC
+ '-//Hibernate/Hibernate Mapping DTD 3.0//EN'
+ 'http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd'>
+
+<hibernate-mapping package="org.hibernate.test.orphan" >
+
+ <class name="Mail" table="t_mail">
+
+ <id name="id" type="integer" column="id">
+ <generator class="native" />
+ </id>
+
+ <property name="alias">
+ <column name="alias" length="20"/>
+ </property>
+
+ <many-to-one name="user"
+ class="User"
+ cascade="save-update"
+ column="userid"
+ not-null="true"
+ property-ref="userid"/>
+
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/Mail.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/Mail.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/Mail.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,41 @@
+package org.hibernate.test.orphan;
+
+public class Mail {
+
+ private Integer id;
+ private String alias;
+ private User user;
+
+ /*package*/ Mail() {
+ }
+
+ /*package*/ Mail(String alias, User user) {
+ this.alias = alias;
+ this.user = user;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ protected void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getAlias() {
+ return alias;
+ }
+
+ public void setAlias(String alias) {
+ this.alias = alias;
+ }
+
+ public User getUser() {
+ return user;
+ }
+
+ public void setUser(User user) {
+ this.user = user;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/OrphanSuite.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/OrphanSuite.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/OrphanSuite.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,18 @@
+package org.hibernate.test.orphan;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class OrphanSuite {
+ public static Test suite() {
+ TestSuite suite = new TestSuite( "orphan delete suite" );
+ suite.addTest( OrphanTest.suite() );
+ suite.addTest( PropertyRefTest.suite() );
+ return suite;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/OrphanTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/OrphanTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/OrphanTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,299 @@
+//$Id: OrphanTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.orphan;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.LockMode;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.util.SerializationHelper;
+
+/**
+ * @author Gavin King
+ */
+public class OrphanTest extends FunctionalTestCase {
+
+ public OrphanTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "orphan/Product.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( OrphanTest.class );
+ }
+
+ public void testOrphanDeleteOnDelete() {
+ Session session = openSession();
+ Transaction t = session.beginTransaction();
+ Product prod = new Product();
+ prod.setName("Widget");
+ Part part = new Part();
+ part.setName("Widge");
+ part.setDescription("part if a Widget");
+ prod.getParts().add(part);
+ Part part2 = new Part();
+ part2.setName("Get");
+ part2.setDescription("another part if a Widget");
+ prod.getParts().add(part2);
+ session.persist(prod);
+ session.flush();
+
+ prod.getParts().remove(part);
+
+ session.delete(prod);
+
+ t.commit();
+ session.close();
+
+ session = openSession();
+ t = session.beginTransaction();
+ assertNull( session.get(Part.class, "Widge") );
+ assertNull( session.get(Part.class, "Get") );
+ assertNull( session.get(Product.class, "Widget") );
+ t.commit();
+ session.close();
+ }
+
+ public void testOrphanDeleteAfterPersist() {
+ Session session = openSession();
+ Transaction t = session.beginTransaction();
+ Product prod = new Product();
+ prod.setName("Widget");
+ Part part = new Part();
+ part.setName("Widge");
+ part.setDescription("part if a Widget");
+ prod.getParts().add(part);
+ Part part2 = new Part();
+ part2.setName("Get");
+ part2.setDescription("another part if a Widget");
+ prod.getParts().add(part2);
+ session.persist(prod);
+
+ prod.getParts().remove(part);
+
+ t.commit();
+ session.close();
+
+ session = openSession();
+ t = session.beginTransaction();
+ assertNull( session.get(Part.class, "Widge") );
+ assertNotNull( session.get(Part.class, "Get") );
+ session.delete( session.get(Product.class, "Widget") );
+ t.commit();
+ session.close();
+ }
+
+ public void testOrphanDeleteAfterPersistAndFlush() {
+ Session session = openSession();
+ Transaction t = session.beginTransaction();
+ Product prod = new Product();
+ prod.setName("Widget");
+ Part part = new Part();
+ part.setName("Widge");
+ part.setDescription("part if a Widget");
+ prod.getParts().add(part);
+ Part part2 = new Part();
+ part2.setName("Get");
+ part2.setDescription("another part if a Widget");
+ prod.getParts().add(part2);
+ session.persist(prod);
+ session.flush();
+
+ prod.getParts().remove(part);
+
+ t.commit();
+ session.close();
+
+ session = openSession();
+ t = session.beginTransaction();
+ assertNull( session.get(Part.class, "Widge") );
+ assertNotNull( session.get(Part.class, "Get") );
+ session.delete( session.get(Product.class, "Widget") );
+ t.commit();
+ session.close();
+ }
+
+ public void testOrphanDeleteAfterLock() {
+ Session session = openSession();
+ Transaction t = session.beginTransaction();
+ Product prod = new Product();
+ prod.setName("Widget");
+ Part part = new Part();
+ part.setName("Widge");
+ part.setDescription("part if a Widget");
+ prod.getParts().add(part);
+ Part part2 = new Part();
+ part2.setName("Get");
+ part2.setDescription("another part if a Widget");
+ prod.getParts().add(part2);
+ session.persist(prod);
+ t.commit();
+ session.close();
+
+
+ session = openSession();
+ t = session.beginTransaction();
+ session.lock(prod, LockMode.READ);
+ prod.getParts().remove(part);
+ t.commit();
+ session.close();
+
+ session = openSession();
+ t = session.beginTransaction();
+ assertNull( session.get(Part.class, "Widge") );
+ assertNotNull( session.get(Part.class, "Get") );
+ session.delete( session.get(Product.class, "Widget") );
+ t.commit();
+ session.close();
+ }
+
+ public void testOrphanDeleteOnSaveOrUpdate() {
+ Session session = openSession();
+ Transaction t = session.beginTransaction();
+ Product prod = new Product();
+ prod.setName("Widget");
+ Part part = new Part();
+ part.setName("Widge");
+ part.setDescription("part if a Widget");
+ prod.getParts().add(part);
+ Part part2 = new Part();
+ part2.setName("Get");
+ part2.setDescription("another part if a Widget");
+ prod.getParts().add(part2);
+ session.persist(prod);
+ t.commit();
+ session.close();
+
+ prod.getParts().remove(part);
+
+ session = openSession();
+ t = session.beginTransaction();
+ session.saveOrUpdate(prod);
+ t.commit();
+ session.close();
+
+ session = openSession();
+ t = session.beginTransaction();
+ assertNull( session.get(Part.class, "Widge") );
+ assertNotNull( session.get(Part.class, "Get") );
+ session.delete( session.get(Product.class, "Widget") );
+ t.commit();
+ session.close();
+ }
+
+ public void testOrphanDeleteOnSaveOrUpdateAfterSerialization() {
+ Session session = openSession();
+ Transaction t = session.beginTransaction();
+ Product prod = new Product();
+ prod.setName("Widget");
+ Part part = new Part();
+ part.setName("Widge");
+ part.setDescription("part if a Widget");
+ prod.getParts().add(part);
+ Part part2 = new Part();
+ part2.setName("Get");
+ part2.setDescription("another part if a Widget");
+ prod.getParts().add(part2);
+ session.persist(prod);
+ t.commit();
+ session.close();
+
+ prod.getParts().remove(part);
+
+ prod = (Product) SerializationHelper.clone(prod);
+
+ session = openSession();
+ t = session.beginTransaction();
+ session.saveOrUpdate(prod);
+ t.commit();
+ session.close();
+
+ session = openSession();
+ t = session.beginTransaction();
+ assertNull( session.get(Part.class, "Widge") );
+ assertNotNull( session.get(Part.class, "Get") );
+ session.delete( session.get(Product.class, "Widget") );
+ t.commit();
+ session.close();
+ }
+
+ public void testOrphanDelete() {
+ Session session = openSession();
+ Transaction t = session.beginTransaction();
+ Product prod = new Product();
+ prod.setName("Widget");
+ Part part = new Part();
+ part.setName("Widge");
+ part.setDescription("part if a Widget");
+ prod.getParts().add(part);
+ Part part2 = new Part();
+ part2.setName("Get");
+ part2.setDescription("another part if a Widget");
+ prod.getParts().add(part2);
+ session.persist(prod);
+ t.commit();
+ session.close();
+
+ getSessions().evict(Product.class);
+ getSessions().evict(Part.class);
+
+ session = openSession();
+ t = session.beginTransaction();
+ prod = (Product) session.get(Product.class, "Widget");
+ assertTrue( Hibernate.isInitialized( prod.getParts() ) );
+ part = (Part) session.get(Part.class, "Widge");
+ prod.getParts().remove(part);
+ t.commit();
+ session.close();
+
+ session = openSession();
+ t = session.beginTransaction();
+ assertNull( session.get(Part.class, "Widge") );
+ assertNotNull( session.get(Part.class, "Get") );
+ session.delete( session.get(Product.class, "Widget") );
+ t.commit();
+ session.close();
+ }
+
+ public void testOrphanDeleteOnMerge() {
+ Session session = openSession();
+ Transaction t = session.beginTransaction();
+ Product prod = new Product();
+ prod.setName("Widget");
+ Part part = new Part();
+ part.setName("Widge");
+ part.setDescription("part if a Widget");
+ prod.getParts().add(part);
+ Part part2 = new Part();
+ part2.setName("Get");
+ part2.setDescription("another part if a Widget");
+ prod.getParts().add(part2);
+ session.persist(prod);
+ t.commit();
+ session.close();
+
+ prod.getParts().remove(part);
+
+ session = openSession();
+ t = session.beginTransaction();
+ session.merge(prod);
+ t.commit();
+ session.close();
+
+ session = openSession();
+ t = session.beginTransaction();
+ assertNull( session.get(Part.class, "Widge") );
+ assertNotNull( session.get(Part.class, "Get") );
+ session.delete( session.get(Product.class, "Widget") );
+ t.commit();
+ session.close();
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/OrphanTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/Part.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/Part.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/Part.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,24 @@
+//$Id: Part.java 5725 2005-02-14 12:10:15Z oneovthafew $
+package org.hibernate.test.orphan;
+
+import java.io.Serializable;
+
+/**
+ * @author Gavin King
+ */
+public class Part implements Serializable {
+ private String name;
+ private String description;
+ public String getDescription() {
+ return description;
+ }
+ public void setDescription(String description) {
+ this.description = description;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/Part.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/Product.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/Product.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/Product.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -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">
+
+<!--
+
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.orphan">
+
+ <class name="Product" table="t_product">
+ <id name="name"/>
+ <set name="parts" cascade="all,delete-orphan" fetch="join">
+ <key column="productName" not-null="true"/>
+ <one-to-many class="Part"/>
+ </set>
+ </class>
+
+ <class name="Part" table="t_part">
+ <id name="name"/>
+ <property name="description" not-null="true"/>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/Product.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/Product.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/Product.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/Product.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,26 @@
+//$Id: Product.java 5725 2005-02-14 12:10:15Z oneovthafew $
+package org.hibernate.test.orphan;
+
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author Gavin King
+ */
+public class Product implements Serializable {
+ private String name;
+ private Set parts = new HashSet();
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public Set getParts() {
+ return parts;
+ }
+ public void setParts(Set parts) {
+ this.parts = parts;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/Product.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/PropertyRefTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/PropertyRefTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/PropertyRefTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,56 @@
+// $Id: PropertyRefTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.orphan;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+
+/**
+ * Test for HHH-565
+ *
+ * @author Steve Ebersole
+ */
+public class PropertyRefTest extends FunctionalTestCase {
+
+ public PropertyRefTest(String name) {
+ super( name );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "orphan/User.hbm.xml", "orphan/Mail.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( PropertyRefTest.class );
+ }
+
+ public void testDeleteParentWithBidirOrphanDeleteCollectionBasedOnPropertyRef() {
+ Session session = openSession();
+ Transaction txn = session.beginTransaction();
+ User user = new User( "test" );
+ user.addMail( "test" );
+ user.addMail( "test" );
+ session.save( user );
+ txn.commit();
+ session.close();
+
+ session = openSession();
+ txn = session.beginTransaction();
+ user = ( User ) session.load( User.class, user.getId() );
+ session.delete( user );
+ txn.commit();
+ session.close();
+
+ session = openSession();
+ txn = session.beginTransaction();
+ session.createQuery( "delete from Mail where alias = :alias" ).setString( "alias", "test" ).executeUpdate();
+ session.createQuery( "delete from User where userid = :userid" ).setString( "userid", "test" ).executeUpdate();
+ txn.commit();
+ session.close();
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/User.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/User.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/User.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE hibernate-mapping PUBLIC
+ '-//Hibernate/Hibernate Mapping DTD 3.0//EN'
+ 'http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd'>
+
+<hibernate-mapping package="org.hibernate.test.orphan" >
+
+ <class name="User" table="t_user">
+
+ <id name="id" type="integer" column="id">
+ <generator class="native" />
+ </id>
+
+ <property name="userid">
+ <column name="userid" length="32" not-null="true"/>
+ </property>
+
+ <set name="mail" table="mail" cascade="all, delete-orphan" inverse="true" lazy="false">
+ <key column="userid" property-ref="userid" not-null="false"/>
+ <one-to-many class="Mail"/>
+ </set>
+
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/User.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/User.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/orphan/User.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,62 @@
+/*
+ * User.java
+ *
+ * Created on May 3, 2005, 9:42 AM
+ */
+
+package org.hibernate.test.orphan;
+
+import java.util.HashSet;
+import java.util.Set;
+
+
+/**
+ * @author Kevin
+ */
+public class User {
+
+ private Integer id;
+ private String userid;
+ private Set mail = new HashSet();
+
+ public User() {
+ }
+
+ public User(String userid) {
+ this.userid = userid;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ protected void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getUserid() {
+ return userid;
+ }
+
+ public void setUserid(String userid) {
+ this.userid = userid;
+ }
+
+ public Set getMail() {
+ return mail;
+ }
+
+ private void setMail(Set mail) {
+ this.mail = mail;
+ }
+
+ public Mail addMail(String alias) {
+ Mail mail = new Mail( alias, this );
+ getMail().add( mail );
+ return mail;
+ }
+
+ public void removeMail(Mail mail) {
+ getMail().remove( mail );
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/pagination/DataPoint.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/pagination/DataPoint.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/pagination/DataPoint.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping
+ package="org.hibernate.test.pagination">
+
+ <class name="DataPoint"
+ dynamic-update="true">
+ <!--rowid="rowid"--> <!-- remove this if not oracle -->
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="x">
+ <column name="xval" not-null="true" precision="4" unique-key="xy"/>
+ </property>
+ <property name="y">
+ <column name="yval" not-null="true" precision="4" unique-key="xy"/>
+ </property>
+ <property name="description"/>
+ </class>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/pagination/DataPoint.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/pagination/DataPoint.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/pagination/DataPoint.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/pagination/DataPoint.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,62 @@
+//$Id: DataPoint.java 7867 2005-08-11 23:35:33Z oneovthafew $
+package org.hibernate.test.pagination;
+
+import java.math.BigDecimal;
+
+/**
+ * @author Gavin King
+ */
+public class DataPoint {
+ private long id;
+ private BigDecimal x;
+ private BigDecimal y;
+ private String description;
+ /**
+ * @return Returns the description.
+ */
+ public String getDescription() {
+ return description;
+ }
+ /**
+ * @param description The description to set.
+ */
+ public void setDescription(String description) {
+ this.description = description;
+ }
+ /**
+ * @return Returns the id.
+ */
+ public long getId() {
+ return id;
+ }
+ /**
+ * @param id The id to set.
+ */
+ public void setId(long id) {
+ this.id = id;
+ }
+ /**
+ * @return Returns the x.
+ */
+ public BigDecimal getX() {
+ return x;
+ }
+ /**
+ * @param x The x to set.
+ */
+ public void setX(BigDecimal x) {
+ this.x = x;
+ }
+ /**
+ * @return Returns the y.
+ */
+ public BigDecimal getY() {
+ return y;
+ }
+ /**
+ * @param y The y to set.
+ */
+ public void setY(BigDecimal y) {
+ this.y = y;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/pagination/DataPoint.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/pagination/PaginationTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/pagination/PaginationTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/pagination/PaginationTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,76 @@
+//$Id: PaginationTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.pagination;
+
+import java.math.BigDecimal;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.criterion.Order;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class PaginationTest extends FunctionalTestCase {
+
+ public PaginationTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "pagination/DataPoint.hbm.xml" };
+ }
+
+ public void configure(Configuration cfg) {
+ cfg.setProperty(Environment.STATEMENT_BATCH_SIZE, "20");
+ }
+
+ public String getCacheConcurrencyStrategy() {
+ return null;
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( PaginationTest.class );
+ }
+
+ public void testPagination() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ for ( int i=0; i<10; i++ ) {
+ DataPoint dp = new DataPoint();
+ dp.setX( new BigDecimal(i * 0.1d).setScale(19, BigDecimal.ROUND_DOWN) );
+ dp.setY( new BigDecimal( Math.cos( dp.getX().doubleValue() ) ).setScale(19, BigDecimal.ROUND_DOWN) );
+ s.persist(dp);
+ }
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ int size = s.createSQLQuery("select id, xval, yval, description from DataPoint order by xval, yval")
+ .addEntity(DataPoint.class)
+ .setMaxResults(5)
+ .list().size();
+ assertEquals(size, 5);
+ size = s.createQuery("from DataPoint order by x, y")
+ .setFirstResult(5)
+ .setMaxResults(2)
+ .list().size();
+ assertEquals(size, 2);
+ size = s.createCriteria(DataPoint.class)
+ .addOrder( Order.asc("x") )
+ .addOrder( Order.asc("y") )
+ .setFirstResult(8)
+ .list().size();
+ assertEquals(size, 2);
+ t.commit();
+ s.close();
+
+ }
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/pagination/PaginationTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/pretty/SQLFormatterTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/pretty/SQLFormatterTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/pretty/SQLFormatterTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,43 @@
+package org.hibernate.test.pretty;
+
+import java.util.StringTokenizer;
+
+import org.hibernate.pretty.Formatter;
+import org.hibernate.junit.UnitTestCase;
+
+public class SQLFormatterTest extends UnitTestCase {
+
+ public SQLFormatterTest(String string) {
+ super( string );
+ }
+
+ public void testNoLoss() {
+ assertNoLoss("insert into Address (city, state, zip, \"from\") values (?, ?, ?, 'insert value')");
+ assertNoLoss("delete from Address where id = ? and version = ?");
+ assertNoLoss("update Address set city = ?, state=?, zip=?, version = ? where id = ? and version = ?");
+ assertNoLoss("update Address set city = ?, state=?, zip=?, version = ? where id in (select aid from Person)");
+ assertNoLoss("select p.name, a.zipCode, count(*) from Person p left outer join Employee e on e.id = p.id and p.type = 'E' and (e.effective>? or e.effective<?) join Address a on a.pid = p.id where upper(p.name) like 'G%' and p.age > 100 and (p.sex = 'M' or p.sex = 'F') and coalesce( trim(a.street), a.city, (a.zip) ) is not null order by p.name asc, a.zipCode asc");
+ assertNoLoss("select ( (m.age - p.age) * 12 ), trim(upper(p.name)) from Person p, Person m where p.mother = m.id and ( p.age = (select max(p0.age) from Person p0 where (p0.mother=m.id)) and p.name like ? )");
+ assertNoLoss("select * from Address a join Person p on a.pid = p.id, Person m join Address b on b.pid = m.id where p.mother = m.id and p.name like ?");
+ assertNoLoss("select case when p.age > 50 then 'old' when p.age > 18 then 'adult' else 'child' end from Person p where ( case when p.age > 50 then 'old' when p.age > 18 then 'adult' else 'child' end ) like ?");
+ assertNoLoss("/* Here we' go! */ select case when p.age > 50 then 'old' when p.age > 18 then 'adult' else 'child' end from Person p where ( case when p.age > 50 then 'old' when p.age > 18 then 'adult' else 'child' end ) like ?");
+ }
+
+ private void assertNoLoss(String query) {
+ String formattedQuery = new Formatter(query).format();
+ StringTokenizer formatted = new StringTokenizer(formattedQuery," \t\n\r\f()");
+ StringTokenizer plain = new StringTokenizer(query," \t\n\r\f()");
+
+ System.out.println("Original: " + query);
+ System.out.println("Formatted: " + formattedQuery);
+ while(formatted.hasMoreTokens() && plain.hasMoreTokens()) {
+ String plainToken = plain.nextToken();
+ String formattedToken = formatted.nextToken();
+ assertEquals("formatter did not return the same token",plainToken, formattedToken);
+ }
+ assertFalse(formatted.hasMoreTokens());
+ assertFalse(plain.hasMoreTokens());
+ }
+
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/PropertyRefSuite.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/PropertyRefSuite.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/PropertyRefSuite.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,29 @@
+package org.hibernate.test.propertyref;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.hibernate.test.propertyref.basic.PropertyRefTest;
+import org.hibernate.test.propertyref.component.complete.CompleteComponentPropertyRefTest;
+import org.hibernate.test.propertyref.component.partial.PartialComponentPropertyRefTest;
+import org.hibernate.test.propertyref.inheritence.discrim.SubclassPropertyRefTest;
+import org.hibernate.test.propertyref.inheritence.joined.JoinedSubclassPropertyRefTest;
+import org.hibernate.test.propertyref.inheritence.union.UnionSubclassPropertyRefTest;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class PropertyRefSuite {
+ public static Test suite() {
+ TestSuite suite = new TestSuite( "property-ref suite" );
+ suite.addTest( PropertyRefTest.suite() );
+ suite.addTest( CompleteComponentPropertyRefTest.suite() );
+ suite.addTest( PartialComponentPropertyRefTest.suite() );
+ suite.addTest( SubclassPropertyRefTest.suite() );
+ suite.addTest( JoinedSubclassPropertyRefTest.suite() );
+ suite.addTest( UnionSubclassPropertyRefTest.suite() );
+ return suite;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/basic/Account.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/basic/Account.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/basic/Account.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,48 @@
+//$Id: Account.java 4399 2004-08-21 08:43:20Z oneovthafew $
+package org.hibernate.test.propertyref.basic;
+
+/**
+ * @author Gavin King
+ */
+public class Account {
+ private String accountId;
+ private Person user;
+ private char type;
+ /**
+ * @return Returns the user.
+ */
+ public Person getUser() {
+ return user;
+ }
+ /**
+ * @param user The user to set.
+ */
+ public void setUser(Person user) {
+ this.user = user;
+ }
+ /**
+ * @return Returns the accountId.
+ */
+ public String getAccountId() {
+ return accountId;
+ }
+ /**
+ * @param accountId The accountId to set.
+ */
+ public void setAccountId(String accountId) {
+ this.accountId = accountId;
+ }
+ /**
+ * @return Returns the type.
+ */
+ public char getType() {
+ return type;
+ }
+ /**
+ * @param type The type to set.
+ */
+ public void setType(char type) {
+ this.type = type;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/basic/Address.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/basic/Address.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/basic/Address.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,73 @@
+//$Id: Address.java 4390 2004-08-20 07:54:48Z oneovthafew $
+package org.hibernate.test.propertyref.basic;
+
+/**
+ * @author gavin
+ */
+public class Address {
+ private Long id;
+ private String address;
+ private String zip;
+ private String country;
+ private Person person;
+ /**
+ * @return Returns the id.
+ */
+ public Long getId() {
+ return id;
+ }
+ /**
+ * @param id The id to set.
+ */
+ public void setId(Long id) {
+ this.id = id;
+ }
+ /**
+ * @return Returns the person.
+ */
+ public Person getPerson() {
+ return person;
+ }
+ /**
+ * @param person The person to set.
+ */
+ public void setPerson(Person person) {
+ this.person = person;
+ }
+ /**
+ * @return Returns the address.
+ */
+ public String getAddress() {
+ return address;
+ }
+ /**
+ * @param address The address to set.
+ */
+ public void setAddress(String address) {
+ this.address = address;
+ }
+ /**
+ * @return Returns the country.
+ */
+ public String getCountry() {
+ return country;
+ }
+ /**
+ * @param country The country to set.
+ */
+ public void setCountry(String country) {
+ this.country = country;
+ }
+ /**
+ * @return Returns the zip.
+ */
+ public String getZip() {
+ return zip;
+ }
+ /**
+ * @param zip The zip to set.
+ */
+ public void setZip(String zip) {
+ this.zip = zip;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/basic/Group.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/basic/Group.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/basic/Group.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,23 @@
+//$Id: Group.java 7589 2005-07-21 01:56:42Z oneovthafew $
+package org.hibernate.test.propertyref.basic;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class Group {
+ private String name;
+ private Set users = new HashSet();
+
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public Set getUsers() {
+ return users;
+ }
+ public void setUsers(Set users) {
+ this.users = users;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/basic/Person.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/basic/Person.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/basic/Person.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,66 @@
+<?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 the use of property-ref to map legacy data where
+ foreign keys reference something other than the primary key of
+ the associated entity. Here we show:
+
+ (1) A one-to-one foreign key association (prefer primary key
+ associations)
+
+ (2) A bidirectional one-to-many association on a key that is
+ not the primary key (prefer associations from foreign keys
+ to primary keys)
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.propertyref.basic">
+
+ <class name="Person" table="PROPREF_PERS">
+ <id name="id">
+ <generator class="hilo"/>
+ </id>
+ <property name="name" length="100"/>
+ <property name="userId" column="person_userid" length="8" unique="true"/>
+ <one-to-one name="address" property-ref="person" cascade="all" fetch="join"/>
+ <set name="accounts" inverse="true">
+ <key column="userId" property-ref="userId"/>
+ <one-to-many class="Account"/>
+ </set>
+ <bag name="systems" table="USER_SYSTEM" lazy="true" inverse="false" cascade="all">
+ <key column="USER_ID" property-ref="userId" />
+ <element type="string" column="SYSTEM" />
+ </bag>
+ </class>
+
+ <class name="Address" table="PROPREF_ADDR">
+ <id name="id">
+ <generator class="hilo"/>
+ </id>
+ <property name="address" length="300"/>
+ <property name="zip" length="5"/>
+ <property name="country" length="25"/>
+ <many-to-one name="person" unique="true" not-null="true"/>
+ </class>
+
+ <class name="Account" table="PROPREF_ACCT">
+ <id name="accountId" length="32">
+ <generator class="uuid.hex"/>
+ </id>
+ <many-to-one name="user" column="userId" property-ref="userId"/>
+ <property name="type" not-null="true"/>
+ </class>
+
+ <class name="Group" table="PROPREF_GRP">
+ <id name="name"/>
+ <set name="users" table="PROPREF_USERGROUP" cascade="save-update">
+ <key column="groupName"/>
+ <many-to-many column="userId" class="Person" property-ref="userId"/>
+ </set>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/basic/Person.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/basic/Person.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/basic/Person.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,88 @@
+//$Id: Person.java 10396 2006-09-01 08:48:02 -0500 (Fri, 01 Sep 2006) steve.ebersole at jboss.com $
+package org.hibernate.test.propertyref.basic;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * @author gavin
+ */
+public class Person {
+ private Long id;
+ private String name;
+ private Address address;
+ private String userId;
+ private Set accounts = new HashSet();
+ private List systems = new ArrayList();
+
+ /**
+ * @return Returns the userId.
+ */
+ public String getUserId() {
+ return userId;
+ }
+ /**
+ * @param userId The userId to set.
+ */
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
+ /**
+ * @return Returns the address.
+ */
+ public Address getAddress() {
+ return address;
+ }
+ /**
+ * @param address The address to set.
+ */
+ public void setAddress(Address address) {
+ this.address = address;
+ }
+ /**
+ * @return Returns the id.
+ */
+ public Long getId() {
+ return id;
+ }
+ /**
+ * @param id The id to set.
+ */
+ public void setId(Long id) {
+ this.id = id;
+ }
+ /**
+ * @return Returns the name.
+ */
+ public String getName() {
+ return name;
+ }
+ /**
+ * @param name The name to set.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+ /**
+ * @return Returns the accounts.
+ */
+ public Set getAccounts() {
+ return accounts;
+ }
+ /**
+ * @param accounts The accounts to set.
+ */
+ public void setAccounts(Set accounts) {
+ this.accounts = accounts;
+ }
+
+ public List getSystems() {
+ return systems;
+ }
+
+ public void setSystems(List systems) {
+ this.systems = systems;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/basic/PropertyRefTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/basic/PropertyRefTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/basic/PropertyRefTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,269 @@
+//$Id: PropertyRefTest.java 10396 2006-09-01 08:48:02 -0500 (Fri, 01 Sep 2006) steve.ebersole at jboss.com $
+package org.hibernate.test.propertyref.basic;
+
+import java.util.Iterator;
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.hibernate.FetchMode;
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.mapping.Column;
+import org.hibernate.mapping.ForeignKey;
+import org.hibernate.mapping.PersistentClass;
+
+/**
+ * @author Gavin King
+ */
+public class PropertyRefTest extends FunctionalTestCase {
+
+ public PropertyRefTest(String name) {
+ super( name );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "propertyref/basic/Person.hbm.xml" };
+ }
+
+ public void configure(Configuration cfg) {
+ cfg.setProperty(Environment.DEFAULT_BATCH_FETCH_SIZE, "1");
+ cfg.setProperty(Environment.GENERATE_STATISTICS, "true");
+ }
+
+ public String getCacheConcurrencyStrategy() {
+ return null;
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( PropertyRefTest.class );
+ }
+
+ public void testNonLazyBagKeyPropertyRef() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Person p = new Person();
+ p.setName( "Steve" );
+ p.setUserId( "steve" );
+ p.getSystems().add( "QA" );
+ p.getSystems().add( "R&D" );
+ s.persist( p );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.createQuery( "from Person" ).list();
+ s.clear();
+ s.createSQLQuery( "select {p.*} from PROPREF_PERS {p}" )
+ .addEntity( "p", Person.class.getName() )
+ .list();
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ List results = s.createQuery( "from Person" ).list();
+ Iterator itr = results.iterator();
+ while ( itr.hasNext() ) {
+ s.delete( itr.next() );
+ }
+ t.commit();
+ s.close();
+ }
+
+ public void testManyToManyPropertyRef() {
+ // prepare some test data relating to the Group->Person many-to-many association
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Person p = new Person();
+ p.setName( "Steve" );
+ p.setUserId( "steve" );
+ s.persist( p );
+ Group g = new Group();
+ g.setName( "Admins" );
+ g.getUsers().add( p );
+ s.persist( g );
+ // force a flush and detachment here to test reattachment handling of the property-ref (HHH-1531)
+ t.commit();
+ s.close();
+
+ Person p2 = new Person();
+ p2.setName( "Max" );
+ p2.setUserId( "max" );
+ g.getUsers().add( p2 );
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.update( g );
+ t.commit();
+ s.close();
+
+ // test retrieval of the group
+ s = openSession();
+ t = s.beginTransaction();
+ g = ( Group ) s.createQuery( "from Group g left join fetch g.users" ).uniqueResult();
+ assertTrue( Hibernate.isInitialized( g.getUsers() ) );
+ assertEquals( 2, g.getUsers().size() );
+ s.delete( g );
+ s.createQuery( "delete Person" ).executeUpdate();
+ t.commit();
+ s.close();
+ }
+
+ public void testOneToOnePropertyRef() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Person p = new Person();
+ p.setName("Steve");
+ p.setUserId("steve");
+ Address a = new Address();
+ a.setAddress("Texas");
+ a.setCountry("USA");
+ p.setAddress(a);
+ a.setPerson(p);
+ s.save(p);
+ Person p2 = new Person();
+ p2.setName("Max");
+ p2.setUserId("max");
+ s.save(p2);
+ Account act = new Account();
+ act.setType('c');
+ act.setUser(p2);
+ p2.getAccounts().add(act);
+ s.save(act);
+ s.flush();
+ s.clear();
+
+ p = (Person) s.get( Person.class, p.getId() ); //get address reference by outer join
+ p2 = (Person) s.get( Person.class, p2.getId() ); //get null address reference by outer join
+ assertNull( p2.getAddress() );
+ assertNotNull( p.getAddress() );
+ List l = s.createQuery("from Person").list(); //pull address references for cache
+ assertEquals( l.size(), 2 );
+ assertTrue( l.contains(p) && l.contains(p2) );
+ s.clear();
+
+ l = s.createQuery("from Person p order by p.name").list(); //get address references by sequential selects
+ assertEquals( l.size(), 2 );
+ assertNull( ( (Person) l.get(0) ).getAddress() );
+ assertNotNull( ( (Person) l.get(1) ).getAddress() );
+ s.clear();
+
+ l = s.createQuery("from Person p left join fetch p.address a order by a.country").list(); //get em by outer join
+ assertEquals( l.size(), 2 );
+ if ( ( (Person) l.get(0) ).getName().equals("Max") ) {
+ assertNull( ( (Person) l.get(0) ).getAddress() );
+ assertNotNull( ( (Person) l.get(1) ).getAddress() );
+ }
+ else {
+ assertNull( ( (Person) l.get(1) ).getAddress() );
+ assertNotNull( ( (Person) l.get(0) ).getAddress() );
+ }
+ s.clear();
+
+ l = s.createQuery("from Person p left join p.accounts a").list();
+ for ( int i=0; i<2; i++ ) {
+ Object[] row = (Object[]) l.get(i);
+ Person px = (Person) row[0];
+ assertFalse( Hibernate.isInitialized( px.getAccounts() ) );
+ assertTrue( px.getAccounts().size()>0 || row[1]==null );
+ }
+ s.clear();
+
+ l = s.createQuery("from Person p left join fetch p.accounts a order by p.name").list();
+ Person p0 = (Person) l.get(0);
+ assertTrue( Hibernate.isInitialized( p0.getAccounts() ) );
+ assertEquals( p0.getAccounts().size(), 1 );
+ assertSame( ( (Account) p0.getAccounts().iterator().next() ).getUser(), p0 );
+ Person p1 = (Person) l.get(1);
+ assertTrue( Hibernate.isInitialized( p1.getAccounts() ) );
+ assertEquals( p1.getAccounts().size(), 0 );
+ s.clear();
+ Account acc = (Account) s.createQuery("from Account a left join fetch a.user").uniqueResult();
+ assertTrue( Hibernate.isInitialized(acc.getUser()) );
+ assertNotNull(acc.getUser());
+ assertTrue( acc.getUser().getAccounts().contains(acc) );
+
+ s.createQuery("delete from Address").executeUpdate();
+ s.createQuery("delete from Account").executeUpdate(); // to not break constraint violation between Person and Account
+ s.createQuery("delete from Person").executeUpdate();
+
+ t.commit();
+ s.close();
+ }
+
+
+ public void testJoinFetchPropertyRef() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Person p = new Person();
+ p.setName("Steve");
+ p.setUserId("steve");
+ Address a = new Address();
+ a.setAddress("Texas");
+ a.setCountry("USA");
+ p.setAddress(a);
+ a.setPerson(p);
+ s.save(p);
+
+ s.flush();
+ s.clear();
+
+ getSessions().getStatistics().clear();
+
+ p = (Person) s.get( Person.class, p.getId() ); //get address reference by outer join
+
+ assertTrue( Hibernate.isInitialized( p.getAddress() ) );
+ assertNotNull( p.getAddress() );
+ assertEquals( getSessions().getStatistics().getPrepareStatementCount(), 1 );
+ assertEquals( getSessions().getStatistics().getEntityFetchCount(), 0 );
+
+ s.clear();
+
+ getSessions().getStatistics().clear();
+
+ p = (Person) s.createCriteria(Person.class)
+ .setFetchMode("address", FetchMode.SELECT)
+ .uniqueResult(); //get address reference by select
+
+ assertTrue( Hibernate.isInitialized( p.getAddress() ) );
+ assertNotNull( p.getAddress() );
+ assertEquals( getSessions().getStatistics().getPrepareStatementCount(), 2 );
+ assertEquals( getSessions().getStatistics().getEntityFetchCount(), 0 );
+
+ s.createQuery("delete from Address").executeUpdate();
+ s.createQuery("delete from Person").executeUpdate();
+
+ t.commit();
+ s.close();
+ }
+
+ public void testForeignKeyCreation() {
+ PersistentClass classMapping = getCfg().getClassMapping("org.hibernate.test.propertyref.basic.Account");
+
+ Iterator foreignKeyIterator = classMapping.getTable().getForeignKeyIterator();
+ boolean found = false;
+ while ( foreignKeyIterator.hasNext() ) {
+ ForeignKey element = (ForeignKey) foreignKeyIterator.next();
+ if(element.getReferencedEntityName().equals(Person.class.getName() ) ) {
+
+ if(!element.isReferenceToPrimaryKey() ) {
+ List referencedColumns = element.getReferencedColumns();
+ Column column = (Column) referencedColumns.get(0);
+ if(column.getName().equals("person_userid") ) {
+ found = true; // extend test to include the columns
+ }
+ }
+ }
+ }
+
+ assertTrue("Property ref foreign key not found",found);
+ }
+}
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/complete/Account.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/complete/Account.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/complete/Account.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,23 @@
+package org.hibernate.test.propertyref.component.complete;
+
+
+public class Account {
+ private String number;
+ private Person owner;
+
+ public Person getOwner() {
+ return owner;
+ }
+
+ public void setOwner(Person owner) {
+ this.owner = owner;
+ }
+
+ public String getNumber() {
+ return number;
+ }
+
+ public void setNumber(String number) {
+ this.number = number;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/complete/CompleteComponentPropertyRefTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/complete/CompleteComponentPropertyRefTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/complete/CompleteComponentPropertyRefTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,71 @@
+package org.hibernate.test.propertyref.component.complete;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class CompleteComponentPropertyRefTest extends FunctionalTestCase {
+
+ public CompleteComponentPropertyRefTest(String name) {
+ super( name );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "propertyref/component/complete/Mapping.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( CompleteComponentPropertyRefTest.class );
+ }
+
+ public void testComponentPropertyRef() {
+ Person p = new Person();
+ p.setIdentity( new Identity() );
+ Account a = new Account();
+ a.setNumber("123-12345-1236");
+ a.setOwner(p);
+ p.getIdentity().setName("Gavin");
+ p.getIdentity().setSsn("123-12-1234");
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ s.persist(p);
+ s.persist(a);
+ s.flush();
+ s.clear();
+
+ a = (Account) s.createQuery("from Account a left join fetch a.owner").uniqueResult();
+ assertTrue( Hibernate.isInitialized( a.getOwner() ) );
+ assertNotNull( a.getOwner() );
+ assertEquals( "Gavin", a.getOwner().getIdentity().getName() );
+ s.clear();
+
+ a = (Account) s.get(Account.class, "123-12345-1236");
+ assertFalse( Hibernate.isInitialized( a.getOwner() ) );
+ assertNotNull( a.getOwner() );
+ assertEquals( "Gavin", a.getOwner().getIdentity().getName() );
+ assertTrue( Hibernate.isInitialized( a.getOwner() ) );
+
+ s.clear();
+
+ getSessions().evict(Account.class);
+ getSessions().evict(Person.class);
+
+ a = (Account) s.get(Account.class, "123-12345-1236");
+ assertTrue( Hibernate.isInitialized( a.getOwner() ) );
+ assertNotNull( a.getOwner() );
+ assertEquals( "Gavin", a.getOwner().getIdentity().getName() );
+ assertTrue( Hibernate.isInitialized( a.getOwner() ) );
+
+ s.delete( a );
+ s.delete( a.getOwner() );
+ tx.commit();
+ s.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/complete/Identity.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/complete/Identity.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/complete/Identity.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,21 @@
+package org.hibernate.test.propertyref.component.complete;
+
+import java.io.Serializable;
+
+public class Identity implements Serializable {
+ private String name;
+ private String ssn;
+
+ public String getSsn() {
+ return ssn;
+ }
+ public void setSsn(String id) {
+ this.ssn = id;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/complete/Mapping.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/complete/Mapping.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/complete/Mapping.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.propertyref.component.complete">
+
+ <class name="Person" table="COMP_COMP_PROPREF_PERSON">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <component name="identity" class="Identity">
+ <property name="name"/>
+ <property name="ssn" unique="true"/>
+ </component>
+ </class>
+
+ <class name="Account" table="COMP_COMP_PROPREF_ACCT">
+ <id name="number" column="accnum"/>
+ <many-to-one name="owner" property-ref="identity">
+ <column name="OWNER_NAME" />
+ <column name="OWNER_SSN" />
+ </many-to-one>
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/complete/Person.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/complete/Person.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/complete/Person.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,20 @@
+package org.hibernate.test.propertyref.component.complete;
+
+
+public class Person {
+ private Long id;
+ private Identity identity;
+
+ public Long getId() {
+ return id;
+ }
+ public void setId(Long id) {
+ this.id = id;
+ }
+ public Identity getIdentity() {
+ return identity;
+ }
+ public void setIdentity(Identity identity) {
+ this.identity = identity;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/partial/Account.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/partial/Account.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/partial/Account.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,23 @@
+//$Id: Account.java 7587 2005-07-21 01:22:38Z oneovthafew $
+package org.hibernate.test.propertyref.component.partial;
+
+public class Account {
+ private String number;
+ private Person owner;
+
+ public Person getOwner() {
+ return owner;
+ }
+
+ public void setOwner(Person owner) {
+ this.owner = owner;
+ }
+
+ public String getNumber() {
+ return number;
+ }
+
+ public void setNumber(String number) {
+ this.number = number;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/partial/Identity.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/partial/Identity.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/partial/Identity.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,20 @@
+//$Id: Identity.java 7587 2005-07-21 01:22:38Z oneovthafew $
+package org.hibernate.test.propertyref.component.partial;
+
+public class Identity {
+ private String name;
+ private String ssn;
+
+ public String getSsn() {
+ return ssn;
+ }
+ public void setSsn(String id) {
+ this.ssn = id;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/partial/Mapping.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/partial/Mapping.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/partial/Mapping.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -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">
+
+<!--
+
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.propertyref.component.partial">
+
+ <class name="Person" table="PART_COMP_PROPREF_PERS">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <component name="identity">
+ <property name="name"/>
+ <property name="ssn" unique="true"/>
+ </component>
+ </class>
+
+ <class name="Account" table="PART_COMP_PROPREF_ACCT">
+ <id name="number" column="accnum"/>
+ <many-to-one name="owner" property-ref="identity.ssn"/>
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/partial/PartialComponentPropertyRefTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/partial/PartialComponentPropertyRefTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/partial/PartialComponentPropertyRefTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,73 @@
+//$Id: PartialComponentPropertyRefTest.java 9914 2006-05-09 09:37:18Z max.andersen at jboss.com $
+package org.hibernate.test.propertyref.component.partial;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class PartialComponentPropertyRefTest extends FunctionalTestCase {
+
+ public PartialComponentPropertyRefTest(String name) {
+ super( name );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "propertyref/component/partial/Mapping.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( PartialComponentPropertyRefTest.class );
+ }
+
+ public void testComponentPropertyRef() {
+ Person p = new Person();
+ p.setIdentity( new Identity() );
+ Account a = new Account();
+ a.setNumber("123-12345-1236");
+ a.setOwner(p);
+ p.getIdentity().setName("Gavin");
+ p.getIdentity().setSsn("123-12-1234");
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ s.persist(p);
+ s.persist(a);
+ s.flush();
+ s.clear();
+
+ a = (Account) s.createQuery("from Account a left join fetch a.owner").uniqueResult();
+ assertTrue( Hibernate.isInitialized( a.getOwner() ) );
+ assertNotNull( a.getOwner() );
+ assertEquals( "Gavin", a.getOwner().getIdentity().getName() );
+ s.clear();
+
+ a = (Account) s.get(Account.class, "123-12345-1236");
+ assertFalse( Hibernate.isInitialized( a.getOwner() ) );
+ assertNotNull( a.getOwner() );
+ assertEquals( "Gavin", a.getOwner().getIdentity().getName() );
+ assertTrue( Hibernate.isInitialized( a.getOwner() ) );
+
+ s.clear();
+
+ getSessions().evict(Account.class);
+ getSessions().evict(Person.class);
+
+ a = (Account) s.get(Account.class, "123-12345-1236");
+ assertTrue( Hibernate.isInitialized( a.getOwner() ) );
+ assertNotNull( a.getOwner() );
+ assertEquals( "Gavin", a.getOwner().getIdentity().getName() );
+ assertTrue( Hibernate.isInitialized( a.getOwner() ) );
+
+ s.delete( a );
+ s.delete( a.getOwner() );
+ tx.commit();
+ s.close();
+ }
+}
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/partial/Person.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/partial/Person.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/component/partial/Person.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,20 @@
+//$Id: Person.java 7587 2005-07-21 01:22:38Z oneovthafew $
+package org.hibernate.test.propertyref.component.partial;
+
+public class Person {
+ private Long id;
+ private Identity identity;
+
+ public Long getId() {
+ return id;
+ }
+ public void setId(Long id) {
+ this.id = id;
+ }
+ public Identity getIdentity() {
+ return identity;
+ }
+ public void setIdentity(Identity identity) {
+ this.identity = identity;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/discrim/Account.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/discrim/Account.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/discrim/Account.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,57 @@
+//$Id: Account.java 6029 2005-03-06 16:34:16Z oneovthafew $
+package org.hibernate.test.propertyref.inheritence.discrim;
+
+/**
+ * @author Gavin King
+ */
+public class Account {
+ private String accountId;
+ private Customer customer;
+ private Person person;
+ private char type;
+ /**
+ * @return Returns the user.
+ */
+ public Customer getCustomer() {
+ return customer;
+ }
+ /**
+ * @param user The user to set.
+ */
+ public void setCustomer(Customer user) {
+ this.customer = user;
+ }
+ /**
+ * @return Returns the accountId.
+ */
+ public String getAccountId() {
+ return accountId;
+ }
+ /**
+ * @param accountId The accountId to set.
+ */
+ public void setAccountId(String accountId) {
+ this.accountId = accountId;
+ }
+ /**
+ * @return Returns the type.
+ */
+ public char getType() {
+ return type;
+ }
+ /**
+ * @param type The type to set.
+ */
+ public void setType(char type) {
+ this.type = type;
+ }
+ public Person getPerson() {
+ return person;
+ }
+
+ public void setPerson(Person person) {
+ this.person = person;
+ }
+
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/discrim/Customer.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/discrim/Customer.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/discrim/Customer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,18 @@
+//$Id: Customer.java 6029 2005-03-06 16:34:16Z oneovthafew $
+package org.hibernate.test.propertyref.inheritence.discrim;
+
+/**
+ * @author Gavin King
+ */
+public class Customer extends Person {
+ private String customerId;
+
+ public String getCustomerId() {
+ return customerId;
+ }
+
+ public void setCustomerId(String customerId) {
+ this.customerId = customerId;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/discrim/Person.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/discrim/Person.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/discrim/Person.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.propertyref.inheritence.discrim">
+
+ <class name="Person" discriminator-value="null" table="D_SBCLS_PROPREF_PERS">
+ <id name="id">
+ <generator class="hilo"/>
+ </id>
+ <discriminator formula="customerId"/>
+ <property name="name" length="100"/>
+ <property name="personId" length="8" unique="true"/>
+
+ <subclass name="Customer" discriminator-value="not null">
+ <property name="customerId" length="8" unique="true"/>
+ </subclass>
+ </class>
+
+ <class name="Account" table="D_SBCLS_PROPREF_ACCT">
+ <id name="accountId" length="32">
+ <generator class="uuid.hex"/>
+ </id>
+ <many-to-one name="person" column="personId" unique="true" property-ref="personId"/>
+ <many-to-one name="customer" column="customerPersonId" unique="true" property-ref="personId"/>
+ <property name="type" not-null="true"/>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/discrim/Person.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/discrim/Person.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/discrim/Person.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,44 @@
+//$Id: Person.java 6029 2005-03-06 16:34:16Z oneovthafew $
+package org.hibernate.test.propertyref.inheritence.discrim;
+
+
+/**
+ * @author gavin
+ */
+public class Person {
+ private Long id;
+ private String name;
+ private String personId;
+ /**
+ * @return Returns the id.
+ */
+ public Long getId() {
+ return id;
+ }
+ /**
+ * @param id The id to set.
+ */
+ public void setId(Long id) {
+ this.id = id;
+ }
+ /**
+ * @return Returns the name.
+ */
+ public String getName() {
+ return name;
+ }
+ /**
+ * @param name The name to set.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+ public String getPersonId() {
+ return personId;
+ }
+
+ public void setPersonId(String personId) {
+ this.personId = personId;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/discrim/SubclassPropertyRefTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/discrim/SubclassPropertyRefTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/discrim/SubclassPropertyRefTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,64 @@
+//$Id: SubclassPropertyRefTest.java 6029 2005-03-06 16:34:16Z oneovthafew $
+package org.hibernate.test.propertyref.inheritence.discrim;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class SubclassPropertyRefTest extends FunctionalTestCase {
+
+ public SubclassPropertyRefTest(String name) {
+ super( name );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "propertyref/inheritence/discrim/Person.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( SubclassPropertyRefTest.class );
+ }
+
+ public void testOneToOnePropertyRef() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Customer c = new Customer();
+ c.setName( "Emmanuel" );
+ c.setCustomerId( "C123-456" );
+ c.setPersonId( "P123-456" );
+ Account a = new Account();
+ a.setCustomer( c );
+ a.setPerson( c );
+ a.setType( 'X' );
+ s.persist( c );
+ s.persist( a );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ a = ( Account ) s.createQuery( "from Account acc join fetch acc.customer join fetch acc.person" )
+ .uniqueResult();
+ assertNotNull( a.getCustomer() );
+ assertTrue( Hibernate.isInitialized( a.getCustomer() ) );
+ assertNotNull( a.getPerson() );
+ assertTrue( Hibernate.isInitialized( a.getPerson() ) );
+ c = ( Customer ) s.createQuery( "from Customer" ).uniqueResult();
+ assertSame( c, a.getCustomer() );
+ assertSame( c, a.getPerson() );
+ s.delete( a );
+ s.delete( a.getCustomer() );
+ s.delete( a.getPerson() );
+ t.commit();
+ s.close();
+ }
+
+}
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/joined/Account.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/joined/Account.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/joined/Account.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,38 @@
+//$Id: Account.java 7274 2005-06-22 17:07:29Z oneovthafew $
+package org.hibernate.test.propertyref.inheritence.joined;
+
+import java.io.Serializable;
+
+/**
+ * @author Gavin King
+ */
+public class Account implements Serializable {
+ private String accountId;
+ private char type;
+
+ /**
+ * @return Returns the accountId.
+ */
+ public String getAccountId() {
+ return accountId;
+ }
+ /**
+ * @param accountId The accountId to set.
+ */
+ public void setAccountId(String accountId) {
+ this.accountId = accountId;
+ }
+ /**
+ * @return Returns the type.
+ */
+ public char getType() {
+ return type;
+ }
+ /**
+ * @param type The type to set.
+ */
+ public void setType(char type) {
+ this.type = type;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/joined/BankAccount.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/joined/BankAccount.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/joined/BankAccount.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,23 @@
+//$Id: BankAccount.java 7274 2005-06-22 17:07:29Z oneovthafew $
+package org.hibernate.test.propertyref.inheritence.joined;
+
+public class BankAccount extends Account {
+ private String accountNumber;
+ private String bsb;
+
+ public String getAccountNumber() {
+ return accountNumber;
+ }
+
+ public void setAccountNumber(String accountNumber) {
+ this.accountNumber = accountNumber;
+ }
+
+ public String getBsb() {
+ return bsb;
+ }
+
+ public void setBsb(String bsb) {
+ this.bsb = bsb;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/joined/JoinedSubclassPropertyRefTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/joined/JoinedSubclassPropertyRefTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/joined/JoinedSubclassPropertyRefTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,70 @@
+//$Id: PropertyRefTest.java 7275 2005-06-22 18:58:16Z oneovthafew $
+package org.hibernate.test.propertyref.inheritence.joined;
+
+import junit.framework.Test;
+
+import org.hibernate.FetchMode;
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class JoinedSubclassPropertyRefTest extends FunctionalTestCase {
+
+ public JoinedSubclassPropertyRefTest(String name) {
+ super( name );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "propertyref/inheritence/joined/Person.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( JoinedSubclassPropertyRefTest.class );
+ }
+
+ public void testPropertyRefToJoinedSubclass() {
+ Session session = openSession();
+ Transaction tx = session.beginTransaction();
+ Person p = new Person();
+ p.setName("Gavin King");
+ BankAccount acc = new BankAccount();
+ acc.setBsb("0634");
+ acc.setType('B');
+ acc.setAccountNumber("xxx-123-abc");
+ p.setBankAccount(acc);
+ session.persist(p);
+ tx.commit();
+ session.close();
+
+ session = openSession();
+ tx = session.beginTransaction();
+ p = (Person) session.get(Person.class, p.getId());
+ assertNotNull( p.getBankAccount() );
+ assertTrue( Hibernate.isInitialized( p.getBankAccount() ) );
+ tx.commit();
+ session.close();
+
+ session = openSession();
+ tx = session.beginTransaction();
+ p = (Person) session.createCriteria(Person.class)
+ .setFetchMode("bankAccount", FetchMode.JOIN)
+ .uniqueResult();
+ assertNotNull( p.getBankAccount() );
+ assertTrue( Hibernate.isInitialized( p.getBankAccount() ) );
+ tx.commit();
+ session.close();
+
+ session = openSession();
+ tx = session.beginTransaction();
+ session.delete(p);
+ tx.commit();
+ session.close();
+ }
+
+}
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/joined/Person.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/joined/Person.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/joined/Person.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,49 @@
+<?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 the use of property-ref to map legacy data where
+ foreign keys reference something other than the primary key of
+ the associated entity. Here we show:
+
+ (1) A one-to-one foreign key association (prefer primary key
+ associations)
+
+ (2) A bidirectional one-to-many association on a key that is
+ not the primary key (prefer associations from foreign keys
+ to primary keys)
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.propertyref.inheritence.joined">
+
+ <class name="Person" table="J_SBCLS_PROPREF_PERS">
+ <id name="id">
+ <generator class="hilo"/>
+ </id>
+ <property name="name" length="100"/>
+ <many-to-one name="bankAccount" property-ref="bsbAccountNumber" cascade="all">
+ <column name="bsb"/>
+ <column name="accountNumber"/>
+ </many-to-one>
+ </class>
+
+ <class name="Account" table="J_SBCLS_PROPREF_ACCT">
+ <id name="accountId" length="32">
+ <generator class="uuid"/>
+ </id>
+ <property name="type" not-null="true"/>
+ <joined-subclass name="BankAccount" table="J_SBCLS_PROPREF_BACCT">
+ <key column="accountId"/>
+ <properties unique="true" name="bsbAccountNumber">
+ <property name="bsb" length="4" not-null="true"/>
+ <property name="accountNumber" length="16" not-null="true"/>
+ </properties>
+ </joined-subclass>
+
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/joined/Person.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/joined/Person.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/joined/Person.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,44 @@
+//$Id: Person.java 7274 2005-06-22 17:07:29Z oneovthafew $
+package org.hibernate.test.propertyref.inheritence.joined;
+
+
+/**
+ * @author gavin
+ */
+public class Person {
+ private Long id;
+ private String name;
+ private BankAccount bankAccount;
+
+ /**
+ * @return Returns the id.
+ */
+ public Long getId() {
+ return id;
+ }
+ /**
+ * @param id The id to set.
+ */
+ public void setId(Long id) {
+ this.id = id;
+ }
+ /**
+ * @return Returns the name.
+ */
+ public String getName() {
+ return name;
+ }
+ /**
+ * @param name The name to set.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public BankAccount getBankAccount() {
+ return bankAccount;
+ }
+ public void setBankAccount(BankAccount bankAccount) {
+ this.bankAccount = bankAccount;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/union/Account.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/union/Account.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/union/Account.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,56 @@
+package org.hibernate.test.propertyref.inheritence.union;
+
+/**
+ * @author Gavin King
+ */
+public class Account {
+ private String accountId;
+ private Customer customer;
+ private Person person;
+ private char type;
+ /**
+ * @return Returns the user.
+ */
+ public Customer getCustomer() {
+ return customer;
+ }
+ /**
+ * @param user The user to set.
+ */
+ public void setCustomer(Customer user) {
+ this.customer = user;
+ }
+ /**
+ * @return Returns the accountId.
+ */
+ public String getAccountId() {
+ return accountId;
+ }
+ /**
+ * @param accountId The accountId to set.
+ */
+ public void setAccountId(String accountId) {
+ this.accountId = accountId;
+ }
+ /**
+ * @return Returns the type.
+ */
+ public char getType() {
+ return type;
+ }
+ /**
+ * @param type The type to set.
+ */
+ public void setType(char type) {
+ this.type = type;
+ }
+ public Person getPerson() {
+ return person;
+ }
+
+ public void setPerson(Person person) {
+ this.person = person;
+ }
+
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/union/Customer.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/union/Customer.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/union/Customer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,17 @@
+package org.hibernate.test.propertyref.inheritence.union;
+
+/**
+ * @author Gavin King
+ */
+public class Customer extends Person {
+ private String customerId;
+
+ public String getCustomerId() {
+ return customerId;
+ }
+
+ public void setCustomerId(String customerId) {
+ this.customerId = customerId;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/union/Person.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/union/Person.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/union/Person.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,29 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.propertyref.inheritence.union">
+
+ <class name="Person" table="U_SBCLS_PROPREF_PERS">
+ <id name="id">
+ <generator class="hilo"/>
+ </id>
+ <property name="name" length="100"/>
+ <property name="personId" length="8" unique="true"/>
+
+ <union-subclass name="Customer" table="U_SBCLS_PROPREF_CUST">
+ <property name="customerId" length="8" unique="true"/>
+ </union-subclass>
+ </class>
+
+ <class name="Account" table="U_SBCLS_PROPREF_ACCT">
+ <id name="accountId" length="32">
+ <generator class="uuid.hex"/>
+ </id>
+ <many-to-one name="person" column="personId" unique="true" property-ref="personId"/>
+ <many-to-one name="customer" column="customerPersonId" unique="true" property-ref="personId"/>
+ <property name="type" not-null="true"/>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/union/Person.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/union/Person.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/union/Person.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,42 @@
+package org.hibernate.test.propertyref.inheritence.union;
+
+/**
+ * @author gavin
+ */
+public class Person {
+ private Long id;
+ private String name;
+ private String personId;
+ /**
+ * @return Returns the id.
+ */
+ public Long getId() {
+ return id;
+ }
+ /**
+ * @param id The id to set.
+ */
+ public void setId(Long id) {
+ this.id = id;
+ }
+ /**
+ * @return Returns the name.
+ */
+ public String getName() {
+ return name;
+ }
+ /**
+ * @param name The name to set.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+ public String getPersonId() {
+ return personId;
+ }
+
+ public void setPersonId(String personId) {
+ this.personId = personId;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/union/UnionSubclassPropertyRefTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/union/UnionSubclassPropertyRefTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/propertyref/inheritence/union/UnionSubclassPropertyRefTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,62 @@
+package org.hibernate.test.propertyref.inheritence.union;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class UnionSubclassPropertyRefTest extends FunctionalTestCase {
+
+ public UnionSubclassPropertyRefTest(String name) {
+ super( name );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "propertyref/inheritence/union/Person.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( UnionSubclassPropertyRefTest.class );
+ }
+
+ public void testOneToOnePropertyRef() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Customer c = new Customer();
+ c.setName( "Emmanuel" );
+ c.setCustomerId( "C123-456" );
+ c.setPersonId( "P123-456" );
+ Account a = new Account();
+ a.setCustomer( c );
+ a.setPerson( c );
+ a.setType( 'X' );
+ s.persist( c );
+ s.persist( a );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ a = ( Account ) s.createQuery( "from Account acc join fetch acc.customer join fetch acc.person" )
+ .uniqueResult();
+ assertNotNull( a.getCustomer() );
+ assertTrue( Hibernate.isInitialized( a.getCustomer() ) );
+ assertNotNull( a.getPerson() );
+ assertTrue( Hibernate.isInitialized( a.getPerson() ) );
+ c = ( Customer ) s.createQuery( "from Customer" ).uniqueResult();
+ assertSame( c, a.getCustomer() );
+ assertSame( c, a.getPerson() );
+ s.delete( a );
+ s.delete( a.getCustomer() );
+ s.delete( a.getPerson() );
+ t.commit();
+ s.close();
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/proxy/Container.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/proxy/Container.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/proxy/Container.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,63 @@
+package org.hibernate.test.proxy;
+
+import java.util.Set;
+import java.util.HashSet;
+import java.io.Serializable;
+
+/**
+ * @author Steve Ebersole
+ */
+public class Container implements Serializable {
+ private Long id;
+ private String name;
+ private Owner owner;
+ private Info info;
+ private Set dataPoints = new HashSet();
+
+ public Container() {
+ }
+
+ public Container(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;
+ }
+
+ public Owner getOwner() {
+ return owner;
+ }
+
+ public void setOwner(Owner owner) {
+ this.owner = owner;
+ }
+
+ public Info getInfo() {
+ return info;
+ }
+
+ public void setInfo(Info info) {
+ this.info = info;
+ }
+
+ public Set getDataPoints() {
+ return dataPoints;
+ }
+
+ public void setDataPoints(Set dataPoints) {
+ this.dataPoints = dataPoints;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/proxy/DataPoint.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/proxy/DataPoint.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/proxy/DataPoint.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,49 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping
+ package="org.hibernate.test.proxy">
+
+ <class name="DataPoint">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="x">
+ <column name="xval" not-null="true" length="4" unique-key="xy"/>
+ </property>
+ <property name="y">
+ <column name="yval" not-null="true" length="4" unique-key="xy"/>
+ </property>
+ <property name="description"/>
+ </class>
+
+ <class name="Owner">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="name" unique="true"/>
+ </class>
+
+ <class name="Info">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="details"/>
+ </class>
+
+ <class name="Container">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="name"/>
+ <many-to-one name="owner" class="Owner" column="owner_name" property-ref="name" cascade="all"/>
+ <many-to-one name="info" class="Info" column="info_id" cascade="all"/>
+ <set name="dataPoints" lazy="true" inverse="false" cascade="all">
+ <key column="c_id"/>
+ <one-to-many class="DataPoint"/>
+ </set>
+ </class>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/proxy/DataPoint.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/proxy/DataPoint.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/proxy/DataPoint.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/proxy/DataPoint.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,77 @@
+//$Id: DataPoint.java 10223 2006-08-04 20:29:21Z steve.ebersole at jboss.com $
+package org.hibernate.test.proxy;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * @author Gavin King
+ */
+public class DataPoint implements Serializable {
+ private long id;
+ private BigDecimal x;
+ private BigDecimal y;
+ private String description;
+
+ public DataPoint() {
+ }
+
+ public DataPoint(BigDecimal x, BigDecimal y, String description) {
+ this.x = x;
+ this.y = y;
+ this.description = description;
+ }
+
+ /**
+ * @return Returns the description.
+ */
+ public String getDescription() {
+ return description;
+ }
+ /**
+ * @param description The description to set.
+ */
+ public void setDescription(String description) {
+ this.description = description;
+ }
+ /**
+ * @return Returns the id.
+ */
+ public long getId() {
+ return id;
+ }
+ /**
+ * @param id The id to set.
+ */
+ public void setId(long id) {
+ this.id = id;
+ }
+ /**
+ * @return Returns the x.
+ */
+ public BigDecimal getX() {
+ return x;
+ }
+ /**
+ * @param x The x to set.
+ */
+ public void setX(BigDecimal x) {
+ this.x = x;
+ }
+ /**
+ * @return Returns the y.
+ */
+ public BigDecimal getY() {
+ return y;
+ }
+ /**
+ * @param y The y to set.
+ */
+ public void setY(BigDecimal y) {
+ this.y = y;
+ }
+
+ void exception() throws Exception {
+ throw new Exception("foo");
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/proxy/DataPoint.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/proxy/Info.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/proxy/Info.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/proxy/Info.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,34 @@
+package org.hibernate.test.proxy;
+
+/**
+ * todo: describe Info
+ *
+ * @author Steve Ebersole
+ */
+public class Info {
+ private Long id;
+ private String details;
+
+ public Info() {
+ }
+
+ public Info(String details) {
+ this.details = details;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getDetails() {
+ return details;
+ }
+
+ public void setDetails(String details) {
+ this.details = details;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/proxy/Owner.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/proxy/Owner.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/proxy/Owner.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,34 @@
+package org.hibernate.test.proxy;
+
+import java.io.Serializable;
+
+/**
+ * @author Steve Ebersole
+ */
+public class Owner implements Serializable {
+ private Long id;
+ private String name;
+
+ public Owner() {
+ }
+
+ public Owner(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/testsuite/src/test/java/org/hibernate/test/proxy/ProxyTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/proxy/ProxyTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/proxy/ProxyTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,378 @@
+//$Id: ProxyTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.proxy;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.hibernate.FlushMode;
+import org.hibernate.Hibernate;
+import org.hibernate.LazyInitializationException;
+import org.hibernate.LockMode;
+import org.hibernate.ObjectNotFoundException;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.impl.SessionImpl;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.proxy.HibernateProxy;
+import org.hibernate.util.SerializationHelper;
+
+/**
+ * @author Gavin King
+ */
+public class ProxyTest extends FunctionalTestCase {
+
+ public ProxyTest(String name) {
+ super( name );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "proxy/DataPoint.hbm.xml" };
+ }
+
+ public void configure(Configuration cfg) {
+ super.configure( cfg );
+ cfg.setProperty( Environment.STATEMENT_BATCH_SIZE, "0" ); // problem on HSQLDB (go figure)
+ }
+
+ public String getCacheConcurrencyStrategy() {
+ return null;
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( ProxyTest.class );
+ }
+
+ public void testFinalizeFiltered() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ DataPoint dp = new DataPoint();
+ dp.setDescription("a data point");
+ dp.setX( new BigDecimal(1.0) );
+ dp.setY( new BigDecimal(2.0) );
+ s.persist(dp);
+ s.flush();
+ s.clear();
+
+ dp = (DataPoint) s.load(DataPoint.class, new Long( dp.getId() ) );
+ assertFalse( Hibernate.isInitialized(dp) );
+
+ try {
+ dp.getClass().getDeclaredMethod("finalize",null);
+ fail();
+
+ }
+ catch (NoSuchMethodException e) {}
+
+ s.delete(dp);
+ t.commit();
+ s.close();
+
+ }
+
+ public void testProxyException() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ DataPoint dp = new DataPoint();
+ dp.setDescription("a data point");
+ dp.setX( new BigDecimal(1.0) );
+ dp.setY( new BigDecimal(2.0) );
+ s.persist(dp);
+ s.flush();
+ s.clear();
+
+ dp = (DataPoint) s.load(DataPoint.class, new Long( dp.getId() ) );
+ assertFalse( Hibernate.isInitialized(dp) );
+
+ try {
+ dp.exception();
+ fail();
+ }
+ catch (Exception e) {
+ assertTrue( e.getClass()==Exception.class );
+ }
+ s.delete(dp);
+ t.commit();
+ s.close();
+ }
+
+ public void testProxySerializationAfterSessionClosed() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ DataPoint dp = new DataPoint();
+ dp.setDescription("a data point");
+ dp.setX( new BigDecimal(1.0) );
+ dp.setY( new BigDecimal(2.0) );
+ s.persist(dp);
+ s.flush();
+ s.clear();
+
+ dp = (DataPoint) s.load( DataPoint.class, new Long( dp.getId() ) );
+ assertFalse( Hibernate.isInitialized(dp) );
+ s.close();
+ SerializationHelper.clone( dp );
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.delete( dp );
+ t.commit();
+ s.close();
+ }
+
+ public void testProxySerialization() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ DataPoint dp = new DataPoint();
+ dp.setDescription("a data point");
+ dp.setX( new BigDecimal(1.0) );
+ dp.setY( new BigDecimal(2.0) );
+ s.persist(dp);
+ s.flush();
+ s.clear();
+
+ dp = (DataPoint) s.load( DataPoint.class, new Long( dp.getId() ) );
+ assertFalse( Hibernate.isInitialized(dp) );
+ dp.getId();
+ assertFalse( Hibernate.isInitialized(dp) );
+ dp.getDescription();
+ assertTrue( Hibernate.isInitialized(dp) );
+ Object none = s.load( DataPoint.class, new Long(666));
+ assertFalse( Hibernate.isInitialized(none) );
+
+ t.commit();
+ s.disconnect();
+
+ Object[] holder = new Object[] { s, dp, none };
+
+ holder = (Object[]) SerializationHelper.clone(holder);
+ Session sclone = (Session) holder[0];
+ dp = (DataPoint) holder[1];
+ none = holder[2];
+
+ //close the original:
+ s.close();
+
+ sclone.reconnect();
+ t = sclone.beginTransaction();
+
+ DataPoint sdp = (DataPoint) sclone.load( DataPoint.class, new Long( dp.getId() ) );
+ assertSame(dp, sdp);
+ assertFalse(sdp instanceof HibernateProxy);
+ Object snone = sclone.load( DataPoint.class, new Long(666) );
+ assertSame(none, snone);
+ assertTrue(snone instanceof HibernateProxy);
+
+ sclone.delete(dp);
+
+ t.commit();
+ sclone.close();
+
+ }
+
+ public void testProxy() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ DataPoint dp = new DataPoint();
+ dp.setDescription("a data point");
+ dp.setX( new BigDecimal(1.0) );
+ dp.setY( new BigDecimal(2.0) );
+ s.persist(dp);
+ s.flush();
+ s.clear();
+
+ dp = (DataPoint) s.load( DataPoint.class, new Long(dp.getId() ));
+ assertFalse( Hibernate.isInitialized(dp) );
+ DataPoint dp2 = (DataPoint) s.get( DataPoint.class, new Long(dp.getId()) );
+ assertSame(dp, dp2);
+ assertTrue( Hibernate.isInitialized(dp) );
+ s.clear();
+
+ dp = (DataPoint) s.load( DataPoint.class, new Long( dp.getId() ) );
+ assertFalse( Hibernate.isInitialized(dp) );
+ dp2 = (DataPoint) s.load( DataPoint.class, new Long( dp.getId() ), LockMode.NONE );
+ assertSame(dp, dp2);
+ assertFalse( Hibernate.isInitialized(dp) );
+ s.clear();
+
+ dp = (DataPoint) s.load( DataPoint.class, new Long( dp.getId() ) );
+ assertFalse( Hibernate.isInitialized(dp) );
+ dp2 = (DataPoint) s.load( DataPoint.class, new Long( dp.getId() ), LockMode.READ );
+ assertSame(dp, dp2);
+ assertTrue( Hibernate.isInitialized(dp) );
+ s.clear();
+
+ dp = (DataPoint) s.load( DataPoint.class, new Long (dp.getId() ));
+ assertFalse( Hibernate.isInitialized(dp) );
+ dp2 = (DataPoint) s.get( DataPoint.class, new Long ( dp.getId() ) , LockMode.READ );
+ assertSame(dp, dp2);
+ assertTrue( Hibernate.isInitialized(dp) );
+ s.clear();
+
+ dp = (DataPoint) s.load( DataPoint.class, new Long ( dp.getId() ) );
+ assertFalse( Hibernate.isInitialized(dp) );
+ dp2 = (DataPoint) s.createQuery("from DataPoint").uniqueResult();
+ assertSame(dp, dp2);
+ assertTrue( Hibernate.isInitialized(dp) );
+ s.delete( dp );
+ t.commit();
+ s.close();
+ }
+
+ public void testSubsequentNonExistentProxyAccess() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ DataPoint proxy = ( DataPoint ) s.load( DataPoint.class, new Long(-1) );
+ assertFalse( Hibernate.isInitialized( proxy ) );
+ try {
+ proxy.getDescription();
+ fail( "proxy access did not fail on non-existent proxy" );
+ }
+ catch( ObjectNotFoundException onfe ) {
+ // expected
+ }
+ catch( Throwable e ) {
+ fail( "unexpected exception type on non-existent proxy access : " + e );
+ }
+ // try it a second (subsequent) time...
+ try {
+ proxy.getDescription();
+ fail( "proxy access did not fail on non-existent proxy" );
+ }
+ catch( ObjectNotFoundException onfe ) {
+ // expected
+ }
+ catch( Throwable e ) {
+ fail( "unexpected exception type on non-existent proxy access : " + e );
+ }
+
+ t.commit();
+ s.close();
+ }
+
+ public void testProxyEviction() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Container container = new Container( "container" );
+ container.setOwner( new Owner( "owner" ) );
+ container.setInfo( new Info( "blah blah blah" ) );
+ container.getDataPoints().add( new DataPoint( new BigDecimal( 1 ), new BigDecimal( 1 ), "first data point" ) );
+ container.getDataPoints().add( new DataPoint( new BigDecimal( 2 ), new BigDecimal( 2 ), "second data point" ) );
+ s.save( container );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Container c = ( Container ) s.load( Container.class, container.getId() );
+ assertFalse( Hibernate.isInitialized( c ) );
+ s.evict( c );
+ try {
+ c.getName();
+ fail( "expecting LazyInitializationException" );
+ }
+ catch( LazyInitializationException e ) {
+ // expected result
+ }
+
+ c = ( Container ) s.load( Container.class, container.getId() );
+ assertFalse( Hibernate.isInitialized( c ) );
+ Info i = c.getInfo();
+ assertTrue( Hibernate.isInitialized( c ) );
+ assertFalse( Hibernate.isInitialized( i ) );
+ s.evict( c );
+ try {
+ i.getDetails();
+ fail( "expecting LazyInitializationException" );
+ }
+ catch( LazyInitializationException e ) {
+ // expected result
+ }
+
+ s.delete( c );
+
+ t.commit();
+ s.close();
+ }
+
+ public void testFullyLoadedPCSerialization() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Long lastContainerId = null;
+ int containerCount = 10;
+ int nestedDataPointCount = 5;
+ for ( int c_indx = 0; c_indx < containerCount; c_indx++ ) {
+ Owner owner = new Owner( "Owner #" + c_indx );
+ Container container = new Container( "Container #" + c_indx );
+ container.setOwner( owner );
+ for ( int dp_indx = 0; dp_indx < nestedDataPointCount; dp_indx++ ) {
+ DataPoint dp = new DataPoint();
+ dp.setDescription( "data-point [" + c_indx + ", " + dp_indx + "]" );
+// more HSQLDB fun...
+// dp.setX( new BigDecimal( c_indx ) );
+ dp.setX( new BigDecimal( c_indx + dp_indx ) );
+ dp.setY( new BigDecimal( dp_indx ) );
+ container.getDataPoints().add( dp );
+ }
+ s.save( container );
+ lastContainerId = container.getId();
+ }
+ t.commit();
+ s.close();
+
+ s = openSession();
+ s.setFlushMode( FlushMode.NEVER );
+ t = s.beginTransaction();
+ // load the last container as a proxy
+ Container proxy = ( Container ) s.load( Container.class, lastContainerId );
+ assertFalse( Hibernate.isInitialized( proxy ) );
+ // load the rest back into the PC
+ List all = s.createQuery( "from Container as c inner join fetch c.owner inner join fetch c.dataPoints where c.id <> :last" )
+ .setLong( "last", lastContainerId.longValue() )
+ .list();
+ Container container = ( Container ) all.get( 0 );
+ s.delete( container );
+ // force a snapshot retrieval of the proxied container
+ SessionImpl sImpl = ( SessionImpl ) s;
+ sImpl.getPersistenceContext().getDatabaseSnapshot(
+ lastContainerId,
+ sImpl.getFactory().getEntityPersister( Container.class.getName() )
+ );
+ assertFalse( Hibernate.isInitialized( proxy ) );
+ t.commit();
+
+// int iterations = 50;
+// long cumulativeTime = 0;
+// long cumulativeSize = 0;
+// for ( int i = 0; i < iterations; i++ ) {
+// final long start = System.currentTimeMillis();
+// byte[] bytes = SerializationHelper.serialize( s );
+// SerializationHelper.deserialize( bytes );
+// final long end = System.currentTimeMillis();
+// cumulativeTime += ( end - start );
+// int size = bytes.length;
+// cumulativeSize += size;
+//// System.out.println( "Iteration #" + i + " took " + ( end - start ) + " ms : size = " + size + " bytes" );
+// }
+// System.out.println( "Average time : " + ( cumulativeTime / iterations ) + " ms" );
+// System.out.println( "Average size : " + ( cumulativeSize / iterations ) + " bytes" );
+
+ byte[] bytes = SerializationHelper.serialize( s );
+ SerializationHelper.deserialize( bytes );
+
+ t = s.beginTransaction();
+ int count = s.createQuery( "delete DataPoint" ).executeUpdate();
+ assertEquals( "unexpected DP delete count", ( containerCount * nestedDataPointCount ), count );
+ count = s.createQuery( "delete Container" ).executeUpdate();
+ assertEquals( "unexpected container delete count", containerCount, count );
+ count = s.createQuery( "delete Owner" ).executeUpdate();
+ assertEquals( "unexpected owner delete count", containerCount, count );
+ t.commit();
+ s.close();
+ }
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/proxy/ProxyTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/querycache/Item.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/querycache/Item.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/querycache/Item.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping
+ package="org.hibernate.test.querycache">
+
+ <class name="Item" table="Items" batch-size="9">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="name" not-null="true"/>
+ <property name="description" not-null="true"/>
+ </class>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/querycache/Item.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/querycache/Item.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/querycache/Item.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/querycache/Item.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,34 @@
+//$Id: Item.java 9587 2006-03-09 21:38:26Z steve.ebersole at jboss.com $
+package org.hibernate.test.querycache;
+
+import java.util.Set;
+import java.util.HashSet;
+
+
+/**
+ * @author Gavin King
+ */
+public class Item {
+ private long id;
+ private String name;
+ private String description;
+
+ public String getDescription() {
+ return description;
+ }
+ public void setDescription(String description) {
+ this.description = description;
+ }
+ 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;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/querycache/Item.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/querycache/QueryCacheTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/querycache/QueryCacheTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/querycache/QueryCacheTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,277 @@
+//$Id: QueryCacheTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.querycache;
+
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.stat.EntityStatistics;
+import org.hibernate.stat.QueryStatistics;
+import org.hibernate.transform.Transformers;
+
+/**
+ * @author Gavin King
+ */
+public class QueryCacheTest extends FunctionalTestCase {
+
+ public QueryCacheTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "querycache/Item.hbm.xml" };
+ }
+
+ public void configure(Configuration cfg) {
+ super.configure( cfg );
+ cfg.setProperty( Environment.USE_QUERY_CACHE, "true" );
+ cfg.setProperty( Environment.CACHE_REGION_PREFIX, "foo" );
+ cfg.setProperty( Environment.USE_SECOND_LEVEL_CACHE, "true" );
+ cfg.setProperty( Environment.GENERATE_STATISTICS, "true" );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( QueryCacheTest.class );
+ }
+
+ public void testQueryCacheInvalidation() throws Exception {
+
+ getSessions().evictQueries();
+ getSessions().getStatistics().clear();
+
+ final String queryString = "from Item i where i.name='widget'";
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.createQuery( queryString ).setCacheable(true).list();
+ Item i = new Item();
+ i.setName("widget");
+ i.setDescription("A really top-quality, full-featured widget.");
+ s.save(i);
+ t.commit();
+ s.close();
+
+ QueryStatistics qs = s.getSessionFactory().getStatistics().getQueryStatistics( queryString );
+ EntityStatistics es = s.getSessionFactory().getStatistics().getEntityStatistics( Item.class.getName() );
+
+ Thread.sleep(200);
+
+ s = openSession();
+ t = s.beginTransaction();
+ List result = s.createQuery( queryString ).setCacheable(true).list();
+ assertEquals( result.size(), 1 );
+ t.commit();
+ s.close();
+
+ assertEquals( qs.getCacheHitCount(), 0 );
+
+ s = openSession();
+ t = s.beginTransaction();
+ result = s.createQuery( queryString ).setCacheable(true).list();
+ assertEquals( result.size(), 1 );
+ t.commit();
+ s.close();
+
+ assertEquals( qs.getCacheHitCount(), 1 );
+ assertEquals( s.getSessionFactory().getStatistics().getEntityFetchCount(), 0 );
+
+ s = openSession();
+ t = s.beginTransaction();
+ result = s.createQuery( queryString ).setCacheable(true).list();
+ assertEquals( result.size(), 1 );
+ assertTrue( Hibernate.isInitialized( result.get(0) ) );
+ i = (Item) result.get(0);
+ i.setName("Widget");
+ t.commit();
+ s.close();
+
+ assertEquals( qs.getCacheHitCount(), 2 );
+ assertEquals( qs.getCacheMissCount(), 2 );
+ assertEquals( s.getSessionFactory().getStatistics().getEntityFetchCount(), 0 );
+
+ Thread.sleep(200);
+
+ s = openSession();
+ t = s.beginTransaction();
+ result = s.createQuery( queryString ).setCacheable(true).list();
+ if ( dialectIsCaseSensitive("i.name='widget' should not match on case sensitive database.") ) {
+ assertEquals( result.size(), 0 );
+ }
+ i = (Item) s.get( Item.class, new Long(i.getId()) );
+ assertEquals( i.getName(), "Widget" );
+
+ s.delete(i);
+ t.commit();
+ s.close();
+
+ assertEquals( qs.getCacheHitCount(), 2 );
+ assertEquals( qs.getCacheMissCount(), 3 );
+ assertEquals( qs.getCachePutCount(), 3 );
+ assertEquals( qs.getExecutionCount(), 3 );
+ assertEquals( es.getFetchCount(), 0 ); //check that it was being cached
+
+ }
+
+ public void testQueryCacheFetch() throws Exception {
+
+ getSessions().evictQueries();
+ getSessions().getStatistics().clear();
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Item i = new Item();
+ i.setName("widget");
+ i.setDescription("A really top-quality, full-featured widget.");
+ Item i2 = new Item();
+ i2.setName("other widget");
+ i2.setDescription("Another decent widget.");
+ s.persist(i);
+ s.persist(i2);
+ t.commit();
+ s.close();
+
+ final String queryString = "from Item i where i.name like '%widget'";
+
+ QueryStatistics qs = s.getSessionFactory().getStatistics().getQueryStatistics( queryString );
+
+ Thread.sleep(200);
+
+ s = openSession();
+ t = s.beginTransaction();
+ List result = s.createQuery( queryString ).setCacheable(true).list();
+ assertEquals( result.size(), 2 );
+ t.commit();
+ s.close();
+
+ assertEquals( qs.getCacheHitCount(), 0 );
+ assertEquals( s.getSessionFactory().getStatistics().getEntityFetchCount(), 0 );
+
+ getSessions().evict(Item.class);
+
+ s = openSession();
+ t = s.beginTransaction();
+ result = s.createQuery( queryString ).setCacheable(true).list();
+ assertEquals( result.size(), 2 );
+ assertTrue( Hibernate.isInitialized( result.get(0) ) );
+ assertTrue( Hibernate.isInitialized( result.get(1) ) );
+ t.commit();
+ s.close();
+
+ assertEquals( qs.getCacheHitCount(), 1 );
+ assertEquals( s.getSessionFactory().getStatistics().getEntityFetchCount(), 1 );
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.createQuery("delete Item").executeUpdate();
+ t.commit();
+ s.close();
+
+ }
+
+ public void testProjectionCache() throws Exception {
+
+ getSessions().evictQueries();
+ getSessions().getStatistics().clear();
+
+ final String queryString = "select i.description from Item i where i.name='widget'";
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.createQuery( queryString ).setCacheable(true).list();
+ Item i = new Item();
+ i.setName("widget");
+ i.setDescription("A really top-quality, full-featured widget.");
+ s.save(i);
+ t.commit();
+ s.close();
+
+ QueryStatistics qs = s.getSessionFactory().getStatistics().getQueryStatistics( queryString );
+ EntityStatistics es = s.getSessionFactory().getStatistics().getEntityStatistics( Item.class.getName() );
+
+ Thread.sleep(200);
+
+ s = openSession();
+ t = s.beginTransaction();
+ List result = s.createQuery( queryString ).setCacheable(true).list();
+ assertEquals( result.size(), 1 );
+ t.commit();
+ s.close();
+
+ assertEquals( qs.getCacheHitCount(), 0 );
+
+ s = openSession();
+ t = s.beginTransaction();
+ result = s.createQuery( queryString ).setCacheable(true).list();
+ assertEquals( result.size(), 1 );
+ t.commit();
+ s.close();
+
+ assertEquals( qs.getCacheHitCount(), 1 );
+
+ s = openSession();
+ t = s.beginTransaction();
+ result = s.createQuery( queryString ).setCacheable(true).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();
+ assertEquals( result.size(), 1 );
+ Map m = (Map) result.get(0);
+ assertEquals(1, m.size());
+ t.commit();
+ s.close();
+
+ assertEquals( "hit count should not go up since we are adding a resulttransformer", qs.getCacheHitCount(), 1 );
+
+ s = openSession();
+ t = s.beginTransaction();
+ result = s.createQuery( queryString ).setCacheable(true).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();
+ assertEquals( result.size(), 1 );
+ m = (Map) result.get(0);
+ assertEquals(1, m.size());
+ t.commit();
+ s.close();
+
+ assertEquals( "hit count should go up since we are using the same resulttransformer", qs.getCacheHitCount(), 2 );
+
+ s = openSession();
+ t = s.beginTransaction();
+ result = s.createQuery( queryString ).setCacheable(true).list();
+ assertEquals( result.size(), 1 );
+ assertTrue( Hibernate.isInitialized( result.get(0) ) );
+ i = (Item) s.get( Item.class, new Long(i.getId()) );
+ i.setName("widget");
+ i.setDescription("A middle-quality widget.");
+ t.commit();
+ s.close();
+
+ assertEquals( qs.getCacheHitCount(), 3 );
+ assertEquals( qs.getCacheMissCount(), 3 );
+
+ Thread.sleep(200);
+
+ s = openSession();
+ t = s.beginTransaction();
+ result = s.createQuery( queryString ).setCacheable(true).list();
+ assertEquals( result.size(), 1 );
+ i = (Item) s.get( Item.class, new Long(i.getId()) );
+ assertEquals( (String) result.get(0), "A middle-quality widget." );
+
+ s.delete(i);
+ t.commit();
+ s.close();
+
+ assertEquals( qs.getCacheHitCount(), 3 );
+ assertEquals( qs.getCacheMissCount(), 4 );
+ assertEquals( qs.getCachePutCount(), 4 );
+ assertEquals( qs.getExecutionCount(), 4 );
+ assertEquals( es.getFetchCount(), 0 ); //check that it was being cached
+
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/querycache/QueryCacheTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/readonly/DataPoint.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/readonly/DataPoint.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/readonly/DataPoint.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping
+ package="org.hibernate.test.readonly">
+
+ <class name="DataPoint"
+ dynamic-update="true">
+ <!--rowid="rowid"--> <!-- remove this if not oracle -->
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="x">
+ <column name="xval" not-null="true" precision="25" scale="20" unique-key="xy"/>
+ </property>
+ <property name="y">
+ <column name="yval" not-null="true" precision="25" scale="20" unique-key="xy"/>
+ </property>
+ <property name="description"/>
+ </class>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/readonly/DataPoint.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/readonly/DataPoint.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/readonly/DataPoint.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/readonly/DataPoint.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,62 @@
+//$Id: DataPoint.java 7231 2005-06-19 22:04:00Z oneovthafew $
+package org.hibernate.test.readonly;
+
+import java.math.BigDecimal;
+
+/**
+ * @author Gavin King
+ */
+public class DataPoint {
+ private long id;
+ private BigDecimal x;
+ private BigDecimal y;
+ private String description;
+ /**
+ * @return Returns the description.
+ */
+ public String getDescription() {
+ return description;
+ }
+ /**
+ * @param description The description to set.
+ */
+ public void setDescription(String description) {
+ this.description = description;
+ }
+ /**
+ * @return Returns the id.
+ */
+ public long getId() {
+ return id;
+ }
+ /**
+ * @param id The id to set.
+ */
+ public void setId(long id) {
+ this.id = id;
+ }
+ /**
+ * @return Returns the x.
+ */
+ public BigDecimal getX() {
+ return x;
+ }
+ /**
+ * @param x The x to set.
+ */
+ public void setX(BigDecimal x) {
+ this.x = x;
+ }
+ /**
+ * @return Returns the y.
+ */
+ public BigDecimal getY() {
+ return y;
+ }
+ /**
+ * @param y The y to set.
+ */
+ public void setY(BigDecimal y) {
+ this.y = y;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/readonly/DataPoint.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,121 @@
+//$Id: ReadOnlyTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.readonly;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.hibernate.CacheMode;
+import org.hibernate.Hibernate;
+import org.hibernate.ScrollMode;
+import org.hibernate.ScrollableResults;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ *
+ * @author Gavin King
+ */
+public class ReadOnlyTest extends FunctionalTestCase {
+
+ public ReadOnlyTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "readonly/DataPoint.hbm.xml" };
+ }
+
+ public void configure(Configuration cfg) {
+ cfg.setProperty(Environment.STATEMENT_BATCH_SIZE, "20");
+ }
+
+ public String getCacheConcurrencyStrategy() {
+ return null;
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( ReadOnlyTest.class );
+ }
+
+ public void testReadOnlyOnProxiesFailureExpected() {
+ Session s = openSession();
+ s.setCacheMode( CacheMode.IGNORE );
+ s.beginTransaction();
+ DataPoint dp = new DataPoint();
+ dp.setX( new BigDecimal( 0.1d ).setScale(19, BigDecimal.ROUND_DOWN) );
+ dp.setY( new BigDecimal( Math.cos( dp.getX().doubleValue() ) ).setScale(19, BigDecimal.ROUND_DOWN) );
+ dp.setDescription( "original" );
+ s.save( dp );
+ long dpId = dp.getId();
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.setCacheMode(CacheMode.IGNORE);
+ dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpId ) );
+ assertFalse( "was initialized", Hibernate.isInitialized( dp ) );
+ s.setReadOnly( dp, true );
+ assertFalse( "was initialized during setReadOnly", Hibernate.isInitialized( dp ) );
+ dp.setDescription( "changed" );
+ assertTrue( "was not initialized during mod", Hibernate.isInitialized( dp ) );
+ assertEquals( "desc not changed in memory", "changed", dp.getDescription() );
+ s.flush();
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ List list = s.createQuery( "from DataPoint where description = 'changed'" ).list();
+ assertEquals( "change written to database", 0, list.size() );
+ s.createQuery("delete from DataPoint").executeUpdate();
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testReadOnlyMode() {
+
+ Session s = openSession();
+ s.setCacheMode(CacheMode.IGNORE);
+ Transaction t = s.beginTransaction();
+ for ( int i=0; i<100; i++ ) {
+ DataPoint dp = new DataPoint();
+ dp.setX( new BigDecimal(i * 0.1d).setScale(19, BigDecimal.ROUND_DOWN) );
+ dp.setY( new BigDecimal( Math.cos( dp.getX().doubleValue() ) ).setScale(19, BigDecimal.ROUND_DOWN) );
+ s.save(dp);
+ }
+ t.commit();
+ s.close();
+
+ s = openSession();
+ s.setCacheMode(CacheMode.IGNORE);
+ t = s.beginTransaction();
+ int i = 0;
+ ScrollableResults sr = s.createQuery("from DataPoint dp order by dp.x asc")
+ .setReadOnly(true)
+ .scroll(ScrollMode.FORWARD_ONLY);
+ while ( sr.next() ) {
+ DataPoint dp = (DataPoint) sr.get(0);
+ if (++i==50) {
+ s.setReadOnly(dp, false);
+ }
+ dp.setDescription("done!");
+ }
+ t.commit();
+ s.clear();
+ t = s.beginTransaction();
+ List single = s.createQuery("from DataPoint where description='done!'").list();
+ assertEquals( single.size(), 1 );
+ s.createQuery("delete from DataPoint").executeUpdate();
+ t.commit();
+ s.close();
+
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/reattachment/Child.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/reattachment/Child.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/reattachment/Child.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,34 @@
+package org.hibernate.test.reattachment;
+
+/**
+ * Child entity
+ *
+ * @author Steve Ebersole
+ */
+public class Child {
+ private String name;
+ private Parent parent;
+
+ public Child() {
+ }
+
+ public Child(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Parent getParent() {
+ return parent;
+ }
+
+ public void setParent(Parent parent) {
+ this.parent = parent;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/reattachment/CollectionReattachmentTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/reattachment/CollectionReattachmentTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/reattachment/CollectionReattachmentTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,78 @@
+package org.hibernate.test.reattachment;
+
+import junit.framework.Test;
+
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.Session;
+
+/**
+ * Test of collection reattachment semantics
+ *
+ * @author Steve Ebersole
+ */
+public class CollectionReattachmentTest extends FunctionalTestCase {
+ public CollectionReattachmentTest(String string) {
+ super( string );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( CollectionReattachmentTest.class );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "reattachment/Mappings.hbm.xml" };
+ }
+
+ public void testUpdateOwnerAfterClear() {
+ Session s = openSession();
+ s.beginTransaction();
+ Parent p = new Parent( "p" );
+ p.getChildren().add( new Child( "c" ) );
+ s.save( p );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ p = ( Parent ) s.get( Parent.class, "p" );
+ // clear...
+ s.clear();
+ // now try to reattach...
+ s.update( p );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ s.delete( p );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testUpdateOwnerAfterEvict() {
+ Session s = openSession();
+ s.beginTransaction();
+ Parent p = new Parent( "p" );
+ p.getChildren().add( new Child( "c" ) );
+ s.save( p );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ p = ( Parent ) s.get( Parent.class, "p" );
+ // evict...
+ s.evict( p );
+ // now try to reattach...
+ s.update( p );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ s.delete( p );
+ s.getTransaction().commit();
+ s.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/reattachment/Mappings.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/reattachment/Mappings.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/reattachment/Mappings.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+
+<hibernate-mapping package="org.hibernate.test.reattachment">
+
+ <class name="Parent">
+ <id name="name" column="NAME" type="string" />
+ <set name="children" inverse="true" cascade="all">
+ <key column="PARENT" />
+ <one-to-many class="Child" />
+ </set>
+ </class>
+
+ <class name="Child">
+ <id name="name" column="NAME" type="string"/>
+ <many-to-one name="parent" class="Parent" cascade="none" />
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/reattachment/Parent.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/reattachment/Parent.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/reattachment/Parent.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,37 @@
+package org.hibernate.test.reattachment;
+
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * Parent entity
+ *
+ * @author Steve Ebersole
+ */
+public class Parent {
+ private String name;
+ private Set children = new HashSet();
+
+ public Parent() {
+ }
+
+ public Parent(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Set getChildren() {
+ return children;
+ }
+
+ public void setChildren(Set children) {
+ this.children = children;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/reattachment/ProxyReattachmentTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/reattachment/ProxyReattachmentTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/reattachment/ProxyReattachmentTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,76 @@
+package org.hibernate.test.reattachment;
+
+import junit.framework.Test;
+
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.Session;
+
+/**
+ * Test of proxy reattachment semantics
+ *
+ * @author Steve Ebersole
+ */
+public class ProxyReattachmentTest extends FunctionalTestCase {
+ public ProxyReattachmentTest(String string) {
+ super( string );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( ProxyReattachmentTest.class );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "reattachment/Mappings.hbm.xml" };
+ }
+
+ public void testUpdateAfterEvict() {
+ Session s = openSession();
+ s.beginTransaction();
+ Parent p = new Parent( "p" );
+ s.save( p );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ p = ( Parent ) s.load( Parent.class, "p" );
+ // evict...
+ s.evict( p );
+ // now try to reattach...
+ s.update( p );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ s.delete( p );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testUpdateAfterClear() {
+ Session s = openSession();
+ s.beginTransaction();
+ Parent p = new Parent( "p" );
+ s.save( p );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ p = ( Parent ) s.load( Parent.class, "p" );
+ // clear...
+ s.clear();
+ // now try to reattach...
+ s.update( p );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ s.delete( p );
+ s.getTransaction().commit();
+ s.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/reattachment/ReattachmentSuite.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/reattachment/ReattachmentSuite.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/reattachment/ReattachmentSuite.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,18 @@
+package org.hibernate.test.reattachment;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * Suite of reattachment specific tests.
+ *
+ * @author Steve Ebersole
+ */
+public class ReattachmentSuite {
+ public static Test suite() {
+ TestSuite suite = new TestSuite( "reattachment semantics" );
+ suite.addTest( CollectionReattachmentTest.suite() );
+ suite.addTest( ProxyReattachmentTest.suite() );
+ return suite;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/rowid/Point.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/rowid/Point.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/rowid/Point.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.rowid">
+
+ <typedef name="rowid" class="org.hibernate.test.rowid.RowIdType"/>
+
+ <class name="Point" rowid="rowid">
+ <composite-id>
+ <key-property name="x" column="`x`"/>
+ <key-property name="y" column="`y`"/>
+ </composite-id>
+ <property name="description"/>
+ <property name="row" type="rowid" column="rowid" insert="false" update="false"/>
+ </class>
+
+</hibernate-mapping>
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/rowid/Point.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/rowid/Point.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/rowid/Point.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/rowid/Point.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,55 @@
+//$Id: Point.java 6477 2005-04-21 07:39:21Z oneovthafew $
+package org.hibernate.test.rowid;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * @author Gavin King
+ */
+public class Point implements Serializable {
+ private BigDecimal x;
+ private BigDecimal y;
+ private String description;
+ private Object row;
+
+ Point() {}
+
+ public Point(BigDecimal x, BigDecimal y) {
+ this.x = x;
+ this.y = y;
+ }
+
+ public BigDecimal getX() {
+ return x;
+ }
+
+ void setX(BigDecimal x) {
+ this.x = x;
+ }
+
+ public BigDecimal getY() {
+ return y;
+ }
+
+ void setY(BigDecimal y) {
+ this.y = y;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public Object getRow() {
+ return row;
+ }
+
+ public void setRow(Object row) {
+ this.row = row;
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/rowid/Point.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/rowid/RowIdTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/rowid/RowIdTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/rowid/RowIdTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,105 @@
+//$Id: RowIdTest.java 11353 2007-03-28 16:03:40Z steve.ebersole at jboss.com $
+package org.hibernate.test.rowid;
+
+import java.math.BigDecimal;
+import java.sql.Statement;
+import java.sql.SQLException;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.dialect.Oracle9Dialect;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.junit.functional.DatabaseSpecificFunctionalTestCase;
+
+/**
+ * @author Gavin King
+ */
+public class RowIdTest extends DatabaseSpecificFunctionalTestCase {
+
+ public RowIdTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "rowid/Point.hbm.xml" };
+ }
+
+ public String getCacheConcurrencyStrategy() {
+ return null;
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( RowIdTest.class );
+ }
+
+ public boolean appliesTo(Dialect dialect) {
+ return dialect instanceof Oracle9Dialect;
+ }
+
+ public boolean createSchema() {
+ return false;
+ }
+
+ public void afterSessionFactoryBuilt(SessionFactoryImplementor sfi) {
+ super.afterSessionFactoryBuilt( sfi );
+ Session session = null;
+ try {
+ session = sfi.openSession();
+ Statement st = session.connection().createStatement();
+ try {
+ st.execute( "drop table Point");
+ }
+ catch( Throwable ignore ) {
+ // ignore
+ }
+ st.execute("create table Point (\"x\" number(19,2) not null, \"y\" number(19,2) not null, description varchar2(255) )");
+ }
+ catch ( SQLException e ) {
+ throw new RuntimeException( "Unable to build actual schema : " + e.getMessage() );
+ }
+ finally {
+ if ( session != null ) {
+ try {
+ session.close();
+ }
+ catch( Throwable ignore ) {
+ // ignore
+ }
+ }
+ }
+ }
+
+ public void testRowId() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Point p = new Point( new BigDecimal(1.0), new BigDecimal(1.0) );
+ s.persist(p);
+ t.commit();
+ s.clear();
+
+ t = s.beginTransaction();
+ p = (Point) s.createCriteria(Point.class).uniqueResult();
+ p.setDescription("new desc");
+ t.commit();
+ s.clear();
+
+ t = s.beginTransaction();
+ p = (Point) s.createQuery("from Point").uniqueResult();
+ p.setDescription("new new desc");
+ t.commit();
+ s.clear();
+
+ t = s.beginTransaction();
+ p = (Point) s.get(Point.class, p);
+ p.setDescription("new new new desc");
+ t.commit();
+ s.close();
+
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/rowid/RowIdTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/rowid/RowIdType.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/rowid/RowIdType.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/rowid/RowIdType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,64 @@
+//$Id: RowIdType.java 6477 2005-04-21 07:39:21Z oneovthafew $
+package org.hibernate.test.rowid;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+
+import org.hibernate.HibernateException;
+import org.hibernate.usertype.UserType;
+
+/**
+ * @author Gavin King
+ */
+public class RowIdType implements UserType {
+
+ public int[] sqlTypes() {
+ return new int[] { Types.JAVA_OBJECT };
+ }
+
+ public Class returnedClass() {
+ return Object.class;
+ }
+
+ public boolean equals(Object x, Object y) throws HibernateException {
+ return x.equals(y);
+ }
+
+ public int hashCode(Object x) throws HibernateException {
+ return x.hashCode();
+ }
+
+ public Object nullSafeGet(ResultSet rs, String[] names, Object owner)
+ throws HibernateException, SQLException {
+ return rs.getObject( names[0] );
+ }
+
+ public void nullSafeSet(PreparedStatement st, Object value, int index)
+ throws HibernateException, SQLException {
+ throw new UnsupportedOperationException();
+ }
+
+ public Object deepCopy(Object value) throws HibernateException {
+ return value;
+ }
+
+ public boolean isMutable() {
+ return false;
+ }
+
+ public Serializable disassemble(Object value) throws HibernateException {
+ return null;
+ }
+
+ public Object assemble(Serializable cached, Object owner) throws HibernateException {
+ return null;
+ }
+
+ public Object replace(Object original, Object target, Object owner) throws HibernateException {
+ return null;
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/rowid/RowIdType.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/schemaupdate/1_Version.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/schemaupdate/1_Version.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/schemaupdate/1_Version.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.schemaupdate">
+
+ <class name="Version">
+ <id name="id">
+ <generator class="org.hibernate.id.TableHiLoGenerator">
+ <param name="table">uid_table</param>
+ <param name="column">next_hi_value_column</param>
+ </generator>
+ </id>
+ <property name="description"/>
+ </class>
+
+</hibernate-mapping>
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/schemaupdate/2_Version.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/schemaupdate/2_Version.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/schemaupdate/2_Version.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.schemaupdate">
+
+ <class name="Version" schema="PUBLIC">
+ <id name="id">
+ <generator class="org.hibernate.id.TableHiLoGenerator">
+ <param name="table">uid_table</param>
+<!-- <param name="schema">PUBLIC</param> -->
+ <param name="column">next_hi_value_column</param>
+ </generator>
+ </id>
+ <property name="description"/>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping>
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/schemaupdate/MigrationTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/schemaupdate/MigrationTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/schemaupdate/MigrationTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,47 @@
+package org.hibernate.test.schemaupdate;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.hibernate.cfg.Configuration;
+import org.hibernate.junit.UnitTestCase;
+import org.hibernate.tool.hbm2ddl.SchemaExport;
+import org.hibernate.tool.hbm2ddl.SchemaUpdate;
+
+/**
+ * @author Max Rydahl Andersen
+ */
+public class MigrationTest extends UnitTestCase {
+
+ public MigrationTest(String str) {
+ super( str );
+ }
+
+ public static Test suite() {
+ return new TestSuite( MigrationTest.class );
+ }
+
+ public void testSimpleColumnAddition() {
+ String resource1 = "org/hibernate/test/schemaupdate/1_Version.hbm.xml";
+ String resource2 = "org/hibernate/test/schemaupdate/2_Version.hbm.xml";
+
+ Configuration v1cfg = new Configuration();
+ v1cfg.addResource( resource1 );
+ new SchemaExport( v1cfg ).execute( false, true, true, false );
+
+ SchemaUpdate v1schemaUpdate = new SchemaUpdate( v1cfg );
+ v1schemaUpdate.execute( true, true );
+
+ assertEquals( 0, v1schemaUpdate.getExceptions().size() );
+
+ Configuration v2cfg = new Configuration();
+ v2cfg.addResource( resource2 );
+
+ SchemaUpdate v2schemaUpdate = new SchemaUpdate( v2cfg );
+ v2schemaUpdate.execute( true, true );
+ assertEquals( 0, v2schemaUpdate.getExceptions().size() );
+
+ }
+
+}
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/schemaupdate/Version.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/schemaupdate/Version.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/schemaupdate/Version.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,30 @@
+package org.hibernate.test.schemaupdate;
+
+public class Version {
+
+ long id;
+ String name;
+ String description;
+
+ 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 String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sorted/Search.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sorted/Search.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sorted/Search.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.sorted">
+
+ <class name="Search">
+ <id name="searchString"/>
+ <set name="searchResults" sort="natural">
+ <key column="searchString"/>
+ <element column="text" type="string"/>
+ </set>
+ </class>
+
+</hibernate-mapping>
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sorted/Search.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sorted/Search.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sorted/Search.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sorted/Search.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,29 @@
+//$Id: Search.java 7772 2005-08-05 23:03:46Z oneovthafew $
+package org.hibernate.test.sorted;
+
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+public class Search {
+ private String searchString;
+ private SortedSet searchResults = new TreeSet();
+
+ Search() {}
+
+ public Search(String string) {
+ searchString = string;
+ }
+
+ public SortedSet getSearchResults() {
+ return searchResults;
+ }
+ public void setSearchResults(SortedSet searchResults) {
+ this.searchResults = searchResults;
+ }
+ public String getSearchString() {
+ return searchString;
+ }
+ public void setSearchString(String searchString) {
+ this.searchString = searchString;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sorted/Search.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sorted/SortTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sorted/SortTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sorted/SortTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,79 @@
+//$Id: SortTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.sorted;
+
+import java.util.Iterator;
+
+import junit.framework.Test;
+
+import org.hibernate.FetchMode;
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class SortTest extends FunctionalTestCase {
+
+ public SortTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "sorted/Search.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( SortTest.class );
+ }
+
+ public void testOrderBy() {
+ Search s = new Search("Hibernate");
+ s.getSearchResults().add("jboss.com");
+ s.getSearchResults().add("hibernate.org");
+ s.getSearchResults().add("HiA");
+
+ Session sess = openSession();
+ Transaction tx = sess.beginTransaction();
+ sess.persist(s);
+ sess.flush();
+
+ sess.clear();
+ s = (Search) sess.createCriteria(Search.class).uniqueResult();
+ assertFalse( Hibernate.isInitialized( s.getSearchResults() ) );
+ Iterator iter = s.getSearchResults().iterator();
+ assertEquals( iter.next(), "HiA" );
+ assertEquals( iter.next(), "hibernate.org" );
+ assertEquals( iter.next(), "jboss.com" );
+ assertFalse( iter.hasNext() );
+
+ sess.clear();
+ s = (Search) sess.createCriteria(Search.class)
+ .setFetchMode("searchResults", FetchMode.JOIN)
+ .uniqueResult();
+ assertTrue( Hibernate.isInitialized( s.getSearchResults() ) );
+ iter = s.getSearchResults().iterator();
+ assertEquals( iter.next(), "HiA" );
+ assertEquals( iter.next(), "hibernate.org" );
+ assertEquals( iter.next(), "jboss.com" );
+ assertFalse( iter.hasNext() );
+
+ sess.clear();
+ s = (Search) sess.createQuery("from Search s left join fetch s.searchResults")
+ .uniqueResult();
+ assertTrue( Hibernate.isInitialized( s.getSearchResults() ) );
+ iter = s.getSearchResults().iterator();
+ assertEquals( iter.next(), "HiA" );
+ assertEquals( iter.next(), "hibernate.org" );
+ assertEquals( iter.next(), "jboss.com" );
+ assertFalse( iter.hasNext() );
+
+ sess.delete(s);
+ tx.commit();
+ sess.close();
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sorted/SortTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/NativeSqlSupportSuite.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/NativeSqlSupportSuite.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/NativeSqlSupportSuite.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,33 @@
+package org.hibernate.test.sql;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.hibernate.test.sql.check.CheckSuite;
+import org.hibernate.test.sql.hand.custom.datadirect.oracle.DataDirectOracleCustomSQLTest;
+import org.hibernate.test.sql.hand.custom.db2.DB2CustomSQLTest;
+import org.hibernate.test.sql.hand.custom.mysql.MySQLCustomSQLTest;
+import org.hibernate.test.sql.hand.custom.oracle.OracleCustomSQLTest;
+import org.hibernate.test.sql.hand.custom.sybase.SybaseCustomSQLTest;
+import org.hibernate.test.sql.hand.query.NativeSQLQueriesTest;
+import org.hibernate.test.sql.hand.identity.CustomInsertSQLWithIdentityColumnTest;
+
+/**
+ * Suite for testing aspects of native SQL support.
+ *
+ * @author Steve Ebersole
+ */
+public class NativeSqlSupportSuite {
+ public static Test suite() {
+ TestSuite suite = new TestSuite( "Native SQL support tests" );
+ suite.addTest( DB2CustomSQLTest.suite() );
+ suite.addTest( DataDirectOracleCustomSQLTest.suite() );
+ suite.addTest( OracleCustomSQLTest.suite() );
+ suite.addTest( SybaseCustomSQLTest.suite() );
+ suite.addTest( MySQLCustomSQLTest.suite() );
+ suite.addTest( NativeSQLQueriesTest.suite() );
+ suite.addTest( CheckSuite.suite() );
+ suite.addTest( CustomInsertSQLWithIdentityColumnTest.suite() );
+ return suite;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/check/CheckSuite.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/check/CheckSuite.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/check/CheckSuite.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,19 @@
+package org.hibernate.test.sql.check;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * Suite for testing custom SQL result checking strategies.
+ * <p/>
+ * Yes, currently there is only one actual test...
+ *
+ * @author Steve Ebersole
+ */
+public class CheckSuite {
+ public static Test suite() {
+ TestSuite suite = new TestSuite( "native sql result checking" );
+ suite.addTest( OracleCheckStyleTest.suite() );
+ return suite;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/check/ExceptionCheckingEntity.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/check/ExceptionCheckingEntity.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/check/ExceptionCheckingEntity.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,30 @@
+package org.hibernate.test.sql.check;
+
+/**
+ * An entity which is expected to be mapped to each database using stored
+ * procedures which throw exceptions on their own; in other words, using
+ * {@link org.hibernate.engine.ExecuteUpdateResultCheckStyle#NONE}.
+ *
+ * @author Steve Ebersole
+ */
+public class ExceptionCheckingEntity {
+ private Long id;
+ private String 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/testsuite/src/test/java/org/hibernate/test/sql/check/OracleCheckStyleTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/check/OracleCheckStyleTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/check/OracleCheckStyleTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,31 @@
+package org.hibernate.test.sql.check;
+
+import junit.framework.Test;
+
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.Oracle9Dialect;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * todo: describe OracleCheckStyleTest
+ *
+ * @author Steve Ebersole
+ */
+public class OracleCheckStyleTest extends ResultCheckStyleTest {
+ public OracleCheckStyleTest(String name) {
+ super( name );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "sql/check/oracle-mappings.hbm.xml" };
+ }
+
+ public boolean appliesTo(Dialect dialect) {
+ return dialect instanceof Oracle9Dialect;
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( OracleCheckStyleTest.class );
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/check/ParamCheckingEntity.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/check/ParamCheckingEntity.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/check/ParamCheckingEntity.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,29 @@
+package org.hibernate.test.sql.check;
+
+/**
+ * An entity which is expected to be mapped to each database using stored
+ * procedures which return "affected row counts"; in other words, using
+ * {@link org.hibernate.engine.ExecuteUpdateResultCheckStyle#PARAM}.
+ *
+ * @author Steve Ebersole
+ */
+public class ParamCheckingEntity {
+ private Long id;
+ private String 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/testsuite/src/test/java/org/hibernate/test/sql/check/ResultCheckStyleTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/check/ResultCheckStyleTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/check/ResultCheckStyleTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,134 @@
+package org.hibernate.test.sql.check;
+
+import org.hibernate.HibernateException;
+import org.hibernate.JDBCException;
+import org.hibernate.Session;
+import org.hibernate.junit.functional.DatabaseSpecificFunctionalTestCase;
+
+/**
+ * todo: describe ResultCheckStyleTest
+ *
+ * @author Steve Ebersole
+ */
+public abstract class ResultCheckStyleTest extends DatabaseSpecificFunctionalTestCase {
+
+ public ResultCheckStyleTest(String name) {
+ super( name );
+ }
+
+ public String getCacheConcurrencyStrategy() {
+ return null;
+ }
+
+ public void testInsertionFailureWithExceptionChecking() {
+ Session s = openSession();
+ s.beginTransaction();
+ ExceptionCheckingEntity e = new ExceptionCheckingEntity();
+ e.setName( "dummy" );
+ s.save( e );
+ try {
+ s.flush();
+ fail( "expection flush failure!" );
+ }
+ catch( JDBCException ex ) {
+ // these should specifically be JDBCExceptions...
+ }
+ s.clear();
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testInsertionFailureWithParamChecking() {
+ Session s = openSession();
+ s.beginTransaction();
+ ParamCheckingEntity e = new ParamCheckingEntity();
+ e.setName( "dummy" );
+ s.save( e );
+ try {
+ s.flush();
+ fail( "expection flush failure!" );
+ }
+ catch( HibernateException ex ) {
+ // these should specifically be HibernateExceptions...
+ }
+ s.clear();
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testUpdateFailureWithExceptionChecking() {
+ Session s = openSession();
+ s.beginTransaction();
+ ExceptionCheckingEntity e = new ExceptionCheckingEntity();
+ e.setId( new Long( 1 ) );
+ e.setName( "dummy" );
+ s.update( e );
+ try {
+ s.flush();
+ fail( "expection flush failure!" );
+ }
+ catch( JDBCException ex ) {
+ // these should specifically be JDBCExceptions...
+ }
+ s.clear();
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testUpdateFailureWithParamChecking() {
+ Session s = openSession();
+ s.beginTransaction();
+ ParamCheckingEntity e = new ParamCheckingEntity();
+ e.setId( new Long( 1 ) );
+ e.setName( "dummy" );
+ s.update( e );
+ try {
+ s.flush();
+ fail( "expection flush failure!" );
+ }
+ catch( HibernateException ex ) {
+ // these should specifically be HibernateExceptions...
+ }
+ s.clear();
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testDeleteWithExceptionChecking() {
+ Session s = openSession();
+ s.beginTransaction();
+ ExceptionCheckingEntity e = new ExceptionCheckingEntity();
+ e.setId( new Long( 1 ) );
+ e.setName( "dummy" );
+ s.delete( e );
+ try {
+ s.flush();
+ fail( "expection flush failure!" );
+ }
+ catch( JDBCException ex ) {
+ // these should specifically be JDBCExceptions...
+ }
+ s.clear();
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testDeleteWithParamChecking() {
+ Session s = openSession();
+ s.beginTransaction();
+ ParamCheckingEntity e = new ParamCheckingEntity();
+ e.setId( new Long( 1 ) );
+ e.setName( "dummy" );
+ s.delete( e );
+ try {
+ s.flush();
+ fail( "expection flush failure!" );
+ }
+ catch( HibernateException ex ) {
+ // these should specifically be HibernateExceptions...
+ }
+ s.clear();
+ s.getTransaction().commit();
+ s.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/check/oracle-mappings.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/check/oracle-mappings.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/check/oracle-mappings.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,103 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.sql.check">
+
+ <class name="ExceptionCheckingEntity" table="ENTITY_E">
+ <id name="id" unsaved-value="0" column="ID">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true"/>
+ <sql-insert callable="true" check="none">{call createEntityE(?,?)}</sql-insert>
+ <sql-update callable="true" check="none">{call updateEntityE(?,?)}</sql-update>
+ <sql-delete callable="true" check="none">{call deleteEntityE(?)}</sql-delete>
+ </class>
+
+ <class name="ParamCheckingEntity" table="ENTITY_P">
+ <id name="id" unsaved-value="0" column="ID">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true"/>
+ <sql-insert callable="true" check="param">{call createEntityP(?,?,?)}</sql-insert>
+ <sql-update callable="true" check="param">{? = call updateEntityP(?,?)}</sql-update>
+ <sql-delete callable="true" check="param">{? = call deleteEntityP(?)}</sql-delete>
+ </class>
+
+
+ <database-object>
+ <create>
+ CREATE OR REPLACE PROCEDURE createEntityE(p_name ENTITY_E.NAME%TYPE, p_id ENTITY_E.ID%TYPE)
+ AS BEGIN
+ RAISE_APPLICATION_ERROR( -20001, 'Insert failure checking' );
+ END;
+ </create>
+ <drop>
+ DROP PROCEDURE createEntityE;
+ </drop>
+ </database-object>
+
+ <database-object>
+ <create>
+ CREATE OR REPLACE PROCEDURE updateEntityE(p_name ENTITY_E.NAME%TYPE, p_id ENTITY_E.ID%TYPE)
+ AS BEGIN
+ RAISE_APPLICATION_ERROR( -20001, 'Update failure checking' );
+ END;
+ </create>
+ <drop>
+ DROP PROCEDURE updateEntityE;
+ </drop>
+ </database-object>
+
+ <database-object>
+ <create>
+ CREATE OR REPLACE PROCEDURE deleteEntityE(p_id ENTITY_E.ID%TYPE)
+ AS BEGIN
+ RAISE_APPLICATION_ERROR( -20001, 'Update failure checking' );
+ END;
+ </create>
+ <drop>
+ DROP PROCEDURE deleteEntityE;
+ </drop>
+ </database-object>
+
+
+ <database-object>
+ <!-- Demonstrate using an Oracle procedure and a registered OUT paramater as part of hand supplied sql -->
+ <create>
+ CREATE OR REPLACE PROCEDURE createEntityP(result OUT INTEGER, p_name ENTITY_E.NAME%TYPE, p_id ENTITY_E.ID%TYPE)
+ AS BEGIN
+ /* force a failure by returning a non-1 result */
+ result := 2;
+ END;
+ </create>
+ <drop>
+ DROP PROCEDURE createEntityP;
+ </drop>
+ </database-object>
+
+ <database-object>
+ <!-- Demonstrate using an Oracle function and it's return value as part of hand supplied sql -->
+ <create>
+ CREATE OR REPLACE FUNCTION updateEntityP(p_name ENTITY_E.NAME%TYPE, p_id ENTITY_E.ID%TYPE)
+ RETURN INTEGER IS BEGIN
+ RETURN 2;
+ END;
+ </create>
+ <drop>
+ DROP PROCEDURE updateEntityP;
+ </drop>
+ </database-object>
+
+ <database-object>
+ <create>
+ CREATE OR REPLACE FUNCTION deleteEntityP(p_id ENTITY_E.ID%TYPE)
+ RETURN INTEGER IS BEGIN
+ RETURN 2;
+ END;
+ </create>
+ <drop>
+ DROP PROCEDURE deleteEntityE;
+ </drop>
+ </database-object>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/Dimension.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/Dimension.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/Dimension.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,31 @@
+package org.hibernate.test.sql.hand;
+
+/**
+ * Used to mimic some tests from the JPA testsuite...
+ *
+ * @author Steve Ebersole
+ */
+public class Dimension {
+ private Long id;
+ private int length;
+ private int width;
+
+ public Dimension() {}
+
+ public Dimension(int length, int width) {
+ this.length = length;
+ this.width = width;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public int getLength() {
+ return length;
+ }
+
+ public int getWidth() {
+ return width;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/Employment.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/Employment.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/Employment.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,107 @@
+//$Id: Employment.java 11486 2007-05-08 21:57:24Z steve.ebersole at jboss.com $
+package org.hibernate.test.sql.hand;
+
+import java.util.Date;
+
+/**
+ * @author Gavin King
+ */
+public class Employment {
+ private long employmentId;
+ private Person employee;
+ private Organization employer;
+ private Date startDate;
+ private Date endDate;
+ private String regionCode;
+ private MonetaryAmount salary;
+
+ public Employment() {}
+
+ public Employment(Person employee, Organization employer, String regionCode) {
+ this.employee = employee;
+ this.employer = employer;
+ this.startDate = new Date();
+ this.regionCode = regionCode;
+ employer.getEmployments().add(this);
+ }
+ /**
+ * @return Returns the employee.
+ */
+ public Person getEmployee() {
+ return employee;
+ }
+ /**
+ * @param employee The employee to set.
+ */
+ public void setEmployee(Person employee) {
+ this.employee = employee;
+ }
+ /**
+ * @return Returns the employer.
+ */
+ public Organization getEmployer() {
+ return employer;
+ }
+ /**
+ * @param employer The employer to set.
+ */
+ public void setEmployer(Organization employer) {
+ this.employer = employer;
+ }
+ /**
+ * @return Returns the endDate.
+ */
+ public Date getEndDate() {
+ return endDate;
+ }
+ /**
+ * @param endDate The endDate to set.
+ */
+ public void setEndDate(Date endDate) {
+ this.endDate = endDate;
+ }
+ /**
+ * @return Returns the id.
+ */
+ public long getEmploymentId() {
+ return employmentId;
+ }
+ /**
+ * @param id The id to set.
+ */
+ public void setEmploymentId(long id) {
+ this.employmentId = id;
+ }
+ /**
+ * @return Returns the startDate.
+ */
+ public Date getStartDate() {
+ return startDate;
+ }
+ /**
+ * @param startDate The startDate to set.
+ */
+ public void setStartDate(Date startDate) {
+ this.startDate = startDate;
+ }
+ /**
+ * @return Returns the regionCode.
+ */
+ public String getRegionCode() {
+ return regionCode;
+ }
+ /**
+ * @param regionCode The regionCode to set.
+ */
+ public void setRegionCode(String regionCode) {
+ this.regionCode = regionCode;
+ }
+
+ public MonetaryAmount getSalary() {
+ return salary;
+ }
+
+ public void setSalary(MonetaryAmount salary) {
+ this.salary = salary;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/Employment.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/MonetaryAmount.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/MonetaryAmount.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/MonetaryAmount.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,78 @@
+package org.hibernate.test.sql.hand;
+
+import java.math.BigDecimal;
+import java.util.Currency;
+import java.io.Serializable;
+
+/**
+ * Represents a monetary amount as value and currency.
+ *
+ * @author Gavin King <gavin at hibernate.org>
+ * @author Christian Bauer <christian at hibernate.org>
+ */
+public class MonetaryAmount implements Serializable {
+
+ private final BigDecimal value;
+ private final Currency currency;
+
+ public MonetaryAmount(BigDecimal value, Currency currency) {
+ this.value = value;
+ this.currency = currency;
+ }
+
+ public Currency getCurrency() {
+ return currency;
+ }
+
+ public BigDecimal getValue() {
+ return value;
+ }
+
+ // ********************** Common Methods ********************** //
+
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof MonetaryAmount)) return false;
+
+ final MonetaryAmount monetaryAmount = (MonetaryAmount) o;
+
+ if (!currency.equals(monetaryAmount.currency)) return false;
+ if (!value.equals(monetaryAmount.value)) return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ int result;
+ result = value.hashCode();
+ result = 29 * result + currency.hashCode();
+ return result;
+ }
+
+ public String toString() {
+ return "Value: '" + getValue() + "', " +
+ "Currency: '" + getCurrency() + "'";
+ }
+
+ public int compareTo(Object o) {
+ if (o instanceof MonetaryAmount) {
+ // TODO: This would actually require some currency conversion magic
+ return this.getValue().compareTo(((MonetaryAmount) o).getValue());
+ }
+ return 0;
+ }
+
+ // ********************** Business Methods ********************** //
+
+ public static MonetaryAmount fromString(String amount, String currencyCode) {
+ return new MonetaryAmount(new BigDecimal(amount),
+ Currency.getInstance(currencyCode));
+ }
+
+ public static MonetaryAmount convert(MonetaryAmount amount,
+ Currency toConcurrency) {
+ // TODO: This requires some conversion magic and is therefore broken
+ return new MonetaryAmount(amount.getValue(), toConcurrency);
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/MonetaryAmountUserType.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/MonetaryAmountUserType.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/MonetaryAmountUserType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,85 @@
+package org.hibernate.test.sql.hand;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.Currency;
+
+import org.hibernate.HibernateException;
+import org.hibernate.usertype.UserType;
+
+/**
+ * This is a simple Hibernate custom mapping type for MonetaryAmount value types.
+ * <p>
+ *
+ * @author Max & Christian
+ */
+public class MonetaryAmountUserType
+ implements UserType {
+
+ private static final int[] SQL_TYPES = {Types.NUMERIC, Types.VARCHAR };
+
+ public int[] sqlTypes() { return SQL_TYPES; }
+
+ public Class returnedClass() { return MonetaryAmount.class; }
+
+ public boolean isMutable() { return false; }
+
+ public Object deepCopy(Object value) {
+ return value; // MonetaryAmount is immutable
+ }
+
+ public boolean equals(Object x, Object y) {
+ if (x == y) return true;
+ if (x == null || y == null) return false;
+ return x.equals(y);
+ }
+
+ public Object nullSafeGet(ResultSet resultSet,
+ String[] names,
+ Object owner)
+ throws HibernateException, SQLException {
+
+ BigDecimal value = resultSet.getBigDecimal(names[0]);
+ if (resultSet.wasNull()) return null;
+ String cur = resultSet.getString(names[1]);
+ Currency userCurrency = Currency.getInstance(cur);
+
+ return new MonetaryAmount(value, userCurrency);
+ }
+
+ public void nullSafeSet(PreparedStatement statement,
+ Object value,
+ int index)
+ throws HibernateException, SQLException {
+
+ if (value == null) {
+ statement.setNull(index, Types.NUMERIC);
+ statement.setNull(index+1, Types.VARCHAR);
+ } else {
+ MonetaryAmount currency = (MonetaryAmount)value;
+ statement.setBigDecimal(index, currency.getValue());
+ statement.setString(index+1, currency.getCurrency().getCurrencyCode());
+ }
+ }
+
+ public Serializable disassemble(Object value) throws HibernateException {
+ return (Serializable) value;
+ }
+
+ public Object assemble(Serializable cached, Object owner) throws HibernateException {
+ return cached;
+ }
+
+ public Object replace(Object original, Object target, Object owner)
+ throws HibernateException {
+ return original;
+ }
+
+ public int hashCode(Object x) throws HibernateException {
+ return x.hashCode();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/Order.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/Order.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/Order.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,55 @@
+package org.hibernate.test.sql.hand;
+
+import java.io.Serializable;
+
+public class Order {
+
+ static public class OrderId implements Serializable {
+ String orgid;
+ String ordernumber;
+ public String getOrdernumber() {
+ return ordernumber;
+ }
+ public void setOrdernumber(String ordernumber) {
+ this.ordernumber = ordernumber;
+ }
+ public String getOrgid() {
+ return orgid;
+ }
+ public void setOrgid(String orgid) {
+ this.orgid = orgid;
+ }
+
+
+ }
+
+ OrderId orderId;
+
+ Product product;
+
+ Person person;
+
+ public Person getPerson() {
+ return person;
+ }
+
+ public void setPerson(Person person) {
+ this.person = person;
+ }
+ public OrderId getOrderId() {
+ return orderId;
+ }
+
+ public void setOrderId(OrderId orderId) {
+ this.orderId = orderId;
+ }
+
+ public Product getProduct() {
+ return product;
+ }
+
+ public void setProduct(Product product) {
+ this.product = product;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/Organization.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/Organization.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/Organization.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,59 @@
+//$Id: Organization.java 11486 2007-05-08 21:57:24Z steve.ebersole at jboss.com $
+package org.hibernate.test.sql.hand;
+
+import java.util.Collection;
+import java.util.HashSet;
+
+/**
+ * @author Gavin King
+ */
+public class Organization {
+ private long id;
+ private String name;
+ private Collection employments;
+
+ public Organization(String name) {
+ this.name = name;
+ employments = new HashSet();
+ }
+
+ public Organization() {}
+
+ /**
+ * @return Returns the employments.
+ */
+ public Collection getEmployments() {
+ return employments;
+ }
+ /**
+ * @param employments The employments to set.
+ */
+ public void setEmployments(Collection employments) {
+ this.employments = employments;
+ }
+ /**
+ * @return Returns the id.
+ */
+ public long getId() {
+ return id;
+ }
+ /**
+ * @param id The id to set.
+ */
+ public void setId(long id) {
+ this.id = id;
+ }
+ /**
+ * @return Returns the name.
+ */
+ public String getName() {
+ return name;
+ }
+ /**
+ * @param name The name to set.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/Organization.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/Person.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/Person.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/Person.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,41 @@
+//$Id: Person.java 11486 2007-05-08 21:57:24Z steve.ebersole at jboss.com $
+package org.hibernate.test.sql.hand;
+
+/**
+ * @author Gavin King
+ */
+public class Person {
+ private long id;
+ private String name;
+
+ public Person(String name) {
+ this.name = name;
+ }
+
+ public Person() {}
+
+/**
+ * @return Returns the id.
+ */
+ public long getId() {
+ return id;
+ }
+ /**
+ * @param id The id to set.
+ */
+ public void setId(long id) {
+ this.id = id;
+ }
+ /**
+ * @return Returns the name.
+ */
+ public String getName() {
+ return name;
+ }
+ /**
+ * @param name The name to set.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/Person.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/Product.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/Product.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/Product.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,66 @@
+package org.hibernate.test.sql.hand;
+
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Set;
+
+public class Product {
+
+ static public class ProductId implements Serializable {
+ String orgid;
+ String productnumber;
+ public String getProductnumber() {
+ return productnumber;
+ }
+ public void setProductnumber(String ordernumber) {
+ this.productnumber = ordernumber;
+ }
+ public String getOrgid() {
+ return orgid;
+ }
+ public void setOrgid(String orgid) {
+ this.orgid = orgid;
+ }
+
+
+ }
+
+ ProductId productId;
+
+ String name;
+
+ Person person;
+
+ Set orders = new HashSet();
+
+ public Set getOrders() {
+ return orders;
+ }
+
+ public void setOrders(Set orders) {
+ this.orders = orders;
+ }
+ public Person getPerson() {
+ return person;
+ }
+
+ public void setPerson(Person person) {
+ this.person = person;
+ }
+ public ProductId getProductId() {
+ return productId;
+ }
+
+ public void setProductId(ProductId orderId) {
+ this.productId = orderId;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String product) {
+ this.name = product;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/SpaceShip.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/SpaceShip.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/SpaceShip.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,53 @@
+//$Id: $
+package org.hibernate.test.sql.hand;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class SpaceShip {
+ private Integer id;
+ private String name;
+ private String model;
+ private double speed;
+ private Dimension dimensions;
+
+ public Dimension getDimensions() {
+ return dimensions;
+ }
+
+ public void setDimensions(Dimension dimensions) {
+ this.dimensions = dimensions;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getModel() {
+ return model;
+ }
+
+ public void setModel(String model) {
+ this.model = model;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public double getSpeed() {
+ return speed;
+ }
+
+ public void setSpeed(double speed) {
+ this.speed = speed;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/Speech.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/Speech.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/Speech.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,35 @@
+//$Id: $
+package org.hibernate.test.sql.hand;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class Speech {
+ private Integer id;
+ private String name;
+ private Double length;
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public Double getLength() {
+ return length;
+ }
+
+ public void setLength(Double length) {
+ this.length = length;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/CustomSQLTestSupport.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/CustomSQLTestSupport.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/CustomSQLTestSupport.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,94 @@
+package org.hibernate.test.sql.hand.custom;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.Iterator;
+
+import org.hibernate.LockMode;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.test.sql.hand.Employment;
+import org.hibernate.test.sql.hand.Organization;
+import org.hibernate.test.sql.hand.Person;
+import org.hibernate.junit.functional.DatabaseSpecificFunctionalTestCase;
+
+/**
+ * Abstract test case defining tests for the support for user-supplied (aka
+ * custom) insert, update, delete SQL.
+ *
+ * @author Steve Ebersole
+ */
+public abstract class CustomSQLTestSupport extends DatabaseSpecificFunctionalTestCase {
+
+ public CustomSQLTestSupport(String name) {
+ super( name );
+ }
+
+ public String getCacheConcurrencyStrategy() {
+ return null;
+ }
+
+ public void testHandSQL() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Organization ifa = new Organization( "IFA" );
+ Organization jboss = new Organization( "JBoss" );
+ Person gavin = new Person( "Gavin" );
+ Employment emp = new Employment( gavin, jboss, "AU" );
+ Serializable orgId = s.save( jboss );
+ s.save( ifa );
+ s.save( gavin );
+ s.save( emp );
+ t.commit();
+
+ t = s.beginTransaction();
+ Person christian = new Person( "Christian" );
+ s.save( christian );
+ Employment emp2 = new Employment( christian, jboss, "EU" );
+ s.save( emp2 );
+ t.commit();
+ s.close();
+
+ getSessions().evict( Organization.class );
+ getSessions().evict( Person.class );
+ getSessions().evict( Employment.class );
+
+ s = openSession();
+ t = s.beginTransaction();
+ jboss = ( Organization ) s.get( Organization.class, orgId );
+ assertEquals( jboss.getEmployments().size(), 2 );
+ assertEquals( jboss.getName(), "JBOSS" );
+ emp = ( Employment ) jboss.getEmployments().iterator().next();
+ gavin = emp.getEmployee();
+ assertEquals( gavin.getName(), "GAVIN" );
+ assertEquals( s.getCurrentLockMode( gavin ), LockMode.UPGRADE );
+ emp.setEndDate( new Date() );
+ Employment emp3 = new Employment( gavin, jboss, "US" );
+ s.save( emp3 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Iterator iter = s.getNamedQuery( "allOrganizationsWithEmployees" ).list().iterator();
+ assertTrue( iter.hasNext() );
+ Organization o = ( Organization ) iter.next();
+ assertEquals( o.getEmployments().size(), 3 );
+ Iterator iter2 = o.getEmployments().iterator();
+ while ( iter2.hasNext() ) {
+ Employment e = ( Employment ) iter2.next();
+ s.delete( e );
+ }
+ iter2 = o.getEmployments().iterator();
+ while ( iter2.hasNext() ) {
+ Employment e = ( Employment ) iter2.next();
+ s.delete( e.getEmployee() );
+ }
+ s.delete( o );
+ assertFalse( iter.hasNext() );
+ s.delete( ifa );
+ t.commit();
+ s.close();
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/CustomStoredProcTestSupport.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/CustomStoredProcTestSupport.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/CustomStoredProcTestSupport.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,83 @@
+package org.hibernate.test.sql.hand.custom;
+
+import java.sql.SQLException;
+import java.util.List;
+
+import org.hibernate.HibernateException;
+import org.hibernate.Query;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.test.sql.hand.Employment;
+import org.hibernate.test.sql.hand.Organization;
+import org.hibernate.test.sql.hand.Person;
+
+/**
+ * Abstract test case defining tests of stored procedure support.
+ *
+ * @author Gail Badner
+ */
+public abstract class CustomStoredProcTestSupport extends CustomSQLTestSupport {
+
+ public CustomStoredProcTestSupport(String name) {
+ super( name );
+ }
+
+ public void testScalarStoredProcedure() throws HibernateException, SQLException {
+ Session s = openSession();
+ Query namedQuery = s.getNamedQuery( "simpleScalar" );
+ namedQuery.setLong( "number", 43 );
+ List list = namedQuery.list();
+ Object o[] = ( Object[] ) list.get( 0 );
+ assertEquals( o[0], "getAll" );
+ assertEquals( o[1], new Long( 43 ) );
+ s.close();
+ }
+
+ public void testParameterHandling() throws HibernateException, SQLException {
+ Session s = openSession();
+
+ Query namedQuery = s.getNamedQuery( "paramhandling" );
+ namedQuery.setLong( 0, 10 );
+ namedQuery.setLong( 1, 20 );
+ List list = namedQuery.list();
+ Object[] o = ( Object[] ) list.get( 0 );
+ assertEquals( o[0], new Long( 10 ) );
+ assertEquals( o[1], new Long( 20 ) );
+
+ namedQuery = s.getNamedQuery( "paramhandling_mixed" );
+ namedQuery.setLong( 0, 10 );
+ namedQuery.setLong( "second", 20 );
+ list = namedQuery.list();
+ o = ( Object[] ) list.get( 0 );
+ assertEquals( o[0], new Long( 10 ) );
+ assertEquals( o[1], new Long( 20 ) );
+ s.close();
+ }
+
+ public void testEntityStoredProcedure() throws HibernateException, SQLException {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ Organization ifa = new Organization( "IFA" );
+ Organization jboss = new Organization( "JBoss" );
+ Person gavin = new Person( "Gavin" );
+ Employment emp = new Employment( gavin, jboss, "AU" );
+ s.persist( ifa );
+ s.persist( jboss );
+ s.persist( gavin );
+ s.persist( emp );
+
+ Query namedQuery = s.getNamedQuery( "selectAllEmployments" );
+ List list = namedQuery.list();
+ assertTrue( list.get( 0 ) instanceof Employment );
+ s.delete( emp );
+ s.delete( ifa );
+ s.delete( jboss );
+ s.delete( gavin );
+
+ t.commit();
+ s.close();
+ }
+
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/datadirect/oracle/DataDirectOracleCustomSQLTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/datadirect/oracle/DataDirectOracleCustomSQLTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/datadirect/oracle/DataDirectOracleCustomSQLTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,35 @@
+//$Id$
+package org.hibernate.test.sql.hand.custom.datadirect.oracle;
+
+import junit.framework.Test;
+
+import org.hibernate.dialect.DataDirectOracle9Dialect;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.test.sql.hand.custom.CustomStoredProcTestSupport;
+
+/**
+ * Custom SQL tests for Oracle via the DataDirect drivers.
+ *
+ * @author Max Rydahl Andersen
+ */
+public class DataDirectOracleCustomSQLTest extends CustomStoredProcTestSupport {
+
+ public DataDirectOracleCustomSQLTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "sql/hand/custom/oracle/Mappings.hbm.xml", "sql/hand/custom/datadirect/oracle/StoredProcedures.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( DataDirectOracleCustomSQLTest.class );
+ }
+
+ public boolean appliesTo(Dialect dialect) {
+ return ( dialect instanceof DataDirectOracle9Dialect );
+ }
+
+}
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/datadirect/oracle/StoredProcedures.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/datadirect/oracle/StoredProcedures.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/datadirect/oracle/StoredProcedures.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,44 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+ This version is for DataDirect drivers jdbc standard handling of stored procedures/functions.
+-->
+<hibernate-mapping package="org.hibernate.test.sql.hand" default-access="field">
+
+ <sql-query name="simpleScalar" callable="true">
+ <return-scalar column="name" type="string"/>
+ <return-scalar column="value" type="long"/>
+ { call simpleScalar(:number) }
+ </sql-query>
+ <sql-query name="paramhandling" callable="true">
+ <return-scalar column="value" type="long" />
+ <return-scalar column="value2" type="long" />
+ { call testParamHandling(?,?) }
+ </sql-query>
+
+ <sql-query name="paramhandling_mixed" callable="true">
+ <return-scalar column="value" type="long" />
+ <return-scalar column="value2" type="long" />
+ { call testParamHandling(?,:second) }
+ </sql-query>
+
+ <sql-query name="selectAllEmployments" callable="true">
+ <return alias="emp" class="Employment">
+ <return-property name="employee" column="EMPLOYEE"/>
+ <return-property name="employer" column="EMPLOYER"/>
+ <return-property name="startDate" column="STARTDATE"/>
+ <return-property name="endDate" column="ENDDATE"/>
+ <return-property name="regionCode" column="REGIONCODE"/>
+ <return-property name="employmentId" column="EMPID"/>
+ <return-property name="salary">
+ <return-column name="VALUE"/>
+ <return-column name="CURRENCY"/>
+ </return-property>
+ </return>
+ { call allEmployments() }
+ </sql-query>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/db2/DB2CustomSQLTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/db2/DB2CustomSQLTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/db2/DB2CustomSQLTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,35 @@
+//$Id$
+package org.hibernate.test.sql.hand.custom.db2;
+
+import junit.framework.Test;
+
+import org.hibernate.dialect.DB2Dialect;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.test.sql.hand.custom.CustomStoredProcTestSupport;
+
+/**
+ * Custom SQL tests for DB2
+ *
+ * @author Max Rydahl Andersen
+ */
+public class DB2CustomSQLTest extends CustomStoredProcTestSupport {
+
+ public DB2CustomSQLTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "sql/hand/custom/db2/Mappings.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( DB2CustomSQLTest.class );
+ }
+
+ public boolean appliesTo(Dialect dialect) {
+ return ( dialect instanceof DB2Dialect);
+ }
+
+}
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/db2/Mappings.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/db2/Mappings.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/db2/Mappings.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,236 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+ This mapping demonstrates the use of Hibernate with all-handwritten SQL!
+
+ NOTE : this version is for DB2 & variants
+-->
+<hibernate-mapping package="org.hibernate.test.sql.hand" default-access="field">
+
+ <class name="Organization" table="ORGANIZATION">
+ <id name="id" unsaved-value="0" column="ORGID">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true" column="NAME"/>
+ <set name="employments"
+ inverse="true"
+ order-by="DUMMY">
+ <key column="EMPLOYER"/> <!-- only needed for DDL generation -->
+ <one-to-many class="Employment"/>
+ <loader query-ref="organizationEmployments"/>
+ </set>
+ <!-- query-list name="currentEmployments"
+ query-ref="organizationCurrentEmployments"-->
+ <loader query-ref="organization"/>
+ <sql-insert>INSERT INTO ORGANIZATION (NAME, ORGID) VALUES ( UPPER(? || ''), ? )</sql-insert>
+ <sql-update>UPDATE ORGANIZATION SET NAME=UPPER(? || '') WHERE ORGID=?</sql-update>
+ <sql-delete>DELETE FROM ORGANIZATION WHERE ORGID=?</sql-delete>
+ </class>
+
+ <class name="Person" table="PERSON">
+ <id name="id" unsaved-value="0" column="PERID">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true" column="NAME"/>
+ <loader query-ref="person"/>
+ <sql-insert>INSERT INTO PERSON (NAME, PERID) VALUES ( UPPER(? || ''), ? )</sql-insert>
+ <sql-update>UPDATE PERSON SET NAME=UPPER(? || '') WHERE PERID=?</sql-update>
+ <sql-delete>DELETE FROM PERSON WHERE PERID=?</sql-delete>
+ </class>
+
+ <class name="Employment" table="EMPLOYMENT">
+ <id name="employmentId" unsaved-value="0" column="EMPID">
+ <generator class="increment"/>
+ </id>
+ <many-to-one name="employee" column="EMPLOYEE" not-null="true" update="false"/>
+ <many-to-one name="employer" column="EMPLOYER" not-null="true" update="false"/>
+ <property name="startDate" column="STARTDATE" not-null="true" update="false" insert="false"/>
+ <property name="endDate" column="ENDDATE" insert="false"/>
+ <property name="regionCode" column="REGIONCODE" update="false"/>
+ <property name="salary" type="org.hibernate.test.sql.hand.MonetaryAmountUserType">
+ <column name="VALUE" sql-type="float"/>
+ <column name="CURRENCY"/>
+ </property>
+ <loader query-ref="employment"/>
+ <sql-insert>
+ INSERT INTO EMPLOYMENT
+ (EMPLOYEE, EMPLOYER, STARTDATE, REGIONCODE, VALUE, CURRENCY, EMPID)
+ <!-- inserting raw specific timestamp since using current date or current timestamp for some reason does not work with a timestamp column! -->
+ VALUES (?, ?, TIMESTAMP ('2006-02-28 11:39:00'), UPPER(? || ''), ?, ?, ?)
+ </sql-insert>
+ <sql-update>UPDATE EMPLOYMENT SET ENDDATE=?, VALUE=?, CURRENCY=? WHERE EMPID=?</sql-update>
+ <sql-delete>DELETE FROM EMPLOYMENT WHERE EMPID=?</sql-delete>
+ </class>
+
+ <resultset name="org-emp-regionCode">
+ <return-scalar column="regionCode" type="string"/>
+ <return alias="org" class="Organization"/>
+ <return-join alias="emp" property="org.employments"/>
+ </resultset>
+
+ <resultset name="org-emp-person">
+ <return alias="org" class="Organization"/>
+ <return-join alias="emp" property="org.employments"/>
+ <return-join alias="pers" property="emp.employee"/>
+ </resultset>
+
+ <sql-query name="person">
+ <return alias="p" class="Person" lock-mode="upgrade"/>
+ SELECT NAME AS {p.name}, PERID AS {p.id} FROM PERSON WHERE PERID=? /*FOR UPDATE*/
+ </sql-query>
+
+ <sql-query name="organization">
+ <return alias="org" class="Organization"/>
+ <return-join alias="emp" property="org.employments"/>
+ SELECT {org.*}, {emp.*}
+ FROM ORGANIZATION org
+ LEFT OUTER JOIN EMPLOYMENT emp ON org.ORGID = emp.EMPLOYER
+ WHERE org.ORGID=?
+ </sql-query>
+
+
+ <!--sql-query name="organization">
+ <return alias="org" class="Organization"/>
+ SELECT NAME AS {org.name}, ORGID AS {org.id} FROM ORGANIZATION
+ WHERE ORGID=?
+ </sql-query-->
+
+ <sql-query name="allOrganizationsWithEmployees" flush-mode="never">
+ <return alias="org" class="Organization"/>
+ SELECT DISTINCT org.NAME AS {org.name}, org.ORGID AS {org.id}
+ FROM ORGANIZATION org
+ INNER JOIN EMPLOYMENT e ON e.EMPLOYER = org.ORGID
+ </sql-query>
+
+
+
+
+
+ <sql-query name="employment">
+ <return alias="emp" class="Employment"/>
+ SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer},
+ STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},
+ REGIONCODE as {emp.regionCode}, EMPID AS {emp.id}
+ FROM EMPLOYMENT
+ WHERE EMPID = ?
+ </sql-query>
+
+ <sql-query name="organizationEmployments">
+ <load-collection alias="empcol" role="Organization.employments"/>
+ SELECT {empcol.*}
+ FROM EMPLOYMENT empcol
+ WHERE EMPLOYER = :id
+ ORDER BY STARTDATE ASC, EMPLOYEE ASC
+ </sql-query>
+
+
+ <sql-query name="organizationCurrentEmployments">
+ <return alias="emp" class="Employment">
+ <return-property name="salary">
+ <!-- as multi column properties are not supported via the
+ {}-syntax, we need to provide an explicit column list for salary via <return-property> -->
+ <return-column name="VALUE"/>
+ <return-column name="CURRENCY"/>
+ </return-property>
+ <!-- Here we are remapping endDate. Notice that we can still use {emp.endDate} in the SQL. -->
+ <return-property name="endDate" column="myEndDate"/>
+ </return>
+ <synchronize table="EMPLOYMENT"/>
+ SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer},
+ STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},
+ REGIONCODE as {emp.regionCode}, EMPID AS {emp.id}, VALUE, CURRENCY
+ FROM EMPLOYMENT
+ WHERE EMPLOYER = :id AND ENDDATE IS NULL
+ ORDER BY STARTDATE ASC
+ </sql-query>
+
+ <sql-query name="simpleScalar" callable="true">
+ <return-scalar column="name" type="string"/>
+ <return-scalar column="value" type="long"/>
+ { call HIBDB2TST.simpleScalar(:number) }
+ </sql-query>
+
+ <sql-query name="paramhandling" callable="true">
+ <return-scalar column="value" type="long"/>
+ <return-scalar column="value2" type="long"/>
+ { call HIBDB2TST.paramHandling(?,?) }
+ </sql-query>
+
+ <sql-query name="paramhandling_mixed" callable="true">
+ <return-scalar column="value" type="long" />
+ <return-scalar column="value2" type="long" />
+ { call HIBDB2TST.paramHandling(?,:second) }
+ </sql-query>
+
+ <sql-query name="selectAllEmployments" callable="true">
+ <return alias="" class="Employment">
+ <return-property name="employee" column="EMPLOYEE"/>
+ <return-property name="employer" column="EMPLOYER"/>
+ <return-property name="startDate" column="STARTDATE"/>
+ <return-property name="endDate" column="ENDDATE"/>
+ <return-property name="regionCode" column="REGIONCODE"/>
+ <return-property name="id" column="EMPID"/>
+ <return-property name="salary">
+ <!-- as multi column properties are not supported via the
+ {}-syntax, we need to provide an explicit column list for salary via <return-property> -->
+ <return-column name="VALUE"/>
+ <return-column name="CURRENCY"/>
+ </return-property>
+ </return>
+ { call HIBDB2TST.selectAllEmployments() }
+ </sql-query>
+
+ <!-- DB2 seem to *require* users to specify explicit schema/location when executing a stored procedure *stupid* -->
+ <database-object>
+ <create>CREATE SCHEMA HIBDB2TST</create>
+ <drop>DROP SCHEMA HIBDB2TST RESTRICT</drop>
+ </database-object>
+
+ <database-object>
+ <create>
+ CREATE PROCEDURE HIBDB2TST.selectAllEmployments ()
+ P1: BEGIN
+ DECLARE C1 CURSOR WITH RETURN FOR
+ SELECT EMPLOYEE, EMPLOYER, STARTDATE, ENDDATE,
+ REGIONCODE, EMPID, VALUE, CURRENCY
+ FROM EMPLOYMENT;
+ OPEN C1;
+ END P1
+ </create>
+ <drop>
+ DROP PROCEDURE HIBDB2TST.selectAllEmployments
+ </drop>
+ </database-object>
+
+
+ <database-object>
+ <create>
+ CREATE PROCEDURE HIBDB2TST.paramHandling (IN j SMALLINT, IN i SMALLINT)
+ P1: BEGIN
+ DECLARE C1 CURSOR WITH RETURN FOR
+ SELECT j as value, i as value2 from sysibm.sysdummy1;
+ OPEN C1;
+ END P1
+ </create>
+ <drop>
+ DROP PROCEDURE HIBDB2TST.paramHandling
+ </drop>
+ </database-object>
+
+ <database-object>
+ <create>
+ CREATE PROCEDURE HIBDB2TST.simpleScalar (IN j SMALLINT)
+ P1: BEGIN
+ DECLARE C1 CURSOR WITH RETURN FOR
+ SELECT j as value, 'getAll' as name from sysibm.sysdummy1;
+ OPEN C1;
+ END P1
+ </create>
+ <drop>
+ DROP PROCEDURE HIBDB2TST.simpleScalar
+ </drop>
+ </database-object>
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/mysql/Mappings.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/mysql/Mappings.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/mysql/Mappings.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,216 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+ This mapping demonstrates the use of Hibernate with all-handwritten SQL!
+
+ This version is for MySQL
+-->
+
+<hibernate-mapping package="org.hibernate.test.sql.hand" default-access="field">
+
+ <class name="Organization">
+ <id name="id" unsaved-value="0" column="orgid">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true"/>
+ <set name="employments"
+ inverse="true"
+ order-by="DUMMY">
+ <key column="employer"/> <!-- only needed for DDL generation -->
+ <one-to-many class="Employment"/>
+ <loader query-ref="organizationEmployments"/>
+ </set>
+ <!-- query-list name="currentEmployments"
+ query-ref="organizationCurrentEmployments"-->
+ <loader query-ref="organization"/>
+ <sql-insert>INSERT INTO ORGANIZATION (NAME, ORGID) VALUES ( UPPER(?), ? )</sql-insert>
+ <sql-update>UPDATE ORGANIZATION SET NAME=UPPER(?) WHERE ORGID=?</sql-update>
+ <sql-delete>DELETE FROM ORGANIZATION WHERE ORGID=?</sql-delete>
+ </class>
+
+ <class name="Person">
+ <id name="id" unsaved-value="0" column="perid">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true"/>
+ <loader query-ref="person"/>
+ <sql-insert>INSERT INTO PERSON (NAME, PERID) VALUES ( UPPER(?), ? )</sql-insert>
+ <sql-update>UPDATE PERSON SET NAME=UPPER(?) WHERE PERID=?</sql-update>
+ <sql-delete>DELETE FROM PERSON WHERE PERID=?</sql-delete>
+ </class>
+
+ <class name="Employment">
+ <id name="employmentId" unsaved-value="0" column="empid">
+ <generator class="increment"/>
+ </id>
+ <many-to-one name="employee" not-null="true" update="false"/>
+ <many-to-one name="employer" not-null="true" update="false"/>
+ <property name="startDate" not-null="true" update="false" insert="false"/>
+ <property name="endDate" insert="false"/>
+ <property name="regionCode" update="false"/>
+ <property name="salary" type="org.hibernate.test.sql.hand.MonetaryAmountUserType">
+ <column name="VALUE" sql-type="float"/>
+ <column name="CURRENCY"/>
+ </property>
+ <loader query-ref="employment"/>
+ <sql-insert>
+ INSERT INTO EMPLOYMENT
+ (EMPLOYEE, EMPLOYER, STARTDATE, REGIONCODE, VALUE, CURRENCY, EMPID)
+ VALUES (?, ?, now(), UPPER(?), ?, ?, ?)
+ </sql-insert>
+ <sql-update>UPDATE EMPLOYMENT SET ENDDATE=?, VALUE=?, CURRENCY=? WHERE EMPID=?</sql-update>
+ <sql-delete>DELETE FROM EMPLOYMENT WHERE EMPID=?</sql-delete>
+ </class>
+
+ <resultset name="org-emp-regionCode">
+ <return-scalar column="regionCode" type="string"/>
+ <return alias="org" class="Organization"/>
+ <return-join alias="emp" property="org.employments"/>
+ </resultset>
+
+ <resultset name="org-emp-person">
+ <return alias="org" class="Organization"/>
+ <return-join alias="emp" property="org.employments"/>
+ <return-join alias="pers" property="emp.employee"/>
+ </resultset>
+
+ <sql-query name="person">
+ <return alias="p" class="Person" lock-mode="upgrade"/>
+ SELECT NAME AS {p.name}, PERID AS {p.id} FROM PERSON WHERE PERID=? /*FOR UPDATE*/
+ </sql-query>
+
+ <sql-query name="organization">
+ <return alias="org" class="Organization"/>
+ <return-join alias="emp" property="org.employments"/>
+ SELECT {org.*}, {emp.*}
+ FROM ORGANIZATION org
+ LEFT OUTER JOIN EMPLOYMENT emp ON org.ORGID = emp.EMPLOYER
+ WHERE org.ORGID=?
+ </sql-query>
+
+
+
+ <!--sql-query name="organization">
+ <return alias="org" class="Organization"/>
+ SELECT NAME AS {org.name}, ORGID AS {org.id} FROM ORGANIZATION
+ WHERE ORGID=?
+ </sql-query-->
+
+ <sql-query name="allOrganizationsWithEmployees" flush-mode="never">
+ <return alias="org" class="Organization"/>
+ SELECT DISTINCT org.NAME AS {org.name}, org.ORGID AS {org.id}
+ FROM ORGANIZATION org
+ INNER JOIN EMPLOYMENT e ON e.EMPLOYER = org.ORGID
+ </sql-query>
+
+ <sql-query name="employment">
+ <return alias="emp" class="Employment"/>
+ SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer},
+ STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},
+ REGIONCODE as {emp.regionCode}, EMPID AS {emp.id}
+ FROM EMPLOYMENT
+ WHERE EMPID = ?
+ </sql-query>
+
+
+ <sql-query name="organizationEmployments">
+ <load-collection alias="empcol" role="Organization.employments"/>
+ SELECT {empcol.*}
+ FROM EMPLOYMENT empcol
+ WHERE EMPLOYER = :id
+ ORDER BY STARTDATE ASC, EMPLOYEE ASC
+ </sql-query>
+
+
+ <sql-query name="organizationCurrentEmployments">
+ <return alias="emp" class="Employment">
+ <return-property name="salary">
+ <!-- as multi column properties are not supported via the
+ {}-syntax, we need to provide an explicit column list for salary via <return-property> -->
+ <return-column name="VALUE"/>
+ <return-column name="CURRENCY"/>
+ </return-property>
+ <!-- Here we are remapping endDate. Notice that we can still use {emp.endDate} in the SQL. -->
+ <return-property name="endDate" column="myEndDate"/>
+ </return>
+ <synchronize table="EMPLOYMENT"/>
+ SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer},
+ STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},
+ REGIONCODE as {emp.regionCode}, EMPID AS {emp.id}, VALUE, CURRENCY
+ FROM EMPLOYMENT
+ WHERE EMPLOYER = :id AND ENDDATE IS NULL
+ ORDER BY STARTDATE ASC
+ </sql-query>
+
+ <sql-query name="simpleScalar" callable="true">
+ <return-scalar column="name" type="string"/>
+ <return-scalar column="value" type="long"/>
+ { call simpleScalar(:number) }
+ </sql-query>
+
+ <sql-query name="paramhandling" callable="true">
+ <return-scalar column="value" type="long"/>
+ <return-scalar column="value2" type="long"/>
+ { call paramHandling(?,?) }
+ </sql-query>
+
+ <sql-query name="paramhandling_mixed" callable="true">
+ <return-scalar column="value" type="long" />
+ <return-scalar column="value2" type="long" />
+ { call paramHandling(?,:second) }
+ </sql-query>
+
+ <sql-query name="selectAllEmployments" callable="true">
+ <return alias="" class="Employment">
+ <return-property name="employee" column="EMPLOYEE"/>
+ <return-property name="employer" column="EMPLOYER"/>
+ <return-property name="startDate" column="STARTDATE"/>
+ <return-property name="endDate" column="ENDDATE"/>
+ <return-property name="regionCode" column="REGIONCODE"/>
+ <return-property name="id" column="EMPID"/>
+ <return-property name="salary">
+ <!-- as multi column properties are not supported via the
+ {}-syntax, we need to provide an explicit column list for salary via <return-property> -->
+ <return-column name="VALUE"/>
+ <return-column name="CURRENCY"/>
+ </return-property>
+ </return>
+ { call selectAllEmployments() }
+ </sql-query>
+
+ <database-object>
+ <create>
+ CREATE PROCEDURE selectAllEmployments ()
+ SELECT EMPLOYEE, EMPLOYER, STARTDATE, ENDDATE,
+ REGIONCODE, EMPID, VALUE, CURRENCY
+ FROM EMPLOYMENT
+ </create>
+ <drop>
+ DROP PROCEDURE selectAllEmployments
+ </drop>
+ </database-object>
+
+ <database-object>
+ <create>
+ CREATE PROCEDURE paramHandling (j int, i int)
+ SELECT j AS value, i AS value2
+ </create>
+ <drop>
+ DROP PROCEDURE paramHandling
+ </drop>
+ </database-object>
+
+ <database-object>
+ <create>
+ CREATE PROCEDURE simpleScalar (number int)
+ SELECT number AS value, 'getAll' AS name
+ </create>
+ <drop>
+ DROP PROCEDURE simpleScalar
+ </drop>
+ </database-object>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/mysql/Mappings.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/mysql/MySQLCustomSQLTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/mysql/MySQLCustomSQLTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/mysql/MySQLCustomSQLTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,34 @@
+//$Id: MySQLCustomSQLTest.java 11486 2007-05-08 21:57:24Z steve.ebersole at jboss.com $
+package org.hibernate.test.sql.hand.custom.mysql;
+
+import junit.framework.Test;
+
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.MySQLDialect;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.test.sql.hand.custom.CustomStoredProcTestSupport;
+
+/**
+ * Custom SQL tests for MySQL
+ *
+ * @author Gavin King
+ */
+public class MySQLCustomSQLTest extends CustomStoredProcTestSupport {
+
+ public MySQLCustomSQLTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "sql/hand/custom/mysql/Mappings.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( MySQLCustomSQLTest.class );
+ }
+
+ public boolean appliesTo(Dialect dialect) {
+ return ( dialect instanceof MySQLDialect );
+ }
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/mysql/MySQLCustomSQLTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/oracle/Mappings.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/oracle/Mappings.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/oracle/Mappings.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,209 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+ This mapping demonstrates the use of Hibernate with all-handwritten SQL!
+
+ Note: this version is for Oracle
+-->
+<hibernate-mapping package="org.hibernate.test.sql.hand" default-access="field">
+
+ <class name="Organization">
+ <id name="id" unsaved-value="0" column="orgid">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true"/>
+ <set name="employments"
+ inverse="true"
+ order-by="DUMMY">
+ <key column="employer"/>
+ <!-- only needed for DDL generation -->
+ <one-to-many class="Employment"/>
+ <loader query-ref="organizationEmployments"/>
+ </set>
+ <!-- query-list name="currentEmployments"
+ query-ref="organizationCurrentEmployments"-->
+ <loader query-ref="organization"/>
+ <sql-insert>INSERT INTO ORGANIZATION (NAME, ORGID) VALUES ( UPPER(?), ? )</sql-insert>
+ <sql-update>UPDATE ORGANIZATION SET NAME=UPPER(?) WHERE ORGID=?</sql-update>
+ <sql-delete>DELETE FROM ORGANIZATION WHERE ORGID=?</sql-delete>
+ </class>
+
+ <class name="Person">
+ <id name="id" unsaved-value="0" column="perid">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true"/>
+ <loader query-ref="person"/>
+ <sql-insert callable="true" check="none">{call createPerson(?,?)}</sql-insert>
+ <sql-update>UPDATE PERSON SET NAME=UPPER(?) WHERE PERID=?</sql-update>
+ <sql-delete>DELETE FROM PERSON WHERE PERID=?</sql-delete>
+ </class>
+
+ <class name="Employment">
+ <id name="employmentId" unsaved-value="0" column="empid">
+ <generator class="increment"/>
+ </id>
+ <many-to-one name="employee" not-null="true" update="false"/>
+ <many-to-one name="employer" not-null="true" update="false"/>
+ <property name="startDate" not-null="true" update="false" insert="false"/>
+ <property name="endDate" insert="false"/>
+ <property name="regionCode" update="false"/>
+ <property name="salary" type="org.hibernate.test.sql.hand.MonetaryAmountUserType">
+ <column name="VALUE" sql-type="float"/>
+ <column name="CURRENCY"/>
+ </property>
+ <loader query-ref="employment"/>
+ <sql-insert>
+ INSERT INTO EMPLOYMENT
+ (EMPLOYEE, EMPLOYER, STARTDATE, REGIONCODE, VALUE, CURRENCY, EMPID)
+ VALUES (?, ?, CURRENT_DATE, UPPER(?), ?, ?, ?)
+ </sql-insert>
+ <sql-update>UPDATE EMPLOYMENT SET ENDDATE=?, VALUE=?, CURRENCY=? WHERE EMPID=?</sql-update>
+ <sql-delete>DELETE FROM EMPLOYMENT WHERE EMPID=?</sql-delete>
+ </class>
+
+ <resultset name="org-emp-regionCode">
+ <return-scalar column="regionCode" type="string"/>
+ <return alias="org" class="Organization"/>
+ <return-join alias="emp" property="org.employments"/>
+ </resultset>
+
+ <resultset name="org-emp-person">
+ <return alias="org" class="Organization"/>
+ <return-join alias="emp" property="org.employments"/>
+ <return-join alias="pers" property="emp.employee"/>
+ </resultset>
+
+ <sql-query name="person">
+ <return alias="p" class="Person" lock-mode="upgrade"/>
+ SELECT NAME AS {p.name}, PERID AS {p.id} FROM PERSON WHERE PERID=? /*FOR UPDATE*/
+ </sql-query>
+
+ <sql-query name="organization">
+ <return alias="org" class="Organization"/>
+ <return-join alias="emp" property="org.employments"/>
+ SELECT {org.*}, {emp.*}
+ FROM ORGANIZATION org
+ LEFT OUTER JOIN EMPLOYMENT emp ON org.ORGID = emp.EMPLOYER
+ WHERE org.ORGID=?
+ </sql-query>
+
+
+ <sql-query name="allOrganizationsWithEmployees" flush-mode="never">
+ <return alias="org" class="Organization"/>
+ SELECT DISTINCT org.NAME AS {org.name}, org.ORGID AS {org.id}
+ FROM ORGANIZATION org
+ INNER JOIN EMPLOYMENT e ON e.EMPLOYER = org.ORGID
+ </sql-query>
+
+
+ <sql-query name="employment">
+ <return alias="emp" class="Employment"/>
+ SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer},
+ STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},
+ REGIONCODE as {emp.regionCode}, EMPID AS {emp.id}
+ FROM EMPLOYMENT
+ WHERE EMPID = ?
+ </sql-query>
+
+
+ <sql-query name="organizationEmployments">
+ <load-collection alias="empcol" role="Organization.employments"/>
+ SELECT {empcol.*}
+ FROM EMPLOYMENT empcol
+ WHERE EMPLOYER = :id
+ ORDER BY STARTDATE ASC, EMPLOYEE ASC
+ </sql-query>
+
+
+ <sql-query name="organizationCurrentEmployments">
+ <return alias="emp" class="Employment">
+ <return-property name="salary">
+ <!-- as multi column properties are not supported via the
+ {}-syntax, we need to provide an explicit column list for salary via <return-property> -->
+ <return-column name="VALUE"/>
+ <return-column name="CURRENCY"/>
+ </return-property>
+ <!-- Here we are remapping endDate. Notice that we can still use {emp.endDate} in the SQL. -->
+ <return-property name="endDate" column="myEndDate"/>
+ </return>
+ <synchronize table="EMPLOYMENT"/>
+ SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer},
+ STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},
+ REGIONCODE as {emp.regionCode}, EMPID AS {emp.id}, VALUE, CURRENCY
+ FROM EMPLOYMENT
+ WHERE EMPLOYER = :id AND ENDDATE IS NULL
+ ORDER BY STARTDATE ASC
+ </sql-query>
+
+ <database-object>
+ <create>
+ CREATE OR REPLACE FUNCTION testParamHandling (j number, i number)
+ RETURN SYS_REFCURSOR AS st_cursor SYS_REFCURSOR;
+ BEGIN
+ OPEN st_cursor FOR
+ SELECT j as value, i as value2 from dual;
+ RETURN st_cursor;
+ END;
+ </create>
+ <drop>
+ DROP FUNCTION testParamHandling
+ </drop>
+ </database-object>
+
+ <database-object>
+ <create>
+ CREATE OR REPLACE FUNCTION simpleScalar (j number)
+ RETURN SYS_REFCURSOR AS st_cursor SYS_REFCURSOR;
+ BEGIN
+ OPEN st_cursor FOR
+ SELECT j as value, 'getAll' as name from dual;
+ RETURN st_cursor;
+ END;
+ </create>
+ <drop>
+ DROP FUNCTION simpleScalar
+ </drop>
+ </database-object>
+
+ <database-object>
+ <create>
+ CREATE OR REPLACE FUNCTION allEmployments
+ RETURN SYS_REFCURSOR AS st_cursor SYS_REFCURSOR;
+ BEGIN
+ OPEN st_cursor FOR
+ SELECT EMPLOYEE, EMPLOYER, STARTDATE, ENDDATE,
+ REGIONCODE, EMPID, VALUE, CURRENCY
+ FROM EMPLOYMENT;
+ RETURN st_cursor;
+ END;
+ </create>
+ <drop>
+ DROP FUNCTION allEmployments
+ </drop>
+ </database-object>
+
+ <database-object>
+ <create>
+ CREATE OR REPLACE PROCEDURE createPerson(p_name PERSON.NAME%TYPE, p_id PERSON.PERID%TYPE)
+ AS
+ rowcount INTEGER;
+ BEGIN
+ INSERT INTO PERSON ( PERID, NAME ) VALUES ( p_id, UPPER( p_name ) );
+ rowcount := SQL%ROWCOUNT;
+ IF rowcount = 1 THEN
+ NULL;
+ ELSE
+ RAISE_APPLICATION_ERROR( -20001, 'Unexpected rowcount [' || rowcount || ']' );
+ END IF;
+ END;
+ </create>
+ <drop>
+ DROP PROCEDURE createPerson;
+ </drop>
+ </database-object>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/oracle/OracleCustomSQLTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/oracle/OracleCustomSQLTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/oracle/OracleCustomSQLTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,36 @@
+//$Id$
+package org.hibernate.test.sql.hand.custom.oracle;
+
+import junit.framework.Test;
+
+import org.hibernate.dialect.DataDirectOracle9Dialect;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.Oracle9Dialect;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.test.sql.hand.custom.CustomStoredProcTestSupport;
+
+/**
+ * Custom SQL tests for Oracle
+ *
+ * @author Gavin King
+ */
+public class OracleCustomSQLTest extends CustomStoredProcTestSupport {
+
+ public OracleCustomSQLTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "sql/hand/custom/oracle/Mappings.hbm.xml", "sql/hand/custom/oracle/StoredProcedures.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( OracleCustomSQLTest.class );
+ }
+
+ public boolean appliesTo(Dialect dialect) {
+ return ( dialect instanceof Oracle9Dialect ) && !( dialect instanceof DataDirectOracle9Dialect );
+ }
+
+}
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/oracle/StoredProcedures.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/oracle/StoredProcedures.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/oracle/StoredProcedures.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,45 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+ This version is for Oracle drivers proprietary handling of stored procedures/functions.
+-->
+<hibernate-mapping package="org.hibernate.test.sql.hand" default-access="field">
+
+ <sql-query name="simpleScalar" callable="true">
+ <return-scalar column="name" type="string"/>
+ <return-scalar column="value" type="long"/>
+ { ? = call simpleScalar(:number) }
+ </sql-query>
+
+ <sql-query name="paramhandling" callable="true">
+ <return-scalar column="value" type="long"/>
+ <return-scalar column="value2" type="long"/>
+ { ? = call testParamHandling(?,?) }
+ </sql-query>
+
+ <sql-query name="paramhandling_mixed" callable="true">
+ <return-scalar column="value" type="long"/>
+ <return-scalar column="value2" type="long"/>
+ { ? = call testParamHandling(?,:second) }
+ </sql-query>
+
+ <sql-query name="selectAllEmployments" callable="true">
+ <return alias="emp" class="Employment">
+ <return-property name="employee" column="EMPLOYEE"/>
+ <return-property name="employer" column="EMPLOYER"/>
+ <return-property name="startDate" column="STARTDATE"/>
+ <return-property name="endDate" column="ENDDATE"/>
+ <return-property name="regionCode" column="REGIONCODE"/>
+ <return-property name="employmentId" column="EMPID"/>
+ <return-property name="salary">
+ <return-column name="VALUE"/>
+ <return-column name="CURRENCY"/>
+ </return-property>
+ </return>
+ { ? = call allEmployments() }
+ </sql-query>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/sybase/Mappings.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/sybase/Mappings.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/sybase/Mappings.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,217 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+ This mapping demonstrates the use of Hibernate with all-handwritten SQL!
+
+ NOTE: this version is for Sybase/SQLServer
+-->
+<hibernate-mapping package="org.hibernate.test.sql.hand" default-access="field">
+
+ <class name="Organization" table="ORGANIZATION">
+ <id name="id" unsaved-value="0" column="ORGID">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true" column="NAME"/>
+ <set name="employments"
+ inverse="true"
+ order-by="DUMMY">
+ <key column="EMPLOYER"/> <!-- only needed for DDL generation -->
+ <one-to-many class="Employment"/>
+ <loader query-ref="organizationEmployments"/>
+ </set>
+ <!-- query-list name="currentEmployments"
+ query-ref="organizationCurrentEmployments"-->
+ <loader query-ref="organization"/>
+ <sql-insert>INSERT INTO ORGANIZATION (NAME, ORGID) VALUES ( UPPER(?), ? )</sql-insert>
+ <sql-update>UPDATE ORGANIZATION SET NAME=UPPER(?) WHERE ORGID=?</sql-update>
+ <sql-delete>DELETE FROM ORGANIZATION WHERE ORGID=?</sql-delete>
+ </class>
+
+ <class name="Person" table="PERSON">
+ <id name="id" unsaved-value="0" column="PERID">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true" column="NAME"/>
+ <loader query-ref="person"/>
+ <sql-insert>INSERT INTO PERSON (NAME, PERID) VALUES ( UPPER(?), ? )</sql-insert>
+ <sql-update>UPDATE PERSON SET NAME=UPPER(?) WHERE PERID=?</sql-update>
+ <sql-delete>DELETE FROM PERSON WHERE PERID=?</sql-delete>
+ </class>
+
+ <class name="Employment" table="EMPLOYMENT">
+ <id name="employmentId" unsaved-value="0" column="EMPID">
+ <generator class="increment"/>
+ </id>
+ <many-to-one name="employee" column="EMPLOYEE" not-null="true" update="false"/>
+ <many-to-one name="employer" column="EMPLOYER" not-null="true" update="false"/>
+ <property name="startDate" column="STARTDATE" not-null="true" update="false" insert="false"/>
+ <property name="endDate" column="ENDDATE" insert="false"/>
+ <property name="regionCode" column="REGIONCODE" update="false"/>
+ <property name="salary" type="org.hibernate.test.sql.hand.MonetaryAmountUserType">
+ <column name="VALUE" sql-type="float"/>
+ <column name="CURRENCY"/>
+ </property>
+ <loader query-ref="employment"/>
+ <sql-insert>
+ INSERT INTO EMPLOYMENT
+ (EMPLOYEE, EMPLOYER, STARTDATE, REGIONCODE, VALUE, CURRENCY, EMPID)
+ VALUES (?, ?, getdate(), UPPER(?), ?, ?, ?)
+ </sql-insert>
+ <sql-update>UPDATE EMPLOYMENT SET ENDDATE=?, VALUE=?, CURRENCY=? WHERE EMPID=?</sql-update>
+ <sql-delete>DELETE FROM EMPLOYMENT WHERE EMPID=?</sql-delete>
+ </class>
+
+ <resultset name="org-emp-regionCode">
+ <return-scalar column="regionCode" type="string"/>
+ <return alias="org" class="Organization"/>
+ <return-join alias="emp" property="org.employments"/>
+ </resultset>
+
+ <resultset name="org-emp-person">
+ <return alias="org" class="Organization"/>
+ <return-join alias="emp" property="org.employments"/>
+ <return-join alias="pers" property="emp.employee"/>
+ </resultset>
+
+ <sql-query name="person">
+ <return alias="p" class="Person" lock-mode="upgrade"/>
+ SELECT NAME AS {p.name}, PERID AS {p.id} FROM PERSON WHERE PERID=? /*FOR UPDATE*/
+ </sql-query>
+
+ <sql-query name="organization">
+ <return alias="org" class="Organization"/>
+ <return-join alias="emp" property="org.employments"/>
+ SELECT {org.*}, {emp.*}
+ FROM ORGANIZATION org
+ LEFT OUTER JOIN EMPLOYMENT emp ON org.ORGID = emp.EMPLOYER
+ WHERE org.ORGID=?
+ </sql-query>
+
+
+ <!--sql-query name="organization">
+ <return alias="org" class="Organization"/>
+ SELECT NAME AS {org.name}, ORGID AS {org.id} FROM ORGANIZATION
+ WHERE ORGID=?
+ </sql-query-->
+
+ <sql-query name="allOrganizationsWithEmployees" flush-mode="never">
+ <return alias="org" class="Organization"/>
+ SELECT DISTINCT org.NAME AS {org.name}, org.ORGID AS {org.id}
+ FROM ORGANIZATION org
+ INNER JOIN EMPLOYMENT e ON e.EMPLOYER = org.ORGID
+ </sql-query>
+
+
+
+
+
+ <sql-query name="employment">
+ <return alias="emp" class="Employment"/>
+ SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer},
+ STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},
+ REGIONCODE as {emp.regionCode}, EMPID AS {emp.id}
+ FROM EMPLOYMENT
+ WHERE EMPID = ?
+ </sql-query>
+
+ <sql-query name="organizationEmployments">
+ <load-collection alias="empcol" role="Organization.employments"/>
+ SELECT {empcol.*}
+ FROM EMPLOYMENT empcol
+ WHERE EMPLOYER = :id
+ ORDER BY STARTDATE ASC, EMPLOYEE ASC
+ </sql-query>
+
+
+ <sql-query name="organizationCurrentEmployments">
+ <return alias="emp" class="Employment">
+ <return-property name="salary">
+ <!-- as multi column properties are not supported via the
+ {}-syntax, we need to provide an explicit column list for salary via <return-property> -->
+ <return-column name="VALUE"/>
+ <return-column name="CURRENCY"/>
+ </return-property>
+ <!-- Here we are remapping endDate. Notice that we can still use {emp.endDate} in the SQL. -->
+ <return-property name="endDate" column="myEndDate"/>
+ </return>
+ <synchronize table="EMPLOYMENT"/>
+ SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer},
+ STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},
+ REGIONCODE as {emp.regionCode}, EMPID AS {emp.id}, VALUE, CURRENCY
+ FROM EMPLOYMENT
+ WHERE EMPLOYER = :id AND ENDDATE IS NULL
+ ORDER BY STARTDATE ASC
+ </sql-query>
+
+ <sql-query name="simpleScalar" callable="true">
+ <return-scalar column="name" type="string"/>
+ <return-scalar column="value" type="long"/>
+ { call simpleScalar(:number) }
+ </sql-query>
+
+ <sql-query name="paramhandling" callable="true">
+ <return-scalar column="value" type="long"/>
+ <return-scalar column="value2" type="long"/>
+ { call paramHandling(?,?) }
+ </sql-query>
+
+ <sql-query name="paramhandling_mixed" callable="true">
+ <return-scalar column="value" type="long" />
+ <return-scalar column="value2" type="long" />
+ { call paramHandling(?,:second) }
+ </sql-query>
+
+ <sql-query name="selectAllEmployments" callable="true">
+ <return alias="" class="Employment">
+ <return-property name="employee" column="EMPLOYEE"/>
+ <return-property name="employer" column="EMPLOYER"/>
+ <return-property name="startDate" column="STARTDATE"/>
+ <return-property name="endDate" column="ENDDATE"/>
+ <return-property name="regionCode" column="REGIONCODE"/>
+ <return-property name="id" column="EMPID"/>
+ <return-property name="salary">
+ <!-- as multi column properties are not supported via the
+ {}-syntax, we need to provide an explicit column list for salary via <return-property> -->
+ <return-column name="VALUE"/>
+ <return-column name="CURRENCY"/>
+ </return-property>
+ </return>
+ { call selectAllEmployments() }
+ </sql-query>
+
+ <database-object>
+ <create>
+ CREATE PROCEDURE selectAllEmployments AS
+ SELECT EMPLOYEE, EMPLOYER, STARTDATE, ENDDATE,
+ REGIONCODE, EMPID, VALUE, CURRENCY
+ FROM EMPLOYMENT
+ </create>
+ <drop>
+ DROP PROCEDURE selectAllEmployments
+ </drop>
+ </database-object>
+
+ <database-object>
+ <create>
+ CREATE PROCEDURE paramHandling @j int, @i int AS
+ SELECT @j as value, @i as value2
+ </create>
+ <drop>
+ DROP PROCEDURE paramHandling
+ </drop>
+ </database-object>
+
+ <database-object>
+ <create>
+ CREATE PROCEDURE simpleScalar @number int AS
+ SELECT @number as value, 'getAll' as name
+ </create>
+ <drop>
+ DROP PROCEDURE simpleScalar
+ </drop>
+ </database-object>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/sybase/Mappings.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/sybase/SybaseCustomSQLTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/sybase/SybaseCustomSQLTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/custom/sybase/SybaseCustomSQLTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,34 @@
+//$Id$
+package org.hibernate.test.sql.hand.custom.sybase;
+
+import junit.framework.Test;
+
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.SybaseDialect;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.test.sql.hand.custom.CustomStoredProcTestSupport;
+
+/**
+ * Custom SQL tests for Sybase/SQLServer (Transact-SQL)
+ *
+ * @author Gavin King
+ */
+public class SybaseCustomSQLTest extends CustomStoredProcTestSupport {
+
+ public SybaseCustomSQLTest(String str) {
+ super( str );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "sql/hand/custom/sybase/Mappings.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( SybaseCustomSQLTest.class );
+ }
+
+ public boolean appliesTo(Dialect dialect) {
+ return ( dialect instanceof SybaseDialect );
+ }
+}
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/identity/CustomInsertSQLWithIdentityColumnTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/identity/CustomInsertSQLWithIdentityColumnTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/identity/CustomInsertSQLWithIdentityColumnTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,50 @@
+package org.hibernate.test.sql.hand.identity;
+
+import junit.framework.Test;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.junit.functional.DatabaseSpecificFunctionalTestCase;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.Session;
+import org.hibernate.JDBCException;
+import org.hibernate.test.sql.hand.Organization;
+
+/**
+ * Custom SQL tests for combined usage of custom insert SQL and identity columns
+ *
+ * @author Gail Badner
+ */
+public class CustomInsertSQLWithIdentityColumnTest extends DatabaseSpecificFunctionalTestCase {
+
+ public CustomInsertSQLWithIdentityColumnTest(String str) {
+ super( str );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( CustomInsertSQLWithIdentityColumnTest.class );
+ }
+
+ public String[] getMappings() {
+ return new String[] {"sql/hand/identity/Mappings.hbm.xml"};
+ }
+
+ public boolean appliesTo(Dialect dialect) {
+ return dialect.supportsIdentityColumns();
+ }
+
+ public void testBadInsertionFails() {
+ Session session = openSession();
+ session.beginTransaction();
+ Organization org = new Organization( "hola!" );
+ try {
+ session.save( org );
+ session.delete( org );
+ fail( "expecting bad custom insert statement to fail" );
+ }
+ catch( JDBCException e ) {
+ // expected failure
+ }
+
+ session.getTransaction().rollback();
+ session.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/identity/Mappings.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/identity/Mappings.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/identity/Mappings.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+ This mapping demonstrates the combined use of IDENTITY PK columns and
+ custom supplied SQL statements.
+
+ @author : Gail Badner
+-->
+
+<hibernate-mapping package="org.hibernate.test.sql.hand" default-access="field">
+
+ <class name="Organization" table="ORGANIZATION">
+ <id name="id" column="ORG_ID">
+ <generator class="identity"/>
+ </id>
+ <property name="name" not-null="true"/>
+ <!-- Intentionally bad SQL statement!!! -->
+ <sql-insert>INSERT INTO PERSON WHERE x=y</sql-insert>
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/query/NativeSQLQueries.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/query/NativeSQLQueries.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/query/NativeSQLQueries.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,262 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+ This mapping demonstrates the use of Hibernate with
+ all-handwritten SQL!
+-->
+
+<hibernate-mapping package="org.hibernate.test.sql.hand" default-access="field">
+
+ <class name="Organization" table="ORGANIZATION">
+ <id name="id" unsaved-value="0" column="ORGID">
+ <generator class="increment"/>
+ </id>
+ <property name="name" column="NAME" not-null="true"/>
+ <set lazy="true" name="employments"
+ inverse="true">
+ <key column="EMPLOYER"/> <!-- only needed for DDL generation -->
+ <one-to-many class="Employment"/>
+ </set>
+ </class>
+
+ <class name="Person" table="PERSON">
+ <id name="id" unsaved-value="0" column="PERID">
+ <generator class="increment"/>
+ </id>
+ <property name="name" column="NAME" not-null="true"/>
+
+ </class>
+
+ <class name="Employment" table="EMPLOYMENT">
+ <id name="employmentId" unsaved-value="0" column="EMPID">
+ <generator class="increment"/>
+ </id>
+ <many-to-one name="employee" column="EMPLOYEE" not-null="true" update="false"/>
+ <many-to-one name="employer" column="EMPLOYER" not-null="true" update="false"/>
+ <property name="startDate" column="STARTDATE" not-null="false"/>
+ <property name="endDate" column="ENDDATE" insert="false"/>
+ <property name="regionCode" column="REGIONCODE" update="false"/>
+ <property name="salary" type="org.hibernate.test.sql.hand.MonetaryAmountUserType">
+ <column name="VALUE" sql-type="float"/>
+ <column name="CURRENCY"/>
+ </property>
+ </class>
+
+ <class name="Order" table="TBL_ORDER">
+ <composite-id name="orderId" class="Order$OrderId">
+ <key-property name="orgid"/>
+ <key-property name="ordernumber"/>
+ </composite-id>
+
+ <many-to-one name="product">
+ <column name="PROD_ORGID"/>
+ <column name="PROD_NO"/>
+ </many-to-one>
+ <many-to-one name="person"/>
+ </class>
+
+ <class name="Product">
+ <composite-id name="productId" class="Product$ProductId">
+ <key-property name="orgid"/>
+ <key-property name="productnumber"/>
+ </composite-id>
+
+ <property name="name"/>
+
+ <set name="orders" inverse="true">
+ <key>
+ <column name="PROD_ORGID"/>
+ <column name="PROD_NO"/>
+ </key>
+ <one-to-many class="Order"/>
+ </set>
+ </class>
+
+ <class name="Dimension">
+ <id name="id" type="long">
+ <generator class="increment"/>
+ </id>
+ <property name="length" column="d_len"/>
+ <property name="width" column="d_width"/>
+ </class>
+
+ <class name="SpaceShip">
+ <id name="id" type="integer">
+ <generator class="increment"/>
+ </id>
+ <property name="name" column="fld_name"/>
+ <property name="model" column="fld_model"/>
+ <property name="speed" column="fld_speed"/>
+ <component name="dimensions">
+ <property name="length" column="fld_length"/>
+ <property name="width" column="fld_width"/>
+ </component>
+ </class>
+
+ <class name="Speech">
+ <id name="id" type="integer">
+ <generator class="increment"/>
+ </id>
+ <property name="name" column="name"/>
+ <property name="length" column="flength"/>
+ </class>
+
+ <resultset name="org-emp-regionCode">
+ <return-scalar column="regionCode" type="string"/>
+ <return alias="org" class="Organization"/>
+ <return-join alias="emp" property="org.employments"/>
+ </resultset>
+
+ <resultset name="org-emp-person">
+ <return alias="org" class="Organization"/>
+ <return-join alias="emp" property="org.employments"/>
+ <return-join alias="pers" property="emp.employee"/>
+ </resultset>
+
+
+ <resultset name="org-description">
+ <return alias="org" class="Organization"/>
+ <return-join alias="emp" property="org.employments"/>
+ </resultset>
+
+ <resultset name="spaceship-vol">
+ <return alias="sps" class="SpaceShip">
+ <return-property name="id" column="id"/>
+ <return-property name="name" column="name"/>
+ <return-property name="model" column="model"/>
+ <return-property name="speed" column="speed"/>
+ <return-property name="dimensions.length" column="length"/>
+ <return-property name="dimensions.width" column="width"/>
+ </return>
+ <return-scalar column="surface"/>
+ <return-scalar column="volume"/>
+ </resultset>
+
+ <resultset name="speech">
+ <return alias="sp" class="Speech">
+ <return-property name="id" column="id"/>
+ <return-property name="name" column="name"/>
+ <return-property name="length" column="flength"/>
+ </return>
+ <return-scalar column="scalarName"/>
+ </resultset>
+
+ <sql-query name="spaceship" resultset-ref="spaceship-vol">
+ select id as id,
+ fld_name as name,
+ fld_model as model,
+ fld_speed as speed,
+ fld_length as length,
+ fld_width as width,
+ fld_length * fld_width as surface,
+ fld_length * fld_width *10 as volume
+ from SpaceShip
+ </sql-query>
+
+ <sql-query name="orgNamesOnly">
+ <return-scalar column="NAME" type="string"/>
+ SELECT org.NAME FROM ORGANIZATION org
+ </sql-query>
+
+ <sql-query name="orgNamesAndOrgs">
+ <return-scalar column="thename" type="string"/>
+ <return alias="org" class="Organization"/>
+ SELECT org.NAME AS thename, org.NAME AS {org.name}, org.ORGID AS {org.id}
+ FROM ORGANIZATION org
+ ORDER BY thename
+ </sql-query>
+
+ <sql-query name="orgsAndOrgNames">
+ <return alias="org" class="Organization"/>
+ <return-scalar column="thename" type="string"/>
+ SELECT org.NAME AS thename, org.NAME AS {org.name}, org.ORGID AS {org.id}
+ FROM ORGANIZATION org
+ ORDER BY thename
+ </sql-query>
+
+ <sql-query name="orgIdsAndOrgNames">
+ <return-scalar column="orgid" type="long"/>
+ <return-scalar column="thename" type="string"/>
+ SELECT NAME AS thename, ORGID AS orgid
+ FROM ORGANIZATION
+ ORDER BY thename
+ </sql-query>
+
+ <sql-query name="AllEmploymentAsMapped">
+ <return class="Employment"/>
+ SELECT * FROM EMPLOYMENT
+ </sql-query>
+
+ <sql-query name="EmploymentAndPerson">
+ <return class="Employment"/>
+ <return class="Person"/>
+ SELECT * FROM EMPLOYMENT, PERSON
+ </sql-query>
+
+ <sql-query name="organizationEmploymentsExplicitAliases">
+ <load-collection alias="empcol" role="Organization.employments"/>
+ SELECT empcol.EMPLOYER as {empcol.key}, empcol.EMPID as {empcol.element}, {empcol.element.*}
+ FROM EMPLOYMENT empcol
+ WHERE EMPLOYER = :id
+ ORDER BY STARTDATE ASC, EMPLOYEE ASC
+ </sql-query>
+
+ <sql-query name="organizationreturnproperty">
+ <return alias="org" class="Organization">
+ <return-property name="id" column="ORGID"/>
+ <return-property name="name" column="NAME"/>
+ </return>
+ <return-join alias="emp" property="org.employments">
+ <return-property name="key" column="EMPLOYER"/>
+ <return-property name="element" column="EMPID"/>
+ <return-property name="element.employee" column="EMPLOYEE"/>
+ <return-property name="element.employer" column="EMPLOYER"/>
+ <return-property name="element.startDate" column="XSTARTDATE"/>
+ <return-property name="element.endDate" column="ENDDATE"/>
+ <return-property name="element.regionCode" column="REGIONCODE"/>
+ <return-property name="element.employmentId" column="EMPID"/>
+ <return-property name="element.salary">
+ <return-column name="VALUE"/>
+ <return-column name="CURRENCY"/>
+ </return-property>
+ </return-join>
+ SELECT org.ORGID as orgid,
+ org.NAME as name,
+ emp.EMPLOYER as employer,
+ emp.EMPID as empid,
+ emp.EMPLOYEE as employee,
+ emp.EMPLOYER as employer,
+ emp.STARTDATE as xstartDate,
+ emp.ENDDATE as endDate,
+ emp.REGIONCODE as regionCode,
+ emp.VALUE as VALUE,
+ emp.CURRENCY as CURRENCY
+ FROM ORGANIZATION org
+ LEFT OUTER JOIN EMPLOYMENT emp ON org.ORGID = emp.EMPLOYER
+ </sql-query>
+
+
+ <sql-query name="organizationautodetect" resultset-ref="org-description">
+ <!-- equal to "organizationpropertyreturn" but since no {} nor return-property are used hibernate will fallback to use the columns directly from the mapping -->
+ <return alias="org" class="Organization"/>
+ <return-join alias="emp" property="org.employments"/>
+ SELECT org.ORGID as orgid,
+ org.NAME as name,
+ emp.EMPLOYER as employer,
+ emp.EMPID as empid,
+ emp.EMPLOYEE as employee,
+ emp.EMPLOYER as employer,
+ emp.STARTDATE as startDate,
+ emp.ENDDATE as endDate,
+ emp.REGIONCODE as regionCode,
+ emp.VALUE as VALUE,
+ emp.CURRENCY as CURRENCY
+ FROM ORGANIZATION org
+ LEFT OUTER JOIN EMPLOYMENT emp ON org.ORGID = emp.EMPLOYER
+ </sql-query>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/query/NativeSQLQueriesTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/query/NativeSQLQueriesTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/sql/hand/query/NativeSQLQueriesTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,618 @@
+package org.hibernate.test.sql.hand.query;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.Query;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.test.sql.hand.Organization;
+import org.hibernate.test.sql.hand.Person;
+import org.hibernate.test.sql.hand.Employment;
+import org.hibernate.test.sql.hand.Product;
+import org.hibernate.test.sql.hand.Order;
+import org.hibernate.test.sql.hand.Dimension;
+import org.hibernate.test.sql.hand.SpaceShip;
+import org.hibernate.test.sql.hand.Speech;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.transform.DistinctRootEntityResultTransformer;
+import org.hibernate.transform.Transformers;
+import org.hibernate.transform.AliasToEntityMapResultTransformer;
+
+/**
+ * Tests of various features of native SQL queries.
+ *
+ * @author Steve Ebersole
+ */
+public class NativeSQLQueriesTest extends FunctionalTestCase {
+
+ public NativeSQLQueriesTest(String x) {
+ super( x );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "sql/hand/query/NativeSQLQueries.hbm.xml" };
+ }
+
+ public void configure(Configuration cfg) {
+ super.configure( cfg );
+ cfg.setProperty( Environment.GENERATE_STATISTICS, "true" );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( NativeSQLQueriesTest.class );
+ }
+
+ protected String getOrganizationFetchJoinEmploymentSQL() {
+ return "SELECT org.ORGID as {org.id}, " +
+ " org.NAME as {org.name}, " +
+ " emp.EMPLOYER as {emp.key}, " +
+ " emp.EMPID as {emp.element}, " +
+ " {emp.element.*} " +
+ "FROM ORGANIZATION org " +
+ " LEFT OUTER JOIN EMPLOYMENT emp ON org.ORGID = emp.EMPLOYER";
+ }
+
+ protected String getOrganizationJoinEmploymentSQL() {
+ return "SELECT org.ORGID as {org.id}, " +
+ " org.NAME as {org.name}, " +
+ " {emp.*} " +
+ "FROM ORGANIZATION org " +
+ " LEFT OUTER JOIN EMPLOYMENT emp ON org.ORGID = emp.EMPLOYER";
+ }
+
+ protected String getEmploymentSQL() {
+ return "SELECT * FROM EMPLOYMENT";
+ }
+
+ protected String getEmploymentSQLMixedScalarEntity() {
+ return "SELECT e.*, e.employer as employerid FROM EMPLOYMENT e" ;
+ }
+
+ protected String getOrgEmpRegionSQL() {
+ return "select {org.*}, {emp.*}, emp.REGIONCODE " +
+ "from ORGANIZATION org " +
+ " left outer join EMPLOYMENT emp on org.ORGID = emp.EMPLOYER";
+ }
+
+ protected String getOrgEmpPersonSQL() {
+ return "select {org.*}, {emp.*}, {pers.*} " +
+ "from ORGANIZATION org " +
+ " join EMPLOYMENT emp on org.ORGID = emp.EMPLOYER " +
+ " join PERSON pers on pers.PERID = emp.EMPLOYEE ";
+ }
+
+ public void testFailOnNoAddEntityOrScalar() {
+ // Note: this passes, but for the wrong reason.
+ // there is actually an exception thrown, but it is the database
+ // throwing a sql exception because the SQL gets passed
+ // "un-processed"...
+ Session s = openSession();
+ try {
+ String sql = "select {org.*} " +
+ "from organization org";
+ s.createSQLQuery( sql ).list();
+ fail( "Should throw an exception since no addEntity nor addScalar has been performed." );
+ }
+ catch( HibernateException he) {
+ // expected behavior
+ }
+ finally {
+ s.close();
+ }
+ }
+
+ public void testManualSynchronization() {
+ Session s = openSession();
+ s.beginTransaction();
+
+ sfi().getStatistics().clear();
+
+ // create an Organization...
+ Organization jboss = new Organization( "JBoss" );
+ s.persist( jboss );
+
+ // now query on Employment, this should not cause an auto-flush
+ s.createSQLQuery( getEmploymentSQL() ).list();
+ assertEquals( 0, sfi().getStatistics().getEntityInsertCount() );
+
+ // now try to query on Employment but this time add Organization as a synchronized query space...
+ s.createSQLQuery( getEmploymentSQL() ).addSynchronizedEntityClass( Organization.class ).list();
+ assertEquals( 1, sfi().getStatistics().getEntityInsertCount() );
+
+ // clean up
+ s.delete( jboss );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testSQLQueryInterface() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Organization ifa = new Organization("IFA");
+ Organization jboss = new Organization("JBoss");
+ Person gavin = new Person("Gavin");
+ Employment emp = new Employment(gavin, jboss, "AU");
+
+ s.persist(ifa);
+ s.persist(jboss);
+ s.persist(gavin);
+ s.persist(emp);
+
+ List l = s.createSQLQuery( getOrgEmpRegionSQL() )
+ .addEntity("org", Organization.class)
+ .addJoin("emp", "org.employments")
+ .addScalar("regionCode", Hibernate.STRING)
+ .list();
+ assertEquals( 2, l.size() );
+
+ l = s.createSQLQuery( getOrgEmpPersonSQL() )
+ .addEntity("org", Organization.class)
+ .addJoin("emp", "org.employments")
+ .addJoin("pers", "emp.employee")
+ .list();
+ assertEquals( l.size(), 1 );
+
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+
+ l = s.createSQLQuery( "select {org.*}, {emp.*} " +
+ "from ORGANIZATION org " +
+ " left outer join EMPLOYMENT emp on org.ORGID = emp.EMPLOYER, ORGANIZATION org2" )
+ .addEntity("org", Organization.class)
+ .addJoin("emp", "org.employments")
+ .setResultTransformer(new DistinctRootEntityResultTransformer())
+ .list();
+ assertEquals( l.size(), 2 );
+
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+
+ s.delete(emp);
+ s.delete(gavin);
+ s.delete(ifa);
+ s.delete(jboss);
+
+ t.commit();
+ s.close();
+ }
+
+ public void testResultSetMappingDefinition() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Organization ifa = new Organization("IFA");
+ Organization jboss = new Organization("JBoss");
+ Person gavin = new Person("Gavin");
+ Employment emp = new Employment(gavin, jboss, "AU");
+
+ s.persist(ifa);
+ s.persist(jboss);
+ s.persist(gavin);
+ s.persist(emp);
+
+ List l = s.createSQLQuery( getOrgEmpRegionSQL() )
+ .setResultSetMapping( "org-emp-regionCode" )
+ .list();
+ assertEquals( l.size(), 2 );
+
+ l = s.createSQLQuery( getOrgEmpPersonSQL() )
+ .setResultSetMapping( "org-emp-person" )
+ .list();
+ assertEquals( l.size(), 1 );
+
+ s.delete(emp);
+ s.delete(gavin);
+ s.delete(ifa);
+ s.delete(jboss);
+
+ t.commit();
+ s.close();
+ }
+
+ public void testScalarValues() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ Organization ifa = new Organization( "IFA" );
+ Organization jboss = new Organization( "JBoss" );
+
+ Serializable idIfa = s.save( ifa );
+ Serializable idJBoss = s.save( jboss );
+
+ s.flush();
+
+ List result = s.getNamedQuery( "orgNamesOnly" ).list();
+ assertTrue( result.contains( "IFA" ) );
+ assertTrue( result.contains( "JBoss" ) );
+
+ result = s.getNamedQuery( "orgNamesOnly" ).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();
+ Map m = (Map) result.get(0);
+ assertEquals( 2, result.size() );
+ assertEquals( 1, m.size() );
+ assertTrue( m.containsKey("NAME") );
+
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+
+ Iterator iter = s.getNamedQuery( "orgNamesAndOrgs" ).list().iterator();
+ Object[] o = ( Object[] ) iter.next();
+ assertEquals( o[0], "IFA" );
+ assertEquals( ( ( Organization ) o[1] ).getName(), "IFA" );
+ o = ( Object[] ) iter.next();
+ assertEquals( o[0], "JBoss" );
+ assertEquals( ( ( Organization ) o[1] ).getName(), "JBoss" );
+
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+
+ // test that the ordering of the results is truly based on the order in which they were defined
+ iter = s.getNamedQuery( "orgsAndOrgNames" ).list().iterator();
+ Object[] row = ( Object[] ) iter.next();
+ assertEquals( "expecting non-scalar result first", Organization.class, row[0].getClass() );
+ assertEquals( "expecting scalar result second", String.class, row[1].getClass() );
+ assertEquals( ( ( Organization ) row[0] ).getName(), "IFA" );
+ assertEquals( row[1], "IFA" );
+ row = ( Object[] ) iter.next();
+ assertEquals( "expecting non-scalar result first", Organization.class, row[0].getClass() );
+ assertEquals( "expecting scalar result second", String.class, row[1].getClass() );
+ assertEquals( ( ( Organization ) row[0] ).getName(), "JBoss" );
+ assertEquals( row[1], "JBoss" );
+ assertFalse( iter.hasNext() );
+
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+
+ iter = s.getNamedQuery( "orgIdsAndOrgNames" ).list().iterator();
+ o = ( Object[] ) iter.next();
+ assertEquals( o[1], "IFA" );
+ assertEquals( o[0], idIfa );
+ o = ( Object[] ) iter.next();
+ assertEquals( o[1], "JBoss" );
+ assertEquals( o[0], idJBoss );
+
+ s.delete( ifa );
+ s.delete( jboss );
+ t.commit();
+ s.close();
+ }
+
+ public void testMappedAliasStrategy() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Organization ifa = new Organization("IFA");
+ Organization jboss = new Organization("JBoss");
+ Person gavin = new Person("Gavin");
+ Employment emp = new Employment(gavin, jboss, "AU");
+ Serializable orgId = s.save(jboss);
+ Serializable orgId2 = s.save(ifa);
+ s.save(gavin);
+ s.save(emp);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Query namedQuery = s.getNamedQuery("AllEmploymentAsMapped");
+ List list = namedQuery.list();
+ assertEquals(1,list.size());
+ Employment emp2 = (Employment) list.get(0);
+ assertEquals(emp2.getEmploymentId(), emp.getEmploymentId() );
+ assertEquals(emp2.getStartDate().getDate(), emp.getStartDate().getDate() );
+ assertEquals(emp2.getEndDate(), emp.getEndDate() );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Query sqlQuery = s.getNamedQuery("EmploymentAndPerson");
+ sqlQuery.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
+ list = sqlQuery.list();
+ assertEquals(1,list.size() );
+ Object res = list.get(0);
+ assertClassAssignability(res.getClass(),Map.class);
+ Map m = (Map) res;
+ assertEquals(2,m.size());
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ sqlQuery = s.getNamedQuery("organizationreturnproperty");
+ sqlQuery.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
+ list = sqlQuery.list();
+ assertEquals(2,list.size() );
+ m = (Map) list.get(0);
+ assertTrue(m.containsKey("org"));
+ assertClassAssignability(m.get("org").getClass(), Organization.class);
+ assertTrue(m.containsKey("emp"));
+ assertClassAssignability(m.get("emp").getClass(), Employment.class);
+ assertEquals(2, m.size());
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ namedQuery = s.getNamedQuery("EmploymentAndPerson");
+ list = namedQuery.list();
+ assertEquals(1,list.size() );
+ Object[] objs = (Object[]) list.get(0);
+ assertEquals(2, objs.length);
+ emp2 = (Employment) objs[0];
+ gavin = (Person) objs[1];
+ s.delete(emp2);
+ s.delete(jboss);
+ s.delete(gavin);
+ s.delete(ifa);
+ t.commit();
+ s.close();
+ }
+
+ /* test for native sql composite id joins which has never been implemented */
+ public void testCompositeIdJoinsFailureExpected() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Person person = new Person();
+ person.setName( "Noob" );
+
+ Product product = new Product();
+ product.setProductId( new Product.ProductId() );
+ product.getProductId().setOrgid( "x" );
+ product.getProductId().setProductnumber( "1234" );
+ product.setName( "Hibernate 3" );
+
+ Order order = new Order();
+ order.setOrderId( new Order.OrderId() );
+ order.getOrderId().setOrdernumber( "1" );
+ order.getOrderId().setOrgid( "y" );
+
+ product.getOrders().add( order );
+ order.setProduct( product );
+ order.setPerson( person );
+
+ s.save( product );
+ s.save( order);
+ s.save( person );
+
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Product p = (Product) s.createQuery( "from Product p join fetch p.orders" ).list().get(0);
+ assertTrue(Hibernate.isInitialized( p.getOrders()));
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Object[] o = (Object[]) s.createSQLQuery( "select\r\n" +
+ " product.orgid as {product.id.orgid}," +
+ " product.productnumber as {product.id.productnumber}," +
+ " {prod_orders}.orgid as orgid3_1_,\r\n" +
+ " {prod_orders}.ordernumber as ordernum2_3_1_,\r\n" +
+ " product.name as {product.name}," +
+ " {prod_orders.element.*}" +
+ /*" orders.PROD_NO as PROD4_3_1_,\r\n" +
+ " orders.person as person3_1_,\r\n" +
+ " orders.PROD_ORGID as PROD3_0__,\r\n" +
+ " orders.PROD_NO as PROD4_0__,\r\n" +
+ " orders.orgid as orgid0__,\r\n" +
+ " orders.ordernumber as ordernum2_0__ \r\n" +*/
+ " from\r\n" +
+ " Product product \r\n" +
+ " inner join\r\n" +
+ " TBL_ORDER {prod_orders} \r\n" +
+ " on product.orgid={prod_orders}.PROD_ORGID \r\n" +
+ " and product.productnumber={prod_orders}.PROD_NO" )
+ .addEntity( "product", Product.class )
+ .addJoin( "prod_orders", "product.orders" )
+ .list().get(0);
+
+ p = (Product) o[0];
+ assertTrue(Hibernate.isInitialized( p.getOrders() ));
+ assertNotNull(p.getOrders().iterator().next());
+ t.commit();
+ s.close();
+ }
+
+ public void testAutoDetectAliasing() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Organization ifa = new Organization("IFA");
+ Organization jboss = new Organization("JBoss");
+ Person gavin = new Person("Gavin");
+ Employment emp = new Employment(gavin, jboss, "AU");
+ Serializable orgId = s.save(jboss);
+ Serializable orgId2 = s.save(ifa);
+ s.save(gavin);
+ s.save(emp);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ List list = s.createSQLQuery( getEmploymentSQL() )
+ .addEntity( Employment.class.getName() )
+ .list();
+ assertEquals( 1,list.size() );
+
+ Employment emp2 = (Employment) list.get(0);
+ assertEquals(emp2.getEmploymentId(), emp.getEmploymentId() );
+ assertEquals(emp2.getStartDate().getDate(), emp.getStartDate().getDate() );
+ assertEquals(emp2.getEndDate(), emp.getEndDate() );
+
+ s.clear();
+
+ list = s.createSQLQuery( getEmploymentSQL() )
+ .addEntity( Employment.class.getName() )
+ .setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP)
+ .list();
+ assertEquals( 1,list.size() );
+ Map m = (Map) list.get(0);
+ assertTrue(m.containsKey("Employment"));
+ assertEquals(1,m.size());
+
+ list = s.createSQLQuery(getEmploymentSQL()).list();
+ assertEquals(1, list.size());
+ Object[] o = (Object[]) list.get(0);
+ assertEquals(8, o.length);
+
+ list = s.createSQLQuery( getEmploymentSQL() ).setResultTransformer( new UpperCasedAliasToEntityMapResultTransformer() ).list();
+ assertEquals(1, list.size());
+ m = (Map) list.get(0);
+ assertTrue(m.containsKey("EMPID"));
+ assertTrue(m.containsKey("VALUE"));
+ assertTrue(m.containsKey("ENDDATE"));
+ assertEquals(8, m.size());
+
+ list = s.createSQLQuery( getEmploymentSQLMixedScalarEntity() ).addScalar( "employerid" ).addEntity( Employment.class ).list();
+ assertEquals(1, list.size());
+ o = (Object[]) list.get(0);
+ assertEquals(2, o.length);
+ assertClassAssignability( o[0].getClass(), Number.class);
+ assertClassAssignability( o[1].getClass(), Employment.class);
+
+
+
+ Query queryWithCollection = s.getNamedQuery("organizationEmploymentsExplicitAliases");
+ queryWithCollection.setLong("id", jboss.getId() );
+ list = queryWithCollection.list();
+ assertEquals(list.size(),1);
+
+ s.clear();
+
+ list = s.createSQLQuery( getOrganizationJoinEmploymentSQL() )
+ .addEntity( "org", Organization.class )
+ .addJoin( "emp", "org.employments" )
+ .list();
+ assertEquals( 2,list.size() );
+
+ s.clear();
+
+ list = s.createSQLQuery( getOrganizationFetchJoinEmploymentSQL() )
+ .addEntity( "org", Organization.class )
+ .addJoin( "emp", "org.employments" )
+ .list();
+ assertEquals( 2,list.size() );
+
+ s.clear();
+
+ // TODO : why twice?
+ s.getNamedQuery( "organizationreturnproperty" ).list();
+ list = s.getNamedQuery( "organizationreturnproperty" ).list();
+ assertEquals( 2,list.size() );
+
+ s.clear();
+
+ list = s.getNamedQuery( "organizationautodetect" ).list();
+ assertEquals( 2,list.size() );
+
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.delete(emp2);
+
+ s.delete(jboss);
+ s.delete(gavin);
+ s.delete(ifa);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Dimension dim = new Dimension( 3, Integer.MAX_VALUE );
+ s.save( dim );
+ list = s.createSQLQuery( "select d_len * d_width as surface, d_len * d_width * 10 as volume from Dimension" ).list();
+ s.delete( dim );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ SpaceShip enterprise = new SpaceShip();
+ enterprise.setModel( "USS" );
+ enterprise.setName( "Entreprise" );
+ enterprise.setSpeed( 50d );
+ Dimension d = new Dimension(45, 10);
+ enterprise.setDimensions( d );
+ s.save( enterprise );
+ Object[] result = (Object[]) s.getNamedQuery( "spaceship" ).uniqueResult();
+ enterprise = ( SpaceShip ) result[0];
+ assertTrue(50d == enterprise.getSpeed() );
+ assertTrue( 450d == extractDoubleValue( result[1] ) );
+ assertTrue( 4500d == extractDoubleValue( result[2] ) );
+ s.delete( enterprise );
+ t.commit();
+ s.close();
+
+ }
+
+ public void testMixAndMatchEntityScalar() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Speech speech = new Speech();
+ speech.setLength( new Double( 23d ) );
+ speech.setName( "Mine" );
+ s.persist( speech );
+ s.flush();
+ s.clear();
+
+ List l = s.createSQLQuery( "select name, id, flength, name as scalarName from Speech" )
+ .setResultSetMapping( "speech" )
+ .list();
+ assertEquals( l.size(), 1 );
+
+ t.rollback();
+ s.close();
+ }
+
+ private double extractDoubleValue(Object value) {
+ if ( value instanceof BigInteger ) {
+ return ( ( BigInteger ) value ).doubleValue();
+ }
+ else if ( value instanceof BigDecimal ) {
+ return ( ( BigDecimal ) value ).doubleValue();
+ }
+ else {
+ return Double.valueOf( value.toString() ).doubleValue();
+ }
+ }
+
+ private static class UpperCasedAliasToEntityMapResultTransformer extends AliasToEntityMapResultTransformer {
+ public Object transformTuple(Object[] tuple, String[] aliases) {
+ String[] ucAliases = new String[aliases.length];
+ for ( int i = 0; i < aliases.length; i++ ) {
+ ucAliases[i] = aliases[i].toUpperCase();
+ }
+ return super.transformTuple( tuple, ucAliases );
+ }
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stateless/Document.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stateless/Document.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stateless/Document.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -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">
+
+<!--
+
+
+-->
+
+<hibernate-mapping
+ package="org.hibernate.test.stateless">
+
+ <class name="Document">
+ <id name="name"/>
+ <timestamp name="lastModified"/>
+ <property name="text"/>
+ </class>
+
+ <class name="Paper">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="color"/>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stateless/Document.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stateless/Document.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stateless/Document.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stateless/Document.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,46 @@
+//$Id: Document.java 8057 2005-08-31 23:19:53Z oneovthafew $
+package org.hibernate.test.stateless;
+
+import java.util.Date;
+
+/**
+ * @author Gavin King
+ */
+public class Document {
+
+ private String text;
+ private String name;
+ private Date lastModified;
+
+ Document() {}
+
+ public Document(String text, String name) {
+ this.text = text;
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ public Date getLastModified() {
+ return lastModified;
+ }
+
+ void setLastModified(Date lastModified) {
+ this.lastModified = lastModified;
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stateless/Document.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stateless/Paper.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stateless/Paper.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stateless/Paper.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,26 @@
+//$Id: Paper.java 8048 2005-08-30 21:27:17Z epbernard $
+package org.hibernate.test.stateless;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class Paper {
+ private Integer id;
+ private String color;
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getColor() {
+ return color;
+ }
+
+ public void setColor(String color) {
+ this.color = color;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stateless/StatelessSessionTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stateless/StatelessSessionTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stateless/StatelessSessionTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,168 @@
+//$Id: StatelessSessionTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.stateless;
+
+import java.util.Date;
+
+import junit.framework.Test;
+
+import org.hibernate.ScrollMode;
+import org.hibernate.ScrollableResults;
+import org.hibernate.StatelessSession;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class StatelessSessionTest extends FunctionalTestCase {
+
+ public StatelessSessionTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "stateless/Document.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( StatelessSessionTest.class );
+ }
+
+ public void testCreateUpdateReadDelete() {
+ StatelessSession ss = getSessions().openStatelessSession();
+ Transaction tx = ss.beginTransaction();
+ Document doc = new Document("blah blah blah", "Blahs");
+ ss.insert(doc);
+ assertNotNull( doc.getName() );
+ Date initVersion = doc.getLastModified();
+ assertNotNull( initVersion );
+ tx.commit();
+
+ tx = ss.beginTransaction();
+ doc.setText("blah blah blah .... blah");
+ ss.update(doc);
+ assertNotNull( doc.getLastModified() );
+ assertNotSame( doc.getLastModified(), initVersion );
+ tx.commit();
+
+ tx = ss.beginTransaction();
+ doc.setText("blah blah blah .... blah blay");
+ ss.update(doc);
+ tx.commit();
+
+ Document doc2 = (Document) ss.get(Document.class.getName(), "Blahs");
+ assertEquals("Blahs", doc2.getName());
+ assertEquals(doc.getText(), doc2.getText());
+
+ doc2 = (Document) ss.createQuery("from Document where text is not null").uniqueResult();
+ assertEquals("Blahs", doc2.getName());
+ assertEquals(doc.getText(), doc2.getText());
+
+ ScrollableResults sr = ss.createQuery("from Document where text is not null")
+ .scroll(ScrollMode.FORWARD_ONLY);
+ sr.next();
+ doc2 = (Document) sr.get(0);
+ sr.close();
+ assertEquals("Blahs", doc2.getName());
+ assertEquals(doc.getText(), doc2.getText());
+
+ doc2 = (Document) ss.createSQLQuery("select * from Document")
+ .addEntity(Document.class)
+ .uniqueResult();
+ assertEquals("Blahs", doc2.getName());
+ assertEquals(doc.getText(), doc2.getText());
+
+ doc2 = (Document) ss.createCriteria(Document.class).uniqueResult();
+ assertEquals("Blahs", doc2.getName());
+ assertEquals(doc.getText(), doc2.getText());
+
+ sr = ss.createCriteria(Document.class).scroll(ScrollMode.FORWARD_ONLY);
+ sr.next();
+ doc2 = (Document) sr.get(0);
+ sr.close();
+ assertEquals("Blahs", doc2.getName());
+ assertEquals(doc.getText(), doc2.getText());
+
+ tx = ss.beginTransaction();
+ ss.delete(doc);
+ tx.commit();
+ ss.close();
+
+ }
+
+ public void testHqlBulk() {
+ StatelessSession ss = getSessions().openStatelessSession();
+ Transaction tx = ss.beginTransaction();
+ Document doc = new Document("blah blah blah", "Blahs");
+ ss.insert(doc);
+ Paper paper = new Paper();
+ paper.setColor( "White" );
+ ss.insert(paper);
+ tx.commit();
+
+ tx = ss.beginTransaction();
+ int count = ss.createQuery( "update Document set name = :newName where name = :oldName" )
+ .setString( "newName", "Foos" )
+ .setString( "oldName", "Blahs" )
+ .executeUpdate();
+ assertEquals( "hql-update on stateless session", 1, count );
+ count = ss.createQuery( "update Paper set color = :newColor" )
+ .setString( "newColor", "Goldenrod" )
+ .executeUpdate();
+ assertEquals( "hql-update on stateless session", 1, count );
+ tx.commit();
+
+ tx = ss.beginTransaction();
+ count = ss.createQuery( "delete Document" ).executeUpdate();
+ assertEquals( "hql-delete on stateless session", 1, count );
+ count = ss.createQuery( "delete Paper" ).executeUpdate();
+ assertEquals( "hql-delete on stateless session", 1, count );
+ tx.commit();
+ ss.close();
+ }
+
+ public void testInitId() {
+ StatelessSession ss = getSessions().openStatelessSession();
+ Transaction tx = ss.beginTransaction();
+ Paper paper = new Paper();
+ paper.setColor( "White" );
+ ss.insert(paper);
+ assertNotNull( paper.getId() );
+ tx.commit();
+
+ tx = ss.beginTransaction();
+ ss.delete( ss.get( Paper.class, paper.getId() ) );
+ tx.commit();
+ ss.close();
+ }
+
+ public void testRefresh() {
+ StatelessSession ss = getSessions().openStatelessSession();
+ Transaction tx = ss.beginTransaction();
+ Paper paper = new Paper();
+ paper.setColor( "whtie" );
+ ss.insert( paper );
+ tx.commit();
+ ss.close();
+
+ ss = getSessions().openStatelessSession();
+ tx = ss.beginTransaction();
+ Paper p2 = ( Paper ) ss.get( Paper.class, paper.getId() );
+ p2.setColor( "White" );
+ ss.update( p2 );
+ tx.commit();
+ ss.close();
+
+ ss = getSessions().openStatelessSession();
+ tx = ss.beginTransaction();
+ assertEquals( "whtie", paper.getColor() );
+ ss.refresh( paper );
+ assertEquals( "White", paper.getColor() );
+ ss.delete( paper );
+ tx.commit();
+ ss.close();
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stateless/StatelessSessionTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stats/Continent.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stats/Continent.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stats/Continent.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,44 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.stats">
+
+ <import class="Locality"/>
+
+ <class name="Continent">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="name"/>
+ <set name="countries">
+ <key column="country_id"/>
+ <one-to-many class="Country"/>
+ </set>
+ </class>
+
+ <class name="Country">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+ <class name="State">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="name"/>
+ <many-to-one name="country"/>
+ </class>
+
+ <class name="Province">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="name"/>
+ <many-to-one name="country"/>
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stats/Continent.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stats/Continent.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stats/Continent.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,38 @@
+//$Id: Continent.java 6736 2005-05-09 16:09:38Z epbernard $
+package org.hibernate.test.stats;
+
+import java.util.Set;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class Continent {
+ private Integer id;
+ private String name;
+ private Set countries;
+
+ 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 Set getCountries() {
+ return countries;
+ }
+
+ public void setCountries(Set countries) {
+ this.countries = countries;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stats/Continent2.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stats/Continent2.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stats/Continent2.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.stats">
+
+ <class name="Continent" table="tbl_continent">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="name"/>
+ <set name="countries">
+ <key column="country_id"/>
+ <one-to-many class="Country"/>
+ </set>
+ </class>
+
+ <class name="Country" table="tbl_country">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="name"/>
+ </class>
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stats/Country.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stats/Country.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stats/Country.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,41 @@
+//$Id: Country.java 6736 2005-05-09 16:09:38Z epbernard $
+package org.hibernate.test.stats;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class Country {
+ private Integer id;
+ private String name;
+
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof Country)) return false;
+
+ final Country country = (Country) o;
+
+ if (!name.equals(country.name)) return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ return name.hashCode();
+ }
+
+ 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;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stats/Locality.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stats/Locality.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stats/Locality.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,42 @@
+package org.hibernate.test.stats;
+
+/**
+ * @author Steve Ebersole
+ */
+public class Locality {
+ private Long id;
+ private String name;
+ private Country country;
+
+ public Locality() {
+ }
+
+ public Locality(String name, Country country) {
+ this.name = name;
+ this.country = country;
+ }
+
+ 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 Country getCountry() {
+ return country;
+ }
+
+ public void setCountry(Country country) {
+ this.country = country;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stats/Province.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stats/Province.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stats/Province.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,7 @@
+package org.hibernate.test.stats;
+
+/**
+ * @author Steve Ebersole
+ */
+public class Province extends Locality {
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stats/SessionStatsTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stats/SessionStatsTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stats/SessionStatsTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,75 @@
+//$Id: SessionStatsTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.stats;
+
+import java.util.HashSet;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.stat.SessionStatistics;
+import org.hibernate.stat.Statistics;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class SessionStatsTest extends FunctionalTestCase {
+
+ public SessionStatsTest(String x) {
+ super(x);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "stats/Continent2.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( SessionStatsTest.class );
+ }
+
+ public void testSessionStatistics() throws Exception {
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ Statistics stats = getSessions().getStatistics();
+ stats.clear();
+ boolean isStats = stats.isStatisticsEnabled();
+ stats.setStatisticsEnabled(true);
+ Continent europe = fillDb(s);
+ tx.commit();
+ s.clear();
+ tx = s.beginTransaction();
+ SessionStatistics sessionStats = s.getStatistics();
+ assertEquals( 0, sessionStats.getEntityKeys().size() );
+ assertEquals( 0, sessionStats.getEntityCount() );
+ assertEquals( 0, sessionStats.getCollectionKeys().size() );
+ assertEquals( 0, sessionStats.getCollectionCount() );
+ europe = (Continent) s.get( Continent.class, europe.getId() );
+ Hibernate.initialize( europe.getCountries() );
+ Hibernate.initialize( europe.getCountries().iterator().next() );
+ assertEquals( 2, sessionStats.getEntityKeys().size() );
+ assertEquals( 2, sessionStats.getEntityCount() );
+ assertEquals( 1, sessionStats.getCollectionKeys().size() );
+ assertEquals( 1, sessionStats.getCollectionCount() );
+ tx.commit();
+ s.close();
+
+ stats.setStatisticsEnabled( isStats);
+
+ }
+
+ private Continent fillDb(Session s) {
+ Continent europe = new Continent();
+ europe.setName("Europe");
+ Country france = new Country();
+ france.setName("France");
+ europe.setCountries( new HashSet() );
+ europe.getCountries().add(france);
+ s.persist(france);
+ s.persist(europe);
+ return europe;
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stats/State.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stats/State.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stats/State.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,7 @@
+package org.hibernate.test.stats;
+
+/**
+ * @author Steve Ebersole
+ */
+public class State extends Locality {
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stats/StatsTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stats/StatsTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/stats/StatsTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,232 @@
+//$Id: StatsTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.stats;
+
+import java.util.HashSet;
+import java.util.Iterator;
+
+import junit.framework.Test;
+
+import org.hibernate.FetchMode;
+import org.hibernate.Hibernate;
+import org.hibernate.ScrollableResults;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.mapping.Collection;
+import org.hibernate.stat.QueryStatistics;
+import org.hibernate.stat.Statistics;
+
+/**
+ * Show the difference between fetch and load
+ *
+ * @author Emmanuel Bernard
+ */
+public class StatsTest extends FunctionalTestCase {
+
+ public StatsTest(String x) {
+ super(x);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "stats/Continent.hbm.xml" };
+ }
+
+ public void configure(Configuration cfg) {
+ super.configure( cfg );
+ cfg.setProperty( Environment.GENERATE_STATISTICS, "true" );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( StatsTest.class );
+ }
+
+ public void testCollectionFetchVsLoad() throws Exception {
+ Statistics stats = getSessions().getStatistics();
+ stats.clear();
+
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ Continent europe = fillDb(s);
+ tx.commit();
+ s.clear();
+
+ tx = s.beginTransaction();
+ assertEquals(0, stats.getCollectionLoadCount() );
+ assertEquals(0, stats.getCollectionFetchCount() );
+ Continent europe2 = (Continent) s.get( Continent.class, europe.getId() );
+ assertEquals("Lazy true: no collection should be loaded", 0, stats.getCollectionLoadCount() );
+ assertEquals( 0, stats.getCollectionFetchCount() );
+ europe2.getCountries().size();
+ assertEquals( 1, stats.getCollectionLoadCount() );
+ assertEquals("Explicit fetch of the collection state", 1, stats.getCollectionFetchCount() );
+ tx.commit();
+ s.close();
+
+ s = openSession();
+ tx = s.beginTransaction();
+ stats.clear();
+ europe = fillDb(s);
+ tx.commit();
+ s.clear();
+ tx = s.beginTransaction();
+ assertEquals( 0, stats.getCollectionLoadCount() );
+ assertEquals( 0, stats.getCollectionFetchCount() );
+ europe2 = (Continent) s.createQuery(
+ "from " + Continent.class.getName() + " a join fetch a.countries where a.id = " + europe.getId()
+ ).uniqueResult();
+ assertEquals( 1, stats.getCollectionLoadCount() );
+ assertEquals( "collection should be loaded in the same query as its parent", 0, stats.getCollectionFetchCount() );
+ tx.commit();
+ s.close();
+
+ Collection coll = getCfg().getCollectionMapping(Continent.class.getName() + ".countries");
+ coll.setFetchMode(FetchMode.JOIN);
+ coll.setLazy(false);
+ SessionFactory sf = getCfg().buildSessionFactory();
+ stats = sf.getStatistics();
+ stats.clear();
+ stats.setStatisticsEnabled(true);
+ s = sf.openSession();
+ tx = s.beginTransaction();
+ europe = fillDb(s);
+ tx.commit();
+ s.clear();
+ tx = s.beginTransaction();
+ assertEquals( 0, stats.getCollectionLoadCount() );
+ assertEquals( 0, stats.getCollectionFetchCount() );
+ europe2 = (Continent) s.get( Continent.class, europe.getId() );
+ assertEquals( 1, stats.getCollectionLoadCount() );
+ assertEquals( "Should do direct load, not indirect second load when lazy false and JOIN", 0, stats.getCollectionFetchCount() );
+ tx.commit();
+ s.close();
+ sf.close();
+
+ coll = getCfg().getCollectionMapping(Continent.class.getName() + ".countries");
+ coll.setFetchMode(FetchMode.SELECT);
+ coll.setLazy(false);
+ sf = getCfg().buildSessionFactory();
+ stats = sf.getStatistics();
+ stats.clear();
+ stats.setStatisticsEnabled(true);
+ s = sf.openSession();
+ tx = s.beginTransaction();
+ europe = fillDb(s);
+ tx.commit();
+ s.clear();
+ tx = s.beginTransaction();
+ assertEquals( 0, stats.getCollectionLoadCount() );
+ assertEquals( 0, stats.getCollectionFetchCount() );
+ europe2 = (Continent) s.get( Continent.class, europe.getId() );
+ assertEquals( 1, stats.getCollectionLoadCount() );
+ assertEquals( "Should do explicit collection load, not part of the first one", 1, stats.getCollectionFetchCount() );
+ Iterator countries = europe2.getCountries().iterator();
+ while ( countries.hasNext() ) {
+ s.delete( countries.next() );
+ }
+ cleanDb( s );
+ tx.commit();
+ s.close();
+ }
+
+ public void testQueryStatGathering() {
+ Statistics stats = getSessions().getStatistics();
+ stats.clear();
+
+ Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ fillDb(s);
+ tx.commit();
+ s.close();
+
+ s = openSession();
+ tx = s.beginTransaction();
+ final String continents = "from Continent";
+ int results = s.createQuery( continents ).list().size();
+ QueryStatistics continentStats = stats.getQueryStatistics( continents );
+ assertNotNull( "stats were null", continentStats );
+ assertEquals( "unexpected execution count", 1, continentStats.getExecutionCount() );
+ assertEquals( "unexpected row count", results, continentStats.getExecutionRowCount() );
+ long maxTime = continentStats.getExecutionMaxTime();
+ assertEquals( maxTime, stats.getQueryExecutionMaxTime() );
+// assertEquals( continents, stats.getQueryExecutionMaxTimeQueryString() );
+
+ Iterator itr = s.createQuery( continents ).iterate();
+ // iterate() should increment the execution count
+ assertEquals( "unexpected execution count", 2, continentStats.getExecutionCount() );
+ // but should not effect the cumulative row count
+ assertEquals( "unexpected row count", results, continentStats.getExecutionRowCount() );
+ Hibernate.close( itr );
+
+ ScrollableResults scrollableResults = s.createQuery( continents ).scroll();
+ // same deal with scroll()...
+ assertEquals( "unexpected execution count", 3, continentStats.getExecutionCount() );
+ assertEquals( "unexpected row count", results, continentStats.getExecutionRowCount() );
+ scrollableResults.close();
+ tx.commit();
+ s.close();
+
+ // explicitly check that statistics for "split queries" get collected
+ // under the original query
+ stats.clear();
+ s = openSession();
+ tx = s.beginTransaction();
+ final String localities = "from Locality";
+ results = s.createQuery( localities ).list().size();
+ QueryStatistics localityStats = stats.getQueryStatistics( localities );
+ assertNotNull( "stats were null", localityStats );
+ // ...one for each split query
+ assertEquals( "unexpected execution count", 2, localityStats.getExecutionCount() );
+ assertEquals( "unexpected row count", results, localityStats.getExecutionRowCount() );
+ maxTime = localityStats.getExecutionMaxTime();
+ assertEquals( maxTime, stats.getQueryExecutionMaxTime() );
+// assertEquals( localities, stats.getQueryExecutionMaxTimeQueryString() );
+ tx.commit();
+ s.close();
+ assertFalse( s.isOpen() );
+
+ // native sql queries
+ stats.clear();
+ s = openSession();
+ tx = s.beginTransaction();
+ final String sql = "select id, name from Country";
+ results = s.createSQLQuery( sql ).addEntity( Country.class ).list().size();
+ QueryStatistics sqlStats = stats.getQueryStatistics( sql );
+ assertNotNull( "sql stats were null", sqlStats );
+ assertEquals( "unexpected execution count", 1, sqlStats.getExecutionCount() );
+ assertEquals( "unexpected row count", results, sqlStats.getExecutionRowCount() );
+ maxTime = sqlStats.getExecutionMaxTime();
+ assertEquals( maxTime, stats.getQueryExecutionMaxTime() );
+// assertEquals( sql, stats.getQueryExecutionMaxTimeQueryString() );
+ tx.commit();
+ s.close();
+
+ s = openSession();
+ tx = s.beginTransaction();
+ cleanDb( s );
+ tx.commit();
+ s.close();
+ }
+
+ private Continent fillDb(Session s) {
+ Continent europe = new Continent();
+ europe.setName("Europe");
+ Country france = new Country();
+ france.setName("France");
+ europe.setCountries( new HashSet() );
+ europe.getCountries().add(france);
+ s.persist(france);
+ s.persist(europe);
+ return europe;
+ }
+
+ private void cleanDb(Session s) {
+ s.createQuery( "delete Locality" ).executeUpdate();
+ s.createQuery( "delete Country" ).executeUpdate();
+ s.createQuery( "delete Continent" ).executeUpdate();
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subclassfilter/Customer.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subclassfilter/Customer.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subclassfilter/Customer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,26 @@
+// $Id: Customer.java 5899 2005-02-24 20:08:04Z steveebersole $
+package org.hibernate.test.subclassfilter;
+
+/**
+ * Implementation of Customer.
+ *
+ * @author Steve Ebersole
+ */
+public class Customer extends Person {
+ private Employee contactOwner;
+
+ public Customer() {
+ }
+
+ public Customer(String name) {
+ super( name );
+ }
+
+ public Employee getContactOwner() {
+ return contactOwner;
+ }
+
+ public void setContactOwner(Employee contactOwner) {
+ this.contactOwner = contactOwner;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subclassfilter/DiscrimSubclassFilterTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subclassfilter/DiscrimSubclassFilterTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subclassfilter/DiscrimSubclassFilterTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,132 @@
+// $Id: DiscrimSubclassFilterTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.subclassfilter;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.hibernate.Transaction;
+import org.hibernate.classic.Session;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * Implementation of DiscrimSubclassFilterTest.
+ *
+ * @author Steve Ebersole
+ */
+public class DiscrimSubclassFilterTest extends FunctionalTestCase {
+
+ public DiscrimSubclassFilterTest(String name) {
+ super( name );
+ }
+
+ public final String[] getMappings() {
+ return new String[] { "subclassfilter/discrim-subclass.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( DiscrimSubclassFilterTest.class );
+ }
+
+ public void testFiltersWithSubclass() {
+ Session s = openSession();
+ s.enableFilter( "region" ).setParameter( "userRegion", "US" );
+ Transaction t = s.beginTransaction();
+
+ prepareTestData( s );
+ s.clear();
+
+ List results;
+ Iterator itr;
+
+ results = s.createQuery( "from Person" ).list();
+ assertEquals( "Incorrect qry result count", 4, results.size() );
+ s.clear();
+
+ results = s.createQuery( "from Employee" ).list();
+ assertEquals( "Incorrect qry result count", 2, results.size() );
+ s.clear();
+
+ results = new ArrayList( new HashSet( s.createQuery( "from Person as p left join fetch p.minions" ).list() ) );
+ assertEquals( "Incorrect qry result count", 4, results.size() );
+ itr = results.iterator();
+ while ( itr.hasNext() ) {
+ // find john
+ final Person p = ( Person ) itr.next();
+ if ( p.getName().equals( "John Doe" ) ) {
+ Employee john = ( Employee ) p;
+ assertEquals( "Incorrect fecthed minions count", 1, john.getMinions().size() );
+ break;
+ }
+ }
+ s.clear();
+
+ results = new ArrayList( new HashSet( s.createQuery( "from Employee as p left join fetch p.minions" ).list() ) );
+ assertEquals( "Incorrect qry result count", 2, results.size() );
+ itr = results.iterator();
+ while ( itr.hasNext() ) {
+ // find john
+ final Person p = ( Person ) itr.next();
+ if ( p.getName().equals( "John Doe" ) ) {
+ Employee john = ( Employee ) p;
+ assertEquals( "Incorrect fecthed minions count", 1, john.getMinions().size() );
+ break;
+ }
+ }
+
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.createQuery( "delete Customer where contactOwner is not null" ).executeUpdate();
+ s.createQuery( "delete Employee where manager is not null" ).executeUpdate();
+ s.createQuery( "delete Person" ).executeUpdate();
+ t.commit();
+ s.close();
+ }
+
+ private void prepareTestData(Session s) {
+ Employee john = new Employee("John Doe");
+ john.setCompany( "JBoss" );
+ john.setDepartment( "hr" );
+ john.setTitle( "hr guru" );
+ john.setRegion( "US" );
+
+ Employee polli = new Employee("Polli Wog");
+ polli.setCompany( "JBoss" );
+ polli.setDepartment( "hr" );
+ polli.setTitle( "hr novice" );
+ polli.setRegion( "US" );
+ polli.setManager( john );
+ john.getMinions().add( polli );
+
+ Employee suzie = new Employee( "Suzie Q" );
+ suzie.setCompany( "JBoss" );
+ suzie.setDepartment( "hr" );
+ suzie.setTitle( "hr novice" );
+ suzie.setRegion( "EMEA" );
+ suzie.setManager( john );
+ john.getMinions().add( suzie );
+
+ Customer cust = new Customer( "John Q Public" );
+ cust.setCompany( "Acme" );
+ cust.setRegion( "US" );
+ cust.setContactOwner( john );
+
+ Person ups = new Person( "UPS guy" );
+ ups.setCompany( "UPS" );
+ ups.setRegion( "US" );
+
+ s.save( john );
+ s.save( cust );
+ s.save( ups );
+
+ s.flush();
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subclassfilter/Employee.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subclassfilter/Employee.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subclassfilter/Employee.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,56 @@
+// $Id: Employee.java 5899 2005-02-24 20:08:04Z steveebersole $
+package org.hibernate.test.subclassfilter;
+
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * Implementation of Employee.
+ *
+ * @author Steve Ebersole
+ */
+public class Employee extends Person {
+ private String title;
+ private String department;
+ private Employee manager;
+ private Set minions = new HashSet();
+
+ public Employee() {
+ }
+
+ public Employee(String name) {
+ super( name );
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getDepartment() {
+ return department;
+ }
+
+ public void setDepartment(String department) {
+ this.department = department;
+ }
+
+ public Employee getManager() {
+ return manager;
+ }
+
+ public void setManager(Employee manager) {
+ this.manager = manager;
+ }
+
+ public Set getMinions() {
+ return minions;
+ }
+
+ public void setMinions(Set minions) {
+ this.minions = minions;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subclassfilter/JoinedSubclassFilterTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subclassfilter/JoinedSubclassFilterTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subclassfilter/JoinedSubclassFilterTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,143 @@
+// $Id: JoinedSubclassFilterTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.subclassfilter;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.hibernate.Transaction;
+import org.hibernate.classic.Session;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * Implementation of JoinedSubclassFilterTest.
+ *
+ * @author Steve Ebersole
+ */
+public class JoinedSubclassFilterTest extends FunctionalTestCase {
+
+ public JoinedSubclassFilterTest(String name) {
+ super( name );
+ }
+
+ public final String[] getMappings() {
+ return new String[] { "subclassfilter/joined-subclass.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( JoinedSubclassFilterTest.class );
+ }
+
+ public void testFiltersWithJoinedSubclass() {
+ Session s = openSession();
+ s.enableFilter( "region" ).setParameter( "userRegion", "US" );
+ Transaction t = s.beginTransaction();
+
+ prepareTestData( s );
+ s.clear();
+
+ List results = s.createQuery( "from Person" ).list();
+ assertEquals( "Incorrect qry result count", 4, results.size() );
+ s.clear();
+
+ results = s.createQuery( "from Employee" ).list();
+ assertEquals( "Incorrect qry result count", 2, results.size() );
+ Iterator itr = results.iterator();
+ while ( itr.hasNext() ) {
+ // find john
+ final Person p = ( Person ) itr.next();
+ if ( p.getName().equals( "John Doe" ) ) {
+ Employee john = ( Employee ) p;
+ assertEquals( "Incorrect fecthed minions count", 2, john.getMinions().size() );
+ break;
+ }
+ }
+ s.clear();
+
+ // TODO : currently impossible to define a collection-level filter w/ joined-subclass elements that will filter based on a superclass column and function correctly in (theta only?) outer joins;
+ // this is consistent with the behaviour of a collection-level where.
+ // this might be one argument for "pulling" the attached class-level filters into collection assocations,
+ // although we'd need some way to apply the appropriate alias in that scenario.
+ results = new ArrayList( new HashSet( s.createQuery( "from Person as p left join fetch p.minions" ).list() ) );
+ assertEquals( "Incorrect qry result count", 4, results.size() );
+ itr = results.iterator();
+ while ( itr.hasNext() ) {
+ // find john
+ final Person p = ( Person ) itr.next();
+ if ( p.getName().equals( "John Doe" ) ) {
+ Employee john = ( Employee ) p;
+ assertEquals( "Incorrect fecthed minions count", 2, john.getMinions().size() );
+ break;
+ }
+ }
+ s.clear();
+
+ results = new ArrayList( new HashSet( s.createQuery( "from Employee as p left join fetch p.minions" ).list() ) );
+ assertEquals( "Incorrect qry result count", 2, results.size() );
+ itr = results.iterator();
+ while ( itr.hasNext() ) {
+ // find john
+ final Person p = ( Person ) itr.next();
+ if ( p.getName().equals( "John Doe" ) ) {
+ Employee john = ( Employee ) p;
+ assertEquals( "Incorrect fecthed minions count", 2, john.getMinions().size() );
+ break;
+ }
+ }
+
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.createQuery( "delete Customer where contactOwner is not null" ).executeUpdate();
+ s.createQuery( "delete Employee where manager is not null" ).executeUpdate();
+ s.createQuery( "delete Person" ).executeUpdate();
+ t.commit();
+ s.close();
+ }
+
+ private void prepareTestData(Session s) {
+ Employee john = new Employee("John Doe");
+ john.setCompany( "JBoss" );
+ john.setDepartment( "hr" );
+ john.setTitle( "hr guru" );
+ john.setRegion( "US" );
+
+ Employee polli = new Employee("Polli Wog");
+ polli.setCompany( "JBoss" );
+ polli.setDepartment( "hr" );
+ polli.setTitle( "hr novice" );
+ polli.setRegion( "US" );
+ polli.setManager( john );
+ john.getMinions().add( polli );
+
+ Employee suzie = new Employee( "Suzie Q" );
+ suzie.setCompany( "JBoss" );
+ suzie.setDepartment( "hr" );
+ suzie.setTitle( "hr novice" );
+ suzie.setRegion( "EMEA" );
+ suzie.setManager( john );
+ john.getMinions().add( suzie );
+
+ Customer cust = new Customer( "John Q Public" );
+ cust.setCompany( "Acme" );
+ cust.setRegion( "US" );
+ cust.setContactOwner( john );
+
+ Person ups = new Person( "UPS guy" );
+ ups.setCompany( "UPS" );
+ ups.setRegion( "US" );
+
+ s.save( john );
+ s.save( cust );
+ s.save( ups );
+
+ s.flush();
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subclassfilter/Person.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subclassfilter/Person.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subclassfilter/Person.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,53 @@
+// $Id: Person.java 5899 2005-02-24 20:08:04Z steveebersole $
+package org.hibernate.test.subclassfilter;
+
+/**
+ * Implementation of Person.
+ *
+ * @author Steve Ebersole
+ */
+public class Person {
+ private Long id;
+ private String name;
+ private String company;
+ private String region;
+
+ public Person() {
+ }
+
+ public Person(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;
+ }
+
+ public String getCompany() {
+ return company;
+ }
+
+ public void setCompany(String company) {
+ this.company = company;
+ }
+
+ public String getRegion() {
+ return region;
+ }
+
+ public void setRegion(String region) {
+ this.region = region;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subclassfilter/UnionSubclassFilterTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subclassfilter/UnionSubclassFilterTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subclassfilter/UnionSubclassFilterTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,130 @@
+// $Id: UnionSubclassFilterTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.subclassfilter;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.hibernate.Transaction;
+import org.hibernate.classic.Session;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * Implementation of DiscrimSubclassFilterTest.
+ *
+ * @author Steve Ebersole
+ */
+public class UnionSubclassFilterTest extends FunctionalTestCase {
+
+ public UnionSubclassFilterTest(String name) {
+ super( name );
+ }
+
+ public final String[] getMappings() {
+ return new String[] { "subclassfilter/union-subclass.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( UnionSubclassFilterTest.class );
+ }
+
+ public void testFiltersWithUnionSubclass() {
+ Session s = openSession();
+ s.enableFilter( "region" ).setParameter( "userRegion", "US" );
+ Transaction t = s.beginTransaction();
+
+ prepareTestData( s );
+ s.clear();
+
+ List results;
+ Iterator itr;
+
+ results = s.createQuery( "from Person" ).list();
+ assertEquals( "Incorrect qry result count", 4, results.size() );
+ s.clear();
+
+ results = s.createQuery( "from Employee" ).list();
+ assertEquals( "Incorrect qry result count", 2, results.size() );
+ s.clear();
+
+ results = new ArrayList( new HashSet( s.createQuery( "from Person as p left join fetch p.minions" ).list() ) );
+ assertEquals( "Incorrect qry result count", 4, results.size() );
+ itr = results.iterator();
+ while ( itr.hasNext() ) {
+ // find john
+ final Person p = ( Person ) itr.next();
+ if ( p.getName().equals( "John Doe" ) ) {
+ Employee john = ( Employee ) p;
+ assertEquals( "Incorrect fecthed minions count", 1, john.getMinions().size() );
+ break;
+ }
+ }
+ s.clear();
+
+ results = new ArrayList( new HashSet( s.createQuery( "from Employee as p left join fetch p.minions" ).list() ) );
+ assertEquals( "Incorrect qry result count", 2, results.size() );
+ itr = results.iterator();
+ while ( itr.hasNext() ) {
+ // find john
+ final Person p = ( Person ) itr.next();
+ if ( p.getName().equals( "John Doe" ) ) {
+ Employee john = ( Employee ) p;
+ assertEquals( "Incorrect fecthed minions count", 1, john.getMinions().size() );
+ break;
+ }
+ }
+
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.delete( "from Person" );
+ t.commit();
+ s.close();
+
+ }
+
+ private void prepareTestData(Session s) {
+ Employee john = new Employee( "John Doe" );
+ john.setCompany( "JBoss" );
+ john.setDepartment( "hr" );
+ john.setTitle( "hr guru" );
+ john.setRegion( "US" );
+
+ Employee polli = new Employee( "Polli Wog" );
+ polli.setCompany( "JBoss" );
+ polli.setDepartment( "hr" );
+ polli.setTitle( "hr novice" );
+ polli.setRegion( "US" );
+ polli.setManager( john );
+ john.getMinions().add( polli );
+
+ Employee suzie = new Employee( "Suzie Q" );
+ suzie.setCompany( "JBoss" );
+ suzie.setDepartment( "hr" );
+ suzie.setTitle( "hr novice" );
+ suzie.setRegion( "EMEA" );
+ suzie.setManager( john );
+ john.getMinions().add( suzie );
+
+ Customer cust = new Customer( "John Q Public" );
+ cust.setCompany( "Acme" );
+ cust.setRegion( "US" );
+ cust.setContactOwner( john );
+
+ Person ups = new Person( "UPS guy" );
+ ups.setCompany( "UPS" );
+ ups.setRegion( "US" );
+
+ s.save( john );
+ s.save( cust );
+ s.save( ups );
+
+ s.flush();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subclassfilter/discrim-subclass.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subclassfilter/discrim-subclass.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subclassfilter/discrim-subclass.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,44 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.subclassfilter">
+
+ <class name="Person" discriminator-value="0" table="SPerson">
+
+ <id name="id" column="person_id">
+ <generator class="native"/>
+ </id>
+
+ <discriminator type="int"
+ formula="CASE WHEN company is null THEN 0 WHEN company = 'JBoss' THEN 1 ELSE 2 END"/>
+
+ <property name="name"/>
+ <property name="company"/>
+ <property name="region"/>
+
+ <subclass name="Employee" discriminator-value="1">
+ <property name="title"/>
+ <property name="department" column="dept"/>
+ <many-to-one name="manager" column="mgr_id" class="Employee" cascade="none"/>
+ <set name="minions" inverse="true" lazy="true" cascade="all">
+ <key column="mgr_id"/>
+ <one-to-many class="Employee"/>
+ <filter name="region" condition="region = :userRegion"/>
+ </set>
+ </subclass>
+
+ <subclass name="Customer" discriminator-value="2">
+ <many-to-one name="contactOwner" class="Employee"/>
+ </subclass>
+
+ <filter name="region" condition="region = :userRegion"/>
+
+ </class>
+
+ <filter-def name="region">
+ <filter-param name="userRegion" type="string"/>
+ </filter-def>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subclassfilter/joined-subclass.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subclassfilter/joined-subclass.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subclassfilter/joined-subclass.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,43 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.subclassfilter">
+
+ <class name="Person" table="JPerson">
+
+ <id name="id" column="person_id">
+ <generator class="native"/>
+ </id>
+
+ <property name="name"/>
+ <property name="company"/>
+ <property name="region"/>
+
+ <joined-subclass name="Employee" table="JEmployee">
+ <key column="person_id"/>
+
+ <property name="title"/>
+ <property name="department" column="dept"/>
+ <many-to-one name="manager" class="Employee" column="mgr_id" cascade="none"/>
+ <set name="minions" inverse="true" cascade="all" lazy="true">
+ <key column="mgr_id"/>
+ <one-to-many class="Employee"/>
+ </set>
+ </joined-subclass>
+
+ <joined-subclass name="Customer" table="JCustomer">
+ <key column="person_id"/>
+
+ <many-to-one name="contactOwner" class="Employee"/>
+ </joined-subclass>
+
+ <filter name="region" condition="region = :userRegion"/>
+ </class>
+
+ <filter-def name="region">
+ <filter-param name="userRegion" type="string"/>
+ </filter-def>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subclassfilter/union-subclass.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subclassfilter/union-subclass.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subclassfilter/union-subclass.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,41 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.subclassfilter">
+
+ <class name="Person" table="UPerson">
+
+ <id name="id" column="person_id">
+ <generator class="hilo"/>
+ </id>
+
+ <property name="name" unique="true"/>
+ <property name="company"/>
+ <property name="region"/>
+
+ <union-subclass name="Employee" table="UEmployee">
+ <property name="title"/>
+ <property name="department" column="dept"/>
+ <many-to-one name="manager" column="mgr_id" class="Employee" cascade="none"/>
+ <set name="minions" inverse="true" lazy="true" cascade="all">
+ <key column="mgr_id"/>
+ <one-to-many class="Employee"/>
+ <filter name="region" condition="region = :userRegion"/>
+ </set>
+ </union-subclass>
+
+ <union-subclass name="Customer" table="UCustomer">
+ <many-to-one name="contactOwner" class="Employee"/>
+ </union-subclass>
+
+ <filter name="region" condition="region = :userRegion"/>
+
+ </class>
+
+ <filter-def name="region">
+ <filter-param name="userRegion" type="string"/>
+ </filter-def>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselect/Alien.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselect/Alien.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselect/Alien.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,37 @@
+//$Id: Alien.java 7203 2005-06-19 02:01:05Z oneovthafew $
+package org.hibernate.test.subselect;
+
+/**
+ * @author Gavin King
+ */
+public class Alien {
+ private Long id;
+ private String identity;
+ private String planet;
+ private String species;
+
+ public void setIdentity(String identity) {
+ this.identity = identity;
+ }
+ public String getIdentity() {
+ return identity;
+ }
+ public void setSpecies(String species) {
+ this.species = species;
+ }
+ public String getSpecies() {
+ return species;
+ }
+ public void setPlanet(String planet) {
+ this.planet = planet;
+ }
+ public String getPlanet() {
+ return planet;
+ }
+ public void setId(Long id) {
+ this.id = id;
+ }
+ public Long getId() {
+ return id;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselect/Alien.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselect/Being.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselect/Being.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselect/Being.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,31 @@
+//$Id: Being.java 7203 2005-06-19 02:01:05Z oneovthafew $
+package org.hibernate.test.subselect;
+
+/**
+ * @author Gavin King
+ */
+public class Being {
+ private long id;
+ private String identity;
+ private String location;
+ private String species;
+
+ public void setLocation(String location) {
+ this.location = location;
+ }
+ public String getLocation() {
+ return location;
+ }
+ public void setSpecies(String species) {
+ this.species = species;
+ }
+ public String getSpecies() {
+ return species;
+ }
+ public void setIdentity(String identity) {
+ this.identity = identity;
+ }
+ public String getIdentity() {
+ return identity;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselect/Being.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselect/Beings.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselect/Beings.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselect/Beings.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,83 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+ This mapping demonstrates
+
+ (1) use of a class-to-subselect mapping, that allows data
+ defined in other classes to be exposed as a read-only
+ entity (you would do this if you really wanted a view,
+ but didn't or couldn't define one for some reason)
+ This is a "derived entity" mapping.
+
+ (2) use of <synchronize/> to ensure that auto-flush happens
+ correctly, and that queries against the derived entity
+ do not return stale data
+
+-->
+
+<hibernate-mapping
+ package="org.hibernate.test.subselect"
+ default-access="field">
+
+ <class name="Human" table="humans">
+
+ <id name="id" unsaved-value="0"
+ column="bid">
+ <generator class="hilo"/>
+ </id>
+
+ <property name="name"
+ not-null="true"/>
+ <property name="sex"
+ not-null="true"
+ update="false"/>
+ <property name="address"/>
+
+ </class>
+
+ <class name="Alien" table="aliens">
+
+ <id name="id" unsaved-value="0"
+ column="bid">
+ <generator class="hilo"/>
+ </id>
+
+ <property name="identity"
+ not-null="true"
+ column="ident"/>
+ <property name="planet"/>
+ <property name="species"
+ not-null="true"
+ update="false"/>
+
+ </class>
+
+ <class name="Being" mutable="false">
+
+ <subselect>
+ select bid, name as ident, address as loc, 'human' as species
+ from humans
+ union
+ select bid, ident, planet as loc, species
+ from aliens
+ </subselect>
+
+ <synchronize table="humans"/>
+ <synchronize table="aliens"/>
+
+ <id name="id" unsaved-value="0"
+ column="bid">
+ <generator class="native"/>
+ </id>
+
+ <property name="identity" column="ident"/>
+ <property name="location" column="loc"/>
+ <property name="species"/>
+
+ </class>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselect/Beings.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselect/Human.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselect/Human.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselect/Human.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,37 @@
+//$Id: Human.java 7203 2005-06-19 02:01:05Z oneovthafew $
+package org.hibernate.test.subselect;
+
+/**
+ * @author Gavin King
+ */
+public class Human {
+ private Long id;
+ private String name;
+ private char sex;
+ private String address;
+
+ public void setAddress(String address) {
+ this.address = address;
+ }
+ public String getAddress() {
+ return address;
+ }
+ public void setSex(char sex) {
+ this.sex = sex;
+ }
+ public char getSex() {
+ return sex;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setId(Long id) {
+ this.id = id;
+ }
+ public Long getId() {
+ return id;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselect/Human.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselect/SubselectTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselect/SubselectTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselect/SubselectTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,72 @@
+//$Id: SubselectTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.subselect;
+
+import java.util.Iterator;
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class SubselectTest extends FunctionalTestCase {
+
+ public SubselectTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "subselect/Beings.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( SubselectTest.class );
+ }
+
+ public void testEntitySubselect() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Human gavin = new Human();
+ gavin.setName( "gavin" );
+ gavin.setSex( 'M' );
+ gavin.setAddress( "Melbourne, Australia" );
+ Alien x23y4 = new Alien();
+ x23y4.setIdentity( "x23y4$$hu%3" );
+ x23y4.setPlanet( "Mars" );
+ x23y4.setSpecies( "martian" );
+ s.save(gavin);
+ s.save(x23y4);
+ s.flush();
+ List beings = s.createQuery("from Being").list();
+ for ( Iterator iter = beings.iterator(); iter.hasNext(); ) {
+ Being b = (Being) iter.next();
+ assertNotNull( b.getLocation() );
+ assertNotNull( b.getIdentity() );
+ assertNotNull( b.getSpecies() );
+ }
+ s.clear();
+ getSessions().evict(Being.class);
+ Being gav = (Being) s.get(Being.class, gavin.getId());
+ assertEquals( gav.getLocation(), gavin.getAddress() );
+ assertEquals( gav.getSpecies(), "human" );
+ assertEquals( gav.getIdentity(), gavin.getName() );
+ s.clear();
+ //test the <synchronized> tag:
+ gavin = (Human) s.get(Human.class, gavin.getId());
+ gavin.setAddress( "Atlanta, GA" );
+ gav = (Being) s.createQuery("from Being b where b.location like '%GA%'").uniqueResult();
+ assertEquals( gav.getLocation(), gavin.getAddress() );
+ s.delete(gavin);
+ s.delete(x23y4);
+ assertTrue( s.createQuery("from Being").list().isEmpty() );
+ t.commit();
+ s.close();
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselect/SubselectTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselectfetch/Child.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselectfetch/Child.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselectfetch/Child.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,34 @@
+//$Id: Child.java 6095 2005-03-17 05:57:29Z oneovthafew $
+package org.hibernate.test.subselectfetch;
+
+import java.util.List;
+
+/**
+ * @author Gavin King
+ */
+public class Child {
+ private String name;
+ private List friends;
+
+ Child() {}
+ public Child(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+ public List getFriends() {
+ return friends;
+ }
+
+ public void setFriends(List friends) {
+ this.friends = friends;
+ }
+
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselectfetch/Child.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselectfetch/Parent.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselectfetch/Parent.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselectfetch/Parent.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,45 @@
+//$Id: Parent.java 6095 2005-03-17 05:57:29Z oneovthafew $
+package org.hibernate.test.subselectfetch;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Gavin King
+ */
+public class Parent {
+ private String name;
+ private List children = new ArrayList();
+ private List moreChildren = new ArrayList();
+
+ Parent() {}
+ public Parent(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public List getChildren() {
+ return children;
+ }
+
+ public void setChildren(List children) {
+ this.children = children;
+ }
+
+ public List getMoreChildren() {
+ return moreChildren;
+ }
+
+ public void setMoreChildren(List moreChildren) {
+ this.moreChildren = moreChildren;
+ }
+
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselectfetch/Parent.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselectfetch/ParentChild.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselectfetch/ParentChild.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselectfetch/ParentChild.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping
+ package="org.hibernate.test.subselectfetch">
+
+ <class name="Child">
+ <id name="name"/>
+ <bag name="friends" fetch="subselect" table="ChildChild">
+ <key column="childName1" not-null="true"/>
+ <many-to-many class="Child" column="childName2"/>
+ </bag>
+ </class>
+
+ <class name="Parent">
+ <id name="name"/>
+ <list name="children" fetch="subselect" cascade="persist, delete">
+ <key column="parentName" not-null="true"/>
+ <list-index column="loc"/>
+ <one-to-many class="Child"/>
+ </list>
+ <list name="moreChildren" table="ParentChild" fetch="subselect">
+ <key column="parentName" not-null="true"/>
+ <list-index column="loc"/>
+ <many-to-many class="Child" column="childName"/>
+ </list>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselectfetch/ParentChild.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselectfetch/SubselectFetchTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselectfetch/SubselectFetchTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselectfetch/SubselectFetchTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,384 @@
+//$Id: SubselectFetchTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.subselectfetch;
+
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.hibernate.FetchMode;
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.criterion.Order;
+import org.hibernate.criterion.Property;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class SubselectFetchTest extends FunctionalTestCase {
+
+ public SubselectFetchTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "subselectfetch/ParentChild.hbm.xml" };
+ }
+
+ public void configure(Configuration cfg) {
+ cfg.setProperty(Environment.GENERATE_STATISTICS, "true");
+ }
+
+ public String getCacheConcurrencyStrategy() {
+ return null;
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( SubselectFetchTest.class );
+ }
+
+ public void testSubselectFetchHql() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Parent p = new Parent("foo");
+ p.getChildren().add( new Child("foo1") );
+ p.getChildren().add( new Child("foo2") );
+ Parent q = new Parent("bar");
+ q.getChildren().add( new Child("bar1") );
+ q.getChildren().add( new Child("bar2") );
+ q.getMoreChildren().addAll( p.getChildren() );
+ s.persist(p);
+ s.persist(q);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+
+ getSessions().getStatistics().clear();
+
+ List parents = s.createQuery("from Parent where name between 'bar' and 'foo' order by name desc")
+ .list();
+ p = (Parent) parents.get(0);
+ q = (Parent) parents.get(1);
+
+ assertFalse( Hibernate.isInitialized( p.getChildren() ) );
+ assertFalse( Hibernate.isInitialized( q.getChildren() ) );
+
+ assertEquals( p.getChildren().size(), 2 );
+
+ assertTrue( Hibernate.isInitialized( p.getChildren().iterator().next() ) );
+
+ assertTrue( Hibernate.isInitialized( q.getChildren() ) );
+
+ assertEquals( q.getChildren().size(), 2 );
+
+ assertTrue( Hibernate.isInitialized( q.getChildren().iterator().next() ) );
+
+ assertFalse( Hibernate.isInitialized( p.getMoreChildren() ) );
+ assertFalse( Hibernate.isInitialized( q.getMoreChildren() ) );
+
+ assertEquals( p.getMoreChildren().size(), 0 );
+
+ assertTrue( Hibernate.isInitialized( q.getMoreChildren() ) );
+
+ assertEquals( q.getMoreChildren().size(), 2 );
+
+ assertTrue( Hibernate.isInitialized( q.getMoreChildren().iterator().next() ) );
+
+ assertEquals( 3, getSessions().getStatistics().getPrepareStatementCount() );
+
+ Child c = (Child) p.getChildren().get(0);
+ c.getFriends().size();
+
+ s.delete(p);
+ s.delete(q);
+
+ t.commit();
+ s.close();
+ }
+
+ public void testSubselectFetchNamedParam() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Parent p = new Parent("foo");
+ p.getChildren().add( new Child("foo1") );
+ p.getChildren().add( new Child("foo2") );
+ Parent q = new Parent("bar");
+ q.getChildren().add( new Child("bar1") );
+ q.getChildren().add( new Child("bar2") );
+ q.getMoreChildren().addAll( p.getChildren() );
+ s.persist(p);
+ s.persist(q);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+
+ getSessions().getStatistics().clear();
+
+ List parents = s.createQuery("from Parent where name between :bar and :foo order by name desc")
+ .setParameter("bar", "bar")
+ .setParameter("foo", "foo")
+ .list();
+ p = (Parent) parents.get(0);
+ q = (Parent) parents.get(1);
+
+ assertFalse( Hibernate.isInitialized( p.getChildren() ) );
+ assertFalse( Hibernate.isInitialized( q.getChildren() ) );
+
+ assertEquals( p.getChildren().size(), 2 );
+
+ assertTrue( Hibernate.isInitialized( p.getChildren().iterator().next() ) );
+
+ assertTrue( Hibernate.isInitialized( q.getChildren() ) );
+
+ assertEquals( q.getChildren().size(), 2 );
+
+ assertTrue( Hibernate.isInitialized( q.getChildren().iterator().next() ) );
+
+ assertFalse( Hibernate.isInitialized( p.getMoreChildren() ) );
+ assertFalse( Hibernate.isInitialized( q.getMoreChildren() ) );
+
+ assertEquals( p.getMoreChildren().size(), 0 );
+
+ assertTrue( Hibernate.isInitialized( q.getMoreChildren() ) );
+
+ assertEquals( q.getMoreChildren().size(), 2 );
+
+ assertTrue( Hibernate.isInitialized( q.getMoreChildren().iterator().next() ) );
+
+ assertEquals( 3, getSessions().getStatistics().getPrepareStatementCount() );
+
+ Child c = (Child) p.getChildren().get(0);
+ c.getFriends().size();
+
+ s.delete(p);
+ s.delete(q);
+
+ t.commit();
+ s.close();
+ }
+
+ public void testSubselectFetchPosParam() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Parent p = new Parent("foo");
+ p.getChildren().add( new Child("foo1") );
+ p.getChildren().add( new Child("foo2") );
+ Parent q = new Parent("bar");
+ q.getChildren().add( new Child("bar1") );
+ q.getChildren().add( new Child("bar2") );
+ q.getMoreChildren().addAll( p.getChildren() );
+ s.persist(p);
+ s.persist(q);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+
+ getSessions().getStatistics().clear();
+
+ List parents = s.createQuery("from Parent where name between ? and ? order by name desc")
+ .setParameter(0, "bar")
+ .setParameter(1, "foo")
+ .list();
+ p = (Parent) parents.get(0);
+ q = (Parent) parents.get(1);
+
+ assertFalse( Hibernate.isInitialized( p.getChildren() ) );
+ assertFalse( Hibernate.isInitialized( q.getChildren() ) );
+
+ assertEquals( p.getChildren().size(), 2 );
+
+ assertTrue( Hibernate.isInitialized( p.getChildren().iterator().next() ) );
+
+ assertTrue( Hibernate.isInitialized( q.getChildren() ) );
+
+ assertEquals( q.getChildren().size(), 2 );
+
+ assertTrue( Hibernate.isInitialized( q.getChildren().iterator().next() ) );
+
+ assertFalse( Hibernate.isInitialized( p.getMoreChildren() ) );
+ assertFalse( Hibernate.isInitialized( q.getMoreChildren() ) );
+
+ assertEquals( p.getMoreChildren().size(), 0 );
+
+ assertTrue( Hibernate.isInitialized( q.getMoreChildren() ) );
+
+ assertEquals( q.getMoreChildren().size(), 2 );
+
+ assertTrue( Hibernate.isInitialized( q.getMoreChildren().iterator().next() ) );
+
+ assertEquals( 3, getSessions().getStatistics().getPrepareStatementCount() );
+
+ Child c = (Child) p.getChildren().get(0);
+ c.getFriends().size();
+
+ s.delete(p);
+ s.delete(q);
+
+ t.commit();
+ s.close();
+ }
+
+ public void testSubselectFetchWithLimit() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Parent p = new Parent("foo");
+ p.getChildren().add( new Child("foo1") );
+ p.getChildren().add( new Child("foo2") );
+ Parent q = new Parent("bar");
+ q.getChildren().add( new Child("bar1") );
+ q.getChildren().add( new Child("bar2") );
+ Parent r = new Parent("aaa");
+ r.getChildren().add( new Child("aaa1") );
+ s.persist(p);
+ s.persist(q);
+ s.persist(r);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+
+ getSessions().getStatistics().clear();
+
+ List parents = s.createQuery("from Parent order by name desc")
+ .setMaxResults(2)
+ .list();
+ p = (Parent) parents.get(0);
+ q = (Parent) parents.get(1);
+ assertFalse( Hibernate.isInitialized( p.getChildren() ) );
+ assertFalse( Hibernate.isInitialized( p.getMoreChildren() ) );
+ assertFalse( Hibernate.isInitialized( q.getChildren() ) );
+ assertFalse( Hibernate.isInitialized( q.getMoreChildren() ) );
+ assertEquals( p.getMoreChildren().size(), 0 );
+ assertEquals( p.getChildren().size(), 2 );
+ assertTrue( Hibernate.isInitialized( q.getChildren() ) );
+ assertTrue( Hibernate.isInitialized( q.getMoreChildren() ) );
+
+ assertEquals( 3, getSessions().getStatistics().getPrepareStatementCount() );
+
+ r = (Parent) s.get( Parent.class, r.getName() );
+ assertTrue( Hibernate.isInitialized( r.getChildren() ) );
+ assertFalse( Hibernate.isInitialized( r.getMoreChildren() ) );
+ assertEquals( r.getChildren().size(), 1 );
+ assertEquals( r.getMoreChildren().size(), 0 );
+
+ s.delete(p);
+ s.delete(q);
+ s.delete(r);
+
+ t.commit();
+ s.close();
+ }
+
+ public void testManyToManyCriteriaJoin() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Parent p = new Parent("foo");
+ p.getChildren().add( new Child("foo1") );
+ p.getChildren().add( new Child("foo2") );
+ Parent q = new Parent("bar");
+ q.getChildren().add( new Child("bar1") );
+ q.getChildren().add( new Child("bar2") );
+ q.getMoreChildren().addAll( p.getChildren() );
+ s.persist(p);
+ s.persist(q);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+
+ List parents = s.createCriteria(Parent.class)
+ .createCriteria("moreChildren")
+ .createCriteria("friends")
+ .addOrder( Order.desc("name") )
+ .list();
+
+ parents = s.createCriteria(Parent.class)
+ .setFetchMode("moreChildren", FetchMode.JOIN)
+ .setFetchMode("moreChildren.friends", FetchMode.JOIN)
+ .addOrder( Order.desc("name") )
+ .list();
+
+ s.delete( parents.get(0) );
+ s.delete( parents.get(1) );
+
+ t.commit();
+ s.close();
+ }
+
+ public void testSubselectFetchCriteria() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Parent p = new Parent("foo");
+ p.getChildren().add( new Child("foo1") );
+ p.getChildren().add( new Child("foo2") );
+ Parent q = new Parent("bar");
+ q.getChildren().add( new Child("bar1") );
+ q.getChildren().add( new Child("bar2") );
+ q.getMoreChildren().addAll( p.getChildren() );
+ s.persist(p);
+ s.persist(q);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+
+ getSessions().getStatistics().clear();
+
+ List parents = s.createCriteria(Parent.class)
+ .add( Property.forName("name").between("bar", "foo") )
+ .addOrder( Order.desc("name") )
+ .list();
+ p = (Parent) parents.get(0);
+ q = (Parent) parents.get(1);
+
+ assertFalse( Hibernate.isInitialized( p.getChildren() ) );
+ assertFalse( Hibernate.isInitialized( q.getChildren() ) );
+
+ assertEquals( p.getChildren().size(), 2 );
+
+ assertTrue( Hibernate.isInitialized( p.getChildren().iterator().next() ) );
+
+ assertTrue( Hibernate.isInitialized( q.getChildren() ) );
+
+ assertEquals( q.getChildren().size(), 2 );
+
+ assertTrue( Hibernate.isInitialized( q.getChildren().iterator().next() ) );
+
+ assertFalse( Hibernate.isInitialized( p.getMoreChildren() ) );
+ assertFalse( Hibernate.isInitialized( q.getMoreChildren() ) );
+
+ assertEquals( p.getMoreChildren().size(), 0 );
+
+ assertTrue( Hibernate.isInitialized( q.getMoreChildren() ) );
+
+ assertEquals( q.getMoreChildren().size(), 2 );
+
+ assertTrue( Hibernate.isInitialized( q.getMoreChildren().iterator().next() ) );
+
+ assertEquals( 3, getSessions().getStatistics().getPrepareStatementCount() );
+
+ Child c = (Child) p.getChildren().get(0);
+ c.getFriends().size();
+
+ s.delete(p);
+ s.delete(q);
+
+ t.commit();
+ s.close();
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/subselectfetch/SubselectFetchTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ternary/Employee.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ternary/Employee.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ternary/Employee.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,47 @@
+//$Id: Employee.java 5686 2005-02-12 07:27:32Z steveebersole $
+package org.hibernate.test.ternary;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author Gavin King
+ */
+public class Employee {
+ private String name;
+ private Date hireDate;
+ private Map managerBySite = new HashMap();
+ private Set underlings = new HashSet();
+
+ Employee() {}
+ public Employee(String name) {
+ this.name=name;
+ }
+ public Map getManagerBySite() {
+ return managerBySite;
+ }
+ public void setManagerBySite(Map managerBySite) {
+ this.managerBySite = managerBySite;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public Set getUnderlings() {
+ return underlings;
+ }
+ public void setUnderlings(Set underlings) {
+ this.underlings = underlings;
+ }
+ public Date getHireDate() {
+ return hireDate;
+ }
+ public void setHireDate(Date hireDate) {
+ this.hireDate = hireDate;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ternary/Employee.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ternary/Site.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ternary/Site.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ternary/Site.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,44 @@
+//$Id: Site.java 5686 2005-02-12 07:27:32Z steveebersole $
+package org.hibernate.test.ternary;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author Gavin King
+ */
+public class Site {
+ private String name;
+ private String description;
+ private Set employees = new HashSet();
+ private Set managers = new HashSet();
+
+ Site() {}
+ public Site(String name) {
+ this.name=name;
+ }
+ public Set getManagers() {
+ return managers;
+ }
+ public void setManagers(Set managers) {
+ this.managers = managers;
+ }
+ public String getDescription() {
+ return description;
+ }
+ public void setDescription(String description) {
+ this.description = description;
+ }
+ public Set getEmployees() {
+ return employees;
+ }
+ public void setEmployees(Set employees) {
+ this.employees = employees;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ternary/Site.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ternary/Ternary.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ternary/Ternary.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ternary/Ternary.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,36 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping
+ package="org.hibernate.test.ternary">
+
+ <class name="Employee">
+ <id name="name"/>
+ <property name="hireDate" type="imm_date"/>
+ <map name="managerBySite" table="employeeSite">
+ <key column="employeeName" not-null="true"/>
+ <map-key-many-to-many column="siteName" class="Site"/>
+ <many-to-many column="managerName" class="Employee"/>
+ </map>
+ <set name="underlings" table="employeeSite" inverse="true">
+ <key column="managerName" not-null="true"/>
+ <many-to-many column="employeeName" class="Employee"/>
+ </set>
+ </class>
+
+ <class name="Site">
+ <id name="name"/>
+ <property name="description"/>
+ <set name="employees" table="employeeSite" inverse="true">
+ <key column="siteName"/>
+ <many-to-many column="employeeName" class="Employee"/>
+ </set>
+ <set name="managers" table="employeeSite" inverse="true">
+ <key column="siteName"/>
+ <many-to-many column="managerName" class="Employee"/>
+ </set>
+ </class>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ternary/Ternary.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ternary/TernaryTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ternary/TernaryTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ternary/TernaryTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,116 @@
+//$Id: TernaryTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.ternary;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class TernaryTest extends FunctionalTestCase {
+
+ public TernaryTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "ternary/Ternary.hbm.xml" };
+ }
+
+ public String getCacheConcurrencyStrategy() {
+ return null;
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( TernaryTest.class );
+ }
+
+ public void testTernary() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Employee bob = new Employee("Bob");
+ Employee tom = new Employee("Tom");
+ Employee jim = new Employee("Jim");
+ Employee tim = new Employee("Tim");
+ Site melb = new Site("Melbourne");
+ Site geel = new Site("Geelong");
+ s.persist(bob);
+ s.persist(tom);
+ s.persist(jim);
+ s.persist(tim);
+ s.persist(melb);
+ s.persist(geel);
+ bob.getManagerBySite().put(melb, tom);
+ bob.getManagerBySite().put(geel, jim);
+ tim.getManagerBySite().put(melb, tom);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ tom = (Employee) s.get(Employee.class, "Tom");
+ assertFalse( Hibernate.isInitialized(tom.getUnderlings()) );
+ assertEquals( tom.getUnderlings().size(), 2 );
+ bob = (Employee) s.get(Employee.class, "Bob");
+ assertFalse( Hibernate.isInitialized(bob.getManagerBySite()) );
+ assertTrue( tom.getUnderlings().contains(bob) );
+ melb = (Site) s.get(Site.class, "Melbourne");
+ assertSame( bob.getManagerBySite().get(melb), tom );
+ assertTrue( melb.getEmployees().contains(bob) );
+ assertTrue( melb.getManagers().contains(tom) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ List l = s.createQuery("from Employee e join e.managerBySite m where m.name='Bob'").list();
+ assertEquals( l.size(), 0 );
+ l = s.createQuery("from Employee e join e.managerBySite m where m.name='Tom'").list();
+ assertEquals( l.size(), 2 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ l = s.createQuery("from Employee e left join fetch e.managerBySite").list();
+ assertEquals( l.size(), 5 );
+ Set set = new HashSet(l);
+ assertEquals( set.size(), 4 );
+ Iterator iter = set.iterator();
+ int total=0;
+ while ( iter.hasNext() ) {
+ Map map = ( (Employee) iter.next() ).getManagerBySite();
+ assertTrue( Hibernate.isInitialized(map) );
+ total += map.size();
+ }
+ assertTrue(total==3);
+
+ l = s.createQuery("from Employee e left join e.managerBySite m left join m.managerBySite m2").list();
+
+ // clean up...
+ l = s.createQuery("from Employee e left join fetch e.managerBySite").list();
+ Iterator itr = l.iterator();
+ while ( itr.hasNext() ) {
+ Employee emp = ( Employee ) itr.next();
+ emp.setManagerBySite( new HashMap() );
+ s.delete( emp );
+ }
+ ((org.hibernate.classic.Session)s).delete("from Site");
+ t.commit();
+ s.close();
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/ternary/TernaryTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/timestamp/Person.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/timestamp/Person.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/timestamp/Person.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,37 @@
+//$Id: Person.java 8054 2005-08-31 20:12:24Z oneovthafew $
+package org.hibernate.test.timestamp;
+
+import java.util.Date;
+
+/**
+ * @author Gavin King
+ */
+public class Person {
+ private String name;
+ private Date dob;
+ private String currentAddress;
+ Person() {}
+ public Person(String name, Date dob, String address) {
+ this.name = name;
+ this.dob = dob;
+ this.currentAddress = address;
+ }
+ public Date getDob() {
+ return dob;
+ }
+ public void setDob(Date dob) {
+ this.dob = dob;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public String getCurrentAddress() {
+ return currentAddress;
+ }
+ public void setCurrentAddress(String currentAddress) {
+ this.currentAddress = currentAddress;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/timestamp/Person.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/timestamp/TimestampTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/timestamp/TimestampTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/timestamp/TimestampTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,91 @@
+//$Id: TimestampTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.timestamp;
+
+import java.util.Date;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class TimestampTest extends FunctionalTestCase {
+
+ public TimestampTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "timestamp/User.hbm.xml" };
+ }
+
+ public void configure(Configuration cfg) {
+ cfg.setProperty(Environment.GENERATE_STATISTICS, "true");
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( TimestampTest.class );
+ }
+
+ public void testUpdateFalse() {
+
+ getSessions().getStatistics().clear();
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ User u = new User( "gavin", "secret", new Person("Gavin King", new Date(), "Karbarook Ave") );
+ s.persist(u);
+ s.flush();
+ u.getPerson().setName("XXXXYYYYY");
+ t.commit();
+ s.close();
+
+ assertEquals( 1, getSessions().getStatistics().getEntityInsertCount() );
+ assertEquals( 0, getSessions().getStatistics().getEntityUpdateCount() );
+
+ s = openSession();
+ t = s.beginTransaction();
+ u = (User) s.get(User.class, "gavin");
+ assertEquals( u.getPerson().getName(), "Gavin King" );
+ s.delete(u);
+ t.commit();
+ s.close();
+
+ assertEquals( 1, getSessions().getStatistics().getEntityDeleteCount() );
+ }
+
+ public void testComponent() {
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ User u = new User( "gavin", "secret", new Person("Gavin King", new Date(), "Karbarook Ave") );
+ s.persist(u);
+ s.flush();
+ u.getPerson().setCurrentAddress("Peachtree Rd");
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ u = (User) s.get(User.class, "gavin");
+ u.setPassword("$ecret");
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ u = (User) s.get(User.class, "gavin");
+ assertEquals( u.getPassword(), "$ecret" );
+ s.delete(u);
+ t.commit();
+ s.close();
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/timestamp/TimestampTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/timestamp/User.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/timestamp/User.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/timestamp/User.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -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">
+
+<!--
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.timestamp">
+
+ <class name="User" table="T_USER" dynamic-update="true">
+ <id name="userName"/>
+ <timestamp name="lastModified"/>
+ <property name="password" not-null="true" optimistic-lock="false"/>
+ <component name="person">
+ <property name="name" update="false" not-null="true"/>
+ <property name="dob" update="false" not-null="true"/>
+ <property name="currentAddress"
+ column="address"
+ insert="false"
+ update="false"/>
+ </component>
+ </class>
+
+ <query name="userNameIn"><![CDATA[from User where person.name in (:nameList) or userName in (:nameList)]]></query>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/timestamp/User.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/timestamp/User.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/timestamp/User.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/timestamp/User.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,44 @@
+//$Id: User.java 8054 2005-08-31 20:12:24Z oneovthafew $
+package org.hibernate.test.timestamp;
+
+import java.util.Date;
+
+/**
+ * @author Gavin King
+ */
+public class User {
+ private String userName;
+ private String password;
+ private Person person;
+ private Date lastModified;
+ User() {}
+ public User(String id, String pw, Person person) {
+ this.userName = id;
+ this.password = pw;
+ this.person = person;
+ }
+ public Date getLastModified() {
+ return lastModified;
+ }
+ public void setLastModified(Date lastModified) {
+ this.lastModified = lastModified;
+ }
+ public String getPassword() {
+ return password;
+ }
+ public void setPassword(String password) {
+ this.password = password;
+ }
+ public Person getPerson() {
+ return person;
+ }
+ public void setPerson(Person person) {
+ this.person = person;
+ }
+ public String getUserName() {
+ return userName;
+ }
+ public void setUserName(String userName) {
+ this.userName = userName;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/timestamp/User.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tm/CMTTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tm/CMTTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tm/CMTTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,513 @@
+//$Id: CMTTest.java 11303 2007-03-19 22:06:14Z steve.ebersole at jboss.com $
+package org.hibernate.test.tm;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import javax.transaction.Transaction;
+
+import junit.framework.Test;
+
+import org.hibernate.ConnectionReleaseMode;
+import org.hibernate.EntityMode;
+import org.hibernate.ScrollableResults;
+import org.hibernate.Session;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.criterion.Order;
+import org.hibernate.dialect.SybaseDialect;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.transaction.CMTTransactionFactory;
+import org.hibernate.util.SerializationHelper;
+
+/**
+ * @author Gavin King
+ */
+public class CMTTest extends FunctionalTestCase {
+
+ public CMTTest(String str) {
+ super( str );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "tm/Item.hbm.xml" };
+ }
+
+ public void configure(Configuration cfg) {
+ cfg.setProperty( Environment.CONNECTION_PROVIDER, DummyConnectionProvider.class.getName() );
+ cfg.setProperty( Environment.TRANSACTION_MANAGER_STRATEGY, DummyTransactionManagerLookup.class.getName() );
+ cfg.setProperty( Environment.TRANSACTION_STRATEGY, CMTTransactionFactory.class.getName() );
+ cfg.setProperty( Environment.AUTO_CLOSE_SESSION, "true" );
+ cfg.setProperty( Environment.FLUSH_BEFORE_COMPLETION, "true" );
+ cfg.setProperty( Environment.RELEASE_CONNECTIONS, ConnectionReleaseMode.AFTER_STATEMENT.toString() );
+ cfg.setProperty( Environment.GENERATE_STATISTICS, "true" );
+ cfg.setProperty( Environment.USE_QUERY_CACHE, "true" );
+ cfg.setProperty( Environment.DEFAULT_ENTITY_MODE, EntityMode.MAP.toString() );
+ }
+
+ public String getCacheConcurrencyStrategy() {
+ return "transactional";
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( CMTTest.class );
+ }
+
+ public void testConcurrent() throws Exception {
+ getSessions().getStatistics().clear();
+
+ DummyTransactionManager.INSTANCE.begin();
+ Session s = openSession();
+ Map foo = new HashMap();
+ foo.put( "name", "Foo" );
+ foo.put( "description", "a big foo" );
+ s.persist( "Item", foo );
+ Map bar = new HashMap();
+ bar.put( "name", "Bar" );
+ bar.put( "description", "a small bar" );
+ s.persist( "Item", bar );
+ DummyTransactionManager.INSTANCE.commit();
+
+ getSessions().evictEntity( "Item" );
+
+ DummyTransactionManager.INSTANCE.begin();
+ Session s1 = openSession();
+ foo = ( Map ) s1.get( "Item", "Foo" );
+ //foo.put("description", "a big red foo");
+ //s1.flush();
+ Transaction tx1 = DummyTransactionManager.INSTANCE.suspend();
+
+ DummyTransactionManager.INSTANCE.begin();
+ Session s2 = openSession();
+ foo = ( Map ) s2.get( "Item", "Foo" );
+ DummyTransactionManager.INSTANCE.commit();
+
+ DummyTransactionManager.INSTANCE.resume( tx1 );
+ tx1.commit();
+
+ getSessions().evictEntity( "Item" );
+
+ DummyTransactionManager.INSTANCE.begin();
+ s1 = openSession();
+ s1.createCriteria( "Item" ).list();
+ //foo.put("description", "a big red foo");
+ //s1.flush();
+ tx1 = DummyTransactionManager.INSTANCE.suspend();
+
+ DummyTransactionManager.INSTANCE.begin();
+ s2 = openSession();
+ s2.createCriteria( "Item" ).list();
+ DummyTransactionManager.INSTANCE.commit();
+
+ DummyTransactionManager.INSTANCE.resume( tx1 );
+ tx1.commit();
+
+ DummyTransactionManager.INSTANCE.begin();
+ s2 = openSession();
+ s2.createCriteria( "Item" ).list();
+ DummyTransactionManager.INSTANCE.commit();
+
+ assertEquals( getSessions().getStatistics().getEntityLoadCount(), 7 );
+ assertEquals( getSessions().getStatistics().getEntityFetchCount(), 0 );
+ assertEquals( getSessions().getStatistics().getQueryExecutionCount(), 3 );
+ assertEquals( getSessions().getStatistics().getQueryCacheHitCount(), 0 );
+ assertEquals( getSessions().getStatistics().getQueryCacheMissCount(), 0 );
+
+ DummyTransactionManager.INSTANCE.begin();
+ s = openSession();
+ s.createQuery( "delete from Item" ).executeUpdate();
+ DummyTransactionManager.INSTANCE.commit();
+ }
+
+ public void testConcurrentCachedQueries() throws Exception {
+
+ DummyTransactionManager.INSTANCE.begin();
+ Session s = openSession();
+ Map foo = new HashMap();
+ foo.put( "name", "Foo" );
+ foo.put( "description", "a big foo" );
+ s.persist( "Item", foo );
+ Map bar = new HashMap();
+ bar.put( "name", "Bar" );
+ bar.put( "description", "a small bar" );
+ s.persist( "Item", bar );
+ DummyTransactionManager.INSTANCE.commit();
+
+ synchronized ( this ) {
+ wait( 1000 );
+ }
+
+ getSessions().getStatistics().clear();
+
+ getSessions().evictEntity( "Item" );
+
+ DummyTransactionManager.INSTANCE.begin();
+ Session s4 = openSession();
+ Transaction tx4 = DummyTransactionManager.INSTANCE.suspend();
+
+ DummyTransactionManager.INSTANCE.begin();
+ Session s1 = openSession();
+ List r1 = s1.createCriteria( "Item" ).addOrder( Order.asc( "description" ) )
+ .setCacheable( true ).list();
+ assertEquals( r1.size(), 2 );
+ Transaction tx1 = DummyTransactionManager.INSTANCE.suspend();
+
+ DummyTransactionManager.INSTANCE.begin();
+ Session s2 = openSession();
+ List r2 = s2.createCriteria( "Item" ).addOrder( Order.asc( "description" ) )
+ .setCacheable( true ).list();
+ assertEquals( r2.size(), 2 );
+ DummyTransactionManager.INSTANCE.commit();
+
+ assertEquals( getSessions().getStatistics().getSecondLevelCacheHitCount(), 2 );
+ assertEquals( getSessions().getStatistics().getSecondLevelCacheMissCount(), 0 );
+ assertEquals( getSessions().getStatistics().getEntityLoadCount(), 2 );
+ assertEquals( getSessions().getStatistics().getEntityFetchCount(), 0 );
+ assertEquals( getSessions().getStatistics().getQueryExecutionCount(), 1 );
+ assertEquals( getSessions().getStatistics().getQueryCachePutCount(), 1 );
+ assertEquals( getSessions().getStatistics().getQueryCacheHitCount(), 1 );
+ assertEquals( getSessions().getStatistics().getQueryCacheMissCount(), 1 );
+
+ DummyTransactionManager.INSTANCE.resume( tx1 );
+ tx1.commit();
+
+ DummyTransactionManager.INSTANCE.begin();
+ Session s3 = openSession();
+ s3.createCriteria( "Item" ).addOrder( Order.asc( "description" ) )
+ .setCacheable( true ).list();
+ DummyTransactionManager.INSTANCE.commit();
+
+ assertEquals( getSessions().getStatistics().getSecondLevelCacheHitCount(), 4 );
+ assertEquals( getSessions().getStatistics().getSecondLevelCacheMissCount(), 0 );
+ assertEquals( getSessions().getStatistics().getEntityLoadCount(), 2 );
+ assertEquals( getSessions().getStatistics().getEntityFetchCount(), 0 );
+ assertEquals( getSessions().getStatistics().getQueryExecutionCount(), 1 );
+ assertEquals( getSessions().getStatistics().getQueryCachePutCount(), 1 );
+ assertEquals( getSessions().getStatistics().getQueryCacheHitCount(), 2 );
+ assertEquals( getSessions().getStatistics().getQueryCacheMissCount(), 1 );
+
+ DummyTransactionManager.INSTANCE.resume( tx4 );
+ List r4 = s4.createCriteria( "Item" ).addOrder( Order.asc( "description" ) )
+ .setCacheable( true ).list();
+ assertEquals( r4.size(), 2 );
+ tx4.commit();
+
+ assertEquals( getSessions().getStatistics().getSecondLevelCacheHitCount(), 6 );
+ assertEquals( getSessions().getStatistics().getSecondLevelCacheMissCount(), 0 );
+ assertEquals( getSessions().getStatistics().getEntityLoadCount(), 2 );
+ assertEquals( getSessions().getStatistics().getEntityFetchCount(), 0 );
+ assertEquals( getSessions().getStatistics().getQueryExecutionCount(), 1 );
+ assertEquals( getSessions().getStatistics().getQueryCachePutCount(), 1 );
+ assertEquals( getSessions().getStatistics().getQueryCacheHitCount(), 3 );
+ assertEquals( getSessions().getStatistics().getQueryCacheMissCount(), 1 );
+
+ DummyTransactionManager.INSTANCE.begin();
+ s = openSession();
+ s.createQuery( "delete from Item" ).executeUpdate();
+ DummyTransactionManager.INSTANCE.commit();
+ }
+
+ public void testConcurrentCachedDirtyQueries() throws Exception {
+ if ( getDialect().doesReadCommittedCauseWritersToBlockReaders() ) {
+ reportSkip( "write locks block readers", "concurrent queries" );
+ return;
+ }
+
+ DummyTransactionManager.INSTANCE.begin();
+ Session s = openSession();
+ Map foo = new HashMap();
+ foo.put( "name", "Foo" );
+ foo.put( "description", "a big foo" );
+ s.persist( "Item", foo );
+ Map bar = new HashMap();
+ bar.put( "name", "Bar" );
+ bar.put( "description", "a small bar" );
+ s.persist( "Item", bar );
+ DummyTransactionManager.INSTANCE.commit();
+
+ synchronized ( this ) {
+ wait( 1000 );
+ }
+
+ getSessions().getStatistics().clear();
+
+ getSessions().evictEntity( "Item" );
+
+ DummyTransactionManager.INSTANCE.begin();
+ Session s4 = openSession();
+ Transaction tx4 = DummyTransactionManager.INSTANCE.suspend();
+
+ DummyTransactionManager.INSTANCE.begin();
+ Session s1 = openSession();
+ List r1 = s1.createCriteria( "Item" ).addOrder( Order.asc( "description" ) )
+ .setCacheable( true ).list();
+ assertEquals( r1.size(), 2 );
+ foo = ( Map ) r1.get( 0 );
+ foo.put( "description", "a big red foo" );
+ s1.flush();
+ Transaction tx1 = DummyTransactionManager.INSTANCE.suspend();
+
+ DummyTransactionManager.INSTANCE.begin();
+ Session s2 = openSession();
+ List r2 = s2.createCriteria( "Item" ).addOrder( Order.asc( "description" ) )
+ .setCacheable( true ).list();
+ assertEquals( r2.size(), 2 );
+ DummyTransactionManager.INSTANCE.commit();
+
+ assertEquals( getSessions().getStatistics().getSecondLevelCacheHitCount(), 0 );
+ assertEquals( getSessions().getStatistics().getSecondLevelCacheMissCount(), 0 );
+ assertEquals( getSessions().getStatistics().getEntityLoadCount(), 4 );
+ assertEquals( getSessions().getStatistics().getEntityFetchCount(), 0 );
+ assertEquals( getSessions().getStatistics().getQueryExecutionCount(), 2 );
+ assertEquals( getSessions().getStatistics().getQueryCachePutCount(), 2 );
+ assertEquals( getSessions().getStatistics().getQueryCacheHitCount(), 0 );
+ assertEquals( getSessions().getStatistics().getQueryCacheMissCount(), 2 );
+
+ DummyTransactionManager.INSTANCE.resume( tx1 );
+ tx1.commit();
+
+ DummyTransactionManager.INSTANCE.begin();
+ Session s3 = openSession();
+ s3.createCriteria( "Item" ).addOrder( Order.asc( "description" ) )
+ .setCacheable( true ).list();
+ DummyTransactionManager.INSTANCE.commit();
+
+ assertEquals( getSessions().getStatistics().getSecondLevelCacheHitCount(), 0 );
+ assertEquals( getSessions().getStatistics().getSecondLevelCacheMissCount(), 0 );
+ assertEquals( getSessions().getStatistics().getEntityLoadCount(), 6 );
+ assertEquals( getSessions().getStatistics().getEntityFetchCount(), 0 );
+ assertEquals( getSessions().getStatistics().getQueryExecutionCount(), 3 );
+ assertEquals( getSessions().getStatistics().getQueryCachePutCount(), 3 );
+ assertEquals( getSessions().getStatistics().getQueryCacheHitCount(), 0 );
+ assertEquals( getSessions().getStatistics().getQueryCacheMissCount(), 3 );
+
+ DummyTransactionManager.INSTANCE.resume( tx4 );
+ List r4 = s4.createCriteria( "Item" ).addOrder( Order.asc( "description" ) )
+ .setCacheable( true ).list();
+ assertEquals( r4.size(), 2 );
+ tx4.commit();
+
+ assertEquals( getSessions().getStatistics().getSecondLevelCacheHitCount(), 2 );
+ assertEquals( getSessions().getStatistics().getSecondLevelCacheMissCount(), 0 );
+ assertEquals( getSessions().getStatistics().getEntityLoadCount(), 6 );
+ assertEquals( getSessions().getStatistics().getEntityFetchCount(), 0 );
+ assertEquals( getSessions().getStatistics().getQueryExecutionCount(), 3 );
+ assertEquals( getSessions().getStatistics().getQueryCachePutCount(), 3 );
+ assertEquals( getSessions().getStatistics().getQueryCacheHitCount(), 1 );
+ assertEquals( getSessions().getStatistics().getQueryCacheMissCount(), 3 );
+
+ DummyTransactionManager.INSTANCE.begin();
+ s = openSession();
+ s.createQuery( "delete from Item" ).executeUpdate();
+ DummyTransactionManager.INSTANCE.commit();
+ }
+
+ public void testCMT() throws Exception {
+ getSessions().getStatistics().clear();
+
+ DummyTransactionManager.INSTANCE.begin();
+ Session s = openSession();
+ DummyTransactionManager.INSTANCE.getTransaction().commit();
+ assertFalse( s.isOpen() );
+
+ assertEquals( getSessions().getStatistics().getFlushCount(), 0 );
+
+ DummyTransactionManager.INSTANCE.begin();
+ s = openSession();
+ DummyTransactionManager.INSTANCE.getTransaction().rollback();
+ assertFalse( s.isOpen() );
+
+ DummyTransactionManager.INSTANCE.begin();
+ s = openSession();
+ Map item = new HashMap();
+ item.put( "name", "The Item" );
+ item.put( "description", "The only item we have" );
+ s.persist( "Item", item );
+ DummyTransactionManager.INSTANCE.getTransaction().commit();
+ assertFalse( s.isOpen() );
+
+ DummyTransactionManager.INSTANCE.begin();
+ s = openSession();
+ item = ( Map ) s.createQuery( "from Item" ).uniqueResult();
+ assertNotNull( item );
+ s.delete( item );
+ DummyTransactionManager.INSTANCE.getTransaction().commit();
+ assertFalse( s.isOpen() );
+
+ assertEquals( getSessions().getStatistics().getTransactionCount(), 4 );
+ assertEquals( getSessions().getStatistics().getSuccessfulTransactionCount(), 3 );
+ assertEquals( getSessions().getStatistics().getEntityDeleteCount(), 1 );
+ assertEquals( getSessions().getStatistics().getEntityInsertCount(), 1 );
+ assertEquals( getSessions().getStatistics().getSessionOpenCount(), 4 );
+ assertEquals( getSessions().getStatistics().getSessionCloseCount(), 4 );
+ assertEquals( getSessions().getStatistics().getQueryExecutionCount(), 1 );
+ assertEquals( getSessions().getStatistics().getFlushCount(), 2 );
+
+ DummyTransactionManager.INSTANCE.begin();
+ s = openSession();
+ s.createQuery( "delete from Item" ).executeUpdate();
+ DummyTransactionManager.INSTANCE.commit();
+
+ }
+
+ public void testCurrentSession() throws Exception {
+ DummyTransactionManager.INSTANCE.begin();
+ Session s = getSessions().getCurrentSession();
+ Session s2 = getSessions().getCurrentSession();
+ assertSame( s, s2 );
+ DummyTransactionManager.INSTANCE.getTransaction().commit();
+ assertFalse( s.isOpen() );
+
+ // TODO : would be nice to automate-test that the SF internal map actually gets cleaned up
+ // i verified that is does currently in my debugger...
+ }
+
+ public void testCurrentSessionWithIterate() throws Exception {
+ DummyTransactionManager.INSTANCE.begin();
+ Session s = openSession();
+ Map item1 = new HashMap();
+ item1.put( "name", "Item - 1" );
+ item1.put( "description", "The first item" );
+ s.persist( "Item", item1 );
+
+ Map item2 = new HashMap();
+ item2.put( "name", "Item - 2" );
+ item2.put( "description", "The second item" );
+ s.persist( "Item", item2 );
+ DummyTransactionManager.INSTANCE.getTransaction().commit();
+
+ // First, test iterating the partial iterator; iterate to past
+ // the first, but not the second, item
+ DummyTransactionManager.INSTANCE.begin();
+ s = getSessions().getCurrentSession();
+ Iterator itr = s.createQuery( "from Item" ).iterate();
+ if ( !itr.hasNext() ) {
+ fail( "No results in iterator" );
+ }
+ itr.next();
+ if ( !itr.hasNext() ) {
+ fail( "Only one result in iterator" );
+ }
+ DummyTransactionManager.INSTANCE.getTransaction().commit();
+
+ // Next, iterate the entire result
+ DummyTransactionManager.INSTANCE.begin();
+ s = getSessions().getCurrentSession();
+ itr = s.createQuery( "from Item" ).iterate();
+ if ( !itr.hasNext() ) {
+ fail( "No results in iterator" );
+ }
+ while ( itr.hasNext() ) {
+ itr.next();
+ }
+ DummyTransactionManager.INSTANCE.getTransaction().commit();
+
+ DummyTransactionManager.INSTANCE.begin();
+ s = openSession();
+ s.createQuery( "delete from Item" ).executeUpdate();
+ DummyTransactionManager.INSTANCE.getTransaction().commit();
+ }
+
+ public void testCurrentSessionWithScroll() throws Exception {
+ DummyTransactionManager.INSTANCE.begin();
+ Session s = getSessions().getCurrentSession();
+ Map item1 = new HashMap();
+ item1.put( "name", "Item - 1" );
+ item1.put( "description", "The first item" );
+ s.persist( "Item", item1 );
+
+ Map item2 = new HashMap();
+ item2.put( "name", "Item - 2" );
+ item2.put( "description", "The second item" );
+ s.persist( "Item", item2 );
+ DummyTransactionManager.INSTANCE.getTransaction().commit();
+
+ // First, test partially scrolling the result with out closing
+ DummyTransactionManager.INSTANCE.begin();
+ s = getSessions().getCurrentSession();
+ ScrollableResults results = s.createQuery( "from Item" ).scroll();
+ results.next();
+ DummyTransactionManager.INSTANCE.getTransaction().commit();
+
+ // Next, test partially scrolling the result with closing
+ DummyTransactionManager.INSTANCE.begin();
+ s = getSessions().getCurrentSession();
+ results = s.createQuery( "from Item" ).scroll();
+ results.next();
+ results.close();
+ DummyTransactionManager.INSTANCE.getTransaction().commit();
+
+ // Next, scroll the entire result (w/o closing)
+ DummyTransactionManager.INSTANCE.begin();
+ s = getSessions().getCurrentSession();
+ results = s.createQuery( "from Item" ).scroll();
+ while ( !results.isLast() ) {
+ results.next();
+ }
+ DummyTransactionManager.INSTANCE.getTransaction().commit();
+
+ // Next, scroll the entire result (closing)
+ DummyTransactionManager.INSTANCE.begin();
+ s = getSessions().getCurrentSession();
+ results = s.createQuery( "from Item" ).scroll();
+ while ( !results.isLast() ) {
+ results.next();
+ }
+ results.close();
+ DummyTransactionManager.INSTANCE.getTransaction().commit();
+
+ DummyTransactionManager.INSTANCE.begin();
+ s = getSessions().getCurrentSession();
+ s.createQuery( "delete from Item" ).executeUpdate();
+ DummyTransactionManager.INSTANCE.getTransaction().commit();
+ }
+
+ public void testAggressiveReleaseWithExplicitDisconnectReconnect() throws Exception {
+ DummyTransactionManager.INSTANCE.begin();
+ Session s = getSessions().getCurrentSession();
+
+ s.createQuery( "from Item" ).list();
+
+ s.disconnect();
+ byte[] bytes = SerializationHelper.serialize( s );
+ s = ( Session ) SerializationHelper.deserialize( bytes );
+ s.reconnect();
+
+ s.createQuery( "from Item" ).list();
+
+ DummyTransactionManager.INSTANCE.getTransaction().commit();
+ }
+
+ public void testAggressiveReleaseWithConnectionRetreival() throws Exception {
+ DummyTransactionManager.INSTANCE.begin();
+ Session s = openSession();
+ Map item1 = new HashMap();
+ item1.put( "name", "Item - 1" );
+ item1.put( "description", "The first item" );
+ s.save( "Item", item1 );
+
+ Map item2 = new HashMap();
+ item2.put( "name", "Item - 2" );
+ item2.put( "description", "The second item" );
+ s.save( "Item", item2 );
+ DummyTransactionManager.INSTANCE.getTransaction().commit();
+
+ try {
+ DummyTransactionManager.INSTANCE.begin();
+ s = getSessions().getCurrentSession();
+ s.createQuery( "from Item" ).scroll().next();
+ s.connection();
+ DummyTransactionManager.INSTANCE.getTransaction().commit();
+ }
+ finally {
+ DummyTransactionManager.INSTANCE.begin();
+ s = openSession();
+ s.createQuery( "delete from Item" ).executeUpdate();
+ DummyTransactionManager.INSTANCE.getTransaction().commit();
+ }
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tm/CMTTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tm/DummyConnectionProvider.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tm/DummyConnectionProvider.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tm/DummyConnectionProvider.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,48 @@
+//$Id: DummyConnectionProvider.java 6501 2005-04-24 00:18:28Z oneovthafew $
+package org.hibernate.test.tm;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import org.hibernate.HibernateException;
+import org.hibernate.connection.ConnectionProvider;
+import org.hibernate.connection.ConnectionProviderFactory;
+
+/**
+ * @author Gavin King
+ */
+public class DummyConnectionProvider implements ConnectionProvider {
+
+ ConnectionProvider cp;
+ boolean isTransaction;
+
+ public void configure(Properties props) throws HibernateException {
+ cp = ConnectionProviderFactory.newConnectionProvider();
+ }
+
+ public Connection getConnection() throws SQLException {
+ DummyTransactionManager dtm = DummyTransactionManager.INSTANCE;
+ if ( dtm!=null && dtm.getCurrent()!=null && dtm.getCurrent().getConnection()!=null ) {
+ isTransaction = true;
+ return dtm.getCurrent().getConnection();
+ }
+ else {
+ isTransaction = false;
+ return cp.getConnection();
+ }
+ }
+
+ public void closeConnection(Connection conn) throws SQLException {
+ if (!isTransaction) conn.close();
+ }
+
+ public void close() throws HibernateException {
+
+ }
+
+ public boolean supportsAggressiveRelease() {
+ return true;
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tm/DummyConnectionProvider.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tm/DummyJTAStyleTransationFactory.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tm/DummyJTAStyleTransationFactory.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tm/DummyJTAStyleTransationFactory.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,138 @@
+package org.hibernate.test.tm;
+
+import org.hibernate.transaction.TransactionFactory;
+import org.hibernate.Transaction;
+import org.hibernate.HibernateException;
+import org.hibernate.ConnectionReleaseMode;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.util.JTAHelper;
+import org.hibernate.jdbc.JDBCContext;
+
+import javax.transaction.SystemException;
+import javax.transaction.Synchronization;
+import java.util.Properties;
+
+/**
+ * todo: describe DummyJTAStyleTransationFactory
+ *
+ * @author Steve Ebersole
+ */
+public class DummyJTAStyleTransationFactory implements TransactionFactory {
+ public Transaction createTransaction(
+ JDBCContext jdbcContext,
+ Context context) throws HibernateException {
+ return new DummyTransactionAdapter();
+ }
+
+ public void configure(Properties props) throws HibernateException {
+ }
+
+ public ConnectionReleaseMode getDefaultReleaseMode() {
+ return ConnectionReleaseMode.AFTER_STATEMENT;
+ }
+
+ public boolean isTransactionManagerRequired() {
+ return true;
+ }
+
+ public boolean areCallbacksLocalToHibernateTransactions() {
+ return false;
+ }
+
+ public boolean isTransactionInProgress(
+ JDBCContext jdbcContext,
+ Context transactionContext,
+ Transaction transaction) {
+ try {
+ return JTAHelper.isTransactionInProgress( DummyTransactionManager.INSTANCE.getCurrent() )
+ && ! JTAHelper.isMarkedForRollback( DummyTransactionManager.INSTANCE.getCurrent() );
+ }
+ catch( SystemException e ) {
+ throw new HibernateException( e );
+ }
+ }
+
+ private static class DummyTransactionAdapter implements Transaction {
+
+ private boolean started;
+ private boolean committed;
+ private boolean rolledback;
+
+ public void begin() throws HibernateException {
+ started = true;
+ committed = false;
+ rolledback = false;
+ try {
+ DummyTransactionManager.INSTANCE.begin();
+ }
+ catch( Throwable t ) {
+ throw new HibernateException( "error on begin()", t );
+ }
+ }
+
+ public void commit() throws HibernateException {
+ if ( !started ) {
+ throw new HibernateException( "not yet started!" );
+ }
+ started = false;
+ rolledback = false;
+ committed = false;
+ try {
+ DummyTransactionManager.INSTANCE.commit();
+ committed = true;
+ }
+ catch( Throwable t ) {
+ throw new HibernateException( "error on commit()", t );
+ }
+ }
+
+ public void rollback() throws HibernateException {
+ if ( !started ) {
+ throw new HibernateException( "not yet started!" );
+ }
+ started = false;
+ rolledback = false;
+ committed = false;
+ try {
+ DummyTransactionManager.INSTANCE.rollback();
+ rolledback = true;
+ }
+ catch( Throwable t ) {
+ throw new HibernateException( "error on rollback()", t );
+ }
+ }
+
+ public boolean wasRolledBack() throws HibernateException {
+ return rolledback;
+ }
+
+ public boolean wasCommitted() throws HibernateException {
+ return committed;
+ }
+
+ public boolean isActive() throws HibernateException {
+ return started;
+ }
+
+ public void registerSynchronization(Synchronization synchronization) throws HibernateException {
+ try {
+ DummyTransactionManager.INSTANCE.getCurrent().registerSynchronization( synchronization );
+ }
+ catch( Throwable t ) {
+ throw new HibernateException( "error on registerSynchronization()", t );
+ }
+ }
+
+ public void setTimeout(int seconds) {
+ // ignore...
+ }
+ }
+
+ public static void setup(Configuration cfg) {
+ cfg.setProperty( Environment.CONNECTION_PROVIDER, DummyConnectionProvider.class.getName() );
+ cfg.setProperty( Environment.TRANSACTION_MANAGER_STRATEGY, DummyTransactionManagerLookup.class.getName() );
+ cfg.setProperty( Environment.TRANSACTION_STRATEGY, DummyJTAStyleTransationFactory.class.getName() );
+ cfg.setProperty( Environment.FLUSH_BEFORE_COMPLETION, "true" );
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tm/DummyTransaction.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tm/DummyTransaction.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tm/DummyTransaction.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,146 @@
+//$Id: DummyTransaction.java 8411 2005-10-14 23:29:04Z maxcsaucdk $
+package org.hibernate.test.tm;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.transaction.HeuristicMixedException;
+import javax.transaction.HeuristicRollbackException;
+import javax.transaction.RollbackException;
+import javax.transaction.Status;
+import javax.transaction.Synchronization;
+import javax.transaction.SystemException;
+import javax.transaction.Transaction;
+import javax.transaction.xa.XAResource;
+
+/**
+ * @author Gavin King
+ */
+public class DummyTransaction implements Transaction {
+
+ int status;
+ private Connection connection;
+ List synchronizations = new ArrayList();
+ private DummyTransactionManager transactionManager;
+
+ DummyTransaction(DummyTransactionManager transactionManager) {
+ status = Status.STATUS_NO_TRANSACTION;
+ this.transactionManager = transactionManager;
+ }
+
+ public void begin() throws SystemException {
+ try {
+ connection = transactionManager.connections.getConnection();
+ }
+ catch (SQLException sqle) {
+
+ sqle.printStackTrace();
+ throw new SystemException(sqle.toString());
+ }
+ status = Status.STATUS_ACTIVE;
+ }
+
+ public void commit() throws RollbackException, HeuristicMixedException,
+ HeuristicRollbackException, SecurityException,
+ IllegalStateException, SystemException {
+
+ if (status == Status.STATUS_MARKED_ROLLBACK) {
+ rollback();
+ }
+ else {
+ status = Status.STATUS_PREPARING;
+
+ for ( int i=0; i<synchronizations.size(); i++ ) {
+ Synchronization s = (Synchronization) synchronizations.get(i);
+ s.beforeCompletion();
+ }
+
+ status = Status.STATUS_COMMITTING;
+
+ try {
+ connection.commit();
+ connection.close();
+ }
+ catch (SQLException sqle) {
+ status = Status.STATUS_UNKNOWN;
+ throw new SystemException();
+ }
+
+ status = Status.STATUS_COMMITTED;
+
+ for ( int i=0; i<synchronizations.size(); i++ ) {
+ Synchronization s = (Synchronization) synchronizations.get(i);
+ s.afterCompletion(status);
+ }
+
+ //status = Status.STATUS_NO_TRANSACTION;
+ transactionManager.endCurrent(this);
+ }
+
+ }
+
+ public boolean delistResource(XAResource arg0, int arg1)
+ throws IllegalStateException, SystemException {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ public boolean enlistResource(XAResource arg0) throws RollbackException,
+ IllegalStateException, SystemException {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ public int getStatus() throws SystemException {
+ return status;
+ }
+
+ public void registerSynchronization(Synchronization sync)
+ throws RollbackException, IllegalStateException, SystemException {
+ synchronizations.add(sync);
+ }
+
+ public void rollback() throws IllegalStateException, SystemException {
+
+ status = Status.STATUS_ROLLING_BACK;
+
+// Synch.beforeCompletion() should *not* be called for rollbacks
+// for ( int i=0; i<synchronizations.size(); i++ ) {
+// Synchronization s = (Synchronization) synchronizations.get(i);
+// s.beforeCompletion();
+// }
+
+ status = Status.STATUS_ROLLEDBACK;
+
+ try {
+ connection.rollback();
+ connection.close();
+ }
+ catch (SQLException sqle) {
+ status = Status.STATUS_UNKNOWN;
+ throw new SystemException();
+ }
+
+ for ( int i=0; i<synchronizations.size(); i++ ) {
+ Synchronization s = (Synchronization) synchronizations.get(i);
+ s.afterCompletion(status);
+ }
+
+ //status = Status.STATUS_NO_TRANSACTION;
+ transactionManager.endCurrent(this);
+ }
+
+ public void setRollbackOnly() throws IllegalStateException, SystemException {
+ status = Status.STATUS_MARKED_ROLLBACK;
+ }
+
+ void setConnection(Connection connection) {
+ this.connection = connection;
+ }
+
+ public Connection getConnection() {
+ return connection;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tm/DummyTransaction.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tm/DummyTransactionManager.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tm/DummyTransactionManager.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tm/DummyTransactionManager.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,84 @@
+//$Id: DummyTransactionManager.java 7003 2005-06-03 16:09:59Z steveebersole $
+package org.hibernate.test.tm;
+
+import java.util.Properties;
+
+import javax.transaction.HeuristicMixedException;
+import javax.transaction.HeuristicRollbackException;
+import javax.transaction.InvalidTransactionException;
+import javax.transaction.NotSupportedException;
+import javax.transaction.RollbackException;
+import javax.transaction.SystemException;
+import javax.transaction.Transaction;
+import javax.transaction.TransactionManager;
+
+import org.hibernate.connection.ConnectionProvider;
+import org.hibernate.connection.ConnectionProviderFactory;
+
+/**
+ * @author Gavin King
+ */
+public class DummyTransactionManager implements TransactionManager {
+
+ public static DummyTransactionManager INSTANCE;
+
+ private DummyTransaction current;
+ ConnectionProvider connections;
+
+ public DummyTransactionManager(Properties props) {
+ connections = ConnectionProviderFactory.newConnectionProvider();
+ }
+
+ public void begin() throws NotSupportedException, SystemException {
+ current = new DummyTransaction(this);
+ current.begin();
+ }
+
+ public void commit() throws RollbackException, HeuristicMixedException,
+ HeuristicRollbackException, SecurityException,
+ IllegalStateException, SystemException {
+ current.commit();
+ }
+
+
+ public int getStatus() throws SystemException {
+ return current.getStatus();
+ }
+
+ public Transaction getTransaction() throws SystemException {
+ return current;
+ }
+
+ public void resume(Transaction tx) throws InvalidTransactionException,
+ IllegalStateException, SystemException {
+ current = (DummyTransaction) tx;
+ }
+
+ public void rollback() throws IllegalStateException, SecurityException,
+ SystemException {
+ current.rollback();
+
+ }
+
+ public void setRollbackOnly() throws IllegalStateException, SystemException {
+ current.setRollbackOnly();
+ }
+
+ public void setTransactionTimeout(int t) throws SystemException {
+ }
+
+ public Transaction suspend() throws SystemException {
+ Transaction result = current;
+ current = null;
+ return result;
+ }
+
+ public DummyTransaction getCurrent() {
+ return current;
+ }
+
+ void endCurrent(DummyTransaction tx) {
+ if (current==tx) current=null;
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tm/DummyTransactionManager.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tm/DummyTransactionManagerLookup.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tm/DummyTransactionManagerLookup.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tm/DummyTransactionManagerLookup.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,28 @@
+//$Id: DummyTransactionManagerLookup.java 5693 2005-02-13 01:59:07Z oneovthafew $
+package org.hibernate.test.tm;
+
+import java.util.Properties;
+
+import javax.transaction.TransactionManager;
+
+import org.hibernate.HibernateException;
+import org.hibernate.transaction.TransactionManagerLookup;
+
+/**
+ * @author Gavin King
+ */
+public class DummyTransactionManagerLookup implements TransactionManagerLookup {
+
+ public TransactionManager getTransactionManager(Properties props)
+ throws HibernateException {
+ if ( DummyTransactionManager.INSTANCE == null ) {
+ DummyTransactionManager.INSTANCE = new DummyTransactionManager(props);
+ }
+ return DummyTransactionManager.INSTANCE;
+ }
+
+ public String getUserTransactionName() {
+ throw new UnsupportedOperationException();
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tm/DummyTransactionManagerLookup.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tm/Item.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tm/Item.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tm/Item.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.tm">
+
+ <class entity-name="Item" table="Things">
+ <id name="name" type="string"/>
+ <property name="description" type="string"/>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tm/Item.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tool/Team.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tool/Team.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tool/Team.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping SYSTEM
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.tool">
+ <class name="Team" table="`Team`">
+ <id name="id" column="`iD`">
+ <generator class="native">
+ <param name="sequence">TEAM_SEQ</param>
+ </generator>
+ </id>
+ <property name="name"/>
+ </class>
+
+ <class entity-name="OtherTeam" name="Team" table="TEAM">
+ <id name="id" column="id">
+ <generator class="native">
+ <param name="sequence">TEAM_SEQ</param>
+ </generator>
+ </id>
+ <property name="name" column="xname"/>
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tool/Team.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tool/Team.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tool/Team.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,20 @@
+package org.hibernate.test.tool;
+
+
+public class Team {
+ private Long id;
+ private String 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/testsuite/src/test/java/org/hibernate/test/tool/TestSchemaTools.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tool/TestSchemaTools.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/tool/TestSchemaTools.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,226 @@
+package org.hibernate.test.tool;
+
+import java.sql.Connection;
+import java.sql.Statement;
+import java.sql.SQLException;
+
+import junit.framework.Test;
+
+import org.hibernate.HibernateException;
+import org.hibernate.Session;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.HSQLDialect;
+import org.hibernate.junit.functional.DatabaseSpecificFunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.tool.hbm2ddl.SchemaExport;
+import org.hibernate.tool.hbm2ddl.SchemaUpdate;
+import org.hibernate.tool.hbm2ddl.SchemaValidator;
+
+/**
+ * @author Anthony
+ *
+ * Basic smoke test for schemaupdate/validator.
+ * Dependent on schemas, and probably also HQLDB - Not possible to have in the global test suite at the moment.
+ *
+ * WARNING, if you want this test to work, you need to define a default schema = SB
+ * in hibernate global configuration.
+ * This schema should not be the same at the default db user schema and should come after the users schema alphabetically.
+ *
+ */
+public class TestSchemaTools extends DatabaseSpecificFunctionalTestCase {
+
+ public TestSchemaTools(String name) {
+ super( name );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "tool/Team.hbm.xml" };
+ }
+
+ public boolean createSchema() {
+ return false;
+ }
+
+ public void afterSessionFactoryBuilt(SessionFactoryImplementor sfi) {
+ super.afterSessionFactoryBuilt( sfi );
+ Session session = null;
+ try {
+ session = sfi.openSession();
+ Statement stat = session.connection().createStatement();
+ stat.execute("CREATE SCHEMA sb AUTHORIZATION DBA ");
+ stat.execute(" CREATE SCHEMA sa AUTHORIZATION DBA ");
+ stat.execute(" CREATE TABLE \"SA\".\"Team\" (test INTEGER) ");
+ stat.close();
+ }
+ catch ( SQLException e ) {
+ throw new RuntimeException( "could not prepare additional schemas" );
+ }
+ finally {
+ if ( session != null ) {
+ try {
+ session.close();
+ }
+ catch( Throwable ignore ) {
+ }
+ }
+ }
+ }
+
+ protected void prepareTest() throws Exception {
+ super.prepareTest();
+ }
+
+
+ public boolean appliesTo(Dialect dialect) {
+ return dialect instanceof HSQLDialect;
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( TestSchemaTools.class );
+ }
+
+ public void testSchemaTools() throws Exception{
+ // database schema have been created thanks to the setUp method
+ // we have 2 schemas SA et SB, SB must be set as the default schema
+ // used by hibernate hibernate.default_schema SB
+ SchemaExport se = new SchemaExport(getCfg());
+ se.create(true,true);
+
+ // here we modify the generated table in order to test SchemaUpdate
+ Session session = openSession();
+ Connection conn = session.connection();
+ Statement stat = conn.createStatement();
+ stat.execute("ALTER TABLE \"SB\".\"Team\" DROP COLUMN name ");
+
+ // update schema
+ SchemaUpdate su = new SchemaUpdate(getCfg());
+ su.execute(true,true);
+
+ // we can run schema validation. Note that in the setUp method a *wrong* table
+ // has been created with different column names
+ // if schema validator chooses the bad db schema, then the testcase will fail (exception)
+ SchemaValidator sv = new SchemaValidator(getCfg());
+ sv.validate();
+
+ // it's time to clean our database
+ se.drop(true,true);
+
+ // then the schemas and false table.
+
+ stat.execute("DROP TABLE \"SA\".\"Team\" ");
+ stat.execute(" DROP SCHEMA sa ");
+ stat.execute("DROP SCHEMA sb ");
+ stat.close();
+ session.close();
+ }
+
+ public void testSchemaToolsNonQuote() throws Exception{
+ // database schema have been created thanks to the setUp method
+ // we have 2 schemas SA et SB, SB must be set as the default schema
+ // used by hibernate hibernate.default_schema SB
+ SchemaExport se = new SchemaExport(getCfg());
+ se.create(true,true);
+
+ // here we modify the generated table in order to test SchemaUpdate
+ Session session = openSession();
+ Connection conn = session.connection();
+ Statement stat = conn.createStatement();
+ stat.execute("ALTER TABLE \"SB\".\"TEAM\" DROP COLUMN xname ");
+
+ // update schema
+ SchemaUpdate su = new SchemaUpdate(getCfg());
+ su.execute(true,true);
+
+ // we can run schema validation. Note that in the setUp method a *wrong* table
+ // has been created with different column names
+ // if schema validator chooses the bad db schema, then the testcase will fail (exception)
+ SchemaValidator sv = new SchemaValidator(getCfg());
+ sv.validate();
+
+ // it's time to clean our database
+ se.drop(true,true);
+
+ // then the schemas and false table.
+
+ stat.execute("DROP TABLE \"SA\".\"Team\" ");
+ stat.execute(" DROP SCHEMA sa ");
+ stat.execute("DROP SCHEMA sb ");
+ stat.close();
+ session.close();
+ }
+ public void testFailingQuoteValidation() throws Exception{
+ // database schema have been created thanks to the setUp method
+ // we have 2 schemas SA et SB, SB must be set as the default schema
+ // used by hibernate hibernate.default_schema SB
+ SchemaExport se = new SchemaExport(getCfg());
+ se.create(true,true);
+
+ // here we modify the generated table in order to test SchemaUpdate
+ Session session = openSession();
+ Connection conn = session.connection();
+ Statement stat = conn.createStatement();
+ stat.execute("ALTER TABLE \"SB\".\"Team\" DROP COLUMN name ");
+
+ // update schema
+ //SchemaUpdate su = new SchemaUpdate(getCfg());
+ //su.execute(true,true);
+
+ try {
+ SchemaValidator sv = new SchemaValidator(getCfg());
+ sv.validate();
+ fail("should fail since we mutated the current schema.");
+ } catch(HibernateException he) {
+
+ }
+
+ // it's time to clean our database
+ se.drop(true,true);
+
+ // then the schemas and false table.
+
+ stat.execute("DROP TABLE \"SA\".\"Team\" ");
+ stat.execute(" DROP SCHEMA sa ");
+ stat.execute("DROP SCHEMA sb ");
+ stat.close();
+ session.close();
+ }
+
+ public void testFailingNonQuoteValidation() throws Exception{
+ // database schema have been created thanks to the setUp method
+ // we have 2 schemas SA et SB, SB must be set as the default schema
+ // used by hibernate hibernate.default_schema SB
+ SchemaExport se = new SchemaExport(getCfg());
+ se.create(true,true);
+
+ // here we modify the generated table in order to test SchemaUpdate
+ Session session = openSession();
+ Connection conn = session.connection();
+ Statement stat = conn.createStatement();
+ stat.execute("ALTER TABLE \"SB\".\"TEAM\" DROP COLUMN xname ");
+
+ // update schema
+ //SchemaUpdate su = new SchemaUpdate(getCfg());
+ //su.execute(true,true);
+
+ try {
+ SchemaValidator sv = new SchemaValidator(getCfg());
+ sv.validate();
+ fail("should fail since we mutated the current schema.");
+ } catch(HibernateException he) {
+
+ }
+
+ // it's time to clean our database
+ se.drop(true,true);
+
+ // then the schemas and false table.
+
+ stat.execute("DROP TABLE \"SA\".\"Team\" ");
+ stat.execute(" DROP SCHEMA sa ");
+ stat.execute("DROP SCHEMA sb ");
+ stat.close();
+ session.close();
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedmanytoone/Address.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedmanytoone/Address.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedmanytoone/Address.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,55 @@
+//$Id: Address.java 6979 2005-06-01 03:51:32Z oneovthafew $
+package org.hibernate.test.typedmanytoone;
+
+import java.io.Serializable;
+
+/**
+ * @author Gavin King
+ */
+public class Address implements Serializable {
+
+ private AddressId addressId;
+ private String street;
+ private String city;
+ private String state;
+ private String zip;
+ private Customer customer;
+
+ public Customer getCustomer() {
+ return customer;
+ }
+ public void setCustomer(Customer customer) {
+ this.customer = customer;
+ }
+
+ public String getCity() {
+ return city;
+ }
+ public void setCity(String city) {
+ this.city = city;
+ }
+ public String getState() {
+ return state;
+ }
+ public void setState(String state) {
+ this.state = state;
+ }
+ public String getStreet() {
+ return street;
+ }
+ public void setStreet(String street) {
+ this.street = street;
+ }
+ public String getZip() {
+ return zip;
+ }
+ public void setZip(String zip) {
+ this.zip = zip;
+ }
+ public AddressId getAddressId() {
+ return addressId;
+ }
+ public void setAddressId(AddressId addressId) {
+ this.addressId = addressId;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedmanytoone/Address.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedmanytoone/AddressId.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedmanytoone/AddressId.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedmanytoone/AddressId.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,45 @@
+//$Id: AddressId.java 6979 2005-06-01 03:51:32Z oneovthafew $
+package org.hibernate.test.typedmanytoone;
+
+import java.io.Serializable;
+
+/**
+ * @author Gavin King
+ */
+public class AddressId implements Serializable {
+ private String type;
+ private String addressId;
+
+ public AddressId(String type, String customerId) {
+ this.addressId = customerId;
+ this.type = type;
+ }
+
+ public AddressId() {}
+
+ public String getType() {
+ return type;
+ }
+ public void setType(String type) {
+ this.type = type;
+ }
+ public String getAddressId() {
+ return addressId;
+ }
+ public void setAddressId(String customerId) {
+ this.addressId = customerId;
+ }
+ public boolean equals(Object other) {
+ if ( !(other instanceof AddressId) ) return false;
+ AddressId add = (AddressId) other;
+ return type.equals(add.type) && addressId.equals(add.addressId);
+ }
+ public int hashCode() {
+ return addressId.hashCode() + type.hashCode();
+ }
+
+ public String toString() {
+ return type + '#' + addressId;
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedmanytoone/AddressId.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedmanytoone/Customer.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedmanytoone/Customer.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedmanytoone/Customer.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,85 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+ Shows how to map a one-to-many relationship in the relational
+ schema to "typed" one-to-one associations in the object model.
+ We map the Address class twice, with different entity names,
+ specifying a filtering condition in each mapping. The typed
+ associations then reference the named entities.
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.typedmanytoone">
+
+ <class name="Customer"
+ select-before-update="true"
+ dynamic-update="true">
+
+ <id name="customerId">
+ <generator class="assigned"/>
+ </id>
+
+ <property name="name" not-null="true"/>
+
+ <many-to-one name="billingAddress"
+ entity-name="BillingAddress"
+ cascade="persist,save-update,delete"
+ fetch="join">
+ <column name="billingAddressId"/>
+ <formula>'BILLING'</formula>
+ </many-to-one>
+
+ <many-to-one name="shippingAddress"
+ entity-name="ShippingAddress"
+ cascade="persist,save-update,delete"
+ fetch="join">
+ <column name="shippingAddressId"/>
+ <formula>'SHIPPING'</formula>
+ </many-to-one>
+
+ </class>
+
+ <class name="Address"
+ table="Address"
+ entity-name="BillingAddress"
+ where="type='BILLING'"
+ check="type in ('BILLING', 'SHIPPING')"
+ select-before-update="true"
+ dynamic-update="true">
+
+ <composite-id name="addressId">
+ <key-property name="addressId"/>
+ <key-property name="type"/>
+ </composite-id>
+
+ <property name="street" not-null="true"/>
+ <property name="city" not-null="true"/>
+ <property name="state" not-null="true"/>
+ <property name="zip" not-null="true"/>
+
+ </class>
+
+ <class name="Address"
+ table="Address"
+ entity-name="ShippingAddress"
+ where="type='SHIPPING'"
+ select-before-update="true"
+ dynamic-update="true">
+
+ <composite-id name="addressId">
+ <key-property name="addressId"/>
+ <key-property name="type"/>
+ </composite-id>
+
+ <property name="street" not-null="true"/>
+ <property name="city" not-null="true"/>
+ <property name="state" not-null="true"/>
+ <property name="zip" not-null="true"/>
+
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedmanytoone/Customer.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedmanytoone/Customer.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedmanytoone/Customer.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedmanytoone/Customer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,40 @@
+//$Id: Customer.java 6979 2005-06-01 03:51:32Z oneovthafew $
+package org.hibernate.test.typedmanytoone;
+
+import java.io.Serializable;
+
+/**
+ * @author Gavin King
+ */
+public class Customer implements Serializable {
+
+ private String name;
+ private String customerId;
+ private Address billingAddress;
+ private Address shippingAddress;
+
+ public Address getBillingAddress() {
+ return billingAddress;
+ }
+ public void setBillingAddress(Address billingAddress) {
+ this.billingAddress = billingAddress;
+ }
+ public String getCustomerId() {
+ return customerId;
+ }
+ public void setCustomerId(String customerId) {
+ this.customerId = customerId;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public Address getShippingAddress() {
+ return shippingAddress;
+ }
+ public void setShippingAddress(Address shippingAddress) {
+ this.shippingAddress = shippingAddress;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedmanytoone/Customer.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedmanytoone/TypedManyToOneTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedmanytoone/TypedManyToOneTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedmanytoone/TypedManyToOneTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,113 @@
+//$Id: TypedManyToOneTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.typedmanytoone;
+
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class TypedManyToOneTest extends FunctionalTestCase {
+
+ public TypedManyToOneTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "typedmanytoone/Customer.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( TypedManyToOneTest.class );
+ }
+
+ public void testCreateQuery() {
+ Customer cust = new Customer();
+ cust.setCustomerId("abc123");
+ cust.setName("Matt");
+
+ Address ship = new Address();
+ ship.setStreet("peachtree rd");
+ ship.setState("GA");
+ ship.setCity("ATL");
+ ship.setZip("30326");
+ ship.setAddressId( new AddressId("SHIPPING", "xyz123") );
+ ship.setCustomer(cust);
+
+ Address bill = new Address();
+ bill.setStreet("peachtree rd");
+ bill.setState("GA");
+ bill.setCity("ATL");
+ bill.setZip("30326");
+ bill.setAddressId( new AddressId("BILLING", "xyz123") );
+ bill.setCustomer(cust);
+
+ cust.setBillingAddress(bill);
+ cust.setShippingAddress(ship);
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.persist(cust);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ List results = s.createQuery("from Customer cust left join fetch cust.billingAddress where cust.customerId='abc123'").list();
+ //List results = s.createQuery("from Customer cust left join fetch cust.billingAddress left join fetch cust.shippingAddress").list();
+ cust = (Customer) results.get(0);
+ assertFalse( Hibernate.isInitialized( cust.getShippingAddress() ) );
+ assertTrue( Hibernate.isInitialized( cust.getBillingAddress() ) );
+ assertEquals( "30326", cust.getBillingAddress().getZip() );
+ assertEquals( "30326", cust.getShippingAddress().getZip() );
+ assertEquals( "BILLING", cust.getBillingAddress().getAddressId().getType() );
+ assertEquals( "SHIPPING", cust.getShippingAddress().getAddressId().getType() );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.saveOrUpdate(cust);
+ ship = cust.getShippingAddress();
+ cust.setShippingAddress(null);
+ s.delete("ShippingAddress", ship);
+ s.flush();
+ assertNull( s.get( "ShippingAddress", ship.getAddressId() ) );
+ s.delete( cust );
+ t.commit();
+ s.close();
+ }
+
+ public void testCreateQueryNull() {
+ Customer cust = new Customer();
+ cust.setCustomerId("xyz123");
+ cust.setName("Matt");
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.persist(cust);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ List results = s.createQuery("from Customer cust left join fetch cust.billingAddress where cust.customerId='xyz123'").list();
+ //List results = s.createQuery("from Customer cust left join fetch cust.billingAddress left join fetch cust.shippingAddress").list();
+ cust = (Customer) results.get(0);
+ assertNull( cust.getShippingAddress() );
+ assertNull( cust.getBillingAddress() );
+ s.delete( cust );
+ t.commit();
+ s.close();
+
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedmanytoone/TypedManyToOneTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedonetoone/Address.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedonetoone/Address.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedonetoone/Address.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,55 @@
+//$Id: Address.java 5686 2005-02-12 07:27:32Z steveebersole $
+package org.hibernate.test.typedonetoone;
+
+import java.io.Serializable;
+
+/**
+ * @author Gavin King
+ */
+public class Address implements Serializable {
+
+ private AddressId addressId;
+ private String street;
+ private String city;
+ private String state;
+ private String zip;
+ private Customer customer;
+
+ public Customer getCustomer() {
+ return customer;
+ }
+ public void setCustomer(Customer customer) {
+ this.customer = customer;
+ }
+
+ public String getCity() {
+ return city;
+ }
+ public void setCity(String city) {
+ this.city = city;
+ }
+ public String getState() {
+ return state;
+ }
+ public void setState(String state) {
+ this.state = state;
+ }
+ public String getStreet() {
+ return street;
+ }
+ public void setStreet(String street) {
+ this.street = street;
+ }
+ public String getZip() {
+ return zip;
+ }
+ public void setZip(String zip) {
+ this.zip = zip;
+ }
+ public AddressId getAddressId() {
+ return addressId;
+ }
+ public void setAddressId(AddressId addressId) {
+ this.addressId = addressId;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedonetoone/Address.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedonetoone/AddressId.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedonetoone/AddressId.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedonetoone/AddressId.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,41 @@
+//$Id: AddressId.java 5686 2005-02-12 07:27:32Z steveebersole $
+package org.hibernate.test.typedonetoone;
+
+import java.io.Serializable;
+
+/**
+ * @author Gavin King
+ */
+public class AddressId implements Serializable {
+ private String type;
+ private String customerId;
+
+ public AddressId(String type, String customerId) {
+ this.customerId = customerId;
+ this.type = type;
+ }
+
+ public AddressId() {}
+
+ public String getType() {
+ return type;
+ }
+ public void setType(String type) {
+ this.type = type;
+ }
+ public String getCustomerId() {
+ return customerId;
+ }
+ public void setCustomerId(String customerId) {
+ this.customerId = customerId;
+ }
+ public boolean equals(Object other) {
+ if ( !(other instanceof AddressId) ) return false;
+ AddressId add = (AddressId) other;
+ return type.equals(add.type) && customerId.equals(add.customerId);
+ }
+ public int hashCode() {
+ return customerId.hashCode() + type.hashCode();
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedonetoone/AddressId.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedonetoone/Customer.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedonetoone/Customer.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedonetoone/Customer.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,85 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+ Shows how to map a one-to-many relationship in the relational
+ schema to "typed" one-to-one associations in the object model.
+ We map the Address class twice, with different entity names,
+ specifying a filtering condition in each mapping. The typed
+ associations then reference the named entities.
+
+-->
+
+<hibernate-mapping package="org.hibernate.test.typedonetoone">
+
+ <class name="Customer">
+
+ <id name="customerId">
+ <generator class="assigned"/>
+ </id>
+
+ <property name="name" not-null="true"/>
+
+ <one-to-one name="billingAddress"
+ entity-name="BillingAddress"
+ property-ref="customer"
+ cascade="persist, delete"
+ fetch="join"/>
+
+ <one-to-one name="shippingAddress"
+ entity-name="ShippingAddress"
+ property-ref="customer"
+ cascade="persist, delete"
+ fetch="join"/>
+
+ </class>
+
+ <class name="Address"
+ table="Address"
+ entity-name="BillingAddress"
+ where="type='BILLING'"
+ check="type in ('BILLING', 'SHIPPING')">
+
+ <composite-id name="addressId">
+ <key-property name="customerId"/>
+ <key-property name="type"/>
+ </composite-id>
+
+ <property name="street" not-null="true"/>
+ <property name="city" not-null="true"/>
+ <property name="state" not-null="true"/>
+ <property name="zip" not-null="true"/>
+
+ <many-to-one name="customer"
+ column="customerId"
+ insert="false"
+ update="false"/>
+
+ </class>
+
+ <class name="Address"
+ table="Address"
+ entity-name="ShippingAddress"
+ where="type='SHIPPING'">
+
+ <composite-id name="addressId">
+ <key-property name="customerId"/>
+ <key-property name="type"/>
+ </composite-id>
+
+ <property name="street" not-null="true"/>
+ <property name="city" not-null="true"/>
+ <property name="state" not-null="true"/>
+ <property name="zip" not-null="true"/>
+
+ <many-to-one name="customer"
+ column="customerId"
+ insert="false"
+ update="false"/>
+
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedonetoone/Customer.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedonetoone/Customer.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedonetoone/Customer.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedonetoone/Customer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,40 @@
+//$Id: Customer.java 5686 2005-02-12 07:27:32Z steveebersole $
+package org.hibernate.test.typedonetoone;
+
+import java.io.Serializable;
+
+/**
+ * @author Gavin King
+ */
+public class Customer implements Serializable {
+
+ private String name;
+ private String customerId;
+ private Address billingAddress;
+ private Address shippingAddress;
+
+ public Address getBillingAddress() {
+ return billingAddress;
+ }
+ public void setBillingAddress(Address billingAddress) {
+ this.billingAddress = billingAddress;
+ }
+ public String getCustomerId() {
+ return customerId;
+ }
+ public void setCustomerId(String customerId) {
+ this.customerId = customerId;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public Address getShippingAddress() {
+ return shippingAddress;
+ }
+ public void setShippingAddress(Address shippingAddress) {
+ this.shippingAddress = shippingAddress;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedonetoone/Customer.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedonetoone/TypedOneToOneTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedonetoone/TypedOneToOneTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedonetoone/TypedOneToOneTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,103 @@
+//$Id: TypedOneToOneTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.typedonetoone;
+
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class TypedOneToOneTest extends FunctionalTestCase {
+
+ public TypedOneToOneTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "typedonetoone/Customer.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( TypedOneToOneTest.class );
+ }
+
+ public void testCreateQuery() {
+ Customer cust = new Customer();
+ cust.setCustomerId("abc123");
+ cust.setName("Matt");
+
+ Address ship = new Address();
+ ship.setStreet("peachtree rd");
+ ship.setState("GA");
+ ship.setCity("ATL");
+ ship.setZip("30326");
+ ship.setAddressId( new AddressId("SHIPPING", "abc123") );
+ ship.setCustomer(cust);
+
+ Address bill = new Address();
+ bill.setStreet("peachtree rd");
+ bill.setState("GA");
+ bill.setCity("ATL");
+ bill.setZip("30326");
+ bill.setAddressId( new AddressId("BILLING", "abc123") );
+ bill.setCustomer(cust);
+
+ cust.setBillingAddress(bill);
+ cust.setShippingAddress(ship);
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.persist(cust);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ List results = s.createQuery("from Customer cust left join fetch cust.billingAddress where cust.customerId='abc123'").list();
+ //List results = s.createQuery("from Customer cust left join fetch cust.billingAddress left join fetch cust.shippingAddress").list();
+ cust = (Customer) results.get(0);
+ assertTrue( Hibernate.isInitialized( cust.getShippingAddress() ) );
+ assertTrue( Hibernate.isInitialized( cust.getBillingAddress() ) );
+ assertEquals( "30326", cust.getBillingAddress().getZip() );
+ assertEquals( "30326", cust.getShippingAddress().getZip() );
+ assertEquals( "BILLING", cust.getBillingAddress().getAddressId().getType() );
+ assertEquals( "SHIPPING", cust.getShippingAddress().getAddressId().getType() );
+ s.delete( cust );
+ t.commit();
+ s.close();
+
+ }
+
+ public void testCreateQueryNull() {
+ Customer cust = new Customer();
+ cust.setCustomerId("xyz123");
+ cust.setName("Matt");
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.persist(cust);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ List results = s.createQuery("from Customer cust left join fetch cust.billingAddress where cust.customerId='xyz123'").list();
+ //List results = s.createQuery("from Customer cust left join fetch cust.billingAddress left join fetch cust.shippingAddress").list();
+ cust = (Customer) results.get(0);
+ assertNull( cust.getShippingAddress() );
+ assertNull( cust.getBillingAddress() );
+ s.delete(cust);
+ t.commit();
+ s.close();
+
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typedonetoone/TypedOneToOneTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typeparameters/DefaultValueIntegerType.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typeparameters/DefaultValueIntegerType.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typeparameters/DefaultValueIntegerType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,82 @@
+package org.hibernate.test.typeparameters;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.Properties;
+
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.HibernateException;
+import org.hibernate.usertype.ParameterizedType;
+import org.hibernate.usertype.UserType;
+
+
+/**
+ * @author Michi
+ */
+public class DefaultValueIntegerType implements UserType, ParameterizedType, Serializable {
+
+ private Integer defaultValue;
+
+ public int[] sqlTypes() {
+ return new int[] {Types.INTEGER};
+ }
+
+ public Class returnedClass() {
+ return int.class;
+ }
+
+ public boolean equals(Object x, Object y) throws HibernateException {
+ if (x==y) return true;
+ if (x==null || y==null) return false;
+ return x.equals(y);
+ }
+
+ public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException {
+ Number result = (Number) rs.getObject(names[0]);
+ return result==null ? defaultValue : new Integer(result.intValue());
+ }
+
+ public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException {
+ if (value == null || defaultValue.equals(value) ) {
+ LogFactory.getLog( getClass() ).trace("binding null to parameter: " + index);
+ st.setNull(index, Types.INTEGER);
+ } else {
+ LogFactory.getLog( getClass() ).trace("binding " + value + " to parameter: " + index);
+ st.setInt(index, ((Integer)value).intValue());
+ }
+ }
+
+ public Object deepCopy(Object value) throws HibernateException {
+ return new Integer(((Integer)value).intValue());
+ }
+
+ public boolean isMutable() {
+ return false;
+ }
+
+ public int hashCode(Object x) throws HibernateException {
+ return x.hashCode();
+ }
+
+ public Object assemble(Serializable cached, Object owner)
+ throws HibernateException {
+ return cached;
+ }
+
+ public Serializable disassemble(Object value) throws HibernateException {
+ return (Serializable) value;
+ }
+
+ public Object replace(Object original, Object target, Object owner)
+ throws HibernateException {
+ return original;
+ }
+
+ public void setParameterValues(Properties parameters) {
+ this.defaultValue = Integer.valueOf((String) parameters.get("default"));
+ }
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typeparameters/TypeParameterTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typeparameters/TypeParameterTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typeparameters/TypeParameterTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,122 @@
+//$Id: TypeParameterTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.typeparameters;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+
+import junit.framework.Test;
+
+import org.hibernate.Transaction;
+import org.hibernate.classic.Session;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * Test for parameterizable types.
+ *
+ * @author Michael Gloegl
+ */
+public class TypeParameterTest extends FunctionalTestCase {
+
+ public TypeParameterTest(String name) {
+ super(name);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "typeparameters/Typedef.hbm.xml", "typeparameters/Widget.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( TypeParameterTest.class );
+ }
+
+ public void testSave() throws Exception {
+ deleteData();
+
+ Session s = openSession();
+
+ Transaction t = s.beginTransaction();
+
+ Widget obj = new Widget();
+ obj.setValueThree(5);
+
+ Integer id = (Integer) s.save(obj);
+
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+
+ Connection connection = s.connection();
+ PreparedStatement statement = connection.prepareStatement("SELECT * FROM STRANGE_TYPED_OBJECT WHERE ID=?");
+ statement.setInt(1, id.intValue());
+ ResultSet resultSet = statement.executeQuery();
+
+ assertTrue("A row should have been returned", resultSet.next());
+ assertTrue("Default value should have been mapped to null", resultSet.getObject("VALUE_ONE") == null);
+ assertTrue("Default value should have been mapped to null", resultSet.getObject("VALUE_TWO") == null);
+ assertEquals("Non-Default value should not be changed", resultSet.getInt("VALUE_THREE"), 5);
+ assertTrue("Default value should have been mapped to null", resultSet.getObject("VALUE_FOUR") == null);
+
+ deleteData();
+ t.commit();
+ s.close();
+ }
+
+ public void testLoading() throws Exception {
+ initData();
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ Widget obj = (Widget) s.createQuery("from Widget o where o.string = :string").setString("string", "all-normal").uniqueResult();
+ assertEquals("Non-Default value incorrectly loaded", obj.getValueOne(), 7);
+ assertEquals("Non-Default value incorrectly loaded", obj.getValueTwo(), 8);
+ assertEquals("Non-Default value incorrectly loaded", obj.getValueThree(), 9);
+ assertEquals("Non-Default value incorrectly loaded", obj.getValueFour(), 10);
+
+ obj = (Widget) s.createQuery("from Widget o where o.string = :string").setString("string", "all-default").uniqueResult();
+ assertEquals("Default value incorrectly loaded", obj.getValueOne(), 1);
+ assertEquals("Default value incorrectly loaded", obj.getValueTwo(), 2);
+ assertEquals("Default value incorrectly loaded", obj.getValueThree(), -1);
+ assertEquals("Default value incorrectly loaded", obj.getValueFour(), -5);
+
+ deleteData();
+ t.commit();
+ s.close();
+ }
+
+ private void initData() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ Widget obj = new Widget();
+ obj.setValueOne(7);
+ obj.setValueTwo(8);
+ obj.setValueThree(9);
+ obj.setValueFour(10);
+ obj.setString("all-normal");
+ s.save(obj);
+
+ obj = new Widget();
+ obj.setValueOne(1);
+ obj.setValueTwo(2);
+ obj.setValueThree(-1);
+ obj.setValueFour(-5);
+ obj.setString("all-default");
+ s.save(obj);
+
+ t.commit();
+ s.close();
+ }
+
+ private void deleteData() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ s.delete("from Widget");
+ t.commit();
+ s.close();
+ }
+}
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typeparameters/Typedef.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typeparameters/Typedef.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typeparameters/Typedef.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping SYSTEM "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping>
+
+ <typedef name="nullToMinusOne"
+ class="org.hibernate.test.typeparameters.DefaultValueIntegerType">
+ <param name="default">-1</param>
+ </typedef>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typeparameters/Widget.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typeparameters/Widget.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typeparameters/Widget.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping SYSTEM "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.typeparameters">
+
+ <typedef name="nullToTwo"
+ class="org.hibernate.test.typeparameters.DefaultValueIntegerType">
+ <param name="default">2</param>
+ </typedef>
+
+ <class name="Widget" table="STRANGE_TYPED_OBJECT">
+ <id name="id" column="ID">
+ <generator class="increment"/>
+ </id>
+ <property name="valueOne" column="VALUE_ONE">
+ <type name="org.hibernate.test.typeparameters.DefaultValueIntegerType">
+ <param name="default">1</param>
+ </type>
+ </property>
+ <property name="valueTwo" column="VALUE_TWO" type="nullToTwo"/>
+ <property name="valueThree" column="VALUE_THREE" type="nullToMinusOne"/>
+ <property name="valueFour" column="VALUE_FOUR">
+ <type name="nullToTwo">
+ <param name="default">-5</param>
+ </type>
+ </property>
+ <property name="string"/>
+ </class>
+
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typeparameters/Widget.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typeparameters/Widget.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/typeparameters/Widget.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,62 @@
+package org.hibernate.test.typeparameters;
+
+/**
+ * @author Michael Gloegl
+ */
+public class Widget {
+
+ private int valueOne = 1;
+ private int valueTwo = 2;
+ private int valueThree = -1;
+ private int valueFour = -5;
+ private Integer id;
+ private String string;
+
+ public int getValueOne() {
+ return valueOne;
+ }
+
+ public void setValueOne(int valueOne) {
+ this.valueOne = valueOne;
+ }
+
+ public int getValueThree() {
+ return valueThree;
+ }
+
+ public void setValueThree(int valueThree) {
+ this.valueThree = valueThree;
+ }
+
+ public int getValueTwo() {
+ return valueTwo;
+ }
+
+ public void setValueTwo(int valueTwo) {
+ this.valueTwo = valueTwo;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getString() {
+ return string;
+ }
+
+ public void setString(String string) {
+ this.string = string;
+ }
+
+ public int getValueFour() {
+ return valueFour;
+ }
+
+ public void setValueFour(int valueFour) {
+ this.valueFour = valueFour;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unconstrained/Employee.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unconstrained/Employee.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unconstrained/Employee.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,28 @@
+//$Id: Employee.java 6298 2005-04-03 03:56:22Z oneovthafew $
+package org.hibernate.test.unconstrained;
+
+/**
+ * @author Gavin King
+ */
+public class Employee {
+
+ private String id;
+
+ public Employee() {
+ }
+
+ public Employee(String id) {
+ this.id = id;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unconstrained/Employee.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unconstrained/Person.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unconstrained/Person.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unconstrained/Person.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping
+ package="org.hibernate.test.unconstrained"
+ default-access="field">
+
+ <class name="Person"
+ dynamic-update="true">
+
+ <id name="name"/>
+
+ <property name="employeeId"
+ unique="true"/>
+
+ <many-to-one name="employee"
+ formula="employeeId"
+ not-found="ignore"
+ cascade="all"/>
+
+ </class>
+
+ <class name="Employee">
+
+ <id name="id"/>
+
+ </class>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unconstrained/Person.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unconstrained/Person.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unconstrained/Person.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unconstrained/Person.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,43 @@
+//$Id: Person.java 6298 2005-04-03 03:56:22Z oneovthafew $
+package org.hibernate.test.unconstrained;
+
+/**
+ * @author Gavin King
+ */
+public class Person {
+
+ private String name;
+ private String employeeId;
+ private Employee employee;
+
+ public Person() {}
+ public Person(String name) {
+ this.name = name;
+ }
+
+ public Employee getEmployee() {
+ return employee;
+ }
+
+ public void setEmployee(Employee employee) {
+ this.employee = employee;
+ }
+
+ public String getEmployeeId() {
+ return employeeId;
+ }
+
+ public void setEmployeeId(String employeeId) {
+ this.employeeId = employeeId;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unconstrained/Person.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unconstrained/UnconstrainedTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unconstrained/UnconstrainedTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unconstrained/UnconstrainedTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,127 @@
+//$Id: UnconstrainedTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.unconstrained;
+
+import junit.framework.Test;
+
+import org.hibernate.FetchMode;
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.criterion.Restrictions;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class UnconstrainedTest extends FunctionalTestCase {
+
+ public UnconstrainedTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "unconstrained/Person.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( UnconstrainedTest.class );
+ }
+
+ public void testUnconstrainedNoCache() {
+ Session session = openSession();
+ Transaction tx = session.beginTransaction();
+ Person p = new Person("gavin");
+ p.setEmployeeId("123456");
+ session.persist(p);
+ tx.commit();
+ session.close();
+
+ getSessions().evict(Person.class);
+
+ session = openSession();
+ tx = session.beginTransaction();
+ p = (Person) session.get(Person.class, "gavin");
+ assertNull( p.getEmployee() );
+ p.setEmployee( new Employee("123456") );
+ tx.commit();
+ session.close();
+
+ getSessions().evict(Person.class);
+
+ session = openSession();
+ tx = session.beginTransaction();
+ p = (Person) session.get(Person.class, "gavin");
+ assertTrue( Hibernate.isInitialized( p.getEmployee() ) );
+ assertNotNull( p.getEmployee() );
+ session.delete(p);
+ tx.commit();
+ session.close();
+ }
+
+ public void testUnconstrainedOuterJoinFetch() {
+ Session session = openSession();
+ Transaction tx = session.beginTransaction();
+ Person p = new Person("gavin");
+ p.setEmployeeId("123456");
+ session.persist(p);
+ tx.commit();
+ session.close();
+
+ getSessions().evict(Person.class);
+
+ session = openSession();
+ tx = session.beginTransaction();
+ p = (Person) session.createCriteria(Person.class)
+ .setFetchMode("employee", FetchMode.JOIN)
+ .add( Restrictions.idEq("gavin") )
+ .uniqueResult();
+ assertNull( p.getEmployee() );
+ p.setEmployee( new Employee("123456") );
+ tx.commit();
+ session.close();
+
+ getSessions().evict(Person.class);
+
+ session = openSession();
+ tx = session.beginTransaction();
+ p = (Person) session.createCriteria(Person.class)
+ .setFetchMode("employee", FetchMode.JOIN)
+ .add( Restrictions.idEq("gavin") )
+ .uniqueResult();
+ assertTrue( Hibernate.isInitialized( p.getEmployee() ) );
+ assertNotNull( p.getEmployee() );
+ session.delete(p);
+ tx.commit();
+ session.close();
+ }
+
+ public void testUnconstrained() {
+ Session session = openSession();
+ Transaction tx = session.beginTransaction();
+ Person p = new Person("gavin");
+ p.setEmployeeId("123456");
+ session.persist(p);
+ tx.commit();
+ session.close();
+
+ session = openSession();
+ tx = session.beginTransaction();
+ p = (Person) session.get(Person.class, "gavin");
+ assertNull( p.getEmployee() );
+ p.setEmployee( new Employee("123456") );
+ tx.commit();
+ session.close();
+
+ session = openSession();
+ tx = session.beginTransaction();
+ p = (Person) session.get(Person.class, "gavin");
+ assertTrue( Hibernate.isInitialized( p.getEmployee() ) );
+ assertNotNull( p.getEmployee() );
+ session.delete(p);
+ tx.commit();
+ session.close();
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unconstrained/UnconstrainedTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unidir/BackrefTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unidir/BackrefTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unidir/BackrefTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,88 @@
+//$Id: BackrefTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.unidir;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class BackrefTest extends FunctionalTestCase {
+
+ public BackrefTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "unidir/ParentChild.hbm.xml" };
+ }
+
+ public String getCacheConcurrencyStrategy() {
+ return null;
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( BackrefTest.class );
+ }
+
+ public void testBackRef() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Parent p = new Parent("Marc");
+ Parent p2 = new Parent("Nathalie");
+ Child c = new Child("Elvira");
+ Child c2 = new Child("Blase");
+ p.getChildren().add(c);
+ p.getChildren().add(c2);
+ s.persist(p);
+ s.persist(p2);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ c = (Child) s.get(Child.class, "Elvira");
+ c.setAge(2);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ p = (Parent) s.get(Parent.class, "Marc");
+ c = (Child) s.get(Child.class, "Elvira");
+ c.setAge(18);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ p = (Parent) s.get(Parent.class, "Marc");
+ p2 = (Parent) s.get(Parent.class, "Nathalie");
+ c = (Child) s.get(Child.class, "Elvira");
+ assertEquals( p.getChildren().indexOf(c), 0 );
+ p.getChildren().remove(c);
+ p2.getChildren().add(c);
+ t.commit();
+
+ s.close();
+ s = openSession();
+ t = s.beginTransaction();
+ Parent p3 = new Parent("Marion");
+ p3.getChildren().add( new Child("Gavin") );
+ s.merge(p3);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.createQuery( "delete from Child" ).executeUpdate();
+ s.createQuery( "delete from Parent" ).executeUpdate();
+ t.commit();
+ s.close();
+ }
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unidir/BackrefTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unidir/Child.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unidir/Child.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unidir/Child.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,27 @@
+//$Id: Child.java 5686 2005-02-12 07:27:32Z steveebersole $
+package org.hibernate.test.unidir;
+
+
+/**
+ * @author Gavin King
+ */
+public class Child {
+ private String name;
+ private int age;
+ Child() {}
+ public Child(String name) {
+ this.name = name;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public int getAge() {
+ return age;
+ }
+ public void setAge(int age) {
+ this.age = age;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unidir/Child.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unidir/Parent.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unidir/Parent.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unidir/Parent.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,29 @@
+//$Id: Parent.java 5686 2005-02-12 07:27:32Z steveebersole $
+package org.hibernate.test.unidir;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Gavin King
+ */
+public class Parent {
+ private String name;
+ private List children = new ArrayList();
+ Parent() {}
+ public Parent(String name) {
+ this.name = name;
+ }
+ public List getChildren() {
+ return children;
+ }
+ public void setChildren(List children) {
+ this.children = children;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unidir/Parent.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unidir/ParentChild.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unidir/ParentChild.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unidir/ParentChild.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping
+ package="org.hibernate.test.unidir">
+
+ <class name="Parent">
+ <id name="name"/>
+ <list name="children" cascade="persist,merge">
+ <key column="parentName" not-null="true"/>
+ <list-index column="sibling"/>
+ <one-to-many class="Child"/>
+ </list>
+ </class>
+
+ <class name="Child">
+ <id name="name"/>
+ <property name="age" not-null="true"/>
+ </class>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unidir/ParentChild.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Alien.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Alien.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Alien.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,38 @@
+//$Id: Alien.java 5686 2005-02-12 07:27:32Z steveebersole $
+package org.hibernate.test.unionsubclass;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Gavin King
+ */
+public class Alien extends Being {
+ private String species;
+ private Hive hive;
+ private List hivemates = new ArrayList();
+ /**
+ * @return Returns the species.
+ */
+ public String getSpecies() {
+ return species;
+ }
+ /**
+ * @param species The species to set.
+ */
+ public void setSpecies(String species) {
+ this.species = species;
+ }
+ public Hive getHive() {
+ return hive;
+ }
+ public void setHive(Hive hive) {
+ this.hive = hive;
+ }
+ public List getHivemates() {
+ return hivemates;
+ }
+ public void setHivemates(List hivemates) {
+ this.hivemates = hivemates;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Alien.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Being.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Being.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Being.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,72 @@
+//$Id: Being.java 6007 2005-03-04 12:01:43Z oneovthafew $
+package org.hibernate.test.unionsubclass;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Gavin King
+ */
+public abstract class Being {
+ private long id;
+ private String identity;
+ private Location location;
+ private List things = new ArrayList();
+ private Map info = new HashMap();
+ /**
+ * @return Returns the id.
+ */
+ public long getId() {
+ return id;
+ }
+ /**
+ * @param id The id to set.
+ */
+ public void setId(long id) {
+ this.id = id;
+ }
+ /**
+ * @return Returns the identity.
+ */
+ public String getIdentity() {
+ return identity;
+ }
+ /**
+ * @param identity The identity to set.
+ */
+ public void setIdentity(String identity) {
+ this.identity = identity;
+ }
+ /**
+ * @return Returns the location.
+ */
+ public Location getLocation() {
+ return location;
+ }
+ /**
+ * @param location The location to set.
+ */
+ public void setLocation(Location location) {
+ this.location = location;
+ }
+ public String getSpecies() {
+ return null;
+ }
+
+ public List getThings() {
+ return things;
+ }
+ public void setThings(List things) {
+ this.things = things;
+ }
+ public Map getInfo() {
+ return info;
+ }
+
+ public void setInfo(Map info) {
+ this.info = info;
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Being.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Beings.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Beings.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Beings.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,113 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping
+ package="org.hibernate.test.unionsubclass"
+ default-access="field">
+
+ <class name="Being" abstract="true">
+
+ <id name="id" unsaved-value="0" column="bid">
+ <generator class="increment"/>
+ <!--generator class="hilo">
+ <param name="table">being_id</param>
+ <param name="column">next_id</param>
+ </generator-->
+ </id>
+
+ <property name="identity" not-null="true" column="ident"/>
+ <many-to-one name="location" not-null="true"/>
+
+ <bag name="things" inverse="true">
+ <key column="owner"/>
+ <one-to-many class="Thing"/>
+ </bag>
+
+ <map name="info">
+ <key column="bid"/>
+ <map-key type="string" column="attribute"/>
+ <element type="string" column="value"/>
+ </map>
+
+ <union-subclass name="Human" table="humans">
+ <property name="sex"
+ not-null="true"
+ update="false"/>
+ <union-subclass name="Employee" table="employees">
+ <property name="salary"/>
+ </union-subclass>
+ </union-subclass>
+
+ <union-subclass name="Alien" table="aliens">
+ <property name="species"
+ not-null="true"
+ update="false"/>
+ <many-to-one name="hive" not-null="true"/>
+ <bag name="hivemates">
+ <key column="alien1"/>
+ <many-to-many column="alien2" class="Alien"/>
+ </bag>
+ </union-subclass>
+
+ </class>
+
+ <class name="Hive" table="hives">
+
+ <id name="id" unsaved-value="0">
+ <generator class="increment"/>
+ <!--generator class="hilo">
+ <param name="table">location_id</param>
+ <param name="column">next_id</param>
+ </generator-->
+ </id>
+
+ <many-to-one name="location" not-null="true"/>
+
+ <bag name="members"
+ inverse="true"
+ cascade="all,delete-orphan">
+ <key column="hive"/>
+ <one-to-many class="Alien"/>
+ </bag>
+
+ </class>
+
+ <class name="Location" table="locations">
+
+ <id name="id" unsaved-value="0">
+ <generator class="increment"/>
+ <!--generator class="hilo">
+ <param name="table">location_id</param>
+ <param name="column">next_id</param>
+ </generator-->
+ </id>
+
+ <property name="name" not-null="true"/>
+
+ <bag name="beings"
+ inverse="true"
+ cascade="all">
+ <key column="location"/>
+ <one-to-many class="Being"/>
+ </bag>
+
+ </class>
+
+ <class name="Thing" table="things">
+
+ <id name="id" unsaved-value="0">
+ <generator class="increment"/>
+ <!--generator class="hilo">
+ <param name="table">thing_id</param>
+ <param name="column">next_id</param>
+ </generator-->
+ </id>
+
+ <property name="description"/>
+ <many-to-one name="owner"/>
+
+ </class>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Beings.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Employee.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Employee.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Employee.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,17 @@
+//$Id: Employee.java 6673 2005-05-03 22:59:24Z epbernard $
+package org.hibernate.test.unionsubclass;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class Employee extends Human {
+ private Double salary;
+
+ public Double getSalary() {
+ return salary;
+ }
+
+ public void setSalary(Double salary) {
+ this.salary = salary;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Hive.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Hive.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Hive.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,32 @@
+//$Id: Hive.java 5686 2005-02-12 07:27:32Z steveebersole $
+package org.hibernate.test.unionsubclass;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Gavin King
+ */
+public class Hive {
+ private long id;
+ private Location location;
+ private List members = new ArrayList();
+ public List getMembers() {
+ return members;
+ }
+ public void setMembers(List hives) {
+ this.members = hives;
+ }
+ public long getId() {
+ return id;
+ }
+ public void setId(long id) {
+ this.id = id;
+ }
+ public Location getLocation() {
+ return location;
+ }
+ public void setLocation(Location location) {
+ this.location = location;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Hive.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Human.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Human.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Human.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,26 @@
+//$Id: Human.java 4364 2004-08-17 12:10:32Z oneovthafew $
+package org.hibernate.test.unionsubclass;
+
+/**
+ * @author Gavin King
+ */
+public class Human extends Being {
+ private char sex;
+
+ /**
+ * @return Returns the sex.
+ */
+ public char getSex() {
+ return sex;
+ }
+ /**
+ * @param sex The sex to set.
+ */
+ public void setSex(char sex) {
+ this.sex = sex;
+ }
+ public String getSpecies() {
+ return "human";
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Human.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Location.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Location.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Location.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,61 @@
+//$Id: Location.java 4357 2004-08-17 09:20:17Z oneovthafew $
+package org.hibernate.test.unionsubclass;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * @author Gavin King
+ */
+public class Location {
+ private long id;
+ private String name;
+ private Collection beings = new ArrayList();
+
+ Location() {}
+
+ public Location(String name) {
+ this.name = name;
+ }
+
+ public void addBeing(Being b) {
+ b.setLocation(this);
+ beings.add(b);
+ }
+ /**
+ * @return Returns the id.
+ */
+ public long getId() {
+ return id;
+ }
+ /**
+ * @param id The id to set.
+ */
+ public void setId(long id) {
+ this.id = id;
+ }
+ /**
+ * @return Returns the name.
+ */
+ public String getName() {
+ return name;
+ }
+ /**
+ * @param name The name to set.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+ /**
+ * @return Returns the beings.
+ */
+ public Collection getBeings() {
+ return beings;
+ }
+ /**
+ * @param beings The beings to set.
+ */
+ public void setBeings(Collection beings) {
+ this.beings = beings;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Location.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Thing.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Thing.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Thing.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,47 @@
+//$Id: Thing.java 4418 2004-08-22 13:38:16Z oneovthafew $
+package org.hibernate.test.unionsubclass;
+
+/**
+ * @author Gavin King
+ */
+public class Thing {
+ private long id;
+ private String description;
+ private Being owner;
+ /**
+ * @return Returns the description.
+ */
+ public String getDescription() {
+ return description;
+ }
+ /**
+ * @param description The description to set.
+ */
+ public void setDescription(String description) {
+ this.description = description;
+ }
+ /**
+ * @return Returns the id.
+ */
+ public long getId() {
+ return id;
+ }
+ /**
+ * @param id The id to set.
+ */
+ public void setId(long id) {
+ this.id = id;
+ }
+ /**
+ * @return Returns the owner.
+ */
+ public Being getOwner() {
+ return owner;
+ }
+ /**
+ * @param owner The owner to set.
+ */
+ public void setOwner(Being owner) {
+ this.owner = owner;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/Thing.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/UnionSubclassTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/UnionSubclassTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/UnionSubclassTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,401 @@
+//$Id: UnionSubclassTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.unionsubclass;
+
+import java.util.Iterator;
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.hibernate.FetchMode;
+import org.hibernate.Hibernate;
+import org.hibernate.Query;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.criterion.Order;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class UnionSubclassTest extends FunctionalTestCase {
+
+ public UnionSubclassTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "unionsubclass/Beings.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( UnionSubclassTest.class );
+ }
+
+ public void testUnionSubclassCollection() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Location mel = new Location("Earth");
+ s.save(mel);
+
+ Human gavin = new Human();
+ gavin.setIdentity("gavin");
+ gavin.setSex('M');
+ gavin.setLocation(mel);
+ mel.addBeing(gavin);
+
+ gavin.getInfo().put("foo", "bar");
+ gavin.getInfo().put("x", "y");
+
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ gavin = (Human) s.createCriteria(Human.class).uniqueResult();
+ assertEquals( gavin.getInfo().size(), 2 );
+ s.delete(gavin);
+ s.delete( gavin.getLocation() );
+ t.commit();
+ s.close();
+ }
+
+ public void testUnionSubclassFetchMode() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Location mel = new Location("Earth");
+ s.save(mel);
+
+ Human gavin = new Human();
+ gavin.setIdentity("gavin");
+ gavin.setSex('M');
+ gavin.setLocation(mel);
+ mel.addBeing(gavin);
+ Human max = new Human();
+ max.setIdentity("max");
+ max.setSex('M');
+ max.setLocation(mel);
+ mel.addBeing(gavin);
+
+ s.flush();
+ s.clear();
+
+ List list = s.createCriteria(Human.class)
+ .setFetchMode("location", FetchMode.JOIN)
+ .setFetchMode("location.beings", FetchMode.JOIN)
+ .list();
+
+ for (int i=0; i<list.size(); i++ ) {
+ Human h = (Human) list.get(i);
+ assertTrue( Hibernate.isInitialized( h.getLocation() ) );
+ assertTrue( Hibernate.isInitialized( h.getLocation().getBeings() ) );
+ s.delete(h);
+ }
+ s.delete( s.get( Location.class, new Long(mel.getId()) ) );
+ t.commit();
+ s.close();
+
+
+ }
+
+ public void testUnionSubclassOneToMany() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Location mel = new Location("Melbourne, Australia");
+ Location mars = new Location("Mars");
+ s.save(mel);
+ s.save(mars);
+
+ Human gavin = new Human();
+ gavin.setIdentity("gavin");
+ gavin.setSex('M');
+ gavin.setLocation(mel);
+ mel.addBeing(gavin);
+
+ Alien x23y4 = new Alien();
+ x23y4.setIdentity("x23y4$$hu%3");
+ x23y4.setLocation(mars);
+ x23y4.setSpecies("martian");
+ mars.addBeing(x23y4);
+
+ Alien yy3dk = new Alien();
+ yy3dk.setIdentity("yy3dk&*!!!");
+ yy3dk.setLocation(mars);
+ yy3dk.setSpecies("martian");
+ mars.addBeing(yy3dk);
+
+ Hive hive = new Hive();
+ hive.setLocation(mars);
+ hive.getMembers().add(x23y4);
+ x23y4.setHive(hive);
+ hive.getMembers().add(yy3dk);
+ yy3dk.setHive(hive);
+ s.persist(hive);
+
+ yy3dk.getHivemates().add(x23y4);
+ x23y4.getHivemates().add(yy3dk);
+
+ s.flush();
+ s.clear();
+
+ hive = (Hive) s.createQuery("from Hive h").uniqueResult();
+ assertFalse( Hibernate.isInitialized( hive.getMembers() ) );
+ assertEquals( hive.getMembers().size(), 2 );
+
+ s.clear();
+
+ hive = (Hive) s.createQuery("from Hive h left join fetch h.location left join fetch h.members").uniqueResult();
+ assertTrue( Hibernate.isInitialized( hive.getMembers() ) );
+ assertEquals( hive.getMembers().size(), 2 );
+
+ s.clear();
+
+ x23y4 = (Alien) s.createQuery("from Alien a left join fetch a.hivemates where a.identity like 'x%'").uniqueResult();
+ assertTrue( Hibernate.isInitialized( x23y4.getHivemates() ) );
+ assertEquals( x23y4.getHivemates().size(), 1 );
+
+ s.clear();
+
+ x23y4 = (Alien) s.createQuery("from Alien a where a.identity like 'x%'").uniqueResult();
+ assertFalse( Hibernate.isInitialized( x23y4.getHivemates() ) );
+ assertEquals( x23y4.getHivemates().size(), 1 );
+
+ s.clear();
+
+ x23y4 = (Alien) s.createCriteria(Alien.class).addOrder( Order.asc("identity") ).list().get(0);
+ s.delete( x23y4.getHive() );
+ s.delete( s.get(Location.class, new Long( mel.getId() ) ) );
+ s.delete( s.get(Location.class, new Long( mars.getId() ) ) );
+ assertTrue( s.createQuery("from Being").list().isEmpty() );
+ t.commit();
+ s.close();
+ }
+
+ public void testUnionSubclassManyToOne() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Location mel = new Location("Melbourne, Australia");
+ Location mars = new Location("Mars");
+ s.save(mel);
+ s.save(mars);
+
+ Human gavin = new Human();
+ gavin.setIdentity("gavin");
+ gavin.setSex('M');
+ gavin.setLocation(mel);
+ mel.addBeing(gavin);
+
+ Alien x23y4 = new Alien();
+ x23y4.setIdentity("x23y4$$hu%3");
+ x23y4.setLocation(mars);
+ x23y4.setSpecies("martian");
+ mars.addBeing(x23y4);
+
+ Hive hive = new Hive();
+ hive.setLocation(mars);
+ hive.getMembers().add(x23y4);
+ x23y4.setHive(hive);
+ s.persist(hive);
+
+ Thing thing = new Thing();
+ thing.setDescription("some thing");
+ thing.setOwner(gavin);
+ gavin.getThings().add(thing);
+ s.save(thing);
+ s.flush();
+
+ s.clear();
+
+ thing = (Thing) s.createQuery("from Thing t left join fetch t.owner").uniqueResult();
+ assertTrue( Hibernate.isInitialized( thing.getOwner() ) );
+ assertEquals( thing.getOwner().getIdentity(), "gavin" );
+ s.clear();
+
+ thing = (Thing) s.createQuery("select t from Thing t left join t.owner where t.owner.identity='gavin'").uniqueResult();
+ assertFalse( Hibernate.isInitialized( thing.getOwner() ) );
+ assertEquals( thing.getOwner().getIdentity(), "gavin" );
+ s.clear();
+
+ gavin = (Human) s.createQuery("from Human h left join fetch h.things").uniqueResult();
+ assertTrue( Hibernate.isInitialized( gavin.getThings() ) );
+ assertEquals( ( (Thing) gavin.getThings().get(0) ).getDescription(), "some thing" );
+ s.clear();
+
+ assertTrue( s.createQuery("from Being b left join fetch b.things").list().size()==2 );
+ s.clear();
+
+ gavin = (Human) s.createQuery("from Being b join fetch b.things").uniqueResult();
+ assertTrue( Hibernate.isInitialized( gavin.getThings() ) );
+ assertEquals( ( (Thing) gavin.getThings().get(0) ).getDescription(), "some thing" );
+ s.clear();
+
+ gavin = (Human) s.createQuery("select h from Human h join h.things t where t.description='some thing'").uniqueResult();
+ assertFalse( Hibernate.isInitialized( gavin.getThings() ) );
+ assertEquals( ( (Thing) gavin.getThings().get(0) ).getDescription(), "some thing" );
+ s.clear();
+
+ gavin = (Human) s.createQuery("select b from Being b join b.things t where t.description='some thing'").uniqueResult();
+ assertFalse( Hibernate.isInitialized( gavin.getThings() ) );
+ assertEquals( ( (Thing) gavin.getThings().get(0) ).getDescription(), "some thing" );
+ s.clear();
+
+ thing = (Thing) s.get( Thing.class, new Long( thing.getId() ) );
+ assertFalse( Hibernate.isInitialized( thing.getOwner() ) );
+ assertEquals( thing.getOwner().getIdentity(), "gavin" );
+
+ thing.getOwner().getThings().remove(thing);
+ thing.setOwner(x23y4);
+ x23y4.getThings().add(thing);
+
+ s.flush();
+
+ s.clear();
+
+ thing = (Thing) s.get( Thing.class, new Long( thing.getId() ) );
+ assertFalse( Hibernate.isInitialized( thing.getOwner() ) );
+ assertEquals( thing.getOwner().getIdentity(), "x23y4$$hu%3" );
+
+ s.delete(thing);
+ x23y4 = (Alien) s.createCriteria(Alien.class).uniqueResult();
+ s.delete( x23y4.getHive() );
+ s.delete( s.get(Location.class, new Long( mel.getId() ) ) );
+ s.delete( s.get(Location.class, new Long( mars.getId() ) ) );
+ assertTrue( s.createQuery("from Being").list().isEmpty() );
+ t.commit();
+ s.close();
+ }
+
+ public void testUnionSubclass() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Location mel = new Location("Melbourne, Australia");
+ Location atl = new Location("Atlanta, GA");
+ Location mars = new Location("Mars");
+ s.save(mel);
+ s.save(atl);
+ s.save(mars);
+
+ Human gavin = new Human();
+ gavin.setIdentity("gavin");
+ gavin.setSex('M');
+ gavin.setLocation(mel);
+ mel.addBeing(gavin);
+
+ Alien x23y4 = new Alien();
+ x23y4.setIdentity("x23y4$$hu%3");
+ x23y4.setLocation(mars);
+ x23y4.setSpecies("martian");
+ mars.addBeing(x23y4);
+
+ Hive hive = new Hive();
+ hive.setLocation(mars);
+ hive.getMembers().add(x23y4);
+ x23y4.setHive(hive);
+ s.persist(hive);
+
+ assertEquals( s.createQuery("from Being").list().size(), 2 );
+ assertEquals( s.createQuery("from Being b where b.class = Alien").list().size(), 1 );
+ assertEquals( s.createQuery("from Alien").list().size(), 1 );
+ s.clear();
+
+ List beings = s.createQuery("from Being b left join fetch b.location").list();
+ for ( Iterator iter = beings.iterator(); iter.hasNext(); ) {
+ Being b = (Being) iter.next();
+ assertTrue( Hibernate.isInitialized( b.getLocation() ) );
+ assertNotNull( b.getLocation().getName() );
+ assertNotNull( b.getIdentity() );
+ assertNotNull( b.getSpecies() );
+ }
+ assertEquals( beings.size(), 2 );
+ s.clear();
+
+ beings = s.createQuery("from Being").list();
+ for ( Iterator iter = beings.iterator(); iter.hasNext(); ) {
+ Being b = (Being) iter.next();
+ assertFalse( Hibernate.isInitialized( b.getLocation() ) );
+ assertNotNull( b.getLocation().getName() );
+ assertNotNull( b.getIdentity() );
+ assertNotNull( b.getSpecies() );
+ }
+ assertEquals( beings.size(), 2 );
+ s.clear();
+
+ List locations = s.createQuery("from Location").list();
+ int count = 0;
+ for ( Iterator iter = locations.iterator(); iter.hasNext(); ) {
+ Location l = (Location) iter.next();
+ assertNotNull( l.getName() );
+ Iterator iter2 = l.getBeings().iterator();
+ while ( iter2.hasNext() ) {
+ count++;
+ assertSame( ( (Being) iter2.next() ).getLocation(), l );
+ }
+ }
+ assertEquals(count, 2);
+ assertEquals( locations.size(), 3 );
+ s.clear();
+
+ locations = s.createQuery("from Location loc left join fetch loc.beings").list();
+ count = 0;
+ for ( Iterator iter = locations.iterator(); iter.hasNext(); ) {
+ Location l = (Location) iter.next();
+ assertNotNull( l.getName() );
+ Iterator iter2 = l.getBeings().iterator();
+ while ( iter2.hasNext() ) {
+ count++;
+ assertSame( ( (Being) iter2.next() ).getLocation(), l );
+ }
+ }
+ assertEquals(count, 2);
+ assertEquals( locations.size(), 3 );
+ s.clear();
+
+ gavin = (Human) s.get( Human.class, new Long( gavin.getId() ) );
+ atl = (Location) s.get( Location.class, new Long( atl.getId() ) );
+
+ atl.addBeing(gavin);
+ assertEquals( s.createQuery("from Human h where h.location.name like '%GA'").list().size(), 1 );
+ s.delete(gavin);
+ x23y4 = (Alien) s.createCriteria(Alien.class).uniqueResult();
+ s.delete( x23y4.getHive() );
+ assertTrue( s.createQuery("from Being").list().isEmpty() );
+
+ s.createQuery("delete from Location").executeUpdate();
+ t.commit();
+ s.close();
+ }
+
+ public void testNestedUnionedSubclasses() throws Exception {
+ Session s;
+ Transaction tx;
+ s = openSession();
+ tx = s.beginTransaction();
+ Location mel = new Location("Earth");
+ Human marcf = new Human();
+ marcf.setIdentity("marc");
+ marcf.setSex('M');
+ mel.addBeing(marcf);
+ Employee steve = new Employee();
+ steve.setIdentity("steve");
+ steve.setSex('M');
+ steve.setSalary( new Double(0) );
+ mel.addBeing(steve);
+ s.persist(mel);
+ tx.commit();
+ s.close();
+ s = openSession();
+ tx = s.beginTransaction();
+ Query q = s.createQuery( "from Being h where h.identity = :name1 or h.identity = :name2" );
+ q.setString("name1", "marc");
+ q.setString("name2", "steve");
+ final List result = q.list();
+ assertEquals( 2, result.size() );
+ s.delete( result.get(0) );
+ s.delete( result.get(1) );
+ s.delete( ( (Human) result.get(0) ).getLocation() );
+ tx.commit();
+ s.close();
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass/UnionSubclassTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass2/Address.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass2/Address.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass2/Address.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,11 @@
+//$Id: Address.java 7192 2005-06-18 14:40:15Z oneovthafew $
+package org.hibernate.test.unionsubclass2;
+
+/**
+ * @author Gavin King
+ */
+public class Address {
+ public String address;
+ public String zip;
+ public String country;
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass2/Address.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass2/Customer.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass2/Customer.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass2/Customer.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,35 @@
+//$Id: Customer.java 7192 2005-06-18 14:40:15Z oneovthafew $
+package org.hibernate.test.unionsubclass2;
+
+/**
+ * @author Gavin King
+ */
+public class Customer extends Person {
+ private Employee salesperson;
+ private String comments;
+
+ /**
+ * @return Returns the salesperson.
+ */
+ public Employee getSalesperson() {
+ return salesperson;
+ }
+ /**
+ * @param salesperson The salesperson to set.
+ */
+ public void setSalesperson(Employee salesperson) {
+ this.salesperson = salesperson;
+ }
+ /**
+ * @return Returns the comments.
+ */
+ public String getComments() {
+ return comments;
+ }
+ /**
+ * @param comments The comments to set.
+ */
+ public void setComments(String comments) {
+ this.comments = comments;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass2/Customer.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass2/Employee.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass2/Employee.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass2/Employee.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,49 @@
+//$Id: Employee.java 7192 2005-06-18 14:40:15Z oneovthafew $
+package org.hibernate.test.unionsubclass2;
+
+import java.math.BigDecimal;
+
+/**
+ * @author Gavin King
+ */
+public class Employee extends Person {
+ private String title;
+ private BigDecimal salary;
+ private Employee manager;
+ /**
+ * @return Returns the title.
+ */
+ public String getTitle() {
+ return title;
+ }
+ /**
+ * @param title The title to set.
+ */
+ public void setTitle(String title) {
+ this.title = title;
+ }
+ /**
+ * @return Returns the manager.
+ */
+ public Employee getManager() {
+ return manager;
+ }
+ /**
+ * @param manager The manager to set.
+ */
+ public void setManager(Employee manager) {
+ this.manager = manager;
+ }
+ /**
+ * @return Returns the salary.
+ */
+ public BigDecimal getSalary() {
+ return salary;
+ }
+ /**
+ * @param salary The salary to set.
+ */
+ public void setSalary(BigDecimal salary) {
+ this.salary = salary;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass2/Employee.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass2/Person.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass2/Person.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass2/Person.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,61 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+
+ This mapping demonstrates
+
+ (1) a table-per-subclass mapping strategy
+
+ (2) a simple component mapping
+
+ (3) recursive associations withing an inheritance tree
+
+-->
+
+<hibernate-mapping
+ package="org.hibernate.test.unionsubclass2"
+ default-access="field">
+
+ <class name="Person" table="UPerson">
+
+ <id name="id"
+ column="person_id"
+ unsaved-value="0">
+ <generator class="increment"/>
+ </id>
+
+ <property name="name"
+ unique-key="NameUniqueKey"
+ not-null="true"
+ length="80"/>
+ <property name="sex"
+ not-null="true"
+ update="false"/>
+
+ <component name="address">
+ <property name="address" index="AddressIndex"/>
+ <property name="zip" index="AddressIndex"/>
+ <property name="country"/>
+ </component>
+
+ <union-subclass name="Employee" table="UEmployee">
+ <property name="title"
+ not-null="true"
+ length="20"/>
+ <property name="salary"
+ length="0"/>
+ <many-to-one name="manager"/>
+ </union-subclass>
+
+ <union-subclass name="Customer" table="UCustomer">
+ <property name="comments"/>
+ <many-to-one name="salesperson"/>
+ </union-subclass>
+
+ </class>
+
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass2/Person.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass2/Person.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass2/Person.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass2/Person.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,70 @@
+//$Id: Person.java 7192 2005-06-18 14:40:15Z oneovthafew $
+package org.hibernate.test.unionsubclass2;
+
+
+/**
+ * @author Gavin King
+ */
+public class Person {
+ private long id;
+ private String name;
+ private char sex;
+ private Address address = new Address();
+ /**
+ * @return Returns the address.
+ */
+ public Address getAddress() {
+ return address;
+ }
+
+ public void setAddress(String string) {
+ this.address.address = string;
+ }
+
+ public void setZip(String string) {
+ this.address.zip = string;
+ }
+
+ public void setCountry(String string) {
+ this.address.country = string;
+ }
+
+
+ /**
+ * @return Returns the sex.
+ */
+ public char getSex() {
+ return sex;
+ }
+ /**
+ * @param sex The sex to set.
+ */
+ public void setSex(char sex) {
+ this.sex = sex;
+ }
+ /**
+ * @return Returns the id.
+ */
+ public long getId() {
+ return id;
+ }
+ /**
+ * @param id The id to set.
+ */
+ public void setId(long id) {
+ this.id = id;
+ }
+ /**
+ * @return Returns the identity.
+ */
+ public String getName() {
+ return name;
+ }
+ /**
+ * @param identity The identity to set.
+ */
+ public void setName(String identity) {
+ this.name = identity;
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass2/Person.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass2/UnionSubclassTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass2/UnionSubclassTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass2/UnionSubclassTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,153 @@
+//$Id: UnionSubclassTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.unionsubclass2;
+
+import java.math.BigDecimal;
+import java.util.Iterator;
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.criterion.Expression;
+import org.hibernate.criterion.Property;
+import org.hibernate.dialect.HSQLDialect;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gavin King
+ */
+public class UnionSubclassTest extends FunctionalTestCase {
+
+ public UnionSubclassTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "unionsubclass2/Person.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( UnionSubclassTest.class );
+ }
+
+ public void testUnionSubclass() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ Employee mark = new Employee();
+ mark.setName("Mark");
+ mark.setTitle("internal sales");
+ mark.setSex('M');
+ mark.setAddress("buckhead");
+ mark.setZip("30305");
+ mark.setCountry("USA");
+
+ Customer joe = new Customer();
+ joe.setName("Joe");
+ joe.setAddress("San Francisco");
+ joe.setZip("XXXXX");
+ joe.setCountry("USA");
+ joe.setComments("Very demanding");
+ joe.setSex('M');
+ joe.setSalesperson(mark);
+
+ Person yomomma = new Person();
+ yomomma.setName("mum");
+ yomomma.setSex('F');
+
+ s.save(yomomma);
+ s.save(mark);
+ s.save(joe);
+
+ assertEquals( s.createQuery("from java.io.Serializable").list().size(), 0 );
+
+ assertEquals( s.createQuery("from Person").list().size(), 3 );
+ assertEquals( s.createQuery("from Person p where p.class = Customer").list().size(), 1 );
+ assertEquals( s.createQuery("from Person p where p.class = Person").list().size(), 1 );
+ s.clear();
+
+ List customers = s.createQuery("from Customer c left join fetch c.salesperson").list();
+ for ( Iterator iter = customers.iterator(); iter.hasNext(); ) {
+ Customer c = (Customer) iter.next();
+ assertTrue( Hibernate.isInitialized( c.getSalesperson() ) );
+ assertEquals( c.getSalesperson().getName(), "Mark" );
+ }
+ assertEquals( customers.size(), 1 );
+ s.clear();
+
+ customers = s.createQuery("from Customer").list();
+ for ( Iterator iter = customers.iterator(); iter.hasNext(); ) {
+ Customer c = (Customer) iter.next();
+ assertFalse( Hibernate.isInitialized( c.getSalesperson() ) );
+ assertEquals( c.getSalesperson().getName(), "Mark" );
+ }
+ assertEquals( customers.size(), 1 );
+ s.clear();
+
+
+ mark = (Employee) s.get( Employee.class, new Long( mark.getId() ) );
+ joe = (Customer) s.get( Customer.class, new Long( joe.getId() ) );
+
+ mark.setZip("30306");
+ assertEquals( s.createQuery("from Person p where p.address.zip = '30306'").list().size(), 1 );
+
+ if ( supportsRowValueConstructorSyntaxInInList() ) {
+ s.createCriteria(Person.class).add(
+ Expression.in("address", new Address[] { mark.getAddress(), joe.getAddress() } )
+ ).list();
+ }
+
+ s.delete(mark);
+ s.delete(joe);
+ s.delete(yomomma);
+ assertTrue( s.createQuery("from Person").list().isEmpty() );
+ t.commit();
+ s.close();
+ }
+
+ public void testQuerySubclassAttribute() {
+ if ( getDialect() instanceof HSQLDialect ) {
+ return; // TODO : why??
+ }
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Person p = new Person();
+ p.setName("Emmanuel");
+ p.setSex('M');
+ s.persist(p);
+ Employee q = new Employee();
+ q.setName("Steve");
+ q.setSex('M');
+ q.setTitle("Mr");
+ q.setSalary( new BigDecimal(1000) );
+ s.persist(q);
+
+ List result = s.createQuery("from Person where salary > 100").list();
+ assertEquals( result.size(), 1 );
+ assertSame( result.get(0), q );
+
+ result = s.createQuery("from Person where salary > 100 or name like 'E%'").list();
+ assertEquals( result.size(), 2 );
+
+ result = s.createCriteria(Person.class)
+ .add( Property.forName("salary").gt( new BigDecimal(100) ) )
+ .list();
+ assertEquals( result.size(), 1 );
+ assertSame( result.get(0), q );
+
+ result = s.createQuery("select salary from Person where salary > 100").list();
+ assertEquals( result.size(), 1 );
+ assertEquals( ( (BigDecimal) result.get(0) ).intValue(), 1000 );
+
+ s.delete(p);
+ s.delete(q);
+ t.commit();
+ s.close();
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/unionsubclass2/UnionSubclassTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/UserCollectionTypeSuite.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/UserCollectionTypeSuite.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/UserCollectionTypeSuite.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,21 @@
+package org.hibernate.test.usercollection;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.hibernate.test.usercollection.basic.UserCollectionTypeTest;
+import org.hibernate.test.usercollection.parameterized.ParameterizedUserCollectionTypeTest;
+
+/**
+ * Suite for testing various aspects of user collection types.
+ *
+ * @author Steve Ebersole
+ */
+public class UserCollectionTypeSuite {
+ public static Test suite() {
+ TestSuite suite = new TestSuite( "user collection type tests" );
+ suite.addTest( UserCollectionTypeTest.suite() );
+ suite.addTest( ParameterizedUserCollectionTypeTest.suite() );
+ return suite;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/basic/Email.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/basic/Email.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/basic/Email.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,39 @@
+//$Id$
+package org.hibernate.test.usercollection.basic;
+
+/**
+ * @author Gavin King
+ */
+public class Email {
+
+ private Long id;
+ private String address;
+
+ Email() {}
+
+ public String getAddress() {
+ return address;
+ }
+ public void setAddress(String type) {
+ this.address = type;
+ }
+ public Email(String type) {
+ this.address = type;
+ }
+ public boolean equals(Object that) {
+ if ( !(that instanceof Email) ) return false;
+ Email p = (Email) that;
+ return this.address.equals(p.address);
+ }
+ public int hashCode() {
+ return address.hashCode();
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ private void setId(Long id) {
+ this.id = id;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/basic/IMyList.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/basic/IMyList.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/basic/IMyList.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,7 @@
+package org.hibernate.test.usercollection.basic;
+
+import java.util.List;
+
+public interface IMyList extends List {
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/basic/MyList.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/basic/MyList.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/basic/MyList.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,14 @@
+package org.hibernate.test.usercollection.basic;
+
+import java.util.ArrayList;
+
+/**
+ * A custom collection class. We extend a java.util.Collection class, but that is not required.
+ * It could be totally non-java-collection type, but then we would need to implement all the PersistentCollection methods.
+ *
+ * @author max
+ *
+ */
+public class MyList extends ArrayList implements IMyList {
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/basic/MyListType.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/basic/MyListType.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/basic/MyListType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,60 @@
+package org.hibernate.test.usercollection.basic;
+
+import java.util.Iterator;
+import java.util.Map;
+
+import org.hibernate.EntityMode;
+import org.hibernate.HibernateException;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.usertype.UserCollectionType;
+
+public class MyListType implements UserCollectionType {
+
+ static int lastInstantiationRequest = -2;
+
+ public PersistentCollection instantiate(SessionImplementor session, CollectionPersister persister) throws HibernateException {
+ return new PersistentMyList(session);
+ }
+
+ public PersistentCollection wrap(SessionImplementor session, Object collection) {
+ if ( session.getEntityMode()==EntityMode.DOM4J ) {
+ throw new IllegalStateException("dom4j not supported");
+ }
+ else {
+ return new PersistentMyList( session, (IMyList) collection );
+ }
+ }
+
+ public Iterator getElementsIterator(Object collection) {
+ return ( (IMyList) collection ).iterator();
+ }
+
+ public boolean contains(Object collection, Object entity) {
+ return ( (IMyList) collection ).contains(entity);
+ }
+
+ public Object indexOf(Object collection, Object entity) {
+ int l = ( (IMyList) collection ).indexOf(entity);
+ if(l<0) {
+ return null;
+ } else {
+ return new Integer(l);
+ }
+ }
+
+ public Object replaceElements(Object original, Object target, CollectionPersister persister, Object owner, Map copyCache, SessionImplementor session) throws HibernateException {
+ IMyList result = (IMyList) target;
+ result.clear();
+ result.addAll((MyList)original);
+ return result;
+ }
+
+ public Object instantiate(int anticipatedSize) {
+ lastInstantiationRequest = anticipatedSize;
+ return new MyList();
+ }
+
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/basic/PersistentMyList.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/basic/PersistentMyList.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/basic/PersistentMyList.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,18 @@
+package org.hibernate.test.usercollection.basic;
+
+import org.hibernate.collection.PersistentList;
+import org.hibernate.engine.SessionImplementor;
+
+public class PersistentMyList extends PersistentList implements IMyList {
+
+ public PersistentMyList(SessionImplementor session) {
+ super(session);
+ }
+
+ public PersistentMyList(SessionImplementor session, IMyList list) {
+ super(session, list);
+ }
+
+
+
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/basic/User.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/basic/User.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/basic/User.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,39 @@
+//$Id$
+package org.hibernate.test.usercollection.basic;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Gavin King
+ */
+public class User {
+ private String userName;
+ private IMyList emailAddresses = new MyList();
+ private Map sessionData = new HashMap();
+
+ User() {}
+ public User(String name) {
+ userName = name;
+ }
+
+ public String getUserName() {
+ return userName;
+ }
+ public void setUserName(String userName) {
+ this.userName = userName;
+ }
+ public List getEmailAddresses() {
+ return emailAddresses;
+ }
+ public void setEmailAddresses(IMyList emailAddresses) {
+ this.emailAddresses = emailAddresses;
+ }
+ public Map getSessionData() {
+ return sessionData;
+ }
+ public void setSessionData(Map sessionData) {
+ this.sessionData = sessionData;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/basic/UserCollectionTypeTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/basic/UserCollectionTypeTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/basic/UserCollectionTypeTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,63 @@
+//$Id $
+package org.hibernate.test.usercollection.basic;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Max Rydahl Andersen
+ */
+public class UserCollectionTypeTest extends FunctionalTestCase {
+
+ public UserCollectionTypeTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "usercollection/basic/UserPermissions.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( UserCollectionTypeTest.class );
+ }
+
+ public void testBasicOperation() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ User u = new User("max");
+ u.getEmailAddresses().add( new Email("max at hibernate.org") );
+ u.getEmailAddresses().add( new Email("max.andersen at jboss.com") );
+ s.persist(u);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ User u2 = (User) s.createCriteria(User.class).uniqueResult();
+ assertTrue( Hibernate.isInitialized( u2.getEmailAddresses() ) );
+ assertEquals( u2.getEmailAddresses().size(), 2 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ u2 = ( User ) s.get( User.class, u.getUserName() );
+ u2.getEmailAddresses().size();
+ assertEquals( 2, MyListType.lastInstantiationRequest );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.delete( u );
+ t.commit();
+ s.close();
+ }
+
+}
+
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/basic/UserPermissions.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/basic/UserPermissions.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/basic/UserPermissions.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+ This mapping is a basic example of how to write a UserCollectionType.
+-->
+
+<hibernate-mapping package="org.hibernate.test.usercollection.basic">
+
+ <import class="Permission"/>
+
+ <class name="User" table="UC_BSC_USER">
+ <id name="userName"/>
+ <list name="emailAddresses" fetch="join" cascade="all, delete-orphan" collection-type="org.hibernate.test.usercollection.basic.MyListType">
+ <key column="userName"/>
+ <list-index column="displayOrder" base="1"/>
+ <one-to-many class="Email"/>
+ </list>
+ </class>
+
+ <class name="Email">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="address"/>
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/parameterized/DefaultableList.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/parameterized/DefaultableList.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/parameterized/DefaultableList.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,13 @@
+package org.hibernate.test.usercollection.parameterized;
+
+import java.util.List;
+
+/**
+ * Our specialized collection contract
+ *
+ * @author Holger Brands
+ * @author Steve Ebersole
+ */
+public interface DefaultableList extends List {
+ public String getDefaultValue();
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/parameterized/DefaultableListImpl.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/parameterized/DefaultableListImpl.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/parameterized/DefaultableListImpl.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,28 @@
+package org.hibernate.test.usercollection.parameterized;
+
+import java.util.ArrayList;
+
+/**
+ * Implementation of our specialized collection contract
+ *
+ * @author Holger Brands
+ * @author Steve Ebersole
+ */
+public class DefaultableListImpl extends ArrayList implements DefaultableList {
+ private String defaultValue;
+
+ public DefaultableListImpl() {
+ }
+
+ public DefaultableListImpl(int anticipatedSize) {
+ super( anticipatedSize + ( int ) Math.ceil( anticipatedSize * .75f ) );
+ }
+
+ public String getDefaultValue() {
+ return defaultValue;
+ }
+
+ public void setDefaultValue(String defaultValue) {
+ this.defaultValue = defaultValue;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/parameterized/DefaultableListType.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/parameterized/DefaultableListType.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/parameterized/DefaultableListType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,75 @@
+package org.hibernate.test.usercollection.parameterized;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Properties;
+import java.util.List;
+
+import org.hibernate.usertype.UserCollectionType;
+import org.hibernate.usertype.ParameterizedType;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.EntityMode;
+
+/**
+ * Our Hibernate type-system extension for defining our specialized collection
+ * contract.
+ *
+ * @author Holger Brands
+ * @author Steve Ebersole
+ */
+public class DefaultableListType implements UserCollectionType, ParameterizedType {
+ private String defaultValue;
+
+ public Object instantiate(int anticipatedSize) {
+ DefaultableListImpl list = anticipatedSize < 0 ? new DefaultableListImpl() : new DefaultableListImpl( anticipatedSize );
+ list.setDefaultValue( defaultValue );
+ return list;
+ }
+
+ public PersistentCollection instantiate(
+ SessionImplementor session,
+ CollectionPersister persister) {
+ return new PersistentDefaultableList( session );
+ }
+
+ public PersistentCollection wrap(SessionImplementor session, Object collection) {
+ if ( session.getEntityMode() == EntityMode.DOM4J ) {
+ throw new IllegalStateException( "dom4j not supported" );
+ }
+ else {
+ return new PersistentDefaultableList( session, ( List ) collection );
+ }
+ }
+
+ public Iterator getElementsIterator(Object collection) {
+ return ( ( DefaultableList ) collection ).iterator();
+ }
+
+ public boolean contains(Object collection, Object entity) {
+ return ( ( DefaultableList ) collection ).contains( entity );
+ }
+
+ public Object indexOf(Object collection, Object entity) {
+ int index = ( ( DefaultableList ) collection ).indexOf( entity );
+ return index >= 0 ? new Integer( index ) : null;
+ }
+
+ public Object replaceElements(
+ Object original,
+ Object target,
+ CollectionPersister persister,
+ Object owner,
+ Map copyCache,
+ SessionImplementor session) {
+ DefaultableList result = ( DefaultableList ) target;
+ result.clear();
+ result.addAll( ( DefaultableList ) original );
+ return result;
+ }
+
+ public void setParameterValues(Properties parameters) {
+ defaultValue = parameters.getProperty( "default" );
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/parameterized/Entity.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/parameterized/Entity.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/parameterized/Entity.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,33 @@
+package org.hibernate.test.usercollection.parameterized;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * Our test entity
+ *
+ * @author Steve Ebersole
+ */
+public class Entity {
+ private String name;
+ private List values = new ArrayList();
+
+ public Entity() {
+ }
+
+ public Entity(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public List getValues() {
+ return values;
+ }
+
+ public void setValues(List values) {
+ this.values = values;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/parameterized/Mapping.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/parameterized/Mapping.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/parameterized/Mapping.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.usercollection.parameterized" default-access="field">
+
+ <typedef name="DefaultableList" class="org.hibernate.test.usercollection.parameterized.DefaultableListType">
+ <param name="default">Hello</param>
+ </typedef>
+
+ <class name="Entity">
+ <id name="name" type="string"/>
+ <list name="values" fetch="join" table="ENT_VAL" collection-type="DefaultableList">
+ <key column="ENT_ID"/>
+ <list-index column="POS"/>
+ <element type="string" column="VAL"/>
+ </list>
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/parameterized/ParameterizedUserCollectionTypeTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/parameterized/ParameterizedUserCollectionTypeTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/parameterized/ParameterizedUserCollectionTypeTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,49 @@
+package org.hibernate.test.usercollection.parameterized;
+
+import junit.framework.Test;
+
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.Hibernate;
+
+/**
+ * Tes for parameterized user collection types.
+ *
+ * @author Holger Brands
+ * @author Steve Ebersole
+ */
+public class ParameterizedUserCollectionTypeTest extends FunctionalTestCase {
+ public ParameterizedUserCollectionTypeTest(String string) {
+ super( string );
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( ParameterizedUserCollectionTypeTest.class );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "usercollection/parameterized/Mapping.hbm.xml" };
+ }
+
+ public void testBasicOperation() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Entity entity = new Entity( "tester" );
+ entity.getValues().add( "value-1" );
+ s.persist( entity );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ entity = ( Entity ) s.get( Entity.class, "tester" );
+ assertTrue( Hibernate.isInitialized( entity.getValues() ) );
+ assertEquals( 1, entity.getValues().size() );
+ assertEquals( "Hello", ( ( DefaultableList ) entity.getValues() ).getDefaultValue() );
+ s.delete( entity );
+ t.commit();
+ s.close();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/parameterized/PersistentDefaultableList.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/parameterized/PersistentDefaultableList.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/usercollection/parameterized/PersistentDefaultableList.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,29 @@
+package org.hibernate.test.usercollection.parameterized;
+
+import java.util.List;
+
+import org.hibernate.collection.PersistentList;
+import org.hibernate.engine.SessionImplementor;
+
+/**
+ * The "persistent wrapper" around our specialized collection contract
+ *
+ * @author Holger Brands
+ * @author Steve Ebersole
+ */
+public class PersistentDefaultableList extends PersistentList implements DefaultableList {
+ public PersistentDefaultableList(SessionImplementor session) {
+ super( session );
+ }
+
+ public PersistentDefaultableList(SessionImplementor session, List list) {
+ super( session, list );
+ }
+
+ public PersistentDefaultableList() {
+ }
+
+ public String getDefaultValue() {
+ return ( ( DefaultableList ) this.list ).getDefaultValue();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/util/PropertiesHelperTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/util/PropertiesHelperTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/util/PropertiesHelperTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,109 @@
+package org.hibernate.test.util;
+
+import java.util.Properties;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.hibernate.junit.UnitTestCase;
+import org.hibernate.util.PropertiesHelper;
+
+/**
+ * @author Steve Ebersole
+ */
+public class PropertiesHelperTest extends UnitTestCase {
+
+ private Properties props;
+
+ public PropertiesHelperTest(String string) {
+ super( string );
+ }
+
+ public static Test suite() {
+ return new TestSuite( PropertiesHelperTest.class );
+ }
+
+ protected void setUp() throws Exception {
+ props = new Properties();
+
+ props.setProperty( "my.nonexistent.prop", "${}" );
+
+ props.setProperty( "my.string.prop", "${test.my.sys.string.prop}" );
+ System.setProperty( "test.my.sys.string.prop", "string" );
+
+ props.setProperty( "my.boolean.prop", "${test.my.sys.boolean.prop}" );
+ System.setProperty( "test.my.sys.boolean.prop", "true" );
+
+ props.setProperty( "my.int.prop", "${test.my.sys.int.prop}" );
+ System.setProperty( "test.my.sys.int.prop", "1" );
+
+ props.setProperty( "my.integer.prop", "${test.my.sys.integer.prop}" );
+ System.setProperty( "test.my.sys.integer.prop", "1" );
+
+ props.setProperty( "partial.prop1", "${somedir}/middle/dir/${somefile}" );
+ props.setProperty( "partial.prop2", "basedir/${somedir}/myfile.txt" );
+ System.setProperty( "somedir", "tmp" );
+ System.setProperty( "somefile", "tmp.txt" );
+
+ props.setProperty( "parse.error", "steve" );
+ }
+
+ public void testPlaceholderReplacement() {
+ PropertiesHelper.resolvePlaceHolders( props );
+
+ String str = PropertiesHelper.getString( "my.nonexistent.prop", props, "did.not.exist" );
+ assertEquals( "did.not.exist", str );
+ str = PropertiesHelper.getString( "my.nonexistent.prop", props, null );
+ assertNull( str );
+ str = PropertiesHelper.getString( "my.string.prop", props, "na" );
+ assertEquals( "replacement did not occur", "string", str );
+ str = PropertiesHelper.getString( "my.string.prop", props, "did.not.exist" );
+ assertEquals( "replacement did not occur", "string", str );
+
+ boolean bool = PropertiesHelper.getBoolean( "my.nonexistent.prop", props );
+ assertFalse( "non-exists as boolean", bool );
+ bool = PropertiesHelper.getBoolean( "my.nonexistent.prop", props, false );
+ assertFalse( "non-exists as boolean", bool );
+ bool = PropertiesHelper.getBoolean( "my.nonexistent.prop", props, true );
+ assertTrue( "non-exists as boolean", bool );
+ bool = PropertiesHelper.getBoolean( "my.boolean.prop", props );
+ assertTrue( "boolean replacement did not occur", bool );
+ bool = PropertiesHelper.getBoolean( "my.boolean.prop", props, false );
+ assertTrue( "boolean replacement did not occur", bool );
+
+ int i = PropertiesHelper.getInt( "my.nonexistent.prop", props, -1 );
+ assertEquals( -1, i );
+ i = PropertiesHelper.getInt( "my.int.prop", props, 100 );
+ assertEquals( 1, i );
+
+ Integer I = PropertiesHelper.getInteger( "my.nonexistent.prop", props );
+ assertNull( I );
+ I = PropertiesHelper.getInteger( "my.integer.prop", props );
+ assertEquals( I, new Integer( 1 ) );
+
+ str = props.getProperty( "partial.prop1" );
+ assertEquals( "partial replacement (ends)", "tmp/middle/dir/tmp.txt", str );
+
+ str = props.getProperty( "partial.prop2" );
+ assertEquals( "partial replacement (midst)", "basedir/tmp/myfile.txt", str );
+ }
+
+ public void testParseExceptions() {
+ boolean b = PropertiesHelper.getBoolean( "parse.error", props );
+ assertFalse( "parse exception case - boolean", b );
+
+ try {
+ PropertiesHelper.getInt( "parse.error", props, 20 );
+ fail( "parse exception case - int" );
+ }
+ catch( NumberFormatException expected ) {
+ }
+
+ try {
+ PropertiesHelper.getInteger( "parse.error", props );
+ fail( "parse exception case - Integer" );
+ }
+ catch( NumberFormatException expected ) {
+ }
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/util/StringHelperTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/util/StringHelperTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/util/StringHelperTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,32 @@
+package org.hibernate.test.util;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.hibernate.junit.UnitTestCase;
+import org.hibernate.util.StringHelper;
+
+/**
+ * @author Steve Ebersole
+ */
+public class StringHelperTest extends UnitTestCase {
+
+ public StringHelperTest(String string) {
+ super( string );
+ }
+
+ public static Test suite() {
+ return new TestSuite( StringHelperTest.class );
+ }
+
+ public void testAliasGeneration() {
+ assertSimpleAlias( "xyz", "xyz_" );
+ assertSimpleAlias( "_xyz", "xyz_" );
+ assertSimpleAlias( "!xyz", "xyz_" );
+ assertSimpleAlias( "abcdefghijklmnopqrstuvwxyz", "abcdefghij_" );
+ }
+
+ private void assertSimpleAlias(String source, String expected) {
+ assertEquals( expected, StringHelper.generateAlias( source ) );
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/util/UtilSuite.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/util/UtilSuite.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/util/UtilSuite.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,20 @@
+package org.hibernate.test.util;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.hibernate.test.util.dtd.EntityResolverTest;
+
+/**
+ * todo: describe UtilSuite
+ *
+ * @author Steve Ebersole
+ */
+public class UtilSuite {
+ public static Test suite() {
+ TestSuite suite = new TestSuite( "Utility package tests" );
+ suite.addTest( PropertiesHelperTest.suite() );
+ suite.addTest( EntityResolverTest.suite() );
+ suite.addTest( StringHelperTest.suite() );
+ return suite;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/util/dtd/Child.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/util/dtd/Child.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/util/dtd/Child.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,36 @@
+package org.hibernate.test.util.dtd;
+
+/**
+ * The Child class.
+ *
+ * @author Steve Ebersole
+ */
+public class Child {
+ private Long id;
+ private int age;
+ private Parent parent;
+
+ public Child() {
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public Parent getParent() {
+ return parent;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public void setAge(int age) {
+ this.age = age;
+ }
+
+
+ /*package*/ void injectParent(Parent parent) {
+ this.parent = parent;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/util/dtd/EntityResolverTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/util/dtd/EntityResolverTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/util/dtd/EntityResolverTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,32 @@
+package org.hibernate.test.util.dtd;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.hibernate.cfg.Configuration;
+import org.hibernate.junit.UnitTestCase;
+
+
+/**
+ * @author Steve Ebersole
+ */
+public class EntityResolverTest extends UnitTestCase {
+
+ public EntityResolverTest(String name) {
+ super( name );
+ }
+
+ public static Test suite() {
+ return new TestSuite( EntityResolverTest.class );
+ }
+
+ public void testEntityIncludeResolution() {
+ // Parent.hbm.xml contains the following entity include:
+ // <!ENTITY child SYSTEM "classpath://org/hibernate/test/util/dtd/child.xml">
+ // which we are expecting the Hibernate custom entity resolver to be able to resolve
+ // locally via classpath lookup.
+ Configuration cfg = new Configuration();
+ cfg.addResource( "org/hibernate/test/util/dtd/Parent.hbm.xml" );
+ cfg.buildMappings();
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/util/dtd/Parent.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/util/dtd/Parent.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/util/dtd/Parent.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" [
+ <!ENTITY child SYSTEM "classpath://org/hibernate/test/util/dtd/child.xml">
+]>
+
+
+<hibernate-mapping package="org.hibernate.test.util.dtd" default-access="field">
+
+ <class name="Parent">
+ <id name="id" column="ID">
+ <generator class="increment"/>
+ </id>
+ <list name="children" inverse="true" cascade="persist,merge">
+ <key column="PRNT_ID" not-null="true"/>
+ <list-index column="SIBLING_ORD"/>
+ <one-to-many class="Child"/>
+ </list>
+ </class>
+
+ &child;
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/util/dtd/Parent.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/util/dtd/Parent.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/util/dtd/Parent.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,33 @@
+package org.hibernate.test.util.dtd;
+
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Iterator;
+
+/**
+ * The Parent class.
+ *
+ * @author Steve Ebersole
+ */
+public class Parent {
+ private Long id;
+ private Set children = new HashSet();
+
+ public Long getId() {
+ return id;
+ }
+
+ public Iterator getChildren() {
+ return children.iterator();
+ }
+
+ public Child newChild() {
+ Child child = new Child();
+ child.setAge( 0 );
+
+ child.injectParent( this );
+ this.children.add( child );
+
+ return child;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/util/dtd/child.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/util/dtd/child.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/util/dtd/child.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,9 @@
+
+
+ <class name="Child">
+ <id name="id" column="ID">
+ <generator class="increment"/>
+ </id>
+ <property name="age" column="AGE" not-null="true"/>
+ <many-to-one name="parent" class="Parent" column="PRNT_ID"/>
+ </class>
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/Person.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/Person.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/Person.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,43 @@
+//$Id: Person.java 7676 2005-07-29 06:27:10Z oneovthafew $
+package org.hibernate.test.version;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Person {
+ private String name;
+ private List things;
+ private List tasks;
+ private int version;
+
+ Person() {}
+ public Person(String name) {
+ this.name = name;
+ this.things = new ArrayList();
+ this.tasks = new ArrayList();
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public List getThings() {
+ return things;
+ }
+ public void setThings(List things) {
+ this.things = things;
+ }
+ public int getVersion() {
+ return version;
+ }
+ public void setVersion(int version) {
+ this.version = version;
+ }
+ public List getTasks() {
+ return tasks;
+ }
+ public void setTasks(List tasks) {
+ this.tasks = tasks;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/Person.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/PersonThing.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/PersonThing.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/PersonThing.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,45 @@
+<?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 how to control the optimistic locking behavior
+ of a collection (do changes to the collection result in
+ a version increment on the owning instance)
+ -->
+<hibernate-mapping
+ package="org.hibernate.test.version">
+
+ <class name="Person">
+ <id name="name"/>
+ <version name="version"/>
+ <bag name="things"
+ inverse="true"
+ cascade="all,delete-orphan">
+ <key column="person"/>
+ <one-to-many class="Thing"/>
+ </bag>
+ <bag name="tasks"
+ inverse="true"
+ cascade="all,delete-orphan"
+ optimistic-lock="false">
+ <key column="person"/>
+ <one-to-many class="Task"/>
+ </bag>
+ </class>
+
+ <class name="Thing">
+ <id name="description"/>
+ <version name="version"/>
+ <property name="longDescription"/>
+ <many-to-one name="person"/>
+ </class>
+
+ <class name="Task">
+ <id name="description"/>
+ <version name="version"/>
+ <many-to-one name="person"/>
+ </class>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/PersonThing.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/Task.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/Task.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/Task.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,33 @@
+//$Id: Task.java 7676 2005-07-29 06:27:10Z oneovthafew $
+package org.hibernate.test.version;
+
+public class Task {
+ private String description;
+ private Person person;
+ private int version;
+
+ public int getVersion() {
+ return version;
+ }
+ public void setVersion(int version) {
+ this.version = version;
+ }
+ Task() {}
+ public Task(String description, Person person) {
+ this.description = description;
+ this.person = person;
+ person.getTasks().add(this);
+ }
+ public String getDescription() {
+ return description;
+ }
+ public void setDescription(String description) {
+ this.description = description;
+ }
+ public Person getPerson() {
+ return person;
+ }
+ public void setPerson(Person person) {
+ this.person = person;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/Task.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/Thing.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/Thing.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/Thing.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,40 @@
+//$Id: Thing.java 7715 2005-08-01 16:46:57Z oneovthafew $
+package org.hibernate.test.version;
+
+public class Thing {
+ private String description;
+ private Person person;
+ private int version;
+ private String longDescription;
+
+ public int getVersion() {
+ return version;
+ }
+ public void setVersion(int version) {
+ this.version = version;
+ }
+ Thing() {}
+ public Thing(String description, Person person) {
+ this.description = description;
+ this.person = person;
+ person.getThings().add(this);
+ }
+ public String getDescription() {
+ return description;
+ }
+ public void setDescription(String description) {
+ this.description = description;
+ }
+ public Person getPerson() {
+ return person;
+ }
+ public void setPerson(Person person) {
+ this.person = person;
+ }
+ public String getLongDescription() {
+ return longDescription;
+ }
+ public void setLongDescription(String longDescription) {
+ this.longDescription = longDescription;
+ }
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/Thing.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/VersionTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/VersionTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/VersionTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,135 @@
+//$Id: VersionTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.version;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Max Rydahl Andersen
+ */
+public class VersionTest extends FunctionalTestCase {
+
+ public VersionTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "version/PersonThing.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( VersionTest.class );
+ }
+
+ public void testVersionShortCircuitFlush() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Person gavin = new Person("Gavin");
+ new Thing("Passport", gavin);
+ s.persist(gavin);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Thing passp = (Thing) s.get(Thing.class, "Passport");
+ passp.setLongDescription("blah blah blah");
+ s.createQuery("from Person").list();
+ s.createQuery("from Person").list();
+ s.createQuery("from Person").list();
+ t.commit();
+ s.close();
+
+ assertEquals( passp.getVersion(), 1 );
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.createQuery("delete from Thing").executeUpdate();
+ s.createQuery("delete from Person").executeUpdate();
+ t.commit();
+ s.close();
+ }
+
+ public void testCollectionVersion() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Person gavin = new Person("Gavin");
+ new Thing("Passport", gavin);
+ s.persist(gavin);
+ t.commit();
+ s.close();
+
+ assertEquals(0, gavin.getVersion());
+
+ s = openSession();
+ t = s.beginTransaction();
+ gavin = (Person) s.createCriteria(Person.class).uniqueResult();
+ new Thing("Laptop", gavin);
+ t.commit();
+ s.close();
+
+ assertEquals(1, gavin.getVersion());
+ assertFalse( Hibernate.isInitialized( gavin.getThings() ) );
+
+ s = openSession();
+ t = s.beginTransaction();
+ gavin = (Person) s.createCriteria(Person.class).uniqueResult();
+ gavin.getThings().clear();
+ t.commit();
+ s.close();
+
+ assertEquals(2, gavin.getVersion());
+ assertTrue( Hibernate.isInitialized( gavin.getThings() ) );
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.delete(gavin);
+ t.commit();
+ s.close();
+ }
+
+ public void testCollectionNoVersion() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Person gavin = new Person("Gavin");
+ new Task("Code", gavin);
+ s.persist(gavin);
+ t.commit();
+ s.close();
+
+ assertEquals(0, gavin.getVersion());
+
+ s = openSession();
+ t = s.beginTransaction();
+ gavin = (Person) s.createCriteria(Person.class).uniqueResult();
+ new Task("Document", gavin);
+ t.commit();
+ s.close();
+
+ assertEquals(0, gavin.getVersion());
+ assertFalse( Hibernate.isInitialized( gavin.getTasks() ) );
+
+ s = openSession();
+ t = s.beginTransaction();
+ gavin = (Person) s.createCriteria(Person.class).uniqueResult();
+ gavin.getTasks().clear();
+ t.commit();
+ s.close();
+
+ assertEquals(0, gavin.getVersion());
+ assertTrue( Hibernate.isInitialized( gavin.getTasks() ) );
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.delete(gavin);
+ t.commit();
+ s.close();
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/VersionTest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/db/DbVersionTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/db/DbVersionTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/db/DbVersionTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,119 @@
+// $Id: DbVersionTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.version.db;
+
+import java.sql.Timestamp;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * Implementation of DbVersionTest.
+ *
+ * @author Steve Ebersole
+ */
+public class DbVersionTest extends FunctionalTestCase {
+ public DbVersionTest(String x) {
+ super( x );
+ }
+
+
+ public String[] getMappings() {
+ return new String[] { "version/db/User.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( DbVersionTest.class );
+ }
+
+ public void testCollectionVersion() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ User steve = new User( "steve" );
+ s.persist( steve );
+ Group admin = new Group( "admin" );
+ s.persist( admin );
+ t.commit();
+ s.close();
+
+ Timestamp steveTimestamp = steve.getTimestamp();
+
+ // For dialects (Oracle8 for example) which do not return "true
+ // timestamps" sleep for a bit to allow the db date-time increment...
+ Thread.sleep( 1500 );
+
+ s = openSession();
+ t = s.beginTransaction();
+ steve = ( User ) s.get( User.class, steve.getId() );
+ admin = ( Group ) s.get( Group.class, admin.getId() );
+ steve.getGroups().add( admin );
+ admin.getUsers().add( steve );
+ t.commit();
+ s.close();
+
+ assertFalse( "owner version not incremented", Hibernate.TIMESTAMP.isEqual( steveTimestamp, steve.getTimestamp() ) );
+
+ steveTimestamp = steve.getTimestamp();
+ Thread.sleep( 1500 );
+
+ s = openSession();
+ t = s.beginTransaction();
+ steve = ( User ) s.get( User.class, steve.getId() );
+ steve.getGroups().clear();
+ t.commit();
+ s.close();
+
+ assertFalse( "owner version not incremented", Hibernate.TIMESTAMP.isEqual( steveTimestamp, steve.getTimestamp() ) );
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.delete( s.load( User.class, steve.getId() ) );
+ s.delete( s.load( Group.class, admin.getId() ) );
+ t.commit();
+ s.close();
+ }
+
+
+ public void testCollectionNoVersion() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ User steve = new User( "steve" );
+ s.persist( steve );
+ Permission perm = new Permission( "silly", "user", "rw" );
+ s.persist( perm );
+ t.commit();
+ s.close();
+
+ Timestamp steveTimestamp = ( Timestamp ) steve.getTimestamp();
+
+ s = openSession();
+ t = s.beginTransaction();
+ steve = ( User ) s.get( User.class, steve.getId() );
+ perm = ( Permission ) s.get( Permission.class, perm.getId() );
+ steve.getPermissions().add( perm );
+ t.commit();
+ s.close();
+
+ assertTrue( "owner version was incremented", Hibernate.TIMESTAMP.isEqual( steveTimestamp, steve.getTimestamp() ) );
+
+ s = openSession();
+ t = s.beginTransaction();
+ steve = ( User ) s.get( User.class, steve.getId() );
+ steve.getPermissions().clear();
+ t.commit();
+ s.close();
+
+ assertTrue( "owner version was incremented", Hibernate.TIMESTAMP.isEqual( steveTimestamp, steve.getTimestamp() ) );
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.delete( s.load( User.class, steve.getId() ) );
+ s.delete( s.load( Permission.class, perm.getId() ) );
+ t.commit();
+ s.close();
+ }
+}
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/db/Group.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/db/Group.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/db/Group.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,56 @@
+// $Id: Group.java 7736 2005-08-03 20:03:34Z steveebersole $
+package org.hibernate.test.version.db;
+
+import java.util.Date;
+import java.util.Set;
+
+/**
+ * Implementation of Group.
+ *
+ * @author Steve Ebersole
+ */
+public class Group {
+ private Long id;
+ private Date timestamp;
+ private String name;
+ private Set users;
+
+ public Group() {
+ }
+
+ public Group(String name) {
+ this.name = name;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public Date getTimestamp() {
+ return timestamp;
+ }
+
+ public void setTimestamp(Date timestamp) {
+ this.timestamp = timestamp;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Set getUsers() {
+ return users;
+ }
+
+ public void setUsers(Set users) {
+ this.users = users;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/db/Permission.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/db/Permission.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/db/Permission.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,66 @@
+// $Id: Permission.java 7736 2005-08-03 20:03:34Z steveebersole $
+package org.hibernate.test.version.db;
+
+import java.util.Date;
+
+/**
+ * Implementation of Permission.
+ *
+ * @author Steve Ebersole
+ */
+public class Permission {
+ private Long id;
+ private Date timestamp;
+ private String name;
+ private String context;
+ private String access;
+
+ public Permission() {
+ }
+
+ public Permission(String name, String context, String access) {
+ this.name = name;
+ this.context = context;
+ this.access = access;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public Date getTimestamp() {
+ return timestamp;
+ }
+
+ public void setTimestamp(Date timestamp) {
+ this.timestamp = timestamp;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getContext() {
+ return context;
+ }
+
+ public void setContext(String context) {
+ this.context = context;
+ }
+
+ public String getAccess() {
+ return access;
+ }
+
+ public void setAccess(String access) {
+ this.access = access;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/db/User.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/db/User.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/db/User.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,55 @@
+<?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 how to control the optimistic locking behavior
+ of a collection (do changes to the collection result in
+ a version increment on the owning instance)
+ -->
+<hibernate-mapping
+ package="org.hibernate.test.version.db">
+
+ <class name="User" table="db_vers_user">
+ <id name="id" column="user_id" type="long">
+ <generator class="native"/>
+ </id>
+ <timestamp name="timestamp" column="ts" source="db"/>
+ <!--<version name="timestamp" column="ts" type="org.hibernate.type.DbTimestampType"/>-->
+ <property name="username" column="user_name" type="string" unique="true"/>
+ <set name="groups" table="db_vers_user_group" batch-size="9" inverse="true" optimistic-lock="true" lazy="true" cascade="none" >
+ <key column="user_id"/>
+ <many-to-many column="group_id" class="Group" lazy="false" fetch="join" />
+ </set>
+ <set name="permissions" table="db_vers_user_perm" batch-size="9" inverse="false" optimistic-lock="false" lazy="true" cascade="none">
+ <key column="user_id"/>
+ <many-to-many column="perm_id" class="Permission" lazy="false" fetch="join"/>
+ </set>
+ </class>
+
+ <class name="Group" table="db_vers_group">
+ <id name="id" column="group_id" type="long">
+ <generator class="native"/>
+ </id>
+ <timestamp name="timestamp" column="ts" source="db"/>
+ <!--<version name="timestamp" column="ts" type="org.hibernate.type.DbTimestampType"/>-->
+ <property name="name" column="name" type="string" unique="true"/>
+ <set name="users" table="db_vers_user_group" batch-size="9" inverse="false" lazy="true" cascade="none" >
+ <key column="group_id"/>
+ <many-to-many column="user_id" class="User" lazy="false" fetch="join" />
+ </set>
+ </class>
+
+ <class name="Permission" table="db_vers_permission">
+ <id name="id" column="perm_id" type="long">
+ <generator class="native"/>
+ </id>
+ <timestamp name="timestamp" column="ts" source="db"/>
+ <!--<version name="timestamp" column="ts" type="org.hibernate.type.DbTimestampType"/>-->
+ <property name="name" column="name" type="string" unique="true"/>
+ <property name="context" column="ctx" type="string"/>
+ <property name="access" column="priv" type="string"/>
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/db/User.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/db/User.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/db/User.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,65 @@
+// $Id: User.java 7736 2005-08-03 20:03:34Z steveebersole $
+package org.hibernate.test.version.db;
+
+import java.util.Set;
+import java.sql.Timestamp;
+
+/**
+ * Implementation of User.
+ *
+ * @author Steve Ebersole
+ */
+public class User {
+ private Long id;
+ private Timestamp timestamp;
+ private String username;
+ private Set groups;
+ private Set permissions;
+
+ public User() {
+ }
+
+ public User(String username) {
+ this.username = username;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public Timestamp getTimestamp() {
+ return timestamp;
+ }
+
+ public void setTimestamp(Timestamp timestamp) {
+ this.timestamp = timestamp;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public Set getGroups() {
+ return groups;
+ }
+
+ public void setGroups(Set groups) {
+ this.groups = groups;
+ }
+
+ public Set getPermissions() {
+ return permissions;
+ }
+
+ public void setPermissions(Set permissions) {
+ this.permissions = permissions;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/sybase/Group.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/sybase/Group.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/sybase/Group.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,56 @@
+// $Id: Group.java 7805 2005-08-10 16:25:11Z steveebersole $
+package org.hibernate.test.version.sybase;
+
+import java.util.Date;
+import java.util.Set;
+
+/**
+ * Implementation of Group.
+ *
+ * @author Steve Ebersole
+ */
+public class Group {
+ private Long id;
+ private Date timestamp;
+ private String name;
+ private Set users;
+
+ public Group() {
+ }
+
+ public Group(String name) {
+ this.name = name;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public Date getTimestamp() {
+ return timestamp;
+ }
+
+ public void setTimestamp(Date timestamp) {
+ this.timestamp = timestamp;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Set getUsers() {
+ return users;
+ }
+
+ public void setUsers(Set users) {
+ this.users = users;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/sybase/Permission.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/sybase/Permission.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/sybase/Permission.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,66 @@
+// $Id: Permission.java 7805 2005-08-10 16:25:11Z steveebersole $
+package org.hibernate.test.version.sybase;
+
+import java.util.Date;
+
+/**
+ * Implementation of Permission.
+ *
+ * @author Steve Ebersole
+ */
+public class Permission {
+ private Long id;
+ private Date timestamp;
+ private String name;
+ private String context;
+ private String access;
+
+ public Permission() {
+ }
+
+ public Permission(String name, String context, String access) {
+ this.name = name;
+ this.context = context;
+ this.access = access;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public Date getTimestamp() {
+ return timestamp;
+ }
+
+ public void setTimestamp(Date timestamp) {
+ this.timestamp = timestamp;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getContext() {
+ return context;
+ }
+
+ public void setContext(String context) {
+ this.context = context;
+ }
+
+ public String getAccess() {
+ return access;
+ }
+
+ public void setAccess(String access) {
+ this.access = access;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/sybase/SybaseTimestampVersioningTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/sybase/SybaseTimestampVersioningTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/sybase/SybaseTimestampVersioningTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,202 @@
+// $Id: SybaseTimestampVersioningTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.version.sybase;
+
+import junit.framework.Test;
+
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.SybaseDialect;
+import org.hibernate.junit.functional.DatabaseSpecificFunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * Implementation of VersionTest.
+ *
+ * @author Steve Ebersole
+ */
+public class SybaseTimestampVersioningTest extends DatabaseSpecificFunctionalTestCase {
+
+ public SybaseTimestampVersioningTest(String x) {
+ super( x );
+ }
+
+ public String[] getMappings() {
+ return new String[] { "version/sybase/User.hbm.xml" };
+ }
+
+ public boolean appliesTo(Dialect dialect) {
+ return dialect instanceof SybaseDialect;
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( SybaseTimestampVersioningTest.class );
+ }
+
+ public void testLocking() throws Throwable {
+ // First, create the needed row...
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ User steve = new User( "steve" );
+ s.persist( steve );
+ t.commit();
+ s.close();
+
+ // next open two sessions, and try to update from each "simultaneously"...
+ Session s1 = null;
+ Session s2 = null;
+ Transaction t1 = null;
+ Transaction t2 = null;
+ try {
+ s1 = getSessions().openSession();
+ t1 = s1.beginTransaction();
+ s2 = getSessions().openSession();
+ t2 = s2.beginTransaction();
+
+ User user1 = ( User ) s1.get( User.class, steve.getId() );
+ User user2 = ( User ) s2.get( User.class, steve.getId() );
+
+ user1.setUsername( "se" );
+ t1.commit();
+ t1 = null;
+
+ user2.setUsername( "steve-e" );
+ try {
+ t2.commit();
+ fail( "optimistic lock check did not fail" );
+ }
+ catch( HibernateException e ) {
+ // expected...
+ try {
+ t2.rollback();
+ }
+ catch( Throwable ignore ) {
+ }
+ }
+ }
+ catch( Throwable error ) {
+ if ( t1 != null ) {
+ try {
+ t1.rollback();
+ }
+ catch( Throwable ignore ) {
+ }
+ }
+ if ( t2 != null ) {
+ try {
+ t2.rollback();
+ }
+ catch( Throwable ignore ) {
+ }
+ }
+ throw error;
+ }
+ finally {
+ if ( s1 != null ) {
+ try {
+ s1.close();
+ }
+ catch( Throwable ignore ) {
+ }
+ }
+ if ( s2 != null ) {
+ try {
+ s2.close();
+ }
+ catch( Throwable ignore ) {
+ }
+ }
+ }
+
+ // lastly, clean up...
+ s = openSession();
+ t = s.beginTransaction();
+ s.delete( s.load( User.class, steve.getId() ) );
+ t.commit();
+ s.close();
+ }
+
+ public void testCollectionVersion() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ User steve = new User( "steve" );
+ s.persist( steve );
+ Group admin = new Group( "admin" );
+ s.persist( admin );
+ t.commit();
+ s.close();
+
+ byte[] steveTimestamp = steve.getTimestamp();
+
+ s = openSession();
+ t = s.beginTransaction();
+ steve = ( User ) s.get( User.class, steve.getId() );
+ admin = ( Group ) s.get( Group.class, admin.getId() );
+ steve.getGroups().add( admin );
+ admin.getUsers().add( steve );
+ t.commit();
+ s.close();
+
+ assertFalse( "owner version not incremented", Hibernate.BINARY.isEqual( steveTimestamp, steve.getTimestamp() ) );
+
+ steveTimestamp = steve.getTimestamp();
+
+ s = openSession();
+ t = s.beginTransaction();
+ steve = ( User ) s.get( User.class, steve.getId() );
+ steve.getGroups().clear();
+ t.commit();
+ s.close();
+
+ assertFalse( "owner version not incremented", Hibernate.BINARY.isEqual( steveTimestamp, steve.getTimestamp() ) );
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.delete( s.load( User.class, steve.getId() ) );
+ s.delete( s.load( Group.class, admin.getId() ) );
+ t.commit();
+ s.close();
+ }
+
+
+ public void testCollectionNoVersion() {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ User steve = new User( "steve" );
+ s.persist( steve );
+ Permission perm = new Permission( "silly", "user", "rw" );
+ s.persist( perm );
+ t.commit();
+ s.close();
+
+ byte[] steveTimestamp = steve.getTimestamp();
+
+ s = openSession();
+ t = s.beginTransaction();
+ steve = ( User ) s.get( User.class, steve.getId() );
+ perm = ( Permission ) s.get( Permission.class, perm.getId() );
+ steve.getPermissions().add( perm );
+ t.commit();
+ s.close();
+
+ assertTrue( "owner version was incremented", Hibernate.BINARY.isEqual( steveTimestamp, steve.getTimestamp() ) );
+
+ s = openSession();
+ t = s.beginTransaction();
+ steve = ( User ) s.get( User.class, steve.getId() );
+ steve.getPermissions().clear();
+ t.commit();
+ s.close();
+
+ assertTrue( "owner version was incremented", Hibernate.BINARY.isEqual( steveTimestamp, steve.getTimestamp() ) );
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.delete( s.load( User.class, steve.getId() ) );
+ s.delete( s.load( Permission.class, perm.getId() ) );
+ t.commit();
+ s.close();
+ }
+}
\ No newline at end of file
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/sybase/User.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/sybase/User.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/sybase/User.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,52 @@
+<?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 TIMESTAMP datatype available in Sybase
+ and SQL Server for optimistic locking value.
+ -->
+<hibernate-mapping package="org.hibernate.test.version.sybase">
+
+ <class name="User" table="syb_ts_user">
+ <id name="id" column="user_id" type="long">
+ <generator class="native"/>
+ </id>
+ <version name="timestamp" type="binary" generated="always">
+ <column name="ts" sql-type="timestamp"/>
+ </version>
+ <property name="username" column="user_name" type="string" unique="true"/>
+ <set name="groups" table="syb_ts_user_group" batch-size="9" inverse="true" optimistic-lock="true" lazy="true" cascade="none" >
+ <key column="user_id"/>
+ <many-to-many column="group_id" class="Group" lazy="false" fetch="join" />
+ </set>
+ <set name="permissions" table="syb_ts_user_perm" batch-size="9" inverse="false" optimistic-lock="false" lazy="true" cascade="none">
+ <key column="user_id"/>
+ <many-to-many column="perm_id" class="Permission" lazy="false" fetch="join"/>
+ </set>
+ </class>
+
+ <class name="Group" table="syb_ts_group">
+ <id name="id" column="group_id" type="long">
+ <generator class="native"/>
+ </id>
+ <timestamp name="timestamp" column="ts" source="db"/>
+ <property name="name" column="name" type="string" unique="true"/>
+ <set name="users" table="syb_ts_user_group" batch-size="9" inverse="false" lazy="true" cascade="none" >
+ <key column="group_id"/>
+ <many-to-many column="user_id" class="User" lazy="false" fetch="join" />
+ </set>
+ </class>
+
+ <class name="Permission" table="syb_ts_permission">
+ <id name="id" column="perm_id" type="long">
+ <generator class="native"/>
+ </id>
+ <timestamp name="timestamp" column="ts" source="db"/>
+ <property name="name" column="name" type="string" unique="true"/>
+ <property name="context" column="ctx" type="string"/>
+ <property name="access" column="priv" type="string"/>
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/sybase/User.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/sybase/User.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/version/sybase/User.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,65 @@
+// $Id: User.java 7805 2005-08-10 16:25:11Z steveebersole $
+package org.hibernate.test.version.sybase;
+
+import java.util.Set;
+import java.sql.Timestamp;
+
+/**
+ * Implementation of User.
+ *
+ * @author Steve Ebersole
+ */
+public class User {
+ private Long id;
+ private byte[] timestamp;
+ private String username;
+ private Set groups;
+ private Set permissions;
+
+ public User() {
+ }
+
+ public User(String username) {
+ this.username = username;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public byte[] getTimestamp() {
+ return timestamp;
+ }
+
+ public void setTimestamp(byte[] timestamp) {
+ this.timestamp = timestamp;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public Set getGroups() {
+ return groups;
+ }
+
+ public void setGroups(Set groups) {
+ this.groups = groups;
+ }
+
+ public Set getPermissions() {
+ return permissions;
+ }
+
+ public void setPermissions(Set permissions) {
+ this.permissions = permissions;
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/where/File.hbm.xml
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/where/File.hbm.xml (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/where/File.hbm.xml 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,24 @@
+<?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 a class-level where restriction
+ -->
+<hibernate-mapping package="org.hibernate.test.where">
+
+ <class name="File" where="deleted=0" table="T_FILE">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="name"/>
+ <property name="deleted" type="org.hibernate.test.where.NumericTrueFalseType"/>
+ <many-to-one name="parent"/>
+ <set name="children" inverse="true" where="deleted=0">
+ <key column="parent"/>
+ <one-to-many class="File"/>
+ </set>
+ </class>
+
+</hibernate-mapping>
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/where/File.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/where/File.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/where/File.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/where/File.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,52 @@
+//$Id: File.java 8043 2005-08-30 15:20:42Z oneovthafew $
+package org.hibernate.test.where;
+
+import java.util.Set;
+
+public class File {
+ private long id;
+ private String name;
+ private File parent;
+ private boolean deleted;
+ private Set children;
+
+ public Set getChildren() {
+ return children;
+ }
+ public void setChildren(Set children) {
+ this.children = children;
+ }
+
+ public File(String name, File parent) {
+ this.name = name;
+ this.parent = parent;
+ }
+
+ File() {}
+
+ public boolean isDeleted() {
+ return deleted;
+ }
+ public void setDeleted(boolean deleted) {
+ this.deleted = deleted;
+ }
+ 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 File getParent() {
+ return parent;
+ }
+ public void setParent(File parent) {
+ this.parent = parent;
+ }
+
+}
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/where/File.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/where/NumericTrueFalseType.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/where/NumericTrueFalseType.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/where/NumericTrueFalseType.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,53 @@
+package org.hibernate.test.where;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.PreparedStatement;
+import java.sql.Types;
+
+import org.hibernate.type.BooleanType;
+import org.hibernate.dialect.Dialect;
+
+/**
+ * Maps int db values to boolean java values. Zero is considered false; any
+ * non-zero value is considered true.
+ *
+ * @author Steve Ebersole
+ */
+public class NumericTrueFalseType extends BooleanType {
+
+ public Object get(ResultSet rs, String name) throws SQLException {
+ int value = rs.getInt( name );
+ if ( rs.wasNull() ) {
+ return getDefaultValue();
+ }
+ else if ( value == 0 ) {
+ return Boolean.FALSE;
+ }
+ else {
+ return Boolean.TRUE;
+ }
+ }
+
+ public void set(PreparedStatement st, Object value, int index) throws SQLException {
+ if ( value == null ) {
+ st.setNull( index, Types.INTEGER );
+ }
+ else {
+ boolean bool = ( ( Boolean ) value ).booleanValue();
+ st.setInt( index, bool ? 1 : 0 );
+ }
+ }
+
+ public String objectToSQLString(Object value, Dialect dialect) throws Exception {
+ return ( ( Boolean ) value ).booleanValue() ? "1" : "0";
+ }
+
+ public int sqlType() {
+ return Types.INTEGER;
+ }
+
+ public String getName() {
+ return "numeric_boolean";
+ }
+}
Added: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/where/WhereTest.java
===================================================================
--- trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/where/WhereTest.java (rev 0)
+++ trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/where/WhereTest.java 2007-05-25 20:19:29 UTC (rev 11563)
@@ -0,0 +1,57 @@
+//$Id: WhereTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
+package org.hibernate.test.where;
+
+import junit.framework.Test;
+
+import org.hibernate.FetchMode;
+import org.hibernate.Session;
+import org.hibernate.criterion.Restrictions;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Max Rydahl Andersen
+ */
+public class WhereTest extends FunctionalTestCase {
+
+ public WhereTest(String str) {
+ super(str);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "where/File.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( WhereTest.class );
+ }
+
+ public void testWhere() {
+ Session s = openSession();
+ s.getTransaction().begin();
+ File parent = new File("parent", null);
+ s.persist( parent );
+ s.persist( new File("child", parent) );
+ File deletedChild = new File("deleted child", parent);
+ deletedChild.setDeleted(true);
+ s.persist( deletedChild );
+ File deletedParent = new File("deleted parent", null);
+ deletedParent.setDeleted(true);
+ s.persist( deletedParent );
+ s.flush();
+ s.clear();
+ parent = (File) s.createCriteria(File.class)
+ .setFetchMode("children", FetchMode.JOIN)
+ .add( Restrictions.isNull("parent") )
+ .uniqueResult();
+ assertEquals( parent.getChildren().size(), 1 );
+ s.clear();
+ parent = (File) s.createQuery("from File f left join fetch f.children where f.parent is null")
+ .uniqueResult();
+ assertEquals( parent.getChildren().size(), 1 );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+}
+
Property changes on: trunk/Hibernate3/testsuite/src/test/java/org/hibernate/test/where/WhereTest.java
___________________________________________________________________
Name: svn:executable
+ *
More information about the hibernate-commits
mailing list